Programming the 80’s way #2
Among ZX Spectrum Next new features is the huge amount of RAM. The question is how to leverage all that memory in Forth.
Looking to how Forth’s system areas are sorted out, the first challenge is to move them down to free the top 8K CPU’s addressable memory between 0E000h and 0FFFFh allowing MMU7 to map to any physical 8K RAM page. There are some peculiar addresses that identify the following Forth system areas:
- 0F840h : Calculator Stack (SP) grows downward, Text Input Buffer (TIB) upward.,
- 0F8E0h : Return Stack (RP) grows downward, User Variables Area upward.
- 0F94Ch : the FIRST disk buffer starts here and buffers area ends just before LIMIT 0FF58h.
I coded this “move” in a few words (available in Screens #220-#223) summarized in the definition DOWN that moves these pointers “down” as follow:
0FF58h → 0E000h : LIMIT
0F9C4h → 0D9F4h : FIRST
0F8E0h → 0D9A0h : Return Stack and User Variables Area
0F840h → 0D900h : Stack Pointer and TIB
The second question is how to encode both page number and address offset in a normal Z80 16-bits pointer variable. Since an 8K offset requires 13 bits, the remaining 3 bits can be used to encode, say, from page 64 to page 71.
16 bit pointer | pppb bbbb | bbbb bbbb | | Page | Offset | | 0100 0ppp | | 111b bbbb | bbbb bbbb |
This solution opens to 64K of physical RAM that can store large amount of data, a so called heap. Long strings are dictionary expensive, so it would be useful storing them in heap as constant-strings and fetch them at need. More, 8K of room is a good place to store an array of strings, or even numeric array and implement some matrices algebra…
The third and final question is how to conceive a “store and retrieve” on heap so that it can keep track of room and a way to let a page to stay in place across I/O to disk operations, that it is known they use addresses C000-FFFF for their purposes. Maybe, this is the tricky part, avoid that my string is “paged away” in the middle of processing.
What I am working on these days are a few new definitions such as a POINTER to create a pointer word, to be seen as a constant that holds an encoded far-address, a HEAP-ALLOT word to require space on the heap, and a FAR word to prefix any ! (store), @ (fetch), or TYPE to turn a pointer into a physical address and perform the paging handling under the hood just before using it. Things are not complete yet, but I hope to share soon some result on github. Maybe, this is enough for a while…
All coding explained below was performed using my “genuine” ZX Spectrum Next or within CSpect emulator in a real 80’s fashion: no cross-assembler nor external tools, just Forth environment itself.
I went a bit further in this RAM topic and coded in assembler two new words using Forth’s ASSEMBLER vocabulary:
MMU7 to fit a given 8K page number at E000h (i.e. MMU7).
>FAR to quick decode a “16 bit pointer” splitting it into “page & offset” as shown in the post above.
Then, I coded a word that uses the two above definitions to convert a “16-bit pointer” and fit the correct page, in a single step, as follow:
Third, POINTER to create a new “constant” to point to heap i.e. to keep a “16-bit pointer” so that when the newly created “constant” is later invoked, it will use FAR to page-in the correct 8K and return the suitable offset from E000, as follow:
This way I will be able to to define a new string directly on heap using something like that:
dereferencing it and printing it to video using, without explicitly using many times FAR, something like this:
Such definition neatly shows one of the most brilliant Forth’s feature, i.e. you can use a high level language semantics to define a new “creative” word by writing very little code.
The work is still in progress.
For curious people, here are the definitions of the two assembler words