don't click here

UPDATED: Speed up the ring loss process (even further) with underwater

Discussion in 'Engineering & Reverse Engineering' started by redhotsonic, Apr 30, 2012.

  1. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    To further improve the speed of the scattered rings object, read this post! This is separate to the guide within this post.




    Warning: This guide is for Sonic 2, and I am using the Xenowhirl 2007 disassembly. For Sonic 1 and/or other disassemblies, it shouldn't be too hard still if you follow this guide. ALSO, REMEMBER TO BACK-UP BEFORE MAKING ANY CHANGES. I'm not held responsible for any screw-ups to your hack.

    Also: If you want your scattered rings to act if they're underwater like Sonic does, it is best to follow this guide first (by me).

    If you want to use the tables I've supplied in this guide, again, you WILL need to follow this guide first


    I bought you the underwater scattered rings guide and SpirituInsanum told us how to speed up the ring loss process. Together, we bring you "How to speed up the ring loss process with underwater".

    Code (ASM):
    1. loc_120BA:
    2.     _move.b #$37,0(a1) ; load obj37
    3.     addq.b  #2,routine(a1)
    4.     move.b  #8,y_radius(a1)
    5.     move.b  #8,x_radius(a1)
    6.     move.w  x_pos(a0),x_pos(a1)
    7.     move.w  y_pos(a0),y_pos(a1)
    8.     move.l  #Obj25_MapUnc_12382,mappings(a1)
    9.     move.w  #$26BC,art_tile(a1)
    10.     bsr.w   Adjust2PArtPointer2
    11.     move.b  #$84,render_flags(a1)
    12.     move.b  #3,priority(a1)
    13.     move.b  #$47,collision_flags(a1)
    14.     move.b  #8,width_pixels(a1)
    15.     move.b  #-1,(Ring_spill_anim_counter).w
    16.     tst.w   d4
    17.     bmi.s   loc_12132
    18.     move.w  d4,d0
    19.     bsr.w   JmpTo4_CalcSine
    20.     move.w  d4,d2
    21.     lsr.w   #8,d2
    22.     asl.w   d2,d0
    23.     asl.w   d2,d1
    24.     move.w  d0,d2
    25.     move.w  d1,d3
    26.     addi.b  #$10,d4
    27.     bcc.s   loc_12132
    28.     subi.w  #$80,d4
    29.     bcc.s   loc_12132
    30.     move.w  #$288,d4
    31.  
    32. loc_12132:
    33.  
    34.     move.w  d2,x_vel(a1)
    35.     move.w  d3,y_vel(a1)
    36.     neg.w   d2
    37.     neg.w   d4
    38.     dbf d5,loc_120B2

    This is part of the scattered rings code. This code repeats itself a maximum of 32 times; each time for each ring (and like said, it will make you scatter a max of 32 rings, but still set your counter to 0). What this code does, is works out how high and how far to scatter a ring, then then whether to make it fly left or right. Once it's done that, it does it again for another ring, and then another, and then another until it either reaches your ring count, or 32. Whilst it's doing this, nothing else can be done. It's not much of a problem when there's not much about, but if you lose a lot of rings when there's so many other objects about and/or in water, it can go slow for a second.



    Anyway, This is quite big and slow, and I'm about to show you how to reduce it significantly; speeding the ring loss process, freeing the processor for other work. Instead of doing lots of calculations on the fly over and over, we will use a pre-made table to insert how high, how far and which way we want our rings to go. This will be a lot quicker as we already have the calculations done this way.


    But how do we come up with our table? Well, I'm about to show you. But first, in this guide, I will be using two pre-made tables myself. These are for a max of 20 rings above water, and 8 rings below water. Also, the ring spill underwater will be using the effect from my other guide. If you're happy to do the same as me, skip ahead to step 4 (although you might want to read the first couple of steps so you know what's going on).





    Step 1 - Change the max amount of rings to spill

    In the scattered rings object, go to "loc_120A2:" and the first line you should see is this:

    Code (ASM):
    1.     moveq   #$20,d0
    This is hexadecimal for 32, this is the max number of rings it will spill. Take the $ sign out (so it's not hexadecimal anymore), and then change the number to the amount of rings you want it to spill at maximum. If you want it to lose a max of 44 rings for example, then simply change the 20 to 44. If you want to change the number of max rings spill for when underwater (as things get slower underwater) we can add a check to see if underwater and if so, to change the number. Say you want to lose 20 rings over water but only 8 when in the water, you would do this:

    Code (ASM):
    1. loc_120A2:
    2.     moveq   #20,d0          ; lose a max of 20 rings
    3.     lea (MainCharacter).w,a2    ; a2=character
    4.     btst    #6,status(a2)       ; is Sonic underwater?
    5.     beq.s   +           ; if not, branch
    6.     moveq   #8,d0           ; lose a max of 8 rings when underwater
    7. +
    8.     cmp.w   d0,d5
    9.     bcs.s   loc_120AA
    10.     move.w  d0,d5
    Make sure the Maincharacter is loaded to a2, otherwise it may interrupt with the rest of our work or the original code.

    You DON'T have to change the max for underwater, but it is advisable, because things get very slow underwater. The more rings there are, the slower things will get, because more processing power goes in to making them bounce and etc. That's purely the only reason.





    Step 2 - Make our tables for pre-calculated figures

    go to "loc_120AA:" and change this:

    Code (ASM):
    1. loc_120AA:
    2.     subq.w  #1,d5
    3.     move.w  #$288,d4
    4.     bra.s   loc_120BA
    to this:

    Code (ASM):
    1. loc_120AA:
    2.     subq.w  #1,d5
    3.     move.w  #$288,d4
    4.     lea ($FFFFAA00).l,a4    ; Load $FFFFAA00 to a4
    5.     bra.s   loc_120BA
    $FFFFAA00 is nemesis's decompression buffer's RAM. It's not used during gameplay, so we can use this. All we've done is copied the RAM address to a4.


    Then go to "loc_12132:" and change this:

    Code (ASM):
    1. loc_12132:
    2.     move.w  d2,x_vel(a1)
    3.     move.w  d3,y_vel(a1)
    4.     neg.w   d2
    5.     neg.w   d4
    6.     dbf d5,loc_120B2
    to this:

    Code (ASM):
    1. loc_12132:
    2.     move.w  d2,x_vel(a1)
    3.     move.w  d3,y_vel(a1)
    4.     neg.w   d2
    5.     neg.w   d4
    6.     move.w  d2,(a4)+    ; Move d2 to a4 then increment a4 by a word
    7.     move.w  d3,(a4)+    ; Move d3 to a4 then increment a4 by a word
    8.     dbf d5,loc_120B2
    Just added two lines here. Basically, once one ring has calculated it's x_vel and y_vel when it's going to spill out, it's copied it's values to a4, then a4 has incremented so that the next value can be added. The code repeats itself for the next ring and again, once it's got it's calculations, it's then copied to a4, and keeps doing this for all rings spilled.





    Step 3 - Build and test. Let's get our new table!

    Save, build and test. Should be no errors. Now, using your emulator (I reccomend Regen), go to the RAM viewer (Tools > RAM viewer) and go to $FFFFAA00. Load any level up. During level loading, the numbers at this RAM address will go crazy, but once the title cards have gone away, it should stop and not do anything (there will still be numbers there).

    Now, collect the max number of rings (or more) and then get hurt and lose your rings. They'll scatter everywhere as usual, but all it's values has just been moved to $FFFFAA00. Ta-dah! There's our new table! What you need to do now is open notepad and write your new values in using words. Give it a label too! Here is a guide on how to do it (mine is a max of 20 rings):

    Code (ASM):
    1. ; ===========================================================================
    2. ; ---------------------------------------------------------------------------
    3. ; Ring Spawn Array
    4. ; ---------------------------------------------------------------------------
    5.  
    6. SpillRingData:  dc.w    $00C4,$FC14, $FF3C,$FC14, $0238,$FCB0, $FDC8,$FCB0 ; 4
    7.                 dc.w    $0350,$FDC8, $FCB0,$FDC8, $03EC,$FF3C, $FC14,$FF3C ; 8
    8.                 dc.w    $03EC,$00C4, $FC14,$00C4, $0350,$0238, $FCB0,$0238 ; 12
    9.                 dc.w    $0238,$0350, $FDC8,$0350, $00C4,$03EC, $FF3C,$03EC ; 16
    10.                 dc.w    $0062,$FE0A, $FF9E,$FE0A, $011C,$FE58, $FEE4,$FE58 ; 20
    11.                 even
    12. ; ===========================================================================
    It goes x_vel, y_vel, x_vel, y_vel, x_vel, y_vel, x_vel, y_vel, etc, etc.


    REMEMBER, if you're doing less rings for underwater, you need to get the max number of rings again, go underwater then get hurt. You'll get another new table. For my underwater, the max is 8. And again, I'm using the underwater scattered rings guide. Here's a guide for 8 rings.

    Code (ASM):
    1. ; ===========================================================================
    2. ; ---------------------------------------------------------------------------
    3. ; Ring Spawn Array Underwater
    4. ; ---------------------------------------------------------------------------
    5.  
    6. SpillRingDataU: dc.w    $0064,$FE08, $FF9C,$FE08, $011C,$FE58, $FEE4,$FE58 ; 4
    7.                 dc.w    $01A8,$FEE4, $FE58,$FEE4, $01F8,$FF9C, $FE08,$FF9C ; 8
    8.                 even
    9. ; ===========================================================================
    10. ; ===========================================================================
    Notice I've changed the label for the underwater table. I've just stuck a U after it. You will need to insert these (or your own, whatever) into your ASM file. A good place to put it, find "BranchTo5_DeleteObject" and stick it after

    Code (ASM):
    1. BranchTo5_DeleteObject
    2.     bra.w   DeleteObject




    Step 4 - Loading our new tables

    Now we've got our new tables, let's use them.

    Go back to "loc_120A2:". This is where we will make it load our new tables. The tables will load into a3. Change it to make it look like this:

    Code (ASM):
    1. loc_120A2:
    2.     lea SpillRingData,a3    ; load the address of the array in a3
    3.     moveq   #20,d0          ; lose a max of 20 rings
    4.     lea (MainCharacter).w,a2    ; a2=character
    5.     btst    #6,status(a2)       ; is Sonic underwater?
    6.     beq.s   +           ; if not, branch
    7.     lea    SpillRingDataU,a3    ; load the UNDERWATER address of the array in a3
    8.     moveq   #8,d0           ; lose a max of 8 rings underwater
    9. +
    10.     cmp.w   d0,d5
    11.     bcs.s   loc_120AA
    12.     move.w  d0,d5
    Basically, it will load the overwater table and make 20 the max rings. It then checks if your underwater and if so, load the underwater table and change the max to 8. If not underwater, it will skip doing that bit and carry on with normal. REMEMBER to change the label and max rings to whatever you gave it. It only does this once every time you lose rings, it doesn't repeat itself unlike the calculations below it.


    Go to "loc_120AA:" and change this:

    Code (ASM):
    1. loc_120AA:
    2.     subq.w  #1,d5
    3.     move.w  #$288,d4
    4.     lea ($FFFFAA00).l,a4    ; Load $FFFFAA00 to a4
    5.     bra.s   loc_120BA
    to this:

    Code (ASM):
    1. loc_120AA:
    2.     subq.w  #1,d5
    3.     bra.s   loc_120BA
    The Nemesis RAM bit isn't needed anymore; that was just for creating our tables. The "move.w #$288,d4" isn't needed anymore either as that's part of the calculations of spilling the rings.





    Step 5 - Using the data from our new tables

    Go to "loc_120BA:"

    Code (ASM):
    1. loc_120BA:
    2.     _move.b #$37,0(a1) ; load obj37
    3.     addq.b  #2,routine(a1)
    4.     move.b  #8,y_radius(a1)
    5.     move.b  #8,x_radius(a1)
    6.     move.w  x_pos(a0),x_pos(a1)
    7.     move.w  y_pos(a0),y_pos(a1)
    8.     move.l  #Obj25_MapUnc_12382,mappings(a1)
    9.     move.w  #$26BC,art_tile(a1)
    10.     bsr.w   Adjust2PArtPointer2
    11.     move.b  #$84,render_flags(a1)
    12.     move.b  #3,priority(a1)
    13.     move.b  #$47,collision_flags(a1)
    14.     move.b  #8,width_pixels(a1)
    15.     move.b  #-1,(Ring_spill_anim_counter).w
    16.     tst.w   d4      ; DELETE ME
    17.     bmi.s   loc_12132   ; DELETE ME
    18.     move.w  d4,d0       ; DELETE ME
    19.     bsr.w   JmpTo4_CalcSine ; DELETE ME
    20.     move.w  d4,d2       ; DELETE ME
    21.     lsr.w   #8,d2       ; DELETE ME
    22.     asl.w   d2,d0       ; DELETE ME
    23.     asl.w   d2,d1       ; DELETE ME
    24.     move.w  d0,d2       ; DELETE ME
    25.     move.w  d1,d3       ; DELETE ME
    26.     addi.b  #$10,d4     ; DELETE ME
    27.     bcc.s   loc_12132   ; DELETE ME
    28.     subi.w  #$80,d4     ; DELETE ME
    29.     bcc.s   loc_12132   ; DELETE ME
    30.     move.w  #$288,d4    ; DELETE ME
    31.  
    32. loc_12132:          ; DELETE ME
    33.     move.w  d2,x_vel(a1)    ; DELETE ME
    34.     move.w  d3,y_vel(a1)    ; DELETE ME
    35.     neg.w   d2      ; DELETE ME
    36.     neg.w   d4      ; DELETE ME
    37.     move.w  d2,(a4)+    ; DELETE ME ; Move d2 to a4 then increment a4 by a word
    38.     move.w  d3,(a4)+    ; DELETE ME ; Move d3 to a4 then increment a4 by a word
    39.     dbf d5,loc_120B2
    See where it says "DELETE ME"? Do what it says. These are not needed anymore! This is what slows the ring loss down! This calculates the ring loss speeds and etc. Instead, we using our tables. Now, where you just deleted that table, insert this instead:

    Code (ASM):
    1.     move.w  (a3)+,x_vel(a1)     ; move the data contained in the array to the x velocity and increment the address in a3
    2.     move.w  (a3)+,y_vel(a1)     ; move the data contained in the array to the y velocity and increment the address in a3
    So, you have something looking like this:

    Code (ASM):
    1. loc_120BA:
    2.     _move.b #$37,0(a1) ; load obj37
    3.     addq.b  #2,routine(a1)
    4.     move.b  #8,y_radius(a1)
    5.     move.b  #8,x_radius(a1)
    6.     move.w  x_pos(a0),x_pos(a1)
    7.     move.w  y_pos(a0),y_pos(a1)
    8.     move.l  #Obj25_MapUnc_12382,mappings(a1)
    9.     move.w  #$26BC,art_tile(a1)
    10.     bsr.w   Adjust2PArtPointer2 ; This is only needed for two player
    11.     move.b  #$84,render_flags(a1)
    12.     move.b  #3,priority(a1)
    13.     move.b  #$47,collision_flags(a1)
    14.     move.b  #8,width_pixels(a1)
    15.     move.b  #-1,(Ring_spill_anim_counter).w
    16.     move.w  (a3)+,x_vel(a1)     ; move the data contained in the array to the x velocity and increment the address in a3
    17.     move.w  (a3)+,y_vel(a1)     ; move the data contained in the array to the y velocity and increment the address in a3
    18.     dbf d5,loc_120B2
    ALL DONE! Phew! Let me explain though. What it does now, is load a word from a3 (the table) and inserts it into x_vel, and then increment a3 by a word. Then it loads a word from a3 again (which has been incremented, so it won't be the same word) and inserts it into y_vel, then increment a3 again. Now one ring has it's speeds. It now knows how high to jump, how far to jump and which way to go, a hell of a lot quicker then doing all them calculations!

    If you have used my tables I've supplied, then when out of water, you can only lose a max of 20 rings, and when in water, you can only lose a max of 8 rings, and it will act if like they're in water.


    The ring loss process will now be much faster! And if in water, it shouldn't slow down (as much)


    Any questions/comments/etc, reply!

    Enjoy!


    Credits

    SpirituInsanum - For the idea and speed loss patterns guide (SSRG only). Also for helping me come up with this guide.
    Me - For underwater scattered rings.



    EDIT: Here is a ROM of Sonic 2 with this guide and my underwater guide combined.
     
  2. Aerosol

    Aerosol

    Not here. Moderator
    11,163
    573
    93
    Not where I want to be.
    Sonic (?): Coming summer of 2055...?
    Fascinating. What if you kept the max for above and below water at 32? Would you still see much of a benefit?
     
  3. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    Underwater, you'd probably wouldn't. This only speeds up the process on creating the rings. To animate them and to keep them moving and to make them bounce hasn't changed. When you enter the water, a lot more sprites appear (bubbles) and the huge code for checking when to drown, when to make that ding in the water, where to spawn the bubbles, etc etc, is running. When you lose a lot of rings, each one of them rings have to check when to bounce, where to move, blah blah blah. The less rings there are, the more power can go into the underwater coding.


    When not in water, you can make it spill more rings, because the underwater coding is not running.


    EDIT: Example: If you got to "Obj0A:" and insert a command to delete the object immediately:

    Code (ASM):
    1. Obj0A:
    2.     bra.w   Deleteobject
    3.     moveq   #0,d0
    4.     move.b  routine(a0),d0
    5.     move.w  Obj0A_States(pc,d0.w),d1
    6.     jmp Obj0A_States(pc,d1.w)
    7. ;   blah, blah, blah
    Underwater will go miles faster, but the small bubbles will never appear and Sonic will never drown =P
     
  4. Good work. Anything that reduces the amount of lag is a plus in my book, it creates a much smoother experience. Nothing is worse than the illusion of speed being broken by a sudden slowdown.
     
  5. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    Thanks. The scattered rings is the biggest cause of slowdowns in all Sonic games and hacks =P
     
  6. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    Double post, but I found out another way you can speed up the ring loss process even more (if anyone cares). I just ported the S3K priority manager into my hack, and you can make the scattered rings object do something extremely similar. Okay, so this is only making one object do it, but because the amount of rings you can lose, this actually makes a BIG difference (as usual, especially underwater).




    Go to "loc_120BA:" (part of the scattered rings object) and delete this line:

    Code (ASM):
    1.     move.b  #3,priority(a1)

    When the rings are being created, it won't have to move 3 to it's priority anymore, saving a command per ring created and slightly speeding it up (you'll probably won't notice any difference, but this isn't what I am trying to accomplish here).


    Now you're probably thinking "WTF are you thinking you crazy boy? The rings aren't going to be displayed now!" Well, in a way, you're wrong. It will still be displayed, but with a priority of 0. Now, we do not want that, we still want it to be 3, and we're about to fix it, and this will be what speeds it up dramatically.




    Go to "loc_121B8" and you should see a command:

    Code (ASM):
    1.     bra.w   DisplaySprite

    We are going to replace this with a new code, similar to how S3K does it. In S2, before it can display the sprite, it convert's the object's priority into a word, and then displays it. It does calculations that are about 3 lines long to convert it into a word. When lots of objects use this code every single frame (Sonic and Tails constantly for example), it can be a slow process.


    Now, imagine you just got hurt and lost 32 rings, each one of them 32 rings branches to DisplaySprite, does the calculations, then displays the sprite; every single frame! All 32 of them! This slows it down quite a bit! Now, you can't just turn the scattered ring's priority into a word, otherwise it will over-write the scattered rings's "width_pixel". So, what do we do? Well, we can just copy part of the DisplaySprite's coding and insert it into the scattered rings' object coding.


    Now, normally, after the rings have jumped to DisplaySprite to convert it's priority into a word, it becomes $180. Look at this:

    Priority = 3 (byte)

    Code (ASM):
    1. DisplaySprite:
    2.     lea (Sprite_Table_Input).w,a1
    3.     move.w  priority(a0),d0         ; To be deleted
    4.     lsr.w   #1,d0               ; To be deleted
    5.     andi.w  #$380,d0            ; To be deleted
    6.     adda.w  d0,a1               ; To be changed
    7.     cmpi.w  #$7E,(a1)
    8.     bcc.s   return_16510
    9.     addq.w  #2,(a1)
    10.     adda.w  (a1),a1
    11.     move.w  a0,(a1)
    12.  
    13. return_16510:
    14.     rts
    Now, Priority = $180 (word)


    So, we're going to copy this code, then edit it a bit, then move it to the scattered rings object. So copy it, then move it to where the branch was in the scattered rings object. So you have something looking like this:


    WAS:

    [ASM]loc_121B8:

    tst.b (Ring_spill_anim_counter).w
    beq.s BranchTo5_DeleteObject
    move.w (Camera_Max_Y_pos_now).w,d0
    addi.w #$E0,d0
    cmp.w y_pos(a0),d0
    bcs.s BranchTo5_DeleteObject
    bra.w DisplaySprite[/ASM]


    And change that to this:

    Code (ASM):
    1. loc_121B8:
    2.  
    3.     tst.b   (Ring_spill_anim_counter).w
    4.     beq.s   BranchTo5_DeleteObject
    5.     move.w  (Camera_Max_Y_pos_now).w,d0
    6.     addi.w  #$E0,d0
    7.     cmp.w   y_pos(a0),d0
    8.     bcs.s   BranchTo5_DeleteObject
    9.     lea (Sprite_Table_Input).w,a1
    10.     move.w  priority(a0),d0         ; To be deleted
    11.     lsr.w   #1,d0               ; To be deleted
    12.     andi.w  #$380,d0            ; To be deleted
    13.     adda.w  d0,a1               ; To be changed
    14.     cmpi.w  #$7E,(a1)
    15.     bcc.s   +
    16.     addq.w  #2,(a1)
    17.     adda.w  (a1),a1
    18.     move.w  a0,(a1)
    19. +
    20.     rts

    "To be changed" means that line is going to be edited and the "To be deleted", well, guess what we will do with that? =P




    Change

    Code (ASM):
    1.     adda.w  d0,a1               ; To be changed

    To this:

    Code (ASM):
    1.     adda.w  #$180,a1

    As we now know 3 would equal $180, we can just add $180 straight to a1. No more calculations every single frame, we've already done it!




    You should have something like this now:

    Code (ASM):
    1. loc_121B8:
    2.  
    3.     tst.b   (Ring_spill_anim_counter).w
    4.     beq.s   BranchTo5_DeleteObject
    5.     move.w  (Camera_Max_Y_pos_now).w,d0
    6.     addi.w  #$E0,d0
    7.     cmp.w   y_pos(a0),d0
    8.     bcs.s   BranchTo5_DeleteObject
    9.     lea (Sprite_Table_Input).w,a1
    10.     adda.w  #$180,a1
    11.     cmpi.w  #$7E,(a1)
    12.     bcc.s   +
    13.     addq.w  #2,(a1)
    14.     adda.w  (a1),a1
    15.     move.w  a0,(a1)
    16. +
    17.     rts

    So, what have we done? Well, we saved a line of it branching somewhere for a start, it's already there, so that's a plus! Also, we're moving $180 straight to a1, rather than doing them 3 lines of calculations! What a time saver! ALL done. That was easy, eh? Now your scattered rings will be even quicker, hopefully not slowing down anything!


    Enjoy.








    EDIT

    This is optional, you do not have to do this if you do not like

    If you look below the code you just edited:

    Code (ASM):
    1. Obj_37_sub_4:
    2.     addq.b  #2,routine(a0)
    3.     move.b  #0,collision_flags(a0)
    4.     move.b  #1,priority(a0)
    5.     bsr.w   sub_11FC2
    6.  
    7. Obj_37_sub_6:
    8.     lea (byte_1237A).l,a1
    9.     bsr.w   AnimateSprite
    10.     bra.w   DisplaySprite
    11. ; ===========================================================================
    12.  
    13. BranchTo5_DeleteObject
    14.     bra.w   DeleteObject


    You can see a command move 1 to priority, and displaying it again. This is for when you collect the rings (when the rings turn into them sparkly effects). You can do something extremely similar here if you like. It will make quite a bit of a difference if you collect a lot of scattered rings at the same time, otherwise, it won't do too much. If you want it to do the same, then you can. But it won't be $180 again! When 1 has been through DisplaySprite's calculations, it will equal $80 instead!


    So, at "Obj_37_sub_4:", delete this line/command:

    Code (ASM):
    1.     move.b  #1,priority(a0)

    Then, at "Obj_37_sub_6:", replace:

    Code (ASM):
    1.     bra.w   DisplaySprite

    with this:

    Code (ASM):
    1.     lea (Sprite_Table_Input).w,a1
    2.     adda.w  #$80,a1
    3.     cmpi.w  #$7E,(a1)
    4.     bcc.s   +
    5.     addq.w  #2,(a1)
    6.     adda.w  (a1),a1
    7.     move.w  a0,(a1)
    8. +
    9.     rts


    So you have something like this:

    Code (ASM):
    1. Obj_37_sub_4:
    2.     addq.b  #2,routine(a0)
    3.     move.b  #0,collision_flags(a0)
    4.     bsr.w   sub_11FC2
    5.  
    6. Obj_37_sub_6:
    7.     lea (byte_1237A).l,a1
    8.     bsr.w   AnimateSprite
    9.     lea (Sprite_Table_Input).w,a1
    10.     adda.w  #$80,a1
    11.     cmpi.w  #$7E,(a1)
    12.     bcc.s   +
    13.     addq.w  #2,(a1)
    14.     adda.w  (a1),a1
    15.     move.w  a0,(a1)
    16. +
    17.     rts


    Done, you've made it slightly faster again!


    As usual, here is a S2 ROM with this guide applied. It also has this guide and this guide applied.



    Cheers,
    redhotsonic






    EDIT at 15:41 GMT: EDIT: I realised I accidently left a branch in the guide which I have fixed in the guide.

    Accidently, I put this:
    Code (ASM):
    1. loc_121B8:
    2.  
    3.     tst.b   (Ring_spill_anim_counter).w
    4.     beq.s   BranchTo5_DeleteObject
    5.     move.w  (Camera_Max_Y_pos_now).w,d0
    6.     addi.w  #$E0,d0
    7.     cmp.w   y_pos(a0),d0
    8.     bcs.s   BranchTo5_DeleteObject
    9.     bra.w   DisplaySprite           ; I accidently left this here
    10.     lea (Sprite_Table_Input).w,a1
    11.     adda.w  #$180,a1
    12.     cmpi.w  #$7E,(a1)
    13.     bcc.s   +
    14.     addq.w  #2,(a1)
    15.     adda.w  (a1),a1
    16.     move.w  a0,(a1)
    17. +
    18.     rts

    SO MAKE SURE YOUR ASM FILE ACTUALLY SAYS THIS:

    Code (ASM):
    1. loc_121B8:
    2.  
    3.     tst.b   (Ring_spill_anim_counter).w
    4.     beq.s   BranchTo5_DeleteObject
    5.     move.w  (Camera_Max_Y_pos_now).w,d0
    6.     addi.w  #$E0,d0
    7.     cmp.w   y_pos(a0),d0
    8.     bcs.s   BranchTo5_DeleteObject
    9.     lea (Sprite_Table_Input).w,a1
    10.     adda.w  #$180,a1
    11.     cmpi.w  #$7E,(a1)
    12.     bcc.s   +
    13.     addq.w  #2,(a1)
    14.     adda.w  (a1),a1
    15.     move.w  a0,(a1)
    16. +
    17.     rts
     
  7. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    Lemme know when this is 100% and I'll cook up another wiki page.
     
  8. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    It is 100% =P
     
  9. Aerosol

    Aerosol

    Not here. Moderator
    11,163
    573
    93
    Not where I want to be.
    Sonic (?): Coming summer of 2055...?
    You sure you won't find another way to speed up ring loss?
     
  10. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    If he does, I'll just amend the wiki. No prob.

    EDIT: Here you go. NOW WHY THE HELL IS MY WIKI EDITS COUNT STILL AT 1??? This is at least the third guide I've put on the wiki... not to mention the other edits I've done throughout the wiki...
     
  11. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    There is one more, but it's hardly worth it. You wouldn't notice any difference.




    Cheers, mate. Yeah, it says you've only made 1. Maybe you should PM a member of the site staff to get it resolved.
     
  12. qiuu

    qiuu

    Tech Member
    144
    9
    18
    Blue Ball & Blocks
    Just a little note on these two lines:

    Code (ASM):
    1.         lea     (Sprite_Table_Input).w,a1
    2.         adda.w  #$180,a1
    As Sprite_Table_Input is just a constant value (namely the address $FFFFAC00), you can do this calculation during compile-time rather than run-time by writing

    Code (ASM):
    1.         lea     (Sprite_Table_Input+$180).w,a1
    or just
    Code (ASM):
    1.         lea     Sprite_Table_Input+$180,a1
    instead of the two lines above. Sprite_Table_Input+$180 is the same as $FFFFAC00+$180 which is the same as $FFFFAD80 (writing either would work).
    It won't make a notable difference performance-wise, but it doesn't hurt to know I guess, and might make the code a tiny bit nicer to read.

    (btw, the ().w just tells the compiler to address it as a word sized value, which works because $FFFFAC00 interpreted as signed value is -$5400 which is in the range [-8000,$7FFF]. Leaving it out works just as well, and if the compiler does some optimization, it will even produce the same bytecode taking the same amount of CPU cycles to run.)
     
  13. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber




    That's one of them. The reason why I didn't put it in the guide before is because when I tried it before, it froze. So I put in the guide the other way. But then I realised the reason why it froze was because I forgot to put the $ sign in it lol
     
  14. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    I'll add this to the page now. I believe this is in part 2...
    BTW RHS, look on the wiki at Part 2.5... I copied and pasted from the topic to the wiki so I'm positive this isn't a typo on my end.

    Is it me, or did you make a mistake typing this:
    Code (ASM):
    1.        
    2. lea     (Sprite_Table_Input).w,a1
    3. adda.w  #$80,a1
    4. cmpi.w  #$7E,(a1)
    5.  
    Instead of this:
    Code (ASM):
    1.  
    2. lea     (Sprite_Table_Input).w,a1
    3. adda.w  #$180,a1
    4. cmpi.w  #$7E,(a1)
    5.  
    I wasn't sure but it looks like a typo to me... I'll wait before changing anything, just lemme know. BTW this post editor is a bastard... I've had to edit the asm portion of my post 8 times now...
     
  15. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    I'm double posting cuz I don't wanna deal with reediting my previous post another 8 times... sorry.

    I fixed up part 2 of the guide. Can someone please make sure I got the edit in Part 2 correct? Thanks. :D
     
  16. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    It's no typo. If you read the bit above it (even in the wiki)

    =P
     
  17. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    Well, alas. That's what I get for copy/pasting...
     
  18. Cyber Axe

    Cyber Axe

    Oldbie
    Thanks for this writeup, and in my apparent tradition of bumping old threads, thought i'd add some info to help people who are wanting to implement it in Sonic 1 Github (Which I have just done)

    1. MainCharacter: Equates to v_16x16
    2. Ring_spill_anim_counter: v_ani3_time
    3. BranchTo5_DeleteObject: RLoss_Delete
    4. Camera_Max_Y_pos_now: v_limitbtm2
    5. y_pos: obY
    6. Sprite_Table_Input: v_spritequeue
    7. loc_120A2: 3 Lines after RLoss_Count: (The Line after move.w (v_rings).w,d5) is where loc_120A2: should be
    8. loc_120AA: is @belowmax:
    9. loc_120BA: is @makerings
    10. loc_12132: is @loc_9D62
    11. Obj_37_sub_4: RLoss_Collect
    12. Obj_37_sub_6: RLoss_Sparkle

    And i also have a small optimisation to add as well:

    At loc_120A2: you're loading the array address then setting the max scatter then doing an if to check if we're under water and if we are then we load the array again and scatter max again.

    Correct me if i'm wrong but a slightly more efficient way is to check if we're underwater first and if we are jump down to set the underwater array and max and if we're not continue onto regular scatter and max then jump to the end of the set underwater max code.

    That way we avoid doing the same task twice if underwater.

    Here's the example from my implementation.

    Code (Text):
    1.  
    2.     loc_120A2:
    3.         lea (v_objspace).w,a2                                    ; a2=character
    4.         btst #6,status(a2)                                  ; is Sonic underwater?
    5.         bne.s @underwater_Scatter                            ; if so, branch
    6.  
    7.         lea SpillRingData32,a3                                ; load the address of the array in a3
    8.         moveq    #max_ring_scatter,d0                            ; scatter a max of 32 rings
    9.         bra.s @decrease_max
    10.  
    11.     @underwater_Scatter:
    12.         lea SpillRingData16,a3                                    ; load the UNDERWATER address of the array in a3
    13.         moveq   #(max_ring_scatter/2),d0        ; lose a max of half the rings when underwater
    14.  
    15.     @decrease_max:
    16.         cmp.w    d0,d5                                                            ; do you have 32 or more?
    17.         bcs.s    @belowmax                                                    ; if not, branch
    18.         move.w    d0,d5                                                        ; if yes, set d5 to 32
    19.  
    Surely you don't recognise how anal most programmers are, especially ones that deal with ASM! :P, though I see it was stated later.
     
    Last edited: Jun 28, 2020
  19. Devon

    Devon

    Down you're going... down you're going... Tech Member
    1,218
    1,373
    93
    your mom
    Actually, in Sonic 1, that's the level block data buffer. Object slots start at (with Sonic's object slot being located at) $FFFFD000.
     
  20. Cyber Axe

    Cyber Axe

    Oldbie
    Ty, I have updated appropriately.
     
    Last edited: Jun 28, 2020