Some changes/fixes for Sonic 1

Discussion in 'Engineering & Reverse Engineering' started by RetroKoH, Sep 4, 2012.

  1. Brainulator


    Regular garden-variety member Member
  2. Alex Field

    Alex Field

    シュート! カオス・エメラルド・ザが消えようとしている! Member
    Downunda, Mobius
    Sonic the Hedgehog 2+, Sonic the Hedgehog 3+
    Specified that both shouldn't be too hard.

    Of note, you could also remove the checks between music/sfx and sfx/specsfx by changing sfx__First to bgm__Last+1, and spec__First to sfx__Last+1. I do plan on updating other guides (Different Songs in Sonic 1) with other, less hackish implementations.
    Last edited: Oct 18, 2021
  3. nineko


    I am the Holy Cat Tech Member
  4. LuigiXHero


    Sonic 1 Character Pak
    Sonic 1 has a bug where if you pause the game on a certain frame the water is shifted over unintentionally causing a gap at the left side, this is in all revision since it's also in all revisions of Sonic 2 (to fix it there go here):

    So in Git Sonic 1 or in Hivebrain 2021 the code is as follows in _incOBJ/1B Water Surface.asm
    Code (Text):
    1. Surf_Action:    ; Routine 2
    2.         move.w    (v_screenposx).w,d1
    3.         andi.w    #$FFE0,d1
    4.         add.w    surf_origX(a0),d1
    5.         btst    #0,(v_framebyte).w
    6.         beq.s    @even        ; branch on even frames
    7.         addi.w    #$20,d1
    9.     @even:
    What we need to do is add a press start check between "add.w surf_origX(a0),d1" and btst #0,(v_framebyte).w like so:
    Code (Text):
    1. Surf_Action:    ; Routine 2
    2.         move.w    (v_screenposx).w,d1
    3.         andi.w    #$FFE0,d1
    4.         add.w    surf_origX(a0),d1
    5.         btst    #bitStart,(v_jpadpress1).w ; is Start button pressed?
    6.         bne.s    @even    ; if yes, branch
    7.         btst    #0,(v_framebyte).w
    8.         beq.s    @even        ; branch on even frames
    9.         addi.w    #$20,d1
    11.     @even:
    This will fix the gap.
    For the old hivebrain disasm go to Obj1B_Action and do the same thing but worse:
    Code (Text):
    1. Obj1B_Action:                ; XREF: Obj1B_Index
    2.         move.w    ($FFFFF700).w,d1
    3.         andi.w    #$FFE0,d1
    4.         add.w    $30(a0),d1
    5.         btst    #7,($FFFFF605).w ; is Start button pressed?
    6.         bne.s    loc_11114    ; if yes, branch
    7.         btst    #0,($FFFFFE05).w
    8.         beq.s    loc_11114
    9.         addi.w    #$20,d1
    11. loc_11114:
    and there you go. Enjoy the finer water surfaces in life.
  5. DeltaWooloo


    Be a boss-man and come to my big and tall man shop Member
    Fix some graphical bubble glitch when drowning in Sonic 1

    I want you to take a look at this image:


    Now see where I circled the issue in pink, you may believe it's intentional, but that little graphic hangs there when you drown, which is quite unlogical. Looking at the code in a standard Hivebrain disassembly appals me:

    Code (Text):
    1. Obj0A_ChkWater:                ; XREF: Obj0A_Index
    2.         move.w    ($FFFFF646).w,d0
    3.         cmp.w    $C(a0),d0    ; has bubble reached the water surface?
    4.         bcs.s    Obj0A_Wobble    ; if not, branch
    5.         move.b    #6,$24(a0)
    6.         addq.b    #7,$1C(a0)
    7.         cmpi.b    #$D,$1C(a0) ; DW: look here
    8.         beq.s    Obj0A_Display ; DW: here
    9.         bra.s    Obj0A_Display; DW: and here
    The issue is they set a check to animation ID D for it to branch instantly to Obj0A_Display, but at the same time, you'll still be branching there, nevertheless making the code pretty redundant if you ask me. So if we check out what animation ID D does in obj0A:

    Code (Text):
    1. byte_14148:    dc.b $E, $FC
    What it does is set a frame that blanks out the art from loading when you drown to 0. To fix it, we would need to set a check, so the animation ID loads in so in Obj0A_ChkWater (or Drown_ChkWater for GitHub/Hivebrain 2021 users), under the second-last line, insert this:

    Code (Text):
    1.         bcs.s    Obj0A_Display ; that would be "bcs.s    Drown_Display" for Git/Hive2021 users
    2.         move.b    #$D,$1C(a0)      ; change $1C to obAnim if you using the disassembly I mentioned above
    This makes sure it loads animation ID D well, so the art will be blank the next time you drown. From there, save, build and test it, and you should get this:


    Well done! You have done and dusted another bug in Sonic 1.
  6. DeltaWooloo


    Be a boss-man and come to my big and tall man shop Member
    Earlier last month, I've had a conversation with vladikcomper to find the best methods to optimize Sonic 1. He came across how it is possible to optimize the BossMove subroutine, which moves the bosses. And after doing my quick analysis by playing GHZ3, I've noticed that the platforms tend to glitch for about a frame or two every time the boss moves. However, upon looking at the code, I saw how quick and easy it was to optimize the code as it shares similar footsteps to two other subroutines: SpeedToPos and ObjectMove.

    So this mini-tutorial will show you how to optimize the BossMove code in vein to S3K. It won't look fast, but it takes less processing time which matters if you intend to have custom bosses with different objects. This will work under any Sonic 1 disassembly. (Sonic 1 Git will be referenced, but it shouldn't be challenging to cross-port the code).

    For Sonic 2 users, I've made a guide for you, so please check it out here.

    Firstly, let's go and check it out:

    Code (Text):
    1. BossMove:
    2.         move.l    $30(a0),d2
    3.         move.l    $38(a0),d3
    4.         move.w    obVelX(a0),d0
    5.         ext.l    d0
    6.         asl.l    #8,d0
    7.         add.l    d0,d2
    8.         move.w    obVelY(a0),d0
    9.         ext.l    d0
    10.         asl.l    #8,d0
    11.         add.l    d0,d3
    12.         move.l    d2,$30(a0)
    13.         move.l    d3,$38(a0)
    14.         rts
    15. ; End of function BossMove
    The way I optimized it was I compared how redhotsonic shortened the subroutine and applied it here. The result you should get is this:

    Code (Text):
    1. BossMove:
    2.         move.w    obVelX(a0),d0        
    3.         ext.l    d0
    4.         lsl.l    #8,d0                
    5.         add.l    d0,$30(a0)  
    6.         move.w    obVelY(a0),d0          
    7.         ext.l    d0
    8.         lsl.l    #8,d0                
    9.         add.l    d0,$38(a0)
    10.         rts  
    11. ; End of function BossMove
    And that's it. Every time the boss moves, it will do the coding quicker in each frame.