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