diff --git a/CMakeLists.txt b/CMakeLists.txt index 492efdd..a900df3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,11 +3,12 @@ project(emulator C CXX) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_STANDDARD 23) +set(CMAKE_CXX_STANDARD 26) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_subdirectory(external/glfw) +add_subdirectory(external/gtest) if(CMAKE_BUILD_TYPE STREQUAL "Debug") add_compile_definitions(Debug) @@ -39,3 +40,5 @@ target_include_directories(emulator PRIVATE external/glfw/include external/imgui external/imgui_club/imgui_memory_editor ) + +add_subdirectory(tests) diff --git a/MOV — Move.pdf b/MOV — Move.pdf deleted file mode 100644 index 1425c7c..0000000 Binary files a/MOV — Move.pdf and /dev/null differ diff --git a/src/Arithmetic.cpp b/src/Arithmetic.cpp index 9d2ad1b..07be745 100644 --- a/src/Arithmetic.cpp +++ b/src/Arithmetic.cpp @@ -10,7 +10,7 @@ namespace executor_cases { void Add_rm32_r32(CPUContext& cc){ - x86::ModRM modrm = cc.m_Instruction.optional.m_ModRM; + x86::ModRM modrm = cc.m_Instruction.m_ModRM; std::cout << "[Instruction] "; @@ -55,7 +55,7 @@ namespace executor_cases { namespace helpers { void Add_rm_dst(CPUContext& cc, uint32_t address) { uint32_t dstPrevValue = cc.m_Bus->AccessX(address); - uint32_t currRegValue = cc.m_Registers[cc.m_Instruction.optional.m_ModRM.m_Reg]; + uint32_t currRegValue = cc.m_Registers[cc.m_Instruction.m_ModRM.m_Reg]; uint32_t result = dstPrevValue + currRegValue; cc.m_Bus->WriteX(address, result); } diff --git a/src/Bus.h b/src/Bus.h index d92da23..d55ee4f 100644 --- a/src/Bus.h +++ b/src/Bus.h @@ -9,6 +9,8 @@ #include "RAM.h" +#include "Exceptions.h" + class Bus { public: Bus(std::shared_ptr m_Bus); @@ -31,7 +33,8 @@ public: break; } default: - throw std::runtime_error("Illegal Access!"); + std::string exception = "Illegal access to: " + std::to_string(address); + throw CPUException(exception); } } @@ -52,7 +55,8 @@ public: return value; } default: - throw std::runtime_error("Illegal Access!"); + std::string exception = "Illegal access to: " + std::to_string(address); + throw std::runtime_error(exception); } } diff --git a/src/CPU.cpp b/src/CPU.cpp index 52b69ef..dd03a19 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -8,6 +8,11 @@ #include #include +#include "Exceptions.h" +#include "InstructionModifierLookup.h" + +static const Instruction s_EmptyInstruction{}; + CPU::CPU(std::shared_ptr& bus) : m_Bus(bus), m_IsHalted(false), m_Context({m_Instruction, m_InstructionPointer, m_Flags, m_Registers, m_Bus, m_IsHalted}) { m_InstructionPointer = 0x00008000; @@ -54,12 +59,7 @@ void CPU::Reset() { std::cout << "[CPU] State Flushed!" << std::endl; m_IsHalted = false; - m_Instruction.m_Opcode = (Opcode)0; - m_Instruction.m_Operand1 = 0; - m_Instruction.m_Operand2 = 0; - m_Instruction.m_Func = nullptr; - std::memset(m_Instruction.m_Displacement, 0, 4); - m_Instruction.optional.m_ModRM = (x86::ModRM){ .m_State = x86::ModRMState::INVALID, .m_Reg = 0, .m_Rm = 0 }; + m_Instruction = s_EmptyInstruction; } CPUStatus CPU::GetStatus() { @@ -67,13 +67,16 @@ CPUStatus CPU::GetStatus() { } void CPU::FetchDecode() { + m_Instruction = s_EmptyInstruction; + uint8_t opcode_raw = m_Bus->AccessX(m_InstructionPointer); Opcode opcode = static_cast(opcode_raw); auto& instruction_table = GetInstructionTable(); if(instruction_table[opcode_raw].m_Executor == nullptr) { std::cout << "Encoding: " << opcode_raw << std::endl; - throw std::runtime_error("[CPU] [FetchDecode] Opcode is not in instruction table!"); + std::string exception = "[CPU] [FetchDecode] Opcode is not in instruction table! Opcode_raw: " + std::to_string(opcode_raw); + throw CPUException(exception); } auto& instruction = instruction_table[opcode_raw]; @@ -82,42 +85,48 @@ void CPU::FetchDecode() { uint8_t encoding = instruction.m_Encoding & encoding_mask; uint8_t immediate = instruction.m_Encoding & ~encoding_mask; + m_Instruction.m_Opcode = opcode; + switch(encoding) { case OI: if (immediate == I32) { m_Instruction.m_Operand1 = m_Bus->AccessX(m_InstructionPointer + 1); m_Instruction.m_Length = 5; - } else if (immediate = I16) { + } else if (immediate == I16) { m_Instruction.m_Operand1 = m_Bus->AccessX(m_InstructionPointer + 1); m_Instruction.m_Length = 3; - } else if (immediate = I8) { + } else if (immediate == I8) { m_Instruction.m_Operand1 = m_Bus->AccessX(m_InstructionPointer + 1); m_Instruction.m_Length = 2; } else { - throw std::runtime_error("[CPU] [FetchDecode] OI instruction encoding only supports imm8,imm16,imm32."); + throw CPUException("[CPU] [FetchDecode] OI instruction encoding only supports imm8,imm16,imm32."); } break; case ZO: m_Instruction.m_Length = 1; break; case RM: - case MR: - m_Instruction.optional.m_ModRM = x86::process_modrm(m_Bus->AccessX(m_InstructionPointer + 1)); + case MR: { + uint8_t modrm = m_Bus->AccessX(m_InstructionPointer + 1); + m_Instruction.m_ModRM = x86::ProcessMODRM(modrm); m_Instruction.m_Length = 2; - FetchModRMFields(); + FetchModRMFields(modrm); + if (m_Instruction.m_ModRM.m_SIB) { + uint8_t sib = m_Bus->AccessX(m_InstructionPointer + m_Instruction.m_Length); + m_Instruction.m_Length += 1; + m_Instruction.m_SIB = x86::ProcessSIB(sib); + } break; + } default: - throw std::runtime_error("[CPU] [FetchDecode] Encoding was not found!"); + throw CPUException("[CPU] [FetchDecode] Encoding was not found!"); } m_Instruction.m_Func = instruction.m_Executor; - m_Instruction.m_Opcode = opcode; m_InstructionPointer += m_Instruction.m_Length; } void CPU::Execute() { - uint8_t opcode_value = static_cast(m_Instruction.m_Opcode); - if(m_Instruction.m_Func != nullptr) { m_Instruction.m_Func(m_Context); @@ -126,28 +135,8 @@ void CPU::Execute() { throw std::runtime_error("[CPU] [Execute] m_Func is nullptr!"); } -void CPU::FetchModRMFields() { +void CPU::FetchModRMFields(uint8_t modrm) { assert(m_Instruction.m_Length != 0); // FetchDecode() must set m_Length before calling FetchModRMFields() - x86::ModRMState state = m_Instruction.optional.m_ModRM.m_State; - switch(state) { - case x86::ModRMState::LR: - case x86::ModRMState::R: - break; - case x86::ModRMState::DISP32: - case x86::ModRMState::LR_DISP32: - { - uint32_t disp = m_Bus->AccessX(m_InstructionPointer + m_Instruction.m_Length); - std::memcpy(&m_Instruction.m_Displacement, &disp, 4); - m_Instruction.m_DisplacementSize = 4; - m_Instruction.m_Length += 4; - break; - } - case x86::ModRMState::LR_DISP8: - m_Instruction.m_Displacement[0] = m_Bus->AccessX(m_InstructionPointer + m_Instruction.m_Length); - m_Instruction.m_DisplacementSize = 1; - m_Instruction.m_Length += 1; - break; - default: - throw std::runtime_error("Instruction could not be modified according to the modrm field!"); - } + auto modrm_table = GetMod32Table(); + modrm_table[modrm](m_Context); } diff --git a/src/CPU.h b/src/CPU.h index 27ca9d7..1ad96aa 100644 --- a/src/CPU.h +++ b/src/CPU.h @@ -8,6 +8,10 @@ #include "ExecutorCases.h" #include "CPUContext.h" +enum class CPUExecutionMode { + BIT_32, +}; + struct CPUStatus { uint32_t* m_Registers; uint32_t& m_IP; @@ -32,8 +36,9 @@ public: private: void FetchDecode(); void Execute(); - + private: + CPUExecutionMode m_Mode; uint32_t m_Registers[8]; uint16_t m_SegmentRegisters[8]; uint32_t m_InstructionPointer; @@ -47,5 +52,6 @@ private: CPUContext m_Context; private: // FetchDecode() must set m_Length before calling FetchModRMFields() - void FetchModRMFields(); + void FetchModRMFields(uint8_t modrm); + void FetchSIB(); }; diff --git a/src/DataTransfer.cpp b/src/DataTransfer.cpp index eb59598..f7cc063 100644 --- a/src/DataTransfer.cpp +++ b/src/DataTransfer.cpp @@ -10,7 +10,7 @@ namespace executor_cases { void Mov_rm32_r32(CPUContext& cc) { - x86::ModRM modrm = cc.m_Instruction.optional.m_ModRM; + x86::ModRM modrm = cc.m_Instruction.m_ModRM; std::cout << "[Instruction] "; diff --git a/src/DataTransfer.h b/src/DataTransfer.h index 96d3274..a8ee9a1 100644 --- a/src/DataTransfer.h +++ b/src/DataTransfer.h @@ -15,7 +15,6 @@ namespace executor_cases { uint8_t reg = cc.m_Instruction.m_Opcode - O; std::cout << "[Instruction] mov " << x86::Register2Str((x86::Register)reg) << ", " << std::hex << cc.m_Instruction.m_Operand1 << std::endl; std::memcpy(&cc.m_Registers[reg], &cc.m_Instruction.m_Operand1, sizeof(T)); - // cc.m_Registers[reg] = cc.m_Instruction.m_Operand1; } void Mov_rm32_r32(CPUContext& cc); diff --git a/src/Exceptions.h b/src/Exceptions.h new file mode 100644 index 0000000..4378b8d --- /dev/null +++ b/src/Exceptions.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class CPUException : std::runtime_error { +public: + CPUException(std::string what) : std::runtime_error(what), m_What(what) { } + ~CPUException() = default; + + std::string GetMessage() { return m_What; } + +private: + std::string m_What; +}; diff --git a/src/ExecutorCases.cpp b/src/ExecutorCases.cpp index befa070..34587be 100644 --- a/src/ExecutorCases.cpp +++ b/src/ExecutorCases.cpp @@ -9,11 +9,13 @@ #include "CPUContext.h" #include "Bus.h" +#include #include #include +#include -constexpr std::array GenerateInstructionTable(){ - std::array table{}; +constexpr std::array GenerateInstructionTable(){ + std::array table{}; for(uint8_t i = 0; i < 255; i++) { table[i] = (InstructionEntry){nullptr, (OperandEncoding)0}; @@ -21,7 +23,6 @@ constexpr std::array GenerateInstructionTable(){ table[Opcode::NOP] = (InstructionEntry){executor_cases::Nop, ZO}; table[Opcode::HLT] = (InstructionEntry){executor_cases::Hlt, ZO}; - // table[Opcode::MOV_R32_IMM16] = (InstructionEntry){executor_cases::Mov_rX_immX, (OperandEncoding)(I16 | OI)}; table[Opcode::MOV_R32_IMM32] = (InstructionEntry){executor_cases::Mov_rX_immX, (OperandEncoding)(I32 | OI)}; table[Opcode::MOV_RM32_R32] = (InstructionEntry){executor_cases::Mov_rm32_r32, MR}; table[Opcode::ADD_RM32_R32] = (InstructionEntry){executor_cases::Add_rm32_r32, MR}; @@ -31,9 +32,8 @@ constexpr std::array GenerateInstructionTable(){ { if((table[i].m_Encoding & OI) != 0) { - for(int j = 1; j < 8 && (i + j) < 255; ++j) { - table[i+j] = table[i]; - } + uint8_t end_offset = std::min(i + 8, 255); + std::fill(table.begin() + i, table.begin() + end_offset, table[i]); i += 8; } else { i++; @@ -42,16 +42,35 @@ constexpr std::array GenerateInstructionTable(){ return table; } -static constexpr std::array s_InstructionTable = GenerateInstructionTable(); +static constexpr std::array s_InstructionTable = GenerateInstructionTable(); -const std::array& GetInstructionTable() { +const std::array& GetInstructionTable() { return s_InstructionTable; } namespace executor_cases::helpers { + + // template + // constexpr std::array GenerateEffectiveAddressCases(std::integer_sequence) { + // return std::array { + // []() { + // return [](CPUContext& c){}; + // }.template operator()()... + // }; + // } + + // constexpr static std::array s_EffectiveAddressCases = GenerateEffectiveAddressCases(std::make_integer_sequence()); + // uint32_t ResolveEffectiveAddress(CPUContext &cc) { + // // uint32_t req = cc.m_Instruction.m_ModRM.m_Rm << 24 | cc.m_Instruction.m_ModRM.m_Reg << 16 | cc.m_Instruction.m_ModRM.m_State << 8; + + // return 0; + // } + uint32_t ResolveModRMAddress(CPUContext& cc) { - x86::ModRM modrm = cc.m_Instruction.optional.m_ModRM; + x86::ModRM modrm = cc.m_Instruction.m_ModRM; + x86::SIB sib = cc.m_Instruction.m_SIB; + uint32_t value = 0; switch(modrm.m_State) { @@ -72,8 +91,7 @@ namespace executor_cases::helpers { default: throw std::runtime_error("Undefined MODRM state encountered!"); } - + return value; } - } diff --git a/src/ExecutorCases.h b/src/ExecutorCases.h index 711b64f..bbcd8ae 100644 --- a/src/ExecutorCases.h +++ b/src/ExecutorCases.h @@ -24,9 +24,9 @@ struct InstructionEntry { OperandEncoding m_Encoding; }; -const std::array& GetInstructionTable(); +const std::array& GetInstructionTable(); -const std::array& GetExecutorTable(); +const std::array& GetExecutorTable(); namespace executor_cases::helpers { uint32_t ResolveModRMAddress(CPUContext& cc); diff --git a/src/GUI.cpp b/src/GUI.cpp index beea004..4601e99 100644 --- a/src/GUI.cpp +++ b/src/GUI.cpp @@ -31,7 +31,7 @@ GUI::GUI(Userspace& user) :m_Userspace(user) { } void GUI::CreateWindow() { - m_Window = glfwCreateWindow(1020, 640, "CPUSecX86", NULL, NULL); + m_Window = glfwCreateWindow(1200, 800, "CPUSecX86", NULL, NULL); if(!m_Window) { glfwTerminate(); @@ -69,6 +69,9 @@ void GUI::Run() { static MemoryEditor process_mem; size_t data_size = 0x10000; process_mem.DrawWindow("Process Memory", m_Userspace.RetrieveMemory(), data_size); + + if(m_Userspace.GetSystemStatus() == SystemStatus::EXCEPTION) + ErrorGUI(); glClear(GL_COLOR_BUFFER_BIT); @@ -90,7 +93,7 @@ void GUI::Terminate() { void GUI::DockingSetup() { ImGuiID dockspace_id = ImGui::GetID("Dockspace"); - ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGuiViewport *viewport = ImGui::GetMainViewport(); if (ImGui::DockBuilderGetNode(dockspace_id) == nullptr) { ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); @@ -109,12 +112,23 @@ void GUI::DockingSetup() { ImGui::DockBuilderFinish(dockspace_id); } - ImGui::DockSpaceOverViewport(dockspace_id, viewport, ImGuiDockNodeFlags_PassthruCentralNode); + ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_NoTabBar; + dockspace_flags |= ImGuiDockNodeFlags_PassthruCentralNode; + + ImGui::DockSpaceOverViewport(dockspace_id, viewport, dockspace_flags); } void GUI::ControlGUI() { ImGui::Begin("Emulator Control"); - ImGui::Text("Current State: %s", m_Userspace.IsRunning() ? "Running" : "Stopped"); + static std::string state = "STOPPED"; + if (m_Userspace.GetSystemStatus() == SystemStatus::STOPPED) + state = "STOPPED"; + else if (m_Userspace.GetSystemStatus() == SystemStatus::RUNNING) + state = "RUNNING"; + else if (m_Userspace.GetSystemStatus() == SystemStatus::EXCEPTION) + state = "EXCEPTION"; + + ImGui::Text("Current State: %s", state.c_str()); if(ImGui::Button("Start")) m_Userspace.Start(); ImGui::SameLine(); @@ -128,11 +142,25 @@ void GUI::ControlGUI() { ImGui::End(); } +void GUI::ErrorGUI() { + ImGui::PushStyleColor(ImGuiCol_TitleBg, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ResizeGrip, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + ImGui::Begin("Exception"); + ImGui::Text("An exception was detected! Trace:"); + auto& e = m_Userspace.GetException(); + for (auto i : e) + ImGui::Text("Err: %s", i.c_str()); + ImGui::End(); + ImGui::PopStyleColor(4); +} + void GUI::DebugInfoGUI() { ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal; - if (m_Userspace.IsRunning()) flags |= ImGuiInputTextFlags_ReadOnly; + if (m_Userspace.GetSystemStatus() == SystemStatus::RUNNING) flags |= ImGuiInputTextFlags_ReadOnly; CPUStatus status = m_Userspace.GetCPUStatus(); - + ImGui::Begin("Debug Information"); ImGui::Text("GP Registers"); ImGui::InputScalar("EAX", ImGuiDataType_U32, &status.m_Registers[0], NULL, NULL, "%08X", flags); @@ -152,9 +180,12 @@ void GUI::DebugInfoGUI() { ImGui::Text("Opcode: %s", Opcode2Str(status.m_Instruction.m_Opcode).c_str()); ImGui::Text("Operand1: %x", status.m_Instruction.m_Operand1); ImGui::Text("Operand2: %x", status.m_Instruction.m_Operand2); - ImGui::Text("ModRM R/M: %d", status.m_Instruction.optional.m_ModRM.m_Rm); - ImGui::Text("ModRM R: %d", status.m_Instruction.optional.m_ModRM.m_Reg); - ImGui::Text("ModRM Status: %d", status.m_Instruction.optional.m_ModRM.m_State); + ImGui::Text("ModRM R/M: %d", status.m_Instruction.m_ModRM.m_Rm); + ImGui::Text("ModRM R: %d", status.m_Instruction.m_ModRM.m_Reg); + ImGui::Text("ModRM Status: %d", status.m_Instruction.m_ModRM.m_State); + ImGui::Text("SIB Scale: %x", status.m_Instruction.m_SIB.m_Scale); + ImGui::Text("SIB Index: %x", status.m_Instruction.m_SIB.m_Index); + ImGui::Text("SIB Base: %x", status.m_Instruction.m_SIB.m_Base); ImGui::End(); } diff --git a/src/GUI.h b/src/GUI.h index 0dbb526..15b6046 100644 --- a/src/GUI.h +++ b/src/GUI.h @@ -19,6 +19,8 @@ private: void ControlGUI(); void DebugInfoGUI(); + void ErrorGUI(); + private: GLFWwindow* m_Window; Userspace& m_Userspace; diff --git a/src/Instruction.cpp b/src/Instruction.cpp index 36b3445..c74139b 100644 --- a/src/Instruction.cpp +++ b/src/Instruction.cpp @@ -1,6 +1,6 @@ #include "Instruction.h" -#include +#include "Exceptions.h" namespace x86 { std::string Register2Str(x86::Register reg) { @@ -16,38 +16,20 @@ namespace x86 { } throw std::runtime_error("Register not found!"); } - // Does not support SIB yet! - ModRM process_modrm(uint8_t modrm){ - uint8_t mod_mask = 0b11000000; - uint8_t reg_mask = 0b00111000; - uint8_t rm_mask = 0b00000111; - - uint8_t mod = modrm & mod_mask; - uint8_t reg = (modrm & reg_mask) >> 3; - uint8_t rm = modrm & rm_mask; - ModRMState state = ModRMState::INVALID; - - switch(mod) { - case 0b00000000: - state = ModRMState::LR; - if(rm == 0b00000101) - state = ModRMState::DISP32; - break; - case 0b01000000: - state = ModRMState::LR_DISP8; - break; - case 0b10000000: - state = ModRMState::LR_DISP32; - break; - case 0b11000000: - state = ModRMState::R; - break; - default: - throw std::runtime_error("Mod R/M does not support non-register operands right now!"); - } - - return {.m_State = state, .m_Reg = reg, .m_Rm = rm}; + SIB ProcessSIB(uint8_t sib) { + uint8_t scale_mask = 0b11000000; + uint8_t index_mask = 0b00111000; + uint8_t base_mask = 0b00000111; + + uint8_t scale = (sib & scale_mask) >> 6; + uint8_t index = (sib & index_mask) >> 3; + uint8_t base = sib & base_mask; + + if(index == 4) index = x86::Register::SIB_NONE; + if(base == 4) base = x86::Register::SIB_DISP; + + return {.m_Scale = scale, .m_Index = (x86::Register)index, .m_Base = (x86::Register)base}; } } @@ -62,6 +44,6 @@ std::string Opcode2Str(Opcode op) { case Opcode::HLT: return "HLT"; } - std::string what = "Opcode '" + std::to_string(op) + "' invalid!"; - throw std::runtime_error(what); + std::string what = "Opcode2Str could not find '" + std::to_string(op) + "'!"; + throw CPUException(what); } diff --git a/src/Instruction.h b/src/Instruction.h index 047ebe7..c80d2ca 100644 --- a/src/Instruction.h +++ b/src/Instruction.h @@ -4,11 +4,15 @@ #include #include "ExecutorCases.h" +#include "Exceptions.h" namespace x86 { enum Register : uint8_t { EAX = 0, ECX = 1, EDX = 2, EBX = 3, - ESP = 4, EBP = 5, ESI = 6, EDI = 7 + ESP = 4, EBP = 5, ESI = 6, EDI = 7, + + SIB_DISP = 254, + SIB_NONE = 255, }; enum class ModRMState : uint8_t @@ -23,11 +27,58 @@ namespace x86 { struct ModRM { ModRMState m_State; - uint8_t m_Reg; + x86::Register m_Reg; uint8_t m_Rm; + bool m_SIB; }; - ModRM process_modrm(uint8_t modrm); + struct SIB { + uint8_t m_Scale; + x86::Register m_Index; + x86::Register m_Base; + }; + + constexpr ModRM ProcessMODRM(uint8_t modrm) { + uint8_t mod_mask = 0b11000000; + uint8_t reg_mask = 0b00111000; + uint8_t rm_mask = 0b00000111; + + uint8_t mod = modrm & mod_mask; + uint8_t reg = (modrm & reg_mask) >> 3; + uint8_t rm = modrm & rm_mask; + + ModRMState state = ModRMState::INVALID; + bool SIB = false; + + switch(mod) { + case 0b00000000: + state = ModRMState::LR; + if(rm == 0b00000101) + state = ModRMState::DISP32; + else if(rm == 0b00000100) + SIB = true; + break; + case 0b01000000: + state = ModRMState::LR_DISP8; + if(rm == 0b00000100) + SIB = true; + break; + case 0b10000000: + state = ModRMState::LR_DISP32; + if(rm == 0b00000100) + SIB = true; + break; + case 0b11000000: + state = ModRMState::R; + break; + default: + throw CPUException("Mod R/M Unknown exception!"); + } + + return {.m_State = state, .m_Reg = (x86::Register)reg, .m_Rm = rm, .m_SIB = SIB}; + } + + SIB ProcessSIB(uint8_t sib); // Helpers std::string Register2Str(x86::Register reg); @@ -52,9 +103,8 @@ struct Instruction{ size_t m_Length; uint32_t m_Operand1; uint32_t m_Operand2; - union { - x86::ModRM m_ModRM; - } optional; + x86::ModRM m_ModRM; + x86::SIB m_SIB; uint8_t m_Displacement[4]; uint8_t m_DisplacementSize; }; diff --git a/src/InstructionModifierLookup.cpp b/src/InstructionModifierLookup.cpp new file mode 100644 index 0000000..87c0350 --- /dev/null +++ b/src/InstructionModifierLookup.cpp @@ -0,0 +1,76 @@ +#include "InstructionModifierLookup.h" + +#include "CPUContext.h" +#include "Instruction.h" +#include "Bus.h" + +#include +#include +#include +#include +#include + +void Mod32_SIB(CPUContext &cc) { + std::cout << "[Mod32_SIB] SIB byte encountered." << std::endl; +} + +void Mod32_LR(CPUContext& cc) { + std::cout << "[Mod32_LR] Executed." << std::endl; + if (cc.m_Instruction.m_ModRM.m_SIB) { + uint32_t src = cc.m_Bus->AccessX(cc.m_InstructionPointer + cc.m_Instruction.m_Length); + std::memcpy(&cc.m_Instruction.m_Displacement, &src, 4); + Mod32_SIB(cc); + } +} + +void Mod32_DISP32(CPUContext& cc) { + uint32_t src = cc.m_Bus->AccessX(cc.m_InstructionPointer + cc.m_Instruction.m_Length); + std::memcpy(&cc.m_Instruction.m_Displacement, &src, 4); + cc.m_Instruction.m_Length += 4; + if (cc.m_Instruction.m_ModRM.m_SIB) + Mod32_SIB(cc); +} + +template +void Mod32_LRDISP32(CPUContext& cc) { + uint32_t disp = cc.m_Registers[RM] + cc.m_Bus->AccessX(cc.m_Instruction.m_Length); + std::memcpy(&cc.m_Instruction.m_Displacement, &disp, 4); + cc.m_Instruction.m_Length += 4; + if (cc.m_Instruction.m_ModRM.m_SIB) + Mod32_SIB(cc); + } + +template +void Mod32_LRDISP8(CPUContext& cc) { + uint32_t disp = cc.m_Registers[RM] + cc.m_Bus->AccessX(cc.m_Instruction.m_Length); + cc.m_Instruction.m_Displacement[0] = disp; + cc.m_Instruction.m_Length += 1; + if (cc.m_Instruction.m_ModRM.m_SIB) + Mod32_SIB(cc); +} + +void Mod32_R(CPUContext& cc) { + std::cout << "[Mod32_R] Executed." << std::endl; +} + +template +static constexpr std::array GenerateModRM32(std::integer_sequence) { + return std::array{ + ([]() { + constexpr auto modrm = x86::ProcessMODRM(I); + + switch (modrm.m_State) { + case x86::ModRMState::LR: return Mod32_LR; + case x86::ModRMState::LR_DISP32: return Mod32_LRDISP32; + case x86::ModRMState::LR_DISP8: return Mod32_LRDISP8; + case x86::ModRMState::DISP32: return Mod32_DISP32; + case x86::ModRMState::R: return Mod32_R; + } + + }.template operator()())... // provide the lambda with a proper template argument + }; +} + +static constexpr std::array s_ModRM32 = GenerateModRM32(std::make_integer_sequence{}); + +std::array GetMod32Table() { return s_ModRM32; } diff --git a/src/InstructionModifierLookup.h b/src/InstructionModifierLookup.h new file mode 100644 index 0000000..1d82023 --- /dev/null +++ b/src/InstructionModifierLookup.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +struct CPUContext; + +typedef void (*ModifierEntry)(CPUContext&); + +std::array GetMod32Table(); + +std::array GetSIBTable(); diff --git a/src/Userspace.cpp b/src/Userspace.cpp index 1c1c6b5..0dc2425 100644 --- a/src/Userspace.cpp +++ b/src/Userspace.cpp @@ -5,10 +5,10 @@ #include -Userspace::Userspace() : m_RAM(std::make_shared()),m_ProgramMemory(std::make_shared()), m_Bus(std::make_shared(m_RAM)), m_CPU(m_Bus) { +Userspace::Userspace() : m_RAM(std::make_shared()), m_ProgramMemory(std::make_shared()), m_Bus(std::make_shared(m_RAM)), m_CPU(m_Bus) { std::cout << "[Userspace] Emulation started." << std::endl; m_ApplicationRunning = true; - m_Running = false; + m_Status = SystemStatus::STOPPED; } void Userspace::Upload2Memory(uint8_t bytes[], size_t len) { @@ -27,36 +27,46 @@ void Userspace::Run() { } } -void Userspace::EmulatorLoop() { - while(m_Running) { - if(!m_CPU.IsHalted()) { - m_CPU.Step(); - } +void Userspace::StepCPU() { + try { + m_CPU.Step(); + } catch (CPUException& c) { + m_Status = SystemStatus::EXCEPTION; + m_ExceptionMessages.push_back(c.GetMessage()); + } +} - if (m_CPU.IsHalted()) { - if(m_Running) - m_Running = false; - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - if(!m_Running) - std::this_thread::sleep_for(std::chrono::milliseconds(10)); +void Userspace::EmulatorLoop() { + while(m_Status == SystemStatus::RUNNING) { + if(!m_CPU.IsHalted()) { + StepCPU(); + } + + if (m_CPU.IsHalted()) { + if(m_Status == SystemStatus::RUNNING) + m_Status = SystemStatus::STOPPED; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + if(m_Status == SystemStatus::STOPPED) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } void Userspace::Reset() { - if(m_CPU.IsHalted()) m_Running = false; + if(m_CPU.IsHalted()) m_Status = SystemStatus::STOPPED; m_CPU.Reset(); std::memcpy(m_RAM->Data(), m_ProgramMemory->Data(), m_ProgramMemoryLen); + m_ExceptionMessages.clear(); } void Userspace::Start() { - m_Running = true; + m_Status = SystemStatus::RUNNING; } void Userspace::Step() { if(!m_CPU.IsHalted()) { - m_CPU.Step(); + StepCPU(); std::cout << "[Userspace] CPU stepped!" << std::endl; } else @@ -66,20 +76,24 @@ void Userspace::Step() { } void Userspace::Stop() { - m_Running = false; + m_Status = SystemStatus::STOPPED; std::cout << "[Userspace] Emulation stopped." << std::endl; } void Userspace::Exit() { - m_Running = false; + m_Status = SystemStatus::STOPPED; m_ApplicationRunning = false; std::cout << "[Userspace] Emulation exited." << std::endl; } -bool Userspace::IsRunning() { - return m_Running; +SystemStatus Userspace::GetSystemStatus() { + return m_Status; } CPUStatus Userspace::GetCPUStatus() { return m_CPU.GetStatus(); } + +std::vector& Userspace::GetException() { + return m_ExceptionMessages; +} diff --git a/src/Userspace.h b/src/Userspace.h index b971a5e..3f9db8c 100644 --- a/src/Userspace.h +++ b/src/Userspace.h @@ -5,9 +5,16 @@ class Bus; #include #include #include +#include #include "CPU.h" +enum class SystemStatus { + STOPPED, + RUNNING, + EXCEPTION +}; + class Userspace { public: Userspace(); @@ -23,20 +30,23 @@ class Userspace { void Reset(); void Exit(); - bool IsRunning(); - + SystemStatus GetSystemStatus(); CPUStatus GetCPUStatus(); + std::vector& GetException(); private: std::shared_ptr m_RAM; std::shared_ptr m_Bus; CPU m_CPU; - std::atomic m_Running = false; + std::atomic m_Status = SystemStatus::STOPPED; std::atomic m_ApplicationRunning = false; + + std::vector m_ExceptionMessages; std::shared_ptr m_ProgramMemory; size_t m_ProgramMemoryLen; private: + void StepCPU(); void EmulatorLoop(); }; diff --git a/src/main.cpp b/src/main.cpp index 2d50f0c..5dd1edb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,7 +26,7 @@ int main(int argc, char** argv) { std::thread tuserspace(&Userspace::Run, &user); gui.Run(); - + user.Exit(); tuserspace.join(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..104ed2c --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,10 @@ +enable_testing() + +add_executable(test test.cpp) + +target_link_libraries(test + GTest::gtest_main + ) + +include(GoogleTest) +gtest_discover_tests(test) diff --git a/tests/test.cpp b/tests/test.cpp new file mode 100644 index 0000000..54efda0 --- /dev/null +++ b/tests/test.cpp @@ -0,0 +1,21 @@ +#include + +//#include "../src/Userspace.cpp" + +// uint8_t test[] = { +// 0xB8, 0x10, +// 0xB9, 0x10, +// 0x01, 0xC0, +// }; + +// Userspace user; + +TEST(ModRM_C0, ModRM) { + // auto a = user.GetCPUStatus(); + + // user.Upload2Memory(test, 6); + // user.Run(); + + //EXPECT_EQ(a.m_Registers[0], 0x20); + EXPECT_EQ(1, 1); +}