diff --git a/gameloop.c65 b/gameloop.c65 index 6d0e985..f30fad4 100644 --- a/gameloop.c65 +++ b/gameloop.c65 @@ -930,7 +930,13 @@ FUNC game_loop WHILE vic_raster != 250 WEND - // Check for menu key (Return or Left Arrow) + // Read mouse input FIRST (before keyboard scan) + // Per Commodore 1351 manual: keyboard scan lines affect POT registers + // so mouse must be read before keyboard to avoid interference + mouse_read() + pointer_update_mouse(mouse_delta_x, mouse_delta_y) + + // Check for menu key (Return or Left Arrow) AFTER mouse key_scan(menu_key) IF menu_key == KEY_RETURN menu_show(menu_action) @@ -943,10 +949,6 @@ FUNC game_loop render_all_piles_initial() ENDIF - // Read mouse input (Port 1) - mouse_read() - pointer_update_mouse(mouse_delta_x, mouse_delta_y) - // Read joystick input (Port 2) joy_read_port2() pointer_update_joystick(joy_state) diff --git a/gamemenu.c65 b/gamemenu.c65 index 9a92eba..799befd 100644 --- a/gamemenu.c65 +++ b/gamemenu.c65 @@ -149,53 +149,34 @@ FEND // Render the full menu screen with current settings // ============================================================================ FUNC menu_render - WORD screen_pos - WORD str_ptr - // Clear screen fill_mem($0400, $0400+999, 32) // 32 = space character fill_mem($D800, $D800+999, 14) // Light blue text // Title (centered at row 2) - screen_pos = 2*40+$0400+8 - POINTER str_ptr -> str_title - menu_print_string(screen_pos, str_ptr) + menu_print_string(2*40+$0400+8, @str_title) // Draw mode option (row 6) - screen_pos = 6*40+$0400+4 - POINTER str_ptr -> str_draw_mode - menu_print_string(screen_pos, str_ptr) + menu_print_string(6*40+$0400+4, @str_draw_mode) // Draw mode value and indicators menu_update_draw_mode() // Foundation to Tableau option (row 9) - screen_pos = 9*40+$0400+4 - POINTER str_ptr -> str_found_to_tab - menu_print_string(screen_pos, str_ptr) + menu_print_string(9*40+$0400+4, @str_found_to_tab) // Foundation to Tableau value menu_update_found_to_tab() - // Instructions for resume/restart (row 14+) - screen_pos = 12*40+$0400+4 - POINTER str_ptr -> str_inst_f7 - menu_print_string(screen_pos, str_ptr) + // Instructions for resume/restart (row 12+) + menu_print_string(12*40+$0400+4, @str_inst_f7) + menu_print_string(15*40+$0400+4, @str_inst_runstop) - screen_pos = 15*40+$0400+4 - POINTER str_ptr -> str_inst_f8 - menu_print_string(screen_pos, str_ptr) + // Controls info (row 20) + menu_print_string(20*40+$0400+2, @str_controls) - // License (row 23, centered) - screen_pos = 20*40+$0400+2 - POINTER str_ptr -> str_controls - menu_print_string(screen_pos, str_ptr) - - - // License (row 23, centered) - screen_pos = 23*40+$0400+1 - POINTER str_ptr -> str_license - menu_print_string(screen_pos, str_ptr) + // License (row 23) + menu_print_string(23*40+$0400+1, @str_license) // Set border color to menu color (purple) POKE $d020 , 4 @@ -280,8 +261,8 @@ FUNC menu_show(out:{BYTE action}) action = MENU_ACTION_RESUME BREAK - CASE KEY_F8 - // Restart game (SHIFT+F7) + CASE KEY_RUNSTOP + // Restart game action = MENU_ACTION_RESTART BREAK ENDSWITCH @@ -364,9 +345,9 @@ ASM !scr "F7: Resume game", 0 ENDASM -LABEL str_inst_f8 +LABEL str_inst_runstop ASM - !scr "F8: Restart game", 0 + !scr "Run/Stop: Restart game", 0 ENDASM LABEL str_controls diff --git a/keyboard.c65 b/keyboard.c65 index b28cdc3..273c389 100644 --- a/keyboard.c65 +++ b/keyboard.c65 @@ -8,10 +8,14 @@ GOTO __skip_lib_keyboard // KEYBOARD INPUT DRIVER FOR COMMODORE 64 // ============================================================================ // Direct CIA keyboard matrix reading (no IRQ required) +// Uses non-interfering keyboard scan technique to avoid conflicts with +// joysticks on ports 1 and 2. // -// C64 Keyboard Matrix: -// $DC00 (Port A): Column select (write, active-low) -// $DC01 (Port B): Row read (read, active-low) +// Based on code from: +// https://codebase64.c64.org/doku.php?id=base:scanning_the_keyboard_without_joysticks_interfere +// +// This method converts keycodes to row/column values and performs double-check +// scanning to ensure joystick inputs don't interfere with keyboard detection. // // Usage: // #INCLUDE "keyboard.c65" @@ -23,11 +27,6 @@ GOTO __skip_lib_keyboard // CASE KEY_F3 // // handle F3 // ENDSWITCH -// -// // Debouncing: wait for KEY_NONE -// WHILE key != KEY_NONE -// key_scan(key) -// WEND // ============================================================================ // CIA Port addresses @@ -36,118 +35,153 @@ WORD CONST CIA_PORTB = $DC01 // Port B - COLUMN reading (read) WORD CONST CIA_DDRA = $DC02 // Port A Data Direction Register WORD CONST CIA_DDRB = $DC03 // Port B Data Direction Register -// Key constants +// Key constants (returned by key_scan) BYTE CONST KEY_NONE = 0 BYTE CONST KEY_F1 = 1 BYTE CONST KEY_F3 = 2 BYTE CONST KEY_F7 = 3 -BYTE CONST KEY_F8 = 4 // F7 + SHIFT +BYTE CONST KEY_RUNSTOP = 4 BYTE CONST KEY_RETURN = 5 -BYTE CONST KEY_LEFT_ARROW = 6 + +// C64 Keycode constants (for internal use) +// Based on: https://www.c64-wiki.com/wiki/Keyboard +BYTE CONST KEYCODE_RETURN = $01 +BYTE CONST KEYCODE_F7F8 = $03 +BYTE CONST KEYCODE_F1F2 = $04 +BYTE CONST KEYCODE_F3F4 = $05 +BYTE CONST KEYCODE_RUNSTOP = $3F + +// Row and column lookup tables for keyboard scanning +LABEL key_row_table +ASM + !8 $FE, $FD, $FB, $F7, $EF, $DF, $BF, $7F +ENDASM + +LABEL key_column_table +ASM + !8 $01, $02, $04, $08, $10, $20, $40, $80 +ENDASM + +// Key history for debouncing (one byte per tracked key) +BYTE key_history_f1 +BYTE key_history_f3 +BYTE key_history_f7 +BYTE key_history_return +BYTE key_history_runstop + +// ============================================================================ +// FUNC key_check_raw +// Low-level keyboard check for a specific keycode +// Uses non-interfering scan method with double-check to avoid joystick conflicts +// Returns: result = 1 if pressed, 0 if not pressed +// This comes from here: +// https://codebase64.c64.org/doku.php?id=base:scanning_the_keyboard_without_joysticks_interfere +// ============================================================================ +FUNC key_check_raw({BYTE keycode} io:{BYTE histv} out:{BYTE result}) + ASM + lda |keycode| + pha + lsr + lsr + lsr + tay + lda key_row_table,y + sta $dc00 + pla + and #$07 + tay + lda $dc01 + and key_column_table,y + bne _nokey + lda #$ff ; key is checked 2nd time, to be sure + sta $dc00 ; joy #1 and #2 are not interfering + lda $dc01 + and key_column_table,y + beq _nokey + + cmp |histv| ; history value? + bne _newkey + clc ; case #3, key is held down. keep history & set C=0 + bcc _backy + +_nokey + asl ; case #1, no keypress, update history value 2x & set C=0 + clc + !byte $24 ; BIT zero page - skips next 2-byte instruction +_newkey + sec ; case #2, key pressed&released. update history & set C=1 + sta |histv| + +_backy + lda #$ff + sta $dc00 ; set default value + lda #$7f + sta $dc01 ; set default value + + ; Convert carry flag to result: C=1 -> pressed (1), C=0 -> not pressed (0) + lda #0 + rol ; shift carry into bit 0 + sta |result| + ENDASM +FEND + // ============================================================================ // FUNC key_scan // Scan keyboard and return which key is pressed // Returns KEY_* constant (KEY_NONE if no relevant key pressed) -// C64 Keyboard Matrix: -// - Write to $DC00 (Port A) to select ROW -// - Read from $DC01 (Port B) to detect COLUMN +// Uses non-interfering keyboard scan to avoid joystick conflicts // ============================================================================ FUNC key_scan(out:{BYTE key_pressed}) - BYTE col_data - BYTE temp - BYTE shift_held - BYTE saved_ddra - BYTE saved_ddrb - BYTE saved_porta - BYTE row0_data - BYTE row1_data - BYTE row6_data + BYTE check_result key_pressed = KEY_NONE - shift_held = 0 - - // Save current DDR state - saved_ddra = PEEK CIA_DDRA - saved_ddrb = PEEK CIA_DDRB - saved_porta = PEEK CIA_PORTA // Set up DDR: Port A = output (rows), Port B = input (columns) - POKE CIA_DDRA , $FF - POKE CIA_DDRB , $00 + ASM + lda #$ff + sta $dc02 // CIA_DDRA + lda #$00 + sta $dc03 // CIA_DDRB + ENDASM - // Check F8 FIRST with two-read atomic check - // Read 1: Select rows 0+1+6 simultaneously ($BC = 10111100) - POKE CIA_PORTA , $BC - BYTE multi_row_data - multi_row_data = PEEK CIA_PORTB - - // Read 2: Select ONLY row 0 ($FE = 11111110) - POKE CIA_PORTA , $FE - col_data = PEEK CIA_PORTB - - // F8 detection: F7 pressed AND SHIFT pressed - // F7 at Row 0, Col 3 - check in row0 data - temp = col_data & $08 - IF temp == 0 - // F7 is pressed, now check if real SHIFT is pressed - // Compare multi-row read vs row-0-only read for cols 4 and 7 - // If col 4 or 7 was low in multi-row but NOT in row-0-only, it's SHIFT - BYTE shift_cols - shift_cols = multi_row_data & $90 // Cols 4 and 7 in multi-row read - temp = col_data & $90 // Cols 4 and 7 in row 0 only read - // If multi-row has a low bit that row-0-only doesn't, it's from rows 1 or 6 (SHIFT) - IF shift_cols != temp - key_pressed = KEY_F8 - ENDIF + // Check F1 + key_check_raw(KEYCODE_F1F2, key_history_f1, check_result) + IF check_result > 0 + key_pressed = KEY_F1 ENDIF - // If not F8, check other Row 0 keys (col_data already has row 0 from above) + // Check F3 IF key_pressed == KEY_NONE - - // Check F1 (Row 0, Column 4) - temp = col_data & $10 - IF temp == 0 - key_pressed = KEY_F1 - ENDIF - - // Check F3 (Row 0, Column 5) - IF key_pressed == KEY_NONE - temp = col_data & $20 - IF temp == 0 - key_pressed = KEY_F3 - ENDIF - ENDIF - - // Check F7 (Row 0, Column 3) - IF key_pressed == KEY_NONE - temp = col_data & $08 - IF temp == 0 - key_pressed = KEY_F7 - ENDIF - ENDIF - - // Check RETURN (Row 0, Column 1) - IF key_pressed == KEY_NONE - temp = col_data & $02 - IF temp == 0 - key_pressed = KEY_RETURN - ENDIF - ENDIF - - // Check Left Arrow (Row 0, Column 7) - IF key_pressed == KEY_NONE - temp = col_data & $80 - IF temp == 0 - key_pressed = KEY_LEFT_ARROW - ENDIF + key_check_raw(KEYCODE_F3F4, key_history_f3, check_result) + IF check_result > 0 + key_pressed = KEY_F3 ENDIF ENDIF - // Always restore saved DDR and port state - POKE CIA_PORTA , saved_porta - POKE CIA_DDRA , saved_ddra - POKE CIA_DDRB , saved_ddrb + // Check F7 + IF key_pressed == KEY_NONE + key_check_raw(KEYCODE_F7F8, key_history_f7, check_result) + IF check_result > 0 + key_pressed = KEY_F7 + ENDIF + ENDIF + + // Check RUN/STOP + IF key_pressed == KEY_NONE + key_check_raw(KEYCODE_RUNSTOP, key_history_runstop, check_result) + IF check_result > 0 + key_pressed = KEY_RUNSTOP + ENDIF + ENDIF + + // Check RETURN + IF key_pressed == KEY_NONE + key_check_raw(KEYCODE_RETURN, key_history_return, check_result) + IF check_result > 0 + key_pressed = KEY_RETURN + ENDIF + ENDIF FEND