Appendix A - Declaration Reference
Every Glimmer declaration in one place. Each entry gives the grammar
production from the Glim grammar reference, then one example from a
program built with glimmer build, then the constraints the compiler
enforces.
Rules that apply everywhere:
- Every statement starts with a keyword;
;starts a comment, in.glimand AZM alike. - Three symbols, one meaning each:
:reads “is a”,=reads “starting at”,->reads “fires”. - All declared names share one namespace and must be unique. The
Glim,Snd_,Curve_,Shape_,CHG_, and__prefixes and the runtime and profile names are reserved. - Flag-carrying cells are allocated by category order - states, then
pulses, then ramps, then
FrameCount- into up to four change-flag banks; the current cap is 32 cells. - Two built-in cells need no declaration:
FrameCountincrements every frame and is legal inon;CurrentCardarrives with the first card.
identifier ::= [A-Za-z_][A-Za-z0-9_]*
number ::= decimal | "$" hex | "0x" hex | "%" binary
Program shape
program
program-decl ::= "program" identifier
program RefMatrix
- Required, exactly once per program; only the entry file declares it.
platform
platform-decl ::= "platform" platform-name ; "tec1g-mon3"
platform tec1g-mon3
- At most once, and only together with
display. - Omitting both selects the generic profile: placeholder API addresses, for tests and for reading the generated structure.
display
display-decl ::= "display" display-name ; "matrix8x8" | "tms9918"
display matrix8x8
- At most once, and only together with
platform. matrix8x8generates the scan-driven matrix runtime;tms9918the vblank-paced VDP runtime with a commit phase.- The display gates its resources: shapes and sounds belong to
matrix8x8, sprites and tiles totms9918.
Facts and moments
state
state-decl ::= "state" identifier ":" cell-type
( "=" number )? ( "changed" )?
| "state" identifier ":" array-type ( "changed" )?
| "state" identifier ":" type-expr ( "changed" )?
cell-type ::= "byte" | "word"
array-type ::= "byte" "[" number "]"
state Count : byte = 0 changed
state Score : word = 0
state Board : byte[8] changed
state Cursor : Point changed
- Scalars are
byteorword; the initial value defaults to 0. changedmarks the cell changed at startup, so dependents run on the first frame.- Arrays and typed cells: one flag for the whole cell, zero-filled storage, no initializer; indexing is ordinary Z80 in block bodies.
byte[N]takes N from 1 to 256; word arrays are unimplemented.
type
type-decl ::= "type" identifier newline
type-field*
"end"
| "type" identifier "=" type-expr ; alias (.typealias)
type-field ::= identifier ":" field-type
field-type ::= "byte" | "word" | "addr"
| number ; byte count
| type-expr
type-expr ::= identifier ( "[" number "]" )?
type Point
x : byte
y : byte
end
type Pair = Point[2]
- Field types:
byte,word,addr, a raw byte count, or another layout, including arrays of layouts; recursion is a parse error. - The block form compiles to an AZM
.typerecord, the alias form to.typealias;sizeof,offset, and layout casts work on the name inside block bodies.
pulse
pulse-decl ::= "pulse" identifier
pulse Step
- A one-frame transient cell, raised by bindings, timers, ramps, or a
block’s
updates; it clears at the end of every frame. - A raise whose consumers are all in later phases lands the same frame; any other raise rolls over whole to the next frame.
bind
bind-decl ::= "bind" "key" key-name trigger "->" identifier
trigger ::= "rising"
| "held" "period" number ; tec1g-mon3 only
key-name ::= identifier | "any" ; validated per platform;
bind key KEY_2 rising -> Step
bind key KEY_4 held period 8 -> Fire
bind key any rising -> AnyKey
- The target must be a declared pulse.
risingfires on the frame the key is first pressed;heldadds an autorepeat everyperiodframes while the key stays down.anyfires on every new press, rising only, tec1g only, alongside any named binding the same press matches.- Key names are validated per platform; Appendix B lists the MON-3 set.
timer
timer-decl ::= "timer" identifier ":" cell-type "=" number
"->" identifier ( "once" )?
timer Blink : byte = 12 -> Tick
timer Gate : word = 384 -> Opened once
- The target must be a declared pulse.
- The cell is the writable period; a hidden countdown fires the pulse and reloads from the cell each time it runs out.
once: the cell is the countdown itself; it fires a single time at zero and stays idle until code writes it again.- Timer cells carry no change flag - trigger on the pulse - so they
are legal in
updatesand absent fromon.
ramp
ramp-decl ::= "ramp" identifier ":" "byte" "steps" number
"->" identifier
ramp Travel : byte steps 64 -> Arrived
- The target must be a declared pulse.
- The cell steps each frame toward
steps - 1, marked changed at every step, fires the pulse on arrival, and idles at the terminal value; writing the cell (usually to 0) starts it moving again. - The cell is legal in both
onandupdates.
Blocks
block-decl ::= block-kind identifier
block-header*
( "begin" newline azm-line* )? ; body optional with goto
"end"
block-kind ::= "compute" | "effect" | "render" | "enter"
block-header ::= "on" name-list ; not on enter
| "updates" name-list ; not on render
| "goto" identifier ; card transition after
name-list ::= identifier ( "," identifier )*
Constraints shared by every block kind:
- Every block needs at least one
ontrigger;enteralone omits it, because entry is its trigger. onnames must be flag-carrying cells,updatesnames writable runtime cells; an array or typed cell is one flag.- Bodies are verbatim AZM and fall through: the generated wrapper
appends the flag bookkeeping and the
ret.endterminates a body when it is the only word on the line. _namelabels are local to the block; a plain label is a file-level symbol. A direct store into a flag-carrying cell missing fromupdatesdraws a compiler warning.
compute
compute DeriveScore
on Count
updates Score
begin
ld a,(Count)
ld l,a
ld h,0
ld (Score),hl
end
- Computes run first in the frame, before effects and renders.
updatesis required: computing state is the block’s purpose.
effect
effect Advance
on Step
updates Count
begin
ld hl,Count
inc (hl)
end
- Effects run second: the phase for game rules.
updatesis optional: an effect may act entirely through calls, such as starting a sound cue.
render
render ShowScore
on Score
begin
ld hl,(Score)
call HudWriteU16
end
- Renders run last in the frame and draw state to the display.
updatesandgotoare rejected on a render: it depicts state.
enter
enter SetupPlaying
updates Count
begin
xor a
ld (Count),a
end
- Legal only inside a card section; runs once on entry to that card,
before the card’s other blocks, with no
online. - Entry is edge-triggered: marking
CurrentCardchanged without switching cards cannot re-run it. - An enter block’s
updatesre-raises the cells a card’s renders need, because a card-gated block sees only flags raised while its card is active.
goto
effect StartGame
on AnyKey
goto Playing
end
gotonames a declared card and switches to it after the block runs; withgoto,beginis optional, so a header-only routing block closes directly withend.- The switch lands at the next frame start; the destination’s
enterblocks run first on the frame it activates. gotois the unconditional form; a transition that depends on a runtime test is a conditional store of aCard.<Name>value intoCurrentCard, underupdates CurrentCard.
routine
routine-decl ::= "routine" identifier newline
"begin" newline
z80-body
"end"
routine ClampX
begin
cp 8
ret c
ld a,7
end
- No triggers, no dispatch: blocks call it with an ordinary
call ClampX. - Emitted as a
.routineboundary followed byClampX:, with its register contract inferred by AZM. - The body falls through and the generator appends the final
ret; conditional early returns are fine.
Resources
curve
curve-decl ::= "curve" identifier preset "steps" number
( "from" number "to" number )?
preset ::= identifier
curve SlideX ease_out steps 64 from 0 to 7
- Computed at build time; emitted as a page-aligned byte table named
Curve_<Name>. - Presets:
linear,ease_in,ease_out,ease_in_out,sine,overshoot,anticipation. stepsruns from 2 to 256;fromandtoare byte values and default to0andsteps - 1.
shape
shape-decl ::= "shape" identifier "color" color-name
( shape-row+ | rot-group+ )
"end"
rot-group ::= "rot" digit shape-row* newline shape-row*
| "rot" digit "=" "rot" digit ; alias of an earlier
shape-row ::= string
color-name ::= "red" | "green" | "blue" | "yellow" | "cyan"
| "magenta" | "white"
shape Dot color green
"XX"
"XX"
end
shape PieceS color green
rot0 "XX."
".XX"
rot1 ".X"
"XX"
"X."
rot2 "..."
"XX."
".XX"
rot3 = rot1
end
- Requires
platform tec1g-mon3withdisplay matrix8x8; rows are rectangular quoted strings, 1 to 8 wide by 1 to 8 high,Xlit and.empty. - The plain form emits a
Shape_<Name>table drawn withShapeDraw(HL = table, B = x, C = y): an OR into the framebuffer, no clipping. - Rotation groups:
rot0..rot3declared in order, 1 to 4 rows each, padded to a 4-row frame;rotN = rotMaliases an earlier distinct rotation, and rotations beyond those declared cycle. - The rotational form compiles to
ShapeRot_<Name>_<k>bitmaps plus the sharedShapeRotPtrTable,ShapeRotRightTbl, andShapeRotColorTbl, with aShapeId_<Name>equate per shape; index an entry byid*4 + rotation.
sprite
sprite-decl ::= "sprite" identifier "color" vdp-color
shape-row+ ; exactly 8 rows of 8
"end"
sprite Player color white
"..XXXX.."
".XXXXXX."
"XX.XX.XX"
"XXXXXXXX"
"XX....XX"
".XXXXXX."
"..XXXX.."
"........"
end
- tms9918 profile only; exactly 8 rows of 8.
- Declaration order is the sprite slot and pattern number; the name
compiles to the slot equate, so the generated op takes it directly:
sprite_at Player, PlayerX, PlayerY. - Patterns, colours, and slot setup upload once through the generated
LoadResourcesVram. At most 31 sprites; slot 31 stays hidden.
tile
tile-decl ::= "tile" identifier "color" vdp-color "on" vdp-color
shape-row+ ; exactly 8 rows of 8
"end"
vdp-color ::= "transparent" | "black" | "medgreen" | "lightgreen"
| "darkblue" | "lightblue" | "darkred" | "cyan"
| "medred" | "lightred" | "darkyellow" | "lightyellow"
| "darkgreen" | "magenta" | "gray" | "white"
tile Pip color white on black
"........"
"..XXXX.."
".XXXXXX."
".XXXXXX."
".XXXXXX."
".XXXXXX."
"..XXXX.."
"........"
end
- tms9918 profile only; exactly 8 rows of 8.
- The name compiles to the tile index equate:
tile_at Pip, 4, 5places it at fixed coordinates, andNamePut(tile A at column D, row E) handles computed positions. - Graphics I colours patterns in groups of eight, so tiles group by their (fg, bg) pair; the first pair’s background is the screen background for empty cells.
sound
sound-decl ::= "sound" identifier "len" number "div" number
sound Click len 2 div 10
- Requires
platform tec1g-mon3withdisplay matrix8x8;lenanddivare byte values from 1 to 255. lenis measured in row ticks of the matrix scan (8 ticks is about one frame);divis the speaker divider, smaller values higher pitch.- Each cue generates a callable routine:
call Snd_Clickstarts it, non-blocking. One cue at a time; a new cue replaces the current one.
text
text-decl ::= "text" identifier string
text MsgPaused "PAUSED"
- tec1g platform with either display: the LCD is board hardware.
- The string is zero-terminated; the generated
lcd_rowop positions the cursor and writes it:lcd_row MsgPaused, LcdRow1in a body. LcdRow1..LcdRow4and the MON-3 LCD call equates are emitted alongside.
Structure
card
card-decl ::= "card" identifier
card Playing
- Starts a section: blocks after it belong to that card until the next
cardline or end of file, with no closing keyword; declarations before the first card are global. - The first declared card is the start card.
- Cards generate a
Cardenum and the built-inCurrentCardcell, legal inonandupdates; a card’s blocks run only while it is active.
part
part-decl ::= "part" string
part "ref-part.glim"
- Entry file only. The semantics are merge: the named file’s declarations join the same program and namespace, so the compilation unit is the project and files are storage.
- Paths resolve relative to the entry file; diagnostics name the file they come from.
- Parts may not declare
program,platform,display, or parts.
import
import-decl ::= "import" string
import "double.asm"
with the module’s exported routine written in ordinary AZM:
.routine in HL out HL clobbers carry,zero,sign,parity,halfCarry
@Double:
add hl,hl
ret
@-exported names become callable from any block (references omit the@); plain labels stay private to the module.- Give each callable a
.routinecontract line; the generated program checks register contracts strictly, and a call into a routine with no declared or inferable contract fails to assemble. - Glimmer places the
.importin a dedicated section outside every execution path, because import bytes land at the directive.