diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..35a081b --- /dev/null +++ b/.clang-format @@ -0,0 +1,120 @@ +# Common settings +BasedOnStyle: WebKit +TabWidth: 4 +IndentWidth: 4 +UseTab: Always +ColumnLimit: 100 + +# Other languages JavaScript, Proto + +--- +Language: Cpp + +# http://releases.llvm.org/6.0.1/tools/clang/docs/ClangFormatStyleOptions.html#disabling-formatting-on-a-piece-of-code +# int formatted_code; +# // clang-format off +# void unformatted_code ; +# // clang-format on +# void formatted_code_again; + +DisableFormat: false +Standard: Cpp11 + +AccessModifierOffset: -4 +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false + +# Configure each individual brace in BraceWrapping +BreakBeforeBraces: Custom +# Control of individual brace wrapping cases +BraceWrapping: { + AfterClass: 'true' + AfterControlStatement: 'true' + AfterEnum : 'true' + AfterFunction : 'true' + AfterNamespace : 'true' + AfterStruct : 'true' + AfterUnion : 'true' + BeforeCatch : 'true' + BeforeElse : 'true' + IndentBraces : 'false' + AfterExternBlock : 'true' + SplitEmptyFunction : 'false' + SplitEmptyRecord : 'false' + SplitEmptyNamespace : 'true' +} + +BreakAfterJavaFieldAnnotations: true +BreakBeforeInheritanceComma: false +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: true +BreakStringLiterals: true + +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +SpaceBeforeCpp11BracedList: false +DerivePointerAlignment: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IndentCaseLabels: false +FixNamespaceComments: true +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +JavaScriptQuotes: Double +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 + +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceAfterTemplateKeyword: true +SpaceBeforeInheritanceColon: true + +SortUsingDeclarations: true +SortIncludes: true + +# Comments are for developers, they should arrange them +ReflowComments: false + +IncludeBlocks: Preserve +IndentPPDirectives: AfterHash \ No newline at end of file diff --git a/.gitignore b/.gitignore index a33f80f..448187d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ *.o *.exe -ztest.cpp \ No newline at end of file +build/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..43cdb72 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.15) +project(intel-x86-emulator LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +file(GLOB_RECURSE CPP_SOURCES "src/*.cpp") +file(GLOB_RECURSE HPP_SOURCES "src/*.hpp") + +add_executable(intel-x86-emulator main.cpp ${CPP_SOURCES} ${HPP_SOURCES}) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +enable_testing() +include(GoogleTest) + +add_executable(ie-test test/Test.cpp ${CPP_SOURCES} ${HPP_SOURCES}) + +target_link_libraries(ie-test GTest::gtest_main) +gtest_discover_tests(ie-test) diff --git a/input_reader.cpp b/input_reader.cpp deleted file mode 100644 index 9b704be..0000000 --- a/input_reader.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "input_reader.hpp" - -#include -#include -#include - -InputReader::InputReader(std::string path){ - this->input_file.open(path); - if(!this->input_file){ - std::cout<<"Error: Failed to open file"; - exit(0); - } -} - -InputReader::~InputReader(){ - this->input_file.close(); -} - -std::uint8_t InputReader::nextByte(){ - - if (this->fileRemaining()){ - std::string next_byte; - this->input_file >> next_byte; // Stops at the whitespace, reading only 1 byte - return (std::uint8_t) std::stoi(next_byte, nullptr, 16); // Read the string's hex value into an integer - } - else { - std::cout << "Error: Reached end of input file while attempting to read further bytes \n"; - exit(0); - } -} - -bool InputReader::fileRemaining(){ - return this->input_file.good(); -} diff --git a/input_reader.hpp b/input_reader.hpp deleted file mode 100644 index f55d74d..0000000 --- a/input_reader.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef INPUTREADER_H -#define INPUTREADER_H - -#include -#include -#include - -class InputReader{ - private: - std::ifstream input_file; - - public: - InputReader(std::string path); - ~InputReader(); - std::uint8_t nextByte(); - bool fileRemaining(); -}; - -#endif \ No newline at end of file diff --git a/instr_utils.hpp b/instr_utils.hpp deleted file mode 100644 index 392a5d6..0000000 --- a/instr_utils.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef INSTR_UTILS_H -#define INSTR_UTILS_H - -#include -#include "register.hpp" -#include "input_reader.hpp" - -uint32_t getSIBMemLocation(uint8_t sib, RegisterBank* rb, uint8_t mod, InputReader* ir); -void setFlagGroup(long long int value, int size, RegisterBank* rb); - -uint8_t getDisp8(InputReader* ir); -uint16_t getDisp16(InputReader* ir); -uint32_t getDisp32(InputReader* ir); - -int8_t getImm8(InputReader* ir); -int16_t getImm16(InputReader* ir); -int32_t getImm32(InputReader* ir); - -#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index c58b28f..45aa9b5 100644 --- a/main.cpp +++ b/main.cpp @@ -1,120 +1,11 @@ -#include "input_reader.hpp" -#include "opcodes.hpp" -#include "operations/all_operations.hpp" -#include "register.hpp" -#include "memory.hpp" +#include "src/emulator.hpp" -#include #include +#include #include -int main(int argc, char *argv[]) { - std::cout<<"----Emulation started----"<<"\n\n"; - std::cout << std::hex; - - InputReader ir = InputReader(argv[1]); - RegisterBank rb = RegisterBank(); - Memory mem = Memory(); - - while (ir.fileRemaining()){ - - uint8_t opcode1 = ir.nextByte(); - - // Test for prefixes / escape opcodes - - switch (opcode1){ - - case 0x0f: { - - uint8_t opcode2 = ir.nextByte(); - - if (opcode2==0x3A || opcode2==0x38){ - // No opcodes with three-byte encodings are implemented - std::cout << "Error: three-byte opcodes not implemented \n"; - exit(0); - } - else { - // Check the opcode maps for two-byte opcodes with 0F byte - - if (opcodes::bsf_ops.count(opcode2)) { - bsf(&ir, &rb, &mem, opcode2); - } - - else if (opcodes::bsr_ops.count(opcode2)) { - bsr(&ir, &rb, &mem, opcode2); - } - - else{ - std::cout << "Error: Unsupported opcode! \n"; - exit(0); - } - } - break; - } - case 0x66: - // No opcodes with 66 mandatory prefix have been implemented - std::cout << "Error: unsupported opcode prefix 66 \n"; - exit(0); - break; - - case 0xF2: - // No opcodes with F2 mandatory prefix have been implemented - std::cout << "Error: unsupported opcode prefix F2 \n"; - exit(0); - break; - - case 0xF3: - // No opcodes with F3 mandatory prefix have been implemented - std::cout << "Error: unsupported opcode prefix F3 \n"; - exit(0); - break; - - default: - - if (opcodes::add_ops.count(opcode1)){ - add(&ir, &rb, &mem, opcode1); - } - - else if (opcodes::cmp_ops.count(opcode1)){ - cmp(&ir, &rb, &mem, opcode1); - } - - else if (opcodes::and_ops.count(opcode1)){ - andOp(&ir, &rb, &mem, opcode1); - } - - else if (opcodes::mov_ops.count(opcode1)){ - mov(&ir, &rb, &mem, opcode1); - } - - else if (opcodes::or_ops.count(opcode1)){ - orOp(&ir, &rb, &mem, opcode1); - } - - else if (opcodes::push_ops.count(opcode1)){ - push(&ir, &rb, &mem, opcode1); - } - - else if (opcodes::pop_ops.count(opcode1)){ - pop(&ir, &rb, &mem, opcode1); - } - - else if (opcodes::xor_ops.count(opcode1)){ - xorOp(&ir, &rb, &mem, opcode1); - } - - // Add other operations below after implementing - - else { - std::cout << "Error: Unsupported opcode! \n"; - exit(0); - } - - } - std::cout << "--------------------------\n"; - } - - std::cout << "\n----Emulation complete----\n"; - rb.dumpValues(); - mem.dumpValues(); +int main(int argc, char* argv[]) +{ + Emulator& emulator = Emulator::getInstance(); + emulator.run(argv[1]); } \ No newline at end of file diff --git a/makefile b/makefile deleted file mode 100644 index c654b0c..0000000 --- a/makefile +++ /dev/null @@ -1,53 +0,0 @@ -CC = g++ -CFLAGS = -std=c++17 -Wall -g - -main: main.o input_reader.o register.o memory.o modrm.o instr_utils.o opcodes.o add.o mov.o and.o or.o cmp.o stack.o xor.o bsf.o bsr.o - $(CC) $(CFLAGS) -o main main.o input_reader.o register.o memory.o modrm.o instr_utils.o opcodes.o add.o mov.o and.o or.o cmp.o stack.o xor.o bsf.o bsr.o - -main.o: main.cpp - $(CC) $(CFLAGS) -c main.cpp - -input_reader.o: input_reader.hpp input_reader.cpp - $(CC) $(CFLAGS) -c input_reader.cpp - -register.o: register.hpp register.cpp - $(CC) $(CFLAGS) -c register.cpp - -memory.o: memory.hpp memory.cpp - $(CC) $(CFLAGS) -c memory.cpp - -modrm.o: modrm.hpp modrm.cpp - $(CC) $(CFLAGS) -c modrm.cpp - -instr_utils.o: instr_utils.hpp instr_utils.cpp - $(CC) $(CFLAGS) -c instr_utils.cpp - -opcodes.o: opcodes.hpp opcodes.cpp - $(CC) $(CFLAGS) -c opcodes.cpp - -add.o: operations/add.hpp operations/add.cpp - $(CC) $(CFLAGS) -c operations/add.cpp - -mov.o: operations/mov.cpp operations/mov.hpp - $(CC) $(CFLAGS) -c operations/mov.cpp - -and.o: operations/and.cpp operations/and.hpp - $(CC) $(CFLAGS) -c operations/and.cpp - -or.o: operations/or.cpp operations/or.hpp - $(CC) $(CFLAGS) -c operations/or.cpp - -cmp.o: operations/cmp.cpp operations/cmp.hpp - $(CC) $(CFLAGS) -c operations/cmp.cpp - -stack.o: operations/stack.cpp operations/stack.hpp - $(CC) $(CFLAGS) -c operations/stack.cpp - -xor.o: operations/xor.cpp operations/xor.hpp - $(CC) $(CFLAGS) -c operations/xor.cpp - -bsf.o: operations/bsf.hpp operations/bsf.cpp - $(CC) $(CFLAGS) -c operations/bsf.cpp - -bsr.o: operations/bsr.cpp operations/bsr.hpp - $(CC) $(CFLAGS) -c operations/bsr.cpp \ No newline at end of file diff --git a/src/emulator.hpp b/src/emulator.hpp new file mode 100644 index 0000000..3c47f77 --- /dev/null +++ b/src/emulator.hpp @@ -0,0 +1,177 @@ +#pragma once + +#include "input_reader.hpp" +#include "memory.hpp" +#include "opcodes.hpp" +#include "operations/all_operations.hpp" +#include "register.hpp" + +#include +#include +#include + +class Emulator +{ + +public: + static Emulator& getInstance() + { + static Emulator instance; + return instance; + } + + ~Emulator() + { + delete m_registerBank; + delete m_memory; + } + + Emulator(Emulator const&) = delete; + void operator=(Emulator const&) = delete; + + RegisterBank& getRegisterBank() + { + return *m_registerBank; + } + + Memory& getMemory() + { + return *m_memory; + } + + void run(std::string opcodeFilePath) + { + + std::cout << "----Emulation started----" << "\n\n"; + std::cout << std::hex; + + InputReader ir = InputReader(opcodeFilePath); + + while(ir.fileRemaining()) + { + + uint8_t opcode1 = ir.nextByte(); + + // Test for prefixes / escape opcodes + + switch(opcode1) + { + + case 0x0f: { + + uint8_t opcode2 = ir.nextByte(); + + if(opcode2 == 0x3A || opcode2 == 0x38) + { + // No opcodes with three-byte encodings are implemented + std::cout << "Error: three-byte opcodes not implemented \n"; + exit(0); + } + else + { + // Check the opcode maps for two-byte opcodes with 0F byte + + if(opcodes::bsf_ops.count(opcode2)) + { + bsf(&ir, m_registerBank, m_memory, opcode2); + } + + else if(opcodes::bsr_ops.count(opcode2)) + { + bsr(&ir, m_registerBank, m_memory, opcode2); + } + + else + { + std::cout << "Error: Unsupported opcode! \n"; + exit(0); + } + } + break; + } + case 0x66: + // No opcodes with 66 mandatory prefix have been implemented + std::cout << "Error: unsupported opcode prefix 66 \n"; + exit(0); + break; + + case 0xF2: + // No opcodes with F2 mandatory prefix have been implemented + std::cout << "Error: unsupported opcode prefix F2 \n"; + exit(0); + break; + + case 0xF3: + // No opcodes with F3 mandatory prefix have been implemented + std::cout << "Error: unsupported opcode prefix F3 \n"; + exit(0); + break; + + default: + + if(opcodes::add_ops.count(opcode1)) + { + add(&ir, m_registerBank, m_memory, opcode1); + } + + else if(opcodes::cmp_ops.count(opcode1)) + { + cmp(&ir, m_registerBank, m_memory, opcode1); + } + + else if(opcodes::and_ops.count(opcode1)) + { + andOp(&ir, m_registerBank, m_memory, opcode1); + } + + else if(opcodes::mov_ops.count(opcode1)) + { + mov(&ir, m_registerBank, m_memory, opcode1); + } + + else if(opcodes::or_ops.count(opcode1)) + { + orOp(&ir, m_registerBank, m_memory, opcode1); + } + + else if(opcodes::push_ops.count(opcode1)) + { + push(&ir, m_registerBank, m_memory, opcode1); + } + + else if(opcodes::pop_ops.count(opcode1)) + { + pop(&ir, m_registerBank, m_memory, opcode1); + } + + else if(opcodes::xor_ops.count(opcode1)) + { + xorOp(&ir, m_registerBank, m_memory, opcode1); + } + + // Add other operations below after implementing + + else + { + std::cout << "Error: Unsupported opcode! \n"; + exit(0); + } + } + std::cout << "--------------------------\n"; + } + + std::cout << "\n----Emulation complete----\n"; + m_registerBank->dumpValues(); + m_memory->dumpValues(); + } + +private: + RegisterBank* m_registerBank; + Memory* m_memory; + + Emulator() + { + m_registerBank = new RegisterBank(); + m_memory = new Memory(); + } +}; \ No newline at end of file diff --git a/src/input_reader.cpp b/src/input_reader.cpp new file mode 100644 index 0000000..7b46b46 --- /dev/null +++ b/src/input_reader.cpp @@ -0,0 +1,40 @@ +#include "input_reader.hpp" + +#include +#include +#include + +InputReader::InputReader(std::string path) +{ + this->input_file.open(path); + if (!this->input_file) + { + throw std::runtime_error("Error: Failed to open file"); + } +} + +InputReader::~InputReader() +{ + this->input_file.close(); +} + +std::uint8_t InputReader::nextByte() +{ + if (this->fileRemaining()) + { + std::string next_byte; + this->input_file >> next_byte; // Stops at the whitespace, reading only 1 byte + return (std::uint8_t)std::stoi( + next_byte, nullptr, 16); // Read the string's hex value into an integer + } + else + { + throw std::runtime_error( + "Error: Reached end of input file while attempting to read further bytes \n"); + } +} + +bool InputReader::fileRemaining() const +{ + return this->input_file.good(); +} diff --git a/src/input_reader.hpp b/src/input_reader.hpp new file mode 100644 index 0000000..1c912b6 --- /dev/null +++ b/src/input_reader.hpp @@ -0,0 +1,20 @@ +#ifndef INPUTREADER_H +#define INPUTREADER_H + +#include +#include +#include + +class InputReader +{ +private: + std::ifstream input_file; + +public: + InputReader(std::string path); + ~InputReader(); + std::uint8_t nextByte(); + bool fileRemaining() const; +}; + +#endif \ No newline at end of file diff --git a/instr_utils.cpp b/src/instruction_utils.cpp similarity index 79% rename from instr_utils.cpp rename to src/instruction_utils.cpp index d914b22..9471b03 100644 --- a/instr_utils.cpp +++ b/src/instruction_utils.cpp @@ -1,13 +1,14 @@ -#include "instr_utils.hpp" +#include "instruction_utils.hpp" #include -std::string reg_names_32bit[] = {"EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI" }; +const std::vector InstructionUtils::reg_names_32bit = { + "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI"}; -uint8_t getDisp8(InputReader* ir){ +uint8_t InstructionUtils::getDisp8(InputReader* ir){ return ir->nextByte(); } -uint16_t getDisp16(InputReader* ir){ +uint16_t InstructionUtils::getDisp16(InputReader* ir){ uint16_t disp16 = 0; // Concatenate the next 2 bytes @@ -17,7 +18,7 @@ uint16_t getDisp16(InputReader* ir){ return disp16; } -uint32_t getDisp32(InputReader* ir){ +uint32_t InstructionUtils::getDisp32(InputReader* ir){ uint32_t disp32 = 0; // Concatenate the 4 bytes following @@ -27,28 +28,28 @@ uint32_t getDisp32(InputReader* ir){ return disp32; } -int8_t getImm8(InputReader* ir){ +int8_t InstructionUtils::getImm8(InputReader* ir){ int8_t ans = (int8_t) getDisp8(ir); // Imm data is signed - show it as a decimal with the sign std::cout << std::dec << "Read Imm8 data decimal value " << +ans <<"\n" << std::hex; return ans; } -int16_t getImm16(InputReader* ir){ +int16_t InstructionUtils::getImm16(InputReader* ir){ int16_t ans = (int16_t) getDisp16(ir); // Imm data is signed - show it as a decimal with the sign std::cout << std::dec << "Read Imm16 data decimal value " << +ans <<"\n" << std::hex; return ans; } -int32_t getImm32(InputReader* ir){ +int32_t InstructionUtils::getImm32(InputReader* ir){ int32_t ans = (int32_t) getDisp32(ir); // Imm data is signed - show it as a decimal with the sign std::cout << std::dec << "Read Imm32 data decimal value " << +ans <<"\n" << std::hex; return ans; } -uint32_t getSIBMemLocation(uint8_t sib, RegisterBank* rb, uint8_t mod, InputReader* ir){ +uint32_t InstructionUtils::getSIBMemLocation(uint8_t sib, RegisterBank* rb, uint8_t mod, InputReader* ir){ int scale = sib >> 6; int index = (sib>>3)&7; @@ -71,7 +72,7 @@ uint32_t getSIBMemLocation(uint8_t sib, RegisterBank* rb, uint8_t mod, InputRead return base_value + scaled_value; } -void setFlagGroup(long long int value, int size, RegisterBank* rb){ +void InstructionUtils::setFlagGroup(long long int value, int size, RegisterBank* rb){ // Check the value and size of its allocated space, and set flags accordingly // Flags affected - CF, OF, SF, ZF, PF diff --git a/src/instruction_utils.hpp b/src/instruction_utils.hpp new file mode 100644 index 0000000..a017eb8 --- /dev/null +++ b/src/instruction_utils.hpp @@ -0,0 +1,26 @@ +#ifndef instruction_utils_H +#define instruction_utils_H + +#include "input_reader.hpp" +#include "register.hpp" +#include +#include + +class InstructionUtils +{ +public: + static const std::vector reg_names_32bit; // TODO keep this info in RB class + + static uint32_t getSIBMemLocation(uint8_t sib, RegisterBank* rb, uint8_t mod, InputReader* ir); + static void setFlagGroup(long long int value, int size, RegisterBank* rb); + + static uint8_t getDisp8(InputReader* ir); + static uint16_t getDisp16(InputReader* ir); + static uint32_t getDisp32(InputReader* ir); + + static int8_t getImm8(InputReader* ir); + static int16_t getImm16(InputReader* ir); + static int32_t getImm32(InputReader* ir); +}; + +#endif \ No newline at end of file diff --git a/memory.cpp b/src/memory.cpp similarity index 100% rename from memory.cpp rename to src/memory.cpp diff --git a/memory.hpp b/src/memory.hpp similarity index 72% rename from memory.hpp rename to src/memory.hpp index 64db83d..10d8eff 100644 --- a/memory.hpp +++ b/src/memory.hpp @@ -5,10 +5,12 @@ #include #include +using MEMORY_MAP = std::unordered_map ; + class Memory { private: - std::unordered_map memory_map; + MEMORY_MAP memory_map; public: Memory(); @@ -18,6 +20,7 @@ class Memory { uint32_t pop_stack(RegisterBank* rb, int size); void dumpValues(); + const MEMORY_MAP& getMemoryLayout() { return memory_map; } }; #endif \ No newline at end of file diff --git a/modrm.cpp b/src/modrm.cpp similarity index 83% rename from modrm.cpp rename to src/modrm.cpp index 5370657..e305f28 100644 --- a/modrm.cpp +++ b/src/modrm.cpp @@ -1,7 +1,7 @@ #include "modrm.hpp" #include "register.hpp" #include "input_reader.hpp" -#include "instr_utils.hpp" +#include "instruction_utils.hpp" std::string regs_32bit[] = {"EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI" }; std::string regs_16bit[] = { "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI" }; @@ -61,10 +61,10 @@ uint32_t getRMMemLocation(uint8_t modrm, RegisterBank* rb, InputReader* ir){ case 4: { uint8_t sib = ir->nextByte(); - return getSIBMemLocation(sib, rb, mod, ir); + return InstructionUtils::getSIBMemLocation(sib, rb, mod, ir); } case 5: - return getDisp32(ir); + return InstructionUtils::getDisp32(ir); default: return rb->get(regs_32bit[rm]); @@ -76,7 +76,7 @@ uint32_t getRMMemLocation(uint8_t modrm, RegisterBank* rb, InputReader* ir){ case 4: { uint8_t sib = ir->nextByte(); uint8_t disp8 = ir->nextByte(); - return getSIBMemLocation(sib, rb, mod, ir) + disp8; + return InstructionUtils::getSIBMemLocation(sib, rb, mod, ir) + disp8; } default: uint8_t disp8 = ir->nextByte(); @@ -88,11 +88,11 @@ uint32_t getRMMemLocation(uint8_t modrm, RegisterBank* rb, InputReader* ir){ case 4: { uint8_t sib = ir->nextByte(); - uint8_t disp32 = getDisp32(ir); - return getSIBMemLocation(sib, rb, mod, ir) + disp32; + uint8_t disp32 = InstructionUtils::getDisp32(ir); + return InstructionUtils::getSIBMemLocation(sib, rb, mod, ir) + disp32; } default: - uint32_t disp32 = getDisp32(ir); + uint32_t disp32 = InstructionUtils::getDisp32(ir); return rb->get(regs_32bit[rm]) + disp32; } } diff --git a/modrm.hpp b/src/modrm.hpp similarity index 100% rename from modrm.hpp rename to src/modrm.hpp diff --git a/opcodes.cpp b/src/opcodes.cpp similarity index 100% rename from opcodes.cpp rename to src/opcodes.cpp diff --git a/opcodes.hpp b/src/opcodes.hpp similarity index 100% rename from opcodes.hpp rename to src/opcodes.hpp diff --git a/operations/add.cpp b/src/operations/add.cpp similarity index 88% rename from operations/add.cpp rename to src/operations/add.cpp index a9441f1..824b01a 100644 --- a/operations/add.cpp +++ b/src/operations/add.cpp @@ -1,6 +1,6 @@ #include "add.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" void add_00(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -24,7 +24,7 @@ void add_00(InputReader* ir, RegisterBank* rb, Memory* mem){ // Set flags long long int temp = (long long int)op1 + (long long int)op2; - setFlagGroup(temp, 8, rb); + InstructionUtils::setFlagGroup(temp, 8, rb); } void add_01(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -49,7 +49,7 @@ void add_01(InputReader* ir, RegisterBank* rb, Memory* mem){ // Set flags long long int temp = (long long int)op1 + (long long int)op2; - setFlagGroup(temp, 32, rb); + InstructionUtils::setFlagGroup(temp, 32, rb); } void add_02(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -74,19 +74,19 @@ void add_02(InputReader* ir, RegisterBank* rb, Memory* mem){ // Set flags long long int temp = (long long int)op1 + (long long int)op2; - setFlagGroup(temp, 8, rb); + InstructionUtils::setFlagGroup(temp, 8, rb); } void add_04(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "04 - Add imm8 to AL \n"; - int8_t imm8 = getImm8(ir); + int8_t imm8 = InstructionUtils::getImm8(ir); uint8_t al = rb->get("AL"); rb->set("AL", (uint8_t) (al+imm8)); // Set flags long long int temp = (long long int)imm8 + (long long int)al; - setFlagGroup(temp, 8, rb); + InstructionUtils::setFlagGroup(temp, 8, rb); } void add_83(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -99,19 +99,19 @@ void add_83(InputReader* ir, RegisterBank* rb, Memory* mem){ if (isRMReg(modrm)){ std::string rm32 = getRMReg(modrm, REG_32); op2 = rb->get(rm32); - imm8 = getImm8(ir); + imm8 = InstructionUtils::getImm8(ir); rb->set(rm32, (imm8+op2)); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); op2 = mem->read(rm_mem); - imm8 = getImm8(ir); + imm8 = InstructionUtils::getImm8(ir); mem->write(rm_mem, (imm8+op2)); } // Set flags long long int temp = (long long int)imm8 + (long long int)op2; - setFlagGroup(temp, 32, rb); + InstructionUtils::setFlagGroup(temp, 32, rb); } void add(InputReader* ir, RegisterBank* rb, Memory* mem, uint8_t opcode){ diff --git a/operations/add.hpp b/src/operations/add.hpp similarity index 100% rename from operations/add.hpp rename to src/operations/add.hpp diff --git a/operations/all_operations.hpp b/src/operations/all_operations.hpp similarity index 100% rename from operations/all_operations.hpp rename to src/operations/all_operations.hpp diff --git a/operations/and.cpp b/src/operations/and.cpp similarity index 82% rename from operations/and.cpp rename to src/operations/and.cpp index 9a8b4ed..71ca86b 100644 --- a/operations/and.cpp +++ b/src/operations/and.cpp @@ -1,29 +1,29 @@ #include "and.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" void and_24(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "24 - AND AL and imm8 \n"; uint8_t al = rb->get("AL"); - uint8_t imm8 = getImm8(ir); + uint8_t imm8 = InstructionUtils::getImm8(ir); rb->set("AL", al&imm8); // Set flags - No overflow possible, so no need to cast to long - setFlagGroup(al&imm8, 8, rb); + InstructionUtils::setFlagGroup(al&imm8, 8, rb); } void and_25(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "25 - AND EAX and imm32 \n"; uint32_t eax = rb->get("EAX"); - uint32_t imm32 = getImm32(ir); + uint32_t imm32 = InstructionUtils::getImm32(ir); rb->set("EAX", eax&imm32); // Set flags - setFlagGroup(eax&imm32, 32, rb); + InstructionUtils::setFlagGroup(eax&imm32, 32, rb); } void and_20(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -36,13 +36,13 @@ void and_20(InputReader* ir, RegisterBank* rb, Memory* mem){ std::string rm8 = getRMReg(modrm, REG_8); uint8_t result = rb->get(r8) & rb->get(rm8); rb->set(rm8, result); - setFlagGroup(result, 8, rb); + InstructionUtils::setFlagGroup(result, 8, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); uint32_t result = rb->get(r8) & mem->read(rm_mem); mem->write(rm_mem, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } } @@ -56,13 +56,13 @@ void and_21(InputReader* ir, RegisterBank* rb, Memory* mem){ std::string rm32 = getRMReg(modrm, REG_32); uint8_t result = rb->get(r32) & rb->get(rm32); rb->set(rm32, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); uint32_t result = rb->get(r32) & mem->read(rm_mem); mem->write(rm_mem, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } } void and_22(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -80,7 +80,7 @@ void and_22(InputReader* ir, RegisterBank* rb, Memory* mem){ } uint8_t result = rb->get(r8)&rm8; rb->set(r8, result); - setFlagGroup(result, 8, rb); + InstructionUtils::setFlagGroup(result, 8, rb); } diff --git a/operations/and.hpp b/src/operations/and.hpp similarity index 100% rename from operations/and.hpp rename to src/operations/and.hpp diff --git a/operations/bsf.cpp b/src/operations/bsf.cpp similarity index 97% rename from operations/bsf.cpp rename to src/operations/bsf.cpp index b676403..ceb86d3 100644 --- a/operations/bsf.cpp +++ b/src/operations/bsf.cpp @@ -1,6 +1,6 @@ #include "bsf.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" #include diff --git a/operations/bsf.hpp b/src/operations/bsf.hpp similarity index 100% rename from operations/bsf.hpp rename to src/operations/bsf.hpp diff --git a/operations/bsr.cpp b/src/operations/bsr.cpp similarity index 97% rename from operations/bsr.cpp rename to src/operations/bsr.cpp index 1fb3c5d..b314c23 100644 --- a/operations/bsr.cpp +++ b/src/operations/bsr.cpp @@ -1,6 +1,6 @@ #include "bsr.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" #include diff --git a/operations/bsr.hpp b/src/operations/bsr.hpp similarity index 100% rename from operations/bsr.hpp rename to src/operations/bsr.hpp diff --git a/operations/cmp.cpp b/src/operations/cmp.cpp similarity index 81% rename from operations/cmp.cpp rename to src/operations/cmp.cpp index 33b7344..8ff418f 100644 --- a/operations/cmp.cpp +++ b/src/operations/cmp.cpp @@ -1,25 +1,25 @@ #include "cmp.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" void cmp_3C(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "3C - Compare AL, imm8 \n"; uint8_t al = rb->get("AL"); - int8_t imm8 = getImm8(ir); + int8_t imm8 = InstructionUtils::getImm8(ir); long long int temp = (long long int)al - (long long int)imm8; - setFlagGroup(temp, 8, rb); + InstructionUtils::setFlagGroup(temp, 8, rb); } void cmp_3D(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "3D - Compare EAX and imm32 \n"; uint32_t eax = rb->get("EAX"); - int32_t imm32 = getImm32(ir); + int32_t imm32 = InstructionUtils::getImm32(ir); long long int temp = (long long int)eax - (long long int)imm32; - setFlagGroup(temp, 32, rb); + InstructionUtils::setFlagGroup(temp, 32, rb); } void cmp_38(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -31,12 +31,12 @@ void cmp_38(InputReader* ir, RegisterBank* rb, Memory* mem){ if (isRMReg(modrm)){ std::string rm8 = getRMReg(modrm, REG_8); long long int temp = (long long int)(rb->get(rm8)) - (long long int)(rb->get(r8)); - setFlagGroup(temp, 8, rb); + InstructionUtils::setFlagGroup(temp, 8, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); long long int temp = (long long int)(mem->read(rm_mem)) - (long long int)(rb->get(r8)); - setFlagGroup(temp, 32, rb); + InstructionUtils::setFlagGroup(temp, 32, rb); } } @@ -49,12 +49,12 @@ void cmp_39(InputReader* ir, RegisterBank* rb, Memory* mem){ if (isRMReg(modrm)){ std::string rm32 = getRMReg(modrm, REG_8); long long int temp = (long long int)(rb->get(rm32)) - (long long int)(rb->get(r32)); - setFlagGroup(temp, 32, rb); + InstructionUtils::setFlagGroup(temp, 32, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); long long int temp = (long long int)(mem->read(rm_mem)) - (long long int)(rb->get(r32)); - setFlagGroup(temp, 32, rb); + InstructionUtils::setFlagGroup(temp, 32, rb); } } diff --git a/operations/cmp.hpp b/src/operations/cmp.hpp similarity index 100% rename from operations/cmp.hpp rename to src/operations/cmp.hpp diff --git a/operations/mov.cpp b/src/operations/mov.cpp similarity index 98% rename from operations/mov.cpp rename to src/operations/mov.cpp index d095de9..f458b4c 100644 --- a/operations/mov.cpp +++ b/src/operations/mov.cpp @@ -1,6 +1,6 @@ #include "mov.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" void mov_88(InputReader* ir, RegisterBank* rb, Memory* mem){ diff --git a/operations/mov.hpp b/src/operations/mov.hpp similarity index 100% rename from operations/mov.hpp rename to src/operations/mov.hpp diff --git a/operations/or.cpp b/src/operations/or.cpp similarity index 81% rename from operations/or.cpp rename to src/operations/or.cpp index efcef40..9a4f9da 100644 --- a/operations/or.cpp +++ b/src/operations/or.cpp @@ -1,29 +1,29 @@ #include "or.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" void or_0C(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "0C - OR AL and imm8 \n"; uint8_t al = rb->get("AL"); - uint8_t imm8 = getImm8(ir); + uint8_t imm8 = InstructionUtils::getImm8(ir); rb->set("AL", al|imm8); // Set flags - No overflow possible, so no need to cast to long - setFlagGroup(al|imm8, 8, rb); + InstructionUtils::setFlagGroup(al|imm8, 8, rb); } void or_0D(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "0D - OR EAX and imm32 \n"; uint32_t eax = rb->get("EAX"); - uint32_t imm32 = getImm32(ir); + uint32_t imm32 = InstructionUtils::getImm32(ir); rb->set("EAX", eax|imm32); // Set flags - setFlagGroup(eax|imm32, 32, rb); + InstructionUtils::setFlagGroup(eax|imm32, 32, rb); } void or_08(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -36,13 +36,13 @@ void or_08(InputReader* ir, RegisterBank* rb, Memory* mem){ std::string rm8 = getRMReg(modrm, REG_8); uint8_t result = rb->get(r8) | rb->get(rm8); rb->set(rm8, result); - setFlagGroup(result, 8, rb); + InstructionUtils::setFlagGroup(result, 8, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); uint32_t result = rb->get(r8) | mem->read(rm_mem); mem->write(rm_mem, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } } @@ -56,13 +56,13 @@ void or_09(InputReader* ir, RegisterBank* rb, Memory* mem){ std::string rm32 = getRMReg(modrm, REG_32); uint32_t result = rb->get(r32) | rb->get(rm32); rb->set(rm32, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); uint32_t result = rb->get(r32) | mem->read(rm_mem); mem->write(rm_mem, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } } diff --git a/operations/or.hpp b/src/operations/or.hpp similarity index 100% rename from operations/or.hpp rename to src/operations/or.hpp diff --git a/operations/stack.cpp b/src/operations/stack.cpp similarity index 93% rename from operations/stack.cpp rename to src/operations/stack.cpp index dfc0e95..f75414f 100644 --- a/operations/stack.cpp +++ b/src/operations/stack.cpp @@ -1,6 +1,6 @@ #include "stack.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" void push_50(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -14,13 +14,13 @@ void push_50(InputReader* ir, RegisterBank* rb, Memory* mem){ void push_6A(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "6A - Push imm8 \n"; - uint8_t imm8 = (uint8_t) getImm8(ir); + uint8_t imm8 = (uint8_t) InstructionUtils::getImm8(ir); mem->push_stack(imm8, rb, 1); } void push_68(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "68 - Push imm32 \n"; - uint32_t imm32 = (uint32_t) getImm32(ir); + uint32_t imm32 = (uint32_t) InstructionUtils::getImm32(ir); mem->push_stack(imm32, rb, 4); } diff --git a/operations/stack.hpp b/src/operations/stack.hpp similarity index 100% rename from operations/stack.hpp rename to src/operations/stack.hpp diff --git a/operations/xor.cpp b/src/operations/xor.cpp similarity index 81% rename from operations/xor.cpp rename to src/operations/xor.cpp index ae2b846..790d618 100644 --- a/operations/xor.cpp +++ b/src/operations/xor.cpp @@ -1,29 +1,29 @@ #include "xor.hpp" #include "../modrm.hpp" -#include "../instr_utils.hpp" +#include "../instruction_utils.hpp" void xor_34(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "34 - XOR AL, imm8 \n"; uint8_t al = rb->get("AL"); - uint8_t imm8 = getImm8(ir); + uint8_t imm8 = InstructionUtils::getImm8(ir); rb->set("AL", al^imm8); // Set flags - No overflow possible, so no need to cast to long - setFlagGroup(al^imm8, 8, rb); + InstructionUtils::setFlagGroup(al^imm8, 8, rb); } void xor_35(InputReader* ir, RegisterBank* rb, Memory* mem){ std::cout << "35 - XOR EAX, imm32 \n"; uint32_t eax = rb->get("EAX"); - uint32_t imm32 = getImm32(ir); + uint32_t imm32 = InstructionUtils::getImm32(ir); rb->set("EAX", eax^imm32); // Set flags - setFlagGroup(eax^imm32, 32, rb); + InstructionUtils::setFlagGroup(eax^imm32, 32, rb); } void xor_30(InputReader* ir, RegisterBank* rb, Memory* mem){ @@ -36,13 +36,13 @@ void xor_30(InputReader* ir, RegisterBank* rb, Memory* mem){ std::string rm8 = getRMReg(modrm, REG_8); uint8_t result = rb->get(r8) ^ rb->get(rm8); rb->set(rm8, result); - setFlagGroup(result, 8, rb); + InstructionUtils::setFlagGroup(result, 8, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); uint32_t result = rb->get(r8) ^ mem->read(rm_mem); mem->write(rm_mem, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } } @@ -56,13 +56,13 @@ void xor_31(InputReader* ir, RegisterBank* rb, Memory* mem){ std::string rm32 = getRMReg(modrm, REG_32); uint32_t result = rb->get(r32) ^ rb->get(rm32); rb->set(rm32, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } else{ uint32_t rm_mem = getRMMemLocation(modrm, rb, ir); uint32_t result = rb->get(r32) ^ mem->read(rm_mem); mem->write(rm_mem, result); - setFlagGroup(result, 32, rb); + InstructionUtils::setFlagGroup(result, 32, rb); } } diff --git a/operations/xor.hpp b/src/operations/xor.hpp similarity index 100% rename from operations/xor.hpp rename to src/operations/xor.hpp diff --git a/register.cpp b/src/register.cpp similarity index 100% rename from register.cpp rename to src/register.cpp diff --git a/register.hpp b/src/register.hpp similarity index 100% rename from register.hpp rename to src/register.hpp diff --git a/src/register_bank.cpp b/src/register_bank.cpp new file mode 100644 index 0000000..142388e --- /dev/null +++ b/src/register_bank.cpp @@ -0,0 +1,11 @@ +#include "register_bank.hpp" +#include + +void RegisterBank::loadLayoutFromFile(std::string filePath) +{ + // parents need to be declared before kids + // name, size, parent and offset + // if something doesn't add up throw error and terminate + + +} \ No newline at end of file diff --git a/src/register_bank.hpp b/src/register_bank.hpp new file mode 100644 index 0000000..54a4b74 --- /dev/null +++ b/src/register_bank.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +using SIZE_32 = uint32_t; +using SIZE_16 = uint16_t; +using SIZE_8 = uint8_t; + +template +class Register +{ + std::string m_name; + SIZE* m_data; + + void clear(); + + template + void set(T value) = delete; + + void set(SIZE value) + { + *m_data = value; + } +}; + +class Flag +{ + std::string name; + uint32_t* m_flagRegisterData; + uint8_t index; + + void set() + { + *m_flagRegisterData |= (1 << index); + } + void clear() + { + *m_flagRegisterData &= (0xFFFF - (1 << index)); + } +}; + +class RegisterBank +{ +private: + // Add more if larger sizes need to be supported. + // Also think about maintaining a reverse mapping of name to map for fast lookup. + std::unordered_map*> m_32BitRegs; + std::unordered_map*> m_16BitRegs; + std::unordered_map*> m_8BitRegs; + + std::unordered_map m_flags; + +public: + void loadLayoutFromFile(std::string filePath); +}; \ No newline at end of file diff --git a/src/utils/csv_reader.hpp b/src/utils/csv_reader.hpp new file mode 100644 index 0000000..c92d0ac --- /dev/null +++ b/src/utils/csv_reader.hpp @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include + +using CsvRow = std::vector; + +class CsvReaderUtils +{ +// todo wrap below in full fileread function + +public: + static void + getCsvRowValues(const std::string& row, char delimiter, std::function action) + { + CsvRow valueVec; + auto left = row.begin(); + for (auto it = left; it != row.end(); ++it) + { + if (*it == delimiter) + { + valueVec.emplace_back(&*left, it - left); + left = it + 1; + } + } + if (left != row.end()) + valueVec.emplace_back(&*left, row.end() - left); + action(valueVec); + } +}; \ No newline at end of file diff --git a/test/Test.cpp b/test/Test.cpp new file mode 100644 index 0000000..1a25ca3 --- /dev/null +++ b/test/Test.cpp @@ -0,0 +1,20 @@ +#include "../src/emulator.hpp" +#include "state_test_utils.hpp" +#include + +TEST(EmulatorE2ETests, Test01) +{ + Emulator& testEmulator = Emulator::getInstance(); + testEmulator.run("../inputs/multi-ops/test-02.txt"); + + RegisterBank rb {}; + Memory mem {}; + + StateTestUtils::loadStateFromFile("../test/resources/expected_00.csv", rb, mem); + + // todo figure out a better way to show fail points + + EXPECT_NE(&testEmulator.getRegisterBank(), nullptr); + EXPECT_TRUE(StateTestUtils::areRegisterBanksEqual(rb, testEmulator.getRegisterBank())); + EXPECT_TRUE(StateTestUtils::areMemoryBanksEqual(mem, testEmulator.getMemory())); +} \ No newline at end of file diff --git a/test/resources/expected_00.csv b/test/resources/expected_00.csv new file mode 100644 index 0000000..0788633 --- /dev/null +++ b/test/resources/expected_00.csv @@ -0,0 +1,41 @@ +EAX,bf8db140 +EBX,ae5ff4 +ECX,6 +EDX,17 +AX,b140 +BX,5ff4 +CX,6 +DX,17 +AH,b1 +BH,5f +CH,0 +DH,0 +AL,40 +BL,f4 +CL,6 +DL,17 +EBP,bf8db118 +EDI,0 +EFLAGS,203 +EIP,8048354 +ESI,9a0ca0 +ESP,bf8db0b8 +AC,0 +AF,0 +CF,1 +DF,0 +ID,0 +IF,1 +NT,0 +OF,0 +PF,0 +RF,0 +SF,0 +TF,0 +VIF,0 +VIP,0 +VM,0 +ZF,0 +9a0ccf,bf8db146 +bf8db144,ae5ff4 +bf8db0b8,bf8db140 \ No newline at end of file diff --git a/test/state_test_utils.hpp b/test/state_test_utils.hpp new file mode 100644 index 0000000..7198bbc --- /dev/null +++ b/test/state_test_utils.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include "../src/memory.hpp" +#include "../src/register.hpp" +#include "../src/utils/csv_reader.hpp" +#include +#include +#include + +class StateTestUtils +{ +public: + // TODO move these to source + static std::unordered_set registerNames; + + static std::unordered_set flagNames; + +public: + static void loadStateFromFile(std::string filePath, RegisterBank& rb, Memory& mem) + { + // TODO clear all in inputs before starting + std::ifstream stateFile; + stateFile.open(filePath); + + if (!stateFile) + { + throw std::runtime_error("File does not exist."); + } + + std::string line; + while (std::getline(stateFile, line)) + { + if (line.empty()) + throw std::runtime_error("File format incorrect"); + + auto constructStateFunc = [&](CsvRow& row) { + if (row.size() != 2) + { + throw std::runtime_error("File format incorrect - no 2 items"); + } + std::string key{row[0]}; + uint32_t value = + static_cast(std::stoul(std::string(row[1]), nullptr, 16)); + // todo handle nullptr + if (registerNames.find(key) != registerNames.end()) + rb.set(key, value); + else if (flagNames.find(key) != flagNames.end()) + { + if (value == 1) + rb.setFlag(key); + } + else + { + uint32_t keyInt = static_cast(std::stoul(key, nullptr, 16)); + mem.write(keyInt, value); + } + }; + + CsvReaderUtils::getCsvRowValues(line, ',', constructStateFunc); + } + stateFile.close(); + } + + // TODO make the args const after adding consts in RB and Mem + static bool areRegisterBanksEqual(RegisterBank& r1, RegisterBank& r2) + { + for (auto& name : registerNames) + { + if (r1.get(name) != r2.get(name)) + return false; + } + for (auto& name : flagNames) + { + if (r1.getFlag(name) != r2.getFlag(name)) + return false; + } + return true; + } + + static bool areMemoryBanksEqual(Memory& m1, Memory& m2) + { + return m1.getMemoryLayout() == m2.getMemoryLayout(); + } +}; + +std::unordered_set StateTestUtils::registerNames = { + "EAX", "EBX", "ECX", "EDX", "AX", "BX", "CX", "DX", "AH", "BH", + "CH", "DH", "AL", "BL", "CL", "DL", "CS", "DS", "ES", "FS", + "GS", "SS", "ESI", "EDI", "EBP", "EIP", "ESP", "EFLAGS"}; + +std::unordered_set StateTestUtils::flagNames = {"CF", + "PF", + "AF", + "ZF", + "SF", + "TF", + "IF", + "DF", + "OF", + "NT", + "RF", + "VM", + "AC", + "VIF", + "VIP", + "ID"}; \ No newline at end of file