diff --git a/biosandbox/Emulation.h b/biosandbox/Emulation.h index df82271..fa767f7 100644 --- a/biosandbox/Emulation.h +++ b/biosandbox/Emulation.h @@ -3,11 +3,13 @@ #include "bsuml.h" #include #include +#include +#include namespace bio { namespace emu { - using instruction_set = std::vector*>>; + using instruction_set = std::vector*, std::map*>>; memory_dependent using mem_buffer = unsigned char[memsize]; @@ -32,6 +34,7 @@ namespace bio { mem_buffer memory; std::vector symbols; std::vector callStack; + std::map registers; virtual void load_app(application& app) = 0; virtual void run_symbol(int symbol) = 0; @@ -67,12 +70,15 @@ namespace bio { bool returned = false; until(returned || instructionPointer >= memsize) { this->instructionPointer += - this->isa[this->memory[instructionPointer]](instructionPointer, this->memory, &returned, &this->callStack); + this->isa[this->memory[instructionPointer]](instructionPointer, this->memory, &returned, &this->callStack, &this->registers); if (instructionPointer < 0 || instructionPointer > memsize) { throw std::out_of_range("Symbol causes instruction pointer to err out of memory"); } } + if (returned) { + std::print("Exited through conventional means [returned out of symbol]\n"); + } } }; diff --git a/biosandbox/Intel.cpp b/biosandbox/Intel.cpp index 6abf0dd..8f2c758 100644 --- a/biosandbox/Intel.cpp +++ b/biosandbox/Intel.cpp @@ -5,18 +5,135 @@ #include +std::string iAPX_reg_lookup(char r) { + switch (r) { + case 0b00000000: + return "ax"; + break; -isa_instruction(bio::Intel::ISAs::iAPX86::invalid) { - std::println("Invalid opcode used - {} at {}", memory[position], position); + case 0b00000001: + return "cx"; + break; + + case 0b00000010: + return "dx"; + break; + + case 0b00000011: + return "bx"; + break; + default: + return "invalid"; + break; + } +} + + +isa_instruction_template(bio::Intel::ISAs::iAPX86::mov_16b_imm_to_reg_template) { + short value; + memcpy(&value, memory + (position + 1), 2); + (*registers)[args] = value; + return 3; +} + +isa_instruction_template(bio::Intel::ISAs::iAPX86::inc_16b_reg_template) { + (*registers)[args]++; return 1; } -isa_instruction(bio::Intel::ISAs::iAPX86::nop) { +isa_instruction_template(bio::Intel::ISAs::iAPX86::dec_16b_reg_template) { + (*registers)[args]--; return 1; } + + +isa_instruction(bio::Intel::ISAs::iAPX86::invalid) { + std::println("Invalid (or unimplemented) opcode used - {} at {}", memory[position], position); + return 1; +} + + +isa_instruction(bio::Intel::ISAs::iAPX86::add_01) { + + + unsigned char mode, src, dest; + + mode = memory[position + 1]; + mode >>= 6; + + src = memory[position + 1]; + src >>= 3; + src &= 0b00000111; + + dest = memory[position + 1]; + dest &= 0b00000111; + + (*registers)[iAPX_reg_lookup(dest)] += (*registers)[iAPX_reg_lookup(src)]; + + return 2; + +} + +isa_instruction(bio::Intel::ISAs::iAPX86::sub_29) { + + + unsigned char mode, src, dest; + + mode = memory[position + 1]; + mode >>= 6; + + src = memory[position + 1]; + src >>= 3; + src &= 0b00000111; + + dest = memory[position + 1]; + dest &= 0b00000111; + + (*registers)[iAPX_reg_lookup(dest)] -= (*registers)[iAPX_reg_lookup(src)]; + + return 2; + +} + +isa_instruction(bio::Intel::ISAs::iAPX86::grp1_83) { + std::println("Unfinished opcode used - {} at {}", memory[position], position); + unsigned char subopcode = memory[position + 1]; + + // adc ax, imm8 + if ((subopcode & 0b11000000) == (subopcode & 0b11110000)) { + short s = (short)(*registers)["ax"]; + s += memory[position + 2]; + (*registers)["ax"] = s; + return 3; + } + + std::println("Unknown GRP1 instruction."); + return 1; +} + +isa_instruction(bio::Intel::ISAs::iAPX86::nop_90) { + return 1; +} + + +isa_instruction(bio::Intel::ISAs::iAPX86::ret_c3) { + + if (callstack->size() == 0) { + *emu_return = true; + return 0; + } + + int lastStackCall = (*callstack)[callstack->size() - 1]; + callstack->pop_back(); + + return lastStackCall - position; + +} + isa_instruction(bio::Intel::ISAs::iAPX86::call_e8) { short jump; memcpy(&jump, memory + (position + 1), 2); + callstack->push_back(position + 3); return jump + 3; } \ No newline at end of file diff --git a/biosandbox/Intel.h b/biosandbox/Intel.h index 4e6994e..82bb7b6 100644 --- a/biosandbox/Intel.h +++ b/biosandbox/Intel.h @@ -9,16 +9,23 @@ namespace bio { namespace ISAs { namespace iAPX86 { + isa_instruction_template(mov_16b_imm_to_reg_template); + isa_instruction_template(inc_16b_reg_template); + isa_instruction_template(dec_16b_reg_template); + isa_instruction(invalid); - isa_instruction(nop); + + isa_instruction(add_01); + isa_instruction(sub_29); + isa_instruction(grp1_83); + isa_instruction(nop_90); + isa_instruction(ret_c3); isa_instruction(call_e8); } } // 8086 emulation - memory_dependent class iAPX86 : public memory_passdown(::bio::emu::linear_emulator_base){ - - + memory_dependent class iAPX86 : public memory_passdown(::bio::emu::linear_emulator_base) { public: @@ -27,9 +34,29 @@ namespace bio { this->isa.push_back(ISAs::iAPX86::invalid); } + this->isa[0x01] = ISAs::iAPX86::add_01; + this->isa[0x29] = ISAs::iAPX86::sub_29; + this->isa[0x40] = isa_instruction_templater(ISAs::iAPX86::inc_16b_reg_template, "ax"); + this->isa[0x41] = isa_instruction_templater(ISAs::iAPX86::inc_16b_reg_template, "cx"); + this->isa[0x42] = isa_instruction_templater(ISAs::iAPX86::inc_16b_reg_template, "dx"); + this->isa[0x43] = isa_instruction_templater(ISAs::iAPX86::inc_16b_reg_template, "bx"); + + this->isa[0x48] = isa_instruction_templater(ISAs::iAPX86::dec_16b_reg_template, "ax"); + this->isa[0x49] = isa_instruction_templater(ISAs::iAPX86::dec_16b_reg_template, "cx"); + this->isa[0x4A] = isa_instruction_templater(ISAs::iAPX86::dec_16b_reg_template, "dx"); + this->isa[0x4B] = isa_instruction_templater(ISAs::iAPX86::dec_16b_reg_template, "bx"); + + this->isa[0x83] = ISAs::iAPX86::grp1_83; + this->isa[0x90] = ISAs::iAPX86::nop_90; + + this->isa[0xB8] = isa_instruction_templater(ISAs::iAPX86::mov_16b_imm_to_reg_template, "ax"); + this->isa[0xB9] = isa_instruction_templater(ISAs::iAPX86::mov_16b_imm_to_reg_template, "cx"); + this->isa[0xBA] = isa_instruction_templater(ISAs::iAPX86::mov_16b_imm_to_reg_template, "dx"); + this->isa[0xBB] = isa_instruction_templater(ISAs::iAPX86::mov_16b_imm_to_reg_template, "bx"); + + this->isa[0xC3] = ISAs::iAPX86::ret_c3; this->isa[0xE8] = ISAs::iAPX86::call_e8; - this->isa[0x90] = ISAs::iAPX86::nop; times(sizeof(this->memory)) { this->memory[___i] = '\0'; diff --git a/biosandbox/bsuml.h b/biosandbox/bsuml.h index 0eb21f2..dddba10 100644 --- a/biosandbox/bsuml.h +++ b/biosandbox/bsuml.h @@ -18,11 +18,36 @@ using native_callable = T(*)(args...); // emulation-related definitions -#define isa_instruction(x) int x(int position, unsigned char* memory, bool* emu_return, ptr> callstack) - // position = current instruction pointer position - // memory = memory buffer - // emureturn = end of symbol -// callstack = callstack pointer +#define isa_instruction(x) int x(int position, \ +unsigned char* memory, \ +bool* emu_return, \ +ptr<::std::vector> callstack, \ +ptr<::std::map<::std::string, \ +unsigned long long>> registers) + // position = current instruction pointer position + // memory = memory buffer + // emureturn = end of symbol + // callstack = callstack pointer +// registers = registers pointer + +#define isa_instruction_template(x) int x(int position, \ +unsigned char* memory, \ +bool* emu_return, \ +ptr<::std::vector> callstack, \ +ptr<::std::map<::std::string, \ +unsigned long long>> registers, \ +::std::string args) + // position = current instruction pointer position + // memory = memory buffer + // emureturn = end of symbol + // callstack = callstack pointer + // registers = registers pointer +// args = template arguments + +#define isa_instruction_templater(x, args) \ +[](int p, unsigned char* m, bool* er, ::std::vector* cs, ::std::map<::std::string,unsigned long long>* r) -> int{\ + return x(p, m, er, cs, r, args);\ +} #define memory_dependent template #define memory_passdown(x) x diff --git a/biosandbox/main.cpp b/biosandbox/main.cpp index 1577248..45fa9f6 100644 --- a/biosandbox/main.cpp +++ b/biosandbox/main.cpp @@ -2,10 +2,15 @@ #include "Intel.h" #include "BinFile.h" -entry{ +entry { ptr> i8086 = new bio::Intel::iAPX86<1024>; ptr bin = new bio::BinFile("code/test"); i8086->load_app(*bin->getApp()); i8086->run_symbol(-1); + + std::println("AX: {}", i8086->registers["ax"]); + std::println("CX: {}", i8086->registers["cx"]); + std::println("DX: {}", i8086->registers["dx"]); + std::println("BX: {}", i8086->registers["bx"]); } \ No newline at end of file diff --git a/gameenv/code/test b/gameenv/code/test index 0fa0477..db2857a 100644 Binary files a/gameenv/code/test and b/gameenv/code/test differ diff --git a/gameenv/code/test.asm b/gameenv/code/test.asm index 129c28d..c5c2db1 100644 --- a/gameenv/code/test.asm +++ b/gameenv/code/test.asm @@ -1,4 +1,48 @@ -global main +;;; tested with NASM version 2.16.01 + section .text + +;; entry point main: -call 0 \ No newline at end of file + mov ax, 1 + call _addition_test + ret + +;; adding tests +_add_cx: + mov cx, 25 + add ax, cx + ret +_add_dx: + mov dx, 50 + add ax, dx + ret +_add_bx: + mov bx, 100 + add ax, bx + ret + +;; subtracting tests +_sub_cx: + sub ax, cx + ret +_sub_dx: + sub ax, dx + ret +_sub_bx: + sub ax, bx + ret + +;; test function for call stack test +_addition_test: + call _add_cx + call _add_dx + call _add_bx + + call _sub_cx + call _sub_dx + call _sub_bx + + inc ax + dec dx + ret ; result should be 2 \ No newline at end of file diff --git a/readme.md b/readme.md index 6dd2acc..9306b85 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,16 @@ -# bioSandbox \ No newline at end of file +# bioSandbox +--- + +## Dependencies +- C++26 STL +- [raylib](https://raylib.com) +- [rxi/dyad](https://github.com/rxi/dyad) + +## 8086 emulator compatibility +All features the 8086 emulator of the engine supports are demonstrated in the [test assembly file and binary](gameenv/code/test.asm). + +- addition on (A/C/D/B)X from (A/C/D/B)X +- subtraction on (A/C/D/B)X from (A/C/D/B)X +- move on (A/C/D/B)X from immediate +- increment/decrement (A/C/D/B)X +- call functions and return \ No newline at end of file