Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

2665 lines (2116 sloc) 73.783 kB
.include "src/includes/sound_defines.asm"
.include "src/includes/sound_macros.asm"
.BANK 2 SLOT 2
.ORG $0000
Bank2:
; =============================================================================
; Sound_Update()
; -----------------------------------------------------------------------------
; Main sound driver entrypoint. Called once per frame (vblank).
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_Update: ; $8000
; check the reset trigger and intialise the PSG
; if required
ld hl, Sound_ResetTrg
ld a, (hl)
or a
jr z, +
; if MSB is reset the sound driver is disabled
ret p
dec (hl)
jp Sound_InitPSG
+: ; read music trigger and copy to "current sound"
call Sound_CheckSoundTriggers
; adjust channels to the correct tempo
call Sound_AdjustForSpeed
; check to see if the current music/sfx needs
; to be faded out
call Sound_FadeOut
; check the trigger byte. if it has changed we
; need to load a new music/sfx module
call Sound_CheckLoadMusic
ld a, ($DE91)
or a
jp z, Sound_UpdateChannels
ld a, ($DE92)
inc a
cp $05
jr z, LABEL_802D_123
ld ($DE92), a
jp Sound_UpdateChannels
LABEL_802D_123:
xor a
ld ($DE92), a
call Sound_UpdateChannels
call Sound_UpdateChannels
ret
; =============================================================================
; Sound_UpdateChannels()
; -----------------------------------------------------------------------------
; Updates each active channel structure.
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_UpdateChannels: ; $8038
ld ix, Sound_Channel_Music_0
Sound_IsChannelActive
call nz, Sound_UpdateToneChannel
ld ix, Sound_Channel_Music_1
Sound_IsChannelActive
call nz, Sound_UpdateToneChannel
ld ix, Sound_Channel_Music_2
Sound_IsChannelActive
call nz, Sound_UpdateToneChannel
ld ix, Sound_Channel_Music_Noise
Sound_IsChannelActive
call nz, Sound_UpdateNoiseChannel
ld ix, Sound_Channel_SFX_0
Sound_IsChannelActive
call nz, Sound_UpdateToneChannel
ld ix, Sound_Channel_SFX_1
Sound_IsChannelActive
call nz, Sound_UpdateToneChannel
ld ix, Sound_Channel_SFX_2
Sound_IsChannelActive
call nz, Sound_UpdateToneChannel
ret
; =============================================================================
; Sound_AdjustForSpeed()
; -----------------------------------------------------------------------------
; Inserts extra ticks into each channel's note duration counter so that
; the module plays at the correct tempo.
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; Destroys:
; A, B, DE, HL
; -----------------------------------------------------------------------------
Sound_AdjustForSpeed: ; $8086
ld hl, Sound_SpeedCounter ;decrement counter value
; return if the speed counter is 0
ld a, (hl)
or a
ret z
; decrement the counter
dec (hl)
ret nz
; if we get here, the counter is zero.
; reset the counter and insert an extra tick into each channel
; restore counter value
ld a, (Sound_Speed)
ld (hl), a
; add an extra tick to each channel
ld hl, Sound_Channel_Music_0 + Sound_ChnlToneDuration
ld de, Sound_ChannelSize ;for the 4 channels
ld b, 4
-: inc (hl)
add hl, de
djnz -
ret
; =============================================================================
; Sound_CheckSoundTriggers
; -----------------------------------------------------------------------------
; Checks each of the 3 trigger bytes for new module numbers and, if the
; priority value allows, copies the module index into the main trigger byte
; at $DD03.
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_CheckSoundTriggers: ; $809F
; load a pointer to the sound triggers
ld de, $DD04
; pointer to the current sound's priority
ld ix, Sound_Priority
; pointer to current sound number
ld iy, $DD03
; check each trigger
call Sound_CheckSoundTriggers_CheckTrigger
call Sound_CheckSoundTriggers_CheckTrigger
Sound_CheckSoundTriggers_CheckTrigger:
; fetch the trigger value
ld a, (de)
and $7F ;ignore MSB
; jump if the trigger is 0 (no new module)
jr z, +
; fetch a priority value for the module number
dec a
ld hl, Sound_Data_Priorities
ld c, a
ld b, $00
add hl, bc
ld a, (hl)
; if the current sound's priority is greater than the
; new sound's don't bother changing anything...
cp (ix + 0)
jr c, + ;jump if $DD0F < A
; ...otherwise, set the new priority...
and $7F
ld (ix + 0), a ;DD0F
; ... and copy module number to the main trigger
ld a, (de)
ld (iy + 0), a ;DD03
+: ; reset the trigger
xor a
ld (de), a
; move to the next trigger byte
inc de
ret
; =============================================================================
; Sound_FadeOut()
; -----------------------------------------------------------------------------
; Fades the volume of the primary tone channels by increasing attenuation.
; If any sound effect channels are active they will be disabled.
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; Destroys:
; A, BC, DE, HL
; -----------------------------------------------------------------------------
Sound_FadeOut: ; $80D0
; check to see if the volume fadeout counter
; has been set
ld a, (Sound_FadeMajorCount)
or a
ret z
; check that none of the sfx channels are active.
; if they are they should be disabled and the correct
; primary channel reactivated.
; load HL with a pointer to the first SFX channel
ld hl, $DE00
; loop 3 times
ld b, $03
; this will be used to move the pointer to the next channel
ld de, $0030
-: ; check to see if the channel is active
bit Sound_ChannelActiveBit, (hl)
jp z, Sound_FadeOut_LoopNext
push hl
; check the PSG channel number
inc hl
ld a, (hl)
; check for tone 1 channel
cp $A0
; jump if channel != tone 1
jp nz, +
; chear the primary tone 1 channel's suppress bit
ld hl, $DD70
res Sound_ChannelSuppressBit, (hl)
jp Sound_FadeOut_DisableSFXChannel
+: ; check for the noise channel
cp $E0
; jump if channel != noise
jp nz, ++
; clear the noise channel's suppress bit
ld hl, $DDD0
res Sound_ChannelSuppressBit, (hl)
++: ; clear the primary tone 2 channel's suppress bit
ld hl, $DDA0
res Sound_ChannelSuppressBit, (hl)
Sound_FadeOut_DisableSFXChannel: ; $8101
pop hl
; clear channel control byte
xor a
ld (hl), a
Sound_FadeOut_LoopNext:
add hl, de
djnz -
; decrement the fadeout minor count
ld a, (Sound_FadeMinorCount)
dec a
; if the minor count value is 0 we need to
; reset it and decrement the major count
jr z, +
ld (Sound_FadeMinorCount), a
ret
+: ; reset the minor count value
ld a, (Sound_FadeMinorReset)
ld (Sound_FadeMinorCount), a
; decrement the major countdown value
ld a, (Sound_FadeMajorCount)
dec a
ld (Sound_FadeMajorCount), a
; reset the psg when the counter == 0
jp z, Sound_ResetAll
; load a pointer to the first channel's volume value
ld hl, Sound_Channel_Music_0 + Sound_ChnlVolumeAdjust
; DE will be added to HL to move the pointer to the next channel
ld de, Sound_ChannelSize
; loop 3 times (3 tone channels)
ld b, 3
-: ; check and attenuate as necessary
call Sound_FadeOut_IncreaseAttenuation
add hl, de
djnz -
; adjust the noise channel volume
ld hl, Sound_NoiseChnlVolume
Sound_FadeOut_IncreaseAttenuation: ; $8132
; if attenuation < $0C then increase
ld a, (hl)
inc a
; return if value >= $0C
cp $0C
ret nc
; store attenuation value
ld (hl), a
ret
; =============================================================================
; Sound_CheckLoadMusic()
; -----------------------------------------------------------------------------
; Checks the main trigger at $DD03 for a value > $80. If it finds such a
; value it loads the relevant music/sfx data.
; -----------------------------------------------------------------------------
; In:
; ($DD03) - Music/SFX trigger
; Out:
; None.
;------------------------------------------------------------------------------
Sound_CheckLoadMusic: ; $8139
; load "current music" number into A
ld a, ($DD03)
; if value < $80 reset the channels
bit 7, a
jp z, Sound_ResetAll
; check to see if value corresponds to music slot
; (i.e. jump if A < $9A)
cp SFX_FirstSlot
jr c, Sound_LoadMusicModule
; check to see if value corresponds to SFX slot
; (i.e. jump if A is between $9A and $C0)
cp $C0
jp c, Sound_LoadSFXModule
; if value >= $C4 then reset the PSG
cp $C4
jp nc, Sound_ResetAll
; value is between $C0 and $C3. use as an index
; into the jump table below
sub $C0
ld hl, DATA_8158
call Sound_CalcIndex_Int16
jp (hl)
DATA_8158:
.dw LABEL_8160
.dw Sound_ResetAll
.dw Sound_StopSFX
.dw $7000
LABEL_8160:
; fade out over 216 ticks
ld a, $0C
ld (Sound_FadeMajorCount), a
ld a, $12
ld (Sound_FadeMinorCount), a
ld (Sound_FadeMinorReset), a
jp Sound_SetActive
; =============================================================================
; Sound_StopSFX()
; -----------------------------------------------------------------------------
; Causes each sound effect tone channel to execute a "stop" command with the
; next tick.
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; Destroys:
; B, DE, HL, IY
; -----------------------------------------------------------------------------
Sound_StopSFX: ; $8170
; load IY with a pointer to the first SFX channel
ld iy, Sound_Channel_SFX_0
; this will be added to IY with each iteration of the loop
; to move to the next channel structure
ld de, Sound_ChannelSize
; loop 3 times
ld b, 3
; this will be stored as the next sound command for the channel
ld hl, _Sound_StopSFX_command
-: ; store the "next command" pointer
ld (iy + Sound_ChnlNextCommand), l
ld (iy + Sound_ChnlNextCommand + 1), h
; move to the next channel structure
add iy, de
djnz -
ret
_Sound_StopSFX_command
Stop
; =============================================================================
; Sound_LoadMusicModule(uint8 module_index)
; -----------------------------------------------------------------------------
; Loads music module header data into the channel structures.
; -----------------------------------------------------------------------------
; In:
; A - Module number.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_LoadMusicModule: ; $8188
; First music slot is $81. Subtract $81 from the value
; passed in A to get a zero-based index
sub Music_FirstSlot
; return if < $81
ret m
; reset all channel structures
ex af, af'
call Sound_ResetAll
ex af, af'
; fetch the tempo/speed value for the module
; FIXME: this is completely pointless as it is overwritten
; further on in this subroutine by the value stored in
; the module header
ld hl, Sound_Data_MusicSpeeds
; put the module number into C for the call to
; Sound_CalcIndex_Int8
ld c, a
ex af, af'
call Sound_CalcIndex_Int8
ld (Sound_SpeedCounter), a
ld (Sound_Speed), a
ex af, af'
; fetch a pointer to the music data
ld hl, Sound_Data_MusicPointers
; calculate the address of the module data
call Sound_CalcIndex_Int16
; skip past the volume envelope pointer
inc hl
inc hl
; get number of channels
ld b, (hl)
; skip the next byte
inc hl
; copy the channel's tempo modifier into the shadow accumulator
inc hl
ld a, (hl)
ex af, af'
; read the module's global speed modifier (note: this overwrites
; the value written earlier).
inc hl
ld a, (hl)
ld (Sound_SpeedCounter), a
ld (Sound_Speed), a
; load IY with a pointer to the sound channel numbers
; for each structure.
ld iy, Sound_Data_ChannelNumbers
inc hl
; initialise required number of channels
; starting with structure at $DD40
ld de, Sound_Channel_Music_0
-: call Sound_InitChannel
djnz -
; =============================================================================
; Sound_SetActive()
; -----------------------------------------------------------------------------
; Marks the main trigger byte ($DD03) as active.
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_SetActive: ; $81C1
ld a, $80
ld ($DD03), a
ret
; =============================================================================
; Sound_LoadSFXModule(uint8 module_index)
; -----------------------------------------------------------------------------
; Loads sound effect module header data into the channel structures. Will
; activate a secondary channel and suppress a primary channel.
; -----------------------------------------------------------------------------
; In:
; A - Module number.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_LoadSFXModule: ; $81C7
; store the sound effect value here
ld (Sound_SFXIndex), a
; subtract the index of the first SFX slot to
; get a zero-based offset value
sub SFX_FirstSlot
; calculate an index into the array at $8C0D
ld hl, Sound_Data_SFXPointers
call Sound_CalcIndex_Int16
; skip past the volume envelope pointer
inc hl
inc hl
ld a, (hl)
inc hl
ex af, af'
; fetch number of channels
ld b, (hl)
inc hl
; loop over each channel
-: inc hl
ld a, (hl)
dec hl
; determine which primary channel to suppress
; and which secondary channel to activate
cp Sound_PSG_Latch | Sound_PSG_Tone1
jr z, Sound_LoadSFXModule_PriT1_SecT0
cp Sound_PSG_Latch | Sound_PSG_Tone2
jr z, LABEL_81ED_114
; if we get here A == $E0 (i.e. noise channel)
ld de, Sound_Channel_SFX_2
ld iy, Sound_Channel_Music_Noise
jr LABEL_820F_115
LABEL_81ED_114:
ld iy, Sound_Channel_SFX_2
bit 6, (iy + Sound_ChnlControl)
jr nz, Sound_LoadSFXModule_PriT2_SecT1
set Sound_ChannelSuppressBit, (iy + Sound_ChnlControl)
ld a, $FF ;noise volume = off
out (Ports_PSG), a
Sound_LoadSFXModule_PriT2_SecT1: ; $81FF
ld de, Sound_Channel_SFX_1
ld iy, Sound_Channel_Music_2
jr LABEL_820F_115
Sound_LoadSFXModule_PriT1_SecT0: ; $8208
ld de, Sound_Channel_SFX_0
ld iy, Sound_Channel_Music_1
LABEL_820F_115:
call Sound_LoadSFXModule_InitChannel
djnz -
jp Sound_SetActive
Sound_LoadSFXModule_InitChannel: ; $8217
; suppress psg writes for the primary channel
set Sound_ChannelSuppressBit, (iy + Sound_ChnlControl)
; initialise the secondary channel structure
ld c, Sound_ChannelSize + 6
push de
pop ix
ldi
ldi
ex af, af'
ld (de), a
inc de
ex af, af'
xor a
ldi
ldi
ldi
ldi
ld (de), a
inc de
ld (de), a
inc de
ld a, c
ld (de), a
inc de
; reset the channel's 3 loop counters
xor a
ld (ix + Sound_ChnlLoopCounter0), a
ld (ix + Sound_ChnlLoopCounter1), a
ld (ix + Sound_ChnlLoopCounter2), a
inc a
ld (de), a
push hl
ld hl, Sound_ChannelSize - $0A
add hl, de
ex de, hl
pop hl
ret
; =============================================================================
; Sound_InitChannel(uint8 tempo, uint16 channel_ptr, uint16 source)
; -----------------------------------------------------------------------------
; Initialise a channel structure with data from a module's channel header.
; -----------------------------------------------------------------------------
; In:
; A' - Tempo
; DE - Pointer to channel structure.
; HL - Pointer to source header data.
; Out:
; DE - Pointer to next channel structure (e.g. if DE was $DD40 on entry
; it will be $DD70 on exit).
; HL - Pointer to data immediately after channel init data.
; Destroys:
; A, BC, IX, A'
; -----------------------------------------------------------------------------
Sound_InitChannel: ;$824C
; load C with $34. C will be used to set the initial
; stack pointer for the channel. The stack pointer will
; start at $30. 4 LDIs will decrement C to $30 before
; we set the stack pointer at (ix + 9)
ld c, Sound_ChannelSize + 4
push de
pop ix
; mark the channel as active
ld a, Sound_ChannelActive
ld (de), a
inc de
; fetch the PSG channel number and copy it into
; the structure
ld a, (iy + 0)
ld (de), a
inc de
inc iy
; fetch the tempo value from the shadow accumulator
ex af, af'
ld (de), a
inc de
ex af, af'
; copy 4 bytes into the channel starting at +$03 bytes
; (e.g. $DD43)
xor a
ldi ; first 2 bytes are command data pointer
ldi
ldi ; next byte is tone adjustment
ldi ; next byte is volume attenuation
; reset tone effect flags
ld (de), a
inc de
; reset volume effect flags
ld (de), a
inc de
; set the default stack pointer for the channel
ld a, c
ld (de), a
inc de
; reset the channel's 3 loop counters
xor a
ld (ix + Sound_ChnlLoopCounter0), a
ld (ix + Sound_ChnlLoopCounter1), a
ld (ix + Sound_ChnlLoopCounter2), a
; set the tone duration counter to 1
inc a
ld (de), a
; move DE to the next channel structure
push hl
ld hl, Sound_ChannelSize - $0A
add hl, de
ex de, hl
pop hl
ret
; these bytes are copied into the "channel number" byte
; of each channel structure (e.g. $DD41)
Sound_Data_ChannelNumbers: ;$8284
.db Sound_PSG_Latch | Sound_PSG_Tone0
.db Sound_PSG_Latch | Sound_PSG_Tone1
.db Sound_PSG_Latch | Sound_PSG_Tone2
.db Sound_PSG_Latch | Sound_PSG_Noise
; =============================================================================
; Sound_LoadPitchBendData(uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Copies parameter values required by the pitch bend effect into the
; channel structure.
; -----------------------------------------------------------------------------
; In:
; IX - Pointer to channel structure.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_LoadPitchBendData: ; $8288
; return if the pitch bend bit is reset
bit Sound_ChnlPitchBendBit, (ix + Sound_ToneEffectFlags)
ret z
; bail out if we need to preserve effect state
bit Sound_ChnlKeepStateBit, (ix + Sound_ChnlControl)
ret nz
; load the pointer at (ix + $10) into DE
ld e, (ix + Sound_PitchBendDataPtr)
ld d, (ix + Sound_PitchBendDataPtr + 1)
; load the channel pointer into HL
push ix
pop hl
; adjust HL to IX+$14
ld b, $00
ld c, Sound_PitchBendCountdown & $FF
add hl, bc
; copy 3 bytes from the data pointer at (IX+$10)
; into the channel structure
ex de, hl
ldi ; pitch bend countdown
ldi ; pitch bend length
ldi ; pitch bend step size
; read the repeat count
ld a, (hl)
; divide by 2
srl a
; ...and store in the channel structure at (ix + $17)
ld (de), a
;clear the tone adjustment value
xor a
ld (ix + Sound_ChnlToneAdjustment), a
ld (ix + Sound_ChnlToneAdjustment + 1), a
ret
LABEL_82B3_142:
bit Sound_VolPreserveBit, (ix + Sound_VolumeEffectFlags)
ret z
bit Sound_ChnlKeepStateBit, (ix + Sound_ChnlControl)
ret nz
; dont update if the "suppress updates" bit is set
bit 7, (ix + $1D)
ret nz
; set volume attenuation to max
ld a, $FF
ld (ix + Sound_ChnlVolume), a
and $10
or (ix + $1E)
ld (ix + $1D), a
ret
; =============================================================================
; Sound_ApplyToneEffect(uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Determins whether a tone effect is in place and adjusts the tone data
; accordingly.
; -----------------------------------------------------------------------------
; In:
; IX - Pointer to channel structure.
; Out:
; HL - Tone data.
; -----------------------------------------------------------------------------
Sound_ApplyToneEffect: ; $82D0
; read the tone value from the channel structure
ld l, (ix + Sound_ChnlToneData)
ld h, (ix + Sound_ChnlToneData + 1)
; should an effect be applied?
ld a, (ix + Sound_ToneEffectFlags)
or a
ret z
; jump if bit 7 is reset
jp p, Sound_ApplyPitchEnvelope
; msb was set - apply pitch bend
; decrement countdown value and return if != 0
dec (ix + Sound_PitchBendCountdown)
ret nz
inc (ix + Sound_PitchBendCountdown) ;ix+$14 = 1
; push the tone value to the stack
push hl
; fetch the tone adjustment
ld l, (ix + Sound_ChnlToneAdjustment)
ld h, (ix + Sound_ChnlToneAdjustment + 1)
; decrement the counter
dec (ix + Sound_PitchBendLength)
jr nz, +
; the counter is zero. we need to apply the effect
; and reset the counter value
; load the effect data pointer into IY
ld e, (ix + Sound_PitchBendDataPtr)
ld d, (ix + Sound_PitchBendDataPtr + 1)
push de
pop iy
; reset the step duration counter
ld a, (iy + $01)
ld (ix + Sound_PitchBendLength), a
; load the signed 8bit step value into the
; bc register pair
ld a, (ix + Sound_PitchBendStepValue)
ld c, a
; rotate MSB into carry flag
and $80
rlca
neg
ld b, a
; add to the current tone adjustment value
add hl, bc
; store the tone adjustment value
ld (ix + Sound_ChnlToneAdjustment), l
ld (ix + Sound_ChnlToneAdjustment + 1), h
+: ; pop the tone value into BC
pop bc
; apply the effect to the tone value
add hl, bc
; decrement the repeat counter
dec (ix + Sound_PitchBendRepeat)
ret nz
; restore the effect repeat counter
ld a, (iy + $03)
ld (ix + Sound_PitchBendRepeat), a
; negate the effect step value
ld a, (ix + Sound_PitchBendStepValue)
neg
ld (ix + Sound_PitchBendStepValue), a
ret
Sound_ApplyPitchEnvelope: ; $8326
dec a
; put the tone data into the DE register pair
ex de, hl
; fetch a pointer to the effect data
ld hl, Sound_Data_PitchEnvelopes
call Sound_CalcIndex_Int16
jr Sound_PitchEnvelope_GetAdjustment
Sound_PitchEnvelope_SetIndex: ; $8330
ld (ix + Sound_PitchEnvlpIndex), a
Sound_PitchEnvelope_GetAdjustment: ; $8333
; read a value from the effect data array
push hl
ld c, (ix + Sound_PitchEnvlpIndex)
call Sound_CalcIndex_Int8
pop hl
; if the value < $80 add it to the tone data
bit 7, a
jr z, Sound_PitchEnvelope_CalculateTone
; if the value == $83 it is a "set index" command
cp $83
jr z, Sound_PitchEnvelope_ReadIndex
; if the value is > $83 it is a negative adjustment
; value. sign extend and add to the tone data
jr nc, Sound_PitchEnvelope_SignExtend
; if the value == $80 it is a "reset index" command
cp $80
jr z, Sound_PitchEnvelope_ResetIndex
; value == $81 or $82
set 5, (ix + Sound_ChnlControl)
pop hl
ret
Sound_PitchEnvelope_ReadIndex: ; $834F
; read the index from the effect data
inc de
ld a, (de)
jr Sound_PitchEnvelope_SetIndex
Sound_PitchEnvelope_ResetIndex: ; $8353
xor a
jr Sound_PitchEnvelope_SetIndex
Sound_PitchEnvelope_SignExtend: ; $8356
ld h, $FF
jr +
Sound_PitchEnvelope_CalculateTone: ; $835A
ld h, $00
+: ld l, a
; add the effect data to the tone data
add hl, de
; increment the index value
inc (ix + Sound_PitchEnvlpIndex)
ret
; =============================================================================
; Sound_ReadChannelStream(uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Updates a channel by reading from the module's sound command data.
; -----------------------------------------------------------------------------
; In:
; IX - Channel pointer.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_ReadChannelStream: ; $8362
;clear some flags
res Sound_ChnlKeepStateBit, (ix + Sound_ChnlControl)
res Sound_ChnlVolSuppressBit, (ix + Sound_ChnlControl)
; fetch the data pointer
ld e, (ix + Sound_ChnlNextCommand)
ld d, (ix + Sound_ChnlNextCommand + 1)
; FALL THROUGH
; =============================================================================
; Sound_ReadStreamData(uint16 channel_ptr, uint16 stream_ptr)
; -----------------------------------------------------------------------------
; Read and interpret the next byte from the sound data.
; -----------------------------------------------------------------------------
; In:
; DE - Current data pointer (points into sound data stream).
; IX - Channel pointer.
; Out:
;
; -----------------------------------------------------------------------------
Sound_ReadStreamData: ; $8370
; read a byte of data and increment the pointer
ld a, (de)
inc de
; jump if the byte is a control byte (i.e. >= $E0)?
cp $E0
jp nc, Sound_InterpretCommand_WithReturn
bit Sound_ChnlReadLiteralBit, (ix + Sound_ChnlControl)
jp nz, Sound_ReadLiteral
; jump if byte < $80
cp $80
jr c, Sound_ReadStreamData_CalcNoteHold
; jump if byte == $80
jr z, Sound_ReadStreamData_Rest
; value is a "note" command
; clear bit 7 of IX+$1D
ex af, af'
ld a, (ix + $1D)
and $7F
ld (ix + $1D), a
ex af, af'
; get the note value
call Sound_GetNoteValue
; store the note value in the channel structure
ld (ix + Sound_ChnlToneData), l
ld (ix + Sound_ChnlToneData + 1), h
; checks to see if the next byte is a "set ticks per note" command
; and sets the note duration as necessary
Sound_ReadStreamData_SetNoteHold: ; $8397
; read the next byte
ld a, (de)
inc de
or a
jp p, Sound_ReadStreamData_CalcNoteHold
; copy the next tone duration to the current
ld a, (ix + Sound_ChnlToneDuration_Next)
ld (ix + Sound_ChnlToneDuration), a
dec de
jr ++
; calculate note duration based on the channel's tempo value
Sound_ReadStreamData_CalcNoteHold: ; $83A6
; multiply the ticks-per-note value by the speed value
call Sound_MultiplyA
ld (ix + Sound_ChnlToneDuration), a
ld (ix + Sound_ChnlToneDuration_Next), a
++: ; store the data pointer
ld (ix + Sound_ChnlNextCommand), e
ld (ix + Sound_ChnlNextCommand + 1), d
; bail out if we need to preserve effect state
bit Sound_ChnlKeepStateBit, (ix + Sound_ChnlControl)
ret nz
bit Sound_ChnlVolumeOnlyBit, (ix + Sound_ChnlControl)
jr nz, +
res 5, (ix + Sound_ChnlControl)
+: ld a, (ix + $0F)
ld (ix + $0E), a
; clear pitch bend length / pitch envelope index value
xor a
ld (ix + Sound_PitchBendLength), a
; check the volume effect flags
bit Sound_VolPreserveBit, (ix + Sound_VolumeEffectFlags)
ret nz
ld (ix + Sound_ChnlVolume), a
ret
Sound_ReadStreamData_Rest: ; $83D7
call LABEL_85F8_134
jr Sound_ReadStreamData_SetNoteHold
; =============================================================================
; Sound_ReadLiteral(uint8 tone_lo_byte, uint16 src_ptr, uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Reads 16-bit, big-endian, tone data from the stream.
; -----------------------------------------------------------------------------
; In:
; A - high-byte of the 16bit tone value.
; DE - Current data pointer (points into sound data).
; IX - Channel pointer.
; Out:
; A - Next byte of data from the stream.
; DE - Pointer to the byte immediately following A.
; Destroys:
; BC, HL
; -----------------------------------------------------------------------------
Sound_ReadLiteral: ;$83DC
; read another byte and copy the 16-bit value into HL
; NOTE: BIG ENDIAN READ
ld h, a ;<-- 'low' byte is copied into H
ld a, (de)
inc de
; if the value is 0 don't bother adjusting
ld l, a ;<-- 'high' byte is copied into L
or h
jr z, +
; apply the pitch adjustment
ld b, $00
; retrieve the tone adjustment value from
; the channel structure
ld a, (ix + Sound_ChnlPitchAdjust)
or a
ld c, a
; check the accumulator's sign bit.
; if the bit is set we need to sign extend into
; the B register
jp p, ++
dec b
++: ; adjust the tone value
add hl, bc
+: ; store the tone data
ld (ix + Sound_ChnlToneData), l
ld (ix + Sound_ChnlToneData+1), h
; read another byte of data and increment the pointer
ld a, (de)
inc de
jp Sound_ReadStreamData_CalcNoteHold
; =============================================================================
; Sound_ResetAll()
; -----------------------------------------------------------------------------
; Clears all memory used by the sound driver and initialises the PSG.
; -----------------------------------------------------------------------------
; In:
; None
; Out:
; None.
; Destroys:
; None.
; -----------------------------------------------------------------------------
Sound_ResetAll: ; $83FA
push hl
push bc
push de
; reset all control bytes and channel structures
ld hl, $DD03
ld de, $DD04
ld bc, $018C
ld (hl), $00
ldir
pop de
pop bc
pop hl
; fall through
; =============================================================================
; Sound_InitPSG()
; -----------------------------------------------------------------------------
; Initialises the PSG registers with default values.
; -----------------------------------------------------------------------------
; In:
; None
; Out:
; None.
; Destroys:
; None.
; -----------------------------------------------------------------------------
Sound_InitPSG: ;$840D
push hl
push bc
; write 10 bytes to the PSG
ld hl, Sound_Data_DefaultRegisterValues
ld b, $0A
ld c, Ports_PSG
otir
pop bc
pop hl
jp Sound_SetActive
Sound_Data_DefaultRegisterValues:
.db Sound_PSG_Latch | Sound_PSG_Tone0, $00 ;set tone 0 = 0
.db Sound_PSG_Latch | Sound_PSG_Tone1, $00 ;set tone 1 = 0
.db Sound_PSG_Latch | Sound_PSG_Tone2, $00 ;set tone 2 = 0
.db Sound_PSG_Latch | Sound_PSG_Type_Volume | Sound_PSG_Tone0 | $0F
.db Sound_PSG_Latch | Sound_PSG_Type_Volume | Sound_PSG_Tone1 | $0F
.db Sound_PSG_Latch | Sound_PSG_Type_Volume | Sound_PSG_Tone2 | $0F
.db Sound_PSG_Latch | Sound_PSG_Type_Volume | Sound_PSG_Noise | $0F
; =============================================================================
; Sound_GetNoteValue(uint8 note_index)
; -----------------------------------------------------------------------------
; Fetches the PSG tone counter register value for the specified note index.
; -----------------------------------------------------------------------------
; In:
; A - Note number.
; Out:
; HL - Note value.
; Destroys:
; BC
; -----------------------------------------------------------------------------
Sound_GetNoteValue: ; $8427
and $7F
add a, (ix + Sound_ChnlPitchAdjust)
ld hl, Sound_Data_NoteValues
; FALL THROUGH
; =============================================================================
; Sound_CalcIndex_Int16(uint16 array_base, uint8 index)
; -----------------------------------------------------------------------------
; Get an element from an array of 16-bit integers.
; -----------------------------------------------------------------------------
; In:
; A - Element index.
; HL - Base address of the array.
; Out:
; HL - Element value.
; Destroys:
; BC
; -----------------------------------------------------------------------------
Sound_CalcIndex_Int16: ; $842F
ld c, a
ld b, $00
add hl, bc
add hl, bc
ld c, (hl)
inc hl
ld h, (hl)
ld l, c
ret
; =============================================================================
; Sound_CalcIndex_Int8(uint32 array_base, uint8 index)
; -----------------------------------------------------------------------------
; Calculate a pointer to an element in an array of bytes.
; -----------------------------------------------------------------------------
; In:
; C - Element index.
; HL - Base address of the array.
; Out:
; A - Element value.
; HL - Pointer to element within the array.
; Destroys:
; BC
; -----------------------------------------------------------------------------
Sound_CalcIndex_Int8: ; $8439
ld b, $00
add hl, bc
ld a, (hl)
ret
; =============================================================================
; Sound_MultiplyA(uint8 multiplicand, uint8 multiplier)
; -----------------------------------------------------------------------------
; Performs 8-bit multiplication with the speed value stored in the channel.
; -----------------------------------------------------------------------------
; In:
; A - Multiplicand
; (ix + 2) - Multiplier
; Out:
; A - Value
; Destroys:
; BC
; -----------------------------------------------------------------------------
Sound_MultiplyA: ; $843E
; fetch the multiplier and check that it's not 1
ld b, (ix + Sound_ChnlSpeed)
dec b
ret z
; multiply using repeated add
ld c, a
-: add a, c
djnz -
ret
; PSG values approximately corresponding to MIDI notes
Sound_Data_NoteValues: ; $8448
.include "src/sound_note_values.asm"
;.include "src/sound_note_values_tuned.asm"
; =============================================================================
; Sound_UpdateToneChannel(uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Runs the specified tone channel for 1 tick.
; -----------------------------------------------------------------------------
; In:
; IX - The channel pointer.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_UpdateToneChannel: ; $84D4
; decrement the note duration counter
dec (ix + Sound_ChnlToneDuration)
; if the duration counter is > 0 we dont need to bother fetching
; new data but we may need to update a tone effect
jr nz, +
; duration counter == 0
; fetch data from the stream
call Sound_ReadChannelStream
; check that PSG volume writes are not suppressed
bit Sound_ChnlVolSuppressBit, (ix + Sound_ChnlControl)
ret nz
; check that the channel hasn't been suppressed by
; a sound effect module
bit Sound_ChannelSuppressBit, (ix + Sound_ChnlControl)
ret nz
; load any pitch effects
call Sound_LoadPitchBendData
call LABEL_82B3_142
; and write to the PSG
jr Sound_WriteChannelData
+: ; check to see if PSG writes should be suppressed
bit Sound_ChannelSuppressBit, (ix + Sound_ChnlControl)
ret nz
ld a, (ix + $0E)
or a
jr z, +
dec (ix + $0E)
call z, LABEL_85F8_134
+: ld a, (ix + Sound_ToneEffectFlags)
or a
jr z, Sound_UpdateChannelVolume
bit 5, (ix + Sound_ChnlControl)
jr nz, Sound_UpdateChannelVolume
; FALL THROUGH
; =============================================================================
; Sound_WriteChannelData(uint16 channel_ptr, uint16 channel_data)
; -----------------------------------------------------------------------------
; Writes data to the PSG and updates the volume.
; -----------------------------------------------------------------------------
; In:
; IX - Pointer to a channel descriptor.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_WriteChannelData: ;$850B
; check to see if we need to update volume only
bit Sound_ChnlVolumeOnlyBit, (ix + Sound_ChnlControl)
jr nz, Sound_UpdateChannelVolume
; apply any pitch effects
call Sound_ApplyToneEffect
; fetch the detune value
ld d, $00
ld a, (ix + Sound_ChnlDetune)
or a
; check the value's sign and extend into D if necessary
jp p, ++
dec d
++: ; add the detune value to the data
ld e, a
add hl, de
; get the current channel number
ld a, (ix + Sound_ChnlNumber)
; jump if it's a tone channel
cp Sound_PSG_Latch | Sound_PSG_Noise
jr nz, ++
; if we get here, the channel is a noise channel
; change to channel 2 instead
ld a, Sound_PSG_Latch | Sound_PSG_Tone2
++: ; store the channel number in the C register
ld c, a
; get the low 4-bits of data and combine them with the channel number
; to create a PSG latch byte
ld a, l
and $0F
or c
; write the latch byte to the PSG
out (Ports_PSG), a
; extract the upper 4 bits and combine with the second byte then
; rotate into the correct position
ld a, l
and $F0
or h
rrca
rrca
rrca
rrca
; write the data byte
out (Ports_PSG), a
; FALL THROUGH
; =============================================================================
; Sound_UpdateChannelVolume(uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Copies a channel's volume data to the PSG.
; -----------------------------------------------------------------------------
; In:
; IX - Pointer to a channel descriptor.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_UpdateChannelVolume: ; $853A
; grab any volume effect data
call Sound_CheckApplyVolumeEffect
; check that PSG writes are not suppressed
bit Sound_ChannelSuppressBit, (ix + Sound_ChnlControl)
ret nz
; check that volume updates are not suppressed
bit Sound_ChnlVolSuppressBit, (ix + Sound_ChnlControl)
ret nz
; add the channel's volume adjustment value to the effect
add a, (ix + Sound_ChnlVolumeAdjust)
; make sure that we don't overflow the 4 data bits
bit 4, a
jr z, +
ld a, $0F
+: ; fetch channel number from the structure
or (ix + Sound_ChnlNumber)
; set the "type" bit to volume
add a, Sound_PSG_Type_Volume
; write to the PSG
out (Ports_PSG), a
ret
; =============================================================================
; Sound_CheckApplyVolumeEffect(uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Checks the channel's volume effect flags and applies an affect as
; necessary.
; -----------------------------------------------------------------------------
; In:
; IX - Pointer to a channel descriptor.
; Out:
; A - Effect data to be added to volume adjustment.
; Destroys:
;
; -----------------------------------------------------------------------------
Sound_CheckApplyVolumeEffect: ; $8558
; get the effect flags from the channel structure
ld a, (ix + Sound_VolumeEffectFlags)
or a
; if flags == 0 no effect should be applied
ret z
; if MSB is reset apply a volume envelope
jp p, Sound_ApplyEnvelope
; check the "fade in" bit
bit 4, (ix + $1D)
jr z, LABEL_8580_159
; fade volume in using effect value
ld d, (ix + Sound_ChnlVolEffectAdjust) ;get volume adjustment value
ld a, (ix + Sound_ChnlVolume) ;get current volume value
sub d
jr nc, +
xor a ;resulting volume was < 0. reset to 0
+: or a
ld (ix + Sound_ChnlVolume), a ;set current volume
jr nz, LABEL_85EE_161
; attenuation is now at zero. turn the "fade out"
; bit off. set bit 5
ld a, (ix + $1D)
xor $30 ;flip bits 4 & 5
ld (ix + $1D), a
jr LABEL_85EE_161
LABEL_8580_159:
bit 5, (ix + $1D)
jr z, LABEL_85B0_162
ld a, (ix + Sound_ChnlVolume)
ld d, (ix + $21)
ld e, (ix + $22)
add a, d
jr c, +
cp e
jr c, ++
+: ld a, e
++: cp e
ld (ix + Sound_ChnlVolume), a
jr nz, LABEL_85EE_161
ld a, (ix + $1D)
bit 3, (ix + $1D)
jr z, +
xor $30
jr ++
+: xor $60
++: ld (ix + $1D), a
jr LABEL_85EE_161
LABEL_85B0_162:
bit 6, (ix + $1D)
jr z, LABEL_85D2_167
; fetch the current volume & adjustment value
ld a, (ix + Sound_ChnlVolume)
ld d, (ix + $23)
; add the adjustment value (increase attenuation)
add a, d
jr nc, +
; there was a carry - set volume off
ld a, $FF
+: cp $FF
ld (ix + Sound_ChnlVolume), a
jr nz, LABEL_85EE_161
; if attenuation == $FF
ld a, (ix + $1D)
; turn off bits 4, 5 & 6 of the volume effects flags
and $8F
ld (ix + $1D), a
jr LABEL_85EE_161
LABEL_85D2_167:
ld a, (ix + Sound_ChnlVolume)
ld d, (ix + $24)
add a, d
jr nc, +
ld a, (ix + $1D)
and $0F
ld (ix + $1D), a
ld a, $FF
ld (ix + Sound_ChnlVolume), a
jp Sound_SetVolumeOff
+: ld (ix + Sound_ChnlVolume), a
; FALL THROUGH
LABEL_85EE_161:
ld a, (ix + Sound_ChnlVolume)
rrca
rrca
rrca
rrca
and $0F
ret
LABEL_85F8_134:
; bail out if we need to preserve effect state
bit Sound_ChnlKeepStateBit, (ix + Sound_ChnlControl)
ret nz
; check to see if a volume effect is being used
bit Sound_VolPreserveBit, (ix + Sound_VolumeEffectFlags)
; jump if there isnt
jp z, Sound_SetVolumeOff
; clear bits 4,5 & 6, set bit 7
ld a, (ix + $1D)
and $0F
or $80
ld (ix + $1D), a
ret
; apply a volume envelope
Sound_ApplyEnvelope: ; $860F
dec a
ld hl, Sound_Data_VolumeEnvelopes
call Sound_CalcIndex_Int16
jr Sound_ApplyEnvelope_GetData
Sound_ApplyEnvelope_SetIndex: ; $8175
; set the index
ld (ix + Sound_VolEnvlpIndex), a
Sound_ApplyEnvelope_GetData: ; $861B
; fetch the effect value from the array addressed by HL
; using (IX+$1F) as an index
push hl
ld c, (ix + Sound_VolEnvlpIndex)
call Sound_CalcIndex_Int8
pop hl
; if effect value < $80
bit 7, a
jr z, Sound_ApplyEnvelope_NextIndex
; if value > $80 it is a command byte
cp $82
jr z, Sound_ApplyEnvelope_SetVolumeOff
cp $81
jr z, Sound_ApplyEnvelope_HoldVolume
cp $80
jr z, Sound_ApplyEnvelope_ResetIndex
; any command bytes >= $83 are a "set index" command
inc de
ld a, (de)
jr Sound_ApplyEnvelope_SetIndex
Sound_ApplyEnvelope_SetVolumeOff: ; $8637
; suppress psg writes
; this SET op seems a bit pointless since Sound_SetVolumeOff
; does the same thing
set Sound_ChnlVolSuppressBit, (ix + Sound_ChnlControl)
; CHECK: what is this POPping?
pop hl
jr Sound_SetVolumeOff
Sound_ApplyEnvelope_ResetIndex: ; $863E
xor a
jr Sound_ApplyEnvelope_SetIndex
Sound_ApplyEnvelope_HoldVolume: ; $8641
; suppress volume writes
set Sound_ChnlVolSuppressBit, (ix + Sound_ChnlControl)
pop hl
ret
Sound_ApplyEnvelope_NextIndex: ; $8647
inc (ix + Sound_VolEnvlpIndex)
ret
; =============================================================================
; Sound_SetVolumeOff()
; -----------------------------------------------------------------------------
; Sets PSG volume attenuation to full without affecting volume state
; variables.
; -----------------------------------------------------------------------------
; In:
; None.
; Out:
; None.
; Destroys:
; A
; -----------------------------------------------------------------------------
Sound_SetVolumeOff: ; $864B
set Sound_ChnlVolSuppressBit, (ix + Sound_ChnlControl)
; are PSG writes suppressed?
bit Sound_ChannelSuppressBit, (ix + Sound_ChnlControl)
ret nz
; set volume attenuation to full
ld a, Sound_PSG_Type_Volume | $0F
add a, (ix + Sound_ChnlNumber)
out (Ports_PSG), a
ret
; =============================================================================
; Sound_UpdateNoiseChannel(uint16 channel_ptr)
; -----------------------------------------------------------------------------
; Runs the noise channel for one tick.
; -----------------------------------------------------------------------------
; In:
; IX - The channel pointer.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_UpdateNoiseChannel: ; $865C
; decrement counter
dec (ix + Sound_ChnlToneDuration)
; update volume if counter != 0
jp nz, Sound_UpdateChannelVolume
; re-enable PSG volume writes
res Sound_ChnlVolSuppressBit, (ix + Sound_ChnlControl)
; fetch the pointer to the next command
ld e, (ix + Sound_ChnlNextCommand)
ld d, (ix + Sound_ChnlNextCommand + 1)
-: ; fetch a byte of data from the source address
ld a, (de)
inc de
; is the byte a command byte?
cp $E0
; jump if it is
jr nc, Sound_UpdateNoiseChannel_InterpretCommand
; jump if A < $80
cp $80
jp c, Sound_ReadStreamData_CalcNoteHold
; if we get here, A is between $80 and $E0
; does something with noise data
call LABEL_8686_180
jp Sound_ReadStreamData_SetNoteHold
Sound_UpdateNoiseChannel_InterpretCommand: ; $867D
ld hl, Sound_UpdateNoiseChannel_ReturnStub
jp Sound_InterpretCommand
Sound_UpdateNoiseChannel_ReturnStub: ; $8663
inc de
jr -
LABEL_8686_180:
; TODO: since accumulator value is not used after the
; jump, using RRCA here would be cheaper
bit 0, a
jr nz, LABEL_86C9_181
bit 1, a
jr nz, LABEL_86A9_182
bit 2, a
jr nz, LABEL_86C1_183
bit 3, a
jr nz, LABEL_86A1_184
bit 4, a
jr nz, LABEL_86B9_185
bit 5, a
jr nz, LABEL_86B1_186
jp Sound_SetVolumeOff
LABEL_86A1_184:
ld a, $03 ; volume envelope
ld b, $02 ; volume adjustment
ld c, Sound_PSG_Latch | Sound_PSG_Noise | 4 ; white noise, reset to $10
jr LABEL_86CF_187
LABEL_86A9_182:
ld a, $03
ld b, $02
ld c, Sound_PSG_Latch | Sound_PSG_Noise | 4 ; white noise, reset to $10
jr LABEL_86CF_187
LABEL_86B1_186:
ld a, $04
ld b, $04
ld c, Sound_PSG_Latch | Sound_PSG_Noise | 4 ; white noise, reset to $10
jr LABEL_86CF_187
LABEL_86B9_185:
ld a, $03
ld b, $03
ld c, Sound_PSG_Latch | Sound_PSG_Noise | 6 ; white noise, reset to $40
jr LABEL_86CF_187
LABEL_86C1_183:
ld a, $02
ld b, $02
ld c, Sound_PSG_Latch | Sound_PSG_Noise | 5 ; white noise, reset to $20
jr LABEL_86CF_187
LABEL_86C9_181:
ld a, $01
ld b, $02
ld c, Sound_PSG_Latch | Sound_PSG_Noise | 4 ; white noise, reset to $10
LABEL_86CF_187:
ld (ix + Sound_VolumeEffectFlags), a
; adjust the noise channel attenuation
ld a, (Sound_NoiseChnlVolume)
add a, b
ld (ix + Sound_ChnlVolumeAdjust), a
; are PSG writes suppressed?
bit Sound_ChannelSuppressBit, (ix + Sound_ChnlControl)
ret nz
; set the noise channel data
ld a, ($DD15)
add a, c
ld ($DD11), a
out (Ports_PSG), a
ret
; =============================================================================
; Sound_InterpretCommand_WithReturn(uint8 command_value, uint16 data_ptr)
; -----------------------------------------------------------------------------
; Interprets a command and calls the relevant subroutine. Control will
; be returned to the Sound_ReadStreamData function.
; -----------------------------------------------------------------------------
; In:
; A - The command value.
; DE - Sound data pointer.
; IX - Channel pointer.
; Out:
;
; Destroys:
;
; -----------------------------------------------------------------------------
Sound_InterpretCommand_WithReturn: ; $86E8
; load this function pointer as a return address
ld hl, Sound_InterpretCommand_ReturnStub
; FALL THROUGH
; =============================================================================
; Sound_InterpretCommand(uint8 cmd, uint16 data, unit16 chnl, uint16 rtrn)
; -----------------------------------------------------------------------------
; Interprets a command and calls the relevant subroutine.
; -----------------------------------------------------------------------------
; In:
; A - The command value.
; DE - Sound data pointer.
; IX - Channel pointer.
; HL - Return address for the command function.
; Out:
; None.
; -----------------------------------------------------------------------------
Sound_InterpretCommand: ; $86EB
; push a function pointer to the stack so that
; the indirect jump has a valid return address
push hl
; use A as an index into the array of functions
sub $E0
ld hl, Sound_CommandDispatchTable
add a, a
ld c, a
ld b, $00
add hl, bc
; fetch the function pointer from the array.
ld c, (hl)
inc hl
ld h, (hl)
ld l, c
; load the next data byte into A
ld a, (de)
; jump to the function.
jp (hl)
; This stub will return control to the Sound_ReadStreamData function
Sound_InterpretCommand_ReturnStub: ;$86FC
inc de
jp Sound_ReadStreamData
Sound_CommandDispatchTable: ;$8700
.dw LABEL_88BB ; $E0
.dw Sound_Command_Detune ; $E1
.dw Sound_Command_AdjustToneVolume ; $E2
.dw Sound_Command_DoNothing ; $E3
.dw Sound_Command_AdjustNoiseVolume ; $E4
.dw LABEL_88D5 ; $E5
.dw Sound_Command_AdjustToneVolume ; $E6
.dw Sound_Command_RepeatNote ; $E7
.dw LABEL_8892 ; $E8
.dw Sound_Command_DoNothing ; $E9
.dw Sound_Command_DoNothing ; $EA
.dw Sound_Command_DoNothing ; $EB
.dw Sound_Command_DoNothing ; $EC
.dw Sound_Command_SetGlobalSpeed ; $ED
.dw Sound_Command_DoNothing ; $EE
.dw Sound_Data_PitchEnvelopes ; $EF - invalid command
.dw Sound_Command_PitchBend ; $F0
.dw Sound_Data_PitchEnvelopes ; $F1 - invalid command
.dw LABEL_87DB ; $F2
.dw LABEL_8766 ; $F3
.dw Sound_Command_PitchEnvelope ; $F4
.dw Sound_Command_VolumeEnvelope ; $F5
.dw Sound_Command_Jump ; $F6
.dw Sound_Command_ConditionalJump ; $F7
.dw Sound_Command_Branch ; $F8
.dw Sound_Command_Return ; $F9
.dw Sound_Command_SetChnlSpeed ; $FA
.dw Sound_Command_PitchAdjust ; $FB
.dw Sound_Command_DoNothing ; $FC
.dw Sound_Command_ReadLiteral ; $FD - set/reset reading of literal tone data
.dw Sound_Command_DoNothing ; $FE
.dw Sound_Command_DoNothing ; $FF
Sound_Command_AdjustToneVolume: ; $8740
; check to see if the sound is fading out
ex af, af'
ld a, (Sound_FadeMajorCount)
or a
ret nz
ex af, af'
; adjust tone volume attenuation
add a, (ix + Sound_ChnlVolumeAdjust)
; cap at 15
and $0F
ld (ix + Sound_ChnlVolumeAdjust), a
; FALL THROUGH
Sound_Command_DoNothing: ; $874F
ret
Sound_Command_AdjustNoiseVolume: ; $8750
ld c, a
ld a, (Sound_NoiseChnlVolume)
add a, c
; cap attenuation at 15
and $0F
ld (Sound_NoiseChnlVolume), a
ret
Sound_Command_PitchAdjust: ; $875B
add a, (ix + Sound_ChnlPitchAdjust)
ld (ix + Sound_ChnlPitchAdjust), a
ret
Sound_Command_SetChnlSpeed: ; $8762
ld (ix + Sound_ChnlSpeed), a
ret
LABEL_8766:
; check the SFX index. jump if the MSB is set.
ex af, af'
ld a, (Sound_SFXIndex)
or a
; jump if SFX is playing
jp m, LABEL_878F
ex af, af'
or $FC
inc a
jr nz, LABEL_878F
; load pointer to secondary tone1 channel
ld hl, $DE30
; check to see if the channel is active
bit 7, (hl)
; jump if it isn't
jr z, LABEL_878F
; CHECK: is this offset in the noise channel?
ld hl, $DDD0
res 2, (hl)
set 4, (hl)
; clear the channel's control byte
xor a
ld (ix + Sound_ChnlControl), a
; write $FF to the PSG (set noise attenuation to $F)
dec a
out (Ports_PSG), a
; write $FF to the sfx index
ld (Sound_SFXIndex), a
pop hl
pop hl
ret
LABEL_878F:
; read a byte of data from the stream and
; write it to the PSG
ld a, (de)
out (Ports_PSG), a
; get a pointer to primary tone2 channel
ld hl, $DDA0
; get a pointer to secondary tone1 channel
ld iy, $DE30
or $FC
inc a
jr nz, LABEL_878D
; latch tone 2 and set volume to $F
ld a, $DF
out (Ports_PSG), a
; reset the "volume only" flag for the current channel
res Sound_ChnlVolumeOnlyBit, (ix + Sound_ChnlControl)
; suppress PSG writes for primary channel2
set 2, (hl)
; suppress PSG writes for secondary channel1
set 2, (iy + Sound_ChnlControl)
ret
LABEL_878D:
; set "volume updates only" flag for the current channel
set Sound_ChnlVolumeOnlyBit, (ix + Sound_ChnlControl)
; jump if secondary channel 1 is active
bit 7, (iy + $00)
jr nz, LABEL_87BA
; clear "suppress PSG writes" flag for primary channel2
res 2, (hl)
ret
LABEL_87BA:
; clear "suppress PSG writes" flag for secondary channel1
res 2, (iy + Sound_ChnlControl)
ret
Sound_Command_VolumeEnvelope: ; $87BF
; store the envelope index in the volume effect flags
ld (ix + Sound_VolumeEffectFlags), a
ret
Sound_Command_PitchEnvelope: ; $87C3
; store the envelope index in the tone effect flags
ld (ix + Sound_ToneEffectFlags), a
ret
Sound_Command_Jump: ; $87C7
; retrieve the new pointer from the source data
ex de, hl
ld e, (hl)
inc hl
ld d, (hl)
; decrement the pointer since the next instruction
; after the RET will INC DE.
dec de
ret
; these 2 functions set and clear the 16bit read flag
; in the channel control byte
Sound_Command_ReadLiteral: ; $87CD
cp $01
jr nz, +
set Sound_ChnlReadLiteralBit, (ix + Sound_ChnlControl)
ret
+: res Sound_ChnlReadLiteralBit, (ix + Sound_ChnlControl)
ret
LABEL_87DB:
; fetch the channel number
ld a, (ix + Sound_ChnlNumber)
; jump tone channel 1
cp Sound_PSG_Latch | Sound_PSG_Tone1 ;$A0
jr z, LABEL_881A
; jump if tone channel 2
cp Sound_PSG_Latch | Sound_PSG_Tone2 ;$C0
jr z, LABEL_880E
bit Sound_ChnlVolumeOnlyBit, (ix + Sound_ChnlControl)
jr nz, +
; load HL with pointer to secondary tone 2 channel
ld hl, $DE30
; jump if channel is active
bit 7, (hl)
jp nz, +
; load HL with pointer to primary tone 2 channel
ld hl, $DDA0
res 2, (hl)
res 6, (hl)
set 4, (hl)
; clear the secondary tone2 channel's control byte
ld hl, $DE30
ld (hl), $00
+: push af
; read noise channel data?
ld a, ($DD11)
out (Ports_PSG), a
pop af
ld hl, $DDD0
jr LABEL_881D
LABEL_880E:
; check to see if secondary tone2 channel is active
ld hl, $DE60
bit 7, (hl)
; jump if it is
jr nz, LABEL_881D
; load HL with a pointer to the primary tone2 channel
ld hl, $DDA0
jr LABEL_881D
LABEL_881A:
; load HL with pointer to primary tone1 channel
ld hl, $DD70
LABEL_881D:
res 2, (hl)
set 4, (hl)
; turn channel volume off
or $1F
out (Ports_PSG), a
; clear the channel control byte (disable channel)
xor a
ld (ix + Sound_ChnlControl), a
ld ix, $DE00
bit 7, (ix+$00)
jr nz, +
ld ix, $DE30
bit 7, (ix+$00)
jr nz, +
ld ix, $DE60
bit 7, (ix+$00)
jr nz, +
; clear the priority value
xor a
ld (Sound_Priority), a
+: pop bc
pop bc
ret
Sound_Command_Branch: ; $884E
; read new data pointer into BC
ld c, a
inc de
ld a, (de)
ld b, a
; push the new data pointer onto the main stack
push bc
; load the channel structure pointer into HL
push ix
pop hl
; read the channel stack pointer into BC and
; decrement it by 2 (we'll be pushing a word
; onto the channel stack).
dec (ix + Sound_ChnlStackPointer)
ld c, (ix + Sound_ChnlStackPointer)
dec (ix + Sound_ChnlStackPointer)
ld b, $00
; calculate the absolute address for the calculated
; channel stack pointer
add hl, bc
; store the current data pointer in the channel stack
ld (hl), d
dec hl
ld (hl), e
; restore the new data pointer into DE
pop de
; decrement DE since the next instruction after the RET
; will increment DE
dec de
ret
Sound_Command_Return: ; $8868
; load channel pointer into HL
push ix
pop hl
; load channel stack pointer into BC
ld c, (ix + Sound_ChnlStackPointer)
ld b, $00
; calculate absolute address of channel
; stack pointer
add hl, bc
; read old data pointer back into DE
ld e, (hl)
inc hl
ld d, (hl)
; restore the stack pointer
inc (ix + Sound_ChnlStackPointer)
inc (ix + Sound_ChnlStackPointer)
ret
Sound_Command_ConditionalJump: ; $887B
inc de
; loop counter memory starts at offset IX+$27
add a, Sound_ChnlLoopCounters
ld c, a
ld b, $00
; load the channel structure pointer into HL
push ix
pop hl
; adjust the pointer to the correct offset
add hl, bc
; read the loop counter from the channel structure
ld a, (hl)
or a
jr nz, +
; if the counter is zero this must be the first iteration
; of the loop. we need to read the counter value from the
; source data
ld a, (de)
ld (hl), a
+: inc de
; decrement the loop counter
dec (hl)
; branch while the counter != 0
jp nz, Sound_Command_Jump
inc de
ret
LABEL_8892:
call Sound_MultiplyA
ld (ix + $0E), a
ld (ix + $0F), a
ret
Sound_Command_PitchBend: ; $889C
; store the effect data pointer in the channel structure
ld (ix + Sound_PitchBendDataPtr), e
ld (ix + Sound_PitchBendDataPtr + 1), d
; set the effect flag
ld (ix + Sound_ToneEffectFlags), Sound_ChnlPitchBend
; move the data pointer past the parameter bytes
inc de
inc de
inc de
ret
Sound_Command_RepeatNote: ; $88AA
set Sound_ChnlKeepStateBit, (ix + Sound_ChnlControl)
dec de
ret
Sound_Command_Detune: ;$88B0
ld (ix + Sound_ChnlDetune), a
ret
Sound_Command_SetGlobalSpeed: ;$88B4
ld (Sound_Speed), a
ld (Sound_SpeedCounter), a
ret
LABEL_88BB:
ld (ix + Sound_VolumeEffectFlags), Sound_VolPreserve
; HL = pointer to channel structure
push ix
pop hl
; adjust the pointer to IX + $20
ld b, $00
ld c, $20
add hl, bc
; copy 5 bytes into the channel structure
ex de, hl
ldi
ldi
ldi
ldi
ldi
ex de, hl
dec de
ret
LABEL_88D5:
or a
jr z, +
ld a, $08
+: ld (ix + $1E), a
ret
Sound_Data_PitchEnvelopes: ; $88DE
.include "src/sound_pitch_effects.asm"
; volume envelopes
Sound_Data_VolumeEnvelopes: ; $8A2F
.include "src/sound_volume_effects.asm"
Sound_Data_Priorities:
.incbin "sound\sound_priorities.bin"
; -----------------------------------------------------------------------------
; Speed values for each music module. Note that these values are referenced
; but not used. The speed value is stored in the module and any values
; copied from here are overwritten later, when the module is loaded.
; See Sound_LoadMusicModule() for more.
; -----------------------------------------------------------------------------
Sound_Data_MusicSpeeds:
.db $03, $00, $05, $00, $00, $03, $00, $00
.db $03, $04, $03, $03, $05, $05, $00, $05
.db $03, $05, $07
Sound_Data_MusicPointers: ; $8BE7
.dw Sound_Data_Music_ALZ
.dw Sound_Data_Music_UGZ
.dw Sound_Data_Music_GMZ
.dw Sound_Data_Music_CEZ
.dw Sound_Data_Music_GHZ
.dw Sound_Data_Music_SHZ
.dw Sound_Data_Music_SEZ
.dw Sound_Data_Music_Intro
.dw Sound_Data_Music_Boss
.dw Sound_Data_Music_SpeedShoes
.dw Sound_Data_Music_Invincibility
.dw Sound_Data_Music_EndOfLevel
.dw Sound_Data_Music_LoseLife
.dw Sound_Data_Music_Continue
.dw Sound_Data_Music_Emerald
.dw Sound_Data_Music_GameOver
.dw Sound_Data_Music_Ending
.dw Sound_Data_Music_Unknown
.dw Sound_Data_Music_TitleCard
Sound_Data_SFXPointers: ; $8C0D
.dw Sound_Data_SFX_Ring ; $00
.dw Sound_Data_SFX_Roll ; $01
.dw Sound_Data_SFX_Spring ; $02
.dw Sound_Data_SFX_Jump ; $03
.dw Sound_Data_SFX_04
.dw Sound_Data_SFX_05
.dw Sound_Data_SFX_06
.dw Sound_Data_SFX_07
.dw Sound_Data_SFX_08
.dw Sound_Data_SFX_09
.dw Sound_Data_SFX_0A
.dw Sound_Data_SFX_0B
.dw Sound_Data_SFX_0C
.dw Sound_Data_SFX_0D
.dw Sound_Data_SFX_0E
.dw Sound_Data_SFX_0F
.dw Sound_Data_SFX_10
.dw Sound_Data_SFX_11
.dw Sound_Data_SFX_12
.dw Sound_Data_SFX_13
.dw Sound_Data_SFX_14
.dw Sound_Data_SFX_15
.dw Sound_Data_SFX_16
.dw Sound_Data_SFX_17
.dw Sound_Data_SFX_18
.dw Sound_Data_SFX_19
.dw Sound_Data_SFX_1A
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
.dw Sound_Data_SFX_1B
Sound_Data_Music_ALZ: ; $8C59
;.incbin "sound/music_alz.bin"
.include "sound/music_alz.asm"
Sound_Data_Music_UGZ: ; $8F98
;.incbin "sound/music_ugz.bin"
.include "sound/music_ugz.asm"
Sound_Data_Music_GMZ: ; $94DD
;.incbin "sound/music_gmz.bin"
.include "sound/music_gmz.asm"
Sound_Data_Music_CEZ: ; $9819
;.incbin "sound/music_cez.bin"
.include "sound/music_cez.asm"
Sound_Data_Music_GHZ: ; $9B99
;.incbin "sound/music_ghz.bin"
.include "sound/music_ghz.asm"
Sound_Data_Music_SHZ: ; $A053
;.incbin "sound/music_shz.bin"
.include "sound/music_shz.asm"
Sound_Data_Music_SEZ: ; $A2CE
;.incbin "sound/music_sez.bin"
.include "sound/music_sez.asm"
Sound_Data_Music_Intro: ; $A7C6
;.incbin "sound/music_intro.bin"
.include "sound/music_intro.asm"
;.include "sound/music_starlight.asm"
Sound_Data_Music_Boss: ; $AA00
;.incbin "sound/music_boss.bin"
.include "sound/music_boss.asm"
Sound_Data_Music_SpeedShoes: ; $AB9A
;.incbin "sound/music_speed_shoes.bin"
.include "sound/music_speed_shoes.asm"
Sound_Data_Music_Invincibility: ; $AC88
;.incbin "sound/music_invincibility.bin"
.include "sound/music_invincibility.asm"
Sound_Data_Music_EndOfLevel: ; $AE1E
;.incbin "sound/music_end_of_level.bin"
.include "sound/music_end_of_level.asm"
Sound_Data_Music_LoseLife: ; $AF1C
;.incbin "sound/music_lose_life.bin"
.include "sound/music_lose_life.asm"
Sound_Data_Music_Continue: ; $AFA4
;.incbin "sound/music_continue.bin"
.include "sound/music_continue.asm"
Sound_Data_Music_Emerald: ; $B0BE
;.incbin "sound/music_emerald.bin"
.include "sound/music_emerald.asm"
Sound_Data_Music_GameOver: ; $B176
;.incbin "sound/music_gameover.bin"
.include "sound/music_gameover.asm"
Sound_Data_Music_Ending: ; $B232
;.incbin "sound/music_ending.bin"
.include "sound/music_ending.asm"
Sound_Data_Music_Unknown: ; $B946
;.incbin "sound/music_unknown.bin"
.include "sound/music_unknown.asm"
Sound_Data_Music_TitleCard: ; $BA90
;.incbin "sound/music_titlecard.bin"
.include "sound/music_titlecard.asm"
Sound_Data_SFX_Ring: ; $BAEA
;.incbin "sound/sfx_ring.bin"
.include "sound/sfx_ring.asm"
Sound_Data_SFX_Roll: ; $BB07
;.incbin "sound/sfx_roll.bin"
.include "sound/sfx_roll.asm"
Sound_Data_SFX_Spring: ; $BB1F
;.incbin "sound/sfx_spring.bin"
.include "sound/sfx_spring.asm"
Sound_Data_SFX_Jump: ; $BB45
;.incbin "sound/sfx_jump.bin"
.include "sound/sfx_jump.asm"
Sound_Data_SFX_04: ; $BB6C
;.incbin "sound/sfx_04.bin"
.include "sound/sfx_04.asm"
Sound_Data_SFX_05: ; $BB89
;.incbin "sound/sfx_05.bin"
.include "sound/sfx_05.asm"
Sound_Data_SFX_06: ; $BBB8
;.incbin "sound/sfx_06.bin"
.include "sound/sfx_06.asm"
Sound_Data_SFX_07: ; $BBD0
;.incbin "sound/sfx_07.bin"
.include "sound/sfx_07.asm"
Sound_Data_SFX_08: ; $BBE6
;.incbin "sound/sfx_08.bin"
.include "sound/sfx_08.asm"
Sound_Data_SFX_09: ; $BBF8
;.incbin "sound/sfx_09.bin"
.include "sound/sfx_09.asm"
Sound_Data_SFX_0A: ; $BC0B
;.incbin "sound/sfx_0A.bin"
.include "sound/sfx_0A.asm"
Sound_Data_SFX_0B: ; $BC33
;.incbin "sound/sfx_0B.bin"
.include "sound/sfx_0B.asm"
Sound_Data_SFX_0C: ; $BC56
;.incbin "sound/sfx_0C.bin"
.include "sound/sfx_0C.asm"
Sound_Data_SFX_0D: ; $BC7F
;.incbin "sound/sfx_0D.bin"
.include "sound/sfx_0D.asm"
Sound_Data_SFX_0E: ; $BCBF
;.incbin "sound/sfx_0E.bin"
.include "sound/sfx_0E.asm"
Sound_Data_SFX_0F: ; $BCDC
;.incbin "sound/sfx_0F.bin"
.include "sound/sfx_0F.asm"
Sound_Data_SFX_10: ; $BCF0
;.incbin "sound/sfx_10.bin"
.include "sound/sfx_10.asm"
Sound_Data_SFX_11: ; $BD0D
;.incbin "sound/sfx_11.bin"
.include "sound/sfx_11.asm"
Sound_Data_SFX_12: ; $BD21
;.incbin "sound/sfx_12.bin"
.include "sound/sfx_12.asm"
Sound_Data_SFX_13: ; $BD3C
;.incbin "sound/sfx_13.bin"
.include "sound/sfx_13.asm"
Sound_Data_SFX_14: ; $BD53
;.incbin "sound/sfx_14.bin"
.include "sound/sfx_14.asm"
Sound_Data_SFX_15: ; $BD6D
;.incbin "sound/sfx_15.bin"
.include "sound/sfx_15.asm"
Sound_Data_SFX_16: ; $BD8D
;.incbin "sound/sfx_16.bin"
.include "sound/sfx_16.asm"
Sound_Data_SFX_17: ; $BDC0
;.incbin "sound/sfx_17.bin"
.include "sound/sfx_17.asm"
Sound_Data_SFX_18: ; $BDE0
;.incbin "sound/sfx_18.bin"
.include "sound/sfx_18.asm"
Sound_Data_SFX_19: ; $BE0D
;.incbin "sound/sfx_19.bin"
.include "sound/sfx_19.asm"
Sound_Data_SFX_1A: ; $BE2A
Stop
Sound_Data_SFX_1B: ; $BE2F
.db $FF
Jump to Line
Something went wrong with that request. Please try again.