Skip to content

KERNAL I/O Routines

The Commodore 64's jump table for screen, keyboard, tape, disk and serial I/O

The last page of the C64's ROM holds 39 three-byte JMP instructions, the KERNAL jump table. Commodore promised the addresses would never move, so JSR $FFD2 prints a character on the PET, VIC-20, C64, Plus/4 and C128 alike, and ten of the jumps go through RAM so that cartridges and fastloaders can take them over.

JSR $FFD2 prints a character. It did so on the original Commodore PET, on the VIC-20, the Commodore 64, the Plus/4 and the C128, because Commodore made a promise about that address and kept it. The top page of each machine’s ROM holds a table of jump instructions, one for each operating-system service, and while the code behind them moved from machine to machine and from one ROM revision to the next, the table stayed where it was. Commodore called the whole arrangement the KERNAL.

The Commodore 64 Programmer’s Reference Guide sets out the reasoning. The KERNAL is “the operating system of the Commodore 64 computer” and, more precisely, “a standardized JUMP TABLE to the input, output, and memory management routines in the operating system”. The routines themselves “may change as the system is upgraded. But the KERNAL jump table will always be changed to match.” A program that calls the table rather than the code behind it keeps working “if the KERNAL or BASIC is changed”, and is easier “to translate … from one Commodore computer to another”.

The spelling is Commodore’s own. Jim Butterfield, writing in 1984, said its origin “seems to be lost in legend”: “it was originally an acronym, standing for something like ‘Keyboard Entry Read, Network and Link’”. He kept “Commodore’s word” rather than correct it to “kernel”.

Fast facts

  • Where: 39 entry points, three bytes apart, from $FF81 to $FFF3. The guide places the table “on the last page of memory, in read-only memory”.
  • What each entry is: a JMP instruction. Twenty-nine jump straight into ROM. Ten jump indirectly through addresses held in RAM at $031A–$032D, and LOAD and SAVE take their own RAM detours through $0330 and $0332.
  • How to call one: put the arguments in the registers, JSR to the table entry, and check the carry flag on return. A routine that fails sets carry and leaves an error number in the accumulator.
  • Devices: 0 keyboard, 1 Datassette, 2 RS-232, 3 screen, 4 printer, 8 disk drive. Device numbers run from 0 to 31, and “device numbers 4 or greater automatically refer to devices on the serial bus”.
  • Who else uses it: BASIC. Its OPEN, LOAD, SAVE, PRINT and GET go through the same table, so a program calling the KERNAL is using the same services BASIC does.

The common entry points

The names are the ones the Programmer’s Reference Guide uses.

Address Name What it does
$FF81 CINT Initialise the screen editor and the VIC-II chip
$FF84 IOINIT Initialise the two CIA chips, silence the SID and set up the processor port
$FF87 RAMTAS Clear zero page and pages 2 and 3, find the top of RAM without disturbing it, set up the tape buffer and put the screen at $0400
$FF8A RESTOR Put the RAM vectors back to their defaults
$FF8D VECTOR Read or replace the whole block of RAM vectors
$FFA5 ACPTR Read a byte from the serial bus
$FFA8 CIOUT Send a byte to the serial bus
$FFB7 READST Read the I/O status word (end of file, time-outs, device not present)
$FFBA SETLFS Set the logical file number, device number and secondary address
$FFBD SETNAM Set the file name: length in A, address in X (low) and Y (high)
$FFC0 OPEN Open a logical file
$FFC3 CLOSE Close a logical file
$FFC6 CHKIN Take input from an open file
$FFC9 CHKOUT Send output to an open file
$FFCC CLRCHN Go back to keyboard input and screen output
$FFCF CHRIN Read a byte from the input channel
$FFD2 CHROUT Write a byte to the output channel
$FFD5 LOAD Load from a device into memory
$FFD8 SAVE Save memory to a device
$FFE1 STOP Test the RUN/STOP key
$FFE4 GETIN Get a character; from the keyboard, returns 0 if the buffer is empty
$FFED SCREEN Return the screen size: 40 columns in X, 25 rows in Y
$FFF0 PLOT Read (carry set) or set (carry clear) the cursor position

Opening a channel

Most KERNAL I/O follows the same pattern as BASIC’s OPEN: name the file with SETNAM, give it a number, a device and a secondary address with SETLFS, call OPEN, then point input or output at it with CHKIN or CHKOUT. From then on CHRIN and CHROUT read and write that file instead of the keyboard and screen, until CLRCHN puts them back.

This program does the machine-code equivalent of OPEN 15,8,15:INPUT#15,... and prints the disk drive’s status message. It is written for the ACME assembler and starts at $C000 (49152), a block BASIC leaves alone; run it with SYS 49152.

Assembly
SETLFS = $ffba
SETNAM = $ffbd
OPEN   = $ffc0
CLOSE  = $ffc3
CHKIN  = $ffc6
CLRCHN = $ffcc
CHRIN  = $ffcf
CHROUT = $ffd2
READST = $ffb7

        * = $c000

        lda #0          ; no file name
        jsr SETNAM
        lda #15         ; logical file 15
        ldx #8          ; device 8, the first disk drive
        ldy #15         ; secondary address 15: the command channel
        jsr SETLFS
        jsr OPEN
        bcs done        ; carry set: error number in A
        ldx #15
        jsr CHKIN       ; take input from file 15
        bcs done
