diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 29fd33122..e194fa3f6 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -789,6 +789,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index d196c703e..4b6017526 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -2880,6 +2880,9 @@
WS
+
+ WS
+
WS
diff --git a/Core/Shared/FirmwareHelper.h b/Core/Shared/FirmwareHelper.h
index 07fe20108..af16fc4b4 100644
--- a/Core/Shared/FirmwareHelper.h
+++ b/Core/Shared/FirmwareHelper.h
@@ -403,6 +403,7 @@ class FirmwareHelper
FirmwareType firmwareType;
switch(model) {
default:
+ case WsModel::PocketChallenge:
case WsModel::Monochrome:
filename = "bootrom.ws";
firmwareType = FirmwareType::WonderSwan;
@@ -418,7 +419,7 @@ class FirmwareHelper
firmwareType = FirmwareType::SwanCrystal;
break;
}
- uint32_t size = model == WsModel::Monochrome ? 0x1000 : 0x2000;
+ uint32_t size = firmwareType == FirmwareType::WonderSwan ? 0x1000 : 0x2000;
string path = FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), filename);
if(AttemptLoadFirmware(bootRom, filename, size)) {
return true;
diff --git a/Core/Shared/SettingTypes.h b/Core/Shared/SettingTypes.h
index 3403dac13..1b3dca764 100644
--- a/Core/Shared/SettingTypes.h
+++ b/Core/Shared/SettingTypes.h
@@ -239,7 +239,8 @@ enum class ControllerType
//WS
WsController,
- WsControllerVertical
+ WsControllerVertical,
+ Pcv2Controller
};
struct KeyMapping
@@ -747,7 +748,8 @@ enum class WsModel : uint8_t
Auto,
Monochrome,
Color,
- SwanCrystal
+ SwanCrystal,
+ PocketChallenge
};
enum class WsAudioMode : uint8_t
@@ -760,6 +762,7 @@ struct WsConfig
{
ControllerConfig ControllerHorizontal;
ControllerConfig ControllerVertical;
+ ControllerConfig ControllerPcv2;
WsModel Model = WsModel::Auto;
bool UseBootRom = false;
diff --git a/Core/WS/APU/WsApu.cpp b/Core/WS/APU/WsApu.cpp
index 21401b361..5c47a6b5c 100644
--- a/Core/WS/APU/WsApu.cpp
+++ b/Core/WS/APU/WsApu.cpp
@@ -23,7 +23,7 @@ WsApu::WsApu(Emulator* emu, WsConsole* console, WsMemoryManager* memoryManager,
_dmaController = dmaController;
_soundMixer = emu->GetSoundMixer();
- _state.MasterVolume = _console->GetModel() == WsModel::Monochrome ? 2 : 3;
+ _state.MasterVolume = _console->IsColorModel() ? 3 : 2;
_state.InternalMasterVolume = _state.MasterVolume;
_ch1.reset(new WsApuCh1(this, _state.Ch1));
@@ -46,9 +46,9 @@ WsApu::~WsApu()
void WsApu::ChangeMasterVolume()
{
- if(_emu->GetSettings()->GetWsConfig().AudioMode == WsAudioMode::Speakers) {
+ if(_console->GetAudioMode() == WsAudioMode::Speakers) {
if(_state.InternalMasterVolume == 0) {
- _state.InternalMasterVolume = _console->GetModel() == WsModel::Monochrome ? 2 : 3;
+ _state.InternalMasterVolume = _console->IsColorModel() ? 3 : 2;
} else {
_state.InternalMasterVolume--;
}
@@ -113,7 +113,7 @@ void WsApu::UpdateOutput()
rightOutput = leftOutput;
}
- if(cfg.AudioMode == WsAudioMode::Headphones) {
+ if(_console->GetAudioMode() == WsAudioMode::Headphones) {
if(_state.HeadphoneEnabled) {
leftOutput <<= 5;
rightOutput <<= 5;
@@ -132,7 +132,7 @@ void WsApu::UpdateOutput()
leftOutput = out;
rightOutput = out;
- if(_console->GetModel() == WsModel::Monochrome) {
+ if(!_console->IsColorModel()) {
switch(_state.InternalMasterVolume) {
case 0:
leftOutput = 0;
@@ -253,7 +253,7 @@ uint8_t WsApu::Read(uint16_t port)
(uint8_t)_state.SpeakerEnabled |
(_state.SpeakerVolume << 1) |
((uint8_t)_state.HeadphoneEnabled << 3) |
- (_emu->GetSettings()->GetWsConfig().AudioMode == WsAudioMode::Headphones ? 0x80 : 0));
+ (_console->GetAudioMode() == WsAudioMode::Headphones ? 0x80 : 0));
case 0x92: return BitUtilities::GetBits<0>(_state.Ch4.Lfsr);
case 0x93: return BitUtilities::GetBits<8>(_state.Ch4.Lfsr);
@@ -275,7 +275,7 @@ uint8_t WsApu::Read(uint16_t port)
case 0x9B: return (GetApuOutput(false) + GetApuOutput(true)) >> 8;
case 0x9E:
- if(_console->GetModel() != WsModel::Monochrome) {
+ if(_console->IsColorModel()) {
return _state.MasterVolume;
}
break;
@@ -366,7 +366,7 @@ void WsApu::Write(uint16_t port, uint8_t value)
break;
case 0x9E:
- if(_console->GetModel() != WsModel::Monochrome) {
+ if(_console->IsColorModel()) {
_state.InternalMasterVolume = value & 0x03;
_state.MasterVolume = value & 0x03;
}
diff --git a/Core/WS/Debugger/WsDebugger.cpp b/Core/WS/Debugger/WsDebugger.cpp
index 62f9e194d..2058564fe 100644
--- a/Core/WS/Debugger/WsDebugger.cpp
+++ b/Core/WS/Debugger/WsDebugger.cpp
@@ -7,6 +7,7 @@
#include "WS/Debugger/WsTraceLogger.h"
#include "WS/Debugger/WsPpuTools.h"
#include "WS/WsController.h"
+#include "WS/Pcv2Controller.h"
#include "WS/WsCpu.h"
#include "WS/WsPpu.h"
#include "WS/WsConsole.h"
@@ -497,20 +498,29 @@ void WsDebugger::ProcessInputOverrides(DebugControllerState inputOverrides[8])
{
BaseControlManager* controlManager = _console->GetControlManager();
for(int i = 0; i < 8; i++) {
- shared_ptr controller = std::dynamic_pointer_cast(controlManager->GetControlDeviceByIndex(i));
- if(controller && inputOverrides[i].HasPressedButton()) {
- controller->SetBitValue(WsController::Buttons::A, inputOverrides[i].A);
- controller->SetBitValue(WsController::Buttons::B, inputOverrides[i].B);
- controller->SetBitValue(WsController::Buttons::Start, inputOverrides[i].Start);
- controller->SetBitValue(WsController::Buttons::Sound, inputOverrides[i].Select);
- controller->SetBitValue(WsController::Buttons::Up, inputOverrides[i].Up);
- controller->SetBitValue(WsController::Buttons::Down, inputOverrides[i].Down);
- controller->SetBitValue(WsController::Buttons::Left, inputOverrides[i].Left);
- controller->SetBitValue(WsController::Buttons::Right, inputOverrides[i].Right);
- controller->SetBitValue(WsController::Buttons::Up2, inputOverrides[i].U);
- controller->SetBitValue(WsController::Buttons::Down2, inputOverrides[i].D);
- controller->SetBitValue(WsController::Buttons::Left2, inputOverrides[i].L);
- controller->SetBitValue(WsController::Buttons::Right2, inputOverrides[i].R);
+ shared_ptr wsController = std::dynamic_pointer_cast(controlManager->GetControlDeviceByIndex(i));
+ if(wsController && inputOverrides[i].HasPressedButton()) {
+ wsController->SetBitValue(WsController::Buttons::A, inputOverrides[i].A);
+ wsController->SetBitValue(WsController::Buttons::B, inputOverrides[i].B);
+ wsController->SetBitValue(WsController::Buttons::Start, inputOverrides[i].Start);
+ wsController->SetBitValue(WsController::Buttons::Sound, inputOverrides[i].Select);
+ wsController->SetBitValue(WsController::Buttons::Up, inputOverrides[i].Up);
+ wsController->SetBitValue(WsController::Buttons::Down, inputOverrides[i].Down);
+ wsController->SetBitValue(WsController::Buttons::Left, inputOverrides[i].Left);
+ wsController->SetBitValue(WsController::Buttons::Right, inputOverrides[i].Right);
+ wsController->SetBitValue(WsController::Buttons::Up2, inputOverrides[i].U);
+ wsController->SetBitValue(WsController::Buttons::Down2, inputOverrides[i].D);
+ wsController->SetBitValue(WsController::Buttons::Left2, inputOverrides[i].L);
+ wsController->SetBitValue(WsController::Buttons::Right2, inputOverrides[i].R);
+ }
+
+ shared_ptr pcv2Controller = std::dynamic_pointer_cast(controlManager->GetControlDeviceByIndex(i));
+ if(pcv2Controller && inputOverrides[i].HasPressedButton()) {
+ pcv2Controller->SetBitValue(Pcv2Controller::Buttons::Up, inputOverrides[i].Up);
+ pcv2Controller->SetBitValue(Pcv2Controller::Buttons::Down, inputOverrides[i].Down);
+ pcv2Controller->SetBitValue(Pcv2Controller::Buttons::Left, inputOverrides[i].Left);
+ pcv2Controller->SetBitValue(Pcv2Controller::Buttons::Right, inputOverrides[i].Right);
+ //TODOWS other buttons
}
}
controlManager->RefreshHubState();
diff --git a/Core/WS/Pcv2Controller.h b/Core/WS/Pcv2Controller.h
new file mode 100644
index 000000000..e5f32f296
--- /dev/null
+++ b/Core/WS/Pcv2Controller.h
@@ -0,0 +1,94 @@
+#pragma once
+#include "pch.h"
+#include "WS/WsConsole.h"
+#include "Shared/BaseControlDevice.h"
+#include "Shared/Emulator.h"
+#include "Shared/EmuSettings.h"
+#include "Shared/InputHud.h"
+#include "Utilities/Serializer.h"
+
+class Pcv2Controller : public BaseControlDevice
+{
+private:
+ WsConsole* _console = nullptr;
+
+protected:
+ string GetKeyNames() override
+ {
+ return "udlrepcCv";
+ }
+
+ void InternalSetStateFromInput() override
+ {
+ for(KeyMapping& keyMapping : _keyMappings) {
+ for(int i = Buttons::Up; i <= Buttons::View; i++) {
+ SetPressedState(i, keyMapping.CustomKeys[i]);
+ }
+ }
+ }
+
+ void RefreshStateBuffer() override
+ {
+ }
+
+public:
+ enum Buttons
+ {
+ Up = 0,
+ Down,
+ Left,
+ Right,
+ Esc,
+ Pass,
+ Circle,
+ Clear,
+ View
+ };
+
+ Pcv2Controller(Emulator* emu, WsConsole* console, uint8_t port, KeyMappingSet mappings) : BaseControlDevice(emu, ControllerType::Pcv2Controller, port, mappings)
+ {
+ _console = console;
+ }
+
+ uint8_t ReadRam(uint16_t addr) override
+ {
+ return 0;
+ }
+
+ void WriteRam(uint16_t addr, uint8_t value) override
+ {
+ }
+
+ void InternalDrawController(InputHud& hud) override
+ {
+ hud.DrawOutline(35, 16);
+
+ hud.DrawButton(5, 5, 3, 3, IsPressed(Buttons::Up));
+ hud.DrawButton(5, 11, 3, 3, IsPressed(Buttons::Down));
+ hud.DrawButton(2, 8, 3, 3, IsPressed(Buttons::Left));
+ hud.DrawButton(8, 8, 3, 3, IsPressed(Buttons::Right));
+ hud.DrawButton(9, 2, 2, 2, IsPressed(Buttons::Esc));
+
+ hud.DrawButton(30, 5, 3, 3, IsPressed(Buttons::Clear));
+ hud.DrawButton(27, 8, 3, 3, IsPressed(Buttons::Circle));
+ hud.DrawButton(24, 11, 3, 3, IsPressed(Buttons::Pass));
+ hud.DrawButton(24, 2, 2, 2, IsPressed(Buttons::View));
+
+ hud.DrawNumber(_port + 1, 16, 2);
+ }
+
+ vector GetKeyNameAssociations() override
+ {
+ return {
+ { "up", Buttons::Up },
+ { "down", Buttons::Down },
+ { "left", Buttons::Left },
+ { "right", Buttons::Right },
+ { "esc", Buttons::Esc },
+ { "pass", Buttons::Pass },
+ { "circle", Buttons::Circle },
+ { "clear", Buttons::Clear },
+ { "view", Buttons::View },
+ };
+ }
+};
\ No newline at end of file
diff --git a/Core/WS/WsConsole.cpp b/Core/WS/WsConsole.cpp
index b6445985b..8efbe70b8 100644
--- a/Core/WS/WsConsole.cpp
+++ b/Core/WS/WsConsole.cpp
@@ -96,10 +96,11 @@ LoadRomResult WsConsole::LoadRom(VirtualFile& romFile)
_model = _emu->GetSettings()->GetWsConfig().Model;
if(_model == WsModel::Auto) {
- _model = hasColorSupport ? WsModel::Color : WsModel::Monochrome;
+ _model = romFile.GetFileExtension() == ".pc2" ? WsModel::PocketChallenge : (hasColorSupport ? WsModel::Color : WsModel::Monochrome);
}
+ _colorModel = _model == WsModel::Color || _model == WsModel::SwanCrystal;
- _workRamSize = _model == WsModel::Monochrome ? 0x4000 : 0x10000;
+ _workRamSize = _colorModel ? 0x10000 : 0x4000;
_workRam = new uint8_t[_workRamSize];
memset(_workRam, 0, _workRamSize);
_emu->RegisterMemory(MemoryType::WsWorkRam, _workRam, _workRamSize);
@@ -112,10 +113,16 @@ LoadRomResult WsConsole::LoadRom(VirtualFile& romFile)
_emu->RegisterMemory(MemoryType::WsBootRom, _bootRom, _bootRomSize);
}
- _internalEepromSize = (uint32_t)(_model == WsModel::Monochrome ? WsEepromSize::Size128 : WsEepromSize::Size2kb);
- _internalEepromData = new uint8_t[_internalEepromSize];
- memset(_internalEepromData, 0, _internalEepromSize);
- _emu->RegisterMemory(MemoryType::WsInternalEeprom, _internalEepromData, _internalEepromSize);
+ switch(_model) {
+ case WsModel::PocketChallenge: _internalEepromSize = (uint32_t)WsEepromSize::Size0; break;
+ case WsModel::Monochrome: _internalEepromSize = (uint32_t)WsEepromSize::Size128; break;
+ default: _internalEepromSize = (uint32_t)WsEepromSize::Size2kb; break;
+ }
+ if(_internalEepromSize) {
+ _internalEepromData = new uint8_t[_internalEepromSize];
+ memset(_internalEepromData, 0, _internalEepromSize);
+ _emu->RegisterMemory(MemoryType::WsInternalEeprom, _internalEepromData, _internalEepromSize);
+ }
_internalEeprom.reset(new WsEeprom(_emu, this, (WsEepromSize)_internalEepromSize, _internalEepromData, true));
@@ -180,6 +187,11 @@ bool WsConsole::IsColorMode()
return _memoryManager->GetState().ColorEnabled;
}
+bool WsConsole::IsColorModel()
+{
+ return _colorModel;
+}
+
bool WsConsole::IsPowerOff()
{
return _cpu->IsPowerOff();
@@ -190,6 +202,15 @@ bool WsConsole::IsVerticalMode()
return _verticalMode;
}
+WsAudioMode WsConsole::GetAudioMode()
+{
+ // PCv2 always uses the headphone port for sound output
+ if(_model == WsModel::PocketChallenge) {
+ return WsAudioMode::Headphones;
+ }
+ return _emu->GetSettings()->GetWsConfig().AudioMode;
+}
+
WsModel WsConsole::GetModel()
{
return _model;
@@ -210,11 +231,11 @@ void WsConsole::Reset()
void WsConsole::InitPostBootRomState()
{
//Init work ram
- if(_model == WsModel::Monochrome) {
- memset(_workRam, 0, _workRamSize);
- } else {
+ if(_colorModel) {
memset(_workRam, 0, 0xFE00);
memset(_workRam + 0xFE00, 0xFF, 0x200);
+ } else {
+ memset(_workRam, 0, _workRamSize);
}
//Init port state
@@ -222,7 +243,13 @@ void WsConsole::InitPostBootRomState()
cpu.CS = 0xFFFF;
cpu.SP = 0x2000;
- if(_model == WsModel::Monochrome) {
+ if(_model == WsModel::PocketChallenge) {
+ // The Pocket Challenge skips most of the boot ROM.
+ // TODOWS: Figure out scanline, cycle, any missing port writes.
+ cpu.CS = 0x4000;
+ cpu.IP = 0x0010;
+ return;
+ } else if(_model == WsModel::Monochrome) {
cpu.AX = 0xFF85;
cpu.BX = 0x0040;
cpu.DX = 0x0005;
@@ -243,7 +270,10 @@ void WsConsole::InitPostBootRomState()
WsPpuState& ppu = _ppu->GetState();
ppu.LcdEnabled = true;
- if(_model == WsModel::Monochrome) {
+ if(_colorModel) {
+ ppu.Scanline = 127;
+ ppu.Cycle = 232;
+ } else {
ppu.Scanline = 26;
ppu.Cycle = 53;
@@ -260,9 +290,6 @@ void WsConsole::InitPostBootRomState()
memcpy(ppu.BwPalettes, defaultBwPalette, sizeof(defaultBwPalette));
memcpy(ppu.BwShades, defaultShades, sizeof(defaultShades));
- } else {
- ppu.Scanline = 127;
- ppu.Cycle = 232;
}
_ppu->SetOutputToBgColor();
@@ -278,12 +305,12 @@ void WsConsole::InitPostBootRomState()
mm.SlowRom = (_prgRom[_prgRomSize - 4] & 0x08) != 0;
WsApuState& apu = _apu->GetState();
- if(_model == WsModel::Monochrome) {
- apu.Ch1.Frequency = 0x7E6;
- apu.Ch2.Frequency = 0x7E6;
- } else {
+ if(_colorModel) {
apu.InternalMasterVolume = _internalEepromData[0x83] & 0x03;
ppu.HighContrast = (_internalEepromData[0x83] & 0x40);
+ } else {
+ apu.Ch1.Frequency = 0x7E6;
+ apu.Ch2.Frequency = 0x7E6;
}
WsEepromState& eeprom = _internalEeprom->GetState();
@@ -296,7 +323,7 @@ void WsConsole::InitPostBootRomState()
_internalEepromData[0x76 + i] = _prgRom[_prgRomSize - 0x10 + 6 + i];
bool supportsColor = _prgRom[_prgRomSize - 0x10 + 7] & 0x01;
- if(_model != WsModel::Monochrome && supportsColor) {
+ if(_colorModel && supportsColor) {
//For games that support color & color models, copy 76-78 to 80-82
_internalEepromData[0x80 + i] = _internalEepromData[0x76 + i];
}
@@ -455,13 +482,34 @@ void WsConsole::GetConsoleState(BaseState& state, ConsoleType consoleType)
(WsState&)state = GetState();
}
+static WsModel GetModelForCompatibilityCheck(WsModel model)
+{
+ if(model == WsModel::SwanCrystal) {
+ //WSC and SC save states are currently interchangeable.
+ return WsModel::Color;
+ }
+ return model;
+}
+
+static string GetModelShortName(WsModel model)
+{
+ switch(model) {
+ case WsModel::Color:
+ case WsModel::SwanCrystal:
+ return "WSC";
+ case WsModel::PocketChallenge:
+ return "PCv2";
+ default:
+ return "WS";
+ }
+}
+
void WsConsole::Serialize(Serializer& s)
{
WsModel model = _model;
SV(model);
- if(!s.IsSaving() && (model == WsModel::Monochrome) != (_model == WsModel::Monochrome)) {
- bool isMono = _model == WsModel::Monochrome;
- MessageManager::DisplayMessage("SaveStates", isMono ? "Can't load WSC state in WS mode." : "Can't load WS state in WSC mode.");
+ if(!s.IsSaving() && GetModelForCompatibilityCheck(model) != GetModelForCompatibilityCheck(_model)) {
+ MessageManager::DisplayMessage("SaveStates", "Can't load " + GetModelShortName(model) + " state in " + GetModelShortName(_model) + " mode.");
s.SetErrorFlag();
return;
}
diff --git a/Core/WS/WsConsole.h b/Core/WS/WsConsole.h
index 628016f73..e48cdf91d 100644
--- a/Core/WS/WsConsole.h
+++ b/Core/WS/WsConsole.h
@@ -1,4 +1,5 @@
#pragma once
+#include "Shared/SettingTypes.h"
#include "pch.h"
#include "Shared/Interfaces/IConsole.h"
#include "WS/WsTypes.h"
@@ -51,6 +52,7 @@ class WsConsole final : public IConsole
uint32_t _cartEepromSize = 0;
WsModel _model = {};
+ bool _colorModel = false;
bool _verticalMode = false;
void InitPostBootRomState();
@@ -59,7 +61,7 @@ class WsConsole final : public IConsole
WsConsole(Emulator* emu);
~WsConsole();
- static vector GetSupportedExtensions() { return { ".ws", ".wsc" }; }
+ static vector GetSupportedExtensions() { return { ".ws", ".wsc", ".pc2" }; }
static vector GetSupportedSignatures() { return {}; }
LoadRomResult LoadRom(VirtualFile& romFile) override;
@@ -67,8 +69,10 @@ class WsConsole final : public IConsole
void GetScreenRotationOverride(uint32_t& rotation) override;
bool IsColorMode();
+ bool IsColorModel();
bool IsPowerOff();
bool IsVerticalMode();
+ WsAudioMode GetAudioMode();
WsModel GetModel();
void ProcessEndOfFrame();
diff --git a/Core/WS/WsControlManager.cpp b/Core/WS/WsControlManager.cpp
index 5759cbfcd..181c4c019 100644
--- a/Core/WS/WsControlManager.cpp
+++ b/Core/WS/WsControlManager.cpp
@@ -3,6 +3,7 @@
#include "WS/WsControlManager.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsController.h"
+#include "WS/Pcv2Controller.h"
#include "Shared/KeyManager.h"
WsControlManager::WsControlManager(Emulator* emu, WsConsole* console) : BaseControlManager(emu, CpuType::Ws)
@@ -24,6 +25,10 @@ shared_ptr WsControlManager::CreateControllerDevice(Controlle
case ControllerType::WsControllerVertical:
device.reset(new WsController(_emu, _console, port, cfg.ControllerHorizontal.Keys, cfg.ControllerVertical.Keys));
break;
+
+ case ControllerType::Pcv2Controller:
+ device.reset(new Pcv2Controller(_emu, _console, port, cfg.ControllerPcv2.Keys));
+ break;
}
return device;
@@ -41,7 +46,7 @@ void WsControlManager::UpdateControlDevices()
ClearDevices();
- shared_ptr device(CreateControllerDevice(ControllerType::WsController, 0));
+ shared_ptr device(CreateControllerDevice(_console->GetModel() == WsModel::PocketChallenge ? ControllerType::Pcv2Controller : ControllerType::WsController, 0));
if(device) {
RegisterControlDevice(device);
}
@@ -70,6 +75,23 @@ uint8_t WsControlManager::Read()
result |= controller->IsPressed(WsController::A) ? 0x04 : 0;
result |= controller->IsPressed(WsController::B) ? 0x08 : 0;
}
+ } else if(controller->GetPort() == 0 && controller->GetControllerType() == ControllerType::Pcv2Controller) {
+ result |= 0x02;
+ if(_state.InputSelect & 0x10) {
+ result |= controller->IsPressed(Pcv2Controller::Clear) ? 0x01 : 0;
+ result |= controller->IsPressed(Pcv2Controller::Circle) ? 0x04 : 0;
+ result |= controller->IsPressed(Pcv2Controller::Pass) ? 0x08 : 0;
+ }
+ if(_state.InputSelect & 0x20) {
+ result |= controller->IsPressed(Pcv2Controller::View) ? 0x01 : 0;
+ result |= controller->IsPressed(Pcv2Controller::Esc) ? 0x04 : 0;
+ result |= controller->IsPressed(Pcv2Controller::Right) ? 0x08 : 0;
+ }
+ if(_state.InputSelect & 0x40) {
+ result |= controller->IsPressed(Pcv2Controller::Left) ? 0x01 : 0;
+ result |= controller->IsPressed(Pcv2Controller::Down) ? 0x04 : 0;
+ result |= controller->IsPressed(Pcv2Controller::Up) ? 0x08 : 0;
+ }
}
}
diff --git a/Core/WS/WsCpu.cpp b/Core/WS/WsCpu.cpp
index f3b3e94a4..7dc3c7a5c 100644
--- a/Core/WS/WsCpu.cpp
+++ b/Core/WS/WsCpu.cpp
@@ -1393,7 +1393,7 @@ void WsCpu::MulUnsigned(T x, T y)
_state.Flags.AuxCarry = false;
_state.Flags.Parity = false;
_state.Flags.Sign = false;
- _state.Flags.Zero = _console->GetModel() != WsModel::Monochrome;
+ _state.Flags.Zero = _console->IsColorModel();
_mulOverflow = overflow;
diff --git a/Core/WS/WsDefaultVideoFilter.cpp b/Core/WS/WsDefaultVideoFilter.cpp
index 08771017e..74e4b37b9 100644
--- a/Core/WS/WsDefaultVideoFilter.cpp
+++ b/Core/WS/WsDefaultVideoFilter.cpp
@@ -37,7 +37,7 @@ void WsDefaultVideoFilter::InitLookupTable()
uint8_t b = rgb444 & 0xF;
if(_adjustColors) {
- if(_console->GetModel() == WsModel::Monochrome) {
+ if(!_console->IsColorModel()) {
//Same formula as what asie recently implemented for ares
auto applyGamma = [](uint32_t color, uint32_t min, uint32_t max) {
return min + (uint32_t)(pow(color / 15.0, 1 / 2.2) * (max - min));
diff --git a/Core/WS/WsEeprom.cpp b/Core/WS/WsEeprom.cpp
index a510694e6..0453df77e 100644
--- a/Core/WS/WsEeprom.cpp
+++ b/Core/WS/WsEeprom.cpp
@@ -53,8 +53,12 @@ WsEepromCommand WsEeprom::GetCommand()
WsEepromSize WsEeprom::GetSize()
{
+ if(_state.Size == WsEepromSize::Size0) {
+ //Process commands as if the eeprom was the monochrome model's 128-byte eeprom on PCv2
+ return WsEepromSize::Size128;
+ }
if(_isInternal && _state.Size == WsEepromSize::Size2kb && !_console->IsColorMode()) {
- //Process commands as is the eeprom was the monochrome model's 128-byte eeprom when color mode is disabled
+ //Process commands as if the eeprom was the monochrome model's 128-byte eeprom when color mode is disabled
return WsEepromSize::Size128;
}
return _state.Size;
@@ -73,7 +77,7 @@ uint16_t WsEeprom::GetCommandAddress()
void WsEeprom::WriteValue(uint16_t addr, uint16_t value)
{
- if(!_state.WriteDisabled && (!_state.InternalEepromWriteProtected || addr < 0x30)) {
+ if((uint32_t)_state.Size && !_state.WriteDisabled && (!_state.InternalEepromWriteProtected || addr < 0x30)) {
if(_isInternal) {
_emu->ProcessMemoryAccess(addr << 1, value);
} else {
@@ -94,6 +98,10 @@ string WsEeprom::ConvertToEepromString(string in)
void WsEeprom::InitInternalEepromData()
{
+ if(!(uint32_t)_state.Size) {
+ return;
+ }
+
memset(_data, 0, (uint32_t)_state.Size);
string name;
@@ -132,7 +140,11 @@ void WsEeprom::Run()
switch(cmd) {
case WsEepromCommand::Read:
- _state.ReadBuffer = _data[addr << 1] | (_data[(addr << 1) + 1] << 8);
+ if(!(uint32_t)_state.Size) {
+ _state.ReadBuffer = 0xFFFF;
+ } else {
+ _state.ReadBuffer = _data[addr << 1] | (_data[(addr << 1) + 1] << 8);
+ }
_state.ReadDone = true;
break;
@@ -207,10 +219,12 @@ void WsEeprom::WritePort(uint8_t port, uint8_t value)
}
_state.Idle = false;
- if(_isInternal) {
- _emu->ProcessMemoryAccess(addr << 1, _state.ReadBuffer);
- } else {
- _emu->ProcessMemoryAccess(addr << 1, _state.ReadBuffer);
+ if((uint32_t)_state.Size) {
+ if(_isInternal) {
+ _emu->ProcessMemoryAccess(addr << 1, _state.ReadBuffer);
+ } else {
+ _emu->ProcessMemoryAccess(addr << 1, _state.ReadBuffer);
+ }
}
}
} else if(write) {
@@ -263,12 +277,16 @@ uint8_t WsEeprom::ReadPort(uint8_t port)
void WsEeprom::LoadBattery()
{
- _emu->GetBatteryManager()->LoadBattery(_isInternal ? ".ieeprom" : ".eeprom", _data, (uint32_t)_state.Size);
+ if((uint32_t)_state.Size) {
+ _emu->GetBatteryManager()->LoadBattery(_isInternal ? ".ieeprom" : ".eeprom", _data, (uint32_t)_state.Size);
+ }
}
void WsEeprom::SaveBattery()
{
- _emu->GetBatteryManager()->SaveBattery(_isInternal ? ".ieeprom" : ".eeprom", _data, (uint32_t)_state.Size);
+ if((uint32_t)_state.Size) {
+ _emu->GetBatteryManager()->SaveBattery(_isInternal ? ".ieeprom" : ".eeprom", _data, (uint32_t)_state.Size);
+ }
}
void WsEeprom::Serialize(Serializer& s)
diff --git a/Core/WS/WsMemoryManager.cpp b/Core/WS/WsMemoryManager.cpp
index db1a38643..23ad46cb1 100644
--- a/Core/WS/WsMemoryManager.cpp
+++ b/Core/WS/WsMemoryManager.cpp
@@ -217,7 +217,7 @@ bool WsMemoryManager::IsUnmappedPort(uint16_t port)
uint8_t WsMemoryManager::GetUnmappedPort()
{
- return _console->GetModel() == WsModel::Monochrome ? 0x90 : 0;
+ return _console->IsColorModel() ? 0 : 0x90;
}
uint8_t WsMemoryManager::InternalReadPort(uint16_t port, bool isWordAccess)
@@ -226,9 +226,9 @@ uint8_t WsMemoryManager::InternalReadPort(uint16_t port, bool isWordAccess)
if(port <= 0x3F) {
return _ppu->ReadPort(port);
- } else if(port >= 0x40 && port <= 0x53 && _console->GetModel() != WsModel::Monochrome) {
+ } else if(port >= 0x40 && port <= 0x53 && _console->IsColorModel()) {
return _state.ColorEnabled ? _dmaController->ReadPort(port) : 0;
- } else if(port >= 0x6A && port <= 0x6B && _console->GetModel() != WsModel::Monochrome) {
+ } else if(port >= 0x6A && port <= 0x6B && _console->IsColorModel()) {
return _apu->Read(port); //HyperVoice
} else if(port >= 0x70 && port <= 0x77 && _console->GetModel() == WsModel::SwanCrystal) {
return _ppu->ReadLcdConfigPort(port);
@@ -237,7 +237,7 @@ uint8_t WsMemoryManager::InternalReadPort(uint16_t port, bool isWordAccess)
} else if(port >= 0xA2 && port <= 0xAB && port != 0xA3) {
return _timer->ReadPort(port);
} else if(port >= 0xBA && port <= 0xBF) {
- if(_console->GetModel() != WsModel::Monochrome || isWordAccess || !(port & 0x01)) {
+ if(_console->IsColorModel() || isWordAccess || !(port & 0x01)) {
return _eeprom->ReadPort(port - 0xBA);
} else {
return GetUnmappedPort();
@@ -246,10 +246,10 @@ uint8_t WsMemoryManager::InternalReadPort(uint16_t port, bool isWordAccess)
return _cart->ReadPort(port);
} else {
switch(port) {
- case 0x60: return _console->GetModel() == WsModel::Monochrome ? GetUnmappedPort() : _state.SystemControl2;
+ case 0x60: return _console->IsColorModel() ? _state.SystemControl2 : GetUnmappedPort();
case 0x62:
- if(_console->GetModel() == WsModel::Monochrome) {
+ if(!_console->IsColorModel()) {
return GetUnmappedPort();
}
return (
@@ -259,7 +259,7 @@ uint8_t WsMemoryManager::InternalReadPort(uint16_t port, bool isWordAccess)
case 0xA0:
return (
(_state.BootRomDisabled ? 0x01 : 0) |
- (_console->GetModel() == WsModel::Monochrome ? 0 : 0x02) |
+ (_console->IsColorModel() ? 0x02 : 0) |
(_state.CartWordBus ? 0x04 : 0) |
(_state.SlowRom ? 0x08 : 0) |
0x80 //mbc authentication?
@@ -294,7 +294,7 @@ void WsMemoryManager::InternalWritePort(uint16_t port, uint8_t value, bool isWor
_ppu->WritePort(port, value);
} else if(port >= 0x40 && port <= 0x53 && _state.ColorEnabled) {
return _dmaController->WritePort(port, value);
- } else if(port >= 0x64 && port <= 0x6B && _console->GetModel() != WsModel::Monochrome) {
+ } else if(port >= 0x64 && port <= 0x6B && _console->IsColorModel()) {
_apu->Write(port, value); //HyperVoice
} else if(port >= 0x70 && port <= 0x77 && !_state.BootRomDisabled && _console->GetModel() == WsModel::SwanCrystal) {
_ppu->WriteLcdConfigPort(port, value);
@@ -303,7 +303,7 @@ void WsMemoryManager::InternalWritePort(uint16_t port, uint8_t value, bool isWor
} else if(port >= 0xA2 && port <= 0xAB && port != 0xA3) {
_timer->WritePort(port, value);
} else if(port >= 0xBA && port <= 0xBF) {
- if(_console->GetModel() != WsModel::Monochrome || isWordAccess || !(port & 0x01)) {
+ if(_console->IsColorModel() || isWordAccess || !(port & 0x01)) {
_eeprom->WritePort(port - 0xBA, value);
}
} else if(port >= 0xC0) {
@@ -311,7 +311,7 @@ void WsMemoryManager::InternalWritePort(uint16_t port, uint8_t value, bool isWor
} else {
switch(port) {
case 0x60:
- if(_console->GetModel() != WsModel::Monochrome) {
+ if(_console->IsColorModel()) {
_state.SystemControl2 = value & 0xEF;
_state.ColorEnabled = value & 0x80;
_state.Enable4bpp = value & 0x40;
@@ -334,7 +334,7 @@ void WsMemoryManager::InternalWritePort(uint16_t port, uint8_t value, bool isWor
break;
case 0x62:
- if(_console->GetModel() != WsModel::Monochrome) {
+ if(_console->IsColorModel()) {
_state.PowerOffRequested = value & 0x01;
}
break;
diff --git a/Core/WS/WsPpu.cpp b/Core/WS/WsPpu.cpp
index 2bf62ff5e..fa5842771 100644
--- a/Core/WS/WsPpu.cpp
+++ b/Core/WS/WsPpu.cpp
@@ -32,7 +32,7 @@ WsPpu::WsPpu(Emulator* emu, WsConsole* console, WsMemoryManager* memoryManager,
_screenHeight = WsConstants::ScreenHeight;
if(console->GetModel() == WsModel::Monochrome) {
_screenHeight += 13;
- } else {
+ } else if(console->IsColorModel()) {
_screenWidth += 13;
}
@@ -201,7 +201,7 @@ void WsPpu::DrawBackground()
}
uint16_t scanline = _state.Scanline;
- uint16_t layerAddr = (uint16_t)(layer.MapAddressLatch & (_console->GetModel() == WsModel::Monochrome ? 0x3FFF : 0x7FFF));
+ uint16_t layerAddr = (uint16_t)(layer.MapAddressLatch & (_console->IsColorModel() ? 0x7FFF : 0x3FFF));
for(int cycle = 0; cycle < WsConstants::ScreenWidth; cycle++) {
int y = (scanline + layer.ScrollYLatch) & 0xFF;
@@ -299,7 +299,7 @@ void WsPpu::ProcessSpriteCopy()
_state.SpriteCountLatch = _state.SpriteCount;
}
- uint16_t baseAddr = _state.SpriteTableAddress & (_console->GetModel() == WsModel::Monochrome ? 0x3FFF : 0x7FFF);
+ uint16_t baseAddr = _state.SpriteTableAddress & (_console->IsColorModel() ? 0x7FFF : 0x3FFF);
int i = _state.Cycle << 1;
_spriteRam[i] = _vram[baseAddr + (((_state.FirstSpriteIndex * 4) + i) & 0x1FF)];
@@ -327,7 +327,7 @@ void WsPpu::DrawIcons()
static constexpr uint16_t aux2[11] = { 0, 0, 0x70, 0xF8, 0x1FC, 0x1FC, 0x1FC, 0xF8, 0x70, 0, 0 };
static constexpr uint16_t aux1[11] = { 0, 0, 0, 0, 0x70, 0x70, 0x70, 0, 0, 0, 0 };
- if(_console->GetModel() == WsModel::Monochrome) {
+ if(!_console->IsColorModel()) {
uint16_t* start = _currentBuffer + (WsConstants::ScreenWidth * WsConstants::ScreenHeight);
std::fill(start, start + WsConstants::ScreenWidth * 13, 0xFFF);
} else {
@@ -343,10 +343,10 @@ void WsPpu::DrawIcons()
//DrawIcon(true, lowBattery, 39, 144);
if(_state.ShowVolumeIconFrame <= _state.FrameCount && _state.FrameCount - _state.ShowVolumeIconFrame < 128) {
//Show speaker/headphone icons if sound button was pressed within the last 128 frames
- if(_emu->GetSettings()->GetWsConfig().AudioMode == WsAudioMode::Headphones) {
+ if(_console->GetAudioMode() == WsAudioMode::Headphones) {
DrawIcon(true, headphones, 65);
} else {
- if(_console->GetModel() == WsModel::Monochrome) {
+ if(!_console->IsColorModel()) {
switch(_console->GetApu()->GetMasterVolume()) {
default:
case 0: DrawIcon(true, volumeWsOff, 52); break;
@@ -374,7 +374,7 @@ void WsPpu::DrawIcons()
void WsPpu::DrawIcon(bool visible, const uint16_t icon[11], uint8_t position)
{
- if(!visible) {
+ if(!visible || _console->GetModel() == WsModel::PocketChallenge) {
return;
}
@@ -441,7 +441,7 @@ uint8_t WsPpu::GetLcdStatus()
uint8_t masterVolume = _console->GetApu()->GetMasterVolume();
uint8_t volumeLevel;
- if(_console->GetModel() == WsModel::Monochrome) {
+ if(!_console->IsColorModel()) {
switch(masterVolume) {
default:
case 0: volumeLevel = 0; break;
@@ -462,7 +462,7 @@ uint8_t WsPpu::GetLcdStatus()
bool speaker = false;
if(_state.ShowVolumeIconFrame <= _state.FrameCount && _state.FrameCount - _state.ShowVolumeIconFrame < 128) {
//Show speaker/headphone icons if sound button was pressed within the last 128 frames
- if(_emu->GetSettings()->GetWsConfig().AudioMode == WsAudioMode::Headphones) {
+ if(_console->GetAudioMode() == WsAudioMode::Headphones) {
headphone = true;
} else {
speaker = true;
@@ -570,15 +570,15 @@ void WsPpu::WritePort(uint16_t port, uint8_t value)
_state.BgWindow.Enabled = value & 0x20;
break;
- case 0x01: _state.BgColor = value & (_console->GetModel() == WsModel::Monochrome ? 0x07 : 0xFF); break;
+ case 0x01: _state.BgColor = value & (_console->IsColorModel() ? 0xFF : 0x07); break;
case 0x03: _state.IrqScanline = value; break;
- case 0x04: _state.SpriteTableAddress = (value & (_console->GetModel() == WsModel::Monochrome ? 0x1F : 0x3F)) << 9; break;
+ case 0x04: _state.SpriteTableAddress = (value & (_console->IsColorModel() ? 0x3F : 0x1F)) << 9; break;
case 0x05: _state.FirstSpriteIndex = value & 0x7F; break;
case 0x06: _state.SpriteCount = value; break;
case 0x07:
- _state.ScreenAddress = value & (_console->GetModel() == WsModel::Monochrome ? 0x77 : 0xFF);
+ _state.ScreenAddress = value & (_console->IsColorModel() ? 0xFF : 0x77);
_state.BgLayers[0].MapAddress = (_state.ScreenAddress & 0x0F) << 11;
_state.BgLayers[1].MapAddress = (_state.ScreenAddress & 0xF0) << 7;
break;
diff --git a/PGOHelper/PGOHelper.cpp b/PGOHelper/PGOHelper.cpp
index 5a28f3928..9a9740768 100644
--- a/PGOHelper/PGOHelper.cpp
+++ b/PGOHelper/PGOHelper.cpp
@@ -48,7 +48,7 @@ int main(int argc, char* argv[])
romFolder = argv[1];
}
- vector testRoms = GetFilesInFolder(romFolder, { ".sfc", ".gb", ".gbc", ".gbx", ".nes", ".pce", ".cue", ".sms", ".gg", ".sg", ".gba", ".col", ".ws", ".wsc" });
+ vector testRoms = GetFilesInFolder(romFolder, { ".sfc", ".gb", ".gbc", ".gbx", ".nes", ".pce", ".cue", ".sms", ".gg", ".sg", ".gba", ".col", ".ws", ".wsc", ".pc2" });
PgoRunTest(testRoms, true);
return 0;
}
diff --git a/UI/Config/FileAssociationHelper.cs b/UI/Config/FileAssociationHelper.cs
index d01e030c4..b65eed216 100644
--- a/UI/Config/FileAssociationHelper.cs
+++ b/UI/Config/FileAssociationHelper.cs
@@ -94,6 +94,7 @@ static public void UpdateLinuxFileAssociations()
CreateMimeType("x-mesen-ws", "ws", "WonderSwan ROM", mimeTypes, cfg.AssociateWsRomFiles);
CreateMimeType("x-mesen-wsc", "wsc", "WonderSwan Color ROM", mimeTypes, cfg.AssociateWsRomFiles);
+ CreateMimeType("x-mesen-pc2", "pc2", "Pocket Challenge V2 ROM", mimeTypes, cfg.AssociateWsRomFiles);
//Icon used for shortcuts
ImageUtilities.BitmapFromAsset("Assets/MesenIcon.png").Save(Path.Combine(iconFolder, "MesenIcon.png"));
@@ -217,6 +218,7 @@ static public void UpdateWindowsFileAssociations()
FileAssociationHelper.UpdateFileAssociation("ws", cfg.AssociateWsRomFiles);
FileAssociationHelper.UpdateFileAssociation("wsc", cfg.AssociateWsRomFiles);
+ FileAssociationHelper.UpdateFileAssociation("pc2", cfg.AssociateWsRomFiles);
}
static private void UpdateFileAssociation(string extension, bool associate)
diff --git a/UI/Config/InputConfig.cs b/UI/Config/InputConfig.cs
index f9f867fe9..522bae829 100644
--- a/UI/Config/InputConfig.cs
+++ b/UI/Config/InputConfig.cs
@@ -410,6 +410,7 @@ public enum ControllerType
//WonderSwan
WsController,
WsControllerVertical,
+ Pcv2Controller,
}
public static class ControllerTypeExtensions
@@ -500,6 +501,7 @@ public static bool CanConfigure(this ControllerType type)
case ControllerType.ColecoVisionController:
case ControllerType.WsController:
case ControllerType.WsControllerVertical:
+ case ControllerType.Pcv2Controller:
return true;
}
diff --git a/UI/Config/WsConfig.cs b/UI/Config/WsConfig.cs
index 985b60a13..f0510fbb8 100644
--- a/UI/Config/WsConfig.cs
+++ b/UI/Config/WsConfig.cs
@@ -13,8 +13,9 @@ public class WsConfig : BaseConfig
{
[Reactive] public ConsoleOverrideConfig ConfigOverrides { get; set; } = new();
- [Reactive] public ControllerConfig ControllerHorizontal { get; set; } = new();
- [Reactive] public ControllerConfig ControllerVertical { get; set; } = new();
+ [Reactive] public WsControllerConfig ControllerHorizontal { get; set; } = new();
+ [Reactive] public WsControllerConfig ControllerVertical { get; set; } = new();
+ [Reactive] public WsControllerConfig ControllerPcv2 { get; set; } = new();
[Reactive] public WsModel Model { get; set; } = WsModel.Auto;
[Reactive] public bool UseBootRom { get; set; } = false;
@@ -40,12 +41,14 @@ public void ApplyConfig()
{
ControllerHorizontal.Type = ControllerType.WsController;
ControllerVertical.Type = ControllerType.WsControllerVertical;
+ ControllerPcv2.Type = ControllerType.Pcv2Controller;
ConfigManager.Config.Video.ApplyConfig();
ConfigApi.SetWsConfig(new InteropWsConfig() {
ControllerHorizontal = ControllerHorizontal.ToInterop(),
ControllerVertical = ControllerVertical.ToInterop(),
+ ControllerPcv2 = ControllerPcv2.ToInterop(),
Model = Model,
UseBootRom = UseBootRom,
@@ -73,6 +76,7 @@ internal void InitializeDefaults(DefaultKeyMappingType defaultMappings)
{
ControllerHorizontal.InitDefaults(defaultMappings, ControllerType.WsController);
ControllerVertical.InitDefaults(defaultMappings, ControllerType.WsControllerVertical);
+ ControllerPcv2.InitDefaults(defaultMappings, ControllerType.Pcv2Controller);
}
}
@@ -81,6 +85,7 @@ public struct InteropWsConfig
{
public InteropControllerConfig ControllerHorizontal;
public InteropControllerConfig ControllerVertical;
+ public InteropControllerConfig ControllerPcv2;
public WsModel Model;
[MarshalAs(UnmanagedType.I1)] public bool UseBootRom;
@@ -108,7 +113,8 @@ public enum WsModel : byte
Auto,
Monochrome,
Color,
- SwanCrystal
+ SwanCrystal,
+ PocketChallenge
}
public enum WsAudioMode : byte
diff --git a/UI/Config/WsControllerConfig.cs b/UI/Config/WsControllerConfig.cs
new file mode 100644
index 000000000..8812ded79
--- /dev/null
+++ b/UI/Config/WsControllerConfig.cs
@@ -0,0 +1,125 @@
+using Mesen.Interop;
+using Mesen.Localization;
+using Mesen.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Mesen.Config
+{
+ public class WsControllerConfig : ControllerConfig
+ {
+ public new WsKeyMapping Mapping1 { get => (WsKeyMapping)_mapping1; set => _mapping1 = value; }
+ public new WsKeyMapping Mapping2 { get => (WsKeyMapping)_mapping2; set => _mapping2 = value; }
+ public new WsKeyMapping Mapping3 { get => (WsKeyMapping)_mapping3; set => _mapping3 = value; }
+ public new WsKeyMapping Mapping4 { get => (WsKeyMapping)_mapping4; set => _mapping4 = value; }
+
+ public WsControllerConfig()
+ {
+ _mapping1 = new WsKeyMapping();
+ _mapping2 = new WsKeyMapping();
+ _mapping3 = new WsKeyMapping();
+ _mapping4 = new WsKeyMapping();
+ }
+ }
+
+ public class WsKeyMapping : KeyMapping
+ {
+ public UInt16[]? Pcv2Buttons { get; set; } = null;
+
+ protected override UInt16[]? GetCustomButtons(ControllerType type)
+ {
+ return type switch {
+ ControllerType.Pcv2Controller => Pcv2Buttons,
+ _ => null
+ };
+ }
+
+ public override List ToCustomKeys(ControllerType type, int mappingIndex)
+ {
+ UInt16[]? buttonMappings = GetCustomButtons(type);
+ if(buttonMappings == null) {
+ if(GetDefaultCustomKeys(type, null) != null) {
+ if(mappingIndex == 0) {
+ SetDefaultKeys(type, null);
+ } else {
+ ClearKeys(type);
+ }
+ }
+
+ buttonMappings = GetCustomButtons(type);
+ if(buttonMappings == null) {
+ return new List();
+ }
+ }
+
+ List keys = type switch {
+ ControllerType.Pcv2Controller => Enum.GetValues().Select(val => new CustomKeyMapping(ResourceHelper.GetEnumText(val), buttonMappings, (int)val)).ToList(),
+ _ => new()
+ };
+
+ keys.Sort((a, b) => a.Name.CompareTo(b.Name));
+
+ return keys;
+ }
+
+ public override void ClearKeys(ControllerType type)
+ {
+ switch(type) {
+ case ControllerType.Pcv2Controller:
+ Pcv2Buttons = new UInt16[9];
+ break;
+
+ case ControllerType.WsController:
+ case ControllerType.WsControllerVertical:
+ base.ClearKeys(type);
+ break;
+ }
+ }
+
+ public override UInt16[]? GetDefaultCustomKeys(ControllerType type, KeyPresetType? preset)
+ {
+ switch(type) {
+ case ControllerType.Pcv2Controller:
+ return new UInt16[9] {
+ InputApi.GetKeyCode("Up Arrow"),
+ InputApi.GetKeyCode("Down Arrow"),
+ InputApi.GetKeyCode("Left Arrow"),
+ InputApi.GetKeyCode("Right Arrow"),
+ InputApi.GetKeyCode("S"),
+ InputApi.GetKeyCode("Z"),
+ InputApi.GetKeyCode("X"),
+ InputApi.GetKeyCode("C"),
+ InputApi.GetKeyCode("A")
+ };
+
+ default:
+ return null;
+ }
+ }
+
+ public override void SetDefaultKeys(ControllerType type, KeyPresetType? preset)
+ {
+ switch(type) {
+ case ControllerType.Pcv2Controller: Pcv2Buttons = GetDefaultCustomKeys(type, preset); break;
+
+ default:
+ base.SetDefaultKeys(type, preset);
+ break;
+ }
+ }
+ }
+
+ public enum Pcv2Buttons
+ {
+ Up,
+ Down,
+ Left,
+ Right,
+ Esc,
+ Pass,
+ Circle,
+ Clear,
+ View
+ };
+}
diff --git a/UI/Debugger/RegisterViewer/WsRegisterViewer.cs b/UI/Debugger/RegisterViewer/WsRegisterViewer.cs
index 40ac369a9..20bea0560 100644
--- a/UI/Debugger/RegisterViewer/WsRegisterViewer.cs
+++ b/UI/Debugger/RegisterViewer/WsRegisterViewer.cs
@@ -30,7 +30,7 @@ private static RegisterViewerTab GetPpuTab(ref WsState ws)
WsPpuState ppu = ws.Ppu;
byte volumeLevel = 0;
- if(ws.Model == WsModel.Monochrome) {
+ if(ws.Model == WsModel.Monochrome || ws.Model == WsModel.PocketChallenge) {
switch(ws.Apu.InternalMasterVolume) {
default: case 0: volumeLevel = 0; break;
case 1: volumeLevel = 2; break;
@@ -406,7 +406,7 @@ private static RegisterViewerTab GetMiscTab(ref WsState ws)
new RegEntry("$A0", "System Control"),
new RegEntry("$A0.0", "Boot ROM Disabled", mm.BootRomDisabled),
- new RegEntry("$A0.1", "Color System", ws.Model != WsModel.Monochrome),
+ new RegEntry("$A0.1", "Color System", ws.Model != WsModel.Monochrome && ws.Model != WsModel.PocketChallenge),
new RegEntry("$A0.2", "16-bit ROM Bus", mm.CartWordBus),
new RegEntry("$A0.3", "ROM Wait State", mm.SlowRom),
diff --git a/UI/Localization/resources.en.xml b/UI/Localization/resources.en.xml
index cadb77149..aa7cf4a28 100644
--- a/UI/Localization/resources.en.xml
+++ b/UI/Localization/resources.en.xml
@@ -644,6 +644,7 @@
Controllers
Controller (Horizontal)
Controller (Vertical)
+ Controller (Pocket Challenge V2)
Setup
@@ -2373,6 +2374,17 @@ E
Button - Right
Button - Left
+
+ Up
+ Down
+ Left
+ Right
+ Esc
+ View
+ Circle
+ Clear
+ Pass
+
NES - Front loader (NES-001)
NES - Top loader (NES-101)
@@ -2873,6 +2885,7 @@ E
Auto
+ Pocket Challenge V2
WonderSwan
WonderSwan Color
SwanCrystal
diff --git a/UI/Utilities/FileDialogHelper.cs b/UI/Utilities/FileDialogHelper.cs
index 88a965f13..d6102a6fc 100644
--- a/UI/Utilities/FileDialogHelper.cs
+++ b/UI/Utilities/FileDialogHelper.cs
@@ -55,7 +55,7 @@ public class FileDialogHelper
"*.pce", "*.sgx", "*.cue", "*.hes",
"*.sms", "*.gg", "*.sg", "*.col",
"*.gba",
- "*.ws", "*.wsc",
+ "*.ws", "*.wsc", "*.pc2",
"*.zip", "*.7z",
"*.ips", "*.bps"
}
@@ -68,7 +68,7 @@ public class FileDialogHelper
filter.Add(new FilePickerFileType("SMS / GG ROM files") { Patterns = new List() { "*.sms", "*.gg" } });
filter.Add(new FilePickerFileType("SG-1000 ROM files") { Patterns = new List() { "*.sg" } });
filter.Add(new FilePickerFileType("ColecoVision ROM files") { Patterns = new List() { "*.col" } });
- filter.Add(new FilePickerFileType("WonderSwan ROM files") { Patterns = new List() { "*.ws", "*.wsc" } });
+ filter.Add(new FilePickerFileType("WonderSwan ROM files") { Patterns = new List() { "*.ws", "*.wsc", "*.pc2" } });
filter.Add(new FilePickerFileType("Patch files (IPS/BPS)") { Patterns = new List() { "*.ips", "*.bps" } });
} else if(ext == FileDialogHelper.FirmwareExt) {
filter.Add(new FilePickerFileType("All firmware files") { Patterns = new List() { "*.sfc", "*.pce", "*.nes", "*.bin", "*.rom", "*.col", "*.sms", "*.gg", "*.gba", "*.ws", "*.wsc" } });
diff --git a/UI/Utilities/FolderHelper.cs b/UI/Utilities/FolderHelper.cs
index 0d64c644c..704651f4f 100644
--- a/UI/Utilities/FolderHelper.cs
+++ b/UI/Utilities/FolderHelper.cs
@@ -17,7 +17,7 @@ public static class FolderHelper
".pce", ".sgx", ".cue",
".sms", ".gg", ".sg", ".col",
".gba",
- ".ws", ".wsc"
+ ".ws", ".wsc", ".pc2"
};
public static bool IsRomFile(string path)
diff --git a/UI/ViewModels/MainMenuViewModel.cs b/UI/ViewModels/MainMenuViewModel.cs
index 6dbb553d6..d49a10003 100644
--- a/UI/ViewModels/MainMenuViewModel.cs
+++ b/UI/ViewModels/MainMenuViewModel.cs
@@ -431,6 +431,7 @@ private void InitOptionsMenu(MainWindow wnd)
GetWsModelMenuItem(WsModel.Monochrome),
GetWsModelMenuItem(WsModel.Color),
GetWsModelMenuItem(WsModel.SwanCrystal),
+ GetWsModelMenuItem(WsModel.PocketChallenge),
}
},
diff --git a/UI/ViewModels/WsConfigViewModel.cs b/UI/ViewModels/WsConfigViewModel.cs
index 49389a0e1..343b42f9b 100644
--- a/UI/ViewModels/WsConfigViewModel.cs
+++ b/UI/ViewModels/WsConfigViewModel.cs
@@ -19,6 +19,7 @@ public class WsConfigViewModel : DisposableViewModel
public ReactiveCommand