Actions

SCHG How-to

Add Spin Dash to Sonic 1/Part 1

From Sonic Retro

(Redirected from SCHG How-to:Add Spin Dash to Sonic 1 (GitHub))

(Original guide by DikinBaus)
(Based on the original guide by Lightning)

In this guide, we'll be porting the Spin Dash from Sonic 2 into Sonic 1. It is designed to work with the latest version of the Sonic 1 GitHub disassembly.

This tutorial was made possible thanks to the references, guides, and information found at Hacking CulT, The Sonic 2 Beta Page, Sonic Retro, and Sonicology. It was inspired by the guide at The Glowing Bridge that details porting the simpler Spin Dash from the Sonic 2 early beta, as well as Lightning's original guide that expands upon the former.

This newly-revamped guide is based off of the original version of the guide that was targeted for the legacy Hivebrain 2005 disassembly of Sonic 1. Thanks to Kramlat for making some additional updates to the original guide before this one. Additional updates to the guide have also been provided by JGMR, new minor fixes by DikinBaus.

Sonic and all related characters and graphics that appear on this page are property of SEGA and are used without permission for no profit.

Requirements

  • Sonic 1 split disassembly
  • Sonic 2 split disassembly (only necessary if you want to grab the spindash sprites yourself)
  • A plain text editor of your choice
  • A tile/mappings editor capable of editing Genesis format tiles
  • A graphics editor of your choice
  • A Sega Genesis emulator of your choice (or real hardware with a flashcart)

In making this tutorial, I used:

Other disassemblies of Sonic 1 and 2 can be used, but you'll have to work out the differences on your own (it shouldn't be too hard to figure it out).

Porting the Spin Dash

Now that you've gotten the required tools and disassemblies of your choice, it's time to start porting over the Spin Dash into Sonic 1!

Adding new flags

Before we can start, we'll need to do some prerequisites before we can port over our code. For the purposes of this tutorial, I'll assume you do it that way too. Open Constants.asm and search for stick_to_convex:. There you will define a new RAM equate by adding the following lines underneath it:

spindash_flag:	equ $39	; spin dash flag
spindash_counter:	equ $3A	; spin dash counter

This should preferably be put in between the stick_to_convex: and standonobject: variables. We could skip this, but then we'll have to replace all spindash_flag in the code for $39 and spindash_counter in the code for $3A, and that's not what we want.

Next, open Variables.asm and above v_shieldobj = v_objspace+object_size*6, add this in:

v_spindust	= v_objspace+object_size*4	; object variable space for the spin dash dust ($40 bytes)

The code should look like this:

v_spindust	= v_objspace+object_size*4	; object variable space for the spin dash dust ($40 bytes)
v_shieldobj	= v_objspace+object_size*6	; object variable space for the shield ($40 bytes)
v_starsobj1	= v_objspace+object_size*8	; object variable space for the invincibility stars #1 ($40 bytes)
v_starsobj2	= v_objspace+object_size*9	; object variable space for the invincibility stars #2 ($40 bytes)
v_starsobj3	= v_objspace+object_size*10	; object variable space for the invincibility stars #3 ($40 bytes)
v_starsobj4	= v_objspace+object_size*11	; object variable space for the invincibility stars #4 ($40 bytes)

Directly copying the object code

Open sonic.asm in your text editor, and search for SonicPlayer:. This should bring you to the Sonic object's code, which is what you're interested in editing right now. For reference, go to your Sonic 2 disassembly and open up s2.asm. Search for Obj01: (like you would in the legacy Hivebrain 2005 disassembly of Sonic 1), and it will once again take you to Sonic's code. Having been based on the same code from Sonic 1, it shows many similarities that you should be able to spot. Thanks to the similar code, our job in porting the Spin Dash should be pretty easy!

Find or scroll to the location Sonic_MdNormal: (line 6948) in Sonic 1's source, and Obj01_MdNormal: in Sonic 2's source. Below this label in each is a series of branches and jumps to subroutines that manage Sonic's actions in different situations. If you look carefully, you'll notice that there are nine of these branches in Sonic 2, but only eight in Sonic 1. That's because Sonic 2 has another branch to the code that handles the Spin Dash.

In the sonic.asm file of your Sonic 1 disassembly, make a copy of the line bsr.w Sonic_Jump and change it to bsr.w Sonic_SpinDash, as shown:

