Skip to content

Choose how your game advances

Choose what advances play, what keeps time and when the machine can show the result.

A game needs a way to accept actions, change its state and show the result. It may also need a clock. Those decisions belong together, but they are separate pieces you can combine.

What makes the next thing happen?

An untimed adventure can print a description, wait for a command, carry out that command and print the result. Nothing in its rules needs to change while you decide. Adding a frame wait would not make the game more correct.

A chess game can also wait for a move while a clock counts down. Accepting the move and running out of time are different events, either of which can end a turn. A turn-based game can certainly have a timer.

An action game might advance its simulation at a chosen rate while drawing and reading controls on their own schedules. A menu might wait for a new press while music continues playing.

Decision Useful choices What to keep separate
Advance play Command, move, turn, simulation step, event A display refresh need not advance the rules
Measure time No clock, elapsed ticks, countdown, scheduled event A deadline need not make the game real-time
Accept input Blocking command, held state, new press, buffered action Holding a button need not repeat an action
Show the result Redraw after a change, animate, queue display work A display boundary is not automatically the game clock

Start with what your game must do while the player thinks. A blocking reader is useful when nothing else needs servicing. If a countdown, animation or sound player must continue, service that work while waiting or use an interrupt with a clear ownership agreement.

Add a deadline to a turn

Keep the current turn and its remaining time in separate state. Accepting an action changes the turn; a timer updates the remaining time and requests a turn change when it expires. Reset the deadline when a new turn begins if that is the rule you want.

Decide what happens if an action arrives at the deadline. Process it before expiry, after expiry, or compare a recorded arrival time: each is a policy with consequences. Do not let the order of two unrelated routines accidentally decide who wins.

The frame-paced examples below show one useful arrangement. Their waits decide when another update may begin; they do not guarantee that the work will fit.

Try the same turns with and without a deadline

The Master System, Game Gear and SG-1000 callers demonstrate both choices. Press one button to commit a turn, and another to enable a deadline. The reusable turn routines do not read input, wait or draw; each machine’s caller connects those jobs.

Reuse a BASIC turn update

The Atom and CPC entries separate a callable rule update from the command reader and screen. Copy the numbered routine into a program, supply its state and inspect whether the command was accepted. Each includes a working step-size change and a diagnosed reset-inside-the-loop mistake. Their blocking callers suit untimed decisions; the rule routine itself does not require blocking input.

Three Atari consoles, three display obligations

A turn can wait for a new press on all three machines. The 2600 caller still produces every scanline; the 5200 caller polls ANTIC’s vertical counter; the 7800 caller accepts MARIA’s blanking intervals. Their callable turn-state routines are shared, while their reset, input conversion and display ownership remain explicit. Each includes a customisation, a held-input mistake and a measured display-service fault with its repair.

More ways to wait for a player

A BASIC adventure can let the firmware read a command and redraw after it changes the world. An Ace FORTH word can wait for a fresh key. A console may have to keep producing a picture while the player decides. Those are useful loops too, even when no countdown is running.

The code-sample inventory now covers thirty named machine configurations, with source, startup instructions, ownership notes and execution records. The MTX caller loads through BASIC; the Einstein caller boots from disk through MOS. Their turn state changes on a new press while the display keeps being serviced.

Each probe establishes its own small job. The records identify three required emulator fixes, and do not claim complete teaching tracks or every peripheral on those machines. Use the machine’s integration notes when adapting the code.

Six frame-paced configurations

Machine Boundary Owner When work runs late
C64 Fresh raster bit-8 transition Main polls; example takes over CIA/IRQs Wait for a new boundary; no replay
48K Spectrum ROM FRAMES advances ROM IM1 counts; main accepts a sample Delta records missed serviced interrupts; no automatic replay
A500 Amiga Fresh VERTB request Main owns the custom request latch Discard pending request and wait anew
NES NMI clock advances NMI announces; main updates Delta records missed announcements; no automatic replay
BBC Model B MOS display-sync wait MOS retains the IRQ, keyboard and display services Work takes longer; next update waits again
Acorn Electron Same MOS service-facing wait Electron OS retains its own IRQ/display services Same policy, separately measured workload

