diff --git a/GSCats.xcodeproj/project.pbxproj b/GSCats.xcodeproj/project.pbxproj index 3726c4e..a2407e4 100644 --- a/GSCats.xcodeproj/project.pbxproj +++ b/GSCats.xcodeproj/project.pbxproj @@ -25,9 +25,12 @@ 7088096D1F2ECE8D00D4C950 /* GenerateRenderSpans.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = GenerateRenderSpans.py; sourceTree = ""; }; 708D1B1E27B9A1A600909AFC /* crosshair.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = crosshair.s; sourceTree = ""; }; 709175C01F60D263008FAFAB /* GenerateCircles.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = GenerateCircles.py; sourceTree = ""; }; + 709816A32A53B21800108D9C /* GenerateSoundBank.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = GenerateSoundBank.sh; sourceTree = ""; }; + 709816A42A54E19F00108D9C /* ReadMe.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ReadMe.md; sourceTree = ""; }; 7099E3841F41022100182A82 /* gameobject.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameobject.s; sourceTree = ""; }; 7099E3851F4107B100182A82 /* GenerateVRAMYOffset.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = GenerateVRAMYOffset.py; sourceTree = ""; }; 70A80FB01F43D7F200BD34C9 /* gamemanager.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gamemanager.s; sourceTree = ""; }; + 70BCB0CE2A4FC92D00B19803 /* sound.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = sound.s; sourceTree = ""; }; 70BDCBC92006AD5F00CB51F1 /* linkerConfig */ = {isa = PBXFileReference; lastKnownFileType = text; path = linkerConfig; sourceTree = ""; }; 70BDCBCA200A99F200CB51F1 /* ParseMapFile.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = ParseMapFile.py; sourceTree = ""; }; 70C073091F5BAA3E009844A9 /* collision.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = collision.s; sourceTree = ""; }; @@ -66,6 +69,8 @@ 700C39C51F2E5CA800C24F9C /* tables.s */, 70E266E31F6F262D005AC7E4 /* circleTable.s */, 70F0869F1F413A89002446C3 /* player.s */, + 70BCB0CE2A4FC92D00B19803 /* sound.s */, + 709816A32A53B21800108D9C /* GenerateSoundBank.sh */, 705456882A4D336200A2B866 /* animation.s */, 700B5E6F2069831000B31C00 /* inventory.s */, 700F21DF1F4A364600D7007D /* projectile.s */, @@ -85,6 +90,7 @@ 705456862A43E03B00A2B866 /* GeneratePixelCircle.py */, 709175C01F60D263008FAFAB /* GenerateCircles.py */, 70BDCBCA200A99F200CB51F1 /* ParseMapFile.py */, + 709816A42A54E19F00108D9C /* ReadMe.md */, ); sourceTree = ""; }; diff --git a/GenerateSoundBank.sh b/GenerateSoundBank.sh new file mode 100755 index 0000000..c1e1fb7 --- /dev/null +++ b/GenerateSoundBank.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +############################################ +# Takes a list of WAV files for the sound effects, +# downsamples them to GS-friendly rates, +# and packs them into a 64k block that can +# be loaded into Ensoniq RAM +# +# IMPORTANT: This depends on sox, which MacOS doesn't have natively. +# You can get it from Homebrew ("brew install sox") +# +# Big thanks to Jeremy Rand, from whom this approach +# borrows heavily +# https://github.com/jeremysrand/BuGS/blob/master/BuGS/sound/sources/createRaw + +rm -f "SoundBank#060000" +rm -f "Sound/SoundMap.txt" + +currAddr=0 + +for (( index=1; index<="$#"; index+=2 )); do + waveFilePath=${!index} + + # Downsample and convert to 8-bit unsigned raw waveform + rateIndex=$((index+1)) + rate=${!rateIndex} + sox "$waveFilePath" -c 1 -r "$rate" --bits 8 --encoding unsigned tempSound.raw + + waveFile=$(basename -- "$waveFilePath") + rawFile="Sound/Generated/${waveFile%.wav}.raw" + + # Replace any 00 bytes with 01 because 00 is the terminator for Ensoniq + LC_ALL=C tr < tempSound.raw '\000' '\001' > "$rawFile" + rm tempSound.raw + + # Null-terminate the waveform + rawSize=`stat -f '%z' "$rawFile"` + dd if=/dev/zero bs=1 count=1 >> "$rawFile" + + # Pad the file out to a wavetable page boundary + rawSize=`stat -f '%z' "$rawFile"` + pageSize=`echo "a=$rawSize; b=4096; if ( a%b ) (a/b+1)*4096 else a/b*4096" | bc` + let "padding = $pageSize - $rawSize" + dd if=/dev/zero bs=1 count="$padding" >> "$rawFile" + + # Concatenate to our soundbank + cat "$rawFile" >> "SoundBank#060000" + + # Update the human-readable sound map (so human can modify loading code as needed) + soundName="${waveFile%.wav}" + printf "$%04x\t%s\t%d bytes\t%d Hz\n" "$currAddr" "$soundName" "$pageSize" "$rate" >> "Sound/SoundMap.txt" + + # Increment address for next sound in bank + currAddr=$((currAddr+pageSize)) + +done + +echo +echo "Sound RAM mapping:" +cat "Sound/SoundMap.txt" + diff --git a/Makefile b/Makefile index 09de518..d100061 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,7 @@ # # Created by Quinn Dunki on 7/14/15. # One Girl, One Laptop Productions -# http://www.quinndunki.com -# http://www.quinndunki.com/blondihacks +# http://www.blondihacks.com # @@ -18,10 +17,12 @@ ADDR=0800 CODEBANK=CODEBANK\#060000 CODEBANKE1=CODEBANKE1\#060800 EXEC=$(PGM)\#06$(ADDR) - +SOUNDBANK=SOUNDBANK\#060000 + PGM=gscats MRSPRITE=../MrSprite/mrsprite GENART=Art/Generated +GENSOUND=Sound/Generated CHROMA=a4dffb PALETTE=a4dffb a4dffb 008800 886611 cc9933 eebb44 dd6666 ff99aa 777777 ff0000 b7b7b7 dddddd 0077bb ffff00 000000 ffffff SPRITES=SpriteBank @@ -41,12 +42,14 @@ diskimage: $(CAD) ADDFILE $(PGM).2mg /$(VOLNAME) $(IMG)/QUIT.SYSTEM/QUIT.SYSTEM#FF2000 $(CAD) ADDFILE $(PGM).2mg /$(VOLNAME) $(IMG)/PRODOS/PRODOS#FF0000 $(CAD) ADDFILE $(PGM).2mg /$(VOLNAME) $(IMG)/BASIC.SYSTEM/BASIC.SYSTEM#FF2000 - + $(PGM): @echo $(REMOTESYMBOLS) @PATH=$(PATH):/usr/local/bin; $(CL65) -t apple2enh -C linkerConfig --cpu 65816 --start-addr 0000 -l$(PGM).lst $(REMOTESYMBOLS) $(PGM).s -o $(CODEBANK) $(CAD) ADDFILE $(PGM).2mg /$(VOLNAME) $(CODEBANK) $(CAD) ADDFILE $(PGM).2mg /$(VOLNAME) $(SPRITEBANK) + $(CAD) ADDFILE $(PGM).2mg /$(VOLNAME) $(SOUNDBANK) + rm -f $(CODEBANK) rm -f $(PGM).o rm -f terrain_e1.map @@ -87,3 +90,9 @@ art: ./MerlinToCA65.sh $(GENART)/$(SPRITES)Src.txt > spritebank.s rm $(GENART)/*.txt rm -f Art/*m.gif + +.PHONY: sound +sound: + rm -f $(GENSOUND)/* + ./GenerateSoundBank.sh Sound/CatHowl.wav 11264 Sound/Meow1.wav 5513 + rm -f $(GENSOUND)/* diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..b5bdf89 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,24 @@ +# GSCats + +## Introduction + +This is a simple game in the style of Apple II Artilley, Scorched Earth, or Worms– but with cats and nobody gets hurt. + +This was written as an exercise for me to learn the fundamentals of Apple IIgs game programming, and I certainly won't claim there's anything special here technically. The neatest part is probably the terrain, which is deformable and scrolls at 60fps on stock hardware. Well, sometimes it does, anyway, depending on how you hold your nose. The GS is hard. + +## Acknowledgements + +I'm writing this game now because I'm learning all the things I wished I could know when I was a teenager and playing all the amazing GS games. Games like Rastan, Task Force, Alien Mind, The Immortal, and Sword of Sodan that did things that people said the GS shouldn't be capable of. I was never able to acquire the knowledge of how these things were done back then, but thanks to an amazing modern retro community, I can! In that spirit, thanks to: + +- John Brooks, who has forgotten more about the GS than I will ever know. +- Rebecca Heineman, who wrote every good game on the platform that you probably played except the ones John wrote +- Jeremy Rand, who is inspiring to me with his prolific retro programming endeavours and humble spirit. Not to mention I learned a lot from reading his code for BuGS. +- Brutal Deluxe, who have made so many great GS tools, including [Mr. Sprite](http://www.brutaldeluxe.fr/products/crossdevtools/mrsprite/index.html) and Cadius, upon which this game depends. I doubt you could find anyone more consistently devoted to the platform than these guys. Mr. Sprite's Tech Page is probably the greatest single treatise ever written on how to do fast Apple IIgs graphics. +- Dagen Brock, for tirelessly maintaining the last remaining (as of this writing) good GS emulator, GSPlus. Without good emulators, retro-development dies, so thank you for fighting the good fight, Dagen. Emulating the GS is a thankless job for a platform almost nobody cares about. +- All of the KansasFest and Apple II community on Slack and elsewhere + + +### Sound Credits: + +- Cat noise compilation, by Counter-gamer. Used with permission under CC3.0 license. [https://freesound.org/s/213889/](url) +- Cat Howl, by InspectorJ. Used with permission under CC4.0 license. [https://freesound.org/s/415209/](url) diff --git a/Sound/CatCompilation.wav b/Sound/CatCompilation.wav new file mode 100644 index 0000000..e3a848e Binary files /dev/null and b/Sound/CatCompilation.wav differ diff --git a/Sound/CatHowl.wav b/Sound/CatHowl.wav new file mode 100644 index 0000000..0b2669a Binary files /dev/null and b/Sound/CatHowl.wav differ diff --git a/Sound/Meow1.wav b/Sound/Meow1.wav new file mode 100644 index 0000000..36c0d4b Binary files /dev/null and b/Sound/Meow1.wav differ diff --git a/Sound/SoundMap.txt b/Sound/SoundMap.txt new file mode 100644 index 0000000..df69755 --- /dev/null +++ b/Sound/SoundMap.txt @@ -0,0 +1,2 @@ +$0000 CatHowl 8192 bytes 11264 Hz +$2000 Meow1 8192 bytes 5513 Hz diff --git a/SoundBank#060000 b/SoundBank#060000 new file mode 100644 index 0000000..c2bad6d Binary files /dev/null and b/SoundBank#060000 differ diff --git a/equates.s b/equates.s index 2d4b7dc..fea714a 100644 --- a/equates.s +++ b/equates.s @@ -14,6 +14,12 @@ SHADOWVRAMBANK = $010000 PRODOS = $bf00 ; MLI entry point PRODOSRETURN = $300 ; Indirect jump to get back to ProDOS from any bank +ENSONIQ_CONTROL = $e1c03c ; Ensoniq-related registers/locations +ENSONIQ_DATA = $e1c03d +ENSONIQ_ADDRL = $e1c03e +ENSONIQ_ADDRH = $e1c03f +SYSTEM_VOLUME = $e100ca + ; Zero page locations we use (unused by Monitor, Applesoft, or ProDOS) PARAM0 = $06 PARAM1 = $07 diff --git a/gamemanager.s b/gamemanager.s index 4067774..0cc9dfe 100644 --- a/gamemanager.s +++ b/gamemanager.s @@ -36,6 +36,9 @@ beginGameplay: lda #3 sta SpriteBankBank00+3 ; Tell compiled sprites what bank they are in BITS16 + + ; Set up audio + jsr initSoundSystem ; Erase the screen ldx #$0000 diff --git a/gscats.s b/gscats.s index 40a7289..b93f075 100644 --- a/gscats.s +++ b/gscats.s @@ -37,6 +37,7 @@ quitGame: .include "random.s" .include "graphics.s" +.include "sound.s" .include "font.s" .include "smallNumbers.s" .include "animation.s" diff --git a/loader.s b/loader.s index 358a083..d828503 100644 --- a/loader.s +++ b/loader.s @@ -138,6 +138,33 @@ loadData: ldy #0 jsr copyBytes + EMULATION + + ; Open the sound file + jsr PRODOS + .byte $c8 + .addr fileOpenSound + bne ioError + + ; Load the sound data into bank 0 + jsr PRODOS + .byte $ca + .addr fileRead + bne ioError + + ; Close the file + jsr PRODOS + .byte $cc + .addr fileClose + + NATIVE + + ; Copy sound data into bank 4 + ldx fileReadLen + lda #4 + ldy #0 + jsr copyBytes + ; Set up a long jump into bank 2, and ; a way for game code to get back here to exit ; properly to ProDOS 8 @@ -259,9 +286,18 @@ fileOpenSprites: .byte 0 ; Result (file handle) .byte 0 ; Padding +fileOpenSound: + .byte 3 + .addr soundPath + .addr $9200 ; 1k below BASIC.SYSTEM + .byte 0 ; Result (file handle) + .byte 0 ; Padding + codePath: pstring "/GSAPP/CODEBANK" codePathE1: pstring "/GSAPP/CODEBANKE1" spritePath: pstring "/GSAPP/SPRITEBANK" +soundPath: + pstring "/GSAPP/SOUNDBANK" diff --git a/player.s b/player.s index 59a7a9b..91f6bcf 100644 --- a/player.s +++ b/player.s @@ -335,6 +335,13 @@ unrenderPlayers: ; renderHitAnimation: SAVE_AXY + + ; Play hit sound + phy + ldy #SOUND_HOWL + jsr playSound + ply + PLAYERPTR_Y jsr unrenderPlayers diff --git a/sound.s b/sound.s new file mode 100644 index 0000000..0f3659f --- /dev/null +++ b/sound.s @@ -0,0 +1,312 @@ +; +; sound +; Code and data structures for playing sound effects +; +; Created by Quinn Dunki on 6/30/23 +; + + +NUM_SOUNDS = 2 + +SOUND_HOWL=0 +SOUND_MEOW1=2 + +soundTable: + ; Sound Ram Address, Wave Size, Low Frequency + .byte $00,WAVE_SIZE_8192,200 ; SOUND_HOWL + .byte $20,WAVE_SIZE_8192,100 ; SOUND_MEOW1 + + +; Ensoniq Control Register bit patterns +DOC_RAMACCESS = $40 ; or +DOC_DOCACCESS = $bf ; and +DOC_AUTOINC = $20 ; or +DOC_NOINC = $df ; and + +; Oscillator Control Register bit patterns +OSC_MODE_FREE = $00 +OSC_MODE_ONESHOT = $02 +OSC_MODE_SYNC = $04 +OSC_MODE_SWAP = $06 + +OSC_CHANNEL_ALL = $70 +OSC_CHANNEL_RIGHT = $00 +OSC_CHANNEL_LEFT = $10 + +; Internal oscillator-related registers (additional per-oscillator registers per the big table on page 110 of Apple IIGS Hardware Reference Manual) +OSC_REG_INTERRUPT = $e0 +OSC_REG_ENABLE = $e1 +OSC_REG_DIGITIZE = $e2 + +; Wave table page size bit patterns +WAVE_SIZE_256 = $07 +WAVE_SIZE_512 = $0f +WAVE_SIZE_1024 = $17 +WAVE_SIZE_2048 = $1f +WAVE_SIZE_4096 = $27 +WAVE_SIZE_8192 = $2f +WAVE_SIZE_16384 = $37 +WAVE_SIZE_32768 = $3f + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ENSONIQ_WAIT +; +; Polls the high bit of the Sound Control Register, which tells us when we're allowed +; to talk to the DOC +; +.macro ENSONIQ_WAIT + .local busyLoop + BITS8A +busyLoop: + lda ENSONIQ_CONTROL + bmi busyLoop + BITS16 +.endmacro + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; writeInternalRegister - Call in 8 BIT MODE ONLY (also why it's a macro) +; +; Writes to the internal registers of the Ensoniq +; +; A = Data to write (8 bits) +; X = Register number to write to (8 bits) +; +; Trashes A +; +.macro writeInternalRegister + pha + txa + sta ENSONIQ_ADDRL + pla + sta ENSONIQ_DATA +.endmacro + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; writeInternalRegisterPair - Call in 8 BIT MODE ONLY (also why it's a macro) +; +; Writes to the internal registers of the Ensoniq for a pair of oscillators +; +; A = Data to write (8 bits) +; X = Register number to write to (8 bits). X+1 will also be written +; +; Trashes A +; +.macro writeInternalRegisterPair + pha + txa + sta ENSONIQ_ADDRL + pla + sta ENSONIQ_DATA + pha + inx + txa + sta ENSONIQ_ADDRL + pla + sta ENSONIQ_DATA +.endmacro + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; initSoundSystem +; +; Trashes SCRATCHL +; +initSoundSystem: + SAVE_AXY + + ENSONIQ_WAIT + + ; Load sound file bank into Ensoniq RAM + lda #$0000 ; Sound location in bank 4 + sta PARAML0 + ldx #$04 + ldy #11749 + jsr copyToSoundRAM + + ; Configure all our oscillators + lda #0 + sta SCRATCHL + + ldy #0 + +initSoundSystemLoop: + BITS8A + lda soundTable,y + sta PARAM0 + iny + lda soundTable,y + sta PARAM1 + iny + lda soundTable,y + sta PARAM2 + iny + BITS16 + + phy + ldy SCRATCHL + jsr configureOscillatorPair + + iny + iny + cpy #NUM_SOUNDS*2 + beq initSoundSystemReady + sty SCRATCHL + ply + bra initSoundSystemLoop + +initSoundSystemReady: + ply + + ; Enable oscillators (see page 108 of Apple IIGS Hardware Reference Manual) + BITS8 + lda #(NUM_SOUNDS*2)<<1 ; Two oscillators per sound and leaving low bit untouched + ldx #OSC_REG_ENABLE + writeInternalRegister + BITS16 + + RESTORE_AXY + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; playSound +; +; Y = Oscillator to start (+1 also starts) +; +playSound: + SAVE_AX + + ENSONIQ_WAIT + + BITS8 + + ; Configure run mode for our oscillators + lda #OSC_CHANNEL_RIGHT | OSC_MODE_ONESHOT + ldx docRegisterTableControl,y + writeInternalRegister + + lda #OSC_CHANNEL_LEFT | OSC_MODE_ONESHOT + ldx docRegisterTableControl+1,y + writeInternalRegister + + BITS16 + RESTORE_AX + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; configureOscillatorPair +; +; Y = Oscillator number (+1 will also be configured) +; PARAM0 = Wave table address (one byte) +; PARAM1 = Wave table page size (must use WAVE_SIZE constants) +; PARAM2 = Low frequency (high frequency is always 0) +; +configureOscillatorPair: + SAVE_AXY + ENSONIQ_WAIT + BITS8 + + ; Configure low frequency + lda PARAM2 + ldx docRegisterTableLowFreq,y + writeInternalRegisterPair + + ; Configure high frequency + lda #0 + ldx docRegisterTableHighFreq,y + writeInternalRegisterPair + + ; Configure volume + lda #$ff ; This is relative to system volume, so set it to max + ldx docRegisterTableVolume,y + writeInternalRegisterPair + + ; Configure sound RAM pointer + lda PARAM0 + ldx docRegisterTableWavePointer,y + writeInternalRegisterPair + + ; Configure sound RAM size + ; Format of this register is tricky. See page 114 of Apple IIGS Hardware Reference Manual + lda PARAM1 + ldx docRegisterTableWaveSize,y + writeInternalRegisterPair + + BITS16 + RESTORE_AXY + rts + +; DOC register locations for each oscillator +docRegisterTableLowFreq: + .byte $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0a,$0b,$0c,$0d,$0e,$0f,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f +docRegisterTableHighFreq: + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f +docRegisterTableVolume: + .byte $40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f,$50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$5b,$5c,$5d,$5e,$5f +docRegisterTableData: + .byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f,$70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f +docRegisterTableWavePointer: + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f,$90,$91,$92,$93,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f +docRegisterTableControl: + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af,$b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf +docRegisterTableWaveSize: + .byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf,$d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; copyToSoundRAM +; +; Copies data from main RAM to sound RAM +; +; PARAML0 = Pointer to sound data in main RAM +; X = Bank number of sound data in main RAM +; Y = Size of sound bank in bytes +; +; Trashes SCRATCHL +; +copyToSoundRAM: + SAVE_AXY + sty SCRATCHL + + ENSONIQ_WAIT + + lda #0 + BITS8 + stx copyToSoundRAMLoop+3 ; Self modifying code. Don't panic + + ; Enable sound RAM access and auto-increment mode + lda ENSONIQ_CONTROL + ora #DOC_RAMACCESS | DOC_AUTOINC + sta ENSONIQ_CONTROL + BITS16 + + ; Prepare start address in sound RAM + lda #$0000 + sta ENSONIQ_ADDRL + + ; Copy all the data + lda #0 + BITS8A + ldx #0 + +copyToSoundRAMLoop: + lda $ff0000,x ; Note: 8 bit accumulator! DOC takes one byte at a time + sta ENSONIQ_DATA ; Do not use indexed addressing here- see page 107 of Apple IIGS Hardware Reference Manual + inx + cpx SCRATCHL + bne copyToSoundRAMLoop + + ; Enable DOC access and disable auto-increment mode + ; This is a nicer mode to leave the Ensoniq in for normal usage once loading is complete + lda ENSONIQ_CONTROL + and #DOC_DOCACCESS & DOC_NOINC + sta ENSONIQ_CONTROL + + BITS16 + RESTORE_AXY + rts +