2025-12-19 18:31:41 +11:00
|
|
|
#include "vmbl.h"
|
2025-12-21 09:44:16 +11:00
|
|
|
#include "file_utils.h"
|
2025-12-19 18:31:41 +11:00
|
|
|
#include <stdio.h>
|
2025-12-20 13:29:49 +11:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2025-12-19 18:31:41 +11:00
|
|
|
|
2025-12-20 19:42:00 +11:00
|
|
|
|
|
|
|
|
#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);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-19 18:31:41 +11:00
|
|
|
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 };
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 16:55:40 +11:00
|
|
|
vmblState->stack[vmblState->stackSize++] = instruction.opperands[0];
|
2025-12-19 18:31:41 +11:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case INSTRUCTION_ADD:
|
2025-12-20 19:42:00 +11:00
|
|
|
MATH_OP(vmblState, +);
|
2025-12-19 18:31:41 +11:00
|
|
|
break;
|
2025-12-20 16:55:40 +11:00
|
|
|
case INSTRUCTION_SUB:
|
2025-12-20 19:42:00 +11:00
|
|
|
MATH_OP(vmblState, -);
|
2025-12-20 16:55:40 +11:00
|
|
|
break;
|
|
|
|
|
case INSTRUCTION_MUL:
|
2025-12-20 19:42:00 +11:00
|
|
|
MATH_OP(vmblState, *);
|
2025-12-20 16:55:40 +11:00
|
|
|
break;
|
|
|
|
|
case INSTRUCTION_DIV:
|
2025-12-21 05:47:15 +11:00
|
|
|
if (vmblState->stack[vmblState->stackSize-1] == 0) {
|
2025-12-20 19:42:00 +11:00
|
|
|
return (VMBL_Exception){ EXCEPTION_DIVIDE_BY_ZERO };
|
|
|
|
|
}
|
2025-12-20 16:55:40 +11:00
|
|
|
|
2025-12-20 19:42:00 +11:00
|
|
|
MATH_OP(vmblState, /);
|
2025-12-20 16:55:40 +11:00
|
|
|
break;
|
2025-12-19 18:31:41 +11:00
|
|
|
|
2025-12-20 13:29:49 +11:00
|
|
|
case INSTRUCTION_DUPLICATE:
|
2025-12-20 16:55:40 +11:00
|
|
|
if (vmblState->stackSize - instruction.opperands[0] <= 0) {
|
2025-12-20 13:29:49 +11:00
|
|
|
return (VMBL_Exception){ EXCEPTION_STACK_UNDERFLOW };
|
|
|
|
|
}
|
2025-12-19 18:31:41 +11:00
|
|
|
|
2025-12-20 13:29:49 +11:00
|
|
|
if (vmblState->stackSize >= VMBL_STACK_SIZE) {
|
|
|
|
|
return (VMBL_Exception){ EXCEPTION_STACK_OVERFLOW };
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 16:55:40 +11:00
|
|
|
if (instruction.opperands[0] < 0) {
|
2025-12-20 13:29:49 +11:00
|
|
|
return (VMBL_Exception) { EXCEPTION_INVALID_OPPERAND };
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 16:55:40 +11:00
|
|
|
vmblState->stack[vmblState->stackSize++] = vmblState->stack[vmblState->stackSize - 1 - instruction.opperands[0]];
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case INSTRUCTION_JUMP:
|
|
|
|
|
|
2025-12-20 16:55:40 +11:00
|
|
|
vmblState->ip = instruction.opperands[0];
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case INSTRUCTION_HALT:
|
|
|
|
|
vmblState->halted = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2025-12-20 16:55:40 +11:00
|
|
|
case INSTRUCTION_EQUAL:
|
2025-12-20 19:42:00 +11:00
|
|
|
MATH_OP(vmblState, ==);
|
2025-12-20 16:55:40 +11:00
|
|
|
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;
|
|
|
|
|
|
2025-12-20 19:42:00 +11:00
|
|
|
case INSTRUCTION_DROP:
|
2025-12-20 16:55:40 +11:00
|
|
|
if (vmblState->stackSize <= 0) {
|
|
|
|
|
return (VMBL_Exception){ EXCEPTION_STACK_UNDERFLOW };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vmblState->stackSize--;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2025-12-21 09:44:16 +11:00
|
|
|
case INSTRUCTION_NOP:
|
|
|
|
|
break;
|
|
|
|
|
|
2025-12-20 13:29:49 +11:00
|
|
|
default:
|
2025-12-19 18:31:41 +11:00
|
|
|
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++) {
|
2025-12-20 16:55:40 +11:00
|
|
|
fprintf(stderr, " 0x%lX: %ld\n", i, vmblState.stack[i]);
|
2025-12-19 18:31:41 +11:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, " [empty]\n");
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 13:29:49 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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++];
|
2025-12-21 09:44:16 +11:00
|
|
|
//printf("%s 0x%lx, 0x%lx, 0x%lx\n", instructionTypeToCStr(instruction.type), instruction.opperands[0], instruction.opperands[1], instruction.opperands[2]);
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
VMBL_Exception exception = VBML_ExecuteInstruction(vmblState, instruction);
|
|
|
|
|
|
|
|
|
|
if (exception.type != EXCEPTION_NONE) {
|
2025-12-20 19:42:00 +11:00
|
|
|
VMBL_Dump(*vmblState, exception);
|
2025-12-20 13:29:49 +11:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 05:47:15 +11:00
|
|
|
memcpy(&vmblState->program, program, programSize);
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
vmblState->programSize = programSize;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 09:44:16 +11:00
|
|
|
void VMBL_LoadExecutableFromFile(VMBL_State *vmblState, char* filePath) {
|
|
|
|
|
/*FILE *file = fopen(filePath, "rb");
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
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];
|
2025-12-21 09:44:16 +11:00
|
|
|
fread(program, sizeof(program[0]), programSize, file);*/
|
|
|
|
|
|
|
|
|
|
VMBL_Instruction *program = (VMBL_Instruction*)readStringFromFile(filePath);
|
2025-12-20 13:29:49 +11:00
|
|
|
|
2025-12-21 09:44:16 +11:00
|
|
|
VMBL_LoadExecutable(vmblState, program, sizeof(program));
|
2025-12-20 13:29:49 +11:00
|
|
|
|
2025-12-21 09:44:16 +11:00
|
|
|
//fclose(file);
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 16:55:40 +11:00
|
|
|
fwrite(program, sizeof(program), programSize, file);
|
2025-12-20 13:29:49 +11:00
|
|
|
fclose(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *instructionTypeToCStr(InstructionType type) {
|
2025-12-21 05:47:15 +11:00
|
|
|
printf("%x\n", type);
|
2025-12-20 13:29:49 +11:00
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
case INSTRUCTION_PUSH:
|
|
|
|
|
return "PUSH";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case INSTRUCTION_ADD:
|
|
|
|
|
return "ADD ";
|
|
|
|
|
break;
|
2025-12-20 19:42:00 +11:00
|
|
|
case INSTRUCTION_SUB:
|
|
|
|
|
return "SUB ";
|
|
|
|
|
break;
|
|
|
|
|
case INSTRUCTION_MUL:
|
|
|
|
|
return "MUL ";
|
|
|
|
|
break;
|
|
|
|
|
case INSTRUCTION_DIV:
|
|
|
|
|
return "DIV ";
|
|
|
|
|
break;
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
case INSTRUCTION_DUPLICATE:
|
|
|
|
|
return "DUP ";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case INSTRUCTION_JUMP:
|
|
|
|
|
return "JMP ";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case INSTRUCTION_HALT:
|
|
|
|
|
return "HALT";
|
|
|
|
|
break;
|
2025-12-20 16:55:40 +11:00
|
|
|
|
|
|
|
|
case INSTRUCTION_EQUAL:
|
|
|
|
|
return "EQ ";
|
|
|
|
|
break;
|
2025-12-21 05:47:15 +11:00
|
|
|
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;
|
2025-12-20 16:55:40 +11:00
|
|
|
|
|
|
|
|
case INSTRUCTION_JUMP_CONDITIONAL:
|
|
|
|
|
return "JC ";
|
|
|
|
|
break;
|
2025-12-20 13:29:49 +11:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return "??? ";
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-12-19 18:31:41 +11:00
|
|
|
}
|