Sonic_MdNormal:
		bsr.w	Sonic_SpinDash	; <-- add this line!
		bsr.w	Sonic_Jump
		...

Don't worry about the code below bsr.w Sonic_Jump, as we won't be touching them.

Now, to actually copy the Spin Dash subroutine. For this, we'll need to make a new ASM file. So create a new file within the _incObj folder and name it "Sonic SpinDash.asm". Now copy the code below and paste it into the Sonic SpinDash.asm file.

; ---------------------------------------------------------------------------
; Subroutine to check for starting to charge a spindash
; ---------------------------------------------------------------------------

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

; loc_1AC3E:
Sonic_SpinDash:
	tst.b	spindash_flag(a0)
	bne.s	Sonic_UpdateSpindash
	cmpi.b #id_Duck,obAnim(a0) 
	bne.s	return_1AC8C
	move.b (v_jpadpress2).w,d0 
	andi.b #$70,d0 
	beq.w	return_1AC8C
	move.b #$1F,obAnim(a0) 
	move.w #$BE,d0 
	jsr	(PlaySound_Special).l
	addq.l	#4,sp
	move.b	#1,spindash_flag(a0)
	move.w	#0,$3A(a0)
	cmpi.b #$C,$28(a0) 	; if he's drowning, branch to not make dust
	blo.s	loc_1AC84
	move.b #2,($FFFFD11C).w
loc_1AC84:
	bsr.w	Sonic_LevelBound
	bsr.w	Sonic_AnglePos

return_1AC8C:
	rts
; End of subroutine Sonic_CheckSpindash


; ---------------------------------------------------------------------------
; Subrouting to update an already-charging spindash
; ---------------------------------------------------------------------------

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

; loc_1AC8E:
Sonic_UpdateSpindash:
	move.b	#$1F,$1C(a0)
	move.b  (v_jpadhold2).w,d0 
	btst    #1,d0 
	bne.w	Sonic_ChargingSpindash

	; unleash the charged spindash and start rolling quickly:
	move.b #$E,$16(a0) 
	move.b #7,$17(a0) 
	move.b	#2,obAnim(a0)
	addq.w	#5,obY(a0)	; add the difference between Sonic's rolling and standing heights
	move.b	#0,spindash_flag(a0)
	moveq	#0,d0
	move.b	$3A(a0),d0
	add.w	d0,d0
	move.w	SpindashSpeeds(pc,d0.w),obInertia(a0)
	move.w	obInertia(a0),d0
	subi.w	#$800,d0
	add.w	d0,d0
	andi.w	#$1F00,d0
	neg.w	d0
	addi.w	#$2000,d0
	move.w  d0,($FFFFEED0).w 
	btst    #0,$22(a0) 
	beq.s	loc_1ACF4
	neg.w   $14(a0)
loc_1ACF4:
	bset    #2,$22(a0) 
	move.b #0,($FFFFD11C).w 
	move.w #$BC,d0 
	jsr     (PlaySound_Special).l 
	bra.s	Obj01_Spindash_ResetScr
; ===========================================================================
; word_1AD0C:
SpindashSpeeds:
	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
; ===========================================================================
; loc_1AD30:
Sonic_ChargingSpindash:			; If still charging the dash...
	tst.w	$3A(a0)
	beq.s	loc_1AD48
	move.w	$3A(a0),d0
	lsr.w	#5,d0
	sub.w	d0,$3A(a0)
	bcc.s	loc_1AD48
	move.w	#0,$3A(a0)
loc_1AD48:
	move.b  (v_jpadpress2).w,d0 
	andi.b  #$70,d0 ; 'p' 
	beq.w   Obj01_Spindash_ResetScr
	move.w  #$1F00,$1C(a0) 
	move.w  #$BE,d0 ; 'à' 
	jsr	(PlaySound_Special).l
	addi.w	#$200,$3A(a0)
	cmpi.w	#$800,$3A(a0)
	blo.s	Obj01_Spindash_ResetScr
	move.w	#$800,$3A(a0)

; loc_1AD78:
Obj01_Spindash_ResetScr:
	addq.l	#4,sp
	cmpi.w	#(224/2)-16,($FFFFEED8).w
	beq.s	loc_1AD8C
	bhs.s	loc_1AD88
	addq.w	#4,($FFFFEED8).w
