First commit in fresh repo
This commit is contained in:
parent
f3e483ca0b
commit
9c723cce1d
43 changed files with 6757 additions and 2 deletions
60
.gitignore
vendored
Normal file
60
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Python bytecode / caches
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.so
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
.music/.venv/ # if your venv lives specifically under /music
|
||||
.python-version
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# Packaging / wheels / builds
|
||||
build/
|
||||
dist/
|
||||
.eggs/
|
||||
*.egg-info/
|
||||
pip-wheel-metadata/
|
||||
|
||||
# Tests / coverage / benchmarks
|
||||
.pytest_cache/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage*
|
||||
htmlcov/
|
||||
.hypothesis/
|
||||
.benchmark/
|
||||
|
||||
# Type checkers / linters
|
||||
.mypy_cache/
|
||||
.pytype/
|
||||
.pyre/
|
||||
.ruff_cache/
|
||||
|
||||
# Jupyter
|
||||
.ipynb_checkpoints/
|
||||
|
||||
# Logs / databases / local data
|
||||
*.log
|
||||
*.sqlite3
|
||||
|
||||
# OS cruft
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDEs / editors
|
||||
.idea/
|
||||
*.iml
|
||||
.vscode/
|
||||
*.s
|
||||
*.sym
|
||||
*.prg
|
||||
|
||||
.claude/
|
||||
.npm/
|
||||
6
Dockerfile
Normal file
6
Dockerfile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
RUN apk add --no-cache bash
|
||||
ENV SHELL=/bin/bash
|
||||
RUN npm install -g @anthropic-ai/claude-code
|
||||
CMD ["claude"]
|
||||
45
README.md
45
README.md
|
|
@ -1,3 +1,44 @@
|
|||
# solitaire-c64
|
||||
# Solitaire C64
|
||||
|
||||
A solitaire style game for the Commodore 64
|
||||
A classic Klondike solitaire card game for the Commodore 64, written in c65gm.
|
||||
|
||||
## Features
|
||||
|
||||
- **Full Klondike Solitaire**: Stock, waste, 7 tableaus, and 4 foundation piles
|
||||
- **Dual Input Support**: Play with joystick or 1351 mouse
|
||||
- **Draw Modes**: Toggle between draw-1 and draw-3 gameplay
|
||||
- **Custom Graphics**: Character-based card rendering using extended color mode
|
||||
- **Sprite Cursor**: Visual pointer for card selection and movement
|
||||
- **Smart Shuffling**: Hardware-seeded RNG for true randomness
|
||||
|
||||
## Technical Details
|
||||
|
||||
**Language**: c65gm (C-like language for 6502/C64)
|
||||
**Memory Layout**: Code at $3000, custom charset at $2000
|
||||
**Graphics**: Extended Color Mode (ECM) with custom character set
|
||||
**Input**: CIA joystick ports + 1351 proportional mouse support
|
||||
|
||||
## Building
|
||||
|
||||
Requires the c65gm compiler and ACME assembler:
|
||||
|
||||
```bash
|
||||
./cm.sh
|
||||
```
|
||||
|
||||
Outputs `main.prg` ready to load on C64 or emulator.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `cardgame.c65` - Main entry point and initialization
|
||||
- `gameloop.c65` - Game loop, interaction, and pile detection
|
||||
- `cardmoves.c65` - Move validation and execution logic
|
||||
- `cardrender.c65` - Card and pile rendering routines
|
||||
- `carddeck.c65` - Deck shuffling and dealing
|
||||
- `mouse.c65` / `joystick.c65` - Input handling
|
||||
- `pointer.c65` - Sprite cursor management
|
||||
- `cardsprites.c65` - Sprite data for cursor
|
||||
|
||||
## License
|
||||
|
||||
See LICENSE file for details.
|
||||
|
|
|
|||
31
cardconsts.c65
Normal file
31
cardconsts.c65
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
#IFNDEF __lib_cardconsts
|
||||
#DEFINE __lib_cardconsts 1
|
||||
|
||||
// Card suits
|
||||
BYTE CONST CARD_SUIT_HEARTS = 0
|
||||
BYTE CONST CARD_SUIT_DIAMONDS = 1
|
||||
BYTE CONST CARD_SUIT_SPADES = 2
|
||||
BYTE CONST CARD_SUIT_CLUBS = 3
|
||||
|
||||
// Card ranks
|
||||
BYTE CONST CARD_ACE = 0
|
||||
BYTE CONST CARD_2 = 1
|
||||
BYTE CONST CARD_3 = 2
|
||||
BYTE CONST CARD_4 = 3
|
||||
BYTE CONST CARD_5 = 4
|
||||
BYTE CONST CARD_6 = 5
|
||||
BYTE CONST CARD_7 = 6
|
||||
BYTE CONST CARD_8 = 7
|
||||
BYTE CONST CARD_9 = 8
|
||||
BYTE CONST CARD_10 = 9
|
||||
BYTE CONST CARD_JACK = 10
|
||||
BYTE CONST CARD_QUEEN = 11
|
||||
BYTE CONST CARD_KING = 12
|
||||
|
||||
// Card representation flags
|
||||
BYTE CONST CARD_FACEDOWN = $80
|
||||
BYTE CONST CARD_MASK = $7F
|
||||
BYTE CONST PILE_END = $FF
|
||||
|
||||
#IFEND
|
||||
289
carddeck.c65
Normal file
289
carddeck.c65
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
|
||||
#IFNDEF __lib_carddeck
|
||||
#DEFINE __lib_carddeck 1
|
||||
|
||||
#INCLUDE "random.c65"
|
||||
|
||||
GOTO __skip_lib_carddeck
|
||||
|
||||
// Validation error codes
|
||||
BYTE CONST VALIDATE_OK = 0
|
||||
BYTE CONST VALIDATE_DUPLICATE = 1
|
||||
BYTE CONST VALIDATE_MISSING = 2
|
||||
BYTE CONST VALIDATE_INVALID = 3
|
||||
|
||||
// Temporary array to track seen cards (52 bytes)
|
||||
LABEL validate_seen
|
||||
ASM
|
||||
!fill 52, 0
|
||||
ENDASM
|
||||
|
||||
// Initialize stock with all 52 cards (face down)
|
||||
// Cards stored as $80-$B3 (card value OR'd with CARD_FACEDOWN)
|
||||
FUNC stock_init
|
||||
WORD ptr @ $fa
|
||||
POINTER ptr -> pile_stock
|
||||
BYTE card
|
||||
|
||||
// Set count to 52
|
||||
POKE ptr[0] , 52
|
||||
|
||||
// Fill with cards 0-51, all face down ($80 = CARD_FACEDOWN)
|
||||
ptr++
|
||||
FOR card = $80 TO $80+51
|
||||
POKE ptr , card
|
||||
ptr++
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
// Shuffle stock pile using Fisher-Yates algorithm
|
||||
FUNC stock_shuffle
|
||||
WORD ptr @ $fa
|
||||
POINTER ptr -> pile_stock
|
||||
BYTE i
|
||||
BYTE j
|
||||
BYTE temp_a
|
||||
BYTE temp_b
|
||||
|
||||
// Fisher-Yates: swap each card with a random card from remaining deck
|
||||
i = 52
|
||||
WHILE i >= 2
|
||||
rand_max(i, j)
|
||||
j++
|
||||
// Swap cards at positions i and j (1-indexed in pile)
|
||||
IF i <> j
|
||||
temp_a = PEEK ptr[i]
|
||||
temp_b = PEEK ptr[j]
|
||||
POKE ptr[i] , temp_b
|
||||
POKE ptr[j] , temp_a
|
||||
ENDIF
|
||||
i--
|
||||
WEND
|
||||
FEND
|
||||
|
||||
// Deal cards from stock to a single tableau pile
|
||||
// num_cards: how many cards to deal
|
||||
// Top card is flipped face up
|
||||
FUNC deal_to_tableau({WORD tab_ptr @ $fc} {BYTE num_cards})
|
||||
WORD stock_ptr @ $fa
|
||||
POINTER stock_ptr -> pile_stock
|
||||
BYTE stock_count
|
||||
BYTE card
|
||||
BYTE i
|
||||
|
||||
// Set tableau count
|
||||
POKE tab_ptr[0] , num_cards
|
||||
|
||||
// Deal cards from stock to tableau
|
||||
stock_count = PEEK stock_ptr[0]
|
||||
FOR i = 1 TO num_cards
|
||||
card = PEEK stock_ptr[stock_count]
|
||||
POKE tab_ptr[i] , card
|
||||
stock_count--
|
||||
NEXT
|
||||
|
||||
// Update stock count
|
||||
POKE stock_ptr[0] , stock_count
|
||||
|
||||
// Flip top card face up (clear CARD_FACEDOWN bit)
|
||||
card = PEEK tab_ptr[num_cards]
|
||||
card = card & CARD_MASK
|
||||
POKE tab_ptr[num_cards] , card
|
||||
FEND
|
||||
|
||||
// Deal cards to all 7 tableau piles (Klondike layout)
|
||||
// Tab0=1, Tab1=2, Tab2=3, Tab3=4, Tab4=5, Tab5=6, Tab6=7 cards
|
||||
// Top card of each tableau is face up
|
||||
FUNC deal_tableaus
|
||||
WORD ptr @ $fc
|
||||
|
||||
POINTER ptr -> pile_tab0
|
||||
deal_to_tableau(ptr, 1)
|
||||
|
||||
POINTER ptr -> pile_tab1
|
||||
deal_to_tableau(ptr, 2)
|
||||
|
||||
POINTER ptr -> pile_tab2
|
||||
deal_to_tableau(ptr, 3)
|
||||
|
||||
POINTER ptr -> pile_tab3
|
||||
deal_to_tableau(ptr, 4)
|
||||
|
||||
POINTER ptr -> pile_tab4
|
||||
deal_to_tableau(ptr, 5)
|
||||
|
||||
POINTER ptr -> pile_tab5
|
||||
deal_to_tableau(ptr, 6)
|
||||
|
||||
POINTER ptr -> pile_tab6
|
||||
deal_to_tableau(ptr, 7)
|
||||
FEND
|
||||
|
||||
// Clear the seen array
|
||||
FUNC validate_clear_seen
|
||||
WORD ptr @ $fa
|
||||
POINTER ptr -> validate_seen
|
||||
BYTE i
|
||||
FOR i = 0 TO 51
|
||||
POKE ptr[i] , 0
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
// Check cards in a pile, mark as seen
|
||||
// Returns 0 if OK, 1 if duplicate found
|
||||
FUNC validate_pile({WORD pile_ptr @ $fa} out:{BYTE result})
|
||||
WORD seen_ptr @ $fc
|
||||
POINTER seen_ptr -> validate_seen
|
||||
|
||||
BYTE count
|
||||
BYTE i
|
||||
BYTE card
|
||||
BYTE card_val
|
||||
BYTE already_seen
|
||||
|
||||
result = VALIDATE_OK
|
||||
count = PEEK pile_ptr[0]
|
||||
|
||||
FOR i = 1 TO count
|
||||
card = PEEK pile_ptr[i]
|
||||
card_val = card & CARD_MASK
|
||||
|
||||
// Check if valid card (0-51)
|
||||
IF card_val >= 52
|
||||
result = VALIDATE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
already_seen = PEEK seen_ptr[card_val]
|
||||
IF already_seen
|
||||
result = VALIDATE_DUPLICATE
|
||||
EXIT
|
||||
ENDIF
|
||||
POKE seen_ptr[card_val] , 1
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
// Validate entire deck across all piles
|
||||
// Returns: 0=OK, 1=duplicate, 2=missing
|
||||
FUNC validate_deck(out:{BYTE result})
|
||||
WORD ptr @ $fa
|
||||
WORD seen_ptr @ $fc
|
||||
BYTE pile_result
|
||||
BYTE i
|
||||
BYTE seen
|
||||
|
||||
// Clear seen array
|
||||
validate_clear_seen()
|
||||
|
||||
result = VALIDATE_OK
|
||||
|
||||
// Check stock
|
||||
POINTER ptr -> pile_stock
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Check waste
|
||||
POINTER ptr -> pile_waste
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Check tableau piles
|
||||
POINTER ptr -> pile_tab0
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_tab1
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_tab2
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_tab3
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_tab4
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_tab5
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_tab6
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Check foundation piles
|
||||
POINTER ptr -> pile_found0
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_found1
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_found2
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POINTER ptr -> pile_found3
|
||||
validate_pile(ptr, pile_result)
|
||||
IF pile_result
|
||||
result = pile_result
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Now check all 52 cards were seen
|
||||
POINTER seen_ptr -> validate_seen
|
||||
FOR i = 0 TO 51
|
||||
seen = PEEK seen_ptr[i]
|
||||
IF seen == 0
|
||||
result = VALIDATE_MISSING
|
||||
EXIT
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
FEND
|
||||
|
||||
LABEL __skip_lib_carddeck
|
||||
|
||||
#IFEND
|
||||
152
cardgame.c65
Normal file
152
cardgame.c65
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#INCLUDE <c64start.c65>
|
||||
#INCLUDE <c64defs.c65>
|
||||
|
||||
//#DEFINE MOUSE_NO_SMOOTHING 1
|
||||
|
||||
// Enable test game setups (comment out for release build)
|
||||
//#DEFINE TEST_GAMES 1
|
||||
|
||||
GOTO start
|
||||
|
||||
ASM
|
||||
*=$3000
|
||||
ENDASM
|
||||
|
||||
#INCLUDE "utils.c65"
|
||||
#INCLUDE "cardconsts.c65"
|
||||
#INCLUDE "piles.c65"
|
||||
#INCLUDE "random.c65"
|
||||
#INCLUDE "carddeck.c65"
|
||||
#INCLUDE "cardrender.c65"
|
||||
//#INCLUDE "cardtests.c65"
|
||||
#INCLUDE "joystick.c65"
|
||||
#INCLUDE "mouse.c65"
|
||||
#INCLUDE "pointer.c65"
|
||||
//#INCLUDE "joysticktests.c65"
|
||||
#IFDEF TEST_GAMES
|
||||
#INCLUDE "testgames.c65"
|
||||
#IFEND
|
||||
#INCLUDE "gameloop.c65"
|
||||
|
||||
|
||||
FUNC main
|
||||
|
||||
|
||||
ASM
|
||||
sei
|
||||
ENDASM
|
||||
|
||||
// Initialize game state
|
||||
#IFNDEF TEST_GAMES
|
||||
// Normal game: random shuffle and deal
|
||||
// Seed RNG with multiple entropy sources for better randomness
|
||||
WORD timer_seed @ $fa
|
||||
BYTE raster @ $d012
|
||||
BYTE extra_entropy
|
||||
BYTE warmup
|
||||
|
||||
// Combine CIA timer with raster position
|
||||
timer_seed = PEEKW $DC04
|
||||
extra_entropy = raster
|
||||
timer_seed = timer_seed ^ extra_entropy
|
||||
rand_seed(timer_seed)
|
||||
|
||||
// Warm up RNG by advancing it based on timer low byte
|
||||
extra_entropy = PEEK $DC04
|
||||
FOR warmup = 0 TO extra_entropy
|
||||
rand(extra_entropy)
|
||||
NEXT
|
||||
|
||||
stock_init()
|
||||
stock_shuffle()
|
||||
deal_tableaus()
|
||||
#IFEND
|
||||
|
||||
BYTE validation_result
|
||||
validate_deck(validation_result)
|
||||
IF validation_result
|
||||
POKE $d020 , color_red
|
||||
ENDIF
|
||||
|
||||
set_vic_bank(0) // $0000 - $3fff
|
||||
set_vic_screenmem(1) // $400
|
||||
set_vic_charmem(4) // $2000
|
||||
set_vic_ecm()
|
||||
|
||||
|
||||
WORD charset_ptr
|
||||
WORD charset_end_ptr
|
||||
POINTER charset_ptr -> card_charset
|
||||
POINTER charset_end_ptr -> card_charset_end
|
||||
mem_copy(charset_ptr, charset_end_ptr, $2000)
|
||||
//mem_copy_range(charset_ptr, $2000, 512)
|
||||
|
||||
fill_mem($d800, $d800+999, color_white) // Fill color mem
|
||||
POKE $d020 , color_white //color_grey
|
||||
POKE $d021 , color_black
|
||||
POKE $d022 , color_red
|
||||
POKE $d023 , color_light_grey
|
||||
POKE $d024 , color_dark_grey
|
||||
|
||||
|
||||
//show_charset()
|
||||
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
|
||||
//render_card($0400, $30, 0) // Ace of Hearts
|
||||
//render_card($0400, $35, 27) // 2 of Spades
|
||||
//render_card($0400, $3a, 15) // 3 of Diamonds
|
||||
|
||||
|
||||
//render_all_cards_test()
|
||||
//render_facedown_test()
|
||||
//render_faceup_stack_test()
|
||||
//render_tableaus_test()
|
||||
//render_midgame_test()
|
||||
//render_card_back($0400, 35) // Test card back at column 35
|
||||
//render_empty_pile($0400, 8*40+35) // Test empty pile below card back
|
||||
//render_foundation_test()
|
||||
//render_waste_test()
|
||||
//render_all_piles_test()
|
||||
|
||||
// Move tests - runs multiple tests, press key between each
|
||||
//test_stock_to_waste_draw1() // Draw 1 card x3 from stock
|
||||
//wait_key()
|
||||
//fill_mem($0400, $0400+999, 0)
|
||||
//test_waste_to_tab_valid() // Move card from waste to tableau
|
||||
//wait_key()
|
||||
//fill_mem($0400, $0400+999, 0)
|
||||
//test_tab_to_found() // Tableau to foundation + auto-flip
|
||||
//wait_key()
|
||||
//fill_mem($0400, $0400+999, 0)
|
||||
//test_tab_to_tab_stack() // Move stack of 3 cards + auto-flip
|
||||
|
||||
// Joystick tests
|
||||
//test_joystick_read() // Test joystick input reading - suit symbols
|
||||
//fill_mem($0400, $0400+999, 0)
|
||||
//test_pointer_sprite() // Test sprite pointer with joystick
|
||||
//fill_mem($0400, $0400+999, 0)
|
||||
//test_pointer_manual() // Test sprite pointer manual movement
|
||||
|
||||
// Mouse test
|
||||
//test_pointer_mouse() // Test sprite pointer with 1351 mouse
|
||||
|
||||
// Start the game!
|
||||
game_init()
|
||||
game_loop() // Never returns
|
||||
|
||||
|
||||
FEND
|
||||
|
||||
|
||||
LABEL start
|
||||
|
||||
|
||||
main()
|
||||
|
||||
|
||||
LABEL card_charset
|
||||
ASM
|
||||
!binary "charpad_cards/charpad_cards7.bin"
|
||||
ENDASM
|
||||
LABEL card_charset_end
|
||||
385
cardmoves.c65
Normal file
385
cardmoves.c65
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
|
||||
#IFNDEF __lib_cardmoves
|
||||
#DEFINE __lib_cardmoves 1
|
||||
|
||||
#INCLUDE "cardconsts.c65"
|
||||
#INCLUDE "piles.c65"
|
||||
#INCLUDE "cardrender.c65"
|
||||
|
||||
GOTO __skip_lib_cardmoves
|
||||
|
||||
// Card colors
|
||||
BYTE CONST COLOR_RED = 0
|
||||
BYTE CONST COLOR_BLACK = 1
|
||||
|
||||
// Move result codes
|
||||
BYTE CONST MOVE_OK = 1
|
||||
BYTE CONST MOVE_INVALID = 0
|
||||
|
||||
// ============================================================================
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
// Get card color (0=red, 1=black)
|
||||
// Cards 0-25 are red (Hearts, Diamonds), 26-51 are black (Spades, Clubs)
|
||||
FUNC card_color({BYTE card_id} out:{BYTE color})
|
||||
IF card_id < 26
|
||||
color = COLOR_RED
|
||||
ELSE
|
||||
color = COLOR_BLACK
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// Get top card from pile (returns card with facedown bit, or PILE_END if empty)
|
||||
// Uses $fa - for source pile operations
|
||||
FUNC pile_top_card({WORD pile_ptr @ $fa} out:{BYTE card})
|
||||
BYTE count
|
||||
count = PEEK pile_ptr[0]
|
||||
IF count == 0
|
||||
card = PILE_END
|
||||
ELSE
|
||||
card = PEEK pile_ptr[count]
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// Remove top card from pile (returns the card, updates count)
|
||||
// Uses $fa - for source pile operations
|
||||
FUNC pile_pop({WORD pile_ptr @ $fa} out:{BYTE card})
|
||||
BYTE count
|
||||
count = PEEK pile_ptr[0]
|
||||
card = PEEK pile_ptr[count]
|
||||
count--
|
||||
POKE pile_ptr[0] , count
|
||||
FEND
|
||||
|
||||
// Add card to top of pile (updates count)
|
||||
// Uses $fc - for destination pile operations
|
||||
FUNC pile_push({WORD pile_ptr @ $fc} {BYTE card})
|
||||
BYTE count
|
||||
count = PEEK pile_ptr[0]
|
||||
count++
|
||||
POKE pile_ptr[count] , card
|
||||
POKE pile_ptr[0] , count
|
||||
FEND
|
||||
|
||||
// Flip top card face-up if it's face-down
|
||||
// Uses $fa - for source pile operations
|
||||
FUNC pile_flip_top({WORD pile_ptr @ $fa})
|
||||
BYTE count
|
||||
BYTE card
|
||||
BYTE is_facedown
|
||||
count = PEEK pile_ptr[0]
|
||||
IF count > 0
|
||||
card = PEEK pile_ptr[count]
|
||||
is_facedown = card & CARD_FACEDOWN
|
||||
IF is_facedown
|
||||
card = card & CARD_MASK
|
||||
POKE pile_ptr[count] , card
|
||||
ENDIF
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// ============================================================================
|
||||
// Validation Functions
|
||||
// ============================================================================
|
||||
|
||||
// Can card go on foundation? (Ace if empty, else same suit +1 rank)
|
||||
// Uses $fc - destination pile
|
||||
FUNC can_place_on_foundation({BYTE card_id} {WORD found_ptr @ $fc} out:{BYTE valid})
|
||||
BYTE card_suit
|
||||
BYTE card_rank
|
||||
BYTE top_card
|
||||
BYTE top_suit
|
||||
BYTE top_rank
|
||||
BYTE count
|
||||
BYTE expected_rank
|
||||
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
count = PEEK found_ptr[0]
|
||||
|
||||
IF count == 0
|
||||
// Empty foundation: only Ace allowed
|
||||
IF card_rank == CARD_ACE
|
||||
valid = 1
|
||||
ELSE
|
||||
valid = 0
|
||||
ENDIF
|
||||
ELSE
|
||||
// Must be same suit, one rank higher
|
||||
top_card = PEEK found_ptr[count]
|
||||
top_card = top_card & CARD_MASK
|
||||
card_id_to_suit_rank(top_card, top_suit, top_rank)
|
||||
expected_rank = top_rank + 1
|
||||
valid = 0
|
||||
IF card_suit == top_suit
|
||||
IF card_rank == expected_rank
|
||||
valid = 1
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// Can card go on tableau? (King if empty, else opposite color -1 rank)
|
||||
// Uses $fc - destination pile
|
||||
FUNC can_place_on_tableau({BYTE card_id} {WORD tab_ptr @ $fc} out:{BYTE valid})
|
||||
BYTE card_rank
|
||||
BYTE card_col
|
||||
BYTE top_card
|
||||
BYTE top_rank
|
||||
BYTE top_col
|
||||
BYTE count
|
||||
BYTE card_suit
|
||||
BYTE top_suit
|
||||
BYTE expected_rank
|
||||
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
card_color(card_id, card_col)
|
||||
count = PEEK tab_ptr[0]
|
||||
|
||||
IF count == 0
|
||||
// Empty tableau: only King allowed
|
||||
IF card_rank == CARD_KING
|
||||
valid = 1
|
||||
ELSE
|
||||
valid = 0
|
||||
ENDIF
|
||||
ELSE
|
||||
// Must be opposite color, one rank lower
|
||||
top_card = PEEK tab_ptr[count]
|
||||
top_card = top_card & CARD_MASK
|
||||
card_id_to_suit_rank(top_card, top_suit, top_rank)
|
||||
card_color(top_card, top_col)
|
||||
expected_rank = card_rank + 1
|
||||
valid = 0
|
||||
IF card_col <> top_col
|
||||
IF expected_rank == top_rank
|
||||
valid = 1
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// ============================================================================
|
||||
// Move Functions
|
||||
// ============================================================================
|
||||
|
||||
// Stock to Waste: Draw cards from stock to waste
|
||||
// draw_count = 1 or 3 depending on game variant
|
||||
// Stock is source @ $fa, Waste is destination @ $fc
|
||||
FUNC move_stock_to_waste({BYTE draw_count} out:{BYTE success})
|
||||
WORD stock_ptr @ $fa
|
||||
WORD waste_ptr @ $fc
|
||||
POINTER stock_ptr -> pile_stock
|
||||
POINTER waste_ptr -> pile_waste
|
||||
BYTE stock_count
|
||||
BYTE card
|
||||
BYTE i
|
||||
|
||||
stock_count = PEEK stock_ptr[0]
|
||||
IF stock_count == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Limit draw_count to available cards
|
||||
IF draw_count > stock_count
|
||||
draw_count = stock_count
|
||||
ENDIF
|
||||
|
||||
FOR i = 1 TO draw_count
|
||||
pile_pop(stock_ptr, card)
|
||||
card = card & CARD_MASK // Flip face-up
|
||||
pile_push(waste_ptr, card)
|
||||
NEXT
|
||||
|
||||
success = MOVE_OK
|
||||
FEND
|
||||
|
||||
// Reset Stock: Flip waste back to stock (all face-down)
|
||||
// Waste is source @ $fa, Stock is destination @ $fc
|
||||
FUNC move_reset_stock(out:{BYTE success})
|
||||
WORD waste_ptr @ $fa
|
||||
WORD stock_ptr @ $fc
|
||||
POINTER waste_ptr -> pile_waste
|
||||
POINTER stock_ptr -> pile_stock
|
||||
BYTE waste_count
|
||||
BYTE card
|
||||
BYTE i
|
||||
|
||||
waste_count = PEEK waste_ptr[0]
|
||||
IF waste_count == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Move all waste cards to stock (reversed, face-down)
|
||||
FOR i = 1 TO waste_count
|
||||
pile_pop(waste_ptr, card)
|
||||
card = card | CARD_FACEDOWN
|
||||
pile_push(stock_ptr, card)
|
||||
NEXT
|
||||
|
||||
success = MOVE_OK
|
||||
FEND
|
||||
|
||||
// Waste to Tableau
|
||||
FUNC move_waste_to_tab({WORD tab_ptr @ $fc} out:{BYTE success})
|
||||
WORD waste_ptr @ $fa
|
||||
POINTER waste_ptr -> pile_waste
|
||||
BYTE card
|
||||
BYTE valid
|
||||
|
||||
pile_top_card(waste_ptr, card)
|
||||
IF card == PILE_END
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
card = card & CARD_MASK
|
||||
can_place_on_tableau(card, tab_ptr, valid)
|
||||
IF valid == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
pile_pop(waste_ptr, card)
|
||||
pile_push(tab_ptr, card)
|
||||
success = MOVE_OK
|
||||
FEND
|
||||
|
||||
// Waste to Foundation
|
||||
FUNC move_waste_to_found({WORD found_ptr @ $fc} out:{BYTE success})
|
||||
WORD waste_ptr @ $fa
|
||||
POINTER waste_ptr -> pile_waste
|
||||
BYTE card
|
||||
BYTE valid
|
||||
|
||||
pile_top_card(waste_ptr, card)
|
||||
IF card == PILE_END
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
card = card & CARD_MASK
|
||||
can_place_on_foundation(card, found_ptr, valid)
|
||||
IF valid == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
pile_pop(waste_ptr, card)
|
||||
pile_push(found_ptr, card)
|
||||
success = MOVE_OK
|
||||
FEND
|
||||
|
||||
// Tableau to Foundation (top card only)
|
||||
FUNC move_tab_to_found({WORD tab_ptr @ $fa} {WORD found_ptr @ $fc} out:{BYTE success})
|
||||
BYTE card
|
||||
BYTE valid
|
||||
BYTE is_facedown
|
||||
|
||||
pile_top_card(tab_ptr, card)
|
||||
IF card == PILE_END
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Must be face-up
|
||||
is_facedown = card & CARD_FACEDOWN
|
||||
IF is_facedown
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
card = card & CARD_MASK
|
||||
can_place_on_foundation(card, found_ptr, valid)
|
||||
IF valid == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
pile_pop(tab_ptr, card)
|
||||
pile_push(found_ptr, card)
|
||||
pile_flip_top(tab_ptr) // Reveal next card
|
||||
success = MOVE_OK
|
||||
FEND
|
||||
|
||||
// Tableau to Tableau: Move card_count cards from src to dst
|
||||
FUNC move_tab_to_tab({WORD src_ptr @ $fa} {WORD dst_ptr @ $fc} {BYTE card_count} out:{BYTE success})
|
||||
BYTE src_count
|
||||
BYTE start_idx
|
||||
BYTE bottom_card
|
||||
BYTE valid
|
||||
BYTE i
|
||||
BYTE card
|
||||
BYTE is_facedown
|
||||
|
||||
src_count = PEEK src_ptr[0]
|
||||
IF card_count > src_count
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
IF card_count == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Find the bottom card of the stack to move
|
||||
start_idx = src_count - card_count
|
||||
start_idx = start_idx + 1
|
||||
bottom_card = PEEK src_ptr[start_idx]
|
||||
|
||||
// Bottom card must be face-up
|
||||
is_facedown = bottom_card & CARD_FACEDOWN
|
||||
IF is_facedown
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
bottom_card = bottom_card & CARD_MASK
|
||||
can_place_on_tableau(bottom_card, dst_ptr, valid)
|
||||
IF valid == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Move cards (preserve order)
|
||||
FOR i = start_idx TO src_count
|
||||
card = PEEK src_ptr[i]
|
||||
pile_push(dst_ptr, card)
|
||||
NEXT
|
||||
|
||||
// Update source count
|
||||
src_count = start_idx - 1
|
||||
POKE src_ptr[0] , src_count
|
||||
|
||||
pile_flip_top(src_ptr) // Reveal next card
|
||||
success = MOVE_OK
|
||||
FEND
|
||||
|
||||
// Foundation to Tableau (optional rule - some variants allow this)
|
||||
FUNC move_found_to_tab({WORD found_ptr @ $fa} {WORD tab_ptr @ $fc} out:{BYTE success})
|
||||
BYTE card
|
||||
BYTE valid
|
||||
|
||||
pile_top_card(found_ptr, card)
|
||||
IF card == PILE_END
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
card = card & CARD_MASK
|
||||
can_place_on_tableau(card, tab_ptr, valid)
|
||||
IF valid == 0
|
||||
success = MOVE_INVALID
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
pile_pop(found_ptr, card)
|
||||
pile_push(tab_ptr, card)
|
||||
success = MOVE_OK
|
||||
FEND
|
||||
|
||||
LABEL __skip_lib_cardmoves
|
||||
|
||||
#IFEND
|
||||
963
cardrender.c65
Normal file
963
cardrender.c65
Normal file
|
|
@ -0,0 +1,963 @@
|
|||
|
||||
#IFNDEF __lib_cardrender
|
||||
#DEFINE __lib_cardrender 1
|
||||
|
||||
#INCLUDE "cardconsts.c65"
|
||||
|
||||
GOTO __skip_lib_cardrender
|
||||
|
||||
LABEL card_charcode_map
|
||||
ASM
|
||||
!8 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
||||
ENDASM
|
||||
|
||||
LABEL card_suit_charcode_map
|
||||
ASM
|
||||
!8 $50, $51, $0e, $0f
|
||||
ENDASM
|
||||
|
||||
LABEL suit_graphic_hearts
|
||||
ASM
|
||||
!8 $1d+64, $1e+64, $1f+64, $2d+64, $2e+64, $2f+64, 0+64, $3e+64, 0+64
|
||||
ENDASM
|
||||
|
||||
LABEL suit_graphic_diamonds
|
||||
ASM
|
||||
!8 0+64, $1b+64, 0+64, $2a+64, $2b+64, $2c+64, 0+64, $3b+64, 0+64
|
||||
ENDASM
|
||||
|
||||
LABEL suit_graphic_spades
|
||||
ASM
|
||||
!8 0, $18, $19, $27, $28, $29, $37, $38, $39
|
||||
ENDASM
|
||||
|
||||
LABEL suit_graphic_clubs
|
||||
ASM
|
||||
!8 0, $15, 0, $24, $25, $26, 0, $35, $36
|
||||
ENDASM
|
||||
|
||||
// Convert card ID (0-51) to suit and rank
|
||||
// 0-12: Hearts, 13-25: Diamonds, 26-38: Spades, 39-51: Clubs
|
||||
FUNC card_id_to_suit_rank({BYTE card_id} out:{BYTE card_suit} out:{BYTE card_rank})
|
||||
card_rank = card_id
|
||||
card_suit = 0
|
||||
|
||||
IF card_rank >= 13
|
||||
card_rank = card_rank - 13
|
||||
card_suit = 1
|
||||
ENDIF
|
||||
|
||||
IF card_rank >= 13
|
||||
card_rank = card_rank - 13
|
||||
card_suit = 2
|
||||
ENDIF
|
||||
|
||||
IF card_rank >= 13
|
||||
card_rank = card_rank - 13
|
||||
card_suit = 3
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// ZP usage $fa-$fd
|
||||
// Render full card using card_id (0-51)
|
||||
FUNC render_card({WORD screen_address} {WORD offset} {BYTE card_id})
|
||||
BYTE card_suit
|
||||
BYTE card_rank
|
||||
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
|
||||
// Get rank charcode
|
||||
WORD card_charcode_map_ptr @ $fa
|
||||
POINTER card_charcode_map_ptr -> card_charcode_map
|
||||
|
||||
BYTE card_rank_charcode
|
||||
card_rank_charcode = PEEK card_charcode_map_ptr[card_rank]
|
||||
|
||||
// Adjust color for red suit
|
||||
IF card_suit < CARD_SUIT_SPADES
|
||||
card_rank_charcode = card_rank_charcode + 64 //red color for hearts and diamonds
|
||||
ENDIF
|
||||
|
||||
// Get suit charcode
|
||||
WORD card_suit_charcode_map_ptr @ $fa
|
||||
POINTER card_suit_charcode_map_ptr -> card_suit_charcode_map
|
||||
|
||||
BYTE suit_charcode
|
||||
suit_charcode = PEEK card_suit_charcode_map_ptr[card_suit]
|
||||
|
||||
|
||||
WORD p2 @ $fa
|
||||
SWITCH card_suit
|
||||
CASE CARD_SUIT_HEARTS
|
||||
POINTER p2 -> suit_graphic_hearts
|
||||
CASE CARD_SUIT_DIAMONDS
|
||||
POINTER p2 -> suit_graphic_diamonds
|
||||
CASE CARD_SUIT_SPADES
|
||||
POINTER p2 -> suit_graphic_spades
|
||||
CASE CARD_SUIT_CLUBS
|
||||
POINTER p2 -> suit_graphic_clubs
|
||||
ENDSWITCH
|
||||
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
|
||||
POKE p[0] , 224 // top left corner
|
||||
POKE p[1] , 225
|
||||
POKE p[2] , 225
|
||||
POKE p[3] , 225
|
||||
POKE p[4] , 226
|
||||
|
||||
p = p + 40
|
||||
|
||||
POKE p[0] , 240
|
||||
POKE p[1] , card_rank_charcode
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , suit_charcode
|
||||
POKE p[4] , 243
|
||||
|
||||
p = p + 40
|
||||
|
||||
|
||||
// Here we draw the bigger suit graphic in the middle of the card
|
||||
BYTE g
|
||||
POKE p[0] , 240
|
||||
g = PEEK p2[0]
|
||||
POKE p[1] , g
|
||||
g = PEEK p2[1]
|
||||
POKE p[2] , g
|
||||
g = PEEK p2[2]
|
||||
POKE p[3] , g
|
||||
POKE p[4] , 243
|
||||
|
||||
p = p + 40
|
||||
|
||||
POKE p[0] , 240
|
||||
g = PEEK p2[3]
|
||||
POKE p[1] , g
|
||||
g = PEEK p2[4]
|
||||
POKE p[2] , g
|
||||
g = PEEK p2[5]
|
||||
POKE p[3] , g
|
||||
POKE p[4] , 243
|
||||
|
||||
p = p + 40
|
||||
|
||||
POKE p[0] , 240
|
||||
g = PEEK p2[6]
|
||||
POKE p[1] , g
|
||||
g = PEEK p2[7]
|
||||
POKE p[2] , g
|
||||
g = PEEK p2[8]
|
||||
POKE p[3] , g
|
||||
POKE p[4] , 243
|
||||
|
||||
p = p + 40
|
||||
|
||||
POKE p[0] , 240
|
||||
POKE p[1] , suit_charcode
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , card_rank_charcode
|
||||
POKE p[4] , 243
|
||||
|
||||
p = p + 40
|
||||
|
||||
POKE p[0] , 241
|
||||
POKE p[1] , 227
|
||||
POKE p[2] , 227
|
||||
POKE p[3] , 227
|
||||
POKE p[4] , 242
|
||||
|
||||
|
||||
FEND
|
||||
|
||||
// Render face-down card stack and top border of visible card
|
||||
// Returns number of rows drawn (for offset calculation)
|
||||
// n=0: just draws normal top border ($e0)
|
||||
// n=1-2: draws modified top border showing stacked cards
|
||||
// n=3+: draws multiple rows ending with top border
|
||||
FUNC render_facedown_stack({WORD screen_address} {WORD offset} {BYTE num_facedown} out:{BYTE rows_drawn})
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
BYTE middle_rows
|
||||
BYTE top_type
|
||||
|
||||
IF num_facedown == 0
|
||||
// Normal top border
|
||||
POKE p[0] , $e0
|
||||
POKE p[1] , $e1
|
||||
POKE p[2] , $e1
|
||||
POKE p[3] , $e1
|
||||
POKE p[4] , $e2
|
||||
rows_drawn = 1
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
IF num_facedown == 1
|
||||
// 1 face-down: $fa row
|
||||
POKE p[0] , $fa
|
||||
POKE p[1] , $da
|
||||
POKE p[2] , $da
|
||||
POKE p[3] , $da
|
||||
POKE p[4] , $fc
|
||||
rows_drawn = 1
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
IF num_facedown == 2
|
||||
// 2 face-down: $fd row
|
||||
POKE p[0] , $fd
|
||||
POKE p[1] , $dc
|
||||
POKE p[2] , $dc
|
||||
POKE p[3] , $dc
|
||||
POKE p[4] , $ff
|
||||
rows_drawn = 1
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// 3+ face-down cards
|
||||
// Top row based on (num_facedown mod 3)
|
||||
top_type = num_facedown
|
||||
WHILE top_type >= 3
|
||||
top_type = top_type - 3
|
||||
WEND
|
||||
|
||||
// Draw top row
|
||||
IF top_type == 0
|
||||
POKE p[0] , $e0
|
||||
POKE p[1] , $e1
|
||||
POKE p[2] , $e1
|
||||
POKE p[3] , $e1
|
||||
POKE p[4] , $e2
|
||||
ENDIF
|
||||
IF top_type == 1
|
||||
POKE p[0] , $fa
|
||||
POKE p[1] , $da
|
||||
POKE p[2] , $da
|
||||
POKE p[3] , $da
|
||||
POKE p[4] , $fc
|
||||
ENDIF
|
||||
IF top_type == 2
|
||||
POKE p[0] , $fd
|
||||
POKE p[1] , $dc
|
||||
POKE p[2] , $dc
|
||||
POKE p[3] , $dc
|
||||
POKE p[4] , $ff
|
||||
ENDIF
|
||||
p = p + 40
|
||||
rows_drawn = 1
|
||||
|
||||
// Calculate middle $d7 rows: (num_facedown - 3) / 3
|
||||
middle_rows = num_facedown - 3
|
||||
WHILE middle_rows >= 3
|
||||
middle_rows = middle_rows - 3
|
||||
// Draw $d7 row
|
||||
POKE p[0] , $d7
|
||||
POKE p[1] , $dc
|
||||
POKE p[2] , $dc
|
||||
POKE p[3] , $dc
|
||||
POKE p[4] , $f4
|
||||
p = p + 40
|
||||
rows_drawn++
|
||||
WEND
|
||||
|
||||
// Draw card top row: $d7 for all n>=3
|
||||
POKE p[0] , $d7
|
||||
POKE p[1] , $dc
|
||||
POKE p[2] , $dc
|
||||
POKE p[3] , $dc
|
||||
POKE p[4] , $f4
|
||||
rows_drawn++
|
||||
FEND
|
||||
|
||||
// Draw connecting border between stacked face-up cards
|
||||
// Used before each face-up card after the first
|
||||
FUNC render_connecting_border({WORD screen_address} {WORD offset})
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
|
||||
POKE p[0] , $d4
|
||||
POKE p[1] , $e1
|
||||
POKE p[2] , $e1
|
||||
POKE p[3] , $e1
|
||||
POKE p[4] , $d6
|
||||
FEND
|
||||
|
||||
// Render just the rank/suit row (no top border)
|
||||
// Used for face-up cards in a stack where top border is already drawn
|
||||
FUNC render_card_body_partial({WORD screen_address} {WORD offset} {BYTE card_id})
|
||||
BYTE card_suit
|
||||
BYTE card_rank
|
||||
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
|
||||
// Get rank charcode
|
||||
WORD card_charcode_map_ptr @ $fa
|
||||
POINTER card_charcode_map_ptr -> card_charcode_map
|
||||
|
||||
BYTE card_rank_charcode
|
||||
card_rank_charcode = PEEK card_charcode_map_ptr[card_rank]
|
||||
|
||||
// Adjust color for red suit
|
||||
IF card_suit < CARD_SUIT_SPADES
|
||||
card_rank_charcode = card_rank_charcode + 64
|
||||
ENDIF
|
||||
|
||||
// Get suit charcode
|
||||
WORD card_suit_charcode_map_ptr @ $fa
|
||||
POINTER card_suit_charcode_map_ptr -> card_suit_charcode_map
|
||||
|
||||
BYTE suit_charcode
|
||||
suit_charcode = PEEK card_suit_charcode_map_ptr[card_suit]
|
||||
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
|
||||
// Rank/suit row
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , card_rank_charcode
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , suit_charcode
|
||||
POKE p[4] , $f3
|
||||
FEND
|
||||
|
||||
// Render card body without top border (rank/suit + middle + bottom)
|
||||
// Used for the last face-up card in a stack
|
||||
FUNC render_card_body_full({WORD screen_address} {WORD offset} {BYTE card_id})
|
||||
BYTE card_suit
|
||||
BYTE card_rank
|
||||
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
|
||||
// Get rank charcode
|
||||
WORD card_charcode_map_ptr @ $fa
|
||||
POINTER card_charcode_map_ptr -> card_charcode_map
|
||||
|
||||
BYTE card_rank_charcode
|
||||
card_rank_charcode = PEEK card_charcode_map_ptr[card_rank]
|
||||
|
||||
// Adjust color for red suit
|
||||
IF card_suit < CARD_SUIT_SPADES
|
||||
card_rank_charcode = card_rank_charcode + 64
|
||||
ENDIF
|
||||
|
||||
// Get suit charcode
|
||||
WORD card_suit_charcode_map_ptr @ $fa
|
||||
POINTER card_suit_charcode_map_ptr -> card_suit_charcode_map
|
||||
|
||||
BYTE suit_charcode
|
||||
suit_charcode = PEEK card_suit_charcode_map_ptr[card_suit]
|
||||
|
||||
// Get suit graphic pointer
|
||||
WORD p2 @ $fa
|
||||
SWITCH card_suit
|
||||
CASE CARD_SUIT_HEARTS
|
||||
POINTER p2 -> suit_graphic_hearts
|
||||
CASE CARD_SUIT_DIAMONDS
|
||||
POINTER p2 -> suit_graphic_diamonds
|
||||
CASE CARD_SUIT_SPADES
|
||||
POINTER p2 -> suit_graphic_spades
|
||||
CASE CARD_SUIT_CLUBS
|
||||
POINTER p2 -> suit_graphic_clubs
|
||||
ENDSWITCH
|
||||
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
WORD CONST SCREEN_MEM_END = $0400+999
|
||||
|
||||
// Bounds check: skip last 2 rows if they'd overflow screen (compact mode)
|
||||
IF p > SCREEN_MEM_END-4
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Row 0: Rank/suit row
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , card_rank_charcode
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , suit_charcode
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Bounds check: skip last 2 rows if they'd overflow screen (compact mode)
|
||||
IF p > SCREEN_MEM_END-4
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Rows 1-3: Suit graphic
|
||||
BYTE g
|
||||
POKE p[0] , $f0
|
||||
g = PEEK p2[0]
|
||||
POKE p[1] , g
|
||||
g = PEEK p2[1]
|
||||
POKE p[2] , g
|
||||
g = PEEK p2[2]
|
||||
POKE p[3] , g
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Bounds check: skip last 2 rows if they'd overflow screen (compact mode)
|
||||
IF p > SCREEN_MEM_END-4
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POKE p[0] , $f0
|
||||
g = PEEK p2[3]
|
||||
POKE p[1] , g
|
||||
g = PEEK p2[4]
|
||||
POKE p[2] , g
|
||||
g = PEEK p2[5]
|
||||
POKE p[3] , g
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Bounds check: skip last 2 rows if they'd overflow screen (compact mode)
|
||||
IF p > SCREEN_MEM_END-4
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
POKE p[0] , $f0
|
||||
g = PEEK p2[6]
|
||||
POKE p[1] , g
|
||||
g = PEEK p2[7]
|
||||
POKE p[2] , g
|
||||
g = PEEK p2[8]
|
||||
POKE p[3] , g
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Bounds check: skip last 2 rows if they'd overflow screen (compact mode)
|
||||
IF p > SCREEN_MEM_END-4
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Row 4: Bottom rank/suit
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , suit_charcode
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , card_rank_charcode
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Bounds check: skip bottom border if it'd overflow (K-2 case)
|
||||
IF p > SCREEN_MEM_END-4
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Row 5: Bottom border
|
||||
POKE p[0] , $f1
|
||||
POKE p[1] , $e3
|
||||
POKE p[2] , $e3
|
||||
POKE p[3] , $e3
|
||||
POKE p[4] , $f2
|
||||
FEND
|
||||
|
||||
// Render a complete tableau pile from pile data
|
||||
// Handles face-down stack and all face-up cards
|
||||
FUNC render_tableau_pile({WORD screen_address} {WORD offset} {WORD pile_ptr @ $fe})
|
||||
BYTE count
|
||||
BYTE facedown_count
|
||||
BYTE faceup_count
|
||||
BYTE faceup_index
|
||||
BYTE card
|
||||
BYTE card_id
|
||||
BYTE i
|
||||
BYTE rows
|
||||
WORD pos
|
||||
BYTE use_compact
|
||||
|
||||
count = PEEK pile_ptr[0]
|
||||
|
||||
// Empty pile - nothing to render
|
||||
IF count == 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Count face-down cards
|
||||
facedown_count = 0
|
||||
BYTE is_facedown
|
||||
FOR i = 1 TO count
|
||||
card = PEEK pile_ptr[i]
|
||||
is_facedown = card & CARD_FACEDOWN
|
||||
IF is_facedown
|
||||
facedown_count++
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
faceup_count = count - facedown_count
|
||||
|
||||
// Calculate if we need compact mode (total rows > 17)
|
||||
BYTE facedown_rows
|
||||
BYTE faceup_rows_normal
|
||||
BYTE total_rows
|
||||
BYTE temp_count
|
||||
|
||||
// Calculate facedown rows
|
||||
IF facedown_count == 0
|
||||
facedown_rows = 0
|
||||
ELSE
|
||||
IF facedown_count <= 2
|
||||
facedown_rows = 1
|
||||
ELSE
|
||||
facedown_rows = 2
|
||||
temp_count = facedown_count - 3
|
||||
WHILE temp_count >= 3
|
||||
temp_count = temp_count - 3
|
||||
facedown_rows++
|
||||
WEND
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Calculate faceup rows (normal mode)
|
||||
IF faceup_count == 0
|
||||
faceup_rows_normal = 0
|
||||
ELSE
|
||||
IF faceup_count == 1
|
||||
faceup_rows_normal = 6
|
||||
ELSE
|
||||
// Normal mode: 4 + 2*N rows
|
||||
faceup_rows_normal = faceup_count + faceup_count
|
||||
faceup_rows_normal = faceup_rows_normal + 4
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Check if we need compact mode (screen rows 8-24 = 17 rows available)
|
||||
total_rows = facedown_rows + faceup_rows_normal
|
||||
use_compact = 0
|
||||
IF total_rows > 17
|
||||
use_compact = 1
|
||||
ENDIF
|
||||
|
||||
// Draw face-down stack + first face-up top border
|
||||
pos = offset
|
||||
render_facedown_stack(screen_address, pos, facedown_count, rows)
|
||||
FOR i = 1 TO rows
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
|
||||
// If no face-up cards, we're done (shouldn't happen in valid game)
|
||||
IF faceup_count == 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Render face-up cards
|
||||
faceup_index = 0
|
||||
#PRAGMA _P_USE_LONG_JUMP 1
|
||||
FOR i = 1 TO count
|
||||
#PRAGMA _P_USE_LONG_JUMP 0
|
||||
card = PEEK pile_ptr[i]
|
||||
|
||||
// Skip face-down cards
|
||||
is_facedown = card & CARD_FACEDOWN
|
||||
IF is_facedown
|
||||
// do nothing
|
||||
ELSE
|
||||
faceup_index++
|
||||
card_id = card & CARD_MASK
|
||||
|
||||
IF faceup_index == 1
|
||||
// First face-up: top border already drawn, just body
|
||||
IF faceup_count == 1
|
||||
// Only one face-up card - render full
|
||||
render_card_body_full(screen_address, pos, card_id)
|
||||
ELSE
|
||||
// More cards follow - render partial
|
||||
render_card_body_partial(screen_address, pos, card_id)
|
||||
pos = pos + 40
|
||||
ENDIF
|
||||
ELSE
|
||||
// Subsequent face-up cards
|
||||
IF use_compact == 0
|
||||
// Normal mode: draw connecting border
|
||||
render_connecting_border(screen_address, pos)
|
||||
pos = pos + 40
|
||||
ENDIF
|
||||
|
||||
IF faceup_index == faceup_count
|
||||
// Last face-up card - render full
|
||||
render_card_body_full(screen_address, pos, card_id)
|
||||
ELSE
|
||||
// More cards follow - render partial
|
||||
render_card_body_partial(screen_address, pos, card_id)
|
||||
pos = pos + 40
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
// Render partial card (top 2 rows only) for stacked display
|
||||
// Used for face-up cards underneath the top card
|
||||
FUNC render_card_partial({WORD screen_address} {WORD offset} {BYTE card_id})
|
||||
BYTE card_suit
|
||||
BYTE card_rank
|
||||
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
|
||||
// Get rank charcode
|
||||
WORD card_charcode_map_ptr @ $fa
|
||||
POINTER card_charcode_map_ptr -> card_charcode_map
|
||||
|
||||
BYTE card_rank_charcode
|
||||
card_rank_charcode = PEEK card_charcode_map_ptr[card_rank]
|
||||
|
||||
// Adjust color for red suit
|
||||
IF card_suit < CARD_SUIT_SPADES
|
||||
card_rank_charcode = card_rank_charcode + 64
|
||||
ENDIF
|
||||
|
||||
// Get suit charcode
|
||||
WORD card_suit_charcode_map_ptr @ $fa
|
||||
POINTER card_suit_charcode_map_ptr -> card_suit_charcode_map
|
||||
|
||||
BYTE suit_charcode
|
||||
suit_charcode = PEEK card_suit_charcode_map_ptr[card_suit]
|
||||
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
|
||||
// Row 0: top border
|
||||
POKE p[0] , 224
|
||||
POKE p[1] , 225
|
||||
POKE p[2] , 225
|
||||
POKE p[3] , 225
|
||||
POKE p[4] , 226
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 1: rank and suit
|
||||
POKE p[0] , 240
|
||||
POKE p[1] , card_rank_charcode
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , suit_charcode
|
||||
POKE p[4] , 243
|
||||
FEND
|
||||
|
||||
// Render back side of a card (face-down full card)
|
||||
// Same border as face card, middle filled with $52
|
||||
FUNC render_card_back({WORD screen_address} {WORD offset})
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
|
||||
// Row 0: top border
|
||||
POKE p[0] , $e0
|
||||
POKE p[1] , $e1
|
||||
POKE p[2] , $e1
|
||||
POKE p[3] , $e1
|
||||
POKE p[4] , $e2
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 1: side borders + $52 fill
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , $52
|
||||
POKE p[2] , $52
|
||||
POKE p[3] , $52
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 2: side borders + $52 fill
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , $52
|
||||
POKE p[2] , $52
|
||||
POKE p[3] , $52
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 3: side borders + $52 fill
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , $52
|
||||
POKE p[2] , $52
|
||||
POKE p[3] , $52
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 4: side borders + $52 fill
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , $52
|
||||
POKE p[2] , $52
|
||||
POKE p[3] , $52
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 5: side borders + $52 fill
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , $52
|
||||
POKE p[2] , $52
|
||||
POKE p[3] , $52
|
||||
POKE p[4] , $f3
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 6: bottom border
|
||||
POKE p[0] , $f1
|
||||
POKE p[1] , $e3
|
||||
POKE p[2] , $e3
|
||||
POKE p[3] , $e3
|
||||
POKE p[4] , $f2
|
||||
FEND
|
||||
|
||||
// Render empty pile placeholder
|
||||
// Same border shape but -$80 for light pink color, interior filled with 0
|
||||
FUNC render_empty_pile({WORD screen_address} {WORD offset})
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
|
||||
// Row 0: top border (pink)
|
||||
POKE p[0] , $60
|
||||
POKE p[1] , $61
|
||||
POKE p[2] , $61
|
||||
POKE p[3] , $61
|
||||
POKE p[4] , $62
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 1: side borders + empty fill
|
||||
POKE p[0] , $70
|
||||
POKE p[1] , 0
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , 0
|
||||
POKE p[4] , $73
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 2: side borders + empty fill
|
||||
POKE p[0] , $70
|
||||
POKE p[1] , 0
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , 0
|
||||
POKE p[4] , $73
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 3: side borders + empty fill
|
||||
POKE p[0] , $70
|
||||
POKE p[1] , 0
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , 0
|
||||
POKE p[4] , $73
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 4: side borders + empty fill
|
||||
POKE p[0] , $70
|
||||
POKE p[1] , 0
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , 0
|
||||
POKE p[4] , $73
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 5: side borders + empty fill
|
||||
POKE p[0] , $70
|
||||
POKE p[1] , 0
|
||||
POKE p[2] , 0
|
||||
POKE p[3] , 0
|
||||
POKE p[4] , $73
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 6: bottom border (pink)
|
||||
POKE p[0] , $71
|
||||
POKE p[1] , $63
|
||||
POKE p[2] , $63
|
||||
POKE p[3] , $63
|
||||
POKE p[4] , $72
|
||||
FEND
|
||||
|
||||
// Render foundation pile - shows top card or empty placeholder
|
||||
FUNC render_foundation_pile({WORD screen_address} {WORD offset} {WORD pile_ptr @ $fe})
|
||||
BYTE count
|
||||
BYTE card
|
||||
BYTE card_id
|
||||
|
||||
count = PEEK pile_ptr[0]
|
||||
|
||||
IF count == 0
|
||||
render_empty_pile(screen_address, offset)
|
||||
ELSE
|
||||
card = PEEK pile_ptr[count]
|
||||
card_id = card & CARD_MASK
|
||||
render_card(screen_address, offset, card_id)
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// Render stock pile - shows card back or empty placeholder
|
||||
FUNC render_stock_pile({WORD screen_address} {WORD offset} {WORD pile_ptr @ $fe})
|
||||
BYTE count
|
||||
|
||||
count = PEEK pile_ptr[0]
|
||||
|
||||
IF count == 0
|
||||
render_empty_pile(screen_address, offset)
|
||||
ELSE
|
||||
render_card_back(screen_address, offset)
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// Render left edge of card (2 columns) for fanned display
|
||||
FUNC render_card_left_edge({WORD screen_address} {WORD offset} {BYTE card_id})
|
||||
BYTE card_suit
|
||||
BYTE card_rank
|
||||
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
|
||||
// Get rank charcode
|
||||
WORD card_charcode_map_ptr @ $fa
|
||||
POINTER card_charcode_map_ptr -> card_charcode_map
|
||||
|
||||
BYTE card_rank_charcode
|
||||
card_rank_charcode = PEEK card_charcode_map_ptr[card_rank]
|
||||
|
||||
// Adjust color for red suit
|
||||
IF card_suit < CARD_SUIT_SPADES
|
||||
card_rank_charcode = card_rank_charcode + 64
|
||||
ENDIF
|
||||
|
||||
// Get suit charcode
|
||||
WORD card_suit_charcode_map_ptr @ $fa
|
||||
POINTER card_suit_charcode_map_ptr -> card_suit_charcode_map
|
||||
|
||||
BYTE suit_charcode
|
||||
suit_charcode = PEEK card_suit_charcode_map_ptr[card_suit]
|
||||
|
||||
// Get suit graphic pointer
|
||||
WORD p2 @ $fa
|
||||
SWITCH card_suit
|
||||
CASE CARD_SUIT_HEARTS
|
||||
POINTER p2 -> suit_graphic_hearts
|
||||
CASE CARD_SUIT_DIAMONDS
|
||||
POINTER p2 -> suit_graphic_diamonds
|
||||
CASE CARD_SUIT_SPADES
|
||||
POINTER p2 -> suit_graphic_spades
|
||||
CASE CARD_SUIT_CLUBS
|
||||
POINTER p2 -> suit_graphic_clubs
|
||||
ENDSWITCH
|
||||
|
||||
WORD p @ $fc
|
||||
p = screen_address + offset
|
||||
|
||||
BYTE g
|
||||
|
||||
// Row 0: top border (2 cols)
|
||||
POKE p[0] , $e0
|
||||
POKE p[1] , $e1
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 1: left border + rank
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , card_rank_charcode
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 2: left border + suit graphic
|
||||
POKE p[0] , $f0
|
||||
g = PEEK p2[0]
|
||||
POKE p[1] , g
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 3: left border + suit graphic
|
||||
POKE p[0] , $f0
|
||||
g = PEEK p2[3]
|
||||
POKE p[1] , g
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 4: left border + suit graphic
|
||||
POKE p[0] , $f0
|
||||
g = PEEK p2[6]
|
||||
POKE p[1] , g
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 5: left border + suit
|
||||
POKE p[0] , $f0
|
||||
POKE p[1] , suit_charcode
|
||||
|
||||
p = p + 40
|
||||
|
||||
// Row 6: bottom border (2 cols)
|
||||
POKE p[0] , $f1
|
||||
POKE p[1] , $e3
|
||||
FEND
|
||||
|
||||
// Render waste pile - draw-3 style fanned display
|
||||
// Shows up to 3 cards fanned horizontally (2 col offset per card)
|
||||
FUNC render_waste_pile({WORD screen_address} {WORD offset} {WORD pile_ptr @ $fe} {BYTE draw_mode})
|
||||
BYTE count
|
||||
BYTE card
|
||||
BYTE card_id
|
||||
WORD off2
|
||||
WORD off4
|
||||
|
||||
count = PEEK pile_ptr[0]
|
||||
|
||||
// Draw-1 mode: only show top card
|
||||
IF draw_mode == 1
|
||||
IF count == 0
|
||||
render_empty_pile(screen_address, offset)
|
||||
ELSE
|
||||
card = PEEK pile_ptr[count]
|
||||
card_id = card & CARD_MASK
|
||||
render_card(screen_address, offset, card_id)
|
||||
ENDIF
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Draw-3 mode: fan out top 3 cards
|
||||
off2 = offset + 2
|
||||
off4 = offset + 4
|
||||
|
||||
SWITCH count
|
||||
CASE 0
|
||||
render_empty_pile(screen_address, offset)
|
||||
|
||||
CASE 1
|
||||
card = PEEK pile_ptr[1]
|
||||
card_id = card & CARD_MASK
|
||||
render_card(screen_address, offset, card_id)
|
||||
|
||||
CASE 2
|
||||
// 2nd from top (left edge)
|
||||
card = PEEK pile_ptr[1]
|
||||
card_id = card & CARD_MASK
|
||||
render_card_left_edge(screen_address, offset, card_id)
|
||||
// Top card (full)
|
||||
card = PEEK pile_ptr[2]
|
||||
card_id = card & CARD_MASK
|
||||
render_card(screen_address, off2, card_id)
|
||||
|
||||
DEFAULT
|
||||
// 3+ cards: show top 3 fanned
|
||||
BYTE idx
|
||||
// 3rd from top (left edge)
|
||||
idx = count - 2
|
||||
card = PEEK pile_ptr[idx]
|
||||
card_id = card & CARD_MASK
|
||||
render_card_left_edge(screen_address, offset, card_id)
|
||||
|
||||
// 2nd from top (left edge)
|
||||
idx = count - 1
|
||||
card = PEEK pile_ptr[idx]
|
||||
card_id = card & CARD_MASK
|
||||
render_card_left_edge(screen_address, off2, card_id)
|
||||
|
||||
// Top card (full)
|
||||
card = PEEK pile_ptr[count]
|
||||
card_id = card & CARD_MASK
|
||||
render_card(screen_address, off4, card_id)
|
||||
ENDSWITCH
|
||||
FEND
|
||||
|
||||
LABEL __skip_lib_cardrender
|
||||
|
||||
#IFEND
|
||||
899
cardsprites.c65
Normal file
899
cardsprites.c65
Normal file
|
|
@ -0,0 +1,899 @@
|
|||
|
||||
#IFNDEF __lib_cardsprites
|
||||
#DEFINE __lib_cardsprites 1
|
||||
|
||||
GOTO __skip_lib_cardsprites
|
||||
|
||||
// ============================================================================
|
||||
// CARD RANK SPRITES (A-K)
|
||||
// ============================================================================
|
||||
// Sprites for displaying card ranks in multiplexed border display
|
||||
// Each sprite is 64 bytes (21 rows of 3 bytes + 1 padding byte)
|
||||
// ============================================================================
|
||||
|
||||
// Sprite 0: Ace
|
||||
LABEL sprite_rank_ace
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $6c, $00
|
||||
!8 $00, $6c, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $01, $ff, $00
|
||||
!8 $01, $83, $00
|
||||
!8 $03, $c7, $80
|
||||
!8 $03, $c7, $80
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 1: 2
|
||||
LABEL sprite_rank_2
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $0e, $00
|
||||
!8 $00, $1c, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $70, $00
|
||||
!8 $00, $e6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 2: 3
|
||||
LABEL sprite_rank_3
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $cc, $00
|
||||
!8 $00, $18, $00
|
||||
!8 $00, $30, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $7e, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 3: 4
|
||||
LABEL sprite_rank_4
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $1c, $00
|
||||
!8 $00, $3c, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $ec, $00
|
||||
!8 $01, $cc, $00
|
||||
!8 $01, $ff, $00
|
||||
!8 $01, $ff, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $1e, $00
|
||||
!8 $00, $1e, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 4: 5
|
||||
LABEL sprite_rank_5
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c0, $00
|
||||
!8 $00, $c0, $00
|
||||
!8 $00, $fc, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 5: 6
|
||||
LABEL sprite_rank_6
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $3c, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $e0, $00
|
||||
!8 $00, $c0, $00
|
||||
!8 $00, $fc, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 6: 7
|
||||
LABEL sprite_rank_7
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $18, $00
|
||||
!8 $00, $18, $00
|
||||
!8 $00, $18, $00
|
||||
!8 $00, $30, $00
|
||||
!8 $00, $30, $00
|
||||
!8 $00, $30, $00
|
||||
!8 $00, $30, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 7: 8
|
||||
LABEL sprite_rank_8
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 8: 9
|
||||
LABEL sprite_rank_9
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7e, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $0e, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $78, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 9: 10
|
||||
LABEL sprite_rank_10
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $01, $9e, $00
|
||||
!8 $01, $bf, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $b3, $00
|
||||
!8 $01, $bf, $00
|
||||
!8 $01, $9e, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 10: Jack
|
||||
LABEL sprite_rank_jack
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $1e, $00
|
||||
!8 $00, $1e, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $cc, $00
|
||||
!8 $00, $cc, $00
|
||||
!8 $00, $fc, $00
|
||||
!8 $00, $78, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 11: Queen
|
||||
LABEL sprite_rank_queen
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $0e, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 12: King
|
||||
LABEL sprite_rank_king
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $01, $ef, $00
|
||||
!8 $01, $ef, $00
|
||||
!8 $00, $cc, $00
|
||||
!8 $00, $d8, $00
|
||||
!8 $00, $f0, $00
|
||||
!8 $00, $e0, $00
|
||||
!8 $00, $f0, $00
|
||||
!8 $00, $d8, $00
|
||||
!8 $00, $cc, $00
|
||||
!8 $00, $c6, $00
|
||||
!8 $01, $ef, $00
|
||||
!8 $01, $ef, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// ============================================================================
|
||||
// CREDITS SPRITES
|
||||
// ============================================================================
|
||||
// Two sprites that go together horizontally to display credits
|
||||
// ============================================================================
|
||||
|
||||
// Sprite 13: Credits (left part)
|
||||
LABEL sprite_credits_left
|
||||
ASM
|
||||
!8 $ae, $ea, $ee
|
||||
!8 $aa, $8a, $2a
|
||||
!8 $ee, $8c, $4a
|
||||
!8 $aa, $8a, $8a
|
||||
!8 $aa, $ea, $ee
|
||||
!8 $00, $00, $00
|
||||
!8 $0e, $4c, $ee
|
||||
!8 $08, $4a, $8a
|
||||
!8 $0e, $4a, $ce
|
||||
!8 $02, $4a, $8c
|
||||
!8 $0e, $4c, $ea
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $ee, $ee
|
||||
!8 $00, $2a, $28
|
||||
!8 $00, $ea, $ee
|
||||
!8 $00, $8a, $8a
|
||||
!8 $00, $ee, $ee
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// Sprite 14: Credits (right part)
|
||||
LABEL sprite_credits_right
|
||||
ASM
|
||||
!8 $4c, $00, $00
|
||||
!8 $4a, $00, $00
|
||||
!8 $4a, $00, $00
|
||||
!8 $4a, $00, $00
|
||||
!8 $4c, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $e0, $00, $00
|
||||
!8 $80, $00, $00
|
||||
!8 $e0, $00, $00
|
||||
!8 $20, $00, $00
|
||||
!8 $e0, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $fe
|
||||
ENDASM
|
||||
|
||||
// ============================================================================
|
||||
// SUIT COLOR SPRITES (Overlay pairs for multi-color display)
|
||||
// ============================================================================
|
||||
// Each suit uses 2 sprites overlaid to allow for two screen colors
|
||||
// Hearts (sprites 15-16), Diamonds (17-18), Clubs (19-20), Spades (21-22)
|
||||
// ============================================================================
|
||||
|
||||
// Sprite 15: Hearts (layer 1)
|
||||
LABEL sprite_suit_hearts_1
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $01, $c7, $00
|
||||
!8 $03, $ef, $80
|
||||
!8 $07, $ff, $c0
|
||||
!8 $07, $ff, $c0
|
||||
!8 $07, $ff, $c0
|
||||
!8 $03, $ff, $80
|
||||
!8 $01, $ff, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $12
|
||||
ENDASM
|
||||
|
||||
// Sprite 16: Hearts (layer 2)
|
||||
LABEL sprite_suit_hearts_2
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $41, $00
|
||||
!8 $00, $20, $80
|
||||
!8 $00, $00, $80
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $0a
|
||||
ENDASM
|
||||
|
||||
// Sprite 17: Diamonds (layer 1)
|
||||
LABEL sprite_suit_diamonds_1
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $01, $ff, $00
|
||||
!8 $03, $ff, $80
|
||||
!8 $03, $ff, $80
|
||||
!8 $01, $ff, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $12
|
||||
ENDASM
|
||||
|
||||
// Sprite 18: Diamonds (layer 2)
|
||||
LABEL sprite_suit_diamonds_2
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $08, $00
|
||||
!8 $00, $04, $00
|
||||
!8 $00, $02, $00
|
||||
!8 $00, $01, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $0a
|
||||
ENDASM
|
||||
|
||||
// Sprite 19: Clubs (layer 1)
|
||||
LABEL sprite_suit_clubs_1
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $03, $bb, $80
|
||||
!8 $07, $d7, $c0
|
||||
!8 $07, $ff, $c0
|
||||
!8 $07, $d7, $c0
|
||||
!8 $03, $93, $80
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $10
|
||||
ENDASM
|
||||
|
||||
// Sprite 20: Clubs (layer 2)
|
||||
LABEL sprite_suit_clubs_2
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $04, $00
|
||||
!8 $00, $04, $00
|
||||
!8 $01, $83, $80
|
||||
!8 $00, $c0, $c0
|
||||
!8 $00, $00, $40
|
||||
!8 $00, $00, $40
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $08, $00
|
||||
!8 $00, $04, $00
|
||||
;!8 $08, $00, $00
|
||||
;!8 $04, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $0b
|
||||
ENDASM
|
||||
|
||||
// Sprite 21: Spades (layer 1)
|
||||
LABEL sprite_suit_spades_1
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $fe, $00
|
||||
!8 $01, $ff, $00
|
||||
!8 $03, $ff, $80
|
||||
!8 $03, $ff, $80
|
||||
!8 $03, $ff, $80
|
||||
!8 $01, $d7, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $38, $00
|
||||
!8 $00, $7c, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $10
|
||||
ENDASM
|
||||
|
||||
// Sprite 22: Spades (layer 2)
|
||||
LABEL sprite_suit_spades_2
|
||||
ASM
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $10, $00
|
||||
!8 $00, $18, $00
|
||||
!8 $00, $0c, $00
|
||||
!8 $00, $06, $00
|
||||
!8 $00, $03, $00
|
||||
!8 $00, $01, $80
|
||||
!8 $00, $00, $80
|
||||
!8 $00, $00, $80
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
;!8 $08, $00, $00
|
||||
!8 $00, $08, $00
|
||||
;!8 $04, $00, $00
|
||||
!8 $00, $04, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $0b
|
||||
ENDASM
|
||||
|
||||
// ============================================================================
|
||||
// TEXT SPRITES - "SELECTED CARD"
|
||||
// ============================================================================
|
||||
// Two sprites that display "SELECTED CARD" text
|
||||
// ============================================================================
|
||||
|
||||
// Sprite 23: "SELECTED CARD" (left part)
|
||||
LABEL sprite_text_selected_card_1
|
||||
ASM
|
||||
!8 $77, $47, $77
|
||||
!8 $44, $44, $42
|
||||
!8 $76, $46, $42
|
||||
!8 $14, $44, $42
|
||||
!8 $77, $77, $72
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $77, $76, $00
|
||||
!8 $45, $55, $00
|
||||
!8 $47, $75, $00
|
||||
!8 $45, $65, $00
|
||||
!8 $75, $56, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00
|
||||
ENDASM
|
||||
|
||||
// Sprite 24: "SELECTED CARD" (right part)
|
||||
LABEL sprite_text_selected_card_2
|
||||
ASM
|
||||
!8 $76, $00, $00
|
||||
!8 $45, $00, $00
|
||||
!8 $65, $00, $00
|
||||
!8 $45, $00, $00
|
||||
!8 $76, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00
|
||||
ENDASM
|
||||
|
||||
// ============================================================================
|
||||
// CARD DISPLAY SPRITE MANAGEMENT
|
||||
// ============================================================================
|
||||
// Functions to display selected card using sprites in upper right corner
|
||||
// Uses sprites 1-5 (sprite 0 is reserved for pointer)
|
||||
// ============================================================================
|
||||
|
||||
// VIC-II Sprite registers for card display (sprites 1-5)
|
||||
// Note: sprite_x_msb and sprite_enable already declared in pointer.c65
|
||||
BYTE sprite_x1 @ $d002 // Sprite 1 X
|
||||
BYTE sprite_y1 @ $d003 // Sprite 1 Y
|
||||
BYTE sprite_x2 @ $d004 // Sprite 2 X
|
||||
BYTE sprite_y2 @ $d005 // Sprite 2 Y
|
||||
BYTE sprite_x3 @ $d006 // Sprite 3 X
|
||||
BYTE sprite_y3 @ $d007 // Sprite 3 Y
|
||||
BYTE sprite_x4 @ $d008 // Sprite 4 X
|
||||
BYTE sprite_y4 @ $d009 // Sprite 4 Y
|
||||
BYTE sprite_x5 @ $d00a // Sprite 5 X
|
||||
BYTE sprite_y5 @ $d00b // Sprite 5 Y
|
||||
BYTE sprite_color1 @ $d028 // Sprite 1 color
|
||||
BYTE sprite_color2 @ $d029 // Sprite 2 color
|
||||
BYTE sprite_color3 @ $d02a // Sprite 3 color
|
||||
BYTE sprite_color4 @ $d02b // Sprite 4 color
|
||||
BYTE sprite_color5 @ $d02c // Sprite 5 color
|
||||
BYTE sprite_pointer1 @ $07f9 // Sprite 1 pointer
|
||||
BYTE sprite_pointer2 @ $07fa // Sprite 2 pointer
|
||||
BYTE sprite_pointer3 @ $07fb // Sprite 3 pointer
|
||||
BYTE sprite_pointer4 @ $07fc // Sprite 4 pointer
|
||||
BYTE sprite_pointer5 @ $07fd // Sprite 5 pointer
|
||||
|
||||
// Card display sprite positions (upper right corner)
|
||||
// Screen starts at (24,50), size 320x200
|
||||
// Sprites are 24 pixels wide
|
||||
WORD CONST CARD_TEXT_X1 = 312 // "SELECTED CARD" left part
|
||||
WORD CONST CARD_TEXT_X2 = CARD_TEXT_X1+24 // "SELECTED CARD" right part
|
||||
BYTE CONST CARD_TEXT_Y = 50 // Top of screen
|
||||
WORD CONST CARD_RANK_X = 312 // Rank sprite X
|
||||
WORD CONST CARD_SUIT_X = CARD_RANK_X+14 // Suit sprites X (both layers) - side by side with rank
|
||||
BYTE CONST CARD_RANK_Y = 64 // Below text (50 + 21 pixel sprite height)
|
||||
|
||||
// Sprite data block numbers (offset from base at $2240 / 64 = 137)
|
||||
BYTE CONST SPRITE_BLOCK_TEXT_LEFT = 160 // sprite_text_selected_card_1 (sprite 23)
|
||||
BYTE CONST SPRITE_BLOCK_TEXT_RIGHT = 161 // sprite_text_selected_card_2 (sprite 24)
|
||||
// Rank blocks: 137-149 (ace=137, 2=138, ... king=149)
|
||||
// Suit blocks: hearts1=152, hearts2=153, diamonds1=154, diamonds2=155,
|
||||
// clubs1=156, clubs2=157, spades1=158, spades2=159
|
||||
|
||||
// ============================================================================
|
||||
// FUNC card_display_init
|
||||
// Initialize card display sprites (position and "SELECTED CARD" text)
|
||||
// Call this once during game initialization
|
||||
// ============================================================================
|
||||
FUNC card_display_init
|
||||
BYTE temp
|
||||
|
||||
// Set "SELECTED CARD" text sprites (sprites 1 & 2)
|
||||
sprite_pointer1 = SPRITE_BLOCK_TEXT_LEFT
|
||||
sprite_pointer2 = SPRITE_BLOCK_TEXT_RIGHT
|
||||
|
||||
// Position text sprites
|
||||
sprite_x1 = CARD_TEXT_X1
|
||||
sprite_y1 = CARD_TEXT_Y
|
||||
sprite_x2 = CARD_TEXT_X2
|
||||
sprite_y2 = CARD_TEXT_Y
|
||||
|
||||
// Set X MSB for sprites > 255
|
||||
temp = sprite_x_msb
|
||||
IF CARD_TEXT_X1 > 255
|
||||
temp = temp | %00000010 // Sprite 1 X MSB
|
||||
ENDIF
|
||||
IF CARD_TEXT_X2 > 255
|
||||
temp = temp | %00000100 // Sprite 2 X MSB
|
||||
ENDIF
|
||||
sprite_x_msb = temp
|
||||
|
||||
// Set text sprite colors (black)
|
||||
sprite_color1 = color_black
|
||||
sprite_color2 = color_black
|
||||
|
||||
// Initially hide all card display sprites (1-5)
|
||||
temp = sprite_enable
|
||||
temp = temp & %11000001 // Keep sprite 0 (pointer), clear 1-5
|
||||
sprite_enable = temp
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC card_display_show
|
||||
// Show selected card using sprites
|
||||
// Parameters:
|
||||
// card_rank: Card rank (0-12: A,2-10,J,Q,K)
|
||||
// card_suit: Card suit (0-3: hearts, diamonds, clubs, spades)
|
||||
// ============================================================================
|
||||
FUNC card_display_show({BYTE card_rank} {BYTE card_suit})
|
||||
BYTE rank_block
|
||||
BYTE suit_block1
|
||||
BYTE suit_block2
|
||||
BYTE suit_color
|
||||
BYTE rank_color
|
||||
BYTE suit_color2
|
||||
|
||||
// Calculate sprite block for rank (137 = ace, 138 = 2, etc.)
|
||||
rank_block = 137
|
||||
rank_block = rank_block + card_rank
|
||||
|
||||
// Calculate sprite blocks for suit (2 layers each)
|
||||
// Sprite order: Hearts: 152-153, Diamonds: 154-155, Clubs: 156-157, Spades: 158-159
|
||||
// Card suit order: 0=Hearts, 1=Diamonds, 2=Spades, 3=Clubs
|
||||
// Need to swap spades(2) and clubs(3)
|
||||
BYTE suit_index
|
||||
IF card_suit == 2
|
||||
suit_index = 3 // Spades (card suit 2) -> sprite index 3
|
||||
ELSE
|
||||
IF card_suit == 3
|
||||
suit_index = 2 // Clubs (card suit 3) -> sprite index 2
|
||||
ELSE
|
||||
suit_index = card_suit // Hearts and Diamonds map directly
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
ASM
|
||||
lda |suit_index|
|
||||
asl // Multiply by 2 (shift left)
|
||||
clc
|
||||
adc #152
|
||||
sta |suit_block1|
|
||||
ENDASM
|
||||
suit_block2 = suit_block1 + 1
|
||||
//suit_block2 = suit_block2 + 1
|
||||
|
||||
// Determine suit colors
|
||||
// Rank and suit layer 1: red for hearts/diamonds, black for clubs/spades
|
||||
// Suit layer 2: pink for red suits, dark grey for black suits
|
||||
|
||||
IF card_suit < 2
|
||||
rank_color = color_red
|
||||
suit_color = color_red
|
||||
suit_color2 = color_pink
|
||||
ELSE
|
||||
rank_color = color_black
|
||||
suit_color = color_black
|
||||
suit_color2 = color_dark_grey
|
||||
ENDIF
|
||||
|
||||
// Set sprite 3: Rank
|
||||
sprite_pointer3 = rank_block
|
||||
sprite_x3 = CARD_RANK_X
|
||||
sprite_y3 = CARD_RANK_Y
|
||||
sprite_color3 = rank_color
|
||||
|
||||
// Set sprites 4 & 5: Suit (2 layers at same position)
|
||||
// Sprite 4 has higher priority (drawn on top), so it gets the overlay layer
|
||||
// Sprite 5 has lower priority (drawn behind), so it gets the base layer
|
||||
sprite_pointer4 = suit_block2 // Overlay layer (drawn on top)
|
||||
sprite_pointer5 = suit_block1 // Base layer (drawn behind)
|
||||
sprite_x4 = CARD_SUIT_X
|
||||
sprite_y4 = CARD_RANK_Y // Same Y as rank
|
||||
sprite_x5 = CARD_SUIT_X // Exactly same X/Y as sprite 4
|
||||
sprite_y5 = CARD_RANK_Y
|
||||
sprite_color4 = suit_color2 // Overlay color (pink/dark grey)
|
||||
sprite_color5 = suit_color // Base color (red/black)
|
||||
|
||||
// Set X MSB for rank and suit sprites
|
||||
IF CARD_RANK_X > 255
|
||||
sprite_x_msb = sprite_x_msb | %00111110
|
||||
ENDIF
|
||||
|
||||
// Enable all card display sprites (1-5)
|
||||
sprite_enable = sprite_enable | %00111110
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC card_display_hide
|
||||
// Hide all card display sprites (when no card selected)
|
||||
// ============================================================================
|
||||
FUNC card_display_hide
|
||||
BYTE temp
|
||||
|
||||
// Disable sprites 1-5, keep sprite 0 (pointer)
|
||||
temp = sprite_enable
|
||||
temp = temp & %11000001
|
||||
sprite_enable = temp
|
||||
FEND
|
||||
|
||||
LABEL __skip_lib_cardsprites
|
||||
|
||||
#IFEND
|
||||
878
cardtests.c65
Normal file
878
cardtests.c65
Normal file
|
|
@ -0,0 +1,878 @@
|
|||
|
||||
#IFNDEF __lib_cardtests
|
||||
#DEFINE __lib_cardtests 1
|
||||
|
||||
#INCLUDE "cardrender.c65"
|
||||
#INCLUDE "piles.c65"
|
||||
#INCLUDE "carddeck.c65"
|
||||
#INCLUDE "cardmoves.c65"
|
||||
|
||||
GOTO __skip_lib_cardtests
|
||||
|
||||
FUNC show_charset
|
||||
|
||||
WORD i
|
||||
BYTE b
|
||||
b = 0
|
||||
FOR i = $0400 TO $0400+999
|
||||
POKE i , b
|
||||
b++
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
// Demo: fill $c000-$c1ff with 512 random bytes
|
||||
FUNC rand_demo
|
||||
WORD ptr @ $fa
|
||||
WORD count
|
||||
BYTE r
|
||||
|
||||
ptr = $c000
|
||||
FOR count = 0 TO 511
|
||||
//rand(r)
|
||||
rand_max(52, r)
|
||||
POKE ptr , r
|
||||
ptr++
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
FUNC render_all_cards_test
|
||||
// Test render_card - render all cards by ID
|
||||
BYTE i
|
||||
WORD wi
|
||||
wi = 0
|
||||
FOR i = 0 TO 12
|
||||
render_card($0404, wi, i)
|
||||
wi = wi + 2
|
||||
NEXT
|
||||
|
||||
wi = 0
|
||||
FOR i = 0+13 TO 12+13
|
||||
render_card(7*40+$0404, wi, i)
|
||||
wi = wi + 2
|
||||
NEXT
|
||||
|
||||
wi = 0
|
||||
FOR i = 13*2+0 TO 13*2+12
|
||||
render_card(2*7*40+$0404, wi, i)
|
||||
wi = wi + 2
|
||||
NEXT
|
||||
|
||||
wi = 0
|
||||
FOR i = 13*3+0 TO 13*3+12
|
||||
render_card(3*7*40+$0404, wi, i)
|
||||
wi = wi + 2
|
||||
NEXT
|
||||
|
||||
FEND
|
||||
|
||||
// Test face-down stack rendering (1-6 cards)
|
||||
FUNC render_facedown_test
|
||||
BYTE rows
|
||||
|
||||
// Column 0: 1 face-down
|
||||
render_facedown_stack($0400, 0, 1, rows)
|
||||
|
||||
// Column 1: 2 face-down
|
||||
render_facedown_stack($0400, 6, 2, rows)
|
||||
|
||||
// Column 2: 3 face-down
|
||||
render_facedown_stack($0400, 12, 3, rows)
|
||||
|
||||
// Column 3: 4 face-down
|
||||
render_facedown_stack($0400, 18, 4, rows)
|
||||
|
||||
// Column 4: 5 face-down
|
||||
render_facedown_stack($0400, 24, 0, rows)
|
||||
|
||||
// Column 5: 6 face-down
|
||||
render_facedown_stack($0400, 30, 51, rows)
|
||||
FEND
|
||||
|
||||
// Test stacked face-up cards
|
||||
FUNC render_faceup_stack_test
|
||||
BYTE rows
|
||||
BYTE i
|
||||
WORD pos
|
||||
|
||||
// Column 0: 1 face-down, 1 face-up (full card)
|
||||
pos = 0
|
||||
render_facedown_stack($0400, pos, 1, rows)
|
||||
FOR i = 1 TO rows
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
render_card_body_full($0400, pos, 0) // Ace of Hearts
|
||||
|
||||
// Column 1: 2 face-down, 2 face-up
|
||||
pos = 6
|
||||
render_facedown_stack($0400, pos, 2, rows)
|
||||
FOR i = 1 TO rows
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
render_card_body_partial($0400, pos, 13) // Ace of Diamonds
|
||||
pos = pos + 40
|
||||
render_connecting_border($0400, pos)
|
||||
pos = pos + 40
|
||||
render_card_body_full($0400, pos, 26) // Ace of Spades
|
||||
|
||||
// Column 2: 1 face-down, 3 face-up
|
||||
pos = 12
|
||||
render_facedown_stack($0400, pos, 1, rows)
|
||||
FOR i = 1 TO rows
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
render_card_body_partial($0400, pos, 1) // 2 of Hearts
|
||||
pos = pos + 40
|
||||
render_connecting_border($0400, pos)
|
||||
pos = pos + 40
|
||||
render_card_body_partial($0400, pos, 14) // 2 of Diamonds
|
||||
pos = pos + 40
|
||||
render_connecting_border($0400, pos)
|
||||
pos = pos + 40
|
||||
render_card_body_full($0400, pos, 27) // 2 of Spades
|
||||
|
||||
// Column 3: 0 face-down, 2 face-up (no face-down cards)
|
||||
pos = 18
|
||||
render_facedown_stack($0400, pos, 0, rows)
|
||||
FOR i = 1 TO rows
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
render_card_body_partial($0400, pos, 39) // Ace of Clubs
|
||||
pos = pos + 40
|
||||
render_connecting_border($0400, pos)
|
||||
pos = pos + 40
|
||||
render_card_body_full($0400, pos, 40) // 2 of Clubs
|
||||
|
||||
// Column 4: 3 face-down, 1 face-up
|
||||
pos = 24
|
||||
render_facedown_stack($0400, pos, 3, rows)
|
||||
FOR i = 1 TO rows
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
render_card_body_full($0400, pos, 12) // King of Hearts
|
||||
|
||||
// Column 5: Standalone card (foundation style)
|
||||
render_card($0400, 30, 51) // King of Clubs
|
||||
FEND
|
||||
|
||||
// Test rendering actual dealt tableau piles
|
||||
FUNC render_tableaus_test
|
||||
WORD ptr @ $f8
|
||||
|
||||
// Render all 7 tableau piles
|
||||
// Each pile is 6 chars wide (5 card + 1 space)
|
||||
POINTER ptr -> pile_tab0
|
||||
render_tableau_pile($0400, 0, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab1
|
||||
render_tableau_pile($0400, 5, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab2
|
||||
render_tableau_pile($0400, 10, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab3
|
||||
render_tableau_pile($0400, 15, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab4
|
||||
render_tableau_pile($0400, 20, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab5
|
||||
render_tableau_pile($0400, 25, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab6
|
||||
render_tableau_pile($0400, 30, ptr)
|
||||
FEND
|
||||
|
||||
// Setup mid-game tableau piles for demo (Klondike rules)
|
||||
// Card IDs: Hearts 0-12 (red), Diamonds 13-25 (red), Spades 26-38 (black), Clubs 39-51 (black)
|
||||
// Rank: A=0, 2=1, 3=2, 4=3, 5=4, 6=5, 7=6, 8=7, 9=8, 10=9, J=10, Q=11, K=12
|
||||
// Klondike: alternating colors, descending rank
|
||||
FUNC setup_midgame_piles
|
||||
WORD ptr @ $f8
|
||||
|
||||
// Tab0: K♥(red) → Q♠(black) → J♦(red) → 10♣(black)
|
||||
POINTER ptr -> pile_tab0
|
||||
POKE ptr[0] , 4
|
||||
POKE ptr[1] , 12 // King of Hearts (red)
|
||||
POKE ptr[2] , 37 // Queen of Spades (black)
|
||||
POKE ptr[3] , 23 // Jack of Diamonds (red)
|
||||
POKE ptr[4] , 48 // 10 of Clubs (black)
|
||||
|
||||
// Tab1: 1 face-down, Q♣(black) → J♥(red) → 10♠(black)
|
||||
POINTER ptr -> pile_tab1
|
||||
POKE ptr[0] , 4
|
||||
POKE ptr[1] , 0|CARD_FACEDOWN
|
||||
POKE ptr[2] , 50 // Queen of Clubs (black)
|
||||
POKE ptr[3] , 10 // Jack of Hearts (red)
|
||||
POKE ptr[4] , 35 // 10 of Spades (black)
|
||||
|
||||
// Tab2: 2 face-down, 7♦(red)
|
||||
POINTER ptr -> pile_tab2
|
||||
POKE ptr[0] , 3
|
||||
POKE ptr[1] , 1|CARD_FACEDOWN
|
||||
POKE ptr[2] , 2|CARD_FACEDOWN
|
||||
POKE ptr[3] , 19 // 7 of Diamonds (red)
|
||||
|
||||
// Tab3: K♠(black) → Q♥(red) → J♣(black) → 10♦(red) → 9♠(black)
|
||||
POINTER ptr -> pile_tab3
|
||||
POKE ptr[0] , 5
|
||||
POKE ptr[1] , 38 // King of Spades (black)
|
||||
POKE ptr[2] , 11 // Queen of Hearts (red)
|
||||
POKE ptr[3] , 49 // Jack of Clubs (black)
|
||||
POKE ptr[4] , 22 // 10 of Diamonds (red)
|
||||
POKE ptr[5] , 34 // 9 of Spades (black)
|
||||
|
||||
// Tab4: 3 face-down, K♦(red) → Q♣(black)
|
||||
POINTER ptr -> pile_tab4
|
||||
POKE ptr[0] , 5
|
||||
POKE ptr[1] , 3|CARD_FACEDOWN
|
||||
POKE ptr[2] , 4|CARD_FACEDOWN
|
||||
POKE ptr[3] , 5|CARD_FACEDOWN
|
||||
POKE ptr[4] , 25 // King of Diamonds (red)
|
||||
POKE ptr[5] , 50 // Queen of Clubs (black)
|
||||
|
||||
// Tab5: 4 face-down, 5♥(red)
|
||||
POINTER ptr -> pile_tab5
|
||||
POKE ptr[0] , 5
|
||||
POKE ptr[1] , 6|CARD_FACEDOWN
|
||||
POKE ptr[2] , 7|CARD_FACEDOWN
|
||||
POKE ptr[3] , 8|CARD_FACEDOWN
|
||||
POKE ptr[4] , 9|CARD_FACEDOWN
|
||||
POKE ptr[5] , 4 // 5 of Hearts (red)
|
||||
|
||||
// Tab6: 5 face-down, 8♣(black) → 7♥(red)
|
||||
POINTER ptr -> pile_tab6
|
||||
POKE ptr[0] , 7
|
||||
POKE ptr[1] , 10|CARD_FACEDOWN
|
||||
POKE ptr[2] , 11|CARD_FACEDOWN
|
||||
POKE ptr[3] , 12|CARD_FACEDOWN
|
||||
POKE ptr[4] , 13|CARD_FACEDOWN
|
||||
POKE ptr[5] , 14|CARD_FACEDOWN
|
||||
POKE ptr[6] , 46 // 8 of Clubs (black)
|
||||
POKE ptr[7] , 6 // 7 of Hearts (red)
|
||||
FEND
|
||||
|
||||
// Test mid-game tableau rendering
|
||||
FUNC render_midgame_test
|
||||
setup_midgame_piles()
|
||||
render_tableaus_test()
|
||||
FEND
|
||||
|
||||
// Setup foundation piles in different states for testing
|
||||
FUNC setup_foundation_test_piles
|
||||
WORD ptr @ $f8
|
||||
|
||||
// Foundation 0: empty (Hearts)
|
||||
POINTER ptr -> pile_found0
|
||||
POKE ptr[0] , 0
|
||||
|
||||
// Foundation 1: Ace only (Diamonds)
|
||||
POINTER ptr -> pile_found1
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 13 // A♦
|
||||
|
||||
// Foundation 2: A-2-3 (Spades)
|
||||
POINTER ptr -> pile_found2
|
||||
POKE ptr[0] , 3
|
||||
POKE ptr[1] , 26 // A♠
|
||||
POKE ptr[2] , 27 // 2♠
|
||||
POKE ptr[3] , 28 // 3♠
|
||||
|
||||
// Foundation 3: full A-K (Clubs)
|
||||
POINTER ptr -> pile_found3
|
||||
POKE ptr[0] , 13
|
||||
BYTE i
|
||||
BYTE card
|
||||
card = 39
|
||||
ptr++
|
||||
FOR i = 0 TO 12
|
||||
POKE ptr , card
|
||||
ptr++
|
||||
card++
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
// Test foundation pile rendering
|
||||
FUNC render_foundation_test
|
||||
WORD ptr @ $f8
|
||||
|
||||
setup_foundation_test_piles()
|
||||
|
||||
// Render 4 foundations side by side
|
||||
POINTER ptr -> pile_found0
|
||||
render_foundation_pile($0400, 0, ptr)
|
||||
|
||||
POINTER ptr -> pile_found1
|
||||
render_foundation_pile($0400, 6, ptr)
|
||||
|
||||
POINTER ptr -> pile_found2
|
||||
render_foundation_pile($0400, 12, ptr)
|
||||
|
||||
POINTER ptr -> pile_found3
|
||||
render_foundation_pile($0400, 18, ptr)
|
||||
FEND
|
||||
|
||||
// Test waste pile rendering with different card counts
|
||||
FUNC render_waste_test
|
||||
WORD ptr @ $f8 // Use $f8 to avoid conflict with $fa used by sub-functions
|
||||
|
||||
// Test 0 cards
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 0
|
||||
render_waste_pile($0400, 0, ptr, 3)
|
||||
|
||||
// Test 1 card
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 0 // A♥
|
||||
render_waste_pile($0400, 10, ptr, 3)
|
||||
|
||||
// Test 2 cards
|
||||
POKE ptr[0] , 2
|
||||
POKE ptr[1] , 13 // A♦
|
||||
POKE ptr[2] , 26 // A♠
|
||||
render_waste_pile($0400, 20, ptr, 3)
|
||||
|
||||
// Test 3 cards
|
||||
POKE ptr[0] , 3
|
||||
POKE ptr[1] , 39 // A♣
|
||||
POKE ptr[2] , 1 // 2♥
|
||||
POKE ptr[3] , 14 // 2♦
|
||||
render_waste_pile($0400, 8*40, ptr, 3)
|
||||
|
||||
// Test 5 cards (should show only top 3)
|
||||
POKE ptr[0] , 5
|
||||
POKE ptr[1] , 10 // J♥
|
||||
POKE ptr[2] , 11 // Q♥
|
||||
POKE ptr[3] , 12 // K♥
|
||||
POKE ptr[4] , 25 // K♦
|
||||
POKE ptr[5] , 38 // K♠
|
||||
render_waste_pile($0400, 8*40+15, ptr, 3)
|
||||
FEND
|
||||
|
||||
// Test all pile renderers together - Klondike layout
|
||||
FUNC render_all_piles_test
|
||||
WORD ptr @ $f8
|
||||
|
||||
// Setup stock with some cards
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 10
|
||||
|
||||
// Setup waste with 3 cards
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 3
|
||||
POKE ptr[1] , 5 // 6♥
|
||||
POKE ptr[2] , 18 // 6♦
|
||||
POKE ptr[3] , 31 // 6♠
|
||||
|
||||
// Setup foundations
|
||||
POINTER ptr -> pile_found0
|
||||
POKE ptr[0] , 0 // empty
|
||||
|
||||
POINTER ptr -> pile_found1
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 13 // A♦
|
||||
|
||||
POINTER ptr -> pile_found2
|
||||
POKE ptr[0] , 2
|
||||
POKE ptr[1] , 26 // A♠
|
||||
POKE ptr[2] , 27 // 2♠
|
||||
|
||||
POINTER ptr -> pile_found3
|
||||
POKE ptr[0] , 3
|
||||
POKE ptr[1] , 39 // A♣
|
||||
POKE ptr[2] , 40 // 2♣
|
||||
POKE ptr[3] , 41 // 3♣
|
||||
|
||||
// Setup tableaus using midgame piles
|
||||
setup_midgame_piles()
|
||||
|
||||
// Row 0: Stock, Waste, gap, 4 Foundations
|
||||
// Stock at col 0
|
||||
POINTER ptr -> pile_stock
|
||||
render_stock_pile($0400, 0, ptr)
|
||||
|
||||
// Waste at col 6 (fanned takes 9 cols)
|
||||
POINTER ptr -> pile_waste
|
||||
render_waste_pile($0400, 6, ptr, 3)
|
||||
|
||||
// Foundations at cols 16, 21, 26, 31
|
||||
POINTER ptr -> pile_found0
|
||||
render_foundation_pile($0400, 16, ptr)
|
||||
|
||||
POINTER ptr -> pile_found1
|
||||
render_foundation_pile($0400, 21, ptr)
|
||||
|
||||
POINTER ptr -> pile_found2
|
||||
render_foundation_pile($0400, 26, ptr)
|
||||
|
||||
POINTER ptr -> pile_found3
|
||||
render_foundation_pile($0400, 31, ptr)
|
||||
|
||||
// Row 8: 7 Tableaus
|
||||
POINTER ptr -> pile_tab0
|
||||
render_tableau_pile($0400, 8*40, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab1
|
||||
render_tableau_pile($0400, 8*40+5, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab2
|
||||
render_tableau_pile($0400, 8*40+10, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab3
|
||||
render_tableau_pile($0400, 8*40+15, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab4
|
||||
render_tableau_pile($0400, 8*40+20, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab5
|
||||
render_tableau_pile($0400, 8*40+25, ptr)
|
||||
|
||||
POINTER ptr -> pile_tab6
|
||||
render_tableau_pile($0400, 8*40+30, ptr)
|
||||
FEND
|
||||
|
||||
// ============================================================================
|
||||
// Move Function Tests
|
||||
// ============================================================================
|
||||
|
||||
// Clear all piles to known empty state
|
||||
FUNC clear_all_piles
|
||||
WORD ptr @ $f8
|
||||
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 0
|
||||
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 0
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
FEND
|
||||
|
||||
// Helper: Render stock and waste side by side for move tests
|
||||
FUNC render_stock_waste_test
|
||||
WORD ptr @ $f8
|
||||
|
||||
POINTER ptr -> pile_stock
|
||||
render_stock_pile($0400, 0, ptr)
|
||||
|
||||
POINTER ptr -> pile_waste
|
||||
render_waste_pile($0400, 6, ptr, 3)
|
||||
FEND
|
||||
|
||||
// Test move_stock_to_waste with draw-1
|
||||
FUNC test_stock_to_waste_draw1
|
||||
WORD ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup stock with 5 cards (face-down)
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 5
|
||||
POKE ptr[1] , 0|CARD_FACEDOWN // A♥
|
||||
POKE ptr[2] , 13|CARD_FACEDOWN // A♦
|
||||
POKE ptr[3] , 26|CARD_FACEDOWN // A♠
|
||||
POKE ptr[4] , 39|CARD_FACEDOWN // A♣
|
||||
POKE ptr[5] , 12|CARD_FACEDOWN // K♥
|
||||
|
||||
// Render BEFORE
|
||||
render_stock_waste_test()
|
||||
wait_key()
|
||||
|
||||
// Draw 1 card three times
|
||||
move_stock_to_waste(1, success) // K♥ to waste
|
||||
move_stock_to_waste(1, success) // A♣ to waste
|
||||
move_stock_to_waste(1, success) // A♠ to waste
|
||||
|
||||
// Render AFTER: stock should have 2, waste should have 3
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
render_stock_waste_test()
|
||||
FEND
|
||||
|
||||
// Test move_stock_to_waste with draw-3
|
||||
FUNC test_stock_to_waste_draw3
|
||||
WORD ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup stock with 7 cards (face-down)
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 7
|
||||
POKE ptr[1] , 0|CARD_FACEDOWN // A♥
|
||||
POKE ptr[2] , 1|CARD_FACEDOWN // 2♥
|
||||
POKE ptr[3] , 2|CARD_FACEDOWN // 3♥
|
||||
POKE ptr[4] , 3|CARD_FACEDOWN // 4♥
|
||||
POKE ptr[5] , 4|CARD_FACEDOWN // 5♥
|
||||
POKE ptr[6] , 5|CARD_FACEDOWN // 6♥
|
||||
POKE ptr[7] , 6|CARD_FACEDOWN // 7♥
|
||||
|
||||
// Draw 3 cards
|
||||
move_stock_to_waste(3, success)
|
||||
|
||||
// Render result: stock should have 4, waste should have 3
|
||||
render_stock_waste_test()
|
||||
FEND
|
||||
|
||||
// Test move_reset_stock
|
||||
FUNC test_reset_stock
|
||||
WORD ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup waste with 4 cards (face-up)
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 4
|
||||
POKE ptr[1] , 0 // A♥
|
||||
POKE ptr[2] , 13 // A♦
|
||||
POKE ptr[3] , 26 // A♠
|
||||
POKE ptr[4] , 39 // A♣
|
||||
|
||||
// Reset stock
|
||||
move_reset_stock(success)
|
||||
|
||||
// Render result: stock should have 4 (face-down), waste empty
|
||||
render_stock_waste_test()
|
||||
FEND
|
||||
|
||||
// Test move_waste_to_tab - valid move
|
||||
FUNC test_waste_to_tab_valid
|
||||
WORD ptr @ $f8
|
||||
WORD tab_ptr @ $f6
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup waste with 8♥ (red, rank 7)
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 7 // 8♥
|
||||
|
||||
// Setup tab0 with 9♠ (black, rank 8) - valid target for red 8
|
||||
POINTER tab_ptr -> pile_tab0
|
||||
POKE tab_ptr[0] , 1
|
||||
POKE tab_ptr[1] , 34 // 9♠
|
||||
|
||||
// Render BEFORE
|
||||
render_tableau_pile($0400, 0, tab_ptr)
|
||||
render_waste_pile($0400, 10, ptr, 3)
|
||||
wait_key()
|
||||
|
||||
// Move waste to tab
|
||||
move_waste_to_tab(tab_ptr, success)
|
||||
|
||||
// Render AFTER
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
POINTER tab_ptr -> pile_tab0
|
||||
render_tableau_pile($0400, 0, tab_ptr)
|
||||
POINTER ptr -> pile_waste
|
||||
render_waste_pile($0400, 10, ptr, 3)
|
||||
FEND
|
||||
|
||||
// Test move_waste_to_tab - King to empty tableau
|
||||
FUNC test_waste_to_tab_king_empty
|
||||
WORD ptr @ $f8
|
||||
WORD tab_ptr @ $fc
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup waste with K♠ (black, rank 12)
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 38 // K♠
|
||||
|
||||
// Tab0 is empty
|
||||
POINTER tab_ptr -> pile_tab0
|
||||
|
||||
// Move King to empty tableau
|
||||
move_waste_to_tab(tab_ptr, success)
|
||||
|
||||
// Render tableau - should show K♠
|
||||
render_tableau_pile($0400, 0, tab_ptr)
|
||||
FEND
|
||||
|
||||
// Test move_waste_to_tab - invalid move (same color)
|
||||
FUNC test_waste_to_tab_invalid
|
||||
WORD ptr @ $f8
|
||||
WORD tab_ptr @ $fc
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup waste with 8♥ (red)
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 7 // 8♥
|
||||
|
||||
// Setup tab0 with 9♦ (red) - invalid target (same color)
|
||||
POINTER tab_ptr -> pile_tab0
|
||||
POKE tab_ptr[0] , 1
|
||||
POKE tab_ptr[1] , 21 // 9♦
|
||||
|
||||
// Try move - should fail
|
||||
move_waste_to_tab(tab_ptr, success)
|
||||
|
||||
// Render - waste should still have card, tableau unchanged
|
||||
render_tableau_pile($0400, 0, tab_ptr)
|
||||
POINTER ptr -> pile_waste
|
||||
render_waste_pile($0400, 10, ptr, 3)
|
||||
FEND
|
||||
|
||||
// Test move_waste_to_found - build Ace to foundation
|
||||
FUNC test_waste_to_found_ace
|
||||
WORD ptr @ $f8
|
||||
WORD found_ptr @ $fc
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup waste with A♥
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 0 // A♥
|
||||
|
||||
// Foundation 0 is empty
|
||||
POINTER found_ptr -> pile_found0
|
||||
|
||||
// Move Ace to foundation
|
||||
move_waste_to_found(found_ptr, success)
|
||||
|
||||
// Render foundation - should show A♥
|
||||
render_foundation_pile($0400, 0, found_ptr)
|
||||
FEND
|
||||
|
||||
// Test move_waste_to_found - build sequence
|
||||
FUNC test_waste_to_found_sequence
|
||||
WORD ptr @ $f8
|
||||
WORD found_ptr @ $fc
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup foundation with A♠, 2♠
|
||||
POINTER found_ptr -> pile_found2
|
||||
POKE found_ptr[0] , 2
|
||||
POKE found_ptr[1] , 26 // A♠
|
||||
POKE found_ptr[2] , 27 // 2♠
|
||||
|
||||
// Setup waste with 3♠
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 28 // 3♠
|
||||
|
||||
// Move 3♠ to foundation
|
||||
move_waste_to_found(found_ptr, success)
|
||||
|
||||
// Render foundation - should show 3♠
|
||||
render_foundation_pile($0400, 0, found_ptr)
|
||||
FEND
|
||||
|
||||
// Test move_tab_to_found
|
||||
FUNC test_tab_to_found
|
||||
WORD tab_ptr @ $f6
|
||||
WORD found_ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup tab0 with face-down card + A♦
|
||||
POINTER tab_ptr -> pile_tab0
|
||||
POKE tab_ptr[0] , 2
|
||||
POKE tab_ptr[1] , 5|CARD_FACEDOWN // 6♥ face-down
|
||||
POKE tab_ptr[2] , 13 // A♦ face-up
|
||||
|
||||
// Foundation 1 empty
|
||||
POINTER found_ptr -> pile_found1
|
||||
|
||||
// Render BEFORE
|
||||
render_foundation_pile($0400, 0, found_ptr)
|
||||
render_tableau_pile($0400, 10, tab_ptr)
|
||||
wait_key()
|
||||
|
||||
// Move A♦ to foundation
|
||||
move_tab_to_found(tab_ptr, found_ptr, success)
|
||||
|
||||
// Render AFTER - foundation should have A♦, tableau should show flipped 6♥
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
POINTER found_ptr -> pile_found1
|
||||
POINTER tab_ptr -> pile_tab0
|
||||
render_foundation_pile($0400, 0, found_ptr)
|
||||
render_tableau_pile($0400, 10, tab_ptr)
|
||||
FEND
|
||||
|
||||
// Test move_tab_to_tab - move single card
|
||||
FUNC test_tab_to_tab_single
|
||||
WORD src_ptr @ $f6
|
||||
WORD dst_ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup src with 8♦ (red)
|
||||
POINTER src_ptr -> pile_tab0
|
||||
POKE src_ptr[0] , 1
|
||||
POKE src_ptr[1] , 20 // 8♦
|
||||
|
||||
// Setup dst with 9♠ (black)
|
||||
POINTER dst_ptr -> pile_tab1
|
||||
POKE dst_ptr[0] , 1
|
||||
POKE dst_ptr[1] , 34 // 9♠
|
||||
|
||||
// Move 1 card from tab0 to tab1
|
||||
move_tab_to_tab(src_ptr, dst_ptr, 1, success)
|
||||
|
||||
// Render both tableaus
|
||||
render_tableau_pile($0400, 0, src_ptr)
|
||||
render_tableau_pile($0400, 10, dst_ptr)
|
||||
FEND
|
||||
|
||||
// Test move_tab_to_tab - move stack of 3 cards
|
||||
FUNC test_tab_to_tab_stack
|
||||
WORD src_ptr @ $f6
|
||||
WORD dst_ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup src with: face-down, Q♠(black), J♦(red), 10♣(black)
|
||||
POINTER src_ptr -> pile_tab0
|
||||
POKE src_ptr[0] , 4
|
||||
POKE src_ptr[1] , 0|CARD_FACEDOWN // hidden card
|
||||
POKE src_ptr[2] , 37 // Q♠ (black)
|
||||
POKE src_ptr[3] , 23 // J♦ (red)
|
||||
POKE src_ptr[4] , 48 // 10♣ (black)
|
||||
|
||||
// Setup dst with K♥ (red) - valid for Q♠
|
||||
POINTER dst_ptr -> pile_tab1
|
||||
POKE dst_ptr[0] , 1
|
||||
POKE dst_ptr[1] , 12 // K♥
|
||||
|
||||
// Render BEFORE state
|
||||
render_tableau_pile($0400, 0, src_ptr)
|
||||
render_tableau_pile($0400, 10, dst_ptr)
|
||||
|
||||
// Wait for keypress
|
||||
wait_key()
|
||||
|
||||
// Move 3 cards (Q♠, J♦, 10♣) from tab0 to tab1
|
||||
move_tab_to_tab(src_ptr, dst_ptr, 3, success)
|
||||
|
||||
// Clear screen before redraw
|
||||
fill_mem($0400, $0400+999, 0)
|
||||
|
||||
// Render AFTER state - src should show flipped card, dst should have 4 cards
|
||||
render_tableau_pile($0400, 0, src_ptr)
|
||||
render_tableau_pile($0400, 10, dst_ptr)
|
||||
FEND
|
||||
|
||||
// Test move_tab_to_tab - King to empty tableau
|
||||
FUNC test_tab_to_tab_king_empty
|
||||
WORD src_ptr @ $f6
|
||||
WORD dst_ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup src with K♣ (black)
|
||||
POINTER src_ptr -> pile_tab0
|
||||
POKE src_ptr[0] , 1
|
||||
POKE src_ptr[1] , 51 // K♣
|
||||
|
||||
// dst is empty
|
||||
POINTER dst_ptr -> pile_tab1
|
||||
|
||||
// Move King to empty tableau
|
||||
move_tab_to_tab(src_ptr, dst_ptr, 1, success)
|
||||
|
||||
// Render
|
||||
render_tableau_pile($0400, 0, src_ptr)
|
||||
render_tableau_pile($0400, 10, dst_ptr)
|
||||
FEND
|
||||
|
||||
// Test move_found_to_tab - undo move from foundation
|
||||
FUNC test_found_to_tab
|
||||
WORD found_ptr @ $f6
|
||||
WORD tab_ptr @ $f8
|
||||
BYTE success
|
||||
|
||||
clear_all_piles()
|
||||
|
||||
// Setup foundation with A♥, 2♥, 3♥
|
||||
POINTER found_ptr -> pile_found0
|
||||
POKE found_ptr[0] , 3
|
||||
POKE found_ptr[1] , 0 // A♥
|
||||
POKE found_ptr[2] , 1 // 2♥
|
||||
POKE found_ptr[3] , 2 // 3♥
|
||||
|
||||
// Setup tableau with 4♠ (black) - valid for 3♥ (red)
|
||||
POINTER tab_ptr -> pile_tab0
|
||||
POKE tab_ptr[0] , 1
|
||||
POKE tab_ptr[1] , 29 // 4♠
|
||||
|
||||
// Move 3♥ from foundation to tableau
|
||||
move_found_to_tab(found_ptr, tab_ptr, success)
|
||||
|
||||
// Render
|
||||
render_foundation_pile($0400, 0, found_ptr)
|
||||
render_tableau_pile($0400, 10, tab_ptr)
|
||||
FEND
|
||||
|
||||
// Run all move tests sequentially (for visual inspection)
|
||||
// Each test clears screen area and renders result
|
||||
FUNC run_all_move_tests
|
||||
BYTE dummy
|
||||
|
||||
// Test 1: Stock to Waste draw-1
|
||||
test_stock_to_waste_draw1()
|
||||
// Wait for keypress or delay here if needed
|
||||
|
||||
// To run individual tests, call them directly:
|
||||
// test_stock_to_waste_draw3()
|
||||
// test_reset_stock()
|
||||
// test_waste_to_tab_valid()
|
||||
// test_waste_to_tab_king_empty()
|
||||
// test_waste_to_tab_invalid()
|
||||
// test_waste_to_found_ace()
|
||||
// test_waste_to_found_sequence()
|
||||
// test_tab_to_found()
|
||||
// test_tab_to_tab_single()
|
||||
// test_tab_to_tab_stack()
|
||||
// test_tab_to_tab_king_empty()
|
||||
// test_found_to_tab()
|
||||
FEND
|
||||
|
||||
LABEL __skip_lib_cardtests
|
||||
|
||||
#IFEND
|
||||
BIN
charpad_cards/charpad_cards.ctm
Normal file
BIN
charpad_cards/charpad_cards.ctm
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards2.ctm
Normal file
BIN
charpad_cards/charpad_cards2.ctm
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards3.ctm
Normal file
BIN
charpad_cards/charpad_cards3.ctm
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards4.ctm
Normal file
BIN
charpad_cards/charpad_cards4.ctm
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards5.ctm
Normal file
BIN
charpad_cards/charpad_cards5.ctm
Normal file
Binary file not shown.
163
charpad_cards/charpad_cards6 - Chars.asm
Normal file
163
charpad_cards/charpad_cards6 - Chars.asm
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
; CharSet Data...
|
||||
; 256 images, 8 bytes per image, total size is 2048 ($800) bytes.
|
||||
|
||||
* = addr_charset_data
|
||||
charset_data
|
||||
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$c7,$bb,$fb,$c7,$bf,$bf,$c3,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$bb,$bb,$bb,$c3,$fb,$fb,$fb,$ff
|
||||
.byte $83,$bf,$bf,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$bf,$87,$bb,$bb,$c7,$ff
|
||||
.byte $87,$fb,$fb,$f7,$ef,$df,$df,$ff,$c7,$bb,$bb,$c7,$bb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$bb,$c3,$fb,$bb,$c7,$ff,$db,$95,$d5,$d5,$d5,$d5,$db,$ff
|
||||
.byte $f3,$fb,$fb,$fb,$fb,$bb,$c7,$ff,$c7,$bb,$bb,$bb,$bb,$b3,$c3,$fd
|
||||
.byte $bb,$b7,$af,$9f,$af,$b7,$bb,$ff,$c7,$bb,$bb,$bb,$83,$bb,$bb,$ff
|
||||
.byte $ef,$c7,$83,$83,$83,$ef,$c7,$ff,$e7,$c3,$a5,$00,$00,$a5,$e7,$c3
|
||||
.byte $ff,$93,$01,$01,$83,$c7,$ef,$ff,$ef,$c7,$83,$01,$83,$c7,$ef,$ff
|
||||
.byte $a2,$55,$2a,$14,$2a,$55,$a2,$41,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $fb,$fb,$fb,$fb,$fb,$fa,$f9,$fb,$ff,$ff,$c3,$81,$00,$00,$00,$00
|
||||
.byte $df,$df,$df,$df,$df,$5f,$9f,$df,$fb,$fa,$f9,$fa,$f9,$fa,$f9,$fb
|
||||
.byte $ff,$ff,$ff,$f7,$e3,$c1,$80,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$7f
|
||||
.byte $ff,$ff,$ff,$00,$ff,$00,$ff,$ff,$ff,$ff,$ff,$ff,$e7,$c3,$81,$00
|
||||
.byte $ff,$00,$ff,$00,$ff,$00,$ff,$ff,$ff,$ff,$ff,$ff,$fe,$fc,$f8,$f0
|
||||
.byte $ff,$ff,$ff,$ff,$38,$10,$00,$00,$ff,$ff,$ff,$ff,$ff,$7f,$3f,$1f
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$ff,$ff,$ff,$ff,$ff,$00,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$ff,$00,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $f1,$e0,$c0,$c0,$c0,$c0,$e0,$f1,$00,$81,$42,$00,$00,$00,$c3,$e7
|
||||
.byte $8f,$07,$03,$03,$03,$03,$07,$8f,$fe,$fc,$f8,$f8,$f0,$f0,$f0,$f0
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$3f,$1f,$0f,$0f,$07,$07,$07,$0f
|
||||
.byte $fe,$fc,$f8,$f0,$f0,$f8,$fc,$fe,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $7f,$3f,$1f,$0f,$0f,$1f,$3f,$7f,$f0,$f0,$f0,$f8,$fc,$fe,$ff,$ff
|
||||
.byte $00,$00,$00,$00,$00,$00,$01,$83,$1f,$1f,$1f,$3f,$7f,$ff,$ff,$ff
|
||||
.byte $fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fd,$fe,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $df,$bf,$7f,$ff,$ff,$ff,$ff,$ff,$df,$df,$df,$df,$df,$df,$df,$df
|
||||
.byte $df,$5f,$9f,$5f,$9f,$5f,$9f,$df,$e7,$e7,$e7,$e7,$c3,$81,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$f8,$fc,$ff,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $14,$36,$f7,$f7,$e3,$c1,$ff,$ff,$0f,$1f,$ff,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$fe,$fd,$fa,$f9,$fb,$00,$81,$c3,$e7,$ff,$ff,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$7f,$bf,$5f,$9f,$df,$ff,$fe,$fd,$fa,$f9,$fa,$f9,$fb
|
||||
.byte $c7,$ef,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$7f,$bf,$5f,$9f,$5f,$9f,$df
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$bb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $db,$95,$d5,$d5,$d5,$d5,$db,$ff,$f3,$fb,$fb,$fb,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$bb,$bb,$bb,$b3,$c3,$fd,$bb,$b7,$af,$9f,$af,$b7,$bb,$ff
|
||||
.byte $00,$05,$0a,$14,$2a,$55,$22,$41,$00,$50,$28,$14,$2a,$54,$a2,$40
|
||||
.byte $22,$55,$2a,$14,$0a,$05,$02,$00,$a2,$54,$2a,$14,$28,$50,$a0,$00
|
||||
.byte $a2,$55,$2a,$14,$2a,$55,$a2,$41,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$fe,$fd,$fa,$f9,$fa,$f9,$fb
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$00,$ff,$ff,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$00,$ff,$ff,$fe,$fd,$fb,$f7,$f7,$f7,$fb,$fb
|
||||
.byte $38,$d7,$ef,$ff,$ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$df,$df,$bf,$bf
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$ff,$ff,$ff,$ff,$ff,$00,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$ff,$00,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $f1,$e0,$c0,$c0,$c0,$c0,$e0,$f1,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$fe,$fc,$f8,$f8,$f0,$f0,$f0,$f8
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fd,$fe,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $df,$bf,$7f,$ff,$ff,$ff,$ff,$ff,$df,$df,$df,$df,$df,$df,$df,$df
|
||||
.byte $ff,$7f,$bf,$5f,$9f,$5f,$9f,$df,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$f8,$fc,$ff,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$ff,$ff,$fe,$fd,$fa,$f9,$fb
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$ff,$ff,$ff,$7f,$bf,$5f,$9f,$df
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$bb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $db,$95,$d5,$d5,$d5,$d5,$db,$ff,$f3,$fb,$fb,$fb,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$bb,$bb,$bb,$b3,$c3,$fd,$bb,$b7,$af,$9f,$af,$b7,$bb,$ff
|
||||
.byte $00,$05,$0a,$14,$2a,$55,$22,$41,$00,$50,$28,$14,$2a,$54,$a2,$40
|
||||
.byte $22,$55,$2a,$14,$0a,$05,$02,$00,$a2,$54,$2a,$14,$28,$50,$a0,$00
|
||||
.byte $a2,$55,$2a,$14,$2a,$55,$a2,$41,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$fe,$fd,$fa,$f9,$fa,$f9,$fb
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$00,$ff,$ff,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$00,$ff,$ff,$fe,$fd,$fb,$f7,$f7,$f7,$fb,$fb
|
||||
.byte $38,$d7,$ef,$ff,$ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$df,$df,$bf,$bf
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$ff,$ff,$ff,$ff,$ff,$00,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$ff,$00,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $f1,$e0,$c0,$c0,$c0,$c0,$e0,$f1,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$fe,$fc,$f8,$f8,$f0,$f0,$f0,$f8
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fd,$fe,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $df,$bf,$7f,$ff,$ff,$ff,$ff,$ff,$df,$df,$df,$df,$df,$df,$df,$df
|
||||
.byte $ff,$7f,$bf,$5f,$9f,$5f,$9f,$df,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$f8,$fc,$ff,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$ff,$ff,$fe,$fd,$fa,$f9,$fb
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$ff,$ff,$ff,$7f,$bf,$5f,$9f,$df
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$fb,$c7,$fb,$bb,$c7,$ff,$c7,$bb,$bb,$c7,$fb,$bb,$c7,$ff
|
||||
.byte $db,$95,$d5,$d5,$d5,$d5,$db,$ff,$f3,$fb,$fb,$fb,$fb,$bb,$c7,$ff
|
||||
.byte $c7,$bb,$bb,$bb,$bb,$b3,$c3,$fd,$bb,$b7,$af,$9f,$af,$b7,$bb,$ff
|
||||
.byte $00,$05,$0a,$14,$2a,$55,$22,$41,$00,$50,$28,$14,$2a,$54,$a2,$40
|
||||
.byte $22,$55,$2a,$14,$0a,$05,$02,$00,$a2,$54,$2a,$14,$28,$50,$a0,$00
|
||||
.byte $a2,$55,$2a,$14,$2a,$55,$a2,$41,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$fe,$fd,$fa,$f9,$fa,$f9,$fb
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$00,$ff,$ff,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$00,$ff,$ff,$fe,$fd,$fb,$f7,$f7,$f7,$fb,$fb
|
||||
.byte $38,$d7,$ef,$ff,$ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$df,$df,$bf,$bf
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$ff,$ff,$ff,$ff,$ff,$00,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$ff,$00,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $f1,$e0,$c0,$c0,$c0,$c0,$e0,$f1,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$fe,$fc,$f8,$f8,$f0,$f0,$f0,$f8
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fb,$fd,$fe,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $df,$bf,$7f,$ff,$ff,$ff,$ff,$ff,$df,$df,$df,$df,$df,$df,$df,$df
|
||||
.byte $ff,$7f,$bf,$5f,$9f,$5f,$9f,$df,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$f8,$fc,$ff,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$fe,$fd,$fb,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$7f,$bf,$df,$ff,$ff,$ff,$fe,$fd,$fa,$f9,$fb
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$ff,$ff,$ff,$7f,$bf,$5f,$9f,$df
|
||||
|
||||
|
||||
|
||||
; CharSet Attribute (L1) Data...
|
||||
; 256 attributes, 1 attribute per image, 8 bits per attribute, total size is 256 ($100) bytes.
|
||||
; nb. Upper nybbles = material, lower nybbles = colour (colour matrix low).
|
||||
|
||||
* = addr_charset_attrib_L1_data
|
||||
charset_attrib_L1_data
|
||||
|
||||
.byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
|
||||
.byte $02,$02,$02,$02,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
|
||||
.byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
|
||||
.byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
|
||||
.byte $0a,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$02,$02
|
||||
.byte $02,$02,$02,$02,$01,$02,$01,$01,$02,$02,$01,$02,$01,$01,$01,$01
|
||||
.byte $02,$02,$02,$02,$02,$02,$01,$02,$02,$01,$02,$02,$02,$02,$02,$02
|
||||
.byte $02,$02,$02,$02,$01,$02,$02,$02,$02,$01,$01,$02,$01,$01,$02,$01
|
||||
.byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$02,$02
|
||||
.byte $01,$01,$02,$02,$01,$02,$01,$01,$02,$02,$01,$01,$01,$01,$01,$01
|
||||
.byte $02,$02,$02,$02,$02,$02,$01,$02,$02,$01,$01,$01,$01,$01,$01,$01
|
||||
.byte $02,$02,$02,$02,$01,$02,$02,$02,$02,$01,$01,$01,$01,$01,$01,$01
|
||||
.byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$02,$02
|
||||
.byte $02,$02,$02,$02,$01,$02,$01,$01,$02,$02,$01,$02,$01,$01,$01,$01
|
||||
.byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
|
||||
.byte $01,$01,$01,$01,$01,$02,$02,$02,$02,$01,$01,$02,$01,$01,$02,$01
|
||||
|
||||
|
||||
|
||||
BIN
charpad_cards/charpad_cards6 - Chars.bin
Normal file
BIN
charpad_cards/charpad_cards6 - Chars.bin
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards6.ctm
Normal file
BIN
charpad_cards/charpad_cards6.ctm
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards7 - Chars.bin
Normal file
BIN
charpad_cards/charpad_cards7 - Chars.bin
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards7 - Map.png
Normal file
BIN
charpad_cards/charpad_cards7 - Map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3 KiB |
BIN
charpad_cards/charpad_cards7.bin
Normal file
BIN
charpad_cards/charpad_cards7.bin
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards7.ctm
Normal file
BIN
charpad_cards/charpad_cards7.ctm
Normal file
Binary file not shown.
BIN
charpad_cards/charpad_cards7_ecm.bin
Normal file
BIN
charpad_cards/charpad_cards7_ecm.bin
Normal file
Binary file not shown.
1
charpad_cards/visualize_chars.sh
Normal file
1
charpad_cards/visualize_chars.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
xxd -b -c1 charpad_cards7.bin | cut -d' ' -f2 | tr '01' '.#'
|
||||
113
charpad_color_sprites/charpad_color_sprites - Project.asm
Normal file
113
charpad_color_sprites/charpad_color_sprites - Project.asm
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
|
||||
; Generated by SpritePad C64 - Subchrist Software, 2003-2025.
|
||||
; Assemble with 64TASS or similar.
|
||||
|
||||
|
||||
; Colour values...
|
||||
|
||||
colr_vic_bg0 = 1
|
||||
colr_vic_sprite_mc1 = 0
|
||||
colr_vic_sprite_mc2 = 1
|
||||
|
||||
|
||||
; Quantities and dimensions...
|
||||
|
||||
sprite_count = 10
|
||||
|
||||
|
||||
; Data block addresses (dummy values) and sizes...
|
||||
|
||||
addr_spriteset_data = $1000
|
||||
size_spriteset_data = $280 ; (640 bytes)
|
||||
|
||||
addr_spriteset_attrib_data = $1000
|
||||
size_spriteset_attrib_data = $a ; (10 bytes)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
; * INSERT EXAMPLE PROGRAM HERE! * (or just include this file in your project).
|
||||
|
||||
|
||||
|
||||
|
||||
; SpriteSet Data...
|
||||
; 10 images, 64 bytes per image, total size is 640 ($280) bytes.
|
||||
|
||||
* = addr_spriteset_data
|
||||
spriteset_data
|
||||
|
||||
sprite_image_0
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01
|
||||
.byte $c7,$00,$03,$ef,$80,$07,$ff,$c0,$07,$ff,$c0,$07,$ff,$c0,$03,$ff
|
||||
.byte $80,$01,$ff,$00,$00,$fe,$00,$00,$7c,$00,$00,$38,$00,$00,$10,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$12
|
||||
|
||||
sprite_image_1
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$41,$00,$00,$20,$80,$00,$00,$80,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0a
|
||||
|
||||
sprite_image_2
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$10,$00,$00
|
||||
.byte $38,$00,$00,$7c,$00,$00,$fe,$00,$01,$ff,$00,$03,$ff,$80,$03,$ff
|
||||
.byte $80,$01,$ff,$00,$00,$fe,$00,$00,$7c,$00,$00,$38,$00,$00,$10,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$12
|
||||
|
||||
sprite_image_3
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$08,$00,$00,$04,$00,$00,$02,$00,$00,$01,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0a
|
||||
|
||||
sprite_image_4
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$38,$00,$00
|
||||
.byte $7c,$00,$00,$7c,$00,$00,$7c,$00,$03,$bb,$80,$07,$d7,$c0,$07,$ff
|
||||
.byte $c0,$07,$d7,$c0,$03,$93,$80,$00,$10,$00,$00,$38,$00,$00,$7c,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$10
|
||||
|
||||
sprite_image_5
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$38,$00,$00
|
||||
.byte $0c,$00,$00,$04,$00,$00,$04,$00,$01,$83,$80,$00,$c0,$c0,$00,$00
|
||||
.byte $40,$00,$00,$40,$00,$00,$00,$00,$00,$00,$00,$08,$00,$00,$04,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0b
|
||||
|
||||
sprite_image_6
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$10,$00,$00
|
||||
.byte $38,$00,$00,$7c,$00,$00,$fe,$00,$01,$ff,$00,$03,$ff,$80,$03,$ff
|
||||
.byte $80,$03,$ff,$80,$01,$d7,$00,$00,$10,$00,$00,$38,$00,$00,$7c,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$10
|
||||
|
||||
sprite_image_7
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$10,$00,$00
|
||||
.byte $18,$00,$00,$0c,$00,$00,$06,$00,$00,$03,$00,$00,$01,$80,$00,$00
|
||||
.byte $80,$00,$00,$80,$00,$00,$00,$00,$00,$00,$00,$08,$00,$00,$04,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0b
|
||||
|
||||
sprite_image_8
|
||||
.byte $77,$47,$77,$44,$44,$42,$76,$46,$42,$14,$44,$42,$77,$77,$72,$00
|
||||
.byte $00,$00,$00,$00,$00,$77,$76,$00,$45,$55,$00,$47,$75,$00,$45,$65
|
||||
.byte $00,$75,$56,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
|
||||
sprite_image_9
|
||||
.byte $76,$00,$00,$45,$00,$00,$65,$00,$00,$45,$00,$00,$76,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
|
||||
|
||||
|
||||
; SpriteSet Attribute Data...
|
||||
; 10 attributes, 1 per image, 8 bits each, total size is 10 ($a) bytes.
|
||||
; nb. Upper nybbles = MYXV, lower nybbles = colour (0-15).
|
||||
|
||||
* = addr_spriteset_attrib_data
|
||||
spriteset_attrib_data
|
||||
|
||||
.byte $12,$0a,$12,$0a,$10,$0b,$10,$0b,$00,$00
|
||||
|
||||
|
||||
|
||||
BIN
charpad_color_sprites/charpad_color_sprites.spd
Normal file
BIN
charpad_color_sprites/charpad_color_sprites.spd
Normal file
Binary file not shown.
143
charpad_rank_sprites/charpad_rank_sprites - Project.asm
Normal file
143
charpad_rank_sprites/charpad_rank_sprites - Project.asm
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
|
||||
; Generated by SpritePad C64 - Subchrist Software, 2003-2025.
|
||||
; Assemble with 64TASS or similar.
|
||||
|
||||
|
||||
; Colour values...
|
||||
|
||||
colr_vic_bg0 = 0
|
||||
colr_vic_sprite_mc1 = 0
|
||||
colr_vic_sprite_mc2 = 1
|
||||
|
||||
|
||||
; Quantities and dimensions...
|
||||
|
||||
sprite_count = 15
|
||||
|
||||
|
||||
; Data block addresses (dummy values) and sizes...
|
||||
|
||||
addr_spriteset_data = $1000
|
||||
size_spriteset_data = $3c0 ; (960 bytes)
|
||||
|
||||
addr_spriteset_attrib_data = $1000
|
||||
size_spriteset_attrib_data = $f ; (15 bytes)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
; * INSERT EXAMPLE PROGRAM HERE! * (or just include this file in your project).
|
||||
|
||||
|
||||
|
||||
|
||||
; SpriteSet Data...
|
||||
; 15 images, 64 bytes per image, total size is 960 ($3c0) bytes.
|
||||
|
||||
* = addr_spriteset_data
|
||||
spriteset_data
|
||||
|
||||
sprite_image_0
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ef,$ff,$ff
|
||||
.byte $ef,$ff,$ff,$c7,$ff,$ff,$c7,$ff,$ff,$93,$ff,$ff,$93,$ff,$ff,$39
|
||||
.byte $ff,$ff,$01,$ff,$fe,$00,$ff,$fe,$7c,$ff,$fc,$38,$7f,$fc,$38,$7f
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_1
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$83,$ff,$ff
|
||||
.byte $01,$ff,$ff,$39,$ff,$ff,$f9,$ff,$ff,$f1,$ff,$ff,$e3,$ff,$ff,$c7
|
||||
.byte $ff,$ff,$8f,$ff,$ff,$19,$ff,$ff,$39,$ff,$ff,$01,$ff,$ff,$01,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_2
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01,$ff,$ff
|
||||
.byte $01,$ff,$ff,$33,$ff,$ff,$e7,$ff,$ff,$cf,$ff,$ff,$83,$ff,$ff,$81
|
||||
.byte $ff,$ff,$f9,$ff,$ff,$f9,$ff,$ff,$39,$ff,$ff,$01,$ff,$ff,$83,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_3
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$f3,$ff,$ff
|
||||
.byte $e3,$ff,$ff,$c3,$ff,$ff,$83,$ff,$ff,$13,$ff,$fe,$33,$ff,$fe,$00
|
||||
.byte $ff,$fe,$00,$ff,$ff,$f3,$ff,$ff,$f3,$ff,$ff,$e1,$ff,$ff,$e1,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_4
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01,$ff,$ff
|
||||
.byte $01,$ff,$ff,$3f,$ff,$ff,$3f,$ff,$ff,$03,$ff,$ff,$01,$ff,$ff,$f9
|
||||
.byte $ff,$ff,$f9,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$01,$ff,$ff,$83,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_5
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$c3,$ff,$ff
|
||||
.byte $83,$ff,$ff,$1f,$ff,$ff,$3f,$ff,$ff,$03,$ff,$ff,$01,$ff,$ff,$39
|
||||
.byte $ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$01,$ff,$ff,$83,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_6
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01,$ff,$ff
|
||||
.byte $01,$ff,$ff,$39,$ff,$ff,$f3,$ff,$ff,$f3,$ff,$ff,$e7,$ff,$ff,$e7
|
||||
.byte $ff,$ff,$e7,$ff,$ff,$cf,$ff,$ff,$cf,$ff,$ff,$cf,$ff,$ff,$cf,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_7
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$83,$ff,$ff
|
||||
.byte $01,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$83,$ff,$ff,$01
|
||||
.byte $ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$01,$ff,$ff,$83,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_8
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$83,$ff,$ff
|
||||
.byte $01,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$01
|
||||
.byte $ff,$ff,$81,$ff,$ff,$f9,$ff,$ff,$f1,$ff,$ff,$83,$ff,$ff,$87,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_9
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$fe,$61,$ff,$fe
|
||||
.byte $40,$ff,$fe,$4c,$ff,$fe,$4c,$ff,$fe,$4c,$ff,$fe,$4c,$ff,$fe,$4c
|
||||
.byte $ff,$fe,$4c,$ff,$fe,$4c,$ff,$fe,$4c,$ff,$fe,$40,$ff,$fe,$61,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_10
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$e1,$ff,$ff
|
||||
.byte $e1,$ff,$ff,$f3,$ff,$ff,$f3,$ff,$ff,$f3,$ff,$ff,$f3,$ff,$ff,$f3
|
||||
.byte $ff,$ff,$f3,$ff,$ff,$33,$ff,$ff,$33,$ff,$ff,$03,$ff,$ff,$87,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_11
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$83,$ff,$ff
|
||||
.byte $01,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39
|
||||
.byte $ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$39,$ff,$ff,$01,$ff,$ff,$83,$ff
|
||||
.byte $ff,$f1,$ff,$ff,$f9,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_12
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$fe,$10,$ff,$fe
|
||||
.byte $10,$ff,$ff,$33,$ff,$ff,$27,$ff,$ff,$0f,$ff,$ff,$1f,$ff,$ff,$0f
|
||||
.byte $ff,$ff,$27,$ff,$ff,$33,$ff,$ff,$39,$ff,$fe,$10,$ff,$fe,$10,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_13
|
||||
.byte $51,$15,$11,$55,$75,$d5,$11,$73,$b5,$55,$75,$75,$55,$15,$11,$ff
|
||||
.byte $ff,$ff,$f1,$b3,$11,$f7,$b5,$75,$f1,$b5,$31,$fd,$b5,$73,$f1,$b3
|
||||
.byte $15,$ff,$ff,$ff,$ff,$11,$11,$ff,$d5,$d7,$ff,$15,$11,$ff,$75,$75
|
||||
.byte $ff,$11,$11,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
sprite_image_14
|
||||
.byte $b3,$ff,$ff,$b5,$ff,$ff,$b5,$ff,$ff,$b5,$ff,$ff,$b3,$ff,$ff,$ff
|
||||
.byte $ff,$ff,$1f,$ff,$ff,$7f,$ff,$ff,$1f,$ff,$ff,$df,$ff,$ff,$1f,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
|
||||
.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$01
|
||||
|
||||
|
||||
|
||||
; SpriteSet Attribute Data...
|
||||
; 15 attributes, 1 per image, 8 bits each, total size is 15 ($f) bytes.
|
||||
; nb. Upper nybbles = MYXV, lower nybbles = colour (0-15).
|
||||
|
||||
* = addr_spriteset_attrib_data
|
||||
spriteset_attrib_data
|
||||
|
||||
.byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
|
||||
|
||||
|
||||
|
||||
BIN
charpad_rank_sprites/charpad_rank_sprites.png
Normal file
BIN
charpad_rank_sprites/charpad_rank_sprites.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1 KiB |
BIN
charpad_rank_sprites/charpad_rank_sprites.spd
Normal file
BIN
charpad_rank_sprites/charpad_rank_sprites.spd
Normal file
Binary file not shown.
BIN
charpad_rank_sprites/charpad_rank_sprites.xcf
Normal file
BIN
charpad_rank_sprites/charpad_rank_sprites.xcf
Normal file
Binary file not shown.
4
claude_code_docker.sh
Normal file
4
claude_code_docker.sh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
export DOCKER_UID=$(id -u)
|
||||
export DOCKER_GID=$(id -g)
|
||||
docker compose run --rm claude-solitaire
|
||||
21
cm.sh
Executable file
21
cm.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
# Define filename as variable
|
||||
PROGNAME="cardgame"
|
||||
|
||||
# Only set C65LIBPATH if not already defined
|
||||
if [ -z "$C65LIBPATH" ]; then
|
||||
export C65LIBPATH=$(readlink -f "../../lib")
|
||||
fi
|
||||
|
||||
# Compile
|
||||
c65gm -in ${PROGNAME}.c65 -out ${PROGNAME}.s
|
||||
#c65cm in:${PROGNAME}.c65 out:${PROGNAME}.s hidelicense
|
||||
echo assemble.
|
||||
acme ${PROGNAME}.s
|
||||
|
||||
# Only remove if exists
|
||||
if [ -f ${PROGNAME}.prg ]; then
|
||||
rm ${PROGNAME}.prg
|
||||
fi
|
||||
#mv main.bin ${PROGNAME}.prg
|
||||
mv main.bin main.prg
|
||||
16
docker-compose.yml
Normal file
16
docker-compose.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
services:
|
||||
claude-solitaire:
|
||||
build: .
|
||||
user: "${DOCKER_UID}:${DOCKER_GID}"
|
||||
volumes:
|
||||
- .:/app
|
||||
environment:
|
||||
#- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||
- CLAUDE_CONFIG_DIR=/app/.claude
|
||||
- HOME=/app
|
||||
- DISABLE_AUTOUPDATER=1
|
||||
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
|
||||
- DISABLE_TELEMETRY=1
|
||||
- DISABLE_ERROR_REPORTING=1
|
||||
stdin_open: true
|
||||
tty: true
|
||||
1
exomizer_compress_prg.sh
Normal file
1
exomizer_compress_prg.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
exomizer sfx basic main.prg -o main2.prg
|
||||
967
gameloop.c65
Normal file
967
gameloop.c65
Normal file
|
|
@ -0,0 +1,967 @@
|
|||
|
||||
#IFNDEF __lib_gameloop
|
||||
#DEFINE __lib_gameloop 1
|
||||
|
||||
#INCLUDE "cardconsts.c65"
|
||||
#INCLUDE "piles.c65"
|
||||
#INCLUDE "cardmoves.c65"
|
||||
#INCLUDE "cardrender.c65"
|
||||
#INCLUDE "mouse.c65"
|
||||
#INCLUDE "pointer.c65"
|
||||
#INCLUDE "cardsprites.c65"
|
||||
|
||||
GOTO __skip_lib_gameloop
|
||||
|
||||
// ============================================================================
|
||||
// SOLITAIRE GAME LOOP AND INTERACTION SYSTEM
|
||||
// ============================================================================
|
||||
// This library provides the main game loop, coordinate-to-pile mapping,
|
||||
// click detection, selection state, and selective rendering.
|
||||
//
|
||||
// Usage:
|
||||
// #INCLUDE "gameloop.c65"
|
||||
// game_init()
|
||||
// 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
|
||||
|
||||
// Layout constants (in character coordinates)
|
||||
BYTE CONST LAYOUT_STOCK_COL = 0
|
||||
BYTE CONST LAYOUT_STOCK_ROW = 0
|
||||
BYTE CONST LAYOUT_WASTE_COL = 6
|
||||
BYTE CONST LAYOUT_WASTE_ROW = 0
|
||||
BYTE CONST LAYOUT_FOUND_COL = 16 // First foundation column
|
||||
BYTE CONST LAYOUT_FOUND_ROW = 0
|
||||
BYTE CONST LAYOUT_TAB_COL = 0 // First tableau column
|
||||
BYTE CONST LAYOUT_TAB_ROW = 8
|
||||
|
||||
BYTE CONST PILE_WIDTH = 5 // Cards are 5 chars wide
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_to_char_coords
|
||||
// Convert sprite pixel coordinates to character coordinates
|
||||
// C64 sprite coords start at (24,50) for top-left of visible screen
|
||||
// Sprite coords: X (0-511), Y (0-250)
|
||||
// Char coords: col (0-39), row (0-24)
|
||||
// ============================================================================
|
||||
FUNC pointer_to_char_coords({WORD sprite_x} {BYTE sprite_y} out:{BYTE char_col} out:{BYTE char_row})
|
||||
WORD temp_x
|
||||
BYTE temp_y
|
||||
|
||||
// Subtract sprite offset (24 pixels X, 50 pixels Y)
|
||||
temp_x = sprite_x - 24
|
||||
temp_y = sprite_y - 50
|
||||
|
||||
// Divide X by 8 (shift right 3 times)
|
||||
// For WORD: need to shift high byte into low byte
|
||||
ASM
|
||||
lda |temp_x|+1 // High byte
|
||||
lsr // Shift right 1
|
||||
sta |temp_x|+1
|
||||
lda |temp_x| // Low byte
|
||||
ror // Rotate right (carry from high byte)
|
||||
lsr // Shift right 1
|
||||
lsr // Shift right 1
|
||||
sta |char_col|
|
||||
ENDASM
|
||||
|
||||
// Divide Y by 8 (shift right 3 times)
|
||||
ASM
|
||||
lda |temp_y|
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
sta |char_row|
|
||||
ENDASM
|
||||
|
||||
// Bounds checking (handle underflow as well)
|
||||
IF char_col > 39
|
||||
char_col = 0
|
||||
ENDIF
|
||||
|
||||
IF char_row > 24
|
||||
char_row = 0
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC get_pile_at_coords
|
||||
// Determine which pile (if any) is at the given character coordinates
|
||||
// Returns PILE_ID_* constant
|
||||
// ============================================================================
|
||||
FUNC get_pile_at_coords({BYTE char_col} {BYTE char_row} out:{BYTE pile_id})
|
||||
pile_id = PILE_ID_NONE
|
||||
|
||||
// Check top row (stock, waste, foundations)
|
||||
IF char_row < 7 // Cards are 7 rows tall
|
||||
|
||||
// Stock: cols 0-4
|
||||
IF char_col >= LAYOUT_STOCK_COL
|
||||
IF char_col < LAYOUT_STOCK_COL+PILE_WIDTH
|
||||
pile_id = PILE_ID_STOCK
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Waste: cols 6-14 (fanned display is 9 chars wide)
|
||||
IF char_col >= LAYOUT_WASTE_COL
|
||||
IF char_col < LAYOUT_WASTE_COL+9
|
||||
pile_id = PILE_ID_WASTE
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Foundations: cols 16-35 (4 foundations x 5 chars, with spacing)
|
||||
IF char_col >= LAYOUT_FOUND_COL
|
||||
// Foundation 0: 16-20
|
||||
IF char_col < LAYOUT_FOUND_COL+PILE_WIDTH
|
||||
pile_id = PILE_ID_FOUND0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Foundation 1: 21-25
|
||||
IF char_col >= 21
|
||||
IF char_col < 21+PILE_WIDTH
|
||||
pile_id = PILE_ID_FOUND1
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Foundation 2: 26-30
|
||||
IF char_col >= 26
|
||||
IF char_col < 26+PILE_WIDTH
|
||||
pile_id = PILE_ID_FOUND2
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Foundation 3: 31-35
|
||||
IF char_col >= 31
|
||||
IF char_col < 31+PILE_WIDTH
|
||||
pile_id = PILE_ID_FOUND3
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Check tableau row (row 8+)
|
||||
#PRAGMA _P_USE_LONG_JUMP 1
|
||||
IF char_row >= LAYOUT_TAB_ROW
|
||||
#PRAGMA _P_USE_LONG_JUMP 0
|
||||
// Tableaus can extend many rows down
|
||||
// Each tableau: 5 chars wide + 1 char gap (except last)
|
||||
|
||||
// Tableau 0: cols 0-4
|
||||
IF char_col < 5
|
||||
pile_id = PILE_ID_TAB0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Tableau 1: cols 5-9
|
||||
IF char_col >= 5
|
||||
IF char_col < 10
|
||||
pile_id = PILE_ID_TAB1
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Tableau 2: cols 10-14
|
||||
IF char_col >= 10
|
||||
IF char_col < 15
|
||||
pile_id = PILE_ID_TAB2
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Tableau 3: cols 15-19
|
||||
IF char_col >= 15
|
||||
IF char_col < 20
|
||||
pile_id = PILE_ID_TAB3
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Tableau 4: cols 20-24
|
||||
IF char_col >= 20
|
||||
IF char_col < 25
|
||||
pile_id = PILE_ID_TAB4
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Tableau 5: cols 25-29
|
||||
IF char_col >= 25
|
||||
IF char_col < 30
|
||||
pile_id = PILE_ID_TAB5
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Tableau 6: cols 30-34
|
||||
IF char_col >= 30
|
||||
IF char_col < 35
|
||||
pile_id = PILE_ID_TAB6
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pile_id_to_pointer
|
||||
// Convert pile ID to pile data pointer
|
||||
// ============================================================================
|
||||
FUNC pile_id_to_pointer({BYTE pile_id} out:{WORD pile_ptr})
|
||||
WORD ptr @ $fa
|
||||
|
||||
SWITCH pile_id
|
||||
CASE PILE_ID_STOCK
|
||||
POINTER ptr -> pile_stock
|
||||
CASE PILE_ID_WASTE
|
||||
POINTER ptr -> pile_waste
|
||||
CASE PILE_ID_FOUND0
|
||||
POINTER ptr -> pile_found0
|
||||
CASE PILE_ID_FOUND1
|
||||
POINTER ptr -> pile_found1
|
||||
CASE PILE_ID_FOUND2
|
||||
POINTER ptr -> pile_found2
|
||||
CASE PILE_ID_FOUND3
|
||||
POINTER ptr -> pile_found3
|
||||
CASE PILE_ID_TAB0
|
||||
POINTER ptr -> pile_tab0
|
||||
CASE PILE_ID_TAB1
|
||||
POINTER ptr -> pile_tab1
|
||||
CASE PILE_ID_TAB2
|
||||
POINTER ptr -> pile_tab2
|
||||
CASE PILE_ID_TAB3
|
||||
POINTER ptr -> pile_tab3
|
||||
CASE PILE_ID_TAB4
|
||||
POINTER ptr -> pile_tab4
|
||||
CASE PILE_ID_TAB5
|
||||
POINTER ptr -> pile_tab5
|
||||
CASE PILE_ID_TAB6
|
||||
POINTER ptr -> pile_tab6
|
||||
DEFAULT
|
||||
ptr = 0
|
||||
ENDSWITCH
|
||||
|
||||
pile_ptr = ptr
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC get_tableau_card_count
|
||||
// Determine how many cards to select from tableau based on click position
|
||||
// Scans screen memory to find which card was clicked, then counts from there
|
||||
// Returns the number of cards from the clicked position to the top
|
||||
// ============================================================================
|
||||
FUNC get_tableau_card_count({WORD pile_ptr @ $f8} {BYTE click_row} {BYTE tableau_col} out:{BYTE card_count})
|
||||
BYTE count
|
||||
BYTE faceup_count
|
||||
BYTE card
|
||||
BYTE is_facedown
|
||||
BYTE i
|
||||
BYTE screen_char
|
||||
BYTE card_rank
|
||||
BYTE card_suit
|
||||
BYTE card_id
|
||||
BYTE clicked_card_id
|
||||
BYTE found_match
|
||||
BYTE match_index
|
||||
BYTE rank_char
|
||||
WORD rank_map_ptr @ $f4
|
||||
WORD screen_pos @ $f6
|
||||
WORD row_offset
|
||||
|
||||
count = PEEK pile_ptr[0]
|
||||
IF count == 0
|
||||
card_count = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Count face-up cards and find top card
|
||||
faceup_count = 0
|
||||
FOR i = 1 TO count
|
||||
card = PEEK pile_ptr[i]
|
||||
is_facedown = card & CARD_FACEDOWN
|
||||
IF is_facedown == 0
|
||||
faceup_count++
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
IF faceup_count == 0
|
||||
card_count = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Read screen character at click position to identify clicked card
|
||||
// Tableau columns: 0,5,10,15,20,25,30 - rank is at column +1
|
||||
|
||||
// Calculate screen position for clicked row
|
||||
// row_offset = click_row * 40
|
||||
row_offset = 0
|
||||
FOR i = 1 TO click_row
|
||||
row_offset = row_offset + 40
|
||||
NEXT
|
||||
|
||||
// Rank character is at tableau_col + 1 (see cardrender.c65:113)
|
||||
screen_pos = $0400 + row_offset
|
||||
screen_pos = screen_pos + tableau_col
|
||||
screen_pos = screen_pos + 1 // Rank is at offset +1
|
||||
screen_char = PEEK screen_pos[0]
|
||||
|
||||
// Debug: show screen character we read
|
||||
//POKE $0400+35 , screen_char
|
||||
|
||||
// Try to match screen character to a rank
|
||||
POINTER rank_map_ptr -> card_charcode_map
|
||||
clicked_card_id = $FF
|
||||
found_match = 0
|
||||
|
||||
// Scan face-up cards in pile to find match (bottom to top)
|
||||
FOR i = 1 TO count
|
||||
card = PEEK pile_ptr[i]
|
||||
is_facedown = card & CARD_FACEDOWN
|
||||
IF is_facedown == 0
|
||||
card_id = card & CARD_MASK
|
||||
card_id_to_suit_rank(card_id, card_suit, card_rank)
|
||||
|
||||
// Get rank character and compare
|
||||
rank_char = PEEK rank_map_ptr[card_rank]
|
||||
|
||||
// Adjust for color (red cards have +64)
|
||||
IF card_suit < 2
|
||||
rank_char = rank_char + 64
|
||||
ENDIF
|
||||
|
||||
IF rank_char == screen_char
|
||||
// Found match - use this card
|
||||
clicked_card_id = card_id
|
||||
match_index = i
|
||||
found_match = 1
|
||||
BREAK // Exit loop early
|
||||
ENDIF
|
||||
ENDIF
|
||||
NEXT
|
||||
|
||||
// If no match found, select top card
|
||||
IF found_match == 0
|
||||
card_count = 1
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Count cards from matched position to top
|
||||
card_count = count - match_index
|
||||
card_count = card_count + 1
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC get_selected_card_info
|
||||
// Returns info about currently selected card (if any)
|
||||
// Returns card_id (0-51) and valid flag (1=valid, 0=no selection)
|
||||
// ============================================================================
|
||||
FUNC get_selected_card_info(out:{BYTE card_id} out:{BYTE valid})
|
||||
WORD pile_ptr @ $fa
|
||||
BYTE pile_count
|
||||
BYTE card_index
|
||||
BYTE card
|
||||
|
||||
valid = 0
|
||||
card_id = 0
|
||||
|
||||
// No selection
|
||||
IF game_selected_pile == PILE_ID_NONE
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Get pile pointer
|
||||
pile_id_to_pointer(game_selected_pile, pile_ptr)
|
||||
IF pile_ptr == 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
pile_count = PEEK pile_ptr[0]
|
||||
IF pile_count == 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Waste: top card
|
||||
IF game_selected_pile == PILE_ID_WASTE
|
||||
card = PEEK pile_ptr[pile_count]
|
||||
card_id = card & CARD_MASK
|
||||
valid = 1
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Tableau: bottom card of selection (the clicked card)
|
||||
IF game_selected_pile >= PILE_ID_TAB0
|
||||
IF game_selected_pile <= PILE_ID_TAB6
|
||||
// Calculate index of clicked card (bottom of selection)
|
||||
card_index = pile_count - game_selected_card_count
|
||||
card_index = card_index + 1
|
||||
card = PEEK pile_ptr[card_index]
|
||||
card_id = card & CARD_MASK
|
||||
valid = 1
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC game_init
|
||||
// Initialize game state and input devices
|
||||
// ============================================================================
|
||||
FUNC game_init
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
game_selected_card_count = 0
|
||||
game_prev_button_state = 0
|
||||
|
||||
// Initialize joystick (Port 2)
|
||||
joy_state = 0
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC detect_click
|
||||
// Detect button click (press and release)
|
||||
// Returns 1 if clicked, 0 if not
|
||||
// ============================================================================
|
||||
FUNC detect_click({BYTE current_button_state} out:{BYTE clicked})
|
||||
clicked = 0
|
||||
|
||||
// Check for left mouse button (bit 4, as shown in test)
|
||||
// Button is active-low, so 0 = pressed
|
||||
BYTE current_pressed
|
||||
BYTE prev_pressed
|
||||
BYTE temp
|
||||
|
||||
// Check if current button is pressed (bit 4 = 0)
|
||||
temp = current_button_state & $10
|
||||
current_pressed = 0
|
||||
IF temp == 0
|
||||
current_pressed = 1
|
||||
ENDIF
|
||||
|
||||
// Check if previous button was pressed
|
||||
temp = game_prev_button_state & $10
|
||||
prev_pressed = 0
|
||||
IF temp == 0
|
||||
prev_pressed = 1
|
||||
ENDIF
|
||||
|
||||
// Click = was pressed, now released
|
||||
IF prev_pressed == 1
|
||||
IF current_pressed == 0
|
||||
clicked = 1
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
game_prev_button_state = current_button_state
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC clear_tableau_column
|
||||
// Clear a tableau column from row 8 to bottom of screen
|
||||
// ============================================================================
|
||||
FUNC clear_tableau_column({BYTE col_offset})
|
||||
WORD pos @ $f6
|
||||
BYTE row
|
||||
BYTE i
|
||||
|
||||
// Clear from row 8 to row 24 (17 rows)
|
||||
// 8*40+$0400 is constant expression, then add variable col_offset
|
||||
pos = 8*40+$0400 + col_offset
|
||||
FOR row = 8 TO 24
|
||||
// Clear 5 characters wide (card width)
|
||||
FOR i = 0 TO 4
|
||||
POKE pos[i] , 0
|
||||
NEXT
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC clear_waste_area
|
||||
// Clear the waste pile area (9 chars wide for fanned display)
|
||||
// ============================================================================
|
||||
FUNC clear_waste_area
|
||||
WORD pos @ $f6
|
||||
BYTE row
|
||||
BYTE i
|
||||
|
||||
// Clear waste area: row 0, cols 6-14 (9 chars), 7 rows tall
|
||||
pos = 6+$0400
|
||||
FOR row = 0 TO 6
|
||||
FOR i = 0 TO 8
|
||||
POKE pos[i] , 0
|
||||
NEXT
|
||||
pos = pos + 40
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC render_pile_by_id
|
||||
// Render a specific pile by ID to its screen location
|
||||
// Clears the area first to remove artifacts from previous render
|
||||
// ============================================================================
|
||||
FUNC render_pile_by_id({BYTE pile_id})
|
||||
WORD pile_ptr @ $fa
|
||||
WORD screen_offset
|
||||
|
||||
pile_id_to_pointer(pile_id, pile_ptr)
|
||||
IF pile_ptr == 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
SWITCH pile_id
|
||||
CASE PILE_ID_STOCK
|
||||
render_stock_pile($0400, 0, pile_ptr)
|
||||
|
||||
CASE PILE_ID_WASTE
|
||||
clear_waste_area()
|
||||
render_waste_pile($0400, 6, pile_ptr, game_draw_mode)
|
||||
|
||||
CASE PILE_ID_FOUND0
|
||||
render_foundation_pile($0400, 16, pile_ptr)
|
||||
|
||||
CASE PILE_ID_FOUND1
|
||||
render_foundation_pile($0400, 21, pile_ptr)
|
||||
|
||||
CASE PILE_ID_FOUND2
|
||||
render_foundation_pile($0400, 26, pile_ptr)
|
||||
|
||||
CASE PILE_ID_FOUND3
|
||||
render_foundation_pile($0400, 31, pile_ptr)
|
||||
|
||||
CASE PILE_ID_TAB0
|
||||
clear_tableau_column(0)
|
||||
render_tableau_pile($0400, 8*40, pile_ptr)
|
||||
|
||||
CASE PILE_ID_TAB1
|
||||
clear_tableau_column(5)
|
||||
render_tableau_pile($0400, 8*40+5, pile_ptr)
|
||||
|
||||
CASE PILE_ID_TAB2
|
||||
clear_tableau_column(10)
|
||||
render_tableau_pile($0400, 8*40+10, pile_ptr)
|
||||
|
||||
CASE PILE_ID_TAB3
|
||||
clear_tableau_column(15)
|
||||
render_tableau_pile($0400, 8*40+15, pile_ptr)
|
||||
|
||||
CASE PILE_ID_TAB4
|
||||
clear_tableau_column(20)
|
||||
render_tableau_pile($0400, 8*40+20, pile_ptr)
|
||||
|
||||
CASE PILE_ID_TAB5
|
||||
clear_tableau_column(25)
|
||||
render_tableau_pile($0400, 8*40+25, pile_ptr)
|
||||
|
||||
CASE PILE_ID_TAB6
|
||||
clear_tableau_column(30)
|
||||
render_tableau_pile($0400, 8*40+30, pile_ptr)
|
||||
ENDSWITCH
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC check_win_condition
|
||||
// Check if all 4 foundations are complete (13 cards each = King on top)
|
||||
// Returns 1 if won, 0 otherwise
|
||||
// ============================================================================
|
||||
FUNC check_win_condition(out:{BYTE is_won})
|
||||
WORD ptr @ $fa
|
||||
BYTE count
|
||||
|
||||
is_won = 1 // Assume won, set to 0 if any foundation incomplete
|
||||
|
||||
// Check foundation 0
|
||||
POINTER ptr -> pile_found0
|
||||
count = PEEK ptr[0]
|
||||
IF count != 13
|
||||
is_won = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Check foundation 1
|
||||
POINTER ptr -> pile_found1
|
||||
count = PEEK ptr[0]
|
||||
IF count != 13
|
||||
is_won = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Check foundation 2
|
||||
POINTER ptr -> pile_found2
|
||||
count = PEEK ptr[0]
|
||||
IF count != 13
|
||||
is_won = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Check foundation 3
|
||||
POINTER ptr -> pile_found3
|
||||
count = PEEK ptr[0]
|
||||
IF count != 13
|
||||
is_won = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC handle_click_on_pile
|
||||
// Handle a click on a specific pile
|
||||
// Implements game logic for selection and move execution
|
||||
// ============================================================================
|
||||
FUNC handle_click_on_pile({BYTE clicked_pile} {BYTE click_row})
|
||||
BYTE is_foundation
|
||||
BYTE is_tableau
|
||||
BYTE selected_is_tableau
|
||||
BYTE success
|
||||
WORD src_ptr
|
||||
WORD dst_ptr
|
||||
WORD tab_ptr @ $f8
|
||||
BYTE tab_col
|
||||
BYTE tab_index
|
||||
BYTE j
|
||||
|
||||
// Determine pile type
|
||||
is_foundation = 0
|
||||
IF clicked_pile >= PILE_ID_FOUND0
|
||||
IF clicked_pile <= PILE_ID_FOUND3
|
||||
is_foundation = 1
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
is_tableau = 0
|
||||
IF clicked_pile >= PILE_ID_TAB0
|
||||
IF clicked_pile <= PILE_ID_TAB6
|
||||
is_tableau = 1
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// If nothing selected, select this pile
|
||||
#PRAGMA _P_USE_LONG_JUMP 1
|
||||
IF game_selected_pile == PILE_ID_NONE
|
||||
#PRAGMA _P_USE_LONG_JUMP 0
|
||||
|
||||
// Can only select waste or tableau
|
||||
IF clicked_pile == PILE_ID_WASTE
|
||||
game_selected_pile = PILE_ID_WASTE
|
||||
game_selected_card_count = 1
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
IF clicked_pile == PILE_ID_STOCK
|
||||
// Stock: draw cards
|
||||
WORD stock_ptr @ $fa
|
||||
POINTER stock_ptr -> pile_stock
|
||||
move_stock_to_waste(game_draw_mode, success)
|
||||
IF success
|
||||
render_pile_by_id(PILE_ID_STOCK)
|
||||
render_pile_by_id(PILE_ID_WASTE)
|
||||
ELSE
|
||||
// Stock empty, reset from waste
|
||||
move_reset_stock(success)
|
||||
IF success
|
||||
render_pile_by_id(PILE_ID_STOCK)
|
||||
render_pile_by_id(PILE_ID_WASTE)
|
||||
ENDIF
|
||||
ENDIF
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
#PRAGMA _P_USE_LONG_JUMP 1
|
||||
IF is_tableau
|
||||
#PRAGMA _P_USE_LONG_JUMP 0
|
||||
// Select tableau - calculate how many cards based on click position
|
||||
pile_id_to_pointer(clicked_pile, tab_ptr)
|
||||
|
||||
// Calculate tableau column: 0,5,10,15,20,25,30
|
||||
tab_index = clicked_pile - PILE_ID_TAB0
|
||||
tab_col = 0
|
||||
FOR j = 1 TO tab_index
|
||||
tab_col = tab_col + 5
|
||||
NEXT
|
||||
|
||||
get_tableau_card_count(tab_ptr, click_row, tab_col, game_selected_card_count)
|
||||
IF game_selected_card_count > 0
|
||||
game_selected_pile = clicked_pile
|
||||
ENDIF
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
// Something is selected, try to move to destination
|
||||
pile_id_to_pointer(game_selected_pile, src_ptr)
|
||||
pile_id_to_pointer(clicked_pile, dst_ptr)
|
||||
|
||||
// Waste to Foundation
|
||||
IF game_selected_pile == PILE_ID_WASTE
|
||||
IF is_foundation
|
||||
move_waste_to_found(dst_ptr, success)
|
||||
IF success
|
||||
render_pile_by_id(PILE_ID_WASTE)
|
||||
render_pile_by_id(clicked_pile)
|
||||
ENDIF
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Waste to Tableau
|
||||
IF game_selected_pile == PILE_ID_WASTE
|
||||
IF is_tableau
|
||||
move_waste_to_tab(dst_ptr, success)
|
||||
IF success
|
||||
render_pile_by_id(PILE_ID_WASTE)
|
||||
render_pile_by_id(clicked_pile)
|
||||
ENDIF
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
EXIT
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Tableau to Foundation/Tableau - check if selected pile is tableau
|
||||
selected_is_tableau = 0
|
||||
IF game_selected_pile >= PILE_ID_TAB0
|
||||
IF game_selected_pile <= PILE_ID_TAB6
|
||||
selected_is_tableau = 1
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
IF selected_is_tableau
|
||||
IF is_foundation
|
||||
move_tab_to_found(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
|
||||
|
||||
// Tableau to Tableau
|
||||
IF selected_is_tableau
|
||||
IF is_tableau
|
||||
move_tab_to_tab(src_ptr, dst_ptr, game_selected_card_count, 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
|
||||
|
||||
// Click on same pile or invalid destination: deselect
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC render_all_piles_initial
|
||||
// Render all piles for initial game display
|
||||
// ============================================================================
|
||||
FUNC render_all_piles_initial
|
||||
BYTE pile_id
|
||||
pile_id = PILE_ID_STOCK
|
||||
|
||||
WHILE pile_id <= PILE_ID_TAB6
|
||||
render_pile_by_id(pile_id)
|
||||
pile_id++
|
||||
WEND
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC game_loop
|
||||
// Main game loop - never returns
|
||||
// ============================================================================
|
||||
FUNC game_loop
|
||||
WORD sprite_x
|
||||
BYTE sprite_y
|
||||
BYTE char_col
|
||||
BYTE char_row
|
||||
BYTE pile_at_cursor
|
||||
BYTE button_state
|
||||
BYTE clicked
|
||||
BYTE is_won
|
||||
WORD src_ptr @ $fa
|
||||
WORD src_end_ptr
|
||||
WORD dst_ptr @ $fc
|
||||
|
||||
// Copy sprite data to $2200 (sprite block 136)
|
||||
// $2000-$21FF reserved for charset
|
||||
POINTER src_ptr -> pointer_sprite_data
|
||||
POINTER src_end_ptr -> pointer_sprite_data_end
|
||||
POINTER dst_ptr -> $2200
|
||||
mem_copy(src_ptr, src_end_ptr, dst_ptr)
|
||||
|
||||
// Copy card sprite data to $2240 (sprite blocks 137+)
|
||||
// 25 sprites × 64 bytes = 1600 bytes
|
||||
POINTER src_ptr -> sprite_rank_ace
|
||||
POINTER dst_ptr -> $2240
|
||||
mem_copy_range(src_ptr, dst_ptr, 64*25)
|
||||
|
||||
// Initialize mouse and pointer
|
||||
mouse_init()
|
||||
pointer_init(160, 100, color_red, 136) // Sprite block 136 = $2200, bright red color
|
||||
|
||||
// Enable sprite
|
||||
pointer_enable(1)
|
||||
|
||||
// Initialize card display sprites
|
||||
card_display_init()
|
||||
|
||||
#IFDEF TEST_GAMES
|
||||
// Test game options (comment/uncomment one):
|
||||
//setup_test_game_tall_tableau() // K->3 in tab0, red 2 in waste
|
||||
//setup_test_game_one_move_to_win() // 1 move from victory
|
||||
setup_test_game_overflow() // K->A in tab3 to test screen overflow
|
||||
#IFEND
|
||||
|
||||
// Initial render
|
||||
fill_mem($0400, $0400+999, 0) // Clear screen
|
||||
render_all_piles_initial()
|
||||
|
||||
BYTE raster @ $d012
|
||||
|
||||
WHILE 1
|
||||
// Wait for raster to avoid tearing
|
||||
WHILE raster != 250
|
||||
WEND
|
||||
|
||||
// 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)
|
||||
|
||||
// Get pointer position
|
||||
pointer_get_x(sprite_x)
|
||||
pointer_get_y(sprite_y)
|
||||
|
||||
// Convert to character coordinates
|
||||
pointer_to_char_coords(sprite_x, sprite_y, char_col, char_row)
|
||||
|
||||
// Get pile under cursor
|
||||
get_pile_at_coords(char_col, char_row, pile_at_cursor)
|
||||
|
||||
#IFDEF TEST_GAMES
|
||||
// Visual feedback - show selection state and hover
|
||||
IF game_selected_pile != PILE_ID_NONE
|
||||
// Something selected - show with cyan border
|
||||
POKE $d020 , color_cyan
|
||||
ELSE
|
||||
// Nothing selected - grey when hovering over pile, white otherwise
|
||||
IF pile_at_cursor != PILE_ID_NONE
|
||||
POKE $d020 , color_light_grey
|
||||
ELSE
|
||||
POKE $d020 , color_white
|
||||
ENDIF
|
||||
ENDIF
|
||||
#IFEND
|
||||
// Detect click from both mouse button (bit 4) and joystick fire (bit 4)
|
||||
// Mouse buttons: bit 4 = left button (active low, 0=pressed)
|
||||
// Joystick state: bit 4 = fire button (active high, 1=pressed after inversion)
|
||||
// Combine both: if either has bit 4 clear (mouse) or set (joy), trigger click
|
||||
BYTE combined_button
|
||||
combined_button = mouse_buttons & %00010000 // Mouse: 0=pressed
|
||||
IF combined_button != 0
|
||||
// Mouse not pressed, check joystick
|
||||
combined_button = joy_state & %00010000 // Joy: 1=pressed
|
||||
IF combined_button
|
||||
combined_button = 0 // Make it 0 (pressed) like mouse
|
||||
ELSE
|
||||
combined_button = %00010000 // Not pressed
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
button_state = combined_button
|
||||
detect_click(button_state, clicked)
|
||||
|
||||
// Handle click if occurred
|
||||
IF clicked
|
||||
#IFDEF TEST_GAMES
|
||||
// Visual feedback - flash border on click
|
||||
POKE $d020 , color_yellow
|
||||
#IFEND
|
||||
|
||||
IF pile_at_cursor != PILE_ID_NONE
|
||||
handle_click_on_pile(pile_at_cursor, char_row)
|
||||
ELSE
|
||||
// Click on empty area: deselect
|
||||
game_selected_pile = PILE_ID_NONE
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Update selected card display (upper right corner using sprites)
|
||||
BYTE display_card_id
|
||||
BYTE display_valid
|
||||
BYTE display_rank
|
||||
BYTE display_suit
|
||||
|
||||
get_selected_card_info(display_card_id, display_valid)
|
||||
IF display_valid
|
||||
// Show selected card rank and suit using sprites
|
||||
card_id_to_suit_rank(display_card_id, display_suit, display_rank)
|
||||
card_display_show(display_rank, display_suit)
|
||||
ELSE
|
||||
// Hide sprites when nothing selected
|
||||
card_display_hide()
|
||||
ENDIF
|
||||
|
||||
// Check win condition
|
||||
check_win_condition(is_won)
|
||||
IF is_won
|
||||
// Flash border or show message
|
||||
POKE $d020 , color_green
|
||||
// Could add "YOU WIN" message here
|
||||
// For now, just keep running to allow admiring the win
|
||||
ENDIF
|
||||
|
||||
// Small delay to avoid reading mouse too fast
|
||||
// Could sync to raster if needed for smoother experience
|
||||
WEND
|
||||
FEND
|
||||
|
||||
|
||||
LABEL __skip_lib_gameloop
|
||||
|
||||
#IFEND
|
||||
72
joystick.c65
Normal file
72
joystick.c65
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
#IFNDEF __lib_joystick
|
||||
#DEFINE __lib_joystick 1
|
||||
|
||||
GOTO __skip_lib_joystick
|
||||
|
||||
// ============================================================================
|
||||
// JOYSTICK INPUT DRIVER FOR COMMODORE 64
|
||||
// ============================================================================
|
||||
// This library provides joystick input reading for the Commodore 64.
|
||||
//
|
||||
// Joystick reading uses CIA port 2 ($DC00) which is preferred for games
|
||||
// Port 1 ($DC01) conflicts with keyboard matrix scanning
|
||||
//
|
||||
// Usage:
|
||||
// #INCLUDE "joystick.c65"
|
||||
// WHILE 1
|
||||
// joy_read()
|
||||
// BYTE test_bit
|
||||
// test_bit = joy_state & JOY_UP_MASK
|
||||
// IF test_bit
|
||||
// // handle up
|
||||
// ENDIF
|
||||
// WEND
|
||||
// ============================================================================
|
||||
|
||||
// CIA Port addresses
|
||||
WORD CONST JOY_PORT1 = $DC01 // Port 1 (conflicts with keyboard)
|
||||
WORD CONST JOY_PORT2 = $DC00 // Port 2 (recommended for games)
|
||||
|
||||
// Joystick bit masks (active low - 0 = pressed)
|
||||
BYTE CONST JOY_UP_MASK = %00000001 // Bit 0
|
||||
BYTE CONST JOY_DOWN_MASK = %00000010 // Bit 1
|
||||
BYTE CONST JOY_LEFT_MASK = %00000100 // Bit 2
|
||||
BYTE CONST JOY_RIGHT_MASK = %00001000 // Bit 3
|
||||
BYTE CONST JOY_FIRE_MASK = %00010000 // Bit 4
|
||||
|
||||
// Joystick state variable
|
||||
BYTE joy_state // Inverted joystick state (1=pressed, 0=not pressed)
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC joy_read_port1
|
||||
// Read joystick port 1 (alternative port, may conflict with keyboard)
|
||||
// Updates the same joy_state variable as joy_read()
|
||||
// ============================================================================
|
||||
FUNC joy_read_port1
|
||||
BYTE joy_raw
|
||||
|
||||
joy_raw = PEEK JOY_PORT1
|
||||
joy_state = joy_raw ^ $FF // Invert bits: 1=pressed, 0=not pressed
|
||||
FEND
|
||||
|
||||
// ============================================================================
|
||||
// FUNC joy_read
|
||||
// Read joystick port 2 and update state variable
|
||||
// Call this every frame to update joystick state
|
||||
// Inverts active-low CIA bits to active-high (1=pressed, 0=not pressed)
|
||||
// ============================================================================
|
||||
FUNC joy_read_port2
|
||||
BYTE joy_raw
|
||||
|
||||
joy_raw = PEEK JOY_PORT2
|
||||
joy_state = joy_raw ^ $FF // Invert bits: 1=pressed, 0=not pressed
|
||||
FEND
|
||||
|
||||
|
||||
|
||||
LABEL __skip_lib_joystick
|
||||
|
||||
#IFEND
|
||||
272
joysticktests.c65
Normal file
272
joysticktests.c65
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
|
||||
#IFNDEF __lib_joysticktests
|
||||
#DEFINE __lib_joysticktests 1
|
||||
|
||||
#INCLUDE "joystick.c65"
|
||||
#INCLUDE "mouse.c65"
|
||||
#INCLUDE "pointer.c65"
|
||||
|
||||
GOTO __skip_lib_joysticktests
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC test_joystick_read
|
||||
// Test basic joystick reading using card suit symbols
|
||||
// Displays suits in 4 directions, lights up when joystick pressed
|
||||
// Runs forever - reset to exit
|
||||
// ============================================================================
|
||||
//FUNC test_joystick_read
|
||||
// WORD screen_ptr @ $fb
|
||||
// BYTE raster @ $d012
|
||||
//
|
||||
// // Initialize joystick
|
||||
// joy_init()
|
||||
//
|
||||
// // Draw suit symbols in cross pattern (center of screen)
|
||||
// // Hearts (red) = UP, Diamonds (red) = DOWN, Spades (black) = LEFT, Clubs (black) = RIGHT
|
||||
//
|
||||
// // UP position - Hearts suit symbol
|
||||
// POINTER screen_ptr -> 9*40+$0400+18
|
||||
// POKE screen_ptr[0] , $50
|
||||
//
|
||||
// // DOWN position - Diamonds suit symbol
|
||||
// POINTER screen_ptr -> 11*40+$0400+18
|
||||
// POKE screen_ptr[0] , $51
|
||||
//
|
||||
// // LEFT position - Spades suit symbol
|
||||
// POINTER screen_ptr -> 10*40+$0400+16
|
||||
// POKE screen_ptr[0] , $0e
|
||||
//
|
||||
// // RIGHT position - Clubs suit symbol
|
||||
// POINTER screen_ptr -> 10*40+$0400+20
|
||||
// POKE screen_ptr[0] , $0f
|
||||
//
|
||||
// // CENTER - Fire indicator (Ace symbol)
|
||||
// POINTER screen_ptr -> 10*40+$0400+18
|
||||
// POKE screen_ptr[0] , 0
|
||||
//
|
||||
// // Main test loop - runs forever
|
||||
// WHILE 1
|
||||
// joy_read_port2()
|
||||
//
|
||||
// // Wait for raster
|
||||
// WHILE raster != 250
|
||||
// WEND
|
||||
//
|
||||
// // Update suit displays based on joystick
|
||||
//
|
||||
// // UP - Hearts (normal or highlighted)
|
||||
// POINTER screen_ptr -> 9*40+$0400+18
|
||||
// IF joy_up
|
||||
// POKE screen_ptr[0] , $50+64 // Red/highlighted
|
||||
// ELSE
|
||||
// POKE screen_ptr[0] , $50 // Normal
|
||||
// ENDIF
|
||||
//
|
||||
// // DOWN - Diamonds
|
||||
// POINTER screen_ptr -> 11*40+$0400+18
|
||||
// IF joy_down
|
||||
// POKE screen_ptr[0] , $51+64 // Red/highlighted
|
||||
// ELSE
|
||||
// POKE screen_ptr[0] , $51 // Normal
|
||||
// ENDIF
|
||||
//
|
||||
// // LEFT - Spades
|
||||
// POINTER screen_ptr -> 10*40+$0400+16
|
||||
// IF joy_left
|
||||
// POKE screen_ptr[0] , $0e+64 // Red/highlighted
|
||||
// ELSE
|
||||
// POKE screen_ptr[0] , $0e // Normal
|
||||
// ENDIF
|
||||
//
|
||||
// // RIGHT - Clubs
|
||||
// POINTER screen_ptr -> 10*40+$0400+20
|
||||
// IF joy_right
|
||||
// POKE screen_ptr[0] , $0f+64 // Red/highlighted
|
||||
// ELSE
|
||||
// POKE screen_ptr[0] , $0f // Normal
|
||||
// ENDIF
|
||||
//
|
||||
// // FIRE - Center Ace
|
||||
// POINTER screen_ptr -> 10*40+$0400+18
|
||||
// IF joy_fire
|
||||
// POKE screen_ptr[0] , 13+64 // Ace highlighted
|
||||
// ELSE
|
||||
// POKE screen_ptr[0] , 0 // Blank
|
||||
// ENDIF
|
||||
// WEND
|
||||
//FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC test_pointer_sprite
|
||||
// Test sprite pointer movement with joystick
|
||||
// Initializes sprite, copies sprite data, and moves pointer with joystick
|
||||
// Run for ~200 frames then returns
|
||||
// ============================================================================
|
||||
FUNC test_pointer_sprite
|
||||
// Copy sprite data to $2200 (sprite block 136)
|
||||
// $2000-$21FF reserved for charset
|
||||
WORD src_ptr @ $fa
|
||||
WORD src_end_ptr
|
||||
WORD dst_ptr @ $fc
|
||||
WORD frame_count
|
||||
BYTE raster @ $d012
|
||||
|
||||
POINTER src_ptr -> pointer_sprite_data
|
||||
POINTER src_end_ptr -> pointer_sprite_data_end
|
||||
POINTER dst_ptr -> $2200
|
||||
mem_copy(src_ptr, src_end_ptr, dst_ptr)
|
||||
|
||||
// Initialize pointer
|
||||
pointer_init(160, 100, color_red, 136) // Sprite block 136 = $2200, red color
|
||||
pointer_set_speed(2)
|
||||
|
||||
// Enable sprite
|
||||
pointer_enable(1)
|
||||
|
||||
|
||||
// Movement loop - runs forever
|
||||
WHILE 1
|
||||
// Wait for raster
|
||||
WHILE raster != 250
|
||||
WEND
|
||||
|
||||
DEC $d020
|
||||
// Read joystick and update pointer
|
||||
joy_read_port2()
|
||||
pointer_update_joystick(joy_state)
|
||||
INC $d020
|
||||
WEND
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC test_pointer_manual
|
||||
// Test pointer with manual position setting
|
||||
// Moves pointer in a square pattern without joystick
|
||||
// ============================================================================
|
||||
//FUNC test_pointer_manual
|
||||
// WORD x
|
||||
// BYTE y
|
||||
// BYTE delay
|
||||
// WORD x_down
|
||||
// BYTE y_down
|
||||
//
|
||||
// // Copy sprite data to $2200 (sprite block 136)
|
||||
// WORD src_ptr @ $fa
|
||||
// WORD src_end_ptr
|
||||
// WORD dst_ptr @ $fc
|
||||
// POINTER src_ptr -> pointer_sprite_data
|
||||
// POINTER src_end_ptr -> pointer_sprite_data_end
|
||||
// POINTER dst_ptr -> $2200
|
||||
// mem_copy(src_ptr, src_end_ptr, dst_ptr)
|
||||
//
|
||||
// // Initialize pointer
|
||||
// pointer_init(50, 50, color_cyan, 136)
|
||||
// pointer_set_speed(3)
|
||||
//
|
||||
// // Move in square pattern
|
||||
// // Right
|
||||
// FOR x = 50 TO 200
|
||||
// pointer_set_x(x)
|
||||
// pointer_set_y(50)
|
||||
// // Small delay
|
||||
// FOR delay = 0 TO 10
|
||||
// NEXT
|
||||
// NEXT
|
||||
//
|
||||
// // Down
|
||||
// FOR y = 50 TO 150
|
||||
// pointer_set_x(200)
|
||||
// pointer_set_y(y)
|
||||
// FOR delay = 0 TO 10
|
||||
// NEXT
|
||||
// NEXT
|
||||
//
|
||||
// // Left
|
||||
// x_down = 200
|
||||
// WHILE x_down > 50
|
||||
// pointer_set_x(x_down)
|
||||
// pointer_set_y(150)
|
||||
// x_down--
|
||||
// FOR delay = 0 TO 10
|
||||
// NEXT
|
||||
// WEND
|
||||
//
|
||||
// // Up
|
||||
// y_down = 150
|
||||
// WHILE y_down > 50
|
||||
// pointer_set_x(50)
|
||||
// pointer_set_y(y_down)
|
||||
// y_down--
|
||||
// FOR delay = 0 TO 10
|
||||
// NEXT
|
||||
// WEND
|
||||
//
|
||||
// // Disable sprite
|
||||
// pointer_enable(0)
|
||||
//FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC test_pointer_mouse
|
||||
// Test sprite pointer movement with 1351 mouse
|
||||
// Initializes sprite, copies sprite data, and moves pointer with mouse
|
||||
// Run forever - press RUN/STOP+RESTORE to exit
|
||||
// ============================================================================
|
||||
FUNC test_pointer_mouse
|
||||
// Copy sprite data to $2200 (sprite block 136)
|
||||
// $2000-$21FF reserved for charset
|
||||
WORD src_ptr @ $fa
|
||||
WORD src_end_ptr
|
||||
WORD dst_ptr @ $fc
|
||||
BYTE raster @ $d012
|
||||
|
||||
POINTER src_ptr -> pointer_sprite_data
|
||||
POINTER src_end_ptr -> pointer_sprite_data_end
|
||||
POINTER dst_ptr -> $2200
|
||||
mem_copy(src_ptr, src_end_ptr, dst_ptr)
|
||||
|
||||
// Initialize mouse and pointer
|
||||
mouse_init()
|
||||
pointer_init(160, 100, color_red, 136) // Sprite block 136 = $2200, white color
|
||||
|
||||
// Enable sprite
|
||||
pointer_enable(1)
|
||||
|
||||
// Movement loop - runs forever
|
||||
WHILE 1
|
||||
// Wait for raster
|
||||
WHILE raster != 250
|
||||
WEND
|
||||
|
||||
DEC $d020
|
||||
// Read mouse and update pointer
|
||||
mouse_read()
|
||||
pointer_update_mouse(mouse_delta_x, mouse_delta_y)
|
||||
|
||||
// Test buttons (active-low: 0=pressed)
|
||||
BYTE left_btn
|
||||
BYTE right_btn
|
||||
|
||||
// Left button = bit 4
|
||||
left_btn = mouse_buttons & %00010000
|
||||
IF left_btn == 0
|
||||
INC $0400 // Pressed
|
||||
ENDIF
|
||||
|
||||
// Right button = bit 0
|
||||
right_btn = mouse_buttons & %00000001
|
||||
IF right_btn == 0
|
||||
INC $0401 // Pressed
|
||||
ENDIF
|
||||
|
||||
INC $d020
|
||||
WEND
|
||||
FEND
|
||||
|
||||
|
||||
LABEL __skip_lib_joysticktests
|
||||
|
||||
#IFEND
|
||||
307
mouse.c65
Normal file
307
mouse.c65
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
|
||||
#IFNDEF __lib_mouse
|
||||
#DEFINE __lib_mouse 1
|
||||
|
||||
GOTO __skip_lib_mouse
|
||||
|
||||
// ============================================================================
|
||||
// COMMODORE 1351 MOUSE DRIVER FOR C64
|
||||
// ============================================================================
|
||||
// This library provides mouse input reading for the Commodore 1351 mouse.
|
||||
//
|
||||
// The 1351 uses the SID chip's POT lines to report relative movement.
|
||||
// The SID hardware automatically samples at ~2kHz, but software should
|
||||
// read once per frame (50-60Hz) to minimize CPU usage.
|
||||
//
|
||||
// Mouse is read from CONTROL PORT 1 (POT lines + buttons)
|
||||
// This allows simultaneous use with joystick on CONTROL PORT 2
|
||||
//
|
||||
// Based on implementations from C64 OS and cc65.
|
||||
//
|
||||
// FAST MOVEMENT SMOOTHING:
|
||||
// By default, directional momentum smoothing is enabled to fix the 1351's
|
||||
// known issue where fast movements can register in the opposite direction.
|
||||
// To disable smoothing (saves CPU cycles), define MOUSE_NO_SMOOTHING:
|
||||
// #DEFINE MOUSE_NO_SMOOTHING 1
|
||||
// #INCLUDE "mouse.c65"
|
||||
//
|
||||
// Usage:
|
||||
// #INCLUDE "mouse.c65"
|
||||
// mouse_init()
|
||||
// WHILE 1
|
||||
// mouse_read() // Call once per frame
|
||||
// pointer_update_mouse(mouse_delta_x, mouse_delta_y)
|
||||
// WEND
|
||||
// ============================================================================
|
||||
|
||||
// SID POT line addresses
|
||||
WORD CONST SID_BASE = $D400
|
||||
BYTE pot_x @ SID_BASE+$19 // SID+$19: POT X (analog input)
|
||||
BYTE pot_y @ SID_BASE+$1A // SID+$1A: POT Y (analog input)
|
||||
|
||||
// CIA port for button reading (same as joystick port 1)
|
||||
WORD CONST CIA1_BASE = $DC00
|
||||
BYTE cia_port1 @ CIA1_BASE+1 // $DC01: Port 1 (mouse buttons)
|
||||
|
||||
// Mouse state variables
|
||||
BYTE mouse_pot_x_old // Previous POT X reading
|
||||
BYTE mouse_pot_y_old // Previous POT Y reading
|
||||
BYTE mouse_delta_x // Signed X movement delta
|
||||
BYTE mouse_delta_y // Signed Y movement delta
|
||||
BYTE mouse_buttons // Button state (bit 4=right, bit 0=left)
|
||||
|
||||
#IFNDEF MOUSE_NO_SMOOTHING
|
||||
// Directional momentum tracking (for fast movement disambiguation)
|
||||
// 0=no momentum, 1-127=rightward, 128-255=leftward (unsigned byte as signed)
|
||||
// Define MOUSE_NO_SMOOTHING before including this library to disable smoothing
|
||||
BYTE mouse_momentum_x
|
||||
BYTE mouse_momentum_y
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC mouse_apply_momentum
|
||||
// Apply directional momentum to disambiguate fast movements
|
||||
// When delta magnitude is large (ambiguous), use momentum to determine
|
||||
// if we should flip the interpretation (±64 in 6-bit space)
|
||||
// ============================================================================
|
||||
FUNC mouse_apply_momentum({BYTE delta} {BYTE momentum} out:{BYTE corrected_delta} out:{BYTE new_momentum})
|
||||
BYTE abs_delta
|
||||
BYTE is_negative
|
||||
BYTE is_ambiguous
|
||||
BYTE was_corrected
|
||||
|
||||
// Get absolute value and sign of delta
|
||||
is_negative = delta & $80
|
||||
IF is_negative
|
||||
abs_delta = 0 - delta
|
||||
ELSE
|
||||
abs_delta = delta
|
||||
ENDIF
|
||||
|
||||
// Check if delta is ambiguous (magnitude >= 12 after division by 2 in convert function)
|
||||
// In raw 6-bit space, deltas near ±32 become ±16 after /2, which is ambiguous
|
||||
is_ambiguous = 0
|
||||
was_corrected = 0
|
||||
#PRAGMA _P_USE_LONG_JUMP 1
|
||||
IF abs_delta >= 12
|
||||
#PRAGMA _P_USE_LONG_JUMP 0
|
||||
is_ambiguous = 1
|
||||
// Ambiguous - check if flipping (negating) would match momentum better
|
||||
BYTE flipped_delta
|
||||
BYTE flipped_negative
|
||||
|
||||
// To flip interpretation, just negate the delta
|
||||
flipped_delta = abs_delta
|
||||
IF is_negative
|
||||
// Delta is negative, flipped would be positive
|
||||
flipped_negative = 0
|
||||
ELSE
|
||||
// Delta is positive, flipped would be negative
|
||||
flipped_negative = 1
|
||||
ENDIF
|
||||
|
||||
// Use momentum to decide: if momentum sign matches flipped, use flipped
|
||||
IF momentum != 0
|
||||
BYTE momentum_is_left
|
||||
BYTE signs_match
|
||||
|
||||
momentum_is_left = 0
|
||||
IF momentum >= 128
|
||||
momentum_is_left = 1
|
||||
ENDIF
|
||||
|
||||
signs_match = 0
|
||||
IF momentum_is_left == flipped_negative
|
||||
signs_match = 1
|
||||
ENDIF
|
||||
|
||||
IF signs_match == 1
|
||||
// Momentum suggests flipped interpretation
|
||||
IF flipped_negative
|
||||
corrected_delta = 0 - flipped_delta
|
||||
ELSE
|
||||
corrected_delta = flipped_delta
|
||||
ENDIF
|
||||
was_corrected = 1
|
||||
ELSE
|
||||
// Keep original
|
||||
corrected_delta = delta
|
||||
was_corrected = 1
|
||||
ENDIF
|
||||
ELSE
|
||||
// No momentum yet - ignore this ambiguous delta, return 0
|
||||
corrected_delta = 0
|
||||
ENDIF
|
||||
ELSE
|
||||
// Unambiguous, use as-is
|
||||
corrected_delta = delta
|
||||
ENDIF
|
||||
|
||||
// Update momentum based on corrected delta
|
||||
// Only update from unambiguous deltas to avoid bootstrapping in wrong direction
|
||||
#PRAGMA _P_USE_LONG_JUMP 1
|
||||
IF is_ambiguous == 0
|
||||
#PRAGMA _P_USE_LONG_JUMP 0
|
||||
IF corrected_delta == 0
|
||||
// No movement - decay momentum toward zero
|
||||
IF momentum > 0
|
||||
IF momentum < 128
|
||||
IF momentum > 2
|
||||
momentum = momentum - 2
|
||||
ELSE
|
||||
momentum = 0
|
||||
ENDIF
|
||||
ELSE
|
||||
IF momentum < 253
|
||||
momentum = momentum + 2
|
||||
ELSE
|
||||
momentum = 0
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
ELSE
|
||||
// Add corrected delta to momentum (clamped)
|
||||
BYTE corrected_negative
|
||||
corrected_negative = corrected_delta & $80
|
||||
|
||||
IF corrected_negative
|
||||
// Moving left - push momentum toward 128-255
|
||||
IF momentum < 128
|
||||
momentum = 128
|
||||
ELSE
|
||||
IF momentum < 240
|
||||
momentum = momentum + 8
|
||||
ENDIF
|
||||
ENDIF
|
||||
ELSE
|
||||
// Moving right - push momentum toward 1-127
|
||||
IF momentum >= 128
|
||||
momentum = 127
|
||||
ELSE
|
||||
IF momentum < 120
|
||||
momentum = momentum + 8
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
new_momentum = momentum
|
||||
FEND
|
||||
#IFEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC mouse_convert_pot_delta
|
||||
// Convert 6-bit POT delta to 8-bit signed delta
|
||||
// The 1351 outputs 6-bit signed values in bits 1-6 (bits 0,7 are noise)
|
||||
// This converts them to standard 8-bit signed values (-128 to +127)
|
||||
// ============================================================================
|
||||
FUNC mouse_convert_pot_delta({BYTE new_val} {BYTE old_val} out:{BYTE delta})
|
||||
BYTE diff
|
||||
BYTE sign_bit
|
||||
|
||||
// Calculate raw difference (wraps at 64 due to 6-bit counter)
|
||||
diff = new_val - old_val
|
||||
|
||||
// Mask to 7 bits (ignore bit 7 noise)
|
||||
diff = diff & %01111111
|
||||
|
||||
// Check if value is negative (bit 6 set means >= 64 in 6-bit space)
|
||||
sign_bit = diff & %01000000
|
||||
|
||||
IF sign_bit
|
||||
// Negative value: set high bits and divide by 2
|
||||
diff = diff | %11000000 // Sign extend
|
||||
ASM
|
||||
lda |diff|
|
||||
cmp #$FF
|
||||
beq @zero
|
||||
sec
|
||||
ror // Arithmetic shift right (preserves sign)
|
||||
sta |diff|
|
||||
jmp @done
|
||||
@zero:
|
||||
lda #0
|
||||
sta |diff|
|
||||
@done:
|
||||
ENDASM
|
||||
delta = diff
|
||||
ELSE
|
||||
// Positive value: just divide by 2
|
||||
ASM
|
||||
lda |diff|
|
||||
beq @zero2
|
||||
lsr // Logical shift right
|
||||
sta |diff|
|
||||
jmp @done2
|
||||
@zero2:
|
||||
lda #0
|
||||
sta |diff|
|
||||
@done2:
|
||||
ENDASM
|
||||
delta = diff
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC mouse_init
|
||||
// Initialize mouse driver - call once at program start
|
||||
// ============================================================================
|
||||
FUNC mouse_init
|
||||
// Read initial pot values
|
||||
mouse_pot_x_old = pot_x
|
||||
mouse_pot_y_old = pot_y
|
||||
mouse_delta_x = 0
|
||||
mouse_delta_y = 0
|
||||
mouse_buttons = 0
|
||||
#IFNDEF MOUSE_NO_SMOOTHING
|
||||
mouse_momentum_x = 0
|
||||
mouse_momentum_y = 0
|
||||
#IFEND
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC mouse_read
|
||||
// Read mouse movement and button state
|
||||
// Call this every frame to update mouse state
|
||||
// Updates mouse_delta_x, mouse_delta_y (signed 8-bit deltas)
|
||||
// Applies directional momentum to fix fast movement bugs
|
||||
// ============================================================================
|
||||
FUNC mouse_read
|
||||
BYTE pot_x_new
|
||||
BYTE pot_y_new
|
||||
|
||||
// Read current POT values
|
||||
pot_x_new = pot_x
|
||||
pot_y_new = pot_y
|
||||
|
||||
// Calculate X delta
|
||||
mouse_convert_pot_delta(pot_x_new, mouse_pot_x_old, mouse_delta_x)
|
||||
mouse_pot_x_old = pot_x_new
|
||||
|
||||
#IFNDEF MOUSE_NO_SMOOTHING
|
||||
// Apply momentum correction to X
|
||||
mouse_apply_momentum(mouse_delta_x, mouse_momentum_x, mouse_delta_x, mouse_momentum_x)
|
||||
#IFEND
|
||||
|
||||
// Calculate Y delta (invert for correct screen movement)
|
||||
mouse_convert_pot_delta(pot_y_new, mouse_pot_y_old, mouse_delta_y)
|
||||
mouse_delta_y = 0 - mouse_delta_y // Negate Y
|
||||
mouse_pot_y_old = pot_y_new
|
||||
|
||||
#IFNDEF MOUSE_NO_SMOOTHING
|
||||
// Apply momentum correction to Y
|
||||
mouse_apply_momentum(mouse_delta_y, mouse_momentum_y, mouse_delta_y, mouse_momentum_y)
|
||||
#IFEND
|
||||
|
||||
// Read buttons from CIA port
|
||||
mouse_buttons = cia_port1
|
||||
FEND
|
||||
|
||||
|
||||
LABEL __skip_lib_mouse
|
||||
|
||||
#IFEND
|
||||
78
piles.c65
Normal file
78
piles.c65
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
|
||||
#IFNDEF __lib_piles
|
||||
#DEFINE __lib_piles 1
|
||||
|
||||
GOTO __skip_lib_piles
|
||||
|
||||
// Pile data structures
|
||||
// byte 0 = count, bytes 1-52 = cards
|
||||
// Cards use CARD_FACEDOWN bit for face-down flag
|
||||
|
||||
LABEL pile_stock
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_waste
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_tab0
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_tab1
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_tab2
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_tab3
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_tab4
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_tab5
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_tab6
|
||||
ASM
|
||||
!fill 53, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_found0
|
||||
ASM
|
||||
!fill 14, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_found1
|
||||
ASM
|
||||
!fill 14, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_found2
|
||||
ASM
|
||||
!fill 14, 0
|
||||
ENDASM
|
||||
|
||||
LABEL pile_found3
|
||||
ASM
|
||||
!fill 14, 0
|
||||
ENDASM
|
||||
|
||||
LABEL __skip_lib_piles
|
||||
|
||||
#IFEND
|
||||
371
pointer.c65
Normal file
371
pointer.c65
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
|
||||
#IFNDEF __lib_pointer
|
||||
#DEFINE __lib_pointer 1
|
||||
|
||||
GOTO __skip_lib_pointer
|
||||
|
||||
// ============================================================================
|
||||
// SPRITE POINTER DRIVER FOR COMMODORE 64
|
||||
// ============================================================================
|
||||
// This library provides sprite-based pointer/cursor control.
|
||||
// Can be driven by joystick, mouse, keyboard, or other input methods.
|
||||
//
|
||||
// Usage:
|
||||
// #INCLUDE "pointer.c65"
|
||||
// pointer_init(100, 50, 1, 192) // Init at x=100, y=50, white color, sprite data block 192
|
||||
// WHILE 1
|
||||
// // Read your input device (joystick, mouse, etc.)
|
||||
// pointer_move(delta_x, delta_y) // Move pointer
|
||||
// WEND
|
||||
// ============================================================================
|
||||
|
||||
// VIC-II Sprite registers
|
||||
WORD CONST VIC2 = $D000
|
||||
BYTE sprite_x0 @ VIC2+0 // Sprite 0 X position
|
||||
BYTE sprite_y0 @ VIC2+1 // Sprite 0 Y position
|
||||
BYTE sprite_x_msb @ VIC2+16 // Sprite X MSB (for X > 255)
|
||||
BYTE sprite_enable @ VIC2+21 // Sprite enable register
|
||||
BYTE sprite_color0 @ VIC2+39 // Sprite 0 color
|
||||
BYTE sprite_pointer0 @ $07F8 // Sprite 0 pointer (screen + $3F8)
|
||||
|
||||
// Pointer position variables
|
||||
WORD pointer_x // Current pointer X position (0-511)
|
||||
BYTE pointer_y // Current pointer Y position
|
||||
BYTE pointer_speed // Base movement speed
|
||||
BYTE pointer_accel // Current acceleration counter
|
||||
BYTE pointer_max_speed // Maximum speed with acceleration
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Compact arrow pointer sprite - 7 pixels tall
|
||||
// Tip at (0,0) for easy pointing at coordinates
|
||||
// ============================================================================
|
||||
LABEL pointer_sprite_data
|
||||
ASM
|
||||
// Simple compact arrow
|
||||
!8 $80, $00, $00 // Row 0: *
|
||||
!8 $C0, $00, $00 // Row 1: **
|
||||
!8 $E0, $00, $00 // Row 2: ***
|
||||
!8 $F0, $00, $00 // Row 3: ****
|
||||
!8 $E0, $00, $00 // Row 4: ***
|
||||
!8 $C0, $00, $00 // Row 5: **
|
||||
!8 $80, $00, $00 // Row 6: *
|
||||
!8 $00, $00, $00 // Row 7-20: blank
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00, $00, $00
|
||||
!8 $00 // Padding
|
||||
ENDASM
|
||||
LABEL pointer_sprite_data_end
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_init
|
||||
// Initialize sprite 0 as a pointer at given position
|
||||
// Parameters:
|
||||
// x_pos: Initial X position (0-255)
|
||||
// y_pos: Initial Y position (0-250)
|
||||
// color: Sprite color (0-15)
|
||||
// sprite_data: Sprite data block number (0-255)
|
||||
// ============================================================================
|
||||
FUNC pointer_init({BYTE x_pos} {BYTE y_pos} {BYTE color} {BYTE sprite_data})
|
||||
BYTE temp
|
||||
|
||||
// Set pointer position
|
||||
pointer_x = x_pos
|
||||
pointer_y = y_pos
|
||||
pointer_speed = 1 // Base speed: 1 pixel
|
||||
pointer_accel = 0 // No acceleration initially
|
||||
pointer_max_speed = 10 // Max accelerated speed
|
||||
|
||||
// Set sprite X position (low byte)
|
||||
sprite_x0 = x_pos
|
||||
sprite_y0 = y_pos
|
||||
|
||||
// Clear X MSB for sprite 0 (x_pos is < 256)
|
||||
temp = sprite_x_msb
|
||||
temp = temp & %11111110 // Clear bit 0
|
||||
sprite_x_msb = temp
|
||||
|
||||
// Set sprite color
|
||||
sprite_color0 = color
|
||||
|
||||
// Set sprite data pointer
|
||||
sprite_pointer0 = sprite_data
|
||||
|
||||
// Enable sprite 0
|
||||
sprite_enable = 1
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_move_left
|
||||
// Move pointer left by speed amount
|
||||
// ============================================================================
|
||||
FUNC pointer_move_left({BYTE speed})
|
||||
|
||||
// Move left with bounds check
|
||||
IF pointer_x >= speed
|
||||
pointer_x = pointer_x - speed
|
||||
ELSE
|
||||
pointer_x = 0
|
||||
ENDIF
|
||||
|
||||
IF pointer_x < 24
|
||||
pointer_x = 24
|
||||
ENDIF
|
||||
|
||||
// Update VIC registers
|
||||
sprite_x0 = pointer_x
|
||||
|
||||
IF pointer_x > 255
|
||||
sprite_x_msb = sprite_x_msb | %00000001
|
||||
ELSE
|
||||
sprite_x_msb = sprite_x_msb & %11111110
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_move_right
|
||||
// Move pointer right by speed amount
|
||||
// ============================================================================
|
||||
FUNC pointer_move_right({BYTE speed})
|
||||
|
||||
// Move right with bounds check
|
||||
pointer_x = pointer_x + speed
|
||||
IF pointer_x > 320+20
|
||||
pointer_x = 320+20
|
||||
ENDIF
|
||||
|
||||
// Update VIC registers
|
||||
sprite_x0 = pointer_x
|
||||
|
||||
IF pointer_x > 255
|
||||
sprite_x_msb = sprite_x_msb | %00000001
|
||||
ELSE
|
||||
sprite_x_msb = sprite_x_msb & %11111110
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_move_up
|
||||
// Move pointer up by speed amount
|
||||
// ============================================================================
|
||||
FUNC pointer_move_up({BYTE speed})
|
||||
|
||||
IF pointer_y >= speed
|
||||
pointer_y = pointer_y - speed
|
||||
ENDIF
|
||||
|
||||
IF pointer_y < 50
|
||||
pointer_y = 50 // Stop at top edge, not 0
|
||||
ENDIF
|
||||
|
||||
sprite_y0 = pointer_y
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_move_down
|
||||
// Move pointer down by speed amount
|
||||
// ============================================================================
|
||||
FUNC pointer_move_down({BYTE speed})
|
||||
WORD new_y
|
||||
new_y = pointer_y + speed
|
||||
|
||||
// Stop at bottom edge (around Y=229 for NTSC, 249 for PAL)
|
||||
IF new_y > 247
|
||||
pointer_y = 247
|
||||
ELSE
|
||||
pointer_y = new_y
|
||||
ENDIF
|
||||
|
||||
sprite_y0 = pointer_y
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_update_joystick
|
||||
// Update pointer based on joystick input state with acceleration
|
||||
// Expects joy_state to be set (from joystick.c65 library)
|
||||
// joy_state bits: UP=bit0, DOWN=bit1, LEFT=bit2, RIGHT=bit3, FIRE=bit4
|
||||
// ============================================================================
|
||||
FUNC pointer_update_joystick({BYTE joy_state})
|
||||
BYTE actual_speed
|
||||
BYTE any_direction
|
||||
BYTE test_bit
|
||||
|
||||
// Check if any direction is pressed (mask all direction bits)
|
||||
any_direction = joy_state & %00001111
|
||||
|
||||
// Reset acceleration if joystick is neutral
|
||||
IF any_direction == 0
|
||||
pointer_accel = 0
|
||||
ELSE
|
||||
// Increase acceleration while moving
|
||||
IF pointer_accel < 255
|
||||
pointer_accel++
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Calculate actual speed with acceleration
|
||||
// Divide accel by 8 (shift right 3 times)
|
||||
actual_speed = pointer_accel
|
||||
ASM
|
||||
lda |actual_speed|
|
||||
;lsr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
sta |actual_speed|
|
||||
ENDASM
|
||||
|
||||
actual_speed = pointer_speed + actual_speed
|
||||
IF actual_speed > pointer_max_speed
|
||||
actual_speed = pointer_max_speed
|
||||
ENDIF
|
||||
|
||||
// Move in pressed directions
|
||||
test_bit = joy_state & %00000100 // LEFT mask
|
||||
IF test_bit
|
||||
pointer_move_left(actual_speed)
|
||||
ENDIF
|
||||
|
||||
test_bit = joy_state & %00001000 // RIGHT mask
|
||||
IF test_bit
|
||||
pointer_move_right(actual_speed)
|
||||
ENDIF
|
||||
|
||||
test_bit = joy_state & %00000001 // UP mask
|
||||
IF test_bit
|
||||
pointer_move_up(actual_speed)
|
||||
ENDIF
|
||||
|
||||
test_bit = joy_state & %00000010 // DOWN mask
|
||||
IF test_bit
|
||||
pointer_move_down(actual_speed)
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_update_mouse
|
||||
// Update pointer based on mouse delta movements
|
||||
// Expects mouse_delta_x and mouse_delta_y to be set (from mouse.c65 library)
|
||||
// Mouse deltas are signed 8-bit values
|
||||
// ============================================================================
|
||||
FUNC pointer_update_mouse({BYTE delta_x} {BYTE delta_y})
|
||||
BYTE sign_x
|
||||
BYTE sign_y
|
||||
BYTE abs_x
|
||||
BYTE abs_y
|
||||
|
||||
// Handle X movement
|
||||
sign_x = delta_x & %10000000
|
||||
IF sign_x
|
||||
// Negative (moving left)
|
||||
abs_x = 0 - delta_x // Get absolute value
|
||||
IF abs_x > 0
|
||||
pointer_move_left(abs_x)
|
||||
ENDIF
|
||||
ELSE
|
||||
// Positive (moving right)
|
||||
IF delta_x > 0
|
||||
pointer_move_right(delta_x)
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Handle Y movement
|
||||
sign_y = delta_y & %10000000
|
||||
IF sign_y
|
||||
// Negative (moving up)
|
||||
abs_y = 0 - delta_y // Get absolute value
|
||||
IF abs_y > 0
|
||||
pointer_move_up(abs_y)
|
||||
ENDIF
|
||||
ELSE
|
||||
// Positive (moving down)
|
||||
IF delta_y > 0
|
||||
pointer_move_down(delta_y)
|
||||
ENDIF
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_set_speed
|
||||
// Set pointer movement speed
|
||||
// Parameters:
|
||||
// speed: Pixels to move per movement (1-8 recommended)
|
||||
// ============================================================================
|
||||
FUNC pointer_set_speed({BYTE speed})
|
||||
pointer_speed = speed
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_get_x
|
||||
// Get current pointer X position
|
||||
// Parameters:
|
||||
// out:x_pos: X position (0-511)
|
||||
// ============================================================================
|
||||
FUNC pointer_get_x(out:{WORD x_pos})
|
||||
x_pos = pointer_x
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_get_y
|
||||
// Get current pointer Y position
|
||||
// Returns: Y position in out parameter
|
||||
// ============================================================================
|
||||
FUNC pointer_get_y(out:{BYTE y_pos})
|
||||
y_pos = pointer_y
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_enable
|
||||
// Enable or disable pointer sprite
|
||||
// Parameters:
|
||||
// enable: 1 to enable, 0 to disable
|
||||
// ============================================================================
|
||||
FUNC pointer_enable({BYTE enable})
|
||||
BYTE temp
|
||||
temp = sprite_enable
|
||||
|
||||
IF enable
|
||||
temp = temp | %00000001
|
||||
ELSE
|
||||
temp = temp & %11111110
|
||||
ENDIF
|
||||
|
||||
sprite_enable = temp
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC pointer_set_color
|
||||
// Change pointer sprite color
|
||||
// Parameters:
|
||||
// color: Color value (0-15)
|
||||
// ============================================================================
|
||||
FUNC pointer_set_color({BYTE color})
|
||||
sprite_color0 = color
|
||||
FEND
|
||||
|
||||
|
||||
LABEL __skip_lib_pointer
|
||||
|
||||
#IFEND
|
||||
131
random.c65
Normal file
131
random.c65
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
#IFNDEF __lib_random
|
||||
#DEFINE __lib_random 1
|
||||
|
||||
GOTO __skip_lib_random
|
||||
|
||||
// LFSR Random Number Generator
|
||||
// Combined 16-bit (period 65535) and 15-bit (period 32767) generators
|
||||
// Total period: ~2.1 billion (65535 * 32767 = 2,147,385,345)
|
||||
//
|
||||
// Based on Hanno Behrens' implementation (LGPL)
|
||||
// Adapted for c65 by using local variable storage
|
||||
//
|
||||
// Usage:
|
||||
// BYTE rnd
|
||||
// rand(rnd) // rnd now contains 0-255
|
||||
//
|
||||
// For ZP acceleration, change the variable declarations below
|
||||
// from regular addresses to ZP addresses (e.g., @ $f6)
|
||||
|
||||
// Random state - initialized with default seeds
|
||||
WORD rand_sr1 = $a55a
|
||||
WORD rand_sr2 = $7653
|
||||
|
||||
// Initialize the random generator with default seeds
|
||||
// Call once at startup (or just use rand_seed() with custom seed)
|
||||
FUNC rand_init
|
||||
rand_sr1 = $a55a
|
||||
rand_sr2 = $7653
|
||||
FEND
|
||||
|
||||
// Initialize with a custom seed value
|
||||
// Good for seeding from user input timing
|
||||
FUNC rand_seed({WORD seed})
|
||||
rand_sr1 = seed ^ $a55a
|
||||
rand_sr2 = seed ^ $7653
|
||||
|
||||
// Ensure neither seed is zero
|
||||
IF rand_sr1 == 0
|
||||
rand_sr1 = $a55a
|
||||
ENDIF
|
||||
IF rand_sr2 == 0
|
||||
rand_sr2 = $7653
|
||||
ENDIF
|
||||
FEND
|
||||
|
||||
// Internal: 16-bit LFSR with period 65535
|
||||
// Taps: 10, 12, 13, 15 (polynomial)
|
||||
FUNC rand_lfsr64k
|
||||
ASM
|
||||
lda |rand_sr1|+1
|
||||
asl
|
||||
asl
|
||||
eor |rand_sr1|+1
|
||||
asl
|
||||
eor |rand_sr1|+1
|
||||
asl
|
||||
asl
|
||||
eor |rand_sr1|+1
|
||||
asl
|
||||
rol |rand_sr1|
|
||||
rol |rand_sr1|+1
|
||||
ENDASM
|
||||
FEND
|
||||
|
||||
// Internal: 15-bit LFSR with period 32767
|
||||
// Taps: 13, 14 (polynomial)
|
||||
FUNC rand_lfsr32k
|
||||
ASM
|
||||
lda |rand_sr2|+1
|
||||
asl
|
||||
eor |rand_sr2|+1
|
||||
asl
|
||||
asl
|
||||
ror |rand_sr2|
|
||||
rol |rand_sr2|+1
|
||||
ENDASM
|
||||
FEND
|
||||
|
||||
// Get random byte (0-255)
|
||||
// Result returned in 'result' parameter
|
||||
FUNC rand(out:{BYTE result})
|
||||
rand_lfsr64k()
|
||||
rand_lfsr32k()
|
||||
|
||||
ASM
|
||||
lda |rand_sr1|
|
||||
eor |rand_sr2|
|
||||
sta |result|
|
||||
ENDASM
|
||||
FEND
|
||||
|
||||
// Get random byte in range [0, max)
|
||||
// Uses rejection sampling for unbiased results
|
||||
FUNC rand_max({BYTE max} out:{BYTE result})
|
||||
BYTE r
|
||||
BYTE mask
|
||||
|
||||
// Calculate mask (smear highest bit down to get next power of 2 minus 1)
|
||||
mask = max - 1
|
||||
ASM
|
||||
lda |mask|
|
||||
lsr
|
||||
ora |mask|
|
||||
sta |mask|
|
||||
lsr
|
||||
lsr
|
||||
ora |mask|
|
||||
sta |mask|
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
ora |mask|
|
||||
sta |mask|
|
||||
ENDASM
|
||||
|
||||
// Rejection sampling loop
|
||||
WHILE 1
|
||||
rand(r)
|
||||
r = r & mask
|
||||
IF r < max
|
||||
result = r
|
||||
EXIT
|
||||
ENDIF
|
||||
WEND
|
||||
FEND
|
||||
|
||||
LABEL __skip_lib_random
|
||||
|
||||
#IFEND
|
||||
249
testgames.c65
Normal file
249
testgames.c65
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
|
||||
#IFNDEF __lib_testgames
|
||||
#DEFINE __lib_testgames 1
|
||||
|
||||
GOTO __skip_lib_testgames
|
||||
|
||||
// ============================================================================
|
||||
// Test game setups for debugging specific scenarios
|
||||
// To use: call setup_test_game_X() in main() instead of normal deal sequence
|
||||
// ============================================================================
|
||||
|
||||
// ============================================================================
|
||||
// FUNC setup_test_game_tall_tableau
|
||||
// Sets up tableau 0 with K->3 (11 cards) and red 2 in waste
|
||||
// Tests compact rendering mode and edge case of moving 2 onto 3
|
||||
// ============================================================================
|
||||
FUNC setup_test_game_tall_tableau
|
||||
WORD ptr @ $fa
|
||||
|
||||
// Clear all piles
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 0
|
||||
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 0
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
// Setup tableau 0: K->3 alternating colors (all face-up)
|
||||
// Spades King (38) -> Hearts Queen (11) -> Spades Jack (36) -> ...
|
||||
POINTER ptr -> pile_tab0
|
||||
POKE ptr[0] , 11 // Count
|
||||
POKE ptr[1] , 38 // Spades King
|
||||
POKE ptr[2] , 11 // Hearts Queen
|
||||
POKE ptr[3] , 36 // Spades Jack
|
||||
POKE ptr[4] , 9 // Hearts 10
|
||||
POKE ptr[5] , 34 // Spades 9
|
||||
POKE ptr[6] , 7 // Hearts 8
|
||||
POKE ptr[7] , 32 // Spades 7
|
||||
POKE ptr[8] , 5 // Hearts 6
|
||||
POKE ptr[9] , 30 // Spades 5
|
||||
POKE ptr[10] , 3 // Hearts 4
|
||||
POKE ptr[11] , 28 // Spades 3
|
||||
|
||||
// Put red 2 (Hearts 2 = 1) in waste
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 1 // Count
|
||||
POKE ptr[1] , 1 // Hearts 2
|
||||
|
||||
// Put some cards in stock for drawing
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 5
|
||||
POKE ptr[1] , 13 // Diamonds Ace
|
||||
POKE ptr[2] , 26 // Spades Ace
|
||||
POKE ptr[3] , 39 // Clubs Ace
|
||||
POKE ptr[4] , 0 // Hearts Ace
|
||||
POKE ptr[5] , 14 // Diamonds 2
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC setup_test_game_one_move_to_win
|
||||
// Sets up game 1 move from victory
|
||||
// 3 foundations complete, 4th has Ace->Queen, Clubs King in waste
|
||||
// ============================================================================
|
||||
FUNC setup_test_game_one_move_to_win
|
||||
WORD ptr @ $fa
|
||||
BYTE i
|
||||
|
||||
// Clear all piles
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 0
|
||||
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 0
|
||||
|
||||
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
|
||||
|
||||
// Foundation 0: Hearts Ace->King (complete)
|
||||
POINTER ptr -> pile_found0
|
||||
POKE ptr[0] , 13
|
||||
POKE ptr[1] , 0
|
||||
POKE ptr[2] , 1
|
||||
POKE ptr[3] , 2
|
||||
POKE ptr[4] , 3
|
||||
POKE ptr[5] , 4
|
||||
POKE ptr[6] , 5
|
||||
POKE ptr[7] , 6
|
||||
POKE ptr[8] , 7
|
||||
POKE ptr[9] , 8
|
||||
POKE ptr[10] , 9
|
||||
POKE ptr[11] , 10
|
||||
POKE ptr[12] , 11
|
||||
POKE ptr[13] , 12
|
||||
|
||||
// Foundation 1: Diamonds Ace->King (complete)
|
||||
POINTER ptr -> pile_found1
|
||||
POKE ptr[0] , 13
|
||||
POKE ptr[1] , 13
|
||||
POKE ptr[2] , 14
|
||||
POKE ptr[3] , 15
|
||||
POKE ptr[4] , 16
|
||||
POKE ptr[5] , 17
|
||||
POKE ptr[6] , 18
|
||||
POKE ptr[7] , 19
|
||||
POKE ptr[8] , 20
|
||||
POKE ptr[9] , 21
|
||||
POKE ptr[10] , 22
|
||||
POKE ptr[11] , 23
|
||||
POKE ptr[12] , 24
|
||||
POKE ptr[13] , 25
|
||||
|
||||
// Foundation 2: Spades Ace->King (complete)
|
||||
POINTER ptr -> pile_found2
|
||||
POKE ptr[0] , 13
|
||||
POKE ptr[1] , 26
|
||||
POKE ptr[2] , 27
|
||||
POKE ptr[3] , 28
|
||||
POKE ptr[4] , 29
|
||||
POKE ptr[5] , 30
|
||||
POKE ptr[6] , 31
|
||||
POKE ptr[7] , 32
|
||||
POKE ptr[8] , 33
|
||||
POKE ptr[9] , 34
|
||||
POKE ptr[10] , 35
|
||||
POKE ptr[11] , 36
|
||||
POKE ptr[12] , 37
|
||||
POKE ptr[13] , 38
|
||||
|
||||
// Foundation 3: Clubs Ace->Queen (missing King)
|
||||
POINTER ptr -> pile_found3
|
||||
POKE ptr[0] , 12
|
||||
POKE ptr[1] , 39
|
||||
POKE ptr[2] , 40
|
||||
POKE ptr[3] , 41
|
||||
POKE ptr[4] , 42
|
||||
POKE ptr[5] , 43
|
||||
POKE ptr[6] , 44
|
||||
POKE ptr[7] , 45
|
||||
POKE ptr[8] , 46
|
||||
POKE ptr[9] , 47
|
||||
POKE ptr[10] , 48
|
||||
POKE ptr[11] , 49
|
||||
POKE ptr[12] , 50
|
||||
|
||||
// Waste: Clubs King (the winning move)
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 1
|
||||
POKE ptr[1] , 51
|
||||
FEND
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// FUNC setup_test_game_overflow
|
||||
// Sets up very tall tableau to test screen overflow bounds checking
|
||||
// Tableau 3 gets King->Ace (13 cards) all face-up to trigger compact mode
|
||||
// ============================================================================
|
||||
FUNC setup_test_game_overflow
|
||||
WORD ptr @ $fa
|
||||
|
||||
// Clear all piles
|
||||
POINTER ptr -> pile_stock
|
||||
POKE ptr[0] , 0
|
||||
|
||||
POINTER ptr -> pile_waste
|
||||
POKE ptr[0] , 0
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
// Setup tableau 3: King->Ace alternating colors (13 cards, all face-up)
|
||||
// This will trigger compact rendering and test overflow protection
|
||||
POINTER ptr -> pile_tab3
|
||||
POKE ptr[0] , 13 // Count
|
||||
POKE ptr[1] , 38 // Spades King
|
||||
POKE ptr[2] , 11 // Hearts Queen
|
||||
POKE ptr[3] , 36 // Spades Jack
|
||||
POKE ptr[4] , 9 // Hearts 10
|
||||
POKE ptr[5] , 34 // Spades 9
|
||||
POKE ptr[6] , 7 // Hearts 8
|
||||
POKE ptr[7] , 32 // Spades 7
|
||||
POKE ptr[8] , 5 // Hearts 6
|
||||
POKE ptr[9] , 30 // Spades 5
|
||||
POKE ptr[10] , 3 // Hearts 4
|
||||
POKE ptr[11] , 28 // Spades 3
|
||||
POKE ptr[12] , 1 // Hearts 2
|
||||
POKE ptr[13] , 26 // Spades Ace
|
||||
FEND
|
||||
|
||||
|
||||
LABEL __skip_lib_testgames
|
||||
|
||||
#IFEND
|
||||
142
utils.c65
Normal file
142
utils.c65
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
|
||||
#IFNDEF __lib_utils
|
||||
#DEFINE __lib_utils 1
|
||||
|
||||
GOTO __skip_lib_utils
|
||||
|
||||
FUNC fill_mem({WORD start_addr @ $fa} {WORD end_addr} {BYTE value})
|
||||
|
||||
FOR start_addr = start_addr TO end_addr
|
||||
POKE start_addr , value
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
FUNC mem_copy({WORD start_addr @ $fa} {WORD end_addr} {WORD target_addr @ $fc})
|
||||
BYTE value
|
||||
|
||||
FOR start_addr = start_addr TO end_addr
|
||||
value = PEEK start_addr
|
||||
POKE target_addr , value
|
||||
target_addr++
|
||||
NEXT
|
||||
FEND
|
||||
|
||||
|
||||
FUNC mem_copy_range({WORD start_addr @ $fa} {WORD target_addr @ $fc} {WORD range})
|
||||
BYTE value
|
||||
|
||||
WHILE range
|
||||
value = PEEK start_addr
|
||||
POKE target_addr , value
|
||||
start_addr++
|
||||
target_addr++
|
||||
range--
|
||||
WEND
|
||||
FEND
|
||||
|
||||
FUNC set_vic_bank({BYTE bank})
|
||||
|
||||
ASM
|
||||
lda $dd02
|
||||
ora #3
|
||||
sta $dd02
|
||||
|
||||
; sanitize and reverse value (so bank 0 = $0000. bank 1 = $4000 etc)
|
||||
lda |bank|
|
||||
and #3
|
||||
eor #3
|
||||
sta |bank|
|
||||
|
||||
|
||||
lda $dd00
|
||||
and #%11111100
|
||||
ora |bank|
|
||||
sta $dd00
|
||||
ENDASM
|
||||
FEND
|
||||
|
||||
|
||||
// Screen mem goes in banks of $0400
|
||||
// 0=$0000
|
||||
// 1=$0400
|
||||
// 2=$0800
|
||||
// ...
|
||||
FUNC set_vic_screenmem({BYTE screenmem})
|
||||
|
||||
ASM
|
||||
; sanitize screenmem and move to relevant $d018 bits
|
||||
lda |screenmem|
|
||||
and #$0f
|
||||
clc
|
||||
rol
|
||||
rol
|
||||
rol
|
||||
rol
|
||||
sta |screenmem|
|
||||
|
||||
lda $d018
|
||||
and #$0f
|
||||
ora |screenmem|
|
||||
sta $d018
|
||||
ENDASM
|
||||
|
||||
FEND
|
||||
|
||||
// Char mem goes in banks of $0800
|
||||
// 0=$0000
|
||||
// 1=$0800
|
||||
// 2=$1000
|
||||
// ...
|
||||
// (Bitmaps in banks of $2000)
|
||||
// 0=$0000
|
||||
// 4=$2000
|
||||
FUNC set_vic_charmem({BYTE charmem})
|
||||
|
||||
ASM
|
||||
; sanitize charmem and move to relevant $d018 bits
|
||||
lda |charmem|
|
||||
and #%00000111
|
||||
clc
|
||||
rol
|
||||
sta |charmem|
|
||||
|
||||
lda $d018
|
||||
and #$f0
|
||||
ora |charmem|
|
||||
sta $d018
|
||||
ENDASM
|
||||
|
||||
FEND
|
||||
|
||||
FUNC set_vic_ecm
|
||||
ASM
|
||||
lda $d011
|
||||
and #$1f ; leave DEN bit + all scroll stuff
|
||||
ora #64
|
||||
sta $d011
|
||||
ENDASM
|
||||
FEND
|
||||
|
||||
// Wait for a keypress using direct CIA keyboard scan
|
||||
// No interrupts needed
|
||||
FUNC wait_key
|
||||
BYTE row @ $dc01
|
||||
BYTE col @ $dc00
|
||||
|
||||
// Wait for NO key pressed first (debounce)
|
||||
col = 0
|
||||
WHILE row <> $ff
|
||||
WEND
|
||||
|
||||
// Now wait for ANY key pressed
|
||||
WHILE row == $ff
|
||||
WEND
|
||||
|
||||
// Wait for key release (debounce)
|
||||
WHILE row <> $ff
|
||||
WEND
|
||||
FEND
|
||||
|
||||
LABEL __skip_lib_utils
|
||||
|
||||
#IFEND
|
||||
Loading…
Reference in a new issue