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. EDIT 3: Rewrote the whole God damn thing to be more accurate and up-to-date.

    This is a WIP disassembly of Sonic CD for the Sega CD by administrator Devon, targeting all regional releases. So far, the following is disassembled and can be messed around with.

    General Programming:
    • Main and system programming
    • Genesis/Mega Drive initialization code
    • Backup RAM
    • Sub CPU
    • SMPS-FM and SMPS-PCM sound drivers
    Game Modes:
    • Title Screen
    • Sound Test
    • DA Garden
    • Visual Mode
    • Full-motion videos
    • Special Stages
    • Time Warp cutscene
    Levels:
    • Palmtree Panic Act 1 (all time periods).
    • Wacky Workbench Act 1 Present
    Download here

    Note that the CDDA audio is not provided in the disassembly; you will need to rip it from your own Sega CD copy instead.
     
    Last edited: Sep 22, 2024
  2. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    Yeah this is something I've been doing. I've been posting about it on SSRG for a bit now, but I'd like to share some additional info. Keep in mind, this is a very WIP thing.
    The repository does not provide the other CD files. You need to put them in yourself in the _Original folder.
    Just Palmtree Panic's, though.

    Now, let's talk about why the level folder is organized the way that it is. It basically has to do with how the original source code was organized and assembled. Here's a sample, using the file names found in the Gems Collection version's symbol data for reference:
    [​IMG]
    As you can see, there's a number of copy and pasted files for various things, like scrolling/level rendering, object tables, and other data. Unfortunately, things are not the most consistent between these copies. Let's take LevelSizeLoad for example.

    In R11A, it starts like this:
    Code (Text):
    1. ; -------------------------------------------------------------------------
    2. ; Get level size and start position
    3. ; -------------------------------------------------------------------------
    4.  
    5. LevelSizeLoad:
    6.     moveq    #0,d0                ; Clear unused variables
    7.     move.b    d0,unusedF740.w
    8.     move.b    d0,unusedF741.w
    9.     move.b    d0,unusedF746.w
    10.     move.b    d0,unusedF748.w
    11.     move.b    d0,eventRoutine.w        ; Clear level event routine
    In R11B, it's this:
    Code (Text):
    1. ; -------------------------------------------------------------------------
    2. ; Get level size and start position
    3. ; -------------------------------------------------------------------------
    4.  
    5. LevelSizeLoad:
    6.     lea    objPlayerSlot.w,a6        ; Get player
    7.  
    8.     moveq    #0,d0                ; Clear unused variables
    9.     move.b    d0,unusedF740.w
    10.     move.b    d0,unusedF741.w
    11.     move.b    d0,unusedF746.w
    12.     move.b    d0,unusedF748.w
    13.     move.b    d0,eventRoutine.w        ; Clear level event routine
    R11B also retains leftover Sonic 1 code for handling credits, while R11A removes it. This puts me in a tricky situation regarding how to organize things. For now, I'm just doing what the original source code did, for the most part.

    To be honest, this is turning out to be better for documentation than it is for hacking.

    btw I added R11D last night, making the entirety of Palmtree Panic Act 1 disassembled and buildable, all with SonLVL support.
     
    Last edited: Jun 9, 2022
  3. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    tfw the demo variant of R11A in the Japanese and European version of the game was built using a slightly older version of R11A, making me have to insert more if/else statements to keep things building bit-perfect.

    Added DEMO11A.

    Also, imagine if GOODEND.BIN streamed data from BADEND.STM and BADEND.BIN streamed data from GOODEND.STM, making it so that the game has to load GOODEND.BIN for the bad ending and vice versa... Man, the development cycle must've been a pain.
     
    Last edited: Jun 10, 2022
  4. Nik Pi

    Nik Pi

    Member
    500
    345
    63
    Kazakhstan
    Sonic 2: Archives
    How difficult is sound editing in fmv intro and outros? As far as I remember, something like modified WAV is used there? Do you have any plans to disasm these videos?

    Sorry, if it's a stupid question... Have a nice day!
     
  5. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    The FMV data format is fairly simple. It's just an array of packets of tiles and Ricoh PCM data. Eventually, I'll figure out a way to split out the data, or at least make a tool to make your own STM file.

    On that note, so there's a slight quirk with the audio playback. They set a PCM loop flag at the very end of wave RAM, and didn't want to overwrite it. Each packet contains 0x8000 bytes of PCM data. However, wave RAM is split into 0x1000 byte banks, which can only be accessed one at a time. Their solution was to only write 0xFFF bytes per bank written to. As a result, only 0x7FF8 bytes out of the packet are written, there's a gap around every 0x1000 bytes in wave RAM, and the last 8 bytes in a packet are skipped. I guess they figured it wasn't that noticeable enough. As it turns out, though, that loop flag isn't really necessary, as playback will just wrap back to the top of wave RAM after going past the end, which is what the FMV audio playback code does anyways with that loop flag.
     
    Last edited: Jun 20, 2022
  6. Nik Pi

    Nik Pi

    Member
    500
    345
    63
    Kazakhstan
    Sonic 2: Archives
    Ok. Thank you. I remember that old SCD fmv hack, where are intro was changed to Rick Roll. Will be great to have a chance to edit not only levels, but the cutscenes too!
     
  7. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    Last edited: Aug 28, 2022
    • Like Like x 7
    • Useful Useful x 1
    • List
  8. Unlimited Trees

    Unlimited Trees

    we Do a Little Mischief, Mischief... Member
    95
    73
    18
    When
    UNITRES, Project Time, etc.
    • Agree Agree x 2
    • Like Like x 1
    • List
  9. Nik Pi

    Nik Pi

    Member
    500
    345
    63
    Kazakhstan
    Sonic 2: Archives
    Stupid question, but..
    Does game have a code, who's said to go in R3 after R1, or it's just go to next .MMD file?
     
  10. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    End results card increments the level ID and IPX makes it load the next file.
     
  11. Nik Pi

    Nik Pi

    Member
    500
    345
    63
    Kazakhstan
    Sonic 2: Archives
    OK, thanks
     
  12. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    So, how about some specifications for the special stage maps (possibly to use for making a tool)?
    • The map itself is just a standard 4096x4096px Sega CD stamp map, with 32x32 stamps. Stamp data and stamp map data are compressed in Kosinski.
      • Stamps are basically just Genesis tiles, arranged vertically, exactly like how tiles are arranged for sprites.
      • [​IMG]
      • Stamp IDs are the address relative to Word RAM divided by 0x80, making 32x32 stamp IDs multiples of 4 only (remember, 16x16 stamps are also a thing on the Sega CD).
      • Stamp maps then are 128x128 stamps in size. Each stamp ID is a word.
        • HRR0 0SSS SSSS SSSS
          • S = Stamp ID (last 2 bits are actually ignored with 32x32 stamps, again, multiples of 4 only)
          • R = Rotation (00 = 0deg, 01 = 90deg, 10 = 180deg, 11 = 270deg)
          • H = Horizontal flip (done after rotation)
          • [​IMG]
    • Stamps are loaded 0x200 bytes into Word RAM, making the first stamp always blank (the stamp bank address is always fixed at the start of Word RAM, by the way).
      • Stage 3 has a secondary set of stamps loaded 0x10000 bytes into Word RAM.
    • Each stamp is assigned a type via a separate array of byte sized IDs (like how each 16x16 in regular stages gets a collision block ID via its own separate array).
      • 0 = Path
      • 1 = Bumper
      • 2 = Fan
      • 3 = Water
      • 4 = Rough surface
      • 5 = Spring
      • 6 = Hazard
      • 7 = Big boost pad
      • 8 = Small boost pad
      • 9 = Oil (Yes, the unused oil stamps have this. No, it doesn't do anything)
    • Stamp animations are handled in its own routine with its own stage specific data
      • 2 animations types: hazard and fan
      • Animation data is an array of stamp IDs to place in various locations in the stage
        • Each entry starts off with a word value indicating the raw offset in the stamp map to write the stamp ID. It's then followed with the stamp IDs used in the animation.
        • Hazard animations have 4 frames, and fan animations have 3.
    • Object positions on the map are 16.16 fixed point.
    • Each UFO has their own path data to follow. No, they do not actively dodge the player, they stay strictly on the path.
      • First byte is the UFO item type
        • 0 = Rings
        • 1 = Speed shoes
        • 2 = Time (Only valid for the time UFO, which is actually a separate object. Regular UFOs will just give you rings)
        • 3 = Hand (Unused, just gives you rings)
      • Second byte is some unknown, unused flag.
      • Each path node contains 5 words:
        • First word is how many frames it takes to move to the next node. Basically, its speed.
        • Second and third words are the node's starting X and Y positions respectively. When a UFO starts on a node, it automatically snaps to this position.
        • Fourth and fifth words are the node's target X and Y positions respectively. It'll move towards this position at within the time specified.
      • Data is terminated with a single 0xFFFF.
    • Time UFO has its own path data that's shared between every stage.
    • A quick fun fact: regular UFOs are always purple and the vertical stripes on them change color depending on the item they have (yellow for rings, white for speed shoes). The time UFO is always blue.
    Data in the disassembly can be referenced, of course.
     
    Last edited: Oct 20, 2022
    • Like Like x 5
    • Informative Informative x 2
    • List
  13. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
  14. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    So, the opening FMV has been extracted into a GIF and 2 WAV files (1 for USA, 1 for Japan/Europe). I also hacked together a quick program to convert them back into an STM file (bit perfect, too), and is included in the disassembly. Currently only supports the opening FMV format, support for the ending and pencil test FMV formats will come later down the line.

    If you want to implement your own FMV with this, make sure that the GIF is 256x112 and has at max 16 colors per frame, and runs at 8 FPS. WAV file can be unsigned 8-bit or signed 16-bit mono or stereo at any sample rate (although the resampling code is very very rudimentary and shit, so it may be best to do that manually to 32768 Hz. Really, the program as a whole was hacked together, but it works for now. I may come back to it later and do a """proper""" version, or maybe someone will come along and do a better job.

    I have this ffmpeg command for converting a video into a compatible GIF:
    Code (Text):
    1. ffmpeg -i [INPUT FILE] \
    2. -vf "fps=8,scale=256:112:force_original_aspect_ratio=decrease,pad=256:112:(ow-iw)/2:(oh-ih)/2,setsar=1,split[s0][s1];[s0]palettegen=max_colors=16:stats_mode=single[p];[s1][p]paletteuse=dither=[DITHER ALGORITHM]:new=1" \
    3. [OUTPUT FILE]

    If for whatever reason you want the video to stretch out to 256x112, you can remove the "force_original_aspect_ratio" bit. You can also make it so that only 1 palette is generated for the entire video by removing the "stats_mode=single" bit as well.

    This is MakeSTM's usage:
    Code (Text):
    1. USAGE: MakeSTM [GIF file] [Wave file] [Color mode] [Sync mode] [Output file]
    2.  
    3. Note: Only supports the opening FMV format so far!
    4.  
    5. Color mode:
    6.     0 = Hardware
    7.     1 = Multiples of 16
    8.     2 = Multiples of 18
    9.  
    10. Sync mode:
    11.     0 = Don't auto-sync video and audio
    12.     1 = Enable auto-sync

    For the opening FMV provided by the disassembly, it's already set up to handle the timing issues with the FMV player, so auto-sync is set to off for it in the build script. However, for custom videos, you should set it to 1.
     
    Last edited: Sep 27, 2022
  15. Unlimited Trees

    Unlimited Trees

    we Do a Little Mischief, Mischief... Member
    95
    73
    18
    When
    UNITRES, Project Time, etc.
    YOU ARE MY HERO... please continue ur incredible work with the sonic cd disassembly ur work has been crazy
     
  16. Pyrochrome

    Pyrochrome

    Member
    6
    0
    1
    Something weird happened.....I made a small code edit so Sonic would jump with just the B button and that changed Palmtree Panic from looking normal, to this:
    SONIC THE HEDGEHOG-CD000.png

    No other changes. No compilation errors. Don't understand what happened. Literally all I did was this

    o.png
     
  17. Nik Pi

    Nik Pi

    Member
    500
    345
    63
    Kazakhstan
    Sonic 2: Archives
    • Like Like x 2
    • Informative Informative x 1
    • List
  18. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    Update:
    • Every PCM driver is now disassembled with their data extracted. Music and SFX in ASM format.
    • Began some cleanup and further documentation that I left untouched forever.
     
  19. Nik Pi

    Nik Pi

    Member
    500
    345
    63
    Kazakhstan
    Sonic 2: Archives
    Hello. I have a question about the music of past tracks;
    If, purely theoretically, I want to do something like this, what should I do? After quickly reviewing the song code, I noticed that smps tracks that use samples instead of FM voices. But, are there any other differences? Obviously, if I replace all the links to FM in the SMPS track with PCM, it will not be able to compile and earn.
     
  20. Devon

    Devon

    DROWN, DROWN, DROWN MYSELF! Tech Member
    1,391
    1,677
    93
    your mom
    • Obviously, no FM/PSG. Only 8 channels for PCM.
      • PCM7 should also be avoided, as that's allocated for SFX, and the driver does not properly override music tracks with SFX.
    • No per song/SFX sample table. A global table is used instead.
    • Sample format is not the same as what's used for streaming to the YM2612's DAC (which is unsigned 8-bit mono PCM). Instead it uses a slightly different format of 8-bit mono PCM for each channel.
      • Range is 0-253, center is at 127.
        • $00 -> Nothing apparently, according to the manual. Maybe 127?
        • $01-$7F -> 126-0
        • $80-$FE -> 127-253
        • $FF is a special flag that makes it loop back to the designated loop address
    • Samples can either be statically stored in wave RAM, or for larger samples, streamed into a designated bank. I'd be careful using static samples, because you are to manually set where in wave RAM they are stored, and streamed samples have their address automatically calculated based on the channel ID.
      • Sonic CD does not store any samples statically in wave RAM, they are all streamed. For simplicity, it's probably for the best to keep up this trend.
      • $FF MUST be used for static samples for them to loop/stop properly, but MUST be AVOIDED for streamed samples, since looping is handled manually as the data is streamed.
    • Should be worth noting that the version of SMPS-PCM in Sonic CD does not actually support rhythm tracks (similar to DAC tracks in regular SMPS). It's only ever referenced, because the support for it was only partially taken out. Enough for it to not work, but still.
    • Vibrato/"modulation" support was taken out
    • Staccato/"note timeout"/"note fill" is also bugged due to how they removed vibrato/"modulation". tl;dr, optimized the call to the staccato handler to not use bsr/jsr, but instead bra, since it's the last function called, but they forgot to remove the line of code that made it stop updating the track if the note was cut.
      • "addq.w #4,sp", which made it skip over returning to the track update routine, and instead the driver update routine. Because the bsr/jsr was changed into a bra, it doesn't actually add another level to the call stack, and as a result, this line of code makes it quit the driver entirely.
      • As a result, if a note is cut with staccato, it'll quit the driver and desync the tracks after by a tick.
    • The driver does not run at 50/60 Hz, since it's not updated during V-BLANK. Instead, it's updated using the Sega CD's timer interrupt, which runs at a different interval. The driver is updated every other interrupt, whose timer is set to 255.
      • (255+1)*30.72 μs = 7864.32 μs = 7.86432 ms
      • Called every other time -> 7.86432 ms * 2 -> Driver called every 15.72864 ms
      • 1000 / 15.72864 = ~63.578 Hz
    • Volume and panning work a bit differently as a result of how the PCM chip works
      • Volume can be $00-$FF. Unlike how volume with the YM2612 and PSG chips work, where $00 is max volume, $00 is minimum volume with the PCM chip, and $FF is the max volume.
      • Panning is smoother than the YM2612's. The YM2612 can only hard pan left or right, or stay in the center. With the PCM chip, you can control the levels of the left and right channels with a range of $00-$0F for each (lower nibble is for the left channel, upper nibble if for the right channel)
        • $FF means center, $F0 = hard right, $0F = hard left, for example
     
    Last edited: Nov 9, 2022
    • Like Like x 1
    • Informative Informative x 1
    • List