solitaire-c64/CLAUDE.md
2026-01-18 17:30:16 +01:00

151 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Workflow
This is a collaborative pair programming effort. Claude runs in a Docker environment with access to the codebase but cannot build or run the project. The user handles all builds and testing.
## Project Overview
Solitaire C64 is a Klondike solitaire card game for the Commodore 64, written in c65gm (a high-level language that compiles to 6502 assembly). Language documentation is available in `language_docs/`.
## Build Commands
```bash
./cm.sh # Build the project (outputs main.prg)
./start_in_vice.sh # Launch in VICE emulator
./exomizer_compress_prg.sh # Compress binary with Exomizer
```
Build chain: C65 source → c65gm compiler → 6502 assembly → ACME assembler → main.prg
Requirements:
- [c65gm compiler](https://git.techserio.com/mattiashz/c65gm)
- ACME 6502 assembler
## Testing
Enable test modes by uncommenting `#DEFINE TEST_GAMES 1` in `cardgame.c65`. Test functions in `cardtests.c65` and `joysticktests.c65` can be called from main() for debugging specific scenarios.
## Architecture
### Module Organization
**Core Game Logic:**
- `cardgame.c65` - Main entry point, initialization, RNG seeding
- `gameloop.c65` - Main loop, input processing, coordinate-to-pile mapping
- `gamestate.c65` - Global state (draw mode, selected piles, interaction flags)
- `cardmoves.c65` - Move validation and execution
- `carddeck.c65` - Fisher-Yates shuffle, dealing
- `cardrender.c65` - Card and pile rendering
**Input System:**
- `joystick.c65` - CIA Port 2 joystick driver
- `mouse.c65` - 1351 mouse driver with smoothing
- `keyboard.c65` - Non-interfering keyboard scan
- `pointer.c65` - VIC-II sprite cursor control
**Data & Constants:**
- `piles.c65` - Pile data structures (13 piles: stock, waste, 4 foundations, 7 tableaus)
- `pileconsts.c65` - Pile ID constants
- `cardconsts.c65` - Card suits, ranks, flags
### Game Data Model
- 52 cards represented as values 0-51 (0-12 Hearts, 13-25 Diamonds, 26-38 Spades, 39-51 Clubs)
- Face-down cards have high bit set ($80)
- Each pile: 1 count byte + card slots (tableaus: 53 bytes, foundations: 14 bytes)
- PILE_END marker ($FF) indicates empty pile
### Memory Layout
- `$0400` - Screen memory
- `$2000` - Custom character set
- `$3000` - Code start
- `$C000` - Screen backup (menu)
- `$D000` - VIC-II registers
- `$DC00` - CIA ports (joystick/keyboard)
### Custom Character Set (ECM Mode)
The game uses a 64-character ECM (Extended Color Mode) font stored at `$2000`. This is NOT a standard ASCII font - it contains only card-specific graphics:
- **Char 0**: Solid filled block (`$FF` bytes)
- **Chars 1-12**: Rank characters (2-10, J, Q, K) - note: Ace uses char 13
- **Char 13**: Ace character
- **Chars 14-15**: Suit symbols (spades, clubs in one color set)
- **Chars 19 ($13)**: Empty/blank character (`$00` bytes)
- **Chars $15-$39**: Suit graphics (hearts, diamonds, spades, clubs as 3x3 grids)
- **Chars $50-$51**: Suit symbols (hearts, diamonds)
- **Various**: Card borders, corners, card back pattern pieces
Character data is in `charpad_cards/`. The map PNG shows the visual layout. ECM mode uses 2 bits from color RAM to select between 4 color sets, effectively giving 64 unique characters × 4 color variations.
Since there are no alphabet characters, any text display (like "YOU WIN!") must be constructed from available shapes (solid blocks, empty spaces, card graphics).
## c65gm Language Notes
See `language_docs/` for full reference. Key points:
**Arithmetic expressions have two contexts:**
*Compile-time* (no spaces) - evaluated by compiler, supports `+ - * /` and logic operators:
```c65
value = 5+6*2 // Computed at compile time, supports * /
offset = 40*5+SCREEN // Fifth row (40 cols * 5 rows) + screen base address
// NOTE: SCREEN+40*5 would mean (SCREEN+40)*5 due to left-to-right eval!
```
*Runtime* (with spaces) - generates 6502 code, only `+ -` and logic operators:
```c65
result = a + b // Runtime addition
result = count - 1 // Runtime subtraction
// No runtime multiplication - split complex expressions into multiple statements
```
**Critical: No operator precedence** in either context. Evaluation is strictly left to right:
```c65
result = 2+3*4 // = 20, NOT 14 (evaluates as (2+3)*4)
```
**Types:**
- `BYTE` (8-bit), `WORD` (16-bit)
- `BYTE CONST` / `WORD CONST` for constants (preferred over `#DEFINE`)
- Memory-mapped: `BYTE borderColor @ $D020`
**Control flow:** `IF`/`ENDIF`, `WHILE`/`WEND`, `FOR`/`NEXT`, `SWITCH`/`CASE`/`ENDSWITCH`, `BREAK`
**Functions:**
```c65
FUNC add(in:a, in:b, out:result)
result = a + b
FEND
```
Parameter modifiers: `in:` (read-only, default), `out:` (write-only), `io:` (read-write, both in and out).
Call with `@<label>` to pass an address: `myFunc(@dataTable)` sends the address of `dataTable` to that WORD parameter.
**Memory access:**
```c65
value = PEEK $D020 // Read byte
POKE $D020 WITH 0 // Write byte
value = PEEK screenPtr[index] // Indexed access (pointer must be WORD in zero page)
POINTER screenPtr TO $0400 // Set pointer
```
Also `PEEKW`/`POKEW` for 16-bit values.
**Pointers:** Not special like in C - just WORD variables. Whether it's a "pointer" depends on usage. Normal WORD variables work fine to hold addresses. Only use zero-page (`@ $xx`) for pointers that need indexed PEEK/POKE access.
**Inline assembly:** Use `ASM`...`ENDASM`. Reference local variables with `|varname|` syntax.
**Include guards pattern:**
```c65
#IFNDEF __MY_LIBRARY
#DEFINE __MY_LIBRARY = 1
GOTO lib_skip
// ... library code ...
LABEL lib_skip
#IFEND
```
**Number formats:** Decimal `123`, hex `$FF`, binary `%11110000`