A takes a turn. Wait a while and nothing else happens. Press T, though, and waiting becomes a decision: leave it too long and the game takes the next turn for you. Another T gives you all the thinking time you need again.
This adds an optional deadline to the existing Blitz command rule. The rule stays unchanged. The important difference is around it: a small clock keeps ticking, and an Intuition window accepts a character without waiting for Return.

Put it in your project
Run make in the source folder. In a blank Ted source, choose Edit → Insert from disk and select build/working.asc. Compile/run with Right Amiga + X. A advances, T toggles timing and Q leaves. Timing starts disabled; 200 ticks is roughly four seconds in the checked PAL configuration.
These downloads are maintained ASCII. Blitz’s native INCLUDE requires tokenised source, so append these pieces in the editor or save tokenised modules first. Do not point INCLUDE at the raw ASCII files. Use a fresh blank listing for each complete variant, keeping its labels separate from earlier imports.
; Original Blitz BASIC 2.10, Amiga mode, PAL.
PL_count.l=0 : PL_step.l=1 : PL_ex.l=0
PL_dl_duration.l=200
Gosub PL_clock_init
PL_dl_now.l=PL_ticks.l : Gosub PL_dl_init
InitBank 0,40,1 : PL_box.l=Bank(0)
Poke.l PL_box.l+32,$1A2B3C4D
WbToScreen 0
Window 0,0,20,420,180,$1000,"Optional turn deadline",1,0
WLocate 8,60 : Print "Bank: ",PL_box.l
WLocate 8,80 : Print "Size: ",BankSize(0)
WLocate 8,100 : Print "0123456789"
Gosub PL_show
Repeat
VWait
Gosub PL_dl_service
Gosub PL_poll_key
PL_dirty.l=0
If PL_dl_expired.l
PL_ex.l=(PL_ex.l+1)&255
PL_key$="A" : Gosub PL_advance : Gosub PL_dl_start
PL_dirty.l=1
Else
If PL_key$="T"
If PL_dl_active.l
PL_dl_active.l=0 : PL_dl_left.l=0
Else
Gosub PL_dl_start
EndIf
PL_dirty.l=1
Else
Gosub PL_advance
If PL_accepted.l
If PL_dl_active.l Then Gosub PL_dl_start
PL_dirty.l=1
EndIf
EndIf
EndIf
If PL_dirty.l Then Gosub PL_show
Gosub PL_monitor
Until PL_key$="Q"
ClrInt 5
DefaultInput : DefaultOutput
Free Window 0
FreeBank 0
End
.PL_show
WLocate 8,20 : Print "Count: ",PL_count.l," "
WLocate 8,40 : Print "Timer: ",PL_dl_active.l," "
ReturnThe builder places the caller first, then appends routines.bb2, clock.bb2, input.bb2, monitor.bb2 and the existing command-loop routines.bb2. The last file supplies PL_advance unchanged. Put the routines after End, so execution cannot fall into a Return without a matching Gosub.
Blitz names are case-sensitive. Preserve PL_dl_init, PL_dl_now and the rest exactly, including the PL capitals. The editor’s command colouring does not make your own identifiers case-insensitive. The bank address, size and row of digits in the sample are optional verification aids, rather than a game interface you need to reproduce.
A deadline which knows nothing about the game
; Signed long integer deadline state; supply samples in 0..65535.
.PL_dl_init
PL_dl_previous.l=PL_dl_now.l
PL_dl_left.l=0 : PL_dl_active.l=0 : PL_dl_expired.l=0 : PL_dl_elapsed.l=0
Return
.PL_dl_start
PL_dl_left.l=PL_dl_duration.l : PL_dl_active.l=0 : PL_dl_expired.l=0
If PL_dl_duration.l>0
PL_dl_active.l=1
EndIf
Return
.PL_dl_poll
PL_dl_expired.l=0
PL_dl_elapsed.l=PL_dl_now.l-PL_dl_previous.l
If PL_dl_elapsed.l<0
PL_dl_elapsed.l=PL_dl_elapsed.l+65536
EndIf
PL_dl_previous.l=PL_dl_now.l
If PL_dl_active.l=0
Return
EndIf
PL_dl_left.l=PL_dl_left.l-PL_dl_elapsed.l
If PL_dl_left.l<=0
PL_dl_left.l=0 : PL_dl_active.l=0 : PL_dl_expired.l=1
EndIf
Return| Value | Contract |
|---|---|
PL_dl_duration.l |
Caller supplies whole ticks, 0..65535. Zero disables. |
PL_dl_now.l |
Caller supplies the current sample, 0..65535. |
PL_dl_previous.l |
Helper retains the previous sample. |
PL_dl_left.l |
Remaining ticks, clamped at zero. |
PL_dl_active.l |
Explicit 0 or 1 active state. |
PL_dl_expired.l |
Explicit 0 or 1 event for the latest poll. |
PL_dl_elapsed.l |
Elapsed ticks from that poll. |
Set duration and current time, then call PL_dl_init once. It begins disabled. PL_dl_start copies the duration into remaining time and enables a nonzero duration. It retains the latest baseline: sample/poll immediately before starting when that instant matters.
Supply a fresh sample and call PL_dl_poll every service iteration, even while disabled. Consume the expiry event before another poll or start clears it. Expiry clamps remaining time to zero and disables the helper. It does not move a monster, update a score or restart itself; those are the caller’s decisions.
The .l suffixes make these signed long integers. Keep those types, and reserve or consistently rename the whole state and label group. Blitz comparisons return -1 for true, while this helper deliberately exposes flags as 0 or 1. One group is one mutable context; multiple independent deadlines need separate state. This helper uses ordinary Gosub/Return calls, is not re-entrant, and owns no window, bank, interrupt handler or input queue.
A clock which continues while you think
; Own the Blitz type-5 handler set for this adapter's lifetime.
.PL_clock_init
PL_ticks.l=0
SetInt 5
PL_ticks.l=PL_ticks.l+1
If PL_ticks.l=65536 Then PL_ticks.l=0
End SetInt
Return
.PL_dl_service
PL_dl_now.l=PL_ticks.l
Gosub PL_dl_poll
ReturnPL_clock_init installs a short SetInt 5 handler. It changes only a numeric counter: one increment, a comparison and a reset at 65536. The main program reads that counter; it does not increment time once per game iteration. In the checked PAL workload the counter advances once per video field, nominally fifty ticks per second.
The caller’s VWait paces servicing. Drawing or other lengthy work can delay when it notices expiry, but the clock keeps measuring elapsed fields. Restarting from observation time introduces drift; carrying lateness or using a fixed-step accumulator is a different policy when the game needs one.
This adapter requires exclusive ownership of Blitz’s type-5 handler set. ClrInt 5 removes every Blitz handler of that type, rather than just this one. Coordinate setup and teardown in a larger game. The interrupt code uses no strings, Intuition, file I/O or blitter calls. Ordinary Amiga services remain running; the caller does not enter BLITZ mode.
The counter belongs to the adapter and starts at zero before installation. Main code only reads it. Its ordinary long load completes before an interrupt can run; this is a single-writer Amiga arrangement, rather than a general synchronisation API for other CPUs or writers.
Wrap is a small piece of useful maths
A sample of 65530 followed by 5 means eleven ticks passed. Subtraction initially gives -65525; adding 65536 recovers eleven. That is modular arithmetic, and a useful connection to Maths for Games.
The intermediate difference fits in a signed long throughout our bounds. Poll before a complete 65536-tick cycle passes: two low-word samples cannot reveal a missed whole cycle. At the checked PAL rate that limit is roughly 21 minutes 51 seconds. Zero duration disables; it does not stand for a whole cycle. Resetting the clock externally requires reinitialisation.
A delayed poll produces one event, rather than every turn you missed. That suits this caller. Choose a longer clock or catch-up policy explicitly when your game needs it; neither is hidden inside the helper.
Characters arrive without stopping time
; WindowInput must already select an owned, open window.
; Delivered characters may include operating-system key repeats.
.PL_poll_key
PL_key$=UCase$(Inkey$)
ReturnOpening the owned window selects WindowInput and WindowOutput. Inkey$ can then return a delivered character without waiting for Return. The earlier console caller uses a submitted line instead, so replacing its Edit$ with Inkey$ without changing the channel would not establish this behaviour.
This reader accepts delivered characters, which may include operating-system repeats. It does not promise one action per physical press or the AMOS reader’s release gate. Focus matters, and another input consumer cannot independently read the same queue. UCase$ accepts lower-case commands too.
Expiry wins when time and a command are serviced together. The caller discards that command, including T or Q. Otherwise A advances the existing bounded rule and restarts only an already enabled deadline. An untimed action stays untimed. T disables and clears remaining time, or starts a new full duration; it does not preserve a paused remainder. Count and expiry count wrap within 0..255.
Change the pressure, keep the helper
; Original Blitz BASIC 2.10, Amiga mode, PAL.
PL_count.l=0 : PL_step.l=1 : PL_ex.l=0
PL_dl_duration.l=100
Gosub PL_clock_init
PL_dl_now.l=PL_ticks.l : Gosub PL_dl_init
InitBank 0,40,1 : PL_box.l=Bank(0)
Poke.l PL_box.l+32,$1A2B3C4D
WbToScreen 0
Window 0,0,20,420,180,$1000,"Optional turn deadline",1,0
WLocate 8,60 : Print "Bank: ",PL_box.l
WLocate 8,80 : Print "Size: ",BankSize(0)
WLocate 8,100 : Print "0123456789"
Gosub PL_show
Repeat
VWait
Gosub PL_dl_service
Gosub PL_poll_key
PL_dirty.l=0
If PL_dl_expired.l
PL_ex.l=(PL_ex.l+1)&255
PL_key$="A" : Gosub PL_advance : Gosub PL_dl_start
PL_dirty.l=1
Else
If PL_key$="T"
If PL_dl_active.l
PL_dl_active.l=0 : PL_dl_left.l=0
Else
Gosub PL_dl_start
EndIf
PL_dirty.l=1
Else
Gosub PL_advance
If PL_accepted.l
If PL_dl_active.l Then Gosub PL_dl_start
PL_dirty.l=1
EndIf
EndIf
EndIf
If PL_dirty.l Then Gosub PL_show
Gosub PL_monitor
Until PL_key$="Q"
ClrInt 5
DefaultInput : DefaultOutput
Free Window 0
FreeBank 0
End
.PL_show
WLocate 8,20 : Print "Count: ",PL_count.l," "
WLocate 8,40 : Print "Timer: ",PL_dl_active.l," "
ReturnThis changes only 200 ticks to 100: roughly two seconds rather than four in the checked PAL workload. build/custom.asc uses the same helper and input policy, and its native expiry arrives after the shorter interval. No extra asset or state field is needed.
You could make a safe room untimed, choose a duration for each encounter, or let expiry happen just once. Those belong in the caller. Changes to numeric bounds, clock units or input arbitration need fresh checks of those contracts.
When a moving clock never spends any time
Deliberately broken service: this assignment replaces the history just before the helper measures it.
; Own the Blitz type-5 handler set for this adapter's lifetime.
.PL_clock_init
PL_ticks.l=0
SetInt 5
PL_ticks.l=PL_ticks.l+1
If PL_ticks.l=65536 Then PL_ticks.l=0
End SetInt
Return
.PL_dl_service
PL_dl_now.l=PL_ticks.l
PL_dl_previous.l=PL_dl_now.l
Gosub PL_dl_poll
ReturnPrevious time now always equals current time. Elapsed stays zero and remaining stays 200. A works, T enables timing, the actual clock advances, but no automatic turn arrives.

