initial commit
This commit is contained in:
commit
ff8389a76b
46 changed files with 9118 additions and 0 deletions
101
src/debugger/CPUWatcher.cpp
Normal file
101
src/debugger/CPUWatcher.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "CPUWatcher.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "../CPU.hpp"
|
||||
|
||||
CPUWatcher::CPUWatcher(CPU* cpu) :
|
||||
DebugWindow("CPU Watch"), cpu(cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void CPUWatcher::OnRender()
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin(title.c_str(), &isOpen))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::Text("Registers");
|
||||
if (ImGui::BeginTable("Registers", 6))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("A");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("X");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Y");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("PC");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("SP");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("P");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02X", cpu->acc);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02X", cpu->idx);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02X", cpu->idy);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%04X", cpu->pc);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02X", cpu->sp);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02X", cpu->status.Raw);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Status Flag Breakdown");
|
||||
if (ImGui::BeginTable("Status", 8))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("N");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("V");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("U");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("B");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("D");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("I");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Z");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("C");
|
||||
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.Negative ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.Overflow ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.NoEffect ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.Break ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.Decimal ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.InterruptDisable ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.Zero ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(cpu->status.Flag.Carry ? "1" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Text("Halted: %s", cpu->halted ? "Yes" : "No");
|
||||
|
||||
ImGui::End();
|
||||
}
|
17
src/debugger/CPUWatcher.hpp
Normal file
17
src/debugger/CPUWatcher.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "DebugWindow.hpp"
|
||||
|
||||
class CPU;
|
||||
|
||||
class CPUWatcher :
|
||||
public DebugWindow
|
||||
{
|
||||
public:
|
||||
CPUWatcher(CPU* cpu);
|
||||
|
||||
virtual void OnRender() override;
|
||||
|
||||
private:
|
||||
CPU* cpu;
|
||||
};
|
19
src/debugger/DebugWindow.hpp
Normal file
19
src/debugger/DebugWindow.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class DebugWindow
|
||||
{
|
||||
public:
|
||||
DebugWindow(const std::string& title) :
|
||||
title(title)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~DebugWindow() = default;
|
||||
virtual void OnRender() = 0;
|
||||
|
||||
public:
|
||||
const std::string title;
|
||||
bool isOpen = false;
|
||||
};
|
127
src/debugger/Debugger.cpp
Normal file
127
src/debugger/Debugger.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
#include "Debugger.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include "../Bus.hpp"
|
||||
#include "CPUWatcher.hpp"
|
||||
#include "PPUWatcher.hpp"
|
||||
#include "Disassembler.hpp"
|
||||
#include "MemoryViewer.hpp"
|
||||
#include "NametableViewer.hpp"
|
||||
|
||||
Debugger::Debugger(Bus* bus) :
|
||||
bus(bus)
|
||||
{
|
||||
windows.push_back(new CPUWatcher(&bus->cpu));
|
||||
windows.push_back(new PPUWatcher(&bus->ppu));
|
||||
windows.push_back(new Disassembler(&bus->cpu));
|
||||
windows.push_back(new MemoryViewer(bus));
|
||||
windows.push_back(new NametableViewer(bus));
|
||||
}
|
||||
|
||||
Debugger::~Debugger()
|
||||
{
|
||||
for (DebugWindow* window : windows)
|
||||
delete window;
|
||||
}
|
||||
|
||||
bool Debugger::Update()
|
||||
{
|
||||
if (running)
|
||||
return bus->Frame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Debugger::Render()
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 600), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("Debugger", (bool*)0, ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("Tools"))
|
||||
{
|
||||
for (DebugWindow* window : windows)
|
||||
{
|
||||
ImGui::MenuItem(window->title.c_str(), NULL, &window->isOpen);
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::CollapsingHeader("Controls", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, running);
|
||||
if (running)
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{ 0.7f, 0.7f, 0.7f, 0.7f });
|
||||
|
||||
if (ImGui::Button("Single Step"))
|
||||
bus->Instruction();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Single Frame"))
|
||||
bus->Frame();
|
||||
|
||||
if (running)
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(running ? "Pause" : "Run"))
|
||||
running = !running;
|
||||
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, running);
|
||||
if (running)
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{ 0.7f, 0.7f, 0.7f, 0.7f });
|
||||
|
||||
if (ImGui::Button("Reset"))
|
||||
{
|
||||
bus->Reset();
|
||||
if (overrideResetVector)
|
||||
bus->cpu.pc.Raw = resetVector;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Reboot"))
|
||||
{
|
||||
bus->Reboot();
|
||||
if (overrideResetVector)
|
||||
bus->cpu.pc.Raw = resetVector;
|
||||
}
|
||||
|
||||
if (running)
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Text("Override Reset Vector: ");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(overrideResetVector ? "Yes" : "No"))
|
||||
overrideResetVector = !overrideResetVector;
|
||||
|
||||
ImGui::InputScalar("Reset Vector", ImGuiDataType_U16, &resetVector, (const void*)0, (const void*)0, "%04X", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
}
|
||||
|
||||
for (DebugWindow* window : windows)
|
||||
{
|
||||
if (window->isOpen) window->OnRender();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
27
src/debugger/Debugger.hpp
Normal file
27
src/debugger/Debugger.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "DebugWindow.hpp"
|
||||
|
||||
class Bus;
|
||||
|
||||
class Debugger
|
||||
{
|
||||
public:
|
||||
Debugger(Bus* bus);
|
||||
~Debugger();
|
||||
|
||||
bool Update();
|
||||
void Render();
|
||||
|
||||
public:
|
||||
bool isOpen = true;
|
||||
|
||||
private:
|
||||
Bus* bus;
|
||||
bool running = false;
|
||||
bool overrideResetVector = false;
|
||||
uint16_t resetVector = 0x0000;
|
||||
|
||||
std::vector<DebugWindow*> windows;
|
||||
};
|
177
src/debugger/Disassembler.cpp
Normal file
177
src/debugger/Disassembler.cpp
Normal file
|
@ -0,0 +1,177 @@
|
|||
#include "Disassembler.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <imgui/imgui.h>
|
||||
#include "../Mapper.hpp"
|
||||
#include "../CPU.hpp"
|
||||
|
||||
#define FORMAT std::setfill('0') << std::setw(4) << std::hex << std::uppercase
|
||||
|
||||
Disassembler::Disassembler(CPU* cpu) :
|
||||
DebugWindow("Disassembler"), cpu(cpu)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Disassembler::OnRender()
|
||||
{
|
||||
static bool scrollCenter = true;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
||||
|
||||
if (!ImGui::Begin(title.c_str(), &isOpen))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string disassembly;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{ 0.8f, 0.8f, 0.8f, 1.0f });
|
||||
if (cpu->pastPCs.size() < 50)
|
||||
{
|
||||
for (int i = 0; i < 50 - cpu->pastPCs.size(); i++)
|
||||
{
|
||||
ImGui::Text("-");
|
||||
}
|
||||
}
|
||||
|
||||
for (Word pc : cpu->pastPCs)
|
||||
{
|
||||
Disassemble(disassembly, pc);
|
||||
ImGui::Text("- %s", disassembly.c_str());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
uint16_t pc = cpu->pc.Raw;
|
||||
Disassemble(disassembly, pc);
|
||||
ImGui::Text("> %s", disassembly.c_str());
|
||||
|
||||
if (scrollCenter)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
scrollCenter = false;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{ 0.8f, 0.8f, 0.8f, 1.0f });
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
Disassemble(disassembly, pc);
|
||||
ImGui::Text("+ %s", disassembly.c_str());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void Disassembler::Disassemble(std::string& target, uint16_t& pc)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << FORMAT << pc << ": ";
|
||||
Instruction* currentInstr = &cpu->InstructionTable[cpu->Read(pc)];
|
||||
|
||||
for (int i = 0; i < currentInstr->Size; i++)
|
||||
{
|
||||
ss << FORMAT << std::setw(2) << (Word)cpu->Read(pc + i) << " ";
|
||||
}
|
||||
ss << std::string(15 - ss.str().size(), ' ') << currentInstr->Mnemonic << " ";
|
||||
|
||||
Address absoluteAddress;
|
||||
switch (currentInstr->AddrType)
|
||||
{
|
||||
case Addressing::ACC:
|
||||
ss << "A";
|
||||
break;
|
||||
|
||||
case Addressing::ABS:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
absoluteAddress.Bytes.hi = cpu->Read(pc + 2);
|
||||
|
||||
ss << "$" << FORMAT << absoluteAddress.Raw;
|
||||
} break;
|
||||
|
||||
case Addressing::ABX:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
absoluteAddress.Bytes.hi = cpu->Read(pc + 2);
|
||||
|
||||
ss << "$" << FORMAT << absoluteAddress.Raw << ",X";
|
||||
} break;
|
||||
|
||||
case Addressing::ABY:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
absoluteAddress.Bytes.hi = cpu->Read(pc + 2);
|
||||
|
||||
ss << "$" << FORMAT << absoluteAddress.Raw << ",Y";
|
||||
} break;
|
||||
|
||||
case Addressing::IMM:
|
||||
{
|
||||
Word value = cpu->Read(pc + 1);
|
||||
|
||||
ss << "#$" << FORMAT << std::setw(2) << value;
|
||||
} break;
|
||||
|
||||
case Addressing::IMP:
|
||||
break;
|
||||
|
||||
case Addressing::IND:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
absoluteAddress.Bytes.hi = cpu->Read(pc + 2);
|
||||
|
||||
ss << "($" << FORMAT << absoluteAddress.Raw << ")";
|
||||
} break;
|
||||
|
||||
case Addressing::IDX:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
|
||||
ss << "($" << FORMAT << std::setw(2) << (Word)absoluteAddress.Bytes.lo << ",X)";
|
||||
} break;
|
||||
|
||||
case Addressing::IDY:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
|
||||
ss << "($" << FORMAT << std::setw(2) << (Word)absoluteAddress.Bytes.lo << "),Y";
|
||||
} break;
|
||||
|
||||
case Addressing::REL:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
|
||||
ss << "$" << FORMAT << std::setw(2) << (Word)absoluteAddress.Bytes.lo;
|
||||
} break;
|
||||
|
||||
case Addressing::ZPG:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
|
||||
ss << "$" << FORMAT << std::setw(2) << (Word)absoluteAddress.Bytes.lo;
|
||||
} break;
|
||||
|
||||
case Addressing::ZPX:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
|
||||
ss << "$" << FORMAT << std::setw(2) << (Word)absoluteAddress.Bytes.lo << ",X";
|
||||
} break;
|
||||
|
||||
case Addressing::ZPY:
|
||||
{
|
||||
absoluteAddress.Bytes.lo = cpu->Read(pc + 1);
|
||||
|
||||
ss << "$" << FORMAT << std::setw(2) << (Word)absoluteAddress.Bytes.lo << ",Y";
|
||||
} break;
|
||||
}
|
||||
|
||||
pc += currentInstr->Size;
|
||||
target = ss.str();
|
||||
}
|
20
src/debugger/Disassembler.hpp
Normal file
20
src/debugger/Disassembler.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "DebugWindow.hpp"
|
||||
|
||||
class CPU;
|
||||
|
||||
class Disassembler :
|
||||
public DebugWindow
|
||||
{
|
||||
public:
|
||||
Disassembler(CPU* cpu);
|
||||
|
||||
virtual void OnRender() override;
|
||||
|
||||
private:
|
||||
void Disassemble(std::string& target, uint16_t& pc);
|
||||
|
||||
private:
|
||||
CPU* cpu;
|
||||
};
|
78
src/debugger/MemoryViewer.cpp
Normal file
78
src/debugger/MemoryViewer.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "MemoryViewer.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "../Bus.hpp"
|
||||
|
||||
MemoryViewer::MemoryViewer(Bus* bus) :
|
||||
DebugWindow("Memory Viewer"), bus(bus)
|
||||
{
|
||||
}
|
||||
|
||||
void MemoryViewer::OnRender()
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
||||
|
||||
if (!ImGui::Begin(title.c_str(), &isOpen))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginTabBar("Pages");
|
||||
|
||||
for (Byte page = 0; page < 8; page++)
|
||||
{
|
||||
char title[7];
|
||||
std::sprintf(title, "Page %d", page);
|
||||
if (ImGui::BeginTabItem(title))
|
||||
{
|
||||
DrawPage(page);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void MemoryViewer::DrawPage(Byte page)
|
||||
{
|
||||
Word baseAddr = ((Word)page << 8);
|
||||
if (ImGui::BeginTable("memorymap", 17))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
for (Byte header = 0x0; header < 0x10; header++)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%X", header);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
for (Byte hi = 0x0; hi <= 0xF; hi++)
|
||||
{
|
||||
Byte hiOffset = hi << 4;
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
ImGui::Text("%04X", baseAddr | hiOffset);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
for (Byte lo = 0x0; lo <= 0xF; lo++)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
Byte entry = bus->RAM[baseAddr | hiOffset | lo];
|
||||
|
||||
if (entry == 0x00)
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
|
||||
ImGui::Text("%02X", entry);
|
||||
|
||||
if (entry == 0x00)
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
21
src/debugger/MemoryViewer.hpp
Normal file
21
src/debugger/MemoryViewer.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "DebugWindow.hpp"
|
||||
#include "../Types.hpp"
|
||||
|
||||
class Bus;
|
||||
|
||||
class MemoryViewer :
|
||||
public DebugWindow
|
||||
{
|
||||
public:
|
||||
MemoryViewer(Bus* bus);
|
||||
|
||||
virtual void OnRender() override;
|
||||
|
||||
private:
|
||||
void DrawPage(Byte page);
|
||||
|
||||
private:
|
||||
Bus* bus;
|
||||
};
|
78
src/debugger/NametableViewer.cpp
Normal file
78
src/debugger/NametableViewer.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "NametableViewer.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "../Bus.hpp"
|
||||
|
||||
NametableViewer::NametableViewer(Bus* bus) :
|
||||
DebugWindow("Nametable Viewer"), bus(bus)
|
||||
{
|
||||
}
|
||||
|
||||
void NametableViewer::OnRender()
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin(title.c_str(), &isOpen))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginTabBar("Nametables");
|
||||
for (uint8_t index = 0; index < 4; index++)
|
||||
{
|
||||
char baseAddress[6];
|
||||
std::sprintf(baseAddress, "$2%X00", index * 4);
|
||||
if (ImGui::BeginTabItem(baseAddress))
|
||||
{
|
||||
DisplayNametable(index);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void NametableViewer::DisplayNametable(uint8_t index)
|
||||
{
|
||||
Word baseAddr = 0x400 * index;
|
||||
Word displayBaseAddr = 0x2000 + baseAddr;
|
||||
if (ImGui::BeginTable("memorymap", 17))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
for (Byte header = 0x0; header < 0x10; header++)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%X", header);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
for (Word hi = 0x0; hi <= 0x3F; hi++)
|
||||
{
|
||||
Byte hiOffset = hi << 4;
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
ImGui::Text("%04X", displayBaseAddr | hiOffset);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
for (Byte lo = 0x0; lo <= 0xF; lo++)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
Byte entry = bus->VRAM[baseAddr | hiOffset | lo];
|
||||
|
||||
if (entry == 0x00)
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
|
||||
ImGui::Text("%02X", entry);
|
||||
|
||||
if (entry == 0x00)
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
20
src/debugger/NametableViewer.hpp
Normal file
20
src/debugger/NametableViewer.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "DebugWindow.hpp"
|
||||
|
||||
class Bus;
|
||||
|
||||
class NametableViewer :
|
||||
public DebugWindow
|
||||
{
|
||||
public:
|
||||
NametableViewer(Bus* bus);
|
||||
|
||||
virtual void OnRender() override;
|
||||
|
||||
private:
|
||||
void DisplayNametable(uint8_t index);
|
||||
|
||||
private:
|
||||
Bus* bus;
|
||||
};
|
152
src/debugger/PPUWatcher.cpp
Normal file
152
src/debugger/PPUWatcher.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include "PPUWatcher.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "../PPU.hpp"
|
||||
|
||||
PPUWatcher::PPUWatcher(PPU* ppu) :
|
||||
DebugWindow("PPU Watch"), ppu(ppu)
|
||||
{
|
||||
}
|
||||
|
||||
void PPUWatcher::OnRender()
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin(title.c_str(), &isOpen))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::Text("On Pixel (%d, %d)", ppu->x, ppu->y);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::CollapsingHeader("PPUCTRL"))
|
||||
{
|
||||
if (ImGui::BeginTable("ppuctrl", 2))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Base Nametable Addr");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("$%04X", 0x2000 + 0x400 * ppu->ppuctrl.Flag.BaseNametableAddr);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("VRAM Addr Increment");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", ppu->ppuctrl.Flag.VRAMAddrIncrement ? 32 : 1);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Sprite Pattern Table Addr");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("$%04X", ppu->ppuctrl.Flag.SpritePatternTableAddr ? 0x1000 : 0x0000);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Backgr Pattern Table Addr");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("$%04X", ppu->ppuctrl.Flag.BackgrPatternTableAddr ? 0x1000 : 0x0000);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Master/Slave");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppuctrl.Flag.MasterSlaveSelect ? "Output to EXT" : "Read from EXT");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("VBlank NMI Generation");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppuctrl.Flag.VBlankNMI ? "On" : "Off");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("PPUMASK"))
|
||||
{
|
||||
if (ImGui::BeginTable("ppumask", 2))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Greyscale");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppumask.Flag.Greyscale ? "On" : "Off");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Left Col Background");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppumask.Flag.BackgroundOnLeft ? "Show" : "Hide");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Left Col Sprites");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppumask.Flag.SpriteOnLeft ? "Show" : "Hide");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Show Background");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppumask.Flag.ShowBackground ? "On" : "Off");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Show Sprites");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppumask.Flag.ShowSprites ? "On" : "Off");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Emphasized Colors");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s%s%s", ppu->ppumask.Flag.EmphasizeRed ? "R" : "-", ppu->ppumask.Flag.EmphasizeGreen ? "G" : "-", ppu->ppumask.Flag.EmphasizeBlue ? "B" : "-");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("PPUSTATUS"))
|
||||
{
|
||||
if (ImGui::BeginTable("ppustatus", 2))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Lower 5 Bits");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02X", ppu->ppustatus.Flag.Unused);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Sprite Overflow");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppustatus.Flag.SpriteOverflow ? "Yes" : "No");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Sprite 0 Hit");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppustatus.Flag.SpriteZeroHit ? "Yes" : "No");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("VBlank Started");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ppu->ppustatus.Flag.VBlankStarted ? "Yes" : "No");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("PPUSCROLL & PPUADDR"))
|
||||
{
|
||||
if (ImGui::BeginTable("ppuscrolladdr", 2))
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Scroll X");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", ppu->ppuscroll.x);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Scroll Y");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", ppu->ppuscroll.x);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Address");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("$%04X", ppu->ppuaddr.Raw);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
17
src/debugger/PPUWatcher.hpp
Normal file
17
src/debugger/PPUWatcher.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "DebugWindow.hpp"
|
||||
|
||||
class PPU;
|
||||
|
||||
class PPUWatcher :
|
||||
public DebugWindow
|
||||
{
|
||||
public:
|
||||
PPUWatcher(PPU* ppu);
|
||||
|
||||
virtual void OnRender() override;
|
||||
|
||||
private:
|
||||
PPU* ppu;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue