MC-ACC: Update/fix behavior based on recent findings

This commit is contained in:
Sour 2019-12-20 23:40:09 -05:00
parent 933fa3e8ba
commit 4ca3442719
5 changed files with 83 additions and 48 deletions

View file

@ -554,6 +554,7 @@
<ClInclude Include="KeyManager.h" />
<ClInclude Include="Lh51.h" />
<ClInclude Include="Mapper116.h" />
<ClInclude Include="McAcc.h" />
<ClInclude Include="MMC3_198.h" />
<ClInclude Include="MMC3_208.h" />
<ClInclude Include="MMC3_224.h" />

View file

@ -1490,6 +1490,9 @@
<ClInclude Include="TxcChip.h">
<Filter>Nes\Mappers\Txc</Filter>
</ClInclude>
<ClInclude Include="McAcc.h">
<Filter>Nes\Mappers\MMC</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

View file

@ -27,7 +27,6 @@ class MMC3 : public BaseMapper
bool _wramWriteProtected;
A12Watcher _a12Watcher;
bool _needIrq;
bool _forceMmc3RevAIrqs;
@ -37,11 +36,6 @@ class MMC3 : public BaseMapper
uint8_t RegA001;
} _state;
bool IsMcAcc()
{
return _romInfo.MapperID == 4 && _romInfo.SubMapperID == 3;
}
protected:
uint8_t _irqReloadValue;
uint8_t _irqCounter;
@ -93,8 +87,6 @@ class MMC3 : public BaseMapper
_wramEnabled = GetPowerOnByte() & 0x01;
_wramWriteProtected = GetPowerOnByte() & 0x01;
_needIrq = false;
}
virtual bool ForceMmc3RevAIrqs() { return _forceMmc3RevAIrqs; }
@ -192,7 +184,7 @@ class MMC3 : public BaseMapper
SnapshotInfo a12Watcher{ &_a12Watcher };
Stream(_state.Reg8000, _state.RegA000, _state.RegA001, _currentRegister, _chrMode, _prgMode,
_irqReloadValue, _irqCounter, _irqReload, _irqEnabled, a12Watcher,
_wramEnabled, _wramWriteProtected, registers, _needIrq);
_wramEnabled, _wramWriteProtected, registers);
}
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
@ -261,52 +253,31 @@ class MMC3 : public BaseMapper
virtual void TriggerIrq()
{
if(IsMcAcc()) {
//MC-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;
} else {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
public:
virtual void NotifyVRAMAddressChange(uint16_t addr) override
{
switch(_a12Watcher.UpdateVramAddress(addr, _console->GetPpu()->GetFrameCycle())) {
case A12StateChange::None:
break;
if(_a12Watcher.UpdateVramAddress(addr, _console->GetPpu()->GetFrameCycle()) == A12StateChange::Rise) {
uint32_t count = _irqCounter;
if(_irqCounter == 0 || _irqReload) {
_irqCounter = _irqReloadValue;
} else {
_irqCounter--;
}
case A12StateChange::Fall:
if(_needIrq) {
//Used by MC-ACC (Acclaim copy of the MMC3), see TriggerIrq above
_console->GetCpu()->SetIrqSource(IRQSource::External);
_needIrq = false;
if(ForceMmc3RevAIrqs() || _console->GetSettings()->CheckFlag(EmulationFlags::Mmc3IrqAltBehavior)) {
//MMC3 Revision A behavior
if((count > 0 || _irqReload) && _irqCounter == 0 && _irqEnabled) {
TriggerIrq();
}
break;
case A12StateChange::Rise:
uint32_t count = _irqCounter;
if(_irqCounter == 0 || _irqReload) {
_irqCounter = _irqReloadValue;
} else {
_irqCounter--;
} else {
if(_irqCounter == 0 && _irqEnabled) {
TriggerIrq();
}
//SubMapper 2 = MC-ACC (Acclaim MMC3 clone)
if(!IsMcAcc() && (ForceMmc3RevAIrqs() || _console->GetSettings()->CheckFlag(EmulationFlags::Mmc3IrqAltBehavior))) {
//MMC3 Revision A behavior
if((count > 0 || _irqReload) && _irqCounter == 0 && _irqEnabled) {
TriggerIrq();
}
} else {
if(_irqCounter == 0 && _irqEnabled) {
TriggerIrq();
}
}
_irqReload = false;
break;
}
_irqReload = false;
}
}
};

View file

@ -145,6 +145,7 @@
#include "Mapper244.h"
#include "Mapper246.h"
#include "Mapper253.h"
#include "McAcc.h"
#include "MMC1.h"
#include "MMC1_105.h"
#include "MMC1_155.h"
@ -306,7 +307,13 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData)
case 1: return new MMC1();
case 2: return new UNROM();
case 3: return new CNROM(false);
case 4: return new MMC3();
case 4:
if(romData.Info.SubMapperID == 3) {
return new McAcc();
} else {
return new MMC3();
}
case 5: return new MMC5();
case 6: return new FrontFareast();
case 7: return new AXROM();

53
Core/McAcc.h Normal file
View file

@ -0,0 +1,53 @@
#pragma once
#include "stdafx.h"
#include "MMC3.h"
//Based on krikzz's research: https://forums.nesdev.com/viewtopic.php?p=242427#p242427
class McAcc : public MMC3
{
private:
uint32_t _counter = 0;
uint16_t _prevAddr = 0;
protected:
void StreamState(bool saving) override
{
MMC3::StreamState(saving);
Stream(_counter, _prevAddr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if((addr & 0xE001) == 0xC001) {
//"Writing to $C001 resets pulse counter."
_counter = 0;
}
MMC3::WriteRegister(addr, value);
}
void NotifyVRAMAddressChange(uint16_t addr) override
{
if(!(addr & 0x1000) && (_prevAddr & 0x1000)) {
_counter++;
if(_counter == 1) {
//"Counter clocking happens once per 8 A12 cycles at first cycle"
if(_irqCounter == 0 || _irqReload) {
_irqCounter = _irqReloadValue;
} else {
_irqCounter--;
}
if(_irqCounter == 0 && _irqEnabled) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
_irqReload = false;
} else if(_counter == 8) {
_counter = 0;
}
}
_prevAddr = addr;
}
};