// // Official Kernal routine, described in: // // - [RG64] C64 Programmer's Reference Guide - page 295 // - [CM64] Compute's 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 #if !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: #if CONFIG_KEYBOARD_C65 && CONFIG_MB_MEGA_65 // Idea by Paul Gardner-Stephen lda VIC_XPOS // check raster X position cmp VIC_XPOS // if it changes, we are on a MEGA65 with VIC-IV registers already visible bne scnkey_viciv_mode // We are on machine that is not in VIC-IV IO mode, so try enabling it (harmless on C64/C65) // If we are on a C65 (or MEGA65 with C65 VIC-III IO mode selected), the above test would // have failed, but we could already be in C65 IO mode. In that case, this will disable C65 IO // mode when done. This is not ideal, but as MEGA65s start C65 mode with VIC-IV IO mode // selected anyway, it should not be a big problem in practice. lda #$A5 sta VIC_KEY lda #$96 sta VIC_KEY jsr scnkey_viciv_mode sta VIC_KEY rts scnkey_viciv_mode: #endif // Prepare for SHFLAG update lda SHFLAG sta LSTSHF lda #$00 sta SHFLAG #if !CONFIG_JOY2_CURSOR && 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 #if CONFIG_KEYBOARD_C128 stx VIC_XSCAN #endif dex // puts $FF cpx CIA1_PRB beq scnkey_no_keys #endif // Retrieve SHIFT / VENDOR / CTRL / ALT / CAPS LOCK status #if 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 !+ // branch if C64, C128 will keep some bits cleared lda CPU_R6510 and #$40 bne !+ // branch if no CAPS LOCK lda #KEY_CAPS_LOCK sta SHFLAG !: #if !CONFIG_KEYBOARD_C128 stx VIC_XSCAN // disconnect C128 keys if no C128 keyboard is supported #endif #endif #if CONFIG_KEYBOARD_C65_CAPS_LOCK lda C65_EXTKEYS_PR and #$01 bne !+ // branch if no CAPS LOCK lda #KEY_CAPS_LOCK sta SHFLAG !: #if !CONFIG_KEYBOARD_C65 ldx #$FF stx C65_EXTKEYS_PR // disconnect C128 keys if no C128 keyboard is supported #endif #endif ldy #(__kb_matrix_bucky_confmask_end - kb_matrix_bucky_confmask - 1) scnkey_bucky_loop: lda kb_matrix_bucky_confmask, y sta CIA1_PRA #if CONFIG_KEYBOARD_C128 lda kb_matrix_bucky_confmask_c128, y sta VIC_XSCAN #endif #if CONFIG_KEYBOARD_C65 lda kb_matrix_bucky_confmask_c65, y sta C65_EXTKEYS_PR #endif lda kb_matrix_bucky_testmask, y and CIA1_PRB bne !+ // branch if not pressed lda SHFLAG ora kb_matrix_bucky_shflag, y sta SHFLAG !: dey bpl scnkey_bucky_loop // Set KEYTAB vector jsr via_keylog // XXX for some CPUs we have indirect jsr #if 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 #if CONFIG_KEY_FAST_SCAN inx // puts $00 stx CIA1_PRA // connect all the rows #if CONFIG_KEYBOARD_C128 stx VIC_XSCAN #endif #if CONFIG_KEYBOARD_C65 stx C65_EXTKEYS_PR #endif dex // puts $FF cpx CIA1_PRB beq scnkey_no_keys #endif // CONFIG_KEY_FAST_SCAN #endif // 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 #if 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 #endif // 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 #if CONFIG_KEYBOARD_C128 jsr scnkey_128 #endif #if CONFIG_KEYBOARD_C65 jsr scnkey_65 #endif 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 !+ // 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 !: 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 #if CONFIG_JOY1_CURSOR scnkey_joystick_1: // Prepare joystick 1 status lda CIA1_PRB and #%00001111 // filter out anything but movement // FALLTROUGH #endif #if CONFIG_JOY1_CURSOR || CONFIG_JOY2_CURSOR scnkey_joystick_filtered: // Set appropriate keyboard matrix and key code for joystick event ldy #$03 !: cmp kb_matrix_joy_status, y beq !+ dey bpl !- // Not found rts !: // 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 #endif 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 lda #$16 sta DELAY // FALLTROUGH scnkey_output_key: // Check if we have free space in the keyboard buffer lda NDX cmp XMAX bcs scnkey_early_repeat // no space in buffer // Reinitialize secondary counter lda #$03 sta KOUNT // Retrieve the PETSCII code #if CONFIG_KEYBOARD_C128 || CONFIG_KEYBOARD_C65 cpy #$40 bcc !+ #if CONFIG_KEYBOARD_C128 lda kb_matrix_128 - $41, y // retrieve key code from C128 extended matrix #elif CONFIG_KEYBOARD_C65 // Select key matrix (normal or shifted) for extended C65 keys lda SHFLAG and #%00000111 // KEY_FLAG_SHIFT + KEY_FLAG_VENDOR + KEY_FLAG_CTRL beq scnkey_output_65_no_shift cmp #KEY_FLAG_SHIFT // XXX how to behave on VENDOR/CTRL key? Harmonize this with claasic keys behavior bne scnkey_no_keys // no scanning for this bucky key combination lda kb_matrix_65_shifted - $41, y jmp scnkey_got_petscii scnkey_output_65_no_shift: lda kb_matrix_65 - $41, y #endif jmp scnkey_got_petscii !: lda (KEYTAB), y // retrieve key code from standard matrix #endif // CONFIG_KEYBOARD_C128 or CONFIG_KEYBOARD_C65 #if CONFIG_KEYBOARD_C128_CAPS_LOCK || CONFIG_KEYBOARD_C65_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: #if !CONFIG_KEYBOARD_C128 && !CONFIG_KEYBOARD_C65 lda (KEYTAB), y #endif tax lda SHFLAG and #(%00000111 + KEY_CAPS_LOCK) cmp #KEY_CAPS_LOCK bne !+ // branch if no special CAPS LOCK handling needed cpx #$41 // 'A' bcc !+ cpx #$5B // 'Z' + 1 bcs !+ txa clc adc #$20 // turn lowercase into uppercase tax !: txa #endif // CONFIG_KEYBOARD_C128_CAPS_LOCK or CONFIG_KEYBOARD_C65_CAPS_LOCK #if !CONFIG_KEYBOARD_C128 && !CONFIG_KEYBOARD_C128_CAPS_LOCK && !CONFIG_KEYBOARD_C65 && !CONFIG_KEYBOARD_C65_CAPS_LOCK lda (KEYTAB), y #endif // Output PETSCII code to the keyboard buffer scnkey_got_petscii: #if CONFIG_KEYBOARD_C128 && CONFIG_EDIT_TABULATORS // Special handling for SHIFT+TAB; it is easier than creating new matrix tax cmp #KEY_TAB_FW bne !+ lda SHFLAG and #KEY_FLAG_SHIFT beq !+ ldx #KEY_TAB_BW !: txa #endif // CONFIG_KEYBOARD_C128 and CONFIG_EDIT_TABULATORS 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: #if !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) !: cmp kb_matrix_alwaysrepeat, x beq scnkey_handle_repeat dex bpl !- bmi scnkey_done scnkey_handle_repeat: #endif // no CONFIG_KEY_REPEAT_ALWAYS // Countdown before first repeat lda DELAY beq !+ dec DELAY rts !: // 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_early_repeat: // Keyboard buffer was full - at least make sure the key // will be repeated as soon as possible lda #$00 sta DELAY sta KOUNT rts via_keylog: jmp (KEYLOG) #endif // no CONFIG_LEGACY_SCNKEY