diff --git a/.gitignore b/.gitignore index ec76eb1..db5d2a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ .vscode/ out/ -*.json \ No newline at end of file +*.json + +*.nes +!roms/nestest.nes \ No newline at end of file diff --git a/src/APU.cpp b/src/APU.cpp new file mode 100644 index 0000000..13a6ab1 --- /dev/null +++ b/src/APU.cpp @@ -0,0 +1,61 @@ +#include "APU.hpp" + +#include "Bus.hpp" + +APU::APU(Bus* bus) : + bus(bus) +{ +} + +void APU::Powerup() +{ + mode = false; + disableInterrupt = false; +} + +void APU::Reset() +{ + // Nothing for now +} + +void APU::Tick() +{ + if (cycles == 0) + sequencer = 0; + else + cycles--; + + if (!APUActionLatch) + { + APUActionLatch = !APUActionLatch; + return; + } + + if (sequencer == 14914 || sequencer == 14915) + { + if(!mode && !disableInterrupt) + bus->IRQ(); + + if (sequencer == 14915) + sequencer = 0; + } + sequencer++; + + APUActionLatch = !APUActionLatch; +} + +void APU::WriteRegister(Word addr, Byte val) +{ + switch (addr) + { + case 0x4017: + { + mode = ((val & 0x80) == 0x80); + disableInterrupt = ((val & 0x40) == 0x40); + + cycles = 3; + if (!APUActionLatch) + cycles++; + } break; + } +} diff --git a/src/APU.hpp b/src/APU.hpp new file mode 100644 index 0000000..14642ec --- /dev/null +++ b/src/APU.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Types.hpp" + +class Bus; + +class APU +{ +public: + APU(Bus* bus); + + void Powerup(); + void Reset(); + + void Tick(); + + void WriteRegister(Word addr, Byte val); + +private: + uint64_t sequencer = 0; + bool mode = false; + bool disableInterrupt = false; + + Byte cycles = 0; + bool APUActionLatch = true; + +private: + Bus* bus; +}; diff --git a/src/Bus.cpp b/src/Bus.cpp index 5e7d069..894893f 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -6,16 +6,17 @@ #include "controllers/StandardController.hpp" Bus::Bus(Screen* screen) : - cpu(this), ppu(this, screen), cartridge(this) + cpu(this), ppu(this, screen), apu(this), cartridge(this) { LOG_CORE_INFO("Allocating RAM"); RAM = std::vector(0x800); LOG_CORE_INFO("Allocating VRAM"); VRAM = std::vector(0x800); + palettes = std::vector(0x20, 0); LOG_CORE_INFO("Inserting cartridge"); - cartridge.Load("roms/donkeykong.nes"); + cartridge.Load("roms/mario.nes"); LOG_CORE_INFO("Powering up CPU"); cpu.Powerup(); @@ -23,6 +24,9 @@ Bus::Bus(Screen* screen) : LOG_CORE_INFO("Powering up PPU"); ppu.Powerup(); + LOG_CORE_INFO("Powering up APU"); + apu.Powerup(); + controllerPort.PlugInController(0); } @@ -30,12 +34,14 @@ void Bus::Reboot() { cpu.Powerup(); ppu.Powerup(); + apu.Powerup(); } void Bus::Reset() { cpu.Reset(); ppu.Reset(); + apu.Reset(); } uint8_t Bus::Tick() @@ -49,13 +55,20 @@ uint8_t Bus::Tick() ppu.Tick(); ppu.Tick(); + // APU is only ticked every 2 cycles, but that logic + // is handled inside the APU class + apu.Tick(); + return result; } void Bus::PPUTick() { if (ppuClock == 0) + { cpu.Tick(); + apu.Tick(); + } ppu.Tick(); ppuClock = (ppuClock + 1) % 3; @@ -129,13 +142,20 @@ Byte Bus::ReadPPU(Word addr) { return cartridge.ReadPPU(addr); } - else if(0x2000 <= addr && addr < 0x4000) + else if(0x2000 <= addr && addr < 0x3F00) { if (cartridge.MapCIRAM(addr)) return cartridge.ReadVRAM(addr); return VRAM[addr & 0xFFF]; } + else if (0x3F00 <= addr && addr < 0x4000) + { + if ((addr & 0x3) == 0x00) + addr &= 0xF; + + return palettes[addr & 0x1F]; + } return 0x00; } @@ -159,9 +179,12 @@ void Bus::WriteCPU(Word addr, Byte val) switch (addr) { case 0x4016: - case 0x4017: controllerPort.Write(addr, val); break; + + case 0x4017: + apu.WriteRegister(addr, val); + break; } } } @@ -174,11 +197,18 @@ void Bus::WritePPU(Word addr, Byte val) { cartridge.WritePPU(addr, val); } - else if (0x2000 <= addr && addr < 0x4000) + else if (0x2000 <= addr && addr < 0x3F00) { if(cartridge.MapCIRAM(addr)) cartridge.WriteVRAM(addr, val); VRAM[addr & 0xFFF] = val; } + else if (0x3F00 <= addr && addr < 0x4000) + { + if ((addr & 0x3) == 0x00) + addr &= 0xF; + + palettes[addr & 0x1F] = val; + } } diff --git a/src/Bus.hpp b/src/Bus.hpp index a161782..8590206 100644 --- a/src/Bus.hpp +++ b/src/Bus.hpp @@ -5,6 +5,7 @@ #include "Types.hpp" #include "CPU.hpp" #include "PPU.hpp" +#include "APU.hpp" #include "Cartridge.hpp" #include "ControllerPort.hpp" @@ -21,6 +22,7 @@ class Bus friend class Debugger; friend class MemoryViewer; friend class NametableViewer; + friend class Palettes; public: Bus(Screen* screen); @@ -82,11 +84,14 @@ public: * @brief Lets the PPU trigger NMIs. */ inline void NMI() { cpu.NMI(); } + inline void IRQ() { cpu.IRQ(); } private: std::vector RAM, VRAM; + std::vector palettes; CPU cpu; PPU ppu; + APU apu; Cartridge cartridge; ControllerPort controllerPort; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ec20574..0f8c80c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,7 @@ add_executable(nesemu "debugger/PPUWatcher.cpp" "debugger/Disassembler.cpp" "debugger/MemoryViewer.cpp" - "debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp" "gfx/Screen.cpp") + "debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp" "gfx/Screen.cpp" "debugger/Palettes.cpp" "APU.cpp") target_include_directories(nesemu PRIVATE mappers diff --git a/src/CPU.cpp b/src/CPU.cpp index e916cc7..f4cfc1e 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -150,6 +150,7 @@ void CPU::CreateInstructionTable() InstructionTable[0x48] = NEW_INSTRUCTION(PHA, IMP, 1, 3); InstructionTable[0x49] = NEW_INSTRUCTION(EOR, IMM, 2, 2); InstructionTable[0x4A] = NEW_INSTRUCTION(LSR, ACC, 1, 2); + InstructionTable[0x4B] = NEW_ILLGL_INSTR(ALR, IMM, 2, 2); InstructionTable[0x4C] = NEW_INSTRUCTION(JMP, ABS, 3, 3); InstructionTable[0x4D] = NEW_INSTRUCTION(EOR, ABS, 3, 4); InstructionTable[0x4E] = NEW_INSTRUCTION(LSR, ABS, 3, 6); @@ -212,6 +213,7 @@ void CPU::CreateInstructionTable() InstructionTable[0x88] = NEW_INSTRUCTION(DEY, IMP, 1, 2); InstructionTable[0x89] = NEW_ILLGL_INSTR(NOP, IMM, 2, 2); InstructionTable[0x8A] = NEW_INSTRUCTION(TXA, IMP, 1, 2); + InstructionTable[0x8B] = NEW_ILLGL_INSTR(ANE, IMM, 2, 2); InstructionTable[0x8C] = NEW_INSTRUCTION(STY, ABS, 3, 4); InstructionTable[0x8D] = NEW_INSTRUCTION(STA, ABS, 3, 4); InstructionTable[0x8E] = NEW_INSTRUCTION(STX, ABS, 3, 4); @@ -239,6 +241,7 @@ void CPU::CreateInstructionTable() InstructionTable[0xA8] = NEW_INSTRUCTION(TAY, IMP, 1, 2); InstructionTable[0xA9] = NEW_INSTRUCTION(LDA, IMM, 2, 2); InstructionTable[0xAA] = NEW_INSTRUCTION(TAX, IMP, 1, 2); + InstructionTable[0xAB] = NEW_ILLGL_INSTR(LXA, IMM, 2, 2); InstructionTable[0xAC] = NEW_INSTRUCTION(LDY, ABS, 3, 4); InstructionTable[0xAD] = NEW_INSTRUCTION(LDA, ABS, 3, 4); InstructionTable[0xAE] = NEW_INSTRUCTION(LDX, ABS, 3, 4); @@ -352,6 +355,20 @@ void CPU::Reset() halted = false; } +void CPU::IRQ() +{ + if (status.Flag.InterruptDisable) + return; + + Push(pc.Bytes.hi); + Push(pc.Bytes.lo); + Push(status.Raw | (0x20 << 4)); + + status.Flag.InterruptDisable = 1; + pc.Bytes.lo = Read(0xFFFE); + pc.Bytes.hi = Read(0xFFFF); +} + void CPU::NMI() { Push(pc.Bytes.hi); @@ -484,6 +501,20 @@ void CPU::ADC() status.Flag.Carry = ((result & 0x100) == 0x100); } +void CPU::ALR() +{ + FetchValue(); + acc &= fetchedVal; + + status.Flag.Carry = ((acc & 0x01) == 0x01); + acc >>= 1; + + status.Flag.Negative = 0; + CHECK_ZERO(acc); + + additionalCycles = 0; +} + void CPU::AND() { FetchValue(); @@ -493,6 +524,19 @@ void CPU::AND() CHECK_ZERO(acc); } +void CPU::ANE() +{ + FetchValue(); + Byte result = (acc | 0xFF) & idx & fetchedVal; + + acc = result; + + CHECK_NEGATIVE(result); + CHECK_ZERO(result); + + additionalCycles = 0; +} + void CPU::ASL() { FetchValue(); @@ -847,6 +891,19 @@ void CPU::LSR() Write(absoluteAddress.Raw, fetchedVal); } +void CPU::LXA() +{ + FetchValue(); + Byte result = (acc & 0xFF) & fetchedVal; + acc = result; + idx = result; + + CHECK_NEGATIVE(result); + CHECK_ZERO(result); + + additionalCycles = 0; +} + void CPU::NOP() { // Nothing to do diff --git a/src/CPU.hpp b/src/CPU.hpp index 35b7464..e7b71f9 100644 --- a/src/CPU.hpp +++ b/src/CPU.hpp @@ -165,7 +165,9 @@ private: // Stuff regarding instructions // Instructions that the NES can perform // They simply perform the operations needed void ADC(); + void ALR(); void AND(); + void ANE(); void ASL(); void BCC(); void BCS(); @@ -200,6 +202,7 @@ private: // Stuff regarding instructions void LDX(); void LDY(); void LSR(); + void LXA(); void NOP(); void ORA(); void PHA(); diff --git a/src/ControllerPort.cpp b/src/ControllerPort.cpp index 046d813..c3ca70b 100644 --- a/src/ControllerPort.cpp +++ b/src/ControllerPort.cpp @@ -20,10 +20,8 @@ ControllerPort::~ControllerPort() Byte ControllerPort::Write(Word addr, Byte val) { - if (addr != 0x4016) - return 0x00; - latch.Raw = val; + return 0x00; } Byte ControllerPort::Read(Word addr) diff --git a/src/PPU.cpp b/src/PPU.cpp index 9ef5ebd..543353f 100644 --- a/src/PPU.cpp +++ b/src/PPU.cpp @@ -4,10 +4,79 @@ #include "gfx/Screen.hpp" +const std::vector PPU::colorTable = { + {84, 84, 84 }, + {0, 30, 116 }, + {8, 16, 144 }, + {48, 0, 136 }, + {68, 0, 100 }, + {92, 0, 48 }, + {84, 4, 0 }, + {60, 24, 0 }, + {32, 42, 0 }, + {8, 58, 0 }, + {0, 64, 0 }, + {0, 60, 0 }, + {0, 50, 60 }, + {0, 0, 0 }, + {0, 0, 0 }, + {0, 0, 0 }, + + {152, 150, 152 }, + {8, 76, 196 }, + {48, 50, 236 }, + {92, 30, 228 }, + {136, 20, 176 }, + {160, 20, 100 }, + {152, 34, 32 }, + {120, 60, 0 }, + {84, 90, 0 }, + {40, 114, 0 }, + {8, 124, 0 }, + {0, 118, 40 }, + {0, 102, 120 }, + {0, 0, 0 }, + {0, 0, 0 }, + {0, 0, 0 }, + + {236, 238, 236 }, + {76, 154, 236 }, + {120, 124, 236 }, + {176, 98, 236 }, + {228, 84, 236 }, + {236, 88, 180 }, + {236, 106, 100 }, + {212, 136, 32 }, + {160, 170, 0 }, + {116, 196, 0 }, + {76, 208, 32 }, + {56, 204, 108 }, + {56, 180, 204 }, + {60, 60, 60 }, + {0, 0, 0 }, + {0, 0, 0 }, + + {236, 238, 236 }, + {168, 204, 236 }, + {188, 188, 236 }, + {212, 178, 236 }, + {236, 174, 236 }, + {236, 174, 212 }, + {236, 180, 176 }, + {228, 196, 144 }, + {204, 210, 120 }, + {180, 222, 120 }, + {168, 226, 144 }, + {152, 226, 180 }, + {160, 214, 228 }, + {160, 162, 160 }, + {0, 0, 0 }, + {0, 0, 0 } +}; + PPU::PPU(Bus* bus, Screen* screen) : bus(bus), screen(screen), ppuctrl{ 0 }, ppustatus{ 0 } { - LOG_CORE_INFO("{0}", sizeof(VRAMAddress)); } void PPU::Powerup() @@ -77,20 +146,49 @@ void PPU::Tick() if (y == 261 && x == 1) ppustatus.Flag.VBlankStarted = 0; - // Need to render if (scanlineType == ScanlineType::Visible || scanlineType == ScanlineType::PreRender) { PerformRenderAction(); + if (x == 257) + { + current.CoarseX = temporary.CoarseX; + current.NametableSel &= 0x2; + current.NametableSel |= temporary.NametableSel & 0x1; + } + + if (scanlineType == ScanlineType::PreRender && x >= 280 && x <= 304) + { + current.FineY = temporary.FineY; + current.CoarseY = temporary.CoarseY; + current.NametableSel &= 0x1; + current.NametableSel |= temporary.NametableSel & 0x2; + } + } if (x < 256 && y < 240) { - uint8_t xOffset = 7 - fineX; + Byte loBit = (loTile.Hi & 0x80) >> 7; + Byte hiBit = (hiTile.Hi & 0x80) >> 7; + Byte loAttrBit = (loAttribute.Hi & 0x80) >> 7; + Byte hiAttrBit = (hiAttribute.Hi & 0x80) >> 7; - uint8_t color = (((patternTableHi >> xOffset) & 0x1) << 1) | ((patternTableLo >> xOffset) & 0x1); - color *= 80; - screen->SetPixel(x, y, { color, color, color}); + uint8_t color = (hiBit << 1) | loBit; + uint8_t palette = (hiAttrBit << 1) | loAttrBit; + if (color == 0x00) + palette = 0x00; + + uint8_t colorVal = Read(0x3F00 | (palette << 2) | color); + if (colorVal != 0x0f) + volatile int dfjk = 3; + + screen->SetPixel(x, y, colorTable[colorVal]); + + loTile.Raw <<= 1; + hiTile.Raw <<= 1; + loAttribute.Raw <<= 1; + hiAttribute.Raw <<= 1; } } @@ -282,7 +380,7 @@ void PPU::PerformRenderAction() break; case FetchingPhase::AttributeTableByte: - attributeTableByte = Read(0x23C0 | (current.Raw & 0x0FFF) | ((current.Raw >> 4) & 0x38) | ((current.Raw >> 2) & 0x07)); + attributeTableByte = Read(0x23C0 | (current.Raw & 0x0C00) | ((current.CoarseY >> 2) << 3) | (current.CoarseX >> 2)); fetchPhase = FetchingPhase::PatternTableLo; break; @@ -292,29 +390,25 @@ void PPU::PerformRenderAction() break; case FetchingPhase::PatternTableHi: - patternTableLo = Read((((Word)ppuctrl.Flag.BackgrPatternTableAddr << 12) | ((Word)nametableByte << 4)) + 8 + current.FineY); + patternTableHi = Read((((Word)ppuctrl.Flag.BackgrPatternTableAddr << 12) | ((Word)nametableByte << 4)) + 8 + current.FineY); + + loTile.Lo = patternTableLo; + hiTile.Lo = patternTableHi; current.CoarseX++; if (x == 256) { - current.CoarseX = temporary.CoarseX; - current.NametableSel ^= 0x1; - - if (current.FineY < 7) + current.FineY++; + if (current.FineY == 0) { - current.FineY++; - } - else - { - current.FineY = temporary.FineY; if (current.CoarseY == 29) { - current.CoarseY = temporary.CoarseY; + current.CoarseY = 0; current.NametableSel ^= 0x2; } else if (current.CoarseY == 31) { - current.CoarseY = temporary.CoarseY; + current.CoarseY = 0; } else { @@ -323,6 +417,13 @@ void PPU::PerformRenderAction() } } + Byte attributeHalfNybble = attributeTableByte; + attributeHalfNybble >>= ((current.CoarseX % 2) ? 2 : 0); + attributeHalfNybble >>= ((current.CoarseY % 2) ? 0 : 4); + + loAttribute.Lo = ((attributeHalfNybble & 1) ? 0xFF : 0x00); + hiAttribute.Lo = ((attributeHalfNybble & 2) ? 0xFF : 0x00); + fetchPhase = FetchingPhase::NametableByte; break; } diff --git a/src/PPU.hpp b/src/PPU.hpp index fc06b49..ef59014 100644 --- a/src/PPU.hpp +++ b/src/PPU.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include "Types.hpp" class Bus; @@ -43,6 +44,17 @@ union VRAMAddress Word Raw; }; +union ShiftRegister +{ + struct + { + Byte Lo; + Byte Hi; + }; + + Word Raw; +}; + /** * @brief The PPU of the NES. */ @@ -50,6 +62,9 @@ class PPU { friend class PPUWatcher; +public: + static const std::vector colorTable; + public: PPU(Bus* bus, Screen* screen); @@ -170,6 +185,11 @@ private: // Registers Byte patternTableLo = 0x00; Byte patternTableHi = 0x00; + ShiftRegister loTile{ 0 }; + ShiftRegister hiTile{ 0 }; + ShiftRegister hiAttribute{ 0 }; + ShiftRegister loAttribute{ 0 }; + private: ScanlineType scanlineType; CycleType cycleType; diff --git a/src/Types.hpp b/src/Types.hpp index fb84e38..894b0c3 100644 --- a/src/Types.hpp +++ b/src/Types.hpp @@ -47,3 +47,10 @@ struct Header Byte TV2; Byte Padding[5]; }; + +struct Color +{ + uint8_t r; + uint8_t g; + uint8_t b; +}; diff --git a/src/controllers/Controller.hpp b/src/controllers/Controller.hpp index be57dc1..f8b3df6 100644 --- a/src/controllers/Controller.hpp +++ b/src/controllers/Controller.hpp @@ -27,7 +27,7 @@ public: { Byte output = outRegister & 1; outRegister >>= 1; - return 0xF ^ (output << outPin); + return (output << outPin); } protected: diff --git a/src/controllers/StandardController.cpp b/src/controllers/StandardController.cpp index ac1f2ee..2735aaf 100644 --- a/src/controllers/StandardController.cpp +++ b/src/controllers/StandardController.cpp @@ -22,4 +22,9 @@ void StandardController::OUT(PortLatch latch) pressed.Buttons.Down = Input::IsKeyDown(GLFW_KEY_DOWN); pressed.Buttons.Left = Input::IsKeyDown(GLFW_KEY_LEFT); pressed.Buttons.Right = Input::IsKeyDown(GLFW_KEY_RIGHT); + + if (pressed.Raw == 0xFF) + volatile int dkjf = 3; + + outRegister = pressed.Raw; } diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp index 7fe0f26..5d8d84b 100644 --- a/src/debugger/Debugger.cpp +++ b/src/debugger/Debugger.cpp @@ -11,6 +11,7 @@ #include "MemoryViewer.hpp" #include "NametableViewer.hpp" #include "ControllerPortViewer.hpp" +#include "Palettes.hpp" Debugger::Debugger(Bus* bus) : bus(bus) @@ -26,6 +27,7 @@ Debugger::Debugger(Bus* bus) : windows.push_back(new MemoryViewer(this, bus)); windows.push_back(new NametableViewer(this, bus)); windows.push_back(new ControllerPortViewer(this, &bus->controllerPort)); + windows.push_back(new Palettes(this, bus)); } Debugger::~Debugger() diff --git a/src/debugger/PPUWatcher.cpp b/src/debugger/PPUWatcher.cpp index 8a5f8a2..ad43ec2 100644 --- a/src/debugger/PPUWatcher.cpp +++ b/src/debugger/PPUWatcher.cpp @@ -86,7 +86,15 @@ void PPUWatcher::OnRender() ImGui::Text("Pattern Tile Lo: %02X", ppu->patternTableLo); ImGui::Text("Pattern Tile Hi: %02X", ppu->patternTableHi); } - + + if (ImGui::CollapsingHeader("Shift Registers")) + { + ImGui::Text("Pattern Tile Lo: %04X", ppu->loTile.Raw); + ImGui::Text("Pattern Tile Hi: %02X", ppu->hiTile.Raw); + ImGui::Text("Attribute Lo : %02X", ppu->loAttribute.Raw); + ImGui::Text("Attribute Hi : %02X", ppu->hiAttribute.Raw); + } + if (ImGui::CollapsingHeader("Registers")) { if (ImGui::TreeNode("PPUCTRL")) diff --git a/src/debugger/Palettes.cpp b/src/debugger/Palettes.cpp new file mode 100644 index 0000000..1662510 --- /dev/null +++ b/src/debugger/Palettes.cpp @@ -0,0 +1,78 @@ +#include "Palettes.hpp" + +#include + +#include "../Bus.hpp" +#include "../PPU.hpp" +#include + + +Palettes::Palettes(Debugger* debugger, Bus* bus) : + DebugWindow("Palettes", debugger), bus(bus) +{ + glCreateTextures(GL_TEXTURE_2D, 4, backgroundPalettes.data()); + glCreateTextures(GL_TEXTURE_2D, 4, spritePalettes.data()); + + for (GLuint texture : backgroundPalettes) + { + glTextureStorage2D(texture, 1, GL_RGB8, 4, 1); + glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + for (GLuint texture : spritePalettes) + { + glTextureStorage2D(texture, 1, GL_RGB8, 4, 1); + glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } +} + +Palettes::~Palettes() +{ + glDeleteTextures(4, spritePalettes.data()); + glDeleteTextures(4, backgroundPalettes.data()); +} + +void Palettes::OnRender() +{ + ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); + if (!ImGui::Begin(title.c_str(), &isOpen)) + { + ImGui::End(); + return; + } + + Color palette[4]; + palette[0] = PPU::colorTable[bus->palettes[0]]; + ImVec2 size = ImGui::GetWindowSize(); + size.x /= 2; + size.y = size.x / 4; + if (ImGui::CollapsingHeader("Background")) + { + for (int i = 0; i < 4; i++) + { + palette[1] = PPU::colorTable[bus->palettes[i * 4 + 1]]; + palette[2] = PPU::colorTable[bus->palettes[i * 4 + 2]]; + palette[3] = PPU::colorTable[bus->palettes[i * 4 + 3]]; + + glTextureSubImage2D(backgroundPalettes[i], 0, 0, 0, 4, 1, GL_RGB, GL_UNSIGNED_BYTE, (const void*)palette); + ImGui::Image((ImTextureID)backgroundPalettes[i], size); + } + } + + if (ImGui::CollapsingHeader("Sprites")) + { + for (int i = 0; i < 4; i++) + { + palette[1] = PPU::colorTable[bus->palettes[0x10 + i * 4 + 1]]; + palette[2] = PPU::colorTable[bus->palettes[0x10 + i * 4 + 2]]; + palette[3] = PPU::colorTable[bus->palettes[0x10 + i * 4 + 3]]; + + glTextureSubImage2D(spritePalettes[i], 0, 0, 0, 4, 1, GL_RGB, GL_UNSIGNED_BYTE, (const void*)palette); + ImGui::Image((ImTextureID)spritePalettes[i], size); + } + } + + ImGui::End(); +} diff --git a/src/debugger/Palettes.hpp b/src/debugger/Palettes.hpp new file mode 100644 index 0000000..38fbaff --- /dev/null +++ b/src/debugger/Palettes.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "DebugWindow.hpp" + +class Bus; + +class Palettes : + public DebugWindow +{ +public: + Palettes(Debugger* debugger, Bus* bus); + ~Palettes(); + + virtual void OnRender() override; + +private: + Bus* bus; + + std::array backgroundPalettes; + std::array spritePalettes; +}; diff --git a/src/gfx/Screen.hpp b/src/gfx/Screen.hpp index eb24ef1..e04168d 100644 --- a/src/gfx/Screen.hpp +++ b/src/gfx/Screen.hpp @@ -2,13 +2,7 @@ #include #include - -struct Color -{ - uint8_t r; - uint8_t g; - uint8_t b; -}; +#include "../Types.hpp" class Screen { diff --git a/src/mappers/Mapper000.cpp b/src/mappers/Mapper000.cpp index 1d95020..e5f45b2 100644 --- a/src/mappers/Mapper000.cpp +++ b/src/mappers/Mapper000.cpp @@ -8,8 +8,9 @@ Mapper000::Mapper000(const Header& header, std::ifstream& ifs) : Mapper(header) { LOG_CORE_INFO("Allocating PRG ROM"); - PRG_ROM = std::vector(0x4000); - ifs.read((char*)PRG_ROM.data(), 0x4000); + prgBanks = header.PrgROM; + PRG_ROM = std::vector(0x4000 * prgBanks); + ifs.read((char*)PRG_ROM.data(), 0x4000 * prgBanks); LOG_CORE_INFO("Allocating CHR ROM"); CHR_ROM = std::vector(0x2000); @@ -20,7 +21,7 @@ Byte Mapper000::ReadCPU(Word addr) { if (0x8000 <= addr && addr <= 0xFFFF) { - return PRG_ROM[addr & 0x03FFF]; + return PRG_ROM[addr & (prgBanks * 0x4000 - 1)]; } return 0x00; diff --git a/src/mappers/Mapper000.hpp b/src/mappers/Mapper000.hpp index daac4b3..459e488 100644 --- a/src/mappers/Mapper000.hpp +++ b/src/mappers/Mapper000.hpp @@ -16,4 +16,7 @@ public: virtual Byte ReadPPU(Word addr) override; virtual void WriteCPU(Word addr, Byte val) override; virtual void WritePPU(Word addr, Byte val) override; + +private: + Byte prgBanks = 0; };