1
0
vt100-games/2048/uz80as/uz80as.c

1241 lines
22 KiB
C

/* ===========================================================================
* uz80as, an assembler for the Zilog Z80 and several other microprocessors.
*
* Assembler.
* ===========================================================================
*/
#include "config.h"
#include "uz80as.h"
#include "options.h"
#include "utils.h"
#include "err.h"
#include "incl.h"
#include "sym.h"
#include "expr.h"
#include "exprint.h"
#include "pp.h"
#include "list.h"
#include "targets.h"
#ifndef ASSERT_H
#include <assert.h>
#endif
#ifndef CTYPE_H
#include <ctype.h>
#endif
#ifndef STDIO_H
#include <stdio.h>
#endif
#ifndef STDLIB_H
#include <stdlib.h>
#endif
#ifndef STRING_H
#include <string.h>
#endif
static void output();
static const char *d_align(const char *);
static const char *d_null(const char *);
static const char *d_block(const char *);
static const char *d_byte(const char *);
static const char *d_chk(const char *);
static const char *d_codes(const char *);
static const char *d_echo(const char *);
static const char *d_eject(const char *);
static const char *d_export(const char *);
static const char *d_end(const char *);
static const char *d_equ(const char *);
static const char *d_fill(const char *);
static const char *d_ds(const char *);
static const char *d_list(const char *);
static const char *d_lsfirst(const char *);
static const char *d_module(const char *);
static const char *d_msfirst(const char *);
static const char *d_nocodes(const char *);
static const char *d_nolist(const char *);
static const char *d_org(const char *);
static const char *d_set(const char *);
static const char *d_text(const char *);
static const char *d_title(const char *);
static const char *d_word(const char *);
/*
* Directives.
* This table must be sorted, to allow for binary search.
*/
static struct direc {
const char *name;
const char *(*fun)(const char *);
} s_directab[] = {
{ "ALIGN", d_align },
{ "BLOCK", d_block },
{ "BYTE", d_byte },
{ "CHK", d_chk },
{ "CODES", d_codes },
{ "DB", d_byte },
{ "DS", d_ds },
{ "DW", d_word },
{ "ECHO", d_echo },
{ "EJECT", d_eject },
{ "END", d_end },
{ "EQU", d_equ },
{ "EXPORT", d_export },
{ "FILL", d_fill },
{ "GLOBAL", d_export },
{ "LIST", d_list },
{ "LSFIRST", d_lsfirst },
{ "MODULE", d_module },
{ "MSFIRST", d_msfirst },
{ "NOCODES", d_nocodes },
{ "NOLIST", d_nolist },
{ "NOPAGE", d_null },
{ "ORG", d_org },
{ "PAGE", d_null },
{ "SECTION", d_null },
{ "SET", d_set },
{ "TEXT", d_text },
{ "TITLE", d_title },
{ "WORD", d_word },
};
/* binary output file */
FILE *fout;
/* output in source order */
int b_flag = 1;
/* The target. */
const struct target *s_target;
/* The z80 addressable memory. The object code. */
static unsigned char s_mem[64 * 1024];
/* Program counter min and max ([s_minpc, s_maxpc[). */
static int s_minpc, s_maxpc;
/* Original input line. */
static char s_line[LINESZ];
/* Label defined on this line. */
static struct sym *s_lastsym;
/* Output words the most significant byte first */
static int s_msbword;
/* If we have seen the .END directive. */
static int s_end_seen;
/* We have issued the error of generating things after an .END. */
static int s_gen_after_end;
/* The empty line, to pass to listing, for compatibility with TASM. */
static const char *s_empty_line = "";
/* Pointer in s_pline for error reporting. */
const char *s_pline_ep;
/* We skip characters until endline or backslash or comment. */
static const char *sync(const char *p)
{
while (*p != '\0' && *p != '\\' && *p != ';')
p++;
return p;
}
/* the written bitmap */
unsigned char membit[65536 / 8];
void
setbit(int pc)
{
membit[pc / 8] |= (1 << (pc % 8));
}
int
isset(int pc)
{
return membit[pc / 8] & (1 << (pc % 8));
}
void
open_output()
{
fout = efopen(s_objfname, "wb");
}
void
close_output()
{
if (fclose(fout) == EOF) {
eprint(_("cannot close file %s\n"), s_objfname);
}
}
/*
* Generates a byte to the output and updates s_pc, s_minpc and s_maxpc.
* Will issue a fatal error if we write beyong 64k.
*/
void genb(int b, const char *ep)
{
if (s_pass == 0 && s_end_seen && !s_gen_after_end) {
s_gen_after_end = 1;
eprint(_("generating code after .END\n"));
eprcol(s_pline, ep);
newerr();
}
if (s_minpc < 0)
s_minpc = s_pc;
if (s_pc >= 65536) {
eprint(_("generating code beyond address 65535\n"));
eprcol(s_pline, ep);
exit(EXIT_FAILURE);
}
s_mem[s_pc] = (unsigned char) b;
setbit(s_pc);
if (s_pass == 1) {
list_genb(b);
if (b_flag) {
fwrite(&s_mem[s_pc], 1, 1, fout);
}
}
if (s_pc < s_minpc)
s_minpc = s_pc;
s_pc++;
if (s_pc > s_maxpc)
s_maxpc = s_pc;
}
/*
* Generate 'n' as a 16 bit word, little endian or big endian depending on
* s_msbword.
*/
static void genw(int n, const char *ep)
{
if (s_msbword)
genb(n >> 8, ep);
genb(n, ep);
if (!s_msbword)
genb(n >> 8, ep);
}
/*
* We have matched an instruction in the table.
* Generate the machine code for the instruction using the generation
* pattern 'p. 'vs are the arguments generated during the matching process.
*/
static void gen(const char *p, const int *vs)
{
// int w, b, i, savepc;
int b, i, savepc;
const char *p_orig;
savepc = s_pc;
p_orig = p;
b = 0;
loop:
i = hexvalu(*p);
if (i >= 0) {
p++;
b = (i << 4) | hexval(*p);
} else if (*p == '.') {
genb(b, s_pline_ep);
b = 0;
} else if (*p == '\0') {
return;
} else {
i = *(p + 1) - '0';
switch (*p) {
case 'b': b |= (vs[i] << 3); break;
case 'c': b |= vs[i]; break;
case 'd': b = vs[i]; break;
case 'e': genb(vs[i] & 0xff, s_pline_ep);
genb(vs[i] >> 8, s_pline_ep);
break;
default:
if (s_target->genf(&b, *p, vs, i, savepc) == -1) {
eprogname();
fprintf(stderr,
_("fatal: bad pattern %s ('%c')"),
p_orig, *p);
enl();
exit(EXIT_FAILURE);
}
}
p++;
}
p++;
goto loop;
}
/*
* Tries to match *p with any of the strings in list.
* If matched, returns the index in list and r points to the position
* in p past the matched string.
*/
int mreg(const char *p, const char *const list[], const char **r)
{
const char *s;
const char *q;
int i;
i = 0;
while ((s = list[i++]) != NULL) {
if (*s == '\0')
continue;
q = p;
while (toupper(*q++) == *s++) {
if (*s == '\0') {
if (!isalnum(*q)) {
*r = q;
return i - 1;
} else {
break;
}
}
}
}
return -1;
}
static int isoctal(int c)
{
return c >= '0' && c <= '7';
}
/*
* Read an octal of 3 digits, being the maximum value 377 (255 decimal);
* Return -1 if there is an error in the syntax.
*/
static int readoctal(const char *p)
{
int n;
const char *q;
if (*p >= '0' && *p <= '3' && isoctal(*(p + 1)) && isoctal(*(p + 2))) {
n = 0;
q = p + 3;
while (p < q) {
n *= 8;
n += (*p - '0');
p++;
}
return n;
}
return -1;
}
enum strmode {
STRMODE_ECHO,
STRMODE_NULL,
STRMODE_BYTE,
STRMODE_WORD
};
/*
* Generate the string bytes until double quote or null char.
* Return a pointer to the ending double quote character or '\0'.
* 'p must point to the starting double quote.
* If mode:
* STRMODE_ECHO only echo to stderr the characters.
* STRMODE_NULL only parses the string.
* STRMODE_BYTE generate the characters in the binary file as bytes.
* STRMODE_WORD generate the characters in the binary file as words.
*/
static const char *genstr(const char *p, enum strmode mode)
{
int c;
for (p = p + 1; *p != '\0' && *p != '\"'; p++) {
c = *p;
if (c == '\\') {
p++;
switch (*p) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 'b': c = '\b'; break;
case 't': c = '\t'; break;
case 'f': c = '\f'; break;
case '\\': c = '\\'; break;
case '\"': c = '\"'; break;
default:
c = readoctal(p);
if (c < 0) {
eprint(_("bad character escape "
"sequence\n"));
eprcol(s_pline, p - 1);
newerr();
p--;
} else {
p += 2;
}
}
}
switch (mode) {
case STRMODE_ECHO: fputc(c, stderr); break;
case STRMODE_NULL: break;
case STRMODE_BYTE: genb(c, p); break;
case STRMODE_WORD: genw(c, p); break;
}
}
return p;
}
/* Match an instruction.
* If no match returns NULL; else returns one past end of match.
* p should point to no whitespace.
*/
static const char *match(const char *p)
{
const struct matchtab *mtab;
const char *s, *pp, *q;
int v, n, vi, linepc;
int vs[4];
assert(!isspace(*p));
mtab = s_target->matcht;
linepc = s_pc;
pp = p;
n = -1;
next:
n++;
s = mtab[n].pat;
if (s == NULL) {
return NULL;
} else if ((s_target->mask & mtab[n].mask) == 0) {
goto next;
} else if (!s_undocumented_op && (s_target->mask & mtab[n].undoc)) {
goto next;
}
p = pp;
vi = 0;
loop:
if (*s == '\0') {
p = skipws(p);
if (*p != ';' && *p != '\0' && *p != '\\')
goto next;
else
goto found;
} else if (*s == ' ') {
if (!isspace(*p))
goto next;
p = skipws(p);
} else if ((*s == ',' || *s == '(' || *s == ')') && isspace(*p)) {
p = skipws(p);
if (*s != *p)
goto next;
p = skipws(p + 1);
} else if (*s == 'a') {
p = expr(p, &v, linepc, s_pass == 0, NULL, NULL);
if (p == NULL)
return NULL;
vs[vi++] = v;
} else if (*s >= 'b' && *s <= 'z') {
v = s_target->matchf(*s, p, &q);
goto reg;
} else if (*p == *s && *p == ',') {
p = skipws(p + 1);
} else if (toupper(*p) == *s) {
p++;
} else {
goto next;
}
freg:
s++;
goto loop;
reg:
if (v < 0) {
goto next;
} else {
assert(vi < sizeof(vs));
vs[vi++] = v;
p = q;
}
goto freg;
found:
// printf("%s\n", s_matchtab[n].pat);
gen(mtab[n].gen, vs);
return p;
}
static const char *
d_null(const char *p)
{
p = sync(p);
while (*p != '\0' && *p != '\\') {
if (!isspace(*p)) {
wprint(_("invalid characters after directive\n"));
eprcol(s_pline, p);
return sync(p);
} else {
p++;
}
}
return p;
}
static const char *d_end(const char *p)
{
enum expr_ecode ecode;
const char *q;
const char *ep;
if (s_pass == 0) {
if (s_end_seen) {
eprint(_("duplicate .END\n"));
eprcol(s_pline, s_pline_ep);
newerr();
} else {
s_end_seen = 1;
}
}
q = expr(p, NULL, s_pc, s_pass == 0, &ecode, &ep);
if (q == NULL && ecode == EXPR_E_NO_EXPR) {
return p;
} else if (q == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
} else {
return q;
}
}
static const char *d_codes(const char *p)
{
s_codes = 1;
return p;
}
static const char *d_module(const char *p)
{
p = sync(p);
while (*p != '\0' && *p != '\\') {
if (!isspace(*p)) {
wprint(_("invalid characters after directive\n"));
eprcol(s_pline, p);
return sync(p);
} else {
p++;
}
}
return p;
}
static const char *d_nocodes(const char *p)
{
s_codes = 0;
return p;
}
static const char *d_list(const char *p)
{
s_list_on = 1;
return p;
}
static const char *d_nolist(const char *p)
{
s_list_on = 0;
return p;
}
static const char *d_eject(const char *p)
{
list_eject();
return p;
}
static const char *d_echo(const char *p)
{
int n;
int mode;
enum expr_ecode ecode;
const char *ep;
mode = (s_pass == 0) ? STRMODE_NULL : STRMODE_ECHO;
if (*p == '\"') {
p = genstr(p, mode);
if (*p == '\"') {
p++;
} else if (s_pass == 0) {
wprint(_("no terminating quote\n"));
eprcol(s_pline, p);
}
} else if (*p != '\0') {
p = expr(p, &n, s_pc, s_pass == 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (mode == STRMODE_ECHO) {
fprintf(stderr, "%d", n);
}
}
return p;
}
static const char *d_equ(const char *p)
{
int n;
enum expr_ecode ecode;
const char *ep;
p = expr(p, &n, s_pc, 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (s_lastsym == NULL) {
eprint(_(".EQU without label\n"));
eprcol(s_pline, s_pline_ep);
newerr();
} else {
/* TODO: check label misalign? */
s_lastsym->val = n;
s_lastsym->isequ = 1;
}
return p;
}
static const char *d_set(const char *p)
{
int n;
enum expr_ecode ecode;
const char *ep;
p = expr(p, &n, s_pc, 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (s_lastsym == NULL) {
eprint(_(".EQU without label\n"));
eprcol(s_pline, s_pline_ep);
newerr();
} else {
/* TODO: check label misalign? */
s_lastsym->val = n;
s_lastsym->isequ = 1;
}
return p;
}
static const char *d_export(const char *p)
{
/* TODO */
return NULL;
}
static const char *d_fill(const char *p)
{
int n, v, er;
const char *q;
enum expr_ecode ecode;
const char *ep, *eps;
eps = p;
er = 0;
p = expr(p, &n, s_pc, 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (n < 0) {
eprint(_("number of positions to fill is negative (%d)\n"), n);
eprcol(s_pline, eps);
exit(EXIT_FAILURE);
}
v = 255;
p = skipws(p);
if (*p == ',') {
p = skipws(p + 1);
q = expr(p, &v, s_pc, s_pass == 0, &ecode, &ep);
if (q == NULL) {
er = 1;
exprint(ecode, s_pline, ep);
newerr();
} else {
p = q;
}
}
while (n--)
genb(v, eps);
if (er)
return NULL;
else
return p;
}
static const char *d_ds(const char *p)
{
int n, v, er;
const char *q;
enum expr_ecode ecode;
const char *ep, *eps;
eps = p;
er = 0;
p = expr(p, &n, s_pc, 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (n < 0) {
eprint(_("number of positions to space over is negative (%d)\n"), n);
eprcol(s_pline, eps);
exit(EXIT_FAILURE);
}
v = 255;
p = skipws(p);
if (*p == ',') {
p = skipws(p + 1);
q = expr(p, &v, s_pc, s_pass == 0, &ecode, &ep);
if (q == NULL) {
er = 1;
exprint(ecode, s_pline, ep);
newerr();
} else {
p = q;
}
}
s_pc += n;
if (er)
return NULL;
else
return p;
}
static const char *d_lsfirst(const char *p)
{
s_msbword = 0;
return p;
}
static const char *d_msfirst(const char *p)
{
s_msbword = 1;
return p;
}
static const char *d_org(const char *p)
{
int n;
enum expr_ecode ecode;
const char *ep, *eps;
eps = p;
p = expr(p, &n, s_pc, 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (n < 0 || n > 65536) {
eprint(_(".ORG address (%d) is not in range [0, 65536]\n"), n);
eprcol(s_pline, eps);
exit(EXIT_FAILURE);
}
s_pc = n;
/* Change the listing PC so in orgs we print the changed PC. */
if (s_pass > 0)
list_setpc(s_pc);
if (s_lastsym != NULL) {
/* TODO: check label misalign? */
s_lastsym->val = s_pc;
s_lastsym->isequ = 1;
}
return p;
}
static const char *d_lst(const char *p, int w)
{
enum strmode mode;
int n, linepc;
enum expr_ecode ecode;
const char *ep, *eps;
if (w)
mode = STRMODE_WORD;
else
mode = STRMODE_BYTE;
linepc = s_pc;
dnlst:
if (*p == '\"') {
p = genstr(p, mode);
if (*p == '\"') {
p++;
} else {
wprint(_("no terminating quote\n"));
eprcol(s_pline, p);
}
} else {
eps = p;
p = expr(p, &n, linepc, s_pass == 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (w)
genw(n, eps);
else
genb(n, eps);
}
p = skipws(p);
if (*p == ',') {
p++;
p = skipws(p);
goto dnlst;
}
return p;
}
static const char *d_align(const char *p)
{
int n, v, er;
const char *q;
enum expr_ecode ecode;
const char *ep, *eps;
eps = p;
er = 0;
p = expr(p, &n, s_pc, 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
if (n < 0) {
eprint(_("align is negative (%d)\n"), n);
eprcol(s_pline, eps);
exit(EXIT_FAILURE);
}
while (s_pc % n) {
genb(0, eps);
}
if (er)
return NULL;
else
return p;
}
static const char *d_byte(const char *p)
{
return d_lst(p, 0);
}
static const char *d_word(const char *p)
{
return d_lst(p, 1);
}
static const char *d_text(const char *p)
{
if (*p == '\"') {
p = genstr(p, STRMODE_BYTE);
if (*p == '\"') {
p++;
} else {
wprint(_("no terminating quote\n"));
eprcol(s_pline, p);
}
return p;
} else {
eprint(_(".TEXT directive needs a quoted string argument\n"));
eprcol(s_pline, p);
newerr();
return NULL;
}
}
static const char *d_title(const char *p)
{
return NULL;
}
static const char *d_block(const char *p)
{
int n;
enum expr_ecode ecode;
const char *ep, *eps;
eps = p;
p = expr(p, &n, s_pc, 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
return NULL;
}
s_pc += n;
if (s_pc < 0 || s_pc > 65536) {
eprint(_("address (%d) set by .BLOCK is not in range "
"[0, 65536]\n"), s_pc);
eprcol(s_pline, eps);
exit(EXIT_FAILURE);
}
return p;
}
/* a must be < b. */
static int checksum(int a, int b)
{
int n;
assert(a < b);
n = 0;
while (a < b)
n += s_mem[a++];
return n;
}
static const char *d_chk(const char *p)
{
int n;
enum expr_ecode ecode;
const char *ep, *eps;
eps = p;
p = expr(p, &n, s_pc, s_pass == 0, &ecode, &ep);
if (p == NULL) {
exprint(ecode, s_pline, ep);
newerr();
genb(0, eps);
return NULL;
}
if (s_pass == 0) {
genb(0, s_pline_ep);
} else if (n < 0 || n >= s_pc) {
eprint(_(".CHK address (%d) is not in range [0, %d[\n"), n,
s_pc);
eprcol(s_pline, eps);
newerr();
genb(0, eps);
} else {
genb(checksum(n, s_pc), eps);
}
return p;
}
/* Parses an internal directive (those that start with '.').
* Returns NULL on error;
* If no error returns position past the parsed directive and arguments. */
static const char *parse_direc(const char *cp)
{
const char *cq, *p;
int a, b, m = 0;
a = 0;
b = NELEMS(s_directab) - 1;
while (a <= b) {
m = (a + b) / 2;
cq = cp;
p = s_directab[m].name;
while (*p != '\0' && toupper(*cq) == *p) {
p++;
cq++;
}
if (*p == '\0' && (*cq == '\0' || isspace(*cq)))
break;
else if (toupper(*cq) < *p)
b = m - 1;
else
a = m + 1;
}
if (a <= b) {
cq = skipws(cq);
return s_directab[m].fun(cq);
} else {
eprint(_("unrecognized directive\n"));
eprcol(s_pline, s_pline_ep);
newerr();
return NULL;
}
}
static void parselin(const char *cp)
{
int col0, alloweq;
const char *q;
s_pline_ep = cp;
start: s_lastsym = NULL;
alloweq = 0;
col0 = 1;
loop:
if (*cp == '\0' || *cp == ';') {
return;
} else if (*cp == '\\') {
if (s_pass == 1) {
list_endln();
list_startln(s_empty_line, curfile()->linenum, s_pc,
nfiles());
}
cp++;
goto start;
} else if (*cp == '.') {
s_pline_ep = cp;
cp++;
q = parse_direc(cp);
if (q == NULL) {
cp = sync(cp);
} else {
cp = d_null(q);
}
} else if ((*cp == '$' || *cp == '*') && cp[1] == '=') {
/* Alternative form of .ORG: *= or $= */
cp += 2;
q = d_org(cp);
if (q == NULL) {
cp = sync(cp);
} else {
cp = d_null(q);
}
} else if (*cp == '=' && alloweq) {
/* equ */
s_pline_ep = cp;
cp++;
q = d_equ(cp);
if (q == NULL) {
cp = sync(cp);
} else {
cp = d_null(q);
}
} else if (isidc0(*cp)) {
if (col0 && *cp != '.') {
/* take label */
s_pline_ep = cp;
q = cp;
col0 = 0;
while (isidc(*cp))
cp++;
s_lastsym = lookup(q, cp, s_pass == 0, s_pc);
if (*cp == ':' || isspace(*cp)) {
alloweq = 1;
cp++;
} else if (*cp == '=') {
alloweq = 1;
}
if (s_pass == 1 && !s_lastsym->isequ &&
s_lastsym->val != s_pc)
{
eprint(_("misaligned label %s\n"),
s_lastsym->name);
fprintf(stderr, _(" Previous value was %XH, "
"new value %XH."), s_lastsym->val,
s_pc);
eprcol(s_pline, s_pline_ep);
newerr();
}
} else {
cp = skipws(cp);
s_pline_ep = cp;
q = match(cp);
if (q == NULL) {
eprint(_("syntax error\n"));
newerr();
cp = sync(cp);
} else {
cp = d_null(q);
}
}
} else if (isspace(*cp)) {
col0 = 0;
while (isspace(*cp))
cp++;
} else {
eprint(_("unexpected character (%c)\n"), *cp);
eprcol(s_pline, cp);
newerr();
cp = sync(cp + 1);
}
goto loop;
}
/*
* Gets a new line into 's_line from 'fin.
* Terminates the line with '\0'.
* Does not read more than LINESZ - 1 characters.
* Does not add a '\n' character, thus a line of length 0 it's possible.
* Always advances to the next line.
* Returns -1 for EOF or the line length.
*/
static int getlin(FILE *fin)
{
int i, c;
c = EOF;
i = 0;
while (i < LINESZ - 1) {
c = getc(fin);
if (c == EOF || c == '\n')
break;
s_line[i++] = (char) c;
}
if (c != EOF && c != '\n') {
wprint(_("line too long, truncated to %d characters\n"),
LINESZ);
}
while (c != EOF && c != '\n')
c = getc(fin);
if (i == 0 && c == EOF)
return -1;
s_line[i] = '\0';
return i;
}
/* Preinstall the macros defined in the command line. */
static void install_predefs(void)
{
struct predef *pdef;
for (pdef = s_predefs; pdef != NULL; pdef = pdef->next)
pp_define(pdef->name);
}
/* Do a pass through the source. */
static void dopass(const char *fname)
{
/* Fill memory with default value. */
if ((s_pass == 0 && s_mem_fillval != 0) || s_pass > 0) {
memset(s_mem, s_mem_fillval, sizeof(s_mem));
}
if (s_pass > 0) {
pp_reset();
list_open(s_lstfname);
s_codes = 1;
s_list_on = 1;
}
install_predefs();
s_minpc = -1;
s_maxpc = -1;
s_pc = 0;
s_lastsym = NULL;
s_msbword = 0;
pushfile(fname, fname + strlen(fname));
while (nfiles() > 0) {
curfile()->linenum++;
if (getlin(curfile()->fin) >= 0) {
if (s_pass == 1) {
list_startln(s_line, curfile()->linenum, s_pc,
nfiles());
}
pp_line(s_line);
if (s_pass == 1)
list_skip(s_skipon);
parselin(s_pline);
if (s_pass == 1)
list_endln();
} else {
popfile();
}
}
if (s_pass > 0) {
list_close();
}
}
/*
* Write the object file in memory order
*/
static void output()
{
int i;
// fprintf(stderr, "output: min: %x max: %x\n", s_minpc, s_maxpc);
if (s_minpc < 0)
s_minpc = 0;
if (s_maxpc < 0)
s_maxpc = 0;
for (i = s_minpc; i < s_maxpc; i++) {
if (isset(i)) {
fwrite(&s_mem[i], 1, 1, fout);
}
}
if (ferror(fout)) {
eprint(_("cannot write to file %s\n"), s_objfname);
clearerr(fout);
}
}
/* Start the assembly using the config in options.c. */
void uz80as(void)
{
s_target = find_target(s_target_id);
if (s_target == NULL) {
eprint(_("target '%s' not supported\n"), s_target_id);
exit(EXIT_FAILURE);
}
for (s_pass = 0; s_nerrors == 0 && s_pass < 2; s_pass++) {
if ((s_pass > 0) && (s_nerrors == 0)) {
open_output();
}
dopass(s_asmfname);
if (s_pass == 0 && !s_end_seen) {
wprint(_("no .END statement in the source\n"));
}
if (s_nerrors == 0) {
if (verbose) printf("Pass %d completed.\n", s_pass + 1);
}
}
if (s_nerrors > 0) {
exit(EXIT_FAILURE);
}
if (!b_flag) {
output();
}
close_output();
}