libsoundGS/libsound.asm

1527 lines
24 KiB
NASM

*
* disk streaming sound driver library for ORCA/M Assembler on Apple IIgs
* Christopher Shepherd <chris@southernmost.us>, July 2007 - May 2012
*
* 1 infinite-length streaming sound from disk
* up to 8 sounds swapped into DOC RAM on-demand and played simultaneously
* DOC oscillators and DOC RAM are all managed/allocated for you
* good stuff eh?
*
case on
keep libsound2
mcopy libsound.macros
*DUMMY999 start libsound
* end
BGSoundStart start
using SGlobals
tsc
tcs
phd
phb
phk
plb
tcd
pushword #0000
pushword #0000
pushword #429
pushlong #FilePath
jsl soundPlayStart
plb
pld
rtl
end
*
* int soundInit( int MMID );
* get DOC ready and install MIRQ vector
* carry set if error
*
soundInit start
using SGlobals
begin equ 1
* 4-byte temporary variable: for handle deref
HndlRef equ begin
* 4-byte temporary variable: deref'd handle in DP
BuffPtr equ begin+4
rtn equ begin+8
mmid equ rtn+3
finish equ mmid+2
stkAdj equ finish-(rtn+3)
varspace equ rtn-begin
tsc
sec
sbc #varspace
tcs
phd
phb
phk
plb
tcd
stz errorFlag
lda mmid
sta ourMMID
ldx #$0100
ldy #0000
jsr AllocDP
jsr CheckError
bcs siret
stx HndlRef
sty HndlRef+2
ldy #0002
lda [HndlRef],y
pha
_SoundStartUp
* bcs siret
ldx #$2000
ldy #0000
jsr AllocDP
* jsr CheckError
bcs siret
stx HndlRef
sty HndlRef+2
lda [HndlRef]
sta BuffPtr
sta BuffPtrSave
ldy #0002
lda [HndlRef],y
sta BuffPtr+2
sta BuffPtrSave+2
jsr DOCHalt
pushLong #SoundMIRQV
_SetSoundMIRQV
bcs siret
ldx #$00E1 ; $E1 - oscillator enable?
lda #$00FF ; all registers?
jsr SetDOCReg
siret tay
lda rtn+1
sta rtn+stkAdj+1
lda rtn
sta rtn+stkAdj
plb
pld
tsc
clc
adc #stkAdj+varspace
tcs
clc
lda errorFlag
beq siret2
sec
siret2 tya
rtl
end
*
* int loadOneSound( char *pathname );
* pathname is gs/os pathname (16-bit length followed by data)
* returns carry set if (GS/OS or MM) error
* otherwise returns sound ID number for use in soundPlayShort
*
loadOneSound start
using SGlobals
begin equ 1
hndlref equ begin
buffptr equ hndlref+4
rtn equ buffptr+4
path equ rtn+3
finish equ path+4
stkAdj equ finish-path
varspace equ rtn-begin
tsc
sec
sbc #varspace
tcs
phd
phb
phk
plb
tcd
stz errorFlag
* OpenGS
lda path
sta LOSOPath
lda path+2
sta LOSOPath+2
_OpenGS LOSOpenParams
bcc geteof
inc errorFlag
brl losend
* GetEOFGS
geteof lda LOSOHndl
sta LOSEOFHndl
sta LOSReadHndl
sta LOSCloseHndl
_GetEOFGS LOSEOFParams
bcc lalloc
inc errorFlag
brl losend
* Alloc
lalloc ldx LOSEOF
stx LOSReadReq
ldy LOSEOF+2
sty LOSReadReq+2
jsr Alloc
bcc lalloc2
inc errorFlag
jmp losend
lalloc2 stx hndlref
sty hndlref+2
lda [hndlref]
sta buffptr
sta LOSReadData
ldy #0002
lda [hndlref],y
sta buffptr+2
sta LOSReadData+2
* ReadGS
_ReadGS LOSReadParams
bcc lclose
inc errorFlag
bra losend
* CloseGS
lclose _CloseGS LOSCloseParams
bcc lentry
inc errorFlag
bra losend
* put entry in table
lentry ldx #0000
txy
lentry2 lda SoundTable,x
bne next
lda SoundTable+2,x
bne next
* found good entry
lda LOSReadData
sta SoundTable,x
lda LOSReadData+2
sta SoundTable+2,x
lda LOSEOF
sta SoundTable+4,x
lda LOSEOF+2
sta SoundTable+6,x
* compute length of sound in pages of $100, and round up
* then store it at the right offset in LengthTbl
phx
tya
asl a
tax
lda LOSEOF
and #$FF00
xba
inc a
sta LengthTbl,x
plx
tya
bra losend
next inx
inx
inx
inx
inx
inx
inx
inx
iny
bra lentry2
* return
losend tay
lda rtn+1
sta rtn+stkAdj+1
lda rtn
sta rtn+stkAdj
plb
pld
tsc
clc
adc #stkAdj+varspace
tcs
clc
lda errorFlag
beq losend2
sec
losend2 tya
rtl
end
*
* int soundPlayShort( int soundid, int rate );
* swap a SHORT (32KB or less) sound into DOC RAM and play it
* int soundid - sound id number from loadOneSound
* int rate - sound playback rate
* int length - length of sound in bytes
*
soundPlayShort start
using SGlobals
begin equ 1
length2 equ begin
dataAddr equ begin+2
oscnum equ dataAddr+4
docpage equ oscnum+2
genoffset equ docpage+2
rtn equ genoffset+2
soundid equ rtn+3
rate equ soundid+2
finish equ rate+2
stkAdj equ finish-(rtn+3)
varspace equ rtn-begin
tsc
sec
sbc #varspace
tcs
phd
phb
phk
plb
tcd
stz errorFlag
* find an unused generator
ldx #00
genck lda genmap+2,x
beq found
cmp #$FFFF
beq failed
inx
inx
inx
inx
inx
inx
inx
inx
bra genck
failed inc errorFlag
lda #0900
brl gh
* don't map it off as used yet, in case there's no ram
* just save the table offset
found stx genoffset
lda genmap,x
sta oscnum
* find enough free pages in DOC ram to play
lda soundid
asl a
tax
lda LengthTbl,x
tay
ldx #0000
ramsrch jsr canfit
bcc found2
inx
cpx #$0080
bcc ramsrch
* failed
inc errorFlag
lda #0800
brl gh
* found it
* x = doc page number
found2 stx docpage
jsr markused
* map generator as used and store info about what's for dinner
ldx genoffset
lda #0001
sta genmap+2,x
lda docpage
sta genmap+6,x
lda soundid
sta genmap+4,x
* transfer sound into DOC
asl a
asl a
asl a
tax
lda SoundTable,x
sta dataAddr
lda SoundTable+2,x
sta dataAddr+2
lda SoundTable+4,x
tay
lda docpage
clc
adc #$80
xba
jsr PutDOCRAM
* play it for one shot
lda #00 ; freq low
clc
adc oscnum
tax
lda rate
and #$00FF
jsr SetDOCReg
lda #$20 ; freq hi
clc
adc oscnum
tax
lda rate
xba
and #$00FF
jsr SetDOCReg
lda #$40 ; volume
clc
adc oscnum
tax
lda #$00FF
jsr SetDOCReg
lda #$80 ; addr
clc
adc oscnum
tax
lda #$0080 ; $8000
clc
adc docpage ; doc page we ended up getting
jsr SetDOCReg
lda #$C0 ; wavetbl
clc
adc oscnum
tax
lda #$003F ; play 32k sound
jsr SetDOCReg
lda #$A0 ; control
clc
adc oscnum
tax
lda #$000A ; one-shot, int enable
jsr SetDOCReg
* go home
gh tay
lda rtn+1
sta rtn+stkAdj+1
lda rtn
sta rtn+stkAdj
plb
pld
tsc
clc
adc #stkAdj+varspace
tcs
clc
lda errorFlag
beq spsend2
sec
spsend2 tya
rtl
end
*
* int soundPlayStart( string *path, int rate, int oversample, int echoDelay );
* begin playing long sound at *path
* oversample = 0 - no oversampling
* oversample = 1 - 2X oversample
* oversample = 2 - 4X oversample
* echoDelay - number of VBLs to desync L/R channels for faux stereo
* carry set if (typically GSOS) error
*
soundPlayStart start
using SGlobals
begin equ 1
* 4-byte variable: handle
HndlRef equ begin
* 4-byte variable: deref'd buffer in DP
BuffPtr equ begin+4
* note that HndlRef isn't used here but the stack frames have to
* match because the same DP vars are used in different internal calls
* and i don't want to ask for DP pages
rtn equ begin+8
path equ rtn+3
rate equ path+4
osx equ rate+2
echo equ osx+2
finish equ echo+2
stkAdj equ finish-(rtn+3)
varspace equ rtn-begin
tsc
sec
sbc #varspace
tcs
phd
phb
phk
plb
tcd
stz errorFlag
* restore saved buffptr into dp
lda BuffPtrSave
sta BuffPtr
lda BuffPtrSave+2
sta BuffPtr+2
* set rate in DOC params
lda rate
xba
and #$FF00
sta RegTbl2
ora #$0010
sta RegTbl2+4
and #$FF00
ora #$0008
sta RegTbl2+8
lda rate
and #$FF00
ora #$0020
sta RegTbl2+2
and #$FF00
ora #$0030
sta RegTbl2+6
and #$FF00
ora #$0028
sta RegTbl2+10
* set path in GS/OS params
lda path
sta OPath
lda path+2
sta OPath+2
* set oversampling
lda osx
sta OverSample
* set echo delay
lda echo
sta EchoDelay
jsr DOCInit
jsr FileOpen
bcs end1
jsr FirstDiskRead
bcs end1
ldy #0000
pl1 lda RegTbl2,y
cmp #$FFFF
beq pl2
and #$00FF
tax
lda RegTbl2,y
xba
and #$00FF
jsr SetDOCReg
iny
iny
bra pl1
pl2 ldx EchoDelay
beq noecho
vbl sep #$20
longa off
vbl1 lda $E0C019
bpl vbl1
vbl2 lda $E0C019
bpl vbl2
rep #$20
longa on
dex
bne vbl
noecho ldx #$00A0 ; A0 - Control 0
lda #0000 ; 00 - free run, no int, no halt (playing)
jsr SetDOCReg
stz BadIntCount
stz SndBank
stz EndFlag
lda #$FFFF
sta IntFlag
lda EndFlag
beq end1
jsr DOCHalt
end1 tay
lda rtn+1
sta rtn+stkAdj+1
lda rtn
sta rtn+stkAdj
plb
pld
tsc
clc
adc #stkAdj+varspace
tcs
clc
lda errorFlag
beq end12
sec
end12 tya
rtl
end
*
* int soundMaint( void );
* call often to keep sound playing
* carry set if error
*
soundMaint start
using SGlobals
begin equ 1
HndlRef equ begin
BuffPtr equ HndlRef+4
rtn equ begin+8
finish equ rtn+3
stkAdj equ 0
varspace equ rtn-begin
tsc
sec
sbc #varspace
tcs
phd
phb
phk
plb
tcd
stz errorFlag
lda BuffPtrSave
sta BuffPtr
lda BuffPtrSave+2
sta BuffPtr+2
lda IntFlag
beq NextChunk
mret tay
lda rtn+1
sta rtn+stkAdj+1
lda rtn
sta rtn+stkAdj
plb
pld
tsc
clc
adc #stkAdj+varspace
tcs
clc
lda errorFlag
beq mret2
sec
mret2 tya
rtl
NextChunk anop
dec IntFlag
jsr DiskRead
bcs end2
ldx SndBank
jsr PutSndData
lda SndBank
inc a
cmp #0004
bne notFour
lda #0000
notFour sta SndBank
lda IntFlag
bne mret
* interrupt happened during refilling (too slow!)
inc BadIntCount
lda BadIntCount
cmp #0003
beq end2
bra mret
end2 anop
pha
jsr DOCHalt
jsr CloseFile
pla
bra mret
end
*
* int soundStop( void );
* call to stop sound from playing
* carry set, A=errno if error
*
soundStop start
using SGlobals
rtn equ 1
stkAdj equ 0
tsc
phd
phb
phk
plb
tcd
stz errorFlag
jsr DOCHalt
jsr CloseFile
lda rtn+1
sta rtn+stkAdj+1
lda rtn
sta rtn+stkAdj
plb
pld
tsc
* don't perform adc #0000
* clc
* adc #stkAdj
tcs
clc
lda #0000
rtl
end
*
* internal subroutines
*
* halt all DOC voices
DOCHalt start
using SGlobals
ldx #$00A0
lda #$0001
dh jsr SetDOCReg
inx
cpx #$00C0
bne dh
rts
end
* read a DOC register
* a = data
* x = register
GetDOCReg start
using SGlobals
sep #$20
longa off
busy lda $E1C03C
bmi busy
lda $E100CA
and $0F
sta $E1C03C
txa
sta $E1C03E
lda $E1C03D
lda $E1C03D
rep #$20
longa on
rts
end
* set a DOC register
* a = data
* x = register
SetDOCReg start
using SGlobals
sep #$20
longa off
pha
busy2 lda $E1C03C
bmi busy2
lda $E100CA
and $0F
sta $E1C03C
txa
sta $E1C03E
pla
sta $E1C03D
rep #$20
longa on
rts
end
* init DOC registers to defaults
DOCInit start
using SGlobals
sep #$20
longa off
lda $E100CA
and #$0F
ora #$60
sta $E1C03C ; DOC RAM, auto-increment
lda #00
sta $E1C03E
sta $E1C03F ; address $0000
ldy #00
lda #$80
base sta $E1C03D ; put $80 (baseline) into DOC RAM
iny
bne base
rep #$20
longa on
ldy #0000
init lda RegTbl1,y
cmp #$FFFF
beq ret
and #$00FF
tax
lda RegTbl1,y
xba
and #$00FF
jsr SetDOCReg
iny
iny
bra init
ret rts
end
* slam data from buffer into DOC ram
* for short sounds
* $5 - pointer to the data
* a - DOC RAM address (8000,9000,A000, etc)
* x - number of pages perhaps?
* y - length of sample
PutDOCRAM start
using SGlobals
length equ 1
BuffPtr equ 3
sty length
xba ; $8000 -> $0080
tax
sep #$20
longa off
lda $E100CA
and #$0F
ora #$60 ; DOC RAM, auto increment
sta $E1C03C
lda #00
sta $E1C03E ; address low byte: 00
txa
sta $E1C03F ; set DOC page
ldy #0000
sloop2 lda [BuffPtr],y
bne nozero2
inc a
nozero2 sta $E1C03D
iny
cpy length
bcc sloop2
lda #00
sta $E1C03D ; 00 byte for end of sound
ret2 rep #$20
longa on
rts
end
* slam data from buffer into DOC ram
* for long sounds / oversampler engine
* 2012/05/05 - New stack-based version, DOESN'T SUPPORT OVERSAMPLING
* a, y - trashed
* $5 - pointer to the data
* x = DOC 4K page number (0,1,2,3)
PutSndData start
using SGlobals
* you'd better have DP that supports this if you call me
BuffPtr equ 5
phb
lda >$000002 ; Save $00/0002 - $00/0000B
pha ; Because it's about to get stomped on
lda >$000004
pha
lda >$000006
pha
lda >$000008
pha
lda BuffPtr
sta >$000002 ; $00/0002 has low word of BuffPtr
lda RTrans
sta >$000004 ; $00/0004 has low word of RTrans
lda RReq
sta >$000006 ; $00/0006 has low word of RReq
tdc
pha ; Keep old DPage Register too
lda #$C000
tcd ; Direct Page register is now $C000
tsc
sta >$000008 ; $00/0008 has old stack pointer
sep #$20
longa off
lda #00
pha
plb ; Data Bank $00
rep #$30
longa on
lda >$000002
tcs ; Stack pointer at top of sound
sep #$20
longa off
lda $E100CA
and #$0F
ora #$60 ; DOC RAM, auto increment
sta $3C
lda #00
sta $3E ; address low byte: 00
txa ; x=0, a=$00
asl a ; x=1, a=$20
asl a ; x=2, a=$40
asl a ; x=3, a=$60
asl a
asl a
sta $3F ; set DOC page
ldy |$000004
sloop pla ; Use the stack, luke!
bne noover
inc a
noover sta $3D
dey
bne sloop
rep #$20
longa on
lda >$000004
cmp >$000006
beq ret2
sep #$20
longa off
stz $3D ; 00 byte for end of sound
rep #$20
longa on
ret2 lda >$000008
tcs ; Restore Stack Pointer
pla
tcd ; Restore Direct Page Register
pla
sta >$000008
pla ; Restore the $00/000x bytes
sta >$000006 ; that we trashed
pla
sta >$000004
pla
sta >$000002
plb ; Restore Data Bank
rts
OverSample2x anop
longa off
cpx #02
beq OverSample4x
pha
rep #$20
longa on
clc
adc OSByte2
lsr a
sep #$20
longa off
sta $E1C03D
pla
sta OSByte2
rts
OverSample4x anop
longa off
pha
rep #$20
longa on
clc
adc OSByte2
lsr a
sta OSByte1
clc
adc OSByte2
lsr a
sep #$20
longa off
sta $E1C03D
lda OSByte1
sta $E1C03D
pla
sta OSByte2
rep #$20
longa on
clc
adc OSByte1
lsr a
sep #$20
longa off
sta $E1C03D
lda OSByte2
rts
end
* Sound MIRQ handler
SoundMIRQV start
using SGlobals
php
phb
phd
rep #$30
longa on
phk
plb
ldx #$00E0 ; oscillator interrupt register
jsr GetDOCReg
bit #$0080 ; interrupt?
beq smi1
and #$003E ; get osc number
lsr a
cmp #0008 ; oscillator8?
bne not8
stz IntFlag ; clear interrupt flag to show this happened
not8 cmp #0004
bne not4
lda #0000
jsr clearreg
bra smi1
not4 cmp #12
bne not12
lda #0001
jsr clearreg
bra smi1
not12 cmp #20
bne not20
lda #0002
jsr clearreg
bra smi1
not20 cmp #28
bne smi1
lda #0003
jsr clearreg
smi1 ldx #$00A0 ; Control Register 0
jsr GetDOCReg
and #0001 ; halted?
beq smi2
dec EndFlag
jsr DOCHalt
smi2 pld
plb
plp
clc
rtl
* mark generator as unused again, and free its managed pages
* a = register offset in genmap table
clearreg anop
asl a
asl a
asl a
tax
phx
lda #0000
sta genmap+2,x ; mark generator unused
lda genmap+4,x ; sound number currently playing
asl a
tax
lda LengthTbl,x ; length in pages in y
tay
plx
lda genmap+6,x ; offset in x
tax
jsr markfree
rts
end
* read a chunk from disk
* first time - fill low and high $4000's
FirstDiskRead start
using SGlobals
lda #0000
sta SDisp
sta SDisp+2 ; init position to 0
_SetMarkGS SeekParams
jsr CheckError
ldx BuffPtrSave
ldy BuffPtrSave+2
stx RData
sty RData+2 ; set GSOS to read to our allocated buffer
lda #$2000
ldx OverSample
beq noOS
OS lsr a ; reduce to $2000 or $1000 if 2x or 4x oversample
dex
bne OS
noOS sta RReq
stz RReq+2
_ReadGS ReadParams
jsr CheckError
ldx #0000
jsr PutSndData
_ReadGS ReadParams
jsr CheckError
ldx #0001
jsr PutSndData
_ReadGS ReadParams
jsr CheckError
ldx #0002
jsr PutSndData
_ReadGS ReadParams
jsr CheckError
ldx #0003
jsr PutSndData
clc
rts
end
* close the soundfile
CloseFile start
using SGlobals
_CloseGS CloseParams
rts
end
* just get one chunk from disk
DiskRead start
using SGlobals
_ReadGS ReadParams
rts
end
* open the file for reading
FileOpen start
using SGlobals
_OpenGS OpenParams
jsr CheckError
lda OHndl
sta RHndl
sta CHndl
sta GHndl
sta SHndl
_GetEOFGS GetEOFParams
jsr CheckError
rts
end
* Get Memory from the Memory Manager
* x = size lo
* y = size hi
Alloc start
using SGlobals
pha
pha
phy
phx
pushword ourMMID
pushword #$C000
pushlong #$000000
_NewHandle
jsr CheckError
plx
ply
rts
end
AllocDP start
using SGlobals
pha
pha
phy
phx
pushword ourMMID
pushword #$C005
pushlong #$000000
_NewHandle
jsr CheckError
plx
ply
rts
end
* Check to see if there's room in DOC ram to fix X pages at X offset
* x = page # offset to start searching
* y = length in pages
canfit start
using SGlobals
phx
phy
txa
asl a
tax
check lda docrammap,x
bne notOK
dey
beq OK
inx
inx
bra check
notOK ply
plx
sec
lda #0700
rts
OK ply
plx
clc
rts
end
* mark pages free
* x = offset
* y = pages
markfree start
using SGlobals
txa
asl a
tax
lda #0000
mark3 sta docrammap,x
dey
beq done2
inx
inx
bra mark3
done2 rts
end
* mark pages used
* x = offset
* y = pages
markused start
using SGlobals
txa
asl a
tax
lda #0001
mark2 sta docrammap,x
dey
beq done
inx
inx
bra mark2
done rts
end
*
* Check for toolbox and GS/OS errors
*
CheckError start
using SGlobals
bcs setFlag
rts
setFlag inc errorFlag
rts
end
*
* Global Variables and such
*
SGlobals data
ourMMID dc i2'$00'
BuffPtrSave dc i4'$00'
errorFlag dc i2'$00'
RegTbl2 anop
dc i1'$00,$01' ; freq low0 = E3
dc i1'$20,$02' ; freq high0 = 01 (freq0 = 1E3 or d483)
dc i1'$10,$01' ; freq low16 = E3
dc i1'$30,$02' ; freq high16 = 01 (freq16 = 1E3 or d483)
dc i1'$08,$01' ; freq low8 = E3
dc i1'$28,$02' ; freq high8 = 01 (freq8 = 1E3 or d483)
dc i1'$40,$FF' ; volume0 = FF
dc i1'$50,$FF' ; volume16 = FF
dc i1'$48,$00' ; volume8 = 00
dc i1'$80,$00' ; addr0 = 00
dc i1'$88,$00' ; addr8 = 00 (formerly 80)
dc i1'$90,$00' ; addr16 = 00
dc i1'$C0,$3F' ; multi0 = 3F (play 32k sound)
dc i1'$C8,$3D' ; multi8 = 3D (play 32k sound, 1/4 resolution)
dc i1'$D0,$3F' ; multi16 = 3F (play 32k sound)
dc i1'$A0,$01' ; control0 = 01 (free run, halt, no int)
dc i1'$A8,$08' ; control8 = 08 (free run, no halt, int)
dc i1'$B0,$10' ; control16 = 10 (free run, no halt, no int, stereo?)
dc i1'$FF,$FF' ; end of table
RegTbl1 anop
* 0, 8, and 16 for long sound / oversampler engine
dc i1'$00,$00' ; freq low0 = 00
dc i1'$20,$08' ; freq hi0 = 08
dc i1'$10,$00' ; freq low16 = 00
dc i1'$30,$08' ; freq hi16 = 08
dc i1'$08,$00' ; freq low8 = 00
dc i1'$28,$08' ; freq hi8 = 00
dc i1'$40,$00' ; volume 0 = 00
dc i1'$50,$00' ; volume 16 = 00
dc i1'$48,$00' ; volume 8 = 00
dc i1'$80,$00' ; address low0 = 00
dc i1'$88,$00' ; address low8 = 00
dc i1'$90,$00' ; address low16 = 00
dc i1'$C0,$00' ; wavtbl0 = 00
dc i1'$C8,$00' ; wavtbl8 = 00
dc i1'$D0,$00' ; wavtbl16 = 00
dc i1'$A0,$02' ; control0 = 02 (one shot)
dc i1'$A8,$02' ; control8 = 02 (one shot)
dc i1'$B0,$02' ; control16 = 02 (one shot)
* we use 4, 12, 20, and 28 for the one-shot sounds
dc i1'$04,$00' ; freq low4 = 00
dc i1'$24,$08' ; freq hi4 = 08
dc i1'$0C,$00' ; freq low12 = 00
dc i1'$2C,$08' ; freq hi12 = 08
dc i1'$14,$00' ; freq low20 = 00
dc i1'$34,$08' ; freq hi20 = 08
dc i1'$18,$00' ; freq low28 = 00
dc i1'$38,$08' ; freq hi28 = 08
dc i1'$44,$00' ; volume4 = 00
dc i1'$4C,$00' ; volume12 = 00
dc i1'$54,$00' ; volume20 = 00
dc i1'$58,$00' ; volume28 = 00
dc i1'$84,$00' ; address low4 = 00
dc i1'$8C,$00' ; address low12 = 00
dc i1'$94,$00' ; address low20 = 00
dc i1'$98,$00' ; address low24 = 00
dc i1'$C4,$00' ; wavtbl4 = 00
dc i1'$CC,$00' ; wavtbl12 = 00
dc i1'$D4,$00' ; wavtbl20 = 00
dc i1'$D8,$00' ; wavtbl28 = 00
dc i1'$A4,$02' ; control4 = 02 (one shot)
dc i1'$AC,$02' ; control12 = 02 (one shot)
dc i1'$B4,$02' ; control20 = 02 (one shot)
dc i1'$B8,$02' ; control28 = 02 (one shot)
* 6,14,22,30 - 4 more voices for one-shotters
dc i1'$06,$00' ; freq low6 = 00
dc i1'$26,$08' ; freq hi6 = 08
dc i1'$0E,$00' ; freq low14 = 00
dc i1'$2E,$08' ; freq hi14 = 08
dc i1'$16,$00' ; freq low22 = 00
dc i1'$36,$08' ; freq hi22 = 08
dc i1'$1A,$00' ; freq low30 = 00
dc i1'$3A,$08' ; freq hi30 = 08
dc i1'$46,$00' ; volume6 = 00
dc i1'$4E,$00' ; volume14 = 00
dc i1'$56,$00' ; volume22 = 00
dc i1'$5A,$00' ; volume30 = 00
dc i1'$86,$00' ; address low6 = 00
dc i1'$8E,$00' ; address low14 = 00
dc i1'$96,$00' ; address low22 = 00
dc i1'$9A,$00' ; address low30 = 00
dc i1'$C6,$00' ; wavtbl6 = 00
dc i1'$CE,$00' ; wavtbl14 = 00
dc i1'$D6,$00' ; wavtbl22 = 00
dc i1'$DA,$00' ; wavtbl30 = 00
dc i1'$A6,$02' ; control6 = 02 (one shot)
dc i1'$AE,$02' ; control14 = 02 (one shot)
dc i1'$B6,$02' ; control22 = 02 (one shot)
dc i1'$BA,$02' ; control30 = 02 (one shot)
dc i1'$FF,$FF' ; end of table
IntFlag dc i2'$FFFF'
EndFlag dc i2'0000'
EchoDelay dc i2'0000'
SndBank dc i2'0000'
BadIntCount dc i2'0000'
OverSample dc i2'0000'
Length dc i2'0000'
Length2 dc i2'0000'
OSByte1 dc i2'0000'
OSByte2 dc i2'0000'
OpenParams dc i2'$0002'
OHndl dc i2'0000'
OPath dc a4'FilePath'
FilePath dosin ':music:thewire.wav'
ReadParams dc i2'$0004'
RHndl dc i2'0000'
RData dc i2'0000'
dc i2'0000'
RReq dc i2'0000'
dc i2'0000'
RTrans dc i2'0000'
dc i2'0000'
CloseParams dc i2'$0001'
CHndl dc i2'0000'
GetEOFParams dc i2'$0002'
GHndl dc i2'0000'
GEOF dc i2'0000'
dc i2'0000'
SeekParams dc i2'$0003'
SHndl dc i2'0000'
SBase dc i2'0000'
SDisp dc i2'0000'
dc i2'0000'
LOSOpenParams dc i2'$0002'
LOSOHndl dc i2'0000'
LOSOPath dc i4'0000'
LOSEOFParams dc i2'$0002'
LOSEOFHndl dc i2'0000'
LOSEOF dc i4'0000'
LOSReadParams dc i2'$0004'
LOSReadHndl dc i2'0000'
LOSReadData dc i4'0000'
LOSReadReq dc i4'0000'
LOSReadTrans dc i4'0000'
LOSCloseParams dc i2'$0001'
LOSCloseHndl dc i2'0000'
* room to hold the pointers to up to 32 sounds loaded into RAM, and their
* lengths in bytes
* hopefully this is more than you ever need
SoundTable anop
addr0 dc i4'0000,0000'
addr1 dc i4'0000,0000'
addr2 dc i4'0000,0000'
addr3 dc i4'0000,0000'
addr4 dc i4'0000,0000'
addr5 dc i4'0000,0000'
addr6 dc i4'0000,0000'
addr7 dc i4'0000,0000'
addr8 dc i4'0000,0000'
addr9 dc i4'0000,0000'
addr10 dc i4'0000,0000'
addr11 dc i4'0000,0000'
addr12 dc i4'0000,0000'
addr13 dc i4'0000,0000'
addr14 dc i4'0000,0000'
addr15 dc i4'0000,0000'
addr16 dc i4'0000,0000'
addr17 dc i4'0000,0000'
addr18 dc i4'0000,0000'
addr19 dc i4'0000,0000'
addr20 dc i4'0000,0000'
addr21 dc i4'0000,0000'
addr22 dc i4'0000,0000'
addr23 dc i4'0000,0000'
addr24 dc i4'0000,0000'
addr25 dc i4'0000,0000'
addr26 dc i4'0000,0000'
addr27 dc i4'0000,0000'
addr28 dc i4'0000,0000'
addr29 dc i4'0000,0000'
addr30 dc i4'0000,0000'
addr31 dc i4'0000,0000'
* and now their lengths, in pages
LengthTbl anop
length0 dc i2'0000'
length1 dc i2'0000'
lengthx dc i2'0000'
length3 dc i2'0000'
length4 dc i2'0000'
length5 dc i2'0000'
length6 dc i2'0000'
length7 dc i2'0000'
length8 dc i2'0000'
length9 dc i2'0000'
length10 dc i2'0000'
length11 dc i2'0000'
length12 dc i2'0000'
length13 dc i2'0000'
length14 dc i2'0000'
length15 dc i2'0000'
length16 dc i2'0000'
length17 dc i2'0000'
length18 dc i2'0000'
length19 dc i2'0000'
length20 dc i2'0000'
length21 dc i2'0000'
length22 dc i2'0000'
length23 dc i2'0000'
length24 dc i2'0000'
length25 dc i2'0000'
length26 dc i2'0000'
length27 dc i2'0000'
length28 dc i2'0000'
length29 dc i2'0000'
length30 dc i2'0000'
length31 dc i2'0000'
* map of DOC RAM in pages of $100
* 2 bytes each, 32K of ram = $80*2 = $FF
docrammap ds 256
dc i2'$FFFF,$FFFF'
* map of DOC generators we're going to manage, and if they're in use
* 2 bytes - generator number
* 2 bytes - in use? 0001 if yet
* 2 bytes - sound currently playing
* 2 bytes - doc ram offset
genmap anop
gen1 dc i2'4,0000,0000,0000'
gen2 dc i2'12,0000,0000,0000'
gen3 dc i2'20,0000,0000,0000'
gen4 dc i2'28,0000,0000,0000'
gen5 dc i2'6,0000,0000,0000'
gen6 dc i2'14,0000,0000,0000'
gen7 dc i2'22,0000,0000,0000'
gen8 dc i2'30,0000,0000,0000'
dc i2'$FFFF,$FFFF'
end