don't click here

ASM Sonic CD (1993) Disassembly by Devon (and Other Things)

Discussion in 'Engineering & Reverse Engineering' started by Alex Field, Jun 9, 2022.

  1. Devon


    Tech Member
    Okay, so the problem lied with how IDA 6.8 interpreted branching instructions compared to IDA 7.7. 6.8 actually just refers to those instructions as "db", "b", "bra", or "bsr", but IDA 7.7 actually uses the full instruction names, with the size. IDA 7.7 also expands the addresses that are stored as words into 24 bits instead of 32 bits, like in earlier versions.

    I have pushed another update, and have confirmed it to work in both 6.8 and 7.7 now.
  2. Script is working correctly now in IDA 7.6. Thank you :>
  3. The script has some sort of issue with the animal objects in R33C and R33D: it hangs when it reaches those objects in the files. (Removing the lines that makes it analyze the animal object allows the script to finish the rest of the file successfully.)
    Last edited: May 15, 2023
  4. Devon


    Tech Member
    I have tracked down the issue. Turns out there's an animation used by the exit barrier in the CCZ boss in which it doesn't have a flag at the end of the data. As a result, it kept continuing to erroneously parse the animation past the end of it, which busted the area where the animal object was at, preventing it from being properly disassembled.
    Code (Text):
    1. Ani_20EA86:dc.w @Ani_20EA86_0-*
    2. @Ani_20EA86_0:dc.b 0
    3.         dc.b 0, 1, 2, 3, 4, 5, 6, 7, 7

    The reason this animation is like this is because the object itself manually checks which frame it's on before stopping it, meaning the flag isn't really needed.

    I have modified the script that takes this into account. Do let me know if you find any other issues with it running.
    Last edited: May 15, 2023
    • Like Like x 3
    • Informative Informative x 1
    • List
  5. Obviously whoever programmed that didn't know about the animation script flags that could take care of stopping it. But then again, given the baffling and idiotic code I've glimpsed elsewhere (subroutines consisting of ONE LINE?) and the overall impression that the developers were unfamiliar with the engine, and in some cases even the standard libraries, I guess I shouldn't be surprised.

    Anyhow, added the fix to my script (I've modified it to use labels matching Hivebrain's S1 disasm), and it works now. There is one other issue I might as well report:in some but not all MMDs, it doesn't delete the raw chunk data, leaving it all dc.b'ed in the exported ASM after the script-generated incbin. I personally haven't consider this to be too significant of an issue since I'm mainly after the code, but I suspect others might not think the same.
  6. Devon


    Tech Member
    Could you tell me which MMDs do that?
  7. Well, as it turns out, the problem is not with your script, but rather some sort of mistake I made while modifying it to output different labels. (Curiously, it only seems to affect PPZ Acts 1 and 2, though I haven't gotten anywhere near close to trying all the MMDs.) I'm going to leave it be, since it doesn't really impact what I'm doing, and I can just fall back to the unmodified script if I need to look at the data store area. (And there may be a better way to replace the labels by using a script in my editor.)

    That said, I have found a couple of bugs in the unmodified script:
    R11C generates following error: 23B89E: can't rename byte as 'Art_Flower' because the name is already used in the program.

    R11D generates following error: 3A8F2: can't rename byte as 'Art_Flower' because the name is already used in the program.

    R31A: generates following errors:
    20B4B4: can't rename byte as '@Ani_20B4AA_1' because this byte can't have a name (it is a tail byte).
    20B4B7: can't rename byte as '@Ani_20B4AA_2' because this byte can't have a name (it is a tail byte).

    Raw data is left at Art_TitleCardText, unk_2358CA, and unk_235A7A.

    For EVERY MMD, the final bit of ChkObjOnScrWidth:
    Code (ASM):
    2. .OffScreen:
    3.    moveq   #1,d0               ; Mark as offscreen
    4.    rts
    is not disassembled at all, instead being rendered as
    Code (ASM):
    2. dword_20xxxx:
    3.    dc.l $70014E75
  8. Devon


    Tech Member
    @OrionNavattan Fixed R11C and R11D (accidentally left in a definition for Art_Flower that was found inside the padding data made up of leftover garbage data). Fixed the animation in R31A (was another case of animations without ending flags). unk_2358CA and unk_235A7A were also animations for the Kama-Kama badnik that were left uncaught. Put in a quick fix for that by catching it from the Kama-Kama object code. The issue with ChkObjOnScrWidth was with UpdateObjects referencing the object table 4 bytes before the start of it (so that ID 1 actually points to the first actual object entry). Got that fixed.

    The raw data at Art_TitleCardText is actually unused data AFTER the title card text graphics. I'm gonna leave it as is for now, because I'm actually running short on free time (I travel out of the country tomorrow), but just keep that in mind.

    I went ahead and also made it parse the debug table for any uncaught mappings data as well. Hopefully that works okay.

  9. BenoitRen


    I'm curious what the code for a random number looks like in the Mega CD version, because the PC port has the following weird implementation:
    Code (Text):
    2. int random()
    3. {
    4.   int_union lD0;
    5.   int_union lD1;
    6.   unsigned short w;
    7.   lD0.l = ranum;
    8.   if (ranum == 0) {
    9.     lD0.l = 10861 | 13914;
    10.   }
    11.   lD1 = lD0;
    12.   if (lD0.l < 0) {
    13.     lD0.l = lD0.l << 2;
    14.     lD0.l |= -32768;
    15.   }
    16.   else {
    17.     lD0.l = lD0.l << 2;
    18.   }
    19.   lD0.l += lD1.l;
    20.   if (lD0.l < 0) {
    21.     lD0.l = lD0.l << 3;
    22.     lD0.l |= -32768;
    23.   }
    24.   else {
    25.     lD0.l = lD0.l << 3;
    26.   }
    27.   lD0.l += lD1.l;
    28.   lD1.w.l += lD0.w.l;
    29.   w = lD0.w.h;
    30.   lD0.w.h = lD0.w.l;
    31.   lD0.w.l = w;
    32.   lD1.w.l += lD0.w.l;
    33.   lD0.w.l = lD1.w.l;
    34.   w = lD0.w.h;
    35.   lD0.w.h = lD0.w.l;
    36.   lD0.w.l = w;
    37.   ranum = lD0.l;
    38.   return lD1.l;
    39. }
  10. The Mega CD version uses the same psuedorandom number routine as Sonic 1, 2 and 3K (it even uses the same initial seed), although curiously, every instance of it except for the one in the Time Warp Cutscene MMD take the precaution to avoid trashing d1.

    Code (ASM):
    2. ; Modified from my Sonic 2 disasm, but this what nearly every instance of
    3. ; this subroutine in Sonic CD looks like.
    4. RandomNumber:
    5.        move.l   d1,-(sp)            
    6.        move.l   (v_random).w,d1
    7.        bne.s   .scramble               ; if d1 is not 0, branch
    8.        move.l   #$2A6D365A,d1               ; if d1 is 0, use this seed number
    10.    .scramble:
    11.        ; set the high word of d0 to be the high word of the RNG
    12.        ; and multiply the RNG by 41        
    13.        move.l   d1,d0
    14.        asl.l   #2,d1
    15.        add.l   d0,d1
    16.        asl.l   #3,d1
    17.        add.l   d0,d1
    19.        ; add the low word of the RNG to the high word of the RNG
    20.        ; and set the low word of d0 to be the result
    21.        move.w   d1,d0
    22.        swap   d1
    23.        add.w   d1,d0
    24.        move.w   d0,d1
    25.        swap   d1
    27.        move.l   d1,(v_random).w
    28.        move.l   (sp)+,d1
  11. Devon


    Tech Member
    As a side note with the routine, it seems to just be a stock Sega RNG function, because I've seen it in other games.
  12. BenoitRen


    I see the C version does the same thing, but adds separate logic for negative numbers. Fascinating!
  13. Brainulator


    Regular garden-variety member Member
    I'm curious: which ones?