Appendix E — AZM Touchpoints
Every Glimmer build writes one AZM assembly file, and AZM assembles and checks it. Each entry below says what the feature is, where Glimmer relies on it, shows one excerpt from a real build, and points into AZM Book 1, where each feature has a dedicated chapter. The excerpts come from generated files of this books' programs: Canvas (Book 1, chapter 10), Tetro (Book 2, chapter 2) and Sprite Chase (Book 2, chapter 4).
Labels and local labels
A label beginning with one underscore is local to the nearest non-local label above it. AZM tracks it as Owner._name, so the same spelling can recur in different scopes.
Every block body is compiled under a generated label, Glim_<BlockName>:, which scopes 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: stayThe 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. 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 define their complete 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) use plain names and stay private to the module. 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, so your bodies are analysed as written. 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:Declared clauses are checked against the routine's body, so a preserves contract 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. Adding an inc b after Canvas's call FbPlot to plot a second pixel one column over makes the build stop on the call:
canvas.glim:116: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. Book 1, 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 strictUnder 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 adjusts when the layout grows.
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 stateBlock 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.
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"Book 1, chapter 12 explains when to use a module; the directive above is the generated form. Full treatment: Ops, Aliases and Source Composition.
op definitions
An op is an assembler 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
endand a render body invoking it, one line where the shadow-table update would otherwise take six:
; --- render block PlacePlayer ---
.routine
Glim_PlacePlayer:
sprite_at Player, PlayerX, PlayerY
retAt 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.
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 $02Each 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 %00000010And 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, GameOverCard.Splash is 0, Card.Playing is 1, and the card gates from Book 1, chapter 13 compares against exactly these symbols: cp Card.Playing. Full treatment: Addresses, Constants and Expressions.