Fixed up docs to reflect reality and for examples to work.

This commit is contained in:
Mattias Hansson 2026-07-12 19:31:38 +02:00
parent 8087aafc32
commit 11cc5f220b
4 changed files with 106 additions and 49 deletions

View file

@ -39,7 +39,7 @@ c65gm compiles high-level source code into ACME assembler syntax for the 6502 pr
- **Functions**: Named functions with parameters and call graph analysis
- **Control flow**: IF/ENDIF, WHILE/WEND, FOR loops, SWITCH/CASE
- **Memory operations**: PEEK/POKE/PEEKW/POKEW with zero-page optimization. Access registers as variables.
- **Operators**: Arithmetic (ADD, SUB), bitwise (AND, OR, XOR)
- **Operators**: Arithmetic (ADD, SUB), bitwise (AND, OR, XOR), shifts (SHL, SHR)
- **Preprocessor**: File inclusion, macros, conditional compilation, Starlark scripting
- **Standard library**: C64 screen/kernal routines, memory management, string handling, graphics (Koala), FAT16 filesystem, and more (accessed via `#include <file>`, path set by `C65LIBPATH` environment variable)
- **Optimizations**: Constant folding, self-assignment detection

View file

@ -91,6 +91,7 @@ BREAK
```
// BREAK in FOR loop
BYTE i
FOR i = 0 TO 100
IF i = 50
BREAK
@ -242,7 +243,8 @@ See [FUNC](#func) for syntax and examples.
## FOR
Loop with automatic counter increment.
Loop with automatic counter increment. The iterator variable must be declared
beforehand.
**Syntax:**
```
@ -253,6 +255,7 @@ FOR <iterator> = <start_value> TO <end_value>
```
// FOR loop with literal values
BYTE i
FOR i = 0 TO 10
screen = i
NEXT
@ -260,6 +263,7 @@ NEXT
```
// FOR loop with variables
BYTE counter
FOR counter = start TO finish
process(counter)
NEXT

View file

