MMC3: Implemented submapper 3 (MM-ACC) - fixes Incredible Crash Dummies

-Also fixed a bug in NES 2.0 mapper ID code
This commit is contained in:
Souryo 2016-06-02 20:35:08 -04:00
parent 4a784ff87a
commit 53f1808f73
4 changed files with 48 additions and 8 deletions

View file

@ -21,6 +21,8 @@ class MMC3 : public BaseMapper
RegE001 = 0xE001 RegE001 = 0xE001
}; };
uint8_t _subMapperID;
uint8_t _currentRegister; uint8_t _currentRegister;
uint8_t _chrMode; uint8_t _chrMode;
uint8_t _prgMode; uint8_t _prgMode;
@ -36,6 +38,8 @@ class MMC3 : public BaseMapper
bool _wramEnabled; bool _wramEnabled;
bool _wramWriteProtected; bool _wramWriteProtected;
bool _needIrq;
struct { struct {
uint8_t Reg8000; uint8_t Reg8000;
uint8_t RegA000; uint8_t RegA000;
@ -61,6 +65,8 @@ class MMC3 : public BaseMapper
_wramEnabled = false; _wramEnabled = false;
_wramWriteProtected = false; _wramWriteProtected = false;
_needIrq = false;
} }
protected: protected:
@ -145,7 +151,7 @@ class MMC3 : public BaseMapper
BaseMapper::StreamState(saving); BaseMapper::StreamState(saving);
Stream(_state.Reg8000, _state.RegA000, _state.RegA001, _currentRegister, _chrMode, _prgMode, Stream(_state.Reg8000, _state.RegA000, _state.RegA001, _currentRegister, _chrMode, _prgMode,
_irqReloadValue, _irqCounter, _irqReload, _irqEnabled, _lastCycle, _cyclesDown, _irqReloadValue, _irqCounter, _irqReload, _irqEnabled, _lastCycle, _cyclesDown,
_wramEnabled, _wramWriteProtected, ArrayInfo<uint8_t>{_registers, 8}); _wramEnabled, _wramWriteProtected, ArrayInfo<uint8_t>{_registers, 8}, _needIrq);
} }
virtual uint16_t GetPRGPageSize() { return 0x2000; } virtual uint16_t GetPRGPageSize() { return 0x2000; }
@ -208,12 +214,41 @@ class MMC3 : public BaseMapper
} }
} }
void TriggerIrq()
{
if(_subMapperID != 3) {
CPU::SetIRQSource(IRQSource::External);
} else {
//MM-ACC (Acclaim copy of the MMC3)
//IRQ will be triggered on the next falling edge of A12 instead of on the rising edge like normal MMC3 behavior
//This adds a 4 ppu cycle delay (until the PPU fetches the next garbage NT tile between sprites)
_needIrq = true;
}
}
public: public:
MMC3(uint8_t subMapperID)
{
_subMapperID = subMapperID;
}
MMC3()
{
_subMapperID = 0;
}
virtual void NotifyVRAMAddressChange(uint16_t addr) virtual void NotifyVRAMAddressChange(uint16_t addr)
{ {
uint32_t cycle = PPU::GetFrameCycle(); uint32_t cycle = PPU::GetFrameCycle();
if((addr & 0x1000) == 0) { if((addr & 0x1000) == 0) {
if(_needIrq) {
//Used by MM-ACC (Acclaim copy of the MMC3), see TriggerIrq above
CPU::SetIRQSource(IRQSource::External);
_needIrq = false;
}
if(_cyclesDown == 0) { if(_cyclesDown == 0) {
_cyclesDown = 1; _cyclesDown = 1;
} else { } else {
@ -233,14 +268,15 @@ class MMC3 : public BaseMapper
_irqCounter--; _irqCounter--;
} }
if(ForceMmc3RevAIrqs() || EmulationSettings::CheckFlag(EmulationFlags::Mmc3IrqAltBehavior)) { //SubMapper 2 = MM-ACC (Acclaim MMC3 clone)
if(_subMapperID != 2 && (ForceMmc3RevAIrqs() || EmulationSettings::CheckFlag(EmulationFlags::Mmc3IrqAltBehavior))) {
//MMC3 Revision A behavior //MMC3 Revision A behavior
if((count > 0 || _irqReload) && _irqCounter == 0 && _irqEnabled) { if((count > 0 || _irqReload) && _irqCounter == 0 && _irqEnabled) {
CPU::SetIRQSource(IRQSource::External); TriggerIrq();
} }
} else { } else {
if(_irqCounter == 0 && _irqEnabled) { if(_irqCounter == 0 && _irqEnabled) {
CPU::SetIRQSource(IRQSource::External); TriggerIrq();
} }
} }
_irqReload = false; _irqReload = false;

View file

@ -104,7 +104,7 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData)
case 1: return new MMC1(); case 1: return new MMC1();
case 2: return new UNROM(); case 2: return new UNROM();
case 3: return new CNROM(false); case 3: return new CNROM(false);
case 4: return new MMC3(); case 4: return new MMC3(romData.SubMapperID);
case 5: return new MMC5(); case 5: return new MMC5();
case 7: return new AXROM(); case 7: return new AXROM();
case 9: return new MMC2(); case 9: return new MMC2();

View file

@ -644,8 +644,12 @@ void PPU::ProcessPreVBlankScanline()
if((_cycle - 260) % 8 == 0) { if((_cycle - 260) % 8 == 0) {
//Cycle 260, 268, etc. This is an approximation (each tile is actually loaded in 8 steps (e.g from 257 to 264)) //Cycle 260, 268, etc. This is an approximation (each tile is actually loaded in 8 steps (e.g from 257 to 264))
LoadSpriteTileInfo(); LoadSpriteTileInfo();
} else if(_cycle == 257) { } else if((_cycle - 257) % 8 == 0) {
_spriteIndex = 0; //Garbage NT sprite fetch (257, 265, 273, etc.) - Required for proper MC-ACC IRQs (MMC3 clone)
_memoryManager->ReadVRAM(GetNameTableAddr());
if(_cycle == 257) {
_spriteIndex = 0;
}
} }
} }
} else if(_cycle == 321 && IsRenderingEnabled()) { } else if(_cycle == 321 && IsRenderingEnabled()) {

View file

@ -71,7 +71,7 @@ struct NESHeader
{ {
switch(GetRomHeaderVersion()) { switch(GetRomHeaderVersion()) {
case RomHeaderVersion::Nes2_0: case RomHeaderVersion::Nes2_0:
return (Byte8 & 0x0F << 4) | (Byte7 & 0xF0) | (Byte6 >> 4); return ((Byte8 & 0x0F) << 4) | (Byte7 & 0xF0) | (Byte6 >> 4);
default: default:
case RomHeaderVersion::iNes: case RomHeaderVersion::iNes:
return (Byte7 & 0xF0) | (Byte6 >> 4); return (Byte7 & 0xF0) | (Byte6 >> 4);