VM registers are now a dynamic array

Stack based machines generally need "variable space".  This may be
quite via a symbol-to-word association a list, a hashmap, or some
other system.  Here I decide to go for the simplest: extending the
register system to a dynamic/infinite number of them.  This means, in
practice, that we may use a theoretically infinite number of indexed
words, hwords and bytes to act as variable space.  This means that the
onus is on those who are targeting this virtual machine to create
their own association system to create syntactic variables: all the
machinery is technically installed within the VM, without the veneer
that causes extra cruft.
This commit is contained in:
2023-11-01 17:49:33 +00:00
parent 89fd2b0d17
commit 7a1129d80f
2 changed files with 77 additions and 56 deletions

View File

@@ -12,6 +12,7 @@
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -125,9 +126,9 @@ err_t vm_execute(vm_t *vm)
} }
else if (instruction.opcode == OP_JUMP_REGISTER) else if (instruction.opcode == OP_JUMP_REGISTER)
{ {
if (instruction.operand.as_byte >= 8) if (instruction.operand.as_word >= vm->registers.available)
return ERR_INVALID_REGISTER_WORD; return ERR_INVALID_REGISTER_WORD;
word addr = vm->registers.reg[instruction.operand.as_byte]; word addr = vm->registers.data[instruction.operand.as_word];
return vm_jump(vm, addr); return vm_jump(vm, addr);
} }
else if (OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF)) else if (OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF))
@@ -253,8 +254,8 @@ err_t vm_execute_all(vm_t *vm)
size_t cycles = 0; size_t cycles = 0;
#endif #endif
#if VERBOSE >= 2 #if VERBOSE >= 2
struct Registers prev_registers = vm->registers; registers_t prev_registers = vm->registers;
size_t prev_sptr = 0; size_t prev_sptr = 0;
#endif #endif
while (program->instructions[program->ptr].opcode != OP_HALT && while (program->instructions[program->ptr].opcode != OP_HALT &&
program->ptr < program->max) program->ptr < program->max)
@@ -270,8 +271,7 @@ err_t vm_execute_all(vm_t *vm)
"----------------------------------------------------------------------" "----------------------------------------------------------------------"
"----------\n", "----------\n",
stdout); stdout);
if (memcmp(prev_registers.reg, vm->registers.reg, if (memcmp(&prev_registers, &vm->registers, sizeof(darr_t)) != 0)
ARR_SIZE(vm->registers.reg)) != 0)
{ {
vm_print_registers(vm, stdout); vm_print_registers(vm, stdout);
prev_registers = vm->registers; prev_registers = vm->registers;
@@ -320,14 +320,21 @@ void vm_load_program(vm_t *vm, inst_t *instructions, size_t size)
vm->program.ptr = 0; vm->program.ptr = 0;
} }
void vm_load_registers(vm_t *vm, registers_t registers)
{
vm->registers = registers;
}
void vm_print_registers(vm_t *vm, FILE *fp) void vm_print_registers(vm_t *vm, FILE *fp)
{ {
struct Registers reg = vm->registers; registers_t reg = vm->registers;
fprintf(fp, "Registers.used = %luB\nRegisters.available = %luB\n",
vm->registers.used, vm->registers.available);
fprintf(fp, "Registers.reg = ["); fprintf(fp, "Registers.reg = [");
for (size_t i = 0; i < VM_REGISTERS; ++i) for (size_t i = 0; i <= (reg.used / WORD_SIZE); ++i)
{ {
fprintf(fp, "{%lu:%lX}", i, reg.reg[i]); fprintf(fp, "{%lu:%lX}", i, VM_NTH_REGISTER(reg, i));
if (i != VM_REGISTERS - 1) if (i != reg.used - 1)
fprintf(fp, ", "); fprintf(fp, ", ");
} }
fprintf(fp, "]\n"); fprintf(fp, "]\n");
@@ -447,80 +454,93 @@ err_t vm_push_word(vm_t *vm, data_t w)
return ERR_OK; return ERR_OK;
} }
err_t vm_push_byte_register(vm_t *vm, byte reg) err_t vm_push_byte_register(vm_t *vm, word reg)
{ {
if (reg >= VM_REGISTERS * 8) if (reg > vm->registers.used)
return ERR_INVALID_REGISTER_BYTE; return ERR_INVALID_REGISTER_BYTE;
// Interpret each word based register as 8 byte registers // Interpret each word based register as 8 byte registers
byte b = WORD_NTH_BYTE(vm->registers.reg[reg / 8], reg % 8); byte b = vm->registers.data[reg];
return vm_push_byte(vm, DBYTE(b)); return vm_push_byte(vm, DBYTE(b));
} }
err_t vm_push_hword_register(vm_t *vm, byte reg) err_t vm_push_hword_register(vm_t *vm, word reg)
{ {
if (reg >= VM_REGISTERS * 2) if (reg > (vm->registers.used / HWORD_SIZE))
return ERR_INVALID_REGISTER_HWORD; return ERR_INVALID_REGISTER_HWORD;
// Interpret each word based register as 2 hword registers // Interpret the bytes at point reg * HWORD_SIZE as an hword
hword hw = WORD_NTH_HWORD(vm->registers.reg[reg / 2], reg % 2); hword hw = *(hword *)(vm->registers.data + (reg * HWORD_SIZE));
return vm_push_hword(vm, DHWORD(hw)); return vm_push_hword(vm, DHWORD(hw));
} }
err_t vm_push_word_register(vm_t *vm, byte reg) err_t vm_push_word_register(vm_t *vm, word reg)
{ {
if (reg >= VM_REGISTERS) if (reg > (vm->registers.used / WORD_SIZE))
return ERR_INVALID_REGISTER_WORD; return ERR_INVALID_REGISTER_WORD;
return vm_push_word(vm, DWORD(vm->registers.reg[reg])); return vm_push_word(vm, DWORD(VM_NTH_REGISTER(vm->registers, reg)));
} }
err_t vm_mov_byte(vm_t *vm, byte reg) err_t vm_mov_byte(vm_t *vm, word reg)
{ {
if (reg >= (VM_REGISTERS * 8)) if (reg >= vm->registers.used)
return ERR_INVALID_REGISTER_BYTE; {
// Expand capacity
darr_ensure_capacity(&vm->registers, reg - vm->registers.used);
vm->registers.used = MAX(vm->registers.used, reg + 1);
}
data_t ret = {0}; data_t ret = {0};
err_t err = vm_pop_byte(vm, &ret); err_t err = vm_pop_byte(vm, &ret);
if (err) if (err)
return err; return err;
word *reg_ptr = &vm->registers.reg[reg / 8]; vm->registers.data[reg] = ret.as_byte;
size_t shift = (reg % 8) * 8;
// This resets the bits in the specific byte register
*reg_ptr = *reg_ptr & ~(0xFF << shift);
// This sets the bits
*reg_ptr = (*reg_ptr) | (ret.as_word << shift);
return ERR_OK; return ERR_OK;
} }
err_t vm_mov_hword(vm_t *vm, byte reg) err_t vm_mov_hword(vm_t *vm, word reg)
{ {
if (reg >= (VM_REGISTERS * 2)) if (reg >= (vm->registers.used / HWORD_SIZE))
return ERR_INVALID_REGISTER_HWORD; {
else if (vm->stack.ptr < sizeof(f64)) // Expand capacity till we can ensure that this is a valid
return ERR_STACK_UNDERFLOW; // register to use
// Number of hwords needed ontop of what is allocated:
const size_t hwords = (reg - (vm->registers.used / HWORD_SIZE));
// Number of bytes needed ontop of what is allocated
const size_t diff = (hwords + 1) * HWORD_SIZE;
darr_ensure_capacity(&vm->registers, diff);
vm->registers.used = MAX(vm->registers.used, (reg * HWORD_SIZE) + 1);
}
data_t ret = {0}; data_t ret = {0};
err_t err = vm_pop_hword(vm, &ret); err_t err = vm_pop_hword(vm, &ret);
if (err) if (err)
return err; return err;
word *reg_ptr = &vm->registers.reg[reg / 2]; // Here we treat vm->registers as a set of hwords
size_t shift = (reg % 2) * 2; hword *hword_ptr = (hword *)(vm->registers.data + (reg * HWORD_SIZE));
// This resets the bits in the specific hword register *hword_ptr = ret.as_hword;
*reg_ptr = *reg_ptr & ~(0xFFFFFFFF << shift);
// This sets the bits
*reg_ptr = (*reg_ptr) | (ret.as_word << shift);
return ERR_OK; return ERR_OK;
} }
err_t vm_mov_word(vm_t *vm, byte reg) err_t vm_mov_word(vm_t *vm, word reg)
{ {
if (reg >= VM_REGISTERS) if (reg >= (vm->registers.used / WORD_SIZE))
return ERR_INVALID_REGISTER_WORD; {
// Number of hwords needed ontop of what is allocated:
const size_t words = (reg - (vm->registers.used / WORD_SIZE));
// Number of bytes needed ontop of what is allocated
const size_t diff = (words + 1) * WORD_SIZE;
darr_ensure_capacity(&vm->registers, diff);
vm->registers.used = MAX(vm->registers.used, (reg * WORD_SIZE) + 1);
}
else if (vm->stack.ptr < sizeof(word)) else if (vm->stack.ptr < sizeof(word))
return ERR_STACK_UNDERFLOW; return ERR_STACK_UNDERFLOW;
data_t ret = {0}; data_t ret = {0};
err_t err = vm_pop_word(vm, &ret); err_t err = vm_pop_word(vm, &ret);
if (err) if (err)
return err; return err;
vm->registers.reg[reg] = ret.as_word; VM_NTH_REGISTER(vm->registers, reg) = ret.as_word;
return ERR_OK; return ERR_OK;
} }

