From b54183aab466bbc199ee4a41a6d95f7ff721191f Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Fri, 19 Dec 2025 19:26:56 +0100 Subject: [PATCH] Added claude code docker setup for project. Added language guide. --- .gitignore | 3 + Dockerfile | 4 + claude_code_docker.sh | 4 + docker-compose.yml | 13 + language.md | 892 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 916 insertions(+) create mode 100644 Dockerfile create mode 100644 claude_code_docker.sh create mode 100644 docker-compose.yml create mode 100644 language.md diff --git a/.gitignore b/.gitignore index f7fad3e..37a00cb 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ out/ *.tmp .DS_Store c65gm + +.claude/ +.npm/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f6a6d4b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM node:18-alpine +WORKDIR /app +RUN npm install -g @anthropic-ai/claude-code +CMD ["claude"] diff --git a/claude_code_docker.sh b/claude_code_docker.sh new file mode 100644 index 0000000..96459ba --- /dev/null +++ b/claude_code_docker.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export DOCKER_UID=$(id -u) +export DOCKER_GID=$(id -g) +docker compose run --rm claude-c65gm diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8c143f2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + claude-c65gm: + 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 + stdin_open: true + tty: true diff --git a/language.md b/language.md new file mode 100644 index 0000000..cf9740d --- /dev/null +++ b/language.md @@ -0,0 +1,892 @@ +# Programming in c65gm + +c65gm is a high-level language for 6502 assembly programming that combines modern programming constructs with direct hardware access. Originally designed for Commodore 64 development, it compiles to efficient 6502 assembly code. + +## Table of Contents + +1. [Getting Started](#getting-started) +2. [Variables and Types](#variables-and-types) +3. [Expressions and Operators](#expressions-and-operators) +4. [Control Flow](#control-flow) +5. [Functions](#functions) +6. [Memory Operations](#memory-operations) +7. [Code Blocks](#code-blocks) +8. [Preprocessor](#preprocessor) +9. [Common Patterns](#common-patterns) + +--- + +## Getting Started + +### Your First Program + +Here's a simple program that changes the screen border color on a Commodore 64: + +```c65 +ORIGIN $0801 + +BYTE borderColor @ $D020 + +borderColor = 5 // Set border to green +``` + +### Program Structure + +A typical c65gm program consists of: + +1. **ORIGIN directive** - Sets where code will be placed in memory +2. **Variable declarations** - Define your data +3. **Code** - Functions and statements that do the work + +```c65 +ORIGIN $0801 + +// Variables +BYTE counter = 0 +WORD screen = $0400 + +// Main code +FUNC main + counter = 10 + processData() +FEND +``` + +--- + +## Variables and Types + +### BYTE Variables + +BYTE variables store 8-bit values (0-255). + +```c65 +BYTE count // Uninitialized +BYTE speed = 5 // Initialized to 5 +BYTE screen @ $D020 // Memory-mapped to specific address +BYTE CONST MAX_SPEED = 10 // Constant (recommended over #DEFINE) +``` + +### WORD Variables + +WORD variables store 16-bit values (0-65535). + +```c65 +WORD counter // Uninitialized +WORD address = $C000 // Initialized to hex value +WORD message = "Hello" // String literal (pointer to text) +WORD screenPtr @ $FB // Zero-page pointer +WORD CONST SCREEN_RAM = $0400 // Constant +``` + +### Memory-Mapped Variables + +Variables can be placed at specific addresses using `@`: + +```c65 +BYTE borderColor @ $D020 // VIC-II border color +WORD irqVector @ $0314 // IRQ vector +WORD zpPointer @ $FB // Zero-page variable (fast access) +``` + +### Constants + +Use `CONST` keyword for named constants (preferred over preprocessor defines): + +```c65 +BYTE CONST MAX_ITEMS = 100 +WORD CONST SCREEN_START = $0400 +WORD CONST CHAR_ROM = $D000 + +// Usage +BYTE itemCount = MAX_ITEMS +screen = SCREEN_START +``` + +--- + +## Expressions and Operators + +### Number Formats + +```c65 +value = 123 // Decimal +value = $FF // Hexadecimal ($ prefix) +value = %11111111 // Binary (% prefix) +``` + +### Arithmetic Operators + +```c65 +result = 10 + 5 // Addition +result = 100 - 5 // Subtraction +result = 4 * 10 // Multiplication +result = 100 / 5 // Division +``` + +### Bitwise Operators + +```c65 +mask = value & $FF // Bitwise AND +flags = flags | $01 // Bitwise OR +toggle = value ^ $FF // Bitwise XOR +``` + +### Increment and Decrement + +```c65 +counter++ // Increment by 1 +counter-- // Decrement by 1 +index++ +lives-- +``` + +### Critical: No Operator Precedence + +**Expressions evaluate strictly left to right!** There is no operator precedence. + +```c65 +result = 2 + 3 * 4 // Evaluates as (2+3)*4 = 20, NOT 2+(3*4) = 14 +value = 100 - 20 + 5 // Evaluates as (100-20)+5 = 85 +``` + +For complex expressions, use temporary variables: + +```c65 +// Instead of: result = a + b * c (which doesn't work as expected) +temp = b * c +result = a + temp +``` + +### Constant Expressions + +Expressions without spaces are treated as compile-time constants: + +```c65 +value = 100+50 // OK - constant expression, evaluated at compile time +value = 100 + 50 // ERROR - spaces make it a runtime expression +``` + +--- + +## Control Flow + +### IF Statements + +Basic conditional execution: + +```c65 +IF count == 10 + result = 1 +ENDIF + +IF value > threshold + process() +ELSE + skip() +ENDIF +``` + +Supported comparison operators: `=` `==` `<>` `!=` `>` `<` `>=` `<=` + +Single-parameter IF treats 0 as false, non-zero as true: + +```c65 +IF running + // Executes if running != 0 + update() +ENDIF +``` + +### WHILE Loops + +Loop while condition is true: + +```c65 +WHILE counter < 100 + counter++ + process(counter) +WEND + +WHILE running + gameLoop() +WEND + +WHILE x != y + x++ +WEND +``` + +### FOR Loops + +Loop with automatic counter: + +```c65 +FOR i = 0 TO 10 + screen = i +NEXT + +FOR x = 0 TO 255 STEP 2 + result = result + x +NEXT + +FOR counter = start TO finish + process(counter) +NEXT +``` + +### BREAK + +Exit a loop early: + +```c65 +FOR i = 0 TO 100 + IF i == 50 + BREAK + ENDIF + process(i) +NEXT + +WHILE running + IF error + BREAK + ENDIF + update() +WEND +``` + +--- + +## Functions + +### Declaring Functions + +```c65 +FUNC initialize + BYTE temp = 0 + screen = temp +FEND + +FUNC setColor(color, brightness) + borderColor = color + backgroundColor = brightness +FEND +``` + +### Calling Functions + +```c65 +initialize() +setColor(1, 14) +process(xpos, ypos, spriteData) +``` + +### Parameter Passing Modes + +Parameters can have different access modes: + +- `in:` - Read-only (default if not specified) +- `out:` - Write-only (for returning values) +- `io:` - Read-write + +```c65 +FUNC add(in:a, in:b, out:result) + result = a + b +FEND + +FUNC swap(io:x, io:y) + BYTE temp = x + x = y + y = temp +FEND +``` + +### Local Variables + +Variables declared inside functions are local: + +```c65 +FUNC calculate(value) + BYTE local = 10 // Only exists inside this function + WORD temp // Local temporary + temp = value + local +FEND +``` + +### Curly Brace Syntax + +Alternative parameter syntax using curly braces: + +```c65 +FUNC process({BYTE value} out:{BYTE result}) + result = value + 1 +FEND +``` + +### Returning from Functions + +```c65 +FUNC checkValue(value) + IF value == 0 + EXIT // Return early + ENDIF + process(value) +FEND +``` + +--- + +## Memory Operations + +### PEEK - Reading Memory + +Read a byte from memory: + +```c65 +value = PEEK $D020 // Read from absolute address +char = PEEK screenPtr[index] // Read with offset +byte = PEEK pointer // Read from pointer +``` + +**Important:** For indexed access, the address must be a WORD variable in zero page. + +```c65 +WORD buffer @ $FB // Zero-page pointer +value = PEEK buffer[10] // Read buffer+10 +``` + +### POKE - Writing Memory + +Write a byte to memory: + +```c65 +POKE $D020 WITH 0 // Write to absolute address +POKE screenPtr[index] WITH char // Write with offset +POKE pointer WITH value // Write to pointer +``` + +### PEEKW - Reading 16-bit Words + +Read a 16-bit value from memory: + +```c65 +WORD address +address = PEEKW $FFFC // Read reset vector + +WORD buffer @ $FB +value = PEEKW buffer[10] // Read word at buffer+10 +``` + +### POKEW - Writing 16-bit Words + +Write a 16-bit value to memory: + +```c65 +POKEW $0314 WITH irqHandler // Set IRQ vector +POKEW dataPtr[0] WITH address // Write word with offset +``` + +### POINTER - Setting Pointers + +Set a pointer to an address: + +```c65 +POINTER screenPtr TO $0400 +POINTER bufferPtr TO dataBuffer +POINTER funcPtr TO myFunction +``` + +--- + +## Code Blocks + +### ASM Blocks + +Inline assembly for direct hardware control: + +```c65 +ASM + lda #$00 + sta $d020 + jsr $ffd2 +ENDASM +``` + +#### Referencing Variables in ASM + +Global variables can be referenced directly: + +```c65 +BYTE temp = 5 + +ASM + lda temp + clc + adc #10 + sta temp +ENDASM +``` + +Local variables (inside FUNC) need pipe delimiters `|varname|`: + +```c65 +FUNC calculate(value) + BYTE local = 10 + ASM + lda |local| // Reference local variable + clc + adc value // Global parameter + sta |local| + ENDASM +FEND +``` + +### SCRIPT Blocks + +Generate assembly code at compile time using Starlark (Python-like): + +```c65 +// Generate a table of squares +SCRIPT + print("squares:") + for i in range(256): + print(" !8 %d" % (i * i % 256)) +ENDSCRIPT +``` + +#### Sine Table Example + +```c65 +SCRIPT + import math + print("sintable:") + for i in range(256): + angle = (i * 2.0 * math.pi) / 256.0 + sine = math.sin(angle) + value = int((sine + 1.0) * 127.5) + print(" !8 %d" % value) +ENDSCRIPT +``` + +#### Referencing Variables + +Use `|varname|` syntax in generated assembly: + +```c65 +BYTE tableSize = 64 + +SCRIPT + print("lookup:") + for i in range(64): + print(" !8 %d" % (i * 2)) + print(" // Size stored in |tableSize|") +ENDSCRIPT +``` + +--- + +## Preprocessor + +### Comments + +```c65 +// Single-line comment +BYTE counter = 0 // End-of-line comment +``` + +In ASM blocks, use assembly syntax: +```c65 +ASM + lda #$00 ; Assembly comment +ENDASM +``` + +In SCRIPT blocks, use Python syntax: +```c65 +SCRIPT + # Python-style comment + print("hello") +ENDSCRIPT +``` + +### Include Files + +```c65 +#INCLUDE mylib.c65 // Relative to current file +#INCLUDE lib/string.c65 // Subdirectory +#INCLUDE // Search in C65LIBPATH +``` + +Include guards prevent multiple inclusion: + +```c65 +#IFNDEF __MY_LIBRARY +#DEFINE __MY_LIBRARY = 1 + +// Library code here + +#IFEND +``` + +### Define Macros + +Text substitution macros: + +```c65 +#DEFINE MAX_SPEED = 10 +#DEFINE SCREEN = $$0400 // $$ escapes to literal $ + +speed = MAX_SPEED // Replaced with 10 +``` + +Special characters in defines: + +```c65 +#DEFINE SPACE = $20 // Space character (hex 20) +#DEFINE NEWLINE = $0D // Carriage return +#DEFINE HEXADDR = $$D020 // Literal "$D020" text +``` + +**Note:** Prefer `BYTE CONST` and `WORD CONST` over `#DEFINE` for constants. + +### Conditional Compilation + +```c65 +#IFDEF DEBUG + BYTE debugFlag = 1 +#IFEND + +#IFNDEF __LIB_INCLUDED + #DEFINE __LIB_INCLUDED = 1 + // Include library code +#IFEND +``` + +### Pragma Directives + +Control compiler behavior: + +```c65 +#PRAGMA _P_USE_LONG_JUMP 1 // Use JMP instead of branches +#PRAGMA _P_USE_IMMUTABLE_CODE 1 // No self-modifying code (for ROM) +#PRAGMA _P_USE_CBM_STRINGS 1 // Use PETSCII encoding +``` + +### Debug Directives + +```c65 +#PRINT Compiling main module // Print during compilation +#HALT // Stop compilation +``` + +--- + +## Common Patterns + +### Screen Manipulation + +```c65 +WORD CONST SCREEN = $0400 +WORD CONST COLOR_RAM = $D800 +BYTE CONST SCREEN_WIDTH = 40 +BYTE CONST SCREEN_HEIGHT = 25 + +FUNC clearScreen + WORD screenPtr @ $FB + POINTER screenPtr TO SCREEN + + WORD remaining = 1000 + WHILE remaining > 0 + POKE screenPtr[0] WITH 32 // Space character + screenPtr++ + remaining-- + WEND +FEND +``` + +### String Handling + +```c65 +// Print null-terminated string +FUNC printString(textPtr) + BYTE char + char = PEEK textPtr[0] + + WHILE char != 0 + ASM + lda |char| + jsr $FFD2 // CHROUT + ENDASM + textPtr++ + char = PEEK textPtr[0] + WEND +FEND +``` + +### Sprite Manipulation + +```c65 +WORD CONST VIC2 = $D000 +BYTE spriteX @ VIC2+0 +BYTE spriteY @ VIC2+1 +BYTE spriteEnable @ VIC2+21 + +FUNC enableSprite(spriteNum) + BYTE mask + mask = 1 + + FOR i = 0 TO spriteNum + mask = mask * 2 + NEXT + + spriteEnable = spriteEnable | mask +FEND +``` + +### Zero Page Optimization + +For frequently accessed pointers, use zero page: + +```c65 +WORD fastPtr @ $FB // Zero page = fast indexed access + +FUNC processBuffer(buffer, size) + POINTER fastPtr TO buffer + + WHILE size > 0 + BYTE value + value = PEEK fastPtr[0] + // Process value + fastPtr++ + size-- + WEND +FEND +``` + +### Interrupt Handlers + +```c65 +WORD CONST IRQ_VECTOR = $0314 +WORD oldIRQ + +FUNC installIRQ + ASM + sei // Disable interrupts + ENDASM + + oldIRQ = PEEKW IRQ_VECTOR + POKEW IRQ_VECTOR WITH myIRQ + + ASM + cli // Enable interrupts + ENDASM +FEND + +LABEL myIRQ + // IRQ handler code + ASM + // Save registers + pha + txa + pha + tya + pha + + // Do IRQ work + inc $d020 + + // Restore and return + pla + tay + pla + tax + pla + rti + ENDASM +``` + +### Lookup Tables + +Generate tables at compile time: + +```c65 +ASM +colorTable: +ENDASM + +SCRIPT + colors = [0, 1, 15, 12, 11, 9, 2, 8] + for c in colors: + print(" !8 %d" % c) +ENDSCRIPT +``` + +### Delay Loops + +```c65 +FUNC delay(frames) + BYTE raster @ $D012 + BYTE oldRaster + + WHILE frames > 0 + oldRaster = raster + WHILE raster == oldRaster + // Wait for raster to change + WEND + frames-- + WEND +FEND +``` + +### Bit Manipulation + +```c65 +// Set bit +flags = flags | %00000001 // Set bit 0 + +// Clear bit +flags = flags & %11111110 // Clear bit 0 + +// Toggle bit +flags = flags ^ %00000001 // Toggle bit 0 + +// Test bit +IF flags & %00000001 + // Bit 0 is set +ENDIF +``` + +--- + +## Best Practices + +### 1. Use Constants for Magic Numbers + +```c65 +// Bad +POKE $D020 WITH 5 + +// Good +BYTE CONST COLOR_GREEN = 5 +BYTE borderColor @ $D020 +borderColor = COLOR_GREEN +``` + +### 2. Zero Page for Performance + +Place frequently accessed pointers in zero page ($00-$FF): + +```c65 +WORD screenPtr @ $FB // Fast indexed access +WORD tempPtr @ $FD +``` + +### 3. Watch Expression Evaluation Order + +Remember: left-to-right evaluation, no precedence! + +```c65 +// Be careful with expressions +result = 2 + 3 * 4 // = 20, not 14 + +// Use temps for clarity +temp = 3 * 4 +result = 2 + temp // Now = 14 +``` + +### 4. Include Guards + +Always use include guards in library files: + +```c65 +#IFNDEF __MY_LIB +#DEFINE __MY_LIB = 1 + +// Library code + +#IFEND +``` + +### 5. Comment Your Code + +```c65 +// Explain what and why, not how +FUNC calculateTrajectory(velocity, angle) + // Use fixed-point math (8.8 format) + // because we don't have floating point + WORD xVel + WORD yVel + // ... +FEND +``` + +### 6. Use Meaningful Names + +```c65 +// Bad +BYTE x +BYTE y + +// Good +BYTE playerXPos +BYTE playerYPos +``` + +--- + +## Further Resources + +- See `commands.md` for complete command reference +- See `syntax.md` for detailed syntax rules +- Check the `lib/` directory for example library code +- Set `C65LIBPATH` environment variable to specify library search paths + +--- + +## Quick Reference Card + +```c65 +// Variables +BYTE varName = 10 +WORD address = $C000 +BYTE CONST MAX = 100 + +// Control Flow +IF x == 10 + // code +ENDIF + +WHILE x < 100 + x++ +WEND + +FOR i = 0 TO 10 + // code +NEXT + +// Functions +FUNC myFunc(in:param1, out:result) + result = param1 + 1 +FEND + +// Memory +value = PEEK $D020 +POKE $D020 WITH 5 +address = PEEKW $FFFC +POKEW $0314 WITH handler + +// Operators ++ - * / // Arithmetic +& | ^ // Bitwise +++ -- // Increment/Decrement +== != < > <= >= // Comparison + +// Blocks +ASM + // assembly code +ENDASM + +SCRIPT + # Python code +ENDSCRIPT +```