User Tools

Site Tools


sd:appendix_5_sound_system

This is an old revision of the document!


Appendix 5 Sound System

This is Appendix 5 of the SD-8516 Programmer's Reference Guide.

Introduction

The SD-8516 is paired with the SD-450 sound subsystem; named for featuring 4 independent voices with 5 waveforms available, each with a programmable ADSR envelope. Let's dive in with an overview of the architecture and a quick look at INT 11h, the sound services library. If you are just interested in how to play sounds and music in your own games, you can skip ahead to Sound and Music.

Voice Architecture

Each voice occupies 16 bytes of memory in Bank 1:

Offset Register Description
+$00 FREQ_LO Frequency low byte
+$01 FREQ_MID Frequency mid byte
+$02 FREQ_HI Frequency high byte
+$03 GATE Waveform/gate control
+$04 VOLUME Volume (0-255)
+$05 ATTACK Attack time
+$06 DECAY Decay time
+$07 SUSTAIN Sustain level
+$08 RELEASE Release time
+$09 DATA1 Pulse width / noise type
+$09-$0F DATA2-7 Reserved - Future expansion

Voice base addresses:

  • Voice 0: $1EF80
  • Voice 1: $1EF90
  • Voice 2: $1EFA0
  • Voice 3: $1EFB0

Waveforms

Gate register values:

  • 0: Silent (gate off)
  • 1: Square wave
  • 2: Triangle wave
  • 3: Sawtooth wave
  • 4: Sine wave
  • 5: Pulse wave (variable width via DATA1)
  • 6: White/pink/brown noise (type via DATA1)

ADSR Envelope

The Attack-Decay-Sustain-Release envelope shapes each note:

  • Attack: Time to reach peak volume (0-255 × 10ms)
  • Decay: Time to decay to sustain level (0-255 × 10ms)
  • Sustain: Held volume level (0.0-1.0 of peak)
  • Release: Time to fade to silence after gate off (0-255 × 10ms)

Example:

; Play middle C on voice 0
    LDA $112B           ; C4 frequency (262 Hz / 0.0596)
    STA [$1ED00]        ; $01ED00 = FREQ_LO/MID
    LDA #0
    STA [$1ED02]        ; $01ED02 = FREQ_HI

    LDAL $4D            ; Initialize volume to ~30%
    STAL [$1ED03]       ; $1ED03 = VOLUME
    
    LDAL $01            ; Square wave
    STAL [$1ED02]       ; set GATE to square wave (i.e. turn on)

