# 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. [Writing Libraries](#writing-libraries)
10. [Common Patterns](#common-patterns)

---

## Getting Started

### Your First C64 Program

Here's a simple complete program that changes the screen border color:

```c65
#INCLUDE <c64start.c65>
#INCLUDE <c64defs.c65>

GOTO start

FUNC main
    BYTE borderColor @ $D020
    borderColor = color_green
FEND

LABEL start
main()
```

### Creating a C64 Executable

To create a runnable C64 program, always start with:

```c65
#INCLUDE <c64start.c65>
```

This creates a valid C64 executable with a BASIC loader (`0 SYS 2064`) and sets up the proper memory layout.

### Program Structure

A typical c65gm program for C64 follows this pattern:

```c65
#INCLUDE <c64start.c65>
#INCLUDE <c64defs.c65>    // Optional: standard C64 definitions

GOTO start                 // Jump over your function definitions

// Variables
BYTE counter = 0
WORD screen = $0400

// Functions - must be declared before we use them.
FUNC processData
    // Do something
FEND

FUNC initialize
    counter = 10
    processData()
FEND

// Entry point - execution starts here
LABEL start
initialize()
```

**Important:** The `GOTO start` jumps over your function definitions. Without it, the CPU would try to execute directly into your function code, causing a crash. Always put `GOTO start` before your functions, and `LABEL start` before your actual program entry point.

---

## Variables and Types

### BYTE Variables

BYTE variables store 8-bit values (0-255).

```c65
BYTE count                    // Uninitialized (0)
BYTE speed = 5               // Initialized to 5
BYTE REGISTER temp           // Register-hinted (default 0)
BYTE REGISTER scratch = 0    // Register-hinted with init value
BYTE screen @ $D020          // Memory-mapped to specific address
BYTE CONST MAX_SPEED = 10    // Constant
```

### REGISTER Variables

The `REGISTER` hint tells the optimizer that a BYTE variable's value only matters as
it flows through the computation — never its stored location. The compiler can keep
the value in a CPU register (A, X, or Y) and eliminate dead stores and memory
allocations entirely.

```c65
FUNC fast_copy
  BYTE REGISTER b
  b = PEEK $d011
  b = b | 32
  POKE $d011, b             // b never touches RAM — flows through A
FEND
```

**Rules:**

- `BYTE REGISTER` is only valid inside `FUNC`/`FEND` blocks (function-local only)
- Cannot be combined with `@` (memory-mapped) or `CONST`
- Cannot be referenced from `ASM`, `SCRIPT`, or `MACRO` blocks via `|varname|`
- `WORD REGISTER` is not supported (the 6502 has no 16-bit ALU register)
- Without `--opt`, `REGISTER` behaves identically to a normal `BYTE`
- With `--opt`, the optimizer may dissolve the variable, eliminating its `!8`
  allocation and all store/load operations

### 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 dataPtr = @myData          // Initialized to label address
WORD screenPtr @ $FB            // Zero-page pointer
WORD CONST SCREEN_RAM = $0400   // Constant
```

### Memory-Mapped Variables

Variables can be placed at specific addresses using `@`. Not compatible with `REGISTER`.

```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
```

### Bitwise Operators

```c65
mask = value & $FF     // Bitwise AND
flags = flags | $01    // Bitwise OR
toggle = value ^ $FF   // Bitwise XOR
result = value << 3    // Shift left
mask = flags >> 2      // Shift right
```

### 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
```

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
temp = b - c
result = a + temp
```

---

## 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. The loop variable must be declared beforehand:

```c65
BYTE i
FOR i = 0 TO 10
    screen = i
NEXT

FOR counter = start TO finish
    process(counter)
NEXT
```

### BREAK

Exit a loop early:

```c65
BYTE i
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({BYTE color} {BYTE brightness})
    borderColor = color
    backgroundColor = brightness
FEND
```

### Calling Functions

```c65
initialize()
setColor(1, 14)
process(xpos, ypos, spriteData)
printMessage("Hello world")        // String literal (passed as pointer)
drawSprite(x, y, @spriteData)      // Label address (passed as pointer)
```

### 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:{BYTE a} in:{BYTE b} out:{BYTE result})
    result = a + b
FEND

FUNC swap(io:{BYTE x} io:{BYTE y})
    BYTE temp
    temp = x
    x = y
    y = temp
FEND
```

### Local Variables

Variables declared inside functions are local:

```c65
FUNC calculate({BYTE value})
    BYTE local = 10      // Only exists inside this function
    WORD temp            // Local temporary
    temp = value + local
FEND
```



### Returning from Functions

```c65
FUNC checkValue({BYTE 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
byte = PEEK pointer                   // Read through a WORD pointer
```

**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

Write a byte to memory:

```c65
POKE $D020, 0                         // Write to absolute address
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

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, irqHandler               // Set IRQ vector
POKEW dataPtr, address                // Write word through a pointer
POKEW dataPtr[2], address             // Write word at dataPtr+2 (zero-page pointer)
```

### 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({BYTE value})
    BYTE local = 10
    ASM
        lda |local|        // Reference local variable
        clc
        adc value          // Function parameter
        sta |local|
    ENDASM
FEND

#### Placing ASM Blocks After Variables

Use the `_P_ASM_AFTER_VARS` pragma to place ASM blocks after all variable storage and constant strings:

```c65
#PRAGMA _P_ASM_AFTER_VARS 1
ASM
sprite_data: !binary "sprite.bin"
    !8 1,2,3,4
ENDASM
#PRAGMA _P_ASM_AFTER_VARS 0
```

This is useful for embedding binary data (graphics, music) with labels that should appear after the variable area.

### 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
```

#### File I/O

Scripts can read binary and text files at compile time using `load_binary()` and `load_text()`. These functions only allow access to files within the project folder (where the main .c65 file resides) and its subdirectories. Absolute paths and path traversal (`..`) are rejected.

**`load_binary(path, offset=0, length=0)`**

Reads a binary file and returns a list of integers (0-255). The optional `offset` parameter skips bytes at the start, and `length` limits how many bytes to read (0 = read to end of file).

```c65
SCRIPT
    sprite = load_binary("assets/hero.spr")
    print("hero_sprite:")
    for i in range(0, len(sprite), 8):
        row = ", ".join(["$%02x" % b for b in sprite[i:i+8]])
        print("  !byte " + row)
ENDSCRIPT
```

**`load_text(path)`**

Reads a text file and returns a list of strings, one per line. Handles both `\n` (Unix) and `\r\n` (Windows) line endings.

```c65
SCRIPT
    level = load_text("levels/lvl1.txt")
    print("level_map:")
    for y in range(len(level)):
        print("  !text " + repr(level[y]))
ENDSCRIPT
```

**Example — resource loading with SCRIPT LIBRARY:**

```c65
SCRIPT LIBRARY
    # Load resources into library globals
    font_data = load_binary("assets/font.chr")
    level = load_text("levels/lvl1.txt")

    def emit_font():
        print("font:")
        for i in range(0, len(font_data), 8):
            row = ", ".join(["$%02x" % b for b in font_data[i:i+8]])
            print("  !byte " + row)

    def emit_level():
        print("level:")
        for line in level:
            print("  !text " + repr(line))
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

### 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 StarLark (Python-like)  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:

- `_P_USE_LONG_JUMP`: Use JMP instead of branches for large switch statements
- `_P_USE_IMMUTABLE_CODE`: Disable self-modifying code (for ROM)
- `_P_USE_CBM_STRINGS`: Use PETSCII encoding for strings
- `_P_IGNORE_UNUSED`: Suppress warnings for unused variables
- `_P_REMOVE_UNUSED`: Remove unused functions from assembly output (requires explicit pragma on each function)
- `_P_ASM_AFTER_VARS`: Place ASM blocks after all variable storage and constant strings

```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
#PRAGMA _P_IGNORE_UNUSED 1          // Suppress unused variable warnings
#PRAGMA _P_IGNORE_UNUSED 0          // Enable unused variable warnings
#PRAGMA _P_REMOVE_UNUSED 1          // Remove function if unused
#PRAGMA _P_REMOVE_UNUSED 0          // Keep function even if unused (default)
#PRAGMA _P_ASM_AFTER_VARS 1         // Place ASM blocks after variables
#PRAGMA _P_ASM_AFTER_VARS 0         // Normal ASM block placement (default)
```

### Debug Directives

```c65
#PRINT Compiling main module       // Print during compilation
#HALT                               // Stop compilation
```

---

## Writing Libraries

When creating a library file to be included by other programs, use this structure:

```c65
#IFNDEF __MY_LIBRARY
#DEFINE __MY_LIBRARY = 1

GOTO lib_mylib_skip        // Jump over library code

// Library variables
WORD lib_mylib_buffer

// Library functions
FUNC lib_mylib_initialize
    lib_mylib_buffer = $C000
FEND

FUNC lib_mylib_process({BYTE value})
    // Do something
FEND

// Skip label - execution continues here after GOTO
LABEL lib_mylib_skip

#IFEND
```

**Key points for libraries:**

1. **Include guard** - Use `#IFNDEF` to prevent multiple inclusion
2. **GOTO skip** - Jump over all library code immediately
3. **LABEL skip** - Place at the end so GOTO jumps past everything
4. **Naming convention** - Prefix all names with `lib_yourlib_` to avoid conflicts

This ensures when someone does `#INCLUDE <mylib.c65>`, the library functions are defined but not executed.

---

## Common Patterns

### Complete Working Example

Here's a complete C64 program showing all the pieces together:

```c65
#INCLUDE <c64start.c65>
#INCLUDE <c64defs.c65>

GOTO start

// Variables
BYTE frameCount = 0
WORD screenPtr @ $FB

// Functions
FUNC initialize
    BYTE borderColor @ $D020
    borderColor = color_black
    POINTER screenPtr TO $0400
FEND

FUNC updateScreen
    BYTE color
    color = frameCount & $0F
    POKE screenPtr, color
    frameCount++
FEND

// Entry point
LABEL start
initialize()

WHILE 1
    updateScreen()
WEND
```

### 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, 32           // Space character
        screenPtr++
        remaining--
    WEND
FEND
```

### String Handling

```c65
// Print null-terminated string
FUNC printString({WORD textPtr})
    BYTE char
    char = PEEK textPtr

    WHILE char != 0
        ASM
            lda |char|
            jsr $FFD2    // CHROUT
        ENDASM
        textPtr++
        char = PEEK textPtr
    WEND
FEND
```

### Sprite Manipulation

```c65
WORD CONST VIC2 = $D000
BYTE spriteX @ VIC2+0
BYTE spriteY @ VIC2+1
BYTE spriteEnable @ VIC2+21

FUNC enableSprite({BYTE spriteNum})
    BYTE mask
    BYTE i
    mask = 1

    FOR i = 0 TO spriteNum
        mask = mask << 1
    NEXT

    spriteEnable = spriteEnable | mask
FEND
```

### Zero Page Optimization

For frequently accessed pointers, use zero page:

```c65
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
        // Process value
        fastPtr++
        size--
    WEND
FEND
```

### Interrupt Handlers

The C64 kernal calls the IRQ vector at `$0314` on every interrupt. Because
the kernal saves the CPU registers *before* calling the vector, your handler
does **not** need to push/pull A/X/Y itself. When finished, chain into the
kernal so it restores the registers and returns from the interrupt:

- `jmp $ea31` — let the kernal do its full IRQ work (scan keyboard, blink
  cursor, update the jiffy clock, ...) and then `RTI`
- `jmp $ea81` — skip that work; just restore the saved registers and `RTI`

Once the vector is installed it keeps firing in the background, so the main
program can simply return to BASIC with `SUBEND` — the handler stays live.

```c65
WORD CONST IRQ_VECTOR = $0314
WORD handler = @myIRQ          // Address of our IRQ handler

FUNC installIRQ
    ASM
        sei                    // Disable interrupts while we patch
    ENDASM

    POKEW IRQ_VECTOR, handler  // Point the vector at our handler

    ASM
        cli                    // Re-enable interrupts
    ENDASM
FEND

LABEL start
    installIRQ()
    SUBEND                     // Return to BASIC; the IRQ stays installed

// No register saving needed — the kernal already did it.
LABEL myIRQ
    ASM
        inc $0400              // Do the IRQ work
        jmp $ea31              // Let the kernal finish the IRQ
    ENDASM
```

See `examples/irq_demo/` for a complete, buildable version.


### 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({BYTE 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
```

---

## Best Practices

### 1. Use Constants for Magic Numbers

```c65
// Bad
POKE $D020, 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
// Constant expressions fold left-to-right at compile time.
// Write them without spaces:
result = 2+3*4         // = (2+3)*4 = 20, not 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

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({WORD velocity} {BYTE 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:{BYTE param1} out:{BYTE result})
    result = param1 + 1
FEND

// Memory
value = PEEK $D020
POKE $D020, 5
address = PEEKW $FFFC
POKEW $0314, handler

// Operators
+ -          // Arithmetic (runtime + constants)
* /          // Multiply/Divide (constant expressions only)
& | ^ << >>  // Bitwise / shift
++ --        // Increment/Decrement
== != < > <= >=  // Comparison

// Blocks
ASM
    // assembly code
ENDASM

SCRIPT
    # 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
```
