You have three rounds and try to spend five. Nothing should happen to the balance. Pick up a large refill while almost full and a different rule may make sense: keep what fits and stop at the cap. This small wallet makes those two choices explicit.

Include and initialise
Reserve public B/A/M/O/E and lines 3000–3200. There are no arrays or private scratch variables. Combine routines.bas with your caller through build.py and prevent fall-through with END, STOP or a jump. C64/BBC use GOSUB; Spectrum uses GO SUB.
Call 3000 at a reset boundary. It clears all five fields. Set the starting balance B afterwards. Each independent resource needs its own renamed fields/line references, or a deliberate wrapper that loads and stores the selected balance.
Request one operation
Set amount A and mode M, then call 3100. M=0 refills: B becomes the smaller of 255 and B+A, with O=1. Excess refill is deliberately discarded. M=1 spends: if A fits the balance it subtracts the whole amount with O=1; otherwise B survives with O=0.
| Field | Use |
|---|---|
| B | Retained balance, zero through 255 |
| A / M | Requested amount / operation |
| O / E | Accepted flag / invalid-input flag |
B/A must be integral bytes. Only modes zero and one are valid. Invalid input preserves B/A/M and returns O=0,E=1. An unaffordable but valid spend returns O=0,E=0. A zero amount is a successful no-op in either mode. A/M survive all helper calls.
The reusable routine
Reusable numbered routines
3000 LET B=0: LET A=0: LET M=0: LET O=0: LET E=0: RETURN
3100 LET O=0: LET E=1
3110 IF B<0 OR B>255 OR B<>INT (B) THEN RETURN
3120 IF A<0 OR A>255 OR A<>INT (A) THEN RETURN
3130 IF M<>0 AND M<>1 THEN RETURN
3140 LET E=0
3150 IF M=1 THEN GO TO 3190
3160 LET B=B+A
3170 IF B>255 THEN LET B=255
3180 LET O=1: RETURN
3190 IF A>B THEN RETURN
3200 LET B=B-A: LET O=1: RETURNThe addition stays wide until it has been capped. Spending tests affordability before touching B. Decrementing first and repairing a negative balance later can already have exposed the wrong state to another part of a game.
Try a purchase
The normal caller tries five from a balance of three and checks the refusal. The custom caller changes only that first spend to two: it succeeds and leaves one. Both later refill to 255, spend 250 and reject an unaffordable six. The saved early checks matter because distinct histories can converge on the same final balance.
Example calls from the maintained caller
70 GO SUB 3000: LET AA=0: LET BB=0
80 LET B=3: LET A=5: LET M=1: GO SUB 3100: GO SUB 2000
90 IF O=0 AND M=1 THEN LET AA=1
100 IF B=3 THEN LET BB=1
110 LET A=255: LET M=0: GO SUB 3100: GO SUB 2000
120 LET A=250: LET M=1: GO SUB 3100: GO SUB 2000
130 LET A=6: LET M=1: GO SUB 3100: GO SUB 2000
140 PRINT "FUND RESULTS";AA;BB
150 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 (Z on Spectrum) and mailbox writes are observations. A game checks E, then O, and creates the effect or purchase only after acceptance. This routine cannot atomically coordinate a second wallet, an object pool or another writer. Validate and reserve all resources before committing a multi-resource action.
Customise the economy
For a smaller capacity, add an explicit cap and validate the initial balance against it; simply truncating a stored value changes the game economy. For multiple fixed categories, use the existing inventory-stacks routine. Its addition rejects a request that does not fit in full; this wallet instead accepts a saturating refill. Pick that policy deliberately.
A partial pickup that leaves a remainder in the world also needs an accepted-quantity result. O alone does not report how much refill was lost. Currency above 255 needs a wider representation; an ordinary wider BASIC scalar may be simpler than byte pairs when there is no binary interchange requirement.
Mistake and repair
The wrong caller labels spends as refills by setting M=0. The balance increases instead of being charged. The saved operation/acceptance and balance checks both fail, even for the custom affordable purchase. Preserve the requested mode and handle its result. Repaired source and loaded program match normal.

Cost and Maths for Games
The helper has fixed validation and one bounded arithmetic path; no category scan or allocation occurs. Atomic rejection and saturating acceptance enforce different invariants. Maths for Games connects conservation, limits and inequality checks. The Craft connects capacity and pickup rules to the player’s decisions.
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
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 321 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-resources.py resource-wallet sinclair-zx-spectrum --tools /path/to/extracted/releases and inspect with python3 scripts/check-basic-resources.py resource-wallet.
Explore further
Compare the assembly technique. The BASIC game-flow routine makes session boundaries explicit. Complete source and native evidence.