← Back to Courses
module
34

Appendix IV - The memory map

Where things are, byte by byte, in both of the machine's memories.

This is a working document. It grows as facts are established, and it is deliberately explicit about the gaps - a map that quietly guesses at the parts nobody has checked is worse than one with blank spaces on it.

How to read it

Every entry carries a mark:

Mark Means
## Established by running code on the machine.
~~ From documentation. Cited, plausible, not tested here.
?? Unknown. Nobody has looked, or looking was inconclusive.

Two separate memories use the same numbers. The Z80's 64K and the VDP's 16K have nothing to do with each other: address 1800 in one is unrelated to address 1800 in the other. Keeping that straight is most of the difficulty.


The Z80's address space - 64K

Not drawn to scale. The interesting parts are at the two ends.

 0000  +====================================+
       |  Restart vectors                   |
       |    0008   RST 08h - the MCAL entry |  ##
       |    0038   RST 38h - break          |  ~~
 0100  +------------------------------------+
       |  Your program loads and runs here  |  ##
       |                                    |
       |  0769   where a program's final    |  ##
       |         RET hands control back     |
       |                                    |
       |  4000   second ROM protocol area,  |  ~~
       |         execution entry at 4004    |
       |                                    |
       |  7530, 8000, 8C3C all confirmed    |  ##
       |  writable RAM - spot checks only   |
       |                                    |
       |  Everything else through here:     |  ??
       |  where RAM begins and ends, where  |
       |  ROM sits, what paging exists      |
       |                                    |
 FB00  +====================================+
       |  MOS scratch pad - see below       |
 FC00  |    interrupt routine area          |  ~~
 FCED  |  <-- SP when your program starts   |  ##
 FE00  |  disc sector buffer, "normally"    |  ~~
 FFFF  +====================================+

The scratch pad, FB00 upwards

Where MOS keeps its working values. Anything a ROM call needs told, beyond what fits in registers, is here. Source for the ~~ rows: the Einstein Compendium's "Scratchpad" chapter, which credits Crystal Research's own "Albert Revealed" for the complete list - this project has only the Compendium's subset.

Address Name What it is
FB00 Interrupt vector, CTC channel 0 ~~
FB02 Interrupt vector, CTC channel 1 ~~
FB04 Interrupt vector, CTC channel 3 ~~
FB06 Interrupt vector, real-time clock ~~
FB08 Interrupt vector, keyboard ~~
FB0A Interrupt vector, A/D converter ~~
FB0C Interrupt vector, fire button ~~
FB0E Interrupt vector, free for user code ~~
FB10 Interrupt vector, printer ~~
FB12 Interrupt vector, PIO port B ~~
FB36 Current VDP mode (a shadow copy - the real register is write-only) ~~
FB38 TCOLR Text colour (high nibble fg, low nibble bg) ~~
FB39 GCOLR Graphics colour ~~
FB43 Key repeat speed ~~
FB44 Disc sector size, in 256-byte blocks (normally 2) ~~
FB45 PCFLGS 80-column card flags ~~
FB46 Code of the last key pressed ~~
FB50 HSTDSC Disc drive, 0-3 ~~
FB51 HSTTRK Track, 0-27h ~~
FB52 HSTSEC Sector, 0-9 ~~
FB53 HSTDMA Sector buffer address ~~
FB55 Flag: disc read or write ~~
FB56 Disc error status ~~
FB57 Retry count before signalling a disc error ~~
FB58-FB5B Current track, one byte per drive 0-3 ~~
FB5F VDP status (a shadow copy) ~~
FB60-FB78 Saved registers for the register display: I, IX, IY, SP, AF', BC', DE', HL', AF, BC, DE, HL, PC ~~
FB92 Flag for interrupts ~~
FB93-FB94 Pointer to the current function key's data in VRAM ~~
FB96 X1 Line end X, two bytes ##
FB98 Y1 Line end Y, two bytes ##
FB9A ORGX Graphics origin X ~~
FB9C ORGY Graphics origin Y ~~
FB9E CX Polygon centre X ~~
FBA0 CY Polygon centre Y ~~
FBA2 RADX Horizontal radius ~~
FBA4 RADY Vertical radius ~~
FBA6 CINC Polygon sides ~~
FBA8-FBAB DOTON etc Line style, four bytes ##
FBAD FILLMOD Fill mode ~~
FBAE Fill routine's stack pointer ~~
FBB0 Disc head stepping rate: 00=6ms, 01=12ms, 02=20ms, 03=30ms ~~
FBB1 Side flag: single=0, double=1 ~~

The stack

SP starts at FCED and grows downward.

 FCED  <-- SP at program entry                        ##
       |
       |  100 bytes of pushes: fine                   ##
       |
 FC8D  ?  somewhere in here it stops working          ??
       |
 FC3D  |  176 bytes of pushes: the machine resets     ##
       v

The boundary is bracketed, not located, and what gets overwritten is not identified. Budget well under 176 bytes and remember every CALL costs two.


The VDP's address space - 16K

This one is fully mapped, and it is drawn roughly to scale.

