From caadd2f706bc805b87464825f9b14c09d6179abd Mon Sep 17 00:00:00 2001 From: 0x221E Date: Thu, 5 Feb 2026 14:57:23 +0100 Subject: [PATCH] Feature: Add mov_rm32_r32 instruction & more expressive execution with CPU trace --- src/Arithmetic.cpp | 21 +++++++++------------ src/Bus.h | 4 ++-- src/CPU.cpp | 33 +++++++++++++++++++++++++++++++-- src/CPU.h | 6 ++++-- src/ControlFlow.cpp | 2 +- src/DataTransfer.cpp | 23 +++++++++++++++++++++++ src/DataTransfer.h | 1 + src/ExecutorCases.cpp | 31 +++++++++++++++++++++++++++++++ src/ExecutorCases.h | 4 ++++ src/Instruction.h | 6 ++++-- src/Misc.cpp | 2 +- src/main.cpp | 12 +++++++++--- 12 files changed, 120 insertions(+), 25 deletions(-) diff --git a/src/Arithmetic.cpp b/src/Arithmetic.cpp index d4b6648..6e73fac 100644 --- a/src/Arithmetic.cpp +++ b/src/Arithmetic.cpp @@ -5,29 +5,26 @@ #include "Bus.h" #include +#include namespace executor_cases { - void Add_rm32_r32(CPUContext& cc){ x86::ModRM modrm = cc.m_Instruction.optional.m_ModRM; + + std::cout << "[Instruction] "; switch(modrm.m_State) { case x86::ModRMState::R: cc.m_Registers[modrm.m_Rm] += cc.m_Registers[modrm.m_Reg]; - break; - case x86::ModRMState::LR: - helpers::Add_rm_dst(cc, cc.m_Registers[modrm.m_Rm]); - break; - case x86::ModRMState::LR_DISP8: - case x86::ModRMState::LR_DISP32: - helpers::Add_rm_dst(cc, (uint32_t)(cc.m_Registers[modrm.m_Rm] + cc.m_Instruction.m_Operand1)); - break; - case x86::ModRMState::DISP32: - helpers::Add_rm_dst(cc, cc.m_Instruction.m_Operand1); + std::cout << "add " << x86::Register2Str((x86::Register)modrm.m_Rm) << ", " << x86::Register2Str((x86::Register)modrm.m_Rm); break; default: - throw std::runtime_error("Invalid ModRM State encountered during Add_rm32_r32"); + helpers::Add_rm_dst(cc, helpers::ResolveModRMAddress(cc)); + std::cout << "add DWORD PTR [0x" << helpers::ResolveModRMAddress(cc) << "], " << x86::Register2Str((x86::Register)modrm.m_Reg); + break; } + + std::cout << std::endl; } namespace helpers { diff --git a/src/Bus.h b/src/Bus.h index 5e5489c..f44685a 100644 --- a/src/Bus.h +++ b/src/Bus.h @@ -17,7 +17,7 @@ public: { static_assert(std::is_unsigned_v, "T must be an unsigned int of any size smaller than 8 bytes!"); - std::cout << "Bus write: " << std::hex << address << std::endl; + // std::cout << "Bus write: " << std::hex << address << std::endl; switch(address) { @@ -37,7 +37,7 @@ public: { static_assert(std::is_unsigned_v, "T must be an unsigned int of any size smaller than 8 bytes!"); - std::cout << "Bus access: " << std::hex << address << std::endl; + //std::cout << "Bus access: " << std::hex << address << std::endl; switch(address) { diff --git a/src/CPU.cpp b/src/CPU.cpp index 0a39267..0153e2d 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -10,10 +11,15 @@ 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; - for(int i = 0; i < 16; i++) + for(int i = 0; i < 8; i++) { m_Registers[i] = 0; } + + for(int i = 0; i < 8; i++) + { + m_SegmentRegisters[i] = 0; + } } void CPU::Step() { @@ -21,6 +27,23 @@ void CPU::Step() { Execute(); } +void CPU::Dump() { + std::cout << "--TRACE-- "; + std::cout << std::endl; + + for(uint8_t i = 0; i < 8; i++) + { + std::cout << x86::Register2Str((x86::Register)i) << ": " << m_Registers[i] << " | "; + } + + std::cout << std::endl; + + std::bitset<32> flags(m_Flags); + std::cout << "IP: " << std::hex << m_InstructionPointer << " | FLAGS: " << flags; + + std::cout << std::endl; +} + void CPU::FetchDecode() { uint8_t opcode_raw = m_Bus->AccessX(m_InstructionPointer); Opcode opcode = static_cast(opcode_raw); @@ -43,18 +66,24 @@ void CPU::FetchDecode() { m_Instruction.m_Length = 2; FetchModRMFields(); break; + case Opcode::MOV_RM32_R32: + m_Instruction.m_Opcode = opcode; + m_Instruction.optional.m_ModRM = x86::process_modrm(m_Bus->AccessX(m_InstructionPointer + 1)); + m_Instruction.m_Length = 2; + FetchModRMFields(); + break; } m_InstructionPointer += m_Instruction.m_Length; } void CPU::Execute() { - std::cout << "Executing... \n"; uint8_t opcode_value = static_cast(m_Instruction.m_Opcode); auto& exec_table = GetExecutorTable(); if(exec_table[opcode_value]) { exec_table[opcode_value](m_Context); + Dump(); return; } throw std::runtime_error("Opcode not found!"); diff --git a/src/CPU.h b/src/CPU.h index 61fe5a2..7b2e0ad 100644 --- a/src/CPU.h +++ b/src/CPU.h @@ -15,6 +15,8 @@ public: public: void Step(); + void Dump(); + bool IsHalted() { return m_IsHalted; } private: @@ -22,14 +24,14 @@ private: void Execute(); private: - uint32_t m_Registers[16]; + uint32_t m_Registers[8]; + uint16_t m_SegmentRegisters[8]; uint32_t m_InstructionPointer; uint32_t m_Flags; bool m_IsHalted; std::shared_ptr m_Bus; - uint64_t m_InstructionRaw; Instruction m_Instruction; CPUContext m_Context; diff --git a/src/ControlFlow.cpp b/src/ControlFlow.cpp index dbbb0d0..a79b4e1 100644 --- a/src/ControlFlow.cpp +++ b/src/ControlFlow.cpp @@ -6,7 +6,7 @@ namespace executor_cases { void Hlt(CPUContext& cc){ - std::cout << "Program halted!" << std::endl; + std::cout << "[Instruction] hlt" << std::endl; cc.m_IsHalted = true; } } diff --git a/src/DataTransfer.cpp b/src/DataTransfer.cpp index 0799c14..c4cb22c 100644 --- a/src/DataTransfer.cpp +++ b/src/DataTransfer.cpp @@ -2,9 +2,32 @@ #include "ExecutorCases.h" #include "Instruction.h" +#include "Bus.h" + +#include namespace executor_cases { void Mov_r32_imm32(CPUContext& cc) { + std::cout << "[Instruction] mov " << x86::Register2Str((x86::Register)cc.m_Instruction.m_Operand1) << ", " << std::hex << cc.m_Instruction.m_Operand2 << std::endl; cc.m_Registers[cc.m_Instruction.m_Operand1] = cc.m_Instruction.m_Operand2; } + + void Mov_rm32_r32(CPUContext& cc) { + x86::ModRM modrm = cc.m_Instruction.optional.m_ModRM; + + std::cout << "[Instruction] "; + + switch(modrm.m_State) { + case x86::ModRMState::R: + cc.m_Registers[modrm.m_Rm] = cc.m_Registers[modrm.m_Reg]; + std::cout << "mov " << x86::Register2Str((x86::Register)modrm.m_Rm) << ", " << x86::Register2Str((x86::Register)modrm.m_Rm); + break; + default: + cc.m_Bus->WriteX(helpers::ResolveModRMAddress(cc), cc.m_Registers[modrm.m_Reg]); + std::cout << "mov DWORD PTR [0x" << helpers::ResolveModRMAddress(cc) << "], " << x86::Register2Str((x86::Register)modrm.m_Reg); + break; + } + + std::cout << std::endl; + } } diff --git a/src/DataTransfer.h b/src/DataTransfer.h index 00ee5a1..c0626b9 100644 --- a/src/DataTransfer.h +++ b/src/DataTransfer.h @@ -4,4 +4,5 @@ struct CPUContext; namespace executor_cases { void Mov_r32_imm32(CPUContext& cc); + void Mov_rm32_r32(CPUContext& cc); } diff --git a/src/ExecutorCases.cpp b/src/ExecutorCases.cpp index 74c4bc6..f710e6d 100644 --- a/src/ExecutorCases.cpp +++ b/src/ExecutorCases.cpp @@ -20,6 +20,7 @@ constexpr std::array GenerateExecutorTable(){ table[Opcode::NOP] = executor_cases::Nop; table[Opcode::HLT] = executor_cases::Hlt; table[Opcode::MOV_R32_IMM32] = executor_cases::Mov_r32_imm32; + table[Opcode::MOV_RM32_R32] = executor_cases::Mov_rm32_r32; table[Opcode::ADD_RM32_R32] = executor_cases::Add_rm32_r32; return table; } @@ -29,3 +30,33 @@ static constexpr std::array s_ExecutorTable = GenerateExecuto const std::array& GetExecutorTable() { return s_ExecutorTable; } + +namespace executor_cases::helpers { + + uint32_t ResolveModRMAddress(CPUContext& cc) { + x86::ModRM modrm = cc.m_Instruction.optional.m_ModRM; + uint32_t value = 0; + + switch(modrm.m_State) { + case x86::ModRMState::LR_DISP32: + case x86::ModRMState::LR_DISP8: + value = cc.m_Registers[modrm.m_Rm] + cc.m_Instruction.m_Operand1; + break; + case x86::ModRMState::DISP32: + value = cc.m_Instruction.m_Operand1; + break; + case x86::ModRMState::LR: + value = cc.m_Registers[modrm.m_Rm]; + break; + case x86::ModRMState::R: + std::cout << "x86::ModRM reached x86::ModRMState::R during ResolveModRMAddress()" << std::endl; + std::cout << "This behavior is unexpected." << std::endl; + break; + default: + throw std::runtime_error("Undefined MODRM state encountered!"); + } + + return value; + } + +} diff --git a/src/ExecutorCases.h b/src/ExecutorCases.h index 92a13f7..7a0f546 100644 --- a/src/ExecutorCases.h +++ b/src/ExecutorCases.h @@ -22,3 +22,7 @@ struct CPUContext { typedef void (*ExecutorCase)(CPUContext&); const std::array& GetExecutorTable(); + +namespace executor_cases::helpers { + uint32_t ResolveModRMAddress(CPUContext& cc); +} diff --git a/src/Instruction.h b/src/Instruction.h index b190821..4f4da0e 100644 --- a/src/Instruction.h +++ b/src/Instruction.h @@ -21,7 +21,7 @@ namespace x86 { R = 5 }; - struct ModRM{ + struct ModRM { ModRMState m_State; uint8_t m_Reg; uint8_t m_Rm; @@ -37,6 +37,7 @@ enum Opcode : uint8_t { NOP = 0x90, HLT = 0xF4, MOV_R32_IMM32 = 0xB8, + MOV_RM32_R32 = 0x89, ADD_RM32_R32 = 0x01, }; @@ -47,7 +48,8 @@ struct Instruction{ size_t m_Length; uint32_t m_Operand1; uint32_t m_Operand2; + // uint8_t m_Displacement[]; union { - x86::ModRM m_ModRM; + x86::ModRM m_ModRM; } optional; }; diff --git a/src/Misc.cpp b/src/Misc.cpp index b89d906..f4b3063 100644 --- a/src/Misc.cpp +++ b/src/Misc.cpp @@ -6,6 +6,6 @@ namespace executor_cases { void Nop(CPUContext& cc){ - std::cout << "No op" << std::endl; + std::cout << "[Instruction] nop" << std::endl; } } diff --git a/src/main.cpp b/src/main.cpp index 3b85100..8195829 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,19 +2,25 @@ #include "Metal.h" +// nop // mov eax, 0xF4 // mov ecx, 0x8000 -// add [0x8010], eax +// mov edx, 0x8000 +// mov [edx], eax +// add DWORD PTR [0x8011], eax uint8_t test[] = { + 0x90, 0xB8, 0xF4, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x80, 0x00, 0x00, - 0x01, 0x05, 0x10, 0x80, 0x00, 0x00, + 0xBA, 0x00, 0x80, 0x00, 0x00, + 0x89, 0x02, + 0x01, 0x05, 0x18, 0x80, 0x00, 0x00, 0x00, }; int main(int argc, char** argv) { Metal metal; - metal.Upload2Memory(test, 17); + metal.Upload2Memory(test, 25); metal.Run(); return 0; }