From be312cfbbf6daf28c7a99464f2f59bd0c1dd355e Mon Sep 17 00:00:00 2001
From: Aryadev Chavali <aryadev@aryadevchavali.com>
Date: Wed, 1 Nov 2023 21:17:33 +0000
Subject: Implemented instructions in the runtime for memory management

Pretty simple, required some new types of errors.  Obviously there's
space for a macro for the differing instruction implementations, but
these things need to be tested a bit more before I can do that.
---
 vm/runtime.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 vm/runtime.h |  28 +++++++++++-
 2 files changed, 167 insertions(+), 5 deletions(-)

diff --git a/vm/runtime.c b/vm/runtime.c
index ce8281a..953470c 100644
--- a/vm/runtime.c
+++ b/vm/runtime.c
@@ -46,6 +46,10 @@ const char *err_as_cstr(err_t err)
     break;
   case ERR_INVALID_PROGRAM_ADDRESS:
     return "INVALID_PROGRAM_ADDRESS";
+  case ERR_INVALID_PAGE_ADDRESS:
+    return "INVALID_PAGE_ADDRESS";
+  case ERR_OUT_OF_BOUNDS:
+    return "OUT_OF_BOUNDS";
   case ERR_END_OF_PROGRAM:
     return "END_OF_PROGRAM";
     break;
@@ -56,7 +60,7 @@ const char *err_as_cstr(err_t err)
 
 err_t vm_execute(vm_t *vm)
 {
-  static_assert(NUMBER_OF_OPCODES == 73, "vm_execute: Out of date");
+  static_assert(NUMBER_OF_OPCODES == 83, "vm_execute: Out of date");
   struct Program *prog = &vm->program;
   if (prog->ptr >= prog->max)
     return ERR_END_OF_PROGRAM;
@@ -69,7 +73,10 @@ err_t vm_execute(vm_t *vm)
   }
   else if (OPCODE_IS_TYPE(instruction.opcode, OP_MOV) ||
            OPCODE_IS_TYPE(instruction.opcode, OP_PUSH_REGISTER) ||
-           OPCODE_IS_TYPE(instruction.opcode, OP_DUP))
+           OPCODE_IS_TYPE(instruction.opcode, OP_DUP) ||
+           OPCODE_IS_TYPE(instruction.opcode, OP_MALLOC) ||
+           OPCODE_IS_TYPE(instruction.opcode, OP_MSET) ||
+           OPCODE_IS_TYPE(instruction.opcode, OP_MGET))
   {
     prog->ptr++;
     return WORD_ROUTINES[instruction.opcode](vm, instruction.operand.as_word);
@@ -105,7 +112,8 @@ err_t vm_execute(vm_t *vm)
            OPCODE_IS_TYPE(instruction.opcode, OP_GT) ||
            OPCODE_IS_TYPE(instruction.opcode, OP_GTE) ||
            OPCODE_IS_TYPE(instruction.opcode, OP_PLUS) ||
-           OPCODE_IS_TYPE(instruction.opcode, OP_MULT))
+           OPCODE_IS_TYPE(instruction.opcode, OP_MULT) ||
+           instruction.opcode == OP_MDELETE)
   {
     prog->ptr++;
     return STACK_ROUTINES[instruction.opcode](vm);
@@ -593,6 +601,123 @@ err_t vm_dup_word(vm_t *vm, word w)
   return vm_push_word(vm, DWORD(convert_bytes_to_word(bytes)));
 }
 
