don't click here

Basic Questions & Answers thread

Discussion in 'Engineering & Reverse Engineering' started by Tweaker, May 29, 2008.

  1. Clownacy


    Tech Member
    asm68k appears to be largely written in x86 assembly, so reverse engineering it isn't too different to editing its original source code, unlike reverse engineering a program that was written in C.
    Last edited: Jul 12, 2023
  2. Lapper


    Lappering Tech Member
    Sonic Studio, Sonic Physics Guide, Kyle & Lucy, Freedom Planet 2
    Random question: how do I go about finding the area in the S3K disassembly where the SSZ cutscene Knuckles checks for and does the "shoo" handwave to Sonic while on the button? I think can locate the button object and cutscene knuckles code in general, but due to a lack of certain labelling I'm more lost when I actually try to locate that specific part & activation.

    I'm mainly searching for this kinda thing in an attempt learn to navigate S3K code in general more effectively.
  3. Devon


    Powered by a malfunctioning Motorola 68000 Tech Member
    your mom
    This might be much, but I think it would be helpful for you if the whole process of the SSZ cutscene was explained in regards to learning how to navigate S3K's code. If you just want the answer you are specifically looking for, go down towards the explanation of sub_65FDE.

    Okay, so here's a summary.
    • SSZ1_ScreenInit spawns Obj_57C1E
    • Obj_57C1E spawns the first teleporter object and the beam that carries the gang towards it from HPZ.
    • Obj_57C1E also spawns the movement controllers for the gang as they are carried up the beam. Knuckles gets Obj_57E34.
    • Because only the player and sidekick get automatically spawned in the stage, Obj_57E34 spawns the Knuckles object as well (Obj_CutsceneKnuckles) and sets it to use the SSZ handler (CutsceneKnux_SSZ). Once Knuckles reaches the top of the beam, bit 0 of _unkFAB8 gets set.
    • From there, Obj_CutsceneKnuckles takes control.
    So, let's shift our focus towards Obj_CutsceneKnuckles, or specifically CutsceneKnux_SSZ. From there, you'll find a jump table (off_6571A) for the different routines in the cutscene:
    • loc_65730 initializes the object and loads the palette.
    • loc_6575E waits for Knuckles' movement controller to finish (waits for bit 0 of _unkFAB8 to be set). Once that's done, it loads the Death Egg graphics.
    • loc_65794 enables gravity and waits for him to fall on top of the teleporter. Once he does, the Death Egg is spawned (loc_659CC, alongside loc_66072 to control its X position).
    • loc_657CE displays the transition frame between standing and laying on the ground for a few frames.
    • loc_657FE waits for the Death Egg to finish rising (waits until bit 1 of _unkFAB8 is set by the Death Egg object).
    • loc_65826 displays the transition frame between laying on the ground and standing for a few frames.
    • loc_6584C displays the standing frame for a few frames. After that, Knuckles jumps.
    • loc_65876 waits for Knuckles to hit the ground after jumping. Knuckles continues to run afterwards.
    • loc_658BA waits for Knuckles' X position to be >= 0x2A8 (680). Once he is, he jumps again above the right pit.
    • loc_658F2 waits for Knuckles to hit the ground after jumping again. Once he lands, he goes back to his tired animation, plays the switch sound, and sets the bridge (Obj_SSZCutsceneBridge) to extend towards the left (done by setting Events_bg+$08 to be nonzero once the camera is close enough to it), which then unlocks the camera and sets a checkpoint.
    • loc_6594A is the final set, and holds what you are looking for. Mainly, it just checks if the player has moved far enough away from Knuckles and despawns him, sets another checkpoint, and loads the monitor graphics. It also calls sub_65FDE...
    So now, let's talk about sub_65FDE. This is the function that makes Knuckles' animation change. This is the code in question:

    Code (Text):
    1. sub_65FDE:
    2.         btst    #3,$38(a0)
    3.         bne.w    locret_6206C
    4.         lea    (Player_1).w,a1
    5.         jsr    (Find_OtherObject).l
    6.         cmpi.w    #$20,d3
    7.         bhs.w    locret_6206C
    8.         subi.w    #$30,d2
    9.         cmpi.w    #$20,d2
    10.         bhs.w    locret_6206C
    11.         tst.w    d0
    12.         beq.w    locret_6206C
    13.         btst    #0,4(a1)
    14.         beq.w    locret_6206C
    15.         cmpi.b    #8,$20(a1)
    16.         bne.w    locret_6206C
    17.         lea    byte_6672A(pc),a1
    18.         jmp    (Set_Raw_Animation).l

    Let's start analyzing it.

    Code (Text):
    1.         btst    #3,$38(a0)
    2.         bne.w    locret_6206C

    I'm not so sure what's going on with this check here. Bit 3 of SST $38 never seems to get set for the Knuckles cutscene object in SSZ. Maybe it was meant to check if the animation was already set, but it doesn't actually work, so let's disregard that.

    Code (Text):
    1.         lea    (Player_1).w,a1
    2.         jsr    (Find_OtherObject).l

    This gets the distance the player is at from Knuckles, and which side the Player is on on both axes. Register d0 will be set to 0 if the player is left of Knuckles, 2 if right. Register d1 will be set to 0 if the player is above Knuckles, 2 if below. Register d2 will hold the absolute horizontal distance from Knuckles, and register d3 will hold the absolute vertical distance. Note that hitboxes are not taken into account, it just uses the position values. To see how the function exactly works, check out Find_OtherObject.

    Code (Text):
    1.         cmpi.w    #$20,d3
    2.         bhs.w    locret_6206C

    This just checks if the player is < 32 pixels away vertically from Knuckles in either direction.

    Code (Text):
    1.         subi.w    #$30,d2
    2.         cmpi.w    #$20,d2
    3.         bhs.w    locret_6206C

    This basically checks if the player is between 48 and 80 pixels away from Knuckles horizontally. It does a subtraction to invalidate distances < 48 pixels and then check using the width of range instead of performing 2 comparisons, which is slightly less optimal.

    Note that the unsigned branch is fine here, since any negative number will be interpreted as a extremely large value, and thus will be seen as >= 32.

    Code (Text):
    1.         tst.w    d0
    2.         beq.w    locret_6206C

    This checks if the player is positioned towards the right of Knuckles.

    Code (Text):
    1.         btst    #0,4(a1)
    2.         beq.w    locret_6206C

    This checks if the player is facing left (sprite flags are on SST $04, with bit 0 being the X flip flag)

    Code (Text):
    1.         cmpi.b    #8,$20(a1)
    2.         bne.w    locret_6206C

    This checks if the player is using the ducking animation.

    Code (Text):
    1.         lea    byte_6672A(pc),a1
    2.         jmp    (Set_Raw_Animation).l

    If all those conditions are met, Knuckles' animation is changed. Though, it should be noted that as long as these conditions are met, it'll constantly be resetting the animation back to the beginning, since Set_Raw_Animation directly sets the animation data pointer. It'll only allow the animation to play if you break a condition. It is possible that bit 3 of SST $38 was meant once this animation starts, implemented the check, but forgot to actually set it.

    So, tl;dr, if the player is < 32 pixels vertically away on either end, is >= 48 pixels and < 80 pixels away horizontally on the right (not accounting for hitboxes), is facing left, and is ducking, the animation change happens.


    I hope this helps!
    Last edited: Jul 11, 2023
    • Informative Informative x 5
    • List
  4. I'm always confused about that animation. It's cool, but the trigger is so oddly specific, it makes you wonder if it was meant to gaslight kids into thinking they never saw it (did it to Cybershell).
  5. E-122-Psi


    Okay, so I'm trying to add scroll code for my level, but don't quite know what I'm doing, and am overrelying on copy paste for some areas. I ended up with this:


    The scroll itself seems to run properly, however it seems to have problems loading the tiles the right place, often switching the bottom and top ones, like it can't quite load them all at once. Any idea what might be causing this?
  6. Cokie


    C. Okie Member
    What led you to ascertain that is was made in raw x86 and not compiled into x86 from C? How can you (and I in the future ) determine this?
  7. Clownacy


    Tech Member
    I've modified asm68k before to fix some of its bugs, using IDA Pro to view its assembly code. Machine-generated assembly looks very different to hand-written assembly, often being much simpler, more readable, and overall easier to understand. Hand-written assembly also rarely stores variables on the stack, opting instead to use registers, even when passing data to and from functions.

    One bug in asm68k is that, if a `moveq` instruction's source operand is a number between $80 and $FF, then the generated machine code will always have a source operand value of $71. This bug is caused by asm68k storing the operand's value in a register, calling a function which overwrites that register, and then trying to read the operand's value from the register. This kind of bug should not occur in compiled C code due to register management being an entirely automated process that the programmer cannot influence. Being outside of the programmer's control means that it is effectively immune to human error. Meanwhile, when writing raw assembly, register management is very much under the control of the programmer and therefore subject to human error. Sonic 2 contains many bugs like this, and that game is also written in raw assembly.
  8. Cokie


    C. Okie Member
    I have noticed this from games like Ecco and Sonic Spinballl that there is some really ugly code. First in what ways besides not using the stack, is hand written assembly simpler, more readable and easier to understand. And what are the big causes for this? If you please give examples of some of the main examples of it being simpler and examples of causes.

    Why doesn't hand written assembly as often use a stack frame? -- Speed pushing popping parameters return address local frame on / off reason vs using registers if you have only need few parameters for d0-d7 a0-d7 ?
    Well I get guess one reason would be you have tons of variables and makes it more complicated and have to manage them incrementing decrementing to access them?

    Also I seen code that displaces from the SP is ugly in Eco 2 has crap similar to this implement from the stack.

    Code (Text):
    1. InnerProduct3D:  MOVEM.L  D4,-(SP)      * save registers
    2.                  ADD.L    #-12,SP       * for local storage
    3.                  MOVE.L   20(SP),D4     * put x1 in D4
    4.                  MULS     32(SP),D4     * mult x1 by x2
    5.                  MOVE.L   D4,8(SP)      * save result in temp1
    6.                  MOVE.L   24(SP),D4     * put y1 in D4
    7.                  MULS     36(SP),D4     * mult y1 by y2
    8.                  MOVE.L   D4,4(SP)      * save result in temp2
    10.                  MOVE.L   28(SP),D4     * put z1 in D4
    11.                  MULS     40(SP),D4     * mult z1 by z2
    12.                  MOVE.L   D4,(SP)       * save result in temp3
    14.                  MOVE.L   8(SP),D4      * put temp1 in D4
    15.                  ADD.L    4(SP),D4      * add temp2 to temp1
    16.                  ADD.L    (SP),D4       * add temp3
    17.                  MOVE.L   44(SP),A4     * get address for result
    18.                  MOVE.L   D4,(A4)
    19.                  ADD.L    #12,SP        * restore sp
    20.                  MOVEM.L  (SP)+,D4      * restore registers
    22.                  RTS
    24.                  END
  9. Cokie


    C. Okie Member
    Another question, so M68000 manual uses the term "displacement" for the displacement integer in addressing modes such as register indirect and program counter indirect. And it uses the term "index" when referring to the register holding, what I will call here a offset . Is the terms displacement vs index purposefully to distinguish: a displacement is a at assemble time integer constant value , while a index is dynamic 'offset' that changes based on what the register is assigned to? And if so , is this distention consistent in computer engineering at the CPU level?
  10. BenoitRen


    Sonic Spinball was actually programmed in C, so its assembly code would be machine-generated, simpler and more readable.
  11. I need help. I am currently making a ROM hack where Amy is the playable character in Sonic 1 (using the GitHub disassembly). I've made significant progress, but I am stumped with the hitbox of the hammer attack (specifically the width).
    This is what I've managed so far:
    amy roll hitbox crop.png amy hammer hitbox crop.png
    The hitbox is shown with the Sonic Physics Guide overlay script.
    This is the code:
    Code (Text):
    1. ReactToItem:
    2.         nop  
    3.         move.w    obX(a0),d2    ; load Player's x-axis position
    4.         move.w    obY(a0),d3    ; load Player's y-axis position
    5.         subq.w    #8,d2
    6.         moveq    #0,d5
    7.         move.b    obHeight(a0),d5    ; load Player's height
    8.         subq.b    #3,d5
    9.         sub.w    d5,d3
    10.         cmpi.b     #id_HammerAttack,obAnim(a0) ; is hammer attacking?
    11.         beq.s     .hammerisattacking ; if yes, branch
    12.         cmpi.b    #fr_Duck,obFrame(a0) ; is Player ducking?
    13.         bne.s    .notducking    ; if not, branch
    14.         addi.w    #$C,d3
    15.         moveq    #$A,d5
    17. .hammerisattacking:
    18.         move.b    obWidth(a0),d4    ; load Player's width
    19.         move.w     #$18,d4 ; hammer attack's hitbox width
    20.         sub.w    #$10,d3 ; move hitbox upwards
    21.         move.w     #$18,d5 ; hammer attack's hitbox height
    The first two lines of the .hammerisattacking label seem to do nothing. I've tried different variations of lines. I've also tried loading the obWidth in the ReactToItem routine. Nothing seems to work. I don't understand why or what I'm doing wrong.