Skip to content

Steer and thrust together

We will inspect the keys separately before changing the flight loop.

  • Sinclair ZX Spectrum
  • BASIC

Continue with Reach a landing pad. The world already updates both coordinates. The remaining restriction is how we read the keyboard. We will inspect the keys separately before changing the flight loop.

Look at the controls without flying

Save the landing program. Enter NEW and run this separate diagnostic. It prints three values; 1 means the named control is pressed and 0 means it is released.

diagnostic.bas
  10 BORDER 0: PAPER 0: INK 7: CLS
  20 PRINT AT 0,1;"O LEFT  P RIGHT  SPACE THRUST"
  30 PRINT AT 2,1;"Hold combinations. Q quits."
 100 IF INKEY$="q" THEN GO TO 200
 110 LET keys=IN 57342
 120 LET right=1-(keys-2*INT (keys/2))
 130 LET left=1-(INT (keys/2)-2*INT (keys/4))
 140 LET keys=IN 32766
 150 LET thrust=1-(keys-2*INT (keys/2))
 160 PRINT AT 5,1;"LEFT: ";left;AT 7,1;"RIGHT: ";right;AT 9,1;"THRUST: ";thrust
 170 GO TO 100
 200 IF INKEY$<>"" THEN GO TO 200
 210 STOP

The Spectrum groups its keyboard into half-rows of five keys. IN reads an input port: its number selects hardware, and the returned number reports what that hardware is doing. These are two different numbers with different jobs.

Port address Selected half-row Bits used here
57342 P, O, I, U, Y Bit 0 for P; bit 1 for O
32766 Space, Symbol Shift, M, N, B Bit 0 for Space

A byte has eight bits, with weights 1, 2, 4, 8, 16, 32, 64 and 128. The port number’s low byte is 254. Its high byte selects a half-row: 223 for the P row, or 127 for the Space row. Thus 254+256*223 gives 57342 and 254+256*127 gives 32766. In binary those high bytes are 11011111 and 01111111: each has a different selection bit cleared. These addresses come from the Spectrum’s wiring, not a search for convenient magic numbers.

The returned byte uses zero for a pressed key. A selected half-row’s other keys and returned bits must not be mistaken for our controls. In particular, the returned EAR input bit can vary, so equality with one whole-byte value is not a reliable key test.

Extract the bit we need

keys-2*INT (keys/2) removes all complete groups of two, leaving a remainder of 0 or 1. That is the lowest bit. For a numerical example, 190 divides into 95 complete pairs and remainder 0; 191 has the same 95 pairs and remainder 1. Those values illustrate the arithmetic, not a promise of a fixed keyboard byte.

To inspect the next bit, first discard the lowest one with INT (keys/2). Then remove its complete pairs. Written together, that is INT (keys/2)-2*INT (keys/4). The diagnostic subtracts each extracted bit from 1 so its displayed values mean pressed=1 rather than pressed=0.

O/P low two bits, high bit first O P
11 released released
10 released pressed
01 pressed released
00 pressed pressed

Hold each key, then pairs, then all three controls. Left and right should both display 1 when O and P are held. Space should change thrust independently. Release everything and check that all three values return to zero. Q alone quits after release.

Why can’t we use AND 1 as a shortcut for extracting the lowest bit?

Show the explanation

Sinclair BASIC’s AND combines logical conditions; it is not the bit-masking operation used by some other languages. The grouping and remainder expressions here extract the actual bits we want.

Return to the landing game

Reload the complete landing program from the previous lesson. Do not apply these edits to the diagnostic.

Replace lines 70, 210, 220, 230, 260, 270.

step-01.bas
  70 PRINT AT 8,2;"SPACE: thrust (with steering)"
 210 LET keys=IN 57342: LET nx=x
 220 IF INT (keys/2)-2*INT (keys/4)=0 THEN LET nx=nx-1
 230 IF keys-2*INT (keys/2)=0 THEN LET nx=nx+1
 260 LET burn=0: LET keys=IN 32766
 270 IF keys-2*INT (keys/2)=0 AND fuel>0 THEN LET burn=1: LET fuel=fuel-1

The two steering tests now inspect separate bits. If both O and P are down, one subtracts from nx and the other adds to it, so they cancel before the boundary clamps. A separate IN reads Space. Fuel, burn, velocity and contact keep their existing rules.

The game tests for a zero bit directly, whereas the diagnostic converts it to 1 for its display. Those conventions describe the same pressed state. We retain INKEY$ for Q and the single-choice menus; it remains useful where we want one answer.

