diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 26c19d0d..a65aa76e 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -19,6 +19,8 @@
+
+
@@ -456,6 +458,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index e415dab3..3d858970 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -1817,6 +1817,12 @@
PCE
+
+ PCE
+
+
+ PCE
+
@@ -1898,6 +1904,9 @@
PCE
+
+ PCE
+
diff --git a/Core/Debugger/DebugUtilities.h b/Core/Debugger/DebugUtilities.h
index f455d7d1..580cd38a 100644
--- a/Core/Debugger/DebugUtilities.h
+++ b/Core/Debugger/DebugUtilities.h
@@ -109,6 +109,7 @@ public:
case MemoryType::PceCdromRam:
case MemoryType::PceCardRam:
case MemoryType::PceAdpcmRam:
+ case MemoryType::PceArcadeCardRam:
case MemoryType::PceVideoRam:
case MemoryType::PceVideoRamVdc2:
case MemoryType::PcePaletteRam:
diff --git a/Core/MemoryType.h b/Core/MemoryType.h
index 748768e6..e7502bb9 100644
--- a/Core/MemoryType.h
+++ b/Core/MemoryType.h
@@ -55,6 +55,7 @@ enum class MemoryType
PceCdromRam,
PceCardRam,
PceAdpcmRam,
+ PceArcadeCardRam,
PceVideoRam,
PceVideoRamVdc2,
PceSpriteRam,
diff --git a/Core/PCE/IPceMapper.h b/Core/PCE/IPceMapper.h
new file mode 100644
index 00000000..469019f6
--- /dev/null
+++ b/Core/PCE/IPceMapper.h
@@ -0,0 +1,9 @@
+#pragma once
+#include "stdafx.h"
+
+class IPceMapper
+{
+public:
+ virtual uint8_t Read(uint8_t bank, uint16_t addr, uint8_t value) { return value; }
+ virtual void Write(uint8_t bank, uint16_t addr, uint8_t value) {}
+};
diff --git a/Core/PCE/PceArcadeCard.cpp b/Core/PCE/PceArcadeCard.cpp
new file mode 100644
index 00000000..3b1fe8cd
--- /dev/null
+++ b/Core/PCE/PceArcadeCard.cpp
@@ -0,0 +1,208 @@
+#include "stdafx.h"
+#include "PCE/PceArcadeCard.h"
+#include "Shared/EmuSettings.h"
+#include "Shared/Emulator.h"
+
+PceArcadeCard::PceArcadeCard(Emulator* emu)
+{
+ _state = {};
+ _ram = new uint8_t[PceArcadeCard::ArcadeRamMemSize];
+ emu->GetSettings()->InitializeRam(_ram, PceArcadeCard::ArcadeRamMemSize);
+ emu->RegisterMemory(MemoryType::PceArcadeCardRam, _ram, PceArcadeCard::ArcadeRamMemSize);
+}
+
+PceArcadeCard::~PceArcadeCard()
+{
+ delete[] _ram;
+}
+
+uint32_t PceArcadeCard::GetAddress(PceArcadeCardPortConfig& port)
+{
+ uint32_t addr = port.BaseAddress;
+ if(port.AddOffset) {
+ addr += port.Offset;
+ if(port.SignedOffset) {
+ addr += 0xFF0000;
+ }
+ }
+ return addr & 0x1FFFFF;
+}
+
+void PceArcadeCard::ProcessAutoInc(PceArcadeCardPortConfig& port)
+{
+ if(port.AutoIncrement) {
+ if(port.AddIncrementToBase) {
+ port.BaseAddress = (port.BaseAddress + port.IncValue) & 0xFFFFFF;
+ } else {
+ port.Offset += port.IncValue;
+ }
+ }
+}
+
+void PceArcadeCard::AddOffsetToBase(PceArcadeCardPortConfig& port)
+{
+ uint32_t addr = port.BaseAddress + port.Offset;
+ if(port.SignedOffset) {
+ addr += 0xFF0000;
+ }
+ port.BaseAddress = addr & 0xFFFFFF;
+}
+
+uint8_t PceArcadeCard::ReadPortValue(uint8_t portNumber)
+{
+ PceArcadeCardPortConfig& port = _state.Port[portNumber];
+ uint32_t addr = GetAddress(port);
+ ProcessAutoInc(port);
+ return _ram[addr];
+}
+
+void PceArcadeCard::WritePortValue(uint8_t portNumber, uint8_t value)
+{
+ PceArcadeCardPortConfig& port = _state.Port[portNumber];
+ uint32_t addr = GetAddress(port);
+ ProcessAutoInc(port);
+ _ram[addr] = value;
+}
+
+uint8_t PceArcadeCard::ReadPortRegister(uint8_t portNumber, uint8_t reg)
+{
+ PceArcadeCardPortConfig& port = _state.Port[portNumber];
+ switch(reg) {
+ case 0x00:
+ case 0x01:
+ return ReadPortValue(portNumber);
+
+ case 0x02: return port.BaseAddress & 0xFF;
+ case 0x03: return (port.BaseAddress >> 8) & 0xFF;
+ case 0x04: return (port.BaseAddress >> 16) & 0xFF;
+ case 0x05: return port.Offset & 0xFF;
+ case 0x06: return (port.Offset >> 8) & 0xFF;
+ case 0x07: return port.IncValue & 0xFF;
+ case 0x08: return (port.IncValue >> 8) & 0xFF;
+ case 0x09: return port.Control;
+ case 0x0A: return 0;
+ default: return 0xFF;
+ }
+}
+
+void PceArcadeCard::WritePortRegister(uint8_t portNumber, uint8_t reg, uint8_t value)
+{
+ PceArcadeCardPortConfig& port = _state.Port[portNumber];
+ switch(reg) {
+ case 0x00:
+ case 0x01:
+ WritePortValue(portNumber, value);
+ break;
+
+ case 0x02: port.BaseAddress = (port.BaseAddress & 0xFFFF00) | value; break;
+ case 0x03: port.BaseAddress = (port.BaseAddress & 0xFF00FF) | (value << 8); break;
+ case 0x04: port.BaseAddress = (port.BaseAddress & 0x00FFFF) | (value << 16); break;
+
+ case 0x05:
+ port.Offset = (port.Offset & 0xFF00) | value;
+ if(port.AddOffsetTrigger == PceArcadePortOffsetTrigger::AddOnLowWrite) {
+ AddOffsetToBase(port);
+ }
+ break;
+
+ case 0x06:
+ port.Offset = (port.Offset & 0x00FF) | (value << 8);
+ if(port.AddOffsetTrigger == PceArcadePortOffsetTrigger::AddOnHighWrite) {
+ AddOffsetToBase(port);
+ }
+ break;
+
+ case 0x07: port.IncValue = (port.IncValue & 0xFF00) | value; break;
+ case 0x08: port.IncValue = (port.IncValue & 0x00FF) | (value << 8); break;
+
+ case 0x09:
+ port.Control = value & 0x7F; //bit 7 is unused?
+ port.AutoIncrement = (value & 0x01) != 0;
+ port.AddOffset = (value & 0x02) != 0;
+ port.SignedIncrement = (value & 0x04) != 0; //not used?
+ port.SignedOffset = (value & 0x08) != 0;
+ port.AddIncrementToBase = (value & 0x10) != 0;
+ port.AddOffsetTrigger = (PceArcadePortOffsetTrigger)((value & 0x60) >> 5);
+ break;
+
+ case 0x0A:
+ if(port.AddOffsetTrigger == PceArcadePortOffsetTrigger::AddOnReg0AWrite) {
+ AddOffsetToBase(port);
+ }
+ break;
+ }
+}
+
+uint8_t PceArcadeCard::Read(uint8_t bank, uint16_t addr, uint8_t value)
+{
+ if(bank == 0xFF) {
+ addr &= 0x1FFF;
+
+ if(addr >= 0x1A00 && addr <= 0x1A3F) {
+ return ReadPortRegister((addr & 0x30) >> 4, addr & 0x0F);
+ } else {
+ switch(addr) {
+ case 0x1AE0: return _state.ValueReg & 0xFF;
+ case 0x1AE1: return (_state.ValueReg >> 8) & 0xFF;
+ case 0x1AE2: return (_state.ValueReg >> 16) & 0xFF;
+ case 0x1AE3: return (_state.ValueReg >> 24) & 0xFF;
+
+ case 0x1AE4: return _state.ShiftReg;
+ case 0x1AE5: return _state.RotateReg;
+
+ //Arcade card version+signature
+ case 0x1AFE: return 0x10;
+ case 0x1AFF: return 0x51;
+ }
+ }
+ } else if(bank >= 0x40 && bank <= 0x43) {
+ return ReadPortValue(bank & 0x03);
+ }
+
+ return value;
+}
+
+void PceArcadeCard::Write(uint8_t bank, uint16_t addr, uint8_t value)
+{
+ if(bank == 0xFF) {
+ addr &= 0x1FFF;
+
+ if(addr >= 0x1A00 && addr <= 0x1A3F) {
+ WritePortRegister((addr & 0x30) >> 4, addr & 0x0F, value);
+ } else {
+ switch(addr) {
+ case 0x1AE0: _state.ValueReg = (_state.ValueReg & 0xFFFFFF00) | value; break;
+ case 0x1AE1: _state.ValueReg = (_state.ValueReg & 0xFFFF00FF) | (value << 8); break;
+ case 0x1AE2: _state.ValueReg = (_state.ValueReg & 0xFF00FFFF) | (value << 16); break;
+ case 0x1AE3: _state.ValueReg = (_state.ValueReg & 0x00FFFFFF) | (value << 24); break;
+
+ case 0x1AE4:
+ _state.ShiftReg = value;
+ if(value) {
+ if(value & 0x08) {
+ _state.ValueReg >>= ~(value & 0x07) + 1;
+ } else {
+ _state.ValueReg <<= (value & 0x07);
+ }
+ }
+ break;
+
+ case 0x1AE5:
+ //untested
+ _state.RotateReg = value;
+ if(value) {
+ if(value & 0x08) {
+ uint8_t rotateRight = ~(value & 0x07) + 1;
+ _state.ValueReg = (_state.ValueReg >> rotateRight) | (_state.ValueReg << (32 - rotateRight));
+ } else {
+ uint8_t rotateLeft = (value & 0x07);
+ _state.ValueReg = (_state.ValueReg << rotateLeft) | (_state.ValueReg >> (32 - rotateLeft));
+ }
+ }
+ break;
+ }
+ }
+ } else if(bank >= 0x40 && bank <= 0x43) {
+ WritePortValue(bank & 0x03, value);
+ }
+}
diff --git a/Core/PCE/PceArcadeCard.h b/Core/PCE/PceArcadeCard.h
new file mode 100644
index 00000000..76230c54
--- /dev/null
+++ b/Core/PCE/PceArcadeCard.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "stdafx.h"
+#include "PCE/IPceMapper.h"
+#include "PCE/PceTypes.h"
+
+class Emulator;
+
+class PceArcadeCard final : public IPceMapper
+{
+private:
+ static constexpr int ArcadeRamMemSize = 0x200000;
+
+ PceArcadeCardState _state;
+ uint8_t* _ram = nullptr;
+
+ uint32_t GetAddress(PceArcadeCardPortConfig& port);
+ void ProcessAutoInc(PceArcadeCardPortConfig& port);
+ void AddOffsetToBase(PceArcadeCardPortConfig& port);
+
+ uint8_t ReadPortValue(uint8_t portNumber);
+ void WritePortValue(uint8_t portNumber, uint8_t value);
+
+ uint8_t ReadPortRegister(uint8_t portNumber, uint8_t reg);
+ void WritePortRegister(uint8_t portNumber, uint8_t reg, uint8_t value);
+
+public:
+ PceArcadeCard(Emulator* emu);
+ virtual ~PceArcadeCard();
+
+ uint8_t Read(uint8_t bank, uint16_t addr, uint8_t value) override;
+ void Write(uint8_t bank, uint16_t addr, uint8_t value) override;
+};
diff --git a/Core/PCE/PceCdRom.cpp b/Core/PCE/PceCdRom.cpp
index addea876..78f4f526 100644
--- a/Core/PCE/PceCdRom.cpp
+++ b/Core/PCE/PceCdRom.cpp
@@ -152,10 +152,13 @@ uint8_t PceCdRom::Read(uint16_t addr)
case 0x0C: case 0x0D: case 0x0E: case 0x0F:
return _adpcm.Read(addr);
- case 0xC0: return 0x00;
- case 0xC1: return 0xAA;
- case 0xC2: return 0x55;
- case 0xC3: return 0x03;
+ case 0xC0: case 0xC1: case 0xC2: case 0xC3:
+ if(_emu->GetSettings()->GetPcEngineConfig().CdRomType == PceCdRomType::CdRom) {
+ return 0xFF;
+ } else {
+ constexpr uint8_t superCdRomSignature[4] = { 0x00, 0xAA, 0x55, 0x03 };
+ return superCdRomSignature[addr & 0x03];
+ }
default:
LogDebug("Read unknown CDROM register: " + HexUtilities::ToHex(addr));
diff --git a/Core/PCE/PceMemoryManager.h b/Core/PCE/PceMemoryManager.h
index ab694de7..fb517a55 100644
--- a/Core/PCE/PceMemoryManager.h
+++ b/Core/PCE/PceMemoryManager.h
@@ -7,6 +7,8 @@
#include "PCE/PceTimer.h"
#include "PCE/PcePsg.h"
#include "PCE/PceControlManager.h"
+#include "PCE/IPceMapper.h"
+#include "PCE/PceArcadeCard.h"
#include "PCE/PceSf2RomMapper.h"
#include "PCE/PceCdRom.h"
#include "Debugger/DebugTypes.h"
@@ -31,7 +33,7 @@ private:
PcePsg* _psg = nullptr;
PceControlManager* _controlManager = nullptr;
PceCdRom* _cdrom = nullptr;
- unique_ptr _mapper;
+ unique_ptr _mapper;
unique_ptr _timer;
PceMemoryManagerState _state = {};
@@ -103,14 +105,15 @@ public:
if(_cdrom) {
_cdromRamSize = 0x10000;
_cdromRam = new uint8_t[_cdromRamSize];
-
- _cardRamSize = 0x30000;
- _cardRam = new uint8_t[_cardRamSize];
-
_emu->GetSettings()->InitializeRam(_cdromRam, _cdromRamSize);
- _emu->GetSettings()->InitializeRam(_cardRam, _cardRamSize);
_emu->RegisterMemory(MemoryType::PceCdromRam, _cdromRam, _cdromRamSize);
- _emu->RegisterMemory(MemoryType::PceCardRam, _cardRam, _cardRamSize);
+
+ if(cfg.CdRomType == PceCdRomType::SuperCdRom || cfg.CdRomType == PceCdRomType::Arcade) {
+ _cardRamSize = 0x30000;
+ _cardRam = new uint8_t[_cardRamSize];
+ _emu->GetSettings()->InitializeRam(_cardRam, _cardRamSize);
+ _emu->RegisterMemory(MemoryType::PceCardRam, _cardRam, _cardRamSize);
+ }
}
_emu->RegisterMemory(MemoryType::PcePrgRom, _prgRom, _prgRomSize);
@@ -154,7 +157,9 @@ public:
UpdateMappings(bankOffsets);
}
- if(_prgRomSize > 0x80 * 0x2000) {
+ if(_cdrom && cfg.CdRomType == PceCdRomType::Arcade) {
+ _mapper.reset(new PceArcadeCard(emu));
+ } else if(_prgRomSize > 0x80 * 0x2000) {
//For ROMs over 1MB, assume this is the SF2 board
_mapper.reset(new PceSf2RomMapper(this));
}
@@ -327,6 +332,11 @@ public:
} else {
value = ReadRegister(addr & 0x1FFF);
}
+
+ if(_mapper) {
+ value = _mapper->Read(bank, addr, value);
+ }
+
if(_cheatManager->HasCheats()) {
_cheatManager->ApplyCheat((bank << 13) | (addr & 0x1FFF), value);
}
@@ -349,11 +359,11 @@ public:
{
_emu->ProcessMemoryWrite(addr, value, type);
+ uint8_t bank = _state.Mpr[(addr & 0xE000) >> 13];
if(_mapper) {
- _mapper->Write(addr, value);
+ _mapper->Write(bank, addr, value);
}
- uint8_t bank = _state.Mpr[(addr & 0xE000) >> 13];
addr &= 0x1FFF;
if(bank != 0xFF) {
if(_writeBanks[bank]) {
diff --git a/Core/PCE/PceSf2RomMapper.cpp b/Core/PCE/PceSf2RomMapper.cpp
index 7ded9c80..44a401bf 100644
--- a/Core/PCE/PceSf2RomMapper.cpp
+++ b/Core/PCE/PceSf2RomMapper.cpp
@@ -7,7 +7,7 @@ PceSf2RomMapper::PceSf2RomMapper(PceMemoryManager* memoryManager)
_memoryManager = memoryManager;
}
-void PceSf2RomMapper::Write(uint16_t addr, uint8_t value)
+void PceSf2RomMapper::Write(uint8_t bank, uint16_t addr, uint8_t value)
{
if((addr & 0x1FFC) == 0x1FF0) {
_selectedBank = addr & 0x03;
diff --git a/Core/PCE/PceSf2RomMapper.h b/Core/PCE/PceSf2RomMapper.h
index 08221fb7..a8e18de2 100644
--- a/Core/PCE/PceSf2RomMapper.h
+++ b/Core/PCE/PceSf2RomMapper.h
@@ -1,9 +1,10 @@
#pragma once
#include "stdafx.h"
+#include "PCE/IPceMapper.h"
class PceMemoryManager;
-class PceSf2RomMapper
+class PceSf2RomMapper : public IPceMapper
{
private:
uint8_t _selectedBank = 0;
@@ -11,5 +12,5 @@ private:
public:
PceSf2RomMapper(PceMemoryManager* memoryManager);
- void Write(uint16_t addr, uint8_t value);
+ void Write(uint8_t bank, uint16_t addr, uint8_t value) override;
};
diff --git a/Core/PCE/PceTypes.h b/Core/PCE/PceTypes.h
index 0a4557e0..772aa49d 100644
--- a/Core/PCE/PceTypes.h
+++ b/Core/PCE/PceTypes.h
@@ -252,6 +252,37 @@ struct PceVideoState : BaseState
PceVdcState Vdc2;
};
+enum class PceArcadePortOffsetTrigger
+{
+ None = 0,
+ AddOnLowWrite = 1,
+ AddOnHighWrite = 2,
+ AddOnReg0AWrite = 3,
+};
+
+struct PceArcadeCardPortConfig
+{
+ uint32_t BaseAddress;
+ uint16_t Offset;
+ uint16_t IncValue;
+
+ uint8_t Control;
+ bool AutoIncrement;
+ bool AddOffset;
+ bool SignedIncrement; //unused?
+ bool SignedOffset;
+ bool AddIncrementToBase;
+ PceArcadePortOffsetTrigger AddOffsetTrigger;
+};
+
+struct PceArcadeCardState
+{
+ PceArcadeCardPortConfig Port[4];
+ uint32_t ValueReg;
+ uint8_t ShiftReg;
+ uint8_t RotateReg;
+};
+
struct PceState
{
PceCpuState Cpu;
diff --git a/NewUI/Interop/DebugApi.cs b/NewUI/Interop/DebugApi.cs
index 49173646..b4cb2d26 100644
--- a/NewUI/Interop/DebugApi.cs
+++ b/NewUI/Interop/DebugApi.cs
@@ -503,6 +503,7 @@ namespace Mesen.Interop
PceCdromRam,
PceCardRam,
PceAdpcmRam,
+ PceArcadeCardRam,
PceVideoRam,
PceVideoRamVdc2,
PceSpriteRam,
diff --git a/NewUI/Interop/MemoryTypeExtensions.cs b/NewUI/Interop/MemoryTypeExtensions.cs
index 6a94e318..ac50af05 100644
--- a/NewUI/Interop/MemoryTypeExtensions.cs
+++ b/NewUI/Interop/MemoryTypeExtensions.cs
@@ -73,6 +73,7 @@ namespace Mesen.Interop
case MemoryType.PceCdromRam:
case MemoryType.PceCardRam:
case MemoryType.PceAdpcmRam:
+ case MemoryType.PceArcadeCardRam:
case MemoryType.PceVideoRam:
case MemoryType.PceVideoRamVdc2:
case MemoryType.PcePaletteRam:
@@ -274,6 +275,7 @@ namespace Mesen.Interop
MemoryType.PceCdromRam => "CDRAM",
MemoryType.PceCardRam => "Card RAM",
MemoryType.PceAdpcmRam => "ADPCM",
+ MemoryType.PceArcadeCardRam => "ARC",
MemoryType.PceVideoRam => "VRAM",
MemoryType.PceVideoRamVdc2 => "VRAM2",
MemoryType.PcePaletteRam => "PAL",
diff --git a/NewUI/Localization/resources.en.xml b/NewUI/Localization/resources.en.xml
index 730780a9..36d5365f 100644
--- a/NewUI/Localization/resources.en.xml
+++ b/NewUI/Localization/resources.en.xml
@@ -1766,7 +1766,7 @@
CD-ROM²
Super CD-ROM²
- Arcade CD-ROM²
+ Arcade CD-ROM² (recommended)
Random Values (Default)
@@ -1892,6 +1892,7 @@
CD-ROM RAM
Card RAM
ADPCM RAM
+ Arcade Card RAM
Video RAM
Video RAM (VDC2)
Palette RAM