Actions

SCHG How-to

Difference between revisions of "Port Sonic 3's Sound Driver to Sonic 1"

From Sonic Retro

(Upgrading Pause / Resume routines)
 
(362 intermediate revisions by 13 users not shown)
Line 1: Line 1:
{{GuideBy|Kram1024}}
+
{{GuideBy|Kramlat}}<br>
 +
'''Updated by''' [[User: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.
+
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.
 +
 
 +
=Hivebrain Disassembly users, start here=
 +
If yours uses snasm68k or even asm68k, please move to as since snasm68k doesnt understand these macros and has been known to have compatibility issues with some versions of Windows and with Wine. Also, asm68k does not understand z80 asm thus you will not be able to use this guide.
  
 
==Overview==
 
==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.  We will be using the hivebrain version, if you wish to do it via svn version, I am sure you can come up with a way that works by seeing what changes I made in the hivebrain version.
+
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.
  
==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.
 
  
first we will fix the vertical interrupt, by changing:
+
==Macros==
<asm>
+
Before going on with this guide, add these macros to sonic1.macrosetup.asm:
loc_B5E: ; XREF: loc_B88
+
<syntaxhighlight lang="asm">
jsr (sub_71B4C).l
+
</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
  
to:
+
; ---------------------------------------------------------------------------
<asm>
+
; Sound commands list.
loc_B5E: ; XREF: loc_B88
 
nop
 
</asm>
 
  
Now we locate:
+
phase $E1
<asm>
+
mus__FirstCmd = * ; ID of the first sound command
loc_119E: ; XREF: PalToCRAM
+
mus_FadeOut ds.b 1 ; $E1 - fade out music
clr.b ($FFFFF64F).w
+
mus_Stop ds.b 1 ; $E2 - stop music and sound effects
movem.l d0-a6,-(sp)
+
mus_MutePSG ds.b 1 ; $E3 - mute all PSG channels
bsr.w Demo_Time
+
mus_StopSFX ds.b 1 ; $E4 - stop all sound effects
jsr (sub_71B4C).l
+
mus_FadeOut2 ds.b 1 ; $E5 - fade out music (duplicate)
movem.l (sp)+,d0-a6
+
Mus__EndCmd = * ; next ID after last sound command
rte
 
; End of function PalToCRAM
 
</asm>
 
See another familiar line?  Yes, you've got it, do the same thing to it:
 
<asm>
 
loc_119E: ; XREF: PalToCRAM
 
clr.b ($FFFFF64F).w
 
movem.l d0-a6,-(sp)
 
bsr.w Demo_Time
 
nop
 
movem.l (sp)+,d0-a6
 
rte
 
; End of function PalToCRAM
 
</asm>
 
That effectively disables the sonic1 style v_int and h_int driver reloading that we will not need and prevents a nasty error down the road.
 
  
==Upgrading the Load Driver Routine==
+
mus_FA = $FA ; $FA - ???
Okay, we disabled the sonic1 driver junk, wouldn't we want now to use the sonic3 driver instead?  Here is where we start installing it.  Locate:
+
mus_StopSEGA = $FE ; $FE - Stop SEGA sound
<asm>
+
mus_SEGA = $FF ; $FF - Play SEGA sound
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Subroutine to load the sound driver
+
; 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
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
; 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
  
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
+
; define an alternate cnop that fills the extra space with 0s instead of FFs
nop
+
cnop0 macro offset,alignment
move.w #$100,($A11100).l ; stop the Z80
+
org0 (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
move.w #$100,($A11200).l ; reset the Z80
+
    endm
lea (Kos_Z80).l,a0 ; load sound driver
+
 
lea ($A00000).l,a1
+
; redefine align in terms of cnop, because the built-in align can be stupid sometimes
bsr.w KosDec ; decompress
+
align macro alignment
move.w #0,($A11200).l
+
cnop 0,alignment
nop
+
    endm
nop
 
nop
 
nop
 
move.w #$100,($A11200).l ; reset the Z80
 
move.w #0,($A11100).l ; start the Z80
 
rts
 
; End of function SoundDriverLoad
 
</asm>
 
  
and replace it all with:
+
; define an alternate align that fills the extra space with 0s instead of FFs
<asm>
+
align0 macro alignment
; ---------------------------------------------------------------------------
+
cnop0 0,alignment
; Subroutine to load the sound driver
+
    endm
; ---------------------------------------------------------------------------
 
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
; define the even pseudo-instruction
 +
even macro
 +
align0 2
 +
    endm
  
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
+
; define a trace macro
nop
+
; lets you easily check what address a location in this disassembly assembles to
move.w #$100,($A11100).l ; Z80 bus request - Start
+
trace macro optionalMessageWithoutQuotes
move.w #$100,($A11200).l ; Z80 stop reset
+
if MOMPASS=1
lea (DriverData).l,a0
+
if ("ALLARGS"<>"")
lea ($A00000).l,a1
+
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF} msg=ALLARGS"
move.w #DriverDataEnd-DriverData,d0
+
else
 +
message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{(*)&$FFFFFFFF}"
 +
endif
 +
tracenum := (tracenum+1)
 +
endif
 +
  endm
 +
tracenum := 0
  
DriverLoadLoop:
+
; function to make a little-endian 16-bit pointer for the Z80 sound driver
move.b (a0)+,(a1)+
+
z80_ptr function x,(x)<<8&$FF00|(x)>>8&$7F|$80
dbf d0,DriverLoadLoop
 
lea (DriverPointers).l,a0
 
lea ($A01300).l,a1
 
move.w #DriverPointersEnd-DriverPointers,d0
 
  
DriverPointersLoadLoop:
+
; macro to declare a little-endian 16-bit pointer for the Z80 sound driver
move.b (a0)+,(a1)+
+
rom_ptr_z80 macro addr
dbf d0, DriverPointersLoadLoop
+
dc.w z80_ptr(addr)
lea (UniversalVoiceBank).l,a0
+
    endm
lea ($A017D8).l,a1
+
</syntaxhighlight>
move.w #UniversalVoiceBankEnd-UniversalVoiceBank,d0
+
below:
 +
<syntaxhighlight lang="asm">
 +
_tst macro
 +
insn1op tst.ATTRIBUTE, ALLARGS
 +
endm
 +
</syntaxhighlight>
 +
this will all be needed for later, really.
  
UniversalVoiceBankLoadLoop:
+
now remove these lines:
move.b (a0)+,(a1)+
+
<syntaxhighlight lang="asm">
dbf d0,UniversalVoiceBankLoadLoop
+
; 128 = 80h = z80, 32988 = 80DCh = z80unDoC
lea (DriverResetData).l,a0
+
notZ80 function cpu,(cpu<>128)&&(cpu<>32988)
lea ($A01C00).l,a1
 
move.w #DriverResetDataEnd-DriverResetData,d0
 
  
DriverResetDataLoadLoop:
+
; make org safer (impossible to overwrite previously assembled bytes) and count padding
move.b (a0)+,(a1)+
+
; and also make it work in Z80 code without creating a new segment
dbf d0,DriverResetDataLoadLoop
+
org macro address
btst #6,($FFFFFFF8).w
+
if notZ80(MOMCPU)
beq.s DriverAlreadyInitialized
+
if address < *
move.b #1,($A01C02).l
+
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
  
DriverAlreadyInitialized:
+
; define the cnop pseudo-instruction
move.w #0,($A11200).l
+
cnop macro offset,alignment
nop
+
if notZ80(MOMCPU)
nop
+
org (*-1+(alignment)-((*-1+(-(offset)))#(alignment)))
nop
+
else
nop
+
org ($-1+(alignment)-(($-1+(-(offset)))#(alignment)))
move.w #$100,($A11200).l ; Z80 start reset
+
endif
move.w #0,($A11100).l ; Z80 bus request - Stop
+
    endm
rts
 
; End of function SoundDriverLoad
 
  
DriverResetData:
+
; redefine align in terms of cnop, for the padding counter
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+
align macro alignment
DriverResetDataEnd:
+
cnop 0,alignment
</asm>
+
    endm
  
==Upgrading the Playback Routines==
+
; define the even pseudo-instruction
Now we have the code to load the new driver, time to add the new playback routines.
+
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
  
First the PlaySound routine, locate:
+
; make ds work in Z80 code without creating a new segment
<asm>
+
ds macro
; ---------------------------------------------------------------------------
+
if notZ80(MOMCPU)
; Subroutine to play a sound or music track
+
!ds.ATTRIBUTE ALLARGS
; ---------------------------------------------------------------------------
+
else
 +
rept ALLARGS
 +
db 0
 +
endm
 +
endif
 +
  endm
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
  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
 +
</syntaxhighlight>
 +
from above:
 +
<syntaxhighlight lang="asm">
 +
    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)
 +
</syntaxhighlight>
  
 +
==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:
 +
<syntaxhighlight lang="asm">
  
PlaySound:
+
Size_of_Snd_driver_guess = $E0C
move.b d0,($FFFFF00A).w
+
Size_of_Snd_driver2_guess = $70F
rts
+
; Approximate size of compressed sound driver. Change when appropriate
; End of function PlaySound
 
</asm>
 
  
and replace it completely with:
+
Size_of_Snd_Bank1 = $3EFC
<asm>
+
; This particular bank has its contents aligned to the end
; ---------------------------------------------------------------------------
 
; Subroutine to play a sound or music track
 
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
; 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
  
PlaySound:
+
; little endian pointers for music and SFX
cmpi.w #$FB,d0
+
z68kPtr: macro addr
blt.s PlayNotSpecialFlag
+
    dc.w ((((addr&$FFFF)|$8000)>>$08)|(((addr&$FFFF)|$8000)<<$08))&$FFFF
bhi.s TestForNormalSpeed
+
    endm
move #8,d0
 
jmp SetTempo
 
  
TestForNormalSpeed:
+
</syntaxhighlight>
cmpi.w #$FC,d0
+
below:
bne.s PlayNotSpecialFlag
+
<syntaxhighlight lang="asm">
clr.w d0
+
include "sonic1.macrosetup.asm"
jmp SetTempo
+
</syntaxhighlight>
  
PlayNotSpecialFlag:
+
==Preparing to use Sonic 3/K/3K sound system==
move.w #$100,($A11100).l
+
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.
  
PlaySoundZ80NotStopped:
+
===Prepare Vertical Interrupt Handler for new driver===
btst #0,($A11100).l
+
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.
bne.s PlaySoundZ80NotStopped ; loop until it says it's stopped
+
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>
move.b d0,($A01C0A).l
+
first we will fix the vertical interrupt, by deleting this line:
move.w #0,($A11100).l
+
<syntaxhighlight lang="asm">
rts
+
jsr (sub_71B4C).l
; End of function PlaySound
+
</syntaxhighlight>
  
; ---------------------------------------------------------------------------
+
under:
; Exclusive sound/music subroutine
 
; ---------------------------------------------------------------------------
 
  
 +
<syntaxhighlight lang="asm">
 +
loc_B5E: ; XREF: loc_B88
 +
</syntaxhighlight>
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
===Prepare Horzontal Interrupt Handler for new driver===
 
+
Now we locate:
 
+
<syntaxhighlight lang="asm">
PlaySound_Ex:
+
loc_119E: ; XREF: PalToCRAM
tst.b 4(A0)
+
clr.b ($FFFFF64F).w
bpl.s SkipPlaySound_Special
+
movem.l d0-a6,-(sp)
</asm>
+
bsr.w Demo_Time
 +
jsr (sub_71B4C).l
 +
movem.l (sp)+,d0-a6
 +
rte
 +
; End of function PalToCRAM
 +
</syntaxhighlight>
 +
See another familiar line?  Yes, you've got it, do the same thing to it:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
That effectively disables the Sonic 1/2 style interrupt handler which we will not need and prevents a nasty error down the road.
  
Now we will replace that unused routine with something more appropriate as well as upgrade the PlaySound_Special routine, find:
+
==Upgrading the Load Driver Routine==
<asm>
+
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:
 +
<syntaxhighlight lang="asm">
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Subroutine to play a special sound/music (E0-E4)
+
; Subroutine to load the sound driver
;
 
; E0 - Fade out
 
; E1 - Sega
 
; E2 - Speed up
 
; E3 - Normal speed
 
; E4 - Stop
 
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
  
Line 212: Line 572:
  
  
PlaySound_Special:
+
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
move.b d0,($FFFFF00B).w
+
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
 
rts
; End of function PlaySound_Special
+
; End of function SoundDriverLoad
 +
</syntaxhighlight>
  
; ===========================================================================
+
and replace it all with:
 +
<syntaxhighlight lang="asm">
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Unused sound/music subroutine
+
; Subroutine to load the sound driver
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
  
PlaySound_Unk:
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
move.b d0,($FFFFF00C).w
+
 
rts
+
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
</asm>
+
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
  
and replace it entirely with:
 
<asm>
 
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Unused sound/music subroutine
+
; 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
 +
</syntaxhighlight>
  
PlaySound_Unk:
+
==Upgrading the Playback Routines==
nop
+
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:
 +
<syntaxhighlight lang="asm">
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Subroutine to play a special sound/music (FB-FF)
+
; Subroutine to play a sound or music track
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
  
PlaySound_Special:
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
move.w #$100,($A11100).l
+
 
  
PlaySound_SpecialZ80NotStopped:
+
PlaySound:
btst #0,($A11100).l
+
move.b d0,($FFFFF00A).w
bne.s PlaySound_SpecialZ80NotStopped
+
rts
cmp.b ($A01C0B).l,d0
+
; End of function PlaySound
beq.s PlaySound_Special1
+
</syntaxhighlight>
tst.b ($A01C0B).l
+
 
bne.s PlaySound_Special0
+
and replace it completely with:
move.b d0,($A01C0B).l
+
<syntaxhighlight lang="asm">
move.w #0,($A11100).l
+
; ---------------------------------------------------------------------------
rts
+
; Subroutine to play a sound or music track
 +
; ---------------------------------------------------------------------------
  
PlaySound_Special0:
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
move.b d0,($A01C0C).l
 
  
PlaySound_Special1:
 
move.w #0,($A11100).l
 
  
SkipPlaySound_Special:
+
PlaySound:
rts
+
cmpi.w #$FB,d0
; End of function PlaySound_Special
+
blt.s PlayNotSpecialFlag
 +
bhi.s TestForNormalSpeed
 +
move #8,d0
 +
jmp SetTempo
  
; ---------------------------------------------------------------------------
+
TestForNormalSpeed:
; Subroutine to change the music tempo
+
cmpi.w #$FC,d0
; ---------------------------------------------------------------------------
+
bne.s PlayNotSpecialFlag
 +
clr.w d0
 +
jmp SetTempo
  
SetTempo:
+
PlayNotSpecialFlag:
 
move.w #$100,($A11100).l
 
move.w #$100,($A11100).l
  
SetTempoZ80NotStopped:
+
PlaySoundZ80NotStopped:
 
btst #0,($A11100).l
 
btst #0,($A11100).l
bne.s SetTempoZ80NotStopped
+
bne.s PlaySoundZ80NotStopped ; loop until it says it's stopped
move.b D0,($A01C08).l
+
move.b d0,($A01C0A).l
 
move.w #0,($A11100).l
 
move.w #0,($A11100).l
 
rts
 
rts
</asm>
+
; End of function PlaySound
 
 
==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:
 
<asm>
 
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Subroutine to pause the game
+
; Exclusive sound/music subroutine
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
 +
  
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 
; ||||||||||||||| 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:
+
PlaySound_Ex:
move.w #1,($FFFFF63A).w ; freeze time
+
tst.b 4(A0)
move.b #1,($FFFFF003).w ; pause music
+
bpl.s SkipPlaySound_Special
 +
</syntaxhighlight>
 +
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)
  
loc_13CA:
+
===sound playback routines===
move.b #$10,($FFFFF62A).w
+
now find:
bsr.w DelayProgram
+
<syntaxhighlight lang="asm">
tst.b ($FFFFFFE1).w ; is slow-motion cheat on?
+
; ---------------------------------------------------------------------------
beq.s Pause_ChkStart ; if not, branch
+
; Subroutine to play a special sound/music (E0-E4)
btst #6,($FFFFF605).w ; is button A pressed?
+
;
beq.s Pause_ChkBC ; if not, branch
+
; E0 - Fade out
move.b #4,($FFFFF600).w ; set game mode to 4 (title screen)
+
; E1 - Sega
nop
+
; E2 - Speed up
bra.s loc_1404
+
; E3 - Normal speed
; ===========================================================================
+
; E4 - Stop
 +
; ---------------------------------------------------------------------------
  
Pause_ChkBC: ; XREF: PauseGame
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
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
+
PlaySound_Special:
move.b #$80,($FFFFF003).w
+
move.b d0,($FFFFF00B).w
 
+
rts
Unpause: ; XREF: PauseGame
+
; End of function PlaySound_Special
move.w #0,($FFFFF63A).w ; unpause the game
 
  
Pause_DoNothing: ; XREF: PauseGame
 
rts
 
 
; ===========================================================================
 
; ===========================================================================
 +
; ---------------------------------------------------------------------------
 +
; Unused sound/music subroutine
 +
; ---------------------------------------------------------------------------
  
Pause_SlowMo: ; XREF: PauseGame
+
PlaySound_Unk:
move.w #1,($FFFFF63A).w
+
move.b d0,($FFFFF00C).w
move.b #$80,($FFFFF003).w
 
 
rts
 
rts
; End of function PauseGame
+
</syntaxhighlight>
</asm>
 
  
and replace it completely with:
+
and replace it entirely with:
<asm>
+
<syntaxhighlight lang="asm">
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Subroutine to pause the game
+
; Subroutine to play a special sound/music (FB-FF)
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
PlaySound_Special:
 +
move.w #$100,($A11100).l
  
 
+
PlaySound_SpecialZ80NotStopped:
PauseGame: ; XREF: Level_MainLoop; et al
 
nop
 
tst.b ($FFFFFE12).w
 
beq Unpause
 
tst.w ($FFFFF63A).w
 
bne.s PauseGame_AlreadyPaused
 
move.b ($FFFFF605).w,d0
 
or.b ($FFFFF607).w,d0
 
andi.b #$80,d0
 
beq Pause_DoNothing
 
 
 
PauseGame_AlreadyPaused:
 
move.w #1,($FFFFF63A).w
 
move.w #$100,($A11100).l
 
 
 
PauseGameZ80NotStopped:
 
 
btst #0,($A11100).l
 
btst #0,($A11100).l
bne.s PauseGameZ80NotStopped
+
bne.s PlaySound_SpecialZ80NotStopped
move.b #1,($A01C10).l
+
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
 
move.w #0,($A11100).l
 +
rts
  
PauseGameLoop:
+
PlaySound_Special0:
move.b #$10,($FFFFF62A).w
+
move.b d0,($A01C0C).l
jsr DelayProgram
 
tst.b ($FFFFFFE1).w ; Edit: the value is $FFFFFFE1 in Sonic 1
 
beq.s Pause_ChkStart
 
btst #6,($FFFFF605).w
 
beq.s Pause_ChkBC
 
move.b #$4,($FFFFF600).w ; Go To Title Screen
 
nop
 
bra.s PauseGame1
 
  
Pause_ChkBC:
+
PlaySound_Special1:
btst #4,($FFFFF604).w
+
move.w #0,($A11100).l
bne.s Pause_SlowMo
 
btst #5,($FFFFF605).w
 
bne.s Pause_SlowMo
 
  
Pause_ChkStart:
+
SkipPlaySound_Special:
cmpi.b #$E,($FFFFFE10).w
+
rts
bcs.s PauseGame0
+
; End of function PlaySound_Special
cmpi.b #$12,($FFFFFE10).w
 
bhi.s PauseGame0
 
tst.b ($FFFFFF8B).w
 
bpl.s PauseGame0
 
btst #4,($FFFFF605).w
 
beq.s PauseGame0
 
move.b #$C0,($FFFFF600).w
 
bra.s PauseGame1
 
  
PauseGame0:
+
; ---------------------------------------------------------------------------
move.b ($FFFFF605).w,d0
+
; Subroutine to change the music tempo
or.b ($FFFFF607).w,d0
+
; ---------------------------------------------------------------------------
andi.b #$80,d0
 
beq.s PauseGameLoop
 
  
PauseGame1:
+
SetTempo:
 
move.w #$100,($A11100).l
 
move.w #$100,($A11100).l
  
Pause_ChkStartZ80NotStopped:
+
SetTempoZ80NotStopped:
 
btst #0,($A11100).l
 
btst #0,($A11100).l
bne.s Pause_ChkStartZ80NotStopped
+
bne.s SetTempoZ80NotStopped
move.b #$80,($A01C10).l
+
move.b D0,($A01C08).l
 
move.w #0,($A11100).l
 
move.w #0,($A11100).l
 +
rts
 +
</syntaxhighlight>
  
Unpause:
+
==Upgrading Pause / Resume routines==
move.w #0,($FFFFF63A).w
+
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.
  
Pause_DoNothing:
+
Find:
rts
+
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to pause the game
 +
; ---------------------------------------------------------------------------
  
Pause_SlowMo:
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
move.w #1,($FFFFF63A).w
 
move.w #$100,($A11100).l
 
  
Pause_SlowMoZ80NotStopped:
 
btst #0,($A11100).l
 
bne.s Pause_SlowMoZ80NotStopped
 
move.b #$80,($A01C10).l
 
move.w #0,($A11100).l
 
rts
 
; End of function PauseGame
 
</asm>
 
  
==Replacing 68k driver with new Z80 driver==
+
PauseGame: ; XREF: Level_MainLoop; et al
We need the sound driver itself if we are going to have any sound and music, so we locate:
+
nop
<asm>
+
tst.b ($FFFFFE12).w ; do you have any lives left?
align 2
+
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
  
Go_SoundTypes: dc.l SoundTypes ; XREF: Sound_Play
+
loc_13BE:
Go_SoundD0: dc.l SoundD0Index ; XREF: Sound_D0toDF
+
move.w #1,($FFFFF63A).w ; freeze time
Go_MusicIndex: dc.l MusicIndex ; XREF: Sound_81to9F
+
move.b #1,($FFFFF003).w ; pause music
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
+
loc_13CA:
; ---------------------------------------------------------------------------
+
move.b #$10,($FFFFF62A).w
; Music Pointers
+
bsr.w WaitForVBla
; ---------------------------------------------------------------------------
+
tst.b ($FFFFFFE1).w ; is slow-motion cheat on?
MusicIndex: dc.l Music81, Music82
+
beq.s Pause_ChkStart ; if not, branch
dc.l Music83, Music84
+
btst #6,($FFFFF605).w ; is button A pressed?
dc.l Music85, Music86
+
beq.s Pause_ChkBC ; if not, branch
dc.l Music87, Music88
+
move.b #4,($FFFFF600).w ; set game mode to 4 (title screen)
dc.l Music89, Music8A
+
nop
dc.l Music8B, Music8C
+
bra.s loc_1404
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 |||||||||||||||||||||||||||||||||||||||
+
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
  
sub_71B4C: ; XREF: loc_B10; PalToCRAM
+
loc_1404: ; XREF: PauseGame
move.w #$100,($A11100).l ; stop the Z80
+
move.b #$80,($FFFFF003).w
nop
 
nop
 
nop
 
  
loc_71B5A:
+
Unpause: ; XREF: PauseGame
btst #0,($A11100).l
+
move.w #0,($FFFFF63A).w ; unpause the game
bne.s loc_71B5A
 
  
btst #7,($A01FFD).l
+
Pause_DoNothing: ; XREF: PauseGame
beq.s loc_71B82
+
rts
move.w #0,($A11100).l ; start the Z80
+
; ===========================================================================
nop
 
nop
 
nop
 
nop
 
nop
 
bra.s sub_71B4C
 
; ===========================================================================
 
  
loc_71B82:
+
Pause_SlowMo: ; XREF: PauseGame
lea ($FFF000).l,a6
+
move.w #1,($FFFFF63A).w
clr.b $E(a6)
+
move.b #$80,($FFFFF003).w
tst.b 3(a6) ; is music paused?
+
rts
bne.w loc_71E50 ; if yes, branch
+
; End of function PauseGame
subq.b #1,1(a6)
+
</syntaxhighlight>
bne.s loc_71B9E
 
jsr sub_7260C(pc)
 
  
loc_71B9E:
+
and replace it completely with:
move.b 4(a6),d0
+
<syntaxhighlight lang="asm">
beq.s loc_71BA8
+
; ---------------------------------------------------------------------------
jsr sub_72504(pc)
+
; Subroutine to pause the game
 +
; ---------------------------------------------------------------------------
  
loc_71BA8:
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
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:
+
PauseGame: ; XREF: Level_MainLoop; et al
cmpi.b #$80,9(a6)
+
nop
beq.s loc_71BC8
+
tst.b ($FFFFFE12).w ; do you have any lives left
jsr Sound_ChkValue(pc)
+
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
  
loc_71BC8:
+
PauseGame_AlreadyPaused:
lea $40(a6),a5
+
move.w #1,($FFFFF63A).w ;unpause the game
tst.b (a5)
+
move.w #$100,($A11100).l  ;stop the z80
bpl.s loc_71BD4
 
jsr sub_71C4E(pc)
 
  
loc_71BD4:
+
PauseGameZ80NotStopped:
clr.b 8(a6)
+
btst #0,($A11100).l
moveq #5,d7
+
bne.s PauseGameZ80NotStopped
 +
move.b #1,($A01C10).l ;unpause the music ;)
 +
move.w #0,($A11100).l ;start the z80
  
loc_71BDA:
+
PauseGameLoop:
adda.w #$30,a5
+
move.b #$10,($FFFFF62A).w ;re-pause the game (used in slow-mo and frame advance)
tst.b (a5)
+
jsr DelayProgram ;wait...
bpl.s loc_71BE6
+
tst.b ($FFFFFFE1).w ; Edit: the value is $FFFFFFE1 in Sonic 1 (slow-mo/frame advance mode)
jsr sub_71CCA(pc)
+
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
  
loc_71BE6:
+
Pause_ChkBC:
dbf d7,loc_71BDA
+
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
  
moveq #2,d7
+
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
  
loc_71BEC:
+
PauseGame0:
adda.w #$30,a5
+
move.b ($FFFFF605).w,d0  ;on controller 1?
tst.b (a5)
+
or.b ($FFFFF607).w,d0          ;or 2?
bpl.s loc_71BF8
+
andi.b #$80,d0                ;if not, no change
jsr sub_72850(pc)
+
beq.s PauseGameLoop  ;in other words, don't pause
  
loc_71BF8:
+
PauseGame1:
dbf d7,loc_71BEC
+
move.w #$100,($A11100).l  ;stop the z80
  
move.b #$80,$E(a6)
+
Pause_ChkStartZ80NotStopped:
moveq #2,d7
+
btst #0,($A11100).l
 +
bne.s Pause_ChkStartZ80NotStopped
 +
move.b #$80,($A01C10).l ;pause the music
 +
move.w #0,($A11100).l    ;start the z80
  
loc_71C04:
+
Unpause:
adda.w #$30,a5
+
move.w #0,($FFFFF63A).w ;unpause the game
tst.b (a5)
 
bpl.s loc_71C10
 
jsr sub_71CCA(pc)
 
  
loc_71C10:
+
Pause_DoNothing:
dbf d7,loc_71C04
+
rts
  
moveq #2,d7
+
Pause_SlowMo:
 +
move.w #1,($FFFFF63A).w ;unpause the music for a frame
 +
move.w #$100,($A11100).l  ;stop the z80
  
loc_71C16:
+
Pause_SlowMoZ80NotStopped:
adda.w #$30,a5
+
btst #0,($A11100).l
tst.b (a5)
+
bne.s Pause_SlowMoZ80NotStopped
bpl.s loc_71C22
+
move.b #$80,($A01C10).l ;pause the music again
jsr sub_72850(pc)
+
move.w #0,($A11100).l ;start the z80
 +
rts
 +
; End of function PauseGame
 +
</syntaxhighlight>
  
loc_71C22:
+
==Replacing 68k driver with new Z80 driver==
dbf d7,loc_71C16
+
We need the sound driver itself if we are going to have any sound and music, so we locate:
move.b #$40,$E(a6)
+
<syntaxhighlight lang="asm">
adda.w #$30,a5
+
align 2
tst.b (a5)
 
bpl.s loc_71C38
 
jsr sub_71CCA(pc)
 
  
loc_71C38:
+
Go_SoundTypes: dc.l SoundTypes ; XREF: Sound_Play
adda.w #$30,a5
+
Go_SoundD0: dc.l SoundD0Index ; XREF: Sound_D0toDF
tst.b (a5)
+
Go_MusicIndex: dc.l MusicIndex ; XREF: Sound_81to9F
bpl.s loc_71C44
+
Go_SoundIndex: dc.l SoundIndex ; XREF: Sound_A0toCF
jsr sub_72850(pc)
+
off_719A0: dc.l byte_71A94 ; XREF: Sound_81to9F
 
+
Go_PSGIndex: dc.l PSG_Index ; XREF: sub_72926
loc_71C44:
+
; ---------------------------------------------------------------------------
move.w #0,($A11100).l ; start the Z80
+
; PSG instruments used in music
rts
+
; ---------------------------------------------------------------------------
; End of function sub_71B4C
+
PSG_Index: dc.l PSG1, PSG2, PSG3
 
+
dc.l PSG4, PSG5, PSG6
 
+
dc.l PSG7, PSG8, PSG9
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
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_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:
+
sub_71B4C: ; XREF: loc_B10; PalToCRAM
moveq #0,d5
+
move.w #$100,($A11100).l ; stop the Z80
move.b (a4)+,d5
+
nop
cmpi.b #-$20,d5
+
nop
bcs.s loc_71C6E
+
nop
jsr sub_72A5A(pc)
 
bra.s loc_71C5E
 
; ===========================================================================
 
  
loc_71C6E:
+
loc_71B5A:
tst.b d5
+
btst #0,($A11100).l
bpl.s loc_71C84
+
bne.s loc_71B5A
move.b d5,$10(a5)
+
 
move.b (a4)+,d5
+
btst #7,($A01FFD).l
bpl.s loc_71C84
+
beq.s loc_71B82
subq.w #1,a4
+
move.w #0,($A11100).l ; start the Z80
move.b $F(a5),$E(a5)
+
nop
bra.s loc_71C88
+
nop
 +
nop
 +
nop
 +
nop
 +
bra.s sub_71B4C
 
; ===========================================================================
 
; ===========================================================================
  
loc_71C84:
+
loc_71B82:
jsr sub_71D40(pc)
+
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_71C88:
+
loc_71B9E:
move.l a4,4(a5)
+
move.b 4(a6),d0
btst #2,(a5)
+
beq.s loc_71BA8
bne.s locret_71CAA
+
jsr sub_72504(pc)
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:
+
loc_71BA8:
rts
+
tst.b $24(a6)
; ===========================================================================
+
beq.s loc_71BB2
 +
jsr sub_7267C(pc)
  
loc_71CAC:
+
loc_71BB2:
subi.b #$88,d0
+
tst.w $A(a6) ; is music or sound being played?
move.b byte_71CC4(pc,d0.w),d0
+
beq.s loc_71BBC ; if not, branch
move.b d0,($A000EA).l
+
jsr Sound_Play(pc)
move.b #$83,($A01FFF).l
 
rts
 
; End of function sub_71C4E
 
  
; ===========================================================================
+
loc_71BBC:
byte_71CC4: dc.b $12, $15, $1C, $1D, $FF, $FF
+
cmpi.b #$80,9(a6)
 +
beq.s loc_71BC8
 +
jsr Sound_ChkValue(pc)
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
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
  
sub_71CCA: ; XREF: sub_71B4C
+
loc_71BDA:
subq.b #1,$E(a5)
+
adda.w #$30,a5
bne.s loc_71CE0
+
tst.b (a5)
bclr #4,(a5)
+
bpl.s loc_71BE6
jsr sub_71CEC(pc)
+
jsr sub_71CCA(pc)
jsr sub_71E18(pc)
+
 
bra.w loc_726E2
+
loc_71BE6:
; ===========================================================================
+
dbf d7,loc_71BDA
  
loc_71CE0:
+
moveq #2,d7
jsr sub_71D9E(pc)
 
jsr sub_71DC6(pc)
 
bra.w loc_71E24
 
; End of function sub_71CCA
 
  
 +
loc_71BEC:
 +
adda.w #$30,a5
 +
tst.b (a5)
 +
bpl.s loc_71BF8
 +
jsr sub_72850(pc)
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
loc_71BF8:
 +
dbf d7,loc_71BEC
  
 +
move.b #$80,$E(a6)
 +
moveq #2,d7
  
sub_71CEC: ; XREF: sub_71CCA
+
loc_71C04:
movea.l 4(a5),a4
+
adda.w #$30,a5
bclr #1,(a5)
+
tst.b (a5)
 +
bpl.s loc_71C10
 +
jsr sub_71CCA(pc)
  
loc_71CF4:
+
loc_71C10:
moveq #0,d5
+
dbf d7,loc_71C04
move.b (a4)+,d5
 
cmpi.b #-$20,d5
 
bcs.s loc_71D04
 
jsr sub_72A5A(pc)
 
bra.s loc_71CF4
 
; ===========================================================================
 
  
loc_71D04:
+
moveq #2,d7
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:
+
loc_71C16:
jsr sub_71D40(pc)
+
adda.w #$30,a5
bra.w sub_71D60
+
tst.b (a5)
; End of function sub_71CEC
+
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)
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
loc_71C38:
 +
adda.w #$30,a5
 +
tst.b (a5)
 +
bpl.s loc_71C44
 +
jsr sub_72850(pc)
  
 
+
loc_71C44:
sub_71D22: ; XREF: sub_71CEC
+
move.w #0,($A11100).l ; start the Z80
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
 
rts
; End of function sub_71D22
+
; End of function sub_71B4C
  
  
Line 740: Line 1,125:
  
  
sub_71D40: ; XREF: sub_71C4E; sub_71CEC; sub_72878
+
sub_71C4E: ; XREF: sub_71B4C
move.b d5,d0
+
subq.b #1,$E(a5)
move.b 2(a5),d1
+
bne.s locret_71CAA
 +
move.b #$80,8(a6)
 +
movea.l 4(a5),a4
  
loc_71D46:
+
loc_71C5E:
subq.b #1,d1
+
moveq #0,d5
beq.s loc_71D4E
+
move.b (a4)+,d5
add.b d5,d0
+
cmpi.b #-$20,d5
bra.s loc_71D46
+
bcs.s loc_71C6E
 +
jsr sub_72A5A(pc)
 +
bra.s loc_71C5E
 
; ===========================================================================
 
; ===========================================================================
  
loc_71D4E:
+
loc_71C6E:
move.b d0,$F(a5)
+
tst.b d5
move.b d0,$E(a5)
+
bpl.s loc_71C84
rts
+
move.b d5,$10(a5)
; End of function sub_71D40
+
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_71D58: ; XREF: sub_71D22
+
loc_71CAC:
bset #1,(a5)
+
subi.b #$88,d0
clr.w $10(a5)
+
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 |||||||||||||||||||||||||||||||||||||||
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
  
  
sub_71D60: ; XREF: sub_71CEC; sub_72878; sub_728AC
+
sub_71CCA: ; XREF: sub_71B4C
move.l a4,4(a5)
+
subq.b #1,$E(a5)
move.b $F(a5),$E(a5)
+
bne.s loc_71CE0
btst #4,(a5)
+
bclr #4,(a5)
bne.s locret_71D9C
+
jsr sub_71CEC(pc)
move.b $13(a5),$12(a5)
+
jsr sub_71E18(pc)
clr.b $C(a5)
+
bra.w loc_726E2
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:
+
loc_71CE0:
rts
+
jsr sub_71D9E(pc)
; End of function sub_71D60
+
jsr sub_71DC6(pc)
 +
bra.w loc_71E24
 +
; End of function sub_71CCA
  
  
Line 792: Line 1,203:
  
  
sub_71D9E: ; XREF: sub_71CCA; sub_72850
+
sub_71CEC: ; XREF: sub_71CCA
tst.b $12(a5)
+
movea.l 4(a5),a4
beq.s locret_71DC4
+
bclr #1,(a5)
subq.b #1,$12(a5)
+
 
bne.s locret_71DC4
+
loc_71CF4:
bset #1,(a5)
+
moveq #0,d5
tst.b 1(a5)
+
move.b (a4)+,d5
bmi.w loc_71DBE
+
cmpi.b #-$20,d5
 +
bcs.s loc_71D04
 +
jsr sub_72A5A(pc)
 +
bra.s loc_71CF4
 +
; ===========================================================================
 +
 
 +
loc_71D04:
 
jsr sub_726FE(pc)
 
jsr sub_726FE(pc)
addq.w #4,sp
+
tst.b d5
rts
+
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_71DBE:
+
loc_71D1A:
jsr sub_729A0(pc)
+
jsr sub_71D40(pc)
addq.w #4,sp
+
bra.w sub_71D60
 
+
; End of function sub_71CEC
locret_71DC4:
 
rts
 
; End of function sub_71D9E
 
  
  
Line 817: Line 1,236:
  
  
sub_71DC6: ; XREF: sub_71CCA; sub_72850
+
sub_71D22: ; XREF: sub_71CEC
addq.w #4,sp
+
subi.b #$80,d5
btst #3,(a5)
+
beq.s loc_71D58
beq.s locret_71E16
+
add.b 8(a5),d5
tst.b $18(a5)
+
andi.w #$7F,d5
beq.s loc_71DDA
+
lsl.w #1,d5
subq.b #1,$18(a5)
+
lea word_72790(pc),a0
 +
move.w (a0,d5.w),d6
 +
move.w d6,$10(a5)
 
rts
 
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_71DDA:
+
loc_71D4E:
subq.b #1,$19(a5)
+
move.b d0,$F(a5)
beq.s loc_71DE2
+
move.b d0,$E(a5)
 
rts
 
rts
; ===========================================================================
+
; End of function sub_71D40
  
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:
+
loc_71D58: ; XREF: sub_71D22
subq.b #1,$1B(a5)
+
bset #1,(a5)
move.b $1A(a5),d6
+
clr.w $10(a5)
ext.w d6
+
 
add.w $1C(a5),d6
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
move.w d6,$1C(a5)
+
 
add.w $10(a5),d6
+
 
subq.w #4,sp
+
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_71E16:
+
locret_71D9C:
 
rts
 
rts
; End of function sub_71DC6
+
; End of function sub_71D60
  
  
Line 860: Line 1,304:
  
  
sub_71E18: ; XREF: sub_71CCA
+
sub_71D9E: ; XREF: sub_71CCA; sub_72850
btst #1,(a5)
+
tst.b $12(a5)
bne.s locret_71E48
+
beq.s locret_71DC4
move.w $10(a5),d6
+
subq.b #1,$12(a5)
beq.s loc_71E4A
+
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_71E24: ; XREF: sub_71CCA
+
loc_71DBE:
move.b $1E(a5),d0
+
jsr sub_729A0(pc)
ext.w d0
+
addq.w #4,sp
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:
+
locret_71DC4:
 
rts
 
rts
; ===========================================================================
+
; End of function sub_71D9E
  
loc_71E4A:
 
bset #1,(a5)
 
rts
 
; End of function sub_71E18
 
  
; ===========================================================================
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
  
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:
+
sub_71DC6: ; XREF: sub_71CCA; sub_72850
jsr sub_7272E(pc)
+
addq.w #4,sp
jsr sub_72764(pc)
+
btst #3,(a5)
addq.b #1,d0
+
beq.s locret_71E16
dbf d3,loc_71E6A
+
tst.b $18(a5)
 +
beq.s loc_71DDA
 +
subq.b #1,$18(a5)
 +
rts
 +
; ===========================================================================
  
moveq #2,d3
+
loc_71DDA:
moveq #$28,d0
+
subq.b #1,$19(a5)
 
+
beq.s loc_71DE2
loc_71E7C:
+
rts
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
+
loc_71DE2:
clr.b 3(a6)
+
movea.l $14(a5),a0
moveq #$30,d3
+
move.b 1(a0),$19(a5)
lea $40(a6),a5
+
tst.b $1B(a5)
moveq #6,d4
+
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
  
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
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
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:
+
sub_71E18: ; XREF: sub_71CCA
adda.w d3,a5
+
btst #1,(a5)
dbf d4,loc_71EC4
+
bne.s locret_71E48
 +
move.w $10(a5),d6
 +
beq.s loc_71E4A
  
lea $340(a6),a5
+
loc_71E24: ; XREF: sub_71CCA
btst #7,(a5)
+
move.b $1E(a5),d0
beq.s loc_71EFE
+
ext.w d0
 +
add.w d0,d6
 
btst #2,(a5)
 
btst #2,(a5)
bne.s loc_71EFE
+
bne.s locret_71E48
move.b #-$4C,d0
+
move.w d6,d1
move.b $A(a5),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)
 
jsr sub_72722(pc)
  
loc_71EFE:
+
locret_71E48:
bra.w loc_71C44
+
rts
 +
; ===========================================================================
  
; ---------------------------------------------------------------------------
+
loc_71E4A:
; Subroutine to play a sound or music track
+
bset #1,(a5)
; ---------------------------------------------------------------------------
+
rts
 +
; End of function sub_71E18
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
; ===========================================================================
  
 +
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
  
Sound_Play: ; XREF: sub_71B4C
+
loc_71E6A:
movea.l (Go_SoundTypes).l,a0
+
jsr sub_7272E(pc)
lea $A(a6),a1 ; load music track number
+
jsr sub_72764(pc)
move.b 0(a6),d3
+
addq.b #1,d0
moveq #2,d4
+
dbf d3,loc_71E6A
  
loc_71F12:
+
moveq #2,d3
move.b (a1),d0 ; move track number to d0
+
moveq #$28,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:
+
loc_71E7C:
andi.w #$7F,d0
+
move.b d3,d1
move.b (a0,d0.w),d2
+
jsr sub_7272E(pc)
cmp.b d3,d2
+
addq.b #4,d1
bcs.s loc_71F3E
+
jsr sub_7272E(pc)
move.b d2,d3
+
dbf d3,loc_71E7C
move.b d1,9(a6) ; set music flag
 
  
loc_71F3E:
+
jsr sub_729B6(pc)
dbf d4,loc_71F12
+
bra.w loc_71C44
 +
; ===========================================================================
  
tst.b d3
+
loc_71E94: ; XREF: loc_71E50
bmi.s locret_71F4A
+
clr.b 3(a6)
move.b d3,0(a6)
+
moveq #$30,d3
 +
lea $40(a6),a5
 +
moveq #6,d4
  
locret_71F4A:
+
loc_71EA0:
rts
+
btst #7,(a5)
; End of function Sound_Play
+
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
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
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)
  
Sound_ChkValue: ; XREF: sub_71B4C
+
loc_71EDC:
moveq #0,d7
+
adda.w d3,a5
move.b 9(a6),d7
+
dbf d4,loc_71EC4
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
+
lea $340(a6),a5
subi.b #$E0,d7
+
btst #7,(a5)
lsl.w #2,d7
+
beq.s loc_71EFE
jmp Sound_ExIndex(pc,d7.w)
+
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
  
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
+
; Subroutine to play a sound or music track
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
  
Sound_E1: ; XREF: Sound_ExIndex
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
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:
+
Sound_Play: ; XREF: sub_71B4C
nop
+
movea.l (Go_SoundTypes).l,a0
dbf d0,loc_71FC4
+
lea $A(a6),a1 ; load music track number
 +
move.b 0(a6),d3
 +
moveq #2,d4
  
dbf d1,loc_71FC0
+
loc_71F12:
 
+
move.b (a1),d0 ; move track number to d0
addq.w #4,sp
+
move.b d0,d1
rts
+
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
 
; ===========================================================================
 
; ===========================================================================
; ---------------------------------------------------------------------------
 
; Play music track $81-$9F
 
; ---------------------------------------------------------------------------
 
  
Sound_81to9F: ; XREF: Sound_ChkValue
+
loc_71F2C:
cmpi.b #$88,d7 ; is "extra life" music played?
+
andi.w #$7F,d0
bne.s loc_72024 ; if not, branch
+
move.b (a0,d0.w),d2
tst.b $27(a6)
+
cmp.b d3,d2
bne.w loc_721B6
+
bcs.s loc_71F3E
lea $40(a6),a5
+
move.b d2,d3
moveq #9,d0
+
move.b d1,9(a6) ; set music flag
  
loc_71FE6:
+
loc_71F3E:
bclr #2,(a5)
+
dbf d4,loc_71F12
adda.w #$30,a5
 
dbf d0,loc_71FE6
 
  
lea $220(a6),a5
+
tst.b d3
moveq #5,d0
+
bmi.s locret_71F4A
 +
move.b d3,0(a6)
  
loc_71FF8:
+
locret_71F4A:
bclr #7,(a5)
+
rts
adda.w #$30,a5
+
; End of function Sound_Play
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)
+
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
clr.b 0(a6)
 
