Close

20230222a -- Hunting for PS/2 Stuff

A project log for ROM Disassembly - AlphaSmart Pro

Wherein I disassemble the ROM from a vintage typewriter-thing

ziggurat29ziggurat29 02/24/2023 at 14:090 Comments

Eager to get the remaining scan codes for the keyboard, I started thinking about exploring the keyboard emulation aspect.  The product apparently works in two modes:  'standalone', which is what I've considered so far, and 'keyboard emulation' which is when it is attached to a desktop machine.  The keyboard emulation provides for file sending, which has been discussed a bit, but also as a general purpose keyboard.  This means that the various non-printing keys (shift, ctrl, F-keys, etc) are sent to the desktop, so there must be native AlphaSmart Pro scan code to standard (PS/2, ADB) scan code conversion somewhere.  And since PS/2 and ADB are documented, I should be able to correlate.

Knowing from prior experience with PS/2 that the first sequence when a PS/2 keyboard is attached is to receive a 'reset' command (0xff), and respond with an 'ack' (0xfa) and the 'passed self test' (0xaa), I did a textual search of #$AA in hopes of maybe finding such.  There was one reference with that text.

Address   Function Instruction                                   
-------   -------- -----------                                   
XROM:E2B7 sub_E265 86 AA                       ldaa    #$AA

to

E2B4             loc_E2B4:
E2B4 BD E2 BE        jsr     sub_E2BE
E2B7 86 AA           ldaa    #$AA
E2B9 BD F7 A0        jsr     sub_F7A0
E2BC 39              rts

Maybe this could be PS/2 emulation related?  I was in luck.  The sub_E2BE prior to that seems to be loading 0xFA:

E2BE             sub_E2BE:
E2BE 86 FA           ldaa    #$FA
E2C0 BD F7 A0        jsr     sub_F7A0
E2C3 39              rts

and they both call sub_F7A0 after.  So that's probably the 'send PS/2 byte' function.  There's a bunch of I/O bit fiddling and delays based on 'send speed', so there will be a bit of work to do in there.  This shouldn't be too bad since PS/2 is a documented protocol.

This led up to a chain that is clearly the PS/2 host command handler.  Most of the stuff is ignored, but some critical things like 'reset' and 'echo' and 'identify' and 'get scan code set' are important.

E265             ps2HandleCommand_E265:
E265 BD E2 FE        jsr     ps2GetByte_E2FE ; XXX ps2 get byte
E268 29 49           bvs     leave_E2B3
E26A 96 72           ldaa    byte_72
E26C 81 FF           cmpa    #$FF            ; 'reset and self-test'
E26E 27 44           beq     ps2ResetResponse_E2B4
E270 81 FE           cmpa    #$FE            ; 'resend last byte'
E272 27 49           beq     locret_E2BD     ; just ignore; no ACK needed
E274 81 FD           cmpa    #$FD            ; 'set specific key to make only'
E276 27 60           beq     ps2EatNack_E2D8 ; PS/2 eat-and-ack for ignored commands
E278 81 FC           cmpa    #$FC            ; 'set specific key to make/release'
E27A 27 5C           beq     ps2EatNack_E2D8 ; PS/2 eat-and-ack for ignored commands
E27C 81 FB           cmpa    #$FB            ; 'set specific key to typematic/autorepeat only'
E27E 27 58           beq     ps2EatNack_E2D8 ; PS/2 eat-and-ack for ignored commands
E280 81 FA           cmpa    #$FA            ; 'set all keys to typematic/autorepeat/make/release'
E282 27 2C           beq     ps2AckIgnore_E2B0 ; PS/2 just ack for ignored commands
E284 81 F9           cmpa    #$F9            ; 'set all keys to make only'
E286 27 28           beq     ps2AckIgnore_E2B0 ; PS/2 just ack for ignored commands
E288 81 F8           cmpa    #$F8            ; 'set all keys to make/release'
E28A 27 24           beq     ps2AckIgnore_E2B0 ; PS/2 just ack for ignored commands
E28C 81 F7           cmpa    #$F7            ; 'set all keys to typematic/autorepeat only'
E28E 27 20           beq     ps2AckIgnore_E2B0 ; PS/2 just ack for ignored commands
E290 81 F6           cmpa    #$F6            ; 'set default parameters'
E292 27 1C           beq     ps2AckIgnore_E2B0 ; PS/2 just ack for ignored commands
E294 81 F5           cmpa    #$F5            ; 'disable scanning'
E296 27 18           beq     ps2AckIgnore_E2B0 ; PS/2 just ack for ignored commands
E298 81 F4           cmpa    #$F4            ; 'enable scanning'
E29A 27 14           beq     ps2AckIgnore_E2B0 ; PS/2 just ack for ignored commands
E29C 81 F3           cmpa    #$F3            ; 'set typematic rate and delay'
E29E 27 38           beq     ps2EatNack_E2D8 ; PS/2 eat-and-ack for ignored commands
E2A0 81 F2           cmpa    #$F2            ; 'identify keyboard'
E2A2 27 26           beq     ps2IdentifyKeyboard_E2CA
E2A4 81 F0           cmpa    #$F0            ; 'get/set current scan code set'
E2A6 27 3A           beq     ps2scanCodeSet_E2E2
E2A8 81 EE           cmpa    #$EE            ; 'echo'; used for liveness testing by controller
E2AA 27 18           beq     ps2sendEcho_E2C4
E2AC 81 ED           cmpa    #$ED            ; 'set LEDs'
E2AE 27 28           beq     ps2EatNack_E2D8 ; PS/2 eat-and-ack for ignored commands
E2B0             ps2AckIgnore_E2B0:
E2B0 BD E2 BE        jsr     ps2SendAck_E2BE ; PS/2 just ack for ignored commands
E2B3             leave_E2B3:
E2B3 39              rts

From this, we can also confirm that this device is emulating 'scan code set 2'.  No surprise, as this is the most common, and the only one guaranteed to be supported, but it's still good to confirm.

The way this scan code set works is that there is a 'make' code (when key down), and a 'break' code (when key up).  The 'make' code is usually a single byte, and the 'break' code has a prefix of 0xF0 followed by that same byte.  There are a few keys that involve two-byte make codes (prefixed with 0xe0) and have three-byte break codes (prefixed with 0xe0, 0xf0).

Looking back at the scan code mapped structures at F8C5, there are byte values at offset +0 and +1 that I hadn't figured out yet.  Making a spot check of a few keys and correlating that to PS/2 documentation for 'scan code set 2', it is strongly suggestive that offset +1 is the 'base' PS/2 code (i.e., without the 0xf0 or 0xe0 prefix, which is probably programmatically supplied).

FA75 0E    fcb $E     ; scan code 6c - 'E'
FA76 24    fcb $24  ; <- PS/2 scan code for 'E'!!!
FA77 65    fcb 'e'
FA78 45    fcb 'E'
FA79 0D    fcb $D     ; scan code 6d - 'W'
FA7A 1D    fcb $1D  ; <- PS/2 scan code for 'W'
FA7B 77    fcb 'w'
FA7C 57    fcb 'W'
FA7D 0C    fcb $C     ; scan code 6e - 'Q'
FA7E 15    fcb $15  ; <- PS/2 scan code for 'Q'
FA7F 71    fcb 'q'
FA80 51    fcb 'Q'

So I think there's another mystery solved.  That should put some light on all the references to the +1 offset, so I've got some commenting and labelling to do....

Discussions