loc_1AD88:
    subq.w	#2,($FFFFEED8).w

loc_1AD8C:
	bsr.w	Sonic_LevelBound
	bsr.w	Sonic_AnglePos
	move.w #$60,(v_lookshift).w
	rts
; End of subroutine Sonic_UpdateSpindash

Now everything should be there. Let's test it! First, save your changes, and then build the ROM. Then, test it out in the emulator, and...

Bugs

Spin Dashing with a strange animation

Uh-oh. Things are looking pretty rough right now.

Okay, so as we've expected, there are some bugs. First off, Sonic's Spin Dashing animation isn't present in Sonic 1, of course, and right now it's using the animation from the spot that the Spin Dash's animation data replaced, we fix this in the "Adding new sprites" and "Adding animation". "Also, the sound when you charge a Spin Dash isn't the same from Sonic 2's. That's because the sound ID that is the Spin Dash sound in Sonic 2 has a different purpose in Sonic 1.

Second, if you try to charge a Spin Dash while having rings and a badnik starts attacking you, they can still hurt you in your Spin Dashing state. Sonic gets injured, when instead the enemy should be destroyed. If you get hit while charging, you'll still be in a Spin Dash-charging state when you land. If you let go of the down button while bouncing backwards, Sonic will release his Spin Dash right away when he hits the ground (depending on which way you got hit; forwards or backwards). Now, this might be awesome in itself and certainly should kill whatever badnik hurt you in the first place, but it isn't what should happen.

Spin Dashing with low camera

Third, move somewhere where the camera has room to move downward. If you hold the down button to duck, then start charging the Spin Dash, the camera stays low until you are no longer rolling/Spin Dashing.

camera lagging behind

Another problem that results from this is that if you Spin Dash before an area where your vertical position radically changes (for instance, the double-S-tube in GHZ1), the camera is too slow to catch up to Sonic in time.

Aside from all of those bugs, the physics of the Spin Dash are clearly in place, however. This is a good start!

Fixing bugs/errors

First of all, let's fix the camera issue. Go back to the Spin Dash code and replace instances of $FFFFEED8 with v_lookshift. Build and test. You should find that the camera does reset itself when you start to Spin Dash, but if you take the double-S-tube in GHZ1, you still barely outrun the screen vertically and get killed. So to fix this, go into the _incObj folder and open the file "Sonic LevelBound.asm". Find the code at the label .bottom:. Here's what you should see:

.bottom:
		cmpi.w	#(id_SBZ<<8)+1,(v_zone).w ; is level SBZ2 ?
		bne.w	KillSonic	; if not, kill Sonic
		cmpi.w	#$2000,(v_player+obX).w
		blo.w	KillSonic
		clr.b	(v_lastlamp).w	; clear	lamppost counter
		move.w	#1,(f_restart).w ; restart the level
		move.w	#(id_LZ<<8)+3,(v_zone).w ; set level to SBZ3 (LZ4)
		rts

First, add a label just before the rts at the end of the block of code, named .dontkill. Then, at the beginning of .bottom:, you're going to compare two values in memory. If (v_limitbtm1).w < (v_limitbtm2).w, then the screen is still scrolling down and you don't want to die. In this case, it should skip the part below that jumps to the routine KillSonic. You should add the following lines of code below .bottom::

		move.w	(v_limitbtm1).w,d0 
		move.w	(v_limitbtm2).w,d1 
		cmp.w	d0,d1		; screen still scrolling down? 
		blt.s	.dontkill	; if so, don't kill Sonic

You should end up with something like this:

.bottom:
		move.w	(v_limitbtm1).w,d0 
		move.w	(v_limitbtm2).w,d1 
		cmp.w	d0,d1		; screen still scrolling down? 
		blt.s	.dontkill	; if so, don't kill Sonic
		cmpi.w	#(id_SBZ<<8)+1,(v_zone).w ; is level SBZ2 ?
		bne.w	KillSonic	; if not, kill Sonic
		cmpi.w	#$2000,(v_player+obX).w
		blo.w	KillSonic
		clr.b	(v_lastlamp).w	; clear	lamppost counter
		move.w	#1,(f_restart).w ; restart the level
		move.w	#(id_LZ<<8)+3,(v_zone).w ; set level to SBZ3 (LZ4)
		rts	
