#include "vmbl.h" #include #include #include #define MATH_OP(vmblState, operation) \ do { \ if ((vmblState)->stackSize < 2) { \ return (VMBL_Exception){ EXCEPTION_STACK_UNDERFLOW }; \ } \ (vmblState)->stack[(vmblState)->stackSize - 2] = (vmblState)->stack[(vmblState)->stackSize - 2] operation (vmblState)->stack[(vmblState)->stackSize - 1]; \ (vmblState)->stackSize--; \ } while (0); VMBL_Exception VBML_ExecuteInstruction(VMBL_State *vmblState, VMBL_Instruction instruction) { switch (instruction.type) { case INSTRUCTION_PUSH: if (vmblState->stackSize >= VMBL_STACK_SIZE) { return (VMBL_Exception){ EXCEPTION_STACK_OVERFLOW }; } vmblState->stack[vmblState->stackSize++] = instruction.opperands[0]; break; case INSTRUCTION_ADD: MATH_OP(vmblState, +); break; case INSTRUCTION_SUB: MATH_OP(vmblState, -); break; case INSTRUCTION_MUL: MATH_OP(vmblState, *); break; case INSTRUCTION_DIV: if (vmblState->stack[vmblState->stackSize-1] == 0) { return (VMBL_Exception){ EXCEPTION_DIVIDE_BY_ZERO }; } MATH_OP(vmblState, /); break; case INSTRUCTION_DUPLICATE: if (vmblState->stackSize - instruction.opperands[0] <= 0) { return (VMBL_Exception){ EXCEPTION_STACK_UNDERFLOW }; } if (vmblState->stackSize >= VMBL_STACK_SIZE) { return (VMBL_Exception){ EXCEPTION_STACK_OVERFLOW }; } if (instruction.opperands[0] < 0) { return (VMBL_Exception) { EXCEPTION_INVALID_OPPERAND }; } vmblState->stack[vmblState->stackSize++] = vmblState->stack[vmblState->stackSize - 1 - instruction.opperands[0]]; break; case INSTRUCTION_JUMP: vmblState->ip = instruction.opperands[0]; break; case INSTRUCTION_HALT: vmblState->halted = true; break; case INSTRUCTION_EQUAL: MATH_OP(vmblState, ==); break; case INSTRUCTION_JUMP_CONDITIONAL: if (instruction.opperands[0] < 0) { return (VMBL_Exception) { EXCEPTION_INVALID_OPPERAND }; } if (vmblState->stack[vmblState->stackSize--]) { vmblState->ip = instruction.opperands[0]; } break; case INSTRUCTION_DROP: if (vmblState->stackSize <= 0) { return (VMBL_Exception){ EXCEPTION_STACK_UNDERFLOW }; } vmblState->stackSize--; break; default: return (VMBL_Exception) { EXCEPTION_INVALID_OPCODE }; break; } return (VMBL_Exception) { EXCEPTION_NONE }; } void VMBL_Dump(VMBL_State vmblState, VMBL_Exception exception) { fprintf(stderr, "EXCEPTION: %s\n\n", exceptionAsCString(exception)); fprintf(stderr, "Stack:\n"); if (vmblState.stackSize > 0) { for (size_t i = 0; i < vmblState.stackSize; i++) { fprintf(stderr, " 0x%lX: %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, 0x%lx, 0x%lx\n", instructionTypeToCStr(instruction.type), instruction.opperands[0], instruction.opperands[1], instruction.opperands[2]); 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, 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), programSize, file); fclose(file); } char *instructionTypeToCStr(InstructionType type) { printf("%x\n", type); switch (type) { case INSTRUCTION_PUSH: return "PUSH"; break; case INSTRUCTION_ADD: return "ADD "; break; case INSTRUCTION_SUB: return "SUB "; break; case INSTRUCTION_MUL: return "MUL "; break; case INSTRUCTION_DIV: return "DIV "; break; case INSTRUCTION_DUPLICATE: return "DUP "; break; case INSTRUCTION_JUMP: return "JMP "; break; case INSTRUCTION_HALT: return "HALT"; break; case INSTRUCTION_EQUAL: return "EQ "; break; case INSTRUCTION_NOT_EQUAL: return "NEQ "; break; case INSTRUCTION_GREATER_THAN: return "GT "; break; case INSTRUCTION_GREATER_THAN_EQUAL: return "GTE "; break; case INSTRUCTION_LESS_THAN: return "LT "; break; case INSTRUCTION_LESS_THAN_EQUAL: return "LTE "; break; case INSTRUCTION_JUMP_CONDITIONAL: return "JC "; break; default: return "??? "; break; } }