diff --git a/Core/CPU.h b/Core/CPU.h index aeaf2293..368404b7 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -882,8 +882,8 @@ public: static void RunDMATransfer(uint8_t* spriteRAM, uint8_t offsetValue); static void StartDmcTransfer(); static uint32_t GetClockRate(NesModel model); - - + static bool IsCpuWrite() { return CPU::Instance->_cpuWrite; } + //Used by debugger for "Set Next Statement" void SetDebugPC(uint16_t value) { SetPC(value); _state.DebugPC = value; } diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c2d57eb3..9e4e917a 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -439,6 +439,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index ad804e3d..391241db 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -778,6 +778,9 @@ Nes\Mappers + + Nes\Mappers + diff --git a/Core/JyCompany.h b/Core/JyCompany.h new file mode 100644 index 00000000..57b50f10 --- /dev/null +++ b/Core/JyCompany.h @@ -0,0 +1,403 @@ +#pragma once +#include "stdafx.h" +#include "BaseMapper.h" +#include "CPU.h" + +class JyCompany : public BaseMapper +{ +private: + enum class JyIrqSource + { + CpuClock = 0, + PpuA12Rise = 1, + PpuRead = 2, + CpuWrite = 3 + }; + + uint8_t _prgRegs[4]; + uint8_t _chrLowRegs[8]; + uint8_t _chrHighRegs[8]; + uint8_t _chrLatch[2]; + + uint8_t _prgMode; + bool _enablePrgAt6000; + + uint8_t _chrMode; + bool _chrBlockMode; + uint8_t _chrBlock; + bool _mirrorChr; + + uint8_t _mirroringReg; + bool _advancedNtControl; + bool _disableNtRam; + + uint8_t _ntRamSelectBit; + uint8_t _ntLowRegs[4]; + uint8_t _ntHighRegs[4]; + + bool _irqEnabled; + JyIrqSource _irqSource; + uint8_t _irqCountDirection; + bool _irqFunkyMode; + uint8_t _irqFunkyModeReg; + bool _irqSmallPrescaler; + uint8_t _irqPrescaler; + uint8_t _irqCounter; + uint8_t _irqXorReg; + + uint8_t _multiplyValue1; + uint8_t _multiplyValue2; + uint8_t _regRamValue; + + uint16_t _lastPpuAddr; + +protected: + virtual uint16_t GetPRGPageSize() { return 0x2000; } + virtual uint16_t GetCHRPageSize() { return 0x0400; } + virtual bool AllowRegisterRead() { return true; } + + void InitMapper() + { + RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); + AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Any); + + _chrLatch[0] = 0; + _chrLatch[1] = 4; + + memset(_prgRegs, 0, sizeof(_prgRegs)); + memset(_chrLowRegs, 0, sizeof(_chrLowRegs)); + memset(_chrHighRegs, 0, sizeof(_chrHighRegs)); + + _prgMode = 0; + _enablePrgAt6000 = false; + + _chrMode = 0; + _chrBlockMode = false; + _chrBlock = 0; + _mirrorChr = false; + + _mirroringReg = 0; + _advancedNtControl = false; + _disableNtRam = false; + + _ntRamSelectBit = 0; + memset(_ntLowRegs, 0, sizeof(_ntLowRegs)); + memset(_ntHighRegs, 0, sizeof(_ntHighRegs)); + + _irqEnabled = false; + _irqSource = JyIrqSource::CpuClock; + _lastPpuAddr = 0; + _irqCountDirection = 0; + _irqFunkyMode = false; + _irqFunkyModeReg = 0; + _irqSmallPrescaler = false; + _irqPrescaler = 0; + _irqCounter = 0; + _irqXorReg = 0; + + _multiplyValue1 = 0; + _multiplyValue2 = 0; + _regRamValue = 0; + + UpdateState(); + } + + void StreamState(bool saving) + { + BaseMapper::StreamState(saving); + + ArrayInfo prgRegs{ _prgRegs, 4 }; + ArrayInfo chrLowRegs{ _chrLowRegs, 8 }; + ArrayInfo chrHighRegs{ _chrHighRegs, 8 }; + ArrayInfo ntLowRegs{ _ntLowRegs, 4 }; + ArrayInfo ntHighRegs{ _ntHighRegs, 4 }; + + Stream(_chrLatch[0], _chrLatch[1], _prgMode, _enablePrgAt6000, _chrMode, _chrBlockMode, _chrBlock, _mirrorChr, _mirroringReg, _advancedNtControl, + _disableNtRam, _ntRamSelectBit, _irqEnabled, _irqSource, _lastPpuAddr, _irqCountDirection, _irqFunkyMode, _irqFunkyModeReg, _irqSmallPrescaler, + _irqPrescaler, _irqCounter, _irqXorReg, _multiplyValue1, _multiplyValue2, _regRamValue, prgRegs, chrLowRegs, chrHighRegs, ntLowRegs, ntHighRegs); + + if(!saving) { + UpdateState(); + } + } + + void UpdateState() + { + UpdatePrgState(); + UpdateChrState(); + UpdateMirroringState(); + } + + uint8_t InvertPrgBits(uint8_t prgReg, bool needInvert) + { + if(needInvert) { + return (prgReg & 0x01) << 6 | (prgReg & 0x02) << 4 | (prgReg & 0x04) << 2 | (prgReg & 0x10) >> 2 | (prgReg & 0x20) >> 4 | (prgReg & 0x40) >> 6; + } else { + return prgReg; + } + } + + void UpdatePrgState() + { + bool invertBits = (_prgMode & 0x03) == 0x03; + int prgRegs[4] = { InvertPrgBits(_prgRegs[0], invertBits), InvertPrgBits(_prgRegs[1], invertBits), + InvertPrgBits(_prgRegs[2], invertBits), InvertPrgBits(_prgRegs[3], invertBits) }; + + int lastPage = (_prgMode & 0x04) ? prgRegs[3] : -1; + + switch(_prgMode & 0x03) { + case 0: + SelectPrgPage4x(0, lastPage * 4); + if(_enablePrgAt6000) { + SetCpuMemoryMapping(0x6000, 0x7FFF, prgRegs[3] * 4 + 3, PrgMemoryType::PrgRom); + } + break; + + case 1: + SelectPrgPage2x(0, prgRegs[1] << 1); + SelectPrgPage2x(1, lastPage * 2); + if(_enablePrgAt6000) { + SetCpuMemoryMapping(0x6000, 0x7FFF, prgRegs[3] * 2 + 1, PrgMemoryType::PrgRom); + } + break; + + case 2: + case 3: + SelectPRGPage(0, prgRegs[0]); + SelectPRGPage(1, prgRegs[1]); + SelectPRGPage(2, prgRegs[2]); + SelectPRGPage(3, lastPage); + if(_enablePrgAt6000) { + SetCpuMemoryMapping(0x6000, 0x7FFF, prgRegs[3], PrgMemoryType::PrgRom); + } + break; + } + + if(!_enablePrgAt6000) { + RemoveCpuMemoryMapping(0x6000, 0x7FFF); + } + } + + uint16_t GetChrReg(int index) + { + if(_chrMode >= 2 && _mirrorChr && (index == 2 || index == 3)) { + index -= 2; + } + + if(_chrBlockMode) { + return _chrLowRegs[index] | (_chrBlock << 8); + } else { + return _chrLowRegs[index] | (_chrHighRegs[index] << 8); + } + } + + void UpdateChrState() + { + int chrRegs[8] = { GetChrReg(0), GetChrReg(1), GetChrReg(2), GetChrReg(3), GetChrReg(4), GetChrReg(5), GetChrReg(6), GetChrReg(7) }; + + switch(_chrMode) { + case 0: + SelectChrPage8x(0, chrRegs[0] << 3); + break; + + case 1: + SelectChrPage4x(0, chrRegs[_chrLatch[0]] << 2); + SelectChrPage4x(1, chrRegs[_chrLatch[1]] << 2); + break; + + case 2: + SelectChrPage2x(0, chrRegs[0] << 1); + SelectChrPage2x(1, chrRegs[2] << 1); + SelectChrPage2x(2, chrRegs[4] << 1); + SelectChrPage2x(3, chrRegs[6] << 1); + break; + + case 3: + for(int i = 0; i < 8; i++) { + SelectCHRPage(i, chrRegs[i]); + } + break; + } + } + + void UpdateMirroringState() + { + //"Mapper 211 behaves as though N were always set (1), and mapper 090 behaves as though N were always clear(0)." + if((_advancedNtControl || _mapperID == 211) && _mapperID != 90) { + for(int i = 0; i < 4; i++) { + if(_disableNtRam || (_ntLowRegs[i] & 0x80) != (_ntRamSelectBit & 0x80)) { + SetPpuMemoryMapping(0x2000 + 0x400 * i, 0x23FF + 0x400 * i, _ntLowRegs[i] | (_ntHighRegs[i] << 8), ChrMemoryType::ChrRom); + } else { + SetNametable(i, _ntLowRegs[i] & 0x01); + } + } + } else { + switch(_mirroringReg) { + case 0: SetMirroringType(MirroringType::Vertical); break; + case 1: SetMirroringType(MirroringType::Horizontal); break; + case 2: SetMirroringType(MirroringType::ScreenAOnly); break; + case 3: SetMirroringType(MirroringType::ScreenBOnly); break; + } + } + } + + uint8_t ReadRegister(uint16_t addr) + { + switch(addr & 0xF803) { + case 0x5000: return 0; //Dip switches + case 0x5800: return (_multiplyValue1 * _multiplyValue2) & 0xFF; + case 0x5801: return ((_multiplyValue1 * _multiplyValue2) >> 8) & 0xFF; + case 0x5803: return _regRamValue; + } + + return MemoryManager::GetOpenBus(); + } + + void WriteRegister(uint16_t addr, uint8_t value) + { + if(addr < 0x8000) { + switch(addr & 0xF803) { + case 0x5800: _multiplyValue1 = value; break; + case 0x5801: _multiplyValue2 = value; break; + case 0x5803: _regRamValue = value; break; + } + } else { + switch(addr & 0xF007) { + case 0x8000: case 0x8001: case 0x8002: case 0x8003: + case 0x8004: case 0x8005: case 0x8006: case 0x8007: + _prgRegs[addr & 0x03] = value & 0x7F; + break; + + case 0x9000: case 0x9001: case 0x9002: case 0x9003: + case 0x9004: case 0x9005: case 0x9006: case 0x9007: + _chrLowRegs[addr & 0x07] = value; + break; + + case 0xA000: case 0xA001: case 0xA002: case 0xA003: + case 0xA004: case 0xA005: case 0xA006: case 0xA007: + _chrHighRegs[addr & 0x07] = value; + break; + + case 0xB000: case 0xB001: case 0xB002: case 0xB003: + _ntLowRegs[addr & 0x03] = value; + break; + + case 0xB004: case 0xB005: case 0xB006: case 0xB007: + _ntHighRegs[addr & 0x03] = value; + break; + + case 0xC000: + if(value & 0x01) { + _irqEnabled = true; + } else { + _irqEnabled = false; + CPU::ClearIRQSource(IRQSource::External); + } + break; + + case 0xC001: + _irqCountDirection = (value >> 6) & 0x03; + _irqFunkyMode = (value & 0x08) == 0x08; + _irqSmallPrescaler = ((value >> 2) & 0x01) == 0x01; + _irqSource = (JyIrqSource)(value & 0x03); + break; + + case 0xC002: + _irqEnabled = false; + CPU::ClearIRQSource(IRQSource::External); + break; + + case 0xC003: _irqEnabled = true; break; + case 0xC004: _irqPrescaler = value ^ _irqXorReg; break; + case 0xC005: _irqCounter = value ^ _irqXorReg; break; + case 0xC006: _irqXorReg = value; break; + case 0xC007: _irqFunkyModeReg = value; break; + + case 0xD000: + _prgMode = value & 0x07; + _chrMode = (value >> 3) & 0x03; + _advancedNtControl = (value & 0x20) == 0x20; + _disableNtRam = (value & 0x40) == 0x40; + _enablePrgAt6000 = (value & 0x80) == 0x80; + break; + + case 0xD001: _mirroringReg = value & 0x03; break; + case 0xD002: _ntRamSelectBit = value & 0x80; break; + + case 0xD003: + _mirrorChr = (value & 0x80) == 0x80; + _chrBlockMode = (value & 0x20) == 0x00; + _chrBlock = value & 0x1F; + break; + + } + } + + UpdateState(); + } + + void ProcessCpuClock() + { + if(_irqSource == JyIrqSource::CpuClock || (_irqSource == JyIrqSource::CpuWrite && CPU::IsCpuWrite())) { + TickIrqCounter(); + } + } + + uint8_t ReadVRAM(uint16_t addr, MemoryOperationType type) + { + if(_irqSource == JyIrqSource::PpuRead && type == MemoryOperationType::PpuRenderingRead) { + TickIrqCounter(); + } + return BaseMapper::ReadVRAM(addr, type); + } + + void NotifyVRAMAddressChange(uint16_t addr) + { + if(_irqSource == JyIrqSource::PpuA12Rise && (addr & 0x1000) && !(_lastPpuAddr & 0x1000)) { + TickIrqCounter(); + } + _lastPpuAddr = addr; + + if(_mapperID == 209) { + switch(addr & 0x2FF8) { + case 0x0FD8: + case 0x0FE8: + _chrLatch[addr >> 12] = addr >> 4 & ((addr >> 10 & 0x04) | 0x02); + UpdateChrState(); + break; + } + } + } + + void TickIrqCounter() + { + bool clockIrqCounter = false; + uint8_t mask = _irqSmallPrescaler ? 0x07 : 0xFF; + uint8_t prescaler = _irqPrescaler & mask; + if(_irqCountDirection == 0x01) { + prescaler++; + if((prescaler & mask) == 0) { + clockIrqCounter = true; + } + } else if(_irqCountDirection == 0x02) { + if(--prescaler == 0) { + clockIrqCounter = true; + } + } + _irqPrescaler = (_irqPrescaler & ~mask) | (prescaler & mask); + + if(clockIrqCounter) { + if(_irqCountDirection == 0x01) { + _irqCounter++; + if(_irqCounter == 0 && _irqEnabled) { + CPU::SetIRQSource(IRQSource::External); + } + } else if(_irqCountDirection == 0x02) { + _irqCounter--; + if(_irqCounter == 0xFF && _irqEnabled) { + CPU::SetIRQSource(IRQSource::External); + } + } + } + } +}; \ No newline at end of file diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp index 59706ff4..2ae1c130 100644 --- a/Core/MapperFactory.cpp +++ b/Core/MapperFactory.cpp @@ -34,6 +34,7 @@ #include "JalecoJf17_19.h" #include "JalecoJfxx.h" #include "JalecoSs88006.h" +#include "JyCompany.h" #include "Kaiser202.h" #include "Kaiser7022.h" #include "Kaiser7058.h" @@ -135,14 +136,17 @@ #include "Waixing252.h" /* -Supported mappers: (... denotes bad mappers, --- denotes potentially bad mappers) +Supported mappers: +... : bad mappers +--- : potentially bad mappers +=== : not supported by Nestopia & FCEUX ----------------------------------------------------------------- | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| | 15| | 16| 17| 18| 19|...| 21| 22| 23| 24| 25| 26| 27| 28| | | 31| | 32| 33| 34| | 36| 37| 38|---| 40| 41| 42|---| 44| 45| 46| 47| -| 48| 49| 50| 51| 52| | | | 56| 57| 58| | 60| 61| 62| 63| +| 48| 49| 50| 51| 52| | | | 56| 57| 58|===| 60| 61| 62| 63| | 64| 65| 66| 67| 68| 69| 70| 71| 72| 73| 74| 75| 76| 77| 78| 79| -| 80| | 82| | | 85| 86| 87| 88| 89| | 91| 92| 93| 94| 95| +| 80|===| 82| |===| 85| 86| 87| 88| 89| 90| 91| 92| 93| 94| 95| | | 97| | 99|...|101| | | | | |107| | | | | |112|113| |115| | |118|119| | | | | | | | | | | | | |132|133| | | |137|138|139|140|141|142|143| @@ -150,7 +154,7 @@ Supported mappers: (... denotes bad mappers, --- denotes potentially bad mapper | | | |163|164| | | | | |170|171|172|173| |175| |176|177| |179|180| |182| |184|185| | | |189| |191| |192|193|194|195| | | | |200|201|202|203| |205|206|207| -| | |210| | | | | | | |218| | | | | | +| |209|210|211| | | | | | |218| | | | | | | |225|226|227|228| |230|231|232| | |235| | | | | |240|241|242|243| | |246| | | | | |252| | | | ----------------------------------------------------------------- @@ -247,6 +251,7 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData) case 87: return new JalecoJfxx(false); case 88: return new Namco108_88(); case 89: return new Sunsoft89(); + case 90: return new JyCompany(); case 91: return new Mapper91(); case 92: return new JalecoJf17_19(true); case 93: return new Sunsoft93(); @@ -314,7 +319,9 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData) case 205: return new MMC3_205(); case 206: return new Namco108(); case 207: return new TaitoX1005(true); + case 209: return new JyCompany(); case 210: return new Namco163(); + case 211: return new JyCompany(); case 218: return new Mapper218(); case 225: return new Mapper225(); case 226: return new Mapper226(); diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 70df550e..f53aa72b 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -670,6 +670,9 @@ void PPU::ProcessPreVBlankScanline() if(_cycle == 257) { _spriteIndex = 0; } + } else if((_cycle - 259) % 8 == 0) { + //Garbage AT sprite fetch + _memoryManager->ReadVRAM(GetAttributeAddr()); } } } else if(_cycle == 321 && IsRenderingEnabled()) { @@ -709,6 +712,10 @@ void PPU::ProcessPrerenderScanline() _scanline = 0; } else if(_cycle >= 321 && _cycle <= 336) { LoadTileInfo(); + } else if(_cycle == 337 || _cycle == 339) { + if(IsRenderingEnabled()) { + _memoryManager->ReadVRAM(GetNameTableAddr()); + } } } @@ -725,6 +732,10 @@ void PPU::ProcessVisibleScanline() } } else if(_cycle >= 321 && _cycle <= 336) { LoadTileInfo(); + } else if(_cycle == 337 || _cycle == 339) { + if(IsRenderingEnabled()) { + _memoryManager->ReadVRAM(GetNameTableAddr()); + } } ProcessPreVBlankScanline();