Fixed game menu. Fixed jerky mouse movement while scanning keyboard (cause was scanning keyboard right before reading mouse). Menu code tighter.
This commit is contained in:
parent
a799d7695c
commit
f4bff18f29
3 changed files with 152 additions and 135 deletions
12
gameloop.c65
12
gameloop.c65
|
|
@ -930,7 +930,13 @@ FUNC game_loop
|
||||||
WHILE vic_raster != 250
|
WHILE vic_raster != 250
|
||||||
WEND
|
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)
|
key_scan(menu_key)
|
||||||
IF menu_key == KEY_RETURN
|
IF menu_key == KEY_RETURN
|
||||||
menu_show(menu_action)
|
menu_show(menu_action)
|
||||||
|
|
@ -943,10 +949,6 @@ FUNC game_loop
|
||||||
render_all_piles_initial()
|
render_all_piles_initial()
|
||||||
ENDIF
|
ENDIF
|
||||||
|
|
||||||
// Read mouse input (Port 1)
|
|
||||||
mouse_read()
|
|
||||||
pointer_update_mouse(mouse_delta_x, mouse_delta_y)
|
|
||||||
|
|
||||||
// Read joystick input (Port 2)
|
// Read joystick input (Port 2)
|
||||||
joy_read_port2()
|
joy_read_port2()
|
||||||
pointer_update_joystick(joy_state)
|
pointer_update_joystick(joy_state)
|
||||||
|
|
|
||||||
47
gamemenu.c65
47
gamemenu.c65
|
|
@ -149,53 +149,34 @@ FEND
|
||||||
// Render the full menu screen with current settings
|
// Render the full menu screen with current settings
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
FUNC menu_render
|
FUNC menu_render
|
||||||
WORD screen_pos
|
|
||||||
WORD str_ptr
|
|
||||||
|
|
||||||
// Clear screen
|
// Clear screen
|
||||||
fill_mem($0400, $0400+999, 32) // 32 = space character
|
fill_mem($0400, $0400+999, 32) // 32 = space character
|
||||||
fill_mem($D800, $D800+999, 14) // Light blue text
|
fill_mem($D800, $D800+999, 14) // Light blue text
|
||||||
|
|
||||||
// Title (centered at row 2)
|
// Title (centered at row 2)
|
||||||
screen_pos = 2*40+$0400+8
|
menu_print_string(2*40+$0400+8, @str_title)
|
||||||
POINTER str_ptr -> str_title
|
|
||||||
menu_print_string(screen_pos, str_ptr)
|
|
||||||
|
|
||||||
// Draw mode option (row 6)
|
// Draw mode option (row 6)
|
||||||
screen_pos = 6*40+$0400+4
|
menu_print_string(6*40+$0400+4, @str_draw_mode)
|
||||||
POINTER str_ptr -> str_draw_mode
|
|
||||||
menu_print_string(screen_pos, str_ptr)
|
|
||||||
|
|
||||||
// Draw mode value and indicators
|
// Draw mode value and indicators
|
||||||
menu_update_draw_mode()
|
menu_update_draw_mode()
|
||||||
|
|
||||||
// Foundation to Tableau option (row 9)
|
// Foundation to Tableau option (row 9)
|
||||||
screen_pos = 9*40+$0400+4
|
menu_print_string(9*40+$0400+4, @str_found_to_tab)
|
||||||
POINTER str_ptr -> str_found_to_tab
|
|
||||||
menu_print_string(screen_pos, str_ptr)
|
|
||||||
|
|
||||||
// Foundation to Tableau value
|
// Foundation to Tableau value
|
||||||
menu_update_found_to_tab()
|
menu_update_found_to_tab()
|
||||||
|
|
||||||
// Instructions for resume/restart (row 14+)
|
// Instructions for resume/restart (row 12+)
|
||||||
screen_pos = 12*40+$0400+4
|
menu_print_string(12*40+$0400+4, @str_inst_f7)
|
||||||
POINTER str_ptr -> str_inst_f7
|
menu_print_string(15*40+$0400+4, @str_inst_runstop)
|
||||||
menu_print_string(screen_pos, str_ptr)
|
|
||||||
|
|
||||||
screen_pos = 15*40+$0400+4
|
// Controls info (row 20)
|
||||||
POINTER str_ptr -> str_inst_f8
|
menu_print_string(20*40+$0400+2, @str_controls)
|
||||||
menu_print_string(screen_pos, str_ptr)
|
|
||||||
|
|
||||||
// License (row 23, centered)
|
// License (row 23)
|
||||||
screen_pos = 20*40+$0400+2
|
menu_print_string(23*40+$0400+1, @str_license)
|
||||||
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)
|
|
||||||
|
|
||||||
// Set border color to menu color (purple)
|
// Set border color to menu color (purple)
|
||||||
POKE $d020 , 4
|
POKE $d020 , 4
|
||||||
|
|
@ -280,8 +261,8 @@ FUNC menu_show(out:{BYTE action})
|
||||||
action = MENU_ACTION_RESUME
|
action = MENU_ACTION_RESUME
|
||||||
BREAK
|
BREAK
|
||||||
|
|
||||||
CASE KEY_F8
|
CASE KEY_RUNSTOP
|
||||||
// Restart game (SHIFT+F7)
|
// Restart game
|
||||||
action = MENU_ACTION_RESTART
|
action = MENU_ACTION_RESTART
|
||||||
BREAK
|
BREAK
|
||||||
ENDSWITCH
|
ENDSWITCH
|
||||||
|
|
@ -364,9 +345,9 @@ ASM
|
||||||
!scr "F7: Resume game", 0
|
!scr "F7: Resume game", 0
|
||||||
ENDASM
|
ENDASM
|
||||||
|
|
||||||
LABEL str_inst_f8
|
LABEL str_inst_runstop
|
||||||
ASM
|
ASM
|
||||||
!scr "F8: Restart game", 0
|
!scr "Run/Stop: Restart game", 0
|
||||||
ENDASM
|
ENDASM
|
||||||
|
|
||||||
LABEL str_controls
|
LABEL str_controls
|
||||||
|
|
|
||||||
228
keyboard.c65
228
keyboard.c65
|
|
@ -8,10 +8,14 @@ GOTO __skip_lib_keyboard
|
||||||
// KEYBOARD INPUT DRIVER FOR COMMODORE 64
|
// KEYBOARD INPUT DRIVER FOR COMMODORE 64
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Direct CIA keyboard matrix reading (no IRQ required)
|
// 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:
|
// Based on code from:
|
||||||
// $DC00 (Port A): Column select (write, active-low)
|
// https://codebase64.c64.org/doku.php?id=base:scanning_the_keyboard_without_joysticks_interfere
|
||||||
// $DC01 (Port B): Row read (read, active-low)
|
//
|
||||||
|
// This method converts keycodes to row/column values and performs double-check
|
||||||
|
// scanning to ensure joystick inputs don't interfere with keyboard detection.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// #INCLUDE "keyboard.c65"
|
// #INCLUDE "keyboard.c65"
|
||||||
|
|
@ -23,11 +27,6 @@ GOTO __skip_lib_keyboard
|
||||||
// CASE KEY_F3
|
// CASE KEY_F3
|
||||||
// // handle F3
|
// // handle F3
|
||||||
// ENDSWITCH
|
// ENDSWITCH
|
||||||
//
|
|
||||||
// // Debouncing: wait for KEY_NONE
|
|
||||||
// WHILE key != KEY_NONE
|
|
||||||
// key_scan(key)
|
|
||||||
// WEND
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// CIA Port addresses
|
// 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_DDRA = $DC02 // Port A Data Direction Register
|
||||||
WORD CONST CIA_DDRB = $DC03 // Port B 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_NONE = 0
|
||||||
BYTE CONST KEY_F1 = 1
|
BYTE CONST KEY_F1 = 1
|
||||||
BYTE CONST KEY_F3 = 2
|
BYTE CONST KEY_F3 = 2
|
||||||
BYTE CONST KEY_F7 = 3
|
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_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
|
// FUNC key_scan
|
||||||
// Scan keyboard and return which key is pressed
|
// Scan keyboard and return which key is pressed
|
||||||
// Returns KEY_* constant (KEY_NONE if no relevant key pressed)
|
// Returns KEY_* constant (KEY_NONE if no relevant key pressed)
|
||||||
// C64 Keyboard Matrix:
|
// Uses non-interfering keyboard scan to avoid joystick conflicts
|
||||||
// - Write to $DC00 (Port A) to select ROW
|
|
||||||
// - Read from $DC01 (Port B) to detect COLUMN
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
FUNC key_scan(out:{BYTE key_pressed})
|
FUNC key_scan(out:{BYTE key_pressed})
|
||||||
BYTE col_data
|
BYTE check_result
|
||||||
BYTE temp
|
|
||||||
BYTE shift_held
|
|
||||||
BYTE saved_ddra
|
|
||||||
BYTE saved_ddrb
|
|
||||||
BYTE saved_porta
|
|
||||||
BYTE row0_data
|
|
||||||
BYTE row1_data
|
|
||||||
BYTE row6_data
|
|
||||||
|
|
||||||
key_pressed = KEY_NONE
|
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)
|
// Set up DDR: Port A = output (rows), Port B = input (columns)
|
||||||
POKE CIA_DDRA , $FF
|
ASM
|
||||||
POKE CIA_DDRB , $00
|
lda #$ff
|
||||||
|
sta $dc02 // CIA_DDRA
|
||||||
|
lda #$00
|
||||||
|
sta $dc03 // CIA_DDRB
|
||||||
|
ENDASM
|
||||||
|
|
||||||
// Check F8 FIRST with two-read atomic check
|
// Check F1
|
||||||
// Read 1: Select rows 0+1+6 simultaneously ($BC = 10111100)
|
key_check_raw(KEYCODE_F1F2, key_history_f1, check_result)
|
||||||
POKE CIA_PORTA , $BC
|
IF check_result > 0
|
||||||
BYTE multi_row_data
|
key_pressed = KEY_F1
|
||||||
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
|
|
||||||
ENDIF
|
ENDIF
|
||||||
|
|
||||||
// If not F8, check other Row 0 keys (col_data already has row 0 from above)
|
// Check F3
|
||||||
IF key_pressed == KEY_NONE
|
IF key_pressed == KEY_NONE
|
||||||
|
key_check_raw(KEYCODE_F3F4, key_history_f3, check_result)
|
||||||
// Check F1 (Row 0, Column 4)
|
IF check_result > 0
|
||||||
temp = col_data & $10
|
key_pressed = KEY_F3
|
||||||
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
|
|
||||||
ENDIF
|
ENDIF
|
||||||
ENDIF
|
ENDIF
|
||||||
|
|
||||||
// Always restore saved DDR and port state
|
// Check F7
|
||||||
POKE CIA_PORTA , saved_porta
|
IF key_pressed == KEY_NONE
|
||||||
POKE CIA_DDRA , saved_ddra
|
key_check_raw(KEYCODE_F7F8, key_history_f7, check_result)
|
||||||
POKE CIA_DDRB , saved_ddrb
|
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
|
FEND
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue