A pause button should let you take a breath without losing your score. Starting again is a different request. These routines keep that distinction visible, with four small states and a separate result telling the caller when to create a fresh session.

Include and call
Combine routines.bas with your numbered caller, in numeric order. The supplied build.py and Makefile do this. Reserve lines 3000–3470 and call 3000 once to initialise the flow. It sets S, V, C, F, E and previous input P to zero. It does not reset your score, lives or world. Put an END, STOP or jump before the helper so ordinary execution cannot fall into it.
The routine owns state S: 0 title, 1 playing, 2 game over, 3 paused. Supply an integral event mask V from 0 to 15, then call 3100. Combine requests by adding their distinct bits once; adding the same bit twice means something different.
| Bit | Request | Where it matters |
|---|---|---|
| 1 | start / return to title | title starts play; game over returns to title |
| 2 | toggle pause | playing pauses; paused resumes |
| 4 | end game | playing enters game over |
| 8 | restart | game over starts a fresh session |
The returned C=1 means the state changed. F=1 means a fresh session, only on title-to-play or game-over-to-play. Otherwise both results are zero. E=0 is success; invalid state gives E=1, invalid event mask E=2. Rejection leaves S unchanged and clears C/F. When simultaneous requests matter, end-game beats pause, and restart beats return-to-title. Each call makes at most one transition.
Turn held buttons into requests
If your input routine supplies a held mask, put it in H and call 3400 instead. It computes newly pressed bits against retained P, updates P, then calls the transition routine. Holding pause does not switch between playing and paused every update. Call this path even when H is zero, so releasing a button is remembered.
It checks H and P as integral masks 0–15. A bad mask returns E=3 without changing P or S; a later state/event rejection can still leave a valid new P recorded. Scratch variables W, U and I belong to the helper. Do not surround it with a caller’s FOR I loop. The routine reads no keyboard, timer, graphics or sound hardware. Translate the machine’s keys or joystick into the four request bits in your own input layer.
The reusable routine
Reusable numbered routines
3000 LET S=0: LET V=0: LET C=0: LET F=0: LET E=0: LET P=0: RETURN
3100 LET C=0: LET F=0: LET E=1
3110 IF S<0 OR S>3 OR S<>INT (S) THEN RETURN
3120 LET E=2
3130 IF V<0 OR V>15 OR V<>INT (V) THEN RETURN
3140 LET E=0: LET W=S
3150 IF S=0 THEN GOTO 3250
3160 IF S=1 THEN GOTO 3260
3170 IF S=2 THEN GOTO 3290
3180 IF INT (V/2)-2*INT (V/4)=1 THEN LET S=1
3190 GOTO 3330
3250 IF V-2*INT (V/2)=1 THEN LET S=1: LET F=1
3255 GOTO 3330
3260 IF INT (V/4)-2*INT (V/8)=1 THEN LET S=2: GOTO 3330
3270 IF INT (V/2)-2*INT (V/4)=1 THEN LET S=3
3280 GOTO 3330
3290 IF INT (V/8)=1 THEN LET S=1: LET F=1: GOTO 3330
3300 IF V-2*INT (V/2)=1 THEN LET S=0
3330 IF S<>W THEN LET C=1
3340 RETURN
3400 LET C=0: LET F=0: LET E=3
3410 IF H<0 OR H>15 OR H<>INT (H) THEN RETURN
3420 IF P<0 OR P>15 OR P<>INT (P) THEN RETURN
3430 LET V=0: LET U=1
3440 FOR I=1 TO 4
3450 IF INT (H/U)-2*INT (H/(2*U))=1 AND INT (P/U)-2*INT (P/(2*U))=0 THEN LET V=V+U
3460 LET U=U*2: NEXT I
3470 LET P=H: GOSUB 3100: RETURNUse the results in a game
Only reset the score, lives, enemies and map when F=1. A pause/resume transition returns C=1 but F=0. Use S to choose your current update and drawing code. The supplied caller increments a small score while playing and records what happens as buttons are held, released, paused and restarted. Its data table and mailbox are diagnostic code; your game needs the helper and its own input/update calls.
The caller owns X (score), L (initial lives), N (number of fresh sessions) and its surrounding J/K loops. The helper does not own those values. The transport’s B/Z/G, A/D result digits and RAM mailbox are also outside the game-state contract. A single shared set of globals supports one state machine; a second instance needs consistent renaming and renumbering of every reference.
Complete caller with retained state observations
10 PRINT CHR$(147)
20 LET B=49152: LET Z=0: LET G=165: POKE B+1000,0: POKE B+1001,90
30 LET X=0: LET N=0: LET L=3: GOSUB 3000: GOSUB 2000
40 FOR J=1 TO 70: READ S,V: GOSUB 3100: GOSUB 2000: NEXT J
50 GOSUB 3000: LET X=0: LET N=0: LET A=0: LET D=0
60 FOR J=1 TO 15: READ H
70 IF S=1 THEN LET X=X+1
80 GOSUB 3400
90 IF F=1 THEN LET X=0: LET N=N+1
100 IF J=9 AND X>0 THEN LET A=1
110 GOSUB 2000: NEXT J
120 IF N=2 THEN LET D=1
130 PRINT "FLOW RESULTS";A;D
140 POKE B+995,A: POKE B+996,D: POKE B+997,G: POKE B+998,Z-256*INT (Z/256): POKE B+999,INT (Z/256): POKE B+1000,239: STOP
2000 POKE B+Z,S: LET Z=Z+1
2010 POKE B+Z,V: LET Z=Z+1
2020 POKE B+Z,C: LET Z=Z+1
2030 POKE B+Z,F: LET Z=Z+1
2040 POKE B+Z,E: LET Z=Z+1
2050 POKE B+Z,X: LET Z=Z+1
2060 POKE B+Z,P: LET Z=Z+1
2070 POKE B+Z,L: LET Z=Z+1
2080 POKE B+Z,N: LET Z=Z+1
2090 RETURN
5000 DATA 0,0
5010 DATA 0,1
5020 DATA 0,2
5030 DATA 0,3
5040 DATA 0,4
5050 DATA 0,5
5060 DATA 0,6
5070 DATA 0,7
5080 DATA 0,8
5090 DATA 0,9
5100 DATA 0,10
5110 DATA 0,11
5120 DATA 0,12
5130 DATA 0,13
5140 DATA 0,14
5150 DATA 0,15
5160 DATA 1,0
5170 DATA 1,1
5180 DATA 1,2
5190 DATA 1,3
5200 DATA 1,4
5210 DATA 1,5
5220 DATA 1,6
5230 DATA 1,7
5240 DATA 1,8
5250 DATA 1,9
5260 DATA 1,10
5270 DATA 1,11
5280 DATA 1,12
5290 DATA 1,13
5300 DATA 1,14
5310 DATA 1,15
5320 DATA 2,0
5330 DATA 2,1
5340 DATA 2,2
5350 DATA 2,3
5360 DATA 2,4
5370 DATA 2,5
5380 DATA 2,6
5390 DATA 2,7
5400 DATA 2,8
5410 DATA 2,9
5420 DATA 2,10
5430 DATA 2,11
5440 DATA 2,12
5450 DATA 2,13
5460 DATA 2,14
5470 DATA 2,15
5480 DATA 3,0
5490 DATA 3,1
5500 DATA 3,2
5510 DATA 3,3
5520 DATA 3,4
5530 DATA 3,5
5540 DATA 3,6
5550 DATA 3,7
5560 DATA 3,8
5570 DATA 3,9
5580 DATA 3,10
5590 DATA 3,11
5600 DATA 3,12
5610 DATA 3,13
5620 DATA 3,14
5630 DATA 3,15
5640 DATA 4,0
5650 DATA 255,0
5660 DATA 0,16
5670 DATA 1,128
5680 DATA 2,255
5690 DATA 3,16
5700 DATA 0,3,3,0,0,3,3,0,3,0,4,0,8,8,0Customise it
The custom caller starts with five lives rather than three. That is a caller policy; the state machine never assumes a health value. To add an options or level-complete state, extend validation and the transition branches together, decide its outgoing requests and add cases to the policy model. Expanding the event mask also requires changing the four-bit edge loop and its validation. Reusing a bit with a new meaning in a different state is possible, but document it at the call site.
For a game with many named states or data-driven transitions, a table can be easier to maintain. For two screens, a couple of explicit branches may be clearer. This helper earns its place when pause, fresh starts and simultaneous requests otherwise become entangled.
A mistake you can recognise
The wrong caller resets its score whenever it enters playing, including resume. It also counts that resume as a new session. Its two saved checks become 0, 0: the resumed score vanished, and there were three resets instead of two. A later restart still works, so checking only the final state would miss the error.
The repair resets caller-owned progress only on F=1. Its source and actual loaded tokenised program are identical to the working caller. The capture below comes from running the mistake, not drawing a mock error screen.

