One press, one step
Wait for release, add a quit key and save the finished movement experiment.
Holding a key is useful for travelling across a screen. Choosing one menu item or moving one board square may feel better with one action per press. We’ll keep the held-key version for comparison and make a second version that waits for release.
Save the program from Let the player steer as held before editing.
Use the tape-saving workflow
to keep an exported copy, not just a program name in memory.
Wait for the next press
Add 51, 59, 71, 72, 160, 170 and 200. Replace 90, 95 and 100, and delete 140 and 150. Here is the whole program:
10 PAPER 0
20 INK 7
30 BORDER 0
40 CLS
50 PRINT "o left p right"
51 PRINT "One press, one step. q quits."
55 LET row=10
56 LET col=15
59 IF INKEY$<>"" THEN GO TO 59
60 PRINT AT row,col;"O"
70 LET k$=INKEY$
71 IF k$="" THEN GO TO 70
72 IF k$="q" THEN GO TO 200
75 LET nextcol=col
80 IF k$="o" THEN LET nextcol=col-1
85 IF k$="p" THEN LET nextcol=col+1
90 IF nextcol<1 THEN GO TO 160
95 IF nextcol>30 THEN GO TO 160
100 IF nextcol=col THEN GO TO 160
110 PRINT AT row,col;" "
120 LET col=nextcol
130 PRINT AT row,col;"O"
160 IF INKEY$<>"" THEN GO TO 160
170 GO TO 70
200 STOP
Line 59 waits until INKEY$ is empty. This stops a key still held from launching
the program being treated as the first move. The marker appears when the keys
are released. Enter INKEY$ using Shift+left Option/Alt, release the chord,
then N. Enter the single not-equal token <> with left Option/Alt+W.
Line 71 returns to reading when no key was captured. Only a non-empty reading continues to the movement or quit decisions. This matters: if we entered the release loop while idle, a newly arriving press could be discarded before its action was considered.
The movement decisions retain their order. Their exits now reach line 160, which keeps reading until the key is released. Line 170 then returns to the main input check. Holding p gives one step; releasing and pressing p again gives a second step.
Rejected moves also wait for release. At an edge, one press remains one attempt, rather than a stream of attempts while the same key is held. The empty-input case returns at line 71, before the release loop.
Line 72 recognises lower-case q and goes to our deliberate STOP at 200. It
sits before the movement tests, so q does not need to be interpreted as a move.
If a direction is still held, release it before pressing q: the release loop
waits for the current input to end. Shift+Space remains the emergency BREAK
chord if you need to stop a mistyped program.
Give the ending a place
Replace line 200 and add 210:
10 PAPER 0
20 INK 7
30 BORDER 0
40 CLS
50 PRINT "o left p right"
51 PRINT "One press, one step. q quits."
55 LET row=10
56 LET col=15
59 IF INKEY$<>"" THEN GO TO 59
60 PRINT AT row,col;"O"
70 LET k$=INKEY$
71 IF k$="" THEN GO TO 70
72 IF k$="q" THEN GO TO 200
75 LET nextcol=col
80 IF k$="o" THEN LET nextcol=col-1
85 IF k$="p" THEN LET nextcol=col+1
90 IF nextcol<1 THEN GO TO 160
95 IF nextcol>30 THEN GO TO 160
100 IF nextcol=col THEN GO TO 160
110 PRINT AT row,col;" "
120 LET col=nextcol
130 PRINT AT row,col;"O"
160 IF INKEY$<>"" THEN GO TO 160
170 GO TO 70
200 PRINT AT 20,0;"Finished. RUN to try again."
210 STOP
After q, line 200 puts the message on row 20, away from the controls and marker.
Line 210 stops. The marker remains where the player left it, and RUN offers
another attempt. This is an experiment with a settled ending, not a game that
needs a score or a win condition.

Test a long held press, release and press again, then try an unrelated key. Move to both boundaries. Try q after a move and while no direction is held. The stored position and the drawn marker should agree in every case.
Choose the behaviour for the job
Compare the saved held version with this one. Which would you choose for a
character walking along a corridor? Which for choosing a square on a board?
Neither response is universally right: travel often benefits from repetition;
a single selection often needs a fresh press.
Could an enemy keep moving while this release loop waits?
Not if its update were elsewhere in this program. While line 160 loops, those other instructions do not run. A continuously moving game would keep updating the world and remember whether each key was down on the previous pass, instead of stopping everything to wait for release.
That distinction matters on modern machines too: input handling should support the player’s task without preventing other necessary work. The details of their input systems differ from this Spectrum polling loop.
Save this version as marker, export the tape, reset or reopen the emulator,
and load it again. Check one move and q. A successful save message is useful;
loading and using the result checks that you kept the program you intended.
What we can now make
We have written a story toy, a guessing game, a playful oracle and a movement experiment. Each grows from instructions we can inspect: values are stored, conditions choose a route, loops repeat work, and routines give a repeated job one home. Printing and sound make that work perceptible.
Pick one small change and predict its effect before editing: a story prompt, a clue’s wording, an oracle reply, or the movement margin. Check the unchanged behaviour as well as the change. For a margin, that means testing both edges; for a reply, it means deliberately selecting that reply.
Bright Spark brings these ideas into a memory game. Foundations offers pseudocode views of the same concepts; The Craft explores how rules and feedback shape play. They are useful routes for curiosity, not attendance requirements. Keep the programs you have made. They are small enough to understand and substantial enough to change on purpose.
Sources
Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 18, “Motion”, pp. 129–131; appendix C, INKEY$, STOP and PRINT AT.