Actions

SCHG How-to

Difference between revisions of "Port S3K Priority Manager into Sonic 2"

From Sonic Retro

m (Step 5: Make sure the copiers are copying correctly: Added color coding)
m (Step 15: Done: cleanup)
 
(27 intermediate revisions by 9 users not shown)
Line 1: Line 1:
(Guide written by [[redhotsonic]])
+
(Guide written by [[User:Redhotsonic|redhotsonic]])
 +
 
 +
(Additional fix by [[User:Pokepunch|Pokepunch]])
  
 
==Preface==
 
==Preface==
'''WARNING: Please back-up your disassembly before attempting to use this guide. I will not be held responsible for anything that goes wrong.'''
+
'''WARNING: Please back-up your disassembly before attempting to use this guide. I will not be held responsible for anything that goes wrong. Alternatively, use a version control system such as Mercurial (used for the Sonic Retro repositories) or git, which will not only allow you to back up your repository before making this change but at every step and in all future steps of whatever changes you make, making finding errors easier.'''
  
This guide is mainly following '''Xenowhirl's 2007 disassembly'''. But, if you're an SVN user, I have tried to make this guide as friendly as possible for you. Anything marked as such is only for <span style="color:green">'''SVN'''</span> or <span style="color:red">'''Xenowhirl'''</span> disassemblies. '''PAY ATTENTION!'''
+
This guide is mainly following '''Xenowhirl's 2007 disassembly'''. But, if you're an SVN user, I have tried to make this guide as friendly as possible for you. Anything marked as such is only for {{green|'''SVN'''}} or {{red|'''Xenowhirl'''}} disassemblies. '''PAY ATTENTION!'''
  
<span style="color:red">'''Anything in red is for Xenowhirls 2007 disassembly users only.'''</span>
+
{{red|Anything in red is for Xenowhirl's 2007 disassembly users only.}}<br>
 +
{{green|Anything in green is for SVN disassembly users only.}}
  
<span style="color:green">'''Anything in green is for SVN disassembly users only.'''</span>
+
'''Also, ''EVERY SST MUST BE EQUATED''! So, if you're using an old disassembly, you won't be able to follow this. Also, for any objects you've made yourself or have ported, make sure that it's all equated (so instead of $18(a0), it should be priority(a0).) Anything that has not been equated may start to cause problems; you have been warned!'''
 
 
'''Also, ''EVERY SST MUST BE EQUATED''! So, if you're using Hivebrain's disassembly, you won't be able to follow this. Also, for any objects you've made yourself or have ported, make sure that it's all equated (so instead of $18(a0), it should be priority(a0).) Anything that has not been equated may start to cause problems; you have been warned!'''
 
  
 
==What is "Priority"?==
 
==What is "Priority"?==
Line 16: Line 17:
  
 
==Differences between S2 & S3 Managers==
 
==Differences between S2 & S3 Managers==
In Sonic 2, the priority's universal SST is a byte. It's start's from #0 (highest) upto #7 (lowest). When each object jumps to the "DisplaySprite" subroutine, it firsts, converts the priority byte into a word. And then it can check whether to display the sprite, and if so, display it. It has to do this every frame for every object for the sprite to be displayed.
+
In Sonic 2, the priority's universal SST is a byte. It's start's from #0 (highest) up to #7 (lowest). When each object jumps to the "DisplaySprite" subroutine, it firsts, converts the priority byte into a word. And then it can check whether to display the sprite, and if so, display it. It has to do this every frame for every object for the sprite to be displayed.
  
 
In Sonic 3 and Knuckles, priority is already a word, again, it's start's from #0 (highest) but goes upto #$380 (lowest). That doesn't mean there's $380 types of priority. It goes up in $80's (#0, $80, $100, $180, etc). Because it's already set as a word, when it jumps to the "DisplaySprite" subroutine, it doesn't have to do them calculations, and can just check whether to display the sprite or not and if so, display it.
 
In Sonic 3 and Knuckles, priority is already a word, again, it's start's from #0 (highest) but goes upto #$380 (lowest). That doesn't mean there's $380 types of priority. It goes up in $80's (#0, $80, $100, $180, etc). Because it's already set as a word, when it jumps to the "DisplaySprite" subroutine, it doesn't have to do them calculations, and can just check whether to display the sprite or not and if so, display it.
Line 30: Line 31:
  
 
If you haven't done this already, you will need to do so. Below is the second step from the SST guide. Follow this step before continuing.
 
If you haven't done this already, you will need to do so. Below is the second step from the SST guide. Follow this step before continuing.
First, go to Sonic's SST's <span style="color:green">'''(This will be in s2constants.asm for SVN users)'''</span> and you'll see
+
First, go to Sonic's SST's {{green|(This will be in '''s2constants.asm''' for SVN users)}} and you'll see
  
<asm>
+
<syntaxhighlight lang="asm">
 
inertia =              $14 ; and $15 ; directionless representation of speed... not updated in the air
 
inertia =              $14 ; and $15 ; directionless representation of speed... not updated in the air
</asm>
+
</syntaxhighlight>
  
 
Change it to
 
Change it to
<asm>
+
<syntaxhighlight lang="asm">
 
inertia =              $20 ; and $21 ; directionless representation of speed... not updated in the air
 
inertia =              $20 ; and $21 ; directionless representation of speed... not updated in the air
</asm>
+
</syntaxhighlight>
  
 
For Sonic and Tails, $20 and $21 is free, so move it there. Why? Simple. $14 is a "convention followed by most objects". I think $15 is still only used by Sonic and Tails, but that doesn't matter. Now, instead of the original $1F, $20 and $21 being free for Sonic and Tails, it's now $15 and $1F that is free for them ($14 is NOT free for Sonic and Tails! About to explain why!)
 
For Sonic and Tails, $20 and $21 is free, so move it there. Why? Simple. $14 is a "convention followed by most objects". I think $15 is still only used by Sonic and Tails, but that doesn't matter. Now, instead of the original $1F, $20 and $21 being free for Sonic and Tails, it's now $15 and $1F that is free for them ($14 is NOT free for Sonic and Tails! About to explain why!)
  
 
Next, go to:
 
Next, go to:
<asm>
+
<syntaxhighlight lang="asm">
 
width_pixels =          $19
 
width_pixels =          $19
</asm>
+
</syntaxhighlight>
  
 
and change to
 
and change to
<asm>
+
<syntaxhighlight lang="asm">
 
width_pixels =          $14
 
width_pixels =          $14
</asm>
+
</syntaxhighlight>
  
 
''width_pixels'' can be moved to $14. Which means, $19 is now free, and it's universal! How about that? Told you it was piss easy! I am using $19 as part of priority (in S3K's priority manager, the priority is a word, so I use $18 and $19). And that's the reason why $14 isn't free for Sonic and Tails, because it's being used for width_pixel. Yes, you're going to lose a SST from them but it's better to gain a universal SST, right?
 
''width_pixels'' can be moved to $14. Which means, $19 is now free, and it's universal! How about that? Told you it was piss easy! I am using $19 as part of priority (in S3K's priority manager, the priority is a word, so I use $18 and $19). And that's the reason why $14 isn't free for Sonic and Tails, because it's being used for width_pixel. Yes, you're going to lose a SST from them but it's better to gain a universal SST, right?
Line 61: Line 62:
  
 
Next, we need to change DisplaySprite and DisplaySprite2. '''Do NOT edit/change or replace DisplaySprite3! Leave that as it is!''' At "DisplaySprite:", you'll see this:
 
Next, we need to change DisplaySprite and DisplaySprite2. '''Do NOT edit/change or replace DisplaySprite3! Leave that as it is!''' At "DisplaySprite:", you'll see this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  priority(a0),d0
 
         move.w  priority(a0),d0
 
         lsr.w  #1,d0
 
         lsr.w  #1,d0
 
         andi.w  #$380,d0
 
         andi.w  #$380,d0
 
         adda.w  d0,a1
 
         adda.w  d0,a1
</asm>
+
</syntaxhighlight>
  
These are the lines that does the calculations every single frame. This is what converts the byte into a word. Like said in S3K, as priority is already a word, these lines are now useless. As this is what we're trying to acheive, we can do the same thing.
+
These are the lines that does the calculations every single frame. This is what converts the byte into a word. Like said in S3K, as priority is already a word, these lines are now useless. As this is what we're trying to achieve, we can do the same thing.
  
 
So, at "DisplaySprite:", change this:
 
So, at "DisplaySprite:", change this:
<asm>
+
<syntaxhighlight lang="asm">
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
  
Line 113: Line 114:
 
         rts
 
         rts
 
; End of function DisplaySprite2
 
; End of function DisplaySprite2
</asm>
+
</syntaxhighlight>
  
 
To this
 
To this
<asm>
+
<syntaxhighlight lang="asm">
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
  
Line 152: Line 153:
 
         rts
 
         rts
 
; End of function DisplaySprite2
 
; End of function DisplaySprite2
</asm>
+
</syntaxhighlight>
  
 
Both of these subroutines are now shorter. May not look by much, but remember, as this is getting repeated all the time for EACH object for each frame, this will make a lot of difference.
 
Both of these subroutines are now shorter. May not look by much, but remember, as this is getting repeated all the time for EACH object for each frame, this will make a lot of difference.
Line 165: Line 166:
 
So, the best thing I found it to actually use them calculations from Sonic 2's DisplaySprite to convert them into a word. So, when creating the object, it will have the priority as a byte, and then do Sonic 2's calculations to convert it into a word. Priority will then remain as a word for the rest of the time that object is there for. Because of this, it will only need to do this calculation once, then carry on the S3K way. Doing it once then the S3K way, is better then doing them calculations all the time, right?
 
So, the best thing I found it to actually use them calculations from Sonic 2's DisplaySprite to convert them into a word. So, when creating the object, it will have the priority as a byte, and then do Sonic 2's calculations to convert it into a word. Priority will then remain as a word for the rest of the time that object is there for. Because of this, it will only need to do this calculation once, then carry on the S3K way. Doing it once then the S3K way, is better then doing them calculations all the time, right?
  
So, first, go to <span style="color:red">'''"loc_112A4:"'''</span> <span style="color:green">'''("Obj1C_Init:" on SVN)'''</span> This is "Object 1C - Bridge stake in Emerald Hill Zone and Hill Top Zone, falling oil in Oil Ocean Zone" object. You can see it's moving a lot of data to mappings and etc. It does this because there's so many subtypes to the object, and this is the best and quickest way for that object to load.
+
So, first, go to {{red|'''"loc_112A4:"'''}} {{green|'''("Obj1C_Init:" on SVN)'''}} This is "Object 1C - Bridge stake in Emerald Hill Zone and Hill Top Zone, falling oil in Oil Ocean Zone" object. You can see it's moving a lot of data to mappings and etc. It does this because there's so many subtypes to the object, and this is the best and quickest way for that object to load.
  
 
So, underneath "move.b (a1)+,priority(a0)", add this:
 
So, underneath "move.b (a1)+,priority(a0)", add this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  priority(a0),d0
 
         move.w  priority(a0),d0
 
         lsr.w  #1,d0
 
         lsr.w  #1,d0
 
         andi.w  #$380,d0
 
         andi.w  #$380,d0
 
         move.w  d0,priority(a0)
 
         move.w  d0,priority(a0)
</asm>
+
</syntaxhighlight>
  
So you have something like this <span style="color:green">'''(SVN user, your code will look slightly different, this is only a reference)'''</span>:
+
So you have something like this {{green|'''(SVN user, your code will look slightly different, this is only a reference)''':}}
<asm>
+
<syntaxhighlight lang="asm">
 
loc_112A4:
 
loc_112A4:
 
         addq.b  #2,routine(a0)
 
         addq.b  #2,routine(a0)
Line 201: Line 202:
 
         move.b  d1,y_radius(a0)
 
         move.b  d1,y_radius(a0)
 
         bset    #4,render_flags(a0)
 
         bset    #4,render_flags(a0)
</asm>
+
</syntaxhighlight>
  
 
Whatever data is grabbed from the table, it's now converted it to a word, and it will never do it again. It will always remain as a word. So, when it goes to DisplaySprite over and over, it can just get on with it.
 
Whatever data is grabbed from the table, it's now converted it to a word, and it will never do it again. It will always remain as a word. So, when it goes to DisplaySprite over and over, it can just get on with it.
  
Next, go to <span style="color:red">'''"loc_1131A:"'''</span> <span style="color:green">'''("Obj71_Init:" for SVN)'''</span> (Object 71 - Bridge stake and pulsing orb from Hidden Palace Zone) and do the same thing, so you end up with this <span style="color:green">'''(SVN users, reference only)'''</span>:
+
Next, go to {{red|'''"loc_1131A:"'''}} {{green|'''("Obj71_Init:" for SVN)'''}} (Object 71 - Bridge stake and pulsing orb from Hidden Palace Zone) and do the same thing, so you end up with this {{green|'''(SVN users, reference only)''':}}
<asm>
+
<syntaxhighlight lang="asm">
 
loc_1131A:
 
loc_1131A:
 
         addq.b  #2,routine(a0)
 
         addq.b  #2,routine(a0)
Line 229: Line 230:
 
         lsr.b  #4,d0
 
         lsr.b  #4,d0
 
         move.b  d0,anim(a0)
 
         move.b  d0,anim(a0)
</asm>
+
</syntaxhighlight>
  