bra.s loc_7202C
 
; ===========================================================================
 
  
loc_72024:
 
clr.b $27(a6)
 
clr.b $26(a6)
 
  
loc_7202C:
+
Sound_ChkValue: ; XREF: sub_71B4C
jsr sub_725CA(pc)
+
moveq #0,d7
movea.l (off_719A0).l,a4
+
move.b 9(a6),d7
subi.b #$81,d7
+
beq.w Sound_E4
move.b (a4,d7.w),$29(a6)
+
bpl.s locret_71F8C
movea.l (Go_MusicIndex).l,a4
+
move.b #$80,9(a6) ; reset music flag
lsl.w #2,d7
+
cmpi.b #$9F,d7
movea.l (a4,d7.w),a4
+
bls.w Sound_81to9F ; music $81-$9F
moveq #0,d0
+
cmpi.b #$A0,d7
move.w (a4),d0
+
bcs.w locret_71F8C
add.l a4,d0
+
cmpi.b #$CF,d7
move.l d0,$18(a6)
+
bls.w Sound_A0toCF ; sound $A0-$CF
move.b 5(a4),d0
+
cmpi.b #$D0,d7
move.b d0,$28(a6)
+
bcs.w locret_71F8C
tst.b $2A(a6)
+
cmpi.b #$E0,d7
beq.s loc_72068
+
bcs.w Sound_D0toDF ; sound $D0-$DF
move.b $29(a6),d0
+
cmpi.b #$E4,d7
 +
bls.s Sound_E0toE4 ; sound $E0-$E4
  
loc_72068:
+
locret_71F8C:
move.b d0,2(a6)
+
rts
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:
+
Sound_E0toE4: ; XREF: Sound_ChkValue
bset #7,(a1)
+
subi.b #$E0,d7
move.b (a2)+,1(a1)
+
lsl.w #2,d7
move.b d4,2(a1)
+
jmp Sound_ExIndex(pc,d7.w)
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:
+
Sound_ExIndex:
moveq #$28,d0
+
bra.w Sound_E0
moveq #6,d1
+
; ===========================================================================
jsr sub_7272E(pc)
+
bra.w Sound_E1
move.b #$42,d0
+
; ===========================================================================
moveq #$7F,d1
+
bra.w Sound_E2
jsr sub_72764(pc)
+
; ===========================================================================
move.b #$4A,d0
+
bra.w Sound_E3
moveq #$7F,d1
+
; ===========================================================================
jsr sub_72764(pc)
+
bra.w Sound_E4
move.b #$46,d0
+
; ===========================================================================
moveq #$7F,d1
+
; ---------------------------------------------------------------------------
jsr sub_72764(pc)
+
; Play "Say-gaa" PCM sound
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:
+
Sound_E1: ; XREF: Sound_ExIndex
moveq #0,d7
+
move.b #$88,($A01FFF).l
move.b 3(a3),d7
+
move.w #0,($A11100).l ; start the Z80
beq.s loc_72154
+
move.w #$11,d1
subq.b #1,d7
 
lea $190(a6),a1
 
lea byte_721C2(pc),a2
 
  
loc_72126:
+
loc_71FC0:
bset #7,(a1)
+
move.w #-1,d0
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:
+
loc_71FC4:
lea $220(a6),a1
+
nop
moveq #5,d7
+
dbf d0,loc_71FC4
 +
 
 +
dbf d1,loc_71FC0
  
loc_7215A:
+
addq.w #4,sp
tst.b (a1)
+
rts
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
 
 
; ===========================================================================
 
; ===========================================================================
 +
; ---------------------------------------------------------------------------
 +
; Play music track $81-$9F
 +
; ---------------------------------------------------------------------------
  
loc_7216E:
+
Sound_81to9F: ; XREF: Sound_ChkValue
lsr.b #3,d0
+
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_72170:
+
loc_71FE6:
lea dword_722CC(pc),a0
+
bclr #2,(a5)
movea.l (a0,d0.w),a0
+
adda.w #$30,a5
bset #2,(a0)
+
dbf d0,loc_71FE6
  
loc_7217C:
+
lea $220(a6),a5
adda.w d6,a1
+
moveq #5,d0
dbf d7,loc_7215A
 
  
tst.w $340(a6)
+
loc_71FF8:
bpl.s loc_7218E
+
bclr #7,(a5)
bset #2,$100(a6)
+
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_7218E:
+
loc_72012:
tst.w $370(a6)
+
move.l (a0)+,(a1)+
bpl.s loc_7219A
+
dbf d0,loc_72012
bset #2,$1F0(a6)
 
  
loc_7219A:
+
move.b #$80,$27(a6)
lea $70(a6),a5
+
clr.b 0(a6)
moveq #5,d4
+
bra.s loc_7202C
 +
; ===========================================================================
  
loc_721A0:
+
loc_72024:
jsr sub_726FE(pc)
+
clr.b $27(a6)
adda.w d6,a5
+
clr.b $26(a6)
dbf d4,loc_721A0
 
moveq #2,d4
 
  
loc_721AC:
+
loc_7202C:
jsr sub_729A0(pc)
+
jsr sub_725CA(pc)
adda.w d6,a5
+
movea.l (off_719A0).l,a4
dbf d4,loc_721AC
+
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_721B6:
+
loc_72068:
addq.w #4,sp
+
move.b d0,2(a6)
rts
+
move.b d0,1(a6)
; ===========================================================================
+
moveq #0,d1
byte_721BA: dc.b 6, 0, 1, 2, 4, 5, 6, 0
+
movea.l a4,a3
align 2
+
addq.w #6,a4
byte_721C2: dc.b $80, $A0, $C0, 0
+
moveq #0,d7
align 2
+
move.b 2(a3),d7
; ===========================================================================
+
beq.w loc_72114
; ---------------------------------------------------------------------------
+
subq.b #1,d7
; Play normal sound effect
+
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
  
Sound_A0toCF: ; XREF: Sound_ChkValue
+
loc_72098:
tst.b $27(a6)
+
bset #7,(a1)
bne.w loc_722C6
+
move.b (a2)+,1(a1)
tst.b 4(a6)
+
move.b d4,2(a1)
bne.w loc_722C6
+
move.b d6,$D(a1)
tst.b $24(a6)
+
move.b d1,$A(a1)
bne.w loc_722C6
+
move.b d5,$E(a1)
cmpi.b #$B5,d7 ; is ring sound effect played?
+
moveq #0,d0
bne.s Sound_notB5 ; if not, branch
+
move.w (a4)+,d0
tst.b $2B(a6)
+
add.l a3,d0
bne.s loc_721EE
+
move.l d0,4(a1)
move.b #$CE,d7 ; play ring sound in left speaker
+
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_721EE:
+
loc_720D8:
bchg #0,$2B(a6) ; change speaker
+
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)
  
Sound_notB5:
+
loc_72114:
cmpi.b #$A7,d7 ; is "pushing" sound played?
+
moveq #0,d7
bne.s Sound_notA7 ; if not, branch
+
move.b 3(a3),d7
tst.b $2C(a6)
+
beq.s loc_72154
bne.w locret_722C4
+
subq.b #1,d7
move.b #$80,$2C(a6)
+
lea $190(a6),a1
 +
lea byte_721C2(pc),a2
  
Sound_notA7:
+
loc_72126:
movea.l (Go_SoundIndex).l,a0
+
bset #7,(a1)
subi.b #$A0,d7
+
move.b (a2)+,1(a1)
lsl.w #2,d7
+
move.b d4,2(a1)
movea.l (a0,d7.w),a3
+
move.b d6,$D(a1)
movea.l a3,a1
+
move.b d5,$E(a1)
moveq #0,d1
+
moveq #0,d0
move.w (a1)+,d1
+
move.w (a4)+,d0
add.l a3,d1
+
add.l a3,d0
move.b (a1)+,d5
+
move.l d0,4(a1)
move.b (a1)+,d7
+
move.w (a4)+,8(a1)
subq.b #1,d7
+
move.b (a4)+,d0
moveq #$30,d6
+
move.b (a4)+,$B(a1)
 +
adda.w d6,a1
 +
dbf d7,loc_72126
  
loc_72228:
+
loc_72154:
moveq #0,d3
+
lea $220(a6),a1
move.b 1(a1),d3
+
moveq #5,d7
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:
+
loc_7215A:
lsr.w #3,d3
+
tst.b (a1)
lea dword_722CC(pc),a5
+
bpl.w loc_7217C
movea.l (a5,d3.w),a5
+
moveq #0,d0
bset #2,(a5)
+
move.b 1(a1),d0
cmpi.b #$C0,d4
+
bmi.s loc_7216E
bne.s loc_7226E
+
subq.b #2,d0
move.b d4,d0
+
lsl.b #2,d0
ori.b #$1F,d0
+
bra.s loc_72170
move.b d0,($C00011).l
+
; ===========================================================================
bchg #5,d0
+
 
move.b d0,($C00011).l
+
loc_7216E:
 +
lsr.b #3,d0
  
loc_7226E:
+
loc_72170:
movea.l dword_722EC(pc,d3.w),a5
+
lea dword_722CC(pc),a0
movea.l a5,a2
+
movea.l (a0,d0.w),a0
moveq #$B,d0
+
bset #2,(a0)
  
loc_72276:
+
loc_7217C:
clr.l (a2)+
+
adda.w d6,a1
dbf d0,loc_72276
+
dbf d7,loc_7215A
  
move.w (a1)+,(a5)
+
tst.w $340(a6)
move.b d5,2(a5)
+
bpl.s loc_7218E
moveq #0,d0
+
bset #2,$100(a6)
move.w (a1)+,d0
+
 
add.l a3,d0
+
loc_7218E:
move.l d0,4(a5)
+
tst.w $370(a6)
move.w (a1)+,8(a5)
+
bpl.s loc_7219A
move.b #1,$E(a5)
+
bset #2,$1F0(a6)
move.b d6,$D(a5)
 
tst.b d4
 
bmi.s loc_722A8
 
move.b #$C0,$A(a5)
 
move.l d1,$20(a5)
 
  
loc_722A8:
+
loc_7219A:
dbf d7,loc_72228
+
lea $70(a6),a5
 +
moveq #5,d4
  
tst.b $250(a6)
+
loc_721A0:
bpl.s loc_722B8
+
jsr sub_726FE(pc)
bset #2,$340(a6)
+
adda.w d6,a5
 +
dbf d4,loc_721A0
 +
moveq #2,d4
  
loc_722B8:
+
loc_721AC:
tst.b $310(a6)
+
jsr sub_729A0(pc)
bpl.s locret_722C4
+
adda.w d6,a5
bset #2,$370(a6)
+
dbf d4,loc_721AC
  
locret_722C4:
+
loc_721B6:
 +
addq.w #4,sp
 
rts
 
rts
 
; ===========================================================================
 
; ===========================================================================
 
+
byte_721BA: dc.b 6, 0, 1, 2, 4, 5, 6, 0
loc_722C6:
+
align 2
clr.b 0(a6)
+
byte_721C2: dc.b $80, $A0, $C0, 0
rts
+
align 2
; ===========================================================================
 
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
+
; Play normal sound effect
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
  
Sound_D0toDF: ; XREF: Sound_ChkValue
+
Sound_A0toCF: ; XREF: Sound_ChkValue
 
tst.b $27(a6)
 
tst.b $27(a6)
bne.w locret_723C6
+
bne.w loc_722C6
 
tst.b 4(a6)
 
tst.b 4(a6)
bne.w locret_723C6
+
bne.w loc_722C6
 
tst.b $24(a6)
 
tst.b $24(a6)
bne.w locret_723C6
+
bne.w loc_722C6
movea.l (Go_SoundD0).l,a0
+
cmpi.b #$B5,d7 ; is ring sound effect played?
subi.b #$D0,d7
+
bne.s Sound_notB5 ; if not, branch
lsl.w #2,d7
+
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 (a0,d7.w),a3
 
movea.l a3,a1
 
movea.l a3,a1
moveq #0,d0
+
moveq #0,d1
move.w (a1)+,d0
+
move.w (a1)+,d1
add.l a3,d0
+
add.l a3,d1
move.l d0,$20(a6)
 
 
move.b (a1)+,d5
 
move.b (a1)+,d5
 
move.b (a1)+,d7
 
move.b (a1)+,d7
Line 1,431: Line 1,833:
 
moveq #$30,d6
 
moveq #$30,d6
  
loc_72348:
+
loc_72228:
move.b 1(a1),d4
+
moveq #0,d3
bmi.s loc_7235A
+
move.b 1(a1),d3
bset #2,$100(a6)
+
move.b d3,d4
lea $340(a6),a5
+
bmi.s loc_72244
bra.s loc_72364
+
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_7235A:
+
loc_72244:
bset #2,$1F0(a6)
+
lsr.w #3,d3
lea $370(a6),a5
+
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_72364:
+
loc_7226E:
 +
movea.l dword_722EC(pc,d3.w),a5
 
movea.l a5,a2
 
movea.l a5,a2
 
moveq #$B,d0
 
moveq #$B,d0
  
loc_72368:
+
loc_72276:
 
clr.l (a2)+
 
clr.l (a2)+
dbf d0,loc_72368
+
dbf d0,loc_72276
  
 
move.w (a1)+,(a5)
 
move.w (a1)+,(a5)
Line 1,461: Line 1,878:
 
move.b d6,$D(a5)
 
move.b d6,$D(a5)
 
tst.b d4
 
tst.b d4
bmi.s loc_72396
+
bmi.s loc_722A8
 
move.b #$C0,$A(a5)
 
move.b #$C0,$A(a5)
 +
move.l d1,$20(a5)
  
loc_72396:
+
loc_722A8:
dbf d7,loc_72348
+
dbf d7,loc_72228
  
 
tst.b $250(a6)
 
tst.b $250(a6)
bpl.s loc_723A6
+
bpl.s loc_722B8
 
bset #2,$340(a6)
 
bset #2,$340(a6)
  
loc_723A6:
+
loc_722B8:
 
tst.b $310(a6)
 
tst.b $310(a6)
bpl.s locret_723C6
+
bpl.s locret_722C4
 
bset #2,$370(a6)
 
bset #2,$370(a6)
ori.b #$1F,d4
 
move.b d4,($C00011).l
 
bchg #5,d4
 
move.b d4,($C00011).l
 
  
locret_723C6:
+
locret_722C4:
 
rts
 
rts
; End of function Sound_ChkValue
+
; ===========================================================================
  
 +
loc_722C6:
 +
clr.b 0(a6)
 +
rts
 
; ===========================================================================
 
; ===========================================================================
 +
dword_722CC: dc.l $FFF0D0
 +
dc.l 0
 
dc.l $FFF100
 
dc.l $FFF100
 +
dc.l $FFF130
 +
dc.l $FFF190
 +
dc.l $FFF1C0
 
dc.l $FFF1F0
 
dc.l $FFF1F0
 +
dc.l $FFF1F0
 +
dword_722EC: dc.l $FFF220
 +
dc.l 0
 
dc.l $FFF250
 
dc.l $FFF250
 +
dc.l $FFF280
 +
dc.l $FFF2B0
 +
dc.l $FFF2E0
 
dc.l $FFF310
 
dc.l $FFF310
dc.l $FFF340
+
dc.l $FFF310
dc.l $FFF370
+
; ===========================================================================
 +
; ---------------------------------------------------------------------------
 +
; Play GHZ waterfall sound
 +
; ---------------------------------------------------------------------------
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
Sound_D0toDF: ; XREF: Sound_ChkValue
 
+
tst.b $27(a6)
 
+
bne.w locret_723C6
Snd_FadeOut1: ; XREF: Sound_E0
+
tst.b 4(a6)
clr.b 0(a6)
+
bne.w locret_723C6
lea $220(a6),a5
+
tst.b $24(a6)
moveq #5,d7
+
bne.w locret_723C6
 
+
movea.l (Go_SoundD0).l,a0
loc_723EA:
+
subi.b #$D0,d7
tst.b (a5)
+
lsl.w #2,d7
bpl.w loc_72472
+
movea.l (a0,d7.w),a3
bclr #7,(a5)
+
movea.l a3,a1
moveq #0,d3
+
moveq #0,d0
move.b 1(a5),d3
+
move.w (a1)+,d0
bmi.s loc_7243C
+
add.l a3,d0
jsr sub_726FE(pc)
+
move.l d0,$20(a6)
cmpi.b #4,d3
+
move.b (a1)+,d5
bne.s loc_72416
+
move.b (a1)+,d7
tst.b $340(a6)
+
subq.b #1,d7
bpl.s loc_72416
+
moveq #$30,d6
 +
 
 +
loc_72348:
 +
move.b 1(a1),d4
 +
bmi.s loc_7235A
 +
bset #2,$100(a6)
 
lea $340(a6),a5
 
lea $340(a6),a5
movea.l $20(a6),a1
+
bra.s loc_72364
bra.s loc_72428
 
 
; ===========================================================================
 
; ===========================================================================
  
loc_72416:
+
loc_7235A:
subq.b #2,d3
+
bset #2,$1F0(a6)
lsl.b #2,d3
+
lea $370(a6),a5
lea dword_722CC(pc),a0
+
 
movea.l a5,a3
+
loc_72364:
movea.l (a0,d3.w),a5
+
movea.l a5,a2
movea.l $18(a6),a1
+
moveq #$B,d0
  
loc_72428:
+
loc_72368:
bclr #2,(a5)
+
clr.l (a2)+
bset #1,(a5)
+
dbf d0,loc_72368
move.b $B(a5),d0
 
jsr sub_72C4E(pc)
 
movea.l a3,a5
 
bra.s loc_72472
 
; ===========================================================================
 
  
loc_7243C:
+
move.w (a1)+,(a5)
jsr sub_729A0(pc)
+
move.b d5,2(a5)
lea $370(a6),a0
+
moveq #0,d0
cmpi.b #$E0,d3
+
move.w (a1)+,d0
beq.s loc_7245A
+
add.l a3,d0
cmpi.b #$C0,d3
+
move.l d0,4(a5)
beq.s loc_7245A
+
move.w (a1)+,8(a5)
lsr.b #3,d3
+
move.b #1,$E(a5)
lea dword_722CC(pc),a0
+
move.b d6,$D(a5)
movea.l (a0,d3.w),a0
+
tst.b d4
 +
bmi.s loc_72396
 +
move.b #$C0,$A(a5)
  
loc_7245A:
+
loc_72396:
bclr #2,(a0)
+
dbf d7,loc_72348
bset #1,(a0)
+
 
cmpi.b #$E0,1(a0)
+
tst.b $250(a6)
bne.s loc_72472
+
bpl.s loc_723A6
move.b $1F(a0),($C00011).l
+
bset #2,$340(a6)
  
loc_72472:
+
loc_723A6:
adda.w #$30,a5
+
tst.b $310(a6)
dbf d7,loc_723EA
+
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
 
rts
; End of function Snd_FadeOut1
+
; 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 |||||||||||||||||||||||||||||||||||||||
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
  
  
Snd_FadeOut2: ; XREF: Sound_E0
+
Snd_FadeOut1: ; XREF: Sound_E0
lea $340(a6),a5
+
clr.b 0(a6)
 +
lea $220(a6),a5
 +
moveq #5,d7
 +
 
 +
loc_723EA:
 
tst.b (a5)
 
tst.b (a5)
bpl.s loc_724AE
+
bpl.w loc_72472
 
bclr #7,(a5)
 
bclr #7,(a5)
btst #2,(a5)
+
moveq #0,d3
bne.s loc_724AE
+
move.b 1(a5),d3
jsr loc_7270A(pc)
+
bmi.s loc_7243C
lea $100(a6),a5
+
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)
 
bclr #2,(a5)
 
bset #1,(a5)
 
bset #1,(a5)
tst.b (a5)
 
bpl.s loc_724AE
 
movea.l $18(a6),a1
 
 
move.b $B(a5),d0
 
move.b $B(a5),d0
 
jsr sub_72C4E(pc)
 
jsr sub_72C4E(pc)
 +
movea.l a3,a5
 +
bra.s loc_72472
 +
; ===========================================================================
  
loc_724AE:
+
loc_7243C:
lea $370(a6),a5
+
jsr sub_729A0(pc)
tst.b (a5)
+
lea $370(a6),a0
bpl.s locret_724E4
+
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)
 
bclr #7,(a5)
 
btst #2,(a5)
 
btst #2,(a5)
bne.s locret_724E4
+
bne.s loc_724AE
jsr loc_729A6(pc)
+
jsr loc_7270A(pc)
lea $1F0(a6),a5
+
lea $100(a6),a5
 
bclr #2,(a5)
 
bclr #2,(a5)
 
bset #1,(a5)
 
bset #1,(a5)
 
tst.b (a5)
 
tst.b (a5)
bpl.s locret_724E4
+
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)
 
cmpi.b #-$20,1(a5)
 
bne.s locret_724E4
 
bne.s locret_724E4
Line 2,886: Line 3,399:
 
SegaPCM: binclude sound\segapcm.bin
 
SegaPCM: binclude sound\segapcm.bin
 
align 2
 
align 2
</asm>
+
</syntaxhighlight>
 +
and replace it completely with:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
  
and replace it completely with:
 
<asm>
 
 
align $8000
 
align $8000
DriverData:
+
; ---------------------------------------------------------------------------
dc.b $F3,$F3,$ED,$56,$C3,$89,$00,$00
+
; ===========================================================================
dc.b $2A,$15
+
; ║                                                                        ║
dc.b $00,$06,$00,$09,$08,$7E
+
; ║                            SONIC&K SOUND DRIVER                        ║
dc.b $23,$66,$6F,$08,$C9
+
; ║                        Modified SMPS Z80 Type 2 DAC                    ║
dc.w ((($1300&$1FFF)>>$08)|(($1300&$1FFF)<<$08))&$FFFF
+
; ║                                                                        ║
dc.b $00,$4F,$06,$00,$09,$09,$00,$00,$00
+
; ===========================================================================
dc.b $7E,$23,$66,$6F,$C9,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+
; Disassembled by MarkeyJester
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$F3,$F5,$FD,$E5,$D9,$ED,$5F,$32
+
; Routines, pointers and stuff by Linncaki
dc.b $17,$1C,$CD,$21,$01,$3A,$02,$1C,$B7,$28,$12,$3A,$04,$1C,$B7,$20
+
; Thoroughly commented and improved (including optional bugfixes) by Flamewing
dc.b $08,$3E,$06,$32,$04,$1C,$C3,$3D,$00,$3D,$32,$04,$1C,$AF,$32,$21
+
; ===========================================================================
dc.b $1C,$3A,$30,$1C,$E6,$7F,$4F,$06,$00,$21,$DC,$00,$09,$7E
+
; Constants
Z80_0x006E:
+
; ===========================================================================
dc.b $C3,$4C,$11
 
dc.b $77,$0F,$77,$0F,$77,$0F,$77,$0F,$77,$0F,$77,$0F,$77,$0F,$77
 
dc.b $AF,$77
 
Z80_0x0082:
 
dc.b $D9,$FD,$E1,$F1,$06,$01,$C9,$31,$00,$20,$0E,$00,$06,$00
 
dc.b $10,$FE,$0D,$28,$F9,$CD,$29,$09,$3E,$80,$3E,$06,$32,$3E,$1C,$AF
 
dc.b $32,$27,$1C,$32,$30,$1C,$32,$3F,$1C,$32,$28,$1C,$3E,$05,$32,$04
 
dc.b $1C,$FB,$C3,$62,$10,$DD,$CB,$01,$7E,$C0,$DD,$CB,$00,$56,$C0,$DD
 
dc.b $86,$01,$DD,$CB,$01,$56,$20,$09,$32,$00,$40,$00,$79,$32,$01,$40
 
dc.b $C9,$D6,$04,$32,$02,$40,$00,$79,$32,$03,$40,$C9
 
Z80_0x00DC:
 
Dac_Sample_Selector:
 
dc.b ((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F))
 
dc.b ((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F))
 
dc.b ((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F))
 
dc.b ((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F))
 
dc.b ((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F))
 
dc.b ((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F))
 
dc.b ((DacBank0>>$F)),((DacBank0>>$F)),((DacBank0>>$F)),((DacBank1>>$F))
 
dc.b ((DacBank1>>$F)),((DacBank1>>$F)),((DacBank1>>$F)),((DacBank1>>$F))
 
dc.b ((DacBank1>>$F)),((DacBank1>>$F)),((DacBank1>>$F)),((DacBank1>>$F))
 
dc.b ((DacBank1>>$F)),((DacBank1>>$F)),((DacBank1>>$F)),((DacBank1>>$F))
 
dc.b ((DacBank1>>$F)),((DacBank1>>$F)),((DacBank1>>$F)),((DacBank2>>$F))
 
dc.b ((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F))
 
dc.b ((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F))
 
dc.b ((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F))
 
dc.b ((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F))
 
dc.b ((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F))
 
dc.b ((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F)),((DacBank2>>$F))
 
dc.b ((DacBank2>>$F))
 
dc.b $CD,$E8,$07,$CD,$A2,$01,$CD,$B1,$09,$CD,$62,$08,$CD,$C6,$08
 
dc.b $3A,$16,$1C,$FE,$29,$20,$18,$3A,$0A,$1C,$FE,$2A,$28,$04,$FE,$32
 
dc.b $38,$04,$AF,$32,$0A,$1C,$AF,$32,$0B,$1C,$32,$0C,$1C,$18,$1F,$3A
 
dc.b $16,$1C,$FE,$FF,$28,$18,$21,$0A,$1C,$5E,$23,$56,$23,$7E,$B2,$B3
 
dc.b $28,$0C,$CD,$C7,$09,$CD,$DF,$04,$CD,$DF,$04,$CD,$DF,$04
 
Z80_0x016E:
 
dc.b $3A,$3E,$1C
 
dc.b $FE,$00
 
dc.b $C2
 
