Enter the generated BASIC loader and RUN it. A digit changes on the screen as the update counter advances. Hold Space to give the CPU too much work for one display frame; release it and the normal pace returns.
Use the wait in your project
Call wait_frame once per update. It invokes OSBYTE 19 through the documented entry point. A becomes 19; X/Y/flags are not preserved by this wrapper. The caller uses OSBYTE 129 to scan Space and OSWRCH to update a text position. That leaves hardware ownership with MOS, unlike the C64 and Amiga takeover examples.
; BBC Model B MOS service. Keep the MOS IRQ path and interrupts enabled.
; OSBYTE &13 waits for the next display sync. A becomes &13; X/Y/flags change.
; ROM entry point, not a replacement IRQ handler. Reuses the system's clock.
OSBYTE = $fff4
wait_frame:
lda #19
jmp OSBYTEStart the complete example
The tested Model B has MOS 1.20 and BASIC II. The loader selects MODE 6 and reserves memory above HIMEM=&3000 for the assembled program. Its DATA statements install our own bytes at &3000; no ROM is supplied. Keep MOS interrupts and the IRQ service intact. Reset to leave this bounded demonstration.
With Asm198x available, keep the sample folders together and run:
make -C bbc-micro/patterns/assembly/framework/game-loopThe Amiga Makefile also needs Build198x. The BBC Makefile produces both the binary and a numbered BASIC loader; enter build/loader.bas and RUN it.
The loop keeps four jobs visible: wait, read input, update state, draw. expensive_work is an optional CPU workload to expose missed deadlines; remove it when building your own game. The changing colour or digit is a small observable result, not a rendering engine.
; BBC Model B, MOS 1.20/BASIC II. Enter generated loader.bas, RUN.
; Space holds slow CPU work. MOS services own IRQ/video/keyboard. Reset to leave.
*=$3000
OSWRCH=$ffee
start:
cli
lda #0
sta updates
sta updates+1
sta slow
main_loop:
jsr wait_frame
jsr read_input
jsr update_game
jsr draw_screen
jsr expensive_work
frame_done:
jmp main_loop
read_input:
lda #129
ldx #$9d ; INKEY(-99): Space
ldy #$ff
jsr OSBYTE
stx slow ; $ff held, 0 released
rts
update_game:
inc updates
bne update_done
inc updates+1
update_done:
rts
draw_screen:
lda #31 ; VDU 31: fixed text position, then a changing digit
jsr OSWRCH
lda #10
jsr OSWRCH
lda #8
jsr OSWRCH
lda updates
lsr
lsr
lsr
and #7
clc
adc #48
jmp OSWRCH
expensive_work:
lda slow
beq work_done
ldy #64
work_outer:
ldx #0
work_inner:
dex
bne work_inner
dey
bne work_outer
work_done:
rts
updates: !word 0
slow: !byte 0
!source "frame.inc"What happens when work runs late
Waiting does not make the game work faster. When work exceeds the display interval, update frequency falls. The helper does not supply an elapsed-frame count or replay missed updates. A replacement IRQ handler that stops MOS processing display sync can break the wait. The Electron example executes this same service-facing source with its own OS/BASIC and workload record.
Check this combination
Code status: assembled and emulator-executed. BBC Model B, pinned MOS/BASIC. The recorded checks identify sources, firmware and observer. In three 30-frame windows the caller made 30 normal, 10 deliberately slowed and 30 recovered updates. An unpaced negative control is rejected by the same rate guard. This is configuration-specific emulator evidence, not an original-hardware or whole-emulator claim.
The loop comparison puts the ownership choices side by side. Each implementation keeps its own timing and resource contract.
The original BBC Micro Advanced User Guide, printed page 135, documents OSBYTE 19 and its dependence on IRQ1 handling; page 153 covers keyboard scanning with OSBYTE 129.