Close

20230222b A Tale of Two Translations

A project log for ROM Disassembly - AlphaSmart Pro

Wherein I disassemble the ROM from a vintage typewriter-thing

ziggurat29ziggurat29 02/24/2023 at 15:060 Comments

There are several translation routines found during the file sending stuff that translate one character value into another one.  Some we understand, like this one:

F4DC             sub_F4DC:
F4DC 84 7F           anda    #$7F            ; reset high bit
F4DE 16              tab
F4DF 4F              clra
F4E0 05              lsld
F4E1 05              lsld
F4E2 C3 F8 C5        addd    #scanCodeStructs_F8C5
F4E5 8F              xgdx                    ; X is scan code struct for scan code originally in A
F4E6 A6 01           ldaa    1,x
F4E8 39              rts

This is a straightforward translation of AlphSmart Pro scan code to the PS/2 scan code (since we have now figured out offset +1 is the PS/2 code).  This is probably used when the device is operating as a conventional keyboard (i.e., not 'standalone mode').

This one's fancier, but we can see what's going on:

F4B6             sub_F4B6:
F4B6 84 7F           anda    #$7F            ; reset high bit
F4B8 CE F8 C5        ldx     #scanCodeStructs_F8C5
F4BB             loop_F4BB:
F4BB 8C FA C5        cpx     #byte_FAC5      ; (end of array)
F4BE 27 18           beq     notfound_F4D8
F4C0 A1 02           cmpa    2,x             ; match either lower case
F4C2 27 0A           beq     matchLower_F4CE
F4C4 A1 03           cmpa    3,x             ; or upper case
F4C6 27 0A           beq     matchUpper_F4D2
F4C8 08              inx
F4C9 08              inx
F4CA 08              inx
F4CB 08              inx                     ; next struct...
F4CC 20 ED           bra     loop_F4BB
F4CE             matchLower_F4CE:
F4CE A6 01           ldaa    1,x
F4D0 20 04           bra     cont_F4D6
F4D2             matchUpper_F4D2:
F4D2 A6 01           ldaa    1,x
F4D4 8A 80           oraa    #$80
F4D6             cont_F4D6:
F4D6 0A              clv
F4D7 39              rts
F4D8             notfound_F4D8:
F4D8 86 4A           ldaa    #$4A
F4DA 0B              sev
F4DB 39              rts

Here a linear search is done through the scanCodeStructs_F8C5 trying to match either the upper or lower case ASCII version of the entry, and then plucking out the PS/2 scan code.  So this is an 'ASCII to PS/2' translation, and is probably used when sending files (which are in ASCII) over the keyboard port.

This one also involved the scanCodeStructs_F8C5 array, but it uses offset +0 that I do not yet understand:

F482             sub_F482:
F482 85 80           bita    #$80            ; remember high bit status (using byte_71 scratch var) for later
F484 27 05           beq     cont_F48B
F486 14 71 FF        bset    byte_71 $FF
F489 20 03           bra     cont_F48E
F48B             cont_F48B:
F48B 7F 00 71        clr     byte_71
F48E             cont_F48E:
F48E 84 7F           anda    #$7F            ; mask off high bit in key scan code
F490 16              tab
F491 4F              clra
F492 05              lsld
F493 05              lsld                    ; effectively scan code *= 4
F494 C3 F8 C5        addd    #scanCodeStructs_F8C5
F497 8F              xgdx                    ; X is now scan code structure pointer
F498 A6 00           ldaa    0,x             ; F8C5[].0
F49A 81 FF           cmpa    #$FF
F49C 27 08           beq     invalidKey_F4A6
F49E 13 71 01 02     brclr   byte_71 1 loc_F4A4 ; restore high bit state
F4A2 8A 80           oraa    #$80
F4A4             loc_F4A4:
F4A4 0A              clv
F4A5 39              rts
F4A6             invalidKey_F4A6:
F4A6 86 2A           ldaa    #$2A
F4A8 0B              sev
F4A9 39              rts

This is an AlphaSmart Pro scan code to +0 converter.  It also takes care to preserve the high bit of the incoming code.  (The high bit so far seems to be a flag indicating that shift is active.)  We will see if I discover the mysteries of offset +0.

The last one is peculiar in that it involves a different array, 'byte_FAC5':

F4AA             sub_F4AA:
F4AA 84 7F           anda    #$7F            ; reset high bit
F4AC 80 20           suba    #' '            ; offset to space = 0
F4AE CE FA C5        ldx     #byte_FAC5      ; index into here
F4B1 16              tab
F4B2 3A              abx
F4B3 A6 00           ldaa    0,x
F4B5 39              rts

This array is 95 bytes.  95 = 127 - 32.  So it seems more than coincidental that this would map to the printable ASCII 20-7fh.  Unlike the scanCodeStructs_F8C5 array, this is not a structure, just a byte.

On a hunch, I am guessing that this is for Apple keyboard ("ADB") support.  Like PS/2, this protocol is documented.  I marked off a few entries in the list denoting what ASCII they correspond to:

FAC5 31   byte_FAC5:  fcb $31  ; 20h - space
...
FAD5 1D               fcb $1D  ; 30h - '0'
...
FAE6 80               fcb $80  ; 41h - 'A'
...
FAFF 86               fcb $86  ; 5ah - 'Z'
...
FB06 00               fcb 0    ; 61h - 'a'
...
FB1F 06               fcb 6    ; 7ah - 'z'

By checking against documentation these do correspond to the ADB key codes for these keys.  It's interesting to note that the high bit is set to indicate if the shift key should be down.  (e.g. difference between 'A' and 'a' and 'Z' and 'z' is just the high bit set for upper case.)

So that seems to solve one mystery.  Hot on this ADB stuff, the new hypothesis is that the +0 entry is the ADB code.  Cross-checking the entries above in the scanCodeStructs_F8C5 correlates once again, so I think this second mystery is solved as well.  So in sum:

and those are our 4 use-cases:  ADB or PS/2, and file sending or keyboard emulation.

What is more interesting to me now is that there are many scan codes which are not valid ASCII (i.e. +2 and +3 are 0).  These are doubtlessly the various function keys that I have not mapped out yet, so I should be able to use the PS/2 documentation to map out most of the remainder of the keyboard matrix.

Discussions