Restore clock.bb2 and import a fresh build/repaired.asc. Two actions followed by waiting give Count 3 with one expiry. The repaired caller was compiled and run separately.
The arithmetic fixture also tries <=0 changed to <0. At exact expiry, remaining time reaches zero but stays active until a later poll. The faulty variant completes all sixteen cases, yet fails three exact-expiry results against the independent oracle. Restoring <=0 fixes those boundaries. A compile error is not evidence of that intended runtime fault.
Resource ownership and costs
The caller owns window 0, its input/output redirection and the adapter’s Blitz type-5 handler set. It obtains screen handle 0 for the shared Workbench screen; it does not own that screen’s lifetime or palette. Choose free object numbers and coordinate them with other components.
Normal Q exit clears the owned handler set, restores DefaultInput/DefaultOutput, frees window 0 and frees diagnostic bank 0. An abnormal interruption needs a separate cleanup policy. The helper itself needs none of those resources.
The optional 40-byte observer
.PL_monitor
Poke.l PL_box.l+36,0
Poke.l PL_box.l,PL_count.l
Poke.l PL_box.l+4,PL_ex.l
Poke.l PL_box.l+8,PL_dl_active.l
Poke.l PL_box.l+12,PL_dl_left.l
Poke.l PL_box.l+16,PL_dl_now.l
Poke.l PL_box.l+20,PL_dl_duration.l
Poke.l PL_box.l+24,PL_dl_elapsed.l
Poke.l PL_box.l+36,239
ReturnThe caller requests bank 0 and discovers its address with Bank(0). Seven four-byte fields at offsets 0..24 hold turn, expiry count, active, remaining, clock, duration and elapsed. A guard is at offset 32; completion 239 at offset 36 marks a completed observation. This is a qualification aid, not an interrupt-safe communication API.
To omit it, remove the bank allocation/address/guard, Bank/Size/digit-display lines, Gosub PL_monitor, FreeBank 0 and the monitor module. Renumber allocation, address lookup, size lookup and free together when another module owns bank 0.
The seven helper long values hold 28 bytes of numeric payload; the adapter’s counter adds four. Code, call frames, compiler/runtime structures, strings and window resources cost more. Those overheads have not been isolated here, and ASCII file size does not measure executable size or establish a minimum-RAM Amiga.
The diagnostic caller separately requests a 40-byte bank; native BankSize(0) reports 40. The arithmetic fixture reports 272 for its own results bank. The normal observer writes seven four-byte fields and two four-byte completion values per poll: 36 explicit data bytes, or 1800 bytes at fifty polls per second. This is a store count, rather than measured bus bandwidth or frame time. Omit the diagnostics to save those stores.
What was checked
The working, shorter-duration, baseline-fault and repaired callers were imported and compiled separately in original Blitz BASIC 2.10 / Ted 1.1, using Emu198x 0.44.1 on a PAL A1200 with 2 MiB chip RAM, no fast RAM, the recorded Kickstart 3.1 identity and three floppy drives. The third drive holds the source-only fixture from boot; licensed runtime media and snapshots are not distributed.
Actual keys exercise untimed idle, accepted input, enable/progression, active restart, expiry without input, disable/idle and normal exit. Counter and active-state pixels agree with guarded bank observations. Two further imports exercise sixteen synthetic arithmetic cases and the strict-comparison control. These synthetic samples do not constitute a twenty-minute wall-clock rollover test.
The source and native record bind the tools, firmware, runtime and maintained programs. CI rechecks retained evidence and failing controls; it does not boot licensed software. This qualifies the named configuration, rather than original hardware, NTSC, AmiBlitz/PED or another compiler.
Both figures show the sample’s native output. Their exports remove only proven duplicate framebuffer columns, retaining every unique pixel. Captured with Emu198x 0.44.1 running Blitz BASIC 2.10.
For an untimed game, the console command loop remains simpler. Compare AMOS’s deadline and release-gated input and the broader loop choices when choosing the services your game needs. The original Blitz BASIC 2.1 manual covers identifier spelling, SetInt/ClrInt, input channels, window ownership and memory banks.