Geneve keyboard control
The Geneve uses a MF1 keyboard (PC-XT). Access to the keyboard is implemented as follows:
Keyboard interface
Shift register
In MDOS mode, the address F118 is used to access the 8-bit shift register in the Gate array which stores the most recent scancode. In TI mode, the address is 8008. The address is not fully decoded; the rightmost three bits are ignored (F118 is the same as F11F).
The shift register is readable throughout the receive period of the scancode; this means you can watch the scancode coming in, shifted from left to right. This also means that you must make use of the available flag to find out whether the shift register contains the full scancode.
Available flag
The bit at CRU address 0010 (or bit 8 at base 0) is set to 1 by default. The interrupt inputs at the 9901 are all negative logic, so we need a 0 to trigger an interrupt.
It is set to 0 when a scancode is available in the shift register at F118. The input is configured to raise an interrupt which will make MDOS read the scancode and put it in a buffer, unless interrupts are masked out (using LIMI).
The bit is reset to 1 with the next SBZ on the control bit 1.
Control bit 0
The bit at CRU address 1EF0 has two functions:
- When set to 0 (SBZ), the keyboard clock is turned off. All following keystrokes are buffered in the keyboard and are not propagated to the Gate Array.
- When set to 1 (SBO), a reset occurs (only if the previous state was 0; otherwise no effect). This means the states of CapsLock, NumLock, and ScrollLock are set to off, the keyboard performs a self-test, clears the scancode buffer, and sends a AA code to the computer. This scancode is transmitted as any other scancode, i.e. we have to check the available flag before getting it from the shift register.
Experiments showed that between the SBZ and the SBO a minimum delay time of around 0.17 ms should pass (about 500 cycles), or no reset will occur.
Another thing to consider is that there should be no intermediate scancodes (possibly from key releases). Before the SBZ one should try to poll remaining scancodes from the keyboard.
Control bit 1
The bit at CRU address 1EF2 also has two functions:
- When set to 0 (SBZ), the shift register is cleared, the available flag is set to 1, and the clock line is enabled. This means the keyboard will now begin to send its buffered scancodes. The shift register is locked to 0, and any incoming scancode will be lost.
- When set to 1 (SBO), the shift register is set to receive. The next incoming scancode will be read into the shift register. Scancodes are transmitted LSB first, so the shift register is loaded from the left. When the complete scancode has been received, the available flag is set to 0, and the clock line is disabled.
Scancode reception is done in the background, without program control. The shift register will accept a scan code at any time after the SBO. This means that the shift register should be checked whether it contains a scancode that has been received in the meantime before setting the control bit 1 to 0, since this will wipe the shift register.
Writing a keyboard driver
MDOS offers XOP operations to query the keyboard which differs significantly from the keyboard of the TI-99/4A. If we are interested in a more low-level access, the following code shows how to get the scan codes.
Get a scancode
GETSC LIMI 0 // Do not disturb LOOP LI R12,>1EE0 // Base address 1EE0 SBZ 9 // Clear shift register, enable keyboard SBO 9 // Set shift register to receive mode CLR R12 // Base address 0000 WAIT TB 8 // Listen on the available flag JEQ WAIT // As long as it is 1 (EQ=1), do the loop. MOVB @>F118,R1 // Get the scan code from the shift register
R1 now has the scan code in its high byte. We are using the CRU base 1EE0, so control bit 0 is bit 8, and control bit 1 is bit 9. The SBZ 9 command releases the clock line of the keyboard so that the keyboard starts transmitting a scan code if there is one in its queue. With SBO 9 the Gate Array switches to receive mode and awaits the incoming bits. We should not waste time here: If the pause between the SBZ and the SBO exceeds 0.2 ms (about 600 CPU cycles) we miss some of the incoming bits, and the scan code is incorrect.
When all 8 bits have been assembled to a byte, this byte is made available via address F118. We know that there is a new byte by monitoring bit 8 (base 0000): The Gate Array pulls down this line, and as the 9901 input is configured as an interrupt input, it will trigger an interrupt and MDOS gets the key code itself. We therefore mask the interrupts away in order to do our own handling.
Note that scan codes are quite different from the keycodes of the TI:
- Some keys create two scancodes in sequence (like E048 for Up Arrow).
- Key releases have a separate scancode which is the key press code plus >80.
Two subsequent scancodes arrive at a 1.3 ms interval which means a maximum rate of 768 scancodes per second, each one having 11 bits (1 start, 8 data, 1 parity, 1 stop), so that means 8448 bits/second. (Note that a keystroke requires at least two scancodes; one for press, one for release.) Adding some processing overhead this yields a connection rate of 9600 baud. Key repetition occurs with about 42 ms intervals, i.e. 23.5 characters per second.
Initialize the keyboard
INITKB LIMI 0 LI R12,>1EE0 SBO 9 // Enable shift register SBZ 8 // Disable keyboard LI R4,60 // Wait a moment WAIT DEC R4 JNE WAIT SBO 8 // Reset it CLR R12 WAIT0 TB 8 // Wait for incoming code JEQ WAIT0 LI R1,>AA00 CB @>F118,R1 JNE FAILED // If not AA, something went wrong
This is quite similar to the above. The SBO 9 line is not required if we are sure that the last operation on the control bit was not a SBZ. The normal case, as shown above, is to set the control bit to 0 only for short, directly followed by a SBO. If you replace the SBO 9 by a SBZ 9, the shift register will not be loaded with the return code.