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();