Files
open-roms/src/kernal/keyboard/scnkey.s
T

441 lines
9.4 KiB
ArmAsm

;; #LAYOUT# STD * #TAKE
;; #LAYOUT# X16 * #IGNORE
;; #LAYOUT# * KERNAL_0 #TAKE
;; #LAYOUT# * * #IGNORE
;
; Official Kernal routine, described in:
;
; - [RG64] C64 Programmers Reference Guide - page 295
; - [CM64] Computes Mapping the Commodore 64 - page 220
;
; CPU registers that has to be preserved (see [RG64]): none
;
; Variables used:
; - KEYTAB - keyboard matrix pointer
; - DELAY - counter for first delay between key repeats
; - KOUNT - counter for next delays between key repeats
; - SHFLAG - SHIFT / CTRL / VENDOR keypress status
; - NDX - number of characters in keyboard buffer
; - XMAX - maximum size of keyboard buffer
; - LSTX - matrix coordinate of last pressed key
; - LSTSHF - previous status of SHFLAG
; - MODE - flag, whether charset toggle (SHIFT+VENDOR) is allowed
; - RPTFLG - whether key repeat is allowed
!ifndef CONFIG_LEGACY_SCNKEY {
; Routine takes some ideas from TWW/CTR proposal, see here:
; - http://codebase64.org/doku.php?id=base:scanning_the_keyboard_the_correct_and_non_kernal_way
SCNKEY:
!ifdef CONFIG_MB_M65 { !ifdef CONFIG_M65_KB_DEV {
jsr M65_MODEGET
bcs scnkey_legacy_mode
; Map KERNAL_C segment
lda VIC_CTRLA
pha
ora #%00100000
sta VIC_CTRLA
; Call the native mode routine
jsr (VKC__m65_scnkey)
; Restore mapping and quit
pla
sta VIC_CTRLA
rts
scnkey_legacy_mode:
} }
; Prepare for SHFLAG update
lda SHFLAG
sta LSTSHF
lda #$00
sta SHFLAG
!ifndef CONFIG_JOY2_CURSOR { !ifdef CONFIG_KEY_FAST_SCAN {
; Check for any activity - speed optimization, but cannot be done
; that early if we are to scan the joystick #2
ldx #$00
stx CIA1_PRA ; connect all the rows
!ifdef CONFIG_KEYBOARD_C128 {
stx VIC_XSCAN
}
dex ; puts $FF
cpx CIA1_PRB
beq scnkey_no_keys
} }
; Retrieve SHIFT / VENDOR / CTRL / ALT / CAPS LOCK status
!ifdef CONFIG_KEYBOARD_C128_CAPS_LOCK {
; First check if this is really a C128 - to avoid false positive
ldx #$00
stx VIC_XSCAN
dex ; puts $FF
cpx VIC_XSCAN
beq @1 ; branch if C64, C128 will keep some bits cleared
lda CPU_R6510
and #$40
bne @1 ; branch if no CAPS LOCK
lda #KEY_FLAG_CAPSL
sta SHFLAG
@1:
!ifndef !CONFIG_KEYBOARD_C128 {
stx VIC_XSCAN ; disconnect C128 keys if no C128 keyboard is supported
} }
ldy #(__kb_matrix_bucky_confmask_end - kb_matrix_bucky_confmask - 1)
scnkey_bucky_loop:
lda kb_matrix_bucky_confmask, y
sta CIA1_PRA
!ifdef CONFIG_KEYBOARD_C128 {
lda kb_matrix_bucky_confmask_c128, y
sta VIC_XSCAN
}
lda kb_matrix_bucky_testmask, y
and CIA1_PRB
bne @3 ; branch if not pressed
lda SHFLAG
ora kb_matrix_bucky_shflag, y
sta SHFLAG
@3:
dey
bpl scnkey_bucky_loop
; Set KEYTAB vector
lda KEYLOG+1
bne @11
jsr scnkey_set_keytab ; KEYLOG routine on zeropage? most likely vector not set
+bra scnkey_keytab_set_done
@11:
!ifdef HAS_OPCODES_65CE02 {
jsr (KEYLOG)
} else {
jsr scnkey_via_keylog
}
scnkey_keytab_set_done:
!ifdef CONFIG_JOY2_CURSOR {
; Check for control port 2 activity
ldx #$00
stx CIA1_DDRA ; set port to input to read joystick
lda CIA1_PRA ; read the joystick 2 status
dex ; puts $FF
stx CIA1_DDRA ; set port back to output
and #%00001111 ; filter out anything but joystick movement
cmp #%00001111
bne scnkey_joystick_filtered
!ifdef CONFIG_KEY_FAST_SCAN {
inx ; puts $00
stx CIA1_PRA ; connect all the rows
!ifdef CONFIG_KEYBOARD_C128 {
stx VIC_XSCAN
}
dex ; puts $FF
cpx CIA1_PRB
beq scnkey_no_keys
} ; CONFIG_KEY_FAST_SCAN
} ; CONFIG_JOY2_CURSOR
; Check for control port 1 activity (can interfere with keyboard)
jsr keyboard_disconnect ; disconnect all the rows, .X will be $FF
cpx CIA1_PRB ; only control port activity will be reported
!ifdef CONFIG_JOY1_CURSOR {
bne scnkey_joystick_1 ; use joystick for cursor keys
} else {
bne scnkey_no_keys ; if activity detected do not scan the keyboard
}
; Check if KEYTAB is not 0 (happens if more than one bucky key is pressed)
; High byte equal to 0 = table considered invalid
lda KEYTAB+1
beq scnkey_no_keys
; Scan the keyboard matrix
ldy #$FF ; offset in key matrix table, $FF for not found yet
!ifdef CONFIG_KEYBOARD_C128 {
jsr scnkey_128
}
ldx #$07
scnkey_matrix_loop:
lda kb_matrix_row_keys, x
sta CIA1_PRA
lda kb_matrix_bucky_filter, x ; filter out bucky keys
ora CIA1_PRB
cmp #$FF
beq scnkey_matrix_loop_next ; skip if no key pressed from this row
cpy #$FF
bne scnkey_no_keys ; jam, more than one key pressed
; We have at least one key pressed in this row, we need to find which one exactly
ldy #$07
scnkey_matrix_loop_inner:
cmp kb_matrix_row_keys, y
bne @4 ; not this particular key
tya ; now .A contains key offset within a row
clc
adc kb_matrix_row_offsets, x ; now .A contains key offset from the matrix start
tay
jmp scnkey_matrix_loop_next
@4:
dey
bpl scnkey_matrix_loop_inner
bmi scnkey_no_keys ; branch always, multiple keys must have been pressed
scnkey_matrix_loop_next:
dex
bpl scnkey_matrix_loop
; Scanning complete - check if we got a key
cpy #$FF
beq scnkey_no_keys
; To be extra sure, check for possible control port 1 interference once again
jsr keyboard_disconnect ; disconnect all the rows, .X will be $FF
cpx CIA1_PRB ; only control port activity will be reported
beq scnkey_got_key ; branch if joystick activity NOT detected
; FALLTROUGH
scnkey_no_keys:
; Mark no key press
lda #$40
sta LSTX
rts
!ifdef CONFIG_JOY1_CURSOR {
scnkey_joystick_1:
; Prepare joystick 1 status
lda CIA1_PRB
and #%00001111 ; filter out anything but movement
; FALLTROUGH
}
!ifdef CONFIG_JOY1_OR_JOY2_CURSOR {
scnkey_joystick_filtered:
; Set appropriate keyboard matrix and key code for joystick event
ldy #$03
@5:
cmp kb_matrix_joy_status, y
beq @6
dey
bpl @5
; Not found
rts
@6:
; Found
lda kb_matrix_joy_keytab_lo, y
sta KEYTAB+0
lda kb_matrix_joy_keytab_hi, y
sta KEYTAB+1
lda kb_matrix_joy_keytab_idx, y
tay
; FALLTROUGH
}
scnkey_got_key: ; .Y should now contain the key offset in matrix pointed by KEYTAB
cpy LSTX
beq scnkey_try_repeat ; branch if the same key as previously
sty LSTX
; Reset key repeat counters - see [CM64] page 58
; Note: according to [CM64] it should be $10 for initializing DELAY, $06 for
; initializing KOUNT for the first time and $04 for subsequent ones - but replicating
; this would make our implementation longer, probably without improving the compatibility.
;
; Besides - since I am the one who writes the code, I will make the values exactly how I like them :D
!ifndef CONFIG_RS232_UP9600 {
lda #$16
} else {
lda #$18
}
sta DELAY
; FALLTROUGH
scnkey_output_key:
; Check if we have free space in the keyboard buffer
lda NDX
cmp XMAX
bcs scnkey_buffer_full ; no space in buffer
; Reinitialize secondary counter
lda #$03
sta KOUNT
; Retrieve the PETSCII code
!ifdef CONFIG_KEYBOARD_C128 {
cpy #$40
bcc scnkey_output_get_keycode
lda kb_matrix_128 - $41, y ; retrieve key code from C128 extended matrix
jmp scnkey_got_petscii
; FALLTROUGH
scnkey_output_get_keycode:
lda (KEYTAB), y ; retrieve key code from standard matrix
} ; CONFIG_KEYBOARD_C128
!ifdef CONFIG_KEYBOARD_C128_CAPS_LOCK {
; Check if we need special handling for a CAPS LOCK key
; This is shorter than a separate set of tables
scnkey_handle_caps_lock:
!ifndef CONFIG_KEYBOARD_C128 {
lda (KEYTAB), y
}
tax
lda SHFLAG
and #(%00000111 + KEY_FLAG_CAPSL)
cmp #KEY_FLAG_CAPSL
bne @7 ; branch if no special CAPS LOCK handling needed
cpx #$41 ; 'A'
bcc @7
cpx #$5B ; 'Z' + 1
bcs @7
txa
clc
adc #$20 ; turn lowercase into uppercase
tax
@7:
txa
} ; CONFIG_KEYBOARD_C128_CAPS_LOCK
!ifndef CONFIG_KEYBOARD_C128 { !ifndef CONFIG_KEYBOARD_C128_CAPS_LOCK {
lda (KEYTAB), y
} }
; Output PETSCII code to the keyboard buffer
scnkey_got_petscii:
beq scnkey_no_keys ; branch if we have no PETSCII code for this key
ldy NDX
sta KEYD, y
inc NDX
; FALLTROUGH
scnkey_done:
rts
scnkey_try_repeat:
!ifndef CONFIG_KEY_REPEAT_ALWAYS {
; Check whether we should repeat keys - first the flag, afterwards hardcoded list
lda RPTFLG
bmi scnkey_handle_repeat ; branch if we should repeat always
tya
ldx #(__kb_matrix_alwaysrepeat_end - kb_matrix_alwaysrepeat - 1)
@9:
cmp kb_matrix_alwaysrepeat, x
beq scnkey_handle_repeat
dex
bpl @9
bmi scnkey_done
scnkey_handle_repeat:
} ; no CONFIG_KEY_REPEAT_ALWAYS
; Countdown before first repeat
lda DELAY
beq @10
dec DELAY
rts
@10:
; Countdown before subsequent repeats
lda KOUNT
beq scnkey_output_key ; if second counter is also 0, we can repeat the key
dec KOUNT
rts
scnkey_buffer_full:
; Keyboard buffer was full - at least make sure the key
; will be repeated as soon as possible
lda #$00
sta DELAY
sta KOUNT
rts
} ; no CONFIG_LEGACY_SCNKEY