Written and Maintained by Gregory Nacu

C64 OS: Technical Documentation

Last modified: Jul 31, 2017

Introduction

Why An OS?

I have had a fascination with OSes going back to the early 90s when I first discovered GEOS 1.2 for my C64c. I was never a fan of Microsoft but I really liked Windows 3.1, it had a charm, novelty and simplicity that peaked my curiosity. But I probably became most enamoured when I first played around with a Performa running MacOS 8.5. On the other hand, I never liked Windows 95 or its spiritual heirs, 98, ME, or 2000. And XP was particularly ugly and boring in my opinion. But to each his own, of course.

I learned to use a Unix command line when I was in high school in the mid to late 90s. I was using my c128D with a Turbo232 and a 33.6K—and later 56K—modem to dial into a shell account at a local ISP. Vanessa Ezekowitz helped me learn about bash and how to manage a Unix command line. When WiNGs (née JOS) first caught my attention in the early 2000s I became obsessed with OSes. They have the ability to empower a computer to do more than any one programmer would ever want to do on their own for just one program.

But there are some downsides to OSes. They add overhead and take up precious memory. They also abstract the developer and the user away from the hardware. In the long arc of computer history hardware abstraction will correctly be seen as one of the most instrumental technical developments that enabled the modern computing era. But from a hobbyist or hardware enthusiast standpoint if an operating system can be ported from one physical computer system to another, it washes away or hides what is unique and special about that computer. As much as I loved WiNGs—and I really loved it—in many ways it transformed a Commodore 64 with SuperCPU into a more powerful but generic PC. Using jsh (the JOS Shell), while powerful, useful and familiar, made using a C64 feel essentially like using a slow Linux PC with crappier graphics.

I believe in the utility of the OS to unleash the power of a computer, especially a Commodore 64 or 128. But I am very cognizant of the tendency of an OS to standardize the experience across computing platforms. I want C64 OS to maintain the C64's quirky and spunky nature. But at the same time, I do not want to have to rewrite or even reinitialize a TCP/IP stack from scratch with every network–based app that I write. And I do not want to reimplement memory management, or mouse input for every app I write. We need a KERNAL of code to help make writing modern apps easier.

What is an OS?

First, OS stands for Operating System. But what an Operating System is is another issue. I've read in forums people arguing over whether the C64's KERNAL and/or BASIC roms are an operating system or not. In my opinion, the answer is a clear and resounding YES. They are definitely an OS. But this leads us to a more general question, what is an OS? How have we gotten to the point where there is confusion and disagreement about whether the C64 has one built–in? You may not agree with my answer, but my view on this matter will color the rest of everything you read in this documentation.

Without software, a computer is just a bunch of chips soldered together on a bus. Those chips don't do anything at all without software to guide the behavior of the CPU to pull data from some chips, interpret the meaning, apply transformations and push data out to other chips, before moving on to the next step. Indivdual programs are software, and operating systems are software. So what is the difference between the two? An operating system is a body of code that is meant to be reusable, partially resident at all times and to augment the development of other programs. It gets a little bit more complicated though because operating systems usually come packaged with their own applications that make use of their services to help the user work with and manage their computer. And so, is the operating system just the bits of code that get reused by other applications or is the operating system the whole package including the useful extra applications it comes packaged with? For our purposes here it doesn't much matter, although I tend to think of an OS as the whole package.

GEOS is more clearly an OS than the KERNAL and BASIC roms, "OS" is right there in the name "GEOS". It contains useful system–level reusable code, but it also includes the deskTop program and other utilities like the calculator and notepad apps. Although, these really are just GEOS apps that can be swapped out for others and/or don't need to be used at all. The KERNAL and BASIC roms do not need any extra programs to be used, because they contain a built–in user interface. But if you aren't fully aware, here are some of the things the KERNAL does: implements the keyboard driver, implements the screen renderer, interprets PETSCII in a kind of terminal emulator, contains hardware drivers for RS232 communications, IEC bus serial communications, tape drive communications. Has hardware test and initialization routines, manages memory regions, maintains a system jiffy clock, and manages file–based I/O. All of these are the sorts of functions you would expect to find even in a modern OS, although many features of a modern OS are missing. The BASIC rom depends upon the presence of the KERNAL rom. It works as a combination command line user interface and script language interpreter that backends on the KERNAL routines to perform its low–level work. Together they allow the user to interface with the computer, to load programs and more. Together, they are an OS. There is absolutely no room for doubt about this in my mind.

