In Which We Have Keyboard Input


So another week rolled-by without an awful lot of time to spend on VIC++ but progress was nevertheless made - I finalised the new .VKM file which XVIC uses to map a standard PC keyboard into VIA rows and columns, and have now resumed work on the ROM code to make use of it. As is often the case with low-level Assembly programming, the hardest part is setting-up data structures and suchlike so that the actual code can do what it needs to with a minimum of fuss - so in this case, I needed the .VKM file to be finished so that I could set-up the 127-byte VIA lookup table in ROM (ordered specifically so that critical 'action' keys like Shift, Caps Lock and Control appear at the beginning), and then a table of jump addresses to the key-handler routines had to be defined, and then a few other little bits of memory had to be allocated in Page 0 and Page 3 as working storage.

Finally, I had enough framework in place that I could start exercising and extending the prototype keyboard scan logic I wrote three weeks ago; that code did little other than talk to VIA #2 to derive the row/column indices which point into the lookup table to obtain a key scan code, but we needed a whole lot more logic in there before it would be anywhere near finished, and that's what I turned-to next.

The basic principle of how the code works is this: having interrogated VIA #2 to obtain a row and column index, we use those values to point into a table of keyscan values - essentially, these are just unique codes which identify a keypress and allow us to figure-out what keycode should appear in a memory location (which we might then display on-screen). Equally, some keys are 'action' keys which don't actually produce a displayable character, but instead indicate an effect such as modifying the keyboard response when Shift or Control is pressed, for example.

The choice of codes is entirely arbitrary, and in fact I've diverged considerably from the original Commodore tables - all action keys have low values ranging from $00 to $15 (at the moment - this might increase if I choose to respond to further action keys later) and printable characters are simply represented with their actual ASCII codes starting with $20 for the 'space' character and going-up to $7A for 'z'. Since the VIA gives us back $FF when no key is pressed, I just replicate that as the code for 'no key' - so this makes it easy to decide whether a keycode should make something happen or just appear as a character on the screen, because any value less than $16 is an action keypress, and any negative value (which in practice means $FF, since the highest displayable code is $7A) is no keypress, so everything else must be a displayable character.

I've specifically placed the action keys 'low' in the table (both in terms of their physical position and numeric value) because for the keyboard modifier codes - Shift, Caps Lock, and Control - I want the keyscan logic to sense these first, allowing adjustments to happen to additional keycodes detected in the same IRQ cycle. In other words, if you press Shift and 'a' (for example) then the logic will return the scancode for 'A' in one pass, rather than perhaps reading the 'a' keypress and then having to do further iterations over the scancode table to see if Shift was also pressed. It makes the code simpler and faster as well, which is always a bonus.

Basic unmodified keyboard response is working, so if I hit the 'a' key I get an 'a' on the screen. I'm now working on the modifier, or 'action', key handlers, and debugging a strange little anomaly in the VIA decode logic - essentially, there's a place in the code that execution should never hit, but every once-in-a-while it does; so there's obviously some behavioural effect in the way the scan process works that is different to what I expect - take a look, see if you can spot something I'm missing:

    LDA #$00          ; [2]    zero for VIA #2 port B register
STA _V2PORTBO ; [4] clear VIA #2 port B register (keyboard column)
LDA _V2PORTAO ; [4] get VIA #2 port A register (keyboard row)
CMP #$FF ; [2] test for all row bits set
BEQ .done ; [3/2] no key pressed if all row bits set

; got a keypress, enable first column
LDY #$07 ; [2] keyboard column bitmask index
.nextcol
LDX #$07 ; [2] keyboard row bit count
LDA _PORTBITS,Y ; [4] get next bitmask
STA _V2PORTBO ; [4] set VIA #2 port B register (keyboard column)

; check each bit in the row response
LDA _V2PORTAO ; [4] get VIA #2 port A register (keyboard row)
.checkrow
LSR ; [2] shift keyboard row bit to Carry
BCC .gotrow ; [2/3] handle row selection if bit clear

; row not selected
DEX ; [2] decrement keyboard row count
BPL .checkrow ; [3/2] loop for next row bit

; no row responded yet, enable next column
DEY ; [2] decrement bitmask index
BPL .nextcol ; [3/2] loop for next column
JAM ; [0] **** should never get here but sometimes we do!

So if any row has a key pressed, we don't get $FF back from Port A, and drop into a simple loop which loads Port B with eight consecutive values (each with one bit set to zero) and looks to see if Port A returned any clear bits (which would indicate that a key is pressed on that row/column intersection). If we get a clear bit we jump off to .gotrow and do the scancode lookup, but if not we just carry-on checking row bits, and then loop back for the next column.

Now we know for sure that somewhere a row/column combination must give us a clear bit for .gotrow, because we didn't get $FF at the beginning - but for some reason, at odd intervals, we hit that JAM instruction, which means that we're losing the keypress somewhere. The only thing I can think of is that for very quick keypresses, we see it at the beginning of the routine but by the time we scan all the columns it's no longer registered on Port A - which might not be a big deal, but I'd like to understand exactly what's going-on. A little more research and testing, methinks.

. . .

Incidentally, I'm starting to get a bit twitchy about Zero Page usage - after allocating the Line 25 bitmap space, working storage for screen redraw, cursor updates, dirty-row tracking and a few miscellaneous bits of storage for other things, I have about 16 bytes free. The problem is that there's a LOT of ROM left to write, and I can foresee a fair amount of ZP being needed - I might have to do some serious tweaking in the not-too-distant future, because I'm pretty sure 16 bytes is far below the amount I'm likely to need. I guess I'll just continue to keep an eye on it as things demand ZP storage, and panic when we get to 0 bytes free... ;)

Yorumlar

Bu blogdaki popüler yayınlar

Sister Lika Aidra Pulsating Semen

Ddlg lisa sugar

Pick marks spanks