Give an accepted action a sound. The same caller changes its picture and triggers this effect; a second version performs the action with the trigger removed. Listen to both.
Working effect
Trigger omitted
Take the effect
The reusable effect
; DMG pulse channel1, both outputs; non-blocking eight-frame effect.
trigger_sound:
ld a,$80
ld [$ff26],a
ld a,$77
ld [$ff24],a
ld a,$11
ld [$ff25],a
xor a
ld [$ff10],a
ld a,$80
ld [$ff11],a
ld a,$f0
ld [$ff12],a
xor a
ld [$ff13],a
ld a,$87
ld [$ff14],a
ld a,8
ld [$c004],a
ret
service_sound:
ld a,[$c004]
or a
ret z
dec a
ld [$c004],a
ret nz
xor a
ld [$ff12],a
retPulse channel 1, duration byte $C004. The trigger also writes shared NR50/NR51/NR52 routing, volume and power controls, so it is not safe beside an unrelated mixer. AF changes; B is preserved.
Include sound.inc once with the caller’s constants and duration byte. Initialise the duration byte to zero before the first service call. Call trigger_sound for an accepted action, then call service_sound exactly once per accepted display update. The eighth service call mutes the effect. Because the supplied caller services in the triggering iteration too, this is a count of service calls, not eight complete elapsed frame intervals. Triggering again restarts the duration; there is no queue.
The complete initialiser also mutes other channels. To reuse just this effect beside music, agree channel ownership and remove unrelated global setup only after providing a replacement. Device latches, power, routing and master volume may affect more than one voice.
Change it and recognise a mistake
Change pulse duty/frequency/envelope operands. An audio engine should initialise shared power/routing once and arbitrate channel use.
The omitted-trigger control still accepts the action but fails the same audio guard as the working caller. Keyboard clicks or other machine output are not enough: the BASIC checks compare against a separate rejected-command recording.
Deliberately omitted sound trigger
; Capability caller: original input/frame service, explicit drawing and sound.
; DMG cartridge: video keeps running; only a new A press advances the turn.
; Owns LCD/VRAM, JOYP and $C000-$C003; maskable interrupts stay disabled.
SECTION "Entry", ROM0[$100]
nop
jp start
ds $134-@,0
db "TURNPROBE"
ds $150-@,0
SECTION "Program", ROM0[$150]
start:
di
ld sp,$fffe
wait_to_disable:
ld a,[$ff44]
cp 144
jr c,wait_to_disable
xor a
ld [$ff40],a ; disable LCD in VBlank before clearing VRAM
ld hl,$8000
ld bc,$2000
clear_vram:
xor a
ld [hl+],a
dec bc
ld a,b
or c
jr nz,clear_vram
xor a
ld [$c000],a ; turn counter
ld [$c001],a ; previous A state
ld [$c002],a ; accepted frame counter, low
ld [$c003],a ; high
call init_picture
ld a,$e4
ld [$ff47],a
ld a,$91
ld [$ff40],a ; background enabled, tile/map zero initialised
main:
call wait_frame
ld a,[$c002]
inc a
ld [$c002],a
jr nz,sample
ld a,[$c003]
inc a
ld [$c003],a
sample:
INCLUDE "../sample.inc"
ld a,[$c001]
cpl
and b
jr z,no_action
ld a,[$c000]
inc a
ld [$c000],a
nop
nop
nop
no_action:
ld a,b
ld [$c001],a
call draw_picture
call service_sound
jp main
; AF clobbered. Requires LCD enabled. Waits beyond any current VBlank,
; then accepts the next VBlank. Does not count missed announcements.
wait_frame:
ld a,[$ff44]
cp 144
jr nc,wait_frame
wait_vblank:
ld a,[$ff44]
cp 144
jr c,wait_vblank
ret
INCLUDE "../picture.inc"
INCLUDE "../sound.inc"
ds $8000-@,0The working implementation above repairs that omission. If an effect instead drones on, check the mute/service path and that its state is initialised. Servicing twice makes a counted effect shorter; servicing only after input can leave it running while the player waits. Those are lifecycle decisions to test when adapting it.
Run the caller
Build the complete caller with make -C nintendo-game-boy/patterns/assembly/input/read-action. Press A once, keep it held, then release and press again. The piece moves on each new press and returns to its starting position after two; holding does not repeat the action. Load the cartridge produced by the Makefile in the named configuration; retain its reset/header setup.

Where setup, trigger and service belong
; Capability caller: original input/frame service, explicit drawing and sound.
; DMG cartridge: video keeps running; only a new A press advances the turn.
; Owns LCD/VRAM, JOYP and $C000-$C003; maskable interrupts stay disabled.
SECTION "Entry", ROM0[$100]
nop
jp start
ds $134-@,0
db "TURNPROBE"
ds $150-@,0
SECTION "Program", ROM0[$150]
start:
di
ld sp,$fffe
wait_to_disable:
ld a,[$ff44]
cp 144
jr c,wait_to_disable
xor a
ld [$ff40],a ; disable LCD in VBlank before clearing VRAM
ld hl,$8000
ld bc,$2000
clear_vram:
xor a
ld [hl+],a
dec bc
ld a,b
or c
jr nz,clear_vram
xor a
ld [$c000],a ; turn counter
ld [$c001],a ; previous A state
ld [$c002],a ; accepted frame counter, low
ld [$c003],a ; high
call init_picture
ld a,$e4
ld [$ff47],a
ld a,$91
ld [$ff40],a ; background enabled, tile/map zero initialised
main:
call wait_frame
ld a,[$c002]
inc a
ld [$c002],a
jr nz,sample
ld a,[$c003]
inc a
ld [$c003],a
sample:
INCLUDE "sample.inc"
ld a,[$c001]
cpl
and b
jr z,no_action
ld a,[$c000]
inc a
ld [$c000],a
call trigger_sound
no_action:
ld a,b
ld [$c001],a
call draw_picture
call service_sound
jp main
; AF clobbered. Requires LCD enabled. Waits beyond any current VBlank,
; then accepts the next VBlank. Does not count missed announcements.
wait_frame:
ld a,[$ff44]
cp 144
jr nc,wait_frame
wait_vblank:
ld a,[$ff44]
cp 144
jr c,wait_vblank
ret
INCLUDE "picture.inc"
INCLUDE "sound.inc"
ds $8000-@,0Compare blocking, serviced and queued sound when choosing the next step.
Verification and sources
Target: DMG default reset without boot ROM, 32 KiB cartridge. These callers were executed with released Emu198x 0.44.1; assembly uses Asm198x 0.0.71. The sample folder contains reproduction instructions and retained observations. The independent checker binds source, native captures and actual output; its rejection tests exercise wrong state, missing files and false audio claims.
This qualifies the named emulator/firmware path. Pitch accuracy, original hardware and other peripherals require their own observations.
Nintendo, Game Boy Programming Manual v1.1: P1/JOYP scan example (six action-button reads), LCDC/LY access rules and NR10–NR14/NR50–NR52.