dc.w (((Z80DefaultBankSwitch&$1FFF)>>$08)|((Z80DefaultBankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $CD
 
dc.w (((Z80BankSwitch0&$1FFF)>>$08)|((Z80BankSwitch0&$1FFF)<<$08))&$FFFF
 
dc.b $18,$03
 
Z80DefaultBankSwitch:
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00
 
Z80_0x0183:
 
dc.b $AF,$32,$19,$1C,$3A,$16,$1C,$FE,$FF,$CC,$05,$0A,$DD
 
dc.b $21,$40,$1C,$DD,$CB,$00,$7E,$C4,$7A,$0B,$06,$08,$DD,$21,$70,$1C
 
dc.b $18,$1A,$3E,$01,$32,$19,$1C
 
Z80_0x01A7:
 
dc.b $3E, ((SndBank>>$0F))
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 
dc.b $DD,$21,$F0,$1D,$06,$07,$C5,$DD,$CB,$00
 
dc.b $7E,$C4,$E6,$01,$11,$30,$00,$DD,$19,$C1,$10,$F0,$3A,$08,$1C,$B7
 
dc.b $C8,$3A,$2F,$1C,$B7,$C2,$E1,$01,$3A,$08,$1C,$32,$2F,$1C,$C3,$27
 
dc.b $01,$3D,$32,$2F,$1C,$C9,$DD,$CB,$01,$7E,$C2,$9C,$0F,$CD,$37,$03
 
dc.b $20,$17,$CD,$74,$02,$DD,$CB,$00,$66,$C0,$CD,$9B,$03,$CD,$6C,$04
 
dc.b $CD,$C6,$03,$CD,$28,$02,$C3,$3F,$03,$DD,$CB,$00,$66,$C0,$CD,$6A
 
dc.b $03,$DD,$7E,$1E,$B7,$28,$06,$DD,$35,$1E,$CA,$58,$03,$CD,$6C,$04
 
dc.b $DD,$CB,$00,$76,$C0,$CD,$C6,$03,$DD,$CB,$00,$56,$C0,$DD,$CB,$00
 
dc.b $46,$C2,$41,$02,$3E,$A4,$4C,$CD,$B5,$00,$3E,$A0,$4D,$CD,$B5,$00
 
dc.b $C9,$DD,$7E,$01,$FE,$02,$20,$EC,$06,$04,$21,$6F,$02,$C5,$7E,$23
 
dc.b $E5,$EB,$4E,$23,$46,$23,$EB,$DD,$6E,$0D,$DD,$66,$0E,$09,$F5,$4C
 
dc.b $CD,$C8,$00,$F1,$D6,$04,$4D,$CD,$C8,$00,$E1,$C1,$10,$DF,$C9,$AD
 
dc.b $AE,$AC,$A6,$C9,$DD,$5E,$03,$DD,$56,$04,$DD,$CB,$00,$8E,$DD,$CB
 
dc.b $00,$A6,$1A,$13,$FE,$E0,$D2,$CF,$0B,$08,$CD,$58,$03,$08,$DD,$CB
 
dc.b $00,$5E,$C2,$E5,$02,$B7,$F2,$05,$03,$D6,$81,$F2,$A3,$02,$CD,$44
 
dc.b $10,$18,$2E,$DD,$86,$05,$21,$88,$0A,$F5,$DF,$F1,$DD,$CB,$01,$7E
 
dc.b $20,$19,$D5,$16,$08,$1E,$0C,$08,$AF,$08,$93,$38,$05,$08,$82,$18
 
dc.b $F8,$08,$83,$21,$30,$0B,$DF,$08,$B4,$67,$D1,$DD,$75,$0D,$DD,$74
 
dc.b $0E,$1A,$B7,$F2,$04,$03,$DD,$7E,$0C,$DD,$77,$0B,$18,$2D,$1A,$13
 
dc.b $DD,$77,$11,$18,$1E,$67,$1A,$13,$6F,$B4,$28,$0C,$DD,$7E,$05,$06
 
dc.b $00,$B7,$F2,$F6,$02,$05,$4F,$09,$DD,$75,$0D,$DD,$74,$0E,$1A,$13
 
dc.b $DD,$77,$11,$1A,$13,$CD,$2D,$03,$DD,$77,$0C,$DD,$73,$03,$DD,$72
 
dc.b $04,$DD,$7E,$0C,$DD,$77,$0B,$DD,$CB,$00,$4E,$C0,$AF,$DD,$77,$25
 
dc.b $DD,$77,$22,$DD,$77,$17,$DD,$7E,$1F,$DD,$77,$1E,$C9,$DD,$46,$02
 
dc.b $05,$C8,$4F,$81,$10,$FD,$C9,$DD,$7E,$0B,$3D,$DD,$77,$0B,$C9,$DD
 
dc.b $7E,$0D,$DD,$B6,$0E,$C8,$DD,$7E,$00,$E6,$06,$C0,$DD,$7E,$01,$F6
 
dc.b $F0,$4F,$3E,$28,$CD,$C8,$00,$C9,$DD,$7E,$00,$E6,$06,$C0,$DD,$4E
 
dc.b $01,$CB,$79,$C0,$3E,$28,$CD,$C8,$00,$C9,$DD,$7E,$18,$B7,$C8,$F8
 
dc.b $3D,$0E,$0A,$CF,$DF,$CD,$12,$10,$DD,$66,$1D,$DD,$6E,$1C,$11,$AE
 
dc.b $04,$06,$04,$DD,$4E,$19,$F5,$CB,$29,$C5,$30,$08,$86,$E6,$7F,$4F
 
dc.b $1A,$CD,$B5,$00,$C1,$13,$23,$F1,$10,$EC,$C9,$DD,$CB,$07,$7E,$C8
 
dc.b $DD,$CB,$00,$4E,$C0,$DD,$5E,$20,$DD,$56,$21,$DD,$E5,$E1,$06,$00
 
dc.b $0E,$24,$09,$EB,$ED,$A0,$ED,$A0,$ED,$A0,$7E,$CB,$3F,$12,$AF,$DD
 
dc.b $77,$22,$DD,$77,$23,$C9,$DD,$7E,$07,$B7,$C8,$FE,$80,$20,$48,$DD
 
dc.b $35,$24,$C0,$DD,$34,$24,$E5,$DD,$6E,$22,$DD,$66,$23,$DD,$5E,$20
 
dc.b $DD,$56,$21,$D5,$FD,$E1,$DD,$35,$25,$20,$17,$FD,$7E,$01,$DD,$77
 
dc.b $25,$DD,$7E,$26,$4F,$E6,$80,$07,$ED,$44,$47,$09,$DD,$75,$22,$DD
 
dc.b $74,$23,$C1,$09,$DD,$35,$27,$C0,$FD,$7E,$03,$DD,$77,$27,$DD,$7E
 
dc.b $26,$ED,$44,$DD,$77,$26,$C9,$3D,$EB,$0E,$08,$CF,$DF,$18,$03,$DD
 
dc.b $77,$25,$E5,$DD,$4E,$25,$06,$00,$09,$7E,$E1,$CB,$7F,$CA,$5D,$04
 
dc.b $FE,$82,$28,$12,$FE,$80,$28,$12,$FE,$84,$28,$11,$26,$FF,$30,$1F
 
dc.b $DD,$CB,$00,$F6,$E1,$C9,$03,$0A,$18,$D5,$AF,$18,$D2,$03,$0A,$DD
 
dc.b $86,$22,$DD,$77,$22,$DD,$34,$25,$DD,$34,$25,$18,$C5,$26,$00,$6F
 
dc.b $DD,$46,$22,$04,$EB,$19,$10,$FD,$DD,$34,$25,$C9,$DD,$66,$0E,$DD
 
dc.b $6E,$0D,$06,$00,$DD,$7E,$10,$B7,$F2,$7D,$04,$06,$FF,$4F,$09,$C9
 
dc.b $2A,$37,$1C,$3A,$19,$1C,$B7,$28,$06,$DD,$6E,$2A,$DD,$66,$2B,$AF
 
dc.b $B0,$C8,$11,$19,$00,$19,$10,$FD,$C9,$B0,$30,$38,$34,$3C,$50,$58
 
dc.b $54,$5C,$60,$68,$64,$6C,$70,$78,$74,$7C,$80,$88,$84,$8C,$40,$48
 
dc.b $44,$4C,$90,$98,$94,$9C,$11,$99,$04,$DD,$4E,$0A,$3E,$B4,$CD,$B5
 
dc.b $00,$CD,$D7,$04,$DD,$77,$1B,$06,$14,$CD,$D7,$04,$10,$FB,$DD,$75
 
dc.b $1C,$DD,$74,$1D,$C3,$9C,$0C,$1A,$13,$4E,$23,$CD,$B5,$00,$C9,$3A
 
dc.b $05,$1C,$32,$09,$1C,$3A,$06,$1C,$32,$05,$1C,$3A,$07,$1C,$32,$06
 
dc.b $1C,$AF,$32,$07,$1C,$3A,$09,$1C,$FE,$FF,$CA,$FB,$09,$FE,$33,$DA
 
dc.b $4A,$05,$FE,$E0,$DA,$99,$06,$FE,$E1,$DA,$29,$09,$FE,$E6,$D2,$29
 
dc.b $09,$D6,$E1,$21,$1C,$05,$DF,$AF,$32,$18,$1C,$E9,$45,$08,$29,$09
 
dc.b $A1,$09,$26,$05,$45,$08,$DD,$21,$F0,$1D,$06,$07,$3E,$01,$32,$19
 
dc.b $1C,$C5,$DD,$CB,$00,$7E,$C4,$45,$05,$11,$30,$00,$DD,$19,$C1,$10
 
dc.b $F0,$CD,$80,$06,$C9,$E5,$E5,$C3,$61,$0C,$D6,$01,$F8,$F5,$FE,$29
 
dc.b $C2,$CD,$05,$3A,$29,$1C,$B7,$CA,$72,$05,$AF,$32,$0A,$1C,$32,$0B
 
dc.b $1C,$32,$0C,$1C,$32,$05,$1C,$32,$06,$1C,$32,$07,$1C,$32,$09,$1C
 
dc.b $F1,$C9,$3A,$16,$1C,$FE,$29,$CA,$D0,$05,$AF,$32,$0A,$1C,$32,$0B
 
dc.b $1C,$32,$0C,$1C,$32,$05,$1C,$32,$06,$1C,$32,$07,$1C,$3A,$3E,$1C
 
dc.b $32,$2D,$1C,$3A,$08,$1C,$32,$2E,$1C,$AF,$32,$08,$1C,$21,$40,$1C
 
dc.b $11,$F0,$1D,$01,$B0,$01,$ED,$B0,$21,$F0,$1D,$11,$30,$00,$06,$09
 
dc.b $7E,$E6,$7F,$CB,$D6,$77,$19,$10,$F7,$3E,$29,$32,$16,$1C,$3A,$24
 
dc.b $1C,$32,$2C,$1C,$2A,$37,$1C,$22,$2A,$1C,$C3,$D0,$05,$CD,$29,$09
 
dc.b $F1,$F5
 
Z80_0x05D2:
 
dc.b $21,$48,$0B
 
dc.b $85,$6F,$8C,$95,$67,$22,$DE,$05,$3A,$48,$0B
 
dc.b $32,$3E,$1C
 
Z80_0x05E3:
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 
dc.b $3E,$B6,$32,$02,$40,$00,$3E,$C0,$32,$03,$40
 
dc.b $F1,$0E,$04,$CF,$DF,$E5,$E5,$E7,$22,$37,$1C,$E1,$FD,$E1,$FD,$7E
 
dc.b $05,$32,$13,$1C,$32,$24,$1C,$11,$06,$00,$19,$22,$33,$1C,$21,$85
 
dc.b $06,$22,$35,$1C,$11,$40,$1C,$FD,$46,$02,$FD,$7E,$04,$C5,$2A,$35
 
dc.b $1C,$ED,$A0,$ED,$A0,$12,$13,$22,$35,$1C,$2A,$33,$1C,$ED,$A0,$ED
 
dc.b $A0,$ED,$A0,$ED,$A0,$22,$33,$1C,$CD,$AE,$07,$C1,$10,$DF,$FD,$7E
 
dc.b $03,$B7,$CA,$80,$06,$47,$21,$93,$06,$22,$35,$1C,$11,$60,$1D,$FD
 
dc.b $7E,$04,$C5,$2A,$35,$1C,$ED,$A0,$ED,$A0,$12,$13,$22,$35,$1C,$2A
 
dc.b $33,$1C,$01,$06,$00,$ED,$B0,$22,$33,$1C,$CD,$B5,$07,$C1,$10,$E2
 
dc.b $AF,$32,$09,$1C,$C9,$80,$06,$80,$00,$80,$01,$80,$02,$80,$04,$80
 
dc.b $05,$80,$06,$80,$80,$80,$A0,$80,$C0,$D6,$33,$B7,$C2,$A7,$06,$3A
 
dc.b $28,$1C,$EE,$01,$32,$28,$1C,$08
 
Z80_0x06A8:
 
dc.b $3E, ((SndBank>>$0F))
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 
Z80_0x06B7:
 
dc.b $AF,$0E,$06,$32,$19,$1C,$08,$FE,$78
 
dc.b $CA,$F5,$06,$FE,$89,$DA,$EF,$06,$F5,$47,$3A,$25,$1C,$90,$C2,$E4
 
dc.b $06,$3E,$80,$32,$26,$1C,$CF,$F1,$4F,$DF,$23,$23,$23,$7E,$32,$31
 
dc.b $1C,$C3,$80,$06,$AF,$32,$26,$1C,$F1,$32,$25,$1C,$C3,$F5,$06,$F5
 
dc.b $AF,$32,$27,$1C,$F1,$CF,$DF,$E5,$E7,$22,$39,$1C,$AF,$32,$15,$1C
 
dc.b $E1,$E5,$FD,$E1,$FD,$7E,$02,$32,$3B,$1C,$11,$04,$00,$19,$FD,$46
 
dc.b $03,$78,$32,$31,$1C,$C5,$E5,$23,$4E,$CD,$78,$07,$CB,$D6,$DD,$E5
 
dc.b $3A,$19,$1C,$B7,$28,$03,$E1,$FD,$E5,$D1,$E1,$ED,$A0,$1A,$FE,$02
 
dc.b $CC,$5E,$09,$ED,$A0,$3A,$3B,$1C,$12,$13,$ED,$A0,$ED,$A0,$ED,$A0
 
dc.b $ED,$A0,$CD,$AE,$07,$DD,$CB,$00,$7E,$28,$0C,$DD,$7E,$01,$FD,$BE
 
dc.b $01,$20,$04,$FD,$CB,$00,$D6,$E5,$2A,$39,$1C,$3A,$19,$1C,$B7,$28
 
dc.b $04,$FD,$E5,$DD,$E1,$DD,$75,$2A,$DD,$74,$2B,$CD,$58,$03,$CD,$6B
 
dc.b $09,$E1,$C1,$10,$A0,$C3,$80,$06,$CB,$79,$20,$08,$79,$CB,$57,$28
 
dc.b $1A,$3D,$18,$17,$3E,$1F,$CD,$4D,$10,$3E,$FF,$32,$11,$7F,$79,$CB
 
dc.b $3F,$CB,$3F,$CB,$3F,$CB,$3F,$CB,$3F,$C6,$02,$D6,$02,$32,$32,$1C
 
dc.b $F5,$21,$C8,$07,$DF,$E5,$DD,$E1,$F1,$21,$D8,$07,$DF,$C9,$08,$AF
 
dc.b $12,$13,$12,$13,$08,$EB,$36,$30,$23,$36,$C0,$23,$36,$01,$06,$24
 
dc.b $23,$36,$00,$10,$FB,$23,$EB,$C9,$F0,$1D,$20,$1E,$50,$1E,$80,$1E
 
dc.b $B0,$1E,$E0,$1E,$10,$1F,$10,$1F,$D0,$1C,$00,$1D,$30,$1D,$40,$1C
 
dc.b $60,$1D,$90,$1D,$C0,$1D,$C0,$1D,$21,$10,$1C,$7E,$B7,$C8,$FA,$F9
 
dc.b $07,$D1,$3D,$C0,$36,$02,$C3,$72,$09,$AF,$77,$3A,$0D,$1C,$B7,$C2
 
dc.b $29,$09,$DD,$21,$70,$1C,$06,$06,$3A,$11,$1C,$B7,$20,$06,$DD,$CB
 
dc.b $00,$7E,$28,$08,$DD,$4E,$0A,$3E,$B4,$CD,$B5,$00,$11,$30,$00,$DD
 
dc.b $19,$10,$E5,$DD,$21,$40,$1F,$06,$07,$DD,$CB,$00,$7E,$28,$0E,$DD
 
dc.b $CB,$01,$7E,$20,$08,$DD,$4E,$0A,$3E,$B4,$CD,$B5,$00,$11,$30,$00
 
dc.b $DD,$19,$10,$E5,$C9,$3E,$28,$32,$0D,$1C,$3E,$06,$32,$0F,$1C,$32
 
dc.b $0E,$1C,$AF,$32,$40,$1C,$32,$C0,$1D,$32,$60,$1D,$32,$90,$1D,$C3
 
dc.b $A1,$09,$21,$0D,$1C,$7E,$B7,$C8,$FC,$52,$08,$CB,$BE,$3A,$0F,$1C
 
dc.b $3D,$28,$04,$32,$0F,$1C,$C9,$3A,$0E,$1C,$32,$0F,$1C,$3A,$0D,$1C
 
dc.b $3D,$32,$0D,$1C,$CA,$29,$09,$3A,$3E,$1C
 
Z80_0x088A:
 
dc.b $21,$00,$60
 
dc.b $77,$1F,$77,$1F,$77,$1F,$77,$AF,$16,$01,$72,$77,$77,$77,$77
 
Z80_0x089C:
 
dc.b $DD,$21,$40,$1C
 
dc.b $06,$06,$DD,$34,$06,$F2,$AD,$08,$DD,$35,$06,$18,$11,$DD,$CB,$00
 
dc.b $7E,$28,$0B,$DD,$CB,$00,$56,$20,$05,$C5,$CD,$9C,$0C,$C1,$11,$30
 
dc.b $00,$DD,$19,$10,$DD,$C9,$3A,$29,$1C,$B7,$C8,$3A,$3E,$1C
 
Z80_0x08CE:
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 
Z80_0x08E0:
 
dc.b $3A,$0E,$1C,$3D,$32,$0E,$1C,$C0,$3A,$0F,$1C,$32,$0E,$1C,$06,$05
 
dc.b $DD,$21,$70,$1C,$11,$30,$00,$DD,$7E,$06,$3D,$DD,$77,$06,$C5,$CD
 
dc.b $9C,$0C,$C1,$DD,$19,$10,$F0,$3A,$29,$1C,$3D,$32,$29,$1C,$C0,$06
 
dc.b $03,$DD,$21,$60,$1D,$11,$30,$00,$DD,$CB,$00,$96,$DD,$19,$10,$F8
 
dc.b $DD,$21,$40,$1C,$DD,$CB,$00,$96,$C9,$21,$0D,$1C,$11,$0E,$1C,$01
 
dc.b $C6,$03,$36,$00,$ED,$B0,$AF,$32,$08,$1C,$DD,$21,$85,$06,$06,$06
 
dc.b $C5,$CD,$DB,$09,$CD,$6B,$09,$DD,$23,$DD,$23,$C1,$10,$F2,$06,$07
 
dc.b $AF,$32,$0D,$1C,$CD,$A1,$09,$0E,$00,$3E,$2B,$CD,$C8,$00,$AF,$32
 
dc.b $12,$1C,$4F,$3E,$27,$CD,$C8,$00,$C3,$80,$06,$3E,$90,$0E,$00,$C3
 
dc.b $EF,$09,$CD,$A1,$09,$C5,$F5,$06,$03,$3E,$B4,$0E,$00,$F5,$CD,$C8
 
dc.b $00,$F1,$3C,$10,$F8,$06,$02,$3E,$B4,$F5,$CD,$D3,$00,$F1,$3C,$10
 
dc.b $F8,$0E,$00,$06,$06,$3E,$28,$F5,$CD,$C8,$00,$0C,$F1,$10,$F8,$F1
 
dc.b $C1,$C5,$06,$04,$3E,$9F,$32,$11,$7F,$C6,$20,$10,$F9,$C1,$C3,$80
 
dc.b $06,$3A,$24,$1C,$21,$13,$1C,$86,$77,$D0,$21,$4B,$1C,$11,$30,$00
 
dc.b $06,$09,$34,$19,$10,$FC,$C9,$21,$0A,$1C,$11,$05,$1C,$ED,$A0,$ED
 
dc.b $A0,$ED,$A0,$AF,$2B,$77,$2B,$77,$2B,$77,$C9,$CD,$EB,$09,$3E,$40
 
dc.b $0E,$7F,$CD,$EF,$09,$DD,$4E,$01,$C3,$64,$03,$3E,$80,$0E,$FF,$06
 
dc.b $04,$F5,$CD,$B5,$00,$F1,$C6,$04,$10,$F7,$C9,$CD,$29,$09,$3E,$01
 
dc.b $32,$3F,$1C,$E1,$C9,$AF,$32,$16,$1C,$3A,$2C,$1C,$32,$24,$1C,$3A
 
dc.b $2E,$1C,$32,$08,$1C,$2A,$2A,$1C,$22,$37,$1C
 
Z80_0x0A1B:
 
dc.b $3A,$2D,$1C,$32,$3E,$1C
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 
Z80_0x0A33:
 
dc.b $21,$F0,$1D,$11,$40,$1C,$01,$B0,$01,$ED,$B0,$3A,$40
 
dc.b $1C,$F6,$84,$32,$40,$1C,$DD,$21,$70,$1C,$06,$08,$DD,$7E,$00,$F6
 
dc.b $84,$DD,$77,$00,$DD,$CB,$01,$7E,$C2,$73,$0A,$DD,$CB,$00,$96,$DD
 
dc.b $7E,$06,$C6,$40,$DD,$77,$06,$DD,$7E,$08,$C5,$47,$CD,$80,$04,$CD
 
dc.b $B6,$04,$C1,$11,$30,$00,$DD,$19,$10,$D2,$3E,$40,$32,$29,$1C,$3E
 
dc.b $02,$32,$0F,$1C,$32,$0E,$1C,$C9,$FF,$03,$FF,$03,$FF,$03,$FF,$03
 
dc.b $FF,$03,$FF,$03,$FF,$03,$FF,$03,$FF,$03,$F7,$03,$BE,$03,$88,$03
 
dc.b $56,$03,$26,$03,$F9,$02,$CE,$02,$A5,$02,$80,$02,$5C,$02,$3A,$02
 
dc.b $1A,$02,$FB,$01,$DF,$01,$C4,$01,$AB,$01,$93,$01,$7D,$01,$67,$01
 
dc.b $53,$01,$40,$01,$2E,$01,$1D,$01,$0D,$01,$FE,$00,$EF,$00,$E2,$00
 
dc.b $D6,$00,$C9,$00,$BE,$00,$B4,$00,$A9,$00,$A0,$00,$97,$00,$8F,$00
 
dc.b $87,$00,$7F,$00,$78,$00,$71,$00,$6B,$00,$65,$00,$5F,$00,$5A,$00
 
dc.b $55,$00,$50,$00,$4B,$00,$47,$00,$43,$00,$40,$00,$3C,$00,$39,$00
 
dc.b $36,$00,$33,$00,$30,$00,$2D,$00,$2B,$00,$28,$00,$26,$00,$24,$00
 
dc.b $22,$00,$20,$00,$1F,$00,$1D,$00,$1B,$00,$1A,$00,$18,$00,$17,$00
 
dc.b $16,$00,$15,$00,$13,$00,$12,$00,$11,$00,$10,$00,$00,$00,$00,$00
 
dc.b $84,$02,$AB,$02,$D3,$02,$FE,$02,$2D,$03,$5C,$03,$8F,$03,$C5,$03
 
dc.b $FF,$03,$3C,$04,$7C,$04,$C0,$04
 
BankSelector:
 
dc.b ((Bank1>>$0F)),((Bank1>>$0F)),((Bank1>>$0F)),((Bank1>>$0F))
 
dc.b ((Bank1>>$0F)),((Bank1>>$0F)),((Bank1>>$0F)),((Bank1>>$0F))
 
dc.b ((Bank1>>$0F)),((Bank1>>$0F)),((Bank2>>$0F)),((Bank2>>$0F))
 
dc.b ((Bank2>>$0F)),((Bank2>>$0F)),((Bank2>>$0F)),((Bank2>>$0F))
 
dc.b ((Bank2>>$0F)),((Bank2>>$0F)),((Bank2>>$0F)),((Bank2>>$0F))
 
dc.b ((Bank2>>$0F)),((Bank2>>$0F)),((Bank2>>$0F)),((Bank2>>$0F))
 
dc.b ((Bank2>>$0F)),((Bank1>>$0F)),((Bank2>>$0F)),((Bank2>>$0F))
 
dc.b ((Bank3>>$0F)),((Bank3>>$0F)),((Bank3>>$0F)),((Bank3>>$0F))
 
dc.b ((Bank3>>$0F)),((Bank3>>$0F)),((Bank3>>$0F)),((Bank3>>$0F))
 
dc.b ((Bank3>>$0F)),((Bank3>>$0F)),((Bank3>>$0F)),((Bank3>>$0F))
 
dc.b ((Bank2>>$0F)),((Bank3>>$0F)),((Bank3>>$0F)),((Bank3>>$0F))
 
dc.b ((Bank3>>$0F)),((Bank0>>$0F)),((Bank2>>$0F)),((Bank0>>$0F))
 
dc.b ((Bank3>>$0F)),((Bank3>>$0F))
 
dc.b $CD,$37,$03,$C0,$DD,$5E
 
dc.b $03,$DD,$56,$04,$1A,$13,$FE,$E0,$D2,$C5,$0B,$B7,$FA,$93,$0B,$1B
 
dc.b $DD,$7E,$0D,$DD,$77,$0D,$FE,$80,$CA,$B5,$0B,$CB,$BF,$D5,$08,$CD
 
dc.b $58,$03,$CD,$5E,$09,$08,$DD,$21,$40,$1C,$DD,$CB,$00,$56,$C2,$B4
 
dc.b $0B,$32,$30,$1C,$D1,$1A,$13,$B7,$F2,$05,$03,$1B,$DD,$7E,$0C,$DD
 
dc.b $77,$0B,$C3,$0B,$03,$21,$CB,$0B,$C3,$D2,$0B,$13,$C3,$84,$0B,$21
 
dc.b $DB,$0B,$E5,$D6,$E0,$21,$DF,$0B,$DF,$1A,$E9,$13,$C3,$82,$02,$33
 
dc.b $0C,$59,$0C,$5D,$0C,$61,$0C,$67,$0C,$83,$0C,$85,$0C,$BD,$0C,$C3
 
dc.b $0C,$47,$0C,$2F,$0C,$CD,$0C,$E3,$0C,$FD,$0C,$03,$0D,$10,$0D,$4F
 
dc.b $0D,$5D,$0D,$69,$0D,$11,$0E,$65,$0D,$30,$0E,$39,$0E,$3F,$0E,$56
 
dc.b $0E,$70,$0E,$83,$0E,$89,$0E,$90,$0E,$B2,$0E,$C0,$0E,$07,$0F,$0E
 
dc.b $0F,$12,$0F,$1A,$0F,$55,$0F,$63,$0F,$72,$0F,$8D,$0F,$96,$0F,$32
 
dc.b $30,$1C,$C9,$0E,$3F,$DD,$7E,$0A,$A1,$D5,$EB,$B6,$DD,$77,$0A,$4F
 
dc.b $3E,$B4,$CD,$B5,$00,$D1,$C9,$21,$27,$1C,$7E,$DD,$86,$05,$DD,$77
 
dc.b $05,$FE,$10,$CA,$57,$0C,$34,$1B,$C9,$DD,$77,$10,$C9,$32,$16,$1C
 
dc.b $C9,$CD,$DB,$09,$C3,$69,$0D,$DD,$CB,$01,$7E,$28,$0D,$CB,$3F,$CB
 
dc.b $3F,$CB,$3F,$EE,$0F,$E6,$0F,$C3,$F9,$0C,$EE,$7F,$E6,$7F,$DD,$77
 
dc.b $06,$18,$19,$13,$1A,$DD,$CB,$01,$7E,$C0,$DD,$86,$06,$F2,$99,$0C
 
dc.b $EA,$97,$0C,$AF,$C3,$99,$0C,$3E,$7F,$DD,$77,$06,$D5,$11,$AE,$04
 
dc.b $DD,$6E,$1C,$DD,$66,$1D,$06,$04,$7E,$B7,$F2,$B0,$0C,$DD,$86,$06
 
dc.b $E6,$7F,$4F,$1A,$CD,$B5,$00,$13,$23,$10,$ED,$D1,$C9,$DD,$CB,$00
 
dc.b $CE,$1B,$C9,$CD,$2D,$03,$DD,$77,$1E,$DD,$77,$1F,$C9,$13,$C6,$28
 
dc.b $4F,$06,$00,$DD,$E5,$E1,$09,$7E,$3D,$CA,$DE,$0C,$13,$C9,$AF,$77
 
dc.b $C3,$39,$0E,$DD,$CB,$01,$7E,$C8,$DD,$CB,$00,$A6,$DD,$35,$17,$DD
 
dc.b $86,$06,$FE,$0F,$DA,$F9,$0C,$3E,$0F,$DD,$77,$06,$C9,$D6,$40,$DD
 
dc.b $77,$05,$C9,$CD,$0A,$0D,$CD,$C8,$00,$C9,$EB,$7E,$23,$4E,$EB,$C9
 
dc.b $DD,$CB,$01,$7E,$20,$30,$CD,$EB,$09,$1A,$DD,$77,$08,$B7,$F2,$3C
 
dc.b $0D,$13,$1A,$DD,$77,$0F,$D5,$DD,$7E,$0F,$D6,$81,$0E,$04,$CF,$DF
 
dc.b $E7,$DD,$7E,$08,$E6,$7F,$47,$CD,$8F,$04,$18,$05,$D5,$47,$CD,$80
 
dc.b $04,$CD,$B6,$04,$D1,$C9,$B7,$F2,$35,$0E,$13,$C3,$35,$0E,$C9,$DD
 
dc.b $73,$20,$DD,$72,$21,$DD,$36,$07,$80,$13,$13,$13,$C9,$13,$DD,$CB
 
dc.b $01,$7E,$20,$01,$1A,$DD,$77,$07,$C9,$DD,$CB,$00,$BE,$3E,$1F,$32
 
dc.b $15,$1C,$CD,$58,$03,$DD,$4E,$01,$DD,$E5,$CD,$78,$07,$3A,$19,$1C
 
dc.b $B7,$28,$77,$AF,$32,$18,$1C,$E5,$2A,$37,$1C,$DD,$E1,$DD,$CB,$00
 
dc.b $96,$DD,$CB,$01,$7E,$20,$68,$DD,$CB,$00,$7E,$28,$5D,$3E,$02,$DD
 
dc.b $BE,$01,$20,$0D,$3E,$4F,$DD,$CB,$00,$46,$20,$02,$E6,$0F,$CD,$E9
 
dc.b $0E,$DD,$7E,$08,$B7,$F2,$BD,$0D,$CD,$26,$0D,$18,$3A,$47,$E5
 
Z80_0x0DBF:
 
dc.b $3A,$3E,$1C
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 
Z80_0x0DD4:
 
dc.b $E1,$CD,$8F,$04,$CD,$B6,$04
 
Z80_0x0DDB:
 
dc.b $3E,((SndBank>>$0F))
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 
dc.b $DD,$7E,$18,$B7,$F2,$FA
 
dc.b $0D,$DD,$5E,$19,$DD,$56,$1A,$CD,$7C,$0F,$DD,$E1,$E1,$E1,$C9,$DD
 
dc.b $CB,$00,$46,$28,$F5,$DD,$7E,$1A,$B7,$F2,$0F,$0E,$32,$11,$7F,$18
 
dc.b $E9,$DD,$CB,$01,$56,$C0,$3E,$DF,$32,$11,$7F,$1A,$DD,$77,$1A,$DD
 
dc.b $CB,$00,$C6,$B7,$20,$06,$DD,$CB,$00,$86,$3E,$FF,$32,$11,$7F,$C9
 
dc.b $DD,$CB,$01,$7E,$C8,$DD,$77,$08,$C9,$EB,$5E,$23,$56,$1B,$C9,$13
 
dc.b $C6,$28,$4F,$06,$00,$DD,$E5,$E1,$09,$7E,$B7,$20,$02,$1A,$77,$13
 
dc.b $35,$C2,$39,$0E,$13,$C9,$4F,$13,$1A,$47,$C5,$DD,$E5,$E1,$DD,$35
 
dc.b $09,$DD,$4E,$09,$DD,$35,$09,$06,$00,$09,$72,$2B,$73,$D1,$1B,$C9
 
dc.b $DD,$E5,$E1,$DD,$4E,$09,$06,$00,$09,$5E,$23,$56,$DD,$34,$09,$DD
 
dc.b $34,$09,$C9,$DD,$CB,$07,$BE,$1B,$C9,$DD,$86,$05,$DD,$77,$05,$C9
 
dc.b $3A,$26,$1C,$FE,$80,$CA,$A1,$0E,$AF,$32,$25,$1C,$32,$26,$1C,$13
 
dc.b $C9,$3A,$31,$1C,$3D,$32,$31,$1C,$C2,$39,$0E,$AF,$32,$26,$1C,$C3
 
dc.b $39,$0E,$FE,$01,$20,$05,$DD,$CB,$00,$DE,$C9,$DD,$CB,$00,$9E,$C9
 
dc.b $DD,$7E,$01,$FE,$02,$20,$2C,$DD,$CB,$00,$C6,$EB,$CD,$73,$02,$06
 
dc.b $04,$C5,$7E,$23,$E5,$21,$F7,$0E,$87,$4F,$06,$00,$09,$ED,$A0,$ED
 
dc.b $A0,$E1,$C1,$10,$EC,$EB,$1B,$3E,$4F,$32,$12,$1C,$4F,$3E,$27,$CD
 
dc.b $C8,$00,$C9,$13,$13,$13,$C9,$00,$00,$32,$01,$8E,$01,$E4,$01,$34
 
dc.b $02,$7E,$02,$C2,$02,$F0,$02,$21,$1F,$0C,$DF,$13,$1A,$E9,$32,$24
 
dc.b $1C,$C9,$DD,$E5,$CD,$F8,$04,$DD,$E1,$C9,$32,$11,$1C,$B7,$28,$1D
 
dc.b $DD,$E5,$D5,$DD,$21,$40,$1C,$06,$09,$11,$30,$00,$DD,$CB,$00,$BE
 
dc.b $CD,$5E,$03,$DD,$19,$10,$F5,$D1,$DD,$E1,$C3,$A1,$09,$DD,$E5,$D5
 
dc.b $DD,$21,$40,$1C,$06,$09,$11,$30,$00,$DD,$CB,$00,$FE,$DD,$19,$10
 
dc.b $F8,$D1,$DD,$E1,$C9,$EB,$5E,$23,$56,$23,$4E,$06,$00,$23,$EB,$ED
 
dc.b $B0,$1B,$C9,$06,$09,$21,$42,$1C,$C5,$01,$30,$00,$77,$09,$C1,$10
 
dc.b $F7,$C9,$DD,$36,$18,$80,$DD,$73,$19,$DD,$72,$1A,$21,$B2,$04,$06
 
dc.b $04,$1A,$13,$4F,$7E,$23,$CD,$B5,$00,$10,$F6,$1B,$C9,$DD,$77,$18
 
dc.b $13,$1A,$DD,$77,$19,$C9,$AF,$32,$27,$1C,$1B,$C9,$CD,$37,$03,$20
 
dc.b $0D,$CD,$74,$02,$DD,$CB,$00,$66,$C0,$CD,$9B,$03,$18,$0C,$DD,$7E
 
dc.b $1E,$B7,$28,$06,$DD,$35,$1E,$CA,$44,$10,$CD,$6C,$04,$CD,$C6,$03
 
dc.b $DD,$CB,$00,$56,$C0,$DD,$4E,$01,$7D,$E6,$0F,$B1,$32,$11,$7F,$7D
 
dc.b $E6,$F0,$B4,$0F,$0F,$0F,$0F,$32,$11,$7F,$DD,$7E,$08,$B7,$0E,$00
 
dc.b $28,$09,$3D,$0E,$0A,$CF,$DF,$CD,$12,$10,$4F,$DD,$CB,$00,$66,$C0
 
dc.b $DD,$7E,$06,$81,$CB,$67,$28,$02,$3E,$0F,$DD,$B6,$01,$C6,$10,$DD
 
dc.b $CB,$00,$46,$20,$04,$32,$11,$7F,$C9,$C6,$20,$32,$11,$7F,$C9,$DD
 
dc.b $77,$17,$E5,$DD,$4E,$17,$06,$00,$09,$7E,$E1,$CB,$7F,$28,$21,$FE
 
dc.b $83,$28,$0C,$FE,$81,$28,$13,$FE,$80,$28,$0C,$03,$0A,$18,$E0,$DD
 
dc.b $CB,$00,$E6,$E1,$C3,$44,$10,$AF,$18,$D5,$E1,$DD,$CB,$00,$E6,$C9
 
dc.b $DD,$34,$17,$C9,$DD,$CB,$00,$E6,$DD,$CB,$00,$56,$C0,$3E,$1F,$DD
 
dc.b $86,$01,$B7,$F0,$32,$11,$7F,$DD,$CB,$00,$46,$C8,$3E,$FF,$32,$11
 
dc.b $7F,$C9,$F3,$3E,$2B,$0E,$00,$CD,$C8,$00,$FB,$3A,$3F,$1C,$B7,$C2
 
dc.b $FE,$10,$3A,$30,$1C,$B7,$28,$F2,$3E,$2B,$0E,$80,$F3,$CD,$C8,$00
 
dc.b $FB,$FD,$21,$EE,$10,$21,$30,$1C,$7E,$3D,$CB,$FE,$21,$00,$80,$DF
 
dc.b $0E,$80,$7E,$32,$A3,$10,$32,$C0,$10,$23,$5E,$23,$56,$23,$7E,$23
 
dc.b $66,$6F,$06,$0A,$FB,$10,$FE,$F3,$3E,$2A,$32,$00,$40,$7E,$07,$07
 
dc.b $07,$07,$E6,$0F,$32,$BA,$10,$79,$FD,$86,$00,$32,$01,$40,$4F,$06
 
dc.b $0A,$FB,$10,$FE,$F3,$3E,$2A,$32,$00,$40,$7E,$E6,$0F,$32,$D3,$10
 
dc.b $79,$FD,$86,$00,$32,$01,$40,$FB,$4F,$3A,$30,$1C,$B7,$F2,$6A,$10
 
dc.b $23,$1B,$7A,$B3,$C2,$A2,$10,$AF,$32,$30,$1C,$C3,$62,$10,$00,$01
 
dc.b $02,$04,$08,$10,$20,$40,$80,$FF,$FE,$FC,$F8,$F0,$E0,$C0,$F3,$CD
 
dc.b $C7,$09,$3E,$2B,$32,$00,$40,$00,$3E,$80,$32,$01,$40
 
Z80_0x110D:
 
dc.b $3E,((SegaPCMBank>>$0F))
 
dc.b $CD
 
dc.w (((Z80BankSwitch&$1FFF)>>$08)|((Z80BankSwitch&$1FFF)<<$08))&$FFFF
 
dc.b $00,$00,$00,$00,$00,$00,$00,$00,$00
 
dc.b $21
 
dc.w (((SegaSnd&$FFFF)|$8000>>$08)|((SegaSnd&$FFFF)|$8000<<$08))&$FFFF
 
dc.b $11,$2F,$5E
 
dc.b $3E,$2A,$32,$00,$40,$00,$7E,$32,$01,$40,$3A,$0A,$1C,$FE,$FE
 
dc.b $28,$0C,$00,$00,$06,$0C,$10,$FE,$23,$1B,$7A,$B3,$20,$E9,$AF,$32
 
dc.b $3F,$1C,$21,$0D,$1C,$CD,$D3,$09,$C3,$62,$10,$FF
 
Z80_0x114C:
 
dc.b $21,$00,$60
 
dc.b $77,$0F,$77,$0F,$77,$0F,$77,$0F,$77,$0F,$77,$0F,$77,$0F,$77
 
dc.b $AF,$77
 
dc.b $C3,$82,$00
 
Z80BankSwitch0:
 
Z80_0x1170:
 
dc.b $21,$00,$60
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $AF
 
dc.b $16,$01
 
dc.b $72
 
dc.b $77
 
dc.b $77
 
dc.b $77
 
dc.b $77
 
dc.b $C9
 
Z80BankSwitch:
 
dc.b $21, $00, $60
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $1F
 
dc.b $77
 
dc.b $C9
 
DriverDataEnd:
 
;-------------------------------------------------------------------------------
 
; Filler to align pointers at 1300h in Z80 Ram
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
dc.b 0,0,0,0,0
 
DriverPointers:
 
dc.w (((MusicPointers&$1FFF)>>$08)|((MusicPointers&$1FFF)<<$08))&$FFFF
 
dc.w (((UniversalVoiceBank&$1FFF)>>$08)|((UniversalVoiceBank&$1FFF)<<$08))&$FFFF
 
dc.w (((MusicPointers&$1FFF)>>$08)|((MusicPointers&$1FFF)<<$08))&$FFFF
 
dc.w (((SndPointers&$1FFF)>>$08)|((SndPointers&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGNoisePointers&$1FFF)>>$08)|((PSGNoisePointers&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGTonePointers&$1FFF)>>$08)|((PSGTonePointers&$1FFF)<<$08))&$FFFF
 
dc.w $3300
 
  
;credit goes to Varion Icaria for finding this index
+
; Set this to 1 to fix some bugs in the driver.
PSGNoisePointers:
 
dc.w (((PSGN_0&$1FFF)>>$08)|((PSGN_0&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGN_1&$1FFF)>>$08)|((PSGN_1&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGN_2&$1FFF)>>$08)|((PSGN_2&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGN_3&$1FFF)>>$08)|((PSGN_3&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGN_4&$1FFF)>>$08)|((PSGN_4&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGN_5&$1FFF)>>$08)|((PSGN_5&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGN_6&$1FFF)>>$08)|((PSGN_6&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGN_7&$1FFF)>>$08)|((PSGN_7&$1FFF)<<$08))&$FFFF
 
  
PSGN_1: binclude sound/PSGN1.bin
+
fix_sndbugs =  0
PSGN_0: binclude sound/PSGN0.bin
 
PSGN_2: binclude sound/PSGN2.bin
 
PSGN_3: binclude sound/PSGN3.bin
 
PSGN_4: binclude sound/PSGN4.bin
 
PSGN_5: binclude sound/PSGN5.bin
 
PSGN_6: binclude sound/PSGN6.bin
 
PSGN_7: binclude sound/PSGN7.bin
 
  
;credit goes to Varion Icaria for finding this index
+
; Function to make a little endian (z80) pointer
PSGTonePointers:
+
k68z80Pointer function addr,((((addr&$7FFF)+$8000)<<8)&$FF00)+(((addr&$7FFF)+$8000)>>8)
dc.w (((PSGT_00&$1FFF)>>$08)|((PSGT_00&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_01&$1FFF)>>$08)|((PSGT_01&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_02&$1FFF)>>$08)|((PSGT_02&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_03&$1FFF)>>$08)|((PSGT_03&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_04&$1FFF)>>$08)|((PSGT_04&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_05&$1FFF)>>$08)|((PSGT_05&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_06&$1FFF)>>$08)|((PSGT_06&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_07&$1FFF)>>$08)|((PSGT_07&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_08&$1FFF)>>$08)|((PSGT_08&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_09&$1FFF)>>$08)|((PSGT_09&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_0A&$1FFF)>>$08)|((PSGT_0A&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_0B&$1FFF)>>$08)|((PSGT_0B&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_0C&$1FFF)>>$08)|((PSGT_0C&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_0D&$1FFF)>>$08)|((PSGT_0D&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_0E&$1FFF)>>$08)|((PSGT_0E&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_0F&$1FFF)>>$08)|((PSGT_0F&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_10&$1FFF)>>$08)|((PSGT_10&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_11&$1FFF)>>$08)|((PSGT_11&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_12&$1FFF)>>$08)|((PSGT_12&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_13&$1FFF)>>$08)|((PSGT_13&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_14&$1FFF)>>$08)|((PSGT_14&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_15&$1FFF)>>$08)|((PSGT_15&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_16&$1FFF)>>$08)|((PSGT_16&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_17&$1FFF)>>$08)|((PSGT_17&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_18&$1FFF)>>$08)|((PSGT_18&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_19&$1FFF)>>$08)|((PSGT_19&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_1A&$1FFF)>>$08)|((PSGT_1A&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_1B&$1FFF)>>$08)|((PSGT_1B&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_1C&$1FFF)>>$08)|((PSGT_1C&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_1D&$1FFF)>>$08)|((PSGT_1D&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_1E&$1FFF)>>$08)|((PSGT_1E&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_1F&$1FFF)>>$08)|((PSGT_1F&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_20&$1FFF)>>$08)|((PSGT_20&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_21&$1FFF)>>$08)|((PSGT_21&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_22&$1FFF)>>$08)|((PSGT_22&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_23&$1FFF)>>$08)|((PSGT_23&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_24&$1FFF)>>$08)|((PSGT_24&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_25&$1FFF)>>$08)|((PSGT_25&$1FFF)<<$08))&$FFFF
 
dc.w (((PSGT_26&$1FFF)>>$08)|((PSGT_26&$1FFF)<<$08))&$FFFF
 
  
PSGT_00: binclude sound/PSGT00.bin
+
little_endian function x,(x)<<8&$FF00|(x)>>8&$FF
PSGT_01: binclude sound/PSGT01.bin
 
PSGT_02: binclude sound/PSGT02.bin
 
PSGT_03: binclude sound/PSGT03.bin
 
PSGT_04: binclude sound/PSGT04.bin
 
PSGT_05: binclude sound/PSGT05.bin
 
PSGT_06: binclude sound/PSGT06.bin
 
PSGT_07: binclude sound/PSGT07.bin
 
PSGT_08: binclude sound/PSGT08.bin
 
PSGT_09: binclude sound/PSGT09.bin
 
PSGT_0A: binclude sound/PSGT0A.bin
 
PSGT_0B: binclude sound/PSGT0B.bin
 
PSGT_0C: binclude sound/PSGT0C.bin
 
PSGT_0D: binclude sound/PSGT0D.bin
 
PSGT_0E: binclude sound/PSGT0E.bin
 
PSGT_0F: binclude sound/PSGT0F.bin
 
PSGT_10: binclude sound/PSGT10.bin
 
PSGT_11: binclude sound/PSGT11.bin
 
PSGT_12: binclude sound/PSGT12.bin
 
PSGT_13: binclude sound/PSGT13.bin
 
PSGT_14: binclude sound/PSGT14.bin
 
PSGT_15: binclude sound/PSGT15.bin
 
PSGT_16: binclude sound/PSGT16.bin
 
PSGT_17: binclude sound/PSGT17.bin
 
PSGT_18: binclude sound/PSGT18.bin
 
PSGT_19: binclude sound/PSGT19.bin
 
PSGT_1A: binclude sound/PSGT1A.bin
 
PSGT_1B: binclude sound/PSGT1B.bin
 
PSGT_1C: binclude sound/PSGT1C.bin
 
PSGT_1D: binclude sound/PSGT1D.bin
 
PSGT_1E: binclude sound/PSGT1E.bin
 
PSGT_1F: binclude sound/PSGT1F.bin
 
PSGT_20: binclude sound/PSGT20.bin
 
PSGT_21: binclude sound/PSGT21.bin
 
PSGT_22: binclude sound/PSGT22.bin
 
PSGT_23: binclude sound/PSGT23.bin
 
PSGT_24: binclude sound/PSGT24.bin
 
PSGT_25: binclude sound/PSGT25.bin
 
PSGT_26: binclude sound/PSGT26.bin
 
  
MB_Ptr equ (Mini_Boss_Snd&$FFFF)|$8000 ; $C71A0
+
startBank macro {INTLABEL}
FB_Ptr equ (Final_Boss_Snd&$FFFF)|$8000 ; $C7A61
+
align $8000
AIz1_Ptr equ (Angel_Island_1_Snd&$FFFF)|$8000 ; $C8000
+
__LABEL__ label *
AIz2_Ptr equ (Angel_Island_2_Snd&$FFFF)|$8000 ; $C9B6D
+
soundBankStart := __LABEL__
HCz1_Ptr equ (Hidrocity_1_Snd&$FFFF)|$8000 ; $CB0BC
+
soundBankName := "__LABEL__"
HCz2_Ptr equ (Hidrocity_2_Snd&$FFFF)|$8000 ; $CC0C6
+
    endm
MGz1_Ptr equ (Marble_Garden_1_Snd&$FFFF)|$8000 ; $CD364
 
MGz2_Ptr equ (Marble_Garden_2_Snd&$FFFF)|$8000 ; $CD97B
 
CNz2_Ptr equ (Carnival_Night_2_Snd&$FFFF)|$8000 ; $CDDA9
 
CNz1_Ptr equ (Carnival_Night_1_Snd&$FFFF)|$8000 ; $CE48F
 
FBz1_Ptr equ (Flying_Battery_1_Snd&$FFFF)|$8000 ; $CEBF1
 
FBz2_Ptr equ (Flying_Battery_2_Snd&$FFFF)|$8000 ; $CF189
 
TDz_Ptr equ (The_Doomsday_Snd&$FFFF)|$8000 ; $CF6F0
 
Iz2_Ptr equ (Icecap_2_Snd&$FFFF)|$8000 ; $D0000
 
Iz1_Ptr equ (Icecap_1_Snd&$FFFF)|$8000 ; $D06AA
 
LBz2_Ptr equ (Launch_Base_2_Snd&$FFFF)|$8000 ; $D0DC8
 
LBz1_Ptr equ (Launch_Base_1_Snd&$FFFF)|$8000 ; $D1345
 
MHz1_Ptr equ (Mushroom_Hill_1_Snd&$FFFF)|$8000 ; $D17A7
 
MHz2_Ptr equ (Mushroom_Hill_2_Snd&$FFFF)|$8000 ; $D1DAF
 
Sz1_Ptr equ (Sandopolis_1_Snd&$FFFF)|$8000 ; $D2331
 
Sz2_Ptr equ (Sandopolis_2_Snd&$FFFF)|$8000 ; $D299B
 
LRz1_Ptr equ (Lava_Reef_1_Snd&$FFFF)|$8000 ; $D2F8E
 
LRz2_Ptr equ (Lava_Reef_2_Snd&$FFFF)|$8000 ; $D399C
 
SCz_Ptr equ (Sky_Sanctuary_Snd&$FFFF)|$8000 ; $D4B29
 
DEz1_Ptr equ (Death_Egg_1_Snd&$FFFF)|$8000 ; $D4F4F
 
DEz2_Ptr equ (Death_Egg_2_Snd&$FFFF)|$8000 ; $D543A
 
MB_SK_Ptr equ (Mini_Boss_SK_Snd&$FFFF)|$8000 ; $D584C
 
Boss_Ptr equ (Boss_Snd&$FFFF)|$8000 ; $D5B7C
 
GS_BS_Ptr equ (Glowing_Spheres_Bonus_Stage_snd&$FFFF)|$8000 ; $D6078
 
SS_Ptr equ (Special_Stage_Snd&$FFFF)|$8000 ; $D65DB
 
LR_Ptr equ (Level_Results_Snd&$FFFF)|$8000 ; $D6E73
 
Menu_Ptr equ (Menu_Snd&$FFFF)|$8000 ; $D7027
 
SM_BS_Ptr equ (Slot_Machine_Bonus_Stage_snd&$FFFF)|$8000 ; $D8000
 
GBM_BS_Ptr equ (Gum_Ball_Machine_Bonus_Stage_snd&$FFFF)|$8000 ; $D8AE8
 
KTE_Ptr equ (Knuckles_Theme_Snd&$FFFF)|$8000 ; $D97FD
 
ALz_Ptr equ (Azure_Lake_Snd&$FFFF)|$8000 ; $D99F7
 
BPz_Ptr equ (Balloon_Park_Snd&$FFFF)|$8000 ; $DA4FD
 
DPz_Ptr equ (Desert_Palace_Snd&$FFFF)|$8000 ; $DB0EC
 
CGz_Ptr equ (Chrome_Gadget_Snd&$FFFF)|$8000 ; $DC324
 
EMz_Ptr equ (Endless_Mine_Snd&$FFFF)|$8000 ; $DDA47
 
TS_Ptr equ (Title_Screen_Snd&$FFFF)|$8000 ; $DE18F
 
Credits_Ptr equ (Credits_Snd&$FFFF)|$8000 ; $DE587
 
TGOvr_Ptr equ (Time_Game_Over_Snd&$FFFF)|$8000 ; $DEA20
 
Continue_Ptr equ (Continue_Snd&$FFFF)|$8000 ; $DEC7B
 
_1_Up_Ptr equ (Extra_Life_Snd&$FFFF)|$8000 ; $DF095
 
Emerald_Ptr equ (Emerald_Snd&$FFFF)|$8000 ; $DF298
 
Invcblty_Ptr equ (Invencibility_Snd&$FFFF)|$8000 ; $DF364
 
_2p_Menu_Ptr equ (Competition_Menu_Snd&$FFFF)|$8000 ; $DF5E4
 
UT_Ptr equ (Underwater_Timming_Snd&$FFFF)|$8000 ; $DFABE
 
PbS_Ptr equ (Presented_by_SEGA_Snd&$FFFF)|$8000 ; $DFBFE
 
  
Sfx_34_Ptr equ (Sfx_34_Snd&$FFFF)|$8000 ; $FDE30
+
DebugSoundbanks := 0
Sfx_35_Ptr equ (Sfx_35_Snd&$FFFF)|$8000 ; $FDE5E
 
Sfx_36_Ptr equ (Sfx_36_Snd&$FFFF)|$8000 ; $FDE6F
 
Sfx_37_Ptr equ (Sfx_37_Snd&$FFFF)|$8000 ; $FDEA1
 
Sfx_38_Ptr equ (Sfx_38_Snd&$FFFF)|$8000 ; $FDEC5
 
Sfx_39_Ptr equ (Sfx_39_Snd&$FFFF)|$8000 ; $FDEF4
 
Sfx_3A_Ptr equ (Sfx_3A_Snd&$FFFF)|$8000 ; $FDF2A
 
Sfx_3B_Ptr equ (Sfx_3B_Snd&$FFFF)|$8000 ; $FDF6B
 
Sfx_3C_Ptr equ (Sfx_3C_Snd&$FFFF)|$8000 ; $FDF96
 
Sfx_3D_Ptr equ (Sfx_3D_Snd&$FFFF)|$8000 ; $FDFE5
 
Sfx_3E_Ptr equ (Sfx_3E_Snd&$FFFF)|$8000 ; $FE023
 
Sfx_3F_Ptr equ (Sfx_3F_Snd&$FFFF)|$8000 ; $FE05D
 
Sfx_40_Ptr equ (Sfx_40_Snd&$FFFF)|$8000 ; $FE088
 
Sfx_41_Ptr equ (Sfx_41_Snd&$FFFF)|$8000 ; $FE0AB
 
Sfx_42_Ptr equ (Sfx_42_Snd&$FFFF)|$8000 ; $FE0CE
 
Sfx_43_Ptr equ (Sfx_43_Snd&$FFFF)|$8000 ; $FE0F1
 
Sfx_44_Ptr equ (Sfx_44_Snd&$FFFF)|$8000 ; $FE109
 
Sfx_45_Ptr equ (Sfx_45_Snd&$FFFF)|$8000 ; $FE122
 
Sfx_46_Ptr equ (Sfx_46_Snd&$FFFF)|$8000 ; $FE14F
 
Sfx_47_Ptr equ (Sfx_47_Snd&$FFFF)|$8000 ; $FE177
 
Sfx_48_Ptr equ (Sfx_48_Snd&$FFFF)|$8000 ; $FE1A4
 
Sfx_49_Ptr equ (Sfx_49_Snd&$FFFF)|$8000 ; $FE1C4
 
Sfx_4A_Ptr equ (Sfx_4A_Snd&$FFFF)|$8000 ; $FE1DE
 
Sfx_4B_Ptr equ (Sfx_4B_Snd&$FFFF)|$8000 ; $FE206
 
Sfx_4C_Ptr equ (Sfx_4C_Snd&$FFFF)|$8000 ; $FE22E
 
Sfx_4D_Ptr equ (Sfx_4D_Snd&$FFFF)|$8000 ; $FE278
 
Sfx_4E_Ptr equ (Sfx_4E_Snd&$FFFF)|$8000 ; $FE2A2
 
Sfx_4F_Ptr equ (Sfx_4F_Snd&$FFFF)|$8000 ; $FE2CF
 
Sfx_50_Ptr equ (Sfx_50_Snd&$FFFF)|$8000 ; $FE313
 
Sfx_51_Ptr equ (Sfx_51_Snd&$FFFF)|$8000 ; $FE322
 
Sfx_52_Ptr equ (Sfx_52_Snd&$FFFF)|$8000 ; $FE35A
 
Sfx_53_Ptr equ (Sfx_53_Snd&$FFFF)|$8000 ; $FE38B
 
Sfx_54_Ptr equ (Sfx_54_Snd&$FFFF)|$8000 ; $FE3A8
 
Sfx_55_Ptr equ (Sfx_55_Snd&$FFFF)|$8000 ; $FE3E8
 
Sfx_56_Ptr equ (Sfx_56_Snd&$FFFF)|$8000 ; $FE42B
 
Sfx_57_Ptr equ (Sfx_57_Snd&$FFFF)|$8000 ; $FE453
 
Sfx_58_Ptr equ (Sfx_58_Snd&$FFFF)|$8000 ; $FE463
 
Sfx_59_Ptr equ (Sfx_59_Snd&$FFFF)|$8000 ; $FE481
 
Sfx_5A_Ptr equ (Sfx_5A_Snd&$FFFF)|$8000 ; $FE49A
 
Sfx_5B_Ptr equ (Sfx_5B_Snd&$FFFF)|$8000 ; $FE4F6
 
Sfx_5C_Ptr equ (Sfx_5C_Snd&$FFFF)|$8000 ; $FE523
 
Sfx_5D_Ptr equ (Sfx_5D_Snd&$FFFF)|$8000 ; $FE530
 
Sfx_5E_Ptr equ (Sfx_5E_Snd&$FFFF)|$8000 ; $FE558
 
Sfx_5F_Ptr equ (Sfx_5F_Snd&$FFFF)|$8000 ; $FE581
 
Sfx_60_Ptr equ (Sfx_60_Snd&$FFFF)|$8000 ; $FE5B2
 
Sfx_61_Ptr equ (Sfx_61_Snd&$FFFF)|$8000 ; $FE5DA
 
Sfx_62_Ptr equ (Sfx_62_Snd&$FFFF)|$8000 ; $FE61B
 
Sfx_63_Ptr equ (Sfx_63_Snd&$FFFF)|$8000 ; $FE64C
 
Sfx_64_Ptr equ (Sfx_64_Snd&$FFFF)|$8000 ; $FE662
 
Sfx_65_Ptr equ (Sfx_65_Snd&$FFFF)|$8000 ; $FE68C
 
Sfx_66_Ptr equ (Sfx_66_Snd&$FFFF)|$8000 ; $FE6AB
 
Sfx_67_Ptr equ (Sfx_67_Snd&$FFFF)|$8000 ; $FE6E1
 
Sfx_68_Ptr equ (Sfx_68_Snd&$FFFF)|$8000 ; $FE730
 
Sfx_69_Ptr equ (Sfx_69_Snd&$FFFF)|$8000 ; $FE75C
 
Sfx_6A_Ptr equ (Sfx_6A_Snd&$FFFF)|$8000 ; $FE7B0
 
Sfx_6B_Ptr equ (Sfx_6B_Snd&$FFFF)|$8000 ; $FE7DD
 
Sfx_6C_Ptr equ (Sfx_6C_Snd&$FFFF)|$8000 ; $FE811
 
Sfx_6D_Ptr equ (Sfx_6D_Snd&$FFFF)|$8000 ; $FE823
 
Sfx_6E_Ptr equ (Sfx_6E_Snd&$FFFF)|$8000 ; $FE833
 
Sfx_6F_Ptr equ (Sfx_6F_Snd&$FFFF)|$8000 ; $FE852
 
Sfx_70_Ptr equ (Sfx_70_Snd&$FFFF)|$8000 ; $FE886
 
Sfx_71_Ptr equ (Sfx_71_Snd&$FFFF)|$8000 ; $FE896
 
Sfx_72_Ptr equ (Sfx_72_Snd&$FFFF)|$8000 ; $FE8E0
 
Sfx_73_Ptr equ (Sfx_73_Snd&$FFFF)|$8000 ; $FE8EA
 
Sfx_74_Ptr equ (Sfx_74_Snd&$FFFF)|$8000 ; $FE917
 
Sfx_75_Ptr equ (Sfx_75_Snd&$FFFF)|$8000 ; $FE94B
 
Sfx_76_Ptr equ (Sfx_76_Snd&$FFFF)|$8000 ; $FE978
 
Sfx_77_Ptr equ (Sfx_77_Snd&$FFFF)|$8000 ; $FE9A7
 
Sfx_78_Ptr equ (Sfx_78_Snd&$FFFF)|$8000 ; $FE9D1
 
Sfx_79_Ptr equ (Sfx_79_Snd&$FFFF)|$8000 ; $FEA1B
 
Sfx_7A_Ptr equ (Sfx_7A_Snd&$FFFF)|$8000 ; $FEA48
 
Sfx_7B_Ptr equ (Sfx_7B_Snd&$FFFF)|$8000 ; $FEA93
 
Sfx_7C_Ptr equ (Sfx_7C_Snd&$FFFF)|$8000 ; $FEAC7
 
Sfx_7D_Ptr equ (Sfx_7D_Snd&$FFFF)|$8000 ; $FEAF7
 
Sfx_7E_Ptr equ (Sfx_7E_Snd&$FFFF)|$8000 ; $FEB28
 
Sfx_7F_Ptr equ (Sfx_7F_Snd&$FFFF)|$8000 ; $FEB55
 
Sfx_80_Ptr equ (Sfx_80_Snd&$FFFF)|$8000 ; $FEB6D
 
Sfx_81_Ptr equ (Sfx_81_Snd&$FFFF)|$8000 ; $FEB8B
 
Sfx_82_Ptr equ (Sfx_82_Snd&$FFFF)|$8000 ; $FEBBA
 
Sfx_83_Ptr equ (Sfx_83_Snd&$FFFF)|$8000 ; $FEC05
 
Sfx_84_Ptr equ (Sfx_84_Snd&$FFFF)|$8000 ; $FEC32
 
Sfx_85_Ptr equ (Sfx_85_Snd&$FFFF)|$8000 ; $FEC7E
 
Sfx_86_Ptr equ (Sfx_86_Snd&$FFFF)|$8000 ; $FECAB
 
Sfx_87_Ptr equ (Sfx_87_Snd&$FFFF)|$8000 ; $FECD8
 
Sfx_88_Ptr equ (Sfx_88_Snd&$FFFF)|$8000 ; $FED05
 
Sfx_89_Ptr equ (Sfx_89_Snd&$FFFF)|$8000 ; $FED3B
 
Sfx_8A_Ptr equ (Sfx_8A_Snd&$FFFF)|$8000 ; $FED68
 
Sfx_8B_Ptr equ (Sfx_8B_Snd&$FFFF)|$8000 ; $FED75
 
Sfx_8C_Ptr equ (Sfx_8C_Snd&$FFFF)|$8000 ; $FEDA9
 
Sfx_8D_Ptr equ (Sfx_8D_Snd&$FFFF)|$8000 ; $FEDDF
 
Sfx_8E_Ptr equ (Sfx_8E_Snd&$FFFF)|$8000 ; $FEE10
 
Sfx_8F_Ptr equ (Sfx_8F_Snd&$FFFF)|$8000 ; $FEE2A
 
Sfx_90_Ptr equ (Sfx_90_Snd&$FFFF)|$8000 ; $FEE5B
 
Sfx_91_Ptr equ (Sfx_91_Snd&$FFFF)|$8000 ; $FEE91
 
Sfx_92_Ptr equ (Sfx_92_Snd&$FFFF)|$8000 ; $FEEC3
 
Sfx_93_Ptr equ (Sfx_93_Snd&$FFFF)|$8000 ; $FEEF9
 
Sfx_94_Ptr equ (Sfx_94_Snd&$FFFF)|$8000 ; $FEF2D
 
Sfx_95_Ptr equ (Sfx_95_Snd&$FFFF)|$8000 ; $FEF77
 
Sfx_96_Ptr equ (Sfx_96_Snd&$FFFF)|$8000 ; $FEFA6
 
Sfx_97_Ptr equ (Sfx_97_Snd&$FFFF)|$8000 ; $FEFD5
 
Sfx_98_Ptr equ (Sfx_98_Snd&$FFFF)|$8000 ; $FF009
 
Sfx_99_Ptr equ (Sfx_99_Snd&$FFFF)|$8000 ; $FF01C
 
Sfx_9A_Ptr equ (Sfx_9A_Snd&$FFFF)|$8000 ; $FF068
 
Sfx_9B_Ptr equ (Sfx_9B_Snd&$FFFF)|$8000 ; $FF090
 
Sfx_9C_Ptr equ (Sfx_9C_Snd&$FFFF)|$8000 ; $FF0AF
 
Sfx_9D_Ptr equ (Sfx_9D_Snd&$FFFF)|$8000 ; $FF114
 
Sfx_9E_Ptr equ (Sfx_9E_Snd&$FFFF)|$8000 ; $FF14B
 
Sfx_9F_Ptr equ (Sfx_9F_Snd&$FFFF)|$8000 ; $FF17F
 
Sfx_A0_Ptr equ (Sfx_A0_Snd&$FFFF)|$8000 ; $FF1C0
 
Sfx_A1_Ptr equ (Sfx_A1_Snd&$FFFF)|$8000 ; $FF1FC
 
Sfx_A2_Ptr equ (Sfx_A2_Snd&$FFFF)|$8000 ; $FF214
 
Sfx_A3_Ptr equ (Sfx_A3_Snd&$FFFF)|$8000 ; $FF23C
 
Sfx_A4_Ptr equ (Sfx_A4_Snd&$FFFF)|$8000 ; $FF274
 
Sfx_A5_Ptr equ (Sfx_A5_Snd&$FFFF)|$8000 ; $FF2A1
 
Sfx_A6_Ptr equ (Sfx_A6_Snd&$FFFF)|$8000 ; $FF2CE
 
Sfx_A7_Ptr equ (Sfx_A7_Snd&$FFFF)|$8000 ; $FF2FB
 
Sfx_A8_Ptr equ (Sfx_A8_Snd&$FFFF)|$8000 ; $FF313
 
Sfx_A9_Ptr equ (Sfx_A9_Snd&$FFFF)|$8000 ; $FF33B
 
Sfx_AA_Ptr equ (Sfx_AA_Snd&$FFFF)|$8000 ; $FF365
 
Sfx_AB_Ptr equ (Sfx_AB_Snd&$FFFF)|$8000 ; $FF38F
 
Sfx_AC_Ptr equ (Sfx_AC_Snd&$FFFF)|$8000 ; $FF3EA
 
Sfx_AD_Ptr equ (Sfx_AD_Snd&$FFFF)|$8000 ; $FF42A
 
Sfx_AE_Ptr equ (Sfx_AE_Snd&$FFFF)|$8000 ; $FF49C
 
Sfx_AF_Ptr equ (Sfx_AF_Snd&$FFFF)|$8000 ; $FF4AB
 
Sfx_B0_Ptr equ (Sfx_B0_Snd&$FFFF)|$8000 ; $FF4DA
 
Sfx_B1_Ptr equ (Sfx_B1_Snd&$FFFF)|$8000 ; $FF507
 
Sfx_B2_Ptr equ (Sfx_B2_Snd&$FFFF)|$8000 ; $FF582
 
Sfx_B3_Ptr equ (Sfx_B3_Snd&$FFFF)|$8000 ; $FF5D7
 
Sfx_B4_Ptr equ (Sfx_B4_Snd&$FFFF)|$8000 ; $FF603
 
Sfx_B5_Ptr equ (Sfx_B5_Snd&$FFFF)|$8000 ; $FF67D
 
Sfx_B6_Ptr equ (Sfx_B6_Snd&$FFFF)|$8000 ; $FF6AA
 
Sfx_B7_Ptr equ (Sfx_B7_Snd&$FFFF)|$8000 ; $FF6D2
 
Sfx_B8_Ptr equ (Sfx_B8_Snd&$FFFF)|$8000 ; $FF713
 
Sfx_B9_Ptr equ (Sfx_B9_Snd&$FFFF)|$8000 ; $FF745
 
Sfx_BA_Ptr equ (Sfx_BA_Snd&$FFFF)|$8000 ; $FF76C
 
Sfx_BB_Ptr equ (Sfx_BB_Snd&$FFFF)|$8000 ; $FF794
 
Sfx_BC_Ptr equ (Sfx_BC_Snd&$FFFF)|$8000 ; $FF7BE
 
Sfx_BD_Ptr equ (Sfx_BD_Snd&$FFFF)|$8000 ; $FF7CE
 
Sfx_BE_Ptr equ (Sfx_BE_Snd&$FFFF)|$8000 ; $FF7F9
 
Sfx_BF_Ptr equ (Sfx_BF_Snd&$FFFF)|$8000 ; $FF837
 
Sfx_C0_Ptr equ (Sfx_C0_Snd&$FFFF)|$8000 ; $FF86A
 
Sfx_C1_Ptr equ (Sfx_C1_Snd&$FFFF)|$8000 ; $FF89C
 
Sfx_C2_Ptr equ (Sfx_C2_Snd&$FFFF)|$8000 ; $FF8D1
 
Sfx_C3_Ptr equ (Sfx_C3_Snd&$FFFF)|$8000 ; $FF907
 
Sfx_C4_Ptr equ (Sfx_C4_Snd&$FFFF)|$8000 ; $FF91E
 
Sfx_C5_Ptr equ (Sfx_C5_Snd&$FFFF)|$8000 ; $FF94E
 
Sfx_C6_Ptr equ (Sfx_C6_Snd&$FFFF)|$8000 ; $FF97E
 
Sfx_C7_Ptr equ (Sfx_C7_Snd&$FFFF)|$8000 ; $FF9B7
 
Sfx_C8_Ptr equ (Sfx_C8_Snd&$FFFF)|$8000 ; $FF9F2
 
Sfx_C9_Ptr equ (Sfx_C9_Snd&$FFFF)|$8000 ; $FFA21
 
Sfx_CA_Ptr equ (Sfx_CA_Snd&$FFFF)|$8000 ; $FFA2B
 
Sfx_CB_Ptr equ (Sfx_CB_Snd&$FFFF)|$8000 ; $FFA66
 
Sfx_CC_Ptr equ (Sfx_CC_Snd&$FFFF)|$8000 ; $FFA9C
 
Sfx_CD_Ptr equ (Sfx_CD_Snd&$FFFF)|$8000 ; $FFAD7
 
Sfx_CE_Ptr equ (Sfx_CE_Snd&$FFFF)|$8000 ; $FFB12
 
Sfx_CF_Ptr equ (Sfx_CF_Snd&$FFFF)|$8000 ; $FFB45
 
Sfx_D0_Ptr equ (Sfx_D0_Snd&$FFFF)|$8000 ; $FFB60
 
Sfx_D1_Ptr equ (Sfx_D1_Snd&$FFFF)|$8000 ; $FFB6A
 
Sfx_D2_Ptr equ (Sfx_D2_Snd&$FFFF)|$8000 ; $FFBA1
 
Sfx_D3_Ptr equ (Sfx_D3_Snd&$FFFF)|$8000 ; $FFBBE
 
Sfx_D4_Ptr equ (Sfx_D4_Snd&$FFFF)|$8000 ; $FFBF4
 
Sfx_D5_Ptr equ (Sfx_D5_Snd&$FFFF)|$8000 ; $FFC2D
 
Sfx_D6_Ptr equ (Sfx_D6_Snd&$FFFF)|$8000 ; $FFC64
 
Sfx_D7_Ptr equ (Sfx_D7_Snd&$FFFF)|$8000 ; $FFC9D
 
Sfx_D8_Ptr equ (Sfx_D8_Snd&$FFFF)|$8000 ; $FFCCE
 
Sfx_D9_Ptr equ (Sfx_D9_Snd&$FFFF)|$8000 ; $FFCFF
 
Sfx_DA_Ptr equ (Sfx_DA_Snd&$FFFF)|$8000 ; $FFD32
 
Sfx_DB_Ptr equ (Sfx_DB_Snd&$FFFF)|$8000 ; $FFD62
 
Sfx_DC_Ptr equ (Sfx_DC_Snd&$FFFF)|$8000 ; $FFD94
 
  
MusicPointers:
+
finishBank macro
dc.w ((AIz1_Ptr>>$08)|(AIz1_Ptr<<$08))&$FFFF
+
if * > soundBankStart + $8000
dc.w ((AIz2_Ptr>>$08)|(AIz2_Ptr<<$08))&$FFFF
+
fatal "soundBank \{soundBankName} must fit in $8000 bytes but was $\{*-soundBankStart}. Try moving something to the other bank."
dc.w ((HCz1_Ptr>>$08)|(HCz1_Ptr<<$08))&$FFFF
+
elseif (DebugSoundbanks<>0)&&(MOMPASS=1)
dc.w ((HCz2_Ptr>>$08)|(HCz2_Ptr<<$08))&$FFFF
+
message "soundBank \{soundBankName} has $\{$8000+soundBankStart-*} bytes free at end."
dc.w ((MGz1_Ptr>>$08)|(MGz1_Ptr<<$08))&$FFFF
+
endif
dc.w ((MGz2_Ptr>>$08)|(MGz2_Ptr<<$08))&$FFFF
+
    endm
dc.w ((CNz1_Ptr>>$08)|(CNz1_Ptr<<$08))&$FFFF
 
dc.w ((CNz2_Ptr>>$08)|(CNz2_Ptr<<$08))&$FFFF
 
dc.w ((FBz1_Ptr>>$08)|(FBz1_Ptr<<$08))&$FFFF
 
dc.w ((FBz2_Ptr>>$08)|(FBz2_Ptr<<$08))&$FFFF
 
dc.w ((Iz1_Ptr>>$08)|(Iz1_Ptr<<$08))&$FFFF
 
dc.w ((Iz2_Ptr>>$08)|(Iz2_Ptr<<$08))&$FFFF
 
dc.w ((LBz1_Ptr>>$08)|(LBz1_Ptr<<$08))&$FFFF
 
dc.w ((LBz2_Ptr>>$08)|(LBz2_Ptr<<$08))&$FFFF
 
dc.w ((MHz1_Ptr>>$08)|(MHz1_Ptr<<$08))&$FFFF
 
dc.w ((MHz2_Ptr>>$08)|(MHz2_Ptr<<$08))&$FFFF
 
dc.w ((Sz1_Ptr>>$08)|(Sz1_Ptr<<$08))&$FFFF
 
dc.w ((Sz2_Ptr>>$08)|(Sz2_Ptr<<$08))&$FFFF
 
dc.w ((LRz1_Ptr>>$08)|(LRz1_Ptr<<$08))&$FFFF
 
dc.w ((LRz2_Ptr>>$08)|(LRz2_Ptr<<$08))&$FFFF
 
dc.w ((SCz_Ptr>>$08)|(SCz_Ptr<<$08))&$FFFF
 
dc.w ((DEz1_Ptr>>$08)|(DEz1_Ptr<<$08))&$FFFF
 
dc.w ((DEz2_Ptr>>$08)|(DEz2_Ptr<<$08))&$FFFF
 
dc.w ((MB_SK_Ptr>>$08)|(MB_SK_Ptr<<$08))&$FFFF
 
dc.w ((Boss_Ptr>>$08)|(Boss_Ptr<<$08))&$FFFF
 
dc.w ((TDz_Ptr>>$08)|(TDz_Ptr<<$08))&$FFFF
 
dc.w ((GS_BS_Ptr>>$08)|(GS_BS_Ptr<<$08))&$FFFF
 
dc.w ((SS_Ptr>>$08)|(SS_Ptr<<$08))&$FFFF
 
dc.w ((SM_BS_Ptr>>$08)|(SM_BS_Ptr<<$08))&$FFFF
 
dc.w ((GBM_BS_Ptr>>$08)|(GBM_BS_Ptr<<$08))&$FFFF
 
dc.w ((KTE_Ptr>>$08)|(KTE_Ptr<<$08))&$FFFF
 
dc.w ((ALz_Ptr>>$08)|(ALz_Ptr<<$08))&$FFFF
 
dc.w ((BPz_Ptr>>$08)|(BPz_Ptr<<$08))&$FFFF
 
dc.w ((DPz_Ptr>>$08)|(DPz_Ptr<<$08))&$FFFF
 
dc.w ((CGz_Ptr>>$08)|(CGz_Ptr<<$08))&$FFFF
 
dc.w ((EMz_Ptr>>$08)|(EMz_Ptr<<$08))&$FFFF
 
dc.w ((TS_Ptr>>$08)|(TS_Ptr<<$08))&$FFFF
 
dc.w ((Credits_Ptr>>$08)|(Credits_Ptr<<$08))&$FFFF
 
dc.w ((TGOvr_Ptr>>$08)|(TGOvr_Ptr<<$08))&$FFFF
 
dc.w ((Continue_Ptr>>$08)|(Continue_Ptr<<$08))&$FFFF
 
dc.w ((LR_Ptr>>$08)|(LR_Ptr<<$08))&$FFFF
 
dc.w ((_1_Up_Ptr>>$08)|(_1_Up_Ptr<<$08))&$FFFF
 
dc.w ((Emerald_Ptr>>$08)|(Emerald_Ptr<<$08))&$FFFF
 
dc.w ((Invcblty_Ptr>>$08)|(Invcblty_Ptr<<$08))&$FFFF
 
dc.w ((_2p_Menu_Ptr>>$08)|(_2p_Menu_Ptr<<$08))&$FFFF
 
dc.w ((MB_Ptr>>$08)|(MB_Ptr<<$08))&$FFFF
 
dc.w ((Menu_Ptr>>$08)|(Menu_Ptr<<$08))&$FFFF
 
dc.w ((FB_Ptr>>$08)|(FB_Ptr<<$08))&$FFFF
 
dc.w ((UT_Ptr>>$08)|(UT_Ptr<<$08))&$FFFF
 
dc.w ((PbS_Ptr>>$08)|(PbS_Ptr<<$08))&$FFFF
 
  
SndPointers:
+
; macro to declare an entry in an offset table rooted at a bank
dc.w ((Sfx_34_Ptr>>$08)|(Sfx_34_Ptr<<$08))&$FFFF
+
offsetBankTableEntry macro ptr
dc.w ((Sfx_35_Ptr>>$08)|(Sfx_35_Ptr<<$08))&$FFFF
+
dc.ATTRIBUTE k68z80Pointer(ptr-soundBankStart)
dc.w ((Sfx_36_Ptr>>$08)|(Sfx_36_Ptr<<$08))&$FFFF
+
    endm
dc.w ((Sfx_37_Ptr>>$08)|(Sfx_37_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_38_Ptr>>$08)|(Sfx_38_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_39_Ptr>>$08)|(Sfx_39_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_3A_Ptr>>$08)|(Sfx_3A_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_3B_Ptr>>$08)|(Sfx_3B_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_3C_Ptr>>$08)|(Sfx_3C_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_3D_Ptr>>$08)|(Sfx_3D_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_3E_Ptr>>$08)|(Sfx_3E_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_3F_Ptr>>$08)|(Sfx_3F_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_40_Ptr>>$08)|(Sfx_40_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_41_Ptr>>$08)|(Sfx_41_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_42_Ptr>>$08)|(Sfx_42_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_43_Ptr>>$08)|(Sfx_43_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_44_Ptr>>$08)|(Sfx_44_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_45_Ptr>>$08)|(Sfx_45_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_46_Ptr>>$08)|(Sfx_46_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_47_Ptr>>$08)|(Sfx_47_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_48_Ptr>>$08)|(Sfx_48_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_49_Ptr>>$08)|(Sfx_49_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_4A_Ptr>>$08)|(Sfx_4A_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_4B_Ptr>>$08)|(Sfx_4B_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_4C_Ptr>>$08)|(Sfx_4C_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_4D_Ptr>>$08)|(Sfx_4D_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_4E_Ptr>>$08)|(Sfx_4E_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_4F_Ptr>>$08)|(Sfx_4F_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_50_Ptr>>$08)|(Sfx_50_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_51_Ptr>>$08)|(Sfx_51_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_52_Ptr>>$08)|(Sfx_52_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_53_Ptr>>$08)|(Sfx_53_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_54_Ptr>>$08)|(Sfx_54_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_55_Ptr>>$08)|(Sfx_55_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_56_Ptr>>$08)|(Sfx_56_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_57_Ptr>>$08)|(Sfx_57_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_58_Ptr>>$08)|(Sfx_58_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_59_Ptr>>$08)|(Sfx_59_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_5A_Ptr>>$08)|(Sfx_5A_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_5B_Ptr>>$08)|(Sfx_5B_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_5C_Ptr>>$08)|(Sfx_5C_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_5D_Ptr>>$08)|(Sfx_5D_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_5E_Ptr>>$08)|(Sfx_5E_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_5F_Ptr>>$08)|(Sfx_5F_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_60_Ptr>>$08)|(Sfx_60_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_61_Ptr>>$08)|(Sfx_61_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_62_Ptr>>$08)|(Sfx_62_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_63_Ptr>>$08)|(Sfx_63_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_64_Ptr>>$08)|(Sfx_64_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_65_Ptr>>$08)|(Sfx_65_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_66_Ptr>>$08)|(Sfx_66_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_67_Ptr>>$08)|(Sfx_67_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_68_Ptr>>$08)|(Sfx_68_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_69_Ptr>>$08)|(Sfx_69_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_6A_Ptr>>$08)|(Sfx_6A_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_6B_Ptr>>$08)|(Sfx_6B_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_6C_Ptr>>$08)|(Sfx_6C_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_6D_Ptr>>$08)|(Sfx_6D_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_6E_Ptr>>$08)|(Sfx_6E_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_6F_Ptr>>$08)|(Sfx_6F_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_70_Ptr>>$08)|(Sfx_70_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_71_Ptr>>$08)|(Sfx_71_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_72_Ptr>>$08)|(Sfx_72_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_73_Ptr>>$08)|(Sfx_73_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_74_Ptr>>$08)|(Sfx_74_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_75_Ptr>>$08)|(Sfx_75_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_76_Ptr>>$08)|(Sfx_76_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_77_Ptr>>$08)|(Sfx_77_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_78_Ptr>>$08)|(Sfx_78_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_79_Ptr>>$08)|(Sfx_79_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_7A_Ptr>>$08)|(Sfx_7A_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_7B_Ptr>>$08)|(Sfx_7B_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_7C_Ptr>>$08)|(Sfx_7C_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_7D_Ptr>>$08)|(Sfx_7D_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_7E_Ptr>>$08)|(Sfx_7E_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_7F_Ptr>>$08)|(Sfx_7F_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_80_Ptr>>$08)|(Sfx_80_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_81_Ptr>>$08)|(Sfx_81_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_82_Ptr>>$08)|(Sfx_82_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_83_Ptr>>$08)|(Sfx_83_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_84_Ptr>>$08)|(Sfx_84_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_85_Ptr>>$08)|(Sfx_85_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_86_Ptr>>$08)|(Sfx_86_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_87_Ptr>>$08)|(Sfx_87_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_88_Ptr>>$08)|(Sfx_88_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_89_Ptr>>$08)|(Sfx_89_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_8A_Ptr>>$08)|(Sfx_8A_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_8B_Ptr>>$08)|(Sfx_8B_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_8C_Ptr>>$08)|(Sfx_8C_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_8D_Ptr>>$08)|(Sfx_8D_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_8E_Ptr>>$08)|(Sfx_8E_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_8F_Ptr>>$08)|(Sfx_8F_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_90_Ptr>>$08)|(Sfx_90_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_91_Ptr>>$08)|(Sfx_91_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_92_Ptr>>$08)|(Sfx_92_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_93_Ptr>>$08)|(Sfx_93_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_94_Ptr>>$08)|(Sfx_94_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_95_Ptr>>$08)|(Sfx_95_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_96_Ptr>>$08)|(Sfx_96_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_97_Ptr>>$08)|(Sfx_97_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_98_Ptr>>$08)|(Sfx_98_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_99_Ptr>>$08)|(Sfx_99_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_9A_Ptr>>$08)|(Sfx_9A_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_9B_Ptr>>$08)|(Sfx_9B_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_9C_Ptr>>$08)|(Sfx_9C_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_9D_Ptr>>$08)|(Sfx_9D_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_9E_Ptr>>$08)|(Sfx_9E_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_9F_Ptr>>$08)|(Sfx_9F_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A0_Ptr>>$08)|(Sfx_A0_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A1_Ptr>>$08)|(Sfx_A1_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A2_Ptr>>$08)|(Sfx_A2_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A3_Ptr>>$08)|(Sfx_A3_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A4_Ptr>>$08)|(Sfx_A4_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A5_Ptr>>$08)|(Sfx_A5_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A6_Ptr>>$08)|(Sfx_A6_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A7_Ptr>>$08)|(Sfx_A7_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A8_Ptr>>$08)|(Sfx_A8_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_A9_Ptr>>$08)|(Sfx_A9_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_AA_Ptr>>$08)|(Sfx_AA_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_AB_Ptr>>$08)|(Sfx_AB_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_AC_Ptr>>$08)|(Sfx_AC_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_AD_Ptr>>$08)|(Sfx_AD_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_AE_Ptr>>$08)|(Sfx_AE_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_AF_Ptr>>$08)|(Sfx_AF_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B0_Ptr>>$08)|(Sfx_B0_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B1_Ptr>>$08)|(Sfx_B1_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B2_Ptr>>$08)|(Sfx_B2_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B3_Ptr>>$08)|(Sfx_B3_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B4_Ptr>>$08)|(Sfx_B4_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B5_Ptr>>$08)|(Sfx_B5_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B6_Ptr>>$08)|(Sfx_B6_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B7_Ptr>>$08)|(Sfx_B7_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B8_Ptr>>$08)|(Sfx_B8_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_B9_Ptr>>$08)|(Sfx_B9_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_BA_Ptr>>$08)|(Sfx_BA_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_BB_Ptr>>$08)|(Sfx_BB_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_BC_Ptr>>$08)|(Sfx_BC_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_BD_Ptr>>$08)|(Sfx_BD_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_BE_Ptr>>$08)|(Sfx_BE_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_BF_Ptr>>$08)|(Sfx_BF_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C0_Ptr>>$08)|(Sfx_C0_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C1_Ptr>>$08)|(Sfx_C1_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C2_Ptr>>$08)|(Sfx_C2_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C3_Ptr>>$08)|(Sfx_C3_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C4_Ptr>>$08)|(Sfx_C4_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C5_Ptr>>$08)|(Sfx_C5_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C6_Ptr>>$08)|(Sfx_C6_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C7_Ptr>>$08)|(Sfx_C7_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C8_Ptr>>$08)|(Sfx_C8_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_C9_Ptr>>$08)|(Sfx_C9_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_CA_Ptr>>$08)|(Sfx_CA_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_CB_Ptr>>$08)|(Sfx_CB_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_CC_Ptr>>$08)|(Sfx_CC_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_CD_Ptr>>$08)|(Sfx_CD_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_CE_Ptr>>$08)|(Sfx_CE_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_CF_Ptr>>$08)|(Sfx_CF_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D0_Ptr>>$08)|(Sfx_D0_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D1_Ptr>>$08)|(Sfx_D1_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D2_Ptr>>$08)|(Sfx_D2_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D3_Ptr>>$08)|(Sfx_D3_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D4_Ptr>>$08)|(Sfx_D4_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D5_Ptr>>$08)|(Sfx_D5_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D6_Ptr>>$08)|(Sfx_D6_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D7_Ptr>>$08)|(Sfx_D7_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D8_Ptr>>$08)|(Sfx_D8_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_D9_Ptr>>$08)|(Sfx_D9_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_DA_Ptr>>$08)|(Sfx_DA_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_DB_Ptr>>$08)|(Sfx_DB_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_DC_Ptr>>$08)|(Sfx_DC_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_DC_Ptr>>$08)|(Sfx_DC_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_DC_Ptr>>$08)|(Sfx_DC_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_DC_Ptr>>$08)|(Sfx_DC_Ptr<<$08))&$FFFF
 
dc.w ((Sfx_DC_Ptr>>$08)|(Sfx_DC_Ptr<<$08))&$FFFF
 
DriverPointersEnd:
 
  
UniversalVoiceBank:
+
; Special BINCLUDE wrapper function
binclude "sound/uvb.bin"
+
DACBINCLUDE macro file,{INTLABEL}
UniversalVoiceBankEnd:
+
__LABEL__ label *
align $8000
+
BINCLUDE file
 +
__LABEL___Len  := little_endian(*-__LABEL__)
 +
__LABEL___Ptr  := k68z80Pointer(__LABEL__-soundBankStart)
 +
__LABEL___Bank := soundBankStart
 +
    endm
  
DacBank0:
+
; Setup macro for DAC samples.
binclude "sound/dac_0.bin"
+
DAC_Setup macro rate,dacptr
 +
dc.b rate
 +
dc.w dacptr_Len
 +
dc.w dacptr_Ptr
 +
    endm
  
align $8000
+
; A "null" DAC sample.
DacBank1:
+
DAC_Invalid:
binclude "sound/dac_1.bin"
+
DAC_Invalid_Len := 0
align $8000
+
DAC_Invalid_Ptr := 0
 +
DAC_Invalid_Bank := 0
  
DacBank2:
+
; Setup a null entry for a DAC sample.
binclude "sound/dac_2.bin"
+
DAC_Null_Setup macro rate,dacptr
align $8000
+
dc.b rate
 +
dc.w $0000,$0000
 +
    endm
  
Bank0:
+
; Setup a chain-linked invalid entry for a DAC sample.
binclude "sound/filler.bin"
+
; The sample's length is correctly stored for the sample,
Mini_Boss_Snd:
+
; while the pointer (usually) goes towards the DAC pointer
binclude "sound/miniboss.snd"
+
; entry of another DAC sample setup.
Final_Boss_Snd:
+
DAC_Null_Chain macro rate,dacptr,linkptr
binclude "sound/f_boss.snd"
+
dc.b rate
align $8000
+
dc.w dacptr_Len,k68z80Pointer(linkptr+3-soundBankStart)
 +
    endm
  
Bank1:
+
; ---------------------------------------------------------------------------
Angel_Island_1_Snd:
+
z80_SoundDriverStart:
binclude "sound/aiz1.snd"
 
Angel_Island_2_Snd:
 
binclude "sound/aiz2.snd"
 
Hidrocity_1_Snd:
 
binclude "sound/hcz1.snd"
 
Hidrocity_2_Snd:
 
binclude "sound/hcz2.snd"
 
Marble_Garden_1_Snd:
 
binclude "sound/mgz1.snd"
 
Marble_Garden_2_Snd:
 
binclude "sound/mgz2.snd"
 
Carnival_Night_2_Snd:
 
binclude "sound/cnz2.snd"
 
Carnival_Night_1_Snd:
 
binclude "sound/cnz1.snd"
 
Flying_Battery_1_Snd:
 
binclude "sound/fbz1.snd"
 
Flying_Battery_2_Snd:
 
binclude "sound/fbz2.snd"
 
The_Doomsday_Snd:
 
binclude "sound/tdz.snd"
 
align $8000
 
  
Bank2:
+
; ---------------------------------------------------------------------------
Icecap_2_Snd:
+
zTrack STRUCT DOTS
binclude "sound/iz2.snd"
+
; Playback control bits:
Icecap_1_Snd:
+
; 0 (01h) Noise channel (PSG) or FM3 special mode (FM)
binclude "sound/iz1.snd"
+
; 1 (02h) Do not attack next note
Launch_Base_2_Snd:
+
; 2 (04h) SFX is overriding this track
binclude "sound/lbz2.snd"
+
; 3 (08h) 'Alternate frequency mode' flag
Launch_Base_1_Snd:
+
; 4 (10h) 'Track is resting' flag
binclude "sound/lbz1.snd"
+
; 5 (20h) 'Pitch slide' flag
Mushroom_Hill_1_Snd:
+
; 6 (40h) 'Sustain frequency' flag -- prevents frequency from changing again for the lifetime of the track
binclude "sound/mhz1.snd"
+
; 7 (80h) Track is playing
Mushroom_Hill_2_Snd:
+
PlaybackControl: ds.b 1 ; S&K: 0
binclude "sound/mhz2.snd"
+
; Voice control bits:
Sandopolis_1_Snd:
+
; 0-1    FM channel assignment bits (00 = FM1 or FM4, 01 = FM2 or FM5, 10 = FM3 or FM6/DAC, 11 = invalid)
binclude "sound/sz1.snd"
+
; 2 (04h) For FM/DAC channels, selects if reg/data writes are bound for part II (set) or part I (unset)
Sandopolis_2_Snd:
+
; 3 (08h) Unknown/unused
binclude "sound/sz2.snd"
+
; 4 (10h) Unknown/unused
Lava_Reef_1_Snd:
+
; 5-6    PSG Channel assignment bits (00 = PSG1, 01 = PSG2, 10 = PSG3, 11 = Noise)
binclude "sound/lrz1.snd"
+
; 7 (80h) PSG track if set, FM or DAC track otherwise
Lava_Reef_2_Snd:
+
VoiceControl: ds.b 1 ; S&K: 1
binclude "sound/lrz2.snd"
+
TempoDivider: ds.b 1 ; S&K: 2
Sky_Sanctuary_Snd:
+
DataPointerLow: ds.b 1 ; S&K: 3
binclude "sound/scz.snd"
+
DataPointerHigh: ds.b 1 ; S&K: 4
Death_Egg_1_Snd:
+
Transpose: ds.b 1 ; S&K: 5
binclude "sound/dez1.snd"
+
Volume: ds.b 1 ; S&K: 6
Death_Egg_2_Snd:
+
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
binclude "sound/dez2.snd"
+
VoiceIndex: ds.b 1 ; S&K: 8 ; FM instrument/PSG voice
Mini_Boss_SK_Snd:
+
StackPointer: ds.b 1 ; S&K: 9 ; For call subroutine coordination flag
binclude "sound/mb_sk.snd"
+
AMSFMSPan: ds.b 1 ; S&K: 0Ah
Boss_Snd:
+
DurationTimeout: ds.b 1 ; S&K: 0Bh
binclude "sound/boss.snd"
+
SavedDuration: ds.b 1 ; S&K: 0Ch ; Already multiplied by timing divisor
Glowing_Spheres_Bonus_Stage_snd:
+
; ---------------------------------
binclude "sound/gs_bs.snd"
+
; Alternate names for same offset:
Special_Stage_Snd:
+
SavedDAC: ; S&K: 0Dh ; For DAC channel
binclude "sound/ss.snd"
+
FreqLow: ds.b 1 ; S&K: 0Dh ; For FM/PSG channels
Level_Results_Snd:
+
; ---------------------------------
binclude "sound/lr.snd"
+
FreqHigh: ds.b 1 ; S&K: 0Eh ; For FM/PSG channels
Menu_Snd:
+
VoiceSongID: ds.b 1 ; S&K: 0Fh ; For using voices from a different song
binclude "sound/menu.snd"
+
Detune: ds.b 1 ; S&K: 10h ; In S&K, some places used 11h instead of 10h
align $8000
+
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
  
Bank3:
+
zTempVariablesStart:
Slot_Machine_Bonus_Stage_snd:
 
binclude "sound/sm_bs.snd"
 
Gum_Ball_Machine_Bonus_Stage_snd:
 
binclude "sound/gbm_bs.snd"
 
Knuckles_Theme_Snd:
 
binclude "sound/kte.snd"
 
Azure_Lake_Snd:
 
binclude "sound/alz.snd"
 
Balloon_Park_Snd:
 
binclude "sound/bpz.snd"
 
Desert_Palace_Snd:
 
binclude "sound/dpz.snd"
 
Chrome_Gadget_Snd:
 
binclude "sound/cgz.snd"
 
Endless_Mine_Snd:
 
binclude "sound/emz.snd"
 
Title_Screen_Snd:
 
binclude "sound/ts.snd"
 
Credits_Snd:
 
binclude "sound/credits.snd"
 
Time_Game_Over_Snd:
 
binclude "sound/tgovr.snd"
 
Continue_Snd:
 
binclude "sound/continue.snd"
 
Extra_Life_Snd:
 
binclude "sound/1up.snd"
 
Emerald_Snd:
 
binclude "sound/emerald.snd"
 
Invencibility_Snd:
 
binclude "sound/invcblty.snd"
 
Competition_Menu_Snd:
 
binclude "sound/2p_menu.snd"
 
Underwater_Timming_Snd:
 
binclude "sound/panic.snd"
 
Presented_by_SEGA_Snd:
 
binclude "sound/p_sega.snd"
 
align $8000
 
  
SndBank:
+
zFadeOutTimeout: ds.b 1
SegaPCMBank:
+
zFadeDelay: ds.b 1
SegaSnd:
+
zFadeDelayTimeout: ds.b 1
binclude "sound/sega.snd"
+
zPauseFlag: ds.b 1
Sfx_34_Snd:
+
zHaltFlag: ds.b 1
binclude "sound/sfx_34.snd"
+
zFM3Settings: ds.b 1 ; Set twice, never read (is read in Z80 Type 1 for YM timer-related purposes)
Sfx_35_Snd:
+
zTempoAccumulator: ds.b 1
binclude "sound/sfx_35.snd"
+
ds.b 1 ; unused
Sfx_36_Snd:
+
unk_1C15 ds.b 1 ; Set twice, never read
binclude "sound/sfx_36.snd"
+
zFadeToPrevFlag: ds.b 1
Sfx_37_Snd:
+
unk_1C17: ds.b 1 ; Set once, never read
binclude "sound/sfx_37.snd"
+
unk_1C18: ds.b 1 ; Set twice, never read
Sfx_38_Snd:
+
zUpdatingSFX: ds.b 1
binclude "sound/sfx_38.snd"
+
ds.b $A ; unused
Sfx_39_Snd:
+
zCurrentTempo: ds.b 1
binclude "sound/sfx_39.snd"
+
zContinuousSFX: ds.b 1
Sfx_3A_Snd:
+
zContinuousSFXFlag: ds.b 1
binclude "sound/sfx_3A.snd"
+
zSpindashRev: ds.b 1
Sfx_3B_Snd:
+
zRingSpeaker: ds.b 1
binclude "sound/sfx_3B.snd"
+
zFadeInTimeout: ds.b 1
Sfx_3C_Snd:
+
zVoiceTblPtrSave: ds.b 2 ; For 1-up
binclude "sound/sfx_3C.snd"
+
zCurrentTempoSave: ds.b 1 ; For 1-up
Sfx_3D_Snd:
+
zSongBankSave: ds.b 1 ; For 1-up
binclude "sound/sfx_3D.snd"
+
zTempoSpeedupSave: ds.b 1 ; For 1-up
Sfx_3E_Snd:
+
zSpeedupTimeout: ds.b 1
binclude "sound/sfx_3E.snd"
+
zDACIndex: ds.b 1 ; bit 7 = 1 if playing, 0 if not; remaining 7 bits are index into DAC tables (1-based)
Sfx_3F_Snd:
+
zContSFXLoopCnt: ds.b 1 ; Used as a loop counter for continuous SFX
binclude "sound/sfx_3F.snd"
+
zSFXSaveIndex: ds.b 1
Sfx_40_Snd:
+
zSongPosition: ds.b 2
binclude "sound/sfx_40.snd"
+
zTrackInitPos: ds.b 2 ; 2 bytes
Sfx_41_Snd:
+
zVoiceTblPtr: ds.b 2 ; 2 bytes
binclude "sound/sfx_41.snd"
+
zSFXVoiceTblPtr: ds.b 2 ; 2 bytes
Sfx_42_Snd:
+
zSFXTempoDivider: ds.b 1
binclude "sound/sfx_42.snd"
+
ds.b 2 ; unused
Sfx_43_Snd:
+
zSongBank: ds.b 1 ; Bits 15 to 22 of M68K bank address
binclude "sound/sfx_43.snd"
+
PlaySegaPCMFlag: ds.b 1
Sfx_44_Snd:
+
; Now starts song and SFX z80 RAM
binclude "sound/sfx_44.snd"
+
; Max number of music channels: 6 FM + 3 PSG or 1 DAC + 5 FM + 3 PSG
Sfx_45_Snd:
+
zTracksStart:
binclude "sound/sfx_45.snd"
+
zSongFM6_DAC: zTrack
Sfx_46_Snd:
+
zSongFM1: zTrack
binclude "sound/sfx_46.snd"
+
zSongFM2: zTrack
Sfx_47_Snd:
+
zSongFM3: zTrack
binclude "sound/sfx_47.snd"
+
zSongFM4: zTrack
Sfx_48_Snd:
+
zSongFM5: zTrack
binclude "sound/sfx_48.snd"
+
zSongPSG1: zTrack
Sfx_49_Snd:
+
zSongPSG2: zTrack
binclude "sound/sfx_49.snd"
+
zSongPSG3: zTrack
Sfx_4A_Snd:
+
zTracksEnd:
binclude "sound/sfx_4A.snd"
+
; This is RAM for backup of songs (when 1-up jingle is playing)
Sfx_4B_Snd:
+
; and for SFX channels. Note these two overlap.
binclude "sound/sfx_4B.snd"
+
; Max number of SFX channels: 4 FM + 3 PSG
Sfx_4C_Snd:
+
zTracksSFXStart:
binclude "sound/sfx_4C.snd"
+
zSFX_FM3: zTrack
Sfx_4D_Snd:
+
zSFX_FM4: zTrack
binclude "sound/sfx_4D.snd"
+
zSFX_FM5: zTrack
Sfx_4E_Snd:
+
zSFX_FM6: zTrack
binclude "sound/sfx_4E.snd"
+
zSFX_PSG1: zTrack
Sfx_4F_Snd:
+
zSFX_PSG2: zTrack
binclude "sound/sfx_4F.snd"
+
zSFX_PSG3: zTrack
Sfx_50_Snd:
+
zTracksSFXEnd:
binclude "sound/sfx_50.snd"
 
Sfx_51_Snd:
 
binclude "sound/sfx_51.snd"
 
Sfx_52_Snd:
 
binclude "sound/sfx_52.snd"
 
Sfx_53_Snd:
 
binclude "sound/sfx_53.snd"
 
Sfx_54_Snd:
 
binclude "sound/sfx_54.snd"
 
Sfx_55_Snd:
 
binclude "sound/sfx_55.snd"
 
Sfx_56_Snd:
 
binclude "sound/sfx_56.snd"
 
Sfx_57_Snd:
 
binclude "sound/sfx_57.snd"
 
Sfx_58_Snd:
 
binclude "sound/sfx_58.snd"
 
Sfx_59_Snd:
 
binclude "sound/sfx_59.snd"
 
Sfx_5A_Snd:
 
binclude "sound/sfx_5A.snd"
 
Sfx_5B_Snd:
 
binclude "sound/sfx_5B.snd"
 
Sfx_5C_Snd:
 
binclude "sound/sfx_5C.snd"
 
Sfx_5D_Snd:
 
binclude "sound/sfx_5D.snd"
 
Sfx_5E_Snd:
 
binclude "sound/sfx_5E.snd"
 
Sfx_5F_Snd:
 
binclude "sound/sfx_5F.snd"
 
Sfx_60_Snd:
 
binclude "sound/sfx_60.snd"
 
Sfx_61_Snd:
 
binclude "sound/sfx_61.snd"
 
Sfx_62_Snd:
 
binclude "sound/sfx_62.snd"
 
Sfx_63_Snd:
 
binclude "sound/sfx_63.snd"
 
Sfx_64_Snd:
 
binclude "sound/sfx_64.snd"
 
Sfx_65_Snd:
 
binclude "sound/sfx_65.snd"
 
Sfx_66_Snd:
 
binclude "sound/sfx_66.snd"
 
Sfx_67_Snd:
 
binclude "sound/sfx_67.snd"
 
Sfx_68_Snd:
 
binclude "sound/sfx_68.snd"
 
Sfx_69_Snd:
 
binclude "sound/sfx_69.snd"
 
Sfx_6A_Snd:
 
binclude "sound/sfx_6A.snd"
 
Sfx_6B_Snd:
 
binclude "sound/sfx_6B.snd"
 
Sfx_6C_Snd:
 
binclude "sound/sfx_6C.snd"
 
Sfx_6D_Snd:
 
binclude "sound/sfx_6D.snd"
 
Sfx_6E_Snd:
 
binclude "sound/sfx_6E.snd"
 
Sfx_6F_Snd:
 
binclude "sound/sfx_6F.snd"
 
Sfx_70_Snd:
 
binclude "sound/sfx_70.snd"
 
Sfx_71_Snd:
 
binclude "sound/sfx_71.snd"
 
Sfx_72_Snd:
 
binclude "sound/sfx_72.snd"
 
Sfx_73_Snd:
 
binclude "sound/sfx_73.snd"
 
Sfx_74_Snd:
 
binclude "sound/sfx_74.snd"
 
Sfx_75_Snd:
 
binclude "sound/sfx_75.snd"
 
Sfx_76_Snd:
 
binclude "sound/sfx_76.snd"
 
Sfx_77_Snd:
 
binclude "sound/sfx_77.snd"
 
Sfx_78_Snd:
 
binclude "sound/sfx_78.snd"
 
Sfx_79_Snd:
 
binclude "sound/sfx_79.snd"
 
Sfx_7A_Snd:
 
binclude "sound/sfx_7A.snd"
 
Sfx_7B_Snd:
 
binclude "sound/sfx_7B.snd"
 
Sfx_7C_Snd:
 
binclude "sound/sfx_7C.snd"
 
Sfx_7D_Snd:
 
binclude "sound/sfx_7D.snd"
 
Sfx_7E_Snd:
 
binclude "sound/sfx_7E.snd"
 
Sfx_7F_Snd:
 
binclude "sound/sfx_7F.snd"
 
Sfx_80_Snd:
 
binclude "sound/sfx_80.snd"
 
Sfx_81_Snd:
 
binclude "sound/sfx_81.snd"
 
Sfx_82_Snd:
 
binclude "sound/sfx_82.snd"
 
Sfx_83_Snd:
 
binclude "sound/sfx_83.snd"
 
Sfx_84_Snd:
 
binclude "sound/sfx_84.snd"
 
Sfx_85_Snd:
 
binclude "sound/sfx_85.snd"
 
Sfx_86_Snd:
 
binclude "sound/sfx_86.snd"
 
Sfx_87_Snd:
 
binclude "sound/sfx_87.snd"
 
Sfx_88_Snd:
 
binclude "sound/sfx_88.snd"
 
Sfx_89_Snd:
 
binclude "sound/sfx_89.snd"
 
Sfx_8A_Snd:
 
binclude "sound/sfx_8A.snd"
 
Sfx_8B_Snd:
 
binclude "sound/sfx_8B.snd"
 
Sfx_8C_Snd:
 
binclude "sound/sfx_8C.snd"
 
Sfx_8D_Snd:
 
binclude "sound/sfx_8D.snd"
 
Sfx_8E_Snd:
 
binclude "sound/sfx_8E.snd"
 
Sfx_8F_Snd:
 
binclude "sound/sfx_8F.snd"
 
Sfx_90_Snd:
 
binclude "sound/sfx_90.snd"
 
Sfx_91_Snd:
 
binclude "sound/sfx_91.snd"
 
Sfx_92_Snd:
 
binclude "sound/sfx_92.snd"
 
Sfx_93_Snd:
 
binclude "sound/sfx_93.snd"
 
Sfx_94_Snd:
 
binclude "sound/sfx_94.snd"
 
Sfx_95_Snd:
 
binclude "sound/sfx_95.snd"
 
Sfx_96_Snd:
 
binclude "sound/sfx_96.snd"
 
Sfx_97_Snd:
 
binclude "sound/sfx_97.snd"
 
Sfx_98_Snd:
 
binclude "sound/sfx_98.snd"
 
Sfx_99_Snd:
 
binclude "sound/sfx_99.snd"
 
Sfx_9A_Snd:
 
binclude "sound/sfx_9A.snd"
 
Sfx_9B_Snd:
 
binclude "sound/sfx_9B.snd"
 
Sfx_9C_Snd:
 
binclude "sound/sfx_9C.snd"
 
Sfx_9D_Snd:
 
binclude "sound/sfx_9D.snd"
 
Sfx_9E_Snd:
 
binclude "sound/sfx_9E.snd"
 
Sfx_9F_Snd:
 
binclude "sound/sfx_9F.snd"
 
Sfx_A0_Snd:
 
binclude "sound/sfx_A0.snd"
 
Sfx_A1_Snd:
 
binclude "sound/sfx_A1.snd"
 
Sfx_A2_Snd:
 
binclude "sound/sfx_A2.snd"
 
Sfx_A3_Snd:
 
binclude "sound/sfx_A3.snd"
 
Sfx_A4_Snd:
 
binclude "sound/sfx_A4.snd"
 
Sfx_A5_Snd:
 
binclude "sound/sfx_A5.snd"
 
Sfx_A6_Snd:
 
binclude "sound/sfx_A6.snd"
 
Sfx_A7_Snd:
 
binclude "sound/sfx_A7.snd"
 
Sfx_A8_Snd:
 
binclude "sound/sfx_A8.snd"
 
Sfx_A9_Snd:
 
binclude "sound/sfx_A9.snd"
 
Sfx_AA_Snd:
 
binclude "sound/sfx_AA.snd"
 
Sfx_AB_Snd:
 
binclude "sound/sfx_AB.snd"
 
Sfx_AC_Snd:
 
binclude "sound/sfx_AC.snd"
 
Sfx_AD_Snd:
 
binclude "sound/sfx_AD.snd"
 
Sfx_AE_Snd:
 
binclude "sound/sfx_AE.snd"
 
Sfx_AF_Snd:
 
binclude "sound/sfx_AF.snd"
 
Sfx_B0_Snd:
 
binclude "sound/sfx_B0.snd"
 
Sfx_B1_Snd:
 
binclude "sound/sfx_B1.snd"
 
Sfx_B2_Snd:
 
binclude "sound/sfx_B2.snd"
 
Sfx_B3_Snd:
 
binclude "sound/sfx_B3.snd"
 
Sfx_B4_Snd:
 
binclude "sound/sfx_B4.snd"
 
Sfx_B5_Snd:
 
binclude "sound/sfx_B5.snd"
 
Sfx_B6_Snd:
 
binclude "sound/sfx_B6.snd"
 
Sfx_B7_Snd:
 
binclude "sound/sfx_B7.snd"
 
Sfx_B8_Snd:
 
binclude "sound/sfx_B8.snd"
 
Sfx_B9_Snd:
 
binclude "sound/sfx_B9.snd"
 
Sfx_BA_Snd:
 
binclude "sound/sfx_BA.snd"
 
Sfx_BB_Snd:
 
binclude "sound/sfx_BB.snd"
 
Sfx_BC_Snd:
 
binclude "sound/sfx_BC.snd"
 
Sfx_BD_Snd:
 
binclude "sound/sfx_BD.snd"
 
Sfx_BE_Snd:
 
binclude "sound/sfx_BE.snd"
 
Sfx_BF_Snd:
 
binclude "sound/sfx_BF.snd"
 
Sfx_C0_Snd:
 
binclude "sound/sfx_C0.snd"
 
Sfx_C1_Snd:
 
binclude "sound/sfx_C1.snd"
 
Sfx_C2_Snd:
 
binclude "sound/sfx_C2.snd"
 
Sfx_C3_Snd:
 
binclude "sound/sfx_C3.snd"
 
Sfx_C4_Snd:
 
binclude "sound/sfx_C4.snd"
 
Sfx_C5_Snd:
 
binclude "sound/sfx_C5.snd"
 
Sfx_C6_Snd:
 
binclude "sound/sfx_C6.snd"
 
Sfx_C7_Snd:
 
binclude "sound/sfx_C7.snd"
 
Sfx_C8_Snd:
 
binclude "sound/sfx_C8.snd"
 
Sfx_C9_Snd:
 
binclude "sound/sfx_C9.snd"
 
Sfx_CA_Snd:
 
binclude "sound/sfx_CA.snd"
 
Sfx_CB_Snd:
 
binclude "sound/sfx_CB.snd"
 
Sfx_CC_Snd:
 
binclude "sound/sfx_CC.snd"
 
Sfx_CD_Snd:
 
binclude "sound/sfx_CD.snd"
 
Sfx_CE_Snd:
 
binclude "sound/sfx_CE.snd"
 
Sfx_CF_Snd:
 
binclude "sound/sfx_CF.snd"
 
Sfx_D0_Snd:
 
binclude "sound/sfx_D0.snd"
 
Sfx_D1_Snd:
 
binclude "sound/sfx_D1.snd"
 
Sfx_D2_Snd:
 
binclude "sound/sfx_D2.snd"
 
Sfx_D3_Snd:
 
binclude "sound/sfx_D3.snd"
 
Sfx_D4_Snd:
 
binclude "sound/sfx_D4.snd"
 
Sfx_D5_Snd:
 
binclude "sound/sfx_D5.snd"
 
Sfx_D6_Snd:
 
binclude "sound/sfx_D6.snd"
 
Sfx_D7_Snd:
 
binclude "sound/sfx_D7.snd"
 
Sfx_D8_Snd:
 
binclude "sound/sfx_D8.snd"
 
Sfx_D9_Snd:
 
binclude "sound/sfx_D9.snd"
 
Sfx_DA_Snd:
 
binclude "sound/sfx_DA.snd"
 
Sfx_DB_Snd:
 
binclude "sound/sfx_DB.snd"
 
Sfx_DC_Snd:
 
binclude "sound/sfx_DC.snd"
 
</asm>
 
  
===Driver data files===
+
phase zTracksSFXStart
then unpack this:
+
zTracksSaveStart:
{{Download|file=s3driverdata.7z|filesize=131kb|title=The Sonic 3 Driver data files|plural=1}}
+
zSaveSongDAC: zTrack
 +
zSaveSongFM1: zTrack
 +
zSaveSongFM2: zTrack
 +
zSaveSongFM3: zTrack
 +
zSaveSongFM4: zTrack
 +
zSaveSongFM5: zTrack
 +
zSaveSongPSG1: zTrack
 +
zSaveSongPSG2: zTrack
 +
zSaveSongPSG3: zTrack
 +
zTracksSaveEnd:
  
==Enabling Sonic 2 / 3 Level Music Memory==
+
zTempVariablesEnd:
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.  Sonic1 uses a second index for restoring music as opposed to actually memorizing it like later sonic games do.
+
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)
  
First we will locate:
+
save
<asm>
+
!org 0 ; z80 Align, handled by the build process
Level_PlayBgm:
+
CPU Z80
lea (MusicList).l,a1 ; load music playlist
+
listing purecode
move.b (a1,d0.w),d0 ; add d0 to a1
+
; ---------------------------------------------------------------------------
bsr.w PlaySound ; play music
+
NoteRest = 080h
move.b #$34,($FFFFD080).w ; load title card object
+
FirstCoordFlag = 0E0h
</asm>
+
; ---------------------------------------------------------------------------
 +
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
  
and insert a new line to make it read:
+
bankswitch3 macro
<asm>
+
ld b, 8
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
 
</asm>
 
  
Now we will locate:
+
.bankloop:
<asm>
+
ld (zBankRegister), a
move.b ($FFFFFE10).w,d0
+
rrca
cmpi.w #$103,($FFFFFE10).w ; check if level is SBZ3
+
djnz .bankloop
bne.s Obj01_PlayMusic
+
xor a
moveq #5,d0 ; play SBZ music
+
ld (zBankRegister), a
 +
    endm
  
Obj01_PlayMusic:
+
bankswitchToMusic macro
lea (MusicList2).l,a1
+
ld hl, zBankRegister
move.b (a1,d0.w),d0
+
ld a, (zSongBank)
jsr (PlaySound).l ; play normal music
+
ld (hl), a
</asm>
+
rept 7
 +
rra
 +
ld (hl), a
 +
endm
 +
xor a
 +
ld (hl), a
 +
    endm
  
seems the invincibility stars conflict with music memory, so we will just make it use music list 1, so the result is:
+
; macro to make a certain error message clearer should you happen to get it...
<asm>
+
rsttarget macro {INTLABEL}
move.b ($FFFFFE10).w,d0
+
if ($&7)||($>38h)
cmpi.w #$103,($FFFFFE10).w ; check if level is SBZ3
+
fatal "Function __LABEL__ is at 0\{$}h, but must be at a multiple of 8 bytes <= 38h to be used with the rst instruction."
bne.s Obj01_PlayMusic
+
endif
moveq #5,d0 ; play SBZ music
+
if "__LABEL__"<>""
 +
__LABEL__ label $
 +
endif
 +
    endm
  
Obj01_PlayMusic:
+
; function to turn a 68k address into a word the Z80 can use to access it
lea (MusicList).l,a1 ; load music playlist
+
zmake68kPtr function addr,zROMWindow+(addr&7FFFh)
move.b (a1,d0.w),d0 ; add d0 to a1
 
move.w d0,($FFFFFF90).w ; store level music
 
jsr (PlaySound).l ; play normal music
 
</asm>
 
  
then remove:
+
; function to turn a 68k address into a bank byte
<asm>
+
zmake68kBank function addr,(((addr&3F8000h)/zROMWindow))
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Music to play after invincibility wears off
+
; ===========================================================================
 +
; Entry Point
 +
; ===========================================================================
 +
 
 +
; EntryPoint:
 +
di ; Disable interrupts
 +
di ; Disable interrupts
 +
im 1 ; set interrupt mode 1
 +
jp zInitAudioDriver
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
MusicList2: binclude misc\muslist2.bin
+
if fix_sndbugs=0
</asm>
+
db 0F2h ; Filler; broken jp p,loc?
now we want to fix the underwater counter music restore code, so locate:
+
endif
<asm>
+
 
ResumeMusic: ; XREF: Obj64_Wobble; Sonic_Water; Obj0A_ReduceAir
+
; =============== S U B R O U T I N E =======================================
cmpi.w #$C,($FFFFFE14).w
+
;
bhi.s loc_140AC
+
; Gets the correct pointer to pointer table for the data type in question
move.w #$82,d0 ; play LZ music
+
; (music, sfx, voices, etc.).
cmpi.w #$103,($FFFFFE10).w ; check if level is 0103 (SBZ3)
+
;
bne.s loc_140A6
+
; Input:  c    ID for data type.
move.w #$86,d0 ; play SBZ music
+
; Output: hl  Master pointer table for index
 +
;         af'  Trashed
 +
;         b    Trashed
  
loc_140A6:
+
; sub_8
jsr (PlaySound).l
+
align 8
</asm>
+
GetPointerTable: rsttarget
and replace it with:
+
if fix_sndbugs
<asm>
+
ld hl, z80_SoundDriverPointers ; Load pointer table
ResumeMusic: ; XREF: Obj64_Wobble; Sonic_Water; Obj0A_ReduceAir
+
else
cmpi.w #$C,($FFFFFE14).w
+
ld hl, (ptrMasterIndex) ; Read pointer to (pointer to pointer table) table
bhi.s loc_140AC
+
endif
move.w ($FFFFFF90).w,d0       ;resume music
+
ld b, 0 ; b = 0
jsr (PlaySound).l
+
add hl, bc ; Add offset into pointer table
</asm>
+
ex af, af' ; Back up af
that sets up music memory for later.
+
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
  
==Fixing the Music Playlist==
+
; ---------------------------------------------------------------------------
Okay, so we now have a working sonic3 driver, but we have yet to fix the music playlist it is still pointing to sound effects.
+
if fix_sndbugs=0
Open ''misc\muslist1.bin'' in your favorite hex editor.<p>
+
;word_15
You will find that it reads:
+
ptrMasterIndex:
{| class="prettytable"
+
dw z80_SoundDriverPointers
! Hex Data
+
endif
|-
 
| '''81 82 83 84 85 86 8D 00'''
 
|}
 
Pick some new music for your game.
 
----
 
  
I prefer to use:
+
; =============== S U B R O U T I N E =======================================
{| class="prettytable"
+
;
! Hex Data
+
; Reads an offset into a pointer table and returns dereferenced pointer.
|-
+
;
| '''01 04 13 0D 07 16 30 00'''
+
;
|}
+
; Input: a    Index into pointer table
Once you rebuild your rom, you will have some music on the levels, but sound effects and some other musics unrelated to the playlist will need fixing. This part is much easier fixing if you applied the sonic2 memory fix to your hack, otherwise edit ''misc/muslist2.bin'' and make the exact same changes.
+
;       hl  Pointer to pointer table
 +
; Output: hl  Selected pointer in pointer table
 +
;        bc  Trashed
  
==Fixing the Sneaker==
+
; sub_18
We need to fix the sneaker in a different way than we normally do, because sonic3 uses a different means for speeding up or slowing down the music then its predecessors do.
+
align 8
first we will locate:
+
PointerTableOffset: rsttarget
<asm>
+
ld c, a ; c = index into pointer table
Obj2E_ChkShoes:
+
ld b, 0 ; b = 0
cmpi.b #3,d0 ; does monitor contain speed shoes?
+
add hl, bc ; hl += bc
bne.s Obj2E_ChkShield
+
add hl, bc ; hl += bc
move.b #1,($FFFFFE2E).w ; speed up the BG music
+
if fix_sndbugs
move.w #$4B0,($FFFFD034).w ; time limit for the power-up
+
jp ReadPointer ; 10 clock cycles, 3 bytes
move.w #$C00,($FFFFF760).w ; change Sonic"s top speed
+
else
move.w #$18,($FFFFF762).w
+
nop ; 12 clock cycles, 3 bytes
move.w #$80,($FFFFF764).w
+
nop
move.w #$E2,d0
+
nop
jmp (PlaySound).l ; Speed up the music
+
endif
</asm>
+
; End of function PointerTableOffset
note that sonic1 plays a special sound to make the music speed up, our new driver does not do that, so we will change it to read:
 
<asm>
 
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
 
</asm>
 
that speeds the music up properly, but we need to tell it how to slow the music back down.  So we find:
 
<asm>
 
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
 
</asm>
 
note that sonic1 also uses another sound to return the music to normal speed, sonic3 doesn't, so we will correct the sneaker wearing off effect, by changin it to:
 
<asm>
 
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
 
</asm>
 
  
==Fixing the screen transition sound effects==
+
; =============== S U B R O U T I N E =======================================
Don't we want music to fade out properly when changing screens?  Well if that is true, then you are reading in the right place, first off, go and find:
+
;
<asm>
+
; Dereferences a pointer.
SegaScreen: ; XREF: GameModeArray
+
;
move.b #$E4,d0
+
; Input: hl Pointer
bsr.w PlaySound_Special ; stop music
+
; output: hl Equal to what that was being pointed to by hl
</asm>
 
  
we want it to change to:
+
; loc_20
<asm>
+
align 8
SegaScreen: ; XREF: GameModeArray
+
ReadPointer: rsttarget
move.b #$E1,d0
+
ld a, (hl) ; Read low byte of pointer into a
bsr.w PlaySound_Special ; Sonic 3 prefers to fade out rather than stop the music.
+
inc hl
</asm>
+
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
  
Good, the sega logo screen does with music what we want it to!  now to fix the Title Screen, locate:
+
; ---------------------------------------------------------------------------
<asm>
+
; There is room for two more 'rsttarget's here
TitleScreen: ; XREF: GameModeArray
+
; ---------------------------------------------------------------------------
move.b #$E4,d0
+
align 38h
bsr.w PlaySound_Special ; stop music
+
; =============== S U B R O U T I N E =======================================
</asm>
+
;
and change it to:
+
; This subroutine is called every V-Int. After it is processed, the z80
<asm>
+
; returns to the digital audio loop to continue playing DAC samples.
TitleScreen: ; XREF: GameModeArray
+
;
move.b #$E1,d0
+
; If the SEGA PCM is being played, it disables interrupts -- this means that
bsr.w PlaySound_Special ; Sonic 3 prefers to fade out rather than stop the music.
+
; this procedure will NOT be called while the SEGA PCM is playing.
</asm>
+
;
 +
;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
  
Good, the title screen transition effect should be fixed now, but we still have a ways to go, so now lets fix the ending sequence transitioning, locate:
+
.pal_timer:
<asm>
+
dec a ; Decrease PAL double-update timeout counter
EndingSequence: ; XREF: GameModeArray
+
ld (zPalDblUpdCounter), a ; Store it
move.b #$E4,d0
 
bsr.w PlaySound_Special ; stop music
 
</asm>
 
and change it to:
 
<asm>
 
EndingSequence: ; XREF: GameModeArray
 
move.b #$E1,d0
 
bsr.w PlaySound_Special ; Sonic 3 prefers to fade out rather than stop the music.
 
</asm>
 
  
Now that the screens fade music as they should, shouldn't we fix the rest of the fades throughout the game?  I would think so, so lets do just that, locate:
+
.not_pal:
<asm>
+
ld a, (zDACIndex) ; Get index of playing DAC sample
move.b #$E0,d0
+
and 7Fh ; Strip 'DAC playing' bit
bsr.w PlaySound_Special ; fade out music
+
ld c, a ; c = a
rts
+
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
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Level select - level pointers
+
;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
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
</asm>
+
 
and change it to:
+
;loc_CB
<asm>
+
zWriteFMII_reduced:
move.b #$E1,d0
+
sub 4 ; Strip 'bound to part II regs' bit
bsr.w PlaySound_Special ; fade out music
 
rts
 
; ===========================================================================
 
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Level select - level pointers
 
; ---------------------------------------------------------------------------
 
</asm>
 
  
that fixes a spot in the level select, which we will work on later. For now locate:
+
; =============== S U B R O U T I N E =======================================
<asm>
+
;
loc_33E4: ; XREF: Demo
+
; Writes a reg/data pair to part II
andi.b #$80,($FFFFF605).w ; is Start button pressed?
+
;
bne.w Title_ChkLevSel ; if yes, branch
+
; Input: a    Value for register
tst.w ($FFFFF614).w
+
;        c    Value for data
bne.w loc_33B6
+
 
move.b #$E0,d0
+
;sub_CD
bsr.w PlaySound_Special ; fade out music
+
zWriteFMII:
</asm>
+
ld (zYM2612_A1), a ; Select YM2612 register
and change it to:
+
if fix_sndbugs=0
<asm>
+
; Pointless, since there's no need to delay between writing to the address port and the data port
loc_33E4: ; XREF: Demo
+
nop ; Wait
andi.b #$80,($FFFFF605).w ; is Start button pressed?
+
endif
bne.w Title_ChkLevSel ; if yes, branch
+
ld a, c ; a = data to send
tst.w ($FFFFF614).w
+
ld (zYM2612_D1), a ; Send data to register
bne.w loc_33B6
+
ret
move.b #$E1,d0
+
; End of function zWriteFMII
bsr.w PlaySound_Special ; fade out music
+
 
</asm>
+
; ---------------------------------------------------------------------------
now find:
+
; ===========================================================================
<asm>
+
; DAC BANKS
Level: ; XREF: GameModeArray
+
; ===========================================================================
bset #7,($FFFFF600).w ; add $80 to screen mode (for pre level sequence)
+
; Note: this table has a dummy first entry for the case when there is no DAC
tst.w ($FFFFFFF0).w
+
; sample being played -- the code still results in a valid bank switch, and
bmi.s loc_37B6
+
; does not need to worry about special cases.
move.b #$E0,d0
+
DAC_Banks:
bsr.w PlaySound_Special ; fade out music
+
db zmake68kBank(DacBank1)              ,zmake68kBank(DAC_81_Data)           ,zmake68kBank(DAC_82_83_84_85_Data)  ,zmake68kBank(DAC_82_83_84_85_Data)
</asm>
+
db zmake68kBank(DAC_82_83_84_85_Data)  ,zmake68kBank(DAC_82_83_84_85_Data)  ,zmake68kBank(DAC_86_Data)            ,zmake68kBank(DAC_87_Data)
and change it to:
+
db zmake68kBank(DAC_88_Data)            ,zmake68kBank(DAC_89_Data)            ,zmake68kBank(DAC_8A_8B_Data)        ,zmake68kBank(DAC_8A_8B_Data)
<asm>
+
db zmake68kBank(DAC_8C_Data)            ,zmake68kBank(DAC_8D_8E_Data)        ,zmake68kBank(DAC_8D_8E_Data)        ,zmake68kBank(DAC_8F_Data)
Level: ; XREF: GameModeArray
+
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)
bset #7,($FFFFF600).w ; add $80 to screen mode (for pre level sequence)
+
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)
tst.w ($FFFFFFF0).w
+
db zmake68kBank(DAC_98_99_9A_Data)      ,zmake68kBank(DAC_98_99_9A_Data)      ,zmake68kBank(DAC_98_99_9A_Data)      ,zmake68kBank(DAC_9B_Data)
bmi.s loc_37B6
+
db zmake68kBank(DAC_9C_Data)            ,zmake68kBank(DAC_9D_Data)            ,zmake68kBank(DAC_9E_Data)            ,zmake68kBank(DAC_9F_Data)
move.b #$E1,d0
+
db zmake68kBank(DAC_A0_Data)            ,zmake68kBank(DAC_A1_Data)            ,zmake68kBank(DAC_A2_Data)            ,zmake68kBank(DAC_A3_Data)
bsr.w PlaySound_Special ; fade out music
+
db zmake68kBank(DAC_A4_Data)            ,zmake68kBank(DAC_A5_Data)            ,zmake68kBank(DAC_A6_Data)            ,zmake68kBank(DAC_A7_Data)
</asm>
+
db zmake68kBank(DAC_A8_Data)            ,zmake68kBank(DAC_A9_Data)            ,zmake68kBank(DAC_AA_Data)            ,zmake68kBank(DAC_AB_Data)
now find:
+
db zmake68kBank(DAC_AC_Data)            ,zmake68kBank(DAC_AD_AE_Data)        ,zmake68kBank(DAC_AD_AE_Data)        ,zmake68kBank(DAC_AF_Data)
<asm>
+
db zmake68kBank(DAC_AF_Data)            ,zmake68kBank(DAC_B1_Data)            ,zmake68kBank(DAC_B2_B3_Data)        ,zmake68kBank(DAC_B2_B3_Data)
Obj81_GetUp: ; XREF: Obj81_Animate
+
db zmake68kBank(DAC_B4_C1_C2_C3_C4_Data),zmake68kBank(DAC_B5_Data)            ,zmake68kBank(DAC_B6_Data)            ,zmake68kBank(DAC_B7_Data)
addq.b #2,$24(a0)
+
db zmake68kBank(DAC_B8_B9_Data)         ,zmake68kBank(DAC_B8_B9_Data)         ,zmake68kBank(DAC_BA_Data)            ,zmake68kBank(DAC_BB_Data)
move.l #Map_Sonic,4(a0)
+
db zmake68kBank(DAC_BC_Data)            ,zmake68kBank(DAC_BD_Data)           ,zmake68kBank(DAC_BE_Data)           ,zmake68kBank(DAC_BF_Data)
move.w #$780,2(a0)
+
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)
move.b #$1E,$1C(a0) ; use "getting up" animation
+
db zmake68kBank(DAC_B4_C1_C2_C3_C4_Data)
clr.w $14(a0)
 
