Port Sonic 3's Sound Driver to Sonic 1
From Sonic Retro
(Original guide by Kramlat)
You probably saw a quick and dirty way to do this on SSRG, but lets do it the proper way, the way that Sonic Team would do it.
Contents
- 1 Hivebrain Disassembly users, start here
- 1.1 Overview
- 1.2 Macros
- 1.3 New changes to beginning of ROM
- 1.4 Preparing to use Sonic 3/K/3K sound system
- 1.5 Upgrading the Load Driver Routine
- 1.6 Upgrading the Playback Routines
- 1.7 Upgrading Pause / Resume routines
- 1.8 Replacing 68k driver with new Z80 driver
- 1.9 Enabling Sonic 2 / 3 Level Music Memory
- 1.10 Fixing the Sounds and Music
- 1.10.1 Fixing the sound commands
- 1.10.2 Fixing the Sound Effects
- 1.10.3 Fixing the Music
- 1.10.3.1 Fixing the Zones
- 1.10.3.2 Invincibility
- 1.10.3.3 1Up
- 1.10.3.4 Special Stage
- 1.10.3.5 Title Screen
- 1.10.3.6 Ending
- 1.10.3.7 Boss
- 1.10.3.8 Final Zone music following SBZ2
- 1.10.3.9 Outro
- 1.10.3.10 Game Over / Time Over
- 1.10.3.11 Continue Screen
- 1.10.3.12 Credits
- 1.10.3.13 Drowning
- 1.10.3.14 Chaos Emerald
- 1.10.4 Fixing the sound test (Sound Select) on the level select
- 1.11 Branch fixes
- 2 GitHub Disassembly users, start here
- 2.1 Overview
- 2.2 Constants
- 2.3 Macros
- 2.4 Final Setup before we begin
- 2.5 Preparing to use Sonic 3/K/3K sound system
- 2.6 Upgrading the Load Driver Routine
- 2.7 Upgrading the Playback Routines
- 2.8 Upgrading Pause / Resume routines
- 2.9 Replacing 68k driver with new Z80 driver
- 2.10 Fixing the sneaker monitor
- 2.11 Branch fixes
- 3 What comes next
- 4 notice to admins
- 5 Credits
Hivebrain Disassembly users, start here
This guide is made for the Macro Assembler AS. If your hack was built off the SNASM68K or ASM68K version of Hivebrain's disassembly, you have to port what you were working on to AS. Staying on either assemblers other than AS will not allow you to follow on with this guide.
Overview
First off, Sonic 3's sound driver has the V_Int reloader built into it so it is not needed. Some routines need replacement, sounds need fixing, and we need to replace the sounds and music.
Macros
Before going on with this guide, add these macros to sonic1.macrosetup.asm:
; tells the Z80 to stop, and waits for it to finish stopping (acquire bus)
stopZ80 macro
move.w #$100,(Z80_bus_request).l ; stop the Z80
loop: btst #0,(Z80_bus_request).l
bne.s loop ; loop until it says it's stopped
endm
; tells the Z80 to start again
startZ80 macro
move.w #0,(Z80_bus_request).l ; start the Z80
endm
Z80_RAM = $A00000 ; start of Z80 RAM
Z80_RAM_end = $A02000 ; end of non-reserved Z80 RAM
Z80_bus_request = $A11100
Z80_reset = $A11200
; ---------------------------------------------------------------------------
; Sound commands list.
phase $E1
mus__FirstCmd = * ; ID of the first sound command
mus_FadeOut ds.b 1 ; $E1 - fade out music
mus_Stop ds.b 1 ; $E2 - stop music and sound effects
mus_MutePSG ds.b 1 ; $E3 - mute all PSG channels
mus_StopSFX ds.b 1 ; $E4 - stop all sound effects
mus_FadeOut2 ds.b 1 ; $E5 - fade out music (duplicate)
Mus__EndCmd = * ; next ID after last sound command
mus_FA = $FA ; $FA - ???
mus_StopSEGA = $FE ; $FE - Stop SEGA sound
mus_SEGA = $FF ; $FF - Play SEGA sound
; ---------------------------------------------------------------------------
; Music ID's list. These do not affect the sound driver, be careful.
phase $01
Mus__First = * ; ID of the first music
mus_AIZ1 ds.b 1 ; $01
mus_AIZ2 ds.b 1 ; $02
mus_HCZ1 ds.b 1 ; $03
mus_HCZ2 ds.b 1 ; $04
mus_MGZ1 ds.b 1 ; $05
mus_MGZ2 ds.b 1 ; $06
mus_CNZ1 ds.b 1 ; $07
mus_CNZ2 ds.b 1 ; $08
mus_FBZ1 ds.b 1 ; $09
mus_FBZ2 ds.b 1 ; $0A
mus_ICZ1 ds.b 1 ; $0B
mus_ICZ2 ds.b 1 ; $0C
mus_LBZ1 ds.b 1 ; $0D
mus_LBZ2 ds.b 1 ; $0E
mus_MHZ1 ds.b 1 ; $0F
mus_MHZ2 ds.b 1 ; $10
mus_SOZ1 ds.b 1 ; $11
mus_SOZ2 ds.b 1 ; $12
mus_LRZ1 ds.b 1 ; $13
mus_HPZ ds.b 1 ; $14
mus_SSZ ds.b 1 ; $15
mus_DEZ1 ds.b 1 ; $16
mus_DEZ2 ds.b 1 ; $17
mus_MinibossK ds.b 1 ; $18
mus_EndBoss ds.b 1 ; $19
mus_DDZ ds.b 1 ; $1A
mus_MagneticOrbs ds.b 1 ; $1B
mus_SpecialStage ds.b 1 ; $1C
mus_SlotMachine ds.b 1 ; $1D
mus_Gumball ds.b 1 ; $1E
mus_Knuckles ds.b 1 ; $1F
mus_ALZ ds.b 1 ; $20
mus_BPZ ds.b 1 ; $21
mus_DPZ ds.b 1 ; $22
mus_CGZ ds.b 1 ; $23
mus_EMZ ds.b 1 ; $24
mus_TitleScreen ds.b 1 ; $25
mus_Credits3 ds.b 1 ; $26
mus_GameOver ds.b 1 ; $27
mus_Continue ds.b 1 ; $28
mus_GotThroughAct ds.b 1 ; $29
mus_ExtraLife ds.b 1 ; $2A
mus_Emerald ds.b 1 ; $2B
mus_Invincibility ds.b 1 ; $2C
mus_CompetitionMenu ds.b 1 ; $2D
mus_Miniboss ds.b 1 ; $2E
mus_DataSelect ds.b 1 ; $2F
mus_FinalBoss ds.b 1 ; $30
mus_Drowning ds.b 1 ; $31
mus_Ending ds.b 1 ; $32
Mus__End = * ; next ID after last music
; ---------------------------------------------------------------------------
; Sound effect ID's list. These do not affect the sound driver, be careful.
phase $33
sfx_First = * ; ID of the first sound effect
sfx_RingRight ds.b 1 ; $33
sfx_RingLeft ds.b 1 ; $34
sfx_Death ds.b 1 ; $35
sfx_Skid ds.b 1 ; $36
sfx_SpikeHit ds.b 1 ; $37
sfx_Bubble ds.b 1 ; $38
sfx_Splash ds.b 1 ; $39
sfx_Shield ds.b 1 ; $3A
sfx_Drown ds.b 1 ; $3B
sfx_Roll ds.b 1 ; $3C
sfx_Break ds.b 1 ; $3D
sfx_FireShield ds.b 1 ; $3E
sfx_BubbleShield ds.b 1 ; $3F
sfx_UnknownShield ds.b 1 ; $40
sfx_ElectricShield ds.b 1 ; $41
sfx_InstaAttack ds.b 1 ; $42
sfx_FireAttack ds.b 1 ; $43
sfx_BubbleAttack ds.b 1 ; $44
sfx_ElectricAttack ds.b 1 ; $45
sfx_SuperAlt ds.b 1 ; $46
sfx_SandwallRise ds.b 1 ; $47
sfx_Blast ds.b 1 ; $48
sfx_Thump ds.b 1 ; $49
sfx_Grab ds.b 1 ; $4A
sfx_WaterfallSplash ds.b 1 ; $4B
sfx_GlideLand ds.b 1 ; $4C
sfx_Projectile ds.b 1 ; $4D
sfx_MissileExplode ds.b 1 ; $4E
sfx_FlamethrowerQuiet ds.b 1 ; $4F
sfx_BossActivate ds.b 1 ; $50
sfx_MissileThrow ds.b 1 ; $51
sfx_SpikeMove ds.b 1 ; $52
sfx_Charging ds.b 1 ; $53
sfx_BossLazer ds.b 1 ; $54
sfx_BlockConveyor ds.b 1 ; $55
sfx_FlipBridge ds.b 1 ; $56
sfx_Geyser ds.b 1 ; $57
sfx_FanLatch ds.b 1 ; $58
sfx_Collapse ds.b 1 ; $59
sfx_UnknownCharge ds.b 1 ; $5A
sfx_Button ds.b 1 ; $5B
sfx_MetalSpark ds.b 1 ; $5C
sfx_FloorThump ds.b 1 ; $5D
sfx_Lazer ds.b 1 ; $5E
sfx_Crash ds.b 1 ; $5F
sfx_BossZoom ds.b 1 ; $60
sfx_BossHitFloor ds.b 1 ; $61
sfx_Jump ds.b 1 ; $62
sfx_Starpost ds.b 1 ; $63
sfx_PulleyGrab ds.b 1 ; $64
sfx_BlueSphere ds.b 1 ; $65
sfx_AllSpheres ds.b 1 ; $66
sfx_LevelProjectile ds.b 1 ; $67
sfx_Perfect ds.b 1 ; $68
sfx_PushBlock ds.b 1 ; $69
sfx_Goal ds.b 1 ; $6A
sfx_ActionBlock ds.b 1 ; $6B
sfx_Splash2 ds.b 1 ; $6C
sfx_UnknownShift ds.b 1 ; $6D
sfx_BossHit ds.b 1 ; $6E
sfx_Rumble2 ds.b 1 ; $6F
sfx_LavaBall ds.b 1 ; $70
sfx_Shield2 ds.b 1 ; $71
sfx_Hoverpad ds.b 1 ; $72
sfx_Transporter ds.b 1 ; $73
sfx_TunnelBooster ds.b 1 ; $74
sfx_BalloonPlatform ds.b 1 ; $75
sfx_TrapDoor ds.b 1 ; $76
sfx_Balloon ds.b 1 ; $77
sfx_GravityMachine ds.b 1 ; $78
sfx_Lightning ds.b 1 ; $79
sfx_BossMagma ds.b 1 ; $7A
sfx_SmallBumpers ds.b 1 ; $7B
sfx_ChainTension ds.b 1 ; $7C
sfx_UnknownPump ds.b 1 ; $7D
sfx_GroundSlide ds.b 1 ; $7E
sfx_FrostPuff ds.b 1 ; $7F
sfx_IceSpikes ds.b 1 ; $80
sfx_TubeLauncher ds.b 1 ; $81
sfx_SandSplash ds.b 1 ; $82
sfx_BridgeCollapse ds.b 1 ; $83
sfx_UnknownPowerUp ds.b 1 ; $84
sfx_UnknownPowerDown ds.b 1 ; $85
sfx_Alarm ds.b 1 ; $86
sfx_MushroomBounce ds.b 1 ; $87
sfx_PulleyMove ds.b 1 ; $88
sfx_WeatherMachine ds.b 1 ; $89
sfx_Bouncy ds.b 1 ; $8A
sfx_ChopTree ds.b 1 ; $8B
sfx_ChopStuck ds.b 1 ; $8C
sfx_UnknownFlutter ds.b 1 ; $8D
sfx_UnknownRevving ds.b 1 ; $8E
sfx_DoorOpen ds.b 1 ; $8F
sfx_DoorMove ds.b 1 ; $90
sfx_DoorClose ds.b 1 ; $91
sfx_GhostAppear ds.b 1 ; $92
sfx_BossRecovery ds.b 1 ; $93
sfx_ChainTick ds.b 1 ; $94
sfx_BossHand ds.b 1 ; $95
sfx_MetalLand ds.b 1 ; $96
sfx_EnemyBreath ds.b 1 ; $97
sfx_BossProjectile ds.b 1 ; $98
sfx_UnknownPlink ds.b 1 ; $99
sfx_SpringLatch ds.b 1 ; $9A
sfx_ThumpBoss ds.b 1 ; $9B
sfx_SuperEmerald ds.b 1 ; $9C
sfx_Targeting ds.b 1 ; $9D
sfx_Clank ds.b 1 ; $9E
sfx_SuperTransform ds.b 1 ; $9F
sfx_MissleShoot ds.b 1 ; $A0
sfx_UnknownOminous ds.b 1 ; $A1
sfx_FloorLauncher ds.b 1 ; $A2
sfx_GravityLift ds.b 1 ; $A3
sfx_MetalTransform ds.b 1 ; $A4
sfx_UnknownRise ds.b 1 ; $A5
sfx_LaunchGrab ds.b 1 ; $A6
sfx_LaunchReady ds.b 1 ; $A7
sfx_EnergyZap ds.b 1 ; $A8
sfx_AirDing ds.b 1 ; $A9
sfx_Bumper ds.b 1 ; $AA
sfx_Spindash ds.b 1 ; $AB
sfx_Continue ds.b 1 ; $AC
sfx_LaunchGo ds.b 1 ; $AD
sfx_Flipper ds.b 1 ; $AE
sfx_EnterSS ds.b 1 ; $AF
sfx_Register ds.b 1 ; $B0
sfx_Spring ds.b 1 ; $B1
sfx_Error ds.b 1 ; $B2
sfx_BigRing ds.b 1 ; $B3
sfx_Explode ds.b 1 ; $B4
sfx_Diamonds ds.b 1 ; $B5
sfx_Dash ds.b 1 ; $B6
sfx_SlotMachine ds.b 1 ; $B7
sfx_Signpost ds.b 1 ; $B8
sfx_RingLoss ds.b 1 ; $B9
sfx_Flying ds.b 1 ; $BA
sfx_FlyTired ds.b 1 ; $BB
sfx__FirstContinuous = * ; ID of the first continuous sound effect
sfx_SlideSkidLoud ds.b 1 ; $BC
sfx_LargeShip ds.b 1 ; $BD
sfx_EggmanSiren ds.b 1 ; $BE
sfx_BossRotate ds.b 1 ; $BF
sfx_FanBig ds.b 1 ; $C0
sfx_FanSmall ds.b 1 ; $C1
sfx_FlamethrowerLoud ds.b 1 ; $C2
sfx_GravityTunnel ds.b 1 ; $C3
sfx_BossPanic ds.b 1 ; $C4
sfx_UnknownSpin ds.b 1 ; $C5
sfx_WaveHover ds.b 1 ; $C6
sfx_CannonTurn ds.b 1 ; $C7
sfx_SlideSkidQuiet ds.b 1 ; $C8
sfx_SpikeBalls ds.b 1 ; $C9
sfx_LightTunnel ds.b 1 ; $CA
sfx_Rumble ds.b 1 ; $CB
sfx_BigRumble ds.b 1 ; $CC
sfx_DeathEggRiseLoud ds.b 1 ; $CD
sfx_WindQuiet ds.b 1 ; $CE
sfx_WindLoud ds.b 1 ; $CF
sfx_Rising ds.b 1 ; $D0
sfx_UnknownFlutter2 ds.b 1 ; $D1
sfx_GumballTab ds.b 1 ; $D2
sfx_DeathEggRiseQuiet ds.b 1 ; $D3
sfx_TurbineHum ds.b 1 ; $D4
sfx_LavaFall ds.b 1 ; $D5
sfx_UnknownZap ds.b 1 ; $D6
sfx_ConveyorPlatform ds.b 1 ; $D7
sfx_UnknownSaw ds.b 1 ; $D8
sfx_MagneticSpike ds.b 1 ; $D9
sfx_LeafBlower ds.b 1 ; $DA
sfx_WaterSkid ds.b 1 ; $DB
mus_CreditsK ds.b 1 ; $DC - Can also be treated as SFX?
ds.b 3 ; unused SFX slots, the driver will happily play them though
sfx__End = * ; next ID after the last sound effect
dephase
!org 0 ; make sure we reset the ROM position to 0
notZ80 function cpu,(cpu<>128)&&(cpu<>32988)
; make org safer (impossible to overwrite previously assembled bytes)
org macro address
if notZ80(MOMCPU)
.diff := address - *
if .diff < 0
error "too much stuff before org $\{address} ($\{(-.diff)} bytes)"
else
while .diff > 1024
; AS can only generate 1 kb of code on a single line
dc.b [1024]$FF
.diff := .diff - 1024
endm
dc.b [.diff]$FF
endif
else
if address < $
error "too much stuff before org 0\{address}h (0\{($-address)}h bytes)"
else
while address > $
db 0
endm
endif
endif
endm
; define an alternate org that fills the extra space with 0s instead of FFs
org0 macro address
.diff := address - *
if .diff < 0
error "too much stuff before org0 $\{address} ($\{(-.diff)} bytes)"
else
while .diff > 1024
; AS can only generate 1 kb of code on a single line
dc.b [1024]0
.diff := .diff - 1024
endm
dc.b [.diff]0
endif
endm
; define the cnop pseudo-instruction
cnop macro offset,alignment
if notZ80(MOMCPU)
org (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
else
org ($-1+(alignment)-(($-1+(-(offset)))#(alignment)))
endif
endm
; define an alternate cnop that fills the extra space with 0s instead of FFs
cnop0 macro offset,alignment
org0 (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
endm
; redefine align in terms of cnop, because the built-in align can be stupid sometimes
align macro alignment
cnop 0,alignment
endm
; define an alternate align that fills the extra space with 0s instead of FFs
align0 macro alignment
cnop0 0,alignment
endm
; define the even pseudo-instruction
even macro
align0 2
endm
; define a trace macro
; lets you easily check what address a location in this disassembly assembles to
trace macro optionalMessageWithoutQuotes
if MOMPASS=1
if ("ALLARGS"<>"")
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF} msg=ALLARGS"
else
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF}"
endif
tracenum := (tracenum+1)
endif
endm
tracenum := 0
; function to make a little-endian 16-bit pointer for the Z80 sound driver
z80_ptr function x,(x)<<8&$FF00|(x)>>8&$7F|$80
; macro to declare a little-endian 16-bit pointer for the Z80 sound driver
rom_ptr_z80 macro addr
dc.w z80_ptr(addr)
endm
below:
_tst macro
insn1op tst.ATTRIBUTE, ALLARGS
endm
this will all be needed for later, really.
now remove these lines:
; 128 = 80h = z80, 32988 = 80DCh = z80unDoC
notZ80 function cpu,(cpu<>128)&&(cpu<>32988)
; make org safer (impossible to overwrite previously assembled bytes) and count padding
; and also make it work in Z80 code without creating a new segment
org macro address
if notZ80(MOMCPU)
if address < *
error "too much stuff before org $\{address} ($\{(*-address)} bytes)"
elseif address > *
paddingSoFar set paddingSoFar + address - *
!org address
endif
else
if address < $
error "too much stuff before org 0\{address}h (0\{($-address)}h bytes)"
else
while address > $
db 0
endm
endif
endif
endm
; define the cnop pseudo-instruction
cnop macro offset,alignment
if notZ80(MOMCPU)
org (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
else
org ($-1+(alignment)-(($-1+(-(offset)))#(alignment)))
endif
endm
; redefine align in terms of cnop, for the padding counter
align macro alignment
cnop 0,alignment
endm
; define the even pseudo-instruction
even macro
if notZ80(MOMCPU)
if (*)&1
paddingSoFar set paddingSoFar+1
dc.b 0 ;ds.b 1
endif
else
if ($)&1
db 0
endif
endif
endm
; make ds work in Z80 code without creating a new segment
ds macro
if notZ80(MOMCPU)
!ds.ATTRIBUTE ALLARGS
else
rept ALLARGS
db 0
endm
endif
endm
if TRUE
; define a trace macro
; lets you easily check what address a location in this disassembly assembles to
; if used in Z80 code, the displayed PC will be relative to the start of Z80 RAM
trace macro optionalMessageWithoutQuotes
if MOMPASS=1
if notZ80(MOMCPU)
if ("ALLARGS"<>"")
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{*} msg=ALLARGS"
else
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{*}"
endif
else
if ("ALLARGS"<>"")
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=\{$}h msg=ALLARGS"
else
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=\{$}h"
endif
endif
tracenum := (tracenum+1)
endif
endm
else
trace macro
endm
endif
tracenum := 0
from above:
if zeroOffsetOptimization=0
; disable a space optimization in AS so we can build a bit-perfect ROM
; (the hard way, but it requires no modification of AS itself)
New changes to beginning of ROM
Okay, we are now using a compressed driver. Traditionally we used an uncompressed sonic3 driver, but not anymore, we will use a modified sonic & knuckles driver now. We will place:
Size_of_Snd_driver_guess = $E0C
Size_of_Snd_driver2_guess = $70F
; Approximate size of compressed sound driver. Change when appropriate
Size_of_Snd_Bank1 = $3EFC
; This particular bank has its contents aligned to the end
; ---------------------------------------------------------------------------
; not in use until the banks are disassembled
; DAC bank pointers
zPtrDAC: macro addr
dc.w ((((addr&$7FFF)+$8000)<<8)&$FF00)+(((addr&$7FFF)+$8000)>>8)
endm
; pointers reserved for the driver
zPtrSpec: macro addr
dc.w (((addr&$1FFF)>>$08)|((addr&$1FFF)<<$08))&$FFFF
endm
; little endian pointers for music and SFX
z68kPtr: macro addr
dc.w ((((addr&$FFFF)|$8000)>>$08)|(((addr&$FFFF)|$8000)<<$08))&$FFFF
endm
below:
include "sonic1.macrosetup.asm"
Preparing to use Sonic 3/K/3K sound system
The Vertical Interrupt and Horizontal Interrupt need to be fixed, Since the Sonic 1/2 system uses them but the Sonic 3/K/3K system doesn't.
Prepare Vertical Interrupt Handler for new driver
Sonic 1 calls a early 68k smps driver in both the vertical blank routine and the horizontal blank routine, we don need this for the new driver, so we will have to make a few deletions here and there.
Sonic 2 also does this with it's compressed z80 driver, so if you are actually placing this in sonic 2, you might want to make a note of that.first we will fix the vertical interrupt, by deleting this line:
jsr (sub_71B4C).l
under:
loc_B5E: ; XREF: loc_B88
Prepare Horzontal Interrupt Handler for new driver
Now we locate:
loc_119E: ; XREF: PalToCRAM
clr.b ($FFFFF64F).w
movem.l d0-a6,-(sp)
bsr.w Demo_Time
jsr (sub_71B4C).l
movem.l (sp)+,d0-a6
rte
; End of function PalToCRAM
See another familiar line? Yes, you've got it, do the same thing to it:
loc_119E: ; XREF: PalToCRAM
clr.b ($FFFFF64F).w
movem.l d0-a6,-(sp)
bsr.w Demo_Time
movem.l (sp)+,d0-a6
rte
; End of function PalToCRAM
That effectively disables the Sonic 1/2 style interrupt handler which we will not need and prevents a nasty error down the road.
Upgrading the Load Driver Routine
Okay, we disabled the driver's interrupt handler, wouldn't we want now to use the sonic3 driver instead? Here is where we start installing it. Locate:
; ---------------------------------------------------------------------------
; Subroutine to load the sound driver
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
nop
move.w #$100,($A11100).l ; stop the Z80
move.w #$100,($A11200).l ; reset the Z80
lea (Kos_Z80).l,a0 ; load sound driver
lea ($A00000).l,a1
bsr.w KosDec ; decompress
move.w #0,($A11200).l
nop
nop
nop
nop
move.w #$100,($A11200).l ; reset the Z80
move.w #0,($A11100).l ; start the Z80
rts
; End of function SoundDriverLoad
and replace it all with:
; ---------------------------------------------------------------------------
; Subroutine to load the sound driver
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
nop
move.w #$100,(Z80_bus_request).l
move.w #$100,(Z80_reset).l ; release Z80 reset
; Load SMPS sound driver
lea (Z80_SoundDriver).l,a0
lea (z80_ram).l,a1
bsr.w KosDec
; Load sound driver data (PSG envelopes, music/sound pointers, FM voice bank)
lea (Z80_SoundDriverData).l,a0
lea (z80_ram+$1300).l,a1
bsr.w KosDec
; Load default variables
lea (Z80_DefaultVariables).l,a0
lea (Z80_RAM+zDataStart).l,a1
move.w #Z80_DefaultVariables_end-Z80_DefaultVariables-1,d0
- move.b (a0)+,(a1)+
dbf d0,-
move.w #0,(Z80_reset).l ; reset Z80
nop
nop
nop
nop
move.w #$100,(Z80_reset).l ; release reset
startZ80
rts
; End of function SndDrvInit
; ---------------------------------------------------------------------------
; Default Z80 variables. These are actually set to more meaningful values
; in other SMPS Z80 drivers.
; ---------------------------------------------------------------------------
Z80_DefaultVariables:
dc.b 0 ; Unused 1
dc.b 0 ; Unused 2
dc.b 0 ; zPalFlag
dc.b 0 ; Unused 3
dc.b 0 ; zPalDblUpdCounter
dc.b 0 ; zSoundQueue0
dc.b 0 ; zSoundQueue1
dc.b 0 ; zSoundQueue2
dc.b 0 ; zTempoSpeedup
dc.b 0 ; zNextSound
dc.b 0 ; zMusicNumber
dc.b 0 ; zSFXNumber0
dc.b 0 ; zSFXNumber1
dc.b 0 ; zFadeOutTimeout
dc.b 0 ; zFadeDelay
dc.b 0 ; zFadeDelayTimeout
Z80_DefaultVariables_end:
; End of function SoundDriverLoad
Upgrading the Playback Routines
Now we have the code to load the new driver, time to add the new playback routines.
Upgrade Music Routine
First the PlaySound routine, locate:
; ---------------------------------------------------------------------------
; Subroutine to play a sound or music track
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound:
move.b d0,($FFFFF00A).w
rts
; End of function PlaySound
and replace it completely with:
; ---------------------------------------------------------------------------
; Subroutine to play a sound or music track
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound:
cmpi.w #$FB,d0
blt.s PlayNotSpecialFlag
bhi.s TestForNormalSpeed
move #8,d0
jmp SetTempo
TestForNormalSpeed:
cmpi.w #$FC,d0
bne.s PlayNotSpecialFlag
clr.w d0
jmp SetTempo
PlayNotSpecialFlag:
move.w #$100,($A11100).l
PlaySoundZ80NotStopped:
btst #0,($A11100).l
bne.s PlaySoundZ80NotStopped ; loop until it says it's stopped
move.b d0,($A01C0A).l
move.w #0,($A11100).l
rts
; End of function PlaySound
; ---------------------------------------------------------------------------
; Exclusive sound/music subroutine
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound_Ex:
tst.b 4(A0)
bpl.s SkipPlaySound_Special
Do not be alarmed about the new sound routine that was added, it is equivalent to the PlaySound_Local routine (as a matter of fact, it is identical, why sonic1 does not already have it is beyond me, since it indeed does work in Sonic 1)
sound playback routines
now find:
; ---------------------------------------------------------------------------
; Subroutine to play a special sound/music (E0-E4)
;
; E0 - Fade out
; E1 - Sega
; E2 - Speed up
; E3 - Normal speed
; E4 - Stop
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound_Special:
move.b d0,($FFFFF00B).w
rts
; End of function PlaySound_Special
; ===========================================================================
; ---------------------------------------------------------------------------
; Unused sound/music subroutine
; ---------------------------------------------------------------------------
PlaySound_Unk:
move.b d0,($FFFFF00C).w
rts
and replace it entirely with:
; ---------------------------------------------------------------------------
; Subroutine to play a special sound/music (FB-FF)
; ---------------------------------------------------------------------------
PlaySound_Special:
move.w #$100,($A11100).l
PlaySound_SpecialZ80NotStopped:
btst #0,($A11100).l
bne.s PlaySound_SpecialZ80NotStopped
cmp.b ($A01C0B).l,d0
beq.s PlaySound_Special1
tst.b ($A01C0B).l
bne.s PlaySound_Special0
move.b d0,($A01C0B).l
move.w #0,($A11100).l
rts
PlaySound_Special0:
move.b d0,($A01C0C).l
PlaySound_Special1:
move.w #0,($A11100).l
SkipPlaySound_Special:
rts
; End of function PlaySound_Special
; ---------------------------------------------------------------------------
; Subroutine to change the music tempo
; ---------------------------------------------------------------------------
SetTempo:
move.w #$100,($A11100).l
SetTempoZ80NotStopped:
btst #0,($A11100).l
bne.s SetTempoZ80NotStopped
move.b D0,($A01C08).l
move.w #0,($A11100).l
rts
Upgrading Pause / Resume routines
Now the playback routines are fixed and we have a routine to set the tempo the way the sneakers do, which we will elaberate later on, but we still have to update the pause / resume routines to the sonic 3 equivilents.
Find:
; ---------------------------------------------------------------------------
; Subroutine to pause the game
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PauseGame: ; XREF: Level_MainLoop; et al
nop
tst.b ($FFFFFE12).w ; do you have any lives left?
beq.s Unpause ; if not, branch
tst.w ($FFFFF63A).w ; is game already paused?
bne.s loc_13BE ; if yes, branch
btst #7,($FFFFF605).w ; is Start button pressed?
beq.s Pause_DoNothing ; if not, branch
loc_13BE:
move.w #1,($FFFFF63A).w ; freeze time
move.b #1,($FFFFF003).w ; pause music
loc_13CA:
move.b #$10,($FFFFF62A).w
bsr.w WaitForVBla
tst.b ($FFFFFFE1).w ; is slow-motion cheat on?
beq.s Pause_ChkStart ; if not, branch
btst #6,($FFFFF605).w ; is button A pressed?
beq.s Pause_ChkBC ; if not, branch
move.b #4,($FFFFF600).w ; set game mode to 4 (title screen)
nop
bra.s loc_1404
; ===========================================================================
Pause_ChkBC: ; XREF: PauseGame
btst #4,($FFFFF604).w ; is button B pressed?
bne.s Pause_SlowMo ; if yes, branch
btst #5,($FFFFF605).w ; is button C pressed?
bne.s Pause_SlowMo ; if yes, branch
Pause_ChkStart: ; XREF: PauseGame
btst #7,($FFFFF605).w ; is Start button pressed?
beq.s loc_13CA ; if not, branch
loc_1404: ; XREF: PauseGame
move.b #$80,($FFFFF003).w
Unpause: ; XREF: PauseGame
move.w #0,($FFFFF63A).w ; unpause the game
Pause_DoNothing: ; XREF: PauseGame
rts
; ===========================================================================
Pause_SlowMo: ; XREF: PauseGame
move.w #1,($FFFFF63A).w
move.b #$80,($FFFFF003).w
rts
; End of function PauseGame
and replace it completely with:
; ---------------------------------------------------------------------------
; Subroutine to pause the game
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PauseGame: ; XREF: Level_MainLoop; et al
nop
tst.b ($FFFFFE12).w ; do you have any lives left
beq Unpause ; if not, branch
tst.w ($FFFFF63A).w ;is the game already paused?
bne.s PauseGame_AlreadyPaused ;if yes, branch
move.b ($FFFFF605).w,d0 ;did you press start
or.b ($FFFFF607).w,d0 ;on either controller?
andi.b #$80,d0
beq Pause_DoNothing ;if not, branch
PauseGame_AlreadyPaused:
move.w #1,($FFFFF63A).w ;unpause the game
move.w #$100,($A11100).l ;stop the z80
PauseGameZ80NotStopped:
btst #0,($A11100).l
bne.s PauseGameZ80NotStopped
move.b #1,($A01C10).l ;unpause the music ;)
move.w #0,($A11100).l ;start the z80
PauseGameLoop:
move.b #$10,($FFFFF62A).w ;re-pause the game (used in slow-mo and frame advance)
jsr DelayProgram ;wait...
tst.b ($FFFFFFE1).w ; Edit: the value is $FFFFFFE1 in Sonic 1 (slow-mo/frame advance mode)
beq.s Pause_ChkStart
btst #6,($FFFFF605).w ;is player 1 pressing either A?
beq.s Pause_ChkBC ;if not, branch
move.b #$4,($FFFFF600).w ; Go To Title Screen
nop
bra.s PauseGame1 ;time to stop the z80 again
Pause_ChkBC:
btst #4,($FFFFF604).w ;did you press a?
bne.s Pause_SlowMo ;if so, branch
btst #5,($FFFFF605).w ;did you press b?
bne.s Pause_SlowMo ;if so, branch
Pause_ChkStart:
btst #4,($FFFFF605).w ;did you press a?
beq.s PauseGame0 ;if yes, then paise the freaken game
move.b #$0,($FFFFF600).w ;prepare to go to sega screen (level select is not 0xC0 in sonic1, but part of title screen code)
bra.s PauseGame1 ;go to title screen
PauseGame0:
move.b ($FFFFF605).w,d0 ;on controller 1?
or.b ($FFFFF607).w,d0 ;or 2?
andi.b #$80,d0 ;if not, no change
beq.s PauseGameLoop ;in other words, don't pause
PauseGame1:
move.w #$100,($A11100).l ;stop the z80
Pause_ChkStartZ80NotStopped:
btst #0,($A11100).l
bne.s Pause_ChkStartZ80NotStopped
move.b #$80,($A01C10).l ;pause the music
move.w #0,($A11100).l ;start the z80
Unpause:
move.w #0,($FFFFF63A).w ;unpause the game
Pause_DoNothing:
rts
Pause_SlowMo:
move.w #1,($FFFFF63A).w ;unpause the music for a frame
move.w #$100,($A11100).l ;stop the z80
Pause_SlowMoZ80NotStopped:
btst #0,($A11100).l
bne.s Pause_SlowMoZ80NotStopped
move.b #$80,($A01C10).l ;pause the music again
move.w #0,($A11100).l ;start the z80
rts
; End of function PauseGame
Replacing 68k driver with new Z80 driver
We need the sound driver itself if we are going to have any sound and music, so we locate:
align 2
Go_SoundTypes: dc.l SoundTypes ; XREF: Sound_Play
Go_SoundD0: dc.l SoundD0Index ; XREF: Sound_D0toDF
Go_MusicIndex: dc.l MusicIndex ; XREF: Sound_81to9F
Go_SoundIndex: dc.l SoundIndex ; XREF: Sound_A0toCF
off_719A0: dc.l byte_71A94 ; XREF: Sound_81to9F
Go_PSGIndex: dc.l PSG_Index ; XREF: sub_72926
; ---------------------------------------------------------------------------
; PSG instruments used in music
; ---------------------------------------------------------------------------
PSG_Index: dc.l PSG1, PSG2, PSG3
dc.l PSG4, PSG5, PSG6
dc.l PSG7, PSG8, PSG9
PSG1: binclude sound\psg1.bin
PSG2: binclude sound\psg2.bin
PSG3: binclude sound\psg3.bin
PSG4: binclude sound\psg4.bin
PSG6: binclude sound\psg6.bin
PSG5: binclude sound\psg5.bin
PSG7: binclude sound\psg7.bin
PSG8: binclude sound\psg8.bin
PSG9: binclude sound\psg9.bin
byte_71A94: dc.b 7, $72, $73, $26, $15, 8, $FF, 5
; ---------------------------------------------------------------------------
; Music Pointers
; ---------------------------------------------------------------------------
MusicIndex: dc.l Music81, Music82
dc.l Music83, Music84
dc.l Music85, Music86
dc.l Music87, Music88
dc.l Music89, Music8A
dc.l Music8B, Music8C
dc.l Music8D, Music8E
dc.l Music8F, Music90
dc.l Music91, Music92
dc.l Music93
; ---------------------------------------------------------------------------
; Type of sound being played ($90 = music; $70 = normal sound effect)
; ---------------------------------------------------------------------------
SoundTypes: dc.b $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90
dc.b $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $90, $80
dc.b $70, $70, $70, $70, $70, $70, $70, $70, $70, $68, $70, $70, $70, $60, $70, $70
dc.b $60, $70, $60, $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $7F, $60
dc.b $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $70, $80
dc.b $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $90
dc.b $90, $90, $90, $90
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71B4C: ; XREF: loc_B10; PalToCRAM
move.w #$100,($A11100).l ; stop the Z80
nop
nop
nop
loc_71B5A:
btst #0,($A11100).l
bne.s loc_71B5A
btst #7,($A01FFD).l
beq.s loc_71B82
move.w #0,($A11100).l ; start the Z80
nop
nop
nop
nop
nop
bra.s sub_71B4C
; ===========================================================================
loc_71B82:
lea ($FFF000).l,a6
clr.b $E(a6)
tst.b 3(a6) ; is music paused?
bne.w loc_71E50 ; if yes, branch
subq.b #1,1(a6)
bne.s loc_71B9E
jsr sub_7260C(pc)
loc_71B9E:
move.b 4(a6),d0
beq.s loc_71BA8
jsr sub_72504(pc)
loc_71BA8:
tst.b $24(a6)
beq.s loc_71BB2
jsr sub_7267C(pc)
loc_71BB2:
tst.w $A(a6) ; is music or sound being played?
beq.s loc_71BBC ; if not, branch
jsr Sound_Play(pc)
loc_71BBC:
cmpi.b #$80,9(a6)
beq.s loc_71BC8
jsr Sound_ChkValue(pc)
loc_71BC8:
lea $40(a6),a5
tst.b (a5)
bpl.s loc_71BD4
jsr sub_71C4E(pc)
loc_71BD4:
clr.b 8(a6)
moveq #5,d7
loc_71BDA:
adda.w #$30,a5
tst.b (a5)
bpl.s loc_71BE6
jsr sub_71CCA(pc)
loc_71BE6:
dbf d7,loc_71BDA
moveq #2,d7
loc_71BEC:
adda.w #$30,a5
tst.b (a5)
bpl.s loc_71BF8
jsr sub_72850(pc)
loc_71BF8:
dbf d7,loc_71BEC
move.b #$80,$E(a6)
moveq #2,d7
loc_71C04:
adda.w #$30,a5
tst.b (a5)
bpl.s loc_71C10
jsr sub_71CCA(pc)
loc_71C10:
dbf d7,loc_71C04
moveq #2,d7
loc_71C16:
adda.w #$30,a5
tst.b (a5)
bpl.s loc_71C22
jsr sub_72850(pc)
loc_71C22:
dbf d7,loc_71C16
move.b #$40,$E(a6)
adda.w #$30,a5
tst.b (a5)
bpl.s loc_71C38
jsr sub_71CCA(pc)
loc_71C38:
adda.w #$30,a5
tst.b (a5)
bpl.s loc_71C44
jsr sub_72850(pc)
loc_71C44:
move.w #0,($A11100).l ; start the Z80
rts
; End of function sub_71B4C
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71C4E: ; XREF: sub_71B4C
subq.b #1,$E(a5)
bne.s locret_71CAA
move.b #$80,8(a6)
movea.l 4(a5),a4
loc_71C5E:
moveq #0,d5
move.b (a4)+,d5
cmpi.b #-$20,d5
bcs.s loc_71C6E
jsr sub_72A5A(pc)
bra.s loc_71C5E
; ===========================================================================
loc_71C6E:
tst.b d5
bpl.s loc_71C84
move.b d5,$10(a5)
move.b (a4)+,d5
bpl.s loc_71C84
subq.w #1,a4
move.b $F(a5),$E(a5)
bra.s loc_71C88
; ===========================================================================
loc_71C84:
jsr sub_71D40(pc)
loc_71C88:
move.l a4,4(a5)
btst #2,(a5)
bne.s locret_71CAA
moveq #0,d0
move.b $10(a5),d0
cmpi.b #$80,d0
beq.s locret_71CAA
btst #3,d0
bne.s loc_71CAC
move.b d0,($A01FFF).l
locret_71CAA:
rts
; ===========================================================================
loc_71CAC:
subi.b #$88,d0
move.b byte_71CC4(pc,d0.w),d0
move.b d0,($A000EA).l
move.b #$83,($A01FFF).l
rts
; End of function sub_71C4E
; ===========================================================================
byte_71CC4: dc.b $12, $15, $1C, $1D, $FF, $FF
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71CCA: ; XREF: sub_71B4C
subq.b #1,$E(a5)
bne.s loc_71CE0
bclr #4,(a5)
jsr sub_71CEC(pc)
jsr sub_71E18(pc)
bra.w loc_726E2
; ===========================================================================
loc_71CE0:
jsr sub_71D9E(pc)
jsr sub_71DC6(pc)
bra.w loc_71E24
; End of function sub_71CCA
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71CEC: ; XREF: sub_71CCA
movea.l 4(a5),a4
bclr #1,(a5)
loc_71CF4:
moveq #0,d5
move.b (a4)+,d5
cmpi.b #-$20,d5
bcs.s loc_71D04
jsr sub_72A5A(pc)
bra.s loc_71CF4
; ===========================================================================
loc_71D04:
jsr sub_726FE(pc)
tst.b d5
bpl.s loc_71D1A
jsr sub_71D22(pc)
move.b (a4)+,d5
bpl.s loc_71D1A
subq.w #1,a4
bra.w sub_71D60
; ===========================================================================
loc_71D1A:
jsr sub_71D40(pc)
bra.w sub_71D60
; End of function sub_71CEC
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71D22: ; XREF: sub_71CEC
subi.b #$80,d5
beq.s loc_71D58
add.b 8(a5),d5
andi.w #$7F,d5
lsl.w #1,d5
lea word_72790(pc),a0
move.w (a0,d5.w),d6
move.w d6,$10(a5)
rts
; End of function sub_71D22
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71D40: ; XREF: sub_71C4E; sub_71CEC; sub_72878
move.b d5,d0
move.b 2(a5),d1
loc_71D46:
subq.b #1,d1
beq.s loc_71D4E
add.b d5,d0
bra.s loc_71D46
; ===========================================================================
loc_71D4E:
move.b d0,$F(a5)
move.b d0,$E(a5)
rts
; End of function sub_71D40
; ===========================================================================
loc_71D58: ; XREF: sub_71D22
bset #1,(a5)
clr.w $10(a5)
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71D60: ; XREF: sub_71CEC; sub_72878; sub_728AC
move.l a4,4(a5)
move.b $F(a5),$E(a5)
btst #4,(a5)
bne.s locret_71D9C
move.b $13(a5),$12(a5)
clr.b $C(a5)
btst #3,(a5)
beq.s locret_71D9C
movea.l $14(a5),a0
move.b (a0)+,$18(a5)
move.b (a0)+,$19(a5)
move.b (a0)+,$1A(a5)
move.b (a0)+,d0
lsr.b #1,d0
move.b d0,$1B(a5)
clr.w $1C(a5)
locret_71D9C:
rts
; End of function sub_71D60
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71D9E: ; XREF: sub_71CCA; sub_72850
tst.b $12(a5)
beq.s locret_71DC4
subq.b #1,$12(a5)
bne.s locret_71DC4
bset #1,(a5)
tst.b 1(a5)
bmi.w loc_71DBE
jsr sub_726FE(pc)
addq.w #4,sp
rts
; ===========================================================================
loc_71DBE:
jsr sub_729A0(pc)
addq.w #4,sp
locret_71DC4:
rts
; End of function sub_71D9E
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71DC6: ; XREF: sub_71CCA; sub_72850
addq.w #4,sp
btst #3,(a5)
beq.s locret_71E16
tst.b $18(a5)
beq.s loc_71DDA
subq.b #1,$18(a5)
rts
; ===========================================================================
loc_71DDA:
subq.b #1,$19(a5)
beq.s loc_71DE2
rts
; ===========================================================================
loc_71DE2:
movea.l $14(a5),a0
move.b 1(a0),$19(a5)
tst.b $1B(a5)
bne.s loc_71DFE
move.b 3(a0),$1B(a5)
neg.b $1A(a5)
rts
; ===========================================================================
loc_71DFE:
subq.b #1,$1B(a5)
move.b $1A(a5),d6
ext.w d6
add.w $1C(a5),d6
move.w d6,$1C(a5)
add.w $10(a5),d6
subq.w #4,sp
locret_71E16:
rts
; End of function sub_71DC6
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_71E18: ; XREF: sub_71CCA
btst #1,(a5)
bne.s locret_71E48
move.w $10(a5),d6
beq.s loc_71E4A
loc_71E24: ; XREF: sub_71CCA
move.b $1E(a5),d0
ext.w d0
add.w d0,d6
btst #2,(a5)
bne.s locret_71E48
move.w d6,d1
lsr.w #8,d1
move.b #-$5C,d0
jsr sub_72722(pc)
move.b d6,d1
move.b #-$60,d0
jsr sub_72722(pc)
locret_71E48:
rts
; ===========================================================================
loc_71E4A:
bset #1,(a5)
rts
; End of function sub_71E18
; ===========================================================================
loc_71E50: ; XREF: sub_71B4C
bmi.s loc_71E94
cmpi.b #2,3(a6)
beq.w loc_71EFE
move.b #2,3(a6)
moveq #2,d3
move.b #-$4C,d0
moveq #0,d1
loc_71E6A:
jsr sub_7272E(pc)
jsr sub_72764(pc)
addq.b #1,d0
dbf d3,loc_71E6A
moveq #2,d3
moveq #$28,d0
loc_71E7C:
move.b d3,d1
jsr sub_7272E(pc)
addq.b #4,d1
jsr sub_7272E(pc)
dbf d3,loc_71E7C
jsr sub_729B6(pc)
bra.w loc_71C44
; ===========================================================================
loc_71E94: ; XREF: loc_71E50
clr.b 3(a6)
moveq #$30,d3
lea $40(a6),a5
moveq #6,d4
loc_71EA0:
btst #7,(a5)
beq.s loc_71EB8
btst #2,(a5)
bne.s loc_71EB8
move.b #-$4C,d0
move.b $A(a5),d1
jsr sub_72722(pc)
loc_71EB8:
adda.w d3,a5
dbf d4,loc_71EA0
lea $220(a6),a5
moveq #2,d4
loc_71EC4:
btst #7,(a5)
beq.s loc_71EDC
btst #2,(a5)
bne.s loc_71EDC
move.b #-$4C,d0
move.b $A(a5),d1
jsr sub_72722(pc)
loc_71EDC:
adda.w d3,a5
dbf d4,loc_71EC4
lea $340(a6),a5
btst #7,(a5)
beq.s loc_71EFE
btst #2,(a5)
bne.s loc_71EFE
move.b #-$4C,d0
move.b $A(a5),d1
jsr sub_72722(pc)
loc_71EFE:
bra.w loc_71C44
; ---------------------------------------------------------------------------
; Subroutine to play a sound or music track
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
Sound_Play: ; XREF: sub_71B4C
movea.l (Go_SoundTypes).l,a0
lea $A(a6),a1 ; load music track number
move.b 0(a6),d3
moveq #2,d4
loc_71F12:
move.b (a1),d0 ; move track number to d0
move.b d0,d1
clr.b (a1)+
subi.b #$81,d0
bcs.s loc_71F3E
cmpi.b #$80,9(a6)
beq.s loc_71F2C
move.b d1,$A(a6)
bra.s loc_71F3E
; ===========================================================================
loc_71F2C:
andi.w #$7F,d0
move.b (a0,d0.w),d2
cmp.b d3,d2
bcs.s loc_71F3E
move.b d2,d3
move.b d1,9(a6) ; set music flag
loc_71F3E:
dbf d4,loc_71F12
tst.b d3
bmi.s locret_71F4A
move.b d3,0(a6)
locret_71F4A:
rts
; End of function Sound_Play
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
Sound_ChkValue: ; XREF: sub_71B4C
moveq #0,d7
move.b 9(a6),d7
beq.w Sound_E4
bpl.s locret_71F8C
move.b #$80,9(a6) ; reset music flag
cmpi.b #$9F,d7
bls.w Sound_81to9F ; music $81-$9F
cmpi.b #$A0,d7
bcs.w locret_71F8C
cmpi.b #$CF,d7
bls.w Sound_A0toCF ; sound $A0-$CF
cmpi.b #$D0,d7
bcs.w locret_71F8C
cmpi.b #$E0,d7
bcs.w Sound_D0toDF ; sound $D0-$DF
cmpi.b #$E4,d7
bls.s Sound_E0toE4 ; sound $E0-$E4
locret_71F8C:
rts
; ===========================================================================
Sound_E0toE4: ; XREF: Sound_ChkValue
subi.b #$E0,d7
lsl.w #2,d7
jmp Sound_ExIndex(pc,d7.w)
; ===========================================================================
Sound_ExIndex:
bra.w Sound_E0
; ===========================================================================
bra.w Sound_E1
; ===========================================================================
bra.w Sound_E2
; ===========================================================================
bra.w Sound_E3
; ===========================================================================
bra.w Sound_E4
; ===========================================================================
; ---------------------------------------------------------------------------
; Play "Say-gaa" PCM sound
; ---------------------------------------------------------------------------
Sound_E1: ; XREF: Sound_ExIndex
move.b #$88,($A01FFF).l
move.w #0,($A11100).l ; start the Z80
move.w #$11,d1
loc_71FC0:
move.w #-1,d0
loc_71FC4:
nop
dbf d0,loc_71FC4
dbf d1,loc_71FC0
addq.w #4,sp
rts
; ===========================================================================
; ---------------------------------------------------------------------------
; Play music track $81-$9F
; ---------------------------------------------------------------------------
Sound_81to9F: ; XREF: Sound_ChkValue
cmpi.b #$88,d7 ; is "extra life" music played?
bne.s loc_72024 ; if not, branch
tst.b $27(a6)
bne.w loc_721B6
lea $40(a6),a5
moveq #9,d0
loc_71FE6:
bclr #2,(a5)
adda.w #$30,a5
dbf d0,loc_71FE6
lea $220(a6),a5
moveq #5,d0
loc_71FF8:
bclr #7,(a5)
adda.w #$30,a5
dbf d0,loc_71FF8
clr.b 0(a6)
movea.l a6,a0
lea $3A0(a6),a1
move.w #$87,d0
loc_72012:
move.l (a0)+,(a1)+
dbf d0,loc_72012
move.b #$80,$27(a6)
clr.b 0(a6)
bra.s loc_7202C
; ===========================================================================
loc_72024:
clr.b $27(a6)
clr.b $26(a6)
loc_7202C:
jsr sub_725CA(pc)
movea.l (off_719A0).l,a4
subi.b #$81,d7
move.b (a4,d7.w),$29(a6)
movea.l (Go_MusicIndex).l,a4
lsl.w #2,d7
movea.l (a4,d7.w),a4
moveq #0,d0
move.w (a4),d0
add.l a4,d0
move.l d0,$18(a6)
move.b 5(a4),d0
move.b d0,$28(a6)
tst.b $2A(a6)
beq.s loc_72068
move.b $29(a6),d0
loc_72068:
move.b d0,2(a6)
move.b d0,1(a6)
moveq #0,d1
movea.l a4,a3
addq.w #6,a4
moveq #0,d7
move.b 2(a3),d7
beq.w loc_72114
subq.b #1,d7
move.b #-$40,d1
move.b 4(a3),d4
moveq #$30,d6
move.b #1,d5
lea $40(a6),a1
lea byte_721BA(pc),a2
loc_72098:
bset #7,(a1)
move.b (a2)+,1(a1)
move.b d4,2(a1)
move.b d6,$D(a1)
move.b d1,$A(a1)
move.b d5,$E(a1)
moveq #0,d0
move.w (a4)+,d0
add.l a3,d0
move.l d0,4(a1)
move.w (a4)+,8(a1)
adda.w d6,a1
dbf d7,loc_72098
cmpi.b #7,2(a3)
bne.s loc_720D8
moveq #$2B,d0
moveq #0,d1
jsr sub_7272E(pc)
bra.w loc_72114
; ===========================================================================
loc_720D8:
moveq #$28,d0
moveq #6,d1
jsr sub_7272E(pc)
move.b #$42,d0
moveq #$7F,d1
jsr sub_72764(pc)
move.b #$4A,d0
moveq #$7F,d1
jsr sub_72764(pc)
move.b #$46,d0
moveq #$7F,d1
jsr sub_72764(pc)
move.b #$4E,d0
moveq #$7F,d1
jsr sub_72764(pc)
move.b #-$4A,d0
move.b #-$40,d1
jsr sub_72764(pc)
loc_72114:
moveq #0,d7
move.b 3(a3),d7
beq.s loc_72154
subq.b #1,d7
lea $190(a6),a1
lea byte_721C2(pc),a2
loc_72126:
bset #7,(a1)
move.b (a2)+,1(a1)
move.b d4,2(a1)
move.b d6,$D(a1)
move.b d5,$E(a1)
moveq #0,d0
move.w (a4)+,d0
add.l a3,d0
move.l d0,4(a1)
move.w (a4)+,8(a1)
move.b (a4)+,d0
move.b (a4)+,$B(a1)
adda.w d6,a1
dbf d7,loc_72126
loc_72154:
lea $220(a6),a1
moveq #5,d7
loc_7215A:
tst.b (a1)
bpl.w loc_7217C
moveq #0,d0
move.b 1(a1),d0
bmi.s loc_7216E
subq.b #2,d0
lsl.b #2,d0
bra.s loc_72170
; ===========================================================================
loc_7216E:
lsr.b #3,d0
loc_72170:
lea dword_722CC(pc),a0
movea.l (a0,d0.w),a0
bset #2,(a0)
loc_7217C:
adda.w d6,a1
dbf d7,loc_7215A
tst.w $340(a6)
bpl.s loc_7218E
bset #2,$100(a6)
loc_7218E:
tst.w $370(a6)
bpl.s loc_7219A
bset #2,$1F0(a6)
loc_7219A:
lea $70(a6),a5
moveq #5,d4
loc_721A0:
jsr sub_726FE(pc)
adda.w d6,a5
dbf d4,loc_721A0
moveq #2,d4
loc_721AC:
jsr sub_729A0(pc)
adda.w d6,a5
dbf d4,loc_721AC
loc_721B6:
addq.w #4,sp
rts
; ===========================================================================
byte_721BA: dc.b 6, 0, 1, 2, 4, 5, 6, 0
align 2
byte_721C2: dc.b $80, $A0, $C0, 0
align 2
; ===========================================================================
; ---------------------------------------------------------------------------
; Play normal sound effect
; ---------------------------------------------------------------------------
Sound_A0toCF: ; XREF: Sound_ChkValue
tst.b $27(a6)
bne.w loc_722C6
tst.b 4(a6)
bne.w loc_722C6
tst.b $24(a6)
bne.w loc_722C6
cmpi.b #$B5,d7 ; is ring sound effect played?
bne.s Sound_notB5 ; if not, branch
tst.b $2B(a6)
bne.s loc_721EE
move.b #$CE,d7 ; play ring sound in left speaker
loc_721EE:
bchg #0,$2B(a6) ; change speaker
Sound_notB5:
cmpi.b #$A7,d7 ; is "pushing" sound played?
bne.s Sound_notA7 ; if not, branch
tst.b $2C(a6)
bne.w locret_722C4
move.b #$80,$2C(a6)
Sound_notA7:
movea.l (Go_SoundIndex).l,a0
subi.b #$A0,d7
lsl.w #2,d7
movea.l (a0,d7.w),a3
movea.l a3,a1
moveq #0,d1
move.w (a1)+,d1
add.l a3,d1
move.b (a1)+,d5
move.b (a1)+,d7
subq.b #1,d7
moveq #$30,d6
loc_72228:
moveq #0,d3
move.b 1(a1),d3
move.b d3,d4
bmi.s loc_72244
subq.w #2,d3
lsl.w #2,d3
lea dword_722CC(pc),a5
movea.l (a5,d3.w),a5
bset #2,(a5)
bra.s loc_7226E
; ===========================================================================
loc_72244:
lsr.w #3,d3
lea dword_722CC(pc),a5
movea.l (a5,d3.w),a5
bset #2,(a5)
cmpi.b #$C0,d4
bne.s loc_7226E
move.b d4,d0
ori.b #$1F,d0
move.b d0,($C00011).l
bchg #5,d0
move.b d0,($C00011).l
loc_7226E:
movea.l dword_722EC(pc,d3.w),a5
movea.l a5,a2
moveq #$B,d0
loc_72276:
clr.l (a2)+
dbf d0,loc_72276
move.w (a1)+,(a5)
move.b d5,2(a5)
moveq #0,d0
move.w (a1)+,d0
add.l a3,d0
move.l d0,4(a5)
move.w (a1)+,8(a5)
move.b #1,$E(a5)
move.b d6,$D(a5)
tst.b d4
bmi.s loc_722A8
move.b #$C0,$A(a5)
move.l d1,$20(a5)
loc_722A8:
dbf d7,loc_72228
tst.b $250(a6)
bpl.s loc_722B8
bset #2,$340(a6)
loc_722B8:
tst.b $310(a6)
bpl.s locret_722C4
bset #2,$370(a6)
locret_722C4:
rts
; ===========================================================================
loc_722C6:
clr.b 0(a6)
rts
; ===========================================================================
dword_722CC: dc.l $FFF0D0
dc.l 0
dc.l $FFF100
dc.l $FFF130
dc.l $FFF190
dc.l $FFF1C0
dc.l $FFF1F0
dc.l $FFF1F0
dword_722EC: dc.l $FFF220
dc.l 0
dc.l $FFF250
dc.l $FFF280
dc.l $FFF2B0
dc.l $FFF2E0
dc.l $FFF310
dc.l $FFF310
; ===========================================================================
; ---------------------------------------------------------------------------
; Play GHZ waterfall sound
; ---------------------------------------------------------------------------
Sound_D0toDF: ; XREF: Sound_ChkValue
tst.b $27(a6)
bne.w locret_723C6
tst.b 4(a6)
bne.w locret_723C6
tst.b $24(a6)
bne.w locret_723C6
movea.l (Go_SoundD0).l,a0
subi.b #$D0,d7
lsl.w #2,d7
movea.l (a0,d7.w),a3
movea.l a3,a1
moveq #0,d0
move.w (a1)+,d0
add.l a3,d0
move.l d0,$20(a6)
move.b (a1)+,d5
move.b (a1)+,d7
subq.b #1,d7
moveq #$30,d6
loc_72348:
move.b 1(a1),d4
bmi.s loc_7235A
bset #2,$100(a6)
lea $340(a6),a5
bra.s loc_72364
; ===========================================================================
loc_7235A:
bset #2,$1F0(a6)
lea $370(a6),a5
loc_72364:
movea.l a5,a2
moveq #$B,d0
loc_72368:
clr.l (a2)+
dbf d0,loc_72368
move.w (a1)+,(a5)
move.b d5,2(a5)
moveq #0,d0
move.w (a1)+,d0
add.l a3,d0
move.l d0,4(a5)
move.w (a1)+,8(a5)
move.b #1,$E(a5)
move.b d6,$D(a5)
tst.b d4
bmi.s loc_72396
move.b #$C0,$A(a5)
loc_72396:
dbf d7,loc_72348
tst.b $250(a6)
bpl.s loc_723A6
bset #2,$340(a6)
loc_723A6:
tst.b $310(a6)
bpl.s locret_723C6
bset #2,$370(a6)
ori.b #$1F,d4
move.b d4,($C00011).l
bchg #5,d4
move.b d4,($C00011).l
locret_723C6:
rts
; End of function Sound_ChkValue
; ===========================================================================
dc.l $FFF100
dc.l $FFF1F0
dc.l $FFF250
dc.l $FFF310
dc.l $FFF340
dc.l $FFF370
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
Snd_FadeOut1: ; XREF: Sound_E0
clr.b 0(a6)
lea $220(a6),a5
moveq #5,d7
loc_723EA:
tst.b (a5)
bpl.w loc_72472
bclr #7,(a5)
moveq #0,d3
move.b 1(a5),d3
bmi.s loc_7243C
jsr sub_726FE(pc)
cmpi.b #4,d3
bne.s loc_72416
tst.b $340(a6)
bpl.s loc_72416
lea $340(a6),a5
movea.l $20(a6),a1
bra.s loc_72428
; ===========================================================================
loc_72416:
subq.b #2,d3
lsl.b #2,d3
lea dword_722CC(pc),a0
movea.l a5,a3
movea.l (a0,d3.w),a5
movea.l $18(a6),a1
loc_72428:
bclr #2,(a5)
bset #1,(a5)
move.b $B(a5),d0
jsr sub_72C4E(pc)
movea.l a3,a5
bra.s loc_72472
; ===========================================================================
loc_7243C:
jsr sub_729A0(pc)
lea $370(a6),a0
cmpi.b #$E0,d3
beq.s loc_7245A
cmpi.b #$C0,d3
beq.s loc_7245A
lsr.b #3,d3
lea dword_722CC(pc),a0
movea.l (a0,d3.w),a0
loc_7245A:
bclr #2,(a0)
bset #1,(a0)
cmpi.b #$E0,1(a0)
bne.s loc_72472
move.b $1F(a0),($C00011).l
loc_72472:
adda.w #$30,a5
dbf d7,loc_723EA
rts
; End of function Snd_FadeOut1
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
Snd_FadeOut2: ; XREF: Sound_E0
lea $340(a6),a5
tst.b (a5)
bpl.s loc_724AE
bclr #7,(a5)
btst #2,(a5)
bne.s loc_724AE
jsr loc_7270A(pc)
lea $100(a6),a5
bclr #2,(a5)
bset #1,(a5)
tst.b (a5)
bpl.s loc_724AE
movea.l $18(a6),a1
move.b $B(a5),d0
jsr sub_72C4E(pc)
loc_724AE:
lea $370(a6),a5
tst.b (a5)
bpl.s locret_724E4
bclr #7,(a5)
btst #2,(a5)
bne.s locret_724E4
jsr loc_729A6(pc)
lea $1F0(a6),a5
bclr #2,(a5)
bset #1,(a5)
tst.b (a5)
bpl.s locret_724E4
cmpi.b #-$20,1(a5)
bne.s locret_724E4
move.b $1F(a5),($C00011).l
locret_724E4:
rts
; End of function Snd_FadeOut2
; ===========================================================================
; ---------------------------------------------------------------------------
; Fade out music
; ---------------------------------------------------------------------------
Sound_E0: ; XREF: Sound_ExIndex
jsr Snd_FadeOut1(pc)
jsr Snd_FadeOut2(pc)
move.b #3,6(a6)
move.b #$28,4(a6)
clr.b $40(a6)
clr.b $2A(a6)
rts
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72504: ; XREF: sub_71B4C
move.b 6(a6),d0
beq.s loc_72510
subq.b #1,6(a6)
rts
; ===========================================================================
loc_72510:
subq.b #1,4(a6)
beq.w Sound_E4
move.b #3,6(a6)
lea $70(a6),a5
moveq #5,d7
loc_72524:
tst.b (a5)
bpl.s loc_72538
addq.b #1,9(a5)
bpl.s loc_72534
bclr #7,(a5)
bra.s loc_72538
; ===========================================================================
loc_72534:
jsr sub_72CB4(pc)
loc_72538:
adda.w #$30,a5
dbf d7,loc_72524
moveq #2,d7
loc_72542:
tst.b (a5)
bpl.s loc_72560
addq.b #1,9(a5)
cmpi.b #$10,9(a5)
bcs.s loc_72558
bclr #7,(a5)
bra.s loc_72560
; ===========================================================================
loc_72558:
move.b 9(a5),d6
jsr sub_7296A(pc)
loc_72560:
adda.w #$30,a5
dbf d7,loc_72542
rts
; End of function sub_72504
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_7256A: ; XREF: Sound_E4; sub_725CA
moveq #2,d3
moveq #$28,d0
loc_7256E:
move.b d3,d1
jsr sub_7272E(pc)
addq.b #4,d1
jsr sub_7272E(pc)
dbf d3,loc_7256E
moveq #$40,d0
moveq #$7F,d1
moveq #2,d4
loc_72584:
moveq #3,d3
loc_72586:
jsr sub_7272E(pc)
jsr sub_72764(pc)
addq.w #4,d0
dbf d3,loc_72586
subi.b #$F,d0
dbf d4,loc_72584
rts
; End of function sub_7256A
; ===========================================================================
; ---------------------------------------------------------------------------
; Stop music
; ---------------------------------------------------------------------------
Sound_E4: ; XREF: Sound_ChkValue; Sound_ExIndex; sub_72504
moveq #$2B,d0
move.b #$80,d1
jsr sub_7272E(pc)
moveq #$27,d0
moveq #0,d1
jsr sub_7272E(pc)
movea.l a6,a0
move.w #$E3,d0
loc_725B6:
clr.l (a0)+
dbf d0,loc_725B6
move.b #$80,9(a6) ; set music to $80 (silence)
jsr sub_7256A(pc)
bra.w sub_729B6
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_725CA: ; XREF: Sound_ChkValue
movea.l a6,a0
move.b 0(a6),d1
move.b $27(a6),d2
move.b $2A(a6),d3
move.b $26(a6),d4
move.w $A(a6),d5
move.w #$87,d0
loc_725E4:
clr.l (a0)+
dbf d0,loc_725E4
move.b d1,0(a6)
move.b d2,$27(a6)
move.b d3,$2A(a6)
move.b d4,$26(a6)
move.w d5,$A(a6)
move.b #$80,9(a6)
jsr sub_7256A(pc)
bra.w sub_729B6
; End of function sub_725CA
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_7260C: ; XREF: sub_71B4C
move.b 2(a6),1(a6)
lea $4E(a6),a0
moveq #$30,d0
moveq #9,d1
loc_7261A:
addq.b #1,(a0)
adda.w d0,a0
dbf d1,loc_7261A
rts
; End of function sub_7260C
; ===========================================================================
; ---------------------------------------------------------------------------
; Speed up music
; ---------------------------------------------------------------------------
Sound_E2: ; XREF: Sound_ExIndex
tst.b $27(a6)
bne.s loc_7263E
move.b $29(a6),2(a6)
move.b $29(a6),1(a6)
move.b #$80,$2A(a6)
rts
; ===========================================================================
loc_7263E:
move.b $3C9(a6),$3A2(a6)
move.b $3C9(a6),$3A1(a6)
move.b #$80,$3CA(a6)
rts
; ===========================================================================
; ---------------------------------------------------------------------------
; Change music back to normal speed
; ---------------------------------------------------------------------------
Sound_E3: ; XREF: Sound_ExIndex
tst.b $27(a6)
bne.s loc_7266A
move.b $28(a6),2(a6)
move.b $28(a6),1(a6)
clr.b $2A(a6)
rts
; ===========================================================================
loc_7266A:
move.b $3C8(a6),$3A2(a6)
move.b $3C8(a6),$3A1(a6)
clr.b $3CA(a6)
rts
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_7267C: ; XREF: sub_71B4C
tst.b $25(a6)
beq.s loc_72688
subq.b #1,$25(a6)
rts
; ===========================================================================
loc_72688:
tst.b $26(a6)
beq.s loc_726D6
subq.b #1,$26(a6)
move.b #2,$25(a6)
lea $70(a6),a5
moveq #5,d7
loc_7269E:
tst.b (a5)
bpl.s loc_726AA
subq.b #1,9(a5)
jsr sub_72CB4(pc)
loc_726AA:
adda.w #$30,a5
dbf d7,loc_7269E
moveq #2,d7
loc_726B4:
tst.b (a5)
bpl.s loc_726CC
subq.b #1,9(a5)
move.b 9(a5),d6
cmpi.b #$10,d6
bcs.s loc_726C8
moveq #$F,d6
loc_726C8:
jsr sub_7296A(pc)
loc_726CC:
adda.w #$30,a5
dbf d7,loc_726B4
rts
; ===========================================================================
loc_726D6:
bclr #2,$40(a6)
clr.b $24(a6)
rts
; End of function sub_7267C
; ===========================================================================
loc_726E2: ; XREF: sub_71CCA
btst #1,(a5)
bne.s locret_726FC
btst #2,(a5)
bne.s locret_726FC
moveq #$28,d0
move.b 1(a5),d1
ori.b #-$10,d1
bra.w sub_7272E
; ===========================================================================
locret_726FC:
rts
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_726FE: ; XREF: sub_71CEC; sub_71D9E; Sound_ChkValue; Snd_FadeOut1
btst #4,(a5)
bne.s locret_72714
btst #2,(a5)
bne.s locret_72714
loc_7270A: ; XREF: Snd_FadeOut2
moveq #$28,d0
move.b 1(a5),d1
bra.w sub_7272E
; ===========================================================================
locret_72714:
rts
; End of function sub_726FE
; ===========================================================================
loc_72716: ; XREF: sub_72A5A
btst #2,(a5)
bne.s locret_72720
bra.w sub_72722
; ===========================================================================
locret_72720:
rts
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72722: ; XREF: sub_71E18; sub_72C4E; sub_72CB4
btst #2,1(a5)
bne.s loc_7275A
add.b 1(a5),d0
; End of function sub_72722
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_7272E: ; XREF: loc_71E6A
move.b ($A04000).l,d2
btst #7,d2
bne.s sub_7272E
move.b d0,($A04000).l
nop
nop
nop
loc_72746:
move.b ($A04000).l,d2
btst #7,d2
bne.s loc_72746
move.b d1,($A04001).l
rts
; End of function sub_7272E
; ===========================================================================
loc_7275A: ; XREF: sub_72722
move.b 1(a5),d2
bclr #2,d2
add.b d2,d0
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72764: ; XREF: loc_71E6A; Sound_ChkValue; sub_7256A; sub_72764
move.b ($A04000).l,d2
btst #7,d2
bne.s sub_72764
move.b d0,($A04002).l
nop
nop
nop
loc_7277C:
move.b ($A04000).l,d2
btst #7,d2
bne.s loc_7277C
move.b d1,($A04003).l
rts
; End of function sub_72764
; ===========================================================================
word_72790: dc.w $25E, $284, $2AB, $2D3, $2FE, $32D, $35C, $38F, $3C5
dc.w $3FF, $43C, $47C, $A5E, $A84, $AAB, $AD3, $AFE, $B2D
dc.w $B5C, $B8F, $BC5, $BFF, $C3C, $C7C, $125E, $1284
dc.w $12AB, $12D3, $12FE, $132D, $135C, $138F, $13C5, $13FF
dc.w $143C, $147C, $1A5E, $1A84, $1AAB, $1AD3, $1AFE, $1B2D
dc.w $1B5C, $1B8F, $1BC5, $1BFF, $1C3C, $1C7C, $225E, $2284
dc.w $22AB, $22D3, $22FE, $232D, $235C, $238F, $23C5, $23FF
dc.w $243C, $247C, $2A5E, $2A84, $2AAB, $2AD3, $2AFE, $2B2D
dc.w $2B5C, $2B8F, $2BC5, $2BFF, $2C3C, $2C7C, $325E, $3284
dc.w $32AB, $32D3, $32FE, $332D, $335C, $338F, $33C5, $33FF
dc.w $343C, $347C, $3A5E, $3A84, $3AAB, $3AD3, $3AFE, $3B2D
dc.w $3B5C, $3B8F, $3BC5, $3BFF, $3C3C, $3C7C
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72850: ; XREF: sub_71B4C
subq.b #1,$E(a5)
bne.s loc_72866
bclr #4,(a5)
jsr sub_72878(pc)
jsr sub_728DC(pc)
bra.w loc_7292E
; ===========================================================================
loc_72866:
jsr sub_71D9E(pc)
jsr sub_72926(pc)
jsr sub_71DC6(pc)
jsr sub_728E2(pc)
rts
; End of function sub_72850
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72878: ; XREF: sub_72850
bclr #1,(a5)
movea.l 4(a5),a4
loc_72880:
moveq #0,d5
move.b (a4)+,d5
cmpi.b #$E0,d5
bcs.s loc_72890
jsr sub_72A5A(pc)
bra.s loc_72880
; ===========================================================================
loc_72890:
tst.b d5
bpl.s loc_728A4
jsr sub_728AC(pc)
move.b (a4)+,d5
tst.b d5
bpl.s loc_728A4
subq.w #1,a4
bra.w sub_71D60
; ===========================================================================
loc_728A4:
jsr sub_71D40(pc)
bra.w sub_71D60
; End of function sub_72878
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_728AC: ; XREF: sub_72878
subi.b #$81,d5
bcs.s loc_728CA
add.b 8(a5),d5
andi.w #$7F,d5
lsl.w #1,d5
lea word_729CE(pc),a0
move.w (a0,d5.w),$10(a5)
bra.w sub_71D60
; ===========================================================================
loc_728CA:
bset #1,(a5)
move.w #-1,$10(a5)
jsr sub_71D60(pc)
bra.w sub_729A0
; End of function sub_728AC
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_728DC: ; XREF: sub_72850
move.w $10(a5),d6
bmi.s loc_72920
; End of function sub_728DC
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_728E2: ; XREF: sub_72850
move.b $1E(a5),d0
ext.w d0
add.w d0,d6
btst #2,(a5)
bne.s locret_7291E
btst #1,(a5)
bne.s locret_7291E
move.b 1(a5),d0
cmpi.b #$E0,d0
bne.s loc_72904
move.b #$C0,d0
loc_72904:
move.w d6,d1
andi.b #$F,d1
or.b d1,d0
lsr.w #4,d6
andi.b #$3F,d6
move.b d0,($C00011).l
move.b d6,($C00011).l
locret_7291E:
rts
; End of function sub_728E2
; ===========================================================================
loc_72920: ; XREF: sub_728DC
bset #1,(a5)
rts
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72926: ; XREF: sub_72850
tst.b $B(a5)
beq.w locret_7298A
loc_7292E: ; XREF: sub_72850
move.b 9(a5),d6
moveq #0,d0
move.b $B(a5),d0
beq.s sub_7296A
movea.l (Go_PSGIndex).l,a0
subq.w #1,d0
lsl.w #2,d0
movea.l (a0,d0.w),a0
move.b $C(a5),d0
move.b (a0,d0.w),d0
addq.b #1,$C(a5)
btst #7,d0
beq.s loc_72960
cmpi.b #$80,d0
beq.s loc_7299A
loc_72960:
add.w d0,d6
cmpi.b #$10,d6
bcs.s sub_7296A
moveq #$F,d6
; End of function sub_72926
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_7296A: ; XREF: sub_72504; sub_7267C; sub_72926
btst #1,(a5)
bne.s locret_7298A
btst #2,(a5)
bne.s locret_7298A
btst #4,(a5)
bne.s loc_7298C
loc_7297C:
or.b 1(a5),d6
addi.b #$10,d6
move.b d6,($C00011).l
locret_7298A:
rts
; ===========================================================================
loc_7298C:
tst.b $13(a5)
beq.s loc_7297C
tst.b $12(a5)
bne.s loc_7297C
rts
; End of function sub_7296A
; ===========================================================================
loc_7299A: ; XREF: sub_72926
subq.b #1,$C(a5)
rts
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_729A0: ; XREF: sub_71D9E; Sound_ChkValue; Snd_FadeOut1; sub_728AC
btst #2,(a5)
bne.s locret_729B4
loc_729A6: ; XREF: Snd_FadeOut2
move.b 1(a5),d0
ori.b #$1F,d0
move.b d0,($C00011).l
locret_729B4:
rts
; End of function sub_729A0
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_729B6: ; XREF: loc_71E7C
lea ($C00011).l,a0
move.b #$9F,(a0)
move.b #$BF,(a0)
move.b #$DF,(a0)
move.b #$FF,(a0)
rts
; End of function sub_729B6
; ===========================================================================
word_729CE: dc.w $356, $326, $2F9, $2CE, $2A5, $280, $25C, $23A, $21A
dc.w $1FB, $1DF, $1C4, $1AB, $193, $17D, $167, $153, $140
dc.w $12E, $11D, $10D, $FE, $EF, $E2, $D6, $C9, $BE, $B4
dc.w $A9, $A0, $97, $8F, $87, $7F, $78, $71, $6B, $65
dc.w $5F, $5A, $55, $50, $4B, $47, $43, $40, $3C, $39
dc.w $36, $33, $30, $2D, $2B, $28, $26, $24, $22, $20
dc.w $1F, $1D, $1B, $1A, $18, $17, $16, $15, $13, $12
dc.w $11, 0
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72A5A: ; XREF: sub_71C4E; sub_71CEC; sub_72878
subi.w #$E0,d5
lsl.w #2,d5
jmp loc_72A64(pc,d5.w)
; End of function sub_72A5A
; ===========================================================================
loc_72A64:
bra.w loc_72ACC
; ===========================================================================
bra.w loc_72AEC
; ===========================================================================
bra.w loc_72AF2
; ===========================================================================
bra.w loc_72AF8
; ===========================================================================
bra.w loc_72B14
; ===========================================================================
bra.w loc_72B9E
; ===========================================================================
bra.w loc_72BA4
; ===========================================================================
bra.w loc_72BAE
; ===========================================================================
bra.w loc_72BB4
; ===========================================================================
bra.w loc_72BBE
; ===========================================================================
bra.w loc_72BC6
; ===========================================================================
bra.w loc_72BD0
; ===========================================================================
bra.w loc_72BE6
; ===========================================================================
bra.w loc_72BEE
; ===========================================================================
bra.w loc_72BF4
; ===========================================================================
bra.w loc_72C26
; ===========================================================================
bra.w loc_72D30
; ===========================================================================
bra.w loc_72D52
; ===========================================================================
bra.w loc_72D58
; ===========================================================================
bra.w loc_72E06
; ===========================================================================
bra.w loc_72E20
; ===========================================================================
bra.w loc_72E26
; ===========================================================================
bra.w loc_72E2C
; ===========================================================================
bra.w loc_72E38
; ===========================================================================
bra.w loc_72E52
; ===========================================================================
bra.w loc_72E64
; ===========================================================================
loc_72ACC: ; XREF: loc_72A64
move.b (a4)+,d1
tst.b 1(a5)
bmi.s locret_72AEA
move.b $A(a5),d0
andi.b #$37,d0
or.b d0,d1
move.b d1,$A(a5)
move.b #$B4,d0
bra.w loc_72716
; ===========================================================================
locret_72AEA:
rts
; ===========================================================================
loc_72AEC: ; XREF: loc_72A64
move.b (a4)+,$1E(a5)
rts
; ===========================================================================
loc_72AF2: ; XREF: loc_72A64
move.b (a4)+,7(a6)
rts
; ===========================================================================
loc_72AF8: ; XREF: loc_72A64
moveq #0,d0
move.b $D(a5),d0
movea.l (a5,d0.w),a4
move.l #0,(a5,d0.w)
addq.w #2,a4
addq.b #4,d0
move.b d0,$D(a5)
rts
; ===========================================================================
loc_72B14: ; XREF: loc_72A64
movea.l a6,a0
lea $3A0(a6),a1
move.w #$87,d0
loc_72B1E:
move.l (a1)+,(a0)+
dbf d0,loc_72B1E
bset #2,$40(a6)
movea.l a5,a3
move.b #$28,d6
sub.b $26(a6),d6
moveq #5,d7
lea $70(a6),a5
loc_72B3A:
btst #7,(a5)
beq.s loc_72B5C
bset #1,(a5)
add.b d6,9(a5)
btst #2,(a5)
bne.s loc_72B5C
moveq #0,d0
move.b $B(a5),d0
movea.l $18(a6),a1
jsr sub_72C4E(pc)
loc_72B5C:
adda.w #$30,a5
dbf d7,loc_72B3A
moveq #2,d7
loc_72B66:
btst #7,(a5)
beq.s loc_72B78
bset #1,(a5)
jsr sub_729A0(pc)
add.b d6,9(a5)
loc_72B78:
adda.w #$30,a5
dbf d7,loc_72B66
movea.l a3,a5
move.b #$80,$24(a6)
move.b #$28,$26(a6)
clr.b $27(a6)
move.w #0,($A11100).l
addq.w #8,sp
rts
; ===========================================================================
loc_72B9E: ; XREF: loc_72A64
move.b (a4)+,2(a5)
rts
; ===========================================================================
loc_72BA4: ; XREF: loc_72A64
move.b (a4)+,d0
add.b d0,9(a5)
bra.w sub_72CB4
; ===========================================================================
loc_72BAE: ; XREF: loc_72A64
bset #4,(a5)
rts
; ===========================================================================
loc_72BB4: ; XREF: loc_72A64
move.b (a4),$12(a5)
move.b (a4)+,$13(a5)
rts
; ===========================================================================
loc_72BBE: ; XREF: loc_72A64
move.b (a4)+,d0
add.b d0,8(a5)
rts
; ===========================================================================
loc_72BC6: ; XREF: loc_72A64
move.b (a4),2(a6)
move.b (a4)+,1(a6)
rts
; ===========================================================================
loc_72BD0: ; XREF: loc_72A64
lea $40(a6),a0
move.b (a4)+,d0
moveq #$30,d1
moveq #9,d2
loc_72BDA:
move.b d0,2(a0)
adda.w d1,a0
dbf d2,loc_72BDA
rts
; ===========================================================================
loc_72BE6: ; XREF: loc_72A64
move.b (a4)+,d0
add.b d0,9(a5)
rts
; ===========================================================================
loc_72BEE: ; XREF: loc_72A64
clr.b $2C(a6)
rts
; ===========================================================================
loc_72BF4: ; XREF: loc_72A64
bclr #7,(a5)
bclr #4,(a5)
jsr sub_726FE(pc)
tst.b $250(a6)
bmi.s loc_72C22
movea.l a5,a3
lea $100(a6),a5
movea.l $18(a6),a1
bclr #2,(a5)
bset #1,(a5)
move.b $B(a5),d0
jsr sub_72C4E(pc)
movea.l a3,a5
loc_72C22:
addq.w #8,sp
rts
; ===========================================================================
loc_72C26: ; XREF: loc_72A64
moveq #0,d0
move.b (a4)+,d0
move.b d0,$B(a5)
btst #2,(a5)
bne.w locret_72CAA
movea.l $18(a6),a1
tst.b $E(a6)
beq.s sub_72C4E
movea.l $20(a5),a1
tst.b $E(a6)
bmi.s sub_72C4E
movea.l $20(a6),a1
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72C4E: ; XREF: Snd_FadeOut1; et al
subq.w #1,d0
bmi.s loc_72C5C
move.w #$19,d1
loc_72C56:
adda.w d1,a1
dbf d0,loc_72C56
loc_72C5C:
move.b (a1)+,d1
move.b d1,$1F(a5)
move.b d1,d4
move.b #$B0,d0
jsr sub_72722(pc)
lea byte_72D18(pc),a2
moveq #$13,d3
loc_72C72:
move.b (a2)+,d0
move.b (a1)+,d1
jsr sub_72722(pc)
dbf d3,loc_72C72
moveq #3,d5
andi.w #7,d4
move.b byte_72CAC(pc,d4.w),d4
move.b 9(a5),d3
loc_72C8C:
move.b (a2)+,d0
move.b (a1)+,d1
lsr.b #1,d4
bcc.s loc_72C96
add.b d3,d1
loc_72C96:
jsr sub_72722(pc)
dbf d5,loc_72C8C
move.b #$B4,d0
move.b $A(a5),d1
jsr sub_72722(pc)
locret_72CAA:
rts
; End of function sub_72C4E
; ===========================================================================
byte_72CAC: dc.b 8, 8, 8, 8, $A, $E, $E, $F
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
sub_72CB4: ; XREF: sub_72504; sub_7267C; loc_72BA4
btst #2,(a5)
bne.s locret_72D16
moveq #0,d0
move.b $B(a5),d0
movea.l $18(a6),a1
tst.b $E(a6)
beq.s loc_72CD8
movea.l $20(a6),a1
tst.b $E(a6)
bmi.s loc_72CD8
movea.l $20(a6),a1
loc_72CD8:
subq.w #1,d0
bmi.s loc_72CE6
move.w #$19,d1
loc_72CE0:
adda.w d1,a1
dbf d0,loc_72CE0
loc_72CE6:
adda.w #$15,a1
lea byte_72D2C(pc),a2
move.b $1F(a5),d0
andi.w #7,d0
move.b byte_72CAC(pc,d0.w),d4
move.b 9(a5),d3
bmi.s locret_72D16
moveq #3,d5
loc_72D02:
move.b (a2)+,d0
move.b (a1)+,d1
lsr.b #1,d4
bcc.s loc_72D12
add.b d3,d1
bcs.s loc_72D12
jsr sub_72722(pc)
loc_72D12:
dbf d5,loc_72D02
locret_72D16:
rts
; End of function sub_72CB4
; ===========================================================================
byte_72D18: dc.b $30, $38, $34, $3C, $50, $58, $54, $5C, $60, $68
dc.b $64, $6C, $70, $78, $74, $7C, $80, $88, $84, $8C
byte_72D2C: dc.b $40, $48, $44, $4C
; ===========================================================================
loc_72D30: ; XREF: loc_72A64
bset #3,(a5)
move.l a4,$14(a5)
move.b (a4)+,$18(a5)
move.b (a4)+,$19(a5)
move.b (a4)+,$1A(a5)
move.b (a4)+,d0
lsr.b #1,d0
move.b d0,$1B(a5)
clr.w $1C(a5)
rts
; ===========================================================================
loc_72D52: ; XREF: loc_72A64
bset #3,(a5)
rts
; ===========================================================================
loc_72D58: ; XREF: loc_72A64
bclr #7,(a5)
bclr #4,(a5)
tst.b 1(a5)
bmi.s loc_72D74
tst.b 8(a6)
bmi.w loc_72E02
jsr sub_726FE(pc)
bra.s loc_72D78
; ===========================================================================
loc_72D74:
jsr sub_729A0(pc)
loc_72D78:
tst.b $E(a6)
bpl.w loc_72E02
clr.b 0(a6)
moveq #0,d0
move.b 1(a5),d0
bmi.s loc_72DCC
lea dword_722CC(pc),a0
movea.l a5,a3
cmpi.b #4,d0
bne.s loc_72DA8
tst.b $340(a6)
bpl.s loc_72DA8
lea $340(a6),a5
movea.l $20(a6),a1
bra.s loc_72DB8
; ===========================================================================
loc_72DA8:
subq.b #2,d0
lsl.b #2,d0
movea.l (a0,d0.w),a5
tst.b (a5)
bpl.s loc_72DC8
movea.l $18(a6),a1
loc_72DB8:
bclr #2,(a5)
bset #1,(a5)
move.b $B(a5),d0
jsr sub_72C4E(pc)
loc_72DC8:
movea.l a3,a5
bra.s loc_72E02
; ===========================================================================
loc_72DCC:
lea $370(a6),a0
tst.b (a0)
bpl.s loc_72DE0
cmpi.b #$E0,d0
beq.s loc_72DEA
cmpi.b #$C0,d0
beq.s loc_72DEA
loc_72DE0:
lea dword_722CC(pc),a0
lsr.b #3,d0
movea.l (a0,d0.w),a0
loc_72DEA:
bclr #2,(a0)
bset #1,(a0)
cmpi.b #$E0,1(a0)
bne.s loc_72E02
move.b $1F(a0),($C00011).l
loc_72E02:
addq.w #8,sp
rts
; ===========================================================================
loc_72E06: ; XREF: loc_72A64
move.b #$E0,1(a5)
move.b (a4)+,$1F(a5)
btst #2,(a5)
bne.s locret_72E1E
move.b -1(a4),($C00011).l
locret_72E1E:
rts
; ===========================================================================
loc_72E20: ; XREF: loc_72A64
bclr #3,(a5)
rts
; ===========================================================================
loc_72E26: ; XREF: loc_72A64
move.b (a4)+,$B(a5)
rts
; ===========================================================================
loc_72E2C: ; XREF: loc_72A64
move.b (a4)+,d0
lsl.w #8,d0
move.b (a4)+,d0
adda.w d0,a4
subq.w #1,a4
rts
; ===========================================================================
loc_72E38: ; XREF: loc_72A64
moveq #0,d0
move.b (a4)+,d0
move.b (a4)+,d1
tst.b $24(a5,d0.w)
bne.s loc_72E48
move.b d1,$24(a5,d0.w)
loc_72E48:
subq.b #1,$24(a5,d0.w)
bne.s loc_72E2C
addq.w #2,a4
rts
; ===========================================================================
loc_72E52: ; XREF: loc_72A64
moveq #0,d0
move.b $D(a5),d0
subq.b #4,d0
move.l a4,(a5,d0.w)
move.b d0,$D(a5)
bra.s loc_72E2C
; ===========================================================================
loc_72E64: ; XREF: loc_72A64
move.b #$88,d0
move.b #$F,d1
jsr sub_7272E(pc)
move.b #$8C,d0
move.b #$F,d1
bra.w sub_7272E
; ===========================================================================
Kos_Z80: binclude sound\z80_1.bin
dc.w ((SegaPCM&$FF)<<8)+((SegaPCM&$FF00)>>8)
dc.b $21
dc.w (((EndOfRom-SegaPCM)&$FF)<<8)+(((EndOfRom-SegaPCM)&$FF00)>>8)
binclude sound\z80_2.bin
align 2
Music81: binclude sound\music81.bin
align 2
Music82: binclude sound\music82.bin
align 2
Music83: binclude sound\music83.bin
align 2
Music84: binclude sound\music84.bin
align 2
Music85: binclude sound\music85.bin
align 2
Music86: binclude sound\music86.bin
align 2
Music87: binclude sound\music87.bin
align 2
Music88: binclude sound\music88.bin
align 2
Music89: binclude sound\music89.bin
align 2
Music8A: binclude sound\music8A.bin
align 2
Music8B: binclude sound\music8B.bin
align 2
Music8C: binclude sound\music8C.bin
align 2
Music8D: binclude sound\music8D.bin
align 2
Music8E: binclude sound\music8E.bin
align 2
Music8F: binclude sound\music8F.bin
align 2
Music90: binclude sound\music90.bin
align 2
Music91: binclude sound\music91.bin
align 2
Music92: binclude sound\music92.bin
align 2
Music93: binclude sound\music93.bin
align 2
; ---------------------------------------------------------------------------
; Sound effect pointers
; ---------------------------------------------------------------------------
SoundIndex: dc.l SoundA0, SoundA1, SoundA2
dc.l SoundA3, SoundA4, SoundA5
dc.l SoundA6, SoundA7, SoundA8
dc.l SoundA9, SoundAA, SoundAB
dc.l SoundAC, SoundAD, SoundAE
dc.l SoundAF, SoundB0, SoundB1
dc.l SoundB2, SoundB3, SoundB4
dc.l SoundB5, SoundB6, SoundB7
dc.l SoundB8, SoundB9, SoundBA
dc.l SoundBB, SoundBC, SoundBD
dc.l SoundBE, SoundBF, SoundC0
dc.l SoundC1, SoundC2, SoundC3
dc.l SoundC4, SoundC5, SoundC6
dc.l SoundC7, SoundC8, SoundC9
dc.l SoundCA, SoundCB, SoundCC
dc.l SoundCD, SoundCE, SoundCF
SoundD0Index: dc.l SoundD0
SoundA0: binclude sound\soundA0.bin
align 2
SoundA1: binclude sound\soundA1.bin
align 2
SoundA2: binclude sound\soundA2.bin
align 2
SoundA3: binclude sound\soundA3.bin
align 2
SoundA4: binclude sound\soundA4.bin
align 2
SoundA5: binclude sound\soundA5.bin
align 2
SoundA6: binclude sound\soundA6.bin
align 2
SoundA7: binclude sound\soundA7.bin
align 2
SoundA8: binclude sound\soundA8.bin
align 2
SoundA9: binclude sound\soundA9.bin
align 2
SoundAA: binclude sound\soundAA.bin
align 2
SoundAB: binclude sound\soundAB.bin
align 2
SoundAC: binclude sound\soundAC.bin
align 2
SoundAD: binclude sound\soundAD.bin
align 2
SoundAE: binclude sound\soundAE.bin
align 2
SoundAF: binclude sound\soundAF.bin
align 2
SoundB0: binclude sound\soundB0.bin
align 2
SoundB1: binclude sound\soundB1.bin
align 2
SoundB2: binclude sound\soundB2.bin
align 2
SoundB3: binclude sound\soundB3.bin
align 2
SoundB4: binclude sound\soundB4.bin
align 2
SoundB5: binclude sound\soundB5.bin
align 2
SoundB6: binclude sound\soundB6.bin
align 2
SoundB7: binclude sound\soundB7.bin
align 2
SoundB8: binclude sound\soundB8.bin
align 2
SoundB9: binclude sound\soundB9.bin
align 2
SoundBA: binclude sound\soundBA.bin
align 2
SoundBB: binclude sound\soundBB.bin
align 2
SoundBC: binclude sound\soundBC.bin
align 2
SoundBD: binclude sound\soundBD.bin
align 2
SoundBE: binclude sound\soundBE.bin
align 2
SoundBF: binclude sound\soundBF.bin
align 2
SoundC0: binclude sound\soundC0.bin
align 2
SoundC1: binclude sound\soundC1.bin
align 2
SoundC2: binclude sound\soundC2.bin
align 2
SoundC3: binclude sound\soundC3.bin
align 2
SoundC4: binclude sound\soundC4.bin
align 2
SoundC5: binclude sound\soundC5.bin
align 2
SoundC6: binclude sound\soundC6.bin
align 2
SoundC7: binclude sound\soundC7.bin
align 2
SoundC8: binclude sound\soundC8.bin
align 2
SoundC9: binclude sound\soundC9.bin
align 2
SoundCA: binclude sound\soundCA.bin
align 2
SoundCB: binclude sound\soundCB.bin
align 2
SoundCC: binclude sound\soundCC.bin
align 2
SoundCD: binclude sound\soundCD.bin
align 2
SoundCE: binclude sound\soundCE.bin
align 2
SoundCF: binclude sound\soundCF.bin
align 2
SoundD0: binclude sound\soundD0.bin
align 2
SegaPCM: binclude sound\segapcm.bin
align 2
and replace it completely with:
; ---------------------------------------------------------------------------
align $8000
; ---------------------------------------------------------------------------
; ===========================================================================
; ║ ║
; ║ SONIC&K SOUND DRIVER ║
; ║ Modified SMPS Z80 Type 2 DAC ║
; ║ ║
; ===========================================================================
; Disassembled by MarkeyJester
; Routines, pointers and stuff by Linncaki
; Thoroughly commented and improved (including optional bugfixes) by Flamewing
; ===========================================================================
; Constants
; ===========================================================================
; Set this to 1 to fix some bugs in the driver.
fix_sndbugs = 0
; Function to make a little endian (z80) pointer
k68z80Pointer function addr,((((addr&$7FFF)+$8000)<<8)&$FF00)+(((addr&$7FFF)+$8000)>>8)
little_endian function x,(x)<<8&$FF00|(x)>>8&$FF
startBank macro {INTLABEL}
align $8000
__LABEL__ label *
soundBankStart := __LABEL__
soundBankName := "__LABEL__"
endm
DebugSoundbanks := 0
finishBank macro
if * > soundBankStart + $8000
fatal "soundBank \{soundBankName} must fit in $8000 bytes but was $\{*-soundBankStart}. Try moving something to the other bank."
elseif (DebugSoundbanks<>0)&&(MOMPASS=1)
message "soundBank \{soundBankName} has $\{$8000+soundBankStart-*} bytes free at end."
endif
endm
; macro to declare an entry in an offset table rooted at a bank
offsetBankTableEntry macro ptr
dc.ATTRIBUTE k68z80Pointer(ptr-soundBankStart)
endm
; Special BINCLUDE wrapper function
DACBINCLUDE macro file,{INTLABEL}
__LABEL__ label *
BINCLUDE file
__LABEL___Len := little_endian(*-__LABEL__)
__LABEL___Ptr := k68z80Pointer(__LABEL__-soundBankStart)
__LABEL___Bank := soundBankStart
endm
; Setup macro for DAC samples.
DAC_Setup macro rate,dacptr
dc.b rate
dc.w dacptr_Len
dc.w dacptr_Ptr
endm
; A "null" DAC sample.
DAC_Invalid:
DAC_Invalid_Len := 0
DAC_Invalid_Ptr := 0
DAC_Invalid_Bank := 0
; Setup a null entry for a DAC sample.
DAC_Null_Setup macro rate,dacptr
dc.b rate
dc.w $0000,$0000
endm
; Setup a chain-linked invalid entry for a DAC sample.
; The sample's length is correctly stored for the sample,
; while the pointer (usually) goes towards the DAC pointer
; entry of another DAC sample setup.
DAC_Null_Chain macro rate,dacptr,linkptr
dc.b rate
dc.w dacptr_Len,k68z80Pointer(linkptr+3-soundBankStart)
endm
; ---------------------------------------------------------------------------
z80_SoundDriverStart:
; ---------------------------------------------------------------------------
zTrack STRUCT DOTS
; Playback control bits:
; 0 (01h) Noise channel (PSG) or FM3 special mode (FM)
; 1 (02h) Do not attack next note
; 2 (04h) SFX is overriding this track
; 3 (08h) 'Alternate frequency mode' flag
; 4 (10h) 'Track is resting' flag
; 5 (20h) 'Pitch slide' flag
; 6 (40h) 'Sustain frequency' flag -- prevents frequency from changing again for the lifetime of the track
; 7 (80h) Track is playing
PlaybackControl: ds.b 1 ; S&K: 0
; Voice control bits:
; 0-1 FM channel assignment bits (00 = FM1 or FM4, 01 = FM2 or FM5, 10 = FM3 or FM6/DAC, 11 = invalid)
; 2 (04h) For FM/DAC channels, selects if reg/data writes are bound for part II (set) or part I (unset)
; 3 (08h) Unknown/unused
; 4 (10h) Unknown/unused
; 5-6 PSG Channel assignment bits (00 = PSG1, 01 = PSG2, 10 = PSG3, 11 = Noise)
; 7 (80h) PSG track if set, FM or DAC track otherwise
VoiceControl: ds.b 1 ; S&K: 1
TempoDivider: ds.b 1 ; S&K: 2
DataPointerLow: ds.b 1 ; S&K: 3
DataPointerHigh: ds.b 1 ; S&K: 4
Transpose: ds.b 1 ; S&K: 5
Volume: ds.b 1 ; S&K: 6
ModulationCtrl: ds.b 1 ; S&K: 7 ; Modulation is on if nonzero. If only bit 7 is set, then it is normal modulation; otherwise, this-1 is index on modulation envelope pointer table
VoiceIndex: ds.b 1 ; S&K: 8 ; FM instrument/PSG voice
StackPointer: ds.b 1 ; S&K: 9 ; For call subroutine coordination flag
AMSFMSPan: ds.b 1 ; S&K: 0Ah
DurationTimeout: ds.b 1 ; S&K: 0Bh
SavedDuration: ds.b 1 ; S&K: 0Ch ; Already multiplied by timing divisor
; ---------------------------------
; Alternate names for same offset:
SavedDAC: ; S&K: 0Dh ; For DAC channel
FreqLow: ds.b 1 ; S&K: 0Dh ; For FM/PSG channels
; ---------------------------------
FreqHigh: ds.b 1 ; S&K: 0Eh ; For FM/PSG channels
VoiceSongID: ds.b 1 ; S&K: 0Fh ; For using voices from a different song
Detune: ds.b 1 ; S&K: 10h ; In S&K, some places used 11h instead of 10h
Unk11h: ds.b 1 ; S&K: 11h
ds.b 5 ; S&K: 12h-16h ; Unused
VolEnv: ds.b 1 ; S&K: 17h ; Used for dynamic volume adjustments
; ---------------------------------
; Alternate names for same offsets:
FMVolEnv: ; S&K: 18h
HaveSSGEGFlag: ds.b 1 ; S&K: 18h ; For FM channels, if track has SSG-EG data
FMVolEnvMask: ; S&K: 19h
SSGEGPointerLow: ds.b 1 ; S&K: 19h ; For FM channels, custom SSG-EG data pointer
PSGNoise: ; S&K: 1Ah
SSGEGPointerHigh: ds.b 1 ; S&K: 1Ah ; For FM channels, custom SSG-EG data pointer
; ---------------------------------
FeedbackAlgo: ds.b 1 ; S&K: 1Bh
TLPtrLow: ds.b 1 ; S&K: 1Ch
TLPtrHigh: ds.b 1 ; S&K: 1Dh
NoteFillTimeout: ds.b 1 ; S&K: 1Eh
NoteFillMaster: ds.b 1 ; S&K: 1Fh
ModulationPtrLow: ds.b 1 ; S&K: 20h
ModulationPtrHigh: ds.b 1 ; S&K: 21h
; ---------------------------------
; Alternate names for same offset:
ModulationValLow: ; S&K: 22h
ModEnvSens: ds.b 1 ; S&K: 22h
; ---------------------------------
ModulationValHigh: ds.b 1 ; S&K: 23h
ModulationWait: ds.b 1 ; S&K: 24h
; ---------------------------------
; Alternate names for same offset:
ModulationSpeed: ; S&K: 25h
ModEnvIndex: ds.b 1 ; S&K: 25h
; ---------------------------------
ModulationDelta: ds.b 1 ; S&K: 26h
ModulationSteps: ds.b 1 ; S&K: 27h
LoopCounters: ds.b 2 ; S&K: 28h ; Might overflow into the following data
VoicesLow: ds.b 1 ; S&K: 2Ah ; Low byte of pointer to track's voices, used only if zUpdatingSFX is set
VoicesHigh: ds.b 1 ; S&K: 2Bh ; High byte of pointer to track's voices, used only if zUpdatingSFX is set
Stack_top: ds.b 4 ; S&K: 2Ch-2Fh ; Track stack; can be used by LoopCounters
zTrack ENDSTRUCT
; ---------------------------------------------------------------------------
z80_stack = $2000
z80_stack_end = z80_stack-$60
; equates: standard (for Genesis games) addresses in the memory map
zYM2612_A0 = $4000
zYM2612_D0 = $4001
zYM2612_A1 = $4002
zYM2612_D1 = $4003
zBankRegister = $6000
zPSG = $7F11
zROMWindow = $8000
; ---------------------------------------------------------------------------
; z80 RAM:
if fix_sndbugs
zDataStart = $1BF0
else
zDataStart = $1C00
endif
phase zDataStart
if fix_sndbugs
zSpecFM3Freqs ds.b 8
zSpecFM3FreqsSFX ds.b 8
endif
ds.b 2 ; unused
zPalFlag: ds.b 1
ds.b 1 ; unused
zPalDblUpdCounter: ds.b 1
zSoundQueue0: ds.b 1
zSoundQueue1: ds.b 1
zSoundQueue2: ds.b 1
zTempoSpeedup: ds.b 1
zNextSound: ds.b 1
; The following 3 variables are used for M68K input
zMusicNumber: ds.b 1 ; Play_Sound
zSFXNumber0: ds.b 1 ; Play_Sound_2
zSFXNumber1: ds.b 1 ; Play_Sound_2
shared zMusicNumber,zSFXNumber0,zSFXNumber1
zTempVariablesStart:
zFadeOutTimeout: ds.b 1
zFadeDelay: ds.b 1
zFadeDelayTimeout: ds.b 1
zPauseFlag: ds.b 1
zHaltFlag: ds.b 1
zFM3Settings: ds.b 1 ; Set twice, never read (is read in Z80 Type 1 for YM timer-related purposes)
zTempoAccumulator: ds.b 1
ds.b 1 ; unused
unk_1C15 ds.b 1 ; Set twice, never read
zFadeToPrevFlag: ds.b 1
unk_1C17: ds.b 1 ; Set once, never read
unk_1C18: ds.b 1 ; Set twice, never read
zUpdatingSFX: ds.b 1
ds.b $A ; unused
zCurrentTempo: ds.b 1
zContinuousSFX: ds.b 1
zContinuousSFXFlag: ds.b 1
zSpindashRev: ds.b 1
zRingSpeaker: ds.b 1
zFadeInTimeout: ds.b 1
zVoiceTblPtrSave: ds.b 2 ; For 1-up
zCurrentTempoSave: ds.b 1 ; For 1-up
zSongBankSave: ds.b 1 ; For 1-up
zTempoSpeedupSave: ds.b 1 ; For 1-up
zSpeedupTimeout: ds.b 1
zDACIndex: ds.b 1 ; bit 7 = 1 if playing, 0 if not; remaining 7 bits are index into DAC tables (1-based)
zContSFXLoopCnt: ds.b 1 ; Used as a loop counter for continuous SFX
zSFXSaveIndex: ds.b 1
zSongPosition: ds.b 2
zTrackInitPos: ds.b 2 ; 2 bytes
zVoiceTblPtr: ds.b 2 ; 2 bytes
zSFXVoiceTblPtr: ds.b 2 ; 2 bytes
zSFXTempoDivider: ds.b 1
ds.b 2 ; unused
zSongBank: ds.b 1 ; Bits 15 to 22 of M68K bank address
PlaySegaPCMFlag: ds.b 1
; Now starts song and SFX z80 RAM
; Max number of music channels: 6 FM + 3 PSG or 1 DAC + 5 FM + 3 PSG
zTracksStart:
zSongFM6_DAC: zTrack
zSongFM1: zTrack
zSongFM2: zTrack
zSongFM3: zTrack
zSongFM4: zTrack
zSongFM5: zTrack
zSongPSG1: zTrack
zSongPSG2: zTrack
zSongPSG3: zTrack
zTracksEnd:
; This is RAM for backup of songs (when 1-up jingle is playing)
; and for SFX channels. Note these two overlap.
; Max number of SFX channels: 4 FM + 3 PSG
zTracksSFXStart:
zSFX_FM3: zTrack
zSFX_FM4: zTrack
zSFX_FM5: zTrack
zSFX_FM6: zTrack
zSFX_PSG1: zTrack
zSFX_PSG2: zTrack
zSFX_PSG3: zTrack
zTracksSFXEnd:
phase zTracksSFXStart
zTracksSaveStart:
zSaveSongDAC: zTrack
zSaveSongFM1: zTrack
zSaveSongFM2: zTrack
zSaveSongFM3: zTrack
zSaveSongFM4: zTrack
zSaveSongFM5: zTrack
zSaveSongPSG1: zTrack
zSaveSongPSG2: zTrack
zSaveSongPSG3: zTrack
zTracksSaveEnd:
zTempVariablesEnd:
if * > z80_stack_end ; Don't declare more space than the RAM can contain!
fatal "The RAM variable declarations are too large. It's \{*-z80_stack_end}h bytes past the start of the bottom of the stack, at \{z80_stack_end}h."
endif
dephase
; ---------------------------------------------------------------------------
!org z80_SoundDriverStart ; Rewind the ROM address to where we were earlier (allocating the RAM above messes with it)
; z80_SoundDriver:
Z80_SoundDriver:
org Z80_SoundDriver+Size_of_Snd_driver_guess ; This 'org' inserts some padding that we can paste the compressed sound driver over later (see the 's3p2bin' tool)
save
!org 0 ; z80 Align, handled by the build process
CPU Z80
listing purecode
; ---------------------------------------------------------------------------
NoteRest = 080h
FirstCoordFlag = 0E0h
; ---------------------------------------------------------------------------
if fix_sndbugs
zID_MusicPointers = 0
zID_SFXPointers = 2
zID_ModEnvPointers = 4
zID_VolEnvPointers = 6
else
zID_PriorityList = 0 ; Earlier drivers had this; unused
zID_UniVoiceBank = 2
zID_MusicPointers = 4
zID_SFXPointers = 6
zID_ModEnvPointers = 8
zID_VolEnvPointers = 0Ah
zID_SongLimit = 0Ch ; Earlier drivers had this; unused
endif
; ---------------------------------------------------------------------------
; ===========================================================================
; Macros
; ===========================================================================
bankswitch1 macro
ld hl, zBankRegister
ld (hl), a
rept 7
rrca
ld (hl), a
endm
xor a
ld (hl), a
endm
bankswitch2 macro
ld hl, zBankRegister
ld (hl), a
rept 7
rra
ld (hl), a
endm
xor a
ld (hl), a
endm
bankswitch3 macro
ld b, 8
.bankloop:
ld (zBankRegister), a
rrca
djnz .bankloop
xor a
ld (zBankRegister), a
endm
bankswitchToMusic macro
ld hl, zBankRegister
ld a, (zSongBank)
ld (hl), a
rept 7
rra
ld (hl), a
endm
xor a
ld (hl), a
endm
; macro to make a certain error message clearer should you happen to get it...
rsttarget macro {INTLABEL}
if ($&7)||($>38h)
fatal "Function __LABEL__ is at 0\{$}h, but must be at a multiple of 8 bytes <= 38h to be used with the rst instruction."
endif
if "__LABEL__"<>""
__LABEL__ label $
endif
endm
; function to turn a 68k address into a word the Z80 can use to access it
zmake68kPtr function addr,zROMWindow+(addr&7FFFh)
; function to turn a 68k address into a bank byte
zmake68kBank function addr,(((addr&3F8000h)/zROMWindow))
; ---------------------------------------------------------------------------
; ===========================================================================
; Entry Point
; ===========================================================================
; EntryPoint:
di ; Disable interrupts
di ; Disable interrupts
im 1 ; set interrupt mode 1
jp zInitAudioDriver
; ---------------------------------------------------------------------------
if fix_sndbugs=0
db 0F2h ; Filler; broken jp p,loc?
endif
; =============== S U B R O U T I N E =======================================
;
; Gets the correct pointer to pointer table for the data type in question
; (music, sfx, voices, etc.).
;
; Input: c ID for data type.
; Output: hl Master pointer table for index
; af' Trashed
; b Trashed
; sub_8
align 8
GetPointerTable: rsttarget
if fix_sndbugs
ld hl, z80_SoundDriverPointers ; Load pointer table
else
ld hl, (ptrMasterIndex) ; Read pointer to (pointer to pointer table) table
endif
ld b, 0 ; b = 0
add hl, bc ; Add offset into pointer table
ex af, af' ; Back up af
ld a, (hl) ; Read low byte of pointer into a
inc hl
ld h, (hl) ; Read high byte of pointer into h
ld l, a ; Put low byte of pointer into l
ex af, af' ; Restore af
ret
; End of function GetPointerTable
; ---------------------------------------------------------------------------
if fix_sndbugs=0
;word_15
ptrMasterIndex:
dw z80_SoundDriverPointers
endif
; =============== S U B R O U T I N E =======================================
;
; Reads an offset into a pointer table and returns dereferenced pointer.
;
;
; Input: a Index into pointer table
; hl Pointer to pointer table
; Output: hl Selected pointer in pointer table
; bc Trashed
; sub_18
align 8
PointerTableOffset: rsttarget
ld c, a ; c = index into pointer table
ld b, 0 ; b = 0
add hl, bc ; hl += bc
add hl, bc ; hl += bc
if fix_sndbugs
jp ReadPointer ; 10 clock cycles, 3 bytes
else
nop ; 12 clock cycles, 3 bytes
nop
nop
endif
; End of function PointerTableOffset
; =============== S U B R O U T I N E =======================================
;
; Dereferences a pointer.
;
; Input: hl Pointer
; output: hl Equal to what that was being pointed to by hl
; loc_20
align 8
ReadPointer: rsttarget
ld a, (hl) ; Read low byte of pointer into a
inc hl
ld h, (hl) ; Read high byte of pointer into h
ld l, a ; Put low byte of pointer into l
ret
; End of function PointerTableOffset
; ---------------------------------------------------------------------------
; There is room for two more 'rsttarget's here
; ---------------------------------------------------------------------------
align 38h
; =============== S U B R O U T I N E =======================================
;
; This subroutine is called every V-Int. After it is processed, the z80
; returns to the digital audio loop to continue playing DAC samples.
;
; If the SEGA PCM is being played, it disables interrupts -- this means that
; this procedure will NOT be called while the SEGA PCM is playing.
;
;zsub_38
zVInt: rsttarget
di ; Disable interrupts
push af ; Save af
push iy ; Save iy
exx ; Save bc,de,hl
.doupdate:
if fix_sndbugs=0
ld a, r ; Get memory refresh register
ld (unk_1C17), a ; Save it
endif
call zUpdateEverything ; Update all tracks
ld a, (zPalFlag) ; Get PAL flag
or a ; Is it set?
jr z, .not_pal ; Branch if not (NTSC)
ld a, (zPalDblUpdCounter) ; Get PAL double-update timeout counter
or a ; Is it zero?
jr nz, .pal_timer ; Branch if not
ld a, 5 ; Set it back to 5...
ld (zPalDblUpdCounter), a ; ... and save it
jp .doupdate ; Go again
.pal_timer:
dec a ; Decrease PAL double-update timeout counter
ld (zPalDblUpdCounter), a ; Store it
.not_pal:
ld a, (zDACIndex) ; Get index of playing DAC sample
and 7Fh ; Strip 'DAC playing' bit
ld c, a ; c = a
ld b, 0 ; Sign extend c to bc
ld hl, DAC_Banks ; Make hl point to DAC bank table
add hl, bc ; Offset into entry for current sample
ld a, (hl) ; Get bank index
bankswitch1 ; Switch to current DAC sample's bank
exx ; Restore bc,de,hl
pop iy ; Restore iy
pop af ; Restore af
ld b, 1 ; b = 1
ret
; ---------------------------------------------------------------------------
;loc_85
zInitAudioDriver:
ld sp, z80_stack ; set the stack pointer to 0x2000 (end of z80 RAM)
; The following instruction block keeps the z80 in a tight loop.
ld c, 0 ; c = 0
.loop:
ld b, 0 ; b = 0
djnz $ ; Loop in this instruction, decrementing b each iteration, until b = 0
dec c ; c--
jr z, .loop ; Loop if c = 0
call zStopAllSound ; Stop all music
ld a, zmake68kBank(Snd_Bank2_Start) ; Set song bank to second music bank (default value)
ld (zSongBank), a ; Store it
xor a ; a = 0
ld (zSpindashRev), a ; Reset spindash rev
ld (zDACIndex), a ; Clear current DAC sample index
ld (PlaySegaPCMFlag), a ; Clear the Sega sound flag
ld (zRingSpeaker), a ; Make rings play on left speaker
ld a, 5 ; Set PAL double-update counter to 5
ld (zPalDblUpdCounter), a ; (that is, do not double-update for 5 frames)
ei ; Enable interrupts
jp zPlayDigitalAudio ; Start digital audio loop
; =============== S U B R O U T I N E =======================================
;
; Writes a reg/data pair to part I or II
;
; Input: a Value for register
; c Value for data
; ix Pointer to track RAM
;sub_AF
zWriteFMIorII:
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
ret nz ; Is so, quit
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding this track?
ret nz ; Return if yes
add a, (ix+zTrack.VoiceControl) ; Add the channel bits to the register address
bit 2, (ix+zTrack.VoiceControl) ; Is this the DAC channel or FM4 or FM5 or FM6?
jr nz, zWriteFMII_reduced ; If yes, write reg/data pair to part II;
; otherwise, write reg/data pair as is to part I.
; End of function zWriteFMIorII
; =============== S U B R O U T I N E =======================================
;
; Writes a reg/data pair to part I
;
; Input: a Value for register
; c Value for data
;sub_C2
zWriteFMI:
ld (zYM2612_A0), a ; Select YM2612 register
if fix_sndbugs=0
; Pointless, since there's no need to delay between writing to the address port and the data port
nop ; Wait
endif
ld a, c ; a = data to send
ld (zYM2612_D0), a ; Send data to register
ret
; End of function zWriteFMI
; ---------------------------------------------------------------------------
;loc_CB
zWriteFMII_reduced:
sub 4 ; Strip 'bound to part II regs' bit
; ---------------------------------------------------------------------------
; =============== S U B R O U T I N E =======================================
;
; Writes a reg/data pair to part II
;
; Input: a Value for register
; c Value for data
;sub_CD
zWriteFMII:
ld (zYM2612_A1), a ; Select YM2612 register
if fix_sndbugs=0
; Pointless, since there's no need to delay between writing to the address port and the data port
nop ; Wait
endif
ld a, c ; a = data to send
ld (zYM2612_D1), a ; Send data to register
ret
; End of function zWriteFMII
; ---------------------------------------------------------------------------
; ===========================================================================
; DAC BANKS
; ===========================================================================
; Note: this table has a dummy first entry for the case when there is no DAC
; sample being played -- the code still results in a valid bank switch, and
; does not need to worry about special cases.
DAC_Banks:
db zmake68kBank(DacBank1) ,zmake68kBank(DAC_81_Data) ,zmake68kBank(DAC_82_83_84_85_Data) ,zmake68kBank(DAC_82_83_84_85_Data)
db zmake68kBank(DAC_82_83_84_85_Data) ,zmake68kBank(DAC_82_83_84_85_Data) ,zmake68kBank(DAC_86_Data) ,zmake68kBank(DAC_87_Data)
db zmake68kBank(DAC_88_Data) ,zmake68kBank(DAC_89_Data) ,zmake68kBank(DAC_8A_8B_Data) ,zmake68kBank(DAC_8A_8B_Data)
db zmake68kBank(DAC_8C_Data) ,zmake68kBank(DAC_8D_8E_Data) ,zmake68kBank(DAC_8D_8E_Data) ,zmake68kBank(DAC_8F_Data)
db zmake68kBank(DAC_90_91_92_93_Data) ,zmake68kBank(DAC_90_91_92_93_Data) ,zmake68kBank(DAC_90_91_92_93_Data) ,zmake68kBank(DAC_90_91_92_93_Data)
db zmake68kBank(DAC_94_95_96_97_Data) ,zmake68kBank(DAC_94_95_96_97_Data) ,zmake68kBank(DAC_94_95_96_97_Data) ,zmake68kBank(DAC_94_95_96_97_Data)
db zmake68kBank(DAC_98_99_9A_Data) ,zmake68kBank(DAC_98_99_9A_Data) ,zmake68kBank(DAC_98_99_9A_Data) ,zmake68kBank(DAC_9B_Data)
db zmake68kBank(DAC_9C_Data) ,zmake68kBank(DAC_9D_Data) ,zmake68kBank(DAC_9E_Data) ,zmake68kBank(DAC_9F_Data)
db zmake68kBank(DAC_A0_Data) ,zmake68kBank(DAC_A1_Data) ,zmake68kBank(DAC_A2_Data) ,zmake68kBank(DAC_A3_Data)
db zmake68kBank(DAC_A4_Data) ,zmake68kBank(DAC_A5_Data) ,zmake68kBank(DAC_A6_Data) ,zmake68kBank(DAC_A7_Data)
db zmake68kBank(DAC_A8_Data) ,zmake68kBank(DAC_A9_Data) ,zmake68kBank(DAC_AA_Data) ,zmake68kBank(DAC_AB_Data)
db zmake68kBank(DAC_AC_Data) ,zmake68kBank(DAC_AD_AE_Data) ,zmake68kBank(DAC_AD_AE_Data) ,zmake68kBank(DAC_AF_Data)
db zmake68kBank(DAC_AF_Data) ,zmake68kBank(DAC_B1_Data) ,zmake68kBank(DAC_B2_B3_Data) ,zmake68kBank(DAC_B2_B3_Data)
db zmake68kBank(DAC_B4_C1_C2_C3_C4_Data),zmake68kBank(DAC_B5_Data) ,zmake68kBank(DAC_B6_Data) ,zmake68kBank(DAC_B7_Data)
db zmake68kBank(DAC_B8_B9_Data) ,zmake68kBank(DAC_B8_B9_Data) ,zmake68kBank(DAC_BA_Data) ,zmake68kBank(DAC_BB_Data)
db zmake68kBank(DAC_BC_Data) ,zmake68kBank(DAC_BD_Data) ,zmake68kBank(DAC_BE_Data) ,zmake68kBank(DAC_BF_Data)
db zmake68kBank(DAC_C0_Data) ,zmake68kBank(DAC_B4_C1_C2_C3_C4_Data),zmake68kBank(DAC_B4_C1_C2_C3_C4_Data),zmake68kBank(DAC_B4_C1_C2_C3_C4_Data)
db zmake68kBank(DAC_B4_C1_C2_C3_C4_Data)
; =============== S U B R O U T I N E =======================================
;
;sub_11B
zUpdateEverything:
call zPauseUnpause ; Pause/unpause according to M68K input
call zUpdateSFXTracks ; Do SFX tracks
;loc_121
zUpdateMusic:
call TempoWait ; Delay song tracks as appropriate for main tempo mod
call zDoMusicFadeOut ; Check if music should be faded out and fade if needed
call zDoMusicFadeIn ; Check if music should be faded in and fade if needed
ld a, (zFadeToPrevFlag) ; Get fade-to-prev flag
cp mus_ExtraLife-Mus__First ; Is it still 1-Up?
jr nz, .check_fade_in ; Branch if not
ld a, (zMusicNumber) ; Get next music to play
cp mus_ExtraLife ; Is it another 1-Up?
jr z, .clr_queue ; Branch if yes
cp Mus__End-Mus__First ; Is it music (except credits song)?
jr c, .clr_sfx ; Branch if not
.clr_queue:
xor a ; a = 0
ld (zMusicNumber), a ; Clear queue entry
.clr_sfx:
xor a ; a = 0
ld (zSFXNumber0), a ; Clear first queued SFX
ld (zSFXNumber1), a ; Clear second queued SFX
jr .update_music
;loc_149
.check_fade_in:
ld a, (zFadeToPrevFlag) ; Get fade-to-previous flag
cp 0FFh ; Is it 0FFh?
jr z, .update_music ; Branch if yes
ld hl, zMusicNumber ; Point hl to M68K input
ld e, (hl) ; e = next song to play
inc hl ; Advance pointer
ld d, (hl) ; d = next SFX to play
inc hl ; Advance pointer
ld a, (hl) ; a = next SFX to play
or d ; Combine bits of a and d
or e ; Is anything in the play queue?
jr z, .update_music ; Branch if not
call zFillSoundQueue ; Transfer M68K input
call zCycleSoundQueue ; Cycle queue and play first entry
call zCycleSoundQueue ; Cycle queue and play second entry
call zCycleSoundQueue ; Cycle queue and play third entry
.update_music:
ld a, (zSongBank) ; Get bank ID for music
bankswitch2 ; Bank switch to it
xor a ; a = 0
ld (zUpdatingSFX), a ; Updating music
ld a, (zFadeToPrevFlag) ; Get fade-to-previous flag
cp 0FFh ; Is it 0FFh?
call z, zFadeInToPrevious ; Fade to previous if yes
ld ix, zSongFM6_DAC ; ix = FM6/DAC track RAM
bit 7, (ix+zTrack.PlaybackControl) ; Is FM6/DAC track playing?
call nz, zUpdateDACTrack ; Branch if yes
ld b, (zTracksEnd-zSongFM1)/zTrack.len ; Number of tracks
ld ix, zSongFM1 ; ix = FM1 track RAM
jr zTrackUpdLoop ; Play all tracks
; =============== S U B R O U T I N E =======================================
;
;sub_19E
zUpdateSFXTracks:
ld a, 1 ; a = 1
ld (zUpdatingSFX), a ; Updating SFX
ld a, zmake68kBank(SndBank) ; Get SFX bank ID
bankswitch2 ; Bank switch to SFX
ld ix, zTracksSFXStart ; ix = start of SFX track RAM
ld b, (zTracksSFXEnd-zTracksSFXStart)/zTrack.len ; Number of channels
zTrackUpdLoop:
push bc ; Save bc
bit 7, (ix+zTrack.PlaybackControl) ; Is track playing?
call nz, zUpdateFMorPSGTrack ; Call routine if yes
ld de, zTrack.len ; Spacing between tracks
add ix, de ; Advance to next track
pop bc ; Restore bc
djnz zTrackUpdLoop ; Loop for all tracks
ld a, (zTempoSpeedup) ; Get tempo speed-up value
or a ; Is music sped up?
ret z ; Return if not
ld a, (zSpeedupTimeout) ; Get extra tempo timeout
or a ; Has it expired?
jp nz, .no_dbl_update ; Branch if not
ld a, (zTempoSpeedup) ; Get master tempo speed-up value
ld (zSpeedupTimeout), a ; Reset extra tempo timeout to it
jp zUpdateMusic ; Update music again
; ---------------------------------------------------------------------------
.no_dbl_update:
dec a ; Decrement timeout...
ld (zSpeedupTimeout), a ; ... then store new value
ret
; End of function zUpdateSFXTracks
; =============== S U B R O U T I N E =======================================
; Updates FM or PSG track.
;
;sub_1E9
zUpdateFMorPSGTrack:
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG channel?
jp nz, zUpdatePSGTrack ; Branch if yes
if fix_sndbugs
dec (ix+zTrack.DurationTimeout) ; Run note timer
else
call zTrackRunTimer ; Run note timer
endif
jr nz, .note_going ; Branch if note hasn't expired yet
call zGetNextNote ; Get next note for FM track
bit 4, (ix+zTrack.PlaybackControl) ; Is track resting?
ret nz ; Return if yes
call zPrepareModulation ; Initialize modulation
call zUpdateFreq ; Add frequency displacement to frequency
call zDoModulation ; Apply modulation
call zFMSendFreq ; Send frequency to YM2612
jp zFMNoteOn ; Note on on all operators
; ---------------------------------------------------------------------------
.note_going:
bit 4, (ix+zTrack.PlaybackControl) ; Is track resting?
ret nz ; Return if yes
call zDoFMVolEnv ; Do FM volume envelope effects for track
ld a, (ix+zTrack.NoteFillTimeout) ; Get note fill timeout
or a ; Is timeout either not running or expired?
jr z, .keep_going ; Branch if yes
dec (ix+zTrack.NoteFillTimeout) ; Update note fill timeout
jp z, zKeyOffIfActive ; Send key off if needed
.keep_going:
call zUpdateFreq ; Add frequency displacement to frequency
bit 6, (ix+zTrack.PlaybackControl) ; Is 'sustain frequency' bit set?
ret nz ; Return if yes
call zDoModulation ; Apply modulation then fall through
; Fall through to next function
; End of function zUpdateFMorPSGTrack
; =============== S U B R O U T I N E =======================================
; Uploads track's frequency to YM2612.
;
; Input: ix Pointer to track RAM
; hl Frequency to upload
; de For FM3 in special mode, pointer to extra FM3 frequency data (never correctly set)
; Output: a Trashed
; bc Trashed
; hl Trashed
; de Increased by 8
;
;sub_22B
zFMSendFreq:
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding this track?
ret nz ; Return if yes
bit 0, (ix+zTrack.PlaybackControl) ; Is track in special mode (FM3 only)?
jp nz, .special_mode ; Branch if yes
.not_fm3:
ld a, 0A4h ; Command to update frequency MSB
ld c, h ; High byte of frequency
call zWriteFMIorII ; Send it to YM2612
ld a, 0A0h ; Command to update frequency LSB
ld c, l ; Low byte of frequency
if fix_sndbugs
jp zWriteFMIorII ; Send it to YM2612
else
call zWriteFMIorII ; Send it to YM2612
ret
endif
; ---------------------------------------------------------------------------
.special_mode:
ld a, (ix+zTrack.VoiceControl) ; a = voice control byte
cp 2 ; Is this FM3?
jr nz, .not_fm3 ; Branch if not
if fix_sndbugs
call zGetSpecialFM3DataPointer ; de = pointer to saved FM3 frequency shifts
endif
ld b, zSpecialFreqCommands_End-zSpecialFreqCommands ; Number of entries
ld hl, zSpecialFreqCommands ; Lookup table
; DANGER! de is unset here, and could be pointing anywhere! Luckily,
; only reads are performed from it.
.loop:
push bc ; Save bc
ld a, (hl) ; a = register selector
inc hl ; Advance pointer
push hl ; Save hl
ex de, hl ; Exchange de and hl
ld c, (hl) ; Get byte from whatever the hell de was pointing to
inc hl ; Advance pointer
ld b, (hl) ; Get byte from whatever the hell de was pointing to
inc hl ; Advance pointer
ex de, hl ; Exchange de and hl
ld l, (ix+zTrack.FreqLow) ; l = low byte of track frequency
ld h, (ix+zTrack.FreqHigh) ; h = high byte of track frequency
add hl, bc ; hl = full frequency for operator
push af ; Save af
ld c, h ; High byte of frequency
call zWriteFMI ; Sent it to YM2612
pop af ; Restore af
sub 4 ; Move on to frequency LSB
ld c, l ; Low byte of frequency
call zWriteFMI ; Sent it to YM2612
pop hl ; Restore hl
pop bc ; Restore bc
djnz .loop ; Loop for all operators
ret
; End of function zFMSendFreq
; ---------------------------------------------------------------------------
;loc_272
zSpecialFreqCommands:
db 0ADh ; Operator 4 frequency MSB
db 0AEh ; Operator 3 frequency MSB
db 0ACh ; Operator 2 frequency MSB
db 0A6h ; Operator 1 frequency MSB
zSpecialFreqCommands_End
; =============== S U B R O U T I N E =======================================
;
zGetSpecialFM3DataPointer:
if fix_sndbugs
ld de, zSpecFM3Freqs ; de = pointer to saved FM3 frequency shifts
ld a, (zUpdatingSFX) ; Get flag
or a ; Is this a SFX track?
ret z ; Return if not
ld de, zSpecFM3FreqsSFX ; de = pointer to saved FM3 frequency shifts
endif
ret
; End of function zGetSpecialFM3DataPointer
; =============== S U B R O U T I N E =======================================
; Gets next note from the track's data stream. If any coordination flags are
; found, they are handled and then the function keeps looping until a note is
; found.
;
; Input: ix Pointer to track's RAM
; Output: de Pointer to current position on track data
; hl Note frequency
; All others possibly trashed due to coordination flags
;
;sub_277
zGetNextNote:
ld e, (ix+zTrack.DataPointerLow) ; e = low byte of track data pointer
ld d, (ix+zTrack.DataPointerHigh) ; d = high byte of track data pointer
res 1, (ix+zTrack.PlaybackControl) ; Clear 'do not attack next note' flag
res 4, (ix+zTrack.PlaybackControl) ; Clear 'track is at rest' flag
;loc_285
zGetNextNote_cont:
ld a, (de) ; Get next byte from track
inc de ; Advance pointer
cp FirstCoordFlag ; Is it a coordination flag?
jp nc, zHandleFMorPSGCoordFlag ; Branch if yes
ex af, af' ; Save af
call zKeyOffIfActive ; Kill note
ex af, af' ; Restore af
bit 3, (ix+zTrack.PlaybackControl) ; Is alternate frequency mode flag set?
jp nz, zAltFreqMode ; Branch if yes
or a ; Is this a duration?
jp p, zStoreDuration ; Branch if yes
sub 81h ; Make the note into a 0-based index
jp p, .got_note ; Branch if it is a note and not a rest
call zRestTrack ; Put track at rest
jr zGetNoteDuration
; ---------------------------------------------------------------------------
.got_note:
add a, (ix+zTrack.Transpose) ; Add in transposition
ld hl, zPSGFrequencies ; PSG frequency lookup table
push af ; Save af
rst PointerTableOffset ; hl = frequency value for note
pop af ; Restore af
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
jr nz, zGotNoteFreq ; Branch if yes
push de ; Save de
ld d, 8 ; Each octave above the first adds this to frequency high bits
ld e, 0Ch ; 12 notes per octave
ex af, af' ; Exchange af with af'
xor a ; Clear a (which will clear a')
.loop:
ex af, af' ; Exchange af with af'
sub e ; Subtract 1 octave from the note
jr c, .got_displacement ; If this is less than zero, we are done
ex af, af' ; Exchange af with af'
add a, d ; One octave up
jr .loop ; Loop
; ---------------------------------------------------------------------------
if fix_sndbugs=0
ex af, af' ; Exchange af with af' (dead code)
endif
; ---------------------------------------------------------------------------
.got_displacement:
add a, e ; Add 1 octave back (so note index is positive)
ld hl, zFMFrequencies ; FM first octave frequency lookup table
rst PointerTableOffset ; hl = frequency of the note on the first octave
ex af, af' ; Exchange af with af'
or h ; a = high bits of frequency (including octave bits, which were in a)
ld h, a ; h = high bits of frequency (including octave bits)
pop de ; Restore de
;loc_2CE
zGotNoteFreq:
ld (ix+zTrack.FreqLow), l ; Store low byte of note frequency
ld (ix+zTrack.FreqHigh), h ; Store high byte of note frequency
;loc_2D4
zGetNoteDuration:
ld a, (de) ; Get duration from the track
or a ; Is it an actual duration?
jp p, zGotNoteDuration ; Branch if yes
if fix_sndbugs=0
ld a, (ix+zTrack.SavedDuration) ; Get saved duration (zFinishTrackUpdate should do it...)
ld (ix+zTrack.DurationTimeout), a ; Set it as next timeout duration (zFinishTrackUpdate should do it...)
endif
jr zFinishTrackUpdate
; ---------------------------------------------------------------------------
zApplyPitchSlide:
if fix_sndbugs=0
; Unused/dead code in S3/S&K/S3D; this is code for pitch slides
; in the Battletoads sound driver.
ld a, (de) ; Get new pitch slide value from track
inc de ; Advance pointer
ld (ix+zTrack.Unk11h), a ; Store pitch slide
jr zGetRawDuration
endif
; ---------------------------------------------------------------------------
;loc_2E8
;zAlternateSMPS
zAltFreqMode:
; Setting bit 3 on zTrack.PlaybackControl puts the song in a weird mode.
;
; This weird mode has literal frequencies and durations on the track.
; Each byte on the track is either a coordination flag (0E0h to 0FFh) or
; the high byte of a frequency. For the latter case, the following byte
; is then the low byte of this same frequency.
; If the frequency is nonzero, the (sign extended) key displacement is
; simply *added* to this frequency.
; After the frequency, there is then a byte that is unused.
; Finally, there is a raw duration byte following.
;
; To put the track in this mode, coord. flag 0FDh can be used; if the
; parameter byte is 1, the mode is toggled on. To turn the mode off,
; coord. flag 0FDh can be used with a parameter != 1.
ld h, a ; h = byte from track
ld a, (de) ; a = next byte from track
inc de ; Advance pointer
ld l, a ; l = last byte read from track
or h ; Is hl nonzero?
jr z, .got_zero ; Branch if not
ld a, (ix+zTrack.Transpose) ; a = transposition
if fix_sndbugs
ld c, a ; bc = sign extension of key displacement
rla ; Carry contains sign of key displacement
sbc a, a ; a = 0 or -1 if carry is 0 or 1
ld b, a ; bc = sign extension of key displacement
else
ld b, 0 ; b = 0
or a ; Is a negative?
jp p, .did_sign_extend ; Branch if not
dec b ; Set b to -1
.did_sign_extend:
ld c, a ; bc = sign extension of key displacement
endif
add hl, bc ; hl += key displacement
.got_zero:
ld (ix+zTrack.FreqLow), l ; Store low byte of note frequency
ld (ix+zTrack.FreqHigh), h ; Store high byte of note frequency
if fix_sndbugs
inc de ; Skip the useless pitch slide byte
else
ld a, (de) ; Get pitch slide value from the track
inc de ; Advance to next byte in track
ld (ix+zTrack.Unk11h), a ; Store pitch slide
endif
;loc_306
zGetRawDuration:
ld a, (de) ; Get raw duration from track
;loc_307
zGotNoteDuration:
inc de ; Advance to next byte in track
;loc_308
zStoreDuration:
call zComputeNoteDuration ; Multiply note by tempo divider
ld (ix+zTrack.SavedDuration), a ; Store it for next note
;loc_30E
zFinishTrackUpdate:
ld (ix+zTrack.DataPointerLow), e ; Save low byte of current location in song
ld (ix+zTrack.DataPointerHigh), d ; Save high byte of current location in song
ld a, (ix+zTrack.SavedDuration) ; Get current saved duration
ld (ix+zTrack.DurationTimeout), a ; Set it as duration timeout
bit 1, (ix+zTrack.PlaybackControl) ; Is 'do not attack next note' flag set?
ret nz ; Return if yes
xor a ; Clear a
ld (ix+zTrack.ModulationSpeed), a ; Clear modulation speed
ld (ix+zTrack.ModulationValLow), a ; Clear low byte of accumulated modulation
ld (ix+zTrack.VolEnv), a ; Reset volume envelope
ld a, (ix+zTrack.NoteFillMaster) ; Get master note fill
ld (ix+zTrack.NoteFillTimeout), a ; Set note fill timeout
ret
; End of function zGetNextNote
; =============== S U B R O U T I N E =======================================
; This routine multiplies the note duration by the tempo divider. This can
; easily overflow, as the result is stored in a byte.
;
; Input: a Note duration
; Output: a Final note duration
; b zero
; c Damaged
;sub_330
zComputeNoteDuration:
ld b, (ix+zTrack.TempoDivider) ; Get tempo divider for this track
dec b ; Make it into a loop counter
ret z ; Return if it was 1
ld c, a ; c = a
.loop:
add a, c ; a += c
djnz .loop ; Loop
ret
; End of function zComputeNoteDuration
; =============== S U B R O U T I N E =======================================
; Reduces note duration timeout for current track.
;
; Input: ix Track data
; Output: a New duration
;sub_33A
if fix_sndbugs=0
zTrackRunTimer:
ld a, (ix+zTrack.DurationTimeout) ; Get track duration timeout
dec a ; Decrement it...
ld (ix+zTrack.DurationTimeout), a ; ... and save new value
ret
; End of function zTrackRunTimer
endif
; ---------------------------------------------------------------------------
;loc_342
zFMNoteOn:
ld a, (ix+zTrack.FreqLow) ; Get low byte of note frequency
or (ix+zTrack.FreqHigh) ; Is the note frequency zero?
ret z ; Return if yes
ld a, (ix+zTrack.PlaybackControl) ; Get playback control byte for track
if fix_sndbugs
and 16h ; Is either bit 4 ("track at rest") or 2 ("SFX overriding this track") or bit 1 ("do not attack next note") set?
else
and 6 ; Is either bit 1 ("do not attack next note") or 2 ("SFX overriding this track") set?
endif
ret nz ; Return if yes
ld a, (ix+zTrack.VoiceControl) ; Get voice control byte from track
or 0F0h ; We want only the FM channel assignment bits
ld c, a ; Key on for all operators
ld a, 28h ; Select key on/of register
if fix_sndbugs
jp zWriteFMI ; Send command to YM2612
else
call zWriteFMI ; Send command to YM2612
ret
endif
; ---------------------------------------------------------------------------
; =============== S U B R O U T I N E =======================================
; Writes reg/data pair to register 28h (key on/off) if track active
;
; Input: ix Track data
; Output: a Damaged
; c Damaged
;sub_35B
zKeyOffIfActive:
ld a, (ix+zTrack.PlaybackControl) ; Get playback control byte for track
and 6 ; Is either bit 1 ("do not attack next note") or 2 ("SFX overriding this track") set?
ret nz ; Return if yes
; End of function zKeyOffIfActive
; =============== S U B R O U T I N E =======================================
; Writes reg/data pair to register 28h (key on/off)
;
; Input: ix Track data
; Output: a Damaged
; c Damaged
;loc_361
zKeyOff:
ld c, (ix+zTrack.VoiceControl) ; Get voice control byte for track (this will turn off all operators as high nibble = 0)
bit 7, c ; Is this a PSG track?
ret nz ; Return if yes
; End of function zKeyOff
; =============== S U B R O U T I N E =======================================
; Writes reg/data pair to register 28h (key on/off)
;
; Input: c Data to write
; Output: a Damaged
;loc_367
zKeyOnOff:
ld a, 28h ; Write to KEY ON/OFF port
if fix_sndbugs
res 6, (ix+zTrack.PlaybackControl) ; From Dyna Brothers 2, but in a better place; clear flag to sustain frequency
jp zWriteFMI ; Send it
else
call zWriteFMI ; Send it
ret
endif
; End of function zKeyOnOff
; =============== S U B R O U T I N E =======================================
; Performs volume envelope effects in FM channels.
;
; Input: ix Pointer to track RAM
; Output: a Trashed
; bc Trashed
; de Trashed
; hl Trashed
;
;sub_36D
;zDoFMFlutter
zDoFMVolEnv:
ld a, (ix+zTrack.FMVolEnv) ; Get FM volume envelope
or a ; Is it zero?
ret z ; Return if yes
ret m ; Return if it is actually the custom SSG-EG flag
dec a ; Make a into an index
ld c, zID_VolEnvPointers ; Value for volume envelope pointer table
rst GetPointerTable ; hl = pointer to volume envelope table
rst PointerTableOffset ; hl = pointer to volume envelope for track
call zDoVolEnv ; a = new volume envelope
ld h, (ix+zTrack.TLPtrHigh) ; h = high byte to TL data pointer
ld l, (ix+zTrack.TLPtrLow) ; l = low byte to TL data pointer
ld de, zFMInstrumentTLTable ; de = pointer to FM TL register table
ld b, zFMInstrumentTLTable_End-zFMInstrumentTLTable ; Number of entries
ld c, (ix+zTrack.FMVolEnvMask) ; c = envelope bitmask
.loop:
push af ; Save af
sra c ; Divide c by 2
push bc ; Save bc
jr nc, .skip_reg ; Branch if c bit shifted was zero
add a, (hl) ; Add TL value to volume envelope
and 7Fh ; Strip sign bit
ld c, a ; c = TL + volume envelope
ld a, (de) ; a = YM2612 register
call zWriteFMIorII ; Send TL data to YM2612
.skip_reg:
pop bc ; Restore bc
inc de ; Advance to next YM2612 register
inc hl ; Advance to next TL value
pop af ; Restore af
djnz .loop ; Loop for all registers
ret
; End of function zDoFMVolEnv
; =============== S U B R O U T I N E =======================================
; Initializes normal modulation.
;
; Input: ix Pointer to track's RAM
; Output: de If modulation control has bit 7 set and track is to attack next note, pointer to modulation steps in track RAM
; hl If modulation control has bit 7 set and track is to attack next note, pointer to modulation steps in track data
;
;sub_39E
zPrepareModulation:
bit 7, (ix+zTrack.ModulationCtrl) ; Is modulation on?
ret z ; Return if not
bit 1, (ix+zTrack.PlaybackControl) ; Is 'do not attack next note' bit set?
ret nz ; Return if yes
ld e, (ix+zTrack.ModulationPtrLow) ; e = low byte of pointer to modulation data
ld d, (ix+zTrack.ModulationPtrHigh) ; d = high byte of pointer to modulation data
push ix ; Save ix
pop hl ; hl = pointer to track data
ld b, 0 ; b = 0
ld c, zTrack.ModulationWait ; c = offset in track RAM for modulation data
add hl, bc ; hl = pointer to modulation data in track RAM
ex de, hl ; Exchange de and hl
ldi ; *de++ = *hl++
ldi ; *de++ = *hl++
ldi ; *de++ = *hl++
ld a, (hl) ; a = number of modulation steps
srl a ; Divide by 2
ld (de), a ; Store in track RAM
xor a ; a = 0
ld (ix+zTrack.ModulationValLow), a ; Clear low byte of accumulated modulation
ld (ix+zTrack.ModulationValHigh), a ; Clear high byte of accumulated modulation
ret
; End of function zPrepareModulation
; =============== S U B R O U T I N E =======================================
; Applies modulation.
;
; Input: ix Pointer to track's RAM
; hl Note frequency
; Output:
; If modulation control is 80h (normal modulation):
; hl Final note frequency
; de Pointer to modulation data in track RAM
; iy Pointer to modulation data in track RAM
; bc Unmodulated note frequency
;
; If modulation control is nonzero and not 80h (modulation envelope effects):
;
;
;sub_3C9
zDoModulation:
ld a, (ix+zTrack.ModulationCtrl) ; Get modulation control byte
or a ; Is modulation active?
ret z ; Return if not
cp 80h ; Is modulation control 80h?
jr nz, zDoModEnvelope ; Branch if not
dec (ix+zTrack.ModulationWait) ; Decrement modulation wait
ret nz ; Return if nonzero
inc (ix+zTrack.ModulationWait) ; Increase it back to 1 for next frame
push hl ; Save hl
ld l, (ix+zTrack.ModulationValLow) ; l = low byte of accumulated modulation
ld h, (ix+zTrack.ModulationValHigh) ; h = high byte of accumulated modulation
; In non-Type 2 DAC versions of SMPS Z80, the following four instructions were below the 'jr nz'
; which could lead to a bug where iy isn't initialised, but still used as a pointer.
ld e, (ix+zTrack.ModulationPtrLow) ; e = low byte of modulation data pointer
ld d, (ix+zTrack.ModulationPtrHigh) ; d = high byte of modulation data pointer
push de ; Save de
pop iy ; iy = pointer to modulation data
dec (ix+zTrack.ModulationSpeed) ; Decrement modulation speed
jr nz, .mod_sustain ; Branch if nonzero
ld a, (iy+1) ; Get original modulation speed
ld (ix+zTrack.ModulationSpeed), a ; Reset modulation speed timeout
ld a, (ix+zTrack.ModulationDelta) ; Get modulation delta per step
ld c, a ; c = modulation delta per step
if fix_sndbugs
rla ; Carry contains sign of delta
sbc a, a ; a = 0 or -1 if carry is 0 or 1
else
and 80h ; Get only sign bit
rlca ; Shift it into bit 0
neg ; Negate (so it is either 0 or -1)
endif
ld b, a ; bc = sign extension of delta
add hl, bc ; hl += bc
ld (ix+zTrack.ModulationValLow), l ; Store low byte of accumulated modulation
ld (ix+zTrack.ModulationValHigh), h ; Store high byte of accumulated modulation
.mod_sustain:
pop bc ; bc = note frequency
add hl, bc ; hl = modulated note frequency
dec (ix+zTrack.ModulationSteps) ; Reduce number of modulation steps
ret nz ; Return if nonzero
ld a, (iy+3) ; Get number of steps from track data
ld (ix+zTrack.ModulationSteps), a ; Reset modulation steps in track RAM
ld a, (ix+zTrack.ModulationDelta) ; Load modulation delta
neg ; Change its sign
ld (ix+zTrack.ModulationDelta), a ; Store it back
ret
; ---------------------------------------------------------------------------
;loc_41A
;zDoFrequencyFlutter
zDoModEnvelope:
dec a ; Convert into pointer table index
ex de, hl ; Exchange de and hl; de = note frequency
ld c, zID_ModEnvPointers ; Value for modulation envelope pointer table
rst GetPointerTable ; hl = pointer to modulation envelope pointer table
rst PointerTableOffset ; hl = modulation envelope pointer for modulation control byte
jr zDoModEnvelope_cont
; ---------------------------------------------------------------------------
;zFreqFlutterSetIndex
zModEnvSetIndex:
ld (ix+zTrack.ModEnvIndex), a ; Set new modulation envelope index
;loc_425
;zDoFrequencyFlutter_cont
zDoModEnvelope_cont:
push hl ; Save hl
ld c, (ix+zTrack.ModEnvIndex) ; c = modulation envelope index
ld b, 0 ; b = 0
add hl, bc ; Offset into modulation envelope table
if fix_sndbugs
; Fix based on similar code from Space Harrier II's sound driver.
; This fixes both "DANGER!" bugs below. This is better than the
; previous fix, which was based on Ristar's driver.
ld c, l
ld b, h
ld a, (bc) ; a = new modulation envelope value
else
ld a, (hl) ; a = new modulation envelope value
endif
pop hl ; Restore hl
bit 7, a ; Is modulation envelope negative?
jp z, zlocPositiveModEnvMod ; Branch if not
cp 82h ; Is it 82h?
jr z, zlocChangeModEnvIndex ; Branch if yes
cp 80h ; Is it 80h?
jr z, zlocResetModEnvMod ; Branch if yes
cp 84h ; Is it 84h?
jr z, zlocModEnvIncMultiplier ; Branch if yes
ld h, 0FFh ; h = 0FFh
jr nc, zlocApplyModEnvMod ; Branch if more than 84h
set 6, (ix+zTrack.PlaybackControl) ; Set 'sustain frequency' bit
pop hl ; Tamper with return location so as to not return to caller
ret
; ---------------------------------------------------------------------------
;loc_449
;zlocChangeFlutterIndex
zlocChangeModEnvIndex:
inc bc ; Increment bc
; DANGER! Uses bc as a pointer, getting bytes from code region.
; This happens for several frequency flutters, so you should avoid them
; unless you enable the driver bug fixes.
ld a, (bc) ; Use it as a pointer??? Getting bytes from code region?
jr zModEnvSetIndex ; Set position to nonsensical value
; ---------------------------------------------------------------------------
;loc_44D
;zlocResetFlutterMod
zlocResetModEnvMod:
xor a ; a = 0
jr zModEnvSetIndex ; Reset position for modulation envelope
; ---------------------------------------------------------------------------
;loc_450
;zlocFlutterIncMultiplier
zlocModEnvIncMultiplier:
inc bc ; Increment bc
; DANGER! Uses bc as a pointer, getting bytes from code region.
; Luckily, this does not happen for any of the existing frequency
; flutters.
ld a, (bc) ; Use it as a pointer??? Getting bytes from code region?
add a, (ix+zTrack.ModEnvSens) ; Add envelope sensibility to a...
ld (ix+zTrack.ModEnvSens), a ; ... then store new value
inc (ix+zTrack.ModEnvIndex) ; Advance envelope modulation...
inc (ix+zTrack.ModEnvIndex) ; ... twice.
jr zDoModEnvelope_cont
; ---------------------------------------------------------------------------
;loc_460
;zlocPositiveFlutterMod
zlocPositiveModEnvMod:
ld h, 0 ; h = 0
;loc_462
;zlocApplyFlutterMod
zlocApplyModEnvMod:
ld l, a ; hl = sign extension of modulation value
ld b, (ix+zTrack.ModEnvSens) ; Fetch envelope sensibility
inc b ; Increment it (minimum 1)
ex de, hl ; Swap hl and de; hl = note frequency
.loop:
add hl, de ; hl += de
djnz .loop ; Make hl = note frequency + b * de
inc (ix+zTrack.ModEnvIndex) ; Advance modulation envelope
ret
; End of function zDoModulation
; =============== S U B R O U T I N E =======================================
; Adds the current frequency displacement (signed) to note frequency.
;
; Input: ix Track RAM
; Output: hl Shifted frequency
; a Damaged
; bc Damaged
;
;sub_46F
;zDoPitchSlide
zUpdateFreq:
ld h, (ix+zTrack.FreqHigh) ; h = high byte of note frequency
ld l, (ix+zTrack.FreqLow) ; l = low byte of note frequency
if fix_sndbugs
ld a, (ix+zTrack.Detune) ; a = detune
ld c, a ; bc = sign extension of frequency displacement
rla ; Carry contains sign of frequency displacement
sbc a, a ; a = 0 or -1 if carry is 0 or 1
ld b, a ; bc = sign extension of frequency displacement
else
ld b, 0 ; b = 0
ld a, (ix+zTrack.Detune) ; a = detune
or a ; Is a negative?
jp p, .did_sign_extend ; Branch if not
ld b, 0FFh ; b = -1
.did_sign_extend:
ld c, a ; bc = sign extension of frequency displacement
endif
add hl, bc ; Add to frequency
ret
; End of function zUpdateFreq
; =============== S U B R O U T I N E =======================================
; Gets offset for requested FM instrument.
;
;sub_483
zGetFMInstrumentPointer:
ld hl, (zVoiceTblPtr) ; hl = pointer to voice table
ld a, (zUpdatingSFX) ; Get flag
or a ; Is this a SFX track?
jr z, zGetFMInstrumentOffset ; Branch if not
ld l, (ix+zTrack.VoicesLow) ; l = low byte of track's voice pointer
ld h, (ix+zTrack.VoicesHigh) ; h = high byte of track's voice pointer
;loc_492
zGetFMInstrumentOffset:
xor a ; a = 0
or b ; Is FM instrument the first one (zero)?
ret z ; Return if so
ld de, 25 ; Size of each FM instrument
.loop:
add hl, de ; Advance pointer to next instrument
djnz .loop ; Loop until instrument offset is found
ret
; End of function zGetFMInstrumentPointer
; ---------------------------------------------------------------------------
;loc_49C
zFMInstrumentRegTable:
db 0B0h ; Feedback/Algorithm
zFMInstrumentOperatorTable:
db 30h ; Detune/multiple operator 1
db 38h ; Detune/multiple operator 3
db 34h ; Detune/multiple operator 2
db 3Ch ; Detune/multiple operator 4
zFMInstrumentRSARTable:
db 50h ; Rate scaling/attack rate operator 1
db 58h ; Rate scaling/attack rate operator 3
db 54h ; Rate scaling/attack rate operator 2
db 5Ch ; Rate scaling/attack rate operator 4
zFMInstrumentAMD1RTable:
db 60h ; Amplitude modulation/first decay rate operator 1
db 68h ; Amplitude modulation/first decay rate operator 3
db 64h ; Amplitude modulation/first decay rate operator 2
db 6Ch ; Amplitude modulation/first decay rate operator 4
zFMInstrumentD2RTable:
db 70h ; Secondary decay rate operator 1
db 78h ; Secondary decay rate operator 3
db 74h ; Secondary decay rate operator 2
db 7Ch ; Secondary decay rate operator 4
zFMInstrumentD1LRRTable:
db 80h ; Secondary amplitude/release rate operator 1
db 88h ; Secondary amplitude/release rate operator 3
db 84h ; Secondary amplitude/release rate operator 2
db 8Ch ; Secondary amplitude/release rate operator 4
zFMInstrumentOperatorTable_End
;loc_4B1
zFMInstrumentTLTable:
db 40h ; Total level operator 1
db 48h ; Total level operator 3
db 44h ; Total level operator 2
db 4Ch ; Total level operator 4
zFMInstrumentTLTable_End
;loc_4B5
zFMInstrumentSSGEGTable:
db 90h ; SSG-EG operator 1
db 98h ; SSG-EG operator 3
db 94h ; SSG-EG operator 2
db 9Ch ; SSG-EG operator 4
zFMInstrumentSSGEGTable_End
; =============== S U B R O U T I N E =======================================
; Subroutine to send FM instrument data to YM2612 chip.
;
;sub_4B9
zSendFMInstrument:
ld de, zFMInstrumentRegTable ; de = pointer to register output table
ld c, (ix+zTrack.AMSFMSPan) ; Send track AMS/FMS/panning
ld a, 0B4h ; Select AMS/FMS/panning register
call zWriteFMIorII ; Set track data
call zSendFMInstrData ; Send data to register
ld (ix+zTrack.FeedbackAlgo), a ; Save current feedback/algorithm
if fix_sndbugs
; Start with detune/multiplier operators
ld b, zFMInstrumentRSARTable-zFMInstrumentOperatorTable ; Number of commands to issue
.loop1:
call zSendFMInstrData ; Send FM instrument data
djnz .loop1 ; Loop
; Now for rate scaling/attack rate. The attack rate must be 1Fh if using
; SSG-EG, which is the reason for the split.
ld b, zFMInstrumentAMD1RTable-zFMInstrumentRSARTable ; Number of commands to issue
.loop2:
call zSendFMInstrDataRSAR ; Send FM instrument data
djnz .loop2 ; Loop
; Finalize with all the other operators.
ld b, zFMInstrumentOperatorTable_End-zFMInstrumentAMD1RTable ; Number of commands to issue
.loop3:
call zSendFMInstrData ; Send FM instrument data
djnz .loop3 ; Loop
else
; DANGER! The following code ignores the fact that SSG-EG mode must be
; used with maximum (1Fh) attack rate or output is officially undefined.
; Setting voices with SSG-EG enabled then has the potential effect of
; weird sound, even in real hardware.
ld b, zFMInstrumentOperatorTable_End-zFMInstrumentOperatorTable ; Number of commands to issue
.loop:
call zSendFMInstrData ; Send FM instrument data
djnz .loop ; Loop
endif
ld (ix+zTrack.TLPtrLow), l ; Save low byte of pointer to (not yet uploaded) TL data
ld (ix+zTrack.TLPtrHigh), h ; Save high byte of pointer to (not yet uploaded) TL data
jp zSendTL ; Send TL data
; End of function zSendFMInstrument
; =============== S U B R O U T I N E =======================================
; Sends FM instrument data to YM2612.
;
; Input: ix Track data
; hl Pointer to instrument data
; de Pointer to register selector table
; Output: a Value written to the register
; c Value written to the register
;
;sub_4DA
zSendFMInstrData:
ld a, (de) ; Get register output
inc de ; Advance pointer
ld c, (hl) ; Get value from instrument RAM
inc hl ; Advance pointer
if fix_sndbugs
jp zWriteFMIorII ; Write track data
else
call zWriteFMIorII ; Write track data
ret
endif
; End of function zSendFMInstrData
if fix_sndbugs
zSendFMInstrDataRSAR:
ld a, (ix+zTrack.HaveSSGEGFlag) ; Get custom SSG-EG flag
or a ; Does track have custom SSG-EG data?
jp p, zSendFMInstrData ; Branch if not
ld a, (hl) ; Get value from instrument RAM
inc hl ; Advance pointer
or 1Fh ; Set AR to maximum
ld c, a ; c = RS/AR for operator
ld a, (de) ; Get register output
inc de ; Advance pointer
jp zWriteFMIorII ; Write track data
endif
; =============== S U B R O U T I N E =======================================
; Rotates sound queue and clears last entry. Then plays the popped sound from
; the queue.
;loc_4E2
zCycleSoundQueue:
ld a, (zSoundQueue0) ; Get first item in sound queue
ld (zNextSound), a ; Save into next sound variable
ld a, (zSoundQueue1) ; Get second item in queue
ld (zSoundQueue0), a ; Move to first spot
ld a, (zSoundQueue2) ; Get third item in queue
ld (zSoundQueue1), a ; Move to second spot
xor a ; a = 0
ld (zSoundQueue2), a ; Clear third spot in queue
ld a, (zNextSound) ; a = next sound to play
; End of function zCycleSoundQueue
; ===========================================================================
; Type Check
; ===========================================================================
; 1-32, DC = Music
; 33-DB, DD-DF = SFX
; E1-E6 = Fade Effects
; FF = SEGA Scream
; TypeCheck:
;sub_4FB
zPlaySoundByIndex:
cp mus_CreditsK ; Is this the credits music?
jp z, zPlayMusicCredits ; Branch if yes
cp mus_SEGA ; Is this the SEGA sound?
jp z, zPlaySegaSound ; Branch if yes
cp Mus__End ; Is this a music?
jp c, zPlayMusic ; Branch if yes
cp sfx__End ; Is this a sound effect?
jp c, zPlaySound_CheckRing ; Branch if yes
cp mus__FirstCmd ; Is it before the first fade effect?
jp c, zStopAllSound ; Branch if yes
cp Mus__EndCmd ; Is this after the last fade effect?
jp nc, zStopAllSound ; Branch if yes
sub mus__FirstCmd ; If none of the checks passed, do fade effects.
ld hl, zFadeEffects ; hl = switch table pointer
rst PointerTableOffset ; Get address of function that handles the fade effect
if fix_sndbugs=0
xor a ; Set a = 0
ld (unk_1C18), a ; Set otherwise unused value to zero
endif
jp (hl) ; Handle fade effect
; End of function zPlaySoundByIndex
; ---------------------------------------------------------------------------
;loc_524
zFadeEffects:
dw zFadeOutMusic ; E1h
dw zStopAllSound ; E2h
dw zPSGSilenceAll ; E3h
dw zStopSFX ; E4h
dw zFadeOutMusic ; E5h
; ---------------------------------------------------------------------------
;sub_52E
zStopSFX:
ld ix, zTracksSFXStart ; ix = pointer to SFX track memory
ld b, (zTracksSFXEnd-zTracksSFXStart)/zTrack.len ; Number of channels
ld a, 1 ; a = 1
ld (zUpdatingSFX), a ; Set flag to update SFX
.loop:
push bc ; Save bc
bit 7, (ix+zTrack.PlaybackControl) ; Is track playing?
call nz, zSilenceStopTrack ; Branch if yes
ld de, zTrack.len ; Spacing between tracks
add ix, de ; ix = pointer to next track
pop bc ; Restore bc
djnz .loop ; Loop for each track
if fix_sndbugs
jp zClearNextSound
else
call zClearNextSound
ret
endif
; =============== S U B R O U T I N E =======================================
; Writes hl to stack twice and stops track, silencing it. The two hl pushes
; will be counteracted by cfSilenceStopTrack.
;
;sub_54D
zSilenceStopTrack:
push hl ; Save hl...
push hl ; ... twice
jp cfSilenceStopTrack ; Silence FM channel and stop track
; End of function zSilenceStopTrack
; ---------------------------------------------------------------------------
;loc_552
zPlayMusicCredits:
ld a, 32h ; Credits music is the last entry on the music table
push af ; Save af
jp zPlayMusic_DoFade ; Continue as music
; ---------------------------------------------------------------------------
;loc_558
zPlayMusic:
sub Mus__First ; Remap index from 1h-32h to 0h-31h (see also credits music, above)
ret m ; Return if negative (id = 0)
push af ; Save af
cp mus_ExtraLife-Mus__First ; Is it the 1-up music?
jp nz, zPlayMusic_DoFade ; Branch if not
ld a, (zFadeInTimeout) ; Fading timeout
or a ; Is music being faded?
jp z, .no_fade ; Branch if not
xor a ; a = 0
ld (zMusicNumber), a ; Clear M68K input queue...
ld (zSFXNumber0), a ; ... including SFX slot 0...
ld (zSFXNumber1), a ; ... and SFX slot 1
ld (zSoundQueue0), a ; Also clear music queue entry 0...
ld (zSoundQueue1), a ; ... and entry 1...
ld (zSoundQueue2), a ; ... and entry 2
ld (zNextSound), a ; Clear currently selected song to play
pop af ; Restore af
ret
; ---------------------------------------------------------------------------
.no_fade:
ld a, (zFadeToPrevFlag) ; Get fade-to-prev flag
cp mus_ExtraLife-Mus__First ; Was it triggered by the 1-up song?
jp z, zBGMLoad ; Branch if yes
xor a ; a = 0
ld (zMusicNumber), a ; Clear M68K input queue...
ld (zSFXNumber0), a ; ... including SFX slot 0...
ld (zSFXNumber1), a ; ... and SFX slot 1
ld (zSoundQueue0), a ; Also clear music queue entry 0...
ld (zSoundQueue1), a ; ... and entry 1...
ld (zSoundQueue2), a ; ... and entry 2
ld a, (zSongBank) ; Get song bank for extant track...
ld (zSongBankSave), a ; ... and save it
ld a, (zTempoSpeedup) ; Get current tempo speed-up value...
ld (zTempoSpeedupSave), a ; ... and save it
xor a ; a = 0
ld (zTempoSpeedup), a ; 1-Up should play on normal speed
ld hl, zTracksStart ; hl = pointer to song RAM
ld de, zTracksSaveStart ; de = pointer to RAM area to save the song data to
ld bc, zTracksSaveEnd-zTracksSaveStart ; Number of bytes to copy
ldir ; while (bc-- > 0) *de++ = *hl++;
ld hl, zTracksSaveStart ; hl = pointer to saved song's RAM area
ld de, zTrack.len ; Spacing between tracks
ld b, (zTracksSaveEnd-zTracksSaveStart)/zTrack.len ; Number of tracks
.loop:
if fix_sndbugs
ld a, (hl) ; Get playback control byte for song
and 7Fh ; Strip the 'playing' bit
or 4 ; Set bit 2 (SFX overriding)
ld (hl), a ; And save it all
else
ld a, (hl) ; Get playback control byte for song
and 7Fh ; Strip the 'playing' bit
set 2, (hl) ; Set bit 2 (SFX overriding)
ld (hl), a ; But then overwrite the whole thing...
endif
add hl, de ; Advance to next track
djnz .loop ; Loop for all tracks
ld a, mus_ExtraLife-Mus__First ; a = 1-up id-1
ld (zFadeToPrevFlag), a ; Set fade-to-prev flag to it
ld a, (zCurrentTempo) ; Get current tempo
ld (zCurrentTempoSave), a ; Save it
ld hl, (zVoiceTblPtr) ; Get voice table pointer
ld (zVoiceTblPtrSave), hl ; Save it
jp zBGMLoad
; ---------------------------------------------------------------------------
zPlayMusic_DoFade:
call zStopAllSound ; Stop all music
;loc_5DE
zBGMLoad:
pop af ; Restore af
push af ; Then save it back again
ld hl, z80_MusicBanks ; hl = table of music banks
; The following block adds the music index to the table address as a 16-bit offset
add a, l ; a += l
ld l, a ; l = low byte of offset into music entry
adc a, h ; a += h, plus 1 if a + l overflowed the 8-bit register
sub l ; Now, a = high byte of offset into music entry
ld h, a ; hl is the offset to the music bank
if fix_sndbugs
ld a, (hl) ; Get bank for the song to play
else
ld (loc_5EB+1), hl ; Store into next instruction (self-modifying code)
loc_5EB:
ld a, (z80_MusicBanks) ; self-modified code; a is set to correct bank for the song to play
endif
ld (zSongBank), a ; Save the song's bank...
bankswitch2 ; ... then bank switch to it
ld a, 0B6h ; Set Panning / AMS / FMS
ld (zYM2612_A1), a ; Write destination address to YM2612 address register
nop
ld a, 0C0h ; default Panning / AMS / FMS settings (only stereo L/R enabled)
ld (zYM2612_D1), a ; Write to YM2612 data register
pop af ; Restore af
ld c, zID_MusicPointers ; c = 4 (music pointer table)
rst GetPointerTable ; hl = pointer table for music pointers
rst PointerTableOffset ; hl = pointer to song data
push hl ; Save hl...
push hl ; ... twice
rst ReadPointer ; Dereference pointer, so that hl = pointer to voice table
ld (zVoiceTblPtr), hl ; Store voice table pointer
pop hl ; Restore hl to pointer to song data
pop iy ; Also set iy = pointer to song data
ld a, (iy+5) ; Main tempo value
ld (zTempoAccumulator), a ; Set starting accumulator value
ld (zCurrentTempo), a ; Store current song tempo
ld de, 6 ; Offset into DAC pointer
add hl, de ; hl = pointer to DAC pointer
ld (zSongPosition), hl ; Save it to RAM
ld hl, zFMDACInitBytes ; Load pointer to init data
ld (zTrackInitPos), hl ; Save it to RAM
ld de, zTracksStart ; de = pointer to track RAM
ld b, (iy+2) ; b = number of FM + DAC channels
ld a, (iy+4) ; a = tempo divider
.fm_dac_loop:
push bc ; Save bc (gets damaged by ldi instructions)
ld hl, (zTrackInitPos) ; Restore saved position for init bytes
ldi ; *de++ = *hl++ (copy initial playback control)
ldi ; *de++ = *hl++ (copy channel assignment bits)
ld (de), a ; Copy tempo divider
inc de ; Advance pointer
ld (zTrackInitPos), hl ; Save current position in channel assignment bits
ld hl, (zSongPosition) ; Load current position in BGM data
ldi ; *de++ = *hl++ (copy track address low byte)
ldi ; *de++ = *hl++ (copy track address high byte)
ldi ; *de++ = *hl++ (default key offset)
ldi ; *de++ = *hl++ (track default volume)
ld (zSongPosition), hl ; Store current position in BGM data
call zInitFMDACTrack ; Init the remainder of the track RAM
pop bc ; Restore bc
djnz .fm_dac_loop ; Loop for all tracks (stored in b)
ld a, (iy+3) ; Get number of PSG tracks
or a ; Do we have any PSG channels?
jp z, zClearNextSound ; Branch if not
ld b, a ; b = number of PSG tracks
ld hl, zPSGInitBytes ; Load pointer to init data
ld (zTrackInitPos), hl ; Save it to RAM
ld de, zSongPSG1 ; de = pointer to RAM for song PSG tracks
ld a, (iy+4) ; a = tempo divider
.psg_loop:
push bc ; Save bc (gets damaged by ldi instructions)
ld hl, (zTrackInitPos) ; Restore saved position for init bytes
ldi ; *de++ = *hl++
ldi ; *de++ = *hl++
ld (de), a ; Copy tempo divider
inc de ; Advance pointer
ld (zTrackInitPos), hl ; Save current position in channel assignment bits
ld hl, (zSongPosition) ; Load current position in BGM data
ld bc, 6 ; Copy 6 bytes
ldir ; while (bc-- > 0) *de++ = *hl++; (copy track address, default key offset, default volume, modulation control, default PSG tone)
ld (zSongPosition), hl ; Store current position in BGM data
call zZeroFillTrackRAM ; Init the remainder of the track RAM
pop bc ; Restore bc
djnz .psg_loop ; Loop for all tracks (stored in b)
; =============== S U B R O U T I N E =======================================
; Clears next sound to play.
;sub_690
zClearNextSound:
xor a
ld (zNextSound), a
ret
; End of function zClearNextSound
; ---------------------------------------------------------------------------
;loc_695
; FM/DAC channel assignment bits
; The first byte in every pair (always 80h) is default value for playback control bits.
; The second byte in every pair goes as follows:
; The first is for DAC; then 0, 1, 2 then 4, 5, 6 for the FM channels (the missing 3
; is the gap between part I and part II for YM2612 port writes).
zFMDACInitBytes:
db 80h, 6
db 80h, 0
db 80h, 1
db 80h, 2
db 80h, 4
db 80h, 5
if fix_sndbugs=0
db 80h, 6 ; FM6 music track (does not exist in this driver)
endif
;loc_6A3
; Default values for PSG tracks
; The first byte in every pair (always 80h) is default value for playback control bits.
; The second byte in every pair is the default values for PSG tracks.
zPSGInitBytes:
db 80h, 80h
db 80h, 0A0h
db 80h, 0C0h
; ---------------------------------------------------------------------------
;loc_6A9
zPlaySound_CheckRing:
sub sfx_First ; Make it a 0-based index
or a ; Is it the ring sound?
jp nz, zPlaySound_Bankswitch ; Branch if not
ld a, (zRingSpeaker) ; Get speaker on which ring sound is played
xor 1 ; Toggle bit 0
ld (zRingSpeaker), a ; Save it
;loc_6B7
zPlaySound_Bankswitch:
ex af, af' ; Save af
ld a, zmake68kBank(SndBank) ; Load SFX sound bank address
bankswitch2 ; Bank switch to it
xor a ; a = 0
ld c, zID_SFXPointers ; SFX table index
ld (zUpdatingSFX), a ; Clear flag to update SFX
ex af, af' ; Restore af
cp sfx_Spindash-sfx_First ; Is this the spindash sound?
jp z, zPlaySound ; Branch if yes
cp sfx__FirstContinuous-sfx_First ; Is this before sound 0BCh?
jp c, zPlaySound_Normal ; Branch if yes
push af ; Save af
ld b, a ; b = sound index
ld a, (zContinuousSFX) ; Load last continuous SFX played
sub b ; Is this the same continuous sound that was playing?
jp nz, zPlaySound_NotCont ; Branch if not
ld a, 80h ; a = 80h
ld (zContinuousSFXFlag), a ; Flag continuous SFX as being extended
rst GetPointerTable ; hl = pointer to SFX data table
pop af ; Restore af
if fix_sndbugs=0
ld c, a ; c = SFX index; redundant, as PointerTableOffset does it already
endif
rst PointerTableOffset ; hl = pointer to SFX data
inc hl ; Skip low byte of voice pointer
inc hl ; Skip high byte of voice pointer
inc hl ; Skip timing divisor
ld a, (hl) ; Get number of SFX tracks
ld (zContSFXLoopCnt), a ; Save it to RAM (loop counter for continuous SFX)
jp zClearNextSound
; ---------------------------------------------------------------------------
;loc_6FB
zPlaySound_NotCont:
xor a ; a = 0
ld (zContinuousSFXFlag), a ; Clear continue continuous SFX flag
pop af ; Restore af
ld (zContinuousSFX), a ; Store SFX index
jp zPlaySound
; ---------------------------------------------------------------------------
;loc_706
zPlaySound_Normal:
push af ; Save af
xor a ; a = 0
ld (zSpindashRev), a ; Reset spindash rev
pop af ; Restore af
;loc_70C
zPlaySound:
rst GetPointerTable ; hl = pointer to SFX data table
rst PointerTableOffset ; hl = pointer to SFX data
push hl ; Save hl
rst ReadPointer ; hl = voice table pointer
ld (zSFXVoiceTblPtr), hl ; Save to RAM
if fix_sndbugs=0
xor a ; a = 0
ld (unk_1C15), a ; Set otherwise unused location to zero
endif
pop hl ; hl = pointer to SFX data
push hl ; Save it again
pop iy ; iy = pointer to SFX data
ld a, (iy+2) ; a = tempo divider
ld (zSFXTempoDivider), a ; Save to RAM
ld de, 4 ; de = 4
add hl, de ; hl = pointer to SFX track data
ld b, (iy+3) ; b = number of tracks (FM + PSG) used by SFX
ld a, b ; Copy to a
ld (zContSFXLoopCnt), a ; Save to RAM (loop counter for continuous SFX)
;loc_72C
zSFXTrackInitLoop:
push bc ; Save bc; damaged by ldi instructions below
push hl ; Save hl
inc hl ; hl = pointer to channel identifier
ld c, (hl) ; c = channel identifier
call zGetSFXChannelPointers ; Get track pointers for track RAM (ix) and overridden song track (hl)
set 2, (hl) ; Set 'SFX is overriding this track' bit
push ix ; Save pointer to SFX track data in RAM
if fix_sndbugs=0
ld a, (zUpdatingSFX) ; Get flag
or a ; Are we updating SFX?
jr z, .normalsfx1 ; Branch if not (hint: it was cleared just below the bank switch above so... always)
; Effectively dead code.
; Analysis of the Battletoads sound driver confirms previous speculation:
; this code was meant for GHZ-like waterfall effects which were subsequently
; scrapped in favor of the continuous SFX system.
; If this system were to be reimplemented, then, after the call to
; zGetSFXChannelPointers, we would have:
; * ix = pointer to the overriding SFX track data in RAM;
; * iy = pointer to the special SFX track data in RAM.
; * hl = pointer to the overridden music track data in RAM;
; This code would then ensure that de points to the correct RAM area for
; the writes below.
pop hl ; hl = pointer to SFX track data in RAM
push iy ; Save iy (pointer to SFX data)
.normalsfx1:
endif
pop de ; de = pointer to SFX track data in RAM (unless you consider the above effectively dead code)
pop hl ; hl = pointer to SFX track data
ldi ; *de++ = *hl++ (initial playback control)
ld a, (de) ; Get the voice control byte from track RAM (to deal with SFX already there)
cp 2 ; Is this FM3?
call z, zFM3NormalMode ; Set FM3 to normal mode if so
ldi ; *de++ = *hl++ (copy channel identifier)
ld a, (zSFXTempoDivider) ; Get SFX tempo divider
ld (de), a ; Store it to RAM
inc de ; Advance pointer
ldi ; *de++ = *hl++ (low byte of channel data pointer)
ldi ; *de++ = *hl++ (high byte of channel data pointer)
ldi ; *de++ = *hl++ (key displacement)
ldi ; *de++ = *hl++ (channel volume)
call zInitFMDACTrack ; Init the remainder of the track RAM
if fix_sndbugs=0
; Analysis of the Battletoads sound driver confirms previous speculation:
; this code was meant for GHZ-like waterfall effects which were subsequently
; scrapped in favor of the continuous SFX system.
; If this system were to be reimplemented, then, after the call to
; zGetSFXChannelPointers, we would have:
; * ix = pointer to the overriding SFX track data in RAM;
; * iy = pointer to the special SFX track data in RAM.
; * hl = pointer to the overridden music track data in RAM;
; The code would then be checking to see if the corresponding SFX track
; was playing, make sure the tracks refer to the same FM/PSG channel
; then, if needed, mark the special SFX track as being overridden by the
; SFX so as to not abruptly end the SFX.
; With the system unimplemented, iy points to the current SFX track data,
; meaning that the second branch is never taken, resulting in an attempted
; write to ROM.
bit 7, (ix+zTrack.PlaybackControl) ; Is the 'playing' bit set for this track?
jr z, .dontoverride ; Branch if not (all SFX define it as 80h, so... never)
ld a, (ix+zTrack.VoiceControl) ; Grab the voice control byte
cp (iy+zTrack.VoiceControl) ; Is this equal to the one for the corresponding special SFX track?
jr nz, .dontoverride ; Branch if not
set 2, (iy+zTrack.PlaybackControl) ; Set bit 2 of playback control ('SFX is overriding this track')
.dontoverride:
endif
push hl ; Save hl
ld hl, (zSFXVoiceTblPtr) ; hl = pointer to voice data
if fix_sndbugs=0
ld a, (zUpdatingSFX) ; Get flag
or a ; Are we updating SFX?
jr z, .normalsfx2 ; Branch if not (hint: it was cleared just below the bank switch above so... always)
; Analysis of the Battletoads sound driver confirms previous speculation:
; this code was meant for GHZ-like waterfall effects which were subsequently
; scrapped in favor of the continuous SFX system.
; If this system were to be reimplemented, then, after the call to
; zGetSFXChannelPointers, we would have:
; * ix = pointer to the overriding SFX track data in RAM;
; * iy = pointer to the special SFX track data in RAM.
; * hl = pointer to the overridden music track data in RAM;
; This code would then make ix point to the correct track data for the
; function calls below.
; Without it implemented, iy points to the current SFX track data.
push iy ; Save iy
pop ix ; ix = pointer to special SFX data
.normalsfx2:
endif
ld (ix+zTrack.VoicesLow), l ; Low byte of voice pointer
ld (ix+zTrack.VoicesHigh), h ; High byte of voice pointer
call zKeyOffIfActive ; Kill channel notes
if fix_sndbugs
bit 7, (ix+zTrack.VoiceControl) ; Is this an FM track?
call z, zFMClearSSGEGOps ; If so, clear SSG-EG operators for track's channels
else
call zFMClearSSGEGOps ; Clear SSG-EG operators for track's channels (even on PSG tracks!!!)
endif
pop hl ; Restore hl
pop bc ; Restore bc
djnz zSFXTrackInitLoop ; Loop for all SFX tracks
jp zClearNextSound
; =============== S U B R O U T I N E =======================================
;
;sub_78F
zGetSFXChannelPointers:
bit 7, c ; Is this a PSG track?
jr nz, .is_psg ; Branch if yes
ld a, c ; a = c
if fix_sndbugs=0
bit 2, a ; Is this FM4, FM5 or FM6?
jr z, .get_ptrs ; Branch if not
dec a ; Remove gap between FM3 and FM4+
endif
jr .get_ptrs
; ---------------------------------------------------------------------------
.is_psg:
if fix_sndbugs
call zSilencePSGChannel ; Silence channel at ix
ld a, c ; a = channel identifier
; Shift high 3 bits to low bits so that we can convert it to a table index
rlca
rlca
rlca
and 7
add a, 3 ; Compensate for subtraction below
else
ld a, 1Fh ; a = 1Fh (redundant, as this is the first instruction of the function)
call zSilencePSGChannel ; Silence channel at ix
; The next two lines are here because zSilencePSGChannel does not do
; its job correctly. See the note there.
ld a, 0FFh ; Command to silence Noise channel
ld (zPSG), a ; Silence it
ld a, c ; a = channel identifier
; The next 5 shifts are so that we can convert it to a table index
srl a
srl a
srl a
srl a
srl a
add a, 2 ; Compensate for subtraction below
endif
.get_ptrs:
sub 2 ; Start table at FM3
ld (zSFXSaveIndex), a ; Save index of overridden channel
push af ; Save af
ld hl, zSFXChannelData ; Pointer table for track RAM
rst PointerTableOffset ; hl = track RAM
push hl ; Save hl
pop ix ; ix = track RAM
pop af ; Restore af
; This is where there is code in other drivers to load the special SFX
; channel pointers to iy
ld hl, zSFXOverriddenChannel ; Pointer table for the overridden music track
if fix_sndbugs
jp PointerTableOffset ; hl = RAM destination to mark as overridden
else
rst PointerTableOffset ; hl = RAM destination to mark as overridden
ret
endif
; End of function zGetSFXChannelPointers
; =============== S U B R O U T I N E =======================================
;
;sub_7C5
zInitFMDACTrack:
ex af, af' ; Save af
xor a ; a = 0
ld (de), a ; Set modulation to inactive
inc de ; Advance to next byte
ld (de), a ; Set FM instrument/PSG tone to zero too
inc de ; Advance to next byte again
ex af, af' ; Restore af
;loc_7CC
zZeroFillTrackRAM:
ex de, hl ; Exchange the contents of de and hl
ld (hl), zTrack.len ; Call subroutine stack pointer
inc hl ; Advance to next byte
ld (hl), 0C0h ; default Panning / AMS / FMS settings (only stereo L/R enabled)
inc hl ; Advance to next byte
ld (hl), 1 ; Current note duration timeout
ld b, zTrack.len-zTrack.DurationTimeout-1 ; Loop counter
.loop:
inc hl ; Advance to next byte
ld (hl), 0 ; Put 0 into this byte
djnz .loop ; Loop until end of track
inc hl ; Make hl point to next track
ex de, hl ; Exchange the contents of de and hl
ret
; End of function zInitFMDACTrack
; ---------------------------------------------------------------------------
;zloc_7DF
zSFXChannelData:
dw zSFX_FM3 ; FM3
if fix_sndbugs
dw 0000h ; Ironically, this filler is smaller than the code made to avoid it
endif
dw zSFX_FM4 ; FM4
dw zSFX_FM5 ; FM5
dw zSFX_FM6 ; FM6 or DAC
dw zSFX_PSG1 ; PSG1
dw zSFX_PSG2 ; PSG2
dw zSFX_PSG3 ; PSG3
dw zSFX_PSG3 ; PSG3/Noise
;zloc_7EF
zSFXOverriddenChannel:
dw zSongFM3 ; FM3
if fix_sndbugs
dw 0000h
endif
dw zSongFM4 ; FM4
dw zSongFM5 ; FM5
dw zSongFM6_DAC ; FM6 or DAC
dw zSongPSG1 ; PSG1
dw zSongPSG2 ; PSG2
dw zSongPSG3 ; PSG3
dw zSongPSG3 ; PSG3/Noise
; =============== S U B R O U T I N E =======================================
; Pauses/unpauses sound.
;
;sub_7FF
zPauseUnpause:
ld hl, zPauseFlag ; hl = pointer to pause flag
ld a, (hl) ; a = pause flag
or a ; Is sound driver paused?
ret z ; Return if not
jp m, .unpause ; Branch if pause flag is negative (unpause)
pop de ; Pop return value from the stack, so that a 'ret' will go back to zVInt
dec a ; Decrease a
ret nz ; Return if nonzero
ld (hl), 2 ; Set pause flag to 2 (i.e., stay paused but don't pause again)
jp zPauseAudio ; Pause all but FM6/DAC
; ---------------------------------------------------------------------------
.unpause:
xor a ; a = 0
ld (hl), a ; Clear pause flag
ld a, (zFadeOutTimeout) ; Get fade timeout
or a ; Is it zero?
jp nz, zStopAllSound ; Stop all music if not
ld ix, zSongFM1 ; Start with FM1 track
if fix_sndbugs
ld b, (zSongPSG1-zSongFM1)/zTrack.len ; Number of FM tracks
else
; DANGER! This treats a PSG channel as if it were an FM channel. This
; will break AMS/FMS/pan for FM1.
ld b, (zSongPSG2-zSongFM1)/zTrack.len ; Number of FM tracks +1
endif
.fm_loop:
ld a, (zHaltFlag) ; Get halt flag
or a ; Is song halted?
jr nz, .set_pan ; Branch if yes
bit 7, (ix+zTrack.PlaybackControl) ; Is track playing?
jr z, .skip_fm_track ; Branch if not
.set_pan:
ld c, (ix+zTrack.AMSFMSPan) ; Get track AMS/FMS/panning
ld a, 0B4h ; Command to select AMS/FMS/panning register
call zWriteFMIorII ; Write data to YM2612
.skip_fm_track:
ld de, zTrack.len ; Spacing between tracks
add ix, de ; Advance to next track
djnz .fm_loop ; Loop for all tracks
if fix_sndbugs
ld ix, zTracksSFXStart ; Start at the start of SFX track data
ld b, (zTracksSFXEnd-zTracksSFXStart)/zTrack.len ; Number of tracks
else
; DANGER! This code goes past the end of Z80 RAM and into reserved territory!
; By luck, it only *reads* from these areas...
ld ix, zTracksSFXEnd ; Start at the END of SFX track data (?)
ld b, 7 ; But loop for 7 tracks (??)
endif
.psg_loop:
bit 7, (ix+zTrack.PlaybackControl) ; Is track playing?
jr z, .skip_psg_track ; Branch if not
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
jr nz, .skip_psg_track ; Branch if yes
ld c, (ix+zTrack.AMSFMSPan) ; Get track AMS/FMS/panning
ld a, 0B4h ; Command to select AMS/FMS/panning register
call zWriteFMIorII ; Write data to YM2612
.skip_psg_track:
ld de, zTrack.len ; Spacing between tracks
add ix, de ; Go to next track
djnz .psg_loop ; Loop for all tracks
ret
; End of function zPauseUnpause
; =============== S U B R O U T I N E =======================================
; Fades out music.
;sub_85C
zFadeOutMusic:
ld a, 28h ; a = 28h
ld (zFadeOutTimeout), a ; Set fade timeout to this (start fading out music)
ld a, 6 ; a = 6
ld (zFadeDelayTimeout), a ; Set fade delay timeout
ld (zFadeDelay), a ; Set fade delay and fall through
; =============== S U B R O U T I N E =======================================
; Halts FM6/DAC, PSG1, PSG2, PSG3.
;sub_869
zHaltDACPSG:
xor a ; a = 0
ld (zSongFM6_DAC), a ; Halt FM6/DAC
ld (zSongPSG3), a ; Halt PSG3
ld (zSongPSG1), a ; Halt PSG1
ld (zSongPSG2), a ; Halt PSG2
jp zPSGSilenceAll
; End of function zHaltDACPSG
; =============== S U B R O U T I N E =======================================
; Fade out music slowly.
;
;sub_879
zDoMusicFadeOut:
ld hl, zFadeOutTimeout ; hl = pointer to fade timeout
ld a, (hl) ; a = fade counter
or a ; Is fade counter zero?
ret z ; Return if yes
call m, zHaltDACPSG ; Kill DAC and PSG channels if negative
res 7, (hl) ; Clear sign bit
ld a, (zFadeDelayTimeout) ; Get fade delay timeout
dec a ; Decrement it
jr z, .timer_expired ; Branch if it zero now
ld (zFadeDelayTimeout), a ; Store it back
ret
; ---------------------------------------------------------------------------
.timer_expired:
ld a, (zFadeDelay) ; Get fade delay
ld (zFadeDelayTimeout), a ; Restore counter to initial value
if fix_sndbugs
ld hl, zFadeOutTimeout ; (hl) = fade timeout
dec (hl) ; Decrement it
else
ld a, (zFadeOutTimeout) ; a = fade timeout
dec a ; Decrement it
ld (zFadeOutTimeout), a ; Then store it back
endif
jp z, zStopAllSound ; Stop all music if it is zero
ld a, (zSongBank) ; a = current music bank ID
bankswitch2 ; Bank switch to music bank
ld ix, zTracksStart ; ix = pointer to track RAM
ld b, (zSongPSG1-zTracksStart)/zTrack.len ; Number of FM+DAC tracks
.loop:
inc (ix+zTrack.Volume) ; Decrease volume
jp p, .chk_change_volume ; If still positive, branch
dec (ix+zTrack.Volume) ; Increase it back to minimum volume (127)
jr .next_track
; ---------------------------------------------------------------------------
.chk_change_volume:
bit 7, (ix+zTrack.PlaybackControl) ; Is track still playing?
jr z, .next_track ; Branch if not
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding track?
jr nz, .next_track ; Branch if yes
push bc ; Save bc
call zSendTL ; Send new volume
pop bc ; Restore bc
.next_track:
ld de, zTrack.len ; Spacing between tracks
add ix, de ; Advance to next track
djnz .loop ; Loop for all tracks
ret
; End of function zDoMusicFadeOut
; =============== S U B R O U T I N E =======================================
; Fades music in.
;
;sub_8DF
zDoMusicFadeIn:
ld a, (zFadeInTimeout) ; Get fading timeout
or a ; Is music being faded?
ret z ; Return if not
ld a, (zSongBank) ; Get current music bank
bankswitch2 ; Bank switch to music
if fix_sndbugs
ld hl, zFadeDelay ; Get fade delay
dec (hl) ; Decrement it
else
ld a, (zFadeDelay) ; Get fade delay
dec a ; Decrement it
ld (zFadeDelay), a ; Store it back
endif
ret nz ; Return if it is not yet zero
ld a, (zFadeDelayTimeout) ; Get current fade delay timeout
ld (zFadeDelay), a ; Reset to starting fade delay
ld b, (zSongPSG1-zSongFM1)/zTrack.len ; Number of FM tracks
ld ix, zSongFM1 ; ix = start of FM1 RAM
ld de, zTrack.len ; Spacing between tracks
.fm_loop:
if fix_sndbugs
dec (ix+zTrack.Volume) ; Increase track volume
else
ld a, (ix+zTrack.Volume) ; Get track volume
dec a ; Increase it
ld (ix+zTrack.Volume), a ; Then store it back
endif
push bc ; Save bc
call zSendTL ; Send new volume
pop bc ; Restore bc
add ix, de ; Advance to next track
djnz .fm_loop ; Loop for all tracks
if fix_sndbugs
ld hl, zFadeInTimeout ; Get fading timeout
dec (hl) ; Decrement it
else
ld a, (zFadeInTimeout) ; Get fading timeout
dec a ; Decrement it
ld (zFadeInTimeout), a ; Then store it back
endif
ret nz ; Return if still fading
ld b, (zTracksEnd-zSongPSG1)/zTrack.len ; Number of PSG tracks
ld ix, zSongPSG1 ; ix = start of PSG RAM
ld de, zTrack.len ; Spacing between tracks
.psg_loop:
res 2, (ix+zTrack.PlaybackControl) ; Clear 'SFX is overriding' bit
add ix, de ; Advance to next track
djnz .psg_loop ; Loop for all tracks
ld ix, zSongFM6_DAC ; ix = start of DAC/FM6 RAM
res 2, (ix+zTrack.PlaybackControl) ; Clear 'SFX is overriding' bit
ret
; End of function zDoMusicFadeIn
; =============== S U B R O U T I N E =======================================
; Wipes music data and fades all FM, PSG and DAC channels.
;sub_944 zMusicFade
zStopAllSound:
; The following block sets to zero the z80 RAM from 1C0Dh to 1FD4h
ld hl, zTempVariablesStart ; Starting source address for copy
ld de, zTempVariablesStart+1 ; Starting destination address for copy
if fix_sndbugs
ld bc, zTempVariablesEnd-zTempVariablesStart-1 ; Length of copy
else
ld bc, zTempVariablesEnd-zTempVariablesStart-1+34h ; Length of copy
endif
ld (hl), 0 ; Initial value of zero
ldir ; while (--length) *de++ = *hl++
xor a ; a = 0
ld (zTempoSpeedup), a ; Fade in normal speed
ld ix, zFMDACInitBytes ; Initialization data for channels
ld b, 6 ; Number of FM channels
.loop:
push bc ; Save bc for loop
if fix_sndbugs=0
call zFMSilenceChannel ; Silence track's channel
else
; Inline it because zKeyOnOff tries to write to ix+0, which we don't want
call zSetMaxRelRate
ld a, 40h ; Set total level...
ld c, 7Fh ; ... to minimum envelope amplitude...
call zFMOperatorWriteLoop ; ... for all operators of this track's channel
ld a, 28h ; Write to KEY ON/OFF port
ld c, (ix+zTrack.VoiceControl) ; Send key off
call zWriteFMI ; Send it
endif
call zFMClearSSGEGOps ; Clears the SSG-EG operators for this channel
inc ix ; Go to next channel byte
inc ix ; But skip the 80h
pop bc ; Restore bc for loop counter
djnz .loop ; Loop while b > 0
if fix_sndbugs=0
ld b, 7 ; Unused
xor a ; a = 0
ld (zFadeOutTimeout), a ; Set fade timeout to zero... again
endif
call zPSGSilenceAll ; Silence PSG
ld c, 0 ; Write a zero...
ld a, 2Bh ; ... to DAC enable register
call zWriteFMI ; Disable DAC
;loc_979
zFM3NormalMode:
if fix_sndbugs
ld c, 0 ; FM3 mode: normal mode
else
xor a ; a = 0 (is 0Fh in Z80 Type 1)
ld (zFM3Settings), a ; Save FM3 settings
ld c, a ; FM3 mode: normal mode
endif
ld a, 27h ; FM3 special settings
call zWriteFMI ; Set it
jp zClearNextSound
; End of function zStopAllSound
; =============== S U B R O U T I N E =======================================
; Sets the SSG-EG registers (90h+) for all operators on this track to 0.
;
; Input: ix Pointer to track RAM
; Output: a Damaged
; b Damaged
; c Damaged
;sub_986
zFMClearSSGEGOps:
ld a, 90h ; Set SSG-EG registers...
ld c, 0 ; ... set to zero (as docs say it should)...
jp zFMOperatorWriteLoop ; ... for all operators of this track's channel
; End of function zFMClearSSGEGOps
; =============== S U B R O U T I N E =======================================
; Pauses all audio.
;loc_98D
zPauseAudio:
if fix_sndbugs=0
call zPSGSilenceAll ; Redundant, as function falls-through to it anyway
endif
push bc ; Save bc
push af ; Save af
ld b, (zSongFM4-zSongFM1)/zTrack.len ; FM1/FM2/FM3
ld a, 0B4h ; Command to select AMS/FMS/panning register (FM1)
ld c, 0 ; AMS=FMS=panning=0
.loop1:
push af ; Save af
call zWriteFMI ; Write reg/data pair to YM2612
pop af ; Restore af
inc a ; Advance to next channel
djnz .loop1 ; Loop for all channels
ld b, (zSongPSG1-zSongFM4)/zTrack.len ; FM4 and FM5, but not FM6
ld a, 0B4h ; Command to select AMS/FMS/panning register
.loop2:
push af ; Save af
call zWriteFMII ; Write reg/data pair to YM2612
pop af ; Restore af
inc a ; Advance to next channel
djnz .loop2 ; Loop for all channels
ld c, 0 ; Note off for all operators
ld b, (zSongPSG1-zSongFM1)/zTrack.len+1 ; 5 FM channels + 1 gap between FM3 and FM4
ld a, 28h ; Command to send note on/off
.loop3:
push af ; Save af
call zWriteFMI ; Write reg/data pair to YM2612
inc c ; Next channel
pop af ; Restore af
djnz .loop3 ; Loop for all channels
pop af ; Restore af
pop bc ; restore bc and fall through
; =============== S U B R O U T I N E =======================================
; Silences all PSG channels, including the noise channel.
;
; Output: a Damaged
;sub_9BC
zPSGSilenceAll:
push bc ; Save bc
ld b, 4 ; Loop 4 times: 3 PSG channels + noise channel
ld a, 9Fh ; Command to silence PSG1
.loop:
ld (zPSG), a ; Write command
add a, 20h ; Next channel
djnz .loop ; Loop for all PSG channels
pop bc ; Restore bc
jp zClearNextSound
; End of function zPSGSilenceAll
; =============== S U B R O U T I N E =======================================
; Tempo works as divisions of the 60Hz clock (there is a fix supplied for
; PAL that "kind of" keeps it on track.) Every time the internal music clock
; does NOT overflow, it will update. So a tempo of 80h will update every
; other frame, or 30 times a second.
;sub_9CC:
TempoWait:
ld a, (zCurrentTempo) ; Get current tempo value
ld hl, zTempoAccumulator ; hl = pointer to tempo accumulator
add a, (hl) ; a += tempo accumulator
ld (hl), a ; Store it as new accumulator value
ret nc ; If the addition did not overflow, return
ld hl, zTracksStart+zTrack.DurationTimeout ; Duration timeout of first track
ld de, zTrack.len ; Spacing between tracks
ld b, (zTracksEnd-zTracksStart)/zTrack.len ; Number of tracks
.loop:
inc (hl) ; Delay notes another frame
add hl, de ; Advance to next track
djnz .loop ; Loop for all channels
ret
; End of function TempoWait
; =============== S U B R O U T I N E =======================================
; Copies over M68K input to the sound queue and clears the input variables
;sub_9E2
zFillSoundQueue:
ld hl, zMusicNumber ; M68K input
ld de, zSoundQueue0 ; Sound queue
ldi ; *de++ = *hl++
ldi ; *de++ = *hl++
ldi ; *de++ = *hl++
xor a ; a = 0
dec hl ; Point to zSFXNumber1
ld (hl), a ; Clear it
dec hl ; Point to zSFXNumber0
ld (hl), a ; Clear it
dec hl ; Point to zMusicNumber
ld (hl), a ; Clear it
ret
; End of function zFillSoundQueue
; =============== S U B R O U T I N E =======================================
; Sets D1L to minimum, RR to maximum and TL to minimum amplitude for all
; operators on this track's channel, then sends note off for the same channel.
;
; Input: ix Pointer to track RAM
; Output: a Damaged
; b Damaged
; c Damaged
;sub_9F6
zFMSilenceChannel:
call zSetMaxRelRate
ld a, 40h ; Set total level...
ld c, 7Fh ; ... to minimum envelope amplitude...
call zFMOperatorWriteLoop ; ... for all operators of this track's channel
ld c, (ix+zTrack.VoiceControl) ; Send key off
jp zKeyOnOff ; This does not safeguard against PSG tracks, making cfSilenceStopTrack dangerous (and zStopSFX even more so)
; End of function zFMSilenceChannel
; =============== S U B R O U T I N E =======================================
; Sets D1L to minimum and RR to maximum for all operators on this track's
; channel.
;
; Input: ix Pointer to track RAM
; Output: a Damaged
; b Damaged
; c Damaged
;sub_A06
;zSetFMMinD1LRR
zSetMaxRelRate:
ld a, 80h ; Set D1L to minimum and RR to maximum...
ld c, 0FFh ; ... for all operators on this track's channel (fall through)
; End of function zSetMaxRelRate
; =============== S U B R O U T I N E =======================================
; Loops through all of a channel's operators and sets them to the desired value.
;
; Input: ix Pointer to track RAM
; a YM2612 register to write to
; c Value to write to register
; Output: b Damaged
;sub_A0A
zFMOperatorWriteLoop:
ld b, 4 ; Loop 4 times
.loop:
push af ; Save af
call zWriteFMIorII ; Write to part I or II, as appropriate
pop af ; Restore af
add a, 4 ; a += 4
djnz .loop ; Loop
ret
; End of function zFMOperatorWriteLoop
; ---------------------------------------------------------------------------
;loc_A16
zPlaySegaSound:
call zStopAllSound ; Fade music before playing the sound
if fix_sndbugs
xor a ; a = 0
ld (zMusicNumber), a ; Clear M68K input queue...
ld (zSFXNumber0), a ; ... including SFX slot 0...
ld (zSFXNumber1), a ; ... and SFX slot 1
ld (zSoundQueue0), a ; Also clear music queue entry 0...
ld (zSoundQueue1), a ; ... and entry 1...
ld (zSoundQueue2), a ; ... and entry 2
inc a ; a = 1
else
ld a, 1 ; a = 1
endif
ld (PlaySegaPCMFlag), a ; Set flag to play SEGA sound
pop hl ; Don't return to caller of zCycleSoundQueue
ret
; =============== S U B R O U T I N E =======================================
; Performs massive restoration and starts fade in of previous music.
;
;sub_A20
zFadeInToPrevious:
xor a ; a = 0
ld (zFadeToPrevFlag), a ; Clear fade-to-prev flag
ld a, (zCurrentTempoSave) ; Get saved current tempo
ld (zCurrentTempo), a ; Restore it
ld a, (zTempoSpeedupSave) ; Get saved tempo speed-up
ld (zTempoSpeedup), a ; Restore it
ld hl, (zVoiceTblPtrSave) ; Get saved voice pointer
ld (zVoiceTblPtr), hl ; Restore it
ld a, (zSongBankSave) ; Get saved song bank ID
ld (zSongBank), a ; Restore it
bankswitch2 ; Bank switch to previous song's bank
ld hl, zTracksSaveStart ; Start of saved track data
ld de, zTracksStart ; Start of track data
ld bc, zTracksSaveEnd-zTracksSaveStart ; Number of bytes to copy
ldir ; while (bc-- > 0) *de++ = *hl++;
if fix_sndbugs
ld hl, zSongFM6_DAC.PlaybackControl
ld a, 84h ; a = 'track is playing' and 'track is resting' flags
or (hl) ; Add in track playback control bits
ld (hl), a ; Save everything
else
ld a, (zSongFM6_DAC.PlaybackControl) ; a = FM6/DAC track playback control
or 84h ; Set 'track is playing' and 'track is resting' flags
ld (zSongFM6_DAC.PlaybackControl), a ; Set new value
endif
ld ix, zSongFM1 ; ix = pointer to FM1 track RAM
ld b, (zTracksEnd-zSongFM1)/zTrack.len ; Number of FM+PSG tracks
.loop:
ld a, (ix+zTrack.PlaybackControl) ; a = track playback control
or 84h ; Set 'track is playing' and 'track is resting' flags
ld (ix+zTrack.PlaybackControl), a ; Set new value
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
jp nz, .skip_track ; Branch if yes
res 2, (ix+zTrack.PlaybackControl) ; Clear 'SFX is overriding track' flag
ld a, (ix+zTrack.Volume) ; Get track volume
add a, 40h ; Lower volume by 40h
ld (ix+zTrack.Volume), a ; Store new volume
ld a, (ix+zTrack.VoiceIndex) ; a = FM instrument
push bc ; Save bc
ld b, a ; b = FM instrument
call zGetFMInstrumentPointer ; hl = pointer to instrument data
call zSendFMInstrument ; Send instrument
pop bc ; Restore bc
.skip_track:
ld de, zTrack.len ; Spacing between tracks
add ix, de ; ix = pointer to next track
djnz .loop ; Loop for all tracks
ld a, 40h ; a = 40h
ld (zFadeInTimeout), a ; Start fade
ld a, 2 ; a = 2
ld (zFadeDelayTimeout), a ; Set fade delay timeout
ld (zFadeDelay), a ; Set fade delay
ret
; End of function zFadeInToPrevious
; ---------------------------------------------------------------------------
;loc_AA5
zPSGFrequencies:
; This table starts with 12 notes not in S1 or S2:
dw 3FFh,3FFh,3FFh,3FFh,3FFh,3FFh,3FFh,3FFh,3FFh,3F7h,3BEh,388h
; The following notes are present on S1 and S2 too:
dw 356h,326h,2F9h,2CEh,2A5h,280h,25Ch,23Ah,21Ah,1FBh,1DFh,1C4h
dw 1ABh,193h,17Dh,167h,153h,140h,12Eh,11Dh,10Dh,0FEh,0EFh,0E2h
dw 0D6h,0C9h,0BEh,0B4h,0A9h,0A0h,097h,08Fh,087h,07Fh,078h,071h
dw 06Bh,065h,05Fh,05Ah,055h,050h,04Bh,047h,043h,040h,03Ch,039h
dw 036h,033h,030h,02Dh,02Bh,028h,026h,024h,022h,020h,01Fh,01Dh
dw 01Bh,01Ah,018h,017h,016h,015h,013h,012h,011h,010h,000h,000h
; Then, it falls through to the 12 base notes from FM octaves.
;loc_B4D
zFMFrequencies:
dw 284h,2ABh,2D3h,2FEh,32Dh,35Ch,38Fh,3C5h,3FFh,43Ch,47Ch,4C0h
; ---------------------------------------------------------------------------
; ===========================================================================
; MUSIC BANKS
; ===========================================================================
z80_MusicBanks:
db zmake68kBank(Snd_AIZ1),zmake68kBank(Snd_AIZ2),zmake68kBank(Snd_HCZ1),zmake68kBank(Snd_HCZ2)
db zmake68kBank(Snd_MGZ1),zmake68kBank(Snd_MGZ2),zmake68kBank(Snd_CNZ1),zmake68kBank(Snd_CNZ2)
db zmake68kBank(Snd_FBZ1),zmake68kBank(Snd_FBZ2),zmake68kBank(Snd_ICZ1),zmake68kBank(Snd_ICZ2)
db zmake68kBank(Snd_LBZ1),zmake68kBank(Snd_LBZ2),zmake68kBank(Snd_MHZ1),zmake68kBank(Snd_MHZ2)
db zmake68kBank(Snd_SOZ1),zmake68kBank(Snd_SOZ2),zmake68kBank(Snd_LRZ1),zmake68kBank(Snd_LRZ2)
db zmake68kBank(Snd_SSZ),zmake68kBank(Snd_DEZ1),zmake68kBank(Snd_DEZ2),zmake68kBank(Snd_Minib_SK)
db zmake68kBank(Snd_Boss),zmake68kBank(Snd_DDZ),zmake68kBank(Snd_PachBonus),zmake68kBank(Snd_SpecialS)
db zmake68kBank(Snd_SlotBonus),zmake68kBank(Snd_GumBonus),zmake68kBank(Snd_Knux),zmake68kBank(Snd_ALZ)
db zmake68kBank(Snd_BPZ),zmake68kBank(Snd_DPZ),zmake68kBank(Snd_CGZ),zmake68kBank(Snd_EMZ)
db zmake68kBank(Snd_Title),zmake68kBank(Snd_S3Credits),zmake68kBank(Snd_GameOver),zmake68kBank(Snd_Continue)
db zmake68kBank(Snd_Results),zmake68kBank(Snd_1UP),zmake68kBank(Snd_Emerald),zmake68kBank(Snd_Invic)
db zmake68kBank(Snd_2PMenu),zmake68kBank(Snd_Minib_SK),zmake68kBank(Snd_Menu),zmake68kBank(Snd_FinalBoss)
db zmake68kBank(Snd_Drown),zmake68kBank(Snd_PresSega),zmake68kBank(Snd_SKCredits)
; =============== S U B R O U T I N E =======================================
;
;sub_B98
zUpdateDACTrack:
if fix_sndbugs
dec (ix+zTrack.DurationTimeout) ; Advance track duration timer
else
call zTrackRunTimer ; Advance track duration timer
endif
ret nz ; Return if note is still going
ld e, (ix+zTrack.DataPointerLow) ; e = low byte of track data pointer
ld d, (ix+zTrack.DataPointerHigh) ; d = high byte of track data pointer
;loc_BA2
zUpdateDACTrack_cont:
ld a, (de) ; Get next byte from track
inc de ; Advance pointer
cp FirstCoordFlag ; Is it a coordination flag?
jp nc, zHandleDACCoordFlag ; Branch if yes
or a ; Is it a note?
jp m, .got_sample ; Branch if yes
dec de ; We got a duration, so go back to it
ld a, (ix+zTrack.SavedDAC) ; Reuse previous DAC sample
.got_sample:
ld (ix+zTrack.SavedDAC), a ; Store new DAC sample
cp NoteRest ; Is it a rest?
jp z, zUpdateDACTrack_GetDuration ; Branch if yes
res 7, a ; Clear bit 7
push de ; Save de
ex af, af' ; Save af
call zKeyOffIfActive ; Kill note (will do nothing if 'do not attack' is on)
call zFM3NormalMode ; Set FM3 to normal mode
ex af, af' ; Restore af
ld ix, zSongFM6_DAC ; ix = pointer to DAC track data
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding DAC channel?
jp nz, .dont_play ; Branch if yes
ld (zDACIndex), a ; Queue DAC sample
.dont_play:
pop de ; Restore de
zUpdateDACTrack_GetDuration:
ld a, (de) ; Get note duration
inc de ; Advance pointer
or a ; Is it a duration?
jp p, zStoreDuration ; Branch if yes
dec de ; Put the byte back to the stream
if fix_sndbugs=0
ld a, (ix+zTrack.SavedDuration) ; Reuse last duration (zFinishTrackUpdate should do it...)
ld (ix+zTrack.DurationTimeout), a ; Set new duration timeout (zFinishTrackUpdate should do it...)
endif
jp zFinishTrackUpdate
; ---------------------------------------------------------------------------
;loc_BE3
zHandleDACCoordFlag:
ld hl, loc_BE9 ; hl = desired return address
jp zHandleCoordFlag
; ---------------------------------------------------------------------------
loc_BE9:
inc de ; Advance to next byte in track
jp zUpdateDACTrack_cont ; Continue processing DAC track
; ---------------------------------------------------------------------------
;loc_BED
zHandleFMorPSGCoordFlag:
ld hl, loc_BF9 ; hl = desired return address
;loc_BF0
zHandleCoordFlag:
push hl ; Set return location (ret) to location stored in hl
sub FirstCoordFlag ; Make it a zero-based index
ld hl, zCoordFlagSwitchTable ; Load switch table into hl
rst PointerTableOffset ; hl = pointer to target location
ld a, (de) ; a = coordination flag parameter
jp (hl) ; Indirect jump to coordination flag handler
; End of function zUpdateDACTrack
; ---------------------------------------------------------------------------
loc_BF9:
inc de ; Advance to next byte in track
jp zGetNextNote_cont ; Continue processing FM/PSG track
; ---------------------------------------------------------------------------
;loc_BFD
zCoordFlagSwitchTable:
dw cfPanningAMSFMS ; 0E0h
dw cfDetune ; 0E1h
dw cfFadeInToPrevious ; 0E2h
dw cfSilenceStopTrack ; 0E3h
dw cfSetVolume ; 0E4h
dw cfChangeVolume2 ; 0E5h
dw cfChangeVolume ; 0E6h
dw cfPreventAttack ; 0E7h
dw cfNoteFill ; 0E8h
dw cfSpindashRev ; 0E9h
dw cfPlayDACSample ; 0EAh
dw cfConditionalJump ; 0EBh
dw cfChangePSGVolume ; 0ECh
dw cfSetKey ; 0EDh
dw cfSendFMI ; 0EEh
dw cfSetVoice ; 0EFh
dw cfModulation ; 0F0h
dw cfAlterModulation ; 0F1h
dw cfStopTrack ; 0F2h
dw cfSetPSGNoise ; 0F3h
dw cfSetModulation ; 0F4h
dw cfSetPSGVolEnv ; 0F5h
dw cfJumpTo ; 0F6h
dw cfRepeatAtPos ; 0F7h
dw cfJumpToGosub ; 0F8h
dw cfJumpReturn ; 0F9h
dw cfDisableModulation ; 0FAh
dw cfChangeTransposition ; 0FBh
dw cfLoopContinuousSFX ; 0FCh
dw cfToggleAltFreqMode ; 0FDh
dw cfFM3SpecialMode ; 0FEh
dw cfMetaCF ; 0FFh
;loc_C3D
zExtraCoordFlagSwitchTable:
dw cfSetTempo ; 0FFh 00h
dw cfPlaySoundByIndex ; 0FFh 01h
dw cfHaltSound ; 0FFh 02h
dw cfCopyData ; 0FFh 03h
dw cfSetTempoDivider ; 0FFh 04h
dw cfSetSSGEG ; 0FFh 05h
dw cfFMVolEnv ; 0FFh 06h
dw cfResetSpindashRev ; 0FFh 07h
dw cfChanSetTempoDivider ; 0FFh 08h
dw cfChanFMCommand ; 0FFh 09h
dw cfNoteFillSet ; 0FFh 0Ah
dw cfPitchSlide ; 0FFh 0Bh
dw cfSetLFO ; 0FFh 0Ch
dw cfPlayMusicByIndex ; 0FFh 0Dh
; =============== S U B R O U T I N E =======================================
; Sets a new DAC sample for play.
;
; Has one parameter, the index (1-based) of the DAC sample to play.
;
;sub_C4D
cfPlayDACSample:
ld (zDACIndex), a ; Set next DAC sample to the parameter byte
ret
; End of function cfPlayDACSample
; =============== S U B R O U T I N E =======================================
; Sets panning for track. By accident, you can sometimes set AMS and FMS
; flags -- but only if the bits in question were zero.
;
; Has one parameter byte, the AMS/FMS/panning bits.
;
;sub_C51
cfPanningAMSFMS:
ld c, 3Fh ; Mask for all but panning
zDoChangePan:
ld a, (ix+zTrack.AMSFMSPan) ; Get current AMS/FMS/panning
and c ; Mask out L+R
push de ; Store de
ex de, hl ; Exchange de and hl
or (hl) ; Mask in the new panning; may also add AMS/FMS
ld (ix+zTrack.AMSFMSPan), a ; Store new value in track RAM
ld c, a ; c = new AMS/FMS/panning
ld a, 0B4h ; a = YM2612 register to write to
call zWriteFMIorII ; Set new panning/AMS/FMS
pop de ; Restore de
ret
; End of function cfPanningAMSFMS
; =============== S U B R O U T I N E =======================================
; Enables or disables the LFO.
;
; Has two parameter bytes: the first one is sent directly to the LFO enable
; register: bit 3 is the enable flag, bits 0-2 are the frequency of the LFO.
; Second parameter byte specifies AMS/FMS sensibility for the channel.
cfSetLFO:
ld c, a ; Copy parameter byte
ld a, 22h ; LFO enable/frequency
call zWriteFMI ; Send it
inc de ; Advance pointer
ld c, 0C0h ; Mask for only panning
jr zDoChangePan
; =============== S U B R O U T I N E =======================================
; Performs an escalating transposition ("revving") of the track.
;
; The saved value for the spindash rev is reset to zero every time a "normal"
; SFX is played (i.e., not a continuous SFX and not the spindash sound).
; Every time this function is called, the spindash rev value is added to the
; track key offset; unless this sum is exactly 10h, then the spindash rev is
; further increased by 1 for future calls.
;
; Has no parameter bytes.
;
;sub_C65
cfSpindashRev:
ld hl, zSpindashRev ; hl = pointer to escalating transposition
ld a, (hl) ; a = value of escalating transposition
add a, (ix+zTrack.Transpose) ; Add in current track transposition
ld (ix+zTrack.Transpose), a ; Store result as new track transposition
cp 10h ; Is the current transposition 10h?
jp z, .skip_rev ; Branch if yes
inc (hl) ; Otherwise, increase escalating transposition
.skip_rev:
dec de ; Put parameter byte back
ret
; End of function cfSpindashRev
; =============== S U B R O U T I N E =======================================
; Sets frequency displacement (signed). The final note frequency is shifted
; by this value.
;
; Has one parameter byte, the new frequency displacement.
;
;sub_C77 cfAlterNoteFreq
cfDetune:
ld (ix+zTrack.Detune), a ; Set detune
ret
; End of function cfDetune
; =============== S U B R O U T I N E =======================================
; Fade in to previous song.
;
; Has one parameter byte. If the parameter byte if FFh, the engine will fade
; to the previous song. If the parameter byte is equal to 29h (1-Up ID - 1),
; the driver will prevent new music or SFX from playing as long as the 1-Up
; music is playing (but will not clear the M68K queue). For all other values,
; the queue will work as normal, but no fade-in will be done.
;
;sub_C7B
cfFadeInToPrevious:
ld (zFadeToPrevFlag), a
ret
; End of function cfFadeInToPrevious
; =============== S U B R O U T I N E =======================================
; Silences FM channel and stops track. Expanded form of coord. flag 0F2h.
;
; Technically, it has a parameter byte, but it is irrelevant and unused.
;
;loc_C7F
cfSilenceStopTrack:
if fix_sndbugs
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
call z, zFMSilenceChannel ; If so, don't mess with the YM2612
else
call zFMSilenceChannel
endif
jp cfStopTrack
; End of function cfSilenceStopTrack
; =============== S U B R O U T I N E =======================================
; Sets track volume.
;
; Has one parameter byte, the volume.
;
; For FM tracks, this is a 7-bit value from 0 (lowest volume) to 127 (highest
; volume). The value is XOR'ed with 7Fh before being sent, then stripped of the
; sign bit. The volume change takes effect immediately.
;
; For PSG tracks, this is a 4-bit value ranging from 8 (lowest volume) to 78h
; (highest volume). The value is shifted 3 bits to the right, XOR'ed with 0Fh
; and AND'ed with 0Fh before being uploaded, so the sign bit and the lower 3
; bits are discarded.
;
;loc_C85
cfSetVolume:
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG channel?
jr z, .not_psg ; Branch if not
; The following code gets bits 3, 4, 5 and 6 from the parameter byte,
; puts them in positions 0 to 3 and inverts them, discarding all other
; bits in the parameter byte.
; Shift the parameter byte 3 bits to the right
srl a
srl a
srl a
xor 0Fh ; Invert lower nibble's bits
and 0Fh ; Clear out high nibble
jp zStoreTrackVolume
; ---------------------------------------------------------------------------
.not_psg:
xor 7Fh ; Invert parameter byte (except irrelevant sign bit)
and 7Fh ; Strip sign bit
ld (ix+zTrack.Volume), a ; Set as new track volume
jr zSendTL ; Begin using new volume immediately
; =============== S U B R O U T I N E =======================================
; Change track volume for a FM track.
;
; Has two parameter bytes: the first byte is ignored, the second is the signed
; change in volume. Positive lowers volume, negative increases it.
;
;loc_CA1
cfChangeVolume2:
inc de ; Advance pointer
ld a, (de) ; Get change in volume then fall-through
; =============== S U B R O U T I N E =======================================
; Change track volume for a FM track.
;
; Has one parameter byte, the signed change in volume. Positive lowers volume,
; negative increases it.
;
;loc_CA3
cfChangeVolume:
; S2 places this check further down (and S1 lacks it altogether),
; allowing PSG channels to change their volume. This means the
; likes of S2's SFX $F0 will sound different in this driver
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
ret nz ; Return if yes
add a, (ix+zTrack.Volume) ; Add in track's current volume
jp p, .set_vol ; Branch if result is still positive
jp pe, .underflow ; Branch if addition overflowed into more than 127 positive
xor a ; Set maximum volume
jp .set_vol
; ---------------------------------------------------------------------------
.underflow:
ld a, 7Fh ; Set minimum volume
.set_vol:
ld (ix+zTrack.Volume), a ; Store new volume
; =============== S U B R O U T I N E =======================================
; Subroutine to send TL information to YM2612.
;
;sub_CBA
zSendTL:
push de ; Save de
ld de, zFMInstrumentTLTable ; de = pointer to FM TL register table
ld l, (ix+zTrack.TLPtrLow) ; l = low byte of pointer to instrument's TL data
ld h, (ix+zTrack.TLPtrHigh) ; h = high byte of pointer to instrument's TL data
ld b, zFMInstrumentTLTable_End-zFMInstrumentTLTable ; Number of entries
.loop:
ld a, (hl) ; a = register data
or a ; Is it positive?
jp p, .skip_track_vol ; Branch if yes
add a, (ix+zTrack.Volume) ; Add track's volume to it
.skip_track_vol:
and 7Fh ; Strip sign bit
ld c, a ; c = new volume for operator
ld a, (de) ; a = register write command
call zWriteFMIorII ; Send it to YM2612
inc de ; Advance pointer
inc hl ; Advance pointer
djnz .loop ; Loop
pop de ; Restore de
ret
; End of function zSendTL
; =============== S U B R O U T I N E =======================================
; Prevents next note from attacking.
;
; Has no parameter bytes.
;
;loc_CDB
cfPreventAttack:
set 1, (ix+zTrack.PlaybackControl) ; Set flag to prevent attack
dec de ; Put parameter byte back
ret
; =============== S U B R O U T I N E =======================================
; Sets the note fill.
;
; Has one parameter byte, the new note fill. This value is multiplied by the
; tempo divider, and so may overflow.
;
;loc_CE1
cfNoteFill:
call zComputeNoteDuration ; Multiply note fill by tempo divider
cfNoteFillSet:
ld (ix+zTrack.NoteFillTimeout), a ; Store result into note fill timeout
ld (ix+zTrack.NoteFillMaster), a ; Store master copy of note fill
ret
; =============== S U B R O U T I N E =======================================
; Jump timeout. Shares the same loop counters as coord. flag 0E7h, so it has
; to be coordinated with these. Each time this coord. flag is encountered, it
; tests if the associated loop counter is 1. If it is, it will jump to the
; target location and the loop counter will be set to zero; otherwise, nothing
; happens.
;
; Has 3 parameter bytes: a loop counter index (identical to that of coord. flag
; 0E7h), and a 2-byte jump target.
;
;loc_CEB
cfConditionalJump:
inc de ; Advance track pointer
add a, zTrack.LoopCounters ; Add offset into loop counters
ld c, a ; c = offset of current loop counter
ld b, 0 ; bc = sign-extended offset to current loop counter
push ix ; Save track RAM pointer
pop hl ; hl = pointer to track RAM
add hl, bc ; hl = pointer in RAM to current loop counter
ld a, (hl) ; a = value of current loop counter
dec a ; Decrement loop counter (note: value is not saved!)
jp z, .do_jump ; Branch if result is zero
inc de ; Skip another byte
ret
; ---------------------------------------------------------------------------
.do_jump:
xor a ; a = 0
ld (hl), a ; Clear loop counter
jp cfJumpTo
; =============== S U B R O U T I N E =======================================
; Change PSG volume. Has no effect on FM or DAC channels.
;
; Has one parameter byte, the change in volume. The value is signed, but any
; result greater than 0Fh (or lower than 0) will result in no output.
;
;loc_D01
cfChangePSGVolume:
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG channel?
ret z ; Return if not
res 4, (ix+zTrack.PlaybackControl) ; Clear 'track is resting' flag
dec (ix+zTrack.VolEnv) ; Decrement envelope index
add a, (ix+zTrack.Volume) ; Add track's current volume
cp 0Fh ; Is it 0Fh or more?
jp c, zStoreTrackVolume ; Branch if not
ld a, 0Fh ; Limit to 0Fh (silence)
;loc_D17
zStoreTrackVolume:
ld (ix+zTrack.Volume), a ; Store new volume
ret
; =============== S U B R O U T I N E =======================================
; Changes the track's key displacement.
;
; There is a single parameter byte, the new track key offset + 40h (that is,
; 40h is subtracted from the parameter byte before the key displacement is set)
;
;loc_D1B
cfSetKey:
sub 40h ; Subtract 40h from key displacement
ld (ix+zTrack.Transpose), a ; Store result as new transposition
ret
; =============== S U B R O U T I N E =======================================
; Sends an FM command to the YM2612. The command is sent to part I, so not all
; registers can be set using this coord. flag (in particular, channels FM4,
; FM5 and FM6 cannot (in general) be affected).
;
; Has 2 parameter bytes: a 1-byte register selector and a 1-byte register data.
;
;loc_D21
cfSendFMI:
call zGetFMParams ; Get parameters for FM command
if fix_sndbugs
jp zWriteFMI ; Send it to YM2612
else
call zWriteFMI ; Send it to YM2612
ret
endif
;loc_D28
zGetFMParams:
ex de, hl ; Exchange de and hl
ld a, (hl) ; Get YM2612 register selector
inc hl ; Advance pointer
ld c, (hl) ; Get YM2612 register data
ex de, hl ; Exchange back de and hl
ret
; End of function cfSendFMI
; =============== S U B R O U T I N E =======================================
; Change current instrument (FM), tone (PSG) or sample (DAC).
;
; Has either a single positive parameter byte or a pair of parameter bytes of
; which the first is negative.
;
; If positive, the first parameter byte is the index of voice to use.
;
; If negative, and on a PSG track, then the first parameter byte is the index
; of voice to use while the second parameter byte is ignored.
;
; If negative and on a FM or DAC track, the first parameter byte is 80h + index
; of voice to use, while the second parameter byte is 7Fh + index of song whose
; voice bank is to be used (here, the AIZ1 song is index 1, not zero).
;
; The uploading of an FM instrument is irrelevant for the DAC.
;
;loc_D2E
cfSetVoice:
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
jr nz, zSetVoicePSG ; Branch if yes
call zSetMaxRelRate ; Set minimum D1L/RR for channel
ld a, (de) ; Get voice index
ld (ix+zTrack.VoiceIndex), a ; Store to track RAM
or a ; Is it negative?
jp p, zSetVoiceUpload ; Branch if not
inc de ; Advance pointer
ld a, (de) ; Get song ID whose bank is desired
ld (ix+zTrack.VoiceSongID), a ; Store to track RAM and fall-through
; =============== S U B R O U T I N E =======================================
; Uploads the FM instrument from another song's voice bank.
;
;sub_D44
zSetVoiceUploadAlter:
push de ; Save de
ld a, (ix+zTrack.VoiceSongID) ; Get saved song ID for instrument data
sub 81h ; Convert it to a zero-based index
ld c, zID_MusicPointers ; Value for music pointer table
rst GetPointerTable ; hl = pointer to music pointer table
rst PointerTableOffset ; hl = pointer to music data
rst ReadPointer ; hl = pointer to music voice data
ld a, (ix+zTrack.VoiceIndex) ; Get voice index
and 7Fh ; Strip sign bit
ld b, a ; b = voice index
call zGetFMInstrumentOffset ; hl = pointer to voice data
jr zSetVoiceDoUpload
; ---------------------------------------------------------------------------
;loc_D5A
zSetVoiceUpload:
push de ; Save de
ld b, a ; b = instrument index
call zGetFMInstrumentPointer ; hl = pointer to instrument data
zSetVoiceDoUpload:
call zSendFMInstrument ; Upload instrument data to YM2612
pop de ; Restore de
ret
; End of function cfSetVoice
; ---------------------------------------------------------------------------
;loc_D64:
zSetVoicePSG:
or a ; Is the voice index positive?
jp p, cfStoreNewVoice ; Branch if yes
inc de ; Otherwise, advance song data pointer
jp cfStoreNewVoice
; ---------------------------------------------------------------------------
if fix_sndbugs=0
ret ; Dead code
endif
; =============== S U B R O U T I N E =======================================
; Turns on modulation on the channel.
;
; Has four 1-byte parameters: delay before modulation starts, modulation speed,
; modulation change per step, number of steps in modulation.
;
;loc_D6D
cfModulation:
ld (ix+zTrack.ModulationPtrLow), e ; Store low byte of modulation data pointer
ld (ix+zTrack.ModulationPtrHigh), d ; Store high byte of modulation data pointer
ld (ix+zTrack.ModulationCtrl), 80h ; Toggle modulation on
inc de ; Advance pointer...
inc de ; ... again...
inc de ; ... and again.
ret
; =============== S U B R O U T I N E =======================================
; Sets modulation status according to parameter bytes.
;
; Has 2 1-byte parameters: the first byte is the new modulation state for PSG
; tracks, while the second byte is the new modulation state for FM tracks.
;
;loc_D7B
cfAlterModulation:
inc de ; Advance track pointer
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
jr nz, cfSetModulation ; Branch if yes
ld a, (de) ; Get new modulation status
; =============== S U B R O U T I N E =======================================
; Sets modulation status.
;
; Has one parameter byte, the new modulation status.
;
;loc_D83
cfSetModulation:
ld (ix+zTrack.ModulationCtrl), a ; Set modulation status
ret
; =============== S U B R O U T I N E =======================================
; Stops the current track.
;
; Technically, it has a parameter byte, but it is irrelevant and unused.
;
;loc_D87
cfStopTrack:
res 7, (ix+zTrack.PlaybackControl) ; Clear 'track playing' flag
if fix_sndbugs=0
ld a, 1Fh ; a = 1Fh
ld (unk_1C15), a ; Set otherwise unused location to 1Fh
endif
call zKeyOffIfActive ; Send key off for track channel
ld c, (ix+zTrack.VoiceControl) ; c = voice control bits
push ix ; Save track pointer
call zGetSFXChannelPointers ; ix = track pointer, hl = overridden track pointer
ld a, (zUpdatingSFX) ; Get flag
or a ; Are we updating SFX?
jp z, zStopCleanExit ; Exit if not
if fix_sndbugs=0
xor a ; a = 0
ld (unk_1C18), a ; Set otherwise unused value to zero
endif
push hl ; Save hl
ld hl, (zVoiceTblPtr) ; hl = pointer to voice table
pop ix ; ix = overridden track's pointer
res 2, (ix+zTrack.PlaybackControl) ; Clear 'SFX is overriding' bit
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG channel?
jr nz, zStopPSGTrack ; Branch if yes
bit 7, (ix+zTrack.PlaybackControl) ; Is 'track playing' bit set?
jr z, zStopCleanExit ; Exit if not
ld a, 2 ; a = 2 (FM3)
cp (ix+zTrack.VoiceControl) ; Is this track for FM3?
jr nz, .not_fm3 ; Branch if not
ld a, 4Fh ; FM3 settings: special mode, enable and load A/B
bit 0, (ix+zTrack.PlaybackControl) ; Is FM3 in special mode?
jr nz, .do_fm3_settings ; Branch if yes
and 0Fh ; FM3 settings: normal mode, enable and load A/B
.do_fm3_settings:
call zWriteFM3Settings ; Set the above FM3 settings
.not_fm3:
ld a, (ix+zTrack.VoiceIndex) ; Get FM instrument
or a ; Is it positive?
jp p, .switch_to_music ; Branch if yes
call zSetVoiceUploadAlter ; Upload the voice from another song's voice bank
jr .send_ssg_eg
; ---------------------------------------------------------------------------
.switch_to_music:
ld b, a ; b = FM instrument
push hl ; Save hl
bankswitchToMusic ; Bank switch to song bank
pop hl ; Restore hl
call zGetFMInstrumentOffset ; hl = pointer to instrument data
call zSendFMInstrument ; Send FM instrument
ld a, zmake68kBank(SndBank) ; Get SFX bank
bankswitch2 ; Bank switch to it
ld a, (ix+zTrack.HaveSSGEGFlag) ; Get custom SSG-EG flag
or a ; Does track have custom SSG-EG data?
jp p, zStopCleanExit ; Exit if not
ld e, (ix+zTrack.SSGEGPointerLow) ; e = low byte of pointer to SSG-EG data
ld d, (ix+zTrack.SSGEGPointerHigh) ; d = high byte of pointer to SSG-EG data
.send_ssg_eg:
call zSendSSGEGData ; Upload custom SSG-EG data
;loc_E22
zStopCleanExit:
pop ix ; Restore ix
pop hl ; Pop return value from stack
pop hl ; Pop another return value from stack
ret
; ---------------------------------------------------------------------------
;loc_E27
zStopPSGTrack:
bit 0, (ix+zTrack.PlaybackControl) ; Is this a noise channel?
jr z, zStopCleanExit ; Exit if not
ld a, (ix+zTrack.PSGNoise) ; Get track's PSG noise setting
or a ; Is it an actual noise?
jp p, .skip_command ; Branch if not
ld (zPSG), a ; Send it to PSG
.skip_command:
jr zStopCleanExit
; =============== S U B R O U T I N E =======================================
; Change PSG noise to parameter, and silences PSG3 channel.
;
; Has one parameter byte: if zero, the channel is changed back to a normal PSG
; channel and the noise is silenced; if non-zero, it must be in the 0E0h-0E7h
; range, and sets the noise type to use (and sets the channel as being a noise
; channel).
;
;loc_E39
cfSetPSGNoise:
if fix_sndbugs
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
ret z ; Return if not
ld (ix+zTrack.PSGNoise), a ; Store to track RAM
set 0, (ix+zTrack.PlaybackControl) ; Mark PSG track as being noise
or a ; Test noise value
ld a, 0DFh ; Command to silence PSG3
jr nz, .skip_noise_silence ; If nonzero, branch
res 0, (ix+zTrack.PlaybackControl) ; Otherwise, mark track as not being a noise channel
ld a, 0FFh ; Command to silence the noise channel
.skip_noise_silence:
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding this track?
ret nz ; Return if yes
ld (zPSG), a ; Execute it
ld a, (de) ; Get PSG noise value
ld (zPSG), a ; Send command to PSG
else
bit 2, (ix+zTrack.VoiceControl) ; Is this a channel bound for part II (FM4, FM5, FM6/DAC)?
ret nz ; Return if yes
ld a, 0DFh ; Command to silence PSG3
ld (zPSG), a ; Execute it
ld a, (de) ; Get PSG noise value
ld (ix+zTrack.PSGNoise), a ; Store to track RAM
set 0, (ix+zTrack.PlaybackControl) ; Mark PSG track as being noise
or a ; Test noise value
jr nz, .skip_noise_silence ; If nonzero, branch
res 0, (ix+zTrack.PlaybackControl) ; Otherwise, mark track as not being a noise channel
ld a, 0FFh ; Command to silence the noise channel
.skip_noise_silence:
ld (zPSG), a ; Send command to PSG
endif
ret
; =============== S U B R O U T I N E =======================================
; Set PSG tone.
;
; Has one parameter byte, the new PSG tone to use.
;
;loc_E58
;cfSetPSGTone
cfSetPSGVolEnv:
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG track?
ret z ; Return if not
;loc_E5D
cfStoreNewVoice:
ld (ix+zTrack.VoiceIndex), a ; Store voice
ret
; =============== S U B R O U T I N E =======================================
; Jump to specified location.
;
; Has a 2-byte parameter, indicating target location for jump.
;
;loc_E61
cfJumpTo:
ex de, hl ; Exchange de and hl
ld e, (hl) ; e = low byte of target location
inc hl ; Advance pointer
ld d, (hl) ; d = high byte of target location
dec de ; Put destination byte back
ret
; =============== S U B R O U T I N E =======================================
; Starts or stops pitch sliding. Ported from Battletoads driver.
;
; Has a single parameter byte: if nonzero enables pitch slide, disables otherwise.
cfPitchSlide:
or a ; Is parameter nonzero?
jr z, .disable_slide ; Branch if not
set 5, (ix+zTrack.PlaybackControl) ; Enable pitch slide
ret
; ---------------------------------------------------------------------------
.disable_slide:
res 1, (ix+zTrack.PlaybackControl) ; Clear 'don't attack' flag
res 5, (ix+zTrack.PlaybackControl) ; Stop pitch slide
ld (ix+zTrack.Detune), a ; Clear detune (we already know a is zero)
ret
; =============== S U B R O U T I N E =======================================
; Loop section of music.
;
; Has four parameter bytes: a loop counter index (exactly like coord. flag 0EBh),
; a repeat count, and a 2-byte jump target.
;
;loc_E67
cfRepeatAtPos:
inc de ; Advance track pointer
add a, zTrack.LoopCounters ; Add offset into loop counters
ld c, a ; c = offset of current loop counter
ld b, 0 ; bc = sign-extended offset to current loop counter
push ix ; Save track RAM pointer
pop hl ; hl = pointer to track RAM
add hl, bc ; hl = pointer in RAM to current loop counter
ld a, (hl) ; a = value of current loop counter
or a ; Is loop counter zero?
jr nz, .run_counter ; Branch if not
ld a, (de) ; Get repeat counter
ld (hl), a ; Reset loop counter to it
.run_counter:
inc de ; Advance track pointer
dec (hl) ; Decrement loop counter
jp nz, cfJumpTo ; Loop if it is nonzero
inc de ; Advance track pointer
ret
; =============== S U B R O U T I N E =======================================
; Call subroutine. Stores current location on track-specific stack so that
; coord. flag 0F9h can be used to return to current location.
;
; Has one 2-byte parameter, the target subroutine's address.
;
;loc_E7E
cfJumpToGosub:
ld c, a ; c = low byte of target address
inc de ; Advance track pointer
ld a, (de) ; a = high byte of target address
ld b, a ; bc = target address
push bc ; Save bc
push ix ; Save ix
pop hl ; hl = pointer to track RAM
dec (ix+zTrack.StackPointer) ; Decrement track stack pointer
ld c, (ix+zTrack.StackPointer) ; c = track stack pointer
dec (ix+zTrack.StackPointer) ; Decrement track stack pointer again
ld b, 0 ; b = 0
add hl, bc ; hl = offset of high byte of return address
ld (hl), d ; Store high byte of return address
dec hl ; Move pointer to target location
ld (hl), e ; Store low byte of return address
pop de ; de = jump target address
dec de ; Put back the byte
ret
; =============== S U B R O U T I N E =======================================
; Returns from subroutine call. Does NOT check for stack overflows!
;
; Has no parameter bytes.
;
;loc_E98
cfJumpReturn:
push ix ; Save track RAM address
pop hl ; hl = pointer to track RAM
ld c, (ix+zTrack.StackPointer) ; c = offset to top of return stack
ld b, 0 ; b = 0
add hl, bc ; hl = pointer to top of return stack
ld e, (hl) ; e = low byte of return address
inc hl ; Advance pointer
ld d, (hl) ; de = return address
inc (ix+zTrack.StackPointer) ; Pop byte from return stack
inc (ix+zTrack.StackPointer) ; Pop byte from return stack
ret
; =============== S U B R O U T I N E =======================================
; Clears sign bit of modulation control, disabling normal modulation.
;
; Has no parameter bytes.
;
;loc_EAB
cfDisableModulation:
res 7, (ix+zTrack.ModulationCtrl) ; Clear bit 7 of modulation control
dec de ; Put byte back
ret
; =============== S U B R O U T I N E =======================================
; Adds a signed value to channel key displacement.
;
; Has one parameter byte, the change in channel key displacement.
;
;loc_EB1 cfAddKey
cfChangeTransposition:
add a, (ix+zTrack.Transpose) ; Add current transposition to parameter
ld (ix+zTrack.Transpose), a ; Store result as new transposition
ret
; =============== S U B R O U T I N E =======================================
; If a continuous SFX is playing, it will continue playing from target address.
; A loop counter is decremented (it is initialized to number of SFX tracks)
; for continuous SFX; if the result is zero, the continuous SFX will be flagged
; to stop.
; Non-continuous SFX do not loop.
;
; Has a 2-byte parameter, the jump target address.
;
;loc_EB8
cfLoopContinuousSFX:
ld a, (zContinuousSFXFlag) ; Get 'continuous sound effect' flag
cp 80h ; Is it equal to 80h?
jp z, .run_counter ; Branch if yes
xor a ; a = 0
ld (zContinuousSFX), a ; Clear last continuous SFX played
if fix_sndbugs=0
ld (zContinuousSFXFlag), a ; Clear continuous sound effect flag (redundant; zContinuousSFXFlag will always be 0 at this point)
endif
inc de ; Skip a byte
ret
; ---------------------------------------------------------------------------
.run_counter:
if fix_sndbugs
ld hl, zContSFXLoopCnt ; Get number loops to perform
dec (hl) ; Decrement it...
else
ld a, (zContSFXLoopCnt) ; Get number loops to perform
dec a ; Decrement it...
ld (zContSFXLoopCnt), a ; ... and store it back
endif
jp nz, cfJumpTo ; If result is non-zero, jump to target address
xor a ; a = 0
ld (zContinuousSFXFlag), a ; Clear continuous sound effect flag
jp cfJumpTo ; Jump to target address
; =============== S U B R O U T I N E =======================================
; Toggles alternate frequency mode according to parameter.
;
; Has a single byte parameter: is 1, enables alternate frequency mode, otherwise
; disables it.
;
;loc_EDA
;cfToggleAlternateSMPS
cfToggleAltFreqMode:
if fix_sndbugs
or a ; Is parameter equal to 0?
jr z, .stop_altfreq_mode ; Branch if so
else
cp 1 ; Is parameter equal to 1?
jr nz, .stop_altfreq_mode ; Branch if not
endif
set 3, (ix+zTrack.PlaybackControl) ; Start alternate frequency mode for track
ret
; ---------------------------------------------------------------------------
.stop_altfreq_mode:
res 3, (ix+zTrack.PlaybackControl) ; Stop alternate frequency mode for track
ret
; =============== S U B R O U T I N E =======================================
; If current track is FM3, it is put into special mode. The function is weird,
; and may not work correctly (subject to verification).
;
; It has 4 1-byte parameters: all of them are indexes into a lookup table of
; unknown purpose, and must be in the 0-7 range. It is possible that this
; lookup table held frequencies (or frequency shifts) for FM3 and its operators
; in special mode.
;
;loc_EE8
cfFM3SpecialMode:
ld a, (ix+zTrack.VoiceControl) ; Get track's voice control
cp 2 ; Is this FM3?
jr nz, zTrackSkip3bytes ; Branch if not
set 0, (ix+zTrack.PlaybackControl) ; Put FM3 in special mode
ex de, hl ; Exchange de and hl
call zGetSpecialFM3DataPointer ; de = pointer to saved FM3 frequency shifts
ld b, 4 ; Loop counter: 4 parameter bytes
; DANGER! The following code will trash the Z80 code due to failed
; initialization of de. At the start of the function, hl was a pointer
; to the coord. flag switch table entry that had the address of this
; function; after 'ex de, hl', now de is this pointer.
; After the code below, the targets of the last few coord. flag handlers
; will be overwritten by the nonsensical numbers at the lookup table.
.loop:
push bc ; Save bc
ld a, (hl) ; Get parameter byte
inc hl ; Advance pointer
push hl ; Save hl
ld hl, zFM3FreqShiftTable ; hl = pointer to lookup table
add a, a ; Multiply a by 2
ld c, a ; c = a
ld b, 0 ; b = 0
add hl, bc ; hl = offset into lookup table
ldi ; *de++ = *hl++
ldi ; *de++ = *hl++
pop hl ; Restore hl
pop bc ; Restore bc
djnz .loop ; Loop for all parameters
ex de, hl ; Exchange back de and hl
dec de ; Put back last byte
ld a, 4Fh ; FM3 settings: special mode, enable and load A/B
; =============== S U B R O U T I N E =======================================
; Set up FM3 special settings
;
; Input: a Settings for FM3
; Output: c Damaged
;sub_F11
zWriteFM3Settings:
if fix_sndbugs=0
ld (zFM3Settings), a ; Save FM3 settings
endif
ld c, a ; c = FM3 settings
ld a, 27h ; Write data to FM3 settings register
if fix_sndbugs
jp zWriteFMI ; Do it
else
call zWriteFMI ; Do it
ret
endif
; End of function zWriteFM3Settings
; =============== S U B R O U T I N E =======================================
; Eats 3 bytes from the song.
zTrackSkip3bytes:
inc de ; Advance pointer...
inc de ; ... again...
inc de ; ... and again.
ret
; ---------------------------------------------------------------------------
; Frequency shift data used in cfFM3SpecialMode, above. That function, as well
; as zFMSendFreq, use invalid addresses for read and write (respectively), so
; that this data is improperly used.
;loc_F1F
zFM3FreqShiftTable:
dw 0, 132h, 18Eh, 1E4h, 234h, 27Eh, 2C2h, 2F0h
; =============== S U B R O U T I N E =======================================
; Meta coordination flag: the first parameter byte is an index into an extra
; coord. flag handler table.
;
; Has at least one parameter byte, the index into the jump table.
;
;loc_F2F
cfMetaCF:
ld hl, zExtraCoordFlagSwitchTable ; Load extra coordination flag switch table
rst PointerTableOffset ; hl = jump target for parameter
inc de ; Advance track pointer
ld a, (de) ; Get another parameter byte
jp (hl) ; Jump to coordination flag handler
; =============== S U B R O U T I N E =======================================
; Sets current tempo to parameter byte.
;
; Has one parameter byte, the new value for current tempo.
;
;loc_F36
cfSetTempo:
ld (zCurrentTempo), a ; Set current tempo to parameter
ret
; =============== S U B R O U T I N E =======================================
; Plays another song or SFX.
;
; Has one parameter byte, the ID of what is to be played.
;
; DO NOT USE THIS TO PLAY THE SEGA PCM! It tampers with the stack pointer, and
; will wreak havok with the track update.
;
;loc_F3A:
cfPlaySoundByIndex:
push ix ; Save track pointer
call zPlaySoundByIndex ; Play sound specified by parameter
pop ix ; Restore track pointer
ret
; =============== S U B R O U T I N E =======================================
; Plays another song.
;
; Has one parameter byte, the ID of what is to be played.
;
; DO NOT USE THIS TO PLAY THE SEGA PCM! It tampers with the stack pointer, and
; will wreak havok with the track update.
;
;loc_F3A:
cfPlayMusicByIndex:
push ix ; Save track pointer
call zPlaySoundByIndex ; Play sound specified by parameter
pop ix ; Restore track pointer
ret
; =============== S U B R O U T I N E =======================================
; Halts or resumes all tracks according to parameter.
;
; Has one parameter byte, which is zero to resume all tracks and non-zero to
; halt them.
;
;loc_F42
cfHaltSound:
ld (zHaltFlag), a ; Set new mute flag
or a ; Is it set now?
jr z, .resume ; Branch if not
push ix ; Save ix
push de ; Save de
ld ix, zTracksStart ; Start of song RAM
ld b, (zTracksEnd-zTracksStart)/zTrack.len ; Number of tracks
ld de, zTrack.len ; Spacing between tracks
.loop1:
res 7, (ix+zTrack.PlaybackControl) ; Clear 'track is playing' bit
call zKeyOff ; Stop current note
add ix, de ; Advance to next track
djnz .loop1 ; Loop for all tracks
pop de ; Restore de
pop ix ; Restore ix
jp zPSGSilenceAll
; ---------------------------------------------------------------------------
.resume:
push ix ; Save ix
push de ; Save de
ld ix, zTracksStart ; Start of song RAM
ld b, (zTracksEnd-zTracksStart)/zTrack.len ; Number of tracks
ld de, zTrack.len ; Spacing between tracks
.loop2:
set 7, (ix+zTrack.PlaybackControl) ; Set 'track is playing' bit
add ix, de ; Advance to next track
djnz .loop2 ; Loop for all tracks
pop de ; Restore de
pop ix ; Restore ix
ret
; =============== S U B R O U T I N E =======================================
; Copies data from selected location to current track. Playback will continue
; after the last byte copied.
;
; Has 3 parameter bytes, a 2-byte pointer to data to be copied and a 1-byte
; number of bytes to copy. The data is copied to the track's byte stream,
; starting after the parameters of this coord. flag, and will overwrite the data
; that what was there before. This likely will not work unless the song/SFX was
; copied to Z80 RAM in the first place.
;
;loc_F7D
cfCopyData:
ex de, hl ; Exchange de and hl
ld e, (hl) ; e = low byte of pointer to new song data
inc hl ; Advance track pointer
ld d, (hl) ; d = high byte of pointer to new song data
inc hl ; Advance track pointer
ld c, (hl) ; c = number of bytes to copy
ld b, 0 ; b = 0
inc hl ; Advance track pointer
ex de, hl ; Exchange back de and hl
ldir ; while (bc-- > 0) *de++ = *hl++;
dec de ; Put back last byte
ret
; =============== S U B R O U T I N E =======================================
; Sets tempo divider for all tracks. Does not take effect until the next note
; duration is set.
;
; Has one parameter, the new tempo divider.
;
;loc_F8B
cfSetTempoDivider:
ld b, (zTracksEnd-zTracksStart)/zTrack.len ; Number of tracks
ld hl, zTracksStart+zTrack.TempoDivider ; Want to change tempo dividers
.loop:
push bc ; Save bc
ld bc, zTrack.len ; Spacing between tracks
ld (hl), a ; Set tempo divider for track
add hl, bc ; Advance to next track
pop bc ; Restore bc
djnz .loop
ret
; =============== S U B R O U T I N E =======================================
; Sets SSG-EG data for current track.
;
; Has 4 parameter bytes, the operator parameters for SSG-EG data desired.
;
;loc_F9A
cfSetSSGEG:
ld (ix+zTrack.HaveSSGEGFlag), 80h ; Set custom SSG-EG data flag
ld (ix+zTrack.SSGEGPointerLow), e ; Save low byte of SSG-EG data pointer
ld (ix+zTrack.SSGEGPointerHigh), d ; Save high byte of SSG-EG data pointer
; =============== S U B R O U T I N E =======================================
; Sends SSG-EG data pointed to by de to appropriate registers in YM2612.
;
;sub_FA4
zSendSSGEGData:
; DANGER! The following code ignores the fact that SSG-EG mode must be
; used with maximum (1Fh) attack rate or output is officially undefined.
; This has the potential effect of weird sound, even in real hardware.
if fix_sndbugs
; This fix is even better than what is done in Ristar's sound driver:
; we preserve rate scaling, whereas that driver sets it to 0.
ld l, (ix+zTrack.TLPtrLow) ; l = low byte of pointer to TL data
ld h, (ix+zTrack.TLPtrHigh) ; hl = pointer to TL data
ld bc, zFMInstrumentTLTable-zFMInstrumentRSARTable ; bc = -10h
add hl, bc ; hl = pointer to RS/AR data
push hl ; Save hl (**)
endif
ld hl, zFMInstrumentSSGEGTable ; hl = pointer to registers for SSG-EG data
ld b, zFMInstrumentSSGEGTable_End-zFMInstrumentSSGEGTable ; Number of entries
.loop:
ld a, (de) ; Get data to sent to SSG-EG register
inc de ; Advance pointer
ld c, a ; c = data to send
ld a, (hl) ; a = register to send to
if fix_sndbugs
call zWriteFMIorII ; Send data to correct channel
ex (sp), hl ; Save hl, hl = pointer to RS/AR data (see line marked (**) above)
ld a, (hl) ; a = RS/AR value for operator
inc hl ; Advance pointer
ex (sp), hl ; Save hl, hl = pointer to registers for SSG-EG data
or 1Fh ; Set AR to maximum, but keep RS intact
ld c, a ; c = RS/AR
ld a, (hl) ; a = register to send to
sub 40h ; Convert into command to set RS/AR
endif
inc hl ; Advance pointer
call zWriteFMIorII ; Send data to correct channel
djnz .loop ; Loop for all registers
if fix_sndbugs
pop hl ; Remove from stack (see line marked (**) above)
endif
dec de ; Rewind data pointer a bit
ret
; End of function zSendSSGEGData
; =============== S U B R O U T I N E =======================================
; Starts or controls FM volume envelope effects, according to the parameters.
;
; Has two parameter bytes: the first is a (1-based) index into the PSG envelope
; table indicating how the envelope should go, while the second is a bitmask
; indicating which operators should be affected (in the form %00004231) for
; the current channel.
;
;loc_FB5
;cfFMFlutter
cfFMVolEnv:
ld (ix+zTrack.FMVolEnv), a ; Store envelope index
inc de ; Advance track pointer
ld a, (de) ; Get envelope mask
ld (ix+zTrack.FMVolEnvMask), a ; Store envelope bitmask
ret
; =============== S U B R O U T I N E =======================================
; Resets spindash rev counter.
;
; Has no parameter bytes.
;
;loc_FBE
cfResetSpindashRev:
xor a ; a = 0
ld (zSpindashRev), a ; Reset spindash rev
dec de ; Put byte back
ret
; =============== S U B R O U T I N E =======================================
; Sets tempo divider of a single track.
;
; Has one parameter, the new tempo divider.
;
cfChanSetTempoDivider:
ld (ix+zTrack.TempoDivider), a ; Set tempo divider for this track
ret
; =============== S U B R O U T I N E =======================================
; Sends an FM command to the YM2612. The command is sent to the adequate part
; for the current track, so it is only appropriate for those registers that
; affect specific channels.
;
; Has 2 parameter bytes: a 1-byte register selector and a 1-byte register data.
;
;loc_D21
cfChanFMCommand:
call zGetFMParams ; Get parameters for FM command
jp zWriteFMIorII ; Send it to YM2612
; End of function cfChanFMCommand
; =============== S U B R O U T I N E =======================================
; Updates a PSG track.
;
; Input: ix Pointer to track RAM
;
;loc_FC4
zUpdatePSGTrack:
if fix_sndbugs
dec (ix+zTrack.DurationTimeout) ; Run note timer
else
call zTrackRunTimer ; Run note timer
endif
jr nz, .note_going ; Branch if note hasn't expired yet
call zGetNextNote ; Get next note for PSG track
bit 4, (ix+zTrack.PlaybackControl) ; Is track resting?
ret nz ; Return if yes
call zPrepareModulation ; Initialize modulation
jr .skip_fill
; ---------------------------------------------------------------------------
.note_going:
ld a, (ix+zTrack.NoteFillTimeout) ; Get note fill
or a ; Has timeout expired?
jr z, .skip_fill ; Branch if yes
dec (ix+zTrack.NoteFillTimeout) ; Update note fill
jp z, zRestTrack ; Put PSG track at rest if needed
.skip_fill:
call zUpdateFreq ; Add frequency displacement to frequency
call zDoModulation ; Do modulation
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding this track?
ret nz ; Return if yes
ld c, (ix+zTrack.VoiceControl) ; c = voice control byte
ld a, l ; a = low byte of new frequency
and 0Fh ; Get only lower nibble
or c ; OR in PSG channel bits
ld (zPSG), a ; Send to PSG, latching register
ld a, l ; a = low byte of new frequency
and 0F0h ; Get high nibble now
or h ; OR in the high byte of the new frequency
; Swap nibbles
rrca
rrca
rrca
rrca
ld (zPSG), a ; Send to PSG, to latched register
ld a, (ix+zTrack.VoiceIndex) ; Get PSG tone
or a ; Test if it is zero
ld c, 0 ; c = 0
jr z, .no_volenv ; Branch if no PSG tone
dec a ; Make it into a 0-based index
ld c, zID_VolEnvPointers ; Value for volume envelope pointer table
rst GetPointerTable ; hl = pointer to volume envelope table
rst PointerTableOffset ; hl = pointer to volume envelope for track
call zDoVolEnv ; Get new volume envelope
ld c, a ; c = new volume envelope
.no_volenv:
bit 4, (ix+zTrack.PlaybackControl) ; Is track resting?
ret nz ; Return if yes
ld a, (ix+zTrack.Volume) ; Get track volume
add a, c ; Add volume envelope to it
bit 4, a ; Is bit 4 set?
jr z, .no_underflow ; Branch if not
ld a, 0Fh ; Set silence on PSG track
.no_underflow:
or (ix+zTrack.VoiceControl) ; Mask in the PSG channel bits
add a, 10h ; Flag to latch volume
bit 0, (ix+zTrack.PlaybackControl) ; Is this a noise channel?
if fix_sndbugs
jr z, .not_noise ; Branch if not
add a, 20h ; Change to noise channel
.not_noise:
ld (zPSG), a ; Set noise channel volume
ret
else
jr nz, .set_noise ; Branch if yes
ld (zPSG), a ; Set PSG volume
ret
; ---------------------------------------------------------------------------
.set_noise:
add a, 20h ; Change to noise channel
ld (zPSG), a ; Set noise channel volume
ret
endif
; ---------------------------------------------------------------------------
;loc_1037
;zDoFlutterSetValue
zDoVolEnvSetValue:
ld (ix+zTrack.VolEnv), a ; Set new value for PSG envelope index and fall through
; =============== S U B R O U T I N E =======================================
; Get next PSG volume envelope value.
;
; Input: ix Pointer to track RAM
; hl Pointer to current PSG volume envelope
; Output: a New volume envelope value
; bc Trashed
;
;sub_103A
;zDoFlutter
zDoVolEnv:
push hl ; Save hl
ld c, (ix+zTrack.VolEnv) ; Get current PSG envelope index
ld b, 0 ; b = 0
add hl, bc ; Offset into PSG envelope table
if fix_sndbugs
; Fix based on similar code from Space Harrier II's sound driver.
; This fixes the "DANGER!" bug below. This is better than the
; previous fix, which was based on Ristar's driver.
ld c, l
ld b, h
ld a, (bc) ; a = PSG volume envelope
else
ld a, (hl) ; a = PSG flutter value
endif
pop hl ; Restore hl
bit 7, a ; Is it a terminator?
jr z, zDoVolEnvAdvance ; Branch if not
cp 83h ; Is it a command to put PSG channel to rest?
jr z, zDoVolEnvFullRest ; Branch if yes
cp 81h ; Is it a command to set rest flag on PSG channel?
jr z, zDoVolEnvRest ; Branch if yes
cp 80h ; Is it a command to reset envelope?
jr z, zDoVolEnvReset ; Branch if yes
inc bc ; Increment envelope index
; DANGER! Will read data from code segment and use it as if it were valid!
; In order to get here, the flutter value would have to be:
; (1) negative;
; (2) not 80h, 81h or 83h.
; As it stands, none of the entries in the flutter tables will allow
; this code to execute.
ld a, (bc) ; Get value from wherever the hell bc is pointing to
jr zDoVolEnvSetValue ; Use this as new envelope index
; ---------------------------------------------------------------------------
;loc_1057
;zDoFlutterFullRest
zDoVolEnvFullRest:
if fix_sndbugs=0
set 4, (ix+zTrack.PlaybackControl) ; Set 'track is resting' bit (zRestTrack already does this)
endif
pop hl ; Pop return value from stack (causes a 'ret' to return to caller of zUpdatePSGTrack)
jp zRestTrack ; Put track at rest
; ---------------------------------------------------------------------------
;loc_105F
;zDoFlutterReset
zDoVolEnvReset:
xor a ; a = 0
jr zDoVolEnvSetValue
; ---------------------------------------------------------------------------
;loc_1062
;zDoFlutterRest
zDoVolEnvRest:
pop hl ; Pop return value from stack (causes a 'ret' to return to caller of zUpdatePSGTrack)
set 4, (ix+zTrack.PlaybackControl) ; Set 'track is resting' bit
ret ; Do NOT silence PSG channel
; ---------------------------------------------------------------------------
;loc_1068
;zDoFlutterAdvance
zDoVolEnvAdvance:
inc (ix+zTrack.VolEnv) ; Advance envelope
ret
; End of function zDoVolEnv
; =============== S U B R O U T I N E =======================================
;
;sub_106C
zRestTrack:
set 4, (ix+zTrack.PlaybackControl) ; Set 'track is resting' bit
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding this track?
ret nz ; Return if so
; End of function zRestTrack
; =============== S U B R O U T I N E =======================================
;
;sub_1075
zSilencePSGChannel:
ld a, 1Fh ; Set volume to zero on PSG channel
add a, (ix+zTrack.VoiceControl) ; Add in the PSG channel selector
or a ; Is it an actual PSG channel?
ret p ; Return if not
ld (zPSG), a ; Silence this channel
if fix_sndbugs
cp 0DFh ; Was this PSG3?
ret nz ; Return if not
else
; This does not work as intended: since this function is called when
; a new channel is starting, this bit will almost inevitably be 0
; and the noise channel will not be silenced.
bit 0, (ix+zTrack.PlaybackControl) ; Is this a noise channel?
ret z ; Return if not
endif
ld a, 0FFh ; Command to silence Noise channel
ld (zPSG), a ; Do it
ret
; End of function zSilencePSGChannel
; =============== S U B R O U T I N E =======================================
;
; Plays digital audio on the DAC, if any is queued. The z80 will be stuck in
; this function unless an interrupt occurs (that is, V-Int); after the V-Int
; is processed, the z80 will return back here.
;loc_108A
zPlayDigitalAudio:
di ; Disable interrupts
ld a, 2Bh ; DAC enable/disable register
ld c, 0 ; Value to disable DAC
call zWriteFMI ; Send YM2612 command
.dac_idle_loop:
ei ; Enable interrupts
ld a, (PlaySegaPCMFlag) ; a = play SEGA PCM flag
or a ; Is SEGA sound being played?
jp nz, zPlaySEGAPCM ; Branch if yes
ld a, (zDACIndex) ; a = DAC index/flag
or a ; Is DAC channel being used?
jr z, .dac_idle_loop ; Loop if not
ld a, 2Bh ; DAC enable/disable register
ld c, 80h ; Value to enable DAC
di ; Disable interrupts
call zWriteFMI ; Send YM2612 command
ei ; Re-enable interrupts
ld iy, DecTable ; iy = pointer to jman2050 decode lookup table
ld hl, zDACIndex ; hl = pointer to DAC index/flag
ld a, (hl) ; a = DAC index
dec a ; a -= 1
set 7, (hl) ; Set bit 7 to indicate that DAC sample is being played
ld hl, zmake68kPtr(DAC_Offsets) ; hl = pointer to ROM window
rst PointerTableOffset ; hl = pointer to DAC data
ld c, 80h ; c is an accumulator below; this initializes it to 80h
ld a, (hl) ; a = DAC rate
ld (.sample1_rate+1), a ; Store into following instruction (self-modifying code)
ld (.sample2_rate+1), a ; Store into following instruction (self-modifying code)
inc hl ; hl = pointer to low byte of DAC sample's length
ld e, (hl) ; e = low byte of DAC sample's length
inc hl ; hl = pointer to high byte of DAC sample's length
ld d, (hl) ; d = high byte of DAC sample's length
inc hl ; hl = pointer to low byte of DAC sample's in-bank location
ld a, (hl) ; a = low byte of DAC sample's in-bank location
inc hl ; hl = pointer to high byte of DAC sample's in-bank location
ld h, (hl) ; h = high byte of DAC sample's in-bank location
ld l, a ; l = low byte of DAC sample's in-bank location
; hl is now pointer to DAC data, while de is the DAC sample's length
.dac_playback_loop:
.sample1_rate:
ld b, 0Ah ; self-modified code; b is set to DAC rate
ei ; Enable interrupts
djnz $ ; Loop in this instruction, decrementing b each iteration, until b = 0
di ; Disable interrupts
ld a, 2Ah ; DAC channel register
ld (zYM2612_A0), a ; Send to YM2612
ld a, (hl) ; a = next byte of DAC sample
; Want only the high nibble now, so shift it into position
rlca
rlca
rlca
rlca
and 0Fh ; Get only low nibble (which was the high nibble originally)
ld (.sample1_index+2), a ; Store into following instruction (self-modifying code)
ld a, c ; a = c
.sample1_index:
add a, (iy+0) ; Self-modified code: the index offset is not zero, but what was set above
ld (zYM2612_D0), a ; Send byte to DAC
ld c, a ; Set c to the new value of a
.sample2_rate:
ld b, 0Ah ; self-modified code; b is set to DAC rate
ei ; Enable interrupts
djnz $ ; Loop in this instruction, decrementing b each iteration, until b = 0
di ; Disable interrupts
ld a, 2Ah ; DAC channel register
ld (zYM2612_A0), a ; Send to YM2612
ld a, (hl) ; a = next byte of DAC sample
and 0Fh ; Want only the low nibble
ld (.sample2_index+2), a ; Store into following instruction (self-modifying code)
ld a, c ; a = c
.sample2_index:
add a, (iy+0) ; Self-modified code: the index offset is not zero, but what was set above
ld (zYM2612_D0), a ; Send byte to DAC
ei ; Enable interrupts
ld c, a ; Set c to the new value of a
ld a, (zDACIndex) ; a = DAC index/flag
or a ; Is playing flag set?
jp p, .dac_idle_loop ; Branch if not
inc hl ; Advance to next sample byte
dec de ; Mark one byte as being done
ld a, d ; a = d
or e ; Is length zero?
jp nz, .dac_playback_loop ; Loop if not
xor a ; a = 0
ld (zDACIndex), a ; Mark DAC as being idle
jp zPlayDigitalAudio ; Loop
; ---------------------------------------------------------------------------
; ===========================================================================
; JMan2050's DAC decode lookup table
; ===========================================================================
DecTable:
db 0, 1, 2, 4, 8, 10h, 20h, 40h
db 80h, -1, -2, -4, -8, -10h, -20h, -40h
; ---------------------------------------------------------------------------
; =============== S U B R O U T I N E =======================================
;
; Plays the SEGA PCM sound. The z80 will be "stuck" in this function (as it
; disables interrupts) until either of the following conditions hold:
;
; (1) The SEGA PCM is fully played
; (2) The next song to play is 0FEh (mus_StopSEGA)
;loc_1126
zPlaySEGAPCM:
di ; Disable interrupts
xor a ; a = 0
ld (PlaySegaPCMFlag), a ; Clear flag
ld a, 2Bh ; DAC enable/disable register
ld (zYM2612_A0), a ; Select the register
nop ; Delay
ld a, 80h ; Value to enable DAC
ld (zYM2612_D0), a ; Enable DAC
ld a, zmake68kBank(SEGA_PCM) ; a = sound bank index
bankswitch3 ; Bank switch to sound bank
ld hl, zmake68kPtr(SEGA_PCM) ; hl = pointer to SEGA PCM
ld de, SEGA_PCM_End-SEGA_PCM ; de = length of SEGA PCM
ld a, 2Ah ; DAC channel register
ld (zYM2612_A0), a ; Send to YM2612
nop ; Delay
.loop:
ld a, (hl) ; a = next byte of SEGA PCM
ld (zYM2612_D0), a ; Send to DAC
ld a, (zMusicNumber) ; Check next song number
cp mus_StopSEGA ; Is it the command to stop playing SEGA PCM?
jr z, .done ; Break the loop if yes
nop
nop
ld b, 0Ch ; Loop counter
djnz $ ; Loop in this instruction, decrementing b each iteration, until b = 0
inc hl ; Advance to next byte of SEGA PCM
dec de ; Mark one byte as being done
ld a, d ; a = d
or e ; Is length zero?
jr nz, .loop ; Loop if not
.done:
jp zPlayDigitalAudio ; Go back to normal DAC code
; ---------------------------------------------------------------------------
db 0
if $ > 1300h
fatal "Your Z80 code won't fit before its tables. It's \{$-1300h}h bytes past the start of music data \{1300h}h"
endif
restore
padding off
!org Z80_SoundDriver+Size_of_Snd_driver_guess ; The assembler still thinks we're in Z80 memory, so use an 'org' to switch back to the cartridge
; Z80_Snd_Driver2:
Z80_SoundDriverData:
org Z80_SoundDriverData+Size_of_Snd_driver2_guess ; Once again, create some padding that we can paste the compressed data over later
; ---------------------------------------------------------------------------
save
CPU Z80
listing purecode
!org 1300h ; z80 Align, handled by the build process
; ---------------------------------------------------------------------------
; ===========================================================================
; Pointers
; ===========================================================================
z80_SoundDriverPointers:
if fix_sndbugs
dw z80_MusicPointers
dw z80_SFXPointers
dw z80_ModEnvPointers
dw z80_VolEnvPointers
else
dw z80_MusicPointers ; This would be the priority array in other drivers
dw z80_UniVoiceBank ; In other drivers, this is a pointer to special SFX table instead
dw z80_MusicPointers
dw z80_SFXPointers
dw z80_ModEnvPointers
dw z80_VolEnvPointers
dw 33h ; This is the song limit; it is never used in any driver
endif
; ---------------------------------------------------------------------------
; ===========================================================================
; Modulation Envelope Pointers
; ===========================================================================
;z80_FreqFlutterPointers
z80_ModEnvPointers:
dw ModEnv_00
dw ModEnv_01
dw ModEnv_02
dw ModEnv_03
dw ModEnv_04
dw ModEnv_05
dw ModEnv_06
dw ModEnv_07
ModEnv_01: db 0
ModEnv_00: db 1, 2, 1, 0,0FFh,0FEh,0FDh,0FCh,0FDh,0FEh,0FFh, 83h
ModEnv_02: db 0, 0, 0, 0, 13h, 26h, 39h, 4Ch, 5Fh, 72h, 7Fh, 72h, 83h
ModEnv_03: db 1, 2, 3, 2, 1, 0,0FFh,0FEh,0FDh,0FEh,0FFh, 0, 82h, 0
ModEnv_04: db 0, 0, 1, 3, 1, 0,0FFh,0FDh,0FFh, 0, 82h, 2
ModEnv_05: db 0, 0, 0, 0, 0, 0Ah, 14h, 1Eh, 14h, 0Ah, 0,0F6h,0ECh,0E2h,0ECh,0F6h
db 82h, 4
ModEnv_06: db 0, 0, 0, 0, 16h, 2Ch, 42h, 2Ch, 16h, 0,0EAh,0D4h,0BEh,0D4h,0EAh, 82h
db 3
ModEnv_07: db 1, 2, 3, 4, 3, 2, 1, 0,0FFh,0FEh,0FDh,0FCh,0FDh,0FEh,0FFh, 0
db 82h, 1
; ---------------------------------------------------------------------------
; ===========================================================================
; Volume Envelope Pointers
; ===========================================================================
;z80_PSGTonePointers
z80_VolEnvPointers:
dw VolEnv_00,VolEnv_01,VolEnv_02,VolEnv_03,VolEnv_04,VolEnv_05
dw VolEnv_06,VolEnv_07,VolEnv_08,VolEnv_09,VolEnv_0A,VolEnv_0B
dw VolEnv_0C,VolEnv_0D,VolEnv_0E,VolEnv_0F,VolEnv_10,VolEnv_11
dw VolEnv_12,VolEnv_13,VolEnv_14,VolEnv_15,VolEnv_16,VolEnv_17
dw VolEnv_18,VolEnv_19,VolEnv_1A,VolEnv_1B,VolEnv_1C,VolEnv_1D
dw VolEnv_1E,VolEnv_1F,VolEnv_20,VolEnv_21,VolEnv_22,VolEnv_23
dw VolEnv_24,VolEnv_25,VolEnv_26,VolEnv_27,VolEnv_28,VolEnv_29
dw VolEnv_2A,VolEnv_2B,VolEnv_2C,VolEnv_2D,VolEnv_2E,VolEnv_2F
dw VolEnv_30,VolEnv_31,VolEnv_32,VolEnv_33
; ---------------------------------------------------------------------------
; ===========================================================================
; Music Pointers
; ===========================================================================
z80_MusicPointers:
dw zmake68kPtr(Snd_AIZ1),zmake68kPtr(Snd_AIZ2),zmake68kPtr(Snd_HCZ1),zmake68kPtr(Snd_HCZ2)
dw zmake68kPtr(Snd_MGZ1),zmake68kPtr(Snd_MGZ2),zmake68kPtr(Snd_CNZ1),zmake68kPtr(Snd_CNZ2)
dw zmake68kPtr(Snd_FBZ1),zmake68kPtr(Snd_FBZ2),zmake68kPtr(Snd_ICZ1),zmake68kPtr(Snd_ICZ2)
dw zmake68kPtr(Snd_LBZ1),zmake68kPtr(Snd_LBZ2),zmake68kPtr(Snd_MHZ1),zmake68kPtr(Snd_MHZ2)
dw zmake68kPtr(Snd_SOZ1),zmake68kPtr(Snd_SOZ2),zmake68kPtr(Snd_LRZ1),zmake68kPtr(Snd_LRZ2)
dw zmake68kPtr(Snd_SSZ),zmake68kPtr(Snd_DEZ1),zmake68kPtr(Snd_DEZ2),zmake68kPtr(Snd_Minib_SK)
dw zmake68kPtr(Snd_Boss),zmake68kPtr(Snd_DDZ),zmake68kPtr(Snd_PachBonus),zmake68kPtr(Snd_SpecialS)
dw zmake68kPtr(Snd_SlotBonus),zmake68kPtr(Snd_GumBonus),zmake68kPtr(Snd_Knux),zmake68kPtr(Snd_ALZ)
dw zmake68kPtr(Snd_BPZ),zmake68kPtr(Snd_DPZ),zmake68kPtr(Snd_CGZ),zmake68kPtr(Snd_EMZ)
dw zmake68kPtr(Snd_Title),zmake68kPtr(Snd_S3Credits),zmake68kPtr(Snd_GameOver),zmake68kPtr(Snd_Continue)
dw zmake68kPtr(Snd_Results),zmake68kPtr(Snd_1UP),zmake68kPtr(Snd_Emerald),zmake68kPtr(Snd_Invic)
dw zmake68kPtr(Snd_2PMenu),zmake68kPtr(Snd_Minib_SK),zmake68kPtr(Snd_Menu),zmake68kPtr(Snd_FinalBoss)
dw zmake68kPtr(Snd_Drown),zmake68kPtr(Snd_PresSega),zmake68kPtr(Snd_SKCredits)
; ---------------------------------------------------------------------------
; ===========================================================================
; SFX Pointers
; ===========================================================================
z80_SFXPointers:
dw zmake68kPtr(Sound_33),zmake68kPtr(Sound_34),zmake68kPtr(Sound_35),zmake68kPtr(Sound_36)
dw zmake68kPtr(Sound_37),zmake68kPtr(Sound_38),zmake68kPtr(Sound_39),zmake68kPtr(Sound_3A)
dw zmake68kPtr(Sound_3B),zmake68kPtr(Sound_3C),zmake68kPtr(Sound_3D),zmake68kPtr(Sound_3E)
dw zmake68kPtr(Sound_3F)
dw zmake68kPtr(Sound_40),zmake68kPtr(Sound_41),zmake68kPtr(Sound_42),zmake68kPtr(Sound_43)
dw zmake68kPtr(Sound_44),zmake68kPtr(Sound_45),zmake68kPtr(Sound_46),zmake68kPtr(Sound_47)
dw zmake68kPtr(Sound_48),zmake68kPtr(Sound_49),zmake68kPtr(Sound_4A),zmake68kPtr(Sound_4B)
dw zmake68kPtr(Sound_4C),zmake68kPtr(Sound_4D),zmake68kPtr(Sound_4E),zmake68kPtr(Sound_4F)
dw zmake68kPtr(Sound_50),zmake68kPtr(Sound_51),zmake68kPtr(Sound_52),zmake68kPtr(Sound_53)
dw zmake68kPtr(Sound_54),zmake68kPtr(Sound_55),zmake68kPtr(Sound_56),zmake68kPtr(Sound_57)
dw zmake68kPtr(Sound_58),zmake68kPtr(Sound_59),zmake68kPtr(Sound_5A),zmake68kPtr(Sound_5B)
dw zmake68kPtr(Sound_5C),zmake68kPtr(Sound_5D),zmake68kPtr(Sound_5E),zmake68kPtr(Sound_5F)
dw zmake68kPtr(Sound_60),zmake68kPtr(Sound_61),zmake68kPtr(Sound_62),zmake68kPtr(Sound_63)
dw zmake68kPtr(Sound_64),zmake68kPtr(Sound_65),zmake68kPtr(Sound_66),zmake68kPtr(Sound_67)
dw zmake68kPtr(Sound_68),zmake68kPtr(Sound_69),zmake68kPtr(Sound_6A),zmake68kPtr(Sound_6B)
dw zmake68kPtr(Sound_6C),zmake68kPtr(Sound_6D),zmake68kPtr(Sound_6E),zmake68kPtr(Sound_6F)
dw zmake68kPtr(Sound_70),zmake68kPtr(Sound_71),zmake68kPtr(Sound_72),zmake68kPtr(Sound_73)
dw zmake68kPtr(Sound_74),zmake68kPtr(Sound_75),zmake68kPtr(Sound_76),zmake68kPtr(Sound_77)
dw zmake68kPtr(Sound_78),zmake68kPtr(Sound_79),zmake68kPtr(Sound_7A),zmake68kPtr(Sound_7B)
dw zmake68kPtr(Sound_7C),zmake68kPtr(Sound_7D),zmake68kPtr(Sound_7E),zmake68kPtr(Sound_7F)
dw zmake68kPtr(Sound_80),zmake68kPtr(Sound_81),zmake68kPtr(Sound_82),zmake68kPtr(Sound_83)
dw zmake68kPtr(Sound_84),zmake68kPtr(Sound_85),zmake68kPtr(Sound_86),zmake68kPtr(Sound_87)
dw zmake68kPtr(Sound_88),zmake68kPtr(Sound_89),zmake68kPtr(Sound_8A),zmake68kPtr(Sound_8B)
dw zmake68kPtr(Sound_8C),zmake68kPtr(Sound_8D),zmake68kPtr(Sound_8E),zmake68kPtr(Sound_8F)
dw zmake68kPtr(Sound_90),zmake68kPtr(Sound_91),zmake68kPtr(Sound_92),zmake68kPtr(Sound_93)
dw zmake68kPtr(Sound_94),zmake68kPtr(Sound_95),zmake68kPtr(Sound_96),zmake68kPtr(Sound_97)
dw zmake68kPtr(Sound_98),zmake68kPtr(Sound_99),zmake68kPtr(Sound_9A),zmake68kPtr(Sound_9B)
dw zmake68kPtr(Sound_9C),zmake68kPtr(Sound_9D),zmake68kPtr(Sound_9E),zmake68kPtr(Sound_9F)
dw zmake68kPtr(Sound_A0),zmake68kPtr(Sound_A1),zmake68kPtr(Sound_A2),zmake68kPtr(Sound_A3)
dw zmake68kPtr(Sound_A4),zmake68kPtr(Sound_A5),zmake68kPtr(Sound_A6),zmake68kPtr(Sound_A7)
dw zmake68kPtr(Sound_A8),zmake68kPtr(Sound_A9),zmake68kPtr(Sound_AA),zmake68kPtr(Sound_AB)
dw zmake68kPtr(Sound_AC),zmake68kPtr(Sound_AD),zmake68kPtr(Sound_AE),zmake68kPtr(Sound_AF)
dw zmake68kPtr(Sound_B0),zmake68kPtr(Sound_B1),zmake68kPtr(Sound_B2),zmake68kPtr(Sound_B3)
dw zmake68kPtr(Sound_B4),zmake68kPtr(Sound_B5),zmake68kPtr(Sound_B6),zmake68kPtr(Sound_B7)
dw zmake68kPtr(Sound_B8),zmake68kPtr(Sound_B9),zmake68kPtr(Sound_BA),zmake68kPtr(Sound_BB)
dw zmake68kPtr(Sound_BC),zmake68kPtr(Sound_BD),zmake68kPtr(Sound_BE),zmake68kPtr(Sound_BF)
dw zmake68kPtr(Sound_C0),zmake68kPtr(Sound_C1),zmake68kPtr(Sound_C2),zmake68kPtr(Sound_C3)
dw zmake68kPtr(Sound_C4),zmake68kPtr(Sound_C5),zmake68kPtr(Sound_C6),zmake68kPtr(Sound_C7)
dw zmake68kPtr(Sound_C8),zmake68kPtr(Sound_C9),zmake68kPtr(Sound_CA),zmake68kPtr(Sound_CB)
dw zmake68kPtr(Sound_CC),zmake68kPtr(Sound_CD),zmake68kPtr(Sound_CE),zmake68kPtr(Sound_CF)
dw zmake68kPtr(Sound_D0),zmake68kPtr(Sound_D1),zmake68kPtr(Sound_D2),zmake68kPtr(Sound_D3)
dw zmake68kPtr(Sound_D4),zmake68kPtr(Sound_D5),zmake68kPtr(Sound_D6),zmake68kPtr(Sound_D7)
dw zmake68kPtr(Sound_D8),zmake68kPtr(Sound_D9),zmake68kPtr(Sound_DA),zmake68kPtr(Sound_DB)
dw zmake68kPtr(Sound_DB),zmake68kPtr(Sound_DB),zmake68kPtr(Sound_DB),zmake68kPtr(Sound_DB)
; ---------------------------------------------------------------------------
; ===========================================================================
; FM Universal Voice Bank
; ===========================================================================
align 17D8h
if $ <> 17D8h
fatal "The universal voice bank is not in a location where music can find it; any song using it will fail."
endif
z80_UniVoiceBank:
; Synth Bass 2
db 3Ch, 1, 0, 0, 0, 1Fh, 1Fh, 15h, 1Fh, 11h, 0Dh, 12h, 5
db 7, 4, 9, 2, 55h, 3Ah, 25h, 1Ah, 1Ah, 80h, 7, 80h ; 0
; Trumpet 1
db 3Dh, 1, 1, 1, 1, 94h, 19h, 19h, 19h, 0Fh, 0Dh, 0Dh, 0Dh
db 7, 4, 4, 4, 25h, 1Ah, 1Ah, 1Ah, 15h, 80h, 80h, 80h ; 25
; Slap Bass 2
db 3, 0,0D7h, 33h, 2, 5Fh, 9Fh, 5Fh, 1Fh, 13h, 0Fh, 0Ah, 0Ah
db 10h, 0Fh, 2, 9, 35h, 15h, 25h, 1Ah, 13h, 16h, 15h, 80h ; 50
; Synth Bass 1
db 34h, 70h, 72h, 31h, 31h, 1Fh, 1Fh, 1Fh, 1Fh, 10h, 6, 6, 6
db 1, 6, 6, 6, 35h, 1Ah, 15h, 1Ah, 10h, 83h, 18h, 83h ; 75
; Bell Synth 1
db 3Eh, 77h, 71h, 32h, 31h, 1Fh, 1Fh, 1Fh, 1Fh, 0Dh, 6, 0, 0
db 8, 6, 0, 0, 15h, 0Ah, 0Ah, 0Ah, 1Bh, 80h, 80h, 80h ; 100
; Bell Synth 2
db 34h, 33h, 41h, 7Eh, 74h, 5Bh, 9Fh, 5Fh, 1Fh, 4, 7, 7, 8
db 0, 0, 0, 0,0FFh,0FFh,0EFh,0FFh, 23h, 80h, 29h, 87h ; 125
; Synth Brass 1
db 3Ah, 1, 7, 31h, 71h, 8Eh, 8Eh, 8Dh, 53h, 0Eh, 0Eh, 0Eh, 3
db 0, 0, 0, 7, 1Fh,0FFh, 1Fh, 0Fh, 18h, 28h, 27h, 80h ; 150
; Synth like Bassoon
db 3Ch, 32h, 32h, 71h, 42h, 1Fh, 18h, 1Fh, 1Eh, 7, 1Fh, 7, 1Fh
db 0, 0, 0, 0, 1Fh, 0Fh, 1Fh, 0Fh, 1Eh, 80h, 0Ch, 80h ; 175
; Bell Horn type thing
db 3Ch, 71h, 72h, 3Fh, 34h, 8Dh, 52h, 9Fh, 1Fh, 9, 0, 0, 0Dh
db 0, 0, 0, 0, 23h, 8, 2,0F7h, 15h, 80h, 1Dh, 87h ; 200
; Synth Bass 3
db 3Dh, 1, 1, 0, 0, 8Eh, 52h, 14h, 4Ch, 8, 8, 0Eh, 3
db 0, 0, 0, 0, 1Fh, 1Fh, 1Fh, 1Fh, 1Bh, 80h, 80h, 9Bh ; 225
; Synth Trumpet
db 3Ah, 1, 1, 1, 2, 8Dh, 7, 7, 52h, 9, 0, 0, 3
db 1, 2, 2, 0, 52h, 2, 2, 28h, 18h, 22h, 18h, 80h ; 250
; Wood Block
db 3Ch, 36h, 31h, 76h, 71h, 94h, 9Fh, 96h, 9Fh, 12h, 0, 14h, 0Fh
db 4, 0Ah, 4, 0Dh, 2Fh, 0Fh, 4Fh, 2Fh, 33h, 80h, 1Ah, 80h ; 275
; Tubular Bell
db 34h, 33h, 41h, 7Eh, 74h, 5Bh, 9Fh, 5Fh, 1Fh, 4, 7, 7, 8
db 0, 0, 0, 0,0FFh,0FFh,0EFh,0FFh, 23h, 90h, 29h, 97h ; 300
; Strike Bass
db 38h, 63h, 31h, 31h, 31h, 10h, 13h, 1Ah, 1Bh, 0Eh, 0, 0, 0
db 0, 0, 0, 0, 3Fh, 0Fh, 0Fh, 0Fh, 1Ah, 19h, 1Ah, 80h ; 325
; Elec Piano
db 3Ah, 31h, 25h, 73h, 41h, 5Fh, 1Fh, 1Fh, 9Ch, 8, 5, 4, 5
db 3, 4, 2, 2, 2Fh, 2Fh, 1Fh, 2Fh, 29h, 27h, 1Fh, 80h ; 350
; Bright Piano
db 4, 71h, 41h, 31h, 31h, 12h, 12h, 12h, 12h, 0, 0, 0, 0
db 0, 0, 0, 0, 0Fh, 0Fh, 0Fh, 0Fh, 23h, 80h, 23h, 80h ; 375
; Church Bell
db 14h, 75h, 72h, 35h, 32h, 9Fh, 9Fh, 9Fh, 9Fh, 5, 5, 0, 0Ah
db 5, 5, 7, 5, 2Fh,0FFh, 0Fh, 2Fh, 1Eh, 80h, 14h, 80h ; 400
; Synth Brass 2
db 3Dh, 1, 0, 1, 2, 12h, 1Fh, 1Fh, 14h, 7, 2, 2, 0Ah
db 5, 5, 5, 5, 2Fh, 2Fh, 2Fh,0AFh, 1Ch, 80h, 82h, 80h ; 425
; Bell Piano
db 1Ch, 73h, 72h, 33h, 32h, 94h, 99h, 94h, 99h, 8, 0Ah, 8, 0Ah
db 0, 5, 0, 5, 3Fh, 4Fh, 3Fh, 4Fh, 1Eh, 80h, 19h, 80h ; 450
; Wet Wood Bass
db 31h, 33h, 1, 0, 0, 9Fh, 1Fh, 1Fh, 1Fh, 0Dh, 0Ah, 0Ah, 0Ah
db 0Ah, 7, 7, 7,0FFh,0AFh,0AFh,0AFh, 1Eh, 1Eh, 1Eh, 80h ; 475
; Silent Bass
db 3Ah, 70h, 76h, 30h, 71h, 1Fh, 95h, 1Fh, 1Fh, 0Eh, 0Fh, 5, 0Ch
db 7, 6, 6, 7, 2Fh, 4Fh, 1Fh, 5Fh, 21h, 12h, 28h, 80h ; 500
; Picked Bass
db 28h, 71h, 0, 30h, 1, 1Fh, 1Fh, 1Dh, 1Fh, 13h, 13h, 6, 5
db 3, 3, 2, 5, 4Fh, 4Fh, 2Fh, 3Fh, 0Eh, 14h, 1Eh, 80h ; 525
; Xylophone
db 3Eh, 38h, 1, 7Ah, 34h, 59h,0D9h, 5Fh, 9Ch, 0Fh, 4, 0Fh, 0Ah
db 2, 2, 5, 5,0AFh,0AFh, 66h, 66h, 28h, 80h,0A3h, 80h ; 550
; Sine Flute
db 39h, 32h, 31h, 72h, 71h, 1Fh, 1Fh, 1Fh, 1Fh, 0, 0, 0, 0
db 0, 0, 0, 0, 0Fh, 0Fh, 0Fh, 0Fh, 1Bh, 32h, 28h, 80h ; 575
; Pipe Organ
db 7, 34h, 74h, 32h, 71h, 1Fh, 1Fh, 1Fh, 1Fh, 0Ah, 0Ah, 5, 3
db 0, 0, 0, 0, 3Fh, 3Fh, 2Fh, 2Fh, 8Ah, 8Ah, 80h, 80h ; 600
; Synth Brass 2
db 3Ah, 31h, 37h, 31h, 31h, 8Dh, 8Dh, 8Eh, 53h, 0Eh, 0Eh, 0Eh, 3
db 0, 0, 0, 0, 1Fh,0FFh, 1Fh, 0Fh, 17h, 28h, 26h, 80h ; 625
; Harpsichord
db 3Bh, 3Ah, 31h, 71h, 74h,0DFh, 1Fh, 1Fh,0DFh, 0, 0Ah, 0Ah, 5
db 0, 5, 5, 3, 0Fh, 5Fh, 1Fh, 5Fh, 32h, 1Eh, 0Fh, 80h ; 650
; Metallic Bass
db 5, 4, 1, 2, 4, 8Dh, 1Fh, 15h, 52h, 6, 0, 0, 4
db 2, 8, 0, 0, 1Fh, 0Fh, 0Fh, 2Fh, 16h, 90h, 84h, 8Ch ; 675
; Alternate Metallic Bass
db 2Ch, 71h, 74h, 32h, 32h, 1Fh, 12h, 1Fh, 12h, 0, 0Ah, 0, 0Ah
db 0, 0, 0, 0, 0Fh, 1Fh, 0Fh, 1Fh, 16h, 80h, 17h, 80h ; 700
; Backdropped Metallic Bass
db 3Ah, 1, 7, 1, 1, 8Eh, 8Eh, 8Dh, 53h, 0Eh, 0Eh, 0Eh, 3
db 0, 0, 0, 7, 1Fh,0FFh, 1Fh, 0Fh, 18h, 28h, 27h, 8Fh ; 725
; Sine like Bell
db 36h, 7Ah, 32h, 51h, 11h, 1Fh, 1Fh, 59h, 1Ch, 0Ah, 0Dh, 6, 0Ah
db 7, 0, 2, 2,0AFh, 5Fh, 5Fh, 5Fh, 1Eh, 8Bh, 81h, 80h ; 750
; Synth like Metallic with Small Bell
db 3Ch, 71h, 72h, 3Fh, 34h, 8Dh, 52h, 9Fh, 1Fh, 9, 0, 0, 0Dh
db 0, 0, 0, 0, 23h, 8, 2,0F7h, 15h, 85h, 1Dh, 8Ah ; 775
; Nice Synth like lead
db 3Eh, 77h, 71h, 32h, 31h, 1Fh, 1Fh, 1Fh, 1Fh, 0Dh, 6, 0, 0
db 8, 6, 0, 0, 15h, 0Ah, 0Ah, 0Ah, 1Bh, 8Fh, 8Fh, 8Fh ; 800
; Rock Organ
db 7, 34h, 74h, 32h, 71h, 1Fh, 1Fh, 1Fh, 1Fh, 0Ah, 0Ah, 5, 3
db 0, 0, 0, 0, 3Fh, 3Fh, 2Fh, 2Fh, 8Ah, 8Ah, 8Ah, 8Ah ; 825
; Strike like Slap Bass
db 20h, 36h, 35h, 30h, 31h,0DFh,0DFh, 9Fh, 9Fh, 7, 6, 9, 6
db 7, 6, 6, 8, 20h, 10h, 10h,0F8h, 19h, 37h, 13h, 80h ; 850
if $ > zDataStart
fatal "Your Z80 tables won't fit before its variables. It's \{$-zDataStart}h bytes past the start of the variables, at \{zDataStart}h"
endif
VolEnv_00: db 2, 83h
VolEnv_01:
VolEnv_0E: db 0, 2, 4, 6, 8, 10h, 83h
VolEnv_02: db 2, 1, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
db 2, 3, 3, 3, 4, 4, 4, 5, 81h
VolEnv_03: db 0, 0, 2, 3, 4, 4, 5, 5, 5, 6, 6, 81h
VolEnv_04: db 3, 0, 1, 1, 1, 2, 3, 4, 4, 5, 81h
VolEnv_05: db 0, 0, 1, 1, 2, 3, 4, 5, 5, 6, 8, 7, 7, 6, 81h
VolEnv_06: db 1, 0Ch, 3, 0Fh, 2, 7, 3, 0Fh, 80h
VolEnv_07: db 0, 0, 0, 2, 3, 3, 4, 5, 6, 7, 8, 9, 0Ah, 0Bh, 0Eh, 0Fh
db 83h
VolEnv_08: db 3, 2, 1, 1, 0, 0, 1, 2, 3, 4, 81h
VolEnv_09: db 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4
db 4, 4, 5, 5, 81h
VolEnv_0A: db 10h, 20h, 30h, 40h, 30h, 20h, 10h, 0,0F0h, 80h
VolEnv_0B: db 0, 0, 1, 1, 3, 3, 4, 5, 83h
VolEnv_0C: db 0, 81h
VolEnv_0D: db 2, 83h
VolEnv_0F: db 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4
db 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 81h
VolEnv_10: db 1, 1, 1, 0, 0, 0, 81h
VolEnv_11: db 3, 0, 1, 1, 1, 2, 3, 4, 4, 5, 81h
VolEnv_12: db 0, 0, 1, 1, 2, 3, 4, 5, 5, 6, 8, 7, 7, 6, 81h
VolEnv_13: db 0Ah, 5, 0, 4, 8, 83h
VolEnv_14: db 0, 0, 0, 2, 3, 3, 4, 5, 6, 7, 8, 9, 0Ah, 0Bh, 0Eh, 0Fh
db 83h
VolEnv_15: db 3, 2, 1, 1, 0, 0, 1, 2, 3, 4, 81h
VolEnv_16: db 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4
db 4, 4, 5, 5, 81h
VolEnv_17: db 10h, 20h, 30h, 40h, 30h, 20h, 10h, 0, 80h
VolEnv_18: db 0, 0, 1, 1, 3, 3, 4, 5, 83h
VolEnv_19: db 0, 2, 4, 6, 8, 16h, 83h
VolEnv_1A: db 0, 0, 1, 1, 3, 3, 4, 5, 83h
VolEnv_1B: db 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1
db 83h
VolEnv_1C: db 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3
db 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7
db 8, 8, 8, 8, 9, 9, 9, 9, 0Ah, 0Ah, 0Ah, 0Ah, 81h
VolEnv_1D: db 0, 0Ah, 83h
VolEnv_1E: db 0, 2, 4, 81h
VolEnv_1F: db 30h, 20h, 10h, 0, 0, 0, 0, 0, 8, 10h, 20h, 30h, 81h
VolEnv_20: db 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8
db 0Ah, 83h
VolEnv_21: db 0, 2, 3, 4, 6, 7, 81h
VolEnv_22: db 2, 1, 0, 0, 0, 2, 4, 7, 81h
VolEnv_23: db 0Fh, 1, 5, 83h
VolEnv_24: db 8, 6, 2, 3, 4, 5, 6, 7, 8, 9, 0Ah, 0Bh, 0Ch, 0Dh, 0Eh, 0Fh
db 10h, 83h
VolEnv_25: db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1
db 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3
db 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4
db 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6
db 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
db 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9
db 9, 9, 83h
VolEnv_26: db 0, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 83h
VolEnv_27: db 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5
db 5, 5, 6, 6, 6, 7, 81h
VolEnv_28: db 0, 2, 4, 6, 8, 10h, 81h
VolEnv_29: db 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 81h
VolEnv_2A: db 0, 0, 2, 3, 4, 4, 5, 5, 5, 6, 81h
VolEnv_2C: db 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 81h
VolEnv_2B: db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
db 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2
db 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 81h
VolEnv_2D: db 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2
db 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 81h
VolEnv_2E: db 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2
db 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6
db 6, 6, 6, 6, 7, 7, 7, 81h
VolEnv_2F: db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0Ah, 0Bh, 0Ch, 0Dh, 0Eh, 0Fh, 81h
VolEnv_30: db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1
db 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
db 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2
db 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 81h
VolEnv_31: db 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1
db 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 81h
VolEnv_32: db 4, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
db 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2
db 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3
db 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
db 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
db 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
db 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6
db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 81h
VolEnv_33: db 0Eh, 0Dh, 0Ch, 0Bh, 0Ah, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 81h
; ---------------------------------------------------------------------------
; ===========================================================================
; END OF SOUND DRIVER
; ===========================================================================
restore
padding off
!org Z80_SoundDriverData+Size_of_Snd_driver2_guess ; The assembler still thinks we're in Z80 memory, so use an 'org' to switch back to the cartridge
; ===========================================================================
; DAC Banks
; ===========================================================================
; DAC Bank 1
; ---------------------------------------------------------------------------
DacBank1: startBank
DAC_Offsets:
offsetBankTableEntry.w DAC_81_Setup
offsetBankTableEntry.w DAC_82_Setup
offsetBankTableEntry.w DAC_83_Setup
offsetBankTableEntry.w DAC_84_Setup
offsetBankTableEntry.w DAC_85_Setup
offsetBankTableEntry.w DAC_86_Setup
offsetBankTableEntry.w DAC_87_Setup
offsetBankTableEntry.w DAC_88_Setup
offsetBankTableEntry.w DAC_89_Setup
offsetBankTableEntry.w DAC_8A_Setup
offsetBankTableEntry.w DAC_8B_Setup
offsetBankTableEntry.w DAC_8C_Setup
offsetBankTableEntry.w DAC_8D_Setup
offsetBankTableEntry.w DAC_8E_Setup
offsetBankTableEntry.w DAC_8F_Setup
offsetBankTableEntry.w DAC_90_Setup
offsetBankTableEntry.w DAC_91_Setup
offsetBankTableEntry.w DAC_92_Setup
offsetBankTableEntry.w DAC_93_Setup
offsetBankTableEntry.w DAC_94_Setup
offsetBankTableEntry.w DAC_95_Setup
offsetBankTableEntry.w DAC_96_Setup
offsetBankTableEntry.w DAC_97_Setup
offsetBankTableEntry.w DAC_98_Setup
offsetBankTableEntry.w DAC_99_Setup
offsetBankTableEntry.w DAC_9A_Setup
offsetBankTableEntry.w DAC_9B_Setup
offsetBankTableEntry.w DAC_9C_Setup
offsetBankTableEntry.w DAC_9D_Setup
offsetBankTableEntry.w DAC_9E_Setup
offsetBankTableEntry.w DAC_9F_Setup
offsetBankTableEntry.w DAC_A0_Setup
offsetBankTableEntry.w DAC_A1_Setup
offsetBankTableEntry.w DAC_A2_Setup
offsetBankTableEntry.w DAC_A3_Setup
offsetBankTableEntry.w DAC_A4_Setup
offsetBankTableEntry.w DAC_A5_Setup
offsetBankTableEntry.w DAC_A6_Setup
offsetBankTableEntry.w DAC_A7_Setup
offsetBankTableEntry.w DAC_A8_Setup
offsetBankTableEntry.w DAC_A9_Setup
offsetBankTableEntry.w DAC_AA_Setup
offsetBankTableEntry.w DAC_AB_Setup
offsetBankTableEntry.w DAC_AC_Setup
offsetBankTableEntry.w DAC_AD_Setup
offsetBankTableEntry.w DAC_AE_Setup
offsetBankTableEntry.w DAC_AF_Setup
offsetBankTableEntry.w DAC_B0_Setup
offsetBankTableEntry.w DAC_B1_Setup
offsetBankTableEntry.w DAC_B2_Setup
offsetBankTableEntry.w DAC_B3_Setup
offsetBankTableEntry.w DAC_B4_Setup
offsetBankTableEntry.w DAC_B5_Setup
offsetBankTableEntry.w DAC_B6_Setup
offsetBankTableEntry.w DAC_B7_Setup
offsetBankTableEntry.w DAC_B8_B9_Setup
offsetBankTableEntry.w DAC_B8_B9_Setup
offsetBankTableEntry.w DAC_BA_Setup
offsetBankTableEntry.w DAC_BB_Setup
offsetBankTableEntry.w DAC_BC_Setup
offsetBankTableEntry.w DAC_BD_Setup
offsetBankTableEntry.w DAC_BE_Setup
offsetBankTableEntry.w DAC_BF_Setup
offsetBankTableEntry.w DAC_C0_Setup
offsetBankTableEntry.w DAC_C1_Setup
offsetBankTableEntry.w DAC_C2_Setup
offsetBankTableEntry.w DAC_C3_Setup
offsetBankTableEntry.w DAC_C4_Setup
DAC_81_Setup: DAC_Setup $04,DAC_81_Data
DAC_82_Setup: DAC_Setup $0E,DAC_82_83_84_85_Data
DAC_83_Setup: DAC_Setup $14,DAC_82_83_84_85_Data
DAC_84_Setup: DAC_Setup $1A,DAC_82_83_84_85_Data
DAC_85_Setup: DAC_Setup $20,DAC_82_83_84_85_Data
DAC_86_Setup: DAC_Setup $04,DAC_86_Data
DAC_87_Setup: DAC_Setup $04,DAC_87_Data
DAC_88_Setup: DAC_Setup $06,DAC_88_Data
DAC_89_Setup: DAC_Setup $0A,DAC_89_Data
DAC_8A_Setup: DAC_Setup $14,DAC_8A_8B_Data
DAC_8B_Setup: DAC_Setup $1B,DAC_8A_8B_Data
DAC_8C_Setup: DAC_Setup $08,DAC_8C_Data
DAC_8D_Setup: DAC_Setup $0B,DAC_8D_8E_Data
DAC_8E_Setup: DAC_Setup $11,DAC_8D_8E_Data
DAC_8F_Setup: DAC_Setup $08,DAC_8F_Data
DAC_90_Setup: DAC_Setup $03,DAC_90_91_92_93_Data
DAC_91_Setup: DAC_Setup $07,DAC_90_91_92_93_Data
DAC_92_Setup: DAC_Setup $0A,DAC_90_91_92_93_Data
DAC_93_Setup: DAC_Setup $0E,DAC_90_91_92_93_Data
DAC_94_Setup: DAC_Setup $06,DAC_94_95_96_97_Data
DAC_95_Setup: DAC_Setup $0A,DAC_94_95_96_97_Data
DAC_96_Setup: DAC_Setup $0D,DAC_94_95_96_97_Data
DAC_97_Setup: DAC_Setup $12,DAC_94_95_96_97_Data
DAC_98_Setup: DAC_Setup $0B,DAC_98_99_9A_Data
DAC_99_Setup: DAC_Setup $13,DAC_98_99_9A_Data
DAC_9A_Setup: DAC_Setup $16,DAC_98_99_9A_Data
DAC_9B_Setup: DAC_Setup $0C,DAC_9B_Data
DAC_A2_Setup: DAC_Setup $0A,DAC_A2_Data
DAC_A3_Setup: DAC_Setup $18,DAC_A3_Data
DAC_A4_Setup: DAC_Setup $18,DAC_A4_Data
DAC_A5_Setup: DAC_Setup $0C,DAC_A5_Data
DAC_A6_Setup: DAC_Setup $09,DAC_A6_Data
DAC_A7_Setup: DAC_Setup $18,DAC_A7_Data
DAC_A8_Setup: DAC_Setup $18,DAC_A8_Data
DAC_A9_Setup: DAC_Setup $0C,DAC_A9_Data
DAC_AA_Setup: DAC_Setup $0A,DAC_AA_Data
DAC_AB_Setup: DAC_Setup $0D,DAC_AB_Data
DAC_AC_Setup: DAC_Setup $06,DAC_AC_Data
DAC_AD_Setup: DAC_Setup $10,DAC_AD_AE_Data
DAC_AE_Setup: DAC_Setup $18,DAC_AD_AE_Data
DAC_AF_Setup: DAC_Setup $09,DAC_AF_Data
DAC_B0_Setup: DAC_Setup $12,DAC_AF_Data
DAC_B1_Setup: DAC_Setup $18,DAC_B1_Data
DAC_B2_Setup: DAC_Setup $16,DAC_B2_B3_Data
DAC_B3_Setup: DAC_Setup $20,DAC_B2_B3_Data
DAC_B4_Setup: DAC_Setup $0C,DAC_B4_C1_C2_C3_C4_Data
DAC_B5_Setup: DAC_Setup $0C,DAC_B5_Data
DAC_B6_Setup: DAC_Setup $0C,DAC_B6_Data
DAC_B7_Setup: DAC_Setup $18,DAC_B7_Data
DAC_B8_B9_Setup: DAC_Setup $0C,DAC_B8_B9_Data
DAC_BA_Setup: DAC_Setup $18,DAC_BA_Data
DAC_BB_Setup: DAC_Setup $18,DAC_BB_Data
DAC_BC_Setup: DAC_Setup $18,DAC_BC_Data
DAC_BD_Setup: DAC_Setup $0C,DAC_BD_Data
DAC_BE_Setup: DAC_Setup $0C,DAC_BE_Data
DAC_BF_Setup: DAC_Setup $1C,DAC_BF_Data
DAC_C0_Setup: DAC_Setup $0B,DAC_C0_Data
DAC_C1_Setup: DAC_Setup $0F,DAC_B4_C1_C2_C3_C4_Data
DAC_C2_Setup: DAC_Setup $11,DAC_B4_C1_C2_C3_C4_Data
DAC_C3_Setup: DAC_Setup $12,DAC_B4_C1_C2_C3_C4_Data
DAC_C4_Setup: DAC_Setup $0B,DAC_B4_C1_C2_C3_C4_Data
DAC_9C_Setup: DAC_Setup $0A,DAC_9C_Data
DAC_9D_Setup: DAC_Setup $18,DAC_9D_Data
DAC_9E_Setup: DAC_Setup $18,DAC_9E_Data
DAC_9F_Setup: DAC_Setup $0C,DAC_9F_Data
DAC_A0_Setup: DAC_Setup $0C,DAC_A0_Data
DAC_A1_Setup: DAC_Setup $0A,DAC_A1_Data
; ---------------------------------------------------------------------------
DAC_86_Data: DACBINCLUDE "Sound/DAC/86.bin"
DAC_81_Data: DACBINCLUDE "Sound/DAC/81.bin"
DAC_82_83_84_85_Data: DACBINCLUDE "Sound/DAC/82-85.bin"
DAC_94_95_96_97_Data: DACBINCLUDE "Sound/DAC/94-97.bin"
DAC_90_91_92_93_Data: DACBINCLUDE "Sound/DAC/90-93.bin"
DAC_88_Data: DACBINCLUDE "Sound/DAC/88.bin"
DAC_8A_8B_Data: DACBINCLUDE "Sound/DAC/8A-8B.bin"
DAC_8C_Data: DACBINCLUDE "Sound/DAC/8C.bin"
DAC_8D_8E_Data: DACBINCLUDE "Sound/DAC/8D-8E.bin"
DAC_87_Data: DACBINCLUDE "Sound/DAC/87.bin"
DAC_8F_Data: DACBINCLUDE "Sound/DAC/8F.bin"
DAC_89_Data: DACBINCLUDE "Sound/DAC/89.bin"
DAC_98_99_9A_Data: DACBINCLUDE "Sound/DAC/98-9A.bin"
DAC_9B_Data: DACBINCLUDE "Sound/DAC/9B.bin"
DAC_B2_B3_Data: DACBINCLUDE "Sound/DAC/B2-B3.bin"
finishBank
; ---------------------------------------------------------------------------
; Dac Bank 2
; ---------------------------------------------------------------------------
DacBank2: startBank
offsetBankTableEntry.w DAC_81_Setup2
offsetBankTableEntry.w DAC_82_Setup2
offsetBankTableEntry.w DAC_83_Setup2
offsetBankTableEntry.w DAC_84_Setup2
offsetBankTableEntry.w DAC_85_Setup2
offsetBankTableEntry.w DAC_86_Setup2
offsetBankTableEntry.w DAC_87_Setup2
offsetBankTableEntry.w DAC_88_Setup2
offsetBankTableEntry.w DAC_89_Setup2
offsetBankTableEntry.w DAC_8A_Setup2
offsetBankTableEntry.w DAC_8B_Setup2
offsetBankTableEntry.w DAC_8C_Setup2
offsetBankTableEntry.w DAC_8D_Setup2
offsetBankTableEntry.w DAC_8E_Setup2
offsetBankTableEntry.w DAC_8F_Setup2
offsetBankTableEntry.w DAC_90_Setup2
offsetBankTableEntry.w DAC_91_Setup2
offsetBankTableEntry.w DAC_92_Setup2
offsetBankTableEntry.w DAC_93_Setup2
offsetBankTableEntry.w DAC_94_Setup2
offsetBankTableEntry.w DAC_95_Setup2
offsetBankTableEntry.w DAC_96_Setup2
offsetBankTableEntry.w DAC_97_Setup2
offsetBankTableEntry.w DAC_98_Setup2
offsetBankTableEntry.w DAC_99_Setup2
offsetBankTableEntry.w DAC_9A_Setup2
offsetBankTableEntry.w DAC_9B_Setup2
offsetBankTableEntry.w DAC_9C_Setup2
offsetBankTableEntry.w DAC_9D_Setup2
offsetBankTableEntry.w DAC_9E_Setup2
offsetBankTableEntry.w DAC_9F_Setup2
offsetBankTableEntry.w DAC_A0_Setup2
offsetBankTableEntry.w DAC_A1_Setup2
offsetBankTableEntry.w DAC_A2_Setup2
offsetBankTableEntry.w DAC_A3_Setup2
offsetBankTableEntry.w DAC_A4_Setup2
offsetBankTableEntry.w DAC_A5_Setup2
offsetBankTableEntry.w DAC_A6_Setup2
offsetBankTableEntry.w DAC_A7_Setup2
offsetBankTableEntry.w DAC_A8_Setup2
offsetBankTableEntry.w DAC_A9_Setup2
offsetBankTableEntry.w DAC_AA_Setup2
offsetBankTableEntry.w DAC_AB_Setup2
offsetBankTableEntry.w DAC_AC_Setup2
offsetBankTableEntry.w DAC_AD_Setup2
offsetBankTableEntry.w DAC_AE_Setup2
offsetBankTableEntry.w DAC_AF_Setup2
offsetBankTableEntry.w DAC_B0_Setup2
offsetBankTableEntry.w DAC_B1_Setup2
offsetBankTableEntry.w DAC_B2_Setup2
offsetBankTableEntry.w DAC_B3_Setup2
offsetBankTableEntry.w DAC_B4_Setup2
offsetBankTableEntry.w DAC_B5_Setup2
offsetBankTableEntry.w DAC_B6_Setup2
offsetBankTableEntry.w DAC_B7_Setup2
offsetBankTableEntry.w DAC_B8_B9_Setup2
offsetBankTableEntry.w DAC_B8_B9_Setup2
offsetBankTableEntry.w DAC_BA_Setup2
offsetBankTableEntry.w DAC_BB_Setup2
offsetBankTableEntry.w DAC_BC_Setup2
offsetBankTableEntry.w DAC_BD_Setup2
offsetBankTableEntry.w DAC_BE_Setup2
offsetBankTableEntry.w DAC_BF_Setup2
offsetBankTableEntry.w DAC_C0_Setup2
offsetBankTableEntry.w DAC_C1_Setup2
offsetBankTableEntry.w DAC_C2_Setup2
offsetBankTableEntry.w DAC_C3_Setup2
offsetBankTableEntry.w DAC_C4_Setup2
DAC_81_Setup2: DAC_Null_Setup $04,DAC_81_Data
DAC_82_Setup2: DAC_Null_Setup $0E,DAC_82_83_84_85_Data
DAC_83_Setup2: DAC_Null_Chain $14,DAC_Invalid,DAC_82_Setup2
DAC_84_Setup2: DAC_Null_Chain $1A,DAC_Invalid,DAC_83_Setup2
DAC_85_Setup2: DAC_Null_Chain $20,DAC_Invalid,DAC_84_Setup2
DAC_86_Setup2: DAC_Null_Setup $04,DAC_86_Data
DAC_87_Setup2: DAC_Null_Setup $04,DAC_87_Data
DAC_88_Setup2: DAC_Null_Setup $06,DAC_88_Data
DAC_89_Setup2: DAC_Null_Setup $0A,DAC_89_Data
DAC_8A_Setup2: DAC_Null_Setup $14,DAC_8A_8B_Data
DAC_8B_Setup2: DAC_Null_Chain $1B,DAC_Invalid,DAC_8A_Setup2
DAC_8C_Setup2: DAC_Null_Setup $08,DAC_8C_Data
DAC_8D_Setup2: DAC_Null_Setup $0B,DAC_8D_8E_Data
DAC_8E_Setup2: DAC_Null_Chain $11,DAC_Invalid,DAC_8D_Setup2
DAC_8F_Setup2: DAC_Null_Setup $08,DAC_8F_Data
DAC_90_Setup2: DAC_Null_Setup $03,DAC_90_91_92_93_Data
DAC_91_Setup2: DAC_Null_Chain $07,DAC_Invalid,DAC_90_Setup2
DAC_92_Setup2: DAC_Null_Chain $0A,DAC_Invalid,DAC_91_Setup2
DAC_93_Setup2: DAC_Null_Chain $0E,DAC_Invalid,DAC_92_Setup2
DAC_94_Setup2: DAC_Null_Setup $06,DAC_94_95_96_97_Data
DAC_95_Setup2: DAC_Null_Chain $0A,DAC_Invalid,DAC_94_Setup2
DAC_96_Setup2: DAC_Null_Chain $0D,DAC_Invalid,DAC_95_Setup2
DAC_97_Setup2: DAC_Null_Chain $12,DAC_Invalid,DAC_96_Setup2
DAC_98_Setup2: DAC_Null_Setup $0B,DAC_98_99_9A_Data
DAC_99_Setup2: DAC_Null_Chain $13,DAC_Invalid,DAC_98_Setup2
DAC_9A_Setup2: DAC_Null_Chain $16,DAC_Invalid,DAC_99_Setup2
DAC_9B_Setup2: DAC_Null_Chain $0C,DAC_9B_Data,Bank2_Filler-3
DAC_A2_Setup2: DAC_Setup $0A,DAC_A2_Data
DAC_A3_Setup2: DAC_Setup $18,DAC_A3_Data
DAC_A4_Setup2: DAC_Setup $18,DAC_A4_Data
DAC_A5_Setup2: DAC_Setup $0C,DAC_A5_Data
DAC_A6_Setup2: DAC_Setup $09,DAC_A6_Data
DAC_A7_Setup2: DAC_Setup $18,DAC_A7_Data
DAC_A8_Setup2: DAC_Setup $18,DAC_A8_Data
DAC_A9_Setup2: DAC_Setup $0C,DAC_A9_Data
DAC_AA_Setup2: DAC_Setup $0A,DAC_AA_Data
DAC_AB_Setup2: DAC_Null_Setup $0D,DAC_AB_Data
DAC_AC_Setup2: DAC_Null_Setup $06,DAC_AC_Data
DAC_AD_Setup2: DAC_Null_Setup $10,DAC_AD_AE_Data
DAC_AE_Setup2: DAC_Null_Chain $18,DAC_Invalid,DAC_AD_Setup2
DAC_AF_Setup2: DAC_Null_Setup $09,DAC_AF_Data
DAC_B0_Setup2: DAC_Null_Chain $12,DAC_Invalid,DAC_AF_Setup2
DAC_B1_Setup2: DAC_Null_Setup $18,DAC_B1_Data
DAC_B2_Setup2: DAC_Null_Setup $16,DAC_B2_B3_Data
DAC_B3_Setup2: DAC_Null_Chain $20,DAC_Invalid,DAC_B2_Setup2
DAC_B4_Setup2: DAC_Null_Setup $0C,DAC_B4_C1_C2_C3_C4_Data
DAC_B5_Setup2: DAC_Null_Setup $0C,DAC_B5_Data
DAC_B6_Setup2: DAC_Null_Setup $0C,DAC_B6_Data
DAC_B7_Setup2: DAC_Null_Setup $18,DAC_B7_Data
DAC_B8_B9_Setup2: DAC_Null_Setup $0C,DAC_B8_B9_Data
DAC_BA_Setup2: DAC_Null_Setup $18,DAC_BA_Data
DAC_BB_Setup2: DAC_Null_Setup $18,DAC_BB_Data
DAC_BC_Setup2: DAC_Null_Setup $18,DAC_BC_Data
DAC_BD_Setup2: DAC_Null_Setup $0C,DAC_BD_Data
DAC_BE_Setup2: DAC_Null_Setup $0C,DAC_BE_Data
DAC_BF_Setup2: DAC_Null_Setup $1C,DAC_BF_Data
DAC_C0_Setup2: DAC_Null_Setup $0B,DAC_C0_Data
DAC_C1_Setup2: DAC_Null_Chain $0F,DAC_Invalid,DAC_B4_Setup2
DAC_C2_Setup2: DAC_Null_Chain $11,DAC_Invalid,DAC_C1_Setup2
DAC_C3_Setup2: DAC_Null_Chain $12,DAC_Invalid,DAC_C2_Setup2
DAC_C4_Setup2: DAC_Null_Chain $0B,DAC_Invalid,DAC_C3_Setup2
DAC_9C_Setup2: DAC_Setup $0A,DAC_9C_Data
DAC_9D_Setup2: DAC_Setup $18,DAC_9D_Data
DAC_9E_Setup2: DAC_Setup $18,DAC_9E_Data
DAC_9F_Setup2: DAC_Setup $0C,DAC_9F_Data
DAC_A0_Setup2: DAC_Setup $0C,DAC_A0_Data
DAC_A1_Setup2: DAC_Setup $0A,DAC_A1_Data
Bank2_Filler: cnop $7F7,soundBankStart
DAC_9C_Data: DACBINCLUDE "Sound/DAC/9C.bin"
DAC_9D_Data: DACBINCLUDE "Sound/DAC/9D.bin"
DAC_9E_Data: DACBINCLUDE "Sound/DAC/9E.bin"
DAC_9F_Data: DACBINCLUDE "Sound/DAC/9F.bin"
DAC_A0_Data: DACBINCLUDE "Sound/DAC/A0.bin"
DAC_A1_Data: DACBINCLUDE "Sound/DAC/A1.bin"
DAC_A2_Data: DACBINCLUDE "Sound/DAC/A2.bin"
DAC_A3_Data: DACBINCLUDE "Sound/DAC/A3.bin"
DAC_A4_Data: DACBINCLUDE "Sound/DAC/A4.bin"
DAC_A5_Data: DACBINCLUDE "Sound/DAC/A5.bin"
DAC_A6_Data: DACBINCLUDE "Sound/DAC/A6.bin"
DAC_A7_Data: DACBINCLUDE "Sound/DAC/A7.bin"
DAC_A8_Data: DACBINCLUDE "Sound/DAC/A8.bin"
DAC_A9_Data: DACBINCLUDE "Sound/DAC/A9.bin"
DAC_AA_Data: DACBINCLUDE "Sound/DAC/AA.bin"
finishBank
; ---------------------------------------------------------------------------
; Dac Bank 3
; ---------------------------------------------------------------------------
DacBank3: startBank
offsetBankTableEntry.w DAC_81_Setup3
offsetBankTableEntry.w DAC_82_Setup3
offsetBankTableEntry.w DAC_83_Setup3
offsetBankTableEntry.w DAC_84_Setup3
offsetBankTableEntry.w DAC_85_Setup3
offsetBankTableEntry.w DAC_86_Setup3
offsetBankTableEntry.w DAC_87_Setup3
offsetBankTableEntry.w DAC_88_Setup3
offsetBankTableEntry.w DAC_89_Setup3
offsetBankTableEntry.w DAC_8A_Setup3
offsetBankTableEntry.w DAC_8B_Setup3
offsetBankTableEntry.w DAC_8C_Setup3
offsetBankTableEntry.w DAC_8D_Setup3
offsetBankTableEntry.w DAC_8E_Setup3
offsetBankTableEntry.w DAC_8F_Setup3
offsetBankTableEntry.w DAC_90_Setup3
offsetBankTableEntry.w DAC_91_Setup3
offsetBankTableEntry.w DAC_92_Setup3
offsetBankTableEntry.w DAC_93_Setup3
offsetBankTableEntry.w DAC_94_Setup3
offsetBankTableEntry.w DAC_95_Setup3
offsetBankTableEntry.w DAC_96_Setup3
offsetBankTableEntry.w DAC_97_Setup3
offsetBankTableEntry.w DAC_98_Setup3
offsetBankTableEntry.w DAC_99_Setup3
offsetBankTableEntry.w DAC_9A_Setup3
offsetBankTableEntry.w DAC_9B_Setup3
offsetBankTableEntry.w DAC_9C_Setup3
offsetBankTableEntry.w DAC_9D_Setup3
offsetBankTableEntry.w DAC_9E_Setup3
offsetBankTableEntry.w DAC_9F_Setup3
offsetBankTableEntry.w DAC_A0_Setup3
offsetBankTableEntry.w DAC_A1_Setup3
offsetBankTableEntry.w DAC_A2_Setup3
offsetBankTableEntry.w DAC_A3_Setup3
offsetBankTableEntry.w DAC_A4_Setup3
offsetBankTableEntry.w DAC_A5_Setup3
offsetBankTableEntry.w DAC_A6_Setup3
offsetBankTableEntry.w DAC_A7_Setup3
offsetBankTableEntry.w DAC_A8_Setup3
offsetBankTableEntry.w DAC_A9_Setup3
offsetBankTableEntry.w DAC_AA_Setup3
offsetBankTableEntry.w DAC_AB_Setup3
offsetBankTableEntry.w DAC_AC_Setup3
offsetBankTableEntry.w DAC_AD_Setup3
offsetBankTableEntry.w DAC_AE_Setup3
offsetBankTableEntry.w DAC_AF_Setup3
offsetBankTableEntry.w DAC_B0_Setup3
offsetBankTableEntry.w DAC_B1_Setup3
offsetBankTableEntry.w DAC_B2_Setup3
offsetBankTableEntry.w DAC_B3_Setup3
offsetBankTableEntry.w DAC_B4_Setup3
offsetBankTableEntry.w DAC_B5_Setup3
offsetBankTableEntry.w DAC_B6_Setup3
offsetBankTableEntry.w DAC_B7_Setup3
offsetBankTableEntry.w DAC_B8_B9_Setup3
offsetBankTableEntry.w DAC_B8_B9_Setup3
offsetBankTableEntry.w DAC_BA_Setup3
offsetBankTableEntry.w DAC_BB_Setup3
offsetBankTableEntry.w DAC_BC_Setup3
offsetBankTableEntry.w DAC_BD_Setup3
offsetBankTableEntry.w DAC_BE_Setup3
offsetBankTableEntry.w DAC_BF_Setup3
offsetBankTableEntry.w DAC_C0_Setup3
offsetBankTableEntry.w DAC_C1_Setup3
offsetBankTableEntry.w DAC_C2_Setup3
offsetBankTableEntry.w DAC_C3_Setup3
offsetBankTableEntry.w DAC_C4_Setup3
DAC_81_Setup3: DAC_Null_Setup $04,DAC_81_Data
DAC_82_Setup3: DAC_Null_Setup $0E,DAC_82_83_84_85_Data
DAC_83_Setup3: DAC_Null_Chain $14,DAC_Invalid,DAC_82_Setup3
DAC_84_Setup3: DAC_Null_Chain $1A,DAC_Invalid,DAC_83_Setup3
DAC_85_Setup3: DAC_Null_Chain $20,DAC_Invalid,DAC_84_Setup3
DAC_86_Setup3: DAC_Null_Setup $04,DAC_86_Data
DAC_87_Setup3: DAC_Null_Setup $04,DAC_87_Data
DAC_88_Setup3: DAC_Null_Setup $06,DAC_88_Data
DAC_89_Setup3: DAC_Null_Setup $0A,DAC_89_Data
DAC_8A_Setup3: DAC_Null_Setup $14,DAC_8A_8B_Data
DAC_8B_Setup3: DAC_Null_Chain $1B,DAC_Invalid,DAC_8A_Setup3
DAC_8C_Setup3: DAC_Null_Setup $08,DAC_8C_Data
DAC_8D_Setup3: DAC_Null_Setup $0B,DAC_8D_8E_Data
DAC_8E_Setup3: DAC_Null_Chain $11,DAC_Invalid,DAC_8D_Setup3
DAC_8F_Setup3: DAC_Null_Setup $08,DAC_8F_Data
DAC_90_Setup3: DAC_Null_Setup $03,DAC_90_91_92_93_Data
DAC_91_Setup3: DAC_Null_Chain $07,DAC_Invalid,DAC_90_Setup3
DAC_92_Setup3: DAC_Null_Chain $0A,DAC_Invalid,DAC_91_Setup3
DAC_93_Setup3: DAC_Null_Chain $0E,DAC_Invalid,DAC_92_Setup3
DAC_94_Setup3: DAC_Null_Setup $06,DAC_94_95_96_97_Data
DAC_95_Setup3: DAC_Null_Chain $0A,DAC_Invalid,DAC_94_Setup3
DAC_96_Setup3: DAC_Null_Chain $0D,DAC_Invalid,DAC_95_Setup3
DAC_97_Setup3: DAC_Null_Chain $12,DAC_Invalid,DAC_96_Setup3
DAC_98_Setup3: DAC_Null_Setup $0B,DAC_98_99_9A_Data
DAC_99_Setup3: DAC_Null_Chain $13,DAC_Invalid,DAC_98_Setup3
DAC_9A_Setup3: DAC_Null_Chain $16,DAC_Invalid,DAC_99_Setup3
DAC_9B_Setup3: DAC_Null_Setup $0C,DAC_9B_Data
DAC_A2_Setup3: DAC_Null_Setup $0A,DAC_A2_Data
DAC_A3_Setup3: DAC_Null_Setup $18,DAC_A3_Data
DAC_A4_Setup3: DAC_Null_Setup $18,DAC_A4_Data
DAC_A5_Setup3: DAC_Null_Setup $0C,DAC_A5_Data
DAC_A6_Setup3: DAC_Null_Setup $09,DAC_A6_Data
DAC_A7_Setup3: DAC_Null_Setup $18,DAC_A7_Data
DAC_A8_Setup3: DAC_Null_Setup $18,DAC_A8_Data
DAC_A9_Setup3: DAC_Null_Setup $0C,DAC_A9_Data
DAC_AA_Setup3: DAC_Null_Setup $0A,DAC_AA_Data
DAC_AB_Setup3: DAC_Setup $0D,DAC_AB_Data
DAC_AC_Setup3: DAC_Setup $06,DAC_AC_Data
DAC_AD_Setup3: DAC_Setup $10,DAC_AD_AE_Data
DAC_AE_Setup3: DAC_Setup $18,DAC_AD_AE_Data
DAC_AF_Setup3: DAC_Setup $09,DAC_AF_Data
DAC_B0_Setup3: DAC_Setup $12,DAC_AF_Data
DAC_B1_Setup3: DAC_Setup $18,DAC_B1_Data
DAC_B2_Setup3: DAC_Null_Chain $16,DAC_B2_B3_Data,Bank3_Filler2-3
DAC_B3_Setup3: DAC_Null_Chain $20,DAC_B2_B3_Data,Bank3_Filler2-3
DAC_B4_Setup3: DAC_Setup $0C,DAC_B4_C1_C2_C3_C4_Data
DAC_B5_Setup3: DAC_Setup $0C,DAC_B5_Data
DAC_B6_Setup3: DAC_Setup $0C,DAC_B6_Data
DAC_B7_Setup3: DAC_Setup $18,DAC_B7_Data
DAC_B8_B9_Setup3: DAC_Setup $0C,DAC_B8_B9_Data
DAC_BA_Setup3: DAC_Setup $18,DAC_BA_Data
DAC_BB_Setup3: DAC_Setup $18,DAC_BB_Data
DAC_BC_Setup3: DAC_Setup $18,DAC_BC_Data
DAC_BD_Setup3: DAC_Setup $0C,DAC_BD_Data
DAC_BE_Setup3: DAC_Setup $0C,DAC_BE_Data
DAC_BF_Setup3: DAC_Setup $1C,DAC_BF_Data
DAC_C0_Setup3: DAC_Setup $0B,DAC_C0_Data
DAC_C1_Setup3: DAC_Setup $0F,DAC_B4_C1_C2_C3_C4_Data
DAC_C2_Setup3: DAC_Setup $11,DAC_B4_C1_C2_C3_C4_Data
DAC_C3_Setup3: DAC_Setup $12,DAC_B4_C1_C2_C3_C4_Data
DAC_C4_Setup3: DAC_Setup $0B,DAC_B4_C1_C2_C3_C4_Data
DAC_9C_Setup3: DAC_Null_Setup $0A,DAC_9C_Data
DAC_9D_Setup3: DAC_Null_Setup $18,DAC_9D_Data
DAC_9E_Setup3: DAC_Null_Setup $18,DAC_9E_Data
DAC_9F_Setup3: DAC_Null_Setup $0C,DAC_9F_Data
DAC_A0_Setup3: DAC_Null_Setup $0C,DAC_A0_Data
DAC_A1_Setup3: DAC_Null_Setup $0A,DAC_A1_Data
DAC_AB_Data: DACBINCLUDE "Sound/DAC/AB.bin"
DAC_AC_Data: DACBINCLUDE "Sound/DAC/AC.bin"
DAC_AD_AE_Data: DACBINCLUDE "Sound/DAC/AD-AE.bin"
DAC_AF_Data: DACBINCLUDE "Sound/DAC/AF.bin"
Bank3_Filler1: cnop $28E0,soundBankStart
DAC_B1_Data: DACBINCLUDE "Sound/DAC/B1.bin"
Bank3_Filler2: cnop $3CAD,soundBankStart
DAC_B4_C1_C2_C3_C4_Data: DACBINCLUDE "Sound/DAC/B4C1-C4.bin"
DAC_B5_Data: DACBINCLUDE "Sound/DAC/B5.bin"
DAC_B6_Data: DACBINCLUDE "Sound/DAC/B6.bin"
DAC_B7_Data: DACBINCLUDE "Sound/DAC/B7.bin"
DAC_B8_B9_Data: DACBINCLUDE "Sound/DAC/B8-B9.bin"
DAC_BA_Data: DACBINCLUDE "Sound/DAC/BA.bin"
DAC_BB_Data: DACBINCLUDE "Sound/DAC/BB.bin"
DAC_BC_Data: DACBINCLUDE "Sound/DAC/BC.bin"
DAC_BD_Data: DACBINCLUDE "Sound/DAC/BD.bin"
DAC_BE_Data: DACBINCLUDE "Sound/DAC/BE.bin"
DAC_BF_Data: DACBINCLUDE "Sound/DAC/BF.bin"
DAC_C0_Data: DACBINCLUDE "Sound/DAC/C0.bin"
finishBank
SonicDriverVer = 4
use_s2_samples = 0
use_s3_samples = 1
use_sk_samples = 1
use_s3d_samples = 0
include "Sound/_smps2asm_inc.asm"
dKick = dKickS3
dSnare = dSnareS3
dVLowTimpani = dLowTimpaniS3
dLowTimpani = dLowTimpaniS3
dMidTimpani = dMidTimpaniS3
dHiTimpani = dMidTimpaniS3
; ===========================================================================
; Music Banks
; ===========================================================================
align $8000
; ---------------------------------------------------------------------------
; Music Bank 1
; ---------------------------------------------------------------------------
Snd_Bank1_Start: startBank
Snd_SKCredits: include "Sound/Music/Mus91 - Credits.asm"
Snd_GameOver: include "Sound/Music/Game Over.asm"
Snd_Continue: include "Sound/Music/Continue.asm"
Snd_Results: include "Sound/Music/Level Outro.asm"
Snd_Invic: include "Sound/Music/Mus87 - Invincibility.asm"
Snd_Menu: include "Sound/Music/Menu.asm"
Snd_FinalBoss: include "Sound/Music/Final Boss.asm"
Snd_PresSega: include "Sound/Music/Mus8B - Ending.asm"
finishBank
; ---------------------------------------------------------------------------
; Music Bank 2
; ---------------------------------------------------------------------------
Snd_Bank2_Start: startBank
Snd_FBZ1: include "Sound/Music/FBZ1.asm"
Snd_FBZ2: include "Sound/Music/FBZ2.asm"
Snd_MHZ1: include "Sound/Music/MHZ1.asm"
Snd_MHZ2: include "Sound/Music/MHZ2.asm"
Snd_SOZ1: include "Sound/Music/SOZ1.asm"
Snd_SOZ2: include "Sound/Music/SOZ2.asm"
Snd_LRZ1: include "Sound/Music/LRZ1.asm"
Snd_LRZ2: include "Sound/Music/LRZ2.asm"
Snd_SSZ: include "Sound/Music/SSZ.asm"
Snd_DEZ1: include "Sound/Music/DEZ1.asm"
Snd_DEZ2: include "Sound/Music/DEZ2.asm"
Snd_Minib_SK: include "Sound/Music/Miniboss.asm"
Snd_Boss: include "Sound/Music/Mus8C - Boss.asm"
Snd_DDZ: include "Sound/Music/DDZ.asm"
Snd_PachBonus: include "Sound/Music/Pachinko.asm"
Snd_SpecialS: include "Sound/Music/Mus89 - Special Stage.asm"
Snd_SlotBonus: include "Sound/Music/Slots.asm"
Snd_Knux: include "Sound/Music/Knuckles.asm"
Snd_Title: include "Sound/Music/Mus8A - Title Screen.asm"
Snd_1UP: include "Sound/Music/Mus88 - Extra Life.asm"
Snd_Emerald: include "Sound/Music/Chaos Emerald.asm"
finishBank
; ---------------------------------------------------------------------------
; Music Bank 3
; ---------------------------------------------------------------------------
Snd_Bank3_Start: startBank
Snd_AIZ1: include "Sound/Music/Mus81 - GHZ.asm"
Snd_AIZ2: include "Sound/Music/Mus82 - LZ.asm"
Snd_HCZ1: include "Sound/Music/Mus83 - MZ.asm"
Snd_HCZ2: include "Sound/Music/Mus84 - SLZ.asm"
Snd_MGZ1: include "Sound/Music/Mus85 - SYZ.asm"
Snd_MGZ2: include "Sound/Music/Mus86 - SBZ.asm"
Snd_CNZ1: include "Sound/Music/Mus8D - FZ.asm"
Snd_CNZ2: include "Sound/Music/CNZ2.asm"
finishBank
; ---------------------------------------------------------------------------
; Music Bank 4
; ---------------------------------------------------------------------------
Snd_Bank4_Start: startBank
Snd_ICZ1: include "Sound/Music/ICZ1.asm"
Snd_ICZ2: include "Sound/Music/ICZ2.asm"
Snd_LBZ1: include "Sound/Music/LBZ1.asm"
Snd_LBZ2: include "Sound/Music/LBZ2.asm"
finishBank
; ---------------------------------------------------------------------------
; Music Bank 5
; ---------------------------------------------------------------------------
Snd_Bank5_Start: startBank
Snd_GumBonus: include "Sound/Music/Gum Ball Machine.asm"
Snd_ALZ: include "Sound/Music/Azure Lake.asm"
Snd_BPZ: include "Sound/Music/Balloon Park.asm"
Snd_DPZ: include "Sound/Music/Desert Palace.asm"
Snd_CGZ: include "Sound/Music/Chrome Gadget.asm"
Snd_EMZ: include "Sound/Music/Endless Mine.asm"
Snd_S3Credits: include "Sound/Music/Sonic 3 Credits.asm"
Snd_2PMenu: include "Sound/Music/Competition Menu.asm"
Snd_Drown: include "Sound/Music/Countdown.asm"
finishBank
; ===========================================================================
; Sound Bank
; ===========================================================================
SndBank: startBank
SEGA_PCM: binclude "Sound/Sega PCM.bin"
SEGA_PCM_End
align 2
Sound_33: include "Sound/SFX/33.asm"
Sound_34: include "Sound/SFX/34.asm"
Sound_35: include "Sound/SFX/35.asm"
Sound_36: include "Sound/SFX/36.asm"
Sound_37: include "Sound/SFX/37.asm"
Sound_38: include "Sound/SFX/38.asm"
Sound_39: include "Sound/SFX/39.asm"
Sound_3A: include "Sound/SFX/3A.asm"
Sound_3B: include "Sound/SFX/3B.asm"
Sound_3C: include "Sound/SFX/3C.asm"
Sound_3D: include "Sound/SFX/3D.asm"
Sound_3E: include "Sound/SFX/3E.asm"
Sound_3F: include "Sound/SFX/3F.asm"
Sound_40: include "Sound/SFX/40.asm"
Sound_41: include "Sound/SFX/41.asm"
Sound_42: include "Sound/SFX/42.asm"
Sound_43: include "Sound/SFX/43.asm"
Sound_44: include "Sound/SFX/44.asm"
Sound_45: include "Sound/SFX/45.asm"
Sound_46: include "Sound/SFX/46.asm"
Sound_47: include "Sound/SFX/47.asm"
Sound_48: include "Sound/SFX/48.asm"
Sound_49: include "Sound/SFX/49.asm"
Sound_4A: include "Sound/SFX/4A.asm"
Sound_4B: include "Sound/SFX/4B.asm"
Sound_4C: include "Sound/SFX/4C.asm"
Sound_4D: include "Sound/SFX/4D.asm"
Sound_4E: include "Sound/SFX/4E.asm"
Sound_4F: include "Sound/SFX/4F.asm"
Sound_50: include "Sound/SFX/50.asm"
Sound_51: include "Sound/SFX/51.asm"
Sound_52: include "Sound/SFX/52.asm"
Sound_53: include "Sound/SFX/53.asm"
Sound_54: include "Sound/SFX/54.asm"
Sound_55: include "Sound/SFX/55.asm"
Sound_56: include "Sound/SFX/56.asm"
Sound_57: include "Sound/SFX/57.asm"
Sound_58: include "Sound/SFX/58.asm"
Sound_59: include "Sound/SFX/59.asm"
Sound_5A: include "Sound/SFX/5A.asm"
Sound_5B: include "Sound/SFX/5B.asm"
Sound_5C: include "Sound/SFX/5C.asm"
Sound_5D: include "Sound/SFX/5D.asm"
Sound_5E: include "Sound/SFX/5E.asm"
Sound_5F: include "Sound/SFX/5F.asm"
Sound_60: include "Sound/SFX/60.asm"
Sound_61: include "Sound/SFX/61.asm"
Sound_62: include "Sound/SFX/62.asm"
Sound_63: include "Sound/SFX/63.asm"
Sound_64: include "Sound/SFX/64.asm"
Sound_65: include "Sound/SFX/65.asm"
Sound_66: include "Sound/SFX/66.asm"
Sound_67: include "Sound/SFX/67.asm"
Sound_68: include "Sound/SFX/68.asm"
Sound_69: include "Sound/SFX/69.asm"
Sound_6A: include "Sound/SFX/6A.asm"
Sound_6B: include "Sound/SFX/6B.asm"
Sound_6C: include "Sound/SFX/6C.asm"
Sound_6D: include "Sound/SFX/6D.asm"
Sound_6E: include "Sound/SFX/6E.asm"
Sound_6F: include "Sound/SFX/6F.asm"
Sound_70: include "Sound/SFX/70.asm"
Sound_71: include "Sound/SFX/71.asm"
Sound_72: include "Sound/SFX/72.asm"
Sound_73: include "Sound/SFX/73.asm"
Sound_74: include "Sound/SFX/74.asm"
Sound_75: include "Sound/SFX/75.asm"
Sound_76: include "Sound/SFX/76.asm"
Sound_77: include "Sound/SFX/77.asm"
Sound_78: include "Sound/SFX/78.asm"
Sound_79: include "Sound/SFX/79.asm"
Sound_7A: include "Sound/SFX/7A.asm"
Sound_7B: include "Sound/SFX/7B.asm"
Sound_7C: include "Sound/SFX/7C.asm"
Sound_7D: include "Sound/SFX/7D.asm"
Sound_7E: include "Sound/SFX/7E.asm"
Sound_7F: include "Sound/SFX/7F.asm"
Sound_80: include "Sound/SFX/80.asm"
Sound_81: include "Sound/SFX/81.asm"
Sound_82: include "Sound/SFX/82.asm"
Sound_83: include "Sound/SFX/83.asm"
Sound_84: include "Sound/SFX/84.asm"
Sound_85: include "Sound/SFX/85.asm"
Sound_86: include "Sound/SFX/86.asm"
Sound_87: include "Sound/SFX/87.asm"
Sound_88: include "Sound/SFX/88.asm"
Sound_89: include "Sound/SFX/89.asm"
Sound_8A: include "Sound/SFX/8A.asm"
Sound_8B: include "Sound/SFX/8B.asm"
Sound_8C: include "Sound/SFX/8C.asm"
Sound_8D: include "Sound/SFX/8D.asm"
Sound_8E: include "Sound/SFX/8E.asm"
Sound_8F: include "Sound/SFX/8F.asm"
Sound_90: include "Sound/SFX/90.asm"
Sound_91: include "Sound/SFX/91.asm"
Sound_92: include "Sound/SFX/92.asm"
Sound_93: include "Sound/SFX/93.asm"
Sound_94: include "Sound/SFX/94.asm"
Sound_95: include "Sound/SFX/95.asm"
Sound_96: include "Sound/SFX/96.asm"
Sound_97: include "Sound/SFX/97.asm"
Sound_98: include "Sound/SFX/98.asm"
Sound_99: include "Sound/SFX/99.asm"
Sound_9A: include "Sound/SFX/9A.asm"
Sound_9B: include "Sound/SFX/9B.asm"
Sound_9C: include "Sound/SFX/9C.asm"
Sound_9D: include "Sound/SFX/9D.asm"
Sound_9E: include "Sound/SFX/9E.asm"
Sound_9F: include "Sound/SFX/9F.asm"
Sound_A0: include "Sound/SFX/A0.asm"
Sound_A1: include "Sound/SFX/A1.asm"
Sound_A2: include "Sound/SFX/A2.asm"
Sound_A3: include "Sound/SFX/A3.asm"
Sound_A4: include "Sound/SFX/A4.asm"
Sound_A5: include "Sound/SFX/A5.asm"
Sound_A6: include "Sound/SFX/A6.asm"
Sound_A7: include "Sound/SFX/A7.asm"
Sound_A8: include "Sound/SFX/A8.asm"
Sound_A9: include "Sound/SFX/A9.asm"
Sound_AA: include "Sound/SFX/AA.asm"
Sound_AB: include "Sound/SFX/AB.asm"
Sound_AC: include "Sound/SFX/AC.asm"
Sound_AD: include "Sound/SFX/AD.asm"
Sound_AE: include "Sound/SFX/AE.asm"
Sound_AF: include "Sound/SFX/AF.asm"
Sound_B0: include "Sound/SFX/B0.asm"
Sound_B1: include "Sound/SFX/B1.asm"
Sound_B2: include "Sound/SFX/B2.asm"
Sound_B3: include "Sound/SFX/B3.asm"
Sound_B4: include "Sound/SFX/B4.asm"
Sound_B5: include "Sound/SFX/B5.asm"
Sound_B6: include "Sound/SFX/B6.asm"
Sound_B7: include "Sound/SFX/B7.asm"
Sound_B8: include "Sound/SFX/B8.asm"
Sound_B9: include "Sound/SFX/B9.asm"
Sound_BA: include "Sound/SFX/BA.asm"
Sound_BB: include "Sound/SFX/BB.asm"
Sound_BC: include "Sound/SFX/BC.asm"
Sound_BD: include "Sound/SFX/BD.asm"
Sound_BE: include "Sound/SFX/BE.asm"
Sound_BF: include "Sound/SFX/BF.asm"
Sound_C0: include "Sound/SFX/C0.asm"
Sound_C1: include "Sound/SFX/C1.asm"
Sound_C2: include "Sound/SFX/C2.asm"
Sound_C3: include "Sound/SFX/C3.asm"
Sound_C4: include "Sound/SFX/C4.asm"
Sound_C5: include "Sound/SFX/C5.asm"
Sound_C6: include "Sound/SFX/C6.asm"
Sound_C7: include "Sound/SFX/C7.asm"
Sound_C8: include "Sound/SFX/C8.asm"
Sound_C9: include "Sound/SFX/C9.asm"
Sound_CA: include "Sound/SFX/CA.asm"
Sound_CB: include "Sound/SFX/CB.asm"
Sound_CC: include "Sound/SFX/CC.asm"
Sound_CD: include "Sound/SFX/CD.asm"
Sound_CE: include "Sound/SFX/CE.asm"
Sound_CF: include "Sound/SFX/CF.asm"
Sound_D0: include "Sound/SFX/D0.asm"
Sound_D1: include "Sound/SFX/D1.asm"
Sound_D2: include "Sound/SFX/D2.asm"
Sound_D3: include "Sound/SFX/D3.asm"
Sound_D4: include "Sound/SFX/D4.asm"
Sound_D5: include "Sound/SFX/D5.asm"
Sound_D6: include "Sound/SFX/D6.asm"
Sound_D7: include "Sound/SFX/D7.asm"
Sound_D8: include "Sound/SFX/D8.asm"
Sound_D9: include "Sound/SFX/D9.asm"
Sound_DA: include "Sound/SFX/DA.asm"
Sound_DB: include "Sound/SFX/DB.asm"
finishBank
Driver data files
Then delete the sound, linux (if it exists), mac (if it exists), and win32 folders as well as any build.bat, split.bat, build.sh, or split.sh sort of file. Now unpack this into your disassembly folder:
Download The Sonic 3 Driver data files
File: driver_stuff.zip (4.08 MB) (info)
|
You will need to replace the files and folders above so that our driver will work.
Enabling Sonic 2 / 3 Level Music Memory
Doing this is optional as it has little to do with the driver itself, but is a very useful and quick hack that makes life a lot easier for the more painful phase of using a massively different sound system than before, fixing the freaken sound. Sonic 1 uses a second index for restoring music as opposed to actually memorizing it like later sonic games do.
We begin on making the game remebmer, here
First we will locate:
Level_PlayBgm:
lea (MusicList).l,a1 ; load music playlist
move.b (a1,d0.w),d0 ; add d0 to a1
bsr.w PlaySound ; play music
move.b #$34,($FFFFD080).w ; load title card object
and insert a new line to make it read:
Level_PlayBgm:
lea (MusicList).l,a1 ; load music playlist
move.b (a1,d0.w),d0 ; add d0 to a1
move.w d0,($FFFFFF90).w ; store level music
bsr.w PlaySound ; play music
move.b #$34,($FFFFD080).w ; load title card object
This is so it remembers what song was playing most of the time.
Properly restore music after invincibility monitors
Now we will locate:
move.b ($FFFFFE10).w,d0
cmpi.w #$103,($FFFFFE10).w ; check if level is SBZ3
bne.s Obj01_PlayMusic
moveq #5,d0 ; play SBZ music
Obj01_PlayMusic:
lea (MusicList2).l,a1
move.b (a1,d0.w),d0
jsr (PlaySound).l ; play normal music
seems the invincibility stars conflict with music memory, so we will just make it use music list 1, so the result is:
move.b ($FFFFFE10).w,d0
cmpi.w #$103,($FFFFFE10).w ; check if level is SBZ3
bne.s Obj01_PlayMusic
moveq #5,d0 ; play SBZ music
Obj01_PlayMusic:
lea (MusicList).l,a1 ; load music playlist
move.b (a1,d0.w),d0 ; add d0 to a1
move.w d0,($FFFFFF90).w ; store level music
jsr (PlaySound).l ; play normal music
then remove:
; ---------------------------------------------------------------------------
; Music to play after invincibility wears off
; ---------------------------------------------------------------------------
MusicList2: binclude misc\muslist2.bin
Restore music after getting air in all levels, not just LZ and SBZ3
now we want to fix the underwater counter music restore code, so locate:
ResumeMusic: ; XREF: Obj64_Wobble; Sonic_Water; Obj0A_ReduceAir
cmpi.w #$C,($FFFFFE14).w
bhi.s loc_140AC
move.w #$82,d0 ; play LZ music
cmpi.w #$103,($FFFFFE10).w ; check if level is 0103 (SBZ3)
bne.s loc_140A6
move.w #$86,d0 ; play SBZ music
loc_140A6:
jsr (PlaySound).l
and replace it with:
ResumeMusic: ; XREF: Obj64_Wobble; Sonic_Water; Obj0A_ReduceAir
cmpi.w #$C,($FFFFFE14).w
bhi.s loc_140AC
move.w ($FFFFFF90).w,d0 ;resume music
jsr (PlaySound).l
this will make is so that when you get air during the countdown, reguardless of the level, the music will always resume, just like in sonic2 and 3.
Restore music after a boss the sonic 2 and 3 way
now find:
loc_179E0:
clr.w $12(a0)
move.w #$81,d0
jsr (PlaySound).l ; play GHZ music
and change it to:
loc_179E0:
clr.w $12(a0)
move.w ($FFFFFF90).w,d0 ; restore level music
jsr (PlaySound).l
The we will do the same for: loc_18112, loc_1856C, loc_18BB4, and loc_194E0
that sets up music memory for later.
Fixing the Sounds and Music
Now we will begin fixing the sound effects, music stops, sega sound, sneakers, music fades, and music itself.
Fixing the sound commands
Before we fix anything else, we will be fixing the sound commands.
Stop Music
Go to:
SegaScreen: ; XREF: GameModeArray
move.b #$E4,d0
and change it to:
SegaScreen: ; XREF: GameModeArray
move.b #mus_Stop,d0
also do the same to: TitleScreen and EndingSequence
Fade Out
Go to:
PlayLevel: ; XREF: ROM:00003246�j ...
move.b #$C,($FFFFF600).w ; set screen mode to $0C (level)
move.b #3,($FFFFFE12).w ; set lives to 3
moveq #0,d0
move.w d0,($FFFFFE20).w ; clear rings
move.l d0,($FFFFFE22).w ; clear time
move.l d0,($FFFFFE26).w ; clear score
move.b d0,($FFFFFE16).w ; clear special stage number
move.b d0,($FFFFFE57).w ; clear emeralds
move.l d0,($FFFFFE58).w ; clear emeralds
move.l d0,($FFFFFE5C).w ; clear emeralds
move.b d0,($FFFFFE18).w ; clear continues
move.b #$E0,d0
bsr.w PlaySound_Special ; fade out music
rts
and change it to:
PlayLevel: ; XREF: ROM:00003246�j ...
move.b #$C,($FFFFF600).w ; set screen mode to $0C (level)
move.b #3,($FFFFFE12).w ; set lives to 3
moveq #0,d0
move.w d0,($FFFFFE20).w ; clear rings
move.l d0,($FFFFFE22).w ; clear time
move.l d0,($FFFFFE26).w ; clear score
move.b d0,($FFFFFE16).w ; clear special stage number
move.b d0,($FFFFFE57).w ; clear emeralds
move.l d0,($FFFFFE58).w ; clear emeralds
move.l d0,($FFFFFE5C).w ; clear emeralds
move.b d0,($FFFFFE18).w ; clear continues
move.b #mus_FadeOut,d0
bsr.w PlaySound_Special ; fade out music
rts
also do the same to: loc_33E4, Level, and Obj81_GetUp
Sega!
Go to:
Sega_WaitPallet:
move.b #2,($FFFFF62A).w
bsr.w DelayProgram
bsr.w PalCycle_Sega
bne.s Sega_WaitPallet
move.b #$E1,d0
bsr.w PlaySound_Special ; play "SEGA" sound
move.b #$14,($FFFFF62A).w
bsr.w DelayProgram
move.w #$1E,($FFFFF614).w
and change it to:
Sega_WaitPallet:
move.b #2,($FFFFF62A).w
bsr.w DelayProgram
bsr.w PalCycle_Sega
bne.s Sega_WaitPallet
move.b #mus_SEGA,d0
bsr.w PlaySound_Special ; play "SEGA" sound
move.b #$14,($FFFFF62A).w
bsr.w DelayProgram
move.w #$7E,($FFFFF614).w
Sneakers
First We need ro Fix the sneaker monitor, we will find:
Obj2E_ChkShoes:
cmpi.b #3,d0 ; does monitor contain speed shoes?
bne.s Obj2E_ChkShield
move.b #1,($FFFFFE2E).w ; speed up the BG music
move.w #$4B0,($FFFFD034).w ; time limit for the power-up
move.w #$C00,($FFFFF760).w ; change Sonic"s top speed
move.w #$18,($FFFFF762).w
move.w #$80,($FFFFF764).w
move.w #$E2,d0
jmp (PlaySound).l ; Speed up the music
and change it to:
Obj2E_ChkShoes:
cmpi.b #3,d0 ; does monitor contain speed shoes?
bne.s Obj2E_ChkShield
move.b #1,($FFFFFE2E).w ; speed up the BG music
move.w #$4B0,($FFFFD034).w ; time limit for the power-up
move.w #$C00,($FFFFF760).w ; change Sonic"s top speed
move.w #$18,($FFFFF762).w
move.w #$80,($FFFFF764).w
move.w #8,d0
jmp (SetTempo).l ; Speed up the music
next, we find:
Obj01_ChkShoes:
tst.b ($FFFFFE2E).w ; does Sonic have speed shoes?
beq.s Obj01_ExitChk ; if not, branch
tst.w $34(a0) ; check time remaining
beq.s Obj01_ExitChk
subq.w #1,$34(a0) ; subtract 1 from time
bne.s Obj01_ExitChk
move.w #$600,($FFFFF760).w ; restore Sonic"s speed
move.w #$C,($FFFFF762).w ; restore Sonic"s acceleration
move.w #$80,($FFFFF764).w ; restore Sonic"s deceleration
move.b #0,($FFFFFE2E).w ; cancel speed shoes
move.w #$E3,d0
jmp (PlaySound).l ; run music at normal speed
and change it to:
Obj01_ChkShoes:
tst.b ($FFFFFE2E).w ; does Sonic have speed shoes?
beq.s Obj01_ExitChk ; if not, branch
tst.w $34(a0) ; check time remaining
beq.s Obj01_ExitChk
subq.w #1,$34(a0) ; subtract 1 from time
bne.s Obj01_ExitChk
move.w #$600,($FFFFF760).w ; restore Sonic"s speed
move.w #$C,($FFFFF762).w ; restore Sonic"s acceleration
move.w #$80,($FFFFF764).w ; restore Sonic"s deceleration
move.b #0,($FFFFFE2E).w ; cancel speed shoes
move.w #0,d0
jmp (SetTempo).l ; run music at normal speed
Now the sneakers should be working as expected.
Fixing the Sound Effects
Now on to the sound effects themselves. You are to search for an instance of:
move.w #$<number>,d0
where <number> is the sound test value for each sound effect as seen in the sound test. replace each one on this table with its proper equate also in the table:
Sonic 1 Sound Test Value | the proper equate | Sonic 1 Sound Test Value | the proper equate | Sonic 1 Sound Test Value | the proper equate |
---|---|---|---|---|---|
A0 | sfx_Jump | B0 | sfx_UnknownSaw | C0 | sfx_InstaAttack |
A1 | sfx_Starpost | B1 | sfx_Lightning | C1 | sfx_Break |
A2 | N/A | B2 | sfx_Drown | C2 | sfx_AirDing |
A3 | sfx_Death | B3 | sfx_FlamethrowerLoud | C3 | sfx_BigRing |
A4 | sfx_Skid | B4 | sfx_Bumper | C4 | sfx_Explode |
A5 | sfx_Explode | B5 | sfx_RingRight | C5 | sfx_Register |
A6 | sfx_SpikeHit | B6 | sfx_FanLatch | C6 | sfx_RingLoss |
A7 | sfx_PushBlock | B7 | sfx_Rumble | C7 | sfx_ChainTick |
A8 | sfx_Goal | B8 | N/A | C8 | sfx_FlamethrowerLoud |
A9 | sfx_ActionBlock | B9 | sfx_Collapse | C9 | sfx_UnknownPowerUp |
AA | sfx_Splash | BA | sfx_Diamonds | CA | sfx_EnterSS |
AB | N/A | BB | sfx_Thump | CB | sfx_Collapse |
AC | sfx_BossHit | BC | sfx_Dash | CC | sfx_Spring |
AD | sfx_Bubble | BD | sfx_Crash | CD | sfx_Button |
AE | sfx_LavaBall | BE | sfx_Roll | CE | N/A |
AF | sfx_Shield | BF | sfx_Continue | CF | sfx_Signpost |
Remember that jsr instruction to look for, also for jmp, or any other kind of branch, including bsr.w. also, somethings it is .b and not .w for the move instruction or a moveq instruction so pay attention to whether or not it is an actual sound. i.e.
move.w #$A0,d0
becomes
move.w #sfx_Jump,d0
Fixing the Music
Now we get to the fun part, fixing the music itself.
Fixing the Zones
TO fix the zone music, we need to edit the playlist. Left convert it to equates to make it easy work with:
; ---------------------------------------------------------------------------
; Music playlist
; ---------------------------------------------------------------------------
MusicList: binclude misc/muslist1.bin
align 2
change it to:
; ---------------------------------------------------------------------------
; Music playlist
; ---------------------------------------------------------------------------
MusicList:
dc.b mus_AIZ1
dc.b mus_AIZ2
dc.b mus_HCZ1
dc.b mus_HCZ2
dc.b mus_MGZ1
dc.b mus_MGZ2
dc.b mus_CNZ1
align 2
Invincibility
find:
Obj2E_ChkInvinc:
cmpi.b #5,d0 ; does monitor contain invincibility?
bne.s Obj2E_ChkRings
move.b #1,($FFFFFE2D).w ; make Sonic invincible
move.w #$4B0,($FFFFD032).w ; time limit for the power-up
move.b #$38,($FFFFD200).w ; load stars object ($3801)
move.b #1,($FFFFD21C).w
move.b #$38,($FFFFD240).w ; load stars object ($3802)
move.b #2,($FFFFD25C).w
move.b #$38,($FFFFD280).w ; load stars object ($3803)
move.b #3,($FFFFD29C).w
move.b #$38,($FFFFD2C0).w ; load stars object ($3804)
move.b #4,($FFFFD2DC).w
tst.b ($FFFFF7AA).w ; is boss mode on?
bne.s Obj2E_NoMusic ; if yes, branch
move.w #$87,d0
jmp (PlaySound).l ; play invincibility music
this will be very sililar to a sound effect fix:
Obj2E_ChkInvinc:
cmpi.b #5,d0 ; does monitor contain invincibility?
bne.s Obj2E_ChkRings
move.b #1,($FFFFFE2D).w ; make Sonic invincible
move.w #$4B0,($FFFFD032).w ; time limit for the power-up
move.b #$38,($FFFFD200).w ; load stars object ($3801)
move.b #1,($FFFFD21C).w
move.b #$38,($FFFFD240).w ; load stars object ($3802)
move.b #2,($FFFFD25C).w
move.b #$38,($FFFFD280).w ; load stars object ($3803)
move.b #3,($FFFFD29C).w
move.b #$38,($FFFFD2C0).w ; load stars object ($3804)
move.b #4,($FFFFD2DC).w
tst.b ($FFFFF7AA).w ; is boss mode on?
bne.s Obj2E_NoMusic ; if yes, branch
move.w #mus_Invincibility,d0
jmp (PlaySound).l ; play invincibility music
1Up
find:
move.w #$88,d0 ; play extra life music
Obj25_PlaySnd:
jmp (PlaySound_Special).l
and change it to:
move.w #mus_ExtraLife,d0 ; play extra life music
Obj25_PlaySnd:
jmp (PlaySound_Special).l
now look for:
ExtraLife:
addq.b #1,($FFFFFE12).w ; add 1 to the number of lives you have
addq.b #1,($FFFFFE1C).w ; add 1 to the lives counter
move.w #$88,d0
jmp (PlaySound).l ; play extra life music
; ===========================================================================
and change it to:
ExtraLife:
addq.b #1,($FFFFFE12).w ; add 1 to the number of lives you have
addq.b #1,($FFFFFE1C).w ; add 1 to the lives counter
move.w #mus_ExtraLife,d0
jmp (PlaySound).l ; play extra life music
; ===========================================================================
and finally look for:
Obj09_Get1Up:
addq.b #1,($FFFFFE12).w ; add 1 to number of lives
addq.b #1,($FFFFFE1C).w ; add 1 to lives counter
move.w #$88,d0
jsr (PlaySound).l ; play extra life music
moveq #0,d4
rts
and change it to:
Obj09_Get1Up:
addq.b #1,($FFFFFE12).w ; add 1 to number of lives
addq.b #1,($FFFFFE1C).w ; add 1 to lives counter
move.w #mus_ExtraLife,d0
jsr (PlaySound).l ; play extra life music
moveq #0,d4
rts
Special Stage
look under:
SS_ClrNemRam:
You sould find something like this:
bsr.w PalCycle_SS
clr.w ($FFFFF780).w ; set stage angle to "upright"
move.w #$40,($FFFFF782).w ; set stage rotation speed
move.w #$89,d0
bsr.w PlaySound ; play special stage BG music
move.w #0,($FFFFF790).w
lea (Demo_Index).l,a1
it needs to be changed to:
bsr.w PalCycle_SS
clr.w ($FFFFF780).w ; set stage angle to "upright"
move.w #$40,($FFFFF782).w ; set stage rotation speed
move.w #mus_SpecialStage,d0
bsr.w PlaySound ; play special stage BG music
move.w #0,($FFFFF790).w
lea (Demo_Index).l,a1
Title Screen
now find:
move.b #8A,d0 ; play title screen music
bsr.w PlaySound_Special
move.b #0,($FFFFFFFA).w ; disable debug mode
move.w #$178,($FFFFF614).w ; run title screen for $178 frames
lea ($FFFFD080).w,a1
moveq #0,d0
move.w #$F,d1
Title_ClrObjRam2:
as you can see, you know what to do here, change that line again:
move.b #mus_TitleScreen,d0 ; play title screen music
bsr.w PlaySound_Special
move.b #0,($FFFFFFFA).w ; disable debug mode
move.w #$178,($FFFFF614).w ; run title screen for $178 frames
lea ($FFFFD080).w,a1
moveq #0,d0
move.w #$F,d1
Title_ClrObjRam2:
Ending
once again, another one of those similar to sound effects. By now you might have figured out how to add custom sounds to badniks or other objects, if yes, kudos to you, if not, another guide will explain that. So, here we go:
move.w #$8B,d0
bsr.w PlaySound ; play ending sequence music
btst #6,($FFFFF604).w ; is button A pressed?
beq.s End_LoadSonic ; if not, branch
move.b #1,($FFFFFFFA).w ; enable debug mode
End_LoadSonic:
change now!:
move.w #mus_Ending,d0
bsr.w PlaySound ; play ending sequence music
btst #6,($FFFFF604).w ; is button A pressed?
beq.s End_LoadSonic ; if not, branch
move.b #1,($FFFFFFFA).w ; enable debug mode
End_LoadSonic:
Boss
looks like we have 5 things to change in total each in blocks like this:
loc_6ED0:
move.w #$8C,d0
bsr.w PlaySound ; play boss music
move.b #1,($FFFFF7AA).w ; lock screen
addq.b #2,($FFFFF742).w
moveq #$11,d0
bra.w LoadPLC ; load boss patterns
that need to be changed to:
loc_6ED0:
move.w #mus_EndBoss,d0
bsr.w PlaySound ; play boss music
move.b #1,($FFFFF7AA).w ; lock screen
addq.b #2,($FFFFF742).w
moveq #$11,d0
bra.w LoadPLC ; load boss patterns
repeat with the rest: loc_6F4A, loc_70D0, loc_7144, and loc_71EC
Final Zone music following SBZ2
I almost forgot about this one, but here we go:
Obj3A_SBZ2: ; XREF: Obj3A_ChkPos2
cmpi.b #4,$1A(a0)
bne.w DeleteObject
addq.b #2,$24(a0)
clr.b ($FFFFF7CC).w ; unlock controls
move.w #$8D,d0
jmp (PlaySound).l ; play FZ music
change it to:
Obj3A_SBZ2: ; XREF: Obj3A_ChkPos2
cmpi.b #4,$1A(a0)
bne.w DeleteObject
addq.b #2,$24(a0)
clr.b ($FFFFF7CC).w ; unlock controls
move.w #mus_CNZ1,d0
jmp (PlaySound).l ; play FZ music
Outro
2 things to fix here, obviously. THe special stage outro and the level outro, we will start with the special stage one:
move.w #$8E,d0
jsr (PlaySound_Special).l ; play end-of-level music
lea ($FFFFD000).w,a1
moveq #0,d0
move.w #$7FF,d1
SS_EndClrObjRam:
to:
move.w #mus_GotThroughAct,d0
jsr (PlaySound_Special).l ; play end-of-level music
lea ($FFFFD000).w,a1
moveq #0,d0
move.w #$7FF,d1
SS_EndClrObjRam:
then:
loc_ECD0:
add.w d0,d0
move.w TimeBonuses(pc,d0.w),($FFFFF7D2).w ; set time bonus
move.w ($FFFFFE20).w,d0 ; load number of rings
mulu.w #10,d0 ; multiply by 10
move.w d0,($FFFFF7D4).w ; set ring bonus
move.w #$8E,d0
jsr (PlaySound_Special).l ; play "Sonic got through" music
to:
loc_ECD0:
add.w d0,d0
move.w TimeBonuses(pc,d0.w),($FFFFF7D2).w ; set time bonus
move.w ($FFFFFE20).w,d0 ; load number of rings
mulu.w #10,d0 ; multiply by 10
move.w d0,($FFFFF7D4).w ; set ring bonus
move.w #mus_GotThroughAct,d0
jsr (PlaySound_Special).l ; play "Sonic got through" music
Game Over / Time Over
okay, just one fix here will do it:
loc_138C2:
move.w #$8F,d0
jsr (PlaySound).l ; play game over music
changing to:
loc_138C2:
move.w #mus_GameOver,d0
jsr (PlaySound).l ; play game over music
Continue Screen
smack in the middle of Cont_ClrObjRam are these two lines:
move.b #$90,d0
bsr.w PlaySound ; play continue music
change them to:
move.b #mus_Continue,d0
bsr.w PlaySound ; play continue music
Credits
now this will surprise you. Yes we used the s&K credits slot for our sonic 1 music.Lets locate:
move.b #$91,d0
bsr.w PlaySound_Special ; play credits music
move.w #0,($FFFFFFF4).w ; set credits index number to 0
rts
; ===========================================================================
loc_52DA:
and change a single line causing it to read:
move.b #mus_CreditsK,d0
bsr.w PlaySound_Special ; play credits music
move.w #0,($FFFFFFF4).w ; set credits index number to 0
rts
; ===========================================================================
loc_52DA:
Drowning
Oh know, sonic is drowning in a sound effect instead of a music, lets fix that:
move.w #$92,d0
jsr (PlaySound).l ; play countdown music
loc_13F02:
you know what to do here by now:
move.w #mus_Drowning,d0
jsr (PlaySound).l ; play countdown music
loc_13F02:
Chaos Emerald
What? Did sonic get the emerald? Yes? WTF! Wrong sound effect, so lets fix that:
Obj09_NoEmer:
move.w #$93,d0
jsr (PlaySound_Special).l ; play emerald music
moveq #0,d4
rts
by changing a line to get:
Obj09_NoEmer:
move.w #mus_Emerald,d0
jsr (PlaySound_Special).l ; play emerald music
moveq #0,d4
rts
Fixing the sound test (Sound Select) on the level select
this will be a lot like fixing the sonic 2 level select, let's begin with this routine:
LevelSelect:
move.b #4,($FFFFF62A).w
bsr.w DelayProgram
bsr.w LevSelControls
bsr.w RunPLC_RAM
tst.l ($FFFFF680).w
bne.s LevelSelect
andi.b #$F0,($FFFFF605).w ; is A, B, C, or Start pressed?
beq.s LevelSelect ; if not, branch
move.w ($FFFFFF82).w,d0
cmpi.w #$14,d0 ; have you selected item $14 (sound test)?
bne.s LevSel_Level_SS ; if not, go to Level/SS subroutine
move.w ($FFFFFF84).w,d0
addi.w #$80,d0
tst.b ($FFFFFFE3).w ; is Japanese Credits cheat on?
beq.s LevSel_NoCheat ; if not, branch
cmpi.w #$9F,d0 ; is sound $9F being played?
beq.s LevSel_Ending ; if yes, branch
cmpi.w #$9E,d0 ; is sound $9E being played?
beq.s LevSel_Credits ; if yes, branch
LevSel_NoCheat:
cmpi.w #$94,d0 ; is sound $80-$94 being played?
bcs.s LevSel_PlaySnd ; if yes, branch
cmpi.w #$A0,d0 ; is sound $95-$A0 being played?
bcs.s LevelSelect ; if yes, branch
LevSel_PlaySnd:
bsr.w PlaySound_Special
bra.s LevelSelect
the andi.w line can be dropped, we will not be using the japanese cheats since they interfere with playback and we do not have to worry about whether a real song is playing here, so we will crop it down to this:
LevelSelect:
move.b #4,($FFFFF62A).w
bsr.w DelayProgram
bsr.w LevSelControls
bsr.w RunPLC_RAM
tst.l ($FFFFF680).w
bne.s LevelSelect
andi.b #$F0,($FFFFF605).w ; is A, B, C, or Start pressed?
beq.s LevelSelect ; if not, branch
move.w ($FFFFFF82).w,d0
cmpi.w #$14,d0 ; have you selected item $14 (sound test)?
bne.s LevSel_Level_SS ; if not, go to Level/SS subroutine
move.w ($FFFFFF84).w,d0
LevSel_PlaySnd:
bsr.w PlaySound_Special
bra.s LevelSelect
now remove all this junk:
LevSel_Ending: ; XREF: LevelSelect
move.b #$18,($FFFFF600).w ; set screen mode to $18 (Ending)
move.w #$600,($FFFFFE10).w ; set level to 0600 (Ending)
rts
; ===========================================================================
LevSel_Credits: ; XREF: LevelSelect
move.b #$1C,($FFFFF600).w ; set screen mode to $1C (Credits)
move.b #$91,d0
bsr.w PlaySound_Special ; play credits music
move.w #0,($FFFFFFF4).w
rts
; ===========================================================================
lets add a nice little touch to this with music on the level select. Like in sonic 2 and 3. We start here:
Title_ChkLevSel:
tst.b ($FFFFFFE0).w ; check if level select code is on
beq.w PlayLevel ; if not, play level
btst #6,($FFFFF604).w ; check if A is pressed
beq.w PlayLevel ; if not, play level
moveq #2,d0
bsr.w PalLoad2 ; load level select pallet
lea ($FFFFCC00).w,a1
moveq #0,d0
move.w #$DF,d1
we add these 2 lines
move.b #mus_DataSelect,d0
jsr PlaySound
to that section of code, making it into this:
Title_ChkLevSel:
tst.b ($FFFFFFE0).w ; check if level select code is on
beq.w PlayLevel ; if not, play level
btst #6,($FFFFF604).w ; check if A is pressed
beq.w PlayLevel ; if not, play level
move.b #mus_DataSelect,d0
jsr PlaySound
moveq #2,d0
bsr.w PalLoad2 ; load level select pallet
lea ($FFFFCC00).w,a1
moveq #0,d0
move.w #$DF,d1
now the next thing we need to edit is THIS section of code:
LevSel_SndTest: ; XREF: LevSelControls
cmpi.w #$14,($FFFFFF82).w ; is item $14 selected?
bne.s LevSel_NoMove ; if not, branch
move.b ($FFFFF605).w,d1
andi.b #$C,d1 ; is left/right pressed?
beq.s LevSel_NoMove ; if not, branch
move.w ($FFFFFF84).w,d0
btst #2,d1 ; is left pressed?
beq.s LevSel_Right ; if not, branch
subq.w #1,d0 ; subtract 1 from sound test
bcc.s LevSel_Right
moveq #$4F,d0 ; if sound test moves below 0, set to $4F
LevSel_Right:
btst #3,d1 ; is right pressed?
beq.s LevSel_Refresh2 ; if not, branch
addq.w #1,d0 ; add 1 to sound test
cmpi.w #$50,d0
bcs.s LevSel_Refresh2
moveq #0,d0 ; if sound test moves above $4F, set to 0
LevSel_Refresh2:
move.w d0,($FFFFFF84).w ; set sound test number
bsr.w LevSelTextLoad ; refresh text
LevSel_NoMove:
rts
first off, $4F is the wrong value, we do not start at $80 amymore, second, we do not want it to go to 0 after $4F, so the corrected code is like this:
LevSel_SndTest: ; XREF: LevSelControls
cmpi.w #$14,($FFFFFF82).w ; is item $14 selected?
bne.s LevSel_NoMove ; if not, branch
move.b ($FFFFF605).w,d1
andi.b #$C,d1 ; is left/right pressed?
beq.s LevSel_NoMove ; if not, branch
move.w ($FFFFFF84).w,d0
btst #2,d1 ; is left pressed?
beq.s LevSel_Right ; if not, branch
subq.w #1,d0 ; subtract 1 from sound test
bcc.s LevSel_Right
moveq #$FF,d0 ; if sound test moves below 0, set to $4F
LevSel_Right:
btst #3,d1 ; is right pressed?
beq.s LevSel_Refresh2 ; if not, branch
addq.w #1,d0 ; add 1 to sound test
LevSel_Refresh2:
move.w d0,($FFFFFF84).w ; set sound test number
bsr.w LevSelTextLoad ; refresh text
LevSel_NoMove:
rts
okay, now we need to stop it from adding $80 to the sound test value like s2 does, so we go to:
loc_3550:
move.l #$6C300003,($C00004).l ; screen position (sound test)
move.w ($FFFFFF84).w,d0
addi.w #$80,d0
move.b d0,d2
lsr.b #4,d0
bsr.w LevSel_ChgSnd
move.b d2,d0
bsr.w LevSel_ChgSnd
rts
and delete this line:
addi.w #$80,d0
Branch fixes
there are some 'bsr's that need fixing otherwise it won't build. Open "09 Sonic in Special Stage.asm". These lines here:
bsr.s sub_1BD30
in sub_1BCE8 need changing to:
jsr sub_1BD30
Now you should be done with adding the s&K driver to your hack. You might check out, what's next.
GitHub Disassembly users, start here
This guide is made for the Macro Assembler AS. If your hack was built off the ASM68K version of the GitHub disassembly, you have to port what you were working on to AS. Staying on ASM68K will not allow you to follow on with this guide.
Not to worry because there are plenty of AS based disasms that will work. If you like MapMacros, then I have a disasm for you. if you use projectrsonic1-two-eight, I also have one for you, otherwise use the official git version of the AS based disasm. If you are on Linux or Mac, I would be surprised if you aren't already using a compatible disassembly.
Overview
First off, Sonic 3's sound driver has the V_Int reloader built into it so it is not needed. Some routines need replacement, sounds need fixing, and we need to replace the sounds and music.
Constants
First off, we will be editing something that makes this superior for you than your hivebrain using friends. We will look for:
z80_dac3_pitch: equ z80_ram+zSample3_Pitch
z80_dac_status: equ z80_ram+zDAC_Status
z80_dac_sample: equ z80_ram+zDAC_Sample
and remove all 3 of those lines. The sonic1 sound driver will no longer be there, so this will not work anymore. Now replace:
; Background music
bgm__First: equ $81
bgm_GHZ: equ ((ptr_mus81-MusicIndex)/4)+bgm__First
bgm_LZ: equ ((ptr_mus82-MusicIndex)/4)+bgm__First
bgm_MZ: equ ((ptr_mus83-MusicIndex)/4)+bgm__First
bgm_SLZ: equ ((ptr_mus84-MusicIndex)/4)+bgm__First
bgm_SYZ: equ ((ptr_mus85-MusicIndex)/4)+bgm__First
bgm_SBZ: equ ((ptr_mus86-MusicIndex)/4)+bgm__First
bgm_Invincible: equ ((ptr_mus87-MusicIndex)/4)+bgm__First
bgm_ExtraLife: equ ((ptr_mus88-MusicIndex)/4)+bgm__First
bgm_SS: equ ((ptr_mus89-MusicIndex)/4)+bgm__First
bgm_Title: equ ((ptr_mus8A-MusicIndex)/4)+bgm__First
bgm_Ending: equ ((ptr_mus8B-MusicIndex)/4)+bgm__First
bgm_Boss: equ ((ptr_mus8C-MusicIndex)/4)+bgm__First
bgm_FZ: equ ((ptr_mus8D-MusicIndex)/4)+bgm__First
bgm_GotThrough: equ ((ptr_mus8E-MusicIndex)/4)+bgm__First
bgm_GameOver: equ ((ptr_mus8F-MusicIndex)/4)+bgm__First
bgm_Continue: equ ((ptr_mus90-MusicIndex)/4)+bgm__First
bgm_Credits: equ ((ptr_mus91-MusicIndex)/4)+bgm__First
bgm_Drowning: equ ((ptr_mus92-MusicIndex)/4)+bgm__First
bgm_Emerald: equ ((ptr_mus93-MusicIndex)/4)+bgm__First
bgm__Last: equ ((ptr_musend-MusicIndex-4)/4)+bgm__First
; Sound effects
sfx__First: equ $A0
sfx_Jump: equ ((ptr_sndA0-SoundIndex)/4)+sfx__First
sfx_Lamppost: equ ((ptr_sndA1-SoundIndex)/4)+sfx__First
sfx_A2: equ ((ptr_sndA2-SoundIndex)/4)+sfx__First
sfx_Death: equ ((ptr_sndA3-SoundIndex)/4)+sfx__First
sfx_Skid: equ ((ptr_sndA4-SoundIndex)/4)+sfx__First
sfx_A5: equ ((ptr_sndA5-SoundIndex)/4)+sfx__First
sfx_HitSpikes: equ ((ptr_sndA6-SoundIndex)/4)+sfx__First
sfx_Push: equ ((ptr_sndA7-SoundIndex)/4)+sfx__First
sfx_SSGoal: equ ((ptr_sndA8-SoundIndex)/4)+sfx__First
sfx_SSItem: equ ((ptr_sndA9-SoundIndex)/4)+sfx__First
sfx_Splash: equ ((ptr_sndAA-SoundIndex)/4)+sfx__First
sfx_AB: equ ((ptr_sndAB-SoundIndex)/4)+sfx__First
sfx_HitBoss: equ ((ptr_sndAC-SoundIndex)/4)+sfx__First
sfx_Bubble: equ ((ptr_sndAD-SoundIndex)/4)+sfx__First
sfx_Fireball: equ ((ptr_sndAE-SoundIndex)/4)+sfx__First
sfx_Shield: equ ((ptr_sndAF-SoundIndex)/4)+sfx__First
sfx_Saw: equ ((ptr_sndB0-SoundIndex)/4)+sfx__First
sfx_Electric: equ ((ptr_sndB1-SoundIndex)/4)+sfx__First
sfx_Drown: equ ((ptr_sndB2-SoundIndex)/4)+sfx__First
sfx_Flamethrower:equ ((ptr_sndB3-SoundIndex)/4)+sfx__First
sfx_Bumper: equ ((ptr_sndB4-SoundIndex)/4)+sfx__First
sfx_Ring: equ ((ptr_sndB5-SoundIndex)/4)+sfx__First
sfx_SpikesMove: equ ((ptr_sndB6-SoundIndex)/4)+sfx__First
sfx_Rumbling: equ ((ptr_sndB7-SoundIndex)/4)+sfx__First
sfx_B8: equ ((ptr_sndB8-SoundIndex)/4)+sfx__First
sfx_Collapse: equ ((ptr_sndB9-SoundIndex)/4)+sfx__First
sfx_SSGlass: equ ((ptr_sndBA-SoundIndex)/4)+sfx__First
sfx_Door: equ ((ptr_sndBB-SoundIndex)/4)+sfx__First
sfx_Teleport: equ ((ptr_sndBC-SoundIndex)/4)+sfx__First
sfx_ChainStomp: equ ((ptr_sndBD-SoundIndex)/4)+sfx__First
sfx_Roll: equ ((ptr_sndBE-SoundIndex)/4)+sfx__First
sfx_Continue: equ ((ptr_sndBF-SoundIndex)/4)+sfx__First
sfx_Basaran: equ ((ptr_sndC0-SoundIndex)/4)+sfx__First
sfx_BreakItem: equ ((ptr_sndC1-SoundIndex)/4)+sfx__First
sfx_Warning: equ ((ptr_sndC2-SoundIndex)/4)+sfx__First
sfx_GiantRing: equ ((ptr_sndC3-SoundIndex)/4)+sfx__First
sfx_Bomb: equ ((ptr_sndC4-SoundIndex)/4)+sfx__First
sfx_Cash: equ ((ptr_sndC5-SoundIndex)/4)+sfx__First
sfx_RingLoss: equ ((ptr_sndC6-SoundIndex)/4)+sfx__First
sfx_ChainRise: equ ((ptr_sndC7-SoundIndex)/4)+sfx__First
sfx_Burning: equ ((ptr_sndC8-SoundIndex)/4)+sfx__First
sfx_Bonus: equ ((ptr_sndC9-SoundIndex)/4)+sfx__First
sfx_EnterSS: equ ((ptr_sndCA-SoundIndex)/4)+sfx__First
sfx_WallSmash: equ ((ptr_sndCB-SoundIndex)/4)+sfx__First
sfx_Spring: equ ((ptr_sndCC-SoundIndex)/4)+sfx__First
sfx_Switch: equ ((ptr_sndCD-SoundIndex)/4)+sfx__First
sfx_RingLeft: equ ((ptr_sndCE-SoundIndex)/4)+sfx__First
sfx_Signpost: equ ((ptr_sndCF-SoundIndex)/4)+sfx__First
sfx__Last: equ ((ptr_sndend-SoundIndex-4)/4)+sfx__First
; Special sound effects
spec__First: equ $D0
sfx_Waterfall: equ ((ptr_sndD0-SpecSoundIndex)/4)+spec__First
spec__Last: equ ((ptr_specend-SpecSoundIndex-4)/4)+spec__First
flg__First: equ $E0
bgm_Fade: equ ((ptr_flgE0-Sound_ExIndex)/4)+flg__First
sfx_Sega: equ ((ptr_flgE1-Sound_ExIndex)/4)+flg__First
bgm_Speedup: equ ((ptr_flgE2-Sound_ExIndex)/4)+flg__First
bgm_Slowdown: equ ((ptr_flgE3-Sound_ExIndex)/4)+flg__First
bgm_Stop: equ ((ptr_flgE4-Sound_ExIndex)/4)+flg__First
flg__Last: equ ((ptr_flgend-Sound_ExIndex-4)/4)+flg__First
with:
; ---------------------------------------------------------------------------
; Sound commands list.
phase $E1
mus__FirstCmd = * ; ID of the first sound command
mus_FadeOut ds.b 1 ; $E1 - fade out music
mus_Stop ds.b 1 ; $E2 - stop music and sound effects
mus_MutePSG ds.b 1 ; $E3 - mute all PSG channels
mus_StopSFX ds.b 1 ; $E4 - stop all sound effects
mus_FadeOut2 ds.b 1 ; $E5 - fade out music (duplicate)
Mus__EndCmd = * ; next ID after last sound command
mus_FA = $FA ; $FA - ???
mus_StopSEGA = $FE ; $FE - Stop SEGA sound
mus_SEGA = $FF ; $FF - Play SEGA sound
phase $01
Mus__First = * ; ID of the first music
mus_AIZ1 ds.b 1 ; $01
mus_AIZ2 ds.b 1 ; $02
mus_HCZ1 ds.b 1 ; $03
mus_HCZ2 ds.b 1 ; $04
mus_MGZ1 ds.b 1 ; $05
mus_MGZ2 ds.b 1 ; $06
mus_CNZ1 ds.b 1 ; $07
mus_CNZ2 ds.b 1 ; $08
mus_FBZ1 ds.b 1 ; $09
mus_FBZ2 ds.b 1 ; $0A
mus_ICZ1 ds.b 1 ; $0B
mus_ICZ2 ds.b 1 ; $0C
mus_LBZ1 ds.b 1 ; $0D
mus_LBZ2 ds.b 1 ; $0E
mus_MHZ1 ds.b 1 ; $0F
mus_MHZ2 ds.b 1 ; $10
mus_SOZ1 ds.b 1 ; $11
mus_SOZ2 ds.b 1 ; $12
mus_LRZ1 ds.b 1 ; $13
mus_HPZ ds.b 1 ; $14
mus_SSZ ds.b 1 ; $15
mus_DEZ1 ds.b 1 ; $16
mus_DEZ2 ds.b 1 ; $17
mus_MinibossK ds.b 1 ; $18
mus_EndBoss ds.b 1 ; $19
mus_DDZ ds.b 1 ; $1A
mus_MagneticOrbs ds.b 1 ; $1B
mus_SpecialStage ds.b 1 ; $1C
mus_SlotMachine ds.b 1 ; $1D
mus_Gumball ds.b 1 ; $1E
mus_Knuckles ds.b 1 ; $1F
mus_ALZ ds.b 1 ; $20
mus_BPZ ds.b 1 ; $21
mus_DPZ ds.b 1 ; $22
mus_CGZ ds.b 1 ; $23
mus_EMZ ds.b 1 ; $24
mus_TitleScreen ds.b 1 ; $25
mus_Credits3 ds.b 1 ; $26
mus_GameOver ds.b 1 ; $27
mus_Continue ds.b 1 ; $28
mus_GotThroughAct ds.b 1 ; $29
mus_ExtraLife ds.b 1 ; $2A
mus_Emerald ds.b 1 ; $2B
mus_Invincibility ds.b 1 ; $2C
mus_CompetitionMenu ds.b 1 ; $2D
mus_Miniboss ds.b 1 ; $2E
mus_DataSelect ds.b 1 ; $2F
mus_FinalBoss ds.b 1 ; $30
mus_Drowning ds.b 1 ; $31
mus_Ending ds.b 1 ; $32
Mus__End = * ; next ID after last music
; ---------------------------------------------------------------------------
; Sound effect ID's list. These do not affect the sound driver, be careful.
phase $33
sfx_First = * ; ID of the first sound effect
sfx_RingRight ds.b 1 ; $33
sfx_RingLeft ds.b 1 ; $34
sfx_Death ds.b 1 ; $35
sfx_Skid ds.b 1 ; $36
sfx_SpikeHit ds.b 1 ; $37
sfx_Bubble ds.b 1 ; $38
sfx_Splash ds.b 1 ; $39
sfx_Shield ds.b 1 ; $3A
sfx_Drown ds.b 1 ; $3B
sfx_Roll ds.b 1 ; $3C
sfx_Break ds.b 1 ; $3D
sfx_FireShield ds.b 1 ; $3E
sfx_BubbleShield ds.b 1 ; $3F
sfx_UnknownShield ds.b 1 ; $40
sfx_ElectricShield ds.b 1 ; $41
sfx_InstaAttack ds.b 1 ; $42
sfx_FireAttack ds.b 1 ; $43
sfx_BubbleAttack ds.b 1 ; $44
sfx_ElectricAttack ds.b 1 ; $45
sfx_SuperAlt ds.b 1 ; $46
sfx_SandwallRise ds.b 1 ; $47
sfx_Blast ds.b 1 ; $48
sfx_Thump ds.b 1 ; $49
sfx_Grab ds.b 1 ; $4A
sfx_WaterfallSplash ds.b 1 ; $4B
sfx_GlideLand ds.b 1 ; $4C
sfx_Projectile ds.b 1 ; $4D
sfx_MissileExplode ds.b 1 ; $4E
sfx_FlamethrowerQuiet ds.b 1 ; $4F
sfx_BossActivate ds.b 1 ; $50
sfx_MissileThrow ds.b 1 ; $51
sfx_SpikeMove ds.b 1 ; $52
sfx_Charging ds.b 1 ; $53
sfx_BossLazer ds.b 1 ; $54
sfx_BlockConveyor ds.b 1 ; $55
sfx_FlipBridge ds.b 1 ; $56
sfx_Geyser ds.b 1 ; $57
sfx_FanLatch ds.b 1 ; $58
sfx_Collapse ds.b 1 ; $59
sfx_UnknownCharge ds.b 1 ; $5A
sfx_Button ds.b 1 ; $5B
sfx_MetalSpark ds.b 1 ; $5C
sfx_FloorThump ds.b 1 ; $5D
sfx_Lazer ds.b 1 ; $5E
sfx_Crash ds.b 1 ; $5F
sfx_BossZoom ds.b 1 ; $60
sfx_BossHitFloor ds.b 1 ; $61
sfx_Jump ds.b 1 ; $62
sfx_Starpost ds.b 1 ; $63
sfx_PulleyGrab ds.b 1 ; $64
sfx_BlueSphere ds.b 1 ; $65
sfx_AllSpheres ds.b 1 ; $66
sfx_LevelProjectile ds.b 1 ; $67
sfx_Perfect ds.b 1 ; $68
sfx_PushBlock ds.b 1 ; $69
sfx_Goal ds.b 1 ; $6A
sfx_ActionBlock ds.b 1 ; $6B
sfx_Splash2 ds.b 1 ; $6C
sfx_UnknownShift ds.b 1 ; $6D
sfx_BossHit ds.b 1 ; $6E
sfx_Rumble2 ds.b 1 ; $6F
sfx_LavaBall ds.b 1 ; $70
sfx_Shield2 ds.b 1 ; $71
sfx_Hoverpad ds.b 1 ; $72
sfx_Transporter ds.b 1 ; $73
sfx_TunnelBooster ds.b 1 ; $74
sfx_BalloonPlatform ds.b 1 ; $75
sfx_TrapDoor ds.b 1 ; $76
sfx_Balloon ds.b 1 ; $77
sfx_GravityMachine ds.b 1 ; $78
sfx_Lightning ds.b 1 ; $79
sfx_BossMagma ds.b 1 ; $7A
sfx_SmallBumpers ds.b 1 ; $7B
sfx_ChainTension ds.b 1 ; $7C
sfx_UnknownPump ds.b 1 ; $7D
sfx_GroundSlide ds.b 1 ; $7E
sfx_FrostPuff ds.b 1 ; $7F
sfx_IceSpikes ds.b 1 ; $80
sfx_TubeLauncher ds.b 1 ; $81
sfx_SandSplash ds.b 1 ; $82
sfx_BridgeCollapse ds.b 1 ; $83
sfx_UnknownPowerUp ds.b 1 ; $84
sfx_UnknownPowerDown ds.b 1 ; $85
sfx_Alarm ds.b 1 ; $86
sfx_MushroomBounce ds.b 1 ; $87
sfx_PulleyMove ds.b 1 ; $88
sfx_WeatherMachine ds.b 1 ; $89
sfx_Bouncy ds.b 1 ; $8A
sfx_ChopTree ds.b 1 ; $8B
sfx_ChopStuck ds.b 1 ; $8C
sfx_UnknownFlutter ds.b 1 ; $8D
sfx_UnknownRevving ds.b 1 ; $8E
sfx_DoorOpen ds.b 1 ; $8F
sfx_DoorMove ds.b 1 ; $90
sfx_DoorClose ds.b 1 ; $91
sfx_GhostAppear ds.b 1 ; $92
sfx_BossRecovery ds.b 1 ; $93
sfx_ChainTick ds.b 1 ; $94
sfx_BossHand ds.b 1 ; $95
sfx_MetalLand ds.b 1 ; $96
sfx_EnemyBreath ds.b 1 ; $97
sfx_BossProjectile ds.b 1 ; $98
sfx_UnknownPlink ds.b 1 ; $99
sfx_SpringLatch ds.b 1 ; $9A
sfx_ThumpBoss ds.b 1 ; $9B
sfx_SuperEmerald ds.b 1 ; $9C
sfx_Targeting ds.b 1 ; $9D
sfx_Clank ds.b 1 ; $9E
sfx_SuperTransform ds.b 1 ; $9F
sfx_MissleShoot ds.b 1 ; $A0
sfx_UnknownOminous ds.b 1 ; $A1
sfx_FloorLauncher ds.b 1 ; $A2
sfx_GravityLift ds.b 1 ; $A3
sfx_MetalTransform ds.b 1 ; $A4
sfx_UnknownRise ds.b 1 ; $A5
sfx_LaunchGrab ds.b 1 ; $A6
sfx_LaunchReady ds.b 1 ; $A7
sfx_EnergyZap ds.b 1 ; $A8
sfx_AirDing ds.b 1 ; $A9
sfx_Bumper ds.b 1 ; $AA
sfx_Spindash ds.b 1 ; $AB
sfx_Continue ds.b 1 ; $AC
sfx_LaunchGo ds.b 1 ; $AD
sfx_Flipper ds.b 1 ; $AE
sfx_EnterSS ds.b 1 ; $AF
sfx_Register ds.b 1 ; $B0
sfx_Spring ds.b 1 ; $B1
sfx_Error ds.b 1 ; $B2
sfx_BigRing ds.b 1 ; $B3
sfx_Explode ds.b 1 ; $B4
sfx_Diamonds ds.b 1 ; $B5
sfx_Dash ds.b 1 ; $B6
sfx_SlotMachine ds.b 1 ; $B7
sfx_Signpost ds.b 1 ; $B8
sfx_RingLoss ds.b 1 ; $B9
sfx_Flying ds.b 1 ; $BA
sfx_FlyTired ds.b 1 ; $BB
sfx__FirstContinuous = * ; ID of the first continuous sound effect
sfx_SlideSkidLoud ds.b 1 ; $BC
sfx_LargeShip ds.b 1 ; $BD
sfx_EggmanSiren ds.b 1 ; $BE
sfx_BossRotate ds.b 1 ; $BF
sfx_FanBig ds.b 1 ; $C0
sfx_FanSmall ds.b 1 ; $C1
sfx_FlamethrowerLoud ds.b 1 ; $C2
sfx_GravityTunnel ds.b 1 ; $C3
sfx_BossPanic ds.b 1 ; $C4
sfx_UnknownSpin ds.b 1 ; $C5
sfx_WaveHover ds.b 1 ; $C6
sfx_CannonTurn ds.b 1 ; $C7
sfx_SlideSkidQuiet ds.b 1 ; $C8
sfx_SpikeBalls ds.b 1 ; $C9
sfx_LightTunnel ds.b 1 ; $CA
sfx_Rumble ds.b 1 ; $CB
sfx_BigRumble ds.b 1 ; $CC
sfx_DeathEggRiseLoud ds.b 1 ; $CD
sfx_WindQuiet ds.b 1 ; $CE
sfx_WindLoud ds.b 1 ; $CF
sfx_Rising ds.b 1 ; $D0
sfx_UnknownFlutter2 ds.b 1 ; $D1
sfx_GumballTab ds.b 1 ; $D2
sfx_DeathEggRiseQuiet ds.b 1 ; $D3
sfx_TurbineHum ds.b 1 ; $D4
sfx_LavaFall ds.b 1 ; $D5
sfx_UnknownZap ds.b 1 ; $D6
sfx_ConveyorPlatform ds.b 1 ; $D7
sfx_UnknownSaw ds.b 1 ; $D8
sfx_MagneticSpike ds.b 1 ; $D9
sfx_LeafBlower ds.b 1 ; $DA
sfx_WaterSkid ds.b 1 ; $DB
mus_CreditsK ds.b 1 ; $DC - Can also be treated as SFX?
ds.b 3 ; unused SFX slots, the driver will happily play them though
sfx__End = * ; next ID after the last sound effect
dephase
!org 0 ; make sure we reset the ROM position to 0
; Background music
bgm__First: equ Mus__First
bgm_GHZ: equ mus_AIZ1
bgm_LZ: equ mus_AIZ2
bgm_MZ: equ mus_HCZ1
bgm_SLZ: equ mus_HCZ2
bgm_SYZ: equ mus_MGZ1
bgm_SBZ: equ mus_MGZ2
bgm_FZ: equ mus_CNZ1
bgm_Boss: equ mus_EndBoss
bgm_SS: equ mus_SpecialStage
bgm_Title: equ mus_TitleScreen
bgm_ExtraLife: equ mus_ExtraLife
bgm_Invincible: equ mus_Invincibility
bgm_GameOver: equ mus_GameOver
bgm_Continue: equ mus_Continue
bgm_GotThrough: equ mus_GotThroughAct
bgm_Emerald: equ mus_Emerald
bgm_Drowning: equ mus_Drowning
bgm_Ending: equ mus_Ending
bgm_Credits: equ mus_CreditsK
bgm__Last: equ Mus__End
; Sound effects
sfx__First: equ sfx_First
sfx_Ring: equ sfx_RingRight
sfx_HitSpikes: equ sfx_SpikeHit
sfx_BreakItem: equ sfx_Break
sfx_Basaran: equ sfx_InstaAttack
sfx_Door: equ sfx_Thump
sfx_SpikesMove: equ sfx_FanLatch
sfx_WallSmash: equ sfx_Collapse
sfx_Switch: equ sfx_Button
sfx_ChainStomp: equ sfx_Crash
sfx_Lamppost: equ sfx_Starpost
sfx_Push: equ sfx_PushBlock
sfx_SSGoal: equ sfx_Goal
sfx_SSItem: equ sfx_ActionBlock
sfx_HitBoss: equ sfx_BossHit
sfx_Fireball: equ sfx_LavaBall
sfx_Electric: equ sfx_Lightning
sfx_ChainRise: equ sfx_ChainTick
sfx_Warning: equ sfx_AirDing
sfx_Cash: equ sfx_Register
sfx_GiantRing: equ sfx_BigRing
sfx_Bomb: equ sfx_Explode
sfx_A5: equ sfx_Explode
sfx_SSGlass: equ sfx_Diamonds
sfx_Teleport: equ sfx_Dash
sfx_Flamethrower:equ sfx_FlamethrowerLoud
sfx_Burning: equ sfx_FlamethrowerLoud
sfx_Bonus: equ sfx_UnknownPowerUp
sfx_Rumbling: equ sfx_Rumble
sfx_Saw: equ sfx_UnknownSaw
sfx__Last: equ sfx__End
; Special sound effects
spec__First: equ sfx_WaterSkid
sfx_Waterfall: equ sfx_WaterSkid
spec__Last: equ sfx_WaterSkid
flg__First: equ mus__FirstCmd
bgm_Fade: equ mus_FadeOut
sfx_Sega: equ mus_SEGA
bgm_Speedup: equ 8
bgm_Slowdown: equ 0
bgm_Stop: equ mus_Stop
flg__Last: equ Mus__EndCmd
Macros
Now for the macros. This will be very similar to what the hivebrain people will be doing.
First off lets replace the org and org0 macros. Open MacroSetup.asm and find:
; make org safer (impossible to overwrite previously assembled bytes)
; and also make it work in Z80 code without creating a new segment
org macro address
if notZ80(MOMCPU)
if address < *
error "too much stuff before org $\{address} ($\{(*-address)} bytes)"
else
!org address
endif
else
if address < $
error "too much stuff before org 0\{address}h (0\{($-address)}h bytes)"
else
while address > $
db 0
endm
endif
endif
endm
; define an alternate org that fills the extra space with 0s instead of FFs
org0 macro address
.diff := address - *
if .diff < 0
error "too much stuff before org0 $\{address} ($\{(-diff)} bytes)"
else
while .diff > 1024
; AS can only generate 1 kb of code on a single line
dc.b [1024]0
.diff := .diff - 1024
endm
dc.b [.diff]0
endif
endm
We cannot use this version, so we will replace it with the s3k version of this, which looks like:
; make org safer (impossible to overwrite previously assembled bytes)
org macro address
if notZ80(MOMCPU)
.diff := address - *
if .diff < 0
error "too much stuff before org $\{address} ($\{(-.diff)} bytes)"
else
while .diff > 1024
; AS can only generate 1 kb of code on a single line
dc.b [1024]$FF
.diff := .diff - 1024
endm
dc.b [.diff]$FF
endif
else
if address < $
error "too much stuff before org 0\{address}h (0\{($-address)}h bytes)"
else
while address > $
db 0
endm
endif
endif
endm
; define an alternate org that fills the extra space with 0s instead of FFs
org0 macro address
.diff := address - *
if .diff < 0
error "too much stuff before org0 $\{address} ($\{(-.diff)} bytes)"
else
while .diff > 1024
; AS can only generate 1 kb of code on a single line
dc.b [1024]0
.diff := .diff - 1024
endm
dc.b [.diff]0
endif
endm
Next it is the cnop and cnop0 macros, once again find:
; define the cnop pseudo-instruction
cnop macro offset,alignment
if notZ80(MOMCPU)
org (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
else
org ($-1+(alignment)-(($-1+(-(offset)))#(alignment)))
endif
endm
; define an alternate cnop that fills the extra space with 0s instead of FFs
cnop0 macro offset,alignment
org0 (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
endm
and replace it with:
; define the cnop pseudo-instruction
cnop macro offset,alignment
if notZ80(MOMCPU)
org (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
else
org ($-1+(alignment)-(($-1+(-(offset)))#(alignment)))
endif
endm
; define an alternate cnop that fills the extra space with 0s instead of FFs
cnop0 macro offset,alignment
org0 (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
endm
for the same reason as above. Now we will repeat the above for the same reason for align, align0, and even. Find:
; redefine align in terms of cnop, because the built-in align can be stupid sometimes
align macro alignment
cnop 0,alignment
endm
; define an alternate align that fills the extra space with 0s instead of FFs
align0 macro alignment
cnop0 0,alignment
endm
; define the even pseudo-instruction
even macro
if notZ80(MOMCPU)
if (*)&1
dc.b 0 ;ds.b 1
endif
else
if ($)&1
db 0
endif
endif
endm
and replace the code with this:
; redefine align in terms of cnop, because the built-in align can be stupid sometimes
align macro alignment
cnop 0,alignment
endm
; define an alternate align that fills the extra space with 0s instead of FFs
align0 macro alignment
cnop0 0,alignment
endm
; define the even pseudo-instruction
even macro
align0 2
endm
Now we update the trace macro by locating:
; define a trace macro
; lets you easily check what address a location in this disassembly assembles to
trace macro optionalMessageWithoutQuotes
if MOMPASS=1
if ("ALLARGS"<>"")
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF} msg=ALLARGS"
else
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF}"
endif
tracenum := (tracenum+1)
endif
endm
tracenum := 0
and replacing it with:
; define a trace macro
; lets you easily check what address a location in this disassembly assembles to
trace macro optionalMessageWithoutQuotes
if MOMPASS=1
if ("ALLARGS"<>"")
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF} msg=ALLARGS"
else
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF}"
endif
tracenum := (tracenum+1)
endif
endm
tracenum := 0
We replaced these macros because the old ones will bork s3p2bin later if we don't.
All that is left now, is adding this:
; function to make a little-endian 16-bit pointer for the Z80 sound driver
z80_ptr function x,(x)<<8&$FF00|(x)>>8&$7F|$80
; macro to declare a little-endian 16-bit pointer for the Z80 sound driver
rom_ptr_z80 macro addr
dc.w z80_ptr(addr)
endm
after:
_tst macro
!tst.ATTRIBUTE ALLARGS
endm
endif
Final Setup before we begin
We almost forgot to add something to the beginning of Sonic.asm, so lets add:
Size_of_Snd_driver_guess = $E0C
Size_of_Snd_driver2_guess = $70F
; Approximate size of compressed sound driver. Change when appropriate
Size_of_Snd_Bank1 = $3EFC
; This particular bank has its contents aligned to the end
; ---------------------------------------------------------------------------
right after:
include "MacroSetup.asm"
include "Constants.asm"
include "Variables.asm"
include "Macros.asm"
Preparing to use Sonic 3/K/3K sound system
The Vertical Interrupt and Horizontal Interrupt need to be fixed, Since the Sonic 1/2 system uses them but the Sonic 3/K/3K system doesn't.
Prepare Vertical Interrupt Handler for new driver
Sonic 1 calls a early 68k smps driver in both the vertical blank routine and the horizontal blank routine, we don't need this for the new driver, so we will have to make a few deletions here and there. Sonic 2 also does this with it's compressed z80 driver, so if you are actually placing this in sonic 2, you might want to make a note of that.<p> first we will fix the vertical interrupt, by deleting this line:
jsr (UpdateMusic).l
under:
VBla_Music:
Prepare Horzontal Interrupt Handler for new driver
Now we locate:
loc_119E:
clr.b ($FFFFF64F).w
movem.l d0-a6,-(sp)
bsr.w Demo_Time
jsr (UpdateMusic).l
movem.l (sp)+,d0-a6
rte
; End of function HBlank
See another familiar line? Yes, you've got it, do the same thing to it:
loc_119E:
clr.b ($FFFFF64F).w
movem.l d0-a6,-(sp)
bsr.w Demo_Time
movem.l (sp)+,d0-a6
rte
; End of function HBlank
That effectively disables the Sonic 1/2 style interrupt handler which we will not need and prevents a nasty error down the road.
Upgrading the Load Driver Routine
Okay, we disabled the driver's interrupt handler, wouldn't we want now to use the sonic3 driver instead? Here is where we start installing it. Locate:
; ---------------------------------------------------------------------------
; Subroutine to load the sound driver
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
SoundDriverLoad:
nop
stopZ80
resetZ80
lea (Kos_Z80).l,a0 ; load sound driver
lea (z80_ram).l,a1 ; target Z80 RAM
bsr.w KosDec ; decompress
resetZ80a
nop
nop
nop
nop
resetZ80
startZ80
rts
; End of function SoundDriverLoad
and replace it all with:
; ---------------------------------------------------------------------------
; Subroutine to load the sound driver
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
nop
stopZ80
resetZ80
; Load SMPS sound driver
lea (Z80_SoundDriver).l,a0
lea (z80_ram).l,a1
bsr.w KosDec
; Load sound driver data (PSG envelopes, music/sound pointers, FM voice bank)
lea (Z80_SoundDriverData).l,a0
lea (z80_ram+$1300).l,a1
bsr.w KosDec
; Load default variables
lea (Z80_DefaultVariables).l,a0
lea (z80_ram+zDataStart).l,a1
move.w #Z80_DefaultVariables_end-Z80_DefaultVariables-1,d0
- move.b (a0)+,(a1)+
dbf d0,-
resetZ80a
nop
nop
nop
nop
resetZ80
startZ80
rts
; End of function SndDrvInit
; ---------------------------------------------------------------------------
; Default Z80 variables. These are actually set to more meaningful values
; in other SMPS Z80 drivers.
; ---------------------------------------------------------------------------
Z80_DefaultVariables:
dc.b 0 ; Unused 1
dc.b 0 ; Unused 2
dc.b 0 ; zPalFlag
dc.b 0 ; Unused 3
dc.b 0 ; zPalDblUpdCounter
dc.b 0 ; zSoundQueue0
dc.b 0 ; zSoundQueue1
dc.b 0 ; zSoundQueue2
dc.b 0 ; zTempoSpeedup
dc.b 0 ; zNextSound
dc.b 0 ; zMusicNumber
dc.b 0 ; zSFXNumber0
dc.b 0 ; zSFXNumber1
dc.b 0 ; zFadeOutTimeout
dc.b 0 ; zFadeDelay
dc.b 0 ; zFadeDelayTimeout
Z80_DefaultVariables_end:
; End of function SoundDriverLoad
Upgrading the Playback Routines
Now we have the code to load the new driver, time to add the new playback routines.
Upgrade Music Routine
First the PlaySound routine, open "sub PlaySound.asm" inside the _incObj folder and locate:
; ---------------------------------------------------------------------------
; Subroutine to play a music track
; input:
; d0 = track to play
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound:
move.b d0,(v_snddriver_ram+v_soundqueue0).w
rts
; End of function PlaySound
and replace it completely with:
; ---------------------------------------------------------------------------
; Subroutine to play a music track
; input:
; d0 = track to play
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound:
cmpi.w #$FB,d0
blt.s PlayNotSpecialFlag
bhi.s TestForNormalSpeed
move #8,d0
jmp SetTempo
TestForNormalSpeed:
cmpi.w #$FC,d0
bne.s PlayNotSpecialFlag
clr.w d0
jmp SetTempo
PlayNotSpecialFlag:
stopZ80
waitZ80
move.b d0,($A01C0A).l
startZ80
rts
; End of function PlaySound
; ---------------------------------------------------------------------------
; Exclusive sound/music subroutine
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound_Ex:
tst.b 4(A0)
bpl.s SkipPlaySound_Special
Do not be alarmed about the new sound routine that was added, it is equivalent to the PlaySound_Local routine (as a matter of fact, it is identical, why sonic1 does not already have it is beyond me, since it indeed does work in Sonic 1)
sound playback routines
now find:
; ---------------------------------------------------------------------------
; Subroutine to play a sound effect
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound_Special:
move.b d0,(v_snddriver_ram+v_soundqueue1).w
rts
; End of function PlaySound_Special
; ===========================================================================
; ---------------------------------------------------------------------------
; Unused sound/music subroutine
; ---------------------------------------------------------------------------
PlaySound_Unused:
move.b d0,(v_snddriver_ram+v_soundqueue2).w
rts
and replace it entirely with:
; ---------------------------------------------------------------------------
; Subroutine to play a special sound/music (FB-FF)
; ---------------------------------------------------------------------------
PlaySound_Special:
stopZ80
waitZ80
cmp.b ($A01C0B).l,d0
beq.s PlaySound_Special1
tst.b ($A01C0B).l
bne.s PlaySound_Special0
move.b d0,($A01C0B).l
startZ80
rts
PlaySound_Special0:
move.b d0,($A01C0C).l
PlaySound_Special1:
startZ80
SkipPlaySound_Special:
rts
; End of function PlaySound_Special
; ---------------------------------------------------------------------------
; Subroutine to change the music tempo
; ---------------------------------------------------------------------------
SetTempo:
stopZ80
waitZ80
move.b D0,($A01C08).l
startZ80
rts
Upgrading Pause / Resume routines
Now the playback routines are fixed and we have a routine to set the tempo the way the sneakers do, which we will elaberate later on, but we still have to update the pause / resume routines to the sonic 3 equivilents.
Find inside of PauseGame.asm (it is in the _inc folder):
; ---------------------------------------------------------------------------
; Subroutine to pause the game
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PauseGame:
nop
tst.b (v_lives).w ; do you have any lives left?
beq.s Unpause ; if not, branch
tst.w (f_pause).w ; is game already paused?
bne.s Pause_StopGame ; if yes, branch
btst #bitStart,(v_jpadpress1).w ; is Start button pressed?
beq.s Pause_DoNothing ; if not, branch
Pause_StopGame:
move.w #1,(f_pause).w ; freeze time
move.b #1,(v_snddriver_ram+f_pausemusic).w ; pause music
Pause_Loop:
move.b #$10,(v_vbla_routine).w
bsr.w WaitForVBla
tst.b (f_slomocheat).w ; is slow-motion cheat on?
beq.s Pause_ChkStart ; if not, branch
btst #bitA,(v_jpadpress1).w ; is button A pressed?
beq.s Pause_ChkBC ; if not, branch
move.b #id_Title,(v_gamemode).w ; set game mode to 4 (title screen)
nop
bra.s Pause_EndMusic
; ===========================================================================
Pause_ChkBC:
btst #bitB,(v_jpadhold1).w ; is button B pressed?
bne.s Pause_SlowMo ; if yes, branch
btst #bitC,(v_jpadpress1).w ; is button C pressed?
bne.s Pause_SlowMo ; if yes, branch
Pause_ChkStart:
btst #bitStart,(v_jpadpress1).w ; is Start button pressed?
beq.s Pause_Loop ; if not, branch
Pause_EndMusic:
move.b #$80,(v_snddriver_ram+f_pausemusic).w ; unpause the music
Unpause:
move.w #0,(f_pause).w ; unpause the game
Pause_DoNothing:
rts
; ===========================================================================
Pause_SlowMo:
move.w #1,(f_pause).w
move.b #$80,(v_snddriver_ram+f_pausemusic).w ; Unpause the music
rts
; End of function PauseGame
and replace it completely with:
; ---------------------------------------------------------------------------
; Subroutine to pause the game
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PauseGame: ; XREF: Level_MainLoop; et al
nop
tst.b (v_lives).w ; do you have any lives left
beq Unpause ; if not, branch
tst.w (f_pause).w ;is the game already paused?
bne.s PauseGame_AlreadyPaused ;if yes, branch
move.b (v_jpadpress1).w,d0 ;did you press start
andi.b #$80,d0
beq Pause_DoNothing ;if not, branch
PauseGame_AlreadyPaused:
move.w #1,(f_pause).w ;unpause the game
stopZ80
waitZ80
move.b #1,($A01C10).l ;unpause the music ;)
startZ80
PauseGameLoop:
move.b #$10,(v_vbla_routine).w ;re-pause the game (used in slow-mo and frame advance)
jsr WaitForVBla ;wait...
tst.b (f_slomocheat).w ; Edit: the value is f_slomocheat in Sonic 1 (slow-mo/frame advance mode)
beq.s Pause_ChkStart
btst #6,(v_jpadpress1).w ;is player 1 pressing either A?
beq.s Pause_ChkBC ;if not, branch
move.b #$4,(v_gamemode).w ; Go To Title Screen
nop
bra.s PauseGame1 ;time to stop the z80 again
Pause_ChkBC:
btst #4,(v_jpadhold1).w ;did you press a?
bne.s Pause_SlowMo ;if so, branch
btst #5,(v_jpadpress1).w ;did you press b?
bne.s Pause_SlowMo ;if so, branch
Pause_ChkStart:
btst #4,(v_jpadpress1).w ;did you press a?
beq.s PauseGame0 ;if yes, then paise the freaken game
move.b #$0,(v_gamemode).w ;prepare to go to sega screen (level select is not 0xC0 in sonic1, but part of title screen code)
bra.s PauseGame1 ;go to title screen
PauseGame0:
move.b (v_jpadpress1).w,d0 ;on controller 1?
andi.b #$80,d0 ;if not, no change
beq.s PauseGameLoop ;in other words, don't pause
PauseGame1:
stopZ80 ;stop the z80
waitZ80
move.b #$80,($A01C10).l ;pause the music
move.w #0,(z80_bus_request).l ;start the z80
Unpause:
move.w #0,(f_pause).w ;unpause the game
Pause_DoNothing:
rts
Pause_SlowMo:
move.w #1,(f_pause).w ;unpause the music for a frame
stopZ80 ;stop the z80
waitZ80
move.b #$80,($A01C10).l ;pause the music again
startZ80
rts
; End of function PauseGame
Replacing 68k driver with new Z80 driver
Then delete the sound and AS folders as well as the s1.sounddriver.asm and build.py files. Now unpack this into your disassembly folder:
Download The Sonic 3 Driver data files
File: git_files.zip (3.44 MB) (info)
|
You will need to replace the files and folders above so that our driver will work.
Fixing the sneaker monitor
by now everything except the sneaker and GHZ waterfalls should be working correctly, though some saw blades in SBZ will sometimes not sound correctly.
We can at least fix the sneaker and that is what we will do here. Before we begin, sonic 1/2 and sonic 3, s&k, and sonic 3k do something different for sneakers.
First we will open "2E Monitor Content Power-Up.asm", you should by now know where that file is. Find:
Pow_ChkShoes:
cmpi.b #3,d0 ; does monitor contain speed shoes?
bne.s Pow_ChkShield
move.b #1,(v_shoes).w ; speed up the BG music
move.w #$4B0,(v_player+$34).w ; time limit for the power-up
move.w #$C00,(v_sonspeedmax).w ; change Sonic's top speed
move.w #$18,(v_sonspeedacc).w ; change Sonic's acceleration
move.w #$80,(v_sonspeeddec).w ; change Sonic's deceleration
music bgm_Speedup,1,0,0 ; Speed up the music
and change it to:
Pow_ChkShoes:
cmpi.b #3,d0 ; does monitor contain speed shoes?
bne.s Pow_ChkShield
move.b #1,(v_shoes).w ; speed up the BG music
move.w #$4B0,(v_player+$34).w ; time limit for the power-up
move.w #$C00,(v_sonspeedmax).w ; change Sonic's top speed
move.w #$18,(v_sonspeedacc).w ; change Sonic's acceleration
move.w #$80,(v_sonspeeddec).w ; change Sonic's deceleration
move.w #8,d0
jmp (SetTempo).l ; Speed up the music
next, we open "Sonic Display.asm". By now you should know where to find that file. Locate:
.chkshoes:
tst.b (v_shoes).w ; does Sonic have speed shoes?
beq.s .exit ; if not, branch
tst.w shoetime(a0) ; check time remaining
beq.s .exit
subq.w #1,shoetime(a0) ; subtract 1 from time
bne.s .exit
move.w #$600,(v_sonspeedmax).w ; restore Sonic's speed
move.w #$C,(v_sonspeedacc).w ; restore Sonic's acceleration
move.w #$80,(v_sonspeeddec).w ; restore Sonic's deceleration
move.b #0,(v_shoes).w ; cancel speed shoes
music bgm_Slowdown,1,0,0 ; run music at normal speed
and change it to:
.chkshoes:
tst.b (v_shoes).w ; does Sonic have speed shoes?
beq.s .exit ; if not, branch
tst.w shoetime(a0) ; check time remaining
beq.s .exit
subq.w #1,shoetime(a0) ; subtract 1 from time
bne.s .exit
move.w #$600,(v_sonspeedmax).w ; restore Sonic's speed
move.w #$C,(v_sonspeedacc).w ; restore Sonic's acceleration
move.w #$80,(v_sonspeeddec).w ; restore Sonic's deceleration
move.b #0,(v_shoes).w ; cancel speed shoes
move.w #0,d0
jmp (SetTempo).l ; run music at normal speed
Now the sneakers should be working as expected.
Fixing the sound test (Sound Select) on the level select
this will be a lot like fixing the sonic 2 level select, let's begin with this routine:
LevelSelect:
move.b #4,(v_vbla_routine).w
bsr.w WaitForVBla
bsr.w LevSelControls
bsr.w RunPLC
tst.l (v_plc_buffer).w
bne.s LevelSelect
andi.b #btnABC+btnStart,(v_jpadpress1).w ; is A, B, C, or Start pressed?
beq.s LevelSelect ; if not, branch
move.w (v_levselitem).w,d0
cmpi.w #$14,d0 ; have you selected item $14 (sound test)?
bne.s LevSel_Level_SS ; if not, go to Level/SS subroutine
move.w (v_levselsound).w,d0
addi.w #$80,d0
tst.b (f_creditscheat).w ; is Japanese Credits cheat on?
beq.s LevSel_NoCheat ; if not, branch
cmpi.w #$9F,d0 ; is sound $9F being played?
beq.s LevSel_Ending ; if yes, branch
cmpi.w #$9E,d0 ; is sound $9E being played?
beq.s LevSel_Credits ; if yes, branch
LevSel_NoCheat:
; This is a workaround for a bug, see Sound_ChkValue for more.
; Once you've fixed the bugs there, comment these four instructions out
cmpi.w #bgm__Last+1,d0 ; is sound $80-$93 being played?
blo.s LevSel_PlaySnd ; if yes, branch
cmpi.w #sfx__First,d0 ; is sound $94-$9F being played?
blo.s LevelSelect ; if yes, branch
LevSel_PlaySnd:
bsr.w PlaySound_Special
bra.s LevelSelect
the andi.w line can be dropped, we will not be using the japanese cheats since they interfere with playback and we do not have to worry about whether a real song is playing here, so we will crop it down to this:
LevelSelect:
move.b #4,(v_vbla_routine).w
bsr.w WaitForVBla
bsr.w LevSelControls
bsr.w RunPLC
tst.l (v_plc_buffer).w
bne.s LevelSelect
andi.b #btnABC+btnStart,(v_jpadpress1).w ; is A, B, C, or Start pressed?
beq.s LevelSelect ; if not, branch
move.w (v_levselitem).w,d0
cmpi.w #$14,d0 ; have you selected item $14 (sound test)?
bne.s LevSel_Level_SS ; if not, go to Level/SS subroutine
move.w (v_levselsound).w,d0
LevSel_NoCheat:
; This is a workaround for a bug, see Sound_ChkValue for more.
; Once you've fixed the bugs there, comment these four instructions out
cmpi.w #bgm__Last+1,d0 ; is sound $80-$93 being played?
blo.s LevSel_PlaySnd ; if yes, branch
cmpi.w #sfx__First,d0 ; is sound $94-$9F being played?
blo.s LevelSelect ; if yes, branch
LevSel_PlaySnd:
bsr.w PlaySound_Special
bra.s LevelSelect
now remove all this junk:
; ===========================================================================
LevSel_Ending:
move.b #id_Ending,(v_gamemode).w ; set screen mode to $18 (Ending)
move.w #(id_EndZ<<8),(v_zone).w ; set level to 0600 (Ending)
rts
; ===========================================================================
LevSel_Credits:
move.b #id_Credits,(v_gamemode).w ; set screen mode to $1C (Credits)
sfx bgm_Credits,0,1,1 ; play credits music
move.w #0,(v_creditsnum).w
rts
lets add a nice little touch to this with music on the level select. Like in sonic 2 and 3. We start here:
Tit_ChkLevSel:
tst.b (f_levselcheat).w ; check if level select code is on
beq.w PlayLevel ; if not, play level
btst #bitA,(v_jpadhold1).w ; check if A is pressed
beq.w PlayLevel ; if not, play level
moveq #palid_LevelSel,d0
bsr.w PalLoad2 ; load level select palette
lea (v_hscrolltablebuffer).w,a1
moveq #0,d0
move.w #$DF,d1
we add these 2 lines
move.b #mus_DataSelect,d0
jsr PlaySound
to that section of code, making it into this:
Title_ChkLevSel:
Tit_ChkLevSel:
tst.b (f_levselcheat).w ; check if level select code is on
beq.w PlayLevel ; if not, play level
btst #bitA,(v_jpadhold1).w ; check if A is pressed
beq.w PlayLevel ; if not, play level
move.b #mus_DataSelect,d0
jsr PlaySound
moveq #palid_LevelSel,d0
bsr.w PalLoad2 ; load level select palette
lea (v_hscrolltablebuffer).w,a1
moveq #0,d0
move.w #$DF,d1
now the next thing we need to edit is THIS section of code:
LevSel_SndTest:
cmpi.w #$14,(v_levselitem).w ; is item $14 selected?
bne.s LevSel_NoMove ; if not, branch
move.b (v_jpadpress1).w,d1
andi.b #btnR+btnL,d1 ; is left/right pressed?
beq.s LevSel_NoMove ; if not, branch
move.w (v_levselsound).w,d0
btst #bitL,d1 ; is left pressed?
beq.s LevSel_Right ; if not, branch
subq.w #1,d0 ; subtract 1 from sound test
bhs.s LevSel_Right
moveq #$4F,d0 ; if sound test moves below 0, set to $4F
LevSel_Right:
btst #bitR,d1 ; is right pressed?
beq.s LevSel_Refresh2 ; if not, branch
addq.w #1,d0 ; add 1 to sound test
cmpi.w #$50,d0
blo.s LevSel_Refresh2
moveq #0,d0 ; if sound test moves above $4F, set to 0
LevSel_Refresh2:
move.w d0,(v_levselsound).w ; set sound test number
bsr.w LevSelTextLoad ; refresh text
LevSel_NoMove:
rts
first off, $4F is the wrong value, we do not start at $80 amymore, second, we do not want it to go to 0 after $4F, so the corrected code is like this:
LevSel_SndTest:
cmpi.w #$14,(v_levselitem).w ; is item $14 selected?
bne.s LevSel_NoMove ; if not, branch
move.b (v_jpadpress1).w,d1
andi.b #btnR+btnL,d1 ; is left/right pressed?
beq.s LevSel_NoMove ; if not, branch
move.w (v_levselsound).w,d0
btst #bitL,d1 ; is left pressed?
beq.s LevSel_Right ; if not, branch
subq.w #1,d0 ; subtract 1 from sound test
bhs.s LevSel_Right
moveq #$FF,d0 ; if sound test moves below 0, set to $4F
LevSel_Right:
btst #bitR,d1 ; is right pressed?
beq.s LevSel_Refresh2 ; if not, branch
addq.w #1,d0 ; add 1 to sound test
LevSel_Refresh2:
move.w d0,(v_levselsound).w ; set sound test number
bsr.w LevSelTextLoad ; refresh text
LevSel_NoMove:
rts
okay, now we need to stop it from adding $80 to the sound test value like s2 does, so we go to:
LevSel_DrawSnd:
locVRAM $EC30 ; sound test position on screen
move.w (v_levselsound).w,d0
addi.w #$80,d0
move.b d0,d2
lsr.b #4,d0
bsr.w LevSel_ChgSnd ; draw 1st digit
move.b d2,d0
bsr.w LevSel_ChgSnd ; draw 2nd digit
rts
and delete this line:
addi.w #$80,d0
Branch fixes
there are some 'bsr's that need fixing otherwise it won't build. Open "09 Sonic in Special Stage.asm". These lines here:
bsr.s sub_1BD30
in sub_1BCE8 need changing to:
jsr sub_1BD30
What comes next
More recently, User:Alriightyman has ported Flamewing's sonic & knuckles driver mod to sonic 2 in this post:
[1]
User:KZG4 is working on a part 2 where we upgrade the sonic 3 driver we installed to the User:Flamewing sound driver.
you can swap out the music for something else that more fits your hack as well as the sound effects.
You can add sound effects to existing objects like a really old version of this guide did.
notice to admins
We have moved to the S&K driver, I took down the Github tutorial here because it is outdated and no longer works. Will replace it when I am ready to do so. I hope you make good use of this driver and make many great hacks.
Credits
I give credit where it is due:
- User:Esrael for the original port of the Sonic 3 Driver to Sonic 2 Beta, which I fixed up.
- User:Tornado for the original guide on SSRG
- User:Selbi for posting info on most of, but not all the sound test values which I had to finish.
- User:KZG4 for sonic 3 beta music files used in the guide and an older attempt at the flamewing driver.
- User:DeltaWooloo and User:KZG4 for inspiring me to push this guide to the next level TM.