Memory map¶
Every data address P8X software uses, as generators/gen_memmap.py defines it: memory regions, I/O ports and the scratch areas of the BIOS, the OS and the commands. The same generator writes memmap.inc for the assembler and memmap.h for C, and the printable memory map is generated from it too. This page is built from its Python output, generators/memmap.py, so it cannot disagree with it.
In short: ROM $0000-$17FF (6 KB, the monitor and BIOS), RAM $1800-$FEFF, I/O $FF00-$FFFF. The OS loads at $2000; programs run from $5900.
Memory-region anchors¶
| Name | Value | Notes |
|---|---|---|
RAMBASE |
$1800 |
first RAM address (ROM shrunk 8K->6K 2026-09-14; $1800-$1FFF is a low RAM island) |
IOBASE |
$FF00 |
memory-mapped I/O page |
ROMSIZE |
$1800 |
6K firmware ROM $0000-$17FF (monitor uses ~5.2K) |
RAMSIZE |
$E700 |
RAM span $1800-$FEFF (IOBASE-RAMBASE) |
OSORG |
$2000 |
OS load/link address (still $2000; the $1800-$1FFF RAM island holds relocated scratch) |
TPABASE |
$5900 |
transient program area base (RUNnable programs load here) |
CSTACKTOP |
$F800 |
compiler C-stack top (grows down; p8cc __csp init) |
I/O ports ($FF00-$FFFF)¶
| Name | Value | Notes |
|---|---|---|
SWITCHES |
$FF00 |
read: DIP/switch input (I/O card port 0; emulator -s) |
LEDS |
$FF02 |
write: LED output latch (emulator -L stamps; POKE 65282) |
IRQGEN |
$FF06 |
write (any value): assert a maskable IRQ (emulator device-IRQ model; hardware IRQ comes from the IRQ-controller card) |
ACIAS |
$FF04 |
ACIA status (rd) / control (wr) |
ACIAD |
$FF05 |
ACIA data |
ACIA2S |
$FF08 |
2nd ACIA status (rd) / control (wr) -- the Kermit/serial-terminal port |
ACIA2D |
$FF09 |
2nd ACIA data |
CFDATA |
$FF10 |
CF task file |
CFFEAT |
$FF11 |
|
CFSCNT |
$FF12 |
|
CFLBA0 |
$FF13 |
|
CFLBA1 |
$FF14 |
|
CFLBA2 |
$FF15 |
|
CFHEAD |
$FF16 |
$E0 = LBA mode, drive 0 |
CFCMD |
$FF17 |
command (wr) / status (rd) |
CFSTAT |
$FF17 |
|
MDA |
$FF30 |
MDU operand a, low byte (write clears the high byte) |
MDB |
$FF31 |
MDU operand b, low byte |
MDC |
$FF32 |
MDU divisor c, low byte |
MDQ |
$FF33 |
read: MDU result (a*b)/c, low byte (poll MDSTAT first) |
MDGO |
$FF34 |
write (any value): start the MDU operation |
MDSTAT |
$FF35 |
read: bit7 BUSY |
MDID |
$FF36 |
read: $4D 'M' -- MDU-presence probe |
GLDATA |
$FF50 |
GL: write one command-stream byte into the FIFO |
GLSTAT |
$FF51 |
read: bit7 FIFO full, bit6 busy, bit1 error pending, bit0 read-back pending |
GLRB |
$FF52 |
read: pop one read-back FIFO byte |
GLERR |
$FF53 |
read: pop one error FIFO byte (0 = empty) |
GLID |
$FF54 |
read: $47 'G' -- graphics-language presence probe |
PSADAT |
$FF58 |
read: port A (keyboard) byte, ready cleared on read (raw Set-2) |
PSAST |
$FF59 |
port A status: r bit0 ready/bit1 overrun/bit2 parity; w bit0 CLK-low/bit1 DATA-low |
PSBDAT |
$FF5A |
read: port B (mouse) byte, ready cleared on read |
PSBST |
$FF5B |
port B status: as PSAST |
PSLINE |
$FF5C |
read: live line states (bit0 Aclk/bit1 Adat/bit2 Bclk/bit3 Bdat) for the bit-banged transmit |
PSID |
$FF5E |
read: $4B 'K' -- PS/2-card presence probe (absent floats $FF) |
MDAH |
$FF39 |
MDU operand a, high byte (write AFTER MDA) |
MDBH |
$FF3A |
MDU operand b, high byte |
MDCH |
$FF3B |
MDU divisor c, high byte |
MDQH |
$FF3C |
read: MDU result, high byte |
BIOS / FS scratch (moved to $1F00-$1FFF island)¶
| Name | Value | Notes |
|---|---|---|
LBUF |
$1F00 |
input line buffer |
ADDRL |
$1F40 |
parsed address |
ADDRH |
$1F41 |
|
HEXL |
$1F42 |
hex accumulator |
HEXH |
$1F43 |
|
LBA |
$1F47 |
current LBA, byte 0 (bits 7:0) |
LBA1 |
$1F48 |
LBA byte 1 (bits 15:8) — 0 after CFINIT unless set |
LBA2 |
$1F49 |
LBA byte 2 (bits 23:16) — 0 after CFINIT unless set |
FNAME |
$1F4A |
12-byte filename (space-padded) — in for both calls |
FSRC |
$1F56 |
FCREATE: source address of the file data (2 bytes) |
FLEN |
$1F58 |
file length in bytes (3 bytes): FCREATE in, FFIND out |
FSAV |
$1F5B |
FCREATE scratch: requested length saved across FFIND (3) |
ROLBA |
$1F5E |
next sector LBA to read (3) |
ROREM |
$1F61 |
bytes remaining in the file (3) |
ROBUF |
$1F64 |
caller's 512-byte sector buffer address (2) |
ROPTR |
$1F66 |
read cursor within ROBUF (2) |
ROCNT |
$1F68 |
bytes left in ROBUF; 0 -> refill (3) |
WOLBA |
$1F6B |
current output sector LBA (3) |
WOPOS |
$1F6E |
byte offset within SBUF; 512 -> flush (2) |
WOTOT |
$1F70 |
total bytes written (-> FLEN at close) (3) |
DIRLBA |
$1F73 |
current directory start LBA, low byte (16-bit: +DIRLBA1) |
DIRN |
$1F74 |
current directory sector count (1) |
FFLAG |
$1F75 |
flag of the entry FSCAN matched (file $01 / dir $02) |
RPATH |
$1F76 |
FRESOLVE path cursor (2) |
DILBA |
$1F78 |
iteration: current directory sector LBA (1) |
DICNT |
$1F79 |
iteration: sectors remaining (1) |
DIIDX |
$1F7A |
iteration: entry index within the sector (0..15) |
FLAREM |
$1F7B |
FLOADAT remaining-bytes counter (CFRDSEC clobbers TMP) (3) |
DIBUFH |
$1F7E |
FNEXT directory-buffer page (high byte; low byte 0). |
DILBA1 |
$1F7F |
FNEXT iteration sector LBA, high byte |
DIRLBA1 |
$1F80 |
current directory start LBA, high byte (pairs DIRLBA) |
FCDH |
$1F81 |
FCREATE directory-sector scan cursor, high byte (HEXL) |
DRVSEL |
$1F82 |
current CF drive for sector I/O (0/1); ORed into CFHEAD |
CFTOL |
$1F83 |
CF bounded-wait timeout counter, low byte |
CFTOH |
$1F84 |
CF bounded-wait timeout counter, high byte |
ROSDRV |
$1F85 |
read-stream drive (captured by FOPEN, re-asserted by FG_FILL) |
WOSDRV |
$1F86 |
write-stream drive (captured by FWOPEN, re-asserted by FW_FLUSH) |
CFIMASK |
$1F87 |
bit N set = drive N has been CFINIT'd this session |
REMW |
$1F88 |
FCOM_CORE ceil(FLEN/512): 24-bit remaining counter (3) |
CNTW |
$1F8B |
FCOM_CORE sector count, 16-bit (files may span >255 sectors) |
ROSTAT |
$1F5E |
read-stream state base (ROLBA..ROCNT, 11 bytes) |
Shared sector buffer¶
| Name | Value | Notes |
|---|---|---|
SBUF |
$1D00 |
sector buffer |
Hardware stack¶
| Name | Value | Notes |
|---|---|---|
STKTOP |
$FEFF |
OS scratch (top moved to the $1800-$1FFF island)¶
| Name | Value | Notes |
|---|---|---|
LINEBUF |
$5700 |
shell input line (64 bytes) |
CMDBUF |
$5740 |
parsed command word (16 bytes) |
NAMEBUF |
$5750 |
12-byte filename (search key / DIR scratch) |
ECNT |
$5763 |
entries-left-in-sector counter |
FLAGS |
$5764 |
current entry flag byte |
MATCH |
$5765 |
1 = name matched / strings equal |
LENLO |
$5766 |
entry length, low 16 bits |
LENHI |
$5767 |
|
STARTLO |
$5768 |
entry start LBA (low byte) |
LOADLO |
$5769 |
entry load address |
LOADHI |
$576A |
|
EXECLO |
$576B |
entry exec address |
EXECHI |
$576C |
|
DLBA |
$576D |
directory sector being scanned |
SECCNT |
$576E |
sectors left to transfer |
CURLBA |
$576F |
current data LBA |
ENTPL |
$5770 |
pointer to a directory entry (in SBUF): |
ENTPH |
$5771 |
flag byte for DEL, entry start for SAVE |
ARGPL |
$5772 |
saved arg position in LINEBUF |
ARGPH |
$5773 |
|
HXLO |
$5774 |
GETHEX result |
HXHI |
$5775 |
|
DIGIT |
$5776 |
HEXVAL digit value |
SHCNT |
$5777 |
shift counter |
SVSTLO |
$5778 |
SAVE source start address |
SVSTHI |
$5779 |
|
FREELO |
$577A |
boot-block free pointer (next data LBA) |
FREEHI |
$577B |
|
SRCLO |
$577C |
running source pointer during the copy |
SRCHI |
$577D |
|
REM |
$577E |
sectors remaining in the SAVE write loop |
NF |
$5780 |
running next-free LBA |
PFOUND |
$5781 |
1 if this pass found an unpacked extent |
MINSTRT |
$5782 |
smallest start LBA >= NF this pass |
MINSEC |
$5783 |
that extent's sector count |
MINPL |
$5784 |
pointer to that entry's start-LBA field (in SBUF) |
MINPH |
$5785 |
|
MINDL |
$5786 |
that entry's directory sector LBA |
ESTART |
$5787 |
current entry start LBA (low byte) |
SRCL |
$5788 |
copy source LBA |
DSTL |
$5789 |
copy dest LBA |
CPYN |
$578A |
sectors left to copy |
CANDL |
$578B |
current entry's start-field pointer |
CANDH |
$578C |
|
ROOTN |
$578D |
root directory sector count (4) |
DATABASE |
$578E |
first data LBA (37) |
CWDL |
$578F |
current directory: start LBA |
CWDN |
$5790 |
sector count |
SDIRL |
$5791 |
directory being scanned this op (start LBA) |
SDIRN |
$5792 |
sector count |
SCNT |
$5793 |
sectors-left counter while scanning a directory |
LSL |
$5794 |
SETPATH: pointer to the last '/' in CWDPATH |
LSH |
$5795 |
|
PATHL |
$5796 |
saved path cursor across DESCEND (FINDENT clobbers P2) |
PATHH |
$5797 |
|
NEWLBA |
$5798 |
MKDIR: LBA of the new directory extent |
PSL |
$5799 |
MKDIR: parent dir start LBA / sector count |
PSN |
$579A |
|
EFLAG |
$579B |
flag byte WRENT stamps (F_FILE for SAVE, F_DIR for MKDIR) |
RMDL |
$579C |
RMDIR: parent directory sector holding the entry |
CDST |
$579D |
current directory: start LBA / sectors / entry index |
CDSC |
$579E |
|
CIDX |
$579F |
|
REDIRF |
$57A0 |
0 = console, 1 = capturing to RBUF |
RCH |
$57A1 |
OUTCH: byte being emitted |
RS2L |
$57A2 |
OUTCH: saved caller P2 |
RS2H |
$57A3 |
|
RPTRL |
$57A4 |
OUTCH: next free byte in the capture buffer |
RPTRH |
$57A5 |
|
RHX |
$57A6 |
OPHEX8 scratch |
REDNAME |
$57A7 |
redirect target filename (null-terminated, <=48): $63A7..$63D6 |
FNDIR |
$57D7 |
directories counted |
FNFIL |
$57D8 |
files counted |
FNDEL |
$57D9 |
deleted slots counted |
FMAXE |
$57DA |
highest extent end LBA seen (data area only) |
FUSED |
$57DB |
data sectors occupied by live extents |
FERR |
$57DC |
problems found (0 = clean) |
FCHILD |
$57DD |
CHKDD: directory whose '..' is being checked |
FEXP |
$57DE |
CHKDD: expected parent LBA |
TSP |
$57E0 |
tree stack depth (0 = at root level) |
TI |
$57E1 |
scratch loop counter for the frame stack |
LENHI2 |
$57E2 |
entry length, bits 16..23 (the BIOS FLEN 3rd byte) |
SECCH |
$57E3 |
SECCOUNT sector-count high byte (files >255 sectors) |
MINSECH |
$57E4 |
PACK: chosen extent's sector count, high byte |
CPYNH |
$57E5 |
PK2MOVE: sectors-to-copy counter, high byte |
TFRAME |
$58B7 |
8 frames x 4 bytes (dst_lo,dst_hi,dsc,idx): $64B7..$64D6 |
PPSEC |
$57FA |
chosen extent's parent-entry: dir sector LBA / slot |
PPSLOT |
$57FB |
|
CANDSEC |
$57FC |
candidate entry's location during the find walk |
CANDSLOT |
$57FD |
|
PARST |
$57FE |
PK2FIX: parent directory start LBA (for '..') |
CWDPATH |
$5800 |
textual CWD path for the prompt (up to 48 bytes) |
INMODE |
$5830 |
SYS_GETC source: 0 = console, 1 = the read stream |
INARM |
$5831 |
shell armed a '< file' for the next RUN |
INNAME |
$5832 |
'< file' name (null-terminated, <=48): $6432..$6461 |
PIPEF |
$5862 |
pipe stage: 0 none, 1 left ran, 2 right ran |
PIPEBUF |
$5863 |
saved right-hand command of a 'cmd | cmd' ($6463..$64A2) |
CWDLH |
$58A3 |
CWDL high byte (current working directory start LBA) |
SDIRLH |
$58A4 |
SDIRL high byte (directory being scanned this op) |
STARTHI |
$58A5 |
STARTLO high byte (entry start LBA from FINDENT) |
DLBAH |
$58A6 |
DLBA high byte (directory-sector scan cursor) |
NEWLBAH |
$58A7 |
NEWLBA high byte (MKDIR new extent) |
PSLH |
$58A8 |
PSL high byte (MKDIR parent extent) |
PARSTH |
$58A9 |
PARST high byte (PACK '..' parent fix) |
RMDLH |
$58AA |
RMDL high byte (RMDIR parent sector) |
CURLBAH |
$58AB |
CURLBA high byte (SAVE data-write LBA, 16-bit) |
NFH |
$58AC |
NF high byte (PACK next-free target) |
MINSTRTH |
$58AD |
MINSTRT high byte (smallest start LBA this pass) |
CDSTH |
$58AE |
CDST high byte (current directory in the walk) |
CANDSECH |
$58AF |
CANDSEC high byte (candidate entry's dir sector) |
PPSECH |
$58B0 |
PPSEC high byte (chosen extent's parent-entry sector) |
SRCH |
$58B1 |
SRCL high byte (PK2MOVE copy source) |
DSTH |
$58B2 |
DSTL high byte (PK2MOVE copy dest) |
FCHILDH |
$58B3 |
FCHILD high byte (CHKDD child dir) |
FEXPH |
$58B4 |
FEXP high byte (CHKDD expected parent) |
FMAXEH |
$58B5 |
FMAXE high byte (FSCK highest extent end) |
FUSEDH |
$58B6 |
FUSED high byte (FSCK live data sectors) |
REDAPP |
$58D7 |
>> append redirect: 1 = prepend the existing file |
APHAVE |
$58D8 |
>> : 1 = an existing file to prepend was found |
APLBA |
$58D9 |
>> : old file's start LBA (2 bytes) |
APREM |
$58DB |
>> : old file bytes left to copy (2 bytes) |
APCHK |
$58DD |
>> : bytes to emit from the current sector (2 bytes) |
SCRIPTM |
$58E0 |
1 = the shell is running lines from a sh script |
SCRSAVE |
$58E1 |
saved script read-stream state (ROSTATE 13 + ROSDRV = 14: $64E1..$64EE) |
SCRCNT |
$58EF |
byte counter for SAVESCR/RESTSCR (1) |
APBUF |
$1B00 |
>> prepend sector buffer (512B, below the TPA); also the |
IBUF |
$1800 |
512-byte buffer for the stdin read stream |
PATHBUF |
$1A00 |
search path, ';'-separated dirs; default '/BIN' ($6700..$673F) |
RUNPATH |
$1A40 |
scratch: candidate program path built during a lookup ($6740..$679F) |
RUNSKIP |
$1AA0 |
DORUN: 1 = skip the program-name word for the arg pointer |
PSCANL |
$1AA1 |
PATH search cursor into PATHBUF (low) |
PSCANH |
$1AA2 |
PATH search cursor into PATHBUF (high) |
GPLF |
$1AA3 |
SYS_GETC console: 1 = a LF is pending after a CR keypress |
CURDRIVE |
$1AA4 |
derived: 1 if the CWD is under /d1 (drive 1), else 0 |
DRVINIT |
$1AA5 |
bitmask: bit N set = drive N has been CFINIT'd this session |
MPSAV |
$1AA6 |
MNTPFX: saved P2 (2 bytes) while sniffing a 'd1' prefix |
TPA (transient programs)¶
| Name | Value | Notes |
|---|---|---|
RBUF |
$5900 |
capture buffer = the TPA (free during a built-in cmd) |
Shell history¶
| Name | Value | Notes |
|---|---|---|
HISTST |
$1F8E |
history ring: index where the next entry is written (0..HISTN-1) |
HISTCT |
$1F8F |
history ring: number of stored entries (0..HISTN) |
HISTNV |
$1F90 |
history ring: recall cursor (0 = not navigating; N = N lines back) |
HISTRING |
$F800 |
history ring buffer base: HISTN x HISTLEN bytes ($F800..$F9FF, the free 512 B above CSTACKTOP; $FA00 = glob page, $FC00 = RDBUF, $FE00 = stack) |
Shell completion¶
| Name | Value | Notes |
|---|---|---|
CMPPFX |
$1B00 |
tab-complete: leaf prefix being completed (NUL-term, 64; aliases APBUF) |
CMPLCP |
$1B40 |
tab-complete: longest common prefix of the matches (NUL-term, 16; aliases APBUF) |
CMPDIR |
$1B50 |
tab-complete: directory-part path string, for CDPATH (NUL-term, 64; aliases APBUF) |
CMPPL |
$1F91 |
tab-complete: length of the typed leaf prefix |
CMPCNT |
$1F92 |
tab-complete: number of matches (saturates at 255) |
CMPFW |
$1F93 |
tab-complete: 1 = completing the command word (first word) |
CMPTABF |
$1F94 |
tab-complete: 1 = the previous key was a no-progress Tab |
CMPISD |
$1F95 |
tab-complete: 1 = the sole match is a directory |
CMPLM |
$1F96 |
tab-complete: 1 = scan in list mode (print matches) |
CMPDL |
$1F97 |
tab-complete: target directory start LBA, low byte |
CMPDLH |
$1F98 |
tab-complete: target directory start LBA, high byte |
CMPDN |
$1F99 |
tab-complete: target directory sector count |
CMPCUR |
$1F9A |
tab-complete: saved line length (cursor) across the scan |
CMPSAV |
$1F9B |
tab-complete: saved SBUF entry cursor across a candidate (2) |
CMPWLB |
$1F9D |
tab-complete: directory-walk running sector LBA (2) |
CMPWSC |
$1F9F |
tab-complete: directory-walk sectors remaining |
CMPIX |
$1FA0 |
tab-complete: KWTAB index during the built-in scan |
Console tty state¶
| Name | Value | Notes |
|---|---|---|
TTYRAW |
$1FA1 |
0 = expand a bare LF to CR LF on console output; nonzero = pass bytes through untouched (for binary over the serial link, like stty raw) |
TTYLST |
$1FA2 |
last byte PUTC transmitted, so an LF that already follows a CR is not doubled |
TTYCH |
$1FA3 |
PUTC's saved character (PUTC must preserve A) |
Graphics presence¶
| Name | Value | Notes |
|---|---|---|
GFXPRES |
$1FA4 |
1 = GL card fitted (screen is the display); 0 = headless serial console |
Glass tty¶
| Name | Value | Notes |
|---|---|---|
GTCOL |
$1FA5 |
glass TTY cursor column (0..GTCOLS-1) |
GTROW |
$1FA6 |
glass TTY cursor row (0..GTROWS-1) |
GTSUSP |
$1FA7 |
nonzero = glass TTY suspended (a full-screen GL app owns the screen; CONOUT is serial-only) |
GCONEN |
$1FAF |
1 = glass TTY console ENABLED (CONOUT mirrors to the GL screen); 0 = off (serial-only, the default -- screen on enables it) |
GTXL |
$1FA8 |
glass TTY cursor pixel x, low byte (0..474, step 6) |
GTXH |
$1FA9 |
glass TTY cursor pixel x, high byte |
GTYL |
$1FAA |
glass TTY text-baseline pixel y (window, y-up), low byte |
GTYH |
$1FAB |
glass TTY text-baseline pixel y, high byte |
GTCH |
$1FAC |
glass TTY: the byte currently being drawn |
GTTMP |
$1FAD |
glass TTY: FIFO-push scratch (holds the byte across the backpressure wait) |
GTCNT |
$1FAE |
glass TTY: table-stream byte counter |