SSFII Genesis Technical Information (2000-07-26)

From Sega Retro

Logo-txt.svg
This is a copy of an "unofficial" document containing original research, for use as a source on Sega Retro. This page likely exists for historical purposes - the contents should ideally be copy-edited and wikified to make better use of Sega Retro's software.
Original source: https://emu-docs.org/Genesis/ssf2.txt


                   ---------------------------------------
                     SSFII GENESIS TECHNICAL INFORMATION
                        Second Edition (July 26, 2000)
                              Bart Trzynadlowski
                   ---------------------------------------


The purpose of this document is to discuss the workings of the Genesis port of
Capcom's "Super Street Fighter II The New Challengers". Virtually all of this
information was originally found by others' efforts.
        Please credit "those who contributed information" rather than me if
you use any of this information. You may pass this document around freely, but
please keep it unaltered. If you see any errors or have more info, contact me.
I can be reached at [email protected].

        Second Edition: July 26, 2000
                - Major update with correct information thanks to Tim Meekins
        First Edition:  July 21, 2000
                - Initial release


0. THE SSFII CHALLENGE

The challenge behind getting "Super Street Fighter II The New Challengers"
(SSFII) to work on a Genesis emulator lies primarily in the fact that the game
is stored on a 40 megabit (5 megabyte) cartridge.
        The Sega Genesis maps ROM from 0x000000 to 0x3FFFFF which means that
the CPU can only see 4 megabytes of cartridge ROM. SSFII needed more space for
the backgrounds and thus Capcom opted for a special 5 megabyte cartridge with
bankswitching in order to access the area of ROM outside of normal range.


1. THE BANKSWITCHING MECHANISM

The bankswitching mechanism probably resides on the cartridge. I'm 99% sure of
this. Sega documentation does offer a description of the mechanism, though,
which led me to suspect for a moment that perhaps the mechanism was on the
Genesis itself.
        The idea is unlikely though, as that range of registers is used by the
