Basic Questions and Answers Thread

Discussion in 'Discussion & Q&A' started by Malevolence, Jul 7, 2009.

  1. Samey

    Samey Le Bored Hedgie Member

    May 3, 2017
    Ah I see.

    So in otherwords I'm doing the animation completely wrong, thanks! XD

    Edit: Looking through the code to see where specifically it increments Sonic's ground velocity, but I'm not really seeing it.

    btw, I'm using the Hivebrain disassembly (sorry for not mentioning it before :oops:)
    I'm mainly looking through the Sonic 1 port of the spindash as it has alot more comments than the sonic 2 disassembly I have on my computer atm, and the only thing I think has to do with velocity is this table:

            dc.w  $800        ; 0
            dc.w  $880        ; 1
            dc.w  $900        ; 2
            dc.w  $980        ; 3
            dc.w  $A00        ; 4
            dc.w  $A80        ; 5
            dc.w  $B00        ; 6
            dc.w  $B80        ; 7
            dc.w  $C00        ; 8
    also @Iso Kilo I already had the actual peelout running animation working fine.
    Was just having trouble getting the actual charging animation to rotate.
    Last edited: Jun 4, 2019
  2. ralakimus

    ralakimus Mommy says I'm just built different Member

    Aug 26, 2013
    When I said "spindash", I meant Sonic CD's, not Sonic 2's.

    For reference, here's how Sonic CD handles the peelout and spindash. Read the comments in the code for the fine details.

    At the start of the Sonic object code, it handles the peelout/spindash timer like so:
    ROM:00203FA2 Obj01:
    ROM:00203FA2                 tst.b   ($FF0F01).l        ; Are we in time attack mode?
    ROM:00203FA8                 bne.s   Obj01_ChkCharge        ; If so, branch
    ROM:00203FAA                 cmpa.w  #$D040,a0            ; Are we loaded in object slot 2?
    ROM:00203FAE                 beq.s   Obj01_ChkCharge        ; If so, branch
    ROM:00203FB0                 tst.b   ($FF1906).l        ; Are we in debug object placement mode?
    ROM:00203FB6                 beq.s   Obj01_ChkCharge        ; If not, branch
    ROM:00203FB8                 jmp     DebugMode            ; Go handle the debug placement mode
    ROM:00203FBE ; ---------------------------------------------------------------------------
    ROM:00203FBE Obj01_ChkCharge:
    ROM:00203FBE                 move.b  $2A(a0),d0            ; Get charge timer
    ROM:00203FC2                 beq.s   Obj01_RunRoutine        ; If it's 0, branch
    ROM:00203FC4                 addq.b  #1,d0            ; Increment the charge timer
    ROM:00203FC6                 btst    #2,$22(a0)            ; Are we spindashing?
    ROM:00203FCC                 beq.s   Obj01_ChkPeelout        ; If not, branch
    ROM:00203FCE                 cmpi.b  #45,d0            ; Is the spindash charge timer at max?
    ROM:00203FD2                 bcs.s   Obj01_SetChargeTimer    ; If not, branch
    ROM:00203FD4                 move.b  #45,d0            ; Cap the timer
    ROM:00203FD8                 bra.s   Obj01_SetChargeTimer    ; ''
    ROM:00203FDA ; ---------------------------------------------------------------------------
    ROM:00203FDA Obj01_ChkPeelout:
    ROM:00203FDA                 cmpi.b  #30,d0            ; Is the peelout charge timer at max?
    ROM:00203FDE                 bcs.s   Obj01_SetChargeTimer    ; If not, branch
    ROM:00203FE0                 move.b  #30,d0            ; Cap the timer
    ROM:00203FE4 Obj01_SetChargeTimer:
    ROM:00203FE4                 move.b  d0,$2A(a0)            ; Set the charge timer
    ROM:00203FE8 Obj01_RunRoutine:
    ROM:00203FE8                 moveq   #0,d0
    ROM:00203FEA                 move.b  $24(a0),d0            ; Get routine ID
    ROM:00203FEE                 move.w  Obj01_Index(pc,d0.w),d1    ; Go to the correct object routine
    ROM:00203FF2                 jmp     Obj01_Index(pc,d1.w)    ; ''
    Sonic's main rolling mode routine adds a check to see if Sonic is spindashing, and if he isn't, then prevent movement:
    ROM:00204642 Obj01_MdRoll:
    ROM:00204642                 bsr.w   Obj01_Handle3DLoop        ; Handle 3D loop movement
    ROM:00204646                 bsr.w   Obj01_ManageTimeTravel    ; Manage time travel
    ROM:0020464A                 bsr.w   Sonic_Jump            ; Check for jumping
    ROM:0020464E                 bsr.w   Sonic_RollRepel        ; Apply repelling while on a slope
    ROM:00204652                 bsr.w   Sonic_RollSpeed        ; Handle rolling deceleration
    ROM:00204656                 bsr.w   Sonic_LevelBound        ; Check for level boundary collision
    ROM:0020465A                 tst.b   $2A(a0)            ; Are we charging a spindash?
    ROM:0020465E                 bne.s   Obj01_NoRollCtrl        ; If so, don't allow movement
    ROM:00204660                 jsr     Sonic_SpeedToPos        ; Allow movement
    ROM:00204666 Obj01_NoRollCtrl:
    ROM:00204666                 bsr.w   Sonic_AnglePos        ; Handle floor collision
    ROM:0020466A                 bsr.w   Sonic_SlopeRepel        ; Check if we need to fall off a steep slope or ceiling
    ROM:0020466E                 rts
    Then, in Sonic's ground movement code, there is an added check for when Sonic is not charging a peelout or spindash (remember, Sonic's ground velocity is used for charging, so it is possible for Sonic to be remaining still while having a nonzero ground velocity!), so that it'll skip to Obj01_ResetScr when Sonic is just moving normally on the ground:
    ROM:0020474A Obj01_NotRight:
    ROM:0020474A                 move.b  $26(a0),d0            ; Get Sonic's current level collision quadrant
    ROM:0020474E                 addi.b  #$20,d0            ; ''
    ROM:00204752                 andi.b  #$C0,d0            ; If Sonic on the floor?
    ROM:00204756                 bne.w   Obj01_ResetScr        ; If not, branch
    ROM:0020475A                 tst.w   $14(a0)            ; Is Sonic moving at all?
    ROM:0020475E                 beq.s   Obj01_StandAnim        ; If not, set the animation to standing
    ROM:00204760                 tst.b   $2A(a0)            ; Is Sonic charging a peelout or spindash?
    ROM:00204764                 beq.w   Obj01_ResetScr        ; If not, branch
    ROM:00204768                 bra.s   Obj01_ChkObjBalance    ; Continue to check for balancing
    ROM:0020476A ; ---------------------------------------------------------------------------
    ROM:0020476A Obj01_StandAnim:
    ROM:0020476A                 bclr    #5,$22(a0)            ; Clear push flag
    ROM:00204770                 move.b  #5,$1C(a0)            ; Set to standing animation
    ROM:00204776 Obj01_ChkObjBalance:
    ROM:00204776                 btst    #3,$22(a0)            ; Is Sonic standing on an object?
    ROM:0020477C                 beq.s   Sonic_Balance        ; If not, branch
    At this point, any other branches to Sonic_LookUp in Sonic 1 are changed to branch to a new set of code that actually handles the peelout and some of the spindash, inserted right before Sonic_LookUp. This is that new set of code:
    ROM:00204820 Obj01_HandleCharge:
    ROM:00204820                 move.b  ($FFF788).w,d0       ; Get charge delay timer
    ROM:00204824                 andi.b  #$F,d0           ; Is it 0?
    ROM:00204828                 beq.s   Obj01_NoChargeDelay   ; If so, don't handle it
    ROM:0020482A                 addq.b  #1,($FFF788).w       ; Increment charge delay timer
    ROM:0020482E                 andi.b  #$CF,($FFF788).w       ; Limit timer between 0-$F
    ROM:00204834 Obj01_NoChargeDelay:
    ROM:00204834                 btst    #7,($FFF788).w       ; Was the peelout disable flag set?
    ROM:0020483A                 bne.w   Sonic_LookUp       ; If so, branch
    ROM:0020483E                 btst    #6,($FFF788).w       ; Was the spindash disable flag set?
    ROM:00204844                 bne.w   Obj01_ChkForSpindash       ; If so, branch
    ROM:00204848                 btst    #1,($FFF602).w       ; Is down being held?
    ROM:0020484E                 bne.w   Obj01_ChkForSpindash       ; If so, branch
    ROM:00204852                 andi.b  #$F,($FFF788).w       ; Is the charge delay timer active?
    ROM:00204858                 beq.s   Obj01_ChkUpPress       ; If not, branch
    ROM:0020485A                 btst    #0,($FFF603).w       ; Was up pressed?
    ROM:00204860                 beq.s   Obj01_ChkPeelout       ; If not, branch
    ROM:00204862                 bset    #7,($FFF788).w       ; Set the peelout disable flag
    ROM:00204868                 bra.w   Obj01_UpdateSpeed
    ROM:0020486C ; ---------------------------------------------------------------------------
    ROM:0020486C Obj01_ChkUpPress:
    ROM:0020486C                 btst    #0,($FFF603).w       ; Was up pressed?
    ROM:00204872                 beq.w   Obj01_ChkPeelout       ; If not, branch
    ROM:00204876                 move.b  #1,($FFF788).w       ; Set the charge delay timer
    ROM:0020487C                 bra.w   Obj01_UpdateSpeed
    ROM:00204880 ; ---------------------------------------------------------------------------
    ROM:00204880 Obj01_ChkPeelout:
    ROM:00204880                 btst    #0,($FFF602).w       ; Is up being held?
    ROM:00204886                 beq.s   Obj01_ChkUnleashPeelout   ; If not, branch
    ROM:00204888                 move.b  #7,$1C(a0)           ; Set to "looking up" animation
    ROM:0020488E                 tst.b   $2A(a0)           ; Is a peelout being charged?
    ROM:00204892                 beq.s   Obj01_ChkStartCharge   ; If not, branch
    ROM:00204894                 move.b  #0,$1C(a0)           ; Set to "running" animation
    ROM:0020489A                 moveq   #100,d0           ; Ground velocity increment value
    ROM:0020489C                 move.w  ($FFF760).w,d1       ; Calculate the max peelout speed
    ROM:002048A0                 move.w  d1,d2           ; (Max velocity * 2), without speed shoes
    ROM:002048A2                 asl.w   #1,d1           ; ''
    ROM:002048A4                 tst.b   ($FF1520).l       ; Are speed shoes activated?
    ROM:002048AA                 beq.s   Obj01_PeeloutNoShoes   ; If not, branch
    ROM:002048AC                 asr.w   #1,d2           ; (Max velocity * 2) - (Max velocity / 2), with speed shoes
    ROM:002048AE                 sub.w   d2,d1           ; ''
    ROM:002048B0 Obj01_PeeloutNoShoes:
    ROM:002048B0                 btst    #0,$22(a0)           ; Are we facing right?
    ROM:002048B6                 beq.s   Obj01_PeeloutIncGVel   ; If so, branch
    ROM:002048B8                 neg.w   d0               ; Negate the increment value
    ROM:002048BA                 neg.w   d1               ; Negate the max ground velocity
    ROM:002048BC Obj01_PeeloutIncGVel:
    ROM:002048BC                 add.w   d0,$14(a0)           ; Increment ground velocity
    ROM:002048C0                 move.w  $14(a0),d0           ; Get current ground velocity
    ROM:002048C4                 btst    #0,$22(a0)           ; Are we facing right?
    ROM:002048CA                 beq.s   Obj01_PeeloutChkMax   ; If so, branch
    ROM:002048CC                 cmp.w   d0,d1           ; Have we reached the max velocity?
    ROM:002048CE                 ble.s   Obj01_PeeloutSetGVel   ; If not, branch
    ROM:002048D0                 bra.s   Obj01_PeeloutCapGVel   ; If so, cap it
    ROM:002048D2 ; ---------------------------------------------------------------------------
    ROM:002048D2 Obj01_PeeloutChkMax:
    ROM:002048D2                 cmp.w   d1,d0           ; Have we reached the max velocity?
    ROM:002048D4                 ble.s   Obj01_PeeloutSetGVel   ; If not, branch
    ROM:002048D6 Obj01_PeeloutCapGVel:
    ROM:002048D6                 move.w  d1,d0           ; Cap the velocity
    ROM:002048D8 Obj01_PeeloutSetGVel:
    ROM:002048D8                 move.w  d0,$14(a0)           ; Set the ground velocity
    ROM:002048DC                 rts
    ROM:002048DE ; ---------------------------------------------------------------------------
    ROM:002048DE Obj01_ChkStartCharge:
    ROM:002048DE                 move.b  ($FFF603).w,d0       ; Were A, B, or C pressed?
    ROM:002048E2                 andi.b  #$70,d0           ; ''
    ROM:002048E6                 beq.s   Obj01_NoPeelout       ; If not, branch
    ROM:002048E8                 move.b  #1,$2A(a0)           ; Initialize the charge timer
    ROM:002048EE                 move.w  #$9C,d0           ; Play the charge sound
    ROM:002048F2                 jsr     PlaySound           ; ''
    ROM:002048F8 Obj01_NoPeelout:
    ROM:002048F8                 bra.w   Obj01_UpdateSpeed
    ROM:002048FC ; ---------------------------------------------------------------------------
    ROM:002048FC Obj01_ChkUnleashPeelout:
    ROM:002048FC                 cmpi.b  #30,$2A(a0)       ; Is the charge timer at max?
    ROM:00204902                 beq.s   Obj01_UnleashPeelout   ; If so, branch
    ROM:00204904                 move.w  #$AB,d0           ; Play the charge stop sound
    ROM:00204908                 jsr     PlaySound           ; ''
    ROM:0020490E                 move.b  #0,$2A(a0)           ; Reset the charge timer
    ROM:00204914                 move.w  #0,$14(a0)           ; Reset the ground velocity
    ROM:0020491A                 bra.s   Obj01_ChkForSpindash
    ROM:0020491C ; ---------------------------------------------------------------------------
    ROM:0020491C Obj01_UnleashPeelout:
    ROM:0020491C                 move.b  #0,$2A(a0)           ; Reset the charge timer
    ROM:00204922                 move.w  #$91,d0           ; Play the charge release sound
    ROM:00204926                 jsr     PlaySound           ; ''
    ROM:0020492C                 bra.w   Obj01_ResetScr
    Sonic_LookUp's branch to Sonic_Duck has been changed to a branch to Obj01_ChkForSpindash, and this code has been added before Sonic_Duck:
    ROM:00204958 Obj01_ChkForSpindash:
    ROM:00204958                 btst    #6,($FFF788).w       ; Is the spindash disable flag set?
    ROM:0020495E                 bne.w   Sonic_Duck           ; If so, branch
    ROM:00204962                 andi.b  #$F,($FFF788).w       ; Is the charge timer active?
    ROM:00204968                 beq.s   Obj01_ChkDownPress       ; If not, branch
    ROM:0020496A                 btst    #1,($FFF603).w       ; Was down pressed?
    ROM:00204970                 beq.s   Obj01_ChkSpindash       ; If not, branch
    ROM:00204972                 bset    #6,($FFF788).w       ; Set the spindash disable flag
    ROM:00204978                 bra.w   Obj01_UpdateSpeed
    ROM:0020497C ; ---------------------------------------------------------------------------
    ROM:0020497C Obj01_ChkDownPress
    ROM:0020497C                 btst    #1,($FFF603).w       ; Was down pressed?
    ROM:00204982                 beq.s   Obj01_ChkSpindash       ; If not, branch
    ROM:00204984                 move.b  #1,($FFF788).w       ; Initialize the charge timer
    ROM:0020498A                 bra.w   Obj01_UpdateSpeed
    ROM:0020498E ; ---------------------------------------------------------------------------
    ROM:0020498E Obj01_ChkSpindash:
    ROM:0020498E                 btst    #1,($FFF602).w       ; Is down being held?
    ROM:00204994                 beq.s   Obj01_ResetScr       ; If not, branch
    ROM:00204996                 move.b  #8,$1C(a0)           ; Set to "ducking" animation
    ROM:0020499C                 tst.b   $2A(a0)           ; Are we charging a spindash?
    ROM:002049A0                 bne.s   Obj01_NoSpindash       ; If so, branch
    ROM:002049A2                 move.b  ($FFF603).w,d0       ; Was A, B, or C pressed?
    ROM:002049A6                 andi.b  #$70,d0           ; ''
    ROM:002049AA                 beq.s   Obj01_NoSpindash       ; If not, branch
    ROM:002049AC                 move.b  #1,$2A(a0)           ; Initialize the charge timer
    ROM:002049B2                 move.w  #$16,$14(a0)       ; Set the initial ground velocity
    ROM:002049B8                 btst    #0,$22(a0)           ; Are we facing right?
    ROM:002049BE                 beq.s   Obj01_StartSpindash   ; If so, branch
    ROM:002049C0                 neg.w   $14(a0)           ; Negate the ground velocity
    ROM:002049C4 Obj01_StartSpindash:
    ROM:002049C4                 move.w  #$9C,d0           ; Play the charge sound
    ROM:002049C8                 jsr     PlaySound           ; ''
    ROM:002049CE                 bsr.w   Obj01_ChkRoll       ; Set Sonic to be rolling
    ROM:002049D2 Obj01_NoSpindash:
    ROM:002049D2                 bra.s   Obj01_UpdateSpeed
    And Sonic_ResetScr has been changed to this to handle the charge delay variable:
    ROM:002049F0 Obj01_ResetScr:
    ROM:002049F0                 cmpi.w  #$60,($FFF73E).w       ; Is the screen in its default position?
    ROM:002049F6                 bne.s   Obj01_ChkScrShiftDir   ; If not, branch
    ROM:002049F8                 move.b  ($FFF788).w,d0       ; Is the charge delay timer active?
    ROM:002049FC                 andi.b  #$F,d0           ; ''
    ROM:00204A00                 bne.s   Obj01_UpdateSpeed       ; If so, branch
    ROM:00204A02                 move.b  #0,($FFF788).w       ; Reset the charge delay timer and flags
    ROM:00204A08                 bra.s   Obj01_UpdateSpeed
    ROM:00204A0A ; ---------------------------------------------------------------------------
    ROM:00204A0A Obj01_ChkScrShiftDir:
    ROM:00204A0A                 bcc.s   Obj01_ShiftScrUp       ; If the screen needs to shift up, branch
    ROM:00204A0C                 addq.w  #4,($FFF73E).w       ; Shift down
    ROM:00204A10 Obj01_ShiftScrUp:
    ROM:00204A10                 subq.w  #2,($FFF73E).w       ; Shift up
    ROM:00204A14 Obj01_UpdateSpeed:
    Sonic_RollSpeed also has added code when Sonic is charging the spindash. This code has been added after loc_13188:
    ROM:00204BEE                 tst.b   $2A(a0)           ; Are we charging a spindash?
    ROM:00204BF2                 beq.w   Obj01_NoSpindashCharge   ; If not, branch
    ROM:00204BF6                 move.w  #75,d0           ; Ground velocity increment value
    ROM:00204BFA                 move.w  ($FFF760).w,d1       ; Calculate the max charge velocity
    ROM:00204BFE                 move.w  d1,d2           ; (Max velocity * 2), without speed shoes
    ROM:00204C00                 asl.w   #1,d1           ; ''
    ROM:00204C02                 tst.b   ($FF1520).l       ; Are speed shoes activated?
    ROM:00204C08                 beq.s   Obj01_ChkSpindashDir   ; If not, branch
    ROM:00204C0A                 asr.w   #1,d2           ; (Max velocity * 2) - (Max velocity / 2), with speed shoes
    ROM:00204C0C                 sub.w   d2,d1           ; ''
    ROM:00204C0E Obj01_ChkSpindashDir:
    ROM:00204C0E                 btst    #0,$22(a0)           ; Are we facing right?
    ROM:00204C14                 beq.s   Obj01_SpindashIncGVel   ; If so, branch
    ROM:00204C16                 neg.w   d0               ; Negate ground velocity increment value
    ROM:00204C18                 neg.w   d1               ; Negate max charge velocity
    ROM:00204C1A Obj01_SpindashIncGVel:
    ROM:00204C1A                 add.w   d0,$14(a0)           ; Increment ground velocity
    ROM:00204C1E                 move.w  $14(a0),d0           ; Get current ground velocity
    ROM:00204C22                 btst    #0,$22(a0)           ; Are we facing right?
    ROM:00204C28                 beq.s   Obj01_SpindashChkMaxGVel   ; If so, branch
    ROM:00204C2A                 cmp.w   d0,d1           ; Have we reached the max charge velocity?
    ROM:00204C2C                 ble.s   Obj01_SpindashSetGVel   ; If not, branch
    ROM:00204C2E                 bra.s   Obj01_SpindashCapGVel   ; If so, cap the ground velocity
    ROM:00204C30 ; ---------------------------------------------------------------------------
    ROM:00204C30 Obj01_SpindashChkMaxGVel:
    ROM:00204C30                 cmp.w   d1,d0           ; Have we reached the max charge velocity?
    ROM:00204C32                 ble.s   Obj01_SpindashSetGVel   ; If not, branch
    ROM:00204C34 Obj01_SpindashCapGVel:
    ROM:00204C34                 move.w  d1,d0           ; Cap the ground velocity
    ROM:00204C36 Obj01_SpindashSetGVel:
    ROM:00204C36                 move.w  d0,$14(a0)           ; Set the ground velocity
    ROM:00204C3A                 btst    #1,($FFF602).w       ; Is down being held?
    ROM:00204C40                 beq.s   Obj01_ChkSpindashRelease   ; If not, branch
    ROM:00204C42                 rts
    ROM:00204C44 ; ---------------------------------------------------------------------------
    ROM:00204C44 Obj01_SpindashNotFull:
    ROM:00204C44                 move.w  #$AB,d0           ; Play the charge stop sound
    ROM:00204C48                 jsr     PlaySound           ; ''
    ROM:00204C4E                 move.b  #0,$2A(a0)           ; Reset the charge timer
    ROM:00204C54                 move.w  #0,$14(a0)           ; Completely stop ourselves
    ROM:00204C5A                 move.w  #0,$10(a0)           ; ''
    ROM:00204C60                 move.w  #0,$12(a0)           ; ''
    ROM:00204C66                 bra.w   Obj01_StopRoll       ; Stop rolling
    ROM:00204C6A ; ---------------------------------------------------------------------------
    ROM:00204C6A Obj01_ChkSpindashRelease:
    ROM:00204C6A                 cmpi.b  #45,$2A(a0)       ; Is the charge timer at max?
    ROM:00204C70                 bne.s   Obj01_SpindashNotFull   ; If not, branch
    ROM:00204C72                 move.b  #0,$2A(a0)           ; Reset the charge timer
    ROM:00204C78                 move.w  #$91,d0           ; Play the charge release sound
    ROM:00204C7C                 jsr     PlaySound           ; ''
    ROM:00204C82                 btst    #0,$22(a0)           ; Are we facing left?
    ROM:00204C88                 bne.s   Obj01_SpindashLeft       ; If so, branch
    ROM:00204C8A                 bsr.w   Sonic_RollRight       ; Go right
    ROM:00204C8E                 bra.s   Obj01_NoSpindashCharge
    ROM:00204C90 ; ---------------------------------------------------------------------------
    ROM:00204C90 Obj01_SpindashLeft:
    ROM:00204C90                 bsr.w   Sonic_RollLeft       ; Go left
    ROM:00204C94                 bra.s   Obj01_NoSpindashCharge
    ROM:00204C96 ; ---------------------------------------------------------------------------
    ROM:00204C96                 rts
    ROM:00204C98 ; ---------------------------------------------------------------------------
    ROM:00204C98 Obj01_NoSpindashCharge:
    With the label "Obj01_StopRoll" being set above the "bclr #2,$22(a0)" later in the code.

    Final small notes:
    Sonic_Jump adds a check for if Sonic is charging a peelout or spindash at the beginning. If he isn't it continues on to the actual routine, and if he is, it just goes to an "rts". Sonic_JumpHeight, for some reason, clears the charge timer if Sonic's jump height gets capped. Sonic_MoveLeft, Sonic_MoveRight, Sonic_SlopeResist, and Sonic_RollRepel also add a check and branches to an "rts" if Sonic is charging at the beginning of each of them. When Sonic gets hurt, the charge timer gets cleared. sub_FC2C/Solid_ResetFloor play the charge stop sound if Sonic is charging and the "ride on object" flag is set to be cleared.

    As you can see, it's a bit of a mess, but that's Sonic CD for you. It should give you an insight on how Sonic CD handles the peelout and spindash, though I recommend you don't implement it exactly like this, as (in my opinion), it's quite sloppy.
    Last edited: Jun 4, 2019
    Pokepunch and ProjectFM like this.
  3. Samey

    Samey Le Bored Hedgie Member

    May 3, 2017
    Oh I see now.. :p silly me for thinking you meant the Sonic 2 spindash.

    Edit: Ok I got it working, but I basically just hijacked loc_13A9C.

    Basically, I tried what Kilo said (basically put it into loc_13A9C) and told it to look for the flag where i'm charging the peelout (instead of having it look for how fast Sonic's going).

            lea    (SonAni_SuperPeelout).l,a1
            cmpi.w    #$A00,d2    ; is Sonic at running speed?
            bcc.s    loc_13AB4    ; if yes, branch
            lea    (SonAni_Run).l,a1 ; use    running    animation
            cmpi.w    #$600,d2    ; is Sonic at running speed?
            bcc.s    loc_13AB4    ; if yes, branch
            lea    (SonAni_WtrWalk).l,a1 ; use water walking animation
            btst    #6,$22(a0)        ; is sonic underwater?
            bne.s    loc_13AB4    ; if yes, branch
            lea    (SonAni_SuperPeelout2).l,a1
            btst    #1,$39(a0)                                    ; is Sonic charging a peelout?
            bne.w    loc_13AB4_2                                    ; if yes, hijack the sub and go to some similar code that doesn't change frame duration
            lea    (SonAni_Walk).l,a1 ; use walking animation
    ;        [rest of original code here]
    ; ===========================================================================
    loc_13AB4_2:                                                ; this is so the charging animations frame duration isn't changed
            add.b    d0,d0
            move.b    d0,d3
            neg.w    d2
            addi.w    #$800,d2
            bpl.s    loc_13AC2_2
            moveq    #0,d2
            lsr.w    #8,d2
            move.b    #0,$1E(a0)    ; modify frame duration, but actually don't
            bsr.w    SAnim_Do2
            add.b    d3,$1A(a0)    ; modify frame number
    ; ===========================================================================
    I did try getting the ground velocity to work, but I just ended up scrapping it (It honestly probably would've screwed up a bunch of other thing's I wouldn't know how to fix)
    Edit 2: Now to fix this glitch where the animation hangs for a second before looping again...
    Edit 3: Fixed that. Who knew deleting instructions willy nilly would introduce bugs?! :eek:
    I re added the instruction that would modify the frame duration, but its set to 0 so it actually doesn't.

    Thanks for all your help.:)
    Last edited: Jun 4, 2019
  4. CHRdutch

    CHRdutch h Member

    Jun 3, 2017
    Earth, I think.

    Does anybody know why my Flex 2 looks like this? I can't find anything online about it.
  5. AsuharaMoon

    AsuharaMoon kakyoin did you lay this egg Member

    Aug 15, 2013
    Alright so I given another try to find the source of those issues I showed up you all a while ago, and... I think this speaks for itself:


    So what values should I add to TouchResponse when Knux' is gliding and sliding?
  6. MarkeyJester

    MarkeyJester ♡ ! Member

    Jun 27, 2009
    I did some observational research:


    Both Sonic and Knuckles can roll, and their hit boxes would be identical. I have rolled knuckles off of a line of springs to prevent gravity from pulling me any faster than a pixel per frame, thus ensuring that I don't overlap the point where the badnik would be destroyed, and instead land exactly on the line. Compared with gliding on top of it, the badnik got destroyed at what appears to be the same distance (I would like to point out that it does not matter where the actual floor collision is, as we're only looking for the relative distance between rolling and gliding, and they are the same).

    So with Sonic, I rolled off of that block (for the gravity reasons), and he destroyed it 28 pixels away from the floor. Do note, your distance from the floor is off by one pixel, the actual floor collision itself is one pixel in the ground, I have however measured from the same floor point as you, again, it's relative, so what matters is the distance between rolling and gliding, thus, gliding should be 28 pixels just like the rolling. I did the test multiple times to ensure the gravity never pulled Knuckles/Sonic down more than a pixel per frame.

    A thing to consider though, it might not be the hit-box that's wrong, it could very well be the sprite itself displayed relatively higher than it should on the object in those hacks you've mentioned.
  7. AsuharaMoon

    AsuharaMoon kakyoin did you lay this egg Member

    Aug 15, 2013
    Trust me, it was the hit-box. Turns out that SCH and Megamix did a specific fix to reduce the lower part of it when's touching an enemy or object (TouchResponse and SolidObject respectively), ending up with a forced y-radius of $14 (20 pixels) in both hacks, perfectly adjusted for Knux's gliding and sliding original sprites sizes and without modifying the ones from the moves codes.
    I couldn't find that much how S3&K handles these things, especially that Knuckles doesn't triggers with certain positioned objects like in Sonic 1.

    Anyway, FireRat gave me a hand and found the solutions to all this.

    Replace "move.b y_radius(a1),d3" with this:
            cmpi.b    #1,collision_property(a1)    ; is Knuckles gliding?
            beq.s     Obj26_Gliding            ; if yes, branch
            cmpi.b    #2,collision_property(a1)    ; is Knuckles falling after gliding?
            beq.s     Obj26_Gliding            ; if yes, branch
            move.b    y_radius(a1),d3
            bra.s     Obj26_NoGlide            ; ++
            moveq     #$14/2,d3            ; ++
    Obj26_NoGlide:                        ; ++

    Replace both "move.b y_radius(a1),d3" from loc_FA94 and loc_FAD0 with this:
            cmpi.b    #1,collision_property(a1)    ; is Knuckles gliding?
            beq.s     @Gliding                ; if yes, branch
            cmpi.b    #3,collision_property(a1)    ; is Knuckles sliding across the ground?
            beq.s     @Gliding                ; if yes, branch
            cmpi.b    #AfterSlide_Frame,mapping_frame(a1)            ; is Knuckles getting up after sliding?
            beq.s     @Gliding                ; if yes, branch
            move.b    y_radius(a1),d3
            bra.s     @NotGliding            ; ++
            moveq     #$14/2,d3
    @NotGliding:                        ; ++

    Add these checks right between these 2 lines, since if we do it on later ones would cause to elevate the Y-radius by one pixel:
            subq.w   #8,d2
            cmpi.b   #1,collision_property(a0)    ; is Knuckles gliding?
            beq.s    @TouchKnux            ; if yes, branch
            cmpi.b   #3,collision_property(a0)    ; is Knuckles sliding across the ground?
            beq.s    @TouchKnux            ; if yes, branch
            moveq    #0,d5
    Then replace these...:
            cmpi.b    #Duck_Frame,mapping_frame(a0)    ; is Sonic ducking?
            bne.s     Touch_NoDuck    ; if not, branch
    ...with all this:
            cmpi.b    #Anim_Duck,anim(a0)    ; is Knuckles ducking?
            beq.s     @TouchDuck    ; if yes, branch
            jmp       Touch_NoDuck
            move.w    y_pos(a0),d3    ; restore ypos
            moveq     #$14/2-3,d5    ; set y-radius (forced)
            sub.w     d5,d3
            jmp       Touch_NoDuck

    And that should be it. Hope this helps for future Knuckles hacks over there. (kinda weird no-one mentioned these things before; even in Sonic Retro)
    Last edited: Jun 22, 2019
    MarkeyJester likes this.
  8. MarkeyJester

    MarkeyJester ♡ ! Member

    Jun 27, 2009
    Out of curiosity, what is the resulting distances from the floor with the above aforementioned fix, and which hacks got it right?

    Was it the 37 or the 29? I assume the latter, because flamewing's work is often quite thorough with detail.
  9. segastar

    segastar Newcomer Trialist

    Jun 16, 2019
    I'm wondering if anyone here can help me with something.
    I'm trying to display an object in Sonic 3K that consists of four sprites, the code for that object is as follows:
    moveq #0,d0
    move.l #Map_LevelUpText,$C(a0)
    move.w #$0410,$A(a0)
    move.w #0,8(a0)
    move.b #$40,7(a0)
    move.b #8,6(a0)
    move.w #$1500,$10(a0)
    move.w #$3D0,$14(a0)
    move.l #Obj_LevelUpText_Display,(a0)

    jmp (Draw_Sprite).l

    Map_LevelUpText: dc.w Map_LevelUpText1-Map_LevelUpText
    Map_LevelUpText1: dc.w 4
    dc.b 0,$8, 0, 0, 0, 0
    dc.b 0, 0, 0, 1, 0,$18
    dc.b 0, 0, 0, 0, 0,$20
    dc.b 0,$4, 0, 3, 0,$28

    I have the object set to appear on the screen when a 1-up monitor is broken. Since I'm trying to teach myself how objects work, I'm just trying to have it show up in a set location in Angel Island Zone for now. I created an instance of the object in Monitor_Give_1up.

    My problem is that the object is displaying incorrectly. It only seems to display one tile instead the whole thing.

    Correct Display.PNG
    Only one tile.PNG

    I thought that maybe something was wrong with my mappings, but the object DOES display correctly when
    I create an instance of it on the title screen. It's only incorrect when displayed in a level.

    Any ideas? Please tell me if you need more info.
    My apologies if this has been answered somewhere else before, I tried searching for an answer but couldn't find one.
    Thank you!
    ProjectFM likes this.
  10. MarkeyJester

    MarkeyJester ♡ ! Member

    Jun 27, 2009
    There are two possible things I can think of that might be wrong.

           move.b   #$04,$04(a0)
    Without this, the object coordinates will be treated as on-screen coordinates, rather than level coordinates.

    The other is possibly the VRAM address is wrong:

           move.w   #$0410,$0A(a0)
    410 x 20 = VRAM 8200.

    Is the "LEVEL UP" art in VRAM at address 8200? It could be possible that something overwrote the "LEVU" tiles.
  11. segastar

    segastar Newcomer Trialist

    Jun 16, 2019
    410 is the block number of the art, which is indeed at address $8200.

    It doesn't look like the tiles are being overwritten, they're still there if I check the pattern viewer on exodus.

    You know what's weird though? If I modify my Map_LevelUpText to just be this:

            dc.b 0,$C,  0,  0,  0,  0
    It'll display that one sprite perfectly:

    1 sprite displaying fine.PNG

    It's only when I try to add multiple sprites to one object that I have a problem. Again, when I create an instance of this object on the title screen it displays perfectly.

    How do the object coordinates work in Sonic 3K? When I write #4,4(a0) no tiles show up on the screen at all. Do I also have to change my values for both $10(a0) and $14(a0)?

    Thank you!
  12. MarkeyJester

    MarkeyJester ♡ ! Member

    Jun 27, 2009
    Aha! I believe I know exactly what it is then...

    If bit 5 of $04(aX) is set, then the mappings pointer is treated as pointing to a single map piece (this is often used for fragmentation or simple icon display), so if this object is spawned by the monitor similar to the way the icons are spawned, then your object probably has this bit set (the icons probably have this bit set seeing as they are just a single sprite displaying the icon, so your banner probably has the same thing).

    Simply put:

           bclr.b   #$05,$04(a0)
    And this will cause the sprite rendering routine to treat this object as having a multi-sprite mapping list, rather than a single sprite.

    --- --- --- --- --- --- --- --- --- ---

    Regarding the coordinates system, sprites have a display "plane" or area if you will, this is $200 x $200, the top/left pixel of the screen is coordinate $80 x $80:


    When bit 2 is clear, the coordinates in $10(aX) and $12(aX) will always reflect the exact plane position. You had $1500 in $10(aX) (that's your X position), and whatever garbage might have been in $12(aX) (that's your Y position), if we pretend you had $C0 as the Y position just for this example the coordinates would be $1500 x $C0.

    The coordinates are automatically looped around the $200 range on both X and Y, that is to say if a sprite goes off the end, it wraps back around, so we'll AND wrap the coordinates by $1FF:

    $1500 & $1FF = $100
    $C0 & $1FF = $C0

    So your sprite would probably display somewhere here on-screen (Again, pretending that $C0 happened to be the word in $12(aX)):


    Now, when the bit is SET, the coordinates used are what's inside $10(aX) and what's inside $14(aX) (the reason is that $12 and $16 are used as fixed point fractions for calculating movement in the level, but we'll leave that for another time), you had $1500 x $3D0, so the sprites will appear in the level at those coordinates, not on the screen itself, but in the level. So if you go to that area in the level, the sprites will appear there, please note if you move the screen to those exact coordinates then the object will appear on the top/left of the screen, the coordinates of the screen are the top left of it.

    If I'm to understand correctly, this object is spawned by the monitor object? If so, then it's likely the monitor object is already setting this bit correctly for you, so I apologise for suggesting it.

    AURORA☆FIELDS so uh yes Member

    Oct 7, 2011
    The information regarding $10 and $12 used for the X and Y positions respecitvely do not apply to Sonic 3 & Knuckles. In any case in this game, $10 is always the X-position of an object, and $14 is the Y-position of an object. In Sonic 1, and I believe Sonic 2, when objects are displayed based on on-screen coordinates, the objects Y-position is changed from being at $0C to $0A, for some odd reason. This to me makes no sense and makes everything just more complex in general, which is probably why the behaviour was changed in the later games.
    MarkeyJester likes this.
  14. segastar

    segastar Newcomer Trialist

    Jun 16, 2019
    Yes, it does look like bit 5 was set. On top of that, it looks like some other offsets of a0 had values in them already left over from the monitor object. 22(a0), the current animation frame, was set to 2 and thus nothing was showing up on the screen.

    Lesson learned. Make sure your object's variables are set properly.

    Thanks for the help!
    EMK-20218, MarkeyJester and Painto like this.
  15. AsuharaMoon

    AsuharaMoon kakyoin did you lay this egg Member

    Aug 15, 2013
    29 (actually 30), since it's the one I was trying to mimic/accomplish taking base of Megamix and Classic Heroes, also cuz' those are the only known ones who brought this up. The rest of the hacks (even some of S2) left the original 37 from KiS2 and S3&K intact, since it was quite hard to notice that.

    PS: Sorry for the late reply!
  16. Trickster

    Trickster The Trickster Member

    Aug 22, 2018
    Brazil Bad Future
    Well, today I began to rework in some old Nineko's SMPS that I'm going to use in my project.
    Since most hackers only use these oldish SMPS without changing NOTHING, I decided to take one step further.
    One of them needs to have the bass changed, and I know exactly what I want.

    Remember this gem? I tried to replicate the bass of it, because IT'S JUST PERFECT ;~;
    And well, I'm here because I failed. So, can someone help me? There's a lot of awesome musicians here, I think It's not a hard thing to do for them.
    Also, please send the data like this, because It's the only way for me to understand it. :^P

    Last edited: Jul 7, 2019
  17. LackofTrack

    LackofTrack Active Member Member

    May 30, 2018

    Done. Went ahead and created 2 bass patches. See which one you prefer!

    spAlgorithm    $00
        spFeedback    $06
        spDetune    $07, $07, $05, $06
        spMultiple    $0A, $00, $0B, $01
        spRateScale    $01, $01, $02, $00
        spAttackRt    $1F, $1F, $1F, $1F
        spAmpMod    $00, $00, $00, $00
        spSustainRt    $0F, $0A, $07, $07
        spSustainLv    $00, $04, $05, $06
        spDecayRt    $0F, $0C, $07, $0A
        spReleaseRt    $08, $08, $07, $0B
        spTotalLv    $14, $10, $3C, $04
    spAlgorithm    $00
        spFeedback    $06
        spDetune    $07, $07, $05, $06
        spMultiple    $0C, $00, $0B, $01
        spRateScale    $01, $01, $02, $00
        spAttackRt    $1F, $1F, $1F, $1F
        spAmpMod    $00, $00, $00, $00
        spSustainRt    $0F, $0A, $07, $07
        spSustainLv    $00, $04, $05, $06
        spDecayRt    $0F, $0C, $07, $0A
        spReleaseRt    $08, $08, $07, $0B
        spTotalLv    $14, $10, $3C, $04
  18. NiphFM

    NiphFM Moderator and Host of the SMPS Competition Member

    Jun 5, 2015
    Music Plant Zone
    It's actually just Chemical Plant's bass that's swapped in, however I will say that is a mighty fine bass patch you've made, LackofTrack!
  19. GreaterGam

    GreaterGam Newcomer Trialist

    Jun 30, 2019
    A Place
    Does anyone know the first step to creating a ROM hack? Do you just download a ROM off of the internet and somehow get into the code? Do you RIP your personal Copy onto a program? If so, what program? Or is it something way simpler then that? I haven't the slightest clue.

    Also, how do you make music? The code doesn't make sense to me. Or to be specific, the instruments like bass talked about earlier.
    Trickster likes this.
  20. MarkeyJester

    MarkeyJester ♡ ! Member

    Jun 27, 2009
    Hacking initially involved editing the ROM (or a savestate) directly using a hexadecimal editor, information about what offsets contain which data and how to edit it were researched, discovered, and documented, by various people and the information shared publicly on a few forums/sites. The legacy of much of this ancient history can be found here. Many tools were written to edit the various offsets of data in a more convenient manner, for example, Stealth's SonED which took the data from the savestate (sometimes the ROM) and displayed it visually and allows you to edit this data visually, rather than via hex/binary.

    However, editing data (or even code) can be troublesome, especially if the changed code is larger than the original and won't fit into the same space without overwriting whatever code/data was after it, methods to get around this involved editing the offset pointers (relative and absolute) to point to a space in the ROM (usually the very end of the ROM) where the data will fit. But the most convenient way would be if the entire ROM were disassembled back into it's original mnemonic instructions with the data separated, and using an assembler to effectively reassemble the game back into a binary ROM, this allows changes to be made to both data and code, for the result to be larger or smaller, and the ROM will be optimally packed together without anything lost or broken.

    Disassembling a game can take a considerable amount of time, even if you have all of the offsets documented thoroughly before hand. A series of dedicated ROM hackers had created their own disassemblies of Sonic 1, and Sonic 2, and shared them out publicly, this was during the mid 2000's. Since then the disassemblies have been built upon, documented more, comments added, and a few other non-essential but considered helpful assets added to these disassemblies. These disassemblies are effectively like having the original source code and data, what you are doing these days is effectively less-not "hacking" and more-so "reprogramming".

    You can find these publicly shared disassemblies which some are pre-split, some aren't, and all come with an assembler attached and setup ready for you here.

    --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---

    Regarding music making for the Mega Drive titles specifically.

    The Mega Drive has a Yamaha Synthesiser and it's VDP chip has a built in PSG Tone/Noise generator (for backwards compatibility), the games will have their own sound drivers which will utilise these chips to produce music and sound effects. There is a fascinating documentation about the sound driver formats in Sonic games which can be found here, this doesn't go into full details about how the chips themselves work specifically, they are mostly detailed about the drivers themselves. I'm sure if ValleyBell pops his head in, he will very likely be able to point you to documentation of more sound drivers for other games which may not be (or could be incompletely) documented on the page I just linked.

    The guitar instrument above is a series of channel/operator specific data for the YM2612 (the Yamama Synthesiser).

    Each channel of the YM2612 will produce a distinctive instrument/sound which is mostly isolated from the other channels (bar perhaps the LFO rate), each channel can have its own frequency (tone/pitch), panning (speaker left/right), LFO amount/harshness (for both AM and FM), and operator specific controls like; how often operator 1 modulates itself, and which operators are key on/off. Each channel has four operators, dubbed 1 to 4, each operator will generate a simple sinewave, and the purpose of the operators is to change amplitude (volume) of the sinewave in real-time, these operators have an envelope system for controlling the amplitude (volume) via a series of steps, this is often referred to as ADSR (Attack, Decay, Sustain, Release).

    Attack - When you key on, this is how quickly the operator will raise it's volume to the maximum level.
    Decay - How quickly the volume should drop after it's reached maximum, to drop it's volume to the sustain level.
    Sustain - How quickly the volume should drop after it's reached sustain level, to drop continuously forever (or until it hits lowest volume).
    Release - When you key off, this is how quickly the volume should drop to mute.

    This can be viewed as a graph (which is where the term "envelope" comes from):


    In that instrument, you have four attacks, four decays, four sustains, four releases, four total levels, four sustain levels, etc, etc, you have four of each, each value is for a specific operator to control this envelope/volume procedure. Now, each operator will generate it's sinewave at the volume flow controlled by this envelope system (if we used the above graph as an example, the volume would raise somewhat sharply, then drop sharply to a point, then drop slowly, and if you release the key it'll drop really quickly).

    Each operator can output this sinewave audibly, these are called "carriers" or "slot operators", or the sinewave can be used to modulate the frequency of another operator's sinewave to speedup/slowdown it's frequency to an extreme degree, going backwards fast enough to even reverse the sinewave on itself, these operators are called "modulators" or "non-slot operators". This is quite difficult to explain in writing, however I was surprised to find that someone shared a wonderful animated gif sometime ago which explains the process very well:


    The orange and blue are operators, and the green is the output audio buffer, both the orange and blue operators are generating sinewave as you can see, but the orange is a carrier because it's outputting directly to the audio buffer, there as the blue is a modulator because it's sinewave is being used to modulate the frequency of the orange's sinewave (hence the term "Frequency Modulation"), you can see the resulting wave in the green buffer is much more complex and likely sounds "sharper" than a soft sinewave.

    The guitar instrument above will likely have it set where one operator generates a normal soft sinewave and fades that out very slowly, while another operator starts at full volume (fastest attack) and fades out very quicly, but is modulating it's sinewave on the casually fading operator, this creates an initially sharp jolting sound, but it fading very quickly it goes from sharp to soft in a matter of seconds, if you listen to a guitar instrument you will find many sound sharp when plucked and fade that sharpness to softness quite quickly. This is why the YM2612 is perfect for guitar/string pluck sounding instruments. The first operator can modulate itself up to 3 times before it passes its wave to the output or another operator, and the channel has an "algorithm" to decide in which order these operators feed into one another, which ones are modulators and which ones are carriers. Some algorithms allow a modulator to feed into a modulator to then feed into a carrier, thus complicating the waveform further.

    Regarding the tone generator (the PSG), this is more of a backwards compatibly audio as the Mega Drive was designed to be able to play Master System games, however, the PSG is freely utilisable on the Mega Drive, and many games/sound drivers will use it.

    There are four PSG channels, three are tone, one is noise. The tone channels are always squarewave, these channels can have their frequency controlled individually to control the pitch, and they have their own "attenuation" (volume control). The noise channel will produce one of two different types of noise, periodic noise (which effectively looks like a squarewave where one side is immensely longer than the other, creating a sharper unique sound) and white noise (almost random positive/negative waveform meant to simulate white tuning noise), this channel can also volume control independently, and the frequency can be in one of four different states, three of them are clock based and produce the same tone/pitch, but at three different octaves due to the nature of the rates' division. The fourth frequency setting is for the noise channel to use the third tone channel's frequency as its own (this does not disable the third tone channel). The noise channel is usually used for sounds that have a more coarse effect, for example, sound effects like the sea, an explosion, rushing water, or even hi-hats for music.

    Using a combination of the YM2612 and the PSG you can produce a variety of music or sound effects, and if done right you can produce them to a realistic sounding degree.