Commodore 128/Memory Model
The Commodore 128 uses a MOS 8722 Memory Management Unit (MMU) to control memory mapping. The CPU (MOS 8502) has a 16-bit address bus and can only address 64 KB at a time; the MMU provides bank switching to access the full 128 KB of RAM and to control ROM and I/O visibility. The MMU can change the upper 8 bits of addresses on the bus on the fly. It works in tandem with a MOS 8721 PLA, which is similar to the C64 PLA.
Information Sources[edit | edit source]
- C128 schematics
- MOS 8722 datasheet
- PLA logic equations
- ms3: C128/C64 mode flag (1=C128)
- ms2: I/O select (derived from MMU CR bit 0. 0 = I/O visible to CPU at $D000)
- ms1/ms0: ROM bank select
Startup Address Map[edit | edit source]
These are the default settings after startup. MMU and PLA can be used to select other configurations.
| CPU Address | Default Content at Power-Up |
|---|---|
$0000-$03FF |
Common RAM (always Bank 0) |
$0400-$3FFF |
RAM (Bank 0) |
$4000-$7FFF |
BASIC Lo ROM (16 KB, U33) |
$8000-$BFFF |
BASIC Hi ROM (16 KB, U34) |
$C000-$CFFF |
Editor ROM (4 KB, in U35) |
$D000-$D3FF |
VIC-IIe registers |
$D400-$D7FF |
SID registers |
$D500-$D50B |
MMU registers |
$D600-$D6FF |
VDC registers |
$D700-$D7FF |
Reserved |
$D800-$DBFF |
Color RAM |
$DC00-$DCFF |
CIA 1 |
$DD00-$DDFF |
CIA 2 |
$DE00-$DFFF |
Reserved / expansion |
$E000-$FFFF |
KERNAL ROM (8 KB, in U35) |
$FF00-$FF04 |
MMU shadow registers |
Startup Sequence[edit | edit source]
On initial power up, the Z80 is active, checks GAME/EXROM lines, and hands over control to the MOS 8502 in C128 MMU mode if neither line is active, or in C64 MMU mode otherwise. This was done because some legacy C64 cartridges put data on the data bus based on the active address (rather than expansion port ROML/ROMH/IO1/IO2 lines), interfering with $Fxxx accesses. The Z80 starts up from $0xxx addresses, which works around that problem.
MMU and the 8721 PLA[edit | edit source]
The C128 uses a MOS 8721 PLA, which is a modified version of the C64 PLA. The MMU and PLA work together to control memory mapping:
- The MMU sends signals to the PLA: 128/64 (mode select), I/O SEL, ROMBANKHI, ROMBANKLO. These replace the processor port's C64-style LORAM/HIRAM/CHAREN role — the PLA uses these MMU signals to determine ROM/RAM and I/O CPU visibility in C128 mode.
- The processor port bits 0-2 from the 8502 still connect to the PLA, but are repurposed for video-related functions in C128 mode.
- The PLA has a CLRBNK output that selects the active color RAM bank ($D800-$DBFF). Processor port bit 0 selects the CPU color bank (during AEC=1), bit 1 selects the VIC color bank (during AEC=0).
In C64 mode (MCR bit 6 = 1), the PLA reverts to standard C64 behavior, with processor port bits 0-2 functioning as C64 LORAM/HIRAM/CHAREN.
PLA ROM Chip Select Outputs[edit | edit source]
| Output | Chip | Contents |
|---|---|---|
| rom1 | U32 | C64 BASIC + KERNAL ROM |
| rom2 | U33 | C128 BASIC Lo ROM ($4000-$7FFF)
|
| rom3 | U34 | C128 BASIC Hi ROM ($8000-$BFFF)
|
| rom4 | U35 | C128 Screen editor and KERNAL ($C000-$FFFF) and Z80 BIOS (CP/M, 4KB at $D000-$DFFF / $0000-$0FFF in Z80 mode)
|
| from | U36 | Internal function ROM |
| charom | U18 | Character ROM, 8KB (two 4KB charsets²) |
² The active 4k charset is selected by the 128/64 MMU output line by default. [1] By changing a jumper, the CAPSLK key can be used instead.
The rom_256 input selects between 128Kbit and 256Kbit ROM configurations:
- 128Kbit (default,
rom_256=1): Separate 16KB ROMs — U32 (rom1), U33 (rom2), U34 (rom3), U35 (rom4). - 256Kbit (
rom_256=0): Consolidated 32KB ROMs. U32 holds rom1+rom4 (CS driven by the 128/64 mode signal), U34 holds rom2+rom3 (A14 connected to CS to select between the two 16KB halves). U33 and U35 are not populated.
MMU Register Set (C128 mode)[edit | edit source]
All registers are accessible at two locations:
- $D500-$D50B: In the I/O range (available when CR bit 0 = 0)
- $FF00-$FF04: Always mapped, independent of ROM/RAM/IO settings
$D500 / $FF00 — CR (Configuration Register)[edit | edit source]
The main register controlling the current memory layout. Bits 7-6 select the active RAM bank; bits 5-0 control ROM/RAM/IO visibility for each address region.
"Internal function ROM" references U36, "external function ROM" refers to an expansion port ROM cartridge.
Bit 7-6: RAM Bank Select
00 = Bank 0 (default)
01 = Bank 1
10 / 11 = reserved (future expansion? MMU 8722 features CAS0/CAS1 which only supports 2x64k RAM though)
Bit 5-4: $C000-$FFFF (High ROM/RAM)
00 = System ROM (KERNAL + Editor)
01 = Internal Function ROM
10 = External Function ROM
11 = RAM
Bit 3-2: $8000-$BFFF (Mid ROM/RAM)
00 = System ROM (BASIC Hi)
01 = Internal Function ROM
10 = External Function ROM
11 = RAM
Bit 1: $4000-$7FFF (Low Rom/RAM)
0 = System ROM (BASIC Lo)
1 = RAM
Bit 0: $D000-$DFFF (Chargen / I/O)
0 = I/O (VIC, SID, MMU, CIAs, Color RAM, etc.)
1 = ROM/RAM (Char ROM or RAM per bit 5-4)
When bit 0 is 0 (I/O mode), the I/O region overrides the high ROM/RAM setting for $D000-$DFFF regardless of bits 5-4.
When bit 0 is 1 (ROM/RAM mode), the content of $D000-$DFFF is determined by bits 5-4: 00 = Char ROM, 11 = RAM.
The power-up default is $00 (all ROMs active, I/O enabled, Bank 0).
Just like in the C64 mode, CPU writes to areas that have ROM mapped get redirected to RAM.
Interaction between bit 0 (CG) and bits 5-4 (High) for $D000-$DFFF:
| CG (bit 0) | High (bits 5-4) | $D000-$DFFF content |
|---|---|---|
| 0 | any | I/O space (MMU, VIC, SID, CIAs, etc.) |
| 1 | 00 | Character ROM |
| 1 | 01 | Internal Function ROM |
| 1 | 10 | External Function ROM |
| 1 | 11 | RAM |
Interaction between bit 0 (CG) and bits 5-4 (High) for $E000-$FFFF:
| CG (bit 0) | High (bits 5-4) | $E000-$FFFF content |
|---|---|---|
| 0 | 00 | KERNAL ROM |
| 0 | 01 | Internal Function ROM |
| 0 | 10 | External Function ROM |
| 0 | 11 | RAM |
| 1 | 00 | KERNAL ROM |
| 1 | 01 | Internal Function ROM |
| 1 | 10 | External Function ROM |
| 1 | 11 | RAM |
Note: bits 5-4 always control $E000-$FFFF regardless of bit 0. Bit 0 only overrides the mapping for $D000-$DFFF (replacing ROM/RAM with I/O).
$D501-$D504 — PCRA through PCRD (Preconfiguration Registers)[edit | edit source]
Four storage registers that hold pre-programmed CR values. Each has the same bit layout as the CR. They cannot directly change the memory configuration; they are used in conjunction with the LCR registers at $FF01-$FF04.
- Writing to PCRA-PCRD stores a value.
- Reading PCRA-PCRD returns the stored value.
- These registers only exist in the I/O range ($D500 area), NOT at $FF01-$FF04.
$FF01-$FF04 — LCRA through LCRD (Load Configuration Registers)[edit | edit source]
Trigger registers for the preconfiguration system.
- Writing to any LCR copies the corresponding PCR value into the CR, immediately activating that memory configuration. The written value itself is discarded.
- Reading any LCR returns the value of the corresponding PCR.
$D505 — MCR (Mode Configuration Register)[edit | edit source]
Bits 7/5/4/3 are I/O pins and have no internal MMU logic effect. Technically, those pins support output and input operation; however, bits 7/5/4 are input by convention, and bit 3 is output. Bits 6/0 influence MMU internal logic.
Bit 7: 40/80 key state (read)
0 = key pressed
1 = key released
Bit 6: C64 Mode (writable)
0 = C128 mode
1 = C64 mode
Changes MMU bus arbitration and PLA signaling.
Bit 5: EXROM (expansion port EXROM line, read)
Bit 4: GAME (expansion port GAME line, read)
Bit 3: FSDIR (Fast Serial Direction, writable)
0 = fast serial output
1 = fast serial input
Bit 2-1: Unused (read as 1)
Bit 0: Z80/8502 CPU select (writable)
0 = Z80 active
1 = 8502 active
Changes MMU bus arbitration and PLA signaling.
Z80 Bus Arbitration and PLA Behavior[edit | edit source]
When MCR bit 0 is cleared (Z80 active), bus arbitration hands control to the Z80A CPU. Two PLA inputs control this:
- z80en (
/BUSACK, active low): Asserted when the Z80 has the bus. Connected to Z80A/BUSRQSTand 8502AECvia logic gates. When the Z80 has the bus, the 8502's AEC is pulled low (high impedance), disconnecting it from the address/data bus. (unsure) - z80io (
/IORQST): Z80A pin 20, asserted during Z80 I/O cycles. (unsure)
When the Z80 is active (z80en=0), the PLA remaps the address space for the Z80's CP/M environment:
- System ROM appears at
$C000-$FFFF(Z80 BIOS). (unsure) - During Z80 I/O cycles (
z80io=0), system ROM (U35) is also mapped at$0000-$0FFF. (unsure)
$D506 — RCR (RAM Configuration Register)[edit | edit source]
Controls the Common Area (shared RAM visible across all banks) and the VIC-IIe video bank selection.
Bit 7-6: Video Bank
X0 = VIC accesses Bank 0 / lower 64k RAM (default)
X1 = VIC accesses Bank 1 / upper 64k RAM
(Bit 7 is reserved/unused in standard 128K systems)
Bit 5-4: Unused
Bit 3: Shared RAM High
Bit 2: Shared RAM Low
Together these control the location of the Common Area:
00 = No Common Area
01 = Common Area at bottom of CPU address range ($0000 upward)
10 = Common Area at top of CPU address range ($FFFF downward)
11 = Common Area split: part at bottom, part at top
Bit 1-0: Shared RAM Size
00 = 1 KB (default)
01 = 4 KB
10 = 8 KB
11 = 16 KB
Power-up default: $00 (VIC Bank 0, 1 KB common at bottom).
Common Area behavior:
- The Common Area is always accessed from Bank 0, regardless of which bank is selected by CR bits 7-6.
- The page-pointer (P0) for zero page and stack pointer (P1) for the stack page take priority over the Common Area. If P0 or P1 remap an address, the Common Area does not apply to that address.
- For all other addresses within the Common Area range, the Common Area overrides the CR-based ROM/RAM mapping, forcing access to Bank 0 RAM.
- With location
01and size 1 KB, addresses$0000-$03FFalways access Bank 0. This is the default and ensures the zero-page and first part of the stack are always visible. - The zero-page pointer (P0) and stack pointer (P1) can further refine mapping within the Common Area (see above).
VIC Bank selection:
- The VIC-IIe chip fetches display data from RAM independently of the CPU.
- Bit 6 of the RCR selects which 64 KB RAM bank the VIC reads from.
- CIA 2 Port A bits 1-0 provide additional address bits (VA14, VA15) to the VIC, selecting a 16 KB window within the 64 KB VIC bank.
$D507 / $D508 — P0L / P0H (Page 0 Pointer: Zero Page)[edit | edit source]
Remaps the CPU zero page ($00-$FF) to any 256-byte page in either RAM bank.
Note: $0000 (data direction register) and $0001 (processor port) are on-chip I/O registers and are not affected by zero page remapping. They always read/write the physical 8502 port regardless of P0 settings.
$D507 (P0L): Bits 7-0 = A15-A8 of the target page address
$D508 (P0H): Bits 3-0 = A19-A16 (bank select, lower 2 bits used for 128K)
Bits 7-4 = unused (read as 1)
The remapped zero page is at physical address: (P0H[3:0] << 16) | (P0L << 8) | (address & $FF)
P0H must be written before P0L. The P0L write latches the value and activates the new mapping.
Power-up default: P0L = $00, P0H = $F0 (zero page at Bank 0, page 0).
$D509 / $D50A — P1L / P1H (Page 1 Pointer: Stack)[edit | edit source]
Remaps the CPU stack page ($0100-$01FF) to any 256-byte page in either RAM bank.
$D509 (P1L): Bits 7-0 = A15-A8 of the target page address
$D50A (P1H): Bits 3-0 = A19-A16 (bank select, lower 2 bits used for 128K)
Bits 7-4 = unused (read as 1)
Same sequencing rule as P0: P1H must be written before P1L.
Power-up default: P1L = $01, P1H = $F0 (stack at Bank 0, page 1).
$D50B — VR (Version Register, Read-Only)[edit | edit source]
Bit 7-4: Bank version
2 = 128 KB (two 64 KB banks)
Bit 3-0: MMU version
0 = first revision
Always reads as $20 on a standard C128.
Default Memory Configurations (BASIC BANK Command)[edit | edit source]
The BASIC 7.0 BANK n command writes to $FF00 to switch configurations:
| BANK | $FF00 value | Description |
|---|---|---|
| 0 | $3F |
All RAM, Bank 0 |
| 1 | $7F |
All RAM, Bank 1 |
| 2 | $BF |
All RAM, Bank 2 (expansion) |
| 3 | $FF |
All RAM, Bank 3 (expansion) |
| 4 | $16 |
RAM $0000-$7FFF (Bank 0), I/O $D000, Int Func ROM $C000-$FFFF |
| 5 | $56 |
Same as 4 but Bank 1 |
| 6 | $96 |
Same as 4 but Bank 2 |
| 7 | $D6 |
Same as 4 but Bank 3 |
| 8 | $2A |
RAM $0000-$7FFF (Bank 0), I/O $D000, Ext Func ROM $C000-$FFFF |
| 9 | $6A |
Same as 8 but Bank 1 |
| 10 | $AA |
Same as 8 but Bank 2 |
| 11 | $EA |
Same as 8 but Bank 3 |
| 12 | $06 |
RAM $0000-$7FFF (Bank 0), I/O, KERNAL ROM, Int Func ROM |
| 13 | $0A |
RAM $0000-$7FFF (Bank 0), I/O, KERNAL ROM, Ext Func ROM |
| 14 | $01 |
RAM $0000-$3FFF (Bank 0), Char ROM, KERNAL ROM, BASIC ROM |
| 15 | $00 |
RAM $0000-$3FFF (Bank 0), I/O, KERNAL ROM, BASIC ROM |
Processor Port $0001[edit | edit source]
The 8502 has an on-chip I/O port at $0001 (data direction register at $0000). In C128 mode its function differs significantly from the C64's 6510 processor port.
C128 Mode Bit Definitions[edit | edit source]
Bit 7: Unknown (reads as 0 at power-up)
Bit 6: CAPS LOCK sense (from keyboard)
Bit 5: Cassette motor (1=off, 0=on) — connected to cassette port as on C64
Bit 4: Cassette sense (1=switch open, 0=switch closed) — connected to cassette port
Bit 3: Cassette data output — connected to cassette port as on C64
Bit 2: Character ROM visible to VIC during DMA (charen, PLA input)
0 = char ROM visible to VIC-IIe during DMA fetches
1 = char ROM hidden from VIC-IIe during DMA fetches
Set to 0 by KERNAL in text mode, 1 in graphics/bitmap mode.
Bit 1: VIC color RAM bank select (hiram, PLA input)
1 = VIC-IIe sees color RAM bank 1 (default)
0 = VIC-IIe sees color RAM bank 0
Bit 0: CPU color RAM bank select (loram, PLA input, PLA CLRBNK output)
1 = CPU sees color RAM bank 1 (default, used for text mode in BASIC V7)
0 = CPU sees color RAM bank 0
Bits 3-5 connect directly to the cassette port, identical to the C64. Bit 6 goes to a CAPS LOCK sense line from the keyboard. Bits 0-2 go to the PLA as loram/charen/hiram inputs; in C128 mode (ms3=1) the PLA uses them for video functions rather than ROM banking.
CPU and VIC can see different color banks simultaneously. The CPU color bank (bit 0) applies during CPU cycles (AEC=1), while the VIC color bank (bit 1) applies during VIC DMA cycles (AEC=0). This allows software to build graphics in one color bank while displaying from the other.
In C128 mode, the processor port does NOT control ROM/RAM mapping. That function is entirely handled by the MMU CR register at $D500/$FF00. The MMU drives the PLA via ROMBANKHI/ROMBANKLO/I/O SEL signals, repurposing bits 0-2 for video functions.
The C128 has two independent 1 KB color RAM banks at $D800-$DBFF. BASIC V7 uses bank 1 for text mode colors; bank 0 is available for other purposes (e.g., graphics modes).
Address Resolution Order[edit | edit source]
When the CPU accesses an address in C128 mode, it is resolved in this priority order:
- $0000/$0001: Always mapped to 8502 data direction/processor port.
- $FF00-$FF04: Always mapped to MMU shadow registers, regardless of any other setting. This ensures the system can always regain control of memory mapping.
- Zero page remap (P0): If the address is in
$00-$FF, it is redirected to the page specified by P0H/P0L in the specified bank. - Stack page remap (P1): If the address is in
$0100-$01FF, it is redirected to the page specified by P1H/P1L in the specified bank. - Common Area (RCR): If the Common Area is enabled and the address falls within it, the access goes to Bank 0 RAM. The Common Area takes priority over the CR-based mapping but does NOT override the zero-page/stack remapping.
- CR-based mapping: For all other addresses, the CR bits determine the source/destination:
- Bank select (bits 7-6) chooses the active RAM bank
- Per-region ROM/RAM/I/O bits select what is visible
MMU and the VIC-IIe[edit | edit source]
The VIC-IIe video chip accesses RAM independently of the CPU:
- The VIC always has a 14-bit address space (16 KB window).
- CIA 2 Port A bits 1-0 select which 16 KB window the VIC uses within its bank.
- MMU RCR bit 6 selects whether the VIC's 64 KB bank is Bank 0 or Bank 1.
- The VIC does NOT see general-purpose ROM (BASIC/KERNAL) — it can only access RAM and character ROM.
- Character ROM is visible to the VIC-IIe at offsets $1000, $5000, $9000, and $D000 within its 64 KB bank (i.e. at system addresses $1000, $5000, $9000, $D000 — the PLA does not gate on va14/va15 in C128 mode). Visibility can be disabled by setting processor port bit 2 (charen=1).