Skip to content

Trigger a sound on Atari 7800

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
; Stock 7800 TIA channel0; no POKEY cartridge extension assumed.
trigger_sound:
    lda #4
    sta $15
    lda #15
    sta $17
    sta $19
    lda #8
    sta $1905
    rts
service_sound:
    lda $1905
    beq sound_done
    dec $1905
    bne sound_done
    lda #0
    sta $19
sound_done:
    rts

Stock TIA channel 0, duration byte $1905; no cartridge POKEY is assumed. A/flags change. The caller owns MARIA display lists, TIA sound and its mode lock.

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 TIA tone/noise controls; a POKEY cartridge is an expanded audio configuration with another implementation.

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
; Capability caller: original input/frame service, explicit drawing and sound.
; atari-7800: untimed turns from a new fire press, with independent field work.
; Owns display/input and $1900-$1904; stack is page 1. IRQ disabled.
turns = $1900
previous = $1901
clock_ticks = $1902
current = $1904
* = $4000
start:
    sei
    cld
    ldx #$ff
    txs
    lda #7
    sta $01                   ; enter/lock 7800 mode; BIOS authentication is external
    lda #0
    sta $3c                   ; MARIA DMA off during pointer setup
    lda #>dll
    sta $2c
    lda #<dll
    sta $30
    jsr init_picture
wait_enable:
    bit $28                   ; enable DMA only during VBlank
    bpl wait_enable
    lda #$40
    sta $3c                   ; 160A, empty display lists, background visible
    lda #0
    sta turns
    sta previous
    sta clock_ticks
    sta clock_ticks+1
main:
    jsr wait_frame
    inc clock_ticks
    bne sample
    inc clock_ticks+1
sample:
    !source "../sample.inc"
    lda previous
    eor #$ff
    and current
    beq no_action
    inc turns
    nop
    nop
    nop
no_action:
    lda current
    sta previous
    jsr draw_picture
    jsr service_sound
    jmp main

wait_frame:
    bit $28                   ; MSTAT bit 7: leave any current VBlank
    bmi wait_frame
wait_new:
    bit $28
    bpl wait_new
    rts
irq:
    rti
!source "../picture.inc"
!source "../sound.inc"
    !align 255,0
dll:
    !for i,1,16 { !byte $0f,>$1940,<$1940 }
    !align 255,0
graphics:
    !fill 4096,$ff
    !fill $fffa-*, $ff
    !word irq,start,irq

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 atari-7800/patterns/assembly/input/read-action. Press fire 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.

Atari 7800 native caller after accepted input.
Captured from the running caller in the configuration below.

Where setup, trigger and service belong

demo.asm
; Capability caller: original input/frame service, explicit drawing and sound.
; atari-7800: untimed turns from a new fire press, with independent field work.
; Owns display/input and $1900-$1904; stack is page 1. IRQ disabled.
turns = $1900
previous = $1901
clock_ticks = $1902
current = $1904
* = $4000
start:
    sei
    cld
    ldx #$ff
    txs
    lda #7
    sta $01                   ; enter/lock 7800 mode; BIOS authentication is external
    lda #0
    sta $3c                   ; MARIA DMA off during pointer setup
    lda #>dll
    sta $2c
    lda #<dll
    sta $30
    jsr init_picture
wait_enable:
    bit $28                   ; enable DMA only during VBlank
    bpl wait_enable
    lda #$40
    sta $3c                   ; 160A, empty display lists, background visible
    lda #0
    sta turns
    sta previous
    sta clock_ticks
    sta clock_ticks+1
main:
    jsr wait_frame
    inc clock_ticks
    bne sample
    inc clock_ticks+1
sample:
    !source "sample.inc"
    lda previous
    eor #$ff
    and current
    beq no_action
    inc turns
    jsr trigger_sound
no_action:
    lda current
    sta previous
    jsr draw_picture
    jsr service_sound
    jmp main

wait_frame:
    bit $28                   ; MSTAT bit 7: leave any current VBlank
    bmi wait_frame
wait_new:
    bit $28
    bpl wait_new
    rts
irq:
    rti
!source "picture.inc"
!source "sound.inc"
    !align 255,0
dll:
    !for i,1,16 { !byte $0f,>$1940,<$1940 }
    !align 255,0
graphics:
    !fill 4096,$ff
    !fill $fffa-*, $ff
    !word irq,start,irq

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

Verification and sources

Target: NTSC 48 KiB cartridge, reset without BIOS authentication. 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.

Atari, 7800 Software Guide: controller/TIA interface, MARIA/MSTAT and cartridge reset; Stella audio register definitions.