151 lines
5.7 KiB
Markdown
151 lines
5.7 KiB
Markdown
# 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`
|