Why the bits and states help
A state is a compact answer to “what is the game doing now?” An event is a request, not a second state. Edge detection keeps bits present in H but absent in P. Here it tests one binary place at a time using division and INT, so Spectrum BASIC’s logical AND is never mistaken for a bitwise AND.
The Craft is a useful home for deciding what pause should freeze. Maths for Games develops the binary representation and discrete-state reasoning underneath this implementation.
Machine and cost
C64 BASIC V2 uses only the first two significant characters of a numeric variable name. The single-letter names are deliberate: check your project for collisions before pasting them. The diagnostic mailbox at $C000 sits outside the normal BASIC heap.
The helper occupies 712 tokenised program bytes on this interpreter, including numbered-line headers. This excludes the caller, tables, numeric variables/arrays, interpreter stacks and ROM. It is not a machine-code size or cycle count. Flow selection uses a bounded set of branches; the held-mask path examines four bit positions on every call. BASIC arithmetic and line lookup add interpreter cost. Call once per intended input/update step; the helper supplies no real-time clock.
Verified examples
All four working/custom/wrong/repaired callers ran through C64’s stock BASIC interpreter under Emu198x 0.44.2. Build198x 0.2.9 tokenises C64 PRG and Spectrum TAP files; C64 enters stock RUN, Spectrum uses ROM tape loading, and BBC enters the complete numbered listing through stock MOS/BASIC. Actual loaded programs, complete state traces, positive completion, guards and visible result pixels are checked. Firmware is not distributed.
The independent policy-table model checks all 64 state/event combinations, six invalid cases and a 15-update held-input session. Custom callers change initial lives. These are configuration-specific observations, not a claim about every physical machine or possible game.
Public CI checks retained records and rejects altered source, configuration, loaded program, state, completion and image evidence; it does not execute restricted firmware. Refresh with python3 scripts/verify-basic-components.py game-flow commodore-64 --tools /path/to/extracted/releases; inspect with python3 scripts/check-basic-components.py game-flow.
Explore further
The BASIC object pool offers another caller-owned component. Compare the assembly version for its representation and cost choices. Exact source and native observations.