We have a display to output information to the user, now we need a keyboard so that the user can type information into our system. I salvaged a keyboard PCB from an unused USB keyboard and trimmed away the part containing the original controller circuit. We only need the keys and the keyboard matrix circuitry. Now we need to decipher the keyboard matrix by tracing the connections or checking them with a continuity tester. In my case this keyboard needs 14 row output and 8 column input lines. The Fn and Windows are connected to an additional input line. We can use these keys for a "warm reboot" so that pressing them both will trigger an NMI interrupt.
The circuit is straightforward. We only need to connect all keyboard lines to IO pins of our VIAs.
Now this looks more like a real computer! Add a battery pack, put this all in an eclosure and you get a typical mobile computer system of the 1980s like the Epson HX-20 .
The keys_update function scans the whole keyboard, performs a simple debounce (read, wait, read again and compare) and stores the code of a pressed key in KEYS_CODE.
keys_update: phaxy jsr scan cmp #$ff bne @debounce sta KEYS_CODE plaxy rts @debounce: sta KEYS_CODE ldx #10 jsr delay_ms jsr scan cmp KEYS_CODE beq @key_pressed lda #$ff sta KEYS_CODE plaxy rts @key_pressed: plaxy rts
The scan function enables one row after the other and reads its column value. For the first row that has a key pressed, the bit position of the column is calculated and #row * 8 is added. The resulting value is the position of the key in the matrix, i.e. the key code. If no key is pressed, $FF is returned.
scan: ldx #13 @next_row: jsr read_row bne @row_pressed dex bpl @next_row lda #$ff rts @row_pressed: ldy #7 @next_column: asl bcs @column_found dey bpl @next_column @column_found: tya @add_row_offset: dex bmi @got_scan_code clc adc #8 bne @add_row_offset @got_scan_code: rts
The read_row function activates a single row (sets it low) and reads and returns the corresponding column value.
read_row: ; Set row X low lda row_out_reg_lo,x sta TMP0 lda row_out_reg_hi,x sta TMP0 + 1 lda row_bit_mask,x ldy #0 and (TMP0),y sta (TMP0),y ; Read column values lda VIA2_IRA eor #$ff tay ; Set all rows high lda #$ff sta VIA2_ORB lda VIA1_ORB ora #$3f sta VIA1_ORB tya rts ; Table of output register addresses for each row row_out_reg_lo: .byte
VIA2_ORB, >VIA2_ORB, >VIA2_ORB, >VIA2_ORB, >VIA2_ORB, >VIA2_ORB, >VIA2_ORB, >VIA2_ORB .byte >VIA1_ORB, >VIA1_ORB, >VIA1_ORB, >VIA1_ORB, >VIA1_ORB, >VIA1_ORB ; Table of output register bit masks for each row (with a 0 bit for the selected row) row_bit_mask: .byte <~VIA_PB0, <~VIA_PB1, <~VIA_PB2, <~VIA_PB3, <~VIA_PB4, <~VIA_PB5, <~VIA_PB6, <~VIA_PB7 .byte <~VIA_PB0, <~VIA_PB1, <~VIA_PB2, <~VIA_PB3, <~VIA_PB4, <~VIA_PB5
I added the second VIA and the keyboard connector.