; data space allocated to sounds 
; &E894 to &E8D8

PLAY0     EQU &E894 
PLAY1     EQU PLAY0+8
PLAY2     EQU PLAY0+16
PLAY3     EQU PLAY0+24

; Offsets within sound control buffers
toneOn    EQU 0            ; zero = off non zero = on top bit set for new sound
Freq      EQU 1            ; 10 bit freqency value, 4 bits of fractional frequency
FreqHi    EQU 2   
Volume    EQU 3            ; 4 bit volume, 4 bits of fractional volume
duration  EQU 4            ; Duration in 1/50 sec
FChange   EQU 5            ; frequency change rate
FChangeHi EQU 6  
Vchange   EQU 7            ; volume change rate

; Game code only touches the music system at a limited number of places
.muted
POP  AF                    ; tidy the stack
RET


.soundSystem
PUSH AF
LD   A,(gameFlagBits)
AND  &01
JR   Z,muted
POP  AF
PUSH HL
PUSH DE
PUSH BC
ADD  A,A                   ; A has the sound type multiply by 8 to get the table offset
ADD  A,A
ADD  A,A
LD   C,A 
LD   B,&00                 ; B remains 0 for future addition and the LDIR
LD   HL,soundEffectTable
ADD  HL,BC
EX   DE,HL                 ; make DE Point 8 bytes of sound data
LD   A,(DE)                ; get the voice number
INC  DE
LD   C,A                   ; the voice number is preset to count in block sizes for speed
LD   HL,PLAY0              
ADD  HL,BC
LD   (HL),0                ; set the sound to inactive in case an interrupt occurs during setup
PUSH HL                    ; save pointer
INC  HL                    ; point to the first byte of the data
EX   DE,HL                 ; HL now has the source DE the destination
LD   C,7
LDIR                       ; copy the remaining 7 bytes to the sound block
POP  HL
LD   (HL),&FF              ; flag the sound block as new (top bit set)
POP  BC
POP  DE
POP  HL
RET




; A=0  key blip?
; A-1  ship ID/countdown
; A=2  Death?
; A=3  fire?
; A=4
; A=5  red alert?
; A=6  error beep
; a=7
; A=8  Jammer? 
; a=9
; a=0A
; a=0B Escape/fire missile
; A=0C hyperdrive
; A=0D lost cargo

; sound format is 8 bytes
; Channel x8 ie 0,8,16,24 followed by 7 bytes of sound data
; Freqency (word), Volume (byte),duration (byte),Freqency change (word), Volume change (byte)

.soundEffectTable
; soundType0 key beep ?
DB 0            ; channel 0
DW 568<4        ; 220Hz
DB 8<4          ; volume 
DB 10           ; frames long
DW 0            ; no frequency change
DB -4           ; volume change
; soundType1 countdown
DB 0            ; channel 0
DW 284<4        ; 440Hz
DB 15<4         ; volume 
DB 30           ; frames long 
DW 20           ; frequency change
DB -1           ; volume change
; soundType2 death ?
DB 1            ; channel 0
DW 568<4        ; 220Hz
DB 13<4         ; volume 
DB 50           ; frames long
DW -4           ; frequency change
DB -4           ; volume change
; soundType3 Laser?
DB 16           ; channel 2
DW 390<4        ; 320Hz
DB 10<4         ; volume 
DB 20           ; frames long
DW 32           ; frequency chaage
DB -8           ; volume change
; soundType4 dummy entry
DB 0            ; channel 0
DW 284<4        ; 440Hz
DB 10<4         ; volume
DB 10           ; frames long 
DW 0            ; no frequency change
DB -4           ; volume change
; soundType5 dummy entry
DB 0            ; channel 0
DW 284<4        ; 440Hz
DB 10<4         ; volume
DB 10           ; frames long 
DW 0            ; no frequency change
DB -4           ; volume change
; soundType6 blip
DB 0            ; channel 0
DW 284<4        ; 440Hz
DB 13<4         ; volume
DB 15           ; frames long 
DW 0            ; no frequency change
DB -4           ; volume change
; soundType7 explode ? 
DB 8            ; channel 1 
DW 1136<4       ; 110Hz
DB 15<4         ; volume 
DB 10           ; frames long 
DW 0            ; no frequency change
DB -16          ; volume change
; soundType8 dummy entry
DB 16           ; channel 2
DW 390<4        ; 320Hz
DB 10<4         ; volume 
DB 20           ; frames long
DW -32          ; frequency chaage
DB -8           ; volume change
; soundType9 dummy entry
DB 0            ; channel 0
DW 284<4        ; 440Hz
DB 10<4         ; volume
DB 10           ; frames long 
DW 0            ; no frequency change
DB -4           ; volume change
; soundType10 dummy entry
DB 0            ; channel 0
DW 284<4        ; 440Hz
DB 10<4         ; volume
DB 10           ; frames long 
DW 0            ; no frequency change
DB -4           ; volume change
; soundType11 missile
DB 8            ; channel 1
DW 156<4        ; 800Hz
DB 15<4         ; volume 15
DB 10           ; frames long
DW 0            ; no frequency chaage
DB -5           ; volume change
; soundType12 hyperdrive
DB 8            ; channel 1
DW 390<4        ; 320Hz
DB 15<4         ; volume 15
DB 50           ; frames long
DW 0            ; no frequency chaage
DB -16          ; volume change

