Chapter 4 - Pulses and Bindings
A fact persists: the beacon’s position outlives every frame that draws it. A moment passes: the instant GO goes down exists once, and the program must catch it or lose it. Glimmer gives moments their own declaration, the pulse, and this chapter covers the whole input story - every key name, both ways a key can fire, and what the generated polling does with the keypad sixty-odd times a second.
The program is Rover: a white dot steered around the whole matrix with 2, 4, 6, and 8 - the keypad’s compass points - with GO recalling it to the centre.
program Rover
platform tec1g-mon3
display matrix8x8
state DotX : byte = 3 changed
state DotY : byte = 3
pulse Up
pulse Down
pulse Left
pulse Right
pulse Home
bind key KEY_2 held period 8 -> Up
bind key KEY_8 held period 8 -> Down
bind key KEY_4 held period 8 -> Left
bind key KEY_6 held period 8 -> Right
bind key KEY_GO rising -> Home
effect MoveUp
on Up
updates DotY
begin
ld a,(DotY)
or a
jr z,_stop ; at the top: stay
dec a
ld (DotY),a
_stop:
end
effect MoveDown
on Down
updates DotY
begin
ld a,(DotY)
cp 7
jr nc,_stop ; at the bottom: stay
inc a
ld (DotY),a
_stop:
end
effect MoveLeft
on Left
updates DotX
begin
ld a,(DotX)
or a
jr z,_stop ; at the left edge: stay
dec a
ld (DotX),a
_stop:
end
effect MoveRight
on Right
updates DotX
begin
ld a,(DotX)
cp 7
jr nc,_stop ; at the right edge: stay
inc a
ld (DotX),a
_stop:
end
effect GoHome
on Home
updates DotX, DotY
begin
ld a,3
ld (DotX),a
ld (DotY),a
end
render DrawDot
on DotX, DotY
begin
call FbClear
ld a,(DotX)
ld b,a ; B = x
ld a,(DotY)
ld c,a ; C = y
ld a,COLOR_WHITE
call FbPlot
end
The second axis costs what you would expect: one more state cell, two
more pulses, two more rules with the clamp turned sideways. GoHome
shows an effect at its simplest - two constant stores - and DrawDot
draws from both facts, so movement on either axis redraws.
Build it, run it, and hold 6 while tapping 2: the dot runs right, steps up on each tap, and carries on running right.
The keypad, by name
The TEC-1G’s MON-3 monitor names every key, and bind uses those
names directly:
| Keys | Names |
|---|---|
| The hex digits 0-F | KEY_0 through KEY_F |
| Plus and minus | KEY_PLUS, KEY_MINUS |
| GO | KEY_GO |
| AD (address) | KEY_AD |
Twenty keys, four of them off the hex pad. Game controls usually live
on the digits - 2, 4, 6, 8 make a compass, 5 sits in the middle of it
for fire or rotate - with GO and AD as start and menu keys. The names
compile to MON-3’s key codes in the generated file, so the binding
bind key KEY_2 ... and the panel’s 2 key mean the same physical
thing.
Rising or held
Every binding chooses one of two shapes, and the choice is a game design decision:
risingfires once, on the frame the key goes down. Press again to fire again. Choose it for actions: fire, rotate, pause, start.held period Nfires on the press, then again every N frames while the key stays down. Choose it for movement, and tune N to taste: a small period is a fast walk, a large one a deliberate step.
Rover uses both shapes for exactly those reasons: held compass keys,
rising GO. A rotate-the-piece key in a falling-blocks game wants
rising - one press, one quarter turn - while the move-left key under
the same thumb wants held. The two shapes on two adjacent keys give
the game its feel, and both are one-line decisions.
One property of the keypad to design around: MON-3 reports a single pressed key at a time, so held movement runs one direction at once, and a fresh press takes over the autorepeat from the key before it. Rover’s controls - and the games later in this book - are built on single-key movement, which suits the keypad’s compass layout.
Any key at all
A third binding form catches every key:
pulse Wake
bind key any rising -> Wake
any fires its pulse on every new press, whichever key it is, and it
fires alongside the named bindings - a press of GO fires both Home
and Wake in the same frame. It comes in the rising shape only: the
moment it exists for is the player touched the machine. Title
screens wait on it - “press any key” is a bind key any and a card
transition, as chapter 13 shows.
What polling looks like
The pulses in Rover come from one generated routine. Here is the top
of it, from rover.main.asm:
; --- input polling (MON-3 _scanKeys) ---
.routine
GlimPollBindings:
ld c,ApiScanKeys
rst $10
jr z,_keydown
ld a,$FF ; no key: disarm autorepeat
ld (Glim_HeldKey),a
ret
_keydown:
ld b,a ; B = key code
jr c,_newpress
ld a,(Glim_HeldKey) ; held: autorepeat armed for this key?
cp b
ret nz
ld a,(Glim_HeldCount)
dec a
ld (Glim_HeldCount),a
ret nz
...
Once per frame, the routine asks MON-3 about the keypad: _scanKeys
answers in the flags - zero set means a key is down, carry set means
the press is new this frame. From those two flags the routine sorts
the three cases you have been designing with. Silence disarms the
autorepeat. A held key counts its repeat clock down and fires its
pulse when the count runs out, reloading the period from your bind
line. A new press fires its pulse at once and arms the clock:
Glim_HeldKey: .db $FF
Glim_HeldCount: .db 0
Two bytes of storage run the whole autorepeat - which key is armed, and how many frames remain until it repeats. When a pulse fires here, the poll writes the pulse’s byte and sets its change bit directly: polling runs before any block, so every phase of the frame sees the moment.
At the other end of the frame, GlimEndFrame clears every pulse byte
- the cleanup you read in chapter 2. Between those two points, a
moment is a fact like any other: one frame wide, one bit in
Changed0, triggering whatever declaredonit.
Summary
- A pulse is a moment made declarable: it fires, triggers its dependents for one frame, and clears at frame end.
bind key <NAME> rising -> Pulsefires once per press;held period Nautorepeats every N frames while the key stays down. Actions take rising; movement takes held.- MON-3 names the twenty keys:
KEY_0..KEY_F,KEY_PLUS,KEY_MINUS,KEY_GO,KEY_AD. bind key any risingfires on every new press, alongside the named bindings - the “press any key” moment.- The keypad reports one key at a time; a new press takes over the autorepeat. Design controls on single-key movement.
- Generated polling reads
_scanKeysonce per frame and runs the autorepeat from two bytes of state; pulses raised there are visible to every phase of that frame.
Next: the three kinds of block, and the order a frame runs them in - Compute, Effect, Render.