; ******************************************************************* ; 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