subq.w #8,$C(a0)
 
move.b #$E0,d0
 
bsr.w PlaySound_Special ; fade out music
 
</asm>
 
and change it to:
 
<asm>
 
Obj81_GetUp: ; XREF: Obj81_Animate
 
addq.b #2,$24(a0)
 
move.l #Map_Sonic,4(a0)
 
move.w #$780,2(a0)
 
move.b #$1E,$1C(a0) ; use "getting up" animation
 
clr.w $14(a0)
 
subq.w #8,$C(a0)
 
move.b #$E1,d0
 
bsr.w PlaySound_Special ; fade out music
 
</asm>
 
okay, that should fix all the music fades between screens, but still there are yet more fixes to be made.
 
  
==Fixing the SEGA! sound==
+
; =============== S U B R O U T I N E =======================================
Want to hear the SEGA! pcm when you should?  Well this section is for you then.  Go find:
+
;
<asm>
+
;sub_11B
Sega_WaitPallet:
+
zUpdateEverything:
move.b #2,($FFFFF62A).w
+
call zPauseUnpause ; Pause/unpause according to M68K input
bsr.w DelayProgram
+
call zUpdateSFXTracks ; Do SFX tracks
bsr.w PalCycle_Sega
 
bne.s Sega_WaitPallet
 
  
move.b #$E1,d0
+
;loc_121
bsr.w PlaySound_Special ; play "SEGA" sound
+
zUpdateMusic:
move.b #$14,($FFFFF62A).w
+
call TempoWait ; Delay song tracks as appropriate for main tempo mod
bsr.w DelayProgram
+
call zDoMusicFadeOut ; Check if music should be faded out and fade if needed
move.w #$1E,($FFFFF614).w
+
call zDoMusicFadeIn ; Check if music should be faded in and fade if needed
</asm>
+
ld a, (zFadeToPrevFlag) ; Get fade-to-prev flag
and change it to:
+
cp mus_ExtraLife-Mus__First ; Is it still 1-Up?
<asm>
+
jr nz, .check_fade_in ; Branch if not
Sega_WaitPallet:
+
ld a, (zMusicNumber) ; Get next music to play
move.b #2,($FFFFF62A).w
+
cp mus_ExtraLife ; Is it another 1-Up?
bsr.w DelayProgram
+
jr z, .clr_queue ; Branch if yes
bsr.w PalCycle_Sega
+
cp Mus__End-Mus__First ; Is it music (except credits song)?
bne.s Sega_WaitPallet
+
jr c, .clr_sfx ; Branch if not
  
