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.
Contended Screen Ram
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.
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.