Skip to content

Trigger a sound on Tatung Einstein

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
; AY channel A; preserve mixer keyboard directions (port A output/B input).
trigger_sound:
    push af
    ld a,0
    out ($02),a
    ld a,100
    out ($03),a
    ld a,1
    out ($02),a
    xor a
    out ($03),a
    ld a,7
    out ($02),a
    ld a,$7e
    out ($03),a
    ld a,8
    out ($02),a
    ld a,15
    out ($03),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,8
    out ($02),a
    xor a
    out ($03),a
    ret

AY channel A, duration $8005. The routine sets mixer $7E, preserving the required port-A-output/port-B-input configuration. Trigger preserves AF/B; service changes AF. Both keyboard and sound share the AY address latch.

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 tone A’s period/volume without changing keyboard port directions. A shared audio driver must coordinate with keyboard scanning.

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 $08
VDP_CTRL equ $09
TURNS equ $8000
SOUND_TICKS equ $8005
; Stock MOS disk boot header: destination, end, entry, disk mode.
    org $9000
    dw $9000,$9200,start
    db 0
start:
    di
    ld sp,$fff0
    xor a
    ld ($8000),a
    ld ($8001),a
    ld ($8002),a
    ld ($8003),a
    ld a,1
    out ($20),a               ; mask dedicated keyboard interrupt
    ld a,7
    out ($02),a
    ld a,$7f
    out ($03),a               ; mute tone/noise gates, port A output / B input
    in a,($09)
    xor a
    out ($09),a
    ld a,$80
    out ($09),a          ; R0 legacy graphics
    xor a
    out ($09),a
    ld a,$81
    out ($09),a          ; R1 display and VDP IRQ disabled
    call init_picture
main:
    call wait_frame
    ld hl,($8002)
    inc hl
    ld ($8002),hl
    include "../sample.inc"
    ld a,($8001)
    cpl
    and b
    jr z,no_action
    ld a,($8000)
    inc a
    ld ($8000),a
    nop
    nop
    nop
no_action:
    ld a,b
    ld ($8001),a
    call draw_picture
    call service_sound
    jp main

; AF clobbered; caller exclusively owns status/control-port latch.
wait_frame:
    in a,($09)
wait_new:
    in a,($09)
    and $80
    jr z,wait_new
    ret
    include "../picture.inc"
    include "../sound.inc"
    defs $9200-$,0

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 tatung-einstein/patterns/assembly/input/read-action. Press Space 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. Attach build/demo.dsk and use Control-BREAK to let MOS boot it. This is a disk-boot program, not a CP/M or BASIC loader.

Tatung Einstein 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 $08
VDP_CTRL equ $09
TURNS equ $8000
SOUND_TICKS equ $8005
; Stock MOS disk boot header: destination, end, entry, disk mode.
    org $9000
    dw $9000,$9200,start
    db 0
start:
    di
    ld sp,$fff0
    xor a
    ld ($8000),a
    ld ($8001),a
    ld ($8002),a
    ld ($8003),a
    ld a,1
    out ($20),a               ; mask dedicated keyboard interrupt
    ld a,7
    out ($02),a
    ld a,$7f
    out ($03),a               ; mute tone/noise gates, port A output / B input
    in a,($09)
    xor a
    out ($09),a
    ld a,$80
    out ($09),a          ; R0 legacy graphics
    xor a
    out ($09),a
    ld a,$81
    out ($09),a          ; R1 display and VDP IRQ disabled
    call init_picture
main:
    call wait_frame
    ld hl,($8002)
    inc hl
    ld ($8002),hl
    include "sample.inc"
    ld a,($8001)
    cpl
    and b
    jr z,no_action
    ld a,($8000)
    inc a
    ld ($8000),a
    call trigger_sound
no_action:
    ld a,b
    ld ($8001),a
    call draw_picture
    call service_sound
    jp main

; AF clobbered; caller exclusively owns status/control-port latch.
wait_frame:
    in a,($09)
wait_new:
    in a,($09)
    and $80
    jr z,wait_new
    ret
    include "picture.inc"
    include "sound.inc"
    defs $9200-$,0

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

Verification and sources

Target: TC-01 PAL, MOS 1.2, Ctrl-BREAK disk boot. 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.

Tatung, Einstein Hardware Manual: programmable sound generator/keyboard interface and port decoding; MOS disk boot header; General Instrument AY-3-8910 data manual.