Go to "loc_3F228:" (Object 3E - Egg prison) and '''''almost''''' do the same thing again. Just look at the registers though, as they are different (it's '''a1''' instead of '''a0'''). You should end up with this <span style="color:green">'''(SVN users, reference only)'''</span>:
+
Go to "loc_3F228:" (Object 3E - Egg prison) and '''''almost''''' do the same thing again. Just look at the registers though, as they are different (it's '''a1''' instead of '''a0'''). You should end up with this {{green|'''(SVN users, reference only)''':}}
<asm>
+
<syntaxhighlight lang="asm">
 
loc_3F228:
 
loc_3F228:
 
         _move.b 0(a0),0(a1) ; load obj
 
         _move.b 0(a0),0(a1) ; load obj
Line 253: Line 254:
 
         move.w  d0,priority(a1)
 
         move.w  d0,priority(a1)
 
         move.b  (a2)+,mapping_frame(a1)
 
         move.b  (a2)+,mapping_frame(a1)
</asm>
+
</syntaxhighlight>
  
 
And finally, go to "LoadSubObject_Part3:". Now this loads all the priorities, widths, mappings, etc, to all objects past Obj8C. This is a life saver. So this will save us a lot of time from doing it to a lot of other objects.
 
And finally, go to "LoadSubObject_Part3:". Now this loads all the priorities, widths, mappings, etc, to all objects past Obj8C. This is a life saver. So this will save us a lot of time from doing it to a lot of other objects.
  
 
So, change this:
 
So, change this:
<asm>
+
<syntaxhighlight lang="asm">
 
LoadSubObject_Part3:
 
LoadSubObject_Part3:
 
         move.l  (a1)+,mappings(a0)
 
         move.l  (a1)+,mappings(a0)
Line 270: Line 271:
 
         addq.b  #2,routine(a0)
 
         addq.b  #2,routine(a0)
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
LoadSubObject_Part3:
 
LoadSubObject_Part3:
 
         move.l  (a1)+,mappings(a0)
 
         move.l  (a1)+,mappings(a0)
Line 289: Line 290:
 
         addq.b  #2,routine(a0)
 
         addq.b  #2,routine(a0)
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
That's half the objects done already!
 
That's half the objects done already!
Line 297: Line 298:
  
 
At "Obj5E:", you'll see this:
 
At "Obj5E:", you'll see this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  #0,priority(a0)
 
         move.b  #0,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
Change it to this:
 
Change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #0,priority(a0)
 
         move.w  #0,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
The move command has now changed from .b to .w as priority is now a word. The number will also need changing, but #0 in S2 is the same as S3K. So 0 doesn't need changing here.
 
The move command has now changed from .b to .w as priority is now a word. The number will also need changing, but #0 in S2 is the same as S3K. So 0 doesn't need changing here.
  
Another example. Go to <span style="color:red">'''"loc_7158:"'''</span> <span style="color:green">'''("Obj5F_Init:" for SVN)'''</span> and change this:
+
Another example. Go to {{red|'''"loc_7158:"'''}} {{green|'''("Obj5F_Init:" for SVN)'''}} and change this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  #1,priority(a0)
 
         move.b  #1,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$80,priority(a0)
 
         move.w  #$80,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
Again, the .b has change to .w (this must be done ALL the time, even if the number is 0). This time, the number is 1. After calculations (or in S3K), the number will be $80. So it's been changed to $80. Remember, in S2, the numbers were only #. With the S3K way (unless 0), it will need to be #$.
 
Again, the .b has change to .w (this must be done ALL the time, even if the number is 0). This time, the number is 1. After calculations (or in S3K), the number will be $80. So it's been changed to $80. Remember, in S2, the numbers were only #. With the S3K way (unless 0), it will need to be #$.
Line 343: Line 344:
  
 
Want one more example? Okay. Go to "ObjDB_Sonic_Init:" and change this:
 
Want one more example? Okay. Go to "ObjDB_Sonic_Init:" and change this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  #2,priority(a0)
 
         move.b  #2,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$100,priority(a0)
 
         move.w  #$100,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
It's that simple. Now go! Convert all them priorities!
 
It's that simple. Now go! Convert all them priorities!
Line 357: Line 358:
  
 
Search:
 
Search:
<asm>
+
<syntaxhighlight lang="asm">
 
move.b  #2,priority(a
 
move.b  #2,priority(a
</asm>
+
</syntaxhighlight>
  
 
Replace with:
 
Replace with:
<asm>
+
<syntaxhighlight lang="asm">
 
move.w  #$100,priority(a
 
move.w  #$100,priority(a
</asm>
+
</syntaxhighlight>
  
 
...and etc. That way, it will replace them all, no matter what register it has. Do the same from #0 - #7.
 
...and etc. That way, it will replace them all, no matter what register it has. Do the same from #0 - #7.
Line 371: Line 372:
  
 
==Step 5: Make sure the copiers are copying correctly==
 
==Step 5: Make sure the copiers are copying correctly==
<span style="color:red">'''At these locations (Xenowhirl 2007 users):
+
{{red|At these locations '''(Xenowhirl 2007 users)''':}}
  
* loc_10B9E:
+
* {{red|'''loc_10B9E:}}
* loc_15E46:
+
* {{red|'''loc_15E46:}}
* Obj79_MakeSpecialStars:
+
* {{red|'''Obj79_MakeSpecialStars:}}
* loc_25C24:
+
* {{red|'''loc_25C24:}}
* loc_28A6E:'''</span>
+
* {{red|'''loc_28A6E:}}
  
<span style="color:green">'''SVN users ONLY:
+
{{green|'''SVN users ONLY:''' For first label, do a search for this line:}}
For first label, do a search for this line:'''</span>
+
<syntaxhighlight lang="asm">
<asm>
 
 
         _move.b d4,id(a1) ; load obj1F
 
         _move.b d4,id(a1) ; load obj1F
</asm>
+
</syntaxhighlight>
<span style="color:green">'''Second label is "BreakObjectToPieces_InitObject:"
+
{{green|Second label is '''"BreakObjectToPieces_InitObject:"'''<br>
Third label is "Obj79_MakeSpecialStars:"
+
Third label is '''"Obj79_MakeSpecialStars:"'''<br>
Fourth Label is "loc_25C24:"
+
Fourth Label is '''"loc_25C24:"<br>'''
And fifth label is "Obj73_LoadSubObject:"'''</span>
+
And fifth label is '''"Obj73_LoadSubObject:"'''}}
  
 
Under each of these labels, you should see this line:
 
Under each of these labels, you should see this line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  priority(a0),priority(a1)
 
         move.b  priority(a0),priority(a1)
</asm>
+
</syntaxhighlight>
  
 
Some objects like these, move the priority to a1 from a0. As you can see, it's only moving a byte. Change the line at all these locations to this:
 
Some objects like these, move the priority to a1 from a0. As you can see, it's only moving a byte. Change the line at all these locations to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  priority(a0),priority(a1)
 
         move.w  priority(a0),priority(a1)
</asm>
+
</syntaxhighlight>
  
 
All the registers at these locations are the same, so the above line is fine to use.
 
All the registers at these locations are the same, so the above line is fine to use.
Line 405: Line 405:
  
 
Go to "loc_34864:" (Tails in Special Stage) and change this bit only:
 
Go to "loc_34864:" (Tails in Special Stage) and change this bit only:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  priority(a0),priority(a1)
 
         move.b  priority(a0),priority(a1)
 
         subi.b  #1,priority(a1)
 
         subi.b  #1,priority(a1)
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  priority(a0),priority(a1)
 
         move.w  priority(a0),priority(a1)
 
         subi.w  #$80,priority(a1)
 
         subi.w  #$80,priority(a1)
</asm>
+
</syntaxhighlight>
  
So you have this '''(SVN users, reference only)''':
+
So you have this <span style="color:green">'''(SVN users, reference only)'''</span>:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_34864:
 
loc_34864:
 
         move.w  #$400,objoff_32(a0)
 
         move.w  #$400,objoff_32(a0)
Line 450: Line 450:
 
         movea.l objoff_38(a0),a0 ; load 0bj address
 
         movea.l objoff_38(a0),a0 ; load 0bj address
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
Now, it's moving a WORD to a1, then subtracting $80 instead of 1. So now it's going how S3K would do it.
 
