Appendix A — Declaration Reference
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 assembly 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 cells are built in:
FrameCountincrements every frame and is legal inon;CurrentCardstarts with the first card.
text
identifier ::= [A-Za-z_][A-Za-z0-9_]*
number ::= decimal | "$" hex | "0x" hex | "%" binaryProgram shape
program
text
program-decl ::= "program" identifier
program RefMatrix- Required, exactly once per program; only the entry file declares it.
platform
text
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
text
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:
matrix8x8provides shapes and sounds, whiletms9918provides sprites and tiles.
Facts and moments
state
text
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 complete 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
text
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 assembler
.typerecord, the alias form to.typealias;sizeof,offset, and layout casts work on the name inside block bodies.
pulse
text
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 is delivered as a unit on the next frame.
bind
text
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
text
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 expiry raises its pulse, so the cell is legal in
updatesandonlines take the pulse.
ramp
text
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
text
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 assembly 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
text
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
text
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
text
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
text
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
text
effect StartGame
on AnyKey
goto Playing
endgotonames 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
text
routine-decl ::= "routine" identifier newline
"begin" newline
z80-body
"end"
routine ClampX
begin
cp 8
ret c
ld a,7
end- Called directly: blocks reach it with an ordinary
call ClampX. - Emitted as a
.routineboundary followed byClampX:, with its register contract inferred by the assembler. - The body falls through and the generator appends the final
ret; conditional early returns are fine.
Resources
curve
text
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
text
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
text
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
text
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
text
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
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
text
card-decl ::= "card" identifier
card Playing- Starts a section: blocks after it are associated with 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
text
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
text
import-decl ::= "import" string
import "double.asm"with the module's exported routine written in ordinary assembly:
asm
.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.- Each callable needs 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.