Making the 'S' box work in Sonic 1

Discussion in 'Tutorials Archive' started by shadowbeasts, Jul 7, 2010.

Thread Status:
Not open for further replies.
  1. shadowbeasts

    shadowbeasts I'm Legend Member

    Joined:
    Jan 5, 2009
    Messages:
    286
    Location:
    Good 'ol USA.
    This tutorial is to make the 'S' box work in Sonic 1. It's not so easy to do there are many things that can be messed up when making the 'S' box give us speed shoes and invincibility. Before we start adding our code we need to find Obj2E_ChkS in our sonic1 ASM.


    First lets look at the code for the 'S' box



    Obj2E_ChkS:
    cmpi.b #7,d0 ; does monitor contain 'S'


    bne.s Obj2E_ChkEnd


    nop



    This code checks if the monitor is being broken and then if it’s open branches to ChkEnd which means the monitor does nothing.


    now we want to make



    bne.s Obj2E_ChkEnd



    to



    bne.s Obj2E_ChkGoggles


    and we're going to get rid of nop.
    So now the code will look like this



    Obj2E_ChkS:
    cmpi.b #7,d0 ; does monitor contain 'S'


    bne.s Obj2E_ChkGoggles



    I'll explain why we branch to the Goggles later.


    Next we add some code from the speed shoes to the 'S' box code. We are going to add



    move.b #1,($FFFFFE2E).w ; speed up the BG music
    move.w #$4B0,($FFFFD034).w ; time limit for the power-up


    move.w #$C00,($FFFFF760).w ; change Sonic's top speed



    now sonic has the power up from the speed shoes. Meaning the first line speeds up the music and the second line is the time limit. I’m using the time limit for the seep shoes not the invincibility. If we were doing the time limit for the invincibility the code



    move.w #$4B0,($FFFFD034).w ; time limit for the power-up


    34 would be 32. The third line changes sonic’s top speed. Next we're adding the invincibility code

    Code:
                    move.b	#1,($FFFFFE2D).w ; make	Sonic invincible
    		move.w	#$4B0,($FFFFD032).w ; time limit for the power-up
    
    
    		move.b	#$38,($FFFFD200).w ; load stars	object ($3801)
    
    
    		move.b	#1,($FFFFD21C).w
    
    
    		move.b	#$38,($FFFFD240).w ; load stars	object ($3802)
    
    
    		move.b	#2,($FFFFD25C).w
    
    
    		move.b	#$38,($FFFFD280).w ; load stars	object ($3803)
    
    
    		move.b	#3,($FFFFD29C).w
    
    
    		move.b	#$38,($FFFFD2C0).w ; load stars	object ($3804)
    
    
    		move.b	#4,($FFFFD2DC).w
    
    
    		tst.b	($FFFFF7AA).w	; is boss mode on?
    
    
    		bne.s	Obj2E_NoMusic	; if yes, branch
    
    
    		move.w	#$87,d0
    
    
    		jmp	(PlaySound).l	; play invincibility music
    
    
    Now the first line makes Sonic invincible and the second line sets the time limit which we want to change. If we don’t change it the speed shoes would last longer than the invincibility so were going to change



    move.w #$4B0,($FFFFD032).w ; time limit for the power-up



    to



    move.w #$4B0,($FFFFD034).w ; time limit for the power-up



    Next we’re going to delete



    tst.b ($FFFFF7AA).w ; is boss mode on?
    bne.s Obj2E_NoMusic ; if yes, branch



    Now the 'S' box does exactly what we want it to, it gives us invincibility and speed shoes with the same time delay. But we're not done yet. If we try it build we'll get an error that tell us we don't have a Obj2E_ChkGoggles label. so lets add that. We just need to add two lines.



    Obj2E_ChkGoggles:
    cmpi.b #8,d0 ; does monitor contain 'S'


    bne.s Obj2E_ChkEnd



    Now we need to change



    bne.s Obj2E_ChkEnd



    to



    bne.w Obj2E_ChkEnd



    so we don't get a branch is too short error.


    Remember when I said we need to branch to the Goggles. Here is the reason why: if we look at any of the monitor codes in ASM it branches to the next monitor code in the line so ChkEggman branches to ChkSonic, ChkSonic branch to ChkShoes and so on and so forth. Also ChkRings branches to ChkS which is empty before we change the code. Eventually all the codes go to the empty ChkS or with this new code the empty ChkGoggles. We do this so we don't mess up the coding of the other monitors or create a continuous loop. In the end the full code should look like this:



    Obj2E_ChkS:
    cmpi.b #7,d0 ; does monitor contain 'S'


    bne.s Obj2E_ChkGoggles


    move.b #1,($FFFFFE2E).w ; speed up the BG music


    move.w #$4B0,($FFFFD034).w ; time limit for the power-up


    move.w #$C00,($FFFFF760).w ; change Sonic's top speed


    move.b #1,($FFFFFE2D).w ; make Sonic invincible


    move.w #$4B0,($FFFFD034).w ; time limit for the power-up


    move.b #$38,($FFFFD200).w ; load stars object ($3801)


    move.b #1,($FFFFD21C).w


    move.b #$38,($FFFFD240).w ; load stars object ($3802)


    move.b #2,($FFFFD25C).w


    move.b #$38,($FFFFD280).w ; load stars object ($3803)


    move.b #3,($FFFFD29C).w


    move.b #$38,($FFFFD2C0).w ; load stars object ($3804)


    move.b #4,($FFFFD2DC).w


    move.w #$87,d0


    jmp (PlaySound).l ; play invincibility music


    Obj2E_ChkGoggles:


    cmpi.b #8,d0 ; does monitor contain 'S'


    bne.w Obj2E_ChkEnd


    Obj2E_ChkEnd:


    rts ; goggles monitor do nothing
     
  2. DanielHall

    DanielHall Well-Known Member Member

    Joined:
    Jan 18, 2010
    Messages:
    860
    Location:
    North Wales
    That's pretty much how I did it, but good job. :)
     
  3. Selbi

    Selbi The Euphonic Mess Member

    Joined:
    Jul 20, 2008
    Messages:
    2,429
    Location:
    Northern Germany
    This can't work, it will give you a Zero Length Branch error. Also, you don't need a worded bne, when it's such a short distance, byte is enough.
     
    Last edited by a moderator: Jul 7, 2010
  4. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    The Zero length branch can only be caused by signed branching iirc
     
  5. shadowbeasts

    shadowbeasts I'm Legend Member

    Joined:
    Jan 5, 2009
    Messages:
    286
    Location:
    Good 'ol USA.
    It works, I'm using this code in my hack and it builds fine. So it works.
     
  6. DanielHall

    DanielHall Well-Known Member Member

    Joined:
    Jan 18, 2010
    Messages:
    860
    Location:
    North Wales
    It does work. I don't see any reason why not, and i used a similar code.
     
  7. theocas

    theocas #! Member

    Joined:
    Apr 10, 2010
    Messages:
    375
    Yeah, I can only say it works as well. If you do get something like a zero-length branch error, just replace it with nop. That'll take care of your problem.
     
  8. DanielHall

    DanielHall Well-Known Member Member

    Joined:
    Jan 18, 2010
    Messages:
    860
    Location:
    North Wales
    I've never bumped into a zero branch error in my whole time of hacking! what is it?
     
  9. theocas

    theocas #! Member

    Joined:
    Apr 10, 2010
    Messages:
    375
    When you have a branch to code that's directly below the code that wants to branch to it. You can basically put a nop below the branch or get rid of the branch to avoid it.


    Most compilers just say it as a warning, but one treats it as an error. I don't remember which one, but I think it was ASM68K.
     
    Last edited by a moderator: Jul 7, 2010
  10. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    I quote...

    bra.w and bra.s.


    bra.w (branch word)


    As it's in word format, when it's assembled, it can branch as many as 7FFF bytes forwards or backwards (Not sure if it's FFFF, might wanna look that up), but considerably further than bra.s


    bra.s (branch signed)


    As it's signed, it's a byte in size, when it's assembled, it can branch as many as only 7F bytes forwards or backwards, but you cannot branch 0 bytes forward, so something like:



    bra.s Location

    Location:



    Wouldn't be possible, though in word format, it would.


    Fun Fact: bra.w is more effective for reaching further places, but bra.s is more effective for being processed more quickly.
     
  11. Selbi

    Selbi The Euphonic Mess Member

    Joined:
    Jul 20, 2008
    Messages:
    2,429
    Location:
    Northern Germany
    While I knew bra.s can't branch as far as bra.w, I didn't knew that there's also signed and unsigned (I always though .s means "short").


    But still, putting a bne.w when the goal is directly below it is not a good idea, a signed one with a nop is probably the smarter idea.


    (Also Markey, bra means BRanch Always and not just BRAnch, as documented here. =P *runs*)
     
  12. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    Hahaha oooh you sly dog.


    Yes indeed it is BRanch Always, also bra.s could infact be "short", I haven't actually looked into names as much as their meanings, appologies if this has mislead anyone (Let this be a reminder to DO YOUR RESEARCH before posting =P)


    Still the meaning and reason as to where the zero length branch occurs is definately true, a tip would be to use bra.s where ever possible, if you cannot use it, THEN use bra.w, this'll probably save you processing time.
     
    Last edited by a moderator: Jul 7, 2010
  13. shobiz

    shobiz Well-Known Member Member

    Joined:
    Aug 11, 2007
    Messages:
    198
    Location:
    Karachi, Pakistan
    Yup, bra.s is short not signed. And the exact reason for why you can't have a zero length short branch, for those who are interested:


    Whenever you use a branch, the displacement is calculated using (address of target label) - (address of branch instruction + 2). So for example, if you have:



    loc_204: bra.w loc_300
    ...


    loc_300: ...



    the branch displacement will be $300 - ($204 + 2) = $FA


    In machine code, short branches have an opcode $60XX, where XX is the displacement, and word branches have an opcode $6000XXXX, where XXXX is the displacement. This implies you can't have a zero displacement short branch, since a zero displacement signals a word branch.


    Of course, this isn't a problem because zero-length branches are useless anyway - just comment out the branch, you don't need it. Also, the exact limits for a short branch are 129 bytes forward or 126 bytes back from the start of the instruction. For a word branch the limits are 32769 bytes forward or 32766 bytes back.
     
    Last edited by a moderator: Jul 8, 2010
Thread Status:
Not open for further replies.