# 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
BYTE speed = 5               // Initialized to 5
BYTE screen @ $D020          // Memory-mapped to specific address
BYTE CONST MAX_SPEED = 10    // Constant (recommended over #DEFINE)
```

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

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

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

For complex expressions, 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:

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

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

### BREAK

Exit a loop early:

```c65
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(color, 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:a, in:b, out:result)
    result = a + b
FEND

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

### Local Variables

Variables declared inside functions are local:

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

### Curly Brace Syntax

Alternative parameter syntax using curly braces:

```c65
FUNC process({BYTE value} out:{BYTE result})
    result = value + 1
FEND
```

### Returning from Functions

```c65
FUNC checkValue(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
char = PEEK screenPtr[index]          // Read with offset
byte = PEEK pointer                   // Read from pointer
```

**Important:** For indexed access, the address must be a WORD variable in zero page.

```c65
WORD buffer @ $FB                     // Zero-page pointer
value = PEEK buffer[10]               // Read buffer+10
```

### POKE - Writing Memory

Write a byte to memory:

```c65
POKE $D020 WITH 0                     // Write to absolute address
POKE screenPtr[index] WITH char       // Write with offset
POKE pointer WITH value               // Write to pointer
```

### 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 WITH irqHandler           // Set IRQ vector
POKEW dataPtr[0] WITH address         // Write word with offset
```

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

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

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

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

### 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(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[0] WITH 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[0] WITH 32    // Space character
        screenPtr++
        remaining--
    WEND
FEND
```

### String Handling

```c65
// Print null-terminated string
FUNC printString(textPtr)
    BYTE char
    char = PEEK textPtr[0]

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

### Sprite Manipulation

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

FUNC enableSprite(spriteNum)
    BYTE mask
    mask = 1

    FOR i = 0 TO spriteNum
        mask = mask * 2
    NEXT

    spriteEnable = spriteEnable | mask
FEND
```

### Zero Page Optimization

For frequently accessed pointers, use zero page:

```c65
WORD fastPtr @ $FB        // Zero page = fast indexed access

FUNC processBuffer(buffer, size)
    POINTER fastPtr TO buffer

    WHILE size > 0
        BYTE value
        value = PEEK fastPtr[0]
        // Process value
        fastPtr++
        size--
    WEND
FEND
```

### Interrupt Handlers

```c65
WORD CONST IRQ_VECTOR = $0314
WORD oldIRQ

FUNC installIRQ
    ASM
        sei                    // Disable interrupts
    ENDASM

    oldIRQ = PEEKW IRQ_VECTOR
    POKEW IRQ_VECTOR WITH myIRQ

    ASM
        cli                    // Enable interrupts
    ENDASM
FEND

LABEL myIRQ
    // IRQ handler code
    ASM
        // Save registers
        pha
        txa
        pha
        tya
        pha

        // Do IRQ work
        inc $d020

        // Restore and return
        pla
        tay
        pla
        tax
        pla
        rti
    ENDASM
```

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

// Test bit
IF flags & %00000001
    // Bit 0 is set
ENDIF
```

---

## Best Practices

### 1. Use Constants for Magic Numbers

```c65
// Bad
POKE $D020 WITH 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
// Be careful with expressions
result = 2 + 3 * 4      // = 20, not 14

// Use temps for clarity
temp = 3 * 4
result = 2 + temp       // Now = 14
```

### 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(velocity, 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:param1, out:result)
    result = param1 + 1
FEND

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

// Operators
+ - * /      // Arithmetic
& | ^        // Bitwise
++ --        // 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
```