move.b #$FF,d0
+
.clr_queue:
bsr.w PlaySound_Special ; play "SEGA" sound
+
xor a ; a = 0
move.b #$14,($FFFFF62A).w
+
ld (zMusicNumber), a ; Clear queue entry
bsr.w DelayProgram
 
move.w #$1E,($FFFFF614).w
 
</asm>
 
That should fix the sega pcm sample on the sega logo screen.
 
  
==Fixing the Title Screen music==
+
.clr_sfx:
Want to get rid of that weird sound effect on the title screen?  Then we need to fix the music. Find:
+
xor a ; a = 0
<asm>
+
ld (zSFXNumber0), a ; Clear first queued SFX
move.b #$8A,d0 ; play title screen music
+
ld (zSFXNumber1), a ; Clear second queued SFX
bsr.w PlaySound_Special
+
jr .update_music
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 #7,d1
 
  
Title_ClrObjRam2:
+
;loc_149
</asm>
+
.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
  
and change it to:
+
.update_music:
<asm>
+
ld a, (zSongBank) ; Get bank ID for music
move.b #$25,d0 ; play title screen music
+
bankswitch2 ; Bank switch to it
bsr.w PlaySound_Special
+
xor a ; a = 0
move.b #0,($FFFFFFFA).w ; disable debug mode
+
ld (zUpdatingSFX), a ; Updating music
move.w #$178,($FFFFF614).w ; run title screen for $178 frames
+
ld a, (zFadeToPrevFlag) ; Get fade-to-previous flag
lea ($FFFFD080).w,a1
+
cp 0FFh ; Is it 0FFh?
moveq #0,d0
+
call z, zFadeInToPrevious ; Fade to previous if yes
move.w #7,d1
+
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
  
