Sega Virtua Processor/Virtua Racing Research

Discussion in 'Discussion & Q&A' started by ralakimus, Nov 12, 2018.

  1. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    Hello, as some of you may know, I have been reverse engineering Virtua Racing to see how the SVP works myself. It is currently known that it's an SSP1601 chip running a custom ROM that generates Mega Drive tile data from model data and that the 68000 can directly access the SVP's DRAM (which is mapped at $300000 on the 68000 side). The chip's XST register can also be directly written to at $A15000.

    However, I wanted to see how this specific ROM worked. Here are my current findings:

    $300000-$305FFF - Tile buffer #1
    $306000-$30BFFF - Tile buffer #2
    $30EC00-$30FBFF - Model/camera data
    $30FC00-$30FDFF - Sprite data
    $30FE00 - Screen mirror flag
    $30FE02 - Command finished flag
    $30FE04 - Tile buffer address
    $30FE06 - Command sent flag
    $30FE08 - Command ID
    $30FE10 - Command parameter (SEGA logo sequence frame ID, title screen end transition sequence frame ID)

    The SVP is initialized by clearing DRAM and setting the XST to the string "OH" and disabling the chip's so called "halt flag" (referenced from the current SVP documentation as of November 2018).

    A command is handled by setting the "sent flag" to a nonzero value and then setting the command ID and then checking the "finished flag" to see if it's nonzero.

    Here are the command I have documented:

    0 - Invalid
    1 - Communication test
    2 - Render models and sprites (with camera, used by Virtua Racing mode and Free Run mode in Virtua Racing)
    3 - Render models and sprites (with camera, used by during Virtua Racing mode when the goal is reached or a game over has happened)
    4 - Renders models and sprites (with camera and split screen, used by the 2P VS. mode in Virtua Racing)
    5 - Render SEGA sequence (Used by the SEGA screen in Virtua Racing)
    6 - Render models (with camera, used by the title screen in Virtua Racing, also renders the Virtua Racing logo close-in transition for the end of the title screen)
    7 - Render the "VR" texture (used by the ending of the title screen sequence)
    8 - Render models and sprites (with camera and course ranking table taking up the first 6 sprite slots, used by the title screen in Virtua Racing)
    9 - Render models (Has smaller position range, but more precision, used by the mode select screen in Virtua Racing)
    $A - Render models and sprites (with camera, used by the instant replay in Virtua Racing)
    $B - Render models and sprites (with a fixed camera, used by the instant replay in Virtua Racing)
    $C - Render models (with camera, has smaller position range, but more precision)
    $D - Invalid
    $E - Invalid
    $F - Invalid

    When a command is sent and completed, a DMA transfer can be done directly from DRAM to VRAM. Virtua Racing uses double buffering using the tile buffers as seen above so after each transfer, it swaps the buffer to use. The buffer is not cleared by the SVP, so it must be done manually by the 68000.

    As for model data, in commands 4 and 6, the first model slot is generally the main camera and can also render the main map. Commands 5 treats the first slot like any other model.

    Command 5 is special, as the placement of the SEGA models are done by the SVP itself and where they are is determined by $30FE10, which gets constantly incremented every frame. In command 5, the only other movement that can be applied is Z axis rotation.

    For each model, it uses $40 bytes of DRAM for its variables, and this is what I have found so far (it also applies to the camera):
    $00 - Visible flag
    $02 - X position
    $04 - Y position
    $06 - Z position
    $08 - X angle
    $0A - Y angle
    $0C - Z angle
    $26 - Model data pointer (which is divided by 2 because the SVP handles things by words instead of bytes, and typically OR'd with $8000000 to handle autoincrement on the SVP's side)

    For each sprite, it uses $10 bytes of DRAM for its variables, and this is what I have documented:
    $00 - Visible flag
    $04 - X and Y position (00YY YYXX XXXY YYYX in binary, note X position is divided by 4 here)
    $06 - Sprite data pointer (same format as a model data pointer)

    The model format is very simple. The first word is just the poly count minus 1, and what follows is the actual poly data. The first 2 bytes for each poly are a set of flags. The high byte handles the colors of the poly. A poly can have up to 2 colors, which are dithered together. Each nibble is a palette index. The low byte is a set of flags with the following bitfield:


    S - Shape (0 = square, 1 = triangle)
    D - Dithering pattern (0 = vertical line, 1 = checkerboard)
    C = Backface culling flag (0 = off, 1 = on)

    Then for each vertex, it's just its X position, Y position, and Z position, all being signed word values. 4 vertices for a square poly and 3 for a triangle poly.

    For sprites, its format is also rather simple. The first word is the width of the sprite divided by 4 minus 1. The second word is the height of the sprite minus 1. This means sprites can only have widths that are multiples of 4. The rest is the actual pixel data, which is generally formatted like a bitmap, while also still using the same pixel format as the Genesis (meaning that each byte still represents 2 pixels with each nibble being a palette index).

    The way I formatted the ROM to make this run was to change the header up a little and implement the DSP ROM and debugging functions. I changed the local and international names to be like in Virtua Racing and put "53 56 00 00 20 00 04 00" at $1C8 in the header as well. I then ripped the DSP ROM and debugging functions (located at $800 and ends at $1FFFF in Virtua Racing) and padded from the end of the header to $800 and put the data at $800.

    Also, some other notes, mainly regarding previously stated information I found in the past, including Sega Retro:
    • As far as I know, there is no texture mapping supported at all for polygons.
    • Same for lighting, seeing as it's only kinda simulated by the colors and dithering set on certain polygons. The SVP wouldn't seem to know what the palette was to try and calculate lighting.
    • I'm highly unsure about "PWM audio" that I've read about on some websites, seeing as the audio pins on the Virtua Racing cartridge aren't even connected to anything.

    And that's all I have for now. I think it's generally enough to get something working at least, but there's still work to do.
    Last edited: Oct 21, 2019
  2. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    So, command 2 is pretty much command 6, except it can render sprites!

    See updated OP for more information.
  3. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    So, I've been really digging into the actual SVP code for a bit with the help of IDA and the SSP1601 module that was released by Notaz (author of PicoDrive, which was the first emulator to emulate the SVP!), alongside previously existing documentation, and a debugging tool I created to trace execution.

    Things I've noticed so far:
    • Alongside a 6 level hardware stack, the SVP also implements a software stack. My best guess is that they probably wanted to avoid wasting stack space, since there's only 6 levels.
    • It dynamically loads code into IRAM.
    • Obvious copy and pasted code in places (kek)
    Other things that were already documented but I still wanna bring up:
    • One thing I find pretty neat about the SSP1601 is the ability to branch to a subroutine on a condition. (i.e. "call z=1,Label" will only call "Label" if the zero flag is set). The same thing applies to the "mod" instruction (used for bit shifting, negating, and getting an absolute value).
    • For some reason, when it does multiplication, the result is multiplied by 2.
    • You can store a pointer in a RAM bank and then use a RAM pointer and effectively use it like you would an address register on the 68000. Although, it always automatically increments.
    Also, I did see that previous documentation said that "PWM audio" was a thing on the SVP, but as far as I know, that's bullshit, as Virtua Racing doesn't even make use of the audio pins on the cartridge.

    Let's see where this leads up, shall we?
    Last edited: Jun 30, 2019
  4. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    Double post, but here's something neat I just found.
  5. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    So, it turns out command 7 doesn't cause corruptions, but rather it's supposed to just render the one "VR" texture on screen for when the title screen sequence it about to end and loop back to the SEGA logo.


    Updated the OP. The reason why I thought it caused a glitchy mess before was that I tested the command outside of Virtua Racing, and thus, it was reading corrupt data.

    Also, command 6 also renders the transition from the title screen to the SEGA logo. (Where the Virtua Racing logo closes in on the screen).

    Last edited: Jul 4, 2019
    ProjectFM likes this.
  6. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    So, I pretty much have a good grasp on how exactly Virtua Racing handles the generated tiles from the SVP. Normally, a Genesis game would keep itself in sync with VSync, and then do all the major graphics transfers during VBlank. However, because the number of tiles generated by the SVP is so large, even with display disabled and with DMA, it would take too long to transfer all of them to VRAM without some issues. So, here's what Virtua Racing does:

    The game doesn't (always) sync itself with VSync, but rather it utilized the H-INT and a counter. Every 8 scanlines, a counter (whose base value is 0x1B) is decremented, and once that counter has run out, it resets the counter (V-INT also resets the counter), it then starts doing all the major graphics transfers. Notice that 0x1B * 8 = 216, 8 pixels less than the typical 224 vertical resolution. What the game is doing here is it's making it so that it starts the updates earlier than normal, and also ends before where the graphics start displaying. Of course, with that in mind, it purposely has top and bottom borders, so the game isn't full screen. The H-INT routine also uses a flag in which when clear, it won't run the updates at all. I'll mention it later down below.


    And also here's how a general game loop works:
    • Wait until the H-INT counter is set to 0x1A
    • Once that condition is met, run one of the following routines
      • Frame initializationn
      • Render setup (part 1)
      • Render setup (part 2)
      • Render setup (part 3) and game execution
      • Frame lag
    • Set to allow updates to run during H-INT and wait for updates to happen
    • Loop
    The "frame initialization" tells the SVP to start generating tiles and also tells the H-INT to do game mode specific general updates.
    The "render setup" is split up into 3 parts as you can see. Here's what the H-INT does for each part:
    • Part 1 transfers the first 0x26C0 bytes of generated tile data from the tile buffer via DMA, and also sets the background's HScroll.
    • Part 2 transfers the next 0x1280 bytes from the tile buffer, and if a flag is set, it'll transfer the palette data to CRAM, and then also transfer HScroll data for a scroll by scanline setup (used in the 2P mode to scroll the backgrounds in each screen independently)
    • Part 3 transfers the final 0x26C0 bytes from the tile buffer, and sets the VScroll value, depending on which tile buffer is being used.
    The "frame lag" only happens when the SVP isn't fully completed with generating tile data. Typically, it'll just tell the H-INT to do the same updates as in "frame initialization".

    In the end, it's possible that the SVP is capable of producing graphics real quickly, and that the reason the framerate is limited the way it is is due to tile transfer speed limitations., but don't take my word for it.

    [​IMG] [​IMG]
    Last edited: Oct 22, 2019
    AkumaYin likes this.
  7. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    I ripped all the models I could find in Virtua Racing and converted them into OBJ format. I also provided the original SVP format files as well.

    I still need to figure out how to handle palettes for exporting before I release this add-on for Blender at all. If anyone here has any ideas at all, please shoot em towards me. The way I currently have it set up for importing is that it creates a material for every possible color combination from a palette. This wouldn't be so bad if it weren't for the fact that each face is assigned 2 colors from the palette, so with this setup, it's super counter intuitive and hackish.

    Attached Files:

    Last edited: Apr 17, 2020
    TheInvisibleSun likes this.
  8. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013

    Getting something figured out...
  9. ralakimus

    ralakimus pretty much a dead account Member

    Aug 26, 2013
    Yeah, I know, the 3rd bump in a row, but things are really looking up now.


    I may be able to release this soon!
    Last edited: Apr 20, 2020
  10. KingofHarts

    KingofHarts Well-Known Member Member

    Sep 30, 2012
    Figured I would post an update on this courtesy of Ralakimus himself, considering the new progress that's been made recently. He sent this my way and asked if I could spread the word.

    It's his old Blender plugin. All should be good, aside from one flag that controls Z sorting that he couldn't quite figure out.

    Last edited: Feb 2, 2021