; soundType13 lost cargo?
DB 8            ; channel 1
DW 114<4        ; 1100Hz
DB 15<4         ; volume 15
DB 20           ; frames long
DW 10           ; frequency chaage
DB -4           ; volume change
; soundType14 dummy entry
DB 0            ; channel 0
DW 284<4        ; 440Hz
DB 10<4         ; volume
DB 10           ; frames long 
DW 0            ; no frequency change
DB -4           ; volume change

; music pointers ?
.SongList

; music player ? called on "elite" animation
.MusicOff
.playSong
RET


; called in the interrupt
; all registers are already saved
.L7645
LD   B,4               ; 4 voices to potentially update
LD   C,%10000000       ; Base value for sound channel first/one byte update
LD   DE,&0008          ; size of the sound Blocks 
LD   IX,PLAY0
.soundUpdateLoop
LD   A,(IX+toneOn)
AND  A
JR   Z,Silent          ; note is off, set volume to off just in case its a new update
JP   M,newNote         ; the top bit is set start a new note
; any other value update the exixting note
DEC  (IX+duration)
JR   Z,noteOff         ; counted down to zero, turn off the note

LD   A,(IX+volume)
ADD  A,(IX+vChange)
LD   (IX+volume),A     ; adjust the volume (8 bits)
LD   A,(IX+freq)
ADD  A,(IX+fChange)
LD   (IX+freq),A
LD   A,(IX+freqHi)
ADC  A,(IX+fChangeHi)
LD   (IX+freqHi),A     ; adjust the tone (16 bits)
JR   playNote

.noteOff
LD   (IX+toneOn),0     ; the note has finished, mark inactive
LD   (IX+freq),0       ; set volume and frequency to zero
LD   (IX+freqHi),0
LD   (IX+volume),0
.silent
LD   A,%00011111       ; attenuation off and volume register bit
OR   C                 ; add in the current channel
CALL doSound
JR   nextChannel
  
.newNote
LD   (IX+toneOn),1     ; flag the note as exixiting, and fall through to play it
.playNote
LD   A,(IX+freq)       ; get the fractional bits, note data is 00XXXXXX XXXXxxxx
RRCA                   ; and rotate XXXX bits to the low nibble of A
RRCA
RRCA
RRCA
AND  &0F               ; mask off the fraction
OR   C                 ; complete the first update
CALL doSound           ; and send it
LD   A,(IX+freqHi)
AND  &3F               ; ensure bit 7 is clear, as it could have overflowed
CALL doSound           ; and send it, no further processing is required
LD   A,(IX+volume)
RRCA                   ; and rotate volume bits to the low nibble of A
RRCA
RRCA
RRCA 
DEC  A                 ; adjust for 0F being sound off not 00
AND  &0F               ; clear the fractional bits
OR   C                 ; add the base time register
OR   %00010000         ; set the volume register bit
CALL doSound           ; and send it
.nextChannel
LD   A,%00100000
ADD  A,C
LD   C,A               ; point the the next tone register
ADD  IX,DE             ; point the then buffer
DJNZ soundUpdateLoop

RET

.doSound
out (PSG),A
IN  A,(PSGstrobe)
RET


End