added syscalls and a stack to the 8086 emulation

This commit is contained in:
Safariminer 2025-12-29 16:35:06 -05:00
parent 244be9f1ae
commit c95b1b0045
13 changed files with 191 additions and 21 deletions

View File

@ -9,8 +9,11 @@
namespace bio {
namespace emu {
using instruction_set = std::vector<native_callable<int, int, unsigned char*, bool*, std::vector<int>*, std::map<std::string, unsigned long long>*>>;
using register_set = std::map<std::string, unsigned long long>;
using syscall = native_callable<void, ptr<register_set>, ptr<unsigned char>, std::vector<int>*>;
using syscall_set = std::map<short, syscall>;
using instruction = native_callable<int, int, unsigned char*, bool*, std::vector<int>*, std::map<std::string, unsigned long long>*, std::vector<int>*, ptr<syscall_set>>;
using instruction_set = std::vector<instruction>;
memory_dependent using mem_buffer = unsigned char[memsize];
struct symbol {
@ -29,13 +32,16 @@ namespace bio {
memory_dependent class emulator_base {
public:
int instructionPointer;
instruction_set isa;
mem_buffer<memsize> memory;
std::vector<symbol> symbols;
std::vector<int> callStack;
std::map<std::string, unsigned long long> registers;
std::vector<int> stack;
register_set registers;
syscall_set syscalls;
virtual void load_app(application& app) = 0;
virtual bool run_symbol(int symbol) = 0;
void clear_registers() {
@ -50,7 +56,6 @@ namespace bio {
memory_dependent class linear_emulator_base : public memory_passdown(emulator_base) {
public:
int instructionPointer;
bool run_symbol(int symbol) {
// check symbol ID is within symbol count
if (symbol > this->symbols.size() && symbol != -1)
@ -71,16 +76,24 @@ namespace bio {
);
// apply instruction pointer
if (symbol >= 0) instructionPointer = this->symbols[symbol].offset;
else instructionPointer = 0;
if (symbol >= 0) this->instructionPointer = this->symbols[symbol].offset;
else this->instructionPointer = 0;
// execute
bool returned = false;
until(returned || instructionPointer >= memsize) {
until(returned || this->instructionPointer >= memsize) {
this->instructionPointer +=
this->isa[this->memory[instructionPointer]](instructionPointer, this->memory, &returned, &this->callStack, &this->registers);
this->isa[this->memory[this->instructionPointer]](
this->instructionPointer,
this->memory,
&returned,
&this->callStack,
&this->registers,
&this->stack,
&this->syscalls
);
if (instructionPointer < 0 || instructionPointer > memsize) {
if (this->instructionPointer < 0 || this->instructionPointer > memsize) {
throw std::out_of_range("Symbol causes instruction pointer to err out of memory");
}
}

View File

@ -47,6 +47,18 @@ isa_instruction_template(bio::Intel::ISAs::iAPX86::dec_16b_reg_template) {
}
isa_instruction_template(bio::Intel::ISAs::iAPX86::push_16b_reg_to_stack_template) {
stack->push_back((*registers)[args]);
return 1;
}
isa_instruction_template(bio::Intel::ISAs::iAPX86::pop_stack_to_16b_reg_template) {
(*registers)[args] = stack->at(stack->size() - 1);
stack->pop_back();
return 1;
}
isa_instruction(bio::Intel::ISAs::iAPX86::invalid) {
std::println("Invalid (or unimplemented) opcode used - {} at {}", memory[position], position);
@ -134,6 +146,13 @@ isa_instruction(bio::Intel::ISAs::iAPX86::ret_c3) {
isa_instruction(bio::Intel::ISAs::iAPX86::call_e8) {
short jump;
memcpy(&jump, memory + (position + 1), 2);
if (syscalls->find(jump + position + 3) != syscalls->end()) {
(*syscalls)[jump + position + 3](registers, memory, stack);
return 3;
}
callstack->push_back(position + 3);
return jump + 3;
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "bsuml.h"
#include "Emulation.h"
#include "SysCalls.h"
namespace bio {
@ -13,6 +14,10 @@ namespace bio {
isa_instruction_template(inc_16b_reg_template);
isa_instruction_template(dec_16b_reg_template);
isa_instruction_template(push_16b_reg_to_stack_template);
isa_instruction_template(pop_stack_to_16b_reg_template);
isa_instruction(invalid);
isa_instruction(add_01);
@ -47,6 +52,18 @@ namespace bio {
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[0x50] = isa_instruction_templater(ISAs::iAPX86::push_16b_reg_to_stack_template, "ax");
this->isa[0x51] = isa_instruction_templater(ISAs::iAPX86::push_16b_reg_to_stack_template, "cx");
this->isa[0x52] = isa_instruction_templater(ISAs::iAPX86::push_16b_reg_to_stack_template, "dx");
this->isa[0x53] = isa_instruction_templater(ISAs::iAPX86::push_16b_reg_to_stack_template, "bx");
this->isa[0x58] = isa_instruction_templater(ISAs::iAPX86::pop_stack_to_16b_reg_template, "ax");
this->isa[0x59] = isa_instruction_templater(ISAs::iAPX86::pop_stack_to_16b_reg_template, "cx");
this->isa[0x5A] = isa_instruction_templater(ISAs::iAPX86::pop_stack_to_16b_reg_template, "dx");
this->isa[0x5B] = isa_instruction_templater(ISAs::iAPX86::pop_stack_to_16b_reg_template, "bx");
this->isa[0x83] = ISAs::iAPX86::grp1_83;
this->isa[0x90] = ISAs::iAPX86::nop_90;
@ -61,6 +78,10 @@ namespace bio {
times(sizeof(this->memory)) {
this->memory[___i] = '\0';
}
this->syscalls[4080] = bio::emu::syscalls::print;
}
void load_app(bio::emu::application& app) {

54
biosandbox/SysCalls.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "SysCalls.h"
#include <iostream>
#include <string>
syscall_definition(bio::emu::syscalls::print) {
std::string format = (const char*)(memory + (*registers)["ax"]);
std::string output;
bool inPercentage = false;
unsigned char currentArgument = 1;
for (char& c : format) {
if (inPercentage) {
switch (c) {
case 'i':
{
short s = (*stack)[stack->size() - (1 + currentArgument)];
output += std::to_string((short)(s));
inPercentage = false;
currentArgument++;
}
break;
case 's':
{
std::string s = (const char*)(memory + (*stack)[stack->size() - (1 + currentArgument)]);
output += s;
inPercentage = false;
currentArgument++;
}
break;
case '%':
output += '%';
inPercentage = false;
break;
}
}
else {
if (c == '%') inPercentage = true;
else {
output += c;
}
}
}
std::print("{}", output);
format.clear();
output.clear();
}

11
biosandbox/SysCalls.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "bsuml.h"
#include "Emulation.h"
namespace bio {
namespace emu {
namespace syscalls {
syscall_definition(print);
}
}
}

View File

@ -139,6 +139,7 @@
<ClCompile Include="Intel.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Network.cpp" />
<ClCompile Include="SysCalls.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="BinFile.h" />
@ -148,6 +149,7 @@
<ClInclude Include="Network.h" />
<ClInclude Include="Object.h" />
<ClInclude Include="Playables.h" />
<ClInclude Include="SysCalls.h" />
<ClInclude Include="TestFramework.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -36,6 +36,9 @@
<ClCompile Include="BinFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SysCalls.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="bsuml.h">
@ -62,5 +65,8 @@
<ClInclude Include="TestFramework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SysCalls.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -23,12 +23,15 @@ 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
unsigned long long>> registers,\
::std::vector<int>* stack, \
::bio::emu::syscall_set* syscalls)
// position = current instruction pointer position
// memory = memory buffer
// emureturn = end of symbol
// callstack = callstack pointer
// registers = registers pointer
// stack = stack pointer
#define isa_instruction_template(x) int x(int position, \
unsigned char* memory, \
@ -36,6 +39,8 @@ bool* emu_return, \
ptr<::std::vector<int>> callstack, \
ptr<::std::map<::std::string, \
unsigned long long>> registers, \
::std::vector<int>* stack, \
::bio::emu::syscall_set* syscalls, \
::std::string args)
// position = current instruction pointer position
// memory = memory buffer
@ -45,10 +50,18 @@ unsigned long long>> registers, \
// 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);\
[](int p, \
unsigned char* m, \
bool* er, \
::std::vector<int>* cs, \
::std::map<::std::string,unsigned long long>* r, \
::std::vector<int>* st,\
::bio::emu::syscall_set* sc) -> int{\
return x(p, m, er, cs, r, st, sc, args);\
}
#define syscall_definition(x) void x(ptr<::bio::emu::register_set> registers, ptr<unsigned char> memory, ptr<::std::vector<int>> stack)
#define memory_dependent template<int memsize>
#define memory_passdown(x) x<memsize>

View File

@ -7,7 +7,7 @@
bio::tests::test iapxTest{
"iAPX Test Suite",
[]() -> bool {
ptr<bio::Intel::iAPX86<1024>> i8086 = new bio::Intel::iAPX86<1024>;
ptr<bio::Intel::iAPX86<SHRT_MAX>> i8086 = new bio::Intel::iAPX86<SHRT_MAX>;
std::filesystem::directory_iterator it("code/tests/bin");

Binary file not shown.

View File

@ -0,0 +1,4 @@
@echo off
nasm test.asm -o "bin/test"
nasm test2.asm -o "bin/test2"
nasm test3.asm -o "bin/test3"

View File

@ -0,0 +1,25 @@
section .text:
main:
mov ax, world
push ax
mov dx, problem
push dx
pop cx
mov ax, hello
push ax
mov ax, trash
push ax
pop bx
mov ax, format
push ax
call 0x0ff0 ; printf
pop bx
pop bx
pop bx
ret
section .data:
world: db "world", 0
hello: db "hello", 0
trash: db "garbage", 0
problem: db "problem", 0
format: db "%s, %s!", 10, 0

View File

@ -13,4 +13,6 @@ All features the 8086 emulator of the engine supports are demonstrated in the [t
- 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
- call functions and return
- create engine syscalls (``0x0ff0`` acts as a printf variant)
- push and pop from a stack