Patch: Fix bus access alignment issue, modrm byte handler enhanced.
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "external/gtest"]
|
||||||
|
path = external/gtest
|
||||||
|
url = https://github.com/google/googletest.git
|
||||||
24
README.md
Normal file
24
README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# CPU Security Emulator [x86]
|
||||||
|
This is mostly a learning project for me. I am learning about the internals of a CPU and how it encodes/decodes instructions to produce the intended results.
|
||||||
|
|
||||||
|
As of now, there are limitied features, and supported instructions are limited.
|
||||||
|
|
||||||
|
See; <https://0xinfinity.dev/specs/emulator> for an exhaustive list of supported instructions and specific opcodes.
|
||||||
|
|
||||||
|
## Planned emulator features
|
||||||
|
- [ ] SIB Byte Support for existing and future instructions.
|
||||||
|
- [ ] Execution Lock and Permission system for RAM.
|
||||||
|
- [ ] Branch Prediction and Speculative Execution //Long-term plan
|
||||||
|
- [ ] SIMD flattening (optional) and AV redirection (optional).
|
||||||
|
- [ ] Buffer overflow prediction, detection, and reporting.
|
||||||
|
|
||||||
|
## Planned architectural features
|
||||||
|
- [ ] Better logging system
|
||||||
|
- [ ] Comprehensive unit tests for each instruction.
|
||||||
|
|
||||||
|
## Planned instructions
|
||||||
|
TBD
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- gcc
|
||||||
|
Rest; TBD
|
||||||
1
external/gtest
vendored
Submodule
1
external/gtest
vendored
Submodule
Submodule external/gtest added at 872d386a87
@@ -5,6 +5,6 @@
|
|||||||
Bus::Bus() {
|
Bus::Bus() {
|
||||||
for(int i = 0; i < 992 * 1024; i++)
|
for(int i = 0; i < 992 * 1024; i++)
|
||||||
{
|
{
|
||||||
m_RAM[i] = 0;
|
m_RAM[i] = 0x90;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/Bus.h
31
src/Bus.h
@@ -4,6 +4,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
class Bus {
|
class Bus {
|
||||||
public:
|
public:
|
||||||
@@ -15,25 +16,39 @@ public:
|
|||||||
void WriteX(uint64_t address, T value)
|
void WriteX(uint64_t address, T value)
|
||||||
{
|
{
|
||||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned int of any size smaller than 8 bytes!");
|
static_assert(std::is_unsigned_v<T>, "T must be an unsigned int of any size smaller than 8 bytes!");
|
||||||
T& loc = AccessX<T>(address);
|
|
||||||
loc = value;
|
switch(address)
|
||||||
|
{
|
||||||
|
case 0x00008000 ... 0x000FFFFF:
|
||||||
|
{
|
||||||
|
uint64_t offset = address - 0x00008000;
|
||||||
|
std::memcpy(&m_RAM[offset], &value, sizeof(T));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Illegal Access!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T& AccessX(uint64_t address)
|
T AccessX(uint64_t address)
|
||||||
{
|
{
|
||||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned int of any size smaller than 8 bytes!");
|
static_assert(std::is_unsigned_v<T>, "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;
|
||||||
|
|
||||||
if (address >= 0x00008000 && address <= 0x000FFFFF)
|
switch(address)
|
||||||
|
{
|
||||||
|
case 0x00008000 ... 0x000FFFFF:
|
||||||
{
|
{
|
||||||
uint64_t offset = address - 0x00008000;
|
uint64_t offset = address - 0x00008000;
|
||||||
T* entry = reinterpret_cast<T*>(&m_RAM[offset]);
|
T value;
|
||||||
return entry[0];
|
std::memcpy(&value, &m_RAM[offset], sizeof(T));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Illegal Access!");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Illegal Access!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
69
src/CPU.cpp
69
src/CPU.cpp
@@ -5,8 +5,9 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
CPU::CPU(std::shared_ptr<Bus> bus) : m_Bus(bus), m_Context({m_Instruction, m_InstructionPointer, m_Flags, m_Registers, m_Bus}){
|
CPU::CPU(std::shared_ptr<Bus> bus) : m_Bus(bus), m_IsHalted(false), m_Context({m_Instruction, m_InstructionPointer, m_Flags, m_Registers, m_Bus, m_IsHalted}) {
|
||||||
m_InstructionPointer = 0x00008000;
|
m_InstructionPointer = 0x00008000;
|
||||||
|
|
||||||
for(int i = 0; i < 16; i++)
|
for(int i = 0; i < 16; i++)
|
||||||
@@ -15,48 +16,39 @@ CPU::CPU(std::shared_ptr<Bus> bus) : m_Bus(bus), m_Context({m_Instruction, m_Ins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::Step(){
|
void CPU::Step() {
|
||||||
FetchDecode();
|
FetchDecode();
|
||||||
Execute();
|
Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::FetchDecode(){
|
void CPU::FetchDecode() {
|
||||||
std::cout << "Fetching instruction: " << std::hex << m_InstructionPointer << std::endl;
|
uint8_t opcode_raw = m_Bus->AccessX<uint8_t>(m_InstructionPointer);
|
||||||
m_InstructionRaw = m_Bus->AccessX<uint64_t>(m_InstructionPointer); // Slice of 8 bytes.
|
Opcode opcode = static_cast<Opcode>(opcode_raw);
|
||||||
|
|
||||||
std::cout << "Context window fetched: " << std::hex << m_InstructionRaw << std::endl; // Start Decode Instruction
|
switch(opcode_raw) {
|
||||||
uint64_t first_byte = m_InstructionRaw & 0xFF;
|
case Opcode::MOV_R_IMM32 ... 0xBF: // 0xB8 to 0xBF
|
||||||
std::cout << "Decoded first byte: " << std::hex << first_byte << std::endl;
|
m_Instruction.m_Opcode = Opcode::MOV_R_IMM32;
|
||||||
Opcode opcode = static_cast<Opcode>(first_byte);
|
m_Instruction.m_Operand1 = opcode_raw - 0xB8;
|
||||||
|
|
||||||
if(first_byte >= 0xB8 && first_byte <= 0xBF)
|
|
||||||
{
|
|
||||||
m_Instruction.m_Operand1 = first_byte - 0xB8;
|
|
||||||
opcode = Opcode::MOV_R_IMM32;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(opcode){
|
|
||||||
case Opcode::NOP: m_Instruction.m_Length = 1; break;
|
|
||||||
case Opcode::HLT: m_Instruction.m_Length = 1; break;
|
|
||||||
case Opcode::MOV_R_IMM32:
|
|
||||||
m_Instruction.m_Operand2 = m_Bus->AccessX<uint32_t>(m_InstructionPointer + 1);
|
m_Instruction.m_Operand2 = m_Bus->AccessX<uint32_t>(m_InstructionPointer + 1);
|
||||||
m_Instruction.m_Length = 5;
|
m_Instruction.m_Length = 5;
|
||||||
break;
|
break;
|
||||||
case Opcode::ADD_RM32_R32:
|
case Opcode::NOP:
|
||||||
m_Instruction.m_Operand1 = m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1);
|
case Opcode::HLT:
|
||||||
m_Instruction.m_Operand2 = m_Bus->AccessX<uint32_t>(m_InstructionPointer + 2);
|
m_Instruction.m_Opcode = opcode;
|
||||||
m_Instruction.m_Length = 6;
|
m_Instruction.m_Length = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
case Opcode::ADD_RM32_R32:
|
||||||
std::runtime_error("Decode encountered unexpected opcode.");
|
m_Instruction.m_Opcode = opcode;
|
||||||
|
m_Instruction.optional.m_ModRM = x86::process_modrm(m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1));
|
||||||
|
m_Instruction.m_Length = 2;
|
||||||
|
FetchModRMFields();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Instruction.m_Opcode = opcode;
|
|
||||||
m_InstructionPointer += m_Instruction.m_Length;
|
m_InstructionPointer += m_Instruction.m_Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::Execute(){
|
void CPU::Execute() {
|
||||||
std::cout << "Executing... \n";
|
std::cout << "Executing... \n";
|
||||||
uint8_t opcode_value = static_cast<uint8_t>(m_Instruction.m_Opcode);
|
uint8_t opcode_value = static_cast<uint8_t>(m_Instruction.m_Opcode);
|
||||||
auto& exec_table = GetExecutorTable();
|
auto& exec_table = GetExecutorTable();
|
||||||
@@ -67,3 +59,24 @@ void CPU::Execute(){
|
|||||||
}
|
}
|
||||||
throw std::runtime_error("Opcode not found!");
|
throw std::runtime_error("Opcode not found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPU::FetchModRMFields() {
|
||||||
|
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:
|
||||||
|
m_Instruction.m_Operand1 = m_Bus->AccessX<uint32_t>(m_InstructionPointer + m_Instruction.m_Length);
|
||||||
|
m_Instruction.m_Length += 4;
|
||||||
|
break;
|
||||||
|
case x86::ModRMState::LR_DISP8:
|
||||||
|
m_Instruction.m_Operand1 = m_Bus->AccessX<uint8_t>(m_InstructionPointer + m_Instruction.m_Length);
|
||||||
|
m_Instruction.m_Length += 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Instruction could not be modified according to the modrm field!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
10
src/CPU.h
10
src/CPU.h
@@ -20,9 +20,10 @@ private:
|
|||||||
void Execute();
|
void Execute();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t m_Registers[16];
|
uint32_t m_Registers[16];
|
||||||
uint64_t m_InstructionPointer;
|
uint32_t m_InstructionPointer;
|
||||||
uint64_t m_Flags;
|
uint32_t m_Flags;
|
||||||
|
bool m_IsHalted;
|
||||||
|
|
||||||
std::shared_ptr<Bus> m_Bus;
|
std::shared_ptr<Bus> m_Bus;
|
||||||
|
|
||||||
@@ -30,4 +31,7 @@ private:
|
|||||||
Instruction m_Instruction;
|
Instruction m_Instruction;
|
||||||
|
|
||||||
CPUContext m_Context;
|
CPUContext m_Context;
|
||||||
|
private:
|
||||||
|
// FetchDecode() must set m_Length before calling FetchModRMFields()
|
||||||
|
void FetchModRMFields();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,41 +6,11 @@
|
|||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
CPUContext::CPUContext(Instruction& i, uint64_t& ip, uint64_t& flags, uint64_t* reg, std::shared_ptr<Bus>& bus) : m_Instruction(i), m_InstructionPointer(ip), m_Flags(flags), m_Registers(reg), m_Bus(bus) { }
|
CPUContext::CPUContext(Instruction& i, uint32_t& ip, uint32_t& flags, uint32_t* reg, std::shared_ptr<Bus>& bus, bool& isHalted) : m_Instruction(i), m_InstructionPointer(ip), m_Flags(flags), m_Registers(reg), m_Bus(bus), m_IsHalted(isHalted) { }
|
||||||
|
|
||||||
CPUContext::~CPUContext() = default;
|
CPUContext::~CPUContext() = default;
|
||||||
|
|
||||||
// NO SIB SUPPORT YET
|
// NO SIB SUPPORT 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;
|
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace executor_cases {
|
namespace executor_cases {
|
||||||
void Nop(CPUContext& cc){
|
void Nop(CPUContext& cc){
|
||||||
std::cout << "No op" << std::endl;
|
std::cout << "No op" << std::endl;
|
||||||
@@ -53,32 +23,33 @@ namespace executor_cases {
|
|||||||
|
|
||||||
void Mov_r32_imm32(CPUContext& cc){
|
void Mov_r32_imm32(CPUContext& cc){
|
||||||
cc.m_Registers[cc.m_Instruction.m_Operand1] = cc.m_Instruction.m_Operand2;
|
cc.m_Registers[cc.m_Instruction.m_Operand1] = cc.m_Instruction.m_Operand2;
|
||||||
std::cout << "Contents of " << x86::Register2Str((x86::Register)cc.m_Instruction.m_Operand1) << " changed to " << cc.m_Registers[cc.m_Instruction.m_Operand1] << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//NO SIB SUPPORT YET
|
|
||||||
void Add_rm32_r32(CPUContext& cc){
|
void Add_rm32_r32(CPUContext& cc){
|
||||||
ModRM modrm = process_modrm(cc.m_Instruction.m_Operand1);
|
x86::ModRM modrm = cc.m_Instruction.optional.m_ModRM;
|
||||||
|
|
||||||
switch(modrm.m_State) {
|
switch(modrm.m_State) {
|
||||||
case ModRMState::R:
|
case x86::ModRMState::R:
|
||||||
{
|
{
|
||||||
cc.m_Registers[modrm.m_Rm] += cc.m_Registers[modrm.m_Reg];
|
cc.m_Registers[modrm.m_Rm] += cc.m_Registers[modrm.m_Reg];
|
||||||
std::cout << "Adding " << x86::Register2Str((x86::Register)modrm.m_Reg) << " to " << x86::Register2Str((x86::Register)modrm.m_Rm) << std::endl;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ModRMState::LR:
|
case x86::ModRMState::LR:
|
||||||
{
|
{
|
||||||
uint32_t dstPrevValue = cc.m_Bus->AccessX<uint32_t>(cc.m_Registers[modrm.m_Rm]);
|
uint32_t dstPrevValue = cc.m_Bus->AccessX<uint32_t>(cc.m_Registers[modrm.m_Rm]);
|
||||||
uint32_t currRegValue = cc.m_Registers[modrm.m_Reg];
|
uint32_t currRegValue = cc.m_Registers[modrm.m_Reg];
|
||||||
uint32_t result = dstPrevValue + currRegValue;
|
uint32_t result = dstPrevValue + currRegValue;
|
||||||
cc.m_Bus->WriteX<uint32_t>(cc.m_Registers[modrm.m_Rm], result);
|
cc.m_Bus->WriteX<uint32_t>(cc.m_Registers[modrm.m_Rm], result);
|
||||||
std::cout << "Memory address " << std::hex << cc.m_Registers[modrm.m_Rm] << " modified to: " << result << std::endl;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ModRMState::LR_DISP8:
|
case x86::ModRMState::LR_DISP8:
|
||||||
{
|
{
|
||||||
|
uint32_t dstAddress = cc.m_Registers[modrm.m_Rm] + cc.m_Instruction.m_Operand2;
|
||||||
|
uint32_t dstPrevValue = cc.m_Bus->AccessX<uint32_t>(dstAddress);
|
||||||
|
uint32_t currRegValue = cc.m_Registers[modrm.m_Reg];
|
||||||
|
uint32_t result = dstPrevValue + currRegValue;
|
||||||
|
cc.m_Bus->WriteX<uint32_t>(dstAddress, result);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,30 +9,16 @@ class Bus;
|
|||||||
|
|
||||||
struct CPUContext {
|
struct CPUContext {
|
||||||
Instruction& m_Instruction;
|
Instruction& m_Instruction;
|
||||||
uint64_t& m_InstructionPointer;
|
uint32_t& m_InstructionPointer;
|
||||||
uint64_t& m_Flags;
|
uint32_t& m_Flags;
|
||||||
uint64_t* m_Registers;
|
uint32_t* m_Registers;
|
||||||
std::shared_ptr<Bus> m_Bus;
|
std::shared_ptr<Bus> m_Bus;
|
||||||
|
bool& m_IsHalted;
|
||||||
|
|
||||||
CPUContext(Instruction& i, uint64_t& ip, uint64_t& flags, uint64_t* reg, std::shared_ptr<Bus>& bus);
|
CPUContext(Instruction& i, uint32_t& ip, uint32_t& flags, uint32_t* reg, std::shared_ptr<Bus>& bus, bool& isHalted);
|
||||||
~CPUContext();
|
~CPUContext();
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ModRMState : uint8_t
|
|
||||||
{
|
|
||||||
INVALID = 0,
|
|
||||||
LR = 1,
|
|
||||||
LR_DISP8 = 2,
|
|
||||||
LR_DISP32 = 3,
|
|
||||||
R = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ModRM{
|
|
||||||
ModRMState m_State;
|
|
||||||
uint8_t m_Reg;
|
|
||||||
uint8_t m_Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (*ExecutorCase)(CPUContext&);
|
typedef void (*ExecutorCase)(CPUContext&);
|
||||||
|
|
||||||
const std::array<ExecutorCase, 255>& GetExecutorTable();
|
const std::array<ExecutorCase, 255>& GetExecutorTable();
|
||||||
|
|||||||
@@ -16,4 +16,47 @@ namespace x86 {
|
|||||||
}
|
}
|
||||||
throw std::runtime_error("Register not found!");
|
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(reg == 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};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Opcode2Str(Opcode op) {
|
||||||
|
switch(op) {
|
||||||
|
case Opcode::ADD_RM32_R32: return "ADD_RM32_R32";
|
||||||
|
case Opcode::MOV_R_IMM32: return "MOV_R_IMM32";
|
||||||
|
case Opcode::NOP: return "NOP";
|
||||||
|
case Opcode::HLT: return "HLT";
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Opcode Invalid!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,33 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "ExecutorCases.h"
|
||||||
|
|
||||||
namespace x86 {
|
namespace x86 {
|
||||||
enum Register : uint8_t {
|
enum Register : uint8_t {
|
||||||
EAX = 0, ECX = 1, EDX = 2, EBX = 3,
|
EAX = 0, ECX = 1, EDX = 2, EBX = 3,
|
||||||
ESP = 4, EBP = 5, ESI = 6, EDI = 7
|
ESP = 4, EBP = 5, ESI = 6, EDI = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ModRMState : uint8_t
|
||||||
|
{
|
||||||
|
INVALID = 0,
|
||||||
|
LR = 1,
|
||||||
|
LR_DISP8 = 2,
|
||||||
|
LR_DISP32 = 3,
|
||||||
|
DISP32 = 4,
|
||||||
|
R = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModRM{
|
||||||
|
ModRMState m_State;
|
||||||
|
uint8_t m_Reg;
|
||||||
|
uint8_t m_Rm;
|
||||||
|
};
|
||||||
|
|
||||||
|
ModRM process_modrm(uint8_t modrm);
|
||||||
|
|
||||||
|
// Helpers
|
||||||
std::string Register2Str(x86::Register reg);
|
std::string Register2Str(x86::Register reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,9 +40,14 @@ enum Opcode : uint8_t {
|
|||||||
ADD_RM32_R32 = 0x01,
|
ADD_RM32_R32 = 0x01,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string Opcode2Str(Opcode op);
|
||||||
|
|
||||||
struct Instruction{
|
struct Instruction{
|
||||||
Opcode m_Opcode;
|
Opcode m_Opcode;
|
||||||
size_t m_Length;
|
size_t m_Length;
|
||||||
uint64_t m_Operand1;
|
uint32_t m_Operand1;
|
||||||
uint64_t m_Operand2;
|
uint32_t m_Operand2;
|
||||||
|
union {
|
||||||
|
x86::ModRM m_ModRM;
|
||||||
|
} optional;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
Metal::Metal() : m_Bus(std::make_shared<Bus>()), m_CPU(m_Bus) { }
|
Metal::Metal() : m_Bus(std::make_shared<Bus>()), m_CPU(m_Bus) { }
|
||||||
|
|
||||||
void Metal::Upload2Memory(uint8_t bytes[], size_t len) {
|
void Metal::Upload2Memory(uint8_t bytes[], size_t len) {
|
||||||
uint64_t start = 0x8000;
|
uint64_t start = 0x00008000;
|
||||||
for (size_t i = 0; i < len; i++) {
|
for (size_t i = 0; i < len; i++) {
|
||||||
m_Bus->WriteX(start + i, bytes[i]);
|
m_Bus->WriteX<uint8_t>(start + i, bytes[i]);
|
||||||
std::cout << "Written " << bytes[i] << " to " << std::hex << start + i << std::endl;
|
std::cout << "Written " << std::hex << bytes[i] << " to " << std::hex << start + i << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ class Metal {
|
|||||||
~Metal() = default;
|
~Metal() = default;
|
||||||
|
|
||||||
void Upload2Memory(uint8_t bytes[], size_t len);
|
void Upload2Memory(uint8_t bytes[], size_t len);
|
||||||
|
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
10
src/main.cpp
10
src/main.cpp
@@ -2,13 +2,19 @@
|
|||||||
|
|
||||||
#include "Metal.h"
|
#include "Metal.h"
|
||||||
|
|
||||||
|
// mov eax, 0xF4
|
||||||
|
// mov ecx, 0x800c
|
||||||
|
// add [ecx], eax
|
||||||
uint8_t test[] = {
|
uint8_t test[] = {
|
||||||
0x90, 0xF4
|
0xB8, 0xF4, 0x00, 0x00, 0x00,
|
||||||
|
0xB9, 0x0c, 0x80, 0x00, 0x00,
|
||||||
|
0x01, 0x01,
|
||||||
|
0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
Metal metal;
|
Metal metal;
|
||||||
metal.Upload2Memory(test, 2);
|
metal.Upload2Memory(test, 13);
|
||||||
metal.Run();
|
metal.Run();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user