There are some things that seem obviously missing. For example, everything related to file and disk management. However, the way Commodore computers handle disk management is very clever. They have only a 16-bit address bus. And so they can only address 64 kilobytes of memory and I/O simultaneously. This is a huge limitation. Imagine if they tried to squeeze all of the disk management you could ever want into a sort of extended KERNAL rom. It would leave less and less memory for programs, and it would be inflexible for dealing with new kinds of storage devices. Instead, the KERNAL implements generic file–based I/O, and the ability to open channels and send commands to devices on the serial bus. Every serial bus device is its own stripped down 8-bit computer. A 1541, like every other CBM and CMD drive, has its own 6502 CPU, its own ram, its own rom, its own system bus and the ability to run its own Disk Operating System software. Some of these devices get their DOS software from an onboard rom, but others, such the CMD HD, have enough rom to get them to load in the bulk of their DOS from the storage medium itself the harddisk drive's special system partition.

There are some disadvantages to this arrangement, but there are also some great advantages especially considering the nature of the computer's limitations. The C64 offloads the work of disk management to another computing device, freeing itself up to have more memory for the user's own programs. It also allows the C64 to work with future devices, such as the uIEC/SD, IDE64, or 1541-Ultimate II+, which are able to work with storage media and file systems that were not yet a twinkle in their engineers' eye at the time the C64 was being designed.

The way the C64's OS, the KERNAL and BASIC roms, plus the DOS on serial devices, works is stylistically quite different, even foreign–seeming, to how modern computers work, but it is most definitely an operating system. C64 OS does not get rid of the KERNAL rom, which provides low–level access to the serial bus. However, it does patch out the BASIC rom, and replaces it with a suite of services that the KERNAL rom doesn't provide. This documentation outlines the features and functions of C64 OS's extended KERNAL, including its new UI and user interaction model.


The Jump Table

Introducing Jump Tables

As was discussed above about what an operating system is, the C64 KERNAL does lots of things. Not all of what the KERNAL does however is intended to be accessed directly by user programs. Because the C64 has no memory protection and the entire address space is always open for any program to access, and because the KERNAL rom has been thoroughly documented, many programs do in fact jump directly into the middle of the KERNAL by using a known absolute memory address. This is frequently done for good reasons, but it can also lead to problems.

Given that the C64 is a product that has essentially been locked in its current form since the late 80s early 90s it doesn't feel like a terrible idea. But from the perspective of a developing platform direct jumps into the KERNAL is a horrible idea. As long as a wide range of software freely jumps into the KERNAL, the KERNAL cannot be changed without risk of breaking all that software. Even as it stands, with the C64 being a product that hasn't commercially changed in over 25 years, jumping straight into the KERNAL is still not recommended. Different models of C64 come with different versions of the KERNAL, so your program might work on the C64 you test it on and may mysteriously break on earlier or later models you haven't tested it on. And there are KERNAL replacements such as JiffyDOS that are more modern, some are still under active development, and have differences that will lead to incompatibilities.

Commodore's recommended programming use of the KERNAL was to use the Jump Table. The C64 Programmers Reference Guide, for example, documents the Jump Table with the expected inputs, expected outputs, and affected registers of each routine. What it does not do is tell you all about the internal implementation of the KERNAL so you can hop anywhere into it at your leisure. The Jump Table exposes set of routines that was relatively stable across their entire 8-bit line. Wikipedia has this to say about the KERNAL:

KERNAL is Commodore's name for the ROM-resident operating system core in its 8-bit home computers; from the original PET of 1977, followed by the extended but strongly related versions used in its successors: the VIC-20, Commodore 64, Plus/4, C16, and C128. —https://en.wikipedia.org/wiki/KERNAL

Extended, but strongly related. However, the internal implementation of the KERNALs were able to vary in arbitrary ways. The Jump Table is a set of byte triplets that come at the very end of the ROM. Each triplet consists of either an absolute JMP or an indirect JMP instruction, followed by a 2-byte address. The positions of the JMPs in the Jump Table are stable, but where exactly they point to in the ROM, or in system RAM in the case of the indirect jumps, can vary from version to version. In theory, if a program makes use of the Jump Table, along with PETSCII control characters to interact with the Screen Editor, a program written for a PET should run on a C64, or a VIC 20 or a c128. This theory works, up until the point where the program makes a direct access to the registers of a chip that only exists in one or the other machine.

