Add onto 8086 emulation test suite

This commit is contained in:
Safariminer 2025-12-28 16:35:28 -05:00
parent 8879c0e432
commit b1fb32c0c1
8 changed files with 258 additions and 19 deletions

View File

@ -3,11 +3,13 @@
#include "bsuml.h"
#include <stdexcept>
#include <format>
#include <map>
#include <print>
namespace bio {
namespace emu {
using instruction_set = std::vector<native_callable<int, int, unsigned char*, bool*, std::vector<int>*>>;
using instruction_set = std::vector<native_callable<int, int, unsigned char*, bool*, std::vector<int>*, std::map<std::string, unsigned long long>*>>;
memory_dependent using mem_buffer = unsigned char[memsize];
@ -32,6 +34,7 @@ namespace bio {
mem_buffer<memsize> memory;
std::vector<symbol> symbols;
std::vector<int> callStack;
std::map<std::string, unsigned long long> 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");
}
}
};

View File

@ -5,18 +5,135 @@
#include <stdexcept>
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;
}

View File

@ -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';

View File

@ -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<std::vector<int>> 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<int>> 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<int>> 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<int>* cs, ::std::map<::std::string,unsigned long long>* r) -> int{\
return x(p, m, er, cs, r, args);\
}
#define memory_dependent template<int memsize>
#define memory_passdown(x) x<memsize>

View File

@ -2,10 +2,15 @@
#include "Intel.h"
#include "BinFile.h"
entry{
entry {
ptr<bio::Intel::iAPX86<1024>> i8086 = new bio::Intel::iAPX86<1024>;
ptr<bio::BinFile> 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"]);
}

Binary file not shown.

View File

@ -1,4 +1,48 @@
global main
;;; tested with NASM version 2.16.01
section .text
;; entry point
main:
call 0
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

View File

@ -1 +1,16 @@
# 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