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


    I think I'm paranoid 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


    Tech Member
    Sonic Spinball was actually programmed in C, so its assembly code would be machine-generated, simpler and more readable.
  11. EDIT: Should've added this a while ago, but this has long since been resolved.

    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.
    Last edited: Feb 19, 2024
  12. is there a reason the only "sonic starts with x rings" code is this one for sonic 2?:
    "2VAT-BCRA Start most levels with some rings (1 player game only)."
    would it be as easy as just changing a value on this one to rings instead of lives?:
    "NN8A-AADN Start with 99 lives (player 1)."
    i'm trying to find a code to start with 50 rings
  13. MarkeyJester


    Nothing's Impossible Resident Jester
    Sonic 2 resets most of its variable RAM spaces to 0 by clearing a register, and then moving that register to multiple variables in a row. This is done to save CPU time and ROM space. If you edit the value of the register, all of the variables will be set to 50, not just rings, the score, timers, and various other important flags will be set to 50 as well.

    This is why the code you provided didn't do that, instead 2VAT-BCRA simply changes which register is used to clear the ring's variables. The register they changed it to has different values depending on the level (left over garbage likely from decompression of the art). I strongly suspect the code was discovered by mistake.

    This code will set player 1 to have 50 rings at the start of ever act:

  14. thanks again @MarkeyJester!
    i was excited when i heard sega ages had a "super sonic mode" and i wanted to see if you could do it on console.
    i was just curious, before seeing your codes i tried to figure it out with a pro action preplay code "infinite rings"
    which is FFFE20:00C8
    i asked my sister what the hexadecimal of 50 was cus i'm still learning haha
    and i tried FFFE20:0032
    it worked! but you don't lose rings over time. was i close? or is it not possible to do it with one PAR code

    also i first tried your 4 codes on a mega everdrive pro and it worked perfect. but i tried a sonic 2 cartridge on a game genie and it freezes when you start an act
    Last edited by a moderator: Oct 17, 2023
  15. nineko


    I am the Holy Cat Tech Member
    FFFE20:0032 works in a different way.

    With Game Genie codes (such as the ones provided by MarkeyJester) you're altering (or "patching") the contents of the ROM, whereas with (Pro) Action Replay codes (such as FFxxxx:yyyy) you're altering the contents of the RAM in real time.

    This means that the specified value (in this case, $32, which is decimal 50) gets written to a particular address in RAM (in this case, $FFFE20, which is where the ring counter is stored). Which is good and all, except that the game has no way to overwrite that value afterwards, because it keeps on getting set to $32.

    If you want to take that route, you should enable the code at the beginning of each level, have it set the ring counter to 50, and disable the code afterwards, allowing the game to undisturbedly handle RAM.
  16. Brainulator


    Regular garden-variety member Member
    I thought Game Genie and PAR codes work with both ROM and RAM.
  17. MainMemory


    Kate the Wolf Tech Member
    PAR can modify ROM also, and I think some emulators allow for GG codes to modify RAM (since they're handled by the same underlying system) but a real GG cart cannot modify RAM.
    • Informative Informative x 2
    • List
  18. is there a tool to convert sonic 2's rev00 game genie codes to rev01, or a guide or something on how to do it? or do you just have to understand how the code works
  19. Dinopony gave me a great explanation on how to convert sonic 2 rev00 gg codes to rev01. Here it is:

    Alright, so to do that, you need to use Bizhawk. Open your REV00 ROM inside Bizhawk
    Go to "Tools > Cheat Code Converter", type the Game Genie code you want to port for the other version inside the lower "Cheat Code" textbox, press Enter and it will disappear.
    Now go into "Tools > Cheats" and you should see the cheat being applied.

    **On my version of the emu, the code didn't work this way, but it's mostly to get the address and the value it applies**
    For instance, here the `AH2T-CAHN` code is meant to reduce the amount of rings needed to get a checkpoint out of a starpost from 50 to 1
    We see it writes the value "1" at address `0x1F1EC`, so the old value must have been 50 (`0x32` in hex)
    Now go to "Tools > Hex Editor", and in the Hex editor window that just opened, go to "Options > Memory Domains > MD Cart"
    Now you can do Ctrl+G ("Go to Address") and paste the address we just found above (1F1EC)
    Well well well! We indeed have a 0032 that will get replaced by a 0001.
    So, that's pretty cool but now we want to port it to REV01
    This chunk of code that surrounds our value **has** to be somewhere inside REV01, it's just... somewhere else in the ROM.
    Select a bit of code before and after.
    If you select too much, you probably won't find it in REV01, if you select too few, you might have several results and not know which one is the right one.

    Then, Ctrl+C and Ctrl+V, and you have the "signature" that you need to look for in your REV01 ROM:
    Now open your REV01 ROM inside Bizhawk, open the Hex Editor again, and inside the MD Cart domain as before, do Ctrl+F to find a chunk of bytes, and type the signature you're looking for
    Looks like we found it
    What's interesting here is the `0032` value that gets replaced by the GameGenie code, which is at address `0x1F1FC` (which is just 16 bytes further than in REV00... that's really close)
    Then you can use an encoder like this one online:
    Type 0001 in Value, 1F1FC in Address, press enter and...
    There you go, you have your GameGenie code for REV01 that does the same thing as in REV00
    The principle is the same everytime :
    - Find a working code for REV00
    - Decode it to know what it changes inside the ROM (here it was `0032` -> `0001`)
    - Find what's in the vincinity of this address in the REV00 ROM, copy paste it somewhere
    - Search for this "signature" inside the REV01 ROM, it's most likely the same thing
    - Find the exact address where you can find the original value that was being changed (`0032` in our example)
    - Create a new GameGenie code
    • Like Like x 2
    • Informative Informative x 1
    • List
  20. muteKi


    Fuck it Member
    I know I love to ask weird questions but is there anyone out there who's tried to fix the bug with the PSG instrument in Lava Powerhouse in Spinball? What even happened there, did a loop flag fail to get set somewhere?