Close

20230220c -- Revisiting Keyboard Scan Code Handling

A project log for ROM Disassembly - AlphaSmart Pro

Wherein I disassemble the ROM from a vintage typewriter-thing

ziggurat29ziggurat29 02/23/2023 at 14:552 Comments

Eager to mine some more scan codes, I am looking at handleKeyScanCode_EE37

EE37 13 63 01 04     brclr   byte_63 1 loc_EE3F ; XXX flag? kbd (maybe option? maybe clover?)
EE3B 12 61 01 C9     brset   byte_61 1 loc_EE08 ; XXX flag? kbd (any col 9) (maybe option? maybe clover?)
EE3F             loc_EE3F:
EE3F 13 64 01 02     brclr   byte_64 1 loc_EE45 ; XXX flag? kbd (any col 15)
EE43 8A 80           oraa    #$80 ; 'Ç'
EE3F 13 64 01 02     brclr   byte_64 1 loc_EE45 ; XXX flag? kbd (any col 15)
EE43 8A 80           oraa    #$80 ; 'Ç'
EE3F             loc_EE45:
EE45 97 70           staa    byte_70
EE47 81 56           cmpa    #$56
EE49 27 DD           beq     handleClearFile_EE28
EE4B 81 2B           cmpa    #$2B
EE4D 27 28           beq     loc_EE77
EE4F 81 35           cmpa    #$35
EE51 27 2A           beq     loc_EE7D
EE53 81 34           cmpa    #$34
EE55 27 36           beq     loc_EE8D
EE57 81 37           cmpa    #$37
EE59 27 40           beq     loc_EE9B
EE5B 81 15           cmpa    #$15
EE5D 27 4A           beq     loc_EEA9
EE5F 81 66           cmpa    #$66
EE61 27 54           beq     loc_EEB7
EE63 81 75           cmpa    #$75
EE65 27 55           beq     loc_EEBC
EE67 81 2D           cmpa    #$2D
EE69 27 56           beq     loc_EEC1
EE6B 7E EF 67        jmp     loc_EF67

This handles various special keys out-of-band of regular keystroke processing.  The 'Clear File' key was figured out because the file clearing logic was found, so we know that is 0x56.  The others are not yet known.

loc_EEC1 is interesting because it toggles the caps lock indicator found before:

EEC1             sub_EEC1:
EEC1 96 A1           ldaa    bCapsLock_A1    ; XXX CAPS lock flag?
EEC3 88 01           eora    #1
EEC5 97 A1           staa    bCapsLock_A1    ; XXX CAPS lock flag?
EEC7 20 11           bra     sub_EEDA

 So scan code 0x2d is likely the caps lock key.

loc_EF67 is interesting because it handles everything else, and is likely involved in key code to ASCII conversion for the bulk of the keyboard.

EEC9             loc_EEC9:
EEC9 96 70           ldaa    byte_70
EECB BD F5 92        jsr     sub_F592
EECE 29 10           bvs     leave_EEE0
EED0 13 A1 01 03     brclr   bCapsLock_A1 1 loc_EED7 ; XXX CAPS lock flag?
EED4 BD EF BF        jsr     sub_EFBF
EED7             loc_EED7:
EED7 BD F3 07        jsr     sub_F307
...

The interesting thing to me is that the scan code was saved in byte_70 earlier, prior to doing a bunch of things, then restored at the start of this routine.  The value could be mutated in sub_F592, and then the capslock flag we saw earlier is checked and an optional transform sub_EFBF is performed if caps lock is on.  Then the final value is processed in sub_F307.  It's just a hunch, but it could be that sub_F592 transforms the scan code to ascii (exiting if it cannot), optionally converts to upper case if caps lock is on via sub_EFBF, and then sub_F307 processes the asii version of the character.

The transform at sub_EFBF is fairly obviously a 'make upper case routine' just for the lower case [a-z] by exploiting ASCII's structure:

EFBF             sub_EFBF:
EFBF 81 61           cmpa    #'a'
EFC1 25 06           bcs     leave_EFC9
EFC3 81 7A           cmpa    #'z'
EFC5 22 02           bhi     leave_EFC9
EFC7 84 DF           anda    #$DF   ; goodbye, bit 5
EFC9             leave_EFC9:
EFC9 39              rts

