Appendix B - The 8x8 Matrix Profile
Everything platform tec1g-mon3 with display matrix8x8 contributes
to a generated program: the scan-shaped loop, the framebuffer, the
colour and key equates, the library routines with their register
contracts, and the 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 at strict
strength.
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 MainLoop
ScanFramelights all eight rows with a fixed dwell, services sound and the HUD once per row, and returns with the matrix blank. Every phase after it runs in the blank window. 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.
MxMaskconverts an x coordinate to this convention. - 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. Whatever renders leave there is the picture.
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 |
ScanFramescans the whole matrix, then blanks it. The loop calls it at frame start; blocks leave it alone.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 keep the whole shape 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 SndStart
call Snd_Click from any block starts the cue without blocking.
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", 0
and 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
end
lcd_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 %11000000
ShapeDraw 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: index an entry 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.- Your own code walks these tables; Chapter 15 reads the Tetro engine that does.