The C64 OS Programmer's Guide is being written

This guide is being written and released and few chapters at a time. If a chapter seems to be empty, or if you click a chapter in the table of contents but it loads up Chapter 1, that's mostly likely because the chapter you've clicked doesn't exist yet.

Discussion of development topics are on-going about what to put in this guide. The discusssions are happening in the C64 OS Community Support Discord server, available to licensed C64 OS users.

C64 OS PROGRAMMER'S GUIDE


Chapter 4: Using the KERNAL → Service (module)

The C64 OS Interrupt Service Routine

The service KERNAL module is responsible for implementing the operating system's interrupt service routine (ISR). In addition, it provides a variety of miscellaneous routines that are central to almost every kind of C64 OS program: Application, Utility, driver, library, etc.

The interrupt service routine has two possible starting points:

KERNAL ROM Patched In

If the KERNAL ROM is patched in when the IRQ occurs, the ROM provides the CPU's IRQ vector at $FFFE/$FFFF which points to a routine elsewhere within the KERNAL ROM. This routine then jumps through the vector in workspace memory at $0314/$0315.

KERNAL ROM Patched Out

If the KERNAL ROM is patched out when the IRQ occurs, the CPU's IRQ vector is installed in RAM at $FFFE/$FFFF which points to a routine in the service module of the C64 OS KERNAL. It performs a similar task to the KERNAL ROM, but directs through a secondary vector in workspace memory at $0312/$0313.

The RAM-based ISR temporarily patches the KERNAL ROM back in, and patches it out again before returning from the interrupt. Because of how this is setup, without needing to mask IRQs, your code is always safe to patch out the KERNAL ROM. Both routines are carefully timed reconvene on the shared interrupt service routine in the same amount of time: 29 cycles exactly.

Order of Interrupt Service Routine

  1. Service the Graphics Library
  2. Poll Devices
  3. Service SID Music
  4. Update Jiffy Clock
  5. Scan the STOP Key
  6. Update Time

Each of these stages has a number of sub-stages.

Graphics Library

When gfx.lib is loaded and linked, it installs itself in the ISR. The standard ISR calls the procgfx routine in the gfx.lib. If split screen is open, procgfx redirects the RAM and ROM IRQ vectors ($0312/$0313 and $0314/$0315 respectively) to point instead into the gfx.lib. On every frame, the gfx.lib toggles the vectors back and forth at the right times to service both the raster split screen and the system's standard ISR.

When the gfx.lib is unloaded, it automatically closes any split screen, exits any fullscreen graphics mode, and unhooks itself from the system's ISR. In your Application code, you only need to load and unload the gfx.lib like any other. Call its routines to configure the graphics context, and the library provide your Application with a user-configurable raster split screen. The interrupt service routines handle themselves. How to use the gfx.lib is covered under Advanced Topics.

Poll Devices

Polling devices is performed by the polldevices routine in the C64 OS KERNAL's input module.

Polldevices checks for an installed game controller driver, and calls the scan buttons routine of that driver. Game controllers do not result in queuing of events. They set standard button state values in memory for 1, 2, 3 or 4 controllers with up to 8 buttons each. A game is required to check these button state memory values as part of its main loop.

Next polldevices checks for an installed pointer driver, and calls routines in that driver to track the movement of the pointer and scan the device's buttons. If the pointer device's buttons are changing or are depressed, a full keyboard scan is not possible due to hardware constraints. Therefore, only a partial keyboard scan is performed for the CONTROL, COMMODORE, and LEFT-SHIFT modifier keys. Mouse events are synthesized from the changing states and enqueued.

If the pointer driver is not interfering with the keyboard lines, a full key scan is performed. Key command events and printable key events are enqueued.

Network device polling may be introduced to this routine in a future version.

SID Music

When sidplay.lib is loaded and linked, it is not immediately installed into the ISR. The ISR calls a SID play vector. By default this vector immediately returns, by being pointed to RAW_RTS. RAW_RTS is a stable place in workspace memory where an RTS is found.

When sidplay.lib is instructed to play, it copies a pointer to the loaded SID file's play routine into the SID play vector. To stop playing music, sidplay.lib silences the SID(s) and restores the SID play vector to RAW_RTS.

Update Jiffy Clock and Scan the STOP Key

Updating the Jiffy Clock and scanning the STOP key are both performed by calling UDTIM in the KERNAL ROM.

The state of the STOP key is updated on every call of the ISR. If the STOP key is depressed, with no other mitigating keys depressed at the same time, then $7f is written to workspace address $91. $7f remains in $91 for the next call of the ISR checks its state again and updates address $91. Calls to the STOP KERNAL routine return the value of $91 compared to #$7f.

Update Time

This stage performs several steps. It uses a single incrementing jiffy count (apart from the Jiffy Clock) to count out fractions of a minute, computed correctly for NTSC and PAL.

CPU Usage History is recorded in a rolling table of 10 increments over 10 seconds. Each record is a snapshot of how many jiffies have passed (from $00 to $ff) since the last time the main event loop was processed.

