Porting Sonic 2's HUD code to Sonic 1

Discussion in 'Tutorials' started by Brighty, Jul 28, 2020.

  1. Brighty

    Brighty Newcomer Member

    Joined:
    May 9, 2013
    Messages:
    15
    Location:
    Somewhere in the United States
    The HUD code in Sonic 1 has it where it's part of the normal object system whereas in Sonic 2, 3 and Knuckles, it's manually written into the priority table and gets called from the sprite rendering routine. To port this over to Sonic 1 (Hivebrain), the old HUD code needs to be removed and to do that, go to Obj21 and you should see something like this:

    Code:
    Obj21:                    ; XREF: Obj_Index
            moveq    #0,d0
            move.b    $24(a0),d0
            move.w    Obj21_Index(pc,d0.w),d1
            jmp    Obj21_Index(pc,d1.w)
    ; ===========================================================================
    Obj21_Index:    dc.w Obj21_Main-Obj21_Index
            dc.w Obj21_Flash-Obj21_Index
    ; ===========================================================================
    
    Obj21_Main:                ; XREF: Obj21_Main
            addq.b    #2,$24(a0)
            move.w    #$90,8(a0)
            move.w    #$108,$A(a0)
            move.l    #Map_obj21,4(a0)
            move.w    #$6CA,2(a0)
            move.b    #0,1(a0)
            move.b    #0,$18(a0)
    
    Obj21_Flash:                ; XREF: Obj21_Main
            tst.w    ($FFFFFE20).w    ; do you have any rings?
            beq.s    Obj21_Flash2    ; if not, branch
            clr.b    $1A(a0)        ; make all counters yellow
            jmp    DisplaySprite
    ; ===========================================================================
    
    Obj21_Flash2:
            moveq    #0,d0
            btst    #3,($FFFFFE05).w
            bne.s    Obj21_Display
            addq.w    #1,d0        ; make ring counter flash red
            cmpi.b    #9,($FFFFFE23).w ; have    9 minutes elapsed?
            bne.s    Obj21_Display    ; if not, branch
            addq.w    #2,d0        ; make time counter flash red
    
    Obj21_Display:
            move.b    d0,$1A(a0)
            jmp    DisplaySprite
    ; ===========================================================================
    
    change it to look like this:

    Code:
    Obj21:                    ; XREF: Obj_Index
            rts
    ; ===========================================================================
    
    You won't need the old HUD code anymore because it's handled by this new routine which I will post below.

    Now for the new HUD code, add this below the rts in the block of code posted above:

    Code:
    ; ---------------------------------------------------------------------------
    ; HUD Object code - SCORE, TIME, RINGS
    ; ---------------------------------------------------------------------------
    loc_40804:
        tst.w    ($FFFFFE20).w
        beq.s    loc_40820
        moveq    #0,d1
        btst    #3,($FFFFFE05).w
        bne.s    BranchTo_loc_40836
        cmpi.b    #9,($FFFFFE23).w
        bne.s    BranchTo_loc_40836
        addq.w    #2,d1
    
    BranchTo_loc_40836
        bra.s    loc_40836
    ; ===========================================================================
    
    loc_40820:
        moveq    #0,d1
        btst    #3,($FFFFFE05).w
        bne.s    loc_40836
        addq.w    #1,d1
        cmpi.b    #9,($FFFFFE23).w
        bne.s    loc_40836
        addq.w    #2,d1
    
    loc_40836:
        move.w    #$90,d3
        move.w    #$108,d2
        lea    (Map_Obj21).l,a1
        movea.w    #$6CA,a3
        add.w    d1,d1
        adda.w    (a1,d1.w),a1
        moveq    #0,d1
        move.b    (a1)+,d1
        subq.b    #1,d1
        bmi.s    return_40858
        jsr    sub_D762
    
    return_40858:
        rts
    ; End of function h
    
    This is the HUD code from S2 slightly modified to work with Sonic 1's mapping system. For the rest of the code, go to BuildSprites and change this part:
    Code:
    BuildSprites:                ; XREF: TitleScreen; et al
            lea    ($FFFFF800).w,a2 ; set address for sprite table
            moveq    #0,d5
            lea    ($FFFFAC00).w,a4
            moveq    #7,d7
    
    to this:

    Code:
    BuildSprites:                ; XREF: TitleScreen; et al
            lea    ($FFFFF800).w,a2 ; set address for sprite table
            moveq    #0,d5
            moveq    #0,d4
            tst.b    ($FFFFFFD0).w ; this was level_started_flag
            beq.s    BuildSprites_2
            jsr    loc_40804
    BuildSprites_2:
            lea    ($FFFFAC00).w,a4
            moveq    #7,d7
    
    For the level_started_flag you can use any unused RAM address for it. I used $FFFFFFD0 for this guide. Go to SegaScreen and then scroll down until you see this line:

    Code:
            move.w    #0,($FFFFF660).w
    
    add this below it:

    Code:
            move.b    #0,($FFFFFFD0).w
    
    Go to Title_ClrPallet and you should see this line somewhere below it:

    Code:
            move.b    #$8A,($FFFFD080).w ; load "SONIC TEAM PRESENTS"    object
    

    add this below it:

    Code:
            move.b    #0,($FFFFFFD0).w
    
    Go to Level and you should see this somewhere below it:

    Code:
            bset    #7,($FFFFF600).w ; add $80 to screen mode (for pre level sequence)
    
    add this below it:

    Code:
            move.b    #0,($FFFFFFD0).w
    
    Further down at loc_3946 you should see this somewhere below the label:

    Code:
            move.b    #$21,($FFFFD040).w ; load HUD object
    
    replace that with this:

    Code:
            move.b    #1,($FFFFFFD0).w
    
    Go to SS_ClrNemRam and find this line below it:

    Code:
            move.b    #9,($FFFFD000).w ; load    special    stage Sonic object
    
    add this below it:

    Code:
            move.b    #0,($FFFFFFD0).w
    
    Go to End_LoadSonic and find this line below it:
    Code:
            move.b    #$21,($FFFFD040).w ; load HUD object
    
    replace it with this:

    Code:
            move.b    #1,($FFFFFFD0).w
    
    Then go to Cred_ClrPallet and find this line below it:

    Code:
            move.b    #$8A,($FFFFD080).w ; load credits object
    
    and add this below it:

    Code:
            move.b    #0,($FFFFFFD0).w
    
    And for the last step (thanks Iso) go to Cont_ClrObjRam and find this line:

    Code:
    move.b    #4,($FFFFD0E4).w
    
    then add this below it:

    Code:
    move.b    #0,($FFFFFFD0).w
    

    You're now done and should have a near perfect port of S2's HUD code to Sonic 1. It also fixes the bug where "Time" does not flash if you have 9 minutes on the timer and have rings. The source code with all steps applied is provided below.
     

    Attached Files:

    Last edited: Feb 8, 2021
    maple_t, Kilo, KCEXE and 6 others like this.
  2. Kilo

    Kilo Foxy Fren Exiled

    Joined:
    Oct 9, 2017
    Messages:
    391
    Location:
    A warm and lovely place~
    Seems you forgot a step!
    upload_2020-11-30_7-22-57.png
    The fix is easy enough, simply go to Cont_ClrObjRam:, and under
    Code:
            move.b    #4,($FFFFD0E4).w
    Clear the HUD draw flag
    Code:
            move.b  #0,($FFFFFFD0).w
     
    Brighty, KCEXE and ProjectFM like this.
  3. Brighty

    Brighty Newcomer Member

    Joined:
    May 9, 2013
    Messages:
    15
    Location:
    Somewhere in the United States
    I can't believe I missed that one but anyways the original post and source code should now be fixed.
     
  4. Techokami

    Techokami For use only on NTSC Genesis Systems. Member

    Joined:
    Oct 29, 2007
    Messages:
    20
    Location:
    LINUX
    Would it be possible to make a version of this guide for the Github disassemblies? Or at least, add some commenting to the code, some actually descriptive labels, and/or replace magic numbers with constants? It's incredibly difficult for people to follow the replacement code in comparison to the original if they're not using the Hivebrain disassembly!

    EDIT: Okay, so I've gotten this working in the GitHub disassembly! I'll be editing this post again with an updated version of your guide in a bit.

    EDIT 2: Here's the guide in OP, modified to work with the GitHub disassembly. First, go to /_incObj/21 HUD.asm and replace the entire contents with the following:
    Code:
    ; ---------------------------------------------------------------------------
    ; Object 21 - SCORE, TIME, RINGS (DEPRECATED)
    ; ---------------------------------------------------------------------------
    
    HUD:
    		rts
    
    ; ---------------------------------------------------------------------------
    ; HUD Object code - SCORE, TIME, RINGS
    ; ---------------------------------------------------------------------------
    loc_40804:
        tst.w    ($FFFFFE20).w
        beq.s    loc_40820
        moveq    #0,d1
        btst    #3,($FFFFFE05).w
        bne.s    BranchTo_loc_40836
        cmpi.b    #9,($FFFFFE23).w
        bne.s    BranchTo_loc_40836
        addq.w    #2,d1
    
    BranchTo_loc_40836
        bra.s    loc_40836
    ; ===========================================================================
    
    loc_40820:
        moveq    #0,d1
        btst    #3,($FFFFFE05).w
        bne.s    loc_40836
        addq.w    #1,d1
        cmpi.b    #9,($FFFFFE23).w
        bne.s    loc_40836
        addq.w    #2,d1
    
    loc_40836:
        move.w    #$90,d3
        move.w    #$108,d2
        lea    (Map_HUD).l,a1
        movea.w    #$6CA,a3
        add.w    d1,d1
        adda.w    (a1,d1.w),a1
        moveq    #0,d1
        move.b    (a1)+,d1
        subq.b    #1,d1
        bmi.s    return_40858
        jsr    (BuildSpr_Normal).l
    
    return_40858:
        rts
    ; End of function h
    This is generally the same as the original guide's, as I haven't replaced all the magic addresses with their proper constants versions. However, I did make two important changes: loading the HUD mappings from the proper location, and using the modern label for what was originally "sub_D762".

    Now, in Variables.asm, we need to define a new variable for when to enable/disable drawing the HUD. This is called "level_started_flag" in the original guide, but here we'll call it "v_level_started_flag". The address used in the original guide was $FFFFFFD0, but if you are using Project Sonic 1: Two Eight, this is used for collision plane management. Instead, we'll use a byte after the player 2 input registers, as there are a bunch of free bytes after it. So, look for:
    Code:
    v_jpadpress1:	equ $FFFFF605	; joypad 1 input - pressed
    ...and add this after it:
    Code:
    v_jpad2hold1:	equ $FFFFF606	; joypad 2 input - held
    v_jpad2press1:	equ $FFFFF607	; joypad 2 input - pressed
    v_level_started_flag: equ $FFFFF608	; Level started flag
    (As an added bonus, now you can track player 2's inputs! Perhaps make your cheat codes trickier by using the second controller?)

    Now, in sonic.asm, go to the label "BuildSprites" and replace this:
    Code:
    BuildSprites:				; XREF: TitleScreen; et al
    		lea	($FFFFF800).w,a2 ; set address for sprite table
    		moveq	#0,d5
    		lea	($FFFFAC00).w,a4
    		moveq	#7,d7
    ...with this:
    Code:
    BuildSprites:                ; XREF: TitleScreen; et al
            lea    ($FFFFF800).w,a2 ; set address for sprite table
            moveq    #0,d5
            moveq    #0,d4
            tst.b    (v_level_started_flag).w ; this was level_started_flag
            beq.s    BuildSprites_2
            jsr    loc_40804
    BuildSprites_2:
            lea    ($FFFFAC00).w,a4
            moveq    #7,d7
    Next, go to the label "GM_Sega" and after this line:
    Code:
    		move.w	#0,(v_pal_buffer+$10).w
    ...add this line:
    Code:
    		move.b	#0,(v_level_started_flag).w
    Next, go to the label "Tit_ClrPal" and after this line:
    Code:
    		move.b	#id_CreditsText,(v_objspace+$80).w ; load "SONIC TEAM PRESENTS" object
    ...add this line:
    Code:
    		move.b	#0,(v_level_started_flag).w
    Next, go to the label "GM_Level" and after this line:
    Code:
    		bset	#7,(v_gamemode).w ; add $80 to screen mode (for pre level sequence)
    ...add this line:
    Code:
    		move.b	#0,(v_level_started_flag).w
    Next, go to the label "Level_SkipTtlCard" and replace this line:
    Code:
    		move.b	#id_HUD,(v_objspace+$40).w ; load HUD object
    ...with this line:
    Code:
    		move.b	#1,(v_level_started_flag).w
    Next, go to the label "SS_ClrNemRam" and after this line:
    Code:
    		move.b	#id_SonicSpecial,(v_player).w ; load special stage Sonic object
    ...add this line:
    Code:
    		move.b	#0,(v_level_started_flag).w
    Next, go to the label "Cont_ClrObjRam" and after this line:
    Code:
    		move.b	#4,(v_objspace+$80+obFrame).w
    ...add this line:
    Code:
    		move.b	#0,(v_level_started_flag).w
    Next, go to the label "End_LoadSonic" and replace this line:
    Code:
    		move.b	#id_HUD,(v_objspace+$40).w ; load HUD object
    ...with this line:
    Code:
    		move.b	#1,(v_level_started_flag).w
    Finally, go to the label "Cred_ClrPal" and after this line:
    Code:
    		move.b	#id_CreditsText,(v_objspace+$80).w ; load credits object
    ...add this line:
    Code:
    		move.b	#0,(v_level_started_flag).w
    That's it! Compile and you should be good to go!
     
    Last edited: Jul 2, 2021
    DeltaWooloo, RandomName and Pacca like this.