; ===========================================================================

.dontkill: 
		rts
There is a
Sonic Retro
forum thread
that talks about this specific bug as well as a fix for it.

Now, you'll fix the problem where Sonic is still in his Spin Dashing state after he gets injured. Open sub ReactToItem.asm located in _incObj and find the subroutine HurtSonic. This is the code that handles hurting Sonic, if you couldn't guess. You're going to have it clear the flag in memory that stores whether or not Sonic is Spin Dashing. Scroll to the label .isleft and add the line move.b #0,(a0) directly below it. You should end up with:

.isleft:
		move.b	#0,spindash_flag(a0)	; clear Spin Dash flag 
		move.w	#0,obInertia(a0)
		move.b	#id_Hurt,obAnim(a0)
		move.w	#120,flashtime(a0)	; set temp invincible time to 2 seconds

Save, build, and test. See if you can try to outrun the camera in the S-tube in GHZ1 and try to see if you're not in a Spin Dashing state after getting hurt. If all goes well, those pesky bugs should be gone! As for the animation, we'll deal with that in the Adding new sprites section of the guide

Adding new sprites

As mentioned before, the Spin Dash that we have doesn't look like the one found in Sonic 2. Sonic doesn't have a dedicated Spin Dash sprite as it's not present in Sonic 1, so we need to implement the sprites for it. So for this section, you'll add the Spin Dash sprites from Sonic 2's sprites to Sonic 1's. To do this, we'll need a tile/sprite/mappings editor of our choice. We'll be using Flex 2 for this guide.

Open up sonic1.flex.json in your disassembly via Flex 2 and select the "Sonic" object under the "Common" folder. Once there, click on the "Load" button under Object and...

setting up Flex 2 part 1

Uh-oh, looks like we have problems yet again. But, not to worry, we can try selecting "Sonic 2" under Game Format, and let’s see what we got:

setting up Flex 2 part 2

Success! Our object now displays as intended!

Now create a new object by hitting the "Object" button, and it should appear above the "Common" folder. We could name our new object to anything we want. Select your newly-created object, and begin loading in your Sonic 2 files. Here's what you will need to find as far as Sonic 2 files go:

  • Sonic's art tiles (found under "art\uncompressed" with the filename "Sonic's art.bin")
  • Sonic's mappings (found under "mappings\sprite" with the filename "Sonic.asm")
  • Sonic's DPLCs (found under "mappings\spriteDPLC" with the filename "Sonic.asm"; rename as "SonicDPLC.asm")

As for the palette, we'll continue to use Sonic's palette from our Sonic 1 disassembly. This way the sprites will still remain consistent in terms of the color palette, otherwise they will use the nearest matching color palette for our Spin Dash sprites if we import them with the Sonic 2 palette, and that’s not what we want.

Now we’ll create a new folder on our disassembly for the time being to put our Sonic 2 files in. We can name this folder to anything we want. Then, we’ll head over to our Sonic 2 disassembly, copy our provided files, and then paste them into our new folder. To save you some time, I have provided the Sonic 2 files here for download. Once you have downloaded the archive containing the files, extract them to the folder that you will be putting the Sonic 2 files in.

Now load those files into Flex 2, select "Sonic 2" under Game Format, and then press the "Load" button near Object. Here's what we should get:

setting up Flex 2 part 3

Success! The basic setup is complete!

