spec-ed a byte code loader that an assembly can target
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 14 Jun 2022 03:30:48 +0000 (22:30 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 14 Jun 2022 03:30:48 +0000 (22:30 -0500)
docs/fileformat.md [new file with mode: 0644]
include/vm.h
src/cli.c
src/vm/program_loader.c [new file with mode: 0644]
src/vm/vm.c

diff --git a/docs/fileformat.md b/docs/fileformat.md
new file mode 100644 (file)
index 0000000..0424fd9
--- /dev/null
@@ -0,0 +1,40 @@
+
+# Reloadable file format
+
+## Necessary sections
+
+- [ ] Header
+- [ ] Code
+- [ ] Data
+- [ ] Func Desc
+- [ ] Native Func Link
+
+### Header
+"OVMI"
+4-byte version
+
+### Code
+Raw bytes that represent the instructions.
+Array of 16-byte instructions.
+
+instr-count (32-bit LE integer) | instrs ...
+
+### Data
+Where, how-long and what the data is.
+
+entry-count (32-bit LE integer) | (offset (32-bit) | size (32-bit) | data ...) ...
+
+### Func Desc
+How many functions.
+Where they start.
+How many params each.
+How many values each.
+
+func-count | (start-instr | param-count | value-count) ...
+
+### Native Func Link
+How many native fucntions.
+The names in numerical order.
+
+func-count | (param-count | name-len-with-null (32-bit integer) | name data (null-terminated)) ...
+
index e0b73251f7a0ebc7e94b4a2651fadcc51fc6ecca..04d15b9988c40b1ef235ae0938af5c3d4abadd3c 100644 (file)
@@ -52,13 +52,14 @@ struct ovm_program_t {
 };
 
 ovm_program_t *ovm_program_new(ovm_store_t *store);
-void           ovm_program_delete(ovm_program_t *program);
-void           ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs);
-void           ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count);
+void ovm_program_delete(ovm_program_t *program);
+void ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs);
+void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count);
 
-void           ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count);
-void           ovm_program_begin_func(ovm_program_t *program, char *name, i32 param_count, i32 value_number_count);
-void           ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count);
+void ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count);
+void ovm_program_begin_func(ovm_program_t *program, char *name, i32 param_count, i32 value_number_count);
+void ovm_program_register_native_func(ovm_program_t *program, char *name,
+        void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count);
 
 //
 // Represents the running configuration and static
@@ -76,6 +77,9 @@ ovm_engine_t *ovm_engine_new(ovm_store_t *store);
 void          ovm_engine_delete(ovm_engine_t *engine);
 void          ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 size);
 
+bool ovm_program_load_from_file(ovm_program_t *program, ovm_engine_t *engine, char *filename);
+void ovm_program_link_native_funcs(ovm_program_t *program, ovm_native_func_t *funcs);
+
 //
 // Represents ephemeral state / execution context.
 // If multiple threads are used, multiple states are needed.
@@ -130,13 +134,15 @@ ovm_instr_t *ovm_func_add_instruction(ovm_func_t *func, ovm_instr_kind_t instr,
 void         ovm_func_delete(ovm_func_t *func);
 
 struct ovm_native_func_t {
+    char *name;
     i32 param_count;
 
     void (*native_func)(void *userdata, ovm_value_t* params, ovm_value_t* result);
     void *userdata;
 };
 
-ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 param_count, ovm_value_t *params);
+ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx,
+        i32 param_count, ovm_value_t *params);
 void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program);
 
 //
