Fixed up docs to reflect reality and for examples to work.
This commit is contained in:
parent
8087aafc32
commit
11cc5f220b
4 changed files with 106 additions and 49 deletions
|
|
@ -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
|
- **Functions**: Named functions with parameters and call graph analysis
|
||||||
- **Control flow**: IF/ENDIF, WHILE/WEND, FOR loops, SWITCH/CASE
|
- **Control flow**: IF/ENDIF, WHILE/WEND, FOR loops, SWITCH/CASE
|
||||||
- **Memory operations**: PEEK/POKE/PEEKW/POKEW with zero-page optimization. Access registers as variables.
|
- **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
|
- **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)
|
- **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
|
- **Optimizations**: Constant folding, self-assignment detection
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ BREAK
|
||||||
|
|
||||||
```
|
```
|
||||||
// BREAK in FOR loop
|
// BREAK in FOR loop
|
||||||
|
BYTE i
|
||||||
FOR i = 0 TO 100
|
FOR i = 0 TO 100
|
||||||
IF i = 50
|
IF i = 50
|
||||||
BREAK
|
BREAK
|
||||||
|
|
@ -242,7 +243,8 @@ See [FUNC](#func) for syntax and examples.
|
||||||
|
|
||||||
## FOR
|
## FOR
|
||||||
|
|
||||||
Loop with automatic counter increment.
|
Loop with automatic counter increment. The iterator variable must be declared
|
||||||
|
beforehand.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
```
|
```
|
||||||
|
|
@ -253,6 +255,7 @@ FOR <iterator> = <start_value> TO <end_value>
|
||||||
|
|
||||||
```
|
```
|
||||||
// FOR loop with literal values
|
// FOR loop with literal values
|
||||||
|
BYTE i
|
||||||
FOR i = 0 TO 10
|
FOR i = 0 TO 10
|
||||||
screen = i
|
screen = i
|
||||||
NEXT
|
NEXT
|
||||||
|
|
@ -260,6 +263,7 @@ NEXT
|
||||||
|
|
||||||
```
|
```
|
||||||
// FOR loop with variables
|
// FOR loop with variables
|
||||||
|
BYTE counter
|
||||||
FOR counter = start TO finish
|
FOR counter = start TO finish
|
||||||
process(counter)
|
process(counter)
|
||||||
NEXT
|
NEXT
|
||||||
|
|
|
||||||
74
language.md
74
language.md
|
|
@ -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
|
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
|
```c65
|
||||||
// Instead of: result = (b - c) + a
|
// Instead of: result = (b - c) + a
|
||||||
|
|
@ -238,9 +244,10 @@ WEND
|
||||||
|
|
||||||
### FOR Loops
|
### FOR Loops
|
||||||
|
|
||||||
Loop with automatic counter:
|
Loop with automatic counter. The loop variable must be declared beforehand:
|
||||||
|
|
||||||
```c65
|
```c65
|
||||||
|
BYTE i
|
||||||
FOR i = 0 TO 10
|
FOR i = 0 TO 10
|
||||||
screen = i
|
screen = i
|
||||||
NEXT
|
NEXT
|
||||||
|
|
@ -255,6 +262,7 @@ NEXT
|
||||||
Exit a loop early:
|
Exit a loop early:
|
||||||
|
|
||||||
```c65
|
```c65
|
||||||
|
BYTE i
|
||||||
FOR i = 0 TO 100
|
FOR i = 0 TO 100
|
||||||
IF i == 50
|
IF i == 50
|
||||||
BREAK
|
BREAK
|
||||||
|
|
@ -354,15 +362,17 @@ Read a byte from memory:
|
||||||
|
|
||||||
```c65
|
```c65
|
||||||
value = PEEK $D020 // Read from absolute address
|
value = PEEK $D020 // Read from absolute address
|
||||||
char = PEEK screenPtr[index] // Read with offset
|
byte = PEEK pointer // Read through a WORD pointer
|
||||||
byte = PEEK pointer // Read from 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
|
```c65
|
||||||
WORD buffer @ $FB // Zero-page pointer
|
WORD buffer @ $FB // Zero-page pointer
|
||||||
value = PEEK buffer[10] // Read buffer+10
|
value = PEEK buffer[10] // Read buffer+10
|
||||||
|
char = PEEK buffer[index] // Read buffer+index
|
||||||
```
|
```
|
||||||
|
|
||||||
### POKE - Writing Memory
|
### POKE - Writing Memory
|
||||||
|
|
@ -371,8 +381,18 @@ Write a byte to memory:
|
||||||
|
|
||||||
```c65
|
```c65
|
||||||
POKE $D020, 0 // Write to absolute address
|
POKE $D020, 0 // Write to absolute address
|
||||||
POKE screenPtr[index], char // Write with offset
|
POKE pointer, value // Write through a WORD pointer
|
||||||
POKE pointer, value // Write to 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
|
### PEEKW - Reading 16-bit Words
|
||||||
|
|
@ -393,7 +413,8 @@ Write a 16-bit value to memory:
|
||||||
|
|
||||||
```c65
|
```c65
|
||||||
POKEW $0314, irqHandler // Set IRQ vector
|
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
|
### POINTER - Setting Pointers
|
||||||
|
|
@ -830,7 +851,7 @@ FEND
|
||||||
FUNC updateScreen
|
FUNC updateScreen
|
||||||
BYTE color
|
BYTE color
|
||||||
color = frameCount & $0F
|
color = frameCount & $0F
|
||||||
POKE screenPtr[0], color
|
POKE screenPtr, color
|
||||||
frameCount++
|
frameCount++
|
||||||
FEND
|
FEND
|
||||||
|
|
||||||
|
|
@ -857,7 +878,7 @@ FUNC clearScreen
|
||||||
|
|
||||||
WORD remaining = 1000
|
WORD remaining = 1000
|
||||||
WHILE remaining > 0
|
WHILE remaining > 0
|
||||||
POKE screenPtr[0], 32 // Space character
|
POKE screenPtr, 32 // Space character
|
||||||
screenPtr++
|
screenPtr++
|
||||||
remaining--
|
remaining--
|
||||||
WEND
|
WEND
|
||||||
|
|
@ -870,7 +891,7 @@ FEND
|
||||||
// Print null-terminated string
|
// Print null-terminated string
|
||||||
FUNC printString({WORD textPtr})
|
FUNC printString({WORD textPtr})
|
||||||
BYTE char
|
BYTE char
|
||||||
char = PEEK textPtr[0]
|
char = PEEK textPtr
|
||||||
|
|
||||||
WHILE char != 0
|
WHILE char != 0
|
||||||
ASM
|
ASM
|
||||||
|
|
@ -878,7 +899,7 @@ FUNC printString({WORD textPtr})
|
||||||
jsr $FFD2 // CHROUT
|
jsr $FFD2 // CHROUT
|
||||||
ENDASM
|
ENDASM
|
||||||
textPtr++
|
textPtr++
|
||||||
char = PEEK textPtr[0]
|
char = PEEK textPtr
|
||||||
WEND
|
WEND
|
||||||
FEND
|
FEND
|
||||||
```
|
```
|
||||||
|
|
@ -893,10 +914,11 @@ BYTE spriteEnable @ VIC2+21
|
||||||
|
|
||||||
FUNC enableSprite({BYTE spriteNum})
|
FUNC enableSprite({BYTE spriteNum})
|
||||||
BYTE mask
|
BYTE mask
|
||||||
|
BYTE i
|
||||||
mask = 1
|
mask = 1
|
||||||
|
|
||||||
FOR i = 0 TO spriteNum
|
FOR i = 0 TO spriteNum
|
||||||
mask = mask * 2
|
mask = mask << 1
|
||||||
NEXT
|
NEXT
|
||||||
|
|
||||||
spriteEnable = spriteEnable | mask
|
spriteEnable = spriteEnable | mask
|
||||||
|
|
@ -908,14 +930,14 @@ FEND
|
||||||
For frequently accessed pointers, use zero page:
|
For frequently accessed pointers, use zero page:
|
||||||
|
|
||||||
```c65
|
```c65
|
||||||
WORD fastPtr @ $FB // Zero page = fast indexed access
|
WORD fastPtr @ $FB // Zero page = fast pointer access
|
||||||
|
|
||||||
FUNC processBuffer({WORD buffer} {BYTE size})
|
FUNC processBuffer({WORD buffer} {BYTE size})
|
||||||
POINTER fastPtr TO buffer
|
POINTER fastPtr TO buffer
|
||||||
|
|
||||||
WHILE size > 0
|
WHILE size > 0
|
||||||
BYTE value
|
BYTE value
|
||||||
value = PEEK fastPtr[0]
|
value = PEEK fastPtr
|
||||||
// Process value
|
// Process value
|
||||||
fastPtr++
|
fastPtr++
|
||||||
size--
|
size--
|
||||||
|
|
@ -1012,11 +1034,6 @@ flags = flags & %11111110 // Clear bit 0
|
||||||
|
|
||||||
// Toggle bit
|
// Toggle bit
|
||||||
flags = flags ^ %00000001 // Toggle bit 0
|
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!
|
Remember: left-to-right evaluation, no precedence!
|
||||||
|
|
||||||
```c65
|
```c65
|
||||||
// Be careful with expressions
|
// Constant expressions fold left-to-right at compile time.
|
||||||
result = 2 + 3 * 4 // = 20, not 14
|
// Write them without spaces:
|
||||||
|
result = 2+3*4 // = (2+3)*4 = 20, not 14
|
||||||
|
|
||||||
// Use temps for clarity
|
// Runtime expressions (with variables) do ONE operation per
|
||||||
temp = 3 * 4
|
// statement and must be space-separated. Use temps to order them:
|
||||||
result = 2 + temp // Now = 14
|
temp = b + c
|
||||||
|
result = a + temp
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Include Guards
|
### 4. Include Guards
|
||||||
|
|
@ -1139,8 +1158,9 @@ address = PEEKW $FFFC
|
||||||
POKEW $0314, handler
|
POKEW $0314, handler
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
+ - * / // Arithmetic
|
+ - // Arithmetic (runtime + constants)
|
||||||
& | ^ // Bitwise
|
* / // Multiply/Divide (constant expressions only)
|
||||||
|
& | ^ << >> // Bitwise / shift
|
||||||
++ -- // Increment/Decrement
|
++ -- // Increment/Decrement
|
||||||
== != < > <= >= // Comparison
|
== != < > <= >= // Comparison
|
||||||
|
|
||||||
|
|
|
||||||
71
syntax.md
71
syntax.md
|
|
@ -12,6 +12,7 @@ C65GM uses C-style line comments.
|
||||||
**Examples:**
|
**Examples:**
|
||||||
```
|
```
|
||||||
BYTE counter = 0 // Initialize counter
|
BYTE counter = 0 // Initialize counter
|
||||||
|
BYTE i // Loop variable (must be declared)
|
||||||
// This is a full line comment
|
// This is a full line comment
|
||||||
FOR i = 0 TO 10 // Loop through values
|
FOR i = 0 TO 10 // Loop through values
|
||||||
counter++ // Increment
|
counter++ // Increment
|
||||||
|
|
@ -623,15 +624,26 @@ $00
|
||||||
|
|
||||||
### Operators
|
### Operators
|
||||||
|
|
||||||
Evaluated strictly left to right:
|
Evaluated strictly left to right (no precedence):
|
||||||
|
|
||||||
- `+` Addition
|
- `+` Addition (runtime and constant expressions)
|
||||||
- `-` Subtraction
|
- `-` Subtraction (runtime and constant expressions)
|
||||||
- `*` Multiplication
|
- `*` Multiplication (constant expressions only)
|
||||||
- `/` Division
|
- `/` Division (constant expressions only)
|
||||||
- `|` Bitwise OR
|
- `|` Bitwise OR
|
||||||
- `&` Bitwise AND
|
- `&` Bitwise AND
|
||||||
- `^` Bitwise XOR
|
- `^` 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
|
### Constants
|
||||||
|
|
||||||
|
|
@ -647,13 +659,20 @@ pointer = SCREEN
|
||||||
|
|
||||||
### Expression Examples
|
### Expression Examples
|
||||||
|
|
||||||
|
Constant expressions (no spaces, all terms literals or constants):
|
||||||
```
|
```
|
||||||
value = 100+50
|
value = 100+50
|
||||||
result = $FF-10
|
result = $FF-10
|
||||||
address = $D000+32
|
address = $D000+32
|
||||||
mask = %11110000&$0F
|
mask = %11110000&$0F
|
||||||
combined = base|offset
|
combined = BASE|OFFSET
|
||||||
calculated = start+length*2
|
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:
|
**Critical:** No operator precedence. Evaluation is strictly left to right:
|
||||||
|
|
@ -682,15 +701,22 @@ INC $D000+20
|
||||||
```
|
```
|
||||||
FOR i = 0 TO MAX_VALUE-1
|
FOR i = 0 TO MAX_VALUE-1
|
||||||
IF x > THRESHOLD+10
|
IF x > THRESHOLD+10
|
||||||
POKE $D020+offset, value
|
POKE $D020+OFFSET, value
|
||||||
result = PEEK $0400+index
|
result = PEEK $0400+INDEX
|
||||||
```
|
```
|
||||||
|
|
||||||
**Arithmetic operations:**
|
**Arithmetic operations:**
|
||||||
|
|
||||||
|
Constant expressions (compile-time, all terms literal/CONST):
|
||||||
```
|
```
|
||||||
sum = value1+value2
|
sum = VALUE1+VALUE2
|
||||||
product = base*factor
|
adjusted = BASE+OFFSET
|
||||||
adjusted = original+OFFSET
|
```
|
||||||
|
|
||||||
|
Runtime expressions (one operation, may use variables):
|
||||||
|
```
|
||||||
|
sum = value1 + value2
|
||||||
|
adjusted = original + offset
|
||||||
```
|
```
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
|
@ -702,11 +728,15 @@ result = (a+b)*c
|
||||||
value = base+(offset*2)
|
value = base+(offset*2)
|
||||||
```
|
```
|
||||||
|
|
||||||
**No nested expressions in assignments:**
|
**No multi-operation runtime expressions:**
|
||||||
```
|
```
|
||||||
; NOT SUPPORTED:
|
; NOT SUPPORTED (more than one operation with variables):
|
||||||
x = y + z ; only single value or constant expression
|
x = a + b + c ; chain of runtime operations
|
||||||
x = a+b ; constant expression (no spaces) OK if a,b are constants
|
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:**
|
**Workaround for complex expressions:**
|
||||||
|
|
@ -720,7 +750,10 @@ result = temp - c
|
||||||
|
|
||||||
Expressions without spaces are treated as constant expressions:
|
Expressions without spaces are treated as constant expressions:
|
||||||
```
|
```
|
||||||
value = 100+50 ; OK - constant expression
|
value = 100+50 ; constant expression (folded at compile time)
|
||||||
value = 100 + 50 ; ERROR - not a simple assignment
|
value = 100 + 50 ; single runtime operation (also OK)
|
||||||
value = MAX+10 ; OK if MAX is constant
|
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.
|
||||||
Loading…
Reference in a new issue