Fit a routine into the loop

Read a controller once and share that sample with movement and actions. Keep state changes in the update. Draw from the resulting state. A hardware sprite write, a software blit and a buffered PPU request have different costs and timing restrictions even when they serve the same job.

The examples show a complete caller and a small wait routine. Copy both while learning, then retain the machine setup and ownership agreements your project needs. Replacing a wait is not enough to port a game.

Choose a policy for missed time

These callers perform one update after each accepted boundary or clock sample. They do not rush through an unlimited backlog. A later game may use elapsed time, bounded catch-up, slower simulation than rendering, or deliberate slowdown. Choose that policy for the game, then measure its worst case on the target.

The byte-clock examples require fewer than 256 frames between samples. The polling examples do not count lost frames. A sound player, drawing routine or interrupt service must agree with those limits rather than silently taking ownership of the clock.

Read the evidence narrowly

A verified run establishes this source, startup path, configuration, input sequence and workload. It can expose useful emulator failures. It does not establish every display mode, peripheral, timing edge or game on that emulator.

More language-specific callers

Atari XL and ColecoVision callers

Amiga BASIC-family command loops

Both languages expose callable bounded state updates. Their command callers can wait; their deadline callers add clocks and state their input/expiry ordering.

Commodore commands, with firmware still running

  • C64 BASIC waits for a submitted line and calls a separate byte-range rule update.
  • PET BASIC qualifies that contract on the 40-column PET’s firmware and screen.
  • PET assembly reads delivered characters through GETIN, preserving the normal firmware services.
  • VIC-20 BASIC runs on the unexpanded PAL machine, with its own memory and display limits.
  • VIC-20 assembly shares a small rule implementation with the PET, while retaining its own load address and native execution record.

Each includes a step-size customisation and the independently observed reset-inside-the-loop fault and repair. Buffered characters, line submissions and physical press edges are different input contracts; choose one deliberately.

Dragon and Ace: different kinds of waiting

All four keep an untimed world waiting. Their memory, stack, input and return agreements differ; each has its own native execution record.

The Ace FORTH deadline adds a separate unsigned elapsed-clock helper and a nonblocking release gate. Its stock-memory cost, ROM-clock limits and stack effects are independently qualified; the original untimed word remains useful on its own.

Aquarius and MTX: the same rule, different ownership

  • Aquarius BASIC waits for GO and a submitted line.
  • Aquarius assembly reads physical keys and returns through the normal USR entry.
  • MTX BASIC lets the interpreter collect numeric commands and draw text through the VDP.
  • MTX assembly takes over until reset and services the display boundary while waiting for a fresh Space press.

Each exposes a separate bounded update. A visible colour change can demonstrate parity without displaying an exact count; a display-service counter can keep advancing without spending a game turn.

MSX and Game Boy: let the firmware help, or own the display

  • MSX BASIC waits for a submitted line and calls a separate byte-range rule update.
  • MSX Z80 assembly uses BIOS keyboard and video services, requires a fresh Space press and returns to BASIC.
  • Game Boy DMG assembly owns JOYP/LCD/VRAM and polls a fresh VBlank while its rule count waits for a new A press.

The BIOS-facing and direct-device callers have different memory, stack and interrupt agreements. The DMG service counter is evidence that the caller keeps running, not a rule that the world must advance on every frame. A timed turn would need a separate deadline policy in any of these languages.

More machines, familiar decisions

The Oric Atmos and SVI-328 callers use blocking BASIC commands. The ZX80 and ZX81 add submitted numbers in a 1 KiB environment, preserving their actual display behaviour.

The Sord M5 and Tatung Einstein service the display while rules wait for a new Space press. Their keyboard polarity, shared ports and startup paths differ: a monitor-started cartridge on the M5, a stock-MOS boot disk on Einstein. Each links to the reusable sampler and the effect’s service contract.

Add a deadline without blocking on a command

