# Syntax Reference ## Comments C65GM uses C-style line comments. **Syntax:** ``` // ``` **Examples:** ``` BYTE counter = 0 // Initialize counter // This is a full line comment FOR i = 0 TO 10 // Loop through values counter++ // Increment NEXT ``` **Notes:** - Comments start with `//` and continue to end of line - Comments in ASM blocks use assembly syntax: `;` - Comments in SCRIPT blocks use Starlark syntax: `#` --- ## Preprocessor Directives ### #DEFINE Defines a text substitution macro. **Syntax:** ``` #DEFINE [= ] ``` **Examples:** ``` #DEFINE MAX_SPEED = 10 #DEFINE HELLO = GOODBYE // Will replace the text HELLO in all source with GOODBYE (except ASM and SCRIPT blocks) #DEFINE HELLO2 = GOOOOOODBYE // Longest match wins. If HELLO2 is encountered this will be the match not the HELLO define above #DEFINE SCREEN = $$0400 // Notice $$, see below. #DEFINE OFFSET = 255 ``` Note: Replacements are not recursive. Once a match is found, it's replaced, and processing starts after the replacement. **Special Characters in Values:** Define values support `$` escape sequences for special characters: - `$XX` (two hex digits) = character with code XX - `$$` = literal `$` character ``` #DEFINE SPACE = $20 // space character #DEFINE NEWLINE = $0D // carriage return #DEFINE TAB = $09 // tab character #DEFINE HEXADDR = $$D020 // stores "$D020" as text ``` **Notes:** - Optional `=` separator - Value can contain previously defined macros - Macros are expanded in source lines - Case sensitive - `$` escapes are processed when the define is created, not when used - For most purposes, use `WORD CONST` or `BYTE CONST` instead of preprocessor defines to define constant named values --- ### #UNDEF Undefines a previously defined macro. **Syntax:** ``` #UNDEF ``` **Examples:** ``` #DEFINE DEBUG = 1 #UNDEF DEBUG ``` --- ### #IFDEF Conditional compilation if macro is defined. **Syntax:** ``` #IFDEF ``` **Examples:** ``` #IFDEF DEBUG BYTE debugFlag = 1 #IFEND #IFDEF PAL BYTE scanlines = 312 #IFEND ``` --- ### #IFNDEF Conditional compilation if macro is not defined. **Syntax:** ``` #IFNDEF ``` **Examples:** ``` #IFNDEF __INCLUDED_SPRITE_LIB #DEFINE __INCLUDED_SPRITE_LIB = 1 FUNC drawSprite ; sprite code FEND #IFEND ``` **Notes:** - Common pattern for include guards - Prevents multiple inclusion --- ### #IFEND Ends conditional compilation block. **Syntax:** ``` #IFEND ``` **Examples:** ``` See #IFDEF and #IFNDEF ``` --- ### #INCLUDE Includes another source file. **Syntax:** ``` #INCLUDE #INCLUDE ``` **Examples:** ``` #INCLUDE sprites.c65 #INCLUDE lib/math.c65 #INCLUDE "constants.c65" #INCLUDE ``` **Notes:** - Relative path: resolves relative to current file - Angle brackets ``: searches in library path defined by C65LIBPATH environment variable - Supports nested includes - Use with #IFNDEF for include guards --- ### #PRINT Prints message during compilation. **Syntax:** ``` #PRINT ``` **Examples:** ``` #PRINT Compiling main module #PRINT MAX_VALUE #PRINT Debug build enabled ``` **Notes:** - Useful for debugging preprocessor - Macros expanded before printing --- ### #HALT Stops compilation immediately. **Syntax:** ``` #HALT ``` **Examples:** ``` #IFDEF INCOMPLETE #PRINT Feature not implemented #HALT #IFEND ``` **Notes:** - Halts entire compilation process, not just preprocessing - Returns exit code 2 --- ### #PRAGMA Sets compiler pragmas (options). **Syntax:** ``` #PRAGMA [] ``` **Available Pragmas:** **_P_USE_LONG_JUMP** - Uses JMP instead of branch instructions for IF/WHILE/FOR - Needed when branch targets exceed 127 byte range - Value: any non-zero value enables **_P_USE_IMMUTABLE_CODE** - Prevents self-modifying code generation - Required for ROM-based code - Errors on PEEK/POKE/GOSUB with variable addresses **_P_USE_CBM_STRINGS** - Encodes strings in CBM PETSCII format - Default: ASCII encoding - Value: any non-empty value enables **Examples:** ``` #PRAGMA _P_USE_LONG_JUMP 1 #PRAGMA _P_USE_IMMUTABLE_CODE 1 #PRAGMA _P_USE_IMMUTABLE_CODE 0 //turn off pragma #PRAGMA _P_USE_CBM_STRINGS 1 ``` --- ## Code Blocks ### ASM...ENDASM Inline assembly code block. **Syntax:** ``` ASM ENDASM ``` **Examples:** ``` ASM lda #$00 sta $d020 jsr $ffd2 ENDASM BYTE temp = 5 ASM lda temp clc adc #10 sta temp ENDASM FUNC calculate(value) BYTE local = 10 ASM lda |local| ; Reference local variable clc adc value sta |local| ENDASM FEND ``` **Notes:** - Assembly code passed through to ACME assembler unchanged - Can reference global variables by name - Local variables inside FUNC must use `|varname|` syntax - Comments in ASM blocks use `;` (assembly syntax) - No macro expansion inside ASM blocks --- ### SCRIPT...ENDSCRIPT Starlark script code block for generating assembly code. Scripts are written in Starlark (Python-like language) and executed at compile time. Output from `print()` statements is fed directly into the assembler. **Syntax:** ``` SCRIPT ENDSCRIPT ``` **Examples:** **Simple data generation:** ``` SCRIPT for i in range(256): print(" !8 %d" % i) ENDSCRIPT ``` **Sine table generation:** ``` 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 ``` **Using compiler variables:** ``` BYTE tableSize = 64 SCRIPT # Reference variables in generated assembly print("lookup:") for i in range(64): print(" !8 %d" % (i * 2)) print(" ; Table size is stored in |tableSize|") ENDSCRIPT ``` **Notes:** - Scripts use Starlark language (Python-like subset) - Comments in SCRIPT blocks use `#` (Python syntax) - Output from `print()` goes directly to assembler - Can reference compiler variables using `|varname|` syntax - Math module available: `import math` - Maximum 1 million execution steps (prevents infinite loops) - Executed at compile time, not runtime --- ### SCRIPT LIBRARY...ENDSCRIPT Defines reusable Starlark functions that persist across all subsequent SCRIPT blocks. **Syntax:** ``` SCRIPT LIBRARY 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, ...) 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. ### Number Formats **Decimal:** ``` 123 0 255 65535 ``` **Hexadecimal ($-prefix):** ``` $FF $D020 $C000 $00 ``` **Binary (%-prefix):** ``` %11111111 %10000000 %00000001 %11110000 ``` ### Operators Evaluated strictly left to right: - `+` Addition - `-` Subtraction - `*` Multiplication - `/` Division - `|` Bitwise OR - `&` Bitwise AND - `^` Bitwise XOR ### Constants Named constants defined with BYTE CONST or WORD CONST: ``` BYTE CONST MAX_SPEED = 10 WORD CONST SCREEN = $0400 speed = MAX_SPEED pointer = SCREEN ``` ### Expression Examples ``` value = 100+50 result = $FF-10 address = $D000+32 mask = %11110000&$0F combined = base|offset calculated = start+length*2 ``` **Critical:** No operator precedence. Evaluation is strictly left to right: ``` result = 2+3*4 ; evaluates as (2+3)*4 = 20, not 2+(3*4) = 14 value = $10|$20&$0F ; evaluates as ($10|$20)&$0F, not $10|($20&$0F) ``` ### Usage Contexts Expressions accepted anywhere a numeric literal is expected: **Variable initialization:** ``` BYTE count = 10+5 WORD addr = $C000+$100 ``` **Absolute addresses:** ``` BYTE screen @ $0400+40*10 INC $D000+20 ``` **Command parameters:** ``` FOR i = 0 TO MAX_VALUE-1 IF x > THRESHOLD+10 POKE $D020+offset WITH value result = PEEK $0400+index ``` **Arithmetic operations:** ``` sum = value1+value2 product = base*factor adjusted = original+OFFSET ``` ### Limitations **No parentheses for grouping:** ``` ; NOT SUPPORTED: result = (a+b)*c value = base+(offset*2) ``` **No nested expressions in assignments:** ``` ; NOT SUPPORTED: x = y + z ; only single value or constant expression x = a+b ; constant expression (no spaces) OK if a,b are constants ``` **Workaround for complex expressions:** Use temporary variables: ``` temp = a + b result = temp - c ``` ### Constant Expressions 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 ```