Commodore 64 Kernal Jump Table — Complete Reference
Source: Compute's VIC-20 & C64 Tool Kit: Kernal; The Anatomy of the Commodore 64
All addresses are jump table entries (3-byte JMP instructions pointing into the Kernal ROM). These addresses are stable across all C64 revisions — always call through the jump table, never jump directly into ROM.
Quick Usage Template
; Open file: device 8, secondary 2, name "DATA"
LDA #1 ; logical file number
LDX #8 ; device number (8 = disk)
LDY #2 ; secondary address
JSR $FFBA ; SETLFS
LDA #4 ; filename length
LDX #<fname ; filename address low
LDY #>fname ; filename address high
JSR $FFBD ; SETNAM
JSR $FFC0 ; OPEN
fname: .BYTE "DATA"
Complete Jump Table
$FF81 — SCINIT: Initialize Screen Editor and VIC-II
- Input: None
- Output: None
- Destroys: A, X, Y
- Notes: Clears screen, sets default colors, initializes VIC-II registers. Called at RESET.
$FF84 — IOINIT: Initialize I/O Devices
- Input: None
- Output: None
- Destroys: A, X, Y
- Notes: Initializes CIA #1 and #2, serial bus, RS-232. Called at RESET.
$FF87 — RAMTAS: Test RAM and Initialize Pointers
- Input: None
- Output: None
- Destroys: A, X, Y
- Notes: Tests RAM, sets HIRAM/LORAM, initializes $0000–$00FF. Called at RESET.
$FF8A — RESTOR: Restore Default System Vectors
- Input: None
- Output: None
- Destroys: A, X, Y
- Notes: Copies ROM default vectors into $0314–$0333 (IRQ, OPEN, CLOSE, etc.)
$FF8D — VECTOR: Read/Set Vectored System Routines
- Input: .C = 0 to write vectors from user table; .C = 1 to read vectors to user table. .XY = pointer to 2×16-byte table (alternating load/store pairs)
- Output: None
- Notes: Batch-sets or batch-reads the $0314–$0333 vector block.
$FF90 — SETMSG: Set Kernal Error/Status Message Flag
- Input: .A = flag value (bit 6 = error messages on; bit 7 = I/O messages on)
- Output: None
- Destroys: A
$FF93 — SECOND: Send Secondary Address After LISTEN
- Input: .A = secondary address (ORed with $60 internally)
- Output: None; carry set on timeout
- Destroys: A
- Notes: Must follow a LISTEN call.
$FF96 — TKSA: Send Secondary Address After TALK
- Input: .A = secondary address (ORed with $60 internally)
- Output: None; carry set on timeout
- Destroys: A
- Notes: Must follow a TALK call.
$FF99 — MEMTOP: Read or Set Top of Memory
- Input: .C = 1 to read; .C = 0 to set (pass address in .XY)
- Output: .XY = top of memory (if reading)
- Destroys: A
$FF9C — MEMBOT: Read or Set Bottom of Memory
- Input: .C = 1 to read; .C = 0 to set (pass address in .XY)
- Output: .XY = bottom of memory (if reading)
- Destroys: A
$FF9F — SCNKEY: Scan Keyboard Matrix
- Input: None
- Output: Updates keyboard buffer at $0277; last key matrix code at $C5; shift flags at $028D
- Destroys: A, X, Y
- Notes: Normally called by the IRQ handler 60×/sec. Call manually if IRQs are disabled.
$FFA2 — SETTMO: Set Serial Bus Timeout
- Input: .A = 0 to disable timeout; non-zero to enable
- Output: None
$FFA5 — ACPTR: Receive Byte From Serial Bus
- Input: Device must already be TALKing (TALK + TKSA already sent)
- Output: .A = received byte; STATUS ($90) updated
- Destroys: A
- Notes: Check STATUS after each byte. ST bit 1 set = EOI (last byte in file).
$FFA8 — CIOUT: Send Byte to Serial Bus
- Input: .A = byte to send; device must be LISTENing
- Output: STATUS updated
- Destroys: A
$FFAB — UNTLK: Send UNTALK to Serial Bus
- Input: None
- Output: None
- Destroys: A
- Notes: Releases currently TALKing device.
$FFAE — UNLSN: Send UNLISTEN to Serial Bus
- Input: None
- Output: None
- Destroys: A
- Notes: Releases currently LISTENing device.
$FFB1 — LISTEN: Command Serial Device to LISTEN
- Input: .A = device number (4–30; 8=disk, 4=printer)
- Output: None; carry set on timeout
- Destroys: A
$FFB4 — TALK: Command Serial Device to TALK
- Input: .A = device number
- Output: None; carry set on timeout
- Destroys: A
$FFB7 — READST: Read I/O Status Word
- Input: None
- Output: .A = STATUS byte ($90)
- Destroys: A
STATUS bits:
| Bit | Meaning |
|-----|------------------------------------------|
| 0 | Write timeout (serial bus out) |
| 1 | Read timeout / EOI received |
| 2 | (RS-232) short block |
| 3 | (RS-232) long block |
| 4 | (RS-232) receive buffer empty / cassette |
| 5 | Checksum error |
| 6 | End of file (EOI on tape read) |
| 7 | End of tape / device not present |
$FFBA — SETLFS: Set Logical File, Device, Secondary Address
- Input: .A = logical file number (1–127 typical); .X = device number; .Y = secondary address (255=$FF = no secondary)
- Output: None
Device numbers:
| # | Device |
|---|---------------|
| 0 | Keyboard |
| 1 | Cassette |
| 2 | RS-232 |
| 3 | Screen |
| 4 | Printer |
| 5 | Printer #2 |
| 8 | Disk drive |
| 9 | Disk drive #2 |
$FFBD — SETNAM: Set Filename
- Input: .A = length (0 for no name); .XY = address of filename (X=low, Y=high)
- Output: None
- Notes: For disk: name can include drive number prefix like “0:MYFILE,S,R”. Length 0 for LOAD without name (loads tape program).
$FFC0 — OPEN: Open a Logical File
- Input: Preceded by SETLFS and SETNAM
- Output: Carry set on error; .A = error code (1=file open, 2=too many files, 6=not input, etc.)
- Destroys: A, X, Y
- Notes: Must CLOSE file when done. Max 10 open files.
$FFC3 — CLOSE: Close a Logical File
- Input: .A = logical file number
- Output: None
- Destroys: A, X, Y
$FFC6 — CHKIN: Set Input Channel
- Input: .X = logical file number (must be already OPENed)
- Output: Carry set on error; .A = error
- Destroys: A, X
- Notes: Redirects CHRIN/GETIN to this file. Call CLRCH to restore keyboard input.
$FFC9 — CKOUT: Set Output Channel
- Input: .X = logical file number
- Output: Carry set on error
- Destroys: A, X
- Notes: Redirects CHROUT/BSOUT to this file.
$FFCC — CLRCH: Restore Default I/O Channels
- Input: None
- Output: None
- Destroys: A, X
- Notes: Resets input to keyboard (device 0), output to screen (device 3).
$FFCF — CHRIN: Input Character from Current Channel
- Input: None
- Output: .A = character received; STATUS updated
- Destroys: A
- Notes: Waits for character if keyboard. Returns PETSCII code. Check STATUS after for EOF.
$FFD2 — CHROUT / BSOUT: Output Character to Current Channel
- Input: .A = character to output (PETSCII)
- Output: STATUS updated
- Destroys: A (X and Y preserved)
- Notes: Most commonly called Kernal routine. Outputs to current CKOUT channel (screen by default). Also vectored via $0326.
Useful PETSCII codes for CHROUT:
| Code | Effect |
|------|---------------------|
| $0D | RETURN (newline) |
| $11 | Cursor down |
| $13 | HOME |
| $14 | DELETE |
| $1D | Cursor right |
| $91 | Cursor up |
| $93 | CLEAR |
| $9D | Cursor left |
| $12 | Reverse on |
| $92 | Reverse off |
$FFD5 — LOAD: Load a File from Device
- Input: Preceded by SETLFS and SETNAM. .A = 0 (load) or 1 (verify). .XY = load address (used only if secondary address = 0, meaning override file's own load address)
- Output: Carry set on error; .A = error code; .XY = address of last byte loaded + 1
- Destroys: A, X, Y
- Notes: With secondary address 1, file loads to its original address. With secondary address 0, .XY overrides load address.
$FFD8 — SAVE: Save a File to Device
- Input: Preceded by SETLFS and SETNAM. .A = zero-page address containing start address (2 bytes). .XY = end address + 1
- Output: Carry set on error
- Destroys: A, X, Y
- Notes: .A is a zero-page pointer to the 2-byte start address, not the start address itself.
Example — Save $C000–$CFFF:
LDA #<$C000
STA $FB ; store start in ZP
LDA #>$C000
STA $FC
LDA #1 ; logical file
LDX #8 ; disk
LDY #1 ; secondary address
JSR $FFBA ; SETLFS
LDA #6
LDX #<savename
LDY #>savename
JSR $FFBD ; SETNAM
LDA #$FB ; ZP pointer to start address
LDX #<$D000 ; end+1 low
LDY #>$D000 ; end+1 high
JSR $FFD8 ; SAVE
savename: .BYTE "MYFILE"
$FFDB — SETTIM: Set Jiffy Clock
- Input: .A = high byte, .X = mid byte, .Y = low byte (24-bit value at 60Hz units)
- Output: None
$FFDE — RDTIM: Read Jiffy Clock
- Input: None
- Output: .A = high byte, .X = mid byte, .Y = low byte
- Notes: Clock wraps after ~18.2 hours (16,777,215 jiffies). Updated 60× per second by IRQ.
$FFE1 — STOP: Check STOP Key
- Input: None
- Output: .Z = 1 if STOP key pressed; .A = keyboard row scan byte
- Destroys: A, X
- Notes: If STOP pressed, also calls CLRCH. Use to allow program abort.
$FFE4 — GETIN: Get Character from Keyboard Buffer
- Input: None
- Output: .A = character (0 if buffer empty); STATUS updated
- Destroys: A, X, Y
- Notes: Non-blocking. Returns 0 immediately if no key waiting. For file input, same as CHRIN.
$FFE7 — CLALL: Close All Files and Channels
- Input: None
- Output: None
- Destroys: A, X
- Notes: Clears open file table and resets I/O to defaults. Does NOT send UNLISTEN/UNTALK to devices.
$FFEA — UDTIM: Update Jiffy Clock
- Input: None
- Output: None
- Notes: Increments 3-byte clock at $A0–$A2. Called by the default IRQ handler. If you replace the IRQ, call this manually to maintain timing.
$FFED — SCREEN: Return Screen Dimensions
- Input: None
- Output: .X = number of columns (40), .Y = number of rows (25)
$FFF0 — PLOT: Read or Set Cursor Position
- Input: .C = 0 to set cursor (X=column, Y=row); .C = 1 to read cursor position
- Output: .X = cursor column, .Y = cursor row (when reading)
- Destroys: A (X, Y preserved when setting)
$FFF3 — IOBASE: Return Base Address of I/O Devices
- Input: None
- Output: .XY = base address of CIA #1 ($DC00)
- Notes: Useful for position-independent code.
Hardware Vectors (ROM, read-only)
| Address |
Vector Name |
Default Points To |
| $FFFA |
NMI vector |
$FE43 (Kernal NMI handler) |
| $FFFC |
RESET vector |
$FCE2 (Kernal cold start) |
| $FFFE |
IRQ/BRK |
$FF48 (Kernal IRQ entry) |
The Kernal IRQ entry at $FF48 pushes registers and then jumps through the RAM vector at $0314 — that is what you override to install custom IRQ handlers.
Complete File I/O Pattern (ML)
Write Sequential File to Disk
WRITE_FILE:
; Set up file
LDA #2 ; logical file #2
LDX #8 ; disk drive
LDY #2 ; secondary address 2 = write
JSR $FFBA ; SETLFS
LDA #FNAME_LEN
LDX #<FNAME
LDY #>FNAME
JSR $FFBD ; SETNAM
JSR $FFC0 ; OPEN
BCS WRITE_ERR ; carry set = error
; Redirect output
LDX #2
JSR $FFC9 ; CKOUT
; Write bytes
LDY #0
WLOOP:
LDA DATA,Y
JSR $FFD2 ; CHROUT
INY
CPY #DATA_LEN
BNE WLOOP
; Close
JSR $FFCC ; CLRCH
LDA #2
JSR $FFC3 ; CLOSE
RTS
WRITE_ERR:
RTS
Read Sequential File from Disk
READ_FILE:
LDA #3
LDX #8
LDY #2 ; secondary address 2 = read
JSR $FFBA
LDA #FNAME_LEN
LDX #<FNAME
LDY #>FNAME
JSR $FFBD
JSR $FFC0 ; OPEN
BCS READ_ERR
LDX #3
JSR $FFC6 ; CHKIN
RLOOP:
JSR $FFCF ; CHRIN
STA BUFFER,Y ; store to buffer
INY
JSR $FFB7 ; READST
AND #$40 ; EOF bit
BEQ RLOOP
JSR $FFCC ; CLRCH
LDA #3
JSR $FFC3 ; CLOSE
RTS
READ_ERR:
RTS
Intercepting BSOUT (Custom Character Output)
To redirect all screen output (PRINT, CHROUT) through your own routine:
INSTALL:
LDA #<MY_BSOUT
STA $0326 ; patch BSOUT vector
LDA #>MY_BSOUT
STA $0327
RTS
MY_BSOUT:
; .A = character to output
; Do custom processing here, then fall through to original:
JMP $F1CA ; Kernal BSOUT (bypassing vector)
REMOVE:
LDA #$CA ; restore default ($F1CA)
STA $0326
LDA #$F1
STA $0327
RTS