loop    jsr CHRIN       ; one byte from the drive
        jsr CHROUT      ; output is still the screen
        jsr READST
        beq loop        ; stop at end of file or on an error
done    jsr CLRCHN      ; keyboard and screen again
        lda #15
        jmp CLOSE       ; close file 15 and return

On a C64 with a 1541 that has just been switched on, it prints 73,CBM DOS V2.6 1541,00,00, the drive’s power-on message. READST returns a non-zero status once the drive has sent the last byte of the message, which ends the loop. The program assumes a drive is connected: with nothing on the serial bus, CHKIN sends the command to talk and then waits indefinitely for a reply.

The vectors in RAM

Ten of the table’s jumps are indirect: JMP ($0326) rather than JMP $F1CA. At power-on the KERNAL fills the vectors at $031A–$032D with the addresses of its own routines, and RESTOR puts them back. Because they are in RAM, a program can point any of them somewhere else, and every caller, BASIC included, follows.

Sheldon Leemon’s Mapping the Commodore 64 gives the purpose: “By changing the vectors to these routines, it is possible to support new I/O devices, such as an IEEE disk drive used through an adapter.” It also gives the warning. Some of these routines run under interrupts, so “it is dangerous to change these vectors without first turning off all interrupts”; the KERNAL’s VECTOR routine swaps the whole block at once.

Vector Routine
$031A OPEN
$031C CLOSE
$031E CHKIN
$0320 CHKOUT
$0322 CLRCHN
$0324 CHRIN
$0326 CHROUT
$0328 STOP
$032A GETIN
$032C CLALL
$0330 LOAD (after the table entry stores its arguments)
$0332 SAVE (likewise)

The LOAD vector is the one that mattered most in practice. A disk fastloader cartridge points $0330 at its own code, so an ordinary LOAD"NAME",8 runs the cartridge’s faster routine without the program or the user doing anything different. Changing $0328 changes what counts as the RUN/STOP key, which is how a program disables it.

Devices and the user port

Device 2, RS-232, is not a chip. The KERNAL does the serial conversion itself: its RS-232 “byte and bit level handlers run under the control of the 6526 CIA #2 device timers and interrupts”, and the signals come out on the user port, which is wired to that CIA, at 0 to 5 volt TTL levels rather than true RS-232 voltages. Opening an RS-232 channel also takes 512 bytes from the top of BASIC memory for its two buffers, and the guide warns that if the space is not free, “the end of your program will be destroyed”.

The screen (3) and keyboard (0) are devices like any other, so a program that writes through CHROUT can send the same output to a printer or a disk file by opening a different device and calling CHKOUT. BASIC’s CMD command does exactly this.

One table, several machines

The table is older than the C64. Even the original PET’s ROM has fifteen jumps at $FFC0–$FFEA, the block where the C64 keeps OPEN, CHKIN, CHROUT, GETIN and their neighbours, and later PET ROMs add more entries below it. The VIC-20’s table begins at $FF8A, without the three initialisation entries. The Plus/4 and C16 have all 39 at the C64’s addresses, and the C128 keeps them and adds nineteen more below $FF81. Butterfield could tell readers of his 1984 book that the routines “are always at the same addresses, and perform in about the same way regardless of which Commodore machine is used: PET, CBM, Commodore 64, PLUS/4, or VIC-20.”

The C64 added one routine to help: SCREEN, which returns the screen size so that a program can work out which machine it is on. The guide says it was “implemented on the Commodore 64 to help upward compatibility of your programs”.

Sources

  • Commodore 64 Programmer’s Reference Guide (Commodore Business Machines): the KERNAL and its jump table (pp. 267-268); power-up and the “indirect jump table in low memory” (p. 268); calling conventions and error returns (pp. 269-270); the table of user-callable routines (pp. 271-272); OPEN (pp. 287-288); READST and RESTOR (pp. 291-292); SCREEN (pp. 294-295); SETLFS and device numbers (p. 296); SETNAM (p. 298); VECTOR (pp. 303-305); RS-232 (pp. 347-348); user-port lines (Table 6-1, p. 354); the user port (p. 359).
  • Jim Butterfield, Machine Language for the Commodore 64 and Other Commodore Computers (Brady, 1984), pp. 24-25: the kernal routines across Commodore machines and the origin of the name.
  • Sheldon Leemon, Mapping the Commodore 64 and 64C (COMPUTE! Books, 1987), pp. 75-76: the KERNAL indirect vectors at 794-813 ($031A–$032D), ILOAD and ISAVE, and locations 808-809 (ISTOP).
  • Commodore 64 BASIC ROM (901226-01) and KERNAL ROM (901227-03); PET, VIC-20, Plus/4 and C128 KERNAL ROMs: the jump tables, the indirect entries, SCREEN, IOINIT and CINT, and BASIC’s calls into the table. The status-message program was run on these ROMs with an emulated 1541.

See also

Checked. Verified against primary sources and signed off by a person.