Date : Mon, 05 Sep 1994 09:24:17 WET DST
From : Bonfield James <jkb@...>
Subject: Keyboard routines
Hello,
I've decided to document my info on the keyboard handling routines. I haven't
got my NAUG with me today so some of this maybe a bit vague. However the
general gist of it should be useful.
When pressing a key, the system 6522 CA2 line is pulled low. This sets the CA2
flag in the system VIA interrupt flag register (IFR). If the CA2 flag in the
interrupt enable register (IER) is set then an IRQ is also sent to the
6502.
Now the OS1.2 IRQ handling routine checks the cause of interrupts in a
particular order. Near the end of this order is an LDA &FE4D (system IFR).
With various ROR and ROL instructions it detmines exactly which bit in the IFR
was set and then calls the appropriate routine. This is JMP (&0228) (KEYV) for
us.
The KEYV is a bit confusing, as both V and C bits of the status register
should be initialised correctly. The IRQ handler does this for us.
We need to know a few more bits about the system VIA here. ORB (&FE40)
references, amongst other things, a latch. Writing to this address will clear
or set a particular bit in the latch. Bits 0-2 reference the latch bit (3 is
the keyboard write enable) and bit 3 is the value to store in this latch bit.
Writing &B to FE40 clears keyboard write enable and writing 3 to FE40 sets
write enable (ie it operates reversely to expected).
So the KEYV now does the following (amongst other things)
0. Disable CA2 interrupts in IER.
1. write 3 to FE40 - allow us to update the keyboard
2. write 7F to FE43 - set bits 0-6 of ORA to be output, and bit 7 to be input
X = 9
3. Clear IFR CA2 bit
4. write X to FE4F
5. Check if IFR CA2 bit is set
6 If not, DEX and loop back to 3 (provided X is still positive - if not then
we've found no key pressed)
The above loop is polling keyboard matrix columns. Our implementation should
generate an interrupt in IFR (not serviced as IER disables it) when the
keyboard column has been written to.
The keyboard column is the bottom 4 bits of the value written to ORA (non
handshaking in this case).
Now, once we've spotted a column, we proceed to check each value. So assuming
return had been pressed (keycode &49) we perform a similar check to above:
A = 09
1. Write A to FE4F
2. Check top bit of ORA (FE4F)
3. If not set, add 10 to A (upto and including &79) and loop back to 1.
4. If we've got here due to the top bit being set, then that keycode has been
pressed. Store this in &EC, and shuffle the old &EC value to &ED. Also
initialise the countdown values for auto repeat delays.
So our keyboard row numbers range from 0 to 7 inclusive.
Our keyboard implementation must therefore take bits 0-3 as a column. If a key
in this column is pressed, pull CA2 line low. Bits 4-6 are the row. If the
keycode referenced by both row and column is set, then set (subject to current
DDRA value) the top bit of ORA. Note that you shouldn't fall into the trap of
defining a row array and a column array. The final row+column check needs to
be done as 'is this keycode pressed' rather than 'is this column set, and is
this row set'.
Actually, we should probably be using IRA instead of ORA to be correct.
However this is only a difference when we start to worry about handshaking and
IO latching.
Also I think there maybe other hacks to the above method to resolve handling
multiple key presses. However we'll ignore this for the time being as this is
an OS issue and the above implementation ought to allow for the OS to do the
bits it needs.
Now our interrupt returns. Currently our OSRDCH routine still hasn't spotted
that the keyboard has been pressed. This is done on the next T1 interrupt
generated by the system VIA. Hence you need a correct implementation of this
before your keyboard code will work correctly.
On the next T1 interrupt, amongst MANY other things, a check is performed on
&EC and &ED. If a key is found to be pressed, INSV is then called to insert
it's value (we do a quick lookup (LDA (&FA),Y ; or some such) to find the
ASCII value of the keycode) into the keyboard input buffer.
The way things usually run on your machine is that BASIC calls OSWORD 0 to
read a line. OSWORD 0 calls OSRDCH to get a character at a time until return
is pressed. OSRDCH continually scans the current insertion and remove pointers
into the keyboard input buffer to detect when new data arrives.
Key auto repeats are also handled by the T1 interrupt. The T1 code also checks
again whether our keys are still pressed. The appropriate &EC/&ED values are
cleared in necessary, or the appropriate count down timers are decremented.
This way the T1 interrupt can continue to call INSV with a ASCII value each
time we wish an auto repeat of the current key held down.
I hope the above information is correct. It's been written from memory of my
experiences of coding the keyboard hardware routines. There's most likely
errors somewhere in it, but I hope it serves to start people on the correct
track.
James B.