Skip to content

Trigger a sound on Game Gear

Reuse a small real effect, understand who stops it, and compare its native output with an omitted-trigger control.

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

sound.inc
PSG equ $7f
; Pulse on SN76489 tone0, eight serviced frames. Other channels are muted here.
trigger_sound:
    push af
    ld a,$80
    out (PSG),a
    ld a,$10
    out (PSG),a
    ld a,$90
    out (PSG),a
    ld a,8
    ld (SOUND_TICKS),a
    pop af
    ret
service_sound:
    ld a,(SOUND_TICKS)
    or a
    ret z
    dec a
    ld (SOUND_TICKS),a
    ret nz
    ld a,$9f
    out (PSG),a
    ret

SN76489-compatible tone 0 at $7F, duration $C005. Trigger preserves AF/B; service changes AF. No stereo-panning interface is provided here.

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 the tone divisor or attenuation. A stereo effect needs a separate routing contract as well as a tone.

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

no-sound.asm
; Original capability caller: owns VDP, one audio channel and declared RAM.
VDP_DATA equ $be
VDP_CTRL equ $bf
TURNS equ $c000
SOUND_TICKS equ $c005
    org 0
    jp start
    defs $66-$,0
    retn
    defs $100-$,0
start:
    di
    ld sp,$c3f0
    xor a
    ld ($c000),a
    ld ($c001),a
    ld ($c002),a
    ld ($c003),a
    call init_picture
main:
    call wait_frame
    ld hl,($c002)
    inc hl
    ld ($c002),hl
    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
wait_frame:
    in a,($bf)
wait_new:
    in a,($bf)
    and $80
    jr z,wait_new
    ret
    include "../picture.inc"
    include "../sound.inc"
    defs $7ff0-$,0
    db "TMR SEGA"
    db 0,0,0,0,0,0,0,$4c

The 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 sega-game-gear/patterns/assembly/input/read-action. Press button 1 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.

Game Gear native caller after accepted input.
Captured from the running caller in the configuration below.

Where setup, trigger and service belong

demo.asm
; Original capability caller: owns VDP, one audio channel and declared RAM.
VDP_DATA equ $be
VDP_CTRL equ $bf
TURNS equ $c000
SOUND_TICKS equ $c005
    org 0
    jp start
    defs $66-$,0
    retn
    defs $100-$,0
start:
    di
    ld sp,$c3f0
    xor a
    ld ($c000),a
    ld ($c001),a
    ld ($c002),a
    ld ($c003),a
    call init_picture
main:
    call wait_frame
    ld hl,($c002)
    inc hl
    ld ($c002),hl
    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
wait_frame:
    in a,($bf)
wait_new:
    in a,($bf)
    and $80
    jr z,wait_new
    ret
    include "picture.inc"
    include "sound.inc"
    defs $7ff0-$,0
    db "TMR SEGA"
    db 0,0,0,0,0,0,0,$4c

Compare blocking, serviced and queued sound when choosing the next step.

Verification and sources

Target: NTSC stock Game Gear cartridge configuration. 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.

Sega, Game Gear hardware/software reference: controller ports, stereo routing and PSG; TI SN76489A latch/data format.