Reworked (de)serialising routines for instructions

No longer relying on darr_t or anything other than the C runtime and
aliases.  This means it should be *even easier* to target this via FFI
from other languages without having to initialise my custom made
structures!  Furthermore I've removed any form of allocation in the
library so FFI callers don't need to manage memory in any way.
Instead we rely on the caller allocating the correct amount of memory
for the functions to work, with basic error handling if that doesn't
happen.

In the case of inst_read_bytecode, error reporting occurs by making
the return of a function an integer.  If the integer is positive it is
the number of bytes read from the buffer.  If negative it flags a
possible error, which is a member of read_err_t.

prog_read_bytecode has been split into two functions: prog_read_header
and prog_read_instructions.  prog_read_instructions works under the
assumption that the program's header has been filled, e.g. via
prog_read_header.  prog_read_header returns 0 if there's not enough
space in the buffer or if the start_address is greater than the count.
prog_read_instructions returns a custom structure which contains an
byte position as well as an error enum, allowing for finer error
reporting.

In the case of inst_write_bytecode via the assumption that the caller
allocated the correct memory there is no need for error reporting.
For prog_write_bytecode if an error occurs due to

In the case of inst_read_bytecode we return the number
This commit is contained in:
2024-04-27 17:22:51 +05:30
parent b9c94d0725
commit 40907e5113
3 changed files with 333 additions and 393 deletions

View File

