From 1220631e7ec7db997b44120bfa87fb0d29e82ed9 Mon Sep 17 00:00:00 2001 From: Lauchmelder Date: Tue, 1 Mar 2022 03:34:19 +0100 Subject: [PATCH] added breakpoints --- src/Bus.cpp | 8 +- src/CMakeLists.txt | 2 +- src/CPU.cpp | 198 +++++-------------------------- src/CPU.hpp | 3 +- src/Cartridge.cpp | 2 +- src/Cartridge.hpp | 20 +--- src/Mapper.hpp | 31 ++++- src/Types.hpp | 32 +++++ src/debugger/CPUWatcher.cpp | 8 +- src/debugger/CPUWatcher.hpp | 2 +- src/debugger/DebugWindow.hpp | 9 +- src/debugger/Debugger.cpp | 36 +++++- src/debugger/Debugger.hpp | 3 + src/debugger/Disassembler.cpp | 61 +++++++++- src/debugger/Disassembler.hpp | 25 +++- src/debugger/MemoryViewer.cpp | 4 +- src/debugger/MemoryViewer.hpp | 2 +- src/debugger/NametableViewer.cpp | 61 ++++++++-- src/debugger/NametableViewer.hpp | 5 +- src/debugger/PPUWatcher.cpp | 4 +- src/debugger/PPUWatcher.hpp | 2 +- src/mappers/Mapper000.cpp | 3 +- src/mappers/Mapper000.hpp | 2 +- 23 files changed, 303 insertions(+), 220 deletions(-) diff --git a/src/Bus.cpp b/src/Bus.cpp index eb38615..96a3b7e 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -10,7 +10,7 @@ Bus::Bus() : RAM = std::vector(0x800); LOG_CORE_INFO("Allocating VRAM"); - VRAM = std::vector(0x1000); + VRAM = std::vector(0x800); LOG_CORE_INFO("Inserting cartridge"); cartridge.Load("roms/nestest.nes"); @@ -107,6 +107,9 @@ Byte Bus::ReadPPU(Word addr) } else if(0x2000 <= addr && addr < 0x4000) { + if (cartridge.MapCIRAM(addr)) + return cartridge.ReadVRAM(addr); + return VRAM[addr & 0xFFF]; } @@ -139,6 +142,9 @@ void Bus::WritePPU(Word addr, Byte val) } else if (0x2000 <= addr && addr < 0x4000) { + if(cartridge.MapCIRAM(addr)) + cartridge.WriteVRAM(addr, val); + VRAM[addr & 0xFFF] = val; } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 20cd563..fc1c269 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") + "debugger/NametableViewer.cpp" ) target_include_directories(nesemu PRIVATE mappers diff --git a/src/CPU.cpp b/src/CPU.cpp index 5b202a5..e916cc7 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -12,16 +12,6 @@ #define CHECK_NEGATIVE(x) status.Flag.Negative = (((x) & 0x80) == 0x80) #define CHECK_ZERO(x) status.Flag.Zero = ((x) == 0x00) -#if !defined(NDEBUG) && !defined(FORCE_NO_DEBUG_LOG) - #define LOG() LOG_DEBUG_TRACE(debugString.str()) - #define RESET_DEBUG_STRING() debugString.str(std::string()) - #define APPEND_DEBUG_STRING(x) debugString << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << x -#else - #define LOG() - #define RESET_DEBUG_STRING() - #define APPEND_DEBUG_STRING(...) -#endif - CPU::CPU(Bus* bus) : bus(bus) { @@ -39,6 +29,12 @@ void CPU::Write(Word addr, Byte val) bus->WriteCPU(addr, val); } +void CPU::FetchValue() +{ + if (currentInstruction->AddrType != Addressing::ACC && currentInstruction->AddrType != Addressing::IMP) + fetchedVal = Read(absoluteAddress.Raw); +} + uint8_t CPU::Tick() { if (halted) @@ -51,9 +47,6 @@ uint8_t CPU::Tick() return remainingCycles--; } - RESET_DEBUG_STRING(); - APPEND_DEBUG_STRING(std::setw(4) << pc.Raw << " "); - // Get current opcode and instruction Byte opcode = Read(pc.Raw++); currentInstruction = &(InstructionTable[opcode]); @@ -70,24 +63,11 @@ uint8_t CPU::Tick() throw std::runtime_error("Encountered unknown opcode"); } - APPEND_DEBUG_STRING((Word)opcode << " "); - // Invoke addressing mode and instruction accumulatorAddressing = false; currentInstruction->Mode(); currentInstruction->Opcode(); - APPEND_DEBUG_STRING(std::string(50 - debugString.str().length(), ' ')); - - APPEND_DEBUG_STRING("A:"); - APPEND_DEBUG_STRING((Word)acc << " X:"); - APPEND_DEBUG_STRING((Word)idx << " Y:"); - APPEND_DEBUG_STRING((Word)idy << " P:"); - APPEND_DEBUG_STRING((Word)status.Raw << " SP:"); - APPEND_DEBUG_STRING(sp << " CYC:" << std::dec << totalCycles); - - LOG(); - // Set remaining cycles remainingCycles = currentInstruction->Cycles + additionalCycles; additionalCycles = 0; @@ -387,30 +367,16 @@ void CPU::NMI() void CPU::ABS() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " " << std::setw(2) << (Word)Read(pc.Raw + 1) << " "); - absoluteAddress.Bytes.lo = Read(pc.Raw++); absoluteAddress.Bytes.hi = Read(pc.Raw++); - - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $"); - APPEND_DEBUG_STRING(std::setw(4) << absoluteAddress.Raw); } void CPU::ABX() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " " << std::setw(2) << (Word)Read(pc.Raw + 1) << " "); - rawAddress.Bytes.lo = Read(pc.Raw++); rawAddress.Bytes.hi = Read(pc.Raw++); absoluteAddress.Raw = rawAddress.Raw + idx; - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $"); - APPEND_DEBUG_STRING(std::setw(4) << fetchedAddr.Raw << ",X @ "); - APPEND_DEBUG_STRING(std::setw(4) << absoluteAddress.Raw); if (rawAddress.Bytes.hi != absoluteAddress.Bytes.hi) additionalCycles = 1; @@ -418,17 +384,10 @@ void CPU::ABX() void CPU::ABY() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " " << std::setw(2) << (Word)Read(pc.Raw + 1) << " "); - rawAddress.Bytes.lo = Read(pc.Raw++); rawAddress.Bytes.hi = Read(pc.Raw++); absoluteAddress.Raw = rawAddress.Raw + idy; - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $"); - APPEND_DEBUG_STRING(std::setw(4) << rawAddress.Raw << ",Y @ "); - APPEND_DEBUG_STRING(std::setw(4) << absoluteAddress.Raw); if (rawAddress.Bytes.hi != absoluteAddress.Bytes.hi) additionalCycles = 1; @@ -436,45 +395,26 @@ void CPU::ABY() void CPU::ACC() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " " << (Word)Read(pc.Raw + 2) << " "); - fetchedVal = acc; accumulatorAddressing = true; - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " A"); } void CPU::IDX() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " "); - Byte index = Read(pc.Raw++); Byte indirectAddress = index + idx; absoluteAddress.Bytes.lo = Read(indirectAddress); absoluteAddress.Bytes.hi = Read((indirectAddress + 1) & 0xFF); - - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " ($"); - APPEND_DEBUG_STRING((Word)index << ",X) @ "); - APPEND_DEBUG_STRING((Word)indirectAddress << " = " << std::setw(4) << absoluteAddress.Raw); } void CPU::IDY() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " "); - Byte index = Read(pc.Raw++); rawAddress.Bytes.lo = Read(index); rawAddress.Bytes.hi = Read((index + 1) & 0xFF); absoluteAddress.Raw = rawAddress.Raw + idy; - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " ($"); - APPEND_DEBUG_STRING((Word)index << "),Y = "); - APPEND_DEBUG_STRING(std::setw(4) << rawAddress.Raw << " @ " << std::setw(4) << absoluteAddress.Raw); if (absoluteAddress.Bytes.hi != rawAddress.Bytes.hi) additionalCycles = 1; @@ -482,90 +422,49 @@ void CPU::IDY() void CPU::IMM() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " "); - - fetchedVal = Read(pc.Raw++); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " #$"); - APPEND_DEBUG_STRING((Word)fetchedVal); + absoluteAddress.Raw = pc.Raw++; } void CPU::IMP() { - APPEND_DEBUG_STRING(" "); // Nothing to do - APPEND_DEBUG_STRING(currentInstruction->Mnemonic); } void CPU::IND() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " " << (Word)Read(pc.Raw + 2) << " "); - rawAddress.Bytes.lo = Read(pc.Raw++); rawAddress.Bytes.hi = Read(pc.Raw++); - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $(" << std::setw(4) << rawAddress.Raw); - absoluteAddress.Bytes.lo = Read(rawAddress.Raw); rawAddress.Bytes.lo++; absoluteAddress.Bytes.hi = Read(rawAddress.Raw); - - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(") = " << std::setw(4) << absoluteAddress.Raw); } void CPU::REL() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " "); - relativeAddress = Read(pc.Raw++); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $"); - APPEND_DEBUG_STRING((Word)pc.Raw + (int8_t)relativeAddress); } void CPU::ZPG() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " "); - absoluteAddress.Bytes.hi = 0x00; absoluteAddress.Bytes.lo = Read(pc.Raw++); - - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $"); - APPEND_DEBUG_STRING((Word)absoluteAddress.Bytes.lo); } void CPU::ZPX() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " "); - rawAddress.Bytes.lo = Read(pc.Raw++); absoluteAddress.Bytes.hi = 0x00; absoluteAddress.Bytes.lo = rawAddress.Bytes.lo + idx; - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $"); - APPEND_DEBUG_STRING((Word)rawAddress.Bytes.lo << ",X @ "); - APPEND_DEBUG_STRING(absoluteAddress.Raw); } void CPU::ZPY() { - APPEND_DEBUG_STRING((Word)Read(pc.Raw) << " "); - rawAddress.Bytes.lo = Read(pc.Raw++); absoluteAddress.Bytes.hi = 0x00; absoluteAddress.Bytes.lo = rawAddress.Bytes.lo + idy; - fetchedVal = Read(absoluteAddress.Raw); - - APPEND_DEBUG_STRING(currentInstruction->Mnemonic << " $"); - APPEND_DEBUG_STRING((Word)rawAddress.Bytes.lo << ",Y @ "); - APPEND_DEBUG_STRING(absoluteAddress.Raw); } #pragma endregion @@ -574,8 +473,7 @@ void CPU::ZPY() void CPU::ADC() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); Word result = (Word)acc + fetchedVal + status.Flag.Carry; status.Flag.Overflow = ((((~acc & ~fetchedVal & result) | (acc & fetchedVal & ~result)) & 0x80) == 0x80); @@ -588,8 +486,7 @@ void CPU::ADC() void CPU::AND() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); acc &= fetchedVal; CHECK_NEGATIVE(acc); @@ -598,8 +495,7 @@ void CPU::AND() void CPU::ASL() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); status.Flag.Carry = ((fetchedVal & 0x80) == 0x80); fetchedVal <<= 1; @@ -656,8 +552,7 @@ void CPU::BEQ() void CPU::BIT() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); status.Flag.Negative = ((fetchedVal & 0x80) == 0x80); status.Flag.Overflow = ((fetchedVal & 0x40) == 0x40); @@ -769,7 +664,7 @@ void CPU::CLV() void CPU::CMP() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); + FetchValue(); Word result = acc - fetchedVal; CHECK_NEGATIVE(result); @@ -779,7 +674,7 @@ void CPU::CMP() void CPU::CPX() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); + FetchValue(); Word result = idx - fetchedVal; CHECK_NEGATIVE(result); @@ -789,7 +684,7 @@ void CPU::CPX() void CPU::CPY() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); + FetchValue(); Word result = idy - fetchedVal; CHECK_NEGATIVE(result); @@ -799,8 +694,7 @@ void CPU::CPY() void CPU::DCP() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); fetchedVal--; Write(absoluteAddress.Raw, fetchedVal); @@ -815,8 +709,7 @@ void CPU::DCP() void CPU::DEC() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); fetchedVal--; CHECK_NEGATIVE(fetchedVal); CHECK_ZERO(fetchedVal); @@ -826,31 +719,21 @@ void CPU::DEC() void CPU::DEX() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - idx--; CHECK_NEGATIVE(idx); CHECK_ZERO(idx); - - Write(absoluteAddress.Raw, idx); } void CPU::DEY() { - - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - idy--; CHECK_NEGATIVE(idy); CHECK_ZERO(idy); - - Write(absoluteAddress.Raw, idy); } void CPU::EOR() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); acc ^= fetchedVal; CHECK_NEGATIVE(acc); @@ -859,6 +742,7 @@ void CPU::EOR() void CPU::INC() { + FetchValue(); fetchedVal++; CHECK_NEGATIVE(fetchedVal); CHECK_ZERO(fetchedVal); @@ -882,8 +766,7 @@ void CPU::INY() void CPU::ISC() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); fetchedVal++; Write(absoluteAddress.Raw, fetchedVal); @@ -914,8 +797,7 @@ void CPU::JSR() void CPU::LAX() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); CHECK_NEGATIVE(fetchedVal); CHECK_ZERO(fetchedVal); @@ -925,8 +807,7 @@ void CPU::LAX() void CPU::LDA() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); CHECK_NEGATIVE(fetchedVal); CHECK_ZERO(fetchedVal); @@ -935,8 +816,7 @@ void CPU::LDA() void CPU::LDX() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); CHECK_NEGATIVE(fetchedVal); CHECK_ZERO(fetchedVal); @@ -945,8 +825,7 @@ void CPU::LDX() void CPU::LDY() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); CHECK_NEGATIVE(fetchedVal); CHECK_ZERO(fetchedVal); @@ -955,8 +834,7 @@ void CPU::LDY() void CPU::LSR() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); status.Flag.Carry = ((fetchedVal & 0x01) == 0x01); fetchedVal >>= 1; @@ -976,8 +854,7 @@ void CPU::NOP() void CPU::ORA() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); acc |= fetchedVal; CHECK_NEGATIVE(acc); @@ -1011,7 +888,7 @@ void CPU::PLP() void CPU::RLA() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); + FetchValue(); Byte oldCarry = status.Flag.Carry; status.Flag.Carry = ((fetchedVal & 0x80) == 0x80); @@ -1030,7 +907,7 @@ void CPU::RLA() void CPU::ROL() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); + FetchValue(); Byte oldCarry = status.Flag.Carry; status.Flag.Carry = ((fetchedVal & 0x80) == 0x80); @@ -1048,7 +925,7 @@ void CPU::ROL() void CPU::ROR() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); + FetchValue(); Byte oldCarry = status.Flag.Carry; status.Flag.Carry = ((fetchedVal & 0x01) == 0x01); @@ -1066,7 +943,7 @@ void CPU::ROR() void CPU::RRA() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); + FetchValue(); Byte oldCarry = status.Flag.Carry; status.Flag.Carry = ((fetchedVal & 0x01) == 0x01); @@ -1105,15 +982,12 @@ void CPU::RTS() void CPU::SAX() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - Write(absoluteAddress.Raw, acc & idx); } void CPU::SBC() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); Word result = (Word)acc - fetchedVal - (1 - status.Flag.Carry); status.Flag.Overflow = ((((~acc & fetchedVal & result) | (acc & ~fetchedVal & ~result)) & 0x80) == 0x80); @@ -1141,8 +1015,7 @@ void CPU::SEI() void CPU::SLO() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); status.Flag.Carry = ((fetchedVal & 0x80) == 0x80); fetchedVal <<= 1; @@ -1158,8 +1031,7 @@ void CPU::SLO() void CPU::SRE() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - + FetchValue(); status.Flag.Carry = ((fetchedVal & 0x01) == 0x01); fetchedVal >>= 1; @@ -1175,23 +1047,17 @@ void CPU::SRE() void CPU::STA() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - Write(absoluteAddress.Raw, acc); additionalCycles = 0; } void CPU::STX() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - Write(absoluteAddress.Raw, idx); } void CPU::STY() { - APPEND_DEBUG_STRING(" = " << std::setw(2) << (Word)fetchedVal); - Write(absoluteAddress.Raw, idy); } diff --git a/src/CPU.hpp b/src/CPU.hpp index 89e9170..35b7464 100644 --- a/src/CPU.hpp +++ b/src/CPU.hpp @@ -58,7 +58,7 @@ struct Instruction Addressing AddrType = Addressing::IMP; uint8_t Size = 0; uint8_t Cycles = 0; - char Mnemonic[5] = " XXX"; + char Mnemonic[5] = " ???"; }; /** @@ -134,6 +134,7 @@ private: */ void Write(Word addr, Byte val); + void FetchValue(); private: // Stuff regarding addressing modes Address rawAddress; //< Temporary storage while decoding addresses diff --git a/src/Cartridge.cpp b/src/Cartridge.cpp index 7bfe87c..d52883c 100644 --- a/src/Cartridge.cpp +++ b/src/Cartridge.cpp @@ -30,7 +30,7 @@ void Cartridge::Load(std::string path) file.read((char*)&header, sizeof(Header)); // Figure out which mapper the cartridge uses and create a mapper object - uint8_t mapperNumber = (header.MapperHi & 0xF0) | (header.MapperLo >> 4); + uint8_t mapperNumber = (header.Flag7.MapperHi << 4) | header.Flag6.MapperLo; LOG_CORE_INFO("Cartridge requires Mapper {0:d}", mapperNumber); switch (mapperNumber) { diff --git a/src/Cartridge.hpp b/src/Cartridge.hpp index f210ff4..546d013 100644 --- a/src/Cartridge.hpp +++ b/src/Cartridge.hpp @@ -8,22 +8,6 @@ class Bus; -/** - * @brief iNES ROM header. - */ -struct Header -{ - Byte Signature[4]; - Byte PrgROM; - Byte ChrROM; - Byte MapperLo; - Byte MapperHi; - Byte PrgRAM; - Byte TV1; - Byte TV2; - Byte Padding[5]; -}; - /** * @brief Represents a cartridge and handles CPU/PPU read/writes. */ @@ -57,6 +41,10 @@ public: inline void WritePPU(Word addr, Byte val) { mapper->WritePPU(addr, val); } + inline bool MapCIRAM(Word& addr) { return mapper->MapCIRAM(addr); } + inline Byte ReadVRAM(Word addr) { return mapper->ReadVRAM(addr); } + inline void WriteVRAM(Word addr, Byte val) { mapper->WriteVRAM(addr, val); } + /** * @brief Load an iNES file from disk. */ diff --git a/src/Mapper.hpp b/src/Mapper.hpp index da38e14..3c11679 100644 --- a/src/Mapper.hpp +++ b/src/Mapper.hpp @@ -13,10 +13,39 @@ public: virtual void WriteCPU(Word addr, Byte val) = 0; virtual void WritePPU(Word addr, Byte val) = 0; + /** + * The cartridge actually controls the PPUs access to VRAM. + * It can modify the effective address the PPU accesses in order to + * enforce nametable mirroring, or even completely remap the address + * to internal VRAM + */ + bool MapCIRAM(Word& addr) + { + if (header.Flag6.IgnoreMirroringBit) + return true; + + if (header.Flag6.Mirroring == 0x0) + { + addr &= ~(1 << 10); + addr |= ((addr & (1 << 11)) >> 1); + addr &= ~(1 << 11); + } + else + { + addr &= ~(1 << 11); + } + + return false; + } + + virtual Byte ReadVRAM(Word addr) { return 0x00; } + virtual void WriteVRAM(Word addr, Byte val) {} + protected: - Mapper() = default; + Mapper(const Header& header) : header(header) {} protected: std::vector PRG_ROM; std::vector CHR_ROM; + Header header; }; diff --git a/src/Types.hpp b/src/Types.hpp index 81f1dbf..fb84e38 100644 --- a/src/Types.hpp +++ b/src/Types.hpp @@ -15,3 +15,35 @@ union Address Word Raw; }; + +/** + * @brief iNES ROM header. + */ +struct Header +{ + Byte Signature[4]; + Byte PrgROM; + Byte ChrROM; + + struct + { + Byte Mirroring : 1; + Byte BatterBackedPRGRAM : 1; + Byte TrainerPresent : 1; + Byte IgnoreMirroringBit : 1; + Byte MapperLo : 4; + } Flag6; + + struct + { + Byte VSUnisystem : 1; + Byte PlayChoice10 : 1; + Byte iNES2Format : 1; + Byte MapperHi : 4; + } Flag7; + + Byte PrgRAM; + Byte TV1; + Byte TV2; + Byte Padding[5]; +}; diff --git a/src/debugger/CPUWatcher.cpp b/src/debugger/CPUWatcher.cpp index e8fe476..c2516c8 100644 --- a/src/debugger/CPUWatcher.cpp +++ b/src/debugger/CPUWatcher.cpp @@ -5,8 +5,8 @@ #include #include "../CPU.hpp" -CPUWatcher::CPUWatcher(CPU* cpu) : - DebugWindow("CPU Watch"), cpu(cpu) +CPUWatcher::CPUWatcher(Debugger* debugger, CPU* cpu) : + DebugWindow("CPU Watch", debugger), cpu(cpu) { } @@ -26,8 +26,8 @@ void CPUWatcher::OnRender() ImGui::InputScalar("X", ImGuiDataType_U8, &cpu->idx, (const void*)0, (const void*)0, "%02X", ImGuiInputTextFlags_CharsHexadecimal); ImGui::InputScalar("Y", ImGuiDataType_U8, &cpu->idy, (const void*)0, (const void*)0, "%02X", ImGuiInputTextFlags_CharsHexadecimal); ImGui::InputScalar("PC", ImGuiDataType_U16, &cpu->pc, (const void*)0, (const void*)0, "%04X", ImGuiInputTextFlags_CharsHexadecimal); - ImGui::InputScalar("SP", ImGuiDataType_U8, &cpu->sp, (const void*)0, (const void*)0, "%04X", ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly); - ImGui::InputScalar("P", ImGuiDataType_U8, &cpu->status.Raw, (const void*)0, (const void*)0, "%04X", ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly); + ImGui::InputScalar("SP", ImGuiDataType_U8, &cpu->sp, (const void*)0, (const void*)0, "%02X", ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly); + ImGui::InputScalar("P", ImGuiDataType_U8, &cpu->status.Raw, (const void*)0, (const void*)0, "%02X", ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_ReadOnly); } ImGui::Separator(); diff --git a/src/debugger/CPUWatcher.hpp b/src/debugger/CPUWatcher.hpp index 88f6537..e45bf1b 100644 --- a/src/debugger/CPUWatcher.hpp +++ b/src/debugger/CPUWatcher.hpp @@ -8,7 +8,7 @@ class CPUWatcher : public DebugWindow { public: - CPUWatcher(CPU* cpu); + CPUWatcher(Debugger* debugger, CPU* cpu); virtual void OnRender() override; diff --git a/src/debugger/DebugWindow.hpp b/src/debugger/DebugWindow.hpp index fa2d8e3..baf3927 100644 --- a/src/debugger/DebugWindow.hpp +++ b/src/debugger/DebugWindow.hpp @@ -2,11 +2,13 @@ #include +class Debugger; + class DebugWindow { public: - DebugWindow(const std::string& title) : - title(title) + DebugWindow(const std::string& title, Debugger* parent) : + title(title), parent(parent) { } @@ -16,4 +18,7 @@ public: public: const std::string title; bool isOpen = false; + +protected: + Debugger* parent; }; diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp index 1682149..ca48b85 100644 --- a/src/debugger/Debugger.cpp +++ b/src/debugger/Debugger.cpp @@ -4,6 +4,7 @@ #include #include "../Bus.hpp" +#include "../Log.hpp" #include "CPUWatcher.hpp" #include "PPUWatcher.hpp" #include "Disassembler.hpp" @@ -13,11 +14,12 @@ 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)); + windows.push_back(new CPUWatcher(this, &bus->cpu)); + windows.push_back(new PPUWatcher(this, &bus->ppu)); + disassembler = new Disassembler(this, &bus->cpu); + windows.push_back(disassembler); + windows.push_back(new MemoryViewer(this, bus)); + windows.push_back(new NametableViewer(this, bus)); } Debugger::~Debugger() @@ -26,10 +28,32 @@ Debugger::~Debugger() delete window; } +bool Debugger::Frame() +{ + try + { + while (!bus->ppu.IsFrameDone()) + { + bus->Tick(); + if (disassembler->BreakpointHit()) + { + running = false; + break; + } + } + } + catch (const std::runtime_error& err) + { + LOG_CORE_FATAL("Fatal Bus error: {0}", err.what()); + bus->cpu.Halt(); + return true; + } +} + bool Debugger::Update() { if (running) - return bus->Frame(); + return Frame(); return true; } diff --git a/src/debugger/Debugger.hpp b/src/debugger/Debugger.hpp index 851880c..ff012d6 100644 --- a/src/debugger/Debugger.hpp +++ b/src/debugger/Debugger.hpp @@ -4,6 +4,7 @@ #include "DebugWindow.hpp" class Bus; +class Disassembler; class Debugger { @@ -11,6 +12,7 @@ public: Debugger(Bus* bus); ~Debugger(); + bool Frame(); bool Update(); void Render(); @@ -24,4 +26,5 @@ private: uint16_t resetVector = 0x0000; std::vector windows; + Disassembler* disassembler; }; diff --git a/src/debugger/Disassembler.cpp b/src/debugger/Disassembler.cpp index 9f8ed49..a15659d 100644 --- a/src/debugger/Disassembler.cpp +++ b/src/debugger/Disassembler.cpp @@ -4,11 +4,12 @@ #include #include "../Mapper.hpp" #include "../CPU.hpp" +#include "Debugger.hpp" #define FORMAT std::setfill('0') << std::setw(4) << std::hex << std::uppercase -Disassembler::Disassembler(CPU* cpu) : - DebugWindow("Disassembler"), cpu(cpu) +Disassembler::Disassembler(Debugger* debugger, CPU* cpu) : + DebugWindow("Disassembler", debugger), cpu(cpu) { } @@ -19,12 +20,23 @@ void Disassembler::OnRender() ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); - if (!ImGui::Begin(title.c_str(), &isOpen)) + if (!ImGui::Begin(title.c_str(), &isOpen, ImGuiWindowFlags_MenuBar)) { ImGui::End(); return; } + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Tools")) + { + ImGui::MenuItem("Breakpoints", NULL, &showBreakpoints); + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + std::string disassembly; ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{ 0.8f, 0.8f, 0.8f, 1.0f }); @@ -65,9 +77,23 @@ void Disassembler::OnRender() } ImGui::PopStyleColor(); + if (showBreakpoints) + { + BreakpointWindow(); + } + ImGui::End(); } +bool Disassembler::BreakpointHit() +{ + auto bpFound = breakpoints.find(cpu->pc.Raw); + if (bpFound != breakpoints.end() && bpFound->active) + return true; + + return false; +} + void Disassembler::Disassemble(std::string& target, uint16_t& pc) { Instruction* currentInstr = &cpu->InstructionTable[cpu->Read(pc)]; @@ -180,3 +206,32 @@ void Disassembler::Disassemble(std::string& target, uint16_t pc, const Instructi target = ss.str(); } + +void Disassembler::BreakpointWindow() +{ + ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); + + if (!ImGui::Begin("Breakpoints", &showBreakpoints)) + { + ImGui::End(); + return; + } + + ImGui::InputScalar("##", ImGuiDataType_U16, &tempBreakpoint, (const void*)0, (const void*)0, "%04X", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::SameLine(); + if (ImGui::Button("Add Breakpoint")) + { + breakpoints.insert(Breakpoint(tempBreakpoint)); + } + + ImGui::Separator(); + + char label[6]; + for (const Breakpoint& breakpoint : breakpoints) + { + std::sprintf(label, "$%04X", breakpoint.GetAddress()); + ImGui::Checkbox(label, &breakpoint.active); + } + + ImGui::End(); +} diff --git a/src/debugger/Disassembler.hpp b/src/debugger/Disassembler.hpp index e0b9a5d..bbb0f7a 100644 --- a/src/debugger/Disassembler.hpp +++ b/src/debugger/Disassembler.hpp @@ -1,22 +1,45 @@ #pragma once +#include #include "DebugWindow.hpp" +#include "../Types.hpp" class CPU; struct Instruction; +struct Breakpoint +{ + Breakpoint(Word addr) : + address(addr), active(true) + {} + + Word address; + mutable bool active; + + inline Word GetAddress() const { return address; } + + inline bool operator<(const Breakpoint& other) const { return (address < other.address); } + inline bool operator<(Word other) const { return (address < other); } +}; + class Disassembler : public DebugWindow { public: - Disassembler(CPU* cpu); + Disassembler(Debugger* debugger, CPU* cpu); virtual void OnRender() override; + bool BreakpointHit(); private: void Disassemble(std::string& target, uint16_t& pc); void Disassemble(std::string& target, uint16_t pc, const Instruction* instr); + void BreakpointWindow(); + private: CPU* cpu; + bool showBreakpoints = false; + Word tempBreakpoint = 0x0000; + std::set breakpoints; }; diff --git a/src/debugger/MemoryViewer.cpp b/src/debugger/MemoryViewer.cpp index ab79dd5..9f4432a 100644 --- a/src/debugger/MemoryViewer.cpp +++ b/src/debugger/MemoryViewer.cpp @@ -3,8 +3,8 @@ #include #include "../Bus.hpp" -MemoryViewer::MemoryViewer(Bus* bus) : - DebugWindow("Memory Viewer"), bus(bus) +MemoryViewer::MemoryViewer(Debugger* debugger, Bus* bus) : + DebugWindow("Memory Viewer", debugger), bus(bus) { } diff --git a/src/debugger/MemoryViewer.hpp b/src/debugger/MemoryViewer.hpp index f6995ce..4360a2b 100644 --- a/src/debugger/MemoryViewer.hpp +++ b/src/debugger/MemoryViewer.hpp @@ -9,7 +9,7 @@ class MemoryViewer : public DebugWindow { public: - MemoryViewer(Bus* bus); + MemoryViewer(Debugger* debugger, Bus* bus); virtual void OnRender() override; diff --git a/src/debugger/NametableViewer.cpp b/src/debugger/NametableViewer.cpp index a7bb05b..93624ab 100644 --- a/src/debugger/NametableViewer.cpp +++ b/src/debugger/NametableViewer.cpp @@ -1,36 +1,83 @@ #include "NametableViewer.hpp" +#include #include #include "../Bus.hpp" -NametableViewer::NametableViewer(Bus* bus) : - DebugWindow("Nametable Viewer"), bus(bus) +NametableViewer::NametableViewer(Debugger* debugger, Bus* bus) : + DebugWindow("Nametable Viewer", debugger), bus(bus), texture(0) { + glCreateTextures(GL_TEXTURE_2D, 1, &texture); + glTextureStorage2D(texture, 1, GL_R8, 32, 32); + + glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +} + +NametableViewer::~NametableViewer() +{ + glDeleteTextures(1, &texture); } void NametableViewer::OnRender() { ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); - if (!ImGui::Begin(title.c_str(), &isOpen)) + if (!ImGui::Begin(title.c_str(), &isOpen, ImGuiWindowFlags_MenuBar)) { ImGui::End(); return; } - ImGui::BeginTabBar("Nametables"); - for (uint8_t index = 0; index < 4; index++) + if (ImGui::BeginMenuBar()) { - char baseAddress[6]; - std::sprintf(baseAddress, "$2%X00", index * 4); + if (ImGui::BeginMenu("Views")) + { + ImGui::MenuItem("Rendered View", NULL, &renderNametable); + + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + + ImGui::BeginTabBar("Nametables"); + for (uint8_t index = 0; index < 2; index++) + { + char baseAddress[12]; + std::sprintf(baseAddress, "Nametable %c", 'A' + index); if (ImGui::BeginTabItem(baseAddress)) { DisplayNametable(index); + if (renderNametable) + { + glTextureSubImage2D(texture, 0, 0, 0, 32, 32, GL_RED, GL_UNSIGNED_BYTE, &bus->VRAM[0x400 * index]); + } + ImGui::EndTabItem(); } } ImGui::EndTabBar(); + if (renderNametable) + { + ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("Rendered Nametable", &renderNametable)) + { + ImGui::End(); + return; + } + + + + float smallerSize = std::min(ImGui::GetWindowWidth(), ImGui::GetWindowHeight()) - 20.0f; + if (smallerSize < 40.0f) + smallerSize = 40.0f; + + ImGui::Image((ImTextureID)texture, ImVec2{smallerSize, smallerSize - 20.0f}); + ImGui::End(); + } + ImGui::End(); } diff --git a/src/debugger/NametableViewer.hpp b/src/debugger/NametableViewer.hpp index f7fec9b..37329df 100644 --- a/src/debugger/NametableViewer.hpp +++ b/src/debugger/NametableViewer.hpp @@ -8,7 +8,8 @@ class NametableViewer : public DebugWindow { public: - NametableViewer(Bus* bus); + NametableViewer(Debugger* debugger, Bus* bus); + ~NametableViewer(); virtual void OnRender() override; @@ -17,4 +18,6 @@ private: private: Bus* bus; + uint32_t texture; + bool renderNametable = false; }; diff --git a/src/debugger/PPUWatcher.cpp b/src/debugger/PPUWatcher.cpp index 8cced7f..b7eb704 100644 --- a/src/debugger/PPUWatcher.cpp +++ b/src/debugger/PPUWatcher.cpp @@ -3,8 +3,8 @@ #include #include "../PPU.hpp" -PPUWatcher::PPUWatcher(PPU* ppu) : - DebugWindow("PPU Watch"), ppu(ppu) +PPUWatcher::PPUWatcher(Debugger* debugger, PPU* ppu) : + DebugWindow("PPU Watch", debugger), ppu(ppu) { } diff --git a/src/debugger/PPUWatcher.hpp b/src/debugger/PPUWatcher.hpp index 472399c..26ceca7 100644 --- a/src/debugger/PPUWatcher.hpp +++ b/src/debugger/PPUWatcher.hpp @@ -8,7 +8,7 @@ class PPUWatcher : public DebugWindow { public: - PPUWatcher(PPU* ppu); + PPUWatcher(Debugger* debugger, PPU* ppu); virtual void OnRender() override; diff --git a/src/mappers/Mapper000.cpp b/src/mappers/Mapper000.cpp index f3aa7ed..1d95020 100644 --- a/src/mappers/Mapper000.cpp +++ b/src/mappers/Mapper000.cpp @@ -4,7 +4,8 @@ #include #include "../Cartridge.hpp" -Mapper000::Mapper000(Header& header, std::ifstream& ifs) +Mapper000::Mapper000(const Header& header, std::ifstream& ifs) : + Mapper(header) { LOG_CORE_INFO("Allocating PRG ROM"); PRG_ROM = std::vector(0x4000); diff --git a/src/mappers/Mapper000.hpp b/src/mappers/Mapper000.hpp index 65e51ff..daac4b3 100644 --- a/src/mappers/Mapper000.hpp +++ b/src/mappers/Mapper000.hpp @@ -10,7 +10,7 @@ class Mapper000 : public Mapper { public: - Mapper000(Header& header, std::ifstream& ifs); + Mapper000(const Header& header, std::ifstream& ifs); virtual Byte ReadCPU(Word addr) override; virtual Byte ReadPPU(Word addr) override;