Try steering while holding thrust, and releasing just one of those keys. The remaining control should keep working. We have changed the input reader, not the acceleration or pad. Compare that responsiveness with the previous version before altering any flight settings.

Complete program for this step
step-01.bas
  10 BORDER 0: PAPER 0: INK 7: BRIGHT 1: CLS
  50 PRINT AT 3,9;"TOUCHDOWN"
  60 PRINT AT 6,2;"O / P: steer left / right"
  70 PRINT AT 8,2;"SPACE: thrust (with steering)"
  80 PRINT AT 10,2;"Pad: ========  Safe: 0 to 12"
  90 PRINT AT 12,2;"Empty tank? You still coast."
 100 PRINT AT 15,2;"S launches. Q quits."
 110 GO SUB 800: IF k$="q" THEN GO TO 850
 120 CLS : GO SUB 600
 130 LET x=18: LET y=400: LET v=0: LET fuel=45: LET r=4: LET burn=0
 140 PRINT AT r,x;"A"
 150 PRINT AT 0,1;"FUEL       SPEED       MAX 12"
 160 PRINT AT 1,1;"O/P STEER  SPACE THRUST  Q QUIT"
 200 LET k$=INKEY$: IF k$="q" THEN GO TO 850
 210 LET keys=IN 57342: LET nx=x
 220 IF INT (keys/2)-2*INT (keys/4)=0 THEN LET nx=nx-1
 230 IF keys-2*INT (keys/2)=0 THEN LET nx=nx+1
 240 IF nx<1 THEN LET nx=1
 250 IF nx>30 THEN LET nx=30
 260 LET burn=0: LET keys=IN 32766
 270 IF keys-2*INT (keys/2)=0 AND fuel>0 THEN LET burn=1: LET fuel=fuel-1
 280 LET v=v+2-6*burn
 290 IF v>60 THEN LET v=60
 300 IF v<-30 THEN LET v=-30
 310 LET ny=y+v: LET contact=0: LET limit=1900
 330 IF ny>=limit THEN LET contact=1
 340 IF contact=1 THEN LET ny=limit
 350 IF ny<300 THEN LET ny=300: LET v=0
 360 LET nr=INT (ny/100)
 370 PRINT AT 0,6;fuel;"  ";AT 0,19;v;"   "
 375 LET a$="A": IF burn=1 THEN LET a$="*"
 380 PRINT AT r,x;" ";AT nr,nx;a$
 390 LET x=nx: LET y=ny: LET r=nr
 400 IF contact=1 THEN GO TO 700
 410 PAUSE 2: GO TO 200
 600 FOR c=0 TO 31
 610 FOR j=20 TO 21: PRINT PAPER 4; INK 0;AT j,c;" ";: NEXT j
 620 PRINT PAPER 4; INK 0;AT 20,c;"#"
 630 NEXT c
 640 FOR c=20 TO 27: PRINT PAPER 6; INK 0;AT 20,c;"=": NEXT c
 650 RETURN
 700 PRINT AT r,x;"A"
 710 LET m$="Off the pad."
 720 IF x>=20 AND x<=27 THEN LET m$="Too fast for the pad."
 730 IF x>=20 AND x<=27 AND v<=12 AND v>=0 THEN LET m$="Safe landing!"
 740 PRINT AT 2,1;m$;"    "
 770 PRINT AT 1,1;"R retries. Q quits.            "
 780 IF INKEY$<>"" THEN GO TO 780
 790 LET k$=INKEY$: IF k$<>"r" AND k$<>"q" THEN GO TO 790
 795 IF INKEY$<>"" THEN GO TO 795
 796 IF k$="q" THEN GO TO 850
 797 GO TO 120
 800 IF INKEY$<>"" THEN GO TO 800
 810 LET k$=INKEY$: IF k$<>"s" AND k$<>"q" THEN GO TO 810
 820 IF INKEY$<>"" THEN GO TO 820
 830 RETURN
 850 IF INKEY$<>"" THEN GO TO 850
 860 PAPER 0: INK 7: CLS : PRINT "Finished.": STOP

Sources

Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 9 (INT), chapter 13 (logical AND), and chapter 23 (keyboard half-rows, ports and returned bits).

Run it here

This unit's program, built for you.

Program
step-01.tap
Machine
ZX Spectrum 48K (PAL)

Download step-01.tap