add data, remove labels and make jumps absolute
This commit is contained in:
parent
d14c655f6a
commit
83c568c8dd
310
creole.c
310
creole.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
46
creole.h
46
creole.h
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue