fox32/src/cpu.c

1319 lines
52 KiB
C
Raw Normal View History

2022-10-23 03:00:51 +02:00
#include <stdio.h>
#include <stdnoreturn.h>
#include <string.h>
#include <setjmp.h>
#include <stdlib.h>
2022-10-23 03:00:51 +02:00
#include "cpu.h"
2022-10-26 07:44:18 +02:00
#include "mmu.h"
2022-10-23 03:00:51 +02:00
typedef fox32_err_t err_t;
static const char *const err_messages[] = {
"",
"internal error",
"breakpoint reached",
"fault while reading memory",
"fault while writing memory",
2022-10-23 03:00:51 +02:00
"invalid opcode",
"invalid condition",
"invalid register",
"write to immediate",
"division by zero",
"io read failed",
"io write failed",
"interrupts disabled",
"error is not recoverable"
};
static const char *err_tostring(err_t err) {
if (err > 0 && err <= FOX32_ERR_CANTRECOVER) {
return err_messages[err];
}
return err_messages[FOX32_ERR_OK];
}
typedef fox32_io_read_t io_read_t;
typedef fox32_io_write_t io_write_t;
static int io_read_default_impl(void *user, uint32_t *value, uint32_t port) {
return (void) user, (void) value, (int) port;
2022-10-23 03:00:51 +02:00
}
static int io_write_default_impl(void *user, uint32_t value, uint32_t port) {
if (port == 0) {
putchar((int) value);
fflush(stdout);
}
return (void) user, (int) port;
2022-10-23 03:00:51 +02:00
}
static io_read_t *const io_read_default = io_read_default_impl;
static io_write_t *const io_write_default = io_write_default_impl;
enum {
OP_NOP = 0x00,
OP_ADD = 0x01,
OP_MUL = 0x02,
OP_AND = 0x03,
OP_SLA = 0x04,
OP_SRA = 0x05,
OP_BSE = 0x06,
OP_CMP = 0x07,
OP_JMP = 0x08,
OP_RJMP = 0x09,
OP_PUSH = 0x0A,
OP_IN = 0x0B,
OP_ISE = 0x0C,
2022-10-26 07:44:18 +02:00
OP_MSE = 0x0D,
2022-10-23 03:00:51 +02:00
OP_HALT = 0x10,
OP_INC = 0x11,
OP_OR = 0x13,
OP_IMUL = 0x14,
2022-10-23 03:00:51 +02:00
OP_SRL = 0x15,
OP_BCL = 0x16,
OP_MOV = 0x17,
OP_CALL = 0x18,
OP_RCALL = 0x19,
OP_POP = 0x1A,
OP_OUT = 0x1B,
OP_ICL = 0x1C,
2022-10-26 07:44:18 +02:00
OP_MCL = 0x1D,
2022-10-23 03:00:51 +02:00
OP_BRK = 0x20,
OP_SUB = 0x21,
OP_DIV = 0x22,
OP_XOR = 0x23,
OP_ROL = 0x24,
OP_ROR = 0x25,
OP_BTS = 0x26,
OP_MOVZ = 0x27,
OP_LOOP = 0x28,
OP_RLOOP = 0x29,
OP_RET = 0x2A,
OP_INT = 0x2C,
2022-10-26 07:44:18 +02:00
OP_TLB = 0x2D,
2022-10-23 03:00:51 +02:00
OP_DEC = 0x31,
OP_REM = 0x32,
OP_NOT = 0x33,
OP_IDIV = 0x34,
OP_IREM = 0x35,
2022-10-23 03:00:51 +02:00
OP_RTA = 0x39,
2022-10-26 07:44:18 +02:00
OP_RETI = 0x3A,
OP_FLP = 0x3D,
2022-10-23 03:00:51 +02:00
};
enum {
SZ_BYTE,
SZ_HALF,
SZ_WORD
};
#define OP(_size, _optype) (((uint8_t) (_optype)) | (((uint8_t) (_size)) << 6))
enum {
CD_ALWAYS,
CD_IFZ,
CD_IFNZ,
CD_IFC,
CD_IFNC,
CD_IFGT,
CD_IFLTEQ
};
enum {
TY_REG,
TY_REGPTR,
TY_IMM,
TY_IMMPTR,
TY_NONE
};
enum {
EX_DIVZERO = 256 + 0x00,
EX_ILLEGAL = 256 + 0x01,
EX_FAULT_RD = 256 + 0x02,
EX_FAULT_WR = 256 + 0x03,
EX_DEBUGGER = 256 + 0x04,
EX_BUS = 256 + 0x05
2022-10-23 03:00:51 +02:00
};
#define SIZE8 1
#define SIZE16 2
#define SIZE32 4
static uint8_t ptr_get8(const void *ptr) {
return *((const uint8_t *) ptr);
}
static uint16_t ptr_get16(const void *ptr) {
const uint8_t *bytes = ptr;
return
(((uint16_t) bytes[0])) |
(((uint16_t) bytes[1]) << 8);
}
static uint32_t ptr_get32(const void *ptr) {
const uint8_t *bytes = ptr;
return
(((uint32_t) bytes[0])) |
(((uint32_t) bytes[1]) << 8) |
(((uint32_t) bytes[2]) << 16) |
(((uint32_t) bytes[3]) << 24);
}
static void ptr_set8(void *ptr, uint8_t value) {
*((uint8_t *) ptr) = value;
}
static void ptr_set16(void *ptr, uint16_t value) {
uint8_t *bytes = ptr;
bytes[0] = (uint8_t) (value);
bytes[1] = (uint8_t) (value >> 8);
}
static void ptr_set32(void *ptr, uint32_t value) {
uint8_t *bytes = ptr;
bytes[0] = (uint8_t) (value);
bytes[1] = (uint8_t) (value >> 8);
bytes[2] = (uint8_t) (value >> 16);
bytes[3] = (uint8_t) (value >> 24);
}
typedef struct {
uint8_t opcode;
uint8_t condition;
uint8_t offset;
2022-10-23 03:00:51 +02:00
uint8_t target;
uint8_t source;
uint8_t size;
} asm_instr_t;
static asm_instr_t asm_instr_from(uint16_t half) {
asm_instr_t instr = {
(half >> 8),
(half >> 4) & 7,
(half >> 7) & 1,
2022-10-23 03:00:51 +02:00
(half >> 2) & 3,
(half ) & 3,
(half >> 14)
};
return instr;
}
typedef struct {
const char *const name;
const unsigned int prcount;
} asm_iinfo_t;
static const asm_iinfo_t asm_iinfo_unknown = { "ERROR", 0 };
static const asm_iinfo_t asm_iinfos[256] = {
[OP_NOP ] = { "NOP ", 0 },
[OP_ADD ] = { "ADD ", 2 },
[OP_MUL ] = { "MUL ", 2 },
[OP_AND ] = { "AND ", 2 },
[OP_SLA ] = { "SLA ", 2 },
[OP_SRA ] = { "SRA ", 2 },
[OP_BSE ] = { "BSE ", 2 },
[OP_CMP ] = { "CMP ", 2 },
[OP_JMP ] = { "JMP ", 1 },
[OP_RJMP ] = { "RJMP ", 1 },
[OP_PUSH ] = { "PUSH ", 1 },
[OP_IN ] = { "IN ", 2 },
[OP_ISE ] = { "ISE ", 0 },
2022-10-26 07:44:18 +02:00
[OP_MSE ] = { "MSE ", 0 },
2022-10-23 03:00:51 +02:00
[OP_HALT ] = { "HALT ", 0 },
[OP_INC ] = { "INC ", 1 },
[OP_OR ] = { "OR ", 2 },
[OP_IMUL ] = { "IMUL ", 2 },
2022-10-23 03:00:51 +02:00
[OP_SRL ] = { "SRL ", 2 },
[OP_BCL ] = { "BCL ", 2 },
[OP_MOV ] = { "MOV ", 2 },
[OP_CALL ] = { "CALL ", 1 },
[OP_RCALL] = { "RCALL", 1 },
[OP_POP ] = { "POP ", 1 },
[OP_OUT ] = { "OUT ", 2 },
[OP_ICL ] = { "ICL ", 0 },
2022-10-26 07:44:18 +02:00
[OP_MCL ] = { "MCL ", 0 },
2022-10-23 03:00:51 +02:00
[OP_BRK ] = { "BRK ", 0 },
[OP_SUB ] = { "SUB ", 2 },
[OP_DIV ] = { "DIV ", 2 },
[OP_XOR ] = { "XOR ", 2 },
[OP_ROL ] = { "ROL ", 2 },
[OP_ROR ] = { "ROR ", 2 },
[OP_BTS ] = { "BTS ", 2 },
[OP_MOVZ ] = { "MOVZ ", 2 },
[OP_LOOP ] = { "LOOP ", 1 },
[OP_RLOOP] = { "RLOOP", 1 },
[OP_RET ] = { "RET ", 0 },
[OP_INT ] = { "INT ", 1 },
2022-10-26 07:44:18 +02:00
[OP_TLB ] = { "TLB ", 1 },
2022-10-23 03:00:51 +02:00
[OP_DEC ] = { "DEC ", 1 },
[OP_REM ] = { "REM ", 2 },
[OP_NOT ] = { "NOT ", 1 },
[OP_IDIV ] = { "IDIV ", 2 },
[OP_IREM ] = { "IREM ", 2 },
2022-10-23 03:00:51 +02:00
[OP_RTA ] = { "RTA ", 2 },
2022-10-26 07:44:18 +02:00
[OP_RETI ] = { "RETI ", 0 },
[OP_FLP ] = { "FLP ", 1 }
2022-10-23 03:00:51 +02:00
};
static const asm_iinfo_t *asm_iinfo_get(uint8_t opcode) {
const asm_iinfo_t *iinfo = &asm_iinfos[opcode & 0x3F];
if (iinfo->name == NULL) {
iinfo = &asm_iinfo_unknown;
}
return iinfo;
}
static uint32_t asm_disas_prsize(asm_instr_t instr, uint8_t prtype) {
if (prtype < TY_IMM) return SIZE8;
if (prtype < TY_IMMPTR) return SIZE32;
switch (instr.size) {
case SZ_BYTE: return SIZE8;
case SZ_HALF: return SIZE16;
case SZ_WORD: return SIZE32;
}
return 0;
}
static uint32_t asm_disas_paramssize(asm_instr_t instr, const asm_iinfo_t *iinfo) {
uint32_t size = 0;
if (iinfo->prcount > 0) size += asm_disas_prsize(instr, instr.source);
if (iinfo->prcount > 1) size += asm_disas_prsize(instr, instr.target);
return size;
}
static const char *const asm_disas_strssize[4] = {
"BYTE",
"HALF",
"WORD",
"????"
};
static const char *const asm_disas_strscondition[8] = {
" ",
"IFZ ",
"IFNZ ",
"IFC ",
"IFNC ",
"IFGT ",
"IFLTEQ",
"??????"
};
static const char *const asm_disas_strslocal[36] = {
2022-10-23 03:00:51 +02:00
"R0 ", "R1 ", "R2 ", "R3 ", "R4 ", "R5 ", "R6 ", "R7 ",
"R8 ", "R9 ", "R10", "R11", "R12", "R13", "R14", "R15",
"R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
"R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31",
"RSP", "ESP", "RFP", "???"
2022-10-23 03:00:51 +02:00
};
static void asm_disas_printparam(asm_instr_t instr, const uint8_t **paramsdata, char **buffer, uint8_t prtype) {
int count = 0;
if (prtype < TY_IMM) {
uint8_t local = ptr_get8(*paramsdata);
*paramsdata += SIZE8;
const char* str_local = asm_disas_strslocal[35];
if (local < 35) {
2022-10-23 03:00:51 +02:00
str_local = asm_disas_strslocal[local];
}
count = sprintf(*buffer, "%s %s ", prtype == TY_REG ? "REG " : "REGPTR", str_local);
} else {
uint32_t value = 0;
switch (instr.size) {
case SZ_BYTE: value = (uint32_t) ptr_get8 (*paramsdata); *paramsdata += SIZE8 ; break;
case SZ_HALF: value = (uint32_t) ptr_get16(*paramsdata); *paramsdata += SIZE16; break;
case SZ_WORD: value = ptr_get32(*paramsdata); *paramsdata += SIZE32; break;
}
count = sprintf(*buffer, "%s %08X", prtype == TY_IMM ? "IMM " : "IMMPTR", value);
2022-10-23 03:00:51 +02:00
}
if (count > 0) {
*buffer += (size_t) count;
}
}
static void asm_disas_print(asm_instr_t instr, const asm_iinfo_t *iinfo, const uint8_t *paramsdata, char *buffer) {
const char *str_size = asm_disas_strssize[3];
if (instr.size < 3) {
str_size = asm_disas_strssize[instr.size];
}
const char *str_condition = asm_disas_strscondition[7];
if (instr.condition < 8) {
str_condition = asm_disas_strscondition[instr.condition];
}
int count = sprintf(buffer, "%s %s %s", str_condition, str_size, iinfo->name);
if (count > 0) {
buffer += (size_t) count;
}
if (iinfo->prcount > 0) {
*buffer++ = ' ';
asm_disas_printparam(instr, &paramsdata, &buffer, instr.source);
}
if (iinfo->prcount > 1) {
*buffer++ = ',';
*buffer++ = ' ';
asm_disas_printparam(instr, &paramsdata, &buffer, instr.target);
}
}
typedef fox32_vm_t vm_t;
static void vm_init(vm_t *vm) {
memset(vm, 0, sizeof(vm_t));
vm->pointer_instr = FOX32_POINTER_DEFAULT_INSTR;
vm->pointer_stack = FOX32_POINTER_DEFAULT_STACK;
vm->halted = true;
vm->soft_halted = false;
2022-10-26 07:44:18 +02:00
vm->mmu_enabled = false;
2022-10-23 03:00:51 +02:00
vm->io_user = NULL;
vm->io_read = io_read_default;
vm->io_write = io_write_default;
}
static noreturn void vm_panic(vm_t *vm, err_t err) {
longjmp(vm->panic_jmp, (vm->panic_err = err, 1));
}
static noreturn void vm_unreachable(vm_t *vm) {
vm_panic(vm, FOX32_ERR_INTERNAL);
}
static uint32_t vm_io_read(vm_t *vm, uint32_t port) {
uint32_t value = 0;
int status = vm->io_read(vm->io_user, &value, port);
if (status != 0) {
vm_panic(vm, FOX32_ERR_IOREAD);
}
return value;
}
static void vm_io_write(vm_t *vm, uint32_t port, uint32_t value) {
int status = vm->io_write(vm->io_user, value, port);
if (status != 0) {
vm_panic(vm, FOX32_ERR_IOWRITE);
}
}
static uint8_t vm_flags_get(vm_t *vm) {
2022-10-26 07:44:18 +02:00
return (((uint8_t) vm->flag_swap_sp) << 3) |
(((uint8_t) vm->flag_interrupt) << 2) |
(((uint8_t) vm->flag_carry) << 1) |
((uint8_t) vm->flag_zero);
2022-10-23 03:00:51 +02:00
}
static void vm_flags_set(vm_t *vm, uint8_t flags) {
vm->flag_zero = (flags & 1) != 0;
vm->flag_carry = (flags & 2) != 0;
vm->flag_interrupt = (flags & 4) != 0;
2022-10-26 07:44:18 +02:00
vm->flag_swap_sp = (flags & 8) != 0;
2022-10-23 03:00:51 +02:00
}
static uint32_t *vm_findlocal(vm_t *vm, uint8_t local) {
if (local < FOX32_REGISTER_COUNT) {
return &vm->registers[local];
}
if (local == FOX32_REGISTER_COUNT) {
return &vm->pointer_stack;
}
if (local == FOX32_REGISTER_COUNT + 1) {
return &vm->pointer_exception_stack;
}
if (local == FOX32_REGISTER_COUNT + 2) {
return &vm->pointer_frame;
}
2022-10-23 03:00:51 +02:00
vm_panic(vm, FOX32_ERR_BADREGISTER);
}
static uint8_t *vm_findmemory_phys(vm_t *vm, uint32_t address, uint32_t size, bool write) {
2022-10-23 03:00:51 +02:00
uint32_t address_end = address + size;
if (address_end > address) {
if (address_end <= FOX32_MEMORY_RAM) {
return &vm->memory_ram[address];
2022-10-23 03:00:51 +02:00
}
if (
!write &&
(address >= FOX32_MEMORY_ROM_START) &&
(address -= FOX32_MEMORY_ROM_START) + size <= FOX32_MEMORY_ROM
) {
return &vm->memory_rom[address];
2022-10-26 07:44:18 +02:00
}
}
if (!write) {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_RD);
} else {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_WR);
}
}
static uint8_t *vm_findmemory(vm_t *vm, uint32_t address, uint32_t size, bool write) {
if (!vm->mmu_enabled) {
return vm_findmemory_phys(vm, address, size, write);
2022-10-26 07:44:18 +02:00
} else {
mmu_page_t *virtual_page = get_present_page(address);
if (virtual_page == NULL) {
if (!write) {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_RD);
} else {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_WR);
}
}
if (!virtual_page->rw && write) {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_WR);
}
2022-10-26 07:44:18 +02:00
uint32_t offset = address & 0x00000FFF;
uint32_t physical_address = virtual_page->physical_address | offset;
return vm_findmemory_phys(vm, physical_address, size, write);
2022-10-23 03:00:51 +02:00
}
}
static uint32_t vm_read_across(vm_t *vm, uint32_t address, int size) {
uint32_t result = 0;
int shift = 0;
// read the first page
int bytes = 0x1000 - (address&0xFFF);
uint8_t *ptr = vm_findmemory(vm, address, bytes, false);
while (bytes) {
result |= (*ptr<<shift);
shift += 8;
ptr++;
bytes--;
}
// read the second page
bytes = (address+size)&0xFFF;
address = (address+size)&0xFFFFF000;
ptr = vm_findmemory(vm, address, bytes, false);
while (bytes) {
result |= (*ptr<<shift);
shift += 8;
ptr++;
bytes--;
}
return result;
}
void vm_write_across(vm_t *vm, uint32_t address, int size, uint32_t value) {
// calculate number of bytes for each page
int bytes_first = 0x1000 - (address & 0x00000FFF);
int address_second = (address + size) & 0xFFFFF000;
int bytes_second = (address + size) & 0x00000FFF;
// make sure both pages are resident before doing any writing
uint8_t *ptr_first = vm_findmemory(vm, address, bytes_first, true);
uint8_t *ptr_second = vm_findmemory(vm, address_second, bytes_second, true);
// write the first page
while (bytes_first) {
*ptr_first = value & 0xFF;
value >>= 8;
ptr_first++;
bytes_first--;
}
// write the second page
while (bytes_second) {
*ptr_second = value & 0xFF;
value >>= 8;
ptr_second++;
bytes_second--;
}
}
2022-10-23 03:00:51 +02:00
#define VM_READ_BODY(_ptr_get, _size) \
2022-10-27 02:00:56 +02:00
if ((address & 0xFFFFF000) == ((address + _size - 1) & 0xFFFFF000)) { \
return _ptr_get(vm_findmemory(vm, address, _size, false)); \
} else { \
return vm_read_across(vm, address, _size); \
}
2022-10-23 03:00:51 +02:00
static uint8_t vm_read8(vm_t *vm, uint32_t address) {
VM_READ_BODY(ptr_get8, SIZE8)
}
static uint16_t vm_read16(vm_t *vm, uint32_t address) {
VM_READ_BODY(ptr_get16, SIZE16)
}
static uint32_t vm_read32(vm_t *vm, uint32_t address) {
VM_READ_BODY(ptr_get32, SIZE32)
}
#define VM_WRITE_BODY(_ptr_set, _size) \
2022-10-27 02:00:56 +02:00
if ((address & 0xFFFFF000) == ((address + _size - 1) & 0xFFFFF000)) { \
return _ptr_set(vm_findmemory(vm, address, _size, true), value); \
} else { \
return vm_write_across(vm, address, _size, value); \
}
2022-10-23 03:00:51 +02:00
static void vm_write8(vm_t *vm, uint32_t address, uint8_t value) {
VM_WRITE_BODY(ptr_set8, SIZE8)
}
static void vm_write16(vm_t *vm, uint32_t address, uint16_t value) {
VM_WRITE_BODY(ptr_set16, SIZE16)
}
static void vm_write32(vm_t *vm, uint32_t address, uint32_t value) {
VM_WRITE_BODY(ptr_set32, SIZE32)
}
#define VM_PUSH_BODY(_vm_write, _size) \
_vm_write(vm, vm->pointer_stack - _size, value); \
vm->pointer_stack -= _size;
2022-10-23 03:00:51 +02:00
static void vm_push8(vm_t *vm, uint8_t value) {
VM_PUSH_BODY(vm_write8, SIZE8)
}
static void vm_push16(vm_t *vm, uint16_t value) {
VM_PUSH_BODY(vm_write16, SIZE16)
}
static void vm_push32(vm_t *vm, uint32_t value) {
VM_PUSH_BODY(vm_write32, SIZE32)
}
#define VM_POP_BODY(_vm_read, _size) \
uint32_t result = _vm_read(vm, vm->pointer_stack); \
vm->pointer_stack += _size; \
return result;
2022-10-23 03:00:51 +02:00
static uint8_t vm_pop8(vm_t *vm) {
VM_POP_BODY(vm_read8, SIZE8)
}
static uint16_t vm_pop16(vm_t *vm) {
VM_POP_BODY(vm_read16, SIZE16)
}
static uint32_t vm_pop32(vm_t *vm) {
VM_POP_BODY(vm_read32, SIZE32)
}
#define VM_SOURCE_BODY(_vm_read, _size, _type, _move, _offset) \
2022-10-23 03:00:51 +02:00
uint32_t pointer_base = vm->pointer_instr_mut; \
switch (prtype) { \
case TY_REG: { \
if (_move) vm->pointer_instr_mut += SIZE8; \
return (_type) *vm_findlocal(vm, vm_read8(vm, pointer_base)); \
}; \
case TY_REGPTR: { \
if (_move) vm->pointer_instr_mut += SIZE8+_offset; \
return _vm_read(vm, *vm_findlocal(vm, vm_read8(vm, pointer_base)) \
+(_offset ? vm_read8(vm, pointer_base + 1) : 0)); \
2022-10-23 03:00:51 +02:00
}; \
case TY_IMM: { \
if (_move) vm->pointer_instr_mut += _size; \
return _vm_read(vm, pointer_base); \
}; \
case TY_IMMPTR: { \
if (_move) vm->pointer_instr_mut += SIZE32; \
return _vm_read(vm, vm_read32(vm, pointer_base)); \
}; \
} \
vm_unreachable(vm);
static uint8_t vm_source8(vm_t *vm, uint8_t prtype, uint8_t offset) {
VM_SOURCE_BODY(vm_read8, SIZE8, uint8_t, true, offset)
2022-10-23 03:00:51 +02:00
}
static uint8_t vm_source8_stay(vm_t *vm, uint8_t prtype, uint8_t offset) {
VM_SOURCE_BODY(vm_read8, SIZE8, uint8_t, false, offset)
2022-10-23 03:00:51 +02:00
}
static uint16_t vm_source16(vm_t *vm, uint8_t prtype, uint8_t offset) {
VM_SOURCE_BODY(vm_read16, SIZE16, uint16_t, true, offset)
2022-10-23 03:00:51 +02:00
}
static uint16_t vm_source16_stay(vm_t *vm, uint8_t prtype, uint8_t offset) {
VM_SOURCE_BODY(vm_read16, SIZE16, uint16_t, false, offset)
2022-10-23 03:00:51 +02:00
}
static uint32_t vm_source32(vm_t *vm, uint8_t prtype, uint8_t offset) {
VM_SOURCE_BODY(vm_read32, SIZE32, uint32_t, true, offset)
2022-10-23 03:00:51 +02:00
}
static uint32_t vm_source32_stay(vm_t *vm, uint8_t prtype, uint8_t offset) {
VM_SOURCE_BODY(vm_read32, SIZE32, uint32_t, false, offset)
2022-10-23 03:00:51 +02:00
}
#define VM_TARGET_BODY(_vm_write, _localvalue, _offset) \
2022-10-23 03:00:51 +02:00
uint32_t pointer_base = vm->pointer_instr_mut; \
switch (prtype) { \
case TY_REG: { \
vm->pointer_instr_mut += SIZE8; \
uint8_t local = vm_read8(vm, pointer_base); \
*vm_findlocal(vm, local) = _localvalue; \
return; \
}; \
case TY_REGPTR: { \
vm->pointer_instr_mut += SIZE8+_offset; \
_vm_write(vm, ( _offset ? vm_read8(vm, pointer_base + 1) : 0) + \
*vm_findlocal(vm, vm_read8(vm, pointer_base)), value); \
2022-10-23 03:00:51 +02:00
return; \
}; \
case TY_IMM: { \
vm_panic(vm, FOX32_ERR_BADIMMEDIATE); \
return; \
}; \
case TY_IMMPTR: { \
vm->pointer_instr_mut += SIZE32; \
_vm_write(vm, vm_read32(vm, pointer_base), value); \
return; \
}; \
}; \
vm_unreachable(vm);
static void vm_target8(vm_t *vm, uint8_t prtype, uint8_t value, uint8_t offset) {
VM_TARGET_BODY(vm_write8, (*vm_findlocal(vm, local) & 0xFFFFFF00) | (uint32_t) value, offset)
2022-10-23 03:00:51 +02:00
}
static void vm_target8_zero(vm_t *vm, uint8_t prtype, uint8_t value, uint8_t offset) {
VM_TARGET_BODY(vm_write8, (uint32_t) value, offset)
2022-10-23 03:00:51 +02:00
}
static void vm_target16(vm_t *vm, uint8_t prtype, uint16_t value, uint8_t offset) {
VM_TARGET_BODY(vm_write16, (*vm_findlocal(vm, local) & 0xFFFF0000) | (uint32_t) value, offset)
2022-10-23 03:00:51 +02:00
}
static void vm_target16_zero(vm_t *vm, uint8_t prtype, uint16_t value, uint8_t offset) {
VM_TARGET_BODY(vm_write16, (uint32_t) value, offset)
2022-10-23 03:00:51 +02:00
}
static void vm_target32(vm_t *vm, uint8_t prtype, uint32_t value, uint8_t offset) {
VM_TARGET_BODY(vm_write32, value, offset)
2022-10-23 03:00:51 +02:00
}
static bool vm_shouldskip(vm_t *vm, uint8_t condition) {
switch (condition) {
case CD_ALWAYS: {
return false;
};
case CD_IFZ: {
return vm->flag_zero == false;
};
case CD_IFNZ: {
return vm->flag_zero == true;
};
case CD_IFC: {
return vm->flag_carry == false;
};
case CD_IFNC: {
return vm->flag_carry == true;
};
case CD_IFGT: {
return (vm->flag_zero == true) || (vm->flag_carry == true);
};
case CD_IFLTEQ: {
return (vm->flag_zero == false) && (vm->flag_carry == false);
};
}
vm_panic(vm, FOX32_ERR_BADCONDITION);
}
static void vm_skipparam(vm_t *vm, uint32_t size, uint8_t prtype, uint8_t offset) {
2022-10-23 03:00:51 +02:00
if (prtype < TY_IMM) {
vm->pointer_instr_mut += SIZE8;
if (offset && prtype==TY_REGPTR)
vm->pointer_instr_mut += SIZE8;
2022-10-23 03:00:51 +02:00
} else if (prtype == TY_IMMPTR) {
vm->pointer_instr_mut += SIZE32;
} else {
vm->pointer_instr_mut += size;
}
}
#define CHECKED_ADD(_a, _b, _out) __builtin_add_overflow(_a, _b, _out)
#define CHECKED_SUB(_a, _b, _out) __builtin_sub_overflow(_a, _b, _out)
#define CHECKED_MUL(_a, _b, _out) __builtin_mul_overflow(_a, _b, _out)
#define OPER_DIV(_a, _b) ((_a) / (_b))
#define OPER_REM(_a, _b) ((_a) % (_b))
#define OPER_AND(_a, _b) ((_a) & (_b))
#define OPER_XOR(_a, _b) ((_a) ^ (_b))
#define OPER_OR(_a, _b) ((_a) | (_b))
#define OPER_SHIFT_LEFT(_a, _b) ((_a) << (_b))
#define OPER_SHIFT_RIGHT(_a, _b) ((_a) >> (_b))
#define OPER_BIT_SET(_a, _b) ((_a) | (1 << (_b)))
#define OPER_BIT_CLEAR(_a, _b) ((_a) & ~(1 << (_b)))
#define ROTATE_LEFT(_size, _a, _b) (((_a) << (_b)) | ((_a) >> (((_size) * 8) - (_b))))
#define ROTATE_LEFT8(_a, _b) ROTATE_LEFT(SIZE8, _a, _b)
#define ROTATE_LEFT16(_a, _b) ROTATE_LEFT(SIZE16, _a, _b)
#define ROTATE_LEFT32(_a, _b) ROTATE_LEFT(SIZE32, _a, _b)
#define ROTATE_RIGHT(_size, _a, _b) (((_a) >> (_b)) | ((_a) << (((_size) * 8) - (_b))))
#define ROTATE_RIGHT8(_a, _b) ROTATE_RIGHT(SIZE8, _a, _b)
#define ROTATE_RIGHT16(_a, _b) ROTATE_RIGHT(SIZE16, _a, _b)
#define ROTATE_RIGHT32(_a, _b) ROTATE_RIGHT(SIZE32, _a, _b)
#define SOURCEMAP_IDENTITY(x) (x)
#define SOURCEMAP_RELATIVE(x) (instr_base + (x))
#define VM_PRELUDE_0() { \
if (vm_shouldskip(vm, instr.condition)) { \
break; \
} \
}
#define VM_PRELUDE_1(_size) { \
if (vm_shouldskip(vm, instr.condition)) { \
vm_skipparam(vm, _size, instr.source, instr.offset); \
break; \
} \
}
#define VM_PRELUDE_2(_size) { \
if (vm_shouldskip(vm, instr.condition)) { \
vm_skipparam(vm, _size, instr.target, instr.offset); \
vm_skipparam(vm, _size, instr.source, instr.offset); \
break; \
} \
}
#define VM_PRELUDE_BIT(_size) { \
if (vm_shouldskip(vm, instr.condition)) { \
vm_skipparam(vm, _size, instr.target, instr.offset); \
vm_skipparam(vm, SIZE8, instr.source, instr.offset); \
break; \
} \
}
#define VM_IMPL_JMP(_sourcemap) { \
VM_PRELUDE_1(SIZE32); \
vm->pointer_instr_mut = _sourcemap(vm_source32(vm, instr.source, instr.offset)); \
break; \
}
#define VM_IMPL_LOOP(_sourcemap) { \
if ( \
!vm_shouldskip(vm, instr.condition) && \
(vm->registers[FOX32_REGISTER_LOOP] -= 1) != 0 \
) { \
vm->pointer_instr_mut = _sourcemap(vm_source32(vm, instr.source, instr.offset)); \
} else { \
vm_skipparam(vm, SIZE32, instr.source, instr.offset); \
} \
break; \
}
#define VM_IMPL_CALL(_sourcemap) { \
VM_PRELUDE_1(SIZE32); \
uint32_t pointer_call = vm_source32(vm, instr.source, instr.offset); \
vm_push32(vm, vm->pointer_instr_mut); \
vm->pointer_instr_mut = _sourcemap(pointer_call); \
break; \
2022-10-23 03:00:51 +02:00
}
// make sure NOT to update the stack pointer until the full instruction has
// been read, and the target has been written. otherwise a pagefault halfway
// through could wreak havoc.
#define VM_IMPL_POP(_size, _vm_target, _vm_pop) { \
VM_PRELUDE_1(_size); \
uint32_t oldsp = vm->pointer_stack; \
uint32_t val = _vm_pop(vm); \
uint32_t newsp = vm->pointer_stack; \
vm->pointer_stack = oldsp; \
_vm_target(vm, instr.source, val, instr.offset); \
vm->pointer_stack = newsp; \
break; \
2022-10-23 03:00:51 +02:00
}
#define VM_IMPL_PUSH(_size, _vm_source, _vm_push) { \
VM_PRELUDE_1(_size); \
_vm_push(vm, _vm_source(vm, instr.source, instr.offset)); \
break; \
2022-10-23 03:00:51 +02:00
}
#define VM_IMPL_MOV(_size, _vm_source, _vm_target) { \
VM_PRELUDE_2(_size); \
_vm_target(vm, instr.target, _vm_source(vm, instr.source, instr.offset), instr.offset); \
break; \
2022-10-23 03:00:51 +02:00
}
#define VM_IMPL_NOT(_size, _type, _vm_source_stay, _vm_target) { \
VM_PRELUDE_1(_size); \
_type v = _vm_source_stay(vm, instr.source, instr.offset); \
2022-10-23 03:00:51 +02:00
_type x = ~v; \
_vm_target(vm, instr.source, x, instr.offset); \
vm->flag_zero = x == 0; \
2022-10-23 03:00:51 +02:00
break; \
}
#define VM_IMPL_INC(_size, _type, _vm_source_stay, _vm_target, _oper) { \
VM_PRELUDE_1(_size); \
_type v = _vm_source_stay(vm, instr.source, instr.offset); \
2022-10-23 03:00:51 +02:00
_type x; \
bool carry = _oper(v, 1 << instr.target, &x); \
_vm_target(vm, instr.source, x, instr.offset); \
vm->flag_carry = carry; \
vm->flag_zero = x == 0; \
2022-10-23 03:00:51 +02:00
break; \
}
#define VM_IMPL_ADD(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper) { \
VM_PRELUDE_2(_size); \
_type a = (_type) _vm_source(vm, instr.source, instr.offset); \
_type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \
2022-10-23 03:00:51 +02:00
_type x; \
bool carry = _oper(b, a, &x); \
_vm_target(vm, instr.target, (_type_target) x, instr.offset); \
vm->flag_carry = carry; \
vm->flag_zero = x == 0; \
2022-10-23 03:00:51 +02:00
break; \
}
#define VM_IMPL_AND(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper) { \
VM_PRELUDE_2(_size); \
_type a = (_type) _vm_source(vm, instr.source, instr.offset); \
_type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \
_type x = _oper(b, a); \
_vm_target(vm, instr.target, (_type_target) x, instr.offset); \
vm->flag_zero = x == 0; \
break; \
}
#define VM_IMPL_SHIFT(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper){\
VM_PRELUDE_BIT(_size); \
_type a = (_type) vm_source8(vm, instr.source, instr.offset); \
_type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \
2022-10-23 03:00:51 +02:00
_type x = _oper(b, a); \
_vm_target(vm, instr.target, (_type_target) x, instr.offset); \
vm->flag_zero = x == 0; \
2022-10-23 03:00:51 +02:00
break; \
}
#define VM_IMPL_DIV(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper) { \
VM_PRELUDE_2(_size); \
_type a = (_type) _vm_source(vm, instr.source, instr.offset); \
_type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \
if (a == 0) { \
vm_panic(vm, FOX32_ERR_DIVZERO); \
break; \
} \
_type x = _oper(b, a); \
_vm_target(vm, instr.target, (_type_target) x, instr.offset); \
vm->flag_zero = x == 0; \
break; \
}
#define VM_IMPL_CMP(_size, _type, _vm_source) { \
VM_PRELUDE_2(_size); \
_type a = _vm_source(vm, instr.source, instr.offset); \
_type b = _vm_source(vm, instr.target, instr.offset); \
_type x; \
vm->flag_carry = CHECKED_SUB(b, a, &x); \
vm->flag_zero = x == 0; \
break; \
2022-10-23 03:00:51 +02:00
}
#define VM_IMPL_BTS(_size, _type, _vm_source) { \
VM_PRELUDE_BIT(_size); \
_type a = vm_source8(vm, instr.source, instr.offset); \
_type b = _vm_source(vm, instr.target, instr.offset); \
_type x = b & (1 << a); \
vm->flag_zero = x == 0; \
break; \
2022-10-23 03:00:51 +02:00
}
2023-01-27 01:38:01 +01:00
static void vm_debug(vm_t *vm, asm_instr_t instr, uint32_t ip, uint32_t sp) {
2022-10-23 03:00:51 +02:00
const asm_iinfo_t *iinfo = asm_iinfo_get(instr.opcode);
uint32_t params_size = asm_disas_paramssize(instr, iinfo);
uint8_t *params_data = NULL;
if (params_size > 0) {
2023-01-27 01:38:01 +01:00
params_data = vm_findmemory(vm, ip + SIZE16, params_size, false);
2022-10-23 03:00:51 +02:00
}
char buffer[128] = {};
asm_disas_print(instr, iinfo, params_data, buffer);
2023-01-27 01:38:01 +01:00
printf("SP=%08X IP=%08X %s\n", sp, ip, buffer);
2022-10-23 03:00:51 +02:00
}
static void vm_execute(vm_t *vm) {
uint32_t instr_base = vm->pointer_instr;
uint16_t instr_raw = vm_read16(vm, instr_base);
asm_instr_t instr = asm_instr_from(instr_raw);
vm->pointer_instr_mut = instr_base + SIZE16;
2023-01-27 01:38:01 +01:00
if (vm->debug) vm_debug(vm, instr, instr_base, vm->pointer_stack);
2022-10-23 03:00:51 +02:00
switch (instr.opcode) {
case OP(SZ_BYTE, OP_NOP):
case OP(SZ_HALF, OP_NOP):
case OP(SZ_WORD, OP_NOP): {
break;
};
case OP(SZ_BYTE, OP_HALT):
case OP(SZ_HALF, OP_HALT):
case OP(SZ_WORD, OP_HALT): {
VM_PRELUDE_0();
vm->soft_halted = true;
2022-10-23 03:00:51 +02:00
break;
};
case OP(SZ_BYTE, OP_BRK):
case OP(SZ_HALF, OP_BRK):
case OP(SZ_WORD, OP_BRK): {
VM_PRELUDE_0();
vm->pointer_instr = vm->pointer_instr_mut;
2022-10-23 03:00:51 +02:00
vm_panic(vm, FOX32_ERR_DEBUGGER);
break;
};
case OP(SZ_WORD, OP_IN): {
VM_PRELUDE_2(SIZE32);
vm_target32(vm, instr.target, vm_io_read(vm, vm_source32(vm, instr.source, 0)), instr.offset);
2022-10-23 03:00:51 +02:00
break;
};
case OP(SZ_WORD, OP_OUT): {
VM_PRELUDE_2(SIZE32);
uint32_t value = vm_source32(vm, instr.source, instr.offset);
uint32_t port = vm_source32(vm, instr.target, instr.offset);
2022-10-23 03:00:51 +02:00
vm_io_write(vm, port, value);
break;
};
case OP(SZ_WORD, OP_RTA): {
VM_PRELUDE_2(SIZE32);
vm_target32(vm, instr.target, instr_base + vm_source32(vm, instr.source, instr.offset), instr.offset);
2022-10-23 03:00:51 +02:00
break;
};
case OP(SZ_WORD, OP_RET): {
VM_PRELUDE_0();
vm->pointer_instr_mut = vm_pop32(vm);
break;
};
case OP(SZ_WORD, OP_RETI): {
VM_PRELUDE_0();
vm_flags_set(vm, vm_pop8(vm));
vm->pointer_instr_mut = vm_pop32(vm);
if (vm->flag_swap_sp) {
vm->pointer_stack = vm_pop32(vm);
}
2022-10-23 03:00:51 +02:00
break;
};
case OP(SZ_WORD, OP_ISE): {
VM_PRELUDE_0();
vm->flag_interrupt = true;
break;
};
case OP(SZ_WORD, OP_ICL): {
VM_PRELUDE_0();
vm->flag_interrupt = false;
break;
};
case OP(SZ_WORD, OP_JMP): VM_IMPL_JMP(SOURCEMAP_IDENTITY);
case OP(SZ_WORD, OP_CALL): VM_IMPL_CALL(SOURCEMAP_IDENTITY);
case OP(SZ_WORD, OP_LOOP): VM_IMPL_LOOP(SOURCEMAP_IDENTITY);
case OP(SZ_WORD, OP_RJMP): VM_IMPL_JMP(SOURCEMAP_RELATIVE);
case OP(SZ_WORD, OP_RCALL): VM_IMPL_CALL(SOURCEMAP_RELATIVE);
case OP(SZ_WORD, OP_RLOOP): VM_IMPL_LOOP(SOURCEMAP_RELATIVE);
case OP(SZ_BYTE, OP_POP): VM_IMPL_POP(SIZE8, vm_target8, vm_pop8);
case OP(SZ_HALF, OP_POP): VM_IMPL_POP(SIZE16, vm_target16, vm_pop16);
case OP(SZ_WORD, OP_POP): VM_IMPL_POP(SIZE32, vm_target32, vm_pop32);
case OP(SZ_BYTE, OP_PUSH): VM_IMPL_PUSH(SIZE8, vm_source8, vm_push8);
case OP(SZ_HALF, OP_PUSH): VM_IMPL_PUSH(SIZE16, vm_source16, vm_push16);
case OP(SZ_WORD, OP_PUSH): VM_IMPL_PUSH(SIZE32, vm_source32, vm_push32);
case OP(SZ_BYTE, OP_MOV): VM_IMPL_MOV(SIZE8, vm_source8, vm_target8);
case OP(SZ_BYTE, OP_MOVZ): VM_IMPL_MOV(SIZE8, vm_source8, vm_target8_zero);
case OP(SZ_HALF, OP_MOV): VM_IMPL_MOV(SIZE16, vm_source16, vm_target16);
case OP(SZ_HALF, OP_MOVZ): VM_IMPL_MOV(SIZE16, vm_source16, vm_target16_zero);
case OP(SZ_WORD, OP_MOV):
case OP(SZ_WORD, OP_MOVZ): VM_IMPL_MOV(SIZE32, vm_source32, vm_target32);
case OP(SZ_BYTE, OP_NOT): VM_IMPL_NOT(SIZE8, uint8_t, vm_source8_stay, vm_target8);
case OP(SZ_HALF, OP_NOT): VM_IMPL_NOT(SIZE16, uint16_t, vm_source16_stay, vm_target16);
case OP(SZ_WORD, OP_NOT): VM_IMPL_NOT(SIZE32, uint32_t, vm_source32_stay, vm_target32);
case OP(SZ_BYTE, OP_INC): VM_IMPL_INC(SIZE8, uint8_t, vm_source8_stay, vm_target8, CHECKED_ADD);
case OP(SZ_HALF, OP_INC): VM_IMPL_INC(SIZE16, uint16_t, vm_source16_stay, vm_target16, CHECKED_ADD);
case OP(SZ_WORD, OP_INC): VM_IMPL_INC(SIZE32, uint32_t, vm_source32_stay, vm_target32, CHECKED_ADD);
case OP(SZ_BYTE, OP_DEC): VM_IMPL_INC(SIZE8, uint8_t, vm_source8_stay, vm_target8, CHECKED_SUB);
case OP(SZ_HALF, OP_DEC): VM_IMPL_INC(SIZE16, uint16_t, vm_source16_stay, vm_target16, CHECKED_SUB);
case OP(SZ_WORD, OP_DEC): VM_IMPL_INC(SIZE32, uint32_t, vm_source32_stay, vm_target32, CHECKED_SUB);
case OP(SZ_BYTE, OP_ADD): VM_IMPL_ADD(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, CHECKED_ADD);
case OP(SZ_HALF, OP_ADD): VM_IMPL_ADD(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_ADD);
case OP(SZ_WORD, OP_ADD): VM_IMPL_ADD(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_ADD);
case OP(SZ_BYTE, OP_SUB): VM_IMPL_ADD(SIZE8, uint8_t, uint8_t ,vm_source8, vm_source8_stay, vm_target8, CHECKED_SUB);
case OP(SZ_HALF, OP_SUB): VM_IMPL_ADD(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_SUB);
case OP(SZ_WORD, OP_SUB): VM_IMPL_ADD(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_SUB);
case OP(SZ_BYTE, OP_MUL): VM_IMPL_ADD(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, CHECKED_MUL);
case OP(SZ_HALF, OP_MUL): VM_IMPL_ADD(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_MUL);
case OP(SZ_WORD, OP_MUL): VM_IMPL_ADD(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_MUL);
case OP(SZ_BYTE, OP_IMUL): VM_IMPL_ADD(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, CHECKED_MUL);
case OP(SZ_HALF, OP_IMUL): VM_IMPL_ADD(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_MUL);
case OP(SZ_WORD, OP_IMUL): VM_IMPL_ADD(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_MUL);
case OP(SZ_BYTE, OP_DIV): VM_IMPL_DIV(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_DIV);
case OP(SZ_HALF, OP_DIV): VM_IMPL_DIV(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_DIV);
case OP(SZ_WORD, OP_DIV): VM_IMPL_DIV(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_DIV);
case OP(SZ_BYTE, OP_REM): VM_IMPL_DIV(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_REM);
case OP(SZ_HALF, OP_REM): VM_IMPL_DIV(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_REM);
case OP(SZ_WORD, OP_REM): VM_IMPL_DIV(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_REM);
case OP(SZ_BYTE, OP_IDIV): VM_IMPL_DIV(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_DIV);
case OP(SZ_HALF, OP_IDIV): VM_IMPL_DIV(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_DIV);
case OP(SZ_WORD, OP_IDIV): VM_IMPL_DIV(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_DIV);
case OP(SZ_BYTE, OP_IREM): VM_IMPL_DIV(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_REM);
case OP(SZ_HALF, OP_IREM): VM_IMPL_DIV(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_REM);
case OP(SZ_WORD, OP_IREM): VM_IMPL_DIV(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_REM);
2022-10-23 03:00:51 +02:00
case OP(SZ_BYTE, OP_AND): VM_IMPL_AND(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_AND);
case OP(SZ_HALF, OP_AND): VM_IMPL_AND(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_AND);
case OP(SZ_WORD, OP_AND): VM_IMPL_AND(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_AND);
case OP(SZ_BYTE, OP_XOR): VM_IMPL_AND(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_XOR);
case OP(SZ_HALF, OP_XOR): VM_IMPL_AND(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_XOR);
case OP(SZ_WORD, OP_XOR): VM_IMPL_AND(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_XOR);
case OP(SZ_BYTE, OP_OR): VM_IMPL_AND(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_OR);
case OP(SZ_HALF, OP_OR): VM_IMPL_AND(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_OR);
case OP(SZ_WORD, OP_OR): VM_IMPL_AND(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_OR);
case OP(SZ_BYTE, OP_SLA): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_SHIFT_LEFT);
case OP(SZ_HALF, OP_SLA): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_SHIFT_LEFT);
case OP(SZ_WORD, OP_SLA): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_SHIFT_LEFT);
case OP(SZ_BYTE, OP_SRL): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_SHIFT_RIGHT);
case OP(SZ_HALF, OP_SRL): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_SHIFT_RIGHT);
case OP(SZ_WORD, OP_SRL): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_SHIFT_RIGHT);
case OP(SZ_BYTE, OP_SRA): VM_IMPL_SHIFT(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_SHIFT_RIGHT);
case OP(SZ_HALF, OP_SRA): VM_IMPL_SHIFT(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_SHIFT_RIGHT);
case OP(SZ_WORD, OP_SRA): VM_IMPL_SHIFT(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_SHIFT_RIGHT);
case OP(SZ_BYTE, OP_ROL): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, ROTATE_LEFT8);
case OP(SZ_HALF, OP_ROL): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, ROTATE_LEFT16);
case OP(SZ_WORD, OP_ROL): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, ROTATE_LEFT32);
case OP(SZ_BYTE, OP_ROR): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, ROTATE_RIGHT8);
case OP(SZ_HALF, OP_ROR): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, ROTATE_RIGHT16);
case OP(SZ_WORD, OP_ROR): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, ROTATE_RIGHT32);
case OP(SZ_BYTE, OP_BSE): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_BIT_SET);
case OP(SZ_HALF, OP_BSE): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_BIT_SET);
case OP(SZ_WORD, OP_BSE): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_BIT_SET);
case OP(SZ_BYTE, OP_BCL): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_BIT_CLEAR);
case OP(SZ_HALF, OP_BCL): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_BIT_CLEAR);
case OP(SZ_WORD, OP_BCL): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_BIT_CLEAR);
2022-10-23 03:00:51 +02:00
case OP(SZ_BYTE, OP_CMP): VM_IMPL_CMP(SIZE8, uint8_t, vm_source8);
case OP(SZ_HALF, OP_CMP): VM_IMPL_CMP(SIZE16, uint16_t, vm_source16);
case OP(SZ_WORD, OP_CMP): VM_IMPL_CMP(SIZE32, uint32_t, vm_source32);
case OP(SZ_BYTE, OP_BTS): VM_IMPL_BTS(SIZE8, uint8_t, vm_source8);
case OP(SZ_HALF, OP_BTS): VM_IMPL_BTS(SIZE16, uint16_t, vm_source16);
case OP(SZ_WORD, OP_BTS): VM_IMPL_BTS(SIZE32, uint32_t, vm_source32);
2022-10-26 07:44:18 +02:00
case OP(SZ_WORD, OP_MSE): {
VM_PRELUDE_0();
vm->mmu_enabled = true;
break;
};
case OP(SZ_WORD, OP_MCL): {
VM_PRELUDE_0();
vm->mmu_enabled = false;
break;
};
case OP(SZ_WORD, OP_INT): {
VM_PRELUDE_1(SIZE32);
uint32_t intr = vm_source32(vm, instr.source, instr.offset);
vm->pointer_instr = vm->pointer_instr_mut;
fox32_raise(vm, intr);
vm->pointer_instr_mut = vm->pointer_instr;
break;
};
2022-10-26 07:44:18 +02:00
case OP(SZ_WORD, OP_TLB): {
VM_PRELUDE_1(SIZE32);
set_and_flush_tlb(vm_source32(vm, instr.source, instr.offset));
2022-10-26 07:44:18 +02:00
break;
};
case OP(SZ_WORD, OP_FLP): {
VM_PRELUDE_1(SIZE32);
flush_single_page(vm_source32(vm, instr.source, instr.offset));
2022-10-26 07:44:18 +02:00
break;
};
2022-10-23 03:00:51 +02:00
default:
vm_panic(vm, FOX32_ERR_BADOPCODE);
}
vm->pointer_instr = vm->pointer_instr_mut;
}
static err_t vm_step(vm_t *vm) {
if (setjmp(vm->panic_jmp) != 0) {
return vm->halted = true, vm->panic_err;
}
vm_execute(vm);
return FOX32_ERR_OK;
}
static err_t vm_resume(vm_t *vm, uint32_t count, uint32_t *executed) {
2022-10-23 03:00:51 +02:00
if (setjmp(vm->panic_jmp) != 0) {
return vm->halted = true, vm->panic_err;
}
vm->halted = false;
2022-10-23 03:00:51 +02:00
uint32_t remaining = count;
while (!vm->halted && !vm->soft_halted && remaining > 0) {
2022-10-23 03:00:51 +02:00
vm_execute(vm);
remaining -= 1;
*executed += 1;
2022-10-23 03:00:51 +02:00
}
if (vm->soft_halted) {
*executed = count;
}
2022-10-23 03:00:51 +02:00
return FOX32_ERR_OK;
}
static fox32_err_t vm_raise(vm_t *vm, uint16_t vector) {
2022-10-26 07:44:18 +02:00
if (!vm->flag_interrupt && vector < 256) {
2022-10-23 03:00:51 +02:00
return FOX32_ERR_NOINTERRUPTS;
}
if (setjmp(vm->panic_jmp) != 0) {
return vm->panic_err;
}
2022-10-26 07:44:18 +02:00
uint32_t pointer_handler =
vm->memory_ram[SIZE32 * (uint32_t) vector] |
vm->memory_ram[SIZE32 * (uint32_t) vector + 1] << 8 |
vm->memory_ram[SIZE32 * (uint32_t) vector + 2] << 16 |
vm->memory_ram[SIZE32 * (uint32_t) vector + 3] << 24;
if (vm->flag_swap_sp) {
uint32_t old_stack_pointer = vm->pointer_stack;
vm->pointer_stack = vm->pointer_exception_stack;
vm_push32(vm, old_stack_pointer);
vm_push32(vm, vm->pointer_instr);
vm_push8(vm, vm_flags_get(vm));
vm->flag_swap_sp = false;
} else {
vm_push32(vm, vm->pointer_instr);
vm_push8(vm, vm_flags_get(vm));
}
2022-10-23 03:00:51 +02:00
2022-10-26 07:44:18 +02:00
if (vector >= 256) {
// if this is an exception, push the operand
vm_push32(vm, vm->exception_operand);
vm->exception_operand = 0;
} else {
// if this is an interrupt, push the vector
vm_push32(vm, (uint32_t) vector);
}
2022-10-23 03:00:51 +02:00
vm->pointer_instr = pointer_handler;
2022-12-26 08:38:26 +01:00
vm->halted = true;
vm->soft_halted = false;
2022-10-23 03:00:51 +02:00
vm->flag_interrupt = false;
return FOX32_ERR_OK;
}
static fox32_err_t vm_recover(vm_t *vm, err_t err) {
switch (err) {
case FOX32_ERR_DEBUGGER:
return vm_raise(vm, EX_DEBUGGER);
case FOX32_ERR_FAULT_RD:
return vm_raise(vm, EX_FAULT_RD);
case FOX32_ERR_FAULT_WR:
return vm_raise(vm, EX_FAULT_WR);
2022-10-23 03:00:51 +02:00
case FOX32_ERR_BADOPCODE:
case FOX32_ERR_BADCONDITION:
case FOX32_ERR_BADREGISTER:
case FOX32_ERR_BADIMMEDIATE:
return vm_raise(vm, EX_ILLEGAL);
case FOX32_ERR_DIVZERO:
return vm_raise(vm, EX_DIVZERO);
case FOX32_ERR_IOREAD:
case FOX32_ERR_IOWRITE:
return vm_raise(vm, EX_BUS);
default:
return FOX32_ERR_CANTRECOVER;
}
}
#define VM_SAFEPUSH_BODY(_vm_push) \
if (setjmp(vm->panic_jmp) != 0) { \
return vm->panic_err; \
} \
_vm_push(vm, value); \
return FOX32_ERR_OK;
static fox32_err_t vm_safepush_byte(vm_t *vm, uint8_t value) {
VM_SAFEPUSH_BODY(vm_push8)
}
static fox32_err_t vm_safepush_half(vm_t *vm, uint16_t value) {
VM_SAFEPUSH_BODY(vm_push16)
}
static fox32_err_t vm_safepush_word(vm_t *vm, uint32_t value) {
VM_SAFEPUSH_BODY(vm_push32)
}
#define VM_SAFEPOP_BODY(_vm_pop) \
*value = 0; \
if (setjmp(vm->panic_jmp) != 0) { \
return vm->panic_err; \
} \
*value = _vm_pop(vm); \
return FOX32_ERR_OK;
static fox32_err_t vm_safepop_byte(vm_t *vm, uint8_t *value) {
VM_SAFEPOP_BODY(vm_pop8)
}
static fox32_err_t vm_safepop_half(vm_t *vm, uint16_t *value) {
VM_SAFEPOP_BODY(vm_pop16)
}
static fox32_err_t vm_safepop_word(vm_t *vm, uint32_t *value) {
VM_SAFEPOP_BODY(vm_pop32)
}
const char *fox32_strerr(fox32_err_t err) {
return err_tostring(err);
}
void fox32_init(fox32_vm_t *vm) {
vm_init(vm);
}
fox32_err_t fox32_step(fox32_vm_t *vm) {
return vm_step(vm);
}
fox32_err_t fox32_resume(fox32_vm_t *vm, uint32_t count, uint32_t *executed) {
return vm_resume(vm, count, executed);
2022-10-23 03:00:51 +02:00
}
fox32_err_t fox32_raise(fox32_vm_t *vm, uint16_t vector) {
return vm_raise(vm, vector);
}
fox32_err_t fox32_recover(fox32_vm_t *vm, fox32_err_t err) {
return vm_recover(vm, err);
}
fox32_err_t fox32_push_byte(fox32_vm_t *vm, uint8_t value) {
return vm_safepush_byte(vm, value);
}
fox32_err_t fox32_push_half(fox32_vm_t *vm, uint16_t value) {
return vm_safepush_half(vm, value);
}
fox32_err_t fox32_push_word(fox32_vm_t *vm, uint32_t value) {
return vm_safepush_word(vm, value);
}
fox32_err_t fox32_pop_byte(fox32_vm_t *vm, uint8_t *value) {
return vm_safepop_byte(vm, value);
}
fox32_err_t fox32_pop_half(fox32_vm_t *vm, uint16_t *value) {
return vm_safepop_half(vm, value);
}
fox32_err_t fox32_pop_word(fox32_vm_t *vm, uint32_t *value) {
return vm_safepop_word(vm, value);
}