mirror of
https://github.com/libretro/Mesen.git
synced 2024-11-27 02:50:28 +00:00
Backport mapper fixes
This commit is contained in:
parent
bbd2149ae0
commit
ecb146c225
@ -17,11 +17,11 @@ protected:
|
||||
{
|
||||
_selectedReg = 0;
|
||||
_mirroringBit = 0;
|
||||
memset(_regs, 0, sizeof(_regs));
|
||||
memset(_regs, 0xFF, sizeof(_regs));
|
||||
|
||||
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
|
||||
|
||||
SelectPRGPage(1, -1);
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
|
@ -574,6 +574,12 @@ void BaseMapper::Initialize(RomData &romData)
|
||||
}
|
||||
}
|
||||
|
||||
if(romData.Info.MiscRoms) {
|
||||
_miscRomSize = (uint32_t)romData.MiscRomsData.size();
|
||||
_miscRom = new uint8_t[_miscRomSize];
|
||||
memcpy(_miscRom, romData.MiscRomsData.data(), _miscRomSize);
|
||||
}
|
||||
|
||||
SetupDefaultWorkRam();
|
||||
|
||||
SetMirroringType(romData.Info.Mirroring);
|
||||
@ -595,6 +601,7 @@ BaseMapper::~BaseMapper()
|
||||
delete[] _saveRam;
|
||||
delete[] _workRam;
|
||||
delete[] _nametableRam;
|
||||
delete[] _miscRom;
|
||||
}
|
||||
|
||||
void BaseMapper::GetMemoryRanges(MemoryRanges &ranges)
|
||||
@ -1166,6 +1173,27 @@ void BaseMapper::RestorePrgChrBackup(vector<uint8_t> &backupData)
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMapper::RevertPrgChrChanges()
|
||||
{
|
||||
memcpy(_prgRom, _originalPrgRom.data(), _originalPrgRom.size());
|
||||
if(_chrRom) {
|
||||
memcpy(_chrRom, _originalChrRom.data(), _originalChrRom.size());
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseMapper::HasPrgChrChanges()
|
||||
{
|
||||
if(memcmp(_prgRom, _originalPrgRom.data(), _originalPrgRom.size()) != 0) {
|
||||
return true;
|
||||
}
|
||||
if(_chrRom) {
|
||||
if(memcmp(_chrRom, _originalChrRom.data(), _originalChrRom.size()) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseMapper::CopyPrgChrRom(shared_ptr<BaseMapper> mapper)
|
||||
{
|
||||
if(_prgSize == mapper->_prgSize && _chrRomSize == mapper->_chrRomSize) {
|
||||
|
@ -68,6 +68,9 @@ protected:
|
||||
bool _hasChrBattery = false;
|
||||
int16_t _vramOpenBusValue = -1;
|
||||
|
||||
uint8_t* _miscRom = nullptr;
|
||||
uint32_t _miscRomSize = 0;
|
||||
|
||||
virtual void InitMapper() = 0;
|
||||
virtual void InitMapper(RomData &romData);
|
||||
virtual uint16_t GetPRGPageSize() = 0;
|
||||
@ -232,5 +235,7 @@ public:
|
||||
|
||||
vector<uint8_t> GetPrgChrCopy();
|
||||
void RestorePrgChrBackup(vector<uint8_t>& backupData);
|
||||
void RevertPrgChrChanges();
|
||||
bool HasPrgChrChanges();
|
||||
void CopyPrgChrRom(std::shared_ptr<BaseMapper> mapper);
|
||||
};
|
||||
|
@ -51,7 +51,7 @@ protected:
|
||||
{
|
||||
if(addr <= 0x7FFF) {
|
||||
if(CanWriteToWorkRam()) {
|
||||
_workRam[addr - 0x6000] = value;
|
||||
WritePrgRam(addr, value);
|
||||
if ((_exReg & 0x07) == 0) {
|
||||
_exReg = addr & 0x3F;
|
||||
UpdatePrgMapping();
|
||||
@ -62,4 +62,4 @@ protected:
|
||||
MMC3::WriteRegister(addr, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
@ -4,12 +4,12 @@
|
||||
|
||||
class Bmc235 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
bool _openBus = false;
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
uint8_t _unrom;
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPrgPage2x(0, 0);
|
||||
@ -19,45 +19,34 @@ protected:
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
SelectPrgPage2x(0, 0);
|
||||
_openBus = false;
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_openBus);
|
||||
if(!saving && _openBus) {
|
||||
RemoveCpuMemoryMapping(0x8000, 0xFFFF);
|
||||
if(_prgSize & 0x20000) {
|
||||
if(softReset) {
|
||||
_unrom = !_unrom;
|
||||
} else {
|
||||
_unrom = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SetMirroringType((addr & 0x0400) ? MirroringType::ScreenAOnly : (addr & 0x2000) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
uint8_t bank = ((addr >> 3) & 0x60) | (addr & 0x1F);
|
||||
|
||||
const uint8_t config[4][4][2] = {
|
||||
{ { 0x00, 0 }, { 0x00, 1 }, { 0x00, 1 }, { 0x00, 1 } },
|
||||
{ { 0x00, 0 }, { 0x00, 1 }, { 0x20, 0 }, { 0x00, 1 } },
|
||||
{ { 0x00, 0 }, { 0x00, 1 }, { 0x20, 0 }, { 0x40, 0 } },
|
||||
{ { 0x00, 0 }, { 0x20, 0 }, { 0x40, 0 }, { 0x60, 0 } }
|
||||
};
|
||||
if(_unrom) {
|
||||
SetMirroringType(MirroringType::Vertical);
|
||||
SelectPRGPage(0, (GetPRGPageCount() & 0xC0) | (value & 0x07));
|
||||
SelectPRGPage(1, (GetPRGPageCount() & 0xC0) | 0x07);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t mode;
|
||||
switch(GetPRGPageCount()) {
|
||||
case 64: mode = 0; break;
|
||||
case 128: mode = 1; break;
|
||||
case 256: mode = 2; break;
|
||||
default: mode = 3; break;
|
||||
};
|
||||
|
||||
uint8_t bank = config[mode][addr >> 8 & 0x03][0] | (addr & 0x1F);
|
||||
|
||||
_openBus = false;
|
||||
if(config[mode][addr >> 8 & 0x03][1]) {
|
||||
//Open bus
|
||||
_openBus = true;
|
||||
if(bank >= (GetPRGPageCount() >> 1)) {
|
||||
RemoveCpuMemoryMapping(0x8000, 0xFFFF);
|
||||
} else if(addr & 0x800) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetMirroringType((addr & 0x0400) ? MirroringType::ScreenAOnly : (addr & 0x2000) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
|
||||
if(addr & 0x800) {
|
||||
bank = (bank << 1) | (addr >> 12 & 0x01);
|
||||
SelectPRGPage(0, bank);
|
||||
SelectPRGPage(1, bank);
|
||||
|
@ -13,7 +13,7 @@ protected:
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
AddRegisterRange(0x5000, 0x5003, MemoryOperation::Write);
|
||||
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
@ -37,14 +37,15 @@ protected:
|
||||
{
|
||||
if(_regs[0] & 0x80) {
|
||||
if(_regs[1] & 0x80) {
|
||||
SelectPrgPage2x(0, (_regs[1] & 0x1F) << 1);
|
||||
SelectPrgPage2x(0, (_regs[1] & 0x3F) << 1);
|
||||
} else {
|
||||
int bank = ((_regs[1] & 0x1F) << 1) | ((_regs[1] >> 6) & 0x01);
|
||||
int bank = ((_regs[1] & 0x3F) << 1) | ((_regs[1] >> 6) & 0x01);
|
||||
SelectPRGPage(0, bank);
|
||||
SelectPRGPage(1, bank);
|
||||
}
|
||||
} else {
|
||||
SelectPRGPage(1, ((_regs[1] & 0x1F) << 1) | ((_regs[1] >> 6) & 0x01));
|
||||
SelectPRGPage(0, ((_regs[1] & 0x3F) << 1) | (_regs[3] & 0x07));
|
||||
SelectPRGPage(1, ((_regs[1] & 0x3F) << 1) | 0x07);
|
||||
}
|
||||
SetMirroringType(_regs[0] & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
SelectCHRPage(0, (_regs[2] << 2) | ((_regs[0] >> 1) & 0x03));
|
||||
@ -53,7 +54,11 @@ protected:
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
_regs[addr & 0x03] = value;
|
||||
if(HasChrRom() == false) {
|
||||
_regs[addr & 0x01] = value;
|
||||
} else {
|
||||
_regs[addr & 0x03] = value;
|
||||
}
|
||||
} else {
|
||||
_regs[3] = value;
|
||||
}
|
||||
|
@ -82,14 +82,15 @@ protected:
|
||||
{
|
||||
if(addr & 0x4000) {
|
||||
_bankMode = addr & 0x30;
|
||||
_prgReg = addr & 0x07;
|
||||
_prgReg = addr & 0x0F;
|
||||
} else {
|
||||
SetMirroringType(addr & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
if(_useOuterBank) {
|
||||
_outerBank = (addr & 0x03) << 3;
|
||||
_outerBank = (addr & 0x07) << 3;
|
||||
} else {
|
||||
_chrReg = addr & 0x07;
|
||||
_chrReg = addr & 0x0F;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
|
@ -33,11 +33,11 @@ protected:
|
||||
if(_mode & 0x02) {
|
||||
SelectPRGPage(0, (_regs[0] & 0x0F) | (_regs[1] & 0x70));
|
||||
} else {
|
||||
SelectPRGPage(0, _regs[0] & 0x03);
|
||||
SelectPRGPage(0, (_regs[0] & ((GetPRGPageCount() - 1) & 0x0F)) | 0x80);
|
||||
}
|
||||
|
||||
SelectPRGPage(1, _regs[1] & 0x7F);
|
||||
SetMirroringType(_regs[0] & 0x10 ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
SetMirroringType((_regs[0] & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
@ -49,6 +49,6 @@ protected:
|
||||
_regs[1] = value;
|
||||
_mode = reg;
|
||||
}
|
||||
UpdateState();
|
||||
UpdateState();
|
||||
}
|
||||
};
|
@ -19,6 +19,7 @@ protected:
|
||||
{
|
||||
_reg = 0;
|
||||
MMC3::Reset(softReset);
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
|
@ -56,4 +56,4 @@ protected:
|
||||
MMC3::WriteRegister(addr, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
47
Core/Bmc830752C.h
Normal file
47
Core/Bmc830752C.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Bmc830752C : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[2];
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_regs[0] = _regs[1] = 0;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1]);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
uint8_t bank = (_regs[1] & 0x0F) << 3;
|
||||
SelectPRGPage(0, bank | (_regs[0] & 0x07));
|
||||
SelectPRGPage(1, bank | 0x07);
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
SetMirroringType((_regs[1] & 0x60) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if ((addr >= 0xA000) && (addr < 0xC000)) {
|
||||
_regs[1] = value;
|
||||
} else {
|
||||
_regs[0] = value;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
};
|
65
Core/Bmc891227.h
Normal file
65
Core/Bmc891227.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Bmc891227 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _reg;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
if(!softReset) {
|
||||
_reg = 0;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_reg);
|
||||
}
|
||||
|
||||
void UpdateState() {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, PrgMemoryType::PrgRom, 0x2000, MemoryAccessType::Read);
|
||||
|
||||
switch((_reg & 0x60) >> 5) {
|
||||
case 0: // NROM-128
|
||||
SelectPRGPage(0, _reg & 0x1F);
|
||||
SelectPRGPage(1, _reg & 0x1F);
|
||||
break;
|
||||
|
||||
case 1: // NROM-256
|
||||
SelectPrgPage2x(0, _reg & 0x1E);
|
||||
break;
|
||||
|
||||
default: // UNROM
|
||||
SelectPRGPage(0, _reg & 0x3F);
|
||||
SelectPRGPage(1, (_reg & 0x3F) | 7);
|
||||
break;
|
||||
}
|
||||
SetMirroringType(_reg & 0x80 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0xC000) {
|
||||
_reg &= 0x07;
|
||||
_reg |= value & ~0x07;
|
||||
} else {
|
||||
_reg &= _reg & ~0x07;
|
||||
_reg |= value & 0x07;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
@ -28,4 +28,4 @@ protected:
|
||||
SelectCHRPage(0, value & 0x0F);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
66
Core/BmcCtc12in1.h
Normal file
66
Core/BmcCtc12in1.h
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class BmcCtc12in1 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _reg;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
if(!softReset) {
|
||||
_reg = 0;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_reg);
|
||||
}
|
||||
|
||||
void UpdateState() {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, PrgMemoryType::PrgRom, 0x2000, MemoryAccessType::Read);
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (_reg & 0x80) ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
||||
|
||||
switch((_reg & 0xC0) >> 6) {
|
||||
case 0: // NROM-128
|
||||
SelectPRGPage(0, _reg & 0x1F);
|
||||
SelectPRGPage(1, _reg & 0x1F);
|
||||
break;
|
||||
|
||||
case 1: // NROM-256
|
||||
SelectPrgPage2x(0, _reg & 0x1E);
|
||||
break;
|
||||
|
||||
default: // UNROM
|
||||
SelectPRGPage(0, _reg & 0x3F);
|
||||
SelectPRGPage(1, (_reg & 0x3F) | 7);
|
||||
break;
|
||||
}
|
||||
SetMirroringType(_reg & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0xC000) {
|
||||
_reg &= 0x07;
|
||||
_reg |= value & ~0x07;
|
||||
} else {
|
||||
_reg &= _reg & ~0x07;
|
||||
_reg |= value & 0x07;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
51
Core/BmcDs07.h
Normal file
51
Core/BmcDs07.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class BmcDs07 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[2];
|
||||
uint8_t _latch;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint16_t RegisterStartAddress() override { return 0x6000; }
|
||||
uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_regs[0] = _regs[1] = -1;
|
||||
_latch = 0;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1], _latch);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
uint8_t base = (_regs[0] & 0xF0) >> 1;
|
||||
SelectPRGPage(0, base | (_latch & 0x07));
|
||||
SelectPRGPage(1, base | 0x07);
|
||||
SetMirroringType((_latch & 0x80) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
_regs[addr & 0x01] = value;
|
||||
} else {
|
||||
if((_regs[0] & 0x80) == 0) {
|
||||
_latch = (_latch & 0xF8) | (value & 0x07);
|
||||
} else {
|
||||
_latch = value;
|
||||
}
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
59
Core/BmcResetNromX1n1.h
Normal file
59
Core/BmcResetNromX1n1.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// BMC-RESETNROM-XIN1 (Mapper 343)
|
||||
// submapper 1 - Sheng Tian 2-in-1(Unl,ResetBase)[p1] - Kung Fu (Spartan X), Super Mario Bros (alt)
|
||||
// submapper 1 - Sheng Tian 2-in-1(Unl,ResetBase)[p2] - B-Wings, Twin-beeSMB1 (wrong mirroring)
|
||||
|
||||
class BmcResetNromX1n1 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _game;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
if (!_romInfo.IsNes20Header || !_romInfo.IsInDatabase) {
|
||||
if (_romInfo.Hash.PrgChrCrc32 == 0x3470F395 || // Sheng Tian 2-in-1(Unl,ResetBase)[p1].unf
|
||||
_romInfo.Hash.PrgChrCrc32 == 0x39F9140F) { // Sheng Tian 2-in-1(Unl,ResetBase)[p2].unf
|
||||
_romInfo.SubMapperID = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
if(!softReset) {
|
||||
_game = 0;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_game);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if(_romInfo.SubMapperID == 1) {
|
||||
SelectPrgPage2x(0, _game << 1);
|
||||
} else {
|
||||
SelectPRGPage(0, _game);
|
||||
SelectPRGPage(1, _game);
|
||||
}
|
||||
SelectCHRPage(0, _game);
|
||||
SetMirroringType((_game & 0x80) ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_game = ~value;
|
||||
UpdateState();
|
||||
}
|
||||
};
|
@ -8,7 +8,6 @@ private:
|
||||
uint8_t _prgBank;
|
||||
uint8_t _outerBank;
|
||||
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
@ -31,6 +30,7 @@ protected:
|
||||
SelectPRGPage(0, (_outerBank << 3) | (_prgBank & 0x07));
|
||||
SelectPRGPage(1, (_outerBank << 3) | 0x07);
|
||||
SelectCHRPage(0, 0);
|
||||
SetMirroringType(((_outerBank & 0x20) == 0x20) ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
@ -39,6 +39,8 @@ protected:
|
||||
Stream(_prgBank, _outerBank);
|
||||
}
|
||||
|
||||
// TODO: suppose to have bus conflicts but why it crash if done so?
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
@ -48,4 +50,4 @@ protected:
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
||||
};
|
64
Core/Ctc15.h
Normal file
64
Core/Ctc15.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Ctc15 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _latch;
|
||||
uint16_t _irqCounter;
|
||||
bool _irqEnabled;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Write);
|
||||
AddRegisterRange(0x5000, 0x57FF, MemoryOperation::Write);
|
||||
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Write);
|
||||
|
||||
_latch = 0x07;
|
||||
_irqCounter = 0;
|
||||
_irqEnabled = false;
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
SelectPRGPage(0, _latch ^ 0x05);
|
||||
SelectPRGPage(1, 0x03);
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::WorkRam);
|
||||
}
|
||||
|
||||
virtual void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_latch, _irqCounter, _irqEnabled);
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if (_irqEnabled) {
|
||||
if (_irqCounter == 23680) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
} else if (_irqCounter == 24320) {
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
_irqCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (addr < 0x5000) {
|
||||
_latch = ((addr >> 3) & 0x04) | ((addr >> 2) & 0x03);
|
||||
_irqEnabled = (addr & 0x04) != 0x04;
|
||||
|
||||
if (!_irqEnabled) {
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
} else {
|
||||
SelectPRGPage(0, _latch ^ 0x05);
|
||||
}
|
||||
}
|
||||
};
|
188
Core/Fk23C.h
188
Core/Fk23C.h
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
#include "A12Watcher.h"
|
||||
|
||||
class Fk23C : public BaseMapper
|
||||
{
|
||||
@ -11,17 +13,17 @@ private:
|
||||
bool _mmc3ChrMode;
|
||||
bool _cnromChrMode;
|
||||
uint16_t _prgBaseBits;
|
||||
uint8_t _chrBaseBits;
|
||||
uint16_t _chrBaseBits;
|
||||
bool _extendedMmc3Mode;
|
||||
uint8_t _wramBankSelect;
|
||||
bool _ramInFirstChrBank;
|
||||
bool _allowSingleScreenMirroring;
|
||||
bool _fk23RegistersEnabled;
|
||||
bool _wramConfigEnabled;
|
||||
|
||||
|
||||
bool _wramEnabled;
|
||||
bool _wramWriteProtected;
|
||||
|
||||
|
||||
bool _invertPrgA14;
|
||||
bool _invertChrA12;
|
||||
|
||||
@ -34,24 +36,52 @@ private:
|
||||
|
||||
uint8_t _mirroringReg;
|
||||
|
||||
uint8_t _cnromChrReg;
|
||||
uint8_t _latch;
|
||||
uint8_t _mmc3Registers[12];
|
||||
|
||||
|
||||
uint8_t _irqDelay;
|
||||
A12Watcher _a12Watcher;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
uint32_t GetChrRamSize() override { return 0x40000; } //Some games have up to 256kb of CHR RAM (only used for iNES 1.0 files w/ no DB entry)
|
||||
uint16_t GetChrRamPageSize() override { return 0x400; }
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
uint32_t GetWorkRamSize() override { return 0x8000; } //Somes games have up to 32kb of Work RAM (only used for iNES 1.0 files w/ no DB entry)
|
||||
uint32_t GetWorkRamPageSize() override { return 0x2000; }
|
||||
virtual uint32_t GetChrRamSize() override {
|
||||
if(!_romInfo.IsNes20Header && !_romInfo.IsInDatabase) {
|
||||
if(HasChrRom()) {
|
||||
// Rockman I-VI uses mixed chr rom/ram
|
||||
if((_prgSize == 2048 * 1024) && (_chrRomSize == 512 * 1024)) {
|
||||
return 0x2000;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0x40000; // Some games have up to 256kb of CHR RAM (only used for iNES 1.0 files w/ no DB entry)
|
||||
}
|
||||
|
||||
void InitMapper() override
|
||||
virtual uint16_t GetChrRamPageSize() override { return 0x400; }
|
||||
|
||||
virtual uint32_t GetWorkRamSize() override { return 0x8000; } //Somes games have up to 32kb of Work RAM (only used for iNES 1.0 files w/ no DB entry)
|
||||
virtual uint32_t GetWorkRamPageSize() override { return 0x2000; }
|
||||
|
||||
virtual void InitMapper() override
|
||||
{
|
||||
if(!_romInfo.IsNes20Header && !_romInfo.IsInDatabase) {
|
||||
if(!HasChrRom()) {
|
||||
if(_prgSize >= 8192 * 1024) { // 120-in-1 (Unl)[U].unif
|
||||
_romInfo.SubMapperID = 2;
|
||||
}
|
||||
} else {
|
||||
if((_prgSize == 1024 *1024) && (_chrRomSize == _prgSize)) { // 4-in-1 (FK23C8078) (Ch) [p1][U][!].unf
|
||||
_romInfo.SubMapperID = 1;
|
||||
} else if((_prgSize >= 128 * 1024) && (_chrRomSize == 64 * 1024)) { // 126-in-1 (5-in-1, 16-in-1, 22-in-1, 42-in-1, 56-in-1, 62-in-1) [p1][U][!].unf
|
||||
_romInfo.SubMapperID = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//$5000
|
||||
_prgBankingMode = 0;
|
||||
_outerChrBankSize = 0;
|
||||
@ -72,15 +102,15 @@ protected:
|
||||
//$A001
|
||||
_wramBankSelect = 0;
|
||||
_ramInFirstChrBank = false;
|
||||
_allowSingleScreenMirroring = false;
|
||||
_allowSingleScreenMirroring = (_romInfo.SubMapperID == 2);
|
||||
_wramConfigEnabled = false;
|
||||
_fk23RegistersEnabled = false;
|
||||
_wramEnabled = false;
|
||||
_wramEnabled = true;
|
||||
_wramWriteProtected = false;
|
||||
|
||||
_currentRegister = 0;
|
||||
|
||||
_cnromChrReg = 0;
|
||||
_latch = 0;
|
||||
|
||||
constexpr uint8_t initValues[12] = { 0,2,4,5,6,7,0,1,0xFE, 0xFF, 0xFF, 0xFF };
|
||||
for(int i = 0; i < 12; i++) {
|
||||
@ -99,10 +129,15 @@ protected:
|
||||
_irqDelay = 0;
|
||||
|
||||
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
|
||||
|
||||
if(_romInfo.SubMapperID == 5) {
|
||||
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Write);
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
if(softReset) {
|
||||
if(_wramConfigEnabled && _selectChrRam && HasBattery()) {
|
||||
@ -112,7 +147,7 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
virtual void StreamState(bool saving) override
|
||||
{
|
||||
SnapshotInfo a12Watcher { &_a12Watcher };
|
||||
ArrayInfo<uint8_t> regs { _mmc3Registers, 12 };
|
||||
@ -120,7 +155,7 @@ protected:
|
||||
Stream(
|
||||
_prgBankingMode, _outerChrBankSize, _selectChrRam, _mmc3ChrMode, _cnromChrMode, _prgBaseBits, _chrBaseBits, _extendedMmc3Mode,
|
||||
_wramBankSelect, _ramInFirstChrBank, _allowSingleScreenMirroring, _fk23RegistersEnabled, _wramConfigEnabled, _wramEnabled, _wramWriteProtected,
|
||||
_invertPrgA14, _invertChrA12, _currentRegister, _irqReloadValue,_irqCounter, _irqReload, _irqEnabled, _mirroringReg, _cnromChrReg,
|
||||
_invertPrgA14, _invertChrA12, _currentRegister, _irqReloadValue,_irqCounter, _irqReload, _irqEnabled, _mirroringReg, _latch,
|
||||
_irqDelay, regs, a12Watcher
|
||||
);
|
||||
|
||||
@ -129,35 +164,43 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
|
||||
virtual void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
|
||||
{
|
||||
bool useChrRam = !HasChrRom() || (_selectChrRam && _chrRamSize > 0) || (_wramConfigEnabled && _ramInFirstChrBank && page <= 7);
|
||||
BaseMapper::SelectCHRPage(slot, page, useChrRam ? ChrMemoryType::ChrRam : ChrMemoryType::ChrRom);
|
||||
}
|
||||
|
||||
void UpdatePrg()
|
||||
virtual void UpdatePrg()
|
||||
{
|
||||
switch(_prgBankingMode) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 2: {
|
||||
uint8_t innerMask = 0x3F >> _prgBankingMode;
|
||||
|
||||
if((_romInfo.SubMapperID == 1 || _romInfo.SubMapperID == 3)) {
|
||||
if(_prgBankingMode == 0) {
|
||||
innerMask = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
if(_extendedMmc3Mode) {
|
||||
uint8_t swap = _invertPrgA14 ? 2 : 0;
|
||||
uint16_t outer = (_prgBaseBits << 1);
|
||||
SelectPRGPage(0 ^ swap, _mmc3Registers[6] | outer);
|
||||
SelectPRGPage(1, _mmc3Registers[7] | outer);
|
||||
SelectPRGPage(2 ^ swap, _mmc3Registers[8] | outer);
|
||||
SelectPRGPage(3, _mmc3Registers[9] | outer);
|
||||
} else {
|
||||
uint8_t swap = _invertPrgA14 ? 2 : 0;
|
||||
uint8_t innerMask = 0x3F >> _prgBankingMode;
|
||||
uint16_t outer = (_prgBaseBits << 1) & ~innerMask;
|
||||
SelectPRGPage(0 ^ swap, (_mmc3Registers[6] & innerMask) | outer);
|
||||
SelectPRGPage(1, (_mmc3Registers[7] & innerMask) | outer);
|
||||
SelectPRGPage(2 ^ swap, (0xFE & innerMask) | outer);
|
||||
SelectPRGPage(3, (0xFF & innerMask) | outer);
|
||||
SelectPRGPage(1, (_mmc3Registers[7] & innerMask) | outer);
|
||||
SelectPRGPage(2 ^ swap, (_mmc3Registers[8] & innerMask) | outer);
|
||||
SelectPRGPage(3, (_mmc3Registers[9] & innerMask) | outer);
|
||||
} else {
|
||||
uint8_t swap = _invertPrgA14 ? 2 : 0;
|
||||
uint16_t outer = (_prgBaseBits << 1) & ~innerMask;
|
||||
SelectPRGPage(0 ^ swap, (_mmc3Registers[6] & innerMask) | outer);
|
||||
SelectPRGPage(1, (_mmc3Registers[7] & innerMask) | outer);
|
||||
SelectPRGPage(2 ^ swap, (0xFE & innerMask) | outer);
|
||||
SelectPRGPage(3, (0xFF & innerMask) | outer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
SelectPrgPage2x(0, _prgBaseBits << 1);
|
||||
@ -167,18 +210,23 @@ protected:
|
||||
case 4:
|
||||
SelectPrgPage4x(0, (_prgBaseBits & 0xFFE) << 1);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
SelectPrgPage2x(0, ((_latch & 0x07) | (_prgBaseBits & ~0x07)) << 1);
|
||||
SelectPrgPage2x(1, (0x07 | _prgBaseBits) << 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateChr()
|
||||
virtual void UpdateChr()
|
||||
{
|
||||
if(!_mmc3ChrMode) {
|
||||
uint16_t innerMask = _cnromChrMode ? (_outerChrBankSize ? 1 : 3) : 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
SelectCHRPage(i, (((_cnromChrReg & innerMask) | _chrBaseBits) << 3) + i);
|
||||
SelectCHRPage(i, (((_latch & innerMask) | _chrBaseBits) << 3) + i);
|
||||
}
|
||||
} else {
|
||||
uint8_t swap = _invertChrA12 ? 0x04 : 0;
|
||||
@ -208,7 +256,7 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
virtual void UpdateMirroring()
|
||||
{
|
||||
switch(_mirroringReg & (_allowSingleScreenMirroring ? 0x03 : 0x01)) {
|
||||
case 0: SetMirroringType(MirroringType::Vertical); break;
|
||||
@ -216,7 +264,11 @@ protected:
|
||||
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
|
||||
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void UpdateState()
|
||||
{
|
||||
UpdateMirroring();
|
||||
UpdatePrg();
|
||||
UpdateChr();
|
||||
|
||||
@ -234,49 +286,81 @@ protected:
|
||||
};
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
virtual void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
if(_fk23RegistersEnabled || !_wramConfigEnabled) {
|
||||
if(((addr >= 0x5000) && (addr <= 0x5FFF)) &&
|
||||
(_fk23RegistersEnabled || !_wramConfigEnabled)) {
|
||||
uint16_t mask = 0x5010;
|
||||
if((addr & mask) != mask) {
|
||||
//not a register
|
||||
return;
|
||||
}
|
||||
|
||||
switch(addr & 0x03) {
|
||||
switch(addr & ((_romInfo.SubMapperID == 3) ? 0x07 : 0x03)) {
|
||||
case 0:
|
||||
_prgBankingMode = value & 0x07;
|
||||
_outerChrBankSize = (value & 0x10) >> 4;
|
||||
_selectChrRam = (value & 0x20) != 0;
|
||||
if(_romInfo.SubMapperID == 1 || _romInfo.SubMapperID == 5) {
|
||||
_cnromChrMode = (value & 0x20) == 0; // Only submapper 1/5 have CNROM latch
|
||||
}
|
||||
_mmc3ChrMode = (value & 0x40) == 0;
|
||||
_prgBaseBits = (_prgBaseBits & ~0x180) | ((value & 0x80) << 1) | ((value & 0x08) << 4);
|
||||
if(_romInfo.SubMapperID == 2) {
|
||||
_prgBaseBits = (_prgBaseBits & ~0x180) | ((value & 0x80) << 1) | ((value & 0x08) << 4);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_prgBaseBits = (_prgBaseBits & ~0x7F) | (value & 0x7F);
|
||||
if(_romInfo.SubMapperID == 5) {
|
||||
_prgBaseBits = (_prgBaseBits & ~0x1F) | (value & 0x1F);
|
||||
} else {
|
||||
_prgBaseBits = (_prgBaseBits & ~0x7F) | (value & 0x7F);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_prgBaseBits = (_prgBaseBits & ~0x200) | ((value & 0x40) << 3);
|
||||
_chrBaseBits = value;
|
||||
_cnromChrReg = 0;
|
||||
if(_romInfo.SubMapperID == 2) {
|
||||
_prgBaseBits = (_prgBaseBits & ~0xE00) | ((value & 0xC0) << 3) | ((value & 0x20) << 6);
|
||||
} else if(_romInfo.SubMapperID == 4) {
|
||||
_prgBaseBits = (_prgBaseBits & ~0x80) | (value & 0x80);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_extendedMmc3Mode = (value & 0x02) != 0;
|
||||
_cnromChrMode = (value & 0x44) != 0;
|
||||
break;
|
||||
|
||||
// Submapper 3 has 8 registers, providing PRG/CHR A21
|
||||
case 5:
|
||||
_prgBaseBits = (_prgBaseBits & 0x7F) | ((value & 0x0F) << 7);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
_chrBaseBits |= value << 8;
|
||||
break;
|
||||
}
|
||||
UpdateState();
|
||||
} else {
|
||||
//FK23C Registers disabled, $5000-$5FFF maps to the second 4 KiB of the 8 KiB WRAM bank 2
|
||||
WritePrgRam(addr, value);
|
||||
if(addr <= 0x4FFF) {
|
||||
// address $4800 - $4FFF is only enabled in submapper 5
|
||||
_prgBaseBits = (_prgBaseBits & 0x1F) | (value << 5);
|
||||
UpdatePrg();
|
||||
} else {
|
||||
//FK23C Registers disabled, $5000-$5FFF maps to the second 4 KiB of the 8 KiB WRAM bank 2
|
||||
WritePrgRam(addr, value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(_cnromChrMode && (addr <= 0x9FFF || addr >= 0xC000)) {
|
||||
_cnromChrReg = value & 0x03;
|
||||
UpdateState();
|
||||
_latch = value;
|
||||
|
||||
if(_prgBankingMode == 5) {
|
||||
UpdatePrg(); // Update UNROM latch
|
||||
}
|
||||
|
||||
if(!_mmc3ChrMode && _cnromChrMode) {
|
||||
UpdateChr(); //Update CNROM latch
|
||||
}
|
||||
|
||||
switch(addr & 0xE001) {
|
||||
@ -307,6 +391,7 @@ protected:
|
||||
break;
|
||||
|
||||
case 0xA001:
|
||||
// NOTE: bit 3 is unknown
|
||||
if((value & 0x20) == 0) {
|
||||
//Ignore extra bits if bit 5 is not set
|
||||
value &= 0xC0;
|
||||
@ -314,10 +399,9 @@ protected:
|
||||
|
||||
_wramBankSelect = (value & 0x03);
|
||||
_ramInFirstChrBank = (value & 0x04) != 0;
|
||||
_allowSingleScreenMirroring = (value & 0x08) != 0;
|
||||
_wramConfigEnabled = (value & 0x20) != 0;
|
||||
_fk23RegistersEnabled = (value & 0x40) != 0;
|
||||
|
||||
|
||||
_wramWriteProtected = (value & 0x40) != 0;
|
||||
_wramEnabled = (value & 0x80) != 0;
|
||||
|
||||
@ -377,4 +461,4 @@ public:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -23,4 +23,4 @@ protected:
|
||||
{
|
||||
SelectPrgPage4x(0, value << 2);
|
||||
}
|
||||
};
|
||||
};
|
368
Core/JyCompany.h
368
Core/JyCompany.h
@ -22,18 +22,20 @@ private:
|
||||
|
||||
uint8_t _prgMode;
|
||||
bool _enablePrgAt6000;
|
||||
uint8_t _prgBlock;
|
||||
|
||||
|
||||
uint8_t _chrMode;
|
||||
bool _chrBlockMode;
|
||||
uint8_t _chrBlock;
|
||||
bool _mirrorChr;
|
||||
uint8_t _outerBank; // $D003
|
||||
|
||||
uint8_t _mirroringReg;
|
||||
bool _extendedMirroring;
|
||||
|
||||
bool _advancedNtControl;
|
||||
bool _disableNtRam;
|
||||
|
||||
uint8_t _ntRamSelectBit;
|
||||
bool _chrWriteEnabled;
|
||||
|
||||
uint8_t _ntLowRegs[4];
|
||||
uint8_t _ntHighRegs[4];
|
||||
|
||||
@ -49,11 +51,15 @@ private:
|
||||
|
||||
uint8_t _multiplyValue1;
|
||||
uint8_t _multiplyValue2;
|
||||
uint8_t _accumulator;
|
||||
uint8_t _regRamValue;
|
||||
|
||||
uint16_t _lastPpuAddr;
|
||||
|
||||
bool _inhibitExtendedMirroring;
|
||||
|
||||
protected:
|
||||
virtual uint32_t GetDipSwitchCount() override { return 2; }
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
virtual bool AllowRegisterRead() override { return true; }
|
||||
@ -71,19 +77,20 @@ protected:
|
||||
memset(_chrHighRegs, 0, sizeof(_chrHighRegs));
|
||||
|
||||
_prgMode = 0;
|
||||
_prgBlock = 0;
|
||||
_enablePrgAt6000 = false;
|
||||
|
||||
_chrMode = 0;
|
||||
_chrBlockMode = false;
|
||||
_chrBlock = 0;
|
||||
_mirrorChr = false;
|
||||
|
||||
_outerBank = 0;
|
||||
|
||||
_mirroringReg = 0;
|
||||
_extendedMirroring = false;
|
||||
_advancedNtControl = false;
|
||||
_disableNtRam = false;
|
||||
|
||||
_ntRamSelectBit = 0;
|
||||
_chrWriteEnabled = false;
|
||||
memset(_ntLowRegs, 0, sizeof(_ntLowRegs));
|
||||
memset(_ntHighRegs, 0, sizeof(_ntHighRegs));
|
||||
|
||||
@ -100,8 +107,13 @@ protected:
|
||||
|
||||
_multiplyValue1 = 0;
|
||||
_multiplyValue2 = 0;
|
||||
_accumulator = 0;
|
||||
_regRamValue = 0;
|
||||
|
||||
if(_romInfo.MapperID == 90 || _romInfo.MapperID == 388) {
|
||||
_inhibitExtendedMirroring = true;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
@ -115,10 +127,11 @@ protected:
|
||||
ArrayInfo<uint8_t> ntLowRegs{ _ntLowRegs, 4 };
|
||||
ArrayInfo<uint8_t> ntHighRegs{ _ntHighRegs, 4 };
|
||||
|
||||
Stream(_chrLatch[0], _chrLatch[1], _prgMode, _enablePrgAt6000, _chrMode, _chrBlockMode, _chrBlock, _mirrorChr, _mirroringReg, _advancedNtControl,
|
||||
Stream(_chrLatch[0], _chrLatch[1], _prgMode, _enablePrgAt6000, _chrMode, _mirrorChr, _outerBank, _mirroringReg, _advancedNtControl,
|
||||
_disableNtRam, _ntRamSelectBit, _irqEnabled, _irqSource, _lastPpuAddr, _irqCountDirection, _irqFunkyMode, _irqFunkyModeReg, _irqSmallPrescaler,
|
||||
_irqPrescaler, _irqCounter, _irqXorReg, _multiplyValue1, _multiplyValue2, _regRamValue, prgRegs, chrLowRegs, chrHighRegs, ntLowRegs, ntHighRegs,
|
||||
_prgBlock);
|
||||
_irqPrescaler, _irqCounter, _irqXorReg, _multiplyValue1, _multiplyValue2, _regRamValue, _accumulator, _chrWriteEnabled, _extendedMirroring,
|
||||
prgRegs, chrLowRegs, chrHighRegs, ntLowRegs, ntHighRegs
|
||||
);
|
||||
|
||||
if(!saving) {
|
||||
UpdateState();
|
||||
@ -132,108 +145,202 @@ protected:
|
||||
UpdateMirroringState();
|
||||
}
|
||||
|
||||
uint8_t InvertPrgBits(uint8_t prgReg, bool needInvert)
|
||||
uint8_t InvertPrgBits(uint8_t value)
|
||||
{
|
||||
if(needInvert) {
|
||||
return (prgReg & 0x01) << 6 | (prgReg & 0x02) << 4 | (prgReg & 0x04) << 2 | (prgReg & 0x10) >> 2 | (prgReg & 0x20) >> 4 | (prgReg & 0x40) >> 6;
|
||||
} else {
|
||||
return prgReg;
|
||||
}
|
||||
return (value & 0x01) << 6 | (value & 0x02) << 4 | (value & 0x04) << 2 | (value & 0x10) >> 2 | (value & 0x20) >> 4 | (value & 0x40) >> 6;
|
||||
}
|
||||
|
||||
void UpdatePrgState()
|
||||
{
|
||||
bool invertBits = (_prgMode & 0x03) == 0x03;
|
||||
uint8_t lastBank = (_prgMode & 0x04) ? _prgRegs[3] : 0x3F;
|
||||
uint8_t wramBank = 0;
|
||||
uint8_t prgMask = 0;
|
||||
uint8_t prgBase = 0;
|
||||
|
||||
switch(_prgMode & 0x03) {
|
||||
case 0:
|
||||
SelectPrgPage4x(0, ((lastBank & 0x0F) | (_prgBlock << 4)) << 2);
|
||||
if(_enablePrgAt6000) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, ((_prgRegs[3] * 4 + 3) & 0x3F) | (_prgBlock << 6), PrgMemoryType::PrgRom);
|
||||
switch(_romInfo.MapperID) {
|
||||
case 281:
|
||||
prgMask = 0x1F;
|
||||
prgBase = (_outerBank & 0x03) << 5;
|
||||
break;
|
||||
|
||||
case 282:
|
||||
case 358:
|
||||
prgMask = 0x1F;
|
||||
prgBase = (_outerBank << 4) & ~prgMask;
|
||||
break;
|
||||
|
||||
case 295:
|
||||
prgMask = 0x0F;
|
||||
prgBase = (_outerBank & 0x07) << 4;
|
||||
break;
|
||||
|
||||
case 386:
|
||||
prgMask = 0x1F;
|
||||
prgBase = (((_outerBank & 0x02) >> 1) | ((_outerBank & 0x08) >> 2)) << 5;
|
||||
break;
|
||||
|
||||
case 387:
|
||||
prgMask = 0x0F;
|
||||
prgBase = (((_outerBank & 0x02) >> 1) | ((_outerBank & 0x08) >> 2)) << 4;
|
||||
break;
|
||||
|
||||
case 388:
|
||||
prgMask = 0x1F;
|
||||
prgBase = (_outerBank & 0x0C) << 3;
|
||||
break;
|
||||
|
||||
case 397:
|
||||
prgMask = 0x1F;
|
||||
prgBase = (_outerBank & 0x06) << 4;
|
||||
break;
|
||||
|
||||
case 421:
|
||||
if(_outerBank & 0x04) {
|
||||
prgMask = 0x3F;
|
||||
prgBase = (_outerBank & 0x0C) << 4;
|
||||
} else {
|
||||
prgMask = 0x1F;
|
||||
prgBase = (_outerBank & 0x0E) << 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
SelectPrgPage2x(0, ((_prgRegs[1] & 0x1F) | (_prgBlock << 5)) << 1);
|
||||
SelectPrgPage2x(1, ((lastBank & 0x1F) | (_prgBlock << 5)) << 1);
|
||||
if(_enablePrgAt6000) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, ((_prgRegs[3] * 2 + 1) & 0x3F) | (_prgBlock << 6), PrgMemoryType::PrgRom);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
SelectPRGPage(0, (InvertPrgBits(_prgRegs[0], invertBits) & 0x3F) | (_prgBlock << 6));
|
||||
SelectPRGPage(1, (InvertPrgBits(_prgRegs[1], invertBits) & 0x3F) | (_prgBlock << 6));
|
||||
SelectPRGPage(2, (InvertPrgBits(_prgRegs[2], invertBits) & 0x3F) | (_prgBlock << 6));
|
||||
SelectPRGPage(3, (InvertPrgBits(lastBank, invertBits) & 0x3F) | (_prgBlock << 6));
|
||||
if(_enablePrgAt6000) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (_prgRegs[3] & 0x3F) | (_prgBlock << 6), PrgMemoryType::PrgRom);
|
||||
}
|
||||
default: // Mapper 35/90/209/211
|
||||
prgMask = 0x3F;
|
||||
prgBase = (_outerBank << 5) & ~prgMask;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!_enablePrgAt6000) {
|
||||
switch(_prgMode & 0x03) {
|
||||
case 0:
|
||||
SelectPrgPage4x(0, ((lastBank & (prgMask >> 2)) | (prgBase >> 2)) << 2);
|
||||
wramBank = (_prgRegs[3] * 4 + 3);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
SelectPrgPage2x(0, ((_prgRegs[1] & (prgMask >> 1)) | (prgBase >> 1)) << 1);
|
||||
SelectPrgPage2x(1, ((lastBank & (prgMask >> 1)) | (prgBase >> 1)) << 1);
|
||||
wramBank = (_prgRegs[3] * 2 + 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
SelectPRGPage(0, (_prgRegs[0] & prgMask) | prgBase);
|
||||
SelectPRGPage(1, (_prgRegs[1] & prgMask) | prgBase);
|
||||
SelectPRGPage(2, (_prgRegs[2] & prgMask) | prgBase);
|
||||
SelectPRGPage(3, (lastBank & prgMask) | prgBase);
|
||||
wramBank = _prgRegs[3];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
SelectPRGPage(0, (InvertPrgBits(_prgRegs[0]) & prgMask) | prgBase);
|
||||
SelectPRGPage(1, (InvertPrgBits(_prgRegs[1]) & prgMask) | prgBase);
|
||||
SelectPRGPage(2, (InvertPrgBits(_prgRegs[2]) & prgMask) | prgBase);
|
||||
SelectPRGPage(3, (InvertPrgBits(lastBank) & prgMask) | prgBase);
|
||||
wramBank = _prgRegs[3];
|
||||
break;
|
||||
}
|
||||
|
||||
if(_enablePrgAt6000) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (wramBank & prgMask) | prgBase, PrgMemoryType::PrgRom);
|
||||
} else {
|
||||
RemoveCpuMemoryMapping(0x6000, 0x7FFF);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t GetChrReg(int index)
|
||||
{
|
||||
if(_chrMode >= 2 && _mirrorChr && (index == 2 || index == 3)) {
|
||||
index -= 2;
|
||||
}
|
||||
|
||||
if(_chrBlockMode) {
|
||||
uint8_t mask = 0;
|
||||
uint8_t shift = 0;
|
||||
switch(_chrMode) {
|
||||
default:
|
||||
case 0: mask = 0x1F; shift = 5; break;
|
||||
case 1: mask = 0x3F; shift = 6; break;
|
||||
case 2: mask = 0x7F; shift = 7; break;
|
||||
case 3: mask = 0xFF; shift = 8; break;
|
||||
}
|
||||
return (_chrLowRegs[index] & mask) | (_chrBlock << shift);
|
||||
} else {
|
||||
return _chrLowRegs[index] | (_chrHighRegs[index] << 8);
|
||||
}
|
||||
return _chrLowRegs[index] | (_chrHighRegs[index] << 8);
|
||||
}
|
||||
|
||||
void UpdateChrState()
|
||||
void GetChrSetup(uint16_t *mask, uint16_t *base)
|
||||
{
|
||||
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);
|
||||
switch(_romInfo.MapperID) {
|
||||
case 281:
|
||||
(*mask) = 0xFF;
|
||||
(*base) = (_outerBank & 0x03) << 8;
|
||||
break;
|
||||
|
||||
case 295:
|
||||
case 397:
|
||||
(*mask) = 0x7F;
|
||||
(*base) = (_outerBank & 0x07) << 7;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
SelectChrPage4x(0, chrRegs[_chrLatch[0]] << 2);
|
||||
SelectChrPage4x(1, chrRegs[_chrLatch[1]] << 2);
|
||||
case 358:
|
||||
case 386:
|
||||
case 387:
|
||||
if(_outerBank & 0x20) {
|
||||
(*mask) = 0x1FF;
|
||||
(*base) = (_outerBank & 0x04) << 7;
|
||||
} else {
|
||||
(*mask) = 0x0FF;
|
||||
(*base) = (((_outerBank & 0x04) >> 1) | (_outerBank & 0x01)) << 8;
|
||||
}
|
||||
break;
|
||||
|
||||
case 388:
|
||||
if(_outerBank & 0x20) {
|
||||
(*mask) = 0x1FF;
|
||||
(*base) = (_outerBank & 0x02) << 8;
|
||||
} else {
|
||||
(*mask) = 0x0FF;
|
||||
(*base) = (_outerBank & 0x03) << 8;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
SelectChrPage2x(0, chrRegs[0] << 1);
|
||||
SelectChrPage2x(1, chrRegs[2] << 1);
|
||||
SelectChrPage2x(2, chrRegs[4] << 1);
|
||||
SelectChrPage2x(3, chrRegs[6] << 1);
|
||||
case 421:
|
||||
(*mask) = 0x1FF;
|
||||
(*base) = (_outerBank & 0x03) << 8;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for(int i = 0; i < 8; i++) {
|
||||
SelectCHRPage(i, chrRegs[i]);
|
||||
default: // Mapper 35/90/209/211/282
|
||||
if(_outerBank & 0x20) {
|
||||
(*mask) = 0x1FF;
|
||||
(*base) = (_outerBank & 0x18) << 6;
|
||||
} else {
|
||||
(*mask) = 0xFF;
|
||||
(*base) = (_outerBank & 0x18) << 6 | (_outerBank & 0x01) << 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateChrState()
|
||||
{
|
||||
uint16_t chrMask = 0;
|
||||
uint16_t chrBase = 0;
|
||||
|
||||
GetChrSetup(&chrMask, &chrBase);
|
||||
|
||||
switch(_chrMode) {
|
||||
case 0:
|
||||
SelectChrPage8x(0, ((GetChrReg(0) & (chrMask >> 3)) | (chrBase >> 3)) << 3);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
SelectChrPage4x(0, ((GetChrReg(_chrLatch[0]) & (chrMask >> 2)) | (chrBase >> 2)) << 2);
|
||||
SelectChrPage4x(1, ((GetChrReg(_chrLatch[1]) & (chrMask >> 2)) | (chrBase >> 2)) << 2);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
SelectChrPage2x(0, ((GetChrReg(0) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
|
||||
SelectChrPage2x(1, ((GetChrReg(2) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
|
||||
SelectChrPage2x(2, ((GetChrReg(4) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
|
||||
SelectChrPage2x(3, ((GetChrReg(6) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for(int i = 0; i < 8; i++) {
|
||||
SelectCHRPage(i, (GetChrReg(i) & chrMask) | chrBase);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::ChrRam, _chrWriteEnabled ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
||||
}
|
||||
|
||||
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 || _romInfo.MapperID == 211) && _romInfo.MapperID != 90) {
|
||||
if(_advancedNtControl || _extendedMirroring) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
SetNametable(i, _ntLowRegs[i] & 0x01);
|
||||
}
|
||||
@ -249,14 +356,20 @@ protected:
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
uint8_t openBus = _console->GetMemoryManager()->GetOpenBus();
|
||||
|
||||
if((addr != 0x5800) && ((addr & 0x3FF) == 0)) {
|
||||
return ((GetDipSwitches() << 6) | (openBus & 0x3F));
|
||||
}
|
||||
|
||||
switch(addr & 0xF803) {
|
||||
case 0x5000: return 0; //Dip switches
|
||||
case 0x5800: return (_multiplyValue1 * _multiplyValue2) & 0xFF;
|
||||
case 0x5801: return ((_multiplyValue1 * _multiplyValue2) >> 8) & 0xFF;
|
||||
case 0x5802: return _accumulator;
|
||||
case 0x5803: return _regRamValue;
|
||||
}
|
||||
|
||||
return _console->GetMemoryManager()->GetOpenBus();
|
||||
return openBus;
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
@ -265,7 +378,11 @@ protected:
|
||||
switch(addr & 0xF803) {
|
||||
case 0x5800: _multiplyValue1 = value; break;
|
||||
case 0x5801: _multiplyValue2 = value; break;
|
||||
case 0x5803: _regRamValue = value; break;
|
||||
case 0x5802: _accumulator += value; break;
|
||||
case 0x5803:
|
||||
_regRamValue = value;
|
||||
_accumulator = 0;
|
||||
break;
|
||||
}
|
||||
} else if((addr >= 0xC000) && (addr < 0xD000)) {
|
||||
switch(addr & 0xF007) {
|
||||
@ -274,6 +391,7 @@ protected:
|
||||
_irqEnabled = true;
|
||||
} else {
|
||||
_irqEnabled = false;
|
||||
_irqPrescaler = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
break;
|
||||
@ -287,6 +405,7 @@ protected:
|
||||
|
||||
case 0xC002:
|
||||
_irqEnabled = false;
|
||||
_irqPrescaler = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
break;
|
||||
|
||||
@ -296,12 +415,9 @@ protected:
|
||||
case 0xC006: _irqXorReg = value; break;
|
||||
case 0xC007: _irqFunkyModeReg = value; break;
|
||||
}
|
||||
} else {
|
||||
// these registers extend only through addressing ranges $x000 - $x7FF
|
||||
} else if ((addr >= 0x9000) && (addr < 0xC000)) {
|
||||
switch(addr & 0xF807) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
_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;
|
||||
@ -319,8 +435,18 @@ protected:
|
||||
case 0xB004: case 0xB005: case 0xB006: case 0xB007:
|
||||
_ntHighRegs[addr & 0x03] = value;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(addr & 0xF803) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
_prgRegs[addr & 0x03] = value & 0x7F;
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
if(_inhibitExtendedMirroring) {
|
||||
value &= ~0x20;
|
||||
}
|
||||
|
||||
_prgMode = value & 0x07;
|
||||
_chrMode = (value >> 3) & 0x03;
|
||||
_advancedNtControl = (value & 0x20) == 0x20;
|
||||
@ -328,16 +454,24 @@ protected:
|
||||
_enablePrgAt6000 = (value & 0x80) == 0x80;
|
||||
break;
|
||||
|
||||
case 0xD001: _mirroringReg = value & 0x03; break;
|
||||
case 0xD002: _ntRamSelectBit = value & 0x80; break;
|
||||
case 0xD001:
|
||||
if(_inhibitExtendedMirroring) {
|
||||
value &= ~0x08;
|
||||
}
|
||||
|
||||
_mirroringReg = value & 0x03;
|
||||
_extendedMirroring = (value & 0x08) == 0x08;
|
||||
break;
|
||||
|
||||
case 0xD002:
|
||||
_chrWriteEnabled = (value & 0x40) == 0x40;
|
||||
_ntRamSelectBit = value & 0x80;
|
||||
break;
|
||||
|
||||
case 0xD003:
|
||||
_mirrorChr = (value & 0x80) == 0x80;
|
||||
_chrBlockMode = (value & 0x20) == 0x00;
|
||||
_chrBlock = ((value & 0x18) >> 2) | (value & 0x01);
|
||||
_prgBlock = (value & 0x06) >> 1;
|
||||
_outerBank = value & 0x3F;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,10 +494,13 @@ protected:
|
||||
if(addr >= 0x2000) {
|
||||
//This behavior only affects reads, not writes.
|
||||
//Additional info: https://forums.nesdev.com/viewtopic.php?f=3&t=17198
|
||||
if((_advancedNtControl || _romInfo.MapperID == 211) && _romInfo.MapperID != 90) {
|
||||
if(_advancedNtControl) {
|
||||
uint8_t ntIndex = ((addr & 0x2FFF) - 0x2000) / 0x400;
|
||||
if(_disableNtRam || (_ntLowRegs[ntIndex] & 0x80) != (_ntRamSelectBit & 0x80)) {
|
||||
uint16_t chrPage = _ntLowRegs[ntIndex] | (_ntHighRegs[ntIndex] << 8);
|
||||
uint16_t mask, base;
|
||||
GetChrSetup(&mask, &base);
|
||||
|
||||
uint16_t chrPage = base | ((_ntLowRegs[ntIndex] | (_ntHighRegs[ntIndex] << 8)) & mask);
|
||||
uint32_t chrOffset = chrPage * 0x400 + (addr & 0x3FF);
|
||||
if(_chrRomSize > chrOffset) {
|
||||
return _chrRom[chrOffset];
|
||||
@ -384,12 +521,14 @@ protected:
|
||||
}
|
||||
_lastPpuAddr = addr;
|
||||
|
||||
if(_romInfo.MapperID == 209) {
|
||||
if(_mirrorChr) {
|
||||
switch(addr & 0x2FF8) {
|
||||
case 0x0FD8:
|
||||
case 0x0FE8:
|
||||
_chrLatch[addr >> 12] = addr >> 4 & ((addr >> 10 & 0x04) | 0x02);
|
||||
UpdateChrState();
|
||||
if(_chrMode == 1) {
|
||||
UpdateChrState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -400,28 +539,31 @@ protected:
|
||||
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(_irqEnabled) {
|
||||
if(_irqCountDirection == 0x01) {
|
||||
_irqCounter++;
|
||||
if(_irqCounter == 0 && _irqEnabled) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
prescaler++;
|
||||
if((prescaler & mask) == 0) {
|
||||
clockIrqCounter = true;
|
||||
}
|
||||
} else if(_irqCountDirection == 0x02) {
|
||||
_irqCounter--;
|
||||
if(_irqCounter == 0xFF && _irqEnabled) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
if(--prescaler == 0) {
|
||||
clockIrqCounter = true;
|
||||
}
|
||||
}
|
||||
_irqPrescaler = (_irqPrescaler & ~mask) | (prescaler & mask);
|
||||
|
||||
if(clockIrqCounter) {
|
||||
if(_irqCountDirection == 0x01) {
|
||||
_irqCounter++;
|
||||
if(_irqCounter == 0) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
} else if(_irqCountDirection == 0x02) {
|
||||
_irqCounter--;
|
||||
if(_irqCounter == 0xFF) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
Core/Kaiser7010.h
Normal file
38
Core/Kaiser7010.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Kaiser7010 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPRGPage(0, 10);
|
||||
SelectPRGPage(1, 11);
|
||||
SelectPRGPage(2, 6);
|
||||
SelectPRGPage(3, 7);
|
||||
SelectCHRPage(0, 0);
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::PrgRom);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if ((addr >= 0xCAB6) && (addr <= 0xCAD7)) {
|
||||
SelectCHRPage(0, (addr >> 2) & 0x0F);
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (addr >> 2) & 0x0F, PrgMemoryType::PrgRom);
|
||||
}
|
||||
else if (((addr & 0xFFFE) == 0xEBE2) || ((addr & 0xFFFE) == 0xEE32)) {
|
||||
SelectCHRPage(0, (addr >> 2) & 0x0F);
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (addr >> 2) & 0x0F, PrgMemoryType::PrgRom);
|
||||
}
|
||||
else if ((addr & 0xFFFE) == 0xFFFC) {
|
||||
SelectCHRPage(0, (addr >> 2) & 0x0F);
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (addr >> 2) & 0x0F, PrgMemoryType::PrgRom);
|
||||
}
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
};
|
76
Core/Kaiser7030.h
Normal file
76
Core/Kaiser7030.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Kaiser7030 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _prgRegs[2];
|
||||
bool _old_mask_rom;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x0400; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint32_t GetWorkRamSize() override { return 0x2000; }
|
||||
uint32_t GetWorkRamPageSize() override { return 0x0400; }
|
||||
uint16_t RegisterStartAddress() override { return 0x8000; }
|
||||
uint16_t RegisterEndAddress() override { return 0x9FFF; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
// last 32 KiB mapped to CPU $8000-$FFFF
|
||||
SetCpuMemoryMapping(0x8000, 0xFFFF, 0x60, PrgMemoryType::PrgRom); // rom-offset $18000
|
||||
|
||||
_prgRegs[0] = 0x0F;
|
||||
_prgRegs[1] = 0x0F;
|
||||
_old_mask_rom = false;
|
||||
|
||||
if(_romInfo.Hash.PrgCrc32 == 0xFA4DAC91) {
|
||||
_old_mask_rom = true;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_prgRegs[0], _prgRegs[1], _old_mask_rom);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if(_old_mask_rom) {
|
||||
// As the FCEUX source code comment indicates, the actual bank order in the 128 KiB mask ROM was unknown until July 2020. Emulators expected the ROM image to be laid out like this:
|
||||
// * the first 32 KiB to contain the eight banks selected by register $8000 mapped to $7000-$7FFF;
|
||||
// * the next 64 KiB to contain the sixteen banks selected by register $9000, with the first 1 KiB mapped to CPU $6C00-$6FFF and the second 3 KiB mapped to CPU $C000-$CBFF;
|
||||
// * the final 32 KiB mapped to CPU $8000-$FFFF except where replaced by RAM and the switchable PRG-ROM bank.
|
||||
SetCpuMemoryMapping(0x6000, 0x6BFF, 0, PrgMemoryType::WorkRam);
|
||||
SetCpuMemoryMapping(0x6C00, 0x6FFF, ((_prgRegs[1] & 0x0F) << 2) + 0x20, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $8000
|
||||
SetCpuMemoryMapping(0x7000, 0x7FFF, ((_prgRegs[0] & 0x07) << 2) + 0x00, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $0
|
||||
SetCpuMemoryMapping(0xB800, 0xBFFF, 3, PrgMemoryType::WorkRam);
|
||||
SetCpuMemoryMapping(0xC000, 0xCBFF, ((_prgRegs[1] & 0x0F) << 2) + 0x21, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $8400
|
||||
SetCpuMemoryMapping(0xCC00, 0xD7FF, 5, PrgMemoryType::WorkRam);
|
||||
} else {
|
||||
// The actual mask ROM layout is as follows:
|
||||
// * the first 64 KiB contain the sixteen banks selected by register $9000, with the first 3 KiB mapped to CPU $C000-$CBFF and the second 1 KiB mapped to CPU $6C00-$6FFF;
|
||||
// * the next 32 KiB contain the eight banks selected by register $8000 mapped to $7000-$7FFF;
|
||||
// * the final 32 KiB mapped to CPU $8000-$FFFF except where replaced by RAM and the switchable PRG-ROM bank.
|
||||
SetCpuMemoryMapping(0x6000, 0x6BFF, 0, PrgMemoryType::WorkRam);
|
||||
SetCpuMemoryMapping(0x6C00, 0x6FFF, ((_prgRegs[1] & 0x0F) << 2) + 0x03, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $0C00
|
||||
SetCpuMemoryMapping(0x7000, 0x7FFF, ((_prgRegs[0] & 0x07) << 2) + 0x40, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $10000
|
||||
SetCpuMemoryMapping(0xB800, 0xBFFF, 3, PrgMemoryType::WorkRam);
|
||||
SetCpuMemoryMapping(0xC000, 0xCBFF, ((_prgRegs[1] & 0x0F) << 2) + 0x00, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $0000
|
||||
SetCpuMemoryMapping(0xCC00, 0xD7FF, 5, PrgMemoryType::WorkRam);
|
||||
}
|
||||
|
||||
SetMirroringType(_prgRegs[0] & 0x08 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_prgRegs[(addr >> 12) & 0x01] = addr & 0x0F;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
};
|
@ -7,7 +7,7 @@ class Kaiser7058 : public BaseMapper
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x1000; }
|
||||
virtual uint16_t RegisterStartAddress() override { return 0xF000; }
|
||||
virtual uint16_t RegisterStartAddress() override { return 0x8000; }
|
||||
virtual uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
|
||||
void InitMapper() override
|
||||
@ -17,9 +17,9 @@ protected:
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0xF080) {
|
||||
case 0xF000: SelectCHRPage(0, value); break;
|
||||
case 0xF080: SelectCHRPage(1, value); break;
|
||||
switch(addr & 0x01) {
|
||||
case 0x00: SelectCHRPage(0, value); break;
|
||||
case 0x01: SelectCHRPage(1, value); break;
|
||||
}
|
||||
}
|
||||
};
|
@ -9,6 +9,7 @@ class Mapper116 : public BaseMapper
|
||||
private:
|
||||
A12Watcher _a12Watcher;
|
||||
uint8_t _mode;
|
||||
uint8_t _game;
|
||||
|
||||
uint8_t _vrc2Chr[8];
|
||||
uint8_t _vrc2Prg[2];
|
||||
@ -28,14 +29,12 @@ private:
|
||||
bool _irqEnabled;
|
||||
|
||||
protected:
|
||||
virtual uint16_t RegisterStartAddress() override { return 0x4100; }
|
||||
virtual uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x400; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_mode = 0;
|
||||
_mode = 1;
|
||||
|
||||
_vrc2Chr[0] = -1;
|
||||
_vrc2Chr[1] = -1;
|
||||
@ -73,6 +72,23 @@ protected:
|
||||
_mmc1Buffer = 0;
|
||||
_mmc1Shift = 0;
|
||||
|
||||
// 5-in-1 multicart
|
||||
_game = (_romInfo.SubMapperID == 3) ? 4 : 0;
|
||||
|
||||
UpdateState();
|
||||
|
||||
AddRegisterRange(0x4100, 0x5FFF, MemoryOperation::Write);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
if(_romInfo.SubMapperID == 3) {
|
||||
_game++;
|
||||
if(_game > 4) {
|
||||
_game = 0;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
@ -119,20 +135,22 @@ protected:
|
||||
|
||||
void UpdatePrg()
|
||||
{
|
||||
uint8_t prgMask = (_romInfo.SubMapperID != 3) ? 0x3F : (_game ? 0x0F : 0x1F);
|
||||
uint32_t outerBank = _game ? (_game + 1) * 0x10 : 0;
|
||||
switch(_mode & 0x03) {
|
||||
case 0:
|
||||
SelectPRGPage(0, _vrc2Prg[0]);
|
||||
SelectPRGPage(1, _vrc2Prg[1]);
|
||||
SelectPRGPage(2, -2);
|
||||
SelectPRGPage(3, -1);
|
||||
SelectPRGPage(0, outerBank | (_vrc2Prg[0] & prgMask));
|
||||
SelectPRGPage(1, outerBank | (_vrc2Prg[1] & prgMask));
|
||||
SelectPRGPage(2, outerBank | ((-2) & prgMask));
|
||||
SelectPRGPage(3, outerBank | ((-1) & prgMask));
|
||||
break;
|
||||
|
||||
case 1: {
|
||||
uint32_t prgMode = (_mmc3Ctrl >> 5) & 0x02;
|
||||
SelectPRGPage(0, _mmc3Regs[6 + prgMode]);
|
||||
SelectPRGPage(1, _mmc3Regs[7]);
|
||||
SelectPRGPage(2, _mmc3Regs[6 + (prgMode ^ 0x02)]);
|
||||
SelectPRGPage(3, _mmc3Regs[9]);
|
||||
SelectPRGPage(0, outerBank | (_mmc3Regs[6 + prgMode] & prgMask));
|
||||
SelectPRGPage(1, outerBank | (_mmc3Regs[7] & prgMask));
|
||||
SelectPRGPage(2, outerBank | (_mmc3Regs[6 + (prgMode ^ 0x02)] & prgMask));
|
||||
SelectPRGPage(3, outerBank | (_mmc3Regs[9] & prgMask));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -140,6 +158,9 @@ protected:
|
||||
case 3: {
|
||||
uint8_t bank = _mmc1Regs[3] & 0x0F;
|
||||
if(_mmc1Regs[0] & 0x08) {
|
||||
if(_romInfo.SubMapperID == 2) { // Huang 2
|
||||
bank >>= 1;
|
||||
}
|
||||
if(_mmc1Regs[0] & 0x04) {
|
||||
SelectPrgPage2x(0, bank << 1);
|
||||
SelectPrgPage2x(1, 0x0F << 1);
|
||||
@ -148,7 +169,7 @@ protected:
|
||||
SelectPrgPage2x(1, bank << 1);
|
||||
}
|
||||
} else {
|
||||
SelectPrgPage4x(0, (bank & 0xFE) << 1);
|
||||
SelectPrgPage4x(0, (outerBank | (bank & 0xFE)) << 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -157,34 +178,39 @@ protected:
|
||||
|
||||
void UpdateChr()
|
||||
{
|
||||
uint32_t outerBank = (_mode & 0x04) << 6;
|
||||
uint32_t chrMask = _game ? 0x7F : 0xFF;
|
||||
uint32_t chrA18 = (_mode & 0x04) << 6;
|
||||
uint32_t outerBank = 0;
|
||||
if(_romInfo.SubMapperID == 3) {
|
||||
outerBank |= _game ? (_game + 1) * 0x80 : 0;
|
||||
}
|
||||
switch(_mode & 0x03) {
|
||||
case 0:
|
||||
for(int i = 0; i < 8; i++) {
|
||||
SelectCHRPage(i, outerBank | _vrc2Chr[i]);
|
||||
SelectCHRPage(i, outerBank | chrA18 | (_vrc2Chr[i] & chrMask));
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: {
|
||||
uint32_t slotSwap = (_mmc3Ctrl & 0x80) ? 4 : 0;
|
||||
SelectCHRPage(0 ^ slotSwap, outerBank | ((_mmc3Regs[0]) & 0xFE));
|
||||
SelectCHRPage(1 ^ slotSwap, outerBank | (_mmc3Regs[0] | 1));
|
||||
SelectCHRPage(2 ^ slotSwap, outerBank | ((_mmc3Regs[1]) & 0xFE));
|
||||
SelectCHRPage(3 ^ slotSwap, outerBank | (_mmc3Regs[1] | 1));
|
||||
SelectCHRPage(4 ^ slotSwap, outerBank | _mmc3Regs[2]);
|
||||
SelectCHRPage(5 ^ slotSwap, outerBank | _mmc3Regs[3]);
|
||||
SelectCHRPage(6 ^ slotSwap, outerBank | _mmc3Regs[4]);
|
||||
SelectCHRPage(7 ^ slotSwap, outerBank | _mmc3Regs[5]);
|
||||
SelectCHRPage(0 ^ slotSwap, outerBank | chrA18 | (((_mmc3Regs[0]) & 0xFE) & chrMask));
|
||||
SelectCHRPage(1 ^ slotSwap, outerBank | chrA18 | ((_mmc3Regs[0] | 1) & chrMask));
|
||||
SelectCHRPage(2 ^ slotSwap, outerBank | chrA18 | (((_mmc3Regs[1]) & 0xFE) & chrMask));
|
||||
SelectCHRPage(3 ^ slotSwap, outerBank | chrA18 | ((_mmc3Regs[1] | 1) & chrMask));
|
||||
SelectCHRPage(4 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[2] & chrMask));
|
||||
SelectCHRPage(5 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[3] & chrMask));
|
||||
SelectCHRPage(6 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[4] & chrMask));
|
||||
SelectCHRPage(7 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[5] & chrMask));
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
case 3: {
|
||||
if(_mmc1Regs[0] & 0x10) {
|
||||
SelectChrPage4x(0, _mmc1Regs[1] << 2);
|
||||
SelectChrPage4x(1, _mmc1Regs[2] << 2);
|
||||
SelectChrPage4x(0, (outerBank | (_mmc1Regs[1] & chrMask)) << 2);
|
||||
SelectChrPage4x(1, (outerBank | (_mmc1Regs[2] & chrMask)) << 2);
|
||||
} else {
|
||||
SelectChrPage8x(0, (_mmc1Regs[1] & 0xFE) << 2);
|
||||
SelectChrPage8x(0, (outerBank | ((_mmc1Regs[1] & 0xFE) & chrMask)) << 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -282,7 +308,7 @@ protected:
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
if((addr & 0x4100) == 0x4100) {
|
||||
if(addr & 0x100) {
|
||||
_mode = value;
|
||||
if(addr & 0x01) {
|
||||
_mmc1Regs[0] = 0xc;
|
||||
@ -290,6 +316,10 @@ protected:
|
||||
_mmc1Buffer = 0;
|
||||
_mmc1Shift = 0;
|
||||
}
|
||||
if((value & 0x03) != 1) {
|
||||
_irqEnabled = false;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
} else {
|
||||
|
75
Core/Mapper127.h
Normal file
75
Core/Mapper127.h
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
|
||||
// Double Dragon II pirate
|
||||
|
||||
class Mapper127 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _irqCounter;
|
||||
bool _irqEnabled;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_irqCounter = 0;
|
||||
_irqEnabled = false;
|
||||
|
||||
SelectPRGPage(0, 0x0F);
|
||||
SelectPRGPage(1, 0x0F);
|
||||
SelectPRGPage(2, 0x0F);
|
||||
SelectPRGPage(3, 0x0F);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_irqEnabled, _irqCounter);
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if(_irqEnabled && (!--_irqCounter)) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0x73) {
|
||||
case 0x00: SelectPRGPage(0, value & 0x0F); break;
|
||||
case 0x01: SelectPRGPage(1, value & 0x0F); break;
|
||||
case 0x02: SelectPRGPage(2, value & 0x0F); break;
|
||||
case 0x03: SelectPRGPage(3, (value & 0x03) | 0x0C); break;
|
||||
|
||||
case 0x10: SelectCHRPage(0, value & 0x7F); break;
|
||||
case 0x11: SelectCHRPage(1, value & 0x7F); break;
|
||||
case 0x12: SelectCHRPage(2, value & 0x7F); break;
|
||||
case 0x13: SelectCHRPage(3, value & 0x7F); break;
|
||||
case 0x20: SelectCHRPage(4, value & 0x7F); break;
|
||||
case 0x21: SelectCHRPage(5, value & 0x7F); break;
|
||||
case 0x22: SelectCHRPage(6, value & 0x7F); break;
|
||||
case 0x23: SelectCHRPage(7, value & 0x7F); break;
|
||||
|
||||
case 0x30: case 0x31: case 0x32: case 0x33:
|
||||
_irqEnabled = true;
|
||||
break;
|
||||
|
||||
case 0x40: case 0x41: case 0x42: case 0x43:
|
||||
_irqEnabled = false;
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
break;
|
||||
|
||||
case 0x50: SetNametable(0, value & 0x01); break;
|
||||
case 0x51: SetNametable(1, value & 0x01); break;
|
||||
case 0x52: SetNametable(2, value & 0x01); break;
|
||||
case 0x53: SetNametable(3, value & 0x01); break;
|
||||
}
|
||||
}
|
||||
};
|
51
Core/Mapper128.h
Normal file
51
Core/Mapper128.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// 4-in-1 pirate multicart
|
||||
|
||||
class Mapper128 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _reg;
|
||||
uint16_t _outerBank;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_outerBank = 0;
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
_outerBank = 0;
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_reg, _outerBank);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
SelectPRGPage(0, ((_outerBank >> 2) & 0x18) | (_reg & 0x07));
|
||||
SelectPRGPage(1, ((_outerBank >> 2) & 0x18) | 0x07);
|
||||
SelectCHRPage(0, 0);
|
||||
SetMirroringType((_outerBank & 0x02) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(_outerBank < 0xF000) {
|
||||
_outerBank = addr;
|
||||
}
|
||||
_reg = value;
|
||||
UpdateState();
|
||||
}
|
||||
};
|
22
Core/Mapper217.h
Normal file
22
Core/Mapper217.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper217 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectCHRPage(0, addr & 0x0F);
|
||||
addr >>= 2;
|
||||
SelectPRGPage(0, addr & 0x03);
|
||||
}
|
||||
};
|
@ -7,16 +7,34 @@ class Mapper225 : public BaseMapper
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
|
||||
uint8_t RAM[4];
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
memset(RAM, 0, sizeof(RAM));
|
||||
|
||||
SelectPRGPage(0, 0);
|
||||
SelectPRGPage(1, 1);
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
AddRegisterRange(0x5800, 0x5FFF, MemoryOperation::Any);
|
||||
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
return RAM[addr & 0x03] & 0x0F;
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr <= 0x5FFF) {
|
||||
RAM[addr & 0x03] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t highBit = (addr >> 8) & 0x40;
|
||||
uint8_t prgPage = ((addr >> 6) & 0x3F) | highBit;
|
||||
if(addr & 0x1000) {
|
||||
|
@ -2,50 +2,53 @@
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// NES Mapper 226
|
||||
// UNIF BMC-Ghostbusters63in1
|
||||
|
||||
class Mapper226 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint8_t _registers[2];
|
||||
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_registers[0] = 0;
|
||||
_registers[1] = 0;
|
||||
|
||||
SelectPRGPage(0, 0);
|
||||
SelectPRGPage(1, 1);
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
virtual void StreamState(bool saving) override
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_registers[0], _registers[1]);
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
if(softReset) {
|
||||
_registers[0] = 0;
|
||||
_registers[1] = 0;
|
||||
_registers[0] = 0;
|
||||
_registers[1] = 0;
|
||||
|
||||
SelectPRGPage(0, 0);
|
||||
SelectPRGPage(1, 1);
|
||||
SelectCHRPage(0, 0);
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
uint8_t GetPrgPage()
|
||||
{
|
||||
const uint8_t banks[4] = { 0, 0, 1, 2 };
|
||||
uint8_t base = ((_registers[0] & 0x80) >> 7) | ((_registers[1] & 0x01) << 1);
|
||||
|
||||
if(_prgSize == (1536 * 1024)) { // for 1536 KB PRG roms / BMC-Ghostbusters63in1
|
||||
base = banks[base];
|
||||
}
|
||||
|
||||
return (_registers[0] & 0x1F) | (base << 5);
|
||||
}
|
||||
|
||||
virtual uint8_t GetPrgPage()
|
||||
{
|
||||
return (_registers[0] & 0x1F) | ((_registers[0] & 0x80) >> 2) | ((_registers[1] & 0x01) << 6);
|
||||
}
|
||||
|
||||
void UpdatePrg()
|
||||
void UpdateState()
|
||||
{
|
||||
uint8_t prgPage = GetPrgPage();
|
||||
|
||||
if(_registers[0] & 0x20) {
|
||||
SelectPRGPage(0, prgPage);
|
||||
SelectPRGPage(1, prgPage);
|
||||
@ -53,17 +56,17 @@ protected:
|
||||
SelectPRGPage(0, prgPage & 0xFE);
|
||||
SelectPRGPage(1, (prgPage & 0xFE) + 1);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
SetMirroringType(_registers[0] & 0x40 ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0x8001) {
|
||||
case 0x8000: _registers[0] = value; break;
|
||||
case 0x8001: _registers[1] = value; break;
|
||||
}
|
||||
_registers[addr & 0x01] = value;
|
||||
|
||||
UpdatePrg();
|
||||
|
||||
SetMirroringType(_registers[0] & 0x40 ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
UpdateState();
|
||||
}
|
||||
};
|
||||
|
@ -4,20 +4,44 @@
|
||||
class Mapper227 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint32_t GetDipSwitchCount() override { return 4; }
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
|
||||
bool _mFlag;
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softreset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_mFlag);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if(_mFlag) {
|
||||
addr |= GetDipSwitches();
|
||||
}
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
uint16_t prgBank = ((addr >> 2) & 0x1F) | ((addr & 0x100) >> 3);
|
||||
bool sFlag = (addr & 0x01) == 0x01;
|
||||
bool lFlag = ((addr >> 9) & 0x01) == 0x01;
|
||||
bool prgMode = ((addr >> 7) & 0x01) == 0x01;
|
||||
_mFlag = ((addr >> 10) & 0x01) == 0x01;
|
||||
|
||||
if(prgMode) {
|
||||
if(sFlag) {
|
||||
@ -46,6 +70,9 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
// protect CHR-RAM on nrom modes
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (!HasBattery() && prgMode) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
|
||||
|
||||
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
@ -1,34 +1,52 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Mapper226.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper233 : public Mapper226
|
||||
class Mapper233 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
uint8_t _reset;
|
||||
|
||||
protected:
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
Mapper226::Reset(softReset);
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
if(softReset) {
|
||||
_reset = _reset ^ 0x01;
|
||||
UpdatePrg();
|
||||
} else {
|
||||
_reset = 0;
|
||||
}
|
||||
void InitMapper() override
|
||||
{
|
||||
_reset = 0;
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
Mapper226::StreamState(saving);
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_reset);
|
||||
}
|
||||
|
||||
uint8_t GetPrgPage() override
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
return (_registers[0] & 0x1F) | (_reset << 5) | ((_registers[1] & 0x01) << 6);
|
||||
if(softReset) {
|
||||
_reset ^= 0x20;
|
||||
}
|
||||
|
||||
WriteRegister(0, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(value & 0x20) {
|
||||
SelectPRGPage(0, _reset | (value & 0x1F));
|
||||
SelectPRGPage(1, _reset | (value & 0x1F));
|
||||
} else {
|
||||
SelectPRGPage(0, _reset | (value & 0x1E));
|
||||
SelectPRGPage(1, _reset | (value & 0x1E) + 1);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
switch((value >> 6) & 0x03) {
|
||||
case 0: SetMirroringType(MirroringType::ScreenAOnly); break;
|
||||
case 1: SetMirroringType(MirroringType::Vertical); break;
|
||||
case 2: SetMirroringType(MirroringType::Horizontal); break;
|
||||
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
72
Core/Mapper237.h
Normal file
72
Core/Mapper237.h
Normal file
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// Used by Teletubbies 420-in-1 multicart
|
||||
|
||||
class Mapper237 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _prgBank;
|
||||
bool _type;
|
||||
bool _lock;
|
||||
|
||||
protected:
|
||||
virtual uint32_t GetDipSwitchCount() override { return 2; }
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
virtual bool AllowRegisterRead() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_lock = false;
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
_lock = false;
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_prgBank, _lock, _type);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if (_type)
|
||||
return GetDipSwitches();
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(_lock) {
|
||||
_prgBank &= ~0x07;
|
||||
_prgBank |= value & 0x07;
|
||||
} else {
|
||||
_type = (addr & 0x01) == 0x01;
|
||||
_lock = (addr & 0x02) == 0x02;
|
||||
_prgBank = ((addr << 3) & 0x20) | (value & 0x1F);
|
||||
}
|
||||
|
||||
SetMirroringType((value & 0x20) == 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
|
||||
if(value & 0x80) {
|
||||
if(value & 0x40) {
|
||||
SelectPrgPage2x(0, _prgBank & 0xFE);
|
||||
} else {
|
||||
SelectPRGPage(0, _prgBank);
|
||||
SelectPRGPage(1, _prgBank);
|
||||
}
|
||||
} else {
|
||||
SelectPRGPage(0, _prgBank);
|
||||
SelectPRGPage(1, _prgBank | 0x07);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
};
|
@ -1,28 +1,69 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#pragma once
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper242 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
Reset(false);
|
||||
SelectCHRPage(0, 0);
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
void Reset(bool softreset) override
|
||||
{
|
||||
SelectPRGPage(0, 0);
|
||||
SetMirroringType(MirroringType::Vertical);
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SetMirroringType(addr & 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
SelectPRGPage(0, (addr >> 3) & 0x0F);
|
||||
uint16_t prgBank = (addr >> 2) & 0x1F;
|
||||
bool sFlag = (addr & 0x01) == 0x01;
|
||||
bool lFlag = ((addr >> 9) & 0x01) == 0x01;
|
||||
bool prgMode = ((addr >> 7) & 0x01) == 0x01;
|
||||
bool cFlag = ((addr >> 10) & 0x01) == 0x01;
|
||||
bool twoChips = (_prgSize & 0x20000) && (_prgSize > 0x20000);
|
||||
|
||||
if(twoChips) {
|
||||
if(cFlag | lFlag) {
|
||||
prgBank &= 0x1F;
|
||||
} else {
|
||||
prgBank = 0x20 + (prgBank & 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
if(prgMode) {
|
||||
if(sFlag) {
|
||||
SelectPrgPage2x(0, prgBank & 0xFE);
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank);
|
||||
}
|
||||
} else {
|
||||
if(sFlag){
|
||||
if(lFlag) {
|
||||
SelectPRGPage(0, prgBank & 0x3E);
|
||||
SelectPRGPage(1, prgBank | 0x07);
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank & 0x3E);
|
||||
SelectPRGPage(1, prgBank & 0x38);
|
||||
}
|
||||
} else {
|
||||
if(lFlag) {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank | 0x07);
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank & 0x38);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// protect CHR-RAM on nrom modes
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (!HasBattery() && prgMode && (_prgSize > 256 * 1024)) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
|
||||
|
||||
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
24
Core/Mapper271.h
Normal file
24
Core/Mapper271.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// BMC-22026 TXC 4-in-1 multicart (MGC-026)
|
||||
|
||||
class Mapper271 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectPRGPage(0, (value >> 4) & 0x03);
|
||||
SelectCHRPage(0, value & 0x0F);
|
||||
SetMirroringType((value & 0x20) ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
}
|
||||
};
|
108
Core/Mapper272.h
Normal file
108
Core/Mapper272.h
Normal file
@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper272 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _chrBanks[8];
|
||||
uint8_t _irqCounter;
|
||||
bool _irqEnabled;
|
||||
uint16_t _lastPpuAddr;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
memset(_chrBanks, 0, sizeof(_chrBanks));
|
||||
|
||||
_irqEnabled = false;
|
||||
_irqCounter = 0;
|
||||
|
||||
SelectPRGPage(2, -2);
|
||||
SelectPRGPage(3, -1);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
ArrayInfo<uint8_t> chrBanks = { _chrBanks, 8 };
|
||||
Stream(_irqEnabled, _irqCounter, chrBanks);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if((addr >= 0xB000) && (addr <= 0xE003)) {
|
||||
uint8_t index = ((((addr >> 12) & 0x07) - 3) << 1) + ((addr >> 1) & 0x01);
|
||||
bool lowBits = (addr & 0x01) == 0x00;
|
||||
if (lowBits) {
|
||||
_chrBanks[index] = (_chrBanks[index] & 0xF0) | (value & 0x0F);
|
||||
} else {
|
||||
_chrBanks[index] = (_chrBanks[index] & 0x0F) | (value << 4);
|
||||
}
|
||||
SelectCHRPage(index, _chrBanks[index]);
|
||||
} else {
|
||||
switch(addr & 0xF000) {
|
||||
case 0x8000:
|
||||
SelectPRGPage(0, value);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
SelectPRGPage(1, value);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
switch(value & 0x01) {
|
||||
case 0: SetMirroringType(MirroringType::Vertical); break;
|
||||
case 1: SetMirroringType(MirroringType::Horizontal); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch(addr & 0xC00C) {
|
||||
case 0x8004:
|
||||
switch(value & 0x03) {
|
||||
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
|
||||
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x800C:
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
break;
|
||||
|
||||
case 0xC004:
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
break;
|
||||
|
||||
case 0xC008:
|
||||
_irqEnabled = true;
|
||||
break;
|
||||
|
||||
case 0xC00C:
|
||||
_irqEnabled = false;
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyVRAMAddressChange(uint16_t addr) override
|
||||
{
|
||||
if ((_lastPpuAddr & 0x2000) && !(addr & 0x2000)) {
|
||||
if(_irqEnabled) {
|
||||
_irqCounter++;
|
||||
if(_irqCounter == 84) {
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lastPpuAddr = addr;
|
||||
}
|
||||
};
|
||||
|
49
Core/Mapper277.h
Normal file
49
Core/Mapper277.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper277 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
bool _locked;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0, 0x08);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
_locked = false;
|
||||
WriteRegister(0, 0x08);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(!_locked) {
|
||||
uint8_t prgBank = value & 0x0F;
|
||||
|
||||
_locked = (value & 0x20) == 0x20;
|
||||
|
||||
if(value & 0x08) {
|
||||
if(value & 0x01) {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank);
|
||||
} else {
|
||||
SelectPrgPage2x(0, prgBank & 0xFE);
|
||||
}
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank | 0x07);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
SetMirroringType(value & 0x10 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
74
Core/Mapper319.h
Normal file
74
Core/Mapper319.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// NES 2.0 Mapper 319
|
||||
// UNIF BMC-HP898F
|
||||
|
||||
class Mapper319 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[3];
|
||||
bool _unif_BankOrder;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint16_t RegisterStartAddress() override { return 0x6000; }
|
||||
uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_regs[0] = _regs[1] = _regs[2] = 0;
|
||||
_unif_BankOrder = false;
|
||||
|
||||
if (_romInfo.Hash.PrgCrc32 == 0xC25FD362) {
|
||||
_unif_BankOrder = true;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1], _regs[2]);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if(_unif_BankOrder) {
|
||||
// The publicly-available UNIF ROM file of Prima Soft 9999999-in-1 has the order of the 16 KiB PRG-ROM banks
|
||||
// slightly mixed up, so that the PRG A14 mode bit operates on A16 instead of A14.
|
||||
// To obtain the correct bank order,
|
||||
// use UNIF 16 KiB PRG banks 0, 4, 1, 5, 2, 6, 3, 7.
|
||||
uint8_t prgReg = (_regs[1] >> 3) & 7;
|
||||
uint8_t prgMask = (_regs[1] >> 4) & 4;
|
||||
SelectCHRPage(0, (((_regs[0] >> 4) & 0x07) & ~(((_regs[0] & 0x01) << 2) | (_regs[0] & 0x02))));
|
||||
SelectPRGPage(0, prgReg & (~prgMask));
|
||||
SelectPRGPage(1, prgReg | prgMask);
|
||||
} else {
|
||||
if(_regs[1] & 0x40) {
|
||||
SelectPrgPage2x(0, (_regs[1] >> 2) & 0xE);
|
||||
} else {
|
||||
uint8_t bank = ((_regs[1] >> 2) & 0x06) | ((_regs[1] >> 5) & 0x01);
|
||||
SelectPRGPage(0, bank);
|
||||
SelectPRGPage(1, bank);
|
||||
}
|
||||
SelectCHRPage(0, ((_regs[0] >> 4) & ~((_regs[0] << 2) & 0x04)) | ((_regs[2] << 2) & ((_regs[0] << 2) & 0x04)));
|
||||
}
|
||||
|
||||
SetMirroringType(_regs[1] & 0x80 ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
_regs[(addr & 0x04) >> 2] = value;
|
||||
UpdateState();
|
||||
} else {
|
||||
_regs[2] = value;
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
};
|
46
Core/Mapper326.h
Normal file
46
Core/Mapper326.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// NES 2.0 Mapper 326 is used for a bootleg version of Contra/Gryzor.
|
||||
// https://forums.nesdev.org/viewtopic.php?f=9&t=17352&p=218722#p218722
|
||||
|
||||
class Mapper326 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPRGPage(3, -1);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0xE010) {
|
||||
case 0x8000: SelectPRGPage(0, value); break;
|
||||
case 0xA000: SelectPRGPage(1, value); break;
|
||||
case 0xC000: SelectPRGPage(2, value); break;
|
||||
}
|
||||
|
||||
if((addr & 0x8010) == 0x8010) {
|
||||
switch(addr & 0x0F) {
|
||||
case 0x00: SelectCHRPage(0, value); break;
|
||||
case 0x01: SelectCHRPage(1, value); break;
|
||||
case 0x02: SelectCHRPage(2, value); break;
|
||||
case 0x03: SelectCHRPage(3, value); break;
|
||||
case 0x04: SelectCHRPage(4, value); break;
|
||||
case 0x05: SelectCHRPage(5, value); break;
|
||||
case 0x06: SelectCHRPage(6, value); break;
|
||||
case 0x07: SelectCHRPage(7, value); break;
|
||||
case 0x08: SetNametable(0, value & 1); break;
|
||||
case 0x09: SetNametable(1, value & 1); break;
|
||||
case 0x0A: SetNametable(2, value & 1); break;
|
||||
case 0x0B: SetNametable(3, value & 1); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
132
Core/Mapper330.h
Normal file
132
Core/Mapper330.h
Normal file
@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "Namco163Audio.h"
|
||||
#include "BatteryManager.h"
|
||||
|
||||
// NES 2.0 Mapper 330 is used for a bootleg version of Namco's 三国志 II: 覇王の大陸 (Sangokushi II: Haō no Tairiku).
|
||||
// http://forums.nesdev.org/viewtopic.php?f=9&t=17469
|
||||
|
||||
class Mapper330 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
unique_ptr<Namco163Audio> _audio;
|
||||
uint16_t _irqCounter;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x400; }
|
||||
virtual bool AllowRegisterRead() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_audio.reset(new Namco163Audio(_console));
|
||||
_irqCounter = 0;
|
||||
|
||||
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Any);
|
||||
RemoveRegisterRange(0x5000, 0xFFFF, MemoryOperation::Read);
|
||||
|
||||
SelectPRGPage(3, -1);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
|
||||
SnapshotInfo audio{ _audio.get() };
|
||||
Stream(_irqCounter, audio);
|
||||
}
|
||||
|
||||
void LoadBattery() override
|
||||
{
|
||||
if(HasBattery()) {
|
||||
vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0);
|
||||
_console->GetBatteryManager()->LoadBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size());
|
||||
|
||||
memcpy(_saveRam, batteryContent.data(), _saveRamSize);
|
||||
memcpy(_audio->GetInternalRam(), batteryContent.data()+_saveRamSize, Namco163Audio::AudioRamSize);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveBattery() override
|
||||
{
|
||||
if(HasBattery()) {
|
||||
vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0);
|
||||
memcpy(batteryContent.data(), _saveRam, _saveRamSize);
|
||||
memcpy(batteryContent.data() + _saveRamSize, _audio->GetInternalRam(), Namco163Audio::AudioRamSize);
|
||||
|
||||
_console->GetBatteryManager()->SaveBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size());
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if(_irqCounter & 0x8000 && (_irqCounter & 0x7FFF) != 0x7FFF) {
|
||||
_irqCounter++;
|
||||
if((_irqCounter & 0x7FFF) == 0x7FFF) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
|
||||
_audio->Clock();
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if(addr & 0x800) {
|
||||
return _audio->ReadRegister(addr);
|
||||
}
|
||||
return BaseMapper::ReadRegister(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0xF000) {
|
||||
case 0x4000:
|
||||
if(addr & 0x800) {
|
||||
_audio->WriteRegister(addr, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8000:
|
||||
case 0x9000:
|
||||
case 0xA000:
|
||||
case 0xB000:
|
||||
if((addr & 0x400) && !(addr & 0x4000)) {
|
||||
if(addr & 0x2000) {
|
||||
_irqCounter = (_irqCounter & 0x00FF) | (value << 8);
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
} else {
|
||||
_irqCounter = (_irqCounter & 0xFF00) | value;
|
||||
}
|
||||
} else {
|
||||
uint8_t bankNumber = (addr - 0x8000) >> 11;
|
||||
SelectCHRPage(bankNumber, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
case 0xD000:
|
||||
if(!(addr & 0x400)) {
|
||||
uint8_t bankNumber = (addr - 0xC000) >> 11;
|
||||
SetNametable(bankNumber, value & 0x01);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
case 0xF000:
|
||||
if(((addr & 0xF000) == 0xF000) && (addr & 0x800)) {
|
||||
_audio->WriteRegister(addr, value);
|
||||
} else {
|
||||
if(!(addr & 0x400)) {
|
||||
uint8_t bankNumber = (addr - 0xE000) >> 11;
|
||||
SelectPRGPage(bankNumber, value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
115
Core/Mapper357.h
Normal file
115
Core/Mapper357.h
Normal file
@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
|
||||
class Mapper357 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
const uint8_t _dipswitchLut[4] = {
|
||||
0x00, 0x08, 0x10, 0x18
|
||||
};
|
||||
|
||||
const uint8_t bankLut[2][8] = {
|
||||
{ 4, 3, 5, 3, 6, 3, 7, 3 },
|
||||
{ 1, 1, 5, 1, 4, 1, 5, 1 }
|
||||
};
|
||||
|
||||
uint16_t _irqCounter;
|
||||
bool _irqEnabled;
|
||||
uint8_t _smb2jReg;
|
||||
uint8_t _unromReg;
|
||||
bool _prgSwap;
|
||||
uint8_t _dipswitch;
|
||||
|
||||
protected:
|
||||
uint16_t RegisterStartAddress() override { return 0x4020; }
|
||||
uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
uint32_t GetDipSwitchCount() override { return 2; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_irqCounter = 0;
|
||||
_irqEnabled = false;
|
||||
_unromReg = 0;
|
||||
_smb2jReg = 0;
|
||||
_prgSwap = 0;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
_dipswitch = _dipswitchLut[GetDipSwitches() & 0x03];
|
||||
|
||||
_irqCounter = 0;
|
||||
_irqEnabled = false;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if(_dipswitch == 0) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, _prgSwap ? 0x00 : 0x02, PrgMemoryType::PrgRom);
|
||||
|
||||
SelectPRGPage(0, _prgSwap ? 0x00 : 0x01);
|
||||
SelectPRGPage(1, 0x00);
|
||||
SelectPRGPage(2, bankLut[_prgSwap][_smb2jReg]);
|
||||
SelectPRGPage(3, _prgSwap ? 0x08 : 0x0A);
|
||||
} else {
|
||||
SelectPrgPage2x(0, (_dipswitch | _unromReg) << 1);
|
||||
SelectPrgPage2x(1, (_dipswitch | 0x07) << 1);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
SetMirroringType((_dipswitch == 0x18) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_irqCounter, _irqEnabled, _unromReg, _smb2jReg, _prgSwap, _dipswitch);
|
||||
|
||||
if(!saving) {
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if(_irqEnabled) {
|
||||
_irqCounter++;
|
||||
if(_irqCounter == 0x1000) {
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr & 0x8000) {
|
||||
_unromReg = value & 0x07;
|
||||
UpdateState();
|
||||
}
|
||||
if((addr & 0x71FF) == 0x4022) {
|
||||
_smb2jReg = value & 0x07;
|
||||
UpdateState();
|
||||
}
|
||||
if((addr & 0x71FF) == 0x4120) {
|
||||
_prgSwap = (value & 0x01) == 0x01;
|
||||
UpdateState();
|
||||
}
|
||||
if((addr & 0x71FF) == 0x4122) {
|
||||
_irqEnabled = (value & 0x01) == 0x01;
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
};
|
265
Core/Mapper359.h
Normal file
265
Core/Mapper359.h
Normal file
@ -0,0 +1,265 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper359 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
// $8000 - $8003
|
||||
uint8_t _prgBanks[4];
|
||||
|
||||
// $9000
|
||||
uint8_t _outerPrgBank;
|
||||
|
||||
// $9001
|
||||
uint8_t _prgMask;
|
||||
uint8_t _chrMask;
|
||||
|
||||
// $9002
|
||||
uint8_t _mirroring;
|
||||
|
||||
// $9003
|
||||
uint16_t _outerChrBank;
|
||||
|
||||
// $A000 - $B003
|
||||
uint8_t _chrBanks[8];
|
||||
|
||||
// IRQ
|
||||
bool _irqPpuA12;
|
||||
bool _irqAutoEnable;
|
||||
bool _irqEnabled;
|
||||
bool _irqReload;
|
||||
uint16_t _irqCounter;
|
||||
|
||||
A12Watcher _a12Watcher;
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_prgBanks[0] = -4;
|
||||
_prgBanks[1] = -3;
|
||||
_prgBanks[2] = -2;
|
||||
_prgBanks[3] = -1;
|
||||
|
||||
_chrBanks[0] = 0x00;
|
||||
_chrBanks[1] = 0x01;
|
||||
_chrBanks[2] = 0x02;
|
||||
_chrBanks[3] = 0x03;
|
||||
_chrBanks[4] = 0x04;
|
||||
_chrBanks[5] = 0x05;
|
||||
_chrBanks[6] = 0x06;
|
||||
_chrBanks[7] = 0x07;
|
||||
|
||||
_outerPrgBank = 0x00;
|
||||
_outerChrBank = 0x00;
|
||||
|
||||
_prgMask = 0x3F;
|
||||
_chrMask = 0xFF;
|
||||
|
||||
_mirroring = 0;
|
||||
|
||||
_irqCounter = 0;
|
||||
_irqPpuA12 = false;
|
||||
_irqAutoEnable = false;
|
||||
_irqReload = false;
|
||||
_irqEnabled = false;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
|
||||
SnapshotInfo a12Watcher { &_a12Watcher };
|
||||
|
||||
ArrayInfo<uint8_t> prgBanks { _prgBanks, 4 };
|
||||
ArrayInfo<uint8_t> chrBanks { _chrBanks, 8 };
|
||||
|
||||
Stream(
|
||||
_outerPrgBank, _outerChrBank,
|
||||
_prgMask, _chrMask, _mirroring,
|
||||
_irqCounter, _irqPpuA12, _irqReload,
|
||||
_irqEnabled, _irqAutoEnable,
|
||||
prgBanks, chrBanks, a12Watcher
|
||||
);
|
||||
|
||||
if(!saving) {
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePrgMapping()
|
||||
{
|
||||
SelectPRGPage(0, _outerPrgBank | (_prgBanks[0] & _prgMask));
|
||||
SelectPRGPage(1, _outerPrgBank | (_prgBanks[1] & _prgMask));
|
||||
SelectPRGPage(2, _outerPrgBank | (_prgBanks[2] & _prgMask));
|
||||
SelectPRGPage(3, _outerPrgBank | ((-1) & _prgMask));
|
||||
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, _outerPrgBank | (_prgBanks[3] & _prgMask), PrgMemoryType::PrgRom);
|
||||
}
|
||||
|
||||
virtual void UpdateChrMapping()
|
||||
{
|
||||
if(HasChrRom()) {
|
||||
SelectCHRPage(0, _outerChrBank | (_chrBanks[0] & _chrMask));
|
||||
SelectCHRPage(1, _outerChrBank | (_chrBanks[1] & _chrMask));
|
||||
SelectCHRPage(2, _outerChrBank | (_chrBanks[2] & _chrMask));
|
||||
SelectCHRPage(3, _outerChrBank | (_chrBanks[3] & _chrMask));
|
||||
SelectCHRPage(4, _outerChrBank | (_chrBanks[4] & _chrMask));
|
||||
SelectCHRPage(5, _outerChrBank | (_chrBanks[5] & _chrMask));
|
||||
SelectCHRPage(6, _outerChrBank | (_chrBanks[6] & _chrMask));
|
||||
SelectCHRPage(7, _outerChrBank | (_chrBanks[7] & _chrMask));
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMirroring()
|
||||
{
|
||||
switch(_mirroring) {
|
||||
case 0: SetMirroringType(MirroringType::Vertical); break;
|
||||
case 1: SetMirroringType(MirroringType::Horizontal); break;
|
||||
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
|
||||
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
UpdatePrgMapping();
|
||||
UpdateChrMapping();
|
||||
UpdateMirroring();
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0xF000) {
|
||||
case 0x8000:
|
||||
_prgBanks[addr & 0x03] = value;
|
||||
UpdatePrgMapping();
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
_outerPrgBank = (value & 0x38) << 1;
|
||||
UpdatePrgMapping();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
switch(value & 0x03) {
|
||||
case 0: _prgMask = 0x3F; break;
|
||||
case 1: _prgMask = 0x1F; break;
|
||||
case 2: _prgMask = 0x2F; break;
|
||||
case 3: _prgMask = 0x0F; break;
|
||||
}
|
||||
_chrMask = (value & 0x40) ? 0xFF : 0x7F;
|
||||
UpdatePrgMapping();
|
||||
UpdateChrMapping();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_mirroring = value & 0x03;
|
||||
UpdateMirroring();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_outerChrBank = value << 7;
|
||||
UpdateChrMapping();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
_chrBanks[0 | (addr & 0x03)] = value;
|
||||
UpdateChrMapping();
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
_chrBanks[4 | (addr & 0x03)] = value;
|
||||
UpdateChrMapping();
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
if(_irqAutoEnable) {
|
||||
_irqEnabled = false;
|
||||
}
|
||||
_irqCounter &= 0xFF00;
|
||||
_irqCounter |= value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(_irqAutoEnable) {
|
||||
_irqEnabled = true;
|
||||
}
|
||||
_irqCounter &= 0x00FF;
|
||||
_irqCounter |= value << 8;
|
||||
_irqReload = true;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_irqEnabled = (value & 0x01) == 0x01;
|
||||
_irqPpuA12 = (value & 0x02) == 0x02;
|
||||
_irqAutoEnable = (value & 0x04) == 0x04;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_irqEnabled = (value & 0x01) == 0x01;
|
||||
break;
|
||||
}
|
||||
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if(_irqPpuA12)
|
||||
return;
|
||||
|
||||
if(_irqEnabled && _irqCounter) {
|
||||
_irqCounter--;
|
||||
if(_irqCounter == 0) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyVRAMAddressChange(uint16_t addr) override
|
||||
{
|
||||
uint8_t counter;
|
||||
|
||||
if(!_irqPpuA12)
|
||||
return;
|
||||
|
||||
switch(_a12Watcher.UpdateVramAddress(addr, _console->GetPpu()->GetFrameCycle())) {
|
||||
case A12StateChange::None:
|
||||
case A12StateChange::Fall:
|
||||
break;
|
||||
|
||||
case A12StateChange::Rise:
|
||||
counter = _irqCounter & 0xFF;
|
||||
|
||||
if(counter == 0 || _irqReload) {
|
||||
counter = (_irqCounter & 0xFF00) >> 8;
|
||||
} else {
|
||||
counter--;
|
||||
}
|
||||
|
||||
if(counter == 0 && _irqEnabled) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
|
||||
_irqReload =false;
|
||||
_irqCounter &= 0xFF00;
|
||||
_irqCounter |= counter;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
31
Core/Mapper360.h
Normal file
31
Core/Mapper360.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// 31-in-1 multicart using dipswitch
|
||||
|
||||
class Mapper360 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint32_t GetDipSwitchCount() override { return 5; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
RemoveRegisterRange(0x8000, 0xFFFF);
|
||||
|
||||
uint32_t _dipsw = GetDipSwitches();
|
||||
|
||||
if (_dipsw < 2)
|
||||
SelectPrgPage2x(0, _dipsw & 0xFE);
|
||||
else {
|
||||
SelectPRGPage(0, _dipsw);
|
||||
SelectPRGPage(1, _dipsw);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, _dipsw);
|
||||
|
||||
SetMirroringType((_dipsw & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
162
Core/Mapper362.h
Normal file
162
Core/Mapper362.h
Normal file
@ -0,0 +1,162 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "VrcIrq.h"
|
||||
#include "Console.h"
|
||||
|
||||
// VRC4 Clone with highest CHR-ROM bank bits are repurposed as outer bank bits
|
||||
|
||||
class Mapper362 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
unique_ptr<VrcIrq> _irq;
|
||||
|
||||
uint8_t _prgReg0;
|
||||
uint8_t _prgReg1;
|
||||
|
||||
uint8_t _prgMode;
|
||||
|
||||
uint8_t _mirroring;
|
||||
|
||||
uint8_t _hiCHRRegs[8];
|
||||
uint8_t _loCHRRegs[8];
|
||||
|
||||
uint8_t _game;
|
||||
uint8_t _currentChrBank;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_irq.reset(new VrcIrq(_console));
|
||||
|
||||
_prgMode = 0;
|
||||
|
||||
_prgReg0 = 0;
|
||||
_prgReg1 = 1;
|
||||
|
||||
_mirroring = 0;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
_loCHRRegs[i] = i;
|
||||
_hiCHRRegs[i] = 0;
|
||||
}
|
||||
|
||||
_currentChrBank = 0;
|
||||
_game = 0;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
if(!softReset || (_prgSize <= (512 * 1024))) {
|
||||
_game = 0;
|
||||
} else {
|
||||
_game = (_game + 1) & 0x01;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
_irq->ProcessCpuClock();
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
uint32_t base = (_game == 0) ? ((_hiCHRRegs[_currentChrBank] << 4) & 0x180) : 0x200;
|
||||
uint32_t mask = (_game == 0) ? 0x7F : 0x1FF;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
uint32_t page = _loCHRRegs[i] | (_hiCHRRegs[i] << 4);
|
||||
SelectCHRPage(i, base | (page & mask));
|
||||
}
|
||||
|
||||
base = (_game == 0) ? ((_hiCHRRegs[_currentChrBank] << 1) & 0x30) : 0x40;
|
||||
mask = 0x0F;
|
||||
|
||||
if(_prgMode == 0) {
|
||||
SelectPRGPage(0, base | (_prgReg0 & mask));
|
||||
SelectPRGPage(1, base | (_prgReg1 & mask));
|
||||
SelectPRGPage(2, base | ((-2) & mask));
|
||||
SelectPRGPage(3, base | ((-1) & mask));
|
||||
} else {
|
||||
SelectPRGPage(0, base | ((-2) & mask));
|
||||
SelectPRGPage(1, base | (_prgReg1 & mask));
|
||||
SelectPRGPage(2, base | (_prgReg0 & mask));
|
||||
SelectPRGPage(3, base | ((-1) & mask));
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
addr &= 0xF003;
|
||||
if(addr >= 0x8000 && addr <= 0x8003) {
|
||||
_prgReg0 = value;
|
||||
} else if(addr >= 0x9000 && addr <= 0x9003) {
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
case 1:
|
||||
switch(value & 0x03) {
|
||||
case 0: SetMirroringType(MirroringType::Vertical); break;
|
||||
case 1: SetMirroringType(MirroringType::Horizontal); break;
|
||||
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
|
||||
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
_prgMode = (value >> 1) & 0x01;
|
||||
break;
|
||||
}
|
||||
} else if(addr >= 0xA000 && addr <= 0xA003) {
|
||||
_prgReg1 = value;
|
||||
} else {
|
||||
if(addr >= 0xB000 && addr <= 0xE003) {
|
||||
uint8_t regNumber = ((((addr >> 12) & 0x07) - 3) << 1) + ((addr >> 1) & 0x01);
|
||||
bool lowBits = (addr & 0x01) == 0x00;
|
||||
if(lowBits) {
|
||||
//The other reg contains the low 4 bits
|
||||
_loCHRRegs[regNumber] = value & 0x0F;
|
||||
} else {
|
||||
//One reg contains the high 5 bits
|
||||
_hiCHRRegs[regNumber] = value & 0x1F;
|
||||
}
|
||||
} else if(addr == 0xF000) {
|
||||
_irq->SetReloadValueNibble(value, false);
|
||||
} else if(addr == 0xF001) {
|
||||
_irq->SetReloadValueNibble(value, true);
|
||||
} else if(addr == 0xF002) {
|
||||
_irq->SetControlValue(value);
|
||||
} else if(addr == 0xF003) {
|
||||
_irq->AcknowledgeIrq();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
ArrayInfo<uint8_t> loChrRegs = { _loCHRRegs, 8 };
|
||||
ArrayInfo<uint8_t> hiChrRegs = { _hiCHRRegs, 8 };
|
||||
SnapshotInfo irq{ _irq.get() };
|
||||
Stream(_prgReg0, _prgReg1, _prgMode, loChrRegs, hiChrRegs, irq, _game, _currentChrBank);
|
||||
}
|
||||
|
||||
uint8_t MapperReadVRAM(uint16_t addr, MemoryOperationType type) override
|
||||
{
|
||||
uint8_t bank = addr >> 10;
|
||||
if(!(addr & 0x2000) && (_game == 0) && (_currentChrBank != bank)) {
|
||||
_currentChrBank = bank;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
return BaseMapper::MapperReadVRAM(addr, type);
|
||||
}
|
||||
};
|
79
Core/Mapper368.h
Normal file
79
Core/Mapper368.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// YUNG-08
|
||||
|
||||
class Mapper368 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _latch;
|
||||
|
||||
bool _irqEnabled;
|
||||
uint16_t _irqCounter;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint16_t RegisterStartAddress() override { return 0x4022; }
|
||||
uint16_t RegisterEndAddress() override { return 0x4FFF; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_irqEnabled = false;
|
||||
_irqCounter = 0;
|
||||
_latch = 0;
|
||||
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0x02, PrgMemoryType::PrgRom);
|
||||
|
||||
SelectPRGPage(0, 1);
|
||||
SelectPRGPage(1, 0);
|
||||
SelectPRGPage(3, 8);
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_latch, _irqEnabled, _irqCounter);
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if(_irqEnabled) {
|
||||
_irqCounter++;
|
||||
if((_irqCounter & 0xFFF) == 0) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if((addr & 0x1FF) == 0x122) {
|
||||
return (0x8A | (_latch & 0x35));
|
||||
}
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0x1FF) {
|
||||
case 0x022:
|
||||
// bank order = { 4, 3, 5, 3, 6, 3, 7, 3 };
|
||||
SelectPRGPage(2, (value & 0x01) ? 0x03 : (0x04 | value >> 1));
|
||||
break;
|
||||
|
||||
case 0x122:
|
||||
_latch = value;
|
||||
_irqEnabled = (value & 0x01) != 0;
|
||||
if(!_irqEnabled) {
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
78
Core/Mapper375.h
Normal file
78
Core/Mapper375.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper375 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
uint16_t latchea;
|
||||
uint8_t latched;
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
virtual void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(latchea, latched);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
uint16_t prgBank = ((latchea >> 2) & 0x1F) | ((latchea >> 3) & 0x20) | ((latchea >> 4) & 0x40);
|
||||
bool sFlag = (latchea & 0x01) == 0x01;
|
||||
bool lFlag = ((latchea >> 9) & 0x01) == 0x01;
|
||||
bool prgMode = ((latchea >> 7) & 0x01) == 0x01;
|
||||
uint16_t p0 = prgBank;
|
||||
|
||||
if((latchea & 0x800) == 0x800)
|
||||
p0 = (prgBank & ~0x07) | (latched & 0x07);
|
||||
|
||||
if (prgMode) {
|
||||
if (sFlag) {
|
||||
SelectPrgPage2x(0, prgBank & 0xFE);
|
||||
} else {
|
||||
SelectPRGPage(0, p0);
|
||||
SelectPRGPage(1, prgBank);
|
||||
}
|
||||
} else {
|
||||
if (sFlag) {
|
||||
if (lFlag) {
|
||||
SelectPRGPage(0, p0 & 0x7E);
|
||||
SelectPRGPage(1, prgBank | 0x07);
|
||||
} else {
|
||||
SelectPRGPage(0, p0 & 0x7E);
|
||||
SelectPRGPage(1, prgBank & 0x78);
|
||||
}
|
||||
} else {
|
||||
if (lFlag) {
|
||||
SelectPRGPage(0, p0);
|
||||
SelectPRGPage(1, prgBank | 0x07);
|
||||
} else {
|
||||
SelectPRGPage(0, p0);
|
||||
SelectPRGPage(1, prgBank & 0x78);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// protect CHR-RAM on nrom modes
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (latchea & 0x80) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
|
||||
|
||||
SetMirroringType((latchea & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(latchea & 0x800) {
|
||||
latched = value;
|
||||
} else {
|
||||
latchea = addr;
|
||||
latched = value;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
63
Core/Mapper380.h
Normal file
63
Core/Mapper380.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper380 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint32_t GetDipSwitchCount() override { return 4; }
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
|
||||
bool _mFlag;
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softreset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_mFlag);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if(_mFlag && _romInfo.SubMapperID == 0) {
|
||||
addr |= GetDipSwitches();
|
||||
}
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
uint16_t prgBank = (addr >> 2) & 0x1F;
|
||||
bool sFlag = (addr & 0x01) == 0x01;
|
||||
bool prgMode = ((addr >> 9) & 0x01) == 0x01;
|
||||
|
||||
_mFlag = ((addr >> 8) & 0x01) == 0x01;
|
||||
|
||||
if (prgMode) {
|
||||
if (sFlag) {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank);
|
||||
} else {
|
||||
SelectPrgPage2x(0, prgBank & 0xFE);
|
||||
}
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank | 0x07 | (((_romInfo.SubMapperID == 1) && (addr & 0x100)) ? 0x08: 0x00));
|
||||
}
|
||||
|
||||
// protect CHR-RAM on nrom modes
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (addr & 0x80) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
|
||||
|
||||
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
68
Core/Mapper382.h
Normal file
68
Core/Mapper382.h
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper382 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _outerPrg;
|
||||
uint8_t _innerPrg;
|
||||
uint8_t _prgMode;
|
||||
uint8_t _mirroring;
|
||||
bool _locked;
|
||||
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
_outerPrg = 0;
|
||||
_innerPrg = 0;
|
||||
_prgMode = 0;
|
||||
_mirroring = 0;
|
||||
_locked = false;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_outerPrg, _innerPrg, _prgMode, _mirroring, _locked);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if (_prgMode) { // BNROM
|
||||
SelectPrgPage2x(0, ((_outerPrg << 2) | (_innerPrg & 0x03)) << 1);
|
||||
} else { // UNROM
|
||||
SelectPRGPage(0, (_outerPrg << 3) | (_innerPrg & 0x07));
|
||||
SelectPRGPage(1, (_outerPrg << 3) | 0x07);
|
||||
}
|
||||
|
||||
SetMirroringType(!_mirroring ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (!_locked) {
|
||||
_outerPrg = addr & 0x07;
|
||||
_prgMode = (addr & 0x08) == 0x08;
|
||||
_mirroring = (addr & 0x10) == 0x10;
|
||||
_locked = (addr & 0x20) == 0x20;
|
||||
}
|
||||
|
||||
_innerPrg = value & 0x07;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
};
|
25
Core/Mapper385.h
Normal file
25
Core/Mapper385.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper385 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPRGPage(0, 0);
|
||||
SelectPRGPage(1, 0);
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectPRGPage(0, (addr >> 1));
|
||||
SelectPRGPage(1, (addr >> 1));
|
||||
|
||||
SetMirroringType(addr & 0x01 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
58
Core/Mapper389.h
Normal file
58
Core/Mapper389.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper389 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[4];
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
memset(_regs, 0, sizeof(_regs));
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Reset(bool softreset) override
|
||||
{
|
||||
memset(_regs, 0, sizeof(_regs));
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1], _regs[2], _regs[3]);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if (_regs[1] & 0x02) {
|
||||
SelectPRGPage(0, (_regs[0] >> 2) | ((_regs[2] >> 2) & 0x03));
|
||||
SelectPRGPage(1, (_regs[0] >> 2) | 0x03);
|
||||
} else
|
||||
SelectPrgPage2x(0, _regs[0] >> 2);
|
||||
|
||||
SelectCHRPage(0, ((_regs[1] >> 1) & 0xFC) | (_regs[2] & 0x03));
|
||||
|
||||
SetMirroringType((_regs[0] & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (addr <= 0x8FFF) {
|
||||
_regs[0] = addr & 0xFF;
|
||||
} else if (addr <= 0x9FFF) {
|
||||
_regs[1] = addr & 0xFF;
|
||||
} else {
|
||||
_regs[2] = addr & 0xFF;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
};
|
163
Core/Mapper398.h
Normal file
163
Core/Mapper398.h
Normal file
@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "VrcIrq.h"
|
||||
#include "Console.h"
|
||||
|
||||
// VRC4 Clone with GNROM-like mode
|
||||
|
||||
class Mapper398 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
unique_ptr<VrcIrq> _irq;
|
||||
|
||||
uint8_t _prgReg0;
|
||||
uint8_t _prgReg1;
|
||||
|
||||
uint8_t _prgMode;
|
||||
|
||||
uint8_t _mirroring;
|
||||
|
||||
uint8_t _hiCHRRegs[8];
|
||||
uint8_t _loCHRRegs[8];
|
||||
|
||||
uint8_t _outerBank;
|
||||
uint8_t _currentChrBank;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_irq.reset(new VrcIrq(_console));
|
||||
|
||||
_prgMode = 0;
|
||||
|
||||
_prgReg0 = 0;
|
||||
_prgReg1 = 1;
|
||||
|
||||
_mirroring = 0;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
_loCHRRegs[i] = i;
|
||||
_hiCHRRegs[i] = 0;
|
||||
}
|
||||
|
||||
_currentChrBank = 0;
|
||||
_outerBank = 0xC0;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
_outerBank = 0xC0;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
_irq->ProcessCpuClock();
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if(_outerBank & 0x80) {
|
||||
uint8_t chrbank = ((_outerBank >> 3) & 0x08) | (_loCHRRegs[_currentChrBank] & 0x07);
|
||||
SelectChrPage8x(0, (0x40 | chrbank) << 3);
|
||||
} else {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
uint32_t page = _loCHRRegs[i] | (_hiCHRRegs[i] << 4);
|
||||
SelectCHRPage(i, page);
|
||||
}
|
||||
}
|
||||
|
||||
if(_outerBank & 0x80) {
|
||||
uint32_t prgBank = ((_outerBank >> 5) & 6) | ((_loCHRRegs[_currentChrBank] >> 2) & 0x01);
|
||||
SelectPrgPage4x(0, prgBank << 2);
|
||||
} else {
|
||||
if(_prgMode == 0) {
|
||||
SelectPRGPage(0, (_prgReg0 & 0x0F));
|
||||
SelectPRGPage(1, (_prgReg1 & 0x0F));
|
||||
SelectPRGPage(2, ((-2) & 0x0F));
|
||||
SelectPRGPage(3, ((-1) & 0x0F));
|
||||
} else {
|
||||
SelectPRGPage(0, ((-2) & 0x0F));
|
||||
SelectPRGPage(1, (_prgReg1 & 0x0F));
|
||||
SelectPRGPage(2, (_prgReg0 & 0x0F));
|
||||
SelectPRGPage(3, ((-1) & 0x0F));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_outerBank = addr & 0xFF;
|
||||
|
||||
addr &= 0xF003;
|
||||
if(addr >= 0x8000 && addr <= 0x8003) {
|
||||
_prgReg0 = value;
|
||||
} else if(addr >= 0x9000 && addr <= 0x9003) {
|
||||
switch(addr & 0x03) {
|
||||
case 0:
|
||||
case 1:
|
||||
switch(value & 0x03) {
|
||||
case 0: SetMirroringType(MirroringType::Vertical); break;
|
||||
case 1: SetMirroringType(MirroringType::Horizontal); break;
|
||||
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
|
||||
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
_prgMode = (value >> 1) & 0x01;
|
||||
break;
|
||||
}
|
||||
} else if(addr >= 0xA000 && addr <= 0xA003) {
|
||||
_prgReg1 = value;
|
||||
} else {
|
||||
if(addr >= 0xB000 && addr <= 0xE003) {
|
||||
uint8_t regNumber = ((((addr >> 12) & 0x07) - 3) << 1) + ((addr >> 1) & 0x01);
|
||||
bool lowBits = (addr & 0x01) == 0x00;
|
||||
if(lowBits) {
|
||||
//The other reg contains the low 4 bits
|
||||
_loCHRRegs[regNumber] = value & 0x0F;
|
||||
} else {
|
||||
//One reg contains the high 5 bits
|
||||
_hiCHRRegs[regNumber] = value & 0x1F;
|
||||
}
|
||||
} else if(addr == 0xF000) {
|
||||
_irq->SetReloadValueNibble(value, false);
|
||||
} else if(addr == 0xF001) {
|
||||
_irq->SetReloadValueNibble(value, true);
|
||||
} else if(addr == 0xF002) {
|
||||
_irq->SetControlValue(value);
|
||||
} else if(addr == 0xF003) {
|
||||
_irq->AcknowledgeIrq();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
ArrayInfo<uint8_t> loChrRegs = { _loCHRRegs, 8 };
|
||||
ArrayInfo<uint8_t> hiChrRegs = { _hiCHRRegs, 8 };
|
||||
SnapshotInfo irq{ _irq.get() };
|
||||
Stream(_prgReg0, _prgReg1, _prgMode, loChrRegs, hiChrRegs, irq, _outerBank, _currentChrBank);
|
||||
}
|
||||
|
||||
uint8_t MapperReadVRAM(uint16_t addr, MemoryOperationType type) override
|
||||
{
|
||||
uint8_t bank = addr >> 10;
|
||||
if(!(addr & 0x2000) && (_outerBank & 0x80) && (_currentChrBank != bank)) {
|
||||
_currentChrBank = bank;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
return BaseMapper::MapperReadVRAM(addr, type);
|
||||
}
|
||||
};
|
65
Core/Mapper400.h
Normal file
65
Core/Mapper400.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper400 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[2];
|
||||
uint8_t _led;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
memset(_regs, 0x00, sizeof(_regs));
|
||||
|
||||
_regs[0] = 0x80;
|
||||
|
||||
AddRegisterRange(0x7800, 0x7FFF, MemoryOperation::Write);
|
||||
|
||||
WriteRegister(0xC000, 0);
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
_regs[0] = 0x80;
|
||||
|
||||
WriteRegister(0xC000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1], _led);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
SelectPRGPage(0, (_regs[0] & 0xF8) | (_regs[1] & 0x07));
|
||||
SelectPRGPage(1, (_regs[0] & 0xF8) | 0x07);
|
||||
|
||||
SelectCHRPage(0, _regs[1] >> 5);
|
||||
|
||||
if(!(_regs[0] == 0x80)) {
|
||||
SetMirroringType((_regs[0] & 0x20) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
_regs[0] = value;
|
||||
UpdateState();
|
||||
} else {
|
||||
if(addr < 0xC000) {
|
||||
_led = value;
|
||||
} else {
|
||||
_regs[1] = value;
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
43
Core/Mapper402.h
Normal file
43
Core/Mapper402.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// J-2282 - 22-in-1 Olympic Games multicart
|
||||
|
||||
class Mapper402 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (addr & 0x0800)
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (((addr & 0x1F) << 1) | 0x03), PrgMemoryType::PrgRom);
|
||||
else {
|
||||
RemoveCpuMemoryMapping(0x6000, 0x7FFF);
|
||||
}
|
||||
|
||||
if (addr & 0x0040) {
|
||||
SelectPrgPage2x(0, (addr & 0x1F) << 1);
|
||||
SelectPrgPage2x(1, (addr & 0x1F) << 1);
|
||||
} else {
|
||||
SelectPrgPage4x(0, (addr & 0x1E) << 1);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, !(addr & 0x0400) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
|
||||
|
||||
SetMirroringType((addr & 0x80) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
75
Core/Mapper403.h
Normal file
75
Core/Mapper403.h
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// 89433
|
||||
|
||||
class Mapper403 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[4];
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
virtual bool AllowRegisterRead() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
memset(_regs, 0, sizeof(_regs));
|
||||
|
||||
AddRegisterRange(0x4100, 0x5FFF, MemoryOperation::Write);
|
||||
|
||||
// TODO: Hack for TetrisA from Tetris Family 19-in-1 NO 1683 to work.
|
||||
AddRegisterRange(0x6000, 0x7FFF, MemoryOperation::Read);
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
memset(_regs, 0, sizeof(_regs));
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1], _regs[2], _regs[3]);
|
||||
}
|
||||
|
||||
virtual void UpdateState()
|
||||
{
|
||||
if (_regs[2] & 0x01) {
|
||||
SelectPRGPage(0, (_regs[0] >> 1) & 0x3F);
|
||||
SelectPRGPage(1, (_regs[0] >> 1) & 0x3F);
|
||||
} else {
|
||||
SelectPrgPage2x(0, (_regs[0] >> 1) & 0x3E);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, _regs[1] & 0x03);
|
||||
|
||||
SetMirroringType((_regs[2] & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (addr < 0x8000) {
|
||||
if (addr & 0x0100) {
|
||||
_regs[(addr & 0x03)] = value;
|
||||
UpdateState();
|
||||
}
|
||||
} else {
|
||||
if (_regs[2] & 0x04) {
|
||||
_regs[1] = value;
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
33
Core/Mapper409.h
Normal file
33
Core/Mapper409.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// retroUSB DPCMcart
|
||||
|
||||
class Mapper409 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
RemoveRegisterRange(0x0000, 0xFFFF, MemoryOperation::Any);
|
||||
AddRegisterRange(0xC000, 0xCFFF, MemoryOperation::Write);
|
||||
|
||||
SelectPRGPage(1, -1);
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
WriteRegister(0xC000, 0);
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0xC000, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectPRGPage(0, addr & 0x0FFF);
|
||||
}
|
||||
};
|
140
Core/Mapper413.h
Normal file
140
Core/Mapper413.h
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
#include "A12Watcher.h"
|
||||
|
||||
class Mapper413 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint32_t _serialAddress;
|
||||
uint8_t _serialControl;
|
||||
|
||||
uint8_t _irqCounter;
|
||||
uint8_t _irqReloadValue;
|
||||
bool _irqEnabled;
|
||||
|
||||
A12Watcher _a12Watcher;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x1000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x1000; }
|
||||
virtual bool AllowRegisterRead() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_serialAddress = 0;
|
||||
_serialControl = 0;
|
||||
|
||||
_irqCounter = 0;
|
||||
_irqReloadValue = 0;
|
||||
_irqEnabled = false;
|
||||
|
||||
SetCpuMemoryMapping(0x5000, 0x5FFF, 1, PrgMemoryType::PrgRom);
|
||||
SetCpuMemoryMapping(0x6000, 0x6FFF, 0, PrgMemoryType::PrgRom);
|
||||
SetCpuMemoryMapping(0x7000, 0x7FFF, 1, PrgMemoryType::PrgRom);
|
||||
|
||||
SelectPrgPage2x(0, 0);
|
||||
SelectPrgPage2x(1, 0);
|
||||
SelectPRGPage(5, 0x07);
|
||||
SelectPRGPage(6, 0x08);
|
||||
SelectPRGPage(7, 0x09);
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
SelectCHRPage(1, -3);
|
||||
|
||||
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Read);
|
||||
RemoveRegisterRange(0x8000, 0xBFFF, MemoryOperation::Read);
|
||||
RemoveRegisterRange(0xD000, 0xFFFF, MemoryOperation::Read);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_serialAddress, _serialControl,
|
||||
_irqCounter, _irqReloadValue, _irqEnabled);
|
||||
}
|
||||
|
||||
void NotifyVRAMAddressChange(uint16_t addr) override
|
||||
{
|
||||
switch(_a12Watcher.UpdateVramAddress(addr, _console->GetPpu()->GetFrameCycle())) {
|
||||
case A12StateChange::None:
|
||||
case A12StateChange::Fall:
|
||||
break;
|
||||
|
||||
case A12StateChange::Rise:
|
||||
if(_irqCounter == 0) {
|
||||
_irqCounter = _irqReloadValue;
|
||||
} else {
|
||||
_irqCounter--;
|
||||
}
|
||||
if (_irqCounter == 0 && _irqEnabled) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if(_serialControl & 0x02) {
|
||||
return (_miscRom[_serialAddress++ & (_miscRomSize - 1)]);
|
||||
} else {
|
||||
return (_miscRom[_serialAddress & (_miscRomSize - 1)]);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch (addr & 0xF000) {
|
||||
case 0x8000:
|
||||
_irqReloadValue = value;
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
_irqCounter = 0;
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
case 0xB000:
|
||||
_irqEnabled = (addr & 0x1000) != 0;
|
||||
if (_irqEnabled == 0) {
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
_serialAddress = (_serialAddress << 1) | (value >> 7);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
_serialControl = value;
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
case 0xF000:
|
||||
switch(value >> 6) {
|
||||
case 0:
|
||||
value <<= 1;
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, value, PrgMemoryType::PrgRom);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value <<= 1;
|
||||
SelectPRGPage(0, value);
|
||||
SelectPRGPage(1, value + 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value <<= 1;
|
||||
SelectPRGPage(2, value);
|
||||
SelectPRGPage(3, value + 1);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
SelectCHRPage(0, value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
63
Core/Mapper414.h
Normal file
63
Core/Mapper414.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
|
||||
class Mapper414 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
const uint16_t _dpswlut[5] = { 0x0010, 0x0000, 0x0030, 0x0070, 0x00F0 };
|
||||
uint16_t _dipSwitch;
|
||||
uint16_t _latch;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
virtual bool AllowRegisterRead() override { return true; }
|
||||
virtual bool HasBusConflicts() override { return true; }
|
||||
virtual uint32_t GetDipSwitchCount() override { return 3; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
uint32_t index = GetDipSwitches();
|
||||
if (index > 4)
|
||||
index = 0;
|
||||
_dipSwitch = _dpswlut[index];
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_latch);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if (addr >= 0xC000) {
|
||||
if (!(_latch & 0x100) && (_latch & _dipSwitch)) {
|
||||
return _console->GetMemoryManager()->GetOpenBus();
|
||||
}
|
||||
}
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_latch = addr;
|
||||
|
||||
if (_latch & 0x2000)
|
||||
SelectPrgPage2x(0, (_latch >> 1) & 0xFE);
|
||||
else {
|
||||
SelectPRGPage(0, _latch >> 1);
|
||||
SelectPRGPage(1, _latch >> 1);
|
||||
}
|
||||
SelectCHRPage(0, value & 0x03);
|
||||
SetMirroringType((_latch & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
29
Core/Mapper415.h
Normal file
29
Core/Mapper415.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
|
||||
// FDS Conversion of Roger Rabbit to Lucky Rabbit
|
||||
|
||||
class Mapper415 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPRGPage(0, -4);
|
||||
SelectPRGPage(1, -3);
|
||||
SelectPRGPage(2, -2);
|
||||
SelectPRGPage(3, -1);
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, value & 0x0F, PrgMemoryType::PrgRom);
|
||||
SetMirroringType((value & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
98
Core/Mapper416.h
Normal file
98
Core/Mapper416.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
|
||||
// N-32 cartridge conversion 4-in-1
|
||||
|
||||
class Mapper416 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[2];
|
||||
uint16_t _irqCounter;
|
||||
bool _irqEnabled;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
AddRegisterRange(0x4000, 0x5FFF, MemoryOperation::Write);
|
||||
RemoveRegisterRange(0xA000, 0xFFFF, MemoryOperation::Write);
|
||||
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0x07, PrgMemoryType::PrgRom);
|
||||
|
||||
_regs[0] = _regs[1] = 0;
|
||||
_irqEnabled = false;
|
||||
_irqCounter = 0;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
virtual void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1], _irqEnabled, _irqCounter);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if (_regs[1] & 0x08) {
|
||||
uint8_t page = ((_regs[1] >> 1) & 0x04) | ((_regs[1] >> 6) & 0x02) | ((_regs[1] >> 5) & 0x01);
|
||||
page <<= 1;
|
||||
if (_regs[1] & 0x80) {
|
||||
SelectPrgPage4x(0, page & 0xFE);
|
||||
} else {
|
||||
if (_regs[1] & 0x40) {
|
||||
SelectPrgPage2x(0, page);
|
||||
SelectPrgPage2x(1, page);
|
||||
} else {
|
||||
SelectPRGPage(0, page);
|
||||
SelectPRGPage(1, page);
|
||||
SelectPRGPage(2, page);
|
||||
SelectPRGPage(3, page);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SelectPRGPage(0, 0x00);
|
||||
SelectPRGPage(1, 0x01);
|
||||
SelectPRGPage(2, (_regs[0] & 0x08) | ((_regs[0] << 2) & 0x04) | ((_regs[0] >> 1) & 0x03));
|
||||
SelectPRGPage(3, 0x03);
|
||||
}
|
||||
|
||||
SelectCHRPage(0x0, _regs[1] >> 1 & 0x03);
|
||||
|
||||
SetMirroringType((_regs[1] & 0x04) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (addr < 0x8000) { // 0x4000-0x5FFF
|
||||
if (addr & 0x20) {
|
||||
if (addr & 0x100) {
|
||||
_irqEnabled = (value & 0x01) == 0x01;
|
||||
} else {
|
||||
_regs[0] = value;
|
||||
}
|
||||
}
|
||||
} else { // 0x8000 - 0x9FFF
|
||||
_regs[1] = value;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
if (_irqEnabled) {
|
||||
_irqCounter++;
|
||||
if (_irqCounter & 0x1000) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
} else {
|
||||
_irqCounter = 0;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
};
|
71
Core/Mapper417.h
Normal file
71
Core/Mapper417.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// Bootleg copy of Batman: The Video Game
|
||||
|
||||
class Mapper417 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint16_t _irqCounter;
|
||||
bool _irqEnabled;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_irqEnabled = false;
|
||||
_irqCounter = 0;
|
||||
|
||||
SelectPRGPage(3, -1);
|
||||
}
|
||||
|
||||
virtual void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_irqEnabled, _irqCounter);
|
||||
}
|
||||
|
||||
void ProcessCpuClock() override
|
||||
{
|
||||
_irqCounter++;
|
||||
if (_irqCounter & 0x400) {
|
||||
if (_irqEnabled) {
|
||||
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch ((addr >> 4) & 0x07) {
|
||||
case 0:
|
||||
SelectPRGPage(addr & 0x03, value);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
SelectCHRPage(addr & 0x03, value);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
SelectCHRPage((addr & 0x03) | 0x04, value);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_irqEnabled = true;
|
||||
_irqCounter = 0;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
_irqEnabled = false;
|
||||
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
SetNametable(addr & 0x03, value & 0x01);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
74
Core/Mapper428.h
Normal file
74
Core/Mapper428.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper428 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[4];
|
||||
uint8_t _chrLatch;
|
||||
|
||||
protected:
|
||||
uint32_t GetDipSwitchCount() override { return 2; }
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
uint16_t RegisterStartAddress() override { return 0x6000; }
|
||||
uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
|
||||
|
||||
_chrLatch = 0;
|
||||
memset(_regs, 0, sizeof(_regs));
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1], _regs[2], _regs[3], _chrLatch);
|
||||
|
||||
if(!saving) {
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
BaseMapper::Reset(softReset);
|
||||
|
||||
_chrLatch = 0;
|
||||
memset(_regs, 0, sizeof(_regs));
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if (_regs[1] & 0x10) {
|
||||
SelectPrgPage2x(0, (_regs[1] >> 5) & 0xFE);
|
||||
} else {
|
||||
SelectPRGPage(0, _regs[1] >> 5);
|
||||
SelectPRGPage(1, _regs[1] >> 5);
|
||||
}
|
||||
SelectCHRPage(0, ((_regs[1] & 0x07) & ~(_regs[2] >> 6)) | (_chrLatch & (_regs[2] >> 6)));
|
||||
SetMirroringType((_regs[1] & 0x08) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
return (_console->GetMemoryManager()->GetOpenBus() & 0xFC) | (GetDipSwitches() & 0x03);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
_regs[addr & 0x03] = value;
|
||||
} else {
|
||||
_chrLatch = value;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
26
Core/Mapper429.h
Normal file
26
Core/Mapper429.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper429 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0x04);
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0x04);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectPRGPage(0, value >> 2);
|
||||
SelectCHRPage(0, value & 0x03);
|
||||
}
|
||||
};
|
51
Core/Mapper431.h
Normal file
51
Core/Mapper431.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// Realtec GN-91B multicart.
|
||||
|
||||
class Mapper431 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[2];
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_regs[0] = _regs[1] = 0;
|
||||
SelectCHRPage(0, 0);
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
_regs[0] = _regs[1] = 0;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_regs[0], _regs[1]);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
SelectPRGPage(0, ((_regs[0] >> 2) & 0x08) | (_regs[1] & 0x07));
|
||||
SelectPRGPage(1, ((_regs[0] >> 2) & 0x08) | 0x07);
|
||||
SetMirroringType((_regs[0] & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (addr < 0xC000) {
|
||||
_regs[0] = value;
|
||||
} else {
|
||||
_regs[1] = value;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
35
Core/Mapper433.h
Normal file
35
Core/Mapper433.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// NC-20MB, 20-in-1 (CA-006) multicart.
|
||||
|
||||
class Mapper433 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
virtual void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectCHRPage(0, 0);
|
||||
if(value & 0x20) {
|
||||
SelectPRGPage(0, value & 0x1F);
|
||||
SelectPRGPage(1, value & 0x1F);
|
||||
} else {
|
||||
SelectPRGPage(0, value & 0x1E);
|
||||
SelectPRGPage(1, (value & 0x1E) + 1);
|
||||
}
|
||||
SetMirroringType(value & 0x40 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
47
Core/Mapper437.h
Normal file
47
Core/Mapper437.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// NTDEC TH2348 8-in-1 multicart
|
||||
|
||||
class Mapper437 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _regs[2];
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
|
||||
|
||||
WriteRegister(0x5000, 0);
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0x5000, 0);
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
SelectCHRPage(0, 0);
|
||||
SelectPRGPage(0, _regs[0] << 3 | (_regs[1] & 0x07));
|
||||
SelectPRGPage(1, _regs[0] << 3 | 0x07);
|
||||
SetMirroringType((_regs[0] & 0x08) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(addr < 0x8000) {
|
||||
_regs[0] = addr & 0x0F;
|
||||
} else {
|
||||
_regs[1] = value;
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
};
|
36
Core/Mapper438.h
Normal file
36
Core/Mapper438.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// K-3071
|
||||
|
||||
class Mapper438 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SetMirroringType((value & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
|
||||
if (addr & 0x01) {
|
||||
SelectPrgPage2x(0, (addr >> 1) & 0xFE);
|
||||
} else {
|
||||
SelectPRGPage(0, addr >> 1);
|
||||
SelectPRGPage(1, addr >> 1);
|
||||
}
|
||||
|
||||
SelectCHRPage(0, value >> 1);
|
||||
}
|
||||
};
|
65
Core/Mapper449.h
Normal file
65
Core/Mapper449.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper449 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint32_t GetDipSwitchCount() override { return 4; }
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
|
||||
bool _mFlag;
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softreset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_mFlag);
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
if(_mFlag) {
|
||||
addr |= GetDipSwitches();
|
||||
}
|
||||
return InternalReadRam(addr);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
uint16_t prgBank = ((addr >> 2) & 0x1F) | ((addr >> 3) & 0x20);
|
||||
bool sFlag = (addr & 0x01) == 0x01;
|
||||
bool prgMode = ((addr >> 7) & 0x01) == 0x01;
|
||||
|
||||
_mFlag = ((addr >> 9) & 0x01) == 0x01;
|
||||
|
||||
if(prgMode) {
|
||||
if(sFlag) {
|
||||
SelectPrgPage2x(0, prgBank & 0xFE);
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank);
|
||||
}
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank | 0x07);
|
||||
}
|
||||
|
||||
// protect CHR-RAM on nrom modes
|
||||
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (!HasBattery() && prgMode) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
|
||||
|
||||
SelectCHRPage(0, value);
|
||||
|
||||
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
};
|
59
Core/Mapper452.h
Normal file
59
Core/Mapper452.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper452 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
uint32_t GetWorkRamSize() override { return 0x2000; }
|
||||
uint32_t GetWorkRamPageSize() override { return 0x2000; }
|
||||
bool ForceWorkRamSize() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
// https://www.nesdev.org/wiki/NES_2.0_Mapper_452
|
||||
// $E000 - $FFFF does not respond to latch to allow writing to
|
||||
// PRG RAM mapped to that range
|
||||
RemoveRegisterRange(0xE000, 0xFFFF, MemoryOperation::Any);
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
uint16_t wramBank = 0x8000 | ((value << 9) & 0x6000);
|
||||
uint8_t prgPage = addr >> 1;
|
||||
|
||||
if(value & 0x02) {
|
||||
SelectPRGPage(0, prgPage);
|
||||
SelectPRGPage(1, prgPage);
|
||||
SelectPRGPage(2, prgPage);
|
||||
SelectPRGPage(3, prgPage);
|
||||
SetCpuMemoryMapping((wramBank ^ 0x4000), (wramBank ^ 0x4000) + 0x1FFF, 0, PrgMemoryType::WorkRam);
|
||||
} else {
|
||||
if (value & 0x08) {
|
||||
SelectPRGPage(0, prgPage | 0);
|
||||
SelectPRGPage(1, prgPage | 1);
|
||||
SelectPRGPage(2, prgPage | 2);
|
||||
SelectPRGPage(3, prgPage | 3 | (value & 0x04));
|
||||
} else {
|
||||
SelectPrgPage2x(0, prgPage & 0xFE);
|
||||
SelectPrgPage2x(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
SetMirroringType((value & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
|
||||
SetCpuMemoryMapping(wramBank, wramBank + 0x1FFF, 0, PrgMemoryType::WorkRam);
|
||||
}
|
||||
};
|
47
Core/Mapper453.h
Normal file
47
Core/Mapper453.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
// Realtec 8042
|
||||
|
||||
class Mapper453 : public BaseMapper
|
||||
{
|
||||
private:
|
||||
uint8_t _reg;
|
||||
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
_reg = 0;
|
||||
WriteRegister(0x8000, 0);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_reg);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if (_reg & 0xE0) {
|
||||
_reg &= 0xE0;
|
||||
_reg |= value & ~0xE0;
|
||||
} else {
|
||||
_reg = value;
|
||||
}
|
||||
|
||||
SelectCHRPage(0, 0);
|
||||
if (_reg & 0x40) {
|
||||
SelectPrgPage2x(0, (((_reg >> 3) & 0x18) | (_reg & 0x07)) << 1);
|
||||
SetMirroringType((_reg & 0x10) ? MirroringType::ScreenAOnly : MirroringType::ScreenBOnly);
|
||||
} else {
|
||||
SelectPRGPage(0, ((_reg >> 2) & 0x38) | (_reg & 0x07));
|
||||
SelectPRGPage(1, ((_reg >> 2) & 0x38) | 0x07);
|
||||
SetMirroringType((_reg & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
|
||||
}
|
||||
}
|
||||
};
|
23
Core/Mapper81.h
Normal file
23
Core/Mapper81.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
class Mapper81 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPRGPage(0, 0);
|
||||
SelectPRGPage(1, -1);
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectPRGPage(0, addr >> 2);
|
||||
SelectCHRPage(0, addr & 0x03);
|
||||
}
|
||||
};
|
@ -6,39 +6,100 @@ class Mapper91 : public MMC3
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t RegisterStartAddress() override { return 0x6000; }
|
||||
virtual uint16_t RegisterEndAddress() override { return 0x7FFF; }
|
||||
virtual uint16_t RegisterEndAddress() override { return 0x9FFF; }
|
||||
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x800; }
|
||||
|
||||
uint8_t _prgBanks[2];
|
||||
uint8_t _chrBanks[4];
|
||||
uint8_t _outerBank;
|
||||
|
||||
// for Submapper 1
|
||||
bool _verticalMirroring;
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPRGPage(2, -2);
|
||||
SelectPRGPage(3, -1);
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void UpdateState() override
|
||||
{
|
||||
//Do nothing, we are only using MMC3 code to emulate the IRQs
|
||||
SelectPRGPage(0, ((_outerBank << 3) & 0x30) | (_prgBanks[0] & 0x0F));
|
||||
SelectPRGPage(1, ((_outerBank << 3) & 0x30) | (_prgBanks[1] & 0x0F));
|
||||
SelectPRGPage(2, ((_outerBank << 3) & 0x30) | 0xE);
|
||||
SelectPRGPage(3, ((_outerBank << 3) & 0x30) | 0xF);
|
||||
|
||||
SelectCHRPage(0, ((_outerBank << 8) & 0x100) | _chrBanks[0]);
|
||||
SelectCHRPage(1, ((_outerBank << 8) & 0x100) | _chrBanks[1]);
|
||||
SelectCHRPage(2, ((_outerBank << 8) & 0x100) | _chrBanks[2]);
|
||||
SelectCHRPage(3, ((_outerBank << 8) & 0x100) | _chrBanks[3]);
|
||||
|
||||
if(_romInfo.SubMapperID == 1) {
|
||||
SetMirroringType(_verticalMirroring ? MirroringType::Vertical : MirroringType::Horizontal);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
switch(addr & 0x7003) {
|
||||
case 0x6000: SelectCHRPage(0, value); break;
|
||||
case 0x6001: SelectCHRPage(1, value); break;
|
||||
case 0x6002: SelectCHRPage(2, value); break;
|
||||
case 0x6003: SelectCHRPage(3, value); break;
|
||||
case 0x7000: SelectPRGPage(0, value & 0x0F); break;
|
||||
case 0x7001: SelectPRGPage(1, value & 0x0F); break;
|
||||
case 0x7002:
|
||||
MMC3::WriteRegister(0xE000, value);
|
||||
break;
|
||||
case 0x7003:
|
||||
MMC3::WriteRegister(0xC000, 0x07);
|
||||
MMC3::WriteRegister(0xC001, value);
|
||||
MMC3::WriteRegister(0xE001, value);
|
||||
break;
|
||||
if(addr <= 0x6FFF) {
|
||||
if(_romInfo.SubMapperID == 1) {
|
||||
switch(addr & 0x07) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
_chrBanks[addr & 0x03] = value;
|
||||
UpdateState();
|
||||
return;
|
||||
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
_verticalMirroring = (addr & 0x01) == 0x01;
|
||||
UpdateState();
|
||||
return;
|
||||
|
||||
case 0x06:
|
||||
return;
|
||||
|
||||
case 0x07:
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_chrBanks[addr & 0x03] = value;
|
||||
UpdateState();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(addr <= 0x7FFF) {
|
||||
switch(addr & 0x03) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
_prgBanks[addr & 0x01] = value;
|
||||
UpdateState();
|
||||
return;
|
||||
|
||||
case 0x02:
|
||||
MMC3::WriteRegister(0xE000, value);
|
||||
return;
|
||||
|
||||
case 0x03:
|
||||
MMC3::WriteRegister(0xC000, 0x07);
|
||||
MMC3::WriteRegister(0xC001, value);
|
||||
MMC3::WriteRegister(0xE001, value);
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_outerBank = (uint8_t)addr;
|
||||
UpdateState();
|
||||
}
|
||||
};
|
@ -34,6 +34,8 @@ void iNesLoader::LoadRom(RomData& romData, vector<uint8_t>& romFile, NESHeader *
|
||||
romData.Info.VsPpuModel = header.GetVsSystemPpuModel();
|
||||
romData.Info.InputType = header.GetInputType();
|
||||
romData.Info.HasTrainer = header.HasTrainer();
|
||||
romData.Info.MiscRoms = header.GetMiscRoms();
|
||||
|
||||
romData.Info.NesHeader = header;
|
||||
|
||||
romData.ChrRamSize = header.GetChrRamSize();
|
||||
@ -81,6 +83,12 @@ void iNesLoader::LoadRom(RomData& romData, vector<uint8_t>& romFile, NESHeader *
|
||||
romData.PrgRom.insert(romData.PrgRom.end(), buffer, buffer + prgSize);
|
||||
buffer += prgSize;
|
||||
romData.ChrRom.insert(romData.ChrRom.end(), buffer, buffer + chrSize);
|
||||
buffer += chrSize;
|
||||
|
||||
if(romData.Info.MiscRoms) {
|
||||
// Misc roms occupies the remaining bytes
|
||||
romData.MiscRomsData.insert(romData.MiscRomsData.end(), buffer, buffer + (dataSize - (prgSize + chrSize)));
|
||||
}
|
||||
|
||||
romData.Info.Hash.PrgCrc32 = CRC32::GetCRC(romData.PrgRom.data(), romData.PrgRom.size());
|
||||
|
||||
@ -125,6 +133,9 @@ void iNesLoader::LoadRom(RomData& romData, vector<uint8_t>& romFile, NESHeader *
|
||||
if(romData.Info.HasTrainer) {
|
||||
Log("[iNes] Trainer: Yes");
|
||||
}
|
||||
if(romData.MiscRomsData.size() > 0) {
|
||||
Log("[iNes] Misc ROMS: " + std::to_string((romData.MiscRomsData.size() / 1024)) + " KB");
|
||||
}
|
||||
|
||||
if(!_checkOnly) {
|
||||
GameDatabase::SetGameInfo(romData.Info.Hash.PrgChrCrc32, romData, GameDatabase::IsEnabled(), preloadedHeader != nullptr);
|
||||
|
Loading…
Reference in New Issue
Block a user