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 SetupPlayerHorizontal { get; } public ReactiveCommand SetupPlayerVertical { get; } + public ReactiveCommand SetupPlayerPcv2 { get; } public WsConfigViewModel() { @@ -27,8 +28,10 @@ public WsConfigViewModel() IObservable button1Enabled = this.WhenAnyValue(x => x.Config.ControllerHorizontal.Type, x => x.CanConfigure()); IObservable button2Enabled = this.WhenAnyValue(x => x.Config.ControllerVertical.Type, x => x.CanConfigure()); + IObservable button3Enabled = this.WhenAnyValue(x => x.Config.ControllerPcv2.Type, x => x.CanConfigure()); SetupPlayerHorizontal = ReactiveCommand.Create