Skip to content

Insert a Score and Keep Ties Stable in BBC Micro BASIC

Maintain four descending byte scores with associated tags, retaining existing equal scores first and rejecting an invalid table without changing it.

Two players finish on the same score. Which name comes first? A small table needs an answer for that tie, for a full board, and for a new score just below the cut. This routine makes each rule visible.

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

Include and initialise

Reserve S(4) for scores, I(4) for their tags, public R/F/O/V/T/N and private J/K/P. Reserve lines 3000–3340, including 3320 after the shift loop. Combine routines.bas with your caller using build.py and put END, STOP or a jump before the helper. C64/BBC use GOSUB; Spectrum uses GO SUB. Private loop/shift scratch must not be borrowed by an outer caller.

Call 3000 for a new table. It clears N, inputs V/T, F/O and arrays, and sets rank R=255. N is the active count, zero to four. Scores are descending in S(1)..S(N); I holds each score’s associated byte tag. Unused elements can retain old values and are ignored.

Insert a completed score

Supply byte score V and byte tag T, call 3100, then read R/F/O. Tags are identifiers, not name strings; your game can use them to find a name or another record. Keep each score/tag pair together during every move.

Field Use
N, S(1)..S(4), I(1)..I(4) Retained table and active count
V / T New score / associated tag; preserved
R / F / O Zero-based rank / inserted flag / valid-input flag

O=0 rejects invalid N, inputs, active score/tag values or a prefix that is not descending. N and the whole table survive. This BASIC guard checks the sorted-prefix precondition explicitly. Valid requests return O=1. If retained, F=1 and R=0..3; otherwise F=0,R=255. A valid low score can miss a full table without being an input error.

Existing equal scores keep their order, and the new equal score comes after them. A better score in a full table drops the last pair. A score tied with all four existing scores misses the cut. R=255 is an absence marker; a score of 255 is still valid.

The reusable routine

Reusable numbered routines

routines.bas
3000 LET R=255: LET F=0: LET O=0: LET V=0: LET T=0: LET N=0
3010 FOR J=1 TO 4: LET S(J)=0: LET I(J)=0: NEXT J: RETURN
3100 LET R=255: LET F=0: LET O=0
3110 IF N<0 OR N>4 OR N<>INT (N) THEN RETURN
3120 IF V<0 OR V>255 OR V<>INT (V) THEN RETURN
3130 IF T<0 OR T>255 OR T<>INT (T) THEN RETURN
3140 IF N=0 THEN GOTO 3210
3150 FOR J=1 TO N
3160 IF S(J)<0 OR S(J)>255 OR S(J)<>INT (S(J)) THEN RETURN
3170 IF I(J)<0 OR I(J)>255 OR I(J)<>INT (I(J)) THEN RETURN
3180 IF J=1 THEN GOTO 3200
3190 IF S(J)>S(J-1) THEN RETURN
3200 NEXT J
3210 LET O=1: LET P=N
3220 IF N=0 THEN GOTO 3260
3230 FOR J=1 TO N
3240 IF S(J)<V THEN LET P=J-1: GOTO 3260
3250 NEXT J
3260 IF P=4 THEN RETURN
3270 LET K=N+1
3280 IF K>4 THEN LET K=4
3290 IF K<=P+1 THEN GOTO 3320
3300 LET S(K)=S(K-1): LET I(K)=I(K-1): LET K=K-1: GOTO 3290
3320 LET S(P+1)=V: LET I(P+1)=T
3330 IF N<4 THEN LET N=N+1
3340 LET R=P: LET F=1: RETURN

The routine finds the first strictly smaller score and shifts the tail from the end backwards. Copying forwards would overwrite a pair before its old value was moved. The insertion position is zero-based; arrays are one-based, so R=0 maps to S(1)/I(1).

Keep one table across results

The normal caller submits repeated scores, a low score that misses the cut, a new maximum and a zero. It finishes with scores 255,100,100,90 and tags 6,1,3,4. The two original 100-point entries keep their order.

Example calls from the maintained caller

demo.bas, excerpt
100 GOSUB 3000: LET AA=0: LET BB=0
110 LET V=100: LET T=1
120 GOSUB 3100: GOSUB 2000
130 LET V=80: LET T=2
140 GOSUB 3100: GOSUB 2000
150 LET V=100: LET T=3
160 GOSUB 3100: GOSUB 2000
170 LET V=90: LET T=4
180 GOSUB 3100: GOSUB 2000
190 LET V=70: LET T=5
200 GOSUB 3100: GOSUB 2000
210 LET V=255: LET T=6
220 GOSUB 3100: GOSUB 2000
230 LET V=0: LET T=7
240 GOSUB 3100: GOSUB 2000
250 LET AA=1: LET BB=1
260 IF S(1)<>255 THEN LET AA=0
270 IF S(2)<>100 THEN LET AA=0
280 IF S(3)<>100 THEN LET AA=0
290 IF S(4)<>90 THEN LET AA=0
300 IF I(1)<>6 THEN LET BB=0
310 IF I(2)<>1 THEN LET BB=0
320 IF I(3)<>3 THEN LET BB=0
330 IF I(4)<>4 THEN LET BB=0
340 PRINT "RANK RESULTS";AA;BB
350 ?(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

The diagnostic’s line 2000, AA/BB/JJ/KK (Z/Q on Spectrum) and mailbox can be removed. A game retains the table between completed sessions, passes the achieved V/T before resetting gameplay, and uses R/F/O to decide what to show.

Customise the table

The custom caller includes maximum 255, zero and a tie at the maximum. It finishes 255,255,1,0 with the two tied tags still in submission order. Choose what each tag identifies; the helper does not store a name or write a save file.

Wider scores need a wider comparison and bounds. More entries need larger arrays, a new rank/count limit and tests for the longest shift. If a whole unsorted collection needs ordering, sorting it once may be a better fit than repeatedly inserting. Four entries keep this component small enough to inspect.

Mistake and repair

The wrong caller clears V before insertion. The table can only record the zero it receives; both the expected scores and their tag order are wrong. Pass the completed score before resetting it. The repair matches the normal source and loaded program 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

A call validates at most four active pairs, searches at most four scores and moves at most three pairs. It allocates no dynamic storage and performs no display or persistent I/O. BASIC numeric fields and arrays have interpreter-managed cost; these are not 14 packed native bytes.

Maths for Games connects ordering, stable ties and insertion bounds. The Craft connects the table’s rules to fair, predictable player feedback and when a completed score should become a saved result.

Machine and measured size

BBC BASIC arrays include element zero; this one-based array interface leaves it unused. 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 656 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/KK 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-records.py high-score-insertion bbc-micro --tools /path/to/extracted/releases and inspect with python3 scripts/check-basic-records.py high-score-insertion.

Explore further

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