initial commit

This commit is contained in:
Lauchmelder 2022-02-28 16:04:25 +01:00
commit ff8389a76b
No known key found for this signature in database
GPG key ID: C2403C69D78F011D
46 changed files with 9118 additions and 0 deletions

101
src/debugger/CPUWatcher.cpp Normal file
View 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();
}

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

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

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

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

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

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

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

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

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