NOTE: This guide isn't just copy-paste. This is intended to teach more than just how to port the Air Roll. Spoiler: Changelog EDIT (1st, 12/6/19): Removed a mention of how putting the bsr just anywhere in Obj01_MdJump/Obj01_MdJump2 would screw up the jump, and moved the placement of the bsr, based off of Iso Kilo's observations. EDIT (2nd, 12/6/19): Unifed this with the Sonic Retro tutorial. EDIT: (3rd, 12/6/19) Undid edit one of this day due to bugs being caused by the changes. This should fix your issues. EDIT: (4th, 4/7/20) FINALLY added Aier's optimized version of this code. As a result, the tutorial has been completely rearranged and partly rewritten. As you likely know, whenever you jump onto a spring in a Sonic game, you enter a vulnerable state. However, in Sonic Triple Trouble, you could press a jump button while in the spring animation to curl up into a ball: Now, this isn't in Sonic 1, 2, or 3 & Knuckles. However, this guide can show you how to add this move to Sonic 1's Hivebrain disasm, so that you can do this in Sonic 1. NOTE: For those not using the S1 Hivebrain disasm or S1 at all, porting should be easy. Make sure to look for the equivalent addresses and routines. Now, we want Sonic to be able to do the air roll ONLY if he's in the air and he isn't already rolling, right? Well, luckily for us, there's already a routine which is branched to when these criteria are set! This routine is Obj01_MdJump. This would be the best place for us to put the first branch to our code, so go to Obj01_MdJump. This is what it should look like normally: Code: Obj01_MdJump: ; XREF: Obj01_Modes bsr.w Sonic_JumpHeight bsr.w Sonic_ChgJumpDir bsr.w Sonic_LevelBound jsr ObjectFall btst #6,$22(a0) beq.s loc_12E5C subi.w #$28,$12(a0) loc_12E5C: bsr.w Sonic_JumpAngle bsr.w Sonic_Floor rts Now, we could arguably put it in anywhere, but the best place IS the top of the routine. So put this: Code: bsr.w Sonic_AirRoll Right under the Obj01_MdJump label. Now, bsr and jsr are a special kind of branch. After the code it branches to is finished, it'll automatically return to the point where we branched from and continue from right below it. This will be important for the format of our Air Roll code. So, we've created a branch to a new subroutine. The problem is, it doesn't exist! If you try to build right now, you'll get an error. Now, we have to create the subroutine itself. Put the proper label within Sonic's code, I've personally put it below Sonic_JumpHeight's function. Now, we have to actually code the action. First of all, we should filter out cases where Sonic has already performed the move, and therefore, in a ball. Luckily, in RAM, there's a SST which stores the current object's animation, which just so happens to be Sonic! Sonic's animation ID for rolling in Sonic 1 is #$2. The SST in question is $1C. IF you know what's about to happen, good job, you already know of the cmp (Compare) instruction. This instruction compares one thing to another thing. Using this, we can perform a branch using beq (Branch if Equal) and bne (Branch if Not Equal). This is what the end result should look like: Code: Sonic_AirRoll: cmpi.b #2,$1C(a0) ; are we already in rolling animation beq.s AirRoll_Return ; if yes, return If you want the move to, just like in Triple Trouble, only be able to occur if Sonic's in his spring animation, change the animation ID to the spring animation (#$10, IIRC) and invert the branch (beq -> bne, bne -> beq). Now, we should probably add this "AirRoll_Return" routine to begin with. So put this: Code: AirRoll_Return: rts Right below the current Sonic_AirRoll: code. Now, this is rts (Return To Subroutine) . The name should hint at it's function. Now, rts returns to the point where the current routine/subroutine was branched from, and continues code execution from there, much like what bsr and jsr automatically do. So, in order to keep the game from executing code it shouldn't, this routine was added. Now, we have a move and it won't trigger if you aren't in a ball, but it currently does nothing. Now, let's code the actual move! Now, we only want the code to execute if A/B/C is pressed, right? Now, this tutorial should show you how to utilize button presses, so check it out real quick, because I'd rather not copy what Selbi had to say. Now that you are back, you should have created something like this, below Sonic_AirRoll, but above AirRoll_Return: Code: AirRoll_CheckBtn: move.b ($FFFFF603).w,d0 ; Move $FFFFF603 to d0 andi.b #$70,d0 ; Has A/B/C been pressed? beq.s AirRoll_Return There's a new label here to help with readability. This will filter out all cases where Sonic DID NOT press A/B/C. However, it still does nothing! But, wait! Now that we have everything filtered out, we can actually do stuff now! Now, do you remember how $1C in the SST has the current object's animation stored to it? We are about to use that information again. There's a handy instruction called move (move), which moves a thing to a location. We can move #$2 to $1C in order to set Sonic's animation to be the rolling animation! So, in the same label, below the above button test, add these lines, which make Sonic roll, and play the roll sound: Code: move.b #2,$1C(a0) ; Set Sonic's animation to rolling. move.w #$BC,d0 jsr (PlaySound_Special).l ; play Sonic rolling sound Now, with how we set up the code, this will now fall through to AirRoll_Return, meaning that we don't leak down into Sonic_JumpHeight! Meaning, we are done! Enjoy the Air Roll! Credits: LoganTheSonicPlayer/Inferno Gear - Creating the guide. Iso Kilo - Responding to my question regarding a snag I hit while creating this code. Aier - Optimizing the code a while back. Sorry for not updating this yet, guys.