index ea9a97eccd099ebd30fca5d30bcfc440e64eb21e..199e8fc9157d6c9e73ab453fb2ce1a0ded204939 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -46,7 +46,7 @@ int main(int argc, char *argv[]) {
     ovm_program_begin_func(prog, "add", 2, 6);
     ovm_program_add_instructions(prog, sizeof(func2_instrs) / sizeof(*func2_instrs), func2_instrs);
 
-    ovm_program_register_native_func(prog, print_result, NULL, 1);
+    ovm_program_register_native_func(prog, "print_result", print_result, NULL, 1);
 
     ovm_program_print_instructions(prog, 0, bh_arr_length(prog->code));
 
diff --git a/src/vm/program_loader.c b/src/vm/program_loader.c
new file mode 100644 (file)
index 0000000..a8996ed
--- /dev/null
@@ -0,0 +1,102 @@
+#include "vm.h"
+
+//
+// I'm very lazy and silly, so this code make the drastic assumption that the
+// endianness of the machine that the file was built on and the machine the
+// VM is running on are the SAME. If this is not the case, the integers very
+// well could be f-ed up. Might be worth switching all integers to use ntohl,
+// so at least that part is consistent...               -brendanfh 06/13/2022
+//
+
+//
+// I wish this didn't take the engine as a parameter... but currently the memory
+// is stored on the engine. So unless the data section elements can be aggregated
+// into an array to be applied later, this is best I got...
+bool ovm_program_load_from_file(ovm_program_t *program, ovm_engine_t *engine, char *filename) {
+    bh_file file;
+    bh_file_error error = bh_file_open(&file, filename);
+    if (error != BH_FILE_ERROR_NONE) {
+        fprintf(stderr, "Failed to open '%s' for reading.\n", filename);
+        return false;
+    }
+
+    char magic[4], version[4];
+    bh_file_read(&file, magic, 4);
+    bh_file_read(&file, version, 4);
+
+    if (strncmp(magic, "OVMI", 4) || strncmp(version, "\x00\x00\x00\x01", 4)) {
+        fprintf(stderr, "Mismatched version/magic number in '%s'.\n", filename);
+        return false;
+    }
+
+    //
+    // Code section
+    // Just copy in the bytes directly.
+    // What's validation anyway?
+    //
+    i32 entry_count;
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    bh_arr_insert_end(program->code, entry_count);
+    bh_file_read(&file, &bh_arr_last(program->code), entry_count * sizeof(ovm_instr_t));
+
+    //
+    // Data section
+    //
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    fori (i, 0, entry_count) {
+        i32 offset, size;
+        bh_file_read(&file, &offset, sizeof(i32));
+        bh_file_read(&file, &size, sizeof(i32));
+
+        assert(engine);
+        assert(engine->memory);
+        bh_file_read(&file, ((u8 *) engine->memory) + offset, size);
+    }
+
+    //
+    // Func section
+    //
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    fori (i, 0, entry_count) {
+        i32 start_instr, param_count, value_number_count;
+        bh_file_read(&file, &start_instr, sizeof(i32));
+        bh_file_read(&file, &param_count, sizeof(i32));
+        bh_file_read(&file, &value_number_count, sizeof(i32));
+
+        ovm_program_register_func(program, "LOADED", start_instr, param_count, value_number_count);
+    }
+
+    //
+    // Native link section
+    //
+    bh_file_read(&file, &entry_count, sizeof(i32));
+    fori (i, 0, entry_count) {
+        i32 param_count, name_len;
+        bh_file_read(&file, &param_count, sizeof(i32));
+        bh_file_read(&file, &name_len, sizeof(i32));
+
+        char *name_buf = bh_alloc_array(program->store->arena_allocator, char, name_len);
+        bh_file_read(&file, name_buf, name_len);
+
+        ovm_program_register_native_func(program, name_buf, NULL, NULL, param_count);
+    }
+
+
+    return true;
+}
+
+void ovm_program_link_native_funcs(ovm_program_t *program, ovm_native_func_t *funcs) {
+    bh_arr_each(ovm_native_func_t, nf, program->native_funcs) {
+        if (nf->native_func && nf->userdata) continue;
+        
+        ovm_native_func_t *func = funcs;
+        while (func->name) {
+            if (!strcmp(nf->name, func->name) && nf->param_count == func->param_count) {
+                nf->native_func = func->native_func;
+                nf->userdata    = func->userdata;
+            }
+
+            func++;
+        }
+    }
+}
index cf6ec4aa348777ce4d2ff032aabd5335c594b5f0..bb8bf6d0da5a52621f94ee333cebbe4e8e909fdf 100644 (file)
@@ -54,8 +54,11 @@ void ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i3
     bh_arr_push(program->funcs, func);
 }
 
-void ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count) {
+void ovm_program_register_native_func(ovm_program_t *program, char *name,
+        void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count) {
+
     ovm_native_func_t native_func;
+    native_func.name = name;
     native_func.param_count = param_count;
     native_func.native_func = func;
     native_func.userdata = data;