+err_t vm_malloc_byte(vm_t *vm, word n)
+{
+  page_t *page = heap_allocate(&vm->heap, n);
+  return vm_push_word(vm, DWORD((word)page));
+}
+
+err_t vm_malloc_hword(vm_t *vm, word n)
+{
+  page_t *page = heap_allocate(&vm->heap, n * HWORD_SIZE);
+  return vm_push_word(vm, DWORD((word)page));
+}
+
+err_t vm_malloc_word(vm_t *vm, word n)
+{
+  page_t *page = heap_allocate(&vm->heap, n * WORD_SIZE);
+  return vm_push_word(vm, DWORD((word)page));
+}
+
+err_t vm_mset_byte(vm_t *vm, word nth)
+{
+  // Stack layout should be [BYTE, PTR]
+  data_t byte = {0};
+  err_t err   = vm_pop_byte(vm, &byte);
+  if (err)
+    return err;
+  data_t ptr = {0};
+  err        = vm_pop_word(vm, &ptr);
+  if (err)
+    return err;
+
+  page_t *page = (page_t *)ptr.as_word;
+  if (nth >= page->available)
+    return ERR_OUT_OF_BOUNDS;
+  page->data[nth] = byte.as_byte;
+
+  return ERR_OK;
+}
+
+err_t vm_mset_hword(vm_t *vm, word nth)
+{
+  // Stack layout should be [HWORD, PTR]
+  data_t byte = {0};
+  err_t err   = vm_pop_hword(vm, &byte);
+  if (err)
+    return err;
+  data_t ptr = {0};
+  err        = vm_pop_word(vm, &ptr);
+  if (err)
+    return err;
+
+  page_t *page = (page_t *)ptr.as_word;
+  if (nth >= (page->available / HWORD_SIZE))
+    return ERR_OUT_OF_BOUNDS;
+  ((hword *)page->data)[nth] = byte.as_hword;
+
+  return ERR_OK;
+}
+
+err_t vm_mset_word(vm_t *vm, word nth)
+{
+  // Stack layout should be [WORD, PTR]
+  data_t byte = {0};
+  err_t err   = vm_pop_word(vm, &byte);
+  if (err)
+    return err;
+  data_t ptr = {0};
+  err        = vm_pop_word(vm, &ptr);
+  if (err)
+    return err;
+
+  page_t *page = (page_t *)ptr.as_word;
+  if (nth >= (page->available / WORD_SIZE))
+    return ERR_OUT_OF_BOUNDS;
+  ((word *)page->data)[nth] = byte.as_hword;
+
+  return ERR_OK;
+}
+
+err_t vm_mget_byte(vm_t *vm, word n)
+{
+  // Stack layout should be [PTR]
+  data_t ptr = {0};
+  err_t err  = vm_pop_word(vm, &ptr);
+  if (err)
+    return err;
+  page_t *page = (page_t *)ptr.as_word;
+  if (n >= page->available)
+    return ERR_OUT_OF_BOUNDS;
+  return vm_push_byte(vm, DBYTE(page->data[n]));
+}
+
+err_t vm_mget_hword(vm_t *vm, word n)
+{
+  // Stack layout should be [PTR]
+  data_t ptr = {0};
+  err_t err  = vm_pop_word(vm, &ptr);
+  if (err)
+    return err;
+  page_t *page = (page_t *)ptr.as_word;
+  if (n >= (page->available / HWORD_SIZE))
+    return ERR_OUT_OF_BOUNDS;
+  return vm_push_byte(vm, DHWORD(((hword *)page->data)[n]));
+}
+
+err_t vm_mget_word(vm_t *vm, word n)
+{
+  // Stack layout should be [PTR]
+  data_t ptr = {0};
+  err_t err  = vm_pop_word(vm, &ptr);
+  if (err)
+    return err;
+  page_t *page = (page_t *)ptr.as_word;
+  if (n >= (page->available / WORD_SIZE))
+    return ERR_OUT_OF_BOUNDS;
+  return vm_push_byte(vm, DHWORD(((word *)page->data)[n]));
+}
+
 err_t vm_pop_byte(vm_t *vm, data_t *ret)
 {
   if (vm->stack.ptr == 0)
@@ -631,6 +756,19 @@ err_t vm_pop_word(vm_t *vm, data_t *ret)
   return ERR_OK;
 }
 
+err_t vm_mdelete(vm_t *vm)
+{
+  data_t ptr = {0};
+  err_t err  = vm_pop_word(vm, &ptr);
+  if (err)
+    return err;
+  page_t *page = (page_t *)ptr.as_word;
+  bool done    = heap_free_page(&vm->heap, page);
+  if (!done)
+    return ERR_INVALID_PAGE_ADDRESS;
+  return ERR_OK;
+}
+
 #define VM_NOT_TYPE(TYPEL, TYPEU)                        \
   err_t vm_not_##TYPEL(vm_t *vm)                         \
   {                                                      \
diff --git a/vm/runtime.h b/vm/runtime.h
index c4d28d8..9ac1b47 100644
--- a/vm/runtime.h
+++ b/vm/runtime.h
@@ -29,6 +29,8 @@ typedef enum
   ERR_INVALID_REGISTER_HWORD,
   ERR_INVALID_REGISTER_WORD,
   ERR_INVALID_PROGRAM_ADDRESS,
+  ERR_INVALID_PAGE_ADDRESS,
+  ERR_OUT_OF_BOUNDS,
   ERR_END_OF_PROGRAM,
 } err_t;
 
@@ -100,6 +102,18 @@ err_t vm_dup_byte(vm_t *, word);
 err_t vm_dup_hword(vm_t *, word);
 err_t vm_dup_word(vm_t *, word);
 
+err_t vm_malloc_byte(vm_t *, word);
+err_t vm_malloc_hword(vm_t *, word);
+err_t vm_malloc_word(vm_t *, word);
+
+err_t vm_mset_byte(vm_t *, word);
+err_t vm_mset_hword(vm_t *, word);
+err_t vm_mset_word(vm_t *, word);
+
+err_t vm_mget_byte(vm_t *, word);
+err_t vm_mget_hword(vm_t *, word);
+err_t vm_mget_word(vm_t *, word);
+
 typedef err_t (*word_f)(vm_t *, word);
 static const word_f WORD_ROUTINES[] = {
     [OP_PUSH_REGISTER_BYTE]  = vm_push_byte_register,
@@ -111,8 +125,18 @@ static const word_f WORD_ROUTINES[] = {
     [OP_DUP_BYTE]            = vm_dup_byte,
     [OP_DUP_HWORD]           = vm_dup_hword,
     [OP_DUP_WORD]            = vm_dup_word,
+    [OP_MALLOC_BYTE]         = vm_malloc_byte,
+    [OP_MALLOC_HWORD]        = vm_malloc_hword,
+    [OP_MALLOC_WORD]         = vm_malloc_word,
+    [OP_MGET_BYTE]           = vm_mget_byte,
+    [OP_MGET_HWORD]          = vm_mget_hword,
+    [OP_MGET_WORD]           = vm_mget_word,
+    [OP_MSET_BYTE]           = vm_mset_byte,
+    [OP_MSET_HWORD]          = vm_mset_hword,
+    [OP_MSET_WORD]           = vm_mset_word,
 };
 
+err_t vm_mdelete(vm_t *);
 
 err_t vm_not_byte(vm_t *);
 err_t vm_not_hword(vm_t *);
@@ -175,8 +199,8 @@ err_t vm_mult_word(vm_t *);
 
 typedef err_t (*stack_f)(vm_t *);
 static const stack_f STACK_ROUTINES[] = {
-    [OP_NOT_BYTE] = vm_not_byte,   [OP_NOT_HWORD] = vm_not_hword,
-    [OP_NOT_WORD] = vm_not_word,
+    [OP_MDELETE] = vm_mdelete,     [OP_NOT_BYTE] = vm_not_byte,
+    [OP_NOT_HWORD] = vm_not_hword, [OP_NOT_WORD] = vm_not_word,
 
     [OP_OR_BYTE] = vm_or_byte,     [OP_OR_HWORD] = vm_or_hword,
     [OP_OR_WORD] = vm_or_word,
-- 
cgit v1.2.3-13-gbd6f