add data, remove labels and make jumps absolute

This commit is contained in:
Peter McGoron 2023-02-18 05:55:04 +00:00
parent d14c655f6a
commit 83c568c8dd
2 changed files with 157 additions and 199 deletions

310
creole.c
View File

@ -30,7 +30,6 @@ enum creole_arg_type {
TYPE_IMM,
TYPE_REG,
TYPE_VAL,
TYPE_LAB,
CREOLE_ARG_TYPE_LEN
};
@ -40,7 +39,7 @@ enum creole_arg_type {
*/
#define defop(s, n, a1, a2, a3) {n, {a1, a2, a3}}
static const struct {
unsigned arglen;
int arglen;
enum creole_arg_type argtype[CREOLE_MAX_ARG];
} opcode_info[CREOLE_OPCODE_LEN] = {
defop(NOOP, 0, TYPE_NONE, TYPE_NONE, TYPE_NONE),
@ -50,11 +49,11 @@ static const struct {
defop(MUL, 3, TYPE_REG, TYPE_VAL, TYPE_VAL),
defop(DIV, 3, TYPE_REG, TYPE_VAL, TYPE_VAL),
defop(SYS, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(CLB, 1, TYPE_LAB, TYPE_NONE, TYPE_NONE),
defop(JL, 3, TYPE_LAB, TYPE_VAL, TYPE_VAL),
defop(JLE, 3, TYPE_LAB, TYPE_VAL, TYPE_VAL),
defop(JE, 3, TYPE_LAB, TYPE_VAL, TYPE_VAL),
defop(JNE, 3, TYPE_LAB, TYPE_VAL, TYPE_VAL)
defop(JL, 3, TYPE_IMM, TYPE_VAL, TYPE_VAL),
defop(JLE, 3, TYPE_IMM, TYPE_VAL, TYPE_VAL),
defop(JE, 3, TYPE_IMM, TYPE_VAL, TYPE_VAL),
defop(JNE, 3, TYPE_IMM, TYPE_VAL, TYPE_VAL),
defop(DB, 1, TYPE_IMM, TYPE_NONE, TYPE_NONE)
};
/*************************************************************************
@ -330,12 +329,49 @@ int creole_encode(creole_word i, unsigned encode_to, unsigned high_bits,
* one single byte of all zeros.
*************************************************************************/
enum creole_compiler_ret
creole_parse_line(struct creole_ins *ins, struct creole_reader *r)
struct ins {
unsigned char *start;
unsigned char *datapt;
enum creole_opcode opcode;
creole_word w[CREOLE_MAX_ARG];
creole_word w_flags[CREOLE_MAX_ARG];
};
static int valid_register(struct creole_env *env, int reg)
{
return reg < env->reglen;
}
static int typecheck_arg(struct creole_env *env, int val,
enum creole_word_flag fl,
enum creole_arg_type typ)
{
switch (typ) {
case TYPE_IMM: return fl == CREOLE_IMMEDIATE;
case TYPE_REG: return fl == CREOLE_REGISTER
&& valid_register(env, val);
case TYPE_VAL: return fl == CREOLE_IMMEDIATE
|| fl == CREOLE_REGISTER;
default: return 0;
}
}
static enum creole_word_flag arg_get_type(unsigned high_bits)
{
if (high_bits & 1) {
return CREOLE_REGISTER;
} else {
return CREOLE_IMMEDIATE;
}
}
static enum creole_compiler_ret
parse_line(struct creole_env *env, struct ins *ins, struct creole_reader *r)
{
struct word w = {0};
unsigned arg = 0;
int i;
ins->start = r->p;
if (!decode_seq(r, &w))
return CREOLE_OPCODE_READ_ERROR;
@ -344,15 +380,34 @@ creole_parse_line(struct creole_ins *ins, struct creole_reader *r)
return CREOLE_OPCODE_MALFORMED;
}
for (arg = 0; arg < opcode_info[ins->opcode].arglen; arg++) {
if (opcode_info[ins->opcode].arglen >= CREOLE_MAX_ARG)
return CREOLE_OPCODE_MALFORMED;
for (i = 0; i < opcode_info[ins->opcode].arglen; i++) {
if (!decode_seq(r, &w))
return CREOLE_ARG_READ_ERROR;
if (w.len == 1)
return CREOLE_ARG_MALFORMED;
ins->w[arg] = w.word;
ins->w_flags[arg] = w.high_bits;
ins->w[i] = w.word;
ins->w_flags[i] = w.high_bits;
if (!typecheck_arg(env, ins->w[i],
arg_get_type(ins->w_flags[i]),
opcode_info[ins->opcode].argtype[i]))
return CREOLE_TYPE_ERROR;
}
if (ins->opcode == CREOLE_DB) {
ins->datapt = r->p;
do {
if (!decode_seq(r, &w))
return CREOLE_ARG_READ_ERROR;
} while (w.len != 1);
if (w.word != 0)
return CREOLE_LAST_READ_ERROR;
return CREOLE_COMPILE_OK;
}
ins->datapt = NULL;
if (!decode_seq(r, &w))
return CREOLE_LAST_READ_ERROR;
if (w.word != 0 || w.len != 1)
@ -364,123 +419,40 @@ creole_parse_line(struct creole_ins *ins, struct creole_reader *r)
* High level compiling interface
*************************************************************************/
static int valid_register(struct creole_env *env, int reg)
static void
add_to_env(struct creole_env *env, struct ins *ins)
{
return reg < env->reglen;
}
static int valid_label(struct creole_env *env, int reg)
{
return reg < env->lablen;
}
static int typecheck(struct creole_env *env, int val,
enum creole_word_flag fl, enum creole_arg_type typ)
{
switch (typ) {
case TYPE_IMM: return fl == CREOLE_IMMEDIATE;
case TYPE_REG: return fl == CREOLE_REGISTER
&& valid_register(env, val);
case TYPE_VAL: return fl == CREOLE_IMMEDIATE
|| fl == CREOLE_REGISTER;
case TYPE_LAB: return fl == CREOLE_IMMEDIATE
&& valid_label(env, val);
default: return 0;
}
}
static enum creole_word_flag get_type_from_high_bit(unsigned high_bits)
{
if (high_bits & 1) {
return CREOLE_REGISTER;
} else {
return CREOLE_IMMEDIATE;
}
}
static enum creole_compiler_ret typecheck_ins(struct creole_env *env,
struct creole_ins *ins)
{
unsigned i;
for (i = 0; i < opcode_info[ins->opcode].arglen; i++) {
if (!typecheck(env, ins->w[i],
get_type_from_high_bit(ins->w_flags[i]),
opcode_info[ins->opcode].argtype[i]))
return CREOLE_TYPE_ERROR;
}
return CREOLE_COMPILE_OK;
}
static void clear_ins(struct creole_ins *i)
{
const struct creole_ins blank = {0};
*i = blank;
}
/****
* Get rid of instructions that can be written out at compile time.
***/
static enum creole_compiler_ret
handle_compiletime_immediate(struct creole_env *env,
struct creole_ins *cur_ins)
{
switch (cur_ins->opcode) {
case CREOLE_CLB:
if (cur_ins->w[0] >= env->lablen)
return CREOLE_LABEL_OVERFLOW;
env->lab[cur_ins->w[0]] = env->prgptr;
/* Delete instruction because it is a compile time
* instruction. Place next instruction in its place. */
clear_ins(cur_ins);
return CREOLE_COMPILE_CLEARED_INSTRUCTION;
case CREOLE_NOOP:
clear_ins(cur_ins);
return CREOLE_COMPILE_CLEARED_INSTRUCTION;
switch (ins->opcode) {
case CREOLE_DB:
env->dats[ins->w[0]] = ins->datapt;
break;
default:
return typecheck_ins(env, cur_ins);
;
}
}
/* TODO: The compile step can be completely removed in favor of directly
* executing the bytecode, disassembling it with creole_parse_line()
* at every instance. This will also make the implementation simpler.
*/
enum creole_compiler_ret
creole_compile(struct creole_env *env, struct creole_reader *r)
creole_compile(struct creole_env *env)
{
struct creole_ins *cur_ins = env->prg;
struct ins ins = {0};
int rcode;
while (env->prgptr < env->prglen) {
rcode = creole_parse_line(cur_ins, r);
env->r_current = env->r_start;
while (!read_eof(&env->r_current)) {
rcode = parse_line(env, &ins, &env->r_current);
if (rcode != CREOLE_COMPILE_OK)
return rcode;
rcode = handle_compiletime_immediate(env, cur_ins);
switch (rcode) {
case CREOLE_COMPILE_CLEARED_INSTRUCTION:
break;
case CREOLE_COMPILE_OK:
cur_ins++;
env->prgptr++;
break;
default:
return rcode;
}
if (read_eof(r))
break;
add_to_env(env, &ins);
}
if (env->prgptr == env->prglen && !read_eof(r))
return CREOLE_PROGRAM_OVERFLOW;
env->prgend = env->prgptr;
env->prgptr = 0;
return CREOLE_COMPILE_OK;
}
/**************************************************************************
* Running and interaction interface
*************************************************************************/
enum creole_run_ret creole_reg_write(struct creole_env *env, unsigned reg,
creole_word w)
{
@ -501,11 +473,11 @@ enum creole_run_ret creole_reg_read(struct creole_env *env, unsigned reg,
}
static enum creole_run_ret read_val(struct creole_env *env,
struct creole_ins *ins,
struct ins *ins,
unsigned arg,
creole_word *w)
{
if (get_type_from_high_bit(ins->w_flags[arg]) == CREOLE_REGISTER) {
if (arg_get_type(ins->w_flags[arg]) == CREOLE_REGISTER) {
return creole_reg_read(env, ins->w[arg], w);
} else {
*w = ins->w[arg];
@ -530,14 +502,6 @@ enum creole_run_ret creole_pop(struct creole_env *env, creole_word *w)
return CREOLE_STEP_CONTINUE;
}
static enum creole_run_ret
check_label(struct creole_env *env, creole_word label)
{
return label < env->lablen
? CREOLE_STEP_CONTINUE
: CREOLE_RUN_LABEL_OVERFLOW;
}
enum argument_signed {
ALL_UNSIGNED = 0, /* 0b00 */
FIRST_SIGNED = 2, /* 0b10 */
@ -556,8 +520,18 @@ static enum argument_signed check_sign_bits(unsigned flags1, unsigned flags2)
return rcode; \
} while(0)
int creole_jump(struct creole_env *env, creole_word off)
{
/* When env->r_start.left == off, this is the end of the program. */
if (env->r_start.left < off)
return 0;
env->r_current.p = env->r_start.p + off;
env->r_current.left = env->r_start.left - off;
return 1;
}
#define chk_sign_op(OPER) do { \
switch (check_sign_bits(ins->w_flags[1], ins->w_flags[2])) { \
switch (check_sign_bits(ins.w_flags[1], ins.w_flags[2])) { \
case ALL_UNSIGNED: \
a1 = a1 OPER a2; \
break; \
@ -577,90 +551,82 @@ static enum argument_signed check_sign_bits(unsigned flags1, unsigned flags2)
enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
{
struct creole_ins *ins = env->prg + env->prgptr;
creole_word a1, a2;
struct ins ins = {0};
creole_word a0, a1, a2;
int rcode = CREOLE_STEP_CONTINUE;
int increase_pointer = 1;
if (env->prgptr == env->prgend)
if (env->r_current.left == 0)
return CREOLE_STEP_STOP;
switch (ins->opcode) {
if (parse_line(env, &ins, &env->r_current) != CREOLE_COMPILE_OK)
return CREOLE_RUN_DECODE_ERROR;
switch (ins.opcode) {
case CREOLE_PUSH:
check(read_val(env, ins, 0, &a1));
check(read_val(env, &ins, 0, &a1));
check(creole_push(env, a1));
break;
case CREOLE_POP:
check(creole_pop(env, &a1));
check(creole_reg_write(env, ins->w[0], a1));
check(creole_reg_write(env, ins.w[0], a1));
break;
case CREOLE_ADD:
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(creole_reg_write(env, ins->w[0], a1 + a2));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
check(creole_reg_write(env, ins.w[0], a1 + a2));
break;
case CREOLE_MUL:
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(creole_reg_write(env, ins->w[0], a1 * a2));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
check(creole_reg_write(env, ins.w[0], a1 * a2));
break;
case CREOLE_DIV:
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
if (a2 == 0) {
return CREOLE_DIV_BY_ZERO;
}
chk_sign_op(/);
check(creole_reg_write(env, ins->w[0], a1));
check(creole_reg_write(env, ins.w[0], a1));
break;
case CREOLE_SYS:
check(read_val(env, ins, 0, sc));
check(read_val(env, &ins, 0, sc));
rcode = CREOLE_STEP_SYSCALL;
break;
case CREOLE_JL:
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(check_label(env, ins->w[0]));
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
chk_sign_op(<);
if (a1) {
env->prgptr = env->lab[ins->w[0]];
increase_pointer = 0;
}
if (a1 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
case CREOLE_JLE:
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(check_label(env, ins->w[0]));
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
chk_sign_op(<=);
if (a1) {
env->prgptr = env->lab[ins->w[0]];
increase_pointer = 0;
}
if (a1 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
case CREOLE_JE:
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(check_label(env, ins->w[0]));
if (a1 == a2) {
env->prgptr = env->lab[ins->w[0]];
increase_pointer = 0;
}
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
if (a1 == a2 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
case CREOLE_JNE:
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(check_label(env, ins->w[0]));
if (a1 != a2) {
env->prgptr = env->lab[ins->w[0]];
increase_pointer = 0;
}
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
if (a1 != a2 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
default:
rcode = CREOLE_STEP_UNKNOWN_OPCODE;
}
if (increase_pointer)
env->prgptr++;
return rcode;
}

View File

@ -41,11 +41,11 @@ enum creole_opcode {
CREOLE_MUL,
CREOLE_DIV,
CREOLE_SYS,
CREOLE_CLB,
CREOLE_JL,
CREOLE_JLE,
CREOLE_JE,
CREOLE_JNE,
CREOLE_DB,
CREOLE_OPCODE_LEN
};
@ -63,7 +63,7 @@ enum creole_compiler_ret {
CREOLE_ARG_MALFORMED,
CREOLE_LAST_READ_ERROR,
CREOLE_LAST_MALFORMED,
CREOLE_LABEL_OVERFLOW,
CREOLE_DATA_OVERFLOW,
CREOLE_TYPE_ERROR,
CREOLE_COMPILE_CLEARED_INSTRUCTION,
CREOLE_PROGRAM_OVERFLOW,
@ -76,45 +76,37 @@ enum creole_run_ret {
CREOLE_STEP_STOP,
CREOLE_STACK_OVERFLOW,
CREOLE_STACK_UNDERFLOW,
CREOLE_RUN_LABEL_OVERFLOW,
CREOLE_RUN_DECODE_ERROR,
CREOLE_REGISTER_OVERFLOW,
CREOLE_STEP_UNKNOWN_OPCODE,
CREOLE_DIV_BY_ZERO,
CREOLE_STEP_HIGH_BIT_MALFORMED,
CREOLE_JUMP_OVERFLOW,
CREOLE_RUN_RET_LEN
};
struct creole_ins {
enum creole_opcode opcode;
unsigned char w_flags[3];
creole_word w[3];
};
struct creole_env {
creole_word *reg;
size_t reglen;
size_t *lab;
size_t lablen;
creole_word *stk;
size_t stkptr, stklen;
struct creole_ins *prg;
size_t prgptr, prgend, prglen;
};
struct creole_reader {
unsigned char *p;
size_t left;
};
struct creole_env {
unsigned char **dats;
size_t datlen;
creole_word *reg;
size_t reglen;
creole_word *stk;
size_t stkptr, stklen;
struct creole_reader r_current;
struct creole_reader r_start;
};
int creole_encode(creole_word i, unsigned encode_to, unsigned high_bits,
unsigned char buf[7]);
enum creole_compiler_ret
creole_parse_line(struct creole_ins *ins, struct creole_reader *r);
enum creole_compiler_ret
creole_compile(struct creole_env *env, struct creole_reader *r);
enum creole_compiler_ret creole_compile(struct creole_env *env);
enum creole_run_ret creole_reg_write(struct creole_env *env, unsigned reg,
creole_word w);