src->vm, Makefile is now a bit more abstracted and pretty colours
Changed folder names for sake of clarity (will be introducing a new build target soon), and Makefile can now easily support more targets.
This commit is contained in:
70
vm/base.h
Normal file
70
vm/base.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the GPLv2
|
||||
* license. You should have received a copy of the GPLv2 license with
|
||||
* this file. If not, please write to: aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Basic types and routines
|
||||
*/
|
||||
|
||||
#ifndef BASE_H
|
||||
#define BASE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ARR_SIZE(xs) (sizeof(xs) / sizeof(xs[0]))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) > (b) ? (b) : (a))
|
||||
|
||||
// Flags
|
||||
#ifndef VERBOSE
|
||||
#define VERBOSE 0
|
||||
#endif
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t i32;
|
||||
typedef int64_t i64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
typedef uint8_t byte;
|
||||
typedef u32 hword;
|
||||
typedef u64 word;
|
||||
|
||||
typedef union
|
||||
{
|
||||
byte as_byte;
|
||||
hword as_hword;
|
||||
word as_word;
|
||||
} data_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DATA_TYPE_NIL = 0,
|
||||
DATA_TYPE_BYTE,
|
||||
DATA_TYPE_HWORD,
|
||||
DATA_TYPE_WORD,
|
||||
} data_type_t;
|
||||
|
||||
#define DBYTE(BYTE) ((data_t){.as_byte = (BYTE)})
|
||||
#define DHWORD(HWORD) ((data_t){.as_hword = (HWORD)})
|
||||
#define DWORD(WORD) ((data_t){.as_word = (WORD)})
|
||||
|
||||
#define HWORD_SIZE sizeof(hword)
|
||||
#define WORD_SIZE sizeof(word)
|
||||
|
||||
// Macros to extract the nth byte or nth hword from a word
|
||||
#define WORD_NTH_BYTE(WORD, N) (((WORD) >> ((N)*8)) & 0xff)
|
||||
#define WORD_NTH_HWORD(WORD, N) (((WORD) >> ((N)*2)) & 0xFFFFFFFF)
|
||||
|
||||
// Assume array contains 4 bytes.
|
||||
hword convert_bytes_to_hword(byte *);
|
||||
void convert_hword_to_bytes(hword, byte *);
|
||||
// Assume array contains 8 bytes.
|
||||
word convert_bytes_to_word(byte *);
|
||||
void convert_word_to_bytes(word, byte *);
|
||||
|
||||
#endif
|
||||
77
vm/darr.c
Normal file
77
vm/darr.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the
|
||||
* GPLv2 license. You should have received a copy of the GPLv2
|
||||
* license with this file. If not, please write to:
|
||||
* aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Dynamically sized byte array
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./darr.h"
|
||||
|
||||
void darr_init(darr_t *darr, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
size = DARR_DEFAULT_SIZE;
|
||||
*darr = (darr_t){
|
||||
.data = calloc(size, 1),
|
||||
.used = 0,
|
||||
.available = size,
|
||||
};
|
||||
}
|
||||
|
||||
void darr_ensure_capacity(darr_t *darr, size_t requested)
|
||||
{
|
||||
if (darr->used + requested >= darr->available)
|
||||
{
|
||||
darr->available =
|
||||
MAX(darr->used + requested, darr->available * DARR_REALLOC_MULT);
|
||||
darr->data = realloc(darr->data, darr->available);
|
||||
}
|
||||
}
|
||||
|
||||
void darr_append_byte(darr_t *darr, byte byte)
|
||||
{
|
||||
darr_ensure_capacity(darr, 1);
|
||||
darr->data[darr->used++] = byte;
|
||||
}
|
||||
|
||||
void darr_append_bytes(darr_t *darr, byte *bytes, size_t n)
|
||||
{
|
||||
darr_ensure_capacity(darr, n);
|
||||
memcpy(darr->data + darr->used, bytes, n);
|
||||
darr->used += n;
|
||||
}
|
||||
|
||||
byte darr_at(darr_t *darr, size_t index)
|
||||
{
|
||||
if (index >= darr->used)
|
||||
// TODO: Error (index is out of bounds)
|
||||
return 0;
|
||||
return darr->data[index];
|
||||
}
|
||||
|
||||
void darr_write_file(darr_t *bytes, FILE *fp)
|
||||
{
|
||||
size_t size = fwrite(bytes->data, bytes->used, 1, fp);
|
||||
assert(size == 1);
|
||||
}
|
||||
|
||||
darr_t darr_read_file(FILE *fp)
|
||||
{
|
||||
darr_t darr = {0};
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long size = ftell(fp);
|
||||
darr_init(&darr, size);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
size_t read = fread(darr.data, size, 1, fp);
|
||||
assert(read == 1);
|
||||
return darr;
|
||||
}
|
||||
39
vm/darr.h
Normal file
39
vm/darr.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the
|
||||
* GPLv2 license. You should have received a copy of the GPLv2
|
||||
* license with this file. If not, please write to:
|
||||
* aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Dynamically sized byte array
|
||||
*/
|
||||
|
||||
#ifndef DARR_H
|
||||
#define DARR_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
byte *data;
|
||||
size_t used, available;
|
||||
} darr_t;
|
||||
|
||||
#define DARR_DEFAULT_SIZE 8
|
||||
#define DARR_REALLOC_MULT 1.5
|
||||
|
||||
void darr_init(darr_t *, size_t);
|
||||
void darr_ensure_capacity(darr_t *, size_t);
|
||||
void darr_append_byte(darr_t *, byte);
|
||||
void darr_append_bytes(darr_t *, byte *, size_t);
|
||||
byte darr_at(darr_t *, size_t);
|
||||
|
||||
void darr_write_file(darr_t *, FILE *);
|
||||
darr_t darr_read_file(FILE *);
|
||||
|
||||
#endif
|
||||
79
vm/fib.c
Normal file
79
vm/fib.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the GPLv2
|
||||
* license. You should have received a copy of the GPLv2 license with
|
||||
* this file. If not, please write to: aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-23
|
||||
* Author: Aryadev Chavali
|
||||
* Description: An example virtual machine program which computes and
|
||||
* prints fibonacci numbers. Note that by default the virtual machine
|
||||
* just rolls overflows over, so this program will never terminate.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./inst.h"
|
||||
#include "./runtime.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
inst_t instructions[] = {
|
||||
// MOV the values 1 and 1 to REG[0] and REG[1] respectively
|
||||
INST_PUSH(WORD, 1),
|
||||
INST_MOV(WORD, 0),
|
||||
INST_PUSH(WORD, 1),
|
||||
INST_MOV(WORD, 1),
|
||||
|
||||
// Print value at register 0 with newline.
|
||||
INST_PUSH_REG(WORD, 0), // <-- #
|
||||
INST_PRINT(WORD),
|
||||
INST_PUSH(BYTE, '\n'),
|
||||
INST_PRINT(CHAR),
|
||||
|
||||
// Print value at register 1 with newline
|
||||
INST_PUSH_REG(WORD, 1),
|
||||
INST_PRINT(WORD),
|
||||
INST_PUSH(BYTE, '\n'),
|
||||
INST_PRINT(CHAR),
|
||||
|
||||
/* Compute the next pair of fibonacci numbers */
|
||||
// REG[0] + REG[1]
|
||||
INST_PUSH_REG(WORD, 0),
|
||||
INST_PUSH_REG(WORD, 1),
|
||||
INST_PLUS(WORD),
|
||||
|
||||
// Mov REG[0] + REG[1] to REG[0]
|
||||
INST_MOV(WORD, 0),
|
||||
|
||||
// REG[0] + REG[1]
|
||||
INST_PUSH_REG(WORD, 0),
|
||||
INST_PUSH_REG(WORD, 1),
|
||||
INST_PLUS(WORD),
|
||||
|
||||
// Mov REG[0] + REG[1] to REG[1]
|
||||
INST_MOV(WORD, 1),
|
||||
|
||||
// Jump to the point #
|
||||
INST_JUMP_ABS(4),
|
||||
INST_HALT,
|
||||
};
|
||||
|
||||
byte stack[256];
|
||||
vm_t vm = {0};
|
||||
vm_load_stack(&vm, stack, ARR_SIZE(stack));
|
||||
vm_load_program(&vm, instructions, ARR_SIZE(instructions));
|
||||
err_t err = vm_execute_all(&vm);
|
||||
|
||||
if (err)
|
||||
{
|
||||
const char *error_str = err_as_cstr(err);
|
||||
fprintf(stderr, "[ERROR]: %s\n", error_str);
|
||||
fprintf(stderr, "[ERROR]: VM Trace:\n");
|
||||
vm_print_all(&vm, stderr);
|
||||
return 255 - err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
399
vm/inst.c
Normal file
399
vm/inst.c
Normal file
@@ -0,0 +1,399 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the
|
||||
* GPLv2 license. You should have received a copy of the GPLv2
|
||||
* license with this file. If not, please write to:
|
||||
* aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Implementation of bytecode for instructions
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./inst.h"
|
||||
|
||||
const char *opcode_as_cstr(opcode_t code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case OP_NOOP:
|
||||
return "NOOP";
|
||||
break;
|
||||
case OP_PUSH_BYTE:
|
||||
return "PUSH_BYTE";
|
||||
break;
|
||||
case OP_PUSH_WORD:
|
||||
return "PUSH_WORD";
|
||||
break;
|
||||
case OP_PUSH_HWORD:
|
||||
return "PUSH_HWORD";
|
||||
break;
|
||||
case OP_PUSH_REGISTER_BYTE:
|
||||
return "PUSH_REGISTER_BYTE";
|
||||
break;
|
||||
case OP_PUSH_REGISTER_WORD:
|
||||
return "PUSH_REGISTER_WORD";
|
||||
break;
|
||||
case OP_PUSH_REGISTER_HWORD:
|
||||
return "PUSH_REGISTER_HWORD";
|
||||
break;
|
||||
case OP_POP_BYTE:
|
||||
return "POP_BYTE";
|
||||
break;
|
||||
case OP_POP_WORD:
|
||||
return "POP_WORD";
|
||||
break;
|
||||
case OP_POP_HWORD:
|
||||
return "POP_HWORD";
|
||||
break;
|
||||
case OP_MOV_BYTE:
|
||||
return "MOV_BYTE";
|
||||
break;
|
||||
case OP_MOV_WORD:
|
||||
return "MOV_WORD";
|
||||
break;
|
||||
case OP_MOV_HWORD:
|
||||
return "MOV_HWORD";
|
||||
break;
|
||||
case OP_DUP_BYTE:
|
||||
return "DUP_BYTE";
|
||||
break;
|
||||
case OP_DUP_HWORD:
|
||||
return "DUP_HWORD";
|
||||
break;
|
||||
case OP_DUP_WORD:
|
||||
return "DUP_WORD";
|
||||
break;
|
||||
case OP_NOT_BYTE:
|
||||
return "NOT_BYTE";
|
||||
break;
|
||||
case OP_NOT_HWORD:
|
||||
return "NOT_HWORD";
|
||||
break;
|
||||
case OP_NOT_WORD:
|
||||
return "NOT_WORD";
|
||||
break;
|
||||
case OP_OR_BYTE:
|
||||
return "OR_BYTE";
|
||||
break;
|
||||
case OP_OR_HWORD:
|
||||
return "OR_HWORD";
|
||||
break;
|
||||
case OP_OR_WORD:
|
||||
return "OR_WORD";
|
||||
break;
|
||||
case OP_AND_BYTE:
|
||||
return "AND_BYTE";
|
||||
break;
|
||||
case OP_AND_HWORD:
|
||||
return "AND_HWORD";
|
||||
break;
|
||||
case OP_AND_WORD:
|
||||
return "AND_WORD";
|
||||
break;
|
||||
case OP_XOR_BYTE:
|
||||
return "XOR_BYTE";
|
||||
break;
|
||||
case OP_XOR_HWORD:
|
||||
return "XOR_HWORD";
|
||||
break;
|
||||
case OP_XOR_WORD:
|
||||
return "XOR_WORD";
|
||||
break;
|
||||
case OP_EQ_BYTE:
|
||||
return "EQ_BYTE";
|
||||
break;
|
||||
case OP_EQ_HWORD:
|
||||
return "EQ_HWORD";
|
||||
break;
|
||||
case OP_EQ_WORD:
|
||||
return "EQ_WORD";
|
||||
break;
|
||||
case OP_PLUS_BYTE:
|
||||
return "PLUS_BYTE";
|
||||
break;
|
||||
case OP_PLUS_HWORD:
|
||||
return "PLUS_HWORD";
|
||||
break;
|
||||
case OP_PLUS_WORD:
|
||||
return "PLUS_WORD";
|
||||
break;
|
||||
case OP_JUMP_ABS:
|
||||
return "JUMP_ABS";
|
||||
break;
|
||||
case OP_JUMP_STACK:
|
||||
return "JUMP_STACK";
|
||||
break;
|
||||
case OP_JUMP_REGISTER:
|
||||
return "JUMP_REGISTER";
|
||||
break;
|
||||
case OP_JUMP_IF_BYTE:
|
||||
return "JUMP_IF_BYTE";
|
||||
break;
|
||||
case OP_JUMP_IF_HWORD:
|
||||
return "JUMP_IF_HWORD";
|
||||
break;
|
||||
case OP_JUMP_IF_WORD:
|
||||
return "JUMP_IF_WORD";
|
||||
break;
|
||||
case OP_PRINT_CHAR:
|
||||
return "PRINT_CHAR";
|
||||
break;
|
||||
case OP_PRINT_BYTE:
|
||||
return "PRINT_BYTE";
|
||||
break;
|
||||
case OP_PRINT_INT:
|
||||
return "PRINT_INT";
|
||||
break;
|
||||
case OP_PRINT_HWORD:
|
||||
return "PRINT_HWORD";
|
||||
break;
|
||||
case OP_PRINT_LONG:
|
||||
return "PRINT_LONG";
|
||||
break;
|
||||
case OP_PRINT_WORD:
|
||||
return "PRINT_WORD";
|
||||
break;
|
||||
case OP_HALT:
|
||||
return "HALT";
|
||||
break;
|
||||
case NUMBER_OF_OPCODES:
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void data_print(data_t datum, data_type_t type, FILE *fp)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DATA_TYPE_NIL:
|
||||
break;
|
||||
case DATA_TYPE_BYTE:
|
||||
fprintf(fp, "%X", datum.as_byte);
|
||||
break;
|
||||
case DATA_TYPE_HWORD:
|
||||
fprintf(fp, "%d", datum.as_hword);
|
||||
break;
|
||||
case DATA_TYPE_WORD:
|
||||
fprintf(fp, "%lX", datum.as_word);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hword convert_bytes_to_hword(byte *bytes)
|
||||
{
|
||||
hword h = 0;
|
||||
memcpy(&h, bytes, HWORD_SIZE);
|
||||
return h;
|
||||
}
|
||||
|
||||
void convert_hword_to_bytes(hword w, byte *bytes)
|
||||
{
|
||||
memcpy(bytes, &w, HWORD_SIZE);
|
||||
}
|
||||
|
||||
void convert_word_to_bytes(word w, byte *bytes)
|
||||
{
|
||||
memcpy(bytes, &w, WORD_SIZE);
|
||||
}
|
||||
|
||||
word convert_bytes_to_word(byte *bytes)
|
||||
{
|
||||
word w = 0;
|
||||
memcpy(&w, bytes, WORD_SIZE);
|
||||
return w;
|
||||
}
|
||||
|
||||
void inst_print(inst_t instruction, FILE *fp)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 46, "inst_bytecode_size: Out of date");
|
||||
fprintf(fp, "%s(", opcode_as_cstr(instruction.opcode));
|
||||
if (OPCODE_IS_TYPE(instruction.opcode, OP_PUSH))
|
||||
{
|
||||
data_type_t type = (data_type_t)instruction.opcode;
|
||||
fprintf(fp, "datum=0x");
|
||||
data_print(instruction.operand, type, fp);
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_PUSH_REGISTER) ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_MOV) ||
|
||||
instruction.opcode == OP_JUMP_REGISTER)
|
||||
{
|
||||
fprintf(fp, "reg=0x");
|
||||
data_print(instruction.operand, DATA_TYPE_BYTE, fp);
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_DUP))
|
||||
{
|
||||
fprintf(fp, "n=%lu", instruction.operand.as_word);
|
||||
}
|
||||
else if (instruction.opcode == OP_JUMP_ABS ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF))
|
||||
{
|
||||
fprintf(fp, "address=0x");
|
||||
data_print(instruction.operand, DATA_TYPE_WORD, fp);
|
||||
}
|
||||
fprintf(fp, ")");
|
||||
}
|
||||
|
||||
size_t inst_bytecode_size(inst_t inst)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 46, "inst_bytecode_size: Out of date");
|
||||
size_t size = 1; // for opcode
|
||||
if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH))
|
||||
{
|
||||
if (inst.opcode == OP_PUSH_BYTE)
|
||||
++size;
|
||||
else if (inst.opcode == OP_PUSH_HWORD)
|
||||
size += HWORD_SIZE;
|
||||
else if (inst.opcode == OP_PUSH_WORD)
|
||||
size += WORD_SIZE;
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH_REGISTER) ||
|
||||
OPCODE_IS_TYPE(inst.opcode, OP_MOV) ||
|
||||
inst.opcode == OP_JUMP_REGISTER)
|
||||
// Only need a byte for the register
|
||||
++size;
|
||||
else if (OPCODE_IS_TYPE(inst.opcode, OP_DUP) || inst.opcode == OP_JUMP_ABS ||
|
||||
OPCODE_IS_TYPE(inst.opcode, OP_JUMP_IF))
|
||||
size += WORD_SIZE;
|
||||
return size;
|
||||
}
|
||||
|
||||
void inst_write_bytecode(inst_t inst, darr_t *darr)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 46, "inst_write_bytecode: Out of date");
|
||||
// Append opcode
|
||||
darr_append_byte(darr, inst.opcode);
|
||||
// Then append 0 or more operands
|
||||
data_type_t to_append = DATA_TYPE_NIL;
|
||||
if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH))
|
||||
to_append = (data_type_t)inst.opcode;
|
||||
else if (OPCODE_IS_TYPE(inst.opcode, OP_PUSH_REGISTER) ||
|
||||
OPCODE_IS_TYPE(inst.opcode, OP_MOV) ||
|
||||
inst.opcode == OP_JUMP_REGISTER)
|
||||
to_append = DATA_TYPE_BYTE;
|
||||
else if (OPCODE_IS_TYPE(inst.opcode, OP_DUP) || inst.opcode == OP_JUMP_ABS ||
|
||||
OPCODE_IS_TYPE(inst.opcode, OP_JUMP_IF))
|
||||
to_append = DATA_TYPE_WORD;
|
||||
|
||||
switch (to_append)
|
||||
{
|
||||
case DATA_TYPE_NIL:
|
||||
break;
|
||||
case DATA_TYPE_BYTE:
|
||||
darr_append_byte(darr, inst.operand.as_byte);
|
||||
break;
|
||||
case DATA_TYPE_HWORD:
|
||||
darr_append_bytes(darr, (byte *)&inst.operand.as_hword, HWORD_SIZE);
|
||||
break;
|
||||
case DATA_TYPE_WORD:
|
||||
darr_append_bytes(darr, (byte *)&inst.operand.as_word, WORD_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void insts_write_bytecode(inst_t *insts, size_t size, darr_t *darr)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
inst_write_bytecode(insts[i], darr);
|
||||
}
|
||||
|
||||
data_t read_type_from_darr(darr_t *darr, data_type_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DATA_TYPE_NIL:
|
||||
break;
|
||||
case DATA_TYPE_BYTE:
|
||||
if (darr->used >= darr->available)
|
||||
// TODO: Error (darr has no space left)
|
||||
return DBYTE(0);
|
||||
return DBYTE(darr->data[darr->used++]);
|
||||
break;
|
||||
case DATA_TYPE_HWORD:
|
||||
if (darr->used + HWORD_SIZE >= darr->available)
|
||||
// TODO: Error (darr has no space left)
|
||||
return DWORD(0);
|
||||
hword u = 0;
|
||||
memcpy(&u, darr->data + darr->used, HWORD_SIZE);
|
||||
darr->used += HWORD_SIZE;
|
||||
return DHWORD(u);
|
||||
break;
|
||||
case DATA_TYPE_WORD:
|
||||
if (darr->used + WORD_SIZE >= darr->available)
|
||||
// TODO: Error (darr has no space left)
|
||||
return DWORD(0);
|
||||
word w = 0;
|
||||
memcpy(&w, darr->data + darr->used, WORD_SIZE);
|
||||
darr->used += WORD_SIZE;
|
||||
return DWORD(w);
|
||||
break;
|
||||
}
|
||||
// TODO: Error (unrecognised type)
|
||||
return DBYTE(0);
|
||||
}
|
||||
|
||||
inst_t inst_read_bytecode(darr_t *darr)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 46, "inst_read_bytecode: Out of date");
|
||||
if (darr->used >= darr->available)
|
||||
return (inst_t){0};
|
||||
inst_t inst = {0};
|
||||
opcode_t opcode = darr->data[darr->used++];
|
||||
if (opcode > OP_HALT || opcode == NUMBER_OF_OPCODES || opcode < OP_NOOP)
|
||||
// Translate to NOOP
|
||||
return inst;
|
||||
// Read operands
|
||||
if (OPCODE_IS_TYPE(opcode, OP_PUSH))
|
||||
inst.operand = read_type_from_darr(darr, (data_type_t)opcode);
|
||||
// Read register (as a byte)
|
||||
else if (OPCODE_IS_TYPE(opcode, OP_PUSH_REGISTER) ||
|
||||
OPCODE_IS_TYPE(opcode, OP_MOV) || inst.opcode == OP_JUMP_STACK)
|
||||
inst.operand = read_type_from_darr(darr, DATA_TYPE_BYTE);
|
||||
else if (OPCODE_IS_TYPE(opcode, OP_DUP) || opcode == OP_JUMP_ABS ||
|
||||
OPCODE_IS_TYPE(opcode, OP_JUMP_IF))
|
||||
inst.operand = read_type_from_darr(darr, DATA_TYPE_WORD);
|
||||
// Otherwise opcode doesn't take operands
|
||||
|
||||
inst.opcode = opcode;
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
inst_t *insts_read_bytecode(darr_t *bytes, size_t *ret_size)
|
||||
{
|
||||
*ret_size = 0;
|
||||
// NOTE: Here we use the darr as a dynamic array of inst_t.
|
||||
darr_t instructions = {0};
|
||||
darr_init(&instructions, sizeof(inst_t));
|
||||
while (bytes->used < bytes->available)
|
||||
{
|
||||
inst_t instruction = inst_read_bytecode(bytes);
|
||||
darr_append_bytes(&instructions, (byte *)&instruction, sizeof(instruction));
|
||||
}
|
||||
*ret_size = instructions.used / sizeof(inst_t);
|
||||
return (inst_t *)instructions.data;
|
||||
}
|
||||
|
||||
void insts_write_bytecode_file(inst_t *instructions, size_t size, FILE *fp)
|
||||
{
|
||||
darr_t darr = {0};
|
||||
darr_init(&darr, 0);
|
||||
insts_write_bytecode(instructions, size, &darr);
|
||||
darr_write_file(&darr, fp);
|
||||
free(darr.data);
|
||||
}
|
||||
|
||||
inst_t *insts_read_bytecode_file(FILE *fp, size_t *ret)
|
||||
{
|
||||
darr_t darr = darr_read_file(fp);
|
||||
inst_t *instructions = insts_read_bytecode(&darr, ret);
|
||||
free(darr.data);
|
||||
return instructions;
|
||||
}
|
||||
156
vm/inst.h
Normal file
156
vm/inst.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the
|
||||
* GPLv2 license. You should have received a copy of the GPLv2
|
||||
* license with this file. If not, please write to:
|
||||
* aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Instructions and opcodes
|
||||
*/
|
||||
|
||||
#ifndef INST_H
|
||||
#define INST_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./base.h"
|
||||
#include "./darr.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OP_NOOP = 0,
|
||||
|
||||
// Dealing with data and registers
|
||||
OP_PUSH_BYTE,
|
||||
OP_PUSH_HWORD,
|
||||
OP_PUSH_WORD,
|
||||
|
||||
OP_POP_BYTE,
|
||||
OP_POP_HWORD,
|
||||
OP_POP_WORD,
|
||||
|
||||
OP_PUSH_REGISTER_BYTE,
|
||||
OP_PUSH_REGISTER_HWORD,
|
||||
OP_PUSH_REGISTER_WORD,
|
||||
|
||||
OP_MOV_BYTE,
|
||||
OP_MOV_HWORD,
|
||||
OP_MOV_WORD,
|
||||
|
||||
OP_DUP_BYTE,
|
||||
OP_DUP_HWORD,
|
||||
OP_DUP_WORD,
|
||||
|
||||
// Boolean operations
|
||||
OP_NOT_BYTE,
|
||||
OP_NOT_HWORD,
|
||||
OP_NOT_WORD,
|
||||
|
||||
OP_OR_BYTE,
|
||||
OP_OR_HWORD,
|
||||
OP_OR_WORD,
|
||||
|
||||
OP_AND_BYTE,
|
||||
OP_AND_HWORD,
|
||||
OP_AND_WORD,
|
||||
|
||||
OP_XOR_BYTE,
|
||||
OP_XOR_HWORD,
|
||||
OP_XOR_WORD,
|
||||
|
||||
OP_EQ_BYTE,
|
||||
OP_EQ_HWORD,
|
||||
OP_EQ_WORD,
|
||||
|
||||
// Mathematical operations
|
||||
OP_PLUS_BYTE,
|
||||
OP_PLUS_HWORD,
|
||||
OP_PLUS_WORD,
|
||||
|
||||
// Simple I/O
|
||||
OP_PRINT_CHAR,
|
||||
OP_PRINT_BYTE,
|
||||
OP_PRINT_INT,
|
||||
OP_PRINT_HWORD,
|
||||
OP_PRINT_LONG,
|
||||
OP_PRINT_WORD,
|
||||
|
||||
// Program control flow
|
||||
OP_JUMP_ABS,
|
||||
OP_JUMP_STACK,
|
||||
OP_JUMP_REGISTER,
|
||||
OP_JUMP_IF_BYTE,
|
||||
OP_JUMP_IF_HWORD,
|
||||
OP_JUMP_IF_WORD,
|
||||
|
||||
// Should not be an opcode
|
||||
NUMBER_OF_OPCODES,
|
||||
OP_HALT = 0b11111111, // top of the byte is a HALT
|
||||
} opcode_t;
|
||||
|
||||
const char *opcode_as_cstr(opcode_t);
|
||||
|
||||
#define OPCODE_IS_TYPE(OPCODE, OP_TYPE) \
|
||||
(((OPCODE) >= OP_TYPE##_BYTE) && ((OPCODE) <= OP_TYPE##_WORD))
|
||||
|
||||
#define OPCODE_DATA_TYPE(OPCODE, OP_TYPE) \
|
||||
((OPCODE) == OP_TYPE##_BYTE ? DATA_TYPE_BYTE \
|
||||
: ((OPCODE) == OP_TYPE##_HWORD) ? DATA_TYPE_HWORD \
|
||||
: DATA_TYPE_WORD)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
opcode_t opcode;
|
||||
data_t operand;
|
||||
} inst_t;
|
||||
|
||||
void inst_print(inst_t, FILE *);
|
||||
|
||||
size_t inst_bytecode_size(inst_t);
|
||||
void inst_write_bytecode(inst_t, darr_t *);
|
||||
void insts_write_bytecode(inst_t *, size_t, darr_t *);
|
||||
// Here the dynamic array is a preloaded buffer of bytes, where
|
||||
// darr.available is the number of overall bytes and used is the
|
||||
// cursor (where we are in the buffer).
|
||||
inst_t inst_read_bytecode(darr_t *);
|
||||
inst_t *insts_read_bytecode(darr_t *, size_t *);
|
||||
|
||||
void insts_write_bytecode_file(inst_t *, size_t, FILE *);
|
||||
inst_t *insts_read_bytecode_file(FILE *, size_t *);
|
||||
|
||||
#define INST_NOOP ((inst_t){0})
|
||||
#define INST_HALT ((inst_t){.opcode = OP_HALT})
|
||||
|
||||
#define INST_PUSH(TYPE, OP) \
|
||||
((inst_t){.opcode = OP_PUSH_##TYPE, .operand = D##TYPE(OP)})
|
||||
|
||||
#define INST_MOV(TYPE, OP) \
|
||||
((inst_t){.opcode = OP_MOV_##TYPE, .operand = D##TYPE(OP)})
|
||||
|
||||
#define INST_POP(TYPE) ((inst_t){.opcode = OP_POP_##TYPE})
|
||||
|
||||
#define INST_PUSH_REG(TYPE, REG) \
|
||||
((inst_t){.opcode = OP_PUSH_REGISTER_##TYPE, .operand = D##TYPE(REG)})
|
||||
|
||||
#define INST_DUP(TYPE, OP) \
|
||||
((inst_t){.opcode = OP_DUP_##TYPE, .operand = DWORD(OP)})
|
||||
|
||||
#define INST_NOT(TYPE) ((inst_t){.opcode = OP_NOT_##TYPE})
|
||||
#define INST_OR(TYPE) ((inst_t){.opcode = OP_OR_##TYPE})
|
||||
#define INST_AND(TYPE) ((inst_t){.opcode = OP_AND_##TYPE})
|
||||
#define INST_XOR(TYPE) ((inst_t){.opcode = OP_XOR_##TYPE})
|
||||
#define INST_EQ(TYPE) ((inst_t){.opcode = OP_EQ_##TYPE})
|
||||
#define INST_PLUS(TYPE) ((inst_t){.opcode = OP_PLUS_##TYPE})
|
||||
|
||||
#define INST_JUMP_ABS(OP) \
|
||||
((inst_t){.opcode = OP_JUMP_ABS, .operand = DWORD(OP)})
|
||||
#define INST_JUMP_STACK ((inst_t){.opcode = OP_JUMP_STACK})
|
||||
#define INST_JUMP_REGISTER ((inst_t){.opcode = OP_JUMP_REGISTER})
|
||||
#define INST_JUMP_IF(TYPE, OP) \
|
||||
((inst_t){.opcode = OP_JUMP_IF_##TYPE, .operand = DWORD(OP)})
|
||||
|
||||
#define INST_PRINT(TYPE) ((inst_t){.opcode = OP_PRINT_##TYPE})
|
||||
#endif
|
||||
62
vm/main.c
Normal file
62
vm/main.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the
|
||||
* GPLv2 license. You should have received a copy of the GPLv2
|
||||
* license with this file. If not, please write to:
|
||||
* aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Entrypoint to program
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./inst.h"
|
||||
#include "./runtime.h"
|
||||
|
||||
int interpret_bytecode(const char *filepath)
|
||||
{
|
||||
FILE *fp = fopen(filepath, "rb");
|
||||
size_t number = 0;
|
||||
inst_t *instructions = insts_read_bytecode_file(fp, &number);
|
||||
fclose(fp);
|
||||
|
||||
byte stack[256];
|
||||
vm_t vm = {0};
|
||||
vm_load_stack(&vm, stack, ARR_SIZE(stack));
|
||||
vm_load_program(&vm, instructions, number);
|
||||
err_t err = vm_execute_all(&vm);
|
||||
|
||||
int ret = 0;
|
||||
if (err)
|
||||
{
|
||||
const char *error_str = err_as_cstr(err);
|
||||
fprintf(stderr, "[ERROR]: %s\n", error_str);
|
||||
vm_print_all(&vm, stderr);
|
||||
ret = 255 - err;
|
||||
}
|
||||
free(instructions);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int assemble_instructions(inst_t *instructions, size_t number,
|
||||
const char *filepath)
|
||||
{
|
||||
FILE *fp = fopen(filepath, "wb");
|
||||
insts_write_bytecode_file(instructions, number, fp);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *filename = "out.bin";
|
||||
if (argc >= 2)
|
||||
filename = argv[1];
|
||||
inst_t instructions[] = {INST_HALT};
|
||||
assemble_instructions(instructions, ARR_SIZE(instructions), filename);
|
||||
return interpret_bytecode(filename);
|
||||
}
|
||||
777
vm/runtime.c
Normal file
777
vm/runtime.c
Normal file
@@ -0,0 +1,777 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the
|
||||
* GPLv2 license. You should have received a copy of the GPLv2
|
||||
* license with this file. If not, please write to:
|
||||
* aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Virtual machine implementation
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./runtime.h"
|
||||
|
||||
const char *err_as_cstr(err_t err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case ERR_OK:
|
||||
return "OK";
|
||||
break;
|
||||
case ERR_STACK_UNDERFLOW:
|
||||
return "STACK_UNDERFLOW";
|
||||
break;
|
||||
case ERR_STACK_OVERFLOW:
|
||||
return "STACK_OVERFLOW";
|
||||
break;
|
||||
case ERR_INVALID_OPCODE:
|
||||
return "INVALID_OPCODE";
|
||||
break;
|
||||
case ERR_INVALID_REGISTER_BYTE:
|
||||
return "INVALID_REGISTER_BYTE";
|
||||
break;
|
||||
case ERR_INVALID_REGISTER_HWORD:
|
||||
return "INVALID_REGISTER_HWORD";
|
||||
break;
|
||||
case ERR_INVALID_REGISTER_WORD:
|
||||
return "INVALID_REGISTER_WORD";
|
||||
break;
|
||||
case ERR_INVALID_PROGRAM_ADDRESS:
|
||||
return "INVALID_PROGRAM_ADDRESS";
|
||||
case ERR_END_OF_PROGRAM:
|
||||
return "END_OF_PROGRAM";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
err_t vm_execute(vm_t *vm)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 46, "vm_execute: Out of date");
|
||||
struct Program *prog = &vm->program;
|
||||
if (prog->ptr >= prog->max)
|
||||
return ERR_END_OF_PROGRAM;
|
||||
inst_t instruction = prog->instructions[prog->ptr];
|
||||
|
||||
if (OPCODE_IS_TYPE(instruction.opcode, OP_PUSH))
|
||||
{
|
||||
prog->ptr++;
|
||||
return PUSH_ROUTINES[instruction.opcode](vm, instruction.operand);
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_MOV) ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_PUSH_REGISTER))
|
||||
{
|
||||
prog->ptr++;
|
||||
return REG_ROUTINES[instruction.opcode](vm, instruction.operand.as_byte);
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_POP))
|
||||
{
|
||||
// NOTE: We use the first register to hold the result of this pop
|
||||
data_type_t type = OPCODE_DATA_TYPE(instruction.opcode, OP_POP);
|
||||
prog->ptr++;
|
||||
switch (type)
|
||||
{
|
||||
case DATA_TYPE_NIL:
|
||||
break;
|
||||
case DATA_TYPE_BYTE:
|
||||
return vm_mov_byte(vm, 0);
|
||||
break;
|
||||
case DATA_TYPE_HWORD:
|
||||
return vm_mov_hword(vm, 0);
|
||||
break;
|
||||
case DATA_TYPE_WORD:
|
||||
return vm_mov_word(vm, 0);
|
||||
break;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_DUP))
|
||||
{
|
||||
prog->ptr++;
|
||||
return DUP_ROUTINES[instruction.opcode](vm, instruction.operand.as_word);
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_NOT) ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_OR) ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_AND) ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_XOR) ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_EQ) ||
|
||||
OPCODE_IS_TYPE(instruction.opcode, OP_PLUS))
|
||||
{
|
||||
prog->ptr++;
|
||||
return STACK_ROUTINES[instruction.opcode](vm);
|
||||
}
|
||||
else if (instruction.opcode == OP_JUMP_ABS)
|
||||
return vm_jump(vm, instruction.operand.as_word);
|
||||
else if (instruction.opcode == OP_JUMP_STACK)
|
||||
{
|
||||
// Set prog->ptr to the word on top of the stack
|
||||
data_t ret = {0};
|
||||
err_t err = vm_pop_word(vm, &ret);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_jump(vm, ret.as_word);
|
||||
}
|
||||
else if (instruction.opcode == OP_JUMP_REGISTER)
|
||||
{
|
||||
if (instruction.operand.as_byte >= 8)
|
||||
return ERR_INVALID_REGISTER_WORD;
|
||||
word addr = vm->registers.reg[instruction.operand.as_byte];
|
||||
return vm_jump(vm, addr);
|
||||
}
|
||||
else if (OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF))
|
||||
{
|
||||
data_t datum = {0};
|
||||
err_t err = ERR_OK;
|
||||
if (instruction.opcode == OP_JUMP_IF_BYTE)
|
||||
err = vm_pop_byte(vm, &datum);
|
||||
else if (instruction.opcode == OP_JUMP_IF_HWORD)
|
||||
err = vm_pop_hword(vm, &datum);
|
||||
else if (instruction.opcode == OP_JUMP_IF_WORD)
|
||||
err = vm_pop_word(vm, &datum);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
// If datum != 0 then jump, else go to the next instruction
|
||||
if (datum.as_word != 0)
|
||||
return vm_jump(vm, instruction.operand.as_word);
|
||||
else
|
||||
++prog->ptr;
|
||||
}
|
||||
else if (instruction.opcode >= OP_PRINT_CHAR &&
|
||||
instruction.opcode <= OP_PRINT_WORD)
|
||||
{
|
||||
data_t datum = {0};
|
||||
enum
|
||||
{
|
||||
TYPE_CHAR,
|
||||
TYPE_BYTE,
|
||||
TYPE_INT,
|
||||
TYPE_HWORD,
|
||||
TYPE_LONG,
|
||||
TYPE_WORD
|
||||
} print_type;
|
||||
err_t err = ERR_OK;
|
||||
if (instruction.opcode == OP_PRINT_BYTE ||
|
||||
instruction.opcode == OP_PRINT_CHAR)
|
||||
{
|
||||
print_type = instruction.opcode == OP_PRINT_BYTE ? TYPE_BYTE : TYPE_CHAR;
|
||||
err = vm_pop_byte(vm, &datum);
|
||||
}
|
||||
else if (instruction.opcode == OP_PRINT_HWORD ||
|
||||
instruction.opcode == OP_PRINT_INT)
|
||||
{
|
||||
print_type = instruction.opcode == OP_PRINT_HWORD ? TYPE_HWORD : TYPE_INT;
|
||||
err = vm_pop_hword(vm, &datum);
|
||||
}
|
||||
else if (instruction.opcode == OP_PRINT_WORD ||
|
||||
instruction.opcode == OP_PRINT_LONG)
|
||||
{
|
||||
print_type = instruction.opcode == OP_PRINT_WORD ? TYPE_WORD : TYPE_LONG;
|
||||
err = vm_pop_word(vm, &datum);
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (print_type)
|
||||
{
|
||||
case TYPE_CHAR: {
|
||||
char c = 0;
|
||||
memcpy(&c, &datum.as_byte, 1);
|
||||
printf("%c", c);
|
||||
break;
|
||||
}
|
||||
case TYPE_BYTE:
|
||||
printf("0x%x", datum.as_byte);
|
||||
break;
|
||||
case TYPE_INT: {
|
||||
int32_t i = 0;
|
||||
memcpy(&i, &datum.as_hword, HWORD_SIZE);
|
||||
printf("%" PRId32, i);
|
||||
break;
|
||||
}
|
||||
case TYPE_HWORD:
|
||||
printf("%" PRIu32, datum.as_hword);
|
||||
break;
|
||||
case TYPE_LONG: {
|
||||
int64_t i = 0;
|
||||
memcpy(&i, &datum.as_word, WORD_SIZE);
|
||||
printf("%" PRId64, i);
|
||||
break;
|
||||
}
|
||||
case TYPE_WORD:
|
||||
printf("%" PRIu64, datum.as_word);
|
||||
break;
|
||||
}
|
||||
|
||||
prog->ptr++;
|
||||
}
|
||||
else if (instruction.opcode == OP_HALT)
|
||||
{
|
||||
// Do nothing here. Should be caught by callers of vm_execute
|
||||
}
|
||||
else
|
||||
return ERR_INVALID_OPCODE;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_execute_all(vm_t *vm)
|
||||
{
|
||||
struct Program *program = &vm->program;
|
||||
err_t err = ERR_OK;
|
||||
#if VERBOSE == 1
|
||||
struct Registers prev_registers = vm->registers;
|
||||
size_t cycles = 0;
|
||||
size_t prev_sptr = 0;
|
||||
#endif
|
||||
while (program->instructions[program->ptr].opcode != OP_HALT &&
|
||||
program->ptr < program->max)
|
||||
{
|
||||
#if VERBOSE >= 1
|
||||
fprintf(stdout, "[vm_execute_all]: Trace(Cycle %lu)\n", cycles);
|
||||
fputs(
|
||||
"----------------------------------------------------------------------"
|
||||
"----------\n",
|
||||
stdout);
|
||||
vm_print_program(vm, stdout);
|
||||
fputs(
|
||||
"----------------------------------------------------------------------"
|
||||
"----------\n",
|
||||
stdout);
|
||||
if (memcmp(prev_registers.reg, vm->registers.reg,
|
||||
ARR_SIZE(vm->registers.reg)) != 0)
|
||||
{
|
||||
vm_print_registers(vm, stdout);
|
||||
prev_registers = vm->registers;
|
||||
fputs("------------------------------------------------------------------"
|
||||
"----"
|
||||
"----------\n",
|
||||
stdout);
|
||||
}
|
||||
if (prev_sptr != vm->stack.ptr)
|
||||
{
|
||||
vm_print_stack(vm, stdout);
|
||||
prev_sptr = vm->stack.ptr;
|
||||
fputs("------------------------------------------------------------------"
|
||||
"----"
|
||||
"----------\n",
|
||||
stdout);
|
||||
}
|
||||
++cycles;
|
||||
#endif
|
||||
err = vm_execute(vm);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
#if VERBOSE >= 1
|
||||
fprintf(stdout, "[vm_execute_all]: Final VM state(Cycle %lu)\n", cycles);
|
||||
vm_print_all(vm, stdout);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
void vm_load_stack(vm_t *vm, byte *bytes, size_t size)
|
||||
{
|
||||
vm->stack.data = bytes;
|
||||
vm->stack.max = size;
|
||||
vm->stack.ptr = 0;
|
||||
}
|
||||
|
||||
void vm_load_program(vm_t *vm, inst_t *instructions, size_t size)
|
||||
{
|
||||
vm->program.instructions = instructions;
|
||||
vm->program.max = size;
|
||||
vm->program.ptr = 0;
|
||||
}
|
||||
|
||||
void vm_print_registers(vm_t *vm, FILE *fp)
|
||||
{
|
||||
struct Registers reg = vm->registers;
|
||||
fprintf(fp, "Registers.reg = [");
|
||||
for (size_t i = 0; i < VM_REGISTERS; ++i)
|
||||
{
|
||||
fprintf(fp, "{%lu:%lX}", i, reg.reg[i]);
|
||||
if (i != VM_REGISTERS - 1)
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
fprintf(fp, "]\n");
|
||||
}
|
||||
|
||||
void vm_print_stack(vm_t *vm, FILE *fp)
|
||||
{
|
||||
struct Stack stack = vm->stack;
|
||||
fprintf(fp, "Stack.max = %lu\nStack.ptr = %lu\nStack.data = [", stack.max,
|
||||
stack.ptr);
|
||||
if (stack.ptr == 0)
|
||||
{
|
||||
fprintf(fp, "]\n");
|
||||
return;
|
||||
}
|
||||
printf("\n");
|
||||
for (size_t i = stack.ptr; i > 0; --i)
|
||||
{
|
||||
byte b = stack.data[i - 1];
|
||||
fprintf(fp, "\t%lu: %X", stack.ptr - i, b);
|
||||
if (i != 1)
|
||||
fprintf(fp, ", ");
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
fprintf(fp, "]\n");
|
||||
}
|
||||
|
||||
void vm_print_program(vm_t *vm, FILE *fp)
|
||||
{
|
||||
struct Program program = vm->program;
|
||||
fprintf(fp,
|
||||
"Program.max = %lu\nProgram.ptr = "
|
||||
"%lu\nProgram.instructions = [\n",
|
||||
program.max, program.ptr);
|
||||
size_t beg = 0;
|
||||
if (program.ptr >= VM_PRINT_PROGRAM_EXCERPT)
|
||||
{
|
||||
fprintf(fp, "\t...\n");
|
||||
beg = program.ptr - VM_PRINT_PROGRAM_EXCERPT;
|
||||
}
|
||||
else
|
||||
beg = 0;
|
||||
size_t end = MIN(program.ptr + VM_PRINT_PROGRAM_EXCERPT, program.max);
|
||||
for (size_t i = beg; i < end; ++i)
|
||||
{
|
||||
fprintf(fp, "\t%lu: ", i);
|
||||
inst_print(program.instructions[i], fp);
|
||||
if (i == program.ptr)
|
||||
fprintf(fp, " <---");
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
if (end != program.max)
|
||||
fprintf(fp, "\t...\n");
|
||||
fprintf(fp, "]\n");
|
||||
}
|
||||
|
||||
void vm_print_all(vm_t *vm, FILE *fp)
|
||||
{
|
||||
fputs("----------------------------------------------------------------------"
|
||||
"----------\n",
|
||||
fp);
|
||||
vm_print_program(vm, fp);
|
||||
fputs("----------------------------------------------------------------------"
|
||||
"----------\n",
|
||||
fp);
|
||||
vm_print_registers(vm, fp);
|
||||
fputs("----------------------------------------------------------------------"
|
||||
"----------\n",
|
||||
fp);
|
||||
vm_print_stack(vm, fp);
|
||||
fputs("----------------------------------------------------------------------"
|
||||
"----------\n",
|
||||
fp);
|
||||
}
|
||||
|
||||
err_t vm_jump(vm_t *vm, word w)
|
||||
{
|
||||
if (w >= vm->program.max)
|
||||
return ERR_INVALID_PROGRAM_ADDRESS;
|
||||
vm->program.ptr = w;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_push_byte(vm_t *vm, data_t b)
|
||||
{
|
||||
if (vm->stack.ptr >= vm->stack.max)
|
||||
return ERR_STACK_OVERFLOW;
|
||||
vm->stack.data[vm->stack.ptr++] = b.as_byte;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_push_hword(vm_t *vm, data_t f)
|
||||
{
|
||||
if (vm->stack.ptr + HWORD_SIZE >= vm->stack.max)
|
||||
return ERR_STACK_OVERFLOW;
|
||||
byte bytes[HWORD_SIZE] = {0};
|
||||
convert_hword_to_bytes(f.as_hword, bytes);
|
||||
for (size_t i = 0; i < HWORD_SIZE; ++i)
|
||||
{
|
||||
byte b = bytes[HWORD_SIZE - i - 1];
|
||||
vm_push_byte(vm, DBYTE(b));
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_push_word(vm_t *vm, data_t w)
|
||||
{
|
||||
if (vm->stack.ptr + WORD_SIZE >= vm->stack.max)
|
||||
return ERR_STACK_OVERFLOW;
|
||||
byte bytes[WORD_SIZE] = {0};
|
||||
convert_word_to_bytes(w.as_word, bytes);
|
||||
for (size_t i = 0; i < WORD_SIZE; ++i)
|
||||
{
|
||||
byte b = bytes[WORD_SIZE - i - 1];
|
||||
vm_push_byte(vm, DBYTE(b));
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_push_byte_register(vm_t *vm, byte reg)
|
||||
{
|
||||
if (reg >= VM_REGISTERS * 8)
|
||||
return ERR_INVALID_REGISTER_BYTE;
|
||||
|
||||
// Interpret each word based register as 8 byte registers
|
||||
byte b = WORD_NTH_BYTE(vm->registers.reg[reg / 8], reg % 8);
|
||||
|
||||
return vm_push_byte(vm, DBYTE(b));
|
||||
}
|
||||
|
||||
err_t vm_push_hword_register(vm_t *vm, byte reg)
|
||||
{
|
||||
if (reg >= VM_REGISTERS * 2)
|
||||
return ERR_INVALID_REGISTER_HWORD;
|
||||
else if (vm->stack.ptr >= vm->stack.max)
|
||||
return ERR_STACK_OVERFLOW;
|
||||
// Interpret each word based register as 2 hword registers
|
||||
hword hw = WORD_NTH_HWORD(vm->registers.reg[reg / 2], reg % 2);
|
||||
return vm_push_hword(vm, DHWORD(hw));
|
||||
}
|
||||
|
||||
err_t vm_push_word_register(vm_t *vm, byte reg)
|
||||
{
|
||||
if (reg >= VM_REGISTERS)
|
||||
return ERR_INVALID_REGISTER_WORD;
|
||||
else if (vm->stack.ptr >= vm->stack.max)
|
||||
return ERR_STACK_OVERFLOW;
|
||||
return vm_push_word(vm, DWORD(vm->registers.reg[reg]));
|
||||
}
|
||||
|
||||
err_t vm_mov_byte(vm_t *vm, byte reg)
|
||||
{
|
||||
if (reg >= (VM_REGISTERS * 8))
|
||||
return ERR_INVALID_REGISTER_BYTE;
|
||||
data_t ret = {0};
|
||||
err_t err = vm_pop_byte(vm, &ret);
|
||||
if (err)
|
||||
return err;
|
||||
word *reg_ptr = &vm->registers.reg[reg / 8];
|
||||
size_t shift = (reg % 8) * 8;
|
||||
// This resets the bits in the specific byte register
|
||||
*reg_ptr = *reg_ptr & ~(0xFF << shift);
|
||||
// This sets the bits
|
||||
*reg_ptr = (*reg_ptr) | (ret.as_word << shift);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_mov_hword(vm_t *vm, byte reg)
|
||||
{
|
||||
if (reg >= (VM_REGISTERS * 2))
|
||||
return ERR_INVALID_REGISTER_HWORD;
|
||||
else if (vm->stack.ptr < sizeof(f64))
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
data_t ret = {0};
|
||||
err_t err = vm_pop_hword(vm, &ret);
|
||||
if (err)
|
||||
return err;
|
||||
word *reg_ptr = &vm->registers.reg[reg / 2];
|
||||
size_t shift = (reg % 2) * 2;
|
||||
// This resets the bits in the specific hword register
|
||||
*reg_ptr = *reg_ptr & ~(0xFFFFFFFF << shift);
|
||||
// This sets the bits
|
||||
*reg_ptr = (*reg_ptr) | (ret.as_word << shift);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_mov_word(vm_t *vm, byte reg)
|
||||
{
|
||||
if (reg >= VM_REGISTERS)
|
||||
return ERR_INVALID_REGISTER_WORD;
|
||||
else if (vm->stack.ptr < sizeof(word))
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
data_t ret = {0};
|
||||
err_t err = vm_pop_word(vm, &ret);
|
||||
if (err)
|
||||
return err;
|
||||
vm->registers.reg[reg] = ret.as_word;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_dup_byte(vm_t *vm, word w)
|
||||
{
|
||||
if (vm->stack.ptr < w + 1)
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
return vm_push_byte(vm, DBYTE(vm->stack.data[vm->stack.ptr - 1 - w]));
|
||||
}
|
||||
|
||||
err_t vm_dup_hword(vm_t *vm, word w)
|
||||
{
|
||||
if (vm->stack.ptr < HWORD_SIZE * (w + 1))
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
byte bytes[HWORD_SIZE] = {0};
|
||||
for (size_t i = 0; i < HWORD_SIZE; ++i)
|
||||
bytes[HWORD_SIZE - i - 1] =
|
||||
vm->stack.data[vm->stack.ptr - (HWORD_SIZE * (w + 1)) + i];
|
||||
return vm_push_hword(vm, DHWORD(convert_bytes_to_hword(bytes)));
|
||||
}
|
||||
|
||||
err_t vm_dup_word(vm_t *vm, word w)
|
||||
{
|
||||
if (vm->stack.ptr < WORD_SIZE * (w + 1))
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
byte bytes[WORD_SIZE] = {0};
|
||||
for (size_t i = 0; i < WORD_SIZE; ++i)
|
||||
bytes[WORD_SIZE - i - 1] =
|
||||
vm->stack.data[vm->stack.ptr - (WORD_SIZE * (w + 1)) + i];
|
||||
return vm_push_word(vm, DWORD(convert_bytes_to_word(bytes)));
|
||||
}
|
||||
|
||||
err_t vm_pop_byte(vm_t *vm, data_t *ret)
|
||||
{
|
||||
if (vm->stack.ptr == 0)
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
*ret = DBYTE(vm->stack.data[--vm->stack.ptr]);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_pop_hword(vm_t *vm, data_t *ret)
|
||||
{
|
||||
if (vm->stack.ptr < HWORD_SIZE)
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
byte bytes[HWORD_SIZE] = {0};
|
||||
for (size_t i = 0; i < HWORD_SIZE; ++i)
|
||||
{
|
||||
data_t b = {0};
|
||||
vm_pop_byte(vm, &b);
|
||||
bytes[i] = b.as_byte;
|
||||
}
|
||||
*ret = DWORD(convert_bytes_to_hword(bytes));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_pop_word(vm_t *vm, data_t *ret)
|
||||
{
|
||||
if (vm->stack.ptr < WORD_SIZE)
|
||||
return ERR_STACK_UNDERFLOW;
|
||||
byte bytes[WORD_SIZE] = {0};
|
||||
for (size_t i = 0; i < WORD_SIZE; ++i)
|
||||
{
|
||||
data_t b = {0};
|
||||
vm_pop_byte(vm, &b);
|
||||
bytes[i] = b.as_byte;
|
||||
}
|
||||
*ret = DWORD(convert_bytes_to_word(bytes));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t vm_not_byte(vm_t *vm)
|
||||
{
|
||||
data_t a = {0};
|
||||
err_t err = vm_pop_byte(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_byte(vm, DBYTE(!a.as_byte));
|
||||
}
|
||||
|
||||
err_t vm_not_hword(vm_t *vm)
|
||||
{
|
||||
data_t a = {0};
|
||||
err_t err = vm_pop_hword(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_hword(vm, DHWORD(!a.as_hword));
|
||||
}
|
||||
|
||||
err_t vm_not_word(vm_t *vm)
|
||||
{
|
||||
data_t a = {0};
|
||||
err_t err = vm_pop_word(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_word(vm, DWORD(!a.as_word));
|
||||
}
|
||||
|
||||
err_t vm_or_byte(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_byte(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_byte(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_byte(vm, DBYTE(a.as_byte | b.as_byte));
|
||||
}
|
||||
|
||||
err_t vm_or_hword(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_hword(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_hword(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_hword(vm, DHWORD(a.as_hword | b.as_hword));
|
||||
}
|
||||
|
||||
err_t vm_or_word(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_word(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_word(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_word(vm, DWORD(a.as_word | b.as_word));
|
||||
}
|
||||
|
||||
err_t vm_and_byte(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_byte(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_byte(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_byte(vm, DBYTE(a.as_byte & b.as_byte));
|
||||
}
|
||||
|
||||
err_t vm_and_hword(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_hword(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_hword(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_hword(vm, DHWORD(a.as_hword & b.as_hword));
|
||||
}
|
||||
|
||||
err_t vm_and_word(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_word(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_word(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_word(vm, DWORD(a.as_word & b.as_word));
|
||||
}
|
||||
|
||||
err_t vm_xor_byte(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_byte(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_byte(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_byte(vm, DBYTE(a.as_byte ^ b.as_byte));
|
||||
}
|
||||
|
||||
err_t vm_xor_hword(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_hword(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_hword(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_hword(vm, DHWORD(a.as_hword ^ b.as_hword));
|
||||
}
|
||||
|
||||
err_t vm_xor_word(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_word(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_word(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_word(vm, DWORD(a.as_word ^ b.as_word));
|
||||
}
|
||||
|
||||
err_t vm_eq_byte(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_byte(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_byte(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_byte(vm, DBYTE(a.as_byte == b.as_byte));
|
||||
}
|
||||
|
||||
err_t vm_eq_hword(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_hword(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_hword(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_hword(vm, DHWORD(a.as_hword == b.as_hword));
|
||||
}
|
||||
|
||||
err_t vm_eq_word(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_word(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_word(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_word(vm, DWORD(a.as_word == b.as_word));
|
||||
}
|
||||
|
||||
err_t vm_plus_byte(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_byte(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_byte(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_byte(vm, DBYTE(a.as_byte + b.as_byte));
|
||||
}
|
||||
|
||||
err_t vm_plus_hword(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_hword(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_hword(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_hword(vm, DHWORD(a.as_hword + b.as_hword));
|
||||
}
|
||||
|
||||
err_t vm_plus_word(vm_t *vm)
|
||||
{
|
||||
data_t a = {0}, b = {0};
|
||||
err_t err = vm_pop_word(vm, &a);
|
||||
if (err)
|
||||
return err;
|
||||
err = vm_pop_word(vm, &b);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_push_word(vm, DWORD(a.as_word + b.as_word));
|
||||
}
|
||||
165
vm/runtime.h
Normal file
165
vm/runtime.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/* Copyright (C) 2023 Aryadev Chavali
|
||||
|
||||
* You may distribute and modify this code under the terms of the
|
||||
* GPLv2 license. You should have received a copy of the GPLv2
|
||||
* license with this file. If not, please write to:
|
||||
* aryadev@aryadevchavali.com.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Virtual machine implementation
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_H
|
||||
#define RUNTIME_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./base.h"
|
||||
#include "./inst.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ERR_OK = 0,
|
||||
ERR_STACK_UNDERFLOW,
|
||||
ERR_STACK_OVERFLOW,
|
||||
ERR_INVALID_OPCODE,
|
||||
ERR_INVALID_REGISTER_BYTE,
|
||||
ERR_INVALID_REGISTER_HWORD,
|
||||
ERR_INVALID_REGISTER_WORD,
|
||||
ERR_INVALID_PROGRAM_ADDRESS,
|
||||
ERR_END_OF_PROGRAM,
|
||||
} err_t;
|
||||
|
||||
const char *err_as_cstr(err_t);
|
||||
|
||||
#define VM_REGISTERS 8
|
||||
typedef struct
|
||||
{
|
||||
struct Registers
|
||||
{
|
||||
word reg[VM_REGISTERS];
|
||||
} registers;
|
||||
struct Stack
|
||||
{
|
||||
byte *data;
|
||||
word ptr, max;
|
||||
} stack;
|
||||
struct Program
|
||||
{
|
||||
inst_t *instructions;
|
||||
word ptr, max;
|
||||
} program;
|
||||
} vm_t;
|
||||
|
||||
#define VM_REG_BYTE(REG) ((REG)&0b11111111)
|
||||
#define VM_REG_HWORD(REG) ((REG)&0b11111111111111111111111111111111)
|
||||
#define VM_REG_WORD(REG) ((REG))
|
||||
|
||||
err_t vm_execute(vm_t *);
|
||||
err_t vm_execute_all(vm_t *);
|
||||
|
||||
void vm_load_stack(vm_t *, byte *, size_t);
|
||||
void vm_load_program(vm_t *, inst_t *, size_t);
|
||||
|
||||
// Print routines
|
||||
#define VM_PRINT_PROGRAM_EXCERPT 5
|
||||
void vm_print_registers(vm_t *, FILE *);
|
||||
void vm_print_stack(vm_t *, FILE *);
|
||||
void vm_print_program(vm_t *, FILE *);
|
||||
void vm_print_all(vm_t *, FILE *);
|
||||
|
||||
// Execution routines
|
||||
err_t vm_jump(vm_t *, word);
|
||||
|
||||
err_t vm_pop_byte(vm_t *, data_t *);
|
||||
err_t vm_pop_hword(vm_t *, data_t *);
|
||||
err_t vm_pop_word(vm_t *, data_t *);
|
||||
|
||||
err_t vm_push_byte(vm_t *, data_t);
|
||||
err_t vm_push_hword(vm_t *, data_t);
|
||||
err_t vm_push_word(vm_t *, data_t);
|
||||
|
||||
typedef err_t (*push_f)(vm_t *, data_t);
|
||||
static const push_f PUSH_ROUTINES[] = {
|
||||
[OP_PUSH_BYTE] = vm_push_byte,
|
||||
[OP_PUSH_HWORD] = vm_push_hword,
|
||||
[OP_PUSH_WORD] = vm_push_word,
|
||||
};
|
||||
|
||||
err_t vm_push_byte_register(vm_t *, byte);
|
||||
err_t vm_push_hword_register(vm_t *, byte);
|
||||
err_t vm_push_word_register(vm_t *, byte);
|
||||
|
||||
err_t vm_mov_byte(vm_t *, byte);
|
||||
err_t vm_mov_hword(vm_t *, byte);
|
||||
err_t vm_mov_word(vm_t *, byte);
|
||||
|
||||
typedef err_t (*reg_f)(vm_t *, byte);
|
||||
static const reg_f REG_ROUTINES[] = {
|
||||
[OP_PUSH_REGISTER_BYTE] = vm_push_byte_register,
|
||||
[OP_PUSH_REGISTER_HWORD] = vm_push_hword_register,
|
||||
[OP_PUSH_REGISTER_WORD] = vm_push_word_register,
|
||||
[OP_MOV_BYTE] = vm_mov_byte,
|
||||
[OP_MOV_HWORD] = vm_mov_hword,
|
||||
[OP_MOV_WORD] = vm_mov_word,
|
||||
};
|
||||
|
||||
err_t vm_dup_byte(vm_t *, word);
|
||||
err_t vm_dup_hword(vm_t *, word);
|
||||
err_t vm_dup_word(vm_t *, word);
|
||||
|
||||
typedef err_t (*dup_f)(vm_t *, word);
|
||||
static const dup_f DUP_ROUTINES[] = {
|
||||
[OP_DUP_BYTE] = vm_dup_byte,
|
||||
[OP_DUP_HWORD] = vm_dup_hword,
|
||||
[OP_DUP_WORD] = vm_dup_word,
|
||||
};
|
||||
|
||||
err_t vm_not_byte(vm_t *);
|
||||
err_t vm_not_hword(vm_t *);
|
||||
err_t vm_not_word(vm_t *);
|
||||
|
||||
err_t vm_or_byte(vm_t *);
|
||||
err_t vm_or_hword(vm_t *);
|
||||
err_t vm_or_word(vm_t *);
|
||||
|
||||
err_t vm_and_byte(vm_t *);
|
||||
err_t vm_and_hword(vm_t *);
|
||||
err_t vm_and_word(vm_t *);
|
||||
|
||||
err_t vm_xor_byte(vm_t *);
|
||||
err_t vm_xor_hword(vm_t *);
|
||||
err_t vm_xor_word(vm_t *);
|
||||
|
||||
err_t vm_eq_byte(vm_t *);
|
||||
err_t vm_eq_hword(vm_t *);
|
||||
err_t vm_eq_word(vm_t *);
|
||||
|
||||
err_t vm_plus_byte(vm_t *);
|
||||
err_t vm_plus_hword(vm_t *);
|
||||
err_t vm_plus_word(vm_t *);
|
||||
|
||||
typedef err_t (*stack_f)(vm_t *);
|
||||
static const stack_f STACK_ROUTINES[] = {
|
||||
[OP_NOT_BYTE] = vm_not_byte, [OP_NOT_HWORD] = vm_not_hword,
|
||||
[OP_NOT_WORD] = vm_not_word,
|
||||
|
||||
[OP_OR_BYTE] = vm_or_byte, [OP_OR_HWORD] = vm_or_hword,
|
||||
[OP_OR_WORD] = vm_or_word,
|
||||
|
||||
[OP_AND_BYTE] = vm_and_byte, [OP_AND_HWORD] = vm_and_hword,
|
||||
[OP_AND_WORD] = vm_and_word,
|
||||
|
||||
[OP_XOR_BYTE] = vm_xor_byte, [OP_XOR_HWORD] = vm_xor_hword,
|
||||
[OP_XOR_WORD] = vm_xor_word,
|
||||
|
||||
[OP_EQ_BYTE] = vm_eq_byte, [OP_EQ_HWORD] = vm_eq_hword,
|
||||
[OP_EQ_WORD] = vm_eq_word,
|
||||
|
||||
[OP_PLUS_BYTE] = vm_plus_byte, [OP_PLUS_HWORD] = vm_plus_hword,
|
||||
[OP_PLUS_WORD] = vm_plus_word,
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user