From 933fa3e8bade178fc0cb8f8e69bc0f07e255fea1 Mon Sep 17 00:00:00 2001 From: Sour Date: Fri, 20 Dec 2019 22:59:09 -0500 Subject: [PATCH] Mappers: Fixed/improved emulation of multiple unlicensed mappers Based on NRS' research --- Core/Core.vcxproj | 2 +- Core/Core.vcxproj.filters | 6 +-- Core/MapperFactory.cpp | 3 +- Core/Sachen74LS374N.h | 51 ++++++++++++++------ Core/Sachen74LS374NB.h | 76 ------------------------------ Core/Sachen_136.h | 42 +++++++++++------ Core/Sachen_147.h | 48 ++++++++++++++++--- Core/Txc22000.h | 57 ++++++++++------------- Core/Txc22211A.h | 44 +++++++++--------- Core/Txc22211B.h | 59 ++++++++++++++++++++++-- Core/Txc22211C.h | 13 +++++- Core/TxcChip.h | 97 +++++++++++++++++++++++++++++++++++++++ 12 files changed, 318 insertions(+), 180 deletions(-) delete mode 100644 Core/Sachen74LS374NB.h create mode 100644 Core/TxcChip.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index ee755795..366ae5b7 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -649,6 +649,7 @@ + @@ -804,7 +805,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index dd103cf6..d5c2da20 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -667,9 +667,6 @@ Nes\Mappers\Sachen - - Nes\Mappers\Sachen - Nes\Mappers\Unnamed @@ -1490,6 +1487,9 @@ Debugger + + Nes\Mappers\Txc + diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp index 3c700f40..72e9fcbe 100644 --- a/Core/MapperFactory.cpp +++ b/Core/MapperFactory.cpp @@ -220,7 +220,6 @@ #include "Sachen_148.h" #include "Sachen_149.h" #include "Sachen74LS374N.h" -#include "Sachen74LS374NB.h" #include "Sachen8259.h" #include "Sachen9602.h" #include "SealieComputing.h" @@ -441,7 +440,7 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData) case 147: return new Sachen_147(); case 148: return new Sachen_148(); case 149: return new Sachen_149(); - case 150: return new Sachen74LS374NB(); + case 150: return new Sachen74LS374N(); case 151: return new VRC1(); case 152: return new Bandai74161_7432(true); case 153: return new BandaiFcg(); diff --git a/Core/Sachen74LS374N.h b/Core/Sachen74LS374N.h index 519ef79e..aaad35a8 100644 --- a/Core/Sachen74LS374N.h +++ b/Core/Sachen74LS374N.h @@ -9,16 +9,18 @@ private: uint8_t _regs[8]; protected: + uint32_t GetDipSwitchCount() override { return _romInfo.MapperID == 150 ? 1 : 0; } uint16_t RegisterStartAddress() override { return 0x4100; } uint16_t RegisterEndAddress() override { return 0x7FFF; } uint16_t GetPRGPageSize() override { return 0x8000; } uint16_t GetCHRPageSize() override { return 0x2000; } + bool AllowRegisterRead() override { return true; } void InitMapper() override { _currentRegister = 0; memset(_regs, 0, sizeof(_regs)); - SelectPRGPage(0, 0); + UpdateState(); } void StreamState(bool saving) override @@ -29,28 +31,49 @@ protected: Stream(_currentRegister, regs); } - virtual void UpdateState() + void UpdateState() { - uint8_t chrPage = ((_regs[2] & 0x01) << 3) | ((_regs[6] & 0x03) << 1) | (_regs[4] & 0x01); + uint8_t chrPage; + if(_romInfo.MapperID == 150) { + chrPage = ((_regs[4] & 0x01) << 2) | (_regs[6] & 0x03); + } else { + chrPage = (_regs[2] & 0x01) | ((_regs[4] & 0x01) << 1) | ((_regs[6] & 0x03) << 2); + } SelectCHRPage(0, chrPage); - SelectPRGPage(0, _regs[5] & 0x01); + SelectPRGPage(0, _regs[5] & 0x03); - SetMirroringType(_regs[7] & 0x01 ? MirroringType::Vertical : MirroringType::Horizontal); + switch((_regs[7] >> 1) & 0x03) { + case 0: SetNametables(0, 0, 0, 1); break; + case 1: SetMirroringType(MirroringType::Horizontal); break; + case 2: SetMirroringType(MirroringType::Vertical); break; + case 3: SetMirroringType(MirroringType::ScreenAOnly); break; + } + } + + uint8_t ReadRegister(uint16_t addr) override + { + uint8_t openBus = _console->GetMemoryManager()->GetOpenBus(); + if((addr & 0xC101) == 0x4101) { + if(GetDipSwitches() & 0x01) { + //"In the latter setting, the ASIC sees all writes as being OR'd with $04, while on reads, D2 is open bus." + return (openBus & 0xFC) | (_regs[_currentRegister] & 0x03); + } else { + return (openBus & 0xF8) | (_regs[_currentRegister] & 0x07); + } + } + return openBus; } void WriteRegister(uint16_t addr, uint8_t value) override { + if(GetDipSwitches() & 0x01) { + //"In the latter setting, the ASIC sees all writes as being OR'd with $04, while on reads, D2 is open bus." + value |= 0x04; + } + switch(addr & 0xC101) { case 0x4100: _currentRegister = value & 0x07; break; - case 0x4101: - _regs[_currentRegister] = value; - if(_currentRegister == 0) { - SelectCHRPage(0, 3); - SelectPRGPage(0, 0); - } else { - UpdateState(); - } - break; + case 0x4101: _regs[_currentRegister] = (value & 0x07); UpdateState(); break; } } }; diff --git a/Core/Sachen74LS374NB.h b/Core/Sachen74LS374NB.h deleted file mode 100644 index 98400b05..00000000 --- a/Core/Sachen74LS374NB.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include "stdafx.h" -#include "Sachen74LS374N.h" - -class Sachen74LS374NB : public BaseMapper -{ -private: - uint8_t _counter; - uint8_t _currentRegister; - uint8_t _regs[8]; - -protected: - uint16_t RegisterStartAddress() override { return 0x4100; } - uint16_t RegisterEndAddress() override { return 0x7FFF; } - uint16_t GetPRGPageSize() override { return 0x8000; } - uint16_t GetCHRPageSize() override { return 0x2000; } - bool AllowRegisterRead() override { return true; } - - void InitMapper() override - { - _counter = 0; - _currentRegister = 0; - memset(_regs, 0, sizeof(_regs)); - - SelectPRGPage(0, 0); - } - - void StreamState(bool saving) override - { - BaseMapper::StreamState(saving); - ArrayInfo regs{ _regs, 8 }; - Stream(_currentRegister, regs, _counter); - } - - void Reset(bool softReset) override - { - if(softReset) { - _counter++; - } - } - - uint8_t ReadRegister(uint16_t addr) override - { - switch(addr & 0xC101) { - case 0x4000: return (~_currentRegister) ^ (_counter & 1); - } - - return 0; - } - - void UpdateState() - { - uint8_t chrPage = ((_regs[2] & 0x01) << 3) | ((_regs[4] & 0x01) << 2) | (_regs[6] & 0x03); - SelectCHRPage(0, chrPage); - if(_currentRegister == 2) { - SelectPRGPage(0, _regs[2] & 0x01); - } else { - SelectPRGPage(0, _regs[5] & 0x07); - } - - switch((_regs[7] >> 1) & 0x02) { - case 0: SetMirroringType(MirroringType::Horizontal); break; - case 1: SetMirroringType(MirroringType::Vertical); break; - case 2: SetNametables(0, 1, 1, 1); break; - case 3: SetMirroringType(MirroringType::ScreenAOnly); break; - } - } - - void WriteRegister(uint16_t addr, uint8_t value) override - { - switch(addr & 0xC101) { - case 0x4100: _currentRegister = value & 0x07; break; - case 0x4101: _regs[_currentRegister] = value; UpdateState(); break; - } - } -}; diff --git a/Core/Sachen_136.h b/Core/Sachen_136.h index b5712f50..9a107189 100644 --- a/Core/Sachen_136.h +++ b/Core/Sachen_136.h @@ -1,44 +1,56 @@ #pragma once #include "stdafx.h" #include "BaseMapper.h" +#include "TxcChip.h" class Sachen_136 : public BaseMapper { private: - uint8_t _chrReg; + TxcChip _txc = TxcChip(true); protected: - virtual uint16_t GetPRGPageSize() override { return 0x8000; } - virtual uint16_t GetCHRPageSize() override { return 0x2000; } - virtual uint16_t RegisterStartAddress() override { return 0x4100; } - virtual uint16_t RegisterEndAddress() override { return 0xFFFF; } - virtual bool AllowRegisterRead() override { return true; } + uint16_t GetPRGPageSize() override { return 0x8000; } + uint16_t GetCHRPageSize() override { return 0x2000; } + uint16_t RegisterStartAddress() override { return 0x8000; } + uint16_t RegisterEndAddress() override { return 0xFFFF; } + bool AllowRegisterRead() override { return true; } void InitMapper() override { - _chrReg = 0; + AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any); + RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); + SelectPRGPage(0, 0); SelectCHRPage(0, 0); - - RemoveRegisterRange(0x4101, 0xFFFF, MemoryOperation::Read); } void StreamState(bool saving) override { BaseMapper::StreamState(saving); - Stream(_chrReg); + Stream(&_txc); + } + + void UpdateState() + { + SelectCHRPage(0, _txc.GetOutput()); } uint8_t ReadRegister(uint16_t addr) override { - return (_chrReg & 0x3F) | (_console->GetMemoryManager()->GetOpenBus() & 0xC0); + uint8_t openBus = _console->GetMemoryManager()->GetOpenBus(); + uint8_t value; + if((addr & 0x103) == 0x100) { + value = (openBus & 0xC0) | (_txc.Read() & 0x3F); + } else { + value = openBus; + } + UpdateState(); + return value; } void WriteRegister(uint16_t addr, uint8_t value) override { - if((addr & 0x0103) == 0x0102) { - _chrReg = value + 3; - SelectCHRPage(0, _chrReg & 0x03); - } + _txc.Write(addr, value & 0x3F); + UpdateState(); } }; \ No newline at end of file diff --git a/Core/Sachen_147.h b/Core/Sachen_147.h index da8df13c..1b28b6b8 100644 --- a/Core/Sachen_147.h +++ b/Core/Sachen_147.h @@ -1,25 +1,59 @@ #pragma once #include "stdafx.h" #include "BaseMapper.h" +#include "TxcChip.h" class Sachen_147 : public BaseMapper { +private: + TxcChip _txc = TxcChip(true); + protected: - virtual uint16_t GetPRGPageSize() override { return 0x8000; } - virtual uint16_t GetCHRPageSize() override { return 0x2000; } - virtual uint16_t RegisterStartAddress() override { return 0x4100; } - virtual uint16_t RegisterEndAddress() override { return 0x7FFF; } + uint16_t GetPRGPageSize() override { return 0x8000; } + uint16_t GetCHRPageSize() override { return 0x2000; } + uint16_t RegisterStartAddress() override { return 0x8000; } + uint16_t RegisterEndAddress() override { return 0xFFFF; } + bool AllowRegisterRead() override { return true; } void InitMapper() override { + AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any); + RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); + SelectPRGPage(0, 0); + SelectCHRPage(0, 0); + } + + void StreamState(bool saving) override + { + BaseMapper::StreamState(saving); + Stream(&_txc); + } + + void UpdateState() + { + uint8_t out = _txc.GetOutput(); + SelectPRGPage(0, ((out & 0x20) >> 4) | (out & 0x01)); + SelectCHRPage(0, (out & 0x1E) >> 1); + } + + uint8_t ReadRegister(uint16_t addr) override + { + uint8_t openBus = _console->GetMemoryManager()->GetOpenBus(); + uint8_t value = openBus; + if((addr & 0x103) == 0x100) { + uint8_t v = _txc.Read(); + value = ((v & 0x3F) << 2) | ((v & 0xC0) >> 6); + } + UpdateState(); + return value; } void WriteRegister(uint16_t addr, uint8_t value) override { - if((addr & 0x4103) == 0x4102) { - SelectPRGPage(0, ((value >> 2) & 0x01) | ((value >> 6) & 0x02)); - SelectCHRPage(0, (value >> 3) & 0x0F); + _txc.Write(addr, ((value & 0xFC) >> 2) | ((value & 0x03) << 6)); + if(addr >= 0x8000) { + UpdateState(); } } }; \ No newline at end of file diff --git a/Core/Txc22000.h b/Core/Txc22000.h index c06a17ce..3b84460d 100644 --- a/Core/Txc22000.h +++ b/Core/Txc22000.h @@ -2,13 +2,13 @@ #include "stdafx.h" #include "BaseMapper.h" #include "MemoryManager.h" +#include "TxcChip.h" class Txc22000 : public BaseMapper { private: - uint8_t _state; - bool _prgBankingMode; - uint8_t _prgBank; + TxcChip _txc = TxcChip(false); + uint8_t _chrBank; protected: virtual uint16_t GetPRGPageSize() override { return 0x8000; } @@ -22,10 +22,7 @@ protected: AddRegisterRange(0x4100, 0x5FFF, MemoryOperation::Any); RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); - _state = 0; - _prgBank = 0; - _prgBankingMode = 0; - + _chrBank = 0; SelectPRGPage(0, 0); SelectCHRPage(0, 0); } @@ -33,39 +30,33 @@ protected: void StreamState(bool saving) override { BaseMapper::StreamState(saving); - Stream(_state, _prgBank, _prgBankingMode); + Stream(&_txc); + Stream(_chrBank); } - virtual uint8_t ReadRegister(uint16_t addr) override + void UpdateState() { - return (_console->GetMemoryManager()->GetOpenBus() & 0xCF) | (_state << 4); + SelectPRGPage(0, _txc.GetOutput() & 0x03); + SelectCHRPage(0, _chrBank); + } + + uint8_t ReadRegister(uint16_t addr) override + { + uint8_t openBus = _console->GetMemoryManager()->GetOpenBus(); + uint8_t value = openBus; + if((addr & 0x103) == 0x100) { + value = (openBus & 0xCF) | ((_txc.Read() << 4) & 0x30); + } + UpdateState(); + return value; } void WriteRegister(uint16_t addr, uint8_t value) override { - if(addr < 0x8000) { - switch(addr & 0xE303) { - //"when M=0, copy PP to RR. When M=1, RR=RR+1" - case 0x4100: - if(_prgBankingMode) { - _state++; - } else { - _state = _prgBank; - } - break; - - case 0x4101: break; //"$4101: no visible effect" - - case 0x4102: _prgBank = (value >> 4) & 0x03; break; - case 0x4103: _prgBankingMode = (value >> 4) & 0x01; break; - - case 0x4200: case 0x4201: case 0x4202: case 0x4203: - SelectCHRPage(0, value & 0x0F); - break; - } - } else { - SelectPRGPage(0, _state); + if((addr & 0xF200) == 0x4200) { + _chrBank = value; } - + _txc.Write(addr, (value >> 4) & 0x03); + UpdateState(); } }; \ No newline at end of file diff --git a/Core/Txc22211A.h b/Core/Txc22211A.h index 54bf96d7..d1b68a60 100644 --- a/Core/Txc22211A.h +++ b/Core/Txc22211A.h @@ -5,49 +5,49 @@ class Txc22211A : public BaseMapper { protected: - virtual uint16_t GetPRGPageSize() override { return 0x8000; } - virtual uint16_t GetCHRPageSize() override { return 0x2000; } - virtual uint16_t RegisterStartAddress() override { return 0x8000; } - virtual uint16_t RegisterEndAddress() override { return 0xFFFF; } - virtual bool AllowRegisterRead() override { return true; } + TxcChip _txc = TxcChip(false); - uint8_t _regs[4]; + uint16_t GetPRGPageSize() override { return 0x8000; } + uint16_t GetCHRPageSize() override { return 0x2000; } + uint16_t RegisterStartAddress() override { return 0x8000; } + uint16_t RegisterEndAddress() override { return 0xFFFF; } + bool AllowRegisterRead() override { return true; } void InitMapper() override { - AddRegisterRange(0x4100, 0x4103, MemoryOperation::Any); + AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any); RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); - memset(_regs, 0, sizeof(_regs)); - SelectPRGPage(0, 0); SelectCHRPage(0, 0); } - + void StreamState(bool saving) override { BaseMapper::StreamState(saving); - Stream(_regs[0], _regs[1], _regs[2], _regs[3]); + Stream(&_txc); } - virtual uint8_t ReadRegister(uint16_t addr) override + virtual void UpdateState() { - return (_regs[1] ^ _regs[2]) | 0x40; + SelectPRGPage(0, (_txc.GetOutput() >> 2) & 0x01); + SelectCHRPage(0, _txc.GetOutput() & 0x03); } - virtual void UpdateState(uint8_t value) + uint8_t ReadRegister(uint16_t addr) override { - SelectPRGPage(0, _regs[2] >> 2); - SelectCHRPage(0, _regs[2]); + uint8_t openBus = _console->GetMemoryManager()->GetOpenBus(); + uint8_t value = openBus; + if((addr & 0x103) == 0x100) { + value = (openBus & 0xF0) | (_txc.Read() & 0x0F); + } + UpdateState(); + return value; } void WriteRegister(uint16_t addr, uint8_t value) override { - if(addr < 0x8000) { - _regs[addr & 0x03] = value; - } else { - UpdateState(value); - } - + _txc.Write(addr, value & 0x0F); + UpdateState(); } }; \ No newline at end of file diff --git a/Core/Txc22211B.h b/Core/Txc22211B.h index 5f018908..0b49b8e4 100644 --- a/Core/Txc22211B.h +++ b/Core/Txc22211B.h @@ -1,13 +1,62 @@ #pragma once #include "stdafx.h" -#include "Txc22211A.h" +#include "BaseMapper.h" +#include "TxcChip.h" -class Txc22211B : public Txc22211A +class Txc22211B : public BaseMapper { +private: + TxcChip _txc = TxcChip(true); + protected: - virtual void UpdateState(uint8_t value) + uint16_t GetPRGPageSize() override { return 0x8000; } + uint16_t GetCHRPageSize() override { return 0x2000; } + uint16_t RegisterStartAddress() override { return 0x8000; } + uint16_t RegisterEndAddress() override { return 0xFFFF; } + bool AllowRegisterRead() override { return true; } + + void InitMapper() override { - SelectPRGPage(0, _regs[2] >> 2); - SelectCHRPage(0, (((value ^ _regs[2]) >> 3) & 0x02) | (((value ^ _regs[2]) >> 5) & 0x01)); + AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any); + RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); + + SelectPRGPage(0, 0); + SelectCHRPage(0, 0); + } + + void StreamState(bool saving) override + { + BaseMapper::StreamState(saving); + Stream(&_txc); + } + + void UpdateState() + { + SelectCHRPage(0, _txc.GetOutput()); + SetMirroringType(_txc.GetInvertFlag() ? MirroringType::Vertical : MirroringType::Horizontal); + } + + uint8_t ConvertValue(uint8_t v) + { + return ((v & 0x01) << 5) | ((v & 0x02) << 3) | ((v & 0x04) << 1) | ((v & 0x08) >> 1) | ((v & 0x10) >> 3) | ((v & 0x20) >> 5); + } + + uint8_t ReadRegister(uint16_t addr) override + { + uint8_t openBus = _console->GetMemoryManager()->GetOpenBus(); + uint8_t value = openBus; + if((addr & 0x103) == 0x100) { + value = (openBus & 0xC0) | ConvertValue(_txc.Read()); + } + UpdateState(); + return value; + } + + void WriteRegister(uint16_t addr, uint8_t value) override + { + _txc.Write(addr, ConvertValue(value)); + if(addr >= 0x8000) { + UpdateState(); + } } }; \ No newline at end of file diff --git a/Core/Txc22211C.h b/Core/Txc22211C.h index d09bddbc..523e7577 100644 --- a/Core/Txc22211C.h +++ b/Core/Txc22211C.h @@ -5,8 +5,17 @@ class Txc22211C : public Txc22211A { protected: - virtual uint8_t ReadRegister(uint16_t addr) override + void UpdateState() override { - return (_regs[1] ^ _regs[2]) | 0x41; + SelectPRGPage(0, 0); + if(_chrRomSize > 0x2000) { + SelectCHRPage(0, (_txc.GetOutput() & 0x01) | (_txc.GetY() ? 0x02 : 0) | ((_txc.GetOutput() & 0x02) << 1)); + } else { + if(_txc.GetY()){ + SelectCHRPage(0, 0); + } else { + RemovePpuMemoryMapping(0, 0x1FFF); + } + } } }; \ No newline at end of file diff --git a/Core/TxcChip.h b/Core/TxcChip.h new file mode 100644 index 00000000..7cbdba69 --- /dev/null +++ b/Core/TxcChip.h @@ -0,0 +1,97 @@ +#pragma once +#include "stdafx.h" +#include "Snapshotable.h" + +class TxcChip : public Snapshotable +{ +private: + uint8_t _accumulator; + uint8_t _inverter; + uint8_t _staging; + uint8_t _output; + bool _increase; + bool _yFlag; + bool _invert; + + uint8_t _mask; + bool _isJv001; + +public: + TxcChip(bool isJv001) + { + _accumulator = 0; + _inverter = 0; + _staging = 0; + _output = 0; + + _increase = false; + _yFlag = false; + + _isJv001 = isJv001; + _mask = isJv001 ? 0x0F : 0x07; + _invert = isJv001; + } + + void StreamState(bool saving) + { + Stream(_accumulator, _invert, _inverter, _staging, _output, _increase, _yFlag); + } + + bool GetInvertFlag() + { + return _invert; + } + + bool GetY() + { + return _yFlag; + } + + uint8_t GetOutput() + { + return _output; + } + + uint8_t Read() + { + uint8_t value = (_accumulator & _mask) | ((_inverter ^ (_invert ? 0xFF : 0)) & ~_mask); + _yFlag = !_invert || ((value & 0x10) != 0); + return value; + } + + void Write(uint16_t addr, uint8_t value) + { + if(addr < 0x8000) { + switch(addr & 0xE103) { + case 0x4100: + if(_increase) { + _accumulator++; + } else { + _accumulator = ((_accumulator & ~_mask) | (_staging & _mask)) ^ (_invert ? 0xFF : 0); + } + break; + + case 0x4101: + _invert = (value & 0x01) != 0; + break; + + case 0x4102: + _staging = value & _mask; + _inverter = value & ~_mask; + break; + + case 0x4103: + _increase = (value & 0x01) != 0; + break; + } + } else { + if(_isJv001) { + _output = (_accumulator & 0x0F) | (_inverter & 0xF0); + } else { + _output = (_accumulator & 0x0F) | ((_inverter & 0x08) << 1); + } + } + + _yFlag = !_invert || ((value & 0x10) != 0); + } +}; \ No newline at end of file