How to decrease processing time for Sonic 2's MarkObjGone routines

Discussion in 'Tutorials Archive' started by ThomasThePencil, Feb 27, 2013.

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

    ThomasThePencil resident psycho Member

    Joined:
    Jan 29, 2013
    Messages:
    910
    Location:
    the united states. where else?
    Since many people don't seem to be able to delete lag completely in their hacks, I've decided to take up the responsibility of assisting them in doing so.

    Note that this guide is not necessarily complete yet. it is updated frequently with new tips and tricks for reducing lag in various aspects of Sonic 2.

    Just so people know, this guide contains other routines as well.

    Table of contents:
    Step 1: MarkObjGone routines
    Step 2: RunObjectDisplayOnly (S3K Priority Manager users only)
    Step 3: Sonic and Tails' display subroutines

    Step 4: Some jump stuff


    Step 1: MarkObjGone routines
    Initially, when you look at the MarkObjGone routines, the bottom of each shows this bit of code:


    +
    lea (Object_Respawn_Table).w,a2
    moveq #0,d0
    move.b respawn_index(a0),d0
    beq.s +
    bclr #7,2(a2,d0.w)
    +
    bra.w DeleteObject
    SEGA might have originally thought this was better, but it actually is not needed; it makes more sense to directly branch to DeleteObject instead of using a +, making the code slightly shorter and therefore freeing ever-so-slightly more processing time. So change each occurence in the five MarkObjGone routines to this:
     


    +
    lea (Object_Respawn_Table).w,a2
    moveq #0,d0
    move.b respawn_index(a0),d0
    beq.w DeleteObject
    bclr #7,2(a2,d0.w)
    bra.w DeleteObject
    The routines themselves are as follows:
    - MarkObjGone (search for ObjectMove and it's the routine right under it)
    - MarkObjGone2 (below the previous one)
    - MarkObjGone3(below the previous one)
    - MarkObjGone_P1(below the previous one)
    - MarkObjGone_P2(below the previous one)

    This may seem small and you may ask yourself, "I see no difference! What does this do?" I answer with this: If you've made a section in a level where a LOT of objects must be destroyed at one time (or if you're like me and love to destroy the hell out of enemies in debug mode by going Super), this really helps to reduce lag during that time.

    Thankfully, you can rest assured this is safe, as I have broken many an object testing this and there are NO problems here.

    Step 2: RunObjectDisplayOnly
    If you didn't port the S3K priority manager to your hack using redhotsonic's guide, you can head on to step 3 and skip this step.
    So you're an S3K Priority Manager user? Let's get started.
    Seems as if the RunObjectDisplayOnly routine could use some work as well. First, let's take a look at the routine itself:
     


    RunObjectDisplayOnly:
    moveq#0,d0; Clear d0 quickly
    move.b (a0),d0 ; get the object's ID
    beq.s++ ; if it's obj00, skip it
    tst.brender_flags(a0); should we render it?
    bpl.s++ ; if not, skip it
    move.w priority(a0),d0 ; move object's priority to d0
    btst#6,render_flags(a0) ; is the compound sprites flag set?
    beq.s+; if not, branch
    move.w #$200,d0; move $200 to d0
    +
    bsr.wDisplaySprite3 ; Display the object with whatever is set at d0
    +
    lea next_object(a0),a0 ; load 0bj address
    dbf d7,RunObjectDisplayOnly
    rts
    ; End of function RunObjectDisplayOnly
    The problem here comes from this bit, which determines whether we should move #$200 to d0 and if not, display the sprite, as follows:
     


    btst #6,render_flags(a0) ; is the compound sprites flag set?
    beq.s+; if not, branch
    move.w #$200,d0; move $200 to d0
    +
    bsr.wDisplaySprite3 ; Display the object with whatever is set at d0
    This appears to be a bit too long. Like last time, we must make it branch directly to its destination.So change the routine to this:
     


    RunObjectDisplayOnly:

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

    However, if one closely observes, there are some issues with this; particularly, if you die at some points in the game, such as right before the EHZ boss and near a CPZ floating platfom. This is being looked into.


    Step 3: Sonic and Tails' display subroutines
    Okay. So we have two routines shortened. But did you know that something similar can be done with Sonic and Tails' display subroutines? Look at Sonic's:
     


    ; loc_1A0C6:
    Sonic_Display:
    move.w invulnerable_time(a0),d0
    beq.s Obj01_Display
    subq.w #1,invulnerable_time(a0)
    lsr.w #3,d0
    bcc.s Obj01_ChkInvin
    ; loc_1A0D4:
    Obj01_Display:
    jsr (DisplaySprite).l
    Since Obj01_Display is used for branching to DisplaySprite, this can ALSO be shortened, freeing up processing time yet again! So change it to this:
     


    ; loc_1A0C6:
    Sonic_Display:
    move.w invulnerable_time(a0),d0
    beq.w DisplaySprite
    subq.w #1,invulnerable_time(a0)
    lsr.w #3,d0
    bcc.s Obj01_ChkInvin
    jsr (DisplaySprite).l
    This should shorten the amount of time needed for Sonic's display subroutine. You may want to do the same with Tails to reduce lag even further for a Sonic and Tails game. A side effect of this, for those using the high jump monitor, is that the high jump effect never wears off. This is also being looked into.

    Step 4: Some jump stuff

    Now we're headed to Sonic_JumpAngle. Say hello to this block of code:


    ; ---------------------------------------------------------------------------
    ; Subroutine to return Sonic's angle to 0 as he jumps
    ; ---------------------------------------------------------------------------

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

    ; loc_1AE4A:
    Sonic_JumpAngle:
    move.b angle(a0),d0 ; get Sonic's angle
    beq.s Sonic_JumpFlip ; if already 0, branch
    bpl.s loc_1AE5A ; if higher than 0, branch

    addq.b #2,d0 ; increase angle
    bcc.s BranchTo_Sonic_JumpAngleSet
    moveq #0,d0

    BranchTo_Sonic_JumpAngleSet
    bra.s Sonic_JumpAngleSet
    ; ===========================================================================

    loc_1AE5A:
    subq.b #2,d0 ; decrease angle
    bcc.s Sonic_JumpAngleSet
    moveq #0,d0

    ; loc_1AE60:
    Sonic_JumpAngleSet:
    move.b d0,angle(a0)
    ; End of function Sonic_JumpAngle
    ; continue straight to Sonic_JumpFlip

    ; ---------------------------------------------------------------------------
    ; Updates Sonic's secondary angle if he's tumbling
    ; ---------------------------------------------------------------------------

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

    ; loc_1AE64:
    Sonic_JumpFlip:
    move.b flip_angle(a0),d0
    beq.s return_1AEA8
    tst.w inertia(a0)
    bmi.s Sonic_JumpLeftFlip
    ; loc_1AE70:
    Sonic_JumpRightFlip:
    move.b flip_speed(a0),d1
    add.b d1,d0
    bcc.s BranchTo_Sonic_JumpFlipSet
    subq.b #1,flips_remaining(a0)
    bcc.s BranchTo_Sonic_JumpFlipSet
    move.b #0,flips_remaining(a0)
    moveq #0,d0

    BranchTo_Sonic_JumpFlipSet
    bra.s Sonic_JumpFlipSet
    ; ===========================================================================
    ; loc_1AE88:
    Sonic_JumpLeftFlip:
    tst.b flip_turned(a0)
    bne.s Sonic_JumpRightFlip
    move.b flip_speed(a0),d1
    sub.b d1,d0
    bcc.s Sonic_JumpFlipSet
    subq.b #1,flips_remaining(a0)
    bcc.s Sonic_JumpFlipSet
    move.b #0,flips_remaining(a0)
    moveq #0,d0
    ; loc_1AEA4:
    Sonic_JumpFlipSet:
    move.b d0,flip_angle(a0)

    return_1AEA8:
    rts
    ; End of function Sonic_JumpFlip
    Think about it: Are the BranchTo's really necessary? NO. So change it to this:


    ; ---------------------------------------------------------------------------
    ; Subroutine to return Sonic's angle to 0 as he jumps
    ; ---------------------------------------------------------------------------

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

    ; loc_1AE4A:
    Sonic_JumpAngle:
    move.b angle(a0),d0 ; get Sonic's angle
    beq.s Sonic_JumpFlip ; if already 0, branch
    bpl.s loc_1AE5A ; if higher than 0, branch

    addq.b #2,d0 ; increase angle
    bcc.s Sonic_JumpAngleSet
    moveq #0,d0
    bra.s Sonic_JumpAngleSet
    ; ===========================================================================

    loc_1AE5A:
    subq.b #2,d0 ; decrease angle
    bcc.s Sonic_JumpAngleSet
    moveq #0,d0

    ; loc_1AE60:
    Sonic_JumpAngleSet:
    move.b d0,angle(a0)
    ; End of function Sonic_JumpAngle
    ; continue straight to Sonic_JumpFlip

    ; ---------------------------------------------------------------------------
    ; Updates Sonic's secondary angle if he's tumbling
    ; ---------------------------------------------------------------------------

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

    ; loc_1AE64:
    Sonic_JumpFlip:
    move.b flip_angle(a0),d0
    beq.s return_1AEA8
    tst.w inertia(a0)
    bmi.s Sonic_JumpLeftFlip
    ; loc_1AE70:
    Sonic_JumpRightFlip:
    move.b flip_speed(a0),d1
    add.b d1,d0
    bcc.s Sonic_JumpFlipSet
    subq.b #1,flips_remaining(a0)
    bcc.s Sonic_JumpFlipSet
    move.b #0,flips_remaining(a0)
    moveq #0,d0
    bra.s Sonic_JumpFlipSet
    ; ===========================================================================
    ; loc_1AE88:
    Sonic_JumpLeftFlip:
    tst.b flip_turned(a0)
    bne.s Sonic_JumpRightFlip
    move.b flip_speed(a0),d1
    sub.b d1,d0
    bcc.s Sonic_JumpFlipSet
    subq.b #1,flips_remaining(a0)
    bcc.s Sonic_JumpFlipSet
    move.b #0,flips_remaining(a0)
    moveq #0,d0
    ; loc_1AEA4:
    Sonic_JumpFlipSet:
    move.b d0,flip_angle(a0)

    return_1AEA8:
    rts
    ; End of function Sonic_JumpFlip


    There are no problems here, unlike the previous two we did. If only they would play ball. And this is possible for all characters, since they each use their own version of this routine (for example, Knuckles uses Knuckles_JumpAngle). All you need to do is get rid of the BranchTo's in the code for the character you want to do this for, then replace all references to the BranchTo's with the actual label itself that was being branched to. Simple, yes?

    This doesn't do much, but it's still useful for freeing up processing time.

    That's really it for now. Check back constantly to see if more routines are added, as I'm constantly updating this guide with more routines and more fixes for problems encountered with this guide. Also, don't hesitate to tell me if there is a bug I haven't picked up on.
     
    Last edited by a moderator: Mar 3, 2013
    Nat The Porcupine likes this.
  2. MainMemory

    MainMemory Well-Known Member Member

    Joined:
    Mar 29, 2011
    Messages:
    922
    In the original code, the branch to DeleteObject is always taken no matter the result of the beq.s before, in your edit it's only branched to if the beq is taken.
     
  3. ThomasThePencil

    ThomasThePencil resident psycho Member

    Joined:
    Jan 29, 2013
    Messages:
    910
    Location:
    the united states. where else?
    Oh. That would probably cause problems. Fixed by replacing the rts with a branch to DeleteObject.
     
  4. ThomasThePencil

    ThomasThePencil resident psycho Member

    Joined:
    Jan 29, 2013
    Messages:
    910
    Location:
    the united states. where else?
    Note the addition of Step 4. I noticed that there was a similar problem there as was in the previous three. Except this time, it was on a bit of a larger scale, so I decided to put it in my guide so people knew of it.

    EDIT: Why am I pretty much the only one who posts in this topic?
     
    Last edited by a moderator: Mar 4, 2013
Thread Status:
Not open for further replies.