Now comes the long part, extracting the Spin Dash sprites. Head over to the "Mappings" tab and press the "Export Spritesheet" option (or press "E" (uppercase) on your keyboard) and save it. Once finished, open your exported sprite sheet in a raster graphics editor of your choice (I'll be using GIMP for this part of the guide). You should see a big list of all of the sprites that we just exported. Make sure to find where the Spin Dash sprites are located. Now you want to crop the image down to focus only on the Spin Dash sprites. Use the selection tool to select the Spin Dash sprites as shown:

selecting spin dash sprites

Now find the option to crop the image. If you're using GIMP, go to "Image" => "Crop to Selection". You should be left with this:

cropped selection showing only spin dash sprites

Now save your image file as a new image by going to "File" => "Export As...", and then save your new image in the folder that you put the Sonic 2 files in. Make sure the file extension ".png" is defined, as we'll be saving in this format. You can name your new image to anything you want; I will be using "spindash.png" as an example. For your convenience, I have provided the sprite images that you'll be needing for this section here; download it and then extract it to the folder you put the Sonic 2 files in.

Then, head over to Flex 2 again, select the "Project" tab, click on the "Sonic" object under the "Common" folder, load it, and then head back to the "Mappings" tab. Click on the Import Spritesheet button (or pressing "s" (lowercase) on your keyboard) and select the Spin Dash sprite you have saved. You should see a screen that looks like this:

importing spin dash sprites in Flex 2 part 1

Leave all of the other options alone for now, as we won't be messing around with them in this guide. Click on Detect Sprites, and you should see all of the Spin Dash sprites being highlighted. Click on Import 6 Sprites. This will take you to the Import Sprite screen for each individual sprite, as shown below:

importing spin dash sprites in Flex 2 part 2

To save us some time in importing each and every single sprite, We'll click on Import All to import all of our Spin Dash sprites. We'll leave all of the other options alone for now. Once finished, we should see our Spin Dash sprites that we added once you scroll down to the bottom under the "Sprites" section.

Next, we'll adjust the positioning of them. To do that, we'll select each individual Spin Dash sprite one by one from our selection and then hitting "Ctrl+A" (or by clicking and dragging the mouse cursor) to select the whole sprite as shown:

selecting spin dash sprites via Flex 2

Then, we'll start moving the sprites down by 9 pixels and to the right by 5 pixels. This will ensure that our newly-added Spin Dash sprites match up with the original Sonic 2 sprites in terms of the positioning. Repeat this step for every single Spin Dash sprite. There is a very handy indicator that shows you the positioning of the sprites when you have selected the whole sprite (or even individual sprite pieces), as shown here (highlighted in red):

Flex 2 hud indicator

Make sure to double-check the positioning of your newly-added sprites by selecting one sprite or another that has the same positioning as the one you've done several times. You can also scroll the horizontal scroll bar to easily see the position of the sprites. Go ahead and save your files by going to the "Project" tab, clicking on the "Sonic" object under the "Common" folder, and then hitting the "Save" button under Object. Make sure to switch the Game Format to "Sonic 1" before saving.

Now you're ready to build your ROM. BUT WAIT! Don't go for that "build.bat" option yet! We've only just scratched the surface!

Adding animations

We still need to set our animation for our new Spin Dash sprites that we've just imported. Go to Sonic.asm (or Sonic (without frame IDs).asm) in the _anim folder and locate ptr_Float4:. You'll see this:

ptr_Float4:	dc.w SonAni_Float4-Ani_Sonic

Add this underneath:

ptr_Spindash:	dc.w SonAni_Spindash-Ani_Sonic ;1F

The ;1F comment after it is again an index in the table. If you were to enumerate all of the lines in this table, starting at zero, this new line would be the $1Fth line. Notice that at index 2 is the rolling animation that we are currently using, and at index 9 is the warping one that it used before we fixed it!

Once that's done, locate SonAni_Float4:. You'll see this:

SonAni_Float4:	dc.b 3,	fr_Float1, afChange, id_Walk
		even

Now, you will again add data just after the even at the end of the second table:

SonAni_Spindash: dc.b 0, fr_Spindash1, fr_Spindash2, fr_Spindash1, fr_Spindash3, fr_Spindash1, fr_Spindash4
		dc.b fr_Spindash1, fr_Spindash5, fr_Spindash1, fr_Spindash6, afEnd
		even

The first byte in this animation script tells the game to use the fastest speed in this animation. Each subsequent word until the afEnd tells the game which animation frame to use. The afEnd at the end tells the game to loop the entire animation. Basically, this script will have the game cycle through the 1st, 2nd, 1st, 3rd, ..., 1st, 6th, 1st, 2nd, ... frames.

Notice that you could have use numbers ($58, $59, etc...) as they are the indexes of your Spin Dash PLCs. In the _anim folder of your disassembly, there is an alternative animation file that does not use frame IDs, titled Sonic (without frame IDs).asm. This is the code that you should looking at when it has been done this way:

SonAni_SpinDash:
		dc.b 0, $58, $59, $58, $5A, $58, $5B, $58, $5C, $58, $5D, afEnd
		even

Paste this underneath the animation data for SonAni_Float4: within Sonic (without frame IDs).asm.

Now go to the very end of the file and add this line:

id_Spindash:	equ (ptr_Spindash-Ani_Sonic)/2	; $1F

Now to call the frame IDs for the Spin Dash animation. Go to Constants.asm and look for fr_WaterSlide: equ $57. Underneath it, insert these lines:

fr_Spindash1:	equ $58
fr_Spindash2:	equ $59
fr_Spindash3:	equ $5A
fr_Spindash4:	equ $5B
fr_Spindash5:	equ $5C
fr_Spindash6:	equ $5D

Now that you have added all the necessary information for using the new Spin Dash tiles in an animation, it's time for the last step in adding this animation: actually having the game use it! Open Sonic SpinDash.asm in your text editor (if you haven't already) and replace instances of id_Roll with id_Spindash, making sure to not touch the other id_Roll animation under Sonic_UpdateSpindash:.

