1
0
mirror of https://github.com/fachat/xa65.git synced 2024-06-08 23:29:30 +00:00
xa65/xa/src/xa.c
2023-12-07 23:34:19 +01:00

1275 lines
31 KiB
C

/* xa65 - 65xx/65816 cross-assembler and utility suite
*
* Copyright (C) 1989-1997 Andre Fachat (afachat@gmx.de)
* maintained by Cameron Kaiser (ckaiser@floodgap.com)
*
* Main program
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <time.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
/* macros */
#include "xad.h"
/* structs and defs */
#include "xah.h"
#include "xah2.h"
/* exported functions are defined here */
#include "xa.h"
#include "xal.h"
#include "xam.h"
#include "xao.h"
#include "xap.h"
#include "xar.h"
#include "xat.h"
#include "xacharset.h"
#include "xalisting.h"
#include "version.h"
/* ANZERR: total number of errors */
/* ANZWARN: total number of warnings */
#define ANZERR 64
#define ANZWARN 13
#define programname "xa"
/* progversion now in xa.h */
#define authors "Written by Andre Fachat, Jolse Maginnis, David Weinehall and Cameron Kaiser"
#define copyright "Copyright (C) 1989-2023 Andre Fachat, Jolse Maginnis, David Weinehall\nand Cameron Kaiser."
/* exported globals */
int ncmos, cmosfl, w65816, n65816;
/* compatibility flags */
int masm = 0; /* MASM */
int ca65 = 0; /* CA65 */
int xa23 = 0; /* ^ and recursive comments, disable \ escape */
int ctypes = 0; /* C compatibility, like "0xab" types */
int nolink = 0;
int romable = 0;
int romaddr = 0;
int noglob = 0;
int showblk = 0;
int crossref = 0;
int undefok = 0;// -R only accepts -Llabels; with -U all undef'd labels are ok in -R mode
char altppchar;
/* local variables */
static char out[MAXLINE];
static time_t tim1, tim2;
static FILE *fpout, *fperr, *fplab, *fplist;
static int ner = 0;
static int ner_max = 20;
static int align = 1;
static void printstat(void);
static void usage(int, FILE*);
static int setfext(char*, char*);
static int x_init(void);
static int pass1(void);
static int pass2(void);
static int puttmp(int);
static int puttmpw(int);
static int puttmps(signed char*, int);
static void chrput(int);
static int xa_getline(char*);
static void lineout(void);
static long ga_p1(void);
static long gm_p1(void);
static int set_compat(char *compat_name);
/* text */
int memode, xmode;
int segment;
int tlen = 0, tbase = 0x1000;
int dlen = 0, dbase = 0x0400;
int blen = 0, bbase = 0x4000;
int zlen = 0, zbase = 4;
int fmode = 0;
int relmode = 0;
int pc[SEG_MAX]; /* segments */
int main(int argc, char *argv[]) {
int er = 1, i;
signed char *s = NULL;
char *tmpp;
char *listformat = NULL;
int mifiles = 5;
int nifiles = 0;
int verbose = 0;
int no_link = 0;
char **ifiles;
char *printfile; /* print listing to this file */
char *ofile; /* output file */
char *efile; /* error listing goes there */
char *lfile; /* labels go here */
char *ifile;
char old_e[MAXLINE];
char old_l[MAXLINE];
char old_o[MAXLINE];
tim1 = time(NULL);
// note: unfortunately we do no full distinction between 65C02 and 65816.
// The conflict is in the column 7 and column f opcodes, where the 65C02
// has the BBR/BBS/SMB/RMB opcodes, but the 65816 has its own.
// Also, we potentially could support the 65SC02, which is the 65C02, but
// without the conflicting BBR/BBS/SMB/RMB opcodes.
// This, however, is a TODO for a later version.
cmosfl = 1;
//fmode = FM_CPU2_65C02;
w65816 = 0; /* default: 6502 only */
ncmos = 0; // counter for CMOS opcodes used
n65816 = 0; // counter for 65816-specific opcodes used
altppchar = '#'; /* i.e., NO alternate char */
if ((tmpp = strrchr(argv[0], '/'))) {
tmpp++;
} else {
tmpp = argv[0];
}
if ((!strcmp(tmpp, "xa65816")) || (!strcmp(tmpp, "XA65816"))
|| (!strcmp(tmpp, "xa816")) || (!strcmp(tmpp, "XA816"))) {
w65816 = 1; /* allow 65816 per default */
}
/* default output charset for strings in quotes */
set_charset("ASCII");
ifiles = malloc(mifiles * sizeof(char*));
afile = alloc_file();
if (argc <= 1) {
usage(w65816, stderr);
exit(1);
}
if (strstr(argv[1], "--help") || strstr(argv[1], "-?")) {
usage(w65816, stdout);
exit(0);
}
if (strstr(argv[1], "--version")) {
version(programname, progversion, authors, copyright);
exit(0);
}
ofile = "a.o65";
efile = NULL;
lfile = NULL;
printfile = NULL;
if (pp_init()) {
logout("fatal: pp: no memory!");
return 1;
}
if (b_init()) {
logout("fatal: b: no memory!");
return 1;
}
if (l_init()) {
logout("fatal: l: no memory!");
return 1;
}
i = 1;
while (i < argc) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'E':
ner_max = 0;
break;
case 'p':
/* intentionally not allowing an argument to follow with a
space to avoid - being seen as the alternate
preprocessor char! */
if (argv[i][2] == '\0') {
fprintf(stderr, "-p requires a character argument\n");
exit(1);
}
if (argv[i][2] == '#')
fprintf(stderr,
"using -p# is not necessary, '#' is the default\n");
altppchar = argv[i][2];
if (argv[i][3] != '\0')
fprintf(stderr,
"warning: extra characters to -p ignored\n");
break;
case 'M':
fprintf(stderr,
"Warning: -M is deprecated (use -XMASM) and will be removed in a future version\n");
masm = 1; /* MASM compatibility mode */
break;
case 'X': /* compatibility across assemblers... */
{
char *name = NULL;
if (argv[i][2] == 0) {
name = argv[++i];
} else {
name = argv[i] + 2;
}
if (set_compat(name) < 0) {
fprintf(stderr,
"Compatibility set '%s' unknown - ignoring! (check case?)\n",
name);
}
}
break;
case 'O': /* output charset */
{
char *name = NULL;
if (argv[i][2] == 0) {
if (i + 1 < argc)
name = argv[++i];
else {
fprintf(stderr, "-O requires an argument\n");
exit(1);
}
} else {
name = argv[i] + 2;
}
if (set_charset(name) < 0) {
fprintf(stderr,
"Output charset name '%s' unknown - ignoring! (check case?)\n",
name);
}
}
break;
case 'A': /* make text segment start so that text relocation
is not necessary when _file_ starts at adr */
romable = 2;
if (argv[i][2] == 0) {
if (i + 1 < argc)
romaddr = atoi(argv[++i]);
else {
fprintf(stderr, "-A requires an argument\n");
exit(1);
}
} else
romaddr = atoi(argv[i] + 2);
break;
case 'G':
noglob = 1;
break;
case 'L': /* define global label */
if (argv[i][2])
lg_set(argv[i] + 2);
break;
case 'r':
crossref = 1;
break;
case 'R':
relmode = 1;
break;
case 'U':
undefok = 1;
break;
case 'D':
s = (signed char*) strstr(argv[i] + 2, "=");
if (s)
*s = ' ';
pp_define(argv[i] + 2);
break;
case 'c':
no_link = 1;
fmode |= FM_OBJ;
break;
case 'v':
verbose = 1;
break;
case 'C':
cmosfl = 0;
fmode &= ~FM_CPU2; // fall back to standard 6502
// breaks existing tests (compare with pre-assembled files)
//if (w65816) {
// fmode |= FM_CPU2_65816E;
//}
break;
case 'W':
w65816 = 0;
fmode &= ~FM_CPU;
fmode &= ~FM_CPU2;
// breaks existing tests (compare with pre-assembled files)
//if (cmosfl) {
// fmode |= FM_CPU2_65C02;
//}
break;
case 'w':
// note: we do not disable cmos here, as opcode tables note CMOS for
// opcodes common to both, CMOS and 65816 as well.
w65816 = 1;
fmode &= ~FM_CPU2;
// breaks existing tests (compare with pre-assembled files)
//fmode |= FM_CPU; // 65816 bit
//fmode |= FM_CPU2_65816E;// 6502 in 65816 emu, to manage opcode compatibility in ldo65
break;
case 'B':
showblk = 1;
break;
case 'I':
if (argv[i][2] == 0) {
if (i + 1 < argc)
reg_include(argv[++i]);
else {
fprintf(stderr, "-I requires an argument\n");
exit(1);
}
} else {
reg_include(argv[i] + 2);
}
break;
case 'P':
if (argv[i][2] == 0) {
printfile = argv[++i];
} else {
printfile = argv[i] + 2;
}
break;
case 'F':
if (argv[i][2] == 0) {
listformat = argv[++i];
} else {
listformat = argv[i] + 2;
}
break;
case 'o':
if (argv[i][2] == 0) {
if (i + 1 < argc)
ofile = argv[++i];
else {
fprintf(stderr, "-o requires an argument\n");
exit(1);
}
} else {
ofile = argv[i] + 2;
}
break;
case 'l':
if (argv[i][2] == 0) {
if (i + 1 < argc)
lfile = argv[++i];
else {
fprintf(stderr, "-l requires an argument\n");
exit(1);
}
} else {
lfile = argv[i] + 2;
}
break;
case 'e':
if (argv[i][2] == 0) {
if (i + 1 < argc)
efile = argv[++i];
else {
fprintf(stderr, "-e requires an argument\n");
exit(1);
}
} else {
efile = argv[i] + 2;
}
break;
case 'b': /* set segment base addresses */
switch (argv[i][2]) {
case 't':
if (argv[i][3] == 0)
tbase = atoi(argv[++i]);
else
tbase = atoi(argv[i] + 3);
break;
case 'd':
if (argv[i][3] == 0)
dbase = atoi(argv[++i]);
else
dbase = atoi(argv[i] + 3);
break;
case 'b':
if (argv[i][3] == 0)
bbase = atoi(argv[++i]);
else
bbase = atoi(argv[i] + 3);
break;
case 'z':
if (argv[i][3] == 0)
zbase = atoi(argv[++i]);
else
zbase = atoi(argv[i] + 3);
break;
default:
fprintf(stderr, "unknown segment type '%c' - ignoring!\n",
argv[i][2]);
break;
}
break;
case 0:
fprintf(stderr,
"Single dash '-' on command line - ignoring!\n");
break;
default:
fprintf(stderr, "Unknown option '%c' - ignoring!\n",
argv[i][1]);
break;
}
} else { /* no option -> filename */
ifiles[nifiles++] = argv[i];
if (nifiles >= mifiles) {
mifiles += 5;
ifiles = realloc(ifiles, mifiles * sizeof(char*));
if (!ifiles) {
fprintf(stderr,
"Oops: couldn't alloc enough mem for filelist table..!\n");
exit(1);
}
}
}
i++;
}
if (!nifiles) {
fprintf(stderr, "No input files given!\n");
exit(0);
}
if (verbose)
fprintf(stderr, "%s\n", copyright);
if (printfile != NULL && !strcmp(printfile, "-")) {
printfile = NULL;
fplist = stdout;
} else {
fplist = printfile ? xfopen(printfile, "w") : NULL;
}
fplab = lfile ? xfopen(lfile, "w") : NULL;
fperr = efile ? xfopen(efile, "w") : NULL;
if (!strcmp(ofile, "-")) {
ofile = NULL;
fpout = stdout;
} else {
fpout = xfopen(ofile, "wb");
}
if (!fpout) {
fprintf(stderr, "Couldn't open output file!\n");
exit(1);
}
if (verbose)
fprintf(stderr, "%s\n", copyright);
if (1 /*!m_init()*/) {
if (1 /*!b_init()*/) {
if (1 /*!l_init()*/) {
/*if(!pp_init())*/
{
if (!x_init()) {
/* if(fperr) fprintf(fperr,"%s\n",copyright); */
if (verbose)
logout(ctime(&tim1));
list_setfile(fplist);
/* Pass 1 */
pc[SEG_ABS] = 0; /* abs addressing */
seg_start(fmode, tbase, dbase, bbase, zbase, 0,
relmode);
if (relmode) {
r_mode(RMODE_RELOC);
segment = SEG_TEXT;
} else {
/* prime old_segment in r_mode with SEG_TEXT */
segment = SEG_ABS;
r_mode(RMODE_ABS);
}
nolink = no_link;
for (i = 0; i < nifiles; i++) {
ifile = ifiles[i];
sprintf(out, "xAss65: Pass 1: %s\n", ifile);
if (verbose)
logout(out);
er = pp_open(ifile);
puttmpw(0);
puttmp(T_FILE);
puttmp(0);
puttmp(0);
puttmps((signed char*) &ifile,
sizeof(filep->fname));
if (!er) {
er = pass1();
pp_close();
} else {
sprintf(out,
"Couldn't open source file '%s'!\n",
ifile);
logout(out);
}
}
if ((er = b_depth())) {
sprintf(out,
"Still %d blocks open at end of file!\n",
er);
logout(out);
}
if (tbase & (align - 1)) {
sprintf(out,
"Warning: text segment ($%04x) start address doesn't align to %d!\n",
tbase, align);
logout(out);
}
if (dbase & (align - 1)) {
sprintf(out,
"Warning: data segment ($%04x) start address doesn't align to %d!\n",
dbase, align);
logout(out);
}
if (bbase & (align - 1)) {
sprintf(out,
"Warning: bss segment ($%04x) start address doesn't align to %d!\n",
bbase, align);
logout(out);
}
if (n65816 > 0)
fmode |= 0x8000;
switch (align) {
case 1:
break;
case 2:
fmode |= 1;
break;
case 4:
fmode |= 2;
break;
case 256:
fmode |= 3;
break;
}
if ((!er) && relmode)
h_write(fpout, fmode, tlen, dlen, blen, zlen, 0);
if (!er) {
if (verbose)
logout("xAss65: Pass 2:\n");
list_start(listformat);
seg_pass2();
if (relmode) {
r_mode(RMODE_RELOC);
segment = SEG_TEXT;
} else {
/* prime old_segment in r_mode with SEG_TEXT */
segment = SEG_ABS;
r_mode(RMODE_ABS);
}
er = pass2();
list_end();
}
if (fplab)
printllist(fplab);
tim2 = time(NULL);
if (verbose)
printstat();
if ((!er) && relmode)
seg_end(fpout); /* write reloc/label info */
if (fplist && fplist != stdout)
fclose(fplist);
if (fperr)
fclose(fperr);
if (fplab)
fclose(fplab);
if (fpout && fpout != stdout)
fclose(fpout);
} else {
logout("fatal: x: no memory!\n");
}
pp_end();
/* } else {
logout("fatal: pp: no memory!");*/
}
} else {
logout("fatal: l: no memory!\n");
}
} else {
logout("fatal: b: no memory!\n");
}
/*m_exit();*/
} else {
logout("Not enough memory available!\n");
}
if (ner || er) {
if (ner_max > 0) {
fprintf(stderr, "Break after %d error%c\n", ner, ner ? 's' : 0);
} else {
/* ner_max==0, i.e. show all errors */
fprintf(stderr, "End after %d error%c\n", ner, ner ? 's' : 0);
}
/*unlink();*/
if (ofile) {
unlink(ofile);
}
}
free(ifiles);
return ((er || ner) ? 1 : 0);
}
static void printstat(void) {
logout("Statistics:\n");
sprintf(out, " %8d of %8d label used\n", ga_lab(), gm_lab());
logout(out);
sprintf(out, " %8ld of %8ld byte label-memory used\n", ga_labm(),
gm_labm());
logout(out);
sprintf(out, " %8d of %8d PP-defs used\n", ga_pp(), gm_pp());
logout(out);
sprintf(out, " %8ld of %8ld byte PP-memory used\n", ga_ppm(), gm_ppm());
logout(out);
sprintf(out, " %8ld of %8ld byte buffer memory used\n", ga_p1(), gm_p1());
logout(out);
sprintf(out, " %8d blocks used\n", ga_blk());
logout(out);
sprintf(out, " %8ld seconds used\n", (long) difftime(tim2, tim1));
logout(out);
}
int h_length(void) {
return 26 + o_length();
}
static int setfext(char *s, char *ext) {
int j, i = (int) strlen(s);
if (i > MAXLINE - 5)
return (-1);
for (j = i - 1; j >= 0; j--) {
if (s[j] == DIRCHAR) {
strcpy(s + i, ext);
break;
}
if (s[j] == '.') {
strcpy(s + j, ext);
break;
}
}
if (!j)
strcpy(s + i, ext);
return (0);
}
static long ga_p1(void) {
return (afile->mn.tmpz);
}
static long gm_p1(void) {
return (TMPMEM);
}
static int pass2(void) {
int c, er, l, ll, i, al;
Datei datei;
signed char *dataseg = NULL;
signed char *datap = NULL;
memode = 0;
xmode = 0;
if ((dataseg = malloc(dlen))) {
if (!dataseg) {
fprintf(stderr, "Couldn't alloc dataseg memory...\n");
exit(1);
}
datap = dataseg;
}
filep = &datei;
afile->mn.tmpe = 0L;
while ((ner_max == 0 || ner < ner_max) && afile->mn.tmpe < afile->mn.tmpz) {
// get the length of the entry (now two byte - need to handle the sign)
l = 255 & afile->mn.tmp[afile->mn.tmpe++];
l |= afile->mn.tmp[afile->mn.tmpe++] << 8;
ll = l;
//printf("%p: l=%d first=%02x\n", afile->mn.tmp+afile->mn.tmpe-1, l, 0xff & afile->mn.tmp[afile->mn.tmpe]);
if (!l) {
if (afile->mn.tmp[afile->mn.tmpe] == T_LINE) {
datei.fline = (afile->mn.tmp[afile->mn.tmpe + 1] & 255)
+ (afile->mn.tmp[afile->mn.tmpe + 2] << 8);
afile->mn.tmpe += 3;
list_line(datei.fline); /* set line number of next listing output */
} else if (afile->mn.tmp[afile->mn.tmpe] == T_FILE) {
// copy the current line number from the current file descriptor
datei.fline = (afile->mn.tmp[afile->mn.tmpe + 1] & 255)
+ (afile->mn.tmp[afile->mn.tmpe + 2] << 8);
// copy the pointer to the file name in the current file descriptor
// Note: the filename in the current file descriptor is separately malloc'd and
// thus save to store the pointer
memcpy(&datei.fname, afile->mn.tmp + afile->mn.tmpe + 3,
sizeof(datei.fname));
afile->mn.tmpe += 3 + sizeof(datei.fname);
list_filename(datei.fname); /* set file name of next listing output */
}
} else {
/* do not attempt address mode optimization on pass 2 */
/* t_p2_l() includes the listing call to do_listing() */
er = t_p2_l(afile->mn.tmp + afile->mn.tmpe, &ll, &al);
if (er == E_NOLINE) {
} else if (er == E_OK) {
if (segment < SEG_DATA) {
for (i = 0; i < ll; i++)
chrput(afile->mn.tmp[afile->mn.tmpe + i]);
} else if (segment == SEG_DATA && datap) {
memcpy(datap, afile->mn.tmp + afile->mn.tmpe, ll);
datap += ll;
}
} else if (er == E_DSB) {
c = afile->mn.tmp[afile->mn.tmpe];
if (segment < SEG_DATA) {
/*printf("E_DSB, ll=%d, l=%d, c=%c\n",ll,l,afile->mn.tmp[afile->mn.tmpe]);*/
for (i = 0; i < ll; i++)
chrput(c);
} else if (segment == SEG_DATA && datap) {
memset(datap, c, ll);
datap += ll;
}
} else if (er == E_BIN) {
int i;
int j;
int flen;
int offset;
int fstart;
FILE *foo;
char binfnam[256];
i = afile->mn.tmpe;
/*
fprintf(stderr, "ok, ready to insert\n");
for (i=0; i<ll; i++) {
fprintf(stderr, "%i: %02x\n", i, afile->mn.tmp[afile->mn.tmpe+i]);
}
*/
offset = afile->mn.tmp[i] + (afile->mn.tmp[i + 1] << 8)
+ (afile->mn.tmp[i + 2] << 16);
fstart = afile->mn.tmp[i + 3] + 1 + (afile->mn.tmp[i + 4] << 8);
/* usually redundant but here for single-char names
that get interpreted as chars */
flen = afile->mn.tmp[i + 5];
if (flen > 1)
fstart++;
/* now fstart points either to string past quote and
length mark, OR, single char byte */
/*
fprintf(stderr, "offset = %i length = %i fstart = %i flen = %i charo = %c\n",
offset, ll, fstart, flen, afile->mn.tmp[afile->mn.tmpe+fstart]);
*/
/* there is a race condition here where altering the
file between validation in t_p2 (xat.c) and
here will cause problems. I'm not going to
worry about this right now. */
for (j = 0; j < flen; j++) {
binfnam[j] = afile->mn.tmp[i + fstart + j];
}
binfnam[flen] = '\0';
/*
fprintf(stderr, "fnam = %s\n", binfnam);
*/
/* primitive insurance */
if (!(foo = fopen(binfnam, "rb"))) {
errout(E_FNF);
ner++;
} else {
fseek(foo, offset, SEEK_SET);
for (j = 0; j < ll; j++) {
/* damn you Andre ;-) */
i = fgetc(foo);
if (segment < SEG_DATA) {
chrput(i);
}
if (segment == SEG_DATA && datap) {
memset(datap++, i, 1);
}
}
fclose(foo);
}
} else {
errout(er);
}
}
afile->mn.tmpe += abs(l);
}
if (relmode) {
if ((ll = fwrite(dataseg, 1, dlen, fpout)) < dlen) {
fprintf(stderr, "Problems writing %d bytes, return gives %d\n",
dlen, ll);
}
}
return (ner);
}
static int pass1(void) {
signed char o[2 * MAXLINE]; /* doubled for token listing */
int l, er, al;
memode = 0;
xmode = 0;
tlen = 0;
ner = 0;
/*FIXIT*/
while (!(er = xa_getline(s))) {
er = t_p1((signed char*) s, o, &l, &al);
switch (segment) {
case SEG_ABS:
case SEG_TEXT:
tlen += al;
break;
case SEG_DATA:
dlen += al;
break;
case SEG_BSS:
blen += al;
break;
case SEG_ZERO:
zlen += al;
break;
}
//printf(": er= %d, l=%d\n",er,l);
if (l) {
if (er) {
if (er == E_OKDEF) {
if (!(er = puttmpw(l)))
er = puttmps(o, l);
} else if (er == E_NOLINE)
er = E_OK;
} else {
if (!(er = puttmpw(-l)))
er = puttmps(o, l);
}
}
if (er) {
lineout();
errout(er);
}
/* printf("tmpz =%d\n",afile->mn.tmpz);
*/
}
if (er != E_EOF) {
errout(er);
}
/* { int i; printf("Pass 1 \n");
for(i=0;i<afile->mn.tmpz;i++)
fprintf(stderr, " %02x",255 & afile->mn.tmp[i]);
getchar();}
*/
return (ner);
}
static void usage(int default816, FILE *fp) {
fprintf(fp, "Usage: %s [options] file\n"
"Cross-assembler for 65xx/R65C02/65816\n"
"\n",
programname);
fprintf(fp, " -v verbose output\n"
" -E do not break after 20 errors, but show all\n"
" -C no CMOS-opcodes\n"
" -W no 65816-opcodes%s\n"
" -w allow 65816-opcodes%s\n",
default816 ? "" : " (default)", default816 ? " (default)" : "");
fprintf(fp,
" -B show lines with block open/close\n"
" -c produce `o65' object instead of executable files (i.e. don't link)\n"
" -o filename sets output filename, default is `a.o65'\n"
" A filename of `-' sets stdout as output file\n");
fprintf(fp,
" -e filename sets errorlog filename, default is none\n"
" -l filename sets labellist filename, default is none\n"
" -P filename sets filename for listing, default is none, '-' is stdout\n"
" -F format sets format for listing, default is plain, 'html' is current only other\n"
" supported format\n"
" -r adds crossreference list to labellist (if `-l' given)\n"
" -M allow ``:'' to appear in comments for MASM compatibility\n"
" (deprecated: prefer -XMASM)\n"
" -Xcompatset set compatibility flags for other assemblers, known values are:\n"
" C, MASM, CA65, XA23 (deprecated: for better 2.3 compatibility)\n"
" -R start assembler in relocating mode\n"
" -U allow all undefined labels in relocating mode\n");
fprintf(fp,
" -Llabel defines `label' as absolute, undefined label even when linking\n"
" -p<c> replace preprocessor char '#' with custom, e.g. '-p!' replaces it with '!'\n"
" -b? addr set segment base address to integer value addr\n"
" `?' stands for t(ext), d(ata), b(ss) and z(ero) segment\n"
" (address can be given more than once, last one is used)\n");
fprintf(fp,
" -A addr make text segment start at an address that when the _file_\n"
" starts at addr, relocation is not necessary. Overrides -bt\n"
" Other segments must be specified with `-b?'\n"
" -G suppress list of exported globals\n");
fprintf(fp,
" -p? set preprocessor character to ?, default is #\n"
" -DDEF=TEXT defines a preprocessor replacement\n"
" -Ocharset set output charset (PETSCII, ASCII, etc.), case-sensitive\n"
" -Idir add directory `dir' to include path (before XAINPUT)\n"
" --version output version information and exit\n"
" --help display this help and exit\n");
}
static char *ertxt[] = { "Syntax", // E_SYNTAX =-1
"Label already defined", // E_LABDEF =-2
"Label not defined", // E_NODEF =-3
"Label table full", // E_LABFULL =-4
"Label expected", // E_LABEXP =-5
"Out of memory", // E_NOMEM =-6
"Illegal opcode", // E_ILLCODE =-7
"Wrong addressing mode", // E_ADRESS =-8
"Branch out of range", // E_RANGE =-9
"Overflow", // E_OVERFLOW =-10
"Division by zero", // E_DIV =-11
"Pseudo-opcode expected", // E_PSOEXP =-12
"Block stack overflow", // E_BLKOVR =-13
"File not found", // E_FNF =-14
"End of file", // E_EOF =-15
"Unmatched block close", // E_BLOCK =-16
"NoBlk", // E_NOBLK =-17
"NoKey", // E_NOKEY =-18
"NoLine", // E_NOLINE =-19
"OKDef", // E_OKDEF =-20
"DSB", // E_DSB =-21
"NewLine", // E_NEWLINE =-22
"NewFile", // E_NEWFILE =-23
"CMOS instruction used with -C", // E_DMOS =-24
"pp:Wrong parameter count", // E_ANZPAR =-25
"Illegal pointer arithmetic (-26)", // E_ILLPOINTER =-26
"Illegal segment", // E_ILLSEGMENT =-27
"File header option too long", // E_OPTLEN =-28
"File option not at file start (when ROM-able)", // E_ROMOPT =-29
"Illegal align value", // E_ILLALIGN =-30
"65816 mode used/required", // E_65816 =-31
"Exceeded recursion limit for label evaluation", // E_ORECMAC =-32
"Unresolved preprocessor directive at end of file", // E_OPENPP =-33
"Data underflow", // E_OUTOFDATA =-34
"Illegal quantity", // E_ILLQUANT =-35
".bin", // E_BIN =-36
"#error directive", // E_UERROR =-37
"Assertion", // E_AERROR =-38
"DSB has negative length", // E_NEGDSBLEN =-39
/* placeholders for future fatal errors */
"", // -40
"", // -41
"", // -42
"", // -43
"", // -44
"", // -45
"", // -46
"", // -47
"", // -48
"", // -49
"", // -50
"", // -51
"", // -52
"", // -53
"", // -54
"", // -55
"", // -56
"", // -57
"", // -58
"", // -59
"", // -60
"", // -61
"", // -62
"", // -63
"", // -64 (was missing...)
/* warnings */
"Cutting word relocation in byte value", // W_ADRRELOC =-65
"Byte relocation in word value", // W_BYTERELOC =-66
"Illegal pointer arithmetic (-66)", // E_WPOINTER =-67
"Address access to low or high byte pointer", // W_ADDRACC =-68
"High byte access to low byte pointer", // W_HIGHACC =-69
"Low byte access to high byte pointer", // W_LOWACC =-70
"Can't optimize forward-defined label; using absolute addressing", // W_FORLAB =-71
"Open preprocessor directive at end of file (intentional?)", // W_OPENPP =-72
"Included binary data exceeds 64KB", // W_OVER64K =-73
"Included binary data exceeds 16MB", // W_OVER16M =-74
"Subtracting pointer from constant not supported in -R mode", // W_SUBTRACT =-75
/* more placeholders */
"", // -76
"", // -77
};
static int gl;
static int gf;
static int x_init(void) {
return 0;
#if 0
int er=0;
/*er=m_alloc(TMPMEM,&tmp);*/
afile->mn.tmp=malloc(TMPMEM);
if(!afile->mn.tmp) er=E_NOMEM;
afile->mn.tmpz=0L;
return(er);
#endif
}
static int puttmp(int c) {
int er = E_NOMEM;
//printf("puttmp: %02x -> %p \n",0xff & c, afile->mn.tmp+afile->mn.tmpz);
if (afile->mn.tmpz < TMPMEM) {
afile->mn.tmp[afile->mn.tmpz++] = c;
er = E_OK;
}
return (er);
}
static int puttmpw(int c) {
int er = E_NOMEM;
//printf("puttmp: %02x -> %p \n",0xff & c, afile->mn.tmp+afile->mn.tmpz);
if (afile->mn.tmpz < TMPMEM - 1) {
afile->mn.tmp[afile->mn.tmpz++] = c & 0xff;
afile->mn.tmp[afile->mn.tmpz++] = (c >> 8) & 0xff;
er = E_OK;
}
return (er);
}
static int puttmps(signed char *s, int l) {
int i = 0, er = E_NOMEM;
// printf("puttmps %d bytes from %p to %p:", l, s, afile->mn.tmp+afile->mn.tmpz);
if (afile->mn.tmpz + l < TMPMEM) {
while (i < l) {
//printf(" %02x", 0xff & s[i]);
afile->mn.tmp[afile->mn.tmpz++] = s[i++];
}
er = E_OK;
}
// printf("\n");
return (er);
}
static char l[MAXLINE];
static int xa_getline(char *s) {
static int ec;
static int i, c;
int hkfl, j, comcom;
j = hkfl = comcom = 0;
ec = E_OK;
if (!gl) {
do {
ec = pgetline(l);
i = 0;
while (l[i] == ' ')
i++;
while (l[i] != '\0' && isdigit(l[i]))
i++;
gf = 1;
if (ec == E_NEWLINE) {
puttmpw(0);
puttmp(T_LINE);
puttmpw(filep->fline);
ec = E_OK;
} else if (ec == E_NEWFILE) {
puttmpw(0);
puttmp(T_FILE);
puttmpw(filep->fline);
puttmps((signed char*) &(filep->fname), sizeof(filep->fname));
ec = E_OK;
}
} while (!ec && l[i] == '\0');
}
gl = 0;
if (!ec || ec == E_EOF) {
int startofline = 1;
do {
c = s[j] = l[i++];
if (!(hkfl & 2) && c == '\"')
hkfl ^= 1;
if (!comcom && !(hkfl & 1) && c == '\'')
hkfl ^= 2;
if (c == ';' && !hkfl) {
comcom = 1;
}
if (c == '\0') {
// end of line
break; /* hkfl = comcom = 0 */
}
if (c == ':' && !hkfl) {
/* if the next char is a "=" - so that we have a ":=" - and we
we have ca65 compatibility, we ignore the colon */
// also check for ":+" and ":-"
if (((!startofline) && l[i] != '=' && l[i] != '+' && l[i] != '-')
|| !ca65 || comcom) {
/* but otherwise we check if it is in a comment and we have
MASM or CA65 compatibility, then we ignore the colon as well */
if (!comcom || !(masm || ca65)) {
/* we found a colon, so we keep the current line in memory
but return the part before the colon, and next time the part
after the colon, so we can parse C64 BASIC text assembler... */
gl = 1;
break;
}
}
}
if (!isspace(c)) {
startofline = 0;
}
j++;
} while (c != '\0' && j < MAXLINE - 1 && i < MAXLINE - 1);
s[j] = '\0';
} else
s[0] = '\0';
#if 0
printf("got line: %s\n", s);
#endif
return (ec);
}
void set_align(int a) {
align = (a > align) ? a : align;
}
static void lineout(void) {
if (gf) {
logout(filep->flinep);
logout("\n");
gf = 0;
}
}
void errout(int er) {
if (er <= -ANZERR || er > -1) {
if (er >= -(ANZERR + ANZWARN) && er <= -ANZERR) {
sprintf(out, "%s:line %d: %04x: Warning - %s\n", filep->fname,
filep->fline, pc[segment], ertxt[(-er) - 1]);
} else {
/* sprintf(out,"%s:Zeile %d: %04x:Unbekannter Fehler Nr.: %d\n",*/
sprintf(out, "%s:line %d: %04x: Unknown error # %d\n", filep->fname,
filep->fline, pc[segment], er);
ner++;
}
} else {
if (er == E_NODEF)
sprintf(out, "%s:line %d: %04x:Label '%s' not defined\n",
filep->fname, filep->fline, pc[segment], lz);
else
sprintf(out, "%s:line %d: %04x:%s error\n", filep->fname,
filep->fline, pc[segment], ertxt[(-er) - 1]);
ner++;
}
logout(out);
}
static void chrput(int c) {
/* printf(" %02x",c&255);*/
putc(c & 0x00ff, fpout);
}
void logout(char *s) {
fprintf(stderr, "%s", s);
if (fperr)
fprintf(fperr, "%s", s);
}
/*****************************************************************/
typedef struct {
char *name;
int *flag;
} compat_set;
static compat_set compat_sets[] = { { "MASM", &masm }, { "CA65", &ca65 }, { "C",
&ctypes }, { "XA23", &xa23 }, { NULL, NULL } };
int set_compat(char *compat_name) {
int i = 0;
while (compat_sets[i].name != NULL) {
if (strcmp(compat_sets[i].name, compat_name) == 0) {
/* set appropriate compatibility flag */
(*compat_sets[i].flag) = 1;
/* warn on old versions of xa */
if (xa23)
fprintf(stderr, "Warning: -XXA23 is explicitly deprecated\n");
return 0;
}
i++;
}
return -1;
}