492 lines
12 KiB
C
492 lines
12 KiB
C
/* Assemble an AMOS extension. Automatically calculates label offsets
|
|
* and numbers. Use "L_Name" as an ordinary label, and do ordinary
|
|
* BSRs/BRAs/etc. to it. (Note: do not use L_xxx for anything else!)
|
|
* For AMOS internal routines, do "jsr L_xxx" or "jmp L_xxx".
|
|
*
|
|
* ExtAsm normally processes all "include" and "incdir" directives
|
|
* itself. If you want to disable this automatic processing for a
|
|
* section of code, bracket it with these two lines:
|
|
|
|
;EXTASM: I
|
|
;... includes ...
|
|
;EXTASM: i
|
|
|
|
* NOTE ON CONDITIONAL INCLUDES:
|
|
* (This section only applies if processing of includes is active.)
|
|
*
|
|
* If you conditinally include one or more files, those include
|
|
* directives *MUST* be bracketed with EXTASM conditionals. They take
|
|
* this form:
|
|
|
|
ifd SYMBOL
|
|
;EXTASM: DSYMBOL
|
|
include "myfile.s"
|
|
;EXTASM: E
|
|
else
|
|
;EXTASM: NSYMBOL
|
|
include "myotherfile.s"
|
|
;EXTASM: E
|
|
endc
|
|
|
|
* The "EXTASM: X" (with X one of D [defined], N [not defined], or E
|
|
* [end conditional]) must be in all caps. Nesting is not allowed.
|
|
* If you do not include these ExtAsm directives, bad things will
|
|
* happen!
|
|
* The symbols used in these tests must be given on the command line
|
|
* using the "-s" option, as in "-sSYMBOL". These are also passed to
|
|
* the assembler, so it must support the "-s" option as well.
|
|
* The ExtAsm tests must be used around INCDIR directives as well.
|
|
*
|
|
* NOTE ON L_xxx LABELS:
|
|
* The first label in your extension (the one that begins the startup
|
|
* stuff) must be L_0. Otherwise, ExtAsm will not recognise any of
|
|
* your labels as valid labels.
|
|
*
|
|
* Copyright (C) 1994-95 Andrew Church.
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <strings.h>
|
|
#include <ctype.h>
|
|
|
|
char verstr[] = "$VER: extasm 1.4 (95/06/18)";
|
|
|
|
FILE *f = NULL, *f2 = NULL;
|
|
char linebuf[256], incdir[256], defincdir[256];
|
|
int ac; /* global copy of argc */
|
|
char **av; /* global copy of argv */
|
|
|
|
char *ASM = "as";
|
|
char *LINK = "dlink";
|
|
|
|
|
|
#define DEBUG
|
|
|
|
#ifdef DEBUG
|
|
/* Set a breakpoint here... */
|
|
void debug()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
|
|
void error(char *s,...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, s);
|
|
vfprintf(stderr, s, args);
|
|
fprintf(stderr, "\n");
|
|
exit(20);
|
|
}
|
|
|
|
FILE *sfopen(char *s, char *m)
|
|
{
|
|
FILE *f = fopen(s,m);
|
|
if (!f) error("Unable to open %s", s);
|
|
return f;
|
|
}
|
|
|
|
char *stripblanks(char *s)
|
|
{
|
|
char *t = s + strlen(s);
|
|
|
|
while (*--t == ' ' || *t == '\t' || *t == '\n' && t >= s); *++t = 0;
|
|
t = s; while (*t == ' ' || *t == '\t' || *t == '\n') t++;
|
|
return strcpy(s, t);
|
|
}
|
|
|
|
char *getline(FILE *f)
|
|
{
|
|
if (fgets(linebuf, 256, f)) {
|
|
if (linebuf[strlen(linebuf)-1] == '\n')
|
|
linebuf[strlen(linebuf)-1] = 0;
|
|
} else {
|
|
*linebuf = 0;
|
|
}
|
|
return linebuf;
|
|
}
|
|
|
|
void include(char *fn, FILE *out)
|
|
{
|
|
FILE *f;
|
|
register char *s, *t;
|
|
register char c;
|
|
char line[256];
|
|
register int ignore = 0; /* set to 1 if an EXTASM conditional is true */
|
|
register int i;
|
|
register int ignore_incl; /* Should we ignore include/incdir's? */
|
|
f = fopen(fn, "r");
|
|
if (!f) {
|
|
strcat(strcpy(line, incdir), fn);
|
|
f = fopen(line, "r");
|
|
if (!f) {
|
|
strcat(strcpy(line, defincdir), fn);
|
|
f = fopen(line, "r");
|
|
if (!f) error("Couldn't find %s (incdir=\"%s\")", fn, incdir);
|
|
}
|
|
}
|
|
s = getline(f);
|
|
if (!strncmp(s, ";EXTASM: ", 9)) {
|
|
c = s[9]; t = s+10;
|
|
switch (c) {
|
|
case 'D':
|
|
ignore = 1;
|
|
for (i = 2; i < ac; i++)
|
|
if (!strnicmp(av[i], "-s", 2) &&
|
|
!strcmp(av[i]+2, t)) {
|
|
ignore = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case 'N':
|
|
ignore = 0;
|
|
for (i = 2; i < ac; i++)
|
|
if (!strnicmp(av[i], "-s", 2) &&
|
|
!strcmp(av[i]+2, t)) {
|
|
ignore = 1;
|
|
break;
|
|
}
|
|
break;
|
|
case 'E':
|
|
ignore = 0;
|
|
break;
|
|
case 'I':
|
|
ignore_incl = 1; break;
|
|
case 'i':
|
|
ignore_incl = 0; break;
|
|
default:
|
|
error("Unknown EXTASM conditional, type %c", c);
|
|
}
|
|
}
|
|
if (*s == '*') *s = 0;
|
|
if (t = strchr(s, ';')) *t = 0;
|
|
strcpy(line, s);
|
|
stripblanks(s);
|
|
while (!feof(f) && stricmp(s, "end")) {
|
|
if (!ignore)
|
|
if (!strnicmp(s, "include", 7) && isspace(s[7]) &&
|
|
!ignore_incl) {
|
|
stripblanks(strcpy(s, s+7));
|
|
t = s+1;
|
|
while (*t != *s) t++; *t = 0;
|
|
include(s+1, out);
|
|
} else if (!strnicmp(s, "incdir", 6) && isspace(s[6]) &&
|
|
!ignore_incl) {
|
|
stripblanks(strcpy(s, s+6));
|
|
t = s+1;
|
|
while (*t != *s) t++; *t = 0;
|
|
strcpy(incdir, s+1);
|
|
if (*(t = incdir + strlen(incdir)-1) != ':' &&
|
|
*t != '/')
|
|
strcat(incdir, "/");
|
|
} else
|
|
fprintf(out, "%s\n", line);
|
|
s = getline(f);
|
|
if (!strncmp(s, ";EXTASM: ", 9)) {
|
|
c = s[9]; t = s+10;
|
|
switch (c) {
|
|
case 'D':
|
|
ignore = 1;
|
|
for(i = 2; i < ac; i++)
|
|
if (!strncmp(av[i], "-S", 2) &&
|
|
!strcmp(av[i]+2, t)) {
|
|
ignore = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case 'N':
|
|
ignore = 0;
|
|
for(i = 2; i < ac; i++)
|
|
if (!strncmp(av[i], "-S", 2) &&
|
|
!strcmp(av[i]+2, t)) {
|
|
ignore = 1;
|
|
break;
|
|
}
|
|
break;
|
|
case 'E':
|
|
ignore = 0;
|
|
break;
|
|
case 'I':
|
|
ignore_incl = 1; break;
|
|
case 'i':
|
|
ignore_incl = 0; break;
|
|
default:
|
|
error("Unknown EXTASM conditional, type %c", c);
|
|
}
|
|
}
|
|
if (*s == '*') *s = 0;
|
|
if (t = strchr(s, ';')) *t = 0;
|
|
strcpy(line, s);
|
|
stripblanks(s);
|
|
}
|
|
#ifdef DEBUG
|
|
debug();
|
|
#endif
|
|
fclose(f);
|
|
}
|
|
|
|
void main(int argc, char **argv)
|
|
{
|
|
static char in[64], out[64], lbl[32], inst[16], op[64], cmd[512],
|
|
line[256], listout[64];
|
|
register char *s, *t;
|
|
int i, n, labelnum = 0, nlabels, codepos, codesize, objsize, curpos,
|
|
codesize2;
|
|
int gotfirstlabel = 0; /* did we find the first label? */
|
|
static int lpos[2048];
|
|
short j, bad, listing = 0;
|
|
char *buf;
|
|
FILE *listf;
|
|
|
|
for (i = 0; i < 2048; i++) lpos[i] = -1;
|
|
*in = *out = *listout = 0;
|
|
ac = argc; av = argv;
|
|
for (i = 1; i < argc; i++) {
|
|
if (*(s = argv[i]) == '-')
|
|
switch (s[1]) {
|
|
case 'o': /* output filename */
|
|
if (s[2] == 0) {
|
|
if (--argc == 0) {
|
|
fprintf(stderr, "-o option needs a filename\n");
|
|
exit(20);
|
|
}
|
|
s = argv[++i];
|
|
} else
|
|
s += 2;
|
|
strcpy(out, s);
|
|
break;
|
|
case 'S': /* define a symbol */
|
|
break; /* - handled by include() */
|
|
case 'l': /* listing of symbols and Lnnn labels */
|
|
listing = 1;
|
|
if (s[2] == '=')
|
|
strcpy(listout, s+3);
|
|
break;
|
|
case 'A': /* assembler */
|
|
if (s[2] == 0) {
|
|
if (--argc == 0) {
|
|
fprintf(stderr, "-A option needs a filename\n");
|
|
exit(20);
|
|
}
|
|
s = argv[++i];
|
|
} else
|
|
s += 2;
|
|
ASM = s; break;
|
|
case 'L': /* linker */
|
|
if (s[2] == 0) {
|
|
if (--argc == 0) {
|
|
fprintf(stderr, "-L option needs a filename\n");
|
|
exit(20);
|
|
}
|
|
s = argv[++i];
|
|
} else
|
|
s += 2;
|
|
LINK = s; break;
|
|
default:
|
|
fprintf(stderr, "Unknown option %s\n", s);
|
|
exit(20);
|
|
}
|
|
else {
|
|
if (*in) {
|
|
fprintf(stderr, "Input file already specified\n");
|
|
exit(20);
|
|
}
|
|
strcpy(in, s);
|
|
}
|
|
}
|
|
if (!*in) error("required argument missing");
|
|
if (!*out) {
|
|
strcpy(out, in);
|
|
if (s = strrchr(out, '.')) *s = 0;
|
|
strcat(out, ".lib");
|
|
}
|
|
if (listing) {
|
|
if (!*listout) {
|
|
strcpy(listout, in);
|
|
if (s = strrchr(listout, '.')) *s = 0;
|
|
strcat(listout, ".lst");
|
|
}
|
|
listf = sfopen(listout, "w");
|
|
}
|
|
f2 = sfopen("T:___tmp___", "w");
|
|
strcpy(incdir, in);
|
|
if (s = strrchr(incdir, '/'))
|
|
*++s = 0;
|
|
else if (s = strrchr(incdir, ':'))
|
|
*++s = 0;
|
|
strcpy(defincdir, incdir);
|
|
include(in, f2);
|
|
fclose(f2);
|
|
f = sfopen("T:___tmp___", "r");
|
|
f2 = sfopen("T:extasm_tmp.a", "w");
|
|
while (!feof(f)) {
|
|
strcpy(line, s = getline(f));
|
|
*lbl = *inst = *op = 0;
|
|
if (*s != ' ' && *s != '\t' && *s != '\n' && *s) {
|
|
t = s; i = 0;
|
|
do
|
|
lbl[i++] = *t++;
|
|
while (*t != ' ' && *t != '\t' && *t != '\n' &&
|
|
*t != ':' && *t);
|
|
lbl[i] = 0;
|
|
if (*t == ':') t++;
|
|
strcpy(s, t);
|
|
t = lbl+1;
|
|
while (t = strchr(t, '.')) {
|
|
*t = '_';
|
|
*(line + (t - lbl)) = '_';
|
|
}
|
|
}
|
|
t = stripblanks(s);
|
|
if (!*s || *s == ';' || *s == '*')
|
|
if (!strncmp(lbl, "L_", 2)) {
|
|
if (!gotfirstlabel)
|
|
gotfirstlabel = !strcmp(lbl+2, "0");
|
|
if (gotfirstlabel) {
|
|
fprintf(f2, "%s\tequ %d\n\txdef\tL%d\nL%d\n",
|
|
lbl, labelnum, labelnum, labelnum);
|
|
if (listing)
|
|
fprintf(listf, "L%d\t%s\n", labelnum, lbl);
|
|
labelnum++;
|
|
} else
|
|
fprintf(f2, "%s\n", line);
|
|
} else
|
|
fprintf(f2, "%s\n", line);
|
|
else {
|
|
i = 0;
|
|
while (*t != ' ' && *t != '\t' && *t != '\n' && *t)
|
|
inst[i++] = *t++;
|
|
inst[i] = 0;
|
|
stripblanks(strcpy(op, t));
|
|
/* check for 'j' or 'b' (e.g. jsr/bsr) since we don't
|
|
* want "rdc.w L_xxx,-1" in the token table.
|
|
*/
|
|
if (!strncmp(t = op, "L_", 2) &&
|
|
((*inst == 'j' || *inst == 'J') ||
|
|
(*inst == 'b' || *inst == 'B'))) {
|
|
while (t = strchr(t, '.'))
|
|
*t = '_';
|
|
sprintf(line, "%s\tr%s\t%s", lbl, inst, op);
|
|
}
|
|
#ifdef FIX_RS
|
|
if (!strnicmp(inst, "rs.", 3)) {
|
|
for (t = op; *t && *t != '\t' &&
|
|
*t != ' ' && *t != ';'; t++);
|
|
*t = 0;
|
|
switch (inst[3]) {
|
|
case 'w':
|
|
strcpy(op, strcat(strcat(
|
|
strcpy(s,"("), op
|
|
), ")*2"));
|
|
break;
|
|
case 'l':
|
|
strcpy(op, strcat(strcat(strcpy(s,"("), op), ")*4"));
|
|
break;
|
|
}
|
|
if (*lbl) fprintf(f2, "%s\tequ __RS\n", lbl);
|
|
fprintf(f2,"__RS\tset __RS+%s\n", op);
|
|
} else if (!stricmp(op, "__RS")) {
|
|
/* Sometimes __Rs is used instead */
|
|
strcpy(op, "__RS");
|
|
fprintf(f2, "%s\t%s %s\n", lbl, inst, op);
|
|
} else if (!stricmp(inst, "rsreset"))
|
|
fprintf(f2,"__RS\tset 0\n");
|
|
else if (stricmp(inst, "rsset")) /* ignore RsSet */
|
|
#endif
|
|
if (!strncmp(lbl, "L_", 2)) {
|
|
if (!gotfirstlabel)
|
|
gotfirstlabel = !strcmp(lbl+2, "0");
|
|
if (gotfirstlabel) {
|
|
fprintf(f2,
|
|
"%s\tequ %d\n\txdef\tL%d\nL%d\t%s%s%s\n",
|
|
lbl, labelnum, labelnum, labelnum,
|
|
inst, *op ? "\t" : "", op);
|
|
labelnum++;
|
|
} else
|
|
fprintf(f2, "%s\n", line);
|
|
} else
|
|
fprintf(f2, "%s\n", line);
|
|
}
|
|
}
|
|
nlabels = labelnum;
|
|
fclose(f);
|
|
fclose(f2);
|
|
if (listing) fclose(listf);
|
|
remove("T:___tmp___");
|
|
sprintf(cmd, "%s T:extasm_tmp.a -oT:eatmp.o", ASM);
|
|
for (i = 1; i < argc; i++)
|
|
if (!strncmp(argv[i], "-S", 2))
|
|
strcat(strcat(cmd, " "), argv[i]);
|
|
i = system(cmd);
|
|
remove("T:extasm_tmp.a");
|
|
if (i >= 10) exit(i);
|
|
f = sfopen("T:eatmp.o", "r");
|
|
fread(&n, 4, 1, f);
|
|
while (n != 0x3E9) {
|
|
fread(&n, 4, 1, f); fseek(f, n*4, SEEK_CUR);
|
|
fread(&n, 4, 1, f);
|
|
}
|
|
codepos = ftell(f);
|
|
fread(&codesize, 4, 1, f); codesize *= 4;
|
|
fseek(f, codesize, SEEK_CUR);
|
|
fread(&n, 4, 1, f);
|
|
while (n != 0x3EF) {
|
|
fread(&n, 4, 1, f); fseek(f, n*4, SEEK_CUR);
|
|
fread(&n, 4, 1, f);
|
|
}
|
|
fread(&n, 4, 1, f);
|
|
while (n) {
|
|
j = n >> 24; n &= 0xFFFFFF;
|
|
switch (j) {
|
|
case 0x01: /* XDEF */
|
|
fread(lbl, 1, n*4, f);
|
|
fread(&n, 4, 1, f);
|
|
if (*lbl == 'L') {
|
|
labelnum = strtol(lbl+1, NULL, 10);
|
|
lpos[labelnum] = n;
|
|
}
|
|
break;
|
|
default: /* shouldn't be any others */
|
|
fprintf(stderr, "Weird HUNK_EXT entry 0x%02x%06x\n",
|
|
j, n);
|
|
exit(20);
|
|
}
|
|
fread(&n, 4, 1, f);
|
|
}
|
|
for (i = 0, bad = 0; i < nlabels; i++) {
|
|
if (lpos[i] == -1) {
|
|
fprintf(stderr, "Label L%d not found\n", i);
|
|
bad = 1;
|
|
}
|
|
}
|
|
if (bad) exit(20);
|
|
fseek(f, 0, SEEK_END); objsize = ftell(f); fseek(f, 0, SEEK_SET);
|
|
f2 = sfopen("T:extasm_tmp.o", "w");
|
|
buf = malloc(objsize); if (!buf) error("Out of memory");
|
|
fread(buf, 1, codepos, f); fwrite(buf, 1, codepos, f2);
|
|
codesize2 = codesize + nlabels * 2 - 2;
|
|
n = (codesize2 + 3) / 4; fwrite(&n, 4, 1, f2);
|
|
fseek(f, 8, SEEK_CUR);
|
|
n = nlabels * 2 - 2; fwrite(&n, 4, 1, f2);
|
|
fread(buf, 1, 14, f); fwrite(buf, 1, 14, f2);
|
|
curpos = lpos[0];
|
|
for (labelnum = 1; labelnum < nlabels; labelnum++) {
|
|
j = (lpos[labelnum] - curpos) / 2;
|
|
fwrite(&j, 2, 1, f2);
|
|
curpos = lpos[labelnum];
|
|
}
|
|
fwrite(buf, 1, i = fread(buf, 1 , codesize-18, f), f2);
|
|
if (codesize2 & 3) { /* longword align */
|
|
j = 0; fwrite(&j, 2, 1, f2);
|
|
}
|
|
fwrite(buf, 1, fread(buf, 1, objsize, f), f2); /* copy remainder */
|
|
fclose(f); fclose(f2); remove("T:eatmp.o");
|
|
sprintf(cmd, "%s T:extasm_tmp.o -o%s", LINK, out);
|
|
i = system(cmd);
|
|
remove("T:extasm_tmp.o");
|
|
exit(i);
|
|
}
|