The Kernal is the C64's operating system ROM, occupying $E000–$FFFF (57344–65535). It provides a jump table at $FF81–$FFF3 that remains accessible regardless of the current ROM banking state. Always call through the jump table, never directly into ROM routines, as addresses may differ between ROM revisions.
Input: None
Output: Screen cleared, cursor home
Clobbers: A, X, Y
Use: Called automatically at reset; call to reinitialize screen
Input: None
Output: CIA chips, VIC-II, SID reset to defaults; IRQ enabled
Clobbers: A, X, Y
Input: None
Output: Zero page and stack cleared, tape buffer zeroed, memory size set
Clobbers: A, X, Y
Input: None
Output: RAM vectors at $0314-$0333 restored to Kernal defaults
Clobbers: A, X
Note: Vectors are copied from ROM table at $E518
Input: Carry clear → copy RAM vectors to user area ($FB/$FC points to destination)
Carry set → copy user area to RAM vectors ($FB/$FC points to source)
X = low byte, Y = high byte of 2-byte pointer at $FB/$FC
Output: Vectors read or written
Clobbers: A, X, Y
Input: A = flag byte
bit 7 = 1: enable BASIC error messages
bit 6 = 1: enable status messages (FOUND, SAVING, etc)
Output: Previous flag value in A
Input: Carry set → set: X = low byte, Y = high byte of new top address
Carry clear → read current top
Output: X = low byte, Y = high byte of top of memory
Input: Carry set → set: X = low byte, Y = high byte of new bottom
Carry clear → read current bottom
Output: X = low byte, Y = high byte of bottom of memory
Input: None
Output: X = number of columns (40), Y = number of rows (25)
Input: Carry set → set cursor: X = row (0-24), Y = column (0-39)
Carry clear → read cursor position
Output: (read) X = current row, Y = current column
Note: Cursor blink is reset
Input: A = character to output (PETSCII)
Output: Character sent to current output channel (screen or file)
Clobbers: A
Note: Also accessible via KERNAL vector at $0326/$0327
Input: None
Output: A = character received (PETSCII)
Note: Waits for input if keyboard; also accessible at $0324/$0325
Input: None
Output: A = character (0 if no character available)
Note: Non-blocking keyboard read; does not wait for keypress
Input: None
Output: Zero flag set if STOP key pressed
A = current value of keyboard row (matrix scan)
Note: Also restores registers if STOP held (calls CLRCH)
Input: A = logical file number (1-127)
X = device number (0=keyboard, 1=tape, 2=RS232, 3=screen, 4-7=serial, 8-15=disk)
Y = secondary address (0-15; 255=no secondary address)
Output: Values stored in OS variables ($B8, $B9, $BA, $BB)
Input: A = length of filename (0 if no name)
X = low byte of filename address
Y = high byte of filename address
Output: Filename parameters stored in OS variables
Input: Parameters set by SETLFS and SETNAM
Output: Carry clear = success
Carry set = error; A = error code
Error codes: 1=file open, 2=too many files, 4=file not found, 5=device not present, 6=not input file, 7=not output file
Input: A = logical file number to close
Output: File closed, channels freed
Clobbers: A, X, Y
Input: X = logical file number (must be open)
Output: Carry clear = success; subsequent BASIN reads from this file
Carry set = error; A = error code
Input: X = logical file number (must be open)
Output: Carry clear = success; subsequent BSOUT writes to this file
Carry set = error; A = error code
Input: None
Output: Default channels restored (keyboard input, screen output)
Clobbers: A, X
Input: None
Output: All logical files closed, channels restored to defaults
Clobbers: A, X
Input: A = 0 for LOAD, 1 for VERIFY
X = low byte of load address (if SA=0, ignored — file provides address)
Y = high byte of load address
SA (set by SETLFS): 0 = load to file's own start address
1 = load to X/Y address
Output: Carry clear = success; X = low byte, Y = high byte of last address+1
Carry set = error; A = error code
Note: Set SETLFS with SA=0 to load at the address stored in the file header
Input: A = zero-page address containing 2-byte start address pointer
X = low byte of end address + 1
Y = high byte of end address + 1
Output: Carry clear = success
Carry set = error; A = error code
Example:
LDA #<STARTADR
STA $C1
LDA #>STARTADR
STA $C2
LDA #$C1 ; ZP pointer to start address
LDX #<(ENDADR+1)
LDY #>(ENDADR+1)
JSR $FFD8
Input: None
Output: A = status byte (ST variable at $90)
Status bits:
Bit 0: Write timeout on serial bus
Bit 1: (serial) Byte lost
Bit 2: Short block (tape)
Bit 3: Long block (tape)
Bit 4: Unrecoverable read error (tape)
Bit 5: Checksum error (tape)
Bit 6: End of file
Bit 7: End of tape / device not present
Input: A = device address (8-30)
Output: Device commanded to LISTEN; subsequent bytes go to device
Input: A = device address (8-30)
Output: Device commanded to TALK; subsequent bytes come from device
Input: A = secondary address (0-31)
Output: Secondary address sent to listening device
Input: A = secondary address (0-31)
Output: Secondary address sent to talking device
Input: None (a device must be talking)
Output: A = byte received from serial bus
Input: A = byte to send (device must be listening)
Output: Byte sent to serial bus
Input: None
Output: UNTALK sent; talking device releases bus
Input: None
Output: UNLISTEN sent; all listening devices release bus
Input: A = high byte, X = middle byte, Y = low byte of 60Hz jiffy count
Input: None
Output: A = high byte, X = middle byte, Y = low byte of jiffy count
Note: Count increments 60× per second (NTSC) or 50× per second (PAL)
Input: None
Output: Jiffy count incremented; called by IRQ handler
The Kernal uses indirect vectors in RAM. These can be patched to intercept OS calls.
| Address | Vector | Default ROM target | Purpose |
|---|---|---|---|
| $0314-$0315 | CINV | $EA31 | IRQ handler |
| $0316-$0317 | CBINV | $FE66 | BRK handler |
| $0318-$0319 | NMINV | $FE47 | NMI handler |
| $031A-$031B | IOPEN | $F34A | OPEN routine |
| $031C-$031D | ICLOSE | $F2C7 | CLOSE routine |
| $031E-$031F | ICHKIN | $F20E | CHKIN routine |
| $0320-$0321 | ICHKOUT | $F250 | CHKOUT routine |
| $0322-$0323 | ICLRCH | $F333 | CLRCH routine |
| $0324-$0325 | IBASIN | $F157 | BASIN (CHRIN) |
| $0326-$0327 | IBSOUT | $F1CA | BSOUT (CHROUT) |
| $0328-$0329 | ISTOP | $F6ED | STOP test |
| $032A-$032B | IGETIN | $F13E | GETIN |
| $032C-$032D | ICLALL | $F32F | CLALL |
| $032E-$032F | USRCMD | $FE66 | User command |
| $0330-$0331 | ILOAD | $F4A5 | LOAD |
| $0332-$0333 | ISAVE | $F5ED | SAVE |
; Install hook on BSOUT to intercept all character output
SEI
LDA $0326
STA OLDBSOUT ; save original
LDA $0327
STA OLDBSOUT+1
LDA #<MYHOOK
STA $0326
LDA #>MYHOOK
STA $0327
CLI
RTS
MYHOOK ; A = character about to be output
; do your interception here
JMP (OLDBSOUT) ; chain to original routine
OLDBSOUT .WORD $F1CA
; Complete file write example
; 1. Set LFS
LDA #1 ; logical file 1
LDX #8 ; disk drive
LDY #2 ; SA=2 (write sequential)
JSR $FFBA ; SETLFS
; 2. Set filename
LDA #FNLEN
LDX #<FNAME
LDY #>FNAME
JSR $FFBD ; SETNAM
; 3. Open
JSR $FFC0 ; OPEN
BCS OERR ; branch on error
; 4. Redirect output
LDX #1 ; LFN
JSR $FFC9 ; CHKOUT
BCS CERR
; 5. Write data
LDX #0
WLOOP LDA DATA,X
BEQ DONE
JSR $FFD2 ; BSOUT
INX
BNE WLOOP
; 6. Clean up
DONE JSR $FFCC ; CLRCH
LDA #1
JSR $FFC3 ; CLOSE
RTS
OERR ; handle open error (A = code)
CERR ; handle channel error
FNAME .TEXT "MYFILE,S,W"
FNLEN = * - FNAME
DATA .TEXT "HELLO WORLD"
.BYTE 0
Powered by TurnKey Linux.