CPU Busy indicator is displayed and incremented one animation frame every 1/3rd of a second, whenever more than 127 jiffies have passed since the main event loop was processed. On PAL this means the CPU Busy indicator displays after 2.56 seconds of the UI being unresponsive. On NTSC it displays after 2.13 seconds. Display of the CPU Busy indicator is under control of the system redraw flags, rcpubusy.

The seconds indicator in the menu bar clock blinks once per second. The indicator is set to a full colon on every half second and cleared at the end of every full second. When the menu bar clock is visible, calculation of the blinking seconds indicator is always performed, but the blinking animation can be disabled by changing the clear state character stored in t_blinks. By setting t_blinks to a full colon, no change is visible.

The full time is redrawn every one minute. The service KERNAL module holds the drawing logic for the time of day clock, and supports 12H and 24H time. Display of the time is under control of the system redraw flags. The time is not updated (even to the screen buffer) while in fullscreen graphics mode. The time is not updated when the menu bar is hidden. And the time is not displayed or updated unless the rclock flag is set. This flag allows the user to turn the display of the clock on and off.

The system redraw flags can be changed by calling setflags. And you can manually request the menu bar clock to be redrawn by calling redrwtod.


alert

Purpose Draw the attention of the user by blinking the border twice.
Module offset 0
Communication registers None
Stack requirements 8 bytes
Zero page usage $02, $09, $d8, $d9
Registers affected A, X, Y
Input parameters None
Output parameters None

Description: This routine is used to visually draw the attention of the user. It does so by blinking the color of the border, twice. Call this routine whenever you want to indicate to the user that something they may have expected to work didn't work. If the user issues a keyboard command, and the key command event propagates its way through the whole system, it calls alert automatically to let the user know that the key command wasn't handled by anything.

The alert uses C64 OS KERNAL timers to schedule the changing of the border color. It changes the color from the current color to a complementary alternative color immediately, then sets a 100ms timer. When the timer elapses it restores the original color, and sets 100ms timer again. When the timer elapses it repeats this process once more.

The complementary colors are hard-coded according to the following table:

Initial Color Alternate Color
black dark grey
white yellow
red light red
cyan light blue
purple blue
green light green
blue light blue
yellow white
orange brown
brown orange
light red red
dark grey black
medium grey light grey
light green green
light blue blue
light grey medium grey


confirq

Purpose Configures the C64 OS interrupt service routine.
Module offset 3
Communication registers None
Stack requirements 2 bytes
Zero page usage None
Registers affected A, X, Y
Input parameters None
Output parameters None

Description: This routine is used to configure the CPU's NMI and IRQ vectors in RAM ($FFFA/$FFFB and $FFFE/$FFFE respectively) to point to the interrupt service routines in the C64 OS KERNAL's service module. It also configures the following workspace interrupt vectors:

Address Vector
$0312/$0313 IRQ when the KERNAL ROM is patched out
$0314/$0315 IRQ when the KERNAL ROM is patched in
$0318/$0319 NMI

This routine is one of the very last routines called by the booter (//os/:booter) and is used to transfer control from the KERNAL ROM to the C64 OS KERNAL. Under normal circumstances this routine never needs to be called. However, if a game or some other program replaces the C64 OS ISR with something custom, calling confirq restores C64 OS's standard control over the system.

For example, perhaps an Application embeds a custom FLI or IFLI image viewing routine. Such a routine requires very strict timing requirements. This routine could temporarily replace the ISR with a custom one. This could show the FLI image and still have time to scan for a key press to leave the viewing routine. Upon leaving the viewing routine, it can call confirq to re-establish C64 OS's control over the system and resume processing input, etc.


General System Routines

The following is a set of routines providing general system services. These routines are used to update the system-wide status bar, adjust the system's redraw flags, update the menu bar clock, enable graphics mode, process global keyboard shortcuts and prepare Applications, Utilties, libraries, drivers and more to be able to link to the KERNAL.

The alert routine should also be considered a general system service.

getargs

Purpose Manipulate the stack to handle inline arguments.
Module offset 6
Communication registers A
Stack requirements 2 bytes
Zero page usage $45, $46
Registers affected A, X
Input parameters A → total argument byte count
Output parameters argptr ← points to start of inline argument block

Description: This routine is used to manipulate the stack from within a routine that uses inline arguments. When that routine returns, execution proceeds on the first byte following the block of inline arguments. Getargs also configures the zero page argptr to point to the start of the inline arguments block.

Inline arguments are useful in certain cases where the number of parameters that need to be passed to a routine exceeds what can be passed in registers alone. A block of several 1 and 2 byte arguments can be put in the code following the JSR to the routine. A routine which takes inline arguments must call getargs before returning, or execution will continue by trying to execute the inline arguments as though they were code.

The following is an example of how to call the fictional routine dataread and supply it with two 16-bit inline arguments. The first is a pointer to buffer and the second is a length to read.