View File

@@ -33,13 +33,13 @@ typedef enum
const char *err_as_cstr(err_t); const char *err_as_cstr(err_t);
#define VM_REGISTERS 8 typedef darr_t registers_t;
#define VM_NTH_REGISTER(REGISTERS, N) (((word *)((REGISTERS).data))[N])
#define VM_REGISTERS_AVAILABLE(REGISTERS) (((REGISTERS).available) / WORD_SIZE)
typedef struct typedef struct
{ {
struct Registers registers_t registers;
{
word reg[VM_REGISTERS];
} registers;
struct Stack struct Stack
{ {
byte *data; byte *data;
@@ -57,6 +57,7 @@ err_t vm_execute_all(vm_t *);
void vm_load_stack(vm_t *, byte *, size_t); void vm_load_stack(vm_t *, byte *, size_t);
void vm_load_program(vm_t *, inst_t *, size_t); void vm_load_program(vm_t *, inst_t *, size_t);
void vm_load_registers(vm_t *, registers_t);
// Print routines // Print routines
#define VM_PRINT_PROGRAM_EXCERPT 5 #define VM_PRINT_PROGRAM_EXCERPT 5
@@ -83,15 +84,15 @@ static const push_f PUSH_ROUTINES[] = {
[OP_PUSH_WORD] = vm_push_word, [OP_PUSH_WORD] = vm_push_word,
}; };
err_t vm_push_byte_register(vm_t *, byte); err_t vm_push_byte_register(vm_t *, word);
err_t vm_push_hword_register(vm_t *, byte); err_t vm_push_hword_register(vm_t *, word);
err_t vm_push_word_register(vm_t *, byte); err_t vm_push_word_register(vm_t *, word);
err_t vm_mov_byte(vm_t *, byte); err_t vm_mov_byte(vm_t *, word);
err_t vm_mov_hword(vm_t *, byte); err_t vm_mov_hword(vm_t *, word);
err_t vm_mov_word(vm_t *, byte); err_t vm_mov_word(vm_t *, word);
typedef err_t (*reg_f)(vm_t *, byte); typedef err_t (*reg_f)(vm_t *, word);
static const reg_f REG_ROUTINES[] = { static const reg_f REG_ROUTINES[] = {
[OP_PUSH_REGISTER_BYTE] = vm_push_byte_register, [OP_PUSH_REGISTER_BYTE] = vm_push_byte_register,
[OP_PUSH_REGISTER_HWORD] = vm_push_hword_register, [OP_PUSH_REGISTER_HWORD] = vm_push_hword_register,