Updated docs to describe new SCRIPT blocks including macros

This commit is contained in:
Mattias Hansson 2026-02-11 14:06:09 +01:00
parent 83a0a20393
commit 104e950d63
3 changed files with 256 additions and 1 deletions

View file

@ -39,6 +39,8 @@ ENDSCRIPT
// SCRIPT MACRO: Named macros with parameters
//-----------------------------------------------------------
SCRIPT MACRO delay(cycles)
if cycles < 2:
fail("Cannot delay less than 2 cycles")
emit_delay(cycles)
ENDSCRIPT

View file

@ -495,6 +495,101 @@ SCRIPT
ENDSCRIPT
```
### SCRIPT LIBRARY Blocks
Define reusable Starlark functions that persist across all subsequent SCRIPT blocks:
```c65
SCRIPT LIBRARY
def emit_nops(count):
for i in range(count):
print(" nop")
def emit_delay(cycles):
for i in range(cycles // 4):
print(" nop")
print(" nop")
remainder = cycles % 4
if remainder >= 3:
print(" bit $ea")
remainder -= 3
for i in range(remainder // 2):
print(" nop")
ENDSCRIPT
// Later, use the library functions:
SCRIPT
emit_nops(5)
emit_delay(10)
ENDSCRIPT
```
Library functions are typically placed in include files for reuse across projects. Multiple SCRIPT LIBRARY blocks accumulate.
### SCRIPT MACRO Blocks
Define named, parameterized macros that expand inline when invoked:
```c65
SCRIPT MACRO delay(cycles)
if cycles < 2:
fail("Cannot delay less than 2 cycles")
emit_delay(cycles)
ENDSCRIPT
SCRIPT MACRO set_irq(handler)
print(" lda #<%s" % handler)
print(" sta $fffe")
print(" lda #>%s" % handler)
print(" sta $ffff")
ENDSCRIPT
```
#### Invoking Macros
Outside ASM blocks, use `@name(args)`:
```c65
BYTE CONST CYCLES_PER_LINE = 63
@delay(10)
@delay(CYCLES_PER_LINE-20)
@set_irq(my_handler)
```
Inside ASM blocks, use `|@name(args)|`:
```c65
ASM
lda #$00
|@delay(8)|
sta $d020
ENDASM
```
#### Parameter Types
- **Integer expressions**: Numbers, constants, arithmetic - passed as int
- **Labels**: Bare identifiers that aren't constants - passed as string
```c65
@delay(10) // 10 passed as int
@delay(CYCLES_PER_LINE-20) // Evaluated, passed as int
@set_irq(my_handler) // "my_handler" passed as string
```
#### Error Handling
Use `fail()` to stop compilation with an error:
```c65
SCRIPT MACRO delay(cycles)
if cycles < 2:
fail("Cannot delay less than 2 cycles")
emit_delay(cycles)
ENDSCRIPT
```
---
## Preprocessor
@ -975,6 +1070,19 @@ ASM
ENDASM
SCRIPT
# Python code
# Starlark code
ENDSCRIPT
SCRIPT LIBRARY
def my_func():
print(" nop")
ENDSCRIPT
SCRIPT MACRO name(param)
my_func()
ENDSCRIPT
// Macro invocation
@name(10) // Outside ASM
|@name(10)| // Inside ASM
```

145
syntax.md
View file

@ -366,6 +366,151 @@ ENDSCRIPT
---
### SCRIPT LIBRARY...ENDSCRIPT
Defines reusable Starlark functions that persist across all subsequent SCRIPT blocks.
**Syntax:**
```
SCRIPT LIBRARY
<starlark function definitions>
ENDSCRIPT
```
**Examples:**
**Defining reusable functions:**
```
SCRIPT LIBRARY
def emit_nops(count):
for i in range(count):
print(" nop")
def emit_delay(cycles):
for i in range(cycles // 4):
print(" nop")
print(" nop")
remainder = cycles % 4
if remainder >= 3:
print(" bit $ea")
remainder -= 3
for i in range(remainder // 2):
print(" nop")
ENDSCRIPT
```
**Using library functions in SCRIPT blocks:**
```
SCRIPT
emit_nops(5)
emit_delay(10)
ENDSCRIPT
```
**Notes:**
- Functions defined in SCRIPT LIBRARY are available to all subsequent SCRIPT, SCRIPT LIBRARY, and SCRIPT MACRO blocks
- Multiple SCRIPT LIBRARY blocks accumulate - later blocks can use functions from earlier ones
- Typically placed in include files for reuse across projects
- The library block itself can also use `print()` to emit code, but this is uncommon
---
### SCRIPT MACRO...ENDSCRIPT
Defines a named, parameterized macro that expands inline when invoked.
**Syntax:**
```
SCRIPT MACRO name(param1, param2, ...)
<starlark code>
ENDSCRIPT
```
**Invocation:**
```
@name(arg1, arg2, ...) // Outside ASM blocks
|@name(arg1, arg2, ...)| // Inside ASM blocks
```
**Examples:**
**Defining macros:**
```
SCRIPT MACRO delay(cycles)
if cycles < 2:
fail("Cannot delay less than 2 cycles")
emit_delay(cycles)
ENDSCRIPT
SCRIPT MACRO set_irq(handler)
print(" lda #<%s" % handler)
print(" sta $fffe")
print(" lda #>%s" % handler)
print(" sta $ffff")
ENDSCRIPT
```
**Invoking macros outside ASM blocks:**
```
BYTE CONST CYCLES_PER_LINE = 63
@delay(10)
@delay(CYCLES_PER_LINE-20)
@set_irq(my_handler)
```
**Invoking macros inside ASM blocks:**
```
ASM
lda #$00
|@delay(8)|
sta $d020
ENDASM
```
**Parameter Types:**
1. **Integer expressions** - Evaluated at compile time:
- Literal numbers: `10`, `$FF`, `%11110000`
- Constants: `CYCLES_PER_LINE`, `MAX_VALUE`
- Arithmetic: `CYCLES_PER_LINE-20`, `BASE+OFFSET`
- Passed to macro as Starlark `int`
2. **Labels/Identifiers** - Passed as strings:
- Bare identifiers that don't resolve to constants: `my_handler`, `loop_start`
- Passed to macro as Starlark `string`
- Use in generated assembly: `print(" jmp %s" % label)`
**Checking parameter types in macros:**
```
SCRIPT MACRO flexible(value)
if type(value) == "int":
print(" lda #%d" % value)
else:
print(" lda %s" % value)
ENDSCRIPT
```
**Error handling with fail():**
```
SCRIPT MACRO delay(cycles)
if cycles < 2:
fail("Cannot delay less than 2 cycles")
emit_delay(cycles)
ENDSCRIPT
```
The `fail()` function stops compilation with an error message pointing to the macro invocation site.
**Notes:**
- Macros are NOT executed at definition time - only when invoked
- Macros have access to all SCRIPT LIBRARY functions
- `print()` output replaces the macro invocation site
- Everything expands inline - no JSR/RTS overhead
- Use for cycle-critical code like raster timing
---
## Expression Syntax
Expressions evaluate left to right with no operator precedence.