32X for an entirely different purpose. I have been informed that that range
(which is marked as "SEGA RESERVED" in the Genesis developer's manual) can be
used for extra devices which may be present on the cartridge.

The bankswitching mechanism is very simple. It views the addressable 4 mega-
bytes of ROM as 8 512KB regions. The first area, 0x000000-0x07FFFF is fixed
and cannot be remapped because that is where the vector table resides.       
        The banking registers on the cartridge work by allocating the 512KB
chunk to a given part of the addressable 4MB ROM space. Below are the
registers and what range they correspond to. The value written to a register
will cause the specified 512KB page to be mapped to that region. A page is
specified with 6 bits (bits 7 and 6 are always 0) thus allowing a possible 64
pages (SSFII only has 10, though.)

        0xA130F3:       0x080000 - 0x0FFFFF
        0xA130F5:       0x100000 - 0x17FFFF
        0xA130F7:       0x180000 - 0x1FFFFF
        0xA130F9:       0x200000 - 0x27FFFF
        0xA130FB:       0x280000 - 0x2FFFFF
        0xA130FD:       0x300000 - 0x37FFFF
        0xA130FF:       0x380000 - 0x3FFFFF

The registers are accessed through byte writes. I haven't seen SSFII try to
read any of the registers, so I don't know if it's possible or not. I don't
emulate anything besides byte writes in my emulator.
        There is also a register 0xA130F1. Apparently the regions specified by
0xA130F9-0xA130FF (0x200000-0x3FFFFF) can be either ROM or RAM and can be
write-protected. Here is the layout of the register as far as I know:

          7  6  5  4  3  2  1  0
        +-----------------------+
        |??|??|??|??|??|??|WP|MD|
        +-----------------------+

        MD:     Mode -- 0 = ROM, 1 = RAM
        WP:     Write protect -- 0 = writable, 1 = not writable

Emulation of the 0xA130F1 register is not necessary, SSFII initializes it at
start-up by writing 0 I believe, which signifies ROM and no write protection
to the regions at 0x200000-0x3FFFFF (ROM isn't writable anyway, there isn't a
need for protection.) 

Examples:

        If 0x01 is written to register 0xA130FF, 0x080000-0x0FFFFF is visible
        at 0x380000-0x3FFFFF.
        If 0x08 is written to register 0xA130F9, the first 512KB of the
        normally invisible upper 1MB of ROM is now visible at 0x200000-
        0x27FFFF.

The registers simply represent address ranges in the 4MB ROM area and you can
page in data to these ranges by specifying the bank # -- it's that simple!


2. CHECKSUM

SSFII contains a checksum routine which calculates the checksum of the first
4MB of the ROM; it then pages in the last 1MB of ROM to 0x300000-0x3FFFFF.
It does this by writing to the bank registers. The following code is taken
directly from the US ROM dumped by Genesis Power:

        ... Checksum first 4MB of ROM (0x000000-0x3FFFFF) ...
        0x003BEC:       move.b  #$08, ($A130FD)
        0x003BF4:       move.b  #$09, ($A130FF)
        0x003BFC:       lea     ($300000), a0
        ... Checksum uppper 1MB of ROM now mapped at 0x300000-0x3FFFFF ...

The bankswitching hardware must be properly emulated before the game starts
working. If it is not, the checksum will fail and the game will hang with a
red screen.
        In the Genesis Power US dump, you can jump to 0x003C3C to avoid the
checksum routine, this is where program flow transfers to if the checksum was
found to be valid. The game doesn't do anything important before the checksum
code as far as emulation is concerned. It writes the SEGA message to the
Trademark Security System register and does some joypad init stuff.


3. EMULATING SSFII

SSFII is fairly straightforward to emulate. All you need to do is detect the
game, load up all 5MB, and emulate the banking registers. The easiest way to
do it, in my opinion, is to keep a second copy of the ROM and do memcpy()s
from that second copy to the ROM being emulated.

For example, here is how Genital does it... I've got genesis.rom which is the
buffer where ROMs are loaded and executed. There is also genesis.rom_ssf2
which is another 5MB buffer where I load the SSFII ROM to as well.
        The bankswitching register emulation is done in my IOWriteByte()
function (which handles byte writes to the 0xA00000-0xBFFFFF range). The
following is the code I use:

if (addr >= 0xa130f3 && config.ssf2_emu)
{
        switch (addr & 0xf)
        {
        case 0x3:                    /* 080000-0FFFFF */
                memcpy((genesis.rom + 0x080000), (genesis.rom_ssf2 + (data * 0x80000)), 0x80000);
                break;
        case 0x5:                    /* 100000-17FFFF */
                memcpy((genesis.rom + 0x100000), (genesis.rom_ssf2 + (data * 0x80000)), 0x80000);
                break;
        case 0x7:                    /* 180000-1FFFFF */
                memcpy((genesis.rom + 0x180000), (genesis.rom_ssf2 + (data * 0x80000)), 0x80000);
                break;
        case 0x9:                    /* 200000-27FFFF */
                memcpy((genesis.rom + 0x200000), (genesis.rom_ssf2 + (data * 0x80000)), 0x80000);
                break;
        case 0xb:                    /* 280000-2FFFFF */
                memcpy((genesis.rom + 0x280000), (genesis.rom_ssf2 + (data * 0x80000)), 0x80000);
                break;
        case 0xd:                    /* 300000-37FFFF */
                memcpy((genesis.rom + 0x300000), (genesis.rom_ssf2 + (data * 0x80000)), 0x80000);
                break;
        case 0xf:                    /* 380000-3FFFFF */
                memcpy((genesis.rom + 0x380000), (genesis.rom_ssf2 + (data * 0x80000)), 0x80000);
                break;
        default:
                break;
        }
        return;
}        

Pardon the bad formatting, it just wouldn't fit correctly in the 80 columns
that the rest of the document sticks to.
        The first line checks wether the address is 0xA130F3 or above (notice
how 0xA130F1 is ignored, SSFII doesn't really use it) and if SSFII emulation
is enabled (in Genital, I have a flag -- config.ssf2_emu -- which is set to 1
to indicate SSFII emulation should occur.) The remainder of the code is a
switch () statement which handles the register emulation.
        Since 0x80000 = 512KB, and the data variable holds the byte written to
the register, data * 0x80000 yields the offset into the ROM that has been
requested to be mapped.
        At first glance, memcpy()s may seem inefficient and slow. Although it
is true, this doesn't apply to emulating SSFII since the game doesn't do much
(if any) bankswitching during gameplay. No slowdown due to the memcpy()s is
noticable.

If you are using an emulator like Starscream which requires the ROMs to be
byteswapped, make certain both ROM images are byteswapped. If you don't use
2 copies of the ROM, you will find the game will still have several glitches
because the memcpy()s will be destroying the data they write over.


4. CONCLUSION

That's all there is to it. This should be sufficient information for anyone
wishing to implement SSFII support in their Sega Genesis emulator. If you have
any questions at all (I'm not great at making things clear) feel free to email
me at [email protected].