LisaKbd
; *******************************************************************
; Apple Lisa Keyboard A6MB101
; Keytronics A65-02592-055 PCB-201A
; FW v5.12 (i8049 controller, 6.0 MHz)
; (c) 1983
; *******************************************************************
; commented disassembly by Dr. P. Schäfer, 2006
;
; RAM
; 00..07 RB0: R0..R7
; 08..17 Stack
; 18..1F RB1: R0..R7
; 20..2B key masks
; 30..37 scancode pointer queue
; 38..3F scancode queue
; ===================================================================
; The keyboard is scanned every 1ms with a SYNC pulse (20µs low).
; If a key has been pressed or released, it answers with a data
; protocol:
;
; S 4 5 6 7 7 0 1 2 3 3
; | | | | | | |
; | | | | | +-+--- last bit has double length
; | | | | +--------- lower nibble
; | | +-+------------- last bit has double length
; | +------------------- upper nibble
; +----------------------- start bit (low)
;
; The length of one bit cell is around 15µs. Data is sent with
; inverted polarity (1=low) and upper nibble first. I.e. 80FAh will
; be sent as 08FAh.
;
; After Reset, the keyboard sends an ID byte, 080h followed by the
; Layout ID. 0FFh is sent as ID when the internal selftest failed.
;
; Layout ID codes: 10zyxxxxb
; 0BFh = US || |
; 0AFh = UK || +--- P2.3-0: DIP SW
; 0AEh = German |+------ P2.4: 1= US, 0= Intl.
; 0ADh = French +------- P2.5: unused, =1
;
; KData input is -INT, KData output is P2.7
; scan driver XR 22-950-3B is connected to P1.0-3 & PROG
; sense amplifier XR 22-908-03 is connected to P1.0-7 & PROG
; ===================================================================
; Jumpers:
; E1-E2: EA (closed)
; E3-E4: P2.5 (open)
; E5-E6: P2.4 (open US, closed Intl.)
; ===================================================================
-0000 44 00 JMP 200h ; Reset
0002 00 NOP
-0003 B6 FF JF0 0FFh ; INT
0005 24 83 JMP 183h
-0007 24 A1 JMP 1A1h ; Timer Int
; *******************************************************************
; Main Program, part II:
; enter here after selftest, F0 set when passed
; *******************************************************************
;
-0009 27 CLR A
000A B8 3F MOV R0,#3Fh
-000C A0 MOV @R0,A ; clear RAM
000D E8 0C DJNZ R0,0Ch
000F D5 SEL RB1
0010 BE 3F MOV R6,#3Fh
0012 BF 3F MOV R7,#3Fh
0014 C5 SEL RB0
0015 B9 30 MOV R1,#30h
0017 B8 1F MOV R0,#1Fh
0019 B6 23 JF0 23h ; selftest passed?
; no, send 08h, FFh
001B 23 08 MOV A,#08h
001D 34 75 CALL 175h ; send 08h
001F 23 FF MOV A,#0FFh
0021 34 75 CALL 175h ; send FFh
; yes, send 08h, xBh with x = Keyboard Layout
-0023 23 08 MOV A,#08h ; send 08h
0025 34 75 CALL 175h
0027 8A FF ORL P2,#0FFh
0029 0A IN A,P2 ; read Jumper & DIP switch
002A 43 80 ORL A,#80h
002C 53 BF ANL A,#0BFh
002E 47 SWAP A
002F 34 75 CALL 175h ; send xxxx101y, xxxx=P2.3-0, y=P2.4
0031 85 CLR F0
0032 95 CPL F0 ; set F0 which is already set
0033 55 STRT T
0034 25 EN TCNTI ; start timer and enable interrupt
;
; main loop -- scan key matrix
-0035 18 INC R0 ; count from #20 to #2B (12 columns)
0036 F8 MOV A,R0
0037 D3 2C XRL A,#2Ch
0039 C6 B4 JZ 0B4h ; all done ->
003B 34 88 CALL 188h ; scan column
003D C6 35 JZ 35h ; no key pressed, next one
; ...we got a keypress !!!
003F AB MOV R3,A ; store row result
0040 BA 19 MOV R2,#19h
-0042 EA 42 DJNZ R2,42h ; wait 50 cycles
0044 3F MOVD P7,A ; strobe chipset
0045 34 88 CALL 188h ; and scan again
0047 99 FE ANL P1,#0FEh ; clear P1.0,
0049 3F MOVD P7,A ; strobe again
004A 5B ANL A,R3 ; compare new scan result with old one
004B C6 35 JZ 35h ; and abort at mismatch
; ...it seems to be real
004D AB MOV R3,A ; store new value
004E BA 01 MOV R2,#01h
0050 F8 MOV A,R0
0051 47 SWAP A
0052 77 RR A
0053 53 78 ANL A,#78h ; column number *8
0055 AF MOV R7,A ; into R7 (scan table offset)
0056 FA MOV A,R2
-0057 5B ANL A,R3 ; row bit (R2) set?
0058 96 63 JNZ 63h ; yes ->
-005A FB MOV A,R3 ; restore row result
005B C6 35 JZ 35h ; and exit when no key pressed
005D FA MOV A,R2
005E E7 RL A
005F AA MOV R2,A ; prepare next mask
0060 1F INC R7 ; increment table offset
0061 04 57 JMP 057h ; and test next bit
; handle identified key
-0063 37 CPL A
0064 5B ANL A,R3
0065 AB MOV R3,A ; mask out current key
0066 D5 SEL RB1
0067 FE MOV A,R6 ; ???
0068 C5 SEL RB0
0069 C6 5A JZ 5Ah ; go, test next bit
006B FA MOV A,R2
006C 50 ANL A,@R0 ; current row bit set in mask?
006D 96 7F JNZ 7Fh ; yes ->
006F FA MOV A,R2
0070 D0 XRL A,@R0
0071 A0 MOV @R0,A ; set it
0072 FF MOV A,R7 ; get scan table offset
0073 A1 MOV @R1,A ; and store it into queue
0074 F9 MOV A,R1
0075 17 INC A
0076 53 37 ANL A,#37h
0078 A9 MOV R1,A ; increment pointer
0079 BE 80 MOV R6,#80h ; set make flag
007B 04 96 JMP 096h
; some selftest code for ROM checksum calculation
-007D A3 MOVP A,@A
007E 83 RET
; current row bit already set in mask,
; ignore keypress when this key is already in the queue
-007F F9 MOV A,R1
0080 AE MOV R6,A ; save queue pointer in R6
-0081 FF MOV A,R7 ; get scancode pointer
0082 D1 XRL A,@R1 ; found in queue?
0083 96 89 JNZ 89h ; no, test next location
0085 FE MOV A,R6
0086 A9 MOV R1,A ; else restore queue pointer
0087 04 5A JMP 05Ah ; and ignore this keypress
-0089 F9 MOV A,R1
008A 17 INC A
008B 53 37 ANL A,#37h
008D A9 MOV R1,A ; increment queue pointer
008E DE XRL A,R6
008F 96 81 JNZ 81h ; and test next one
0091 FA MOV A,R2
0092 D0 XRL A,@R0
0093 A0 MOV @R0,A ; clear row bit in mask
0094 BE 00 MOV R6,#00h ; and set break flag
; meet again here with scancode pointer in R7 and
; make (80h) / break (00h) flag in R6
-0096 FF MOV A,R7 ; get scancode pointer
0097 D3 01 XRL A,#01h
0099 C6 A6 JZ 0A6h ; =Shift (01h)? ->
009B D3 09 XRL A,#09h
009D C6 AD JZ 0ADh ; =Shift (08h)? ->
-009F FF MOV A,R7
00A0 E3 MOVP3 A,@A ; get scan code from table
00A1 4E ORL A,R6 ; merge in make/break flag
00A2 34 91 CALL 191h ; store scancode into scancode queue
00A4 04 5A JMP 05Ah ; and handle next key
; ignore left shift key when right one already pressed
-00A6 18 INC R0
00A7 F0 MOV A,@R0 ; get mask of next column
00A8 C8 DEC R0
00A9 12 5A JB0 5Ah ; ignore keypress when bit0 set
00AB 04 9F JMP 09Fh
; ignore right shift key when left one already pressed
-00AD C8 DEC R0
00AE F0 MOV A,@R0 ; get mask of previous column
00AF 18 INC R0
00B0 32 5A JB1 5Ah ; ignore keypress when bit1 set
00B2 04 9F JMP 09Fh
; all 12 columns scanned
-00B4 16 F4 JTF 0F4h ; timer expired -> zap one queue entry
-00B6 D5 SEL RB1
00B7 76 CC JF1 0CCh
00B9 FF MOV A,R7
00BA DE XRL A,R6
00BB C6 CC JZ 0CCh ; buffer empty ->
00BD FE MOV A,R6
00BE 96 C2 JNZ 0C2h
00C0 FF MOV A,R7
00C1 AE MOV R6,A
00C2 FF MOV A,R7
00C3 A9 MOV R1,A
00C4 07 DEC A
00C5 43 38 ORL A,#38h
00C7 AF MOV R7,A
00C8 F1 MOV A,@R1 ; get next char to send
00C9 47 SWAP A ; swap nibbles
00CA AC MOV R4,A ; and store into R4'
00CB B5 CPL F1 ; mark data available
-00CC BA 55 MOV R2,#55h
00CE C5 SEL RB0
; check for proper SYNC pulse
00CF BA 06 MOV R2,#06h ; max. SYNC pulse width
00D1 86 DA JNI 0DAh ; KData low ->
00D3 76 D7 JF1 0D7h ; data available -> enable interrupt
00D5 04 E1 JMP 0E1h
-00D7 05 EN I
00D8 04 E1 JMP 0E1h
;
-00DA EA D1 DJNZ R2,0D1h
00DC 9A 7F ANL P2,#7Fh ; KData has been low too long, shutdown
00DE 35 DIS TCNTI
00DF 04 DF JMP 0DFh ; wait for Reset
;
-00E1 B8 20 MOV R0,#20h
00E3 89 FF ORL P1,#0FFh
00E5 8A FF ORL P2,#0FFh
00E7 FD MOV A,R5
00E8 B6 EF JF0 0EFh
00EA 96 F2 JNZ 0F2h
00EC 95 CPL F0
00ED 04 F2 JMP 0F2h
-00EF C6 F2 JZ 0F2h
00F1 85 CLR F0
-00F2 04 3B JMP 03Bh
;
-00F4 C5 SEL RB0
00F5 27 CLR A
00F6 37 CPL A
00F7 A1 MOV @R1,A ; zap current scancode queue entry
00F8 F9 MOV A,R1
00F9 17 INC A
00FA 53 37 ANL A,#37h
00FC A9 MOV R1,A ; and increment pointer
00FD 04 B6 JMP 0B6h
; *******************************************************************
; external interrupt entry when F0 set
; *******************************************************************
;
-00FF D5 SEL RB1
0100 2C XCH A,R4 ; get char to send and save Acc
0101 76 06 JF1 06h ; valid -> send
0103 15 DIS I ; no, exit
0104 2C XCH A,R4 ; restore Acc
0105 93 RETR
-0106 86 71 JNI 71h ; KData stuck low -> abort
-0108 9A 7F ANL P2,#7Fh ; set KData low for start bit
010A 00 NOP
010B 00 NOP ; wait 1 cell length
010C 00 NOP
010D 12 51 JB0 51h ; bit0 =1 -> set KData low
010F 8A 80 ORL P2,#80h ; else set KData high
0111 00 NOP
0112 00 NOP
-0113 32 55 JB1 55h ; bit1 =1 -> set KData low
0115 8A 80 ORL P2,#80h ; else set KData high
0117 00 NOP
0118 00 NOP
-0119 00 NOP
011A 52 59 JB2 59h ; bit2 =1 -> set KData low
011C 8A 80 ORL P2,#80h ; else set KData high
011E 00 NOP
011F 00 NOP
-0120 72 5D JB3 5Dh ; bit3 =1 -> set KData low
0122 8A 80 ORL P2,#80h ; else set KData high
0124 00 NOP
0125 00 NOP
-0126 00 NOP
0127 00 NOP ; wait 1 bit cell to separate nibbles
0128 00 NOP
0129 00 NOP
012A 00 NOP
012B 00 NOP
012C 92 61 JB4 61h ; bit4 =1 -> set KData low
012E 8A 80 ORL P2,#80h ; else set KData high
0130 00 NOP
0131 00 NOP
-0132 B2 65 JB5 65h ; bit5 =1 -> set KData low
0134 8A 80 ORL P2,#80h ; else set KData high
0136 00 NOP
0137 00 NOP
-0138 00 NOP
0139 D2 69 JB6 69h ; bit6 =1 -> set KData low
013B 8A 80 ORL P2,#80h ; else set KData high
013D 00 NOP
013E 00 NOP
-013F F2 6D JB7 6Dh ; bit7 =1 -> set KData low
0141 8A 80 ORL P2,#80h ; else set KData high
0143 00 NOP
0144 00 NOP
-0145 00 NOP ; last bit has double length
0146 00 NOP
0147 00 NOP
0148 8A 80 ORL P2,#80h ; release KData
014A A5 CLR F1 ; mark character invalid
014B 15 DIS I
014C 2C XCH A,R4 ; and restore Acc
014D C5 SEL RB0
014E BD 01 MOV R5,#01h
0150 93 RETR
-0151 9A 7F ANL P2,#7Fh ; set KData low
0153 24 13 JMP 113h
-0155 9A 7F ANL P2,#7Fh ; set KData low
0157 24 19 JMP 119h
-0159 9A 7F ANL P2,#7Fh ; set KData low
015B 24 20 JMP 120h
-015D 9A 7F ANL P2,#7Fh ; set KData low
015F 24 26 JMP 126h
-0161 9A 7F ANL P2,#7Fh ; set KData low
0163 24 32 JMP 132h
-0165 9A 7F ANL P2,#7Fh ; set KData low
0167 24 38 JMP 138h
-0169 9A 7F ANL P2,#7Fh ; set KData low
016B 24 3F JMP 13Fh
-016D 9A 7F ANL P2,#7Fh ; set KData low
016F 24 45 JMP 145h
-0171 9A 7F ANL P2,#7Fh ; set KData low
-0173 24 73 JMP 173h ; wait for Reset or Interrupt
; wait for SYNC pulse on data line
-0175 86 75 JNI 75h ; wait for INT high
0177 00 NOP
-0178 86 7C JNI 7Ch ; wait for INT low
017A 24 78 JMP 178h
-017C BA 02 MOV R2,#02h
017E EA 7E DJNZ R2,7Eh ; wait 4 cycles
0180 D5 SEL RB1
0181 24 08 JMP 108h
; *******************************************************************
; external interrupt entry when F0 cleared
; *******************************************************************
;
-0183 C5 SEL RB0
0184 2D XCH A,R5
0185 27 CLR A ; clear R5 and exit
0186 2D XCH A,R5
0187 93 RETR
; scan column R0, mask result with value @R0 and return in A
-0188 F8 MOV A,R0 ; get column
0189 39 OUTL P1,A ; output value for 22-950-3B
018A 3F MOVD P7,A ; and pulse strobe line
018B 89 FF ORL P1,#0FFh
018D 09 IN A,P1 ; read row
018E 37 CPL A
018F D0 XRL A,@R0
0190 93 RETR
; store scancode into scancode queue
0191 D5 SEL RB1
0192 2E XCH A,R6
0193 C6 9F JZ 9Fh ; R6' zero ->
0195 A9 MOV R1,A ; scancode queue pointer
0196 07 DEC A
0197 43 38 ORL A,#38h
0199 2E XCH A,R6 ; decrement R6'
019A A1 MOV @R1,A ; and store scancode
019B FE MOV A,R6
019C DF XRL A,R7
019D 96 A0 JNZ 0A0h
-019F AE MOV R6,A ; restore R6'
-01A0 93 RETR
; *******************************************************************
; Timer interrupt entry
; *******************************************************************
;
-01A1 D5 SEL RB1
01A2 2A XCH A,R2
01A3 D3 55 XRL A,#55h
01A5 96 D3 JNZ 0D3h
01A7 23 83 MOV A,#83h
01A9 62 MOV T,A
01AA C7 MOV A,PSW
01AB 53 07 ANL A,#07h
01AD 07 DEC A
01AE E7 RL A
01AF 03 09 ADD A,#09h
01B1 A8 MOV R0,A
01B2 F0 MOV A,@R0
01B3 53 0E ANL A,#0Eh
01B5 96 D3 JNZ 0D3h
01B7 FF MOV A,R7
01B8 53 F8 ANL A,#0F8h
01BA D3 38 XRL A,#38h
01BC 96 D3 JNZ 0D3h
01BE C5 SEL RB0
01BF F9 MOV A,R1
01C0 53 F8 ANL A,#0F8h
01C2 D3 30 XRL A,#30h
01C4 96 D3 JNZ 0D3h
01C6 F8 MOV A,R0
01C7 53 F0 ANL A,#0F0h
01C9 D3 20 XRL A,#20h
01CB 96 D3 JNZ 0D3h
01CD D5 SEL RB1
01CE 23 AA MOV A,#0AAh
01D0 2A XCH A,R2
01D1 15 DIS I
01D2 93 RETR
01D3 89 FF ORL P1,#0FFh
01D5 8A FF ORL P2,#0FFh
01D7 80 MOVX A,@R0
01D8 27 CLR A
01D9 D7 MOV PSW,A
01DA B8 3F MOV R0,#3Fh
01DC A0 MOV @R0,A
01DD E8 DC DJNZ R0,0DCh
01DF D5 SEL RB1
01E0 BE 3F MOV R6,#3Fh
01E2 BF 3F MOV R7,#3Fh
01E4 C5 SEL RB0
01E5 B9 30 MOV R1,#30h
01E7 B8 1F MOV R0,#1Fh
01E9 A5 CLR F1
01EA 16 EC JTF 0ECh
01EC 34 F0 CALL 1F0h
01EE 04 35 JMP 035h
01F0 93 RETR
-01F1 A3 MOVP A,@A
01F2 83 RET
; *******************************************************************
; Main Progam:
; enter here from Reset, perform ROM and RAM selftest
; F0 cleared when test failed, set when passed
; *******************************************************************
;
-0200 27 CLR A
0201 D7 MOV PSW,A
0202 27 CLR A
0203 85 CLR F0 ; set test as failed
0204 C5 SEL RB0
; disable timer and interrupt
0205 35 DIS TCNTI
0206 15 DIS I
; calculate ROM checksum
0207 AE MOV R6,A ; clear R7, R6
0208 AF MOV R7,A
-0209 14 7D CALL 07Dh ; get byte (A) from page 0
020B 6E ADD A,R6
020C AE MOV R6,A ; and add it to R6
020D 1F INC R7
020E FF MOV A,R7
020F 96 09 JNZ 09h ; do this 256 times
-0211 34 F1 CALL 1F1h ; get byte (A) from page 1
0213 6E ADD A,R6
0214 AE MOV R6,A ; and add it to R6
0215 1F INC R7
0216 FF MOV A,R7
0217 96 11 JNZ 11h ; do this 256 times
-0219 A3 MOVP A,@A ; get byte (A) from page 2
021A 6E ADD A,R6
021B AE MOV R6,A ; and add it to R6
021C 1F INC R7
021D FF MOV A,R7
021E 96 19 JNZ 19h ; do this 256 times
-0220 E3 MOVP3 A,@A ; get byte (A) from page 3
0221 6E ADD A,R6
0222 AE MOV R6,A ; and add it to R6
0223 1F INC R7
0224 FF MOV A,R7
0225 96 20 JNZ 20h ; do this 256 times
0227 FE MOV A,R6 ; get checksum
0228 96 3D JNZ 3Dh ; exit when failed
; test RAM
022A B8 3F MOV R0,#3Fh ; start with 3Fh
-022C F8 MOV A,R0 ; address into A,
022D A0 MOV @R0,A ; into RAM cell,
022E F0 MOV A,@R0 ; back from there,
022F D8 XRL A,R0 ; same as in R0?
0230 96 3D JNZ 3Dh ; exit when failed
0232 F8 MOV A,R0
0233 37 CPL A
0234 A0 MOV @R0,A ; complement into RAM cell,
0235 F0 MOV A,@R0 ; back from there
0236 D8 XRL A,R0 ; this should be #FFh,
0237 37 CPL A ; now #00h
0238 96 3D JNZ 3Dh ; exit when failed
023A E8 2C DJNZ R0,2Ch ; do this for each location except R0
023C 95 CPL F0 ; flag test as passed
; selftest done, continue at -0009
-023D B8 08 MOV R0,#08h
023F B0 09 MOV @R0,#09h ; put #0009 as return addess
0241 18 INC R0 ; into first stack location
0242 B0 00 MOV @R0,#00h
0244 04 09 JMP 009h ; go to next part
-0246 26 00 JNT0 00h
; *******************************************************************
; Scan Code Table
; *******************************************************************
0300 79 7E 70 7D 74 68 78 75 ; Z Shft A ALck 1! `~ Tab Q
7E 00 00 48 45 41 57 42 ; Shft Retn Bksp =+ ]} \|
00 4C 5B 5A 40 51 44 56 ; /? '" ;: -_ 0) P [{
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
6E 6C 6A 69 64 73 65 66 ; B V G F 5% 4$ R T
58 6F 54 6B 62 61 67 52 ; M N J H 7& 6^ Y U
5E 5D 59 55 50 63 53 5F ; .> ,< L K 9( 8* I O
43 2C 49 4E 7F 7C 5C 46 ; <> . 0 ROptn Apple LOptn Spc Enter
6D 7A 7B 76 72 71 77 60 ; C X D S 3# 2@ W E
2F 2E 2B 2A 23 22 26 27 ; NRtn 3 + 6 = * 9 /
2D 4D 29 28 21 20 24 25 ; 2 1 5 4 - Clr 7 8