Appendix B — The 8x8 Matrix Profile
The combination of platform tec1g-mon3 and display matrix8x8 contributes the scan-shaped loop, framebuffer, colour and key equates, library routines, sound, HUD and LCD services. Every listing and value here is copied from a program built with glimmer build; the register interfaces are the .routine lines the assembler checks under strict contract checking.
The loop
; --- runtime loop ---
Start:
call FbClear
call HudBlankDig
MainLoop:
call ScanFrame ; show one full frame, then blank
call GlimPollBindings ; game work runs in the blank window
call GlimRunLogicEffects
call GlimMergeRaised
call GlimRunRenderEffects
call GlimEndFrame
jp MainLoopScanFramelights all eight rows with a fixed dwell, services sound and the HUD once per row, and returns with the matrix blank. Brief block work leaves brightness steady; heavy blank-window work lowers the sweep rate and with it average brightness; treat the gap as the frame's budget.GlimPollBindingsreads the keypad through MON-3_scanKeysand raises bound pulses; the phase dispatchers then test change flags and call your blocks.
Platform equates
| Equate | Value | Meaning |
|---|---|---|
ApiScanKeys | 16 | MON-3 keypad call number for rst $10 |
ApiRandom | 49 | A = random byte, destroys B |
PortDigits | $01 | HUD digit select; the speaker shares this port |
PortSegs | $02 | HUD segment data |
PortRow | $05 | matrix row select, one bit per row |
PortRed | $06 | red plane column data |
PortGreen | $F8 | green plane column data |
PortBlue | $F9 | blue plane column data |
SpeakerBit | $80 | speaker bit within PortDigits |
ScanDwellPeriod | 255 | djnz count per lit row |
Framebuffer
Framebuffer: .ds 32 ; 8 rows x R,G,B,aux| Offset | Byte |
|---|---|
y*4 + 0 | red plane, one bit per column |
y*4 + 1 | green plane |
y*4 + 2 | blue plane |
y*4 + 3 | aux, skipped by the scanner |
- Bit 7 is column 0, the leftmost; bit 0 is column 7.
- The aux byte pads each row to four bytes, so a row's address is
Framebuffer + y * 4: twoadd a,ainstructions. - The scanner reads the framebuffer every frame.
Colours
| Name | Value | Planes |
|---|---|---|
COLOR_RED | $01 | red |
COLOR_GREEN | $02 | green |
COLOR_BLUE | $04 | blue |
COLOR_YELLOW | $03 | red + green |
COLOR_CYAN | $06 | green + blue |
COLOR_MAGENTA | $05 | red + blue |
COLOR_WHITE | $07 | all three |
A colour is a set of planes; the generated file defines the composites as sums (COLOR_YELLOW .equ COLOR_RED + COLOR_GREEN). FbPlot ORs the pixel into each plane whose bit is set, so overlapping plots mix: red over green reads back yellow.
MON-3 keys
| Key | Code |
|---|---|
KEY_0 .. KEY_F | $00 .. $0F |
KEY_PLUS | $10 |
KEY_MINUS | $11 |
KEY_GO | $12 |
KEY_AD | $13 |
risingfires on the frame the key is first pressed;held period Nfires on the first press, then every N frames while the key stays down;anyfires on every new press, alongside any named binding the same press matches.- The generated file emits a key-code equate for each key a binding uses. Held bindings add two scratch cells,
Glim_HeldKey($FFwhen disarmed) andGlim_HeldCount. _scanKeysreturns Z when a key is down with its code in A, and carry when the press is new. The generated poller keeps the code in B across compares; its comment records that DE is unsafe across the call on a matrix keyboard.
Library routines
Each entry's .routine contract is copied from the generated file. ShapeDraw and its scratch cells appear when the program declares a plain shape; Snd_<Name> wrappers appear per sound cue.
| Routine | Contract |
|---|---|
ScanFrame | clobbers A,BC,DE,HL,carry,zero,sign,parity,halfCarry |
FbClear | clobbers A,B,HL,carry,zero,sign,parity,halfCarry |
FbPlot | in A,B,C clobbers A,B,DE,HL,carry,zero,sign,parity,halfCarry |
MxMask | in A out A clobbers B,carry,zero,sign,parity,halfCarry |
ShapeDraw | in B,C,HL clobbers A,BC,DE,HL,carry,zero,sign,parity,halfCarry |
SndStart | in A,C clobbers A,carry,zero,sign,parity,halfCarry |
Snd_<Name> | bare .routine; contract inferred from the body |
HudWriteU16 | in HL out BC,HL clobbers A,DE,carry,zero,sign,parity,halfCarry |
HudBlankDig | clobbers A,B,HL,carry,zero,sign,parity,halfCarry |
- The generated loop calls
ScanFrameat frame start. FbClearzeroes all 32 framebuffer bytes.FbPlotsets one pixel: B = x (0-7), C = y (0-7), A = colour bits, OR-combined. It ORs into the framebuffer; C survives the call.MxMaskconverts x in A (0 = leftmost) to the matrix bit convention,%10000000for column 0.ShapeDrawdraws a plain shape: HL =Shape_<Name>, B = x, C = y. It plots with no clipping, so the complete shape must remain inside the 8x8 matrix.SndStart(re)starts a cue: A = duration in row ticks, C = divider half-period.Snd_<Name>loads both from the declaration and jumps into it.HudWriteU16encodes HL as decimal into the HUD digits.HudBlankDigzeroes all six.
Three service routines run inside the scanner and the HUD encoder: SndService and HudScanDig are called once per row by ScanFrame, and HudDecDigit is called five times by HudWriteU16. Their contracts sit in the generated file beside the ones above.
Sound service
| Cell | Purpose |
|---|---|
SpeakerPort: .db 0 | shadow of the speaker bit written to PortDigits |
SoundTimer: .db 0 | remaining row ticks; 0 is silence |
SndDivReload: .db 0 | divider half-period |
SndDivCount: .db 0 | countdown to the next speaker toggle |
- The time unit is the row tick:
ScanFramecallsSndServiceonce per row, 8 ticks per frame, solen 16sounds for about two frames. - One cue is active at a time. Starting a cue replaces the current one.
divis the toggle half-period in row ticks; smaller values are higher pitch. The speaker bit (SpeakerBit,$80) rides onPortDigits, andHudScanDigpreserves it in every digit strobe.
Each sound declaration compiles to a wrapper. sound Click len 2 div 10 generates:
.routine
Snd_Click:
ld a,2
ld c,10
jp SndStartcall Snd_Click from any block starts the cue and returns at once.
HUD service
| Cell | Purpose |
|---|---|
HudScanIndex: .db 0 | next digit to strobe, 0-5 |
HudSegBuffer: .ds 6 | segment bytes, one per digit |
HudMaskTbl | digit select masks $20, $10, $08, $04, $02, $01 |
HudGlyphTbl | sixteen segment glyphs for digits 0-F |
HudScanDigstrobes one digit per row tick fromHudSegBuffer, so all six digits refresh within each frame's scan.HudWriteU16takes the value in HL: slot 0 shows 0, slots 1-5 show the 10000..1 decimal digits.- A block may also store glyph bytes straight into
HudSegBuffer, indexingHudGlyphTblfor the 0-F patterns.
LCD slice
The LCD is board hardware, available with either display. A text declaration brings in the equates, the string data, and the lcd_row op.
| Equate | Value | Meaning |
|---|---|---|
ApiStringToLcd | 13 | HL = string; destroys A,HL |
ApiCharToLcd | 14 | A = character |
ApiCommandToLcd | 15 | B = instruction byte |
LcdRow1 | $80 | cursor command for row 1 |
LcdRow2 | $C0 | cursor command for row 2 |
LcdRow3 | $94 | cursor command for row 3 |
LcdRow4 | $D4 | cursor command for row 4 |
text MsgHello "HELLO" compiles to a zero-terminated string:
MsgHello:
.db "HELLO", 0and the generated op positions the cursor, then writes it:
op lcd_row(msg imm16, row imm8)
ld b,row
ld c,ApiCommandToLcd
rst $10
ld hl,msg
ld c,ApiStringToLcd
rst $10
endlcd_row MsgHello, LcdRow1 in a block body expands the op inline.
Matrix resources
A plain shape compiles to a Shape_<Name> table: width, height, colour, then left-aligned row masks.
Shape_Dot:
.db 2, 2, COLOR_GREEN
.db %11000000
.db %11000000ShapeDraw walks the table and plots each lit bit with FbPlot. The first plain shape also brings nine scratch cells, ShapePtr through ShapeColIndex, that ShapeDraw uses in place of registers.
A rotational shape compiles to the piece-engine tables. This shape PieceS color cyan with rot0, rot1, rot2 = rot0, rot3 = rot1 generates:
ShapeRot_PieceS_0:
.db %11000000
.db %01100000
.db %00000000
.db %00000000
ShapeRot_PieceS_1:
.db %01000000
.db %11000000
.db %10000000
.db %00000000
ShapeRotPtrTable:
.dw ShapeRot_PieceS_0, ShapeRot_PieceS_1, ShapeRot_PieceS_0, ShapeRot_PieceS_1
ShapeRotRightTbl:
.db 2,1,2,1
ShapeRotColorTbl:
.db COLOR_CYAN
ShapeId_PieceS .equ 0
ShapeRotCount .equ 1- Each distinct rotation is a 4-row bitmap, padded with empty rows; aliases repeat pointers in
ShapeRotPtrTableinstead of repeating bitmaps. ShapeRotPtrTableandShapeRotRightTblhold four entries per shape. An entry is indexed byid*4 + rotation, withidfrom theShapeId_<Name>equate.ShapeRotColorTblholds one colour byte per shape, indexed byidalone.ShapeRotRightTblrecords each rotation's rightmost occupied column, the X bound a collision probe checks first.- Block and imported-module code can walk these tables directly; the Tetro engine is one complete example.