Titan Overdrive 2: Part 1

When I first saw Titan’s Overdrive 2 demo I was blown away, both by the presentation itself and by how in the world they managed to push that out of the Mega Drive. After how much I struggled to get Overdrive 1 to work in my emulator, I just assumed that getting Overdrive 2 to work was out of reach.

…But I also thought to myself, why not try?

First Things First

Immediately upon downloading Overdrive 2, you’ll notice the first thing unusual: it has 8MB of ROM. The Genesis only allocates 4MB of its memory map to the cartridge; how does this work?

It turns out that one officially released game already solved this. Super Street Fighter 2 has 5MB of ROM, and it includes a memory controller on the cartridge that allows remapping each 512KB memory bank to any 512KB ROM bank from the cartridge (except for the first memory bank which is always mapped to the first 512KB of ROM).

My emulator already supported this but only for Super Street Fighter 2 specifically. There is apparently a homebrew convention that if a ROM’s header specifies its system as “SEGA SSF” instead of the typical “SEGA GENESIS” or “SEGA MEGA DRIVE”, that is a signal to emulators and flash carts to use the Super Street Fighter 2 mapper. Alright, Overdrive 2 does indeed do that, and it’s easy enough to check for that.

And it boots!

It Boots

Test Run

Let’s let it run and see what needs fixing.

No Rain

Okay, the clouds look all wrong and the rain effect is completely missing.

Overdrive 2 Logo

Uhh…

Arcade

What in the world?

Supposed to Be a Cube

Oh no.

(Not pictured is that the audio player crashed on the Overdrive 2 logo screen, after which the demo simply emitted a high-pitched buzzing noise instead of playing music.)

And Actually the Intro Is Broken Too

The intro sequence (before the ship enters the city) seemed fine visually, but I noticed some audio pops and cracks that didn’t sound like they were supposed to be there. And then upon comparing to the hardware recording I further noticed that some of the audio channels seemed to be completely missing! This seemed like a more major issue because as far as I’m aware the demo only uses undocumented video features, not audio features.

Well…kind of.

The primary Genesis sound chip is a YM2612, a Yamaha FM synthesis chip with 6 FM synth channels. According to Yamaha’s and Sega’s documentation, the YM2612 has 4 write ports: 2 address ports and 2 data ports. When you want to change a global register or a channel 1-3 register, you write a register address to the first address port (generally $4000 in the Z80’s memory map) and then write a value to the first data port (generally $4001). When you want to change a channel 4-6 register, you do the same with the second address port ($4002) and the second data port ($4003).

Except that’s not actually how it works. In reality there’s only one address register and one data port, and the YM2612 remembers which of the 2 address ports was last written to. Whenever either data port address is written to, the YM2612 chooses the register to write based on which address port was last written. Overdrive 2 very frequently makes all of its data port writes through $4001, even for the channel 4-6 registers.

This was at least easy to fix once I figured it out. The main hint was noticing that Overdrive 2 kept enabling FM channels 4 and 5 without configuring them basically at all, which was what caused the popping noises.

Desync

With that audio issue fixed, I let the demo run again, and very quickly noticed another audio issue: video/audio desynchronization. It was really obvious when the ship was flying through the city, because the thunder sound effects were playing way before the lightning flashes.

Thankfully this time I immediately knew the cause, which was that I hadn’t emulated bus arbiter wait states, so it was just a matter of implementing that.

The gist of it is that the Genesis has 2 CPUs, a 68000 (main CPU) and a Z80 (secondary CPU meant for audio processing). Each CPU has its own section of the bus and there’s a bus arbiter that sits in between them. The cartridge is connected to the 68000 bus, so any time the Z80 accesses the cartridge, the bus arbiter inserts wait states for both CPUs in order to avoid a bus conflict. Overdrive 2’s audio player code depends on these wait states for it to play audio at the correct speed.

It was really unclear to me how to even attempt to precisely emulate exactly how many wait states the bus arbiter will insert for each access, but at least for Overdrive 2 it isn’t necessary to emulate at that level. Giving each CPU a fixed average number of wait states on each Z80 ROM access works well enough.

While this fixed the desync issue, it did not fix the audio player crashing on the logo screen.

Double VRAM Mode?

And now for the first undocumented VDP feature: 128KB VRAM mode, toggled by bit 7 in VDP register #1. This seemed like the easiest one to start with, and I had a hunch it would fix the audio player crash due to having an impact on DMA bandwidth. This hunch was largely based on the audio suddenly desyncing during the transition into the Overdrive 2 logo screen, seconds before the audio player crashed.

