Compare commits
2 Commits
c32251b5a0
...
d8c9336261
Author | SHA1 | Date | |
---|---|---|---|
d8c9336261 | |||
bdff748461 |
686
asm/assemble.c
Normal file
686
asm/assemble.c
Normal file
@ -0,0 +1,686 @@
|
||||
#include "asm.h"
|
||||
#include "eval.h"
|
||||
#include "parse.h"
|
||||
#include "report.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum {
|
||||
// clang-format off
|
||||
M_err, M_d8, M_d16, M_nop, M_hlt, M_jmp,
|
||||
M_jmpf, M_jnz, M_cmp, M_mov, M_in, M_out,
|
||||
M_call, M_callf, M_ret, M_retf, M_lit, M_int,
|
||||
M_iret, M_or, M_xor, M_and, M_shl, M_rshl,
|
||||
M_shr, M_rshr, M_add, M_sub, M_rsub, M_mul,
|
||||
M_imul, M_div, M_idiv, M_rdiv, M_ridiv, M_mod,
|
||||
M_rmod, M_push, M_pop
|
||||
// clang-format on
|
||||
} Mnemonic;
|
||||
|
||||
static const char* mnemonic_str[] = {
|
||||
// clang-format off
|
||||
"err", "d8", "d16", "nop", "hlt", "jmp",
|
||||
"jmpf", "jnz", "cmp", "mov", "in", "out",
|
||||
"call", "callf", "ret", "retf", "lit", "int",
|
||||
"iret", "or", "xor", "and", "shl", "rshl",
|
||||
"shr", "rshr", "add", "sub", "rsub", "mul",
|
||||
"imul", "div", "idiv", "rdiv", "ridiv", "mod",
|
||||
"rmod", "push", "pop"
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
uint16_t pline_assemble(OperandEvaluator* evaluator,
|
||||
uint16_t* object,
|
||||
const PLine* line,
|
||||
Reporter* rep)
|
||||
{
|
||||
|
||||
#define CHECK_OPERAND(OP) \
|
||||
do { \
|
||||
if ((OP).ty == EoTy_Err) { \
|
||||
return 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSEMBLE_ONE(RVAL) \
|
||||
do { \
|
||||
Line l = (RVAL); \
|
||||
return assemble_line(object, &l); \
|
||||
} while (0)
|
||||
|
||||
size_t mnemonics_amount = sizeof(mnemonic_str) / sizeof(mnemonic_str[0]);
|
||||
Mnemonic m = M_err;
|
||||
for (size_t i = 0; i < mnemonics_amount; ++i) {
|
||||
if (strcmp(mnemonic_str[i], line->op) == 0) {
|
||||
m = (Mnemonic)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (m) {
|
||||
case M_err: {
|
||||
REPORTF_ERROR("unrecognized mnemonic '%s'", line->op);
|
||||
reporter_print_loc(rep, line->loc);
|
||||
return 0;
|
||||
}
|
||||
case M_d8: {
|
||||
if (line->ops_size > 64) {
|
||||
reporter_error_with_loc(
|
||||
rep, "too many operands (max is 64)", line->loc);
|
||||
return 0;
|
||||
}
|
||||
size_t buffer_capacity = 128;
|
||||
uint8_t* buffer = malloc(sizeof(uint8_t) * buffer_capacity);
|
||||
size_t buffer_size = 0;
|
||||
for (size_t i = 0; i < line->ops_size; ++i) {
|
||||
EvaledOperand val = eval_operand(evaluator, line->ops[i]);
|
||||
CHECK_OPERAND(val);
|
||||
switch (val.ty) {
|
||||
case EoTy_Imm:
|
||||
buffer[buffer_size++] = (uint8_t)val.imm;
|
||||
break;
|
||||
case EoTy_Str: {
|
||||
for (size_t si = 0; si < line->ops[i]->str_len; ++si) {
|
||||
buffer[buffer_size++]
|
||||
= (uint8_t)line->ops[i]->str[si];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reporter_error_with_loc(
|
||||
rep, "invalid operand", line->ops[i]->loc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
uint16_t ip_diff = 0;
|
||||
for (size_t i = 0; i < buffer_size; i += 2) {
|
||||
uint16_t data = 0;
|
||||
// XXX: little endian
|
||||
data |= buffer[i];
|
||||
if (i + 1 < buffer_size) {
|
||||
data |= (uint16_t)((uint16_t)buffer[i] << 8);
|
||||
}
|
||||
Line l = s_data_i(data);
|
||||
ip_diff += assemble_line(object, &l);
|
||||
}
|
||||
return ip_diff;
|
||||
}
|
||||
case M_d16: {
|
||||
if (line->ops_size > 32) {
|
||||
reporter_error_with_loc(
|
||||
rep, "too many operands (max is 32)", line->loc);
|
||||
return 0;
|
||||
}
|
||||
uint16_t ip_diff = 0;
|
||||
for (size_t i = 0; i < line->ops_size; ++i) {
|
||||
EvaledOperand val = eval_operand(evaluator, line->ops[i]);
|
||||
CHECK_OPERAND(val);
|
||||
switch (val.ty) {
|
||||
case EoTy_Imm: {
|
||||
Line l = s_data_i(val.imm);
|
||||
ip_diff += assemble_line(object, &l);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reporter_error_with_loc(
|
||||
rep, "invalid operand", line->ops[i]->loc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ip_diff;
|
||||
}
|
||||
case M_nop:
|
||||
if (line->ops_size == 0)
|
||||
ASSEMBLE_ONE(s_nop());
|
||||
break;
|
||||
case M_hlt:
|
||||
if (line->ops_size == 0)
|
||||
ASSEMBLE_ONE(s_hlt());
|
||||
break;
|
||||
case M_jmp:
|
||||
if (line->ops_size == 1) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
if (op1.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_jmp_r(op1.reg));
|
||||
if (op1.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_jmp_i(op1.imm));
|
||||
}
|
||||
break;
|
||||
case M_jmpf:
|
||||
if (line->ops_size == 2) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_jmpf_r_r(op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_jmpf_r_i(op1.reg, op2.imm));
|
||||
}
|
||||
if (op1.ty == EoTy_Imm) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_jmpf_i_r(op1.imm, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_jmpf_i_i(op1.imm, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_jnz:
|
||||
if (line->ops_size == 2) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_jnz_r(op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_jnz_i(op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_cmp:
|
||||
if (line->ops_size == 2) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_cmp_r(op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_cmp_i(op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_mov:
|
||||
if (line->ops_size == 2) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_mov16_r_r(op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_mov16_r_i(op1.reg, op2.imm));
|
||||
if (op2.ty == EoTy_Mem8Reg)
|
||||
ASSEMBLE_ONE(s_mov8_r_mr(op1.reg, op2.reg, op2.offset));
|
||||
if (op2.ty == EoTy_Mem8Imm)
|
||||
ASSEMBLE_ONE(s_mov8_r_mi(op1.reg, op2.imm));
|
||||
if (op2.ty == EoTy_MemU16Reg)
|
||||
ASSEMBLE_ONE(
|
||||
s_mov16_r_mr(op1.reg, op2.reg, op2.offset));
|
||||
if (op2.ty == EoTy_MemU16Imm) {
|
||||
ASSEMBLE_ONE(s_mov16_r_mi(op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
if (op1.ty == EoTy_Mem8Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_mov8_mr_r(op1.reg, op1.offset, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_mov8_mr_i(op1.reg, op1.offset, op2.imm));
|
||||
}
|
||||
if (op1.ty == EoTy_Mem8Imm) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_mov8_mi_r(op1.imm, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_mov8_mi_i(op1.imm, op2.imm));
|
||||
}
|
||||
if (op1.ty == EoTy_MemU16Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(
|
||||
s_mov16_mr_r(op1.reg, op1.offset, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(
|
||||
s_mov16_mr_i(op1.reg, op1.offset, op2.imm));
|
||||
}
|
||||
if (op1.ty == EoTy_MemU16Imm) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_mov16_mi_r(op1.imm, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_mov16_mi_i(op1.imm, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_in:
|
||||
if (line->ops_size == 2) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
if (dst.ty == EoTy_Reg) {
|
||||
if (op1.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_in_r(dst.reg, op1.reg));
|
||||
if (op1.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_in_i(dst.reg, op1.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_out:
|
||||
if (line->ops_size == 2) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_out_r_r(op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_out_r_i(op1.reg, op2.imm));
|
||||
}
|
||||
if (op1.ty == EoTy_Imm) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_out_i_r(op1.imm, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_out_i_i(op1.imm, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_call:
|
||||
if (line->ops_size == 1) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
if (op1.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_call_r(op1.reg));
|
||||
if (op1.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_call_i(op1.imm));
|
||||
}
|
||||
break;
|
||||
case M_callf:
|
||||
if (line->ops_size == 2) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_callf_r_r(op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_callf_r_i(op1.reg, op2.imm));
|
||||
}
|
||||
if (op1.ty == EoTy_Imm) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_callf_i_r(op1.imm, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_callf_i_i(op1.imm, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_ret:
|
||||
if (line->ops_size == 0) {
|
||||
ASSEMBLE_ONE(s_ret());
|
||||
}
|
||||
break;
|
||||
case M_retf:
|
||||
if (line->ops_size == 0) {
|
||||
ASSEMBLE_ONE(s_retf());
|
||||
}
|
||||
break;
|
||||
case M_lit:
|
||||
if (line->ops_size == 1) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
if (op1.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_lit_r(op1.reg));
|
||||
if (op1.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_lit_i(op1.imm));
|
||||
}
|
||||
break;
|
||||
case M_int:
|
||||
if (line->ops_size == 1) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
if (op1.ty == EoTy_Imm) {
|
||||
if (op1.imm > 0xff) {
|
||||
reporter_error_with_loc(
|
||||
rep, "interrupt id exceeds 1 byte", line->loc);
|
||||
return 0;
|
||||
}
|
||||
ASSEMBLE_ONE(s_int((uint8_t)op1.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_iret:
|
||||
if (line->ops_size == 0) {
|
||||
ASSEMBLE_ONE(s_iret());
|
||||
}
|
||||
break;
|
||||
case M_or:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_or_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm) {
|
||||
ASSEMBLE_ONE(s_or_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_xor:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_xor_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_xor_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_and:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_and_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_and_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_shl:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_shl_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_shl_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_rshl:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_rshl_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_rshl_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_shr:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_shr_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_shr_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_rshr:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_rshr_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_rshr_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_add:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_add_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_add_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_sub:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_sub_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_sub_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_rsub:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_rsub_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_rsub_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_mul:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_mul_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_mul_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_imul:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_imul_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_imul_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_div:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_div_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_div_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_idiv:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_idiv_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_idiv_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_rdiv:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_rdiv_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_rdiv_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_ridiv:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_ridiv_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_ridiv_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_mod:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_mod_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_mod_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_rmod:
|
||||
if (line->ops_size == 3) {
|
||||
EvaledOperand dst = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(dst);
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[1]);
|
||||
CHECK_OPERAND(op1);
|
||||
EvaledOperand op2 = eval_operand(evaluator, line->ops[2]);
|
||||
CHECK_OPERAND(op2);
|
||||
if (dst.ty == EoTy_Reg && op1.ty == EoTy_Reg) {
|
||||
if (op2.ty == EoTy_Reg)
|
||||
ASSEMBLE_ONE(s_rmod_r(dst.reg, op1.reg, op2.reg));
|
||||
if (op2.ty == EoTy_Imm)
|
||||
ASSEMBLE_ONE(s_rmod_i(dst.reg, op1.reg, op2.imm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_push:
|
||||
if (line->ops_size == 1) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
uint16_t size = 0;
|
||||
Line l;
|
||||
l = s_add_i(Rsp, Rsp, 2);
|
||||
size += assemble_line(object, &l);
|
||||
l = s_mov16_mr_r(Rsp, 0, op1.reg);
|
||||
size += assemble_line(object, &l);
|
||||
return size;
|
||||
}
|
||||
if (op1.ty == EoTy_Imm) {
|
||||
uint16_t size = 0;
|
||||
Line l;
|
||||
l = s_add_i(Rsp, Rsp, 2);
|
||||
size += assemble_line(object, &l);
|
||||
l = s_mov16_mr_i(Rsp, 0, op1.imm);
|
||||
size += assemble_line(object, &l);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M_pop:
|
||||
if (line->ops_size == 1) {
|
||||
EvaledOperand op1 = eval_operand(evaluator, line->ops[0]);
|
||||
CHECK_OPERAND(op1);
|
||||
if (op1.ty == EoTy_Reg) {
|
||||
uint16_t size = 0;
|
||||
Line l;
|
||||
l = s_mov16_r_mr(op1.reg, Rsp, 0);
|
||||
size += assemble_line(object, &l);
|
||||
l = s_sub_i(Rsp, Rsp, 2);
|
||||
size += assemble_line(object, &l);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
reporter_error_with_loc(rep, "malformed instruction", line->loc);
|
||||
return 0;
|
||||
|
||||
#undef CHECK_OPERAND
|
||||
#undef ASSEMBLE_ONE
|
||||
}
|
11
asm/assemble.h
Normal file
11
asm/assemble.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "eval.h"
|
||||
#include "parse.h"
|
||||
#include "report.h"
|
||||
#include <stdint.h>
|
||||
|
||||
uint16_t pline_assemble(OperandEvaluator* evaluator,
|
||||
uint16_t* object,
|
||||
const PLine* line,
|
||||
Reporter* rep);
|
304
asm/eval.c
Normal file
304
asm/eval.c
Normal file
@ -0,0 +1,304 @@
|
||||
#include "eval.h"
|
||||
#include "parse.h"
|
||||
#include "report.h"
|
||||
#include "resolve.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline uint16_t eval_poperandty_unary(POperandTy ty, uint16_t operand)
|
||||
{
|
||||
switch (ty) {
|
||||
case PoTy_Not:
|
||||
return ~operand;
|
||||
case PoTy_Negate:
|
||||
return (uint16_t)-(int16_t)operand;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t eval_poperandty_binary(
|
||||
POperandTy ty, uint16_t left, uint16_t right)
|
||||
{
|
||||
switch (ty) {
|
||||
case PoTy_Or:
|
||||
return left | right;
|
||||
case PoTy_Xor:
|
||||
return left ^ right;
|
||||
case PoTy_And:
|
||||
return left & right;
|
||||
case PoTy_Shl:
|
||||
return (uint16_t)(left << right);
|
||||
case PoTy_Shr:
|
||||
return (uint16_t)(left >> right);
|
||||
case PoTy_Add:
|
||||
return (uint16_t)((int16_t)left + (int16_t)right);
|
||||
case PoTy_Sub:
|
||||
return (uint16_t)((int16_t)left - (int16_t)right);
|
||||
case PoTy_Mul:
|
||||
return (uint16_t)((int16_t)left * (int16_t)right);
|
||||
case PoTy_Div:
|
||||
return (uint16_t)((int16_t)left / (int16_t)right);
|
||||
case PoTy_Mod:
|
||||
return (uint16_t)((int16_t)left % (int16_t)right);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
EvaledOperand eval_operand_to_imm(
|
||||
OperandEvaluator* evaluator, POperand* operand)
|
||||
{
|
||||
switch (operand->ty) {
|
||||
case PoTy_Str:
|
||||
REPORTF_ERROR("%s", "strings cannot be part of expressions");
|
||||
reporter_print_loc(evaluator->rep, operand->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
case PoTy_Mem8:
|
||||
case PoTy_Mem16:
|
||||
REPORTF_ERROR("%s", "indirections cannot be part of expressions");
|
||||
reporter_print_loc(evaluator->rep, operand->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
case PoTy_Reg:
|
||||
REPORTF_ERROR("%s", "registers cannot be part of expressions");
|
||||
reporter_print_loc(evaluator->rep, operand->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
case PoTy_Imm:
|
||||
return (EvaledOperand) { .ty = EoTy_Imm, .imm = operand->imm };
|
||||
case PoTy_Ident:
|
||||
case PoTy_SubLabel: {
|
||||
const IdentResol* re
|
||||
= ident_resolver_resolve(evaluator->re, operand->str);
|
||||
if (re == NULL) {
|
||||
if (!evaluator->second_pass) {
|
||||
return (EvaledOperand) { .ty = EoTy_Imm, .imm = 0 };
|
||||
}
|
||||
REPORTF_ERROR("undefined identifier '%s'", operand->str);
|
||||
reporter_print_loc(evaluator->rep, operand->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
}
|
||||
switch (re->ty) {
|
||||
case IdentResolTy_None:
|
||||
break;
|
||||
case IdentResolTy_Label:
|
||||
case IdentResolTy_SubLabel:
|
||||
return (EvaledOperand) { .ty = EoTy_Imm, .imm = re->ip };
|
||||
}
|
||||
fprintf(stderr, "unreachable\n");
|
||||
exit(1);
|
||||
}
|
||||
case PoTy_Not:
|
||||
case PoTy_Negate: {
|
||||
EvaledOperand inner
|
||||
= eval_operand_to_imm(evaluator, operand->operand);
|
||||
if (inner.ty == EoTy_Err) {
|
||||
return inner;
|
||||
} else if (inner.ty != EoTy_Imm) {
|
||||
REPORTF_ERROR("%s", "operand cannot be used in expressions");
|
||||
reporter_print_loc(evaluator->rep, operand->operand->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Imm,
|
||||
.imm = eval_poperandty_unary(operand->ty, inner.imm),
|
||||
};
|
||||
}
|
||||
case PoTy_Or:
|
||||
case PoTy_Xor:
|
||||
case PoTy_And:
|
||||
case PoTy_Shl:
|
||||
case PoTy_Shr:
|
||||
case PoTy_Add:
|
||||
case PoTy_Sub:
|
||||
case PoTy_Mul:
|
||||
case PoTy_Div:
|
||||
case PoTy_Mod: {
|
||||
EvaledOperand left = eval_operand_to_imm(evaluator, operand->left);
|
||||
if (left.ty == EoTy_Err) {
|
||||
return left;
|
||||
} else if (left.ty != EoTy_Imm) {
|
||||
REPORTF_ERROR("%s", "operand cannot be used in expressions");
|
||||
reporter_print_loc(evaluator->rep, operand->left->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
}
|
||||
EvaledOperand right
|
||||
= eval_operand_to_imm(evaluator, operand->right);
|
||||
if (right.ty == EoTy_Err) {
|
||||
return right;
|
||||
} else if (right.ty != EoTy_Imm) {
|
||||
REPORTF_ERROR("%s", "operand cannot be used in expressions");
|
||||
reporter_print_loc(evaluator->rep, operand->right->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Imm,
|
||||
.imm = eval_poperandty_binary(operand->ty, left.imm, right.imm),
|
||||
};
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "unreachable\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
EvaledOperand eval_operand_indirection_expr(
|
||||
OperandEvaluator* evaluator, POperand* operand)
|
||||
{
|
||||
switch (operand->ty) {
|
||||
case PoTy_Reg:
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Reg,
|
||||
.reg = operand->reg,
|
||||
.offset = 0,
|
||||
};
|
||||
case PoTy_Str:
|
||||
REPORTF_ERROR("%s", "strings cannot be part of indirections");
|
||||
reporter_print_loc(evaluator->rep, operand->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
case PoTy_Mem8:
|
||||
case PoTy_Mem16:
|
||||
REPORTF_ERROR("%s", "indirections cannot be part of indirections");
|
||||
reporter_print_loc(evaluator->rep, operand->loc);
|
||||
return (EvaledOperand) { .ty = EoTy_Err };
|
||||
case PoTy_Imm:
|
||||
case PoTy_Ident:
|
||||
case PoTy_SubLabel:
|
||||
case PoTy_Not:
|
||||
case PoTy_Negate:
|
||||
case PoTy_Or:
|
||||
case PoTy_Xor:
|
||||
case PoTy_And:
|
||||
case PoTy_Shl:
|
||||
case PoTy_Shr:
|
||||
case PoTy_Mul:
|
||||
case PoTy_Div:
|
||||
case PoTy_Mod: {
|
||||
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
|
||||
if (evaled.ty == EoTy_Err) {
|
||||
return evaled;
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Imm,
|
||||
.imm = evaled.imm,
|
||||
};
|
||||
}
|
||||
case PoTy_Add: {
|
||||
if (operand->left->ty == PoTy_Reg) {
|
||||
EvaledOperand right
|
||||
= eval_operand_to_imm(evaluator, operand->right);
|
||||
if (right.ty == EoTy_Err) {
|
||||
return right;
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Imm,
|
||||
.reg = operand->left->reg,
|
||||
.offset = right.imm,
|
||||
};
|
||||
} else if (operand->right->ty == PoTy_Reg) {
|
||||
EvaledOperand left
|
||||
= eval_operand_to_imm(evaluator, operand->left);
|
||||
if (left.ty == EoTy_Err) {
|
||||
return left;
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Imm,
|
||||
.reg = operand->right->reg,
|
||||
.offset = left.imm,
|
||||
};
|
||||
} else {
|
||||
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
|
||||
if (evaled.ty == EoTy_Err) {
|
||||
return evaled;
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Imm,
|
||||
.imm = evaled.imm,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PoTy_Sub: {
|
||||
if (operand->left->ty == PoTy_Reg) {
|
||||
EvaledOperand right
|
||||
= eval_operand_to_imm(evaluator, operand->right);
|
||||
if (right.ty == EoTy_Err) {
|
||||
return right;
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Imm,
|
||||
.reg = operand->left->reg,
|
||||
.offset = (uint16_t)-(int16_t)right.imm,
|
||||
};
|
||||
} else if (operand->right->ty == PoTy_Reg) {
|
||||
EvaledOperand left
|
||||
= eval_operand_to_imm(evaluator, operand->left);
|
||||
if (left.ty == EoTy_Err) {
|
||||
return left;
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Imm,
|
||||
.reg = operand->right->reg,
|
||||
.offset = (uint16_t)-(int16_t)left.imm,
|
||||
};
|
||||
} else {
|
||||
EvaledOperand evaled = eval_operand_to_imm(evaluator, operand);
|
||||
if (evaled.ty == EoTy_Err) {
|
||||
return evaled;
|
||||
}
|
||||
return (EvaledOperand) {
|
||||
.ty = EoTy_Mem8Imm,
|
||||
.imm = evaled.imm,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "unreachable\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
EvaledOperand eval_operand(OperandEvaluator* evaluator, POperand* operand)
|
||||
{
|
||||
switch (operand->ty) {
|
||||
case PoTy_Str:
|
||||
return (EvaledOperand) { .ty = EoTy_Str };
|
||||
case PoTy_Mem8:
|
||||
return eval_operand_indirection_expr(evaluator, operand->operand);
|
||||
case PoTy_Mem16: {
|
||||
EvaledOperand evaled
|
||||
= eval_operand_indirection_expr(evaluator, operand->operand);
|
||||
switch (evaled.ty) {
|
||||
case EoTy_Mem8Reg:
|
||||
evaled.ty = EoTy_MemU16Reg;
|
||||
break;
|
||||
case EoTy_Mem8Imm:
|
||||
evaled.ty = EoTy_MemU16Imm;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return evaled;
|
||||
}
|
||||
case PoTy_Reg:
|
||||
return (EvaledOperand) { .ty = EoTy_Reg, .reg = operand->reg };
|
||||
case PoTy_Imm:
|
||||
case PoTy_Ident:
|
||||
case PoTy_SubLabel:
|
||||
case PoTy_Not:
|
||||
case PoTy_Negate:
|
||||
case PoTy_Or:
|
||||
case PoTy_Xor:
|
||||
case PoTy_And:
|
||||
case PoTy_Shl:
|
||||
case PoTy_Shr:
|
||||
case PoTy_Add:
|
||||
case PoTy_Sub:
|
||||
case PoTy_Mul:
|
||||
case PoTy_Div:
|
||||
case PoTy_Mod:
|
||||
return eval_operand_to_imm(evaluator, operand);
|
||||
}
|
||||
fprintf(stderr, "unreachable\n");
|
||||
exit(1);
|
||||
}
|
39
asm/eval.h
Normal file
39
asm/eval.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "parse.h"
|
||||
#include "resolve.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
EoTy_Err,
|
||||
EoTy_Reg,
|
||||
EoTy_Imm,
|
||||
EoTy_Str,
|
||||
EoTy_Mem8Reg,
|
||||
EoTy_Mem8Imm,
|
||||
EoTy_MemU16Reg,
|
||||
EoTy_MemU16Imm,
|
||||
} EvaledOperandTy;
|
||||
|
||||
typedef struct {
|
||||
EvaledOperandTy ty;
|
||||
union {
|
||||
Reg reg;
|
||||
uint16_t imm;
|
||||
};
|
||||
uint16_t offset;
|
||||
} EvaledOperand;
|
||||
|
||||
typedef struct {
|
||||
IdentResolver* re;
|
||||
Reporter* rep;
|
||||
bool second_pass;
|
||||
} OperandEvaluator;
|
||||
|
||||
EvaledOperand eval_operand_to_imm(
|
||||
OperandEvaluator* evaluator, POperand* operand);
|
||||
EvaledOperand eval_operand_indirection_expr(
|
||||
OperandEvaluator* evaluator, POperand* operand);
|
||||
EvaledOperand eval_operand(OperandEvaluator* evaluator, POperand* operand);
|
178
asm/lex.c
Normal file
178
asm/lex.c
Normal file
@ -0,0 +1,178 @@
|
||||
#include "lex.h"
|
||||
#include "report.h"
|
||||
#include "str.h"
|
||||
#include <string.h>
|
||||
|
||||
void lexer_construct(Lexer* lexer, const char* filename, const char* text)
|
||||
{
|
||||
*lexer = (Lexer) {
|
||||
.filename = filename,
|
||||
.text = text,
|
||||
.text_len = strlen(text),
|
||||
.idx = 0,
|
||||
.line = 1,
|
||||
.col = 1,
|
||||
.ch = text[0],
|
||||
.error_occured = false,
|
||||
};
|
||||
}
|
||||
|
||||
static inline bool lexer_done(const Lexer* lexer)
|
||||
{
|
||||
return lexer->idx >= lexer->text_len;
|
||||
}
|
||||
|
||||
static inline void lexer_step(Lexer* lexer)
|
||||
{
|
||||
if (lexer_done(lexer)) {
|
||||
return;
|
||||
}
|
||||
if (lexer->ch == '\n') {
|
||||
lexer->line += 1;
|
||||
lexer->col = 1;
|
||||
} else {
|
||||
lexer->col += 1;
|
||||
}
|
||||
lexer->idx += 1;
|
||||
lexer->ch = lexer->text[lexer->idx];
|
||||
}
|
||||
|
||||
static inline Loc lexer_loc(const Lexer* lexer)
|
||||
{
|
||||
return (Loc) { .idx = lexer->idx, .line = lexer->line, .col = lexer->col };
|
||||
}
|
||||
|
||||
static inline Tok lexer_tok(const Lexer* lexer, TokTy ty, Loc loc)
|
||||
{
|
||||
return (Tok) { .ty = ty, .loc = loc, .len = lexer->idx - loc.idx };
|
||||
}
|
||||
|
||||
static inline int lexer_skip_literal_char(Lexer* lexer)
|
||||
{
|
||||
char ch = lexer->ch;
|
||||
lexer_step(lexer);
|
||||
if (ch == '\\') {
|
||||
if (lexer_done(lexer))
|
||||
return -1;
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void lexer_report(Lexer* lexer, const char* msg, Loc loc)
|
||||
{
|
||||
lexer->error_occured = true;
|
||||
REPORTF_ERROR("%s", msg);
|
||||
print_report_loc(lexer->filename, lexer->text, lexer->text_len, loc);
|
||||
}
|
||||
|
||||
Tok lexer_next(Lexer* lexer)
|
||||
{
|
||||
const char* ident_chars = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
|
||||
const char* int_chars = "1234567890";
|
||||
const char* hex_chars = "01234567889abcdefABCDEF";
|
||||
|
||||
Loc loc = lexer_loc(lexer);
|
||||
if (lexer_done(lexer)) {
|
||||
return lexer_tok(lexer, TT_Eof, loc);
|
||||
}
|
||||
if (lexer->ch == '\n') {
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, '\n', loc);
|
||||
} else if (str_includes(" \t", lexer->ch)) {
|
||||
while (!lexer_done(lexer) && str_includes(" \t", lexer->ch)) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_next(lexer);
|
||||
} else if (str_includes(ident_chars, lexer->ch)) {
|
||||
while (!lexer_done(lexer)
|
||||
&& (str_includes(ident_chars, lexer->ch)
|
||||
|| str_includes(int_chars, lexer->ch))) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Ident, loc);
|
||||
} else if (str_includes(int_chars, lexer->ch) && lexer->ch != '0') {
|
||||
while (!lexer_done(lexer) && (str_includes(int_chars, lexer->ch))) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Int, loc);
|
||||
} else if (lexer->ch == ';') {
|
||||
while (!lexer_done(lexer) && lexer->ch != '\n') {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_next(lexer);
|
||||
} else if (lexer->ch == '0') {
|
||||
lexer_step(lexer);
|
||||
if (lexer->ch == 'b') {
|
||||
lexer_step(lexer);
|
||||
if (lexer_done(lexer) || !str_includes("01", lexer->ch)) {
|
||||
lexer_report(lexer, "malformed binary literal", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
while (!lexer_done(lexer) && str_includes("01", lexer->ch)) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Binary, loc);
|
||||
} else if (lexer->ch == 'x') {
|
||||
lexer_step(lexer);
|
||||
if (lexer_done(lexer) || !str_includes(hex_chars, lexer->ch)) {
|
||||
lexer_report(lexer, "malformed hex literal", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
while (!lexer_done(lexer) && str_includes(hex_chars, lexer->ch)) {
|
||||
lexer_step(lexer);
|
||||
}
|
||||
return lexer_tok(lexer, TT_Hex, loc);
|
||||
|
||||
} else {
|
||||
return lexer_tok(lexer, TT_Int, loc);
|
||||
}
|
||||
} else if (lexer->ch == '\'') {
|
||||
lexer_step(lexer);
|
||||
lexer_skip_literal_char(lexer);
|
||||
if (lexer_done(lexer) || lexer->ch != '\'') {
|
||||
lexer_report(lexer, "malformed character literal", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, TT_Char, loc);
|
||||
} else if (lexer->ch == '"') {
|
||||
lexer_step(lexer);
|
||||
while (!lexer_done(lexer) && lexer->ch != '"') {
|
||||
lexer_skip_literal_char(lexer);
|
||||
}
|
||||
if (lexer_done(lexer) || lexer->ch != '"') {
|
||||
lexer_report(lexer, "malformed string literal", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, TT_Str, loc);
|
||||
} else if (lexer->ch == '<') {
|
||||
lexer_step(lexer);
|
||||
if (!lexer_done(lexer) && lexer->ch == '<') {
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, TT_DoubleLt, loc);
|
||||
} else {
|
||||
lexer_report(lexer, "expected '<'", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
} else if (lexer->ch == '>') {
|
||||
lexer_step(lexer);
|
||||
if (!lexer_done(lexer) && lexer->ch == '>') {
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, TT_DoubleGt, loc);
|
||||
} else {
|
||||
lexer_report(lexer, "expected '>'", loc);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
} else if (str_includes("|^&+-*/%()[].,:!", lexer->ch)) {
|
||||
char ch = lexer->ch;
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, (TokTy)ch, loc);
|
||||
} else {
|
||||
lexer_report(lexer, "illegal character", loc);
|
||||
lexer_step(lexer);
|
||||
return lexer_tok(lexer, TT_Err, loc);
|
||||
}
|
||||
}
|
55
asm/lex.h
Normal file
55
asm/lex.h
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "report.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum {
|
||||
TT_Err,
|
||||
TT_Eof,
|
||||
TT_Ident,
|
||||
TT_Int,
|
||||
TT_Binary,
|
||||
TT_Hex,
|
||||
TT_Char,
|
||||
TT_Str,
|
||||
TT_Newline = '\n',
|
||||
TT_DoubleLt,
|
||||
TT_DoubleGt,
|
||||
TT_Pipe = '|',
|
||||
TT_Hat = '^',
|
||||
TT_Ampersand = '&',
|
||||
TT_Plus = '+',
|
||||
TT_Minus = '-',
|
||||
TT_Asterisk = '*',
|
||||
TT_Slash = '/',
|
||||
TT_Percent = '%',
|
||||
TT_LParen = '(',
|
||||
TT_RParen = ')',
|
||||
TT_LBracket = '[',
|
||||
TT_RBracket = ']',
|
||||
TT_Dot = '.',
|
||||
TT_Comma = ',',
|
||||
TT_Colon = ':',
|
||||
TT_Exclamation = '!',
|
||||
} TokTy;
|
||||
|
||||
typedef struct {
|
||||
TokTy ty;
|
||||
Loc loc;
|
||||
size_t len;
|
||||
} Tok;
|
||||
|
||||
typedef struct {
|
||||
const char* filename;
|
||||
const char* text;
|
||||
size_t text_len;
|
||||
size_t idx;
|
||||
int line;
|
||||
int col;
|
||||
char ch;
|
||||
bool error_occured;
|
||||
} Lexer;
|
||||
|
||||
void lexer_construct(Lexer* lexer, const char* filename, const char* text);
|
||||
Tok lexer_next(Lexer* lexer);
|
2013
asm/main.c
2013
asm/main.c
File diff suppressed because it is too large
Load Diff
534
asm/parse.c
Normal file
534
asm/parse.c
Normal file
@ -0,0 +1,534 @@
|
||||
#include "parse.h"
|
||||
#include "lex.h"
|
||||
#include "report.h"
|
||||
#include "str.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
PLabel* plabel_new(PLabel* next, char* ident, bool sub_label, Loc loc)
|
||||
{
|
||||
PLabel* label = malloc(sizeof(PLabel));
|
||||
*label = (PLabel) { next, ident, loc, sub_label };
|
||||
return label;
|
||||
}
|
||||
|
||||
void plabel_free(PLabel* label)
|
||||
{
|
||||
if (!label) {
|
||||
return;
|
||||
}
|
||||
plabel_free(label->next);
|
||||
free(label->ident);
|
||||
free(label);
|
||||
}
|
||||
|
||||
POperand* poperand_new_reg(Reg reg, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) { .ty = PoTy_Reg, .loc = loc, .reg = reg };
|
||||
return operand;
|
||||
}
|
||||
|
||||
POperand* poperand_new_imm(uint16_t imm, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) { .ty = PoTy_Imm, .loc = loc, .imm = imm };
|
||||
return operand;
|
||||
}
|
||||
|
||||
POperand* poperand_new_str(POperandTy ty, char* str, size_t str_len, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) {
|
||||
.ty = ty,
|
||||
.loc = loc,
|
||||
.str = str,
|
||||
.str_len = str_len,
|
||||
};
|
||||
return operand;
|
||||
}
|
||||
|
||||
POperand* poperand_new_unary(POperandTy ty, POperand* inner, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand = (POperand) { .ty = ty, .loc = loc, .operand = inner };
|
||||
return operand;
|
||||
}
|
||||
|
||||
POperand* poperand_new_binary(
|
||||
POperandTy ty, POperand* left, POperand* right, Loc loc)
|
||||
{
|
||||
POperand* operand = malloc(sizeof(POperand));
|
||||
*operand
|
||||
= (POperand) { .ty = ty, .loc = loc, .left = left, .right = right };
|
||||
return operand;
|
||||
}
|
||||
|
||||
void poperand_free(POperand* operand)
|
||||
{
|
||||
switch (operand->ty) {
|
||||
case PoTy_Reg:
|
||||
case PoTy_Imm:
|
||||
break;
|
||||
case PoTy_Ident:
|
||||
case PoTy_SubLabel:
|
||||
case PoTy_Str:
|
||||
free(operand->str);
|
||||
break;
|
||||
case PoTy_Mem8:
|
||||
case PoTy_Mem16:
|
||||
case PoTy_Not:
|
||||
case PoTy_Negate:
|
||||
poperand_free(operand->operand);
|
||||
break;
|
||||
case PoTy_Or:
|
||||
case PoTy_Xor:
|
||||
case PoTy_And:
|
||||
case PoTy_Shl:
|
||||
case PoTy_Shr:
|
||||
case PoTy_Add:
|
||||
case PoTy_Sub:
|
||||
case PoTy_Mul:
|
||||
case PoTy_Div:
|
||||
case PoTy_Mod:
|
||||
poperand_free(operand->left);
|
||||
poperand_free(operand->right);
|
||||
break;
|
||||
}
|
||||
free(operand);
|
||||
}
|
||||
|
||||
PLine* pline_new(
|
||||
char* op, PLabel* labels, Loc loc, size_t ops_size, POperand** ops)
|
||||
{
|
||||
PLine* line = malloc(sizeof(PLine) + sizeof(POperand*) * ops_size);
|
||||
*line = (PLine) {
|
||||
.labels = labels,
|
||||
.op = op,
|
||||
.loc = loc,
|
||||
.ops_size = ops_size,
|
||||
};
|
||||
for (size_t i = 0; i < ops_size; ++i) {
|
||||
line->ops[i] = ops[i];
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
void pline_free(PLine* pline)
|
||||
{
|
||||
plabel_free(pline->labels);
|
||||
free(pline->op);
|
||||
for (size_t i = 0; i < pline->ops_size; ++i) {
|
||||
poperand_free(pline->ops[i]);
|
||||
}
|
||||
free(pline);
|
||||
}
|
||||
|
||||
void pstmt_free(PStmt* stmt)
|
||||
{
|
||||
switch (stmt->ty) {
|
||||
case PStmtTy_Line:
|
||||
pline_free(stmt->line);
|
||||
break;
|
||||
case PStmtTy_Global:
|
||||
case PStmtTy_Extern:
|
||||
case PStmtTy_Define:
|
||||
free(stmt->ident);
|
||||
break;
|
||||
}
|
||||
free(stmt);
|
||||
}
|
||||
|
||||
void parser_construct(Parser* parser, const char* filename, const char* text)
|
||||
{
|
||||
Lexer lexer;
|
||||
lexer_construct(&lexer, filename, text);
|
||||
|
||||
*parser = (Parser) {
|
||||
.lexer = lexer,
|
||||
.tok = lexer_next(&lexer),
|
||||
.eaten = (Tok) { 0 },
|
||||
.error_occured = false,
|
||||
};
|
||||
}
|
||||
|
||||
bool parser_done(const Parser* parser)
|
||||
{
|
||||
return parser->tok.ty == TT_Eof;
|
||||
}
|
||||
|
||||
bool parser_error_occured(const Parser* parser)
|
||||
{
|
||||
return parser->error_occured || parser->lexer.error_occured;
|
||||
}
|
||||
|
||||
static inline void parser_step(Parser* parser)
|
||||
{
|
||||
parser->tok = lexer_next(&parser->lexer);
|
||||
}
|
||||
|
||||
static inline bool parser_test(const Parser* parser, TokTy ty)
|
||||
{
|
||||
return parser->tok.ty == ty;
|
||||
}
|
||||
|
||||
static inline bool parser_eat(Parser* parser, TokTy ty)
|
||||
{
|
||||
if (parser_test(parser, ty)) {
|
||||
parser->eaten = parser->tok;
|
||||
parser_step(parser);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline char* parser_ident_val(const Parser* parser, Tok tok)
|
||||
{
|
||||
return asm_strndup(&parser->lexer.text[tok.loc.idx], tok.len);
|
||||
}
|
||||
|
||||
static inline void parser_report(Parser* parser, const char* msg, Loc loc)
|
||||
{
|
||||
parser->error_occured = true;
|
||||
REPORTF_ERROR("%s", msg);
|
||||
print_report_loc(parser->lexer.filename,
|
||||
parser->lexer.text,
|
||||
parser->lexer.text_len,
|
||||
loc);
|
||||
}
|
||||
|
||||
static inline void parser_skip_newlines(Parser* parser)
|
||||
{
|
||||
while (parser_eat(parser, '\n')) { }
|
||||
}
|
||||
|
||||
static inline PLabel* parser_parse_labels(
|
||||
Parser* parser, char** ident, Loc* ident_loc)
|
||||
{
|
||||
*ident = NULL;
|
||||
PLabel* labels = NULL;
|
||||
while (parser->tok.ty != TT_Eof && *ident == NULL) {
|
||||
parser_skip_newlines(parser);
|
||||
Loc loc = parser->tok.loc;
|
||||
if (parser_eat(parser, '.')) {
|
||||
if (!parser_eat(parser, TT_Ident)) {
|
||||
parser_report(parser, "expected identifier", parser->tok.loc);
|
||||
plabel_free(labels);
|
||||
return NULL;
|
||||
}
|
||||
char* label_ident = parser_ident_val(parser, parser->eaten);
|
||||
if (!parser_eat(parser, ':')) {
|
||||
parser_report(parser, "expected ':'", parser->tok.loc);
|
||||
plabel_free(labels);
|
||||
free(label_ident);
|
||||
return NULL;
|
||||
}
|
||||
labels = plabel_new(labels, label_ident, true, loc);
|
||||
} else if (parser_eat(parser, TT_Ident)) {
|
||||
*ident = parser_ident_val(parser, parser->eaten);
|
||||
*ident_loc = loc;
|
||||
if (!parser_eat(parser, ':')) {
|
||||
break;
|
||||
}
|
||||
labels = plabel_new(labels, *ident, false, loc);
|
||||
*ident = NULL;
|
||||
} else {
|
||||
parser_report(
|
||||
parser, "expected identifier or ':'", parser->tok.loc);
|
||||
plabel_free(labels);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
static inline char literal_char_val(const char* str)
|
||||
{
|
||||
if (str[0] == '\\') {
|
||||
switch (str[1]) {
|
||||
case '0':
|
||||
return 0;
|
||||
case 't':
|
||||
return '\t';
|
||||
case 'n':
|
||||
return '\n';
|
||||
default:
|
||||
return str[1];
|
||||
}
|
||||
} else {
|
||||
return str[0];
|
||||
}
|
||||
}
|
||||
|
||||
static const int parser_binary_prec = 6;
|
||||
static inline POperand* parser_parse_operand_2(Parser* parser, int prec);
|
||||
|
||||
static inline POperand* parser_parse_operand_0(Parser* parser)
|
||||
{
|
||||
Loc loc = parser->tok.loc;
|
||||
if (parser_eat(parser, TT_Ident)) {
|
||||
char* ident = parser_ident_val(parser, parser->eaten);
|
||||
const char* reg_key[10] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "rbp", "rsp", "rfl", "rcs", "rip"
|
||||
};
|
||||
Reg reg_val[10] = { R0, R1, R2, R3, R4, Rbp, Rsp, Rfl, Rcs, Rip };
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
if (strcmp(reg_key[i], ident) == 0) {
|
||||
free(ident);
|
||||
return poperand_new_reg(reg_val[i], loc);
|
||||
}
|
||||
}
|
||||
return poperand_new_str(PoTy_Ident, ident, parser->eaten.len, loc);
|
||||
} else if (parser_eat(parser, TT_Int)) {
|
||||
char* str = parser_ident_val(parser, parser->eaten);
|
||||
uint64_t val = strtoull(str, NULL, 10);
|
||||
free(str);
|
||||
if (val > 0xffff) {
|
||||
parser_report(parser,
|
||||
"integers larger than 65536 not supported",
|
||||
parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
uint16_t imm = (uint16_t)val;
|
||||
return poperand_new_imm(imm, loc);
|
||||
} else if (parser_eat(parser, TT_Binary)) {
|
||||
char* str = parser_ident_val(parser, parser->eaten);
|
||||
uint64_t val = strtoull(&str[2], NULL, 2);
|
||||
free(str);
|
||||
if (val > 0xffff) {
|
||||
parser_report(parser,
|
||||
"integers larger than 65536 not supported",
|
||||
parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
uint16_t imm = (uint16_t)val;
|
||||
return poperand_new_imm(imm, loc);
|
||||
} else if (parser_eat(parser, TT_Hex)) {
|
||||
char* str = parser_ident_val(parser, parser->eaten);
|
||||
uint64_t val = strtoull(&str[2], NULL, 16);
|
||||
free(str);
|
||||
if (val > 0xffff) {
|
||||
parser_report(parser,
|
||||
"integers larger than 65536 not supported",
|
||||
parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
uint16_t imm = (uint16_t)val;
|
||||
return poperand_new_imm(imm, loc);
|
||||
} else if (parser_eat(parser, TT_Char)) {
|
||||
char* str = parser_ident_val(parser, parser->eaten);
|
||||
uint16_t imm = (uint16_t)literal_char_val(&str[1]);
|
||||
free(str);
|
||||
return poperand_new_imm(imm, loc);
|
||||
} else if (parser_eat(parser, TT_Str)) {
|
||||
char* lit = parser_ident_val(parser, parser->eaten);
|
||||
size_t lit_len = strlen(lit);
|
||||
char* str = calloc(lit_len - 1, sizeof(char));
|
||||
size_t str_len = 0;
|
||||
for (size_t i = 1; i < lit_len - 2; ++i) {
|
||||
str[i] = literal_char_val(&lit[i]);
|
||||
}
|
||||
free(lit);
|
||||
return poperand_new_str(PoTy_Str, str, str_len, loc);
|
||||
} else if (parser_eat(parser, '.')) {
|
||||
if (!parser_eat(parser, TT_Ident)) {
|
||||
parser_report(parser, "expected identifier", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
char* ident = parser_ident_val(parser, parser->eaten);
|
||||
return poperand_new_str(PoTy_SubLabel, ident, parser->eaten.len, loc);
|
||||
} else if (parser_eat(parser, '(')) {
|
||||
POperand* operand = parser_parse_operand_2(parser, parser_binary_prec);
|
||||
if (!parser_eat(parser, ')')) {
|
||||
parser_report(parser, "expected ')'", parser->tok.loc);
|
||||
poperand_free(operand);
|
||||
return NULL;
|
||||
}
|
||||
return operand;
|
||||
} else {
|
||||
parser_report(parser, "expected operand", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline POperand* parser_parse_operand_1(Parser* parser)
|
||||
{
|
||||
|
||||
Loc loc = parser->tok.loc;
|
||||
if (parser_eat(parser, '-')) {
|
||||
POperand* operand = parser_parse_operand_1(parser);
|
||||
return poperand_new_unary(PoTy_Negate, operand, loc);
|
||||
} else if (parser_eat(parser, '!')) {
|
||||
POperand* operand = parser_parse_operand_1(parser);
|
||||
return poperand_new_unary(PoTy_Not, operand, loc);
|
||||
} else {
|
||||
return parser_parse_operand_0(parser);
|
||||
}
|
||||
}
|
||||
|
||||
static inline POperand* parser_parse_operand_2(Parser* parser, int prec)
|
||||
{
|
||||
const POperandTy op_tys[] = {
|
||||
PoTy_Or,
|
||||
PoTy_Xor,
|
||||
PoTy_And,
|
||||
PoTy_Shr,
|
||||
PoTy_Shl,
|
||||
PoTy_Add,
|
||||
PoTy_Sub,
|
||||
PoTy_Mul,
|
||||
PoTy_Div,
|
||||
PoTy_Mod,
|
||||
};
|
||||
const TokTy op_tts[] = {
|
||||
'|',
|
||||
'^',
|
||||
'&',
|
||||
TT_DoubleGt,
|
||||
TT_DoubleLt,
|
||||
'+',
|
||||
'-',
|
||||
'*',
|
||||
'/',
|
||||
'%',
|
||||
};
|
||||
const int op_precs[] = { 6, 5, 4, 3, 3, 2, 2, 1, 1, 1 };
|
||||
static_assert(sizeof(op_tys) / sizeof(op_tys[0])
|
||||
== sizeof(op_tts) / sizeof(op_tts[0]),
|
||||
"misaligned");
|
||||
static_assert(sizeof(op_tys) / sizeof(op_tys[0])
|
||||
== sizeof(op_precs) / sizeof(op_precs[0]),
|
||||
"misaligned");
|
||||
|
||||
if (prec == 0) {
|
||||
return parser_parse_operand_1(parser);
|
||||
}
|
||||
POperand* left = parser_parse_operand_2(parser, prec - 1);
|
||||
bool should_continue = true;
|
||||
while (should_continue) {
|
||||
should_continue = false;
|
||||
for (size_t i = 0; i < sizeof(op_tys) / sizeof(op_tys[0]); ++i) {
|
||||
if (prec >= op_precs[i] && parser_eat(parser, op_tts[i])) {
|
||||
POperand* right = parser_parse_operand_2(parser, prec - 1);
|
||||
left = poperand_new_binary(op_tys[i], left, right, left->loc);
|
||||
should_continue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
static inline POperand* parser_parse_operand_3(Parser* parser)
|
||||
{
|
||||
Loc loc = parser->tok.loc;
|
||||
if (parser_eat(parser, TT_LBracket)) {
|
||||
parser_report(parser, "expected 'u8' or 'u16' before '['", loc);
|
||||
return NULL;
|
||||
}
|
||||
if (!parser_test(parser, TT_Ident)) {
|
||||
return parser_parse_operand_2(parser, parser_binary_prec);
|
||||
}
|
||||
char* ident = parser_ident_val(parser, parser->tok);
|
||||
if (strcmp(ident, "u8") == 0) {
|
||||
free(ident);
|
||||
parser_step(parser);
|
||||
if (!parser_eat(parser, '[')) {
|
||||
parser_report(parser, "expected '['", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
POperand* operand = parser_parse_operand_2(parser, parser_binary_prec);
|
||||
if (!parser_eat(parser, ']')) {
|
||||
parser_report(parser, "expected ']'", parser->tok.loc);
|
||||
poperand_free(operand);
|
||||
return NULL;
|
||||
}
|
||||
return poperand_new_unary(PoTy_Mem8, operand, loc);
|
||||
} else if (strcmp(ident, "u16") == 0) {
|
||||
free(ident);
|
||||
parser_step(parser);
|
||||
if (!parser_eat(parser, '[')) {
|
||||
parser_report(parser, "expected '['", parser->tok.loc);
|
||||
return NULL;
|
||||
}
|
||||
POperand* operand = parser_parse_operand_2(parser, parser_binary_prec);
|
||||
if (!parser_eat(parser, ']')) {
|
||||
parser_report(parser, "expected ']'", parser->tok.loc);
|
||||
poperand_free(operand);
|
||||
return NULL;
|
||||
}
|
||||
return poperand_new_unary(PoTy_Mem16, operand, loc);
|
||||
} else {
|
||||
free(ident);
|
||||
return parser_parse_operand_2(parser, parser_binary_prec);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void parser_skip_to_next_line(Parser* parser)
|
||||
{
|
||||
while (!parser_done(parser) && !parser_eat(parser, TT_Newline)) {
|
||||
parser_step(parser);
|
||||
}
|
||||
}
|
||||
|
||||
PLine* parser_next(Parser* parser)
|
||||
{
|
||||
char* ident;
|
||||
Loc loc;
|
||||
PLabel* labels = parser_parse_labels(parser, &ident, &loc);
|
||||
|
||||
const size_t max_ops_size = 64;
|
||||
// TODO: Move allocation out-of-band.
|
||||
POperand** ops = malloc(sizeof(POperand) * max_ops_size);
|
||||
size_t ops_size = 0;
|
||||
|
||||
if (!parser_test(parser, TT_Eof) && !parser_test(parser, '\n')) {
|
||||
POperand* operand = parser_parse_operand_3(parser);
|
||||
if (!operand) {
|
||||
parser_skip_to_next_line(parser);
|
||||
goto error_free_ops;
|
||||
}
|
||||
ops[ops_size++] = operand;
|
||||
while (!parser_test(parser, TT_Eof) && !parser_test(parser, '\n')
|
||||
&& ops_size < 3) {
|
||||
if (ops_size >= max_ops_size) {
|
||||
parser_report(parser,
|
||||
"exceeded maximum number of operands (64)",
|
||||
parser->tok.loc);
|
||||
parser_skip_to_next_line(parser);
|
||||
goto error_free_ops;
|
||||
}
|
||||
if (!parser_eat(parser, ',')) {
|
||||
parser_report(parser, "expected ','", parser->tok.loc);
|
||||
parser_skip_to_next_line(parser);
|
||||
goto error_free_ops;
|
||||
}
|
||||
POperand* operand = parser_parse_operand_3(parser);
|
||||
if (!operand) {
|
||||
parser_skip_to_next_line(parser);
|
||||
goto error_free_ops;
|
||||
}
|
||||
ops[ops_size++] = operand;
|
||||
}
|
||||
}
|
||||
if (!parser_eat(parser, '\n') && !parser_test(parser, TT_Eof)) {
|
||||
parser_report(parser, "expected newline", parser->tok.loc);
|
||||
goto error_free_ops;
|
||||
}
|
||||
parser_skip_newlines(parser);
|
||||
|
||||
PLine* line = pline_new(ident, labels, loc, ops_size, ops);
|
||||
free(ops);
|
||||
return line;
|
||||
|
||||
error_free_ops:
|
||||
for (size_t i = 0; i < ops_size; ++i)
|
||||
if (ops[i])
|
||||
poperand_free(ops[i]);
|
||||
free(ops);
|
||||
plabel_free(labels);
|
||||
free(ident);
|
||||
return NULL;
|
||||
}
|
110
asm/parse.h
Normal file
110
asm/parse.h
Normal file
@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "lex.h"
|
||||
#include "report.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct PLabel PLabel;
|
||||
|
||||
struct PLabel {
|
||||
PLabel* next;
|
||||
char* ident;
|
||||
Loc loc;
|
||||
bool sub_label;
|
||||
};
|
||||
|
||||
PLabel* plabel_new(PLabel* next, char* ident, bool sub_label, Loc loc);
|
||||
void plabel_free(PLabel* label);
|
||||
|
||||
typedef enum {
|
||||
PoTy_Reg,
|
||||
PoTy_Imm,
|
||||
PoTy_Ident,
|
||||
PoTy_SubLabel,
|
||||
PoTy_Str,
|
||||
PoTy_Mem8,
|
||||
PoTy_Mem16,
|
||||
PoTy_Not,
|
||||
PoTy_Negate,
|
||||
PoTy_Or,
|
||||
PoTy_Xor,
|
||||
PoTy_And,
|
||||
PoTy_Shl,
|
||||
PoTy_Shr,
|
||||
PoTy_Add,
|
||||
PoTy_Sub,
|
||||
PoTy_Mul,
|
||||
PoTy_Div,
|
||||
PoTy_Mod,
|
||||
} POperandTy;
|
||||
|
||||
typedef struct POperand POperand;
|
||||
|
||||
struct POperand {
|
||||
POperandTy ty;
|
||||
Loc loc;
|
||||
union {
|
||||
Reg reg;
|
||||
uint16_t imm;
|
||||
struct {
|
||||
char* str;
|
||||
size_t str_len;
|
||||
};
|
||||
POperand* operand;
|
||||
struct {
|
||||
POperand* left;
|
||||
POperand* right;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
POperand* poperand_new_reg(Reg reg, Loc loc);
|
||||
POperand* poperand_new_imm(uint16_t imm, Loc loc);
|
||||
POperand* poperand_new_str(POperandTy ty, char* str, size_t str_len, Loc loc);
|
||||
POperand* poperand_new_unary(POperandTy ty, POperand* inner, Loc loc);
|
||||
POperand* poperand_new_binary(
|
||||
POperandTy ty, POperand* left, POperand* right, Loc loc);
|
||||
void poperand_free(POperand* operand);
|
||||
|
||||
typedef struct {
|
||||
PLabel* labels;
|
||||
char* op;
|
||||
Loc loc;
|
||||
size_t ops_size;
|
||||
POperand* ops[];
|
||||
} PLine;
|
||||
|
||||
PLine* pline_new(
|
||||
char* op, PLabel* labels, Loc loc, size_t ops_size, POperand** ops);
|
||||
void pline_free(PLine* pline);
|
||||
|
||||
typedef enum {
|
||||
PStmtTy_Line,
|
||||
PStmtTy_Global,
|
||||
PStmtTy_Extern,
|
||||
PStmtTy_Define,
|
||||
} PStmtTy;
|
||||
|
||||
typedef struct {
|
||||
PStmtTy ty;
|
||||
union {
|
||||
PLine* line;
|
||||
char* ident;
|
||||
};
|
||||
} PStmt;
|
||||
|
||||
void pstmt_free(PStmt* stmt);
|
||||
|
||||
typedef struct {
|
||||
Lexer lexer;
|
||||
Tok tok;
|
||||
Tok eaten;
|
||||
bool error_occured;
|
||||
} Parser;
|
||||
|
||||
void parser_construct(Parser* parser, const char* filename, const char* text);
|
||||
bool parser_done(const Parser* parser);
|
||||
bool parser_error_occured(const Parser* parser);
|
||||
PLine* parser_next(Parser* parser);
|
44
asm/report.c
Normal file
44
asm/report.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include "report.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void print_report_loc(
|
||||
const char* filename, const char* text, size_t text_len, Loc loc)
|
||||
{
|
||||
size_t line_start = loc.idx;
|
||||
while (line_start > 0 && text[line_start] != '\n') {
|
||||
line_start -= 1;
|
||||
}
|
||||
if (text[line_start] == '\n') {
|
||||
line_start += 1;
|
||||
}
|
||||
size_t line_end = loc.idx + 1;
|
||||
while (line_end < text_len && text[line_end] != '\n') {
|
||||
line_end += 1;
|
||||
}
|
||||
const char* line = &text[line_start];
|
||||
int line_len = (int)line_end - (int)line_start;
|
||||
|
||||
fprintf(stderr,
|
||||
" \x1b[96m--> ./%s:%d:%d\n "
|
||||
"\x1b[37m|\n\x1b[96m%5d\x1b[37m|%.*s\n "
|
||||
"|%*c\x1b[1;91m^\x1b[0m\n",
|
||||
filename,
|
||||
loc.line,
|
||||
loc.col,
|
||||
loc.line,
|
||||
line_len,
|
||||
line,
|
||||
loc.col - 1,
|
||||
' ');
|
||||
}
|
||||
|
||||
void reporter_print_loc(Reporter* rep, Loc loc)
|
||||
{
|
||||
print_report_loc(rep->filename, rep->text, rep->text_len, loc);
|
||||
}
|
||||
|
||||
void reporter_error_with_loc(Reporter* rep, const char* msg, Loc loc)
|
||||
{
|
||||
REPORTF_ERROR("%s", msg);
|
||||
reporter_print_loc(rep, loc);
|
||||
}
|
31
asm/report.h
Normal file
31
asm/report.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
size_t idx;
|
||||
int line;
|
||||
int col;
|
||||
} Loc;
|
||||
|
||||
#define REPORTF_ERROR(FMT, ...) \
|
||||
(fprintf( \
|
||||
stderr, "\x1b[1;91merror\x1b[1;97m: " FMT "\x1b[0m\n", __VA_ARGS__))
|
||||
#define REPORTF_INFO(FMT, ...) \
|
||||
(fprintf(stderr, "\x1b[1;96minfo\x1b[1;97m: " FMT "\x1b[0m\n", __VA_ARGS__))
|
||||
#define REPORTF_WARNING(FMT, ...) \
|
||||
(fprintf( \
|
||||
stderr, "\x1b[1;93mwarning\x1b[1;97m: " FMT "\x1b[0m\n", __VA_ARGS__))
|
||||
|
||||
void print_report_loc(
|
||||
const char* filename, const char* text, size_t text_len, Loc loc);
|
||||
|
||||
typedef struct {
|
||||
const char* filename;
|
||||
const char* text;
|
||||
size_t text_len;
|
||||
} Reporter;
|
||||
|
||||
void reporter_print_loc(Reporter* rep, Loc loc);
|
||||
void reporter_error_with_loc(Reporter* rep, const char* msg, Loc loc);
|
92
asm/resolve.c
Normal file
92
asm/resolve.c
Normal file
@ -0,0 +1,92 @@
|
||||
#include "resolve.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void ident_resol_destroy(IdentResol* resol)
|
||||
{
|
||||
switch (resol->ty) {
|
||||
case IdentResolTy_None:
|
||||
break;
|
||||
case IdentResolTy_Label:
|
||||
case IdentResolTy_SubLabel:
|
||||
free(resol->ident);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ident_resolver_construct(IdentResolver* resolver)
|
||||
{
|
||||
size_t capacity = 512;
|
||||
*resolver = (IdentResolver) {
|
||||
.resols = malloc(sizeof(IdentResol) * capacity),
|
||||
.resols_capacity = capacity,
|
||||
.resols_size = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void ident_resolver_destroy(IdentResolver* resolver)
|
||||
{
|
||||
for (size_t i = 0; i < resolver->resols_size; ++i) {
|
||||
ident_resol_destroy(&resolver->resols[i]);
|
||||
}
|
||||
free(resolver->resols);
|
||||
}
|
||||
|
||||
static inline size_t ident_resolver_first_empty(IdentResolver* resolver)
|
||||
{
|
||||
size_t i = 0;
|
||||
for (; i < resolver->resols_size; ++i) {
|
||||
if (resolver->resols[i].ty == IdentResolTy_None) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= resolver->resols_size) {
|
||||
if (resolver->resols_size + 1 > resolver->resols_capacity) {
|
||||
resolver->resols_capacity *= 2;
|
||||
resolver->resols = realloc(resolver->resols,
|
||||
sizeof(IdentResol) * resolver->resols_capacity);
|
||||
}
|
||||
resolver->resols_size += 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void ident_resolver_define_label(
|
||||
IdentResolver* resolver, char* ident, Loc loc, uint16_t asm_ip)
|
||||
{
|
||||
size_t i = ident_resolver_first_empty(resolver);
|
||||
resolver->resols[i] = (IdentResol) {
|
||||
.ident = ident,
|
||||
.loc = loc,
|
||||
.ty = IdentResolTy_Label,
|
||||
.ip = asm_ip * 2,
|
||||
};
|
||||
resolver->current_parent = &resolver->resols[i];
|
||||
}
|
||||
|
||||
void ident_resolver_define_sublabel(
|
||||
IdentResolver* resolver, char* ident, Loc loc, uint16_t asm_ip)
|
||||
{
|
||||
size_t i = ident_resolver_first_empty(resolver);
|
||||
resolver->resols[i] = (IdentResol) {
|
||||
.ident = ident,
|
||||
.loc = loc,
|
||||
.parent = resolver->current_parent,
|
||||
.ty = IdentResolTy_SubLabel,
|
||||
.ip = asm_ip * 2,
|
||||
};
|
||||
}
|
||||
|
||||
const IdentResol* ident_resolver_resolve(
|
||||
const IdentResolver* resolver, const char* ident)
|
||||
{
|
||||
for (size_t i = resolver->resols_size; i > 0; --i) {
|
||||
IdentResol* re = &resolver->resols[i - 1];
|
||||
if (re->ty != IdentResolTy_None && strcmp(re->ident, ident) == 0
|
||||
&& (re->ty != IdentResolTy_SubLabel
|
||||
|| re->parent == resolver->current_parent)) {
|
||||
return re;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
40
asm/resolve.h
Normal file
40
asm/resolve.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "report.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
IdentResolTy_None,
|
||||
IdentResolTy_Label,
|
||||
IdentResolTy_SubLabel,
|
||||
} IdentResolTy;
|
||||
|
||||
typedef struct IdentResol IdentResol;
|
||||
struct IdentResol {
|
||||
char* ident;
|
||||
Loc loc;
|
||||
const IdentResol* parent;
|
||||
IdentResolTy ty;
|
||||
union {
|
||||
uint16_t ip;
|
||||
};
|
||||
};
|
||||
|
||||
void ident_resol_destroy(IdentResol* resol);
|
||||
|
||||
typedef struct IdentResolver IdentResolver;
|
||||
|
||||
struct IdentResolver {
|
||||
IdentResol* resols;
|
||||
size_t resols_capacity;
|
||||
size_t resols_size;
|
||||
const IdentResol* current_parent;
|
||||
};
|
||||
void ident_resolver_construct(IdentResolver* resolver);
|
||||
void ident_resolver_destroy(IdentResolver* resolver);
|
||||
void ident_resolver_define_label(
|
||||
IdentResolver* resolver, char* ident, Loc loc, uint16_t asm_ip);
|
||||
void ident_resolver_define_sublabel(
|
||||
IdentResolver* resolver, char* ident, Loc loc, uint16_t asm_ip);
|
||||
const IdentResol* ident_resolver_resolve(
|
||||
const IdentResolver* resolver, const char* ident);
|
28
asm/str.c
Normal file
28
asm/str.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include "str.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool str_includes(const char* str, char ch)
|
||||
{
|
||||
for (size_t i = 0; str[i] != '\0'; ++i) {
|
||||
if (str[i] == ch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* asm_strdup(const char* str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
char* val = calloc(len + 1, sizeof(char));
|
||||
strncpy(val, str, len);
|
||||
return val;
|
||||
}
|
||||
|
||||
char* asm_strndup(const char* str, size_t len)
|
||||
{
|
||||
char* val = calloc(len + 1, sizeof(char));
|
||||
strncpy(val, str, len);
|
||||
return val;
|
||||
}
|
8
asm/str.h
Normal file
8
asm/str.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
bool str_includes(const char* str, char ch);
|
||||
char* asm_strdup(const char* str);
|
||||
char* asm_strndup(const char* str, size_t len);
|
@ -96,16 +96,10 @@ keyboard_interrupt:
|
||||
iret
|
||||
|
||||
put_char:
|
||||
; push rbp
|
||||
add rsp, rsp, 2
|
||||
mov u16 [rsp], rbp
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
; push r1
|
||||
add rsp, rsp, 2
|
||||
mov u16 [rsp], r1
|
||||
; push r2
|
||||
add rsp, rsp, 2
|
||||
mov u16 [rsp], r2
|
||||
push r1
|
||||
push r2
|
||||
|
||||
mov r2, u16 [screen_y]
|
||||
mul r2, r2, 40 ; vcd_width_in_ch
|
||||
|
Loading…
x
Reference in New Issue
Block a user