Unfinished visual debugger implementation (to be finished later)

This commit is contained in:
Souryo 2015-06-24 19:26:19 -04:00
parent 366d4aec80
commit f8f9755eff
29 changed files with 1714 additions and 33 deletions

View file

@ -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
View 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
View 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;
};

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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);

View file

@ -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" />

View file

@ -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
View 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
View 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
View 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
View 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
View 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();
};

View file

@ -9,6 +9,7 @@ enum class ConsoleNotificationType
GamePaused = 3,
GameResumed = 4,
GameStopped = 5,
CodeBreak = 6,
};
class INotificationListener

View file

@ -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;

View file

@ -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);

View file

@ -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
View 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
View 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);
};

Binary file not shown.

View file

@ -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" />

View file

@ -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">

View file

@ -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;

View file

@ -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
View 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];
}
};

Binary file not shown.

View file

@ -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;

View file

@ -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;