Feature: Add mov_rm32_r32 instruction & more expressive execution with CPU trace
This commit is contained in:
@@ -5,29 +5,26 @@
|
||||
#include "Bus.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
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 {
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
{
|
||||
static_assert(std::is_unsigned_v<T>, "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>, "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)
|
||||
{
|
||||
|
||||
33
src/CPU.cpp
33
src/CPU.cpp
@@ -3,6 +3,7 @@
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <bitset>
|
||||
#include <stdlib.h>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
@@ -10,10 +11,15 @@
|
||||
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;
|
||||
|
||||
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<uint8_t>(m_InstructionPointer);
|
||||
Opcode opcode = static_cast<Opcode>(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<uint8_t>(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<uint8_t>(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!");
|
||||
|
||||
@@ -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<Bus> m_Bus;
|
||||
|
||||
uint64_t m_InstructionRaw;
|
||||
Instruction m_Instruction;
|
||||
|
||||
CPUContext m_Context;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,32 @@
|
||||
|
||||
#include "ExecutorCases.h"
|
||||
#include "Instruction.h"
|
||||
#include "Bus.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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<uint32_t>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ struct CPUContext;
|
||||
|
||||
namespace executor_cases {
|
||||
void Mov_r32_imm32(CPUContext& cc);
|
||||
void Mov_rm32_r32(CPUContext& cc);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ constexpr std::array<ExecutorCase, 255> 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<ExecutorCase, 255> s_ExecutorTable = GenerateExecuto
|
||||
const std::array<ExecutorCase, 255>& 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,3 +22,7 @@ struct CPUContext {
|
||||
typedef void (*ExecutorCase)(CPUContext&);
|
||||
|
||||
const std::array<ExecutorCase, 255>& GetExecutorTable();
|
||||
|
||||
namespace executor_cases::helpers {
|
||||
uint32_t ResolveModRMAddress(CPUContext& cc);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
|
||||
namespace executor_cases {
|
||||
void Nop(CPUContext& cc){
|
||||
std::cout << "No op" << std::endl;
|
||||
std::cout << "[Instruction] nop" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
12
src/main.cpp
12
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user