Skip to content

Advance a turn with a command in Blitz BASIC

Keep game state between decisions, accept only useful commands, and leave the clock out until the rules need one.

Press A twice and the counter reaches 2. Press X between those actions and it stays put. That tiny rule is enough to organise an adventure, a puzzle or a turn-based game: read a decision, decide whether it counts, change the game, then show the result.

This version uses Blitz BASIC 2.10 with Ted 1.1, in Amiga mode. It is an untimed command loop. Waiting longer does not take another turn. A turn deadline, independent animation or music servicing would need an additional clock or service path; this caller does not provide one.

Native Blitz BASIC program showing two accepted A commands with the counter at 2.
A, X, A: two accepted actions. The invalid command earns no turn.

Put it in your project

These are two pieces of one program, not two executables. Put the caller first and append the routines after End. The complete demo.bb2 in the source folder already does that. End prevents execution falling into a subroutine and reaching Return without a matching call.

caller.bb2
; Blitz BASIC 2.1 Amiga mode; append routines after End.
PL_count.l=0 : PL_step.l=1
Repeat
  NPrint "Count: ",PL_count.l
  Print "A advances, X ignored, Q quits: "
  PL_key$=UCase$(Edit$(1))
  Gosub PL_advance
Until PL_key$="Q"
End

Initialise the counter and step once, before Repeat. Read a command inside the loop. The caller decides that Q means quit; the state routine does not end your program for you.

The reusable routine

routines.bb2
; Integer globals: PL_count.l and PL_step.l in 0..255.
; PL_key$ is a delivered command; PL_accepted.l reports acceptance.
.PL_advance
PL_accepted.l=0
If PL_key$="A"
  PL_count.l=PL_count.l+PL_step.l
  If PL_count.l>=256 Then PL_count.l=PL_count.l-256
  PL_accepted.l=1
EndIf
Return

The state routine accepts an uppercase command in PL_key$. It returns PL_accepted.l as 1 for A and 0 for every other command. Trigger a move sound or award a turn only when that result is 1. Merely drawing the screen must not change the game.

The counter and step are integers from 0 to 255. An accepted action adds the step and wraps modulo 256. At the usual step of 1, 254 → 255 → 0. With both inputs bounded, their sum is at most 510, so one subtraction is enough. A larger step, a negative value or a different counter range needs revised arithmetic and fresh checks.

The counter, step, command and acceptance flag are caller-owned globals. Rename the whole group when another module uses the same names. This is one mutable state context; multiple players need separate state or an explicit context adaptation. The update routine does not draw, wait, allocate graphics, start audio or install interrupts.

A line is a command

Edit$(1) accepts at most one character and waits for Return. UCase$ normalises it. Enter A then Return to act; enter X or an empty line to leave the counter alone. This counts delivered command lines, not physical key-down transitions. Keyboard buffering and repeat belong to the console. It does not implement the AMOS reader’s release-and-fresh-press contract.

The .l suffix makes the counter, step and result signed long integers. A byte-sized replacement could wrap during the addition, before the explicit range test. Keep the type when copying the routine. This caller uses Amiga mode and the default console channels. Calling BLITZ changes which operating-system services are available; a bitmap/slice input path needs different setup and qualification.

The maintained .bb2 listings here are ASCII source, not tokenised editor saves. Enter the complete text in Ted. The checked original environment compiles and runs with right-Amiga+X. AmiBlitz/PED has different import formats, controls and requirements; this record does not qualify that route.

Make it your own

Change the initial step from 1 to 3. Two accepted commands now give 0 → 3 → 6; X between them still does nothing. The routine and state allocation stay the same, with no new asset. At 254, one step of 3 would wrap to 1; keep the same input bounds.

customise.bb2
; Blitz BASIC 2.1 Amiga mode; append routines after End.
PL_count.l=0 : PL_step.l=3
Repeat
  NPrint "Count: ",PL_count.l
  Print "A advances, X ignored, Q quits: "
  PL_key$=UCase$(Edit$(1))
  Gosub PL_advance
Until PL_key$="Q"
End

; Integer globals: PL_count.l and PL_step.l in 0..255.
; PL_key$ is a delivered command; PL_accepted.l reports acceptance.
.PL_advance
PL_accepted.l=0
If PL_key$="A"
  PL_count.l=PL_count.l+PL_step.l
  If PL_count.l>=256 Then PL_count.l=PL_count.l-256
  PL_accepted.l=1
EndIf
Return
The native custom caller shows 6 after two accepted commands with a step of 3.
The caller changes the rule; the reusable update routine stays the same.

When earlier turns disappear

Deliberately broken caller: this version resets the counter inside Repeat.

reset-each-command.bb2
; Blitz BASIC 2.1 Amiga mode; append routines after End.
PL_count.l=0 : PL_step.l=1
Repeat
  NPrint "Count: ",PL_count.l
  Print "A advances, X ignored, Q quits: "
  PL_count.l=0
  PL_key$=UCase$(Edit$(1))
  Gosub PL_advance
Until PL_key$="Q"
End

; Integer globals: PL_count.l and PL_step.l in 0..255.
; PL_key$ is a delivered command; PL_accepted.l reports acceptance.
.PL_advance
PL_accepted.l=0
If PL_key$="A"
  PL_count.l=PL_count.l+PL_step.l
  If PL_count.l>=256 Then PL_count.l=PL_count.l-256
  PL_accepted.l=1
EndIf
Return

A then A shows 1 then 1, instead of 1 then 2. The previous value was discarded before the next command. An ignored X can also expose the reset by showing 0. The input reader and addition are doing their jobs; the initialisation is in the wrong place.

The deliberately broken native caller shows 1 after its second accepted A command.
Two actions, but only the latest survives. Move initialisation before the loop.

Restore the working caller, leaving the routines alone. Check A, X, A again: 0 → 1 → 1 → 2. Both the faulty caller and that repaired path were executed; the pictures are native emulator captures.

Costs and limits

The state update costs one command comparison, then for A an addition, a range comparison and sometimes a subtraction. Those are operations, not measured CPU cycles. Screen/console work, strings and the resident interpreter or compiled runtime dominate the tiny state group. Source bytes do not measure executable size or total memory use.

This is useful when the world can wait for the player. For continuous movement, a deadline or presentation that continues during a decision, compare the loop choices and the Amiga assembly loop. The implementation must fit the services and hardware it owns.

What was checked

Native execution uses released Emu198x 0.44.1 on a stock PAL A1200 profile: 2 MiB chip RAM, no fast RAM, and the recorded Kickstart 3.1 identity. It qualifies this runtime and workload, not original hardware, an A500, NTSC, another ROM or another BASIC dialect. The original interpreter/compiler and operating-system console ran inside the emulator; no host BASIC interpreter substituted for them.

The retained source and execution record includes initial, accepted, ignored, repeated, wrapping, nonzero, custom, faulty and repaired cases. Counters and visible output were checked alongside actual keyboard input; transport completion alone is not a pass.

The original Blitz BASIC 2 manual describes Edit$, default console channels and Gosub/Return. The source record names the edition and sections.