Skip to content

Sample keys without repeating turns on BBC Micro

A reusable BASIC press-edge update with a MOS-paced caller, separate display work and verified held-key and missing-wait mistakes.

Tap Space: the turn counter advances. Hold it down: the loop keeps sampling, but the world takes just one turn. Release it and press again for the next move.

That gives us a useful split. The caller owns pacing, input and drawing. A small callable routine decides whether the current key sample represents a new press. You can keep that routine when the game around it grows.

The BBC Micro BASIC caller after two presses, showing TURN 2.

Enter the working program

Enter the caller, then the routines below, as one numbered BASIC program. Type RUN. Use a stock BBC Model B with MOS and BASIC; this caller selects MODE 6 and keeps normal operating-system interrupt handling.

The complete caller: wait, sample, apply a turn and draw changes

caller.bas
10 MODE 6:HIMEM=&3000
20 N%=0:S%=1:P%=FALSE:F%=0:C%=0:?&3004=165:?&3005=90:PRINT "TURN ";N%
30 GOSUB 1000
40 K%=INKEY(-99)
50 GOSUB 1100
60 F%=(F%+1)AND65535
70 IF C% THEN PRINT TAB(0,0);"TURN ";N%;"   "
80 ?&3000=N%:?&3001=F%AND255:?&3002=F%DIV256:?&3003=C%
90 GOTO 30

Startup happens once. Each pass waits through MOS, reads Space with INKEY(-99), applies the rule and redraws only if a turn was accepted. F% counts loop iterations; it is not the turn counter and is not a reliable elapsed-time clock.

The addresses above HIMEM=&3000 are a diagnostic mirror: &3000 holds the counter, &3001–&3002 the iteration count and &3003 the accepted flag. Two guard bytes follow at &3004–&3005. The reusable routines do not require those six bytes. Remove the mirror writes if your game does not need them, and reserve any memory you do keep against BASIC’s growing program and variable workspace.

The reusable pieces

Copy lines 1000–1130 to free line numbers, or renumber them and the call sites together. GOSUB 1000 performs the wait. GOSUB 1100 applies the press rule.

MOS pacing and a reusable press-edge turn update

routines.bas
1000 *FX 19
1010 RETURN
1100 C%=0
1110 IF K% AND NOT P% THEN N%=(N%+S%)AND255:C%=1
1120 P%=K%
1130 RETURN

*FX 19 uses the same documented MOS synchronisation service as the assembly wait. It passes through the MOS command interpreter, so it costs more than a short assembly call. Keep the normal IRQ service running; this is not a replacement for a takeover game’s timing code.

For the rule update, put a TRUE/FALSE sample in K%. Keep P%, the previous sample, between calls. The key is newly pressed when it is held now and was not held previously.

Global Ownership and result
N% Counter; new presses add the step modulo 256
S% Step; input, preserved
K% Current TRUE/FALSE sample; input, preserved
P% Previous sample; replaced with K% on every call
C% Result: 1 for a new accepted press, 0 otherwise

Initialise P%=FALSE once. Do not clear it inside the loop, or every held sample looks new. Consume C% before another routine reuses that global. These are BASIC globals, not local variables; rename them consistently to avoid collisions. Each GOSUB uses an interpreter return frame. The press routine owns no display, keyboard driver, heap or interrupt handler.

Customise the useful parts

Change S%=1 to S%=2. Two presses now produce 0 → 2 → 4. Holding the first press still adds only two once. No extra routine or state variable is needed.

The customised BBC Micro BASIC caller after two presses, showing TURN 4.

Start N% at 254 to check 254 → 255 → 0. The mask makes this a byte counter even though BASIC integers are wider. If you need a larger move count, change the representation and wrap policy deliberately.

To use another key, replace the negative INKEY key number with the documented one. An ASCII code is a different kind of query. A short tap entirely between samples can be missed; an input queue or interrupt capture needs a different contract.

Common mistake: treating held as newly pressed

This replacement is deliberately broken. It looks only at the current sample, so holding Space pours turns into the counter.

Broken routine: held input becomes repeated turns

no-edge.bas
1000 *FX 19
1010 RETURN
1100 C%=0
1110 IF K% THEN N%=(N%+S%)AND255:C%=1
1120 P%=K%
1130 RETURN

The faulty BBC Micro caller after holding Space, showing multiple unwanted turns.

Restore the original routine and keep P% between calls. The native checks run the repair afresh: press once, hold, release and press again. The working progression is 0 → 1 → 1 → 2.

Common mistake: removing the wait

The next replacement removes the MOS wait while keeping the press rule intact. The iteration counter now follows interpreter work rather than display fields.

Broken pacing: RETURN without waiting for a field

no-wait.bas
1000 RETURN
1010 REM No field wait: this counts BASIC iterations.
1100 C%=0
1110 IF K% AND NOT P% THEN N%=(N%+S%)AND255:C%=1
1120 P%=K%
1130 RETURN

The tested faulty BBC caller completes 56 iterations in 30 native fields, so the same field-rate guard rejects it. Restore *FX 19 in the wait routine. A program that prints its speed to the screen can be misleading: printing itself adds work and can conceal the missing wait.

Keep enough time for the work

A wait supplies a synchronisation point; it cannot make slow work fit. In our diagnostic, redrawing the text every pass made the Electron miss alternate fields. Drawing only when state changes restores 30 idle iterations in 30 native fields on each tested machine. A frame containing a redraw can still take longer. That is a workload observation, not a promise for every game.

The BBC’s separate display circuitry leaves a different CPU budget from the Electron’s shared-memory display path. Keep expensive drawing separate from rules, measure your real workload, and choose an elapsed-time source if the game has timed turns. A missed field is not a reason to take extra player turns.

Choose how your game advances separates progression from timing and presentation. The other Acorn BASIC port uses the same maintained source and has its own native execution record.

What we checked

Code status: firmware-entered and emulator-executed. Released Emu198x 0.44.0 cold-boots the BBC Model B, types the whole program and drives actual key press/release events. The native record identifies its own tool binary, firmware, source and captures.

Seven fresh cases cover default and step-two input, a nonzero start, byte wrap, the held-key fault, a repaired run and an isolated missing-wait fault. Checks observe guest state and guards, measure field pacing and reject idle advancement or repeated working turns. The retained checker rejects incomplete cases and altered inputs; it does not stand in for fresh emulation. No original-hardware or complete-game performance claim is implied.

Primary references

Acorn’s BBC Microcomputer User Guide, INKEY, its negative-key table and *FX 19; Acorn’s Electron User Guide, the corresponding BASIC and operating-system commands.