8048 AT INTERN 4584751 ======================== Commented Intel 8048 disassembly of the IBM 83-key Type-2 Model F (XT) keyboard ROM A. Tarpai 2018 (Questions & comments are welcome to tarpai76 at gmail) This file has been downloaded from http://www.halicery.com/8042/8048_XT_INTERN.TEXT Because of this analysis was done after the 84-key AT ROM (1503099), much of the general info can be found there. The two keyboards are very similar electronically. The main purpose was to confirm/expand the knowledge on that mysterious silver, SENSE AMPLIFIER IC ("SA"). Only the most important figs for reading the comments are here. The IBM PC/XT keyboard is this "compact", tough keyboard from 1981 with capacitive technology and 83 keys, numbered after IBM as: ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ __________ __________ __________ | 59 || 60 | | 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9 || 10 || 11 || 12 || 13 || 14 || 69 || 70 | |____||____| |____||____||____||____||____||____||____||____||____||____||____||____||____||__________||__________||__________| ____ ____ _______ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ _____ ______ ____ ____ ____ ____ | 61 || 62 | | 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22 || 23 || 24 || 25 || 26 || 27 || 28 || 71 || 72 || 73 || 74 | |____||____| |_______||____||____||____||____||____||____||____||____||____||____||____||_____|| ||____||____||____||____| ____ ____ ________ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ | | ____ ____ ____ ____ | 63 || 64 | | 29 || 30 || 31 || 32 || 33 || 34 || 35 || 36 || 37 || 38 || 39 || 40 || 41 || || 75 || 76 || 77 || | |____||____| |________||____||____||____||____||____||____||____||____||____||____||____||____||______||____||____||____|| | ____ ____ _____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ _______ ______ ____ ____ ____ | 78 | | 65 || 66 | | 42 || 43 || 44 || 45 || 46 || 47 || 48 || 49 || 50 || 51 || 52 || 53 || 54 || 55 || 79 || 80 || 81 || | |____||____| |_____||____||____||____||____||____||____||____||____||____||____||____||_______||______||____||____||____|| | ____ ____ __________ _________________________________________________________ ___________ __________ __________ | | | 67 || 68 | | 56 || 57 || 58 || 82 || 83 || | |____||____| |__________||_________________________________________________________||___________||__________||__________||____| Schematics ========== The firmware corresponds with the Logic Diagram of the Type-2 PC/XT Keyboard from IBM Ref Manuals. 12 drive lines are connected directly to the 8048 (port P1 and P2 HI), while P2 LO controls the SA: 8 col - -+-+-+-+-+-+-+-+- - -+-+-+-+-+-+-+-+- P1 - -+-+-+-+-+-+-+-+- +---------------------------> - -+-+-+-+-+-+-+-+- | - -+-+-+-+-+-+-+-+- 12 row | - -+-+-+-+-+-+-+-+- | - -+-+-+-+-+-+-+-+- | - -+-+-+-+-+-+-+-+- | - -+-+-+-+-+-+-+-+- | P2 HI - -+-+-+-+-+-+-+-+- | +----------------> - -+-+-+-+-+-+-+-+- | | - -+-+-+-+-+-+-+-+- | | | | | | | | | | | | | | P2 LO - +-----------------------+ | +-----------------> - | SENSE AMPLIFIER | | | 3-to-8 - | MUX IC | | | +-----------------------+ | | | Q out | | | | | _ | | \____ low | | | | | | | +-->--+ +5V---+------->-------+ | | | | | | +------------------------------------------------------------------------+ | | P1 P2 P23 T1 DB5 INT| <------+------- KB DATA (pulled low by KB) | 8048 | | MICROCOMPUTER DB6 T0 | <------+------- KB CLK (REQIN/REQOUT when pulled low) +------------------------------------------------------------------------+ REQIN | | | +5V---+-------->------+ REQOUT The XT's SENSE AMPLIFIER ======================== The timing diagram according to the code in the ROM shows similarities with the AT (see 1503099). Opposed to the AT there is no variable threshold used: each sensing is driven with sensitivity threshold "3". Drive is separately controlled. The enable SA, drop drive pulse, read SA output appears in the same sequence: read T1 1 2 3 4 5 6 7 8 | | | | | | | | ______ ___|____|______ \_________________________/ | | SA LATCH RESET (DB7) | | ___________ ___________________|____|______ ___________\____/ | \_|______ SA CLOSED/OUTPUT Q (P23/T1) | _________| __________________________/ \___________ DRIVE SELECT (P1/P2HI) ___________ _________ __________________________ ___________X_________3__________________________ SENSE SELECT (P2_0..3) mux thr This is done for each sensor writing P2_0_3 twice: 1. clear RESET 2. clear Q and write SENSE SELECT with 0..7 3. pull Q high: SA latches the mux address 4. keep Q high and write SENSE SELECT with "3" 5. write DRIVE SELECT (charge) 6. pull RESET high: SA latches threshold value and enabled 7. Now clear DRIVE SELECT (discharge): drive pulse goes low 8. sample T1 for low (=SA CLOSED, depressed button) (On RESET P1, P2 set FFh high) Ports of the 8048 ================= 7 6 5 4 3 2 1 0 +--------+--------+--------+--------+--------+--------+--------+--------+ | | CLK | DATA | | | | DB +--------+--------+--------+--------+--------+--------+--------+--------+ | | T0 INT 7 6 5 4 3 2 1 0 +--------+--------+--------+--------+--------+--------+--------+--------+ | DRIVE SELECT LO | P1 +--------+--------+--------+--------+--------+--------+--------+--------+ 7 6 5 4 3 2 1 0 +--------+--------+--------+--------+--------+--------+--------+--------+ | DRIVE SELECT HI | Q | SENSE SELECT | P2 +--------+--------+--------+--------+--------+--------+--------+--------+ | T1 The matrix scanning loop ======================== For each row all of the 8 columns are sampled and the result is stored in R4, the "Sense Word". The controller drives a double pulse to confirm a changed row state (Double Sense). When these readings differ, it is considered noise and throws it away, and keeps the previous state of the row. Flow chart ========== R2: Row/Col counter R3: typematic counter (fix Delay/Rate used) R5: scancode of the typematic key RESET <---------------------------------------------------------------+ | | R2=00 | | | +------>------ R2=0? ------ Init | | | | | | | Wait Timer | | | | | | +----<------+ | | | | | End/beginning ------------ REQIN? -----------> Run BAT | | of scan? | | | | | | OK? --> put AA into FIFO --->--+ | | Transmit one scancode | | | | from FIFO +---------------------->------+ | | | | +-------<--------------+ | | | Scan row R2 | | | For all columns: | changed? ---------------------------------------------+ | | | | | Double Sense | | update row state | | | | | Push? ---------------+ | held? ---------+ | | | | | | Put makecode | | DEC R3*--------+ | into scancode FIFO | | expired? | | | | | | | | Put makecode into R5 | | | Put makecode R5 | Load Delay into R3 | | | into scancode FIFO | | | | | | Put breakcode | | | | Load Rate into R3 into scancode FIFO | | | | | | | | | | | R5 released? --+ | | | | | | | | | | | | | Clear R5 | | | | | | | | | +-----<-------+----<------+------------<----------+------<---+-----<--+ | | | DEC row | | +------<---------+ *: once per full scan, i.e. multiple keys held keep the same rate RAM map for this firmware ========================= 64 bytes of RAM +----------------------------------------+ | $3F | | ... 12 bytes MATRIX MAP | | $34 | | | | $30 | | ... 16+1 byte FIFO | | $20 | +----------------------------------------+ | $1F R7' | | $1E R6' | | $1D R5' | | $1C R4' | | $1B R3' Flag for Typematic counter | | $1A R2' Typematic counter | | $19 R1' tmp for FIFO out | | $18 R0' FIFO PTR RB1 | +----------------------------------------+ | STACK | +----------------------------------------+ | R7 | | R6 SENSE SELECT | | R5 Typematic makecode | | R4 Sense Word | | R3 Diff Word (complemented) | | R2 Row/Col Counter | | R1 MATRIX MAP PTR | | R0 DRIVE BIT SELECT | +----------------------------------------+ $1B: Flag for Typematic counter to count full scans. Start of scan clears it, Typematic counter $1A is only decremented when it is zero, then $1B is set. CPU Flag F0 seems global meaning: cleared after RESET, in BAT, Set in Send A, but cleared in REQIN when reading 'flips', unstable, floating? DATA not connected well? Used as flag in Double Sense CPU flag F1: FIFO is empty when 0. Then 8048 will also initialize the FIFO and the FIFO PTR (Put FIFO routine) Opening 4584751.m1 ********************* ** RESET ** ********************* 0000: 00 NOP 0001: C5 SEL RB0 ; RAM bank0 0002: 15 DIS I ; disable external interrupt 0003: 35 DIS TCNTI ; disable timer/counter interrupt 0004: E5 SEL MB0 ; ROM bank0 0005: 85 CLR F0 0006: A5 CLR F1 0007: 23 DF MOV A,#$DF 0009: 02 OUTL BUS,A ; release CLK and pull DATA low ; _________ ; C____/ ; ____ bus idle state ; D____\____ 000A: 27 CLR A 000B: B8 3F MOV R0,#$3F ; clear 64 bytes of RAM ---+ 000D: A0 MOV @R0,A ; with all registers | 000E: E8 0D DJNZ R0,$000D ; -------------------------+ 0010: 54 34 CALL $0234 ; -> RETR (to clear all PSW flags? But cleared right after.. "RETR reenables the interrupt input logic"?) 0012: 27 CLR A 0013: AA MOV R2,A ; init R2=00 Row/Col Counter 0014: D7 MOV PSW,A ; clear user flags (CY,AC,F0 and BS) 0015: 62 MOV T,A ; clear Timer 0016: 39 OUTL P1,A ; set all drive lines 0017: 3A OUTL P2,A 0018: 55 STRT T ; start Timer ***************************************** ** MAIN SCANNING LOOP ** ** ** ***************************************** ; R2 is the Row/Col coordinate for a given sensor, which starts counting down from 60h (96 decimal). ; Each row decrements R2 by 8, or in 8048 language lacking SUB, adds F8h. ; R0 is the given line to drive and goes to P1 or P2. ; We read 12 rows, scanning goes backwards until zero: ; ; HI -> P2 | LO -> P1 ; R2 60 58 50 48 | 40 38 30 28 20 18 10 08 <-- Row/Col coordinate ; R0 80 40 20 10 | 80 40 20 10 08 04 02 01 <-- DRIVE 0019: FA MOV A,R2 001A: 96 2D JNZ $002D ; R2 nonzero -> 001C: D5 SEL RB1 ; R2 zero: init start of scan 001D: BB 00 MOV R3,#$00 ; clear $1B (flag to dec typematic $1A) 001F: C5 SEL RB0 0020: B8 01 MOV R0,#$01 ; R0=01 drive select LO 0022: BA 60 MOV R2,#$60 ; R2=60 0024: B9 40 MOV R1,#$40 ; init MATRIX MAP PTR 0026: 42 MOV A,T 0027: 03 4D ADD A,#$4D 0029: E6 26 JNC $0026 ; wait a little for timer.. 002B: 27 CLR A ; between full scans 002C: 62 MOV T,A ; reset timer 002D: FA MOV A,R2 002E: D3 08 XRL A,#$08 0030: C6 36 JZ $0036 ; R2=08? -> 0032: D3 68 XRL A,#$68 0034: 96 38 JNZ $0038 ; (there is a TIMER loop between these two) 0036: 54 35 CALL $0235 ; --> R2=08 or 60: Check REQIN, Send from FIFO if any 0038: C9 DEC R1 ; dec MATRIX MAP PTR 0039: F8 MOV A,R0 003A: 77 RR A 003B: A8 MOV R0,A ; rotate R0 DRIVE SELECT: init to 01 -> we start with 80 003C: BE 07 MOV R6,#$07 ; load SENSE SELECT counter 003E: 27 CLR A 003F: AC MOV R4,A ; clear Sense Word R4: row status 0040: BF 08 MOV R7,#$08 ; column counter 0042: FA MOV A,R2 0043: D3 40 XRL A,#$40 0045: 96 49 JNZ $0049 0047: B8 80 MOV R0,#$80 ; R2=40: set R0=80 0049: FA MOV A,R2 ; 60 58 50 48 | 40 38 30 28 20 18 10 08 (the 12 R2 values) 004A: 97 CLR C 004B: 03 B8 ADD A,#$B8 ; R2>=48? -> drive hi 004D: F6 69 JC $0069 ; R2<48? -> drive lo ***************************************** ** SENSE ** ** ** ***************************************** 7 6 5 4 3 2 1 0 Result in R4 with +---+---+---+---+---+---+---+---+ opposite lsb | | | 1 | | | | | | R4 +---+---+---+---+---+---+---+---+ eg. col 2 active ; drive LO LINES on P1 004F: 98 7F ANL BUS,#$7F ; clear RESET ----------------------------------------+ 0051: FE MOV A,R6 | 0052: 3A OUTL P2,A ; clear Q and write SENSE SELECT | 0053: CE DEC R6 ; | 0054: 8A 08 ORL P2,#$08 ; pull Q high | 0056: 23 0B MOV A,#$0B | 0058: 3A OUTL P2,A ; keep Q high and write sens 3 | 0059: F8 MOV A,R0 | 005A: 39 OUTL P1,A ; R0 -> P1 DRIVE SELECT | 005B: 88 80 ORL BUS,#$80 ; pull RESET high | 005D: 99 00 ANL P1,#$00 ; clear drive select: start SENSE | 005F: 56 62 JT1 $0062 ; sample Q | 0061: 1C INC R4 ; low? set bit=pushed/closed sensor | 0062: FC MOV A,R4 | 0063: 77 RR A | 0064: AC MOV R4,A | 0065: EF 4F DJNZ R7,$004F ; do this 8 times ------------------------------------+ 0067: 04 80 JMP $0080 ; drive HI LINES on P2 HI (guess P1=00) 0069: 98 7F ANL BUS,#$7F ; clear RESET ----------------------------------------+ 006B: FE MOV A,R6 | 006C: 3A OUTL P2,A ; write SENSE SELECT and clear Q | 006D: CE DEC R6 | 006E: 8A 08 ORL P2,#$08 ; pull Q high | 0070: F8 MOV A,R0 | 0071: 03 0B ADD A,#$0B ; P2 <- R0 + 0B | 0073: 3A OUTL P2,A ; keep Q high and write sens 3 + P2_HI | 0074: 88 80 ORL BUS,#$80 ; pull RESET high | 0076: 9A 0F ANL P2,#$0F ; clear drive select: start SENSE | 0078: 56 7B JT1 $007B ; sample Q | 007A: 1C INC R4 ; low? set bit=pushed/closed sensor | 007B: FC MOV A,R4 | 007C: 77 RR A | 007D: AC MOV R4,A | 007E: EF 69 DJNZ R7,$0069 ; do this 8 times ------------------------------------+ ***************************** ** Double Sense ** ***************************** R4 = present row state @R1 = previous row state 0080: B6 98 JF0 $0098 ; F0=1? have read nonzero row twice -> 0082: F1 MOV A,@R1 ; F0=0: first row reading 0083: DC XRL A,R4 0084: 96 93 JNZ $0093 ; state change? -> 0086: FC MOV A,R4 ; no, same state: 0087: 96 8F JNZ $008F ; state nonzero? -> 0089: FA MOV A,R2 ; same state and zero: 008A: 03 F8 ADD A,#$F8 ; add F8 = sub 08 008C: AA MOV R2,A 008D: 04 19 JMP $0019 ; --> scan next row 008F: BB FF MOV R3,#$FF ; same non-zero state: set no change on row's Diff word ('1' means no change!) 0091: 04 A7 JMP $00A7 ; R3 = FF and encode, possibly held key -> 0093: 95 CPL F0 ; state change: flip F0 0094: FC MOV A,R4 0095: AB MOV R3,A 0096: 04 3C JMP $003C ; save new state to R3 and -> read row again 0098: 85 CLR F0 ; F0=1, we've read nonzero row twice: clear F0 0099: FC MOV A,R4 009A: DB XRL A,R3 ; compare 1st and 2nd 009B: C6 A1 JZ $00A1 ; same, stable -> 009D: F1 MOV A,@R1 ; 1st and 2nd is different: throw away both sensing 009E: AC MOV R4,A ; if prev is zero -> advance to next row. If not: set Diff Word to FF (no change) and proceed 009F: 04 86 JMP $0086 ; 00A1: F1 MOV A,@R1 ; 1st and 2nd same: 00A2: DC XRL A,R4 00A3: 37 CPL A ; R3 Diff word is OPPOSITE: '1' means no change. 00A4: AB MOV R3,A ; R3 = cpl of changed bits 00A5: FC MOV A,R4 00A6: A1 MOV @R1,A ; update map.. then fall thru to encode ***************************************** ** Change encoding ** ** R4 = new state ** ** R3 = complemented Diff Word ** ***************************************** We have 4 cases: prev ~diff state 0 1 0 nothing 0 1 1 push 1 0 0 release 1 0 1 held 00A7: BF 08 MOV R7,#$08 ; 00A9: CA DEC R2 ; dec col --------------------+ 00AA: FB MOV A,R3 | 00AB: 12 D1 JB0 $00D1 ; no change? | 00AD: 77 RR A ; change | 00AE: AB MOV R3,A ; | 00AF: FC MOV A,R4 | 00B0: 12 C4 JB0 $00C4 ; -> to push | 00B2: 77 RR A ; 10 release: | 00B3: AC MOV R4,A | 00B4: FA MOV A,R2 | 00B5: E3 MOVP3 A,@A ; load scancode | 00B6: DD XRL A,R5 ; last released? | 00B7: 96 BA JNZ $00BA | 00B9: AD MOV R5,A ; yes: clear R5=00 | 00BA: FA MOV A,R2 ; no | 00BB: E3 MOVP3 A,@A ; load scancode | 00BC: 43 80 ORL A,#$80 ; breakcode | 00BE: 54 20 CALL $0220 ; --> Put 'A' into FIFO | 00C0: EF A9 DJNZ R7,$00A9 ; -----------------------------+ 00C2: 04 19 JMP $0019 ; --> next row | | 00C4: 77 RR A ; 01: push | 00C5: AC MOV R4,A | 00C6: FA MOV A,R2 | 00C7: E3 MOVP3 A,@A ; load scancode | 00C8: AD MOV R5,A ; store in R5: last held key | 00C9: 54 20 CALL $0220 ; --> Put 'A' into FIFO | 00CB: 74 60 CALL $0360 ; --> typematic delay | 00CD: EF A9 DJNZ R7,$00A9 ; -----------------------------+ 00CF: 04 19 JMP $0019 ; --> next row | | 00D1: 77 RR A ; no change: | 00D2: AB MOV R3,A ; | 00D3: FC MOV A,R4 | 00D4: 12 DC JB0 $00DC ; key down? | 00D6: 77 RR A ; 00: do nothing | 00D7: AC MOV R4,A | 00D8: EF A9 DJNZ R7,$00A9 ; -----------------------------+ 00DA: 04 19 JMP $0019 ; --> next row | | 00DC: 77 RR A ; no change, 11 key held | 00DD: AC MOV R4,A | 00DE: 74 63 CALL $0363 ; --> typematic rate | 00E0: EF A9 DJNZ R7,$00A9 ; -----------------------------+ 00E2: 04 19 JMP $0019 ; --> Main Loop for next row >> SUB TEST ROM chksum 00E4: A3 MOVP A,@A ; read page-0 >> SUM ROM 00E5: 69 ADD A,R1 ; sum 00E6: A9 MOV R1,A 00E7: F8 MOV A,R0 00E8: 83 RET 00E9: | 00 00 00 00 00 00 00 | ................ 00F0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ *********************** ** BAT ** *********************** 0100: C5 SEL RB0 0101: 15 DIS I 0102: 35 DIS TCNTI 0103: E5 SEL MB0 0104: 85 CLR F0 0105: A5 CLR F1 0106: 27 CLR A 0107: B8 3F MOV R0,#$3F ; TEST 64 bytes of RAM with FF and 00 0109: 37 CPL A ; ---------------------+ 010A: A0 MOV @R0,A ; write FF | 010B: D0 XRL A,@R0 ; read back, same? | 010C: 96 28 JNZ $0128 ; -> err | 010E: A0 MOV @R0,A ; ok, write 00 | 010F: D0 XRL A,@R0 ; read back, same? | 0110: 96 28 JNZ $0128 ; -> err | 0112: E8 09 DJNZ R0,$0109 ; ok ------------------+ 0114: F8 MOV A,R0 ; TEST 1KB ROM ----------+ 0115: A3 MOVP A,@A ; read Page-1 | 0116: 14 E5 CALL $00E5 ; sum | 0118: E3 MOVP3 A,@A ; read Page-3 | 0119: 14 E5 CALL $00E5 ; sum | 011B: 14 E4 CALL $00E4 ; read Page-0 and sum | 011D: 54 00 CALL $0200 ; read Page-2 and sum | 011F: E8 14 DJNZ R0,$0114 ; -----------------------+ 0121: F9 MOV A,R1 0122: 96 28 JNZ $0128 ; sum non-zero? -> err 0124: 23 AA MOV A,#$AA ; 0126: 54 20 CALL $0220 ; --> OK sum=0: Put AA into FIFO (will send Self-test passed), then Start 0128: 04 10 JMP $0010 ; --> RAM err, ROM chksum err: just Start 012A: | 00 00 00 00 00 00 | ................ 0130: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 0140: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 0150: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 0160: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 0170: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 0180: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 0190: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 01A0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 01B0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 01C0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 01D0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 01E0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 01F0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ >> SUB TEST ROM chksum 0200: A3 MOVP A,@A ; read page-2 0201: 04 E5 JMP $00E5 ; add and return ************************************* ** SUB SHIFT FIFO after transmit ** ************************************* 0203: D5 SEL RB1 0204: F8 MOV A,R0 0205: 07 DEC A ; dec FIFO PTR $18 0206: AF MOV R7,A ; last byte to move 0207: A8 MOV R0,A 0208: D3 20 XRL A,#$20 : nothing to move? 020A: 96 0E JNZ $020E 020C: A5 CLR F1 ; set FIFO empty and RETR 020D: 93 RETR 020E: B9 21 MOV R1,#$21 ; at least one more above $20 0210: F1 MOV A,@R1 ; ---------------------------+ 0211: C9 DEC R1 | 0212: A1 MOV @R1,A | 0213: 19 INC R1 | 0214: FF MOV A,R7 | 0215: D9 XRL A,R1 | 0216: C6 0D JZ $020D ; last moved? -> RETR | 0218: F9 MOV A,R1 ; (redundant.. | 0219: D3 30 XRL A,#$30 ; never executes?) | 021B: C6 0D JZ $020D ; moved last as $30? -> RETR | 021D: 19 INC R1 ; next | 021E: 44 10 JMP $0210 ; ---------------------------+ R7 R7 would be anyway | | $20 $21 $22 $23 $24 .. .. .. .. .. .. .. .. .. .. $30 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | <-- <-- <-- FF Scancode FIFO $18 -----------------------------------´ shift FIFO | FIFO PTR FIFO empty: $18=20 and F1=0 overrun byte next slot to fill ********************************* ** SUB Put 'A' into FIFO ** ********************************* 0220: D5 SEL RB1 0221: 76 26 JF1 $0226 0223: B8 20 MOV R0,#$20 ; F1=0, FIFO empty: init FIFO PTR $18=20 0225: B5 CPL F1 ; and set F1=1 (FIFO has data) 0226: AF MOV R7,A ; save A 0227: F8 MOV A,R0 0228: D3 31 XRL A,#$31 022A: C6 34 JZ $0234 ; $18=31? -> FIFO full: RETR 022C: 07 DEC A 022D: 96 31 JNZ $0231 022F: BF FF MOV R7,#$FF ; last slot to fill, $18=30: load FIFO overrun byte 0231: FF MOV A,R7 ; ok, restore A 0232: A0 MOV @R0,A ; store at @$18 0233: 18 INC R0 ; inc FIFO PTR 0234: 93 RETR ********************************* ** SUB Send A ** ********************************* IRQ1: when set (pending, serviced) it also pulls DATA low on the system board -> before sending anything, 8048 releases DATA.. and checks if stays high. If it's low, polls it for a while and waits for PC to finish processing the previous byte. XT KBD_RESET: BIOS holds CLK low for 20ms.. then reads port. When 'AA' it is a keyboard. When '65' then there is a test-board connected thru the KB DIN: loads a test program to 500h and executes. This is MANUFACTURING TEST 2. 0235: D5 SEL RB1 ; Select Register Bank 1 0236: 85 CLR F0 0237: 95 CPL F0 ; F0=1 0238: 26 87 JNT0 $0287 ; CLK held low? REQIN -> 023A: 76 3D JF1 $023D ; F1 set? scancode in FIFO? -> send 023C: 93 RETR ; SEL RB0 023D: B9 20 MOV R1,#$20 ; F1 set: 023F: F1 MOV A,@R1 ; load from FIFO $20 0240: 97 CLR C 0241: A7 CPL C ; set Carry (prepare sending 9 bits starting with '1') 0242: F7 RLC A ; rotate thru carry = 7 6 5 4 3 2 1 0 C 0243: 26 87 JNT0 $0287 ; CLK went low in the meantime? do REQIN instead -> 0245: 98 BF ANL BUS,#$BF ; ok, CLK high: pull it low 0247: 88 20 ORL BUS,#$20 ; and release DATA.. 0249: 00 NOP 024A: 00 NOP ; = KB signals a REQOUT: 024B: BF 0E MOV R7,#$0E ; now check if DATA stays high (when low, wait a little, probably IRQ1 pending and DATA pulled low by PC) 024D: 86 53 JNI $0253 ; --------------+ 024F: 86 55 JNI $0255 | 0251: 44 5C JMP $025C ; -> DATA released, high -> transmit 0253: 44 55 JMP $0255 | 0255: EF 4D DJNZ R7,$024D ; --------------+ 0257: 88 40 ORL BUS,#$40 ; DATA still low: we give up, release CLK high 0259: 98 DF ANL BUS,#$DF ; pull DATA low (KB idle state) 025B: 93 RETR ; and RETR ************************************ ** JMP DATA low: transmit A ** ************************************ ________ C \_______ We enter here with a stable REQOUT state: 8048 pulls CLK low and DATA is released. _______ Carry and A has the 'pre-rotated' 9-bits to transmit: x x x x x x x x 1 -> KB DATA D________/ Transmission is lsb first when CLK is low. LS322 latches data on the low-to-high clock transition. During normal operation the LS322 is cleared and enabled. IRQ1 is cleared: the IRQ flip-flop is in RESET state. The KB will send 9 bits of data: START=1 and 8 data bits. Bits 'rotated right' into LS322 (note the flip-flops' output A..H from the LS322 are connected 'backwards' to PORT-A). When START=1 reaches FF-H/bit0, QH' goes high. Although QH' is connected to the D input of the IRQ-FF, there is a propagation delay - that time IRQ-FF has already latched the 'previous' state of QH', i.e. still low. When the last bit arrives IRQ-FF enters its PRESET state and IRQ1 is signalled. Also, Q* goes low and does two things. It keeps KB DATA pulled low: the KB will not send another byte until KB DATA is released again. Q* is also connected back to PRE*: it 'locks' the FF in the PRESET-STATE regardless of what's happenning from now on on the D input. It can only be RESET again by asserting CLR*, which is connected to PB7 and driven by the ISR, i.e. interrupt has been served. Then the IRQ flip-flop is in RESET state again. LS322 IRQ-FF +---------------------------------------+ +----------+ | | delay | | Q --------------> IRQ1 KB DATA ---> D | -> FF-A -> FF-B ... -> FF-H --| QH' ---------------> D | | ____CLK | | | | | ___ CLK | ___ ___ | Q* ---+--- - - -> KB DATA __/ | | | | | __/ | CLR PRE | | +---------------------------------------+ +----------+ | | | | | +----------+ PA7 PA6 ... PA0 PB7 . This works, because: CLK goes high in the same time for LS322 and IRQ-FF. QH' appears after ~22ns (typ propagation delay). IRQ-FF need only a 5ns hold time for the input, so it will sample the previous state of QH'! 025C: BF 09 MOV R7,#$09 025E: 00 NOP 025F: EF 5F DJNZ R7,$025F ; delay.. 0261: BF 09 MOV R7,#$09 ; start rotating out 9 bits, lsb first (starting with '1' then 8-bit data) 0263: BE 0A MOV R6,#$0A ; load delay -------------------------+ 0265: 12 6D JB0 $026D ; | 0267: 98 DF ANL BUS,#$DF ; bit='0': pull DATA low | 0269: 88 40 ORL BUS,#$40 ; set CLK high | 026B: 44 73 JMP $0273 | 026D: 88 20 ORL BUS,#$20 ; bit='1': set DATA high | 026F: 88 40 ORL BUS,#$40 ; set CLK high | 0271: 44 73 JMP $0273 | 0273: EE 73 DJNZ R6,$0273 ; delay.. | 0275: 00 NOP ; let LS322 latch it in | 0276: 98 BF ANL BUS,#$BF ; pull CLK low | 0278: 67 RRC A | 0279: EF 63 DJNZ R7,$0263 ; ------------------------------------+ ________ | __ | __ | __ | | __ | ________| C \_______|____/ \_|____/ \_|____/ \_| |____/ \_|____/ | done _______|__ ______|__ ______|__ ______| .... |__ ______|__ | bus idle D________/ | 1 | X______|__X______| |__X______| \__________| | | | | | ^ | | CY 0 1 7 | | START=1 | REQOUT 1. write DATA LS322 STOP state 2. set CLK high latches 3. delay on rising 4. pull CLK low edge 027B: 98 DF ANL BUS,#$DF ; STOP: pull DATA low 027D: 00 NOP 027E: 00 NOP 027F: 00 NOP 0280: 00 NOP 0281: 00 NOP 0282: 88 40 ORL BUS,#$40 ; release CLK high 0284: 54 03 CALL $0203 ; Shift FIFO 0286: 93 RETR **************************************** ** JNT0 REQIN: CLK held low by PC ** **************************************** In the BIOS RESET_KBD routine 0287: BF 08 MOV R7,#$08 0289: EF 89 DJNZ R7,$0289 ; delay 028B: 36 86 JT0 $0286 ; CLK went high in the meantime? -> RETR 028D: 98 BF ANL BUS,#$BF ; we also pull CLK low 028F: 88 20 ORL BUS,#$20 ; and release DATA, high 0291: BF 11 MOV R7,#$11 0293: EF 93 DJNZ R7,$0293 ; delay.. (make sure BIOS releases CLK? we'll drive it from now on) ________ | __ | __ | | __ | ________| C \_______|__/ \_____|__/ \_____| |__/ \_____|____/ | done _______|___________|___________| ...... |___________|__ | bus idle D________/ | | | | | \__________| | ^ ^ | ^ ^ | | ^ ^ | | | | | | | | sample sample sample DATA DATA DATA R6=9 R6=8 R6=1 Before BIOS wants to reset the KB by pulling CLK low for a long time (clearing PB6), it first disables the LS322 (clearing PB7). This also releases (20ms), it first disables the LS322. This puts all its outputs to HIGH-Z. Because of the pull-up the DATA line is therefore high, and this code checks it: reads 9 bits from DATA and accepts the RESET only when all bits read are '1'. It samples all bits twice and signals any difference by clearing F0, but this is not checked afterwards(?). 0295: BE 09 MOV R6,#$09 ; read 9-bits from DATA: PC BIOS is suppose to disable LS322 before this. We should sample only '1's 0297: 88 40 ORL BUS,#$40 ; set CLK high----------+ 0299: BF 08 MOV R7,#$08 | 029B: EF 9B DJNZ R7,$029B ; delay.. | 029D: 98 BF ANL BUS,#$BF ; pull CLK low | 029F: 86 AA JNI $02AA ; DATA low? -> | 02A1: 43 01 ORL A,#$01 ; H: rotate '1' into A | 02A3: 86 AE JNI $02AE ; flipped? err -> | 02A5: 67 RRC A ; OK: rotate | 02A6: EE 97 DJNZ R6,$0297 ; ---- 9 times ---------+ 02A8: 44 B1 JMP $02B1 ; -> exit | 02AA: 53 FE ANL A,#$FE ; rotate '0' into A | 02AC: 86 A5 JNI $02A5 ; still low? ok | 02AE: 85 CLR F0 ; no, flipped? err | <- clear F0 but not checked afterwards..? 02AF: 44 A5 JMP $02A5 ; ----------------------+ 02B1: 88 40 ORL BUS,#$40 ; set CLK high, release 02B3: BF 05 MOV R7,#$05 02B5: EF B5 DJNZ R7,$02B5 ; delay.. 02B7: 98 DF ANL BUS,#$DF ; pull DATA low (bus idle state) 02B9: 37 CPL A 02BA: 96 86 JNZ $0286 ; -> got any bits pulled low? just RETR 02BC: E6 86 JNC $0286 ; -> 9th bit Carry zero? just RETR 02BE: 24 00 JMP $0100 ; -> OK: received only '1' bits: proper KBD_RESET, run BAT and go to Start 02C0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 02D0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 02E0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 02F0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ ******************************************* ** SCANCODE TABLE ** ** ** ** For the 12 x 8 = 96 key matrix ** ******************************************* 0300: 00 4e 51 4d 4a 49 00 46 | 53 50 4f 4c 4b 47 48 45 | .NQMJI.FSPOLKGHE 0310: 52 37 36 29 1c 00 00 0e | 3a 35 28 00 1b 1a 00 0d | R76)....:5(..... 0320: 00 34 27 26 00 19 0c 0b | 32 33 25 24 18 17 0a 09 | .4'&....23%$.... 0330: 39 31 30 23 16 15 08 07 | 2e 2f 22 21 14 13 06 05 | 910#...../"!.... 0340: 2d 2c 20 1f 12 11 04 03 | 2b 2a 1d 1e 10 0f 02 01 | -, .....+*...... 0350: 38 44 42 40 00 3e 00 3c | 00 43 41 3f 00 3d 76 3b | 8DB@.>.<.CA?.=v; | 76? For ROM chksum? Why here, there are plenty of 00-s in the ROM to use.. ******************************************* ** Typematic counter ** ** Delay or Rate in R3 ** ******************************************* Main Loop signals beginning of scan by clearing $1B. This routine checks $1B and if zero, sets it to 01 and decrements the $1A counter to count the (fix) Typematic Delay or Rate. >> SUB called when a key depressed for the first time 0360: D5 SEL RB1 0361: BA 26 MOV R2,#$26 ; $1A = 26 (FIX typematic delay: 38 full scan) >> SUB called when a key is still held between scans 0363: D5 SEL RB1 0364: FB MOV A,R3 0365: 12 76 JB0 $0376 ; $1B_0 set? --> RETR 0367: 43 01 ORL A,#$01 0369: AB MOV R3,A ; not set: set it 036A: CA DEC R2 ; dec $1A 036B: FA MOV A,R2 036C: 96 76 JNZ $0376 ; non-zero? --> RETR 036E: BA 06 MOV R2,#$06 ; $1A is zero: set $1A = 06 (FIX typematic rate: 6 full scan) 0370: C5 SEL RB0 0371: FD MOV A,R5 ; R5 typematic 0372: C6 76 JZ $0376 0374: 54 20 CALL $0220 ; R5 typematic nonzero: --> Put 'A' into FIFO 0376: 93 RETR 0377: 00 | 00 00 00 00 00 00 00 00 | ................ 0380: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 0390: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 03A0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 03B0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 03C0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 03D0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 03E0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ 03F0: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................