Added a game menu to the system with possibility to restart the game and to choose game options.
This commit is contained in:
parent
bf7e30d4ea
commit
e4d8e42821
8 changed files with 767 additions and 123 deletions
|
|
@ -14,7 +14,9 @@ ENDASM
|
|||
|
||||
#INCLUDE "utils.c65"
|
||||
#INCLUDE "cardconsts.c65"
|
||||
#INCLUDE "pileconsts.c65"
|
||||
#INCLUDE "piles.c65"
|
||||
#INCLUDE "gamestate.c65"
|
||||
#INCLUDE "random.c65"
|
||||
#INCLUDE "carddeck.c65"
|
||||
#INCLUDE "cardrender.c65"
|
||||
|
|
@ -22,6 +24,8 @@ ENDASM
|
|||
#INCLUDE "joystick.c65"
|
||||
#INCLUDE "mouse.c65"
|
||||
#INCLUDE "pointer.c65"
|
||||
#INCLUDE "keyboard.c65"
|
||||
#INCLUDE "gamemenu.c65"
|
||||
//#INCLUDE "joysticktests.c65"
|
||||
#IFDEF TEST_GAMES
|
||||
#INCLUDE "testgames.c65"
|
||||
|
|
|
|||
114
gameloop.c65
114
gameloop.c65
|
|
@ -3,12 +3,16 @@
|
|||
#DEFINE __lib_gameloop 1
|
||||
|
||||
#INCLUDE "cardconsts.c65"
|
||||
#INCLUDE "pileconsts.c65"
|
||||
#INCLUDE "piles.c65"
|
||||
#INCLUDE "gamestate.c65"
|
||||
#INCLUDE "cardmoves.c65"
|
||||
#INCLUDE "cardrender.c65"
|
||||
#INCLUDE "mouse.c65"
|
||||
#INCLUDE "pointer.c65"
|
||||
#INCLUDE "cardsprites.c65"
|
||||
#INCLUDE "keyboard.c65"
|
||||
#INCLUDE "gamemenu.c65"
|
||||
|
||||
GOTO __skip_lib_gameloop
|
||||
|
||||
|
|
@ -24,27 +28,8 @@ GOTO __skip_lib_gameloop
|
|||
// game_loop() // Never returns
|
||||
// ============================================================================
|
||||
|
||||
// Pile identifiers
|
||||
BYTE CONST PILE_ID_NONE = 0
|
||||
BYTE CONST PILE_ID_STOCK = 1
|
||||
BYTE CONST PILE_ID_WASTE = 2
|
||||
BYTE CONST PILE_ID_FOUND0 = 3
|
||||
BYTE CONST PILE_ID_FOUND1 = 4
|
||||
BYTE CONST PILE_ID_FOUND2 = 5
|
||||
BYTE CONST PILE_ID_FOUND3 = 6
|
||||
BYTE CONST PILE_ID_TAB0 = 7
|
||||
BYTE CONST PILE_ID_TAB1 = 8
|
||||
BYTE CONST PILE_ID_TAB2 = 9
|
||||
BYTE CONST PILE_ID_TAB3 = 10
|
||||
BYTE CONST PILE_ID_TAB4 = 11
|
||||
BYTE CONST PILE_ID_TAB5 = 12
|
||||
BYTE CONST PILE_ID_TAB6 = 13
|
||||
|
||||
// Game state variables
|
||||
BYTE game_selected_pile // Currently selected pile ID (PILE_ID_NONE if none)
|
||||
BYTE game_selected_card_count // Number of cards selected from tableau (1+ for tableau stacks)
|
||||
BYTE game_prev_button_state // Previous mouse button state for click detection
|
||||
BYTE game_draw_mode = 1 // Stock draw mode: 1 or 3 cards per draw
|
||||
// Pile identifiers now in pileconsts.c65
|
||||
// Game state variables now in gamestate.c65
|
||||
|
||||
// Layout constants (in character coordinates)
|
||||
BYTE CONST LAYOUT_STOCK_COL = 0
|
||||
|
|
@ -786,11 +771,54 @@ FUNC handle_click_on_pile({BYTE clicked_pile} {BYTE click_row})
|
|||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Foundation to Tableau (if enabled)
|
||||
IF game_allow_found_to_tab
|
||||
BYTE selected_is_foundation
|
||||
selected_is_foundation = 0
|
||||
IF game_selected_pile >= PILE_ID_FOUND0
|
||||
IF game_selected_pile <= PILE_ID_FOUND3
|
||||
selected_is_foundation = 1
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
IF selected_is_foundation
|
||||
IF is_tableau
|
||||
move_found_to_tab(src_ptr, dst_ptr, success)
|
||||
IF success
|
||||
render_pile_by_id(game_selected_pile)
|
||||
render_pile_by_id(clicked_pile)
|
||||
ENDIF
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Click on same pile or invalid destination: deselect
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC restart_game
|
||||
// Restart the game with new shuffle and deal
|
||||
// ============================================================================
|
||||
FUNC restart_game
|
||||
// Clear all piles (waste, foundations, tableaus)
|
||||
clear_all_piles()
|
||||
|
||||
// Re-initialize deck and shuffle (RNG continues from current state)
|
||||
stock_init()
|
||||
stock_shuffle()
|
||||
deal_tableaus()
|
||||
|
||||
// Reset game state
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
game_selected_card_count = 0
|
||||
game_prev_button_state = 0
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC render_all_piles_initial
|
||||
// Render all piles for initial game display
|
||||
|
|
@ -836,6 +864,12 @@ FUNC game_loop
|
|||
POINTER dst_ptr -> $2240
|
||||
mem_copy_range(src_ptr, dst_ptr, 64*25)
|
||||
|
||||
|
||||
|
||||
// Outer loop for restart handling
|
||||
WHILE 1
|
||||
|
||||
|
||||
// Initialize mouse and pointer
|
||||
mouse_init()
|
||||
pointer_init(160, 100, color_red, 136) // Sprite block 136 = $2200, bright red color
|
||||
|
|
@ -853,17 +887,45 @@ FUNC game_loop
|
|||
setup_test_game_overflow() // K->A in tab3 to test screen overflow
|
||||
#IFEND
|
||||
|
||||
BYTE raster @ $d012
|
||||
BYTE menu_key
|
||||
BYTE menu_action
|
||||
|
||||
|
||||
// Initial render
|
||||
fill_mem($0400, $0400+999, 0) // Clear screen
|
||||
|
||||
render_all_piles_initial()
|
||||
|
||||
BYTE raster @ $d012
|
||||
|
||||
// Inner game loop
|
||||
WHILE 1
|
||||
// Wait for raster to avoid tearing
|
||||
WHILE raster != 250
|
||||
WEND
|
||||
|
||||
// Check for menu key (Return or Left Arrow)
|
||||
key_scan(menu_key)
|
||||
IF menu_key == KEY_RETURN
|
||||
menu_show(menu_action)
|
||||
IF menu_action == MENU_ACTION_RESTART
|
||||
// Restart requested - break out to restart
|
||||
BREAK
|
||||
ENDIF
|
||||
// Clear screen and re-render after resume
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
render_all_piles_initial()
|
||||
ENDIF
|
||||
IF menu_key == KEY_LEFT_ARROW
|
||||
menu_show(menu_action)
|
||||
IF menu_action == MENU_ACTION_RESTART
|
||||
// Restart requested - break out to restart
|
||||
BREAK
|
||||
ENDIF
|
||||
// Clear screen and re-render after resume
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
render_all_piles_initial()
|
||||
ENDIF
|
||||
|
||||
// Read mouse input (Port 1)
|
||||
mouse_read()
|
||||
pointer_update_mouse(mouse_delta_x, mouse_delta_y)
|
||||
|
|
@ -957,7 +1019,11 @@ FUNC game_loop
|
|||
|
||||
// Small delay to avoid reading mouse too fast
|
||||
// Could sync to raster if needed for smoother experience
|
||||
WEND
|
||||
WEND // End inner game loop
|
||||
|
||||
// If we get here, restart was requested
|
||||
restart_game()
|
||||
WEND // End outer restart loop
|
||||
FEND
|
||||
|
||||
|
||||
|
|
|
|||
313
gamemenu.c65
Normal file
313
gamemenu.c65
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
|
||||
#IFNDEF __lib_gamemenu
|
||||
#DEFINE __lib_gamemenu 1
|
||||
|
||||
#INCLUDE "pileconsts.c65"
|
||||
#INCLUDE "keyboard.c65"
|
||||
#INCLUDE "utils.c65"
|
||||
|
||||
GOTO __skip_lib_gamemenu
|
||||
|
||||
// ============================================================================
|
||||
// GAME MENU SYSTEM
|
||||
// ============================================================================
|
||||
// Pause game and display menu for configuration and restart
|
||||
//
|
||||
// Usage:
|
||||
// #INCLUDE "gamemenu.c65"
|
||||
// BYTE action
|
||||
// menu_show(action) // Returns MENU_ACTION_* constant
|
||||
// ============================================================================
|
||||
|
||||
// Menu action constants
|
||||
BYTE CONST MENU_ACTION_RESUME = 0
|
||||
BYTE CONST MENU_ACTION_RESTART = 1
|
||||
|
||||
// Screen backup buffer (1000 bytes for screen, 1000 for color)
|
||||
// Located at $C000-$C7CF (2000 bytes)
|
||||
WORD CONST SCREEN_BACKUP = $C000
|
||||
WORD CONST COLOR_BACKUP = $C3E8
|
||||
|
||||
// ============================================================================
|
||||
// FUNC menu_save_screen
|
||||
// Save current screen and color memory to backup buffer
|
||||
// ============================================================================
|
||||
FUNC menu_save_screen
|
||||
mem_copy_range($0400, SCREEN_BACKUP, 1000)
|
||||
mem_copy_range($D800, COLOR_BACKUP, 1000)
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC menu_restore_screen
|
||||
// Restore screen and color memory from backup buffer
|
||||
// ============================================================================
|
||||
FUNC menu_restore_screen
|
||||
mem_copy_range(SCREEN_BACKUP, $0400, 1000)
|
||||
mem_copy_range(COLOR_BACKUP, $D800, 1000)
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC menu_switch_to_rom_charset
|
||||
// Switch VIC-II to use ROM charset at $1800 (uppercase/lowercase)
|
||||
// ============================================================================
|
||||
FUNC menu_switch_to_rom_charset
|
||||
set_vic_charmem(3) // $1800 / $0800 = 3
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC menu_switch_to_card_charset
|
||||
// Switch VIC-II back to custom card charset at $2000
|
||||
// ============================================================================
|
||||
FUNC menu_switch_to_card_charset
|
||||
set_vic_charmem(4) // $2000 / $0800 = 4
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC menu_print_string
|
||||
// Print a string at a specific screen position
|
||||
// ============================================================================
|
||||
FUNC menu_print_string({WORD screen_pos @ $e0} {WORD str_ptr @ $e2})
|
||||
BYTE char
|
||||
BYTE str_offset
|
||||
|
||||
str_offset = 0
|
||||
char = PEEK str_ptr[str_offset]
|
||||
WHILE char != 0
|
||||
POKE screen_pos[0] , char
|
||||
screen_pos++
|
||||
str_offset++
|
||||
char = PEEK str_ptr[str_offset]
|
||||
WEND
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC menu_render
|
||||
// Render the menu screen with current settings
|
||||
// ============================================================================
|
||||
FUNC menu_render
|
||||
WORD screen_pos @ $f0
|
||||
WORD str_ptr @ $f2
|
||||
BYTE draw_indicator
|
||||
BYTE found_to_tab_indicator
|
||||
|
||||
// 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)
|
||||
|
||||
// Draw mode option (row 6)
|
||||
screen_pos = 6*40+$0400+4
|
||||
POINTER str_ptr -> str_draw_mode
|
||||
menu_print_string(screen_pos, str_ptr)
|
||||
|
||||
// Draw mode value
|
||||
screen_pos = 6*40+$0400+24
|
||||
IF game_draw_mode == 1
|
||||
POKE screen_pos[0] , 49 // '1'
|
||||
ELSE
|
||||
POKE screen_pos[0] , 51 // '3'
|
||||
ENDIF
|
||||
|
||||
// Draw mode indicator
|
||||
IF game_draw_mode == 1
|
||||
draw_indicator = 91 // '['
|
||||
screen_pos = 6*40+$0400+23
|
||||
POKE screen_pos[0] , draw_indicator
|
||||
screen_pos = 6*40+$0400+25
|
||||
POKE screen_pos[0] , 93 // ']'
|
||||
ELSE
|
||||
draw_indicator = 91 // '['
|
||||
screen_pos = 6*40+$0400+26
|
||||
POKE screen_pos[0] , draw_indicator
|
||||
screen_pos = 6*40+$0400+28
|
||||
POKE screen_pos[0] , 93 // ']'
|
||||
ENDIF
|
||||
|
||||
// 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)
|
||||
|
||||
// Foundation to Tableau value
|
||||
screen_pos = 9*40+$0400+28
|
||||
IF game_allow_found_to_tab == 0
|
||||
POINTER str_ptr -> str_off
|
||||
ELSE
|
||||
POINTER str_ptr -> str_on
|
||||
ENDIF
|
||||
menu_print_string(screen_pos, str_ptr)
|
||||
|
||||
// Instructions for resume/restart (row 14+)
|
||||
screen_pos = 14*40+$0400+4
|
||||
POINTER str_ptr -> str_inst_f7
|
||||
menu_print_string(screen_pos, str_ptr)
|
||||
|
||||
screen_pos = 15*40+$0400+4
|
||||
POINTER str_ptr -> str_inst_f8
|
||||
menu_print_string(screen_pos, str_ptr)
|
||||
|
||||
// Set border color to menu color (purple)
|
||||
POKE $d020 , 4
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC menu_show
|
||||
// Display menu and handle input, return action to take
|
||||
// ============================================================================
|
||||
FUNC menu_show(out:{BYTE action})
|
||||
BYTE key
|
||||
BYTE prev_key
|
||||
|
||||
action = MENU_ACTION_RESUME
|
||||
prev_key = KEY_NONE
|
||||
|
||||
// Clear any card selection before entering menu
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
game_selected_card_count = 0
|
||||
|
||||
// Save current screen state
|
||||
menu_save_screen()
|
||||
|
||||
// Disable all sprites
|
||||
POKE $D015 , 0 // Sprite enable register - disable all
|
||||
|
||||
// Switch to ROM charset and normal character mode
|
||||
menu_switch_to_rom_charset()
|
||||
clear_vic_ecm()
|
||||
|
||||
// Render menu
|
||||
menu_render()
|
||||
|
||||
// Wait for key release first (debounce)
|
||||
key_scan(key)
|
||||
WHILE key != KEY_NONE
|
||||
key_scan(key)
|
||||
WEND
|
||||
|
||||
// Menu loop
|
||||
WHILE 1
|
||||
key_scan(key)
|
||||
|
||||
// Only process on key press (transition from NONE to key)
|
||||
IF key != KEY_NONE
|
||||
IF prev_key == KEY_NONE
|
||||
SWITCH key
|
||||
CASE KEY_F1
|
||||
// Toggle draw mode
|
||||
IF game_draw_mode == 1
|
||||
game_draw_mode = 3
|
||||
ELSE
|
||||
game_draw_mode = 1
|
||||
ENDIF
|
||||
menu_render()
|
||||
|
||||
CASE KEY_F3
|
||||
// Toggle foundation to tableau
|
||||
IF game_allow_found_to_tab == 0
|
||||
game_allow_found_to_tab = 1
|
||||
ELSE
|
||||
game_allow_found_to_tab = 0
|
||||
ENDIF
|
||||
menu_render()
|
||||
|
||||
CASE KEY_F7
|
||||
// Resume game
|
||||
action = MENU_ACTION_RESUME
|
||||
BREAK
|
||||
|
||||
CASE KEY_F8
|
||||
// Restart game (SHIFT+F7)
|
||||
action = MENU_ACTION_RESTART
|
||||
BREAK
|
||||
ENDSWITCH
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
prev_key = key
|
||||
WEND
|
||||
|
||||
// Wait for key release before returning
|
||||
WHILE key != KEY_NONE
|
||||
key_scan(key)
|
||||
WEND
|
||||
|
||||
// Restore screen or clear for restart
|
||||
IF action == MENU_ACTION_RESUME
|
||||
menu_restore_screen()
|
||||
ELSE
|
||||
// Restarting - clear screen and reset colors
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
fill_mem($d800, $d800+999, 1) // White color
|
||||
ENDIF
|
||||
|
||||
// Switch back to card charset and ECM mode
|
||||
menu_switch_to_card_charset()
|
||||
set_vic_ecm()
|
||||
|
||||
// Re-enable pointer sprite (game loop will handle card display sprites)
|
||||
pointer_enable(1)
|
||||
|
||||
// Restore border color
|
||||
POKE $d020 , 1 // White
|
||||
FEND
|
||||
|
||||
// Menu strings (null-terminated) - Using !scr for screen codes
|
||||
LABEL str_title
|
||||
ASM
|
||||
!scr "Klondike Solitaire Menu", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_draw_mode
|
||||
ASM
|
||||
!scr "F1: Draw mode:", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_found_to_tab
|
||||
ASM
|
||||
!scr "F3: Foundation>Tableau:", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_on
|
||||
ASM
|
||||
!scr "On", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_off
|
||||
ASM
|
||||
!scr "Off", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_inst_f1
|
||||
ASM
|
||||
!scr "F1: Toggle draw mode (1/3)", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_inst_f3
|
||||
ASM
|
||||
!scr "F3: Toggle foundation moves", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_inst_f7
|
||||
ASM
|
||||
!scr "F7: Resume game", 0
|
||||
ENDASM
|
||||
|
||||
LABEL str_inst_f8
|
||||
ASM
|
||||
!scr "F8: Restart game", 0
|
||||
ENDASM
|
||||
|
||||
|
||||
LABEL __skip_lib_gamemenu
|
||||
|
||||
#IFEND
|
||||
25
gamestate.c65
Normal file
25
gamestate.c65
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
#IFNDEF __lib_gamestate
|
||||
#DEFINE __lib_gamestate 1
|
||||
|
||||
GOTO __skip_lib_gamestate
|
||||
|
||||
// ============================================================================
|
||||
// GAME STATE VARIABLES
|
||||
// ============================================================================
|
||||
// Global game state that needs to be accessible from multiple modules
|
||||
// ============================================================================
|
||||
|
||||
// Game configuration
|
||||
BYTE game_draw_mode = 1 // Stock draw mode: 1 or 3 cards per draw
|
||||
BYTE game_allow_found_to_tab = 0 // Allow foundation to tableau moves: 0=disallow, 1=allow
|
||||
|
||||
// Game interaction state
|
||||
BYTE game_selected_pile // Currently selected pile ID (PILE_ID_NONE if none)
|
||||
BYTE game_selected_card_count // Number of cards selected from tableau (1+ for tableau stacks)
|
||||
BYTE game_prev_button_state // Previous mouse button state for click detection
|
||||
|
||||
|
||||
LABEL __skip_lib_gamestate
|
||||
|
||||
#IFEND
|
||||
156
keyboard.c65
Normal file
156
keyboard.c65
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
|
||||
#IFNDEF __lib_keyboard
|
||||
#DEFINE __lib_keyboard 1
|
||||
|
||||
GOTO __skip_lib_keyboard
|
||||
|
||||
// ============================================================================
|
||||
// KEYBOARD INPUT DRIVER FOR COMMODORE 64
|
||||
// ============================================================================
|
||||
// Direct CIA keyboard matrix reading (no IRQ required)
|
||||
//
|
||||
// C64 Keyboard Matrix:
|
||||
// $DC00 (Port A): Column select (write, active-low)
|
||||
// $DC01 (Port B): Row read (read, active-low)
|
||||
//
|
||||
// Usage:
|
||||
// #INCLUDE "keyboard.c65"
|
||||
// BYTE key
|
||||
// key_scan(key)
|
||||
// SWITCH key
|
||||
// CASE KEY_F1
|
||||
// // handle F1
|
||||
// CASE KEY_F3
|
||||
// // handle F3
|
||||
// ENDSWITCH
|
||||
//
|
||||
// // Debouncing: wait for KEY_NONE
|
||||
// WHILE key != KEY_NONE
|
||||
// key_scan(key)
|
||||
// WEND
|
||||
// ============================================================================
|
||||
|
||||
// CIA Port addresses
|
||||
WORD CONST CIA_PORTA = $DC00 // Port A - ROW selection (write)
|
||||
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
|
||||
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_RETURN = 5
|
||||
BYTE CONST KEY_LEFT_ARROW = 6
|
||||
|
||||
// ============================================================================
|
||||
// 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
|
||||
// ============================================================================
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
ENDIF
|
||||
|
||||
// If not F8, check other Row 0 keys (col_data already has row 0 from above)
|
||||
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
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Always restore saved DDR and port state
|
||||
POKE CIA_PORTA , saved_porta
|
||||
POKE CIA_DDRA , saved_ddra
|
||||
POKE CIA_DDRB , saved_ddrb
|
||||
FEND
|
||||
|
||||
|
||||
LABEL __skip_lib_keyboard
|
||||
|
||||
#IFEND
|
||||
31
pileconsts.c65
Normal file
31
pileconsts.c65
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
#IFNDEF __lib_pileconsts
|
||||
#DEFINE __lib_pileconsts 1
|
||||
|
||||
GOTO __skip_lib_pileconsts
|
||||
|
||||
// ============================================================================
|
||||
// PILE ID CONSTANTS
|
||||
// ============================================================================
|
||||
// Pile identifiers used throughout the game
|
||||
// ============================================================================
|
||||
|
||||
BYTE CONST PILE_ID_NONE = 0
|
||||
BYTE CONST PILE_ID_STOCK = 1
|
||||
BYTE CONST PILE_ID_WASTE = 2
|
||||
BYTE CONST PILE_ID_FOUND0 = 3
|
||||
BYTE CONST PILE_ID_FOUND1 = 4
|
||||
BYTE CONST PILE_ID_FOUND2 = 5
|
||||
BYTE CONST PILE_ID_FOUND3 = 6
|
||||
BYTE CONST PILE_ID_TAB0 = 7
|
||||
BYTE CONST PILE_ID_TAB1 = 8
|
||||
BYTE CONST PILE_ID_TAB2 = 9
|
||||
BYTE CONST PILE_ID_TAB3 = 10
|
||||
BYTE CONST PILE_ID_TAB4 = 11
|
||||
BYTE CONST PILE_ID_TAB5 = 12
|
||||
BYTE CONST PILE_ID_TAB6 = 13
|
||||
|
||||
|
||||
LABEL __skip_lib_pileconsts
|
||||
|
||||
#IFEND
|
||||
41
piles.c65
41
piles.c65
|
|
@ -73,6 +73,47 @@ ASM
|
|||
!fill 14, 0
|
||||
ENDASM
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC clear_all_piles
|
||||
// Clear all piles (waste, foundations, tableaus) by setting their counts to 0
|
||||
// Stock is not cleared as it's managed by stock_init()
|
||||
// ============================================================================
|
||||
FUNC clear_all_piles
|
||||
WORD ptr @ $f0
|
||||
|
||||
// Clear waste pile
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 0
|
||||
|
||||
// Clear all foundation piles
|
||||
POINTER ptr -> pile_found0
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_found1
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_found2
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_found3
|
||||
POKE ptr[0] , 0
|
||||
|
||||
// Clear all tableau piles
|
||||
POINTER ptr -> pile_tab0
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_tab1
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_tab2
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_tab3
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_tab4
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_tab5
|
||||
POKE ptr[0] , 0
|
||||
POINTER ptr -> pile_tab6
|
||||
POKE ptr[0] , 0
|
||||
FEND
|
||||
|
||||
|
||||
LABEL __skip_lib_piles
|
||||
|
||||
#IFEND
|
||||
|
|
|
|||
|
|
@ -117,6 +117,14 @@ FUNC set_vic_ecm
|
|||
ENDASM
|
||||
FEND
|
||||
|
||||
FUNC clear_vic_ecm
|
||||
ASM
|
||||
lda $d011
|
||||
and #$1f ; leave DEN bit + all scroll stuff, clear ECM bit
|
||||
sta $d011
|
||||
ENDASM
|
||||
FEND
|
||||
|
||||
// Wait for a keypress using direct CIA keyboard scan
|
||||
// No interrupts needed
|
||||
FUNC wait_key
|
||||
|
|
|
|||
Loading…
Reference in a new issue