Add onto 8086 emulation test suite
This commit is contained in:
parent
8879c0e432
commit
b1fb32c0c1
@ -3,11 +3,13 @@
|
|||||||
#include "bsuml.h"
|
#include "bsuml.h"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include <map>
|
||||||
|
#include <print>
|
||||||
|
|
||||||
namespace bio {
|
namespace bio {
|
||||||
namespace emu {
|
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];
|
memory_dependent using mem_buffer = unsigned char[memsize];
|
||||||
|
|
||||||
@ -32,6 +34,7 @@ namespace bio {
|
|||||||
mem_buffer<memsize> memory;
|
mem_buffer<memsize> memory;
|
||||||
std::vector<symbol> symbols;
|
std::vector<symbol> symbols;
|
||||||
std::vector<int> callStack;
|
std::vector<int> callStack;
|
||||||
|
std::map<std::string, unsigned long long> registers;
|
||||||
|
|
||||||
virtual void load_app(application& app) = 0;
|
virtual void load_app(application& app) = 0;
|
||||||
virtual void run_symbol(int symbol) = 0;
|
virtual void run_symbol(int symbol) = 0;
|
||||||
@ -67,12 +70,15 @@ namespace bio {
|
|||||||
bool returned = false;
|
bool returned = false;
|
||||||
until(returned || instructionPointer >= memsize) {
|
until(returned || instructionPointer >= memsize) {
|
||||||
this->instructionPointer +=
|
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) {
|
if (instructionPointer < 0 || instructionPointer > memsize) {
|
||||||
throw std::out_of_range("Symbol causes instruction pointer to err out of memory");
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,18 +5,135 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
||||||
|
std::string iAPX_reg_lookup(char r) {
|
||||||
|
switch (r) {
|
||||||
|
case 0b00000000:
|
||||||
|
return "ax";
|
||||||
|
break;
|
||||||
|
|
||||||
isa_instruction(bio::Intel::ISAs::iAPX86::invalid) {
|
case 0b00000001:
|
||||||
std::println("Invalid opcode used - {} at {}", memory[position], position);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
isa_instruction(bio::Intel::ISAs::iAPX86::nop) {
|
isa_instruction_template(bio::Intel::ISAs::iAPX86::dec_16b_reg_template) {
|
||||||
|
(*registers)[args]--;
|
||||||
return 1;
|
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) {
|
isa_instruction(bio::Intel::ISAs::iAPX86::call_e8) {
|
||||||
short jump;
|
short jump;
|
||||||
memcpy(&jump, memory + (position + 1), 2);
|
memcpy(&jump, memory + (position + 1), 2);
|
||||||
|
callstack->push_back(position + 3);
|
||||||
return jump + 3;
|
return jump + 3;
|
||||||
}
|
}
|
||||||
@ -9,8 +9,17 @@ namespace bio {
|
|||||||
|
|
||||||
namespace ISAs {
|
namespace ISAs {
|
||||||
namespace iAPX86 {
|
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(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);
|
isa_instruction(call_e8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -18,8 +27,6 @@ namespace bio {
|
|||||||
// 8086 emulation
|
// 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:
|
public:
|
||||||
|
|
||||||
iAPX86() {
|
iAPX86() {
|
||||||
@ -27,9 +34,29 @@ namespace bio {
|
|||||||
this->isa.push_back(ISAs::iAPX86::invalid);
|
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[0xE8] = ISAs::iAPX86::call_e8;
|
||||||
this->isa[0x90] = ISAs::iAPX86::nop;
|
|
||||||
|
|
||||||
times(sizeof(this->memory)) {
|
times(sizeof(this->memory)) {
|
||||||
this->memory[___i] = '\0';
|
this->memory[___i] = '\0';
|
||||||
|
|||||||
@ -18,11 +18,36 @@ using native_callable = T(*)(args...);
|
|||||||
|
|
||||||
// emulation-related definitions
|
// emulation-related definitions
|
||||||
|
|
||||||
#define isa_instruction(x) int x(int position, unsigned char* memory, bool* emu_return, ptr<std::vector<int>> callstack)
|
#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
|
// position = current instruction pointer position
|
||||||
// memory = memory buffer
|
// memory = memory buffer
|
||||||
// emureturn = end of symbol
|
// emureturn = end of symbol
|
||||||
// callstack = callstack pointer
|
// 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_dependent template<int memsize>
|
||||||
#define memory_passdown(x) x<memsize>
|
#define memory_passdown(x) x<memsize>
|
||||||
|
|||||||
@ -8,4 +8,9 @@ entry{
|
|||||||
|
|
||||||
i8086->load_app(*bin->getApp());
|
i8086->load_app(*bin->getApp());
|
||||||
i8086->run_symbol(-1);
|
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.
@ -1,4 +1,48 @@
|
|||||||
global main
|
;;; tested with NASM version 2.16.01
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
|
|
||||||
|
;; entry point
|
||||||
main:
|
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
|
||||||
15
readme.md
15
readme.md
@ -1 +1,16 @@
|
|||||||
# bioSandbox
|
# 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
|
||||||
Loading…
x
Reference in New Issue
Block a user