Appendix E - AZM Touchpoints

Every Glimmer build writes one AZM assembly file, and AZM assembles and checks it. Reading that file, and writing modules of your own, goes faster once you know the assembler features Glimmer leans on. Each entry below says what the feature is, where Glimmer relies on it, shows one excerpt from a real build, and points into ← Appendix D | Book chapter of its own. The excerpts come from generated files of this book’s programs: Canvas (chapter 10), Tetro (chapter 15), Sprite Chase (chapter 17).

Labels and local labels

A label names an address. A label beginning with one underscore is local: it belongs to the nearest non-local label above it, its owner, and AZM tracks it as Owner._name, so the same spelling can recur under different owners.

Every block body is compiled under a generated label, Glim_<BlockName>:, and that label owns the block’s locals. Two of Canvas’s movement blocks, side by side in canvas.main.asm:

; --- logic block MoveUp ---
.routine
Glim_MoveUp:
    ld a,(Cursor + offset(Point, y))
    or a
    jr z,_stop      ; at the top edge: stay
    dec a
    ld (Cursor + offset(Point, y)),a
_stop:
        ...

; --- logic block MoveDown ---
.routine
Glim_MoveDown:
    ld a,(Cursor + offset(Point, y))
    cp 7
    jr nc,_stop     ; at the bottom edge: stay

The first _stop is Glim_MoveUp._stop and the second Glim_MoveDown._stop: distinct symbols, one spelling, and Debug80’s symbol table shows the qualified names. Every block in the book names its own _stop or _done on this rule. Full treatment: Source Syntax and Symbols.

@ exports in imported modules

Inside an imported source unit, a plain label stays private to that unit; writing @ in front of the declaration exports it. The @ marks the declaration alone: the symbol’s name is the bare identifier, and every reference writes it without the prefix.

Hand-written modules brought in with import shape their whole API this way. Tetro’s collision engine, tetro-lib.asm, exports eight routines and keeps its tables and scratch to itself:

.routine clobbers A,C,DE,HL,carry,zero,sign,parity,halfCarry
@SetCurPiece:

Block bodies in tetro.glim reach it by its bare name, call SetCurPiece. The module’s private labels (ClearScoreTbl, CurPiecePtr) carry no @ and stay the module’s own. Full treatment: Source Syntax and Symbols and Ops, Aliases and Source Composition.

.routine register contracts

.routine on the line above a label declares that label a routine and a boundary for register-contract analysis. The clauses on the same line - in, out, clobbers, preserves - are the contract, and a register the line leaves unmentioned counts as preserved.

A bare .routine asks AZM to infer the contract from the body, and Glimmer emits exactly that above every generated block: your bodies are analysed as written, with no annotations to maintain. The profile library declares its contracts in full:

; Set one pixel. B = x (0-7), C = y (0-7), A = colour bits
; (COLOR_RED/GREEN/BLUE, OR-combined). ORs into the framebuffer.
.routine in A,B,C clobbers A,B,DE,HL,carry,zero,sign,parity,halfCarry
FbPlot:

The checker proves the contract from both sides. Declared clauses are checked against the routine’s own body, so a preserves promise the body breaks fails the build; and every call site is checked against the callee’s contract, so a caller reusing a clobbered register fails too. Add an inc b after Canvas’s call FbPlot to plot a second pixel one column over, and the build stops on the call:

canvas.glim:112:5: [AZMN_REGISTER_CONTRACTS] error: CALL FbPlot may modify B, but the pre-call value is used later.

B sits on FbPlot’s clobbers line, so the value inc b consumes may be anything; the diagnostic carries the call, the register and the reason, at the .glim line and column you typed. Chapter 11 walks this bug and its fix. Full treatment: Register Contracts.

.contracts strict

.contracts is a file-level policy line selecting the checking strength - strict, audit or off - for the whole physical file, one directive per file.

Glimmer writes the strictest setting near the top of every generated file, from tetro.main.asm:

; Register contracts are declared with .routine and checked at
; strict strength over this whole generated file.
        .contracts strict

Under strict, every call and executable tail jump must land on a declared routine, so the entire program - runtime, library, imported modules and your bodies - passes through the checker on every build. Full treatment: Register Contracts.

Layout types

.type opens a record: named fields, each with a size, closed by .endtype. From the record, sizeof(Name) and offset(Type, field) are constants the assembler computes, so field access is written by name and survives the layout growing.

A type declaration in a .glim file compiles to the record, and a typed state cell reserves storage sized by it. Canvas’s Point:

; --- layout types ---
; AZM owns the type system: sizeof, offset, and layout casts
; work on these names in block bodies.
Point .type
    x             .byte
    y             .byte
.endtype

; --- state storage ---
Cursor:           .ds Point, 0   ; typed state

Block bodies apply the functions directly, and each line passes through to the generated file verbatim:

    ld a,(Cursor + offset(Point, y))

Full treatment: The Layout System and Built-in Functions.

.import source units

.import loads another source file as a module: its bytes are emitted at the import point, its @ declarations become visible to the importer, and its plain declarations stay inside. This is the boundary that gives @ its meaning.

A Glimmer import "file.asm" line becomes exactly this directive, placed after the runtime, from tetro.main.asm:

; --- imported AZM modules ---
; Import names resolve program-wide; bytes land here, outside
; every execution path. @ labels are the modules' public API.
        .import "tetro-lib.asm"

Chapter 12 teaches when a program earns a module; the directive above is what that decision compiles to. Full treatment: Ops, Aliases and Source Composition.

op definitions

An op is an assembler-owned macro: a named instruction sequence with typed parameters, expanded inline wherever its name appears in code. Contract findings inside an expansion attach to the line that invoked the op, so diagnostics land where you can act.

The profiles generate ops for the moves render bodies repeat: sprite_at and tile_at on the TMS9918 display, lcd_row wherever a program declares text resources. From sprite-chase.main.asm:

op sprite_at(slot imm8, xcell imm16, ycell imm16)
        ld      a,(xcell)
        ld      d,a
        ld      a,(ycell)
        ld      e,a
        ld      a,slot
        call    SpriteSet
end

and a render body invoking it, one line where the shadow-table dance would otherwise be six:

; --- render block PlacePlayer ---
.routine
Glim_PlacePlayer:
    sprite_at Player, PlayerX, PlayerY
        ret

At assembly, that line becomes the op’s body with Player (the slot equate), PlayerX and PlayerY (cell addresses) substituted. tile_at and lcd_row expand the same way: Tetro’s pause screen is one line, lcd_row MsgPause, LcdRow1. Full treatment: Ops, Aliases and Source Composition.

.equ and .enum

.equ binds a name to a constant expression; .enum groups related constants under one name, numbering the members from 0, each referenced as Group.Member. Between them they name nearly every number in a generated file.

The file opens with .equ blocks for the platform: key codes, ports, colours, LCD rows. From tetro.main.asm:

; --- MON-3 key codes ---
KEY_4             .equ $04
KEY_6             .equ $06
KEY_2             .equ $02

Each cell gets a change-flag mask the same way, the CHG_ names your updates lines compile into, from canvas.main.asm:

CHG_CURSOR        .equ %00000001
CHG_PICTURE       .equ %00000010

And a program’s card declarations become one enum, from tetro.main.asm:

; --- cards ---
; Exactly one card is active; CurrentCard holds it. Blocks in a
; card's section dispatch only while it is active.
Card              .enum Splash, Playing, Paused, GameOver

Card.Splash is 0, Card.Playing is 1, and the card gates from chapter 13 compare against exactly these symbols: cp Card.Playing. Full treatment: Addresses, Constants and Expressions.