Skip to content

Propose a Push from a Moving Solid in BBC Micro BASIC

Detect a one-dimensional moving solid crossing an actor and propose a safe displacement or an explicit blocked result.

A sliding wall can cross an actor even when it finishes beyond the actor’s old position. Keep both wall positions, establish which side the actor started on, then ask where the actor would need to end up.

BBC Micro BASIC shows both retained behaviour checks passing.
The working caller completes with both saved checks equal to one.

Include and initialise

Reserve public P/U/C/O/X/W/A/B/S/L/H, private T and lines 3000–3260. There are no arrays. Combine routines.bas with the caller through build.py; prevent fall-through with END, STOP or a jump. Use GOSUB on C64/BBC and GO SUB on Spectrum. Call 3000 to clear public fields, then supply actual world state.

X/W are actor position/size, A/B the solid’s old/new positions and S its size. L/H bound the actor’s leading position. All seven inputs are integral bytes; sizes are nonzero, X lies within L/H and every interval endpoint is at most 255. Coordinates do not wrap. Half-open intervals treat exact touching as non-overlap. The actor and solid must not overlap initially.

Ask for a proposal

Call 3100. P initially equals X. O=1 means the query is valid. If the moving solid crosses the actor from the left, P becomes B+S; from the right, P becomes B-W. U=1 reports a proposed push. If that proposed position would leave L/H or its actor endpoint exceed 255, the helper preserves P=X, returns U=0 and C=1 for blocked.

Reusable numbered routines

routines.bas
3000 LET P=0: LET U=0: LET C=0: LET O=0: LET X=0: LET W=0: LET A=0: LET B=0: LET S=0: LET L=0: LET H=0: RETURN
3100 LET P=X: LET U=0: LET C=0: LET O=0
3110 IF X<0 OR X>255 OR X<>INT (X) THEN RETURN
3120 IF W<1 OR W>255 OR W<>INT (W) THEN RETURN
3130 IF A<0 OR A>255 OR A<>INT (A) THEN RETURN
3140 IF B<0 OR B>255 OR B<>INT (B) THEN RETURN
3150 IF S<1 OR S>255 OR S<>INT (S) THEN RETURN
3160 IF L<0 OR L>255 OR L<>INT (L) THEN RETURN
3170 IF H<0 OR H>255 OR H<>INT (H) THEN RETURN
3180 IF L>H OR X<L OR X>H OR X+W>255 OR A+S>255 OR B+S>255 THEN RETURN
3190 IF A<X+W AND A+S>X THEN RETURN
3200 LET O=1
3210 IF B>A AND A+S<=X AND B+S>X THEN LET T=B+S: GOTO 3250
3220 IF B<A AND B<X+W AND A>=X+W THEN LET T=B-W: GOTO 3250
3230 RETURN
3250 IF T<L OR T>H OR T+W>255 THEN LET C=1: RETURN
3260 LET P=T: LET U=1: RETURN
U / C / O Meaning
0 / 0 / 1 Valid, no push needed
1 / 0 / 1 Safe one-dimensional proposal
0 / 1 / 1 Push would leave the allowed interval
0 / 0 / 0 Invalid query or initially overlapping intervals

Invalid input preserves every input and P=X. No branch commits X or moves the solid. A blocked proposal is valid feedback, distinct from corrupt data.

Follow a crossing

Normally the actor occupies 10 through 12 and a size-two solid moves from zero to twelve. It crosses the actor and proposes P=14. The custom actor is at five, size two, while the solid moves from ten to zero: the required proposal is minus two, outside the lower bound. C=1 reports that it cannot fit.

Example calls from the maintained caller

demo.bas, excerpt
70 GOSUB 3000: LET AA=0: LET BB=0
80 LET X=10: LET W=2: LET A=0: LET B=12: LET S=2: LET L=0: LET H=30
90 GOSUB 3100: GOSUB 2000
100 IF P=14 THEN LET AA=1
110 IF U=1 THEN LET BB=1
120 LET X=20: LET A=0: LET B=5
130 GOSUB 3100: GOSUB 2000
140 LET X=10: LET A=20: LET B=8
150 GOSUB 3100: GOSUB 2000
160 LET X=10: LET A=10: LET B=12
170 GOSUB 3100: GOSUB 2000
180 PRINT "SOLI RESULTS";AA;BB
190 ?(MB+995)=AA: ?(MB+996)=BB: ?(MB+997)=GD: ?(MB+998)=WC-256*INT (WC/256): ?(MB+999)=INT (WC/256): ?(MB+1000)=239: STOP