The Atom assembly deadline counts observed flyback edges; the CPC assembly deadline uses the firmware’s 300 Hz elapsed clock. Both keep the reusable deadline state separate from input and turn progression, show a shorter-duration customisation, and demonstrate the mistake of ignoring expiry when no command arrives. Their different clock, input and ownership limits matter when adapting them.

Optional deadlines in BASIC

Both leave untimed play valid, define expiry/action ordering and demonstrate the baseline-reset fault. Character delivery is not a physical press edge. A native clock may continue while the interpreter is busy, but the game notices expiry only when its caller polls again.

MOS deadlines with physical key edges

The BBC Micro assembly deadline and Electron assembly deadline reuse the same 6502 helper and MOS clock adapter, with separate native qualification. They sample physical key edges and preserve firmware clock ownership. The common interface does not make their interrupt sources, display contention or workload costs identical.

Electron and CPC BASIC deadlines

The Electron BASIC deadline reuses the BBC BASIC implementation with its own native qualification. The CPC BASIC deadline uses explicit real state and a separate 300 Hz clock adapter. The same policy crosses languages only when their numeric ranges, clock units and input behaviour are respected.

Commodore assembly deadlines

The C64 and unexpanded VIC-20 share a countdown and KERNAL clock/input adapters, with distinct memory layouts and native qualification. Their daily clock reset needs different treatment from an ordinary binary wrap. Delivered keyboard characters, physical press edges and display pacing remain separate choices.

PET clock ownership

The PET assembly deadline keeps the shared countdown and policy, but snapshots the upgrade-ROM PET’s own clock bytes. Its named default configuration counts one service tick per roughly 50 Hz field. A compatible CPU does not establish a compatible firmware API or seconds-per-tick conversion.

PET and VIC-20 BASIC deadlines

The PET BASIC caller and stock VIC-20 BASIC caller reuse the full-clock BASIC helper with separate native memory and timing records. Their optional observers explicitly borrow inactive cassette buffers. BASIC and assembly remain different implementations with different state, cost and integration contracts.

Dragon BASIC: optional firmware-clock deadlines

Dragon 32 BASIC deadlines replace blocking INPUT with INKEY$ when a turn is timed. TIMER supplies a 16-bit clock; the helper handles wrap while the caller decides what expiry means. The untimed command loop stays useful in its own right.

Dragon 32 assembly deadlines sample the same ROM clock through a small 6809 adapter. The reusable helper accepts X=context, while its caller owns input, turn arbitration and drawing. Its explicit twelve-byte context is different from BASIC’s named numeric state.

Ace assembly: wait for the interpreter, then service the clock

Ace Z80 deadlines reuse the elapsed-time helper with a stock FORTH clock adapter and sampled release gate. The generated C, loader waits for actual compilation progress; keyboard injection alone does not prove that an executable is ready. The FORTH implementation stays a separate language route.

MSX assembly: retain BIOS ownership

MSX BIOS-clock deadlines read JIFFY while using buffered keyboard services and BIOS VRAM copying. The shared countdown does not erase those hardware/firmware differences: retain the normal slots and ISR, choose the right tick-to-seconds conversion and keep the game policy explicit.

MSX BASIC deadlines use TIME and INKEY$ with the maintained BASIC countdown. The native interpreter allocates double-precision numeric state by default; its measured storage contract is separate from the Z80 helper’s ten-byte context. Both routes keep untimed play valid.

Spectrum: the same clock, different language contracts

The Z80 deadline reads FRAMES in one coherent word instruction. The BASIC deadline retries a high/low/high PEEK sequence and uses its own numeric storage and LET syntax. Both keep untimed play valid and gate sampled commands on release; neither turns a clock sample into a frame wait.

Atari XL: VBI-owned clock, BASIC-owned workspace

The 6502 deadline caller reads RTCLOK coherently while normal NMI service runs. Its complete USR example exceeds page six: a checked high-RAM layout leaves a display-clear/scroll margin. The shared countdown still owns no display, input or interrupt handler.

The Atari BASIC version keeps the same low-word contract with a retrying PEEK reader and an independent keyboard-entered program. Interpretation, input and optional monitoring consume real service time; the reusable helper does not require assembly.