A game can have more background jobs than it wants to run in one update. Always starting at job zero lets that job monopolise the available turns. A rotating start position gives the other ready jobs a chance.

Include and initialise
Reserve public S/F/N/M/O and private I/K/P/T. Reserve lines 3000–3210, combine routines.bas and your caller with build.py and keep END, STOP or a jump before the helper. C64/BBC use GOSUB; Spectrum uses GO SUB. Private I is not available as an outer caller’s FOR variable.
Call 3000 at a deliberate scheduling reset. It clears every public field. Set ready mask M afterwards and retain next position N between decisions. Bit zero represents slot 0; bit seven represents slot 7. Add powers of two to mark several ready slots.
Select one ready slot
Call 3100. Starting at N modulo eight, the helper examines each slot at most once, wrapping after seven. If it finds a set bit, S is that slot, F=1 and N becomes the following slot modulo eight. M is preserved; the caller decides whether a job remains ready after doing its work.
| Field | Use |
|---|---|
| N | Retained next starting position; byte input, normalised modulo eight |
| M | Ready-task bit mask, 0..255; preserved |
| S / F | Returned slot / found flag |
| O | Fresh valid-input flag |
No ready slots returns S=255,F=0 and normalises N without advancing it. A valid request sets O=1. Invalid non-byte/non-integral N or M returns O=0,S=255,F=0 and preserves N/M. This BASIC guard extends the assembly helper’s byte-input representation. S=0 is a real task; check F.
The reusable routine
Reusable numbered routines
3000 LET S=0: LET F=0: LET N=0: LET M=0: LET O=0: RETURN
3100 LET S=255: LET F=0: LET O=0
3110 IF N<0 OR N>255 OR N<>INT (N) THEN RETURN
3120 IF M<0 OR M>255 OR M<>INT (M) THEN RETURN
3130 LET N=N-8*INT (N/8): LET O=1: LET K=N
3140 FOR I=1 TO 8
3150 LET P=2^K: LET T=INT (M/P): LET T=T-2*INT (T/2)
3160 IF T=1 THEN LET S=K: LET F=1: LET N=K+1: GOTO 3200
3170 LET K=K+1
3180 IF K=8 THEN LET K=0
3190 NEXT I: RETURN
3200 IF N=8 THEN LET N=0
3210 RETURNThe arithmetic bit test extracts one mask bit without assuming common bitwise syntax across dialects. Advancing N after a successful choice creates rotation; the mask by itself does not supply fairness.
Retain the next start
With M=21, bits 0, 2 and 4 are ready. Six decisions return 0, 2, 4, 0, 2, 4. The custom caller starts at 7 with bits 7 and 0 ready, showing wraparound as 7, 0, 7, 0, 7, 0.
Example calls from the maintained caller
70 GOSUB 3000: LET AA=0: LET BB=0
80 LET N=0: LET M=21: LET SS=0: LET NN=0: FOR JJ=1 TO 6
90 GOSUB 3100: GOSUB 2000: LET SS=SS+S: LET NN=NN+2^S: NEXT JJ
100 IF SS=12 THEN LET AA=1
110 IF NN=42 THEN LET BB=1
120 PRINT "TURN RESULTS";AA;BB
130 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: STOPThe diagnostic’s line 2000, AA/BB/SS/NN/JJ (Z on Spectrum) and mailbox record those decisions. Your game calls 3100, checks O/F and runs one bounded piece of the selected job. The helper does not run the job itself or read elapsed time.
Customise the ready mask
Change M as work becomes ready or finishes. With a stable mask, every ready slot receives one selection per cycle. If readiness changes, the next search uses the new mask from the retained N. This says nothing about equal CPU time: a long job can still block every other job.
For predictable update cost, each selected job must do a limited piece of work and return. Fixed priority is useful for urgent work but can starve other jobs. Weighted service, deadlines or a mixed urgent/background budget need explicit policies and additional state. Rotation alone does not enforce a frame budget.
Mistake and repair
The wrong caller resets N to zero before every decision. Slot zero is continuously ready and wins every time. The sum and membership checks both fail because later slots never run. Retain N between calls. Repaired source and loaded program match the normal caller.

Cost and Maths for Games
Each decision checks at most eight candidates with division, powers and remainder arithmetic. There are no arrays or dynamic allocation in the helper. These are operation bounds, not measured cycle or frame-time guarantees.
Maths for Games connects cyclic order, modulo arithmetic and bit sets. The Craft connects fair background service to the work that must finish in time for responsive play.
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. This helper uses scalar numeric fields and allocates no arrays. The diagnostic uses a mailbox at $C000, outside the normal BASIC heap.
The numbered helper occupies 330 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 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 round-robin-service commodore-64 --tools /path/to/extracted/releases and inspect with python3 scripts/check-basic-services.py round-robin-service.
Explore further
Compare the assembly technique. The BASIC game-flow routine makes session boundaries explicit. Complete source and native evidence.