Skip to content

Glimmer Reference02

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

asm
; --- 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
  • ScanFrame lights 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.
  • GlimPollBindings reads the keypad through MON-3 _scanKeys and raises bound pulses; the phase dispatchers then test change flags and call your blocks.

Platform equates

EquateValueMeaning
ApiScanKeys16MON-3 keypad call number for rst $10
ApiRandom49A = random byte, destroys B
PortDigits$01HUD digit select; the speaker shares this port
PortSegs$02HUD segment data
PortRow$05matrix row select, one bit per row
PortRed$06red plane column data
PortGreen$F8green plane column data
PortBlue$F9blue plane column data
SpeakerBit$80speaker bit within PortDigits
ScanDwellPeriod255djnz count per lit row

Framebuffer

asm
Framebuffer:      .ds 32           ; 8 rows x R,G,B,aux
OffsetByte
y*4 + 0red plane, one bit per column
y*4 + 1green plane
y*4 + 2blue plane
y*4 + 3aux, 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: two add a,a instructions.
  • The scanner reads the framebuffer every frame.

Colours

NameValuePlanes
COLOR_RED$01red
COLOR_GREEN$02green
COLOR_BLUE$04blue
COLOR_YELLOW$03red + green
COLOR_CYAN$06green + blue
COLOR_MAGENTA$05red + blue
COLOR_WHITE$07all 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

KeyCode
KEY_0 .. KEY_F$00 .. $0F
KEY_PLUS$10
KEY_MINUS$11
KEY_GO$12
KEY_AD$13
  • rising fires on the frame the key is first pressed; held period N fires on the first press, then every N frames while the key stays down; any fires 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 ($FF when disarmed) and Glim_HeldCount.
  • _scanKeys returns 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.

RoutineContract
ScanFrameclobbers A,BC,DE,HL,carry,zero,sign,parity,halfCarry
FbClearclobbers A,B,HL,carry,zero,sign,parity,halfCarry
FbPlotin A,B,C clobbers A,B,DE,HL,carry,zero,sign,parity,halfCarry
MxMaskin A out A clobbers B,carry,zero,sign,parity,halfCarry
ShapeDrawin B,C,HL clobbers A,BC,DE,HL,carry,zero,sign,parity,halfCarry
SndStartin A,C clobbers A,carry,zero,sign,parity,halfCarry
Snd_<Name>bare .routine; contract inferred from the body
HudWriteU16in HL out BC,HL clobbers A,DE,carry,zero,sign,parity,halfCarry
HudBlankDigclobbers A,B,HL,carry,zero,sign,parity,halfCarry
  • The generated loop calls ScanFrame at frame start.
  • FbClear zeroes all 32 framebuffer bytes.
  • FbPlot sets one pixel: B = x (0-7), C = y (0-7), A = colour bits, OR-combined. It ORs into the framebuffer; C survives the call.
  • MxMask converts x in A (0 = leftmost) to the matrix bit convention, %10000000 for column 0.
  • ShapeDraw draws 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.
  • HudWriteU16 encodes HL as decimal into the HUD digits. HudBlankDig zeroes 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

CellPurpose
SpeakerPort: .db 0shadow of the speaker bit written to PortDigits
SoundTimer: .db 0remaining row ticks; 0 is silence
SndDivReload: .db 0divider half-period
SndDivCount: .db 0countdown to the next speaker toggle
  • The time unit is the row tick: ScanFrame calls SndService once per row, 8 ticks per frame, so len 16 sounds for about two frames.
  • One cue is active at a time. Starting a cue replaces the current one.
  • div is the toggle half-period in row ticks; smaller values are higher pitch. The speaker bit (SpeakerBit, $80) rides on PortDigits, and HudScanDig preserves it in every digit strobe.

Each sound declaration compiles to a wrapper. sound Click len 2 div 10 generates:

asm
.routine
Snd_Click:
        ld      a,2
        ld      c,10
        jp      SndStart

call Snd_Click from any block starts the cue and returns at once.

HUD service

CellPurpose
HudScanIndex: .db 0next digit to strobe, 0-5
HudSegBuffer: .ds 6segment bytes, one per digit
HudMaskTbldigit select masks $20, $10, $08, $04, $02, $01
HudGlyphTblsixteen segment glyphs for digits 0-F
  • HudScanDig strobes one digit per row tick from HudSegBuffer, so all six digits refresh within each frame's scan.
  • HudWriteU16 takes 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, indexing HudGlyphTbl for 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.

EquateValueMeaning
ApiStringToLcd13HL = string; destroys A,HL
ApiCharToLcd14A = character
ApiCommandToLcd15B = instruction byte
LcdRow1$80cursor command for row 1
LcdRow2$C0cursor command for row 2
LcdRow3$94cursor command for row 3
LcdRow4$D4cursor command for row 4

text MsgHello "HELLO" compiles to a zero-terminated string:

asm
MsgHello:
        .db     "HELLO", 0

and the generated op positions the cursor, then writes it:

asm
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.

asm
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:

asm
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 ShapeRotPtrTable instead of repeating bitmaps.
  • ShapeRotPtrTable and ShapeRotRightTbl hold four entries per shape. An entry is indexed by id*4 + rotation, with id from the ShapeId_<Name> equate. ShapeRotColorTbl holds one colour byte per shape, indexed by id alone.
  • ShapeRotRightTbl records 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.