Difference between revisions of "Add Spin Dash to Sonic 1/Part 2"
From Sonic Retro
m (Text replace - 'spindash' to 'Spin Dash') |
|||
(27 intermediate revisions by 15 users not shown) | |||
Line 1: | Line 1: | ||
{{GuideBy|Puto}} | {{GuideBy|Puto}} | ||
− | [[Media:Spindash1.zip|Here]]'s a Sonic 1 + | + | [[Media:Spindash1.zip|Here]]'s a Sonic 1 + Spin Dash ROM built by following [[User:Lightning|Lightning]]'s [[SCHG_How-to:Add_Spin Dash_to_Sonic_1/Part_1|guide]]. |
If you try it, you will be able to easily see that there are several things that keep it from being a perfect port.<br> | If you try it, you will be able to easily see that there are several things that keep it from being a perfect port.<br> | ||
For example, if you Spin Dash next to a monitor, you won't break it. <br> | For example, if you Spin Dash next to a monitor, you won't break it. <br> | ||
− | Also, the sound effect, while fitting, isn't ideal, there's no dust, and if you Spin Dash while on a seesaw (SLZ), Strange | + | Also, the sound effect, while fitting, isn't ideal, there's no dust, and if you Spin Dash while on a seesaw (SLZ), Strange Things™ |
will happen. So let's fix this. | will happen. So let's fix this. | ||
==Problem 1: The Monitor Bug== | ==Problem 1: The Monitor Bug== | ||
− | <asm>loc_A1EC: ; XREF: Obj26_Solid | + | When you try to Spin Dash next to a monitor, you won't be able to do that and simply stop when releasing. It only works if you walk a little bit away from the monitor and do the Spin Dash. To fix that, go to ''loc_A1EC'' ('''Hint for GitHub Disassembly users:''' Look for file called _incObj/26 Monitor.asm). You should see something like this: |
+ | <syntaxhighlight lang="asm">loc_A1EC: ; XREF: Obj26_Solid | ||
move.w #$1A,d1 | move.w #$1A,d1 | ||
move.w #$F,d2 | move.w #$F,d2 | ||
Line 16: | Line 17: | ||
bmi.s loc_A20A | bmi.s loc_A20A | ||
cmpi.b #2,$1C(a1) ; is Sonic rolling? | cmpi.b #2,$1C(a1) ; is Sonic rolling? | ||
− | beq.s loc_A25C ; if yes, branch</ | + | beq.s loc_A25C ; if yes, branch</syntaxhighlight> |
− | + | By reading this code, you can see that there is a check for the rolling animation there. This is what causes the monitor bug. To fix it, simply add a second check for the Spin Dash animation after this one: | |
− | causes the monitor bug. To fix it, simply add a second check for the Spin Dash animation after this one: | ||
− | <asm>loc_A1EC: ; XREF: Obj26_Solid | + | <syntaxhighlight lang="asm">loc_A1EC: ; XREF: Obj26_Solid |
move.w #$1A,d1 | move.w #$1A,d1 | ||
move.w #$F,d2 | move.w #$F,d2 | ||
Line 30: | Line 30: | ||
cmpi.b #2,$1C(a1) ; is Sonic rolling? | cmpi.b #2,$1C(a1) ; is Sonic rolling? | ||
beq.s loc_A25C ; if yes, branch | beq.s loc_A25C ; if yes, branch | ||
− | + | cmpi.b #$1F,$1C(a1) ; is Sonic spin-dashing? | |
− | beq loc_A25C ; if yes, branch</ | + | beq.s loc_A25C ; if yes, branch</syntaxhighlight> |
− | + | Great, let's try it. It should be working and... oh dear.<br> | |
− | |||
This isn't right. | This isn't right. | ||
[[Image:SpindashGuide_Pic1.png]] | [[Image:SpindashGuide_Pic1.png]] | ||
− | We're Spin Dashing, we're not supposed to use the pushing animation. To fix that, go to | + | We're Spin Dashing, we're not supposed to use the pushing animation. To fix that, go to Sonic_SpinDash, and look at what it does.<br> |
These are the first lines: | These are the first lines: | ||
− | <asm> | + | <syntaxhighlight lang="asm">Sonic_SpinDash: |
tst.b $39(a0) ; already Spin Dashing? | tst.b $39(a0) ; already Spin Dashing? | ||
− | bne.s loc2_1AC8E ; if set, branch</ | + | bne.s loc2_1AC8E ; if set, branch</syntaxhighlight> |
Since we know we're Spin Dashing when $39(a0) is set, let's make sure that the Spin Dash animation is used when that happens.<br> | Since we know we're Spin Dashing when $39(a0) is set, let's make sure that the Spin Dash animation is used when that happens.<br> | ||
Therefore, go to loc2_1AC8E, and right after the label, add the following line: | Therefore, go to loc2_1AC8E, and right after the label, add the following line: | ||
− | <asm> move.b #$1F,$1C(a0)</ | + | <syntaxhighlight lang="asm"> move.b #$1F,$1C(a0)</syntaxhighlight> |
Build the ROM again, and the monitor bug should be permanently taken care of. [[Media:Spindash2.zip|Here]]'s a ROM after this | Build the ROM again, and the monitor bug should be permanently taken care of. [[Media:Spindash2.zip|Here]]'s a ROM after this | ||
step was carried out. | step was carried out. | ||
− | ==Problem 2: | + | ==Problem 2: Spin Dash Sound Effect== |
Adding a sound effect to Sonic 1 isn't the most trivial of things to do. So be sure to follow these steps exactly. | Adding a sound effect to Sonic 1 isn't the most trivial of things to do. So be sure to follow these steps exactly. | ||
Line 61: | Line 60: | ||
line there: | line there: | ||
− | <asm> cmpi.b #$E0,d7 | + | <syntaxhighlight lang="asm"> cmpi.b #$E0,d7 |
− | bcs.w Sound_D0toDF ; sound $D0-$DF</ | + | bcs.w Sound_D0toDF ; sound $D0-$DF</syntaxhighlight> |
Since only $D0 is actually used here, it's a rather large waste to keep the rest of the slots unused, so change that $E0 to $D1: | Since only $D0 is actually used here, it's a rather large waste to keep the rest of the slots unused, so change that $E0 to $D1: | ||
− | <asm> cmpi.b #$D1,d7 | + | <syntaxhighlight lang="asm"> cmpi.b #$D1,d7 |
− | bcs.w Sound_D0toDF ; sound $D0</ | + | bcs.w Sound_D0toDF ; sound $D0</syntaxhighlight> |
And after that line, let's add our own comparison: | And after that line, let's add our own comparison: | ||
− | <asm> | + | <syntaxhighlight lang="asm"> |
− | + | cmpi.b #$DF,d7 | |
+ | blo.w Sound_D1toDF ; sound $D1-$DF | ||
+ | </syntaxhighlight> | ||
Now, we have to actually create this routine. So go to Sound_A0toCF, and see what it does: | Now, we have to actually create this routine. So go to Sound_A0toCF, and see what it does: | ||
− | <asm>Sound_A0toCF: ; XREF: Sound_ChkValue | + | <syntaxhighlight lang="asm">Sound_A0toCF: ; XREF: Sound_ChkValue |
tst.b $27(a6) | tst.b $27(a6) | ||
bne.w loc_722C6 | bne.w loc_722C6 | ||
Line 112: | Line 113: | ||
subq.b #1,d7 | subq.b #1,d7 | ||
moveq #$30,d6 | moveq #$30,d6 | ||
− | (...)</ | + | (...)</syntaxhighlight> |
As you can see, this routine has a lot of checks for special sound effects, like the ring effect and the pushing effect.<br> | As you can see, this routine has a lot of checks for special sound effects, like the ring effect and the pushing effect.<br> | ||
We don't need that for our new routine, so trash all of that, and you end up with: | We don't need that for our new routine, so trash all of that, and you end up with: | ||
− | <asm>Sound_D1toDF: | + | <syntaxhighlight lang="asm">Sound_D1toDF: |
tst.b $27(a6) | tst.b $27(a6) | ||
bne.w loc_722C6 | bne.w loc_722C6 | ||
Line 136: | Line 137: | ||
subq.b #1,d7 | subq.b #1,d7 | ||
moveq #$30,d6 | moveq #$30,d6 | ||
− | (...)</ | + | (...)</syntaxhighlight> |
There's a problem here: If we subtract $A0 to get the current index, then the next entry in this index will be sound $D0, | There's a problem here: If we subtract $A0 to get the current index, then the next entry in this index will be sound $D0, | ||
which is handled by a different routine, forcing us to have an unused entry in an index. So, change that line to subtract $A1 instead: | which is handled by a different routine, forcing us to have an unused entry in an index. So, change that line to subtract $A1 instead: | ||
− | <asm>Sound_D1toDF: | + | <syntaxhighlight lang="asm">Sound_D1toDF: |
tst.b $27(a6) | tst.b $27(a6) | ||
bne.w loc_722C6 | bne.w loc_722C6 | ||
Line 160: | Line 161: | ||
subq.b #1,d7 | subq.b #1,d7 | ||
moveq #$30,d6 | moveq #$30,d6 | ||
− | (...)</ | + | (...)</syntaxhighlight> |
Also, it can be noted that any code after the $A1 line is common to the two routines. <br> | Also, it can be noted that any code after the $A1 line is common to the two routines. <br> | ||
So in the original routine, add a label right before "lsl.w #2,d7": | So in the original routine, add a label right before "lsl.w #2,d7": | ||
− | <asm>Sound_notA7: | + | <syntaxhighlight lang="asm">Sound_notA7: |
movea.l (Go_SoundIndex).l,a0 | movea.l (Go_SoundIndex).l,a0 | ||
subi.b #$A0,d7 | subi.b #$A0,d7 | ||
Line 179: | Line 180: | ||
subq.b #1,d7 | subq.b #1,d7 | ||
moveq #$30,d6 | moveq #$30,d6 | ||
− | (...)</ | + | (...)</syntaxhighlight> |
And we can now greatly reduce the size of our new routine: | And we can now greatly reduce the size of our new routine: | ||
− | <asm>Sound_D1toDF: | + | <syntaxhighlight lang="asm">Sound_D1toDF: |
tst.b $27(a6) | tst.b $27(a6) | ||
bne.w loc_722C6 | bne.w loc_722C6 | ||
Line 192: | Line 193: | ||
movea.l (Go_SoundIndex).l,a0 | movea.l (Go_SoundIndex).l,a0 | ||
sub.b #$A1,d7 | sub.b #$A1,d7 | ||
− | bra SoundEffects_Common</ | + | bra SoundEffects_Common</syntaxhighlight> |
So, after this is done, put the routine right above Sound_A0toCF.<br> | So, after this is done, put the routine right above Sound_A0toCF.<br> | ||
Line 198: | Line 199: | ||
Put this file in the sound\ directory of your source code, and go to the SoundD0 label. After the even, add the following lines: | Put this file in the sound\ directory of your source code, and go to the SoundD0 label. After the even, add the following lines: | ||
− | <asm>SoundD1: incbin sound\soundD1.bin | + | <syntaxhighlight lang="asm">SoundD1: incbin sound\soundD1.bin |
− | even</ | + | even</syntaxhighlight> |
So the surrounding code should look like this: | So the surrounding code should look like this: | ||
− | <asm>SoundCF: incbin sound\soundCF.bin | + | <syntaxhighlight lang="asm">SoundCF: incbin sound\soundCF.bin |
even | even | ||
SoundD0: incbin sound\soundD0.bin | SoundD0: incbin sound\soundD0.bin | ||
Line 210: | Line 211: | ||
even | even | ||
SegaPCM: incbin sound\segapcm.bin | SegaPCM: incbin sound\segapcm.bin | ||
− | even</ | + | even</syntaxhighlight> |
And finally, add SoundD1 to the index: | And finally, add SoundD1 to the index: | ||
− | <asm>SoundIndex: dc.l SoundA0, SoundA1, SoundA2 | + | <syntaxhighlight lang="asm">SoundIndex: dc.l SoundA0, SoundA1, SoundA2 |
dc.l SoundA3, SoundA4, SoundA5 | dc.l SoundA3, SoundA4, SoundA5 | ||
dc.l SoundA6, SoundA7, SoundA8 | dc.l SoundA6, SoundA7, SoundA8 | ||
Line 230: | Line 231: | ||
dc.l SoundCA, SoundCB, SoundCC | dc.l SoundCA, SoundCB, SoundCC | ||
dc.l SoundCD, SoundCE, SoundCF | dc.l SoundCD, SoundCE, SoundCF | ||
− | dc.l SoundD1</ | + | dc.l SoundD1</syntaxhighlight> |
− | Now, go to | + | Now, go to Sonic_SpinDash, and change all references to sound $BE to reference sound $D1 instead. |
For reference, here's the complete routine: | For reference, here's the complete routine: | ||
− | <asm> | + | <syntaxhighlight lang="asm">Sonic_SpinDash: |
tst.b $39(a0) ; already Spin Dashing? | tst.b $39(a0) ; already Spin Dashing? | ||
bne.s loc2_1AC8E ; if set, branch | bne.s loc2_1AC8E ; if set, branch | ||
Line 256: | Line 257: | ||
bsr.w Sonic_LevelBound | bsr.w Sonic_LevelBound | ||
bsr.w Sonic_AnglePos | bsr.w Sonic_AnglePos | ||
− | + | ||
locret2_1AC8C: | locret2_1AC8C: | ||
rts | rts | ||
; --------------------------------------------------------------------------- | ; --------------------------------------------------------------------------- | ||
− | + | ||
loc2_1AC8E: | loc2_1AC8E: | ||
move.b #$1F,$1C(a0) | move.b #$1F,$1C(a0) | ||
Line 274: | Line 275: | ||
move.b $3A(a0),d0 ; copy charge count | move.b $3A(a0),d0 ; copy charge count | ||
add.w d0,d0 ; double it | add.w d0,d0 ; double it | ||
− | move.w | + | move.w Dash_Speeds(pc,d0.w),$14(a0) ; get normal speed |
− | |||
− | |||
− | |||
− | |||
− | |||
move.w $14(a0),d0 ; get inertia | move.w $14(a0),d0 ; get inertia | ||
subi.w #$800,d0 ; subtract $800 | subi.w #$800,d0 ; subtract $800 | ||
Line 290: | Line 286: | ||
beq.s loc2_1ACF4 ; if not, branch | beq.s loc2_1ACF4 ; if not, branch | ||
neg.w $14(a0) ; negate inertia | neg.w $14(a0) ; negate inertia | ||
− | + | ||
loc2_1ACF4: | loc2_1ACF4: | ||
bset #2,$22(a0) ; set unused (in s1) flag | bset #2,$22(a0) ; set unused (in s1) flag | ||
Line 297: | Line 293: | ||
jsr (PlaySound_Special).l ; play it! | jsr (PlaySound_Special).l ; play it! | ||
bra.s loc2_1AD78 | bra.s loc2_1AD78 | ||
− | ; | + | ; =========================================================================== |
− | + | Dash_Speeds: dc.w $800 ; 0 | |
− | |||
dc.w $880 ; 1 | dc.w $880 ; 1 | ||
dc.w $900 ; 2 | dc.w $900 ; 2 | ||
Line 308: | Line 303: | ||
dc.w $B80 ; 7 | dc.w $B80 ; 7 | ||
dc.w $C00 ; 8 | dc.w $C00 ; 8 | ||
− | + | ; =========================================================================== | |
− | + | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
loc2_1AD30: ; If still charging the dash... | loc2_1AD30: ; If still charging the dash... | ||
tst.w $3A(a0) ; check charge count | tst.w $3A(a0) ; check charge count | ||
Line 329: | Line 313: | ||
bcc.s loc2_1AD48 ; ??? branch if carry clear | bcc.s loc2_1AD48 ; ??? branch if carry clear | ||
move.w #0,$3A(a0) ; set charge count to 0 | move.w #0,$3A(a0) ; set charge count to 0 | ||
− | + | ||
loc2_1AD48: | loc2_1AD48: | ||
move.b ($FFFFF603).w,d0 ; read controller | move.b ($FFFFF603).w,d0 ; read controller | ||
Line 341: | Line 325: | ||
bcs.s loc2_1AD78 ; if not, then branch | bcs.s loc2_1AD78 ; if not, then branch | ||
move.w #$800,$3A(a0) ; reset it to max | move.w #$800,$3A(a0) ; reset it to max | ||
− | + | ||
loc2_1AD78: | loc2_1AD78: | ||
addq.l #4,sp ; increase stack ptr | addq.l #4,sp ; increase stack ptr | ||
Line 348: | Line 332: | ||
bcc.s loc2_1AD88 | bcc.s loc2_1AD88 | ||
addq.w #4,($FFFFEED8).w | addq.w #4,($FFFFEED8).w | ||
− | + | ||
loc2_1AD88: | loc2_1AD88: | ||
subq.w #2,($FFFFEED8).w | subq.w #2,($FFFFEED8).w | ||
− | + | ||
loc2_1AD8C: | loc2_1AD8C: | ||
bsr.w Sonic_LevelBound | bsr.w Sonic_LevelBound | ||
bsr.w Sonic_AnglePos | bsr.w Sonic_AnglePos | ||
move.w #$60,($FFFFF73E).w ; reset looking up/down | move.w #$60,($FFFFF73E).w ; reset looking up/down | ||
− | rts</ | + | rts</syntaxhighlight> |
So, problem solved. [[Media:Spindash5.zip|Here]]'s a rom of the output after fixing this problem. | So, problem solved. [[Media:Spindash5.zip|Here]]'s a rom of the output after fixing this problem. | ||
− | ==Problem 3: See-Saw Bug (AKA Sonic 2 | + | ==Problem 3: See-Saw Bug (AKA Sonic 2 Spin Dash Bug)== |
This problem occurs when you're Spin Dashing on a seesaw, and then you get thrown up by the seesaw.<br> | This problem occurs when you're Spin Dashing on a seesaw, and then you get thrown up by the seesaw.<br> | ||
You keep your Spin Dash state, and dash off immediately once you land. This is clearly not the wanted behaviour.<br> | You keep your Spin Dash state, and dash off immediately once you land. This is clearly not the wanted behaviour.<br> | ||
To fix this, go to Obj01_MdJump and Obj01_MdJump2, and add the following line at the beginning of each of the two routines: | To fix this, go to Obj01_MdJump and Obj01_MdJump2, and add the following line at the beginning of each of the two routines: | ||
− | <asm> clr.b $39(a0)</ | + | <syntaxhighlight lang="asm"> clr.b $39(a0)</syntaxhighlight> |
This should fix the see-saw bug. [[Media:Spindash6.zip|Here]]'s the rom. | This should fix the see-saw bug. [[Media:Spindash6.zip|Here]]'s the rom. | ||
− | ==Problem 4: | + | ==Problem 4: Spin Dash Dust== |
This is a big one. For starters, you need to port the Spin Dash dust object from Sonic 2.<br> | This is a big one. For starters, you need to port the Spin Dash dust object from Sonic 2.<br> | ||
For your convenience, here is the code to the ported object, paste it right before Obj01 (the sonic object): | For your convenience, here is the code to the ported object, paste it right before Obj01 (the sonic object): | ||
− | <asm> | + | <syntaxhighlight lang="asm">SpinDash_dust: |
− | Sprite_1DD20: ; DATA XREF: ROM: | + | Sprite_1DD20: ; DATA XREF: ROM:0001600C?o |
moveq #0,d0 | moveq #0,d0 | ||
move.b $24(a0),d0 | move.b $24(a0),d0 | ||
Line 380: | Line 364: | ||
jmp off_1DD2E(pc,d1.w) | jmp off_1DD2E(pc,d1.w) | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | off_1DD2E: dc loc_1DD36-off_1DD2E; 0 ; DATA XREF: h+ | + | off_1DD2E: dc loc_1DD36-off_1DD2E; 0 ; DATA XREF: h+6DBA?o h+6DBC?o ... |
dc loc_1DD90-off_1DD2E; 1 | dc loc_1DD90-off_1DD2E; 1 | ||
dc loc_1DE46-off_1DD2E; 2 | dc loc_1DE46-off_1DD2E; 2 | ||
Line 386: | Line 370: | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DD36: ; DATA XREF: h+ | + | loc_1DD36: ; DATA XREF: h+6DBA?o |
addq.b #2,$24(a0) | addq.b #2,$24(a0) | ||
move.l #MapUnc_1DF5E,4(a0) | move.l #MapUnc_1DF5E,4(a0) | ||
Line 404: | Line 388: | ||
; move #-$6E80,$3C(a0) | ; move #-$6E80,$3C(a0) | ||
− | loc_1DD8C: ; CODE XREF: h+ | + | loc_1DD8C: ; CODE XREF: h+6DF6?j h+6E04?j |
; bsr.w sub_16D6E | ; bsr.w sub_16D6E | ||
− | loc_1DD90: ; DATA XREF: h+ | + | loc_1DD90: ; DATA XREF: h+6DBA?o |
movea.w $3E(a0),a2 | movea.w $3E(a0),a2 | ||
moveq #0,d0 | moveq #0,d0 | ||
Line 415: | Line 399: | ||
jmp off_1DDA4(pc,d1.w) | jmp off_1DDA4(pc,d1.w) | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | off_1DDA4: dc loc_1DE28-off_1DDA4; 0 ; DATA XREF: h+ | + | off_1DDA4: dc loc_1DE28-off_1DDA4; 0 ; DATA XREF: h+6E30?o h+6E32?o ... |
dc loc_1DDAC-off_1DDA4; 1 | dc loc_1DDAC-off_1DDA4; 1 | ||
dc loc_1DDCC-off_1DDA4; 2 | dc loc_1DDCC-off_1DDA4; 2 | ||
Line 421: | Line 405: | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DDAC: ; DATA XREF: h+ | + | loc_1DDAC: ; DATA XREF: h+6E30?o |
move ($FFFFF646).w,$C(a0) | move ($FFFFF646).w,$C(a0) | ||
tst.b $1D(a0) | tst.b $1D(a0) | ||
Line 431: | Line 415: | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DDCC: ; DATA XREF: h+ | + | loc_1DDCC: ; DATA XREF: h+6E30?o |
; cmp.b #$C,$28(a2) | ; cmp.b #$C,$28(a2) | ||
; bcs.s loc_1DE3E | ; bcs.s loc_1DE3E | ||
Line 446: | Line 430: | ||
sub #4,$C(a0) | sub #4,$C(a0) | ||
− | loc_1DE06: ; CODE XREF: h+ | + | loc_1DE06: ; CODE XREF: h+6E8A?j |
tst.b $1D(a0) | tst.b $1D(a0) | ||
bne.s loc_1DE28 | bne.s loc_1DE28 | ||
Line 455: | Line 439: | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DE20: ; DATA XREF: h+ | + | loc_1DE20: ; DATA XREF: h+6E30?o |
− | loc_1DE28: ; CODE XREF: h+ | + | loc_1DE28: ; CODE XREF: h+6E42?j h+6E56?j ... |
lea (off_1DF38).l,a1 | lea (off_1DF38).l,a1 | ||
jsr AnimateSprite | jsr AnimateSprite | ||
Line 463: | Line 447: | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DE3E: ; CODE XREF: h+ | + | loc_1DE3E: ; CODE XREF: h+6E5E?j h+6E66?j ... |
move.b #0,$1C(a0) | move.b #0,$1C(a0) | ||
rts | rts | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DE46: ; DATA XREF: h+ | + | loc_1DE46: ; DATA XREF: h+6DBA?o |
bra.w DeleteObject | bra.w DeleteObject | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
Line 487: | Line 471: | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DE64: ; CODE XREF: h+ | + | loc_1DE64: ; CODE XREF: h+6EE0?j |
subq.b #1,$32(a0) | subq.b #1,$32(a0) | ||
bpl.s loc_1DEE0 | bpl.s loc_1DEE0 | ||
Line 500: | Line 484: | ||
sub #4,d1 | sub #4,d1 | ||
− | loc_1DE9A: ; CODE XREF: h+ | + | loc_1DE9A: ; CODE XREF: h+6F1E?j |
add d1,$C(a1) | add d1,$C(a1) | ||
move.b #0,$22(a1) | move.b #0,$22(a1) | ||
Line 516: | Line 500: | ||
or #-$8000,2(a1) | or #-$8000,2(a1) | ||
− | loc_1DEE0: ; CODE XREF: h+ | + | loc_1DEE0: ; CODE XREF: h+6EF4?j h+6F00?j ... |
bsr.s loc_1DEE4 | bsr.s loc_1DEE4 | ||
rts | rts | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | loc_1DEE4: ; CODE XREF: h+ | + | loc_1DEE4: ; CODE XREF: h+6EC0?p h+6F6C?p |
moveq #0,d0 | moveq #0,d0 | ||
move.b $1A(a0),d0 | move.b $1A(a0),d0 | ||
Line 535: | Line 519: | ||
move $3C(a0),d4 | move $3C(a0),d4 | ||
− | loc_1DF0A: ; CODE XREF: h+ | + | loc_1DF0A: ; CODE XREF: h+6FBE?j |
moveq #0,d1 | moveq #0,d1 | ||
move (a2)+,d1 | move (a2)+,d1 | ||
Line 552: | Line 536: | ||
rts | rts | ||
− | locret_1DF36: ; CODE XREF: h+ | + | locret_1DF36: ; CODE XREF: h+6F7A?j h+6F90?j |
rts | rts | ||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ | ||
− | off_1DF38: dc byte_1DF40-off_1DF38; 0 ; DATA XREF: h+ | + | off_1DF38: dc byte_1DF40-off_1DF38; 0 ; DATA XREF: h+6EB4?o h+6FC4?o ... |
dc byte_1DF43-off_1DF38; 1 | dc byte_1DF43-off_1DF38; 1 | ||
dc byte_1DF4F-off_1DF38; 2 | dc byte_1DF4F-off_1DF38; 2 | ||
dc byte_1DF58-off_1DF38; 3 | dc byte_1DF58-off_1DF38; 3 | ||
− | byte_1DF40: dc.b $1F, 0,$FF ; 0 ; DATA XREF: h+ | + | byte_1DF40: dc.b $1F, 0,$FF ; 0 ; DATA XREF: h+6FC4?o |
− | byte_1DF43: dc.b 3, 1, 2, 3, 4, 5, 6, 7, 8, 9,$FD, 0; 0 ; DATA XREF: h+ | + | byte_1DF43: dc.b 3, 1, 2, 3, 4, 5, 6, 7, 8, 9,$FD, 0; 0 ; DATA XREF: h+6FC4?o |
− | byte_1DF4F: dc.b 1, $A, $B, $C, $D, $E, $F,$10,$FF; 0 ; DATA XREF: h+ | + | byte_1DF4F: dc.b 1, $A, $B, $C, $D, $E, $F,$10,$FF; 0 ; DATA XREF: h+6FC4?o |
− | byte_1DF58: dc.b 3,$11,$12,$13,$14,$FC; 0 ; DATA XREF: h+ | + | byte_1DF58: dc.b 3,$11,$12,$13,$14,$FC; 0 ; DATA XREF: h+6FC4?o |
; ------------------------------------------------------------------------------- | ; ------------------------------------------------------------------------------- | ||
; Unknown Sprite Mappings | ; Unknown Sprite Mappings | ||
Line 697: | Line 681: | ||
word_1E0EC: dc 1 | word_1E0EC: dc 1 | ||
dc $F0BA | dc $F0BA | ||
− | even</ | + | even</syntaxhighlight> |
Now, save and try to build... oops! It seems that there's a routine missing: DMA_68KtoVRAM.<br> | Now, save and try to build... oops! It seems that there's a routine missing: DMA_68KtoVRAM.<br> | ||
Line 705: | Line 689: | ||
...easier than porting from the Sonic 2 Final disassembly, so let's use that). | ...easier than porting from the Sonic 2 Final disassembly, so let's use that). | ||
− | <asm>DMA_68KtoVRAM: ; CODE XREF: LoadSonicDynPLC+ | + | <syntaxhighlight lang="asm">DMA_68KtoVRAM: ; CODE XREF: LoadSonicDynPLC+48?p |
− | ; LoadTailsDynPLC+ | + | ; LoadTailsDynPLC+48?p ... |
movea.l ($FFFFDCFC).w,a1 | movea.l ($FFFFDCFC).w,a1 | ||
cmpa.w #$DCFC,a1 | cmpa.w #$DCFC,a1 | ||
Line 740: | Line 724: | ||
move.w #0,(a1) | move.w #0,(a1) | ||
− | DMA_68KtoVRAM_NoDMA: ; CODE XREF: DMA_68KtoVRAM+ | + | DMA_68KtoVRAM_NoDMA: ; CODE XREF: DMA_68KtoVRAM+8?j |
− | ; DMA_68KtoVRAM+ | + | ; DMA_68KtoVRAM+56?j |
rts | rts | ||
; End of function DMA_68KtoVRAM | ; End of function DMA_68KtoVRAM | ||
Line 749: | Line 733: | ||
− | Process_DMA: ; CODE XREF: ROM: | + | Process_DMA: ; CODE XREF: ROM:00000D9C?p |
− | ; ROM: | + | ; ROM:00000E84?p ... |
lea ($C00004).l,a5 | lea ($C00004).l,a5 | ||
lea ($FFFFDC00).w,a1 | lea ($FFFFDC00).w,a1 | ||
− | Process_DMA_Loop: ; CODE XREF: Process_DMA+ | + | Process_DMA_Loop: ; CODE XREF: Process_DMA+20?j |
move.w (a1)+,d0 | move.w (a1)+,d0 | ||
beq.s Process_DMA_End | beq.s Process_DMA_End | ||
Line 767: | Line 751: | ||
bne.s Process_DMA_Loop | bne.s Process_DMA_Loop | ||
− | Process_DMA_End: ; CODE XREF: Process_DMA+ | + | Process_DMA_End: ; CODE XREF: Process_DMA+C?j |
move.w #0,($FFFFDC00).w | move.w #0,($FFFFDC00).w | ||
move.l #$FFFFDC00,($FFFFDCFC).w | move.l #$FFFFDC00,($FFFFDCFC).w | ||
rts | rts | ||
− | ; End of function Process_DMA</ | + | ; End of function Process_DMA</syntaxhighlight> |
Line 793: | Line 777: | ||
Here's the routine after this conversion has been done: | Here's the routine after this conversion has been done: | ||
− | <asm>DMA_68KtoVRAM: ; CODE XREF: LoadSonicDynPLC+ | + | <syntaxhighlight lang="asm">DMA_68KtoVRAM: ; CODE XREF: LoadSonicDynPLC+48?p |
− | ; LoadTailsDynPLC+ | + | ; LoadTailsDynPLC+48?p ... |
movea.l ($FFFFD3EE).w,a1 | movea.l ($FFFFD3EE).w,a1 | ||
cmpa.w #$D3EE,a1 | cmpa.w #$D3EE,a1 | ||
Line 828: | Line 812: | ||
move.w #0,(a1) | move.w #0,(a1) | ||
− | DMA_68KtoVRAM_NoDMA: ; CODE XREF: DMA_68KtoVRAM+ | + | DMA_68KtoVRAM_NoDMA: ; CODE XREF: DMA_68KtoVRAM+8?j |
− | ; DMA_68KtoVRAM+ | + | ; DMA_68KtoVRAM+56?j |
rts | rts | ||
; End of function DMA_68KtoVRAM | ; End of function DMA_68KtoVRAM | ||
Line 837: | Line 821: | ||
− | Process_DMA: ; CODE XREF: ROM: | + | Process_DMA: ; CODE XREF: ROM:00000D9C?p |
− | ; ROM: | + | ; ROM:00000E84?p ... |
lea ($C00004).l,a5 | lea ($C00004).l,a5 | ||
lea ($FFFFD3C2).w,a1 | lea ($FFFFD3C2).w,a1 | ||
− | Process_DMA_Loop: ; CODE XREF: Process_DMA+ | + | Process_DMA_Loop: ; CODE XREF: Process_DMA+20?j |
move.w (a1)+,d0 | move.w (a1)+,d0 | ||
beq.s Process_DMA_End | beq.s Process_DMA_End | ||
Line 855: | Line 839: | ||
bne.s Process_DMA_Loop | bne.s Process_DMA_Loop | ||
− | Process_DMA_End: ; CODE XREF: Process_DMA+ | + | Process_DMA_End: ; CODE XREF: Process_DMA+C?j |
move.w #0,($FFFFD3C2).w | move.w #0,($FFFFD3C2).w | ||
move.l #$FFFFD3C2,($FFFFD3EE).w | move.l #$FFFFD3C2,($FFFFD3EE).w | ||
rts | rts | ||
− | ; End of function Process_DMA</ | + | ; End of function Process_DMA</syntaxhighlight> |
Paste it right after ShowVDPGraphics.<br> | Paste it right after ShowVDPGraphics.<br> | ||
Line 865: | Line 849: | ||
To do that, go to loc_D50, and add these two lines at the beginning: | To do that, go to loc_D50, and add these two lines at the beginning: | ||
− | <asm> move #$83,($FFFFF640).w | + | <syntaxhighlight lang="asm"> move #$83,($FFFFF640).w |
− | jsr Process_DMA</ | + | jsr Process_DMA</syntaxhighlight> |
The queue should now be working. Now we need to do one more thing, call the actual Spin Dash dust object. <br> | The queue should now be working. Now we need to do one more thing, call the actual Spin Dash dust object. <br> | ||
− | To do that, open _inc\Object pointers.asm, and replace the first call to ObjectFall in the second line with | + | To do that, open _inc\Object pointers.asm, and replace the first call to ObjectFall in the second line with Spin Dash_dust. |
The outcome should be something like this: | The outcome should be something like this: | ||
− | <asm>; --------------------------------------------------------------------------- | + | <syntaxhighlight lang="asm">; --------------------------------------------------------------------------- |
; Object pointers | ; Object pointers | ||
; --------------------------------------------------------------------------- | ; --------------------------------------------------------------------------- | ||
dc.l Obj01, ObjectFall, ObjectFall, ObjectFall | dc.l Obj01, ObjectFall, ObjectFall, ObjectFall | ||
− | dc.l | + | dc.l SpinDash_dust, ObjectFall, ObjectFall, Obj08 |
dc.l Obj09, Obj0A, Obj0B, Obj0C | dc.l Obj09, Obj0A, Obj0B, Obj0C | ||
dc.l Obj0D, Obj0E, Obj0F, Obj10 | dc.l Obj0D, Obj0E, Obj0F, Obj10 | ||
Line 910: | Line 894: | ||
dc.l Obj81, Obj82, Obj83, Obj84 | dc.l Obj81, Obj82, Obj83, Obj84 | ||
dc.l Obj85, Obj86, Obj87, Obj88 | dc.l Obj85, Obj86, Obj87, Obj88 | ||
− | dc.l Obj89, Obj8A, Obj8B, Obj8C</ | + | dc.l Obj89, Obj8A, Obj8B, Obj8C</syntaxhighlight> |
This will cause Object ID 05, which was previously unused, to now point to the Spin Dash dust object. | This will cause Object ID 05, which was previously unused, to now point to the Spin Dash dust object. | ||
Line 917: | Line 901: | ||
add the following line: | add the following line: | ||
− | <asm> move.b #5,$FFFFD1C0.w</ | + | <syntaxhighlight lang="asm"> move.b #5,$FFFFD1C0.w</syntaxhighlight> |
This will load the dust object to address $FFFFD1C0, (addresses from $D000 to $D400 are part of the SST). | This will load the dust object to address $FFFFD1C0, (addresses from $D000 to $D400 are part of the SST). | ||
− | Finally, we need to have the Spin Dash routine set the dust's animation. To do that, go to | + | Finally, we need to have the Spin Dash routine set the dust's animation. To do that, go to Sonic_SpinDash, and change this line: |
− | <asm> move.b #2,($FFFFD11C).w</ | + | <syntaxhighlight lang="asm"> move.b #2,($FFFFD11C).w</syntaxhighlight> |
to this: | to this: | ||
− | <asm> move.b #2,($FFFFD1DC).w ; Set the Spin Dash dust animation to $2.</ | + | <syntaxhighlight lang="asm"> move.b #2,($FFFFD1DC).w ; Set the Spin Dash dust animation to $2.</syntaxhighlight> |
Also, remove this line: | Also, remove this line: | ||
− | <asm> bcs.s loc2_1AC84 ; ??? branch if carry</ | + | <syntaxhighlight lang="asm"> bcs.s loc2_1AC84 ; ??? branch if carry</syntaxhighlight> |
Line 939: | Line 923: | ||
And again, in loc2_1ACF4, change this line: | And again, in loc2_1ACF4, change this line: | ||
− | <asm> move.b #0,($FFFFD11C).w ; clear $D11C (unused?)</ | + | <syntaxhighlight lang="asm"> move.b #0,($FFFFD11C).w ; clear $D11C (unused?)</syntaxhighlight> |
to this: | to this: | ||
− | <asm> move.b #0,($FFFFD1DC).w ; clear Spin Dash dust animation.</ | + | <syntaxhighlight lang="asm"> move.b #0,($FFFFD1DC).w ; clear Spin Dash dust animation.</syntaxhighlight> |
And finally, add the following line in loc2_1AD48, right before the jsr to PlaySound_Special: | And finally, add the following line in loc2_1AD48, right before the jsr to PlaySound_Special: | ||
− | <asm> move.b #2,$FFFFD1DC.w ; Set the Spin Dash dust animation to $2.</ | + | <syntaxhighlight lang="asm"> move.b #2,$FFFFD1DC.w ; Set the Spin Dash dust animation to $2.</syntaxhighlight> |
− | For reference, here's the complete | + | For reference, here's the complete Sonic_SpinDash routine: |
− | <asm> | + | <syntaxhighlight lang="asm">Sonic_SpinDash: |
tst.b $39(a0) ; already Spin Dashing? | tst.b $39(a0) ; already Spin Dashing? | ||
bne.s loc2_1AC8E ; if set, branch | bne.s loc2_1AC8E ; if set, branch | ||
Line 1,073: | Line 1,057: | ||
bsr.w Sonic_AnglePos | bsr.w Sonic_AnglePos | ||
move.w #$60,($FFFFF73E).w ; reset looking up/down | move.w #$60,($FFFFF73E).w ; reset looking up/down | ||
− | rts</ | + | rts</syntaxhighlight> |
Line 1,081: | Line 1,065: | ||
and at the end of the ROM, after SegaPCM, add the following lines: | and at the end of the ROM, after SegaPCM, add the following lines: | ||
− | <asm>Art_Dust incbin artunc\spindust.bin</ | + | <syntaxhighlight lang="asm">Art_Dust incbin artunc\spindust.bin</syntaxhighlight> |
Line 1,099: | Line 1,083: | ||
So open the file _inc\Pattern load cues.asm, and search for these lines: | So open the file _inc\Pattern load cues.asm, and search for these lines: | ||
− | <asm>PLC_Main: dc.w 4 | + | <syntaxhighlight lang="asm">PLC_Main: dc.w 4 |
dc.l Nem_Lamp ; lamppost | dc.l Nem_Lamp ; lamppost | ||
− | dc.w $F400</ | + | dc.w $F400</syntaxhighlight> |
Change them to: | Change them to: | ||
− | <asm>PLC_Main: dc.w 4 | + | <syntaxhighlight lang="asm">PLC_Main: dc.w 4 |
dc.l Nem_Lamp ; lamppost | dc.l Nem_Lamp ; lamppost | ||
− | dc.w $D800</ | + | dc.w $D800</syntaxhighlight> |
This will load the lamppost art in $D800 in VRAM, instead of $F400. <br> | This will load the lamppost art in $D800 in VRAM, instead of $F400. <br> | ||
Line 1,113: | Line 1,097: | ||
So now go to Obj79 (lamppost object) and search for this line, which should be in Obj79_Main: | So now go to Obj79 (lamppost object) and search for this line, which should be in Obj79_Main: | ||
− | <asm> move.w #$7A0,2(a0)</ | + | <syntaxhighlight lang="asm"> move.w #$7A0,2(a0)</syntaxhighlight> |
Change it to: | Change it to: | ||
− | <asm> move.w #($D800/$20),2(a0)</ | + | <syntaxhighlight lang="asm"> move.w #($D800/$20),2(a0)</syntaxhighlight> |
The Obj79_Main routine should then look like this: | The Obj79_Main routine should then look like this: | ||
− | <asm>Obj79_Main: ; XREF: Obj79_Index | + | <syntaxhighlight lang="asm">Obj79_Main: ; XREF: Obj79_Index |
addq.b #2,$24(a0) | addq.b #2,$24(a0) | ||
move.l #Map_obj79,4(a0) | move.l #Map_obj79,4(a0) | ||
Line 1,139: | Line 1,123: | ||
andi.b #$7F,d2 | andi.b #$7F,d2 | ||
cmp.b d2,d1 ; is lamppost number higher than the number hit? | cmp.b d2,d1 ; is lamppost number higher than the number hit? | ||
− | bcs.s Obj79_BlueLamp ; if yes, branch</ | + | bcs.s Obj79_BlueLamp ; if yes, branch</syntaxhighlight> |
Similarly, change this line in Obj79_HitLamp: | Similarly, change this line in Obj79_HitLamp: | ||
− | <asm> move.w #$7A0,2(a1)</ | + | <syntaxhighlight lang="asm"> move.w #$7A0,2(a1)</syntaxhighlight> |
to this: | to this: | ||
− | <asm> move.w #($D800/$20),2(a1)</ | + | <syntaxhighlight lang="asm"> move.w #($D800/$20),2(a1)</syntaxhighlight> |
Therefore making Obj79_HitLamp look like this: | Therefore making Obj79_HitLamp look like this: | ||
− | <asm>Obj79_HitLamp: | + | <syntaxhighlight lang="asm">Obj79_HitLamp: |
move.w ($FFFFD008).w,d0 | move.w ($FFFFD008).w,d0 | ||
Line 1,179: | Line 1,163: | ||
move.b #4,$18(a1) | move.b #4,$18(a1) | ||
move.b #2,$1A(a1) | move.b #2,$1A(a1) | ||
− | move.w #$20,$36(a1)</ | + | move.w #$20,$36(a1)</syntaxhighlight> |
...and that's it. Compile your ROM and we should now have perfect Spin Dash in Sonic 1. | ...and that's it. Compile your ROM and we should now have perfect Spin Dash in Sonic 1. | ||
Line 1,187: | Line 1,171: | ||
[[Media:Spindash8_Final.zip|Here]]'s the final result of this thing. Source code available [[Media:SpindashGuide_EndSource.rar|here.]] Have fun :) | [[Media:Spindash8_Final.zip|Here]]'s the final result of this thing. Source code available [[Media:SpindashGuide_EndSource.rar|here.]] Have fun :) | ||
− | Note that you should look [[SCHG_How-to:Fix the SEGA Sound|here]] for instructions on fixing the SEGA sound, and see [[SCHG How-to:Add | + | Note that you should look [[SCHG_How-to:Fix the SEGA Sound|here]] for instructions on fixing the SEGA sound, and see [[SCHG How-to:Add Spin Dash to Sonic 1/Part 3|Part 3]] for some additional fixes not covered in this guide. |
− | + | {{S1Howtos}} | |
+ | |Add Spin Dash to Sonic 1/Part 2]] |
Latest revision as of 17:43, 20 November 2023
(Original guide by Puto)
Here's a Sonic 1 + Spin Dash ROM built by following Lightning's guide.
If you try it, you will be able to easily see that there are several things that keep it from being a perfect port.
For example, if you Spin Dash next to a monitor, you won't break it.
Also, the sound effect, while fitting, isn't ideal, there's no dust, and if you Spin Dash while on a seesaw (SLZ), Strange Things™
will happen. So let's fix this.
Contents
Problem 1: The Monitor Bug
When you try to Spin Dash next to a monitor, you won't be able to do that and simply stop when releasing. It only works if you walk a little bit away from the monitor and do the Spin Dash. To fix that, go to loc_A1EC (Hint for GitHub Disassembly users: Look for file called _incObj/26 Monitor.asm). You should see something like this:
loc_A1EC: ; XREF: Obj26_Solid
move.w #$1A,d1
move.w #$F,d2
bsr.w Obj26_SolidSides
beq.w loc_A25C
tst.w $12(a1)
bmi.s loc_A20A
cmpi.b #2,$1C(a1) ; is Sonic rolling?
beq.s loc_A25C ; if yes, branch
By reading this code, you can see that there is a check for the rolling animation there. This is what causes the monitor bug. To fix it, simply add a second check for the Spin Dash animation after this one:
loc_A1EC: ; XREF: Obj26_Solid
move.w #$1A,d1
move.w #$F,d2
bsr.w Obj26_SolidSides
beq.w loc_A25C
tst.w $12(a1)
bmi.s loc_A20A
cmpi.b #2,$1C(a1) ; is Sonic rolling?
beq.s loc_A25C ; if yes, branch
cmpi.b #$1F,$1C(a1) ; is Sonic spin-dashing?
beq.s loc_A25C ; if yes, branch
Great, let's try it. It should be working and... oh dear.
This isn't right.
We're Spin Dashing, we're not supposed to use the pushing animation. To fix that, go to Sonic_SpinDash, and look at what it does.
These are the first lines:
Sonic_SpinDash:
tst.b $39(a0) ; already Spin Dashing?
bne.s loc2_1AC8E ; if set, branch
Since we know we're Spin Dashing when $39(a0) is set, let's make sure that the Spin Dash animation is used when that happens.
Therefore, go to loc2_1AC8E, and right after the label, add the following line:
move.b #$1F,$1C(a0)
Build the ROM again, and the monitor bug should be permanently taken care of. Here's a ROM after this step was carried out.
Problem 2: Spin Dash Sound Effect
Adding a sound effect to Sonic 1 isn't the most trivial of things to do. So be sure to follow these steps exactly.
First, we need a free slot. Since all the slots reserved for sound effects in Sonic 1 are taken, we therefore need to reserve extra slots for sound effects. The $D1-$DF area is free, so let's use that. Go to Sound_ChkValue, and you should be able to see this line there:
cmpi.b #$E0,d7
bcs.w Sound_D0toDF ; sound $D0-$DF
Since only $D0 is actually used here, it's a rather large waste to keep the rest of the slots unused, so change that $E0 to $D1:
cmpi.b #$D1,d7
bcs.w Sound_D0toDF ; sound $D0
And after that line, let's add our own comparison:
cmpi.b #$DF,d7
blo.w Sound_D1toDF ; sound $D1-$DF
Now, we have to actually create this routine. So go to Sound_A0toCF, and see what it does:
Sound_A0toCF: ; XREF: Sound_ChkValue
tst.b $27(a6)
bne.w loc_722C6
tst.b 4(a6)
bne.w loc_722C6
tst.b $24(a6)
bne.w loc_722C6
cmpi.b #$B5,d7 ; is ring sound effect played?
bne.s Sound_notB5 ; if not, branch
tst.b $2B(a6)
bne.s loc_721EE
move.b #$CE,d7 ; play ring sound in left speaker
loc_721EE:
bchg #0,$2B(a6) ; change speaker
Sound_notB5:
cmpi.b #$A7,d7 ; is "pushing" sound played?
bne.s Sound_notA7 ; if not, branch
tst.b $2C(a6)
bne.w locret_722C4
move.b #$80,$2C(a6)
Sound_notA7:
movea.l (Go_SoundIndex).l,a0
subi.b #$A0,d7
lsl.w #2,d7
movea.l (a0,d7.w),a3
movea.l a3,a1
moveq #0,d1
move.w (a1)+,d1
add.l a3,d1
move.b (a1)+,d5
move.b (a1)+,d7
subq.b #1,d7
moveq #$30,d6
(...)
As you can see, this routine has a lot of checks for special sound effects, like the ring effect and the pushing effect.
We don't need that for our new routine, so trash all of that, and you end up with:
Sound_D1toDF:
tst.b $27(a6)
bne.w loc_722C6
tst.b 4(a6)
bne.w loc_722C6
tst.b $24(a6)
bne.w loc_722C6
movea.l (Go_SoundIndex).l,a0
subi.b #$A0,d7
lsl.w #2,d7
movea.l (a0,d7.w),a3
movea.l a3,a1
moveq #0,d1
move.w (a1)+,d1
add.l a3,d1
move.b (a1)+,d5
move.b (a1)+,d7
subq.b #1,d7
moveq #$30,d6
(...)
There's a problem here: If we subtract $A0 to get the current index, then the next entry in this index will be sound $D0, which is handled by a different routine, forcing us to have an unused entry in an index. So, change that line to subtract $A1 instead:
Sound_D1toDF:
tst.b $27(a6)
bne.w loc_722C6
tst.b 4(a6)
bne.w loc_722C6
tst.b $24(a6)
bne.w loc_722C6
movea.l (Go_SoundIndex).l,a0
subi.b #$A1,d7
lsl.w #2,d7
movea.l (a0,d7.w),a3
movea.l a3,a1
moveq #0,d1
move.w (a1)+,d1
add.l a3,d1
move.b (a1)+,d5
move.b (a1)+,d7
subq.b #1,d7
moveq #$30,d6
(...)
Also, it can be noted that any code after the $A1 line is common to the two routines.
So in the original routine, add a label right before "lsl.w #2,d7":
Sound_notA7:
movea.l (Go_SoundIndex).l,a0
subi.b #$A0,d7
SoundEffects_Common:
lsl.w #2,d7
movea.l (a0,d7.w),a3
movea.l a3,a1
moveq #0,d1
move.w (a1)+,d1
add.l a3,d1
move.b (a1)+,d5
move.b (a1)+,d7
subq.b #1,d7
moveq #$30,d6
(...)
And we can now greatly reduce the size of our new routine:
Sound_D1toDF:
tst.b $27(a6)
bne.w loc_722C6
tst.b 4(a6)
bne.w loc_722C6
tst.b $24(a6)
bne.w loc_722C6
movea.l (Go_SoundIndex).l,a0
sub.b #$A1,d7
bra SoundEffects_Common
So, after this is done, put the routine right above Sound_A0toCF.
Now, all we need is the actual ported spin-dash sound effect, which I provide here.
Put this file in the sound\ directory of your source code, and go to the SoundD0 label. After the even, add the following lines:
SoundD1: incbin sound\soundD1.bin
even
So the surrounding code should look like this:
SoundCF: incbin sound\soundCF.bin
even
SoundD0: incbin sound\soundD0.bin
even
SoundD1: incbin sound\soundD1.bin
even
SegaPCM: incbin sound\segapcm.bin
even
And finally, add SoundD1 to the index:
SoundIndex: dc.l SoundA0, SoundA1, SoundA2
dc.l SoundA3, SoundA4, SoundA5
dc.l SoundA6, SoundA7, SoundA8
dc.l SoundA9, SoundAA, SoundAB
dc.l SoundAC, SoundAD, SoundAE
dc.l SoundAF, SoundB0, SoundB1
dc.l SoundB2, SoundB3, SoundB4
dc.l SoundB5, SoundB6, SoundB7
dc.l SoundB8, SoundB9, SoundBA
dc.l SoundBB, SoundBC, SoundBD
dc.l SoundBE, SoundBF, SoundC0
dc.l SoundC1, SoundC2, SoundC3
dc.l SoundC4, SoundC5, SoundC6
dc.l SoundC7, SoundC8, SoundC9
dc.l SoundCA, SoundCB, SoundCC
dc.l SoundCD, SoundCE, SoundCF
dc.l SoundD1
Now, go to Sonic_SpinDash, and change all references to sound $BE to reference sound $D1 instead. For reference, here's the complete routine:
Sonic_SpinDash:
tst.b $39(a0) ; already Spin Dashing?
bne.s loc2_1AC8E ; if set, branch
cmpi.b #8,$1C(a0) ; is anim duck
bne.s locret2_1AC8C ; if not, return
move.b ($FFFFF603).w,d0 ; read controller
andi.b #$70,d0 ; pressing A/B/C ?
beq.w locret2_1AC8C ; if not, return
move.b #$1F,$1C(a0) ; set Spin Dash anim (9 in s2)
move.w #$D1,d0 ; spin sound ($E0 in s2)
jsr (PlaySound_Special).l ; play spin sound
addq.l #4,sp ; increment stack ptr
move.b #1,$39(a0) ; set Spin Dash flag
move.w #0,$3A(a0) ; set charge count to 0
cmpi.b #$C,$28(a0) ; ??? oxygen remaining?
bcs.s loc2_1AC84 ; ??? branch if carry
move.b #2,($FFFFD11C).w ; ??? $D11C only seems
; to be used in Spin Dash
loc2_1AC84:
bsr.w Sonic_LevelBound
bsr.w Sonic_AnglePos
locret2_1AC8C:
rts
; ---------------------------------------------------------------------------
loc2_1AC8E:
move.b #$1F,$1C(a0)
move.b ($FFFFF602).w,d0 ; read controller
btst #1,d0 ; check down button
bne.w loc2_1AD30 ; if set, branch
move.b #$E,$16(a0) ; $16(a0) is height/2
move.b #7,$17(a0) ; $17(a0) is width/2
move.b #2,$1C(a0) ; set animation to roll
addq.w #5,$C(a0) ; $C(a0) is Y coordinate
move.b #0,$39(a0) ; clear Spin Dash flag
moveq #0,d0
move.b $3A(a0),d0 ; copy charge count
add.w d0,d0 ; double it
move.w Dash_Speeds(pc,d0.w),$14(a0) ; get normal speed
move.w $14(a0),d0 ; get inertia
subi.w #$800,d0 ; subtract $800
add.w d0,d0 ; double it
andi.w #$1F00,d0 ; mask it against $1F00
neg.w d0 ; negate it
addi.w #$2000,d0 ; add $2000
move.w d0,($FFFFEED0).w ; move to $EED0
btst #0,$22(a0) ; is sonic facing right?
beq.s loc2_1ACF4 ; if not, branch
neg.w $14(a0) ; negate inertia
loc2_1ACF4:
bset #2,$22(a0) ; set unused (in s1) flag
move.b #0,($FFFFD11C).w ; clear $D11C (unused?)
move.w #$BC,d0 ; spin release sound
jsr (PlaySound_Special).l ; play it!
bra.s loc2_1AD78
; ===========================================================================
Dash_Speeds: dc.w $800 ; 0
dc.w $880 ; 1
dc.w $900 ; 2
dc.w $980 ; 3
dc.w $A00 ; 4
dc.w $A80 ; 5
dc.w $B00 ; 6
dc.w $B80 ; 7
dc.w $C00 ; 8
; ===========================================================================
loc2_1AD30: ; If still charging the dash...
tst.w $3A(a0) ; check charge count
beq.s loc2_1AD48 ; if zero, branch
move.w $3A(a0),d0 ; otherwise put it in d0
lsr.w #5,d0 ; shift right 5 (divide it by 32)
sub.w d0,$3A(a0) ; subtract from charge count
bcc.s loc2_1AD48 ; ??? branch if carry clear
move.w #0,$3A(a0) ; set charge count to 0
loc2_1AD48:
move.b ($FFFFF603).w,d0 ; read controller
andi.b #$70,d0 ; pressing A/B/C?
beq.w loc2_1AD78 ; if not, branch
move.w #$1F00,$1C(a0) ; reset spdsh animation
move.w #$D1,d0 ; was $E0 in sonic 2
jsr (PlaySound_Special).l ; play charge sound
addi.w #$200,$3A(a0) ; increase charge count
cmpi.w #$800,$3A(a0) ; check if it's maxed
bcs.s loc2_1AD78 ; if not, then branch
move.w #$800,$3A(a0) ; reset it to max
loc2_1AD78:
addq.l #4,sp ; increase stack ptr
cmpi.w #$60,($FFFFEED8).w ; $EED8 only ever seems
beq.s loc2_1AD8C ; to be used in Spin Dash
bcc.s loc2_1AD88
addq.w #4,($FFFFEED8).w
loc2_1AD88:
subq.w #2,($FFFFEED8).w
loc2_1AD8C:
bsr.w Sonic_LevelBound
bsr.w Sonic_AnglePos
move.w #$60,($FFFFF73E).w ; reset looking up/down
rts
So, problem solved. Here's a rom of the output after fixing this problem.
Problem 3: See-Saw Bug (AKA Sonic 2 Spin Dash Bug)
This problem occurs when you're Spin Dashing on a seesaw, and then you get thrown up by the seesaw.
You keep your Spin Dash state, and dash off immediately once you land. This is clearly not the wanted behaviour.
To fix this, go to Obj01_MdJump and Obj01_MdJump2, and add the following line at the beginning of each of the two routines:
clr.b $39(a0)
This should fix the see-saw bug. Here's the rom.
Problem 4: Spin Dash Dust
This is a big one. For starters, you need to port the Spin Dash dust object from Sonic 2.
For your convenience, here is the code to the ported object, paste it right before Obj01 (the sonic object):
SpinDash_dust:
Sprite_1DD20: ; DATA XREF: ROM:0001600C?o
moveq #0,d0
move.b $24(a0),d0
move off_1DD2E(pc,d0.w),d1
jmp off_1DD2E(pc,d1.w)
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
off_1DD2E: dc loc_1DD36-off_1DD2E; 0 ; DATA XREF: h+6DBA?o h+6DBC?o ...
dc loc_1DD90-off_1DD2E; 1
dc loc_1DE46-off_1DD2E; 2
dc loc_1DE4A-off_1DD2E; 3
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DD36: ; DATA XREF: h+6DBA?o
addq.b #2,$24(a0)
move.l #MapUnc_1DF5E,4(a0)
or.b #4,1(a0)
move.b #1,$18(a0)
move.b #$10,$19(a0)
move #$7A0,2(a0)
move #-$3000,$3E(a0)
move #$F400,$3C(a0)
cmp #-$2E40,a0
beq.s loc_1DD8C
move.b #1,$34(a0)
; cmp #2,($FFFFFF70).w
; beq.s loc_1DD8C
; move #$48C,2(a0)
; move #-$4FC0,$3E(a0)
; move #-$6E80,$3C(a0)
loc_1DD8C: ; CODE XREF: h+6DF6?j h+6E04?j
; bsr.w sub_16D6E
loc_1DD90: ; DATA XREF: h+6DBA?o
movea.w $3E(a0),a2
moveq #0,d0
move.b $1C(a0),d0
add d0,d0
move off_1DDA4(pc,d0.w),d1
jmp off_1DDA4(pc,d1.w)
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
off_1DDA4: dc loc_1DE28-off_1DDA4; 0 ; DATA XREF: h+6E30?o h+6E32?o ...
dc loc_1DDAC-off_1DDA4; 1
dc loc_1DDCC-off_1DDA4; 2
dc loc_1DE20-off_1DDA4; 3
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DDAC: ; DATA XREF: h+6E30?o
move ($FFFFF646).w,$C(a0)
tst.b $1D(a0)
bne.s loc_1DE28
move 8(a2),8(a0)
move.b #0,$22(a0)
and #$7FFF,2(a0)
bra.s loc_1DE28
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DDCC: ; DATA XREF: h+6E30?o
; cmp.b #$C,$28(a2)
; bcs.s loc_1DE3E
cmp.b #4,$24(a2)
bcc.s loc_1DE3E
tst.b $39(a2)
beq.s loc_1DE3E
move 8(a2),8(a0)
move $C(a2),$C(a0)
move.b $22(a2),$22(a0)
and.b #1,$22(a0)
tst.b $34(a0)
beq.s loc_1DE06
sub #4,$C(a0)
loc_1DE06: ; CODE XREF: h+6E8A?j
tst.b $1D(a0)
bne.s loc_1DE28
and #$7FFF,2(a0)
tst 2(a2)
bpl.s loc_1DE28
or #-$8000,2(a0)
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DE20: ; DATA XREF: h+6E30?o
loc_1DE28: ; CODE XREF: h+6E42?j h+6E56?j ...
lea (off_1DF38).l,a1
jsr AnimateSprite
bsr.w loc_1DEE4
jmp DisplaySprite
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DE3E: ; CODE XREF: h+6E5E?j h+6E66?j ...
move.b #0,$1C(a0)
rts
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DE46: ; DATA XREF: h+6DBA?o
bra.w DeleteObject
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DE4A:
movea.w $3E(a0),a2
moveq #$10,d1
cmp.b #$D,$1C(a2)
beq.s loc_1DE64
moveq #$6,d1
cmp.b #$3,$21(a2)
beq.s loc_1DE64
move.b #2,$24(a0)
move.b #0,$32(a0)
rts
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DE64: ; CODE XREF: h+6EE0?j
subq.b #1,$32(a0)
bpl.s loc_1DEE0
move.b #3,$32(a0)
jsr SingleObjLoad
bne.s loc_1DEE0
move.b 0(a0),0(a1)
move 8(a2),8(a1)
move $C(a2),$C(a1)
tst.b $34(a0)
beq.s loc_1DE9A
sub #4,d1
loc_1DE9A: ; CODE XREF: h+6F1E?j
add d1,$C(a1)
move.b #0,$22(a1)
move.b #3,$1C(a1)
addq.b #2,$24(a1)
move.l 4(a0),4(a1)
move.b 1(a0),1(a1)
move.b #1,$18(a1)
move.b #4,$19(a1)
move 2(a0),2(a1)
move $3E(a0),$3E(a1)
and #$7FFF,2(a1)
tst 2(a2)
bpl.s loc_1DEE0
or #-$8000,2(a1)
loc_1DEE0: ; CODE XREF: h+6EF4?j h+6F00?j ...
bsr.s loc_1DEE4
rts
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc_1DEE4: ; CODE XREF: h+6EC0?p h+6F6C?p
moveq #0,d0
move.b $1A(a0),d0
cmp.b $30(a0),d0
beq.w locret_1DF36
move.b d0,$30(a0)
lea (off_1E074).l,a2
add d0,d0
add (a2,d0.w),a2
move (a2)+,d5
subq #1,d5
bmi.w locret_1DF36
move $3C(a0),d4
loc_1DF0A: ; CODE XREF: h+6FBE?j
moveq #0,d1
move (a2)+,d1
move d1,d3
lsr.w #8,d3
and #$F0,d3 ; 'ð'
add #$10,d3
and #$FFF,d1
lsl.l #5,d1
add.l #Art_Dust,d1
move d4,d2
add d3,d4
add d3,d4
jsr (DMA_68KtoVRAM).l
dbf d5,loc_1DF0A
rts
locret_1DF36: ; CODE XREF: h+6F7A?j h+6F90?j
rts
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
off_1DF38: dc byte_1DF40-off_1DF38; 0 ; DATA XREF: h+6EB4?o h+6FC4?o ...
dc byte_1DF43-off_1DF38; 1
dc byte_1DF4F-off_1DF38; 2
dc byte_1DF58-off_1DF38; 3
byte_1DF40: dc.b $1F, 0,$FF ; 0 ; DATA XREF: h+6FC4?o
byte_1DF43: dc.b 3, 1, 2, 3, 4, 5, 6, 7, 8, 9,$FD, 0; 0 ; DATA XREF: h+6FC4?o
byte_1DF4F: dc.b 1, $A, $B, $C, $D, $E, $F,$10,$FF; 0 ; DATA XREF: h+6FC4?o
byte_1DF58: dc.b 3,$11,$12,$13,$14,$FC; 0 ; DATA XREF: h+6FC4?o
; -------------------------------------------------------------------------------
; Unknown Sprite Mappings
; -------------------------------------------------------------------------------
MapUnc_1DF5E:
dc word_1DF8A-MapUnc_1DF5E; 0
dc word_1DF8C-MapUnc_1DF5E; 1
dc word_1DF96-MapUnc_1DF5E; 2
dc word_1DFA0-MapUnc_1DF5E; 3
dc word_1DFAA-MapUnc_1DF5E; 4
dc word_1DFB4-MapUnc_1DF5E; 5
dc word_1DFBE-MapUnc_1DF5E; 6
dc word_1DFC8-MapUnc_1DF5E; 7
dc word_1DFD2-MapUnc_1DF5E; 8
dc word_1DFDC-MapUnc_1DF5E; 9
dc word_1DFE6-MapUnc_1DF5E; 10
dc word_1DFF0-MapUnc_1DF5E; 11
dc word_1DFFA-MapUnc_1DF5E; 12
dc word_1E004-MapUnc_1DF5E; 13
dc word_1E016-MapUnc_1DF5E; 14
dc word_1E028-MapUnc_1DF5E; 15
dc word_1E03A-MapUnc_1DF5E; 16
dc word_1E04C-MapUnc_1DF5E; 17
dc word_1E056-MapUnc_1DF5E; 18
dc word_1E060-MapUnc_1DF5E; 19
dc word_1E06A-MapUnc_1DF5E; 20
dc word_1DF8A-MapUnc_1DF5E; 21
word_1DF8A: dc.b 0
word_1DF8C: dc.b 1
dc.b $F2, $0D, $0, 0,$F0; 0
word_1DF96: dc.b 1
dc.b $E2, $0F, $0, 0,$F0; 0
word_1DFA0: dc.b 1
dc.b $E2, $0F, $0, 0,$F0; 0
word_1DFAA: dc.b 1
dc.b $E2, $0F, $0, 0,$F0; 0
word_1DFB4: dc.b 1
dc.b $E2, $0F, $0, 0,$F0; 0
word_1DFBE: dc.b 1
dc.b $E2, $0F, $0, 0,$F0; 0
word_1DFC8: dc.b 1
dc.b $F2, $0D, $0, 0,$F0; 0
word_1DFD2: dc.b 1
dc.b $F2, $0D, $0, 0,$F0; 0
word_1DFDC: dc.b 1
dc.b $F2, $0D, $0, 0,$F0; 0
word_1DFE6: dc.b 1
dc.b $4, $0D, $0, 0,$E0; 0
word_1DFF0: dc.b 1
dc.b $4, $0D, $0, 0,$E0; 0
word_1DFFA: dc.b 1
dc.b $4, $0D, $0, 0,$E0; 0
word_1E004: dc.b 2
dc.b $F4, $01, $0, 0,$E8; 0
dc.b $4, $0D, $0, 2,$E0; 4
word_1E016: dc.b 2
dc.b $F4, $05, $0, 0,$E8; 0
dc.b $4, $0D, $0, 4,$E0; 4
word_1E028: dc.b 2
dc.b $F4, $09, $0, 0,$E0; 0
dc.b $4, $0D, $0, 6,$E0; 4
word_1E03A: dc.b 2
dc.b $F4, $09, $0, 0,$E0; 0
dc.b $4, $0D, $0, 6,$E0; 4
word_1E04C: dc.b 1
dc.b $F8, $05, $0, 0,$F8; 0
word_1E056: dc.b 1
dc.b $F8, $05, $0, 4,$F8; 0
word_1E060: dc.b 1
dc.b $F8, $05, $0, 8,$F8; 0
word_1E06A: dc.b 1
dc.b $F8, $05, $0, $C,$F8; 0
dc.b 0
off_1E074: dc word_1E0A0-off_1E074; 0
dc word_1E0A2-off_1E074; 1
dc word_1E0A6-off_1E074; 2
dc word_1E0AA-off_1E074; 3
dc word_1E0AE-off_1E074; 4
dc word_1E0B2-off_1E074; 5
dc word_1E0B6-off_1E074; 6
dc word_1E0BA-off_1E074; 7
dc word_1E0BE-off_1E074; 8
dc word_1E0C2-off_1E074; 9
dc word_1E0C6-off_1E074; 10
dc word_1E0CA-off_1E074; 11
dc word_1E0CE-off_1E074; 12
dc word_1E0D2-off_1E074; 13
dc word_1E0D8-off_1E074; 14
dc word_1E0DE-off_1E074; 15
dc word_1E0E4-off_1E074; 16
dc word_1E0EA-off_1E074; 17
dc word_1E0EA-off_1E074; 18
dc word_1E0EA-off_1E074; 19
dc word_1E0EA-off_1E074; 20
dc word_1E0EC-off_1E074; 21
word_1E0A0: dc 0
word_1E0A2: dc 1
dc $7000
word_1E0A6: dc 1
dc $F008
word_1E0AA: dc 1
dc $F018
word_1E0AE: dc 1
dc $F028
word_1E0B2: dc 1
dc $F038
word_1E0B6: dc 1
dc $F048
word_1E0BA: dc 1
dc $7058
word_1E0BE: dc 1
dc $7060
word_1E0C2: dc 1
dc $7068
word_1E0C6: dc 1
dc $7070
word_1E0CA: dc 1
dc $7078
word_1E0CE: dc 1
dc $7080
word_1E0D2: dc 2
dc $1088
dc $708A
word_1E0D8: dc 2
dc $3092
dc $7096
word_1E0DE: dc 2
dc $509E
dc $70A4
word_1E0E4: dc 2
dc $50AC
dc $70B2
word_1E0EA: dc 0
word_1E0EC: dc 1
dc $F0BA
even
Now, save and try to build... oops! It seems that there's a routine missing: DMA_68KtoVRAM.
This routine handles the DMA queue present in Sonic 2, but that queue does not exist in Sonic 1.
This queue is used in Sonic 2 to handle the dynamic art for Sonic, Tails, and the dust.
Here's the code to the DMA queue routines in the Sonic 2 Nick Arcade prototype (porting it from the NA disassembly is somewhat...
...easier than porting from the Sonic 2 Final disassembly, so let's use that).
DMA_68KtoVRAM: ; CODE XREF: LoadSonicDynPLC+48?p
; LoadTailsDynPLC+48?p ...
movea.l ($FFFFDCFC).w,a1
cmpa.w #$DCFC,a1
beq.s DMA_68KtoVRAM_NoDMA
move.w #$9300,d0
move.b d3,d0
move.w d0,(a1)+
move.w #$9400,d0
lsr.w #8,d3
move.b d3,d0
move.w d0,(a1)+
move.w #$9500,d0
lsr.l #1,d1
move.b d1,d0
move.w d0,(a1)+
move.w #$9600,d0
lsr.l #8,d1
move.b d1,d0
move.w d0,(a1)+
move.w #$9700,d0
lsr.l #8,d1
move.b d1,d0
move.w d0,(a1)+
andi.l #$FFFF,d2
lsl.l #2,d2
lsr.w #2,d2
swap d2
ori.l #$40000080,d2
move.l d2,(a1)+
move.l a1,($FFFFDCFC).w
cmpa.w #$DCFC,a1
beq.s DMA_68KtoVRAM_NoDMA
move.w #0,(a1)
DMA_68KtoVRAM_NoDMA: ; CODE XREF: DMA_68KtoVRAM+8?j
; DMA_68KtoVRAM+56?j
rts
; End of function DMA_68KtoVRAM
; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ S U B R O U T I N E ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
Process_DMA: ; CODE XREF: ROM:00000D9C?p
; ROM:00000E84?p ...
lea ($C00004).l,a5
lea ($FFFFDC00).w,a1
Process_DMA_Loop: ; CODE XREF: Process_DMA+20?j
move.w (a1)+,d0
beq.s Process_DMA_End
move.w d0,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
cmpa.w #$DCFC,a1
bne.s Process_DMA_Loop
Process_DMA_End: ; CODE XREF: Process_DMA+C?j
move.w #0,($FFFFDC00).w
move.l #$FFFFDC00,($FFFFDCFC).w
rts
; End of function Process_DMA
Now, a brief explanation(sp?) of how I believe this routine works.
There's a queue that's $100 bytes long, that's stored at $DC00 in 68K RAM.
This queue is where the dynamically loaded art for Sonic, Tails, and the dust gets stored (you send the art to the queue by calling DMA_68KtoVRAM),
until it's processed and sent to VRAM, which is done when the Process_DMA routine is called.
To port this to Sonic 1, we need to find an appropriate RAM location to fit it, and call Process_DMA at the right location.
So, where can we find $100 free bytes of RAM in Sonic 1 to fit this queue?
...Oops, we can't. But let's look at this from another point of view.
In Sonic 2, this queue is used to store the art for both Sonic, Tails, AND the dust.
For that reason, it needs $100 bytes. But we only need it to store the dust, and for that, we only actually need $30 bytes.
So, is there any location in RAM where we can stick a $30 bytes-long queue? Yes. We can stick it inside an unused object's SST.
I used the RAM location starting at $FFFFD3C2, if you know any better location, feel free to use it instead.
So we now need to change this routine to point to this new RAM location, as well as reduce its size.
To do that, replace all references to $FFFFDC00 with $FFFFD3C2, and $FFFFDCFC with $FFFFD3EE
(and similarly, do the same for the "reduced" versions, $DC00->$D3C2, $DCFC->$D3EE).
Here's the routine after this conversion has been done:
DMA_68KtoVRAM: ; CODE XREF: LoadSonicDynPLC+48?p
; LoadTailsDynPLC+48?p ...
movea.l ($FFFFD3EE).w,a1
cmpa.w #$D3EE,a1
beq.s DMA_68KtoVRAM_NoDMA
move.w #$9300,d0
move.b d3,d0
move.w d0,(a1)+
move.w #$9400,d0
lsr.w #8,d3
move.b d3,d0
move.w d0,(a1)+
move.w #$9500,d0
lsr.l #1,d1
move.b d1,d0
move.w d0,(a1)+
move.w #$9600,d0
lsr.l #8,d1
move.b d1,d0
move.w d0,(a1)+
move.w #$9700,d0
lsr.l #8,d1
move.b d1,d0
move.w d0,(a1)+
andi.l #$FFFF,d2
lsl.l #2,d2
lsr.w #2,d2
swap d2
ori.l #$40000080,d2
move.l d2,(a1)+
move.l a1,($FFFFD3EE).w
cmpa.w #$D3EE,a1
beq.s DMA_68KtoVRAM_NoDMA
move.w #0,(a1)
DMA_68KtoVRAM_NoDMA: ; CODE XREF: DMA_68KtoVRAM+8?j
; DMA_68KtoVRAM+56?j
rts
; End of function DMA_68KtoVRAM
; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ S U B R O U T I N E ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
Process_DMA: ; CODE XREF: ROM:00000D9C?p
; ROM:00000E84?p ...
lea ($C00004).l,a5
lea ($FFFFD3C2).w,a1
Process_DMA_Loop: ; CODE XREF: Process_DMA+20?j
move.w (a1)+,d0
beq.s Process_DMA_End
move.w d0,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
move.w (a1)+,(a5)
cmpa.w #$D3EE,a1
bne.s Process_DMA_Loop
Process_DMA_End: ; CODE XREF: Process_DMA+C?j
move.w #0,($FFFFD3C2).w
move.l #$FFFFD3C2,($FFFFD3EE).w
rts
; End of function Process_DMA
Paste it right after ShowVDPGraphics.
Now all that's left to get the queue up and running is to call Process_DMA in the right location.
To do that, go to loc_D50, and add these two lines at the beginning:
move #$83,($FFFFF640).w
jsr Process_DMA
The queue should now be working. Now we need to do one more thing, call the actual Spin Dash dust object.
To do that, open _inc\Object pointers.asm, and replace the first call to ObjectFall in the second line with Spin Dash_dust.
The outcome should be something like this:
; ---------------------------------------------------------------------------
; Object pointers
; ---------------------------------------------------------------------------
dc.l Obj01, ObjectFall, ObjectFall, ObjectFall
dc.l SpinDash_dust, ObjectFall, ObjectFall, Obj08
dc.l Obj09, Obj0A, Obj0B, Obj0C
dc.l Obj0D, Obj0E, Obj0F, Obj10
dc.l Obj11, Obj12, Obj13, Obj14
dc.l Obj15, Obj16, Obj17, Obj18
dc.l Obj19, Obj1A, Obj1B, Obj1C
dc.l Obj1D, Obj1E, Obj1F, Obj20
dc.l Obj21, Obj22, Obj23, Obj24
dc.l Obj25, Obj26, Obj27, Obj28
dc.l Obj29, Obj2A, Obj2B, Obj2C
dc.l Obj2D, Obj2E, Obj2F, Obj30
dc.l Obj31, Obj32, Obj33, Obj34
dc.l Obj35, Obj36, Obj37, Obj38
dc.l Obj39, Obj3A, Obj3B, Obj3C
dc.l Obj3D, Obj3E, Obj3F, Obj40
dc.l Obj41, Obj42, Obj43, Obj44
dc.l Obj45, Obj46, Obj47, Obj48
dc.l Obj49, Obj4A, Obj4B, Obj4C
dc.l Obj4D, Obj4E, Obj4F, Obj50
dc.l Obj51, Obj52, Obj53, Obj54
dc.l Obj55, Obj56, Obj57, Obj58
dc.l Obj59, Obj5A, Obj5B, Obj5C
dc.l Obj5D, Obj5E, Obj5F, Obj60
dc.l Obj61, Obj62, Obj63, Obj64
dc.l Obj65, Obj66, Obj67, Obj68
dc.l Obj69, Obj6A, Obj6B, Obj6C
dc.l Obj6D, Obj6E, Obj6F, Obj70
dc.l Obj71, Obj72, Obj73, Obj74
dc.l Obj75, Obj76, Obj77, Obj78
dc.l Obj79, Obj7A, Obj7B, Obj7C
dc.l Obj7D, Obj7E, Obj7F, Obj80
dc.l Obj81, Obj82, Obj83, Obj84
dc.l Obj85, Obj86, Obj87, Obj88
dc.l Obj89, Obj8A, Obj8B, Obj8C
This will cause Object ID 05, which was previously unused, to now point to the Spin Dash dust object.
Finally, we need to call the object itself. So go to Obj01_Main, and at the end of the routine (before Obj01_Control), add the following line:
move.b #5,$FFFFD1C0.w
This will load the dust object to address $FFFFD1C0, (addresses from $D000 to $D400 are part of the SST).
Finally, we need to have the Spin Dash routine set the dust's animation. To do that, go to Sonic_SpinDash, and change this line:
move.b #2,($FFFFD11C).w
to this:
move.b #2,($FFFFD1DC).w ; Set the Spin Dash dust animation to $2.
Also, remove this line:
bcs.s loc2_1AC84 ; ??? branch if carry
And again, in loc2_1ACF4, change this line:
move.b #0,($FFFFD11C).w ; clear $D11C (unused?)
to this:
move.b #0,($FFFFD1DC).w ; clear Spin Dash dust animation.
And finally, add the following line in loc2_1AD48, right before the jsr to PlaySound_Special:
move.b #2,$FFFFD1DC.w ; Set the Spin Dash dust animation to $2.
For reference, here's the complete Sonic_SpinDash routine:
Sonic_SpinDash:
tst.b $39(a0) ; already Spin Dashing?
bne.s loc2_1AC8E ; if set, branch
cmpi.b #8,$1C(a0) ; is anim duck
bne.s locret2_1AC8C ; if not, return
move.b ($FFFFF603).w,d0 ; read controller
andi.b #$70,d0 ; pressing A/B/C ?
beq.w locret2_1AC8C ; if not, return
move.b #$1F,$1C(a0) ; set Spin Dash anim (9 in s2)
move.w #$D1,d0 ; spin sound ($E0 in s2)
jsr (PlaySound_Special).l ; play spin sound
addq.l #4,sp ; increment stack ptr
move.b #1,$39(a0) ; set Spin Dash flag
move.w #0,$3A(a0) ; set charge count to 0
cmpi.b #$C,$28(a0) ; ??? oxygen remaining?
move.b #2,($FFFFD1DC).w ; Set the Spin Dash dust animation to $2
loc2_1AC84:
bsr.w Sonic_LevelBound
bsr.w Sonic_AnglePos
locret2_1AC8C:
rts
; ---------------------------------------------------------------------------
loc2_1AC8E:
move.b #$1F,$1C(a0)
move.b ($FFFFF602).w,d0 ; read controller
btst #1,d0 ; check down button
bne.w loc2_1AD30 ; if set, branch
move.b #$E,$16(a0) ; $16(a0) is height/2
move.b #7,$17(a0) ; $17(a0) is width/2
move.b #2,$1C(a0) ; set animation to roll
addq.w #5,$C(a0) ; $C(a0) is Y coordinate
move.b #0,$39(a0) ; clear Spin Dash flag
moveq #0,d0
move.b $3A(a0),d0 ; copy charge count
add.w d0,d0 ; double it
move.w spdsh_norm(pc,d0.w),$14(a0) ; get normal speed
tst.b ($FFFFFE19).w ; is sonic super?
beq.s loc2_1ACD0 ; if no, branch
move.w spdsh_super(pc,d0.w),$14(a0) ; get super speed
loc2_1ACD0: ; TODO: figure this out
move.w $14(a0),d0 ; get inertia
subi.w #$800,d0 ; subtract $800
add.w d0,d0 ; double it
andi.w #$1F00,d0 ; mask it against $1F00
neg.w d0 ; negate it
addi.w #$2000,d0 ; add $2000
move.w d0,($FFFFEED0).w ; move to $EED0
btst #0,$22(a0) ; is sonic facing right?
beq.s loc2_1ACF4 ; if not, branch
neg.w $14(a0) ; negate inertia
loc2_1ACF4:
bset #2,$22(a0) ; set unused (in s1) flag
move.b #0,($FFFFD1DC).w ; clear Spin Dash dust animation
move.w #$BC,d0 ; spin release sound
jsr (PlaySound_Special).l ; play it!
bra.s loc2_1AD78
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
spdsh_norm:
dc.w $800 ; 0
dc.w $880 ; 1
dc.w $900 ; 2
dc.w $980 ; 3
dc.w $A00 ; 4
dc.w $A80 ; 5
dc.w $B00 ; 6
dc.w $B80 ; 7
dc.w $C00 ; 8
spdsh_super:
dc.w $B00 ; 0
dc.w $B80 ; 1
dc.w $C00 ; 2
dc.w $C80 ; 3
dc.w $D00 ; 4
dc.w $D80 ; 5
dc.w $E00 ; 6
dc.w $E80 ; 7
dc.w $F00 ; 8
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
loc2_1AD30: ; If still charging the dash...
tst.w $3A(a0) ; check charge count
beq.s loc2_1AD48 ; if zero, branch
move.w $3A(a0),d0 ; otherwise put it in d0
lsr.w #5,d0 ; shift right 5 (divide it by 32)
sub.w d0,$3A(a0) ; subtract from charge count
bcc.s loc2_1AD48 ; ??? branch if carry clear
move.w #0,$3A(a0) ; set charge count to 0
loc2_1AD48:
move.b ($FFFFF603).w,d0 ; read controller
andi.b #$70,d0 ; pressing A/B/C?
beq.w loc2_1AD78 ; if not, branch
move.w #$1F00,$1C(a0) ; reset spdsh animation
move.w #$D1,d0 ; was $E0 in sonic 2
move.b #2,$FFFFD1DC.w ; Set the Spin Dash dust animation to $2.
jsr (PlaySound_Special).l ; play charge sound
addi.w #$200,$3A(a0) ; increase charge count
cmpi.w #$800,$3A(a0) ; check if it's maxed
bcs.s loc2_1AD78 ; if not, then branch
move.w #$800,$3A(a0) ; reset it to max
loc2_1AD78:
addq.l #4,sp ; increase stack ptr
cmpi.w #$60,($FFFFEED8).w ; $EED8 only ever seems
beq.s loc2_1AD8C ; to be used in Spin Dash
bcc.s loc2_1AD88
addq.w #4,($FFFFEED8).w
loc2_1AD88:
subq.w #2,($FFFFEED8).w
loc2_1AD8C:
bsr.w Sonic_LevelBound
bsr.w Sonic_AnglePos
move.w #$60,($FFFFF73E).w ; reset looking up/down
rts
Now, build the ROM, and the result is... oops, something's missing. Right, we forgot to add the dust art.
So download the Spin Dash dust art here (put it in the artunc\ folder inside your source dir),
and at the end of the ROM, after SegaPCM, add the following lines:
Art_Dust incbin artunc\spindust.bin
Now compile it and try a Spin Dash:
Nice, it seems to be working...
...or not.
I'm pretty sure the lamppost wasn't meant to look like THAT.
The problem here seems to be that the dust art is overwriting the lamppost art.
To fix this, we need to move the lamppost art to a different location.
For that, we can use the VRAM location $D800, which is free.
So open the file _inc\Pattern load cues.asm, and search for these lines:
PLC_Main: dc.w 4
dc.l Nem_Lamp ; lamppost
dc.w $F400
Change them to:
PLC_Main: dc.w 4
dc.l Nem_Lamp ; lamppost
dc.w $D800
This will load the lamppost art in $D800 in VRAM, instead of $F400.
Now we must make the lamppost object use that art, instead of the one in $F400.
So now go to Obj79 (lamppost object) and search for this line, which should be in Obj79_Main:
move.w #$7A0,2(a0)
Change it to:
move.w #($D800/$20),2(a0)
The Obj79_Main routine should then look like this:
Obj79_Main: ; XREF: Obj79_Index
addq.b #2,$24(a0)
move.l #Map_obj79,4(a0)
move.w #($D800/$20),2(a0)
move.b #4,1(a0)
move.b #8,$19(a0)
move.b #5,$18(a0)
lea ($FFFFFC00).w,a2
moveq #0,d0
move.b $23(a0),d0
bclr #7,2(a2,d0.w)
btst #0,2(a2,d0.w)
bne.s Obj79_RedLamp
move.b ($FFFFFE30).w,d1
andi.b #$7F,d1
move.b $28(a0),d2 ; get lamppost number
andi.b #$7F,d2
cmp.b d2,d1 ; is lamppost number higher than the number hit?
bcs.s Obj79_BlueLamp ; if yes, branch
Similarly, change this line in Obj79_HitLamp:
move.w #$7A0,2(a1)
to this:
move.w #($D800/$20),2(a1)
Therefore making Obj79_HitLamp look like this:
Obj79_HitLamp:
move.w ($FFFFD008).w,d0
sub.w 8(a0),d0
addq.w #8,d0
cmpi.w #$10,d0
bcc.w locret_16F90
move.w ($FFFFD00C).w,d0
sub.w $C(a0),d0
addi.w #$40,d0
cmpi.w #$68,d0
bcc.s locret_16F90
move.w #$A1,d0
jsr (PlaySound_Special).l ; play lamppost sound
addq.b #2,$24(a0)
jsr SingleObjLoad
bne.s loc_16F76
move.b #$79,0(a1) ; load twirling lamp object
move.b #6,$24(a1) ; use "Obj79_Twirl" routine
move.w 8(a0),$30(a1)
move.w $C(a0),$32(a1)
subi.w #$18,$32(a1)
move.l #Map_obj79,4(a1)
move.w #($D800/$20),2(a1)
move.b #4,1(a1)
move.b #8,$19(a1)
move.b #4,$18(a1)
move.b #2,$1A(a1)
move.w #$20,$36(a1)
...and that's it. Compile your ROM and we should now have perfect Spin Dash in Sonic 1.
Here's the final result of this thing. Source code available here. Have fun :)
Note that you should look here for instructions on fixing the SEGA sound, and see Part 3 for some additional fixes not covered in this guide.
|Add Spin Dash to Sonic 1/Part 2]]