diff --git a/fib.vmbl b/fib.vmbl new file mode 100644 index 0000000..ec10039 Binary files /dev/null and b/fib.vmbl differ diff --git a/src/exception.c b/src/exception.c index 9024450..204ea87 100644 --- a/src/exception.c +++ b/src/exception.c @@ -19,8 +19,17 @@ char* exceptionAsCString(VMBL_Exception exception) case EXCEPTION_INVALID_OPCODE: 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; } } \ No newline at end of file diff --git a/src/exception.h b/src/exception.h index cc17e00..8b65030 100644 --- a/src/exception.h +++ b/src/exception.h @@ -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 diff --git a/src/main.c b/src/main.c index 4c304f5..df6e216 100644 --- a/src/main.c +++ b/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; } \ No newline at end of file diff --git a/src/vmbl.c b/src/vmbl.c index 44a910c..e048cb1 100644 --- a/src/vmbl.c +++ b/src/vmbl.c @@ -1,5 +1,7 @@ #include "vmbl.h" #include +#include +#include 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; + } } \ No newline at end of file diff --git a/src/vmbl.h b/src/vmbl.h index c4fb320..5b337c1 100644 --- a/src/vmbl.h +++ b/src/vmbl.h @@ -4,34 +4,55 @@ #include #include #include +#include #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; -#define MAKE_INST_PUSH(value) (VMBL_Instruction) { .type = INSTRUCTION_PUSH, .opperand = (value) } -#define MAKE_INST_ADD (VMBL_Instruction) { .type = INSTRUCTION_ADD } +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_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