From b60ff7be12c0e5630311dce15aa1feb14f4f5ff1 Mon Sep 17 00:00:00 2001 From: "matthias@arch" Date: Sun, 31 Dec 2023 01:59:06 +0100 Subject: [PATCH] wip keyboard --- main.s65 | 81 +++++------- spicode.s65 | 304 +++++++++++++++++++++++++++----------------- system/keyboard.h65 | 134 ++++++++++++++++++- system/keyboard.s65 | 54 ++++++-- system/system.h65 | 9 ++ test.s65 | 23 +++- 6 files changed, 427 insertions(+), 178 deletions(-) diff --git a/main.s65 b/main.s65 index 3ef7a71..97b6753 100644 --- a/main.s65 +++ b/main.s65 @@ -1,34 +1,11 @@ .include "system/system.h65" +.include "string.h65" .export home,homeloop .import printer:absolute .import spi_menu:absolute .code -.macro DEBUG_LED_OFF nr - pha - lda IO1 + IO::RA -.if nr = 0 - and #%11111110 -.else - and #%11111101 -.endif - sta IO1 + IO::RA - pla -.endmacro - -.macro DEBUG_LED_ON nr - pha - lda IO1 + IO::RA -.if nr = 0 - ora #%00000001 -.else - ora #%00000010 -.endif - sta IO1 + IO::RA - pla -.endmacro - ;******************************************************************************** ; Modules ;******************************************************************************** @@ -62,12 +39,14 @@ nmi: rti irq: ; read IRFs, while bit 7 ist set handle interrupts + ; DEBUG_LED_ON 2 @irq_io1: lda IO1 + IO::IFR sta irq_via_ifr - bbr7 irq_via_ifr,@irq_io1 ; skip + bbr7 irq_via_ifr,@irq_io2 ; skip bbs2 irq_via_ifr,@irq_kb1 ; shit reg -> first 8 bits bbs5 irq_via_ifr,@irq_kb2 ; timer -> last 3 bits + bra @irq_unknown @irq_io2: lda IO2 + IO::IFR sta irq_via_ifr @@ -75,6 +54,7 @@ irq: bbs2 irq_via_ifr,@irq_spi_p ; check SR bbs1 irq_via_ifr,@irq_keypad ; check CA1 +@irq_unknown: ; this SHOULD never be reached jsr lcd::clear Print "Unknown IRQ" @@ -87,13 +67,16 @@ irq: jsr kp::read_irq bra @irq_return @irq_spi_p: - JsrIndirect (spi_p::irq_handler), @irq_return + jsr spi_p::irq_read_byte + bra @irq_return + ; jmp (spi_p::irq_handler) + ; JsrIndirect (spi_p::irq_handler), @irq_return @irq_kb1: - Print "$3000" + ; Print "$3000" jsr $3000 bra @irq_return @irq_kb2: - Print "$3100" + ; Print "$3100" jsr $3100 bra @irq_return ; @irq_dht: @@ -114,25 +97,20 @@ reset: sta IO1 + IO::DDRA DEBUG_LED_OFF 0 DEBUG_LED_OFF 1 - + DEBUG_LED_OFF 1 jsr kp::init - SetCustomChar chars::CAT,0 - SetCustomChar chars::SMILEY,1 - SetCustomChar chars::SMILEY_XD,2 + DEBUG_LED_ON 0 + jsr spi_p::init + DEBUG_LED_ON 1 - ; ; INIT DHT - ; lda #%11000010 ; enable interrupt for Timer 1 and CA1 on IO2 - ; sta IER2 - ; lda #%00111111 ; set Timer 1 to interrupt when loaded - ; and ACR2 - ; sta ACR2 - ; lda #%00000001 ; set PCR2 bit 0 CA1 pos edge interrupt - ; ora PCR2 - ; sta PCR2 - ; stz DHT_STATUS + ; SetCustomChar chars::CAT,0 + ; SetCustomChar chars::SMILEY,1 + ; SetCustomChar chars::SMILEY_XD,2 + + ; DEBUG_LED_ON 2 ; enable interrupts cli @@ -170,9 +148,14 @@ reset: cmp #'5' beq @debug1_off cmp #'7' + beq @debug2_on + cmp #'8' + beq @debug2_off + cmp #'9' beq @print_rb cmp #'*' ; print home menu again if not visible (message 1 and 2 jmp to home) beq home + wai jmp @loop @debug0_off: @@ -187,6 +170,12 @@ reset: @debug1_on: DEBUG_LED_ON 1 jmp @loop +@debug2_off: + DEBUG_LED_OFF 2 + jmp @loop +@debug2_on: + DEBUG_LED_ON 2 + jmp @loop @print_rb: jsr lcd::clear Print str_io2 @@ -215,10 +204,10 @@ message_1: .byte " **** " .asciiz "www.quintern.xyz" message_2: - .byte " Hello " - .byte " there " - .byte " <3 " - .asciiz "================" + .byte "0123456789=!?#+*" + .byte "ABCDEFGHIJKLMNOP" + .byte "QRSTUVWXYZÖÄÜß-_" + .asciiz "{[(<>)]}$%&/,;.:" message_menu: .byte " Printer " ; .byte " Temperatur " diff --git a/spicode.s65 b/spicode.s65 index f8fb77d..0be8e7d 100644 --- a/spicode.s65 +++ b/spicode.s65 @@ -20,16 +20,10 @@ CODE_START: jsr lcd::clear lda #'$' jsr lcd::print_char - - ; stz kp::_DEBUG_VAL -; @loop: - ; lda kp::_DEBUG_VAL - ; beq @loop - ; stz kp::_DEBUG_VAL - ; cmp #'*' - ; jeq homeloop - ; jsr lcd::print_char - ; bra @loop + ; jmp homeloop + DEBUG_LED_OFF 0 + DEBUG_LED_OFF 1 + DEBUG_LED_OFF 2 lda #$3000 sta ARG3 - ldy #20 + ldy #10 jsr memcopy lda #$3100 sta ARG3 - ldy #20 + ldy #10 jsr memcopy - lda #'?' - jsr lcd::print_char ; PrintNC $3000 jsr kb::init + stz kb::status + lda #'%' jsr lcd::print_char @@ -67,8 +61,41 @@ CODE_START: ldy #0 @loop: + wai + lda kb::scancode + beq @noscancode + jsr process_scancode + stz kb::scancode + lda char + beq @onlykeycode + Strf fmt_codechar,out_str,keycode,kb::status,char + PrintNC out_str + stz char + stz keycode + bra @noscancode +@onlykeycode: + lda keycode + beq @noscancode + Strf fmt_code,out_str,keycode,keycode_flags + PrintNC out_str + stz keycode +@noscancode: + bbr5 kb::status, @shift_off +@shift_on: + DEBUG_LED_ON 0 + bra @check_caps +@shift_off: + DEBUG_LED_OFF 0 +@check_caps: + bbr4 kb::status, @caps_off +@caps_on: + DEBUG_LED_ON 2 + bra @read_keypad +@caps_off: + DEBUG_LED_OFF 2 +@read_keypad: lda kp::_DEBUG_VAL - beq @loop + jeq @loop stz kp::_DEBUG_VAL cmp #'*' jeq homeloop @@ -85,125 +112,45 @@ CODE_START: cmp #'C' beq @lC jsr lcd::print_char - bra @loop + jmp @loop @l1: ; jsr irq_on_shift_reg jsr $3000 lda #'*' jsr lcd::print_char - bra @loop + jmp @loop @l2: ; jsr irq_on_timer jsr $3100 lda #'#' jsr lcd::print_char - bra @loop + jmp @loop @l3: lda $3000,y jsr lcd::print_char iny - bra @loop + jmp @loop @lA: lda kb::KB_IO + IO::SR jsr lcd::print_char - bra @loop + jmp @loop @lB: - Strf fmt_str, out_str, kb::keycode + ; Printf "kc%x;", out_str, kb::scancode + jsr lcd::clear + ldx out_idx + lda #0 + sta out_str,x Print out_str - bra @loop + stz out_idx + + jmp @loop @lC: jsr lcd::clear - bra @loop + jmp @loop -; .proc kbinit -; lda #'[' -; jsr lcd::print_char -; ; - use the shift register interrupts to read the first 8 bits -; ; set shift register to shift in under external clock on CB1 -; ; - configure timer for timing the read of the last 3 bits -; ; timer 2 one shot mode is sufficient, leaves T1 available -; lda #(IO::ACR::SR_SIN_PHIE | IO::ACR::T2_IRQ_LOAD) -; tsb kb::KB_IO + IO::ACR -; ; the 3 last bits take about 230us, at @1MHz => wait 230 cycles and then the shift register -; lda #230 -; sta kb::KB_IO + IO::T2CL -; stz key_read -; stz key_read+1 -; ; enable SR interrupts -; lda #(IO::IRQ::IRQ | IO::IRQ::SR) -; sta kb::KB_IO + IO::IER -; ; load SR to reset -; lda kb::KB_IO + IO::SR -; lda #']' -; jsr lcd::print_char -; rts -; .endproc - - -; ;; @details -; ;; IO::SR has to be read before the next bit is shifted in, which happens ~75us after the irq -; ;; at 1MHz, handlings this interrupt takes about 50us (without any additional debug code), so it should work -; .proc irq_on_shift_reg -; ; lda #'{' -; ; jsr lcd::print_char -; lda kb::KB_IO + IO::SR -; sta key_read -; stz kb::KB_IO + IO::SR - -; ; disable SR interrupts -; lda #IO::IRQ::SR -; sta kb::KB_IO + IO::IER -; ; enable timer interrupts -; lda #(IO::IRQ::IRQ | IO::IRQ::T2) -; sta kb::KB_IO + IO::IER -; ; start timer -; lda #1 -; sta kb::KB_IO + IO::T2CH -; ; lda #'}' -; ; jsr lcd::print_char -; rts -; .endproc - - -.proc irq_timer_handler - ; lda #'<' - ; jsr lcd::print_char - lda kb::KB_IO + IO::SR - sta kb::key_read + 1 - - lda kb::KB_IO + IO::T2CL ; clear interrupt flag - - ; disable timer interrupts - lda #(IO::IRQ::T2) - sta kb::KB_IO + IO::IER - ; enable shift register interrupts - lda #(IO::IRQ::IRQ | IO::IRQ::SR) - sta kb::KB_IO + IO::IER - ; reset SR - stz kb::KB_IO + IO::SR - - ; lda #'|' - ; rotate bit 2 (last bit of keycode) into the carry - ror - ror - ror - lda kb::key_read ; not affecting carry - rol ; rotate carry into byte, rotate startbit into carry - ; TODO byte is inverted, maybe consider wasting 256 bytes for a bit reverse lookup table? - sta kb::keycode - - Strf fmt_str2, out_str, kb::keycode, kb::key_read, kb::key_read+1 - PrintNC out_str - - stz kb::key_read - stz kb::key_read+1 - - rts -.endproc - ; key_read: .res 2 -; keycode: .res 1 +; scancode: .res 1 kb_irq1: ; lda #'!' ; jsr lcd::print_char @@ -216,13 +163,142 @@ kb_irq2: ; lda #'?' ; jsr lcd::print_char ; jsr kb::on_timer_irq - jsr irq_timer_handler + jsr kb::irq_timer_handler ; lda #';' ; jsr lcd::print_char rts .byte '@' -out_str: .res 40 -fmt_str: .asciiz "kc%x;" -fmt_str2: .asciiz "kc%x-%x-%x; " +.proc process_scancode + ; DEBUG_LED_OFF 1 + ; check how this scancode needs to be interpreted + ldx kb::scancode + bit kb::status + jmi release_key ; bit 7 set + bvs @twobytecode ; bit 6 set + ; DEBUG_LED_ON 1 + ; check mods + cpx #kb::K_RELEASE + beq @code_release + cpx #kb::K::LEFTSHIFT + beq @code_shift + cpx #kb::K::RIGHTSHIFT + beq @code_shift + cpx #kb::K_TWOBYTE + beq @code_twobytecode + cpx #kb::K::CAPSLOCK + beq @code_capslock + + ; a real keycode and not a released keycode, twobyte or release scancode + stz keycode_flags + stx keycode + cmp #$62 ; $61 is the largest ascii scancode + bcs @special + bbs5 kb::status,@modshift ; if shift active +@nomod: + lda kb::CHARS_NOMOD,x + beq @special +@char: ; order which is fastet most of the time (lowercase characters) + sta char + rts +@modshift: + lda kb::CHARS_MODSHIFT,x + bne @char +@special: + rts + +@code_release: ; set release + smb7 kb::status + rts +@code_twobytecode: ; set twobytecode + smb6 kb::status + rts +@code_shift: ; process shift + bbs4 kb::status, @unset_shift ; unset shift when capslock is on +@set_shift: + smb5 kb::status + rts +@unset_shift: + rmb5 kb::status + rts +@code_capslock: ; toggle capslock & shift + bbs1 kb::status, @rts ; ignore if held + lda kb::status + ora #(kb::STATUS::CAPSLOCK_HELD) + ; toggle and capslock shift + eor #(kb::STATUS::CAPSLOCK | kb::STATUS::SHIFT) + sta kb::status + rts +@code_rightalt: ; set rightalt + smb3 kb::status + rts +@code_numlock: ; set numlock + smb2 kb::status + rts + +@twobytecode: + rmb6 kb::status ; unset twobytecode + ; check mods + cpx #kb::K2::RIGHTALT + beq @code_rightalt + + lda #kb::STATUS::TWOBYTE + sta keycode_flags + stx keycode +@rts: + rts +.endproc + +;; @param S: The status register must be set through `bit kb::status` +;; @param X: The scancode +.proc release_key + bvs @twobytecode + rmb7 kb::status + + ; check mods + cpx #kb::K_TWOBYTE + beq @code_twobytecode + cpx #kb::K::LEFTSHIFT + beq @code_shift + cpx #kb::K::RIGHTSHIFT + beq @code_shift + cpx #kb::K::CAPSLOCK + beq @code_capslock + ; no need to process capslock - it gets toggled when pressed + rts +@code_twobytecode: ; unset special + rmb6 kb::status + rts +@code_shift: ; process shift + bbs4 kb::status, @code_shift_caps ; set shift when capslock is on + rmb5 kb::status ; unset if capslock is off + rts +@code_shift_caps: + smb5 kb::status + rts +@code_capslock: ; unset CAPSLOCK_HELD + rmb1 kb::status + rts +@code_rightalt: ; unset rightalt + rmb3 kb::status + rts +@code_numlock: ; unset numlock + rmb2 kb::status + rts + +@twobytecode: + rmb6 kb::status ; unset twobytecode + ; check mods + cpx #kb::K2::RIGHTALT + beq @code_rightalt + rts +.endproc + +out_idx: .res 1 +out_str: .res 40 +char: .res 1 +keycode: .res 1 +keycode_flags: .res 1 +fmt_codechar: .asciiz "%x %x %c " +fmt_code: .asciiz "%x %x " diff --git a/system/keyboard.h65 b/system/keyboard.h65 index 8e3fa97..f8d7de0 100644 --- a/system/keyboard.h65 +++ b/system/keyboard.h65 @@ -3,7 +3,7 @@ ;; @type drive ;; @details: ;; Support for a PS2 Keyboard using the shift register of a 6522 VIA -;; Pressing a key causes 11 bits to be sent: 1 start - 8 keycode - 1 parity - 1 stop +;; Pressing a key causes 11 bits to be sent: 1 start - 8 scancode - 1 parity - 1 stop ;; The VIA is set up to interrupt after 8 bits have been shifted into the shift register ;; from the external clock pulses of the keyboard (additional hardware required to ;; address the hardware bug of the VIA, where bit get lost when the external clock @@ -16,8 +16,136 @@ INCLUDE_KEYBOARD = 1 .include "system.h65" .scope kb -Import kb,init,irq_shift_reg_handler,irq_timer_handler,keycode,key_read +Import kb,init,irq_shift_reg_handler,irq_timer_handler,scancode,key_read +Import kb, CHARS_NOMOD, CHARS_MODSHIFT +ImportZp kb,status KB_IO = IO1 +.enum STATUS + RELEASE = %10000000 + TWOBYTE = %01000000 + SHIFT = %00100000 + CAPSLOCK = %00010000 + CAPSLOCK_HELD = %00000010 + RIGHTALT = %00001000 + NUMLOCK = %00000100 +.endenum + +K_TWOBYTE = $E0 +K_RELEASE = $F0 + +.enum K + F9 = $01 + F5 = $03 + F3 = $04 + F1 = $05 + F2 = $06 + F12 = $07 + F10 = $09 + F8 = $0A + F6 = $0B + F4 = $0C + TAB = $0D + GRAVE = $0E + LEFTALT = $11 + LEFTSHIFT = $12 + LEFTCTRL = $14 + K_Q = $15 + K_1 = $16 + K_Y = $1A + K_S = $1B + K_A = $1C + K_W = $1D + K_2 = $1E + K_C = $21 + K_X = $22 + K_D = $23 + K_E = $24 + K_4 = $25 + K_3 = $26 + SPACE = $29 + K_V = $2A + K_F = $2B + K_T = $2C + K_R = $2D + K_5 = $2E + K_N = $31 + K_B = $32 + K_H = $33 + K_G = $34 + K_Z = $35 + K_6 = $36 + K_M = $3A + K_J = $3B + K_U = $3C + K_7 = $3D + K_8 = $3E + COMMA = $41 + K_K = $42 + K_I = $43 + K_O = $44 + K_0 = $45 + K_9 = $46 + DOT = $49 + DASH = $4A + K_L = $4B + OUML = $4C + K_P = $4D + SSHARP = $4E + AUML = $52 + UUML = $54 + BACKTICK = $55 + CAPSLOCK = $58 + RIGHTSHIFT = $59 + ENTER = $5A + PLUS = $5B + HASH = $5D + LESSTHAN = $61 + BACKSPACE = $66 + KP1 = $69 + KP4 = $6B + KP7 = $6C + KP0 = $70 + KPDOT = $71 + KP2 = $72 + KP5 = $73 + KP6 = $74 + KP8 = $75 + ESCAPE = $76 + NUMLOCK = $77 + F11 = $78 + KPPLUS = $79 + KP3 = $7A + KPMINUS = $7B + KPASTERISK = $7C + KP9 = $7D + SCROLLLOCK = $7E + F7 = $83 +.endenum + +.enum K2 + RIGHTALT = $11 + RIGHTCTRL = $14 + LEFTMETA = $1F + RIGHTMETA = $27 + MENU = $2F + BREAK = $37 + SYSRQ = $3F + KPDIVIDE = $4A + KPENTER = $5A + END = $69 + LEFT = $6B + POS1 = $6C + INSERT = $70 + DELETE = $71 + DOWN = $72 + RIGHT = $74 + UP = $75 + PAGEDOWN = $7A + PAGEUP = $7D +.endenum + + .endscope -.endif + +.endif ; guard diff --git a/system/keyboard.s65 b/system/keyboard.s65 index 17c369f..86d6e38 100644 --- a/system/keyboard.s65 +++ b/system/keyboard.s65 @@ -1,11 +1,15 @@ .include "keyboard.h65" .include "string.h65" .include "lcd.h65" -Export kb,init,irq_shift_reg_handler,irq_timer_handler,keycode,key_read +Export kb,init,irq_shift_reg_handler,irq_timer_handler,scancode,key_read +Export kb, CHARS_NOMOD, CHARS_MODSHIFT +ExportZp kb,status +.zeropage +status: .bss key_read: .res 2 -keycode: .res 1 +scancode: .res 1 .code ;;******************************************************************************** @@ -25,6 +29,8 @@ keycode: .res 1 sta kb::KB_IO + IO::T2CL stz key_read stz key_read+1 + stz scancode + stz status ; enable SR interrupts lda #(IO::IRQ::IRQ | IO::IRQ::SR) @@ -60,7 +66,7 @@ keycode: .res 1 lda #(IO::IRQ::IRQ | IO::IRQ::T2) sta kb::KB_IO + IO::IER ; start timer, low order count already in latch after init - lda #1 + lda #0 sta kb::KB_IO + IO::T2CH rts .endproc @@ -84,23 +90,49 @@ keycode: .res 1 ; disable timer interrupts lda #(IO::IRQ::T2) sta kb::KB_IO + IO::IER + + ; reset SR + ; disabling shifting in acr seems necessary to reset - otherwise + ; it continues counting and interrupts after the first 5 bits of the next keypress + lda #(IO::ACR::SR_SIN_PHIE | IO::ACR::T2_IRQ_LOAD) + trb kb::KB_IO + IO::ACR + tsb kb::KB_IO + IO::ACR + stz kb::KB_IO + IO::SR ; enable shift register interrupts lda #(IO::IRQ::IRQ | IO::IRQ::SR) sta kb::KB_IO + IO::IER - ; reset SR - stz kb::KB_IO + IO::SR - ; rotate bit 2 (last bit of keycode) into the carry lda key_read+1 ror ror ror lda key_read ; not affecting carry - rol ; rotate carry into byte, rotate startbit into carry - ; TODO byte is inverted, maybe consider wasting 256 bytes for a bit reverse lookup table? - sta keycode + rol ; rotate carry into byte, rotate startbit into carry + Reverse A + sta scancode - stz key_read - stz key_read+1 rts .endproc + +.rodata +.align 256 +CHARS_NOMOD: + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00" + .byte "\x00\x00\x00\x00\x00q1\x00\x00\x00ysaw2\x00" + .byte "\x00cxde43\x00\x00 vftr5\x00" + .byte "\x00nbhgz6\x00\x00\x00mju78\x00" + .byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00" + .byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00#\x00\x00" + .byte "\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + +.align 256 +CHARS_MODSHIFT: + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00" + .byte "\x00\x00\x00\x00\x00Q!\x00\x00\x00YSAW\"\x00" + .byte "\x00CXDE$\xED\x00\x00 VFTR%\x00" + .byte "\x00NBHGZ&\x00\x00\x00MJU/(\x00" + .byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00" + .byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00'\x00\x00" + .byte "\x00>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" diff --git a/system/system.h65 b/system/system.h65 index 1b0533c..098989d 100644 --- a/system/system.h65 +++ b/system/system.h65 @@ -67,4 +67,13 @@ IO1 = $6000 ; IO2: .res 16 IO2 = $7000 +; charmappings for the lcd +; .charmap 'ü', $F5 +; .charmap 'Ü', $F5 +; .charmap 'ö', $EF +; .charmap 'Ö', $EF +; .charmap 'ä', $E1 +; .charmap 'Ä', $E1 +; .charmap 'ß', $E2 + .endif ; include guard diff --git a/test.s65 b/test.s65 index 481902d..ca84486 100644 --- a/test.s65 +++ b/test.s65 @@ -1,8 +1,23 @@ .include "system/system.h65" +.include "system/lcd.h65" .segment "TEST" +.macro MaskedWrite2 addr,value,mask + lda #value + eor addr + and #mask + eor addr + sta addr +.endmacro +.import __SPI_SIZE__ label: - JsrIndirect $2022 - .byte 0,1,2,3,4 - JsrIndirect {($2022,x)}, label - .byte 5,6,7,8,9 + ; JsrIndirect $2022 + ; .byte 0,1,2,3,4 + ; JsrIndirect {($2022,x)}, label + ; .byte 5,6,7,8,9 + ; MaskedWrite2 lcd::LCD_IO+IO::DDRA, (lcd::RS | lcd::RW | lcd::E), lcd::RA_MASK + ; lda #(lcd::RS | lcd::RW | lcd::E) ; RA 5-7 output + ; sta lcd::LCD_IO+IO::DDRA + .word __SPI_SIZE__ + .byte >__SPI_SIZE__ + .byte <__SPI_SIZE__