What this also means is that coming into sub_F592 is a scan code in A, and then when it returns, coming into sub_EFBF is ASCII in A (or else the make upper wouldn't work).  So sub_F592 /has/ to do scan code to ASCII translation.

The routine sub_F592 masks off the high bit and then multiplies that value by 4, and offsets it into a large array of 4-byte structures at unk_F8C5.  We know that A coming in is the scan code, so this table seems to be some sort of scan code to something (ASCII?) translation.  Lo! and Behold, this array is 128 entries, which would be consistent with the mask of 0x7f.  It is also consistent with our scan code scheme of (row|col), since there are 16 columns and 8 rows.

F592             sub_F592:
F592 84 7F           anda    #$7F ;  ; remove high bit, if present
F594 16              tab
F595 4F              clra
F596 05              lsld
F597 05              lsld            ; effectively multiply by 4 (4 byte entries)
F598 C3 F8 C5        addd    #unk_F8C5  ; XXX 128 4-byte structures
F59B 8F              xgdx            ; X is now struct address for this scan code
F59C 13 64 01 04     brclr   byte_64 1 loc_F5A4 ; XXX flag?
F5A0 A6 03           ldaa    3,x
F5A2 20 02           bra     cont_F5A6
F5A4             loc_F5A4:
F5A4 A6 02           ldaa    2,x
F5A6             cont_F5A6:
F5A6 81 00           cmpa    #0
F5A8 27 02           beq     nochar_F5AC
F5AA 0A              clv
F5AB 39              rts
F5AC             nochar_F5AC:
F5AC 86 2A           ldaa    #$2A ; '*'
F5AE 0B              sev
F5AF 39              rts

There is also a check of flag byte_64 which affects whether element at +2 or +3 is selected.  This isn't the caps lock flag, but maybe it is the shift flag?  Caps lock generally only affect alpha characters, whereas you still need to use shift to get to things above the numerics.

Since I had managed to figure out a few scan codes earlier by correlating with menu options, lets check those out for consistency.  I (tediously) labelled all the entries in the array.  We know (well, believe) that 'y' for 'yes' is 0x22.  So, 0x22 * 4 + 0xF8C5 = 0xf94d

F94D 10             fcb $10
F94E 35             fcb $35 ; 5
F94F 79             fcb $79 ; y
F950 59             fcb $59 ; Y

And element +2 is lower-case y, and +3 is upper-case 'Y'.  This looks promising.  Some of the others known:

code 0x5c, believed to be '3':

FA35 14        fcb $14
FA36 26        fcb $26 ; &
FA37 33        fcb $33 ; 3
FA38 23        fcb $23 ; #

code 0x5d, believed to be '2':

FA39 13        fcb $13
FA3A 1E        fcb $1E
FA3B 32        fcb $32 ; 2
FA3C 40        fcb $40 ; @

 code 0x5e, believed to be '1':

FA3D 12        fcb $12
FA3E 16        fcb $16
FA3F 31        fcb $31 ; 1
FA40 21        fcb $21 ; !

code 0x5e, believed to be '4':

FA09 15        fcb $15
FA0A 25        fcb $25 ; %
FA0B 34        fcb $34 ; 4
FA0C 24        fcb $24 ; $

So this is checking out pretty good so far!  And the upper case of the numerics corresponds to their shifted symbol on the keyboard.

OK, now I must tediously go through the entire table, and markup a printout of the keyboard.  I will not be able to figure out all things, since this just covers stuff that is meant to translate to printable characters, but it will be a bit leg up.  Plus, I don't know what elements +0 and +1 do yet

There are 80 physical keys on the AlphaSmart Pro, so most of these entries will be 'invalid' from the standpoint of the ASCI conversion.

Discussions

Eric Hertz wrote 02/23/2023 at 23:05 point

I hesitate to offer photos of the membranes....

I'm amazed at what you're accomplishing, and I gather that you enjoy the detective-work...(?)

  Are you sure? yes | no

ziggurat29 wrote 02/23/2023 at 23:22 point

it will be interesting to see the insides of the keyboard at some point!
I do enjoy the detective work to see how far I can push it blind.
I'm a bit behind on posting, but tomorrow's post on yesterday's work, more-or-less concludes keyboard stuff.  There are some ambiguities I cannot resolve from software alone (e.g. left/right alt key).  Deets in the post.

  Are you sure? yes | no