C64 OS operates much the way the KERNAL rom operates. It's like an extended KERNAL. The C64 KERNAL is in ROM at $E000 to $FFFF. The 6502/6510 has hardware vectors for NMI, RESET and IRQ in the final 6 bytes, $FFFA-$FFFB, $FFFC-$FFFD, $FFFE-$FFFF It makes sense for the C64 KERNAL to sit across these hardware vectors so it can handle them as they occur. Below those vectors are 4 bytes RRBY that don't seem to have any functional role, according to people on IRC they're the initials of some of the developers.

However, besides these exceptional 10 bytes, the 117 bytes below that are for 39 routines. It makes a lot of sense for the Jump Table to come at the end of the ROM as well. If they ever wanted to add a few more routines, they would be added below the lowest entry, and addresses would be shifted around in the others according to how the routines had to be moved about to find space to implement them.

Below $E000 sits the C64's 4K I/O area, $D000 to $DFFFF. Below this is typically a free 4K area called himem from $C000 to $CFFF. Below this is typically the BASIC rom in the 8K area from $A000 to $BFFF. C64 OS patches out the BASIC rom, and loads itself into memory such that it ends at $CFFF. Given that there are no special hardware vectors at $CFFE-$CFFF, etc, the C64 OS Jump Table begins right at $CFFF, 3-bytes per routine, working down in memory. Just like the KERNAL rom.

C64 OS Modules

C64 OS divides its code up into modules. Each module consists of one source code file (*.a), one or more include files for constants and macros (*.s), and one jump table header (*.h). You can read more about this here.

There are currently nine modules, with a tenth planned:

  1. memory
  2. petscii
  3. input
  4. string
  5. screen
  6. menu
  7. service
  8. file
  9. toolkit, and
  10. network

There is too much code across all the C64 OS modules to combine them into a single file. However, once the code is split into multiple source files, it becomes difficult for the assembly process of one module to know the absolute addresses of routines in the other modules. For the module which contains the Jump Table, it is difficult for it to know where the routines are to jump to.

The solution is outlined in a couple of blog posts about it. Modules are statically laid out in memory, such that the they pack together in memory. During development, the modules are each assigned a region of memory larger than they need so that code additions or bug fixes don't force all subsequent modules to be relocated. The static layout is done by measuring the size of the assembled object files, and then giving them fixed, known load addresses such that they'll layout side–by–side without overlapping or leaving any gaps. During development 10% to 20% may be added to the object file size to account for future development.

The Jump Table's entries consists of indirect JMPs to the start of the module, plus some offset in 2 byte increments. At the start of each module is not a jump table, but a table of addresses where the routines are found within that source code file.

So the first several lines of code in samplemodule1 consists of what is called an exports table, like this:

      *=$c105 ;Some precalculated address
						
;***** Exports *****
      .word routine1
      .word routine2
      .word routine3
:*******************
						

In the exports table, routine1, routine2, routine3, etc are local labels. Thus the addresses in the exports table of a module are resolved at the time the module is assembled, and may change each time the module is assembled as the length and layout of the routines within the module move about as the module is developed.

The C64 OS Jump Table is assembled at the end of the memory module. When this module is assembled, its entries for the samplemodule1's exports are based on its known starting address, like this:

samplemodule1 = $c105
						
      jmp (samplemodule1+0)
      jmp (samplemodule1+2)
      jmp (samplemodule1+4)
						

It is not necessary to name the routines in the Jump Table. It is only necessary that the Jump Table contains as many entries for a module as that module has exports. The next piece that makes this module system work is the jump table header file provided by each module. Samplemodule1, since it exports 3 routines, will have an includable header file that lists the names of these three routines, and their offsets within the Jump Table. Like this:

samplemodulebase = $d000 - (10 * 3)
						
      routine1 = samplemodulebase+0
      routine2 = samplemodulebase+3
      routine3 = samplemodulebase+6
						

The Jump Table header file for samplemodule1 is included by any other module, even the memory module for consistency's sake, that needs to access any of its three exported routines. In the importing module, then, routine1, routine2 and routine3 become defined labels. But they're defined as the addresses of the entries in the C64 OS Jump Table, not directly to the absolute addresses of those routines.

In the jump table header file, the "base" address starts at $d000 and works down 3 bytes per Jump Table entry until the start of the jump table entries for this module. This could change between release versions of C64 OS. But, the goal is to make it as stable as possible. Although it might help to put in some bogus placeholders to routines that are planned for the future.

