don't click here

ASM Spinball disassembly

Discussion in 'Engineering & Reverse Engineering' started by Andlabs, Oct 1, 2010.

  1. Scrambled Eggman

    Scrambled Eggman

    Worm Bagged Tech Member
    29
    142
    28
    Picking at Sonic Spinball Disassembly
    Couple of progress milestones to share today:

    OSD triggers & animation
    The OSD was intuitively one of the first things to look at since it's the part that ties in to the human-readable ASCII strings in this ROM. Using triggers from the level-specific code as a reference, I've been able to label all of the animations available to use, including all parameters to control the timings and colour palettes of each message.

    Enter anims
    Code (Text):
    1.  
    2. OSD_ENTER_ANIM_NONE 0
    3. OSD_ENTER_ANIM_SCROLL_FROM_RIGHT 1
    4. OSD_ENTER_ANIM_SCROLL_FROM_LEFT 2
    5. OSD_ENTER_ANIM_SPLIT_TEXT 3
    6. OSD_ENTER_ANIM_NONE_PERSISTENT 4
    7. OSD_ENTER_ANIM_SWIPE_FROM_LEFT 5
    8. OSD_ENTER_ANIM_FOLDING_LETTERS 6
    9.  
    Exit anims
    Code (Text):
    1.  
    2. OSD_EXIT_ANIM_NONE 0
    3. OSD_EXIT_ANIM_SCROLL_TO_LEFT 1
    4. OSD_EXIT_ANIM_SCROLL_TO_RIGHT 2
    5. OSD_EXIT_ANIM_SPLIT_TEXT 3
    6. OSD_EXIT_ANIM_SWIPE_UP 4
    7. OSD_EXIT_ANIM_CLIP_BOX_TO_MIDDLE 5
    8. OSD_EXIT_ANIM_FLASH_TEXT_OUT 6
    9.  
    Colour anims
    Code (Text):
    1.  
    2. OSD_MSG_COLOUR_ANIM_FLASHING 1
    3. OSD_MSG_COLOUR_ANIM_PALETTE_CYCLING 2
    4. OSD_MSG_COLOUR_ANIM_STATIC_PERSISTENT 3
    5. OSD_MSG_COLOUR_ANIM_STATIC_TIMED 4
    6.  
    Colour palettes
    Code (Text):
    1.  
    2. OSD_MSG_COLOUR_INVISIBLE 0
    3. OSD_MSG_COLOUR_FLAT_GREEN 1
    4. OSD_MSG_COLOUR_FLAT_RED 2
    5. OSD_MSG_COLOUR_FLAT_CYAN 3
    6. OSD_MSG_COLOUR_FLAT_YELLOW 4
    7. OSD_MSG_COLOUR_RAINBOW 5
    8. OSD_MSG_COLOUR_FIERY_RED 6
    9. OSD_MSG_COLOUR_TOXIC_GREEN 7
    10.  

    The messages themselves use an enum/array offset to determine which colours to use, however the ROM does actually supply a colour palette using an BGR format. I'm pretty sure the first nibble is unused.

    Code (Text):
    1.  
    2. Format: Uses 12-bit BGR colour format
    3.         (0x   0   0-F   0-F   0-F)
    4.                  BLUE  GREEN  RED
    5.              
    6. 000c13f0 - Colour line 0
    7. 000c1400 - Colour line 1
    8. 000c1410 - Colour line 2
    9. 000c1420 - Colour line 3
    10.  
    I've put together a silly little demo of this working. In addition to triggering custom messages, I was able to relocate the message queue in RAM - allowing me to extend the message queue from 3 msgs, to 10 msgs. As such, the sequence in the following image is entirely sequenced by the OSD system.

    [​IMG]

    Animation System
    I had a significant breakthrough in my understanding of the animation system. Any Animated Game Object may reference an animation "definition" in the ROM. This contains a ptr to the sprite asset, and a ptr to the animation timeline data.

    The sprite data I've not been able to make progress with, however I have reached out to @drx, creator of the SSCDec decompressor tool to get insight (and hopefully some kind of documentation) on the compression algorithms used. Of course if anyone else can enlighten me on this tech, I'd appreciate the help.

    The animation timelines however I was able to make progress with. It appears to have an encoded format that allows for certain commands to be executed at certain points in the timeline. There even appears to be a code path to trigger an arbitrary function callback too.

    Each "frame" can be a variable length (in terms of its data, but technically you can change animation speed between frames too). At its most basic, each frame is a 2-byte word that specifies an index that appears to reference a sprite list somewhere on the ROM (likely related to the sprite data ptr mentioned earlier). These can be chained together to make custom animations. There is then a specific command that allows the animation to then jump arbitrarily to another animation timeline, either from the start or at an arbitrary offset inside that timeline. This state machine is utilised by several fundamental game mechanics, like the delayed jumping, the mud launcher in Toxic Caves, the hopping blue bois in Lava Powerhouse etc.

    To experiment with this, I messed around and put together a silly little dancing animation of sonic that plays in the options screen (and will also play in-game when you wait around since the options screen directly uses the same animation table as the in-game player).

    spinball_thriller_2.gif spinball_thriller_toxic_caves_2.gif
     
    Last edited: Nov 17, 2024
  2. Scrambled Eggman

    Scrambled Eggman

    Worm Bagged Tech Member
    29
    142
    28
    Picking at Sonic Spinball Disassembly
    EDIT: Fixed some attachments

    Uncompressed Sprites
    As previously mentioned, I've been investigating the asset management and rendering of sprites and tiles in Spinball. I am learning the VDP protocols and architecture as a complete newbie, so it will take some time to decipher all of the opcodes and controls.

    I was however able to find some information around the data structure of the uncompressed sprites in the ROM. The notes I used were found at https://sonicresearch.org/guides/sbhack/ (which appears to have gone down since?). It however was incomplete and missing key information, such as the way the x size of the sprite is stored.

    Uncompressed Sprite format (incomplete)
    Code (Text):
    1. X offset | signed word
    2. Y offset | signed word
    3. Y size   | byte
    4. X size   | byte - This is read as (val + 1) * 2
    5. Data     | byte[(xsize*ysize)/2] - each byte is a pair of 4-bit palette indices. The palette used gets determined by the sprite object instance, not the sprite data
    The first bit of valid sprite data that I've been able to locate begins at 0x14D6. Reading from this address onwards results in some reasonably accurate sprite output, however the observant of you will notice some oddities with a good number of these sprites.

    spinball_sonic_sprites_incomplete.png

    Sprites that extend beyond 32x32 pixels appear to have some additional data at the beginning of the sprite (you can see the pixel data at the top left of the examples below). This results in my attempts at rendering the sprite to offset unusually, but also there is some additional data that usually comes after the sprite. There is likely some header data to signal that the sprite is made up of multiple "tiles". Presumably the additional tiles should supply an offset and size data just like the main sprite. It's quite possible this allows for much larger arrays of sprites to be chained together, such as the Scorpius tail. I hope to locate this behaviour in the code to fully understand the encoded header data that can be seen here.

    spinball_sonic_sprite_large_tile_data.png

    Sonic Spintool
    I'm cobbling together a tool that allows me to trawl the ROM data and verify any findings I am making via the decompilation/disassembly of the ROM. I hope to use this more to potentially find unused data inthe released ROMs and Prototype ROMs. I hope to iterate on this as a public tool for exploring Spinball, but ultimately be a tool to hack it.
    sonic_spintool.png

    Sonic Spinball (August 1993 Prototype) - Prototype-only sprites found
    As a sort of test run, I tried using the crude search algorithm to find sprite data in the August '93 prototype ROM. The results are a very limited set due to the data format not being fully understood just yet, however there were a few tidbits!

    Code (Text):
    1. 0xFEA6, 0xFDDC - Butterfly
    2. 0xFFD0, 0x100EE - Bird
    3. 0x10224, 0x1030E - Bat
    4. 0x10408, 0x104AE - Bee
    These locations refer friendly animals are analogous to the flickies/birds seen in the released version. These are less of a hidden asset, but it was interesting as I had not noticed the spawned animals when killing enemies were different before!

    Code (Text):
    1. 0x1744A, 0x17644 - Switch with triangle shape (Toxic Caves)
    2. 0x17762, 0x178DC - Switch with star shape (Toxic Caves)
    3. 0x27102, 0x2725C - Horizontal switch (Lava Powerhouse)
    These locations seem to contain additional switch variants that are not seen in-game, or to be found in the released USA ROM. The Toxic Caves switches being most distinctive due to having unique shapes (a triangle and a star). Curiously however, these switch variant can be found in the tile maps for the J2ME version of spinball. This would suggest that the assets were stripped from the ROM late in the day to bring it home under a 1MiB budget.

    upload_2024-11-29_15-35-48.png

    "SSC" Compressed data
    I was able to reach out to drx about the SSCDec decompressor for spinball data, however they did not have any recollection or source code available to help. Fortunately it did occur to me that I have been using a decompiler this whole time, and so I could try to figure out at least why the tool didn't work for me.

    It turns out that the tool looks for a very specific part of the ROM header to determine if it's valid or not. The part that it looks at seems like an odd choice to me (and drx could not recall either lol) - and it turns out it was incorrectly looked for the string GN NK- when in fact the ROM contains the string GM MK-. I've hex-edited this mistake and the tool functions correctly again. Keep in mind however that it appears to be using fixed address locations, so will only work with the USA release.

    Code (Text):
    1. iVar1 = _stricmp("GN NK-1",local_24);
    2. if (iVar1 == 0) {
    3.     FUN_00401334(0x394aa,"Stage 1 Foreground");
    4.     FUN_00401334(0x3dbb2,"Stage 1 Background");
    5.     FUN_00401334(0x4fee4,"Stage 4 Foreground");
    6.     FUN_00401334(0x53214,"Stage 4 Background");
    7.     FUN_00401334(0x64bb6,"Stage 2 Foreground");
    8.     FUN_00401334(0x67672,"Stage 2 Background");
    9.     FUN_00401334(0x7da22,"Messages font");
    10.     FUN_00401334(0x7f29c,"Stage 3 Foreground");
    11.     FUN_00401334(0x81eb6,"Stage 3 Background");
    12.     FUN_00401334(0xbde06,"Options - frames");
    13. }
    - The decompilation that showed the ROM locations based on the incorrect strcmp.

    SSCDecPatched.png

    SSC Decompressor Patched: View attachment SSCDec_Patched.zip
     

    Attached Files:

    Last edited: Nov 29, 2024
    • Like Like x 13
    • Informative Informative x 1
    • List
  3. Aerosol

    Aerosol

    Not here. Moderator
    11,201
    601
    93
    Not where I want to be.
    Sonic (?): Coming summer of 2055...?
    This is pretty cool stuff. Keep going!
     
  4. Chimes

    Chimes

    The One SSG-EG Maniac Member
    970
    676
    93
    Have you figured out how to edit YM2612 values in the music yet? It'd be interesting to see what's going on with the Options theme as to my recollection the "tesla coil" patches have their volume set at 0, the maximum volume value
     
  5. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,491
    1,829
    93
    your mom
    Spinball just uses GEMS, with the patch table starting at 0xAA0B0, from what I gathered.
     
    • Informative Informative x 1
    • Useful Useful x 1
    • List
  6. Scrambled Eggman

    Scrambled Eggman

    Worm Bagged Tech Member
    29
    142
    28
    Picking at Sonic Spinball Disassembly
    @Chimes Yeah I believe the GEMS data was located earlier in this thread. I've quoted below for convenience, but I have not attempted to dissect the audio systems just yet as I figured it was a more standardised part of Spinball that had would already been looked into at some point.

    This thread will likely be of use to you, if you have not already been able to locate it on the forum yourself. The last post contains uploads of numerous tools for extracting the GEMS data from any ROMs that use it.
    https://forums.sonicretro.org/index.php?threads/gems-sound-driver-research.18892/

    Additionally, the Sega Retro wiki page has a download link for the GEMS utilities which includes the tools for editing and composing audio with the driver. I can't vouch for whether it's compatible with whatever version of GEMS that Spinball uses however. All I will say is you will need a way to run 16-bit software (Running using DOSBox appears to work correctly from some small tests I did a while back)
    https://segaretro.org/GEMS

    Github

    I've now made my work publicly available at: https://github.com/Eggplant891/spindisasm

    A lot of the notes in the doc folder are not up-to-date or complete, which is why I have not yet performed a pull request just yet. The main source of all my latest findings are within the Ghidra project archive.

    However I can update them after a pull request if I were to have permissions on the main repo :shobon:

    EDIT: You know it's been a long time since I've actively been a forum denizen when I casually forget about double-posting rules.
     
    Last edited: Nov 30, 2024
  7. This is exciting stuff. I've always believed Spinball without the lag and with better physics would be a great game, I'm excited this finally might actually happen.
     
  8. Scrambled Eggman

    Scrambled Eggman

    Worm Bagged Tech Member
    29
    142
    28
    Picking at Sonic Spinball Disassembly
    Uncompressed sprite format solved
    • Sprites consist of one or multiple tiles that may be up to 32x32 each. So large objects will consist of multiple tiles.
    • Each sprite specifies a number of tiles that it will define (and appears to define the number of VDP tiles it will require i.e. a 32x32 sprite will require 16 (0x10) tiles)
    • Each tile defines its offset and size in the header of the sprite data.
      NOTE: The Y size is defined before the X size.
      NOTE: The X size is defined as the number of bytes i.e. the width of the sprite / 2. So 0x10 == 32 pixels.
    • The pixel data for the tiles is stored sequentially after the last tile's header data. Each byte contains 2 4-bit palette indices, so will define 2 pixels per byte.

    EDIT: Sprite data begins at ROM offset 0x14D2, and all uncompressed sprites are stored sequentially from that point onwards. 0x3909D seems to be the final byte of uncompressed sprite data.

    Code (Text):
    1. Num Tiles - unsigned word
    2. Num VDP Tiles - unsigned word
    3. (For each tile)
    4. X Offset - signed word
    5. Y Offset - signed word
    6. Y size - byte
    7. X size - byte
    8. (For each tile)
    9. Pixel Data[X size * Y size] - 2x 4-bit palette indices per byte
    upload_2024-12-3_1-38-49.png
     
    Last edited: Dec 3, 2024
    • Informative Informative x 7
    • Like Like x 4
    • Useful Useful x 1
    • List
  9. Bobblen

    Bobblen

    Member
    452
    232
    43
    Really cool to see Spinball getting a bit of attention. Given the famously short turn around time there could be all sorts of little fragments of things sitting unused in the code that there wasn't time for the devs to remove.

    Also looking forward to the inevitable Knuckles sprite hack :-D you know someone will end up doing it!
     
  10. Scrambled Eggman

    Scrambled Eggman

    Worm Bagged Tech Member
    29
    142
    28
    Picking at Sonic Spinball Disassembly
    SSC format decompression solved

    Using offsets conveniently provided in the SSCDec tool (and using the output of that tool to verify my work), I have been able to make sense of and document the SSC compression format. This compression format is used for the level tile graphics and likely for other non-sprite assets such as those used in the intro sequences and main menu.

    Code (Text):
    1. ---SSC compression format---
    2.  
    3. --- Header ---
    4. unsigned short - Number of tiles (seems to only be needed for loading to VDP)
    5.  
    6. --- Data block ---
    7. (Up to any number of these can be defined)
    8.  
    9. byte - Bit field of which data in the following fragments is raw or represents instructions to "blit" a value multiple times.
    10.       (this gets left-shifted 1 bit each iteration, then the bit_flag is used to determine how to read the next set of data)
    11.  
    12. [Up to x8 of the following fragments per data block]
    13. if (bit_flag == 1)
    14.   Raw data - Writes this fragment to the target location, as-is, read from the source data.
    15. else
    16.   Blittable data - This will read a previously written byte sequence. The information to perform this is packed into 2 bytes.
    17.   - The first byte is the lower part of the offset to read the data value from.
    18.   - The second byte has  2 parts:
    19.      - First 4 bits used as an upper nibble for the read offset
    20.      - Second 4 bits represents the number of bytes to print (n + 1).
    21.  
    22.   Example:
    23.     Byte 1 - $18  - Will copy data from offset $18 from the start of the already decompressed data.
    24.     Byte 2 - $1C  - Split into 2 parts:
    25.                     => ($1 << 4) | $18 => $118
    26.                     => Will blit a sequence that is $D ($C + 1) bytes long. Will read from $118 to $125 and blit each byte sequentially.
    27.              
    28. The indices used to reference the previously written values must be sanitised using a bitwise AND (0x0FFF) as the upper 4 bits may contain stale data in the register.
    29.  
    EDIT: The decompressed tile assets use the same format for defining pixel data in the uncompressed sprite assets. Consequently, each tile is 4x8 bytes.

    EDIT2: Reworded some parts as it read as though it would blit the same byte multiple times, rather than blitting a sequence of bytes.

    Couple of screenshots of the Spintool rendering the background tileset for The Machine, and foreground tileset for Toxic Caves:

    upload_2024-12-5_3-37-55.png

    upload_2024-12-5_3-25-31.png
     
    Last edited: Dec 10, 2024
    • Like Like x 7
    • Informative Informative x 2
    • List
  11. Bobblen

    Bobblen

    Member
    452
    232
    43
    I've enjoyed the internet detective work in this thread. I'd never heard of Tony H's Sonic Spinball Rom Editor before, or Dr. X's SSCDec tool. I didn't know the options screen music is allegedly 'broken' (I thought it was just meant to sound like that because the 90s) and have been enjoying all the remixes on youtube.

    One nugget that did stick in my sieve like brain is that the stage select code is different in the prototype, as per Saxman in this thread.
    https://forums.sonicretro.org/index.php?posts/1051736/

    EDIT
    looks like Tony H found it too (along with the buttons for the stage select in the even older proto)
    Sonic Spinball ROM Editor

    Looking forward to whatever discoveries are next!
     
    Last edited: Dec 5, 2024
  12. Scrambled Eggman

    Scrambled Eggman

    Worm Bagged Tech Member
    29
    142
    28
    Picking at Sonic Spinball Disassembly
    Sprite import pipeline
    I've spent the past couple of days working on the pipelines in the Spintool to allow for easy hacking of the uncompressed sprite assets. More work is still needed to export to multi-tile sprites reliably (and with a stable disassembly a way to resize the sprites), but this example shows palette modding and multiple custom sprites being pushed into the ROM, using alpha-channelled PNG assets as a source.

    EDIT: This change also adds support for exporting the sprite assets to PNG.

    Not a whole lot of insight with this update, but it's a milestone in the journey towards others eventually being able to make Spinball hacks.

    Credit to @Paphvul for the Tails sprites

    spinball_tails_demo.gif

    upload_2024-12-9_23-40-42.png
     
    Last edited: Dec 10, 2024
  13. Paphvul

    Paphvul

    Member
    14
    13
    3
    THE BABY!

    God, I made that edit years ago, I'm honored you liked it enough to actually implement it into the ROM! I'll see about doing the rest of the sheet, soon.
     
  14. Sonic Hachelle-Bee

    Sonic Hachelle-Bee

    Taking a Sand Shower Tech Member
    823
    218
    43
    Lyon, France
    Sonic 2 Long Version
    Can't wait for Sonic Spinball & Knuckles
     
  15. Paphvul

    Paphvul

    Member
    14
    13
    3
    Worked on it a little bit more. Next update will be more involved.

    https://sta.sh/027dmjw81oop

    Oh, shit, hi! I loved Sonic 2 Long Version as a kid!

    Also, don't tempt me, I'll fuckin' do it! XD (After I finish the li'l foxie baby, obviously.)
     
    Last edited: Dec 14, 2024
  16. Techokami

    Techokami

    For use only on NTSC Genesis systems Researcher
    1,375
    86
    28
    HoleNet!
    Sonic Worlds Next
  17. Paphvul

    Paphvul

    Member
    14
    13
    3
    Huh, hotlinking the image didn't work, I edited the post to just link to where it's hosted
     
  18. Scrambled Eggman

    Scrambled Eggman

    Worm Bagged Tech Member
    29
    142
    28
    Picking at Sonic Spinball Disassembly
    A mix and match of updates today!

    Spintool improvements
    I've been spending a decent amount of time refactoring a bit of the Spintool code to look to improving the general maintainability of the tool. With this I have been able to implement numerous quality of life improvements with the ultimate aim to make this usable by people that aren't me. It still runs like ass and hogs memory, but it sure does the job for the moment!

    The tool now supports importing sprites that use multiple "tiles", which has allowed for the extended set of sprites that Paphvul has made to be used.
    spinball_tails_emerald_power2.gif

    It can also visualise these tiles, as well as rendering an origin point to visualise the offsets better. Sprites that are aligned in the ROM may have whacky offsets that can give rise to extra visual movement that you might not otherwise realise when looking at the sprites in isolation.

    The level tile deserialisation and search tools has begun to take some kind of shape. The tool will now load tilesets from the known locations, and can currently be viewed in the sprite navigator. I hope to decipher the level layout format and show full previews of levels in the near future.

    Spintool Screenshots
    upload_2024-12-20_3-28-28.png
    upload_2024-12-20_3-29-54.png
    upload_2024-12-20_3-35-16.png

    Level data fun
    Since I've been spending a lot of time looking at sprites and level assets, I felt like doing some more digging around the physics and gameplay code a bit more. I've continued collating level data and tried a few hacks to observe the effects. The result of one such hack was this customised Toxic Caves intro sequence that makes it use the common intro animation from other levels, and customises the camera start location and player entry location to make you enter straight onto the mud launcher.

    spinball_custom_toxic_caves3.gif

    Jumping physics
    I've been spending some time documenting and labelling all of the player states and animations, and I was able to find the code that controls 2 of the crappiest aspects of platforming in spinball:
    - Delayed jumping
    - All momentum is lost when jumping

    The first was relative straight forward. There is a timer set when the player enters a "pre jump" state, which when it expires will then "Launch" the player with a set X/Y velocity. The timer was a simple value hack to reduce it to a single frame.

    The momentum required some new code, but it was still relatively simple. This "Launch" function overwrites the X and Y velocities when called, which is why jumping cancels your momentum. To transfer the momentum, I had to translate the player X velocity into the value space that is used by the Launch method (RE: they are different values and don't obey typical signed/unsigned word value ranges, oh and they're flipped just for good measure) then push that as the x velocity parameter, instead of the default which as just a neutral value that would cancel all x velocity.

    I can proudly say that platforming in Spinball is now actually no longer complete ass (video linked):



    EDIT: Added the snippet used to translate the parameters for the Launch function. This is shimmed the switch branch at loc_DD1F6 in the disasm (I have pushed this to my github fork in the spinball_modified.asm source)
    Code (Text):
    1.  
    2. new_jump_physics:
    3.    ; pea     (unk_4000).w
    4.    ; pea     (off_50).w
    5.    move.l  D7,-8(sp)            ; Defensively maintaining register states since this is a hack
    6.    clr.l   D7
    7.    move.w  ($FF5776),D7         ; Get player X velocity (major unit only atm)
    8.    lsl.w   #8, D7
    9.    lsl.w   #3, D7               ; << 11 bits to move over 3 bytes i.e. 000D -> D000, then divide by 2
    10.    neg.w   D7                   ; X Velocities are flipped when calling Launch function
    11.    addi.w  #$4000,D7            ; $4000 is the neutral X velocity value for launches
    12.    move.l  D7,-(sp)             ; Push new launch X velocity value
    13.    move.l  -4(sp),D7            ; Rescue original D7 register value
    14.    move.l  #$50,-(sp)           ; Original value of $50
    15.    jmp new_jump_physics_return
    16.  
     
    Last edited: Dec 20, 2024
    • Like Like x 12
    • Useful Useful x 1
    • List
  19. Bobblen

    Bobblen

    Member
    452
    232
    43
    Seeing proper jumping physics in Spinball after all these years is just bizarre! (in a good way), strange decision from the devs to limit it the way they did, can't blame that one on performance. Excellent work.

    Also excited about the future possibility of new maps created from the actual data rather than stitched together screenshots.
     
  20. Chimes

    Chimes

    The One SSG-EG Maniac Member
    970
    676
    93
    Ever play Prince of Persia?
    I highly suspect that's what the controls were modelled after.
     
    • Agree Agree x 1
    • Informative Informative x 1
    • List