save and load compiled vmbl files
This commit is contained in:
@@ -20,7 +20,16 @@ char* exceptionAsCString(VMBL_Exception exception)
|
||||
return "EXCEPTION_INVALID_OPCODE";
|
||||
break;
|
||||
|
||||
case EXCEPTION_INVALID_OPPERAND:
|
||||
return "EXCEPTION_INVALID_OPPERAND";
|
||||
break;
|
||||
|
||||
case EXCEPTION_INVALID_INSTRUCTION_ACCESS:
|
||||
return "EXCEPTION_INVALID_INSTRUCTION_ACCESS";
|
||||
break;
|
||||
|
||||
default:
|
||||
return "EXCEPTION_UNKOWN";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ typedef enum {
|
||||
EXCEPTION_NONE,
|
||||
EXCEPTION_STACK_OVERFLOW,
|
||||
EXCEPTION_STACK_UNDERFLOW,
|
||||
EXCEPTION_INVALID_OPCODE
|
||||
EXCEPTION_INVALID_OPCODE,
|
||||
EXCEPTION_INVALID_OPPERAND,
|
||||
EXCEPTION_INVALID_INSTRUCTION_ACCESS
|
||||
} VMBL_ExceptionType;
|
||||
|
||||
typedef struct
|
||||
|
||||
19
src/main.c
19
src/main.c
@@ -4,21 +4,20 @@
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]));
|
||||
|
||||
VMBL_Instruction program[] = {
|
||||
MAKE_INST_PUSH(69),
|
||||
MAKE_INST_PUSH(420),
|
||||
MAKE_INST_PUSH(0),
|
||||
MAKE_INST_PUSH(1),
|
||||
MAKE_INST_DUP(1),
|
||||
MAKE_INST_DUP(1),
|
||||
MAKE_INST_ADD,
|
||||
MAKE_INST_JMP(2)
|
||||
};
|
||||
|
||||
int main() {
|
||||
VMBL_State vmblState = {};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(program) i++) {
|
||||
VMBL_Exception exception = VBML_ExecuteInstruction(&vmblState, program[i]);
|
||||
if (exception.type != EXCEPTION_NONE) {
|
||||
VMBL_Dump(vmblState, exception);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
VMBL_LoadExecutable(&vmblState, program, sizeof(program));
|
||||
//VMBL_SaveExecutable("fib.vmbl", program, sizeof(program));
|
||||
//VMBL_LoadExecutableFromFile(&vmblState, "fib.vmbl");
|
||||
VMBL_StartVM(&vmblState);
|
||||
|
||||
return 0;
|
||||
}
|
||||
137
src/vmbl.c
137
src/vmbl.c
@@ -1,5 +1,7 @@
|
||||
#include "vmbl.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction instruction) {
|
||||
switch (instruction.type)
|
||||
@@ -21,8 +23,34 @@ VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction i
|
||||
vmblState->stackSize--;
|
||||
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 };
|
||||
break;
|
||||
}
|
||||
@@ -36,10 +64,115 @@ void VMBL_Dump(VMBL_State vmblState, VMBL_Exception exception) {
|
||||
|
||||
if (vmblState.stackSize > 0) {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
37
src/vmbl.h
37
src/vmbl.h
@@ -4,34 +4,55 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include "exception.h"
|
||||
|
||||
#define VMBL_STACK_SIZE 1024
|
||||
#define VMBL_PROGRAM_SIZE 1024
|
||||
|
||||
typedef int64_t Word;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INSTRUCTION_PUSH,
|
||||
INSTRUCTION_ADD
|
||||
INSTRUCTION_ADD,
|
||||
INSTRUCTION_DUPLICATE,
|
||||
INSTRUCTION_JUMP,
|
||||
INSTRUCTION_HALT
|
||||
} InstructionType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Word stack[VMBL_STACK_SIZE];
|
||||
size_t stackSize;
|
||||
} VMBL_State;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
InstructionType type;
|
||||
Word opperand;
|
||||
} VMBL_Instruction;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
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_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);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user