C64 OS Jump Table

The above was using a sample module and some fake routine names to discuss how it works in the abstract. Now let's look at the actual modules and their Jump Table routines as they are in C64 OS at the time of this writing.

NOTE: This Jump Table is not complete, because C64 OS is still in progress. New routines are likely to be added before the v1.0 release. The network module has no representation at all because its development has not yet begun.

The Jump Table begins with a set of labels called the Component Linker Table. This is just a block of labels that represent the starting addresses of each of the modules. If some module in the middle, say menu, doubles in size and overflows the space that has been allocated to it, its own start address will have to be changed and that will displace all the modules below it. Service, file and toolkit would have to be moved down in memory as well to make room for the much larger menu module.

Displaced modules have to have their start address adjusted in their source file, and then have to be reassembled. Their new start addresses have to be updated in the Component Linker Table, and the memory module—which is the home of the Jump Table—has to be reassembled. All other modules that make use of routines in those relocated modules can be left as is, because they access the routines via the Jump Table.

The entries in the Jump Table for the memory routines use local labels. That is because the Jump Table is situated at the bottom of the same source code file that implements those memory routines.

The sections below go into detail about the routines provided by each module and how they are to be used, and how they are intended to work together when building a C64 OS application.


Memory Management

(Module: memory)


Strings and Charsets

(Module: petscii, string)


Mouse and Keyboard

(Module: input)

The input module implements a 1351 compatible mouse proportional mouse driver. It's based on a modification of the code found in the back of the 1351 manual, which is also available from codebase64. The code in this example is very simple, and just outlines how to read the SID pots and use them to change the coordinates of a sprite.

C64 OS's mouse driver drives a two-sprites pointer, binds the pointer to the screen edges and implements a basic acceleration curve. The mouse driver also reads the mouse buttons using an exclusion technique similar to what is found in the keyboard key rollover routine. If the left button is depressed, activity on the right button is ignored, and vice versa. If the left button is depressed, then the right button, then the left is raised, and then the right is raised, the final right up is also ignored.

The input module also re-implements the keyboard driver, a key scanning routine that replaces the one built into the KERNAL. It's based on the 3-key rollover routine by Craig Bruce, which is also available from codebase64. Bruce's routine is designed to be a drop–in replacement and to remain compatible with software that makes use of the KERNAL's default key handling routines.

C64 OS's keyboard driver changes the way the modifier keys, Control, Commodore, Left Shift and Right Shift are handled. And keyboard activity into two broad types, key commands and printable characters (including some unmodified control sequences, such as cursor keys, tab and carriage return.) Only PETSCII characters that can be readily mapped to 7-bit ASCII can be produced by C64 OS's keyboard driver. PETSCII graphic symbols cannot be generated by key presses, nor can colors be changed, reverse on/off be toggled nor character sets changed.

The input module is responsible for generating mouse and keyboard events, queueing and dequeueing those events.

For a technical deep dive into how C64 OS handles the mouse and keyboard, you can read in much more detail about how it works here.


initmouse

Initmouse initializes and turns on the mouse pointer and sets a flag to enable the mouse driver to start tracking mouse movements, producing events and updating the position of the mouse pointer.

During the initmouse routine the mouse pointer's sprites are read from a file, called ospointer.o in the C64 OS system folder. The initall routine in the service module calls initmouse automatically when C64 OS boot up. So you don't have to do anything to get mouse support.

The sprites file can be customized to change the appearence of the mouse pointer. It uses two overlapping hires sprites to give the pointer a sharp outline. The top left corner of the sprites is used to determine the coordinates of the pointer when an event is generated.

The initmouse routine can be called at any time to re-read in the ospointer.o file. There is no harm caused by re-setting the flags it sets on the VIC-II and in the mouse driver. This may be done to update the appearence of the mouse pointer during runtime.

Initmouse has no input parameters and doesn't return anything.

killmouse

There may be some applications for C64 OS that do not need to use the mouse, and may want to reclaim the sprites for some other purpose. Killmouse has no input parameters and doesn't return anything. It can be called at any time.

It turns off the sprites via the VIC-II registers, and unsets the mouse active flag in the mouse driver. This causes the mouse driver to stop reading the SID pots, and to stop updating the position of the mouse pointer, and stops reading the mouse buttons or producing mouse events.

