The memory map on a 48K Spectrum is fairly straightforward.
The Z80 processor can address 64KB of RAM; the 48K Spectrum uses all of this address space without paging.
The first 16K is ROM; the rest of the memory map is RAM.
There are two ways to run machine code on the Spectrum. You can reserve memory for your code and preserve all the system memory areas. That way you can drop back into BASIC after the machine code has completed. Or, you can use all the memory from &5B00 to &FFFF but the only way back to BASIC is with a reset or switching the Spectrum off and on. Games programmers tended to go for the latter option as it gave them the most available RAM.
If you do wish to reserve memory, then issue the command CLEAR n, where n is the last byte you wish to be available to BASIC. So, if you want your machine code to sit at address &8000 in the memory map, then issue the command:
We want the last byte of memory available to basic to be just before our code, so (&8000 – 1) is 32767 in decimal. Assemble and load your code into address 32768 and run.
128K Toastrack / +2
The 128K Spectrums employ paging to map the extra memory into the 64K Z80 address space.
The memory is paged by writing to port &7FFD.
- RAM – The RAM bank to page into &C000 to &FFFF
- SCR – Set to 0 to display normal screen, 1 to display screen in bank 7
- ROM – Set to 0 to page in the 128K editor, or 1 to page in the 48K BASIC ROM
- DIS – Set to 1 to disable memory paging until next reset
- The port is write-only – reading it will just return a floating bus value.
- Setting the screen page determines the area of memory the ULA is looking at to render the screen – it does not page Bank 7 into &4000 to &7FFF.
- Selecting 48K Spectrum from the startup menu, or using the BASIC command ‘SPECTRUM’ from 128K mode, sets the DIS bit to prevent the extra memory from being accessed. You can however boot into 48K BASIC by typing in ‘RANDOMIZE USR 0’ from 128K BASIC without setting this bit.
Contended Screen Ram
On 48K Spectrums, the block of RAM between &4000 and &7FFF is contented, that is access to the RAM is shared between the processor and the ULA. The ULA has priority access when the screen is being drawn.
In addtion, on the 128K/+2 models, the odd-numbered banks (1, 3, 5 and 7) are also contented.
Code that runs in or accesses contended memory will run slightly slower. For that reason it is recommended that speed critical routines (sprites, scrolling, etc) are not executed in this area of RAM.
Memory Handling in Basic
Back in the day, before emulators, you would have to ensure that your code could load into memory, preserving system variables, so that the BASIC game preloader could execute the RANDOMIZE USR command to execute the machine code.
A simple preloader would be a couple of lines of BASIC like this:
10 LOAD "gamecode" CODE 32768 20 RANDOMIZE USR 32768
This would be saved, to autorun from line 10, by the command
SAVE "gamename" LINE 10
This small BASIC preloader would then autorun, load the file gamecode (which would have been saved on the cassette straight after the preloader) and then execute the machine code with the RANDOMIZE USR command; assuming that the entry point in the machine code was 32768.
A whole chapter of history was written about the subject of piracy. There were various attempts to obfuscate the loader code to prevent this. Nearly all anti piracy measures failed the twin tape deck test and those that did succeed just annoyed the users.