@@ -276,36 +276,37 @@ void inst_print(inst_t instruction, FILE *fp)
fprintf(fp, ")");
}
size_t inst_bytecode_size(inst_t inst)
size_t opcode_bytecode_size(opcode_t opcode)
{
static_assert(NUMBER_OF_OPCODES == 98, "inst_bytecode_size: Out of date");
size_t size = 1; // for opcode
if (UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_PUSH))
if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH))
{
if (inst.opcode == OP_PUSH_BYTE)
if (opcode == OP_PUSH_BYTE)
++size;
else if (inst.opcode == OP_PUSH_HWORD)
else if (opcode == OP_PUSH_HWORD)
size += HWORD_SIZE;
else if (inst.opcode == OP_PUSH_WORD)
else if (opcode == OP_PUSH_WORD)
size += WORD_SIZE;
}
else if (UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_PUSH_REGISTER) ||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MOV) ||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_DUP) ||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MALLOC) ||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MSET) ||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MGET) ||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_JUMP_IF) ||
inst.opcode == OP_JUMP_ABS || inst.opcode == OP_CALL)
else if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH_REGISTER) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MOV) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_DUP) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MALLOC) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MSET) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MGET) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_JUMP_IF) ||
opcode == OP_JUMP_ABS || opcode == OP_CALL)
size += WORD_SIZE;
return size;
}
void inst_write_bytecode(inst_t inst, darr_t *darr)
size_t inst_write_bytecode(inst_t inst, byte_t *bytes)
{
static_assert(NUMBER_OF_OPCODES == 98, "inst_write_bytecode: Out of date");
// Append opcode
darr_append_byte(darr, inst.opcode);
size_t written = 1;
bytes[0] = inst.opcode;
// Then append 0 or more operands
data_type_t to_append = DATA_TYPE_NIL;
if (UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_PUSH))
@@ -325,72 +326,70 @@ void inst_write_bytecode(inst_t inst, darr_t *darr)
case DATA_TYPE_NIL:
break;
case DATA_TYPE_BYTE:
darr_append_byte(darr, inst.operand.as_byte);
bytes[1] = inst.operand.as_byte;
written += 1;
break;
case DATA_TYPE_HWORD:
darr_ensure_capacity(darr, HWORD_SIZE);
convert_hword_to_bytes(inst.operand.as_hword, darr->data + darr->used);
darr->used += HWORD_SIZE;
convert_hword_to_bytes(inst.operand.as_hword, bytes + 1);
written += HWORD_SIZE;
break;
case DATA_TYPE_WORD:
darr_ensure_capacity(darr, WORD_SIZE);
convert_word_to_bytes(inst.operand.as_word, darr->data + darr->used);
darr->used += WORD_SIZE;
convert_word_to_bytes(inst.operand.as_word, bytes + 1);
written += WORD_SIZE;
break;
}
return written;
}
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)
bool read_type_from_darr(byte_t *bytes, size_t size, data_type_t type,
data_t *data)
{
data_t datum = {0};
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++]);
if (size == 0)
return false;
datum = DBYTE(bytes[0]);
break;
case DATA_TYPE_HWORD:
if (darr->used + HWORD_SIZE > darr->available)
// TODO: Error (darr has no space left)
return DWORD(0);
hword_t u = convert_bytes_to_hword(darr->data + darr->used);
darr->used += HWORD_SIZE;
return DHWORD(u);
if (size < HWORD_SIZE)
return false;
hword_t u = convert_bytes_to_hword(bytes);
datum = 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_t w = convert_bytes_to_word(darr->data + darr->used);
darr->used += WORD_SIZE;
return DWORD(w);
if (size < WORD_SIZE)
return false;
word_t w = convert_bytes_to_word(bytes);
datum = DWORD(w);
break;
default:
return false;
}
// TODO: Error (unrecognised type)
return DBYTE(0);
*data = datum;
return true;
}
inst_t inst_read_bytecode(darr_t *darr)
int inst_read_bytecode(inst_t *ptr, byte_t *bytes, size_t size_bytes)
{
static_assert(NUMBER_OF_OPCODES == 98, "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++];
opcode_t opcode = *(bytes++);
if (opcode > OP_HALT || opcode == NUMBER_OF_OPCODES || opcode < OP_NOOP)
return INST_NOOP;
return READ_ERR_INVALID_OPCODE;
inst_t inst = {opcode, {0}};
--size_bytes;
bool success = true;
// Read operands
if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH))
inst.operand = read_type_from_darr(darr, (data_type_t)opcode);
success = read_type_from_darr(bytes, size_bytes, (data_type_t)opcode,
&inst.operand);
// Read register (as a byte)
else if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH_REGISTER) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MOV) ||
@@ -400,111 +399,90 @@ inst_t inst_read_bytecode(darr_t *darr)
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MGET) ||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_JUMP_IF) ||
opcode == OP_JUMP_ABS || opcode == OP_CALL)
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)
success =
read_type_from_darr(bytes, size_bytes, DATA_TYPE_WORD, &inst.operand);
else
{
inst_t instruction = inst_read_bytecode(bytes);
darr_append_bytes(&instructions, (byte_t *)&instruction,
sizeof(instruction));
// Instruction doesn't take operands
}
*ret_size = instructions.used / sizeof(inst_t);
return (inst_t *)instructions.data;
if (success)
{
*ptr = inst;
return (int)(READ_ERR_END) - (int)(size_bytes);
}
else
return READ_ERR_OPERAND_NO_FIT;
}
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;
}
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);
}
static_assert(sizeof(prog_t) == WORD_SIZE * 2,
static_assert(sizeof(prog_t) == (WORD_SIZE * 2) + sizeof(inst_t *),
"prog_{write|read}_* is out of date");
void prog_write_bytecode(prog_t *program, darr_t *buffer)
size_t prog_bytecode_size(prog_t program)
{
size_t size = WORD_SIZE * 2;
for (size_t i = 0; i < program.count; ++i)
size += opcode_bytecode_size(program.instructions[i].opcode);
return size;
}
size_t prog_write_bytecode(prog_t program, byte_t *bytes, size_t size_bytes)
{
if (size_bytes < PROG_HEADER_SIZE || prog_bytecode_size(program) < size_bytes)
return 0;
// Write program header i.e. the start and count
word_t start = word_htobc(program->start_address);
darr_append_bytes(buffer, (byte_t *)&start, sizeof(start));
word_t count = word_htobc(program->count);
darr_append_bytes(buffer, (byte_t *)&count, sizeof(count));
word_t start = word_htobc(program.start_address);
*(bytes++) = start;
word_t count = word_htobc(program.count);
*(bytes++) = count;
// Write instructions
insts_write_bytecode(program->instructions, program->count, buffer);
}
void prog_append_bytecode(prog_t *program, darr_t *buffer)
{
insts_write_bytecode(program->instructions, program->count, buffer);
}
prog_t *prog_read_bytecode(darr_t *buffer)
{
// TODO: Error (not enough space for program header)
if ((buffer->available - buffer->used) < sizeof(prog_t))
return NULL;
// Read program header
word_t start_address = convert_bytes_to_word(buffer->data + buffer->used);
buffer->used += sizeof(start_address);
word_t count = convert_bytes_to_word(buffer->data + buffer->used);
buffer->used += sizeof(word_t);
// TODO: Error (not enough space for program instruction count)
if ((buffer->available - buffer->used) < WORD_SIZE)
return NULL;
prog_t *program = malloc(sizeof(*program) + (sizeof(inst_t) * count));
size_t i;
for (i = 0; i < count && (buffer->used < buffer->available); ++i)
program->instructions[i] = inst_read_bytecode(buffer);
// TODO: Error (Expected more instructions)
if (i < count - 1)
size_t p_iter = 0, b_iter = PROG_HEADER_SIZE;
for (; p_iter < program.count && b_iter < size_bytes; ++p_iter)
{
free(program);
return NULL;
size_t written =
inst_write_bytecode(program.instructions[p_iter], bytes + b_iter);
if (written == 0)
return 0;
b_iter += written;
}
program->start_address = start_address;
program->count = count;
return program;
return b_iter;
}
void prog_write_file(prog_t *program, FILE *fp)
size_t prog_read_header(prog_t *prog, byte_t *bytes, size_t size_bytes)
{
darr_t bytecode = {0};
prog_write_bytecode(program, &bytecode);
fwrite(bytecode.data, bytecode.used, 1, fp);
free(bytecode.data);
if (size_bytes < PROG_HEADER_SIZE)
return 0;
prog->start_address = convert_bytes_to_word(bytes);
prog->count = convert_bytes_to_word(bytes + WORD_SIZE);
if (prog->start_address >= prog->count)
return 0;
return PROG_HEADER_SIZE;
}
prog_t *prog_read_file(FILE *fp)
read_err_prog_t prog_read_instructions(prog_t *program, size_t *size_bytes_read,
byte_t *bytes, size_t size_bytes)
{
darr_t buffer = darr_read_file(fp);
prog_t *p = prog_read_bytecode(&buffer);
free(buffer.data);
return p;
// If no count then must be empty
if (program->count == 0)
return (read_err_prog_t){0};
size_t program_iter = 0, byte_iter = 0;
for (; program_iter < program->count && byte_iter < size_bytes;
++program_iter)
{
inst_t inst = {0};
int bytes_read =
inst_read_bytecode(&inst, bytes + byte_iter, size_bytes - byte_iter);
if (bytes_read < 0)
return (read_err_prog_t){bytes_read, byte_iter};
byte_iter += bytes_read;
}
if (program_iter < program->count)
return (read_err_prog_t){READ_ERR_EXPECTED_MORE, 0};
*size_bytes_read = byte_iter;
return (read_err_prog_t){0};
}

View File

@@ -13,14 +13,10 @@
#ifndef INST_H
#define INST_H
#include <lib/darr.h>
#include <lib/prog.h>
#include <lib/base.h>
#include <stdio.h>
#include <stdlib.h>
const char *opcode_as_cstr(opcode_t);
#define UNSIGNED_OPCODE_IS_TYPE(OPCODE, OP_TYPE) \
(((OPCODE) >= OP_TYPE##_BYTE) && ((OPCODE) <= OP_TYPE##_WORD))
@@ -29,85 +25,224 @@ const char *opcode_as_cstr(opcode_t);
#define OPCODE_DATA_TYPE(OPCODE, OP_TYPE) (OPCODE - OP_TYPE##_BYTE)
// OPCODE_DATA_TYPE: opcode_t -> data_type_t. data_type_t acts as
// a map between types and their offsets from the first type of
// instruction. That means for opcode_type A and data_type u,
// OP_<A>_BYTE + u = OP_<A>_<u>.
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,
// Dealing with the heap
OP_MALLOC_BYTE,
OP_MALLOC_HWORD,
OP_MALLOC_WORD,
OP_MALLOC_STACK_BYTE,
OP_MALLOC_STACK_HWORD,
OP_MALLOC_STACK_WORD,
OP_MSET_BYTE,
OP_MSET_HWORD,
OP_MSET_WORD,
OP_MSET_STACK_BYTE,
OP_MSET_STACK_HWORD,
OP_MSET_STACK_WORD,
OP_MGET_BYTE,
OP_MGET_HWORD,
OP_MGET_WORD,
OP_MGET_STACK_BYTE,
OP_MGET_STACK_HWORD,
OP_MGET_STACK_WORD,
OP_MDELETE,
OP_MSIZE,
// 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,
OP_SUB_BYTE,
OP_SUB_HWORD,
OP_SUB_WORD,
OP_MULT_BYTE,
OP_MULT_HWORD,
OP_MULT_WORD,
// Comparison operations
OP_LT_BYTE,
OP_LT_CHAR,
OP_LT_HWORD,
OP_LT_INT,
OP_LT_WORD,
OP_LT_LONG,
OP_LTE_BYTE,
OP_LTE_CHAR,
OP_LTE_HWORD,
OP_LTE_INT,
OP_LTE_WORD,
OP_LTE_LONG,
OP_GT_BYTE,
OP_GT_CHAR,
OP_GT_HWORD,
OP_GT_INT,
OP_GT_WORD,
OP_GT_LONG,
OP_GTE_BYTE,
OP_GTE_CHAR,
OP_GTE_HWORD,
OP_GTE_INT,
OP_GTE_WORD,
OP_GTE_LONG,
// Simple I/O
OP_PRINT_BYTE,
OP_PRINT_CHAR,
OP_PRINT_HWORD,
OP_PRINT_INT,
OP_PRINT_WORD,
OP_PRINT_LONG,
// Program control flow
OP_JUMP_ABS,
OP_JUMP_STACK,
OP_JUMP_IF_BYTE,
OP_JUMP_IF_HWORD,
OP_JUMP_IF_WORD,
// Subroutines
OP_CALL,
OP_CALL_STACK,
OP_RET,
// Should not be an opcode
NUMBER_OF_OPCODES,
OP_HALT = 0b11111111, // top of the byte is a HALT
} opcode_t;
size_t opcode_bytecode_size(opcode_t);
const char *opcode_as_cstr(opcode_t);
typedef struct
{
opcode_t opcode;
data_t operand;
} inst_t;
/**
@brief Serialise an instruction into a byte buffer
@details Given an instruction and a suitably sized byte buffer,
write the bytecode for the instruction into the buffer. NOTE: This
function does NOT check the bounds of `bytes` i.e. we assume the
caller has created a suitably sized buffer.
@param[inst] Instruction to serialise
@param[bytes] Buffer to write on
@return[size_t] Number of bytes written to `bytes`.
*/
size_t inst_write_bytecode(inst_t inst, byte_t *bytes);
typedef enum
{
READ_ERR_INVALID_OPCODE = -1,
READ_ERR_OPERAND_NO_FIT = -2,
READ_ERR_EXPECTED_MORE = -3,
READ_ERR_END = -4
} read_err_t;
/**
@brief Deserialise an instruction from a bytecode buffer
@details Given a buffer of bytes, deserialise an instruction,
storing the result in the pointer given. The number of bytes read
in the buffer is returned, which should be opcode_bytecode_size().
NOTE: If bytes is not suitably sized for the instruction expected
or it is not well formed i.e. not the right schema then a negative
number is returned.
@param[inst] Pointer to instruction which will store result
@param[bytes] Bytecode buffer to deserialise
@param[size_bytes] Number of bytes in buffer
@return[int] Number of bytes read. If negative then an error
occurred in deserialisation (either buffer was not suitably sized
or instruction was not well formed) so any result must be
considered invalid.
*/
int inst_read_bytecode(inst_t *inst, byte_t *bytes, size_t size_bytes);
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 *);
typedef struct
{
word_t start_address;
word_t count;
inst_t *instructions;
} prog_t;
void insts_write_bytecode_file(inst_t *, size_t, FILE *);
inst_t *insts_read_bytecode_file(FILE *, size_t *);
#define PROG_HEADER_SIZE (WORD_SIZE * 2)
// Write the entire program as bytecode
void prog_write_bytecode(prog_t *, darr_t *);
// Only append the instructions as bytecode
void prog_append_bytecode(prog_t *, darr_t *);
// Read an entire program as bytecode
prog_t *prog_read_bytecode(darr_t *);
size_t prog_bytecode_size(prog_t);
void prog_write_file(prog_t *, FILE *);
prog_t *prog_read_file(FILE *);
size_t prog_write_bytecode(prog_t program, byte_t *bytes, size_t size_bytes);
#define INST_NOOP ((inst_t){0})
#define INST_HALT ((inst_t){.opcode = OP_HALT})
size_t prog_read_header(prog_t *program, byte_t *bytes, size_t size_bytes);
#define INST_PUSH(TYPE, OP) \
((inst_t){.opcode = OP_PUSH_##TYPE, .operand = D##TYPE(OP)})
typedef struct
{
read_err_t type;
size_t index;
} read_err_prog_t;
#define INST_MOV(TYPE, OP) \
((inst_t){.opcode = OP_MOV_##TYPE, .operand = D##TYPE(OP)})
read_err_prog_t prog_read_instructions(prog_t *program, size_t *size_bytes_read,
byte_t *bytes, size_t size_bytes);
#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_MALLOC(TYPE, OP) \
((inst_t){.opcode = OP_MALLOC_##TYPE, .operand = DWORD(OP)})
#define INST_MALLOC_STACK(TYPE) ((inst_t){.opcode = OP_MALLOC_STACK_##TYPE})
#define INST_MSET(TYPE, OP) \
((inst_t){.opcode = OP_MSET_##TYPE, .operand = DWORD(OP)})
#define INST_MSET_STACK(TYPE) ((inst_t){.opcode = OP_MSET_STACK_##TYPE})
#define INST_MGET(TYPE, OP) \
((inst_t){.opcode = OP_MGET_##TYPE, .operand = DWORD(OP)})
#define INST_MGET_STACK(TYPE) ((inst_t){.opcode = OP_MGET_STACK_##TYPE})
#define INST_MDELETE ((inst_t){.opcode = OP_MDELETE})
#define INST_MSIZE ((inst_t){.opcode = OP_MSIZE})
#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_LT(TYPE) ((inst_t){.opcode = OP_LT_##TYPE})
#define INST_LTE(TYPE) ((inst_t){.opcode = OP_LTE_##TYPE})
#define INST_GT(TYPE) ((inst_t){.opcode = OP_GT_##TYPE})
#define INST_GTE(TYPE) ((inst_t){.opcode = OP_GTE_##TYPE})
#define INST_PLUS(TYPE) ((inst_t){.opcode = OP_PLUS_##TYPE})
#define INST_SUB(TYPE) ((inst_t){.opcode = OP_SUB_##TYPE})
#define INST_MULT(TYPE) ((inst_t){.opcode = OP_MULT_##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_IF(TYPE, OP) \
((inst_t){.opcode = OP_JUMP_IF_##TYPE, .operand = DWORD(OP)})
#define INST_CALL(OP) ((inst_t){.opcode = OP_CALL, .operand = DWORD(OP)})
#define INST_CALL_STACK ((inst_t){.opcode = OP_CALL_STACK})
#define INST_RET ((inst_t){.opcode = OP_RET})
#define INST_PRINT(TYPE) ((inst_t){.opcode = OP_PRINT_##TYPE})
#endif

View File

@@ -1,173 +0,0 @@
/* Copyright (C) 2024 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: 2024-04-14
* Author: Aryadev Chavali
* Description: Structures for both instructions and programs for the
* virtual machine
*/
#ifndef PROG_H
#define PROG_H
#include <lib/base.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,
// Dealing with the heap
OP_MALLOC_BYTE,
OP_MALLOC_HWORD,
OP_MALLOC_WORD,
OP_MALLOC_STACK_BYTE,
OP_MALLOC_STACK_HWORD,
OP_MALLOC_STACK_WORD,
OP_MSET_BYTE,
OP_MSET_HWORD,
OP_MSET_WORD,
OP_MSET_STACK_BYTE,
OP_MSET_STACK_HWORD,
OP_MSET_STACK_WORD,
OP_MGET_BYTE,
OP_MGET_HWORD,
OP_MGET_WORD,
OP_MGET_STACK_BYTE,
OP_MGET_STACK_HWORD,
OP_MGET_STACK_WORD,
OP_MDELETE,
OP_MSIZE,
// 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,
OP_SUB_BYTE,
OP_SUB_HWORD,
OP_SUB_WORD,
OP_MULT_BYTE,
OP_MULT_HWORD,
OP_MULT_WORD,
// Comparison operations
OP_LT_BYTE,
OP_LT_CHAR,
OP_LT_HWORD,
OP_LT_INT,
OP_LT_WORD,
OP_LT_LONG,
OP_LTE_BYTE,
OP_LTE_CHAR,
OP_LTE_HWORD,
OP_LTE_INT,
OP_LTE_WORD,
OP_LTE_LONG,
OP_GT_BYTE,
OP_GT_CHAR,
OP_GT_HWORD,
OP_GT_INT,
OP_GT_WORD,
OP_GT_LONG,
OP_GTE_BYTE,
OP_GTE_CHAR,
OP_GTE_HWORD,
OP_GTE_INT,
OP_GTE_WORD,
OP_GTE_LONG,
// Simple I/O
OP_PRINT_BYTE,
OP_PRINT_CHAR,
OP_PRINT_HWORD,
OP_PRINT_INT,
OP_PRINT_WORD,
OP_PRINT_LONG,
// Program control flow
OP_JUMP_ABS,
OP_JUMP_STACK,
OP_JUMP_IF_BYTE,
OP_JUMP_IF_HWORD,
OP_JUMP_IF_WORD,
// Subroutines
OP_CALL,
OP_CALL_STACK,
OP_RET,
// Should not be an opcode
NUMBER_OF_OPCODES,
OP_HALT = 0b11111111, // top of the byte is a HALT
} opcode_t;
typedef struct
{
opcode_t opcode;
data_t operand;
} inst_t;
typedef struct
{
word_t start_address;
word_t count;
inst_t instructions[];
} prog_t;
#endif