Now let's save our progress, build the ROM, and test it out in our emulator to see how it goes:

Spin Dashing with Spin Dash animation

Success! We now have a Spin Dash that looks and functions identical to Sonic 2! BUT WAIT! We still haven't forgotten about the Spin Dashing badnik bug! So even though we now have the new animation in place, the game’s hurt-checking routine doesn't recognize this new animation that we just set, so if a badnik touches Sonic while Spin Dashing, he will be hurt instead of actually killing the badnik.

Final bugfix

So to fix this prior bug once and for all, go back to sub ReactToItem under _incObj and look for React_Enemy:. Find this piece of code:

React_Enemy:
		tst.b	(v_invinc).w	; is Sonic invincible?
		bne.s	.donthurtsonic	; if yes, branch
		cmpi.b	#id_Roll,obAnim(a0) ; is Sonic rolling/jumping?
		bne.w	React_ChkHurt	; if not, branch

Just above the line cmpi.b #id_Roll,obAnim(a0) ; is Sonic rolling/jumping?, add these lines underneath:

		cmpi.b	#id_Spindash,obAnim(a0)	; is Sonic Spin Dashing? 
		beq.w	.donthurtsonic	; if yes, branch

You should end up with something like this:

React_Enemy:
		tst.b	(v_invinc).w	; is Sonic invincible?
		bne.s	.donthurtsonic	; if yes, branch
		cmpi.b	#id_Spindash,obAnim(a0)	; is Sonic Spin Dashing? 
		beq.w	.donthurtsonic	; if yes, branch
		cmpi.b	#id_Roll,obAnim(a0) ; is Sonic rolling/jumping?
		bne.w	React_ChkHurt	; if not, branch

