Added WonderSwan support

This commit is contained in:
Sour
2024-09-19 21:19:43 +09:00
parent f7f9ce7623
commit c5406773a0
193 changed files with 18415 additions and 4858 deletions
+47
View File
@@ -754,12 +754,40 @@
<ClInclude Include="Shared\Video\VideoRenderer.h" />
<ClInclude Include="Shared\Audio\WaveRecorder.h" />
<ClInclude Include="Shared\Interfaces\IMouseManager.h" />
<ClInclude Include="WS\APU\WsApuCh1.h" />
<ClInclude Include="WS\APU\WsApuCh2.h" />
<ClInclude Include="WS\APU\WsApuCh3.h" />
<ClInclude Include="WS\APU\WsApuCh4.h" />
<ClInclude Include="WS\APU\WsHyperVoice.h" />
<ClInclude Include="WS\Carts\WsCart.h" />
<ClInclude Include="WS\Debugger\DummyWsCpu.h" />
<ClInclude Include="WS\Debugger\WsAssembler.h" />
<ClInclude Include="WS\Debugger\WsDebugger.h" />
<ClInclude Include="WS\Debugger\WsDisUtils.h" />
<ClInclude Include="WS\Debugger\WsEventManager.h" />
<ClInclude Include="WS\Debugger\WsPpuTools.h" />
<ClInclude Include="WS\Debugger\WsTraceLogger.h" />
<ClInclude Include="WS\APU\WsApu.h" />
<ClInclude Include="WS\WsConsole.h" />
<ClInclude Include="WS\WsController.h" />
<ClInclude Include="WS\WsControlManager.h" />
<ClInclude Include="WS\WsCpu.h" />
<ClInclude Include="WS\WsCpuPrefetch.h" />
<ClInclude Include="WS\WsDefaultVideoFilter.h" />
<ClInclude Include="WS\WsDmaController.h" />
<ClInclude Include="WS\WsEeprom.h" />
<ClInclude Include="WS\WsMemoryManager.h" />
<ClInclude Include="WS\WsPpu.h" />
<ClInclude Include="WS\WsSerial.h" />
<ClInclude Include="WS\WsTimer.h" />
<ClInclude Include="WS\WsTypes.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Debugger\Base6502Assembler.cpp" />
<ClCompile Include="Debugger\BaseEventManager.cpp" />
<ClCompile Include="Debugger\CdlManager.cpp" />
<ClCompile Include="Debugger\DisassemblySearch.cpp" />
<ClCompile Include="Debugger\ExpressionEvaluator.Ws.cpp" />
<ClCompile Include="Debugger\ExpressionEvaluator.Cx4.cpp" />
<ClCompile Include="Debugger\ExpressionEvaluator.Gameboy.cpp" />
<ClCompile Include="Debugger\ExpressionEvaluator.Gba.cpp" />
@@ -1032,6 +1060,25 @@
<ClCompile Include="Shared\Video\VideoDecoder.cpp" />
<ClCompile Include="Shared\Video\VideoRenderer.cpp" />
<ClCompile Include="Shared\Audio\WaveRecorder.cpp" />
<ClCompile Include="WS\APU\WsApu.cpp" />
<ClCompile Include="WS\Carts\WsCart.cpp" />
<ClCompile Include="WS\Debugger\DummyWsCpu.cpp" />
<ClCompile Include="WS\Debugger\WsDebugger.cpp" />
<ClCompile Include="WS\Debugger\WsDisUtils.cpp" />
<ClCompile Include="WS\Debugger\WsEventManager.cpp" />
<ClCompile Include="WS\Debugger\WsPpuTools.cpp" />
<ClCompile Include="WS\Debugger\WsTraceLogger.cpp" />
<ClCompile Include="WS\WsConsole.cpp" />
<ClCompile Include="WS\WsControlManager.cpp" />
<ClCompile Include="WS\WsCpu.cpp" />
<ClCompile Include="WS\WsCpuPrefetch.cpp" />
<ClCompile Include="WS\WsDefaultVideoFilter.cpp" />
<ClCompile Include="WS\WsDmaController.cpp" />
<ClCompile Include="WS\WsEeprom.cpp" />
<ClCompile Include="WS\WsMemoryManager.cpp" />
<ClCompile Include="WS\WsPpu.cpp" />
<ClCompile Include="WS\WsSerial.cpp" />
<ClCompile Include="WS\WsTimer.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="Core.ruleset" />
+153
View File
@@ -2841,6 +2841,87 @@
<ClInclude Include="SMS\Input\ColecoVisionController.h">
<Filter>SMS\Input</Filter>
</ClInclude>
<ClInclude Include="WS\WsTypes.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsCpu.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsConsole.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsMemoryManager.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsControlManager.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsDefaultVideoFilter.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\Debugger\WsDebugger.h">
<Filter>WS\Debugger</Filter>
</ClInclude>
<ClInclude Include="WS\Debugger\WsAssembler.h">
<Filter>WS\Debugger</Filter>
</ClInclude>
<ClInclude Include="WS\Debugger\WsDisUtils.h">
<Filter>WS\Debugger</Filter>
</ClInclude>
<ClInclude Include="WS\Debugger\WsEventManager.h">
<Filter>WS\Debugger</Filter>
</ClInclude>
<ClInclude Include="WS\Debugger\WsPpuTools.h">
<Filter>WS\Debugger</Filter>
</ClInclude>
<ClInclude Include="WS\Debugger\WsTraceLogger.h">
<Filter>WS\Debugger</Filter>
</ClInclude>
<ClInclude Include="WS\WsPpu.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsController.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\Carts\WsCart.h">
<Filter>WS\Carts</Filter>
</ClInclude>
<ClInclude Include="WS\WsTimer.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsDmaController.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsEeprom.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\WsCpuPrefetch.h">
<Filter>WS</Filter>
</ClInclude>
<ClInclude Include="WS\APU\WsApuCh1.h">
<Filter>WS\APU</Filter>
</ClInclude>
<ClInclude Include="WS\APU\WsApuCh2.h">
<Filter>WS\APU</Filter>
</ClInclude>
<ClInclude Include="WS\APU\WsApuCh3.h">
<Filter>WS\APU</Filter>
</ClInclude>
<ClInclude Include="WS\APU\WsApuCh4.h">
<Filter>WS\APU</Filter>
</ClInclude>
<ClInclude Include="WS\APU\WsApu.h">
<Filter>WS\APU</Filter>
</ClInclude>
<ClInclude Include="WS\APU\WsHyperVoice.h">
<Filter>WS\APU</Filter>
</ClInclude>
<ClInclude Include="WS\Debugger\DummyWsCpu.h">
<Filter>WS\Debugger</Filter>
</ClInclude>
<ClInclude Include="WS\WsSerial.h">
<Filter>WS</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Shared\Video\RotateFilter.cpp">
@@ -3116,6 +3197,66 @@
<ClCompile Include="GBA\Cart\GbaGpio.cpp">
<Filter>GBA\Cart</Filter>
</ClCompile>
<ClCompile Include="WS\WsConsole.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\Debugger\WsDebugger.cpp">
<Filter>WS\Debugger</Filter>
</ClCompile>
<ClCompile Include="WS\Debugger\WsDisUtils.cpp">
<Filter>WS\Debugger</Filter>
</ClCompile>
<ClCompile Include="WS\Debugger\WsEventManager.cpp">
<Filter>WS\Debugger</Filter>
</ClCompile>
<ClCompile Include="WS\Debugger\WsPpuTools.cpp">
<Filter>WS\Debugger</Filter>
</ClCompile>
<ClCompile Include="WS\Debugger\WsTraceLogger.cpp">
<Filter>WS\Debugger</Filter>
</ClCompile>
<ClCompile Include="WS\WsCpu.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="Debugger\ExpressionEvaluator.Ws.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="WS\WsControlManager.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\WsPpu.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\WsMemoryManager.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\Carts\WsCart.cpp">
<Filter>WS\Carts</Filter>
</ClCompile>
<ClCompile Include="WS\WsTimer.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\WsDmaController.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\WsCpuPrefetch.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\APU\WsApu.cpp">
<Filter>WS\APU</Filter>
</ClCompile>
<ClCompile Include="WS\Debugger\DummyWsCpu.cpp">
<Filter>WS\Debugger</Filter>
</ClCompile>
<ClCompile Include="WS\WsSerial.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\WsDefaultVideoFilter.cpp">
<Filter>WS</Filter>
</ClCompile>
<ClCompile Include="WS\WsEeprom.cpp">
<Filter>WS</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="PCE">
@@ -3226,5 +3367,17 @@
<Filter Include="GBA\Cart">
<UniqueIdentifier>{4cc96bee-c354-4a9b-9947-85959ccc072f}</UniqueIdentifier>
</Filter>
<Filter Include="WS">
<UniqueIdentifier>{8d532045-df57-4c7d-be0b-19680c2c2d10}</UniqueIdentifier>
</Filter>
<Filter Include="WS\Debugger">
<UniqueIdentifier>{14eeda13-2b16-4034-9006-7a7a347f548d}</UniqueIdentifier>
</Filter>
<Filter Include="WS\Carts">
<UniqueIdentifier>{ffbcd5be-271d-4c27-81f4-7684bfb2b8d9}</UniqueIdentifier>
</Filter>
<Filter Include="WS\APU">
<UniqueIdentifier>{e8ed2b40-f0e0-4dc5-89b7-9acc460359c8}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
+14 -1
View File
@@ -107,6 +107,19 @@ enum class RowDataType
CPSR,
Mode,
AX,
BX,
CX,
DX,
CS,
IP,
SS,
BP,
DS,
ES,
SI,
DI
};
struct TraceLogPpuState
@@ -256,7 +269,7 @@ protected:
template<typename T>
void WriteIntValue(string& output, T value, RowPart& rowPart)
{
string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value);
string str = rowPart.DisplayInHex ? DebugUtilities::AddressToHex(_cpuType, value) : std::to_string(value);
if(rowPart.MinWidth > (int)str.size()) {
if(rowPart.DisplayInHex) {
str = std::string(rowPart.MinWidth - str.size(), '0') + str;
+7 -2
View File
@@ -201,6 +201,7 @@ enum class TileFormat
SmsSgBpp1,
GbaBpp4,
GbaBpp8,
WsBpp4Packed
};
enum class TileLayout
@@ -332,7 +333,9 @@ enum class BreakSource
GbaInvalidOpCode,
GbaNopLoad,
GbaUnalignedMemoryAccess
GbaUnalignedMemoryAccess,
BreakOnUndefinedOpCode
};
struct BreakEvent
@@ -476,6 +479,8 @@ struct DebugControllerState
bool Y;
bool L;
bool R;
bool U;
bool D;
bool Up;
bool Down;
bool Left;
@@ -485,6 +490,6 @@ struct DebugControllerState
bool HasPressedButton()
{
return A || B || X || Y || L || R || Up || Down || Left || Right || Select || Start;
return A || B || X || Y || L || R || U || D || Up || Down || Left || Right || Select || Start;
}
};
+20 -2
View File
@@ -21,6 +21,7 @@ public:
case CpuType::Pce: return MemoryType::PceMemory;
case CpuType::Sms: return MemoryType::SmsMemory;
case CpuType::Gba: return MemoryType::GbaMemory;
case CpuType::Ws: return MemoryType::WsMemory;
}
throw std::runtime_error("Invalid CPU type");
@@ -40,6 +41,7 @@ public:
case CpuType::Pce: return 4;
case CpuType::Sms: return 4;
case CpuType::Gba: return 8;
case CpuType::Ws: return 5;
}
throw std::runtime_error("Invalid CPU type");
@@ -144,6 +146,16 @@ public:
case MemoryType::GbaPaletteRam:
return CpuType::Gba;
case MemoryType::WsMemory:
case MemoryType::WsPrgRom:
case MemoryType::WsWorkRam:
case MemoryType::WsCartRam:
case MemoryType::WsCartEeprom:
case MemoryType::WsBootRom:
case MemoryType::WsInternalEeprom:
case MemoryType::WsPort:
return CpuType::Ws;
default:
throw std::runtime_error("Invalid CPU type");
}
@@ -156,7 +168,7 @@ public:
static constexpr MemoryType GetLastCpuMemoryType()
{
return MemoryType::GbaMemory;
return MemoryType::WsMemory;
}
static constexpr bool IsPpuMemory(MemoryType memType)
@@ -214,6 +226,7 @@ public:
case MemoryType::SmsBootRom:
case MemoryType::GbaPrgRom:
case MemoryType::GbaBootRom:
case MemoryType::WsPrgRom:
return true;
default:
@@ -235,6 +248,7 @@ public:
case MemoryType::SnesRegister:
case MemoryType::SmsCartRam:
case MemoryType::GbaSaveRam:
case MemoryType::WsCartRam:
return false;
default:
@@ -244,7 +258,7 @@ public:
static constexpr CpuType GetLastCpuType()
{
return CpuType::Gba;
return CpuType::Ws;
}
static string AddressToHex(CpuType cpuType, int32_t address)
@@ -252,8 +266,12 @@ public:
int size = GetProgramCounterSize(cpuType);
if(size == 4) {
return HexUtilities::ToHex((uint16_t)address);
} else if(size == 5) {
return HexUtilities::ToHex20(address);
} else if(size == 6) {
return HexUtilities::ToHex24(address);
} else if(size == 8) {
return HexUtilities::ToHex32(address);
} else {
return HexUtilities::ToHex(address);
}
+58 -3
View File
@@ -40,6 +40,8 @@
#include "SMS/SmsTypes.h"
#include "GBA/Debugger/GbaDebugger.h"
#include "GBA/GbaTypes.h"
#include "WS/Debugger/WsDebugger.h"
#include "WS/WsTypes.h"
#include "Shared/BaseControlManager.h"
#include "Shared/EmuSettings.h"
#include "Shared/Audio/SoundMixer.h"
@@ -93,6 +95,7 @@ Debugger::Debugger(Emulator* emu, IConsole* console)
case CpuType::Pce: debugger.reset(new PceDebugger(this)); break;
case CpuType::Sms: debugger.reset(new SmsDebugger(this)); break;
case CpuType::Gba: debugger.reset(new GbaDebugger(this)); break;
case CpuType::Ws: debugger.reset(new WsDebugger(this)); break;
default: throw std::runtime_error("Unsupported CPU type");
}
@@ -172,6 +175,7 @@ uint64_t Debugger::GetCpuCycleCount()
case CpuType::Pce: return GetDebugger<type, PceDebugger>()->GetCpuCycleCount();
case CpuType::Sms: return GetDebugger<type, SmsDebugger>()->GetCpuCycleCount();
case CpuType::Gba: return GetDebugger<type, GbaDebugger>()->GetCpuCycleCount();
case CpuType::Ws: return GetDebugger<type, WsDebugger>()->GetCpuCycleCount();
default: return 0; break;
}
}
@@ -218,6 +222,7 @@ void Debugger::ProcessInstruction()
case CpuType::Pce: GetDebugger<type, PceDebugger>()->ProcessInstruction(); break;
case CpuType::Sms: GetDebugger<type, SmsDebugger>()->ProcessInstruction(); break;
case CpuType::Gba: GetDebugger<type, GbaDebugger>()->ProcessInstruction(); break;
case CpuType::Ws: GetDebugger<type, WsDebugger>()->ProcessInstruction(); break;
}
debugger->AllowChangeProgramCounter = false;
@@ -250,6 +255,11 @@ void Debugger::ProcessMemoryRead(uint32_t addr, T& value, MemoryOperationType op
case CpuType::Pce: GetDebugger<CpuType::Pce, PceDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Sms: GetDebugger<CpuType::Sms, SmsDebugger>()->ProcessRead(addr, value, opType); break;
case CpuType::Gba: GetDebugger<CpuType::Gba, GbaDebugger>()->ProcessRead<accessWidth>(addr, value, opType); break;
case CpuType::Ws:
if constexpr(accessWidth <= 2) {
GetDebugger<CpuType::Ws, WsDebugger>()->ProcessRead<accessWidth>(addr, value, opType);
}
break;
}
if(_scriptManager->HasCpuMemoryCallbacks()) {
@@ -277,6 +287,11 @@ bool Debugger::ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType o
case CpuType::Pce: GetDebugger<CpuType::Pce, PceDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Sms: GetDebugger<CpuType::Sms, SmsDebugger>()->ProcessWrite(addr, value, opType); break;
case CpuType::Gba: GetDebugger<CpuType::Gba, GbaDebugger>()->ProcessWrite<accessWidth>(addr, value, opType); break;
case CpuType::Ws:
if constexpr(accessWidth <= 2) {
GetDebugger<CpuType::Ws, WsDebugger>()->ProcessWrite<accessWidth>(addr, value, opType);
}
break;
}
if(_scriptManager->HasCpuMemoryCallbacks()) {
@@ -291,6 +306,8 @@ void Debugger::ProcessMemoryAccess(uint32_t addr, T& value)
{
IDebugger* debugger = _debuggers[(int)cpuType].Debugger.get();
constexpr int accessWidth = std::is_same<T, uint16_t>::value ? 2 : 1;
if(debugger->IsStepBack()) {
return;
}
@@ -299,17 +316,22 @@ void Debugger::ProcessMemoryAccess(uint32_t addr, T& value)
MemoryOperationInfo operation(addr, value, opType, memType);
if constexpr(opType == MemoryOperationType::Write) {
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _emu->GetMasterClock());
_memoryAccessCounter->ProcessMemoryWrite<accessWidth>(addressInfo, _emu->GetMasterClock());
} else {
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _emu->GetMasterClock());
_memoryAccessCounter->ProcessMemoryRead<accessWidth>(addressInfo, _emu->GetMasterClock());
}
switch(cpuType) {
default: break;
case CpuType::Sms: GetDebugger<CpuType::Sms, SmsDebugger>()->ProcessMemoryAccess<opType>(addr, value, memType); break;
case CpuType::Ws: GetDebugger<CpuType::Ws, WsDebugger>()->ProcessMemoryAccess<opType, T>(addr, value, memType); break;
}
ProcessBreakConditions(cpuType, *debugger->GetStepRequest(), debugger->GetBreakpointManager(), operation, addressInfo);
if(_scriptManager->HasCpuMemoryCallbacks()) {
ProcessScripts<cpuType>(addr, value, memType, opType);
}
ProcessBreakConditions<accessWidth>(cpuType, *debugger->GetStepRequest(), debugger->GetBreakpointManager(), operation, addressInfo);
}
template<CpuType type>
@@ -420,6 +442,7 @@ void Debugger::ProcessPpuCycle()
case CpuType::Pce: GetDebugger<type, PceDebugger>()->ProcessPpuCycle(); break;
case CpuType::Sms: GetDebugger<type, SmsDebugger>()->ProcessPpuCycle(); break;
case CpuType::Gba: GetDebugger<type, GbaDebugger>()->ProcessPpuCycle(); break;
case CpuType::Ws: GetDebugger<type, WsDebugger>()->ProcessPpuCycle(); break;
default: throw std::runtime_error("Invalid cpu type");
}
}
@@ -627,6 +650,7 @@ void Debugger::PauseOnNextFrame()
case CpuType::Pce: Step(CpuType::Pce, 243, StepType::SpecificScanline, BreakSource::PpuStep); break;
case CpuType::Sms: Step(CpuType::Sms, 240, StepType::SpecificScanline, BreakSource::PpuStep); break;
case CpuType::Gba: Step(CpuType::Gba, 160, StepType::SpecificScanline, BreakSource::PpuStep); break;
case CpuType::Ws: Step(CpuType::Ws, 145, StepType::SpecificScanline, BreakSource::PpuStep); break;
}
}
@@ -714,6 +738,7 @@ bool Debugger::IsDebugWindowOpened(CpuType cpuType)
case CpuType::Pce: return _settings->CheckDebuggerFlag(DebuggerFlags::PceDebuggerEnabled);
case CpuType::Sms: return _settings->CheckDebuggerFlag(DebuggerFlags::SmsDebuggerEnabled);
case CpuType::Gba: return _settings->CheckDebuggerFlag(DebuggerFlags::GbaDebuggerEnabled);
case CpuType::Ws: return _settings->CheckDebuggerFlag(DebuggerFlags::WsDebuggerEnabled);
}
return false;
@@ -764,6 +789,7 @@ void Debugger::GetCpuState(BaseState &dstState, CpuType cpuType)
case CpuType::Pce: memcpy(&dstState, &srcState, sizeof(PceCpuState)); break;
case CpuType::Sms: memcpy(&dstState, &srcState, sizeof(SmsCpuState)); break;
case CpuType::Gba: memcpy(&dstState, &srcState, sizeof(GbaCpuState)); break;
case CpuType::Ws: memcpy(&dstState, &srcState, sizeof(WsCpuState)); break;
}
}
@@ -783,6 +809,7 @@ void Debugger::SetCpuState(BaseState& srcState, CpuType cpuType)
case CpuType::Pce: memcpy(&dstState, &srcState, sizeof(PceCpuState)); break;
case CpuType::Sms: memcpy(&dstState, &srcState, sizeof(SmsCpuState)); break;
case CpuType::Gba: memcpy(&dstState, &srcState, sizeof(GbaCpuState)); break;
case CpuType::Ws: memcpy(&dstState, &srcState, sizeof(WsCpuState)); break;
}
}
@@ -828,6 +855,11 @@ void Debugger::GetPpuState(BaseState& state, CpuType cpuType)
GetDebugger<CpuType::Gba, GbaDebugger>()->GetPpuState(state);
break;
}
case CpuType::Ws: {
GetDebugger<CpuType::Ws, WsDebugger>()->GetPpuState(state);
break;
}
}
}
@@ -869,6 +901,11 @@ void Debugger::SetPpuState(BaseState& state, CpuType cpuType)
GetDebugger<CpuType::Gba, GbaDebugger>()->SetPpuState(state);
break;
}
case CpuType::Ws: {
GetDebugger<CpuType::Ws, WsDebugger>()->SetPpuState(state);
break;
}
}
}
@@ -982,6 +1019,7 @@ bool Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption str
case CpuType::Pce: return GetDebugger<CpuType::Pce, PceDebugger>()->SaveRomToDisk(filename, saveAsIps, stripOption);
case CpuType::Sms: return GetDebugger<CpuType::Sms, SmsDebugger>()->SaveRomToDisk(filename, saveAsIps, stripOption);
case CpuType::Gba: return GetDebugger<CpuType::Gba, GbaDebugger>()->SaveRomToDisk(filename, saveAsIps, stripOption);
case CpuType::Ws: return GetDebugger<CpuType::Ws, WsDebugger>()->SaveRomToDisk(filename, saveAsIps, stripOption);
}
return false;
@@ -1100,6 +1138,7 @@ template void Debugger::ProcessInstruction<CpuType::Nes>();
template void Debugger::ProcessInstruction<CpuType::Pce>();
template void Debugger::ProcessInstruction<CpuType::Sms>();
template void Debugger::ProcessInstruction<CpuType::Gba>();
template void Debugger::ProcessInstruction<CpuType::Ws>();
template void Debugger::ProcessMemoryRead<CpuType::Snes>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Sa1>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
@@ -1116,6 +1155,8 @@ template void Debugger::ProcessMemoryRead<CpuType::Sms>(uint32_t addr, uint8_t&
template void Debugger::ProcessMemoryRead<CpuType::Gba, 1>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Gba, 2>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Gba, 4>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Ws, 1>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Ws, 2>(uint32_t addr, uint16_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Snes>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Sa1>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
@@ -1131,6 +1172,8 @@ template bool Debugger::ProcessMemoryWrite<CpuType::Sms>(uint32_t addr, uint8_t&
template bool Debugger::ProcessMemoryWrite<CpuType::Gba, 1>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Gba, 2>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Gba, 4>(uint32_t addr, uint32_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Ws, 1>(uint32_t addr, uint8_t& value, MemoryOperationType opType);
template bool Debugger::ProcessMemoryWrite<CpuType::Ws, 2>(uint32_t addr, uint16_t& value, MemoryOperationType opType);
template void Debugger::ProcessMemoryAccess<CpuType::Pce, MemoryType::PceAdpcmRam, MemoryOperationType::Write>(uint32_t addr, uint8_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Pce, MemoryType::PceAdpcmRam, MemoryOperationType::Read>(uint32_t addr, uint8_t& value);
@@ -1139,6 +1182,15 @@ template void Debugger::ProcessMemoryAccess<CpuType::Pce, MemoryType::PceArcadeC
template void Debugger::ProcessMemoryAccess<CpuType::Sms, MemoryType::SmsPort, MemoryOperationType::Write>(uint32_t addr, uint8_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Sms, MemoryType::SmsPort, MemoryOperationType::Read>(uint32_t addr, uint8_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Write>(uint32_t addr, uint8_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Read>(uint32_t addr, uint8_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Write>(uint32_t addr, uint16_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Read>(uint32_t addr, uint16_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsInternalEeprom, MemoryOperationType::Read>(uint32_t addr, uint16_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsInternalEeprom, MemoryOperationType::Write>(uint32_t addr, uint16_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsCartEeprom, MemoryOperationType::Read>(uint32_t addr, uint16_t& value);
template void Debugger::ProcessMemoryAccess<CpuType::Ws, MemoryType::WsCartEeprom, MemoryOperationType::Write>(uint32_t addr, uint16_t& value);
template void Debugger::ProcessIdleCycle<CpuType::Snes>();
template void Debugger::ProcessIdleCycle<CpuType::Sa1>();
@@ -1147,6 +1199,7 @@ template void Debugger::ProcessHaltedCpu<CpuType::Spc>();
template void Debugger::ProcessHaltedCpu<CpuType::Gameboy>();
template void Debugger::ProcessHaltedCpu<CpuType::Sms>();
template void Debugger::ProcessHaltedCpu<CpuType::Gba>();
template void Debugger::ProcessHaltedCpu<CpuType::Ws>();
template void Debugger::ProcessInterrupt<CpuType::Snes>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessInterrupt<CpuType::Sa1>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
@@ -1155,6 +1208,7 @@ template void Debugger::ProcessInterrupt<CpuType::Nes>(uint32_t originalPc, uint
template void Debugger::ProcessInterrupt<CpuType::Pce>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessInterrupt<CpuType::Sms>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessInterrupt<CpuType::Gba>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessInterrupt<CpuType::Ws>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessPpuRead<CpuType::Snes>(uint16_t addr, uint8_t& value, MemoryType memoryType, MemoryOperationType opType);
template void Debugger::ProcessPpuRead<CpuType::Gameboy>(uint16_t addr, uint8_t& value, MemoryType memoryType, MemoryOperationType opType);
@@ -1176,6 +1230,7 @@ template void Debugger::ProcessPpuCycle<CpuType::Nes>();
template void Debugger::ProcessPpuCycle<CpuType::Pce>();
template void Debugger::ProcessPpuCycle<CpuType::Sms>();
template void Debugger::ProcessPpuCycle<CpuType::Gba>();
template void Debugger::ProcessPpuCycle<CpuType::Ws>();
template void Debugger::ProcessBreakConditions<1>(CpuType sourceCpu, StepRequest& step, BreakpointManager* bpManager, MemoryOperationInfo& operation, AddressInfo& addressInfo);
template void Debugger::ProcessBreakConditions<2>(CpuType sourceCpu, StepRequest& step, BreakpointManager* bpManager, MemoryOperationInfo& operation, AddressInfo& addressInfo);
+4 -1
View File
@@ -5,6 +5,8 @@ enum class VectorType
{
Indirect,
Direct,
x86,
x86WithOffset
};
struct CpuVectorDefinition
@@ -25,6 +27,7 @@ struct DebuggerFeatures
bool CallStack;
bool CpuCycleStep;
uint8_t IrqVectorOffset;
uint8_t CpuVectorCount;
CpuVectorDefinition CpuVectors[10];
CpuVectorDefinition CpuVectors[16];
};
+23
View File
@@ -18,6 +18,8 @@
#include "NES/NesTypes.h"
#include "PCE/PceTypes.h"
#include "SMS/SmsTypes.h"
#include "WS/WsTypes.h"
#include "WS/Debugger/WsDisUtils.h"
#include "Shared/EmuSettings.h"
#include "Utilities/FastString.h"
#include "Utilities/HexUtilities.h"
@@ -88,6 +90,7 @@ void Disassembler::ResetPrgCache()
InitSource(MemoryType::PcePrgRom);
InitSource(MemoryType::SmsPrgRom);
InitSource(MemoryType::GbaPrgRom);
InitSource(MemoryType::WsPrgRom);
}
void Disassembler::InvalidateCache(AddressInfo addrInfo, CpuType type)
@@ -561,6 +564,26 @@ void Disassembler::GetLineData(DisassemblyResult& row, CpuType type, MemoryType
}
break;
}
case CpuType::Ws:
{
WsCpuState state = (WsCpuState&)_debugger->GetCpuStateRef(lineCpuType);
WsDisUtils::UpdateAddressCsIp(row.CpuAddress, state);
CodeDataLogger* cdl = cdlManager->GetCodeDataLogger(row.Address.Type);
if(!disInfo.IsInitialized()) {
disInfo = DisassemblyInfo(row.Address.Address, 0, CpuType::Ws, row.Address.Type, _memoryDumper);
} else {
data.Flags |= (!cdl || cdl->IsCode(data.AbsoluteAddress.Address)) ? LineFlags::VerifiedCode : LineFlags::UnexecutedCode;
}
data.OpSize = disInfo.GetOpSize();
data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType);
if(showMemoryValues && data.EffectiveAddress.ValueSize >= 0) {
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType);
}
break;
}
}
if(!showMemoryValues) {
+10 -1
View File
@@ -18,6 +18,7 @@
#include "PCE/Debugger/PceDisUtils.h"
#include "SMS/Debugger/SmsDisUtils.h"
#include "GBA/Debugger/GbaDisUtils.h"
#include "WS/Debugger/WsDisUtils.h"
#include "Shared/EmuSettings.h"
DisassemblyInfo::DisassemblyInfo()
@@ -38,7 +39,7 @@ void DisassemblyInfo::Initialize(uint32_t cpuAddress, uint8_t cpuFlags, CpuType
_opSize = GetOpSize(_byteCode[0], _flags, _cpuType, cpuAddress, memType, memoryDumper);
for(int i = 1; i < _opSize; i++) {
for(int i = 1; i < _opSize && i < 8; i++) {
_byteCode[i] = memoryDumper->GetMemoryValue(memType, cpuAddress+i);
}
@@ -77,6 +78,7 @@ void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr, LabelMana
case CpuType::Pce: PceDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
case CpuType::Sms: SmsDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
case CpuType::Gba: GbaDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
case CpuType::Ws: WsDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
default:
throw std::runtime_error("GetDisassembly - Unsupported CPU type");
@@ -111,6 +113,7 @@ EffectiveAddressInfo DisassemblyInfo::GetEffectiveAddress(Debugger *debugger, vo
case CpuType::Pce: return PceDisUtils::GetEffectiveAddress(*this, (PceConsole*)debugger->GetConsole(), *(PceCpuState*)cpuState);
case CpuType::Sms: return SmsDisUtils::GetEffectiveAddress(*this, (SmsConsole*)debugger->GetConsole(), *(SmsCpuState*)cpuState);
case CpuType::Gba: return GbaDisUtils::GetEffectiveAddress(*this, (GbaConsole*)debugger->GetConsole(), *(GbaCpuState*)cpuState);
case CpuType::Ws: return WsDisUtils::GetEffectiveAddress(*this, (WsConsole*)debugger->GetConsole(), *(WsCpuState*)cpuState);
}
throw std::runtime_error("GetEffectiveAddress - Unsupported CPU type");
@@ -134,6 +137,7 @@ uint32_t DisassemblyInfo::GetFullOpCode()
case CpuType::NecDsp: return _byteCode[0] | (_byteCode[1] << 8) | (_byteCode[2] << 16);
case CpuType::Cx4: return _byteCode[1];
case CpuType::Gba: return _byteCode[0] | (_byteCode[1] << 8) | (_opSize == 4 ? ((_byteCode[2] << 16) | (_byteCode[3] << 24)) : 0);
case CpuType::Ws: return WsDisUtils::GetFullOpCode(*this);
}
}
@@ -183,6 +187,7 @@ uint8_t DisassemblyInfo::GetOpSize(uint32_t opCode, uint8_t flags, CpuType type,
case CpuType::Pce: return PceDisUtils::GetOpSize(opCode);
case CpuType::Sms: return SmsDisUtils::GetOpSize(opCode, cpuAddress, memType, memoryDumper);
case CpuType::Gba: return GbaDisUtils::GetOpSize(opCode, flags);
case CpuType::Ws: return WsDisUtils::GetOpSize(cpuAddress, memType, memoryDumper);
}
throw std::runtime_error("GetOpSize - Unsupported CPU type");
@@ -202,6 +207,7 @@ bool DisassemblyInfo::IsJumpToSub()
case CpuType::Pce: return PceDisUtils::IsJumpToSub(GetOpCode());
case CpuType::Sms: return SmsDisUtils::IsJumpToSub(GetOpCode());
case CpuType::Gba: return GbaDisUtils::IsJumpToSub(GetFullOpCode<CpuType::Gba>(), _flags);
case CpuType::Ws: return WsDisUtils::IsJumpToSub(GetFullOpCode<CpuType::Ws>());
}
throw std::runtime_error("IsJumpToSub - Unsupported CPU type");
@@ -221,6 +227,7 @@ bool DisassemblyInfo::IsReturnInstruction()
case CpuType::Pce: return PceDisUtils::IsReturnInstruction(GetOpCode());
case CpuType::Sms: return SmsDisUtils::IsReturnInstruction(_byteCode[0] | (_byteCode[1] << 8));
case CpuType::Gba: return GbaDisUtils::IsReturnInstruction(GetFullOpCode<CpuType::Gba>(), _flags);
case CpuType::Ws: return WsDisUtils::IsReturnInstruction(GetFullOpCode<CpuType::Ws>());
}
throw std::runtime_error("IsReturnInstruction - Unsupported CPU type");
@@ -255,6 +262,7 @@ bool DisassemblyInfo::IsUnconditionalJump()
case CpuType::Pce: return PceDisUtils::IsUnconditionalJump(GetOpCode());
case CpuType::Sms: return SmsDisUtils::IsUnconditionalJump(GetOpCode());
case CpuType::Gba: return GbaDisUtils::IsUnconditionalJump(GetFullOpCode<CpuType::Gba>(), _flags);
case CpuType::Ws: return WsDisUtils::IsUnconditionalJump(GetFullOpCode<CpuType::Ws>());
}
throw std::runtime_error("IsUnconditionalJump - Unsupported CPU type");
@@ -279,6 +287,7 @@ bool DisassemblyInfo::IsJump()
case CpuType::Pce: return PceDisUtils::IsConditionalJump(GetOpCode());
case CpuType::Sms: return SmsDisUtils::IsConditionalJump(GetOpCode());
case CpuType::Gba: return GbaDisUtils::IsConditionalJump(GetFullOpCode<CpuType::Gba>(), _flags);
case CpuType::Ws: return WsDisUtils::IsConditionalJump(GetFullOpCode<CpuType::Ws>());
}
throw std::runtime_error("IsJump - Unsupported CPU type");
+90
View File
@@ -0,0 +1,90 @@
#include "pch.h"
#include "Debugger/ExpressionEvaluator.h"
#include "WS/Debugger/WsDebugger.h"
#include "WS/WsTypes.h"
unordered_map<string, int64_t>& ExpressionEvaluator::GetWsTokens()
{
static unordered_map<string, int64_t> supportedTokens = {
{ "ax", EvalValues::RegAX },
{ "bx", EvalValues::RegBX },
{ "cx", EvalValues::RegCX },
{ "dx", EvalValues::RegDX },
{ "al", EvalValues::RegAL },
{ "bl", EvalValues::RegBL },
{ "cl", EvalValues::RegCL },
{ "dl", EvalValues::RegDL },
{ "ah", EvalValues::RegAH },
{ "bh", EvalValues::RegBH },
{ "ch", EvalValues::RegCH },
{ "dh", EvalValues::RegDH },
{ "cs", EvalValues::RegCS },
{ "ds", EvalValues::RegDS },
{ "es", EvalValues::RegES },
{ "ss", EvalValues::RegSS },
{ "si", EvalValues::RegSI },
{ "di", EvalValues::RegDI },
{ "bp", EvalValues::RegBP },
{ "ip", EvalValues::RegIP },
{ "f", EvalValues::RegF },
{ "sp", EvalValues::RegSP },
{ "pc", EvalValues::RegPC },
{ "frame", EvalValues::PpuFrameCount },
{ "cycle", EvalValues::PpuCycle },
{ "scanline", EvalValues::PpuScanline },
};
return supportedTokens;
}
int64_t ExpressionEvaluator::GetWsTokenValue(int64_t token, EvalResultType& resultType)
{
auto ppu = [this]() -> WsPpuState {
WsPpuState ppu;
((WsDebugger*)_cpuDebugger)->GetPpuState(ppu);
return ppu;
};
WsCpuState& s = (WsCpuState&)((WsDebugger*)_cpuDebugger)->GetState();
switch(token) {
case EvalValues::RegAX: return s.AX;
case EvalValues::RegBX: return s.BX;
case EvalValues::RegCX: return s.CX;
case EvalValues::RegDX: return s.DX;
case EvalValues::RegAL: return s.AX & 0xFF;
case EvalValues::RegBL: return s.BX & 0xFF;
case EvalValues::RegCL: return s.CX & 0xFF;
case EvalValues::RegDL: return s.DX & 0xFF;
case EvalValues::RegAH: return s.AX >> 8;
case EvalValues::RegBH: return s.BX >> 8;
case EvalValues::RegCH: return s.CX >> 8;
case EvalValues::RegDH: return s.DX >> 8;
case EvalValues::RegCS: return s.CS;
case EvalValues::RegDS: return s.DS;
case EvalValues::RegES: return s.ES;
case EvalValues::RegSS: return s.SS;
case EvalValues::RegSI: return s.SI;
case EvalValues::RegDI: return s.DI;
case EvalValues::RegBP: return s.BP;
case EvalValues::RegIP: return s.IP;
case EvalValues::RegF: return s.Flags.Get();
case EvalValues::RegSP: return s.SP;
case EvalValues::RegPC: return (s.CS << 4) + s.IP;
case EvalValues::PpuFrameCount: return ppu().FrameCount;
case EvalValues::PpuCycle: return ppu().Cycle;
case EvalValues::PpuScanline: return ppu().Scanline;
default: return 0;
}
}
+14 -1
View File
@@ -70,6 +70,7 @@ unordered_map<string, int64_t>* ExpressionEvaluator::GetAvailableTokens()
case CpuType::Pce: return &GetPceTokens();
case CpuType::Sms: return &GetSmsTokens();
case CpuType::Gba: return &GetGbaTokens();
case CpuType::Ws: return &GetWsTokens();
}
return nullptr;
@@ -156,10 +157,21 @@ string ExpressionEvaluator::GetNextToken(string expression, size_t &pos, Express
string output;
success = true;
if(expression.empty()) {
success = false;
return "";
}
char c = std::tolower(expression[pos]);
if(c == '$') {
char nextChar = (pos + 1 < expression.size()) ? std::tolower(expression[pos + 1]) : '\0';
if(c == '$' || (c == '0' && nextChar == 'x')) {
//Hex numbers
pos++;
if(c == '0') {
//Skip over 'x' in '0x' prefix
pos++;
}
for(size_t len = expression.size(); pos < len; pos++) {
c = std::tolower(expression[pos]);
if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
@@ -413,6 +425,7 @@ int64_t ExpressionEvaluator::Evaluate(ExpressionData &data, EvalResultType &resu
case CpuType::Pce: token = GetPceTokenValue(token, resultType); break;
case CpuType::Sms: token = GetSmsTokenValue(token, resultType); break;
case CpuType::Gba: token = GetGbaTokenValue(token, resultType); break;
case CpuType::Ws: token = GetWsTokenValue(token, resultType); break;
}
}
break;
+28
View File
@@ -177,6 +177,31 @@ enum EvalValues : int64_t
CPSR,
RegAX,
RegBX,
RegCX,
RegDX,
RegAL,
RegBL,
RegCL,
RegDL,
RegAH,
RegBH,
RegCH,
RegDH,
RegCS,
RegDS,
RegES,
RegSS,
RegSI,
RegDI,
RegBP,
RegIP,
FirstLabelIndex,
};
@@ -258,6 +283,9 @@ private:
unordered_map<string, int64_t>& GetGbaTokens();
int64_t GetGbaTokenValue(int64_t token, EvalResultType& resultType);
unordered_map<string, int64_t>& GetWsTokens();
int64_t GetWsTokenValue(int64_t token, EvalResultType& resultType);
bool ReturnBool(int64_t value, EvalResultType& resultType);
int64_t ProcessSharedTokens(string token);
+21
View File
@@ -22,6 +22,8 @@
#include "SMS/SmsMemoryManager.h"
#include "GBA/GbaConsole.h"
#include "GBA/GbaMemoryManager.h"
#include "WS/WsConsole.h"
#include "WS/WsMemoryManager.h"
#include "Shared/Video/VideoDecoder.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DebugBreakHelper.h"
@@ -51,6 +53,8 @@ MemoryDumper::MemoryDumper(Debugger* debugger)
_smsConsole = sms;
} else if(GbaConsole* gba = dynamic_cast<GbaConsole*>(console)) {
_gbaConsole = gba;
} else if(WsConsole* ws = dynamic_cast<WsConsole*>(console)) {
_wsConsole = ws;
}
for(int i = 0; i < DebugUtilities::GetMemoryTypeCount(); i++) {
@@ -97,8 +101,10 @@ uint32_t MemoryDumper::GetMemorySize(MemoryType type)
case MemoryType::PceMemory: return 0x10000;
case MemoryType::SmsMemory: return 0x10000;
case MemoryType::GbaMemory: return 0x10000000;
case MemoryType::WsMemory: return 0x100000;
case MemoryType::SnesRegister: return 0x10000;
case MemoryType::SmsPort: return 0x100;
case MemoryType::WsPort: return 0x10000;
default: return _emu->GetMemory(type).Size;
}
}
@@ -207,6 +213,17 @@ void MemoryDumper::GetMemoryState(MemoryType type, uint8_t *buffer)
}
break;
}
case MemoryType::WsMemory: {
if(_wsConsole) {
WsMemoryManager* memManager = _wsConsole->GetMemoryManager();
for(int i = 0; i <= 0xFFFFF; i++) {
buffer[i] = memManager->DebugRead(i);
}
}
break;
}
default:
uint8_t* src = GetMemoryBuffer(type);
if(src) {
@@ -257,6 +274,7 @@ void MemoryDumper::InternalSetMemoryValues(MemoryType originalMemoryType, uint32
case MemoryType::PceMemory: _pceConsole->GetMemoryManager()->DebugWrite(address, value); break;
case MemoryType::SmsMemory: _smsConsole->GetMemoryManager()->DebugWrite(address, value); break;
case MemoryType::GbaMemory: _gbaConsole->GetMemoryManager()->DebugWrite(address, value); break;
case MemoryType::WsMemory: _wsConsole->GetMemoryManager()->DebugWrite(address, value); break;
case MemoryType::SpcDspRegisters: _spc->DebugWriteDspReg(address, value); break;
default:
@@ -349,7 +367,10 @@ uint8_t MemoryDumper::InternalGetMemoryValue(MemoryType memoryType, uint32_t add
case MemoryType::NesPpuMemory: return _nesConsole->DebugReadVram(address);
case MemoryType::PceMemory: return _pceConsole->GetMemoryManager()->DebugRead(address);
case MemoryType::SmsMemory: return _smsConsole->GetMemoryManager()->DebugRead(address);
case MemoryType::SmsPort: return _smsConsole->GetMemoryManager()->DebugReadPort(address);
case MemoryType::GbaMemory: return _gbaConsole->GetMemoryManager()->DebugRead(address);
case MemoryType::WsMemory: return _wsConsole->GetMemoryManager()->DebugRead(address);
case MemoryType::WsPort: return _wsConsole->GetMemoryManager()->DebugReadPort<uint8_t>(address);
default:
uint8_t* src = GetMemoryBuffer(memoryType);
+2
View File
@@ -14,6 +14,7 @@ class Gameboy;
class PceConsole;
class SmsConsole;
class GbaConsole;
class WsConsole;
class Emulator;
class Debugger;
@@ -40,6 +41,7 @@ private:
PceConsole* _pceConsole = nullptr;
SmsConsole* _smsConsole = nullptr;
GbaConsole* _gbaConsole = nullptr;
WsConsole* _wsConsole = nullptr;
BaseCartridge* _cartridge = nullptr;
Debugger* _debugger = nullptr;
bool _isMemorySupported[DebugUtilities::GetMemoryTypeCount()] = {};
+16
View File
@@ -45,6 +45,7 @@ void PpuTools::GetTileView(GetTileViewOptions options, uint8_t* source, uint32_t
case TileFormat::GbaBpp4: InternalGetTileView<TileFormat::GbaBpp4>(options, source, srcSize, colors, outBuffer); break;
case TileFormat::GbaBpp8: InternalGetTileView<TileFormat::GbaBpp8>(options, source, srcSize, colors, outBuffer); break;
case TileFormat::WsBpp4Packed: InternalGetTileView<TileFormat::WsBpp4Packed>(options, source, srcSize, colors, outBuffer); break;
}
}
@@ -112,6 +113,8 @@ void PpuTools::InternalGetTileView(GetTileViewOptions options, uint8_t *source,
case TileFormat::GbaBpp4: bpp = 4; rowOffset = 4; break;
case TileFormat::GbaBpp8: bpp = 8; rowOffset = 8; break;
case TileFormat::WsBpp4Packed: bpp = 4; rowOffset = 4; break;
default: bpp = 8; break;
}
@@ -243,6 +246,7 @@ void PpuTools::GetSetTilePixel(AddressInfo tileAddress, TileFormat format, int32
case TileFormat::SmsSgBpp1: rowOffset = 1; break;
case TileFormat::GbaBpp4: rowOffset = 4; break;
case TileFormat::GbaBpp8: rowOffset = 8; break;
case TileFormat::WsBpp4Packed: rowOffset = 4; break;
}
uint8_t* ram = (uint8_t*)memInfo.Memory;
@@ -377,6 +381,18 @@ void PpuTools::GetSetTilePixel(AddressInfo tileAddress, TileFormat format, int32
break;
}
case TileFormat::WsBpp4Packed: {
uint8_t pixelOffset = (7 - shift);
int32_t addr = (rowStart + (pixelOffset >> 1));
if(addr <= ramMask) {
int offset = pixelOffset & 0x01 ? 0 : 4;
for(int i = 0; i < 4; i++) {
setBit(addr, i+offset, i);
}
}
break;
}
default:
throw std::runtime_error("unsupported format");
}
+18 -1
View File
@@ -184,12 +184,14 @@ enum class RawPaletteFormat
Rgb555,
Rgb333,
Rgb222,
Rgb444
Rgb444,
Bgr444
};
struct DebugPaletteInfo
{
MemoryType PaletteMemType;
uint32_t PaletteMemOffset;
bool HasMemType;
uint32_t ColorCount;
@@ -281,6 +283,7 @@ template<TileFormat format> uint32_t PpuTools::GetRgbPixelColor(const uint32_t*
case TileFormat::Bpp4:
case TileFormat::SmsBpp4:
case TileFormat::GbaBpp4:
case TileFormat::WsBpp4Packed:
case TileFormat::PceSpriteBpp4:
case TileFormat::PceSpriteBpp2Sp01:
case TileFormat::PceSpriteBpp2Sp23:
@@ -423,6 +426,20 @@ template<TileFormat format> uint8_t PpuTools::GetTilePixelColor(const uint8_t* r
}
}
case TileFormat::WsBpp4Packed: {
uint8_t pixelOffset = (7 - shift);
uint32_t addr = (rowStart + (pixelOffset >> 1));
if(addr <= ramMask) {
if(pixelOffset & 0x01) {
return ram[addr] & 0x0F;
} else {
return ram[addr] >> 4;
}
} else {
return 0;
}
}
default:
throw std::runtime_error("unsupported format");
}
+1 -1
View File
@@ -12,7 +12,7 @@ struct GbaGpioState
bool ReadWrite;
};
class GbaGpio : public ISerializable
class GbaGpio final : public ISerializable
{
private:
GbaGpioState _state = {};
+1 -1
View File
@@ -21,7 +21,7 @@ struct GbaRtcState
bool TestMode; //unimplemented
};
class GbaRtc : public ISerializable
class GbaRtc final : public ISerializable
{
private:
enum class Command : uint8_t
+2 -2
View File
@@ -21,8 +21,8 @@ private:
string mapperId = footer.GetMapperId();
MessageManager::Log("[GBX] Mapper: " + mapperId);
MessageManager::Log(string("[GBX] ROM Size: ") + std::to_string(footer.GetRomSize() / 1024) + " kB");
MessageManager::Log(string("[GBX] RAM Size: ") + std::to_string(footer.GetRamSize() / 1024) + " kB");
MessageManager::Log(string("[GBX] ROM Size: ") + std::to_string(footer.GetRomSize() / 1024) + " KB");
MessageManager::Log(string("[GBX] RAM Size: ") + std::to_string(footer.GetRamSize() / 1024) + " KB");
MessageManager::Log("[GBX] Battery: " + string(footer.HasBattery() ? "Yes" : "No"));
MessageManager::Log("[GBX] RTC: " + string(footer.HasRtc() ? "Yes" : "No"));
MessageManager::Log("[GBX] Rumble: " + string(footer.HasRumble() ? "Yes" : "No"));
+3 -3
View File
@@ -13,8 +13,8 @@ GbDefaultVideoFilter::GbDefaultVideoFilter(Emulator* emu, bool applyNtscFilter)
{
InitLookupTable();
_applyNtscFilter = applyNtscFilter;
_prevFrame = new uint16_t[160 * 144];
memset(_prevFrame, 0, 160 * 144 * sizeof(uint16_t));
_prevFrame = new uint16_t[GbConstants::PixelCount];
memset(_prevFrame, 0, GbConstants::PixelCount * sizeof(uint16_t));
}
GbDefaultVideoFilter::~GbDefaultVideoFilter()
@@ -88,7 +88,7 @@ void GbDefaultVideoFilter::OnBeforeApplyFilter()
bool blendFrames = gbConfig.BlendFrames && !_emu->GetRewindManager()->IsRewinding() && !_emu->IsPaused();
if(_blendFrames != blendFrames) {
_blendFrames = blendFrames;
memset(_prevFrame, 0, 160 * 144 * sizeof(uint16_t));
memset(_prevFrame, 0, GbConstants::PixelCount * sizeof(uint16_t));
}
_videoConfig = config;
}
+1 -1
View File
@@ -54,7 +54,7 @@ protected:
}
if(_microphoneEnabled && (_emu->GetFrameCount() % 3) == 0) {
SetPressedState(Buttons::Microphone, keyMapping.Microphone);
SetPressedState(Buttons::Microphone, keyMapping.GenericKey1);
}
if(!_emu->GetSettings()->GetNesConfig().AllowInvalidInput) {
+13
View File
@@ -9,6 +9,11 @@ public:
return (color << 3) + (color >> 2);
}
static uint8_t Convert4BitTo8Bit(uint8_t color)
{
return (color << 4) | color;
}
static uint32_t Rgb555ToArgb(uint16_t rgb555)
{
uint8_t b = Convert5BitTo8Bit((rgb555 >> 10) & 0x1F);
@@ -46,6 +51,14 @@ public:
return Rgb555ToArgb(Rgb444To555(rgb444));
}
static uint32_t Bgr444ToArgb(uint16_t bgr444)
{
uint8_t b = (bgr444 & 0x00F);
uint8_t g = (bgr444 & 0x0F0) >> 4;
uint8_t r = (bgr444 & 0xF00) >> 8;
return 0xFF000000 | (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | (b << 0);
}
static uint16_t Rgb888To555(uint32_t rgb888)
{
uint8_t r = (rgb888 >> 19) & 0x1F;
+3 -2
View File
@@ -13,7 +13,8 @@ enum class CpuType : uint8_t
Nes,
Pce,
Sms,
Gba
Gba,
Ws
};
class CpuTypeUtilities
@@ -21,6 +22,6 @@ class CpuTypeUtilities
public:
static constexpr int GetCpuTypeCount()
{
return (int)CpuType::Sms + 1;
return (int)CpuType::Ws + 1;
}
};
+17 -2
View File
@@ -119,6 +119,10 @@ void EmuSettings::Serialize(Serializer& s)
SV(_gba.Controller.Type);
break;
case ConsoleType::Ws:
//TODOWS
break;
default:
throw std::runtime_error("unsupported console type");
}
@@ -261,6 +265,16 @@ CvConfig& EmuSettings::GetCvConfig()
return _cv;
}
void EmuSettings::SetWsConfig(WsConfig& config)
{
_ws = config;
}
WsConfig& EmuSettings::GetWsConfig()
{
return _ws;
}
void EmuSettings::SetGameConfig(GameConfig& config)
{
_game = config;
@@ -422,6 +436,7 @@ OverscanDimensions EmuSettings::GetOverscan()
case ConsoleType::Gameboy:
case ConsoleType::Gba:
case ConsoleType::Ws:
break;
}
@@ -450,8 +465,8 @@ double EmuSettings::GetAspectRatio(ConsoleRegion region, FrameInfo baseFrameSize
//For auto, ntsc and pal, these are PAR ratios, so multiply them with the base screen's aspect ratio to get the expected screen aspect ratio
case VideoAspectRatio::Auto:
if(_emu->GetConsoleType() == ConsoleType::Gameboy || _emu->GetConsoleType() == ConsoleType::Gba) {
//GB shouldn't use NTSC/PAL aspect ratio when in auto mode
if(_emu->GetConsoleType() == ConsoleType::Gameboy || _emu->GetConsoleType() == ConsoleType::Gba || _emu->GetConsoleType() == ConsoleType::Ws) {
//GB/GBA/WS shouldn't use NTSC/PAL aspect ratio when in auto mode
return screenAspectRatio;
}
return screenAspectRatio * ((region == ConsoleRegion::Pal || region == ConsoleRegion::Dendy) ? (11.0 / 8.0) : (8.0 / 7.0));
+4
View File
@@ -30,6 +30,7 @@ private:
SmsConfig _sms;
CvConfig _cv;
GbaConfig _gba;
WsConfig _ws;
atomic<uint32_t> _flags;
atomic<uint64_t> _debuggerFlags;
@@ -92,6 +93,9 @@ public:
void SetCvConfig(CvConfig& config);
CvConfig& GetCvConfig();
void SetWsConfig(WsConfig& config);
WsConfig& GetWsConfig();
void SetGameConfig(GameConfig& config);
GameConfig& GetGameConfig();
+19 -6
View File
@@ -38,6 +38,7 @@
#include "PCE/PceConsole.h"
#include "SMS/SmsConsole.h"
#include "GBA/GbaConsole.h"
#include "WS/WsConsole.h"
#include "Debugger/Debugger.h"
#include "Debugger/BaseEventManager.h"
#include "Debugger/DebugTypes.h"
@@ -554,6 +555,7 @@ void Emulator::TryLoadRom(VirtualFile& romFile, LoadRomResult& result, unique_pt
TryLoadRom<PceConsole>(romFile, result, console, useFileSignature);
TryLoadRom<SmsConsole>(romFile, result, console, useFileSignature);
TryLoadRom<GbaConsole>(romFile, result, console, useFileSignature);
TryLoadRom<WsConsole>(romFile, result, console, useFileSignature);
}
template<typename T>
@@ -906,11 +908,11 @@ void Emulator::Serialize(ostream& out, bool includeSettings, int compressionLeve
s.SaveTo(out, compressionLevel);
}
bool Emulator::Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> srcConsoleType, bool sendNotification)
DeserializeResult Emulator::Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> srcConsoleType, bool sendNotification)
{
Serializer s(fileFormatVersion, false);
if(!s.LoadFrom(in)) {
return false;
return DeserializeResult::InvalidFile;
}
if(includeSettings) {
@@ -922,7 +924,7 @@ bool Emulator::Deserialize(istream& in, uint32_t fileFormatVersion, bool include
SaveStateCompatInfo compatInfo = _console->ValidateSaveStateCompatibility(srcConsoleType.value());
if(!compatInfo.IsCompatible) {
MessageManager::DisplayMessage("SaveStates", "SaveStateWrongSystem");
return false;
return DeserializeResult::SpecificError;
}
s.RemoveKeys(compatInfo.FieldsToRemove);
@@ -935,16 +937,19 @@ bool Emulator::Deserialize(istream& in, uint32_t fileFormatVersion, bool include
if(!s.IsValid()) {
MessageManager::DisplayMessage("SaveStates", "SaveStateWrongSystem");
return false;
return DeserializeResult::SpecificError;
}
}
s.Stream(_console, "");
if(s.HasError()) {
return DeserializeResult::SpecificError;
}
if(sendNotification) {
_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
}
return true;
return DeserializeResult::Success;
}
BaseVideoFilter* Emulator::GetVideoFilter(bool getDefaultFilter)
@@ -953,6 +958,14 @@ BaseVideoFilter* Emulator::GetVideoFilter(bool getDefaultFilter)
return console ? console->GetVideoFilter(getDefaultFilter) : new SnesDefaultVideoFilter(this);
}
void Emulator::GetScreenRotationOverride(uint32_t& rotation)
{
shared_ptr<IConsole> console = GetConsole();
if(console) {
console->GetScreenRotationOverride(rotation);
}
}
void Emulator::InputBarcode(uint64_t barcode, uint32_t digitCount)
{
shared_ptr<IConsole> console = GetConsole();
+4 -3
View File
@@ -183,7 +183,7 @@ public:
void SuspendDebugger(bool release);
void Serialize(ostream& out, bool includeSettings, int compressionLevel = 1);
bool Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> consoleType = std::nullopt, bool sendNotification = true);
DeserializeResult Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> consoleType = std::nullopt, bool sendNotification = true);
SoundMixer* GetSoundMixer() { return _soundMixer.get(); }
VideoRenderer* GetVideoRenderer() { return _videoRenderer.get(); }
@@ -204,6 +204,7 @@ public:
shared_ptr<SystemActionManager> GetSystemActionManager() { return _systemActionManager; }
BaseVideoFilter* GetVideoFilter(bool getDefaultFilter = false);
void GetScreenRotationOverride(uint32_t& rotation);
void InputBarcode(uint64_t barcode, uint32_t digitCount);
void ProcessTapeRecorderAction(TapeRecorderAction action, string filename);
@@ -268,10 +269,10 @@ public:
return true;
}
template<CpuType cpuType, MemoryType memType, MemoryOperationType opType> __forceinline void ProcessMemoryAccess(uint32_t addr, uint8_t value)
template<CpuType cpuType, MemoryType memType, MemoryOperationType opType, typename T> __forceinline void ProcessMemoryAccess(uint32_t addr, T value)
{
if(_debugger) {
_debugger->ProcessMemoryAccess<cpuType, memType, opType>(addr, value);
_debugger->ProcessMemoryAccess<cpuType, memType, opType, T>(addr, value);
}
}
+43 -1
View File
@@ -28,7 +28,10 @@ enum class FirmwareType
StudyBox,
PceSuperCd,
PceGamesExpress,
ColecoVision
ColecoVision,
WonderSwan,
WonderSwanColor,
SwanCrystal,
};
struct MissingFirmwareMessage
@@ -114,6 +117,18 @@ struct MissingFirmwareMessage
FileHashes[1] = "FB4A898EB93B19B36773D87A6C70EB28F981B2686BEBDD2D431B05DCDF9CFFD4";
break;
case FirmwareType::WonderSwan:
FileHashes[0] = "BF4480DBEA1C47C8B54CE7BE9382BC1006148F431FBE4277E136351FA74F635E";
break;
case FirmwareType::WonderSwanColor:
FileHashes[0] = "F5A5C044D84CE1681F94E9EF74287CB989784497BE5BD5108DF17908DFA55DB2";
break;
case FirmwareType::SwanCrystal:
FileHashes[0] = "82E96ADDF5AB1CE09A84B6EEDAA904E4CA432756851F7E0CC0649006C183834D";
break;
default:
throw std::runtime_error("Unexpected firmware type");
}
@@ -410,4 +425,31 @@ public:
MessageManager::DisplayMessage("Error", "Could not find firmware file for the ColecoVision");
return false;
}
static bool LoadWsBootRom(Emulator* emu, vector<uint8_t>& bootRom, WsModel model)
{
string filename;
FirmwareType firmwareType;
switch(model) {
default:
case WsModel::Monochrome: filename = "bootrom.ws"; firmwareType = FirmwareType::WonderSwan; break;
case WsModel::Color: filename = "bootrom.wsc"; firmwareType = FirmwareType::WonderSwanColor; break;
case WsModel::SwanCrystal: filename = "bootrom_sc.wsc"; firmwareType = FirmwareType::SwanCrystal; break;
}
uint32_t size = model == WsModel::Monochrome ? 0x1000 : 0x2000;
string path = FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), filename);
if(AttemptLoadFirmware(bootRom, filename, size)) {
return true;
}
MissingFirmwareMessage msg(filename.c_str(), firmwareType, size);
emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
if(AttemptLoadFirmware(bootRom, filename, size)) {
return true;
}
MessageManager::DisplayMessage("Error", "Could not find boot rom for the WonderSwan, skipping boot screen.");
return false;
}
};
+1
View File
@@ -88,6 +88,7 @@ public:
}
virtual BaseVideoFilter* GetVideoFilter(bool getDefaultFilter) = 0;
virtual void GetScreenRotationOverride(uint32_t& rotation) {}
virtual PpuFrameInfo GetPpuFrame() = 0;
+9
View File
@@ -14,6 +14,7 @@ enum class MemoryType
PceMemory,
SmsMemory,
GbaMemory,
WsMemory,
SnesPrgRom,
SnesWorkRam,
@@ -82,6 +83,14 @@ enum class MemoryType
GbaVideoRam,
GbaSpriteRam,
GbaPaletteRam,
WsPrgRom,
WsWorkRam,
WsCartRam,
WsCartEeprom,
WsBootRom,
WsInternalEeprom,
WsPort,
None
};
+2
View File
@@ -30,6 +30,8 @@ enum class RomFormat
ColecoVision,
Gba,
Ws,
};
struct DipSwitchInfo
+5 -1
View File
@@ -199,7 +199,9 @@ bool SaveStateManager::LoadState(istream &stream)
stream.read(nameBuffer.data(), nameBuffer.size());
string romName(nameBuffer.data(), nameLength);
if(_emu->Deserialize(stream, fileFormatVersion, false, stateConsoleType)) {
DeserializeResult result = _emu->Deserialize(stream, fileFormatVersion, false, stateConsoleType);
if(result == DeserializeResult::Success) {
//Stop any movie that might have been playing/recording if a state is loaded
//(Note: Loading a state is disabled in the UI while a movie is playing/recording)
_emu->GetMovieManager()->Stop();
@@ -211,6 +213,8 @@ bool SaveStateManager::LoadState(istream &stream)
_emu->GetVideoDecoder()->UpdateFrame(frame, true, false);
}
return true;
} else if(result == DeserializeResult::SpecificError) {
return false;
}
}
+53 -4
View File
@@ -233,6 +233,10 @@ enum class ControllerType
//GBA
GbaController,
//WS
WsController,
WsControllerVertical
};
struct KeyMapping
@@ -249,6 +253,8 @@ struct KeyMapping
uint16_t Right = 0;
uint16_t Start = 0;
uint16_t Select = 0;
uint16_t U = 0;
uint16_t D = 0;
uint16_t TurboA = 0;
uint16_t TurboB = 0;
@@ -259,13 +265,13 @@ struct KeyMapping
uint16_t TurboSelect = 0;
uint16_t TurboStart = 0;
uint16_t Microphone = 0;
uint16_t GenericKey1 = 0;
uint16_t CustomKeys[100] = {};
bool HasKeySet()
{
if(A || B || X || Y || L || R || Up || Down || Left || Right || Start || Select || TurboA || TurboB || TurboX || TurboY || TurboL || TurboR || TurboStart || TurboSelect || Microphone) {
if(A || B || X || Y || L || R || U || D || Up || Down || Left || Right || Start || Select || TurboA || TurboB || TurboX || TurboY || TurboL || TurboR || TurboStart || TurboSelect || GenericKey1) {
return true;
}
for(uint32_t i = 0; i < 100; i++) {
@@ -353,7 +359,8 @@ enum class ConsoleType
Nes = 2,
PcEngine = 3,
Sms = 4,
Gba = 5
Gba = 5,
Ws = 6
};
enum class GameboyModel
@@ -704,6 +711,45 @@ struct CvConfig
uint32_t ChannelVolumes[4] = {};
};
enum class WsModel : uint8_t
{
Auto,
Monochrome,
Color,
SwanCrystal
};
enum class WsAudioMode : uint8_t
{
Headphones,
Speakers
};
struct WsConfig
{
ControllerConfig ControllerHorizontal;
ControllerConfig ControllerVertical;
WsModel Model = WsModel::Auto;
bool UseBootRom = false;
bool AutoRotate = false;
bool BlendFrames = false;
bool LcdAdjustColors = false;
bool LcdShowIcons = false;
bool HideBgLayers[2] = {};
bool DisableSprites = false;
WsAudioMode AudioMode = WsAudioMode::Headphones;
uint32_t Channel1Vol = 100;
uint32_t Channel2Vol = 100;
uint32_t Channel3Vol = 100;
uint32_t Channel4Vol = 100;
uint32_t Channel5Vol = 100;
};
struct AudioPlayerConfig
{
uint32_t Volume = 100;
@@ -764,6 +810,8 @@ struct DebugConfig
bool GbaBreakOnNopLoad = false;
bool GbaBreakOnInvalidOpCode = false;
bool GbaBreakOnUnalignedMemAccess = false;
bool WsBreakOnInvalidOpCode = false;
bool ScriptAllowIoOsAccess = false;
bool ScriptAllowNetworkAccess = false;
@@ -790,7 +838,7 @@ struct PreferencesConfig
bool ShowTurboRewindIcons = false;
bool DisableGameSelectionScreen = false;
HudDisplaySize HudSize;
HudDisplaySize HudSize = HudDisplaySize::Fixed;
uint32_t AutoSaveStateDelay = 5;
uint32_t RewindBufferSize = 300;
@@ -1008,4 +1056,5 @@ enum class DebuggerFlags
PceDebuggerEnabled = (1 << 8),
SmsDebuggerEnabled = (1 << 9),
GbaDebuggerEnabled = (1 << 10),
WsDebuggerEnabled = (1 << 11),
};
+2
View File
@@ -184,6 +184,8 @@ void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename
uint8_t scale = 1;
uint32_t screenRotation = _emu->GetSettings()->GetVideoConfig().ScreenRotation;
_emu->GetScreenRotationOverride(screenRotation);
unique_ptr<RotateFilter> rotateFilter(new RotateFilter(screenRotation));
if(screenRotation != 0) {
pngBuffer = rotateFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height);
+13
View File
@@ -48,6 +48,19 @@ void ScaleFilter::ApplyLcdGridFilter(uint32_t* inputArgbBuffer)
uint8_t bottomLeft = (uint8_t)(cfg.LcdGridBottomLeftBrightness * 255);
uint8_t bottomRight = (uint8_t)(cfg.LcdGridBottomRightBrightness * 255);
//Rotate lcd effect as needed
uint32_t screenRotation = _emu->GetSettings()->GetVideoConfig().ScreenRotation;
_emu->GetScreenRotationOverride(screenRotation);
uint8_t offset = screenRotation / 90;
for(int i = 0; i < offset; i++) {
uint8_t orgTopLeft = topLeft;
topLeft = topRight;
topRight = bottomRight;
bottomRight = bottomLeft;
bottomLeft = orgTopLeft;
}
for(uint32_t y = 0; y < _height; y++) {
for(uint32_t x = 0; x < _width; x++) {
uint32_t srcColor = inputArgbBuffer[y * _width + x];
+2
View File
@@ -81,6 +81,8 @@ void VideoDecoder::UpdateVideoFilter()
}
uint32_t screenRotation = _emu->GetSettings()->GetVideoConfig().ScreenRotation;
_emu->GetScreenRotationOverride(screenRotation);
if(screenRotation != 0) {
if(!_rotateFilter || _rotateFilter->GetAngle() != screenRotation) {
_rotateFilter.reset(new RotateFilter(screenRotation));
+468
View File
@@ -0,0 +1,468 @@
#include "pch.h"
#include "WS/APU/WsApu.h"
#include "WS/APU/WsApuCh1.h"
#include "WS/APU/WsApuCh2.h"
#include "WS/APU/WsApuCh3.h"
#include "WS/APU/WsApuCh4.h"
#include "WS/APU/WsHyperVoice.h"
#include "WS/WsConsole.h"
#include "WS/WsPpu.h"
#include "WS/WsDmaController.h"
#include "WS/WsMemoryManager.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/Audio/SoundMixer.h"
#include "Utilities/BitUtilities.h"
#include "Utilities/Serializer.h"
WsApu::WsApu(Emulator* emu, WsConsole* console, WsMemoryManager* memoryManager, WsDmaController* dmaController)
{
_emu = emu;
_console = console;
_memoryManager = memoryManager;
_dmaController = dmaController;
_soundMixer = emu->GetSoundMixer();
_state.MasterVolume = _console->GetModel() == WsModel::Monochrome ? 2 : 3;
_state.InternalMasterVolume = _state.MasterVolume;
_ch1.reset(new WsApuCh1(this, _state.Ch1));
_ch2.reset(new WsApuCh2(this, _state.Ch2));
_ch3.reset(new WsApuCh3(this, _state.Ch3));
_ch4.reset(new WsApuCh4(this, _state.Ch4));
_hyperVoice.reset(new WsHyperVoice(_state.Voice));
_soundBuffer = new int16_t[WsApu::MaxSamples * 2];
memset(_soundBuffer, 0, WsApu::MaxSamples * 2 * sizeof(int16_t));
_filterL.SetCutoffFrequency(16, WsApu::ApuFrequency);
_filterR.SetCutoffFrequency(16, WsApu::ApuFrequency);
}
WsApu::~WsApu()
{
delete[] _soundBuffer;
}
void WsApu::ChangeMasterVolume()
{
if(_emu->GetSettings()->GetWsConfig().AudioMode == WsAudioMode::Speakers) {
if(_state.InternalMasterVolume == 0) {
_state.InternalMasterVolume = _console->GetModel() == WsModel::Monochrome ? 2 : 3;
} else {
_state.InternalMasterVolume--;
}
}
_console->GetPpu()->ShowVolumeIcon();
}
void WsApu::PlayQueuedAudio()
{
_soundMixer->PlayAudioBuffer(_soundBuffer, _sampleCount, WsApu::ApuFrequency);
_sampleCount = 0;
_clockCounter = 0;
}
void WsApu::WriteDma(bool forHyperVoice, uint8_t sampleValue)
{
if(forHyperVoice) {
_hyperVoice->WriteDma(sampleValue);
} else {
_state.Ch2.RightVolume = sampleValue & 0x0F;
_state.Ch2.LeftVolume = sampleValue >> 4;
}
}
uint8_t WsApu::ReadSample(uint8_t ch, uint8_t pos)
{
//TODOWS review once the exact behavior for these 2 flags is understood
if(_state.ForceOutput4) {
return 4;
} else if(_state.ForceOutput2) {
return 2;
}
uint32_t addr = _state.WaveTableAddress + (ch * 16) + (pos >> 1);
uint8_t value = _memoryManager->InternalRead(addr);
_emu->ProcessMemoryRead<CpuType::Ws>(addr, value, MemoryOperationType::Read);
if(pos & 0x01) {
return value & 0x0F;
} else {
return value >> 4;
}
}
void WsApu::UpdateOutput()
{
WsConfig& cfg = _emu->GetSettings()->GetWsConfig();
int32_t leftOutput = (
_state.Ch1.LeftOutput * cfg.Channel1Vol / 100 +
_state.Ch2.LeftOutput * cfg.Channel2Vol / 100 +
_state.Ch3.LeftOutput * cfg.Channel3Vol / 100 +
_state.Ch4.LeftOutput * cfg.Channel4Vol / 100
);
int32_t rightOutput = (
_state.Ch1.RightOutput * cfg.Channel1Vol / 100 +
_state.Ch2.RightOutput * cfg.Channel2Vol / 100 +
_state.Ch3.RightOutput * cfg.Channel3Vol / 100 +
_state.Ch4.RightOutput * cfg.Channel4Vol / 100
);
if(_state.ForceOutputCh2Voice) {
leftOutput = (_state.Ch2.GetVolume() * 5) & 0x3FF;
rightOutput = leftOutput;
}
if(cfg.AudioMode == WsAudioMode::Headphones) {
if(_state.HeadphoneEnabled) {
leftOutput <<= 5;
rightOutput <<= 5;
if(_state.Voice.Enabled) {
leftOutput += _state.Voice.LeftOutput * cfg.Channel5Vol / 100;
rightOutput += _state.Voice.RightOutput * cfg.Channel5Vol / 100;
}
} else {
leftOutput = 0;
rightOutput = 0;
}
} else {
if(_state.SpeakerEnabled) {
int32_t out = (((leftOutput + rightOutput) >> _state.SpeakerVolume) & 0xFF) << 7;
leftOutput = out;
rightOutput = out;
if(_console->GetModel() == WsModel::Monochrome) {
switch(_state.InternalMasterVolume) {
case 0: leftOutput = 0; rightOutput = 0; break;
case 1: leftOutput >>= 1; rightOutput >>= 1; break;
case 2: break;
}
} else {
switch(_state.InternalMasterVolume) {
case 0: leftOutput = 0; rightOutput = 0; break;
case 1: leftOutput /= 3; rightOutput /= 3; break;
case 2: leftOutput = leftOutput * 2 / 3; rightOutput = rightOutput * 2 / 3; break;
case 3: break;
}
}
} else {
leftOutput = 0;
rightOutput = 0;
}
}
leftOutput = std::clamp<int32_t>(leftOutput, INT16_MIN, INT16_MAX);
rightOutput = std::clamp<int32_t>(rightOutput, INT16_MIN, INT16_MAX);
//Use low pass filter and subtract the result to filter out DC offset
_soundBuffer[_sampleCount*2] = leftOutput - _filterL.Process(leftOutput);
_soundBuffer[_sampleCount*2+1] = rightOutput - _filterR.Process(rightOutput);
_sampleCount++;
if(_sampleCount >= WsApu::MaxSamples) {
PlayQueuedAudio();
}
}
void WsApu::Run()
{
_clockCounter++;
_ch1->Exec();
_ch2->Exec();
_ch3->Exec();
_ch4->Exec();
uint8_t clock = (_clockCounter - 1) & 0x7F;
if(clock >= 116) {
switch(clock) {
case 116: _dmaController->ProcessSoundDma(); break;
case 122: _hyperVoice->Exec(); break;
case 123: _ch1->UpdateOutput(); break;
case 124: _ch2->UpdateOutput(); break;
case 125: _ch3->UpdateOutput(); break;
case 126: _ch4->UpdateOutput(); break;
case 127: UpdateOutput(); break;
}
}
}
uint8_t WsApu::Read(uint16_t port)
{
switch(port) {
case 0x6A:
case 0x6B:
return _hyperVoice->Read(port);
case 0x80: return BitUtilities::GetBits<0>(_state.Ch1.Frequency);
case 0x81: return BitUtilities::GetBits<8>(_state.Ch1.Frequency);
case 0x82: return BitUtilities::GetBits<0>(_state.Ch2.Frequency);
case 0x83: return BitUtilities::GetBits<8>(_state.Ch2.Frequency);
case 0x84: return BitUtilities::GetBits<0>(_state.Ch3.Frequency);
case 0x85: return BitUtilities::GetBits<8>(_state.Ch3.Frequency);
case 0x86: return BitUtilities::GetBits<0>(_state.Ch4.Frequency);
case 0x87: return BitUtilities::GetBits<8>(_state.Ch4.Frequency);
case 0x88: return _state.Ch1.GetVolume();
case 0x89: return _state.Ch2.GetVolume();
case 0x8A: return _state.Ch3.GetVolume();
case 0x8B: return _state.Ch4.GetVolume();
case 0x8C: return (uint8_t)_state.Ch3.SweepValue;
case 0x8D: return _state.Ch3.SweepPeriod;
case 0x8E:
return (
_state.Ch4.TapMode |
((uint8_t)_state.Ch4.LfsrEnabled << 4)
);
case 0x8F: return _state.WaveTableAddress >> 6;
case 0x90:
return (
(uint8_t)_state.Ch1.Enabled |
((uint8_t)_state.Ch2.Enabled << 1) |
((uint8_t)_state.Ch3.Enabled << 2) |
((uint8_t)_state.Ch4.Enabled << 3) |
((uint8_t)_state.Ch2.PcmEnabled << 5) |
((uint8_t)_state.Ch3.SweepEnabled << 6) |
((uint8_t)_state.Ch4.NoiseEnabled << 7)
);
case 0x91:
return (
(uint8_t)_state.SpeakerEnabled |
(_state.SpeakerVolume << 1) |
((uint8_t)_state.HeadphoneEnabled << 3) |
(_emu->GetSettings()->GetWsConfig().AudioMode == WsAudioMode::Headphones ? 0x80 : 0)
);
case 0x92: return BitUtilities::GetBits<0>(_state.Ch4.Lfsr);
case 0x93: return BitUtilities::GetBits<8>(_state.Ch4.Lfsr);
case 0x94:
return (
(uint8_t)_state.Ch2.MaxPcmVolumeRight |
((uint8_t)_state.Ch2.HalfPcmVolumeRight << 1) |
((uint8_t)_state.Ch2.MaxPcmVolumeLeft << 2) |
((uint8_t)_state.Ch2.HalfPcmVolumeLeft << 3)
);
case 0x95:
return (
(uint8_t)_state.HoldChannels |
((uint8_t)_state.Ch3.UseSweepCpuClock << 1) |
(_state.Ch4.HoldLfsr << 2) |
//todows bit 4?
((uint8_t)_state.ForceOutputCh2Voice << 5) |
((uint8_t)_state.ForceOutput2 << 6) |
((uint8_t)_state.ForceOutput4 << 7)
);
case 0x96: return GetApuOutput(true);
case 0x97: return GetApuOutput(true) >> 8;
case 0x98: return GetApuOutput(false);
case 0x99: return GetApuOutput(false) >> 8;
case 0x9A: return GetApuOutput(false) + GetApuOutput(true);
case 0x9B: return (GetApuOutput(false) + GetApuOutput(true)) >> 8;
case 0x9E:
if(_console->GetModel() != WsModel::Monochrome) {
return _state.MasterVolume;
}
break;
}
//todows open bus
return 0x90;
}
void WsApu::Write(uint16_t port, uint8_t value)
{
switch(port) {
case 0x64: case 0x65: case 0x66: case 0x67:
case 0x69: case 0x6A: case 0x6B:
_hyperVoice->Write(port, value);
break;
case 0x80: BitUtilities::SetBits<0>(_state.Ch1.Frequency, value); break;
case 0x81: BitUtilities::SetBits<8>(_state.Ch1.Frequency, value & 0x0F); break;
case 0x82: BitUtilities::SetBits<0>(_state.Ch2.Frequency, value); break;
case 0x83: BitUtilities::SetBits<8>(_state.Ch2.Frequency, value & 0x0F); break;
case 0x84: BitUtilities::SetBits<0>(_state.Ch3.Frequency, value); break;
case 0x85: BitUtilities::SetBits<8>(_state.Ch3.Frequency, value & 0x0F); break;
case 0x86: BitUtilities::SetBits<0>(_state.Ch4.Frequency, value); break;
case 0x87: BitUtilities::SetBits<8>(_state.Ch4.Frequency, value & 0x0F); break;
case 0x88: _state.Ch1.SetVolume(value); break;
case 0x89: _state.Ch2.SetVolume(value); break;
case 0x8A: _state.Ch3.SetVolume(value); break;
case 0x8B: _state.Ch4.SetVolume(value); break;
case 0x8C: _state.Ch3.SweepValue = (int8_t)value; break;
case 0x8D: _state.Ch3.SweepPeriod = value & 0x1F; break;
case 0x8E: {
static constexpr uint8_t tapShifts[8] = { 14, 10, 13, 4, 8, 6, 9, 11 };
_state.Ch4.TapMode = value & 0x07;
_state.Ch4.TapShift = tapShifts[_state.Ch4.TapMode];
if(value & 0x08) {
_state.Ch4.Lfsr = 0;
}
_state.Ch4.LfsrEnabled = value & 0x10;
break;
}
case 0x8F:
_state.WaveTableAddress = value << 6;
break;
case 0x90:
_state.Ch1.Enabled = value & 0x01;
_state.Ch2.Enabled = value & 0x02;
_state.Ch3.Enabled = value & 0x04;
_state.Ch4.Enabled = value & 0x08;
_state.Ch2.PcmEnabled = value & 0x20;
_state.Ch3.SweepEnabled = value & 0x40;
_state.Ch4.NoiseEnabled = value & 0x80;
break;
case 0x91:
_state.SpeakerEnabled = value & 0x01;
_state.SpeakerVolume = (value & 0x06) >> 1;
_state.HeadphoneEnabled = value & 0x08;
break;
case 0x94:
_state.Ch2.MaxPcmVolumeRight = value & 0x01;
_state.Ch2.HalfPcmVolumeRight = value & 0x02;
_state.Ch2.MaxPcmVolumeLeft = value & 0x04;
_state.Ch2.HalfPcmVolumeLeft = value & 0x08;
break;
case 0x95:
_state.HoldChannels = value & 0x01;
_state.Ch3.UseSweepCpuClock = value & 0x02;
_state.Ch4.HoldLfsr = (value & 0x0C) >> 2;
//todows bit 4?
_state.ForceOutputCh2Voice = value & 0x20;
_state.ForceOutput2 = value & 0x40;
_state.ForceOutput4 = value & 0x80;
break;
case 0x9E:
if(_console->GetModel() != WsModel::Monochrome) {
_state.InternalMasterVolume = value & 0x03;
_state.MasterVolume = value & 0x03;
}
break;
}
}
uint16_t WsApu::GetApuOutput(bool forRight)
{
if(forRight) {
return (
_state.Ch1.RightOutput +
_state.Ch2.RightOutput +
_state.Ch3.RightOutput +
_state.Ch4.RightOutput
);
} else {
return (
_state.Ch1.LeftOutput +
_state.Ch2.LeftOutput +
_state.Ch3.LeftOutput +
_state.Ch4.LeftOutput
);
}
}
void WsApu::Serialize(Serializer& s)
{
SV(_state.Ch1.Frequency);
SV(_state.Ch1.Timer);
SV(_state.Ch1.Enabled);
SV(_state.Ch1.LeftVolume);
SV(_state.Ch1.RightVolume);
SV(_state.Ch1.SamplePosition);
SV(_state.Ch1.LeftOutput);
SV(_state.Ch1.RightOutput);
SV(_state.Ch2.Frequency);
SV(_state.Ch2.Timer);
SV(_state.Ch2.Enabled);
SV(_state.Ch2.LeftVolume);
SV(_state.Ch2.RightVolume);
SV(_state.Ch2.SamplePosition);
SV(_state.Ch2.LeftOutput);
SV(_state.Ch2.RightOutput);
SV(_state.Ch2.PcmEnabled);
SV(_state.Ch2.MaxPcmVolumeRight);
SV(_state.Ch2.HalfPcmVolumeRight);
SV(_state.Ch2.MaxPcmVolumeLeft);
SV(_state.Ch2.HalfPcmVolumeLeft);
SV(_state.Ch3.Frequency);
SV(_state.Ch3.Timer);
SV(_state.Ch3.Enabled);
SV(_state.Ch3.LeftVolume);
SV(_state.Ch3.RightVolume);
SV(_state.Ch3.SamplePosition);
SV(_state.Ch3.LeftOutput);
SV(_state.Ch3.RightOutput);
SV(_state.Ch3.SweepScaler);
SV(_state.Ch3.SweepEnabled);
SV(_state.Ch3.SweepValue);
SV(_state.Ch3.SweepPeriod);
SV(_state.Ch3.SweepTimer);
SV(_state.Ch3.UseSweepCpuClock);
SV(_state.Ch4.Frequency);
SV(_state.Ch4.Timer);
SV(_state.Ch4.Enabled);
SV(_state.Ch4.LeftVolume);
SV(_state.Ch4.RightVolume);
SV(_state.Ch4.SamplePosition);
SV(_state.Ch4.LeftOutput);
SV(_state.Ch4.RightOutput);
SV(_state.Ch4.NoiseEnabled);
SV(_state.Ch4.LfsrEnabled);
SV(_state.Ch4.TapMode);
SV(_state.Ch4.TapShift);
SV(_state.Ch4.Lfsr);
SV(_state.Ch4.HoldLfsr);
SV(_state.Voice.LeftOutput);
SV(_state.Voice.RightOutput);
SV(_state.Voice.Enabled);
SV(_state.Voice.LeftSample);
SV(_state.Voice.RightSample);
SV(_state.Voice.UpdateRightValue);
SV(_state.Voice.Divisor);
SV(_state.Voice.Timer);
SV(_state.Voice.Input);
SV(_state.Voice.Shift);
SV(_state.Voice.ChannelMode);
SV(_state.Voice.ScalingMode);
SV(_state.Voice.ControlLow);
SV(_state.Voice.ControlHigh);
SV(_state.WaveTableAddress);
SV(_state.SpeakerEnabled);
SV(_state.SpeakerVolume);
SV(_state.MasterVolume);
SV(_state.InternalMasterVolume);
SV(_state.HeadphoneEnabled);
SV(_state.HoldChannels);
SV(_state.ForceOutput2);
SV(_state.ForceOutput4);
SV(_state.ForceOutputCh2Voice);
SV(_filterL);
SV(_filterR);
}
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Utilities/ISerializable.h"
#include "Utilities/Audio/OnePoleLowPassFilter.h"
class Emulator;
class WsConsole;
class WsMemoryManager;
class WsDmaController;
class WsApuCh1;
class WsApuCh2;
class WsApuCh3;
class WsApuCh4;
class WsHyperVoice;
class SoundMixer;
class WsApu final : public ISerializable
{
private:
static constexpr int ApuFrequency = 24000;
static constexpr int MaxSamples = 256;
private:
WsApuState _state = {};
Emulator* _emu = nullptr;
WsConsole* _console = nullptr;
WsMemoryManager* _memoryManager = nullptr;
WsDmaController* _dmaController = nullptr;
SoundMixer* _soundMixer = nullptr;
unique_ptr<WsApuCh1> _ch1;
unique_ptr<WsApuCh2> _ch2;
unique_ptr<WsApuCh3> _ch3;
unique_ptr<WsApuCh4> _ch4;
unique_ptr<WsHyperVoice> _hyperVoice;
OnePoleLowPassFilter _filterL;
OnePoleLowPassFilter _filterR;
int16_t* _soundBuffer = nullptr;
uint32_t _clockCounter = 0;
uint16_t _sampleCount = 0;
void UpdateOutput();
uint16_t GetApuOutput(bool forRight);
public:
WsApu(Emulator* emu, WsConsole* console, WsMemoryManager* memoryManager, WsDmaController* dmaController);
~WsApu();
void ChangeMasterVolume();
void PlayQueuedAudio();
void WriteDma(bool forHyperVoice, uint8_t sampleValue);
WsApuState& GetState() { return _state; }
uint8_t GetMasterVolume() { return _state.InternalMasterVolume; }
uint8_t ReadSample(uint8_t ch, uint8_t pos);
void Run();
uint8_t Read(uint16_t port);
void Write(uint16_t port, uint8_t value);
void Serialize(Serializer& s) override;
};
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include "pch.h"
#include "WS/APU/WsApu.h"
#include "WS/WsTypes.h"
class WsApuCh1
{
private:
WsApu* _apu = nullptr;
WsApuCh1State* _state = nullptr;
public:
WsApuCh1(WsApu* apu, WsApuCh1State& state)
{
_state = &state;
_apu = apu;
}
void Exec()
{
if(!_state->Enabled) {
return;
}
if(_state->Timer == 0) {
_state->Timer = 2047 - _state->Frequency;
_state->SamplePosition = (_state->SamplePosition + 1) & 0x1F;
} else {
_state->Timer--;
}
}
void UpdateOutput()
{
if(!_state->Enabled) {
return;
}
uint8_t sample = _apu->ReadSample(0, _state->SamplePosition);
_state->LeftOutput = _state->LeftVolume * sample;
_state->RightOutput = _state->RightVolume * sample;
}
};
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include "pch.h"
#include "WS/APU/WsApu.h"
#include "WS/WsTypes.h"
class WsApuCh2
{
private:
WsApu* _apu = nullptr;
WsApuCh2State* _state = nullptr;
public:
WsApuCh2(WsApu* apu, WsApuCh2State& state)
{
_state = &state;
_apu = apu;
}
void Exec()
{
if(!_state->Enabled) {
return;
}
if(_state->Timer == 0) {
_state->Timer = 2047 - _state->Frequency;
_state->SamplePosition = (_state->SamplePosition + 1) & 0x1F;
} else {
_state->Timer--;
}
}
void UpdateOutput()
{
if(!_state->Enabled) {
return;
}
if(_state->PcmEnabled) {
uint8_t sample = _state->GetVolume();
_state->LeftOutput = sample >> (_state->MaxPcmVolumeLeft ? 0 : (_state->HalfPcmVolumeLeft ? 1 : 8));
_state->RightOutput = sample >> (_state->MaxPcmVolumeRight ? 0 : (_state->HalfPcmVolumeRight ? 1 : 8));
} else {
uint8_t sample = _apu->ReadSample(1, _state->SamplePosition);
_state->LeftOutput = _state->LeftVolume * sample;
_state->RightOutput = _state->RightVolume * sample;
}
}
};
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include "pch.h"
#include "WS/APU/WsApu.h"
#include "WS/WsTypes.h"
class WsApuCh3
{
private:
WsApu* _apu = nullptr;
WsApuCh3State* _state = nullptr;
public:
WsApuCh3(WsApu* apu, WsApuCh3State& state)
{
_state = &state;
_apu = apu;
}
void Exec()
{
if(!_state->Enabled) {
return;
}
if(_state->SweepEnabled) {
_state->SweepScaler++;
if(_state->UseSweepCpuClock || _state->SweepScaler >= 0x2000) {
_state->SweepScaler = 0;
if(_state->SweepTimer == 0) {
_state->SweepTimer = _state->SweepPeriod;
_state->Frequency = (_state->Frequency + _state->SweepValue) & 0xFFF;
} else {
_state->SweepTimer--;
}
}
}
if(_state->Timer == 0) {
_state->Timer = 2047 - _state->Frequency;
_state->SamplePosition = (_state->SamplePosition + 1) & 0x1F;
} else {
_state->Timer--;
}
}
void UpdateOutput()
{
if(!_state->Enabled) {
return;
}
uint8_t sample = _apu->ReadSample(2, _state->SamplePosition);
_state->LeftOutput = _state->LeftVolume * sample;
_state->RightOutput = _state->RightVolume * sample;
}
};
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "pch.h"
#include "WS/APU/WsApu.h"
#include "WS/WsTypes.h"
class WsApuCh4
{
private:
WsApu* _apu = nullptr;
WsApuCh4State* _state = nullptr;
public:
WsApuCh4(WsApu* apu, WsApuCh4State& state)
{
_state = &state;
_apu = apu;
}
void Exec()
{
if(!_state->Enabled) {
return;
}
if(_state->Timer == 0) {
_state->Timer = 2047 - _state->Frequency;
_state->SamplePosition = (_state->SamplePosition + 1) & 0x1F; //todows does this happen even when lfsr is enabled?
if(_state->LfsrEnabled) {
uint8_t newBit = ((_state->Lfsr >> 7) ^ (_state->Lfsr >> _state->TapShift) ^ 0x01) & 0x01;
_state->Lfsr = ((_state->Lfsr << 1) | newBit) & 0x7FFF;
}
} else {
_state->Timer--;
}
}
void UpdateOutput()
{
if(!_state->Enabled) {
return;
}
uint8_t sample;
if(_state->NoiseEnabled) {
sample = (_state->Lfsr & 0x01) ? 0x0F : 0;
} else {
sample = _apu->ReadSample(3, _state->SamplePosition);
}
_state->LeftOutput = _state->LeftVolume * sample;
_state->RightOutput = _state->RightVolume * sample;
}
};
+127
View File
@@ -0,0 +1,127 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Utilities/BitUtilities.h"
class WsHyperVoice
{
private:
WsApuHyperVoiceState* _state = nullptr;
public:
WsHyperVoice(WsApuHyperVoiceState& state)
{
_state = &state;
}
int16_t ConvertSampleToOutput(uint8_t sample)
{
int32_t output = 0;
switch(_state->ScalingMode) {
case WsHyperVoiceScalingMode::Unsigned: output = sample << 8; break;
case WsHyperVoiceScalingMode::UnsignedNegated: output = 0xFFFF0000 | (sample << 8); break;
case WsHyperVoiceScalingMode::Signed: output = ((int32_t)(int8_t)sample) << 8; break;
case WsHyperVoiceScalingMode::None: output = (int16_t)(sample << 8);
}
if(_state->ScalingMode != WsHyperVoiceScalingMode::None) {
output >>= _state->Shift;
}
return output;
}
void Exec()
{
if(!_state->Enabled) {
return;
}
if(_state->Timer == 0) {
_state->Timer = _state->Divisor;
_state->LeftOutput = ConvertSampleToOutput(_state->LeftSample);
_state->RightOutput = ConvertSampleToOutput(_state->RightSample);
} else {
_state->Timer--;
}
}
uint8_t Read(uint16_t port)
{
switch(port) {
case 0x6A: return _state->ControlLow;
case 0x6B: return _state->ControlHigh;
}
//TODOWS open bus
return 0x90;
}
void WriteStereoInput(uint8_t sample)
{
if(_state->UpdateRightValue) {
_state->RightSample = sample;
} else {
_state->LeftSample = sample;
}
_state->UpdateRightValue = !_state->UpdateRightValue;
}
void WriteDma(uint8_t sample)
{
switch(_state->ChannelMode) {
case WsHyperVoiceChannelMode::Stereo:
WriteStereoInput(sample);
break;
case WsHyperVoiceChannelMode::MonoLeft:
_state->LeftSample = sample;
break;
case WsHyperVoiceChannelMode::MonoRight:
_state->RightSample = sample;
break;
case WsHyperVoiceChannelMode::MonoBoth:
_state->LeftSample = sample;
_state->RightSample = sample;
break;
}
}
void Write(uint16_t port, uint8_t value)
{
switch(port) {
case 0x64: BitUtilities::SetBits<0>(_state->LeftOutput, value); break;
case 0x65: BitUtilities::SetBits<8>(_state->LeftOutput, value); break;
case 0x66: BitUtilities::SetBits<0>(_state->RightOutput, value); break;
case 0x67: BitUtilities::SetBits<8>(_state->RightOutput, value); break;
case 0x69: WriteStereoInput(value); break;
case 0x6A: {
_state->Shift = value & 0x03;
_state->ScalingMode = (WsHyperVoiceScalingMode)((value >> 2) & 0x03);
_state->Enabled = value & 0x80;
uint8_t dividor = (value >> 4) & 0x07;
switch(dividor) {
default: _state->Divisor = dividor; break;
case 6: _state->Divisor = 7; break;
case 7: _state->Divisor = 11; break;
}
_state->ControlLow = value;
break;
}
case 0x6B: {
bool reset = (value & 0x10);
if(reset) {
_state->UpdateRightValue = false;
}
_state->ChannelMode = (WsHyperVoiceChannelMode)((value >> 5) & 0x03);
_state->ControlHigh = value & 0x6F;
break;
}
}
}
};
+64
View File
@@ -0,0 +1,64 @@
#include "pch.h"
#include "WS/Carts/WsCart.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsEeprom.h"
#include "Utilities/Serializer.h"
void WsCart::Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly)
{
_memoryManager->Map(start, end, type, offset, readonly);
}
void WsCart::Unmap(uint32_t start, uint32_t end)
{
_memoryManager->Unmap(start, end);
}
WsCart::WsCart()
{
_state.SelectedBanks[0] = 0xFF;
_state.SelectedBanks[1] = 0xFF;
_state.SelectedBanks[2] = 0xFF;
_state.SelectedBanks[3] = 0xFF;
}
void WsCart::Init(WsMemoryManager* memoryManager, WsEeprom* cartEeprom)
{
_memoryManager = memoryManager;
_cartEeprom = cartEeprom;
}
void WsCart::RefreshMappings()
{
Map(0x10000, 0x1FFFF, MemoryType::WsCartRam, _state.SelectedBanks[1] * 0x10000, false);
Map(0x20000, 0x2FFFF, MemoryType::WsPrgRom, _state.SelectedBanks[2] * 0x10000, true);
Map(0x30000, 0x3FFFF, MemoryType::WsPrgRom, _state.SelectedBanks[3] * 0x10000, true);
Map(0x40000, 0xFFFFF, MemoryType::WsPrgRom, _state.SelectedBanks[0] * 0x100000 + 0x40000, true);
}
uint8_t WsCart::ReadPort(uint16_t port)
{
if(port < 0xC4) {
return _state.SelectedBanks[port - 0xC0];
} else if(port < 0xC9 && _cartEeprom) {
return _cartEeprom->ReadPort(port - 0xC4);
}
//TODOWS open bus
return 0x90;
}
void WsCart::WritePort(uint16_t port, uint8_t value)
{
if(port < 0xC4) {
_state.SelectedBanks[port - 0xC0] = value;
_memoryManager->RefreshMappings();
} else if(port < 0xC9 && _cartEeprom) {
_cartEeprom->WritePort(port - 0xC4, value);
}
}
void WsCart::Serialize(Serializer& s)
{
SVArray(_state.SelectedBanks, 4);
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Utilities/ISerializable.h"
#include "Shared/MemoryType.h"
class WsMemoryManager;
class WsEeprom;
//TODOWS RTC
//TODOWS Flash
class WsCart final: public ISerializable
{
protected:
WsCartState _state = {};
WsMemoryManager* _memoryManager = nullptr;
WsEeprom* _cartEeprom = nullptr;
void Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly);
void Unmap(uint32_t start, uint32_t end);
public:
WsCart();
virtual ~WsCart() {}
void Init(WsMemoryManager* memoryManager, WsEeprom* cartEeprom);
void RefreshMappings();
WsCartState& GetState() { return _state; }
virtual uint8_t ReadPort(uint16_t port);
virtual void WritePort(uint16_t port, uint8_t value);
void Serialize(Serializer& s) override;
};
+45
View File
@@ -0,0 +1,45 @@
#include "pch.h"
#include "WS/Debugger/DummyWsCpu.h"
#define DUMMYCPU
#define WsCpu DummyWsCpu
#include "WS/WsCpu.cpp"
#undef WsCpu
#undef DUMMYCPU
void DummyWsCpu::SetDummyState(WsCpuState& state)
{
_memOpCounter = 0;
_state = state;
_state.Halted = false;
_state.Flags.Trap = false;
}
uint32_t DummyWsCpu::GetOperationCount()
{
return _memOpCounter;
}
void DummyWsCpu::LogMemoryOperation(uint32_t addr, uint16_t value, MemoryOperationType type, MemoryType memType, bool isWordAccess)
{
if(_memOpCounter < 32) {
_memOperations[_memOpCounter] = {
addr,
(int32_t)value,
type,
memType
};
_memWordAccess[_memOpCounter] = isWordAccess;
_memOpCounter++;
}
}
MemoryOperationInfo DummyWsCpu::GetOperationInfo(uint32_t index)
{
return _memOperations[index];
}
bool DummyWsCpu::IsWordAccess(uint32_t index)
{
return _memWordAccess[index];
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "pch.h"
#include "Debugger/DebugTypes.h"
#define DUMMYCPU
#define WsCpu DummyWsCpu
#include "WS/WsCpu.h"
#undef WsCpu
#undef DUMMYCPU
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "pch.h"
#include "Debugger/IAssembler.h"
class LabelManager;
class WsAssembler final : public IAssembler
{
private:
LabelManager* _labelManager;
public:
WsAssembler(LabelManager* labelManager)
{
_labelManager = labelManager;
}
virtual ~WsAssembler() {}
uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode)
{
//TODOWS
return 0;
}
};
+513
View File
@@ -0,0 +1,513 @@
#include "pch.h"
#include "WS/Debugger/DummyWsCpu.h"
#include "WS/Debugger/WsDebugger.h"
#include "WS/Debugger/WsAssembler.h"
#include "WS/Debugger/WsDisUtils.h"
#include "WS/Debugger/WsEventManager.h"
#include "WS/Debugger/WsTraceLogger.h"
#include "WS/Debugger/WsPpuTools.h"
#include "WS/WsController.h"
#include "WS/WsCpu.h"
#include "WS/WsPpu.h"
#include "WS/WsConsole.h"
#include "WS/WsMemoryManager.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Disassembler.h"
#include "Debugger/MemoryDumper.h"
#include "Debugger/CallstackManager.h"
#include "Debugger/BreakpointManager.h"
#include "Debugger/Debugger.h"
#include "Debugger/MemoryAccessCounter.h"
#include "Debugger/ExpressionEvaluator.h"
#include "Debugger/MemoryDumper.h"
#include "Debugger/CodeDataLogger.h"
#include "Debugger/BaseEventManager.h"
#include "Debugger/StepBackManager.h"
#include "Utilities/Patches/IpsPatcher.h"
#include "Utilities/HexUtilities.h"
#include "Shared/EmuSettings.h"
#include "Shared/Emulator.h"
#include "Shared/BaseControlManager.h"
#include "Shared/MemoryOperationType.h"
WsDebugger::WsDebugger(Debugger* debugger) : IDebugger(debugger->GetEmulator())
{
_debugger = debugger;
_memoryDumper = debugger->GetMemoryDumper();
_emu = debugger->GetEmulator();
_disassembler = debugger->GetDisassembler();
_memoryAccessCounter = debugger->GetMemoryAccessCounter();
_console = ((WsConsole*)debugger->GetConsole());
_cpu = _console->GetCpu();
_ppu = _console->GetPpu();
_memoryManager = _console->GetMemoryManager();
_settings = debugger->GetEmulator()->GetSettings();
_codeDataLogger.reset(new CodeDataLogger(debugger, MemoryType::WsPrgRom, _emu->GetMemory(MemoryType::WsPrgRom).Size, CpuType::Ws, _emu->GetCrc32()));
_cdlFile = _codeDataLogger->GetCdlFilePath(_emu->GetRomInfo().RomFile.GetFileName());
_codeDataLogger->LoadCdlFile(_cdlFile, _settings->GetDebugConfig().AutoResetCdl);
_traceLogger.reset(new WsTraceLogger(debugger, this, _ppu));
_ppuTools.reset(new WsPpuTools(debugger, debugger->GetEmulator(), _console));
_stepBackManager.reset(new StepBackManager(_emu, this));
_eventManager.reset(new WsEventManager(debugger, _console, _cpu, _ppu));
_callstackManager.reset(new CallstackManager(debugger, this));
_breakpointManager.reset(new BreakpointManager(debugger, this, CpuType::Ws, _eventManager.get()));
_step.reset(new StepRequest());
_assembler.reset(new WsAssembler(debugger->GetLabelManager()));
_dummyCpu.reset(new DummyWsCpu(_emu, _memoryManager));
ResetPrevOpCode();
}
WsDebugger::~WsDebugger()
{
_codeDataLogger->SaveCdlFile(_cdlFile);
}
void WsDebugger::Reset()
{
_callstackManager->Clear();
ResetPrevOpCode();
}
void WsDebugger::ProcessInstruction()
{
WsCpuState& state = _cpu->GetState();
uint32_t pc = _cpu->GetProgramCounter(true);
AddressInfo addressInfo = _console->GetAbsoluteAddress(pc);
uint8_t value = _memoryManager->DebugRead(pc);
MemoryOperationInfo operation(pc, value, MemoryOperationType::ExecOpCode, MemoryType::WsMemory);
InstructionProgress.LastMemOperation = operation;
InstructionProgress.StartCycle = state.CycleCount;
if(addressInfo.Address >= 0) {
if(addressInfo.Type == MemoryType::WsPrgRom) {
if(WsDisUtils::IsConditionalJump(_prevOpCode)) {
uint8_t opSize = WsDisUtils::GetOpSize(_prevProgramCounter, MemoryType::WsMemory, _memoryDumper);
_codeDataLogger->SetCode(addressInfo.Address, WsDisUtils::GetOpFlags(_prevOpCode, pc, _prevProgramCounter, opSize));
} else {
_codeDataLogger->SetCode(addressInfo.Address, WsDisUtils::GetOpFlags(_prevOpCode, pc, _prevProgramCounter, 0));
}
}
_disassembler->BuildCache(addressInfo, 0, CpuType::Ws);
}
ProcessCallStackUpdates(addressInfo, pc);
if(_step->BreakAddress == (int32_t)pc) {
//If we're on the expected return address, break immediately (for step over/step out)
if(WsDisUtils::IsReturnInstruction(_prevOpCode)) {
_step->Break(BreakSource::CpuStep);
} else {
uint8_t prefix = _memoryManager->DebugRead(_prevProgramCounter);
if(prefix == 0xF2 || prefix == 0xF3) {
//REPZ/REPNZ prefix
_step->Break(BreakSource::CpuStep);
}
}
}
_prevOpCode = WsDisUtils::GetFullOpCode(state.CS, state.IP, _memoryManager);
_prevProgramCounter = pc;
if(_settings->CheckDebuggerFlag(DebuggerFlags::WsDebuggerEnabled)) {
if(_settings->GetDebugConfig().WsBreakOnInvalidOpCode && WsDisUtils::IsUndefinedOpCode(_prevOpCode)) {
_step->Break(BreakSource::BreakOnUndefinedOpCode);
}
}
_step->ProcessCpuExec();
if(_step->StepCount != 0 && _breakpointManager->HasBreakpoints() && _settings->GetDebugConfig().UsePredictiveBreakpoints) {
_dummyCpu->SetDummyState(state);
_dummyCpu->Exec();
for(uint32_t i = 1; i < _dummyCpu->GetOperationCount(); i++) {
MemoryOperationInfo memOp = _dummyCpu->GetOperationInfo(i);
if(_breakpointManager->HasBreakpointForType(memOp.Type)) {
AddressInfo absAddr = _console->GetAbsoluteAddress(memOp.Address);
_debugger->ProcessPredictiveBreakpoint(CpuType::Ws, _breakpointManager.get(), memOp, absAddr);
}
}
}
_debugger->ProcessBreakConditions(CpuType::Ws, *_step.get(), _breakpointManager.get(), operation, addressInfo);
}
template<uint8_t accessWidth>
void WsDebugger::ProcessRead(uint32_t addr, uint16_t value, MemoryOperationType type)
{
AddressInfo addressInfo = _console->GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, MemoryType::WsMemory);
InstructionProgress.LastMemOperation = operation;
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Ws);
_traceLogger->Log(_cpu->GetState(), disInfo, operation, addressInfo);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _console->GetMasterClock());
if(_step->ProcessCpuCycle()) {
_debugger->SleepUntilResume(CpuType::Ws, BreakSource::CpuStep, &operation);
}
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Address >= 0 && addressInfo.Type == MemoryType::WsPrgRom) {
_codeDataLogger->SetCode(addressInfo.Address);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation, addressInfo);
}
_memoryAccessCounter->ProcessMemoryExec<accessWidth>(addressInfo, _console->GetMasterClock());
_step->ProcessCpuCycle();
_debugger->ProcessBreakConditions(CpuType::Ws, *_step.get(), _breakpointManager.get(), operation, addressInfo);
} else {
if(addressInfo.Address >= 0 && addressInfo.Type == MemoryType::WsPrgRom) {
_codeDataLogger->SetData<0, accessWidth>(addressInfo.Address);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation, addressInfo);
}
if(addr < 0xFE00 || addr >= 0xFF80) {
ReadResult result = _memoryAccessCounter->ProcessMemoryRead<accessWidth>(addressInfo, _console->GetMasterClock());
if(result != ReadResult::Normal) {
//Memory access was a read on an uninitialized memory address
if(result == ReadResult::FirstUninitRead) {
//Only warn the first time
_debugger->Log("[WS] Uninitialized memory read: $" + HexUtilities::ToHex20(addr));
}
if(_settings->CheckDebuggerFlag(DebuggerFlags::WsDebuggerEnabled) && _settings->GetDebugConfig().BreakOnUninitRead) {
_step->Break(BreakSource::BreakOnUninitMemoryRead);
}
}
}
_step->ProcessCpuCycle();
_debugger->ProcessBreakConditions<accessWidth>(CpuType::Ws, *_step.get(), _breakpointManager.get(), operation, addressInfo);
}
}
template<uint8_t accessWidth>
void WsDebugger::ProcessWrite(uint32_t addr, uint16_t value, MemoryOperationType type)
{
AddressInfo addressInfo = _console->GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, MemoryType::WsMemory);
InstructionProgress.LastMemOperation = operation;
if(addressInfo.Type == MemoryType::WsWorkRam || addressInfo.Type == MemoryType::WsCartRam) {
if(addressInfo.Type == MemoryType::WsWorkRam) {
bool isMono = _ppu->GetState().Mode == WsVideoMode::Monochrome;
if(isMono && addressInfo.Address >= 0x2000 && addressInfo.Address <= 0x3FFF) {
_eventManager->AddEvent(DebugEventType::Register, operation);
} else if(!isMono && (addressInfo.Address >= 0xFE00 || (addressInfo.Address >= 0x4000 && addressInfo.Address <= 0xBFFF))) {
_eventManager->AddEvent(DebugEventType::Register, operation);
}
}
_disassembler->InvalidateCache(addressInfo, CpuType::Ws);
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation, addressInfo);
}
_memoryAccessCounter->ProcessMemoryWrite<accessWidth>(addressInfo, _console->GetMasterClock());
_step->ProcessCpuCycle();
_debugger->ProcessBreakConditions<accessWidth>(CpuType::Ws, *_step.get(), _breakpointManager.get(), operation, addressInfo);
}
template<MemoryOperationType opType, typename T>
void WsDebugger::ProcessMemoryAccess(uint32_t addr, T value, MemoryType memType)
{
MemoryOperationInfo operation(addr, value, opType, memType);
_eventManager->AddEvent(DebugEventType::Register, operation);
}
void WsDebugger::Run()
{
_step.reset(new StepRequest());
}
void WsDebugger::Step(int32_t stepCount, StepType type)
{
StepRequest step(type);
switch(type) {
case StepType::Step: step.StepCount = stepCount; break;
case StepType::StepOut: step.BreakAddress = _callstackManager->GetReturnAddress(); break;
case StepType::StepOver:
if(WsDisUtils::IsJumpToSub(_prevOpCode)) {
step.BreakAddress = _prevProgramCounter + GetPrevOpCodeSize();
} else {
uint8_t prefix = _memoryManager->DebugRead(_prevProgramCounter);
if(prefix == 0xF2 || prefix == 0xF3) {
//REPZ/REPNZ prefixed instruction
step.BreakAddress = _prevProgramCounter + GetPrevOpCodeSize();
} else {
//For any other instruction, step over is the same as step into
step.StepCount = 1;
}
}
break;
case StepType::CpuCycleStep: step.CpuCycleStepCount = stepCount; break;
case StepType::PpuStep: step.PpuStepCount = stepCount; break;
case StepType::PpuScanline: step.PpuStepCount = 256 * stepCount; break;
case StepType::PpuFrame: step.PpuStepCount = 256 * _ppu->GetScanlineCount() * stepCount; break;
case StepType::SpecificScanline: step.BreakScanline = stepCount; break;
}
_step.reset(new StepRequest(step));
}
StepBackConfig WsDebugger::GetStepBackConfig()
{
return {
_cpu->GetCycleCount(),
256,
256u * _ppu->GetScanlineCount()
};
}
void WsDebugger::DrawPartialFrame()
{
_ppu->DebugSendFrame();
}
uint8_t WsDebugger::GetPrevOpCodeSize()
{
return WsDisUtils::GetOpSize(_prevProgramCounter, MemoryType::WsMemory, _debugger->GetMemoryDumper());
}
void WsDebugger::ProcessCallStackUpdates(AddressInfo& destAddr, uint32_t destPc)
{
if(WsDisUtils::IsJumpToSub(_prevOpCode) && destPc != _prevProgramCounter + GetPrevOpCodeSize()) {
//CALL and RST, and PC doesn't match the next instruction, so the call was (probably) done
uint8_t opSize = GetPrevOpCodeSize();
uint32_t returnPc = _prevProgramCounter + opSize;
AddressInfo src = _console->GetAbsoluteAddress(_prevProgramCounter);
AddressInfo ret = _console->GetAbsoluteAddress(returnPc);
_callstackManager->Push(src, _prevProgramCounter, destAddr, destPc, ret, returnPc, StackFrameFlags::None);
} else if(WsDisUtils::IsReturnInstruction(_prevOpCode) && destPc != _prevProgramCounter + GetPrevOpCodeSize()) {
//RET used, and PC doesn't match the next instruction, so the ret was (probably) taken
_callstackManager->Pop(destAddr, destPc);
}
}
void WsDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{
AddressInfo src = _console->GetAbsoluteAddress(_prevProgramCounter);
AddressInfo ret = _console->GetAbsoluteAddress(originalPc);
AddressInfo dest = _console->GetAbsoluteAddress(currentPc);
if(dest.Type == MemoryType::WsPrgRom && dest.Address >= 0) {
_codeDataLogger->SetCode(dest.Address, CdlFlags::SubEntryPoint);
}
//If a call/return occurred just before IRQ, it needs to be processed now
ProcessCallStackUpdates(ret, originalPc);
ResetPrevOpCode();
_debugger->InternalProcessInterrupt(
CpuType::Ws, *this, *_step.get(),
src, _prevProgramCounter, dest, currentPc, ret, originalPc, forNmi
);
}
void WsDebugger::ProcessPpuCycle()
{
if(_ppuTools->HasOpenedViewer()) {
_ppuTools->UpdateViewers(_ppu->GetScanline(), _ppu->GetCycle());
}
if(_step->HasRequest) {
if(_step->HasScanlineBreakRequest() && _ppu->GetCycle() == 0 && _ppu->GetScanline() == _step->BreakScanline) {
_debugger->SleepUntilResume(CpuType::Ws, _step->GetBreakSource());
} else if(_step->PpuStepCount > 0) {
_step->PpuStepCount--;
if(_step->PpuStepCount == 0) {
_debugger->SleepUntilResume(CpuType::Ws, _step->GetBreakSource());
}
}
}
}
DebuggerFeatures WsDebugger::GetSupportedFeatures()
{
DebuggerFeatures features = {};
features.RunToIrq = true;
features.RunToNmi = false;
features.StepOver = true;
features.StepOut = true;
features.StepBack = true;
features.CallStack = true;
features.CpuCycleStep = false;
features.ChangeProgramCounter = AllowChangeProgramCounter;
features.CpuVectors[0] = { "DIV0", 0, VectorType::x86 };
features.CpuVectors[1] = { "TRAP", 1 * 4, VectorType::x86 };
features.CpuVectors[2] = { "INT3", 3 * 4, VectorType::x86 };
features.CpuVectors[3] = { "INTO", 4 * 4, VectorType::x86 };
features.CpuVectors[4] = { "BOUND", 5 * 4, VectorType::x86 };
features.IrqVectorOffset = _memoryManager->GetState().IrqVectorOffset;
features.CpuVectors[5] = { "UART Send", 0, VectorType::x86WithOffset };
features.CpuVectors[6] = { "Key Pressed", 1, VectorType::x86WithOffset };
features.CpuVectors[7] = { "Cartridge", 2, VectorType::x86WithOffset };
features.CpuVectors[8] = { "UART Receive", 3, VectorType::x86WithOffset };
features.CpuVectors[9] = { "Scanline", 4, VectorType::x86WithOffset };
features.CpuVectors[10] = { "V.Blank Timer", 5, VectorType::x86WithOffset };
features.CpuVectors[11] = { "Vertical Blank", 6, VectorType::x86WithOffset };
features.CpuVectors[12] = { "H.Blank Timer", 7, VectorType::x86WithOffset };
features.CpuVectorCount = 13;
return features;
}
void WsDebugger::SetProgramCounter(uint32_t addr, bool updateDebuggerOnly)
{
if(!updateDebuggerOnly) {
WsCpuState& state = _cpu->GetState();
WsDisUtils::UpdateAddressCsIp(addr, state);
_cpu->ClearPrefetch();
}
_prevOpCode = WsDisUtils::GetFullOpCode(_cpu->GetState().CS, _cpu->GetState().IP, _memoryManager);
_prevProgramCounter = addr;
}
uint32_t WsDebugger::GetProgramCounter(bool getInstPc)
{
return getInstPc ? _prevProgramCounter : _cpu->GetProgramCounter();
}
uint64_t WsDebugger::GetCpuCycleCount(bool forProfiler)
{
return _cpu->GetState().CycleCount;
}
void WsDebugger::ResetPrevOpCode()
{
_prevOpCode = 0x90; //nop
}
BaseEventManager* WsDebugger::GetEventManager()
{
return _eventManager.get();
}
IAssembler* WsDebugger::GetAssembler()
{
return _assembler.get();
}
CallstackManager* WsDebugger::GetCallstackManager()
{
return _callstackManager.get();
}
BreakpointManager* WsDebugger::GetBreakpointManager()
{
return _breakpointManager.get();
}
BaseState& WsDebugger::GetState()
{
return _cpu->GetState();
}
void WsDebugger::GetPpuState(BaseState& state)
{
(WsPpuState&)state = _ppu->GetState();
}
void WsDebugger::SetPpuState(BaseState& srcState)
{
WsPpuState& dstState = _ppu->GetState();
dstState = (WsPpuState&)srcState;
}
ITraceLogger* WsDebugger::GetTraceLogger()
{
return _traceLogger.get();
}
PpuTools* WsDebugger::GetPpuTools()
{
return _ppuTools.get();
}
bool WsDebugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption)
{
vector<uint8_t> output;
uint8_t* prgRom = _debugger->GetMemoryDumper()->GetMemoryBuffer(MemoryType::WsPrgRom);
uint32_t prgRomSize = _debugger->GetMemoryDumper()->GetMemorySize(MemoryType::WsPrgRom);
vector<uint8_t> rom = vector<uint8_t>(prgRom, prgRom + prgRomSize);
if(saveAsIps) {
vector<uint8_t> originalRom;
_emu->GetRomInfo().RomFile.ReadFile(originalRom);
output = IpsPatcher::CreatePatch(originalRom, rom);
} else {
if(stripOption != CdlStripOption::StripNone) {
_codeDataLogger->StripData(rom.data(), stripOption);
//Preserve WS header
memcpy(rom.data() + rom.size() - 0x10, prgRom + prgRomSize - 0x10, 0x10);
}
output = rom;
}
ofstream file(filename, ios::out | ios::binary);
if(file) {
file.write((char*)output.data(), output.size());
file.close();
return true;
}
return false;
}
void WsDebugger::ProcessInputOverrides(DebugControllerState inputOverrides[8])
{
BaseControlManager* controlManager = _console->GetControlManager();
for(int i = 0; i < 8; i++) {
shared_ptr<WsController> controller = std::dynamic_pointer_cast<WsController>(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);
}
}
controlManager->RefreshHubState();
}
template void WsDebugger::ProcessMemoryAccess<MemoryOperationType::Read>(uint32_t addr, uint8_t value, MemoryType memType);
template void WsDebugger::ProcessMemoryAccess<MemoryOperationType::Read>(uint32_t addr, uint16_t value, MemoryType memType);
template void WsDebugger::ProcessMemoryAccess<MemoryOperationType::Write>(uint32_t addr, uint8_t value, MemoryType memType);
template void WsDebugger::ProcessMemoryAccess<MemoryOperationType::Write>(uint32_t addr, uint16_t value, MemoryType memType);
template void WsDebugger::ProcessRead<1>(uint32_t addr, uint16_t value, MemoryOperationType type);
template void WsDebugger::ProcessRead<2>(uint32_t addr, uint16_t value, MemoryOperationType type);
template void WsDebugger::ProcessWrite<1>(uint32_t addr, uint16_t value, MemoryOperationType type);
template void WsDebugger::ProcessWrite<2>(uint32_t addr, uint16_t value, MemoryOperationType type);
+102
View File
@@ -0,0 +1,102 @@
#pragma once
#include "pch.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/IDebugger.h"
class MemoryDumper;
class Disassembler;
class Debugger;
class WsTraceLogger;
class WsConsole;
class WsMemoryManager;
class CallstackManager;
class MemoryAccessCounter;
class BreakpointManager;
class EmuSettings;
class WsAssembler;
class Emulator;
class CodeDataLogger;
class DummyWsCpu;
class WsCpu;
class WsPpu;
class WsPpuTools;
class WsEventManager;
class DummyWsCpu;
enum class MemoryOperationType;
class WsDebugger final : public IDebugger
{
Debugger* _debugger = nullptr;
MemoryDumper* _memoryDumper = nullptr;
Emulator* _emu = nullptr;
WsCpu* _cpu = nullptr;
WsPpu* _ppu = nullptr;
WsMemoryManager* _memoryManager = nullptr;
Disassembler* _disassembler = nullptr;
MemoryAccessCounter* _memoryAccessCounter = nullptr;
WsConsole* _console = nullptr;
EmuSettings* _settings = nullptr;
unique_ptr<WsEventManager> _eventManager;
unique_ptr<WsPpuTools> _ppuTools;
unique_ptr<CallstackManager> _callstackManager;
unique_ptr<CodeDataLogger> _codeDataLogger;
unique_ptr<BreakpointManager> _breakpointManager;
unique_ptr<WsAssembler> _assembler;
unique_ptr<WsTraceLogger> _traceLogger;
unique_ptr<DummyWsCpu> _dummyCpu;
uint16_t _prevOpCode = 0;
uint32_t _prevProgramCounter = 0;
string _cdlFile;
__forceinline uint8_t GetPrevOpCodeSize();
__forceinline void ProcessCallStackUpdates(AddressInfo& destAddr, uint32_t destPc);
public:
WsDebugger(Debugger* debugger);
~WsDebugger();
void Reset() override;
void ProcessInstruction();
template<uint8_t accessWidth> void ProcessRead(uint32_t addr, uint16_t value, MemoryOperationType type);
template<uint8_t accessWidth> void ProcessWrite(uint32_t addr, uint16_t value, MemoryOperationType type);
template<MemoryOperationType opType, typename T>
void ProcessMemoryAccess(uint32_t addr, T value, MemoryType memType);
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override;
void ProcessPpuCycle();
void Run() override;
void Step(int32_t stepCount, StepType type) override;
StepBackConfig GetStepBackConfig() override;
void DrawPartialFrame() override;
bool SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption);
void ProcessInputOverrides(DebugControllerState inputOverrides[8]) override;
void SetProgramCounter(uint32_t addr, bool updateDebuggerOnly = false) override;
uint32_t GetProgramCounter(bool getInstPc) override;
uint64_t GetCpuCycleCount(bool forProfiler) override;
void ResetPrevOpCode() override;
DebuggerFeatures GetSupportedFeatures() override;
BaseEventManager* GetEventManager() override;
IAssembler* GetAssembler() override;
CallstackManager* GetCallstackManager() override;
BreakpointManager* GetBreakpointManager() override;
ITraceLogger* GetTraceLogger() override;
PpuTools* GetPpuTools() override;
BaseState& GetState() override;
void GetPpuState(BaseState& state) override;
void SetPpuState(BaseState& state) override;
};
+729
View File
@@ -0,0 +1,729 @@
#include "pch.h"
#include "WS/Debugger/WsDisUtils.h"
#include "WS/Debugger/DummyWsCpu.h"
#include "WS/WsTypes.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsConsole.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/LabelManager.h"
#include "Debugger/MemoryDumper.h"
#include "Shared/MemoryType.h"
#include "Shared/EmuSettings.h"
#include "Utilities/FastString.h"
#include "Utilities/HexUtilities.h"
static constexpr const char* _modRegLut8[8] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
static constexpr const char* _modRegLut16[8] = { "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI" };
static constexpr const char* _modSegRegLut[4] = { "ES", "CS", "SS", "DS" };
constexpr const char* _opTemplate[256] = {
//0-0F
"ADD m", "ADD m", "ADD m", "ADD m", "ADD AL, i", "ADD AX, j", "PUSH ES", "POP ES",
"OR m", "OR m", "OR m", "OR m", "OR AL, i", "OR AX, j", "PUSH CS", "UNDEFINED_0F",
//10-1F
"ADC m", "ADC m", "ADC m", "ADC m", "ADC AL, i", "ADC AX, j", "PUSH SS", "POP SS",
"SBB m", "SBB m", "SBB m", "SBB m", "SBB AL, i", "SBB AX, j", "PUSH DS", "POP DS",
//20-2F
"AND m", "AND m", "AND m", "AND m", "AND AL, i", "AND AX, j", "ES:", "DAA",
"SUB m", "SUB m", "SUB m", "SUB m", "SUB AL, i", "SUB AX, j", "CS:", "DAS",
//30-3F
"XOR m", "XOR m", "XOR m", "XOR m", "XOR AL, i", "XOR AX, j", "SS:", "AAA",
"CMP m", "CMP m", "CMP m", "CMP m", "CMP AL, i", "CMP AX, j", "DS:", "AAS",
//40-4F
"INC AX", "INC CX", "INC DX", "INC BX", "INC SP", "INC BP", "INC SI", "INC DI",
"DEC AX", "DEC CX", "DEC DX", "DEC BX", "DEC SP", "DEC BP", "DEC SI", "DEC DI",
//50-5F
"PUSH AX", "PUSH CX", "PUSH DX", "PUSH BX", "PUSH SP", "PUSH BP", "PUSH SI", "PUSH DI",
"POP AX", "POP CX", "POP DX", "POP BX", "POP SP", "POP BP", "POP SI", "POP DI",
//60-6F
"PUSHA", "POPA", "BOUND p", "UNDEFINED_63", "UNDEFINED_64", "UNDEFINED_65", "UNDEFINED_66", "UNDEFINED_67",
"PUSH j", "IMUL n, j", "PUSH i", "IMUL n, i", "INSB", "INSW", "OUTSB", "OUTSW",
//70-7F
"JO r", "JNO r", "JB r", "JNB r", "JZ r", "JNZ r", "JBE r", "JA r",
"JS r", "JNS r", "JPE r", "JPO r", "JL r", "JGE r", "JLE r", "JG r",
//80-8F
"v", "v", "v", "v", "TEST m", "TEST m", "XCHG m", "XCHG m",
"MOV m", "MOV m", "MOV m", "MOV m", "MOV q", "LEA o", "MOV q", "POP n",
//90-9F
"NOP", "XCHG CX, AX", "XCHG DX, AX", "XCHG BX, AX", "XCHG SP, AX", "XCHG BP, AX", "XCHG SI, AX", "XCHG DI, AX",
"CBW", "CWD", "CALL t", "WAIT", "PUSHF", "POPF", "SAHF", "LAHF",
//A0-AF
"MOV AL, a", "MOV AX, a", "MOV a, AL", "MOV a, AX", "MOVSB", "MOVSW", "CMPSB", "CMPSW",
"TEST AL, i", "TEST AX, j", "STOSB", "STOSW", "LODSB", "LODSW", "SCASB", "SCASW",
//B0-BF
"MOV AL, i", "MOV CL, i", "MOV DL, i", "MOV BL, i", "MOV AH, i", "MOV CH, i", "MOV DH, i", "MOV BH, i",
"MOV AX, j", "MOV CX, j", "MOV DX, j", "MOV BX, j", "MOV SP, j", "MOV BP, j", "MOV SI, j", "MOV DI, j",
//C0-CF
"w, i", "w, i", "RET j", "RET", "LES o", "LDS o", "MOV n, i", "MOV n, j",
"ENTER j, i", "LEAVE", "RETF j", "RETF", "INT 3", "INT i" , "INTO", "IRET",
//D0-DF
"w", "w", "w, CL", "w, CL", "AAM i", "AAD i", "SALC", "XLAT",
"UNDEFINED_D8", "UNDEFINED_D9", "UNDEFINED_DA", "UNDEFINED_DB", "UNDEFINED_DC", "UNDEFINED_DD", "UNDEFINED_DE", "UNDEFINED_DF",
//E0-EF
"LOOPNZ r", "LOOPZ r", "LOOP r", "JCXZ r", "IN AL, i", "IN AX, i", "OUT i, AL", "OUT i, AX",
"CALL s", "JMP s", "JMP t", "JMP r", "IN AL, DX", "IN AX, DX", "OUT DX, AL", "OUT DX, AX",
//F0-FF
"LOCK", "UNDEFINED_F1", "REPNZ", "REPZ", "HLT", "CMC", "x", "x",
"CLC", "STC", "CLI", "STI", "CLD", "STD", "y", "z"
};
void WsDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
{
FastString str(settings->GetDebugConfig().UseLowerCaseDisassembly);
uint8_t* byteCode = info.GetByteCode();
uint8_t opCode = byteCode[0];
WsSegment segment = WsSegment::Default;
while(IsPrefix(opCode)) {
switch(opCode) {
case 0x26: segment = WsSegment::ES; break;
case 0x36: segment = WsSegment::SS; break;
case 0x2E: segment = WsSegment::CS; break;
case 0x3E: segment = WsSegment::DS; break;
case 0xF0:
case 0xF2:
case 0xF3:
//rep, lock
str.WriteAll(_opTemplate[opCode], ' ');
break;
}
byteCode++;
opCode = byteCode[0];
}
const char* op = _opTemplate[opCode];
int i = 0;
while(op[i]) {
switch(op[i]) {
//Address offset
case 'a':
GetSegment(str, segment, "DS");
str.WriteAll("[$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))), "]");
break;
//Immediate values
case 'i': str.WriteAll("$", HexUtilities::ToHex(byteCode[1])); byteCode++; break;
case 'j': str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8)))); byteCode += 2; break;
//ModRM (2 operands)
case 'm': {
bool word = opCode & 0x01;
bool direction = opCode & 0x02;
if(direction) {
GetModRegParam(str, byteCode, word);
str.Write(", ");
GetModRmParam(str, byteCode, segment, word);
} else {
GetModRmParam(str, byteCode, segment, word);
str.Write(", ");
GetModRegParam(str, byteCode, word);
}
break;
}
//ModRM (+ immediate value)
case 'n': {
bool word = op[i] == 'l';
byteCode += GetModRmParam(str, byteCode, segment, word);
break;
}
//ModRM (2 operands, LEA/LDS/LES)
case 'o':
GetModRegParam(str, byteCode, true);
str.Write(", ");
GetModRmParam(str, byteCode, segment, true, true);
break;
//ModRM (2 operands, BOUND)
case 'p':
GetModRegParam(str, byteCode, true);
str.Write(", ");
GetModRmParam(str, byteCode, segment, true, false);
break;
//ModRM (mov segment)
case 'q': {
bool direction = opCode & 0x02;
if(direction) {
GetModSegRegParam(str, byteCode);
str.Write(", ");
GetModRmParam(str, byteCode, segment, true);
} else {
GetModRmParam(str, byteCode, segment, true);
str.Write(", ");
GetModSegRegParam(str, byteCode);
}
break;
}
//Relative jumps
case 'r': GetJmpDestination(str, byteCode, 1); break;
case 's': GetJmpDestination(str, byteCode, 2); break;
case 't': GetJmpDestination(str, byteCode, 4); break;
case 'v': //GRP1
switch((byteCode[1] >> 3) & 0x07) {
case 0: str.Write("ADD "); break;
case 1: str.Write("OR "); break;
case 2: str.Write("ADC "); break;
case 3: str.Write("SBB "); break;
case 4: str.Write("AND "); break;
case 5: str.Write("SUB "); break;
case 6: str.Write("XOR "); break;
case 7: str.Write("CMP "); break;
}
byteCode += GetModRmParam(str, byteCode, segment, opCode & 0x01);
str.Write(", ");
if((opCode & 0x03) == 0x01) {
str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))));
} else if((opCode & 0x03) == 0x03) {
str.WriteAll("$", HexUtilities::ToHex((uint16_t)(int16_t)(int8_t)byteCode[1]));
} else {
str.WriteAll("$", HexUtilities::ToHex(byteCode[1]));
}
break;
case 'w': { //GRP2
switch((byteCode[1] >> 3) & 0x07) {
case 0: str.Write("ROL "); break;
case 1: str.Write("ROR "); break;
case 2: str.Write("RCL "); break;
case 3: str.Write("RCR "); break;
case 4: str.Write("SHL "); break;
case 5: str.Write("SHR "); break;
case 6: str.Write("UNDEFINED_GRP2_6 "); break;
case 7: str.Write("SAR "); break;
}
//Followed by an immediate value for C0/C1
byteCode += GetModRmParam(str, byteCode, segment, opCode & 0x01);
break;
}
case 'x': { //GRP3
uint8_t mode = (byteCode[1] >> 3) & 0x07;
bool word = opCode & 0x01;
switch(mode) {
case 0: str.Write("TEST "); break;
case 1: str.Write("UNDEFINED_GRP3_1 "); break;
case 2: str.Write("NOT "); break;
case 3: str.Write("NEG "); break;
case 4: str.WriteAll("MUL ", word ? "AX, " : ""); break;
case 5: str.WriteAll("IMUL ", word ? "AX, " : ""); break;
case 6: str.WriteAll("DIV ", word ? "AX, " : ""); break;
case 7: str.WriteAll("IDIV ", word ? "AX, " : ""); break;
}
byteCode += GetModRmParam(str, byteCode, segment, word);
if(mode == 0) {
if(word) {
str.WriteAll(", $", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))));
} else {
str.WriteAll(", $", HexUtilities::ToHex(byteCode[1]));
}
}
break;
}
case 'y': //grp 4
case 'z': //grp 5
switch((byteCode[1] >> 3) & 0x07) {
case 0: str.Write("INC "); break;
case 1: str.Write("DEC "); break;
case 2: str.Write("CALL "); break;
case 3: str.Write("CALL FAR "); break;
case 4: str.Write("JMP "); break;
case 5: str.Write("JMP FAR "); break;
case 6: str.Write("PUSH "); break;
case 7: str.Write("UNDEFINED_GRP5_7 "); break;
}
GetModRmParam(str, byteCode, segment, opCode & 0x01);
break;
default: str.Write(op[i]); break;
}
i++;
}
out += str.ToString();
}
void WsDisUtils::GetJmpDestination(FastString& str, uint8_t* byteCode, uint8_t size)
{
if(size == 4) {
str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[3] | (byteCode[4] << 8))));
str.WriteAll(":$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))));
} else if(size == 2) {
uint16_t jmpOffset = (uint16_t)(byteCode[1] | (byteCode[2] << 8));
str.WriteAll("+$", HexUtilities::ToHex(jmpOffset));
} else if(size == 1) {
uint8_t jmpOffset = (uint8_t)byteCode[1];
str.WriteAll("+$", HexUtilities::ToHex(jmpOffset));
}
}
void WsDisUtils::GetSegment(FastString& str, WsSegment segment, const char* defaultSegment)
{
switch(segment) {
default: case WsSegment::Default: str.WriteAll(defaultSegment, ':'); break;
case WsSegment::ES: str.Write("ES:"); break;
case WsSegment::SS: str.Write("SS:"); break;
case WsSegment::CS: str.Write("CS:"); break;
case WsSegment::DS: str.Write("DS:"); break;
}
}
void WsDisUtils::GetModRegParam(FastString& str, uint8_t* byteCode, bool word)
{
uint8_t modRm = byteCode[1];
uint8_t reg = (modRm >> 3) & 0x07;
str.Write(word ? _modRegLut16[reg] : _modRegLut8[reg]);
}
void WsDisUtils::GetModSegRegParam(FastString& str, uint8_t* byteCode)
{
uint8_t modRm = byteCode[1];
uint8_t reg = (modRm >> 3) & 0x03;
str.Write(_modSegRegLut[reg]);
}
int WsDisUtils::GetModRmParam(FastString& str, uint8_t* byteCode, WsSegment segment, bool word, bool forLeaLdsLes)
{
uint8_t modRm = byteCode[1];
uint8_t mode = (modRm >> 6) & 0x03;
uint8_t rm = modRm & 0x07;
int len = 1;
string param;
string param2;
if(mode == 3) {
if(forLeaLdsLes) {
switch(rm) {
case 0x00: GetSegment(str, segment, "DS"); str.Write("[BX+AX]"); break;
case 0x01: GetSegment(str, segment, "DS"); str.Write("[BX+CX]"); break;
case 0x02: GetSegment(str, segment, "SS"); str.Write("[BP+DX]"); break;
case 0x03: GetSegment(str, segment, "SS"); str.Write("[BP+BX]"); break;
case 0x04: GetSegment(str, segment, "DS"); str.Write("[SP+SI]"); break;
case 0x05: GetSegment(str, segment, "DS"); str.Write("[BP+DI]"); break;
case 0x06: GetSegment(str, segment, "SS"); str.Write("[BP+SI]"); break;
case 0x07: GetSegment(str, segment, "DS"); str.Write("[BX+DI]"); break;
}
} else {
str.Write(word ? _modRegLut16[rm] : _modRegLut8[rm]);
}
} else if(mode == 0 && rm == 0x06) {
GetSegment(str, segment, "DS");
str.WriteAll("[$", HexUtilities::ToHex((uint16_t)(byteCode[2] | (byteCode[3] << 8))), ']');
len += 2;
} else {
switch(rm) {
case 0x00: GetSegment(str, segment, "DS"); str.Write("[BX+SI"); break;
case 0x01: GetSegment(str, segment, "DS"); str.Write("[BX+DI"); break;
case 0x02: GetSegment(str, segment, "SS"); str.Write("[BP+SI"); break;
case 0x03: GetSegment(str, segment, "SS"); str.Write("[BP+DI"); break;
case 0x04: GetSegment(str, segment, "DS"); str.Write("[SI"); break;
case 0x05: GetSegment(str, segment, "DS"); str.Write("[DI"); break;
case 0x06: GetSegment(str, segment, "SS"); str.Write("[BP"); break;
case 0x07: GetSegment(str, segment, "DS"); str.Write("[BX"); break;
}
if(mode == 1) {
str.WriteAll("+$", HexUtilities::ToHex((uint8_t)byteCode[2]), ']');
len++;
} else if(mode == 2) {
str.WriteAll("+$", HexUtilities::ToHex((uint16_t)(byteCode[2] | (byteCode[3] << 8))), ']');
len += 2;
} else {
str.Write(']');
}
}
return len;
}
EffectiveAddressInfo WsDisUtils::GetEffectiveAddress(DisassemblyInfo& info, WsConsole* console, WsCpuState& state)
{
DummyWsCpu dummyCpu(nullptr, console->GetMemoryManager());
dummyCpu.SetDummyState(state);
dummyCpu.Exec();
uint16_t opCode = WsDisUtils::GetFullOpCode(info);
if(WsDisUtils::IsReturnInstruction(opCode) || WsDisUtils::IsPushPopInstruction(opCode)) {
return {};
}
if((uint8_t)opCode == 0xC8 || (uint8_t)opCode == 0xC9) {
//Show nothing for Enter/Leave
return {};
}
if(WsDisUtils::IsConditionalJump(opCode) || WsDisUtils::IsUnconditionalJump(opCode)) {
EffectiveAddressInfo result;
result.ShowAddress = true;
result.Address = dummyCpu.GetProgramCounter();
result.Type = MemoryType::WsMemory;
return result;
}
//For everything else, return the last read/write address
uint32_t count = dummyCpu.GetOperationCount();
for(int i = count - 1; i > 0; i--) {
MemoryOperationInfo opInfo = dummyCpu.GetOperationInfo(i);
if(opInfo.Type != MemoryOperationType::ExecOperand) {
MemoryOperationInfo prevOpInfo = dummyCpu.GetOperationInfo(i - 1);
EffectiveAddressInfo result;
result.ShowAddress = true;
if(dummyCpu.IsWordAccess(i)) {
result.Address = opInfo.Address;
result.Type = opInfo.MemType;
result.ValueSize = 2;
} else {
if(prevOpInfo.Type == opInfo.Type && prevOpInfo.Address == opInfo.Address - 1) {
//For 16-bit read/writes that were split into 2 8-bit accesses, return the first address
result.Address = prevOpInfo.Address;
result.Type = prevOpInfo.MemType;
result.ValueSize = 2;
} else {
result.Address = opInfo.Address;
result.Type = opInfo.MemType;
result.ValueSize = 1;
}
}
return result;
}
}
return {};
}
int WsDisUtils::GetModRmSize(uint8_t modRm)
{
uint8_t mode = (modRm >> 6) & 0x03;
uint8_t rm = modRm & 0x07;
if(mode == 2 || (mode == 0 && rm == 0x06)) {
return 3;
} else if(mode == 1) {
return 2;
}
return 1;
}
bool WsDisUtils::IsPrefix(uint8_t opCode)
{
return opCode == 0xF0 || opCode == 0xF2 || opCode == 0xF3 || opCode == 0x26 || opCode == 0x36 || opCode == 0x2E || opCode == 0x3F;
}
uint8_t WsDisUtils::GetOpSize(uint32_t cpuAddress, MemoryType memType, MemoryDumper* memoryDumper)
{
uint8_t size = 1;
uint8_t opCode = memoryDumper->GetMemoryValue(memType, cpuAddress);
while(IsPrefix(opCode)) {
cpuAddress++;
opCode = memoryDumper->GetMemoryValue(memType, cpuAddress);
size++;
}
const char* op = _opTemplate[opCode];
int i = 0;
while(op[i]) {
switch(op[i]) {
//Absolute address
case 'a': size+=2; break;
//Immediate values
case 'i': size++; break;
case 'j': size += 2; break;
//ModRM
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
size += GetModRmSize(memoryDumper->GetMemoryValue(memType, cpuAddress + 1));
break;
//Relative jumps
case 'r': size++; break;
case 's': size += 2; break;
case 't': size += 4; break;
case 'v': //grp1
size += GetModRmSize(memoryDumper->GetMemoryValue(memType, cpuAddress + 1));
size++;
if((opCode & 0x3) == 0x01) {
size++;
}
break;
case 'x': { //grp3
uint8_t modRm = memoryDumper->GetMemoryValue(memType, cpuAddress + 1);
size += GetModRmSize(modRm);
if(((modRm >> 3) & 0x07) == 0) {
size += (opCode & 0x01) ? 2 : 1;
}
break;
}
case 'w': //grp2
case 'y': //grp4
case 'z': //grp5
size += GetModRmSize(memoryDumper->GetMemoryValue(memType, cpuAddress + 1));
break;
default: break;
}
i++;
}
return size;
}
uint16_t WsDisUtils::GetFullOpCode(uint16_t cs, uint16_t ip, WsMemoryManager* memoryManager)
{
int i = 0;
while(IsPrefix(memoryManager->DebugRead((cs << 4) + ip)) && i < 6) {
ip++;
i++;
}
return (
memoryManager->DebugRead((cs << 4) + ip) |
(memoryManager->DebugRead((cs << 4) + (uint16_t)(ip + 1)) << 8)
);
}
uint16_t WsDisUtils::GetFullOpCode(DisassemblyInfo& disInfo)
{
uint8_t* byteCode = disInfo.GetByteCode();
int i = 0;
while(IsPrefix(byteCode[i]) && i < 6) {
i++;
}
return byteCode[i] | (byteCode[i+1] << 8);
}
bool WsDisUtils::IsJumpToSub(uint16_t opCode)
{
switch((uint8_t)opCode) {
case 0x9A:
case 0xE8:
return true;
case 0xFE:
case 0xFF: {
uint8_t mode = (opCode >> 11) & 0x07;
switch(mode) {
case 2: //CALL
case 3: //CALL FAR
return true;
default:
return false;
}
}
}
return false;
}
bool WsDisUtils::IsReturnInstruction(uint16_t opCode)
{
switch((uint8_t)opCode) {
case 0xC2: //RET
case 0xC3: //RET
case 0xCA: //RETF
case 0xCB: //RETF
case 0xCF: //IRET
return true;
}
return false;
}
bool WsDisUtils::IsUnconditionalJump(uint16_t opCode)
{
switch((uint8_t)opCode) {
case 0x9A: //CALL
case 0xC2: //RET
case 0xC3: //RET
case 0xCA: //RETF
case 0xCB: //RETF
case 0xCF: //IRET
case 0xE8: //CALL relative
case 0xE9: //JMP
case 0xEA: //JMP
case 0xEB: //JMP
return true;
case 0xFE:
case 0xFF: {
uint8_t mode = (opCode >> 11) & 0x07;
switch(mode) {
case 2: //CALL
case 3: //CALL FAR
case 4: //JMP
case 5: //JMP FAR
return true;
default:
return false;
}
}
}
return false;
}
bool WsDisUtils::IsConditionalJump(uint16_t opCode)
{
switch((uint8_t)opCode) {
case 0x70: //JO
case 0x71: //JNO
case 0x72: //JB
case 0x73: //JNB
case 0x74: //JZ
case 0x75: //JNZ
case 0x76: //JBE
case 0x77: //JA
case 0x78: //JS
case 0x79: //JNS
case 0x7A: //JPE
case 0x7B: //JPO
case 0x7C: //JL
case 0x7D: //JGE
case 0x7E: //JLE
case 0x7F: //JG
case 0xE0: //LOOPNZ
case 0xE1: //LOOPZ
case 0xE2: //LOOP
case 0xE3: //JCXZ
return true;
}
return false;
}
bool WsDisUtils::IsUndefinedOpCode(uint16_t opCode)
{
switch((uint8_t)opCode) {
case 0x0F:
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0xD8:
case 0xD9:
case 0xDA:
case 0xDB:
case 0xDC:
case 0xDD:
case 0xDE:
case 0xDF:
case 0xF1:
return true;
case 0xC0:
case 0xC1:
case 0xD0:
case 0xD1:
case 0xD2:
case 0xD3:
return ((opCode >> 11) & 0x07) == 0x06;
case 0xF6:
case 0xF7:
return ((opCode >> 11) & 0x07) == 0x01;
case 0xFE:
case 0xFF:
return ((opCode >> 11) & 0x07) == 0x07;
default:
return false;
}
}
bool WsDisUtils::IsPushPopInstruction(uint16_t opCode)
{
switch((uint8_t)opCode) {
//Push/pop segments
case 0x06: case 0x07: case 0x0E: case 0x16:
case 0x17: case 0x1E: case 0x1F:
//Push
case 0x50: case 0x51: case 0x52: case 0x53:
case 0x54: case 0x55: case 0x56: case 0x57:
//Pop
case 0x58: case 0x59: case 0x5A: case 0x5B:
case 0x5C: case 0x5D: case 0x5E: case 0x5F:
//Push/pop all
case 0x60: case 0x61:
//Push immediate
case 0x68: case 0x6A:
//Push/pop flags
case 0x9C: case 0x9D:
return true;
}
return false;
}
CdlFlags::CdlFlags WsDisUtils::GetOpFlags(uint16_t opCode, uint32_t pc, uint32_t prevPc, uint8_t opSize)
{
if(WsDisUtils::IsJumpToSub(opCode)) {
return CdlFlags::SubEntryPoint;
} else if(WsDisUtils::IsUnconditionalJump(opCode) && !WsDisUtils::IsReturnInstruction(opCode)) {
return CdlFlags::JumpTarget;
} else if(WsDisUtils::IsConditionalJump(opCode) && (pc != prevPc + opSize)) {
return CdlFlags::JumpTarget;
}
return CdlFlags::None;
}
void WsDisUtils::UpdateAddressCsIp(uint32_t addr, WsCpuState& state)
{
uint32_t baseAddr = (state.CS << 4);
if(addr >= baseAddr && addr <= baseAddr + 0xFFFF) {
//address fits within the current code segment, only change the instruction pointer
state.IP = addr - baseAddr;
} else {
//address is outside the range of the current CS, change both values
state.CS = (addr & 0xF0000) >> 4;
state.IP = (addr & 0xFFFF);
}
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include "pch.h"
#include "Debugger/DebugTypes.h"
class WsConsole;
class WsMemoryManager;
class LabelManager;
class EmuSettings;
class MemoryDumper;
class FastString;
struct WsCpuState;
enum class WsSegment : uint8_t;
class WsDisUtils
{
private:
static void GetModRegParam(FastString& str, uint8_t* byteCode, bool word);
static void GetModSegRegParam(FastString& str, uint8_t* byteCode);
static int GetModRmParam(FastString& str, uint8_t* byteCode, WsSegment segment, bool word, bool forLeaLdsLes = false);
static int GetModRmSize(uint8_t modRm);
static bool IsPrefix(uint8_t opCode);
static void GetJmpDestination(FastString& str, uint8_t* byteCode, uint8_t size);
static void GetSegment(FastString& str, WsSegment segment, const char* defaultSegment);
public:
static void GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings);
static EffectiveAddressInfo GetEffectiveAddress(DisassemblyInfo& info, WsConsole* console, WsCpuState& state);
static uint8_t GetOpSize(uint32_t cpuAddress, MemoryType memType, MemoryDumper* memoryDumper);
static uint16_t GetFullOpCode(uint16_t cs, uint16_t ip, WsMemoryManager* memoryManager);
static uint16_t GetFullOpCode(DisassemblyInfo& disInfo);
static bool IsJumpToSub(uint16_t opCode);
static bool IsReturnInstruction(uint16_t opCode);
static bool IsUnconditionalJump(uint16_t opCode);
static bool IsConditionalJump(uint16_t opCode);
static bool IsUndefinedOpCode(uint16_t opCode);
static bool IsPushPopInstruction(uint16_t opCode);
static CdlFlags::CdlFlags GetOpFlags(uint16_t opCode, uint32_t pc, uint32_t prevPc, uint8_t opSize);
static void UpdateAddressCsIp(uint32_t addr, WsCpuState& state);
};
+196
View File
@@ -0,0 +1,196 @@
#include "pch.h"
#include "WS/Debugger/WsEventManager.h"
#include "WS/WsCpu.h"
#include "WS/WsPpu.h"
#include "WS/WsConsole.h"
#include "Shared/ColorUtilities.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugBreakHelper.h"
#include "Debugger/BaseEventManager.h"
WsEventManager::WsEventManager(Debugger* debugger, WsConsole* console, WsCpu* cpu, WsPpu* ppu)
{
_debugger = debugger;
_console = console;
_cpu = cpu;
_ppu = ppu;
_ppuBuffer = new uint16_t[WsConstants::MaxPixelCount];
memset(_ppuBuffer, 0, WsConstants::MaxPixelCount * sizeof(uint16_t));
}
WsEventManager::~WsEventManager()
{
delete[] _ppuBuffer;
}
void WsEventManager::AddEvent(DebugEventType type, MemoryOperationInfo& operation, int32_t breakpointId)
{
DebugEventInfo evt = {};
evt.Type = type;
evt.Flags = (uint32_t)EventFlags::ReadWriteOp;
evt.Operation = operation;
evt.Scanline = _ppu->GetScanline();
evt.Cycle = _ppu->GetCycle();
evt.BreakpointId = breakpointId;
evt.DmaChannel = -1;
evt.ProgramCounter = _debugger->GetProgramCounter(CpuType::Ws, true);
_debugEvents.push_back(evt);
}
void WsEventManager::AddEvent(DebugEventType type)
{
DebugEventInfo evt = {};
evt.Type = type;
evt.Scanline = _ppu->GetScanline();
evt.Cycle = _ppu->GetCycle();
evt.BreakpointId = -1;
evt.DmaChannel = -1;
evt.ProgramCounter = _cpu->GetProgramCounter();
_debugEvents.push_back(evt);
}
DebugEventInfo WsEventManager::GetEvent(uint16_t y, uint16_t x)
{
auto lock = _lock.AcquireSafe();
x /= 2; //convert to cycle value
y /= 2; //convert to scanline value
//Search without including larger background color first
for(DebugEventInfo& evt : _sentEvents) {
if(evt.Cycle == x && evt.Scanline == y) {
return evt;
}
}
//If no exact match, extend to the background color
for(int i = (int)_sentEvents.size() - 1; i >= 0; i--){
DebugEventInfo& evt = _sentEvents[i];
if(std::abs((int)evt.Cycle - (int)x) <= 1 && std::abs((int)evt.Scanline - (int)y) <= 1) {
return evt;
}
}
DebugEventInfo empty = {};
empty.ProgramCounter = 0xFFFFFFFF;
return empty;
}
bool WsEventManager::ShowPreviousFrameEvents()
{
return _config.ShowPreviousFrameEvents;
}
void WsEventManager::SetConfiguration(BaseEventViewerConfig& config)
{
_config = (WsEventViewerConfig&)config;
}
EventViewerCategoryCfg WsEventManager::GetEventConfig(DebugEventInfo& evt)
{
switch(evt.Type) {
default: return {};
case DebugEventType::Breakpoint: return _config.MarkedBreakpoints;
case DebugEventType::Irq: return _config.Irq;
case DebugEventType::Register:
bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type == MemoryOperationType::DmaWrite;
uint32_t port = evt.Operation.Address;
if(evt.Operation.MemType == MemoryType::WsMemory) {
if(evt.Operation.Address >= 0xFE00) {
return isWrite ? _config.PpuPaletteWrite : _config.PpuPaletteRead;
} else {
return isWrite ? _config.PpuVramWrite : _config.PpuVramRead;
}
} else {
if(port <= 0x3F) {
if(port >= 0x08 && port <= 0x0F) {
return isWrite ? _config.PpuWindowWrite : _config.PpuWindowRead;
} else if(port >= 0x10 && port <= 0x13) {
return isWrite ? _config.PpuScrollWrite : _config.PpuScrollRead;
} else if(port == 0x02 && !isWrite) {
return _config.PpuVCounterRead;
} else if(port >= 0x1C && port <= 0x3F) {
return isWrite ? _config.PpuPaletteWrite : _config.PpuPaletteRead;
} else {
return isWrite ? _config.PpuOtherWrite : _config.PpuOtherRead;
}
} else if(port >= 0x40 && port <= 0x53) {
return isWrite ? _config.DmaWrite : _config.DmaRead;
} else if((port >= 0x80 && port <= 0x9F) || (port >= 0x64 && port <= 0x6B)) {
return isWrite ? _config.AudioWrite : _config.AudioRead;
} else if(port >= 0xA2 && port <= 0xAB) {
return isWrite ? _config.TimerWrite : _config.TimerRead;
} else if(port == 0xB0 || port == 0xB2 || port == 0xB4 || port == 0xB6 || port == 0xB7) {
return isWrite ? _config.IrqWrite : _config.IrqRead;
} else if(port == 0xB1 || port == 0xB3) {
return isWrite ? _config.SerialWrite : _config.SerialRead;
} else if(port == 0xB5) {
return isWrite ? _config.InputWrite : _config.InputRead;
} else if(port >= 0xBA && port <= 0xBF) {
return isWrite ? _config.EepromWrite : _config.EepromRead;
} else if(port >= 0xC0) {
return isWrite ? _config.CartWrite : _config.CartRead;
} else if(port == 0x60 || port == 0x62 || port == 0xA0 || (port >= 0x70 && port <= 0x77)) {
//system control 1/2/3, tft control, etc.
return isWrite ? _config.OtherWrite : _config.OtherRead;
}
}
return {};
}
}
void WsEventManager::ConvertScanlineCycleToRowColumn(int32_t& x, int32_t& y)
{
y *= 2;
x *= 2;
}
uint32_t WsEventManager::TakeEventSnapshot(bool forAutoRefresh)
{
DebugBreakHelper breakHelper(_debugger);
auto lock = _lock.AcquireSafe();
uint16_t cycle = _ppu->GetCycle();
uint16_t scanline = _ppu->GetScanline();
if(scanline > _ppu->GetVisibleScanlineCount()) {
memcpy(_ppuBuffer, _ppu->GetScreenBuffer(false), WsConstants::MaxPixelCount * sizeof(uint16_t));
} else {
uint32_t offset = _ppu->GetScreenWidth() * (std::max<int>(1, scanline) - 1);
memcpy(_ppuBuffer, _ppu->GetScreenBuffer(false), offset * sizeof(uint16_t));
memcpy(_ppuBuffer + offset, _ppu->GetScreenBuffer(true) + offset, (WsConstants::MaxPixelCount - offset) * sizeof(uint16_t));
}
_snapshotCurrentFrame = _debugEvents;
_snapshotPrevFrame = _prevDebugEvents;
_snapshotScanline = scanline;
_snapshotCycle = cycle;
_forAutoRefresh = forAutoRefresh;
_scanlineCount = _ppu->GetScanlineCount();
return _scanlineCount;
}
FrameInfo WsEventManager::GetDisplayBufferSize()
{
FrameInfo size;
size.Width = WsEventManager::ScanlineWidth;
size.Height = _scanlineCount * 2;
return size;
}
void WsEventManager::DrawScreen(uint32_t* buffer)
{
uint16_t *src = _ppuBuffer;
uint16_t screenWidth = _ppu->GetScreenWidth();
for(uint32_t y = 0, len = WsConstants::ScreenHeight * 2; y < len; y++) {
for(uint32_t x = 0; x < WsConstants::ScreenWidth * 2; x++) {
int srcOffset = (y >> 1) * screenWidth + (x >> 1);
buffer[(y + 2)*WsEventManager::ScanlineWidth + x] = ColorUtilities::Bgr444ToArgb(src[srcOffset]);
}
}
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/BaseEventManager.h"
#include "Utilities/SimpleLock.h"
enum class DebugEventType;
struct DebugEventInfo;
class WsCpu;
class WsPpu;
class WsConsole;
class Debugger;
struct WsEventViewerConfig : public BaseEventViewerConfig
{
EventViewerCategoryCfg PpuPaletteRead;
EventViewerCategoryCfg PpuPaletteWrite;
EventViewerCategoryCfg PpuVramRead;
EventViewerCategoryCfg PpuVramWrite;
EventViewerCategoryCfg PpuVCounterRead;
EventViewerCategoryCfg PpuScrollRead;
EventViewerCategoryCfg PpuScrollWrite;
EventViewerCategoryCfg PpuWindowRead;
EventViewerCategoryCfg PpuWindowWrite;
EventViewerCategoryCfg PpuOtherRead;
EventViewerCategoryCfg PpuOtherWrite;
EventViewerCategoryCfg AudioRead;
EventViewerCategoryCfg AudioWrite;
EventViewerCategoryCfg SerialRead;
EventViewerCategoryCfg SerialWrite;
EventViewerCategoryCfg DmaRead;
EventViewerCategoryCfg DmaWrite;
EventViewerCategoryCfg InputRead;
EventViewerCategoryCfg InputWrite;
EventViewerCategoryCfg IrqRead;
EventViewerCategoryCfg IrqWrite;
EventViewerCategoryCfg TimerRead;
EventViewerCategoryCfg TimerWrite;
EventViewerCategoryCfg EepromRead;
EventViewerCategoryCfg EepromWrite;
EventViewerCategoryCfg CartRead;
EventViewerCategoryCfg CartWrite;
EventViewerCategoryCfg OtherRead;
EventViewerCategoryCfg OtherWrite;
EventViewerCategoryCfg Irq;
EventViewerCategoryCfg MarkedBreakpoints;
bool ShowPreviousFrameEvents;
};
class WsEventManager final : public BaseEventManager
{
private:
static constexpr int ScanlineWidth = WsConstants::ClocksPerScanline*2;
static constexpr int ScreenHeight = WsConstants::ScanlineCount;
WsEventViewerConfig _config;
WsPpu* _ppu;
WsCpu* _cpu;
WsConsole* _console;
Debugger* _debugger;
uint32_t _scanlineCount = 159;
uint16_t* _ppuBuffer = nullptr;
protected:
bool ShowPreviousFrameEvents() override;
void ConvertScanlineCycleToRowColumn(int32_t& x, int32_t& y) override;
void DrawScreen(uint32_t* buffer) override;
public:
WsEventManager(Debugger* debugger, WsConsole* console, WsCpu* cpu, WsPpu* ppu);
~WsEventManager();
void AddEvent(DebugEventType type, MemoryOperationInfo& operation, int32_t breakpointId = -1) override;
void AddEvent(DebugEventType type) override;
EventViewerCategoryCfg GetEventConfig(DebugEventInfo& evt) override;
uint32_t TakeEventSnapshot(bool forAutoRefresh) override;
DebugEventInfo GetEvent(uint16_t y, uint16_t x) override;
FrameInfo GetDisplayBufferSize() override;
void SetConfiguration(BaseEventViewerConfig& config) override;
};
+393
View File
@@ -0,0 +1,393 @@
#include "pch.h"
#include "WS/Debugger/WsPpuTools.h"
#include "WS/WsTypes.h"
#include "WS/WsConsole.h"
#include "WS/WsPpu.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/MemoryDumper.h"
#include "Shared/SettingTypes.h"
WsPpuTools::WsPpuTools(Debugger* debugger, Emulator *emu, WsConsole* console) : PpuTools(debugger, emu)
{
_console = console;
}
FrameInfo WsPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& baseState)
{
return { 256, 256 };
}
DebugTilemapInfo WsPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer)
{
WsPpuState& state = (WsPpuState&)baseState;
uint16_t ramMask = state.Mode == WsVideoMode::Monochrome ? 0x3FFF : 0xFFFF;
uint16_t baseAddr = state.BgLayers[options.Layer].MapAddress;
std::fill(outBuffer, outBuffer + 256 * 256, 0xFFFFFFFF);
int tileSize = state.Mode >= WsVideoMode::Color4bpp ? 32 : 16;
int bpp = state.Mode >= WsVideoMode::Color4bpp ? 4 : 2;
int paletteSize = state.Mode == WsVideoMode::Monochrome ? 4 : 16;
int bank0Addr = state.Mode >= WsVideoMode::Color4bpp ? 0x4000 : 0x2000;
int bank1Addr = state.Mode >= WsVideoMode::Color4bpp ? 0x8000 : 0x4000;
uint8_t colorMask = 0xFF;
if(options.DisplayMode == TilemapDisplayMode::Grayscale) {
palette = (uint32_t*)_grayscaleColorsBpp2;
colorMask = 0x03;
}
for(int row = 0; row < 32; row++) {
uint16_t baseOffset = baseAddr + row * 64;
for(int column = 0; column < 32; column++) {
uint16_t tilemapAddr = (baseOffset + column * 2);
uint16_t tilemapData = vram[tilemapAddr] | (vram[tilemapAddr + 1] << 8);
uint16_t tileIndex = tilemapData & 0x1FF;
uint8_t tilePalette = (tilemapData >> 9) & 0x0F;
bool vMirror = tilemapData & 0x8000;
bool hMirror = tilemapData & 0x4000;
uint16_t tilesetAddr = state.Mode >= WsVideoMode::Color2bpp && (tilemapData & 0x2000) ? bank1Addr : bank0Addr;
uint16_t tileStart = tilesetAddr + tileIndex * tileSize;
for(int y = 0; y < 8; y++) {
uint8_t tileRow = vMirror ? 7 - (y & 0x07) : (y & 0x07);
uint16_t tileAddr = tileStart + tileRow * bpp;
for(int x = 0; x < 8; x++) {
uint8_t tileColumn = hMirror ? 7 - (x & 0x07) : (x & 0x07);
uint8_t color;
switch(state.Mode) {
default:
case WsVideoMode::Monochrome:
case WsVideoMode::Color2bpp:
color = GetTilePixelColor<TileFormat::Bpp2>(vram, ramMask, tileAddr, tileColumn);
break;
case WsVideoMode::Color4bpp:
color = GetTilePixelColor<TileFormat::SmsBpp4>(vram, ramMask, tileAddr, tileColumn);
break;
case WsVideoMode::Color4bppPacked:
color = GetTilePixelColor<TileFormat::WsBpp4Packed>(vram, ramMask, tileAddr, tileColumn);
break;
}
if(color != 0 || (!(tilePalette & 0x04) && state.Mode <= WsVideoMode::Color2bpp)) {
uint32_t outPos = (row * 8 + y) * 256 + column * 8 + x;
outBuffer[outPos] = palette[((tilePalette * paletteSize) + color) & colorMask];
}
}
}
}
}
DebugTilemapInfo result = {};
result.Bpp = bpp;
switch(state.Mode) {
case WsVideoMode::Monochrome:
case WsVideoMode::Color2bpp:
result.Format = TileFormat::Bpp2;
break;
case WsVideoMode::Color4bpp:
result.Format = TileFormat::SmsBpp4;
break;
case WsVideoMode::Color4bppPacked:
result.Format = TileFormat::WsBpp4Packed;
break;
}
result.TileWidth = 8;
result.TileHeight = 8;
result.ColumnCount = 32;
result.RowCount = 32;
result.TilemapAddress = baseAddr;
result.TilesetAddress = bank0Addr;
result.ScrollX = state.BgLayers[options.Layer].ScrollX;
result.ScrollY = state.BgLayers[options.Layer].ScrollY;
result.ScrollWidth = WsConstants::ScreenWidth;
result.ScrollHeight = WsConstants::ScreenHeight;
return result;
}
DebugTilemapTileInfo WsPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState)
{
DebugTilemapTileInfo result = {};
FrameInfo size = GetTilemapSize(options, baseState);
if(x >= size.Width || y >= size.Height) {
return result;
}
WsPpuState& state = (WsPpuState&)baseState;
int tileSize = state.Mode >= WsVideoMode::Color4bpp ? 32 : 16;
int bank0Addr = state.Mode >= WsVideoMode::Color4bpp ? 0x4000 : 0x2000;
int bank1Addr = state.Mode >= WsVideoMode::Color4bpp ? 0x8000 : 0x4000;
int row = y / 8;
int column = x / 8;
uint16_t baseAddr = state.BgLayers[options.Layer].MapAddress;
uint16_t tilemapAddr = (baseAddr + row * 64 + column * 2);
uint16_t tilemapData = vram[tilemapAddr] | (vram[tilemapAddr + 1] << 8);
uint16_t tileIndex = tilemapData & 0x1FF;
uint8_t tilePalette = (tilemapData >> 9) & 0x0F;
bool vMirror = tilemapData & 0x8000;
bool hMirror = tilemapData & 0x4000;
uint16_t tilesetAddr = state.Mode >= WsVideoMode::Color2bpp && (tilemapData & 0x2000) ? bank1Addr : bank0Addr;
result.Column = column;
result.Row = row;
result.Height = 8;
result.Width = 8;
result.TileMapAddress = tilemapAddr;
result.TileIndex = tileIndex;
result.TileAddress = tilesetAddr + tileIndex * tileSize;
result.PaletteIndex = tilePalette;
result.PaletteAddress = state.Mode >= WsVideoMode::Color2bpp ? (0xFE00 | (result.PaletteIndex << 4)) : (result.PaletteIndex << 2);
result.HorizontalMirroring = (NullableBoolean)hMirror;
result.VerticalMirroring = (NullableBoolean)vMirror;
return result;
}
void WsPpuTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& baseState, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t* outBuffer)
{
uint32_t bgColor = GetSpriteBackgroundColor(options.Background, palette, false);
uint32_t darkBg = GetSpriteBackgroundColor(options.Background, palette, true);
std::fill(outBuffer, outBuffer + 256 * WsConstants::ScreenHeight, bgColor);
std::fill(outBuffer + 256 * WsConstants::ScreenHeight, outBuffer + 256 * 256, darkBg);
for(int i = 0; i < WsConstants::ScreenHeight; i++) {
std::fill(outBuffer + WsConstants::ScreenWidth + i * 256, outBuffer + 256 + i * 256 , darkBg);
}
int spriteCount = 128;
for(int i = spriteCount - 1; i >= 0; i--) {
DebugSpriteInfo& sprite = sprites[i];
uint32_t* spritePreview = spritePreviews + i * _spritePreviewSize;
int spritePosY = sprite.Y;
for(int y = 0; y < sprite.Height; y++) {
for(int x = 0; x < sprite.Width; x++) {
if(spritePosY + y >= 256) {
spritePosY -= 256;
}
uint32_t color = spritePreview[y * sprite.Width + x];
if(color != 0) {
if(sprite.X + x >= 256 || sprite.Visibility == SpriteVisibility::Disabled) {
continue;
}
outBuffer[((spritePosY + y) * 256) + sprite.X + x] = color;
} else {
spritePreview[y * sprite.Width + x] = bgColor;
}
}
}
}
}
DebugSpritePreviewInfo WsPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState)
{
DebugSpritePreviewInfo info = {};
info.Height = 256;
info.Width = 256;
info.SpriteCount = 128;
info.CoordOffsetX = 0;
info.CoordOffsetY = 0;
info.WrapBottomToTop = true;
info.WrapRightToLeft = true;
info.VisibleX = 0;
info.VisibleY = 0;
info.VisibleWidth = WsConstants::ScreenWidth;
info.VisibleHeight = WsConstants::ScreenHeight;
return info;
}
void WsPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, uint16_t i, GetSpritePreviewOptions& options, WsPpuState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette)
{
uint16_t ramMask = state.Mode == WsVideoMode::Monochrome ? 0x3FFF : 0xFFFF;
uint8_t* oam = oamRam ? oamRam : (vram + state.SpriteTableAddress);
int tileSize = state.Mode >= WsVideoMode::Color4bpp ? 32 : 16;
int bpp = state.Mode >= WsVideoMode::Color4bpp ? 4 : 2;
int bank0Addr = state.Mode >= WsVideoMode::Color4bpp ? 0x4000 : 0x2000;
sprite.Bpp = bpp;
switch(state.Mode) {
case WsVideoMode::Monochrome:
case WsVideoMode::Color2bpp:
sprite.Format = TileFormat::Bpp2;
break;
case WsVideoMode::Color4bpp:
sprite.Format = TileFormat::SmsBpp4;
break;
case WsVideoMode::Color4bppPacked:
sprite.Format = TileFormat::WsBpp4Packed;
break;
}
sprite.SpriteIndex = i;
sprite.UseExtendedVram = false;
sprite.RawY = oam[i*4 + 2];
sprite.RawX = oam[i*4 + 3];
sprite.Y = sprite.RawY;
sprite.X = sprite.RawX;
sprite.UseSecondTable = NullableBoolean::Undefined;
uint8_t attributes = oam[i*4 + 1];
bool highPriority = attributes & 0x20;
bool vMirror = attributes & 0x80;
bool hMirror = attributes & 0x40;
uint16_t tileIndex = oam[i*4] | ((attributes & 0x01) << 8);
sprite.TileIndex = tileIndex;
uint8_t sprPalette = ((attributes >> 1) & 0x07);
int paletteSize = state.Mode == WsVideoMode::Monochrome ? 4 : 16;
sprite.Palette = sprPalette;
sprite.PaletteAddress = (8 + sprPalette) * paletteSize;
sprite.Priority = highPriority ? DebugSpritePriority::Foreground : DebugSpritePriority::Background;
sprite.Width = 8;
sprite.Height = 8;
if(i < state.FirstSpriteIndex || i >= state.FirstSpriteIndex + state.SpriteCount) {
sprite.Visibility = SpriteVisibility::Disabled;
} else if(sprite.Y < WsConstants::ScreenHeight && sprite.X < WsConstants::ScreenWidth) {
sprite.Visibility = SpriteVisibility::Visible;
} else {
sprite.Visibility = SpriteVisibility::Offscreen;
}
uint16_t tilesetAddr = bank0Addr;
uint16_t tileStart = tilesetAddr + (sprite.TileIndex * tileSize);
sprite.TileAddresses[0] = tileStart;
sprite.TileCount = 1;
sprite.TileAddress = tileStart;
for(int y = 0; y < 8; y++) {
uint8_t yPos = vMirror ? 7 - y : y;
uint16_t pixelStart = tileStart + yPos * bpp;
for(int x = 0; x < 8; x++) {
uint8_t color;
uint8_t xPos = hMirror ? 7 - x : x;
switch(state.Mode) {
default:
case WsVideoMode::Monochrome:
case WsVideoMode::Color2bpp:
color = GetTilePixelColor<TileFormat::Bpp2>(vram, ramMask, pixelStart, xPos);
break;
case WsVideoMode::Color4bpp:
color = GetTilePixelColor<TileFormat::SmsBpp4>(vram, ramMask, pixelStart, xPos);
break;
case WsVideoMode::Color4bppPacked:
color = GetTilePixelColor<TileFormat::WsBpp4Packed>(vram, ramMask, pixelStart, xPos);
break;
}
uint32_t outOffset = (y * 8) + x;
if(color > 0 || (!(sprPalette & 0x04) && state.Mode <= WsVideoMode::Color2bpp)) {
spritePreview[outOffset] = palette[sprite.PaletteAddress + color];
} else {
spritePreview[outOffset] = 0;
}
}
}
}
void WsPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview)
{
WsPpuState& state = (WsPpuState&)baseState;
int spriteCount = 128;
for(int i = 0; i < spriteCount; i++) {
outBuffer[i].Init();
GetSpriteInfo(outBuffer[i], spritePreviews + (i * _spritePreviewSize), i, options, state, vram, oamRam, palette);
}
GetSpritePreview(options, baseState, outBuffer, spritePreviews, palette, screenPreview);
}
DebugPaletteInfo WsPpuTools::GetPaletteInfo(GetPaletteInfoOptions options)
{
DebugPaletteInfo info = {};
WsPpuState state;
_debugger->GetPpuState(state, CpuType::Ws);
if(state.Mode == WsVideoMode::Monochrome) {
info.ColorCount = 64;
info.BgColorCount = 32;
info.SpriteColorCount = 32;
info.ColorsPerPalette = 4;
info.SpritePaletteOffset = info.BgColorCount;
info.RawFormat = RawPaletteFormat::Indexed;
for(int i = 0; i < 64; i++) {
info.RawPalette[i] = state.BwPalettes[i];
uint8_t brightness = state.BwShades[state.BwPalettes[i]] ^ 0x0F;
info.RgbPalette[i] = ColorUtilities::Rgb444ToArgb(brightness | (brightness << 4) | (brightness << 8));
}
} else {
info.ColorCount = 256;
info.BgColorCount = 128;
info.SpriteColorCount = 128;
info.ColorsPerPalette = 16;
info.SpritePaletteOffset = info.BgColorCount;
info.HasMemType = true;
info.PaletteMemType = MemoryType::WsWorkRam;
info.PaletteMemOffset = 0xFE00;
uint8_t* palette = _debugger->GetMemoryDumper()->GetMemoryBuffer(MemoryType::WsWorkRam);
info.RawFormat = RawPaletteFormat::Rgb444;
for(int i = 0; i < 256; i++) {
int addr = 0xFE00 + i * 2;
info.RawPalette[i] = palette[addr] | ((palette[addr + 1] & 0x0F) << 8);
info.RgbPalette[i] = ColorUtilities::Bgr444ToArgb(info.RawPalette[i]);
}
}
return info;
}
void WsPpuTools::SetPaletteColor(int32_t colorIndex, uint32_t color)
{
WsPpuState state;
_debugger->GetPpuState(state, CpuType::Ws);
if(state.Mode == WsVideoMode::Monochrome) {
_console->GetPpu()->GetState().BwPalettes[colorIndex] = color;
} else {
uint8_t r = (color >> 20) & 0x0F;
uint8_t g = (color >> 12) & 0x0F;
uint8_t b = (color >> 4) & 0x0F;
uint16_t rgb444 = (r << 8) | (g << 4) | b;
_debugger->GetMemoryDumper()->SetMemoryValue(MemoryType::WsWorkRam, 0xFE00 + colorIndex * 2, rgb444 & 0xFF);
_debugger->GetMemoryDumper()->SetMemoryValue(MemoryType::WsWorkRam, 0xFE00 + colorIndex * 2 + 1, (rgb444 >> 8));
}
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "pch.h"
#include "Debugger/PpuTools.h"
class Debugger;
class Emulator;
class WsConsole;
struct WsPpuState;
class WsPpuTools final : public PpuTools
{
private:
WsConsole* _console = nullptr;
void GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, uint16_t spriteIndex, GetSpritePreviewOptions& options, WsPpuState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette);
void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t *outBuffer);
public:
WsPpuTools(Debugger* debugger, Emulator *emu, WsConsole* console);
DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override;
FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override;
DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override;
DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override;
void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override;
DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override;
void SetPaletteColor(int32_t colorIndex, uint32_t color) override;
};
+104
View File
@@ -0,0 +1,104 @@
#include "pch.h"
#include "WS/Debugger/WsTraceLogger.h"
#include "WS/WsPpu.h"
#include "WS/WsTypes.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/FastString.h"
WsTraceLogger::WsTraceLogger(Debugger* debugger, IDebugger* cpuDebugger, WsPpu* ppu) : BaseTraceLogger(debugger, cpuDebugger, CpuType::Ws)
{
_ppu = ppu;
}
RowDataType WsTraceLogger::GetFormatTagType(string& tag)
{
if(tag == "AX") {
return RowDataType::AX;
} else if(tag == "BX") {
return RowDataType::BX;
} else if(tag == "CX") {
return RowDataType::CX;
} else if(tag == "DX") {
return RowDataType::DX;
} else if(tag == "CS") {
return RowDataType::CS;
} else if(tag == "IP") {
return RowDataType::IP;
} else if(tag == "SS") {
return RowDataType::SS;
} else if(tag == "SP") {
return RowDataType::SP;
} else if(tag == "BP") {
return RowDataType::BP;
} else if(tag == "DS") {
return RowDataType::DS;
} else if(tag == "ES") {
return RowDataType::ES;
} else if(tag == "SI") {
return RowDataType::SI;
} else if(tag == "DI") {
return RowDataType::DI;
} else if(tag == "F") {
return RowDataType::F;
} else if(tag == "PC") {
return RowDataType::PC;
} else {
return RowDataType::Text;
}
}
void WsTraceLogger::GetTraceRow(string &output, WsCpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
for(RowPart& rowPart : _rowParts) {
switch(rowPart.DataType) {
case RowDataType::AX: WriteIntValue(output, cpuState.AX, rowPart); break;
case RowDataType::BX: WriteIntValue(output, cpuState.BX, rowPart); break;
case RowDataType::CX: WriteIntValue(output, cpuState.CX, rowPart); break;
case RowDataType::DX: WriteIntValue(output, cpuState.DX, rowPart); break;
case RowDataType::CS: WriteIntValue(output, cpuState.CS, rowPart); break;
case RowDataType::IP: WriteIntValue(output, cpuState.IP, rowPart); break;
case RowDataType::SS: WriteIntValue(output, cpuState.SS, rowPart); break;
case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break;
case RowDataType::BP: WriteIntValue(output, cpuState.BP, rowPart); break;
case RowDataType::DS: WriteIntValue(output, cpuState.DS, rowPart); break;
case RowDataType::ES: WriteIntValue(output, cpuState.ES, rowPart); break;
case RowDataType::SI: WriteIntValue(output, cpuState.SI, rowPart); break;
case RowDataType::DI: WriteIntValue(output, cpuState.DI, rowPart); break;
case RowDataType::F:
if(rowPart.DisplayInHex) {
WriteIntValue(output, cpuState.Flags.Get(), rowPart);
} else {
FastString flags;
bool showInactive = (rowPart.MinWidth > 0);
flags.Write(cpuState.Flags.Carry ? "C" : (showInactive ? "c" : ""));
flags.Write(cpuState.Flags.Parity ? "P" : (showInactive ? "p" : ""));
flags.Write(cpuState.Flags.AuxCarry ? "A" : (showInactive ? "a" : ""));
flags.Write(cpuState.Flags.Zero ? "Z" : (showInactive ? "z" : ""));
flags.Write(cpuState.Flags.Sign ? "S" : (showInactive ? "s" : ""));
flags.Write(cpuState.Flags.Trap ? "T" : (showInactive ? "t" : ""));
flags.Write(cpuState.Flags.Irq ? "I" : (showInactive ? "i" : ""));
flags.Write(cpuState.Flags.Direction ? "D" : (showInactive ? "d" : ""));
flags.Write(cpuState.Flags.Overflow ? "O" : (showInactive ? "o" : ""));
flags.Write(cpuState.Flags.Mode ? "M" : (showInactive ? "m" : ""));
WriteStringValue(output, flags.ToString(), rowPart);
}
break;
default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break;
}
}
}
void WsTraceLogger::LogPpuState()
{
_ppuState[_currentPos] = {
_ppu->GetCycle(),
_ppu->GetCycle(),
_ppu->GetScanline(),
_ppu->GetFrameCount()
};
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "pch.h"
#include "Debugger/BaseTraceLogger.h"
#include "WS/WsTypes.h"
class DisassemblyInfo;
class Debugger;
class WsPpu;
class WsTraceLogger : public BaseTraceLogger<WsTraceLogger, WsCpuState>
{
private:
WsPpu* _ppu = nullptr;
protected:
RowDataType GetFormatTagType(string& tag) override;
public:
WsTraceLogger(Debugger* debugger, IDebugger* cpuDebugger, WsPpu* ppu);
void GetTraceRow(string& output, WsCpuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo);
void LogPpuState();
__forceinline uint32_t GetProgramCounter(WsCpuState& state) { return (state.CS << 4) + state.IP; }
__forceinline uint64_t GetCycleCount(WsCpuState& state) { return state.CycleCount; }
__forceinline uint8_t GetStackPointer(WsCpuState& state) { return (uint8_t)state.SP; }
};
+472
View File
@@ -0,0 +1,472 @@
#include "pch.h"
#include "WS/WsConsole.h"
#include "WS/WsCpu.h"
#include "WS/WsPpu.h"
#include "WS/WsTimer.h"
#include "WS/Carts/WsCart.h"
#include "WS/WsControlManager.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsDmaController.h"
#include "WS/WsEeprom.h"
#include "WS/WsSerial.h"
#include "WS/WsDefaultVideoFilter.h"
#include "WS/APU/WsApu.h"
#include "Shared/CpuType.h"
#include "Shared/Emulator.h"
#include "Shared/SettingTypes.h"
#include "Shared/FirmwareHelper.h"
#include "Shared/BatteryManager.h"
WsConsole::WsConsole(Emulator* emu)
{
_emu = emu;
}
WsConsole::~WsConsole()
{
delete[] _workRam;
delete[] _saveRam;
delete[] _prgRom;
delete[] _bootRom;
delete[] _internalEepromData;
delete[] _cartEepromData;
}
LoadRomResult WsConsole::LoadRom(VirtualFile& romFile)
{
vector<uint8_t> romData;
romFile.ReadFile(romData);
if(romData.size() < 0x10000) {
return LoadRomResult::Failure;
}
MessageManager::Log("------------------------------");
_prgRomSize = (uint32_t)romData.size();
_prgRom = new uint8_t[_prgRomSize];
memcpy(_prgRom, romData.data(), _prgRomSize);
_emu->RegisterMemory(MemoryType::WsPrgRom, _prgRom, _prgRomSize);
uint8_t sramType = _prgRom[_prgRomSize - 5];
switch(sramType) {
case 0x00: _saveRamSize = 0; break;
case 0x01: _saveRamSize = 32 * 1024; break;
case 0x02: _saveRamSize = 32 * 1024; break;
case 0x03: _saveRamSize = 128 * 1024; break;
case 0x04: _saveRamSize = 256 * 1024; break;
case 0x05: _saveRamSize = 512 * 1024; break;
case 0x10: _cartEepromSize = 0x80; break;
case 0x20: _cartEepromSize = 0x800; break;
case 0x50: _cartEepromSize = 0x400; break;
default: MessageManager::Log("Save RAM: unrecognized value"); break;
}
bool hasColorSupport = (_prgRom[_prgRomSize - 9] & 0x01) || romFile.GetFileExtension() == ".wsc";
uint8_t mapperType = _prgRom[_prgRomSize - 3];
MessageManager::Log(string("Color supported: ") + (hasColorSupport ? "Yes" : "No"));
MessageManager::Log("Save RAM size: " + std::to_string(_saveRamSize / 1024) + " KB");
MessageManager::Log("Cart EEPROM size: " + std::to_string(_cartEepromSize) + " bytes");
MessageManager::Log(string("Mapper: ") + (mapperType == 0 ? "Bandai 2001 / KARNAK" : (mapperType == 1 ? "Bandai 2003" : ("Unknown: " + std::to_string(mapperType)))));
MessageManager::Log("------------------------------");
if(_saveRamSize > 0) {
_saveRam = new uint8_t[_saveRamSize];
memset(_saveRam, 0, _saveRamSize);
_emu->RegisterMemory(MemoryType::WsCartRam, _saveRam, _saveRamSize);
}
if(_cartEepromSize > 0) {
_cartEepromData = new uint8_t[_cartEepromSize];
memset(_cartEepromData, 0, _cartEepromSize);
_emu->RegisterMemory(MemoryType::WsCartEeprom, _cartEepromData, _cartEepromSize);
_cartEeprom.reset(new WsEeprom(_emu, this, (WsEepromSize)_cartEepromSize, _cartEepromData, false));
}
_model = _emu->GetSettings()->GetWsConfig().Model;
if(_model == WsModel::Auto) {
_model = hasColorSupport ? WsModel::Color : WsModel::Monochrome;
}
_workRamSize = _model == WsModel::Monochrome ? 0x4000 : 0x10000;
_workRam = new uint8_t[_workRamSize];
memset(_workRam, 0, _workRamSize);
_emu->RegisterMemory(MemoryType::WsWorkRam, _workRam, _workRamSize);
vector<uint8_t> bootRom;
if(_emu->GetSettings()->GetWsConfig().UseBootRom && FirmwareHelper::LoadWsBootRom(_emu, bootRom, _model)) {
_bootRomSize = (uint32_t)bootRom.size();
_bootRom = new uint8_t[_bootRomSize];
memcpy(_bootRom, bootRom.data(), _bootRomSize);
_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);
_internalEeprom.reset(new WsEeprom(_emu, this, (WsEepromSize)_internalEepromSize, _internalEepromData, true));
_controlManager.reset(new WsControlManager(_emu, this));
_memoryManager.reset(new WsMemoryManager());
_cpu.reset(new WsCpu(_emu, _memoryManager.get()));
_timer.reset(new WsTimer());
_serial.reset(new WsSerial(this));
_dmaController.reset(new WsDmaController());
_ppu.reset(new WsPpu(_emu, this, _timer.get(), _workRam));
_apu.reset(new WsApu(_emu, this, _memoryManager.get(), _dmaController.get()));
_cart.reset(new WsCart());
_cart->Init(_memoryManager.get(), _cartEeprom.get());
_memoryManager->Init(_emu, this, _cpu.get(), _ppu.get(), _controlManager.get(), _cart.get(), _timer.get(), _dmaController.get(), _internalEeprom.get(), _apu.get(), _serial.get());
_timer->Init(_memoryManager.get());
_dmaController->Init(_memoryManager.get(), _apu.get());
if(!_bootRom) {
InitPostBootRomState();
}
LoadBattery();
return LoadRomResult::Success;
}
void WsConsole::RunFrame()
{
uint32_t frameCount = _ppu->GetFrameCount();
while(frameCount == _ppu->GetFrameCount()) {
_cpu->Exec();
}
_apu->PlayQueuedAudio();
_verticalMode |= _ppu->GetState().Icons.Vertical;
if(_ppu->GetState().Icons.Horizontal) {
_verticalMode = false;
}
}
void WsConsole::GetScreenRotationOverride(uint32_t& rotation)
{
if(_emu->GetSettings()->GetWsConfig().AutoRotate) {
if(_verticalMode) {
rotation = 270;
} else {
rotation = 0;
}
}
}
bool WsConsole::IsColorMode()
{
return _memoryManager->GetState().ColorEnabled;
}
bool WsConsole::IsVerticalMode()
{
return _verticalMode;
}
WsModel WsConsole::GetModel()
{
return _model;
}
void WsConsole::ProcessEndOfFrame()
{
_controlManager->UpdateControlDevices();
_controlManager->UpdateInputState();
}
void WsConsole::Reset()
{
//The WS has no reset button, behave like power cycle
_emu->ReloadRom(true);
}
void WsConsole::InitPostBootRomState()
{
//Init work ram
if(_model == WsModel::Monochrome) {
memset(_workRam, 0, _workRamSize);
} else {
memset(_workRam, 0, 0xFE00);
memset(_workRam + 0xFE00, 0xFF, 0x200);
}
//Init port state
WsCpuState& cpu = _cpu->GetState();
cpu.DS = 0xFE00;
cpu.CS = 0xFFFF;
cpu.SP = 0x2000;
if(_model == WsModel::Monochrome) {
cpu.AX = 0xFF85;
cpu.BX = 0x0040;
cpu.DX = 0x0005;
cpu.SI = 0x023D;
cpu.DI = 0x040D;
cpu.Flags.Sign = true;
} else {
cpu.AX = 0xFF87;
cpu.BX = 0x0043;
cpu.DX = 0x0001;
cpu.SI = 0x0435;
cpu.DI = 0x040B;
cpu.Flags.Sign = true;
cpu.Flags.Parity = true;
}
WsPpuState& ppu = _ppu->GetState();
ppu.LcdEnabled = true;
if(_model == WsModel::Monochrome) {
ppu.Scanline = 26;
ppu.Cycle = 53;
constexpr static uint8_t defaultBwPalette[] = {
0,0,0,7,0,0,0,5,0,0,0,4,0,0,0,3,
0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,
0,3,5,7,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
constexpr static uint8_t defaultShades[] = { 0,2,4,6,8,10,12,15 };
memcpy(ppu.BwPalettes, defaultBwPalette, sizeof(defaultBwPalette));
memcpy(ppu.BwShades, defaultShades, sizeof(defaultShades));
} else {
ppu.Scanline = 127;
ppu.Cycle = 232;
}
_ppu->SetOutputToBgColor();
WsMemoryManagerState& mm = _memoryManager->GetState();
mm.BootRomDisabled = true;
mm.CartWordBus = true;
if(_model != WsModel::Monochrome) {
mm.SlowSram = true;
mm.SlowPort = true;
}
WsApuState& apu = _apu->GetState();
if(_model == WsModel::Monochrome) {
apu.Ch1.Frequency = 0x7E6;
apu.Ch2.Frequency = 0x7E6;
} else {
apu.InternalMasterVolume = _internalEepromData[0x83] & 0x03;
ppu.HighContrast = (_internalEepromData[0x83] & 0x40);
}
WsEepromState& eeprom = _internalEeprom->GetState();
eeprom.ReadDone = true;
eeprom.Idle = true;
eeprom.InternalEepromWriteProtected = true;
//Copy data from rom header to internal eeprom, like the boot rom would
for(int i = 0; i < 3; i++) {
_internalEepromData[0x76 + i] = _prgRom[_prgRomSize - 0x10 + 6 + i];
bool supportsColor = _prgRom[_prgRomSize - 0x10 + 7] & 0x01;
if(_model != WsModel::Monochrome && supportsColor) {
//For games that support color & color models, copy 76-78 to 80-82
_internalEepromData[0x80 + i] = _internalEepromData[0x76 + i];
}
}
_memoryManager->RefreshMappings();
}
void WsConsole::LoadBattery()
{
_internalEeprom->LoadBattery();
if(_cartEeprom) {
_cartEeprom->LoadBattery();
}
if(_saveRam) {
_emu->GetBatteryManager()->LoadBattery(".sav", _saveRam, _saveRamSize);
}
}
void WsConsole::SaveBattery()
{
_internalEeprom->SaveBattery();
if(_cartEeprom) {
_cartEeprom->SaveBattery();
}
if(_saveRam) {
_emu->GetBatteryManager()->SaveBattery(".sav", _saveRam, _saveRamSize);
}
}
BaseControlManager* WsConsole::GetControlManager()
{
return _controlManager.get();
}
ConsoleRegion WsConsole::GetRegion()
{
return ConsoleRegion::Ntsc;
}
ConsoleType WsConsole::GetConsoleType()
{
return ConsoleType::Ws;
}
vector<CpuType> WsConsole::GetCpuTypes()
{
return { CpuType::Ws };
}
uint64_t WsConsole::GetMasterClock()
{
return _cpu->GetCycleCount();
}
uint32_t WsConsole::GetMasterClockRate()
{
return 12288000 / 4;
}
double WsConsole::GetFps()
{
return 12000.0 / _ppu->GetScanlineCount();
}
BaseVideoFilter* WsConsole::GetVideoFilter(bool getDefaultFilter)
{
if(getDefaultFilter) {
return new WsDefaultVideoFilter(_emu, this, false);
}
VideoFilterType filterType = _emu->GetSettings()->GetVideoConfig().VideoFilter;
switch(filterType) {
case VideoFilterType::NtscBisqwit:
case VideoFilterType::NtscBlargg:
return new WsDefaultVideoFilter(_emu, this, true);
default: return new WsDefaultVideoFilter(_emu, this, false);
}
}
PpuFrameInfo WsConsole::GetPpuFrame()
{
PpuFrameInfo frame = {};
frame.FirstScanline = 0;
frame.FrameCount = _ppu->GetFrameCount();
frame.Width = _ppu->GetScreenWidth();
frame.Height = _ppu->GetScreenHeight();
frame.ScanlineCount = WsConstants::ScanlineCount;
frame.CycleCount = WsConstants::ClocksPerScanline;
frame.FrameBufferSize = frame.Width * frame.Height * sizeof(uint16_t);
frame.FrameBuffer = (uint8_t*)_ppu->GetScreenBuffer(false);
return frame;
}
RomFormat WsConsole::GetRomFormat()
{
return RomFormat::Ws;
}
AudioTrackInfo WsConsole::GetAudioTrackInfo()
{
//TODOWS
return AudioTrackInfo();
}
void WsConsole::ProcessAudioPlayerAction(AudioPlayerActionParams p)
{
//TODOWS
}
AddressInfo WsConsole::GetAbsoluteAddress(uint32_t relAddress)
{
return _memoryManager->GetAbsoluteAddress(relAddress);
}
AddressInfo WsConsole::GetAbsoluteAddress(AddressInfo& relAddress)
{
return GetAbsoluteAddress(relAddress.Address);
}
AddressInfo WsConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpuType)
{
return { _memoryManager->GetRelativeAddress(absAddress), MemoryType::WsMemory };
}
WsState WsConsole::GetState()
{
WsState state = {};
state.Model = _model;
state.Cpu = _cpu->GetState();
state.Ppu = _ppu->GetState();
state.Apu = _apu->GetState();
state.MemoryManager = _memoryManager->GetState();
state.ControlManager = _controlManager->GetState();
state.DmaController = _dmaController->GetState();
state.Timer = _timer->GetState();
state.Serial = _serial->GetState();
state.InternalEeprom = _internalEeprom->GetState();
state.Cart = _cart->GetState();
if(_cartEeprom) {
state.CartEeprom = _cartEeprom->GetState();
}
return state;
}
void WsConsole::GetConsoleState(BaseState& state, ConsoleType consoleType)
{
(WsState&)state = GetState();
}
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.");
s.SetErrorFlag();
return;
}
SV(_cpu);
SV(_ppu);
SV(_apu);
//Process cart before memory manager to ensure mappings are updated properly
SV(_cart);
SV(_memoryManager);
SV(_dmaController);
SV(_controlManager);
SV(_timer);
SV(_serial);
SV(_internalEeprom);
SV(_verticalMode);
SVArray(_workRam, _workRamSize);
if(_saveRam && _saveRamSize) {
SVArray(_saveRam, _saveRamSize);
}
if(_internalEepromData && _internalEepromSize) {
SVArray(_internalEepromData, _internalEepromSize);
}
if(_cartEepromData && _cartEepromSize) {
SVArray(_cartEepromData, _cartEepromSize);
}
if(_cartEeprom) {
SV(_cartEeprom);
}
}
+104
View File
@@ -0,0 +1,104 @@
#pragma once
#include "pch.h"
#include "Shared/Interfaces/IConsole.h"
#include "WS/WsTypes.h"
class Emulator;
class WsCpu;
class WsPpu;
class WsApu;
class WsCart;
class WsTimer;
class WsSerial;
class WsMemoryManager;
class WsControlManager;
class WsDmaController;
class WsEeprom;
enum class WsModel : uint8_t;
class WsConsole final : public IConsole
{
private:
Emulator* _emu = nullptr;
unique_ptr<WsCpu> _cpu;
unique_ptr<WsPpu> _ppu;
unique_ptr<WsApu> _apu;
unique_ptr<WsCart> _cart;
unique_ptr<WsTimer> _timer;
unique_ptr<WsSerial> _serial;
unique_ptr<WsMemoryManager> _memoryManager;
unique_ptr<WsControlManager> _controlManager;
unique_ptr<WsDmaController> _dmaController;
unique_ptr<WsEeprom> _internalEeprom;
unique_ptr<WsEeprom> _cartEeprom;
uint8_t* _workRam = nullptr;
uint32_t _workRamSize = 0;
uint8_t* _saveRam = nullptr;
uint32_t _saveRamSize = 0;
uint8_t* _bootRom = nullptr;
uint32_t _bootRomSize = 0;
uint8_t* _prgRom = nullptr;
uint32_t _prgRomSize = 0;
uint8_t* _internalEepromData = nullptr;
uint32_t _internalEepromSize = 0;
uint8_t* _cartEepromData = nullptr;
uint32_t _cartEepromSize = 0;
WsModel _model = {};
bool _verticalMode = false;
void InitPostBootRomState();
public:
WsConsole(Emulator* emu);
~WsConsole();
static vector<string> GetSupportedExtensions() { return { ".ws", ".wsc" }; }
static vector<string> GetSupportedSignatures() { return { }; }
LoadRomResult LoadRom(VirtualFile& romFile) override;
void RunFrame() override;
void GetScreenRotationOverride(uint32_t& rotation) override;
bool IsColorMode();
bool IsVerticalMode();
WsModel GetModel();
void ProcessEndOfFrame();
void Reset() override;
void LoadBattery();
void SaveBattery() override;
BaseControlManager* GetControlManager() override;
ConsoleRegion GetRegion() override;
ConsoleType GetConsoleType() override;
vector<CpuType> GetCpuTypes() override;
uint64_t GetMasterClock() override;
uint32_t GetMasterClockRate() override;
double GetFps() override;
BaseVideoFilter* GetVideoFilter(bool getDefaultFilter) override;
PpuFrameInfo GetPpuFrame() override;
RomFormat GetRomFormat() override;
AudioTrackInfo GetAudioTrackInfo() override;
void ProcessAudioPlayerAction(AudioPlayerActionParams p) override;
AddressInfo GetAbsoluteAddress(uint32_t relAddr);
AddressInfo GetAbsoluteAddress(AddressInfo& relAddress) override;
AddressInfo GetRelativeAddress(AddressInfo& absAddress, CpuType cpuType) override;
WsState GetState();
void GetConsoleState(BaseState& state, ConsoleType consoleType) override;
WsCpu* GetCpu() { return _cpu.get(); }
WsPpu* GetPpu() { return _ppu.get(); }
WsApu* GetApu() { return _apu.get(); }
WsMemoryManager* GetMemoryManager() { return _memoryManager.get(); }
void Serialize(Serializer& s) override;
};
+117
View File
@@ -0,0 +1,117 @@
#include "pch.h"
#include "WS/WsConsole.h"
#include "WS/WsControlManager.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsController.h"
#include "Shared/KeyManager.h"
WsControlManager::WsControlManager(Emulator* emu, WsConsole* console) : BaseControlManager(emu, CpuType::Ws)
{
_console = console;
}
shared_ptr<BaseControlDevice> WsControlManager::CreateControllerDevice(ControllerType type, uint8_t port)
{
shared_ptr<BaseControlDevice> device;
WsConfig& cfg = _emu->GetSettings()->GetWsConfig();
switch(type) {
default:
case ControllerType::None: break;
case ControllerType::WsController:
case ControllerType::WsControllerVertical:
device.reset(new WsController(_emu, _console, port, cfg.ControllerHorizontal.Keys, cfg.ControllerVertical.Keys));
break;
}
return device;
}
void WsControlManager::UpdateControlDevices()
{
WsConfig cfg = _emu->GetSettings()->GetWsConfig();
if(_emu->GetSettings()->IsEqual(_prevConfig, cfg) && _controlDevices.size() > 0) {
//Do nothing if configuration is unchanged
return;
}
auto lock = _deviceLock.AcquireSafe();
ClearDevices();
shared_ptr<BaseControlDevice> device(CreateControllerDevice(ControllerType::WsController, 0));
if(device) {
RegisterControlDevice(device);
}
}
uint8_t WsControlManager::Read()
{
uint8_t result = 0;
for(shared_ptr<BaseControlDevice>& controller : _controlDevices) {
if(controller->GetPort() == 0 && controller->GetControllerType() == ControllerType::WsController) {
if(_state.InputSelect & 0x10) {
result |= controller->IsPressed(WsController::Up2) ? 0x01 : 0;
result |= controller->IsPressed(WsController::Right2) ? 0x02 : 0;
result |= controller->IsPressed(WsController::Down2) ? 0x04 : 0;
result |= controller->IsPressed(WsController::Left2) ? 0x08 : 0;
}
if(_state.InputSelect & 0x20) {
result |= controller->IsPressed(WsController::Up) ? 0x01 : 0;
result |= controller->IsPressed(WsController::Right) ? 0x02 : 0;
result |= controller->IsPressed(WsController::Down) ? 0x04 : 0;
result |= controller->IsPressed(WsController::Left) ? 0x08 : 0;
}
if(_state.InputSelect & 0x40) {
result |= controller->IsPressed(WsController::Start) ? 0x02 : 0;
result |= controller->IsPressed(WsController::A) ? 0x04 : 0;
result |= controller->IsPressed(WsController::B) ? 0x08 : 0;
}
}
}
return result;
}
void WsControlManager::Write(uint8_t value)
{
_state.InputSelect = value & 0x70;
}
bool WsControlManager::IsSoundPressed()
{
for(shared_ptr<BaseControlDevice>& controller : _controlDevices) {
if(controller->GetPort() == 0 && controller->GetControllerType() == ControllerType::WsController) {
return controller->IsPressed(WsController::Sound);
}
}
return false;
}
void WsControlManager::UpdateInputState()
{
BaseControlManager::UpdateInputState();
uint8_t newInput = Read();
if((_prevInput | newInput) > _prevInput) {
//Trigger IRQ whenever any extra bits are set compared to the previous input
_console->GetMemoryManager()->SetIrqSource(WsIrqSource::KeyPressed);
}
_prevInput = newInput;
bool soundButtonPressed = IsSoundPressed();
if(soundButtonPressed && !_soundButtonPressed) {
_console->GetApu()->ChangeMasterVolume();
}
_soundButtonPressed = soundButtonPressed;
}
void WsControlManager::Serialize(Serializer& s)
{
SV(_state.InputSelect);
SV(_prevInput);
SV(_soundButtonPressed);
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "pch.h"
#include "Shared/SettingTypes.h"
#include "Shared/BaseControlManager.h"
#include "WS/WsTypes.h"
class WsConsole;
class WsControlManager final : public BaseControlManager
{
private:
WsControlManagerState _state = {};
WsConfig _prevConfig = {};
uint8_t _prevInput = 0;
bool _soundButtonPressed = false;
WsConsole* _console = nullptr;
public:
WsControlManager(Emulator* emu, WsConsole* console);
WsControlManagerState& GetState() { return _state; }
shared_ptr<BaseControlDevice> CreateControllerDevice(ControllerType type, uint8_t port) override;
void UpdateControlDevices() override;
void UpdateInputState() override;
uint8_t Read();
void Write(uint8_t value);
bool IsSoundPressed();
void Serialize(Serializer& s) override;
};
+135
View File
@@ -0,0 +1,135 @@
#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 WsController : public BaseControlDevice
{
private:
WsConsole* _console = nullptr;
vector<KeyMapping> _verticalMappings;
uint32_t _turboSpeed = 0;
protected:
string GetKeyNames() override
{
return "UDLRudlrSsBA";
}
void InternalSetStateFromInput() override
{
vector<KeyMapping>& keyMappings = _console->IsVerticalMode() ? _verticalMappings : _keyMappings;
for(KeyMapping& keyMapping : keyMappings) {
SetPressedState(Buttons::A, keyMapping.A);
SetPressedState(Buttons::B, keyMapping.B);
SetPressedState(Buttons::Sound, keyMapping.GenericKey1);
SetPressedState(Buttons::Start, keyMapping.Start);
SetPressedState(Buttons::Up, keyMapping.Up);
SetPressedState(Buttons::Down, keyMapping.Down);
SetPressedState(Buttons::Left, keyMapping.Left);
SetPressedState(Buttons::Right, keyMapping.Right);
SetPressedState(Buttons::Up2, keyMapping.U);
SetPressedState(Buttons::Down2, keyMapping.D);
SetPressedState(Buttons::Left2, keyMapping.L);
SetPressedState(Buttons::Right2, keyMapping.R);
uint8_t turboFreq = 1 << (4 - _turboSpeed);
bool turboOn = (uint8_t)(_emu->GetFrameCount() % turboFreq) < turboFreq / 2;
if(turboOn) {
SetPressedState(Buttons::A, keyMapping.TurboA);
SetPressedState(Buttons::B, keyMapping.TurboB);
}
}
}
void RefreshStateBuffer() override
{}
public:
enum Buttons { Up = 0, Down, Left, Right, Up2, Down2, Left2, Right2, Sound, Start, B, A };
WsController(Emulator* emu, WsConsole* console, uint8_t port, KeyMappingSet horizontalMappings, KeyMappingSet verticalMappings) : BaseControlDevice(emu, ControllerType::WsController, port, horizontalMappings)
{
//TODOWS turbo support
_verticalMappings = verticalMappings.GetKeyMappingArray();
_console = console;
_turboSpeed = horizontalMappings.TurboSpeed;
}
uint8_t ReadRam(uint16_t addr) override
{
return 0;
}
void WriteRam(uint16_t addr, uint8_t value) override
{
}
void InternalDrawController(InputHud& hud) override
{
if(_console->IsVerticalMode()) {
hud.DrawOutline(28, 31);
hud.DrawButton(20, 20, 3, 3, IsPressed(Buttons::Right));
hud.DrawButton(20, 26, 3, 3, IsPressed(Buttons::Left));
hud.DrawButton(17, 23, 3, 3, IsPressed(Buttons::Up));
hud.DrawButton(23, 23, 3, 3, IsPressed(Buttons::Down));
hud.DrawButton(5, 20, 3, 3, IsPressed(Buttons::Right2));
hud.DrawButton(5, 26, 3, 3, IsPressed(Buttons::Left2));
hud.DrawButton(2, 23, 3, 3, IsPressed(Buttons::Up2));
hud.DrawButton(8, 23, 3, 3, IsPressed(Buttons::Down2));
hud.DrawButton(23, 6, 3, 3, IsPressed(Buttons::B));
hud.DrawButton(20, 3, 3, 3, IsPressed(Buttons::A));
hud.DrawButton(21, 11, 2, 3, IsPressed(Buttons::Start));
hud.DrawButton(21, 15, 2, 3, IsPressed(Buttons::Sound));
hud.DrawNumber(_port + 1, 13, 2);
} else {
hud.DrawOutline(35, 24);
hud.DrawButton(5, 2, 3, 3, IsPressed(Buttons::Up2));
hud.DrawButton(5, 8, 3, 3, IsPressed(Buttons::Down2));
hud.DrawButton(2, 5, 3, 3, IsPressed(Buttons::Left2));
hud.DrawButton(8, 5, 3, 3, IsPressed(Buttons::Right2));
hud.DrawButton(5, 13, 3, 3, IsPressed(Buttons::Up));
hud.DrawButton(5, 19, 3, 3, IsPressed(Buttons::Down));
hud.DrawButton(2, 16, 3, 3, IsPressed(Buttons::Left));
hud.DrawButton(8, 16, 3, 3, IsPressed(Buttons::Right));
hud.DrawButton(25, 19, 3, 3, IsPressed(Buttons::B));
hud.DrawButton(29, 16, 3, 3, IsPressed(Buttons::A));
hud.DrawButton(14, 17, 4, 2, IsPressed(Buttons::Sound));
hud.DrawButton(19, 17, 4, 2, IsPressed(Buttons::Start));
hud.DrawNumber(_port + 1, 16, 2);
}
}
vector<DeviceButtonName> GetKeyNameAssociations() override
{
return {
{ "a", Buttons::A },
{ "b", Buttons::B },
{ "sound", Buttons::Sound },
{ "start", Buttons::Start },
{ "up", Buttons::Up },
{ "down", Buttons::Down },
{ "left", Buttons::Left },
{ "right", Buttons::Right },
{ "up2", Buttons::Up2 },
{ "down2", Buttons::Down2 },
{ "left2", Buttons::Left2 },
{ "right2", Buttons::Right2 },
};
}
};
+2132
View File
File diff suppressed because it is too large Load Diff
+296
View File
@@ -0,0 +1,296 @@
#if (defined(DUMMYCPU) && !defined(__DUMMYWsCpu__H)) || (!defined(DUMMYCPU) && !defined(__WsCpu__H))
#ifdef DUMMYCPU
#define __DUMMYWsCpu__H
#else
#define __WsCpu__H
#endif
#include "pch.h"
#include "WS/WsCpuPrefetch.h"
#include "WS/WsTypes.h"
#include "Shared/MemoryOperationType.h"
#include "Utilities/ISerializable.h"
class Emulator;
class WsMemoryManager;
class WsCpu final : public ISerializable
{
private:
struct ModRmState
{
uint16_t Segment;
uint16_t Offset;
uint8_t Mode;
uint8_t Register;
uint8_t Rm;
};
enum class WsRepMode : uint8_t
{
None,
Zero,
NotZero
};
enum class AluOp : uint8_t
{
Add = 0,
Or,
Adc,
Sbb,
And,
Sub,
Xor,
Cmp
};
enum class Grp2Mode
{
One,
CL,
Immediate
};
struct PrefixState
{
uint16_t PrefixCount;
WsSegment Segment;
WsRepMode Rep;
bool Lock;
bool Preserve;
};
static WsCpuParityTable _parity;
Emulator* _emu = nullptr;
WsMemoryManager* _memoryManager = nullptr;
WsCpuState _state = {};
ModRmState _modRm = {};
PrefixState _prefix = {};
#ifndef DUMMYCPU
WsCpuPrefetch _prefetch;
#endif
uint16_t* _modRegLut8[4] = { &_state.AX, &_state.CX, &_state.DX, &_state.BX };
uint16_t* _modSegLut16[4] = { &_state.ES, &_state.CS, &_state.SS, &_state.DS };
uint16_t* _modRegLut16[8] = {
&_state.AX, &_state.CX, &_state.DX, &_state.BX,
&_state.SP, &_state.BP, &_state.SI, &_state.DI
};
//Used to re-fill prefetch buffer with last opcode when REP prefix is used
uint8_t _opCode = 0;
//Divisions/AAM set carry/overflow to the last carry/overflow produced by the previous MUL operation
bool _mulOverflow = false;
template<typename T> __forceinline T ReadPort(uint16_t port);
template<typename T> __forceinline void WritePort(uint16_t port, T value);
__forceinline void ProcessMemoryAccess(uint32_t addr);
__forceinline void ProcessPortAccess(uint16_t port);
__forceinline uint8_t ReadCodeByte(bool forOpCode = false);
__forceinline uint16_t ReadCodeWord();
template<typename T> __forceinline T ReadImmediate();
template<typename T> __forceinline T ReadMemory(uint16_t seg, uint16_t offset);
template<typename T> __forceinline void WriteMemory(uint16_t seg, uint16_t offset, T value);
template<uint8_t cycles = 1> __forceinline void Idle();
template<typename T> constexpr uint32_t GetMaxValue();
template<typename T> constexpr uint32_t GetBitCount();
template<typename T> constexpr uint32_t GetSign();
void Move(uint16_t& dst, uint16_t src);
void MoveLo(uint16_t& dst, uint8_t src);
void MoveHi(uint16_t& dst, uint8_t src);
void Push(uint16_t value);
void PushSP();
void Pop(uint16_t& dst);
void PushSegment(uint16_t value);
void PopSegment(uint16_t& dst);
void PushFlags();
void PopFlags();
void PopMemory();
void PopAll();
void PushAll();
uint16_t GetSegment(WsSegment defaultSegment);
__forceinline void ReadModRmByte();
template<typename T> T GetModRegister(uint8_t reg) = delete;
template<typename T> void SetModRegister(uint8_t reg, T value) = delete;
uint16_t GetModSegRegister(uint8_t reg);
void SetModSegRegister(uint8_t reg, uint16_t value);
template<typename T> T GetModRm();
template<typename T> void SetModRm(T value);
template<bool sign, typename T> void Grp1ModRm();
template<typename T, Grp2Mode mode> void Grp2ModRm();
template<typename T> void Grp3ModRm();
template<typename T> void Grp45ModRm();
template<typename T> void TestModRm();
template<typename T> void TestImmediate();
template<typename T> void ExchangeModRm();
void Exchange(uint16_t& x, uint16_t& y);
template<bool direction, typename T> void MoveModRm();
template<bool direction> void MoveSegment();
template<bool direction, typename T> void MoveAccumulator();
template<typename T> void MoveImmediate();
template<AluOp op, bool direction, typename T> void ProcessAluModRm();
template<AluOp op, typename T> void ProcessAluImm();
template<typename T> T GetAluResult(AluOp op, T param1, T param2);
template<typename T> void Inc(T& dst);
template<typename T> void Dec(T& dst);
template<typename T> T Add(T x, T y, uint8_t carry);
template<typename T> T Sub(T x, T y, uint8_t borrow);
template<typename T> void MultSignedModRm();
template<typename T> void MulSigned(T x, T y);
template<typename T> int32_t GetMultiplyResult(T x, T y);
template<typename T> void MulUnsigned(T x, T y);
template<typename T> void DivSigned(T y);
template<typename T> void DivUnsigned(T y);
void ProcessInvalidDiv();
template<typename T> void UpdateFlags(T result);
template<typename T> T And(T x, T y);
template<typename T> T Or(T x, T y);
template<typename T> T Xor(T x, T y);
template<typename T> T ROL(T x, uint8_t shift);
template<typename T> T ROR(T x, uint8_t shift);
template<typename T> T RCL(T x, uint8_t shift);
template<typename T> T RCR(T x, uint8_t shift);
template<typename T> T SHL(T x, uint8_t shift);
template<typename T> T SHR(T x, uint8_t shift);
template<typename T> T SAR(T x, uint8_t shift);
void JumpFar();
void Jump(bool condition);
void JumpNearWord();
void Call(uint16_t offset);
void CallNearWord();
void CallFar();
void CallFar(uint16_t segment, uint16_t offset);
void Loop();
void LoopIf(bool condition);
void Ret();
void RetImm();
void RetFar();
void RetFarImm();
void RetInterrupt();
void Interrupt(uint8_t vector, bool pushFirstPrefix = false);
void InterruptOverflow();
void Enter();
void Leave();
void NOP();
void FP01();
void BOUND();
void SALC();
void CBW();
void CWD();
void LAHF();
void SAHF();
void LdsLesLeaModRm();
uint16_t LoadSegment();
void LDS();
void LES();
void LEA();
void XLAT();
void AdjustAscii(bool forSub);
void AAA();
void AAS();
void AAD();
void AAM();
void AdjustDecimal(bool forSub);
void DAA();
void DAS();
template<typename T, uint8_t delay> void Out(uint16_t port, T data);
template<typename T, bool isDxPort> void InStoreAx(uint16_t port);
template<typename T> void INS();
template<typename T> void OUTS();
template<typename T> void ProcessStringOperation(bool incSi, bool incDi);
template<typename T> void ProcessStringCmpOperation(bool incSi);
template<typename T> void MOVS();
template<typename T> void STOS();
template<typename T> void LODS();
template<typename T> void CMPS();
template<typename T> void SCAS();
void Undefined();
void Wait();
void Halt();
void SetFlagValue(bool& flag, bool value);
public:
WsCpu(Emulator* emu, WsMemoryManager* memoryManager);
WsCpuState& GetState() { return _state; }
uint64_t GetCycleCount() { return _state.CycleCount; }
void ProcessCpuCycle();
__forceinline void IncCycleCount() { _state.CycleCount++; }
void ClearPrefetch();
uint32_t GetProgramCounter(bool adjustForRepLoop = false);
void Exec();
void ExecOpCode();
void Serialize(Serializer& s) override;
#ifdef DUMMYCPU
private:
uint32_t _memOpCounter = 0;
MemoryOperationInfo _memOperations[32] = {};
bool _memWordAccess[32] = {};
public:
void SetDummyState(WsCpuState& state);
uint32_t GetOperationCount();
void LogMemoryOperation(uint32_t addr, uint16_t value, MemoryOperationType type, MemoryType memType, bool isWordAccess);
MemoryOperationInfo GetOperationInfo(uint32_t index);
bool IsWordAccess(uint32_t index);
#endif
};
template<> uint8_t WsCpu::GetModRegister<uint8_t>(uint8_t reg);
template<> uint16_t WsCpu::GetModRegister<uint16_t>(uint8_t reg);
template<> void WsCpu::SetModRegister<uint8_t>(uint8_t reg, uint8_t value);
template<> void WsCpu::SetModRegister<uint16_t>(uint8_t reg, uint16_t value);
#endif
+93
View File
@@ -0,0 +1,93 @@
#include "pch.h"
#include "WS/WsCpuPrefetch.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsCpu.h"
WsCpuPrefetch::WsCpuPrefetch(WsCpu* cpu, WsMemoryManager* memoryManager)
{
_cpu = cpu;
_memoryManager = memoryManager;
}
bool WsCpuPrefetch::IsFull()
{
return _size >= 16;
}
void WsCpuPrefetch::PushByte(uint8_t value)
{
_data[(_writePos++) & 0x0F] = value;
_fetchIp++;
_size++;
}
void WsCpuPrefetch::Prefetch()
{
_cpu->ProcessCpuCycle();
_waitCycles++;
if(IsFull()) {
return;
}
uint32_t addr = ((_fetchCs << 4) + _fetchIp) & 0xFFFFF;
uint8_t cycles = _memoryManager->GetWaitStates(addr);
if(_waitCycles < cycles) {
return;
}
_waitCycles = 0;
bool isWordBus = _memoryManager->IsWordBus(addr);
//TODOWS debugger can't see these reads
PushByte(_memoryManager->InternalRead(addr));
if(!IsFull() && isWordBus && (_fetchIp & 0x01)) {
PushByte(_memoryManager->InternalRead(((_fetchCs << 4) + _fetchIp)) & 0xFFFFF);
}
}
void WsCpuPrefetch::ProcessRep(uint8_t opCode)
{
if(IsFull()) {
_writePos--;
_fetchIp--;
_size--;
}
_readPos--;
_size++;
_data[_readPos & 0x0F] = opCode;
}
uint8_t WsCpuPrefetch::Read()
{
while(_size < 2) {
Prefetch();
}
_size--;
return _data[(_readPos++) & 0x0F];
}
void WsCpuPrefetch::Clear(uint16_t cs, uint16_t ip)
{
_fetchCs = cs;
_fetchIp = ip;
_size = 0;
_readPos = 0;
_writePos = 0;
_waitCycles = 0;
}
void WsCpuPrefetch::Serialize(Serializer& s)
{
SV(_fetchCs);
SV(_fetchIp);
SV(_readPos);
SV(_writePos);
SV(_size);
SV(_waitCycles);
SVArray(_data, 16);
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include "pch.h"
#include "Utilities/ISerializable.h"
class WsCpu;
class WsMemoryManager;
class WsCpuPrefetch final : public ISerializable
{
private:
uint16_t _fetchCs = 0;
uint16_t _fetchIp = 0;
uint8_t _readPos = 0;
uint8_t _writePos = 0;
uint8_t _size = 0;
uint8_t _waitCycles = 0;
uint8_t _data[16] = {};
WsMemoryManager* _memoryManager = nullptr;
WsCpu* _cpu = nullptr;
bool IsFull();
void PushByte(uint8_t value);
public:
WsCpuPrefetch(WsCpu* cpu, WsMemoryManager* memoryManager);
void Prefetch();
void ProcessRep(uint8_t opCode);
uint8_t Read();
void Clear(uint16_t cs, uint16_t ip);
void Serialize(Serializer& s) override;
};
+115
View File
@@ -0,0 +1,115 @@
#include "pch.h"
#include "WS/WsDefaultVideoFilter.h"
#include "WS/WsConsole.h"
#include "Shared/EmuSettings.h"
#include "Shared/Emulator.h"
#include "Shared/ColorUtilities.h"
#include "Shared/RewindManager.h"
WsDefaultVideoFilter::WsDefaultVideoFilter(Emulator* emu, WsConsole* console, bool applyNtscFilter) : BaseVideoFilter(emu), _ntscFilter(emu)
{
_emu = emu;
_console = console;
_applyNtscFilter = applyNtscFilter;
_prevFrame = new uint16_t[WsConstants::MaxPixelCount];
InitLookupTable();
}
WsDefaultVideoFilter::~WsDefaultVideoFilter()
{
delete[] _prevFrame;
}
uint32_t WsDefaultVideoFilter::BlendPixels(uint32_t a, uint32_t b)
{
return ((((a) ^ (b)) & 0xfffefefeL) >> 1) + ((a) & (b));
}
void WsDefaultVideoFilter::InitLookupTable()
{
VideoConfig config = _emu->GetSettings()->GetVideoConfig();
InitConversionMatrix(config.Hue, config.Saturation);
for(int rgb444 = 0; rgb444 < 0x1000; rgb444++) {
uint8_t r = (rgb444 >> 8) & 0xF;
uint8_t g = (rgb444 >> 4) & 0xF;
uint8_t b = rgb444 & 0xF;
if(_adjustColors) {
uint8_t r2 = r * 13 + g * 2 + b;
uint8_t g2 = g * 12 + b * 4;
uint8_t b2 = r * 3 + g * 2 + b * 11;
r = r2;
g = g2;
b = b2;
} else {
r = ColorUtilities::Convert4BitTo8Bit(r);
g = ColorUtilities::Convert4BitTo8Bit(g);
b = ColorUtilities::Convert4BitTo8Bit(b);
}
if(config.Hue != 0 || config.Saturation != 0 || config.Brightness != 0 || config.Contrast != 0) {
ApplyColorOptions(r, g, b, config.Brightness, config.Contrast);
_calculatedPalette[rgb444] = 0xFF000000 | (r << 16) | (g << 8) | b;
} else {
_calculatedPalette[rgb444] = 0xFF000000 | (r << 16) | (g << 8) | b;
}
}
_videoConfig = config;
}
FrameInfo WsDefaultVideoFilter::GetFrameInfo()
{
if(_applyNtscFilter) {
FrameInfo frameInfo;
frameInfo.Width = SNES_NTSC_OUT_WIDTH(_baseFrameInfo.Width);
frameInfo.Height = _baseFrameInfo.Height;
return frameInfo;
}
return BaseVideoFilter::GetFrameInfo();
}
void WsDefaultVideoFilter::OnBeforeApplyFilter()
{
VideoConfig config = _emu->GetSettings()->GetVideoConfig();
WsConfig wsConfig = _emu->GetSettings()->GetWsConfig();
bool adjustColors = wsConfig.LcdAdjustColors;
if(_videoConfig.Hue != config.Hue || _videoConfig.Saturation != config.Saturation || _videoConfig.Contrast != config.Contrast || _videoConfig.Brightness != config.Brightness || _adjustColors != adjustColors) {
_adjustColors = adjustColors;
InitLookupTable();
}
bool blendFrames = wsConfig.BlendFrames && !_emu->GetRewindManager()->IsRewinding() && !_emu->IsPaused();
if(_blendFrames != blendFrames) {
_blendFrames = blendFrames;
memset(_prevFrame, 0, WsConstants::MaxPixelCount * sizeof(uint16_t));
}
_videoConfig = config;
}
void WsDefaultVideoFilter::ApplyFilter(uint16_t* ppuOutputBuffer)
{
uint32_t* out = GetOutputBuffer();
FrameInfo size = _baseFrameInfo;
if(_blendFrames && _prevFrameSize.Width == size.Width && _prevFrameSize.Height == size.Height) {
for(uint32_t i = 0, len = size.Height * size.Width; i < len; i++) {
out[i] = BlendPixels(_calculatedPalette[_prevFrame[i]], _calculatedPalette[ppuOutputBuffer[i]]);
}
} else {
for(uint32_t i = 0, len = size.Height * size.Width; i < len; i++) {
out[i] = _calculatedPalette[ppuOutputBuffer[i]];
}
}
if(_blendFrames) {
std::copy(ppuOutputBuffer, ppuOutputBuffer + WsConstants::MaxPixelCount, _prevFrame);
_prevFrameSize = size;
}
if(_applyNtscFilter) {
_ntscFilter.ApplyFilter(out, size.Width, size.Height, 0);
}
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include "pch.h"
#include "Shared/Video/BaseVideoFilter.h"
#include "Shared/Video/GenericNtscFilter.h"
class WsConsole;
class Emulator;
class WsDefaultVideoFilter final : public BaseVideoFilter
{
private:
Emulator* _emu = nullptr;
WsConsole* _console = nullptr;
uint32_t _calculatedPalette[0x1000] = {};
VideoConfig _videoConfig = {};
FrameInfo _prevFrameSize = {};
uint16_t* _prevFrame = nullptr;
bool _blendFrames = false;
bool _adjustColors = false;
bool _applyNtscFilter = false;
GenericNtscFilter _ntscFilter;
uint32_t BlendPixels(uint32_t a, uint32_t b);
void InitLookupTable();
protected:
FrameInfo GetFrameInfo() override;
void OnBeforeApplyFilter() override;
public:
WsDefaultVideoFilter(Emulator* emu, WsConsole* console, bool applyNtscFilter);
~WsDefaultVideoFilter();
void ApplyFilter(uint16_t* ppuOutputBuffer) override;
};
+213
View File
@@ -0,0 +1,213 @@
#include "pch.h"
#include "WS/WsDmaController.h"
#include "WS/WsMemoryManager.h"
#include "WS/APU/WsApu.h"
#include "Utilities/BitUtilities.h"
#include "Utilities/Serializer.h"
void WsDmaController::Init(WsMemoryManager* memoryManager, WsApu* apu)
{
_memoryManager = memoryManager;
_apu = apu;
}
void WsDmaController::RunGeneralDma()
{
int offset = (_state.GdmaControl & 0x40) ? -2 : 2;
if(_state.GdmaLength == 0 || _memoryManager->GetWaitStates(_state.GdmaSrc) > 1 || !_memoryManager->IsWordBus(_state.GdmaSrc)) {
//When length is 0, or if transfer has an invalid source address, stop processing immediately
//The 5 cycles of setup time aren't executed in this scenario
return;
}
//GDMA takes 5+2*length cycles
_memoryManager->Exec();
_memoryManager->Exec();
_memoryManager->Exec();
_memoryManager->Exec();
_memoryManager->Exec();
while(_state.GdmaLength) {
if(_memoryManager->GetWaitStates(_state.GdmaSrc) > 1 || !_memoryManager->IsWordBus(_state.GdmaSrc)) {
//ROM with wait states and 8-bit buses are not allowed (SRAM, cart when 16-bit flag isn't set)
break;
}
uint16_t value = _memoryManager->Read<uint16_t>((_state.GdmaSrc >> 4) & 0xF000, (uint16_t)_state.GdmaSrc, MemoryOperationType::DmaRead);
_memoryManager->Write<uint16_t>(0, _state.GdmaDest, value, MemoryOperationType::DmaWrite);
_state.GdmaSrc = (_state.GdmaSrc + offset) & 0xFFFFF;
_state.GdmaDest = _state.GdmaDest + offset;
_state.GdmaLength -= 2;
}
_state.GdmaControl &= ~0x80;
}
void WsDmaController::ProcessSoundDma()
{
if(!_state.SdmaEnabled || _state.SdmaLength == 0) {
//TODOWS what does enabling when length = 0 do?
return;
}
if(_state.SdmaTimer == 0) {
_state.SdmaTimer = _state.SdmaFrequency;
//Sound DMA steals 6 cycles + N access cycles to run
//4 cycles at the start + N read cycles + 1 dma write cycle
_memoryManager->Exec();
_memoryManager->Exec();
_memoryManager->Exec();
_memoryManager->Exec();
_memoryManager->Exec();
uint8_t sampleValue = _memoryManager->Read<uint8_t>((_state.SdmaSrc >> 4) & 0xF000, (uint16_t)_state.SdmaSrc, MemoryOperationType::DmaRead);
if(_state.SdmaHold) {
sampleValue = 0;
}
if(!_state.SdmaHold) {
int offset = _state.SdmaDecrement ? -1 : 1;
_state.SdmaSrc = (_state.SdmaSrc + offset) & 0xFFFFF;
_state.SdmaLength--;
if(_state.SdmaLength == 0) {
if(_state.SdmaRepeat) {
_state.SdmaSrc = _state.SdmaSrcReloadValue;
_state.SdmaLength = _state.SdmaLengthReloadValue;
} else {
_state.SdmaEnabled = false;
_state.SdmaControl &= 0x7F;
}
}
} else {
sampleValue = 0;
}
_memoryManager->Exec();
_apu->WriteDma(_state.SdmaHyperVoice, sampleValue);
} else {
_state.SdmaTimer--;
}
}
uint8_t WsDmaController::ReadPort(uint16_t port)
{
switch(port) {
case 0x40: return BitUtilities::GetBits<0>(_state.GdmaSrc);
case 0x41: return BitUtilities::GetBits<8>(_state.GdmaSrc);
case 0x42: return BitUtilities::GetBits<16>(_state.GdmaSrc);
case 0x43: return 0;
case 0x44: return BitUtilities::GetBits<0>(_state.GdmaDest);
case 0x45: return BitUtilities::GetBits<8>(_state.GdmaDest);
case 0x46: return BitUtilities::GetBits<0>(_state.GdmaLength);
case 0x47: return BitUtilities::GetBits<8>(_state.GdmaLength);
case 0x48: return _state.GdmaControl;
case 0x4A: return BitUtilities::GetBits<0>(_state.SdmaSrc);
case 0x4B: return BitUtilities::GetBits<8>(_state.SdmaSrc);
case 0x4C: return BitUtilities::GetBits<16>(_state.SdmaSrc);
case 0x4D: return 0;
case 0x4E: return BitUtilities::GetBits<0>(_state.SdmaLength);
case 0x4F: return BitUtilities::GetBits<8>(_state.SdmaLength);
case 0x50: return BitUtilities::GetBits<16>(_state.SdmaLength);
case 0x51: return 0;
case 0x52: return _state.SdmaControl;
case 0x53: return 0;
}
//TODOWS openbus
return 0x90;
}
void WsDmaController::WritePort(uint16_t port, uint8_t value)
{
switch(port) {
case 0x40: BitUtilities::SetBits<0>(_state.GdmaSrc, value & 0xFE); break;
case 0x41: BitUtilities::SetBits<8>(_state.GdmaSrc, value); break;
case 0x42: BitUtilities::SetBits<16>(_state.GdmaSrc, value & 0x0F); break;
case 0x43: break;
case 0x44: BitUtilities::SetBits<0>(_state.GdmaDest, value & 0xFE); break;
case 0x45: BitUtilities::SetBits<8>(_state.GdmaDest, value); break;
case 0x46: BitUtilities::SetBits<0>(_state.GdmaLength, value & 0xFE); break;
case 0x47: BitUtilities::SetBits<8>(_state.GdmaLength, value); break;
case 0x48:
_state.GdmaControl = value & 0xC0;
if(_state.GdmaControl & 0x80) {
RunGeneralDma();
}
break;
case 0x4A:
BitUtilities::SetBits<0>(_state.SdmaSrc, value);
BitUtilities::SetBits<0>(_state.SdmaSrcReloadValue, value);
break;
case 0x4B:
BitUtilities::SetBits<8>(_state.SdmaSrc, value);
BitUtilities::SetBits<8>(_state.SdmaSrcReloadValue, value);
break;
case 0x4C:
BitUtilities::SetBits<16>(_state.SdmaSrc, value & 0x0F);
BitUtilities::SetBits<16>(_state.SdmaSrcReloadValue, value & 0x0F);
break;
case 0x4E:
BitUtilities::SetBits<0>(_state.SdmaLength, value);
BitUtilities::SetBits<0>(_state.SdmaLengthReloadValue, value);
break;
case 0x4F:
BitUtilities::SetBits<8>(_state.SdmaLength, value);
BitUtilities::SetBits<8>(_state.SdmaLengthReloadValue, value);
break;
case 0x50:
BitUtilities::SetBits<16>(_state.SdmaLength, value & 0x0F);
BitUtilities::SetBits<16>(_state.SdmaLengthReloadValue, value & 0x0F);
break;
case 0x52:
_state.SdmaControl = value & 0xDF;
switch(value & 0x03) {
default:
case 0: _state.SdmaFrequency = 5; break;
case 1: _state.SdmaFrequency = 3; break;
case 2: _state.SdmaFrequency = 1; break;
case 3: _state.SdmaFrequency = 0; break;
}
_state.SdmaHold = value & 0x04;
_state.SdmaRepeat = value & 0x08;
_state.SdmaHyperVoice = value & 0x10;
_state.SdmaDecrement = value & 0x40;
_state.SdmaEnabled = value & 0x80;
break;
}
}
void WsDmaController::Serialize(Serializer& s)
{
SV(_state.GdmaSrc);
SV(_state.SdmaSrc);
SV(_state.SdmaLength);
SV(_state.SdmaSrcReloadValue);
SV(_state.SdmaLengthReloadValue);
SV(_state.GdmaDest);
SV(_state.GdmaLength);
SV(_state.GdmaControl);
SV(_state.SdmaControl);
SV(_state.SdmaEnabled);
SV(_state.SdmaDecrement);
SV(_state.SdmaHyperVoice);
SV(_state.SdmaRepeat);
SV(_state.SdmaHold);
SV(_state.SdmaFrequency);
SV(_state.SdmaTimer);
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Utilities/ISerializable.h"
class WsMemoryManager;
class WsApu;
class WsDmaController final : public ISerializable
{
private:
WsMemoryManager* _memoryManager = nullptr;
WsDmaControllerState _state = {};
WsApu* _apu = nullptr;
public:
void Init(WsMemoryManager* memoryManager, WsApu* apu);
void RunGeneralDma();
void ProcessSoundDma();
WsDmaControllerState& GetState() { return _state; }
uint8_t ReadPort(uint16_t port);
void WritePort(uint16_t port, uint8_t value);
void Serialize(Serializer& s) override;
};
+225
View File
@@ -0,0 +1,225 @@
#include "pch.h"
#include "WS/WsEeprom.h"
#include "WS/WsConsole.h"
#include "Shared/Emulator.h"
#include "Shared/BatteryManager.h"
#include "Utilities/BitUtilities.h"
#include "Utilities/Serializer.h"
WsEeprom::WsEeprom(Emulator* emu, WsConsole* console, WsEepromSize size, uint8_t* eepromData, bool isInternal)
{
_emu = emu;
_console = console;
_isInternal = isInternal;
_data = eepromData;
_state.Size = size;
_state.Idle = true;
_state.ReadDone = true;
_state.WriteDisabled = true;
if(_isInternal) {
InitInternalEepromData();
}
}
WsEepromCommand WsEeprom::GetCommand()
{
uint16_t command = _state.Command;
if(GetSize() != WsEepromSize::Size128) {
command >>= 4;
}
switch(command & 0xFFC0) {
case 0x0180: return WsEepromCommand::Read;
case 0x0140: return WsEepromCommand::Write;
case 0x01C0: return WsEepromCommand::Erase;
case 0x0100:
switch((command >> 4) & 0x03) {
case 0x00: return WsEepromCommand::WriteDisable;
case 0x01: return WsEepromCommand::WriteAll;
case 0x02: return WsEepromCommand::EraseAll;
case 0x03: return WsEepromCommand::WriteEnable;
}
}
return WsEepromCommand::Unknown;
}
WsEepromSize WsEeprom::GetSize()
{
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
return WsEepromSize::Size128;
}
return _state.Size;
}
uint16_t WsEeprom::GetCommandAddress()
{
switch(GetSize()) {
case WsEepromSize::Size128: return _state.Command & 0x3F;
case WsEepromSize::Size1kb: return _state.Command & 0x1FF;
case WsEepromSize::Size2kb: return _state.Command & 0x3FF;
}
return 0;
}
void WsEeprom::WriteValue(uint16_t addr, uint16_t value)
{
if(!_state.WriteDisabled && (!_state.InternalEepromWriteProtected || addr < 0x30)) {
if(_isInternal) {
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsInternalEeprom, MemoryOperationType::Write>(addr << 1, value);
} else {
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsCartEeprom, MemoryOperationType::Write>(addr << 1, value);
}
_data[addr << 1] = (uint8_t)value;
_data[(addr << 1) + 1] = (value >> 8);
}
}
string WsEeprom::ConvertToEepromString(string in)
{
for(size_t i = 0; i < in.size(); i++) {
in[i] = in[i] - 0x36;
}
return in;
}
void WsEeprom::InitInternalEepromData()
{
memset(_data, 0, (uint32_t)_state.Size);
string name;
switch(_console->GetModel()) {
case WsModel::Monochrome:
name = "WONDERSWAN";
break;
case WsModel::Color:
case WsModel::SwanCrystal:
//Set volume to max by default
_data[0x83] = 0x03;
if(_console->GetModel() == WsModel::Color) {
name = "WONDERSWANCOLOR";
} else {
name = "SWANCRYSTAL";
}
break;
}
memcpy(_data + 0x60, ConvertToEepromString(name).c_str(), name.size());
}
void WsEeprom::WritePort(uint8_t port, uint8_t value)
{
switch(port) {
case 0x00: BitUtilities::SetBits<0>(_state.WriteBuffer, value); break;
case 0x01: BitUtilities::SetBits<8>(_state.WriteBuffer, value); break;
case 0x02: BitUtilities::SetBits<0>(_state.Command, value); break;
case 0x03: BitUtilities::SetBits<8>(_state.Command, value); break;
case 0x04:
bool read = value & 0x10;
bool write = value & 0x20;
bool other = value & 0x40;
bool writeProtect = value & 0x80;
if((uint8_t)read + (uint8_t)write + (uint8_t)other + (uint8_t)writeProtect > 1) {
//Invalid operation, do nothing
return;
}
//TODOWS abort (cart eeprom)
if(writeProtect && _isInternal) {
_state.InternalEepromWriteProtected = true;
return;
}
WsEepromCommand cmd = GetCommand();
uint16_t addr = GetCommandAddress();
if(read) {
if(cmd == WsEepromCommand::Read) {
_state.ReadBuffer = _data[addr << 1] | (_data[(addr << 1) + 1] << 8);
if(_isInternal) {
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsInternalEeprom, MemoryOperationType::Read>(addr << 1, _state.ReadBuffer);
} else {
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsCartEeprom, MemoryOperationType::Read>(addr << 1, _state.ReadBuffer);
}
_state.ReadDone = true;
}
} else if(write) {
if(cmd == WsEepromCommand::Write) {
WriteValue(addr, _state.WriteBuffer);
} else if(cmd == WsEepromCommand::WriteAll) {
for(int i = 0; i < (int)_state.Size; i += 2) {
WriteValue(i >> 1, _state.WriteBuffer);
}
}
} else if(other) {
switch(cmd) {
case WsEepromCommand::Erase:
WriteValue(addr, 0xFFFF);
break;
case WsEepromCommand::WriteDisable:
_state.WriteDisabled = true;
break;
case WsEepromCommand::EraseAll:
for(int i = 0; i < (int)_state.Size; i += 2) {
WriteValue(i >> 1, 0xFFFF);
}
break;
case WsEepromCommand::WriteEnable:
_state.WriteDisabled = false;
break;
}
}
break;
}
}
uint8_t WsEeprom::ReadPort(uint8_t port)
{
switch(port) {
case 0x00: return BitUtilities::GetBits<0>(_state.ReadBuffer);
case 0x01: return BitUtilities::GetBits<8>(_state.ReadBuffer);
case 0x02: return BitUtilities::GetBits<0>(_state.Command);
case 0x03: return BitUtilities::GetBits<8>(_state.Command);
case 0x04:
return (
(_state.ReadDone ? 0x01 : 0) |
(_state.Idle ? 0x02 : 0) |
(_state.InternalEepromWriteProtected ? 0x80 : 0)
);
}
return 0;
}
void WsEeprom::LoadBattery()
{
_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);
}
void WsEeprom::Serialize(Serializer& s)
{
SV(_state.Size);
SV(_state.ReadBuffer);
SV(_state.WriteBuffer);
SV(_state.Command);
SV(_state.Control);
SV(_state.WriteDisabled);
SV(_state.ReadDone);
SV(_state.Idle);
SV(_state.InternalEepromWriteProtected);
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Utilities/ISerializable.h"
class WsConsole;
class Emulator;
enum class WsEepromCommand
{
Read,
Write,
Erase,
WriteDisable,
WriteEnable,
EraseAll,
WriteAll,
Unknown
};
class WsEeprom final : public ISerializable
{
private:
WsEepromState _state = {};
WsConsole* _console = nullptr;
Emulator* _emu = nullptr;
uint8_t* _data = nullptr;
bool _isInternal = false;
WsEepromCommand GetCommand();
WsEepromSize GetSize();
uint16_t GetCommandAddress();
void WriteValue(uint16_t addr, uint16_t value);
string ConvertToEepromString(string in);
void InitInternalEepromData();
public:
WsEeprom(Emulator* emu, WsConsole* console, WsEepromSize size, uint8_t* eepromData, bool isInternal);
WsEepromState& GetState() { return _state; }
void WritePort(uint8_t port, uint8_t value);
uint8_t ReadPort(uint8_t port);
void LoadBattery();
void SaveBattery();
void Serialize(Serializer& s);
};
+480
View File
@@ -0,0 +1,480 @@
#include "pch.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsConsole.h"
#include "WS/WsDmaController.h"
#include "WS/WsControlManager.h"
#include "WS/WsTimer.h"
#include "WS/WsSerial.h"
#include "WS/WsEeprom.h"
#include "WS/Carts/WsCart.h"
#include "Shared/MessageManager.h"
#include "Shared/Emulator.h"
#include "Utilities/Serializer.h"
void WsMemoryManager::Init(Emulator* emu, WsConsole* console, WsCpu* cpu, WsPpu* ppu, WsControlManager* controlManager, WsCart* cart, WsTimer* timer, WsDmaController* dmaController, WsEeprom* eeprom, WsApu* apu, WsSerial* serial)
{
_emu = emu;
_console = console;
_cpu = cpu;
_cart = cart;
_ppu = ppu;
_apu = apu;
_timer = timer;
_dmaController = dmaController;
_controlManager = controlManager;
_eeprom = eeprom;
_serial = serial;
_workRamSize = _emu->GetMemory(MemoryType::WsWorkRam).Size;
_prgRom = (uint8_t*)_emu->GetMemory(MemoryType::WsPrgRom).Memory;
_prgRomSize = _emu->GetMemory(MemoryType::WsPrgRom).Size;
_saveRam = (uint8_t*)_emu->GetMemory(MemoryType::WsCartRam).Memory;
_saveRamSize = _emu->GetMemory(MemoryType::WsCartRam).Size;
_bootRom = (uint8_t*)_emu->GetMemory(MemoryType::WsBootRom).Memory;
_bootRomSize = _emu->GetMemory(MemoryType::WsBootRom).Size;
RefreshMappings();
}
void WsMemoryManager::RefreshMappings()
{
_cart->RefreshMappings();
if(_state.ColorEnabled) {
Map(0, 0xFFFF, MemoryType::WsWorkRam, 0, false);
} else {
Unmap(0x4000, 0xFFFF);
Map(0, 0x3FFF, MemoryType::WsWorkRam, 0, false);
}
if(!_state.BootRomDisabled) {
Map(0x100000 - _bootRomSize, 0xFFFFF, MemoryType::WsBootRom, 0, true);
}
}
void WsMemoryManager::Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly)
{
uint8_t* src = (uint8_t*)_emu->GetMemory(type).Memory;
uint32_t size = _emu->GetMemory(type).Size;
if(size > 0) {
while(offset >= size) {
offset -= size;
}
src += offset;
for(uint32_t i = start; i < end; i += 0x1000) {
_reads[i >> 12] = src;
_writes[i >> 12] = readonly ? nullptr : src;
if(src) {
src += 0x1000;
offset = (offset + 0x1000);
if(offset >= size) {
offset = 0;
src = (uint8_t*)_emu->GetMemory(type).Memory;
}
}
}
} else {
Unmap(start, end);
}
}
void WsMemoryManager::Unmap(uint32_t start, uint32_t end)
{
for(uint32_t i = start; i < end; i += 0x1000) {
_reads[i >> 12] = nullptr;
_writes[i >> 12] = nullptr;
}
}
uint8_t WsMemoryManager::DebugRead(uint32_t addr)
{
uint8_t* handler = _reads[addr >> 12];
if(handler) {
return handler[addr & 0xFFF];
}
return 0;
}
void WsMemoryManager::DebugWrite(uint32_t addr, uint8_t value)
{
uint8_t* handler = _writes[addr >> 12];
if(handler) {
handler[addr & 0xFFF] = value;
}
}
template<typename T>
T WsMemoryManager::DebugCpuRead(uint16_t seg, uint16_t offset)
{
uint32_t addr = (seg << 4) + offset;
if constexpr(std::is_same<T, uint16_t>::value) {
bool splitReads = !IsWordBus(addr) || (addr & 0x01);
if(splitReads) {
uint8_t lo = DebugCpuRead<uint8_t>(seg, offset);
uint8_t hi = DebugCpuRead<uint8_t>(seg, offset + 1);
return lo | (hi << 8);
} else {
uint8_t lo = DebugRead(addr);
addr = (seg << 4) + (uint16_t)(offset + 1);
uint8_t hi = DebugRead(addr);
return lo | (hi << 8);
}
} else {
return DebugRead(addr);
}
}
template<typename T>
T WsMemoryManager::ReadPort(uint16_t port)
{
if constexpr(std::is_same<T, uint16_t>::value) {
bool splitReads = !IsWordPort(port) || (port & 0x01);
if(splitReads) {
uint8_t lo = ReadPort<uint8_t>(port);
uint8_t hi = ReadPort<uint8_t>(port + 1);
return lo | (hi << 8);
} else {
Exec();
uint16_t value;
if(IsUnmappedPort(port)) {
//TODOWS open bus
value = 0x9090;
} else {
uint8_t lo = InternalReadPort(port, true);
uint8_t hi = InternalReadPort(port + 1, true);
value = lo | (hi << 8);
}
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Read>(port, value);
return value;
}
} else {
Exec();
//TODOWS open bus
uint8_t value = IsUnmappedPort(port) ? 0x90 : InternalReadPort(port, false);
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Read>(port, value);
return value;
}
}
template<typename T>
void WsMemoryManager::WritePort(uint16_t port, T value)
{
if constexpr(std::is_same<T, uint16_t>::value) {
bool splitWrites = !IsWordPort(port) || (port & 0x01);
if(splitWrites) {
WritePort<uint8_t>(port, value);
WritePort<uint8_t>(port + 1, value >> 8);
} else {
Exec();
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Write>(port, value);
if(!IsUnmappedPort(port)) {
InternalWritePort(port, value, true);
InternalWritePort(port + 1, value >> 8, true);
}
}
} else {
Exec();
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsPort, MemoryOperationType::Write>(port, value);
if(!IsUnmappedPort(port)) {
InternalWritePort(port, value, false);
}
}
}
template<typename T>
T WsMemoryManager::DebugReadPort(uint16_t port)
{
if constexpr(std::is_same<T, uint16_t>::value) {
uint8_t lo = DebugReadPort<uint8_t>(port);
uint8_t hi = DebugReadPort<uint8_t>(port + 1);
return lo | (hi << 8);
} else {
if(IsUnmappedPort(port)) {
//TODOWS open bus
return 0x90;
}
if(port == 0xB1 || port == 0xB3 || port == 0xB5) {
//TODOWS implement peek to avoid side-effects for these ports
return 0;
}
return InternalReadPort(port, false);
}
}
bool WsMemoryManager::IsUnmappedPort(uint16_t port)
{
return (port & 0x100) || (port >= 0x100 && (port & 0xFF) >= 0xB7);
}
uint8_t WsMemoryManager::InternalReadPort(uint16_t port, bool isWordAccess)
{
port &= 0xFF;
if(port <= 0x3F) {
return _ppu->ReadPort(port);
} else if(port >= 0x40 && port <= 0x53 && _state.ColorEnabled) {
return _dmaController->ReadPort(port);
} else if(port >= 0x64 && port <= 0x6B && _state.ColorEnabled) {
return _apu->Read(port); //HyperVoice
} else if(port >= 0x70 && port <= 0x77 && _console->GetModel() == WsModel::SwanCrystal) {
return _ppu->ReadLcdConfigPort(port);
} else if(port >= 0x80 && port <= 0x9F) {
return _apu->Read(port);
} else if(port >= 0xA2 && port <= 0xAB) {
return _timer->ReadPort(port);
} else if(port >= 0xBA && port <= 0xBF) {
if(_console->GetModel() != WsModel::Monochrome || isWordAccess || !(port & 0x01)) {
return _eeprom->ReadPort(port - 0xBA);
} else {
//TODOWS open bus
return 0x90;
}
} else if(port >= 0xC0) {
return _cart->ReadPort(port);
} else {
switch(port) {
case 0x60: return _state.SystemControl2;
case 0x62: return _console->GetModel() == WsModel::SwanCrystal ? 0x80 : 0;
case 0xA0:
return (
(_state.BootRomDisabled ? 0x01 : 0) |
(_console->GetModel() == WsModel::Monochrome ? 0 : 0x02) |
(_state.CartWordBus ? 0x04 : 0) |
(_state.SlowRom ? 0x08 : 0) |
0x80 //mbc authentication?
);
case 0xB0: return GetIrqVector();
case 0xB1: return _serial->Read(port);
case 0xB2: return _state.EnabledIrqs;
case 0xB3: return _serial->Read(port);
case 0xB4: return GetActiveIrqs();
case 0xB5: return _controlManager->Read();
case 0xB7: return _state.EnableLowBatteryNmi ? 0x10 : 0;
default:
//TODOWS open bus
LogDebug("[Debug] Read - missing handler: $" + HexUtilities::ToHex(port));
return 0x90;
}
}
}
void WsMemoryManager::InternalWritePort(uint16_t port, uint8_t value, bool isWordAccess)
{
port &= 0xFF;
if(port <= 0x3F) {
_ppu->WritePort(port, value);
} else if(port >= 0x40 && port <= 0x53 && _state.ColorEnabled) {
return _dmaController->WritePort(port, value);
} else if(port >= 0x64 && port <= 0x6B && _state.ColorEnabled) {
_apu->Write(port, value); //HyperVoice
} else if(port >= 0x70 && port <= 0x77 && !_state.BootRomDisabled && _console->GetModel() == WsModel::SwanCrystal) {
_ppu->WriteLcdConfigPort(port, value);
} else if(port >= 0x80 && port <= 0x9F) {
_apu->Write(port, value);
} else if(port >= 0xA2 && port <= 0xAB) {
_timer->WritePort(port, value);
} else if(port >= 0xBA && port <= 0xBF) {
if(_console->GetModel() != WsModel::Monochrome || isWordAccess || !(port & 0x01)) {
_eeprom->WritePort(port - 0xBA, value);
}
} else if(port >= 0xC0) {
_cart->WritePort(port, value);
} else {
switch(port) {
case 0x60:
if(_console->GetModel() != WsModel::Monochrome) {
_state.SystemControl2 = value & 0xEF;
_state.ColorEnabled = value & 0x80;
_state.Enable4bpp = value & 0x40;
_state.Enable4bppPacked = value & 0x20;
_state.SlowSram = value & 0x02;
_state.SlowPort = value & 0x08;
RefreshMappings();
WsVideoMode mode = WsVideoMode::Monochrome;
if(_state.ColorEnabled) {
mode = WsVideoMode::Color2bpp;
if(_state.Enable4bpp) {
mode = WsVideoMode::Color4bpp;
if(_state.Enable4bppPacked) {
mode = WsVideoMode::Color4bppPacked;
}
}
}
_ppu->SetVideoMode(mode);
}
break;
case 0x62:
if(value & 0x01) {
MessageManager::DisplayMessage("WS", "Power off.");
}
break;
case 0xA0:
_state.BootRomDisabled = (value & 0x01) || _state.BootRomDisabled;
_state.CartWordBus = value & 0x04;
_state.SlowRom = value & 0x08;
RefreshMappings();
break;
case 0xB0: _state.IrqVectorOffset = value & 0xF8; break;
case 0xB1: _serial->Write(port, value); break;
case 0xB2: _state.EnabledIrqs = value; break;
case 0xB3: _serial->Write(port, value); break;
case 0xB5: _controlManager->Write(value); break;
case 0xB6: _state.ActiveIrqs &= ~value; break;
case 0xB7: _state.EnableLowBatteryNmi = value & 0x10; break;
default:
LogDebug("[Debug] Write - missing handler: $" + HexUtilities::ToHex(port) + " = " + HexUtilities::ToHex(value));
break;
}
}
}
bool WsMemoryManager::IsWordBus(uint32_t addr)
{
switch(addr >> 16) {
case 0: return true;
case 1: return false;
default: return _state.CartWordBus;
}
}
uint8_t WsMemoryManager::GetWaitStates(uint32_t addr)
{
switch(addr >> 16) {
case 0: return 1;
case 1: return 1 + (uint8_t)_state.SlowSram;
default: return 1 + (uint8_t)_state.SlowRom;
}
}
bool WsMemoryManager::IsWordPort(uint16_t port)
{
return port < 0xC0;
}
uint8_t WsMemoryManager::GetPortWaitStates(uint16_t port)
{
return 1 + (uint8_t)(_state.SlowPort && port >= 0xC0 && port <= 0xFF);
}
void WsMemoryManager::SetIrqSource(WsIrqSource src)
{
if(_state.EnabledIrqs & (uint8_t)src) {
_state.ActiveIrqs |= (uint8_t)src;
}
}
void WsMemoryManager::ClearIrqSource(WsIrqSource src)
{
_state.ActiveIrqs &= (uint8_t)src;
}
uint8_t WsMemoryManager::GetActiveIrqs()
{
if(_serial->HasSendIrq() && (_state.EnabledIrqs & (uint8_t)WsIrqSource::UartSendReady)) {
_state.ActiveIrqs |= (uint8_t)WsIrqSource::UartSendReady;
}
return _state.ActiveIrqs;
}
uint8_t WsMemoryManager::GetIrqVector()
{
uint8_t activeIrqs = GetActiveIrqs();
for(int i = 7; i >= 0; i--) {
if(activeIrqs & (1 << i)) {
return _state.IrqVectorOffset + i;
}
}
return _state.IrqVectorOffset;
}
AddressInfo WsMemoryManager::GetAbsoluteAddress(uint32_t relAddr)
{
if(relAddr < _workRamSize) {
return { (int)(relAddr & (_workRamSize - 1)), MemoryType::WsWorkRam };
}
uint8_t* ptr = _reads[relAddr >> 12];
if(ptr >= _prgRom && ptr < _prgRom + _prgRomSize) {
return { (int)(ptr - _prgRom + (relAddr & 0xFFF)), MemoryType::WsPrgRom };
} else if(ptr >= _saveRam && ptr < _saveRam + _saveRamSize) {
return { (int)(ptr - _saveRam + (relAddr & 0xFFF)), MemoryType::WsCartRam };
} else if(ptr >= _bootRom && ptr < _bootRom + _bootRomSize) {
return { (int)(ptr - _bootRom + (relAddr & 0xFFF)), MemoryType::WsBootRom };
}
return { -1, MemoryType::None };
}
int WsMemoryManager::GetRelativeAddress(AddressInfo& absAddress)
{
switch(absAddress.Type) {
case MemoryType::WsPrgRom:
case MemoryType::WsCartRam:
case MemoryType::WsBootRom: {
//Use the closest mirror to the current code segment
uint8_t startBank = (_cpu->GetState().CS >> 12) << 4;
for(int32_t i = 0; i < 256; i++) {
uint8_t bank = (uint8_t)(startBank + i);
AddressInfo blockAddr = GetAbsoluteAddress(bank * 0x1000);
if(blockAddr.Type == absAddress.Type && (blockAddr.Address & ~0xFFF) == (absAddress.Address & ~0xFFF)) {
return (bank << 12) | (absAddress.Address & 0xFFF);
}
}
return -1;
}
case MemoryType::WsWorkRam:
return absAddress.Address & (_workRamSize - 1);
}
return -1;
}
void WsMemoryManager::Serialize(Serializer& s)
{
SV(_state.ActiveIrqs);
SV(_state.EnabledIrqs);
SV(_state.IrqVectorOffset);
SV(_state.SystemControl2);
SV(_state.ColorEnabled);
SV(_state.Enable4bpp);
SV(_state.Enable4bppPacked);
SV(_state.BootRomDisabled);
SV(_state.SlowPort);
SV(_state.SlowRom);
SV(_state.SlowSram);
SV(_state.CartWordBus);
SV(_state.EnableLowBatteryNmi);
if(!s.IsSaving()) {
RefreshMappings();
}
}
template uint8_t WsMemoryManager::ReadPort(uint16_t port);
template uint16_t WsMemoryManager::ReadPort(uint16_t port);
template uint8_t WsMemoryManager::DebugReadPort(uint16_t port);
template uint16_t WsMemoryManager::DebugReadPort(uint16_t port);
template void WsMemoryManager::WritePort(uint16_t port, uint8_t value);
template void WsMemoryManager::WritePort(uint16_t port, uint16_t value);
template uint8_t WsMemoryManager::DebugCpuRead(uint16_t seg, uint16_t offset);
template uint16_t WsMemoryManager::DebugCpuRead(uint16_t seg, uint16_t offset);
+167
View File
@@ -0,0 +1,167 @@
#pragma once
#include "pch.h"
#include "WS/WsCpu.h"
#include "WS/APU/WsApu.h"
#include "WS/WsPpu.h"
#include "WS/WsTypes.h"
#include "Utilities/ISerializable.h"
class WsConsole;
class WsTimer;
class WsControlManager;
class WsCart;
class WsSerial;
class WsDmaController;
class WsEeprom;
class Emulator;
class WsMemoryManager final : public ISerializable
{
private:
WsConsole* _console = nullptr;
WsCpu* _cpu = nullptr;
WsPpu* _ppu = nullptr;
WsApu* _apu = nullptr;
WsControlManager* _controlManager = nullptr;
WsCart* _cart = nullptr;
WsTimer* _timer = nullptr;
WsSerial* _serial = nullptr;
WsDmaController* _dmaController = nullptr;
WsEeprom* _eeprom = nullptr;
Emulator* _emu = nullptr;
uint8_t* _prgRom = nullptr;
uint32_t _prgRomSize = 0;
uint8_t* _saveRam = nullptr;
uint32_t _saveRamSize = 0;
uint8_t* _bootRom = nullptr;
uint32_t _bootRomSize = 0;
uint32_t _workRamSize = 0;
WsMemoryManagerState _state = {};
uint8_t* _reads[256] = {};
uint8_t* _writes[256] = {};
bool IsWordPort(uint16_t port);
uint8_t GetPortWaitStates(uint16_t port);
bool IsUnmappedPort(uint16_t port);
public:
WsMemoryManager() {}
void Init(Emulator* emu, WsConsole* console, WsCpu* cpu, WsPpu* ppu, WsControlManager* controlManager, WsCart* cart, WsTimer* timer, WsDmaController* dmaController, WsEeprom* eeprom, WsApu* apu, WsSerial* serial);
WsMemoryManagerState& GetState() { return _state; }
void RefreshMappings();
void Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly);
void Unmap(uint32_t start, uint32_t end);
__forceinline void Exec()
{
_cpu->IncCycleCount();
_ppu->Exec();
_apu->Run();
}
__forceinline uint8_t InternalRead(uint32_t addr)
{
uint8_t* handler = _reads[addr >> 12];
uint8_t value = 0x90;
if(handler) {
value = handler[addr & 0xFFF];
}
//TODOWS open bus
return value;
}
__forceinline void InternalWrite(uint32_t addr, uint8_t value)
{
//TODOWS open bus
uint8_t* handler = _writes[addr >> 12];
if(handler) {
handler[addr & 0xFFF] = value;
}
}
uint8_t DebugRead(uint32_t addr);
void DebugWrite(uint32_t addr, uint8_t value);
template<typename T>
T DebugCpuRead(uint16_t seg, uint16_t offset);
template<typename T>
__forceinline T Read(uint16_t seg, uint16_t offset, MemoryOperationType opType = MemoryOperationType::Read)
{
uint32_t addr = ((seg << 4) + offset) & 0xFFFFF;
if constexpr(std::is_same<T, uint16_t>::value) {
bool splitReads = !IsWordBus(addr) || (addr & 0x01);
if(splitReads) {
uint8_t lo = Read<uint8_t>(seg, offset);
uint8_t hi = Read<uint8_t>(seg, offset + 1);
return lo | (hi << 8);
} else {
Exec();
uint8_t lo = InternalRead(addr);
uint8_t hi = InternalRead(((seg << 4) + (uint16_t)(offset + 1)) & 0xFFFFF);
uint16_t value = lo | (hi << 8);
_emu->ProcessMemoryRead<CpuType::Ws, 2>(addr, value, opType);
return value;
}
} else {
Exec();
uint8_t value = InternalRead(addr);
_emu->ProcessMemoryRead<CpuType::Ws, 1>(addr, value, opType);
return value;
}
}
template<typename T>
__forceinline void Write(uint16_t seg, uint16_t offset, T value, MemoryOperationType opType = MemoryOperationType::Write)
{
uint32_t addr = ((seg << 4) + offset) & 0xFFFFF;
if constexpr(std::is_same<T, uint16_t>::value) {
bool splitWrites = !IsWordBus(addr) || (addr & 0x01);
if(splitWrites) {
Write<uint8_t>(seg, offset, (uint8_t)value);
Write<uint8_t>(seg, offset + 1, (uint8_t)(value >> 8));
} else {
Exec();
_emu->ProcessMemoryWrite<CpuType::Ws, 2>(addr, value, opType);
InternalWrite(addr, (uint8_t)value);
InternalWrite(((seg << 4) + (uint16_t)(offset + 1)) & 0xFFFFF, value >> 8);
}
} else {
Exec();
_emu->ProcessMemoryWrite<CpuType::Ws>(addr, value, opType);
InternalWrite(addr, value);
}
}
template<typename T> T ReadPort(uint16_t port);
template<typename T> void WritePort(uint16_t port, T value);
uint8_t InternalReadPort(uint16_t port, bool isWordAccess);
void InternalWritePort(uint16_t port, uint8_t value, bool isWordAccess);
template<typename T> T DebugReadPort(uint16_t port);
bool IsWordBus(uint32_t addr);
uint8_t GetWaitStates(uint32_t addr);
void SetIrqSource(WsIrqSource src);
void ClearIrqSource(WsIrqSource src);
uint8_t GetActiveIrqs();
uint8_t GetIrqVector();
__forceinline bool HasPendingIrq()
{
return (GetActiveIrqs() & _state.EnabledIrqs) != 0;
}
AddressInfo GetAbsoluteAddress(uint32_t relAddr);
int GetRelativeAddress(AddressInfo& absAddress);
void Serialize(Serializer& s) override;
};
+746
View File
@@ -0,0 +1,746 @@
#include "pch.h"
#include "WS/WsConsole.h"
#include "WS/WsConsole.h"
#include "WS/WsTimer.h"
#include "WS/WsControlManager.h"
#include "WS/WsMemoryManager.h"
#include "WS/APU/WsApu.h"
#include "Shared/EmuSettings.h"
#include "Shared/NotificationManager.h"
#include "Shared/RewindManager.h"
#include "Shared/Video/VideoDecoder.h"
#include "Shared/RenderedFrame.h"
#include "Shared/EventType.h"
#include "Shared/MessageManager.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/Serializer.h"
WsPpu::WsPpu(Emulator* emu, WsConsole* console, WsTimer* timer, uint8_t* vram)
{
_emu = emu;
_console = console;
_timer = timer;
_vram = vram;
_state.LastScanline = 158;
_state.BackPorchScanline = 155;
_state.ShowVolumeIconFrame = UINT32_MAX;
_screenWidth = WsConstants::ScreenWidth;
_screenHeight = WsConstants::ScreenHeight;
if(console->GetModel() == WsModel::Monochrome) {
_screenHeight += 13;
} else {
_screenWidth += 13;
}
_outputBuffers[0] = new uint16_t[WsConstants::MaxPixelCount];
_outputBuffers[1] = new uint16_t[WsConstants::MaxPixelCount];
memset(_outputBuffers[0], 0, WsConstants::MaxPixelCount * sizeof(uint16_t));
memset(_outputBuffers[1], 0, WsConstants::MaxPixelCount * sizeof(uint16_t));
_currentBuffer = _outputBuffers[0];
_showIcons = _emu->GetSettings()->GetWsConfig().LcdShowIcons;
}
WsPpu::~WsPpu()
{
delete[] _outputBuffers[0];
delete[] _outputBuffers[1];
}
void WsPpu::SetVideoMode(WsVideoMode mode)
{
_state.NextMode = mode;
}
void WsPpu::ProcessHblank()
{
_timer->TickHorizontalTimer();
if(_state.Scanline < WsConstants::ScreenHeight) {
switch(_state.Mode) {
case WsVideoMode::Monochrome: DrawScanline<WsVideoMode::Monochrome>(); break;
case WsVideoMode::Color2bpp: DrawScanline<WsVideoMode::Color2bpp>(); break;
case WsVideoMode::Color4bpp: DrawScanline<WsVideoMode::Color4bpp>(); break;
case WsVideoMode::Color4bppPacked: DrawScanline<WsVideoMode::Color4bppPacked>(); break;
}
}
}
void WsPpu::ProcessEndOfScanline()
{
_state.Cycle = 0;
_state.Scanline++;
_state.BgLayers[0].Latch();
_state.BgLayers[1].Latch();
_state.BgWindow.Latch();
_state.SpriteWindow.Latch();
_state.SpritesEnabledLatch = _state.SpritesEnabled;
_state.DrawOutsideBgWindowLatch = _state.DrawOutsideBgWindow;
if(_state.Scanline > _state.LastScanline) {
if(_state.Scanline <= 145) {
//Support sending frame to LCD even when number of scanlines is less than the 144px resolution
SendFrame();
}
_state.Mode = _state.NextMode;
_state.Scanline = 0;
_emu->ProcessEvent(EventType::StartFrame, CpuType::Ws);
_currentBuffer = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0];
_showIcons = _emu->GetSettings()->GetWsConfig().LcdShowIcons;
} else if(_state.Scanline == 145) {
SendFrame();
} else if(_state.Scanline == 144) {
_timer->TickVerticalTimer();
_console->GetMemoryManager()->SetIrqSource(WsIrqSource::VerticalBlank);
}
if(_state.Scanline == _state.IrqScanline) {
_console->GetMemoryManager()->SetIrqSource(WsIrqSource::Scanline);
}
}
template<WsVideoMode mode>
void WsPpu::DrawScanline()
{
uint8_t rowIndex = _state.Scanline & 0x01;
std::fill(_rowData[rowIndex], _rowData[rowIndex] + WsConstants::ScreenWidth, PixelData{});
DrawSprites<mode>();
DrawBackground<mode, 0>();
DrawBackground<mode, 1>();
}
template<WsVideoMode mode>
void WsPpu::DrawSprites()
{
WsConfig& cfg = _emu->GetSettings()->GetWsConfig();
if(!_state.SpritesEnabled || cfg.DisableSprites) {
return;
}
uint16_t scanline = _state.Scanline;
uint8_t rowIndex = _state.Scanline & 0x01;
constexpr int tileSize = mode >= WsVideoMode::Color4bpp ? 32 : 16;
constexpr int tileBytesPerRow = mode >= WsVideoMode::Color4bpp ? 4 : 2;
constexpr int bank0Addr = mode >= WsVideoMode::Color4bpp ? 0x4000 : 0x2000;
int spriteCount = 0;
for(int i = 0; i < _state.SpriteCountLatch; i++) {
uint16_t addr = i * 4;
uint8_t attributes = _spriteRam[addr + 1];
bool highPriority = attributes & 0x20;
uint8_t y = _spriteRam[addr + 2];
uint8_t tileRow = scanline - y;
if(tileRow < 8 && scanline < y + 8) {
spriteCount++;
bool showOutsideWindow = attributes & 0x10;
bool vMirror = attributes & 0x80;
bool hMirror = attributes & 0x40;
uint16_t tileIndex = _spriteRam[addr] | ((attributes & 0x01) << 8);
uint8_t palette = ((attributes >> 1) & 0x07) + 8;
uint8_t sprX = _spriteRam[addr + 3];
if(vMirror) {
tileRow = 7 - tileRow;
}
uint16_t tileDataAddr = (bank0Addr + tileIndex * tileSize + tileRow * tileBytesPerRow);
for(int j = 0; j < 8; j++) {
uint8_t x = sprX + j;
if(x >= 224) {
continue;
}
else if(_rowData[rowIndex][x].Priority > 0) {
continue;
}
else if(_state.SpriteWindow.EnabledLatch && showOutsideWindow == _state.SpriteWindow.IsInsideWindow(x, scanline)) {
//Don't draw this pixel, it's outside/inside the window and should only be drawn on the other side
continue;
}
int tileColumn = j;
if(hMirror) {
tileColumn = 7 - tileColumn;
}
uint8_t color = GetPixelColor<mode>(tileDataAddr, tileColumn);
if(color != 0 || (!(palette & 0x04) && mode <= WsVideoMode::Color2bpp)) {
_rowData[rowIndex][x] = { palette, color, highPriority ? (uint8_t)2 : (uint8_t)1 };
}
}
}
if(spriteCount >= 32) {
break;
}
}
}
template<WsVideoMode mode, int layerIndex>
void WsPpu::DrawBackground()
{
constexpr int tileSize = mode >= WsVideoMode::Color4bpp ? 32 : 16;
constexpr int tileBytesPerRow = mode >= WsVideoMode::Color4bpp ? 4 : 2;
constexpr int bank0Addr = mode >= WsVideoMode::Color4bpp ? 0x4000 : 0x2000;
constexpr int bank1Addr = mode >= WsVideoMode::Color4bpp ? 0x8000 : 0x4000;
WsConfig& cfg = _emu->GetSettings()->GetWsConfig();
uint8_t rowIndex = _state.Scanline & 0x01;
WsBgLayer& layer = _state.BgLayers[layerIndex];
if(cfg.HideBgLayers[layerIndex] || !layer.EnabledLatch) {
return;
}
uint16_t scanline = _state.Scanline;
uint16_t layerAddr = (uint16_t)(layer.MapAddressLatch & (_console->GetModel() == WsModel::Monochrome ? 0x3FFF : 0x7FFF));
for(int cycle = 0; cycle < WsConstants::ScreenWidth; cycle++) {
int y = (scanline + layer.ScrollYLatch) & 0xFF;
int x = (cycle + layer.ScrollXLatch) & 0xFF;
int row = y / 8;
int column = x / 8;
uint16_t tilemapAddr = layerAddr + row * 32 * 2 + column * 2;
uint16_t tilemapData = _vram[tilemapAddr] | (_vram[tilemapAddr + 1] << 8);
int tileRow = y & 0x07;
int tileColumn = x & 0x07;
int counter = 8 - tileColumn;
uint16_t tileIndex = tilemapData & 0x1FF;
uint8_t palette = (tilemapData >> 9) & 0x0F;
bool vMirror = tilemapData & 0x8000;
bool hMirror = tilemapData & 0x4000;
if(hMirror) {
tileColumn = 7 - tileColumn;
}
if(vMirror) {
tileRow = 7 - tileRow;
}
uint16_t tilesetAddr = mode >= WsVideoMode::Color2bpp && (tilemapData & 0x2000) ? bank1Addr : bank0Addr;
uint16_t tileDataAddr = (tilesetAddr + tileIndex * tileSize + tileRow * tileBytesPerRow);
for(int i = cycle, end = std::min<int>(cycle + counter, WsConstants::ScreenWidth); i < end; i++) {
uint8_t color = GetPixelColor<mode>(tileDataAddr, tileColumn);
tileColumn += (hMirror ? -1 : 1);
if(_rowData[rowIndex][i].Priority >= layerIndex + 1) {
continue;
}
if constexpr(layerIndex == 1) {
if(_state.BgWindow.EnabledLatch && _state.DrawOutsideBgWindowLatch == _state.BgWindow.IsInsideWindow(i, scanline)) {
//Pixel is hidden by window
continue;
}
}
//"In two bit per pixel modes: Palettes 0-3 and 8-11 are opaque. For these, index zero is treated as opaque."
if(color != 0 || (!(palette & 0x04) && mode <= WsVideoMode::Color2bpp)) {
_rowData[rowIndex][i] = { palette, color, (uint8_t)(layerIndex + 1) };
}
}
cycle += counter - 1;
}
}
template<WsVideoMode mode>
uint16_t WsPpu::GetPixelColor(uint16_t tileAddr, uint8_t column)
{
switch(mode) {
case WsVideoMode::Monochrome: {
uint8_t tileData = _vram[tileAddr];
uint8_t tileData2 = _vram[tileAddr + 1];
return (
((tileData << column) & 0x80) >> 7 |
((tileData2 << column) & 0x80) >> 6
);
}
case WsVideoMode::Color2bpp: {
uint8_t tileData = _vram[tileAddr];
uint8_t tileData2 = _vram[tileAddr + 1];
return (
((tileData << column) & 0x80) >> 7 |
((tileData2 << column) & 0x80) >> 6
);
}
case WsVideoMode::Color4bpp: {
uint8_t tileData = _vram[tileAddr];
uint8_t tileData2 = _vram[tileAddr + 1];
uint8_t tileData3 = _vram[tileAddr + 2];
uint8_t tileData4 = _vram[tileAddr + 3];
return (
((tileData << column) & 0x80) >> 7 |
((tileData2 << column) & 0x80) >> 6 |
((tileData3 << column) & 0x80) >> 5 |
((tileData4 << column) & 0x80) >> 4
);
}
case WsVideoMode::Color4bppPacked: {
return (_vram[tileAddr + column / 2] >> (column & 0x01 ? 0 : 4)) & 0x0F;
}
}
return 0;
}
void WsPpu::ProcessSpriteCopy()
{
if(_state.Cycle == 0) {
_state.SpriteCountLatch = _state.SpriteCount;
}
uint16_t baseAddr = _state.SpriteTableAddress & (_console->GetModel() == WsModel::Monochrome ? 0x3FFF : 0x7FFF);
int i = _state.Cycle << 1;
_spriteRam[i] = _vram[baseAddr + (((_state.FirstSpriteIndex * 4) + i) & 0x1FF)];
_spriteRam[i+1] = _vram[baseAddr + (((_state.FirstSpriteIndex * 4) + i + 1) & 0x1FF)];
}
void WsPpu::DrawIcons()
{
//11x11 1bpp icons (same shapes as ares)
static constexpr uint16_t power[11] = { 0x70, 0xD8, 0x18C, 0x18C, 0x3DE, 0x3FE, 0x3FE, 0x1FC, 0x1FC, 0xF8, 0x70 };
//static constexpr uint16_t initialized[11] = { 0, 0x3FE, 0x7FF, 0x603, 0x603, 0x603, 0x7FF, 0x7FF, 0x7FF, 0x3FE, 0 };
static constexpr uint16_t sleep[11] = { 0x80, 0x250, 0x11B, 0x1F, 0x3E, 0xFC, 0x3C, 0x1E, 0x3E, 0x33, 0 };
//static constexpr uint16_t lowBattery[11] = { 0x70, 0xD8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x98, 0xB8, 0x88, 0xF8 };
static constexpr uint16_t volumeWsOff[11] = { 0, 0x10, 0x18, 0x1C, 0x1E, 0x1E, 0x1E, 0x1C, 0x18, 0x10, 0 };
static constexpr uint16_t volumeWsLow[11] = { 0, 0x10, 0x18, 0x5C, 0x9E, 0x9E, 0x9E, 0x5C, 0x18, 0x10, 0 };
static constexpr uint16_t volumeWsHigh[11] = { 0, 0x90, 0x118, 0x25C, 0x29E, 0x29E, 0x29E, 0x25C, 0x118, 0x90, 0 };
static constexpr uint16_t volumeWscOff[11] = { 0, 0, 0, 0, 0, 0, 0, 0x3FE, 0x1FC, 0xF8, 0x70 };
static constexpr uint16_t volumeWscLow[11] = { 0, 0, 0, 0xF8, 0x104, 0, 0, 0x3FE, 0x1FC, 0xF8, 0x70 };
static constexpr uint16_t volumeWscMed[11] = { 0x1FC, 0x202, 0x401, 0, 0x70, 0x88, 0, 0x3FE, 0x1FC, 0xF8, 0x70 };
static constexpr uint16_t volumeWscHigh[11] = { 0x1FC, 0x202, 0x4F9, 0x104, 0x272, 0x88, 0, 0x3FE, 0x1FC, 0xF8, 0x70 };
static constexpr uint16_t headphones[11] = { 0x70, 0x18C, 0x306, 0x20B, 0x41F, 0x40E, 0x447, 0x2E2, 0x370, 0x1F8, 0xD0 };
static constexpr uint16_t verticalIcon[11] = { 0x6, 0x1E, 0x38, 0x3BF, 0x7FF, 0x7FF, 0x7FF, 0x3BF, 0x38, 0x1E, 0x6 };
static constexpr uint16_t horizontalIcon[11] = { 0x70, 0xF8, 0xF8, 0xF8, 0x70, 0x1FC, 0x3FE, 0x3FE, 0x6FB, 0x6FB, 0xF8 };
static constexpr uint16_t aux3[11] = { 0, 0x70, 0x1FC, 0x1FC, 0x3FE, 0x3FE, 0x3FE, 0x1FC, 0x1FC, 0x70, 0 };
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) {
uint16_t* start = _currentBuffer + (WsConstants::ScreenWidth * WsConstants::ScreenHeight);
std::fill(start, start + WsConstants::ScreenWidth * 13, 0xFFF);
} else {
for(int i = 0; i < WsConstants::ScreenHeight; i++) {
uint16_t* start = _currentBuffer + (i * _screenWidth) + WsConstants::ScreenWidth;
std::fill(start, start + 13, 0);
}
}
DrawIcon(true, power, 0);
//DrawIcon(true, initialized, 13);
DrawIcon(_state.Icons.Sleep, sleep, 26);
//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) {
DrawIcon(true, headphones, 65);
} else {
if(_console->GetModel() == WsModel::Monochrome) {
switch(_console->GetApu()->GetMasterVolume()) {
default: case 0: DrawIcon(true, volumeWsOff, 52); break;
case 1: DrawIcon(true, volumeWsLow, 52); break;
case 2: DrawIcon(true, volumeWsHigh, 52); break;
}
} else {
switch(_console->GetApu()->GetMasterVolume()) {
default: case 0: DrawIcon(true, volumeWscOff, 52); break;
case 1: DrawIcon(true, volumeWscLow, 52); break;
case 2: DrawIcon(true, volumeWscMed, 52); break;
case 3: DrawIcon(true, volumeWscHigh, 52); break;
}
}
}
}
DrawIcon(_state.Icons.Vertical, verticalIcon, 78);
DrawIcon(_state.Icons.Horizontal, horizontalIcon, 91);
DrawIcon(_state.Icons.Aux3, aux3, 104);
DrawIcon(_state.Icons.Aux2, aux2, 117);
DrawIcon(_state.Icons.Aux1, aux1, 130);
}
void WsPpu::DrawIcon(bool visible, const uint16_t icon[11], uint8_t position)
{
if(!visible) {
return;
}
uint8_t xPos;
uint8_t yPos;
uint16_t color;
if(_console->GetModel() == WsModel::Monochrome) {
xPos = position + 1;
yPos = 145;
color = 0;
} else {
xPos = 225;
yPos = WsConstants::ScreenHeight - 13 - position;
color = 0xFFF;
}
for(int y = 0; y < 11; y++) {
uint16_t basePos = (yPos + y) * _screenWidth + xPos;
for(int x = 0; x < 11; x++) {
if(icon[y] & (1 << x)) {
_currentBuffer[basePos + x] = color;
}
}
}
}
uint16_t WsPpu::GetVisibleScanlineCount()
{
uint16_t scanlineCount = GetScanlineCount();
return scanlineCount <= WsConstants::ScreenHeight ? scanlineCount - 1 : WsConstants::ScreenHeight;
}
uint16_t* WsPpu::GetScreenBuffer(bool prevFrame)
{
return prevFrame ? ((_currentBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0]) : _currentBuffer;
}
void WsPpu::DebugSendFrame()
{
int offset = std::max(0, (int)(_state.Cycle + (_state.Scanline - 1) * _screenWidth));
int pixelsToClear = WsConstants::MaxPixelCount - offset;
if(pixelsToClear > 0) {
memset(_currentBuffer + offset, 0, pixelsToClear * sizeof(uint16_t));
}
uint16_t width = _showIcons ? _screenWidth : WsConstants::ScreenWidth;
uint16_t height = _showIcons ? _screenHeight : WsConstants::ScreenHeight;
RenderedFrame frame(_currentBuffer, width, height, 1.0, _state.FrameCount);
_emu->GetVideoDecoder()->UpdateFrame(frame, false, false);
}
void WsPpu::SetOutputToBgColor()
{
std::fill(_currentBuffer, _currentBuffer + WsConstants::MaxPixelCount, GetBgColor());
}
void WsPpu::ShowVolumeIcon()
{
_state.ShowVolumeIconFrame = _state.FrameCount;
}
uint8_t WsPpu::GetLcdStatus()
{
uint8_t masterVolume = _console->GetApu()->GetMasterVolume();
uint8_t volumeLevel;
if(_console->GetModel() == WsModel::Monochrome) {
switch(masterVolume) {
default: case 0: volumeLevel = 0; break;
case 1: volumeLevel = 2; break;
case 2: volumeLevel = 3; break;
}
} else {
switch(masterVolume) {
default: case 0: volumeLevel = 0; break;
case 1: volumeLevel = 2; break;
case 2: volumeLevel = 1; break;
case 3: volumeLevel = 3; break;
}
}
bool headphone = false;
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) {
headphone = true;
} else {
speaker = true;
}
}
return (
(_state.SleepEnabled ? 0x01 : 0) |
(headphone ? 0x02 : 0) |
(volumeLevel << 2) |
(speaker ? 0x10 : 0)
);
}
void WsPpu::SendFrame()
{
if(_state.SleepEnabled || !_state.LcdEnabled || _state.LastScanline == 255) {
//Screen should be white when in sleep mode, or if the last scanline is set to 255
std::fill(_currentBuffer, _currentBuffer + WsConstants::MaxPixelCount, 0xFFF);
} else if(_state.LastScanline < 144) {
//Clear everything after the last scanline (results in less than 144 visible scanlines)
std::fill(_currentBuffer + _state.LastScanline * _screenWidth, _currentBuffer + WsConstants::MaxPixelCount, 0xFFF);
}
if(_showIcons) {
DrawIcons();
}
_emu->ProcessEvent(EventType::EndFrame, CpuType::Ws);
_state.FrameCount++;
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone);
uint16_t width = _showIcons ? _screenWidth : WsConstants::ScreenWidth;
uint16_t height = _showIcons ? _screenHeight : WsConstants::ScreenHeight;
RenderedFrame frame(_currentBuffer, width, height, 1.0, _state.FrameCount, _console->GetControlManager()->GetPortStates());
bool rewinding = _emu->GetRewindManager()->IsRewinding();
_emu->GetVideoDecoder()->UpdateFrame(frame, rewinding, rewinding);
_emu->ProcessEndOfFrame();
_console->ProcessEndOfFrame();
}
uint8_t WsPpu::ReadPort(uint16_t port)
{
switch(port) {
case 0x00: return _state.Control;
case 0x01: return _state.BgColor & (_console->GetModel() == WsModel::Monochrome ? 0x07 : 0xFF);
case 0x02: return _state.Scanline;
case 0x03: return _state.IrqScanline;
case 0x04: return (_state.SpriteTableAddress >> 9) & (_console->GetModel() == WsModel::Monochrome ? 0x1F : 0x3F);
case 0x05: return _state.FirstSpriteIndex;
case 0x06: return _state.SpriteCount;
case 0x07: return _state.ScreenAddress & (_console->GetModel() == WsModel::Monochrome ? 0x77 : 0xFF);
case 0x08: return _state.BgWindow.Left;
case 0x09: return _state.BgWindow.Top;
case 0x0A: return _state.BgWindow.Right;
case 0x0B: return _state.BgWindow.Bottom;
case 0x0C: return _state.SpriteWindow.Left;
case 0x0D: return _state.SpriteWindow.Top;
case 0x0E: return _state.SpriteWindow.Right;
case 0x0F: return _state.SpriteWindow.Bottom;
case 0x10: return _state.BgLayers[0].ScrollX;
case 0x11: return _state.BgLayers[0].ScrollY;
case 0x12: return _state.BgLayers[1].ScrollX;
case 0x13: return _state.BgLayers[1].ScrollY;
case 0x14:
return (
(_state.LcdEnabled ? 0x01 : 0) |
(_state.HighContrast ? 0x02 : 0)
);
case 0x15: return _state.Icons.Value;
case 0x16: return _state.LastScanline;
case 0x17: return _state.BackPorchScanline;
case 0x1A: return GetLcdStatus();
case 0x1C:
case 0x1D:
case 0x1E:
case 0x1F:
return (
_state.BwShades[(port - 0x1C) * 2] |
(_state.BwShades[(port - 0x1C) * 2 + 1] << 4)
);
default:
if(port >= 0x20 && port <= 0x3F) {
return (
_state.BwPalettes[(port - 0x20) * 2] |
(_state.BwPalettes[(port - 0x20) * 2 + 1] << 4)
);
}
else {
LogDebug("[Debug] PPU Read - missing handler: $" + HexUtilities::ToHex(port));
return 0;
}
}
}
void WsPpu::WritePort(uint16_t port, uint8_t value)
{
switch(port) {
case 0x00:
_state.Control = value & 0x3F;
_state.BgLayers[0].Enabled = value & 0x01;
_state.BgLayers[1].Enabled = value & 0x02;
_state.SpritesEnabled = value & 0x04;
_state.SpriteWindow.Enabled = value & 0x08;
_state.DrawOutsideBgWindow = value & 0x10;
_state.BgWindow.Enabled = value & 0x20;
break;
case 0x01: _state.BgColor = value & (_console->GetModel() == WsModel::Monochrome ? 0x07 : 0xFF); break;
case 0x03: _state.IrqScanline = value; break;
case 0x04: _state.SpriteTableAddress = (value & (_console->GetModel() == WsModel::Monochrome ? 0x1F : 0x3F)) << 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.BgLayers[0].MapAddress = (_state.ScreenAddress & 0x0F) << 11;
_state.BgLayers[1].MapAddress = (_state.ScreenAddress & 0xF0) << 7;
break;
case 0x08: _state.BgWindow.Left = value; break;
case 0x09: _state.BgWindow.Top = value; break;
case 0x0A: _state.BgWindow.Right = value; break;
case 0x0B: _state.BgWindow.Bottom = value; break;
case 0x0C: _state.SpriteWindow.Left = value; break;
case 0x0D: _state.SpriteWindow.Top = value; break;
case 0x0E: _state.SpriteWindow.Right = value; break;
case 0x0F: _state.SpriteWindow.Bottom = value; break;
case 0x10: _state.BgLayers[0].ScrollX = value; break;
case 0x11: _state.BgLayers[0].ScrollY = value; break;
case 0x12: _state.BgLayers[1].ScrollX = value; break;
case 0x13: _state.BgLayers[1].ScrollY = value; break;
case 0x14:
_state.LcdEnabled = value & 0x01;
if(_console->GetModel() == WsModel::Color) {
_state.HighContrast = value & 0x02;
}
break;
case 0x15:
_state.Icons.Sleep = value & 0x01;
_state.Icons.Vertical = value & 0x02;
_state.Icons.Horizontal = value & 0x04;
_state.Icons.Aux1 = value & 0x08;
_state.Icons.Aux2 = value & 0x10;
_state.Icons.Aux3 = value & 0x20;
_state.Icons.Value = value & 0x3F;
break;
case 0x16: _state.LastScanline = value; break;
case 0x17:
if(_console->GetModel() == WsModel::Color) {
_state.BackPorchScanline = value; break;
}
break;
case 0x1A:
_state.SleepEnabled = value & 0x01;
break;
case 0x1C:
case 0x1D:
case 0x1E:
case 0x1F:
_state.BwShades[(port - 0x1C) * 2] = value & 0x0F;
_state.BwShades[(port - 0x1C) * 2 + 1] = (value >> 4) & 0x0F;
break;
default:
if(port >= 0x20 && port <= 0x3F) {
_state.BwPalettes[(port - 0x20) * 2] = value & 0x07;
_state.BwPalettes[(port - 0x20) * 2 + 1] = (value >> 4) & 0x07;
}
else {
LogDebug("[Debug] PPU Write - missing handler: $" + HexUtilities::ToHex(port) + " = " + HexUtilities::ToHex(value));
}
break;
}
}
uint8_t WsPpu::ReadLcdConfigPort(uint16_t port)
{
return _state.LcdTftConfig[port - 0x70];
}
void WsPpu::WriteLcdConfigPort(uint16_t port, uint8_t value)
{
_state.LcdTftConfig[port - 0x70] = value;
}
void WsPpu::Serialize(Serializer& s)
{
SV(_state.FrameCount);
SV(_state.Cycle);
SV(_state.Scanline);
SV(_state.LastScanline);
for(int i = 0; i < 2; i++) {
SVI(_state.BgLayers[i].Enabled);
SVI(_state.BgLayers[i].EnabledLatch);
SVI(_state.BgLayers[i].MapAddress);
SVI(_state.BgLayers[i].MapAddressLatch);
SVI(_state.BgLayers[i].ScrollX);
SVI(_state.BgLayers[i].ScrollXLatch);
SVI(_state.BgLayers[i].ScrollY);
SVI(_state.BgLayers[i].ScrollYLatch);
}
SV(_state.BgWindow.Enabled);
SV(_state.BgWindow.EnabledLatch);
SV(_state.BgWindow.Bottom);
SV(_state.BgWindow.BottomLatch);
SV(_state.BgWindow.Top);
SV(_state.BgWindow.TopLatch);
SV(_state.BgWindow.Left);
SV(_state.BgWindow.LeftLatch);
SV(_state.BgWindow.Right);
SV(_state.BgWindow.RightLatch);
SV(_state.SpriteWindow.Enabled);
SV(_state.SpriteWindow.EnabledLatch);
SV(_state.SpriteWindow.Bottom);
SV(_state.SpriteWindow.BottomLatch);
SV(_state.SpriteWindow.Top);
SV(_state.SpriteWindow.TopLatch);
SV(_state.SpriteWindow.Left);
SV(_state.SpriteWindow.LeftLatch);
SV(_state.SpriteWindow.Right);
SV(_state.SpriteWindow.RightLatch);
SV(_state.DrawOutsideBgWindow);
SV(_state.DrawOutsideBgWindowLatch);
SVArray(_state.BwPalettes, 0x40);
SVArray(_state.BwShades, 8);
SVArray(_spriteRam, 512);
SVArray(_state.LcdTftConfig, 8);
SV(_state.SpriteTableAddress);
SV(_state.FirstSpriteIndex);
SV(_state.SpriteCount);
SV(_state.SpriteCountLatch);
SV(_state.SpritesEnabled);
SV(_state.SpritesEnabledLatch);
SV(_state.Mode);
SV(_state.NextMode);
SV(_state.BgColor);
SV(_state.IrqScanline);
SV(_state.Icons.Sleep);
SV(_state.Icons.Horizontal);
SV(_state.Icons.Vertical);
SV(_state.Icons.Aux1);
SV(_state.Icons.Aux2);
SV(_state.Icons.Aux3);
SV(_state.Icons.Value);
SV(_state.Control);
SV(_state.ScreenAddress);
SV(_state.LcdEnabled);
SV(_state.HighContrast);
SV(_state.SleepEnabled);
SV(_state.ShowVolumeIconFrame);
SV(_state.BackPorchScanline);
}
+144
View File
@@ -0,0 +1,144 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Shared/Emulator.h"
#include "Shared/SettingTypes.h"
#include "Utilities/ISerializable.h"
class Emulator;
class WsTimer;
class WsConsole;
class WsPpu final : public ISerializable
{
private:
WsPpuState _state = {};
Emulator* _emu = nullptr;
WsConsole* _console = nullptr;
WsTimer* _timer = nullptr;
uint16_t* _outputBuffers[2] = {};
uint16_t* _currentBuffer = nullptr;
uint8_t* _vram = nullptr;
uint8_t _spriteRam[512] = {};
struct PixelData
{
uint8_t Palette;
uint8_t Color;
uint8_t Priority;
};
PixelData _rowData[2][224];
uint16_t _screenHeight = 0;
uint16_t _screenWidth = 0;
bool _showIcons = false;
void ProcessEndOfScanline();
void ProcessSpriteCopy();
void DrawIcons();
void DrawIcon(bool visible, const uint16_t icon[11], uint8_t position);
uint8_t GetLcdStatus();
void SendFrame();
template<WsVideoMode mode> void DrawScanline();
template<WsVideoMode mode> void DrawSprites();
template<WsVideoMode mode, int layerIndex> void DrawBackground();
template<WsVideoMode mode> __forceinline uint16_t GetPixelColor(uint16_t tileAddr, uint8_t column);
__forceinline uint16_t GetBgColor()
{
if(_state.Mode == WsVideoMode::Monochrome) {
uint8_t bgBrightness = _state.BwShades[_state.BgColor & 0x07] ^ 0x0F;
return bgBrightness | (bgBrightness << 4) | (bgBrightness << 8);
} else {
return _vram[0xFE00 | (_state.BgColor << 1)] | ((_vram[0xFE00 | (_state.BgColor << 1) | 1] & 0x0F) << 8);
}
}
__forceinline uint16_t GetPixelRgbColor(WsVideoMode mode, uint8_t color, uint8_t palette)
{
switch(mode) {
case WsVideoMode::Monochrome:
{
uint8_t shadeValue = _state.BwPalettes[(palette << 2) | color];
uint8_t brightness = _state.BwShades[shadeValue] ^ 0x0F;
return brightness | (brightness << 4) | (brightness << 8);
}
case WsVideoMode::Color2bpp:
case WsVideoMode::Color4bpp:
case WsVideoMode::Color4bppPacked:
{
uint16_t paletteAddr = 0xFE00 | (palette << 5) | (color << 1);
return _vram[paletteAddr] | ((_vram[paletteAddr + 1] & 0x0F) << 8);
}
}
return 0;
}
void ProcessHblank();
public:
WsPpu(Emulator* emu, WsConsole* console, WsTimer* timer, uint8_t* vram);
~WsPpu();
__forceinline void Exec()
{
if(_state.Scanline == WsConstants::ScreenHeight) {
ProcessSpriteCopy();
}
if(_state.Cycle < 224) {
if(_state.Scanline < WsConstants::ScreenHeight + 1 && _state.Scanline > 0) {
//Palette lookup + output pixel on the first 224 cycles
uint8_t rowIndex = (_state.Scanline & 0x01) ^ 1;
PixelData& data = _rowData[rowIndex][_state.Cycle];
uint16_t screenWidth = _showIcons ? _screenWidth : WsConstants::ScreenWidth;
uint16_t offset = (_state.Scanline - 1) * screenWidth + _state.Cycle;
_currentBuffer[offset] = data.Priority == 0 ? GetBgColor() : GetPixelRgbColor(_state.Mode, data.Color, data.Palette);
}
} else {
if(_state.Cycle == 224) {
ProcessHblank();
} else if(_state.Cycle == 255) {
ProcessEndOfScanline();
_state.Cycle = -1;
}
}
_state.Cycle++;
_emu->ProcessPpuCycle<CpuType::Ws>();
}
void SetVideoMode(WsVideoMode mode);
uint8_t ReadPort(uint16_t port);
void WritePort(uint16_t port, uint8_t value);
uint8_t ReadLcdConfigPort(uint16_t port);
void WriteLcdConfigPort(uint16_t port, uint8_t value);
uint16_t GetCycle() { return _state.Cycle; }
uint16_t GetScanline() { return _state.Scanline; }
uint16_t GetScanlineCount() { return _state.LastScanline + 1; }
uint32_t GetFrameCount() { return _state.FrameCount; }
uint16_t GetScreenWidth() { return _showIcons ? _screenWidth : WsConstants::ScreenWidth; }
uint16_t GetScreenHeight() { return _showIcons ? _screenHeight : WsConstants::ScreenHeight; }
WsPpuState& GetState() { return _state; }
uint16_t GetVisibleScanlineCount();
uint16_t* GetScreenBuffer(bool prevFrame);
void DebugSendFrame();
void SetOutputToBgColor();
void ShowVolumeIcon();
void Serialize(Serializer& s) override;
};
+80
View File
@@ -0,0 +1,80 @@
#include "pch.h"
#include "WS/WsSerial.h"
#include "WS/WsConsole.h"
#include "Utilities/Serializer.h"
WsSerial::WsSerial(WsConsole* console)
{
_console = console;
}
uint8_t WsSerial::Read(uint16_t port)
{
switch(port) {
case 0xB1: return _state.ReceiveBuffer;
case 0xB3:
UpdateState();
return (
(_state.HasReceiveData ? 0x01 : 0) |
(_state.ReceiveOverflow ? 0x02 : 0) |
(_state.HasSendData ? 0 : 0x04) |
(_state.HighSpeed ? 0x40 : 0) |
(_state.Enabled ? 0x80 : 0)
);
}
return 0;
}
void WsSerial::Write(uint16_t port, uint8_t value)
{
switch(port) {
case 0xB1:
_state.SendBuffer = value;
_state.HasSendData = true;
_state.SendClock = _console->GetMasterClock();
break;
case 0xB3:
_state.Enabled = value & 0x80;
_state.HighSpeed = value & 0x40;
if(value & 0x20) {
_state.ReceiveOverflow = false;
}
break;
}
}
void WsSerial::UpdateState()
{
if(_state.HasSendData) {
//At 9600 bauds/s, it takes 3200 master clocks to send 1 byte
//At 38400 (high speed mode), it takes 800 master clocks
int cyclesPerByte = _state.HighSpeed ? 800 : 3200;
uint64_t cyclesElapsed = _console->GetMasterClock() - _state.SendClock;
if(cyclesElapsed > cyclesPerByte) {
_state.HasSendData = false;
}
}
}
bool WsSerial::HasSendIrq()
{
if(_state.HasSendData) {
UpdateState();
}
return _state.Enabled && !_state.HasSendData;
}
void WsSerial::Serialize(Serializer& s)
{
SV(_state.Enabled);
SV(_state.HighSpeed);
SV(_state.HasReceiveData);
SV(_state.HasSendData);
SV(_state.SendBuffer);
SV(_state.SendClock);
SV(_state.ReceiveBuffer);
SV(_state.ReceiveOverflow);
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Utilities/ISerializable.h"
class WsConsole;
class WsSerial final : public ISerializable
{
private:
WsSerialState _state = {};
WsConsole* _console = nullptr;
void UpdateState();
public:
WsSerial(WsConsole* console);
WsSerialState& GetState() { return _state; }
uint8_t Read(uint16_t port);
void Write(uint16_t port, uint8_t value);
bool HasSendIrq();
void Serialize(Serializer& s) override;
};
+98
View File
@@ -0,0 +1,98 @@
#include "pch.h"
#include "WS/WsTimer.h"
#include "WS/WsMemoryManager.h"
#include "Utilities/BitUtilities.h"
void WsTimer::Init(WsMemoryManager* memoryManager)
{
_memoryManager = memoryManager;
}
void WsTimer::TickHorizontalTimer()
{
if(_state.HTimer == 1) {
_memoryManager->SetIrqSource(WsIrqSource::HorizontalBlankTimer);
}
if(_state.HBlankEnabled) {
if(_state.HTimer && !--_state.HTimer && _state.HBlankAutoReload) {
_state.HTimer = _state.HReloadValue;
}
}
}
void WsTimer::TickVerticalTimer()
{
if(_state.VTimer == 1) {
_memoryManager->SetIrqSource(WsIrqSource::VerticalBlankTimer);
}
if(_state.VBlankEnabled) {
if(_state.VTimer && !--_state.VTimer && _state.VBlankAutoReload) {
_state.VTimer = _state.VReloadValue;
}
}
}
uint8_t WsTimer::ReadPort(uint16_t port)
{
switch(port) {
case 0xA2: return _state.Control;
case 0xA4: return BitUtilities::GetBits<0>(_state.HReloadValue); break;
case 0xA5: return BitUtilities::GetBits<8>(_state.HReloadValue); break;
case 0xA6: return BitUtilities::GetBits<0>(_state.VReloadValue); break;
case 0xA7: return BitUtilities::GetBits<8>(_state.VReloadValue); break;
case 0xA8: return BitUtilities::GetBits<0>(_state.HTimer); break;
case 0xA9: return BitUtilities::GetBits<8>(_state.HTimer); break;
case 0xAA: return BitUtilities::GetBits<0>(_state.VTimer); break;
case 0xAB: return BitUtilities::GetBits<8>(_state.VTimer); break;
}
return 0;
}
void WsTimer::WritePort(uint16_t port, uint8_t value)
{
switch(port) {
case 0xA2:
_state.Control = value & 0x0F;
_state.HBlankEnabled = value & 0x01;
_state.HBlankAutoReload = value & 0x02;
_state.VBlankEnabled = value & 0x04;
_state.VBlankAutoReload = value & 0x08;
break;
case 0xA4:
BitUtilities::SetBits<0>(_state.HReloadValue, value);
_state.HTimer = _state.HReloadValue;
break;
case 0xA5:
BitUtilities::SetBits<8>(_state.HReloadValue, value);
_state.HTimer = _state.HReloadValue;
break;
case 0xA6:
BitUtilities::SetBits<0>(_state.VReloadValue, value);
_state.VTimer = _state.VReloadValue;
break;
case 0xA7:
BitUtilities::SetBits<8>(_state.VReloadValue, value);
_state.VTimer = _state.VReloadValue;
break;
}
}
void WsTimer::Serialize(Serializer& s)
{
SV(_state.HTimer);
SV(_state.VTimer);
SV(_state.HReloadValue);
SV(_state.VReloadValue);
SV(_state.Control);
SV(_state.HBlankEnabled);
SV(_state.HBlankAutoReload);
SV(_state.VBlankEnabled);
SV(_state.VBlankAutoReload);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "Utilities/ISerializable.h"
class WsMemoryManager;
class WsTimer final : public ISerializable
{
private:
WsMemoryManager* _memoryManager = nullptr;
WsTimerState _state = {};
public:
void Init(WsMemoryManager* memoryManager);
WsTimerState& GetState() { return _state; }
void TickHorizontalTimer();
void TickVerticalTimer();
uint8_t ReadPort(uint16_t port);
void WritePort(uint16_t port, uint8_t value);
void Serialize(Serializer& s) override;
};
+479
View File
@@ -0,0 +1,479 @@
#pragma once
#include "pch.h"
#include "Shared/BaseState.h"
#include "Shared/SettingTypes.h"
class WsConstants
{
public:
static constexpr uint32_t ScreenWidth = 224;
static constexpr uint32_t ScreenHeight = 144;
static constexpr uint32_t ClocksPerScanline = 256;
static constexpr uint32_t ScanlineCount = 159;
static constexpr uint32_t PixelCount = WsConstants::ScreenWidth * WsConstants::ScreenHeight;
static constexpr uint32_t MaxPixelCount = WsConstants::ScreenWidth * (WsConstants::ScreenHeight + 13);
};
struct WsCpuFlags
{
bool Carry; //0x01
bool Parity; //0x04
bool AuxCarry; //0x10
bool Zero; //0x40
bool Sign; //0x80
bool Trap; //0x100
bool Irq; //0x200
bool Direction; //0x400
bool Overflow; //0x800
bool Mode; //0x8000
uint16_t Get()
{
return (
(uint8_t)Carry |
((uint8_t)Parity << 2) |
((uint8_t)AuxCarry << 4) |
((uint8_t)Zero << 6) |
((uint8_t)Sign << 7) |
((uint8_t)Trap << 8) |
((uint8_t)Irq << 9) |
((uint8_t)Direction << 10) |
((uint8_t)Overflow << 11) |
((uint8_t)Mode << 15) |
0x7002
);
}
void Set(uint16_t f)
{
Carry = f & 0x01;
Parity = f & 0x04;
AuxCarry = f & 0x10;
Zero = f & 0x40;
Sign = f & 0x80;
Trap = f & 0x100;
Irq = f & 0x200;
Direction = f & 0x400;
Overflow = f & 0x800;
Mode = f & 0x8000;
}
};
struct WsCpuState : BaseState
{
uint64_t CycleCount;
uint16_t CS;
uint16_t IP;
uint16_t SS;
uint16_t SP;
uint16_t BP;
uint16_t DS;
uint16_t ES;
uint16_t SI;
uint16_t DI;
uint16_t AX;
uint16_t BX;
uint16_t CX;
uint16_t DX;
WsCpuFlags Flags;
bool Halted;
};
struct WsBgLayer
{
uint16_t MapAddress;
uint16_t MapAddressLatch;
uint8_t ScrollX;
uint8_t ScrollXLatch;
uint8_t ScrollY;
uint8_t ScrollYLatch;
bool Enabled;
bool EnabledLatch;
void Latch()
{
EnabledLatch = Enabled;
ScrollXLatch = ScrollX;
ScrollYLatch = ScrollY;
MapAddressLatch = MapAddress;
}
};
struct WsWindow
{
bool Enabled;
bool EnabledLatch;
uint8_t Left;
uint8_t LeftLatch;
uint8_t Right;
uint8_t RightLatch;
uint8_t Top;
uint8_t TopLatch;
uint8_t Bottom;
uint8_t BottomLatch;
bool IsInsideWindow(uint8_t x, uint8_t y) { return x >= LeftLatch && x <= RightLatch && y >= TopLatch && y <= BottomLatch; }
void Latch()
{
EnabledLatch = Enabled;
LeftLatch = Left;
RightLatch = Right;
TopLatch = Top;
BottomLatch = Bottom;
}
};
struct WsLcdIcons
{
bool Sleep;
bool Vertical;
bool Horizontal;
bool Aux1;
bool Aux2;
bool Aux3;
uint8_t Value;
};
enum class WsVideoMode : uint8_t
{
Monochrome,
Color2bpp,
Color4bpp,
Color4bppPacked
};
struct WsPpuState : BaseState
{
uint32_t FrameCount;
uint16_t Cycle;
uint16_t Scanline;
WsBgLayer BgLayers[2];
WsWindow BgWindow;
WsWindow SpriteWindow;
bool DrawOutsideBgWindow;
bool DrawOutsideBgWindowLatch;
uint8_t BwPalettes[0x20 * 2];
uint8_t BwShades[8];
uint16_t SpriteTableAddress;
uint8_t FirstSpriteIndex;
uint8_t SpriteCount;
uint8_t SpriteCountLatch;
bool SpritesEnabled;
bool SpritesEnabledLatch;
WsVideoMode Mode;
WsVideoMode NextMode;
uint8_t BgColor;
uint8_t IrqScanline;
bool LcdEnabled;
bool HighContrast;
bool SleepEnabled;
WsLcdIcons Icons;
uint8_t LastScanline;
uint8_t BackPorchScanline;
uint32_t ShowVolumeIconFrame;
uint8_t LcdTftConfig[8];
uint8_t Control; //$00
uint8_t ScreenAddress; //$07
};
enum class WsSegment : uint8_t
{
Default,
ES,
SS,
CS,
DS
};
enum class WsIrqSource : uint8_t
{
UartSendReady = 0x01,
KeyPressed = 0x02,
Cart = 0x04,
UartRecvReady = 0x08,
Scanline = 0x10,
VerticalBlankTimer = 0x20,
VerticalBlank = 0x40,
HorizontalBlankTimer = 0x80
};
struct WsMemoryManagerState
{
uint8_t ActiveIrqs;
uint8_t EnabledIrqs;
uint8_t IrqVectorOffset;
uint8_t SystemControl2;
bool ColorEnabled;
bool Enable4bpp;
bool Enable4bppPacked;
bool BootRomDisabled;
bool CartWordBus;
bool SlowRom;
bool SlowSram;
bool SlowPort;
bool EnableLowBatteryNmi;
};
struct WsControlManagerState
{
uint8_t InputSelect;
};
struct WsDmaControllerState
{
uint32_t GdmaSrc;
uint32_t SdmaSrc;
uint32_t SdmaLength;
uint32_t SdmaSrcReloadValue;
uint32_t SdmaLengthReloadValue;
uint16_t GdmaDest;
uint16_t GdmaLength;
uint8_t GdmaControl;
uint8_t SdmaControl;
bool SdmaEnabled;
bool SdmaDecrement;
bool SdmaHyperVoice;
bool SdmaRepeat;
bool SdmaHold;
uint8_t SdmaFrequency;
uint8_t SdmaTimer;
};
struct WsTimerState
{
uint16_t HTimer;
uint16_t VTimer;
uint16_t HReloadValue;
uint16_t VReloadValue;
uint8_t Control;
bool HBlankEnabled;
bool HBlankAutoReload;
bool VBlankEnabled;
bool VBlankAutoReload;
};
struct BaseWsApuState
{
uint16_t Frequency;
uint16_t Timer;
bool Enabled;
uint8_t LeftVolume;
uint8_t RightVolume;
uint8_t SamplePosition;
uint8_t LeftOutput;
uint8_t RightOutput;
void SetVolume(uint8_t value)
{
RightVolume = value & 0x0F;
LeftVolume = (value >> 4);
}
uint8_t GetVolume()
{
return RightVolume | (LeftVolume << 4);
}
};
struct WsApuCh1State : public BaseWsApuState {};
struct WsApuCh2State : public BaseWsApuState
{
bool PcmEnabled;
bool MaxPcmVolumeRight;
bool HalfPcmVolumeRight;
bool MaxPcmVolumeLeft;
bool HalfPcmVolumeLeft;
};
struct WsApuCh3State : public BaseWsApuState
{
uint16_t SweepScaler;
bool SweepEnabled;
int8_t SweepValue;
uint8_t SweepPeriod;
uint8_t SweepTimer;
bool UseSweepCpuClock;
};
struct WsApuCh4State : public BaseWsApuState
{
bool NoiseEnabled;
bool LfsrEnabled;
uint8_t TapMode;
uint8_t TapShift;
uint16_t Lfsr;
uint8_t HoldLfsr;
};
enum class WsHyperVoiceScalingMode : uint8_t
{
Unsigned,
UnsignedNegated,
Signed,
None
};
enum class WsHyperVoiceChannelMode : uint8_t
{
Stereo,
MonoLeft,
MonoRight,
MonoBoth
};
struct WsApuHyperVoiceState
{
int16_t LeftOutput;
int16_t RightOutput;
bool Enabled;
uint8_t LeftSample;
uint8_t RightSample;
bool UpdateRightValue;
uint8_t Divisor;
uint8_t Timer;
uint8_t Input;
uint8_t Shift;
WsHyperVoiceChannelMode ChannelMode;
WsHyperVoiceScalingMode ScalingMode;
uint8_t ControlLow;
uint8_t ControlHigh;
};
struct WsApuState
{
WsApuCh1State Ch1;
WsApuCh2State Ch2;
WsApuCh3State Ch3;
WsApuCh4State Ch4;
WsApuHyperVoiceState Voice;
uint16_t WaveTableAddress;
bool SpeakerEnabled;
uint8_t SpeakerVolume;
uint8_t InternalMasterVolume;
uint8_t MasterVolume;
bool HeadphoneEnabled;
bool HoldChannels;
bool ForceOutput2;
bool ForceOutput4;
bool ForceOutputCh2Voice;
};
struct WsSerialState
{
uint64_t SendClock;
bool Enabled;
bool HighSpeed;
bool ReceiveOverflow;
bool HasReceiveData;
uint8_t ReceiveBuffer;
bool HasSendData;
uint8_t SendBuffer;
};
enum class WsEepromSize : uint16_t
{
Size0 = 0,
Size128 = 0x80,
Size1kb = 0x400,
Size2kb = 0x800
};
struct WsEepromState
{
WsEepromSize Size;
uint16_t ReadBuffer;
uint16_t WriteBuffer;
uint16_t Command;
uint16_t Control;
bool WriteDisabled;
bool ReadDone;
bool Idle;
bool InternalEepromWriteProtected;
};
struct WsCartState
{
uint8_t SelectedBanks[4];
};
struct WsState
{
WsCpuState Cpu;
WsPpuState Ppu;
WsApuState Apu;
WsMemoryManagerState MemoryManager;
WsControlManagerState ControlManager;
WsDmaControllerState DmaController;
WsTimerState Timer;
WsSerialState Serial;
WsEepromState InternalEeprom;
WsCartState Cart;
WsEepromState CartEeprom;
WsModel Model;
};
class WsCpuParityTable
{
private:
uint8_t _parityTable[0x100];
public:
WsCpuParityTable()
{
for(int i = 0; i < 256; i++) {
int count = 0;
for(int j = 0; j < 8; j++) {
count += (i & (1 << j)) ? 1 : 0;
}
_parityTable[i] = count & 0x01 ? 0 : 1;
}
}
__forceinline bool CheckParity(uint8_t val)
{
return _parityTable[val];
}
};
+5
View File
@@ -66,6 +66,11 @@ extern "C" {
_emu->GetSettings()->SetCvConfig(config);
}
DllExport void __stdcall SetWsConfig(WsConfig config)
{
_emu->GetSettings()->SetWsConfig(config);
}
DllExport void __stdcall SetGameConfig(GameConfig config)
{
_emu->GetSettings()->SetGameConfig(config);
-2
View File
@@ -34,7 +34,6 @@ SystemMouseState LinuxMouseManager::GetSystemMouseState(void* rendererHandle)
Window root = 0;
Window c = 0;
Window child = 0;
int rootX, rootY, childX, childY;
uint32_t mask;
@@ -42,7 +41,6 @@ SystemMouseState LinuxMouseManager::GetSystemMouseState(void* rendererHandle)
XQueryPointer(_display, _rootWindow, &root, &c, &rootX, &rootY, &childX, &childY, &mask);
if(root != _rootWindow) c = root;
while(c != 0) {
child = c;
XQueryPointer(_display, c, &root, &c, &rootX, &rootY, &childX, &childY, &mask);
}
XUngrabServer(_display);
+1 -1
View File
@@ -47,7 +47,7 @@ int main(int argc, char* argv[])
romFolder = argv[1];
}
vector<string> testRoms = GetFilesInFolder(romFolder, { ".sfc", ".gb", ".gbc", ".gbx", ".nes", ".pce", ".cue", ".sms", ".gg", ".sg", ".gba", ".col" });
vector<string> testRoms = GetFilesInFolder(romFolder, { ".sfc", ".gb", ".gbc", ".gbx", ".nes", ".pce", ".cue", ".sms", ".gg", ".sg", ".gba", ".col", ".ws", ".wsc" });
PgoRunTest(testRoms, true);
return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

+8
View File
@@ -33,6 +33,7 @@ namespace Mesen.Config
[Reactive] public SmsConfig Sms { get; set; } = new();
[Reactive] public CvConfig Cv { get; set; } = new();
[Reactive] public GbaConfig Gba { get; set; } = new();
[Reactive] public WsConfig Ws { get; set; } = new();
[Reactive] public PreferencesConfig Preferences { get; set; } = new();
[Reactive] public AudioPlayerConfig AudioPlayer { get; set; } = new();
[Reactive] public DebugConfig Debug { get; set; } = new();
@@ -88,6 +89,7 @@ namespace Mesen.Config
Snes.ApplyConfig();
Sms.ApplyConfig();
Cv.ApplyConfig();
Ws.ApplyConfig();
Preferences.ApplyConfig();
AudioPlayer.ApplyConfig();
Debug.ApplyConfig();
@@ -121,6 +123,10 @@ namespace Mesen.Config
Cv.InitializeDefaults(DefaultKeyMappings);
}
if(ConfigUpgrade < (int)ConfigUpgradeHint.WsInput) {
Ws.InitializeDefaults(DefaultKeyMappings);
}
ConfigUpgrade = (int)ConfigUpgradeHint.NextValue - 1;
}
@@ -134,6 +140,7 @@ namespace Mesen.Config
PcEngine.InitializeDefaults(DefaultKeyMappings);
Sms.InitializeDefaults(DefaultKeyMappings);
Cv.InitializeDefaults(DefaultKeyMappings);
Ws.InitializeDefaults(DefaultKeyMappings);
ConfigUpgrade = (int)ConfigUpgradeHint.NextValue - 1;
}
Preferences.InitializeDefaultShortcuts();
@@ -305,6 +312,7 @@ namespace Mesen.Config
SmsInput,
GbaInput,
CvInput,
WsInput,
NextValue,
}
}
+1
View File
@@ -37,6 +37,7 @@ public class ConsoleOverrideConfig : BaseConfig<GameConfig>
return ConfigManager.Config.Sms.ConfigOverrides;
}
case ConsoleType.Gba: return ConfigManager.Config.Gba.ConfigOverrides;
case ConsoleType.Ws: return ConfigManager.Config.Ws.ConfigOverrides;
}
return null;
+1 -36
View File
@@ -54,42 +54,7 @@ public class CvConfig : BaseConfig<CvConfig>
internal void InitializeDefaults(DefaultKeyMappingType defaultMappings)
{
List<CvKeyMapping> mappings = new List<CvKeyMapping>();
if(defaultMappings.HasFlag(DefaultKeyMappingType.Xbox)) {
CvKeyMapping mapping = new();
mapping.ColecoVisionControllerButtons = mapping.GetDefaultCustomKeys(ControllerType.ColecoVisionController, KeyPresetType.XboxP1);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.Ps4)) {
CvKeyMapping mapping = new();
mapping.ColecoVisionControllerButtons = mapping.GetDefaultCustomKeys(ControllerType.ColecoVisionController, KeyPresetType.Ps4P1);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.WasdKeys)) {
CvKeyMapping mapping = new();
mapping.ColecoVisionControllerButtons = mapping.GetDefaultCustomKeys(ControllerType.ColecoVisionController, KeyPresetType.WasdKeys);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.ArrowKeys)) {
CvKeyMapping mapping = new();
mapping.ColecoVisionControllerButtons = mapping.GetDefaultCustomKeys(ControllerType.ColecoVisionController, KeyPresetType.ArrowKeys);
mappings.Add(mapping);
}
Port1.Type = ControllerType.ColecoVisionController;
Port1.TurboSpeed = 2;
if(mappings.Count > 0) {
Port1.Mapping1 = mappings[0];
if(mappings.Count > 1) {
Port1.Mapping2 = mappings[1];
if(mappings.Count > 2) {
Port1.Mapping3 = mappings[2];
if(mappings.Count > 3) {
Port1.Mapping4 = mappings[3];
}
}
}
}
Port1.InitDefaults<CvKeyMapping>(defaultMappings, ControllerType.ColecoVisionController);
}
}
+2 -7
View File
@@ -40,7 +40,7 @@ namespace Mesen.Config
{
UInt16[]? buttonMappings = GetCustomButtons(type);
if(buttonMappings == null) {
if(GetDefaultCustomKeys(type) != null) {
if(GetDefaultCustomKeys(type, null) != null) {
if(mappingIndex == 0) {
SetDefaultKeys(type, null);
} else {
@@ -73,7 +73,7 @@ namespace Mesen.Config
}
}
public UInt16[]? GetDefaultCustomKeys(ControllerType type, KeyPresetType? preset)
public override UInt16[]? GetDefaultCustomKeys(ControllerType type, KeyPresetType? preset)
{
switch(type) {
case ControllerType.ColecoVisionController:
@@ -129,11 +129,6 @@ namespace Mesen.Config
}
}
protected override UInt16[]? GetDefaultCustomKeys(ControllerType type)
{
return GetDefaultCustomKeys(type, null);
}
public override void SetDefaultKeys(ControllerType type, KeyPresetType? preset)
{
switch(type) {
+4
View File
@@ -90,6 +90,8 @@ namespace Mesen.Config
GbaBreakOnNopLoad = Debugger.Gba.BreakOnNopLoad,
GbaBreakOnUnalignedMemAccess = Debugger.Gba.BreakOnUnalignedMemAccess,
WsBreakOnUndefinedOpCode = Debugger.Ws.BreakOnUndefinedOpCode,
ScriptAllowIoOsAccess = ScriptWindow.AllowIoOsAccess,
ScriptAllowNetworkAccess = ScriptWindow.AllowNetworkAccess,
ScriptTimeout = ScriptWindow.ScriptTimeout
@@ -150,6 +152,8 @@ namespace Mesen.Config
[MarshalAs(UnmanagedType.I1)] public bool GbaBreakOnNopLoad;
[MarshalAs(UnmanagedType.I1)] public bool GbaBreakOnInvalidOpCode;
[MarshalAs(UnmanagedType.I1)] public bool GbaBreakOnUnalignedMemAccess;
[MarshalAs(UnmanagedType.I1)] public bool WsBreakOnUndefinedOpCode;
[MarshalAs(UnmanagedType.I1)] public bool ScriptAllowIoOsAccess;
[MarshalAs(UnmanagedType.I1)] public bool ScriptAllowNetworkAccess;
+1
View File
@@ -32,6 +32,7 @@ namespace Mesen.Config
[Reactive] public GbaDebuggerConfig Gba { get; set; } = new();
[Reactive] public PceDebuggerConfig Pce { get; set; } = new();
[Reactive] public SmsDebuggerConfig Sms { get; set; } = new();
[Reactive] public WsDebuggerConfig Ws { get; set; } = new();
[Reactive] public bool BreakOnUninitRead { get; set; } = false;
[Reactive] public bool BreakOnOpen { get; set; } = true;
+58
View File
@@ -0,0 +1,58 @@
using Avalonia.Media;
namespace Mesen.Config;
public static class EventViewerColors
{
public static Color[] Colors => _colors;
private static readonly Color[] _colors = new Color[] {
//Pairs
Color.FromRgb(0x00, 0x6E, 0x8E),
Color.FromRgb(0x78, 0xA5, 0xB2),
Color.FromRgb(0x97, 0x75, 0x00),
Color.FromRgb(0xB7, 0xAA, 0x7B),
Color.FromRgb(0x2D, 0x92, 0x3D),
Color.FromRgb(0x6A, 0xAF, 0x7B),
Color.FromRgb(0x96, 0x45, 0x45),
Color.FromRgb(0xD3, 0x4F, 0x4F),
Color.FromRgb(0xE5, 0xCF, 0x2B),
Color.FromRgb(0xFA, 0xFF, 0x39),
Color.FromRgb(0xD7, 0x2C, 0x2F),
Color.FromRgb(0xFF, 0x16, 0x1A),
Color.FromRgb(0xF5, 0xFF, 0x72),
Color.FromRgb(0xFD, 0xFF, 0xB3),
Color.FromRgb(0x41, 0xCE, 0xDB),
Color.FromRgb(0xC9, 0xFF, 0xFD),
Color.FromRgb(0x9D, 0x6D, 0xC2),
Color.FromRgb(0x84, 0x73, 0xC0),
Color.FromRgb(0x5D, 0xE3, 0x3C),
Color.FromRgb(0xC4, 0xF4, 0x7A),
Color.FromRgb(0xC7, 0x80, 0x00),
Color.FromRgb(0xFF, 0x8C, 0x00),
Color.FromRgb(0xBE, 0x52, 0xC0),
Color.FromRgb(0xFC, 0x5E, 0xC6),
Color.FromRgb(0x00, 0xFF, 0x7F),
Color.FromRgb(0x1A, 0xB0, 0x3D),
Color.FromRgb(0x9F, 0xCC, 0xA4),
Color.FromRgb(0xC7, 0xFF, 0xC4),
//Single colors
Color.FromRgb(0xFF, 0xA5, 0x62),
Color.FromRgb(0x03, 0x35, 0xFF),
Color.FromRgb(0x00, 0x8C, 0xBB),
};
}
+1
View File
@@ -26,5 +26,6 @@ namespace Mesen.Config
[Reactive] public GbaEventViewerConfig GbaConfig { get; set; } = new GbaEventViewerConfig();
[Reactive] public PceEventViewerConfig PceConfig { get; set; } = new PceEventViewerConfig();
[Reactive] public SmsEventViewerConfig SmsConfig { get; set; } = new SmsEventViewerConfig();
[Reactive] public WsEventViewerConfig WsConfig { get; set; } = new WsEventViewerConfig();
}
}
+2
View File
@@ -32,6 +32,7 @@ namespace Mesen.Config
[Reactive] public TraceLoggerCpuConfig PceConfig { get; set; } = new();
[Reactive] public TraceLoggerCpuConfig SmsConfig { get; set; } = new();
[Reactive] public TraceLoggerCpuConfig GbaConfig { get; set; } = new();
[Reactive] public TraceLoggerCpuConfig WsConfig { get; set; } = new();
public TraceLoggerConfig()
{
@@ -51,6 +52,7 @@ namespace Mesen.Config
CpuType.Pce => PceConfig,
CpuType.Sms => SmsConfig,
CpuType.Gba => GbaConfig,
CpuType.Ws => WsConfig,
_ => throw new NotImplementedException("Unsupport cpu type")
};
}
+16
View File
@@ -0,0 +1,16 @@
using Avalonia;
using Avalonia.Media;
using Mesen.Debugger;
using Mesen.Interop;
using ReactiveUI.Fody.Helpers;
using System.Reactive.Linq;
using System.Reactive;
using Mesen.ViewModels;
namespace Mesen.Config
{
public class WsDebuggerConfig : ViewModelBase
{
[Reactive] public bool BreakOnUndefinedOpCode { get; set; } = false;
}
}
+95
View File
@@ -0,0 +1,95 @@
using Mesen.Interop;
using Mesen.ViewModels;
using ReactiveUI.Fody.Helpers;
namespace Mesen.Config;
public class WsEventViewerConfig : ViewModelBase
{
[Reactive] public EventViewerCategoryCfg PpuPaletteRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[0]);
[Reactive] public EventViewerCategoryCfg PpuPaletteWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[1]);
[Reactive] public EventViewerCategoryCfg PpuVramRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[2]);
[Reactive] public EventViewerCategoryCfg PpuVramWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[3]);
[Reactive] public EventViewerCategoryCfg PpuScrollRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[4]);
[Reactive] public EventViewerCategoryCfg PpuScrollWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[5]);
[Reactive] public EventViewerCategoryCfg PpuWindowRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[6]);
[Reactive] public EventViewerCategoryCfg PpuWindowWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[7]);
[Reactive] public EventViewerCategoryCfg PpuOtherRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[8]);
[Reactive] public EventViewerCategoryCfg PpuOtherWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[9]);
[Reactive] public EventViewerCategoryCfg AudioRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[10]);
[Reactive] public EventViewerCategoryCfg AudioWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[11]);
[Reactive] public EventViewerCategoryCfg SerialRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[12]);
[Reactive] public EventViewerCategoryCfg SerialWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[13]);
[Reactive] public EventViewerCategoryCfg DmaRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[14]);
[Reactive] public EventViewerCategoryCfg DmaWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[15]);
[Reactive] public EventViewerCategoryCfg InputRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[16]);
[Reactive] public EventViewerCategoryCfg InputWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[17]);
[Reactive] public EventViewerCategoryCfg IrqRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[18]);
[Reactive] public EventViewerCategoryCfg IrqWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[19]);
[Reactive] public EventViewerCategoryCfg TimerRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[20]);
[Reactive] public EventViewerCategoryCfg TimerWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[21]);
[Reactive] public EventViewerCategoryCfg EepromRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[22]);
[Reactive] public EventViewerCategoryCfg EepromWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[23]);
[Reactive] public EventViewerCategoryCfg CartRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[24]);
[Reactive] public EventViewerCategoryCfg CartWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[25]);
[Reactive] public EventViewerCategoryCfg OtherRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[26]);
[Reactive] public EventViewerCategoryCfg OtherWrite { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[27]);
[Reactive] public EventViewerCategoryCfg PpuVCounterRead { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[28]);
[Reactive] public EventViewerCategoryCfg Irq { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[29]);
[Reactive] public EventViewerCategoryCfg MarkedBreakpoints { get; set; } = new EventViewerCategoryCfg(EventViewerColors.Colors[30]);
[Reactive] public bool ShowPreviousFrameEvents { get; set; } = true;
public InteropWsEventViewerConfig ToInterop()
{
return new InteropWsEventViewerConfig() {
PpuPaletteRead = this.PpuPaletteRead,
PpuPaletteWrite = this.PpuPaletteWrite,
PpuVramRead = this.PpuVramRead,
PpuVramWrite = this.PpuVramWrite,
PpuVCounterRead = this.PpuVCounterRead,
PpuScrollRead = this.PpuScrollRead,
PpuScrollWrite = this.PpuScrollWrite,
PpuWindowRead = this.PpuWindowRead,
PpuWindowWrite = this.PpuWindowWrite,
PpuOtherRead = this.PpuOtherRead,
PpuOtherWrite = this.PpuOtherWrite,
AudioRead = this.AudioRead,
AudioWrite = this.AudioWrite,
SerialRead = this.SerialRead,
SerialWrite = this.SerialWrite,
DmaRead = this.DmaRead,
DmaWrite = this.DmaWrite,
InputRead = this.InputRead,
InputWrite = this.InputWrite,
IrqRead = this.IrqRead,
IrqWrite = this.IrqWrite,
TimerRead = this.TimerRead,
TimerWrite = this.TimerWrite,
EepromRead = this.EepromRead,
EepromWrite = this.EepromWrite,
CartRead = this.CartRead,
CartWrite = this.CartWrite,
OtherRead = this.OtherRead,
OtherWrite = this.OtherWrite,
Irq = this.Irq,
MarkedBreakpoints = this.MarkedBreakpoints,
ShowPreviousFrameEvents = this.ShowPreviousFrameEvents
};
}
}
+6
View File
@@ -90,6 +90,9 @@ namespace Mesen.Config
CreateMimeType("x-mesen-gg", "gg", "Game Gear ROM", mimeTypes, cfg.AssociateGameGearRomFiles);
CreateMimeType("x-mesen-sg", "sg", "SG-1000 ROM", mimeTypes, cfg.AssociateSgRomFiles);
CreateMimeType("x-mesen-col", "col", "ColecoVision ROM", mimeTypes, cfg.AssociateCvRomFiles);
CreateMimeType("x-mesen-ws", "ws", "WonderSwan ROM", mimeTypes, cfg.AssociateWsRomFiles);
CreateMimeType("x-mesen-wsc", "wsc", "WonderSwan Color ROM", mimeTypes, cfg.AssociateWsRomFiles);
//Icon used for shortcuts
ImageUtilities.BitmapFromAsset("Assets/MesenIcon.png").Save(Path.Combine(iconFolder, "MesenIcon.png"));
@@ -209,6 +212,9 @@ namespace Mesen.Config
FileAssociationHelper.UpdateFileAssociation("gg", cfg.AssociateGameGearRomFiles);
FileAssociationHelper.UpdateFileAssociation("sg", cfg.AssociateSgRomFiles);
FileAssociationHelper.UpdateFileAssociation("col", cfg.AssociateCvRomFiles);
FileAssociationHelper.UpdateFileAssociation("ws", cfg.AssociateWsRomFiles);
FileAssociationHelper.UpdateFileAssociation("wsc", cfg.AssociateWsRomFiles);
}
static private void UpdateFileAssociation(string extension, bool associate)
+1 -36
View File
@@ -68,42 +68,7 @@ namespace Mesen.Config
internal void InitializeDefaults(DefaultKeyMappingType defaultMappings)
{
List<KeyMapping> mappings = new List<KeyMapping>();
if(defaultMappings.HasFlag(DefaultKeyMappingType.Xbox)) {
KeyMapping mapping = new();
KeyPresets.ApplyXboxLayout(mapping, 0, ControllerType.GameboyController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.Ps4)) {
KeyMapping mapping = new();
KeyPresets.ApplyPs4Layout(mapping, 0, ControllerType.GameboyController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.WasdKeys)) {
KeyMapping mapping = new();
KeyPresets.ApplyWasdLayout(mapping, ControllerType.GameboyController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.ArrowKeys)) {
KeyMapping mapping = new();
KeyPresets.ApplyArrowLayout(mapping, ControllerType.GameboyController);
mappings.Add(mapping);
}
Controller.Type = ControllerType.GameboyController;
Controller.TurboSpeed = 2;
if(mappings.Count > 0) {
Controller.Mapping1 = mappings[0];
if(mappings.Count > 1) {
Controller.Mapping2 = mappings[1];
if(mappings.Count > 2) {
Controller.Mapping3 = mappings[2];
if(mappings.Count > 3) {
Controller.Mapping4 = mappings[3];
}
}
}
}
Controller.InitDefaults(defaultMappings, ControllerType.GameboyController);
}
}
+1 -36
View File
@@ -71,42 +71,7 @@ namespace Mesen.Config
internal void InitializeDefaults(DefaultKeyMappingType defaultMappings)
{
List<KeyMapping> mappings = new List<KeyMapping>();
if(defaultMappings.HasFlag(DefaultKeyMappingType.Xbox)) {
KeyMapping mapping = new();
KeyPresets.ApplyXboxLayout(mapping, 0, ControllerType.GbaController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.Ps4)) {
KeyMapping mapping = new();
KeyPresets.ApplyPs4Layout(mapping, 0, ControllerType.GbaController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.WasdKeys)) {
KeyMapping mapping = new();
KeyPresets.ApplyWasdLayout(mapping, ControllerType.GbaController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.ArrowKeys)) {
KeyMapping mapping = new();
KeyPresets.ApplyArrowLayout(mapping, ControllerType.GbaController);
mappings.Add(mapping);
}
Controller.Type = ControllerType.GbaController;
Controller.TurboSpeed = 2;
if(mappings.Count > 0) {
Controller.Mapping1 = mappings[0];
if(mappings.Count > 1) {
Controller.Mapping2 = mappings[1];
if(mappings.Count > 2) {
Controller.Mapping3 = mappings[2];
if(mappings.Count > 3) {
Controller.Mapping4 = mappings[3];
}
}
}
}
Controller.InitDefaults(defaultMappings, ControllerType.GbaController);
}
}

Some files were not shown because too many files have changed in this diff Show More