profi200 / open_agb_firm

open_agb_firm is a bare metal app for running GBA homebrew/games using the 3DS builtin GBA hardware.
GNU General Public License v3.0
860 stars 42 forks source link

Implement ROM mirroring for Classic NES games #20

Closed Powerlated closed 2 years ago

Powerlated commented 3 years ago

The Classic NES re-releases rely on a multitude of quirks and edge-cases as anti-emulation measures. The cartridges are particularly unique in that they are the only Game Paks to mirror out-of-bounds ROM areas. Without mirroring, in-game controls refuse to work correctly and only accept input somewhat sporadically. At the moment, open_agb_firm pads the out-of-bounds ROM area with 0xFFFFFFFF, which is incorrect behavior for both Classic NES and regular Game Paks.

profi200 commented 3 years ago

I didn't notice any problem with input for these games but i tried only 1 or 2 of them. Example of misbehaving input? Mirroring can be implemented for these games only.

I don't think this is incorrect behavior for all non-Classic NES games. There is no way to make it behave as open bus when accessing the ROM area out of bounds because the hardware cart emu has fixed ROM sizes of 32 or 16 MiB (depending on save type). So padding with 0xFFs is not correct but the best i can do. Mirroring for other games would also be just as wrong.

Powerlated commented 3 years ago

The Classic NES game I was specifically having issues on was Super Mario Bros.

Out-of-bounds ROM reads on non-Classic NES cartridges do not return "regular" open bus, rather they return "cartridge" open bus. Since the Game Pak interface uses the same pins for address and data, the cartridge open bus value is a direct function of the address used to access out-of-bounds ROM.

According to GBATek, (Address/2) AND FFFFh can be used to calculate the correct value for a given ROM address. https://problemkaputt.de/gbatek.htm#gbaunpredictablethings

Endrift's test suite has a test case for this under Memory tests > ROM out-of-bounds load. https://github.com/mgba-emu/suite

profi200 commented 3 years ago

I tested Super Mario Bros. and it's perfectly playable. Are you sure your dump is a verified good one?

The problem i see coming is, that it will give different values for different sizes. As stated on gbatek everything was tested with 32 bit reads.

You mentioned this in the other issue and i tried that. It's also not passing a few unaligned access SRAM tests i can't do anything about.

EDIT: I see why the verified dump is working fine. The dump contains mirrors of the actual ROM within the 4 MiB. Unsure if the mirroring goes beyond the physical ROM size.

Powerlated commented 3 years ago

Theoretically, there shouldn’t be any issues with different read sizes as all Game Pak accesses are executed as 16-bit, meaning a 32-bit read will contain two combined cartridge open bus values as a 32-bit cartridge read is actually two 16-bit reads.

Edit: woops, accidentally closed.

profi200 commented 3 years ago

So, it turns out the verified Classic NES dumps listed on no-intro are actually overdumps. They should not include the mirrors. endrift and vaguerant were able to confirm the ROM chip on The Legend of Zelda is actually 8 Mbit by opening a carttridge.

I will see if i can "fake" the cart open bus behavior. And ROM mirroring needs to be implemented too.

EDIT: Seems to work. I can make all tests pass with this:

    for(uintptr_t i = ROM_LOC + romSize; i < ROM_LOC + MAX_ROM_SIZE; i += 4)
    {
        *((u32*)i) = (i / 2 + 1)<<16 | (i / 2 & 0xFFFFu);
    }
Kirit29 commented 3 years ago

long shot question as i know nothing about reverse engineering the 3ds gba code and emulation development. But would the solution to this problem help to bypass the cartridge emulation size limit?

profi200 commented 3 years ago

No. This is just trying to mimic the behavior when you access the ROM beyond the real size. For example the actual ROM is 1 MiB and you access it 1 byte beyond the 1 MiB. Most ROM chips don't answer to out of bounds requests so you get back whatever was on the bus before the GBA tries to "read" the answer. In this case it's the address the GBA sent to the ROM. So kind of an echo.

For the Classic NES games this is a little different. Instead of not answering the address will wrap around internally in the ROM chip. For the example above if you access it 1 byte beyond its size you will get the very first byte of the ROM back.

ROM space is still limited to 32 MiB max. by the hardware.