diff --git a/src/Bus.cpp b/src/Bus.cpp index 1d7d94f..d26379b 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -52,6 +52,15 @@ uint8_t Bus::Tick() return result; } +void Bus::PPUTick() +{ + if (ppuClock == 0) + cpu.Tick(); + + ppu.Tick(); + ppuClock = (ppuClock + 1) % 3; +} + bool Bus::Instruction() { try diff --git a/src/Bus.hpp b/src/Bus.hpp index bbbeb8c..7fd5b7c 100644 --- a/src/Bus.hpp +++ b/src/Bus.hpp @@ -45,6 +45,8 @@ public: */ uint8_t Tick(); + void PPUTick(); + /** * @brief Advance the emulator by one CPU instruction. */ @@ -87,4 +89,6 @@ private: PPU ppu; Cartridge cartridge; ControllerPort controllerPort; + + uint8_t ppuClock = 0; }; diff --git a/src/PPU.cpp b/src/PPU.cpp index ebbf88d..a0c0205 100644 --- a/src/PPU.cpp +++ b/src/PPU.cpp @@ -16,9 +16,15 @@ void PPU::Powerup() ppuscroll.y = 0x00; ppuaddr.Raw = 0x0000; - x = 0; - y = 0; + // This will cause the Tick() functioon to set both to 0 on the first tick + x = 400; + y = 400; + fineX = 0; addressLatch = 0; + + scanlineType = ScanlineType::Visible; + cycleType = CycleType::Idle; + fetchPhase = FetchingPhase::NametableByte; } void PPU::Reset() @@ -28,13 +34,63 @@ void PPU::Reset() ppuscroll.x = 0x00; ppuscroll.y = 0x00; - x = 0; - y = 0; - addressLatch = 1; + // This will cause the Tick() functioon to set both to 0 on the first tick + x = 400; + y = 400; + fineX = 0; + addressLatch = 0; + + scanlineType = ScanlineType::Visible; + cycleType = CycleType::Idle; + fetchPhase = FetchingPhase::NametableByte; } void PPU::Tick() { + // Advance pixel counters + x++; + if (x > 340) + { + x = 0; + y++; + if (y > 261) + y = 0; + } + + current.CoarseX++; + if (current.CoarseX > 31) + { + current.CoarseX = 0; + current.NametableSel ^= 0x1; + } + + if (x == 256) + { + if (current.FineY < 7) + { + current.FineY++; + } + else + { + current.FineY = 0; + if (current.CoarseY == 29) + { + current.CoarseY = 0; + current.NametableSel ^= 0x2; + } + else if (current.CoarseY == 31) + { + current.CoarseY = 0; + } + else + { + current.CoarseY++; + } + } + } + + UpdateState(); + // On this cycle the VBlankStarted bit is set in the ppustatus if (y == 241 && x == 1) { @@ -50,15 +106,13 @@ void PPU::Tick() if (y == 261 && x == 1) ppustatus.Flag.VBlankStarted = 0; - // Advance pixel counters - x++; - if (x > 340) + + // Need to render + if (scanlineType == ScanlineType::Visible || scanlineType == ScanlineType::PreRender) { - x = 0; - y++; - if (y > 261) - y = 0; + PerformRenderAction(); } + } Byte PPU::ReadRegister(Byte id) @@ -107,6 +161,7 @@ void PPU::WriteRegister(Byte id, Byte val) { case 0: ppuctrl.Raw = val; + temporary.NametableSel = ppuctrl.Flag.BaseNametableAddr; break; case 1: @@ -122,18 +177,37 @@ void PPU::WriteRegister(Byte id, Byte val) // determines whether the hi or lo byte should be written next case 5: if (addressLatch == 0) + { ppuscroll.x = val; + temporary.CoarseX = (val >> 3); + fineX = val & 0x3; + } else + { ppuscroll.y = val; + temporary.CoarseY = (val >> 3); + temporary.FineY = val & 0x3; + } addressLatch = !addressLatch; break; case 6: if (addressLatch == 0) + { ppuaddr.Bytes.hi = val; + temporary.Raw &= 0xFF; + temporary.Raw |= ((Word)(val & 0x3F) << 8); + temporary.FineY &= 0x3; + } else + { ppuaddr.Bytes.lo = val; + temporary.Raw &= ((Word)0xFF << 8); + temporary.Raw |= val; + + current.Raw = temporary.Raw; + } addressLatch = !addressLatch; break; @@ -160,3 +234,84 @@ void PPU::Write(Word addr, Byte val) { bus->WritePPU(addr, val); } + +void PPU::UpdateState() +{ + switch (y) + { + case 0: + scanlineType = ScanlineType::Visible; + break; + + case 240: + scanlineType = ScanlineType::PostRender; + break; + + case 241: + scanlineType = ScanlineType::VBlank; + break; + + case 261: + scanlineType = ScanlineType::PreRender; + break; + } + + switch (x) + { + case 0: + cycleType = CycleType::Idle; + break; + + case 1: + cycleType = CycleType::Fetching; + break; + + case 257: + cycleType = CycleType::SpriteFetching; + break; + + case 321: + cycleType = CycleType::PreFetching; + break; + + case 337: + cycleType = CycleType::UnknownFetching; + break; + } +} + +void PPU::PerformRenderAction() +{ + if (cycleType == CycleType::Idle) + return; + + if (memoryAccessLatch == 1) + { + switch (fetchPhase) + { + case FetchingPhase::NametableByte: + nametableByte = Read(0x2000 | (current.Raw & 0x0FFF)); + + if(cycleType != CycleType::UnknownFetching) + fetchPhase = FetchingPhase::AttributeTableByte; + break; + + case FetchingPhase::AttributeTableByte: + attributeTableByte = Read(0x23C0 | (current.Raw & 0x0FFF) | ((current.Raw >> 4) & 0x38) | ((current.Raw >> 2) & 0x07)); + fetchPhase = FetchingPhase::PatternTableLo; + break; + + case FetchingPhase::PatternTableLo: + patternTableLo = Read(ppuctrl.Flag.BackgrPatternTableAddr | nametableByte); + fetchPhase = FetchingPhase::PatternTableHi; + break; + + case FetchingPhase::PatternTableHi: + patternTableLo = Read((ppuctrl.Flag.BackgrPatternTableAddr | nametableByte) + 8); + fetchPhase = FetchingPhase::NametableByte; + break; + } + } + + memoryAccessLatch = 1 - memoryAccessLatch; +} diff --git a/src/PPU.hpp b/src/PPU.hpp index 58cfc9f..fab4445 100644 --- a/src/PPU.hpp +++ b/src/PPU.hpp @@ -4,6 +4,44 @@ class Bus; +enum class ScanlineType +{ + PreRender, + Visible, + PostRender, + VBlank +}; + +enum class CycleType +{ + Idle, + Fetching, + SpriteFetching, + PreFetching, + UnknownFetching +}; + +enum class FetchingPhase +{ + NametableByte, + AttributeTableByte, + PatternTableLo, + PatternTableHi +}; + +union VRAMAddress +{ + struct + { + Byte CoarseX : 5; + Byte CoarseY : 5; + Byte NametableSel : 2; + Byte FineY : 3; + }; + + Word Raw; +}; + /** * @brief The PPU of the NES. */ @@ -59,6 +97,9 @@ private: */ void Write(Word addr, Byte val); + void UpdateState(); + void PerformRenderAction(); + private: // Registers union @@ -115,12 +156,25 @@ private: // Registers Address ppuaddr; - // Current x, y position the PPU operates on - uint16_t x, y; - Byte latch = 0; + VRAMAddress current{ 0 }; + VRAMAddress temporary{ 0 }; + uint16_t fineX; bool addressLatch = false; + Byte latch = 0; + + Word x, y; + Byte nametableByte = 0x00; + Byte attributeTableByte = 0x00; + Byte patternTableLo = 0x00; + Byte patternTableHi = 0x00; + private: + ScanlineType scanlineType; + CycleType cycleType; + FetchingPhase fetchPhase; + + uint8_t memoryAccessLatch = 0; bool isFrameDone = false; Bus* bus; }; diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp index c7e087f..7fe0f26 100644 --- a/src/debugger/Debugger.cpp +++ b/src/debugger/Debugger.cpp @@ -16,9 +16,13 @@ Debugger::Debugger(Bus* bus) : bus(bus) { windows.push_back(new CPUWatcher(this, &bus->cpu)); - windows.push_back(new PPUWatcher(this, &bus->ppu)); + + ppuWatcher = new PPUWatcher(this, &bus->ppu); + windows.push_back(ppuWatcher); + disassembler = new Disassembler(this, &bus->cpu); windows.push_back(disassembler); + windows.push_back(new MemoryViewer(this, bus)); windows.push_back(new NametableViewer(this, bus)); windows.push_back(new ControllerPortViewer(this, &bus->controllerPort)); @@ -37,7 +41,7 @@ bool Debugger::Frame() while (!bus->ppu.IsFrameDone()) { bus->Tick(); - if (disassembler->BreakpointHit()) + if (disassembler->BreakpointHit() || ppuWatcher->BreakpointHit()) { running = false; break; @@ -92,13 +96,18 @@ void Debugger::Render() if (running) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{ 0.7f, 0.7f, 0.7f, 0.7f }); + if (ImGui::Button("PPU Tick")) + bus->PPUTick(); + + ImGui::SameLine(); + if (ImGui::Button("Single Step")) bus->Instruction(); ImGui::SameLine(); if (ImGui::Button("Single Frame")) - bus->Frame(); + Frame(); if (running) ImGui::PopStyleColor(); diff --git a/src/debugger/Debugger.hpp b/src/debugger/Debugger.hpp index ff012d6..67b3913 100644 --- a/src/debugger/Debugger.hpp +++ b/src/debugger/Debugger.hpp @@ -5,6 +5,7 @@ class Bus; class Disassembler; +class PPUWatcher; class Debugger { @@ -27,4 +28,5 @@ private: std::vector windows; Disassembler* disassembler; + PPUWatcher* ppuWatcher; }; diff --git a/src/debugger/NametableViewer.cpp b/src/debugger/NametableViewer.cpp index 93624ab..f595fb0 100644 --- a/src/debugger/NametableViewer.cpp +++ b/src/debugger/NametableViewer.cpp @@ -98,7 +98,7 @@ void NametableViewer::DisplayNametable(uint8_t index) for (Word hi = 0x0; hi <= 0x3F; hi++) { - Byte hiOffset = hi << 4; + Word hiOffset = hi << 4; ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f)); diff --git a/src/debugger/PPUWatcher.cpp b/src/debugger/PPUWatcher.cpp index b7eb704..8a5f8a2 100644 --- a/src/debugger/PPUWatcher.cpp +++ b/src/debugger/PPUWatcher.cpp @@ -1,11 +1,38 @@ #include "PPUWatcher.hpp" +#include #include #include "../PPU.hpp" +static const std::map scanlineTypeNames = { + {ScanlineType::PreRender, "Pre-render"}, + {ScanlineType::Visible, "Visible"}, + {ScanlineType::PostRender, "Post-render"}, + {ScanlineType::VBlank, "VBlank"} +}; + +static const std::map cycleTypeNames = { + {CycleType::Idle, "Idle"}, + {CycleType::Fetching, "Fetching BG Tiles"}, + {CycleType::SpriteFetching, "Sprite Fetching"}, + {CycleType::PreFetching, "Pre-Fetching"}, + {CycleType::UnknownFetching, "Random Fetches"} +}; + +static const std::map fetchingPhaseNames = { + {FetchingPhase::NametableByte, "Nametable"}, + {FetchingPhase::AttributeTableByte, "Attribute Table"}, + {FetchingPhase::PatternTableLo, "Pattern Table (lo)"}, + {FetchingPhase::PatternTableHi, "Pattern Table (hi)"}, +}; + PPUWatcher::PPUWatcher(Debugger* debugger, PPU* ppu) : DebugWindow("PPU Watch", debugger), ppu(ppu) { + breakpoints.emplace_back(ScanlineType::PreRender, "Pre-Render"); + breakpoints.emplace_back(ScanlineType::Visible, "Visible"); + breakpoints.emplace_back(ScanlineType::PostRender, "Post-Render"); + breakpoints.emplace_back(ScanlineType::VBlank, "VBlank"); } void PPUWatcher::OnRender() @@ -18,135 +45,205 @@ void PPUWatcher::OnRender() } ImGui::Text("On Pixel (%d, %d)", ppu->x, ppu->y); + ImGui::Text("Scanline : %s", scanlineTypeNames.find(ppu->scanlineType)->second.c_str()); + ImGui::Text("Cycle : %s", cycleTypeNames.find(ppu->cycleType)->second.c_str()); + ImGui::Text("Fetching Phase: %s", fetchingPhaseNames.find(ppu->fetchPhase)->second.c_str()); ImGui::Separator(); - if (ImGui::CollapsingHeader("PPUCTRL")) + if (ImGui::CollapsingHeader("Internal Registers")) { - if (ImGui::BeginTable("ppuctrl", 2)) + ImGui::Text("Current VRAM Address (v) : %02X ($%04X)", ppu->current.Raw, 0x2000 | (ppu->current.Raw & 0x0FFF)); + if (ImGui::TreeNode("Breakdown (v)")) { - ImGui::TableNextColumn(); - ImGui::Text("Base Nametable Addr"); - ImGui::TableNextColumn(); - ImGui::Text("$%04X", 0x2000 + 0x400 * ppu->ppuctrl.Flag.BaseNametableAddr); + ImGui::Text("Coarse X : %02X", ppu->current.CoarseX); + ImGui::Text("Coarse Y : %02X", ppu->current.CoarseY); + ImGui::Text("Nametable: %02X", ppu->current.NametableSel); + ImGui::Text("Fine Y : %02X", ppu->current.FineY); - ImGui::TableNextColumn(); - ImGui::Text("VRAM Addr Increment"); - ImGui::TableNextColumn(); - ImGui::Text("%d", ppu->ppuctrl.Flag.VRAMAddrIncrement ? 32 : 1); + ImGui::TreePop(); + } - ImGui::TableNextColumn(); - ImGui::Text("Sprite Pattern Table Addr"); - ImGui::TableNextColumn(); - ImGui::Text("$%04X", ppu->ppuctrl.Flag.SpritePatternTableAddr ? 0x1000 : 0x0000); + ImGui::Text("Temporary VRAM Address (t): %02X ($%04X)", ppu->temporary.Raw, 0x2000 | (ppu->current.Raw & 0x0FFF)); + if (ImGui::TreeNode("Breakdown (t)")) + { + ImGui::Text("Coarse X : %02X", ppu->temporary.CoarseX); + ImGui::Text("Coarse Y : %02X", ppu->temporary.CoarseY); + ImGui::Text("Nametable: %02X", ppu->temporary.NametableSel); + ImGui::Text("Fine Y : %02X", ppu->temporary.FineY); - ImGui::TableNextColumn(); - ImGui::Text("Backgr Pattern Table Addr"); - ImGui::TableNextColumn(); - ImGui::Text("$%04X", ppu->ppuctrl.Flag.BackgrPatternTableAddr ? 0x1000 : 0x0000); + ImGui::TreePop(); + } - ImGui::TableNextColumn(); - ImGui::Text("Master/Slave"); - ImGui::TableNextColumn(); - ImGui::Text(ppu->ppuctrl.Flag.MasterSlaveSelect ? "Output to EXT" : "Read from EXT"); + ImGui::Text("Fine X Scroll : %02X", ppu->fineX); + ImGui::Text("Write Toggle Bit : %02X", ppu->addressLatch); + } - ImGui::TableNextColumn(); - ImGui::Text("VBlank NMI Generation"); - ImGui::TableNextColumn(); - ImGui::Text(ppu->ppuctrl.Flag.VBlankNMI ? "On" : "Off"); + if (ImGui::CollapsingHeader("Latches")) + { + ImGui::Text("Nametable Byte : %02X", ppu->nametableByte); + ImGui::Text("Attribute Byte : %02X", ppu->attributeTableByte); + ImGui::Text("Pattern Tile Lo: %02X", ppu->patternTableLo); + ImGui::Text("Pattern Tile Hi: %02X", ppu->patternTableHi); + } + + if (ImGui::CollapsingHeader("Registers")) + { + if (ImGui::TreeNode("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::EndTable(); + 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(); + ImGui::TreePop(); + } + } + + if (ImGui::TreeNode("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(); + ImGui::TreePop(); + } + } + + if (ImGui::TreeNode("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(); + ImGui::TreePop(); + } + } + + if (ImGui::TreeNode("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::TreePop(); + } } } - if (ImGui::CollapsingHeader("PPUMASK")) + if (ImGui::CollapsingHeader("Breakpoints")) { - if (ImGui::BeginTable("ppumask", 2)) + ImGui::Text("Break at beginning of"); + + for (FrameStateBreakpoint& breakpoint : breakpoints) { - 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::Checkbox(breakpoint.name.c_str(), &breakpoint.enabled); } } ImGui::End(); } + +bool PPUWatcher::BreakpointHit() +{ + if (ppu->x > 2) + return false; + + for (const FrameStateBreakpoint& breakpoint : breakpoints) + { + if (breakpoint.enabled && breakpoint.location == ppu->scanlineType) + return true; + } + + return false; +} diff --git a/src/debugger/PPUWatcher.hpp b/src/debugger/PPUWatcher.hpp index 26ceca7..24ac79e 100644 --- a/src/debugger/PPUWatcher.hpp +++ b/src/debugger/PPUWatcher.hpp @@ -1,8 +1,19 @@ #pragma once +#include #include "DebugWindow.hpp" +#include "../PPU.hpp" -class PPU; +struct FrameStateBreakpoint +{ + FrameStateBreakpoint(ScanlineType location, const std::string& name) : + location(location), name(name), enabled(false) + { } + + ScanlineType location; + std::string name; + bool enabled; +}; class PPUWatcher : public DebugWindow @@ -11,7 +22,9 @@ public: PPUWatcher(Debugger* debugger, PPU* ppu); virtual void OnRender() override; + bool BreakpointHit(); private: PPU* ppu; + std::vector breakpoints; };