# Commodore 64 — Complete Memory Map Reference > Source: Compute's Mapping the Commodore 64; The Anatomy of the Commodore 64 --- ## Quick-Reference Regions | Range | Size | Description | |---------------|-------|----------------------------------------------| | $0000–$00FF | 256 B | Zero Page (fastest addressing) | | $0100–$01FF | 256 B | CPU Stack | | $0200–$03FF | 512 B | OS/BASIC work area (vectors, tables) | | $0400–$07FF | 1 KB | Default screen text matrix (40×25) | | $0800–$9FFF | 38 KB | BASIC program area (default) | | $A000–$BFFF | 8 KB | BASIC ROM (or cartridge/RAM under) | | $C000–$CFFF | 4 KB | Upper RAM (ML friendly) | | $D000–$DFFF | 4 KB | I/O space (VIC-II/SID/CIA/Color RAM) | | $E000–$FFFF | 8 KB | Kernal ROM (or RAM under) | --- ## Zero Page — $0000–$00FF (Selected) | Address | Label | Description | |---------|----------|--------------------------------------------------| | $00 | R6510 | 6510 I/O direction register | | $01 | R6510+1 | 6510 I/O port (RAM/ROM banking + cassette) | | $02 | UNUSED | Free zero-page byte | | $03–$04 | ADRAY1 | Convert float→integer vector | | $05–$06 | ADRAY2 | Convert integer→float vector | | $07 | CHARAC | Search character for BASIC scanner | | $08 | ENDCHR | Flag: scan to end of BASIC line | | $09 | TRMPOS | Terminal position for tab | | $0A | VERCKK | LOAD (0) or VERIFY (1) flag | | $0B | COUNT | Input buffer character count | | $0C | DIMFLG | Default (0) or DIM flag | | $0D | VALTYP | Type flag: 0=float, $FF=string | | $0E | INTFLG | Integer flag | | $0F | GARBFL | DATA scan / LIST quote / garbage collect flag | | $10 | SUBFLG | Subscript flag for FN | | $11 | INPFLG | INPUT (0) or GET ($40) or READ ($98) | | $12 | TANSGN | TAN sign / comparison flag | | $13 | CHANNL | Current I/O channel (0=keyboard/screen) | | $14–$15 | LINNUM | Integer line number | | $16 | TEMPPT | Pointer: next temp string | | $17–$18 | LASTPT | Pointer: last temp string | | $19–$21 | TEMPST | Temp string descriptor stack | | $22–$25 | INDEX1 | Utility pointer area | | $26–$2A | FACEXP | Floating-point accumulator #1 | | $2B–$2C | TXTTAB | Pointer: start of BASIC text ($0801 default) | | $2D–$2E | VARTAB | Pointer: start of BASIC variables | | $2F–$30 | ARYTAB | Pointer: start of BASIC arrays | | $31–$32 | STREND | Pointer: end of arrays (+1) | | $33–$34 | FRETOP | Pointer: bottom of string storage | | $35–$36 | FRESPC | Utility pointer for strings | | $37–$38 | MEMSIZ | Pointer: top of memory ($A000 default) | | $39–$3A | CURLIN | Current BASIC line number | | $3B–$3C | OLDLIN | Previous BASIC line number | | $3D–$3E | OLDTXT | Pointer: previous BASIC statement | | $3F–$40 | DATLIN | Line number of current DATA statement | | $41–$42 | DATPTR | Pointer into current DATA statement | | $43–$44 | INPPTR | Pointer: INPUT source | | $45–$46 | VARNAM | Current variable name | | $47–$48 | VARPNT | Pointer: current variable value | | $49–$4A | FORPNT | Pointer: FOR/NEXT variable | | $4B–$4E | OPPTR | Math operator work area | | $52–$55 | FAC2 | Floating-point accumulator #2 | | $56 | ARGSGN | Sign comparison, FAC1 vs FAC2 | | $57–$58 | ARISGN | Sign of result | | $59 | FACOV | FAC1 overflow byte | | $5A–$5B | FBUFPT | Pointer: BASIC print buffer | | $61–$66 | FAC | FAC1 (5 bytes) | | $69–$6E | ARG | FAC2 (5 bytes) | | $90 | STATUS | Kernal I/O status (ST) word | | $91 | STKEY | STOP key flag ($7F = pressed) | | $93 | VERCK | LOAD/VERIFY flag (Kernal) | | $94 | C3PO | Cassette: 1=write leader sent | | $95 | BSOUR | Cassette: byte to write | | $9A | SXREG | Serial bus I/O address | | $9B | RDFNM | Record/file number | | $9C | RDEVNM | Record device | | $9D | RDSEC | Record secondary address | | $9E | RIRQSC | RS-232 recv IRQ state | | $9F | STATUS2 | RS-232 status | | $A0–$A2 | TIME | 60Hz jiffy clock (24-bit, MSB=$A0) | | $A3–$A4 | PCTR | Cassette sync / block count | | $B0–$B5 | EAL | Kernal: device address / sector pointers | | $BA | DFLTN | Default input device (0=keyboard) | | $BB | DFLTO | Default output device (3=screen) | | $BC | PRTY | Cassette parity byte | | $BD | DPSW | Cassette EOT flag | | $BE | MYCH | Tape character in assembly | | $BF | CAS1 | Cassette read error correction | | $C0 | FSBLK | Cassette read: first block flag | | $C1–$C2 | MXPNT | Pointer: maximum BASIC variable | | $C3–$C4 | SXPTR | Pointer to tape buffer or screen for cassette | | $C5 | LSTX | Matrix code of last key pressed | | $C6 | NDX | Keyboard buffer index (# chars) | | $C7 | RVS | Reverse mode flag (1=on) | | $C8 | INDX | Pointer: end of logical input line | | $C9–$CA | LXSP | Cursor column and row at I/O start | | $CB | SFDX | Flag: shift/commodore key | | $CC | BLNSW | Cursor blink enable (0=on) | | $CD | BLNCT | Countdown to cursor blink | | $CE | GDBLN | Character under cursor (saved) | | $CF | BLNON | Last blink state (0=off) | | $D0 | CRSW | INPUT/GET mode (0=INPUT) | | $D1–$D2 | PNT | Pointer: current screen line address | | $D3 | PNTR | Cursor column on current line | | $D4 | QTSW | Quoted mode flag | | $D5 | LNMX | Logical line length (39 or 79) | | $D6 | TBLX | Current cursor row | | $D7 | DATA | Last character output to screen | | $D8 | INSRT | Insert mode count | | $D9–$F2 | LDTB1 | Screen line link table (25 entries) | | $F3–$F4 | USER | Pointer: base of color RAM ($D800) | | $F5–$F6 | KEYD | Pointer: keyboard decode table | | $F7–$F8 | RIBUF | RS-232 input buffer pointer | | $F9–$FA | ROBUF | RS-232 output buffer pointer | | $FB–$FE | FREKZP | Free zero-page bytes (user/ML) | | $FF | BASZPT | BASIC: temp for float-to-string | --- ## Page 1 — CPU Stack ($0100–$01FF) The 6510 stack grows **downward** from $01FF. SP register holds low byte of current top. Do not use this region for data storage in time-critical IRQs — pushes/pulls compete with JSR/RTS. --- ## Page 2 — OS Work Area ($0200–$02FF, Selected) | Address | Label | Description | |-------------|----------|--------------------------------------------------| | $0200–$0258 | BUF | BASIC/Kernal input buffer (89 bytes) | | $0259–$0262 | LAT | Logical file number table (10 entries) | | $0263–$026C | FAT | Device number table (10 entries) | | $026D–$0276 | SAT | Secondary address table (10 entries) | | $0277–$0280 | KEYD | Keyboard buffer (10 bytes) | | $0281–$0282 | MEMSTR | Start of memory pointer ($0800 default) | | $0283–$0284 | MEMSIZ | Top of memory ($A000 default) | | $0285 | TIMOUT | Serial IEEE timeout flag | | $0286 | COLOR | Current foreground color code | | $0287 | BG COL | Color under cursor | | $0288 | HIBASE | Screen RAM page ($04 = $0400) | | $0289 | XMAX | Keyboard buffer max size (10 default) | | $028A | RPTFLG | Key repeat flag ($40=repeat all) | | $028B | KOUNT | Repeat delay counter | | $028C | DELAY | Repeat speed counter | | $028D | SHFLAG | Keyboard shift flags | | $028E | LSTSHF | Previous shift state | | $028F–$0290 | KEYLOG | Pointer: keyboard decode logic | | $0291 | MODE | Shift+Commodore mode (0=switch, 128=lock) | | $0292 | AUTODN | Screen scroll enable (0=on) | | $0293–$0294 | M51CTR | RS-232 control register (fake 6551) | | $0295–$0296 | M51CDR | RS-232 command register | | $0297–$0298 | M51AJB | RS-232 baud rate (non-standard) | | $0299–$029A | RIDBE | RS-232 buffer pointers | | $029B–$02FF | | Other RS-232/cassette work areas | --- ## Page 3 — Vectors ($0300–$03FF) | Address | Default Points To | Description | |-------------|-------------------|--------------------------------------| | $0300–$0301 | $E38B | IERROR — BASIC error message vector | | $0302–$0303 | $A483 | IMAIN — BASIC main loop vector | | $0304–$0305 | $A57C | ICRNCH — Tokenize input line vector | | $0306–$0307 | $A71A | IQPLOP — List a token vector | | $0308–$0309 | $A7E4 | IGONE — Execute a statement vector | | $030A–$030B | $AE86 | IEVAL — Evaluate expression vector | | $030C | $0 | SAREG — .A saved on IRQ | | $030D | $00 | SXREG — .X saved on IRQ | | $030E | $00 | SYREG — .Y saved on IRQ | | $030F | $00 | SPREG — SP saved on IRQ | | $0310–$0311 | $FE43/$8B | USRPOK — USR() vector (JMP + addr) | | $0312–$0313 | $EA31 | IRQ vector (vectored from $FFFE) | | $0314–$0315 | $EA31 | Hardware IRQ vector (CINV) | | $0316–$0317 | $FE66 | BRK handler vector (CBINV) | | $0318–$0319 | $FE47 | NMI vector (NMINV) | | $031A–$031B | $F34A | IOPEN — OPEN routine vector | | $031C–$031D | $F291 | ICLOSE — CLOSE routine vector | | $031E–$031F | $F20E | ICHKIN — CHKIN routine vector | | $0320–$0321 | $F250 | ICKOUT — CKOUT routine vector | | $0322–$0323 | $F333 | ICLRCH — CLRCH routine vector | | $0324–$0325 | $F157 | IBASIN — BASIN/CHRIN routine vector | | $0326–$0327 | $F1CA | IBSOUT — BSOUT/CHROUT routine vector | | $0328–$0329 | $F6ED | ISTOP — STOP key check vector | | $032A–$032B | $F13E | IGETIN — GETIN routine vector | | $032C–$032D | $F32F | ICLALL — CLALL routine vector | | $032E–$032F | $FE66 | IUSRTO — User-defined load vector | | $0330–$0331 | $F549 | ILOAD — LOAD routine vector | | $0332–$0333 | $F685 | ISAVE — SAVE routine vector | --- ## Screen & Color RAM ($0400–$07FF / $D800–$DBFF) | Region | Address | Notes | |---------------|---------------|---------------------------------------------| | Screen Matrix | $0400–$07E7 | 1000 bytes: 40 cols × 25 rows (default) | | Unused | $07E8–$07FF | 24 bytes free | | Color RAM | $D800–$DBE7 | 1000 bytes, nibble per cell (low 4 bits) | | Color unused | $DBE8–$DBFF | 24 bytes | **Screen address formula:** `addr = $0400 + (row × 40) + col` --- ## BASIC Work Area ($0800–$9FFF) - **$0800–$0801** — BASIC start: 2-byte link ($00 $08 if empty) - **$0801–$9FFF** — 38,911 bytes available to BASIC programs - BASIC ROM at $A000 interprets tokenized lines starting at $0801 - `POKE 43,1 : POKE 44,8` resets BASIC start pointer --- ## Upper RAM ($C000–$CFFF) 4 KB of RAM, never overlaid by ROM. Ideal location for: - ML subroutines called from BASIC - Custom IRQ handlers - Character set data --- ## I/O Space ($D000–$DFFF) | Range | Chip | Function | |---------------|---------|-------------------------------------| | $D000–$D3FF | VIC-II | Video Interface Controller | | $D400–$D7FF | SID | Sound Interface Device | | $D800–$DBFF | Color | Color RAM (nibbles) | | $DC00–$DCFF | CIA #1 | Keyboard, joysticks, IRQ timer | | $DD00–$DDFF | CIA #2 | Serial bus, VIC bank, NMI timer | | $DE00–$DEFF | I/O #1 | Cartridge expansion port I/O area 1 | | $DF00–$DFFF | I/O #2 | Cartridge expansion port I/O area 2 | When HIRAM=0 or banking overrides I/O region, character ROM or RAM appears instead. --- ## Banking Control — $0001 (6510 I/O Port) | Bit | Name | 0 = … | 1 = … | |-----|---------|----------------------|---------------------------| | 0 | LORAM | BASIC ROM disabled | BASIC ROM at $A000 enabled | | 1 | HIRAM | Kernal ROM disabled | Kernal ROM at $E000 enabled| | 2 | CHAREN | Char ROM at $D000 | I/O at $D000 enabled | | 3 | CASS WR | Cassette write low | Cassette write high | | 4 | CASS SW | (read only) | Cassette sense line | | 5 | CASS MT | Cassette motor on | Cassette motor off | **Common values:** - `$37` (binary 00110111) — default: BASIC+Kernal+I/O visible - `$36` — Kernal+I/O visible, BASIC RAM underneath - `$35` — I/O visible, both ROMs as RAM - `$34` — all RAM, no I/O (character ROM at $D000) - `$33` — Kernal ROM, character ROM at $D000, no I/O --- ## Kernal ROM ($E000–$FFFF, Selected Routines) | Address | Label | Function | |---------|----------|---------------------------------------| | $E003 | — | Kernal version/signature bytes | | $EA31 | IRQRTL | Standard IRQ handler entry | | $E544 | PLOT | Read/set cursor position | | $E566 | IOBASE | Return CIA base address in .XY | | $FF81 | SCINIT | Initialize VIC/screen editor | | $FF84 | IOINIT | Initialize CIA chips | | $FF87 | RAMTAS | RAM test and zero page init | | $FF8A | RESTOR | Restore default vectors | | $FF8D | VECTOR | Read/set system vectors | | $FF90 | SETMSG | Set Kernal message flag | | $FF93 | SECOND | Send secondary address after LISTEN | | $FF96 | TKSA | Send secondary address after TALK | | $FF99 | MEMTOP | Read/set top of memory | | $FF9C | MEMBOT | Read/set bottom of memory | | $FF9F | SCNKEY | Scan keyboard | | $FFA2 | SETTMO | Set serial bus timeout | | $FFA5 | ACPTR | Receive byte from serial bus | | $FFA8 | CIOUT | Send byte to serial bus | | $FFAB | UNTLK | Command device to UNTALK | | $FFAE | UNLSN | Command device to UNLISTEN | | $FFB1 | LISTEN | Command device to LISTEN | | $FFB4 | TALK | Command device to TALK | | $FFB7 | READST | Read I/O status word | | $FFBA | SETLFS | Set logical/device/secondary address | | $FFBD | SETNAM | Set filename address and length | | $FFC0 | OPEN | Open logical file | | $FFC3 | CLOSE | Close logical file | | $FFC6 | CHKIN | Set input channel | | $FFC9 | CKOUT | Set output channel | | $FFCC | CLRCH | Restore default I/O channels | | $FFCF | CHRIN | Input character from current channel | | $FFD2 | CHROUT | Output character to current channel | | $FFD5 | LOAD | Load file from device | | $FFD8 | SAVE | Save file to device | | $FFDB | SETTIM | Set jiffy clock | | $FFDE | RDTIM | Read jiffy clock | | $FFE1 | STOP | Check STOP key | | $FFE4 | GETIN | Get character from keyboard buffer | | $FFE7 | CLALL | Close all channels and files | | $FFEA | UDTIM | Increment jiffy clock | | $FFED | SCREEN | Return screen dimensions | | $FFF0 | PLOT | Read/set cursor X/Y position | | $FFF3 | IOBASE | Return I/O base address | | $FFFA | — | NMI vector (points into Kernal) | | $FFFC | — | RESET vector ($FCE2) | | $FFFE | — | IRQ/BRK vector ($FF48) | --- ## VIC-II Bank Selection (via CIA #2, $DD00) | Bits 0–1 | VIC Bank | Address Range | |----------|----------|----------------| | 11 | 0 | $0000–$3FFF | | 10 | 1 | $4000–$7FFF | | 01 | 2 | $8000–$BFFF | | 00 | 3 | $C000–$FFFF | **Default:** Bank 0 ($0000–$3FFF) — screen at $0400, charset at $1000 (ROM image). Character ROM is mirrored into VIC banks 0 and 2 at offsets $1000 and $1800. --- ## Sprite Memory Layout Each sprite = 64 bytes (63 data + 1 padding). Pointer bytes stored at screen RAM + $03F8 (8 bytes). | Pointer Value | Sprite Data Address (Bank 0) | |---------------|------------------------------| | $0D | $0340 | | $0E | $0380 | | $0F | $03C0 | | $80 | $2000 | | $81 | $2040 | | … | … | `sprite_addr = bank_base + (pointer_value × 64)`