Actions

SCHG How-to

Difference between revisions of "Port Knuckles into Sonic 2"

From Sonic Retro

m (Getting the Knuckles object and putting it in Sonic 2)
(Explaining how to get sliding sound working.)
Line 587: Line 587:
  
 
== Adding Knuckles' sound effects from S3K ==
 
== Adding Knuckles' sound effects from S3K ==
''Sliding sound doesn't work yet, requires sound driver modifications.''<br />
 
 
You may notice that Knuckles is missing some sound effects for when he grabs a wall, lands from a glide, and slides along the ground. Since KiS2 uses S2's sound driver directly, they couldn't add these sounds, but you can.<br />
 
You may notice that Knuckles is missing some sound effects for when he grabs a wall, lands from a glide, and slides along the ground. Since KiS2 uses S2's sound driver directly, they couldn't add these sounds, but you can.<br />
 
Download [[:File:S2KnuxSFX.zip|this file]] and extract its contents to the sound folder of your disassembly.<br />
 
Download [[:File:S2KnuxSFX.zip|this file]] and extract its contents to the sound folder of your disassembly.<br />
Line 632: Line 631:
 
<asm>SndID_Skidding = id(SndPtr_Skidding) ; A4</asm>
 
<asm>SndID_Skidding = id(SndPtr_Skidding) ; A4</asm>
 
and you'll replace the $A4 with SndID_Skidding.
 
and you'll replace the $A4 with SndID_Skidding.
 +
Once you do all this and test, you will notice that the sliding sound is not working. This is because a slight change to the driver is needed to get it to work. So open up s2.sounddriver.asm and locate this:
 +
<z80>; zloc_285:
 +
;zGetFrequency
 +
zFMSetFreq:
 +
; 'a' holds a note to get frequency for
 +
sub 80h
 +
jr z,zFMDoRest ; If this is a rest, jump to zFMDoRest
 +
add a,(ix+5) ; Add current channel key offset (coord flag E9)
 +
add a,a ; Offset into Frequency table...
 +
add a,zFrequencies&0FFh
 +
ld (zloc_292+2),a ; store into the instruction after zloc_292 (self-modifying code)
 +
; ld d,a
 +
; adc a,(zFrequencies&0FF00h)>>8
 +
; sub d
 +
