A route might move one step right, one step left, then two steps right. Storing differences makes that relationship visible, but the differences are useless without their starting point. This decoder keeps the base and wrapping rule explicit.

Include and initialise
Reserve Q(8) for delta bytes, R(8) for output, public W/O/N/B and private I/T. Reserve lines 3000–3190, combine routines.bas with your caller using build.py and keep END, STOP or a jump before the helper. C64/BBC use GOSUB; Spectrum uses GO SUB. Private I/T are not caller scratch during a call.
Call 3000 at a deliberate reset boundary. It clears all public fields and both arrays. Then supply base B, count N=0..8 and the selected delta bytes. Retain an output array only as long as your game needs its decoded values; this is a whole-block service, not a streaming cursor.
Decode the selected deltas
Call 3100. Each Q element is a two’s-complement byte:0..127 mean that nonnegative difference,128..255 mean −128..−1. Starting at B, add each selected difference modulo 256 and place the successive values in R(1)..R(N). B, N and Q survive the call.
| Field | Use |
|---|---|
| B / N / Q(1)..Q(8) | Base, selected count and source delta bytes |
| R(1)..R(8) | Owned destination; unused tail preserved |
| W / O | Actual written count / valid flag |
N, B and selected Q values must be integral and in range. Success returns W=N,O=1. Invalid input returns W=0,O=0 and preserves all output elements; the helper validates the complete selected input before writing. Empty input is valid, writes nothing and does not inspect unused Q elements.
This is deliberate byte wrapping, not clamping. For example 250+10 becomes 4, and 4 plus a delta byte 128(which represents−128) becomes 132 modulo 256. Use a wider or checked-coordinate contract where wrapping would be wrong for the world.
The reusable routine
Reusable numbered routines
3000 LET W=0: LET O=0: LET N=0: LET B=0
3010 FOR I=1 TO 8: LET Q(I)=0: LET R(I)=0: NEXT I: RETURN
3100 LET W=0: LET O=0
3110 IF N<0 OR N>8 OR N<>INT (N) THEN RETURN
3120 IF B<0 OR B>255 OR B<>INT (B) THEN RETURN
3130 IF N=0 THEN LET O=1: RETURN
3140 FOR I=1 TO N
3150 IF Q(I)<0 OR Q(I)>255 OR Q(I)<>INT (Q(I)) THEN RETURN
3160 NEXT I: LET T=B
3170 FOR I=1 TO N
3180 LET T=T+Q(I): LET T=T-256*INT (T/256): LET R(I)=T
3190 NEXT I: LET W=N: LET O=1: RETURNAdding the stored unsigned byte and reducing modulo 256 gives the same wrapped result as adding its signed interpretation. That avoids dialect-specific signed-byte conversion while retaining the specified output. The helper allocates no storage or display resources.
Keep the base with its stream
The normal caller starts at 100 and decodes a mix of forward, backward and zero moves. The custom caller starts at 250 and includes wrapping and the −128 delta. Later calls exercise empty input, invalid count and a new base while checking the untouched output tail.
Example calls from the maintained caller
100 GOSUB 3000: LET AA=0: LET BB=0
110 LET Q(1)=1: LET Q(2)=255: LET Q(3)=2: LET Q(4)=254
120 LET Q(5)=10: LET Q(6)=0: LET Q(7)=1: LET Q(8)=255
130 LET N=8: LET B=100: FOR JJ=1 TO 8: LET R(JJ)=99: NEXT JJ
140 GOSUB 3100: GOSUB 2000: LET AA=1
150 IF R(1)<>101 THEN LET AA=0
160 IF R(2)<>100 THEN LET AA=0
170 IF R(3)<>102 THEN LET AA=0
180 IF R(4)<>100 THEN LET AA=0
190 IF R(5)<>110 THEN LET AA=0
200 IF R(6)<>110 THEN LET AA=0
210 IF R(7)<>111 THEN LET AA=0
220 IF R(8)<>110 THEN LET AA=0
230 IF B=100 THEN LET BB=1
240 LET N=0: LET B=200: GOSUB 3100: GOSUB 2000
250 LET N=9: LET B=200: GOSUB 3100: GOSUB 2000
260 LET N=3: LET B=200: GOSUB 3100: GOSUB 2000
270 PRINT "MOVE RESULTS";AA;BB
280 POKE MB+995,AA: POKE MB+996,BB: POKE MB+997,GD: POKE MB+998,WC-256*INT (WC/256): POKE MB+999,INT (WC/256): POKE MB+1000,239: STOPLine 2000, AA/BB/JJ/KK (Z/Q on Spectrum) and mailbox writes are diagnostics. A game supplies B/N/Q, calls 3100 and consumes only the first W output elements after checking O. Do not confuse the allocated eight slots with the returned length.
Customise representation
Choose what the decoded bytes represent: coordinates, table entries or another cyclic byte quantity. A long path needs larger arrays/offsets and perhaps periodic absolute checkpoints for random access. With only deltas, reaching a later point requires summing the earlier differences.
One delta byte still produces one output byte. This alone does not compress the stream; savings require narrower packed deltas or another encoding, with an escape rule for changes that do not fit. Absolute bytes can be simpler and give immediate random access.
Mistake and repair
The wrong caller clears B before decoding. Every output is shifted from the intended route, even though the helper completes successfully. Both saved checks fail because the decoded values and supplied base differ. Preserve the base alongside the deltas. The repair matches the normal source and loaded program exactly.

Cost and Maths for Games
A call scans at most eight selected inputs before making at most eight cumulative additions and output stores. The second phase runs only after validation. BASIC arithmetic and array storage remain interpreter-managed; no native-byte size or cycle budget is inferred.
Maths for Games connects signed representations, cumulative sums and modulo arithmetic. The Craft helps decide whether a path needs wrapping, clamping or a larger coordinate range.
Machine and measured size
C64 BASIC V2 recognises only the first two significant characters of numeric variable names. Short names make the ownership visible; check for collisions before including another component. Array element zero exists but is unused here. The diagnostic uses a mailbox at $C000, outside the normal BASIC heap.
The numbered helper occupies 334 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 C64’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-services.py bounded-delta-decoder commodore-64 --tools /path/to/extracted/releases and inspect with python3 scripts/check-basic-services.py bounded-delta-decoder.
Explore further
Compare the assembly technique. The BASIC game-flow routine makes session boundaries explicit. Complete source and native evidence.