Title_ClrObjRam2:
+
; =============== S U B R O U T I N E =======================================
</asm>
+
;
 +
;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
  
that fixes the title screen's music so that it plays sonic 3 title screen music, or you can pick another music if you so choose.
+
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
  
==Fixing the Special Stage music==
+
ld a, (zTempoSpeedup) ; Get tempo speed-up value
now that the title screen plays the right music, lets fix the special stage music. We simply want to locate:
+
or a ; Is music sped up?
<asm>
+
ret z ; Return if not
SS_ClrNemRam:
+
ld a, (zSpeedupTimeout) ; Get extra tempo timeout
move.l d0,(a1)+
+
or a ; Has it expired?
dbf d1,SS_ClrNemRam ; clear Nemesis buffer
+
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
  
clr.b ($FFFFF64E).w
 
clr.w ($FFFFFE02).w
 
moveq #$A,d0
 
bsr.w PalLoad1 ; load special stage pallet
 
jsr (SS_Load).l
 
move.l #0,($FFFFF700).w
 
move.l #0,($FFFFF704).w
 
move.b #9,($FFFFD000).w ; load special stage Sonic object
 
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
 
moveq #6,d0
 
lsl.w #2,d0
 
movea.l (a1,d0.w),a1
 
move.b 1(a1),($FFFFF792).w
 
subq.b #1,($FFFFF792).w
 
clr.w ($FFFFFE20).w
 
clr.b ($FFFFFE1B).w
 
move.w #0,($FFFFFE08).w
 
move.w #1800,($FFFFF614).w
 
tst.b ($FFFFFFE2).w ; has debug cheat been entered?
 
beq.s SS_NoDebug ; if not, branch
 
btst #6,($FFFFF604).w ; is A button pressed?
 
beq.s SS_NoDebug ; if not, branch
 
move.b #1,($FFFFFFFA).w ; enable debug mode
 
</asm>
 
and change it to:
 
<asm>
 
SS_ClrNemRam:
 
move.l d0,(a1)+
 
dbf d1,SS_ClrNemRam ; clear Nemesis buffer
 
  
clr.b ($FFFFF64E).w
+
; =============== S U B R O U T I N E =======================================
clr.w ($FFFFFE02).w
+
; Updates FM or PSG track.
moveq #$A,d0
+
;
bsr.w PalLoad1 ; load special stage pallet
+
;sub_1E9
jsr (SS_Load).l
+
zUpdateFMorPSGTrack:
move.l #0,($FFFFF700).w
+
bit 7, (ix+zTrack.VoiceControl) ; Is this a PSG channel?
move.l #0,($FFFFF704).w
+
jp nz, zUpdatePSGTrack ; Branch if yes
move.b #9,($FFFFD000).w ; load special stage Sonic object
+
if fix_sndbugs
bsr.w PalCycle_SS
+
dec (ix+zTrack.DurationTimeout) ; Run note timer
clr.w ($FFFFF780).w ; set stage angle to "upright"
+
else
move.w #$40,($FFFFF782).w ; set stage rotation speed
+
call zTrackRunTimer ; Run note timer
move.w #$1C,d0
+
endif
bsr.w PlaySound ; play special stage BG music
+
jr nz, .note_going ; Branch if note hasn't expired yet
move.w #0,($FFFFF790).w
+
call zGetNextNote ; Get next note for FM track
lea (Demo_Index).l,a1
+
bit 4, (ix+zTrack.PlaybackControl) ; Is track resting?
moveq #6,d0
+
ret nz ; Return if yes
lsl.w #2,d0
+
call zPrepareModulation ; Initialize modulation
movea.l (a1,d0.w),a1
+
call zUpdateFreq ; Add frequency displacement to frequency
move.b 1(a1),($FFFFF792).w
+
call zDoModulation ; Apply modulation
subq.b #1,($FFFFF792).w
+
call zFMSendFreq ; Send frequency to YM2612
clr.w ($FFFFFE20).w
+
jp zFMNoteOn ; Note on on all operators
clr.b ($FFFFFE1B).w
+
; ---------------------------------------------------------------------------
move.w #0,($FFFFFE08).w
+
.note_going:
move.w #1800,($FFFFF614).w
+
bit 4, (ix+zTrack.PlaybackControl) ; Is track resting?
tst.b ($FFFFFFE2).w ; has debug cheat been entered?
+
ret nz ; Return if yes
beq.s SS_NoDebug ; if not, branch
+
call zDoFMVolEnv ; Do FM volume envelope effects for track
btst #6,($FFFFF604).w ; is A button pressed?
+
ld a, (ix+zTrack.NoteFillTimeout) ; Get note fill timeout
beq.s SS_NoDebug ; if not, branch
+
or a ; Is timeout either not running or expired?
move.b #1,($FFFFFFFA).w ; enable debug mode
+
jr z, .keep_going ; Branch if yes
</asm>
+
dec (ix+zTrack.NoteFillTimeout) ; Update note fill timeout
now we have sonic 3 special stage music on the sonic 1 special stage.  Or you could choose a different song of your choice.
+
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
  
==Fixing the Ending Sequence music==
 
now time to fix the music for the ending sequence.  Find:
 
<asm>
 
End_LoadData:
 
moveq #$1C,d0
 
bsr.w RunPLC_ROM ; load ending sequence patterns
 
jsr (Hud_Base).l
 
bsr.w LevelSizeLoad
 
bsr.w DeformBgLayer
 
bset #2,($FFFFF754).w
 
bsr.w MainLoadBlockLoad
 
bsr.w LoadTilesFromStart
 
move.l #Col_GHZ,($FFFFF796).w ; load collision index
 
move #$2300,sr
 
lea (Kos_EndFlowers).l,a0 ; load extra flower patterns
 
lea ($FFFF9400).w,a1 ; RAM address to buffer the patterns
 
bsr.w KosDec
 
moveq #3,d0
 
bsr.w PalLoad1 ; load Sonic"s pallet
 
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
 
</asm>
 
and change it to:
 
<asm>
 
End_LoadData:
 
moveq #$1C,d0
 
bsr.w RunPLC_ROM ; load ending sequence patterns
 
jsr (Hud_Base).l
 
bsr.w LevelSizeLoad
 
bsr.w DeformBgLayer
 
bset #2,($FFFFF754).w
 
bsr.w MainLoadBlockLoad
 
bsr.w LoadTilesFromStart
 
move.l #Col_GHZ,($FFFFF796).w ; load collision index
 
move #$2300,sr
 
lea (Kos_EndFlowers).l,a0 ; load extra flower patterns
 
lea ($FFFF9400).w,a1 ; RAM address to buffer the patterns
 
bsr.w KosDec
 
moveq #3,d0
 
bsr.w PalLoad1 ; load Sonic"s pallet
 
move.w #$32,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
 
</asm>
 
that makes it so that sonic 3 ending sequence music plays.  Or you could choose a different music.
 
  
==Fixing the Credits music==
+
; =============== S U B R O U T I N E =======================================
time to fix the credits music, this time it needs to be fixed in multiple places. First we find:
+
; Uploads track's frequency to YM2612.
<asm>
+
;
LevSel_Credits: ; XREF: LevelSelect
+
; Input:   ix    Pointer to track RAM
move.b #$1C,($FFFFF600).w ; set screen mode to $1C (Credits)
+
;         hl    Frequency to upload
move.b #$91,d0
+
;         de    For FM3 in special mode, pointer to extra FM3 frequency data (never correctly set)
bsr.w PlaySound_Special ; play credits music
+
; Output: a    Trashed
move.w #0,($FFFFFFF4).w
+
;         bc    Trashed
rts
+
;         hl    Trashed
</asm>
+
;         de    Increased by 8
and change it to:
+
;
<asm>
+
;sub_22B
LevSel_Credits: ; XREF: LevelSelect
+
zFMSendFreq:
move.b #$1C,($FFFFF600).w ; set screen mode to $1C (Credits)
+
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding this track?
move.b #$26,d0
+
ret nz ; Return if yes
bsr.w PlaySound_Special ; play credits music
+
bit 0, (ix+zTrack.PlaybackControl) ; Is track in special mode (FM3 only)?
move.w #0,($FFFFFFF4).w
+
jp nz, .special_mode ; Branch if yes
rts
 
</asm>
 
then we find:
 
<asm>
 
End_MainLoop:
 
bsr.w PauseGame
 
move.b #$18,($FFFFF62A).w
 
bsr.w DelayProgram
 
addq.w #1,($FFFFFE04).w
 
bsr.w End_MoveSonic
 
jsr (ObjectsLoad).l
 
bsr.w DeformBgLayer
 
jsr (BuildSprites).l
 
jsr (ObjPosLoad).l
 
bsr.w PalCycle_Load
 
bsr.w OscillateNumDo
 
bsr.w ChangeRingFrame
 
cmpi.b #$18,($FFFFF600).w ; is scene number $18 (ending)?
 
beq.s loc_52DA ; if yes, branch
 
move.b #$1C,($FFFFF600).w ; set scene to $1C (credits)
 
move.b #$91,d0
 
bsr.w PlaySound_Special ; play credits music
 
move.w #0,($FFFFFFF4).w ; set credits index number to 0
 
rts
 
</asm>
 
and change it to:
 
<asm>
 
End_MainLoop:
 
bsr.w PauseGame
 
move.b #$18,($FFFFF62A).w
 
bsr.w DelayProgram
 
addq.w #1,($FFFFFE04).w
 
bsr.w End_MoveSonic
 
jsr (ObjectsLoad).l
 
bsr.w DeformBgLayer
 
jsr (BuildSprites).l
 
jsr (ObjPosLoad).l
 
bsr.w PalCycle_Load
 
bsr.w OscillateNumDo
 
bsr.w ChangeRingFrame
 
cmpi.b #$18,($FFFFF600).w ; is scene number $18 (ending)?
 
beq.s loc_52DA ; if yes, branch
 
move.b #$1C,($FFFFF600).w ; set scene to $1C (credits)
 
move.b #$26,d0
 
bsr.w PlaySound_Special ; play credits music
 
move.w #0,($FFFFFFF4).w ; set credits index number to 0
 
rts
 
</asm>
 
  
That fixes the credits music with sonic 3 credits music, otherwise you could use a different song if you choose.
+
.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
  
==Fixing Special Event sound==
+
; DANGER! de is unset here, and could be pointing anywhere! Luckily,
time to fix sound for events that only happen in certain situations, i.e. end of level, game over / time over, extra life and such.  first we will start with Game Over / Time Over, go and find:
+
; only reads are performed from it.
<asm>
+
.loop:
loc_138C2:
+
push bc ; Save bc
move.w #$8F,d0
+
ld a, (hl) ; a = register selector
jsr (PlaySound).l ; play game over music
+
inc hl ; Advance pointer
moveq #3,d0
+
push hl ; Save hl
jmp (LoadPLC).l ; load game over patterns
+
ex de, hl ; Exchange de and hl
</asm>
+
ld c, (hl) ; Get byte from whatever the hell de was pointing to
and change it to:
+
inc hl ; Advance pointer
<asm>
+
ld b, (hl) ; Get byte from whatever the hell de was pointing to
loc_138C2:
+
inc hl ; Advance pointer
move.w #$27,d0
+
ex de, hl ; Exchange de and hl
jsr (PlaySound).l ; play game over music
+
ld l, (ix+zTrack.FreqLow) ; l = low byte of track frequency
moveq #3,d0
+
ld h, (ix+zTrack.FreqHigh) ; h = high byte of track frequency
jmp (LoadPLC).l ; load game over patterns
+
add hl, bc ; hl = full frequency for operator
</asm>
+
push af ; Save af
that causes the Game Over and Time Over screens to use its sonic 3 version or you could choose a different song if you wish. Now we will go on to End of level Results. Find:
+
ld c, h ; High byte of frequency
<asm>
+
call zWriteFMI ; Sent it to YM2612
move.w ($FFFFFE20).w,d0
+
pop af ; Restore af
mulu.w #10,d0 ; multiply rings by 10
+
sub 4 ; Move on to frequency LSB
move.w d0,($FFFFF7D4).w ; set rings bonus
+
ld c, l ; Low byte of frequency
move.w #$8E,d0
+
call zWriteFMI ; Sent it to YM2612
jsr (PlaySound_Special).l ; play end-of-level music
+
pop hl ; Restore hl
lea ($FFFFD000).w,a1
+
pop bc ; Restore bc
moveq #0,d0
+
djnz .loop ; Loop for all operators
move.w #$7FF,d1
+
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
  
SS_EndClrObjRam:
+
;loc_2CE
</asm>
+
zGotNoteFreq:
and change it to:
+
ld (ix+zTrack.FreqLow), l ; Store low byte of note frequency
<asm>
+
ld (ix+zTrack.FreqHigh), h ; Store high byte of note frequency
move.w ($FFFFFE20).w,d0
 
mulu.w #10,d0 ; multiply rings by 10
 
move.w d0,($FFFFF7D4).w ; set rings bonus
 
move.w #$29,d0
 
jsr (PlaySound_Special).l ; play end-of-level music
 
lea ($FFFFD000).w,a1
 
moveq #0,d0
 
move.w #$7FF,d1
 
  
SS_EndClrObjRam:
+
;loc_2D4
</asm>
+
zGetNoteDuration:
now find:
+
ld a, (de) ; Get duration from the track
<asm>
+
or a ; Is it an actual duration?
loc_ECD0:
+
jp p, zGotNoteDuration ; Branch if yes
add.w d0,d0
+
if fix_sndbugs=0
move.w TimeBonuses(pc,d0.w),($FFFFF7D2).w ; set time bonus
+
ld a, (ix+zTrack.SavedDuration) ; Get saved duration (zFinishTrackUpdate should do it...)
move.w ($FFFFFE20).w,d0 ; load number of rings
+
ld (ix+zTrack.DurationTimeout), a ; Set it as next timeout duration (zFinishTrackUpdate should do it...)
mulu.w #10,d0 ; multiply by 10
+
endif
move.w d0,($FFFFF7D4).w ; set ring bonus
+
jr zFinishTrackUpdate
move.w #$8E,d0
+
; ---------------------------------------------------------------------------
jsr (PlaySound_Special).l ; play "Sonic got through" music
+
zApplyPitchSlide:
</asm>
+
if fix_sndbugs=0
and change it to:
+
; Unused/dead code in S3/S&K/S3D; this is code for pitch slides
<asm>
+
; in the Battletoads sound driver.
loc_ECD0:
+
ld a, (de) ; Get new pitch slide value from track
add.w d0,d0
+
inc de ; Advance pointer
move.w TimeBonuses(pc,d0.w),($FFFFF7D2).w ; set time bonus
+
ld (ix+zTrack.Unk11h), a ; Store pitch slide
move.w ($FFFFFE20).w,d0 ; load number of rings
+
jr zGetRawDuration
mulu.w #10,d0 ; multiply by 10
+
endif
move.w d0,($FFFFF7D4).w ; set ring bonus
+
; ---------------------------------------------------------------------------
move.w #$29,d0
+
;loc_2E8
jsr (PlaySound_Special).l ; play "Sonic got through" music
+
;zAlternateSMPS
</asm>
+
zAltFreqMode:
that fixes the end of level and end of special stage results music by using modern version, but you could use a different song if you wish.  Next we will fix the extra life sound, so find:
+
; Setting bit 3 on zTrack.PlaybackControl puts the song in a weird mode.
<asm>
+
;
loc_9CA4:
+
; This weird mode has literal frequencies and durations on the track.
addq.b #1,($FFFFFE12).w ; add 1 to the number of lives you have
+
; Each byte on the track is either a coordination flag (0E0h to 0FFh) or
addq.b #1,($FFFFFE1C).w ; add 1 to the lives counter
+
; the high byte of a frequency. For the latter case, the following byte
move.w #$88,d0 ; play extra life music
+
; is then the low byte of this same frequency.
</asm>
+
; If the frequency is nonzero, the (sign extended) key displacement is
and change it to:
+
; simply *added* to this frequency.
<asm>
+
; After the frequency, there is then a byte that is unused.
loc_9CA4:
+
; Finally, there is a raw duration byte following.
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
+
; To put the track in this mode, coord. flag 0FDh can be used; if the
move.w #$2A,d0 ; play extra life music
+
; parameter byte is 1, the mode is toggled on. To turn the mode off,
</asm>
+
; coord. flag 0FDh can be used with a parameter != 1.
now find:
+
ld h, a ; h = byte from track
<asm>
+
ld a, (de) ; a = next byte from track
ExtraLife:
+
inc de ; Advance pointer
addq.b #1,($FFFFFE12).w ; add 1 to the number of lives you have
+
ld l, a ; l = last byte read from track
addq.b #1,($FFFFFE1C).w ; add 1 to the lives counter
+
or h ; Is hl nonzero?
move.w #$88,d0
+
jr z, .got_zero ; Branch if not
jmp (PlaySound).l ; play extra life music
+
ld a, (ix+zTrack.Transpose) ; a = transposition
</asm>
+
if fix_sndbugs
and change it to:
+
ld c, a ; bc = sign extension of key displacement
<asm>
+
rla ; Carry contains sign of key displacement
ExtraLife:
+
sbc a, a ; a = 0 or -1 if carry is 0 or 1
addq.b #1,($FFFFFE12).w ; add 1 to the number of lives you have
+
ld b, a ; bc = sign extension of key displacement
addq.b #1,($FFFFFE1C).w ; add 1 to the lives counter
+
else
move.w #$2A,d0
+
ld b, 0 ; b = 0
jmp (PlaySound).l ; play extra life music
+
or a ; Is a negative?
</asm>
+
jp p, .did_sign_extend ; Branch if not
now find:
+
dec b ; Set b to -1
<asm>
+
 
