Unfinished visual debugger implementation (to be finished later)
This commit is contained in:
parent
366d4aec80
commit
f8f9755eff
29 changed files with 1714 additions and 33 deletions
|
@ -51,14 +51,14 @@ class BaseMapper : public IMemoryHandler, public Snapshotable
|
|||
void SelectPRGPage(uint32_t slot, uint32_t page)
|
||||
{
|
||||
//std::cout << std::dec << "PRG Slot " << (short)slot << ": " << (short)(page & (GetPRGPageCount() - 1)) << std::endl;
|
||||
_prgPages[slot] = &_prgRAM[(page & (GetPRGPageCount() - 1)) * GetPRGPageSize()];
|
||||
_prgPages[slot] = &_prgRAM[(page % GetPRGPageCount()) * GetPRGPageSize()];
|
||||
_prgSlotPages[slot] = page;
|
||||
}
|
||||
|
||||
void SelectCHRPage(uint32_t slot, uint32_t page)
|
||||
{
|
||||
//std::cout << std::dec << "CHR Slot " << (short)slot << ": " << (short)page << std::endl;
|
||||
_chrPages[slot] = &_chrRAM[(page & (GetCHRPageCount() - 1)) * GetCHRPageSize()];
|
||||
_chrPages[slot] = &_chrRAM[(page % GetCHRPageCount()) * GetCHRPageSize()];
|
||||
_chrSlotPages[slot] = page;
|
||||
}
|
||||
|
||||
|
@ -254,6 +254,53 @@ class BaseMapper : public IMemoryHandler, public Snapshotable
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* GetPRGCopy()
|
||||
{
|
||||
uint8_t* prgCopy = new uint8_t[_prgSize];
|
||||
memcpy(prgCopy, _prgRAM, _prgSize);
|
||||
return prgCopy;
|
||||
}
|
||||
|
||||
uint32_t GetPRGSize()
|
||||
{
|
||||
return _prgSize;
|
||||
}
|
||||
|
||||
uint32_t ToAbsoluteAddress(uint16_t addr)
|
||||
{
|
||||
return GetPRGPageSize() * (_prgSlotPages[AddrToPRGSlot(addr)] % GetPRGPageCount()) + (addr & (GetPRGPageSize() - 1));
|
||||
}
|
||||
|
||||
int32_t FromAbsoluteAddress(uint32_t addr)
|
||||
{
|
||||
uint32_t page = addr / GetPRGPageSize();
|
||||
for(int i = 0, len = GetPRGSlotCount(); i < len; i++) {
|
||||
if((_prgSlotPages[i] % GetPRGPageCount()) == page) {
|
||||
uint32_t offset = addr - (page * GetPRGPageSize());
|
||||
return GetPRGPageSize() * i + offset + 0x8000;
|
||||
}
|
||||
}
|
||||
|
||||
//Address is currently not mapped
|
||||
return -1;
|
||||
}
|
||||
|
||||
vector<uint32_t> GetPRGRanges()
|
||||
{
|
||||
vector<uint32_t> memoryRanges;
|
||||
uint32_t slotCount = GetPRGSlotCount();
|
||||
|
||||
for(uint32_t i = 0; i < slotCount; i++) {
|
||||
uint32_t page = _prgSlotPages[i] % GetPRGPageCount();
|
||||
uint32_t pageStart = page * GetPRGPageSize();
|
||||
uint32_t pageEnd = (page + 1) * GetPRGPageSize();
|
||||
memoryRanges.push_back(pageStart);
|
||||
memoryRanges.push_back(pageEnd);
|
||||
}
|
||||
|
||||
return memoryRanges;
|
||||
}
|
||||
|
||||
virtual uint16_t RegisterStartAddress() { return 0x8000; }
|
||||
virtual uint16_t RegisterEndAddress() { return 0xFFFF; }
|
||||
virtual void WriteRegister(uint16_t addr, uint8_t value) { }
|
||||
|
|
12
Core/Breakpoint.cpp
Normal file
12
Core/Breakpoint.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Breakpoint.h"
|
||||
|
||||
Breakpoint::Breakpoint(BreakpointType type, uint32_t addr, bool isAbsoluteAddr)
|
||||
{
|
||||
UpdateBreakpoint(type, addr, isAbsoluteAddr, true);
|
||||
}
|
||||
|
||||
Breakpoint::~Breakpoint()
|
||||
{
|
||||
}
|
70
Core/Breakpoint.h
Normal file
70
Core/Breakpoint.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
enum class BreakpointType
|
||||
{
|
||||
Execute = 0,
|
||||
Read = 1,
|
||||
Write = 2
|
||||
};
|
||||
|
||||
class Breakpoint
|
||||
{
|
||||
public:
|
||||
Breakpoint(BreakpointType type, uint32_t addr, bool isAbsoluteAddr);
|
||||
~Breakpoint();
|
||||
|
||||
bool Breakpoint::Matches(uint32_t memoryAddr, uint32_t absoluteAddr)
|
||||
{
|
||||
return _enabled && ((memoryAddr == _addr && !_isAbsoluteAddr) || (absoluteAddr == _addr && _isAbsoluteAddr));
|
||||
}
|
||||
|
||||
uint32_t GetAddr()
|
||||
{
|
||||
return _addr;
|
||||
}
|
||||
|
||||
bool IsAbsoluteAddr()
|
||||
{
|
||||
return _isAbsoluteAddr;
|
||||
}
|
||||
|
||||
BreakpointType GetType()
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
wstring GetTypeText()
|
||||
{
|
||||
switch(_type) {
|
||||
case BreakpointType::Execute: return L"Exec";
|
||||
case BreakpointType::Read: return L"Read";
|
||||
case BreakpointType::Write: return L"Write";
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
|
||||
bool IsEnabled()
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
void SetEnabled(bool enabled)
|
||||
{
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
||||
void UpdateBreakpoint(BreakpointType type, uint32_t addr, bool isAbsoluteAddr, bool enabled)
|
||||
{
|
||||
_type = type;
|
||||
_addr = addr;
|
||||
_isAbsoluteAddr = isAbsoluteAddr;
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
BreakpointType _type;
|
||||
uint32_t _addr;
|
||||
bool _isAbsoluteAddr;
|
||||
bool _enabled;
|
||||
};
|
|
@ -32,7 +32,7 @@ CPU::CPU(MemoryManager *memoryManager) : _memoryManager(memoryManager)
|
|||
AddrMode addrMode[] = {
|
||||
Imm, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
None, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Abs, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
|
@ -120,7 +120,7 @@ uint32_t CPU::Exec()
|
|||
//static ofstream log("log.txt", ios::out | ios::binary);
|
||||
uint32_t executedCycles = 0;
|
||||
if(!_runNMI && !_runIRQ) {
|
||||
uint8_t opCode = ReadByte();
|
||||
uint8_t opCode = GetOPCode();
|
||||
|
||||
if(CPU::NMIFlag) {
|
||||
_runNMI = true;
|
||||
|
|
22
Core/CPU.h
22
Core/CPU.h
|
@ -21,9 +21,9 @@ namespace PSFlags
|
|||
|
||||
enum AddrMode
|
||||
{
|
||||
None, Imp, Imm, Rel,
|
||||
None, Acc, Imp, Imm, Rel,
|
||||
Zero, ZeroX, ZeroY,
|
||||
IndX, IndY, IndYW,
|
||||
Ind, IndX, IndY, IndYW,
|
||||
Abs, AbsX, AbsXW, AbsY, AbsYW
|
||||
};
|
||||
|
||||
|
@ -72,6 +72,13 @@ private:
|
|||
bool _runNMI = false;
|
||||
bool _runIRQ = false;
|
||||
|
||||
uint8_t GetOPCode()
|
||||
{
|
||||
uint8_t value = _memoryManager->Read(_state.PC, true);
|
||||
_state.PC++;
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t ReadByte()
|
||||
{
|
||||
return MemoryRead(_state.PC++);
|
||||
|
@ -205,19 +212,11 @@ private:
|
|||
}
|
||||
|
||||
uint8_t GetImmediate() { return ReadByte(); }
|
||||
uint8_t GetZero() { return MemoryRead(GetZeroAddr()); }
|
||||
uint8_t GetZeroAddr() { return ReadByte(); }
|
||||
|
||||
uint8_t GetZeroX() { return MemoryRead(GetZeroXAddr()); }
|
||||
uint8_t GetZeroXAddr() { return ReadByte() + X(); }
|
||||
|
||||
uint8_t GetZeroY() { return MemoryRead(GetZeroYAddr()); }
|
||||
uint8_t GetZeroYAddr() { return ReadByte() + Y(); }
|
||||
|
||||
uint8_t GetAbs() { return MemoryRead(GetAbsAddr()); }
|
||||
uint16_t GetAbsAddr() { return ReadWord(); }
|
||||
|
||||
uint8_t GetAbsX() { return MemoryRead(GetAbsXAddr(false)); }
|
||||
uint16_t GetAbsXAddr(bool dummyRead = true) {
|
||||
uint16_t baseAddr = ReadWord();
|
||||
bool pageCrossed = CheckPageCrossed(baseAddr, X());
|
||||
|
@ -229,7 +228,6 @@ private:
|
|||
return baseAddr + X();
|
||||
}
|
||||
|
||||
uint8_t GetAbsY() { return MemoryRead(GetAbsYAddr(false)); }
|
||||
uint16_t GetAbsYAddr(bool dummyRead = true) {
|
||||
uint16_t baseAddr = ReadWord();
|
||||
bool pageCrossed = CheckPageCrossed(baseAddr, Y());
|
||||
|
@ -253,7 +251,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t GetIndX() { return MemoryRead(GetIndXAddr()); }
|
||||
uint16_t GetIndXAddr() {
|
||||
uint8_t zero = ReadByte();
|
||||
|
||||
|
@ -271,7 +268,6 @@ private:
|
|||
return addr;
|
||||
}
|
||||
|
||||
uint8_t GetIndY() { return MemoryRead(GetIndYAddr(false)); }
|
||||
uint16_t GetIndYAddr(bool dummyRead = true) {
|
||||
uint8_t zero = ReadByte();
|
||||
|
||||
|
|
|
@ -338,6 +338,11 @@ void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
|
|||
LoadState(stream);
|
||||
}
|
||||
|
||||
shared_ptr<Debugger> Console::GetDebugger()
|
||||
{
|
||||
return shared_ptr<Debugger>(new Debugger(shared_ptr<Console>(this), _cpu, _memoryManager, _mapper));
|
||||
}
|
||||
|
||||
bool Console::RunTest(uint8_t *expectedResult)
|
||||
{
|
||||
Timer timer;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "BaseMapper.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "ControlManager.h"
|
||||
#include "Debugger.h"
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
#include "IMessageManager.h"
|
||||
#include "INotificationListener.h"
|
||||
|
@ -28,12 +29,12 @@ class Console
|
|||
static IMessageManager* MessageManager;
|
||||
static list<INotificationListener*> NotificationListeners;
|
||||
|
||||
unique_ptr<CPU> _cpu;
|
||||
shared_ptr<CPU> _cpu;
|
||||
unique_ptr<PPU> _ppu;
|
||||
unique_ptr<APU> _apu;
|
||||
shared_ptr<BaseMapper> _mapper;
|
||||
unique_ptr<ControlManager> _controlManager;
|
||||
unique_ptr<MemoryManager> _memoryManager;
|
||||
shared_ptr<MemoryManager> _memoryManager;
|
||||
|
||||
wstring _romFilepath;
|
||||
|
||||
|
@ -59,6 +60,8 @@ class Console
|
|||
bool RunTest(uint8_t* expectedResult);
|
||||
void SaveTestResult();
|
||||
|
||||
shared_ptr<Debugger> Console::GetDebugger();
|
||||
|
||||
static void SaveState(wstring filename);
|
||||
static void SaveState(ostream &saveStream);
|
||||
static bool LoadState(wstring filename);
|
||||
|
|
|
@ -92,9 +92,13 @@
|
|||
<ClInclude Include="APU.h" />
|
||||
<ClInclude Include="AXROM.h" />
|
||||
<ClInclude Include="BaseMapper.h" />
|
||||
<ClInclude Include="Breakpoint.h" />
|
||||
<ClInclude Include="CNROM.h" />
|
||||
<ClInclude Include="ColorDreams.h" />
|
||||
<ClInclude Include="ControlManager.h" />
|
||||
<ClInclude Include="Debugger.h" />
|
||||
<ClInclude Include="Disassembler.h" />
|
||||
<ClInclude Include="DisassemblyInfo.h" />
|
||||
<ClInclude Include="GameClient.h" />
|
||||
<ClInclude Include="GameClientConnection.h" />
|
||||
<ClInclude Include="GameConnection.h" />
|
||||
|
@ -146,8 +150,12 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="APU.cpp" />
|
||||
<ClCompile Include="Breakpoint.cpp" />
|
||||
<ClCompile Include="Console.cpp" />
|
||||
<ClCompile Include="ControlManager.cpp" />
|
||||
<ClCompile Include="Debugger.cpp" />
|
||||
<ClCompile Include="Disassembler.cpp" />
|
||||
<ClCompile Include="DisassemblyInfo.cpp" />
|
||||
<ClCompile Include="GameClient.cpp" />
|
||||
<ClCompile Include="GameClientConnection.cpp" />
|
||||
<ClCompile Include="GameConnection.cpp" />
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
<Filter Include="Header Files\NetPlay\Messages">
|
||||
<UniqueIdentifier>{8455d832-c72b-4ec2-b90d-ac30ad3d3ad8}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Debugger">
|
||||
<UniqueIdentifier>{ff3c6e48-3987-41d2-8916-b588a457ff30}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
|
@ -191,6 +194,18 @@
|
|||
<ClInclude Include="MMC3_189.h">
|
||||
<Filter>Header Files\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debugger.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Disassembler.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DisassemblyInfo.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Breakpoint.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CPU.cpp">
|
||||
|
@ -238,5 +253,17 @@
|
|||
<ClCompile Include="MapperFactory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Disassembler.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DisassemblyInfo.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Breakpoint.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
221
Core/Debugger.cpp
Normal file
221
Core/Debugger.cpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Debugger.h"
|
||||
#include "Console.h"
|
||||
#include "Disassembler.h"
|
||||
|
||||
Debugger* Debugger::Instance = nullptr;
|
||||
|
||||
Debugger::Debugger(shared_ptr<Console> console, shared_ptr<CPU> cpu, shared_ptr<MemoryManager> memoryManager, shared_ptr<BaseMapper> mapper)
|
||||
{
|
||||
_console = console;
|
||||
_cpu = cpu;
|
||||
_memoryManager = memoryManager;
|
||||
_mapper = mapper;
|
||||
|
||||
_disassembler.reset(new Disassembler(mapper->GetPRGCopy(), mapper->GetPRGSize()));
|
||||
|
||||
_stepCount = -1;
|
||||
|
||||
Debugger::Instance = this;
|
||||
}
|
||||
|
||||
Debugger::~Debugger()
|
||||
{
|
||||
if(Debugger::Instance == this) {
|
||||
Debugger::Instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::AddBreakpoint(BreakpointType type, uint32_t address, bool isAbsoluteAddr)
|
||||
{
|
||||
_bpLock.Acquire();
|
||||
|
||||
if(isAbsoluteAddr) {
|
||||
address = _mapper->ToAbsoluteAddress(address);
|
||||
}
|
||||
|
||||
shared_ptr<Breakpoint> breakpoint(new Breakpoint(type, address, isAbsoluteAddr));
|
||||
switch(type) {
|
||||
case BreakpointType::Execute: _execBreakpoints.push_back(breakpoint); break;
|
||||
case BreakpointType::Read: _readBreakpoints.push_back(breakpoint); break;
|
||||
case BreakpointType::Write: _writeBreakpoints.push_back(breakpoint); break;
|
||||
}
|
||||
|
||||
_bpLock.Release();
|
||||
}
|
||||
|
||||
vector<shared_ptr<Breakpoint>> Debugger::GetBreakpoints()
|
||||
{
|
||||
vector<shared_ptr<Breakpoint>> breakpoints;
|
||||
|
||||
breakpoints.insert(breakpoints.end(), _execBreakpoints.begin(), _execBreakpoints.end());
|
||||
breakpoints.insert(breakpoints.end(), _readBreakpoints.begin(), _readBreakpoints.end());
|
||||
breakpoints.insert(breakpoints.end(), _writeBreakpoints.begin(), _writeBreakpoints.end());
|
||||
|
||||
return breakpoints;
|
||||
}
|
||||
|
||||
vector<uint32_t> Debugger::GetExecBreakpointAddresses()
|
||||
{
|
||||
_bpLock.Acquire();
|
||||
|
||||
vector<uint32_t> result;
|
||||
|
||||
for(int i = 0, len = _execBreakpoints.size(); i < len; i++) {
|
||||
shared_ptr<Breakpoint> breakpoint = _execBreakpoints[i];
|
||||
int32_t addr = breakpoint->GetAddr();
|
||||
if(breakpoint->IsAbsoluteAddr()) {
|
||||
addr = _mapper->FromAbsoluteAddress(addr);
|
||||
}
|
||||
|
||||
if(addr >= 0) {
|
||||
result.push_back(addr);
|
||||
}
|
||||
}
|
||||
|
||||
_bpLock.Release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Debugger::RemoveBreakpoint(shared_ptr<Breakpoint> breakpoint)
|
||||
{
|
||||
_bpLock.Acquire();
|
||||
|
||||
for(int i = 0, len = _execBreakpoints.size(); i < len; i++) {
|
||||
if(_execBreakpoints[i] == breakpoint) {
|
||||
_execBreakpoints.erase(_execBreakpoints.begin()+i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_bpLock.Release();
|
||||
|
||||
//_readBreakpoints.remove(breakpoint);
|
||||
//_writeBreakpoints.remove(breakpoint);
|
||||
}
|
||||
|
||||
shared_ptr<Breakpoint> Debugger::GetMatchingBreakpoint(BreakpointType type, uint32_t addr)
|
||||
{
|
||||
uint32_t absoluteAddr = _mapper->ToAbsoluteAddress(addr);
|
||||
vector<shared_ptr<Breakpoint>> *breakpoints = nullptr;
|
||||
|
||||
switch(type) {
|
||||
case BreakpointType::Execute: breakpoints = &_execBreakpoints; break;
|
||||
case BreakpointType::Read: breakpoints = &_readBreakpoints; break;
|
||||
case BreakpointType::Write: breakpoints = &_writeBreakpoints; break;
|
||||
}
|
||||
|
||||
_bpLock.Acquire();
|
||||
for(int i = 0, len = breakpoints->size(); i < len; i++) {
|
||||
shared_ptr<Breakpoint> breakpoint = (*breakpoints)[i];
|
||||
if(breakpoint->Matches(addr, absoluteAddr)) {
|
||||
_bpLock.Release();
|
||||
return breakpoint;
|
||||
}
|
||||
}
|
||||
|
||||
_bpLock.Release();
|
||||
return shared_ptr<Breakpoint>();
|
||||
}
|
||||
|
||||
void Debugger::PrivateCheckBreakpoint(BreakpointType type, uint32_t addr)
|
||||
{
|
||||
//Check if a breakpoint has been hit and freeze execution if one has
|
||||
bool breakDone = false;
|
||||
if(type == BreakpointType::Execute) {
|
||||
_disassembler->BuildCache(_mapper->ToAbsoluteAddress(addr), addr);
|
||||
breakDone = SleepUntilResume();
|
||||
}
|
||||
|
||||
if(!breakDone && GetMatchingBreakpoint(type, addr)) {
|
||||
//Found a matching breakpoint, stop execution
|
||||
Step(1);
|
||||
SleepUntilResume();
|
||||
}
|
||||
}
|
||||
|
||||
bool Debugger::SleepUntilResume()
|
||||
{
|
||||
uint32_t stepCount = _stepCount.load();
|
||||
if(stepCount > 0) {
|
||||
_stepCount--;
|
||||
stepCount = _stepCount.load();
|
||||
}
|
||||
|
||||
if(stepCount == 0) {
|
||||
//Break
|
||||
_console->SendNotification(ConsoleNotificationType::CodeBreak);
|
||||
while(stepCount == 0) {
|
||||
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
|
||||
stepCount = _stepCount.load();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
State Debugger::GetCPUState()
|
||||
{
|
||||
return _cpu->GetState();
|
||||
}
|
||||
|
||||
void Debugger::Step(uint32_t count)
|
||||
{
|
||||
//Run CPU for [count] cycles and before breaking again
|
||||
_stepCount = count;
|
||||
}
|
||||
|
||||
void Debugger::Run()
|
||||
{
|
||||
//Resume execution after a breakpoint has been hit
|
||||
_stepCount = -1;
|
||||
}
|
||||
|
||||
bool Debugger::IsCodeChanged()
|
||||
{
|
||||
string output = GenerateOutput();
|
||||
if(_outputCache.compare(output) == 0) {
|
||||
return false;
|
||||
} else {
|
||||
_outputCache = output;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
string Debugger::GenerateOutput()
|
||||
{
|
||||
vector<uint32_t> memoryRanges = _mapper->GetPRGRanges();
|
||||
|
||||
std::ostringstream output;
|
||||
uint16_t memoryAddr = 0x8000;
|
||||
for(int i = 0, size = memoryRanges.size(); i < size; i += 2) {
|
||||
output << _disassembler->GetCode(memoryRanges[i], memoryRanges[i+1], memoryAddr);
|
||||
}
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
string Debugger::GetCode()
|
||||
{
|
||||
return _outputCache;
|
||||
}
|
||||
|
||||
uint8_t Debugger::GetMemoryValue(uint32_t addr)
|
||||
{
|
||||
return _memoryManager->DebugRead(addr);
|
||||
}
|
||||
|
||||
uint32_t Debugger::GetRelativeAddress(uint32_t addr)
|
||||
{
|
||||
return _mapper->FromAbsoluteAddress(addr);
|
||||
}
|
||||
|
||||
void Debugger::CheckBreakpoint(BreakpointType type, uint32_t addr)
|
||||
{
|
||||
if(Debugger::Instance) {
|
||||
Debugger::Instance->PrivateCheckBreakpoint(type, addr);
|
||||
}
|
||||
}
|
139
Core/Disassembler.cpp
Normal file
139
Core/Disassembler.cpp
Normal file
|
@ -0,0 +1,139 @@
|
|||
#include "stdafx.h"
|
||||
#include "Disassembler.h"
|
||||
#include "DisassemblyInfo.h"
|
||||
#include "CPU.h"
|
||||
|
||||
Disassembler::Disassembler(uint8_t* prgROM, uint32_t prgSize)
|
||||
{
|
||||
_prgROM = prgROM;
|
||||
_prgSize = prgSize;
|
||||
for(uint32_t i = 0; i < prgSize; i++) {
|
||||
_disassembleCache.push_back(shared_ptr<DisassemblyInfo>(nullptr));
|
||||
}
|
||||
|
||||
string opName[256] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
"BRK", "ORA", "", "", "NOP", "ORA", "ASL", "", "PHP", "ORA", "ASL", "", "NOP", "ORA", "ASL", "", //0
|
||||
"BPL", "ORA", "", "", "NOP", "ORA", "ASL", "", "CLC", "ORA", "", "", "NOP", "ORA", "ASL", "", //1
|
||||
"JSR", "AND", "", "", "BIT", "AND", "ROL", "", "PLP", "AND", "ROL", "", "BIT", "AND", "ROL", "", //2
|
||||
"BMI", "AND", "", "", "NOP", "AND", "ROL", "", "SEC", "AND", "", "", "NOP", "AND", "ROL", "", //3
|
||||
"RTI", "EOR", "", "", "NOP", "EOR", "LSR", "", "PHA", "EOR", "LSR", "", "JMP", "EOR", "LSR", "", //4
|
||||
"BVC", "EOR", "", "", "NOP", "EOR", "LSR", "", "CLI", "EOR", "", "", "NOP", "EOR", "LSR", "", //5
|
||||
"RTS", "ADC", "", "", "NOP", "ADC", "ROR", "", "PLA", "ADC", "ROR", "", "JMP", "ADC", "ROR", "", //6
|
||||
"BVS", "ADC", "", "", "NOP", "ADC", "ROR", "", "SEI", "ADC", "", "", "NOP", "ADC", "ROR", "", //7
|
||||
"NOP", "STA", "NOP", "", "STY", "STA", "STX", "", "DEY", "NOP", "TXA", "", "STY", "STA", "STX", "", //8
|
||||
"BCC", "STA", "", "", "STY", "STA", "STX", "", "TYA", "STA", "TXS", "", "", "STA", "", "", //9
|
||||
"LDY", "LDA", "LDX", "", "LDY", "LDA", "LDX", "", "TAY", "LDA", "TAX", "", "LDY", "LDA", "LDX", "", //A
|
||||
"BCS", "LDA", "", "", "LDY", "LDA", "LDX", "", "CLV", "LDA", "TSX", "", "LDY", "LDA", "LDX", "", //B
|
||||
"CPY", "CPA", "NOP", "", "CPY", "CPA", "DEC", "", "INY", "CPA", "DEX", "", "CPY", "CPA", "DEC", "", //C
|
||||
"BNE", "CPA", "", "", "NOP", "CPA", "DEC", "", "CLD", "CPA", "", "", "NOP", "CPA", "DEC", "", //D
|
||||
"CPX", "SBC", "NOP", "", "CPX", "SBC", "INC", "", "INX", "SBC", "NOP", "", "CPX", "SBC", "INC", "", //E
|
||||
"BEQ", "SBC", "", "", "NOP", "SBC", "INC", "", "SED", "SBC", "", "", "NOP", "SBC", "INC", "" //F
|
||||
};
|
||||
|
||||
AddrMode opMode[256] = {
|
||||
Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Acc, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
Abs, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Acc, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Acc, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Acc, Imm, Ind, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndYW, None, IndY, ZeroX, ZeroX, ZeroY, ZeroY, Imp, AbsYW, Imp, AbsY, AbsXW, AbsXW, AbsYW, AbsY,
|
||||
Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndY, ZeroX, ZeroX, ZeroY, ZeroY, Imp, AbsY, Imp, AbsY, AbsX, AbsX, AbsY, AbsY,
|
||||
Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs,
|
||||
Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW,
|
||||
};
|
||||
|
||||
for(int i = 0; i < 256; i++) {
|
||||
DisassemblyInfo::OPName[i] = opName[i];
|
||||
DisassemblyInfo::OPMode[i] = opMode[i];
|
||||
switch(DisassemblyInfo::OPMode[i]) {
|
||||
case AddrMode::Abs:
|
||||
case AddrMode::AbsX:
|
||||
case AddrMode::AbsXW:
|
||||
case AddrMode::AbsY:
|
||||
case AddrMode::AbsYW:
|
||||
case AddrMode::Ind:
|
||||
DisassemblyInfo::OPSize[i] = 3;
|
||||
break;
|
||||
|
||||
case AddrMode::Imm:
|
||||
case AddrMode::IndX:
|
||||
case AddrMode::IndY:
|
||||
case AddrMode::IndYW:
|
||||
case AddrMode::Rel:
|
||||
case AddrMode::Zero:
|
||||
case AddrMode::ZeroX:
|
||||
case AddrMode::ZeroY:
|
||||
DisassemblyInfo::OPSize[i] = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
DisassemblyInfo::OPSize[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Disassembler::BuildCache(uint32_t absoluteAddr, uint16_t memoryAddr)
|
||||
{
|
||||
while(!_disassembleCache[absoluteAddr]) {
|
||||
shared_ptr<DisassemblyInfo> disInfo(new DisassemblyInfo(&_prgROM[absoluteAddr]));
|
||||
_disassembleCache[absoluteAddr] = disInfo;
|
||||
|
||||
uint8_t opCode = _prgROM[absoluteAddr];
|
||||
|
||||
if(opCode == 0x10 || opCode == 0x20 || opCode == 0x30 || opCode == 0x40 || opCode == 0x50 || opCode == 0x60 || opCode == 0x70 || opCode == 0x90 || opCode == 0xB0 || opCode == 0xD0 || opCode == 0xF0 || opCode == 0x4C || opCode == 0x6C) {
|
||||
//Hit a jump/return instruction, can't assume that what follows is actual code, stop disassembling
|
||||
break;
|
||||
}
|
||||
|
||||
absoluteAddr += disInfo->GetSize();
|
||||
memoryAddr += disInfo->GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t &memoryAddr)
|
||||
{
|
||||
std::ostringstream output;
|
||||
|
||||
uint32_t addr = startAddr;
|
||||
uint32_t byteCount = 0;
|
||||
while(addr < endAddr) {
|
||||
shared_ptr<DisassemblyInfo> info;
|
||||
if(info = _disassembleCache[addr]) {
|
||||
if(byteCount > 0) {
|
||||
output << "\\par\n";
|
||||
byteCount = 0;
|
||||
}
|
||||
output << "{\\highlight1\n " << std::hex << std::uppercase << memoryAddr << ":} " << info->ToString(memoryAddr) << "\\par\n";
|
||||
addr += info->GetSize();
|
||||
memoryAddr += info->GetSize();
|
||||
} else {
|
||||
if(byteCount >= 8) {
|
||||
output << "\\par\n";
|
||||
byteCount = 0;
|
||||
}
|
||||
if(byteCount == 0) {
|
||||
output << "{\\highlight1\n " << std::hex << std::uppercase << memoryAddr << ":} " << ".db";
|
||||
}
|
||||
output << std::hex << " $" << std::setfill('0') << std::setw(2) << (short)_prgROM[addr];
|
||||
|
||||
byteCount++;
|
||||
addr++;
|
||||
memoryAddr++;
|
||||
}
|
||||
}
|
||||
|
||||
if(byteCount > 0) {
|
||||
output << "\\par\n";
|
||||
}
|
||||
|
||||
return output.str();
|
||||
}
|
18
Core/Disassembler.h
Normal file
18
Core/Disassembler.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class DisassemblyInfo;
|
||||
|
||||
class Disassembler
|
||||
{
|
||||
private:
|
||||
vector<shared_ptr<DisassemblyInfo>> _disassembleCache;
|
||||
uint8_t* _prgROM;
|
||||
uint32_t _prgSize;
|
||||
|
||||
public:
|
||||
Disassembler(uint8_t* prgROM, uint32_t prgSize);
|
||||
|
||||
void BuildCache(uint32_t absoluteAddr, uint16_t memoryAddr);
|
||||
string GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t &memoryAddr);
|
||||
};
|
115
Core/DisassemblyInfo.cpp
Normal file
115
Core/DisassemblyInfo.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include "stdafx.h"
|
||||
#include "DisassemblyInfo.h"
|
||||
#include "CPU.h"
|
||||
|
||||
string DisassemblyInfo::OPName[256];
|
||||
AddrMode DisassemblyInfo::OPMode[256];
|
||||
uint32_t DisassemblyInfo::OPSize[256];
|
||||
|
||||
void DisassemblyInfo::Initialize(uint32_t memoryAddr)
|
||||
{
|
||||
_lastAddr = memoryAddr;
|
||||
|
||||
std::ostringstream output;
|
||||
uint8_t opCode = *_opPointer;
|
||||
_opSize = DisassemblyInfo::OPSize[opCode];
|
||||
_opMode = DisassemblyInfo::OPMode[opCode];
|
||||
|
||||
output << DisassemblyInfo::OPName[opCode];
|
||||
if(opCode == 0x40 || opCode == 0x60) {
|
||||
//Make end of function/interrupt routines more obvious
|
||||
output << " ---------------------------";
|
||||
}
|
||||
|
||||
if(DisassemblyInfo::OPName[opCode].empty()) {
|
||||
std::cout << "error";
|
||||
}
|
||||
|
||||
std::ostringstream nextByte;
|
||||
std::ostringstream nextWord;
|
||||
if(_opSize == 2) {
|
||||
nextByte << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << (short)(*(_opPointer + 1));
|
||||
} else if(_opSize == 3) {
|
||||
nextWord << std::uppercase << std::hex << std::setw(4) << std::setfill('0') << (*(_opPointer + 1) | (*(_opPointer + 2) << 8));
|
||||
}
|
||||
|
||||
switch(_opMode) {
|
||||
case AddrMode::Abs:
|
||||
output << " $" << nextWord.str();
|
||||
break;
|
||||
|
||||
case AddrMode::AbsX:
|
||||
case AddrMode::AbsXW:
|
||||
output << " $" << nextWord.str() << ",X";
|
||||
break;
|
||||
|
||||
case AddrMode::AbsY:
|
||||
case AddrMode::AbsYW:
|
||||
output << " $" << nextWord.str() << ",Y";
|
||||
break;
|
||||
|
||||
case AddrMode::Imm:
|
||||
output << " #" << nextByte.str();
|
||||
break;
|
||||
|
||||
case AddrMode::Ind:
|
||||
output << " ($" << nextWord.str() << ")";
|
||||
break;
|
||||
|
||||
case AddrMode::IndX:
|
||||
output << " ($" << nextByte.str() << ",X)";
|
||||
break;
|
||||
|
||||
case AddrMode::IndY:
|
||||
case AddrMode::IndYW:
|
||||
output << " ($" << nextByte.str() << "),Y";
|
||||
break;
|
||||
|
||||
case AddrMode::Rel:
|
||||
//TODO (not correct when banks are switched around in memory)
|
||||
output << " $" << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << ((int8_t)*(_opPointer + 1) + memoryAddr + 2);
|
||||
break;
|
||||
|
||||
case AddrMode::Zero:
|
||||
output << " $" << nextByte.str();
|
||||
break;
|
||||
|
||||
case AddrMode::ZeroX:
|
||||
output << " $" << nextByte.str() << ",X";
|
||||
break;
|
||||
|
||||
case AddrMode::ZeroY:
|
||||
output << " $" << nextByte.str() << ",Y";
|
||||
break;
|
||||
|
||||
case AddrMode::Acc:
|
||||
output << " A";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_disassembly = output.str();
|
||||
}
|
||||
|
||||
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer)
|
||||
{
|
||||
_opPointer = opPointer;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
string DisassemblyInfo::ToString(uint32_t memoryAddr)
|
||||
{
|
||||
if(memoryAddr != _lastAddr && _opMode == AddrMode::Rel) {
|
||||
Initialize(memoryAddr);
|
||||
}
|
||||
return _disassembly;
|
||||
}
|
||||
|
||||
uint32_t DisassemblyInfo::GetSize()
|
||||
{
|
||||
return _opSize;
|
||||
}
|
||||
|
29
Core/DisassemblyInfo.h
Normal file
29
Core/DisassemblyInfo.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
extern enum AddrMode;
|
||||
|
||||
class DisassemblyInfo
|
||||
{
|
||||
public:
|
||||
static string OPName[256];
|
||||
static AddrMode OPMode[256];
|
||||
static uint32_t OPSize[256];
|
||||
|
||||
private:
|
||||
string _disassembly;
|
||||
uint8_t *_opPointer = nullptr;
|
||||
uint32_t _opSize = 0;
|
||||
AddrMode _opMode;
|
||||
uint32_t _lastAddr = 0;
|
||||
|
||||
private:
|
||||
void Initialize(uint32_t memoryAddr = 0);
|
||||
|
||||
public:
|
||||
DisassemblyInfo(uint8_t* opPointer);
|
||||
|
||||
string ToString(uint32_t memoryAddr);
|
||||
uint32_t GetSize();
|
||||
};
|
||||
|
|
@ -9,6 +9,7 @@ enum class ConsoleNotificationType
|
|||
GamePaused = 3,
|
||||
GameResumed = 4,
|
||||
GameStopped = 5,
|
||||
CodeBreak = 6,
|
||||
};
|
||||
|
||||
class INotificationListener
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "PPU.h"
|
||||
#include "Debugger.h"
|
||||
|
||||
MemoryManager::MemoryManager(shared_ptr<BaseMapper> mapper)
|
||||
{
|
||||
|
@ -92,8 +93,21 @@ void MemoryManager::RegisterIODevice(IMemoryHandler *handler)
|
|||
InitializeMemoryHandlers(_vramWriteHandlers, handler, ranges.GetVRAMWriteAddresses());
|
||||
}
|
||||
|
||||
uint8_t MemoryManager::Read(uint16_t addr)
|
||||
uint8_t MemoryManager::DebugRead(uint16_t addr)
|
||||
{
|
||||
if(addr <= 0x1FFF) {
|
||||
return _internalRAM[addr & 0x07FF];
|
||||
} else if(addr > 0x4017) {
|
||||
return ReadRegister(addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t MemoryManager::Read(uint16_t addr, bool forExecution)
|
||||
{
|
||||
Debugger::CheckBreakpoint(forExecution ? BreakpointType::Execute : BreakpointType::Read, addr);
|
||||
|
||||
uint8_t value;
|
||||
PPU::ExecStatic(3);
|
||||
if(addr <= 0x1FFF) {
|
||||
|
@ -106,6 +120,8 @@ uint8_t MemoryManager::Read(uint16_t addr)
|
|||
|
||||
void MemoryManager::Write(uint16_t addr, uint8_t value)
|
||||
{
|
||||
Debugger::CheckBreakpoint(BreakpointType::Write, addr);
|
||||
|
||||
PPU::ExecStatic(3);
|
||||
if(addr <= 0x1FFF) {
|
||||
_internalRAM[addr & 0x07FF] = value;
|
||||
|
|
|
@ -46,7 +46,8 @@ class MemoryManager: public Snapshotable
|
|||
void InitializeMemoryHandlers(IMemoryHandler** memoryHandlers, IMemoryHandler* handler, vector<uint16_t> *addresses);
|
||||
void RegisterIODevice(IMemoryHandler *handler);
|
||||
|
||||
uint8_t Read(uint16_t addr);
|
||||
uint8_t DebugRead(uint16_t addr);
|
||||
uint8_t Read(uint16_t addr, bool forExecution = false);
|
||||
uint16_t ReadWord(uint16_t addr);
|
||||
void Write(uint16_t addr, uint8_t value);
|
||||
|
||||
|
|
|
@ -36,4 +36,5 @@ using std::atomic_flag;
|
|||
using std::atomic;
|
||||
using std::list;
|
||||
using std::max;
|
||||
using std::thread;
|
||||
using std::thread;
|
||||
using std::string;
|
791
GUI/DebugWindow.cpp
Normal file
791
GUI/DebugWindow.cpp
Normal file
|
@ -0,0 +1,791 @@
|
|||
#include "stdafx.h"
|
||||
#include <Windowsx.h>
|
||||
#include <Richedit.h>
|
||||
#include <Commctrl.h>
|
||||
#include "DebugWindow.h"
|
||||
#include "../Core/Debugger.h"
|
||||
#include "../Core/Breakpoint.h"
|
||||
#include "../Core/Console.h"
|
||||
#include "../Utilities/utf8conv.h"
|
||||
|
||||
#define WM_UPDATECPUSTATUS (WM_APP + 1)
|
||||
|
||||
DebugWindow* DebugWindow::Instance = nullptr;
|
||||
WNDPROC DebugWindow::OriginalRichEditWndProc = nullptr;
|
||||
|
||||
DebugWindow::DebugWindow(HWND parentWnd, shared_ptr<Debugger> debugger)
|
||||
{
|
||||
Console::RegisterNotificationListener(this);
|
||||
DebugWindow::Instance = this;
|
||||
_debugger = debugger;
|
||||
|
||||
LoadLibrary(TEXT("Riched20.dll"));
|
||||
_hWnd = CreateDialog(nullptr, MAKEINTRESOURCE(IDD_DEBUG), parentWnd, DebuggerWndProc);
|
||||
if(_hWnd) {
|
||||
ShowWindow(_hWnd, SW_SHOW);
|
||||
}
|
||||
}
|
||||
|
||||
DebugWindow::~DebugWindow()
|
||||
{
|
||||
Console::UnregisterNotificationListener(this);
|
||||
}
|
||||
|
||||
wstring DebugWindow::GetWordUnderMouse(HWND hEdit, int x, int y)
|
||||
{
|
||||
POINT pt = { x, y };
|
||||
int ci = SendMessage(hEdit, EM_CHARFROMPOS, 0, (LPARAM)&pt); //current character index
|
||||
if(ci < 0) {
|
||||
return L"";
|
||||
}
|
||||
int lix = SendMessage(hEdit, EM_EXLINEFROMCHAR, 0, ci); //line index
|
||||
int co = ci - SendMessage(hEdit, EM_LINEINDEX, lix, 0); //current character offset/position
|
||||
wchar_t buffer[1024] = { 0, }; //buffer to hold characters (1024 should be enough for the average line)
|
||||
((WORD *)buffer)[0] = 1024; //set first byte to size of buffer as specified in MSDN
|
||||
SendMessage(hEdit, EM_GETLINE, lix, (LPARAM)buffer);
|
||||
if(buffer[co] == ' ' || buffer[co] == '\n' || buffer[co] == '\r') {
|
||||
return L""; //currently at a space character or end of line
|
||||
}
|
||||
std::wostringstream str; //string stream to hold the resultant word
|
||||
int i = co;
|
||||
bool forward = i == 0 ? true : false; //direction the loop is searching in
|
||||
//search for spaces between words
|
||||
if(i == lstrlenW(buffer) - 1) {
|
||||
return L"";
|
||||
}
|
||||
while(i < lstrlenW(buffer)) {
|
||||
if(!forward) {
|
||||
//decrease pointer looking for start of line or space
|
||||
i--;
|
||||
if(buffer[i] == ' ' || buffer[i] == '\n' || buffer[i] == '\r') {
|
||||
i++; //skip over this character
|
||||
forward = true;
|
||||
} else if(i > 0) {
|
||||
continue; //not at 0 keep looping backwards
|
||||
} else {
|
||||
forward = true; //at 0 so exit this part of the loop
|
||||
}
|
||||
}
|
||||
//increment pointer looking forward for a space
|
||||
if(buffer[i] == ' ' || buffer[i] == '\n' || buffer[i] == '\r') {
|
||||
break; //break out of the loop
|
||||
}
|
||||
//append current characters to output stream
|
||||
str << buffer[i];
|
||||
++i;
|
||||
}
|
||||
return str.str(); // <- and here is our word
|
||||
}
|
||||
|
||||
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||
void DebugWindow::CreateToolTipForRect()
|
||||
{
|
||||
HWND toolTipWnd = CreateWindowEx(WS_EX_TOPMOST,
|
||||
TOOLTIPS_CLASSW, 0, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
0, 0, GetModuleHandle(nullptr), 0);
|
||||
|
||||
TOOLINFOW ti = {};
|
||||
ti.cbSize = sizeof(TOOLINFOW);
|
||||
ti.uFlags = TTF_ABSOLUTE | TTF_IDISHWND /* | TTF_TRACK */; // Don't specify TTF_TRACK here. Otherwise the tooltip won't show up.
|
||||
ti.hwnd = toolTipWnd; // By doing this, you don't have to create another window.
|
||||
ti.hinst = NULL;
|
||||
ti.uId = (UINT)toolTipWnd;
|
||||
ti.lpszText = L"";
|
||||
|
||||
SendMessage(toolTipWnd, TTM_ADDTOOLW, 0, (LPARAM)&ti);
|
||||
SendMessage(toolTipWnd, TTM_SETMAXTIPWIDTH, 0, (LPARAM)350);
|
||||
|
||||
_toolTipWnd = toolTipWnd;
|
||||
}
|
||||
|
||||
void DebugWindow::ShowTooltip(wstring text, int x, int y)
|
||||
{
|
||||
TOOLINFOW ti = {};
|
||||
ti.cbSize = sizeof(TOOLINFOW);
|
||||
ti.hwnd = _toolTipWnd;
|
||||
ti.uId = (UINT)_toolTipWnd;
|
||||
ti.lpszText = (LPWSTR)text.c_str();
|
||||
SendMessage(_toolTipWnd, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti); // This will update the tooltip content.
|
||||
SendMessage(_toolTipWnd, TTM_TRACKACTIVATE, (WPARAM)true, (LPARAM)&ti);
|
||||
SendMessage(_toolTipWnd, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(x, y)); // Update the position of your tooltip. Screen coordinate.
|
||||
}
|
||||
|
||||
void DebugWindow::HideTooltip()
|
||||
{
|
||||
TOOLINFOW ti = {};
|
||||
ti.cbSize = sizeof(TOOLINFOW);
|
||||
ti.hwnd = _toolTipWnd;
|
||||
ti.uId = (UINT)_toolTipWnd;
|
||||
ti.lpszText = (LPWSTR)L"";
|
||||
|
||||
SendMessage(_toolTipWnd, TTM_TRACKACTIVATE, (WPARAM)false, (LPARAM)&ti);
|
||||
}
|
||||
|
||||
void DebugWindow::GenerateTooltip(LPARAM lParam)
|
||||
{
|
||||
static LPARAM lastPos = 0;
|
||||
wstring currentWord;
|
||||
|
||||
if(lastPos != lParam) {
|
||||
currentWord = GetWordUnderMouse(_richEdit, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||||
if(!currentWord.empty()) {
|
||||
std::wostringstream output;
|
||||
if(currentWord[0] == '$') {
|
||||
uint16_t addr = std::stoi(currentWord.substr(1), nullptr, 16);
|
||||
output << currentWord << L" = " << std::hex << std::uppercase << std::setfill(L'0') << std::setw(2) << (short)_debugger->GetMemoryValue(addr);
|
||||
|
||||
if(addr >= 0x8000) {
|
||||
_clickAddr = addr;
|
||||
output << std::endl << "Click to go to location";
|
||||
}
|
||||
ShowTooltip(output.str().c_str(), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||||
} else if(currentWord[0] == L'●') {
|
||||
|
||||
} else {
|
||||
_clickAddr = -1;
|
||||
HideTooltip();
|
||||
}
|
||||
} else {
|
||||
_clickAddr = -1;
|
||||
HideTooltip();
|
||||
}
|
||||
lastPos = lParam;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugWindow::ClearLineStyling(int32_t lineNumber)
|
||||
{
|
||||
SendMessage(_richEdit, WM_SETREDRAW, false, 0);
|
||||
|
||||
Color bgColor = { 255, 255, 255 };
|
||||
Color textColor = { 0, 0, 0 };
|
||||
|
||||
SetLineColor(textColor, bgColor, lineNumber);
|
||||
SetLineSymbol(0, bgColor, lineNumber);
|
||||
|
||||
SendMessage(_richEdit, WM_SETREDRAW, true, 0);
|
||||
InvalidateRect(_richEdit, nullptr, true);
|
||||
|
||||
RefreshBreakpointList();
|
||||
}
|
||||
|
||||
void DebugWindow::ToggleBreakpoint()
|
||||
{
|
||||
int lineNumber = GetCurrentLine();
|
||||
uint16_t addr = GetLineAddr(lineNumber);
|
||||
shared_ptr<Breakpoint> existingBreakpoint = _debugger->GetMatchingBreakpoint(BreakpointType::Execute, addr);
|
||||
if(existingBreakpoint) {
|
||||
_debugger->RemoveBreakpoint(existingBreakpoint);
|
||||
ClearLineStyling(lineNumber);
|
||||
SetActiveStatement(_lastActiveLine);
|
||||
} else {
|
||||
_debugger->AddBreakpoint(BreakpointType::Execute, addr, true);
|
||||
RefreshBreakpointList();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugWindow::SetBreakpoint(int32_t lineNumber)
|
||||
{
|
||||
SendMessage(_richEdit, WM_SETREDRAW, false, 0);
|
||||
|
||||
Color bgColor = { 229, 20, 0 };
|
||||
Color textColor = { 255, 255, 255 };
|
||||
|
||||
SetLineColor(textColor, bgColor, lineNumber);
|
||||
SetLineSymbol(9679, bgColor, lineNumber);
|
||||
|
||||
SendMessage(_richEdit, WM_SETREDRAW, true, 0);
|
||||
InvalidateRect(_richEdit, nullptr, true);
|
||||
}
|
||||
|
||||
void DebugWindow::SetActiveStatement(int32_t lineNumber)
|
||||
{
|
||||
SendMessage(_richEdit, WM_SETREDRAW, false, 0);
|
||||
|
||||
if(lineNumber == -1) {
|
||||
lineNumber = GetCurrentLine();
|
||||
}
|
||||
|
||||
if(_lastActiveLine != -1) {
|
||||
ClearLineStyling(_lastActiveLine);
|
||||
}
|
||||
|
||||
Color bgColor = { 174, 254, 79 };
|
||||
Color textColor = { 0, 0, 0 };
|
||||
Color symbolColor = { 124, 204, 49 };
|
||||
|
||||
_lastActiveLine = SetLineColor(textColor, bgColor, lineNumber);
|
||||
SetLineSymbol(187, symbolColor, lineNumber);
|
||||
|
||||
SendMessage(_richEdit, WM_SETREDRAW, true, 0);
|
||||
InvalidateRect(_richEdit, nullptr, true);
|
||||
}
|
||||
|
||||
int DebugWindow::GetCurrentLine()
|
||||
{
|
||||
int lineStartIndex = SendMessage(_richEdit, EM_LINEINDEX, -1, 0);
|
||||
int lineLength = SendMessage(_richEdit, EM_LINELENGTH, lineStartIndex, 0);
|
||||
int lineIndex = SendMessage(_richEdit, EM_EXLINEFROMCHAR, 0, lineStartIndex);
|
||||
|
||||
return lineIndex;
|
||||
}
|
||||
|
||||
string DebugWindow::GetRTFColor(Color color)
|
||||
{
|
||||
string result;
|
||||
result += "\\red" + std::to_string(color.R);
|
||||
result += "\\green" + std::to_string(color.G);
|
||||
result += "\\blue" + std::to_string(color.B) + ";";
|
||||
return result;
|
||||
}
|
||||
|
||||
int DebugWindow::SetLineColor(Color textColor, Color bgColor, int32_t lineNumber)
|
||||
{
|
||||
int caretPosition = 0;
|
||||
SendMessage(_richEdit, EM_GETSEL, (WPARAM)&caretPosition, 0);
|
||||
|
||||
string lineFormat = "{\\rtf1\\ansi{\\fonttbl{\\f0 Consolas;}}{\\colortbl ;";
|
||||
lineFormat += GetRTFColor(bgColor);
|
||||
lineFormat += GetRTFColor(textColor);
|
||||
lineFormat += "}\\f0\\cf2\\highlight1\n";
|
||||
wchar_t lineBuffer[1024] = { 0 }; //buffer to hold characters (1024 should be enough for the average line)
|
||||
((WORD *)lineBuffer)[0] = 1024; //set first byte to size of buffer as specified in MSDN
|
||||
|
||||
int lineStartIndex = SendMessage(_richEdit, EM_LINEINDEX, lineNumber, 0);
|
||||
int lineLength = SendMessage(_richEdit, EM_LINELENGTH, lineStartIndex, 0);
|
||||
int lineIndex = SendMessage(_richEdit, EM_EXLINEFROMCHAR, 0, lineStartIndex);
|
||||
SendMessage(_richEdit, EM_GETLINE, lineIndex, (LPARAM)lineBuffer);
|
||||
lineBuffer[lineLength] = 0;
|
||||
|
||||
SendMessage(_richEdit, EM_SETSEL, lineStartIndex + 9, lineStartIndex + lineLength);
|
||||
SendMessage(_richEdit, EM_REPLACESEL, false, (LPARAM)(lineFormat + utf8util::UTF8FromUTF16(lineBuffer+9)).c_str());
|
||||
|
||||
SendMessage(_richEdit, EM_SETSEL, caretPosition, caretPosition);
|
||||
|
||||
return lineIndex;
|
||||
}
|
||||
|
||||
void DebugWindow::SetLineSymbol(int16_t symbolCode, Color symbolColor, int32_t lineNumber)
|
||||
{
|
||||
int caretPosition = 0;
|
||||
SendMessage(_richEdit, EM_GETSEL, (WPARAM)&caretPosition, 0);
|
||||
|
||||
Color bgColor = { 230, 230, 230 };
|
||||
//9679: Breakpoint
|
||||
//10145: Arrow
|
||||
string symbol = "{\\rtf1\\ansi{\\colortbl ;";
|
||||
symbol += GetRTFColor(symbolColor);
|
||||
symbol += GetRTFColor(bgColor);
|
||||
symbol += "}\\f0\\cf1\\highlight2\n";
|
||||
if(symbolCode > 0) {
|
||||
symbol += "\\b\\u" + std::to_string(symbolCode) + "?";
|
||||
} else {
|
||||
symbol += " ";
|
||||
}
|
||||
|
||||
int lineStartIndex = SendMessage(_richEdit, EM_LINEINDEX, lineNumber, 0);
|
||||
|
||||
SendMessage(_richEdit, EM_SETSEL, lineStartIndex + 1, lineStartIndex + 2);
|
||||
SendMessage(_richEdit, EM_REPLACESEL, false, (LPARAM)(symbol).c_str());
|
||||
|
||||
SendMessage(_richEdit, EM_SETSEL, caretPosition, caretPosition);
|
||||
}
|
||||
|
||||
wstring DebugWindow::IntToHex(int value, int minWidth)
|
||||
{
|
||||
std::wostringstream output;
|
||||
output << std::hex << std::uppercase << std::setfill(L'0') << std::setw(minWidth) << value;
|
||||
return output.str();
|
||||
}
|
||||
|
||||
uint32_t DebugWindow::HexToInt(wstring hex)
|
||||
{
|
||||
size_t startPos = hex.find_first_not_of(L" \t$");
|
||||
if(startPos != string::npos) {
|
||||
hex = hex.substr(startPos);
|
||||
}
|
||||
|
||||
return std::stoi(hex, nullptr, 16);
|
||||
}
|
||||
|
||||
bool DebugWindow::GoToAddr(wstring addrText)
|
||||
{
|
||||
uint32_t addr = HexToInt(addrText);
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(GetAddrCharIndex(addr) >= 0) {
|
||||
GoToAddr(addr);
|
||||
return true;
|
||||
}
|
||||
addr--;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DebugWindow::GoToAddr(uint32_t addr)
|
||||
{
|
||||
SendMessage(_richEdit, WM_SETREDRAW, false, 0);
|
||||
|
||||
RefreshDisassembly();
|
||||
|
||||
int charIndex = GetAddrCharIndex(addr);
|
||||
if(charIndex >= 0) {
|
||||
SetFocus(_richEdit);
|
||||
SendMessage(_richEdit, EM_SETSEL, charIndex + 6, charIndex + 6);
|
||||
SendMessage(_richEdit, EM_SCROLLCARET, 0, 0);
|
||||
}
|
||||
|
||||
SendMessage(_richEdit, WM_SETREDRAW, true, 0);
|
||||
InvalidateRect(_richEdit, nullptr, true);
|
||||
}
|
||||
|
||||
int32_t DebugWindow::GetAddrCharIndex(uint32_t addr)
|
||||
{
|
||||
FINDTEXTW findText;
|
||||
findText.chrg.cpMin = 0;
|
||||
findText.chrg.cpMax = -1;
|
||||
wstring searchString = IntToHex(addr, 4) + L":";
|
||||
findText.lpstrText = searchString.c_str();
|
||||
|
||||
return SendMessage(_richEdit, EM_FINDTEXTW, FR_DOWN, (LPARAM)&findText);
|
||||
}
|
||||
|
||||
uint32_t DebugWindow::GetAddrLineNumber(uint32_t addr)
|
||||
{
|
||||
int charIndex = GetAddrCharIndex(addr);
|
||||
|
||||
if(charIndex >= 0) {
|
||||
return SendMessage(_richEdit, EM_EXLINEFROMCHAR, 0, charIndex);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DebugWindow::GetLineAddr(int32_t lineNumber)
|
||||
{
|
||||
int caretPosition = 0;
|
||||
SendMessage(_richEdit, EM_GETSEL, (WPARAM)&caretPosition, NULL);
|
||||
|
||||
int lix = SendMessage(_richEdit, EM_EXLINEFROMCHAR, 0, caretPosition); //line index
|
||||
wchar_t buffer[1024] = { 0, }; //buffer to hold characters (1024 should be enough for the average line)
|
||||
((WORD *)buffer)[0] = 1024; //set first byte to size of buffer as specified in MSDN
|
||||
SendMessage(_richEdit, EM_GETLINE, lix, (LPARAM)buffer);
|
||||
|
||||
wstring lineContent = buffer;
|
||||
|
||||
return std::stoi(lineContent.substr(3), nullptr, 16);
|
||||
}
|
||||
|
||||
void DebugWindow::RefreshDisassembly()
|
||||
{
|
||||
if(_debugger->IsCodeChanged()) {
|
||||
string text;
|
||||
|
||||
text += "{\\rtf1\\ansi{\\fonttbl{\\f0 Consolas;}{\\f1\\fnil\\fcharset128 \\'82\\'6c\\'82\\'72 \\'96\\'be\\'92\\'a9;}}";
|
||||
|
||||
//0: black, 1: light gray
|
||||
Color lightGray = { 230, 230, 230 };
|
||||
text += "{\\colortbl ;";
|
||||
text += GetRTFColor(lightGray) + "}";
|
||||
|
||||
text += _debugger->GetCode();
|
||||
|
||||
SendMessage(_richEdit, WM_SETTEXT, 0, (LPARAM)text.c_str());
|
||||
|
||||
RefreshBreakpointList();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugWindow::InitializeDialog(HWND hDlg)
|
||||
{
|
||||
_hWnd = hDlg;
|
||||
_richEdit = GetDlgItem(_hWnd, IDC_RICHEDIT21);
|
||||
OriginalRichEditWndProc = (WNDPROC)SetWindowLong(_richEdit, GWL_WNDPROC, (LONG)RichEditWndProc);
|
||||
|
||||
SendMessage(_richEdit, EM_LIMITTEXT, 0xFFFFFF, NULL);
|
||||
|
||||
CreateToolTipForRect();
|
||||
RefreshDisassembly();
|
||||
|
||||
SetFocus(_richEdit);
|
||||
|
||||
//CPU Status
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKBRK), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKCARRY), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKDECIMAL), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKINTERRUPT), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKNEGATIVE), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKOVERFLOW), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKRESERVED), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKZERO), false);
|
||||
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKDMC), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKEXTERNAL), false);
|
||||
EnableWindow(GetDlgItem(_hWnd, IDC_CHKFRAMECOUNTER), false);
|
||||
|
||||
InitializeWatch();
|
||||
InitializeBreakpointList();
|
||||
}
|
||||
|
||||
void DebugWindow::InitializeBreakpointList()
|
||||
{
|
||||
_breakpointList = GetDlgItem(_hWnd, IDC_BREAKPOINTLIST);
|
||||
ListView_SetExtendedListViewStyle(_breakpointList, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
|
||||
InsertColumn(_breakpointList, L"Relative Address", 80);
|
||||
InsertColumn(_breakpointList, L"ROM Addressing", 80);
|
||||
InsertColumn(_breakpointList, L"Type", 40);
|
||||
InsertColumn(_breakpointList, L"Address", 60);
|
||||
}
|
||||
|
||||
void DebugWindow::RefreshBreakpointList()
|
||||
{
|
||||
ListView_DeleteAllItems(_breakpointList);
|
||||
for(shared_ptr<Breakpoint> breakpoint : _debugger->GetBreakpoints()) {
|
||||
wstring relAddrString;
|
||||
wstring absAddrString = wstring(L"$") + IntToHex(breakpoint->GetAddr(), 4);
|
||||
if(breakpoint->IsAbsoluteAddr()) {
|
||||
uint32_t relAddr = _debugger->GetRelativeAddress(breakpoint->GetAddr());
|
||||
if(relAddr == -1) {
|
||||
relAddrString = L"<not in memory>";
|
||||
} else {
|
||||
relAddrString = wstring(L"$") + IntToHex(relAddr, 4);
|
||||
}
|
||||
} else {
|
||||
relAddrString = absAddrString;
|
||||
}
|
||||
|
||||
vector<wstring> bpText = {
|
||||
absAddrString,
|
||||
breakpoint->GetTypeText(),
|
||||
breakpoint->IsAbsoluteAddr() ? L"✔" : L"",
|
||||
relAddrString
|
||||
};
|
||||
InsertRow(_breakpointList, bpText, (LPARAM)breakpoint.get(), breakpoint->IsEnabled());
|
||||
}
|
||||
|
||||
for(uint32_t addr : _debugger->GetExecBreakpointAddresses()) {
|
||||
SetBreakpoint(GetAddrLineNumber(addr));
|
||||
}
|
||||
}
|
||||
|
||||
void DebugWindow::EditBreakpoint()
|
||||
{
|
||||
LVITEM item;
|
||||
item.iItem = ListView_GetNextItem(_breakpointList, -1, LVNI_SELECTED);
|
||||
if(item.iItem >= 0) {
|
||||
item.iSubItem = 0;
|
||||
item.mask = LVIF_PARAM;
|
||||
ListView_GetItem(_breakpointList, &item);
|
||||
|
||||
DialogBoxParam(nullptr, MAKEINTRESOURCE(IDD_BREAKPOINT), _hWnd, BreakpointWndProc, item.lParam);
|
||||
|
||||
RefreshBreakpointList();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugWindow::UpdateBreakpointWindow(HWND bpWnd, Breakpoint* breakpoint)
|
||||
{
|
||||
SetDlgItemText(bpWnd, IDC_EDITBreakAddress, IntToHex(breakpoint->GetAddr(), 4).c_str());
|
||||
SendDlgItemMessage(bpWnd, IDC_CHKBreakEnabled, BM_SETCHECK, breakpoint->IsEnabled(), 0);
|
||||
SendDlgItemMessage(bpWnd, IDC_CHKAbsoluteAddr, BM_SETCHECK, breakpoint->IsAbsoluteAddr(), 0);
|
||||
int selectedValue = IDC_RADExec;
|
||||
switch(breakpoint->GetType()) {
|
||||
case BreakpointType::Execute: selectedValue = IDC_RADExec; break;
|
||||
case BreakpointType::Read: selectedValue = IDC_RADRead; break;
|
||||
case BreakpointType::Write: selectedValue = IDC_RADWrite; break;
|
||||
}
|
||||
CheckRadioButton(bpWnd, IDC_RADExec, IDC_RADWrite, selectedValue);
|
||||
}
|
||||
|
||||
void DebugWindow::CommitBreakpointChanges(HWND bpWnd, Breakpoint* breakpoint)
|
||||
{
|
||||
wchar_t buffer[1000];
|
||||
GetDlgItemText(bpWnd, IDC_EDITBreakAddress, buffer, 1000);
|
||||
uint32_t addr = HexToInt(buffer);
|
||||
bool enabled = IsDlgButtonChecked(bpWnd, IDC_CHKBreakEnabled) == TRUE;
|
||||
bool isAbsoluteAddr = IsDlgButtonChecked(bpWnd, IDC_CHKAbsoluteAddr) == TRUE;
|
||||
BreakpointType type = BreakpointType::Execute;
|
||||
|
||||
if(IsDlgButtonChecked(bpWnd, IDC_RADRead)) {
|
||||
type = BreakpointType::Read;
|
||||
} else if(IsDlgButtonChecked(bpWnd, IDC_RADWrite)) {
|
||||
type = BreakpointType::Write;
|
||||
}
|
||||
|
||||
breakpoint->UpdateBreakpoint(type, addr, isAbsoluteAddr, enabled);
|
||||
}
|
||||
|
||||
void DebugWindow::InsertColumn(HWND listView, LPWSTR text, int width)
|
||||
{
|
||||
LVCOLUMN column;
|
||||
column.mask = LVCF_TEXT | LVCF_WIDTH;
|
||||
column.pszText = text;
|
||||
column.cx = width;
|
||||
|
||||
SendMessage(listView, LVM_INSERTCOLUMN, 0, (LPARAM)&column);
|
||||
}
|
||||
|
||||
void DebugWindow::InsertRow(HWND listView, vector<wstring> columnText, LPARAM lParam, bool checked)
|
||||
{
|
||||
LVITEM item;
|
||||
item.mask = LVIF_TEXT | LVIF_PARAM;
|
||||
item.iItem = 0;
|
||||
item.iSubItem = 0;
|
||||
item.pszText = (LPWSTR)columnText[0].c_str();
|
||||
item.lParam = lParam;
|
||||
|
||||
SendMessage(listView, LVM_INSERTITEM, 0, (LPARAM)&item);
|
||||
ListView_SetCheckState(listView, 0, checked);
|
||||
|
||||
item.mask = LVIF_TEXT;
|
||||
for(int i = 1, len = columnText.size(); i < len; i++) {
|
||||
item.iSubItem++;
|
||||
item.pszText = (LPWSTR)columnText[i].c_str();
|
||||
SendMessage(listView, LVM_SETITEM, 0, (LPARAM)&item);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugWindow::InitializeWatch()
|
||||
{
|
||||
_watchList = GetDlgItem(_hWnd, IDC_WATCHLIST);
|
||||
|
||||
ListView_SetExtendedListViewStyle(_watchList, LVS_EX_FULLROWSELECT);
|
||||
|
||||
InsertColumn(_watchList, L"Value", 100);
|
||||
InsertColumn(_watchList, L"Name", 50);
|
||||
InsertRow(_watchList, { L"" });
|
||||
InsertRow(_watchList, { L"aaaa" });
|
||||
}
|
||||
|
||||
void DebugWindow::UpdateCPUStatus(State cpuState)
|
||||
{
|
||||
SetDlgItemText(_hWnd, IDC_EDITA, IntToHex(cpuState.A, 2).c_str());
|
||||
SetDlgItemText(_hWnd, IDC_EDITX, IntToHex(cpuState.X, 2).c_str());
|
||||
SetDlgItemText(_hWnd, IDC_EDITY, IntToHex(cpuState.Y, 2).c_str());
|
||||
SetDlgItemText(_hWnd, IDC_EDITPC, IntToHex(cpuState.PC, 4).c_str());
|
||||
SetDlgItemText(_hWnd, IDC_EDITSP, IntToHex(cpuState.SP, 2).c_str());
|
||||
SetDlgItemText(_hWnd, IDC_EDITPS, IntToHex(cpuState.PS, 2).c_str());
|
||||
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKBRK, BM_SETCHECK, cpuState.PS & PSFlags::Break, 0);
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKCARRY, BM_SETCHECK, cpuState.PS & PSFlags::Carry, 0);
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKDECIMAL, BM_SETCHECK, cpuState.PS & PSFlags::Decimal, 0);
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKINTERRUPT, BM_SETCHECK, cpuState.PS & PSFlags::Interrupt, 0);
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKNEGATIVE, BM_SETCHECK, cpuState.PS & PSFlags::Negative, 0);
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKOVERFLOW, BM_SETCHECK, cpuState.PS & PSFlags::Overflow, 0);
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKRESERVED, BM_SETCHECK, cpuState.PS & PSFlags::Reserved, 0);
|
||||
SendDlgItemMessage(_hWnd, IDC_CHKZERO, BM_SETCHECK, cpuState.PS & PSFlags::Zero, 0);
|
||||
|
||||
wstring stack;
|
||||
for(int i = cpuState.SP + 1; i <= 0xFF; i++) {
|
||||
stack += IntToHex(_debugger->GetMemoryValue(i + 0x100), 2) + L" ";
|
||||
}
|
||||
SetDlgItemText(_hWnd, IDC_EDITSTACK, stack.c_str());
|
||||
|
||||
_activeAddr = cpuState.PC;
|
||||
GoToAddr(cpuState.PC);
|
||||
SetActiveStatement();
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DebugWindow::DebuggerWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
NMLVDISPINFO *lpNmlvdispInfo;
|
||||
LPNMLISTVIEW pnmv;
|
||||
|
||||
switch(message) {
|
||||
case WM_INITDIALOG:
|
||||
DebugWindow::Instance->InitializeDialog(hDlg);
|
||||
return (INT_PTR)FALSE;
|
||||
|
||||
case WM_SHOWWINDOW:
|
||||
DebugWindow::Instance->_debugger->Step(1);
|
||||
break;
|
||||
case WM_APPCOMMAND:
|
||||
switch(GET_APPCOMMAND_LPARAM(lParam)) {
|
||||
case APPCOMMAND_BROWSER_BACKWARD:
|
||||
|
||||
break;
|
||||
|
||||
case APPCOMMAND_BROWSER_FORWARD:
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch(LOWORD(wParam)) {
|
||||
case ID_DEBUG_RUN:
|
||||
DebugWindow::Instance->ClearLineStyling(DebugWindow::Instance->_lastActiveLine);
|
||||
DebugWindow::Instance->_debugger->Run();
|
||||
break;
|
||||
|
||||
case ID_DEBUG_STEPINTO:
|
||||
case ID_DEBUG_STEPOVER:
|
||||
DebugWindow::Instance->_debugger->Step(1);
|
||||
break;
|
||||
|
||||
case ID_DEBUG_STEPOUT:
|
||||
break;
|
||||
|
||||
case ID_DEBUG_RUNTOCURSOR:
|
||||
break;
|
||||
|
||||
case ID_DEBUG_TOGGLEBREAKPOINT:
|
||||
DebugWindow::Instance->ToggleBreakpoint();
|
||||
break;
|
||||
|
||||
case ID_VIEW_NAVIGATEBACKWARD:
|
||||
DebugWindow::Instance->GoToAddr(DebugWindow::Instance->_history.GoBack());
|
||||
break;
|
||||
|
||||
case ID_VIEW_NAVIGATEFORWARD:
|
||||
DebugWindow::Instance->GoToAddr(DebugWindow::Instance->_history.GoForward());
|
||||
break;
|
||||
|
||||
case ID_VIEW_GOTO:
|
||||
DialogBox(nullptr, MAKEINTRESOURCE(IDD_GOTOADDR), DebugWindow::Instance->_hWnd, GoToAddrWndProc);
|
||||
break;
|
||||
|
||||
case ID_VIEW_SHOWNEXTSTATEMENT:
|
||||
DebugWindow::Instance->GoToAddr(DebugWindow::Instance->_activeAddr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_UPDATECPUSTATUS:
|
||||
DebugWindow::Instance->UpdateCPUStatus(*(State*)wParam);
|
||||
break;
|
||||
|
||||
case WM_NOTIFY:
|
||||
switch(((LPNMHDR)lParam)->code) {
|
||||
case LVN_ENDLABELEDIT:
|
||||
lpNmlvdispInfo = (NMLVDISPINFO*)lParam;
|
||||
if(((LPNMHDR)lParam)->hwndFrom == DebugWindow::Instance->_watchList && lpNmlvdispInfo->item.pszText != nullptr) {
|
||||
/*int itemCount = SendMessage(GetDlgItem(DebugWindow::Instance->_hWnd, IDC_WATCHLIST), LVM_GETITEMCOUNT, 0, 0);
|
||||
if(lpNmlvdispInfo->item.iItem == itemCount - 1) {
|
||||
//Editing the last one, which means we're adding a new item
|
||||
DebugWindow::Instance->InsertRow(GetDlgItem(DebugWindow::Instance->_hWnd, IDC_WATCHLIST), L"");
|
||||
}*/
|
||||
SetWindowLong(hDlg, DWL_MSGRESULT, true);
|
||||
return true;
|
||||
}
|
||||
case LVN_ITEMCHANGED:
|
||||
pnmv = (LPNMLISTVIEW)lParam;
|
||||
if((pnmv->uNewState & 0x1000) == 0x1000 && (pnmv->uOldState & 0x2000) == 0x2000) {
|
||||
//Unchecked
|
||||
((Breakpoint*)pnmv->lParam)->SetEnabled(false);
|
||||
} else if((pnmv->uOldState & 0x1000) == 0x1000 && (pnmv->uNewState & 0x2000) == 0x2000) {
|
||||
//Checked
|
||||
((Breakpoint*)pnmv->lParam)->SetEnabled(true);
|
||||
}
|
||||
break;
|
||||
case NM_DBLCLK:
|
||||
if(((LPNMHDR)lParam)->hwndFrom == DebugWindow::Instance->_breakpointList) {
|
||||
DebugWindow::Instance->EditBreakpoint();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DebugWindow::RichEditWndProc(HWND hEdit, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
wstring currentWord;
|
||||
|
||||
switch(message) {
|
||||
case WM_MOUSEMOVE:
|
||||
DebugWindow::Instance->GenerateTooltip(lParam);
|
||||
break;
|
||||
|
||||
case WM_MOUSEWHEEL:
|
||||
//Disable smooth scrolling (scroll 1 line at a time)
|
||||
if((short)HIWORD(wParam) < 0) {
|
||||
SendMessage(hEdit, WM_VSCROLL, SB_LINEDOWN, 0);
|
||||
} else {
|
||||
SendMessage(hEdit, WM_VSCROLL, SB_LINEUP, 0);
|
||||
}
|
||||
return true;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
if(DebugWindow::Instance->_clickAddr >= 0) {
|
||||
DebugWindow::Instance->_history.AddLocation(DebugWindow::Instance->_activeAddr);
|
||||
DebugWindow::Instance->GoToAddr(DebugWindow::Instance->_clickAddr);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SETCURSOR:
|
||||
static HCURSOR handCursor = LoadCursor(NULL, IDC_HAND);
|
||||
if(DebugWindow::Instance->_clickAddr >= 0) {
|
||||
SetCursor(handCursor);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CallWindowProc(OriginalRichEditWndProc, hEdit, message,wParam,lParam);
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DebugWindow::GoToAddrWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(lParam);
|
||||
HWND hEdit = GetDlgItem(hDlg, IDC_EDITADDRESS);
|
||||
switch(message) {
|
||||
case WM_INITDIALOG:
|
||||
SetFocus(hEdit);
|
||||
return (INT_PTR)FALSE;
|
||||
|
||||
case WM_COMMAND:
|
||||
if(LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
||||
if(LOWORD(wParam) == IDOK) {
|
||||
wchar_t addressText[1000];
|
||||
GetWindowText(hEdit, addressText, sizeof(addressText));
|
||||
if(!DebugWindow::Instance->GoToAddr(addressText)) {
|
||||
MessageBox(hDlg, L"Invalid address.", L"Error", MB_OK | MB_ICONEXCLAMATION);
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
}
|
||||
EndDialog(hDlg, LOWORD(wParam));
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DebugWindow::BreakpointWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
HWND hEdit = GetDlgItem(hDlg, IDC_EDITBreakAddress);
|
||||
static Breakpoint *bp = nullptr;
|
||||
switch(message) {
|
||||
case WM_INITDIALOG:
|
||||
bp = (Breakpoint*)lParam;
|
||||
DebugWindow::Instance->UpdateBreakpointWindow(hDlg, bp);
|
||||
SetFocus(hEdit);
|
||||
return (INT_PTR)FALSE;
|
||||
|
||||
case WM_COMMAND:
|
||||
if(LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
||||
if(LOWORD(wParam) == IDOK) {
|
||||
DebugWindow::Instance->CommitBreakpointChanges(hDlg, bp);
|
||||
}
|
||||
EndDialog(hDlg, LOWORD(wParam));
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
void DebugWindow::ProcessNotification(ConsoleNotificationType type)
|
||||
{
|
||||
switch(type) {
|
||||
case ConsoleNotificationType::CodeBreak:
|
||||
if(_debugger) {
|
||||
SendMessage(_hWnd, WM_UPDATECPUSTATUS, (WPARAM)&_debugger->GetCPUState(), 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
101
GUI/DebugWindow.h
Normal file
101
GUI/DebugWindow.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Resource.h"
|
||||
#include "../Core/INotificationListener.h"
|
||||
#include "NavigationHistory.h"
|
||||
|
||||
struct State;
|
||||
class Debugger;
|
||||
class Breakpoint;
|
||||
|
||||
struct Color
|
||||
{
|
||||
uint8_t R;
|
||||
uint8_t G;
|
||||
uint8_t B;
|
||||
};
|
||||
|
||||
class DebugWindow : public INotificationListener
|
||||
{
|
||||
private:
|
||||
static DebugWindow* Instance;
|
||||
|
||||
NavigationHistory _history;
|
||||
|
||||
HWND _hWnd = nullptr;
|
||||
shared_ptr<Debugger> _debugger;
|
||||
HWND _richEdit = nullptr;
|
||||
HWND _toolTipWnd = nullptr;
|
||||
|
||||
HWND _watchList = nullptr;
|
||||
HWND _breakpointList = nullptr;
|
||||
|
||||
HBRUSH _bgBrush;
|
||||
|
||||
int32_t _lastActiveLine;
|
||||
int32_t _clickAddr;
|
||||
int32_t _activeAddr;
|
||||
|
||||
private:
|
||||
static WNDPROC OriginalRichEditWndProc;
|
||||
static INT_PTR CALLBACK RichEditWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK DebuggerWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK GoToAddrWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK BreakpointWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
void InitializeDialog(HWND hDlg);
|
||||
|
||||
void UpdateCPUStatus(State cpuState);
|
||||
|
||||
void InsertColumn(HWND listView, LPWSTR text, int width);
|
||||
void InsertRow(HWND listView, vector<wstring> columnText, LPARAM lParam = NULL, bool checked = false);
|
||||
|
||||
void InitializeWatch();
|
||||
void RefreshWatchList();
|
||||
|
||||
void InitializeBreakpointList();
|
||||
void RefreshBreakpointList();
|
||||
void EditBreakpoint();
|
||||
void UpdateBreakpointWindow(HWND bpWnd, Breakpoint* breakpoint);
|
||||
void CommitBreakpointChanges(HWND bpWnd, Breakpoint* breakpoint);
|
||||
|
||||
void RefreshDisassembly();
|
||||
|
||||
wstring GetWordUnderMouse(HWND hEdit, int x, int y);
|
||||
void CreateToolTipForRect();
|
||||
|
||||
void GenerateTooltip(LPARAM lParam);
|
||||
void ShowTooltip(wstring text, int x, int y);
|
||||
void HideTooltip();
|
||||
|
||||
int SetLineColor(Color textColor, Color bgColor, int32_t lineNumber = -1);
|
||||
void SetLineSymbol(int16_t symbolCode, Color symbolColor, int32_t lineNumber = -1);
|
||||
void SetBreakpoint(int32_t lineNumber = -1);
|
||||
void SetActiveStatement(int32_t lineNumber = -1);
|
||||
void ClearLineStyling(int32_t lineNumber = -1);
|
||||
|
||||
int GetCurrentLine();
|
||||
int32_t GetAddrCharIndex(uint32_t addr);
|
||||
uint32_t GetAddrLineNumber(uint32_t addr);
|
||||
uint32_t GetLineAddr(int32_t lineNumber);
|
||||
|
||||
void ToggleBreakpoint();
|
||||
|
||||
string GetRTFColor(Color color);
|
||||
|
||||
void GoToAddr(uint32_t addr);
|
||||
bool GoToAddr(wstring addrText);
|
||||
wstring IntToHex(int value, int minWidth);
|
||||
uint32_t HexToInt(wstring hex);
|
||||
|
||||
public:
|
||||
DebugWindow(HWND parentWnd, shared_ptr<Debugger> debugger);
|
||||
~DebugWindow();
|
||||
|
||||
HWND GetHWND()
|
||||
{
|
||||
return _hWnd;
|
||||
}
|
||||
|
||||
virtual void ProcessNotification(ConsoleNotificationType type);
|
||||
};
|
BIN
GUI/GUI.rc
BIN
GUI/GUI.rc
Binary file not shown.
|
@ -98,6 +98,7 @@
|
|||
</FxCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DebugWindow.h" />
|
||||
<ClInclude Include="DirectXTK\Audio.h" />
|
||||
<ClInclude Include="DirectXTK\CommonStates.h" />
|
||||
<ClInclude Include="DirectXTK\DDSTextureLoader.h" />
|
||||
|
@ -115,6 +116,7 @@
|
|||
<ClInclude Include="DirectXTK\XboxDDSTextureLoader.h" />
|
||||
<ClInclude Include="GamePad.h" />
|
||||
<ClInclude Include="InputManager.h" />
|
||||
<ClInclude Include="NavigationHistory.h" />
|
||||
<ClInclude Include="Renderer.h" />
|
||||
<ClInclude Include="Resource.h" />
|
||||
<ClInclude Include="SoundManager.h" />
|
||||
|
@ -122,6 +124,7 @@
|
|||
<ClInclude Include="MainWindow.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="DebugWindow.cpp" />
|
||||
<ClCompile Include="GamePad.cpp" />
|
||||
<ClCompile Include="InputManager.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
<Filter Include="Header Files\DirectXTK">
|
||||
<UniqueIdentifier>{e3fb11e9-60dd-47df-8444-72d62eb07828}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Debugger">
|
||||
<UniqueIdentifier>{5c2b5caf-e71a-40cc-823a-625c65ee7cb2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
|
@ -84,6 +87,12 @@
|
|||
<ClInclude Include="SoundManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DebugWindow.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NavigationHistory.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@ -107,6 +116,9 @@
|
|||
<ClCompile Include="SoundManager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DebugWindow.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GUI.rc">
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../Utilities/ConfigManager.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "InputManager.h"
|
||||
#include "DebugWindow.h"
|
||||
|
||||
using namespace DirectX;
|
||||
|
||||
|
@ -75,14 +76,23 @@ namespace NES
|
|||
ControlManager::RegisterControlDevice(&inputManager2, 1);
|
||||
|
||||
HACCEL hAccel = LoadAccelerators(_hInstance, MAKEINTRESOURCE(IDC_Accelerator));
|
||||
if(hAccel == nullptr) {
|
||||
//error
|
||||
HACCEL hAccelDebugger = LoadAccelerators(_hInstance, MAKEINTRESOURCE(IDR_AcceleratorDebugger));
|
||||
if(hAccel == nullptr || hAccelDebugger == nullptr) {
|
||||
std::cout << "error";
|
||||
}
|
||||
|
||||
MSG msg = { 0 };
|
||||
while(WM_QUIT != msg.message) {
|
||||
if(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
if(_debugWindow != nullptr) {
|
||||
if(TranslateAccelerator(_debugWindow->GetHWND(), hAccelDebugger, &msg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(IsDialogMessage(_debugWindow->GetHWND(), &msg)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!TranslateAccelerator(_hWnd, hAccel, &msg)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
|
@ -161,7 +171,7 @@ namespace NES
|
|||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
|
||||
INT_PTR CALLBACK MainWindow::ConnectWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(lParam);
|
||||
|
@ -638,6 +648,10 @@ namespace NES
|
|||
mainWindow->_playingMovie = false;
|
||||
break;
|
||||
|
||||
case ID_TOOLS_DEBUGGER:
|
||||
mainWindow->_debugWindow.reset(new DebugWindow(hWnd, mainWindow->_console->GetDebugger()));
|
||||
break;
|
||||
|
||||
case ID_NETPLAY_STARTSERVER:
|
||||
GameServer::StartServer();
|
||||
break;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../Utilities/ConfigManager.h"
|
||||
#include "../Core/GameServer.h"
|
||||
#include "../Core/GameClient.h"
|
||||
#include "DebugWindow.h"
|
||||
|
||||
namespace NES {
|
||||
class MainWindow : public INotificationListener
|
||||
|
@ -24,6 +25,8 @@ namespace NES {
|
|||
wstring _currentROM;
|
||||
wstring _currentROMName;
|
||||
|
||||
unique_ptr<DebugWindow> _debugWindow;
|
||||
|
||||
int _currentSaveSlot = 0;
|
||||
|
||||
bool _runningTests = false;
|
||||
|
@ -38,8 +41,6 @@ namespace NES {
|
|||
static INT_PTR CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static wstring MainWindow::VKToString(int vk);
|
||||
|
||||
static MainWindow* GetInstance() { return MainWindow::Instance; }
|
||||
|
||||
void SaveTestResult();
|
||||
|
|
45
GUI/NavigationHistory.h
Normal file
45
GUI/NavigationHistory.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class NavigationHistory
|
||||
{
|
||||
private:
|
||||
vector<uint32_t> _history;
|
||||
int _position;
|
||||
|
||||
public:
|
||||
NavigationHistory()
|
||||
{
|
||||
_position = -1;
|
||||
}
|
||||
|
||||
void AddLocation(uint32_t addr)
|
||||
{
|
||||
//Erase forward history
|
||||
if(!_history.empty()) {
|
||||
_history.erase(_history.begin() + _position + 1, _history.end());
|
||||
}
|
||||
|
||||
_history.push_back(addr);
|
||||
_position++;
|
||||
}
|
||||
|
||||
int32_t GoBack()
|
||||
{
|
||||
if(_position == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _history[--_position];
|
||||
}
|
||||
|
||||
int32_t GoForward()
|
||||
{
|
||||
if(_position >= (int)_history.size() - 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _history[++_position];
|
||||
}
|
||||
|
||||
};
|
BIN
GUI/resource.h
BIN
GUI/resource.h
Binary file not shown.
|
@ -50,8 +50,12 @@
|
|||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
using std::thread;
|
||||
using std::list;
|
||||
using std::vector;
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
using std::shared_ptr;
|
||||
using std::wstring;
|
||||
using std::string;
|
|
@ -4,7 +4,7 @@
|
|||
class SimpleLock
|
||||
{
|
||||
private:
|
||||
size_t _holderThreadID;
|
||||
DWORD _holderThreadID;
|
||||
uint32_t _lockCount;
|
||||
atomic_flag _lock;
|
||||
|
||||
|
@ -14,11 +14,12 @@ public:
|
|||
_lockCount = 0;
|
||||
_holderThreadID = ~0;
|
||||
}
|
||||
|
||||
void Acquire()
|
||||
{
|
||||
if(_lockCount == 0 || _holderThreadID != std::this_thread::get_id().hash()) {
|
||||
if(_lockCount == 0 || _holderThreadID != GetCurrentThreadId()) {
|
||||
while(_lock.test_and_set());
|
||||
_holderThreadID = std::this_thread::get_id().hash();
|
||||
_holderThreadID = GetCurrentThreadId();
|
||||
_lockCount = 1;
|
||||
} else {
|
||||
//Same thread can acquire the same lock multiple times
|
||||
|
@ -44,7 +45,7 @@ public:
|
|||
|
||||
void Release()
|
||||
{
|
||||
if(_lockCount > 0 && _holderThreadID == std::this_thread::get_id().hash()) {
|
||||
if(_lockCount > 0 && _holderThreadID == GetCurrentThreadId()) {
|
||||
_lockCount--;
|
||||
if(_lockCount == 0) {
|
||||
_holderThreadID = ~0;
|
||||
|
|
Loading…
Add table
Reference in a new issue