Graphics II puts the pattern table and the colour table - 6144 bytes each - in the lower and upper 8K halves. That leaves exactly 2048 bytes spare in each half, and everything else the machine needs is squeezed into those two holes. It is worth seeing the halves before the detail:

 0000  +====================================+
       |  LOWER HALF          8192 bytes    |
       |    pattern table  0000-17FF  6144  |
       |    spare          1800-1FFF  2048  |  <- the font
 2000  +====================================+
       |  UPPER HALF          8192 bytes    |
       |    colour table   2000-37FF  6144  |
       |    spare          3800-3FFF  2048  |  <- everything else
 3FFF  +====================================+

In detail:

 0000  +====================================+
       |  Pattern bank 0                    |  ##
       |    256 patterns x 8 bytes          |
       |    serves screen rows 0-7          |
 0800  +------------------------------------+
       |  Pattern bank 1                    |  ##
       |    serves screen rows 8-15         |
 1000  +------------------------------------+
       |  Pattern bank 2                    |  ##
       |    serves screen rows 16-23        |
 1800  +====================================+
       |  MOS font  AND  sprite patterns    |  ##
       |    256 characters x 8 bytes        |
       |    a glyph is at 1800 + code * 8   |
       |    6 pixels wide, left-justified   |
       |    ** this is also the sprite      |
       |    pattern table: sprite pattern   |
       |    n IS character n **             |
       |    and it has to be - 1800 is the  |
       |    only 2K block in VRAM that is   |
       |    not already a VDP table         |
 2000  +====================================+
       |  Colour table                      |  ##
       |    bank 0   2000-27FF              |
       |    bank 1   2800-2FFF              |
       |    bank 2   3000-37FF              |
       |    8 bytes per pattern, one per    |
       |    row: high nibble colours the 1  |
       |    bits, low nibble the 0 bits     |
       |    MOS leaves F0 everywhere        |
       |    high bytes 96 / 104 / 112       |
 3800  +====================================+
       |  Name table                        |  ##
       |    768 entries, one per cell       |
       |    cell r,c is entry r * 32 + c    |
       |    MOS leaves it 00 01 02 03 ...   |
 3B00  +------------------------------------+
       |  Sprite attribute table            |  ##
       |    32 entries x 4 bytes            |
       |    Y, X, pattern number, then      |
       |    colour in the low nibble with   |
       |    the early clock bit as bit 7    |
       |    MOS leaves it all 00 - every    |
       |    sprite present and transparent  |
 3B80  +------------------------------------+
       |  Function key data                 |  ~~
       |    128 bytes, MOS leaves it all 80 |
       |    one string per key, separated   |
       |    by adding 80h to its last byte  |
 3C00  +====================================+
       |  MOS's text buffer                 |  ##
       |    960 bytes of ASCII, 40 x 24     |
       |    row r, column c at              |
       |    3C00 + r * 40 + c               |
       |    the VDP never reads this        |
 3FC0  +------------------------------------+
       |  64 bytes, purpose unknown         |  ~~??
       |    not reset by the reset button   |
       |    (power cycling it is)           |
 3FFF  +====================================+

Why there are two lots of text in there

Worth spelling out, because it confuses everyone once.

MOS runs the display as a bitmap: the name table is filled with ascending numbers so every cell owns a private pattern, and text is drawn as pixels into the pattern banks. So the letters on screen exist as pixels in 0000-17FF.

The font at 1800 is the source those pixels are copied from.

And 3C00 is a plain ASCII copy of what has been printed, which MOS keeps because a bitmap cannot be read back as characters. That is the copy the line-input call hands you.

Three appearances of the same text, in three different forms, for three different reasons.


I/O ports

The Z80 has a third, separate space for talking to hardware, reached with OUT and IN. Only two entries are established.

Port What it is
8 VDP data ##
9 VDP address and register control ##

Everything else - keyboard, sound, disc, printer, serial - is ??. The ROM calls reach them and no experiment here has gone underneath.

Talking to the VDP

 write to VRAM:      port 9 <- low byte of address
                     port 9 <- 40h + top six bits
                     port 8 <- data, address advances itself

 read from VRAM:     port 9 <- low byte
                     port 9 <- top six bits, no 40h
                     port 8 -> data                      ~~

 write a register:   port 9 <- the value
                     port 9 <- 80h + register number

 read the status:    port 9 -> status byte                ~~

The write paths are established. The two read paths come from the datasheet and have not been exercised here.


The biggest gaps

Named plainly, because they are the honest content of a map like this:

Where RAM begins and ends. Three addresses have been poked and found writable. That is not a map. Nothing is known about where ROM sits, whether paging exists, or what a program may safely use.

The VDP registers. They are write-only, and the emulator exposes no way to read them. Their values have been worked out from behaviour instead - the display mode from the memory layout, and R5 = 118 and R6 = 3 from the sprite table addresses that work. Derived, not read.

Every port but 8 and 9.

The interrupt rate. Interrupts are known to be enabled and something generates them. How often is not established.

Adding to it

When an experiment settles something, the entry goes in with its mark. A ~~ becomes ## when code has confirmed it - not before, and the mark changes only for the specific thing tested. "The datasheet says the sector buffer is normally at FE00" and "a program read the sector buffer at FE00" are different claims and get different marks.

A ?? that turns out to be genuinely unknowable stays ?? with a note. There is no shame in a blank space, and considerable risk in filling one with a guess.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.