Agregando disasembler
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
struct Instruction
|
||||
{
|
||||
OpCode op_code;
|
||||
uint16_t address;
|
||||
uint8_t operation;
|
||||
uint16_t instruction;
|
||||
uint8_t x;
|
||||
|
||||
@@ -14,13 +14,20 @@ Interpreter::Interpreter(std::shared_ptr<MachineState> 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<Instruction> Interpreter::disassembly() const
|
||||
{
|
||||
std::vector<Instruction> 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();
|
||||
|
||||
@@ -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,6 +63,8 @@ class Interpreter
|
||||
|
||||
public:
|
||||
explicit Interpreter(std::shared_ptr<MachineState> machine_state);
|
||||
uint16_t get_word(uint16_t address) const;
|
||||
std::vector<Instruction> disassembly() const;
|
||||
|
||||
void tick();
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../Interpreter/Instruction.h"
|
||||
|
||||
struct CallbackManager
|
||||
{
|
||||
std::vector<std::function<void(const std::string&)>> rom_load_callback;
|
||||
@@ -14,6 +16,7 @@ struct CallbackManager
|
||||
std::vector<std::function<void(int)>> step_callback;
|
||||
std::vector<std::function<void(int)>> ips_callback;
|
||||
std::vector<std::function<void()>> reload_callback;
|
||||
std::vector<std::function<void(const std::vector<Instruction>)>> disassembly_callback;
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
void trigger(const std::vector<Func>& callbacks, Args&&... args)
|
||||
|
||||
189
src/UI/Disassembler.cpp
Normal file
189
src/UI/Disassembler.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "Disassembler.h"
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
Disassembler::Disassembler(
|
||||
std::shared_ptr<Graphics> graphics,
|
||||
std::shared_ptr<MachineState> machine_state,
|
||||
std::shared_ptr<CallbackManager> 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<Instruction>& 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 + "-";
|
||||
}
|
||||
}
|
||||
33
src/UI/Disassembler.h
Normal file
33
src/UI/Disassembler.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef DISASSEMBLER_H
|
||||
#define DISASSEMBLER_H
|
||||
#include <memory>
|
||||
|
||||
#include "CallbackManager.h"
|
||||
#include "../Graphics/Graphics.h"
|
||||
#include "../Interpreter/MachineState.h"
|
||||
|
||||
|
||||
class Disassembler
|
||||
{
|
||||
std::shared_ptr<Graphics> graphis;
|
||||
std::shared_ptr<MachineState> machine_state;
|
||||
std::shared_ptr<CallbackManager> callback_manager;
|
||||
|
||||
std::vector<Instruction> disassembly;
|
||||
|
||||
bool follow = false;
|
||||
|
||||
public:
|
||||
Disassembler(
|
||||
std::shared_ptr<Graphics> graphics,
|
||||
std::shared_ptr<MachineState> machine_state,
|
||||
std::shared_ptr<CallbackManager> callback_manager
|
||||
);
|
||||
|
||||
void render();
|
||||
void on_disassembly_callback(const std::vector<Instruction>& disassembly);
|
||||
std::string build_line(Instruction instruction) const;
|
||||
};
|
||||
|
||||
|
||||
#endif //DISASSEMBLER_H
|
||||
@@ -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<Display>(this->graphics, this->machine_state)},
|
||||
control_panel{std::make_unique<ControlPanel>(this->graphics, this->machine_state, this->callback_manager)},
|
||||
rom_info{std::make_unique<RomInfo>(this->graphics, this->machine_state, this->callback_manager)},
|
||||
memory_viewer{std::make_unique<MemoryViewer>(this->graphics, this->machine_state, this->callback_manager)}
|
||||
memory_viewer{std::make_unique<MemoryViewer>(this->graphics, this->machine_state, this->callback_manager)},
|
||||
disassembler{std::make_unique<Disassembler>(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();
|
||||
}
|
||||
|
||||
@@ -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<ControlPanel> control_panel;
|
||||
std::unique_ptr<RomInfo> rom_info;
|
||||
std::unique_ptr<MemoryViewer> memory_viewer;
|
||||
std::unique_ptr<Disassembler> disassembler;
|
||||
|
||||
public:
|
||||
UIManager(
|
||||
|
||||
Reference in New Issue
Block a user