Later queries cover an unaffected actor, a push from the other side and invalid initial overlap. Line 2000, AA/BB/JJ (Z on Spectrum) and the mailbox are diagnostics. A game commits an accepted P only after its other collision rules have agreed.

Choose the blocked policy

A game might stop the solid, reject its complete move, damage the actor or begin a crush animation. C=1 chooses none of those. Nor does this query test the other axis, detect walls beyond the allowed interval, resolve several solids or handle a rotating shape.

The starting non-overlap requirement prevents guessing a depenetration direction. A separate overlap-recovery routine needs its own policy. For wider worlds, change coordinate bounds and intermediate capacity together; for wrapping worlds, supply an explicit displacement convention.

Mistake and repair

The wrong caller replaces old A with new B before the query. There is then no movement to sweep. The normal push disappears; the custom blocked crossing also disappears. Both saved checks fail. Keep the old position until the query has consumed it. Repaired source and loaded program match normal exactly.

BBC Micro BASIC shows two failed checks from the deliberate caller mistake.
The wrong caller runs to completion, but both saved behaviour checks are zero.

Cost and Maths for Games

This bounded query validates seven inputs and several widened endpoints, then performs at most one proposal check. It neither enumerates world objects nor allocates storage. BASIC retains signed intermediate arithmetic; an assembly implementation needs deliberate width and borrow handling.

Maths for Games connects interval intersections, inequalities and swept displacement. The Craft connects movement order and blocked feedback to the rules the player experiences.

Global names and line numbers

BASIC numeric names, arrays and numbered lines are global. To combine helpers, rename public and private fields and renumber the helper together with every GO SUB/GO TO reference. Keep each state block under one owner. The maintained build.py rejects duplicate line numbers; it does not automatically namespace or renumber routines.

Machine and measured size

This helper uses scalar numeric fields and allocates no arrays. Its diagnostic uses MODE 7 and HIMEM=&6000 to separate program storage, mailbox and screen. The helper itself changes neither mode nor HIMEM.

The numbered helper occupies 588 tokenised program bytes on this interpreter, including line headers. This excludes the caller, test tables, numeric scalar/array storage, interpreter stacks and ROM. Tokenised size is not machine-code size or a hardware-cycle count. No real-time or whole-game performance budget is inferred from the diagnostic. The caller’s AA/BB, JJ and RAM mailbox are observation machinery, not required game state.

Execution evidence

Working, custom, deliberately wrong and exact repaired callers executed through BBC Micro’s stock BASIC interpreter under Emu198x 0.44.2. Build198x 0.2.9 tokenises C64 PRG and Spectrum TAP programs. C64 enters stock RUN, Spectrum uses ROM tape loading, and BBC enters every numbered source line through MOS/BASIC. Actual loaded program bytes, complete state packets, positive completion/count/guards and visible result pixels are checked. After the positive completion marker, the observer advances two complete display frames before capturing, so a partially scanned final character cannot masquerade as a finished picture. Firmware is not distributed.

An independent mathematical/order model checks the trace and rejects altered source, configuration, loaded program, state, completion and image evidence. Host exhaustive checks complement the native boundary/sequential examples; those host ranges are not claimed as natively exhaustive. The supplied diagnostic callers deliberately end with STOP; the interpreter report is expected. The wrong caller prints 0,0 and the normal/custom/repaired callers print 1,1.

Public CI checks retained evidence; it does not execute restricted firmware. Refresh with python3 scripts/verify-basic-traversal.py moving-solid-push bbc-micro --tools /path/to/extracted/releases and inspect with python3 scripts/check-basic-traversal.py moving-solid-push.

Explore further

Compare the assembly technique. The BASIC game-flow routine makes session boundaries explicit. Complete source and native evidence.