Traffic should continue while you decide what to do. Replace manual advancement with a shared update beat, and let holding a direction repeat the player’s movement.
Replace lines 180, 300, 340, 341, 370, 470.
Add lines 280, 290, 291, 292, 293, 294, 350, 360.
Delete lines 210, 240, 250, 480, 1095. Enter each deleted line number alone.
180 GO SUB 1000: GO SUB 3000: LET dx=0: LET dy=0: LET tick=PEEK 23672
280 LET elapsed=PEEK 23672 -tick: IF elapsed<0 THEN LET elapsed=elapsed+256
290 IF elapsed<32 THEN GO TO 200
291 LET k$=INKEY$
292 LET dx=0: LET dy=0
293 IF k$="q" OR k$="Q" THEN GO TO 9000
294 IF k$="r" OR k$="R" THEN GO TO 100
300 LET tick=PEEK 23672: LET nx=x+dx: LET ny=y+dy
340 IF nx=x AND ny=y THEN GO TO 350
341 GO SUB 3100: LET x=nx: LET y=ny: GO SUB 3000
350 LET t=t-1
360 IF t>0 THEN GO TO 440
370 LET t=v: LET p=p+d
470 LET dx=0: LET dy=0: GO TO 200RUN the changed program.
Complete source at this checkpoint
10 GO SUB 7000
100 LET p=10: LET d=-1: LET v=2: LET t=v: DIM a$(30): DIM b$(30)
110 LET x=7: LET y=2: LET steps=0
140 FOR j=0 TO 2: FOR k=0 TO 3
150 LET c=2 *p+10 *j+k: LET c=c-30 *INT (c/30)+1
160 LET a$(c)=CHR$ (144 +k): LET b$(c)=CHR$ (148 +k)
170 NEXT k: NEXT j
180 GO SUB 1000: GO SUB 3000: LET dx=0: LET dy=0: LET tick=PEEK 23672
200 LET k$=INKEY$
220 IF k$="q" OR k$="Q" THEN GO TO 9000
230 IF k$="r" OR k$="R" THEN GO TO 100
280 LET elapsed=PEEK 23672 -tick: IF elapsed<0 THEN LET elapsed=elapsed+256
290 IF elapsed<32 THEN GO TO 200
291 LET k$=INKEY$
292 LET dx=0: LET dy=0
293 IF k$="q" OR k$="Q" THEN GO TO 9000
294 IF k$="r" OR k$="R" THEN GO TO 100
295 IF k$="i" OR k$="I" THEN LET dy=-1
296 IF k$="k" OR k$="K" THEN LET dy=1
297 IF k$="j" OR k$="J" THEN LET dx=-1
298 IF k$="l" OR k$="L" THEN LET dx=1
300 LET tick=PEEK 23672: LET nx=x+dx: LET ny=y+dy
310 IF nx<0 OR nx>14 OR ny<0 OR ny>2 THEN LET nx=x: LET ny=y
340 IF nx=x AND ny=y THEN GO TO 350
341 GO SUB 3100: LET x=nx: LET y=ny: GO SUB 3000
350 LET t=t-1
360 IF t>0 THEN GO TO 440
370 LET t=v: LET p=p+d
380 IF p=15 THEN LET p=0
390 IF p=-1 THEN LET p=14
400 IF d=1 THEN LET a$=a$(29 TO 30)+a$(1 TO 28): LET b$=b$(29 TO 30)+b$(1 TO 28)
410 IF d=-1 THEN LET a$=a$(3 TO 30)+a$(1 TO 2): LET b$=b$(3 TO 30)+b$(1 TO 2)
420 GO SUB 1200: IF y=1 THEN GO SUB 3000
440 LET steps=steps+1
470 LET dx=0: LET dy=0: GO TO 200
1000 BORDER 0: PAPER 0: INK 7: CLS
1010 PRINT AT 0,1; INK 5;"QUICKSTEP";AT 0,21; INK 4;"EXIT ABOVE"
1030 FOR j=0 TO 2 STEP 2
1040 PRINT AT 2+2*j,1; PAPER 1; INK 5;s$;AT 3+2*j,1;s$
1060 NEXT j
1070 PRINT AT 2,15; PAPER 4; INK 7;" ";AT 3,15;" "
1080 GO SUB 1200: LET z$=">": IF d=-1 THEN LET z$="<"
1081 PRINT AT row,0; PAPER 0; INK colour;z$;AT row,31;z$
1090 PRINT AT 20,2; PAPER 0; INK 7;"I up J left K down L right"
1110 PRINT AT 21,2; INK 7;"R retry Q quit";
1120 RETURN
1200 LET row=4: LET colour=6: IF d=-1 THEN LET colour=5
1210 PRINT AT row,1; PAPER 0; INK colour;a$;AT row+1,1;b$
1240 RETURN
3000 LET shade=0: IF y<>1 THEN LET shade=1
3010 IF y=0 AND x=7 THEN LET shade=4
3020 PRINT AT 2+2*y,1+2*x; PAPER shade; INK 7;h$;AT 3+2*y,1+2*x;f$
3030 RETURN
3100 IF y=1 THEN GO SUB 1200: RETURN
3110 IF y=0 AND x=7 THEN PRINT AT 2,15; PAPER 4;" ";AT 3,15;" ": RETURN
3120 PRINT AT 2+2*y,1+2*x; PAPER 1; INK 5;g$;AT 3+2*y,1+2*x;g$: RETURN
7000 RESTORE 7200: FOR j=0 TO 103: READ n: POKE USR "a"+j,n: NEXT j
7010 LET h$=CHR$ 152 +CHR$ 153: LET f$=CHR$ 154 +CHR$ 155
7020 LET g$=CHR$ 156 +CHR$ 156: LET s$="": FOR j=1 TO 15: LET s$=s$+g$: NEXT j: RETURN
7200 DATA 0,0,63,127,120,120,120,120
7210 DATA 0,0,255,255,24,24,24,24
7220 DATA 0,0,255,255,24,24,24,24
7230 DATA 0,0,252,254,30,30,30,30
7240 DATA 127,127,127,127,63,7,7,0
7250 DATA 255,255,255,255,255,192,192,0
7260 DATA 255,255,255,255,255,3,3,0
7270 DATA 254,254,254,254,252,224,224,0
7280 DATA 0,7,15,13,15,7,3,31
7290 DATA 0,224,240,176,240,224,192,248
7300 DATA 63,55,7,7,6,6,14,0
7310 DATA 252,236,224,224,96,96,112,0
7320 DATA 0,0,0,0,0,16,0,0
9000 PAPER 0: INK 7: PRINT AT 21,0;"Finished. RUN to try again. ";: STOPSPACE no longer advances traffic. Leave the controls alone: the lane keeps moving. Hold J or L and the person steps repeatedly until the boundary. Contact is still unenforced, so you can inspect movement without losing.
A clock outside the loop
PEEK reads a byte from memory. Address 23672 is the lowest byte of the Spectrum’s FRAMES counter. On our PAL target, the counter advances every 20 milliseconds. We read it without changing it.
At initialisation, tick remembers the current byte. Line 280 subtracts tick from the current reading. Because this byte cycles through 0..255, a negative difference needs 256 added back. For example, a start value of 250 followed by 10 gives −240; adding 256 gives sixteen elapsed frames.
Line 290 waits until elapsed reaches 32. R and Q are still checked during that wait. At the update boundary, the program reads the movement key and resets dx/dy. Line 300 records the next starting time before doing the update’s drawing and traffic work.
One clock, two rates
The player gets one movement opportunity per update. Traffic uses a separate countdown t, initially v=2. Each update reduces t by one. A positive value skips traffic movement; zero reloads t from v and advances the lane.
| Update | t before decrement | Result |
|---|---|---|
| 1 | 2 | t becomes 1; traffic stays put |
| 2 | 1 | t becomes 0; reload 2 and move |
| 3 | 2 | t becomes 1; traffic stays put |
steps counts completed updates, not seconds. The v value counts updates too. Neither measures how long the BASIC work takes.
Does v=2 guarantee that traffic moves once every 64 video frames?
Show the explanation
No. Thirty-two frames is the target spacing between update starts. If an update’s work takes longer, the next opportunity arrives later. The lane moves every second update, whatever those updates cost.
Work can take longer than the target
We do not add a fresh 32-frame pause after drawing. Time spent updating already counts towards the next start. Nor do we run several catch-up moves after a slow update; the next update records a new tick and does one piece of work.
The low-byte subtraction assumes the loop returns before a whole 256-frame cycle passes. It is suitable for this bounded game loop, not a timer for arbitrarily long pauses. Shorter teaching programs and the eventual six-lane game need not feel equally fast.
Try a very short tap while a vehicle row is being redrawn. It may have ended before line 291 asks for a key. Holding a direction works because it remains present at that sample. We will address recognised short presses later; do not repair this stage by making it wait for key release again, because that would also stop traffic while waiting.
If nothing moves, check that lines 210 and 480 were deleted. If SPACE still controls traffic, remove 240. If the speed alternates unexpectedly, inspect the decrement, reload and skip at 350–370 before changing the frame target.
Sources
Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 18, chapter 25. Chapter 18 discusses keyboard input and timing; chapter 25 identifies FRAMES at 23672. The frame target and lane countdown are this program’s choices.
Run it here
This unit's program, built for you.
- Program
- quickstep.tap
- Machine
- ZX Spectrum 48K (PAL)