mcim/mcimc.c

587 lines
8.8 KiB
C

/*
* Copyright (c) 2021, 2022 ipc
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "mcimc.h"
enum {
IDNT,
LBL,
REG,
ADDR,
PROC
};
#define SYMLEN 1024
#define SYMALLOC 8192
#define MAXLBL 8192
#define MAXREG 16
struct sym {
int t;
int l;
char s[SYMLEN];
};
struct ins {
char s[SYMLEN];
char fmt[4];
};
struct lbl {
char s[SYMLEN];
uint addr;
int l;
struct lbl *next;
};
static struct sym *tab;
static struct sym *sp;
static struct lbl *lab[MAXLBL];
static struct lbl *lbp;
static int nsym;
static int asym;
static int errc;
static uchar *buf;
static int abuf;
static uint pc;
struct ins instab[];
static FILE *f;
static char in[1024];
static char *progname;
static void mfree(void);
static void
err(int f, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vfprintf(stderr, fmt, arg);
va_end(arg);
errc++;
if (f) {
mfree();
exit(EXIT_FAILURE);
}
}
static void
usage(void)
{
err(1, "usage: %s [-o out] file\n", progname);
}
static void
mfree(void)
{
int i;
struct lbl *p, *t;
free(tab);
free(buf);
for (i = 0; i < MAXLBL; i++) {
p = lab[i];
while (p != NULL) {
t = p;
p = p->next;
free(t);
}
}
}
static int
allocsym(long n)
{
asym += n;
if ((tab = realloc(tab, asym*sizeof(struct sym))) == NULL)
return -1;
return 0;
}
static struct sym *
makesym(int t, int l, char *s)
{
struct sym *p;
if ((p = malloc(sizeof(struct sym))) == NULL)
return NULL;
p->t = t;
p->l = l;
strcpy(p->s, s);
return p;
}
static struct sym *
pushsym(struct sym *p)
{
if (nsym >= asym)
if (allocsym(SYMALLOC) == -1)
return NULL;
tab[nsym++] = *p;
return p;
}
static struct lbl *
makelbp(char *s, uint n, uint l)
{
struct lbl *p;
if ((p = malloc(sizeof(struct lbl))) == NULL)
return NULL;
strcpy(p->s, s);
p->addr = n;
p->l = l;
p->next = lbp;
lbp = p;
return p;
}
static int l = 1;
static int
cget(void)
{
int c;
c = getc(f);
l += c == '\n';
return c;
}
static int
get(struct sym **p)
{
char s[SYMLEN];
int n;
int c;
int t;
int lp;
n = 0;
t = -1;
while ((c = cget()) != EOF && isspace(c))
;
switch (c) {
case '%':
t = REG;
break;
case '$':
t = ADDR;
break;
case '.':
t = PROC;
while ((c = cget()) != EOF && c != '\n') {
if (n >= SYMLEN-1)
break;
s[n++] = c;
}
s[n] = '\0';
lp = c == '\n' ? l-1 : l;
*p = makesym(t, lp, s);
return 1;
case ';':
while ((c = cget()) != EOF && c != '\n')
;
return 0;
}
if (t == -1 && isalpha(c)) {
t = IDNT;
ungetc(c, f);
while ((c = cget()) != EOF) {
if (c == ':')
t = LBL;
if (!isalnum(c))
break;
if (n >= SYMLEN-1)
break;
s[n++] = c;
}
s[n] = '\0';
lp = c == '\n' ? l-1 : l;
*p = makesym(t, lp, s);
return 1;
}
if (t == REG || t == ADDR) {
while ((c = cget()) != EOF) {
if (!isxdigit(c) && !isspace(c)) {
err(0, "%s:%d: bad address\n", in, l);
break;
} else if (!isxdigit(c))
break;
if (n >= SYMLEN-1)
break;
s[n++] = c;
}
s[n] = '\0';
lp = c == '\n' ? l-1 : l;
*p = makesym(t, lp, s);
return 1;
}
if (c != EOF) {
err(0, "%s:%d: unknown symbol\n", in, l);
return 0;
}
return -1;
}
static int
com(char *s)
{
uint n;
uchar *p;
n = 0;
for (p = (uchar*) s; *p != '\0'; p++)
n = n * 31 + *p;
return n % MAXLBL;
}
static struct lbl *
lookup(char *s, int c)
{
int n;
struct lbl *p;
n = com(s);
for (p = lab[n]; p != NULL; p = p->next)
if (strcmp(s, p->s) == 0)
return p;
if (c) {
if ((p = malloc(sizeof(struct lbl))) == NULL)
return NULL;
strcpy(p->s, s);
p->addr = pc;
p->next = lab[n];
lab[n] = p;
return NULL;
}
return p;
}
static void
lp(uchar *p, uint l)
{
*p = l;
*(p+1) = l >> 8;
*(p+2) = l >> 16;
*(p+3) = l >> 24;
}
static int
allocb(void)
{
abuf += 8192;
buf = realloc(buf, abuf);
return buf == NULL ? -1 : 0;
}
static void
cput(uchar c)
{
if (pc+1 >= abuf)
allocb();
buf[pc++] = c;
}
static void
lput(uint l)
{
if (pc+4 >= abuf)
allocb();
lp(buf+pc, l);
pc += 4;
}
static void
reg(void)
{
int n;
if (sp->t != REG)
err(0, "%s:%d: expected register\n", in, sp->l);
else if ((n = strtol(sp->s, NULL, 16)) >= MAXREG)
err(0, "%s:%d: bad register %02x\n", in, sp->l, n);
else
cput(n);
sp++;
}
static void
addr(void)
{
int n;
n = 0;
if (sp->t == ADDR)
n = strtol(sp->s, NULL, 16);
else if (sp->t == IDNT)
makelbp(sp->s, pc, sp->l);
else {
err(0, "%s:%d: expected immediate\n", in, sp->l);
sp++;
return;
}
lput(n);
sp++;
}
static int gen(void);
static int
pget(char *p, char *s)
{
if (*p != '\'')
return -1;
for (p++; *p != '\0' && *p != '\''; p++)
*s++ = *p;
if (*p != '\'')
return -1;
*s = '\0';
return 0;
}
static void
inc(struct sym *sp, char *p)
{
char path[1024];
char mpath[1024];
FILE *fp;
int lp;
if (pget(p, path) == -1) {
err(0, "%s:%d: expected value in include\n", in, sp->l);
return;
}
fp = f;
if ((f = fopen(path, "r")) == NULL) {
err(0, "%s:%d: couldn't open '%s'\n", in, sp->l, path);
f = fp;
return;
}
strcpy(mpath, in);
strcpy(in, path);
lp = l;
gen();
f = fp;
l = lp;
strcpy(in, mpath);
}
struct proc {
char *s;
void (*fn) (struct sym *, char *);
};
static struct proc proctab[] = {
{ "include", &inc },
{ NULL, NULL }
};
static void
proc(struct sym *sp)
{
int i;
char *p;
char *s;
char com[1024];
p = sp->s;
s = com;
while (*p != '\0' && !isspace(*p))
*s++ = *p++;
*s = '\0';
while (isspace(*p))
p++;
for (i = 0; proctab[i].s != NULL; i++)
if (strcmp(com, proctab[i].s) == 0) {
proctab[i].fn(sp, p);
return;
}
err(0, "%s:%d: unknown directive '%s'\n", in, sp->l, com);
}
static void
ins(void)
{
char *p;
struct ins *ip;
int i;
if (sp->t == LBL) {
if (lookup(sp->s, 1) != NULL)
err(0, "%s:%d: redefining label '%s'\n", in, sp->l,
sp->s);
sp++;
return;
}
if (sp->t != IDNT) {
err(0, "%s:%d: expected instruction\n", in, sp->l);
sp++;
return;
}
for (ip = NULL, i = 0; i < AINS; i++)
if (strcmp(sp->s, instab[i].s) == 0) {
ip = &instab[i];
break;
}
if (ip == NULL) {
err(0, "%s:%d: invalid instruction '%s'\n", in, sp->l, sp->s);
sp++;
return;
}
cput(i);
sp++;
for (p = ip->fmt; *p != '\0'; p++)
switch (*p) {
case 'r':
reg();
break;
case 'i':
addr();
break;
}
}
static void
lbl(void)
{
struct lbl *p;
while (lbp != NULL) {
if ((p = lookup(lbp->s, 0)) == NULL)
err(0, "%s:%d: no such label '%s'\n", in, lbp->l,
lbp->s);
else
lp(buf+lbp->addr, p->addr);
p = lbp;
lbp = lbp->next;
free(p);
}
}
static int
gen(void)
{
struct sym *s;
struct sym n;
int c;
while ((c = get(&s)) != -1)
if (c != 0) {
if (s->t == PROC)
proc(s);
else
pushsym(s);
}
fclose(f);
n.t = -1;
pushsym(&n);
while (sp->t != -1)
ins();
sp++;
if (errc > 0) {
err(0, "%s: %d error%s\n", in, errc, errc>1?"s":"");
errc--;
return -1;
}
errc = 0;
return 0;
}
int
main(int argc, char *argv[])
{
char *out;
char *s;
int n;
uchar hd[4];
progname = *argv[0]!='\0'?argv[0]:"mcimc";
out = "a";
while (--argc > 0 && (*++argv)[0] == '-')
for (s = argv[0]+1; *s != '\0'; s++)
switch (*s) {
case 'o':
if (argc < 2)
usage();
out = argv[1];
argc--;
argv++;
break;
default:
usage();
break;
}
if (argc < 1)
usage();
strcpy(in, argv[0]);
if ((f = fopen(in, "r")) == NULL)
err(1, "%s: couldn't open '%s'\n", progname, in);
if (allocsym(SYMALLOC) == -1) {
fclose(f);
return EXIT_FAILURE;
}
sp = tab;
allocb();
n = gen();
if (n == -1) {
mfree();
return EXIT_FAILURE;
}
lbl();
if ((f = fopen(out, "wb")) == NULL)
err(1, "%s: couldn't open '%s'\n", progname, out);
lp(hd, pc);
fwrite(hd, 1, sizeof hd, f);
fwrite(buf, 1, pc, f);
fclose(f);
mfree();
return 0;
}
struct ins instab[AINS] = {
{ "lw", "ri" },
{ "lwu", "ri" },
{ "lb", "ri" },
{ "li", "ri" },
{ "sw", "ri" },
{ "swu", "ri" },
{ "sb", "ri" },
{ "sr", "ri" },
{ "add", "rrr" },
{ "addi", "rir" },
{ "sub", "rrr" },
{ "mul", "rrr" },
{ "div", "rrr" },
{ "ble", "rri" },
{ "bgt", "rri" },
{ "beq", "rri" },
{ "bne", "rri" },
{ "j", "i" },
{ "jr", "r" },
{ "jal", "i" },
{ "sys", "r" }
};