Load the NTSC NROM cartridge. The background colour advances with the update counter. Hold controller 1 A to add excessive CPU work; release it to restore the normal update pace.
Use the wait in your project
Call initialise_clock before enabling NMI, then wait_frame once per update. A/X/flags change. NMI owns frame_clock; main owns last_frame and frame_delta. The ISR changes no general register and RTI restores flags. The helper accepts a single captured clock value, preventing one frame from being consumed repeatedly.
; NMI owns frame_clock; main owns last_frame/frame_delta.
; Call initialise_clock before enabling NMI. NMI handler must preserve registers.
; A/X/flags change. NMI must remain enabled. No other reader consumes this state.
; Work must take fewer than 256 frames between accepted samples.
.segment "CODE"
initialise_clock:
lda frame_clock
sta last_frame
rts
wait_frame:
lda frame_clock
cmp last_frame
beq wait_frame
tax
sec
sbc last_frame
sta frame_delta
stx last_frame
rts
.segment "BSS"
frame_clock: .res 1
last_frame: .res 1
frame_delta: .res 1Start the complete example
Reset disables rendering, DMC, APU channels and the frame IRQ, clears writable RAM and waits through PPU warm-up. It initialises the clock before enabling NMI. NMI only increments frame_clock: it does not read controls, update the game or draw. Reset to leave.
With Asm198x available, keep the sample folders together and run:
make -C nintendo-entertainment-system/patterns/assembly/framework/game-loopThe Amiga Makefile also needs Build198x. Open the generated file under build using the machine’s normal loading route.
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.
; NTSC NROM. A holds excessive CPU work. NMI only increments the frame clock.
; No rendering/DMC; background palette changes show updates. Reset to leave.
.segment "HEADER"
.byte "NES",$1a
.byte 2,1,0,0
.byte 0,0,0,0,0,0,0,0
.segment "BSS"
updates: .res 2
slow: .res 1
.segment "CODE"
reset:
sei
cld
ldx #$ff
txs
inx
stx $2000
stx $2001
stx $4010
stx $4015
lda #$40
sta $4017
lda #0
clear_ram:
sta $0000,x
sta $0100,x
sta $0200,x
sta $0300,x
sta $0400,x
sta $0500,x
sta $0600,x
sta $0700,x
inx
bne clear_ram
warmup_1:
bit $2002
bpl warmup_1
warmup_2:
bit $2002
bpl warmup_2
jsr initialise_clock
lda #$80
sta $2000
main_loop:
jsr wait_frame
jsr read_controls
lda controls
and #$80
sta slow
jsr update_game
jsr draw_screen
jsr expensive_work
frame_done:
jmp main_loop
update_game:
inc updates
bne update_done
inc updates+1
update_done:
rts
draw_screen:
bit $2002
lda #$3f
sta $2006
lda #0
sta $2006
lda updates
lsr
lsr
lsr
and #$0f
sta $2007
lda #0
sta $2006
sta $2006
rts
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
nmi:
inc frame_clock
rti
irq:
rti
.include "frame.inc"
.include "../input-to-sound/input.inc"
.segment "VECTORS"
.word nmi,reset,irq
.segment "CHARS"
.res 8192,0What happens when work runs late
Both clocks are bytes: keep the gap between samples below 256 frames. The returned delta records missed announcements, but the sample deliberately runs one update instead of an unbounded catch-up loop. Rendering is off, so its palette writes are not a template for unrestricted PPU writes during a rendered game. OAM DMA, buffered nametable work and their VBlank budgets belong in the next composition.
Check this combination
Code status: assembled and emulator-executed. NTSC NROM NES. The recorded checks identify sources, firmware and observer. In three 30-frame windows the caller made 30 normal, 11 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.