A restart point should put the player back where you saved them, with the right resource count. Restoring one field before discovering that the rest of the record is damaged can leave a peculiar half-restored world. This helper checks one small RAM snapshot before changing the live fields.

Include and initialise
Reserve live X/Y/R, saved S/T/U, checksum K, presence V, mode M, results O/E and private A/B/C/H/I/P/D. Reserve lines 3000–3820, combine numbered source using build.py and keep END, STOP or a jump before the helper. C64/BBC use GOSUB; Spectrum uses GO SUB. Private scratch must not be an enclosing caller FOR variable.
Call 3000 at a deliberate new-session boundary. It clears every public field, including the saved-presence flag. There are no arrays. X/Y can hold coordinates and R a resource count, but their game meaning belongs to the caller. Each is an integral byte value from zero to 255.
Capture or restore
Set M=0 and call 3100 to capture X/Y/R into S/T/U, compute their XOR checksum K and set V=1. O=1,E=0 reports completion. Set M=1 and call 3100 to restore. It checks the presence flag, saved values and checksum before changing any live field. A successful restore copies all three and returns O=1,E=0.
An absent snapshot, V=0, returns O=0,E=0 without changing X/Y/R. A zero checksum is valid; V tells you whether a record exists. E=1 rejects an invalid presence flag or selected source fields; E=2 rejects an unknown mode; E=3 reports a checksum mismatch. Failures preserve live fields. Capture validates the live triple; restore validates the stored triple/checksum. It can replace live destination values without first treating those old destination values as a snapshot.
The reusable routine
Reusable numbered routines
3000 LET X=0: LET Y=0: LET R=0: LET S=0: LET T=0: LET U=0: LET K=0: LET V=0: LET M=0: LET O=0: LET E=0: RETURN
3100 LET O=0: LET E=1
3110 IF V<>0 AND V<>1 THEN RETURN
3120 LET E=2
3130 IF M<>0 AND M<>1 THEN RETURN
3140 IF M=1 THEN GO TO 3300
3150 LET E=1
3160 IF X<0 OR X>255 OR X<>INT (X) THEN RETURN
3170 IF Y<0 OR Y>255 OR Y<>INT (Y) THEN RETURN
3180 IF R<0 OR R>255 OR R<>INT (R) THEN RETURN
3200 LET A=X: LET B=Y: LET C=R: GO SUB 3800
3210 LET S=X: LET T=Y: LET U=R: LET K=H: LET V=1: LET O=1: LET E=0: RETURN
3300 LET E=0
3310 IF V=0 THEN RETURN
3320 LET E=1
3330 IF S<0 OR S>255 OR S<>INT (S) THEN RETURN
3340 IF T<0 OR T>255 OR T<>INT (T) THEN RETURN
3350 IF U<0 OR U>255 OR U<>INT (U) THEN RETURN
3360 IF K<0 OR K>255 OR K<>INT (K) THEN RETURN
3370 LET A=S: LET B=T: LET C=U: GO SUB 3800: LET E=3
3380 IF K<>H THEN RETURN
3390 LET X=S: LET Y=T: LET R=U: LET O=1: LET E=0: RETURN
3800 LET H=0: LET P=1: FOR I=1 TO 8
3810 LET D=INT (A/P)-2*INT (A/(2*P))+INT (B/P)-2*INT (B/(2*P))+INT (C/P)-2*INT (C/(2*P))
3820 LET D=D-2*INT (D/2): LET H=H+D*P: LET P=P*2: NEXT I: RETURNThe arithmetic bit loop computes XOR parity without assuming every BASIC dialect has the same bitwise operators. Only after the whole record passes validation does the restore copy its fields. This is a single-owner subroutine contract, not interrupt exclusion or a transaction spanning other components.
Restore a saved position
The maintained caller captures a position/resource count, changes the live values and restores. It then damages one saved field, checks rejection, marks the snapshot absent and captures again. The complete diagnostic also covers empty records, valid zero checksum, maximum bytes, checksum failure and an XOR collision.
Example calls from the maintained caller
70 GO SUB 3000: LET X=10: LET Y=20: LET R=3: GO SUB 3100: GO SUB 2000
80 LET X=200: LET Y=201: LET R=1: LET M=1
90 GO SUB 3100: GO SUB 2000: LET AA=0: LET BB=0
100 IF X=10 AND Y=20 THEN LET AA=1
110 IF R=3 THEN LET BB=1
120 LET S=S+1: GO SUB 3100: GO SUB 2000
130 LET V=0: GO SUB 3100: GO SUB 2000
140 LET M=0: GO SUB 3100: GO SUB 2000
150 LET M=1: GO SUB 3100: GO SUB 2000
160 PRINT "SAVE RESULTS";AA;BB
170 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, mailbox writes, AA/BB and JJ (Z on Spectrum) serve the diagnostic. Your game supplies its fields, calls 3100 and handles O/E. It does not need the packet writer or saved test digits.
Customise the snapshot
The custom caller changes the initial coordinates and resource count. Choose what those fields mean, and add game-specific checks where needed. A byte range alone cannot tell you whether a coordinate is inside a particular room or a resource count is legal for that actor.
Independent checkpoints or other helpers need distinct variables, private scratch and numbered references. Do not reuse saved fields as another routine’s scratch.
This is one RAM checkpoint, not a whole-game save file. It owns no disk/tape format, room objects, random-generator state, external handles or version migration. Extending a snapshot requires capturing and validating every required field before committing a restore. A saved reference may also need its object’s lifetime state; three unrelated bytes do not make a coherent world automatically.
The XOR checksum detects some mistakes but has many collisions. Swapping the two saved coordinates leaves it unchanged, and some paired changes cancel out. It supplies no authenticity guarantee. Stronger checksums and structured validation cost more code/state and need their own format and tests. A missing record and a corrupt one remain distinct outcomes here.
Mistake and repair
The wrong caller uses the saved checksum K as scratch and sets it to zero before restoring. The chosen record’s checksum is nonzero, so restoration is refused. Both saved checks print zero because the live position and resource count still hold the changed values.
The repair preserves the complete saved record until a deliberate recapture. Repaired source and loaded tokenised program match the working caller exactly.

Cost and Maths for Games
Each capture or present restore calculates eight bit parities over three values, in addition to range checks and copying. That is more interpreter arithmetic than a native XOR instruction. The fields are BASIC numbers, not eleven packed machine-code bytes. There is no measured cycle budget or durable-storage claim.
Maths for Games connects parity, XOR and collisions to error checking. The Craft helps decide where checkpoints belong and what the player expects a restart to preserve.
Machine and measured size
Spectrum FOR variables must be single letters: this diagnostic uses Z instead of the other callers’ JJ. The helper uses scalar numeric fields and allocates no arrays. AND/OR in these listings join comparisons; neither is being used as a bitwise operator. The diagnostic reserves its mailbox above CLEAR 59999.
The numbered helper occupies 954 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, Z and RAM mailbox are observation machinery, not required game state.
Execution evidence
Working, custom, deliberately wrong and exact repaired callers executed through Spectrum’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-timelines.py checkpoint-state sinclair-zx-spectrum --tools /path/to/extracted/releases and inspect with python3 scripts/check-basic-timelines.py checkpoint-state.
Explore further
Compare the assembly technique. The BASIC game-flow routine makes session boundaries explicit. Complete source and native evidence.