; ld (zloc_292+3),a ; this is how you could store the high byte of the pointer too (unnecessary if it's in the right range)
 +
zloc_292:</z80>
 +
There are no issues if the comments are not there; in any event, you want to edit it to match this:
 +
<z80>; zloc_285:
 +
;zGetFrequency
 +
zFMSetFreq:
 +
; 'a' holds a note to get frequency for
 +
sub 80h
 +
jr z,zFMDoRest ; If this is a rest, jump to zFMDoRest
 +
add a,(ix+5) ; Add current channel key offset (coord flag E9)
 +
add a,a ; Offset into Frequency table...
 +
add a,zFrequencies&0FFh
 +
ld (zloc_292+2),a ; store into the instruction after zloc_292 (self-modifying code)
 +
ld d,a
 +
adc a,(zFrequencies&0FF00h)>>8
 +
sub d
 +
ld (zloc_292+3),a ; this is how you could store the high byte of the pointer too (unnecessary if it's in the right range)
 +
zloc_292:</z80>
 +
After you are done, find this:
 +
<z80>;zloc_460
 +
zPSGSetFreq:
 +
sub 81h ; a = a-$81 (zero-based index from lowest note)
 +
jr c,+ ; If carry (only time that happens if 80h because of earlier logic) this is a rest!
 +
add a,(ix+5) ; Add current channel key offset (coord flag E9)
 +
add a,a ; Multiply note value by 2
 +
add a,zPSGFrequencies&0FFh ; Point to proper place in table
 +
ld (zloc_46D+2),a ; store into the instruction after zloc_46D (self-modifying code)
 +
zloc_46D:</z80>
 +
and change it to this:
 +
<z80>;zloc_460
 +
zPSGSetFreq:
 +
sub 81h ; a = a-$81 (zero-based index from lowest note)
 +
jr c,+ ; If carry (only time that happens if 80h because of earlier logic) this is a rest!
 +
add a,(ix+5) ; Add current channel key offset (coord flag E9)
 +
add a,a ; Multiply note value by 2
 +
add a,zPSGFrequencies&0FFh ; Point to proper place in table
 +
ld (zloc_46D+2),a ; store into the instruction after zloc_46D (self-modifying code)
 +
ld d,a
 +
adc a,(zPSGFrequencies&0FF00h)>>8
 +
sub d
 +
ld (zloc_46D+3),a ; this is how you could store the high byte of the pointer too (unnecessary if it's in the right range)
 +
zloc_46D:</z80>
 +
We will be moving the zFrequencies and zPSGFrequencies tables to they don't get in the way, and these changes load the high byte of the pointer too. So now time to move the tables: find this:
 +
<z80>; This the note -> frequency setting lookup
 +
; the same array is found at $729CE in Sonic 1, and at $C9C44 in Ristar
 +
; zword_359:
 +
ensure1byteoffset 8Ch
 +
zPSGFrequencies:
 +
dw 356h,  326h, 2F9h, 2CEh, 2A5h, 280h, 25Ch, 23Ah
 +
dw 21Ah,  1FBh, 1DFh, 1C4h, 1ABh, 193h, 17Dh, 167h
 +
dw 153h,  140h, 12Eh, 11Dh, 10Dh, 0FEh, 0EFh, 0E2h
 +
dw 0D6h,  0C9h, 0BEh, 0B4h, 0A9h, 0A0h,  97h,  8Fh
 +
dw 87h,  7Fh,  78h,  71h,  6Bh,  65h,  5Fh,  5Ah
 +
dw 55h,  50h,  4Bh,  47h,  43h,  40h,  3Ch,  39h
 +
dw 36h,  33h,  30h,  2Dh,  2Bh,  28h,  26h,  24h
 +
dw 22h,  20h,  1Fh,  1Dh,  1Bh,  1Ah,  18h,  17h
 +
dw 16h,  15h,  13h,  12h,  11h,    0
 +
; ---------------------------------------------------------------------------</z80>
 +
and delete it; then find this:
 +
<z80>; lookup table of FM note frequencies for instruments and sound effects
 +
ensure1byteoffset 0C0h
 +
; zbyte_534:
 +
zFrequencies:
 +
dw 025Eh,0284h,02ABh,02D3h,02FEh,032Dh,035Ch,038Fh,03C5h,03FFh,043Ch,047Ch
 +
dw 0A5Eh,0A84h,0AABh,0AD3h,0AFEh,0B2Dh,0B5Ch,0B8Fh,0BC5h,0BFFh,0C3Ch,0C7Ch
 +
dw 125Eh,1284h,12ABh,12D3h,12FEh,132Dh,135Ch,138Fh,13C5h,13FFh,143Ch,147Ch
 +
dw 1A5Eh,1A84h,1AABh,1AD3h,1AFEh,1B2Dh,1B5Ch,1B8Fh,1BC5h,1BFFh,1C3Ch,1C7Ch
 +
dw 225Eh,2284h,22ABh,22D3h,22FEh,232Dh,235Ch,238Fh,23C5h,23FFh,243Ch,247Ch
 +
dw 2A5Eh,2A84h,2AABh,2AD3h,2AFEh,2B2Dh,2B5Ch,2B8Fh,2BC5h,2BFFh,2C3Ch,2C7Ch
 +
dw 325Eh,3284h,32ABh,32D3h,32FEh,332Dh,335Ch,338Fh,33C5h,33FFh,343Ch,347Ch
 +
dw 3A5Eh,3A84h,3AABh,3AD3h,3AFEh,3B2Dh,3B5Ch,3B8Fh,3BC5h,3BFFh,3C3Ch,3C7Ch ; 96 entries
 +
 +
</z80>
 +
and delete it too; now find this:
 +
<z80>; ---------------------------------------------------------------------------
 +
;zbyte_FD8h
 +
zSFXPriority:</z80>
 +
and edit it to match this:
 +
<z80>; ---------------------------------------------------------------------------
 +
; This the note -> frequency setting lookup
 +
; the same array is found at $729CE in Sonic 1, and at $C9C44 in Ristar
 +
; zword_359:
 +
zPSGFrequencies:
 +
dw 356h,  326h, 2F9h, 2CEh, 2A5h, 280h, 25Ch, 23Ah
 +
dw 21Ah,  1FBh, 1DFh, 1C4h, 1ABh, 193h, 17Dh, 167h
 +
dw 153h,  140h, 12Eh, 11Dh, 10Dh, 0FEh, 0EFh, 0E2h
 +
dw 0D6h,  0C9h, 0BEh, 0B4h, 0A9h, 0A0h,  97h,  8Fh
 +
dw 87h,  7Fh,  78h,  71h,  6Bh,  65h,  5Fh,  5Ah
 +
dw 55h,  50h,  4Bh,  47h,  43h,  40h,  3Ch,  39h
 +
dw 36h,  33h,  30h,  2Dh,  2Bh,  28h,  26h,  24h
 +
dw 22h,  20h,  1Fh,  1Dh,  1Bh,  1Ah,  18h,  17h
 +
dw 16h,  15h,  13h,  12h,  11h,    0,  10h,    0
 +
dw 284h,  2ABh, 2D3h, 2FEh, 32Dh, 35Ch, 38Fh, 3C5h
 +
dw 3FFh,  43Ch, 47Ch, 4C0h
 +
; lookup table of FM note frequencies for instruments and sound effects
 +
; zbyte_534:
 +
zFrequencies:
 +
dw 025Eh,0284h,02ABh,02D3h,02FEh,032Dh,035Ch,038Fh,03C5h,03FFh,043Ch,047Ch
 +
dw 0A5Eh,0A84h,0AABh,0AD3h,0AFEh,0B2Dh,0B5Ch,0B8Fh,0BC5h,0BFFh,0C3Ch,0C7Ch
 +
dw 125Eh,1284h,12ABh,12D3h,12FEh,132Dh,135Ch,138Fh,13C5h,13FFh,143Ch,147Ch
 +
dw 1A5Eh,1A84h,1AABh,1AD3h,1AFEh,1B2Dh,1B5Ch,1B8Fh,1BC5h,1BFFh,1C3Ch,1C7Ch
 +
dw 225Eh,2284h,22ABh,22D3h,22FEh,232Dh,235Ch,238Fh,23C5h,23FFh,243Ch,247Ch
 +
dw 2A5Eh,2A84h,2AABh,2AD3h,2AFEh,2B2Dh,2B5Ch,2B8Fh,2BC5h,2BFFh,2C3Ch,2C7Ch
 +
dw 325Eh,3284h,32ABh,32D3h,32FEh,332Dh,335Ch,338Fh,33C5h,33FFh,343Ch,347Ch
 +
dw 3A5Eh,3A84h,3AABh,3AD3h,3AFEh,3B2Dh,3B5Ch,3B8Fh,3BC5h,3BFFh,3C3Ch,3C7Ch ; 96 entries
 +
; ---------------------------------------------------------------------------
 +
;zbyte_FD8h
 +
zSFXPriority:</z80>
 +
If you compare it with the old tables, you will see that zPSGFrequencies gained several new entries; it is because of these new entries that we had to move all this stuff around. Also, the sliding sound uses these entries, which do not exist in the Sonic 2 driver; so it should now be fixed.
 
{{S2Howtos}}
 
{{S2Howtos}}
 
[[Category:SCHG How-tos|{{PAGENAME}}]]
 
[[Category:SCHG How-tos|{{PAGENAME}}]]

Revision as of 17:30, 5 September 2014

(Original guide by MainMemory)
Note: this guide is written with the Sonic 2 Git disassembly in mind.

Getting the Knuckles object and putting it in Sonic 2

First you will need s2k.asm from the Knuckles in Sonic 2 disassembly.
Open s2k.asm, select everything from Obj01: (line 27324) to the end of ArtConvTable (line 30585), and copy it into a new asm file called Knuckles.asm in your S2 disasm folder. This is so we don't accidentally change something in our Sonic 2 disassembly while we're fixing Knuckles.
Open Knuckles.asm and do a Find and Replace operation to replace every instance of "Obj01" (without quotes) with "ObjXX", where XX is the object ID you want Knuckles to use. The following IDs are unused in Sonic 2: 4C, 4D, 4E, 4F, 62, D0, D1, and everything from DD to FF. Use that ID whenever I refer to ObjXX.
Next, do the following replacements: "CheckGameOver" to "ObjXX_CheckGameOver", "JmpTo_KillCharacter" to "JmpToK_KillCharacter", "Player_CheckFloor" to "Sonic_CheckFloor", "CheckCeilingDist" to "Sonic_CheckCeiling".
Find the line <asm> bsr.w Adjust2PArtPointer2_Useless</asm> and change it to <asm> jsr Adjust2PArtPointer</asm> Now you'll need to copy some more code from s2k.asm. Specifically, sub_3192E6, loc_318FE8, sub_318FF6, loc_319208 and loc_3193D2. Because they're all small routines, I'll paste them here: <asm>; =============== S U B R O U T I N E =======================================

Doesn't exist in S2

sub_3192E6:  ; ... move.b $17(a0),d0 ext.w d0 sub.w d0,d2 eor.w #$F,d2 lea ($FFFFF768).w,a4 move.w #-$10,a3 move.w #$800,d6 bsr.w FindFloor move.b #$80,d2

loc_319306: bra.w loc_318FE8

End of function sub_3192E6
START OF FUNCTION CHUNK FOR CheckRightWallDist

loc_318FE8:  ; ... move.b ($FFFFF768).w,d3 btst #0,d3 beq.s return_318FF4 move.b d2,d3

return_318FF4:  ; ... rts

END OF FUNCTION CHUNK FOR CheckRightWallDist
=============== S U B R O U T I N E =======================================


sub_318FF6:  ; ... move.b $17(a0),d0 ext.w d0 add.w d0,d2 lea ($FFFFF768).w,a4 move.w #$10,a3 move.w #0,d6 bsr.w FindFloor move.b #0,d2 bra.s loc_318FE8

End of function sub_318FF6
---------------------------------------------------------------------------
This doesn't exist in S2...
START OF FUNCTION CHUNK FOR sub_315C22

loc_319208:  ; ... move.b $17(a0),d0 ext.w d0 add.w d0,d3 lea ($FFFFF768).w,a4 move.w #$10,a3 move.w #0,d6 bsr.w FindWall move.b #$C0,d2 bra.w loc_318FE8

END OF FUNCTION CHUNK FOR sub_315C22
---------------------------------------------------------------------------
START OF FUNCTION CHUNK FOR sub_315C22

loc_3193D2:  ; ... move.b $17(a0),d0 ext.w d0 sub.w d0,d3 eor.w #$F,d3 lea ($FFFFF768).w,a4 move.w #$FFF0,a3 move.w #$400,d6 bsr.w FindWall move.b #$40,d2 bra.w loc_318FE8

END OF FUNCTION CHUNK FOR sub_315C22</asm>

Put this code at the end of Knuckles' asm.

Now we're ready to put the code in our S2 disassembly, so open s2.asm. You can place Knuckles' code wherever you want, but the place that seems to cause the fewest errors is right before Obj79. You can either add the line <asm>include "Knuckles.asm"</asm> or just copy and paste his code in.
When you do that, run build.bat in the S2 disassembly. It should tell you there are 14 errors and to check s2.log, so that's what we'll do. Everything in the log will be in this format:

> > >s2.asm(line#): error: jump distance too big
> > > 	bsr.w	label

What you need to do is go to each line and change the bsr.w to jsr. There is one line that's a bra.w instead, change that to jmp.

Importing Knuckles' art and mappings

If you try to build now, you will see three "symbol undefined" errors: SK_Map_Knuckles, SK_PLC_Knuckles and SK_ArtUnc_Knux. So let's fix those.

For SK_Map_Knuckles, you need to take Knuckles' sprite mappings from S&K and convert them to Sonic 2 format. There are two ways to do this:

  • Using SonMapEd, set the Game Format setting to "Sonic 3 & Knuckles", load the mappings file, change the Game Format setting to "Sonic 2", and save the mappings as Knuckles.asm in the mappings/sprite folder of your S2 disasm.
  • Using MappingsConverter, load the mappings with input set to Game: "Sonic 3 & Knuckles", Format: ASM and output set to Game: "Sonic 2", Format: ASM, and save the mappings as Knuckles.asm in the mappings/sprite folder of your S2 disasm.
    • If you're using the MapMacros branch of the S2 disassembly as a base, you can also set the output format to "ASM with Macros".

Then, in s2.asm, somewhere near Mapunc_Sonic, add the line <asm>SK_Map_Knuckles: include "mappings/sprite/Knuckles.asm"</asm>
For SK_PLC_Knuckles, you can simply take Knuckles' DPLCs from S&K and save them as Knuckles.asm in the mappings/spriteDPLC folder of your S2 disasm, since the DPLC format is the same for S3K's players and Sonic 2.
Then, in s2.asm, somewhere near MapRUnc_Sonic, add the line <asm>SK_PLC_Knuckles: include "mappings/spriteDPLC/Knuckles.asm"</asm>
For SK_ArtUnc_Knux, take Knuckles' art from S&K and save it as Knuckles.bin in the art/uncompressed folder of your S2 disasm.
Then, in s2.asm, somewhere near ArtUnc_Sonic, add the lines <asm> align $20 SK_ArtUnc_Knux: BINCLUDE "art/uncompressed/Knuckles.bin"</asm>

Optimizing Knuckles' art loading

This step is optional, but recommended.
You may have noticed that Knuckles' art file is the same file as S3K, even though the two games' palettes are organized differently. This is possible because Knuckles' code dynamically recolors all his sprites in LoadKnucklesDynPLC_Part2. This saves some space in the KiS2 ROM, but for a Sonic 2 hack, it's just wasting CPU, so we'll remove it.
Select everything under the line bmi.w return_31753E in LoadKnucklesDynPLC_Part2 up to (but not including) return_31753E, and delete it. Replace it with this code, modified from LoadSonicDynPLC_Part2: <asm> move.w #tiles_to_bytes(ArtTile_ArtUnc_Sonic),d4

loc_1B86E

KPLC_ReadEntry: moveq #0,d1 move.w (a2)+,d1 move.w d1,d3 lsr.w #8,d3 andi.w #$F0,d3 addi.w #$10,d3 andi.w #$FFF,d1 lsl.l #5,d1 addi.l #SK_ArtUnc_Knux,d1 move.w d4,d2 add.w d3,d4 add.w d3,d4 jsr (QueueDMATransfer).l dbf d5,KPLC_ReadEntry ; repeat for number of entries</asm> Then you can delete ArtConvTable and the comments above it.
Now you just need Knuckles' art converted to Sonic 2's palette, and replace Knuckles' art file (art/uncompressed/Knuckles.bin) with that.

Getting Knuckles ingame

At this point, you should be getting no errors when building, but you still can't play as Knuckles, because nothing references his code!
Go to Obj_Index in s2.asm, find the line corresponding to the object ID you gave Knuckles' code, and replace it with this: <asm>ObjPtr_Knuckles: dc.l ObjXX ; Knuckles</asm> Now, go to s2.constants.asm, find the end of the ObjID list, and add <asm>ObjID_Knuckles = id(ObjPtr_Knuckles)</asm> Back in s2.asm, under InitPlayers, copy the first two lines under InitPlayers_Alone and paste them under InitPlayers_TailsAlone, changing the label after the bne to InitPlayers_KnucklesAlone. Then add the following lines under the rts at the end of InitPlayers_TailsAlone: <asm>InitPlayers_KnucklesAlone: move.b #ObjID_Knuckles,(MainCharacter+id).w ; load ObjXX Knuckles object at $FFFFB000 move.b #ObjID_SpindashDust,(Sonic_Dust+id).w ; load Obj08 Knuckles' spindash dust/splash object at $FFFFD100 rts</asm> Next go to OptionScreen_Choices and change the 3 to a 4 in the first line. This changes how many characters are available in the character select.
Under off_92D2 and off_92DE add the following line at the end of the list: <asm> dc.l TextOptScr_KnucklesAlone</asm> Then go to TextOptScr_TailsAlone and add the line <asm>TextOptScr_KnucklesAlone: menutxt "KNUCKLES ALONE "</asm> Now build the game, run it, and choose Knuckles in the options menu, and you'll see... Blue Knuckles!

Importing Knuckles' palettes

Here are the palette files, put them in the art/palettes folder.
Add the following lines under Pal_UNK7 for the palette data: <asm>Pal_Knux: palette Knuckles.bin,SonicAndTails2.bin ; "Sonic and Miles" background palette (also usually the primary palette line) Pal_CPZ_K_U: palette CPZ Knux underwater.bin ; Chemical Plant Zone underwater palette Pal_ARZ_K_U: palette ARZ Knux underwater.bin ; Aquatic Ruin Zone underwater palette</asm> Add these lines under PalPtr_UNK7 to add them to the palette list: <asm>PalPtr_Knux: palptr Pal_Knux, 0 PalPtr_CPZ_K_U: palptr Pal_CPZ_K_U, 0 PalPtr_ARZ_K_U: palptr Pal_ARZ_K_U, 0</asm> In s2.constants.asm, find the end of the PalID list, and add the lines <asm>PalID_Knux = id(PalPtr_Knux) ; 28 PalID_CPZ_K_U = id(PalPtr_CPZ_K_U) ; 29 PalID_ARZ_K_U = id(PalPtr_ARZ_K_U) ; 30</asm> Now find Level_LoadPal in s2.asm, and replace everything until Level_WaterPal with <asm>Level_LoadPal: moveq #PalID_BGND,d0 cmpi.w #3,(Player_mode).w ; are you playing as Knuckles? blt.s + ; if not, branch moveq #PalID_Knux,d0 ; load Knuckles' palette index + bsr.w PalLoad2 ; load Sonic's palette line tst.b (Water_flag).w ; does level have water? beq.s Level_GetBgm ; if not, branch moveq #PalID_HPZ_U,d0 ; palette number $15 cmpi.b #hidden_palace_zone,(Current_Zone).w beq.s Level_WaterPal ; branch if level is HPZ moveq #PalID_CPZ_U,d0 ; palette number $16 cmpi.b #chemical_plant_zone,(Current_Zone).w bne.s Level_PalNotCPZ ; branch if level is not CPZ cmpi.w #3,(Player_mode).w ; are you playing as Knuckles? blt.s Level_WaterPal ; if not, branch moveq #PalID_CPZ_K_U,d0 bra.s Level_WaterPal ; branch Level_PalNotCPZ: moveq #PalID_ARZ_U,d0 ; palette number $17 cmpi.w #3,(Player_mode).w ; are you playing as Knuckles? blt.s Level_WaterPal ; if not, branch moveq #PalID_ARZ_K_U,d0</asm>

Super Knuckles

In s2k.asm, select everything from PalCycle_SuperKnuckles (line 3173) to Pal_KnucklesReds (line 3227) and paste it into s2.asm just before Pal_FadeTo. Then, under PalCycle_SuperSonic, add the lines <asm> cmpi.w #3,(Player_mode).w ; are we Knuckles? beq.w PalCycle_SuperKnuckles ; if we are, run the Super Knuckles cycle instead</asm>

Fixing bugs

Monitors don't break when gliding

In SolidObject_Monitor_Sonic, just under bne.s Obj26_ChkOverEdge, add the following lines: <asm> cmpi.b #ObjID_Knuckles,id(a1) beq.s SolidObject_Monitor_Knuckles</asm> Then, under the rts, add <asm>SolidObject_Monitor_Knuckles: cmp.b #1,$21(a1) beq.s + cmp.b #3,$21(a1) beq.s + cmpi.b #AniIDSonAni_Roll,anim(a1) bne.w SolidObject_cont + rts</asm> Then, under loc_3F768, replace everything from cmpi.b #2,anim(a0) up to return_3F78A with <asm> cmpi.b #2,anim(a0) beq.s Break_Monitor cmpi.b #ObjID_Knuckles,id(a0) bne.s return_3F78A cmp.b #1,$21(a0) beq.s Break_Monitor cmp.b #3,$21(a0) bne.s return_3F78A

Break_Monitor: neg.w y_vel(a0) ; reverse Sonic's y-motion move.b #4,routine(a1) move.w a0,parent(a1)</asm>

Knuckles gets hurt gliding into enemies

Under Touch_Enemy, add the following lines: <asm> cmpi.b #ObjID_Knuckles,id(a0) bne.s Touch_NotKnuckles cmp.b #1,$21(a0) beq.s + cmp.b #3,$21(a0) beq.s +

Touch_NotKnuckles:</asm>

Knuckles doesn't behave properly when standing on objects

Under Sonic_ResetOnFloor_Part2, add the following lines: <asm> cmpi.b #ObjID_Knuckles,id(a0) ; is this object ID Knuckles? beq.w Knuckles_ResetOnFloor_Part2 ; if it is, branch to the Knuckles version of this code</asm>

Knuckles doesn't bounce off bosses when gliding

Under Touch_Enemy, just before neg.w x_vel(a0) add the following lines: <asm> cmpi.b #ObjID_Knuckles,id(a0) bne.s + cmp.b #1,$21(a0) bne.s + move.b #2,$21(a0) move.b #$21,anim(a0) +</asm> Then add the same lines just before the neg.w x_vel(a0) under Touch_Enemy_Part2.

Dust doesn't appear when Knuckles is sliding

Change Obj08_CheckSkid to this: <asm>Obj08_CheckSkid: movea.w parent(a0),a2 ; a2=character moveq #$10,d1 ; move y offset to d1 cmpi.b #AniIDSonAni_Stop,anim(a2) ; SonAni_Stop beq.s Obj08_SkidDust moveq #6,d1 ; move different y offset to d1 cmpi.b #ObjID_Knuckles,id(a2) ; playing as Knuckles? bne.s + cmpi.b #3,$21(a2) ; check for sliding beq.s Obj08_SkidDust + move.b #2,routine(a0) move.b #0,objoff_32(a0) rts</asm> Then, under Obj08_SkidDust, change the line <asm>addi.w #$10,y_pos(a1)</asm> to <asm>add.w d1,y_pos(a1)</asm>

Knuckles' height is incorrect when breathing a bubble

Under loc_1FB0C, find the lines <asm> cmpi.b #1,(a1) bne.s loc_1FBA8</asm> and replace them with <asm> cmpi.b #ObjID_Knuckles,id(a1) beq.s + cmpi.b #1,(a1) bne.s loc_1FBA8 +</asm>

Knuckles' sprite is incorrect when exiting debug mode

In loc_41C56, add the following lines under move.l #Mapunc_Sonic,mappings(a1): <asm> cmpi.b #ObjID_Knuckles,id(a1) bne.s + move.l #SK_Map_Knuckles,mappings(a1) +</asm>

Knuckles starts gliding during the WFZ end scene

Under ObjB2_Jump_to_plane, find the line <asm> move.w #((button_right_mask|button_A_mask)<<8)|button_right_mask|button_A_mask,(Ctrl_1_Logical).w</asm> and replace it with <asm> move.w #(button_right_mask|button_A_mask)<<8,(Ctrl_1_Logical).w</asm> Then, just above loc_3AB8A, add <asm> addq.b #2,routine_secondary(a0) bra.s loc_3AB8A

ObjB2_Jumping_to_ship: cmpi.w #$447,objoff_2A(a0) bcc.s loc_3AB8A move.w #button_A_mask<<8,(Ctrl_1_Logical).w</asm> Under ObjB2_Main_WFZ_states, just before the last item, add <asm> offsetTableEntry.w ObjB2_Jumping_to_ship</asm>

Knuckles doesn't stop gliding on OOZ fans

Under loc_2A990 go to the + label and insert the following lines: <asm> cmpi.b #ObjID_Knuckles,id(a1) bne.s + clr.b $21(a1) +</asm>

Knuckles doesn't stop gliding on WFZ propellers

Under ObjB5_CheckPlayer go to the + label and insert the following lines: <asm> cmpi.b #ObjID_Knuckles,id(a1) bne.s ObjB5_NotKnuckles clr.b $21(a1)

ObjB5_NotKnuckles:</asm>

Knuckles doesn't stop gliding in WFZ wind tunnels

Under WindTunnel find the line bset #1,status(a1) and insert the following lines: <asm> cmpi.b #ObjID_Knuckles,id(a1) bne.s + clr.b $21(a1)

+</asm>

The level doesn't reset properly after dying

Under ObjXX_ResetLevel_Part2, change the line <asm> tst.w ($FFFFFFDC).w</asm> to <asm> tst.w (Two_player_mode).w</asm>

Importing graphics from KiS2

Importing and PLC setup

In s2k.asm, copy everything from ArtNem_MiniKnuckles (line 75253) up to ArtNem_TitleSprites_1 (line 76054), and paste it anywhere you want in s2.asm, like after ArtNem_MiniTails.
Under PlrList_ResultsTails_End, add the lines <asm>;---------------------------------------------------------------------------------------

Pattern load queue
Knuckles life counter
---------------------------------------------------------------------------------------

PlrList_KnucklesLife: plrlistheader plreq tiles_to_bytes(ArtTile_ArtNem_life_counter), ArtNem_KTELife PlrList_KnucklesLife_End

---------------------------------------------------------------------------------------
PATTERN LOAD REQUEST LIST
Standard 2 - loaded for every level
---------------------------------------------------------------------------------------

PlrList_Std2Knuckles: plrlistheader plreq tiles_to_bytes(ArtTile_ArtNem_Checkpoint), ArtNem_Checkpoint plreq tiles_to_bytes(ArtTile_ArtNem_Powerups), ArtNem_Powerups plreq tiles_to_bytes(ArtTile_ArtNem_Powerups+$2C), ArtNem_MonitorIconsMod plreq tiles_to_bytes(ArtTile_ArtNem_Shield), ArtNem_InvincibilityShield PlrList_Std2Knuckles_End

---------------------------------------------------------------------------------------
Pattern load queue
Knuckles end of level results screen
---------------------------------------------------------------------------------------

PlrList_ResultsKnuckles: plrlistheader plreq tiles_to_bytes(ArtTile_ArtNem_TitleCard), ArtNem_TitleCard plreq tiles_to_bytes(ArtTile_ArtNem_ResultsText), ArtNem_ResultsText plreq tiles_to_bytes(ArtTile_ArtNem_MiniCharacter), ArtNem_MiniKnuckles plreq tiles_to_bytes(ArtTile_ArtNem_Perfect), ArtNem_Perfect PlrList_ResultsKnuckles_End

---------------------------------------------------------------------------------------
Pattern load queue
End of level signpost
---------------------------------------------------------------------------------------

PlrList_SignpostKnuckles: plrlistheader plreq tiles_to_bytes(ArtTile_ArtNem_Signpost), ArtNem_Signpost plreq tiles_to_bytes(ArtTile_ArtNem_Signpost+$22), ArtNem_Signpost_KnucklesPatch PlrList_SignpostKnuckles_End</asm> Then under PLCptr_ResultsTails, add <asm>PLCptr_KnucklesLife: offsetTableEntry.w PlrList_KnucklesLife PLCptr_Std2Knuckles: offsetTableEntry.w PlrList_Std2Knuckles PLCptr_ResultsKnuckles: offsetTableEntry.w PlrList_ResultsKnuckles PLCptr_SignpostKnuckles: offsetTableEntry.w PlrList_SignpostKnuckles</asm> In s2.constants.asm, under PLCID_ResultsTails add the lines <asm>PLCID_KnucklesLife = id(PLCptr_KnucklesLife) PLCID_Std2Knuckles = id(PLCptr_Std2Knuckles) PLCID_ResultsKnuckles = id(PLCptr_ResultsKnuckles) PLCID_SignpostKnuckles = id(PLCptr_SignpostKnuckles)</asm>

Loading the PLCs

Now that the art is in the PLC list, we need to tell the game to load it.
Under Level, find the line moveq #PLCID_Std2,d0 and replace everything from there up to Level_ClrRam with <asm> bsr.w Level_SetPlayerMode moveq #PLCID_Std2,d0 cmpi.w #3,(Player_mode).w ; are we Knuckles? bne.s + ; if not, branch moveq #PLCID_Std2Knuckles,d0 ; load Knuckles' standard art + bsr.w LoadPLC moveq #PLCID_Miles1up,d0 tst.w (Two_player_mode).w bne.s + cmpi.w #2,(Player_mode).w bne.s Level_NoTails addq.w #PLCID_MilesLife-PLCID_Miles1up,d0 + tst.b (Graphics_Flags).w bpl.s + addq.w #PLCID_Tails1up-PLCID_Miles1up,d0 + bsr.w LoadPLC

Level_NoTails: cmpi.w #3,(Player_mode).w bne.s Level_ClrRam moveq #PLCID_KnucklesLife,d0 bsr.w LoadPLC</asm> In CheckLoadSignpostArt find the line moveq #PLCID_Signpost,d0 and under it, add <asm> cmpi.w #3,(Player_mode).w bne.w LoadPLC2 ; load signpost art moveq #PLCID_SignpostKnuckles,d0</asm> Additionally change the bne.s + under tst.w (Two_player_mode).w to bne.s ++
Then under Load_EndOfAct, insert the following just before jsr (LoadPLC2).l: <asm> cmpi.w #3,(Player_mode).w bne.s + moveq #PLCID_ResultsKnuckles,d0 +</asm>

Life Icon mappings

In KiS2, Knuckles' initials in the life counter use palette line 1, but S2 uses line 2. To fix this, take this file and extract it to the mappings/sprite folder.
Then, under HUD_MapUnc_40C82 add <asm>HUD_MapUnc_Knuckles: BINCLUDE "mappings/sprite/hud_k.bin"</asm> Next go to BuildHUD, find the line lea (HUD_MapUnc_40A9A).l,a1 and add this below it: <asm> cmpi.w #3,(Player_mode).w bne.s + lea (HUD_MapUnc_Knuckles).l,a1 +</asm>

Score Tally

To get the score tally to display Knuckles' name, add the following lines just before Obj6F_MapUnc_14ED0: <asm>Map_Obj3A_Knuckles: offsetTable offsetTableEntry.w word_311BF6 offsetTableEntry.w word_14D1C offsetTableEntry.w word_14D5E offsetTableEntry.w word_14DA0 offsetTableEntry.w word_14DDA offsetTableEntry.w word_14BC8 offsetTableEntry.w word_14BEA offsetTableEntry.w word_14BF4 offsetTableEntry.w word_14BFE offsetTableEntry.w word_14DF4 offsetTableEntry.w word_14E1E offsetTableEntry.w word_14E50 offsetTableEntry.w word_14E82 offsetTableEntry.w word_14E8C offsetTableEntry.w word_14E96 word_311BF6: dc.w $B dc.w 5, $85C6, $82E3, $FF88 dc.w 5, $8584, $82C2, $FF98 dc.w 5, $85D8, $82EC, $FFA8 dc.w 5, $85B4, $82DA, $FFB8 dc.w 5, $85C6, $82E3, $FFC8 dc.w 5, $85C2, $82E1, $FFD8 dc.w 5, $8580, $82C0, $FFE8 dc.w 5, $85D0, $82E8, $FFF8 dc.w 5, $85B8, $82DC, $10 dc.w 5, $8588, $82C4, $20 dc.w 5, $85D4, $82EA, $2F</asm> Then, in loc_140CE find the line move.l #Obj3A_MapUnc_14CBC,mappings(a1) and add the following under it: <asm> cmpi.w #3,(Player_mode).w bne.s + move.l #Map_Obj3A_Knuckles,mappings(a1) +</asm> Under the last results_screen_object in byte_14380, add <asm>byte_14380_K: results_screen_object $28, $138, $B8, 2, 0 results_screen_object $200, $100, $CA, 4, 3 results_screen_object $240, $140, $CA, 6, 4 results_screen_object $278, $178, $BE, 8, 6 results_screen_object $350, $120, $120, 4, 9 results_screen_object $320, $120, $F0, 4, $A results_screen_object $330, $120, $100, 4, $B results_screen_object $340, $120, $110, $16, $E</asm> Then, in loc_140AC find lea byte_14380(pc),a2 and under it add <asm> cmpi.w #3,(Player_mode).w bne.s + lea byte_14380_K(pc),a2 +</asm> Download this file and put it in the art/nemesis folder. Then, under ArtNem_TitleCard2 add the lines <asm> even ArtNem_KnucklesK: BINCLUDE "art/nemesis/S2KnuxK.bin"</asm> Finally under PlrList_ResultsKnuckles (remember that?) add the line <asm> plreq tiles_to_bytes(ArtTile_ArtNem_ResultsText+$16), ArtNem_KnucklesK</asm> at the end of the list.

Title Card background

To make Knuckles' title card background green instead of red, go to LoadTitleCard0, and under bsr.w JmpTo2_NemDec add <asm> cmpi.w #3,(Player_option).w bne.s LoadTitleCard_Art2 move.l #vdpComm(tiles_to_bytes(ArtTile_ArtNem_TitleCard+$5A),VRAM,WRITE),(VDP_control_port).l moveq #$F,d0

loc_312364: move.l #$44444444,(VDP_data_port).l dbf d0,loc_312364 LoadTitleCard_Art2:</asm>

CNZ slot machines

To get Casino Night Zone's slot machines to show Knuckles instead of Sonic, go to SlotMachine_GetPixelRow and add <asm> cmpi.w #3,(Player_mode).w beq.s sub_325964</asm> Then, under the rts, paste this routine from KiS2: <asm>sub_325964:  ; ... move.w d3,d0 lsr.w #8,d0 and.w #7,d0 move.b (a3,d0.w),d0 and.w #7,d0 beq.s loc_32598C ror.w #7,d0 lea (ArtUnc_CNZSlotPics).l,a2 add.w d0,a2 move.w d3,d0 and.w #$F8,d0 lsr.w #1,d0 add.w d0,a2 rts

---------------------------------------------------------------------------

loc_32598C:  ; ... lea (byte_33B1F0).l,a2 move.w d3,d0 and.w #$F8,d0 lsr.w #1,d0 add.w d0,a2 rts

End of function sub_325964</asm>

Ending Sequence

Note: This section is not finished.

Under EndingSequence find the line move.w d0,(Ending_Routine).w and just above it add <asm> cmpi.w #3,(Player_mode).w bne.s + moveq #6,d0 cmpi.b #7,(Emerald_count).w bne.s + addq.w #2,d0 +</asm> Then under EndingSequence_LoadCharacterArt_Characters add the lines <asm> offsetTableEntry.w EndingSequence_LoadCharacterArt_Knuckles ; 6 offsetTableEntry.w EndingSequence_LoadCharacterArt_Knuckles ; 8</asm> at the end of the table.
Under the bra.w at the end of EndingSequence_LoadCharacterArt_Tails, add the lines <asm>EndingSequence_LoadCharacterArt_Knuckles: move.l #vdpComm(tiles_to_bytes(ArtTile_EndingCharacter),VRAM,WRITE),(VDP_control_port).l lea (ArtNem_EndingKnuckles).l,a0 bra.w JmpTo_NemDec</asm> Under EndingSequence_LoadFlickyArt_Flickies add the lines <asm> offsetTableEntry.w EndingSequence_LoadFlickyArt_Bird ; 6 offsetTableEntry.w EndingSequence_LoadFlickyArt_Eagle ; 8</asm> at the end of the table.
Under ObjCA, change the bit above st (Super_Sonic_flag).w to <asm> addq.w #1,objoff_32(a0) cmpi.w #8,(Ending_Routine).w beq.s + cmpi.w #2,(Ending_Routine).w beq.s + bra.s ++ +</asm> Under ObjCA_State5_States add the lines <asm> offsetTableEntry.w loc_A2E0k ; 6 offsetTableEntry.w loc_A2EEk ; 8</asm> at the end of the table.
Under the rts at the end of loc_A2F2, add the lines <asm>loc_A2E0k: moveq #$10,d0 - move.b #ObjID_Knuckles,id(a1) ; load Sonic object move.b #$81,obj_control(a1) rts

===========================================================================

loc_A2EEk: moveq #$12,d0 bra.s -</asm> Under PaletteChangerDataIndex add the lines <asm> offsetTableEntry.w off_310678 ;$10 offsetTableEntry.w off_310690 ;$12</asm> at the end of the table.
Under off_133E0, add the lines <asm>off_310678: C9PalInfo loc_1344C,Pal_3090BC, 0,$1F,4,7 off_310690: C9PalInfo loc_1344C,Pal_3090BC+$C0,0,$F,4,7</asm> Then in s2k.asm, take everything from Pal_3090BC (line 11400) up to dword_30919C (line 11406) and paste it in s2.asm under Pal_AD3E.
Under loc_A30A find the line cmpi.w #2,(Ending_Routine).w and replace everything from there to move.b #AniIDSonAni_Walk,anim(a1) with: <asm> cmpi.w #8,(Ending_Routine).w beq.s + cmpi.w #2,(Ending_Routine).w beq.s + bra.s ++ +</asm> Under loc_A4B6, find the line move.b #7,mapping_frame(a0) and just above it, add <asm> cmpi.w #8,(Ending_Routine).w beq.s +</asm> Under off_A534, add the lines <asm> offsetTableEntry.w loc_A53Ak ; 6 offsetTableEntry.w loc_A55C ; 8</asm> at the end of the table.
Under the bra.s - at the end of loc_A582, add: <asm>loc_A53Ak: move.w y_pos(a0),d0 subi.w #$1C,d0 move.w d0,y_pos(a1) move.w x_pos(a0),x_pos(a1) move.w #$500,anim(a1) move.w #$100,anim_frame_duration(a1) rts</asm> Under off_A5FC, add the lines <asm> offsetTableEntry.w byte_A602 ; 6 offsetTableEntry.w byte_A61E ; 8</asm> at the end of the table.

Adding Knuckles' sound effects from S3K

You may notice that Knuckles is missing some sound effects for when he grabs a wall, lands from a glide, and slides along the ground. Since KiS2 uses S2's sound driver directly, they couldn't add these sounds, but you can.
Download this file and extract its contents to the sound folder of your disassembly.
Just above MusicPoint2 add the lines: <asm>SonicDriverVer = 2 include "sound/_smps2asm_inc.asm"</asm> Under SoundIndex, add the following lines anywhere before SndPtr__End: <asm>SndPtr_WallGrab: rom_ptr_z80 Snd_WallGrab SndPtr_Land: rom_ptr_z80 Snd_Land SndPtr_Slide: rom_ptr_z80 Snd_Slide</asm> Their position within the list determones their priority: anything below them will be interrupted by these sounds, and anything above them can interrupt them. I personally put them just above SndPtr_Ring.
Then after the end of Sound70, above the if line, add <asm>Snd_WallGrab: include "sound/wallgrab.asm" Snd_Land: include "sound/land.asm" Snd_Slide: include "sound/slide.asm"</asm> If you try to build now, you will likely get an error telling you the sound bank is too large. In that case, you can go above SoundIndex and add <asm> align $8000 soundBankStart := *</asm> This will put the sound effects in their own sound bank.
In s2.constants.asm anywhere between SndID__First and SndID__End (it doesn't matter where) add <asm>SndID_WallGrab = id(SndPtr_WallGrab) SndID_Land = id(SndPtr_Land) SndID_Slide = id(SndPtr_Slide)</asm> Now you're ready to get Knuckles to play the sounds.
Go to loc_315804 and replace the rts with: <asm> move.w #SndID_WallGrab,d0 jmp PlaySound</asm> Just above return_315900 add: <asm> move.w #SndID_Land,d0 jmp PlaySound</asm> Then under loc_315958, just above the rts add: <asm> move.b (Timer_frames+1).w,d0 andi.b #7,d0 bne.s + moveq #SndID_Slide,d0 jmp PlaySound +</asm> Now depending on where you placed the new sounds, you may have broken some of Knuckles' code which uses numbers rather than SndID constants.
To fix this you can search for every occurrence of PlaySound, and change the move.w line just above it to use a SndID constant.
Luckily for you, the SndID definitions in s2.constants.asm all have their original values as comments, so if you find <asm> move.w #$A4,d0 jsr PlaySound</asm> you can search the SndID list to find <asm>SndID_Skidding = id(SndPtr_Skidding) ; A4</asm> and you'll replace the $A4 with SndID_Skidding. Once you do all this and test, you will notice that the sliding sound is not working. This is because a slight change to the driver is needed to get it to work. So open up s2.sounddriver.asm and locate this: <z80>; zloc_285:

zGetFrequency

zFMSetFreq: ; 'a' holds a note to get frequency for sub 80h jr z,zFMDoRest ; If this is a rest, jump to zFMDoRest add a,(ix+5) ; Add current channel key offset (coord flag E9) add a,a ; Offset into Frequency table... add a,zFrequencies&0FFh ld (zloc_292+2),a ; store into the instruction after zloc_292 (self-modifying code)

ld d,a
adc a,(zFrequencies&0FF00h)>>8
sub d
ld (zloc_292+3),a ; this is how you could store the high byte of the pointer too (unnecessary if it's in the right range)

zloc_292:</z80> There are no issues if the comments are not there; in any event, you want to edit it to match this: <z80>; zloc_285:

zGetFrequency

zFMSetFreq: ; 'a' holds a note to get frequency for sub 80h jr z,zFMDoRest ; If this is a rest, jump to zFMDoRest add a,(ix+5) ; Add current channel key offset (coord flag E9) add a,a ; Offset into Frequency table... add a,zFrequencies&0FFh ld (zloc_292+2),a ; store into the instruction after zloc_292 (self-modifying code) ld d,a adc a,(zFrequencies&0FF00h)>>8 sub d ld (zloc_292+3),a ; this is how you could store the high byte of the pointer too (unnecessary if it's in the right range) zloc_292:</z80> After you are done, find this: <z80>;zloc_460 zPSGSetFreq: sub 81h ; a = a-$81 (zero-based index from lowest note) jr c,+ ; If carry (only time that happens if 80h because of earlier logic) this is a rest! add a,(ix+5) ; Add current channel key offset (coord flag E9) add a,a ; Multiply note value by 2 add a,zPSGFrequencies&0FFh ; Point to proper place in table ld (zloc_46D+2),a ; store into the instruction after zloc_46D (self-modifying code) zloc_46D:</z80> and change it to this: <z80>;zloc_460 zPSGSetFreq: sub 81h ; a = a-$81 (zero-based index from lowest note) jr c,+ ; If carry (only time that happens if 80h because of earlier logic) this is a rest! add a,(ix+5) ; Add current channel key offset (coord flag E9) add a,a ; Multiply note value by 2 add a,zPSGFrequencies&0FFh ; Point to proper place in table ld (zloc_46D+2),a ; store into the instruction after zloc_46D (self-modifying code) ld d,a adc a,(zPSGFrequencies&0FF00h)>>8 sub d ld (zloc_46D+3),a ; this is how you could store the high byte of the pointer too (unnecessary if it's in the right range) zloc_46D:</z80> We will be moving the zFrequencies and zPSGFrequencies tables to they don't get in the way, and these changes load the high byte of the pointer too. So now time to move the tables: find this: <z80>; This the note -> frequency setting lookup

the same array is found at $729CE in Sonic 1, and at $C9C44 in Ristar
zword_359

ensure1byteoffset 8Ch zPSGFrequencies: dw 356h, 326h, 2F9h, 2CEh, 2A5h, 280h, 25Ch, 23Ah dw 21Ah, 1FBh, 1DFh, 1C4h, 1ABh, 193h, 17Dh, 167h dw 153h, 140h, 12Eh, 11Dh, 10Dh, 0FEh, 0EFh, 0E2h dw 0D6h, 0C9h, 0BEh, 0B4h, 0A9h, 0A0h, 97h, 8Fh dw 87h, 7Fh, 78h, 71h, 6Bh, 65h, 5Fh, 5Ah dw 55h, 50h, 4Bh, 47h, 43h, 40h, 3Ch, 39h dw 36h, 33h, 30h, 2Dh, 2Bh, 28h, 26h, 24h dw 22h, 20h, 1Fh, 1Dh, 1Bh, 1Ah, 18h, 17h dw 16h, 15h, 13h, 12h, 11h, 0

---------------------------------------------------------------------------</z80>

and delete it; then find this: <z80>; lookup table of FM note frequencies for instruments and sound effects ensure1byteoffset 0C0h

zbyte_534

zFrequencies: dw 025Eh,0284h,02ABh,02D3h,02FEh,032Dh,035Ch,038Fh,03C5h,03FFh,043Ch,047Ch dw 0A5Eh,0A84h,0AABh,0AD3h,0AFEh,0B2Dh,0B5Ch,0B8Fh,0BC5h,0BFFh,0C3Ch,0C7Ch dw 125Eh,1284h,12ABh,12D3h,12FEh,132Dh,135Ch,138Fh,13C5h,13FFh,143Ch,147Ch dw 1A5Eh,1A84h,1AABh,1AD3h,1AFEh,1B2Dh,1B5Ch,1B8Fh,1BC5h,1BFFh,1C3Ch,1C7Ch dw 225Eh,2284h,22ABh,22D3h,22FEh,232Dh,235Ch,238Fh,23C5h,23FFh,243Ch,247Ch dw 2A5Eh,2A84h,2AABh,2AD3h,2AFEh,2B2Dh,2B5Ch,2B8Fh,2BC5h,2BFFh,2C3Ch,2C7Ch dw 325Eh,3284h,32ABh,32D3h,32FEh,332Dh,335Ch,338Fh,33C5h,33FFh,343Ch,347Ch dw 3A5Eh,3A84h,3AABh,3AD3h,3AFEh,3B2Dh,3B5Ch,3B8Fh,3BC5h,3BFFh,3C3Ch,3C7Ch ; 96 entries

</z80> and delete it too; now find this: <z80>; ---------------------------------------------------------------------------

zbyte_FD8h

zSFXPriority:</z80> and edit it to match this: <z80>; ---------------------------------------------------------------------------

This the note -> frequency setting lookup
the same array is found at $729CE in Sonic 1, and at $C9C44 in Ristar
zword_359

zPSGFrequencies: dw 356h, 326h, 2F9h, 2CEh, 2A5h, 280h, 25Ch, 23Ah dw 21Ah, 1FBh, 1DFh, 1C4h, 1ABh, 193h, 17Dh, 167h dw 153h, 140h, 12Eh, 11Dh, 10Dh, 0FEh, 0EFh, 0E2h dw 0D6h, 0C9h, 0BEh, 0B4h, 0A9h, 0A0h, 97h, 8Fh dw 87h, 7Fh, 78h, 71h, 6Bh, 65h, 5Fh, 5Ah dw 55h, 50h, 4Bh, 47h, 43h, 40h, 3Ch, 39h dw 36h, 33h, 30h, 2Dh, 2Bh, 28h, 26h, 24h dw 22h, 20h, 1Fh, 1Dh, 1Bh, 1Ah, 18h, 17h dw 16h, 15h, 13h, 12h, 11h, 0, 10h, 0 dw 284h, 2ABh, 2D3h, 2FEh, 32Dh, 35Ch, 38Fh, 3C5h dw 3FFh, 43Ch, 47Ch, 4C0h

lookup table of FM note frequencies for instruments and sound effects
zbyte_534

zFrequencies: dw 025Eh,0284h,02ABh,02D3h,02FEh,032Dh,035Ch,038Fh,03C5h,03FFh,043Ch,047Ch dw 0A5Eh,0A84h,0AABh,0AD3h,0AFEh,0B2Dh,0B5Ch,0B8Fh,0BC5h,0BFFh,0C3Ch,0C7Ch dw 125Eh,1284h,12ABh,12D3h,12FEh,132Dh,135Ch,138Fh,13C5h,13FFh,143Ch,147Ch dw 1A5Eh,1A84h,1AABh,1AD3h,1AFEh,1B2Dh,1B5Ch,1B8Fh,1BC5h,1BFFh,1C3Ch,1C7Ch dw 225Eh,2284h,22ABh,22D3h,22FEh,232Dh,235Ch,238Fh,23C5h,23FFh,243Ch,247Ch dw 2A5Eh,2A84h,2AABh,2AD3h,2AFEh,2B2Dh,2B5Ch,2B8Fh,2BC5h,2BFFh,2C3Ch,2C7Ch dw 325Eh,3284h,32ABh,32D3h,32FEh,332Dh,335Ch,338Fh,33C5h,33FFh,343Ch,347Ch dw 3A5Eh,3A84h,3AABh,3AD3h,3AFEh,3B2Dh,3B5Ch,3B8Fh,3BC5h,3BFFh,3C3Ch,3C7Ch ; 96 entries

---------------------------------------------------------------------------
zbyte_FD8h

zSFXPriority:</z80> If you compare it with the old tables, you will see that zPSGFrequencies gained several new entries; it is because of these new entries that we had to move all this stuff around. Also, the sliding sound uses these entries, which do not exist in the Sonic 2 driver; so it should now be fixed.

SCHG How-To Guide: Sonic the Hedgehog 2 (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix Super Sonic Bugs | Use Correct Height When Roll Jumping | Fix Jump Height Bug When Exiting Water | Fix Screen Boundary Spin Dash Bug | Correct Drowning Bugs | Fix Camera Y Position for Tails | Fix Tails Subanimation Error | Fix Tails' Respawn Speeds | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix Rexon Crash | Fix Monitor Collision Bug | Fix EHZ Deformation Bug | Correct CPZ Boss Attack Behavior | Fix Bug in ARZ Boss Arrow's Platform Behavior | Fix ARZ Boss Walking on Air Glitch | Fix ARZ Boss Sprite Behavior | Fix Multiple CNZ Boss Bugs | Fix HTZ Background Scrolling Mountains | Fix OOZ Launcher Speed Up Glitch | Fix DEZ Giant Mech Collision Glitch | Fix Boss Deconstruction Behavior | Fix Speed Bugs | Fix 14 Continues Cheat | Fix Debug Mode Crash | Fix 99+ Lives | Fix Sonic 2's Sega Screen
Design Choices
Remove the Air Speed Cap | Disable Floor Collision While Dying | Modify Super Sonic Transformation Methods & Behavior | Enable/Disable Tails in Certain Levels | Collide with Water After Being Hurt | Retain Rings When Returning at a Star Post | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Insert LZ Water Ripple Effect | Restore Lost CPZ Boss Feature | Prevent SCZ Tornado Spin Dash Death | Improve ObjectMove Subroutines | Port S3K Rings Manager | Port S3K Object Manager | Port S3K Priority Manager | Edit Level Order with ASM‎ | Alter Ring Requirements in Special Stages | Make Special Stage Characters Use Normal DPLCs | Speed Up Ring Loss Process | Change spike behaviour in Sonic 2
Adding Features
Create Insta-kill and High Jump Monitors | Create Clone and Special Stage Monitors | Port Knuckles
Sound Features
Expand Music Index to Start at $00 | Port Sonic 1 Sound Driver | Port Sonic 2 Clone Driver | Port Sonic 3 Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Expand the Music Index to Start at $00 (Sonic 2 Clone Driver Version) | Play Different Songs Per Act
Extending the Game
Extend the Level Index Past $10 | Extend the Level Select | Extend Water Tables | Add Extra Characters | Free Up 2 Universal SSTs