Add onto 8086 emulation test suite
This commit is contained in:
parent
8879c0e432
commit
b1fb32c0c1
@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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';
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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.
@ -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
|
||||
17
readme.md
17
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