Obj09_Get1Up:
+
.did_sign_extend:
addq.b #1,($FFFFFE12).w ; add 1 to number of lives
+
ld c, a ; bc = sign extension of key displacement
addq.b #1,($FFFFFE1C).w ; add 1 to lives counter
+
endif
move.w #$88,d0
+
add hl, bc ; hl += key displacement
jsr (PlaySound).l ; play extra life music
+
 
moveq #0,d4
+
.got_zero:
rts
+
ld (ix+zTrack.FreqLow), l ; Store low byte of note frequency
</asm>
+
ld (ix+zTrack.FreqHigh), h ; Store high byte of note frequency
and change it to:
+
if fix_sndbugs
<asm>
+
inc de ; Skip the useless pitch slide byte
Obj09_Get1Up:
+
else
addq.b #1,($FFFFFE12).w ; add 1 to number of lives
+
ld a, (de) ; Get pitch slide value from the track
addq.b #1,($FFFFFE1C).w ; add 1 to lives counter
+
inc de ; Advance to next byte in track
move.w #$2A,d0
+
ld (ix+zTrack.Unk11h), a ; Store pitch slide
jsr (PlaySound).l ; play extra life music
+
endif
moveq #0,d4
+
;loc_306
rts
+
zGetRawDuration:
</asm>
+
ld a, (de) ; Get raw duration from track
That fixes the extra life sound with the sonic3 version, though I am sure you could use a different one in a different slot if such thing exists.  Time to fix the invincibinity music, so find:
+
 
<asm>
+
;loc_307
Obj2E_ChkInvinc:
+
zGotNoteDuration:
cmpi.b #5,d0 ; does monitor contain invincibility?
+
inc de ; Advance to next byte in track
bne.s Obj2E_ChkRings
+
 
move.b #1,($FFFFFE2D).w ; make Sonic invincible
+
;loc_308
move.w #$4B0,($FFFFD032).w ; time limit for the power-up
+
zStoreDuration:
move.b #$38,($FFFFD200).w ; load stars object ($3801)
+
call zComputeNoteDuration ; Multiply note by tempo divider
move.b #1,($FFFFD21C).w
+
ld (ix+zTrack.SavedDuration), a ; Store it for next note
move.b #$38,($FFFFD240).w ; load stars object ($3802)
+
 
move.b #2,($FFFFD25C).w
+
;loc_30E
move.b #$38,($FFFFD280).w ; load stars object ($3803)
+
zFinishTrackUpdate:
move.b #3,($FFFFD29C).w
+
ld (ix+zTrack.DataPointerLow), e ; Save low byte of current location in song
move.b #$38,($FFFFD2C0).w ; load stars object ($3804)
+
ld (ix+zTrack.DataPointerHigh), d ; Save high byte of current location in song
move.b #4,($FFFFD2DC).w
+
ld a, (ix+zTrack.SavedDuration) ; Get current saved duration
tst.b ($FFFFF7AA).w ; is boss mode on?
+
ld (ix+zTrack.DurationTimeout), a ; Set it as duration timeout
bne.s Obj2E_NoMusic ; if yes, branch
+
bit 1, (ix+zTrack.PlaybackControl) ; Is 'do not attack next note' flag set?
move.w #$87,d0
+
ret nz ; Return if yes
jmp (PlaySound).l ; play invincibility music
+
xor a ; Clear a
</asm>
+
ld (ix+zTrack.ModulationSpeed), a ; Clear modulation speed
and change it to:
+
ld (ix+zTrack.ModulationValLow), a ; Clear low byte of accumulated modulation
<asm>
+
ld (ix+zTrack.VolEnv), a ; Reset volume envelope
Obj2E_ChkInvinc:
+
ld a, (ix+zTrack.NoteFillMaster) ; Get master note fill
cmpi.b #5,d0 ; does monitor contain invincibility?
+
ld (ix+zTrack.NoteFillTimeout), a ; Set note fill timeout
bne.s Obj2E_ChkRings
+
ret
move.b #1,($FFFFFE2D).w ; make Sonic invincible
+
; End of function zGetNextNote
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
+
; =============== S U B R O U T I N E =======================================
move.b #$38,($FFFFD240).w ; load stars object ($3802)
+
; This routine multiplies the note duration by the tempo divider. This can
move.b #2,($FFFFD25C).w
+
; easily overflow, as the result is stored in a byte.
move.b #$38,($FFFFD280).w ; load stars object ($3803)
+
;
move.b #3,($FFFFD29C).w
+
; Input:  a   Note duration
move.b #$38,($FFFFD2C0).w ; load stars object ($3804)
+
; Output: a   Final note duration
move.b #4,($FFFFD2DC).w
+
;          b    zero
tst.b ($FFFFF7AA).w ; is boss mode on?
+
;          c    Damaged
bne.s Obj2E_NoMusic ; if yes, branch
+
;sub_330
move.w #$2C,d0
+
zComputeNoteDuration:
jmp (PlaySound).l ; play invincibility music
+
ld b, (ix+zTrack.TempoDivider) ; Get tempo divider for this track
</asm>
+
dec b ; Make it into a loop counter
that fixes the invincibility sound with the sonic3 version, but you could choose a different one if you wish. Next we will fix the get a continue sound, so find:
+
ret z ; Return if it was 1
<asm>
+
ld c, a ; c = a
Obj7E_Continue: ; XREF: Obj7E_Index
+
 
move.b #4,($FFFFD6DA).w
+
.loop:
move.b #$14,($FFFFD6E4).w
+
add a, c ; a += c
move.w #$BF,d0
+
djnz .loop ; Loop
jsr (PlaySound_Special).l ; play continues music
+
ret
addq.b #2,$24(a0)
+
; End of function zComputeNoteDuration
move.w #360,$1E(a0) ; set time delay to 6 seconds
+
 
bra.w DisplaySprite
+
 
</asm>
+
; =============== S U B R O U T I N E =======================================
and change it to:
+
; Reduces note duration timeout for current track.
<asm>
+
;
Obj7E_Continue: ; XREF: Obj7E_Index
+
; Input:   ix  Track data
move.b #4,($FFFFD6DA).w
+
; Output:  a    New duration
move.b #$14,($FFFFD6E4).w
+
;sub_33A
move.w #$AC,d0
+
if fix_sndbugs=0
jsr (PlaySound_Special).l ; play continues music
+
zTrackRunTimer:
addq.b #2,$24(a0)
+
ld a, (ix+zTrack.DurationTimeout) ; Get track duration timeout
move.w #360,$1E(a0) ; set time delay to 6 seconds
+
dec a ; Decrement it...
bra.w DisplaySprite
+
ld (ix+zTrack.DurationTimeout), a ; ... and save new value
</asm>
+
ret
then find:
+
; End of function zTrackRunTimer
<asm>
+
endif
Obj09_GetCont:
+
 
jsr (CollectRing).l
+
; ---------------------------------------------------------------------------
cmpi.w #50,($FFFFFE20).w ; check if you have 50 rings
+
;loc_342
bcs.s Obj09_NoCont
+
zFMNoteOn:
bset #0,($FFFFFE1B).w
+
ld a, (ix+zTrack.FreqLow) ; Get low byte of note frequency
bne.s Obj09_NoCont
+
or (ix+zTrack.FreqHigh) ; Is the note frequency zero?
addq.b #1,($FFFFFE18).w ; add 1 to number of continues
+
ret z ; Return if yes
move.w #$BF,d0
+
ld a, (ix+zTrack.PlaybackControl) ; Get playback control byte for track
jsr (PlaySound).l ; play extra continue sound
+
if fix_sndbugs
</asm>
+
and 16h ; Is either bit 4 ("track at rest") or 2 ("SFX overriding this track") or bit 1 ("do not attack next note") set?
and change it to:
+
else
<asm>
+
and 6 ; Is either bit 1 ("do not attack next note") or 2 ("SFX overriding this track") set?
Obj09_GetCont:
+
endif
jsr (CollectRing).l
+
ret nz ; Return if yes
cmpi.w #50,($FFFFFE20).w ; check if you have 50 rings
+
ld a, (ix+zTrack.VoiceControl) ; Get voice control byte from track
bcs.s Obj09_NoCont
+
or 0F0h ; We want only the FM channel assignment bits
bset #0,($FFFFFE1B).w
+
ld c, a ; Key on for all operators
bne.s Obj09_NoCont
+
ld a, 28h ; Select key on/of register
addq.b #1,($FFFFFE18).w ; add 1 to number of continues
+
if fix_sndbugs
move.w #$AC,d0
+
jp zWriteFMI ; Send command to YM2612
jsr (PlaySound).l ; play extra continue sound
+
else
</asm>
+
call zWriteFMI ; Send command to YM2612
that fixes the extra continue sound that plays on both the end result screen and in the special stage.  How about fixing the music for the continue screen itself?  That is what we are doing next, so find:
+
ret
<asm>
+
endif
jsr (ContScrCounter).l ; run countdown (start from 10)
+
; ---------------------------------------------------------------------------
moveq #$12,d0
+
 
bsr.w PalLoad1 ; load continue screen pallet
+
; =============== S U B R O U T I N E =======================================
move.b #$90,d0
+
; Writes reg/data pair to register 28h (key on/off) if track active
bsr.w PlaySound ; play continue music
+
;
</asm>
+
; Input:   ix  Track data
and change it to:
+
; Output: a    Damaged
<asm>
+
;          c    Damaged
jsr (ContScrCounter).l ; run countdown (start from 10)
+
;sub_35B
moveq #$12,d0
+
zKeyOffIfActive:
bsr.w PalLoad1 ; load continue screen pallet
+
ld a, (ix+zTrack.PlaybackControl) ; Get playback control byte for track
move.b #$28,d0
+
and 6 ; Is either bit 1 ("do not attack next note") or 2 ("SFX overriding this track") set?
bsr.w PlaySound ; play continue music
+
ret nz ; Return if yes
</asm>
+
; End of function zKeyOffIfActive
that fixes the continue screen music.  Though you could choose a different song if you wish.  Time to fix the special stage entry sound, so find:
+
 
<asm>
+
; =============== S U B R O U T I N E =======================================
SpecialStage: ; XREF: GameModeArray
+
; Writes reg/data pair to register 28h (key on/off)
move.w #$CA,d0
+
;
bsr.w PlaySound_Special ; play special stage entry sound
+
; Input:  ix  Track data
</asm>
+
; Output:  a    Damaged
and change it to:
+
;          c    Damaged
<asm>
+
;loc_361
SpecialStage: ; XREF: GameModeArray
+
zKeyOff:
move.w #$AF,d0
+
ld c, (ix+zTrack.VoiceControl) ; Get voice control byte for track (this will turn off all operators as high nibble = 0)
bsr.w PlaySound_Special ; play special stage entry sound
+
bit 7, c ; Is this a PSG track?
</asm>
+
ret nz ; Return if yes
then find:
+
; End of function zKeyOff
<asm>
+
 
SS_NormalExit:
+
; =============== S U B R O U T I N E =======================================
bsr.w PauseGame
+
; Writes reg/data pair to register 28h (key on/off)
move.b #$C,($FFFFF62A).w
+
;
bsr.w DelayProgram
+
; Input:   c    Data to write
jsr (ObjectsLoad).l
+
; Output:  a    Damaged
jsr (BuildSprites).l
+
;loc_367
bsr.w RunPLC_RAM
+
zKeyOnOff:
tst.w ($FFFFFE02).w
+
ld a, 28h ; Write to KEY ON/OFF port
beq.s SS_NormalExit
+
if fix_sndbugs
tst.l ($FFFFF680).w
+
res 6, (ix+zTrack.PlaybackControl) ; From Dyna Brothers 2, but in a better place; clear flag to sustain frequency
bne.s SS_NormalExit
+
jp zWriteFMI ; Send it
move.w #$CA,d0
+
else
bsr.w PlaySound_Special ; play special stage exit sound
+
call zWriteFMI ; Send it
bsr.w Pal_MakeFlash
+
ret
rts
+
endif
</asm>
+
; End of function zKeyOnOff
and change it to:
+
 
<asm>
+
; =============== S U B R O U T I N E =======================================
SS_NormalExit:
+
; Performs volume envelope effects in FM channels.
bsr.w PauseGame
+
;
move.b #$C,($FFFFF62A).w
+
; Input:  ix    Pointer to track RAM
bsr.w DelayProgram
+
; Output: a    Trashed
jsr (ObjectsLoad).l
+
;          bc    Trashed
jsr (BuildSprites).l
+
;          de    Trashed
bsr.w RunPLC_RAM
+
;          hl    Trashed
tst.w ($FFFFFE02).w
+
;
beq.s SS_NormalExit
+
;sub_36D
tst.l ($FFFFF680).w
+
;zDoFMFlutter
bne.s SS_NormalExit
+
zDoFMVolEnv:
move.w #$AF,d0
+
ld a, (ix+zTrack.FMVolEnv) ; Get FM volume envelope
bsr.w PlaySound_Special ; play special stage exit sound
+
or a ; Is it zero?
bsr.w Pal_MakeFlash
+
ret z ; Return if yes
rts
+
ret m ; Return if it is actually the custom SSG-EG flag
</asm>
+
dec a ; Make a into an index
on entry and exit, the special stage should now play the correct sound. Time to fix the boss music, so find:
+
ld c, zID_VolEnvPointers ; Value for volume envelope pointer table
<asm>
+
rst GetPointerTable ; hl = pointer to volume envelope table
loc_6ED0:
+
rst PointerTableOffset ; hl = pointer to volume envelope for track
move.w #$8C,d0
+
call zDoVolEnv ; a = new volume envelope
bsr.w PlaySound ; play boss music
+
ld h, (ix+zTrack.TLPtrHigh) ; h = high byte to TL data pointer
</asm>
+
ld l, (ix+zTrack.TLPtrLow) ; l = low byte to TL data pointer
and change it to:
+
ld de, zFMInstrumentTLTable ; de = pointer to FM TL register table
<asm>
+
ld b, zFMInstrumentTLTable_End-zFMInstrumentTLTable ; Number of entries
loc_6ED0:
+
ld c, (ix+zTrack.FMVolEnvMask) ; c = envelope bitmask
move.w #$19,d0
+
 
bsr.w PlaySound ; play boss music
+
.loop:
</asm>
+
push af ; Save af
then find:
+
sra c ; Divide c by 2
<asm>
+
push bc ; Save bc
loc_6F4A:
+
jr nc, .skip_reg ; Branch if c bit shifted was zero
move.w #$8C,d0
+
add a, (hl) ; Add TL value to volume envelope
bsr.w PlaySound ; play boss music
+
and 7Fh ; Strip sign bit
</asm>
+
ld c, a ; c = TL + volume envelope
and change it to:
+
ld a, (de) ; a = YM2612 register
<asm>
+
call zWriteFMIorII ; Send TL data to YM2612
loc_6F4A:
+
 
move.w #$19,d0
+
.skip_reg:
bsr.w PlaySound ; play boss music
+
pop bc ; Restore bc
</asm>
+
inc de ; Advance to next YM2612 register
then find:
+
inc hl ; Advance to next TL value
<asm>
+
pop af ; Restore af
loc_70D0:
+
djnz .loop ; Loop for all registers
move.w #$8C,d0
+
ret
bsr.w PlaySound ; play boss music
+
; End of function zDoFMVolEnv
</asm>
+
 
and change it to:
+
; =============== S U B R O U T I N E =======================================
<asm>
+
; Initializes normal modulation.
loc_70D0:
+
;
move.w #$19,d0
+
; Input:  ix    Pointer to track's RAM
bsr.w PlaySound ; play boss music
+
; Output: de    If modulation control has bit 7 set and track is to attack next note, pointer to modulation steps in track RAM
</asm>
+
;          hl    If modulation control has bit 7 set and track is to attack next note, pointer to modulation steps in track data
then find:
+
;
<asm>
+
;sub_39E
loc_7144:
+
zPrepareModulation:
move.w #$8C,d0
+
bit 7, (ix+zTrack.ModulationCtrl) ; Is modulation on?
bsr.w PlaySound ; play boss music
+
ret z ; Return if not
</asm>
+
bit 1, (ix+zTrack.PlaybackControl) ; Is 'do not attack next note' bit set?
and change it to:
+
ret nz ; Return if yes
<asm>
+
ld e, (ix+zTrack.ModulationPtrLow) ; e = low byte of pointer to modulation data
loc_7144:
+
ld d, (ix+zTrack.ModulationPtrHigh) ; d = high byte of pointer to modulation data
move.w #$8C,d0
+
push ix ; Save ix
bsr.w PlaySound ; play boss music
+
pop hl ; hl = pointer to track data
</asm>
+
ld b, 0 ; b = 0
then find:
+
ld c, zTrack.ModulationWait ; c = offset in track RAM for modulation data
<asm>
+
add hl, bc ; hl = pointer to modulation data in track RAM
loc_71EC:
+
ex de, hl ; Exchange de and hl
move.w #$19,d0
+
ldi ; *de++ = *hl++
bsr.w PlaySound ; play boss music
+
ldi ; *de++ = *hl++
</asm>
+
ldi ; *de++ = *hl++
that fixes the boss music, though you could choose different music per-level if you wish within this part of the how-to. Next we will fix the cut scene just before SBZ3, and I would choose to have that music be SBZ music, to have an effect like the cut scenes in s&k.  So find:
+
ld a, (hl) ; a = number of modulation steps
<asm>
+
srl a ; Divide by 2
Obj3A_SBZ2: ; XREF: Obj3A_ChkPos2
+
ld (de), a ; Store in track RAM
cmpi.b #4,$1A(a0)
+
xor a ; a = 0
bne.w DeleteObject
+
ld (ix+zTrack.ModulationValLow), a ; Clear low byte of accumulated modulation
addq.b #2,$24(a0)
+
ld (ix+zTrack.ModulationValHigh), a ; Clear high byte of accumulated modulation
clr.b ($FFFFF7CC).w ; unlock controls
+
ret
move.w #$8D,d0
+
; End of function zPrepareModulation
jmp (PlaySound).l ; play FZ music
+
 
</asm>
+
 
and change it to
+
; =============== S U B R O U T I N E =======================================
<asm>
+
; Applies modulation.
Obj3A_SBZ2: ; XREF: Obj3A_ChkPos2
+
;
cmpi.b #4,$1A(a0)
+
; Input:  ix    Pointer to track's RAM
bne.w DeleteObject
+
;          hl    Note frequency
addq.b #2,$24(a0)
+
; Output:
clr.b ($FFFFF7CC).w ; unlock controls
+
;    If modulation control is 80h (normal modulation):
move.w ($FFFFFF90).w,d0
+
;          hl    Final note frequency
jmp (PlaySound).l ; play level music
+
;          de    Pointer to modulation data in track RAM
</asm>
+
;          iy    Pointer to modulation data in track RAM
that causes the game to use whatever music the level started with, but now we need to fix the air counter music, so find:
+
;          bc    Unmodulated note frequency
<asm>
+
;
move.w #$92,d0
+
;    If modulation control is nonzero and not 80h (modulation envelope effects):
jsr (PlaySound).l ; play countdown music
+
;
 +
;
 +
;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_13F02:
+
;loc_70C
</asm>
+
zPlaySound:
and change it to:
+
rst GetPointerTable ; hl = pointer to SFX data table
<asm>
+
rst PointerTableOffset ; hl = pointer to SFX data
move.w #$31,d0
+
push hl ; Save hl
jsr (PlaySound).l ; play countdown music
+
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_13F02:
+
;loc_72C
</asm>
+
zSFXTrackInitLoop:
this uses the sonic 3 version of the underwater counter music, or you can use a custom one in a location of your own choice.  Now lets fix the sound effects.
+
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
  
==Fixing the sound effects so that they sound right, or at least close to right==
+
if fix_sndbugs=0
So now we have all the music and a few sounds fixed, but what about the general sound effects (A0 - CF in sonic 1).  Sound effects in sonic 3 start at 33 and end at DC, so lets fix them in sonic 1 order, starting with the jump sound, so find:
+
ld a, (zUpdatingSFX) ; Get flag
<asm>
+
or a ; Are we updating SFX?
move.w #$A0,d0
+
jr z, .normalsfx1 ; Branch if not (hint: it was cleared just below the bank switch above so... always)
jsr (PlaySound_Special).l ; play jumping sound
 
move.b #$13,$16(a0)
 
move.b #9,$17(a0)
 
btst #2,$22(a0)
 
bne.s loc_13490
 
move.b #$E,$16(a0)
 
move.b #7,$17(a0)
 
move.b #2,$1C(a0) ; use "jumping" animation
 
bset #2,$22(a0)
 
addq.w #5,$C(a0)
 
  
locret_1348E:
+
; Effectively dead code.
</asm>
+
; Analysis of the Battletoads sound driver confirms previous speculation:
and change it to:
+
; this code was meant for GHZ-like waterfall effects which were subsequently
<asm>
+
; scrapped in favor of the continuous SFX system.
move.w #$62,d0
+
; If this system were to be reimplemented, then, after the call to
jsr (PlaySound_Special).l ; play jumping sound
+
; zGetSFXChannelPointers, we would have:
move.b #$13,$16(a0)
+
; * ix = pointer to the overriding SFX track data in RAM;
move.b #9,$17(a0)
+
; * iy = pointer to the special SFX track data in RAM.
btst #2,$22(a0)
+
; * hl = pointer to the overridden music track data in RAM;
bne.s loc_13490
+
; This code would then ensure that de points to the correct RAM area for
move.b #$E,$16(a0)
+
; the writes below.
move.b #7,$17(a0)
+
pop hl ; hl = pointer to SFX track data in RAM
move.b #2,$1C(a0) ; use "jumping" animation
+
push iy ; Save iy (pointer to SFX data)
bset #2,$22(a0)
+
.normalsfx1:
addq.w #5,$C(a0)
+
endif
  
locret_1348E:
+
pop de ; de = pointer to SFX track data in RAM (unless you consider the above effectively dead code)
</asm>
+
pop hl ; hl = pointer to SFX track data
then find:
+
ldi ; *de++ = *hl++ (initial playback control)
<asm>
+
ld a, (de) ; Get the voice control byte from track RAM (to deal with SFX already there)
move.w #$A0,d0
+
cp 2 ; Is this FM3?
jsr (PlaySound_Special).l ; play jumping sound
+
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
  
Obj09_NoJump:
+
if fix_sndbugs=0
</asm>
+
; Analysis of the Battletoads sound driver confirms previous speculation:
and change it to:
+
; this code was meant for GHZ-like waterfall effects which were subsequently
<asm>
+
; scrapped in favor of the continuous SFX system.
move.w #$62,d0
+
; If this system were to be reimplemented, then, after the call to
jsr (PlaySound_Special).l ; play jumping sound
+
; 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
  
Obj09_NoJump:
+
push hl ; Save hl
</asm>
+
ld hl, (zSFXVoiceTblPtr) ; hl = pointer to voice data
the jump sound should now be what it is supposed to be, so we fix the lamp post next, so find:
 
<asm>
 
Obj79_HitLamp:
 
move.w ($FFFFD008).w,d0
 
sub.w 8(a0),d0
 
addq.w #8,d0
 
cmpi.w #$10,d0
 
bcc.w locret_16F90
 
move.w ($FFFFD00C).w,d0
 
sub.w $C(a0),d0
 
addi.w #$40,d0
 
cmpi.w #$68,d0
 
bcc.s locret_16F90
 
move.w #$A1,d0
 
jsr (PlaySound_Special).l ; play lamppost sound
 
</asm>
 
and change it to:
 
<asm>
 
Obj79_HitLamp:
 
move.w ($FFFFD008).w,d0
 
sub.w 8(a0),d0
 
addq.w #8,d0
 
cmpi.w #$10,d0
 
bcc.w locret_16F90
 
move.w ($FFFFD00C).w,d0
 
sub.w $C(a0),d0
 
addi.w #$40,d0
 
cmpi.w #$68,d0
 
bcc.s locret_16F90
 
move.w #$63,d0
 
jsr (PlaySound_Special).l ; play lamppost sound
 
</asm>
 
that makes the lamp post sound correct, so lets fix the normal damage sound.  Find:
 
<asm>
 
Hurt_ChkSpikes:
 
move.w #0,$14(a0)
 
move.b #$1A,$1C(a0)
 
move.w #$78,$30(a0)
 
move.w #$A3,d0 ; load normal damage sound
 
</asm>
 
and change it to:
 
<asm>
 
Hurt_ChkSpikes:
 
move.w #0,$14(a0)
 
move.b #$1A,$1C(a0)
 
move.w #$78,$30(a0)
 
move.w #$35,d0 ; load normal damage sound
 
</asm>
 
seems we have the hurt sound working, but now we fix the skid and stop sound, so find:
 
<asm>
 
move.w #$A4,d0
 
jsr (PlaySound_Special).l ; play stopping sound
 
  
locret_130E8:
+
if fix_sndbugs=0
</asm>
+
ld a, (zUpdatingSFX) ; Get flag
and change it to:
+
or a ; Are we updating SFX?
<asm>
+
jr z, .normalsfx2 ; Branch if not (hint: it was cleared just below the bank switch above so... always)
move.w #$36,d0
 
jsr (PlaySound_Special).l ; play stopping sound
 
  
locret_130E8:
+
; Analysis of the Battletoads sound driver confirms previous speculation:
</asm>
+
; this code was meant for GHZ-like waterfall effects which were subsequently
then find:
+
; scrapped in favor of the continuous SFX system.
<asm>
+
; If this system were to be reimplemented, then, after the call to
move.w #$A4,d0
+
; zGetSFXChannelPointers, we would have:
jsr (PlaySound_Special).l ; play stopping sound
+
; * 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
  
locret_1314E:
+
ld (ix+zTrack.VoicesLow), l ; Low byte of voice pointer
</asm>
+
ld (ix+zTrack.VoicesHigh), h ; High byte of voice pointer
and change it to:
+
call zKeyOffIfActive ; Kill channel notes
<asm>
+
if fix_sndbugs
move.w #$36,d0
+
bit 7, (ix+zTrack.VoiceControl) ; Is this an FM track?
jsr (PlaySound_Special).l ; play stopping sound
+
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
  
locret_1314E:
+
; =============== S U B R O U T I N E =======================================
</asm>
+
;
now sonic skids right, but we need to fix another sound.  Find:
+
;sub_78F
<asm>
+
zGetSFXChannelPointers:
move.w #$A5,d0
+
bit 7, c ; Is this a PSG track?
jsr (PlaySound_Special).l ; play explosion sound
+
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
  
Obj24_Animate: ; XREF: Obj24_Index
+
.get_ptrs:
</asm>
+
sub 2 ; Start table at FM3
and change it to:
+
ld (zSFXSaveIndex), a ; Save index of overridden channel
<asm>
+
push af ; Save af
move.w #$67,d0
+
ld hl, zSFXChannelData ; Pointer table for track RAM
jsr (PlaySound_Special).l ; play explosion sound
+
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
  
Obj24_Animate: ; XREF: Obj24_Index
 
</asm>
 
now find:
 
<asm>
 
move.w #$A6,d0 ; load spikes damage sound
 
  
Hurt_Sound:
+
; =============== S U B R O U T I N E =======================================
jsr (PlaySound_Special).l
+
;
moveq #-1,d0
+
;sub_7C5
rts
+
zInitFMDACTrack:
</asm>
+
ex af, af' ; Save af
and change it to:
+
xor a ; a = 0
<asm>
+
ld (de), a ; Set modulation to inactive
move.w #$37,d0 ; load spikes damage sound
+
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.
  
Hurt_Sound:
+
ld b, (zSongPSG2-zSongFM1)/zTrack.len ; Number of FM tracks +1
jsr (PlaySound_Special).l
+
endif
moveq #-1,d0
 
rts
 
</asm>
 
now find:
 
<asm>
 
move.w #$A3,d0 ; play normal death sound
 
cmpi.b #$36,(a2) ; check if you were killed by spikes
 
bne.s Kill_Sound
 
move.w #$A6,d0 ; play spikes death sound
 
  
Kill_Sound:
+
.fm_loop:
jsr (PlaySound_Special).l
+
ld a, (zHaltFlag) ; Get halt flag
</asm>
+
or a ; Is song halted?
and change it to:
+
jr nz, .set_pan ; Branch if yes
<asm>
+
bit 7, (ix+zTrack.PlaybackControl) ; Is track playing?
move.w #$35,d0 ; play normal death sound
+
jr z, .skip_fm_track ; Branch if not
cmpi.b #$36,(a2) ; check if you were killed by spikes
 
bne.s Kill_Sound
 
move.w #$37,d0 ; play spikes death sound
 
  
Kill_Sound:
+
.set_pan:
jsr (PlaySound_Special).l
+
ld c, (ix+zTrack.AMSFMSPan) ; Get track AMS/FMS/panning
</asm>
+
ld a, 0B4h ; Command to select AMS/FMS/panning register
now find:
+
call zWriteFMIorII ; Write data to YM2612
<asm>
 
loc_C294:
 
lea ($FFFFD000).w,a1
 
add.w d0,8(a1)
 
move.w d1,$14(a1)
 
move.w #0,$10(a1)
 
move.w d0,-(sp)
 
move.w #$A7,d0
 
jsr (PlaySound_Special).l ; play pushing sound
 
</asm>
 
and change it to:
 
<asm>
 
loc_C294:
 
lea ($FFFFD000).w,a1
 
add.w d0,8(a1)
 
move.w d1,$14(a1)
 
move.w #0,$10(a1)
 
move.w d0,-(sp)
 
move.w #$69,d0
 
jsr (PlaySound_Special).l ; play pushing sound
 
</asm>
 
now find:
 
<asm>
 
move.w #$A8,d0
 
jsr (PlaySound_Special).l ; play Special Stage "GOAL" sound
 
  
Obj4A_Display:
+
.skip_fm_track:
jmp (DisplaySprite).l
+
ld de, zTrack.len ; Spacing between tracks
</asm>
+
add ix, de ; Advance to next track
and change it to:
+
djnz .fm_loop ; Loop for all tracks
<asm>
 
move.w #$6A,d0
 
jsr (PlaySound_Special).l ; play Special Stage "GOAL" sound
 
  
Obj4A_Display:
+
if fix_sndbugs
jmp (DisplaySprite).l
+
ld ix, zTracksSFXStart ; Start at the start of SFX track data
</asm>
+
ld b, (zTracksSFXEnd-zTracksSFXStart)/zTrack.len ; Number of tracks
now find:
+
else
<asm>
+
; DANGER! This code goes past the end of Z80 RAM and into reserved territory!
move.w #$A8,d0
+
; By luck, it only *reads* from these areas...
jsr (PlaySound_Special).l ; play special stage GOAL sound
 
  
locret_1B60C:
+
ld ix, zTracksSFXEnd ; Start at the END of SFX track data (?)
rts
+
ld b, 7 ; But loop for 7 tracks (??)
</asm>
+
endif
and change it to:
+
.psg_loop:
<asm>
+
bit 7, (ix+zTrack.PlaybackControl) ; Is track playing?
move.w #$6A,d0
+
jr z, .skip_psg_track ; Branch if not
jsr (PlaySound_Special).l ; play special stage GOAL sound
+
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
  
locret_1B60C:
+
.skip_psg_track:
rts
+
ld de, zTrack.len ; Spacing between tracks
</asm>
+
add ix, de ; Go to next track
now find:
+
djnz .psg_loop ; Loop for all tracks
<asm>
 
Obj09_GOAL:
 
cmpi.b #$27,d0 ; is the item a "GOAL"?
 
bne.s Obj09_UPblock
 
addq.b #2,$24(a0) ; run routine "Obj09_ExitStage"
 
move.w #$A8,d0 ; change item
 
jsr (PlaySound_Special).l ; play "GOAL" sound
 
rts
 
</asm>
 
and change it to:
 
<asm>
 
Obj09_GOAL:
 
cmpi.b #$27,d0 ; is the item a "GOAL"?
 
bne.s Obj09_UPblock
 
addq.b #2,$24(a0) ; run routine "Obj09_ExitStage"
 
move.w #$6A,d0 ; change item
 
jsr (PlaySound_Special).l ; play "GOAL" sound
 
rts
 
</asm>
 
now find:
 
<asm>
 
Obj09_UPsnd:
 
move.w #$A9,d0
 
jmp (PlaySound_Special).l ; play up/down sound
 
</asm>
 
and change it to:
 
<asm>
 
Obj09_UPsnd:
 
move.w #$6B,d0
 
jmp (PlaySound_Special).l ; play up/down sound
 
</asm>
 
then find:
 
<asm>
 
Obj09_DOWNsnd:
 
move.w #$A9,d0
 
jmp (PlaySound_Special).l ; play up/down sound
 
</asm>
 
and change it to:
 
<asm>
 
Obj09_DOWNsnd:
 
move.w #$6B,d0
 
jmp (PlaySound_Special).l ; play up/down sound
 
</asm>
 
then find:
 
<asm>
 
Obj09_RevStage:
 
neg.w ($FFFFF782).w ; reverse stage rotation
 
move.w #$A9,d0
 
jmp (PlaySound_Special).l ; play sound
 
</asm>
 
and change it to:
 
<asm>
 
Obj09_RevStage:
 
neg.w ($FFFFF782).w ; reverse stage rotation
 
move.w #$6B,d0
 
jmp (PlaySound_Special).l ; play sound
 
</asm>
 
now find:
 
<asm>
 
move.w #$AA,d0
 
jmp (PlaySound_Special).l ; play splash sound
 
; ===========================================================================
 
  
Obj01_OutWater:
+
ret
</asm>
+
; End of function zPauseUnpause
and change it to:
 
<asm>
 
move.w #$6C,d0
 
jmp (PlaySound_Special).l ; play splash sound
 
; ===========================================================================
 
  
Obj01_OutWater:
+
; =============== S U B R O U T I N E =======================================
</asm>
+
; Fades out music.
then find:
+
;sub_85C
<asm>
+
zFadeOutMusic:
loc_12E0E:
+
ld a, 28h ; a = 28h
move.w #$AA,d0
+
ld (zFadeOutTimeout), a ; Set fade timeout to this (start fading out music)
jmp (PlaySound_Special).l ; play splash sound
+
ld a, 6 ; a = 6
; End of function Sonic_Water
+
ld (zFadeDelayTimeout), a ; Set fade delay timeout
</asm>
+
ld (zFadeDelay), a ; Set fade delay and fall through
and change it to:
+
 
<asm>
+
; =============== S U B R O U T I N E =======================================
loc_12E0E:
+
; Halts FM6/DAC, PSG1, PSG2, PSG3.
move.w #$57,d0
+
;sub_869
jmp (PlaySound_Special).l ; play splash sound
+
zHaltDACPSG:
; End of function Sonic_Water
+
xor a ; a = 0
</asm>
+
ld (zSongFM6_DAC), a ; Halt FM6/DAC
now find:
+
ld (zSongPSG3), a ; Halt PSG3
<asm>
+
ld (zSongPSG1), a ; Halt PSG1
Obj14_PlaySnd:
+
ld (zSongPSG2), a ; Halt PSG2
move.w #$AE,d0
+
jp zPSGSilenceAll
jsr (PlaySound_Special).l ; play lava ball sound
+
; End of function zHaltDACPSG
</asm>
+
 
and change it to:
+
 
