Added claude code docker setup for project. Added language guide.
This commit is contained in:
parent
5f01282df5
commit
b54183aab4
5 changed files with 916 additions and 0 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -27,3 +27,6 @@ out/
|
||||||
*.tmp
|
*.tmp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
c65gm
|
c65gm
|
||||||
|
|
||||||
|
.claude/
|
||||||
|
.npm/
|
||||||
|
|
|
||||||
4
Dockerfile
Normal file
4
Dockerfile
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
FROM node:18-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm install -g @anthropic-ai/claude-code
|
||||||
|
CMD ["claude"]
|
||||||
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-c65gm
|
||||||
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
|
|
@ -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
|
||||||
892
language.md
Normal file
892
language.md
Normal file
|
|
@ -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 <stdlib.c65> // 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
|
||||||
|
```
|
||||||
Loading…
Reference in a new issue