This is a software-based 6502 computer, not a Commodore 64 emulator. It
uses ASCII (not PETSCII), a flat 64K address space (no memory banking), and a
small set of memory-mapped devices. This document reflects the actual kernel
(kernel.asm) and BASIC (basic.asm) source and the linker configs
(memory.cfg, basic_memory.cfg).
| Address Range | Size | Purpose |
|---|---|---|
$0000-$00FF |
256 B | Zero page — shared between EhBASIC and the monitor (see split below) |
$0100-$01FF |
256 B | Stack — grows down from $01FF |
$0200-$03FF |
512 B | System variables — BASIC page-2 vars + monitor variables/buffers |
$0400-$07E7 |
1000 B | Screen RAM — 40×25 text ($07E8-$07FF is unused padding) |
$0800-$8FFF |
~34 KB | Free RAM — user programs; BASIC program/variables/strings when BASIC runs; the assembler reserves $8000-$8FFF (source) and $7E00-$7FFF (symbols) while building |
$9000-$AFFF |
8 KB | DOS ROM — always-mapped MFC-DOS resident ROM (FAT16 filesystem; DOS shell later) |
$B000-$DFFF |
12 KB | Module window — bank 0 = RAM, banks 1..255 = ROM modules (BASIC is bank 1) |
$E000-$FFFF |
8 KB | Kernel ROM (monitor) |
$FE00-$FE28 |
— | PIA I/O + MODULE_BANK ($FE23) + block-device registers ($FE24-$FE28) — within the kernel region |
There is no VIC-II / SID / CIA / color memory. The screen is plain RAM at
$0400 rendered by the host display; the keyboard and file I/O are exposed
through a small PIA-style register block at $FE00. The $B000-$DFFF window is
a bank-switched module slot: the MODULE_BANK register ($FE23) selects
RAM (bank 0) or one of up to 255 pre-loaded ROM modules. See
module_slot_design.md. The $9000-$AFFF DOS ROM is an always-mapped (never
banked) read-only region holding the resident filesystem; see dos_design.md.
The monitor's zero-page variables were relocated to $14-$39 to avoid the
EhBASIC interpreter, which uses page zero heavily. The split:
| Range | Owner | Notes |
|---|---|---|
$00-$13 |
EhBASIC | warm-start vector, USR vector, FAC temporaries, etc. |
$14-$39 |
Monitor | see table below (free when BASIC is not the active workspace) |
$3A-$5A |
free | unused gap |
$5B-$FF |
EhBASIC | descriptor stack, program/var/array/string pointers, FACs, PRNG, decimal workspace |
| Address | Symbol | Purpose |
|---|---|---|
$14-$15 |
MON_CURRADDR_LO/HI |
Current memory address |
$16-$17 |
MON_MSG_PTR_LO/HI |
Message string pointer |
$18-$19 |
JUMP_VECTOR |
Indirect jump / ZP pointer scratch |
$1A-$1B |
SCREEN_PTR_LO/HI |
Current screen position |
$1C-$1D |
SCRL_SRC_ADDR_LO/HI |
Scroll source |
$1E-$1F |
SCRL_DEST_ADDR_LO/HI |
Scroll destination |
$20 |
SCRL_BYTE_CNT |
Scroll byte counter |
$21 |
CMD_LINE_COUNT |
Lines printed by current command (paging) |
$22 |
PAGE_ABORT_FLAG |
Set when ESC pressed during paging |
$23 |
RNG_SEED |
PRNG seed |
$24 |
RNG_MAX |
PRNG max value |
$25-$34 |
HEX_LOOKUP_TABLE |
16-byte hex digit table |
$35-$36 |
DEC_TEMP_LO/HI |
Decimal-conversion temp |
$37 |
DEC_DIGIT_IDX |
Decimal digit index |
$38-$39 |
DEC_RESULT_LO/HI |
Decimal-conversion result |
System stack, growing downward from $01FF. The stack pointer is initialized
to $FF at reset.
When BASIC is running it owns the low part of this region; the monitor's variables live above it. The monitor command buffer overlaps BASIC's area but the two never run at the same time.
| Range | Owner | Purpose |
|---|---|---|
$0200-$020C |
EhBASIC | I/O vectors (ccflag, VEC_IN/VEC_OUT/VEC_LD/VEC_SV) |
$0221-$0268 |
EhBASIC | input line buffer (Ibuff) |
$0200-$024F |
Monitor | MON_CMDBUF — 80-byte command input buffer (overlaps BASIC; mutually exclusive) |
$0269-$028D |
Monitor | monitor variables (relocated above BASIC's $0268) — see below |
$028E-$02DD |
Monitor | MON_LAST_CMD_BUF — 80-byte last-command buffer (. recall) |
$02DE-$03FF |
free | available system RAM |
| Address | Symbol | Purpose |
|---|---|---|
$0269 |
MON_CMDPTR |
Command buffer position |
$026A |
MON_CMDLEN |
Command length |
$026B |
MON_MODE |
Monitor mode (0=Command, 1=Write) |
$026C-$026D |
MON_STARTADDR_LO/HI |
Range start address |
$026E-$026F |
MON_ENDADDR_LO/HI |
Range end address |
$0270 |
MON_PARSE_PTR |
Parser position |
$0271 |
MON_PARSE_LEN |
Remaining parse length |
$0272 |
MON_HEX_TEMP |
Hex conversion temp |
$0273 |
MON_BYTE_COUNT |
Byte counter |
$0274 |
MON_LINE_COUNT |
Display line counter |
$0275 |
MON_ERROR_FLAG |
Error flag |
$0276 |
CURSOR_X |
Cursor X (0-39) |
$0277 |
CURSOR_Y |
Cursor Y (0-24) |
$0278 |
MON_MSG_TMP_POS |
Temp message position |
$0279 |
MON_FILL_VALUE |
Fill (F:) byte value |
$027A-$027B |
MON_DEST_ADDR_LO/HI |
Move/copy (M:) destination |
$027C |
MON_COPY_MODE |
Move/copy mode (0=copy, 1=move) |
$027D-$028C |
MON_SEARCH_PATTERN |
Search (X:) pattern, up to 16 bytes |
$028D |
MON_PATTERN_LEN |
Search pattern length |
Note: DEC_DIGIT_BUFFER ($027D) deliberately aliases MON_SEARCH_PATTERN —
the D:/H: and X: commands never run at the same time.
| Range | Purpose |
|---|---|
$0400-$07E7 |
1000 bytes — 40×25 character display (written as ASCII) |
$07E8-$07FF |
24 bytes — unused padding to the page boundary |
The I/O page sits at $FE00-$FEFF, inside the kernel ROM region (the kernel just
avoids placing code there). It was moved here from the old $DC00 so the
$B000-$DFFF region is a clean, I/O-free, bank-switched module slot (see
module_slot_design.md).
A single PIA-style device provides keyboard input and host file I/O. There are
two file models: block (kernel L:/S: — whole memory range in/out) and
byte stream (BASIC LOAD/SAVE — one byte at a time via the data register).
Separately, a block device ($FE24-$FE28) presents a host disk.img as
512-byte sectors — the storage layer beneath the MFC-DOS FAT16 filesystem (see
dos_design.md); it is independent of the PIA file models above.
| Address | Register | Purpose |
|---|---|---|
$FE00 |
PIA_DATA |
Keyboard data (read consumes a key) |
$FE02 |
PIA_CONTROL |
Status flags (bit 0 = data available) |
$FE10 |
FILE_COMMAND |
File op: load/save (block), open-read/open-write/close (stream) |
$FE11 |
FILE_STATUS |
Idle / in-progress / success / stream-open / EOF / error |
$FE12-$FE13 |
FILE_ADDR_LO/HI |
Block load/save target/start address |
$FE14-$FE1F |
FILE_NAME_BUF |
Filename buffer (12 bytes) |
$FE20-$FE21 |
FILE_END_ADDR_LO/HI |
Block save end address |
$FE22 |
FILE_DATA |
Byte-stream data register (read next / write byte) |
$FE23 |
MODULE_BANK |
Module bank select: 0 = RAM, 1..255 = ROM module mapped at $B000-$DFFF |
$FE24-$FE25 |
BLK_LBA |
Block device: 16-bit sector number (little-endian) |
$FE26 |
BLK_CMD |
Block device: 1 = read sector, 2 = write sector |
$FE27 |
BLK_STATUS |
Block device: 0 = ready, $FF = error |
$FE28 |
BLK_DATA |
Block device: 512-byte sector data port (auto-incrementing) |
| Segment | Range | Purpose |
|---|---|---|
CODE |
$E000-$EF62 (3939 B) |
Monitor code and data |
IORESV |
$FE00-$FEFF (256 B) |
Reserved I/O page (PIA + MODULE_BANK) |
JUMPS |
$FF00-$FF1D (30 B) |
Kernel API jump table (10 entries) |
VECS |
$FFFA-$FFFF (6 B) |
Interrupt/reset vectors |
| (free) | ~$EF63-$FDFF |
~3.6 KB unused |
| Address | Symbol | Routine |
|---|---|---|
$FF00 |
K_PRINT_CHAR |
PRINT_CHAR |
$FF03 |
K_PRINT_MESSAGE |
PRINT_MESSAGE |
$FF06 |
K_PRINT_NEWLINE |
PRINT_NEWLINE |
$FF09 |
K_GET_KEYSTROKE |
GET_KEYSTROKE |
$FF0C |
K_CLEAR_SCREEN |
CLEAR_SCREEN |
$FF0F |
K_GET_RAND_NUM |
GET_RANDOM_NUMBER |
$FF12 |
K_RETURN_MODULE |
RETURN_FROM_MODULE — unmaps the bank, returns to monitor (BASIC BYE) |
$FF15 |
K_READ_LINE |
READ_COMMAND_LINE — edited line input (backspace/ESC) → MON_CMDBUF/MON_CMDLEN |
$FF18 |
K_PARSE_HEX |
HEX_QUAD_TO_ADDR — X = offset in MON_CMDBUF → MON_CURRADDR, carry set if invalid |
$FF1B |
K_PRINT_HEX_BYTE |
PRINT_HEX_BYTE — print A as two hex digits |
The jump table is also the module ABI: a ROM module reaches kernel services
only through these entries, so it is independent of where the kernel's internal
routines live. The $FF15/$FF18 services share the monitor's command buffer
(MON_CMDBUF) and MON_CURRADDR as scratch — safe because the monitor is
suspended while a module runs and that state is saved/restored across the launch.
A bank-switched slot selected by MODULE_BANK ($FE23). Bank 0 is RAM (the
boot/default state, zeroed by RESET); banks 1..255 are read-only ROM modules
pre-loaded by the host. The kernel owns a MODULE_DIR catalog (bank #, entry
address, name); the B: menu lists it and, on selection, writes MODULE_BANK
and JMPs to the module entry. A module exits with JMP $FF12, which unmaps
the bank.
BASIC is module bank 1. EhBASIC 2.22p5 with project additions; cold start
(LAB_COLD) is at $B000. BASIC I/O is routed through the kernel via the
page-2 vectors (VEC_IN/OUT → keyboard/screen; VEC_LD/SV → the
file-stream LOAD/SAVE routines).
| Address | Vector | Handler |
|---|---|---|
$FFFA-$FFFB |
NMI | NMI_HANDLER (currently a bare RTI) |
$FFFC-$FFFD |
RESET | RESET (power-on entry) |
$FFFE-$FFFF |
IRQ | IRQ_HANDLER (currently a bare RTI) |
$3A-$5A— small free zero-page gap (fast addressing) when BASIC is not in use.$02DE-$03FF— leftover system-variable space.$0800-$8FFF— main user RAM (~34 KB). Avoid$0400-$07E7(screen) and$9000-$AFFF(DOS ROM). When BASIC is active this is its program/variable/string space (Ram_base=$0800,Ram_top=$9000). The assembler reserves the top of this region while building ($8000-$8FFFsource,$7E00-$7FFFsymbols).
| Symbol | Value | Purpose |
|---|---|---|
STACK_TOP |
$FF |
Initial stack pointer |
SCREEN_START |
$0400 |
Start of screen RAM |
SCREEN_WIDTH |
40 |
Characters per line |
SCREEN_HEIGHT |
25 |
Lines on screen |
LINES_PER_PAGE |
24 |
Paging threshold |