# 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 ```asm ; 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 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:** ```asm 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 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 ```asm 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 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 ```asm READ_FILE: LDA #3 LDX #8 LDY #2 ; secondary address 2 = read JSR $FFBA LDA #FNAME_LEN LDX #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: ```asm INSTALL: 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 ```