After calling killmouse, call initmouse to restore normal mouse functionality and resume generating mouse events.

hidemouse

Hidemouse turns off the mouse pointer sprites but leaves all other mouse related functionality intact. The mouse pointer is automatically reenabled when the mouse is moved.

Typically this routine is only called internally by other C64 OS routines. The most obvious candidates for hiding the mouse are the text input widgets in the toolkit module. After clicking on a text field to input text, the mouse pointer is inconveniently left exactly above where the user is about to edit text. When the user starts typing the toolkit input widget may call this routine to temporarily hide the mouse pointer.

Hidemouse has no input parameters and does not return anything.


mouserc

Mouse events, more details about mouse events below, store the coordinates of the event in pixels. Because the horizontal resolution of the screen (320px) exceeds 8-bits (0-255), a 9th bit is required which is one of the bits of the mouse event flags byte.

Many applications, and most of the toolkit, do not require pixel precision. This routine is called to convert a mouse event's X and Y pixel coordinates into column and row coordinates. Since there are only 40 and 25 of these respectively they do not overflow 8-bits and are often much easier to work with.

The input parameters to mouserc are the same as the return parameters from readmouse. After calling readmouse .X and .Y hold the pixel coordinates of where the mouse was when the event was generated, and .A holds the event flags, including the 9th horizontal position bit. After calling mouserc the .A flags byte is left unchanged, but .X and .Y have been converted in–place to column and row coordinates.

readmouse and deqmouse

The mouse driver scans the SID pots and the mouse buttons and automatically generates mouse events which it adds to a FIFO queue. Your application does not need to do anything to make this happen. Whenever a mouse event is on the queue, more details about the event model below, the main event loop calls the application's registered mouse event handling routine.

If the application does not implement a mouse handling routine then it will not receive automatic notices of waiting mouse events. It is still possible for the application to manually poll for the presence of mouse events, but doing so would only make sense under unusual circumstances.

When the application's mouse handling routine is called no parameters are passed to it. Instead the application forwards the notice by calling some other routine that ought to handle mouse events, typically this is toolkit's hit testing routine, however the application may inject custom behaviors before passing control to the toolkit, or the application may not make use of toolkit at all.

Whatever routine ultimately needs to have the event specifics calls readmouse. Readmouse returns in .A, .X and .Y the 3-byte mouse event that is first on the mouse event queue. Readmouse does not modify the queue and it may end up being called many times in the process of figuring out everything that will be affected by it.

When the application's mouse handling routine finally returns, the system's main event loop automatically dequeues the event by calling deqmouse. Deqmouse shouldn't ever need to be called explicitly by the application. Deqmouse takes no input parameters and doesn't return anything. It just removes the first event from the mouse event queue, if there is one, and shifts the others, if there are others, one place in the queue. The queue can buffer up to 3 mouse events before.

readkcmd and deqkcmd

The C64 KERNAL reduces held modifier keys to just one and then uses that to select a key decoding table, and the non-modifier key pressed is used to index into that table to find a single byte in PETSCII. That byte is added to the 10-byte keyboard queue. This is not how C64 OS handles keyboard input.

The keyboard driver scans the keyboard, using the rollover technique devised and described by Craig Bruce in great detail in Commodore Hacking Issue #6. From all of the simultaneously depressed keys it pulls out the modifier keys. If Control or Commodore keys are held down with any other non-modifier key it generates a 2-byte keyboard command event. The keyboard command event is added to the keyboard command event queue. One byte is the un-modified PETSCII character and the other byte is used as bit flags for the modifier keys, Control, Commodore, Left Shift and Right Shift.

Whenever there is a key command event in the queue the main event loop calls into the menu system's key command handler. The Application's main menu commands are given the first opportunity to match this key command event. If there is a match the menu system will issue a menu action to the application and the key command event will be dequeued automatically.

If no menu matches the key command event the main event loop calls the application's registered key command event handling routine. If the application does not implement a key command event handler then it will not receive automatic notices of key command events. It is still possible for the application to poll for their presence, but it would only make sense to do so under unusual circumstances.

The application's key command event handling routine can, at this point, perform any custom behaviour that it wants. To get the specifics of the key command event it calls readkcmd, the PETSCII value and flags byte are returned in .A and .Y.