No, the Genesis doesn’t have a second 64KB of VRAM sitting around somewhere; this feature was added during console development to experiment with the console possibly having 128KB of VRAM split across two 64KB chips, interleaving bytes between them. Presumably the experiment didn’t pan out, or at least the benefit wouldn’t have been worth the cost of another 64KB of RAM.

128KB mode is useless for rendering because it causes the VDP to simply display garbage due to the second RAM chip not existing. However, it has some utility for VRAM writes, namely:

  • It enables byte-size writes to VRAM, which normally only supports word-size writes (whether through the VDP data port or DMA)
  • VRAM write bandwidth is doubled, for both DMA and FIFO queue writes (though only one byte of each word is actually written to VRAM)

One quirk is that 128KB mode uses a very different mapping from VRAM address to physical address on the RAM chip. This is my mapping function:

1
2
3
4
5
6
7
fn convert_128kb_vram_address(address: u32) -> u32 {
    // Formula from https://plutiedev.com/mirror/kabuto-hardware-notes#128k-abuse
    (((address & 0x2) ^ 0x2) >> 1)
        | ((address & 0x400) >> 9)
        | (address & 0x3FC)
        | ((address & 0x1F800) >> 1)
}

It’s not hard to emulate this because it’s just a matter of calling this function for VRAM writes in 128KB mode, but it is something that code using this mode needs to be aware of.

Overdrive 2 uses this feature almost constantly, and implementing it fixed several effects (and also the audio player crash!):

Rain Fixed Logo

The Mighty Debug Register

The (officially) completely undocumented VDP debug register is at the core of Overdrive 2’s most impressive effects. Writing to memory address $C0001C with specific bits set makes some very interesting things happen. …Well, on most consoles; it apparently doesn’t work on all Mega Drive consoles.

The most interesting functionality is the ability to “mask” the entire frame using a specific plane: the sprite plane, background A, or background B. This has two effects:

  • Any pixel that would normally show the backdrop color will show a color from the mask plane instead
  • Any pixel that would normally show from a plane other than the mask plane will have its 6-bit color value bitwise ANDed with the 6-bit color value from the mask plane

Other interesting functionality is the ability to force the VDP to only display pixels from a specific plane, ignoring the other planes and the backdrop color.

This requires more work to utilize than say the SNES color math feature, but it does enable a form of layer blending nonetheless.

The debug register was actually not terribly difficult to emulate, with one quirk: When the sprite plane is used as the mask plane, if all sprite pixels in a particular position are transparent, the AND mask needs to use the palette from the last sprite pixel that was evaluated. If a position contains no sprite pixels, transparent or otherwise, that position will always display color 0 (the result of masking with a sprite pixel of 0x00). The Overdrive 2 logo screen depends on this for the blue circles to display correctly.

Implementing this fixed a lot of effects, though not all of them.

Transparent logo (the Genesis is not supposed to be able to do this):

Transparent Logo

Rotozooming fractals, extremely cool effect in motion:

Rotozooming Fractals

Transparent multi-colored foreground layer (again, the Genesis is not supposed to be able to do this):

More Blending

Prohibited? I Don’t Think So

I noticed that Overdrive 2 was setting invalid background sizes during many screen transitions, which seemed to cause visual glitches. It’s really noticeable here, leading into the Voronoi circles effect:

Voronoi Scroll Size Glitch

This is all that the official documentation has to say on that:

Scroll Size

What does the “prohibited” size actually do? The community exhaustively tested it here. Basically:

  • Setting H size to prohibited forces the background size to 32 cells wide x 1 cell high
  • If H size is not prohibited, setting V size to prohibited has the same effect as setting V size to 32 cells
    • …Unless H size is set to 32 cells, which produces a weird duplication/mirroring effect (but Overdrive 2 doesn’t seem to depend on this)

Implementing this fixed the effect:

Fixed Voronoi Lead-In

However, immediately after that is another problem:

Voronoi H Scroll Glitch

This turns out to be the result of another “prohibited” register setting, this time horizontal scroll mode:

H Scroll Mode

Apparently this setting’s effect has been known to the community for decades and I just missed it. It’s similar to per-line H scroll but it repeatedly cycles through the first 8 scroll values instead of giving each line its own scroll value. Implementing that fixes the Voronoi circles:

Voronoi Fixed

To Be Continued…

Several effects are still broken. The 3D textured cube animation:

Glitched Textured Cube

The plasma twisters:

Broken Plasma Twisters

The arcade scene with its border trickery:

Arcade

These will be covered in a future post.

updatedupdated2024-01-122024-01-12