Refactor: ModRM Handler replaced with compile-time lookup table.
Started refactoring to a library-based model to easily integrate comprehensive unit testing. Added CPU exception handling system for the GUI. Non-critical exceptions (though critical to execution of emulated application) will not shutdown the application in the future releases. Refactored Userspace::Run() logic so that the GUI-controls reflect emulator behavior.
This commit is contained in:
@@ -3,11 +3,12 @@ project(emulator C CXX)
|
|||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
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_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
add_subdirectory(external/glfw)
|
add_subdirectory(external/glfw)
|
||||||
|
add_subdirectory(external/gtest)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
add_compile_definitions(Debug)
|
add_compile_definitions(Debug)
|
||||||
@@ -39,3 +40,5 @@ target_include_directories(emulator PRIVATE external/glfw/include
|
|||||||
external/imgui
|
external/imgui
|
||||||
external/imgui_club/imgui_memory_editor
|
external/imgui_club/imgui_memory_editor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(tests)
|
||||||
|
|||||||
BIN
MOV — Move.pdf
BIN
MOV — Move.pdf
Binary file not shown.
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace executor_cases {
|
namespace executor_cases {
|
||||||
void Add_rm32_r32(CPUContext& cc){
|
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] ";
|
std::cout << "[Instruction] ";
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ namespace executor_cases {
|
|||||||
namespace helpers {
|
namespace helpers {
|
||||||
void Add_rm_dst(CPUContext& cc, uint32_t address) {
|
void Add_rm_dst(CPUContext& cc, uint32_t address) {
|
||||||
uint32_t dstPrevValue = cc.m_Bus->AccessX<uint32_t>(address);
|
uint32_t dstPrevValue = cc.m_Bus->AccessX<uint32_t>(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;
|
uint32_t result = dstPrevValue + currRegValue;
|
||||||
cc.m_Bus->WriteX<uint32_t>(address, result);
|
cc.m_Bus->WriteX<uint32_t>(address, result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
#include "RAM.h"
|
#include "RAM.h"
|
||||||
|
|
||||||
|
#include "Exceptions.h"
|
||||||
|
|
||||||
class Bus {
|
class Bus {
|
||||||
public:
|
public:
|
||||||
Bus(std::shared_ptr<RAM> m_Bus);
|
Bus(std::shared_ptr<RAM> m_Bus);
|
||||||
@@ -31,7 +33,8 @@ public:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Illegal Access!");
|
std::string exception = "Illegal access to: " + std::to_string(address);
|
||||||
|
throw std::runtime_error(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
69
src/CPU.cpp
69
src/CPU.cpp
@@ -8,6 +8,11 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "Exceptions.h"
|
||||||
|
#include "InstructionModifierLookup.h"
|
||||||
|
|
||||||
|
static const Instruction s_EmptyInstruction{};
|
||||||
|
|
||||||
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}) {
|
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;
|
||||||
|
|
||||||
@@ -54,12 +59,7 @@ void CPU::Reset() {
|
|||||||
|
|
||||||
std::cout << "[CPU] State Flushed!" << std::endl;
|
std::cout << "[CPU] State Flushed!" << std::endl;
|
||||||
m_IsHalted = false;
|
m_IsHalted = false;
|
||||||
m_Instruction.m_Opcode = (Opcode)0;
|
m_Instruction = s_EmptyInstruction;
|
||||||
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 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CPUStatus CPU::GetStatus() {
|
CPUStatus CPU::GetStatus() {
|
||||||
@@ -67,13 +67,16 @@ CPUStatus CPU::GetStatus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::FetchDecode() {
|
void CPU::FetchDecode() {
|
||||||
|
m_Instruction = s_EmptyInstruction;
|
||||||
|
|
||||||
uint8_t opcode_raw = m_Bus->AccessX<uint8_t>(m_InstructionPointer);
|
uint8_t opcode_raw = m_Bus->AccessX<uint8_t>(m_InstructionPointer);
|
||||||
Opcode opcode = static_cast<Opcode>(opcode_raw);
|
Opcode opcode = static_cast<Opcode>(opcode_raw);
|
||||||
|
|
||||||
auto& instruction_table = GetInstructionTable();
|
auto& instruction_table = GetInstructionTable();
|
||||||
if(instruction_table[opcode_raw].m_Executor == nullptr) {
|
if(instruction_table[opcode_raw].m_Executor == nullptr) {
|
||||||
std::cout << "Encoding: " << opcode_raw << std::endl;
|
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];
|
auto& instruction = instruction_table[opcode_raw];
|
||||||
@@ -82,42 +85,48 @@ void CPU::FetchDecode() {
|
|||||||
uint8_t encoding = instruction.m_Encoding & encoding_mask;
|
uint8_t encoding = instruction.m_Encoding & encoding_mask;
|
||||||
uint8_t immediate = instruction.m_Encoding & ~encoding_mask;
|
uint8_t immediate = instruction.m_Encoding & ~encoding_mask;
|
||||||
|
|
||||||
|
m_Instruction.m_Opcode = opcode;
|
||||||
|
|
||||||
switch(encoding) {
|
switch(encoding) {
|
||||||
case OI:
|
case OI:
|
||||||
if (immediate == I32) {
|
if (immediate == I32) {
|
||||||
m_Instruction.m_Operand1 = m_Bus->AccessX<uint32_t>(m_InstructionPointer + 1);
|
m_Instruction.m_Operand1 = m_Bus->AccessX<uint32_t>(m_InstructionPointer + 1);
|
||||||
m_Instruction.m_Length = 5;
|
m_Instruction.m_Length = 5;
|
||||||
} else if (immediate = I16) {
|
} else if (immediate == I16) {
|
||||||
m_Instruction.m_Operand1 = m_Bus->AccessX<uint16_t>(m_InstructionPointer + 1);
|
m_Instruction.m_Operand1 = m_Bus->AccessX<uint16_t>(m_InstructionPointer + 1);
|
||||||
m_Instruction.m_Length = 3;
|
m_Instruction.m_Length = 3;
|
||||||
} else if (immediate = I8) {
|
} else if (immediate == I8) {
|
||||||
m_Instruction.m_Operand1 = m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1);
|
m_Instruction.m_Operand1 = m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1);
|
||||||
m_Instruction.m_Length = 2;
|
m_Instruction.m_Length = 2;
|
||||||
} else {
|
} 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;
|
break;
|
||||||
case ZO:
|
case ZO:
|
||||||
m_Instruction.m_Length = 1;
|
m_Instruction.m_Length = 1;
|
||||||
break;
|
break;
|
||||||
case RM:
|
case RM:
|
||||||
case MR:
|
case MR: {
|
||||||
m_Instruction.optional.m_ModRM = x86::process_modrm(m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1));
|
uint8_t modrm = m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1);
|
||||||
|
m_Instruction.m_ModRM = x86::ProcessMODRM(modrm);
|
||||||
m_Instruction.m_Length = 2;
|
m_Instruction.m_Length = 2;
|
||||||
FetchModRMFields();
|
FetchModRMFields(modrm);
|
||||||
|
if (m_Instruction.m_ModRM.m_SIB) {
|
||||||
|
uint8_t sib = m_Bus->AccessX<uint8_t>(m_InstructionPointer + m_Instruction.m_Length);
|
||||||
|
m_Instruction.m_Length += 1;
|
||||||
|
m_Instruction.m_SIB = x86::ProcessSIB(sib);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
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_Func = instruction.m_Executor;
|
||||||
m_Instruction.m_Opcode = opcode;
|
|
||||||
m_InstructionPointer += m_Instruction.m_Length;
|
m_InstructionPointer += m_Instruction.m_Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::Execute() {
|
void CPU::Execute() {
|
||||||
uint8_t opcode_value = static_cast<uint8_t>(m_Instruction.m_Opcode);
|
|
||||||
|
|
||||||
if(m_Instruction.m_Func != nullptr)
|
if(m_Instruction.m_Func != nullptr)
|
||||||
{
|
{
|
||||||
m_Instruction.m_Func(m_Context);
|
m_Instruction.m_Func(m_Context);
|
||||||
@@ -126,28 +135,8 @@ void CPU::Execute() {
|
|||||||
throw std::runtime_error("[CPU] [Execute] m_Func is nullptr!");
|
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()
|
assert(m_Instruction.m_Length != 0); // FetchDecode() must set m_Length before calling FetchModRMFields()
|
||||||
x86::ModRMState state = m_Instruction.optional.m_ModRM.m_State;
|
auto modrm_table = GetMod32Table();
|
||||||
switch(state) {
|
modrm_table[modrm](m_Context);
|
||||||
case x86::ModRMState::LR:
|
|
||||||
case x86::ModRMState::R:
|
|
||||||
break;
|
|
||||||
case x86::ModRMState::DISP32:
|
|
||||||
case x86::ModRMState::LR_DISP32:
|
|
||||||
{
|
|
||||||
uint32_t disp = m_Bus->AccessX<uint32_t>(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<uint8_t>(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!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
#include "ExecutorCases.h"
|
#include "ExecutorCases.h"
|
||||||
#include "CPUContext.h"
|
#include "CPUContext.h"
|
||||||
|
|
||||||
|
enum class CPUExecutionMode {
|
||||||
|
BIT_32,
|
||||||
|
};
|
||||||
|
|
||||||
struct CPUStatus {
|
struct CPUStatus {
|
||||||
uint32_t* m_Registers;
|
uint32_t* m_Registers;
|
||||||
uint32_t& m_IP;
|
uint32_t& m_IP;
|
||||||
@@ -34,6 +38,7 @@ private:
|
|||||||
void Execute();
|
void Execute();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
CPUExecutionMode m_Mode;
|
||||||
uint32_t m_Registers[8];
|
uint32_t m_Registers[8];
|
||||||
uint16_t m_SegmentRegisters[8];
|
uint16_t m_SegmentRegisters[8];
|
||||||
uint32_t m_InstructionPointer;
|
uint32_t m_InstructionPointer;
|
||||||
@@ -47,5 +52,6 @@ private:
|
|||||||
CPUContext m_Context;
|
CPUContext m_Context;
|
||||||
private:
|
private:
|
||||||
// FetchDecode() must set m_Length before calling FetchModRMFields()
|
// FetchDecode() must set m_Length before calling FetchModRMFields()
|
||||||
void FetchModRMFields();
|
void FetchModRMFields(uint8_t modrm);
|
||||||
|
void FetchSIB();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
namespace executor_cases {
|
namespace executor_cases {
|
||||||
|
|
||||||
void Mov_rm32_r32(CPUContext& cc) {
|
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] ";
|
std::cout << "[Instruction] ";
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ namespace executor_cases {
|
|||||||
uint8_t reg = cc.m_Instruction.m_Opcode - O;
|
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::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));
|
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);
|
void Mov_rm32_r32(CPUContext& cc);
|
||||||
|
|||||||
14
src/Exceptions.h
Normal file
14
src/Exceptions.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
@@ -9,11 +9,13 @@
|
|||||||
#include "CPUContext.h"
|
#include "CPUContext.h"
|
||||||
#include "Bus.h"
|
#include "Bus.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
constexpr std::array<InstructionEntry, 255> GenerateInstructionTable(){
|
constexpr std::array<InstructionEntry, 256> GenerateInstructionTable(){
|
||||||
std::array<InstructionEntry, 255> table{};
|
std::array<InstructionEntry, 256> table{};
|
||||||
|
|
||||||
for(uint8_t i = 0; i < 255; i++) {
|
for(uint8_t i = 0; i < 255; i++) {
|
||||||
table[i] = (InstructionEntry){nullptr, (OperandEncoding)0};
|
table[i] = (InstructionEntry){nullptr, (OperandEncoding)0};
|
||||||
@@ -21,7 +23,6 @@ constexpr std::array<InstructionEntry, 255> GenerateInstructionTable(){
|
|||||||
|
|
||||||
table[Opcode::NOP] = (InstructionEntry){executor_cases::Nop, ZO};
|
table[Opcode::NOP] = (InstructionEntry){executor_cases::Nop, ZO};
|
||||||
table[Opcode::HLT] = (InstructionEntry){executor_cases::Hlt, ZO};
|
table[Opcode::HLT] = (InstructionEntry){executor_cases::Hlt, ZO};
|
||||||
// table[Opcode::MOV_R32_IMM16] = (InstructionEntry){executor_cases::Mov_rX_immX<uint16_t, 0xB8>, (OperandEncoding)(I16 | OI)};
|
|
||||||
table[Opcode::MOV_R32_IMM32] = (InstructionEntry){executor_cases::Mov_rX_immX<uint32_t, 0xB8>, (OperandEncoding)(I32 | OI)};
|
table[Opcode::MOV_R32_IMM32] = (InstructionEntry){executor_cases::Mov_rX_immX<uint32_t, 0xB8>, (OperandEncoding)(I32 | OI)};
|
||||||
table[Opcode::MOV_RM32_R32] = (InstructionEntry){executor_cases::Mov_rm32_r32, MR};
|
table[Opcode::MOV_RM32_R32] = (InstructionEntry){executor_cases::Mov_rm32_r32, MR};
|
||||||
table[Opcode::ADD_RM32_R32] = (InstructionEntry){executor_cases::Add_rm32_r32, MR};
|
table[Opcode::ADD_RM32_R32] = (InstructionEntry){executor_cases::Add_rm32_r32, MR};
|
||||||
@@ -31,9 +32,8 @@ constexpr std::array<InstructionEntry, 255> GenerateInstructionTable(){
|
|||||||
{
|
{
|
||||||
if((table[i].m_Encoding & OI) != 0)
|
if((table[i].m_Encoding & OI) != 0)
|
||||||
{
|
{
|
||||||
for(int j = 1; j < 8 && (i + j) < 255; ++j) {
|
uint8_t end_offset = std::min(i + 8, 255);
|
||||||
table[i+j] = table[i];
|
std::fill(table.begin() + i, table.begin() + end_offset, table[i]);
|
||||||
}
|
|
||||||
i += 8;
|
i += 8;
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
@@ -42,16 +42,35 @@ constexpr std::array<InstructionEntry, 255> GenerateInstructionTable(){
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr std::array<InstructionEntry, 255> s_InstructionTable = GenerateInstructionTable();
|
static constexpr std::array<InstructionEntry, 256> s_InstructionTable = GenerateInstructionTable();
|
||||||
|
|
||||||
const std::array<InstructionEntry, 255>& GetInstructionTable() {
|
const std::array<InstructionEntry, 256>& GetInstructionTable() {
|
||||||
return s_InstructionTable;
|
return s_InstructionTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace executor_cases::helpers {
|
namespace executor_cases::helpers {
|
||||||
|
|
||||||
|
// template<uint8_t... I>
|
||||||
|
// constexpr std::array<ExecutorCase, 256> GenerateEffectiveAddressCases(std::integer_sequence<uint8_t, I...>) {
|
||||||
|
// return std::array<ExecutorCase, 256> {
|
||||||
|
// []<uint8_t S>() {
|
||||||
|
// return [](CPUContext& c){};
|
||||||
|
// }.template operator()<I>()...
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// constexpr static std::array<ExecutorCase, 256> s_EffectiveAddressCases = GenerateEffectiveAddressCases(std::make_integer_sequence<uint8_t, 255>());
|
||||||
|
|
||||||
|
// 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) {
|
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;
|
uint32_t value = 0;
|
||||||
|
|
||||||
switch(modrm.m_State) {
|
switch(modrm.m_State) {
|
||||||
@@ -75,5 +94,4 @@ namespace executor_cases::helpers {
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ struct InstructionEntry {
|
|||||||
OperandEncoding m_Encoding;
|
OperandEncoding m_Encoding;
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::array<InstructionEntry, 255>& GetInstructionTable();
|
const std::array<InstructionEntry, 256>& GetInstructionTable();
|
||||||
|
|
||||||
const std::array<ExecutorCase, 255>& GetExecutorTable();
|
const std::array<ExecutorCase, 256>& GetExecutorTable();
|
||||||
|
|
||||||
namespace executor_cases::helpers {
|
namespace executor_cases::helpers {
|
||||||
uint32_t ResolveModRMAddress(CPUContext& cc);
|
uint32_t ResolveModRMAddress(CPUContext& cc);
|
||||||
|
|||||||
47
src/GUI.cpp
47
src/GUI.cpp
@@ -31,7 +31,7 @@ GUI::GUI(Userspace& user) :m_Userspace(user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GUI::CreateWindow() {
|
void GUI::CreateWindow() {
|
||||||
m_Window = glfwCreateWindow(1020, 640, "CPUSecX86", NULL, NULL);
|
m_Window = glfwCreateWindow(1200, 800, "CPUSecX86", NULL, NULL);
|
||||||
if(!m_Window)
|
if(!m_Window)
|
||||||
{
|
{
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
@@ -70,6 +70,9 @@ void GUI::Run() {
|
|||||||
size_t data_size = 0x10000;
|
size_t data_size = 0x10000;
|
||||||
process_mem.DrawWindow("Process Memory", m_Userspace.RetrieveMemory(), data_size);
|
process_mem.DrawWindow("Process Memory", m_Userspace.RetrieveMemory(), data_size);
|
||||||
|
|
||||||
|
if(m_Userspace.GetSystemStatus() == SystemStatus::EXCEPTION)
|
||||||
|
ErrorGUI();
|
||||||
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
@@ -90,7 +93,7 @@ void GUI::Terminate() {
|
|||||||
|
|
||||||
void GUI::DockingSetup() {
|
void GUI::DockingSetup() {
|
||||||
ImGuiID dockspace_id = ImGui::GetID("Dockspace");
|
ImGuiID dockspace_id = ImGui::GetID("Dockspace");
|
||||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||||
|
|
||||||
if (ImGui::DockBuilderGetNode(dockspace_id) == nullptr) {
|
if (ImGui::DockBuilderGetNode(dockspace_id) == nullptr) {
|
||||||
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
||||||
@@ -109,12 +112,23 @@ void GUI::DockingSetup() {
|
|||||||
ImGui::DockBuilderFinish(dockspace_id);
|
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() {
|
void GUI::ControlGUI() {
|
||||||
ImGui::Begin("Emulator Control");
|
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"))
|
if(ImGui::Button("Start"))
|
||||||
m_Userspace.Start();
|
m_Userspace.Start();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -128,9 +142,23 @@ void GUI::ControlGUI() {
|
|||||||
ImGui::End();
|
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() {
|
void GUI::DebugInfoGUI() {
|
||||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal;
|
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();
|
CPUStatus status = m_Userspace.GetCPUStatus();
|
||||||
|
|
||||||
ImGui::Begin("Debug Information");
|
ImGui::Begin("Debug Information");
|
||||||
@@ -152,9 +180,12 @@ void GUI::DebugInfoGUI() {
|
|||||||
ImGui::Text("Opcode: %s", Opcode2Str(status.m_Instruction.m_Opcode).c_str());
|
ImGui::Text("Opcode: %s", Opcode2Str(status.m_Instruction.m_Opcode).c_str());
|
||||||
ImGui::Text("Operand1: %x", status.m_Instruction.m_Operand1);
|
ImGui::Text("Operand1: %x", status.m_Instruction.m_Operand1);
|
||||||
ImGui::Text("Operand2: %x", status.m_Instruction.m_Operand2);
|
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/M: %d", status.m_Instruction.m_ModRM.m_Rm);
|
||||||
ImGui::Text("ModRM R: %d", status.m_Instruction.optional.m_ModRM.m_Reg);
|
ImGui::Text("ModRM R: %d", status.m_Instruction.m_ModRM.m_Reg);
|
||||||
ImGui::Text("ModRM Status: %d", status.m_Instruction.optional.m_ModRM.m_State);
|
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();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ private:
|
|||||||
void ControlGUI();
|
void ControlGUI();
|
||||||
void DebugInfoGUI();
|
void DebugInfoGUI();
|
||||||
|
|
||||||
|
void ErrorGUI();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLFWwindow* m_Window;
|
GLFWwindow* m_Window;
|
||||||
Userspace& m_Userspace;
|
Userspace& m_Userspace;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "Instruction.h"
|
#include "Instruction.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include "Exceptions.h"
|
||||||
|
|
||||||
namespace x86 {
|
namespace x86 {
|
||||||
std::string Register2Str(x86::Register reg) {
|
std::string Register2Str(x86::Register reg) {
|
||||||
@@ -16,38 +16,20 @@ 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;
|
SIB ProcessSIB(uint8_t sib) {
|
||||||
uint8_t reg = (modrm & reg_mask) >> 3;
|
uint8_t scale_mask = 0b11000000;
|
||||||
uint8_t rm = modrm & rm_mask;
|
uint8_t index_mask = 0b00111000;
|
||||||
|
uint8_t base_mask = 0b00000111;
|
||||||
|
|
||||||
ModRMState state = ModRMState::INVALID;
|
uint8_t scale = (sib & scale_mask) >> 6;
|
||||||
|
uint8_t index = (sib & index_mask) >> 3;
|
||||||
|
uint8_t base = sib & base_mask;
|
||||||
|
|
||||||
switch(mod) {
|
if(index == 4) index = x86::Register::SIB_NONE;
|
||||||
case 0b00000000:
|
if(base == 4) base = x86::Register::SIB_DISP;
|
||||||
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};
|
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";
|
case Opcode::HLT: return "HLT";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string what = "Opcode '" + std::to_string(op) + "' invalid!";
|
std::string what = "Opcode2Str could not find '" + std::to_string(op) + "'!";
|
||||||
throw std::runtime_error(what);
|
throw CPUException(what);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,15 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "ExecutorCases.h"
|
#include "ExecutorCases.h"
|
||||||
|
#include "Exceptions.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,
|
||||||
|
|
||||||
|
SIB_DISP = 254,
|
||||||
|
SIB_NONE = 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ModRMState : uint8_t
|
enum class ModRMState : uint8_t
|
||||||
@@ -23,11 +27,58 @@ namespace x86 {
|
|||||||
|
|
||||||
struct ModRM {
|
struct ModRM {
|
||||||
ModRMState m_State;
|
ModRMState m_State;
|
||||||
uint8_t m_Reg;
|
x86::Register m_Reg;
|
||||||
uint8_t m_Rm;
|
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
|
// Helpers
|
||||||
std::string Register2Str(x86::Register reg);
|
std::string Register2Str(x86::Register reg);
|
||||||
@@ -52,9 +103,8 @@ struct Instruction{
|
|||||||
size_t m_Length;
|
size_t m_Length;
|
||||||
uint32_t m_Operand1;
|
uint32_t m_Operand1;
|
||||||
uint32_t m_Operand2;
|
uint32_t m_Operand2;
|
||||||
union {
|
x86::ModRM m_ModRM;
|
||||||
x86::ModRM m_ModRM;
|
x86::SIB m_SIB;
|
||||||
} optional;
|
|
||||||
uint8_t m_Displacement[4];
|
uint8_t m_Displacement[4];
|
||||||
uint8_t m_DisplacementSize;
|
uint8_t m_DisplacementSize;
|
||||||
};
|
};
|
||||||
|
|||||||
76
src/InstructionModifierLookup.cpp
Normal file
76
src/InstructionModifierLookup.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include "InstructionModifierLookup.h"
|
||||||
|
|
||||||
|
#include "CPUContext.h"
|
||||||
|
#include "Instruction.h"
|
||||||
|
#include "Bus.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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<uint32_t>(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<uint32_t>(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<uint8_t RM>
|
||||||
|
void Mod32_LRDISP32(CPUContext& cc) {
|
||||||
|
uint32_t disp = cc.m_Registers[RM] + cc.m_Bus->AccessX<uint32_t>(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<uint8_t RM>
|
||||||
|
void Mod32_LRDISP8(CPUContext& cc) {
|
||||||
|
uint32_t disp = cc.m_Registers[RM] + cc.m_Bus->AccessX<uint8_t>(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<uint8_t...Is>
|
||||||
|
static constexpr std::array<ModifierEntry, 256> GenerateModRM32(std::integer_sequence<uint8_t, Is...>) {
|
||||||
|
return std::array<ModifierEntry, 256>{
|
||||||
|
([]<uint8_t I>() {
|
||||||
|
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<modrm.m_Rm>;
|
||||||
|
case x86::ModRMState::LR_DISP8: return Mod32_LRDISP8<modrm.m_Rm>;
|
||||||
|
case x86::ModRMState::DISP32: return Mod32_DISP32;
|
||||||
|
case x86::ModRMState::R: return Mod32_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
}.template operator()<Is>())... // provide the lambda with a proper template argument
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<ModifierEntry, 256> s_ModRM32 = GenerateModRM32(std::make_integer_sequence<uint8_t, 255>{});
|
||||||
|
|
||||||
|
std::array<ModifierEntry, 256> GetMod32Table() { return s_ModRM32; }
|
||||||
11
src/InstructionModifierLookup.h
Normal file
11
src/InstructionModifierLookup.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
struct CPUContext;
|
||||||
|
|
||||||
|
typedef void (*ModifierEntry)(CPUContext&);
|
||||||
|
|
||||||
|
std::array<ModifierEntry, 256> GetMod32Table();
|
||||||
|
|
||||||
|
std::array<ModifierEntry, 256> GetSIBTable();
|
||||||
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
Userspace::Userspace() : m_RAM(std::make_shared<RAM>()),m_ProgramMemory(std::make_shared<RAM>()), m_Bus(std::make_shared<Bus>(m_RAM)), m_CPU(m_Bus) {
|
Userspace::Userspace() : m_RAM(std::make_shared<RAM>()), m_ProgramMemory(std::make_shared<RAM>()), m_Bus(std::make_shared<Bus>(m_RAM)), m_CPU(m_Bus) {
|
||||||
std::cout << "[Userspace] Emulation started." << std::endl;
|
std::cout << "[Userspace] Emulation started." << std::endl;
|
||||||
m_ApplicationRunning = true;
|
m_ApplicationRunning = true;
|
||||||
m_Running = false;
|
m_Status = SystemStatus::STOPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Userspace::Upload2Memory(uint8_t bytes[], size_t len) {
|
void Userspace::Upload2Memory(uint8_t bytes[], size_t len) {
|
||||||
@@ -27,36 +27,46 @@ void Userspace::Run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Userspace::EmulatorLoop() {
|
void Userspace::StepCPU() {
|
||||||
while(m_Running) {
|
try {
|
||||||
if(!m_CPU.IsHalted()) {
|
m_CPU.Step();
|
||||||
m_CPU.Step();
|
} catch (CPUException& c) {
|
||||||
}
|
m_Status = SystemStatus::EXCEPTION;
|
||||||
|
m_ExceptionMessages.push_back(c.GetMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_CPU.IsHalted()) {
|
void Userspace::EmulatorLoop() {
|
||||||
if(m_Running)
|
while(m_Status == SystemStatus::RUNNING) {
|
||||||
m_Running = false;
|
if(!m_CPU.IsHalted()) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
StepCPU();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if(!m_Running)
|
if (m_CPU.IsHalted()) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
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() {
|
void Userspace::Reset() {
|
||||||
if(m_CPU.IsHalted()) m_Running = false;
|
if(m_CPU.IsHalted()) m_Status = SystemStatus::STOPPED;
|
||||||
m_CPU.Reset();
|
m_CPU.Reset();
|
||||||
std::memcpy(m_RAM->Data(), m_ProgramMemory->Data(), m_ProgramMemoryLen);
|
std::memcpy(m_RAM->Data(), m_ProgramMemory->Data(), m_ProgramMemoryLen);
|
||||||
|
m_ExceptionMessages.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Userspace::Start() {
|
void Userspace::Start() {
|
||||||
m_Running = true;
|
m_Status = SystemStatus::RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Userspace::Step() {
|
void Userspace::Step() {
|
||||||
if(!m_CPU.IsHalted())
|
if(!m_CPU.IsHalted())
|
||||||
{
|
{
|
||||||
m_CPU.Step();
|
StepCPU();
|
||||||
std::cout << "[Userspace] CPU stepped!" << std::endl;
|
std::cout << "[Userspace] CPU stepped!" << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -66,20 +76,24 @@ void Userspace::Step() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Userspace::Stop() {
|
void Userspace::Stop() {
|
||||||
m_Running = false;
|
m_Status = SystemStatus::STOPPED;
|
||||||
std::cout << "[Userspace] Emulation stopped." << std::endl;
|
std::cout << "[Userspace] Emulation stopped." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Userspace::Exit() {
|
void Userspace::Exit() {
|
||||||
m_Running = false;
|
m_Status = SystemStatus::STOPPED;
|
||||||
m_ApplicationRunning = false;
|
m_ApplicationRunning = false;
|
||||||
std::cout << "[Userspace] Emulation exited." << std::endl;
|
std::cout << "[Userspace] Emulation exited." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Userspace::IsRunning() {
|
SystemStatus Userspace::GetSystemStatus() {
|
||||||
return m_Running;
|
return m_Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPUStatus Userspace::GetCPUStatus() {
|
CPUStatus Userspace::GetCPUStatus() {
|
||||||
return m_CPU.GetStatus();
|
return m_CPU.GetStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>& Userspace::GetException() {
|
||||||
|
return m_ExceptionMessages;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,9 +5,16 @@ class Bus;
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "CPU.h"
|
#include "CPU.h"
|
||||||
|
|
||||||
|
enum class SystemStatus {
|
||||||
|
STOPPED,
|
||||||
|
RUNNING,
|
||||||
|
EXCEPTION
|
||||||
|
};
|
||||||
|
|
||||||
class Userspace {
|
class Userspace {
|
||||||
public:
|
public:
|
||||||
Userspace();
|
Userspace();
|
||||||
@@ -23,20 +30,23 @@ class Userspace {
|
|||||||
void Reset();
|
void Reset();
|
||||||
void Exit();
|
void Exit();
|
||||||
|
|
||||||
bool IsRunning();
|
SystemStatus GetSystemStatus();
|
||||||
|
|
||||||
CPUStatus GetCPUStatus();
|
CPUStatus GetCPUStatus();
|
||||||
|
std::vector<std::string>& GetException();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<RAM> m_RAM;
|
std::shared_ptr<RAM> m_RAM;
|
||||||
std::shared_ptr<Bus> m_Bus;
|
std::shared_ptr<Bus> m_Bus;
|
||||||
CPU m_CPU;
|
CPU m_CPU;
|
||||||
std::atomic<bool> m_Running = false;
|
std::atomic<SystemStatus> m_Status = SystemStatus::STOPPED;
|
||||||
std::atomic<bool> m_ApplicationRunning = false;
|
std::atomic<bool> m_ApplicationRunning = false;
|
||||||
|
|
||||||
|
std::vector<std::string> m_ExceptionMessages;
|
||||||
|
|
||||||
std::shared_ptr<RAM> m_ProgramMemory;
|
std::shared_ptr<RAM> m_ProgramMemory;
|
||||||
size_t m_ProgramMemoryLen;
|
size_t m_ProgramMemoryLen;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void StepCPU();
|
||||||
void EmulatorLoop();
|
void EmulatorLoop();
|
||||||
};
|
};
|
||||||
|
|||||||
10
tests/CMakeLists.txt
Normal file
10
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
enable_testing()
|
||||||
|
|
||||||
|
add_executable(test test.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(test
|
||||||
|
GTest::gtest_main
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GoogleTest)
|
||||||
|
gtest_discover_tests(test)
|
||||||
21
tests/test.cpp
Normal file
21
tests/test.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
//#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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user