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

View file

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

View file

@ -220,7 +220,6 @@
#include "Sachen_148.h" #include "Sachen_148.h"
#include "Sachen_149.h" #include "Sachen_149.h"
#include "Sachen74LS374N.h" #include "Sachen74LS374N.h"
#include "Sachen74LS374NB.h"
#include "Sachen8259.h" #include "Sachen8259.h"
#include "Sachen9602.h" #include "Sachen9602.h"
#include "SealieComputing.h" #include "SealieComputing.h"
@ -441,7 +440,7 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData)
case 147: return new Sachen_147(); case 147: return new Sachen_147();
case 148: return new Sachen_148(); case 148: return new Sachen_148();
case 149: return new Sachen_149(); case 149: return new Sachen_149();
case 150: return new Sachen74LS374NB(); case 150: return new Sachen74LS374N();
case 151: return new VRC1(); case 151: return new VRC1();
case 152: return new Bandai74161_7432(true); case 152: return new Bandai74161_7432(true);
case 153: return new BandaiFcg(); case 153: return new BandaiFcg();

View file

@ -9,16 +9,18 @@ private:
uint8_t _regs[8]; uint8_t _regs[8];
protected: protected:
uint32_t GetDipSwitchCount() override { return _romInfo.MapperID == 150 ? 1 : 0; }
uint16_t RegisterStartAddress() override { return 0x4100; } uint16_t RegisterStartAddress() override { return 0x4100; }
uint16_t RegisterEndAddress() override { return 0x7FFF; } uint16_t RegisterEndAddress() override { return 0x7FFF; }
uint16_t GetPRGPageSize() override { return 0x8000; } uint16_t GetPRGPageSize() override { return 0x8000; }
uint16_t GetCHRPageSize() override { return 0x2000; } uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override void InitMapper() override
{ {
_currentRegister = 0; _currentRegister = 0;
memset(_regs, 0, sizeof(_regs)); memset(_regs, 0, sizeof(_regs));
SelectPRGPage(0, 0); UpdateState();
} }
void StreamState(bool saving) override void StreamState(bool saving) override
@ -29,28 +31,49 @@ protected:
Stream(_currentRegister, regs); 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); 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 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) { switch(addr & 0xC101) {
case 0x4100: _currentRegister = value & 0x07; break; case 0x4100: _currentRegister = value & 0x07; break;
case 0x4101: case 0x4101: _regs[_currentRegister] = (value & 0x07); UpdateState(); break;
_regs[_currentRegister] = value;
if(_currentRegister == 0) {
SelectCHRPage(0, 3);
SelectPRGPage(0, 0);
} else {
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 #pragma once
#include "stdafx.h" #include "stdafx.h"
#include "BaseMapper.h" #include "BaseMapper.h"
#include "TxcChip.h"
class Sachen_136 : public BaseMapper class Sachen_136 : public BaseMapper
{ {
private: private:
uint8_t _chrReg; TxcChip _txc = TxcChip(true);
protected: protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; } uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; } uint16_t GetCHRPageSize() override { return 0x2000; }
virtual uint16_t RegisterStartAddress() override { return 0x4100; } uint16_t RegisterStartAddress() override { return 0x8000; }
virtual uint16_t RegisterEndAddress() override { return 0xFFFF; } uint16_t RegisterEndAddress() override { return 0xFFFF; }
virtual bool AllowRegisterRead() override { return true; } bool AllowRegisterRead() override { return true; }
void InitMapper() override void InitMapper() override
{ {
_chrReg = 0; AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
SelectPRGPage(0, 0); SelectPRGPage(0, 0);
SelectCHRPage(0, 0); SelectCHRPage(0, 0);
RemoveRegisterRange(0x4101, 0xFFFF, MemoryOperation::Read);
} }
void StreamState(bool saving) override void StreamState(bool saving) override
{ {
BaseMapper::StreamState(saving); BaseMapper::StreamState(saving);
Stream(_chrReg); Stream(&_txc);
}
void UpdateState()
{
SelectCHRPage(0, _txc.GetOutput());
} }
uint8_t ReadRegister(uint16_t addr) override 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 void WriteRegister(uint16_t addr, uint8_t value) override
{ {
if((addr & 0x0103) == 0x0102) { _txc.Write(addr, value & 0x3F);
_chrReg = value + 3; UpdateState();
SelectCHRPage(0, _chrReg & 0x03);
}
} }
}; };

View file

@ -1,25 +1,59 @@
#pragma once #pragma once
#include "stdafx.h" #include "stdafx.h"
#include "BaseMapper.h" #include "BaseMapper.h"
#include "TxcChip.h"
class Sachen_147 : public BaseMapper class Sachen_147 : public BaseMapper
{ {
private:
TxcChip _txc = TxcChip(true);
protected: protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; } uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; } uint16_t GetCHRPageSize() override { return 0x2000; }
virtual uint16_t RegisterStartAddress() override { return 0x4100; } uint16_t RegisterStartAddress() override { return 0x8000; }
virtual uint16_t RegisterEndAddress() override { return 0x7FFF; } uint16_t RegisterEndAddress() override { return 0xFFFF; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override void InitMapper() override
{ {
AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
SelectPRGPage(0, 0); 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 void WriteRegister(uint16_t addr, uint8_t value) override
{ {
if((addr & 0x4103) == 0x4102) { _txc.Write(addr, ((value & 0xFC) >> 2) | ((value & 0x03) << 6));
SelectPRGPage(0, ((value >> 2) & 0x01) | ((value >> 6) & 0x02)); if(addr >= 0x8000) {
SelectCHRPage(0, (value >> 3) & 0x0F); UpdateState();
} }
} }
}; };

View file

@ -2,13 +2,13 @@
#include "stdafx.h" #include "stdafx.h"
#include "BaseMapper.h" #include "BaseMapper.h"
#include "MemoryManager.h" #include "MemoryManager.h"
#include "TxcChip.h"
class Txc22000 : public BaseMapper class Txc22000 : public BaseMapper
{ {
private: private:
uint8_t _state; TxcChip _txc = TxcChip(false);
bool _prgBankingMode; uint8_t _chrBank;
uint8_t _prgBank;
protected: protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; } virtual uint16_t GetPRGPageSize() override { return 0x8000; }
@ -22,10 +22,7 @@ protected:
AddRegisterRange(0x4100, 0x5FFF, MemoryOperation::Any); AddRegisterRange(0x4100, 0x5FFF, MemoryOperation::Any);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
_state = 0; _chrBank = 0;
_prgBank = 0;
_prgBankingMode = 0;
SelectPRGPage(0, 0); SelectPRGPage(0, 0);
SelectCHRPage(0, 0); SelectCHRPage(0, 0);
} }
@ -33,39 +30,33 @@ protected:
void StreamState(bool saving) override void StreamState(bool saving) override
{ {
BaseMapper::StreamState(saving); 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 void WriteRegister(uint16_t addr, uint8_t value) override
{ {
if(addr < 0x8000) { if((addr & 0xF200) == 0x4200) {
switch(addr & 0xE303) { _chrBank = value;
//"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);
} }
_txc.Write(addr, (value >> 4) & 0x03);
UpdateState();
} }
}; };

View file

@ -5,49 +5,49 @@
class Txc22211A : public BaseMapper class Txc22211A : public BaseMapper
{ {
protected: protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; } TxcChip _txc = TxcChip(false);
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; }
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 void InitMapper() override
{ {
AddRegisterRange(0x4100, 0x4103, MemoryOperation::Any); AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read); RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
memset(_regs, 0, sizeof(_regs));
SelectPRGPage(0, 0); SelectPRGPage(0, 0);
SelectCHRPage(0, 0); SelectCHRPage(0, 0);
} }
void StreamState(bool saving) override void StreamState(bool saving) override
{ {
BaseMapper::StreamState(saving); 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); uint8_t openBus = _console->GetMemoryManager()->GetOpenBus();
SelectCHRPage(0, _regs[2]); 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 void WriteRegister(uint16_t addr, uint8_t value) override
{ {
if(addr < 0x8000) { _txc.Write(addr, value & 0x0F);
_regs[addr & 0x03] = value; UpdateState();
} else {
UpdateState(value);
}
} }
}; };

View file

@ -1,13 +1,62 @@
#pragma once #pragma once
#include "stdafx.h" #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: 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); AddRegisterRange(0x4020, 0x5FFF, MemoryOperation::Any);
SelectCHRPage(0, (((value ^ _regs[2]) >> 3) & 0x02) | (((value ^ _regs[2]) >> 5) & 0x01)); 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 class Txc22211C : public Txc22211A
{ {
protected: 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);
}
};