Memory and Banking
The Z80 sees 64 KiB. Every Spectrum past the 48K has more RAM than that and shows it a window at a time, so writing for one means telling the assembler which physical memory an address currently stands for.
Knowing what each machine has
| Target | Windows | Page size | Pageable |
|---|---|---|---|
zxnext | 8 | 8 KiB | all eight, over up to 256 pages |
zx128 | 4 | 16 KiB | $C000 only, over banks 0–7 |
zx48 | 4 | 16 KiB | none |
zx16 | — | — | none, and nothing above $8000 |
The 64 KiB space is always a uniform tiling: the window size is the page size, so a Next address divides by 8192 to give its slot and a 128K address by 16384 to give its window.
zx48 and zx16 refuse the paging directives outright, rather than accepting them and doing nothing:
Error [E1008]: Invalid instruction BANK directive not supported for this targetSelecting a page
| Directive | |
|---|---|
bank <n> | Select a 16 KiB bank |
bank <n>, <slot> | Select it into a slot, on the Next only |
page <page>, <slot> | Select an 8 KiB page into one of the Next’s eight slots |
slot <page>, <slot> | The same directive under another name |
⛔ slot is an alias for page, and takes the same arguments in the same order — the page first, then the slot. slot 9, 4 puts page 9 into slot 4, exactly as page 9, 4 does. The name reads the other way round and does not mean it.
page requires both arguments; page 5 on its own is refused with PAGE requires a slot.
bank is valid on the Next as well as the 128K. A 16 KiB bank is the unit the .nex file format uses, and maps to the slot pair 2N/2N+1, so it is not treated as foreign there. On the Next it takes an optional slot, which a 128K refuses — BANK directive does not accept a slot for zx128 target.
⛔ Give the slot when you mean to put code in the bank. bank 11, 3 moves assembly to $6000, so the bytes that follow belong to bank 11. Bare bank 11 selects the bank but leaves the program counter where it was, so on a Next the bytes carry on landing wherever they were going and the bank stays empty — which an .nex then drops for holding nothing. On a 128K, bare bank moves assembly to $C000, that machine having only the one pageable window.
page/slot on a 128K warn rather than fail:
Warning [W1020]: page/slot is the Next 8K paging modelThey are best-effort mapped, so old source keeps working, but the machine has no 8 KiB paging and the warning is telling you the truth.
Knowing where you are
Three read-only values, usable in any expression:
| Written | Is |
|---|---|
_page | The physical 8 KiB page holding the current address |
_bank | Its 16 KiB bank — _page / 2 |
_slot | The 8 KiB slot index, 0–7 |
⛔ The leading underscore is required. Without it the word is read as a label of that name, so ld hl,page reports an undefined label rather than answering with the page number. A label really called page is reached that way, and is yours.
All three are derived from the live slot map at that point in the source, so they follow your paging directives:
org $8000
db _page, _slot, _bank ; 04 04 02 on a Next at reset
page 9, 4
db _page, _slot, _bank ; 09 04 04They are well defined on every machine — _bank is the useful one on 16 KiB machines, _page and _slot on the Next — so no target branching is needed to use them.
Assuming a RAM size
A Next has 1 MiB or 2 MiB depending on the machine, and --ram says which you are targeting:
fantasm build main.asm -t zxnext --ram 512kor ram = "512k" under [assembler] — see The Project File.
This narrows the valid bank range rather than describing the hardware. It defaults to the target’s maximum, so a Next allows banks 0–127. 512K is 32 banks, and the build tells you when you exceed it:
bank 31 ok
bank 32 Error [E1082]: Invalid address or bankA program that must run on a 512K machine then fails at your desk rather than on the desk of whoever owns one.
Accepted forms are 2mb, 1mb, 512k, or a bare number in KiB.
Testing paged code
!test blocks run on a paged machine that mirrors the assembler’s slot map, so a test that pages memory reads and executes the right physical page. Runtime NEXTREG MMU writes and the 128K paging port remap slots live, as they would on hardware.
For the default layout every slot maps to a different page, so a test that does no paging behaves exactly as it would against a flat 64 KiB. See Testing.
Working within the limits
⛔ Cross-bank library code is not supported by --gc-modules. A module body placed in a different bank from the code that calls it is untested with elimination on, and is not diagnosed. If you page banks yourself and call across them, leave --gc-modules off — see Modules.
zx16 refuses anything above $8000, which is the machine’s own ceiling rather than a budget you set. -M/max_code_size is the separate limit on how much code there is; see Command Line.