314 lines
9.3 KiB
Plaintext
314 lines
9.3 KiB
Plaintext
|
.include "ps2_keyboard.h65"
|
||
|
.include "string.h65"
|
||
|
.include "lcd.h65"
|
||
|
.include "parity.h65"
|
||
|
Export ps2kb,init,begin_receive,send_command,scancode,status,scancode_handler
|
||
|
Export ps2kb, _receive_irq_shift_reg_handler, _receive_irq_timer_handler
|
||
|
|
||
|
.bss
|
||
|
status: .res 1
|
||
|
send_last_bits: .res 1
|
||
|
send_data: .res 1
|
||
|
send_cmd: .res 1
|
||
|
key_read: .res 2
|
||
|
scancode: .res 1
|
||
|
scancode_handler: .res 2
|
||
|
|
||
|
.code
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @macro Enable the clock signal from the keyboard
|
||
|
;; @modifies: A
|
||
|
;; @details
|
||
|
;; Stop pulling the keyboards clock low
|
||
|
;;********************************************************************************
|
||
|
.macro _EnableClock
|
||
|
; set pin to input
|
||
|
lda ps2kb::VIA + ps2kb::CLK_PULL_DDR
|
||
|
and #<~ps2kb::CLK_PULL_MASK
|
||
|
sta ps2kb::VIA + ps2kb::CLK_PULL_DDR
|
||
|
.endmacro
|
||
|
|
||
|
.macro _DisableTimerIRQ
|
||
|
; set pin to input
|
||
|
lda ps2kb::VIA + ps2kb::CLK_PULL_DDR
|
||
|
and #<~ps2kb::CLK_PULL_MASK
|
||
|
sta ps2kb::VIA + ps2kb::CLK_PULL_DDR
|
||
|
.endmacro
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @macro Disable the clock signal from the keyboard
|
||
|
;; @modifies: A
|
||
|
;; @details
|
||
|
;; Pulls the keyboards clock low
|
||
|
;;********************************************************************************
|
||
|
.macro _DisableClock
|
||
|
; set pin to output
|
||
|
lda ps2kb::VIA + ps2kb::CLK_PULL_DDR
|
||
|
ora #ps2kb::CLK_PULL_MASK
|
||
|
sta ps2kb::VIA + ps2kb::CLK_PULL_DDR
|
||
|
; set pin low
|
||
|
lda ps2kb::VIA + ps2kb::CLK_PULL_R
|
||
|
and #<~ps2kb::CLK_PULL_MASK
|
||
|
sta ps2kb::VIA + ps2kb::CLK_PULL_R
|
||
|
.endmacro
|
||
|
|
||
|
|
||
|
.proc init
|
||
|
stz key_read
|
||
|
stz key_read+1
|
||
|
stz scancode
|
||
|
stz status
|
||
|
; T2 oneshot mode, clear shift reg
|
||
|
lda ps2kb::VIA + IO::ACR
|
||
|
and #<~IO::ACR_MASK::SR
|
||
|
ora #IO::ACR::T2_IRQ_LOAD
|
||
|
sta ps2kb::VIA + IO::ACR
|
||
|
; the 3 last bits take about 230us, at @1MHz => wait 230 cycles and then the shift register
|
||
|
; (this could be shorter since the it takes a few cycles after the interrupt)
|
||
|
lda #<ps2kb::TIMER
|
||
|
sta ps2kb::VIA + IO::T2CL
|
||
|
; load this rts as scancode_handler
|
||
|
StoreDByte @rts, scancode_handler
|
||
|
@rts:
|
||
|
rts
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @function Start receiving data from the keyboard
|
||
|
;; @details
|
||
|
;; - use the shift register interrupts to read the first 8 bits
|
||
|
;; - configure timer for timing the read of the last 3 bits
|
||
|
;; @modifies: A
|
||
|
;;********************************************************************************
|
||
|
.proc begin_receive
|
||
|
; disable timer interrupts (this might be called while waiting on the last 3 bits)
|
||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
|
||
|
; set shift register to shift in under external clock on CB1
|
||
|
lda ps2kb::VIA + IO::ACR
|
||
|
and #<~IO::ACR_MASK::SR
|
||
|
ora #IO::ACR::SR_SIN_PHIE
|
||
|
sta ps2kb::VIA + IO::ACR
|
||
|
stz key_read
|
||
|
stz key_read+1
|
||
|
stz scancode
|
||
|
bit status
|
||
|
; set to RECEIVE_KEYS only if RECEIVE_ANSWER is not set
|
||
|
bvs @receive_answer
|
||
|
lda #ps2kb::STATUS::RECEIVE_KEYS
|
||
|
sta status
|
||
|
@receive_answer:
|
||
|
|
||
|
_EnableClock
|
||
|
|
||
|
; todo setup irq handlers
|
||
|
|
||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
|
||
|
; reset and start SR
|
||
|
stz ps2kb::VIA + IO::SR
|
||
|
rts
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @function Read the first 8 bits an
|
||
|
;; @modifies: A
|
||
|
;; @details
|
||
|
;; - read shift register
|
||
|
;; - disable shift register interrupts
|
||
|
;; - reset shift register
|
||
|
;; - enable timer 2 interrupts
|
||
|
;; - start timer 2
|
||
|
;; IO::SR has to be read before the next bit is shifted in, which happens ~75us after the irq
|
||
|
;; at 1MHz, handling this interrupt takes about 50us (without any additional debug code),
|
||
|
;; so it should work
|
||
|
;;********************************************************************************
|
||
|
.proc _receive_irq_shift_reg_handler
|
||
|
lda ps2kb::VIA + IO::SR
|
||
|
sta key_read
|
||
|
stz ps2kb::VIA + IO::SR
|
||
|
|
||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::SR
|
||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::T2
|
||
|
; start timer, low order count already in latch after init
|
||
|
lda #>ps2kb::TIMER
|
||
|
sta ps2kb::VIA + IO::T2CH
|
||
|
rts
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @function Read the last 3 bits after after timer 2 is up
|
||
|
;; @modifies: A
|
||
|
;; @details
|
||
|
;; - read shift register
|
||
|
;; - disable timer 2 interrupts
|
||
|
;; - enable shift register interrupts
|
||
|
;; - reset shift register
|
||
|
;; - jump to the subroutine pointed to by scancode_handler
|
||
|
;;********************************************************************************
|
||
|
.proc _receive_irq_timer_handler
|
||
|
lda ps2kb::VIA + IO::SR
|
||
|
sta key_read + 1
|
||
|
|
||
|
lda ps2kb::VIA + IO::T2CL ; clear interrupt flag
|
||
|
|
||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
|
||
|
; 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 ps2kb::VIA + IO::ACR
|
||
|
tsb ps2kb::VIA + IO::ACR
|
||
|
stz ps2kb::VIA + IO::SR
|
||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
|
||
|
|
||
|
lda key_read+1
|
||
|
ror ; stop bit -> C
|
||
|
sta key_read+1 ; parity in bit 0, D7 in bit 1
|
||
|
ror ; parity -> C
|
||
|
ror ; D7 -> C
|
||
|
lda key_read ; not affecting carry
|
||
|
rol ; C -> bit 0, startbit -> C
|
||
|
Reverse A
|
||
|
sta scancode
|
||
|
CalculateOddParity
|
||
|
eor key_read+1 ; if bit 0 is 1 - parity error
|
||
|
and #1 ; bit 1 is still D7
|
||
|
bne @parity_error
|
||
|
; check what to do with the scancode
|
||
|
bit status
|
||
|
bcc @status_not_receive_keys
|
||
|
jmp (scancode_handler)
|
||
|
@status_not_receive_keys:
|
||
|
bvc @status_ignore
|
||
|
jmp _process_cmd_answer
|
||
|
@status_ignore:
|
||
|
rts
|
||
|
@parity_error: ; TODO handle somehow
|
||
|
lda #$ff
|
||
|
sta scancode
|
||
|
DEBUG_LED_ON 2
|
||
|
rts
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @function Send a command to the keyboard
|
||
|
;; @modifies: A,X,Y
|
||
|
;; @param A: The command byte
|
||
|
;; @param X: The data byte or 0 if only the command byte should be sent
|
||
|
;;********************************************************************************
|
||
|
.proc send_command
|
||
|
sta send_cmd ; store if it needs to be resent
|
||
|
cpx #0
|
||
|
beq @jmp_send_byte
|
||
|
@store_data:
|
||
|
stx send_data
|
||
|
@jmp_send_byte:
|
||
|
jmp _send_byte
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @function Send a byte to the keyboard
|
||
|
;; @modifies: A,X,Y
|
||
|
;; @param A: The byte to send
|
||
|
;; @details
|
||
|
;; - pull clock low to stop keyboard transmissions
|
||
|
;; - setup shift register to shift out
|
||
|
;; - put startbit + D0-6 in shift register
|
||
|
;; - store D7 + parity + stopbit in memory
|
||
|
;; - TODO setup SR and T2 interrupt handlers
|
||
|
;;********************************************************************************
|
||
|
.proc _send_byte
|
||
|
pha
|
||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
|
||
|
_DisableClock
|
||
|
; (re)set shift register to shift out under external clock on CB1
|
||
|
lda ps2kb::VIA + IO::ACR
|
||
|
and #<~IO::ACR_MASK::SR
|
||
|
ora #IO::ACR::SR_SOUT_PHIE
|
||
|
sta ps2kb::VIA + IO::ACR
|
||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
|
||
|
pla
|
||
|
pha
|
||
|
; split into the 11 bits
|
||
|
CalculateOddParity
|
||
|
tax
|
||
|
pla
|
||
|
clc ; C = 0 (startbi)
|
||
|
rol ; C -> bit 0, D7 -> C
|
||
|
sta ps2kb::VIA + IO::SR
|
||
|
txa
|
||
|
ror ; C -> bit 0 (D7)
|
||
|
ora #%00000100 ; set stopbit
|
||
|
; A = 000001P7 where 7 = D7, P = Parity
|
||
|
sta send_last_bits ; loaded into SR by irq handler
|
||
|
; stop pulling clk low, which causes the startbit to be shifted out
|
||
|
_EnableClock
|
||
|
rts
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @function Send the lasts 3 bits
|
||
|
;; @modifies: A
|
||
|
;; @details
|
||
|
;; - load the remaining 3 bits of the transmission into the shift register
|
||
|
;; - disable shift register interrupts
|
||
|
;; - enable timer 2 interrupts
|
||
|
;; - start timer 2
|
||
|
;;********************************************************************************
|
||
|
.proc _send_irq_shift_reg_handler
|
||
|
lda send_last_bits
|
||
|
sta ps2kb::VIA + IO::SR
|
||
|
|
||
|
; disable SR interrupts
|
||
|
lda #IO::IRQ::SR
|
||
|
sta ps2kb::VIA + IO::IER
|
||
|
; enable timer interrupts
|
||
|
lda #(IO::IRQ::IRQ | IO::IRQ::T2)
|
||
|
sta ps2kb::VIA + IO::IER
|
||
|
; start timer, low order count already in latch after init
|
||
|
lda #>ps2kb::TIMER
|
||
|
sta ps2kb::VIA + IO::T2CH
|
||
|
rts
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
;;********************************************************************************
|
||
|
;; @function Send databyte or reveice the keyboards answer
|
||
|
;; @modifies: A, X, Y
|
||
|
;; @details
|
||
|
;; - disable timer 2 interrupts
|
||
|
;; - if send_data is zero:
|
||
|
;; - begin_receive
|
||
|
;; - set status RECEIVE_ANSWER
|
||
|
;; - else
|
||
|
;; - send_byte send_data
|
||
|
;; - set send_data = 0
|
||
|
;;********************************************************************************
|
||
|
.proc _send_irq_timer_handler
|
||
|
lda ps2kb::VIA + IO::T2CL ; clear interrupt flag
|
||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
|
||
|
; no SR reset necessary, will be done in begin_receive or send_byte
|
||
|
|
||
|
lda send_data
|
||
|
beq @receive
|
||
|
stz send_data
|
||
|
jmp _send_byte
|
||
|
rts
|
||
|
@receive:
|
||
|
lda #ps2kb::STATUS::RECEIVE_ANSWER
|
||
|
sta status
|
||
|
jmp begin_receive
|
||
|
.endproc
|
||
|
|
||
|
|
||
|
.proc _process_cmd_answer
|
||
|
@success:
|
||
|
rts
|
||
|
.endproc
|