If the application does not want or need to intercept this key event it should forward the notice to the toolkit. Toolkit then dispatches the event to the view which has keyboard focus. The notice is then passed up the view hierarchy and each view is given an opportunity to respond to that event. Typically these behaviors are not something the application needs to worry about as the toolkit knows how to handle and process key command events all on its own. Forwarding notice of the presence of a key command event does not involve copying or sending the key command event data, the routine that eventually needs to know about the event details calls readkcmd. And readcmd may end up being called many times in the process of figuring everything that is affected by it.

When the application's key command event handling routine finally returns the system's main event loop automatically dequeues the event by calling deqkcmd. Deqkcmd shouldn't ever need to be called explicitly by the application. Deqkcmd takes no input parameters and doesn't return anything. It just removes the first key command event from the queue, if there is one, and shifts the others, if there are others, down one place in the queue. The queue can buffer up to three keyboard command events.

There is one small modifier key exception. The left and right shift keys are handled independently. If the left shift key is held down while a cursor key is pressed, then a key command event is generated. These events are be handled by text editing widgets in the toolit to extend text selection ranges. When the left shift key is held and a cursor key is pressed, the right shift key is checked independently of the left shift key, and is used to change the direction of the cursor keys.

The F1 through F8 keys always produce key command events, even if they are pressed without the presence of other modifier keys. If a function key is pressed with other modifier keys, the modifier key flags are set in the key command event along with the function key as the value. This greatly extends the number of possible functions that the 4 function keys can perform. Commodore + F1, for example, is different than Control + F1, and both are different again from Commodore + Control + F1.

readkprnt and deqkprnt

Following on the description of the keyboard driver from the section above regarding key command events, when a key is pressed, if neither the Control key nor the Commodore key are held down, and the left shift and cursor keys are not being used in concert, then a printable key event will be generated.

When the cursor keys are not being used, the left and right shift keys are treated the same and select between the normal and shifted KERNAL key decode tables. The PETSCII character retrieved from the decode table is the complete 1-byte event, and is added to the event queue, which is the KERNAL's standard 10-byte keyboard buffer.

Whenever an event is in the printable key event queue the system's main event loop calls the application's printable key event handling routine. Similar with mouse and key command events, if the application does not implement this handling routine it will not receive automatic notice that they are there, but can still poll for their presence by periodically calling readkprnt.

The application's printable key event routine has the first opportunity to handle the event to perform some custom task. Typically the application will forward the printable key event notice to the toolkit. As with the other event types, passing notice does not involve copying or sending the event. The event handling routines are not passed any arguments. Whichever routine eventually needs to know the details of the current printable key event, it may call readkprnt. Readkprnt does not modify the queue. Readkprnt may end up being called many times in different contexts before it is finally dequeued. Readkprnt takes no input parameters and returns the single printable character byte in .A.

When notice is given to toolkit, toolkit forwards the notice to the view with keyboard focus. Toolkit views know how to pass the notice up the view hierarchy. If a view in that hierarchy handles printable key events, it may use the event, for example, to insert a character into its back-stored string. The application does not need to play any roll in making this work as toolkit knows how to handle printable key events.

When the application's printable key event handling routine eventually returns, the main event loop automatically dequeues the printable key event by calling deqkprnt. The application should not ever have to call deqkprnt explicitly. Deqkprnt does not take any input parameters and it doesn't return anything. When it is called it removes the first printable key event from the queue, if there is one, and shifts the remaining printable key events, if there are any, one place in the queue.

updatemnk

Updatemnk stands for Update Mouse and Keyboard. This routine is what makes the mouse and keyboard drivers tick. It has to be called with some regularity in order for the mouse pointer to be responsive to physical mouse movements, and for characters typed on the keyboard to be noticed. Calling this routine represents one tick of the drivers. The drivers generate the three above–described types of events automatically as long as this routine continues to be called with regularity.

The initall routine of the service module configures C64 OS's interrupt handler. The CIA timer is configured to produce an interrupt 60 times a second. Calling updatemnk is one of the interrupt handler's jobs. The application should not have to ever call updatemnk.

If the application masks interrupts (with the SEI instruction), the mouse and keyboard will become unresponsive until interrupts are restored (with the CLI instruction). This should be avoided unless it's absolutely necessary, because it will make the user interface unresponsive.

The keyboard driver cannot be disabled, and the key matrix is scanned 60 times a second. However the driver is very efficient at noticing that no keys are held down. The mouse driver, and its pointer, can be disabled by calling killmouse. An internal flag is set which causes updatemnk to skip processing any mouse activity.