You can also use the sound services library (I

Sound System Memory Map

Sound System Memory Map

The sound system memory map occupies ($01EF80–$01EFBF) – 64 bytes total

Address Range Size Symbol / Register Description
$01EF80–$01EF8F 16 bytes SOUND0_BASE Voice 0
$01EF80 1 byte SOUND0_FREQ_LO Frequency low byte (bits 7–0)
$01EF81 1 byte SOUND0_FREQ_HI Frequency high byte (bits 15–8)
$01EF82 1 byte SOUND0_GATE Gate / Waveform select (gate bit + waveform type: noise, pulse, saw, triangle, etc.)
$01EF83 1 byte SOUND0_VOLUME Master volume for voice 0 (usually 0–15, may include global volume in some implementations)
$01EF84 1 byte SOUND0_ATTACK Attack rate (0–15)
$01EF85 1 byte SOUND0_DECAY Decay rate (0–15)
$01EF86 1 byte SOUND0_SUSTAIN Sustain level (0–15)
$01EF87 1 byte SOUND0_RELEASE Release rate (0–15)
$01EF88 1 byte SOUND0_DATA1 Voice-specific control / extra parameter 1 (e.g. pulse width low, filter routing, etc.)
$01EF89 1 byte SOUND0_DATA2 Voice-specific control / extra parameter 2 (e.g. pulse width high, ring/mod flags, etc.)
$01EF8A–$01EF8F 6 bytes Reserved / unused / future expansion for Voice 0
Address Range Size Symbol / Register Description
$01EF90–$01EF9F 16 bytes SOUND1_BASE Voice 1
$01EF90 1 byte SOUND1_FREQ_LO Frequency low byte
$01EF91 1 byte SOUND1_FREQ_HI Frequency high byte
$01EF92 1 byte SOUND1_GATE Gate / Waveform select
$01EF93 1 byte SOUND1_VOLUME Volume for voice 1
$01EF94 1 byte SOUND1_ATTACK Attack rate
$01EF95 1 byte SOUND1_DECAY Decay rate
$01EF96 1 byte SOUND1_SUSTAIN Sustain level
$01EF97 1 byte SOUND1_RELEASE Release rate
$01EF98 1 byte SOUND1_DATA1 Extra control 1
$01EF99 1 byte SOUND1_DATA2 Extra control 2
$01EF9A–$01EF9F 6 bytes Reserved / unused / future expansion for Voice 1
Address Range Size Symbol / Register Description
$01EFA0–$01EFAF 16 bytes SOUND2_BASE Voice 2
$01EFA0 1 byte SOUND2_FREQ_LO Frequency low byte
$01EFA1 1 byte SOUND2_FREQ_HI Frequency high byte
$01EFA2 1 byte SOUND2_GATE Gate / Waveform select
$01EFA3 1 byte SOUND2_VOLUME Volume for voice 2
$01EFA4 1 byte SOUND2_ATTACK Attack rate
$01EFA5 1 byte SOUND2_DECAY Decay rate
$01EFA6 1 byte SOUND2_SUSTAIN Sustain level
$01EFA7 1 byte SOUND2_RELEASE Release rate
$01EFA8 1 byte SOUND2_DATA1 Extra control 1
$01EFA9 1 byte SOUND2_DATA2 Extra control 2
$01EFAA–$01EFAF 6 bytes Reserved / unused / future expansion for Voice 2
Address Range Size Symbol / Register Description
$01EFB0–$01EFBF 16 bytes SOUND3_BASE Voice 3
$01EFB0 1 byte SOUND3_FREQ_LO Frequency low byte
$01EFB1 1 byte SOUND3_FREQ_HI Frequency high byte
$01EFB2 1 byte SOUND3_GATE Gate / Waveform select
$01EFB3 1 byte SOUND3_VOLUME Volume for voice 3
$01EFB4 1 byte SOUND3_ATTACK Attack rate
$01EFB5 1 byte SOUND3_DECAY Decay rate
$01EFB6 1 byte SOUND3_SUSTAIN Sustain level
$01EFB7 1 byte SOUND3_RELEASE Release rate
$01EFB8 1 byte SOUND3_DATA1 Extra control 1
$01EFB9 1 byte SOUND3_DATA2 Extra control 2
$01EFBA–$01EFBF 6 bytes Reserved / unused / future expansion for Voice 3

INT 11h Sound Services

    ; ============================================================================
    ; INT 11h - SOUND & MUSIC SERVICES
    ; SD-450 Sound Interface Device + Music Player
    ; ============================================================================
    ;
    ; REGISTER CONVENTION:
    ;   AH = command
    ;   AL = voice (1-4 for music commands, 0-3 for hardware channel)
    ;   B  = data (BL=low, BH=high; or BL=single byte)
    ;   C  = additional data
    ;   D, E, F = extended data (for AH=30h "new instrument")
    ;
    ; FUNCTION MAP:
    ;   --- Music Player Control ---
    ;   AH=00h: Poll music player (check tick timer, advance if ready)
    ;   AH=01h: Execute next note group from song data
    ;   AH=02h: Set clock for next note group
    ;   AH=03h: Execute a single music command
    ;   AH=04h: Reset music player (stop + rewind to start)
    ;   AH=05h: Start / continue music player
    ;   AH=06h: Pause / unpause (AL=00 pause, AL=01+ unpause)
    ;   AH=07h: Load new song (pointer, tempo, calc tick length)
    ;   AH=08h: Insert note data by index
    ;   AH=09h: Delete note data by index
    ;   AH=0Ah: Ask JavaScript to LOAD a music file
    ;   AH=0Bh: Ask JavaScript to SAVE a music file
    ;
    ;   --- Direct Sound Commands (also used by music file data) ---
    ;   AH=10h: Turn off voice
    ;   AH=11h: Turn on as square   (+3 bytes freq: BL, BH, CL)
    ;   AH=12h: Turn on as triangle (+3 bytes freq)
    ;   AH=13h: Turn on as sawtooth (+3 bytes freq)
    ;   AH=14h: Turn on as sine     (+3 bytes freq)
    ;   AH=15h: Turn on as PWM      (+3 bytes freq)
    ;   AH=16h: Turn on as noise    (+3 bytes freq)
    ;   AH=17h: Turn on as waveform byte + note index
    ;            (BL=note index 1-88, BH=waveform 01-06)
    ;   AH=21h: Set GATE        (BL = gate byte)
    ;   AH=22h: Set FREQ        (BL=lo, BH=mid, CL=hi)
    ;   AH=23h: Set ATTACK      (BL = attack byte)
    ;   AH=24h: Set SUSTAIN     (BL = sustain byte)
    ;   AH=25h: Set RELEASE     (BL = release byte)
    ;   AH=26h: Set DATA1       (BL = data1 byte)
    ;   AH=27h: Set DATA2       (BL = data2 byte)
    ;   AH=28h: Set VOLUME      (BL = volume byte)
    ;   AH=29h: Set DECAY       (BL = decay byte)
    ;   AH=30h: Set new voice data at-once (10 bytes: BCDEF)
    ;
    ;   --- Legacy / Utility ---
    ;   AH=40h: Initialize sound system
    ;   AH=41h: Play note (blocking, with duration)
    ;   AH=42h: Stop channel
    ;   AH=43h: Stop all channels
    ;   AH=44h: Set master volume
    ;   AH=45h: Get channel status
    ;   AH=46h: Sound effect (bell, beep, buzz, etc.)
    ;   AH=47h: Play startup sound (GECF)
    ;   AH=48h: Wait milliseconds (C = ms)
    ;   AH=49h: Note index to frequency lookup (BH=index, returns B:CL)
    ;
    ; ============================================================================

Sound and Music

It is possible to play sound sound and music in the background (or while-you-wait) on the SD-8516. The boot chime “GECF” is proof of this.

There are two main ways you can play sound and music. One is by directly controlling the music registers. As listed above, you can write data to these registers and the “SD-450 Sound Chip” will play the sounds. This can be a good way to make sound effects. Alternately you can use the Music Player system in INT 11h.

Although INT 11h is used to make music, you can create short scripts to play sound effects. Each channel operates independently so you can have up to four simultaneous sounds playing at the same time. Making a sound effect relies entirely on your own creativity. Here's an example of the sound we use for the system beep; it's a good example of a directly programmed sound:

.equ SOUND_CH0_BASE    $01EF80
.equ SOUND_CH0_GATE    $01EF83
.equ SOUND_CH0_VOLUME  $01EF84
.equ SOUND_CH0_DATA1   $01EF89

play_beep:
    ; Play a beep (~880hz A4)
    
    ; Set channel 0 frequency to A4
    LDELM $001CD7               ; A4 freq (3 bytes: $D7, $1C, $00)
    STELM [@SOUND_CH0_BASE]     ; write freq_lo/mid/hi

    ; Set pulse width 50%
    LDAL $40
    STAL [@SOUND_CH0_DATA1]

    ; Set volume
    LDAL $4D
    STAL [@SOUND_CH0_VOLUME]

    ; Gate on with pulse waveform
    LDAL @WAVE_PWM
    STAL [@SOUND_CH0_GATE]

    ; Wait 100ms
    LDC #100
    LDAH $48     ; wait_ms -- wait for C ms (blocking)
    INT 0x11

    ; Gate off
    LDAL $00
    STAL [@SOUND_CH0_GATE]

    ; Release delay
    LDC #20
    LDAH $48     ; wait_ms -- wait for C ms (blocking)
    INT 0x11

As you can see – nothing to it!

Music Player System

First you will define a series of commands such as:

BOOT_CHIME_DATA:
    ; Tick 1: setup + G/C
    .byte $01, $00                      ; tick index 1
    .byte $F1, $83, $00                 ; set tempo = 131ms
    .byte $F2, $03                      ; min_note = 3 (eighth)
    .byte $01, $28, $20                 ; v1 duty 12.5%
    .byte $01, $17, $2F, $05            ; v1 PWM + G4
    .byte $02, $17, $1C, $02            ; v2 tri + C3
    .byte $00

    ; Tick 2: E/A
    .byte $02, $00
    .byte $01, $28, $80                 ; v1 duty 50%
    .byte $01, $17, $2C, $05            ; v1 PWM + E4
    .byte $02, $17, $14, $02            ; v2 tri + E2
    .byte $00

    ; Tick 3: C/F
    .byte $03, $00
    .byte $01, $28, $40                 ; v1 duty 25%
    .byte $01, $17, $28, $05            ; v1 PWM + C4
    .byte $02, $17, $10, $02            ; v2 tri + C2
    .byte $00

    ; Tick 4: F/G
    .byte $04, $00
    .byte $01, $28, $20                 ; v1 duty 12.5%
    .byte $01, $17, $2D, $05            ; v1 PWM + F4
    .byte $02, $17, $14, $02            ; v2 tri + E2
    .byte $00

    ; Tick 5: silence
    .byte $05, $00
    .byte $01, $10                      ; v1 off
    .byte $02, $10                      ; v2 off
    .byte $00
    ; End
    .byte $00, $00

With this data, you can play it like this:

    ; Initialize sound system
    LDAH $40
    INT 0x11

    ; Point ELM at our song data and load it (AH=07h)
    LDELM @MUSIC_DATA
    LDAH $07
    INT 0x11

    ; Start playback: set state and prime the clock
    LDAH $05
    INT 0x11

    ; Blocking playback loop: poll until song finishes
player_loop:
    YIELD
    LDAH $00    ; Poll Music Player
    INT 0x11    ; This checks if there is a note, and plays it.
    LDAH $0F    ; Check player state
    INT 0x11
    CMP AL, 1   ; 1 = still playing (0 = song is finished)
    JZ @player_loop

    ; Done
    LDAH $43    ; Stop player (turn off all channels)
    INT 0x11    ; This causes the music player to stop.

Now, if you have a game loop, you can just put this code in your main loop:

  LDAH $00    ; Poll Music Player
  INT 0x11    ; This checks if there is a note, and plays it.

It will quickly check if there is a note and play it. In this way you can have “background music”.

sd/appendix_5_sound_system.1772821092.txt.gz · Last modified: by appledog

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki