Written and Maintained by Gregory Nacu

C64 OS TECHNICAL DOCS

Last modified: May 14, 2018

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)

Next Section: Event Model

Table of Contents



This is a document in progress.
Check back again soon.