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
$FF81to$FFF3. The guide places the table “on the last page of memory, in read-only memory”. - What each entry is: a
JMPinstruction. Twenty-nine jump straight into ROM. Ten jump indirectly through addresses held in RAM at$031A–$032D, andLOADandSAVEtake their own RAM detours through$0330and$0332. - How to call one: put the arguments in the registers,
JSRto 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,PRINTandGETgo 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.
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 returnOn 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);READSTandRESTOR(pp. 291-292);SCREEN(pp. 294-295);SETLFSand 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),ILOADandISAVE, 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,IOINITandCINT, and BASIC’s calls into the table. The status-message program was run on these ROMs with an emulated 1541.