<asm>
+
; =============== S U B R O U T I N E =======================================
Obj14_PlaySnd:
+
; Fade out music slowly.
move.w #$70,d0
+
;
jsr (PlaySound_Special).l ; play lava ball sound
+
;sub_879
</asm>
+
zDoMusicFadeOut:
then find:
+
ld hl, zFadeOutTimeout ; hl = pointer to fade timeout
<asm>
+
ld a, (hl) ; a = fade counter
Obj62_Sound:
+
or a ; Is fade counter zero?
move.w #$AE,d0
+
ret z ; Return if yes
jsr (PlaySound_Special).l ; play lava ball sound
+
call m, zHaltDACPSG ; Kill DAC and PSG channels if negative
</asm>
+
res 7, (hl) ; Clear sign bit
then change it to:
+
ld a, (zFadeDelayTimeout) ; Get fade delay timeout
<asm>
+
dec a ; Decrement it
Obj62_Sound:
+
jr z, .timer_expired ; Branch if it zero now
move.w #$70,d0
+
ld (zFadeDelayTimeout), a ; Store it back
jsr (PlaySound_Special).l ; play lava ball sound
+
ret
</asm>
+
; ---------------------------------------------------------------------------
then find:
+
.timer_expired:
<asm>
+
ld a, (zFadeDelay) ; Get fade delay
loc_1870A:
+
ld (zFadeDelayTimeout), a ; Restore counter to initial value
move.b #$1E,$29(a0)
+
if fix_sndbugs
move.w #$AE,d0
+
ld hl, zFadeOutTimeout ; (hl) = fade timeout
jsr (PlaySound_Special).l ; play lava sound
+
dec (hl) ; Decrement it
</asm>
+
else
and change it to:
+
ld a, (zFadeOutTimeout) ; a = fade timeout
<asm>
+
dec a ; Decrement it
loc_1870A:
+
ld (zFadeOutTimeout), a ; Then store it back
move.b #$1E,$29(a0)
+
endif
move.w #$70,d0
+
jp z, zStopAllSound ; Stop all music if it is zero
jsr (PlaySound_Special).l ; play lava sound
+
ld a, (zSongBank) ; a = current music bank ID
</asm>
+
bankswitch2 ; Bank switch to music bank
now find:
+
ld ix, zTracksStart ; ix = pointer to track RAM
<asm>
+
ld b, (zSongPSG1-zTracksStart)/zTrack.len ; Number of FM+DAC tracks
Obj2E_ChkShield:
+
 
cmpi.b #4,d0 ; does monitor contain a shield?
+
.loop:
bne.s Obj2E_ChkInvinc
+
inc (ix+zTrack.Volume) ; Decrease volume
move.b #1,($FFFFFE2C).w ; give Sonic a shield
+
jp p, .chk_change_volume ; If still positive, branch
move.b #$38,($FFFFD180).w ; load shield object ($38)
+
dec (ix+zTrack.Volume) ; Increase it back to minimum volume (127)
move.w #$AF,d0
+
jr .next_track
jmp (PlaySound).l ; play shield sound
+
; ---------------------------------------------------------------------------
</asm>
+
.chk_change_volume:
and change it to:
+
bit 7, (ix+zTrack.PlaybackControl) ; Is track still playing?
<asm>
+
jr z, .next_track ; Branch if not
Obj2E_ChkShield:
+
bit 2, (ix+zTrack.PlaybackControl) ; Is SFX overriding track?
cmpi.b #4,d0 ; does monitor contain a shield?
+
jr nz, .next_track ; Branch if yes
bne.s Obj2E_ChkInvinc
+
push bc ; Save bc
move.b #1,($FFFFFE2C).w ; give Sonic a shield
+
call zSendTL ; Send new volume
move.b #$38,($FFFFD180).w ; load shield object ($38)
+
pop bc ; Restore bc
move.w #$71,d0
+
 
jmp (PlaySound).l ; play shield sound
+
.next_track:
</asm>
+
ld de, zTrack.len ; Spacing between tracks
now find:
+
add ix, de ; Advance to next track
<asm>
+
djnz .loop ; Loop for all tracks
loc_15A46:
+
ret
tst.b 1(a0)
+
; End of function zDoMusicFadeOut
bpl.s locret_15A60
 
move.w ($FFFFFE04).w,d0
 
andi.w #$F,d0
 
bne.s locret_15A60
 
move.w #$B0,d0
 
jsr (PlaySound_Special).l ; play saw sound
 
</asm>
 
and change it to:
 
<asm>
 
loc_15A46:
 
tst.b 1(a0)
 
bpl.s locret_15A60
 
move.w ($FFFFFE04).w,d0
 
andi.w #$F,d0
 
bne.s locret_15A60
 
move.w #$D8,d0
 
jsr (PlaySound_Special).l ; play saw sound
 
</asm>
 
now find:
 
<asm>
 
loc_15A96:
 
tst.b 1(a0)
 
bpl.s locret_15AB0
 
move.b ($FFFFFE64).w,d0
 
cmpi.b #$18,d0
 
bne.s locret_15AB0
 
move.w #$B0,d0
 
jsr (PlaySound_Special).l ; play saw sound
 
</asm>
 
and change it to:
 
<asm>
 
loc_15A96:
 
tst.b 1(a0)
 
bpl.s locret_15AB0
 
move.b ($FFFFFE64).w,d0
 
cmpi.b #$18,d0
 
bne.s locret_15AB0
 
move.w #$D8,d0
 
jsr (PlaySound_Special).l ; play saw sound
 
</asm>
 
now find:
 
<asm>
 
move.w #$B0,d0
 
jsr (PlaySound_Special).l ; play saw sound
 
  
loc_15B02:
 
addq.l #4,sp
 
</asm>
 
and change it to:
 
<asm>
 
move.w #$D8,d0
 
jsr (PlaySound_Special).l ; play saw sound
 
  
loc_15B02:
+
; =============== S U B R O U T I N E =======================================
addq.l #4,sp
+
; Fades music in.
</asm>
+
;
now find:
+
;sub_8DF
<asm>
+
zDoMusicFadeIn:
move.w #$B0,d0
+
ld a, (zFadeInTimeout) ; Get fading timeout
jsr (PlaySound_Special).l ; play saw sound
+
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
  
loc_15B74:
+
.fm_loop:
addq.l #4,sp
+
if fix_sndbugs
</asm>
+
dec (ix+zTrack.Volume) ; Increase track volume
and change it to:
+
else
<asm>
+
ld a, (ix+zTrack.Volume) ; Get track volume
move.w #$D8,d0
+
dec a ; Increase it
jsr (PlaySound_Special).l ; play saw sound
+
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
  
loc_15B74:
+
if fix_sndbugs
addq.l #4,sp
+
ld hl, zFadeInTimeout ; Get fading timeout
</asm>
+
dec (hl) ; Decrement it
the next sound is a little different.  Sonic1 uses the same sound effect for both the shocker orb thingies and the balls of lightning in the final boss, but the sonic3 driver has a couple of sound effects that I see fitting better.  shocker orbs will use the sound used for a certain badnik in carnival night zone because it sounds better in my opinion, while the balls of lightning from final boss will use the sound used for knux being shocked in hidden palace zone.  This is how sonic 3d blast / flickies island used similar sound effects, so we will find:
+
else
<asm>
+
ld a, (zFadeInTimeout) ; Get fading timeout
Obj6E_Shock: ; XREF: Obj6E_Index
+
dec a ; Decrement it
move.w ($FFFFFE04).w,d0
+
ld (zFadeInTimeout), a ; Then store it back
and.w $34(a0),d0
+
endif
bne.s Obj6E_Animate
+
ret nz ; Return if still fading
move.b #1,$1C(a0) ; run "shocking" animation
+
ld b, (zTracksEnd-zSongPSG1)/zTrack.len ; Number of PSG tracks
tst.b 1(a0)
+
ld ix, zSongPSG1 ; ix = start of PSG RAM
bpl.s Obj6E_Animate
+
ld de, zTrack.len ; Spacing between tracks
move.w #$B1,d0
 
jsr (PlaySound_Special).l ; play electricity sound
 
</asm>
 
and change it to:
 
<asm>
 
Obj6E_Shock: ; XREF: Obj6E_Index
 
move.w ($FFFFFE04).w,d0
 
and.w $34(a0),d0
 
bne.s Obj6E_Animate
 
move.b #1,$1C(a0) ; run "shocking" animation
 
tst.b 1(a0)
 
bpl.s Obj6E_Animate
 
move.w #$79,d0
 
jsr (PlaySound_Special).l ; play cnz shock and teleport sound
 
</asm>
 
then find:
 
<asm>
 
loc_1A020:
 
move.w #$B1,d0
 
jmp (PlaySound_Special).l ; play electricity sound
 
</asm>
 
and change it to:
 
<asm>
 
loc_1A020:
 
move.w #$78,d0
 
jmp (PlaySound_Special).l ; play shock knux sound
 
</asm>
 
the next sound is a lot more like the others, it is the drowning sound.  We will locate:
 
<asm>
 
Obj0A_ReduceAir:
 
subq.w #1,($FFFFFE14).w ; subtract 1 from air remaining
 
bcc.w Obj0A_GoMakeItem ; if air is above 0, branch
 
bsr.w ResumeMusic
 
move.b #$81,($FFFFF7C8).w ; lock controls
 
move.w #$B2,d0
 
</asm>
 
and change it to:
 
<asm>
 
Obj0A_ReduceAir:
 
subq.w #1,($FFFFFE14).w ; subtract 1 from air remaining
 
bcc.w Obj0A_GoMakeItem ; if air is above 0, branch
 
bsr.w ResumeMusic
 
move.b #$81,($FFFFF7C8).w ; lock controls
 
move.w #$3B,d0
 
</asm>
 
now find:
 
<asm>
 
move.w #$B3,d0
 
jsr (PlaySound_Special).l ; play flame sound
 
  
loc_E57A:
+
.psg_loop:
</asm>
+
res 2, (ix+zTrack.PlaybackControl) ; Clear 'SFX is overriding' bit
and change it to:
+
add ix, de ; Advance to next track
<asm>
+
djnz .psg_loop ; Loop for all tracks
move.w #$48,d0
 
jsr (PlaySound_Special).l ; play flame sound
 
  
loc_E57A:
+
ld ix, zSongFM6_DAC ; ix = start of DAC/FM6 RAM
</asm>
+
res 2, (ix+zTrack.PlaybackControl) ; Clear 'SFX is overriding' bit
now find:
+
ret
<asm>
+
; End of function zDoMusicFadeIn
move.w #$B4,d0
 
jsr (PlaySound_Special).l ; play bumper sound
 
lea ($FFFFFC00).w,a2
 
moveq #0,d0
 
move.b $23(a0),d0
 
beq.s Obj47_Score
 
cmpi.b #$8A,2(a2,d0.w) ; has bumper been hit $8A times?
 
bcc.s Obj47_Display ; if yes, Sonic gets no points
 
addq.b #1,2(a2,d0.w)
 
  
Obj47_Score:
 
</asm>
 
and change it to:
 
<asm>
 
move.w #$AA,d0
 
jsr (PlaySound_Special).l ; play bumper sound
 
lea ($FFFFFC00).w,a2
 
moveq #0,d0
 
move.b $23(a0),d0
 
beq.s Obj47_Score
 
cmpi.b #$8A,2(a2,d0.w) ; has bumper been hit $8A times?
 
bcc.s Obj47_Display ; if yes, Sonic gets no points
 
addq.b #1,2(a2,d0.w)
 
  
Obj47_Score:
+
; =============== S U B R O U T I N E =======================================
</asm>
+
; Wipes music data and fades all FM, PSG and DAC channels.
and find:
+
;sub_944 zMusicFade
<asm>
+
zStopAllSound:
Obj09_BumpSnd:
+
; The following block sets to zero the z80 RAM from 1C0Dh to 1FD4h
move.w #$B4,d0
+
ld hl, zTempVariablesStart ; Starting source address for copy
jmp (PlaySound_Special).l ; play bumper sound
+
ld de, zTempVariablesStart+1 ; Starting destination address for copy
; ===========================================================================
+
if fix_sndbugs
</asm>
+
ld bc, zTempVariablesEnd-zTempVariablesStart-1 ; Length of copy
and change it to:
+
else
<asm>
+
ld bc, zTempVariablesEnd-zTempVariablesStart-1+34h ; Length of copy
Obj09_BumpSnd:
+
endif
move.w #$AA,d0
+
ld (hl), 0 ; Initial value of zero
jmp (PlaySound_Special).l ; play bumper sound
+
ldir ; while (--length) *de++ = *hl++
; ===========================================================================
 
</asm>
 
now find:
 
<asm>
 
Title_PlayRing:
 
move.b #1,(a0,d1.w) ; activate cheat
 
move.b #$B5,d0 ; play ring sound when code is entered
 
bsr.w PlaySound_Special
 
</asm>
 
and change it to:
 
<asm>
 
Title_PlayRing:
 
move.b #1,(a0,d1.w) ; activate cheat
 
move.b #$34,d0 ; play ring sound when code is entered
 
bsr.w PlaySound_Special
 
</asm>
 
then find:
 
<asm>
 
CollectRing: ; XREF: Obj25_Collect
 
addq.w #1,($FFFFFE20).w ; add 1 to rings
 
ori.b #1,($FFFFFE1D).w ; update the rings counter
 
move.w #$B5,d0 ; play ring sound
 
</asm>
 
and change it to:
 
<asm>
 
CollectRing: ; XREF: Obj25_Collect
 
addq.w #1,($FFFFFE20).w ; add 1 to rings
 
ori.b #1,($FFFFFE1D).w ; update the rings counter
 
move.w #$34,d0 ; play ring sound
 
</asm>
 
then find:
 
<asm>
 
Obj2E_RingSound:
 
move.w #$B5,d0
 
jmp (PlaySound).l ; play ring sound
 
</asm>
 
and change it to:
 
<asm>
 
Obj2E_RingSound:
 
move.w #$34,d0
 
jmp (PlaySound).l ; play ring sound
 
</asm>
 
now find:
 
<asm>
 
move.w #$B6,d0
 
jsr (PlaySound_Special).l ; play "spikes moving" sound
 
bra.s locret_CFE6
 
; ===========================================================================
 
  
loc_CFA4:
+
xor a ; a = 0
</asm>
+
ld (zTempoSpeedup), a ; Fade in normal speed
and change it to:
 
<asm>
 
move.w #$52,d0
 
jsr (PlaySound_Special).l ; play "spikes moving" sound
 
bra.s locret_CFE6
 
; ===========================================================================
 
  
loc_CFA4:
+
ld ix, zFMDACInitBytes ; Initialization data for channels
</asm>
+
ld b, 6 ; Number of FM channels
now find:
+
 
<asm>
+
.loop:
move.w #$B7,d0
+
push bc ; Save bc for loop
bsr.w PlaySound_Special ; play sound $B7 (rumbling)
+
 
 
+
if fix_sndbugs=0
loc_3D54:
+
call zFMSilenceChannel ; Silence track's channel
</asm>
+
else
and change it to:
+
; Inline it because zKeyOnOff tries to write to ix+0, which we don't want
<asm>
+
call zSetMaxRelRate
move.w #$6F,d0
+
ld a, 40h ; Set total level...
bsr.w PlaySound_Special ; play sound $6F (rumbling)
+
ld c, 7Fh ; ... to minimum envelope amplitude...
 
+
call zFMOperatorWriteLoop ; ... for all operators of this track's channel
loc_3D54:
+
ld a, 28h ; Write to KEY ON/OFF port
</asm>
+
ld c, (ix+zTrack.VoiceControl) ; Send key off
now find:
+
call zWriteFMI ; Send it
<asm>
+
endif
move.w #$B7,d0
+
 
bsr.w PlaySound_Special ; play rumbling sound
+
call zFMClearSSGEGOps ; Clears the SSG-EG operators for this channel
 
+
inc ix ; Go to next channel byte
loc_6F28:
+
inc ix ; But skip the 80h
</asm>
+
pop bc ; Restore bc for loop counter
and change it to:
+
djnz .loop ; Loop while b > 0
<asm>
+
 
move.w #$6F,d0
+
if fix_sndbugs=0
bsr.w PlaySound_Special ; play rumbling sound
+
ld b, 7 ; Unused
 
+
xor a ; a = 0
loc_6F28:
+
ld (zFadeOutTimeout), a ; Set fade timeout to zero... again
</asm>
+
endif
now find:
+
call zPSGSilenceAll ; Silence PSG
<asm>
+
ld c, 0 ; Write a zero...
move.w #$B7,d0
+
ld a, 2Bh ; ... to DAC enable register
jsr (PlaySound_Special).l ; play rumbling sound
+
call zWriteFMI ; Disable DAC
 
+
 
loc_19F10:
+
;loc_979
</asm>
+
zFM3NormalMode:
and change it to:
+
if fix_sndbugs
<asm>
+
ld c, 0 ; FM3 mode: normal mode
move.w #$6F,d0
+
else
jsr (PlaySound_Special).l ; play rumbling sound
+
xor a ; a = 0 (is 0Fh in Z80 Type 1)
 
+
ld (zFM3Settings), a ; Save FM3 settings
loc_19F10:
+
ld c, a ; FM3 mode: normal mode
</asm>
+
endif
now find:
+
ld a, 27h ; FM3 special settings
<asm>
+
call zWriteFMI ; Set it
loc_84F2:
+
jp zClearNextSound
bsr.w DisplaySprite
+
; End of function zStopAllSound
move.w #$B9,d0
+
 
jmp (PlaySound_Special).l ; play collapsing sound
+
; =============== S U B R O U T I N E =======================================
</asm>
+
; Sets the SSG-EG registers (90h+) for all operators on this track to 0.
and we change it to:
+
;
<asm>
+
; Input:  ix    Pointer to track RAM
loc_84F2:
+
; Output: a    Damaged
bsr.w DisplaySprite
+
;        b    Damaged
move.w #$59,d0
+
;        c    Damaged
jmp (PlaySound_Special).l ; play collapsing sound
+
;sub_986
</asm>
+
zFMClearSSGEGOps:
'''note: the breaking and collapsing sounds in sonic3 are the same.'''
+
ld a, 90h ; Set SSG-EG registers...
now we will find:
+
ld c, 0 ; ... set to zero (as docs say it should)...
<asm>
+
jp zFMOperatorWriteLoop ; ... for all operators of this track's channel
Obj09_GlassSnd:
+
; End of function zFMClearSSGEGOps
move.w #$BA,d0
+
 
jmp (PlaySound_Special).l ; play glass block sound
+
; =============== S U B R O U T I N E =======================================
</asm>
+
; Pauses all audio.
and change it to:
+
;loc_98D
<asm>
+
zPauseAudio:
Obj09_GlassSnd:
+
if fix_sndbugs=0
move.w #$B5,d0
+
call zPSGSilenceAll ; Redundant, as function falls-through to it anyway
jmp (PlaySound_Special).l ; play glass block sound
+
endif
</asm>
+
push bc ; Save bc
now find:
+
push af ; Save af
<asm>
+
ld b, (zSongFM4-zSongFM1)/zTrack.len ; FM1/FM2/FM3
move.w #$BB,d0
+
ld a, 0B4h ; Command to select AMS/FMS/panning register (FM1)
jsr (PlaySound_Special).l ; play door sound
+
ld c, 0 ; AMS=FMS=panning=0
 
+
 
Obj0C_Solid:
+
.loop1:
</asm>
+
push af ; Save af
and change it to:
+
call zWriteFMI ; Write reg/data pair to YM2612
<asm>
+
pop af ; Restore af
move.w #$58,d0
+
inc a ; Advance to next channel
jsr (PlaySound_Special).l ; play door sound
+
djnz .loop1 ; Loop for all channels
 
+
 
Obj0C_Solid:
+
ld b, (zSongPSG1-zSongFM4)/zTrack.len ; FM4 and FM5, but not FM6
</asm>
+
ld a, 0B4h ; Command to select AMS/FMS/panning register
 
+
 
now find:
+
.loop2:
<asm>
+
push af ; Save af
move.w #$BB,d0
+
call zWriteFMII ; Write reg/data pair to YM2612
jsr (PlaySound_Special).l ; play door sound
+
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
 +
</syntaxhighlight>
 +
 
 +
===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|file=driver_stuff.zip|filesize=3.0Mb|title=The Sonic 3 Driver data files|plural=1}}
 +
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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
and insert a new line to make it read:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
This is so it remembers what song was playing most of the time.<br>
 +
===Properly restore music after invincibility monitors===
 +
Now we will locate:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
seems the invincibility stars conflict with music memory, so we will just make it use music list 1, so the result is:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
then remove:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; Music to play after invincibility wears off
 +
; ---------------------------------------------------------------------------
 +
MusicList2: binclude misc\muslist2.bin
 +
</syntaxhighlight>
 +
===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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and replace it with:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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.<br>
 +
===Restore music after a boss the sonic 2 and 3 way===
 +
now find:
 +
<syntaxhighlight lang="asm">
 +
loc_179E0:
 +
clr.w $12(a0)
 +
move.w #$81,d0
 +
jsr (PlaySound).l ; play GHZ music
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
loc_179E0:
 +
clr.w $12(a0)
 +
move.w ($FFFFFF90).w,d0 ; restore level music
 +
jsr (PlaySound).l
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
SegaScreen: ; XREF: GameModeArray
 +
move.b #$E4,d0
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
SegaScreen: ; XREF: GameModeArray
 +
move.b #mus_Stop,d0
 +
</syntaxhighlight>
 +
also do the same to: TitleScreen and EndingSequence
 +
 
 +
====Fade Out====
 +
Go to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
also do the same to: loc_33E4, Level, and Obj81_GetUp
 +
 
 +
====Sega!====
 +
Go to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
====Sneakers====
 +
First We need ro Fix the sneaker monitor, we will find:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
next, we find:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
move.w #$<number>,d0
 +
</syntaxhighlight>
 +
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:
 +
 
 +
{|class="prettytable"| width="20"
 +
!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.
 +
<syntaxhighlight lang="asm">
 +
move.w #$A0,d0
 +
</syntaxhighlight>
 +
becomes
 +
<syntaxhighlight lang="asm">
 +
move.w #sfx_Jump,d0
 +
</syntaxhighlight>
 +
 
 +
===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:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; Music playlist
 +
; ---------------------------------------------------------------------------
 +
MusicList: binclude misc/muslist1.bin
 +
align 2
 +
</syntaxhighlight>
 +
change it to:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
====Invincibility====
 +
find:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
this will be very sililar to a sound effect fix:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
====1Up====
 +
find:
 +
<syntaxhighlight lang="asm">
 +
move.w #$88,d0 ; play extra life music
 +
 
 +
Obj25_PlaySnd:
 +
jmp (PlaySound_Special).l
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
move.w #mus_ExtraLife,d0 ; play extra life music
 +
 
 +
Obj25_PlaySnd:
 +
jmp (PlaySound_Special).l
 +
</syntaxhighlight>
 +
now look for:
 +
<syntaxhighlight lang="asm">
 +
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
 +
; ===========================================================================
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
; ===========================================================================
 +
</syntaxhighlight>
 +
and finally look for:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
====Special Stage====
 +
look under:
 +
<syntaxhighlight lang="asm">
 +
SS_ClrNemRam:
 +
</syntaxhighlight>
 +
You sould find something like this:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
it needs to be changed to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
====Title Screen====
 +
now find:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
as you can see, you know what to do here, change that line again:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
 
 +
====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:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
change now!:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
 
 +
====Boss====
 +
looks like we have 5 things to change in total each in blocks like this:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
that need to be changed to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
====Outro====
 +
2 things to fix here, obviously. THe special stage outro and the level outro, we will start with the special stage one:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
to:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
then:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
 
 +
====Game Over / Time Over====
 +
okay, just one fix here will do it:
 +
<syntaxhighlight lang="asm">
 +
loc_138C2:
 +
move.w #$8F,d0
 +
jsr (PlaySound).l ; play game over music
 +
</syntaxhighlight>
 +
changing to:
 +
<syntaxhighlight lang="asm">
 +
loc_138C2:
 +
move.w #mus_GameOver,d0
 +
jsr (PlaySound).l ; play game over music
 +
</syntaxhighlight>
 +
 
 +
====Continue Screen====
 +
smack in the middle of Cont_ClrObjRam are these two lines:
 +
<syntaxhighlight lang="asm">
 +
move.b #$90,d0
 +
bsr.w PlaySound ; play continue music
 +
</syntaxhighlight>
 +
change them to:
 +
<syntaxhighlight lang="asm">
 +
move.b #mus_Continue,d0
 +
bsr.w PlaySound ; play continue music
 +
</syntaxhighlight>
 +
 
 +
====Credits====
 +
now this will surprise you. Yes we used the s&K credits slot for our sonic 1 music.Lets locate:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
and change a single line causing it to read:
 +
<syntaxhighlight lang="asm">
 +
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:
 +
</syntaxhighlight>
 +
 
 +
====Drowning====
 +
Oh know, sonic is drowning in a sound effect instead of a music, lets fix that:
 +
<syntaxhighlight lang="asm">
 +
move.w #$92,d0
 +
jsr (PlaySound).l ; play countdown music
 +
 
 +
loc_13F02:
 +
</syntaxhighlight>
 +
you know what to do here by now:
 +
<syntaxhighlight lang="asm">
 +
move.w #mus_Drowning,d0
 +
jsr (PlaySound).l ; play countdown music
 +
 
 +
loc_13F02:
 +
</syntaxhighlight>
 +
 
 +
====Chaos Emerald====
 +
What? Did sonic get the emerald? Yes? WTF! Wrong sound effect, so lets fix that:
 +
<syntaxhighlight lang="asm">
 +
Obj09_NoEmer:
 +
move.w #$93,d0
 +
jsr (PlaySound_Special).l ; play emerald music
 +
moveq #0,d4
 +
rts
 +
</syntaxhighlight>
 +
by changing a line to get:
 +
<syntaxhighlight lang="asm">
 +
Obj09_NoEmer:
 +
move.w #mus_Emerald,d0
 +
jsr (PlaySound_Special).l ; play emerald music
 +
moveq #0,d4
 +
rts
 +
</syntaxhighlight>
 +
 
 +
===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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
now remove all this junk:
 +
<syntaxhighlight lang="asm">
 +
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
 +
; ===========================================================================
 +
</syntaxhighlight>
 +
lets add a nice little touch to this with music on the level select. Like in sonic 2 and 3. We start here:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
we add these 2 lines
 +
<syntaxhighlight lang="asm">
 +
        move.b #mus_DataSelect,d0
 +
        jsr PlaySound
 +
</syntaxhighlight>
 +
to that section of code, making it into this:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
now the next thing we need to edit is THIS section of code:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
okay, now we need to stop it from adding $80 to the sound test value like s2 does, so we go to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and delete this line:
 +
<syntaxhighlight lang="asm">
 +
addi.w #$80,d0
 +
</syntaxhighlight>
 +
 
 +
==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:
 +
<syntaxhighlight lang="asm">
 +
bsr.s sub_1BD30
 +
</syntaxhighlight>
 +
in sub_1BCE8 need changing to:
 +
<syntaxhighlight lang="asm">
 +
jsr sub_1BD30
 +
</syntaxhighlight>
 +
Now you should be done with adding the s&K driver to your hack. You might check out, what's next.
 +
 
 +
=GIT Disassembly users, start here=
 +
If yours uses asm68k, please move to as since asm68k does not understand z80 asm thus you will not be able to use this guide.<br>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
z80_dac3_pitch: equ z80_ram+zSample3_Pitch
 +
z80_dac_status: equ z80_ram+zDAC_Status
 +
z80_dac_sample: equ z80_ram+zDAC_Sample
 +
</syntaxhighlight>
 +
and remove all 3 of those lines. The sonic1 sound driver will no longer be there, so this will not work anymore. Now replace:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
with:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
==Macros==
 +
Now for the macros. This will be very similar to what the hivebrain people will be doing.<br>
 +
First off lets replace the org and org0 macros. Open MacroSetup.asm and find:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
We cannot use this version, so we will replace it with the s3k version of this, which looks like:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
Next it is the cnop and cnop0 macros, once again find:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
and replace it with:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
for the same reason as above. Now we will repeat the above for the same reason for align, align0, and even. Find:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
and replace the code with this:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
Now we update the trace macro by locating:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
and replacing it with:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
We replaced these macros because the old ones will bork s3p2bin later if we don't.<br>
 +
All that is left now, is adding this:
 +
<syntaxhighlight lang="asm">
 +
; 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
 +
</syntaxhighlight>
 +
after:
 +
<syntaxhighlight lang="asm">
 +
_tst macro
 +
!tst.ATTRIBUTE ALLARGS
 +
endm
 +
 
 +
    endif
 +
</syntaxhighlight>
 +
==Final Setup before we begin==
 +
We almost forgot to add something to the beginning of Sonic.asm, so lets add:
 +
<syntaxhighlight lang="asm">
 +
 
 +
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
 +
; ---------------------------------------------------------------------------
 +
</syntaxhighlight>
 +
right after:
 +
<syntaxhighlight lang="asm">
 +
include "MacroSetup.asm"
 +
include "Constants.asm"
 +
include "Variables.asm"
 +
include "Macros.asm"
 +
</syntaxhighlight>
 +
 
 +
==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:
 +
<syntaxhighlight lang="asm">
 +
jsr (UpdateMusic).l
 +
</syntaxhighlight>
 +
 
 +
under:
 +
 
 +
<syntaxhighlight lang="asm">
 +
VBla_Music:
 +
</syntaxhighlight>
 +
 
 +
<syntaxhighlight lang="asm">
 +
</syntaxhighlight>
 +
===Prepare Horzontal Interrupt Handler for new driver===
 +
Now we locate:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
See another familiar line?  Yes, you've got it, do the same thing to it:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
and replace it all with:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
==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:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
and replace it completely with:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
and replace it entirely with:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
==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):
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
and replace it completely with:
 +
<syntaxhighlight lang="asm">
 +
; ---------------------------------------------------------------------------
 +
; 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
 +
</syntaxhighlight>
 +
 
 +
==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|file=git_files.zip|filesize=3.4Mb|title=The Sonic 3 Driver data files|plural=1}}
 +
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.<br>
 +
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.<br>
 +
First we will open "2E Monitor Content Power-Up.asm", you should by now know where that file is. Find:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
next, we open "Sonic Display.asm". By now you should know where to find that file. Locate:
 +
<syntaxhighlight lang="asm">
 +
.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
 +
</syntaxhighlight>
 +
and change it to:
 +
<syntaxhighlight lang="asm">
 +
.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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
now remove all this junk:
 +
<syntaxhighlight lang="asm">
 +
; ===========================================================================
 +
 
 +
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
 +
</syntaxhighlight>
 +
lets add a nice little touch to this with music on the level select. Like in sonic 2 and 3. We start here:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
we add these 2 lines
 +
<syntaxhighlight lang="asm">
 +
        move.b #mus_DataSelect,d0
 +
        jsr PlaySound
 +
</syntaxhighlight>
 +
to that section of code, making it into this:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
now the next thing we need to edit is THIS section of code:
 +
<syntaxhighlight lang="asm">
 +
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
 +
</syntaxhighlight>
 +
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:
 +
<syntaxhighlight lang="asm">
 +
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
  
Obj69_Animate:
+
LevSel_Right:
</asm>
+
btst #bitR,d1 ; is right pressed?
and change it to:
+
beq.s LevSel_Refresh2 ; if not, branch
<asm>
+
addq.w #1,d0 ; add 1 to sound test
move.w #$58,d0
 
jsr (PlaySound_Special).l ; play door sound
 
  
Obj69_Animate:
+
LevSel_Refresh2:
</asm>
+
move.w d0,(v_levselsound).w ; set sound test number
'''note: this sound effect is used differently in sonic3 but is the closest to what we have here'''
+
bsr.w LevSelTextLoad ; refresh text
now we find:
 
<asm>
 
move.w #$BC,d0
 
jsr (PlaySound_Special).l ; play teleport sound
 
  
locret_16796:
+
LevSel_NoMove:
 
rts
 
rts
</asm>
+
</syntaxhighlight>
and change it to:
+
okay, now we need to stop it from adding $80 to the sound test value like s2 does, so we go to:
<asm>
+
<syntaxhighlight lang="asm">
move.w #$73,d0
+
LevSel_DrawSnd:
jsr (PlaySound_Special).l ; play teleport sound
+
locVRAM $EC30 ; sound test position on screen
 
+
move.w (v_levselsound).w,d0
locret_16796:
+
addi.w #$80,d0
rts
+
move.b d0,d2
</asm>
+
lsr.b #4,d0
now find:
+
bsr.w LevSel_ChgSnd ; draw 1st digit
<asm>
+
move.b d2,d0
move.w #$BD,d0
+
bsr.w LevSel_ChgSnd ; draw 2nd digit
jsr (PlaySound_Special).l ; play stomping sound
 
 
 
Obj31_Restart:
 
</asm>
 
and change it to:
 
<asm>
 
move.w #$5F,d0
 
jsr (PlaySound_Special).l ; play stomping sound
 
 
 
Obj31_Restart:
 
</asm>
 
now find:
 
<asm>
 
move.w #$BD,d0
 
jsr (PlaySound_Special).l ; play stomping sound
 
 
 
loc_B97C:
 
bra.w Obj31_Restart
 
</asm>
 
and change it to:
 
<asm>
 
move.w #$5F,d0
 
jsr (PlaySound_Special).l ; play stomping sound
 
 
 
loc_B97C:
 
bra.w Obj31_Restart
 
</asm>
 
'''note: this stomp sound is very different, though it is a stomping sound, the one from MGZ to be exact'''
 
now find:
 
<asm>
 
move.w #$BE,d0
 
jsr (PlaySound_Special).l ; play rolling sound
 
tst.w $14(a0)
 
bne.s locret_133E8
 
move.w #$200,$14(a0)
 
 
 
locret_133E8:
 
 
rts
 
rts
</asm>
+
</syntaxhighlight>
and change it to:
+
and delete this line:
<asm>
+
<syntaxhighlight lang="asm">
move.w #$3C,d0
+
addi.w #$80,d0
jsr (PlaySound_Special).l ; play rolling sound
+
</syntaxhighlight>
tst.w $14(a0)
 
bne.s locret_133E8
 
move.w #$200,$14(a0)
 
  
locret_133E8:
+
==Branch fixes==
rts
+
there are some 'bsr's that need fixing otherwise it won't build. Open "09 Sonic in Special Stage.asm". These lines here:
</asm>
+
<syntaxhighlight lang="asm">
now find:
+
bsr.s sub_1BD30
<asm>
+
</syntaxhighlight>
move.w #$BE,d0
+
in sub_1BCE8 need changing to:
jsr (PlaySound_Special).l ; play Sonic rolling sound
+
<syntaxhighlight lang="asm">
 +
jsr sub_1BD30
 +
</syntaxhighlight>
  
locret_1675C:
+
=What comes next=
rts
+
More recently, [[User:Alriightyman]] has ported Flamewing's sonic & knuckles driver mod to sonic 2 in this post:
</asm>
+
[http://forums.sonicretro.org/index.php?showtopic=32061]<br>
and change it to:
+
[[User:KZG4]] is working on a part 2 where we upgrade the sonic 3 driver we installed to the [[User:Flamewing]] sound driver.<br>
<asm>
+
you can swap out the music for something else that more fits your hack as well as the sound effects.<br>
move.w #$3C,d0
+
You can add sound effects to existing objects like a really old version of this guide did.
jsr (PlaySound_Special).l ; play Sonic rolling sound
 
  
locret_1675C:
+
=notice to admins=
rts
+
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.
</asm>
+
I hope you make good use of this driver and make many great hacks.
  
==Fixing the sound test on the level select==
+
=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.
  
[[Category:SCHG How-tos|{{PAGENAME}}]]
+
{{S1Howtos}}
 +
|{{PAGENAME}}]]

Latest revision as of 11:32, 7 April 2021

(Original guide by Kramlat)
Updated by User: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

Hivebrain Disassembly users, start here

If yours uses snasm68k or even asm68k, please move to as since snasm68k doesnt understand these macros and has been known to have compatibility issues with some versions of Windows and with Wine. Also, asm68k does not understand z80 asm thus you will not be able to use 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.svg 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.

GIT Disassembly users, start here

If yours uses asm68k, please move to as since asm68k does not understand z80 asm thus you will not be able to use 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.svg 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.
SCHG How-To Guide: Sonic the Hedgehog (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix the SEGA Sound | Display the Press Start Button Text | Fix the Level Select Menu | Fix the Hidden Points Bug | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix the Walk-Jump Bug | Correct Drowning Bugs | Fix the Death Boundary Bug | Fix the Camera Follow Bug | Fix Song Restoration Bugs | Fix the HUD Blinking | Fix the Level Select Graphics Bug | Fix a remember sprite related bug
Changing Design Choices
Change Spike Behavior | Collide with Water After Being Hurt | Fix Special Stage Jumping Physics | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Remove the Speed Cap | Port the REV01 Background Effects | Port Sonic 2's Level Art Loader | Retain Rings Between Acts | Add Sonic 2 (Simon Wai Prototype) Level Select | Improve ObjectMove Subroutines | Port Sonic 2 Level Select
Adding Features
Add Spin Dash ( Part 1 / Part 2 / Part 3 / Part 4 ) | Add Eggman Monitor | Add Super Sonic | Add the Air Roll
Sound Features
Expand the Sound Index | Play Different Songs Per Act | Port Sonic 2 Final Sound Driver | Port Sonic 3's Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Change The SEGA Sound
Extending the Game
Load Chunks From ROM | Add Extra Characters | Make an Alternative Title Screen | Use Dynamic Tilesets | Make GHZ Load Alternate Art | Make Ending Load Alternate Art | Add a New Zone | Set Up the Goggle Monitor | Add New Moves | Add a Dynamic Collision System | Dynamic Special Stage Walls System | Extend Sprite Mappings and Art Limit | Enigma Credits | Use Dynamic Palettes
Miscellaneous
Convert the Hivebrain 2005 Disassembly to ASM68K
Split Disassembly Guides
Set Up a Split Disassembly | Basic Level Editing | Basic Art Editing | Basic ASM Editing (Spin Dash)
|Port Sonic 3's Sound Driver to Sonic 1]]