summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Arithmetic.cpp63
-rw-r--r--src/Arithmetic.h14
-rw-r--r--src/Bus.cpp7
-rw-r--r--src/Bus.h65
-rw-r--r--src/CPU.cpp142
-rw-r--r--src/CPU.h57
-rw-r--r--src/CPUContext.cpp5
-rw-r--r--src/CPUContext.h19
-rw-r--r--src/ControlFlow.cpp13
-rw-r--r--src/ControlFlow.h7
-rw-r--r--src/DataTransfer.cpp30
-rw-r--r--src/DataTransfer.h21
-rw-r--r--src/Exceptions.h14
-rw-r--r--src/ExecutorCases.cpp97
-rw-r--r--src/ExecutorCases.h33
-rw-r--r--src/GUI.cpp194
-rw-r--r--src/GUI.h27
-rw-r--r--src/Instruction.cpp49
-rw-r--r--src/Instruction.h110
-rw-r--r--src/InstructionModifierLookup.cpp76
-rw-r--r--src/InstructionModifierLookup.h11
-rw-r--r--src/Misc.cpp12
-rw-r--r--src/Misc.h7
-rw-r--r--src/RAM.cpp12
-rw-r--r--src/RAM.h14
-rw-r--r--src/Userspace.cpp99
-rw-r--r--src/Userspace.h52
-rw-r--r--src/main.cpp35
28 files changed, 1285 insertions, 0 deletions
diff --git a/src/Arithmetic.cpp b/src/Arithmetic.cpp
new file mode 100644
index 0000000..dd2a1ef
--- /dev/null
+++ b/src/Arithmetic.cpp
@@ -0,0 +1,63 @@
+#include "Arithmetic.h"
+
+#include "ExecutorCases.h"
+#include "CPUContext.h"
+#include "Instruction.h"
+#include "Bus.h"
+
+#include <stdexcept>
+#include <iostream>
+
+namespace executor_cases {
+ void Add_rm32_r32(CPUContext& cc){
+ x86::ModRM modrm = cc.m_Instruction.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 << "add " << x86::Register2Str((x86::Register)modrm.m_Rm) << ", " << x86::Register2Str((x86::Register)modrm.m_Rm);
+ break;
+ default:
+ 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;
+ }
+
+ // TODO: Finish the function
+ void Add_r32_rm32(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_Reg] += cc.m_Registers[modrm.m_Rm];
+ // std::cout << "add " << x86::Register2Str((x86::Register)modrm.m_Reg) << ", " << x86::Register2Str((x86::Register)modrm.m_Rm);
+ // break;
+ // default:
+ // uint32_t dstAddress = ;ResolveModRMAddress(cc);
+ // uint32_t dstPrevValue = cc.m_Bus->AccessX<uint32_t>(dstAddress);
+ // uint32_t currRegValue = cc.m_Registers[cc.m_Instruction.optional.m_ModRM.m_Reg];
+ // uint32_t result = dstPrevValue + currRegValue;
+ // std::cout << "add DWORD PTR [0x" << helpers::ResolveModRMAddress(cc) << "], " << x86::Register2Str((x86::Register)modrm.m_Reg);
+ // break;
+ // }
+
+ // std::cout << std::endl;
+ std::runtime_error("Not implemented!");
+ }
+
+ namespace helpers {
+ void Add_rm_dst(CPUContext& cc, uint32_t address) {
+ uint32_t dstPrevValue = cc.m_Bus->AccessX<uint32_t>(address);
+ uint32_t currRegValue = cc.m_Registers[cc.m_Instruction.m_ModRM.m_Reg];
+ uint32_t result = dstPrevValue + currRegValue;
+ cc.m_Bus->WriteX<uint32_t>(address, result);
+ }
+ }
+}
diff --git a/src/Arithmetic.h b/src/Arithmetic.h
new file mode 100644
index 0000000..fd80fc0
--- /dev/null
+++ b/src/Arithmetic.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <cstdint>
+
+struct CPUContext;
+
+namespace executor_cases {
+ void Add_rm32_r32(CPUContext& cc);
+ void Add_r32_rm32(CPUContext& cc); //TODO: Finish
+
+ namespace helpers {
+ void Add_rm_dst(CPUContext& cc, uint32_t address);
+ }
+}
diff --git a/src/Bus.cpp b/src/Bus.cpp
new file mode 100644
index 0000000..ad035ce
--- /dev/null
+++ b/src/Bus.cpp
@@ -0,0 +1,7 @@
+#include "Bus.h"
+
+#include <stdexcept>
+
+Bus::Bus(std::shared_ptr<RAM> ram) : m_RAM(ram) {
+
+}
diff --git a/src/Bus.h b/src/Bus.h
new file mode 100644
index 0000000..d55ee4f
--- /dev/null
+++ b/src/Bus.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <iostream>
+#include <cstdint>
+#include <type_traits>
+#include <stdexcept>
+#include <cstring>
+#include <memory>
+
+#include "RAM.h"
+
+#include "Exceptions.h"
+
+class Bus {
+public:
+ Bus(std::shared_ptr<RAM> m_Bus);
+ ~Bus() = default;
+
+public:
+ template<typename T>
+ 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!");
+
+ // std::cout << "Bus write: " << std::hex << address << std::endl;
+
+ switch(address)
+ {
+ case 0x00008000 ... 0x000FFFFF:
+ {
+ uint64_t offset = address - 0x00008000;
+ std::memcpy(&m_RAM->Data()[offset], &value, sizeof(T));
+ break;
+ }
+ default:
+ std::string exception = "Illegal access to: " + std::to_string(address);
+ throw CPUException(exception);
+ }
+ }
+
+ template<typename T>
+ 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!");
+
+ //std::cout << "Bus access: " << std::hex << address << std::endl;
+
+ switch(address)
+ {
+ case 0x00008000 ... 0x000FFFFF:
+ {
+ uint64_t offset = address - 0x00008000;
+ T value;
+ std::memcpy(&value, &m_RAM->Data()[offset], sizeof(T));
+ return value;
+ }
+ default:
+ std::string exception = "Illegal access to: " + std::to_string(address);
+ throw std::runtime_error(exception);
+ }
+ }
+
+private:
+ std::shared_ptr<RAM> m_RAM;
+};
diff --git a/src/CPU.cpp b/src/CPU.cpp
new file mode 100644
index 0000000..dd03a19
--- /dev/null
+++ b/src/CPU.cpp
@@ -0,0 +1,142 @@
+#include "CPU.h"
+
+#include <stdexcept>
+#include <iostream>
+#include <iomanip>
+#include <bitset>
+#include <stdlib.h>
+#include <array>
+#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}) {
+ m_InstructionPointer = 0x00008000;
+
+ 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() {
+ FetchDecode();
+ 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::Reset() {
+ for(uint8_t i = 0; i < 8; i++)
+ {
+ m_Registers[i] = 0;
+ m_SegmentRegisters[i] = 0;
+ }
+
+ m_InstructionPointer = 0x8000;
+ m_Flags = 0;
+
+ std::cout << "[CPU] State Flushed!" << std::endl;
+ m_IsHalted = false;
+ m_Instruction = s_EmptyInstruction;
+}
+
+CPUStatus CPU::GetStatus() {
+ return (CPUStatus){.m_Registers = m_Registers, .m_IP = m_InstructionPointer, .m_Instruction = m_Instruction};
+}
+
+void CPU::FetchDecode() {
+ m_Instruction = s_EmptyInstruction;
+
+ uint8_t opcode_raw = m_Bus->AccessX<uint8_t>(m_InstructionPointer);
+ Opcode opcode = static_cast<Opcode>(opcode_raw);
+
+ auto& instruction_table = GetInstructionTable();
+ if(instruction_table[opcode_raw].m_Executor == nullptr) {
+ std::cout << "Encoding: " << opcode_raw << std::endl;
+ 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];
+
+ uint8_t encoding_mask = 0b00001111;
+ 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<uint32_t>(m_InstructionPointer + 1);
+ m_Instruction.m_Length = 5;
+ } else if (immediate == I16) {
+ m_Instruction.m_Operand1 = m_Bus->AccessX<uint16_t>(m_InstructionPointer + 1);
+ m_Instruction.m_Length = 3;
+ } else if (immediate == I8) {
+ m_Instruction.m_Operand1 = m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1);
+ m_Instruction.m_Length = 2;
+ } else {
+ 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: {
+ uint8_t modrm = m_Bus->AccessX<uint8_t>(m_InstructionPointer + 1);
+ m_Instruction.m_ModRM = x86::ProcessMODRM(modrm);
+ m_Instruction.m_Length = 2;
+ 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;
+ }
+ default:
+ throw CPUException("[CPU] [FetchDecode] Encoding was not found!");
+ }
+
+ m_Instruction.m_Func = instruction.m_Executor;
+ m_InstructionPointer += m_Instruction.m_Length;
+}
+
+void CPU::Execute() {
+ if(m_Instruction.m_Func != nullptr)
+ {
+ m_Instruction.m_Func(m_Context);
+ return;
+ }
+ throw std::runtime_error("[CPU] [Execute] m_Func is nullptr!");
+}
+
+void CPU::FetchModRMFields(uint8_t modrm) {
+ assert(m_Instruction.m_Length != 0); // FetchDecode() must set m_Length before calling FetchModRMFields()
+ auto modrm_table = GetMod32Table();
+ modrm_table[modrm](m_Context);
+}
diff --git a/src/CPU.h b/src/CPU.h
new file mode 100644
index 0000000..1ad96aa
--- /dev/null
+++ b/src/CPU.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <cstdint>
+#include <memory>
+
+#include "Instruction.h"
+#include "Bus.h"
+#include "ExecutorCases.h"
+#include "CPUContext.h"
+
+enum class CPUExecutionMode {
+ BIT_32,
+};
+
+struct CPUStatus {
+ uint32_t* m_Registers;
+ uint32_t& m_IP;
+ Instruction& m_Instruction;
+};
+
+class CPU {
+public:
+ CPU(std::shared_ptr<Bus>& bus);
+ ~CPU() = default;
+
+public:
+ void Step();
+
+ void Dump();
+ void Reset();
+
+ bool IsHalted() { return m_IsHalted; }
+
+ CPUStatus GetStatus();
+
+private:
+ void FetchDecode();
+ void Execute();
+
+private:
+ CPUExecutionMode m_Mode;
+ 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;
+
+ Instruction m_Instruction;
+
+ CPUContext m_Context;
+private:
+ // FetchDecode() must set m_Length before calling FetchModRMFields()
+ void FetchModRMFields(uint8_t modrm);
+ void FetchSIB();
+};
diff --git a/src/CPUContext.cpp b/src/CPUContext.cpp
new file mode 100644
index 0000000..e91d581
--- /dev/null
+++ b/src/CPUContext.cpp
@@ -0,0 +1,5 @@
+#include "CPUContext.h"
+
+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;
diff --git a/src/CPUContext.h b/src/CPUContext.h
new file mode 100644
index 0000000..a1402e6
--- /dev/null
+++ b/src/CPUContext.h
@@ -0,0 +1,19 @@
+#pragma once
+
+struct Instruction;
+struct Bus;
+
+#include <cstdint>
+#include <memory>
+
+struct CPUContext {
+ Instruction& m_Instruction;
+ uint32_t& m_InstructionPointer;
+ uint32_t& m_Flags;
+ uint32_t* m_Registers;
+ std::shared_ptr<Bus> m_Bus;
+ bool& m_IsHalted;
+
+ CPUContext(Instruction& i, uint32_t& ip, uint32_t& flags, uint32_t* reg, std::shared_ptr<Bus>& bus, bool& isHalted);
+ ~CPUContext();
+};
diff --git a/src/ControlFlow.cpp b/src/ControlFlow.cpp
new file mode 100644
index 0000000..52465ae
--- /dev/null
+++ b/src/ControlFlow.cpp
@@ -0,0 +1,13 @@
+#include "ControlFlow.h"
+
+#include "ExecutorCases.h"
+#include "CPUContext.h"
+
+#include <iostream>
+
+namespace executor_cases {
+ void Hlt(CPUContext& cc){
+ std::cout << "[Instruction] hlt" << std::endl;
+ cc.m_IsHalted = true;
+ }
+}
diff --git a/src/ControlFlow.h b/src/ControlFlow.h
new file mode 100644
index 0000000..0f8aa87
--- /dev/null
+++ b/src/ControlFlow.h
@@ -0,0 +1,7 @@
+#pragma once
+
+struct CPUContext;
+
+namespace executor_cases {
+ void Hlt(CPUContext& cc);
+}
diff --git a/src/DataTransfer.cpp b/src/DataTransfer.cpp
new file mode 100644
index 0000000..f7cc063
--- /dev/null
+++ b/src/DataTransfer.cpp
@@ -0,0 +1,30 @@
+#include "DataTransfer.h"
+
+#include "ExecutorCases.h"
+#include "Instruction.h"
+#include "CPUContext.h"
+#include "Bus.h"
+
+#include <iostream>
+
+namespace executor_cases {
+
+ void Mov_rm32_r32(CPUContext& cc) {
+ x86::ModRM modrm = cc.m_Instruction.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;
+ }
+}
diff --git a/src/DataTransfer.h b/src/DataTransfer.h
new file mode 100644
index 0000000..a8ee9a1
--- /dev/null
+++ b/src/DataTransfer.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <type_traits>
+#include <cstdint>
+#include <iostream>
+#include <cstring>
+
+#include "CPUContext.h"
+#include "Instruction.h"
+
+namespace executor_cases {
+ template<typename T, int O>
+ void Mov_rX_immX(CPUContext& cc) {
+ static_assert(std::is_unsigned_v<T>, "Mov_rX_immX requires an unsigned type!");
+ 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));
+ }
+
+ 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 <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;
+};
diff --git a/src/ExecutorCases.cpp b/src/ExecutorCases.cpp
new file mode 100644
index 0000000..34587be
--- /dev/null
+++ b/src/ExecutorCases.cpp
@@ -0,0 +1,97 @@
+#include "ExecutorCases.h"
+
+#include "DataTransfer.h"
+#include "ControlFlow.h"
+#include "Arithmetic.h"
+#include "Misc.h"
+
+#include "Instruction.h"
+#include "CPUContext.h"
+#include "Bus.h"
+
+#include <algorithm>
+#include <bitset>
+#include <iostream>
+#include <utility>
+
+constexpr std::array<InstructionEntry, 256> GenerateInstructionTable(){
+ std::array<InstructionEntry, 256> table{};
+
+ for(uint8_t i = 0; i < 255; i++) {
+ table[i] = (InstructionEntry){nullptr, (OperandEncoding)0};
+ }
+
+ table[Opcode::NOP] = (InstructionEntry){executor_cases::Nop, ZO};
+ table[Opcode::HLT] = (InstructionEntry){executor_cases::Hlt, ZO};
+ 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::ADD_RM32_R32] = (InstructionEntry){executor_cases::Add_rm32_r32, MR};
+ table[Opcode::ADD_R32_RM32] = (InstructionEntry){executor_cases::Add_r32_rm32, RM};
+
+ for(uint8_t i = 0; i < 255;)
+ {
+ if((table[i].m_Encoding & OI) != 0)
+ {
+ uint8_t end_offset = std::min(i + 8, 255);
+ std::fill(table.begin() + i, table.begin() + end_offset, table[i]);
+ i += 8;
+ } else {
+ i++;
+ }
+ }
+ return table;
+}
+
+static constexpr std::array<InstructionEntry, 256> s_InstructionTable = GenerateInstructionTable();
+
+const std::array<InstructionEntry, 256>& GetInstructionTable() {
+ return s_InstructionTable;
+}
+
+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) {
+ x86::ModRM modrm = cc.m_Instruction.m_ModRM;
+ x86::SIB sib = cc.m_Instruction.m_SIB;
+
+ 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_Displacement[0];
+ break;
+ case x86::ModRMState::DISP32:
+ std::memcpy(&value, &cc.m_Instruction.m_Displacement, 4);
+ 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
new file mode 100644
index 0000000..bbcd8ae
--- /dev/null
+++ b/src/ExecutorCases.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <cstdint>
+#include <memory>
+#include <array>
+
+struct CPUContext;
+
+typedef void (*ExecutorCase)(CPUContext&);
+
+enum OperandEncoding : uint8_t {
+ ZO = 0b00000000,
+ RM = 0b00000001,
+ MR = 0b00000010,
+ MI = 0b00000100,
+ OI = 0b00001000,
+ I32 = 0b00100000,
+ I16 = 0b01000000,
+ I8 = 0b10000000,
+};
+
+struct InstructionEntry {
+ ExecutorCase m_Executor;
+ OperandEncoding m_Encoding;
+};
+
+const std::array<InstructionEntry, 256>& GetInstructionTable();
+
+const std::array<ExecutorCase, 256>& GetExecutorTable();
+
+namespace executor_cases::helpers {
+ uint32_t ResolveModRMAddress(CPUContext& cc);
+}
diff --git a/src/GUI.cpp b/src/GUI.cpp
new file mode 100644
index 0000000..4601e99
--- /dev/null
+++ b/src/GUI.cpp
@@ -0,0 +1,194 @@
+#include "GUI.h"
+
+#include <imgui.h>
+#include <imgui_internal.h>
+#include <backends/imgui_impl_glfw.h>
+#include <backends/imgui_impl_opengl3.h>
+#include <imgui_memory_editor.h>
+#include <glad/glad.h>
+#include <GLFW/glfw3.h>
+#include <cstdlib>
+#include <iostream>
+#include "Userspace.h"
+
+void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
+ {
+ glfwSetWindowShouldClose(window, true);
+ return;
+ }
+}
+
+GUI::GUI(Userspace& user) :m_Userspace(user) {
+ if(!glfwInit())
+ exit(1);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ CreateWindow();
+}
+
+void GUI::CreateWindow() {
+ m_Window = glfwCreateWindow(1200, 800, "CPUSecX86", NULL, NULL);
+ if(!m_Window)
+ {
+ glfwTerminate();
+ return;
+ }
+
+ glfwMakeContextCurrent(m_Window);
+ glfwSetKeyCallback(m_Window, key_callback);
+ ImGui::CreateContext();
+
+ int version = gladLoadGL();
+ std::cout << "OpenGL Version: " << std::dec << version << std::endl;
+
+ ImGui_ImplGlfw_InitForOpenGL(m_Window, true);
+ ImGui_ImplOpenGL3_Init("#version 460");
+
+ ImGuiIO& io = ImGui::GetIO();
+ io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
+ io.FontGlobalScale = 1.5f;
+}
+
+void GUI::Run() {
+ while (!glfwWindowShouldClose(m_Window)) {
+ glfwPollEvents();
+
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+ DockingSetup();
+
+ ControlGUI();
+ DebugInfoGUI();
+
+ 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);
+
+ ImGui::Render();
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ glfwSwapBuffers(m_Window);
+ }
+
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+}
+
+void GUI::Terminate() {
+ glfwDestroyWindow(m_Window);
+ glfwTerminate();
+}
+
+void GUI::DockingSetup() {
+ ImGuiID dockspace_id = ImGui::GetID("Dockspace");
+ ImGuiViewport *viewport = ImGui::GetMainViewport();
+
+ if (ImGui::DockBuilderGetNode(dockspace_id) == nullptr) {
+ ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
+ ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
+ ImGuiID dock_id_left = 0;
+ ImGuiID dock_id_main = dockspace_id;
+ ImGui::DockBuilderSplitNode(dock_id_main, ImGuiDir_Left, 0.40f, &dock_id_left, &dock_id_main);
+ ImGuiID dock_id_right = 0;
+ ImGui::DockBuilderSplitNode(dock_id_main, ImGuiDir_Right, 0.40f, &dock_id_right, &dock_id_main);
+ // ImGuiID dock_id_left_top = 0;
+ // ImGuiID dock_id_left_bottom = 0;
+ // ImGui::DockBuilderSplitNode(dock_id_left, ImGuiDir_Up, 0.50f, &dock_id_left_top, &dock_id_left_bottom);
+ ImGui::DockBuilderDockWindow("Process Memory", dock_id_main);
+ ImGui::DockBuilderDockWindow("Debug Information", dock_id_left);
+ ImGui::DockBuilderDockWindow("Emulator Control", dock_id_right);
+ ImGui::DockBuilderFinish(dockspace_id);
+ }
+
+ ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_NoTabBar;
+ dockspace_flags |= ImGuiDockNodeFlags_PassthruCentralNode;
+
+ ImGui::DockSpaceOverViewport(dockspace_id, viewport, dockspace_flags);
+}
+
+void GUI::ControlGUI() {
+ ImGui::Begin("Emulator Control");
+ 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();
+ if(ImGui::Button("Stop"))
+ m_Userspace.Stop();
+ ImGui::SameLine();
+ if (ImGui::Button("Reset"))
+ m_Userspace.Reset();
+ if(ImGui::Button("Step"))
+ m_Userspace.Step();
+ 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.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);
+ ImGui::InputScalar("ECX", ImGuiDataType_U32, &status.m_Registers[1], NULL, NULL, "%08X", flags);
+ ImGui::InputScalar("EDX", ImGuiDataType_U32, &status.m_Registers[2], NULL, NULL, "%08X", flags);
+ ImGui::InputScalar("EBX", ImGuiDataType_U32, &status.m_Registers[3], NULL, NULL, "%08X", flags);
+ ImGui::InputScalar("ESP", ImGuiDataType_U32, &status.m_Registers[4], NULL, NULL, "%08X", flags);
+ ImGui::InputScalar("EBP", ImGuiDataType_U32, &status.m_Registers[5], NULL, NULL, "%08X", flags);
+ ImGui::InputScalar("ESI", ImGuiDataType_U32, &status.m_Registers[6], NULL, NULL, "%08X", flags);
+ ImGui::InputScalar("EDI", ImGuiDataType_U32, &status.m_Registers[7], NULL, NULL, "%08X", flags);
+ ImGui::Separator();
+ ImGui::Text("Other Registers");
+ ImGui::InputScalar("IP", ImGuiDataType_U32, &status.m_IP, NULL, NULL, "%08X", flags);
+ ImGui::Separator();
+ ImGui::Text("Instruction Information");
+ ImGui::Text("Instruction Length: %d", status.m_Instruction.m_Length);
+ 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.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();
+}
+
+GUI::~GUI() {
+ Terminate();
+}
diff --git a/src/GUI.h b/src/GUI.h
new file mode 100644
index 0000000..15b6046
--- /dev/null
+++ b/src/GUI.h
@@ -0,0 +1,27 @@
+#pragma once
+
+struct GLFWwindow;
+struct Userspace;
+
+class GUI {
+public:
+ GUI(Userspace& user);
+ ~GUI();
+
+ void Run();
+
+private:
+ void CreateWindow();
+ void Terminate();
+
+ void DockingSetup();
+
+ void ControlGUI();
+ void DebugInfoGUI();
+
+ void ErrorGUI();
+
+ private:
+ GLFWwindow* m_Window;
+ Userspace& m_Userspace;
+};
diff --git a/src/Instruction.cpp b/src/Instruction.cpp
new file mode 100644
index 0000000..c74139b
--- /dev/null
+++ b/src/Instruction.cpp
@@ -0,0 +1,49 @@
+#include "Instruction.h"
+
+#include "Exceptions.h"
+
+namespace x86 {
+ std::string Register2Str(x86::Register reg) {
+ switch(reg){
+ case x86::Register::EAX: return "EAX";
+ case x86::Register::ECX: return "ECX";
+ case x86::Register::EDX: return "EDX";
+ case x86::Register::EBX: return "EBX";
+ case x86::Register::ESP: return "ESP";
+ case x86::Register::EBP: return "EBP";
+ case x86::Register::ESI: return "ESI";
+ case x86::Register::EDI: return "EDI";
+ }
+ throw std::runtime_error("Register not found!");
+ }
+
+ 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};
+ }
+}
+
+std::string Opcode2Str(Opcode op) {
+ switch(op) {
+ case Opcode::INVALID: return "INVALID";
+ case Opcode::ADD_RM32_R32: return "ADD_RM32_R32";
+ case Opcode::ADD_R32_RM32: return "ADD_R32_RM32";
+ case Opcode::MOV_R32_IMM32 ... 0xBF: return "MOV_R32_IMM32";
+ case Opcode::MOV_RM32_R32: return "MOV_RM32_R32";
+ case Opcode::NOP: return "NOP";
+ case Opcode::HLT: return "HLT";
+ }
+
+ std::string what = "Opcode2Str could not find '" + std::to_string(op) + "'!";
+ throw CPUException(what);
+}
diff --git a/src/Instruction.h b/src/Instruction.h
new file mode 100644
index 0000000..c80d2ca
--- /dev/null
+++ b/src/Instruction.h
@@ -0,0 +1,110 @@
+#pragma once
+
+#include <string>
+#include <cstdint>
+
+#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,
+
+ SIB_DISP = 254,
+ SIB_NONE = 255,
+ };
+
+ enum class ModRMState : uint8_t
+ {
+ INVALID = 0,
+ LR = 1,
+ LR_DISP8 = 2,
+ LR_DISP32 = 3,
+ DISP32 = 4,
+ R = 5
+ };
+
+ struct ModRM {
+ ModRMState m_State;
+ x86::Register m_Reg;
+ uint8_t m_Rm;
+ bool m_SIB;
+ };
+
+ 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);
+}
+
+enum Opcode : uint8_t {
+ INVALID = 0,
+ NOP = 0x90,
+ HLT = 0xF4,
+ MOV_R32_IMM32 = 0xB8,
+ MOV_RM32_R32 = 0x89,
+ ADD_RM32_R32 = 0x01,
+ ADD_R32_RM32 = 0x03,
+};
+
+std::string Opcode2Str(Opcode op);
+
+struct Instruction{
+ ExecutorCase m_Func;
+ uint8_t m_Prefix;
+ Opcode m_Opcode;
+ size_t m_Length;
+ uint32_t m_Operand1;
+ uint32_t m_Operand2;
+ 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 <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; }
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 <array>
+
+struct CPUContext;
+
+typedef void (*ModifierEntry)(CPUContext&);
+
+std::array<ModifierEntry, 256> GetMod32Table();
+
+std::array<ModifierEntry, 256> GetSIBTable();
diff --git a/src/Misc.cpp b/src/Misc.cpp
new file mode 100644
index 0000000..767aed6
--- /dev/null
+++ b/src/Misc.cpp
@@ -0,0 +1,12 @@
+#include "Misc.h"
+
+#include "ExecutorCases.h"
+#include "CPUContext.h"
+
+#include <iostream>
+
+namespace executor_cases {
+ void Nop(CPUContext& cc){
+ std::cout << "[Instruction] nop" << std::endl;
+ }
+}
diff --git a/src/Misc.h b/src/Misc.h
new file mode 100644
index 0000000..8f52706
--- /dev/null
+++ b/src/Misc.h
@@ -0,0 +1,7 @@
+#pragma once
+
+struct CPUContext;
+
+namespace executor_cases {
+ void Nop(CPUContext& cc);
+}
diff --git a/src/RAM.cpp b/src/RAM.cpp
new file mode 100644
index 0000000..fc16777
--- /dev/null
+++ b/src/RAM.cpp
@@ -0,0 +1,12 @@
+#include "RAM.h"
+
+RAM::RAM() {
+ for(int i = 0; i < 992 * 1024; i++)
+ {
+ m_Memory[i] = 0x90;
+ }
+}
+
+uint8_t* RAM::Data() {
+ return m_Memory;
+}
diff --git a/src/RAM.h b/src/RAM.h
new file mode 100644
index 0000000..c432ea1
--- /dev/null
+++ b/src/RAM.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <cstdint>
+
+class RAM {
+ public:
+ RAM();
+ ~RAM() = default;
+
+ uint8_t* Data();
+
+ private:
+ uint8_t m_Memory[992 * 1024];
+};
diff --git a/src/Userspace.cpp b/src/Userspace.cpp
new file mode 100644
index 0000000..0dc2425
--- /dev/null
+++ b/src/Userspace.cpp
@@ -0,0 +1,99 @@
+#include "Userspace.h"
+
+#include "Bus.h"
+#include "RAM.h"
+
+#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) {
+ std::cout << "[Userspace] Emulation started." << std::endl;
+ m_ApplicationRunning = true;
+ m_Status = SystemStatus::STOPPED;
+}
+
+void Userspace::Upload2Memory(uint8_t bytes[], size_t len) {
+ m_ProgramMemoryLen = len;
+ std::memcpy(m_RAM->Data(), bytes, len);
+ std::memcpy(m_ProgramMemory->Data(), bytes, len);
+}
+
+uint8_t* Userspace::RetrieveMemory() {
+ return m_RAM->Data();
+}
+
+void Userspace::Run() {
+ while(m_ApplicationRunning) {
+ EmulatorLoop();
+ }
+}
+
+void Userspace::StepCPU() {
+ try {
+ m_CPU.Step();
+ } catch (CPUException& c) {
+ m_Status = SystemStatus::EXCEPTION;
+ m_ExceptionMessages.push_back(c.GetMessage());
+ }
+}
+
+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_Status = SystemStatus::STOPPED;
+ m_CPU.Reset();
+ std::memcpy(m_RAM->Data(), m_ProgramMemory->Data(), m_ProgramMemoryLen);
+ m_ExceptionMessages.clear();
+}
+
+void Userspace::Start() {
+ m_Status = SystemStatus::RUNNING;
+}
+
+void Userspace::Step() {
+ if(!m_CPU.IsHalted())
+ {
+ StepCPU();
+ std::cout << "[Userspace] CPU stepped!" << std::endl;
+ }
+ else
+ {
+ std::cout << "[Userspace] CPU is halted! Cannot step." << std::endl;
+ }
+}
+
+void Userspace::Stop() {
+ m_Status = SystemStatus::STOPPED;
+ std::cout << "[Userspace] Emulation stopped." << std::endl;
+}
+
+void Userspace::Exit() {
+ m_Status = SystemStatus::STOPPED;
+ m_ApplicationRunning = false;
+ std::cout << "[Userspace] Emulation exited." << std::endl;
+}
+
+SystemStatus Userspace::GetSystemStatus() {
+ return m_Status;
+}
+
+CPUStatus Userspace::GetCPUStatus() {
+ return m_CPU.GetStatus();
+}
+
+std::vector<std::string>& Userspace::GetException() {
+ return m_ExceptionMessages;
+}
diff --git a/src/Userspace.h b/src/Userspace.h
new file mode 100644
index 0000000..3f9db8c
--- /dev/null
+++ b/src/Userspace.h
@@ -0,0 +1,52 @@
+#pragma once
+
+class Bus;
+
+#include <cstdint>
+#include <memory>
+#include <atomic>
+#include <vector>
+
+#include "CPU.h"
+
+enum class SystemStatus {
+ STOPPED,
+ RUNNING,
+ EXCEPTION
+};
+
+class Userspace {
+ public:
+ Userspace();
+ ~Userspace() = default;
+
+ void Upload2Memory(uint8_t bytes[], size_t len);
+ uint8_t* RetrieveMemory();
+
+ void Run();
+ void Start();
+ void Stop();
+ void Step();
+ void Reset();
+ void Exit();
+
+ SystemStatus GetSystemStatus();
+ CPUStatus GetCPUStatus();
+ std::vector<std::string>& GetException();
+
+ private:
+ std::shared_ptr<RAM> m_RAM;
+ std::shared_ptr<Bus> m_Bus;
+ CPU m_CPU;
+ std::atomic<SystemStatus> m_Status = SystemStatus::STOPPED;
+ std::atomic<bool> m_ApplicationRunning = false;
+
+ std::vector<std::string> m_ExceptionMessages;
+
+ std::shared_ptr<RAM> m_ProgramMemory;
+ size_t m_ProgramMemoryLen;
+
+ private:
+ void StepCPU();
+ void EmulatorLoop();
+};
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..5dd1edb
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,35 @@
+#include <iostream>
+
+#include "Userspace.h"
+#include "GUI.h"
+#include <thread>
+
+// nop
+// mov eax, 0xF4
+// mov ecx, 0x8000
+// mov edx, 0x8000
+// add DWORD PTR [0x8011], eax
+uint8_t test[] = {
+ 0x90,
+ 0xB8, 0xF4, 0x00, 0x00, 0x00,
+ 0xB9, 0x00, 0x80, 0x00, 0x00,
+ 0xBA, 0x00, 0x80, 0x00, 0x00,
+ 0x01, 0x05, 0x16, 0x80, 0x00, 0x00,
+ 0x00,
+};
+
+int main(int argc, char** argv) {
+ Userspace user;
+ GUI gui(user);
+
+ user.Upload2Memory(test, 23);
+ std::thread tuserspace(&Userspace::Run, &user);
+
+ gui.Run();
+
+ user.Exit();
+
+ tuserspace.join();
+
+ return 0;
+}