diff --git a/build.bat b/build.bat index 5a7eabc..4dba5d5 100644 --- a/build.bat +++ b/build.bat @@ -34,6 +34,10 @@ REM SET INCF=%INCLUDEDIR% SET ADDR_CODE=0x106 SET ADDR_DATA=0x0 +cd src\audio\ +%ASM% -o audio.asm +cd ..\.. + SDCC --code-loc %ADDR_CODE% --data-loc %ADDR_DATA% --disable-warning 196 -mz80 --no-std-crt0 --opt-code-size fusion.lib -L %LIBDIR% %INC1% %INC2% %INC3% %INC4% %INC5% %INC6% %INC7% %INC8% %INC9% %INCA% %INCB% %INCC% %INCD% %INCE% %INCF% -c src\font.c SDCC --code-loc %ADDR_CODE% --data-loc %ADDR_DATA% --disable-warning 196 -mz80 --no-std-crt0 --opt-code-size fusion.lib -L %LIBDIR% %INC1% %INC2% %INC3% %INC4% %INC5% %INC6% %INC7% %INC8% %INC9% %INCA% %INCB% %INCC% %INCD% %INCE% %INCF% -c src\startscreen.c @@ -46,7 +50,7 @@ SDCC --code-loc %ADDR_CODE% --data-loc %ADDR_DATA% --disable-warning 196 -mz80 - SDCC --code-loc %ADDR_CODE% --data-loc %ADDR_DATA% --disable-warning 196 -mz80 --no-std-crt0 --opt-code-size fusion.lib -L %LIBDIR% %INC1% %INC2% %INC3% %INC4% %INC5% %INC6% %INC7% %INC8% %INC9% %INCA% %INCB% %INCC% %INCD% %INCE% %INCF% -c src\combat.c -SDCC --code-loc %ADDR_CODE% --data-loc %ADDR_DATA% --disable-warning 196 -mz80 --no-std-crt0 --opt-code-size fusion.lib -L %LIBDIR% %INC1% %INC2% %INC3% %INC4% %INC5% %INC6% %INC7% %INC8% %INC9% %INCA% %INCB% %INCC% %INCD% %INCE% %INCF% src\main.c font.rel startscreen.rel menu.rel procgen.rel dungeon.rel combat.rel +SDCC --code-loc %ADDR_CODE% --data-loc %ADDR_DATA% --disable-warning 196 -mz80 --no-std-crt0 --opt-code-size fusion.lib -L %LIBDIR% %INC1% %INC2% %INC3% %INC4% %INC5% %INC6% %INC7% %INC8% %INC9% %INCA% %INCB% %INCC% %INCD% %INCE% %INCF% src\main.c font.rel startscreen.rel menu.rel procgen.rel dungeon.rel combat.rel src\audio\audio.rel SET cpath=%~dp0 diff --git a/dsk/srangers.com b/dsk/srangers.com index 6abaf84..a949096 100644 Binary files a/dsk/srangers.com and b/dsk/srangers.com differ diff --git a/generate_sound.bat b/generate_sound.bat new file mode 100644 index 0000000..e56b760 --- /dev/null +++ b/generate_sound.bat @@ -0,0 +1,11 @@ +@echo off + +cd sound + +rasm compile_at2_files.asm -o UniversalAt2Files -s -sl -sq + +Disark UniversalAt2Files.bin At2Files.asm --symbolFile UniversalAt2Files.sym --sourceProfile sdcc + +copy At2Files.asm ..\src\audio\At2Files.asm + +cd .. diff --git a/hex2bin.exe b/hex2bin.exe new file mode 100644 index 0000000..eac13bf Binary files /dev/null and b/hex2bin.exe differ diff --git a/img2sf5.exe b/img2sf5.exe new file mode 100644 index 0000000..f45d103 Binary files /dev/null and b/img2sf5.exe differ diff --git a/sound/Disark.exe b/sound/Disark.exe new file mode 100644 index 0000000..a44cf1d Binary files /dev/null and b/sound/Disark.exe differ diff --git a/sound/PlayerAkg.asm b/sound/PlayerAkg.asm new file mode 100644 index 0000000..371bdaf --- /dev/null +++ b/sound/PlayerAkg.asm @@ -0,0 +1,3670 @@ +; Arkos Tracker 2 player "generic" player. +; By Targhan/Arkos. +; Psg optimization trick on CPC by Madram/Overlanders. +; +; This compiles with RASM. Check the compatibility page on the Arkos Tracker 2 website, it contains a source converter to any Z80 assembler! +; +; The player uses the stack for optimizations. Make sure the interruptions are disabled before it is called. +; The stack pointer is saved at the beginning and restored at the end. +; +; Target hardware: +; --------------- +; This code can target Amstrad CPC, MSX, Spectrum and Pentagon. By default, it targets Amstrad CPC. +; Simply use one of the follow line (BEFORE this player): +; PLY_AKG_HARDWARE_CPC = 1 +; PLY_AKG_HARDWARE_MSX = 1 +; PLY_AKG_HARDWARE_SPECTRUM = 1 +; PLY_AKG_HARDWARE_PENTAGON = 1 +; Note that the PRESENCE of this variable is tested, NOT its value. +; +; ROM +; ---------------------- +; To use a ROM player (no automodification, use of a small buffer to put in RAM): +; PLY_AKG_ROM = 1 +; PLY_AKG_ROM_Buffer = #4000 (or wherever). +; This makes the player a bit slower and slightly bigger. +; The buffer is PLY_AKG_ROM_BufferSize long (=250 max). You can hardcode this value, because it is calculated, so it won't be accessible before this player is assembled. +; This value decreases when you use player configuration, but increases if you use sound effects. +; +; Optimizations: +; -------------- +; - Use the Player Configuration of Arkos Tracker 2 to generate a configuration file to be included at the beginning of this player. +; It will disable useless features according to your songs, saving for memory and CPU! Check the manual for more details, or more simply the testers. +; - Set PLY_AKG_USE_HOOKS to 0 to remove the three hooks just below to save 9 bytes (yay!). +; - Set PLY_AKG_STOP_SOUNDS to 0 if you don't intent to stop the music via the PLY_AKG_Stop method. +; - If you play your song "one shot" (i.e. without restarting it again), you can set the PLY_AKG_FULL_INIT_CODE to 0, some +; initialization code will not be assembled. + +; Sound effects: +; -------------- +; Sound effects are disabled by default. Declare PLY_AKG_MANAGE_SOUND_EFFECTS to enable it: +; PLY_AKG_MANAGE_SOUND_EFFECTS = 1 +; Check the sound effect tester to see how it enables it. +; Note that the PRESENCE of this variable is tested, NOT its value. + +; Additional note: +; - There can be a slightly difference when using volume in/out effects compared to the PC side, because the volume management is +; different. This means there can be a difference of 1 at certain frames. As it shouldn't be a bother, I let it this way. +; This allows the Z80 code to be faster and simpler. +; +; ------------------------------------------------------- + + +PLY_AKG_Start: + + ;Checks the hardware. Only one must be selected. +PLY_AKG_HardwareCounter = 0 + IFDEF PLY_AKG_HARDWARE_CPC + PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1 + ENDIF + IFDEF PLY_AKG_HARDWARE_MSX + PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1 + PLY_AKG_HARDWARE_SPECTRUM_OR_MSX = 1 + ENDIF + IFDEF PLY_AKG_HARDWARE_SPECTRUM + PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1 + PLY_AKG_HARDWARE_SPECTRUM_OR_PENTAGON = 1 + PLY_AKG_HARDWARE_SPECTRUM_OR_MSX = 1 + ENDIF + IFDEF PLY_AKG_HARDWARE_PENTAGON + PLY_AKG_HardwareCounter = PLY_AKG_HardwareCounter + 1 + PLY_AKG_HARDWARE_SPECTRUM_OR_PENTAGON = 1 + ENDIF + IF PLY_AKG_HardwareCounter > 1 + FAIL 'Only one hardware must be selected!' + ENDIF + ;By default, selects the Amstrad CPC. + IF PLY_AKG_HardwareCounter == 0 + PLY_AKG_HARDWARE_CPC = 1 + ENDIF + + +PLY_AKG_USE_HOOKS: equ 1 ;Use hooks for external calls? 0 if the Init/Play/Stop methods are directly called. Will save a few bytes. +PLY_AKG_STOP_SOUNDS: equ 1 ;1 to have the "stop sounds" code. Set it to 0 if you never plan on stopping your music. +PLY_AKG_FULL_INIT_CODE: equ 1 ;0 to skip some init code/values, saving memory. Possible if you don't plan on restarting your song. + + + ;Is there a loaded Player Configuration source? If no, use a default configuration. + IFNDEF PLY_CFG_ConfigurationIsPresent + PLY_CFG_UseSpeedTracks = 1 + PLY_CFG_UseEventTracks = 1 + PLY_CFG_UseTranspositions = 1 + PLY_CFG_UseHardwareSounds = 1 + PLY_CFG_UseEffects = 1 + PLY_CFG_UseRetrig = 1 + ;PLY_CFG_UseInstrumentRetrig = 1 ;Not tested, the UseRetrig is used instead. + PLY_CFG_UseInstrumentLoopTo = 1 + ;PLY_CFG_NoSoftNoHard = 1 ;Not tested, because always present. + PLY_CFG_NoSoftNoHard_Noise = 1 + PLY_CFG_SoftOnly = 1 + PLY_CFG_SoftOnly_Noise = 1 + PLY_CFG_SoftOnly_ForcedSoftwarePeriod = 1 + PLY_CFG_SoftOnly_SoftwareArpeggio = 1 + PLY_CFG_SoftOnly_SoftwarePitch = 1 + PLY_CFG_SoftToHard = 1 + PLY_CFG_SoftToHard_Noise = 1 + PLY_CFG_SoftToHard_ForcedSoftwarePeriod = 1 + PLY_CFG_SoftToHard_SoftwareArpeggio = 1 + PLY_CFG_SoftToHard_SoftwarePitch = 1 + PLY_CFG_SoftToHard_HardwarePitch = 1 + PLY_CFG_SoftToHard_Retrig = 1 + PLY_CFG_HardOnly = 1 + PLY_CFG_HardOnly_Noise = 1 + PLY_CFG_HardOnly_ForcedHardwarePeriod = 1 + PLY_CFG_HardOnly_HardwareArpeggio = 1 + PLY_CFG_HardOnly_HardwarePitch = 1 + PLY_CFG_HardOnly_Retrig = 1 + PLY_CFG_HardToSoft = 1 + PLY_CFG_HardToSoft_Noise = 1 + PLY_CFG_HardToSoft_ForcedHardwarePeriod = 1 + PLY_CFG_HardToSoft_HardwareArpeggio = 1 + PLY_CFG_HardToSoft_HardwarePitch = 1 + PLY_CFG_HardToSoft_SoftwarePitch = 1 + PLY_CFG_HardToSoft_Retrig = 1 + PLY_CFG_SoftAndHard = 1 + PLY_CFG_SoftAndHard_Noise = 1 + PLY_CFG_SoftAndHard_ForcedSoftwarePeriod = 1 + PLY_CFG_SoftAndHard_SoftwareArpeggio = 1 + PLY_CFG_SoftAndHard_SoftwarePitch = 1 + PLY_CFG_SoftAndHard_ForcedHardwarePeriod = 1 + PLY_CFG_SoftAndHard_HardwareArpeggio = 1 + PLY_CFG_SoftAndHard_HardwarePitch = 1 + PLY_CFG_SoftAndHard_Retrig = 1 + PLY_CFG_UseEffect_Legato = 1 + PLY_CFG_UseEffect_Reset = 1 + PLY_CFG_UseEffect_ForcePitchTableSpeed = 1 + PLY_CFG_UseEffect_ForceArpeggioSpeed = 1 + PLY_CFG_UseEffect_ForceInstrumentSpeed = 1 + PLY_CFG_UseEffect_PitchGlide = 1 + PLY_CFG_UseEffect_PitchUp = 1 + PLY_CFG_UseEffect_PitchDown = 1 + PLY_CFG_UseEffect_PitchTable = 1 + PLY_CFG_UseEffect_Arpeggio3Notes = 1 + PLY_CFG_UseEffect_Arpeggio4Notes = 1 + PLY_CFG_UseEffect_ArpeggioTable = 1 + PLY_CFG_UseEffect_SetVolume = 1 + PLY_CFG_UseEffect_VolumeIn = 1 + PLY_CFG_UseEffect_VolumeOut = 1 + ENDIF + + ;Agglomerates some flags, because they are treated the same way by this player. + ;-------------------------------------------------- + ;Special Track Used? + IFDEF PLY_CFG_UseSpeedTracks + PLY_AKG_UseSpecialTracks = 1 + ENDIF + IFDEF PLY_CFG_UseEventTracks + PLY_AKG_UseSpecialTracks = 1 + ENDIF + ;SoftwareOnly and HardOnly share some code. + IFDEF PLY_CFG_SoftOnly + PLY_AKG_UseSoftOnlyOrHardOnly = 1 + ENDIF + IFDEF PLY_CFG_HardOnly + PLY_AKG_UseSoftOnlyOrHardOnly = 1 + ENDIF + ;The same for their noise. + IFDEF PLY_CFG_SoftOnly_Noise + PLY_AKG_UseSoftOnlyOrHardOnly_Noise = 1 + ENDIF + IFDEF PLY_CFG_HardOnly_Noise + PLY_AKG_UseSoftOnlyOrHardOnly_Noise = 1 + ENDIF + + ;Agglomerates the Forced periods (soft/hard). + IFDEF PLY_CFG_SoftOnly_ForcedSoftwarePeriod + PLY_AKG_UseInstrumentForcedPeriods = 1 + ENDIF + IFDEF PLY_CFG_HardOnly_ForcedHardwarePeriod + PLY_AKG_UseInstrumentForcedPeriods = 1 + ENDIF + IFDEF PLY_CFG_SoftToHard_ForcedSoftwarePeriod + PLY_AKG_UseInstrumentForcedPeriods = 1 + ENDIF + IFDEF PLY_CFG_HardToSoft_ForcedHardwarePeriod + PLY_AKG_UseInstrumentForcedPeriods = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_ForcedSoftwarePeriod + PLY_AKG_UseInstrumentForcedPeriods = 1 + ENDIF + ;Agglomerates the Instrument Arpeggios (soft/hard). + IFDEF PLY_CFG_SoftOnly_SoftwareArpeggio + PLY_AKG_UseInstrumentArpeggios = 1 + ENDIF + IFDEF PLY_CFG_SoftToHard_SoftwareArpeggio + PLY_AKG_UseInstrumentArpeggios = 1 + ENDIF + IFDEF PLY_CFG_HardOnly_HardwareArpeggio + PLY_AKG_UseInstrumentArpeggios = 1 + ENDIF + IFDEF PLY_CFG_HardToSoft_HardwareArpeggio + PLY_AKG_UseInstrumentArpeggios = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_SoftwareArpeggio + PLY_AKG_UseInstrumentArpeggios = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_HardwareArpeggio + PLY_AKG_UseInstrumentArpeggios = 1 + ENDIF + ;Agglomerates the Instrument Pitchs (soft/hard). + IFDEF PLY_CFG_SoftOnly_SoftwarePitch + PLY_AKG_UseInstrumentPitchs = 1 + ENDIF + IFDEF PLY_CFG_SoftToHard_SoftwarePitch + PLY_AKG_UseInstrumentPitchs = 1 + ENDIF + IFDEF PLY_CFG_HardOnly_HardwarePitch + PLY_AKG_UseInstrumentPitchs = 1 + ENDIF + IFDEF PLY_CFG_HardToSoft_HardwarePitch + PLY_AKG_UseInstrumentPitchs = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_SoftwarePitch + PLY_AKG_UseInstrumentPitchs = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_HardwarePitch + PLY_AKG_UseInstrumentPitchs = 1 + ENDIF + ;Agglomerates the Instrument Forced Periods, Arpeggios and Pitchs (soft/hard). + IFDEF PLY_AKG_UseInstrumentForcedPeriods + PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1 + ENDIF + IFDEF PLY_AKG_UseInstrumentArpeggios + PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1 + ENDIF + IFDEF PLY_AKG_UseInstrumentPitchs + PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs = 1 + ENDIF + + ;Agglomerates the Retrig flags for SoftToHard, HardToSoft, SoftAndHard. + IFDEF PLY_CFG_SoftToHard_Retrig + PLY_AKG_UseRetrig_StoH_HtoS_SandH = 1 + ENDIF + IFDEF PLY_CFG_HardToSoft_Retrig + PLY_AKG_UseRetrig_StoH_HtoS_SandH = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_Retrig + PLY_AKG_UseRetrig_StoH_HtoS_SandH = 1 + ENDIF + ;Agglomerates the noise flags for SoftToHard, HardToSoft, SoftAndHard. + IFDEF PLY_CFG_SoftToHard_Noise + PLY_AKG_UseNoise_StoH_HtoS_SandH = 1 + ENDIF + IFDEF PLY_CFG_HardToSoft_Noise + PLY_AKG_UseNoise_StoH_HtoS_SandH = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_Noise + PLY_AKG_UseNoise_StoH_HtoS_SandH = 1 + ENDIF + ;Agglomerates the noise flags to know if the code about R6 must be compiled. + IFDEF PLY_CFG_NoSoftNoHard_Noise + PLY_AKG_Use_NoiseRegister = 1 + ENDIF + IFDEF PLY_CFG_SoftOnly_Noise + PLY_AKG_Use_NoiseRegister = 1 + ENDIF + IFDEF PLY_CFG_HardOnly_Noise + PLY_AKG_Use_NoiseRegister = 1 + ENDIF + IFDEF PLY_CFG_SoftToHard_Noise + PLY_AKG_Use_NoiseRegister = 1 + ENDIF + IFDEF PLY_CFG_HardToSoft_Noise + PLY_AKG_Use_NoiseRegister = 1 + ENDIF + IFDEF PLY_CFG_SoftAndHard_Noise + PLY_AKG_Use_NoiseRegister = 1 + ENDIF + + ;Agglomerates the effect volume in/out. + IFDEF PLY_CFG_UseEffect_VolumeIn + PLY_AKG_UseEffect_VolumeSlide = 1 + ENDIF + IFDEF PLY_CFG_UseEffect_VolumeOut + PLY_AKG_UseEffect_VolumeSlide = 1 + ENDIF + + ;Agglomerates the Arpeggios Table effects. + IFDEF PLY_CFG_UseEffect_Arpeggio3Notes + PLY_AKS_UseEffect_Arpeggio = 1 + ENDIF + IFDEF PLY_CFG_UseEffect_Arpeggio4Notes + PLY_AKS_UseEffect_Arpeggio = 1 + ENDIF + IFDEF PLY_CFG_UseEffect_ArpeggioTable + PLY_AKS_UseEffect_Arpeggio = 1 + ENDIF + + ;Agglomerates the PitchUp/Down effects. + IFDEF PLY_CFG_UseEffect_PitchUp + PLY_AKS_UseEffect_PitchUpOrDown = 1 + ENDIF + IFDEF PLY_CFG_UseEffect_PitchDown + PLY_AKS_UseEffect_PitchUpOrDown = 1 + ENDIF + ;Agglomerates the PitchUp/Down/Glide effects. + ;IMPORTANT TO NOTE that if there is Glide, there WILL be pitch up/down, because the Glide is + ;embedded in the pitch up/down code. + IFDEF PLY_AKS_UseEffect_PitchUpOrDown + PLY_AKS_UseEffect_PitchUpOrDownOrGlide = 1 + ENDIF + IFDEF PLY_CFG_UseEffect_PitchGlide + PLY_AKS_UseEffect_PitchUpOrDownOrGlide = 1 + ENDIF + + ;Agglomerates a special flag combining ArpeggioTable and PitchTable. + IFDEF PLY_AKS_UseEffect_Arpeggio + PLY_AKS_UseEffect_ArpeggioTableOrPitchTable = 1 + ENDIF + IFDEF PLY_CFG_UseEffect_PitchTable + PLY_AKS_UseEffect_ArpeggioTableOrPitchTable = 1 + ENDIF + +;A nice trick to manage the offset using the same instructions, according to the player (ROM or not). + IFDEF PLY_AKG_Rom +PLY_AKG_Offset1b: equ 0 +PLY_AKG_Offset2b: equ 0 ;Used for instructions such as ld iyh,xx + ELSE +PLY_AKG_Offset1b: equ 1 +PLY_AKG_Offset2b: equ 2 + ENDIF + + IFNDEF PLY_AKG_Rom +PLY_AKG_OPCODE_OR_A: equ #b7 ;Opcode for "or a". +PLY_AKG_OPCODE_SCF: equ #37 ;Opcode for "scf". + ELSE + ;Another trick for the ROM player. The original opcodes are converted to number, which will be multiplied by 2, provoking a carry or not. +PLY_AKG_OPCODE_OR_A: equ 0 ;0 * 2 = 0, no carry. +PLY_AKG_OPCODE_SCF: equ #ff ;255 * 2 = carry. +PLY_AKG_OPCODE_JP: equ #c3 + ENDIF + +PLY_AKG_OPCODE_ADD_HL_BC_LSB: equ #09 ;Opcode for "add hl,bc", LSB. +PLY_AKG_OPCODE_ADD_HL_BC_MSB: equ #00 ;Opcode for "add hl,bc", MSB (fake, it is only 8 bits). +PLY_AKG_OPCODE_SBC_HL_BC_LSB: equ #42 ;Opcode for "sbc hl,bc", LSB. +PLY_AKG_OPCODE_SBC_HL_BC_MSB: equ #ed ;Opcode for "sbc hl,bc", MSB. +PLY_AKG_OPCODE_INC_HL: equ #23 ;Opcode for "inc hl". +PLY_AKG_OPCODE_DEC_HL: equ #2b ;Opcode for "dec hl". +PLY_AKG_OPCODE_ADD_A_IMMEDIATE: equ #c6 ;Opcode for "add a,x". +PLY_AKG_OPCODE_SUB_IMMEDIATE: equ #d6 ;Opcode for "sub x". + + + ;Disark macro: Word region Start. + disarkCounter = 0 + IFNDEF dkws + MACRO dkws +PLY_AKG_DisarkWordRegionStart_{disarkCounter} + ENDM + ENDIF + ;Disark macro: Word region End. + IFNDEF dkwe + MACRO dkwe +PLY_AKG_DisarkWordRegionEnd_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + ;Disark macro: Pointer region Start. + disarkCounter = 0 + IFNDEF dkps + MACRO dkps +PLY_AKG_DisarkPointerRegionStart_{disarkCounter} + ENDM + ENDIF + ;Disark macro: Pointer region End. + IFNDEF dkpe + MACRO dkpe +PLY_AKG_DisarkPointerRegionEnd_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + ;Disark macro: Byte region Start. + disarkCounter = 0 + IFNDEF dkbs + MACRO dkbs +PLY_AKG_DisarkByteRegionStart_{disarkCounter} + ENDM + ENDIF + ;Disark macro: Byte region End. + IFNDEF dkbe + MACRO dkbe +PLY_AKG_DisarkByteRegionEnd_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + ;Disark macro: Force "No Reference Area" for 3 bytes (ld hl,xxxx). + IFNDEF dknr3 + MACRO dknr3 +PLY_AKG_DisarkForceNonReferenceDuring3_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + ;Hooks for external calls. Can be removed if not needed. + if PLY_AKG_USE_HOOKS + assert PLY_AKG_Start == $ ;Makes sure no extra byte were inserted before the hooks. + jp PLY_AKG_Init ;PLY_AKG_Start + 0. + jp PLY_AKG_Play ;PLY_AKG_Start + 3. + if PLY_AKG_STOP_SOUNDS + jp PLY_AKG_Stop ;PLY_AKG_Start + 6. + endif ;PLY_AKG_STOP_SOUNDS + endif ;PLY_AKG_USE_HOOKS + + ;Includes the sound effects player, if wanted. Important to do it as soon as possible, so that + ;its code can react to the Player Configuration and possibly alter it. + IFDEF PLY_AKG_MANAGE_SOUND_EFFECTS + include "PlayerAkg_SoundEffects.asm" + ENDIF ;PLY_AKG_MANAGE_SOUND_EFFECTS + ;[[INSERT_SOUND_EFFECT_SOURCE]] ;A tag for test units. Don't touch or you're dead. + +;Initializes the player. +;IN: HL = music address. +; A = subsong index (>=0). +PLY_AKG_InitDisarkGenerateExternalLabel: +PLY_AKG_Init: + + IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC + ;Skips the tag. +dknr3: ld de,4 + add hl,de + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld de,PLY_AKG_ArpeggiosTable + PLY_AKG_Offset1b + ldi + ldi + ELSE + inc hl + inc hl + ENDIF + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + ld de,PLY_AKG_PitchesTable + PLY_AKG_Offset1b + ldi + ldi + ELSE + inc hl + inc hl + ENDIF ;PLY_CFG_UseEffect_PitchTable + ELSE + ;No effects. Skips the tag and the arp/pitch table. +dknr3: ld de,4 + 2 + 2 + add hl,de + ENDIF ;PLY_CFG_UseEffects + ld de,PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b + ldi + ldi + IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC + ld c,(hl) + inc hl + ld b,(hl) + inc hl + ld (PLY_AKG_Channel_ReadEffects_EffectBlocks1 + PLY_AKG_Offset1b),bc + IFNDEF PLY_AKG_Rom + ;Not used in ROM, the same value is used. + ld (PLY_AKG_Channel_ReadEffects_EffectBlocks2 + PLY_AKG_Offset1b),bc + ENDIF + ELSE + ;No effects. Skips the effect block table. + inc hl + inc hl + ENDIF ;PLY_CFG_UseEffects + + + ;We have reached the Subsong addresses. Which one to use? + add a,a + ld e,a + ld d,0 + add hl,de + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + ;HL points on the Subsong metadata. +dknr3: ld de,5 ;Skips the replay frequency, digichannel, psg count, loop ^art index, end index. + add hl,de + ld de,PLY_AKG_CurrentSpeed + PLY_AKG_Offset1b ;Reads the initial speed (>0). + ldi + ld de,PLY_AKG_BaseNoteIndex + PLY_AKG_Offset1b ;Reads the base note of the note that is considered "optimized", contrary to "escaped". + ldi + ld (PLY_AKG_ReadLinker_PtLinker + PLY_AKG_Offset1b),hl + + ;Initializes values. You can remove this part if you don't stop/restart your song. + if PLY_AKG_FULL_INIT_CODE + ld hl,PLY_AKG_InitTable0 +dknr3: ld bc,((PLY_AKG_InitTable0_End - PLY_AKG_InitTable0) / 2 + 1) * 256 + 0 + call PLY_AKG_Init_ReadWordsAndFill + inc c + ld hl,PLY_AKG_InitTable1 + ld b,(PLY_AKG_InitTable1_End - PLY_AKG_InitTable1) / 2 + 1 + call PLY_AKG_Init_ReadWordsAndFill + ld hl,PLY_AKG_InitTableOrA +dknr3: ld bc,((PLY_AKG_InitTableOrA_End - PLY_AKG_InitTableOrA) / 2 + 1) * 256 + PLY_AKG_OPCODE_OR_A + call PLY_AKG_Init_ReadWordsAndFill + + IFDEF PLY_AKG_Rom + ;The ROM version requires a bit more of setup. + ld hl,PLY_AKG_InitTableJp +dknr3: ld bc,((PLY_AKG_InitTableJp_End - PLY_AKG_InitTableJp) / 2 + 1) * 256 + PLY_AKG_OPCODE_JP + call PLY_AKG_Init_ReadWordsAndFill + ENDIF + + IFDEF PLY_CFG_UseRetrig ;CONFIG SPECIFIC + ld a,255 + ld (PLY_AKG_PSGReg13_OldValue + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseRetrig + endif + + ;Stores the address to the empty instrument *data* (header skipped). + ld hl,(PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b) + ld e,(hl) + inc hl + ld d,(hl) + ex de,hl + inc hl ;Skips the header. + ld (PLY_AKG_EmptyInstrumentDataPt + PLY_AKG_Offset1b),hl + ;Sets all the instrument to "empty". + ld (PLY_AKG_Channel1_PtInstrument + PLY_AKG_Offset1b),hl + ld (PLY_AKG_Channel2_PtInstrument + PLY_AKG_Offset1b),hl + ld (PLY_AKG_Channel3_PtInstrument + PLY_AKG_Offset1b),hl + + ;The ROM version requires a bit more of setup. + IFDEF PLY_AKG_Rom + REPEAT 3, channelNumber + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC + ld hl,PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAddOrSubReturn + ld (PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress + 1),hl + ld hl,PLY_AKG_Channel{channelNumber}_PitchTrackAddOrSbc_16bitsReturn + ld (PLY_AKG_Channel{channelNumber}_PitchTrackAfterAddOrSbcJumpInstrAndAddress + 1),hl + + ld hl,PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValueReturnAfterJp + ld (PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValueReturnJp + 1),hl + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + REND + ENDIF + ret + + if PLY_AKG_FULL_INIT_CODE +;Fills all the read addresses with a byte. +;IN: HL = table where the addresses are. +; B = how many items in the table + 1. +; C = byte to fill. +PLY_AKG_Init_ReadWordsAndFill_Loop: + ld e,(hl) + inc hl + ld d,(hl) + inc hl + ld a,c + ld (de),a +PLY_AKG_Init_ReadWordsAndFill: + djnz PLY_AKG_Init_ReadWordsAndFill_Loop + ret + +;Table initializing some data with 0. +PLY_AKG_InitTable0: +dkps ;Disark macro. + dw PLY_AKG_Channel1_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + dw PLY_AKG_Channel1_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + 1 ;PLY_AKG_Offset2b must NOT be used here. + dw PLY_AKG_Channel2_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + dw PLY_AKG_Channel2_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + 1 + dw PLY_AKG_Channel3_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + dw PLY_AKG_Channel3_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + 1 + IFDEF PLY_AKS_UseEffect_PitchUpOrDown ;CONFIG SPECIFIC + dw PLY_AKG_Channel1_Pitch + PLY_AKG_Offset1b + dw PLY_AKG_Channel1_Pitch + PLY_AKG_Offset1b + 1 + dw PLY_AKG_Channel2_Pitch + PLY_AKG_Offset1b + dw PLY_AKG_Channel2_Pitch + PLY_AKG_Offset1b + 1 + dw PLY_AKG_Channel3_Pitch + PLY_AKG_Offset1b + dw PLY_AKG_Channel3_Pitch + PLY_AKG_Offset1b + 1 + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDown + IFDEF PLY_CFG_UseRetrig ;CONFIG SPECIFIC + dw PLY_AKG_Retrig + 1 + ENDIF ;PLY_CFG_UseRetrig +dkpe ;Disark macro. +PLY_AKG_InitTable0_End: + +PLY_AKG_InitTable1: +dkps ;Disark macro. + dw PLY_AKG_PatternDecreasingHeight + PLY_AKG_Offset1b + dw PLY_AKG_TickDecreasingCounter + PLY_AKG_Offset1b +dkpe ;Disark macro. +PLY_AKG_InitTable1_End: + +PLY_AKG_InitTableOrA: +dkps ;Disark macro. + IFDEF PLY_AKG_UseEffect_VolumeSlide ;CONFIG SPECIFIC + dw PLY_AKG_Channel1_IsVolumeSlide + dw PLY_AKG_Channel2_IsVolumeSlide + dw PLY_AKG_Channel3_IsVolumeSlide + ENDIF ;PLY_AKG_UseEffect_VolumeSlide + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + dw PLY_AKG_Channel1_IsArpeggioTable + dw PLY_AKG_Channel2_IsArpeggioTable + dw PLY_AKG_Channel3_IsArpeggioTable + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + dw PLY_AKG_Channel1_IsPitchTable + dw PLY_AKG_Channel2_IsPitchTable + dw PLY_AKG_Channel3_IsPitchTable + ENDIF ;PLY_CFG_UseEffect_PitchTable + IFDEF PLY_AKS_UseEffect_PitchUpOrDown ;CONFIG SPECIFIC + dw PLY_AKG_Channel1_IsPitch + dw PLY_AKG_Channel2_IsPitch + dw PLY_AKG_Channel3_IsPitch + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDown +dkpe ;Disark macro. +PLY_AKG_InitTableOrA_End: + endif ;PLY_AKG_FULL_INIT_CODE + + IFDEF PLY_AKG_Rom +PLY_AKG_InitTableJp: +dkps ;Disark macro. + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide + dw PLY_AKG_Channel1_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress + dw PLY_AKG_Channel2_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress + dw PLY_AKG_Channel3_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress + dw PLY_AKG_Channel1_PitchTrackAfterAddOrSbcJumpInstrAndAddress + dw PLY_AKG_Channel2_PitchTrackAfterAddOrSbcJumpInstrAndAddress + dw PLY_AKG_Channel3_PitchTrackAfterAddOrSbcJumpInstrAndAddress + dw PLY_AKG_Channel1_PitchTrackDecimalInstrAndValueReturnJp + dw PLY_AKG_Channel2_PitchTrackDecimalInstrAndValueReturnJp + dw PLY_AKG_Channel3_PitchTrackDecimalInstrAndValueReturnJp + ENDIF + + IFDEF PLY_CFG_UseEffects + dw PLY_AKG_Channel_ReadEffects_EndJumpInstrAndAddress + ENDIF + dw PLY_AKG_TempPlayInstrumentJumpInstrAndAddress +dkpe ;Disark macro. +PLY_AKG_InitTableJp_End: + ENDIF + + if PLY_AKG_STOP_SOUNDS +;Stops the music. This code can be removed if you don't intend to stop it! +PLY_AKG_StopDisarkGenerateExternalLabel: +PLY_AKG_Stop: + ld (PLY_AKG_SaveSP + PLY_AKG_Offset1b),sp ;Only useful because the PLY_AKG_SendPSGRegisters restores it at the end. + + ;All the volumes to 0, all sound/noise channels stopped. + xor a + ld l,a + ld h,a + ld (PLY_AKG_PSGReg8),a + ld (PLY_AKG_PSGReg9_10_Instr + PLY_AKG_Offset1b),hl + IFDEF PLY_AKG_HARDWARE_MSX + ld a,%10111111 ;On MSX, bit 7 must be 1, bit 6 0. + ELSE + ld a,%00111111 ;On CPC, bit 6 must be 0. Other platforms don't care. + ENDIF + jp PLY_AKG_SendPSGRegisters + endif ;PLY_AKG_STOP_SOUNDS + +;Plays one frame of the subsong. +PLY_AKG_PlayDisarkGenerateExternalLabel: +PLY_AKG_Play: + ld (PLY_AKG_SaveSP + PLY_AKG_Offset1b),sp + + IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC + xor a + ld (PLY_AKG_Event),a + ENDIF ;PLY_CFG_UseEventTracks + + ;Decreases the tick counter. If 0 is reached, a new line must be read. + IFNDEF PLY_AKG_Rom +PLY_AKG_TickDecreasingCounter: ld a,1 + ELSE + ld a,(PLY_AKG_TickDecreasingCounter) + ENDIF + dec a + jp nz,PLY_AKG_SetSpeedBeforePlayStreams ;Jumps if there is no new line: continues playing the sound stream. + + ;New line! Is the Pattern ended? Not as long as there are lines to read. + IFNDEF PLY_AKG_Rom +PLY_AKG_PatternDecreasingHeight: ld a,1 + ELSE + ld a,(PLY_AKG_PatternDecreasingHeight) + ENDIF + dec a + jr nz,PLY_AKG_SetCurrentLineBeforeReadLine ;Jumps if the pattern isn't ended. + + ;New pattern! + ;Reads the Linker. This is called at the start of the song, or at the end of every position. +PLY_AKG_ReadLinker: + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_ReadLinker_PtLinker: ld sp,0 + ELSE + ld sp,(PLY_AKG_ReadLinker_PtLinker) + ENDIF + ;Reads the address of each Track. + pop hl + ld a,l + or h + jr nz,PLY_AKG_ReadLinker_NoLoop ;Reached the end of the song? + ;End of the song. + pop hl ;HL is the loop address. + ld sp,hl + pop hl ;Reads once again the address of Track 1, in the pattern looped to. +PLY_AKG_ReadLinker_NoLoop: + ld (PLY_AKG_Channel1_PtTrack + PLY_AKG_Offset1b),hl + pop hl + ld (PLY_AKG_Channel2_PtTrack + PLY_AKG_Offset1b),hl + pop hl + ld (PLY_AKG_Channel3_PtTrack + PLY_AKG_Offset1b),hl + ;Reads the address of the LinkerBlock. + pop hl + ld (PLY_AKG_ReadLinker_PtLinker + PLY_AKG_Offset1b),sp + ld sp,hl + + ;Reads the LinkerBlock. SP = LinkerBlock. + ;Reads the height and transposition1. + pop hl + ld c,l ;Stores the pattern height, used below. + IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC + ld a,h + ld (PLY_AKG_Channel1_Transposition + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseTranspositions + ;Reads the transposition2 and 3. + IFDEF PLY_AKG_UseSpecialTracks ;CONFIG SPECIFIC + IFNDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC + ;Transpositions not used? We could stop here. BUT the SpecialTracks, if present, must access their data after. + ;So in this case, the transpositions must be skipped. + pop hl + ENDIF ;PLY_CFG_UseTranspositions + ENDIF ;PLY_AKG_UseSpecialTracks + IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC + pop hl + ld a,l + ld (PLY_AKG_Channel2_Transposition + PLY_AKG_Offset1b),a + ld a,h + ld (PLY_AKG_Channel3_Transposition + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseTranspositions + IFDEF PLY_AKG_UseSpecialTracks ;CONFIG SPECIFIC + ;Reads the special Tracks addresses. + pop hl ;Must be performed even SpeedTracks not used, because EventTracks might be present, the word must be skipped. + IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC + ld (PLY_AKG_SpeedTrack_PtTrack + PLY_AKG_Offset1b),hl + ENDIF ;PLY_CFG_UseSpeedTracks + + IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC + pop hl + ld (PLY_AKG_EventTrack_PtTrack + PLY_AKG_Offset1b),hl + ENDIF ;PLY_CFG_UseEventTracks + ENDIF ;PLY_AKG_UseSpecialTracks + + xor a + ;Forces the reading of every Track and Special Track. + IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC + ld (PLY_AKG_SpeedTrack_WaitCounter + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseSpeedTracks + IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC + ld (PLY_AKG_EventTrack_WaitCounter + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseEventTracks + ld (PLY_AKG_Channel1_WaitCounter + PLY_AKG_Offset1b),a + ld (PLY_AKG_Channel2_WaitCounter + PLY_AKG_Offset1b),a + ld (PLY_AKG_Channel3_WaitCounter + PLY_AKG_Offset1b),a + ld a,c +PLY_AKG_SetCurrentLineBeforeReadLine: + ld (PLY_AKG_PatternDecreasingHeight + PLY_AKG_Offset1b),a + + + ;Reads the new line (notes, effects, Special Tracks, etc.). +PLY_AKG_ReadLine: + ;Reads the Speed Track. + IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC + ;------------------------------------------------------------------- + IFNDEF PLY_AKG_Rom +PLY_AKG_SpeedTrack_WaitCounter: ld a,0 ;Lines to wait? + ELSE + ld a,(PLY_AKG_SpeedTrack_WaitCounter) + ENDIF + sub 1 + jr nc,PLY_AKG_SpeedTrack_MustWait ;Jump if there are still lines to wait. + ;No more lines to wait. Reads a new data. It may be an event value or a wait value. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_SpeedTrack_PtTrack: ld hl,0 + ELSE + ld hl,(PLY_AKG_SpeedTrack_PtTrack) + ENDIF + ld a,(hl) + inc hl + srl a ;Bit 0: wait? + jr c,PLY_AKG_SpeedTrack_StorePointerAndWaitCounter ;Jump if wait: A is the wait value. + ;Value found. If 0, escape value (rare). + jr nz,PLY_AKG_SpeedTrack_NormalValue + ;Escape code. Reads the right value. + ld a,(hl) + inc hl +PLY_AKG_SpeedTrack_NormalValue: + ld (PLY_AKG_CurrentSpeed + PLY_AKG_Offset1b),a + + xor a ;Next time, a new value is read. +PLY_AKG_SpeedTrack_StorePointerAndWaitCounter: + ld (PLY_AKG_SpeedTrack_PtTrack + PLY_AKG_Offset1b),hl +PLY_AKG_SpeedTrack_MustWait: + ld (PLY_AKG_SpeedTrack_WaitCounter + PLY_AKG_Offset1b),a +PLY_AKG_SpeedTrack_End: + ENDIF ;PLY_CFG_UseSpeedTracks + + + + + + ;Reads the Event Track. + ;------------------------------------------------------------------- + IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +PLY_AKG_EventTrack_WaitCounter: ld a,0 ;Lines to wait? + ELSE + ld a,(PLY_AKG_EventTrack_WaitCounter) + ENDIF + sub 1 + jr nc,PLY_AKG_EventTrack_MustWait ;Jump if there are still lines to wait. + ;No more lines to wait. Reads a new data. It may be an event value or a wait value. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_EventTrack_PtTrack: ld hl,0 + ELSE + ld hl,(PLY_AKG_EventTrack_PtTrack) + ENDIF + ld a,(hl) + inc hl + srl a ;Bit 0: wait? + jr c,PLY_AKG_EventTrack_StorePointerAndWaitCounter ;Jump if wait: A is the wait value. + ;Value found. If 0, escape value (rare). + jr nz,PLY_AKG_EventTrack_NormalValue + ;Escape code. Reads the right value. + ld a,(hl) + inc hl +PLY_AKG_EventTrack_NormalValue: + ld (PLY_AKG_Event),a + + xor a ;Next time, a new value is read. +PLY_AKG_EventTrack_StorePointerAndWaitCounter: + ld (PLY_AKG_EventTrack_PtTrack + PLY_AKG_Offset1b),hl +PLY_AKG_EventTrack_MustWait: + ld (PLY_AKG_EventTrack_WaitCounter + PLY_AKG_Offset1b),a +PLY_AKG_EventTrack_End: + ENDIF ;PLY_CFG_UseEventTracks + + + + + + + + ;------------------------------------------------------------------------- + ;Reads the possible Cell of the Channel 1, 2 and 3. Use a Macro for each channel, but the code is duplicated. + ;------------------------------------------------------------------------- + + MACRO PLY_AKG_ReadTrack channelNumber + + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_WaitCounter: ld a,0 ;Lines to wait? + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_WaitCounter) + ENDIF + sub 1 + jr c,PLY_AKG_Channel{channelNumber}_ReadTrack + ;Still some lines to wait. + ld (PLY_AKG_Channel{channelNumber}_WaitCounter + PLY_AKG_Offset1b),a + jp PLY_AKG_Channel{channelNumber}_ReadCellEnd + +PLY_AKG_Channel{channelNumber}_ReadTrack: + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_Channel{channelNumber}_PtTrack: ld hl,0 ;Points on the Cell to read. + ELSE + ld hl,(PLY_AKG_Channel{channelNumber}_PtTrack) + ENDIF + ;Reads note data. It can be a note, a wait... + + ld c,(hl) ;C = data (b5-0) + effect? (b6) + new Instrument? (b7). + inc hl + ld a,c + and %111111 ;A = data. + cp 60 ;0-59: note. "cp" is preferred to "sub" so that the "note" branch (the slowest) is note-ready. + jr c,PLY_AKG_Channel{channelNumber}_Note + sub 60 + jp z,PLY_AKG_Channel{channelNumber}_MaybeEffects ;60 = no note, but maybe effects. + dec a + jr z,PLY_AKG_Channel{channelNumber}_Wait ;61 = wait, no effect. + dec a + jr z,PLY_AKG_Channel{channelNumber}_SmallWait ;62 = small wait, no effect. + ;63 = escape code for note, maybe effects. + ;Reads the note in the next byte (HL has already been incremented). + ld a,(hl) + inc hl + jr PLY_AKG_Channel{channelNumber}_AfterNoteKnown + + ;Small wait, no effect. +PLY_AKG_Channel{channelNumber}_SmallWait: + ld a,c ;Uses bit 6/7 to indicate how many lines to wait. + rlca + rlca + and %11 + inc a ;This wait start at 2 lines, to 5. + ld (PLY_AKG_Channel{channelNumber}_WaitCounter + PLY_AKG_Offset1b),a + jr PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer + + ;Wait, no effect. +PLY_AKG_Channel{channelNumber}_Wait: + ld a,(hl) ;Reads the wait value on the next byte (HL has already been incremented). + ld (PLY_AKG_Channel{channelNumber}_WaitCounter + PLY_AKG_Offset1b),a + inc hl + jr PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer + + ;Little subcode put here, called just below. A bit dirty, but avoids long jump. +PLY_AKG_Channel{channelNumber}_SameInstrument: + ;No new instrument. The instrument pointer must be reset. + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel{channelNumber}_PtBaseInstrument: ld de,0 + ELSE + ld de,(PLY_AKG_Channel{channelNumber}_PtBaseInstrument) + ENDIF + ld (PLY_AKG_Channel{channelNumber}_PtInstrument + PLY_AKG_Offset1b),de + jr PLY_AKG_Channel{channelNumber}_AfterInstrument + + ;A note has been found, plus maybe an Instrument and effects. A = note. C = still has the New Instrument/Effects flags. +PLY_AKG_Channel{channelNumber}_Note: + if {channelNumber} == 1 ;Declares this only for the first channel, else refers to it. + IFNDEF PLY_AKG_Rom +PLY_AKG_BaseNoteIndex: add a,0 ;The encoded note is only from a 4 octave range, but the first note depends on he best window, determined by the song generator. + ELSE + ld b,a + ld a,(PLY_AKG_BaseNoteIndex + PLY_AKG_Offset1b) + add a,b + ENDIF + else + ld b,a + ld a,(PLY_AKG_BaseNoteIndex + PLY_AKG_Offset1b) + add a,b + endif +PLY_AKG_Channel{channelNumber}_AfterNoteKnown: + IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_Transposition: add a,0 ;Adds the Track transposition. + ELSE + ld b,a + ld a,(PLY_AKG_Channel{channelNumber}_Transposition + PLY_AKG_Offset1b) + add a,b + ENDIF + ENDIF ;PLY_CFG_UseTranspositions + ld (PLY_AKG_Channel{channelNumber}_TrackNote + PLY_AKG_Offset1b),a + + ;HL = next data. C = data byte. + rl c ;New Instrument? + jr nc,PLY_AKG_Channel{channelNumber}_SameInstrument + ;Gets the new Instrument. + ld a,(hl) + inc hl + exx + if {channelNumber} == 1 ;Declares this only for the first channel, else refers to it. + ld l,a + ld h,0 + add hl,hl + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_InstrumentsTable: ld de,0 ;Points on the Instruments table of the music (set on song initialization). + ELSE + ld de,(PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b) + ENDIF + add hl,de + else + ld e,a + ld d,0 + ld hl,(PLY_AKG_InstrumentsTable + PLY_AKG_Offset1b) ;Points on the Instruments table of the music (set on song initialization). + add hl,de + add hl,de + endif + ld sp,hl + pop hl + + ld a,(hl) ;Gets the speed. + inc hl + ;No need to store an "original speed" if "force instrument speed" effect is not used. + IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed ;CONFIG SPECIFIC + ld (PLY_AKG_Channel{channelNumber}_InstrumentOriginalSpeed + PLY_AKG_Offset1b),a + ELSE + ld (PLY_AKG_Channel{channelNumber}_InstrumentSpeed + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseEffect_ForceInstrumentSpeed + ld (PLY_AKG_Channel{channelNumber}_PtInstrument + PLY_AKG_Offset1b),hl + ld (PLY_AKG_Channel{channelNumber}_PtBaseInstrument + PLY_AKG_Offset1b),hl ;Useful when playing another note with the same instrument. + exx +PLY_AKG_Channel{channelNumber}_AfterInstrument: + + ;There is a new note. The instrument pointer has already been reset. + ;------------------------------------------------------------------- + ;Instrument number is set. + ;Arpeggio and Pitch Table are reset. + + ;HL must be preserved! But it is faster to use HL than DE when storing 16 bits value. + ;So it is stored in DE for now. + ex de,hl + + ;The track pitch and glide, instrument step are reset. + xor a + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC + ld l,a + ld h,a + ld (PLY_AKG_Channel{channelNumber}_Pitch + PLY_AKG_Offset1b),hl + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld (PLY_AKG_Channel{channelNumber}_ArpeggioTableCurrentStep + PLY_AKG_Offset1b),a + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + ld (PLY_AKG_Channel{channelNumber}_PitchTableCurrentStep + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseEffect_PitchTable + ld (PLY_AKG_Channel{channelNumber}_InstrumentStep + PLY_AKG_Offset2b),a + + ;If the "force instrument speed" effect is used, the instrument speed must be reset to its original value. + IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_InstrumentOriginalSpeed ld a,0 + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_InstrumentOriginalSpeed) + ENDIF + ld (PLY_AKG_Channel{channelNumber}_InstrumentSpeed + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseEffect_ForceInstrumentSpeed + + IFDEF PLY_AKS_UseEffect_PitchUpOrDown ;CONFIG SPECIFIC + ld a,PLY_AKG_OPCODE_OR_A + ld (PLY_AKG_Channel{channelNumber}_IsPitch),a + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDown + + ;Resets the speed of the Arpeggio and the Pitch. + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld a,(PLY_AKG_Channel{channelNumber}_ArpeggioBaseSpeed) + ld (PLY_AKG_Channel{channelNumber}_ArpeggioTableSpeed),a + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + ld a,(PLY_AKG_Channel{channelNumber}_PitchBaseSpeed) + ld (PLY_AKG_Channel{channelNumber}_PitchTableSpeed),a + ENDIF ;PLY_CFG_UseEffect_PitchTable + + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld hl,(PLY_AKG_Channel{channelNumber}_ArpeggioTableBase) ;Points to the first value of the Arpeggio. + ld (PLY_AKG_Channel{channelNumber}_ArpeggioTable + PLY_AKG_Offset1b),hl + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + ld hl,(PLY_AKG_Channel{channelNumber}_PitchTableBase) ;Points to the first value of the Pitch. + ld (PLY_AKG_Channel{channelNumber}_PitchTable + PLY_AKG_Offset1b),hl + ENDIF ;PLY_CFG_UseEffect_PitchTable + + ex de,hl + + IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC + ;Effects? + rl c + jp c,PLY_AKG_Channel{channelNumber}_ReadEffects + ENDIF ;PLY_CFG_UseEffects + + ;No effects. Nothing more to read for this cell. +PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer: + ld (PLY_AKG_Channel{channelNumber}_PtTrack + PLY_AKG_Offset1b),hl +PLY_AKG_Channel{channelNumber}_ReadCellEnd: + + ENDM ;PLY_AKG_ReadTrack + + ;Generates the code for each channel, from the macro above. + PLY_AKG_ReadTrack 1 + PLY_AKG_ReadTrack 2 + PLY_AKG_ReadTrack 3 + + + + + + + + + + + + + IFNDEF PLY_AKG_Rom +PLY_AKG_CurrentSpeed: ld a,0 ;>0. + ELSE + ld a,(PLY_AKG_CurrentSpeed) + ENDIF +PLY_AKG_SetSpeedBeforePlayStreams: + ld (PLY_AKG_TickDecreasingCounter + PLY_AKG_Offset1b),a + + + + + + + + ;----------------------------------------------------------------------------------------- + ;Applies the trailing effects for channel 1, 2, 3. Uses a macro instead of duplicating the code. + ;----------------------------------------------------------------------------------------- + + MACRO PLY_AKG_ApplyTrailingEffects channelNumber + + ;Use Volume slide? + ;---------------------------- + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_Channel{channelNumber}_InvertedVolumeIntegerAndDecimal: ld hl,0 + ELSE + ld hl,(PLY_AKG_Channel{channelNumber}_InvertedVolumeIntegerAndDecimal) + ENDIF +PLY_AKG_Channel{channelNumber}_InvertedVolumeInteger: equ PLY_AKG_Channel{channelNumber}_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b + 1 + IFDEF PLY_AKG_UseEffect_VolumeSlide ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_IsVolumeSlide: or a ;Is there a Volume Slide ? Automodified. SCF if yes, OR A if not. + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_IsVolumeSlide) + add a,a ;Creates the carry or not. + ENDIF + jr nc,PLY_AKG_Channel{channelNumber}_VolumeSlide_End + + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel{channelNumber}_VolumeSlideValue: ld de,0 ;May be negative. + ELSE + ld de,(PLY_AKG_Channel{channelNumber}_VolumeSlideValue) + ENDIF + add hl,de + ;Went below 0? + bit 7,h + jr z,PLY_AKG_Channel{channelNumber}_VolumeNotOverflow + ld h,0 ;No need to set L to 0... Shouldn't make any hearable difference. + jr PLY_AKG_Channel{channelNumber}_VolumeSetAgain +PLY_AKG_Channel{channelNumber}_VolumeNotOverflow: + ;Higher than 15? + ld a,h + cp 16 + jr c,PLY_AKG_Channel{channelNumber}_VolumeSetAgain + ld h,15 +PLY_AKG_Channel{channelNumber}_VolumeSetAgain: + ld (PLY_AKG_Channel{channelNumber}_InvertedVolumeIntegerAndDecimal + PLY_AKG_Offset1b),hl + +PLY_AKG_Channel{channelNumber}_VolumeSlide_End: + ENDIF ;PLY_AKG_UseEffect_VolumeSlide + ld a,h + ld (PLY_AKG_Channel{channelNumber}_GeneratedCurrentInvertedVolume + PLY_AKG_Offset1b),a + + + + + + ;Use Arpeggio table? OUT: C = value. + ;---------------------------------------- + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld c,0 ;Default value of the arpeggio. + + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_IsArpeggioTable: or a ;Is there an arpeggio table? Automodified. SCF if yes, OR A if not. + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_IsArpeggioTable) + add a,a ;Creates the carry or not. + ENDIF + jr nc,PLY_AKG_Channel{channelNumber}_ArpeggioTable_End + + ;We can read the Arpeggio table for a new value. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_Channel{channelNumber}_ArpeggioTable: ld hl,0 ;Points on the data, after the header. + ELSE + ld hl,(PLY_AKG_Channel{channelNumber}_ArpeggioTable) + ENDIF + ld a,(hl) + cp -128 ;Loop? + jr nz,PLY_AKG_Channel{channelNumber}_ArpeggioTable_AfterLoopTest + ;Loop. Where to? + inc hl + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + ld a,(hl) ;Reads the value. Safe, we know there is no loop here. + + ;HL = pointer on what is follows. + ;A = value to use. +PLY_AKG_Channel{channelNumber}_ArpeggioTable_AfterLoopTest: + ld c,a + + ;Checks the speed. If reached, the pointer can be saved to read a new value next time. + ld a,(PLY_AKG_Channel{channelNumber}_ArpeggioTableSpeed) + ld d,a + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_ArpeggioTableCurrentStep: ld a,0 + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_ArpeggioTableCurrentStep) + ENDIF + inc a + cp d ;From 1 to 256. + jr c,PLY_AKG_Channel{channelNumber}_ArpeggioTable_BeforeEnd_SaveStep ;C, not NZ, because the current step may be higher than the limit if Force Speed effect is used. + ;Stores the pointer to read a new value next time. + inc hl + ld (PLY_AKG_Channel{channelNumber}_ArpeggioTable + PLY_AKG_Offset1b),hl + + xor a +PLY_AKG_Channel{channelNumber}_ArpeggioTable_BeforeEnd_SaveStep: + ld (PLY_AKG_Channel{channelNumber}_ArpeggioTableCurrentStep + PLY_AKG_Offset1b),a +PLY_AKG_Channel{channelNumber}_ArpeggioTable_End: + ENDIF ;PLY_AKS_UseEffect_Arpeggio + + + + ;Use Pitch table? OUT: DE = pitch value. + ;C must NOT be modified! + ;----------------------- + +dknr3 + ld de,0 ;Default value. + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_IsPitchTable: or a ;Is there an arpeggio table? Automodified. SCF if yes, OR A if not. + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_IsPitchTable) + add a,a ;Creates the carry or not. + ENDIF + jr nc,PLY_AKG_Channel{channelNumber}_PitchTable_End + + ;Read the Pitch table for a value. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_Channel{channelNumber}_PitchTable: ld sp,0 ;Points on the data, after the header. + ELSE + ld sp,(PLY_AKG_Channel{channelNumber}_PitchTable) + ENDIF + pop de ;Reads the value. + pop hl ;Reads the pointer to the next value. Manages the loop automatically! + + ;Checks the speed. If reached, the pointer can be saved (advance in the Pitch). + ld a,(PLY_AKG_Channel{channelNumber}_PitchTableSpeed) + ld b,a + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_PitchTableCurrentStep: ld a,0 + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_PitchTableCurrentStep) + ENDIF + inc a + cp b ;From 1 to 256. + jr c,PLY_AKG_Channel{channelNumber}_PitchTable_BeforeEnd_SaveStep ;C, not NZ, because the current step may be higher than the limit if Force Speed effect is used. + ;Advances in the Pitch. + ld (PLY_AKG_Channel{channelNumber}_PitchTable + PLY_AKG_Offset1b),hl + + xor a +PLY_AKG_Channel{channelNumber}_PitchTable_BeforeEnd_SaveStep: + ld (PLY_AKG_Channel{channelNumber}_PitchTableCurrentStep + PLY_AKG_Offset1b),a +PLY_AKG_Channel{channelNumber}_PitchTable_End: + ENDIF ;PLY_CFG_UseEffect_PitchTable + + + + ;Pitch management. The Glide is embedded, but relies on the Pitch (Pitch can exist without Glide, but Glide can not without Pitch). + ;Do NOT modify C or DE. + ;------------------------------------------------------------------------------------------ + IFNDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC +dknr3: ld hl,0 ;No pitch. + IFNDEF PLY_AKG_Rom ;Nothing to declare if ROM. + ;Some dirty duplication in case there is no pitch up/down/glide. The "real" vars are a bit below. +PLY_AKG_Channel{channelNumber}_SoundStream_RelativeModifierAddress: ;Put here, no need for better place (see the real label below, with the same name). + IFDEF PLY_AKS_UseEffect_ArpeggioTableOrPitchTable ;CONFIG SPECIFIC + jr PLY_AKG_Channel{channelNumber}_AfterArpeggioPitchVariables + + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC +dkbs ;Disark macro. +PLY_AKG_Channel{channelNumber}_ArpeggioTableSpeed: db 0 +PLY_AKG_Channel{channelNumber}_ArpeggioBaseSpeed: db 0 +PLY_AKG_Channel{channelNumber}_ArpeggioTableBase: dw 0 +dkbe ;Disark macro. + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC +dkbs ;Disark macro. +PLY_AKG_Channel{channelNumber}_PitchTableSpeed: db 0 +PLY_AKG_Channel{channelNumber}_PitchBaseSpeed: db 0 +PLY_AKG_Channel{channelNumber}_PitchTableBase: dw 0 +dkbe ;Disark macro. + ENDIF ;PLY_CFG_UseEffect_PitchTable +PLY_AKG_Channel{channelNumber}_AfterArpeggioPitchVariables: + ENDIF ;PLY_AKS_UseEffect_ArpeggioTableOrPitchTable + ENDIF ;PLY_AKG_ROM + ELSE ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel{channelNumber}_Pitch: ld hl,0 +PLY_AKG_Channel{channelNumber}_IsPitch: or a ;Is there a Pitch? Automodified. SCF if yes, OR A if not. + ELSE + ld hl,(PLY_AKG_Channel{channelNumber}_Pitch) + ld a,(PLY_AKG_Channel{channelNumber}_IsPitch) + add a,a ;Creates the carry or not. + ENDIF + jr nc,PLY_AKG_Channel{channelNumber}_Pitch_End + ;C must NOT be modified, stores it. + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld ixl,c + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel{channelNumber}_PitchTrack: ld bc,0 ;Value from the user. ALWAYS POSITIVE. Does not evolve. B is always 0. + ELSE + ld bc,(PLY_AKG_Channel{channelNumber}_PitchTrack) + ENDIF + + or a ;Required if the code is changed to sbc. + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_PitchTrackAddOrSbc_16bits: nop : add hl,bc ;WILL BE AUTOMODIFIED to add or sbc. But SBC requires 2*8 bits! Damn. + ELSE + jp PLY_AKG_Channel{channelNumber}_PitchTrackAddOrSbc_16bits ;Calls a code that holds the instruction. +PLY_AKG_Channel{channelNumber}_PitchTrackAddOrSbc_16bitsReturn: + ENDIF + + ;Makes the decimal part evolves. + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalCounter: ld a,0 +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstr: add a,0 ;Value from the user. WILL BE AUTOMODIFIED to add or sub. +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalValue: equ PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstr + 1 + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_PitchTrackDecimalValue) + ld b,a + ld a,(PLY_AKG_Channel{channelNumber}_PitchTrackDecimalCounter) + ;Add a,b or sub b? Lets the subcode decide. It will return just below. + jp PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValue +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValueReturnAfterJp: + ENDIF + ld (PLY_AKG_Channel{channelNumber}_PitchTrackDecimalCounter + PLY_AKG_Offset1b),a + + jr nc,PLY_AKG_Channel{channelNumber}_PitchNoCarry + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAddOrSub: inc hl ;WILL BE AUTOMODIFIED to inc hl/dec hl + ELSE + jp PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAddOrSub ;Calls a code that holds the instruction. +PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAddOrSubReturn: + ENDIF +PLY_AKG_Channel{channelNumber}_PitchNoCarry: + ld (PLY_AKG_Channel{channelNumber}_Pitch + PLY_AKG_Offset1b),hl + + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_SoundStream_RelativeModifierAddress: ;This must be placed at the any location to allow reaching the variables via IX/IY. + ENDIF + IFDEF PLY_CFG_UseEffect_PitchGlide ;CONFIG SPECIFIC + ;Glide? + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_GlideDirection: ld a,0 ;0 = no glide. 1 = glide/pitch up. 2 = glide/pitch down. + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_GlideDirection) + ENDIF + or a ;Is there a Glide? + jr z,PLY_AKG_Channel{channelNumber}_Glide_End + + ld (PLY_AKG_Channel{channelNumber}_Glide_SaveHL + PLY_AKG_Offset1b),hl + ld c,l + ld b,h + ;Finds the period of the current note. + ex af,af' + ld a,(PLY_AKG_Channel{channelNumber}_TrackNote + PLY_AKG_Offset1b) + add a,a ;Encoded on 7 bits, so no problem. + ld l,a + ex af,af' + ld h,0 + ld sp,PLY_AKG_PeriodTable + add hl,sp + ld sp,hl + pop hl ;HL = current note period. + dec sp + dec sp ;We will need this value if the glide is over, it is faster to reuse the stack. + + add hl,bc ;HL is now the current period (note period + track pitch). + + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel{channelNumber}_GlideToReach: ld bc,0 ;Period to reach (note given by the user, converted to period). + ELSE + ld bc,(PLY_AKG_Channel{channelNumber}_GlideToReach) + ENDIF + ;Have we reached the glide destination? + ;Depends on the direction. + rra ;If 1, the carry is set. If 2, no. + jr nc,PLY_AKG_Channel{channelNumber}_GlideDownCheck + ;Glide up. Check. + ;The glide period should be lower than the current pitch. + or a + sbc hl,bc + jr nc,PLY_AKG_Channel{channelNumber}_Glide_BeforeEnd ;If not reached yet, continues the pitch. + jr PLY_AKG_Channel{channelNumber}_GlideOver + +PLY_AKG_Channel{channelNumber}_GlideDownCheck: + ;The glide period should be higher than the current pitch. + sbc hl,bc ;No carry, no need to remove it. + jr c,PLY_AKG_Channel{channelNumber}_Glide_BeforeEnd ;If not reached yet, continues the pitch. +PLY_AKG_Channel{channelNumber}_GlideOver: + ;The glide is over. However, it may be over, so we can't simply use the current pitch period. We have to set the exact needed value. + ld l,c + ld h,b + pop bc + or a + sbc hl,bc + + ld (PLY_AKG_Channel{channelNumber}_Pitch + PLY_AKG_Offset1b),hl + ld a,PLY_AKG_OPCODE_OR_A + ld (PLY_AKG_Channel{channelNumber}_IsPitch),a + ;Skips the HL restoration, the one we have is fine and will give us the right pitch to use. + jr PLY_AKG_Channel{channelNumber}_Glide_End + ELSE + IFNDEF PLY_AKG_Rom + ;Skips the variables below, if there are present. + IFDEF PLY_AKS_UseEffect_ArpeggioTableOrPitchTable ;CONFIG SPECIFIC + jr PLY_AKG_Channel{channelNumber}_AfterArpeggioPitchVariables + ENDIF ;PLY_AKS_UseEffect_ArpeggioTableOrPitchTable + ENDIF ;PLY_AKG_Rom + ENDIF ;PLY_CFG_UseEffect_PitchGlide + ;A small place to stash some vars which have to be within relative range. Dirty, but no choice. + ;Note that the vars just below are duplicated due to the conditional assembling (they are a bit above). + IFNDEF PLY_AKG_Rom + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC +dkbs ;Disark macro. +PLY_AKG_Channel{channelNumber}_ArpeggioTableSpeed: db 0 +PLY_AKG_Channel{channelNumber}_ArpeggioBaseSpeed: db 0 +PLY_AKG_Channel{channelNumber}_ArpeggioTableBase: dw 0 +dkbe ;Disark macro. + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC +dkbs ;Disark macro. +PLY_AKG_Channel{channelNumber}_PitchTableSpeed: db 0 +PLY_AKG_Channel{channelNumber}_PitchBaseSpeed: db 0 +PLY_AKG_Channel{channelNumber}_PitchTableBase: dw 0 +dkbe ;Disark macro. + ENDIF ;PLY_CFG_UseEffect_PitchTable +PLY_AKG_Channel{channelNumber}_AfterArpeggioPitchVariables: + ENDIF ;PLY_AKG_Rom + + IFDEF PLY_CFG_UseEffect_PitchGlide ;CONFIG SPECIFIC +PLY_AKG_Channel{channelNumber}_Glide_BeforeEnd: + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_Channel{channelNumber}_Glide_SaveHL: ld hl,0 ;Restores HL. + ELSE + ld hl,(PLY_AKG_Channel{channelNumber}_Glide_SaveHL) + ENDIF +PLY_AKG_Channel{channelNumber}_Glide_End: + ENDIF ;PLY_CFG_UseEffect_PitchGlide + + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld c,ixl ;Restores C (arp), saved before. + ENDIF ;PLY_AKS_UseEffect_Arpeggio + +PLY_AKG_Channel{channelNumber}_Pitch_End: + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + + + add hl,de ;Adds the Pitch Table value. + ld (PLY_AKG_Channel{channelNumber}_GeneratedCurrentPitch + PLY_AKG_Offset1b),hl + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld a,c + ld (PLY_AKG_Channel{channelNumber}_GeneratedCurrentArpNote + PLY_AKG_Offset1b),a + ENDIF ;PLY_AKS_UseEffect_Arpeggio + + ENDM ;PLY_AKG_ApplyTrailingEffects + + ;Applies the macro for each channel. + PLY_AKG_ApplyTrailingEffects 1 + PLY_AKG_ApplyTrailingEffects 2 + PLY_AKG_ApplyTrailingEffects 3 + + + + + + + + + + + + + + ;The stack must NOT be diverted during the Play Streams! + ld sp,(PLY_AKG_SaveSP + PLY_AKG_Offset1b) + + +;------------------------------------------------------------------------------------- +;Plays the instrument on channel 1, 2, 3. The PSG registers related to the channels are set. +;A macro is used instead of duplicating the code. +;------------------------------------------------------------------------------------- + + MACRO PLY_AKG_PlayInstrument channelNumber + + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_PlayInstrument_RelativeModifierAddress: ;This must be placed at the any location to allow reaching the variables via IX/IY. + ENDIF + + ;What note to play? + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_Channel{channelNumber}_GeneratedCurrentPitch: ld hl,0 ;The pitch to add to the real note, according to the Pitch Table + Pitch/Glide effect. + ELSE + ld hl,(PLY_AKG_Channel{channelNumber}_GeneratedCurrentPitch) + ENDIF + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_TrackNote: ld a,0 +PLY_AKG_Channel{channelNumber}_GeneratedCurrentArpNote: add 0 ;Adds the arpeggio value. + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_TrackNote) + ld e,a + ld a,(PLY_AKG_Channel{channelNumber}_GeneratedCurrentArpNote) + add a,e + ENDIF + ld e,a + ld d,0 + ELSE ;PLY_AKS_UseEffect_Arpeggio + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel{channelNumber}_TrackNote: ld de,0 ;Not automodified, stays this way. + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_TrackNote) ;In ROM, MUST pass by a variable anyway to be analog to if Arpeggio is used (see above). + ld e,a + ld d,0 + ENDIF + ENDIF ;PLY_AKS_UseEffect_Arpeggio + exx + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_InstrumentStep: ld iyl,0 +dknr3: +PLY_AKG_Channel{channelNumber}_PtInstrument: ld hl,0 ;Instrument data to read (past the header). + if {channelNumber} == 1 ;Different code for the first channel. +PLY_AKG_Channel{channelNumber}_GeneratedCurrentInvertedVolume: ld de,%11100000 * 256 + 15 ;R7, shift twice TO THE LEFT. By default, the noise is cut (111), the sound is on (most usual case). + else +PLY_AKG_Channel{channelNumber}_GeneratedCurrentInvertedVolume: ld e,15 + nop ;Stupid, but required for relative registers to reach addresses independently of the channels. + endif + ELSE + ld a,(PLY_AKG_Channel{channelNumber}_InstrumentStep) + ld iyl,a + ld hl,(PLY_AKG_Channel{channelNumber}_PtInstrument) + ld a,(PLY_AKG_Channel{channelNumber}_GeneratedCurrentInvertedVolume) + ld e,a + if {channelNumber} == 1 + ;For the first channel, sets the R7 value to no noise, all channel on by default (). + ld d,%11100000 + endif + ENDIF + +; D = Reg7 +; E = inverted volume. +; D' = 0, E' = note (instrument + Track transposition). +; HL' = track pitch. + + call PLY_AKG_ReadInstrumentCell + + ;The new and increased Instrument pointer is stored only if its speed has been reached. + IFDEF PLY_AKG_Rom + ld a,(PLY_AKG_Channel{channelNumber}_InstrumentSpeed) + ld b,a + ENDIF + ld a,iyl + inc a + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel{channelNumber}_InstrumentSpeed: cp 0 ;(>0) + ELSE + cp b + ENDIF + jr c,PLY_AKG_Channel{channelNumber}_SetInstrumentStep ;Checks C, not only NZ because since the speed can be changed via an effect, the step can get beyond the limit, this must be taken in account. + ;The speed is reached. We can go to the next line on the next frame. + ld (PLY_AKG_Channel{channelNumber}_PtInstrument + PLY_AKG_Offset1b),hl + xor a +PLY_AKG_Channel{channelNumber}_SetInstrumentStep: + ld (PLY_AKG_Channel{channelNumber}_InstrumentStep + PLY_AKG_Offset2b),a + + + ;Saves the software period and volume for the PSG to send later. + ld a,e + ld (PLY_AKG_PSGReg{{channelNumber} + 7}),a ;Reaches register/label 8/9/10. + + if {channelNumber} != 3 + IFDEF PLY_AKG_HARDWARE_CPC + srl d ;Shift D to the right to let room for the other channels. Use SRL, not RR, to make sure bit 6 is 0 at the end (else, no more keyboard on CPC!). + ELSE + IFDEF PLY_AKG_HARDWARE_MSX + if {channelNumber} == 1 + srl d ;R7 bit 6 on MSX must be 0. + else + scf ;R7 bit 7 on MSX must be 1. + rr d + endif + ELSE + rr d ;On other platform, we don't care. + ENDIF + ENDIF + + + else + ;Gets the R7. + ld a,d + endif + + exx + if {channelNumber} == 1 + ld (PLY_AKG_PSGReg01_Instr + PLY_AKG_Offset1b),hl + elseif {channelNumber} == 2 + ld (PLY_AKG_PSGReg23_Instr + PLY_AKG_Offset1b),hl + elseif {channelNumber} == 3 + ld (PLY_AKG_PSGReg45_Instr + PLY_AKG_Offset1b),hl + endif + + ENDM ;PLY_AKG_PlayInstrument + + + ;Generates the code for all channels using the macro above. + PLY_AKG_PlayInstrument 1 + PLY_AKG_PlayInstrument 2 + PLY_AKG_PlayInstrument 3 + + + + + +;Plays the sound effects, if desired. +;------------------------------------------- + IFDEF PLY_AKG_MANAGE_SOUND_EFFECTS + ;IN : A = R7 + ;OUT: A = R7, possibly modified. + call PLY_AKG_PlaySoundEffectsStream + ENDIF ;PLY_AKG_MANAGE_SOUND_EFFECTS + + + +; ----------------------------------------------------------------------------------- +; PSG access. +; ----------------------------------------------------------------------------------- + +;Sends the registers to the PSG. Only general registers are sent, the specific ones have already been sent. +;IN: A = R7. +PLY_AKG_SendPSGRegisters: + IFDEF PLY_AKG_HARDWARE_CPC +dknr3: ld bc,#f680 + ld e,#c0 + out (c),e ;#f6c0 ;Madram's trick requires to start with this. out (c),b works, but will activate K7's relay! Not clean. + exx +dknr3: ld bc,#f401 ;C is the PSG register. + + ;Register 0 and 1. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg01_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg01_Instr) + ENDIF + out (c),0 ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),l ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),h ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + ;Register 2 and 3. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg23_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg23_Instr) + ENDIF + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),l ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),h ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + ;Register 4 and 5. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg45_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg45_Instr) + ENDIF + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),l ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),h ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + ;Register 6. + IFDEF PLY_AKG_Use_NoiseRegister ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg6_8_Instr: ld hl,0 ;L is R6, H is R8. Faster to set a 16 bits register than 2 8-bit. +PLY_AKG_PSGReg6: equ PLY_AKG_PSGReg6_8_Instr + 1 +PLY_AKG_PSGReg8: equ PLY_AKG_PSGReg6_8_Instr + 2 + ELSE + ld hl,(PLY_AKG_PSGReg6_8_Instr) + ENDIF + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),l ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + ELSE + ;No noise. But R8 must still be set. + IFNDEF PLY_AKG_Rom +PLY_AKG_PSGReg8_Instr: ld h,0 +PLY_AKG_PSGReg8: equ PLY_AKG_PSGReg8_Instr + 1 + ELSE + ld hl,(PLY_AKG_PSGReg6_8_Instr) ;L was not useful, but A must not be modified yet. + ENDIF + inc c + ENDIF ;PLY_AKG_Use_NoiseRegister + + ;Register 7. The value is A. + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),a ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + ;Register 8. The value is loaded above via HL. + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),h ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg9_10_Instr: ld hl,0 ;L is R9, H is R10. Faster to set a 16 bits register than 2 8-bit. +PLY_AKG_PSGReg9: equ PLY_AKG_PSGReg9_10_Instr + 1 +PLY_AKG_PSGReg10: equ PLY_AKG_PSGReg9_10_Instr + 2 + ELSE + ld hl,(PLY_AKG_PSGReg9_10_Instr) + ENDIF + ;Register 9. + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),l ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + ;Register 10. + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),h ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC + ;Register 11 and 12. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGHardwarePeriod_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGHardwarePeriod_Instr) + ENDIF + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),l ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),h ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + exx + ENDIF ;PLY_CFG_UseHardwareSounds + ENDIF + IFDEF PLY_AKG_HARDWARE_SPECTRUM_OR_PENTAGON + + ex af,af' ;Saves R7. +dknr3: ld de,#bfff +dknr3: ld bc,#fffd + + ld a,1 ;Register. + + ;Register 0 and 1. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg01_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg01_Instr) + ENDIF + out (c),0 ;#fffd + register. + ld b,d + out (c),l ;#bffd + value + ld b,e + + out (c),a ;#fffd + register. + ld b,d + out (c),h ;#bffd + value + ld b,e + + ;Register 2 and 3. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg23_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg23_Instr) + ENDIF + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),l ;#bffd + value + ld b,e + + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),h ;#bffd + value + ld b,e + + ;Register 4 and 5. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg45_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg45_Instr) + ENDIF + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),l ;#bffd + value + ld b,e + + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),h ;#bffd + value + ld b,e + + ;Register 6. + IFDEF PLY_AKG_Use_NoiseRegister ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg6_8_Instr: ld hl,0 ;L is R6, H is R8. Faster to set a 16 bits register than 2 8-bit. +PLY_AKG_PSGReg6: equ PLY_AKG_PSGReg6_8_Instr + 1 +PLY_AKG_PSGReg8: equ PLY_AKG_PSGReg6_8_Instr + 2 + ELSE + ld hl,(PLY_AKG_PSGReg6_8_Instr) + ENDIF + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),l ;#bffd + value + ld b,e + ELSE + ;No noise. But R8 must still be set. + IFNDEF PLY_AKG_Rom +PLY_AKG_PSGReg8_Instr: ld h,0 +PLY_AKG_PSGReg8: equ PLY_AKG_PSGReg8_Instr + 1 + ELSE + ld hl,(PLY_AKG_PSGReg6_8_Instr) ;L not useful, but A needs to be not modified. + ENDIF + inc a + ENDIF ;PLY_AKG_Use_NoiseRegister + + ;Register 7. The value is A. + inc a + out (c),a ;#fffd + register. + ld b,d + ex af,af' ;Retrieves R7. + out (c),a ;#bffd + value + ex af,af' + ld b,e + + ;Register 8. The value is loaded above via HL. + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),h ;#bffd + value + ld b,e + + ;Register 9 and 10. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg9_10_Instr: ld hl,0 +PLY_AKG_PSGReg9: equ PLY_AKG_PSGReg9_10_Instr + 1 +PLY_AKG_PSGReg10: equ PLY_AKG_PSGReg9_10_Instr + 2 + ELSE + ld hl,(PLY_AKG_PSGReg9_10_Instr) + ENDIF + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),l ;#bffd + value + ld b,e + + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),h ;#bffd + value + ld b,e + + IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC + ;Register 11 and 12. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGHardwarePeriod_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGHardwarePeriod_Instr) + ENDIF + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),l ;#bffd + value + ld b,e + + inc a + out (c),a ;#fffd + register. + ld b,d + out (c),h ;#bffd + value + ld b,e + ENDIF ;PLY_CFG_UseHardwareSounds + + ENDIF + IFDEF PLY_AKG_HARDWARE_MSX + + ld b,a ;Preserves R7. + ld a,7 + out (#a0),a ;Register. + ld a,b + out (#a1),a ;Value. + + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg01_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg01_Instr) + ENDIF + xor a + out (#a0),a ;Register. + ld a,l + out (#a1),a ;Value. + + ld a,1 + out (#a0),a ;Register. + ld a,h + out (#a1),a ;Value. + + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg23_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg23_Instr) + ENDIF + ld a,2 + out (#a0),a ;Register. + ld a,l + out (#a1),a ;Value. + + ld a,3 + out (#a0),a ;Register. + ld a,h + out (#a1),a ;Value. + + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg45_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGReg45_Instr) + ENDIF + ld a,4 + out (#a0),a ;Register. + ld a,l + out (#a1),a ;Value. + + ld a,5 + out (#a0),a ;Register. + ld a,h + out (#a1),a ;Value. + + ;Register 6. + IFDEF PLY_AKG_Use_NoiseRegister ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg6_8_Instr: ld hl,0 ;L is R6, H is R8. Faster to set a 16 bits register than 2 8-bit. +PLY_AKG_PSGReg6: equ PLY_AKG_PSGReg6_8_Instr + 1 +PLY_AKG_PSGReg8: equ PLY_AKG_PSGReg6_8_Instr + 2 + ELSE + ld hl,(PLY_AKG_PSGReg6_8_Instr) + ENDIF + ld a,6 + out (#a0),a ;Register. + ld a,l + out (#a1),a ;Value. + + ld a,8 + out (#a0),a ;Register. + ld a,h + out (#a1),a ;Value. + ELSE + ;No noise. Takes care of R8. + ld a,8 + out (#a0),a ;Register. + IFNDEF PLY_AKG_Rom +PLY_AKG_PSGReg8_Instr: ld a,0 +PLY_AKG_PSGReg8: equ PLY_AKG_PSGReg8_Instr + 1 + ELSE + ld a,(PLY_AKG_PSGReg8) + ENDIF + out (#a1),a ;Value. + ENDIF ;PLY_AKG_Use_NoiseRegister + + ;Register 9 and 10. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGReg9_10_Instr: ld hl,0 +PLY_AKG_PSGReg9: equ PLY_AKG_PSGReg9_10_Instr + 1 +PLY_AKG_PSGReg10: equ PLY_AKG_PSGReg9_10_Instr + 2 + ELSE + ld hl,(PLY_AKG_PSGReg9_10_Instr) + ENDIF + ld a,9 + out (#a0),a ;Register. + ld a,l + out (#a1),a ;Value. + + ld a,10 + out (#a0),a ;Register. + ld a,h + out (#a1),a ;Value. + + IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC + ;Register 11 and 12. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PSGHardwarePeriod_Instr: ld hl,0 + ELSE + ld hl,(PLY_AKG_PSGHardwarePeriod_Instr) + ENDIF + ld a,11 + out (#a0),a ;Register. + ld a,l + out (#a1),a ;Value. + + ld a,12 + out (#a0),a ;Register. + ld a,h + out (#a1),a ;Value. + ENDIF ;PLY_CFG_UseHardwareSounds + + ENDIF + + + IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC + ;R13. + IFDEF PLY_AKG_HARDWARE_MSX + ld a,13 ;Selects R13 now, even if not changed, because A will be modified. + out (#a0),a ;Register. + ENDIF + IFDEF PLY_AKG_HARDWARE_SPECTRUM_OR_PENTAGON + inc a ;Selects R13 now, even if not changed, because A will be modified. + out (c),a ;#fffd + register. + ENDIF + IFDEF PLY_CFG_UseRetrig ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +PLY_AKG_PSGReg13_OldValue: ld a,255 +PLY_AKG_Retrig: or 0 ;0 = no retrig. Else, should be >0xf to be sure the old value becomes a sentinel (i.e. unreachable) value. +PLY_AKG_PSGReg13_Instr: ld l,0 ;Register 13. + ELSE + ld a,(PLY_AKG_PSGReg13_Instr) + ld l,a + ld a,(PLY_AKG_Retrig) + ld h,a + ld a,(PLY_AKG_PSGReg13_OldValue) + or h + ENDIF + cp l ;Is the new value still the same? If yes, the new value must not be set again. + jr z,PLY_AKG_PSGReg13_End + ;Different R13. + ld a,l + ELSE ;PLY_CFG_UseRetrig + IFNDEF PLY_AKG_Rom +PLY_AKG_PSGReg13_Instr: ld a,0 ;Register 13. +PLY_AKG_PSGReg13_OldValue: cp 255 + ELSE + ld a,(PLY_AKG_PSGReg13_OldValue) + ld l,a + ld a,(PLY_AKG_PSGReg13_Instr) + cp l + ENDIF + jr z,PLY_AKG_PSGReg13_End + ENDIF ;PLY_CFG_UseRetrig ;CONFIG SPECIFIC + ld (PLY_AKG_PSGReg13_OldValue + PLY_AKG_Offset1b),a + + + IFDEF PLY_AKG_HARDWARE_CPC + inc c + out (c),c ;#f400 + register. + exx + out (c),0 ;#f600. + exx + out (c),a ;#f400 + value. + exx + out (c),c ;#f680. + out (c),e ;#f6c0. + ;exx + + ENDIF + IFDEF PLY_AKG_HARDWARE_MSX + out (#a1),a ;Value. + ENDIF + IFDEF PLY_AKG_HARDWARE_SPECTRUM_OR_PENTAGON + ld b,d + out (c),a ;#bffd + value + + ENDIF + IFDEF PLY_CFG_UseRetrig ;CONFIG SPECIFIC + xor a + ld (PLY_AKG_Retrig + PLY_AKG_Offset1b),a + ENDIF ;PLY_CFG_UseRetrig +PLY_AKG_PSGReg13_End: + ENDIF ;PLY_CFG_UseHardwareSounds + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_SaveSP: ld sp,0 + ELSE + ld sp,(PLY_AKG_SaveSp) + ENDIF + + ret + + + + + + + + + + + + + + +;Channel1/2/3 sub-codes. Uses a macro to mutualize the code. +;----------------------------------------------------------- + + MACRO PLY_AKG_ChannelSubcodes channelNumber + +PLY_AKG_Channel{channelNumber}_MaybeEffects: + ;There is one wait in all cases. + ;xor a ;A is supposed to be 0. + ld (PLY_AKG_Channel{channelNumber}_WaitCounter + PLY_AKG_Offset1b),a + IFNDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC + jp PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer + ELSE + bit 6,c ;Effects? + jp z,PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer + ;Manage effects. + +;Reads the effects. +;IN: HL = Points on the effect blocks +;OUT: HL = Points after on the effect blocks +PLY_AKG_Channel{channelNumber}_ReadEffects: + ld iy,PLY_AKG_Channel{channelNumber}_SoundStream_RelativeModifierAddress + ld ix,PLY_AKG_Channel{channelNumber}_PlayInstrument_RelativeModifierAddress + ld de,PLY_AKG_Channel{channelNumber}_BeforeEnd_StoreCellPointer + ;Only adds a jump if this is not the last channel, as the code only need to jump below. + if {channelNumber} != 3 + jr PLY_AKG_Channel_ReadEffects + endif + ENDIF ;PLY_CFG_UseEffects +PLY_AKG_Channel{channelNumber}_ReadEffectsEnd: + ENDM ;PLY_AKG_ChannelSubcodes + + ;Generates the code thanks to the macro declare above. + PLY_AKG_ChannelSubcodes 1 + PLY_AKG_ChannelSubcodes 2 + PLY_AKG_ChannelSubcodes 3 + + ;** NO CODE between the code above and below! ** + + IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC +;IN: HL = Points on the effect blocks +; DE = Where to go to when over. +; IX = Address from which the data of the instrument are modified. +; IY = Address from which data of the channels (pitch, volume, etc) are modified. +;OUT: HL = Points after on the effect blocks +PLY_AKG_Channel_ReadEffects: + assert $ == PLY_AKG_Channel3_ReadEffectsEnd ;Makes sure this code is directly below the one above. + + IFNDEF PLY_AKG_Rom + ld (PLY_AKG_Channel_ReadEffects_EndJump + PLY_AKG_Offset1b),de + ELSE + ld (PLY_AKG_Channel_ReadEffects_EndJumpInstrAndAddress + 1),de + ENDIF + ;HL will be very useful, so we store the pointer in DE. + ex de,hl + + ;Reads the effect block. It may be an index or a relative address. + ld a,(de) + inc de + sla a + jr c,PLY_AKG_Channel_ReadEffects_RelativeAddress + ;Index. + exx + ld l,a + ld h,0 + + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel_ReadEffects_EffectBlocks1: ld de,0 + ELSE + ld de,(PLY_AKG_Channel_ReadEffects_EffectBlocks1) + ENDIF + add hl,de ;The index is already *2. + ld e,(hl) ;Gets the address referred by the table. + inc hl + ld d,(hl) +PLY_AKG_Channel_RE_EffectAddressKnown: + ;DE points on the current effect block header/data. + ld a,(de) ;Gets the effect number/more effect flag. + inc de + ld (PLY_AKG_Channel_RE_ReadNextEffectInBlock + PLY_AKG_Offset1b),a ;Stores the flag indicating whether there are more effects. + + ;Gets the effect number. + and %11111110 + ld l,a + ld h,0 + ld sp,PLY_AKG_EffectTable + add hl,sp ;Effect is already * 2. + ld sp,hl ;Jumps to the effect code. + ret + ;All the effects return here. +PLY_AKG_Channel_RE_EffectReturn: + ;Is there another effect? + IFNDEF PLY_AKG_Rom +PLY_AKG_Channel_RE_ReadNextEffectInBlock: ld a,0 ;Bit 0 indicates whether there are more effects. + ELSE + ld a,(PLY_AKG_Channel_RE_ReadNextEffectInBlock) + ENDIF + rra + jr c,PLY_AKG_Channel_RE_EffectAddressKnown + ;No more effects. + exx + + ;Put back in HL the point on the Track Cells. + ex de,hl + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel_ReadEffects_EndJump: jp 0 ;PLY_AKG_Channel1/2/3_BeforeEnd_StoreCellPointer + ELSE + jp PLY_AKG_Channel_ReadEffects_EndJumpInstrAndAddress + ENDIF + +PLY_AKG_Channel_ReadEffects_RelativeAddress: + srl a ;A was the relative MSB. Only 7 relevant bits. + exx + ld h,a + exx + ld a,(de) ;Reads the relative LSB. + inc de + exx + ld l,a + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Channel_ReadEffects_EffectBlocks2: ld de,0 + ELSE + ld de,(PLY_AKG_Channel_ReadEffects_EffectBlocks1) ;In ROM, reads the first value, it is the same. Duplicating it is only an optimization for RAM player to avoid reading memory. + ENDIF + add hl,de + jr PLY_AKG_Channel_RE_EffectAddressKnown + ENDIF ;PLY_CFG_UseEffects + + + + + +;--------------------------------- +;Codes that read InstrumentCells. +;IN: HL = pointer on the Instrument data cell to read. +; IX = can be modified. +; IYL = Instrument step (>=0). Useful for retrig. +; SP = normal use of the stack, do not pervert it! +; D = register 7, as if it was the channel 3 (so, bit 2 and 5 filled only). +; By default, the noise is OFF, the sound is ON, so no need to do anything if these values match. +; E = inverted volume. +; A = SET BELOW: first byte of the data, shifted of 3 bits to the right. +; B = SET BELOW: first byte of the data, unmodified. +; HL' = track pitch. +; DE' = 0 / note (instrument + Track transposition). +; BC' = temp, use at will. + +;OUT: HL = new pointer on the Instrument (may be on the empty sound). If not relevant, any value can be returned, it doesn't matter. +; IYL = Not 0 if retrig for this channel. +; D = register 7, updated, as if it was the channel 1 (so, bit 2 and 5 filled only). +; E = volume to encode (0-16). +; HL' = software period. If not relevant, do not set it. +; DE' = output period. + +PLY_AKG_BitForSound: equ 2 +PLY_AKG_BitForNoise: equ 5 + + +PLY_AKG_ReadInstrumentCell: + ld a,(hl) ;Gets the first byte of the cell. + inc hl + ld b,a ;Stores the first byte, handy in many cases. + + ;What type if the cell? + rra + jp c,PLY_AKG_S_Or_H_Or_SaH_Or_EndWithLoop + ;No Soft No Hard, or Soft To Hard, or Hard To Soft, or End without loop. + rra + IFNDEF PLY_AKG_Rom + jr c,PLY_AKG_StH_Or_EndWithoutLoop + ELSE + jp c,PLY_AKG_StH_Or_EndWithoutLoop + ENDIF + ;No Soft No Hard, or Hard to Soft. + rra + IFDEF PLY_CFG_HardToSoft ;CONFIG SPECIFIC + jr c,PLY_AKG_HardToSoft + ENDIF ;PLY_CFG_HardToSoft + + + + + + + ;------------------------------------------------- + ;"No soft, no hard". + ;------------------------------------------------- +PLY_AKG_NoSoftNoHard: + and %1111 ;Necessary, we don't know what crap is in the 4th bit of A. + sub e ;Decreases the volume, watching for overflow. + jr nc,$ + 3 + xor a + + ld e,a ;Sets the volume. + + IFDEF PLY_CFG_NoSoftNoHard_Noise ;CONFIG SPECIFIC + rl b ;Noise? + jr nc,PLY_AKG_NSNH_NoNoise + ;Noise. + ld a,(hl) + inc hl + ld (PLY_AKG_PSGReg6),a + set PLY_AKG_BitForSound,d ;Noise, no sound (both non-default values). + res PLY_AKG_BitForNoise,d + ret +PLY_AKG_NSNH_NoNoise: + ENDIF ;PLY_CFG_NoSoftNoHard_Noise + set PLY_AKG_BitForSound,d ;No noise (default), no sound. + ret + + + + + + + + ;------------------------------------------------- + ;"Soft only". + ;------------------------------------------------- + IFDEF PLY_CFG_SoftOnly ;CONFIG SPECIFIC +PLY_AKG_Soft: + ;Calculates the volume. + and %1111 ;Necessary, we don't know what crap is in the 4th bit of A. + + sub e ;Decreases the volume, watching for overflow. + jr nc,$ + 3 ;Checks for overflow. + xor a + + ld e,a ;Sets the volume. + ENDIF ;PLY_CFG_SoftOnly + IFDEF PLY_AKG_UseSoftOnlyOrHardOnly ;CONFIG SPECIFIC +PLY_AKG_SoftOnly_HardOnly_TestSimple_Common: ;This code is also used by "Hard only". + ;Simple sound? Gets the bit, let the subroutine do the job. + rl b + jr nc,PLY_AKG_S_NotSimple + ;Simple. + ld c,0 ;This will force the noise to 0. + jr PLY_AKG_S_AfterSimpleTest +PLY_AKG_S_NotSimple: + ;Not simple. Reads and keeps the next byte, containing the noise. WARNING, the following code must NOT modify the Carry! + ld b,(hl) + ld c,b + inc hl +PLY_AKG_S_AfterSimpleTest: + + call PLY_AKG_S_Or_H_CheckIfSimpleFirst_CalculatePeriod + + IFDEF PLY_AKG_UseSoftOnlyOrHardOnly_Noise ;CONFIG SPECIFIC + ;Noise? + ld a,c + and %11111 + ret z ;if noise not present, sound present, we can stop here, R7 is fine. + ;Noise is present. + ld (PLY_AKG_PSGReg6),a + res PLY_AKG_BitForNoise,d ;Noise present. + ENDIF ;PLY_AKG_UseSoftOnlyOrHardOnly_Noise + ret + ENDIF ;PLY_AKG_UseSoftOnlyOrHardOnly + + + + + + ;------------------------------------------------- + ;"Hard to soft". + ;------------------------------------------------- + IFDEF PLY_CFG_HardToSoft ;CONFIG SPECIFIC +PLY_AKG_HardToSoft: + call PLY_AKG_StoH_HToS_SandH_Common + ;We have the ratio jump calculated and the primary period too. It must be divided to get the software frequency. + + IFNDEF PLY_AKG_Rom + ld (PLY_AKG_HS_JumpRatio + 1),a + ELSE + ;Stores where to jump after the JumpRatio label. Only BC' is free... + exx + ld bc,PLY_AKG_HS_JumpRatio + add a,c + ld c,a + ld a,b + adc a,0 + ld b,a + ld (PLY_AKG_TempPlayInstrumentJumpInstrAndAddress + 1),bc ;The first byte has a jump. + exx + ENDIF + + ;Gets B, we need the bit to know if a software pitch shift is added. + IFDEF PLY_CFG_HardToSoft_SoftwarePitch ;CONFIG SPECIFIC + ld a,b + ENDIF ;PLY_CFG_HardToSoft_SoftwarePitch + exx + ;The hardware period can be stored. + ld (PLY_AKG_PSGHardwarePeriod_Instr + PLY_AKG_Offset1b),hl + IFNDEF PLY_AKG_Rom +PLY_AKG_HS_JumpRatio: jr $ + 2 ;Automodified by the line above to jump on the right code. + ELSE + jp PLY_AKG_TempPlayInstrumentJumpInstrAndAddress ;If ROM, jumps to the buffer, it will jump back just after according to the ratio. +PLY_AKG_HS_JumpRatio: + ENDIF + sla l + rl h + sla l + rl h + sla l + rl h + sla l + rl h + sla l + rl h + sla l + rl h + sla l + rl h + ;Any Software pitch shift? + IFDEF PLY_CFG_HardToSoft_SoftwarePitch ;CONFIG SPECIFIC + rla + jr nc,PLY_AKG_SH_NoSoftwarePitchShift +;Pitch shift. Reads it. + exx + ld a,(hl) + inc hl + exx + add a,l + ld l,a + exx + ld a,(hl) + inc hl + exx + adc a,h + ld h,a +PLY_AKG_SH_NoSoftwarePitchShift: + ENDIF ;PLY_CFG_HardToSoft_SoftwarePitch + exx + + ret + ENDIF ;PLY_CFG_HardToSoft + + + + ;------------------------------------------------- + ;End without loop. Put here to satisfy the JR range below. + ;------------------------------------------------- +PLY_AKG_EndWithoutLoop: + ;Loops to the "empty" instrument, and makes another iteration. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_EmptyInstrumentDataPt: ld hl,0 + ELSE + ld hl,(PLY_AKG_EmptyInstrumentDataPt) + ENDIF + ;No need to read the data, consider a void value. + inc hl + xor a + ld b,a + IFNDEF PLY_AKG_Rom + jr PLY_AKG_NoSoftNoHard + ELSE + jp PLY_AKG_NoSoftNoHard + ENDIF + + + + ;----------------------------------------- +PLY_AKG_StH_Or_EndWithoutLoop: + rra + IFNDEF PLY_CFG_SoftToHard ;CONFIG SPECIFIC + jr PLY_AKG_EndWithoutLoop + ELSE + jr c,PLY_AKG_EndWithoutLoop + + ;------------------------------------------------- + ;"Soft to Hard". + ;------------------------------------------------- + + call PLY_AKG_StoH_HToS_SandH_Common + ;We have the ratio jump calculated and the primary period too. It must be divided to get the hardware frequency. + + IFNDEF PLY_AKG_Rom + ld (PLY_AKG_SH_JumpRatio + 1),a + ELSE + ;Stores where to jump after the JumpRatio label. Only BC' is free... + exx + ld bc,PLY_AKG_SH_JumpRatio + add a,c + ld c,a + ld a,b + adc a,0 + ld b,a + ld (PLY_AKG_TempPlayInstrumentJumpInstrAndAddress + 1),bc ;The first byte has a jump. + exx + ENDIF + + ;Gets B, we need the bit to know if a hardware pitch shift is added. + IFDEF PLY_CFG_SoftToHard_HardwarePitch ;CONFIG SPECIFIC + ld a,b + ENDIF ;PLY_CFG_SoftToHard_HardwarePitch + exx + ;Saves the original frequency in DE. + ld e,l + ld d,h + IFNDEF PLY_AKG_Rom +PLY_AKG_SH_JumpRatio: jr $ + 2 ;Automodified by the line above to jump on the right code. + ELSE + jp PLY_AKG_TempPlayInstrumentJumpInstrAndAddress ;If ROM, jumps to the buffer, it will jump back just after according to the ratio. +PLY_AKG_SH_JumpRatio: + ENDIF + srl h + rr l + srl h + rr l + srl h + rr l + srl h + rr l + srl h + rr l + srl h + rr l + srl h + rr l + jr nc,PLY_AKG_SH_JumpRatioEnd + inc hl +PLY_AKG_SH_JumpRatioEnd: + IFDEF PLY_CFG_SoftToHard_HardwarePitch ;CONFIG SPECIFIC + ;Any Hardware pitch shift? + rla + jr nc,PLY_AKG_SH_NoHardwarePitchShift + ;Pitch shift. Reads it. + exx + ld a,(hl) + inc hl + exx + add a,l + ld l,a + exx + ld a,(hl) + inc hl + exx + adc a,h + ld h,a +PLY_AKG_SH_NoHardwarePitchShift: + ENDIF ;PLY_CFG_SoftToHard_HardwarePitch + ld (PLY_AKG_PSGHardwarePeriod_Instr + PLY_AKG_Offset1b),hl + + ;Put back the frequency in HL. + ex de,hl + exx + + ret + ENDIF ;PLY_CFG_SoftToHard + + + + +PLY_AKG_S_Or_H_Or_SaH_Or_EndWithLoop: + ;Second bit of the type. + rra + jr c,PLY_AKG_H_Or_EndWithLoop + ;Third bit of the type. + rra + IFDEF PLY_CFG_SoftOnly ;CONFIG SPECIFIC + jp nc,PLY_AKG_Soft + ENDIF ;PLY_CFG_SoftOnly + + IFDEF PLY_CFG_SoftAndHard ;CONFIG SPECIFIC + ;------------------------------------------------- + ;"Soft and Hard". + ;------------------------------------------------- + exx + push hl ;Saves the note and track pitch, because the first pass below will modify it, we need it for the second pass. + push de + exx + + call PLY_AKG_StoH_HToS_SandH_Common + ;We have now calculated the hardware frequency. Stores it. + exx + ld (PLY_AKG_PSGHardwarePeriod_Instr + PLY_AKG_Offset1b),hl + + pop de ;Get back the note and track pitch for the second pass. + pop hl + exx + + + ;Now calculate the software frequency. + rl b ;Simple sound? Used by the sub-code. + jp PLY_AKG_S_Or_H_CheckIfSimpleFirst_CalculatePeriod ;That's all! + ENDIF ;PLY_CFG_SoftAndHard + + + + + +PLY_AKG_H_Or_EndWithLoop: + IFDEF PLY_CFG_HardOnly ;CONFIG SPECIFIC + ;Third bit of the type. Only used for HardOnly, not in case of EndWithLoop. + rra + IFDEF PLY_CFG_UseInstrumentLoopTo ;CONFIG SPECIFIC + ;Ok to remove this jump if PLY_CFG_HardOnly variable absent, it will directly go to the code below. + jr c,PLY_AKG_EndWithLoop + ENDIF + + ;------------------------------------------------- + ;"Hard only". + ;------------------------------------------------- + + ld e,16 ;Sets the hardware volume. + + ;Retrig? + rra + IFDEF PLY_CFG_HardOnly_Retrig ;CONFIG SPECIFIC + jr nc,PLY_AKG_H_AfterRetrig + ld c,a + ;Retrig is only set if we are on the first step of the instrument! + ld a,iyl + or a + jr nz,PLY_AKG_H_RetrigEnd + ld a,e + ld (PLY_AKG_Retrig + PLY_AKG_Offset1b),a +PLY_AKG_H_RetrigEnd: + ld a,c +PLY_AKG_H_AfterRetrig: + ENDIF ;PLY_CFG_HardOnly_Retrig + + ;Calculates the hardware envelope. The value given is from 8-15, but encoded as 0-7. + and %111 + add a,8 + ld (PLY_AKG_PSGReg13_Instr + PLY_AKG_Offset1b),a + + ;Use the code of Soft Only to calculate the period and the noise. + call PLY_AKG_SoftOnly_HardOnly_TestSimple_Common + + ;The period is actually an hardware period. We don't care about the software period, the sound channel is cut. + exx + ld (PLY_AKG_PSGHardwarePeriod_Instr + PLY_AKG_Offset1b),hl + exx + + ;Stops the sound. + set PLY_AKG_BitForSound,d + + ret + ENDIF ;PLY_CFG_HardOnly + + ;** WARNING! ** Do not put instructions here between HardOnly and EndWithLoop, else conditional assembling will fail. + + ;------------------------------------------------- + ;End with loop. + ;------------------------------------------------- + IFDEF PLY_CFG_UseInstrumentLoopTo ;CONFIG SPECIFIC +PLY_AKG_EndWithLoop: + ;Loops to the encoded pointer, and makes another iteration. + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + jp PLY_AKG_ReadInstrumentCell + ENDIF ;PLY_CFG_UseInstrumentLoopTo + + + + + +;Common code for calculating the period, regardless of Soft or Hard. The same register constraints as the methods above apply. +;IN: HL = the next bytes to read. +; HL' = note + transposition. +; B = contains three bits: +; b7: forced period? (if yes, the two other bits are irrelevant) +; b6: arpeggio? +; b5: pitch? +; C = do not modify. +; Carry: Simple sound? +;OUT: B = shift three times to the left. +; C = unmodified. +; HL = advanced. +; HL' = calculated period. +PLY_AKG_S_Or_H_CheckIfSimpleFirst_CalculatePeriod: + + ;Simple sound? Checks the carry. + IFDEF PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs ;CONFIG SPECIFIC + jr nc,PLY_AKG_S_Or_H_NextByte + ENDIF ;PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs + ;No more bytes to read, the sound is "simple". The software period must still be calculated. + ;Calculates the note period from the note of the track. This is the same code as below. + exx + ex de,hl ;Now HL = track note + transp, DE is track pitch. + add hl,hl + ld bc,PLY_AKG_PeriodTable + add hl,bc + + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + add hl,de ;Adds the track pitch. + exx + ;Important: the bits must be shifted so that B is in the same state as if it were not a "simple" sound. + rl b + rl b + rl b + ;No need to modify R7. + ret + + IFDEF PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs ;CONFIG SPECIFIC +PLY_AKG_S_Or_H_NextByte: + ;Not simple. Reads the next bits to know if there is pitch/arp/forced software period. + ;Forced period? + rl b + IFDEF PLY_AKG_UseInstrumentForcedPeriods ;CONFIG SPECIFIC + jr c,PLY_AKG_S_Or_H_ForcedPeriod + ENDIF ;PLY_AKG_UseInstrumentForcedPeriods + ;No forced period. Arpeggio? + rl b + IFDEF PLY_AKG_UseInstrumentArpeggios ;CONFIG SPECIFIC + jr nc,PLY_AKG_S_Or_H_AfterArpeggio + ld a,(hl) + inc hl + exx + add a,e ;We don't care about overflow, no time for that. + ld e,a + exx +PLY_AKG_S_Or_H_AfterArpeggio: + ENDIF ;PLY_AKG_UseInstrumentArpeggios + ;Pitch? + rl b + IFDEF PLY_AKG_UseInstrumentPitchs ;CONFIG SPECIFIC + jr nc,PLY_AKG_S_Or_H_AfterPitch + ;Reads the pitch. Slow, but shouldn't happen so often. + ld a,(hl) + inc hl + exx + add a,l + ld l,a ;Adds the cell pitch to the track pitch, in two passes. + exx + ld a,(hl) + inc hl + exx + adc a,h + ld h,a + exx +PLY_AKG_S_Or_H_AfterPitch: + ENDIF ;PLY_AKG_UseInstrumentPitchs + + ;Calculates the note period from the note of the track. + exx + ex de,hl ;Now HL = track note + transp, DE is track pitch. + add hl,hl + ld bc,PLY_AKG_PeriodTable + add hl,bc + + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + add hl,de ;Adds the track pitch. + exx + + ret + ENDIF ;PLY_AKG_UseInstrumentForcedPeriodsOrArpeggiosOrPitchs + + + IFDEF PLY_AKG_UseInstrumentForcedPeriods ;CONFIG SPECIFIC +PLY_AKG_S_Or_H_ForcedPeriod: + ;Reads the period. A bit slow, but doesn't happen often. + ld a,(hl) + inc hl + exx + ld l,a + exx + ld a,(hl) + inc hl + exx + ld h,a + exx + + ;The pitch and arpeggios have been skipped, since the period is forced, the bits must be compensated. + rl b + rl b + ret + ENDIF ;PLY_AKG_UseInstrumentForcedPeriods + + ;------------------------------------------------------------------ +;Common code for SoftToHard and HardToSoft, and even Soft And Hard. The same register constraints as the methods above apply. +;OUT: HL' = frequency. +; A = shifted inverted ratio (xxx000), ready to be used in a JR to multiply/divide the frequency. +; B = bit states, shifted four times to the left (for StoH/HtoS, the msb will be "pitch shift?") (hardware for SoftTohard, software for HardToSoft). + IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC +PLY_AKG_StoH_HToS_SandH_Common: + ld e,16 ;Sets the hardware volume. + + ;Retrig? + rra + IFDEF PLY_AKG_UseRetrig_StoH_HtoS_SandH ;CONFIG SPECIFIC + jr nc,PLY_AKG_SHoHS_AfterRetrig + ld c,a + ;Retrig is only set if we are on the first step of the instrument! + ld a,iyl + or a + jr nz,PLY_AKG_SHoHS_RetrigEnd + dec a + ld (PLY_AKG_Retrig + PLY_AKG_Offset1b),a +PLY_AKG_SHoHS_RetrigEnd: + ld a,c +PLY_AKG_SHoHS_AfterRetrig: + ENDIF ;PLY_AKG_UseRetrig_StoH_HtoS_SandH + + ;Calculates the hardware envelope. The value given is from 8-15, but encoded as 0-7. + and %111 + add a,8 + ld (PLY_AKG_PSGReg13_Instr + PLY_AKG_Offset1b),a + + ;Noise? If yes, reads the next byte. + rl b + IFDEF PLY_AKG_UseNoise_StoH_HtoS_SandH ;CONFIG SPECIFIC + jr nc,PLY_AKG_SHoHS_AfterNoise + ;Noise is present. + ld a,(hl) + inc hl + ld (PLY_AKG_PSGReg6),a + res PLY_AKG_BitForNoise, d ;Noise present. +PLY_AKG_SHoHS_AfterNoise: + ENDIF ;PLY_AKG_UseNoise_StoH_HtoS_SandH + + ;Read the next data byte. + ld c,(hl) ;C = ratio, kept for later. + ld b,c + inc hl + + rl b ;Simple (no need to test the other bits)? The carry is transmitted to the called code below. + ;Call another common subcode. + call PLY_AKG_S_Or_H_CheckIfSimpleFirst_CalculatePeriod + ;Let's calculate the hardware frequency from it. + ld a,c ;Gets the ratio. + rla + rla + and %11100 + + ret + ENDIF ;PLY_CFG_UseHardwareSounds + + + + + + + + +; ----------------------------------------------------------------------------------- +; Effects management. +; ----------------------------------------------------------------------------------- + IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC +;All the effects code. +PLY_AKG_EffectTable: +dkps ;Disark macro. + IFDEF PLY_CFG_UseEffect_Reset ;CONFIG SPECIFIC + dw PLY_AKG_Effect_ResetFullVolume ;0 + dw PLY_AKG_Effect_Reset ;1 + ELSE + dw 0 + dw 0 + ENDIF ;PLY_CFG_UseEffect_Reset + + IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC + dw PLY_AKG_Effect_Volume ;2 + ELSE + dw 0 + ENDIF ;PLY_CFG_UseEffect_SetVolume + + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + dw PLY_AKG_Effect_ArpeggioTable ;3 + dw PLY_AKG_Effect_ArpeggioTableStop ;4 + ELSE + dw 0 + dw 0 + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + dw PLY_AKG_Effect_PitchTable ;5 + dw PLY_AKG_Effect_PitchTableStop ;6 + ELSE + dw 0 + dw 0 + ENDIF ;PLY_CFG_UseEffect_PitchTable + IFDEF PLY_AKG_UseEffect_VolumeSlide ;CONFIG SPECIFIC + dw PLY_AKG_Effect_VolumeSlide ;7 + dw PLY_AKG_Effect_VolumeSlideStop ;8 + ELSE + dw 0 + dw 0 + ENDIF ;PLY_AKG_UseEffect_VolumeSlide + + IFDEF PLY_CFG_UseEffect_PitchUp ;CONFIG SPECIFIC + dw PLY_AKG_Effect_PitchUp ;9 + ELSE + dw 0 + ENDIF ;PLY_CFG_UseEffect_PitchUp + IFDEF PLY_CFG_UseEffect_PitchDown ;CONFIG SPECIFIC + dw PLY_AKG_Effect_PitchDown ;10 + ELSE + dw 0 + ENDIF ;PLY_CFG_UseEffect_PitchDown + + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC + dw PLY_AKG_Effect_PitchStop ;11 + ELSE + dw 0 + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + + IFDEF PLY_CFG_UseEffect_PitchGlide ;CONFIG SPECIFIC + dw PLY_AKG_Effect_GlideWithNote ;12 + dw PLY_AKG_Effect_GlideSpeed ;13 + ELSE + dw 0 + dw 0 + ENDIF ;PLY_CFG_UseEffect_PitchGlide + + + IFDEF PLY_CFG_UseEffect_Legato ;CONFIG SPECIFIC + dw PLY_AKG_Effect_Legato ;14 + ELSE + dw 0 + ENDIF ;PLY_CFG_UseEffect_Legato + + IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed ;CONFIG SPECIFIC + dw PLY_AKG_Effect_ForceInstrumentSpeed ;15 + ELSE + dw 0 + ENDIF ;PLY_CFG_UseEffect_ForceInstrumentSpeed + + IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC + dw PLY_AKG_Effect_ForceArpeggioSpeed ;16 + ELSE + dw 0 + ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed + + IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC + dw PLY_AKG_Effect_ForcePitchSpeed ;17 + ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed + ;Last effect: no need to use padding with dw. +dkpe ;Disark macro. + +;Effects. +;---------------------------------------------------------------- +;For all effects: +;IN: DE' = Points on the data of this effect. +; IX = Address from which the data of the instrument are modified. +; IY = Address from which the data of the channels (pitch, volume, etc) are modified. +; HL = Must NOT be modified. +; WARNING, we are on auxiliary registers! + +; SP = Can be modified at will. + +;OUT: DE' = Points after on the data of this effect. +; WARNING, remains on auxiliary registers! +;---------------------------------------------------------------- + + IFDEF PLY_CFG_UseEffect_Reset ;CONFIG SPECIFIC +PLY_AKG_Effect_ResetFullVolume: + xor a ;The inverted volume is 0 (full volume). + jr PLY_AKG_Effect_ResetVolume_AfterReading + +PLY_AKG_Effect_Reset: + ld a,(de) ;Reads the inverted volume. + inc de +PLY_AKG_Effect_ResetVolume_AfterReading: + ld (iy + PLY_AKG_Channel1_InvertedVolumeInteger - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + + ;The current pitch is reset. + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC + xor a + ld (iy + PLY_AKG_Channel1_Pitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),a + ld (iy + PLY_AKG_Channel1_Pitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b + 1),a + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + + ld a,PLY_AKG_OPCODE_OR_A + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC + ld (iy + PLY_AKG_Channel1_IsPitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + ld (iy + PLY_AKG_Channel1_IsPitchTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ENDIF ;PLY_CFG_UseEffect_PitchTable + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ld (iy + PLY_AKG_Channel1_IsArpeggioTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ENDIF ;PLY_AKS_UseEffect_Arpeggio + IFDEF PLY_AKG_UseEffect_VolumeSlide ;CONFIG SPECIFIC + ld (iy + PLY_AKG_Channel1_IsVolumeSlide - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ENDIF ;PLY_AKG_UseEffect_VolumeSlide + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_Reset + + + + IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC +PLY_AKG_Effect_Volume: + ld a,(de) ;Reads the inverted volume. + inc de + + ld (iy + PLY_AKG_Channel1_InvertedVolumeInteger - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + + IFDEF PLY_AKG_UseEffect_VolumeSlide ;CONFIG SPECIFIC + ld (iy + PLY_AKG_Channel1_IsVolumeSlide - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_OR_A + ENDIF ;PLY_AKG_UseEffect_VolumeSlide + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_SetVolume + + + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC +PLY_AKG_Effect_ArpeggioTable: + ld a,(de) ;Reads the arpeggio table index. + inc de + + ;Finds the address of the Arpeggio. + ld l,a + ld h,0 + add hl,hl + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_ArpeggiosTable: ld bc,0 + ELSE + ld bc,(PLY_AKG_ArpeggiosTable) + ENDIF + add hl,bc + ld c,(hl) + inc hl + ld b,(hl) + inc hl + + ;Reads the speed. + ld a,(bc) + inc bc + ld (iy + PLY_AKG_Channel1_ArpeggioTableSpeed - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0),a + ld (iy + PLY_AKG_Channel1_ArpeggioBaseSpeed - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0),a + + ld (iy + PLY_AKG_Channel1_ArpeggioTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),c + ld (iy + PLY_AKG_Channel1_ArpeggioTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b + 1),b + ld (iy + PLY_AKG_Channel1_ArpeggioTableBase - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0),c + ld (iy + PLY_AKG_Channel1_ArpeggioTableBase - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 1),b + + ld (iy + PLY_AKG_Channel1_IsArpeggioTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_SCF + xor a + ld (iy + PLY_AKG_Channel1_ArpeggioTableCurrentStep - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),a + + jp PLY_AKG_Channel_RE_EffectReturn + +PLY_AKG_Effect_ArpeggioTableStop: + ld (iy + PLY_AKG_Channel1_IsArpeggioTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_OR_A + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_AKS_UseEffect_Arpeggio + + + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC +;Pitch table. Followed by the Pitch Table index. +PLY_AKG_Effect_PitchTable: + ld a,(de) ;Reads the Pitch table index. + inc de + + ;Finds the address of the Pitch. + ld l,a + ld h,0 + add hl,hl + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_PitchesTable: ld bc,0 + ELSE + ld bc,(PLY_AKG_PitchesTable) + ENDIF + add hl,bc + ld c,(hl) + inc hl + ld b,(hl) + inc hl + + ;Reads the speed. + ld a,(bc) + inc bc + ld (iy + PLY_AKG_Channel1_PitchTableSpeed - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ld (iy + PLY_AKG_Channel1_PitchBaseSpeed - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + + ld (iy + PLY_AKG_Channel1_PitchTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),c + ld (iy + PLY_AKG_Channel1_PitchTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b + 1),b + ld (iy + PLY_AKG_Channel1_PitchTableBase - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0),c + ld (iy + PLY_AKG_Channel1_PitchTableBase - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 1),b + + ld (iy + PLY_AKG_Channel1_IsPitchTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_SCF + + xor a + ld (iy + PLY_AKG_Channel1_PitchTableCurrentStep - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),a + + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_PitchTable + + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC +;Stops the pitch table. +PLY_AKG_Effect_PitchTableStop: + ;Only the pitch is stopped, but the value remains. + ld (iy + PLY_AKG_Channel1_IsPitchTable - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_OR_A + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_PitchTable + + IFDEF PLY_AKG_UseEffect_VolumeSlide ;CONFIG SPECIFIC +;Volume slide effect. Followed by the volume, as a word. +PLY_AKG_Effect_VolumeSlide: + ld a,(de) ;Reads the slide. + inc de + ld (iy + PLY_AKG_Channel1_VolumeSlideValue - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),a + ld a,(de) + inc de + ld (iy + PLY_AKG_Channel1_VolumeSlideValue - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b + 1),a + + ld (iy + PLY_AKG_Channel1_IsVolumeSlide - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_SCF + jp PLY_AKG_Channel_RE_EffectReturn + +;Volume slide stop effect. +PLY_AKG_Effect_VolumeSlideStop: + ;Only stops the slide, don't reset the value. + ld (iy + PLY_AKG_Channel1_IsVolumeSlide - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_OR_A + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_AKG_UseEffect_VolumeSlide + +;Pitch track effect. Followed by the pitch, as a word. + IFDEF PLY_CFG_UseEffect_PitchDown ;CONFIG SPECIFIC +PLY_AKG_Effect_PitchDown: + ;Changes the sign of the operations. + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_ADD_HL_BC_MSB + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 1), PLY_AKG_OPCODE_ADD_HL_BC_LSB + ld (iy + PLY_AKG_Channel1_PitchTrackDecimalInstr - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_ADD_A_IMMEDIATE + ld (iy + PLY_AKG_Channel1_PitchTrackIntegerAddOrSub - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_INC_HL + ENDIF ;PLY_CFG_UseEffect_PitchDown + IFDEF PLY_AKS_UseEffect_PitchUpOrDown ;CONFIG SPECIFIC +PLY_AKG_Effect_PitchUpDown_Common: ;The Pitch up will jump here. + ;Authorizes the pitch, disabled the glide. + ld (iy + PLY_AKG_Channel1_IsPitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_SCF + IFDEF PLY_CFG_UseEffect_PitchGlide ;CONFIG SPECIFIC + ld (iy + PLY_AKG_Channel1_GlideDirection - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),0 + ENDIF ;PLY_CFG_UseEffect_PitchGlide + + ld a,(de) ;Reads the Pitch. + inc de + ld (iy + PLY_AKG_Channel1_PitchTrackDecimalValue - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ld a,(de) + inc de + ld (iy + PLY_AKG_Channel1_PitchTrack - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),a + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDown + + IFDEF PLY_CFG_UseEffect_PitchUp ;CONFIG SPECIFIC +PLY_AKG_Effect_PitchUp: + ;Changes the sign of the operations. + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_SBC_HL_BC_MSB + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 1), PLY_AKG_OPCODE_SBC_HL_BC_LSB + ld (iy + PLY_AKG_Channel1_PitchTrackDecimalInstr - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_SUB_IMMEDIATE + ld (iy + PLY_AKG_Channel1_PitchTrackIntegerAddOrSub - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_DEC_HL + jr PLY_AKG_Effect_PitchUpDown_Common + ENDIF ;PLY_CFG_UseEffect_PitchUp + + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC +;Pitch track stop. Used by Pitch up/down/glide. +PLY_AKG_Effect_PitchStop: + ;Only stops the pitch, don't reset the value. No need to reset the Glide either. + ld (iy + PLY_AKG_Channel1_IsPitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),PLY_AKG_OPCODE_OR_A + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + + IFDEF PLY_CFG_UseEffect_PitchGlide ;CONFIG SPECIFIC +;Glide, with a note. +PLY_AKG_Effect_GlideWithNote: + ;Reads the note to reach. + ld a,(de) + inc de + ld (PLY_AKG_Effect_GlideWithNoteSaveDE + PLY_AKG_Offset1b),de ;Have to save, no more registers. Damn. + ;Finds the period related to the note, stores it. + add a,a ;The note is 7 bits only, so it fits. + ld l,a + ld h,0 + ld bc,PLY_AKG_PeriodTable + add hl,bc + + ld sp,hl + pop de ;DE = period to reach. + ld (iy + PLY_AKG_Channel1_GlideToReach - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),e + ld (iy + PLY_AKG_Channel1_GlideToReach - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b + 1),d + + ;Calculates the period of the current note to calculate the difference. + ld a,(ix + PLY_AKG_Channel1_TrackNote - PLY_AKG_Channel1_PlayInstrument_RelativeModifierAddress + PLY_AKG_Offset1b) + add a,a + ld l,a + ld h,0 + add hl,bc + + ld sp,hl + pop hl ;HL = current period. + ;Adds the current Track Pitch to have the current period, else the direction may be biased. + ld c,(iy + PLY_AKG_Channel1_Pitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b) + ld b,(iy + PLY_AKG_Channel1_Pitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b + 1) + add hl,bc + + ;What is the difference? + or a + sbc hl,de + IFNDEF PLY_AKG_Rom +dknr3 +PLY_AKG_Effect_GlideWithNoteSaveDE: ld de,0 ;Retrieves DE. This does not modified the Carry. + ELSE + ld de,(PLY_AKG_Effect_GlideWithNoteSaveDE) + ENDIF + jr c,PLY_AKG_Effect_Glide_PitchDown + ;Pitch up. + ld (iy + PLY_AKG_Channel1_GlideDirection - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),1 + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_SBC_HL_BC_MSB + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 1), PLY_AKG_OPCODE_SBC_HL_BC_LSB + ld (iy + PLY_AKG_Channel1_PitchTrackDecimalInstr - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_SUB_IMMEDIATE + ld (iy + PLY_AKG_Channel1_PitchTrackIntegerAddOrSub - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_DEC_HL + + ;Reads the Speed, which is actually the "pitch". +PLY_AKG_Effect_Glide_ReadSpeed: +PLY_AKG_Effect_GlideSpeed: ;This is an effect. + ld a,(de) + inc de + ld (iy + PLY_AKG_Channel1_PitchTrackDecimalValue - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a ;No offset, the value is directly targeted. + ld a,(de) + inc de + ld (iy + PLY_AKG_Channel1_PitchTrack - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),a + + ;Enables the pitch, as the Glide relies on it. The Glide is enabled below, via its direction. + ld a,PLY_AKG_OPCODE_SCF + ld (iy + PLY_AKG_Channel1_IsPitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + + jp PLY_AKG_Channel_RE_EffectReturn +PLY_AKG_Effect_Glide_PitchDown: + ;Pitch down. + ld (iy + PLY_AKG_Channel1_GlideDirection - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),2 + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_ADD_HL_BC_MSB + ld (iy + PLY_AKG_Channel1_PitchTrackAddOrSbc_16bits - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 1), PLY_AKG_OPCODE_ADD_HL_BC_LSB + ld (iy + PLY_AKG_Channel1_PitchTrackDecimalInstr - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_ADD_A_IMMEDIATE + ld (iy + PLY_AKG_Channel1_PitchTrackIntegerAddOrSub - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + 0), PLY_AKG_OPCODE_INC_HL + jr PLY_AKG_Effect_Glide_ReadSpeed + ENDIF ;PLY_CFG_UseEffect_PitchGlide + + + IFDEF PLY_CFG_UseEffect_Legato ;CONFIG SPECIFIC +;Legato. Followed by the note to play. +PLY_AKG_Effect_Legato: + ;Reads and sets the new note to play. + ld a,(de) + inc de + ld (ix + PLY_AKG_Channel1_TrackNote - PLY_AKG_Channel1_PlayInstrument_RelativeModifierAddress + PLY_AKG_Offset1b),a + + ;Stops the Pitch effect, resets the Pitch. + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide ;CONFIG SPECIFIC + ld a,PLY_AKG_OPCODE_OR_A + ld (iy + PLY_AKG_Channel1_IsPitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + xor a + ld (iy + PLY_AKG_Channel1_Pitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b),a + ld (iy + PLY_AKG_Channel1_Pitch - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress + PLY_AKG_Offset1b + 1),a + ENDIF ;PLY_AKS_UseEffect_PitchUpOrDownOrGlide + + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_Legato + + + IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed ;CONFIG SPECIFIC +;Forces the Instrument Speed. Followed by the speed. +PLY_AKG_Effect_ForceInstrumentSpeed: + ;Reads and sets the new speed. + ld a,(de) + inc de + ld (ix + PLY_AKG_Channel1_InstrumentSpeed - PLY_AKG_Channel1_PlayInstrument_RelativeModifierAddress + PLY_AKG_Offset1b),a + + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_ForceInstrumentSpeed + + + IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC +;Forces the Arpeggio Speed. Followed by the speed. +PLY_AKG_Effect_ForceArpeggioSpeed: + IFDEF PLY_AKS_UseEffect_Arpeggio ;CONFIG SPECIFIC + ;Is IT possible to use a Force Arpeggio even if there is no Arpeggio. Unlikely, but... + ;Reads and sets the new speed. + ld a,(de) + inc de + ld (iy + PLY_AKG_Channel1_ArpeggioTableSpeed - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ELSE + inc de + ENDIF ;PLY_AKS_UseEffect_Arpeggio + + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed + + IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC +;Forces the Pitch Speed. Followed by the speed. +PLY_AKG_Effect_ForcePitchSpeed: + IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC + ;Is IT possible to use a Force Arpeggio even if there is no Arpeggio. Unlikely, but... + ;Reads and sets the new speed. + ld a,(de) + inc de + ld (iy + PLY_AKG_Channel1_PitchTableSpeed - PLY_AKG_Channel1_SoundStream_RelativeModifierAddress),a + ELSE + inc de + ENDIF ;PLY_CFG_UseEffect_PitchTable + + jp PLY_AKG_Channel_RE_EffectReturn + ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed + + ENDIF ;PLY_CFG_UseEffects ;CONFIG SPECIFIC + + + + IFDEF PLY_CFG_UseEventTracks ;CONFIG SPECIFIC + IFNDEF PLY_AKG_Rom +dkbs ;Disark macro. +PLY_AKG_Event: db 0 ;Possible event sent from the music for the caller to interpret. +dkbe ;Disark macro. + ENDIF + ENDIF ;PLY_CFG_UseEventTracks + + + + +;The period table for each note (from 0 to 127 included). +PLY_AKG_PeriodTable: +dkws ;Disark macro. + IFDEF PLY_AKG_HARDWARE_CPC + ;PSG running to 1000000 Hz. + dw 3822,3608,3405,3214,3034,2863,2703,2551,2408,2273,2145,2025 ;0 + dw 1911,1804,1703,1607,1517,1432,1351,1276,1204,1136,1073,1012 ;12 + dw 956,902,851,804,758,716,676,638,602,568,536,506 ;24 + dw 478,451,426,402,379,358,338,319,301,284,268,253 ;36 + dw 239,225,213,201,190,179,169,159,150,142,134,127 ;48 + dw 119,113,106,100,95,89,84,80,75,71,67,63 ;60 + dw 60,56,53,50,47,45,42,40,38,36,34,32 ;72 + dw 30,28,27,25,24,22,21,20,19,18,17,16 ;84 + dw 15,14,13,13,12,11,11,10,9,9,8,8 ;96 + dw 7,7,7,6,6,6,5,5,5,4,4,4 ;108 + dw 4,4,3,3,3,3,3,2 ;,2,2,2,2 ;120 -> 127 + ENDIF + IFDEF PLY_AKG_HARDWARE_SPECTRUM_OR_MSX + ;PSG running to 1773400 Hz. + dw 6778, 6398, 6039, 5700, 5380, 5078, 4793, 4524, 4270, 4030, 3804, 3591 ; Octave 0 + dw 3389, 3199, 3019, 2850, 2690, 2539, 2397, 2262, 2135, 2015, 1902, 1795 ; Octave 1 + dw 1695, 1599, 1510, 1425, 1345, 1270, 1198, 1131, 1068, 1008, 951, 898 ; Octave 2 + dw 847, 800, 755, 712, 673, 635, 599, 566, 534, 504, 476, 449 ; Octave 3 + dw 424, 400, 377, 356, 336, 317, 300, 283, 267, 252, 238, 224 ; Octave 4 + dw 212, 200, 189, 178, 168, 159, 150, 141, 133, 126, 119, 112 ; Octave 5 + dw 106, 100, 94, 89, 84, 79, 75, 71, 67, 63, 59, 56 ; Octave 6 + dw 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28 ; Octave 7 + dw 26, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14 ; Octave 8 + dw 13, 12, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7 ; Octave 9 + dw 7, 6, 6, 6, 5, 5, 5, 4 ; Octave 10 + ENDIF + IFDEF PLY_AKG_HARDWARE_PENTAGON + ;PSG running to 1750000 Hz. + dw 6689, 6314, 5959, 5625, 5309, 5011, 4730, 4464, 4214, 3977, 3754, 3543 ; Octave 0 + dw 3344, 3157, 2980, 2812, 2655, 2506, 2365, 2232, 2107, 1989, 1877, 1772 ; Octave 1 + dw 1672, 1578, 1490, 1406, 1327, 1253, 1182, 1116, 1053, 994, 939, 886 ; Octave 2 + dw 836, 789, 745, 703, 664, 626, 591, 558, 527, 497, 469, 443 ; Octave 3 + dw 418, 395, 372, 352, 332, 313, 296, 279, 263, 249, 235, 221 ; Octave 4 + dw 209, 197, 186, 176, 166, 157, 148, 140, 132, 124, 117, 111 ; Octave 5 + dw 105, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59, 55 ; Octave 6 + dw 52, 49, 47, 44, 41, 39, 37, 35, 33, 31, 29, 28 ; Octave 7 + dw 26, 25, 23, 22, 21, 20, 18, 17, 16, 16, 15, 14 ; Octave 8 + dw 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7 ; Octave 9 + dw 7, 6, 6, 5, 5, 5, 5, 4 ; Octave 10 + ENDIF +dkwe ;Disark macro. +PLY_AKG_PeriodTable_End: + + + +;Buffer used for the ROM player. This part needs to be set to RAM. PLY_AKG_ROM_Buffer must be set. + IFDEF PLY_AKG_Rom + + PLY_AKG_BufferOffset = 0 + +;Generic data. + IFDEF PLY_CFG_UseRetrig +PLY_AKG_Event: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF +PLY_AKG_CurrentSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_BaseNoteIndex: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_PatternDecreasingHeight: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_TickDecreasingCounter: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + IFDEF PLY_CFG_UseSpeedTracks +PLY_AKG_SpeedTrack_WaitCounter: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_CFG_UseEventTracks +PLY_AKG_EventTrack_WaitCounter: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_CFG_UseHardwareSounds +PLY_AKG_PSGReg13_OldValue: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_PSGReg13_Instr: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + IFDEF PLY_CFG_UseRetrig +PLY_AKG_Retrig: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + ENDIF +PLY_AKG_Channel_RE_ReadNextEffectInBlock: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ;Words +PLY_AKG_ReadLinker_PtLinker: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + IFDEF PLY_CFG_UseSpeedTracks +PLY_AKG_SpeedTrack_PtTrack: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF + IFDEF PLY_CFG_UseEventTracks +PLY_AKG_EventTrack_PtTrack: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF + IFDEF PLY_AKS_UseEffect_Arpeggio +PLY_AKG_ArpeggiosTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF + IFDEF PLY_CFG_UseEffect_PitchTable +PLY_AKG_PitchesTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF +PLY_AKG_InstrumentsTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + IFDEF PLY_CFG_UseEffects + ;For ROM, only one is used, the second is the same, but it makes it faster on non-ROM as it avoid reading the memory. +PLY_AKG_Channel_ReadEffects_EffectBlocks1: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF +PLY_AKG_EmptyInstrumentDataPt: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_SaveSp: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_PSGReg01_Instr: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_PSGReg23_Instr: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_PSGReg45_Instr: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_PSGReg6_8_Instr: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_PSGReg6: equ PLY_AKG_PSGReg6_8_Instr + 0 +PLY_AKG_PSGReg8: equ PLY_AKG_PSGReg6_8_Instr + 1 +PLY_AKG_PSGReg9_10_Instr: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_PSGReg9: equ PLY_AKG_PSGReg9_10_Instr + 0 +PLY_AKG_PSGReg10: equ PLY_AKG_PSGReg9_10_Instr + 1 + IFDEF PLY_CFG_UseHardwareSounds +PLY_AKG_PSGHardwarePeriod_Instr: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF +PLY_AKG_Channel_ReadEffects_EndJumpInstrAndAddress: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 3 ;3 bytes. JP xxxx + IFDEF PLY_CFG_UseEffect_PitchGlide +PLY_AKG_Effect_GlideWithNoteSaveDE: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF +PLY_AKG_TempPlayInstrumentJumpInstrAndAddress: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 3 ;3 bytes. JP xxxx + + ;Section specific to each channel. + REPEAT 3, channelNumber +PLY_AKG_Channel{channelNumber}_SoundStream_RelativeModifierAddress: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset +PLY_AKG_Channel{channelNumber}_PlayInstrument_RelativeModifierAddress: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset + + ;Bytes + IFDEF PLY_CFG_UseTranspositions +PLY_AKG_Channel{channelNumber}_Transposition: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF +PLY_AKG_Channel{channelNumber}_WaitCounter: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + IFDEF PLY_AKG_UseEffect_VolumeSlide +PLY_AKG_Channel{channelNumber}_IsVolumeSlide: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_AKS_UseEffect_Arpeggio +PLY_AKG_Channel{channelNumber}_IsArpeggioTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_CFG_UseEffect_PitchTable +PLY_AKG_Channel{channelNumber}_IsPitchTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide +PLY_AKG_Channel{channelNumber}_IsPitch: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed +PLY_AKG_Channel{channelNumber}_InstrumentOriginalSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF +PLY_AKG_Channel{channelNumber}_InstrumentSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_InstrumentStep: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + IFDEF PLY_AKS_UseEffect_Arpeggio +PLY_AKG_Channel{channelNumber}_ArpeggioTableCurrentStep: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_GeneratedCurrentArpNote: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_ArpeggioBaseSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_ArpeggioTableSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_CFG_UseEffect_PitchTable +PLY_AKG_Channel{channelNumber}_PitchTableCurrentStep: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_PitchBaseSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_PitchTableSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide +PLY_AKG_Channel{channelNumber}_PitchTrackDecimal: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalCounter: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF +PLY_AKG_Channel{channelNumber}_TrackNote: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + IFDEF PLY_CFG_UseEffect_PitchGlide +PLY_AKG_Channel{channelNumber}_GlideDirection: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ENDIF +PLY_AKG_Channel{channelNumber}_GeneratedCurrentInvertedVolume: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + ;Words +PLY_AKG_Channel{channelNumber}_PtTrack: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + IFDEF PLY_AKS_UseEffect_Arpeggio +PLY_AKG_Channel{channelNumber}_ArpeggioTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_ArpeggioTableBase: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide +PLY_AKG_Channel{channelNumber}_PitchTrack: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF + IFDEF PLY_CFG_UseEffect_PitchTable +PLY_AKG_Channel{channelNumber}_PitchTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_PitchTableBase: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF +PLY_AKG_Channel{channelNumber}_EffectBlocks1: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_EffectBlocks2: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_InvertedVolumeIntegerAndDecimal: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_Pitch: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_PtInstrument: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_PtBaseInstrument: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_VolumeSlideValue: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + IFDEF PLY_CFG_UseEffect_PitchGlide +PLY_AKG_Channel{channelNumber}_GlideToReach: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_Glide_SaveHL: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ENDIF + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValue: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 ;Add/sub b, xx. +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstr: equ PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValue + 0 +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalValue: equ PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValue + 1 + ;The add/sub must be followed by the return JP. +PLY_AKG_Channel{channelNumber}_PitchTrackDecimalInstrAndValueReturnJp: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 3 ;JP xxxx. + ENDIF + +PLY_AKG_Channel{channelNumber}_GeneratedCurrentPitch: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + IFDEF PLY_AKS_UseEffect_PitchUpOrDownOrGlide +PLY_AKG_Channel{channelNumber}_PitchTrackAddOrSbc_16bits: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + ;3 bytes. +PLY_AKG_Channel{channelNumber}_PitchTrackAfterAddOrSbcJumpInstrAndAddress: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 3 ;3 bytes. JP xxxx. +PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAddOrSub: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_PitchTrackIntegerAfterAddOrSubJumpInstrAndAddress: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 3 ;3 bytes. JP xxxx. + ENDIF + REND + + ;The buffers for sound effects (if any), for each channel. They are treated apart, because they must be consecutive. + IFDEF PLY_AKG_MANAGE_SOUND_EFFECTS +PLY_AKG_PtSoundEffectTable: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 + REPEAT 3, channelNumber +PLY_AKG_Channel{channelNumber}_SoundEffectData: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 2 +PLY_AKG_Channel{channelNumber}_SoundEffectInvertedVolume: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_SoundEffectCurrentStep: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 +PLY_AKG_Channel{channelNumber}_SoundEffectSpeed: equ PLY_AKG_ROM_Buffer + PLY_AKG_BufferOffset : PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 1 + if channelNumber != 3 + PLY_AKG_BufferOffset = PLY_AKG_BufferOffset + 3 ;Padding of 3, but only necessary for channel 1 and 2. + endif + REND + ENDIF + + + + PLY_AKG_ROM_BufferSize = PLY_AKG_BufferOffset + + expectedBufferSize = 250 + IFNDEF PLY_AKG_MANAGE_SOUND_EFFECTS + assert PLY_AKG_BufferOffset <= expectedBufferSize ;Decreases when using the Player Configuration. + ELSE + assert PLY_AKG_BufferOffset <= (expectedBufferSize + 23) ;With sound effects, it takes a bit more memory. + ENDIF + + + ENDIF ;PLY_AKG_Rom + + +PLY_AKG_End: diff --git a/sound/PlayerAkg_SoundEffects.asm b/sound/PlayerAkg_SoundEffects.asm new file mode 100644 index 0000000..a95a001 --- /dev/null +++ b/sound/PlayerAkg_SoundEffects.asm @@ -0,0 +1,545 @@ +; Player of sound effects, for AKG (Generic) the player, format V2 (including speed). +; This file is meant to be included to the AKG player, do not use it stand-alone. +; If you want sound effects without music, there is a specific player for that. + + ;Is there a loaded Player Configuration source? If no, use a default configuration. + IFNDEF PLY_CFG_SFX_ConfigurationIsPresent + PLY_CFG_UseHardwareSounds = 1 + PLY_CFG_SFX_LoopTo = 1 + PLY_CFG_SFX_NoSoftNoHard = 1 + PLY_CFG_SFX_NoSoftNoHard_Noise = 1 + PLY_CFG_SFX_SoftOnly = 1 + PLY_CFG_SFX_SoftOnly_Noise = 1 + PLY_CFG_SFX_HardOnly = 1 + PLY_CFG_SFX_HardOnly_Noise = 1 + PLY_CFG_SFX_HardOnly_Retrig = 1 + PLY_CFG_SFX_SoftAndHard = 1 + PLY_CFG_SFX_SoftAndHard_Noise = 1 + PLY_CFG_SFX_SoftAndHard_Retrig = 1 + ENDIF + +; Agglomerates some Player Configuration flags. +; -------------------------------------------- +; Mixes the Hardware flags into one. + IFDEF PLY_CFG_SFX_HardOnly + PLY_AKG_SE_HardwareSounds = 1 + ENDIF + IFDEF PLY_CFG_SFX_SoftAndHard + PLY_AKG_SE_HardwareSounds = 1 + ENDIF +; Mixes the Hardware Noise flags into one. + IFDEF PLY_CFG_SFX_HardOnly_Noise + PLY_AKG_SE_HardwareNoise = 1 + ENDIF + IFDEF PLY_CFG_SFX_SoftAndHard_Noise + PLY_AKG_SE_HardwareNoise = 1 + ENDIF +; Mixes the Noise flags into one. + IFDEF PLY_AKG_SE_HardwareNoise + PLY_AKG_SE_Noise = 1 + ENDIF + IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise + PLY_AKG_SE_Noise = 1 + ENDIF + IFDEF PLY_CFG_SFX_SoftOnly + PLY_AKG_SE_Noise = 1 + ENDIF +; If Noise, the R6 code in the music player must be compiled. + IFDEF PLY_AKG_SE_Noise + PLY_AKG_Use_NoiseRegister = 1 + ENDIF + +; Mixes the Software Volume flags into one. + IFDEF PLY_CFG_SFX_NoSoftNoHard + PLY_AKG_SE_VolumeSoft = 1 + PLY_AKG_SE_VolumeSoftOrHard = 1 + ENDIF + IFDEF PLY_CFG_SFX_SoftOnly + PLY_AKG_SE_VolumeSoft = 1 + PLY_AKG_SE_VolumeSoftOrHard = 1 + ENDIF +; Mixes the volume (soft/hard) into one. + IFDEF PLY_CFG_UseHardwareSounds + PLY_AKG_SE_VolumeSoftOrHard = 1 + ENDIF +; Mixes the retrig flags into one. + IFDEF PLY_CFG_SFX_HardOnly_Retrig + PLY_AKG_SE_Retrig = 1 + ENDIF + IFDEF PLY_CFG_SFX_SoftAndHard_Retrig + PLY_AKG_SE_Retrig = 1 + ENDIF + + + ;Disark macro: Word region Start. + disarkCounter = 0 + IFNDEF dkws + MACRO dkws +PLY_AKG_DisarkWordRegionStart_{disarkCounter} + ENDM + ENDIF + ;Disark macro: Word region End. + IFNDEF dkwe + MACRO dkwe +PLY_AKG_DisarkWordRegionEnd_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + ;Disark macro: Pointer region Start. + disarkCounter = 0 + IFNDEF dkps + MACRO dkps +PLY_AKG_DisarkPointerRegionStart_{disarkCounter} + ENDM + ENDIF + ;Disark macro: Pointer region End. + IFNDEF dkpe + MACRO dkpe +PLY_AKG_DisarkPointerRegionEnd_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + ;Disark macro: Byte region Start. + disarkCounter = 0 + IFNDEF dkbs + MACRO dkbs +PLY_AKG_DisarkByteRegionStart_{disarkCounter} + ENDM + ENDIF + ;Disark macro: Byte region End. + IFNDEF dkbe + MACRO dkbe +PLY_AKG_DisarkByteRegionEnd_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + ;Disark macro: Force "No Reference Area" for 3 bytes (ld hl,xxxx). + IFNDEF dknr3 + MACRO dknr3 +PLY_AKG_DisarkForceNonReferenceDuring3_{disarkCounter}: + disarkCounter = disarkCounter + 1 + ENDM + ENDIF + + + +;Initializes the sound effects. It MUST be called at any times before a first sound effect is triggered. +;It doesn't matter whether the song is playing or not, or if it has been initialized or not. +;IN: HL = Address to the sound effects data. +PLY_AKG_InitSoundEffectsDisarkGenerateExternalLabel +PLY_AKG_InitSoundEffects: + ld (PLY_AKG_PtSoundEffectTable + PLY_AKG_Offset1b),hl + ret + + +;Plays a sound effect. If a previous one was already playing on the same channel, it is replaced. +;This does not actually plays the sound effect, but programs its playing. +;The music player, when called, will call the PLY_AKG_PlaySoundEffectsStream method below. +;IN: A = Sound effect number (>0!). +; C = The channel where to play the sound effect (0, 1, 2). +; B = Inverted volume (0 = full volume, 16 = no sound). Hardware sounds are also lowered. +PLY_AKG_PlaySoundEffectDisarkGenerateExternalLabel +PLY_AKG_PlaySoundEffect: + + ;Gets the address to the sound effect. + dec a ;The 0th is not encoded. + IFNDEF PLY_AKG_Rom +dknr3: +PLY_AKG_PtSoundEffectTable: ld hl,0 + ELSE + ld hl,(PLY_AKG_PtSoundEffectTable) + ENDIF + ld e,a + ld d,0 + add hl,de + add hl,de + ld e,(hl) + inc hl + ld d,(hl) + ;Reads the header of the sound effect to get the speed. + ld a,(de) + inc de + ex af,af' + + ld a,b + + ;Finds the pointer to the sound effect of the desired channel. + ld hl,PLY_AKG_Channel1_SoundEffectData + ld b,0 + sla c + sla c + sla c + add hl,bc + ld (hl),e + inc hl + ld (hl),d + inc hl + + ;Now stores the inverted volume. + ld (hl),a + inc hl + + ;Resets the current speed, stores the instrument speed. + ld (hl),0 + inc hl + ex af,af' + ld (hl),a + + ret + +;Stops a sound effect. Nothing happens if there was no sound effect. +;IN: A = The channel where to stop the sound effect (0, 1, 2). +PLY_AKG_StopSoundEffectFromChannelDisarkGenerateExternalLabel +PLY_AKG_StopSoundEffectFromChannel: + ;Puts 0 to the pointer of the sound effect. + add a,a + add a,a + add a,a + ld e,a + ld d,0 + ld hl,PLY_AKG_Channel1_SoundEffectData + add hl,de + ld (hl),d ;0 means "no sound". + inc hl + ld (hl),d + ret + +;Plays the sound effects, if any has been triggered by the user. +;This does not actually send registers to the PSG, it only overwrite the required values of the registers of the player. +;The sound effects initialization method must have been called before! +;As R7 is required, this must be called after the music has been played, but BEFORE the registers are sent to the PSG. +;IN: A = R7. +;OUT: A = new R7. +PLY_AKG_PlaySoundEffectsStream: + ;Shifts the R7 to the left twice, so that bit 2 and 5 only can be set for each track, below. + rla + rla + + ;Plays the sound effects on every channel. + ld ix,PLY_AKG_Channel1_SoundEffectData + ld iy,PLY_AKG_PSGReg8 + ld hl,PLY_AKG_PSGReg01_Instr + PLY_AKG_Offset1b + exx + ld c,a + ;Channel 1. + ;---------- + call PLY_AKG_PSES_Play + ld ix,PLY_AKG_Channel2_SoundEffectData + ld iy,PLY_AKG_PSGReg9 + exx + ld hl,PLY_AKG_PSGReg23_Instr + PLY_AKG_Offset1b + exx + ;Channel 2. + ;---------- + IFDEF PLY_AKG_HARDWARE_CPC + srl c ;Not RR, to make sure bit 6 is 0 (else, no more keyboard on CPC!). + ELSE + IFDEF PLY_AKG_HARDWARE_MSX + srl c ;R7 bit 6 on MSX must be 0. + ELSE + rr c ;On other platforms, we don't care. + ENDIF + ENDIF + + call PLY_AKG_PSES_Play + ld ix,PLY_AKG_Channel3_SoundEffectData + ld iy,PLY_AKG_PSGReg10 + exx + ld hl,PLY_AKG_PSGReg45_Instr + PLY_AKG_Offset1b + exx + ;Channel 3. + ;---------- + IFDEF PLY_AKG_HARDWARE_MSX + scf ;R7 bit 7 on MSX must be 1. + ENDIF + rr c + call PLY_AKG_PSES_Play + + ld a,c + ret + + +;Plays the sound stream from the given pointer to the sound effect. If 0, no sound is played. +;The given R7 is given shift twice to the left, so that this code MUST set/reset the bit 2 (sound), and maybe reset bit 5 (noise). +;This code MUST overwrite these bits because sound effects have priority over the music. +;IN: IX = Points on the sound effect pointer. If the sound effect pointer is 0, nothing must be played. +; IY = Points on the address where to store the volume for this channel. +; HL'= Points on the address where to store the software period for this channel. +; C = R7, shifted twice to the left. +;OUT: The pointed pointer by IX may be modified as the sound advances. +; C = R7, MUST be modified if there is a sound effect. +PLY_AKG_PSES_Play: + ;Reads the pointer pointed by IX. + ld l,(ix + 0) + ld h,(ix + 1) + ld a,l + or h + ret z ;No sound to be played? Returns immediately. + + ;Reads the first byte. What type of sound is it? +PLY_AKG_PSES_ReadFirstByte: + ld a,(hl) + inc hl + ld b,a + rra + jr c,PLY_AKG_PSES_SoftwareOrSoftwareAndHardware + rra + IFDEF PLY_CFG_SFX_HardOnly ;CONFIG SPECIFIC + jr c,PLY_AKG_PSES_HardwareOnly + ENDIF ;PLY_CFG_SFX_HardOnly + + ;No software, no hardware, or end/loop. + ;------------------------------------------- + ;End or loop? + rra + IFDEF PLY_CFG_SFX_NoSoftNoHard ;CONFIG SPECIFIC. If not present, the jump is not needed, the method is just below. + jr c,PLY_AKG_PSES_S_EndOrLoop + + ;No software, no hardware. + ;------------------------------------------- + ;Gets the volume. + call PLY_AKG_PSES_ManageVolumeFromA_Filter4Bits + + ;Noise? + IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise ;CONFIG SPECIFIC + rl b + call c,PLY_AKG_PSES_ReadNoiseAndOpenNoiseChannel + ENDIF ;PLY_CFG_SFX_NoSoftNoHard_Noise + + jr PLY_AKG_PSES_SavePointerAndExit + ENDIF ;PLY_CFG_SFX_NoSoftNoHard + + ;**Warning!** Do not put any instruction between EndOrLoop and NoSoftNoHard. + +PLY_AKG_PSES_S_EndOrLoop: + IFDEF PLY_CFG_SFX_LoopTo ;CONFIG SPECIFIC. If no "loop to", the sounds always end, no need to test. + ;Is it an end? + rra + jr c,PLY_AKG_PSES_S_Loop + ENDIF ;PLY_CFG_SFX_LoopTo + ;End of the sound. Marks the sound pointer with 0, meaning "no sound". + xor a + ld (ix + 0),a + ld (ix + 1),a + ret + IFDEF PLY_CFG_SFX_LoopTo ;CONFIG SPECIFIC. +PLY_AKG_PSES_S_Loop: + ;Loops. Reads the pointer and directly uses it. + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + jr PLY_AKG_PSES_ReadFirstByte + ENDIF ;PLY_CFG_SFX_LoopTo + + +;Saves HL into IX, and exits. This must be called at the end of each Cell. +;If the speed has not been reached, it is not saved. +PLY_AKG_PSES_SavePointerAndExit: + ;Speed reached? + ld a,(ix + PLY_AKG_SoundEffectData_OffsetCurrentStep) + cp (ix + PLY_AKG_SoundEffectData_OffsetSpeed) + jr c,PLY_AKG_PSES_NotReached + ;The speed has been reached, so resets it and saves the pointer to the next cell to read. + ld (ix + PLY_AKG_SoundEffectData_OffsetCurrentStep),0 + ld (ix + 0),l + ld (ix + 1),h + ret +PLY_AKG_PSES_NotReached: + ;Speed not reached. Increases it, that's all. The same cell will be read next time. + inc (ix + PLY_AKG_SoundEffectData_OffsetCurrentStep) + ret + + IFDEF PLY_CFG_SFX_HardOnly ;CONFIG SPECIFIC + ;Hardware only. + ;------------------------------------------- +PLY_AKG_PSES_HardwareOnly: + ;Calls the shared code that manages everything. + call PLY_AKG_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise + ;Cuts the sound. + set 2,c + + jr PLY_AKG_PSES_SavePointerAndExit + ENDIF ;PLY_CFG_SFX_HardOnly + + + +PLY_AKG_PSES_SoftwareOrSoftwareAndHardware: + ;Software only? + rra + IFDEF PLY_CFG_SFX_SoftAndHard ;CONFIG SPECIFIC + jr c,PLY_AKG_PSES_SoftwareAndHardware + ENDIF ;PLY_CFG_SFX_SoftAndHard + + ;Software. + ;------------------------------------------- + IFDEF PLY_CFG_SFX_SoftOnly ;CONFIG SPECIFIC + ;Volume. + call PLY_AKG_PSES_ManageVolumeFromA_Filter4Bits + + ;Noise? + rl b + IFDEF PLY_CFG_SFX_SoftOnly_Noise ;CONFIG SPECIFIC + call c,PLY_AKG_PSES_ReadNoiseAndOpenNoiseChannel + ENDIF ;PLY_CFG_SFX_SoftOnly_Noise + + ;Opens the "sound" channel. + res 2,c + + ;Reads the software period. + call PLY_AKG_PSES_ReadSoftwarePeriod + + jr PLY_AKG_PSES_SavePointerAndExit + ENDIF ;PLY_CFG_SFX_SoftOnly + + + ;Software and Hardware. + ;------------------------------------------- + IFDEF PLY_AKG_SE_HardwareSounds ;CONFIG SPECIFIC +PLY_AKG_PSES_SoftwareAndHardware: + ;Calls the shared code that manages everything. + call PLY_AKG_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise + + ;Reads the software period. + call PLY_AKG_PSES_ReadSoftwarePeriod + + ;Opens the sound. + res 2,c + + jr PLY_AKG_PSES_SavePointerAndExit + ENDIF ;PLY_AKG_SE_HardwareSounds + + + IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC + ;Shared code used by the "hardware only" and "software and hardware" part. + ;Reads the Retrig flag, the Hardware Envelope, the possible noise, the hardware period, + ;and sets the volume to 16. The R7 sound channel is NOT modified. +PLY_AKG_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise: + ;Retrig? + rra + IFDEF PLY_AKG_SE_Retrig ;CONFIG SPECIFIC + jr nc,PLY_AKG_PSES_H_AfterRetrig + ld d,a + ld a,255 + ld (PLY_AKG_PSGReg13_OldValue + PLY_AKG_Offset1b),a + ld a,d +PLY_AKG_PSES_H_AfterRetrig: + ENDIF ;PLY_AKG_SE_Retrig + + ;The hardware envelope can be set (8-15). + and %111 + add a,8 + ld (PLY_AKG_PSGReg13_Instr + PLY_AKG_Offset1b),a + + IFDEF PLY_AKG_SE_HardwareNoise ;CONFIG SPECIFIC. B not needed after, we can put it in the condition too. + ;Noise? + rl b + call c,PLY_AKG_PSES_ReadNoiseAndOpenNoiseChannel + ENDIF ;PLY_AKG_SE_HardwareNoise + + ;Reads the hardware period. + call PLY_AKG_PSES_ReadHardwarePeriod + + ;Sets the volume to "hardware". It still may be decreased. + ld a,16 + jp PLY_AKG_PSES_ManageVolumeFromA_Hard + ENDIF ;PLY_CFG_UseHardwareSounds + + + IFDEF PLY_AKG_SE_Noise +;Reads the noise pointed by HL, increases HL, and opens the noise channel. +PLY_AKG_PSES_ReadNoiseAndOpenNoiseChannel: + ;Reads the noise. + ld a,(hl) + ld (PLY_AKG_PSGReg6),a + inc hl + + ;Opens noise channel. + res 5,c + ret + ENDIF ;PLY_AKG_SE_Noise + + IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC +;Reads the hardware period from HL and sets the R11/R12 registers. HL is incremented of 2. +PLY_AKG_PSES_ReadHardwarePeriod: + ld a,(hl) + ld (PLY_AKG_PSGHardwarePeriod_Instr + PLY_AKG_Offset1b),a + inc hl + ld a,(hl) + ld (PLY_AKG_PSGHardwarePeriod_Instr + PLY_AKG_Offset1b + 1),a + inc hl + ret + ENDIF ;PLY_CFG_UseHardwareSounds + +;Reads the software period from HL and sets the period registers in HL'. HL is incremented of 2. +PLY_AKG_PSES_ReadSoftwarePeriod: + ld a,(hl) + inc hl + exx + ld (hl),a + inc hl + exx + ld a,(hl) + inc hl + exx + ld (hl),a + exx + ret + + IFDEF PLY_AKG_SE_VolumeSoft ;CONFIG SPECIFIC +;Reads the volume in A, decreases it from the inverted volume of the channel, and sets the volume via IY. +;IN: A = volume, from 0 to 15 (no hardware envelope). +PLY_AKG_PSES_ManageVolumeFromA_Filter4Bits: + and %1111 + ENDIF ;PLY_AKG_SE_VolumeSoft + IFDEF PLY_AKG_SE_VolumeSoftOrHard ;CONFIG SPECIFIC +;After the filtering. Useful for hardware sound (volume has been forced to 16). +PLY_AKG_PSES_ManageVolumeFromA_Hard: + ;Decreases the volume, checks the limit. + sub (ix + PLY_AKG_SoundEffectData_OffsetInvertedVolume) + jr nc,PLY_AKG_PSES_MVFA_NoOverflow + xor a +PLY_AKG_PSES_MVFA_NoOverflow: + ld (iy + 0),a + ret + ENDIF ;PLY_AKG_SE_VolumeSoftOrHard + + + ;The data for RAM player. For ROM player, it is declared in the main player. + IFNDEF PLY_AKG_Rom +;The data of the Channels MUST be consecutive. +PLY_AKG_Channel1_SoundEffectData: +dkws + dw 0 ;Points to the sound effect for the track 1, or 0 if not playing. +dkwe +dkbs +PLY_AKG_Channel1_SoundEffectInvertedVolume: + db 0 ;Inverted volume. +PLY_AKG_Channel1_SoundEffectCurrentStep: + db 0 ;Current step (>=0). +PLY_AKG_Channel1_SoundEffectSpeed: + db 0 ;Speed (>=0). + ds 3,0 ;Padding. +dkbe +PLY_AKG_Channel_SoundEffectDataSize: equ $ - PLY_AKG_Channel1_SoundEffectData + +dkbs +PLY_AKG_Channel2_SoundEffectData: + ds PLY_AKG_Channel_SoundEffectDataSize, 0 +PLY_AKG_Channel3_SoundEffectData: + ds PLY_AKG_Channel_SoundEffectDataSize, 0 +dkbe + + ;Checks that the pointers are consecutive. + assert (PLY_AKG_Channel1_SoundEffectData + PLY_AKG_Channel_SoundEffectDataSize) == PLY_AKG_Channel2_SoundEffectData + assert (PLY_AKG_Channel2_SoundEffectData + PLY_AKG_Channel_SoundEffectDataSize) == PLY_AKG_Channel3_SoundEffectData + + ENDIF ;PLY_AKG_Rom + +;Offset from the beginning of the data, to reach the inverted volume. +PLY_AKG_SoundEffectData_OffsetInvertedVolume: equ PLY_AKG_Channel1_SoundEffectInvertedVolume - PLY_AKG_Channel1_SoundEffectData +PLY_AKG_SoundEffectData_OffsetCurrentStep: equ PLY_AKG_Channel1_SoundEffectCurrentStep - PLY_AKG_Channel1_SoundEffectData +PLY_AKG_SoundEffectData_OffsetSpeed: equ PLY_AKG_Channel1_SoundEffectSpeed - PLY_AKG_Channel1_SoundEffectData diff --git a/sound/compile_at2_files.asm b/sound/compile_at2_files.asm new file mode 100644 index 0000000..fff5224 --- /dev/null +++ b/sound/compile_at2_files.asm @@ -0,0 +1,19 @@ +;Compiles the player, the music and sfxs, using RASM. +;No ORG needed. + + ;Songs. + include "song.asm" + include "song_playerconfig.asm" ;Optional. + + ;Sound effects. + ;include "SoundEffects.asm" + ;include "SoundEffects_playerconfig.asm" ;Optional. + + ;Hardware config. + PLY_AKG_HARDWARE_MSX = 1 + + ;Comment/delete this line if not using sound effects. + PLY_AKG_MANAGE_SOUND_EFFECTS = 1 + + ;This is the player. + include "PlayerAkg.asm" diff --git a/sound/rasm.exe b/sound/rasm.exe new file mode 100644 index 0000000..d083b2d Binary files /dev/null and b/sound/rasm.exe differ diff --git a/sound/song.asm b/sound/song.asm new file mode 100644 index 0000000..c4db5b5 --- /dev/null +++ b/sound/song.asm @@ -0,0 +1,2396 @@ +; Sarkboteur, AKG format, v1.0. + +; Generated by Arkos Tracker 2. + +castle_Start +castle_StartDisarkGenerateExternalLabel + +castle_DisarkByteRegionStart0 + db "AT20" +castle_DisarkPointerRegionStart1 + dw castle_ArpeggioTable ; The address of the Arpeggio table. + dw castle_PitchTable ; The address of the Pitch table. + dw castle_InstrumentTable ; The address of the Instrument table. + dw castle_EffectBlockTable ; The address of the Effect Block table. +castle_DisarkPointerRegionEnd1 + + +; The addresses of each Subsong: +castle_DisarkPointerRegionStart2 + dw castle_Subsong0_Start +castle_DisarkPointerRegionEnd2 + +; Declares all the Arpeggios. +castle_ArpeggioTable +castle_DisarkPointerRegionStart3 + dw castle_Arpeggio1 + dw castle_Arpeggio2 + dw castle_Arpeggio3 + dw castle_Arpeggio4 + dw castle_Arpeggio5 + dw castle_Arpeggio6 + dw castle_Arpeggio7 +castle_DisarkPointerRegionEnd3 + +castle_Arpeggio1 + db 1 ; The speed. + + db 0 + db 12 + db -128 +castle_DisarkWordForceReference4 + dw castle_Arpeggio1 + 0 + 1 ; Loop. + +castle_Arpeggio2 + db 1 ; The speed. + + db 12 + db 0 + db 12 + db 12 + db 0 + db 0 + db 0 + db 0 + db 0 + db 0 + db 12 + db -128 +castle_DisarkWordForceReference5 + dw castle_Arpeggio2 + 1 + 1 ; Loop. + +castle_Arpeggio3 + db 1 ; The speed. + + db 1 + db 0 + db 0 + db 0 + db 0 + db 0 + db 0 + db 0 + db -128 +castle_DisarkWordForceReference6 + dw castle_Arpeggio3 + 0 + 1 ; Loop. + +castle_Arpeggio4 + db 1 ; The speed. + + db 12 + db 0 + db 0 + db -128 +castle_DisarkWordForceReference7 + dw castle_Arpeggio4 + 0 + 1 ; Loop. + +castle_Arpeggio5 + db 1 ; The speed. + + db 0 + db 0 + db 0 + db 0 + db -5 + db -5 + db -5 + db -7 + db -7 + db -7 + db -11 + db -11 + db -11 + db -11 + db -7 + db -7 + db -7 + db -5 + db -5 + db -5 + db -128 +castle_DisarkWordForceReference8 + dw castle_Arpeggio5 + 0 + 1 ; Loop. + +castle_Arpeggio6 + db 1 ; The speed. + + db 0 + db 0 + db 0 + db 0 + db -4 + db -4 + db -4 + db -5 + db -5 + db -5 + db -11 + db -12 + db -12 + db -12 + db -5 + db -5 + db -5 + db -4 + db -4 + db -4 + db -128 +castle_DisarkWordForceReference9 + dw castle_Arpeggio6 + 0 + 1 ; Loop. + +castle_Arpeggio7 + db 1 ; The speed. + + db 0 + db 0 + db 0 + db 0 + db -2 + db -2 + db -2 + db -3 + db -3 + db -3 + db -6 + db -6 + db -6 + db -6 + db -3 + db -3 + db -3 + db -2 + db -2 + db -2 + db -128 +castle_DisarkWordForceReference10 + dw castle_Arpeggio7 + 0 + 1 ; Loop. + +; Declares all the Pitches. +castle_PitchTable +castle_DisarkPointerRegionStart11 + dw castle_Pitch1 + dw castle_Pitch2 + dw castle_Pitch3 +castle_DisarkPointerRegionEnd11 + +castle_Pitch1 + db 1 ; The speed. + + dw -2 +castle_DisarkWordForceReference12 + dw $ + 2 + dw 0 +castle_DisarkWordForceReference13 + dw $ + 2 + dw 0 +castle_DisarkWordForceReference14 + dw $ + 2 + dw -1 +castle_DisarkWordForceReference15 + dw $ + 2 + dw -2 +castle_DisarkWordForceReference16 + dw $ + 2 + dw -3 +castle_DisarkWordForceReference17 + dw $ + 2 + dw -2 +castle_DisarkWordForceReference18 + dw $ + 2 + dw -1 +castle_DisarkWordForceReference19 + dw castle_Pitch1 + 4 * 2 + 1 + +castle_Pitch2 + db 1 ; The speed. + + dw 0 +castle_DisarkWordForceReference20 + dw $ + 2 + dw 0 +castle_DisarkWordForceReference21 + dw $ + 2 + dw 0 +castle_DisarkWordForceReference22 + dw $ + 2 + dw 0 +castle_DisarkWordForceReference23 + dw $ + 2 + dw -1 +castle_DisarkWordForceReference24 + dw $ + 2 + dw -2 +castle_DisarkWordForceReference25 + dw $ + 2 + dw -2 +castle_DisarkWordForceReference26 + dw $ + 2 + dw -1 +castle_DisarkWordForceReference27 + dw castle_Pitch2 + 4 * 2 + 1 + +castle_Pitch3 + db 2 ; The speed. + + dw 0 +castle_DisarkWordForceReference28 + dw $ + 2 + dw -1 +castle_DisarkWordForceReference29 + dw $ + 2 + dw 0 +castle_DisarkWordForceReference30 + dw $ + 2 + dw 1 +castle_DisarkWordForceReference31 + dw castle_Pitch3 + 4 * 0 + 1 + +; Declares all the Instruments. +castle_InstrumentTable +castle_DisarkPointerRegionStart32 + dw castle_EmptyInstrument + dw castle_Instrument1 + dw castle_Instrument2 + dw castle_Instrument3 + dw castle_Instrument4 + dw castle_Instrument5 + dw castle_Instrument6 + dw castle_Instrument7 + dw castle_Instrument8 + dw castle_Instrument9 + dw castle_Instrument10 +castle_DisarkPointerRegionEnd32 + +castle_EmptyInstrument + db 0 ; The speed (>0, 0 for 256). +castle_EmptyInstrument_Loop db 0 ; No Soft no Hard. Volume: 0. Noise? false. + + db 6 ; Loop to silence. + +castle_Instrument1 + db 1 ; The speed (>0, 0 for 256). + db 105 ; Soft only. Volume: 13. + db 1 ; Additional data. Noise: 1. Pitch? false. Arp? false. Period? false. + + db 2 ; Soft to Hard. Envelope: 8. Retrig ? false. Noise ? false. + db 130 ; Simple case. Ratio: 5 + + db 2 ; Soft to Hard. Envelope: 8. Retrig ? false. Noise ? false. + db 130 ; Simple case. Ratio: 5 + + db 233 ; Soft only. Volume: 13. Volume only. + + db 225 ; Soft only. Volume: 12. Volume only. + + db 217 ; Soft only. Volume: 11. Volume only. + + db 209 ; Soft only. Volume: 10. Volume only. + + db 201 ; Soft only. Volume: 9. Volume only. + + db 193 ; Soft only. Volume: 8. Volume only. + + db 185 ; Soft only. Volume: 7. Volume only. + + db 177 ; Soft only. Volume: 6. Volume only. + + db 169 ; Soft only. Volume: 5. Volume only. + + db 161 ; Soft only. Volume: 4. Volume only. + + db 153 ; Soft only. Volume: 3. Volume only. + + db 145 ; Soft only. Volume: 2. Volume only. + + db 137 ; Soft only. Volume: 1. Volume only. + + db 6 ; Loop to silence. + +castle_Instrument2 + db 1 ; The speed (>0, 0 for 256). +castle_Instrument2_Loop db 113 ; Soft only. Volume: 14. + db 64 ; Additional data. Noise: 0. Pitch? false. Arp? true. Period? false. + db -12 ; Arpeggio. + + db 241 ; Soft only. Volume: 14. Volume only. + + db 241 ; Soft only. Volume: 14. Volume only. + + db 7 ; Loop. +castle_DisarkWordForceReference33 + dw castle_Instrument2_Loop ; Loop here. + +castle_Instrument3 + db 2 ; The speed (>0, 0 for 256). + db 233 ; Soft only. Volume: 13. Volume only. + + db 225 ; Soft only. Volume: 12. Volume only. + + db 217 ; Soft only. Volume: 11. Volume only. + + db 209 ; Soft only. Volume: 10. Volume only. + +castle_Instrument3_Loop db 201 ; Soft only. Volume: 9. Volume only. + + db 7 ; Loop. +castle_DisarkWordForceReference34 + dw castle_Instrument3_Loop ; Loop here. + +castle_Instrument4 + db 1 ; The speed (>0, 0 for 256). + db 97 ; Soft only. Volume: 12. + db 1 ; Additional data. Noise: 1. Pitch? false. Arp? false. Period? false. + + db 2 ; Soft to Hard. Envelope: 8. Retrig ? false. Noise ? false. + db 131 ; Simple case. Ratio: 4 + + db 2 ; Soft to Hard. Envelope: 8. Retrig ? false. Noise ? false. + db 131 ; Simple case. Ratio: 4 + + db 225 ; Soft only. Volume: 12. Volume only. + + db 217 ; Soft only. Volume: 11. Volume only. + + db 209 ; Soft only. Volume: 10. Volume only. + + db 201 ; Soft only. Volume: 9. Volume only. + + db 193 ; Soft only. Volume: 8. Volume only. + + db 185 ; Soft only. Volume: 7. Volume only. + + db 177 ; Soft only. Volume: 6. Volume only. + + db 169 ; Soft only. Volume: 5. Volume only. + + db 161 ; Soft only. Volume: 4. Volume only. + + db 153 ; Soft only. Volume: 3. Volume only. + + db 145 ; Soft only. Volume: 2. Volume only. + + db 137 ; Soft only. Volume: 1. Volume only. + + db 6 ; Loop to silence. + +castle_Instrument5 + db 1 ; The speed (>0, 0 for 256). + db 97 ; Soft only. Volume: 12. + db 1 ; Additional data. Noise: 1. Pitch? false. Arp? false. Period? false. + + db 2 ; Soft to Hard. Envelope: 8. Retrig ? false. Noise ? false. + db 132 ; Simple case. Ratio: 3 + + db 2 ; Soft to Hard. Envelope: 8. Retrig ? false. Noise ? false. + db 132 ; Simple case. Ratio: 3 + + db 225 ; Soft only. Volume: 12. Volume only. + + db 217 ; Soft only. Volume: 11. Volume only. + + db 209 ; Soft only. Volume: 10. Volume only. + + db 201 ; Soft only. Volume: 9. Volume only. + + db 193 ; Soft only. Volume: 8. Volume only. + + db 185 ; Soft only. Volume: 7. Volume only. + + db 177 ; Soft only. Volume: 6. Volume only. + + db 169 ; Soft only. Volume: 5. Volume only. + + db 161 ; Soft only. Volume: 4. Volume only. + + db 153 ; Soft only. Volume: 3. Volume only. + + db 145 ; Soft only. Volume: 2. Volume only. + + db 137 ; Soft only. Volume: 1. Volume only. + + db 6 ; Loop to silence. + +castle_Instrument6 + db 3 ; The speed (>0, 0 for 256). + db 233 ; Soft only. Volume: 13. Volume only. + + db 225 ; Soft only. Volume: 12. Volume only. + + db 217 ; Soft only. Volume: 11. Volume only. + + db 209 ; Soft only. Volume: 10. Volume only. + + db 201 ; Soft only. Volume: 9. Volume only. + + db 193 ; Soft only. Volume: 8. Volume only. + +castle_Instrument6_Loop db 185 ; Soft only. Volume: 7. Volume only. + + db 177 ; Soft only. Volume: 6. Volume only. + + db 7 ; Loop. +castle_DisarkWordForceReference35 + dw castle_Instrument6_Loop ; Loop here. + +castle_Instrument7 + db 1 ; The speed (>0, 0 for 256). +castle_Instrument7_Loop db 4 ; Hard to Soft. Envelope: 8. Retrig ? false. Noise ? false. + db 10 ; Complex case. Ratio: 5 + dw -4 ; Destination pitch shift. + + db 7 ; Loop. +castle_DisarkWordForceReference36 + dw castle_Instrument7_Loop ; Loop here. + +castle_Instrument8 + db 1 ; The speed (>0, 0 for 256). +castle_Instrument8_Loop db 233 ; Soft only. Volume: 13. Volume only. + + db 7 ; Loop. +castle_DisarkWordForceReference37 + dw castle_Instrument8_Loop ; Loop here. + +castle_Instrument9 + db 1 ; The speed (>0, 0 for 256). + db 233 ; Soft only. Volume: 13. Volume only. + + db 225 ; Soft only. Volume: 12. Volume only. + + db 217 ; Soft only. Volume: 11. Volume only. + + db 209 ; Soft only. Volume: 10. Volume only. + + db 201 ; Soft only. Volume: 9. Volume only. + + db 193 ; Soft only. Volume: 8. Volume only. + +castle_Instrument9_Loop db 185 ; Soft only. Volume: 7. Volume only. + + db 7 ; Loop. +castle_DisarkWordForceReference38 + dw castle_Instrument9_Loop ; Loop here. + +castle_Instrument10 + db 1 ; The speed (>0, 0 for 256). +castle_Instrument10_Loop db 105 ; Soft only. Volume: 13. + db 64 ; Additional data. Noise: 0. Pitch? false. Arp? true. Period? false. + db 12 ; Arpeggio. + + db 233 ; Soft only. Volume: 13. Volume only. + + db 7 ; Loop. +castle_DisarkWordForceReference39 + dw castle_Instrument10_Loop ; Loop here. + + +; The indexes of the effect blocks used by this song. +castle_EffectBlockTable +castle_DisarkPointerRegionStart40 + dw castle_EffectBlock_P11P1P6P1 ; Index 0 + dw castle_EffectBlock_P1P6P3 ; Index 1 + dw castle_EffectBlock_P14P16P0 ; Index 2 + dw castle_EffectBlock_P5P0P11P0P6P1 ; Index 3 + dw castle_EffectBlock_P5P0P11P1P6P1 ; Index 4 + dw castle_EffectBlock_P10P2 ; Index 5 + dw castle_EffectBlock_P5P1P13P6P5 ; Index 6 + dw castle_EffectBlock_P1P11P0P6P2 ; Index 7 + dw castle_EffectBlock_P11P1P6P0 ; Index 8 + dw castle_EffectBlock_P5P1P6P4 ; Index 9 + dw castle_EffectBlock_P5P1P6P6 ; Index 10 + dw castle_EffectBlock_P16 ; Index 11 + dw castle_EffectBlock_P20P0P5 ; Index 12 + dw castle_EffectBlock_P4P4 ; Index 13 +castle_DisarkPointerRegionEnd40 + +castle_EffectBlock_P16 + db 16 +castle_EffectBlock_P4P4 + db 4, 4 +castle_EffectBlock_P14P16P0 + db 14, 16, 0 +castle_EffectBlock_P11P1P6P0 + db 11, 1, 6, 0 +castle_EffectBlock_P11P1P6P1 + db 11, 1, 6, 1 +castle_EffectBlock_P5P1P13P6P5 + db 5, 1, 13, 6, 5 +castle_EffectBlock_P1P11P0P6P2 + db 1, 11, 0, 6, 2 +castle_EffectBlock_P5P1P6P4 + db 5, 1, 6, 4 +castle_EffectBlock_P5P1P6P6 + db 5, 1, 6, 6 +castle_EffectBlock_P5P0P11P0P6P1 + db 5, 0, 11, 0, 6, 1 +castle_EffectBlock_P20P0P5 + db 20, 0, 5 +castle_EffectBlock_P5P0P11P1P6P1 + db 5, 0, 11, 1, 6, 1 +castle_EffectBlock_P10P2 + db 10, 2 +castle_EffectBlock_P1P6P3 + db 1, 6, 3 + +castle_DisarkByteRegionEnd0 + +; Subsong 0 +; ---------------------- +castle_Subsong0_DisarkByteRegionStart0 +castle_Subsong0_Start + db 2 ; ReplayFrequency (0=12.5hz, 1=25, 2=50, 3=100, 4=150, 5=300). + db 1 ; Digichannel (0-2). + db 1 ; PSG count (>0). + db 0 ; Loop start index (>=0). + db 79 ; End index (>=0). + db 5 ; Initial speed (>=0). + db 21 ; Base note index (>=0). + +castle_Subsong0_Linker +castle_Subsong0_DisarkPointerRegionStart1 +; Position 0 +castle_Subsong0_Linker_Loop + dw castle_Subsong0_Track1 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track44 + dw castle_Subsong0_LinkerBlock0 + +; Position 1 + dw castle_Subsong0_Track1 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock0 + +; Position 2 + dw castle_Subsong0_Track1 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track2 + dw castle_Subsong0_LinkerBlock0 + +; Position 3 + dw castle_Subsong0_Track1 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock0 + +; Position 4 + dw castle_Subsong0_Track1 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track4 + dw castle_Subsong0_LinkerBlock0 + +; Position 5 + dw castle_Subsong0_Track6 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track5 + dw castle_Subsong0_LinkerBlock1 + +; Position 6 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track8 + dw castle_Subsong0_LinkerBlock2 + +; Position 7 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track9 + dw castle_Subsong0_LinkerBlock3 + +; Position 8 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track10 + dw castle_Subsong0_LinkerBlock4 + +; Position 9 + dw castle_Subsong0_Track12 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track11 + dw castle_Subsong0_LinkerBlock5 + +; Position 10 + dw castle_Subsong0_Track1 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track4 + dw castle_Subsong0_LinkerBlock0 + +; Position 11 + dw castle_Subsong0_Track6 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track5 + dw castle_Subsong0_LinkerBlock1 + +; Position 12 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track8 + dw castle_Subsong0_LinkerBlock2 + +; Position 13 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track9 + dw castle_Subsong0_LinkerBlock3 + +; Position 14 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track13 + dw castle_Subsong0_LinkerBlock6 + +; Position 15 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track14 + dw castle_Subsong0_LinkerBlock7 + +; Position 16 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock8 + +; Position 17 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock9 + +; Position 18 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock10 + +; Position 19 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock11 + +; Position 20 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock12 + +; Position 21 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock13 + +; Position 22 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock14 + +; Position 23 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock5 + +; Position 24 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track16 + dw castle_Subsong0_LinkerBlock8 + +; Position 25 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track17 + dw castle_Subsong0_LinkerBlock15 + +; Position 26 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track18 + dw castle_Subsong0_LinkerBlock8 + +; Position 27 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock16 + +; Position 28 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track16 + dw castle_Subsong0_LinkerBlock8 + +; Position 29 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track17 + dw castle_Subsong0_LinkerBlock15 + +; Position 30 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track19 + dw castle_Subsong0_LinkerBlock6 + +; Position 31 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track15 + dw castle_Subsong0_LinkerBlock17 + +; Position 32 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track22 + dw castle_Subsong0_LinkerBlock18 + +; Position 33 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track23 + dw castle_Subsong0_LinkerBlock19 + +; Position 34 + dw castle_Subsong0_Track24 + dw castle_Subsong0_Track25 + dw castle_Subsong0_Track26 + dw castle_Subsong0_LinkerBlock18 + +; Position 35 + dw castle_Subsong0_Track27 + dw castle_Subsong0_Track28 + dw castle_Subsong0_Track29 + dw castle_Subsong0_LinkerBlock18 + +; Position 36 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track30 + dw castle_Subsong0_LinkerBlock20 + +; Position 37 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track30 + dw castle_Subsong0_LinkerBlock21 + +; Position 38 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track31 + dw castle_Subsong0_LinkerBlock22 + +; Position 39 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track31 + dw castle_Subsong0_LinkerBlock23 + +; Position 40 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track32 + dw castle_Subsong0_LinkerBlock4 + +; Position 41 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track32 + dw castle_Subsong0_LinkerBlock24 + +; Position 42 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track33 + dw castle_Subsong0_LinkerBlock25 + +; Position 43 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track33 + dw castle_Subsong0_LinkerBlock5 + +; Position 44 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track30 + dw castle_Subsong0_LinkerBlock20 + +; Position 45 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track30 + dw castle_Subsong0_LinkerBlock21 + +; Position 46 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track31 + dw castle_Subsong0_LinkerBlock22 + +; Position 47 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track31 + dw castle_Subsong0_LinkerBlock23 + +; Position 48 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track32 + dw castle_Subsong0_LinkerBlock4 + +; Position 49 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track32 + dw castle_Subsong0_LinkerBlock24 + +; Position 50 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track33 + dw castle_Subsong0_LinkerBlock25 + +; Position 51 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track33 + dw castle_Subsong0_LinkerBlock5 + +; Position 52 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track34 + dw castle_Subsong0_LinkerBlock26 + +; Position 53 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track34 + dw castle_Subsong0_LinkerBlock27 + +; Position 54 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track35 + dw castle_Subsong0_LinkerBlock22 + +; Position 55 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track35 + dw castle_Subsong0_LinkerBlock23 + +; Position 56 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track37 + dw castle_Subsong0_LinkerBlock28 + +; Position 57 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock28 + +; Position 58 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track2 + dw castle_Subsong0_LinkerBlock29 + +; Position 59 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock30 + +; Position 60 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track37 + dw castle_Subsong0_LinkerBlock28 + +; Position 61 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock28 + +; Position 62 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track38 + dw castle_Subsong0_LinkerBlock31 + +; Position 63 + dw castle_Subsong0_Track20 + dw castle_Subsong0_Track21 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock31 + +; Position 64 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track36 + dw castle_Subsong0_LinkerBlock20 + +; Position 65 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track39 + dw castle_Subsong0_LinkerBlock21 + +; Position 66 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock20 + +; Position 67 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track2 + dw castle_Subsong0_LinkerBlock21 + +; Position 68 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track36 + dw castle_Subsong0_LinkerBlock32 + +; Position 69 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track39 + dw castle_Subsong0_LinkerBlock33 + +; Position 70 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock32 + +; Position 71 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track2 + dw castle_Subsong0_LinkerBlock34 + +; Position 72 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track36 + dw castle_Subsong0_LinkerBlock35 + +; Position 73 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track39 + dw castle_Subsong0_LinkerBlock36 + +; Position 74 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track0 + dw castle_Subsong0_LinkerBlock35 + +; Position 75 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track2 + dw castle_Subsong0_LinkerBlock37 + +; Position 76 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track40 + dw castle_Subsong0_LinkerBlock25 + +; Position 77 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track41 + dw castle_Subsong0_LinkerBlock5 + +; Position 78 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track42 + dw castle_Subsong0_LinkerBlock25 + +; Position 79 + dw castle_Subsong0_Track7 + dw castle_Subsong0_Track3 + dw castle_Subsong0_Track43 + dw castle_Subsong0_LinkerBlock5 + +castle_Subsong0_DisarkPointerRegionEnd1 + dw 0 ; Loop. +castle_Subsong0_DisarkWordForceReference2 + dw castle_Subsong0_Linker_Loop + +castle_Subsong0_LinkerBlock0 + db 32 ; Height. + db 0 ; Transposition 0. + db 0 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference3 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference4 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock1 + db 32 ; Height. + db 5 ; Transposition 0. + db 5 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference5 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference6 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock2 + db 16 ; Height. + db 0 ; Transposition 0. + db -4 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference7 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference8 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock3 + db 16 ; Height. + db -7 ; Transposition 0. + db 1 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference9 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference10 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock4 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference11 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference12 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock5 + db 16 ; Height. + db -1 ; Transposition 0. + db 7 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference13 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference14 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock6 + db 16 ; Height. + db -2 ; Transposition 0. + db 6 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference15 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference16 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock7 + db 16 ; Height. + db -9 ; Transposition 0. + db -1 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference17 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference18 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock8 + db 16 ; Height. + db -4 ; Transposition 0. + db 4 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference19 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference20 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock9 + db 16 ; Height. + db -4 ; Transposition 0. + db 4 ; Transposition 1. + db 5 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference21 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference22 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock10 + db 16 ; Height. + db -7 ; Transposition 0. + db 1 ; Transposition 1. + db 6 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference23 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference24 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock11 + db 16 ; Height. + db -7 ; Transposition 0. + db 1 ; Transposition 1. + db 6 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference25 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference26 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock12 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db 6 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference27 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference28 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock13 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db 11 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference29 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference30 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock14 + db 16 ; Height. + db -1 ; Transposition 0. + db 7 ; Transposition 1. + db 12 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference31 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference32 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock15 + db 16 ; Height. + db -5 ; Transposition 0. + db 3 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference33 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference34 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock16 + db 16 ; Height. + db -7 ; Transposition 0. + db 1 ; Transposition 1. + db 14 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference35 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference36 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock17 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db 15 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference37 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference38 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock18 + db 48 ; Height. + db 0 ; Transposition 0. + db 0 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference39 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference40 + dw castle_Subsong0_EventTrack2 ; EventTrack address. +castle_Subsong0_LinkerBlock19 + db 48 ; Height. + db 2 ; Transposition 0. + db 2 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference41 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference42 + dw castle_Subsong0_EventTrack2 ; EventTrack address. +castle_Subsong0_LinkerBlock20 + db 16 ; Height. + db -8 ; Transposition 0. + db 0 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference43 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference44 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock21 + db 16 ; Height. + db -8 ; Transposition 0. + db 0 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference45 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference46 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock22 + db 16 ; Height. + db -3 ; Transposition 0. + db 5 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference47 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference48 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock23 + db 16 ; Height. + db -3 ; Transposition 0. + db 5 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference49 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference50 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock24 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference51 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference52 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock25 + db 16 ; Height. + db -1 ; Transposition 0. + db 7 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference53 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference54 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock26 + db 16 ; Height. + db 0 ; Transposition 0. + db 8 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference55 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference56 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock27 + db 16 ; Height. + db 0 ; Transposition 0. + db 8 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference57 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference58 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock28 + db 16 ; Height. + db 8 ; Transposition 0. + db 8 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference59 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference60 + dw castle_Subsong0_EventTrack2 ; EventTrack address. +castle_Subsong0_LinkerBlock29 + db 16 ; Height. + db 7 ; Transposition 0. + db 7 ; Transposition 1. + db 3 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference61 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference62 + dw castle_Subsong0_EventTrack2 ; EventTrack address. +castle_Subsong0_LinkerBlock30 + db 16 ; Height. + db 7 ; Transposition 0. + db 7 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference63 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference64 + dw castle_Subsong0_EventTrack2 ; EventTrack address. +castle_Subsong0_LinkerBlock31 + db 16 ; Height. + db 16 ; Transposition 0. + db 16 ; Transposition 1. + db 0 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference65 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference66 + dw castle_Subsong0_EventTrack2 ; EventTrack address. +castle_Subsong0_LinkerBlock32 + db 16 ; Height. + db -3 ; Transposition 0. + db 5 ; Transposition 1. + db 5 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference67 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference68 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock33 + db 16 ; Height. + db -3 ; Transposition 0. + db 5 ; Transposition 1. + db 5 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference69 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference70 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock34 + db 16 ; Height. + db -3 ; Transposition 0. + db 5 ; Transposition 1. + db -2 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference71 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference72 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock35 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db 3 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference73 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference74 + dw castle_Subsong0_EventTrack0 ; EventTrack address. +castle_Subsong0_LinkerBlock36 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db 3 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference75 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference76 + dw castle_Subsong0_EventTrack1 ; EventTrack address. +castle_Subsong0_LinkerBlock37 + db 16 ; Height. + db -10 ; Transposition 0. + db -2 ; Transposition 1. + db -4 ; Transposition 2. +castle_Subsong0_DisarkWordForceReference77 + dw castle_Subsong0_SpeedTrack0 ; SpeedTrack address. +castle_Subsong0_DisarkWordForceReference78 + dw castle_Subsong0_EventTrack1 ; EventTrack address. + +castle_Subsong0_Track0 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track1 + db 132 + db 1 ; New Instrument (1). + db 16 + db 4 + db 60 ; Waits for 1 line. + + db 4 + db 16 + db 28 + db 4 + db 60 ; Waits for 1 line. + + db 16 + db 16 + db 60 ; Waits for 1 line. + + db 4 + db 60 ; Waits for 1 line. + + db 16 + db 28 + db 4 + db 16 + db 4 + db 60 ; Waits for 1 line. + + db 4 + db 16 + db 28 + db 4 + db 60 ; Waits for 1 line. + + db 16 + db 16 + db 60 ; Waits for 1 line. + + db 4 + db 60 ; Waits for 1 line. + + db 16 + db 28 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track2 + db 255 + db 80 ; Escape note (80). + db 10 ; New Instrument (10). + db 6 ; Index to an effect block. + db 124 ; No note, but effects. + db 2 ; Index to an effect block. + db 62 + 2 * 64 ; Optimized wait for 4 lines. + + db 124 ; No note, but effects. + db 11 ; Index to an effect block. + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track3 + db 208 + db 2 ; New Instrument (2). + db 13 ; Index to an effect block. + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track4 + db 223 + db 3 ; New Instrument (3). + db 3 ; Index to an effect block. + db 62 + 3 * 64 ; Optimized wait for 5 lines. + + db 30 + db 28 + db 31 + db 61, 10 ; Waits for 11 lines. + + db 28 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 30 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 31 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track5 + db 225 + db 3 ; New Instrument (3). + db 0 ; Index to an effect block. + db 62 + 3 * 64 ; Optimized wait for 5 lines. + + db 31 + db 30 + db 33 + db 61, 10 ; Waits for 11 lines. + + db 33 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 35 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 36 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track6 + db 132 + db 4 ; New Instrument (4). + db 16 + db 4 + db 60 ; Waits for 1 line. + + db 4 + db 16 + db 156 + db 5 ; New Instrument (5). + db 132 + db 4 ; New Instrument (4). + db 60 ; Waits for 1 line. + + db 16 + db 16 + db 60 ; Waits for 1 line. + + db 4 + db 60 ; Waits for 1 line. + + db 16 + db 156 + db 5 ; New Instrument (5). + db 132 + db 4 ; New Instrument (4). + db 16 + db 4 + db 60 ; Waits for 1 line. + + db 4 + db 16 + db 156 + db 5 ; New Instrument (5). + db 132 + db 4 ; New Instrument (4). + db 60 ; Waits for 1 line. + + db 16 + db 16 + db 60 ; Waits for 1 line. + + db 4 + db 60 ; Waits for 1 line. + + db 16 + db 156 + db 5 ; New Instrument (5). + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track7 + db 140 + db 4 ; New Instrument (4). + db 24 + db 12 + db 60 ; Waits for 1 line. + + db 12 + db 24 + db 36 + db 12 + db 60 ; Waits for 1 line. + + db 24 + db 24 + db 60 ; Waits for 1 line. + + db 12 + db 60 ; Waits for 1 line. + + db 24 + db 36 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track8 + db 228 + db 3 ; New Instrument (3). + db 0 ; Index to an effect block. + db 62 + 3 * 64 ; Optimized wait for 5 lines. + + db 31 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track9 + db 225 + db 3 ; New Instrument (3). + db 0 ; Index to an effect block. + db 61, 6 ; Waits for 7 lines. + + db 35 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 36 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track10 + db 230 + db 3 ; New Instrument (3). + db 0 ; Index to an effect block. + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 36 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 35 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 33 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track11 + db 227 + db 3 ; New Instrument (3). + db 0 ; Index to an effect block. + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 33 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 31 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 30 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track12 + db 140 + db 4 ; New Instrument (4). + db 24 + db 12 + db 60 ; Waits for 1 line. + + db 12 + db 24 + db 164 + db 5 ; New Instrument (5). + db 140 + db 4 ; New Instrument (4). + db 60 ; Waits for 1 line. + + db 24 + db 24 + db 60 ; Waits for 1 line. + + db 12 + db 60 ; Waits for 1 line. + + db 24 + db 164 + db 5 ; New Instrument (5). + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track13 + db 230 + db 3 ; New Instrument (3). + db 0 ; Index to an effect block. + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 36 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 34 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 33 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track14 + db 162 + db 3 ; New Instrument (3). + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 33 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 31 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 29 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track15 + db 155 + db 3 ; New Instrument (3). + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track16 + db 231 + db 3 ; New Instrument (3). + db 8 ; Index to an effect block. + db 60 ; Waits for 1 line. + + db 38 + db 36 + db 39 + db 62 + 3 * 64 ; Optimized wait for 5 lines. + + db 36 + db 38 + db 39 + db 60 ; Waits for 1 line. + + db 38 + db 36 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track17 + db 166 + db 3 ; New Instrument (3). + db 60 ; Waits for 1 line. + + db 36 + db 35 + db 38 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track18 + db 167 + db 3 ; New Instrument (3). + db 60 ; Waits for 1 line. + + db 38 + db 36 + db 39 + db 62 + 3 * 64 ; Optimized wait for 5 lines. + + db 36 + db 38 + db 39 + db 60 ; Waits for 1 line. + + db 38 + db 36 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track19 + db 166 + db 3 ; New Instrument (3). + db 60 ; Waits for 1 line. + + db 36 + db 34 + db 38 + db 62 + 3 * 64 ; Optimized wait for 5 lines. + + db 38 + db 60 ; Waits for 1 line. + + db 38 + db 39 + db 41 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track20 + db 247 + db 7 ; New Instrument (7). + db 5 ; Index to an effect block. + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track21 + db 191 + db 16 ; Escape note (16). + db 8 ; New Instrument (8). + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track22 + db 223 + db 6 ; New Instrument (6). + db 7 ; Index to an effect block. + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 30 + db 60 ; Waits for 1 line. + + db 26 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 29 + db 60 ; Waits for 1 line. + + db 28 + db 60 ; Waits for 1 line. + + db 24 + db 60 ; Waits for 1 line. + + db 27 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 26 + db 60 ; Waits for 1 line. + + db 22 + db 60 ; Waits for 1 line. + + db 26 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track23 + db 166 + db 6 ; New Instrument (6). + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 37 + db 60 ; Waits for 1 line. + + db 33 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 36 + db 60 ; Waits for 1 line. + + db 35 + db 60 ; Waits for 1 line. + + db 31 + db 60 ; Waits for 1 line. + + db 34 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 33 + db 60 ; Waits for 1 line. + + db 29 + db 60 ; Waits for 1 line. + + db 33 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track24 + db 242 + db 7 ; New Instrument (7). + db 5 ; Index to an effect block. + db 61, 6 ; Waits for 7 lines. + + db 52 + db 61, 6 ; Waits for 7 lines. + + db 53 + db 61, 6 ; Waits for 7 lines. + + db 54 + db 61, 6 ; Waits for 7 lines. + + db 55 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track25 + db 130 + db 8 ; New Instrument (8). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 13 ; Escape note (13). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 14 ; Escape note (14). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 15 ; Escape note (15). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 16 ; Escape note (16). + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track26 + db 171 + db 6 ; New Instrument (6). + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 42 + db 60 ; Waits for 1 line. + + db 38 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 41 + db 60 ; Waits for 1 line. + + db 40 + db 60 ; Waits for 1 line. + + db 36 + db 60 ; Waits for 1 line. + + db 39 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 38 + db 60 ; Waits for 1 line. + + db 34 + db 60 ; Waits for 1 line. + + db 38 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track27 + db 255 + db 78 ; Escape note (78). + db 7 ; New Instrument (7). + db 5 ; Index to an effect block. + db 61, 6 ; Waits for 7 lines. + + db 63 + db 80 ; Escape note (80). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 81 ; Escape note (81). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 82 ; Escape note (82). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 83 ; Escape note (83). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 84 ; Escape note (84). + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track28 + db 191 + db 18 ; Escape note (18). + db 8 ; New Instrument (8). + db 61, 6 ; Waits for 7 lines. + + db 63 + db 20 ; Escape note (20). + db 61, 6 ; Waits for 7 lines. + + db 0 + db 61, 6 ; Waits for 7 lines. + + db 1 + db 61, 6 ; Waits for 7 lines. + + db 2 + db 61, 6 ; Waits for 7 lines. + + db 3 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track29 + db 178 + db 6 ; New Instrument (6). + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 49 + db 60 ; Waits for 1 line. + + db 45 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 48 + db 60 ; Waits for 1 line. + + db 47 + db 60 ; Waits for 1 line. + + db 43 + db 60 ; Waits for 1 line. + + db 46 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 45 + db 60 ; Waits for 1 line. + + db 41 + db 60 ; Waits for 1 line. + + db 45 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track30 + db 239 + db 9 ; New Instrument (9). + db 1 ; Index to an effect block. + db 60 ; Waits for 1 line. + + db 43 + db 60 ; Waits for 1 line. + + db 40 + db 60 ; Waits for 1 line. + + db 43 + db 47 + db 62 + 0 * 64 ; Optimized wait for 2 lines. + + db 43 + db 40 + db 48 + db 60 ; Waits for 1 line. + + db 47 + db 43 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track31 + db 240 + db 9 ; New Instrument (9). + db 1 ; Index to an effect block. + db 60 ; Waits for 1 line. + + db 45 + db 60 ; Waits for 1 line. + + db 40 + db 60 ; Waits for 1 line. + + db 45 + db 48 + db 62 + 0 * 64 ; Optimized wait for 2 lines. + + db 45 + db 40 + db 50 + db 60 ; Waits for 1 line. + + db 48 + db 47 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track32 + db 237 + db 9 ; New Instrument (9). + db 1 ; Index to an effect block. + db 60 ; Waits for 1 line. + + db 43 + db 60 ; Waits for 1 line. + + db 42 + db 60 ; Waits for 1 line. + + db 43 + db 45 + db 62 + 0 * 64 ; Optimized wait for 2 lines. + + db 43 + db 42 + db 48 + db 60 ; Waits for 1 line. + + db 47 + db 43 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track33 + db 239 + db 9 ; New Instrument (9). + db 1 ; Index to an effect block. + db 60 ; Waits for 1 line. + + db 45 + db 60 ; Waits for 1 line. + + db 42 + db 60 ; Waits for 1 line. + + db 45 + db 47 + db 62 + 0 * 64 ; Optimized wait for 2 lines. + + db 45 + db 42 + db 48 + db 60 ; Waits for 1 line. + + db 47 + db 45 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track34 + db 240 + db 9 ; New Instrument (9). + db 1 ; Index to an effect block. + db 60 ; Waits for 1 line. + + db 47 + db 60 ; Waits for 1 line. + + db 43 + db 60 ; Waits for 1 line. + + db 46 + db 45 + db 62 + 0 * 64 ; Optimized wait for 2 lines. + + db 41 + db 60 ; Waits for 1 line. + + db 44 + db 60 ; Waits for 1 line. + + db 43 + db 40 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track35 + db 237 + db 9 ; New Instrument (9). + db 1 ; Index to an effect block. + db 60 ; Waits for 1 line. + + db 44 + db 60 ; Waits for 1 line. + + db 40 + db 60 ; Waits for 1 line. + + db 43 + db 42 + db 62 + 0 * 64 ; Optimized wait for 2 lines. + + db 38 + db 60 ; Waits for 1 line. + + db 41 + db 60 ; Waits for 1 line. + + db 40 + db 36 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track36 + db 232 + db 3 ; New Instrument (3). + db 4 ; Index to an effect block. + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 39 + db 60 ; Waits for 1 line. + + db 35 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 38 + db 60 ; Waits for 1 line. + + db 37 + db 60 ; Waits for 1 line. + + db 33 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track37 + db 255 + db 83 ; Escape note (83). + db 10 ; New Instrument (10). + db 9 ; Index to an effect block. + db 124 ; No note, but effects. + db 2 ; Index to an effect block. + db 62 + 2 * 64 ; Optimized wait for 4 lines. + + db 124 ; No note, but effects. + db 11 ; Index to an effect block. + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track38 + db 255 + db 78 ; Escape note (78). + db 10 ; New Instrument (10). + db 10 ; Index to an effect block. + db 124 ; No note, but effects. + db 2 ; Index to an effect block. + db 62 + 2 * 64 ; Optimized wait for 4 lines. + + db 124 ; No note, but effects. + db 11 ; Index to an effect block. + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track39 + db 164 + db 3 ; New Instrument (3). + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 35 + db 60 ; Waits for 1 line. + + db 31 + db 60 ; Waits for 1 line. + + db 35 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track40 + db 239 + db 6 ; New Instrument (6). + db 1 ; Index to an effect block. + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 45 + db 60 ; Waits for 1 line. + + db 42 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 48 + db 60 ; Waits for 1 line. + + db 47 + db 60 ; Waits for 1 line. + + db 43 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track41 + db 175 + db 6 ; New Instrument (6). + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 45 + db 60 ; Waits for 1 line. + + db 42 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 43 + db 60 ; Waits for 1 line. + + db 42 + db 60 ; Waits for 1 line. + + db 40 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track42 + db 175 + db 6 ; New Instrument (6). + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 45 + db 60 ; Waits for 1 line. + + db 42 + db 62 + 1 * 64 ; Optimized wait for 3 lines. + + db 45 + db 60 ; Waits for 1 line. + + db 43 + db 60 ; Waits for 1 line. + + db 40 + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track43 + db 170 + db 6 ; New Instrument (6). + db 61, 10 ; Waits for 11 lines. + + db 124 ; No note, but effects. + db 12 ; Index to an effect block. + db 61, 127 ; Waits for 128 lines. + + +castle_Subsong0_Track44 + db 128 + db 0 ; New Instrument (0). + db 61, 127 ; Waits for 128 lines. + + +; The speed tracks +castle_Subsong0_SpeedTrack0 + db 255 ; Wait for 128 lines. + +; The event tracks +castle_Subsong0_EventTrack0 + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 3 ; Wait for 2 lines. + db 8 ; Value: 4. + db 3 ; Wait for 2 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 1 ; Wait for 1 lines. + db 14 ; Value: 7. + db 1 ; Wait for 1 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 14 ; Value: 7. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 3 ; Wait for 2 lines. + db 8 ; Value: 4. + db 3 ; Wait for 2 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 10 ; Value: 5. + db 191 ; Wait for 96 lines. +castle_Subsong0_EventTrack1 + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 14 ; Value: 7. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 3 ; Wait for 2 lines. + db 8 ; Value: 4. + db 3 ; Wait for 2 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 10 ; Value: 5. + db 223 ; Wait for 112 lines. +castle_Subsong0_EventTrack2 + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 5 ; Wait for 3 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 14 ; Value: 7. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 5 ; Wait for 3 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 12 ; Value: 6. + db 1 ; Wait for 1 lines. + db 8 ; Value: 4. + db 1 ; Wait for 1 lines. + db 10 ; Value: 5. + db 165 ; Wait for 83 lines. + +castle_Subsong0_DisarkByteRegionEnd0 diff --git a/sound/song_playerconfig.asm b/sound/song_playerconfig.asm new file mode 100644 index 0000000..1490d91 --- /dev/null +++ b/sound/song_playerconfig.asm @@ -0,0 +1,30 @@ +; Configuration that can be included to Arkos Tracker 2 players. +; It indicates what parts of code are useful to the song/sound effects, to save both memory and CPU. +; The players may or may not take advantage of these flags, it is up to them. + +; You can either: +; - Include this to the source that also includes the player (BEFORE the player is included) (recommended solution). +; - Include this at the beginning of the player code. +; - Copy/paste this directly in the player. +; If you use one player but several songs, don't worry, these declarations will stack up. +; If no configuration is used, the player will use default values (full code used). + + PLY_CFG_ConfigurationIsPresent = 1 + PLY_CFG_UseEventTracks = 1 + PLY_CFG_UseTranspositions = 1 + PLY_CFG_UseHardwareSounds = 1 + PLY_CFG_UseEffects = 1 + PLY_CFG_UseInstrumentLoopTo = 1 + PLY_CFG_NoSoftNoHard = 1 + PLY_CFG_SoftOnly = 1 + PLY_CFG_SoftOnly_Noise = 1 + PLY_CFG_SoftOnly_SoftwareArpeggio = 1 + PLY_CFG_SoftToHard = 1 + PLY_CFG_HardToSoft = 1 + PLY_CFG_HardToSoft_SoftwarePitch = 1 + PLY_CFG_UseEffect_Reset = 1 + PLY_CFG_UseEffect_PitchDown = 1 + PLY_CFG_UseEffect_PitchTable = 1 + PLY_CFG_UseEffect_ArpeggioTable = 1 + PLY_CFG_UseEffect_SetVolume = 1 + PLY_CFG_UseEffect_VolumeOut = 1 diff --git a/src/audio/audio.asm b/src/audio/audio.asm new file mode 100644 index 0000000..d384283 --- /dev/null +++ b/src/audio/audio.asm @@ -0,0 +1,22 @@ +;; +;; sf_init_song function +;; input: nothing +;; output: nothing +;; +_sf_init_song:: + ld hl,#CASTLE_START + xor a + call PLY_AKG_INIT + ret + +;; +;; sf_play_song function +;; input: nothing +;; output: nothing +;; +_sf_play_song:: + ;Play one frame of the song. + call PLY_AKG_PLAY + ret + +.include "At2Files.asm" diff --git a/src/main.c b/src/main.c index a8a499b..8691216 100644 --- a/src/main.c +++ b/src/main.c @@ -249,7 +249,7 @@ void sf_wait(int cicles) { for (int i=0; i