From ab4a43163a677823da3289d52989fdd14485b083 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 28 Jun 2025 01:40:57 -0400 Subject: [PATCH] Agregando disasembler --- src/CMakeLists.txt | 2 + src/Interpreter/Instruction.h | 3 +- src/Interpreter/Interpreter.cpp | 33 +++++- src/Interpreter/Interpreter.h | 6 +- src/Interpreter/OpCode.h | 2 +- src/Machine.cpp | 2 + src/UI/CallbackManager.h | 3 + src/UI/Disassembler.cpp | 189 ++++++++++++++++++++++++++++++++ src/UI/Disassembler.h | 33 ++++++ src/UI/UIManager.cpp | 5 +- src/UI/UIManager.h | 2 + 11 files changed, 269 insertions(+), 11 deletions(-) create mode 100644 src/UI/Disassembler.cpp create mode 100644 src/UI/Disassembler.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bee3a69..f26b5a7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,8 @@ target_sources( UI/RomInfo.h UI/UIManager.cpp UI/UIManager.h + UI/Disassembler.cpp + UI/Disassembler.h ) add_compile_options(-Wall -Wextra -Wpedantic -Werror) diff --git a/src/Interpreter/Instruction.h b/src/Interpreter/Instruction.h index 5673f7f..1b4b7da 100644 --- a/src/Interpreter/Instruction.h +++ b/src/Interpreter/Instruction.h @@ -8,6 +8,7 @@ struct Instruction { OpCode op_code; + uint16_t address; uint8_t operation; uint16_t instruction; uint8_t x; @@ -17,4 +18,4 @@ struct Instruction uint16_t nnn; }; -#endif //INSTRUCTION_H \ No newline at end of file +#endif //INSTRUCTION_H diff --git a/src/Interpreter/Interpreter.cpp b/src/Interpreter/Interpreter.cpp index 3c3c1c8..98da7aa 100644 --- a/src/Interpreter/Interpreter.cpp +++ b/src/Interpreter/Interpreter.cpp @@ -14,13 +14,20 @@ Interpreter::Interpreter(std::shared_ptr machine_state) : } } -void Interpreter::tick() +uint16_t Interpreter::get_word(uint16_t address) const { - const uint8_t high_word = this->machine_state->memory[this->machine_state->pc]; - const uint8_t low_word = this->machine_state->memory[this->machine_state->pc + 1]; + const uint8_t high_word = this->machine_state->memory[address]; + const uint8_t low_word = this->machine_state->memory[address + 1]; const uint16_t word = high_word << 8 | low_word; - const auto instruction = this->decode(word); + return word; +} + +void Interpreter::tick() +{ + auto word = this->get_word(this->machine_state->pc); + + const auto instruction = this->decode(word, this->machine_state->pc); this->machine_state->pc += 2; this->execute_instruction(instruction); @@ -31,7 +38,7 @@ void Interpreter::tick() } } -Instruction Interpreter::decode(const uint16_t word) const +Instruction Interpreter::decode(const uint16_t word, const uint16_t address) const { const uint8_t operation = (word & 0xF000) >> 12; const uint8_t x = (word & 0x0F00) >> 8; @@ -197,6 +204,7 @@ Instruction Interpreter::decode(const uint16_t word) const return Instruction{ .op_code = op_code, + .address = address, .operation = operation, .instruction = word, .x = x, @@ -207,6 +215,19 @@ Instruction Interpreter::decode(const uint16_t word) const }; } +std::vector Interpreter::disassembly() const +{ + std::vector instructions(std::size(machine_state->memory) / 2); + + for (auto address = 0; address < std::size(machine_state->memory); address += 2) + { + const auto word = this->get_word(address); + instructions[address / 2] = (this->decode(word, address)); + } + + return instructions; +} + void Interpreter::execute_instruction(const Instruction& instruction) { if (instruction.op_code == OpCode::CLS) this->cls(); @@ -622,4 +643,4 @@ void Interpreter::ld_vx_i(const Instruction& instruction) const this->machine_state->v[reg] = this->machine_state->memory[this->machine_state->i + reg]; } } -} \ No newline at end of file +} diff --git a/src/Interpreter/Interpreter.h b/src/Interpreter/Interpreter.h index 67e5895..44eb20f 100644 --- a/src/Interpreter/Interpreter.h +++ b/src/Interpreter/Interpreter.h @@ -21,7 +21,7 @@ class Interpreter uint8_t quirks = 0; - Instruction decode(uint16_t word) const; + Instruction decode(uint16_t word, uint16_t address) const; void execute_instruction(const Instruction& instruction); void sys_addr() const; @@ -63,9 +63,11 @@ class Interpreter public: explicit Interpreter(std::shared_ptr machine_state); + uint16_t get_word(uint16_t address) const; + std::vector disassembly() const; void tick(); }; -#endif //INTERPRETER_H \ No newline at end of file +#endif //INTERPRETER_H diff --git a/src/Interpreter/OpCode.h b/src/Interpreter/OpCode.h index a8dc642..cbbd5e9 100644 --- a/src/Interpreter/OpCode.h +++ b/src/Interpreter/OpCode.h @@ -41,4 +41,4 @@ enum class OpCode NOP, // INVALID OPERATION }; -#endif //OPCODE_H \ No newline at end of file +#endif //OPCODE_H diff --git a/src/Machine.cpp b/src/Machine.cpp index a25b4aa..82835a4 100644 --- a/src/Machine.cpp +++ b/src/Machine.cpp @@ -37,6 +37,8 @@ void Machine::iterate() this->execute_interpreter(); } + this->callback_manager->trigger(this->callback_manager->disassembly_callback, this->interpreter->disassembly()); + this->graphics->start(); this->ui_manager->render(); this->graphics->end(); diff --git a/src/UI/CallbackManager.h b/src/UI/CallbackManager.h index 616825f..16069e0 100644 --- a/src/UI/CallbackManager.h +++ b/src/UI/CallbackManager.h @@ -5,6 +5,8 @@ #include #include +#include "../Interpreter/Instruction.h" + struct CallbackManager { std::vector> rom_load_callback; @@ -14,6 +16,7 @@ struct CallbackManager std::vector> step_callback; std::vector> ips_callback; std::vector> reload_callback; + std::vector)>> disassembly_callback; template void trigger(const std::vector& callbacks, Args&&... args) diff --git a/src/UI/Disassembler.cpp b/src/UI/Disassembler.cpp new file mode 100644 index 0000000..d233c5e --- /dev/null +++ b/src/UI/Disassembler.cpp @@ -0,0 +1,189 @@ +#include "Disassembler.h" + +#include +#include +#include + +#include "imgui.h" + +Disassembler::Disassembler( + std::shared_ptr graphics, + std::shared_ptr machine_state, + std::shared_ptr callback_manager) : + graphis{std::move(graphics)}, + machine_state{std::move(machine_state)}, + callback_manager{std::move(callback_manager)} +{ + this->callback_manager->disassembly_callback.push_back(std::bind( + &Disassembler::on_disassembly_callback, + this, + std::placeholders::_1 + )); +} + +void Disassembler::render() +{ + if (ImGui::Begin("Chip-8 - Disassembly")) + { + ImGui::Checkbox("Follow", &this->follow); + + ImGuiListClipper clipper; + clipper.Begin(this->disassembly.size()); + ImGui::BeginChild("##instructions"); + + if (this->follow) + { + size_t pc_index = machine_state->pc / 2; + const float line_h = ImGui::GetTextLineHeightWithSpacing(); + + if (pc_index < disassembly.size()) + { + const float window_h = ImGui::GetWindowHeight(); + const float target_y = pc_index * line_h; + const float scroll_y = target_y - (window_h * 0.1f) + (line_h * 0.1f); + ImGui::SetScrollY(scroll_y); + } + } + + + while (clipper.Step()) + { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const auto& instruction = this->disassembly[i]; + auto line = this->build_line(instruction); + if (instruction.address == machine_state->pc) + { + ImGui::TextColored(ImVec4(1, 1, 0, 1), "%s", line.c_str()); + } + else + { + ImGui::TextUnformatted(line.c_str()); + } + } + } + ImGui::EndChild(); + } + ImGui::End(); +} + +void Disassembler::on_disassembly_callback(const std::vector& disassembly) +{ + this->disassembly = disassembly; +} + +std::string Disassembler::build_line(const Instruction instruction) const +{ + const std::string addr_prefix = std::format("0x{:0>4x} | ", instruction.address); + + switch (instruction.op_code) + { + case OpCode::CLS: + return addr_prefix + "CLS"; + + case OpCode::RET: + return addr_prefix + "RET"; + + case OpCode::SYS_ADDR: + return addr_prefix + "SYS"; + + case OpCode::JP_ADDR: + return addr_prefix + std::format("JP 0x{:0>3x}", instruction.nnn); + + case OpCode::CALL_ADDR: + return addr_prefix + std::format("CALL 0x{:0>3x}", instruction.nnn); + + case OpCode::SE_VX_BYTE: + return addr_prefix + std::format("SE V{}, 0x{:0>2x}", instruction.x, instruction.kk); + + case OpCode::SNE_VX_BYTE: + return addr_prefix + std::format("SNE V{}, 0x{:0>2x}", instruction.x, instruction.kk); + + case OpCode::SE_VX_VY: + return addr_prefix + std::format("SE V{}, V{}", instruction.x, instruction.y); + + case OpCode::LD_VX_BYTE: + return addr_prefix + std::format("LD V{}, 0x{:0>2x}", instruction.x, instruction.kk); + + case OpCode::ADD_VX_BYTE: + return addr_prefix + std::format("ADD V{}, 0x{:0>2x}", instruction.x, instruction.kk); + + case OpCode::LD_VX_VY: + return addr_prefix + std::format("LD V{}, V{}", instruction.x, instruction.y); + + case OpCode::OR_VX_VY: + return addr_prefix + std::format("OR V{}, V{}", instruction.x, instruction.y); + + case OpCode::AND_VX_VY: + return addr_prefix + std::format("AND V{}, V{}", instruction.x, instruction.y); + + case OpCode::XOR_VX_VY: + return addr_prefix + std::format("XOR V{}, V{}", instruction.x, instruction.y); + + case OpCode::ADD_VX_VY: + return addr_prefix + std::format("ADD V{}, V{}", instruction.x, instruction.y); + + case OpCode::SUB_VX_VY: + return addr_prefix + std::format("SUB V{}, V{}", instruction.x, instruction.y); + + case OpCode::SHR_VX_VY: + return addr_prefix + std::format("SHR V{}, V{}", instruction.x, instruction.y); + + case OpCode::SUBN_VX_VY: + return addr_prefix + std::format("SUBN V{}, V{}", instruction.x, instruction.y); + + case OpCode::SHL_VX_VY: + return addr_prefix + std::format("SHL V{}, V{}", instruction.x, instruction.y); + + case OpCode::SNE_VX_VY: + return addr_prefix + std::format("SNE V{}, V{}", instruction.x, instruction.y); + + case OpCode::LD_I_ADDR: + return addr_prefix + std::format("LD I, 0x{:0>3x}", instruction.nnn); + + case OpCode::JP_V0_ADDR: + return addr_prefix + std::format("JP V0, 0x{:0>3x}", instruction.nnn); + + case OpCode::RND_VX_BYTE: + return addr_prefix + std::format("RAND V{}, 0x{:0>2x}", instruction.x, instruction.kk); + + case OpCode::DRW_VX_VY_NIBBLE: + return addr_prefix + std::format("DRW V{}, V{} 0x{:x}", instruction.x, instruction.y, instruction.n); + + case OpCode::SKP_VX: + return addr_prefix + std::format("SKP V{}", instruction.x); + + case OpCode::SKNP_VX: + return addr_prefix + std::format("SKNP V{}", instruction.x); + + case OpCode::LD_VX_DT: + return addr_prefix + std::format("LD V{}, DT", instruction.x); + + case OpCode::LD_VX_K: + return addr_prefix + std::format("LD V{}, K", instruction.x); + + case OpCode::LD_DT_VX: + return addr_prefix + std::format("LD DT, V{}", instruction.x); + + case OpCode::LD_ST_VX: + return addr_prefix + std::format("LD ST, V{}", instruction.x); + + case OpCode::ADD_I_VX: + return addr_prefix + std::format("ADD I, V{}", instruction.x); + + case OpCode::LD_F_VX: + return addr_prefix + std::format("LD F, V{}", instruction.x); + + case OpCode::LD_B_VX: + return addr_prefix + std::format("LD B, V{}", instruction.x); + + case OpCode::LD_I_VX: + return addr_prefix + std::format("LD I, V{}", instruction.x); + + case OpCode::LD_VX_I: + return addr_prefix + std::format("LD V{}, I", instruction.x); + + default: + return addr_prefix + "-"; + } +} diff --git a/src/UI/Disassembler.h b/src/UI/Disassembler.h new file mode 100644 index 0000000..75c6371 --- /dev/null +++ b/src/UI/Disassembler.h @@ -0,0 +1,33 @@ +#ifndef DISASSEMBLER_H +#define DISASSEMBLER_H +#include + +#include "CallbackManager.h" +#include "../Graphics/Graphics.h" +#include "../Interpreter/MachineState.h" + + +class Disassembler +{ + std::shared_ptr graphis; + std::shared_ptr machine_state; + std::shared_ptr callback_manager; + + std::vector disassembly; + + bool follow = false; + +public: + Disassembler( + std::shared_ptr graphics, + std::shared_ptr machine_state, + std::shared_ptr callback_manager + ); + + void render(); + void on_disassembly_callback(const std::vector& disassembly); + std::string build_line(Instruction instruction) const; +}; + + +#endif //DISASSEMBLER_H diff --git a/src/UI/UIManager.cpp b/src/UI/UIManager.cpp index 90ecf1f..7b58e44 100644 --- a/src/UI/UIManager.cpp +++ b/src/UI/UIManager.cpp @@ -1,5 +1,6 @@ #include "UIManager.h" +#include "Disassembler.h" #include "MemoryViewer.h" #include "RomInfo.h" @@ -15,7 +16,8 @@ UIManager::UIManager( display{std::make_unique(this->graphics, this->machine_state)}, control_panel{std::make_unique(this->graphics, this->machine_state, this->callback_manager)}, rom_info{std::make_unique(this->graphics, this->machine_state, this->callback_manager)}, - memory_viewer{std::make_unique(this->graphics, this->machine_state, this->callback_manager)} + memory_viewer{std::make_unique(this->graphics, this->machine_state, this->callback_manager)}, + disassembler{std::make_unique(this->graphics, this->machine_state, this->callback_manager)} { } @@ -25,4 +27,5 @@ void UIManager::render() this->control_panel->render(); this->rom_info->render(); this->memory_viewer->render(); + this->disassembler->render(); } diff --git a/src/UI/UIManager.h b/src/UI/UIManager.h index 5c1582d..10b2882 100644 --- a/src/UI/UIManager.h +++ b/src/UI/UIManager.h @@ -4,6 +4,7 @@ #include "CallbackManager.h" #include "ControlPanel.h" +#include "Disassembler.h" #include "Display.h" #include "MemoryViewer.h" #include "RomInfo.h" @@ -18,6 +19,7 @@ class UIManager std::unique_ptr control_panel; std::unique_ptr rom_info; std::unique_ptr memory_viewer; + std::unique_ptr disassembler; public: UIManager(