Now, it's moving a WORD to a1, then subtracting $80 instead of 1. So now it's going how S3K would do it.
  
 
Similar story here. Go to "Obj88:" (Tails' tails in Special Stage) and change this bit only:
 
Similar story here. Go to "Obj88:" (Tails' tails in Special Stage) and change this bit only:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  priority(a1),d0
 
         move.b  priority(a1),d0
 
         subq.b  #1,d0
 
         subq.b  #1,d0
 
         move.b  d0,priority(a0)
 
         move.b  d0,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  priority(a1),d0
 
         move.w  priority(a1),d0
 
         subi.w  #$80,d0
 
         subi.w  #$80,d0
 
         move.w  d0,priority(a0)
 
         move.w  d0,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
One more. Go to "loc_3C1F4:" (Breakable plating from WFZ) and change this bit only:
 
One more. Go to "loc_3C1F4:" (Breakable plating from WFZ) and change this bit only:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  priority(a0),d4
 
         move.b  priority(a0),d4
 
         subq.b  #1,d4
 
         subq.b  #1,d4
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  priority(a0),d4
 
         move.w  priority(a0),d4
 
         subi.w  #$80,d4
 
         subi.w  #$80,d4
</asm>
+
</syntaxhighlight>
  
 
There's a little more to this object. Next, go to "loc_3C20E:" and change this bit only:
 
There's a little more to this object. Next, go to "loc_3C20E:" and change this bit only:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  d4,priority(a1)
 
         move.b  d4,priority(a1)
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  d4,priority(a1)
 
         move.w  d4,priority(a1)
</asm>
+
</syntaxhighlight>
  
 
These objects will now display correctly.
 
These objects will now display correctly.
Line 499: Line 499:
  
 
The EHZ boss is showing these commands:
 
The EHZ boss is showing these commands:
<asm>
+
<syntaxhighlight lang="asm">
 
         cmpi.b  #2,priority(a0)
 
         cmpi.b  #2,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
Change them to this:
 
Change them to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         cmpi.w  #$100,priority(a0)
 
         cmpi.w  #$100,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
They were still treating priority as a byte, so we just needed to change these to a word.
 
They were still treating priority as a byte, so we just needed to change these to a word.
Line 515: Line 515:
 
Luckily, there are many objects that do a similar thing; using priority for a complete different reason. This is where "DisplaySprite3" comes into play. Objects that use priority SST for other reasons, instead, moves a word to d0, then jumps to DisplaySprite3. So, we're going to do something similar for ARZ, CNZ, MCZ and OOZ's bosses.
 
Luckily, there are many objects that do a similar thing; using priority for a complete different reason. This is where "DisplaySprite3" comes into play. Objects that use priority SST for other reasons, instead, moves a word to d0, then jumps to DisplaySprite3. So, we're going to do something similar for ARZ, CNZ, MCZ and OOZ's bosses.
  
'''ARZ boss'''
+
====ARZ boss====
 
Go to "loc_304D4:" and delete the line:
 
Go to "loc_304D4:" and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$100,priority(a0)
 
         move.w  #$100,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
This is no longer needed, as it's going to be replaced with other things later in the ARZ boss code anyway.
 
This is no longer needed, as it's going to be replaced with other things later in the ARZ boss code anyway.
  
 
Still at this label, you should also see these commands:
 
Still at this label, you should also see these commands:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$200,priority(a1)
 
         move.w  #$200,priority(a1)
</asm>
+
</syntaxhighlight>
  
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$100,priority(a1)
 
         move.w  #$100,priority(a1)
</asm>
+
</syntaxhighlight>
  
 
You might as well get rid of them too.
 
You might as well get rid of them too.
  
 
'''Make sure you do NOT delete this line:'''
 
'''Make sure you do NOT delete this line:'''
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$488,priority(a0)
 
         move.w  #$488,priority(a0)
</asm>
+
</syntaxhighlight>
  
'''''XENOWHIRL NOTE:''''' This is for the y_pos of the hammer, one of the reason why the priority needs changing of the boss.
+
'''This is for the y_pos of the hammer, one of the reason why the priority needs changing of the boss.'''
  
 
Next, go to "loc_30BC8:" and find and delete the line:
 
Next, go to "loc_30BC8:" and find and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$200,priority(a0)
 
         move.w  #$200,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
Again, no point in this!
 
Again, no point in this!
  
 
Next, find the label "JmpTo37_DisplaySprite". Only the ARZ boss jumps to this location for displaying a sprite, so we know we can freely edit this for our ARZ boss without affecting anything else. Anyway, you should see this '''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':
 
Next, find the label "JmpTo37_DisplaySprite". Only the ARZ boss jumps to this location for displaying a sprite, so we know we can freely edit this for our ARZ boss without affecting anything else. Anyway, you should see this '''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo37_DisplaySprite  
 
JmpTo37_DisplaySprite  
 
         jmp    DisplaySprite
 
         jmp    DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
Now, because them odd numbers are set as priority, when it jumps to DisplaySprite, it will try to process it and freeze. So, this is what we're going to do. Change it to this:
 
Now, because them odd numbers are set as priority, when it jumps to DisplaySprite, it will try to process it and freeze. So, this is what we're going to do. Change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo37_DisplaySprite  
 
JmpTo37_DisplaySprite  
 
         move.w  #$200,d0        ; move $200 to ARZ boss' priority
 
         move.w  #$200,d0        ; move $200 to ARZ boss' priority
 
         jmp    DisplaySprite3  ; Dispaly it
 
         jmp    DisplaySprite3  ; Dispaly it
</asm>
+
</syntaxhighlight>
  
 
Now, everytime the boss needs displaying, instead of grabbing the data from the priority SST, it will process what we've just moved to d0. #$200 seems good, I use that.
 
Now, everytime the boss needs displaying, instead of grabbing the data from the priority SST, it will process what we've just moved to d0. #$200 seems good, I use that.
Line 567: Line 567:
 
Viola! ARZ boss is sorted and will no longer freeze.
 
Viola! ARZ boss is sorted and will no longer freeze.
  
'''CNZ boss'''
+
====CNZ boss====
 
The problem with this boss is the exact same problem as ARZ. Priority is being used for something else. Same sort of fix applies here.
 
The problem with this boss is the exact same problem as ARZ. Priority is being used for something else. Same sort of fix applies here.
  
 
Go to "loc_31904:" and delete the line:
 
Go to "loc_31904:" and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$180,priority(a0)
 
         move.w  #$180,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
Also, go to "loc_31F48:" and delete the line:
 
Also, go to "loc_31F48:" and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$380,priority(a0)
 
         move.w  #$380,priority(a0)
</asm>
+
</syntaxhighlight>
  
Next, find the label "JmpTo39_DisplaySprite". Only the CNZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this '''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':
+
Next, find the label "JmpTo39_DisplaySprite". Only the CNZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this <span style="color:green">'''(SVN user, yours will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)'''</span>:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo39_DisplaySprite  
 
JmpTo39_DisplaySprite  
 
         jmp    DisplaySprite
 
         jmp    DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
Change it to this:
 
Change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo39_DisplaySprite  
 
JmpTo39_DisplaySprite  
 
         move.w  #$180,d0        ; move $180 to CNZ boss' priority
 
         move.w  #$180,d0        ; move $180 to CNZ boss' priority
 
         jmp    DisplaySprite3  ; Dispaly it
 
         jmp    DisplaySprite3  ; Dispaly it
</asm>
+
</syntaxhighlight>
  
 
CNZ boss should no longer freeze. #$180 seems good here. Again, CNZ boss shares the same problem as ARZ boss, so Eggman, the catchers and spikeball have to be the same priority, and you shouldn't notice any difference.
 
CNZ boss should no longer freeze. #$180 seems good here. Again, CNZ boss shares the same problem as ARZ boss, so Eggman, the catchers and spikeball have to be the same priority, and you shouldn't notice any difference.
  
 
+
====MCZ boss====
'''MCZ boss'''
+
Same issue. Priority is being used for something else. Same sort of fix applies here. But luckily, all the priorities are #$180, so at least the priority won't change after this edit unlike the previous two bosses we've edited.
Same issue. Priority is being used for something else. Same sort of fix applies here. But luckily, all the priorties are #$180, so at least the priority won't change after this edit unlike the previous two bosses we've edited.
 
  
 
Go to "loc_30FB8:" and delete the line:
 
Go to "loc_30FB8:" and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$180,priority(a0)
 
         move.w  #$180,priority(a0)
</asm>
+
</syntaxhighlight>
  
Also, go to "loc_313DA:" '''("Obj57_LoadStoneSpike:" for SVN)''' and delete the same(ish) line (I say 'ish' because it's using a1 instead of a0):
+
Also, go to <span style="color:red">'''"loc_313DA:"'''</span> <span style="color:green">'''("Obj57_LoadStoneSpike:" for SVN)'''</span> and delete the same(ish) line (I say 'ish' because it's using a1 instead of a0):
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$180,priority(a1)
 
         move.w  #$180,priority(a1)
</asm>
+
</syntaxhighlight>
  
Next, find the label "JmpTo38_DisplaySprite". Only the MCZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this '''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':
+
Next, find the label "JmpTo38_DisplaySprite". Only the MCZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this <span style="color:green">'''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)'''</span>:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo38_DisplaySprite  
 
JmpTo38_DisplaySprite  
 
         jmp    DisplaySprite
 
         jmp    DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
Change it to this:
 
Change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo38_DisplaySprite  
 
JmpTo38_DisplaySprite  
 
         move.w  #$180,d0        ; move $180 to MCZ boss' priority
 
         move.w  #$180,d0        ; move $180 to MCZ boss' priority
 
         jmp    DisplaySprite3  ; Dispaly it
 
         jmp    DisplaySprite3  ; Dispaly it
</asm>
+
</syntaxhighlight>
  
 
Seeming as both priority lines we just deleted was #$180, we know that we can use #$180 here. MCZ boss should no longer freeze.
 
Seeming as both priority lines we just deleted was #$180, we know that we can use #$180 here. MCZ boss should no longer freeze.
  
'''OOZ boss'''
+
====OOZ boss====
 
This boss uses Priority as a x_pos for the sub objects. Again, we're going to have to do a similar thing like we did with the other bosses.
 
This boss uses Priority as a x_pos for the sub objects. Again, we're going to have to do a similar thing like we did with the other bosses.
  
Go to "loc_32FA8:" '''("Obj55_Init:" for SVN)''' and delete the line:
+
Go to <span style="color:red">'''"loc_32FA8:"'''</span> <span style="color:green">'''("Obj55_Init:" for SVN)'''</span> and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$180,priority(a0)
 
         move.w  #$180,priority(a0)
</asm>
+
</syntaxhighlight>
  
Also, go to "loc_33586:" '''("Obj55_Laser_Init:" for SVN)''' and delete the line:
+
Also, go to <span style="color:red">'''"loc_33586:"'''</span> <span style="color:green">'''("Obj55_Laser_Init:" for SVN)'''</span> and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$200,priority(a0)
 
         move.w  #$200,priority(a0)
</asm>
+
</syntaxhighlight>
  
Also, go to "loc_33640:" '''("Obj55_Laser_CreateWave:" for SVN)''' and delete the line:
+
Also, go to <span style="color:red">'''"loc_33640:"'''</span> <span style="color:green">'''("Obj55_Laser_CreateWave:" for SVN)'''</span> and delete the line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$100,priority(a1)
 
         move.w  #$100,priority(a1)
</asm>
+
</syntaxhighlight>
  
Next, find the label "JmpTo41_DisplaySprite". Only the OOZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this '''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':
+
Next, find the label "JmpTo41_DisplaySprite". Only the OOZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this <span style="color:green">'''(SVN user, yours will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)'''</span>:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo41_DisplaySprite
 
JmpTo41_DisplaySprite
 
         jmp    DisplaySprite
 
         jmp    DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
Change it to this:
 
Change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo41_DisplaySprite  
 
JmpTo41_DisplaySprite  
 
         move.w  #$180,d0        ; move $180 to OOZ boss' priority
 
         move.w  #$180,d0        ; move $180 to OOZ boss' priority
 
         jmp    DisplaySprite3  ; Dispaly it
 
         jmp    DisplaySprite3  ; Dispaly it
</asm>
+
</syntaxhighlight>
  
 
OOZ boss should no longer freeze. #$180 seems good here. Again, all boss objects have to be the same priority, and you shouldn't notice any difference.
 
OOZ boss should no longer freeze. #$180 seems good here. Again, all boss objects have to be the same priority, and you shouldn't notice any difference.
Line 663: Line 662:
  
 
Like the others, the boss uses priority for other things (you know the little flame on Eggman's ship? Priority is used for that y_pos. But luckily for us, anim_frame_duration's SST is completely free. Also, the SST after it (objoff_1F) is also free! So we can use this as a word!
 
Like the others, the boss uses priority for other things (you know the little flame on Eggman's ship? Priority is used for that y_pos. But luckily for us, anim_frame_duration's SST is completely free. Also, the SST after it (objoff_1F) is also free! So we can use this as a word!
 +
 +
===Note by Shadow05===
 +
If you've followed [[SCHG_How-to:Port_S3K_Object_Manager_into_Sonic_2|this guide]] you can use $40-41 as you've expanded the [[SST]].
  
 
Unfortunately, it seems that I couldn't make the flame use "anim_frame_duration" and I'm not sure why. So instead, we're going to make the DisplaySprite read from anim_frame_duration. Okay, let's fix this damn boss...
 
Unfortunately, it seems that I couldn't make the flame use "anim_frame_duration" and I'm not sure why. So instead, we're going to make the DisplaySprite read from anim_frame_duration. Okay, let's fix this damn boss...
  
Go to "loc_3229E:" '''("Obj54_Init:" for SVN)''' and change this:
+
Go to <span style="color:red">'''"loc_3229E:"'''</span> <span style="color:green">'''("Obj54_Init:" for SVN)'''</span> and change this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$180,priority(a0)
 
         move.w  #$180,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$180,anim_frame_duration(a0)
 
         move.w  #$180,anim_frame_duration(a0)
</asm>
+
</syntaxhighlight>
  
 
Still at this location, change:
 
Still at this location, change:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$300,priority(a1)
 
         move.w  #$300,priority(a1)
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$300,anim_frame_duration(a1)
 
         move.w  #$300,anim_frame_duration(a1)
</asm>
+
</syntaxhighlight>
  
 
'''Make sure you do NOT edit/change this line:'''
 
'''Make sure you do NOT edit/change this line:'''
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  y_pos(a0),priority(a0)
 
         move.w  y_pos(a0),priority(a0)
</asm>
+
</syntaxhighlight>
 +
 
 +
'''This is for the y_pos of the flame.  DO NOT EDIT'''
  
'''''XENOWHIRL NOTE:''''' This is for the y_pos of the flame.
 
  
Go to these locations (Xenowhirl):
+
<span style="color:red">Go to these locations '''(Xenowhirl 2007 users)''':</span>
* loc_32966:
+
* <span style="color:red">'''loc_32966:</span>
* loc_32B1A:
+
* <span style="color:red">'''loc_32B1A:</span>
* loc_32B34:
+
* <span style="color:red">'''loc_32B34:</span>
* loc_32B42:
+
* <span style="color:red">'''loc_32B42:</span>
* loc_32B56:
+
* <span style="color:red">'''loc_32B56:</span>
* loc_32CC0:
+
* <span style="color:red">'''loc_32CC0:</span>
  
'''SVN users ONLY:
+
<span style="color:green">'''SVN users ONLY:''' For first label, do a search for this line:</span>
For first label, do a search for this line:'''
+
<syntaxhighlight lang="asm">
<asm>
 
 
         move.l  objoff_34(a0),objoff_34(a1)
 
         move.l  objoff_34(a0),objoff_34(a1)
</asm>
+
</syntaxhighlight>
  
'''Second label is "Obj53_SetAnimPriority:"
+
<span style="color:green">Second label is '''"Obj53_SetAnimPriority:"'''<br></span>
Third label is directly underneath the "Obj53_SetAnimPriority:" label
+
<span style="color:green">Third label is directly underneath the "Obj53_SetAnimPriority:" label<br></span>
Fourth Label is directly underneath the previous label we were at
+
<span style="color:green">Fourth Label is directly underneath the previous label we were at<br></span>
Fifth label is directly underneath the previous label we were at
+
<span style="color:green">Fifth label is directly underneath the previous label we were at<br></span>
And sixth label is "Obj54_Laser_Init:"'''
+
<span style="color:green">And sixth label is '''"Obj54_Laser_Init:"'''</span>
  
And change the priority to anim_frame_duration. For example '''(SVN users, reference only)''':
+
And change the priority to anim_frame_duration. For example <span style="color:green">'''(SVN users, reference only)''':</span>
<asm>
+
<syntaxhighlight lang="asm">
 
loc_32B34:
 
loc_32B34:
 
         move.b  #4,mapping_frame(a0)
 
         move.b  #4,mapping_frame(a0)
 
         move.w  #$100,anim_frame_duration(a0)
 
         move.w  #$100,anim_frame_duration(a0)
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
Next, find the label "JmpTo40_DisplaySprite". Only the MTZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this '''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':
+
Next, find the label "JmpTo40_DisplaySprite". Only the MTZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this <span style="color:green">'''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)'''</span>:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo40_DisplaySprite
 
JmpTo40_DisplaySprite
 
         jmp    DisplaySprite
 
         jmp    DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
Change it to this:
 
Change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo40_DisplaySprite  
 
JmpTo40_DisplaySprite  
 
         move.w  anim_frame_duration(a0),d0
 
         move.w  anim_frame_duration(a0),d0
 
         jmp    DisplaySprite3
 
         jmp    DisplaySprite3
</asm>
+
</syntaxhighlight>
  
 
There we go! This boss is now fixed and will no longer freeze. And the priorities haven't been changed! Hooray! If only the other bosses we had to edit would play ball.
 
There we go! This boss is now fixed and will no longer freeze. And the priorities haven't been changed! Hooray! If only the other bosses we had to edit would play ball.
Line 742: Line 744:
 
The problem is, when Sonic (main character) dies, all objects freeze (well, most of them anyway). When they've frozen, they all jump to "RunObjectDisplayOnly:"
 
The problem is, when Sonic (main character) dies, all objects freeze (well, most of them anyway). When they've frozen, they all jump to "RunObjectDisplayOnly:"
  
<asm>
+
<syntaxhighlight lang="asm">
 
RunObjectDisplayOnly:
 
RunObjectDisplayOnly:
 
         moveq  #0,d0
 
         moveq  #0,d0
Line 755: Line 757:
 
         rts
 
         rts
 
; End of function RunObjectDisplayOnly
 
; End of function RunObjectDisplayOnly
</asm>
+
</syntaxhighlight>
  
 
As you can see, '''ALL''' objects will jump to DisplaySprite. This wasn't a problem before because it just took a byte from the priority and then convert it to a word, which was always even. But now, it doesn't with our new manager. It just takes the word. So, if the priority SST is odd, it will still try to display it, and then freeze.
 
As you can see, '''ALL''' objects will jump to DisplaySprite. This wasn't a problem before because it just took a byte from the priority and then convert it to a word, which was always even. But now, it doesn't with our new manager. It just takes the word. So, if the priority SST is odd, it will still try to display it, and then freeze.
Line 762: Line 764:
  
 
What we need to do, is set a new priority for all objects when Sonic is dead. So, we're going to change the branch to DisplaySprite to something else. So, go to "RunObjectDisplayOnly:" and change it to this:
 
What we need to do, is set a new priority for all objects when Sonic is dead. So, we're going to change the branch to DisplaySprite to something else. So, go to "RunObjectDisplayOnly:" and change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
RunObjectDisplayOnly:
 
RunObjectDisplayOnly:
 
         moveq  #0,d0
 
         moveq  #0,d0
Line 776: Line 778:
 
         rts
 
         rts
 
; End of function RunObjectDisplayOnly
 
; End of function RunObjectDisplayOnly
</asm>
+
</syntaxhighlight>
  
 
So now, when Sonic dies, all objects that are frozen will have their priority set to #$200. This is what most objects use anyway. I didn't actually notice any difference to when Sonic normally dies.
 
So now, when Sonic dies, all objects that are frozen will have their priority set to #$200. This is what most objects use anyway. I didn't actually notice any difference to when Sonic normally dies.
Line 784: Line 786:
 
The "Game Over" object is one of the few objects that doesn't freeze when Sonic dies, so that object will still use it's own priority (which is 0). So you don't have to worry about the "Game Over" text being behind any objects.
 
The "Game Over" object is one of the few objects that doesn't freeze when Sonic dies, so that object will still use it's own priority (which is 0). So you don't have to worry about the "Game Over" text being behind any objects.
  
''The only bad thing I noticed was if you die during the final boss, Eggman's arms would go behind the boss. If you enter and exit debug, it will be fine again though. I will try to figure a fix for this but until then, I'm sure you can survive with it.''
+
Now, A couple issues occur, because, when you die, all objects jump to the "RunObjectDisplayOnly:". As they're all using the same priority, a newer one will overlap. Take the EHZ boss. His drill gets displayed first, then his wheels (so they overlap), then the cockpit (so they overlap the rest), then finally Eggman (which overlaps all). Another example of this is during the final boss, where the final boss overlaps his arms.
 +
 
 +
To fix this, it's simple, and I should have thought of it before (Sonic 2 Recreation has different coding and doesn't suffer this). Anyway, go to "RunObjectDisplayOnly:", and change from this:
 +
 
 +
<syntaxhighlight lang="asm">
 +
RunObjectDisplayOnly:
 +
        moveq  #0,d0
 +
        move.b  (a0),d0 ; get the object's ID
 +
        beq.s  +      ; if it's obj00, skip it
 +
        tst.b  render_flags(a0)        ; should we render it?
 +
        bpl.s  +                      ; if not, skip it
 +
        move.w  #$200,d0
 +
        bsr.w  DisplaySprite3
 +
+
 +
        lea    next_object(a0),a0 ; load 0bj address
 +
        dbf    d7,RunObjectDisplayOnly
 +
        rts
 +
; End of function RunObjectDisplayOnly
 +
</syntaxhighlight>
 +
 
 +
to this:
 +
<syntaxhighlight lang="asm">
 +
RunObjectDisplayOnly:
 +
        moveq  #0,d0                  ; Clear d0 quickly
 +
        move.b  (a0),d0                ; get the object's ID
 +
        beq.s  ++                      ; if it's obj00, skip it
 +
        tst.b  render_flags(a0)        ; should we render it?
 +
        bpl.s  ++                      ; if not, skip it
 +
        move.w  priority(a0),d0        ; move object's priority to d0
 +
        btst    #6,render_flags(a0)    ; is the compound sprites flag set?
 +
        beq.s  +                      ; if not, branch
 +
        move.w  #$200,d0                ; move $200 to d0
 +
+
 +
        bsr.w  DisplaySprite3          ; Display the object with whatever is set at d0
 +
+
 +
        lea    next_object(a0),a0      ; load 0bj address
 +
        dbf    d7,RunObjectDisplayOnly
 +
        rts
 +
; End of function RunObjectDisplayOnly
 +
</syntaxhighlight>
 +
 
 +
So now, when all objects jump here, it will ask first, does it have the compound sprite flag set? If not, branch and continue, and use the object's original priority. If it IS set, it means that the current object's status table also contains information about other child sprites which need to be drawn using the current object's mappings. In other words, for our sake, it's using priority for something different. Therefore, it will '''NOT''' branch and it will move $200 to d0 instead.
  
 
==Step 11: Fix the priority for Special Stages==
 
==Step 11: Fix the priority for Special Stages==
Line 796: Line 839:
 
You may notice that it's not moving anything to a priority SST. That's because the priority for this object is 0. The problem is, it's moving numbers from SST $19(a0) onwards. Mainly at $19(a0) is #$20. Now, because we've got the new priority manager, it will grab it's priority (which is 0), but as it's now a word, it will grab that #$20. So, it will have the data #$0020. DisplaySprite cannot process that, and crashes. We need to make it grab #$0000 and not #$0020, but we can't change anything at SST $19(a0), otherwise the special stage will start to misbehave. The fix? It's quite simple.
 
You may notice that it's not moving anything to a priority SST. That's because the priority for this object is 0. The problem is, it's moving numbers from SST $19(a0) onwards. Mainly at $19(a0) is #$20. Now, because we've got the new priority manager, it will grab it's priority (which is 0), but as it's now a word, it will grab that #$20. So, it will have the data #$0020. DisplaySprite cannot process that, and crashes. We need to make it grab #$0000 and not #$0020, but we can't change anything at SST $19(a0), otherwise the special stage will start to misbehave. The fix? It's quite simple.
  
At these locations (Xenowhirl):
+
<span style="color:red">At these locations '''(Xenowhirl 2007 users)''':</span>
* loc_7536:
+
* <span style="color:red">'''loc_7536:</span>
* loc_758C:
+
* <span style="color:red">'''loc_758C:</span>
* loc_75BC:
+
* <span style="color:red">'''loc_75BC:</span>
* loc_75C8:
+
* <span style="color:red">'''loc_75C8:</span>
* loc_7648:
+
* <span style="color:red">'''loc_7648:</span>
  
'''SVN users ONLY:
+
<span style="color:green">'''SVN users ONLY:</span>
 
+
<span style="color:green">First label is '''"loc_7536:"<br></span>
First label is "loc_7536:"
+
<span style="color:green">For second label, do a search for this line:</span>
For second label, do a search for this line:'''
+
<syntaxhighlight lang="asm">
<asm>
 
 
move.b  d2,sub2_mapframe-sub2_x_pos(a1) ; sub2_mapframe
 
move.b  d2,sub2_mapframe-sub2_x_pos(a1) ; sub2_mapframe
</asm>
+
</syntaxhighlight>
  
'''Third label is directly underneath the previous label we were at
+
<span style="color:green">Third label is directly underneath the previous label we were at<br></span>
Fourth Label is directly underneath the previous label we were at
+
<span style="color:green">Fourth Label is directly underneath the previous label we were at<br></span>
And, for the fith label, do a seach for this line:'''
+
<span style="color:green">And, for the fifth label, do a search for this line:</span>
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$D8,(a1)      ; sub?_x_pos
 
         move.w  #$D8,(a1)      ; sub?_x_pos
</asm>
+
</syntaxhighlight>
  
'''and it's the + label directly underneath it'''
+
<span style="color:green">and it's the + label directly underneath it</span>
  
 
You will see the code branching (in some way) to "JmpTo_DisplaySprite". We're going to create a new label for this object to jump to instead. So, at these locations, change all:
 
You will see the code branching (in some way) to "JmpTo_DisplaySprite". We're going to create a new label for this object to jump to instead. So, at these locations, change all:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo_DisplaySprite
 
JmpTo_DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo_DisplaySpriteSpecial
 
JmpTo_DisplaySpriteSpecial
</asm>
+
</syntaxhighlight>
  
Example '''(SVN users, reference only)''':
+
Example <span style="color:green">'''(SVN users, reference only)''':</span>
<asm>
+
<syntaxhighlight lang="asm">
 
loc_7536:
 
loc_7536:
 
         move.b  d3,objoff_F(a0)
 
         move.b  d3,objoff_F(a0)
 
         bra.w  JmpTo_DisplaySpriteSpecial
 
         bra.w  JmpTo_DisplaySpriteSpecial
</asm>
+
</syntaxhighlight>
  
 
'''Make sure you only change it to the new label at these locations!'''
 
'''Make sure you only change it to the new label at these locations!'''
  
Next, go to the label itself "JmpTo_DisplaySprite" to see this '''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':
+
Next, go to the label itself "JmpTo_DisplaySprite" to see this <span style="color:green">'''(SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show)''':</span>
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo_DisplaySprite  
 
JmpTo_DisplaySprite  
 
         jmp    DisplaySprite.l
 
         jmp    DisplaySprite.l
</asm>
+
</syntaxhighlight>
  
 
Directly underneath it, add this:
 
Directly underneath it, add this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo_DisplaySpriteSpecial
 
JmpTo_DisplaySpriteSpecial
 
         move.w  #0,d0
 
         move.w  #0,d0
 
         jmp    DisplaySprite3.l
 
         jmp    DisplaySprite3.l
</asm>
+
</syntaxhighlight>
  
So you end up with this '''(SVN users, reference only)''':
+
So you end up with this <span style="color:green">'''(SVN users, reference only)''':</span>
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo_DisplaySprite  
 
JmpTo_DisplaySprite  
 
         jmp    DisplaySprite.l
 
         jmp    DisplaySprite.l
Line 860: Line 902:
 
         move.w  #0,d0
 
         move.w  #0,d0
 
         jmp    DisplaySprite3.l
 
         jmp    DisplaySprite3.l
</asm>
+
</syntaxhighlight>
  
 
Now, obj87 only, will jump here for displaying the sprite. It will force #$0000 to be displayed instead. And it doesn't affect any of the object itself. We couldn't edit the "JmpTo_DisplaySprite" label itself because other objects are jumping to that label, and we don't want to edit other object's priority.
 
Now, obj87 only, will jump here for displaying the sprite. It will force #$0000 to be displayed instead. And it doesn't affect any of the object itself. We couldn't edit the "JmpTo_DisplaySprite" label itself because other objects are jumping to that label, and we don't want to edit other object's priority.
Line 872: Line 914:
 
This object displays the "Rings to go!" text and the "Collect 40 rings" text and to make them fly apart from each other. It also displays the "Cool" and "Not enough rings" text and the hand with the blue emblem, etc. To be totally honest, this object is almost perfect, it's the "Rings to go" that's causing the freezing. The problem is pretty much the same as "Number of rings in Special Stage" object.
 
This object displays the "Rings to go!" text and the "Collect 40 rings" text and to make them fly apart from each other. It also displays the "Cool" and "Not enough rings" text and the hand with the blue emblem, etc. To be totally honest, this object is almost perfect, it's the "Rings to go" that's causing the freezing. The problem is pretty much the same as "Number of rings in Special Stage" object.
  
Go to "loc_357B2:" '''("Obj5A_FlashMessage:" for SVN)''' And you should see '''(SVN users, reference only)''':
+
Go to <span style="color:red">'''"loc_357B2:"'''</span> <span style="color:green">'''("Obj5A_FlashMessage:" for SVN)'''</span> And you should see '''<span style="color:green">(SVN users, reference only)</span>''':
<asm>
+
<syntaxhighlight lang="asm">
 
loc_357B2:
 
loc_357B2:
 
         tst.b  ($FFFFDBA0).w
 
         tst.b  ($FFFFDBA0).w
Line 883: Line 925:
 
         cmpi.b  #6,d0
 
         cmpi.b  #6,d0
 
         bcs.w  JmpTo44_DisplaySprite
 
         bcs.w  JmpTo44_DisplaySprite
</asm>
+
</syntaxhighlight>
  
That branch to "JmpTo44_DisplaySprite" is the problem. Looking at the object, this bit's priority is always 0. So, change the "JmpTo44_DisplaySprite" to "JmpTo_DisplaySpriteSpecial2", so you have this '''(SVN users, reference only)''':
+
That branch to "JmpTo44_DisplaySprite" is the problem. Looking at the object, this bit's priority is always 0. So, change the "JmpTo44_DisplaySprite" to "JmpTo_DisplaySpriteSpecial2", so you have this <span style="color:green">'''(SVN users, reference only)'''</span>:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_357B2:
 
loc_357B2:
 
         tst.b  ($FFFFDBA0).w
 
         tst.b  ($FFFFDBA0).w
Line 896: Line 938:
 
         cmpi.b  #6,d0
 
         cmpi.b  #6,d0
 
         bcs.w  JmpTo_DisplaySpriteSpecial2
 
         bcs.w  JmpTo_DisplaySpriteSpecial2
</asm>
+
</syntaxhighlight>
  
 
Yes, we're creating another new label! It's the best way to do it. Next, go to "loc_35F76:". Right '''''above''''' that label, paste this:
 
Yes, we're creating another new label! It's the best way to do it. Next, go to "loc_35F76:". Right '''''above''''' that label, paste this:
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo_DisplaySpriteSpecial2:
 
JmpTo_DisplaySpriteSpecial2:
 
         move.w  #0,d0
 
         move.w  #0,d0
 
         jmp    Displaysprite3.l
 
         jmp    Displaysprite3.l
</asm>
+
</syntaxhighlight>
  
So you end up with this '''(SVN users, reference only)''':
+
So you end up with this '''<span style="color:green">(SVN users, reference only)</span>''':
<asm>
+
<syntaxhighlight lang="asm">
 
JmpTo_DisplaySpriteSpecial2:
 
JmpTo_DisplaySpriteSpecial2:
 
         move.w  #0,d0
 
         move.w  #0,d0
Line 920: Line 962:
 
         move.w  word_35F92+4(pc,d0.w),(Normal_palette_line4+$1A).w
 
         move.w  word_35F92+4(pc,d0.w),(Normal_palette_line4+$1A).w
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
Fixed! That part of the object will be forced to use #$0000 again and to display it. The rest of the object is completely fine though, and does not need editing (plus, the rest of it isn't using 0).
 
Fixed! That part of the object will be forced to use #$0000 again and to display it. The rest of the object is completely fine though, and does not need editing (plus, the rest of it isn't using 0).
 +
 +
===Step 11.b: EXTRA STEP for SVN users===
 +
'''<span style="color:green"> ATTENTION SVN USERS!</span>''' You've got one more thing to do. Fortunately it is piss easy. Now, if you're a Xenowhirl user, you're safe and can ignore this part and move along to Step 12.
 +
 +
Seems that if you've followed this guide thus far as an SVN user, there's a glitch with the Special Stages. Sonic and Tails act like they're being hit by the bombs object over and over, making it impossible to retain any rings in the special stages. The reason? inertia is being used for something else in the special stages. This doesn't seem to be the case in Xenowhirl's disassembly.
 +
 +
So, in your SST table, you're better off switching inertia with something else. invulnerable_time seems to be the best to swap it with. So change them so you end up with this:
 +
 +
<syntaxhighlight lang="asm">
 +
invulnerable_time =    $20 ; and $21 ; time remaining until you stop blinking
 +
</syntaxhighlight>
 +
<syntaxhighlight lang="asm">
 +
inertia =              $30 ; and $31 ; directionless representation of speed... not updated in the air
 +
</syntaxhighlight>
  
 
==Step 12: Fix the CNZ pull-spring==
 
==Step 12: Fix the CNZ pull-spring==
 
This CNZ spring has lost it's width_pixels SST. Similar problem to what the priorty manager has done, it's width_pixel is being over-written by something else in the objects code.
 
This CNZ spring has lost it's width_pixels SST. Similar problem to what the priorty manager has done, it's width_pixel is being over-written by something else in the objects code.
  
If you've already applied this fix to your hack from my "How to free up 2 universal SST's" guide, you can skip this step. Otherwise, here is a link to the topic: [http://forums.sonicretro.org/index.php?showtopic=28787]
+
If you've already applied this fix to your hack from my "How to free up 2 universal SST's" guide, you can skip this step. Otherwise, here is an excerpt:
 +
 
 +
Go to "loc_2ABFA:" and you'll see this:
 +
<syntaxhighlight lang="asm">
 +
loc_2ABFA:
 +
        bsr.w  JmpTo49_Adjust2PArtPointer
 +
        move.b  #4,render_flags(a0)
 +
        bset    #6,render_flags(a0)
 +
        move.b  #1,objoff_B(a0)
 +
        tst.b  subtype(a0)
 +
        beq.s  loc_2AC54
 +
        addq.b  #2,routine(a0)
 +
        move.b  #$20,objoff_E(a0)
 +
        move.b  #$18,width_pixels(a0)
 +
        move.w  x_pos(a0),objoff_2E(a0)
 +
        move.w  y_pos(a0),objoff_34(a0)
 +
        move.w  x_pos(a0),d2
 +
        move.w  y_pos(a0),d3
 +
        addi.w  #0,d3
 +
        move.b  #1,objoff_F(a0)
 +
        lea    $10(a0),a2
 +
        move.w  d2,(a2)+
 +
        move.w  d3,(a2)+
 +
        move.w  #2,(a2)+
 +
        bra.w  loc_2AE56
 +
</syntaxhighlight>
 +
 
 +
Cut out the line "move.b #$18,width_pixels(a0)" so it's not there anymore. We're going to move it.
 +
 
 +
Next, go to "Obj85:" and you'll see this:
 +
<syntaxhighlight lang="asm">
 +
Obj85:
 +
        moveq  #0,d0
 +
        move.b  routine(a0),d0
 +
        move.w  off_2ABCE(pc,d0.w),d1
 +
        jsr    off_2ABCE(pc,d1.w)
 +
        move.w  #$200,d0
 +
        tst.w  (Two_player_mode).w
 +
        beq.s  loc_2ABA0
 +
        bra.w  JmpTo4_DisplaySprite3
 +
</syntaxhighlight>
 +
 
 +
Just before the "move.w #$200,d0", paste the width_pixel line there. So, you end up with this:
 +
<syntaxhighlight lang="asm">
 +
Obj85:
 +
        moveq  #0,d0
 +
        move.b  routine(a0),d0
 +
        move.w  off_2ABCE(pc,d0.w),d1
 +
        jsr    off_2ABCE(pc,d1.w)
 +
        move.b  #$18,width_pixels(a0)  ; Now moved here instead of being at loc_2ABFA
 +
        move.w  #$200,d0
 +
        tst.w  (Two_player_mode).w
 +
        beq.s  loc_2ABA0
 +
        bra.w  JmpTo4_DisplaySprite3
 +
</syntaxhighlight>
 +
 
 +
There. That object is now fixed.
 +
 
 +
==Step 13: Fix the priority for Tails' tails==
 +
[[File:S3KPM13(1).png|An example of the error with Tails' tails.]]<br><br>
 +
Uh oh, an observant eye would notice that Tails' tails' priority has been affected, despite the fact that Tails' priority, and his tails' priority are both $100. Now it seems that his tails are not as much as a priority anymore. Anyway, flamewing had a solution, which he uses for his hack; Sonic 2 Heroes. The fix:
 +
 
 +
First, you're going to have to use some RAM. Only a word. So, go to your list of equates. I used $FFFFF5C0 (it's free whether you use the S1 sound driver or not). Call the RAM "Tails_Tails_ptr"
 +
<syntaxhighlight lang="asm">
 +
Tails_Tails_ptr =              ramaddr( $FFFFF5C0 )
 +
</syntaxhighlight>
 +
 
 +
Our new RAM is ready for use. First, go to "InitPlayers:" and under the line:
 +
<syntaxhighlight lang="asm">
 +
        move.b  #2,(Sidekick).w ; load Obj02 Tails object at $FFFFB040
 +
</syntaxhighlight>
 +
 
 +
{{green|'''SVN users''', yours will say:}}
 +
<syntaxhighlight lang="asm">
 +
        move.b  #ObjID_Tails,(Sidekick+id).w ; load Obj02 Tails object at $FFFFB040
 +
</syntaxhighlight>
 +
 
 +
Insert this:
 +
<syntaxhighlight lang="asm">
 +
        move.w  #Tails_Tails,(Tails_Tails_ptr).w
 +
</syntaxhighlight>
 +
 
 +
So you have something like this ({{green|'''SVN users''', reference only}}):
 +
<syntaxhighlight lang="asm">
 +
        move.b  #2,(Sidekick).w ; load Obj02 Tails object at $FFFFB040
 +
        move.w  #Tails_Tails,(Tails_Tails_ptr).w
 +
        move.w  (MainCharacter+x_pos).w,(Sidekick+x_pos).w
 +
        move.w  (MainCharacter+y_pos).w,(Sidekick+y_pos).w
 +
        subi.w  #$20,(Sidekick+x_pos).w
 +
        addi.w  #4,(Sidekick+y_pos).w
 +
        move.b  #8,(Tails_Dust).w ; load Obj08 Tails' spindash dust/splash object at $FFFFD140
 +
</syntaxhighlight>
 +
 
 +
Do the same at label "InitPlayers_TailsAlone:", so you have something like this ({{green|'''SVN users''', reference only}}):
 +
<syntaxhighlight lang="asm">
 +
InitPlayers_TailsAlone:
 +
        move.b  #2,(MainCharacter).w ; load Obj02 Tails object at $FFFFB000
 +
        move.w  #Tails_Tails,(Tails_Tails_ptr).w
 +
        move.b  #8,(Tails_Dust).w ; load Obj08 Tails' spindash dust/splash object at $FFFFD100
 +
        addi.w  #4,(MainCharacter+y_pos).w
 +
        rts
 +
; End of function InitPlayers
 +
</syntaxhighlight>
 +
 
 +
Next, go to "loc_A2F2:". This is for when you're at the cutscene at the end of the game.
 +
 
 +
{{red|'''XenoWhirl users''', Change this:}}
 +
<syntaxhighlight lang="asm">
 +
loc_A2F2:
 +
        moveq  #$E,d0
 +
        move.b  #2,(a1) ; load Tails object
 +
        move.b  #$81,$2A(a1)
 +
        move.b  #5,(Object_RAM+$80).w ; load Obj05 (Tails' tails) at $FFFFB080
 +
        move.w  a1,(Object_RAM+$80+parent).w
 +
        rts
 +
</syntaxhighlight>
 +
{{red|to this:}}
 +
<syntaxhighlight lang="asm">
 +
loc_A2F2:
 +
        moveq  #$E,d0
 +
        move.b  #2,(a1) ; load Tails object
 +
        move.b  #$81,$2A(a1)
 +
        move.w  #Object_RAM+$80,(Tails_Tails_ptr).w
 +
        rts
 +
</syntaxhighlight>
 +
 
 +
{{green|'''SVN users''', change this:}}
 +
<syntaxhighlight lang="asm">
 +
loc_A2F2:
 +
        moveq  #$E,d0
 +
        move.b  #ObjID_Tails,id(a1) ; load Tails object
 +
        move.b  #$81,obj_control(a1)
 +
        move.b  #ObjID_TailsTails,(Tails_Tails_Cutscene+id).w ; load Obj05 (Tails' tails) at $FFFFB080
 +
        move.w  a1,(Tails_Tails_Cutscene+parent).w
 +
        rts
 +
</syntaxhighlight>
 +
 
 +
{{green|to this:}}
 +
<syntaxhighlight lang="asm">
 +
loc_A2F2:
 +
        moveq  #$E,d0
 +
        move.b  #ObjID_Tails,id(a1) ; load Tails object
 +
        move.b  #$81,obj_control(a1)
 +
        move.w  #Tails_Tails_Cutscene,(Tails_Tails_ptr).w ; Tails' tails at $FFFFB080
 +
        rts
 +
</syntaxhighlight>
 +
 
 +
Next, go to "Obj02_Init_Continued:", and change the last few lines.
 +
 
 +
{{red|'''Xenowhirl users''', change this:}}
 +
<syntaxhighlight lang="asm">
 +
        move.b  #5,(Tails_Tails).w ; load Obj05 (Tails' Tails) at $FFFFD000
 +
        move.w  a0,(Tails_Tails+parent).w ; set its parent object to this
 +
</syntaxhighlight>
 +
 
 +
{{red|And change it to this:}}
 +
<syntaxhighlight lang="asm">
 +
        movea.w (Tails_Tails_ptr).w,a1
 +
        move.b  #5,0(a1) ; load Obj05 (Tails' Tails) at $FFFFD000
 +
        move.w  a0,parent(a1) ; set its parent object to this
 +
</syntaxhighlight>
 +
 
 +
{{green|'''SVN users''', yours will say:}}
 +
<syntaxhighlight lang="asm">
 +
        move.b  #ObjID_TailsTails,(Tails_Tails+id).w ; load Obj05 (Tails' Tails) at $FFFFD000
 +
        move.w  a0,(Tails_Tails+parent).w ; set its parent object to this
 +
</syntaxhighlight>
 +
 
 +
{{green|Change it to this:}}
 +
<syntaxhighlight lang="asm">
 +
        movea.w  (Tails_Tails_ptr).w,a1
 +
        move.b  #ObjID_TailsTails,id(a1) ; load Obj05 (Tails' Tails)
 +
        move.w  a0,parent(a1) ; set its parent object to this
 +
</syntaxhighlight>
 +
 
 +
One more step! Go to "Obj02:" See the command {{red|"jmp Obj02_States(pc,d1.w)"}}/{{green|"jmp Obj02_Index(pc,d1.w)"}}? Change the "jmp" to a "jsr". And directly underneath it, add this:
 +
<syntaxhighlight lang="asm">
 +
        movea.w (Tails_Tails_ptr).w,a1
 +
        tst.b  routine(a1)
 +
        beq.s  +
 +
        jmp    (DisplaySprite2).l
 +
+
 +
        rts
 +
</syntaxhighlight>
 +
 
 +
So you have something like this ({{green|'''SVN users''', reference only}}):
 +
<syntaxhighlight lang="asm">
 +
Obj02:
 +
        ; a0=character
 +
        cmpi.w  #2,(Player_mode).w
 +
        bne.s  +
 +
        move.w  (Camera_Min_X_pos).w,(Tails_Min_X_pos).w
 +
        move.w  (Camera_Max_X_pos).w,(Tails_Max_X_pos).w
 +
        move.w  (Camera_Max_Y_pos_now).w,(Tails_Max_Y_pos).w
 +
+
 +
        moveq  #0,d0
 +
        move.b  routine(a0),d0
 +
        move.w  Obj02_States(pc,d0.w),d1
 +
        jsr    Obj02_States(pc,d1.w)
 +
        movea.w (Tails_Tails_ptr).w,a1
 +
        tst.b  routine(a1)
 +
        beq.s  +
 +
        jmp    (DisplaySprite2).l
 +
+
 +
        rts
 +
</syntaxhighlight>
  
I can put the excerpt for this up later... but for now, just look through the topic. Follow this, and the object will work as it should.
+
Done! Tails' tails should now work with the right priority! Tails will now queue his tails for drawing right after himself. Without this, the tails would be queued for drawing after all objects between Tails and the tails that have the same priority. And that's why before, Tails' tails had less priority.
  
==Step 13: Improve ObjectMove and ObjectMoveandFall (optional)==
+
==Step 14: Fix Eggman Death Egg Robot Priority==
This optional step will be included later. I currently don't have time at the moment to add it. I'll do it as soon as possible.
+
One more fix! When Eggman jumps into the Death Egg robot boss he shows up in front of the robot. This isn't a big deal but we might as well fix it.
  
After this, you should be all finished. I haven't fully put the guide on the wiki yet, though its only missing the optional bit...
+
Go to "loc_3CFC0" and simply set Eggman's priority higher than his robot, $300 works here.
Feel free to check out the topic if you haven't, or to check out the download link to try a ROM with this change applied: [http://forums.sonicretro.org/index.php?showtopic=29165]
+
<syntaxhighlight lang="asm">
 +
loc_3CFC0:
 +
move.b #2,mapping_frame(a0)
 +
clr.w x_vel(a0)
 +
tst.b render_flags(a0)
 +
bpl.s +
 +
addq.b #2,routine_secondary(a0) ; => ObjC6_State2_State5
 +
move.w #$300,priority(a0) ; Set Eggman's priority to fix an issue with the Death Egg Robot
 +
move.w #$80,x_vel(a0)
 +
move.w #-$200,y_vel(a0)
 +
move.b #2,mapping_frame(a0)
 +
move.w #$50,objoff_2A(a0)
 +
bset #3,status(a0)
 +
</syntaxhighlight>
 +
==Step 15: Done==
 +
That's it guys! All done! You have now successfully ported Sonic 3 and Knuckles' Priority Manager into Sonic 2. Your hack should now be experiencing less lag! Please be aware though, this will not eliminate all lag from your hack. It just helps to get rid of some of it. To get rid of more lag, you need to do more; like Object Managers and etc.
  
 
{{S2Howtos}}
 
{{S2Howtos}}
[[Category:SCHG How-tos|{{PAGENAME}}]]
 

Latest revision as of 22:28, 27 March 2023

(Guide written by redhotsonic)

(Additional fix by Pokepunch)

Preface

WARNING: Please back-up your disassembly before attempting to use this guide. I will not be held responsible for anything that goes wrong. Alternatively, use a version control system such as Mercurial (used for the Sonic Retro repositories) or git, which will not only allow you to back up your repository before making this change but at every step and in all future steps of whatever changes you make, making finding errors easier.

This guide is mainly following Xenowhirl's 2007 disassembly. But, if you're an SVN user, I have tried to make this guide as friendly as possible for you. Anything marked as such is only for SVN or Xenowhirl disassemblies. PAY ATTENTION!

Anything in red is for Xenowhirl's 2007 disassembly users only.
Anything in green is for SVN disassembly users only.

Also, EVERY SST MUST BE EQUATED! So, if you're using an old disassembly, you won't be able to follow this. Also, for any objects you've made yourself or have ported, make sure that it's all equated (so instead of $18(a0), it should be priority(a0).) Anything that has not been equated may start to cause problems; you have been warned!

What is "Priority"?

In all Sonic games, there is a universal SST called Priority. Each sprite has one. The higher the priority number, the lower priority it has. Then the objects can be displayed accordingly. For example, if Sonic has a priority of 2, and Dr. Eggman's priority is 3, then when both sprites are displayed over each other, Sonic will ALWAYS be displayed in front of him. It is the equivalent of what other programming languages (Such as Game Maker) would call "depth".

Differences between S2 & S3 Managers

In Sonic 2, the priority's universal SST is a byte. It's start's from #0 (highest) up to #7 (lowest). When each object jumps to the "DisplaySprite" subroutine, it firsts, converts the priority byte into a word. And then it can check whether to display the sprite, and if so, display it. It has to do this every frame for every object for the sprite to be displayed.

In Sonic 3 and Knuckles, priority is already a word, again, it's start's from #0 (highest) but goes upto #$380 (lowest). That doesn't mean there's $380 types of priority. It goes up in $80's (#0, $80, $100, $180, etc). Because it's already set as a word, when it jumps to the "DisplaySprite" subroutine, it doesn't have to do them calculations, and can just check whether to display the sprite or not and if so, display it.

So basically, S3K's "DisplaySprite" has shaven a few commands away. Because S3K does not have to do them calculations changing from a byte to a word like S2 does on every single frame, it can save a lot of time, and can use the processor on other things instead. When you see a lot of sprites on screen, this can make a huge difference to gameplay.

Porting S3K's Priority Manager into S2 can help get rid of some of the lag your hack may be experiencing. If you follow this guide step-by-step carefully, and make sure you have some time on your hands because it can take a little while, everything should go fine.

Please be aware, once you've started this, you cannot rebuild your ROM until you've finished. Otherwise, you'll get errors and crashes.

Step 1: Getting a free Universal SST

The biggest problem is freeing a universal SST, seeming as they are all being used. Luckily, quite a while back, I showed a guide on how to free two universal SST's! [1] If you have already done part 2 of this guide, you do not need to carry on with this step. Part 2 is the essential SST we need for this guide. Part 1 isn't as important.

If you haven't done this already, you will need to do so. Below is the second step from the SST guide. Follow this step before continuing. First, go to Sonic's SST's (This will be in s2constants.asm for SVN users) and you'll see

inertia =               $14 ; and $15 ; directionless representation of speed... not updated in the air

Change it to

inertia =               $20 ; and $21 ; directionless representation of speed... not updated in the air

For Sonic and Tails, $20 and $21 is free, so move it there. Why? Simple. $14 is a "convention followed by most objects". I think $15 is still only used by Sonic and Tails, but that doesn't matter. Now, instead of the original $1F, $20 and $21 being free for Sonic and Tails, it's now $15 and $1F that is free for them ($14 is NOT free for Sonic and Tails! About to explain why!)

Next, go to:

width_pixels =          $19

and change to

width_pixels =          $14

width_pixels can be moved to $14. Which means, $19 is now free, and it's universal! How about that? Told you it was piss easy! I am using $19 as part of priority (in S3K's priority manager, the priority is a word, so I use $18 and $19). And that's the reason why $14 isn't free for Sonic and Tails, because it's being used for width_pixel. Yes, you're going to lose a SST from them but it's better to gain a universal SST, right?

So now, you have $19 free universally, therefore priority can now be used as a word ($18 and $19)!

Step 2: Setting up DisplaySprite and DisplaySprite2

You should now have width_pixels at $14 instead of $19. Because priority is at $18, and $19 is now free, theoretically, the priority SST can now be used as a word.

Next, we need to change DisplaySprite and DisplaySprite2. Do NOT edit/change or replace DisplaySprite3! Leave that as it is! At "DisplaySprite:", you'll see this:

        move.w  priority(a0),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        adda.w  d0,a1

These are the lines that does the calculations every single frame. This is what converts the byte into a word. Like said in S3K, as priority is already a word, these lines are now useless. As this is what we're trying to achieve, we can do the same thing.

So, at "DisplaySprite:", change this:

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_164F4:
DisplaySprite:
        lea     (Sprite_Table_Input).w,a1
        move.w  priority(a0),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        adda.w  d0,a1
        cmpi.w  #$7E,(a1)
        bcc.s   return_16510
        addq.w  #2,(a1)
        adda.w  (a1),a1
        move.w  a0,(a1)

return_16510:
        rts
; End of function DisplaySprite

; ---------------------------------------------------------------------------
; Subroutine to display a sprite/object, when a1 is the object RAM
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_16512:
DisplaySprite2:
        lea     (Sprite_Table_Input).w,a2
        move.w  priority(a1),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        adda.w  d0,a2
        cmpi.w  #$7E,(a2)
        bcc.s   return_1652E
        addq.w  #2,(a2)
        adda.w  (a2),a2
        move.w  a1,(a2)

return_1652E:
        rts
; End of function DisplaySprite2

To this

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_164F4:
DisplaySprite:
        lea     (Sprite_Table_Input).w,a1
        adda.w  priority(a0),a1
        cmpi.w  #$7E,(a1)
        bcc.s   return_16510
        addq.w  #2,(a1)
        adda.w  (a1),a1
        move.w  a0,(a1)

return_16510:
        rts
; End of function DisplaySprite

; ---------------------------------------------------------------------------
; Subroutine to display a sprite/object, when a1 is the object RAM
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_16512:
DisplaySprite2:
        lea     (Sprite_Table_Input).w,a2
        adda.w  priority(a1),a2
        cmpi.w  #$7E,(a2)
        bcc.s   return_1652E
        addq.w  #2,(a2)
        adda.w  (a2),a2
        move.w  a1,(a2)

return_1652E:
        rts
; End of function DisplaySprite2

Both of these subroutines are now shorter. May not look by much, but remember, as this is getting repeated all the time for EACH object for each frame, this will make a lot of difference.

DisplaySprite3 does not need changing. This is because it already does a similar thing. Some objects move a priority word to d0, then DisplaySprite3 uses that d0. As it's already a word, nothing needs changing.

Step 3: Set objects that uses tables for priority to the new manager

Now, a lot of objects just move a byte to the object's priority. But some objects use a table for priority, width, mappings, etc.

Now, editing the table can cause problems, it can start making the objects code out of line, or even the rest of the game, which will cause the game to randomly crash at certain times. But it has to be a word, it cannot stay as a byte.

So, the best thing I found it to actually use them calculations from Sonic 2's DisplaySprite to convert them into a word. So, when creating the object, it will have the priority as a byte, and then do Sonic 2's calculations to convert it into a word. Priority will then remain as a word for the rest of the time that object is there for. Because of this, it will only need to do this calculation once, then carry on the S3K way. Doing it once then the S3K way, is better then doing them calculations all the time, right?

So, first, go to "loc_112A4:" ("Obj1C_Init:" on SVN) This is "Object 1C - Bridge stake in Emerald Hill Zone and Hill Top Zone, falling oil in Oil Ocean Zone" object. You can see it's moving a lot of data to mappings and etc. It does this because there's so many subtypes to the object, and this is the best and quickest way for that object to load.

So, underneath "move.b (a1)+,priority(a0)", add this:

        move.w  priority(a0),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        move.w  d0,priority(a0)

So you have something like this (SVN user, your code will look slightly different, this is only a reference):

loc_112A4:
        addq.b  #2,routine(a0)
        moveq   #0,d0
        move.b  subtype(a0),d0
        move.w  d0,d1
        lsl.w   #3,d0
        lea     dword_111E6(pc),a1
        lea     (a1,d0.w),a1
        move.b  (a1),mapping_frame(a0)
        move.l  (a1)+,mappings(a0)
        move.w  (a1)+,art_tile(a0)
        bsr.w   Adjust2PArtPointer
        ori.b   #4,render_flags(a0)
        move.b  (a1)+,width_pixels(a0)
        move.b  (a1)+,priority(a0)
        move.w  priority(a0),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        move.w  d0,priority(a0)
        lea     byte_1128E(pc),a1
        move.b  (a1,d1.w),d1
        beq.s   BranchTo_MarkObjGone
        move.b  d1,y_radius(a0)
        bset    #4,render_flags(a0)

Whatever data is grabbed from the table, it's now converted it to a word, and it will never do it again. It will always remain as a word. So, when it goes to DisplaySprite over and over, it can just get on with it.

Next, go to "loc_1131A:" ("Obj71_Init:" for SVN) (Object 71 - Bridge stake and pulsing orb from Hidden Palace Zone) and do the same thing, so you end up with this (SVN users, reference only):

loc_1131A:
        addq.b  #2,routine(a0)
        move.b  subtype(a0),d0
        andi.w  #$F,d0
        lsl.w   #3,d0
        lea     dword_11302(pc),a1
        lea     (a1,d0.w),a1
        move.b  (a1),mapping_frame(a0)
        move.l  (a1)+,mappings(a0)
        move.w  (a1)+,art_tile(a0)
        bsr.w   Adjust2PArtPointer
        ori.b   #4,render_flags(a0)
        move.b  (a1)+,width_pixels(a0)
        move.b  (a1)+,priority(a0)
        move.w  priority(a0),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        move.w  d0,priority(a0)
        move.b  subtype(a0),d0
        andi.w  #$F0,d0
        lsr.b   #4,d0
        move.b  d0,anim(a0)

Go to "loc_3F228:" (Object 3E - Egg prison) and almost do the same thing again. Just look at the registers though, as they are different (it's a1 instead of a0). You should end up with this (SVN users, reference only):

loc_3F228:
        _move.b 0(a0),0(a1) ; load obj
        move.w  x_pos(a0),x_pos(a1)
        move.w  y_pos(a0),y_pos(a1)
        move.w  y_pos(a0),objoff_30(a1)
        move.l  #Obj3E_MapUnc_3F436,mappings(a1)
        move.w  #$2680,art_tile(a1)
        move.b  #$84,render_flags(a1)
        moveq   #0,d0
        move.b  (a2)+,d0
        sub.w   d0,y_pos(a1)
        move.w  y_pos(a1),objoff_30(a1)
        move.b  (a2)+,routine(a1)
        move.b  (a2)+,width_pixels(a1)
        move.b  (a2)+,priority(a1)
        move.w  priority(a1),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        move.w  d0,priority(a1)
        move.b  (a2)+,mapping_frame(a1)

And finally, go to "LoadSubObject_Part3:". Now this loads all the priorities, widths, mappings, etc, to all objects past Obj8C. This is a life saver. So this will save us a lot of time from doing it to a lot of other objects.

So, change this:

LoadSubObject_Part3:
        move.l  (a1)+,mappings(a0)
        move.w  (a1)+,art_tile(a0)
        jsr     Adjust2PArtPointer
        move.b  (a1)+,d0
        or.b    d0,render_flags(a0)
        move.b  (a1)+,priority(a0)
        move.b  (a1)+,width_pixels(a0)
        move.b  (a1),collision_flags(a0)
        addq.b  #2,routine(a0)
        rts

to this:

LoadSubObject_Part3:
        move.l  (a1)+,mappings(a0)
        move.w  (a1)+,art_tile(a0)
        jsr     Adjust2PArtPointer
        move.b  (a1)+,d0
        or.b    d0,render_flags(a0)
        move.b  (a1)+,priority(a0)
        move.w  priority(a0),d0
        lsr.w   #1,d0
        andi.w  #$380,d0
        move.w  d0,priority(a0)
        move.b  (a1)+,width_pixels(a0)
        move.b  (a1),collision_flags(a0)
        addq.b  #2,routine(a0)
        rts

That's half the objects done already!

Step 4: Set the rest of the objects to the new manager

Now, this step is easy, but time consuming. You will be a while doing this. You need to search through the whole of your asm file, and change all priority's to a word. Here is an example.

At "Obj5E:", you'll see this:

        move.b  #0,priority(a0)

Change it to this:

        move.w  #0,priority(a0)

The move command has now changed from .b to .w as priority is now a word. The number will also need changing, but #0 in S2 is the same as S3K. So 0 doesn't need changing here.

Another example. Go to "loc_7158:" ("Obj5F_Init:" for SVN) and change this:

        move.b  #1,priority(a0)

to this:

        move.w  #$80,priority(a0)

Again, the .b has change to .w (this must be done ALL the time, even if the number is 0). This time, the number is 1. After calculations (or in S3K), the number will be $80. So it's been changed to $80. Remember, in S2, the numbers were only #. With the S3K way (unless 0), it will need to be #$.

You need to find all these and change all .b to .w and change the numbers to the relavant number it would have become after the original calculations.

To help, I've made this table for reference:


===============================
TABLE CONVERSION FROM S2 TO S3K
===============================
| | S 2 | S3K |
|---|-----|-----|
| ~ | # | #$ |
| P | 0 | 0 |
| R | 1 | 80 |
| I | 2 | 100 |
| O | 3 | 180 |
| R | 4 | 200 |
| I | 5 | 280 |
| T | 6 | 300 |
| Y | 7 | 380 |
===============================


Want one more example? Okay. Go to "ObjDB_Sonic_Init:" and change this:

        move.b  #2,priority(a0)

to this:

        move.w  #$100,priority(a0)

It's that simple. Now go! Convert all them priorities!

HINT: To make this step miles quicker, you can do a search and replace. Just be careful when doing this. Make sure you do it right. Remember that not all priorities use a0. Some use a1, or a2. You could do something similar to this:

Search:

move.b  #2,priority(a

Replace with:

move.w  #$100,priority(a

...and etc. That way, it will replace them all, no matter what register it has. Do the same from #0 - #7.

Be warned though! If you've edited the tabs (spaces) on any coding, or on your own made objects, the "Search and Replace" may not be the best way to go, and you may have to do this manually. If you do do the "search and replace" idea, I'd highly reccommend you still search through the ASM file after to check you've done it right.

Step 5: Make sure the copiers are copying correctly

At these locations (Xenowhirl 2007 users):

  • loc_10B9E:
  • loc_15E46:
  • Obj79_MakeSpecialStars:
  • loc_25C24:
  • loc_28A6E:

SVN users ONLY: For first label, do a search for this line:

        _move.b d4,id(a1) ; load obj1F

Second label is "BreakObjectToPieces_InitObject:"
Third label is "Obj79_MakeSpecialStars:"
Fourth Label is "loc_25C24:"
And fifth label is "Obj73_LoadSubObject:"

Under each of these labels, you should see this line:

        move.b  priority(a0),priority(a1)

Some objects like these, move the priority to a1 from a0. As you can see, it's only moving a byte. Change the line at all these locations to this:

        move.w  priority(a0),priority(a1)

All the registers at these locations are the same, so the above line is fine to use.

Step 6: Make sure some objects are calculating correctly

Similar to step 5, some objects copy the priority to another address register or data register, then does a small subtraction, then moves it back. As it's going by S2's priority, this needs changing also.

Go to "loc_34864:" (Tails in Special Stage) and change this bit only:

        move.b  priority(a0),priority(a1)
        subi.b  #1,priority(a1)

to this:

        move.w  priority(a0),priority(a1)
        subi.w  #$80,priority(a1)

So you have this (SVN users, reference only):

loc_34864:
        move.w  #$400,objoff_32(a0)
        move.b  #$40,angle(a0)
        move.b  #1,($FFFFF7DE).w
        clr.b   collision_property(a0)
        clr.b   respawn_index(a0)
        bsr.w   loc_349C8
        movea.l #Object_RAM+$180,a1
        move.b  #$63,(a1) ; load obj63 (shadow) at $FFFFB180
        move.w  x_pos(a0),x_pos(a1)
        move.w  y_pos(a0),y_pos(a1)
        addi.w  #$18,y_pos(a1)
        move.l  #Obj63_MapUnc_34492,mappings(a1)
        move.w  #$623C,art_tile(a1)
        move.b  #4,render_flags(a1)
        move.w  #$200,priority(a1)
        move.l  a0,objoff_38(a1)
        movea.l #Object_RAM+$1C0,a1
        move.b  #$88,(a1) ; load obj88
        move.w  x_pos(a0),x_pos(a1)
        move.w  y_pos(a0),y_pos(a1)
        move.l  #Obj88_MapUnc_34DA8,mappings(a1)
        move.w  #$4316,art_tile(a1)
        move.b  #4,render_flags(a1)
        move.w  priority(a0),priority(a1)
        subi.w  #$80,priority(a1)
        move.l  a0,objoff_38(a1)
        movea.l a1,a0
        move.b  #1,($FFFFF7DF).w
        clr.b   respawn_index(a0)
        movea.l objoff_38(a0),a0 ; load 0bj address
        rts

Now, it's moving a WORD to a1, then subtracting $80 instead of 1. So now it's going how S3K would do it.

Similar story here. Go to "Obj88:" (Tails' tails in Special Stage) and change this bit only:

        move.b  priority(a1),d0
        subq.b  #1,d0
        move.b  d0,priority(a0)

to this:

        move.w  priority(a1),d0
        subi.w  #$80,d0
        move.w  d0,priority(a0)

One more. Go to "loc_3C1F4:" (Breakable plating from WFZ) and change this bit only:

        move.b  priority(a0),d4
        subq.b  #1,d4

to this:

        move.w  priority(a0),d4
        subi.w  #$80,d4

There's a little more to this object. Next, go to "loc_3C20E:" and change this bit only:

        move.b  d4,priority(a1)

to this:

        move.w  d4,priority(a1)

These objects will now display correctly.

Step 7: Make sure EHZ's boss uses the right compares

At these locations:

  • loc_2F714:
  • loc_2F77E:
  • loc_2F7A6:

The EHZ boss is showing these commands:

        cmpi.b  #2,priority(a0)

Change them to this:

        cmpi.w  #$100,priority(a0)

They were still treating priority as a byte, so we just needed to change these to a word.

Step 8: Fix ARZ, CNZ, MCZ and OOZ's boss's priority

These are a pain in the ass to fix! These bosses use the priority and (the old) width_pixel's SST's together for a complete different reason (The hammer, drills and catchers). It's moves odd numbers to the priority SST (well, it's actually positions, but the number is odd to DisplaySprite) and because of this, it cannot display the word any longer and therefore, the game freezes pretty much instantly when you approach one of these bosses.

Luckily, there are many objects that do a similar thing; using priority for a complete different reason. This is where "DisplaySprite3" comes into play. Objects that use priority SST for other reasons, instead, moves a word to d0, then jumps to DisplaySprite3. So, we're going to do something similar for ARZ, CNZ, MCZ and OOZ's bosses.

ARZ boss

Go to "loc_304D4:" and delete the line:

        move.w  #$100,priority(a0)

This is no longer needed, as it's going to be replaced with other things later in the ARZ boss code anyway.

Still at this label, you should also see these commands:

        move.w  #$200,priority(a1)
        move.w  #$100,priority(a1)

You might as well get rid of them too.

Make sure you do NOT delete this line:

        move.w  #$488,priority(a0)

This is for the y_pos of the hammer, one of the reason why the priority needs changing of the boss.

Next, go to "loc_30BC8:" and find and delete the line:

        move.w  #$200,priority(a0)

Again, no point in this!

Next, find the label "JmpTo37_DisplaySprite". Only the ARZ boss jumps to this location for displaying a sprite, so we know we can freely edit this for our ARZ boss without affecting anything else. Anyway, you should see this (SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show):

JmpTo37_DisplaySprite 
        jmp     DisplaySprite

Now, because them odd numbers are set as priority, when it jumps to DisplaySprite, it will try to process it and freeze. So, this is what we're going to do. Change it to this:

JmpTo37_DisplaySprite 
        move.w  #$200,d0        ; move $200 to ARZ boss' priority
        jmp     DisplaySprite3  ; Dispaly it

Now, everytime the boss needs displaying, instead of grabbing the data from the priority SST, it will process what we've just moved to d0. #$200 seems good, I use that.

I would have tried changing these priority commands to a different universal SST, but the ARZ boss unfortunately uses them all, so there's nothing free. So, the priority for Eggman, arrows, hammer and totem poles now have to be the same priority, but really, you won't notice any different to the way the boss displays it's sprites.

Viola! ARZ boss is sorted and will no longer freeze.

CNZ boss

The problem with this boss is the exact same problem as ARZ. Priority is being used for something else. Same sort of fix applies here.

Go to "loc_31904:" and delete the line:

        move.w  #$180,priority(a0)

Also, go to "loc_31F48:" and delete the line:

        move.w  #$380,priority(a0)

Next, find the label "JmpTo39_DisplaySprite". Only the CNZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this (SVN user, yours will look ever so slightly different, but you can still copy and paste my fix that I'm about to show):

JmpTo39_DisplaySprite 
        jmp     DisplaySprite

Change it to this:

JmpTo39_DisplaySprite 
        move.w  #$180,d0        ; move $180 to CNZ boss' priority
        jmp     DisplaySprite3  ; Dispaly it

CNZ boss should no longer freeze. #$180 seems good here. Again, CNZ boss shares the same problem as ARZ boss, so Eggman, the catchers and spikeball have to be the same priority, and you shouldn't notice any difference.

MCZ boss

Same issue. Priority is being used for something else. Same sort of fix applies here. But luckily, all the priorities are #$180, so at least the priority won't change after this edit unlike the previous two bosses we've edited.

Go to "loc_30FB8:" and delete the line:

        move.w  #$180,priority(a0)

Also, go to "loc_313DA:" ("Obj57_LoadStoneSpike:" for SVN) and delete the same(ish) line (I say 'ish' because it's using a1 instead of a0):

        move.w  #$180,priority(a1)

Next, find the label "JmpTo38_DisplaySprite". Only the MCZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this (SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show):

JmpTo38_DisplaySprite 
        jmp     DisplaySprite

Change it to this:

JmpTo38_DisplaySprite 
        move.w  #$180,d0        ; move $180 to MCZ boss' priority
        jmp     DisplaySprite3  ; Dispaly it

Seeming as both priority lines we just deleted was #$180, we know that we can use #$180 here. MCZ boss should no longer freeze.

OOZ boss

This boss uses Priority as a x_pos for the sub objects. Again, we're going to have to do a similar thing like we did with the other bosses.

Go to "loc_32FA8:" ("Obj55_Init:" for SVN) and delete the line:

        move.w  #$180,priority(a0)

Also, go to "loc_33586:" ("Obj55_Laser_Init:" for SVN) and delete the line:

        move.w  #$200,priority(a0)

Also, go to "loc_33640:" ("Obj55_Laser_CreateWave:" for SVN) and delete the line:

        move.w  #$100,priority(a1)

Next, find the label "JmpTo41_DisplaySprite". Only the OOZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this (SVN user, yours will look ever so slightly different, but you can still copy and paste my fix that I'm about to show):

JmpTo41_DisplaySprite
        jmp     DisplaySprite

Change it to this:

JmpTo41_DisplaySprite 
        move.w  #$180,d0        ; move $180 to OOZ boss' priority
        jmp     DisplaySprite3  ; Dispaly it

OOZ boss should no longer freeze. #$180 seems good here. Again, all boss objects have to be the same priority, and you shouldn't notice any difference.

Notice: I have compared the priorities to the original Sonic 2's priorities for these bosses with our new "set" priorities (that we've moved to d0) and I saw no difference whatsover. So all the above is fine. For example, with Sonic 2's original priorities set, Sonic was always in front of the ARZ boss, hammer, totem poles and arrows. He was never behind them. The way we have set up the bosses above, this is still the case. That's why you shouldn't notice any difference.

Step 9: Fix MTZ's boss's priority

You're probably thinking, "What? This boss gets it's very own step?" Yup, and the reason why, is because there is a free universal SST! Hoozah! That means we can fix this boss without making everything use the same priority!

Like the others, the boss uses priority for other things (you know the little flame on Eggman's ship? Priority is used for that y_pos. But luckily for us, anim_frame_duration's SST is completely free. Also, the SST after it (objoff_1F) is also free! So we can use this as a word!

Note by Shadow05

If you've followed this guide you can use $40-41 as you've expanded the SST.

Unfortunately, it seems that I couldn't make the flame use "anim_frame_duration" and I'm not sure why. So instead, we're going to make the DisplaySprite read from anim_frame_duration. Okay, let's fix this damn boss...

Go to "loc_3229E:" ("Obj54_Init:" for SVN) and change this:

        move.w  #$180,priority(a0)

to this:

        move.w  #$180,anim_frame_duration(a0)

Still at this location, change:

        move.w  #$300,priority(a1)

to this:

        move.w  #$300,anim_frame_duration(a1)

Make sure you do NOT edit/change this line:

        move.w  y_pos(a0),priority(a0)

This is for the y_pos of the flame. DO NOT EDIT


Go to these locations (Xenowhirl 2007 users):

  • loc_32966:
  • loc_32B1A:
  • loc_32B34:
  • loc_32B42:
  • loc_32B56:
  • loc_32CC0:

SVN users ONLY: For first label, do a search for this line:

        move.l  objoff_34(a0),objoff_34(a1)

Second label is "Obj53_SetAnimPriority:"
Third label is directly underneath the "Obj53_SetAnimPriority:" label
Fourth Label is directly underneath the previous label we were at
Fifth label is directly underneath the previous label we were at
And sixth label is "Obj54_Laser_Init:"

And change the priority to anim_frame_duration. For example (SVN users, reference only):

loc_32B34:
        move.b  #4,mapping_frame(a0)
        move.w  #$100,anim_frame_duration(a0)
        rts

Next, find the label "JmpTo40_DisplaySprite". Only the MTZ boss jumps to this location for displaying a sprite, so again, we know it's safe to edit. You should see this (SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show):

JmpTo40_DisplaySprite
        jmp     DisplaySprite

Change it to this:

JmpTo40_DisplaySprite 
        move.w  anim_frame_duration(a0),d0
        jmp     DisplaySprite3

There we go! This boss is now fixed and will no longer freeze. And the priorities haven't been changed! Hooray! If only the other bosses we had to edit would play ball.

Step 10: Fix the priority for when the main character is dead

As previously stated, some objects use the priority SST for other purposes. That's why a lot of them use DisplaySprite3 instead.

The problem is, when Sonic (main character) dies, all objects freeze (well, most of them anyway). When they've frozen, they all jump to "RunObjectDisplayOnly:"

RunObjectDisplayOnly:
        moveq   #0,d0
        move.b  (a0),d0 ; get the object's ID
        beq.s   +       ; if it's obj00, skip it
        tst.b   render_flags(a0)        ; should we render it?
        bpl.s   +                       ; if not, skip it
        bsr.w   DisplaySprite
+
        lea     next_object(a0),a0 ; load 0bj address
        dbf     d7,RunObjectDisplayOnly
        rts
; End of function RunObjectDisplayOnly

As you can see, ALL objects will jump to DisplaySprite. This wasn't a problem before because it just took a byte from the priority and then convert it to a word, which was always even. But now, it doesn't with our new manager. It just takes the word. So, if the priority SST is odd, it will still try to display it, and then freeze.

Most of the time, when Sonic dies, everything will be fine. But if Sonic dies near the EHZ bridge, CNZ spring, ARZ boss, most stuff that use DisplaySprite3, the game will freeze. Because instead of displaying whatever is at d0, it's jumping to DisplaySprite, and taking it from priority SST instead, which is more-than-likely odd (because it was used for something else).

What we need to do, is set a new priority for all objects when Sonic is dead. So, we're going to change the branch to DisplaySprite to something else. So, go to "RunObjectDisplayOnly:" and change it to this:

RunObjectDisplayOnly:
        moveq   #0,d0
        move.b  (a0),d0 ; get the object's ID
        beq.s   +       ; if it's obj00, skip it
        tst.b   render_flags(a0)        ; should we render it?
        bpl.s   +                       ; if not, skip it
        move.w  #$200,d0
        bsr.w   DisplaySprite3
+
        lea     next_object(a0),a0 ; load 0bj address
        dbf     d7,RunObjectDisplayOnly
        rts
; End of function RunObjectDisplayOnly

So now, when Sonic dies, all objects that are frozen will have their priority set to #$200. This is what most objects use anyway. I didn't actually notice any difference to when Sonic normally dies.

Because of this now, objects that were originally DisplaySprite3, when Sonic dies, it won't try taking the priority SST anymore, and just use the #$200 we've set. If you enter debug when in the middle of dying, then exit so you're back alive, all objects will start using their orignial priority (or d0 if they were using DisplaySprite3).

The "Game Over" object is one of the few objects that doesn't freeze when Sonic dies, so that object will still use it's own priority (which is 0). So you don't have to worry about the "Game Over" text being behind any objects.

Now, A couple issues occur, because, when you die, all objects jump to the "RunObjectDisplayOnly:". As they're all using the same priority, a newer one will overlap. Take the EHZ boss. His drill gets displayed first, then his wheels (so they overlap), then the cockpit (so they overlap the rest), then finally Eggman (which overlaps all). Another example of this is during the final boss, where the final boss overlaps his arms.

To fix this, it's simple, and I should have thought of it before (Sonic 2 Recreation has different coding and doesn't suffer this). Anyway, go to "RunObjectDisplayOnly:", and change from this:

RunObjectDisplayOnly:
        moveq   #0,d0
        move.b  (a0),d0 ; get the object's ID
        beq.s   +       ; if it's obj00, skip it
        tst.b   render_flags(a0)        ; should we render it?
        bpl.s   +                       ; if not, skip it
        move.w  #$200,d0
        bsr.w   DisplaySprite3
+
        lea     next_object(a0),a0 ; load 0bj address
        dbf     d7,RunObjectDisplayOnly
        rts
; End of function RunObjectDisplayOnly

to this:

RunObjectDisplayOnly:
        moveq   #0,d0                   ; Clear d0 quickly
        move.b  (a0),d0                 ; get the object's ID
        beq.s   ++                      ; if it's obj00, skip it
        tst.b   render_flags(a0)        ; should we render it?
        bpl.s   ++                      ; if not, skip it
        move.w  priority(a0),d0         ; move object's priority to d0
        btst    #6,render_flags(a0)     ; is the compound sprites flag set?
        beq.s   +                       ; if not, branch
        move.w  #$200,d0                ; move $200 to d0
+
        bsr.w   DisplaySprite3          ; Display the object with whatever is set at d0
+
        lea     next_object(a0),a0      ; load 0bj address
        dbf     d7,RunObjectDisplayOnly
        rts
; End of function RunObjectDisplayOnly

So now, when all objects jump here, it will ask first, does it have the compound sprite flag set? If not, branch and continue, and use the object's original priority. If it IS set, it means that the current object's status table also contains information about other child sprites which need to be drawn using the current object's mappings. In other words, for our sake, it's using priority for something different. Therefore, it will NOT branch and it will move $200 to d0 instead.

Step 11: Fix the priority for Special Stages

This took me fucking forever to fix! But luckily for you, I've figured it out without having to change any of the priorities (unlike the bosses we did earlier). It's going to be easy for you!

Playing the special stages as they are now will make the game freeze within 2 seconds. That can't be good, right? We will need to fix this.

Go to "Obj87:". This is the "Number of rings in Special Stage" object. This is what cause the special stage to freeze in a matter of seconds.

You may notice that it's not moving anything to a priority SST. That's because the priority for this object is 0. The problem is, it's moving numbers from SST $19(a0) onwards. Mainly at $19(a0) is #$20. Now, because we've got the new priority manager, it will grab it's priority (which is 0), but as it's now a word, it will grab that #$20. So, it will have the data #$0020. DisplaySprite cannot process that, and crashes. We need to make it grab #$0000 and not #$0020, but we can't change anything at SST $19(a0), otherwise the special stage will start to misbehave. The fix? It's quite simple.

At these locations (Xenowhirl 2007 users):

  • loc_7536:
  • loc_758C:
  • loc_75BC:
  • loc_75C8:
  • loc_7648:

SVN users ONLY: First label is "loc_7536:"
For second label, do a search for this line:

move.b  d2,sub2_mapframe-sub2_x_pos(a1) ; sub2_mapframe

Third label is directly underneath the previous label we were at
Fourth Label is directly underneath the previous label we were at
And, for the fifth label, do a search for this line:

        move.w  #$D8,(a1)       ; sub?_x_pos

and it's the + label directly underneath it

You will see the code branching (in some way) to "JmpTo_DisplaySprite". We're going to create a new label for this object to jump to instead. So, at these locations, change all:

JmpTo_DisplaySprite

to this:

JmpTo_DisplaySpriteSpecial

Example (SVN users, reference only):

loc_7536:
        move.b  d3,objoff_F(a0)
        bra.w   JmpTo_DisplaySpriteSpecial

Make sure you only change it to the new label at these locations!

Next, go to the label itself "JmpTo_DisplaySprite" to see this (SVN user, you're will look ever so slightly different, but you can still copy and paste my fix that I'm about to show):

JmpTo_DisplaySprite 
        jmp     DisplaySprite.l

Directly underneath it, add this:

JmpTo_DisplaySpriteSpecial
        move.w  #0,d0
        jmp     DisplaySprite3.l

So you end up with this (SVN users, reference only):

JmpTo_DisplaySprite 
        jmp     DisplaySprite.l
        
JmpTo_DisplaySpriteSpecial
        move.w  #0,d0
        jmp     DisplaySprite3.l

Now, obj87 only, will jump here for displaying the sprite. It will force #$0000 to be displayed instead. And it doesn't affect any of the object itself. We couldn't edit the "JmpTo_DisplaySprite" label itself because other objects are jumping to that label, and we don't want to edit other object's priority.

The special stage will no longer freeze within seconds...

But hold on there!

I'm sorry, the special stage can still freeze halfway through. Yeah, another object does not want to play ball. The "Messages/checkpoint from Special Stage" object.

This object displays the "Rings to go!" text and the "Collect 40 rings" text and to make them fly apart from each other. It also displays the "Cool" and "Not enough rings" text and the hand with the blue emblem, etc. To be totally honest, this object is almost perfect, it's the "Rings to go" that's causing the freezing. The problem is pretty much the same as "Number of rings in Special Stage" object.

Go to "loc_357B2:" ("Obj5A_FlashMessage:" for SVN) And you should see (SVN users, reference only):

loc_357B2:
        tst.b   ($FFFFDBA0).w
        bne.w   return_357D0
        tst.b   ($FFFFDBA6).w
        bne.s   return_357D0
        move.b  ($FFFFFE0F).w,d0
        andi.b  #7,d0
        cmpi.b  #6,d0
        bcs.w   JmpTo44_DisplaySprite

That branch to "JmpTo44_DisplaySprite" is the problem. Looking at the object, this bit's priority is always 0. So, change the "JmpTo44_DisplaySprite" to "JmpTo_DisplaySpriteSpecial2", so you have this (SVN users, reference only):

loc_357B2:
        tst.b   ($FFFFDBA0).w
        bne.w   return_357D0
        tst.b   ($FFFFDBA6).w
        bne.s   return_357D0
        move.b  ($FFFFFE0F).w,d0
        andi.b  #7,d0
        cmpi.b  #6,d0
        bcs.w   JmpTo_DisplaySpriteSpecial2

Yes, we're creating another new label! It's the best way to do it. Next, go to "loc_35F76:". Right above that label, paste this:

JmpTo_DisplaySpriteSpecial2:
        move.w  #0,d0
        jmp     Displaysprite3.l

So you end up with this (SVN users, reference only):

JmpTo_DisplaySpriteSpecial2:
        move.w  #0,d0
        jmp     Displaysprite3.l

loc_35F76:
        add.w   d0,d0
        move.w  d0,d1
        add.w   d0,d0
        add.w   d1,d0
        move.w  word_35F92(pc,d0.w),(Normal_palette_line4+$16).w
        move.w  word_35F92+2(pc,d0.w),(Normal_palette_line4+$18).w
        move.w  word_35F92+4(pc,d0.w),(Normal_palette_line4+$1A).w
        rts

Fixed! That part of the object will be forced to use #$0000 again and to display it. The rest of the object is completely fine though, and does not need editing (plus, the rest of it isn't using 0).

Step 11.b: EXTRA STEP for SVN users

ATTENTION SVN USERS! You've got one more thing to do. Fortunately it is piss easy. Now, if you're a Xenowhirl user, you're safe and can ignore this part and move along to Step 12.

Seems that if you've followed this guide thus far as an SVN user, there's a glitch with the Special Stages. Sonic and Tails act like they're being hit by the bombs object over and over, making it impossible to retain any rings in the special stages. The reason? inertia is being used for something else in the special stages. This doesn't seem to be the case in Xenowhirl's disassembly.

So, in your SST table, you're better off switching inertia with something else. invulnerable_time seems to be the best to swap it with. So change them so you end up with this:

invulnerable_time =     $20 ; and $21 ; time remaining until you stop blinking
inertia =               $30 ; and $31 ; directionless representation of speed... not updated in the air

Step 12: Fix the CNZ pull-spring

This CNZ spring has lost it's width_pixels SST. Similar problem to what the priorty manager has done, it's width_pixel is being over-written by something else in the objects code.

If you've already applied this fix to your hack from my "How to free up 2 universal SST's" guide, you can skip this step. Otherwise, here is an excerpt:

Go to "loc_2ABFA:" and you'll see this:

loc_2ABFA:
        bsr.w   JmpTo49_Adjust2PArtPointer
        move.b  #4,render_flags(a0)
        bset    #6,render_flags(a0)
        move.b  #1,objoff_B(a0)
        tst.b   subtype(a0)
        beq.s   loc_2AC54
        addq.b  #2,routine(a0)
        move.b  #$20,objoff_E(a0)
        move.b  #$18,width_pixels(a0)
        move.w  x_pos(a0),objoff_2E(a0)
        move.w  y_pos(a0),objoff_34(a0)
        move.w  x_pos(a0),d2
        move.w  y_pos(a0),d3
        addi.w  #0,d3
        move.b  #1,objoff_F(a0)
        lea     $10(a0),a2
        move.w  d2,(a2)+
        move.w  d3,(a2)+
        move.w  #2,(a2)+
        bra.w   loc_2AE56

Cut out the line "move.b #$18,width_pixels(a0)" so it's not there anymore. We're going to move it.

Next, go to "Obj85:" and you'll see this:

Obj85:
        moveq   #0,d0
        move.b  routine(a0),d0
        move.w  off_2ABCE(pc,d0.w),d1
        jsr     off_2ABCE(pc,d1.w)
        move.w  #$200,d0
        tst.w   (Two_player_mode).w
        beq.s   loc_2ABA0
        bra.w   JmpTo4_DisplaySprite3

Just before the "move.w #$200,d0", paste the width_pixel line there. So, you end up with this:

Obj85:
        moveq   #0,d0
        move.b  routine(a0),d0
        move.w  off_2ABCE(pc,d0.w),d1
        jsr     off_2ABCE(pc,d1.w)
        move.b  #$18,width_pixels(a0)   ; Now moved here instead of being at loc_2ABFA
        move.w  #$200,d0
        tst.w   (Two_player_mode).w
        beq.s   loc_2ABA0
        bra.w   JmpTo4_DisplaySprite3

There. That object is now fixed.

Step 13: Fix the priority for Tails' tails

An example of the error with Tails' tails.

Uh oh, an observant eye would notice that Tails' tails' priority has been affected, despite the fact that Tails' priority, and his tails' priority are both $100. Now it seems that his tails are not as much as a priority anymore. Anyway, flamewing had a solution, which he uses for his hack; Sonic 2 Heroes. The fix:

First, you're going to have to use some RAM. Only a word. So, go to your list of equates. I used $FFFFF5C0 (it's free whether you use the S1 sound driver or not). Call the RAM "Tails_Tails_ptr"

Tails_Tails_ptr =               ramaddr( $FFFFF5C0 )

Our new RAM is ready for use. First, go to "InitPlayers:" and under the line:

        move.b  #2,(Sidekick).w ; load Obj02 Tails object at $FFFFB040

SVN users, yours will say:

        move.b  #ObjID_Tails,(Sidekick+id).w ; load Obj02 Tails object at $FFFFB040

Insert this:

        move.w  #Tails_Tails,(Tails_Tails_ptr).w

So you have something like this (SVN users, reference only):

        move.b  #2,(Sidekick).w ; load Obj02 Tails object at $FFFFB040
        move.w  #Tails_Tails,(Tails_Tails_ptr).w
        move.w  (MainCharacter+x_pos).w,(Sidekick+x_pos).w
        move.w  (MainCharacter+y_pos).w,(Sidekick+y_pos).w
        subi.w  #$20,(Sidekick+x_pos).w
        addi.w  #4,(Sidekick+y_pos).w
        move.b  #8,(Tails_Dust).w ; load Obj08 Tails' spindash dust/splash object at $FFFFD140

Do the same at label "InitPlayers_TailsAlone:", so you have something like this (SVN users, reference only):

InitPlayers_TailsAlone:
        move.b  #2,(MainCharacter).w ; load Obj02 Tails object at $FFFFB000
        move.w  #Tails_Tails,(Tails_Tails_ptr).w
        move.b  #8,(Tails_Dust).w ; load Obj08 Tails' spindash dust/splash object at $FFFFD100
        addi.w  #4,(MainCharacter+y_pos).w
        rts
; End of function InitPlayers

Next, go to "loc_A2F2:". This is for when you're at the cutscene at the end of the game.

XenoWhirl users, Change this:

loc_A2F2:
        moveq   #$E,d0
        move.b  #2,(a1) ; load Tails object
        move.b  #$81,$2A(a1)
        move.b  #5,(Object_RAM+$80).w ; load Obj05 (Tails' tails) at $FFFFB080
        move.w  a1,(Object_RAM+$80+parent).w
        rts

to this:

loc_A2F2:
        moveq   #$E,d0
        move.b  #2,(a1) ; load Tails object
        move.b  #$81,$2A(a1)
        move.w  #Object_RAM+$80,(Tails_Tails_ptr).w
        rts

SVN users, change this:

loc_A2F2:
        moveq   #$E,d0
        move.b  #ObjID_Tails,id(a1) ; load Tails object
        move.b  #$81,obj_control(a1)
        move.b  #ObjID_TailsTails,(Tails_Tails_Cutscene+id).w ; load Obj05 (Tails' tails) at $FFFFB080
        move.w  a1,(Tails_Tails_Cutscene+parent).w
        rts

to this:

loc_A2F2:
        moveq   #$E,d0
        move.b  #ObjID_Tails,id(a1) ; load Tails object
        move.b  #$81,obj_control(a1)
        move.w  #Tails_Tails_Cutscene,(Tails_Tails_ptr).w ; Tails' tails at $FFFFB080
        rts

Next, go to "Obj02_Init_Continued:", and change the last few lines.

Xenowhirl users, change this:

        move.b  #5,(Tails_Tails).w ; load Obj05 (Tails' Tails) at $FFFFD000
        move.w  a0,(Tails_Tails+parent).w ; set its parent object to this

And change it to this:

        movea.w (Tails_Tails_ptr).w,a1
        move.b  #5,0(a1) ; load Obj05 (Tails' Tails) at $FFFFD000
        move.w  a0,parent(a1) ; set its parent object to this

SVN users, yours will say:

        move.b  #ObjID_TailsTails,(Tails_Tails+id).w ; load Obj05 (Tails' Tails) at $FFFFD000
        move.w  a0,(Tails_Tails+parent).w ; set its parent object to this

Change it to this:

        movea.w  (Tails_Tails_ptr).w,a1
        move.b  #ObjID_TailsTails,id(a1) ; load Obj05 (Tails' Tails)
        move.w  a0,parent(a1) ; set its parent object to this

One more step! Go to "Obj02:" See the command "jmp Obj02_States(pc,d1.w)"/"jmp Obj02_Index(pc,d1.w)"? Change the "jmp" to a "jsr". And directly underneath it, add this:

        movea.w (Tails_Tails_ptr).w,a1
        tst.b   routine(a1)
        beq.s   +
        jmp     (DisplaySprite2).l
+
        rts

So you have something like this (SVN users, reference only):

Obj02:
        ; a0=character
        cmpi.w  #2,(Player_mode).w
        bne.s   +
        move.w  (Camera_Min_X_pos).w,(Tails_Min_X_pos).w
        move.w  (Camera_Max_X_pos).w,(Tails_Max_X_pos).w
        move.w  (Camera_Max_Y_pos_now).w,(Tails_Max_Y_pos).w
+
        moveq   #0,d0
        move.b  routine(a0),d0
        move.w  Obj02_States(pc,d0.w),d1
        jsr     Obj02_States(pc,d1.w)
        movea.w (Tails_Tails_ptr).w,a1
        tst.b   routine(a1)
        beq.s   +
        jmp     (DisplaySprite2).l
+
        rts

Done! Tails' tails should now work with the right priority! Tails will now queue his tails for drawing right after himself. Without this, the tails would be queued for drawing after all objects between Tails and the tails that have the same priority. And that's why before, Tails' tails had less priority.

Step 14: Fix Eggman Death Egg Robot Priority

One more fix! When Eggman jumps into the Death Egg robot boss he shows up in front of the robot. This isn't a big deal but we might as well fix it.

Go to "loc_3CFC0" and simply set Eggman's priority higher than his robot, $300 works here.

loc_3CFC0:
	move.b	#2,mapping_frame(a0)
	clr.w	x_vel(a0)
	tst.b	render_flags(a0)
	bpl.s	+
	addq.b	#2,routine_secondary(a0) ; => ObjC6_State2_State5
	move.w	#$300,priority(a0)		; Set Eggman's priority to fix an issue with the Death Egg Robot 
	move.w	#$80,x_vel(a0)
	move.w	#-$200,y_vel(a0)
	move.b	#2,mapping_frame(a0)
	move.w	#$50,objoff_2A(a0)
	bset	#3,status(a0)

Step 15: Done

That's it guys! All done! You have now successfully ported Sonic 3 and Knuckles' Priority Manager into Sonic 2. Your hack should now be experiencing less lag! Please be aware though, this will not eliminate all lag from your hack. It just helps to get rid of some of it. To get rid of more lag, you need to do more; like Object Managers and etc.

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