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

448 lines
10 KiB
C

#include <stdlib.h>
#include <merror.h>
#include <melf.h>
#include <mips.h>
#include <mips1.h>
#include <mips32r2.h>
#include <mips32r6.h>
#include "tab.h"
#include "gen.h"
#include "parse.h"
///
/// generation functions
///
static int gen_directive_whb(struct generator *gen, const void *data,
uint32_t count, uint32_t len)
{
// TODO: endianess
for (uint32_t i = 0; i < count; i++) {
void *ptr = (char *) data + (len * i);
if (section_push(gen->current, ptr, len))
return M_ERROR;
}
return M_SUCCESS;
}
static int gen_directive(struct generator *gen,
const struct expr *const e)
{
const struct expr_directive *const expr = &e->directive;
int res = M_SUCCESS;
switch (expr->type) {
case EXPR_DIRECTIVE_ALIGN:
if (expr->align < 1) {
ERROR("alignment cannot be zero");
print_curr_line(gen, e);
return M_ERROR;
}
gen->current->align = expr->align;
break;
case EXPR_DIRECTIVE_SPACE:
res = section_zero(gen->current, expr->space);
break;
case EXPR_DIRECTIVE_WORD:
res = gen_directive_whb(gen, expr->words, expr->len,
sizeof(uint32_t));
break;
case EXPR_DIRECTIVE_HALF:
res = gen_directive_whb(gen, expr->halfs, expr->len,
sizeof(uint16_t));
break;
case EXPR_DIRECTIVE_BYTE:
res = gen_directive_whb(gen, expr->bytes, expr->len,
sizeof(uint8_t));
break;
case EXPR_DIRECTIVE_SECTION:
res = section_get(gen, &gen->current, &expr->section);
break;
case EXPR_DIRECTIVE_EXTERN: {
struct symbol *sym;
res = symtab_find_or_stub(&gen->symtab, &sym, &expr->label);
if (res == M_SUCCESS)
sym->type = SYM_EXTERN;
break;
}
case EXPR_DIRECTIVE_GLOBL: {
struct symbol *sym;
res = symtab_find_or_stub(&gen->symtab, &sym, &expr->label);
if (res == M_SUCCESS)
sym->type = SYM_GLOBAL;
break;
}
case EXPR_DIRECTIVE_ASCII:
res = section_push(gen->current, expr->string.str,
expr->string.len - 1);
break;
case EXPR_DIRECTIVE_ASCIIZ:
res = section_push(gen->current, expr->string.str,
expr->string.len);
break;
}
return res;
}
static int gen_constant(struct generator *gen, struct expr_const *const expr)
{
(void) gen;
(void) expr;
ERROR("constants not yet implemented");
return M_ERROR;
}
static int gen_ins_write_state(
struct generator *gen,
union mips32_instruction ins, // the instruction to modify
struct gen_ins_state *state, // the current read state
char *grammer) // the gramemr to parse
{
// grammer pointer
char *ptr = grammer;
// new reference type if needed
enum reference_type reftype = REF_NONE;
// hardcoded
struct gen_ins_override hstate;
bool hc = false;
// read values into state
while (*ptr != '\0') {
// parse next dsl entry
size_t skip;
enum grammer_type gmr = get_gmr_type(ptr, &skip);
// check for dsl hardcoded register argument
hc = false;
if (*(ptr + skip) == '=') {
// get value pointer
char *value = ptr + skip + 1;
hc = true;
// parse value
if (get_gmr_hc(state, &hstate, value)) {
BUG("invalid grammer: %s", grammer);
exit(1);
}
}
// skip till next comma
for (;*ptr != '\0' && *ptr != ','; ptr++);
if (*ptr == ',')
ptr++;
switch (gmr) {
case GMR_RD:
ins.rd = hc ? hstate.reg : state->rd;
break;
case GMR_RS:
ins.rs = hc ? hstate.reg : state->rs;
break;
case GMR_RT:
ins.rt = hc ? hstate.reg : state->rt;
break;
case GMR_FS:
ins.fs = hc ? hstate.fpreg : state->fs;
break;
case GMR_FT:
ins.ft = hc ? hstate.fpreg : state->ft;
break;
case GMR_FD:
ins.fd = hc ? hstate.fpreg : state->fd;
break;
case GMR_IMMD:
ins.immd = hc ? hstate.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 ? hstate.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;
break;
}
}
// get offset for reference (if needed)
uint32_t offset = gen->current->len;
size_t zeros = offset % gen->current->align;
if (zeros)
zeros = gen->current->align - zeros;
offset += zeros;
// write instructon to section
uint32_t raw = B32(ins.raw);
if (section_push(gen->current, &raw, sizeof(uint32_t))) {
return M_ERROR;
}
// create reference (if needed)
if (reftype != REF_NONE && state->label != NULL) {
struct symbol *sym;
if (symtab_find_or_stub(&gen->symtab, &sym, state->label))
return M_ERROR;
struct reference ref = {
.type = reftype,
.symbol = sym,
.offset = offset
};
if (reftab_push(&gen->current->reftab, &ref)) {
return M_ERROR;
}
}
return M_SUCCESS;
}
static int gen_ins(struct generator *gen, struct expr *const expr)
{
struct mips32_grammer *grammer = NULL;
for (uint32_t i = 0; i < gen->grammers_len; i++) {
struct mips32_grammer *temp = &gen->grammers[i];
if (strcasecmp(temp->name, expr->instruction.name.str) != 0)
continue;
grammer = temp;
break;
}
if (grammer == NULL) {
ERROR("unknown instruction");
print_curr_line(gen, expr);
return M_ERROR;
}
struct gen_ins_state state;
state.label = NULL;
// read in the values from the parser
if (gen_ins_read_state(gen, expr, &state, grammer))
return M_ERROR;
// write the values into the instructions
// ...and then the sections
if (grammer->pseudo_len > 0) {
// write pseudo
for (int i = 0; i < grammer->pseudo_len; i++) {
union mips32_instruction ins = gen->instructions[
grammer->pseudo_grammer[i].enum_index];
if (gen_ins_write_state(gen, ins, &state,
grammer->pseudo_grammer[i].update))
return M_ERROR;
}
} else {
// write real
union mips32_instruction ins
= gen->instructions[grammer->enum_index];
if (gen_ins_write_state(gen, ins, &state, grammer->grammer))
return M_ERROR;
}
return M_SUCCESS;
}
static int gen_label(struct generator *gen, struct string *const label)
{
uint32_t offset = gen->current->len;
ptrdiff_t secidx = gen->current - gen->sections;
size_t zeros = offset % gen->current->align;
if (zeros)
zeros = gen->current->align - zeros;
offset += zeros;
struct symbol *sym;
/* update existing symbol (if exists) */
if (symtab_find(&gen->symtab, &sym, label->str) == M_SUCCESS) {
if (sym->secidx != SYM_SEC_STUB) {
// symbols that are not labeled stub are fully defined,
// it is a error to redefine them
ERROR("redefined symbol '%s'", label->str);
return M_ERROR;
}
sym->secidx = secidx;
sym->offset = offset;
/* create a new symbol */
} else {
struct symbol new = {
.secidx = secidx,
.offset = offset,
.type = SYM_LOCAL,
};
if (string_clone(&new.name, label))
return M_ERROR;
if (symtab_push(&gen->symtab, &new)) {
string_free(&new.name);
return M_ERROR;
}
}
return M_SUCCESS;
}
/* run codegen for next expression */
static int generate_next(struct generator *gen)
{
struct expr expr;
int res = M_SUCCESS;
// get the next expression
if ((res = parser_next(&gen->parser, &expr)))
return res;
// if its not a segment directive
// (and we dont have a section)
// create the default
if ((
expr.type != EXPR_DIRECTIVE ||
expr.directive.type != EXPR_DIRECTIVE_SECTION) &&
gen->current == NULL) {
// create .data section
struct string temp = {
.str = ".data",
.len = 5,
.size = 5,
.allocated = false
};
if (section_get(gen, &gen->current, &temp)) {
expr_free(&expr);
return M_ERROR;
}
}
res = M_SUCCESS;
switch (expr.type) {
case EXPR_DIRECTIVE:
res = gen_directive(gen, &expr);
break;
case EXPR_CONSTANT:
res = gen_constant(gen, &expr.constant);
break;
case EXPR_INS:
res = gen_ins(gen, &expr);
break;
case EXPR_LABEL:
res = gen_label(gen, &expr.label);
break;
}
expr_free(&expr);
return res;
}
static int generate(struct generator *gen)
{
int res;
while (res = generate_next(gen), 1) {
if (res == M_ERROR)
return M_ERROR;
if (res == M_EOF)
break;
}
return M_SUCCESS;
}
//
// the following functions set default generator
// state values for different versions of the mips
// isa
//
/* run codegen with the mips32r6 specification */
int generate_mips32r6(struct generator *gen)
{
gen->instructions_len = __MIPS32R6_INS_LEN;
gen->instructions = mips32r6_instructions;
gen->grammers_len = __MIPS32R6_GRAMMER_LEN;
gen->grammers = mips32r6_grammers;
return generate(gen);
}
/* run codegen with the mips32r2 specification */
int generate_mips32r2(struct generator *gen)
{
gen->instructions_len = __MIPS32R2_INS_LEN;
gen->instructions = mips32r2_instructions;
gen->grammers_len = __MIPS32R2_GRAMMER_LEN;
gen->grammers = mips32r2_grammers;
return generate(gen);
}
/* run codegen with the mips32r6 specification */
int generate_mips1(struct generator *gen)
{
gen->instructions_len = __MIPS1_INS_LEN;
gen->instructions = mips1_instructions;
gen->grammers_len = __MIPS1_GRAMMER_LEN;
gen->grammers = mips1_grammers;
return generate(gen);
}
//
// constructors and deconstructors
//
int generator_init(const char *file, struct generator *gen)
{
if (parser_init(file, &gen->parser))
return M_ERROR;
if (symtab_init(&gen->symtab))
return M_ERROR;
gen->sections = NULL;
gen->sections_len = 0;
gen->sections_size = 0;
return M_SUCCESS;
}
void generator_free(struct generator *gen)
{
parser_free(&gen->parser);
symtab_free(&gen->symtab);
for (size_t i = 0; i < gen->sections_len; i++)
section_free(&gen->sections[i]);
free(gen->sections);
}