save and load compiled vmbl files

This commit is contained in:
SpookyDervish
2025-12-20 13:29:49 +11:00
parent f97ef75445
commit c88a6a993d
6 changed files with 186 additions and 22 deletions

BIN
fib.vmbl Normal file

Binary file not shown.

View File

@@ -20,7 +20,16 @@ char* exceptionAsCString(VMBL_Exception exception)
return "EXCEPTION_INVALID_OPCODE"; return "EXCEPTION_INVALID_OPCODE";
break; break;
case EXCEPTION_INVALID_OPPERAND:
return "EXCEPTION_INVALID_OPPERAND";
break;
case EXCEPTION_INVALID_INSTRUCTION_ACCESS:
return "EXCEPTION_INVALID_INSTRUCTION_ACCESS";
break;
default: default:
return "EXCEPTION_UNKOWN";
break; break;
} }
} }

View File

@@ -5,7 +5,9 @@ typedef enum {
EXCEPTION_NONE, EXCEPTION_NONE,
EXCEPTION_STACK_OVERFLOW, EXCEPTION_STACK_OVERFLOW,
EXCEPTION_STACK_UNDERFLOW, EXCEPTION_STACK_UNDERFLOW,
EXCEPTION_INVALID_OPCODE EXCEPTION_INVALID_OPCODE,
EXCEPTION_INVALID_OPPERAND,
EXCEPTION_INVALID_INSTRUCTION_ACCESS
} VMBL_ExceptionType; } VMBL_ExceptionType;
typedef struct typedef struct

View File

@@ -4,21 +4,20 @@
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])); #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]));
VMBL_Instruction program[] = { VMBL_Instruction program[] = {
MAKE_INST_PUSH(69), MAKE_INST_PUSH(0),
MAKE_INST_PUSH(420), MAKE_INST_PUSH(1),
MAKE_INST_DUP(1),
MAKE_INST_DUP(1),
MAKE_INST_ADD, MAKE_INST_ADD,
MAKE_INST_JMP(2)
}; };
int main() { int main() {
VMBL_State vmblState = {}; VMBL_State vmblState = {};
VMBL_LoadExecutable(&vmblState, program, sizeof(program));
for (size_t i = 0; i < ARRAY_SIZE(program) i++) { //VMBL_SaveExecutable("fib.vmbl", program, sizeof(program));
VMBL_Exception exception = VBML_ExecuteInstruction(&vmblState, program[i]); //VMBL_LoadExecutableFromFile(&vmblState, "fib.vmbl");
if (exception.type != EXCEPTION_NONE) { VMBL_StartVM(&vmblState);
VMBL_Dump(vmblState, exception);
return 1;
}
}
return 0; return 0;
} }

View File

