mips/masm/gen/grammer.c
2024-10-21 12:27:18 -04:00

643 lines
12 KiB
C

#include <strings.h>
#include <string.h>
#include <merror.h>
#include "../gen.h"
#include "mips.h"
struct grammer_mapping {
char *name;
int name_len;
enum grammer_type type;
};
#define MAPPING(name, enum) {name, sizeof(name), GMR_ ##enum}
static const struct grammer_mapping grammer_mappings[__GMR_LEN] = {
// registers
MAPPING("rd", RD),
MAPPING("rs", RS),
MAPPING("rt", RT),
// fp registers
MAPPING("fs", FS),
MAPPING("ft", FT),
MAPPING("fd", FD),
// numberic fileds
MAPPING("immd", IMMD),
MAPPING("cc", CC),
MAPPING("code", CODE),
MAPPING("pos", POS),
MAPPING("size", SIZE),
MAPPING("hb", HB),
MAPPING("hint", HINT),
// addresses
MAPPING("hi", HI),
MAPPING("lo", LO),
MAPPING("target", TARGET),
MAPPING("offset", OFFSET),
MAPPING("offset(base)", OFFSET_BASE),
MAPPING("index(base)", INDEX_BASE)
};
/* Parses the input string and matches it to a grammer type. Returns
* the number of characters consumed, or -1 on error.
*/
int gen_parse_grammer_type(const char *name, enum grammer_type *res)
{
for (int i = 0; i < __GMR_LEN; i++) {
const struct grammer_mapping *mapping = &grammer_mappings[i];
if (strncasecmp(name, mapping->name, mapping->name_len) != 0)
continue;
if (res != NULL)
*res = mapping->type;
return mapping->name_len;
}
return -1;
}
/* Parses a register name, returing 0 on success, 1 on error*/
int gen_parse_register(enum mips32_register *reg, struct string *name)
{
int len = name->len;
int c0 = len > 0 ? name->str[0] : '\0',
c1 = len > 1 ? name->str[1] : '\0',
c2 = len > 2 ? name->str[2] : '\0',
c3 = len > 3 ? name->str[3] : '\0';
// $zero
if (c0 == 'z') {
if (c1 == 'e' && c2 == 'r' && c3 == 'o') {
*reg = MIPS32_REG_ZERO;
return M_SUCCESS;
}
}
// $a0-a3 $at
else if (c0 == 'a') {
if (c1 == 't') {
*reg = MIPS32_REG_AT;
return M_SUCCESS;
}
if (c1 >= '0' && c1 <= '3') {
*reg = MIPS32_REG_A0;
*reg += c1 - '0';
return M_SUCCESS;
}
}
// $v0-v1
else if (c0 == 'v') {
if (c1 >= '0' && c1 <= '1') {
*reg = MIPS32_REG_V0;
*reg += c1 - '0';
return M_SUCCESS;
}
}
// $t0-t9
else if (c0 == 't') {
if (c1 >= '0' && c1 <= '7') {
*reg = MIPS32_REG_T0;
*reg += c1 - '0';
return M_SUCCESS;
}
// reg T8-T9 are not in order with T0-T7
if (c1 >= '8' && c1 <= '9') {
*reg = MIPS32_REG_T8;
*reg += c1 - '8';
return M_SUCCESS;
}
}
// $s0-s7 $sp
else if (c0 == 's') {
if (c1 >= '0' && c1 <= '7') {
*reg = MIPS32_REG_S0;
*reg += c1 - '0';
return M_SUCCESS;
}
if (c1 == 'p') {
*reg = MIPS32_REG_SP;
return M_SUCCESS;
}
}
// $k0-k1
else if (c0 == 'k') {
if (c1 >= '0' && c1 <= '1') {
*reg = MIPS32_REG_K0;
*reg += c1 - '0';
return M_SUCCESS;
}
}
// $gp
else if (c0 == 'g') {
if (c1 == 'p') {
*reg = MIPS32_REG_GP;
return M_SUCCESS;
}
}
// $fp
else if (c0 == 'f') {
if (c1 == 'p') {
*reg = MIPS32_REG_FP;
return M_SUCCESS;
}
}
// $rp
else if (c0 == 'r') {
if (c1 == 'a') {
*reg = MIPS32_REG_RA;
return M_SUCCESS;
}
}
// $0-31 (non aliased register names)
else if (c0 >= '0' && c0 <= '9') {
int i = c0 - '0';
if (c1 >= '0' && c1 <= '9') {
i *= 10;
i += c1 - '0';
}
if (i <= 31) {
*reg = i;
return M_SUCCESS;
}
}
ERROR("unknown register $%.*s", name->len, name->str);
return 1;
}
/* Parses a floating point register name, returing 0 on success, 1 on error*/
int gen_parse_fp_register(enum mips32_fp_register *reg, struct string *name)
{
int len = name->len;
int c0 = len > 0 ? name->str[0] : '\0',
c1 = len > 1 ? name->str[1] : '\0',
c2 = len > 2 ? name->str[2] : '\0';
*reg = 0;
if (c0 != 'f')
goto error;
if (c1 < '0' || c1 > '9')
goto error;
*reg += c1 - '0';
if (c1 == '\0')
return M_SUCCESS;
if (c2 < '0' || c2 > '9')
goto error;
*reg *= 10;
*reg += c2 - '0';
return M_SUCCESS;
error:
ERROR("unknown fp register $%.*s", name->len, name->str);
return 1;
}
/* convert string to number with int len max length */
static int antoi(char *str, int len)
{
char c;
int num = 0;
while (c = *str++, len--) {
if (c < '0' || c > '9')
break;
num *= 10;
num += c - '0';
}
return num;
}
/* Parses the override expression (after the =) in the dsl for the grammer.
* returns the number of characters consumed, or -1 on error. */
int gen_parse_grammer_override(struct gen_ins_state *state,
struct gen_ins_override *over, char *value)
{
int value_len = 0;
/* get length of value */
for (const char *ptr = value;
*ptr != '\0' && *ptr != ',';
ptr++, value_len++);
// rd
if (strncmp("rd", value, value_len))
over->reg = state->rd;
// rs
else if (strncmp("rs", value, value_len))
over->reg = state->rs;
// rt
else if (strncmp("rt", value, value_len))
over->reg = state->rt;
// fd
else if (strncmp("fd", value, value_len))
over->fpreg = state->fd;
// fs
else if (strncmp("fs", value, value_len))
over->fpreg = state->fs;
// ft
else if (strncmp("ft", value, value_len))
over->fpreg = state->ft;
// immd
else if (strncmp("immd", value, value_len))
over->immd = state->immd;
// -immd
else if (strncmp("-immd", value, value_len))
over->immd = -state->immd;
// floating point register
else if (value_len > 2 && value[0] == '$' && value[1] == 'f') {
struct string temp = {
.str = value + 1,
.len = value_len - 1
};
if (gen_parse_fp_register(&over->fpreg, &temp))
return -1;
}
// register
else if (value_len > 1 && value[0] == '$') {
struct string temp = {
.str = value + 1,
.len = value_len - 1
};
if (gen_parse_register(&over->reg, &temp))
return -1;
}
// number
else
over->immd = antoi(value, value_len);
return value_len;
}
static int read_next_state(struct expr_ins_arg *arg,
struct gen_ins_state *state,
enum grammer_type type)
{
switch (type) {
case GMR_RD:
// rd
if (arg->type != EXPR_INS_ARG_REGISTER) {
ERROR("expected a register");
return 1;
}
if (gen_parse_register(&state->rd, &arg->reg))
return 1;
break;
case GMR_RS:
// rs
if (arg->type != EXPR_INS_ARG_REGISTER) {
ERROR("expected a register");
return 1;
}
if (gen_parse_register(&state->rs, &arg->reg))
return 1;
break;
case GMR_RT:
// rt
if (arg->type != EXPR_INS_ARG_REGISTER) {
ERROR("expected a register");
return 1;
}
if (gen_parse_register(&state->rt, &arg->reg))
return 1;
break;
case GMR_FS:
// fs
if (arg->type != EXPR_INS_ARG_REGISTER) {
ERROR("expected a register");
return 1;
}
if (gen_parse_fp_register(&state->fs, &arg->reg))
return 1;
break;
case GMR_FT:
// ft
if (arg->type != EXPR_INS_ARG_REGISTER) {
ERROR("expected a register");
return 1;
}
if (gen_parse_fp_register(&state->ft, &arg->reg))
return 1;
break;
case GMR_FD:
// fd
if (arg->type != EXPR_INS_ARG_REGISTER) {
ERROR("expected a register");
return 1;
}
if (gen_parse_fp_register(&state->fd, &arg->reg))
return 1;
break;
case GMR_IMMD:
// immd
if (arg->type != EXPR_INS_ARG_IMMEDIATE) {
ERROR("expected an immediate");
return 1;
}
state->immd = arg->immd;
break;
case GMR_CC:
// cc
if (arg->type != EXPR_INS_ARG_IMMEDIATE) {
ERROR("expected an immediate");
return 1;
}
state->cc = arg->immd;
break;
case GMR_CODE:
// code
if (arg->type != EXPR_INS_ARG_IMMEDIATE) {
ERROR("expected an immediate");
return 1;
}
state->code = arg->immd;
break;
case GMR_POS:
// pos
if (arg->type != EXPR_INS_ARG_IMMEDIATE) {
ERROR("expected an immediate");
return 1;
}
state->pos = arg->immd;
break;
case GMR_SIZE:
// size
if (arg->type != EXPR_INS_ARG_IMMEDIATE) {
ERROR("expected an immediate");
return 1;
}
// TODO: check valid size
state->size = arg->immd;
break;
case GMR_HB:
// hb
if (arg->type != EXPR_INS_ARG_IMMEDIATE) {
ERROR("expected an immediate");
return 1;
}
state->hb = !!(arg->immd);
break;
case GMR_HINT:
// hint
if (arg->type != EXPR_INS_ARG_IMMEDIATE) {
ERROR("expected an immediate");
return 1;
}
state->hint = arg-> immd;
break;
case GMR_OFFSET:
// offset
state->offset = 0;
if (arg->type == EXPR_INS_ARG_IMMEDIATE)
state->offset = arg->immd;
else if (arg->type == EXPR_INS_ARG_LABEL)
state->label = &arg->label;
else {
ERROR("invalid instruction");
return 1;
}
break;
case GMR_OFFSET_BASE:
// offset(base)
if (arg->type != EXPR_INS_ARG_OFFSET) {
ERROR("expected an offset($base)");
return 1;
}
state->offset = arg->offset.immd;
if (gen_parse_register(&state->base, &arg->offset.reg))
return 1;
break;
case GMR_INDEX_BASE:
// index(base)
if (arg->type != EXPR_INS_ARG_OFFSET) {
ERROR("expected an index($base)");
return 1;
}
state->index = arg->offset.immd;
if (gen_parse_register(&state->base, &arg->offset.reg))
return 1;
break;
case GMR_TARGET:
// target
state->target = 0;
if (arg->type == EXPR_INS_ARG_IMMEDIATE)
state->target = arg->immd;
else if (arg->type == EXPR_INS_ARG_LABEL)
state->label = &arg->label;
else {
ERROR("invalid instruction");
return 1;
}
break;
default:
break;
}
return 0;
}
int gen_read_grammer_state(struct generator *gen,
struct expr *const expr,
struct gen_ins_state *state,
struct mips32_grammer *grammer)
{
char *ptr = grammer->grammer;
uint32_t argi = 0;
// read values into state
while (*ptr != '\0') {
struct expr_ins_arg *arg;
enum grammer_type type;
size_t consumed;
if (argi >= expr->instruction.args_len) {
ERROR("not enough arguments passed");
gen_output_expr(gen, expr);
return 1;
}
arg = &expr->instruction.args[argi++];
consumed = gen_parse_grammer_type(ptr, &type);
if (consumed < 0) {
BUG("invalid grammer: %s", grammer);
return 1;
}
if (read_next_state(arg, state, type)) {
gen_output_expr(gen, expr);
return 1;
}
ptr += consumed;
if (*ptr == ',') {
ptr++;
continue;
}
if (*ptr != '\0') {
BUG("invalid grammer: %s", grammer);
return 1;
}
}
return 0;
}
static int write_next_state(struct gen_ins_state *state,
struct gen_ins_override *over,
union mips32_instruction *ins,
enum reference_type *reftype,
char *ptr)
{
enum grammer_type type;
size_t consumed;
bool hc = false;
consumed = gen_parse_grammer_type(ptr, &type);
if (consumed < 0) {
BUG("invalid grammer: %s", ptr);
return -1;
}
if (*(ptr + consumed) == '=') {
hc = true;
consumed += gen_parse_grammer_override(state, over, ptr);
if (consumed < 0) {
BUG("invalid grammer: %s", ptr);
return -1;
}
}
switch (type) {
case GMR_RD:
ins->rd = hc ? over->reg : state->rd;
break;
case GMR_RS:
ins->rs = hc ? over->reg : state->rs;
break;
case GMR_RT:
ins->rt = hc ? over->reg : state->rt;
break;
case GMR_FS:
ins->fs = hc ? over->fpreg : state->fs;
break;
case GMR_FT:
ins->ft = hc ? over->fpreg : state->ft;
break;
case GMR_FD:
ins->fd = hc ? over->fpreg : state->fd;
break;
case GMR_IMMD:
ins->immd = hc ? over->immd : state->immd;
break;
case GMR_CC:
ins->cc = state->cc;
break;
case GMR_CODE:
ins->code = state->code;
break;
case GMR_POS:
ins->shamt = state->pos;
break;
case GMR_SIZE:
ins->rd = state->size ? state->size - 1 : 0;
break;
case GMR_HB:
ins->hb = hc ? over->immd : state->hb;
break;
case GMR_HINT:
ins->rt = state->hint;
break;
case GMR_OFFSET:
ins->offset = state->offset;
*reftype = REF_MIPS_PC16;
break;
case GMR_OFFSET_BASE:
ins->offset = state->offset;
ins->rs = state->base;
*reftype = REF_MIPS_16;
break;
case GMR_INDEX_BASE:
ins->rt = state->index;
ins->rs = state->base;
break;
case GMR_TARGET:
ins->target = state->target;
*reftype = REF_MIPS_26;
break;
case GMR_HI:
ins->immd = state->target >> 16;
*reftype = REF_MIPS_HI16;
break;
case GMR_LO:
ins->immd = state->target & 0x0000FFFF;
*reftype = REF_MIPS_LO16;
break;
default:
break;
}
return consumed;
}
int gen_write_grammer_state(struct generator *gen,
struct gen_ins_state *state,
char *grammer)
{
enum reference_type reftype = REF_NONE;
struct gen_ins_override over;
char *ptr = grammer;
union mips32_instruction ins;
while (*ptr != '\0') {
int consumed;
consumed = write_next_state(state, &over,
&ins, &reftype, ptr);
if (consumed < 0)
return 1;
ptr += consumed;
if (*ptr == ',') {
ptr++;
continue;
}
if (*ptr != '\0') {
BUG("invalid grammer: %s", grammer);
return 1;
}
}
}