Jump Table

The following file is the jump table header file for the input module. Any source code file of the application that needs to call one of the exported routines from input should include this header. This header defines the labels for the exported routines as offsets into the system's main jump table.

It also serves as documentation to the programmer about how to use these routines, listing their input and output parameters.



Mouse Events

An in depth discussion of mouse events can be found here under the Mouse Events heading.

A mouse event consists of three bytes, X position, Y position and flags. The flags byte consists of an event type in the low 4-bits, 3-bits to represent the modifier keys, Left Shift, Control and Commodore. Bit 7, the last bit, is used to make 9-bits of X position resolution.

X position - 1 Byte
Y position - 1 Byte
Flags      - 1 Byte

Flags Bits:

0 - | These 4 bits are used 
1 - | to hold a number
2 - | 0-F that represents
3 - | the event type

4 - Left Shift
5 - Control
6 - Commodore
7 - X Pos MSB

The Mouse Event types are as follows:

0000 - 0 - move
0001 - 1 - left down
0010 - 2 - left tracking
0011 - 3 - left up
0100 - 4 - left click
0101 - 5 - left dblclick
0110 - 6 - right down
0111 - 7 - right up
1000 - 8 - right click
1001 - 9 - reserved
1010 - A - reserved
1011 - B - reserved
1100 - C - reserved
1101 - D - reserved
1110 - E - reserved
1111 - F - reserved

A mouse event's coordinates can be converted to rows and columns by calling mouserc. See above for a description of mouserc.

Mouse events carry modifier key flags with them, this allows for clicking with a modifier key or key combination to be programmatically handled differently than an unmodified click. Note that Right Shift is not available as a mouse event modifier key. The Right Shift key collides with the mouse buttons in the commodore key matrix. If the mouse button is depressed, it is fundamentally impossible to determine the state of the Right Shift key.

Key Events

An in depth discussion of key events can be found here under the Keyboard Command Events heading, and further below under Printable Keyboard Events, plus additional notes on special key commands and cursor control.

Key Command Events

A key command event consists of two bytes, the PETSCII character value, and a modifier key flags byte. The modifier key flags are similar to the flags used by the KERNAL, however, C64 OS breaks out the Right Shift key as a separate flag.

PETSCII value - 1 Byte
Flags         - 1 Byte

Flags Bits:

0 - Left Shift
1 - Commodore Key
2 - Control Key
3 - Right Shift

4 - Reserved
5 - Reserved
6 - Reserved
7 - Reserved

The PETSCII value is extracted from the KERNAL key decode tables, standard and shifted, depending on whether a shift key is used.

Function keys are always encoded as key command events. The PETSCII value of such an event is the F1 through F8 PETSCII codes ($85 through $8C).

Cursor keys used in combination with the Left Shift key are encoded as key commands. The PETSCII value is the cursor key PETSCII values ($11/$91 down/up, $1D/$9D right/left).

Printable Key Events

Key strokes which do not produce key command events produce printable key events. This consists mostly of standard and shifted alpha numberic characters and printable symbols.

Some single-byte PETSCII control sequences are handled as printable key events. Such as carriage return, home, insert, cursor left, up, right and down. However, other sequences that are typically handled by the KERNAL's screen editor, such as changes of color, reverse on/off, changes of character set, and function keys are not queued as printable key events.

There are some very standard ASCII characters that are in wide and unavoidable use on the internet today that Commodore 8-bit computers do not have keys on the keyboard to produce. And the PETSCII itself is actually missing these characters, replacing them instead with some custom graphical symbols. But, if C64 OS is to interact with the internet it needs a way to type these characters.

There are 6 keys, three in the upper row: plus, minus, pound, and three in the row below that: @, asterisk, up arrow, which when combined with shift produce PETSCII graphic symbols. In C64 OS there is special logic which maps these shifted keys to common ASCII characters that we need to be able to type. The rational for all of these and a more in depth discussion can be found under the Printable Keyboard Events heading here.

Shift @ { (open brace)
Shift * } (close brace)
Shift Up Arrow ~ (tilde)
Shift £ | (pipe)
Shift - _ (underscore)
Shift + ` (backtick)

Event Model

(Module: input,screen,service)


Screen Compositor

(Module: screen)


Widget Toolkit

(Module: toolkit)


Menu System

(Module: menu)


Drive Support

(Module: file)


File Access

(Module: file)


TCP/IP Networking

(Module: network)