@@ -1,5 +1,7 @@
#include "vmbl.h" #include "vmbl.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction instruction) { VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction instruction) {
switch (instruction.type) switch (instruction.type)
@@ -21,8 +23,34 @@ VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction i
vmblState->stackSize--; vmblState->stackSize--;
break; break;
default: case INSTRUCTION_DUPLICATE:
if (vmblState->stackSize - instruction.opperand <= 0) {
return (VMBL_Exception){ EXCEPTION_STACK_UNDERFLOW };
}
if (vmblState->stackSize >= VMBL_STACK_SIZE) {
return (VMBL_Exception){ EXCEPTION_STACK_OVERFLOW };
}
if (instruction.opperand < 0) {
return (VMBL_Exception) { EXCEPTION_INVALID_OPPERAND };
}
vmblState->stack[vmblState->stackSize++] = vmblState->stack[vmblState->stackSize - 1 - instruction.opperand];
break;
case INSTRUCTION_JUMP:
vmblState->ip = instruction.opperand;
break;
case INSTRUCTION_HALT:
vmblState->halted = true;
break;
default:
return (VMBL_Exception) { EXCEPTION_INVALID_OPCODE }; return (VMBL_Exception) { EXCEPTION_INVALID_OPCODE };
break; break;
} }
@@ -36,10 +64,115 @@ void VMBL_Dump(VMBL_State vmblState, VMBL_Exception exception) {
if (vmblState.stackSize > 0) { if (vmblState.stackSize > 0) {
for (size_t i = 0; i < vmblState.stackSize; i++) { for (size_t i = 0; i < vmblState.stackSize; i++) {
fprintf(stderr, " 0x%lX: %ld\n", i, vmblState.stack[i]); fprintf(stderr, " 0x%lX: 0x%ld\n", i, vmblState.stack[i]);
} }
} else { } else {
fprintf(stderr, " [empty]\n"); fprintf(stderr, " [empty]\n");
} }
} }
void VMBL_StartVM(VMBL_State *vmblState) {
for (;;)
{
// fetch next instruction
if (vmblState->ip >= VMBL_PROGRAM_SIZE) {
VMBL_Dump(*vmblState, (VMBL_Exception){ EXCEPTION_INVALID_INSTRUCTION_ACCESS});
return;
}
VMBL_Instruction instruction = vmblState->program[vmblState->ip++];
printf("%s 0x%lx\n", instructionTypeToCStr(instruction.type), instruction.opperand);
VMBL_Exception exception = VBML_ExecuteInstruction(vmblState, instruction);
if (exception.type != EXCEPTION_NONE) {
VMBL_Dump(*vmblState, exception);
return;
}
if (vmblState->halted) {
return;
}
}
}
// executable operations
void VMBL_LoadExecutable(VMBL_State *vmblState, VMBL_Instruction *program, size_t programSize) {
if (programSize > VMBL_PROGRAM_SIZE) {
fprintf(stderr, "VMBL: failed to load program from memory because it is %ld words in size, while the max size is %d.", programSize, VMBL_PROGRAM_SIZE);
exit(1);
}
memcpy(vmblState->program, program, sizeof(program[0]) * programSize);
vmblState->programSize = programSize;
}
void VMBL_LoadExecutableFromFile(VMBL_State *vmblState, const char* filePath) {
FILE *file = fopen(filePath, "rb");
if (file == NULL) {
perror("VMBL: Failed to open file");
exit(1);
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
rewind(file);
size_t programSize = size / sizeof(vmblState->program[0]);
VMBL_Instruction program[programSize];
fread(program, sizeof(program[0]), programSize, file);
VMBL_LoadExecutable(vmblState, program, programSize);
fclose(file);
}
void VMBL_SaveExecutable(const char* filePath, VMBL_Instruction *program, size_t programSize) {
FILE *file = fopen(filePath, "wb");
if (file == NULL) {
perror("VMBL: Failed to open file");
exit(1);
}
fwrite(program, sizeof(program[0]), programSize, file);
fclose(file);
}
char *instructionTypeToCStr(InstructionType type) {
switch (type)
{
case INSTRUCTION_PUSH:
return "PUSH";
break;
case INSTRUCTION_ADD:
return "ADD ";
break;
case INSTRUCTION_DUPLICATE:
return "DUP ";
break;
case INSTRUCTION_JUMP:
return "JMP ";
break;
case INSTRUCTION_HALT:
return "HALT";
break;
default:
return "??? ";
break;
}
}

View File

@@ -4,34 +4,55 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <stdbool.h>
#include "exception.h" #include "exception.h"
#define VMBL_STACK_SIZE 1024 #define VMBL_STACK_SIZE 1024
#define VMBL_PROGRAM_SIZE 1024
typedef int64_t Word; typedef int64_t Word;
typedef enum typedef enum
{ {
INSTRUCTION_PUSH, INSTRUCTION_PUSH,
INSTRUCTION_ADD INSTRUCTION_ADD,
INSTRUCTION_DUPLICATE,
INSTRUCTION_JUMP,
INSTRUCTION_HALT
} InstructionType; } InstructionType;
typedef struct
{
Word stack[VMBL_STACK_SIZE];
size_t stackSize;
} VMBL_State;
typedef struct typedef struct
{ {
InstructionType type; InstructionType type;
Word opperand; Word opperand;
} VMBL_Instruction; } VMBL_Instruction;
#define MAKE_INST_PUSH(value) (VMBL_Instruction) { .type = INSTRUCTION_PUSH, .opperand = (value) } typedef struct
#define MAKE_INST_ADD (VMBL_Instruction) { .type = INSTRUCTION_ADD } {
Word stack[VMBL_STACK_SIZE];
size_t stackSize;
VMBL_Instruction program[VMBL_PROGRAM_SIZE];
size_t programSize;
long ip;
bool halted;
} VMBL_State;
#define MAKE_INST_PUSH(value) (VMBL_Instruction) { .type = INSTRUCTION_PUSH, .opperand = (value) }
#define MAKE_INST_ADD (VMBL_Instruction) { .type = INSTRUCTION_ADD, }
#define MAKE_INST_DUP(value) (VMBL_Instruction) { .type = INSTRUCTION_DUPLICATE, .opperand = (value) }
#define MAKE_INST_JMP(value) (VMBL_Instruction) { .type = INSTRUCTION_JUMP , .opperand = (value) }
#define MAKE_INST_HALT (VMBL_Instruction) { .type = INSTRUCTION_HALT }
VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction instruction); VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction instruction);
void VMBL_Dump(VMBL_State vmblState, VMBL_Exception exception); void VMBL_Dump(VMBL_State vmblState, VMBL_Exception exception);
void VMBL_StartVM(VMBL_State *vmblState);
void VMBL_LoadExecutable(VMBL_State *vmblState, VMBL_Instruction *program, size_t programSize);
void VMBL_LoadExecutableFromFile(VMBL_State *vmblState, const char* filePath);
void VMBL_SaveExecutable(const char* filePath, VMBL_Instruction *program, size_t programSize);
char *instructionTypeToCStr(InstructionType type);
#endif // !VMBL_H #endif // !VMBL_H