Mappers: Fixed/improved emulation of multiple unlicensed mappers

Based on NRS' research
This commit is contained in:
Sour 2019-12-20 22:59:09 -05:00
parent 11135ec485
commit 933fa3e8ba
12 changed files with 318 additions and 180 deletions

View file

@ -649,6 +649,7 @@
<ClInclude Include="SuborKeyboard.h" />
<ClInclude Include="SuborMouse.h" />
<ClInclude Include="T230.h" />
<ClInclude Include="TxcChip.h" />
<ClInclude Include="Unl158B.h" />
<ClInclude Include="Unl8237A.h" />
<ClInclude Include="UnlD1038.h" />
@ -804,7 +805,6 @@
<ClInclude Include="Rambo1.h" />
<ClInclude Include="Rt01.h" />
<ClInclude Include="Sachen74LS374N.h" />
<ClInclude Include="Sachen74LS374NB.h" />
<ClInclude Include="Sachen8259.h" />
<ClInclude Include="Sachen_133.h" />
<ClInclude Include="Sachen_136.h" />

View file

@ -667,9 +667,6 @@
<ClInclude Include="Sachen74LS374N.h">
<Filter>Nes\Mappers\Sachen</Filter>
</ClInclude>
<ClInclude Include="Sachen74LS374NB.h">
<Filter>Nes\Mappers\Sachen</Filter>
</ClInclude>
<ClInclude Include="Mapper227.h">
<Filter>Nes\Mappers\Unnamed</Filter>
</ClInclude>
@ -1490,6 +1487,9 @@
<ClInclude Include="EventManager.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="TxcChip.h">
<Filter>Nes\Mappers\Txc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

View file

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

View file

@ -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;
}
}
};

View file

@ -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<uint8_t> 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;
}
}
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}
}
}
};

97
Core/TxcChip.h Normal file
View file

@ -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);
}
};