This code adds a check to see if the animation is id_Spindash (#$1F), our new animation. If it is, it branches over the code that jumps to the routine to hurt Sonic. Notice that there is another comparison for if the animation id_Roll (#2) is present, which concealed this bug before, since we were using that animation.

Save, build, and test. Let's test our Spin Dash against a badnik:

killing an enemy while Spin Dashing

Pop! Congratulations! We have now successfully imported the Spin Dash into Sonic 1! It's still not fully complete though, as it's missing a few bells and whistles. We have to add the Spin Dash smoke/dust object, as well as fixing a few more bugs. Luckily, Puto has written a wonderful guide continuing from this point that does exactly that.

Complete Spin Dash code

For your convenience, here's the full Spin Dash code after following this guide, complete with commenting:

; ---------------------------------------------------------------------------
; Subroutine to make Sonic perform a spindash
; ---------------------------------------------------------------------------

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


Sonic_SpinDash:
		tst.b	spindash_flag(a0)	; already Spin Dashing?
		bne.s	Sonic_UpdateSpindash	; if set, branch
		cmpi.b	#id_Duck,anim(a0)	; is anim duck?
		bne.s	return_1AC8C	; if not, return
		move.b	(v_jpadpress2).w,d0	; read controller
		andi.b	#btnB|btnC|btnA,d0	; pressing A/B/C ?
		beq.w	return_1AC8C	; if not, return
		move.b	#id_Spindash,anim(a0)	; set Spin Dash anim (9 in s2)
		move.w	#sfx_Roll,d0	; spin sound ($E0 in s2)
		jsr	(PlaySound_Special).l		; play spin sound
		addq.l	#4,sp		; increment stack ptr
		move.b	#1,spindash_flag(a0)	; set Spin Dash flag
		move.w	#0,spindash_counter(a0)	; set charge count to 0
		cmpi.b	#12,air_left(a0)	; if he's drowning, branch to not make dust
		blo.s	+
		move.b	#2,(v_spindust+anim).w	; set Spin Dash dust anim to 2
+
		bsr.w	Sonic_LevelBound
		bsr.w	Sonic_AnglePos

return_1AC8C:
		rts
; End of function Sonic_SpinDash


; ---------------------------------------------------------------------------
; Subroutine to update an already-charging spindash
; ---------------------------------------------------------------------------

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


Sonic_UpdateSpindash:
		move.b	(v_jpadhold2).w,d0	; read controller
		btst	#bitDn,d0			; check down button
		bne.w	Sonic_ChargingSpindash	; if set, branch

		; unleash the charged spindash and start rolling quickly:
		move.b	#$E,y_radius(a0)	; y_radius(a0) is height/2
		move.b	#7,x_radius(a0)		; x_radius(a0) is width/2
		move.b	#id_Roll,anim(a0)	; set animation to roll
		addq.w	#5,y_pos(a0)	; add the difference between Sonic's rolling and standing heights
		move.b	#0,spindash_flag(a0)	; clear Spin Dash flag
		moveq	#0,d0
		move.b	spindash_counter(a0),d0	; copy charge count
		add.w	d0,d0	; double it
		move.w	SpindashSpeeds(pc,d0.w),inertia(a0)	; get spindash speed

		; Determine how long to lag the camera for.
		; Notably, the faster Sonic goes, the less the camera lags.
		; This is seemingly to prevent Sonic from going off-screen.
		move.w	inertia(a0),d0	; get inertia
		subi.w	#$800,d0 ; $800 is the lowest spin dash speed
		add.w	d0,d0	; double it
		andi.w	#$1F00,d0 ; This line is not necessary, as none of the removed bits are ever set in the first place
		neg.w	d0		; negate it
		addi.w	#$2000,d0	; add $2000
		move.w	d0,($FFFFEED0).w	; move to $EED0

		btst	#0,status(a0)	; is sonic facing right?
		beq.s	+		; if not, branch
		neg.w	inertia(a0)	; negate inertia
+
		bset	#2,status(a0)	; set unused (in s1) flag
		move.b	#0,(v_spindust+anim).w	; clear Spin Dash dust anim
		move.w	#sfx_Teleport,d0	; spindash zoom sound
		jsr	(PlaySound_Special).l	; play it!
		bra.s	Sonic_Spindash_ResetScr
; ===========================================================================
SpindashSpeeds:
		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
; ===========================================================================

Sonic_ChargingSpindash:			; If still charging the dash...
		tst.w	spindash_counter(a0)	; check charge count
		beq.s	+				; if zero, branch
		move.w	spindash_counter(a0),d0	; otherwise put it in d0
		lsr.w	#5,d0			; shift right 5 (divide it by 32)
		sub.w	d0,spindash_counter(a0)	; subtract from charge count
		bcc.s	+				; ??? branch if carry clear
		move.w	#0,spindash_counter(a0)	; set charge count to 0
+
		move.b	(v_jpadpress2).w,d0	; read controller
		andi.b	#btnB|btnC|btnA,d0	; pressing A/B/C?
		beq.w	Sonic_Spindash_ResetScr	; if not, branch
		move.w	#(id_Spindash<<8)|(id_Walk<<0),anim(a0)	; reset spindash animation
		move.w	#sfx_Roll,d0	; was $E0 in sonic 2
		jsr	(PlaySound_Special).l	; play charge sound
		addi.w	#$200,spindash_counter(a0)	; increase charge count
		cmpi.w	#$800,spindash_counter(a0)	; check if it's maxed
		blo.s	Sonic_Spindash_ResetScr	; if not, then branch
		move.w	#$800,spindash_counter(a0)	; reset it to max

Sonic_Spindash_ResetScr:
		addq.l	#4,sp		; increase stack ptr
		cmpi.w	#(224/2)-16,(v_lookshift).w
		beq.s	loc_1AD8C
		bhs.s	+
		addq.w	#4,(v_lookshift).w
+		subq.w	#2,(v_lookshift).w

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

|Add Spin Dash to Sonic 1 (GitHub)]]