@ -178,7 +178,13 @@ 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:
These multi-term forms are folded at compile time and only work when every
term is a literal or `CONST` (write them without spaces). `*` and `/` are
**only** available in these constant expressions, not on runtime variables.
Runtime expressions involving a variable perform exactly one operation and
must be space-separated (`dest = a + b`). For anything more complex, use
temporary variables:
```c65
// Instead of: result = (b - c) + a
@ -238,9 +244,10 @@ WEND
### FOR Loops
Loop with automatic counter:
Loop with automatic counter. The loop variable must be declared beforehand:
```c65
BYTE i
FOR i = 0 TO 10
screen = i
NEXT
@ -255,6 +262,7 @@ NEXT
Exit a loop early:
```c65
BYTE i
FOR i = 0 TO 100
IF i == 50
BREAK
@ -354,15 +362,17 @@ 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
byte = PEEK pointer // Read through a WORD pointer
```
**Important:** For indexed access, the address must be a WORD variable in zero page.
**Indexed access:** Add `[offset]` to read at pointer+offset. The offset can
be a constant or a BYTE variable, and the pointer **must** be a WORD variable
in zero page:
```c65
WORD buffer @ $FB // Zero-page pointer
value = PEEK buffer[10] // Read buffer+10
char = PEEK buffer[index] // Read buffer+index
```
### POKE - Writing Memory
@ -371,8 +381,18 @@ Write a byte to memory:
```c65
POKE $D020, 0 // Write to absolute address
POKE screenPtr[index], char // Write with offset
POKE pointer, value // Write to pointer
POKE pointer, value // Write through a WORD pointer
```
**Indexed access:** As with PEEK, `[offset]` requires a zero-page WORD
pointer. This is handy for reaching into structured data through a base
pointer:
```c65
WORD vic @ $FB
POINTER vic TO $D000 // VIC-II base register
POKE vic[$20], 2 // Write border color ($D020) = red
POKE vic[$21], 0 // Write background color ($D021) = black
```
### PEEKW - Reading 16-bit Words
@ -393,7 +413,8 @@ Write a 16-bit value to memory:
```c65
POKEW $0314, irqHandler // Set IRQ vector
POKEW dataPtr[0], address // Write word with offset
POKEW dataPtr, address // Write word through a pointer
POKEW dataPtr[2], address // Write word at dataPtr+2 (zero-page pointer)
```
### POINTER - Setting Pointers
@ -830,7 +851,7 @@ FEND
FUNC updateScreen
BYTE color
color = frameCount & $0F
POKE screenPtr[0], color
POKE screenPtr, color
frameCount++
FEND
@ -857,7 +878,7 @@ FUNC clearScreen
WORD remaining = 1000
WHILE remaining > 0
POKE screenPtr[0], 32 // Space character
POKE screenPtr, 32 // Space character
screenPtr++
remaining--
WEND
@ -870,7 +891,7 @@ FEND
// Print null-terminated string
FUNC printString({WORD textPtr})
BYTE char
char = PEEK textPtr[0]
char = PEEK textPtr
WHILE char != 0
ASM
@ -878,7 +899,7 @@ FUNC printString({WORD textPtr})
jsr $FFD2 // CHROUT
ENDASM
textPtr++
char = PEEK textPtr[0]
char = PEEK textPtr
WEND
FEND
```
@ -893,10 +914,11 @@ BYTE spriteEnable @ VIC2+21
FUNC enableSprite({BYTE spriteNum})
BYTE mask
BYTE i
mask = 1
FOR i = 0 TO spriteNum
mask = mask * 2
mask = mask << 1
NEXT
spriteEnable = spriteEnable | mask
@ -908,14 +930,14 @@ FEND
For frequently accessed pointers, use zero page:
```c65
WORD fastPtr @ $FB // Zero page = fast indexed access
WORD fastPtr @ $FB // Zero page = fast pointer access
FUNC processBuffer({WORD buffer} {BYTE size})
POINTER fastPtr TO buffer
WHILE size > 0
BYTE value
value = PEEK fastPtr[0]
value = PEEK fastPtr
// Process value
fastPtr++
size--
@ -1012,11 +1034,6 @@ flags = flags & %11111110 // Clear bit 0
// Toggle bit
flags = flags ^ %00000001 // Toggle bit 0
// Test bit
IF flags & %00000001
// Bit 0 is set
ENDIF
```
---
@ -1049,12 +1066,14 @@ WORD tempPtr @ $FD
Remember: left-to-right evaluation, no precedence!
```c65
// Be careful with expressions
result = 2 + 3 * 4 // = 20, not 14
// Constant expressions fold left-to-right at compile time.
// Write them without spaces:
result = 2+3*4 // = (2+3)*4 = 20, not 14
// Use temps for clarity
temp = 3 * 4
result = 2 + temp // Now = 14
// Runtime expressions (with variables) do ONE operation per
// statement and must be space-separated. Use temps to order them:
temp = b + c
result = a + temp
```
### 4. Include Guards
@ -1139,8 +1158,9 @@ address = PEEKW $FFFC
POKEW $0314, handler
// Operators
+ - * / // Arithmetic
& | ^ // Bitwise
+ - // Arithmetic (runtime + constants)
* / // Multiply/Divide (constant expressions only)
& | ^ << >> // Bitwise / shift
++ -- // Increment/Decrement
== != < > <= >= // Comparison

View file

@ -12,6 +12,7 @@ C65GM uses C-style line comments.
**Examples:**
```
BYTE counter = 0 // Initialize counter
BYTE i // Loop variable (must be declared)
// This is a full line comment
FOR i = 0 TO 10 // Loop through values
counter++ // Increment
@ -623,15 +624,26 @@ $00
### Operators
Evaluated strictly left to right:
Evaluated strictly left to right (no precedence):
- `+` Addition
- `-` Subtraction
- `*` Multiplication
- `/` Division
- `+` Addition (runtime and constant expressions)
- `-` Subtraction (runtime and constant expressions)
- `*` Multiplication (constant expressions only)
- `/` Division (constant expressions only)
- `|` Bitwise OR
- `&` Bitwise AND
- `^` Bitwise XOR
- `<<` Shift left
- `>>` Shift right
There are two distinct expression forms:
- **Constant expressions** — written *without spaces* (e.g. `2+3*4`). Every
term must be a literal or a `CONST`; these are folded at compile time and
may chain any number of terms. `*` and `/` are only available here.
- **Runtime expressions** — written *with spaces* (e.g. `a + b`). These
perform exactly one operation, where at least one operand may be a variable.
Only `+ - & | ^ << >>` are supported; `*` and `/` are not.
### Constants
@ -647,13 +659,20 @@ pointer = SCREEN
### Expression Examples
Constant expressions (no spaces, all terms literals or constants):
```
value = 100+50
result = $FF-10
address = $D000+32
mask = %11110000&$0F
combined = base|offset
calculated = start+length*2
combined = BASE|OFFSET
calculated = START+LENGTH*2
```
Runtime expressions (spaces, one operation, may use variables):
```
combined = base | offset
adjusted = value + count
```
**Critical:** No operator precedence. Evaluation is strictly left to right:
@ -682,15 +701,22 @@ INC $D000+20
```
FOR i = 0 TO MAX_VALUE-1
IF x > THRESHOLD+10
POKE $D020+offset, value
result = PEEK $0400+index
POKE $D020+OFFSET, value
result = PEEK $0400+INDEX
```
**Arithmetic operations:**
Constant expressions (compile-time, all terms literal/CONST):
```
sum = value1+value2
product = base*factor
adjusted = original+OFFSET
sum = VALUE1+VALUE2
adjusted = BASE+OFFSET
```
Runtime expressions (one operation, may use variables):
```
sum = value1 + value2
adjusted = original + offset
```
### Limitations
@ -702,11 +728,15 @@ result = (a+b)*c
value = base+(offset*2)
```
**No nested expressions in assignments:**
**No multi-operation runtime expressions:**
```
; NOT SUPPORTED:
x = y + z ; only single value or constant expression
x = a+b ; constant expression (no spaces) OK if a,b are constants
; NOT SUPPORTED (more than one operation with variables):
x = a + b + c ; chain of runtime operations
x = a + b * c ; ditto
; SUPPORTED:
x = y + z ; single runtime operation (one operator)
x = a+b ; constant expression (no spaces) if a,b are literals/CONST
```
**Workaround for complex expressions:**
@ -720,7 +750,10 @@ result = temp - c
Expressions without spaces are treated as constant expressions:
```
value = 100+50 ; OK - constant expression
value = 100 + 50 ; ERROR - not a simple assignment
value = MAX+10 ; OK if MAX is constant
value = 100+50 ; constant expression (folded at compile time)
value = 100 + 50 ; single runtime operation (also OK)
value = MAX+10 ; constant expression, OK if MAX is a constant
```
The difference matters for chaining: `2+3*4` (no spaces) folds all terms at
compile time, while a spaced form allows only one operation.