diff --git a/README b/README index 426f5ca..dbb013d 100644 --- a/README +++ b/README @@ -13,3 +13,8 @@ These are the directories: xa - contains the main source. The master branch is the stable version (currently 2.3.5) +These are the current branches: + +master - stable branch, currently 2.3.5 +listing - experimental branch that adds ca65 compatibility features and assembler listing + diff --git a/xa/ChangeLog b/xa/ChangeLog index 7314462..58e2ccf 100644 --- a/xa/ChangeLog +++ b/xa/ChangeLog @@ -304,3 +304,24 @@ xa-2.3.6 * Fixed message offsets. -- Cameron Kaiser 11 July 2014 + +xa-2.x.x + + * Add -E commandline option to not stop after 20 errors, but show all + of them + * Introduce -X compatibility set commandline option, to distinguish + between MASM and CA65 compatibility options + * Implement CA65 "cheap local labels", ":=" label definitions, + and various pseudo opcodes (.include, .import, .importzp, + .zeropage, .proc (anonymous only), .endproc, .code, .org, .reloc) + * some bugfixes around error/warning logs + * some more comments + * Stuff is mostly untested... + + -- André Fachat 25 June, 2011 + +xa-2.x.x + + * add test suite code + + -- André Fachat 20 August, 2014 diff --git a/xa/TODO b/xa/TODO index 0944889..97efd47 100644 --- a/xa/TODO +++ b/xa/TODO @@ -8,3 +8,13 @@ o inc a -> ina, dec a -> dea (right now uses bare inc and dec) o VICE label file support o Smarter -X that can cope with non-block-aligned segment sizes + +o The listing feature is not bug-free yet: + - ca65 and other assembler compatibility pseudo-opcodes will be + listed as xa specific ones, not the original in the source + E.g. a CA65 ".scope" will appear as ".(" in the listing + - The assembler has no constant handling, so constants are + resolved at parse time. Thus they appear as their value + in the listing. Only situation now is ".listbytes unlimited", + which will show as ".listbytes 0" in the listing + diff --git a/xa/man/reloc65.1 b/xa/man/reloc65.1 index ba891a9..2229092 100644 --- a/xa/man/reloc65.1 +++ b/xa/man/reloc65.1 @@ -40,8 +40,9 @@ respectively. Not valid for bss or zero. .B \-X Extract text and data segment together from the file instead of writing back the whole -file. Relocating data segment to the end of the text segment -(ignoring the \-xd option) before extracting. +file. Relocating data segment to the end of the text segment and +bss segment to the end of the data segment +(\-xd and \-xb options override the derived address) before extracting. .TP .B \-\-help Show summary of options. diff --git a/xa/man/xa.1 b/xa/man/xa.1 index 8dc5e88..5a2a480 100644 --- a/xa/man/xa.1 +++ b/xa/man/xa.1 @@ -19,6 +19,9 @@ further in this manual page. .SH OPTIONS .TP +.B \-E +Do not stop after 20 errors, but show all errors. +.TP .B \-v Verbose output. .TP @@ -54,6 +57,15 @@ use the special filename .BR \- to output to standard output. .TP +.B \-P filename +Set listing filename, default is none. use the special filename +.BR \- +to print the listing to standard output. +.TP +.B \-F html +Set listing format, default is plain. The only other currently supported format is +.BR html +.TP .B \-e filename Set errorlog filename, default is none. .TP @@ -67,12 +79,27 @@ to reconstruct source. Add cross-reference list to labellist (requires .BR \-l ). .TP +.B \-Xcompatset +Enables compatibility settings to become more (not fully!) compatible with other 6502 assemblers. +Currently supported are compatibility set "MASM" and "CA65". +"MASM" allows colons to appear in comments. This does not affect colon interpretation elsewhere. +"CA65" allows the ":=" label definition (instead of "="), It adds the "unnamed labels" and also the +"cheap local labels" using +the "@" character. This, however, disables the "@" 24-bit enforcement. +"C" enables the usage of "0xHEX" and "0OCTAL" C-style number encodings. Note that you can combine +compatibility sets e.g. with "-XCA65 -XC". +.TP .B \-M Allow colons to appear in comments; for MASM compatibility. This does -not affect colon interpretation elsewhere. +not affect colon interpretation elsewhere. This option is deprecated. Use +.BR -X +instead. .TP .B \-R -Start assembler in relocating mode. +Start assembler in relocating mode, i.e. use segments. +.TP +.B \-U +Do not allow undefined labels in relocating mode. .TP .B \-Llabel Defines @@ -339,7 +366,7 @@ for those opcodes that support it (i.e., keep as 16 bit word) .B @ render as 24-bit quantity for 65816 (must specify .B \-w -command-line option). +command-line option, must not specify the CA65 compatibility). .B This is required to specify any .B 24-bit quantity! .TP @@ -441,7 +468,7 @@ and are synonymous, so you can mix things such as .B .byt $43, 22, """a character string""" and get the expected result. The string is subject to the current character -set, but the remaining bytes are inserted wtihout modification. +set, but the remaining bytes are inserted without modification. .TP .B .aasc """text1""","text2",... Specifies a character string that is @@ -491,10 +518,21 @@ a block, precede the label with or precede it with .B & to declare it within the previous level only (or globally if you are only -one level deep). Sixteen levels of scoping are permitted. +one level deep). Sixteen levels of scoping are permitted. +.B \&.) +is equivalent to +.B .block +or +.B .proc +. The latter is similar to the ca65 pseudo-opcode, but only anonymous blocks +are supported. .TP .B \&.) -Closes a block. +Closes a block. This is equivalent to +.B .bend +or +.B .endproc +. .TP .B \.as \.al \.xs \.xl Only relevant in 65816 mode (with the @@ -521,6 +559,11 @@ and generate errors if .B \-w is not specified. +.TP +.B .include "filename" +Includes another file in place of the pseudo opcode. Same as the preprocessor +.B #include + .LP The following pseudo-ops apply primarily to relocatable .o65 objects. A full discussion of the relocatable format is beyond the @@ -538,6 +581,11 @@ space for allocation and .zero being uninitialized zero page space for allocation. In .bss and .zero, only labels are evaluated. These pseudo-ops are valid in relative and absolute modes. .TP +.B .code +For CA65 compatibility this is currently mapped to ".text". +.B .zeropage +For CA65 compatibility this is currently mapped to ".zero". +.TP .B .align value Aligns the current segment to a byte boundary (2, 4 or 256) as specified by .B @@ -555,6 +603,17 @@ is used to specify the file option being referenced. A table of these options is in the relocatable o65 file format description. The remainder of the options are interpreted as values to insert. Any number of values may be specified, and may also be strings. +.TP +.B .import label1, label2, label3, ... +Defines the given labels as global labels which are imported and resolved during +the link stage. Similar to the +.B -L +command line parameter +.TP +.B .importzp label1, label2, label3, ... +Similar to +.B .import +only it imports zeropage labels (i.e. byte values) .SH PREPROCESSOR @@ -830,7 +889,7 @@ This manual page was written by David Weinehall , Andre Fachat and Cameron Kaiser . Original xa package (C)1989-1997 Andre Fachat. Additional changes -(C)1989-2009 Andre Fachat, Jolse Maginnis, David Weinehall, +(C)1989-2011 Andre Fachat, Jolse Maginnis, David Weinehall, Cameron Kaiser. The official maintainer is Cameron Kaiser. .SH WEBSITE diff --git a/xa/misc/reloc65.c b/xa/misc/reloc65.c index eeb64cf..9fff50d 100644 --- a/xa/misc/reloc65.c +++ b/xa/misc/reloc65.c @@ -74,6 +74,7 @@ void usage(FILE *fp) " -X extracts the file such that text and data\n" " segments are chained, i.e. possibly relocating\n" " the data segment to the end of the text segment\n" + " -v verbose output\n" " --version output version information and exit\n" " --help display this help and exit\n"); } @@ -86,6 +87,7 @@ int main(int argc, char *argv[]) { int tbase = 0, dbase = 0, bbase = 0, zbase = 0; char *outfile = "a.o65"; int extract = 0; + int verbose = 0; if (argc <= 1) { usage(stderr); @@ -106,6 +108,9 @@ int main(int argc, char *argv[]) { if(argv[i][0]=='-') { /* process options */ switch(argv[i][1]) { + case 'v': + verbose = 1; + break; case 'o': if(argv[i][2]) outfile=argv[i]+2; else outfile=argv[++i]; @@ -188,25 +193,53 @@ int main(int argc, char *argv[]) { file.tbase = file.buf[ 9]*256+file.buf[ 8]; file.tlen = file.buf[11]*256+file.buf[10]; - file.tdiff = tflag? tbase - file.tbase : 0; + file.tdiff = tflag ? tbase - file.tbase : 0; + file.dbase = file.buf[13]*256+file.buf[12]; file.dlen = file.buf[15]*256+file.buf[14]; + file.ddiff = dflag ? dbase - file.dbase : 0; if (extract == 3) { if (dflag) { - fprintf(stderr,"reloc65: %s: Warning: data segment address ignored for -X option\n", argv[i]); - } - dbase = file.tbase + file.tdiff + file.tlen; - file.ddiff = dbase - file.dbase; - } else { - file.ddiff = dflag? dbase - file.dbase : 0; + fprintf(stderr,"reloc65: %s: Warning: data segment address overrides -X option\n", argv[i]); + } else { + dbase = file.tbase + file.tdiff + file.tlen; + file.ddiff = dbase - file.dbase; + } } + file.bbase = file.buf[17]*256+file.buf[16]; file.blen = file.buf[19]*256+file.buf[18]; - file.bdiff = bflag? bbase - file.bbase : 0; - file.zbase = file.buf[21]*256+file.buf[20]; - file.zlen = file.buf[23]*256+file.buf[21]; - file.zdiff = zflag? zbase - file.zbase : 0; + file.bdiff = bflag ? bbase - file.bbase : 0; + if (extract == 3) { + if (bflag) { + fprintf(stderr,"reloc65: %s: Warning: bss segment address overrides -X option\n", argv[i]); + } else { + bbase = file.dbase + file.ddiff + file.dlen; + file.bdiff = bbase - file.bbase; + } + } + file.zbase = file.buf[21]*256+file.buf[20]; + file.zlen = file.buf[23]*256+file.buf[22]; + file.zdiff = zflag ? zbase - file.zbase : 0; + + if (verbose) { + printf("Relocating segments to:\n"); + printf("text segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", + file.tbase + file.tdiff, file.tbase + file.tdiff + file.tlen, + file.tlen, file.tlen, file.tdiff, file.tdiff & 0xffff); + printf("data segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", + file.dbase + file.ddiff, file.dbase + file.ddiff + file.dlen, + file.dlen, file.dlen, file.ddiff, file.ddiff & 0xffff); + printf("bss segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", + file.bbase + file.bdiff, file.bbase + file.bdiff + file.blen, + file.blen, file.blen, file.bdiff, file.bdiff & 0xffff); + printf("zero segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", + file.zbase + file.zdiff, file.zbase + file.zdiff + file.zlen, + file.zlen, file.zlen, file.zdiff, file.zdiff & 0xffff); + } + + /* pointer of position in file */ file.segt = file.buf + hlen; file.segd = file.segt + file.tlen; file.utab = file.segd + file.dlen; diff --git a/xa/src/.gitignore b/xa/src/.gitignore new file mode 100644 index 0000000..874c63c --- /dev/null +++ b/xa/src/.gitignore @@ -0,0 +1,2 @@ +*.o + diff --git a/xa/src/Makefile b/xa/src/Makefile index dc15ea6..8e32165 100644 --- a/xa/src/Makefile +++ b/xa/src/Makefile @@ -1,7 +1,8 @@ -OBJ = xa.o xaa.o xal.o xap.o xat.o xar.o xar2.o xao.o xau.o xam.o xacharset.o +OBJ = xa.o xaa.o xal.o xap.o xat.o xar.o xar2.o xao.o xau.o xam.o xacharset.o xalisting.o -#CFLAGS=-W -Wall -pedantic -ansi #-g +#CFLAGS=-W -Wall -pedantic -ansi -g #CFLAGS=-W -Wall -ansi -O2 +CFLAGS=-g -std=c99 #LD = ${CC} #LDFLAGS = "-lc" diff --git a/xa/src/xa.c b/xa/src/xa.c index f0e4d1e..7ec1cb0 100644 --- a/xa/src/xa.c +++ b/xa/src/xa.c @@ -45,6 +45,7 @@ #include "xar.h" #include "xat.h" #include "xacharset.h" +#include "xalisting.h" #include "version.h" @@ -55,26 +56,33 @@ #define ANZWARN 13 #define programname "xa" -#define progversion "v2.3.6" +#define progversion "v2.3.6+af" #define authors "Written by Andre Fachat, Jolse Maginnis, David Weinehall and Cameron Kaiser" #define copyright "Copyright (C) 1989-2014 Andre Fachat, Jolse Maginnis, David Weinehall\nand Cameron Kaiser." /* exported globals */ int ncmos, cmosfl, w65816, n65816; -int masm = 0; +/* compatibility flags */ +int masm = 0; /* MASM */ +int ca65 = 0; /* CA65 */ +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 noundef = 0; // overrides -R acceptance of undefined labels char altppchar; /* local variables */ + static char out[MAXLINE]; static time_t tim1, tim2; -static FILE *fpout, *fperr, *fplab; +static FILE *fpout, *fperr, *fplab, *fplist; static int ner = 0; +static int ner_max = 20; static int align = 1; @@ -85,12 +93,14 @@ 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; @@ -110,6 +120,8 @@ int main(int argc,char *argv[]) signed char *s=NULL; char *tmpp; + char *listformat = NULL; + int mifiles = 5; int nifiles = 0; int verbose = 0; @@ -117,9 +129,10 @@ int main(int argc,char *argv[]) int no_link = 0; char **ifiles; - char *ofile; - char *efile; - char *lfile; + 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]; @@ -173,6 +186,7 @@ int main(int argc,char *argv[]) ofile="a.o65"; efile=NULL; lfile=NULL; + printfile=NULL; if(pp_init()) { logout("fatal: pp: no memory!"); @@ -191,6 +205,9 @@ int main(int argc,char *argv[]) while(i 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); @@ -615,39 +681,41 @@ static int pass2(void) filep=&datei; afile->mn.tmpe=0L; - while(ner<20 && afile->mn.tmpemn.tmpz) + while((ner_max==0 || nermn.tmpemn.tmpz) { - l=afile->mn.tmp[afile->mn.tmpe++]; + // 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); -/* - datei.fname = malloc(strlen((char*) afile->mn.tmp+afile->mn.tmpe+3)+1); - if(!datei.fname) { - fprintf(stderr,"Oops, no more memory\n"); - exit(1); - } - strcpy(datei.fname,(char*) afile->mn.tmp+afile->mn.tmpe+3); - afile->mn.tmpe+=3+strlen(datei.fname); -*/ + + list_filename(datei.fname); /* set file name of next listing output */ } } else { /* do not attempt address mode optimization on pass 2 */ - er=t_p2(afile->mn.tmp+afile->mn.tmpe,&ll,1,&al); - + + /* 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 @@ -752,16 +820,14 @@ fprintf(stderr, "offset = %i length = %i fstart = %i flen = %i charo = %c\n", static int pass1(void) { - signed char o[MAXLINE]; - int l,er,temp_er,al; + signed char o[2*MAXLINE]; /* doubled for token listing */ + int l,er,al; memode=0; xmode=0; tlen=0; ner=0; - temp_er = 0; - /*FIXIT*/ while(!(er=xa_getline(s))) { @@ -774,7 +840,7 @@ static int pass1(void) case SEG_ZERO: zlen += al; break; } - /*printf(": er= %d, l=%d, tmpz=%d\n",er,l,tmpz); */ + //printf(": er= %d, l=%d\n",er,l); if(l) { @@ -782,14 +848,14 @@ static int pass1(void) { if(er==E_OKDEF) { - if(!(er=puttmp(l))) + if(!(er=puttmpw(l))) er=puttmps(o,l); } else if(er==E_NOLINE) er=E_OK; } else { - if(!(er=puttmp(-l))) + if(!(er=puttmpw(-l))) er=puttmps(o,l); } } @@ -827,6 +893,7 @@ static void usage(int default816, FILE *fp) programname); fprintf(fp, " -v verbose output\n" + " -E do not break after 20 errors, but show all\n" " -x old filename behaviour (overrides `-o', `-e', `-l')\n" " This is deprecated and may disappear in future versions!\n" " -C no CMOS-opcodes\n" @@ -842,11 +909,18 @@ static void usage(int default816, FILE *fp) 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" - " -R start assembler in relocating mode\n"); + " -Xcompatset set compatibility flags for other assemblers, known values are:\n" + " MASM, CA65\n" + " -R start assembler in relocating mode\n" + " -U do not allow undefined labels in relocating mode\n"); fprintf(fp, " -Llabel defines `label' as absolute, undefined label even when linking\n" + " -p 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"); @@ -900,7 +974,7 @@ static char *ertxt[] = { "NewFile", "CMOS instruction used with -C", "pp:Wrong parameter count", - "Illegal pointer arithmetic", + "Illegal pointer arithmetic (-26)", "Illegal segment", "File header option too long", "File option not at file start (when ROM-able)", @@ -939,11 +1013,10 @@ static char *ertxt[] = { "", "", "", - "", /* warnings */ "Cutting word relocation in byte value", "Byte relocation in word value", - "Illegal pointer arithmetic", + "Illegal pointer arithmetic (-66)", "Address access to low or high byte pointer", "High byte access to low byte pointer", "Low byte access to high byte pointer", @@ -977,7 +1050,9 @@ static int x_init(void) static int puttmp(int c) { int er=E_NOMEM; -/*printf("puttmp: afile=%p, tmp=%p, tmpz=%d\n",afile, afile?afile->mn.tmp:0, afile?afile->mn.tmpz:0);*/ + + //printf("puttmp: %02x -> %p \n",0xff & c, afile->mn.tmp+afile->mn.tmpz); + if(afile->mn.tmpzmn.tmp[afile->mn.tmpz++]=c; @@ -986,17 +1061,37 @@ static int puttmp(int c) 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.tmpzmn.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+lmn.tmp[afile->mn.tmpz++]=s[i++]; + while(imn.tmp[afile->mn.tmpz++]=s[i++]; + } er=E_OK; } + // printf("\n"); return(er); } @@ -1026,25 +1121,19 @@ static int xa_getline(char *s) if(ec==E_NEWLINE) { - puttmp(0); + puttmpw(0); puttmp(T_LINE); - puttmp((filep->fline)&255); - puttmp(((filep->fline)>>8)&255); - ec=E_OK; + puttmpw(filep->fline); + ec=E_OK; } else if(ec==E_NEWFILE) { - puttmp(0); + puttmpw(0); puttmp(T_FILE); - puttmp((filep->fline)&255); - puttmp(((filep->fline)>>8)&255); + puttmpw(filep->fline); puttmps((signed char*)&(filep->fname), sizeof(filep->fname)); -/* - puttmps((signed char*)filep->fname, - 1+(int)strlen(filep->fname)); -*/ ec=E_OK; } } while(!ec && l[i]=='\0'); @@ -1053,26 +1142,50 @@ static int xa_getline(char *s) gl=0; if(!ec || ec==E_EOF) { + int startofline = 1; do { c=s[j]=l[i++]; - if (c=='\"') + if (c=='\"') { hkfl^=1; - if (c==';' && !hkfl) + } + if (c==';' && !hkfl) { + // start of comment comcom = 1; - if (c=='\0') - break; /* hkfl = comcom = 0 */ - if (c==':' && !hkfl && (!comcom || !masm)) { - gl=1; - break; - } + } + 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-1) { - if(er>=-(ANZERR+ANZWARN) && er < -ANZERR) { + 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 { @@ -1129,3 +1242,31 @@ void logout(char *s) fprintf(fperr,"%s",s); } +/*****************************************************************/ + +typedef struct { + char *name; + int *flag; +} compat_set; + +static compat_set compat_sets[] = { + { "MASM", &masm }, + { "CA65", &ca65 }, + { "C", &ctypes }, + { 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; + return 0; + } + i++; + } + return -1; +} + + diff --git a/xa/src/xa.h b/xa/src/xa.h index 67ee3fe..40af87b 100644 --- a/xa/src/xa.h +++ b/xa/src/xa.h @@ -22,11 +22,12 @@ #include "xah.h" /* For SEG_MAX */ extern int ncmos, cmosfl, w65816, n65816; -extern int masm, nolink; +extern int masm, ca65, nolink,noundef; extern int noglob; extern int showblk; extern int relmode; extern int crossref; +extern int ctypes; extern char altppchar; extern int tlen, tbase; diff --git a/xa/src/xaa.c b/xa/src/xaa.c index 73dd861..e57f0f1 100644 --- a/xa/src/xaa.c +++ b/xa/src/xaa.c @@ -119,13 +119,15 @@ static int ag_term(signed char *s, int p, int *v, int *nafl, int *label) if(s[pp]==T_LABEL) { er=l_get(cval(s+pp+1),v, &afl); -/* printf("label: er=%d, seg=%d, afl=%d, nolink=%d, fundef=%d\n", - er, segment, afl, nolink, fundef); */ + //printf("label: er=%d, seg=%d, afl=%d, nolink=%d, fundef=%d\n", + // er, segment, afl, nolink, fundef); if(er==E_NODEF && segment != SEG_ABS && fundef ) { - if( nolink || (afl==SEG_UNDEF)) { + if( (nolink && !noundef) || ((afl==SEG_UNDEF) || (afl==SEG_UNDEFZP))) { er = E_OK; *v = 0; - afl = SEG_UNDEF; + if(afl!=SEG_UNDEFZP) { + afl = SEG_UNDEF; + } *label = cval(s+pp+1); } } @@ -135,7 +137,7 @@ static int ag_term(signed char *s, int p, int *v, int *nafl, int *label) if(s[pp]==T_VALUE) { *v=lval(s+pp+1); - pp+=4; + pp+=5; /* printf("value: v=%04x\n",*v); */ } else @@ -143,7 +145,7 @@ static int ag_term(signed char *s, int p, int *v, int *nafl, int *label) { afl = s[pp+1]; *v=cval(s+pp+2); - pp+=4; + pp+=6; /* printf("pointer: v=%04x, afl=%04x\n",*v,afl); */ } else @@ -159,7 +161,7 @@ static int ag_term(signed char *s, int p, int *v, int *nafl, int *label) *v *= mf; - while(!er && s[pp]!=')' && s[pp]!=']' && s[pp]!=',' && s[pp]!=T_END) + while(!er && s[pp]!=')' && s[pp]!=']' && s[pp]!=',' && s[pp]!=T_END && s[pp]!=T_COMMENT) { er=get_op(s,&o); @@ -169,7 +171,7 @@ static int ag_term(signed char *s, int p, int *v, int *nafl, int *label) if(!(er=ag_term(s,pr[o],&w, nafl, label))) { if(afl || *nafl) { /* check pointer arithmetic */ - if((afl == *nafl) && (afl!=SEG_UNDEF) && o==2) { + if((afl == *nafl) && (afl!=SEG_UNDEFZP) && (afl!=SEG_UNDEF) && o==2) { afl = 0; /* substract two pointers */ } else if(((afl && !*nafl) || (*nafl && !afl)) && o==1) { @@ -180,6 +182,8 @@ static int ag_term(signed char *s, int p, int *v, int *nafl, int *label) } else { if(segment!=SEG_ABS) { if(!dsb_len) { + /*printf("ILLPOINTER=dsb_len=%d,segment=%d\n",dsb_len, segment);*/ + /* e.g. adding two pointers, adding two undefined values */ er=E_ILLPOINTER; } } @@ -202,10 +206,18 @@ static int get_op(signed char *s, int *o) *o=s[pp]; - if(*o<1 || *o>17) + if(*o<1 || *o>17) { +/* + printf("*o=%d, pp=%d, s=%s\n", *o, pp, s); + for (int i=0; i< 10; i++) { + printf(" %02x", s[i]); + } + printf("\n"); +*/ er=E_SYNTAX; - else + } else { er=E_OK; + } return(er); } diff --git a/xa/src/xad.h b/xa/src/xad.h index 79528fc..2c2a64c 100644 --- a/xa/src/xad.h +++ b/xa/src/xad.h @@ -31,11 +31,12 @@ #define cval(s) 256 * ((s)[1] & 255) + ((s)[0]&255) #define lval(s) 65536 * ((s)[2] & 255) + 256 * ((s)[1] & 255) + ((s)[0] & 255) -#define wval(i, v) do { \ +#define wval(i, v, f) do { \ t[i++] = T_VALUE; \ t[i++] = v & 255; \ t[i++] = (v >> 8) & 255; \ t[i++] = (v >> 16) & 255; \ + t[i++] = f & 255; \ } while (0) #endif /* __XA65_XAD_H__ */ diff --git a/xa/src/xah.h b/xa/src/xah.h index 6744ef8..a9cd5ca 100644 --- a/xa/src/xah.h +++ b/xa/src/xah.h @@ -20,15 +20,29 @@ #ifndef __XA65_XAH_H__ #define __XA65_XAH_H__ -#define ANZLAB 5000 /* mal 14 -> Byte */ +/* + * Note: the computations to get the number of bytes necessary to allocate are + * a historic remnant of the Atari ST (the original platform) not being able to efficiently allocate small chunks + * of memory so I had to allocate a large chunk myself and manage the tables myself. + * This has changed and some parts of xa65 are modified to just do a malloc() now. + * These fixed numbers should actually go away. AF 20110623 + */ +#define ANZLAB 5000 /* multiplied by sizeof(Labtab) -> Byte */ #define LABMEM 40000L #define MAXLAB 32 #define MAXBLK 16 #define MAXFILE 7 #define MAXLINE 2048 #define MAXPP 40000L -#define ANZDEF 2340 /* mal 14 -> Byte, ANZDEF * 14 < 32768 */ -#define TMPMEM 200000L /* Zwischenspeicher von Pass1 nach Pass 2 */ +#define ANZDEF 2340 /* multiplied by sizeof(List) -> Byte, ANZDEF * 20 < 32768 */ +#define TMPMEM 2000000L /* temporary memory buffer from Pass1 to Pass 2 (includes all source, thus enlarged) */ + +typedef enum { + STD = 0, + CHEAP = 1, + UNNAMED = 2, + UNNAMED_DEF = 3 +} label_t; typedef struct LabOcc { struct LabOcc *next; @@ -36,17 +50,27 @@ typedef struct LabOcc { char *fname; } LabOcc; +/** + * struct that defines a label, after it has been parsed + */ typedef struct { int blk; int val; int len; int fl; /* 0 = label value not valid/known, * 1 = label value known + * 2 = label value not known, external global label (imported on link) */ int afl; /* 0 = no address (no relocation), 1 = address label */ int nextindex; + label_t is_cll; /* 0 is normal label, 1 is cheap local label (used for listing) */ char *n; struct LabOcc *occlist; + // within a block, make a linked list for the unnamed label counting + // use indexes, as the label table can be re-alloced (so pointers change) + // -1 is the "undef'd" end of list + int blknext; + int blkprev; } Labtab; typedef struct { @@ -71,20 +95,20 @@ typedef struct { #define BUFSIZE 4096 /* File-Puffegroesse (wg Festplatte) */ -#define E_OK 0 /* Fehlernummern */ -#define E_SYNTAX -1 /* Syntax Fehler */ -#define E_LABDEF -2 /* Label definiert */ -#define E_NODEF -3 /* Label nicht definiert */ -#define E_LABFULL -4 /* Labeltabelle voll */ -#define E_LABEXP -5 /* Label erwartet */ -#define E_NOMEM -6 /* kein Speicher mehr */ -#define E_ILLCODE -7 /* Illegaler Opcode */ -#define E_ADRESS -8 /* Illegale Adressierung */ +#define E_OK 0 /* No error */ +#define E_SYNTAX -1 /* Syntax error */ +#define E_LABDEF -2 /* Label already defined (duplicate label definition) */ +#define E_NODEF -3 /* Label not defined */ +#define E_LABFULL -4 /* Label table full */ +#define E_LABEXP -5 /* Label expected but not found */ +#define E_NOMEM -6 /* out of memory */ +#define E_ILLCODE -7 /* Illegal Opcode */ +#define E_ADRESS -8 /* Illegal Addressing mode */ #define E_RANGE -9 /* Branch out of range */ -#define E_OVERFLOW -10 /* Ueberlauf */ -#define E_DIV -11 /* Division durch Null */ -#define E_PSOEXP -12 /* Pseudo-Opcode erwartet */ -#define E_BLKOVR -13 /* Block-Stack Uebergelaufen */ +#define E_OVERFLOW -10 /* overflow */ +#define E_DIV -11 /* Division by zero */ +#define E_PSOEXP -12 /* Pseudo-Opcode expected but not found */ +#define E_BLKOVR -13 /* Block-Stack overflow */ #define E_FNF -14 /* File not found (pp) */ #define E_EOF -15 /* End of File */ #define E_BLOCK -16 /* Block inkonsistent */ @@ -126,18 +150,25 @@ typedef struct { #define W_OVER16M -74 /* included binary over 16M in 65816 mode */ /* warnings 75-77 are placeholders */ -#define T_VALUE -1 -#define T_LABEL -2 -#define T_OP -3 -#define T_END -4 -#define T_LINE -5 -#define T_FILE -6 -#define T_POINTER -7 +/* Meta-values for the token list. Note must not overlap with the + * K* definitions in xat.c, which have outgrown the positive numbers + * and are now growing up from -128 ... */ +#define T_VALUE -1 /* following is a 24 bit value in the token list */ +#define T_LABEL -2 /* referring to a label, following the token is the 16bit label number */ +#define T_OP -3 /* label oriented operation; following is the label number (16bit), plus the operation char (e.g. '+') */ +#define T_END -4 /* end of line marker */ +#define T_LINE -5 /* new line indicator; following is the 16 bit line number */ +#define T_FILE -6 /* new file indicator; following is the 16 bit line number, then the file name (zero-term) */ +#define T_POINTER -7 /* ??? */ +#define T_COMMENT -8 /* unused */ +#define T_DEFINE -9 /* define a label; inserted at conversion and discarded in pass1, only used in listing output */ +#define T_LISTING -10 /* meta token, inserted after conversion before pass1, used after pass2 to create listing */ +#define T_CAST -11 /* token inserted for a cast */ -#define P_START 0 /* Prioritaeten fuer Arithmetik */ -#define P_LOR 1 /* Von zwei Operationen wird immer */ -#define P_LAND 2 /* die mit der hoeheren Prioritaet */ -#define P_OR 3 /* zuerst ausgefuehrt */ +#define P_START 0 /* arithmetic operation priorities */ +#define P_LOR 1 /* of any two operations, the one with */ +#define P_LAND 2 /* the higher priority will be done first */ +#define P_OR 3 #define P_XOR 4 #define P_AND 5 #define P_EQU 6 @@ -168,6 +199,8 @@ typedef struct { #define SEG_ZERO 5 #define SEG_MAX 6 +#define SEG_UNDEFZP 7 /* is being mapped to UNDEF */ + typedef struct Fopt { signed char *text; /* text after pass1 */ int len; @@ -188,8 +221,11 @@ typedef struct File { int base[SEG_MAX]; int len[SEG_MAX]; struct { + // temporary memory between pass1 and pass2 signed char *tmp; + // write pointer unsigned long tmpz; + // read pointer unsigned long tmpe; } mn; struct { diff --git a/xa/src/xal.c b/xa/src/xal.c index 5598c75..b9451a2 100644 --- a/xa/src/xal.c +++ b/xa/src/xal.c @@ -48,7 +48,21 @@ static int b_fget(int*,int); static int b_ltest(int,int); static int b_get(int*); static int b_test(int); -static int ll_def(char *s, int *n, int b); +static int ll_def(char *s, int *n, int b, label_t ltype); +static int b_link(int); + +static int b_new(void); + +static void cll_init(); +static int cll_get(); +static void cll_clear(); +static int cll_getcur(); + +/* +static void unn_init(); +static int unn_get(); +static void unn_clear(); +*/ /* local variables */ @@ -69,6 +83,8 @@ static Labtab *ltp; int l_init(void) { + cll_init(); + //unn_init(); return 0; #if 0 int er; @@ -139,69 +155,168 @@ FILE *fp; } } +/********************************************************************************** + * cheap local labels + */ + +static int cll_current = 0; /* the current cheap local labels block */ + +/** + * init the cheap local labels + */ +void cll_init() { + cll_current = 0; +} + +/** + * get the block number for a new cheap local label block + */ +int cll_get() { + if (cll_current == 0) { + cll_current = b_new(); + } + return cll_current; +} + +/** + * clear the local labels + */ +void cll_clear() { + cll_current = 0; +} + +int cll_getcur() { + return cll_current; +} + +/**********************************************************************************/ + +/** + * define a global label (from the "-L" command line parameter) + */ int lg_set(char *s ) { int n, er; - er = ll_search(s,&n); + er = ll_search(s,&n, STD); if(er==E_OK) { fprintf(stderr,"Warning: global label doubly defined!\n"); } else { - if(!(er=ll_def(s,&n,0))) { - ltp=afile->la.lt+n; - ltp->fl=2; - ltp->afl=SEG_UNDEF; - } + if(!(er=ll_def(s,&n,0, STD))) { + return lg_import(n); + } } return er; } +/** + * define a global label (from the .import pseudo opcode)) + * "s" is a pointer to the first label character, end is at \0 + * or at non-alphanumeric/_ char + */ +int lg_import(int n) { + int er=E_OK; + + ltp=afile->la.lt+n; + ltp->fl=2; + ltp->afl=SEG_UNDEF; + + return er; +} + +/** + * define a global zeropage label (from the .importzp pseudo opcode)) + * "s" is a pointer to the first label character, end is at \0 + * or at non-alphanumeric/_ char + */ +int lg_importzp(int n) { + int er=E_OK; + + ltp=afile->la.lt+n; + ltp->fl=2; + ltp->afl=SEG_UNDEFZP; + + return er; +} + +/**********************************************************************************/ int l_def(char *s, int *l, int *x, int *f) { int n,er,b,i=0; + label_t cll_fl; - *f=0; - b=0; - n=0; + *f=0; /* flag (given as param) that the label is to be re-defined and the + "label defined error" is to be skipped */ + b=0; /* block level on block stack, resp. block number */ + n=0; /* flag, when set, b is absolute block number and not being translated */ + cll_fl=STD; /* when 0, clear the cheap local label block */ + if(s[0]==':') { + // ca65 unnamed label + i++; + //n++; /* block number b is absolute */ + //b=unn_get(); /* current (possibly newly allocated) unnamed label block */ + cll_fl = UNNAMED; // keep the cheap local label block + } else if(s[0]=='-') { - *f+=1; + *f+=1; /* label is being redefined */ i++; } else + if(s[0]=='@') + { + i++; + n++; /* block number b is absolute */ + b=cll_get(); /* current (possibly newly allocated) cheap label block */ + cll_fl=CHEAP; /* do not clear the cll block again... */ + } else if(s[0]=='+') { i++; - n++; - b=0; + n++; /* block number b is absolute */ + b=0; /* global block number */ } while(s[i]=='&') { - n=0; + if (n) b=0; /* reset block number */ + n=0; /* block number is relative */ i++; - b++; + b++; /* one (more) level up the block stack */ } - if(!n) + if(!n) { + /* translate from block stack level to absolute block number */ b_fget(&b,b); + } + if(cll_fl == STD) { + /* clear cheap local labels */ + cll_clear(); + } - if(!isalpha(s[i]) && s[i]!='_') + if((!isalpha(s[i])) && (s[i]!='_') && !(ca65 && ((cll_fl == UNNAMED) || isdigit(s[i])) ) ) { + //printf("SYNTAX cll_fl=%d, i=%d, s[i]=%02x (%c)\n", cll_fl, i, s[i], s[i]); er=E_SYNTAX; - else + } else { - er=ll_search(s+i,&n); + er = E_NODEF; + if (cll_fl != UNNAMED) { + er=ll_search(s+i,&n, cll_fl); + } if(er==E_OK) { + /* we actually found an existing label in the same scope */ ltp=afile->la.lt+n; if(*f) { + /* redefinition of label */ *l=ltp->len+i; } else if(ltp->fl==0) { + /* label has not been defined yet, (e.g. pass1 forward ref), so we try to set it. */ *l=ltp->len+i; if(b_ltest(ltp->blk,b)) er=E_LABDEF; @@ -213,11 +328,13 @@ int l_def(char *s, int *l, int *x, int *f) } else if(er==E_NODEF) { - if(!(er=ll_def(s+i,&n,b))) /* ll_def(...,*f) */ + + if(!(er=ll_def(s+i,&n,b, cll_fl) )) /* store the label in the table of labels */ { ltp=afile->la.lt+n; *l=ltp->len+i; ltp->fl=0; + ltp->is_cll=cll_fl; } } @@ -229,15 +346,31 @@ int l_def(char *s, int *l, int *x, int *f) int l_search(char *s, int *l, int *x, int *v, int *afl) { int n,er,b; + label_t cll_fl; *afl=0; - er=ll_search(s,&n); -/*printf("l_search: lab=%s(l=%d), afl=%d, er=%d, n=%d\n",s,*l, *afl,er,n);*/ + /* check cheap local label */ + cll_fl=STD; + if (s[0]=='@') { + cll_fl=CHEAP; + s++; + } else + if (s[0]==':') { + cll_fl = UNNAMED_DEF; + s++; + } + + er = E_NODEF; + if (cll_fl != UNNAMED_DEF) { + er=ll_search(s,&n, cll_fl); + } + +//printf("l_search: lab=%s(afl=%d, er=%d, cll_fl=%d, cll_cur=%d)\n",s,*afl,er, cll_fl, cll_getcur()); if(er==E_OK) { ltp=afile->la.lt+n; - *l=ltp->len; + *l=ltp->len + ((cll_fl == STD) ? 0 : 1); if(ltp->fl == 1) { l_get(n,v,afl);/* *v=lt[n].val;*/ @@ -251,12 +384,22 @@ int l_search(char *s, int *l, int *x, int *v, int *afl) } else { - b_get(&b); - er=ll_def(s,x,b); /* ll_def(...,*v); */ + if(cll_fl == CHEAP) { + b=cll_get(); + } else + if(cll_fl == UNNAMED_DEF) { + b_get(&b); // b=unn_get(); + } else { + b_get(&b); + } + + er=ll_def(s,x,b, cll_fl); /* ll_def(...,*v); */ ltp=afile->la.lt+(*x); - - *l=ltp->len; + ltp->is_cll = cll_fl; + + *l=ltp->len + ((cll_fl == STD) ? 0 : 1); + //*l=ltp->len + cll_fl; if(!er) { @@ -303,11 +446,87 @@ void l_addocc(int n, int *v, int *afl) { } } +/* for the list functionality */ +char *l_get_name(int n, label_t *is_cll) { + if (n > afile->la.ltm) { + fprintf(stderr, "Corrupted structures! n=%d, but max=%d\n", n, afile->la.ltm); + exit(1); + } + ltp=afile->la.lt+n; + *is_cll = ltp->is_cll; + return ltp->n; +} + +// works on the static(!) ltp "label table pointer" +// also returns the actual index in the table of the current ltp +static int resolve_unnamed() { + // need to count up/down in the linkd label list for the block + char *namep = ltp->n; + int nextp = -1; + //printf("::: unnamed_def: %s, n=%d\n", namep, n); + while ((*namep == '+') || (*namep == '-')) { + char c = *namep; + nextp = -1; + if (c == '+') { + nextp = ltp->blknext; + } else + if (c == '-') { + nextp = ltp->blkprev; + } + //printf("::: nextp=%d\n", nextp); + if (nextp == -1) { + return -1; // E_NODEF + } + ltp = afile->la.lt+nextp; + //printf("::: leads to: %s, nextp=%d\n", ltp->n, nextp); + if (ltp->is_cll == UNNAMED) { + namep++; + } + } + return nextp; +} + +/* for the listing, esp. html links; returns a pointer to a static buffer, available till next call */ +char *l_get_unique_name(int n) { + static char buf[MAXLINE]; + ltp=afile->la.lt+n; + + if (ltp->is_cll == CHEAP || ltp->is_cll == STD) { + sprintf(buf, "%d%c%s", ltp->blk, + (ltp->is_cll == CHEAP) ? 'C' : '_', + ltp->n); + } else + if (ltp->is_cll == UNNAMED) { + // definition of unnamed label - name is NULL + // so use the actual index + sprintf(buf, "%dU%d", ltp->blk, n); + } else + if (ltp->is_cll == UNNAMED_DEF) { + // we actually need to find the correct label from the "+" and "-" + // in the name + int tmp = resolve_unnamed(); + if (tmp >= 0) { + sprintf(buf, "%dU%d", ltp->blk, tmp); + } else { + sprintf(buf, "__%d", tmp); + } + } else { + buf[0] = 0; // no value + } + return buf; +} + int l_get(int n, int *v, int *afl) { if(crossref) l_addocc(n,v,afl); - ltp=afile->la.lt+n; + ltp = afile->la.lt+n; + + if (ltp->is_cll == UNNAMED_DEF) { + int tmp = resolve_unnamed(); + if (tmp == -1) return E_NODEF; + // now ltp is set to the actual label + } (*v)=ltp->val; lz=ltp->n; *afl = ltp->afl; @@ -335,19 +554,23 @@ static void ll_exblk(int a, int b) } } -static int ll_def(char *s, int *n, int b) /* definiert naechstes Label nr->n */ +/* defines next label, returns new label number in out param n */ +static int ll_def(char *s, int *n, int b, label_t ltype) { int j=0,er=E_NOMEM,hash; - char *s2; + char *s2 = NULL; -/*printf("ll_def: s=%s\n",s); */ +//printf("ll_def: s=%s, ltype=%d, no_name=%d\n",s, ltype, no_name); + // label table for the file ... if(!afile->la.lt) { + // ... does not exist yet, so malloc it afile->la.lti = 0; afile->la.ltm = 1000; afile->la.lt = malloc(afile->la.ltm * sizeof(Labtab)); } if(afile->la.lti>=afile->la.ltm) { + // ... or is at its capacity limit, so realloc it afile->la.ltm *= 1.5; afile->la.lt = realloc(afile->la.lt, afile->la.ltm * sizeof(Labtab)); } @@ -355,54 +578,66 @@ static int ll_def(char *s, int *n, int b) /* definiert naechstes Label fprintf(stderr, "Oops: no memory!\n"); exit(1); } -#if 0 - if((ltila.lt+afile->la.lti; -/* - s2=ltp->n=ln+lni; - while((jla.lt + afile->la.lti; + + if (ltype != UNNAMED) { + // alloc space and copy over name + if (ltype == UNNAMED_DEF) { + // unnamed lables are like ":--" or ":+" with variable length + while((s[j]!='\0') && (s[j]=='+' || s[j]=='-')) j++; + } else { + // standard (and cheap) labels are normal text + while((s[j]!='\0') && (isalnum(s[j]) || (s[j]=='_'))) j++; + } + s2 = malloc(j+1); + if(!s2) { fprintf(stderr,"Oops: no memory!\n"); exit(1); + } + strncpy(s2,s,j); + s2[j]=0; } - strncpy(s2,s,j); - s2[j]=0; -/* - if(jlen=j; - ltp->n = s2; - ltp->blk=b; + ltp->len=j; // length of label + ltp->n = s2; // name of label (char*) + ltp->blk=b; // block number ltp->fl=0; ltp->afl=0; - ltp->occlist=NULL; - hash=hashcode(s,j); - ltp->nextindex=afile->la.hashindex[hash]; - afile->la.hashindex[hash]=afile->la.lti; - *n=afile->la.lti; - afile->la.lti++; -/* lni+=j+1;*/ -/* } - } -*/ -/*printf("ll_def return: %d\n",er);*/ + ltp->is_cll=ltype; // STD, CHEAP, or UNNAMED label + ltp->occlist=NULL; + hash=hashcode(s,j); // compute hashcode + ltp->nextindex=afile->la.hashindex[hash]; // and link in before last entry with same hashcode + afile->la.hashindex[hash]=afile->la.lti; // set as start of list for that hashcode + + // TODO: does not work across files! + ltp->blknext = -1; // no next block + ltp->blkprev = b_link( afile->la.lti ); // previous block, linked within block + + if (ltp->blkprev != -1) { + ltp = afile->la.lt + ltp->blkprev; + ltp->blknext = afile->la.lti; + } + + *n=afile->la.lti; // return the list index for that file in the out parameter n + afile->la.lti++; // increase last index in lable table + return(er); } - -int ll_search(char *s, int *n) /* search Label in Tabelle ,nr->n */ +/** + * search a label name in the label table. Return the label number + * in "n". Finds only labels that are in a block that is in the current + * set of blocks (in the block stack) + * + * If cll_fl is set, the label is also searched in the local cheap label scope + * + * Do not define the label (as is done in l_search()!) + */ +int ll_search(char *s, int *n, label_t cll_fl) /* search Label in Tabelle ,nr->n */ { int i,j=0,k,er=E_NODEF,hash; @@ -411,7 +646,6 @@ int ll_search(char *s, int *n) /* search Label in Tabelle ,nr->n */ hash=hashcode(s,j); i=afile->la.hashindex[hash]; -/*printf("search?\n");*/ if(i>=afile->la.ltm) return E_NODEF; do @@ -422,11 +656,24 @@ int ll_search(char *s, int *n) /* search Label in Tabelle ,nr->n */ { for (k=0;(kn[k]==s[k]);k++); - if((j==k)&&(!b_test(ltp->blk))) - { - er=E_OK; - break; - } + if (cll_fl == CHEAP) { + if (ltp->blk == cll_getcur()) { + er=E_OK; + break; + } + } else + if (cll_fl == UNNAMED) { + // TODO + } else { + /* check if the found label is in any of the blocks in the + current block stack */ + if((j==k)&&(!b_test(ltp->blk))) + { + /* ok, label found and it is reachable (its block nr is in the current block stack */ + er=E_OK; + break; + } + } } if(!i) @@ -451,7 +698,7 @@ int ll_pdef(char *t) { int n; - if(ll_search(t,&n)==E_OK) + if(ll_search(t,&n, STD)==E_OK) { ltp=afile->la.lt+n; if(ltp->fl) @@ -496,19 +743,39 @@ int l_write(FILE *fp) return 0; } -static int bt[MAXBLK]; -static int blk; -static int bi; +/******************************************************************************************* + * block management code. Here the ".(" and ".)" blocks are maintained. + * + * Blocks are numbered uniquely, every time a new block is opened, the "blk" variable + * is increased and its number used as block number. + * + * The currently open blocks are maintained in a stack (bt[]). The lowest entry is the outermost + * block number, adding block numbers as blocks are opened. When a block is closed, + * the block stack is shortened again (bi has the length of the block stack) + * + * Methods exist to open new blocks, close a block, and do some checks, e.g. whether + * a specific block number is contained in the current block stack. + */ +static int bt[MAXBLK]; /* block stack */ +static int labind; /* last allocated label, -1 none yet alloc'd - used for linking to find unnamed labels */ +static int bi; /* length of the block stack (minus 1, i.e. bi[bi] has the innermost block) */ +static int blk; /* current block number for allocation */ int b_init(void) { blk =0; bi =0; bt[bi]=blk; + labind=-1; return(E_OK); } +int b_new(void) +{ + return ++blk; +} + int b_depth(void) { return bi; @@ -519,19 +786,26 @@ int ga_blk(void) return(blk); } +/** + * open a new block scope + */ int b_open(void) { int er=E_BLKOVR; if(bi=0) @@ -562,6 +843,10 @@ static int b_fget(int *n, int i) return(E_OK); } +/** + * tests whether the given block number n is in the current stack of + * current block numbers bt[] + */ static int b_test(int n) { int i=bi; @@ -572,6 +857,9 @@ static int b_test(int n) return( i+1 ? E_OK : E_NOBLK ); } +/** + * tests whether the given block number "a" is in the + */ static int b_ltest(int a, int b) /* testet ob bt^-1(b) in intervall [0,bt^-1(a)] */ { int i=0,er=E_OK; @@ -593,3 +881,10 @@ static int b_ltest(int a, int b) /* testet ob bt^-1(b) in intervall [0,bt^-1( return(er); } +int b_link(int newlab) { + int tmp = labind; + //printf("b_link: old was %d, set to %d\n", tmp, newlab); + labind = newlab; + return tmp; +} + diff --git a/xa/src/xal.h b/xa/src/xal.h index 98a1d75..d4c53b5 100644 --- a/xa/src/xal.h +++ b/xa/src/xal.h @@ -21,8 +21,13 @@ #include /* for FILE */ +/* nasty stuff - "lz" is exported from xal.c so xa.c can print the name + * of the label that was last searched for in the error message that the + * label was not found... + */ extern char *lz; + int l_init(void); int ga_lab(void); int gm_lab(void); @@ -30,6 +35,8 @@ long gm_labm(void); long ga_labm(void); int lg_set(char *); +int lg_import(int); +int lg_importzp(int); int b_init(void); int b_depth(void); @@ -40,9 +47,11 @@ int ga_blk(void); int l_def(char *s, int* l, int *x, int *f); int l_search(char *s, int *l, int *x, int *v, int *afl); void l_set(int n, int v, int afl); +char* l_get_name(int n, label_t *is_cll); +char* l_get_unique_name(int n); int l_get(int n, int *v, int *afl); int l_vget(int n, int *v, char **s); -int ll_search(char *s, int *n); +int ll_search(char *s, int *n, label_t labeltype); int ll_pdef(char *t); int b_open(void); diff --git a/xa/src/xalisting.c b/xa/src/xalisting.c new file mode 100644 index 0000000..85440c9 --- /dev/null +++ b/xa/src/xalisting.c @@ -0,0 +1,730 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * maintained by Cameron Kaiser + * + * Assembler listing + * + * 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. + */ + +/* enable this to turn on (copious) optimization output */ +/* #define DEBUG_AM */ +#undef LISTING_DEBUG + +#include +#include + +#include "xah.h" +#include "xal.h" +#include "xat.h" + + +/*********************************************************************************************/ +/* this is the listing code + * + * Unfortunately this code has to go here (for now), as this file is the only one + * where we have access to the tables that allow to convert the tokens back to + * a listing + */ + +static FILE *listfp = NULL; +static int list_lineno = 1; /* current line number */ +static int list_last_lineno = 0; /* current line number */ +static char *list_filenamep = NULL; /* current file name pointer */ + +static int list_numbytes = 8; + +static int list_string(char *buf, char *string); +static int list_tokens(char *buf, signed char *input, int len); +static int list_value(char *buf, int val, signed char format); +static int list_nchar(char *buf, signed char c, int n); +static int list_char(char *buf, signed char c); +static int list_sp(char *buf); +static int list_word(char *buf, int outword); +static int list_word_f(char *buf, int outword, signed char format); +static int list_byte(char *buf, int outbyte); +static int list_byte_f(char *buf, int outbyte, signed char format); +static int list_nibble_f(char *buf, int outnib, signed char format); + +/*********************************************************************************************/ + +// formatter +typedef struct { + void (*start_listing)(char *name); + void (*start_line)(); + int (*set_anchor)(char *buf, char *name); // returns number of bytes added to buf + int (*start_label)(char *buf, char *name); // returns number of bytes added to buf + int (*end_label)(char *buf); + void (*end_line)(); + void (*end_listing)(); + char* (*escape)(char *toescape); // returns pointer to static buffer, valid until next call + char* (*escape_char)(char toescape); // returns pointer to static buffer, valid until next call +} formatter_t; + +static char *def_escape(char *toescape) { + return toescape; +} + +static char *def_escape_char(char toescape) { + static char buf[2]; + buf[0] = toescape; + buf[1] = 0; + return buf; +} + +static formatter_t def_format = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + def_escape, def_escape_char +}; + +static void html_start_listing(char *name) { + // really short version for now + fprintf(listfp, "%s
\n", 
+		(name == NULL) ? "(null)" : name);
+}
+
+static int html_set_anchor(char *buf, char *name) {
+	sprintf(buf, " ", name);
+	return strlen(buf);
+}
+
+static int html_start_label(char *buf, char *name) {
+	sprintf(buf, "", name);
+	return strlen(buf);
+}
+
+static int html_end_label(char *buf) {
+	sprintf(buf, "");
+	return strlen(buf);
+}
+
+static void html_end_listing() {
+	fprintf(listfp, "
\n"); +} + +static char *html_escape(char *toescape) { + static char buf[MAXLINE]; + + char *p = toescape; + char *q = buf; + + while (*p != 0) { + if (*p == '<') { + strcpy(q, "<"); + q+=4; + p++; + } else + if (*p == '&') { + strcpy(q, "&"); + q+=5; + p++; + } else { + *q = *p; + q++; + p++; + } + } + *q = 0; // string terminator + return buf; +} + +static char *html_escape_char(char toescape) { + static char buf[2]; + + buf[0] = toescape; + buf[1] = 0; + + return html_escape(buf); +} + +static formatter_t html_format = { + html_start_listing, + NULL, + html_set_anchor, + html_start_label, + html_end_label, + NULL, + html_end_listing, + html_escape, html_escape_char +}; + +static formatter_t *formatp = &def_format; + +/*********************************************************************************************/ + +void list_flush() { + if (listfp != NULL) { + fflush(listfp); + } +} + +void list_start(const char *formatname) { + formatp = &def_format; + + if (formatname != NULL && strcmp("html", formatname) == 0) { + formatp = &html_format; + } + + if (listfp != NULL) { + if (formatp->start_listing != NULL) formatp->start_listing(list_filenamep); + } +} + +void list_end() { + if (listfp != NULL) { + if (formatp->end_listing != NULL) formatp->end_listing(); + } +} + +// set number of bytes per line displayed as hex +void list_setbytes(int number_of_bytes_per_line) { + list_numbytes = number_of_bytes_per_line; +} + +/* set line number for the coming listing output */ +void list_line(int l) { + list_lineno = l; +} + +/* set file name for the coming listing output */ +void list_filename(char *fname) { + if (list_filenamep == NULL || (fname != NULL && strcmp(fname, list_filenamep) != 0)) { + list_filenamep = fname; + list_lineno = 1; + list_last_lineno = 0; + + /* Hack */ + if (listfp != NULL) { + fprintf(listfp, "\n%s\n\n", fname); + } + } +} + +/** + * set the output file descriptor where to write the listing + */ +void list_setfile(FILE *fp) { + listfp = fp; +} + +char *list_preamble(char *buf, int lineno, int seg, int pc) { + /* line number in file */ + snprintf(buf, 10, "% 5d", lineno); + int i = strlen(buf); + buf += i; + buf += list_char(buf, ' '); + + char c = '?'; + /* preamble ':'
' ' */ + switch(seg) { + case SEG_ABS: c='A'; break; + case SEG_TEXT: c='T'; break; + case SEG_BSS: c='B'; break; + case SEG_DATA: c='D'; break; + case SEG_UNDEF: c='U'; break; + case SEG_ZERO: c='Z'; break; + } + buf = buf + list_char(buf, c); + buf = buf + list_char(buf, ':'); + buf = buf + list_word(buf, pc); + buf = buf + list_nchar(buf, ' ', 2); + + return buf; +} + + +/** + * listing/listing_len give the buffer address and length respectively that contains + * the token as they are produced by the tokenizer. + * bincode/bincode_len give the buffer address and length that contain the binary code + * that is produced from the token listing + * + * Note that both lengths may be zero + */ +void do_listing(signed char *listing, int listing_len, signed char *bincode, int bincode_len) { + + int i, n_hexb; + + char outline[MAXLINE]; + char *buf = outline; + + int lst_seg = listing[0]; + int lst_pc = (listing[2]<<8) | (listing[1] & 255); + + /* no output file (not even stdout) */ + if (listfp == NULL) return; + + + /*printf("do_listing: listing=%p (%d), bincode=%p (%d)\n", listing, listing_len, bincode, bincode_len);*/ + + if (bincode_len < 0) bincode_len = -bincode_len; + + /* do we need a separation line? */ + if (list_lineno > list_last_lineno+1) { + /* yes */ + /*fprintf(listfp, "line=%d, last=%d\n", list_lineno, list_last_lineno);*/ + fprintf(listfp, "\n"); + } + list_last_lineno = list_lineno; + + // could be extended to include the preamble... + if (formatp->start_line != NULL) formatp->start_line(); + + buf = list_preamble(buf, list_lineno, lst_seg, lst_pc); + + // check if we have labels, so we can adjust the max printable number of + // bytes in the last line + int num_last_line = 11; + int tmp = listing[3] & 255; + if (tmp == (T_DEFINE & 255)) { + // we have label definition + num_last_line = 8; + } + int overflow = 0; + + /* binary output (up to 8 byte. If more than 8 byte, print 7 plus "..." */ + n_hexb = bincode_len; + if (list_numbytes != 0 && n_hexb >= list_numbytes) { + n_hexb = list_numbytes-1; + overflow = 1; + } + for (i = 0; i < n_hexb; i++) { + buf = buf + list_byte(buf, bincode[i]); + buf = buf + list_sp(buf); + if ( (i%16) == 15) { + // make a break + buf[0] = 0; + fprintf(listfp, "%s\n", outline); + if (formatp->end_line != NULL) formatp->end_line(); + if (formatp->start_line != NULL) formatp->start_line(); + buf = outline; + buf = list_preamble(buf, list_lineno, lst_seg, lst_pc + i + 1); + } + } + if (overflow) { + // are we at the last byte? + if (n_hexb + 1 == bincode_len) { + // just print the last byte + buf = buf + list_byte(buf, bincode[i]); + buf = buf + list_sp(buf); + } else { + // display "..." + buf = buf + list_nchar(buf, '.', 3); + } + n_hexb++; + } + i = n_hexb % 16; + if (i > num_last_line) { + // make a break (Note: with original PC, as now the assembler text follows + buf[0] = 0; + fprintf(listfp, "%s\n", outline); + if (formatp->end_line != NULL) formatp->end_line(); + if (formatp->start_line != NULL) formatp->start_line(); + buf = outline; + buf = list_preamble(buf, list_lineno, lst_seg, lst_pc); + i = 0; + } + i = num_last_line - i; + buf = buf + list_nchar(buf, ' ', i * 3); + + buf = buf + list_sp(buf); + + buf += list_tokens(buf, listing + 3, listing_len - 3); + +#ifdef LISTING_DEBUG + /* for now only do a hex dump so we see what actually happens */ + { + char valbuf[32]; + i = buf - outline; + if (i<80) buf += list_nchar(buf, ' ', 80-i); + + buf += list_string(buf, " >>"); + sprintf(valbuf, "%p", listing+3); + buf += list_string(buf, valbuf); + buf += list_sp(buf); + for (i = 3; i < listing_len; i++) { + buf = buf + list_byte(buf, listing[i]); + buf = buf + list_sp(buf); + } + } +#endif + buf[0] = 0; + + fprintf(listfp, "%s\n", outline); + + if (formatp->end_line != NULL) formatp->end_line(); +} + +int list_tokens(char *buf, signed char *input, int len) { + int outp = 0; + int inp = 0; + int tmp; + char *name; + signed char c; + label_t is_cll; + int tabval; + signed char format; + + if (inp >= len) return 0; + + tmp = input[inp] & 255; + + tabval = 0; + if (tmp == (T_DEFINE & 255)) { + while (inp < len && tmp == (T_DEFINE & 255)) { + tmp = ((input[inp+2]&255)<<8) | (input[inp+1]&255); +/*printf("define: len=%d, inp=%d, tmp=%d\n", len, inp, tmp);*/ + name=l_get_name(tmp, &is_cll); + + // duplicate anchor names? + if (formatp->set_anchor != NULL) { + outp += formatp->set_anchor(buf+outp, l_get_unique_name(tmp)); + } + + if (is_cll == CHEAP) { + outp += list_char(buf+outp, '@'); + } else + if (is_cll == UNNAMED_DEF || is_cll == UNNAMED) { + outp += list_char(buf+outp, ':'); + } + if (is_cll != UNNAMED) { + tmp = list_string(buf+outp, name); + tabval += tmp + 1 + is_cll; + outp += tmp; + } + outp += list_char(buf+outp, ' '); + inp += 3; + tmp = input[inp] & 255; + } + if (tabval < 10) { + outp += list_nchar(buf+outp, ' ', 10-tabval); + } + } else { + if (tmp >= 0 && tmp < number_of_valid_tokens) { + outp += list_string(buf+outp, " "); + } + } + + if (tmp >= 0 && tmp < number_of_valid_tokens) { + /* assembler keyword */ + /*printf("tmp=%d, kt[tmp]=%p\n", tmp, kt[tmp]);*/ + if (kt[tmp] != NULL) { + outp += list_string(buf+outp, kt[tmp]); + } + outp += list_sp(buf + outp); + inp += 1; +#if 0 + if (tmp == Kinclude) { + /* just another exception from the rule... */ + /* next char is terminator (", ') then the length and then the file name */ + char term = input[inp]; + int len = input[inp+1] & 255; + outp += list_char(buf+outp, term); + for (tmp = 2; tmp < len+2; tmp++) { + outp += list_char(buf+outp, input[inp+tmp]); + } + outp += list_char(buf+outp, term); + inp += len + 2; + } +#endif + } + + while (inp < len) { + int operator = 0; + + switch(input[inp]) { + case T_CAST: + outp += list_string(buf+outp, formatp->escape_char(input[inp+1])); + inp+=2; + break; + case T_VALUE: + /*outp += list_char(buf+outp, 'V');*/ + /* 24 bit value */ + tmp = ((input[inp+3]&255)<<16) | ((input[inp+2]&255)<<8) | (input[inp+1]&255); + format = input[inp+4]; + outp += list_value(buf+outp, tmp, format); + inp += 5; + operator = 1; /* check if arithmetic operator follows */ + break; + case T_LABEL: + /*outp += list_char(buf+outp, 'L');*/ + /* 16 bit label number */ + tmp = ((input[inp+2]&255)<<8) | (input[inp+1]&255); + name=l_get_name(tmp, &is_cll); + + // duplicate label name + if (formatp->start_label != NULL) { + outp += formatp->start_label(buf+outp, l_get_unique_name(tmp)); + } + if (is_cll == CHEAP) { + outp += list_char(buf+outp, '@'); + } else + if (is_cll == UNNAMED || is_cll == UNNAMED_DEF) { + outp += list_char(buf+outp, ':'); + } + if (is_cll != UNNAMED) { + outp += list_string(buf+outp, name == NULL ? "" : name); + } + + if (formatp->end_label != NULL) outp += formatp->end_label(buf+outp); + + inp += 3; + operator = 1; /* check if arithmetic operator follows */ + break; + case T_OP: + /* label arithmetic operation; inp[3] is operation like '=' or '+' */ + tmp = ((input[inp+2]&255)<<8) | (input[inp+1]&255); + name=l_get_name(tmp, &is_cll); + if (input[inp+3] == '=') { + // label definition + if (formatp->set_anchor != NULL) { + outp += formatp->set_anchor(buf+outp, l_get_unique_name(tmp)); + } + } + if (is_cll) outp += list_char(buf+outp, '@'); + outp += list_string(buf+outp, name); + outp += list_char(buf+outp, input[inp+3]); + inp += 4; + + break; + case T_END: + /* end of operation */ + /*outp += list_string(buf+outp, ";");*/ + inp += 1; + goto end; + break; + case T_COMMENT: + if (inp > 0 && inp < 20) { + outp += list_nchar(buf+outp, ' ', 20-inp); + } + tmp = ((input[inp+2]&255)<<8) | (input[inp+1]&255); + outp += list_char(buf+outp, ';'); + outp += list_string(buf+outp, (char*)input+inp+3); + inp += tmp + 3; + break; + case T_LINE: + case T_FILE: + /* those two are meta-tokens, evaluated outside the t_p2 call, + * they result in calls to list_line(), list_filename() */ + break; + case T_POINTER: + /* what is this? It's actually resolved during token conversion */ + tmp = ((input[inp+5]&255)<<8) | (input[inp+4]&255); + name=l_get_name(tmp, &is_cll); + if (formatp->start_label != NULL) { + outp += formatp->start_label(buf+outp, l_get_unique_name(tmp)); + } + if (is_cll) outp += list_char(buf+outp, '@'); + outp += list_string(buf+outp, name); + + if (formatp->end_label != NULL) outp += formatp->end_label(buf+outp); + /* + outp += list_byte(buf+outp, input[inp+1]); + outp += list_char(buf+outp, '#'); + tmp = ((input[inp+3]&255)<<8) | (input[inp+2]&255); + outp += list_value(buf+outp, tmp); + */ + inp += 6; + operator = 1; /* check if arithmetic operator follows */ + break; + case '"': + // string display + inp++; + outp += list_char(buf+outp, '"'); + int len = input[inp] & 0xff; + for (int i = 0; i < len; i++) { + inp++; + outp += list_char(buf+outp, input[inp]); + } + inp++; + outp += list_char(buf+outp, '"'); + break; + default: + c = input[inp]; + if (c > 31) { + outp += list_string(buf+outp, formatp->escape_char(input[inp])); + } else { + outp += list_char(buf+outp, '\''); + outp += list_byte(buf+outp, input[inp]); + } + inp += 1; + break; + } + + if (operator && inp < len) { + signed char op = input[inp]; + if (op > 0 && op <= 17) { + outp += list_string(buf+outp, formatp->escape(arith_ops[op])); + inp += 1; + } + operator = 0; + } + } +end: + return outp; +} + +int list_string(char *buf, char *string) { + if (buf == NULL || string == NULL) { + fprintf(stderr, "NULL pointer: buf=%p, string=%p\n", buf, string); + fflush(stderr); + //exit(1); + return 0; + } + + int p = 0; + while (string[p] != 0) { + buf[p] = string[p]; + p++; + } + return p; +} + +int list_value(char *buf, int val, signed char format) { + int p = 0; + char valbuf[32]; + + switch (format) { + case '$': + p += list_char(buf + p, '$'); + if (val & (255<<16)) { + p += list_byte(buf+p, val>>16); + p += list_word(buf+p, val); + } else + if (val & (255<<8)) { + p += list_word(buf+p, val); + } else { + p += list_byte(buf+p, val); + } + break; + case '%': + p += list_char(buf + p, '%'); + if (val & (255<<16)) { + p += list_byte_f(buf+p, val>>16,'%'); + p += list_word_f(buf+p, val,'%'); + } else + if (val & (255<<8)) { + p += list_word_f(buf+p, val,'%'); + } else { + p += list_byte_f(buf+p, val,'%'); + } + break; + case '&': + snprintf(valbuf, 32, "%o",val); + p+= list_char(buf+p, '&'); + p+= list_string(buf+p, valbuf); + break; + case 'd': + snprintf(valbuf, 32, "%d",val); + p+= list_string(buf+p, valbuf); + break; + case '\'': + case '"': + p+= list_char(buf+p, format); + p+= list_char(buf+p, val); + p+= list_char(buf+p, format); + break; + default: + /* hex format as fallback */ + p += list_char(buf + p, '$'); + if (val & (255<<16)) { + p += list_byte(buf+p, val>>16); + p += list_word(buf+p, val); + } else + if (val & (255<<8)) { + p += list_word(buf+p, val); + } else { + p += list_byte(buf+p, val); + } + break; + } + return p; +} + +int list_nchar(char *buf, signed char c, int n) { + int i; + for (i = 0; i < n; i++) { + buf[i]=c; + } + return n; +} + +int list_char(char *buf, signed char c) { + buf[0] = c; + return 1; +} + +int list_sp(char *buf) { + buf[0] = ' '; + return 1; +} + +int list_word(char *buf, int outword) { + return list_word_f(buf, outword, '$'); +} + +int list_word_f(char *buf, int outword, signed char format) { + int p = 0; + p+= list_byte_f(buf+p, outword >> 8, format); + p+= list_byte_f(buf+p, outword, format); + return p; +} + +int list_byte(char *buf, int outbyte) { + return list_byte_f(buf, outbyte, '$'); +} + +int list_byte_f(char *buf, int outbyte, signed char format) { + int p = 0; + p+= list_nibble_f(buf+p, (outbyte >> 4), format); + p+= list_nibble_f(buf+p, outbyte, format); + return p; +} + +int list_nibble_f(char *buf, int outnib, signed char format) { + int p = 0; + outnib = outnib & 0xf; + switch(format) { + case '$': + if (outnib < 10) { + buf[p]='0'+outnib; + } else { + buf[p]='a'-10+outnib; + } + p++; + break; + case '%': + buf[p++] = (outnib&8)?'1':'0'; + buf[p++] = (outnib&4)?'1':'0'; + buf[p++] = (outnib&2)?'1':'0'; + buf[p++] = (outnib&1)?'1':'0'; + break; + default: + /* hex as default */ + if (outnib < 10) { + buf[p]='0'+outnib; + } else { + buf[p]='a'-10+outnib; + } + p++; + break; + } + return p; +} + + + diff --git a/xa/src/xalisting.h b/xa/src/xalisting.h new file mode 100644 index 0000000..8505047 --- /dev/null +++ b/xa/src/xalisting.h @@ -0,0 +1,34 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * 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. + */ +#ifndef __XA65_XALISTING_H__ +#define __XA65_XALISTING_H__ + +void list_start(char *formatname); //either NULL or "html" +void list_end(); + +void list_flush(); // debug helper + +void list_setfile(FILE *fp); +void list_line(int l); /* set line number for the coming listing output */ +void list_filename(char *fname);/* set file name for the coming listing output */ + +// list a single line/token set +void do_listing(signed char *listing, int listing_len, signed char *bincode, int bincode_len); + +#endif /* __XA65_XALISTING_H__ */ diff --git a/xa/src/xam.c b/xa/src/xam.c index 5e4b8ef..968c8bb 100644 --- a/xa/src/xam.c +++ b/xa/src/xam.c @@ -53,10 +53,13 @@ FILE *xfopen(const char *fn,const char *mode) return NULL; } + // copy to xname by replacing windows backslashes with the proper DIRCHAR for(i=0;ifo.olist[i].len; t=afile->fo.olist[i].text; /* do not optimize */ - t_p2(t, &l, 1, &afl); + t_p2_l(t, &l, &afl); if(l>254) { errout(E_OPTLEN); diff --git a/xa/src/xap.c b/xa/src/xap.c index da8e3c3..0fa4a5b 100644 --- a/xa/src/xap.c +++ b/xa/src/xap.c @@ -40,7 +40,8 @@ #include "xap.h" /* define this for recursive evaluation output */ -/* #define DEBUG_RECMAC */ +#undef DEBUG_RECMAC +#undef DEBUG_REPLACE char s[MAXLINE]; Datei *filep; @@ -50,7 +51,8 @@ static int pp_replace(char*,char*,int,int); static int searchdef(char*); static int fgetline(char*,int len, int *rlen, FILE*); -static int icl_open(char*),pp_ifdef(char*),pp_ifndef(char*); +/*static int icl_open(char*);*/ +static int pp_ifdef(char*),pp_ifndef(char*); static int pp_else(char*),pp_endif(char*); static int pp_echo(char*),pp_if(char*),pp_print(char*),pp_prdef(char*); static int pp_ifldef(char*),pp_iflused(char*); @@ -130,7 +132,7 @@ int pp_ifldef(char *t) int pp_iflused(char *t) { int n; - loopfl=(loopfl<<1)+( ll_search(t,&n) ? 1 : 0 ); + loopfl=(loopfl<<1)+( ll_search(t,&n,STD) ? 1 : 0 ); return(0); } @@ -353,7 +355,10 @@ int pp_define(char *k) { i++; liste[rl].p_anz++; - for(j=0; t[i+j]!=')' && t[i+j]!=',' && t[i+j]!='\0';j++); + // skip whitespace before parameter name + while(isspace(t[i])) i++; + // find length + for(j=0; (!isspace(t[i+j])) && t[i+j]!=')' && t[i+j]!=',' && t[i+j]!='\0';j++); if(ji=%d, len=%d\n", n, liste[n].search,i, liste[n].s_len); +#endif + return i == liste[n].s_len; +} + +/** + * this is a break out of the original pp_replace code, as this code + * was basically duplicated for initial and recursive calls. + */ +static int pp_replace_part(char *to, char *t, int n, int sl, int recursive, int *l, int blist) { - char *t=to,c,*x,*y,*mx,*rs; - int i,l,n,sl,d,ld,er=E_OK,hkfl,klfl; - char fti[MAXLINE],fto[MAXLINE]; -/* - int flag=!strncmp(ti,"TOUT",4); - if(flag) printf("flag=%d\n",flag); -*/ - (void)strcpy(t,ti); + int er = E_OK; - if(rlist) - { - while(t[0]!='\0') - { - while(!isalpha(t[0]) && t[0]!='_') - if(t[0]=='\0') - break; /*return(E_OK);*/ - else - { - t++; - ti++; - } - - for(l=0;isalnum(t[l])||t[l]=='_';l++); - ld=l; -/* - if(flag) printf("l=%d,a=%d,t=%s\n",l,a,t); -*/ - if(a<0) - { - n=hashindex[hashcode(t,l)]; - - do - { - sl=liste[n].s_len; - - if(sl && (sl==l)) - { - i=0; - x=liste[n].search; - while(t[i]==*x++ && t[i]) - i++; + // save mem, to restore it when we don't need the pseudo replacements anymore + // Note: in a real version, that should probably be a parameter, and not fiddling + // with global variables... + char *saved_mem = mem; - if(i==sl) - { - rs=liste[n].replace; - +#ifdef DEBUG_RECMAC + printf("replace part: n=%d, sl=%d, rec=%d, %s\n", n, sl, recursive, t); +#endif + + // yes, mark replacement string + char *rs=liste[n].replace; + + // does it have parameters? if(liste[n].p_anz) { + // yes, we have parameters, so we need to pp_replace them + // as well. + char fti[MAXLINE],fto[MAXLINE]; + + // copy replacement into temp buffer (void)strcpy(fti,liste[n].replace); - if(rlist+liste[n].p_anz>=ANZDEF || memfre=ANZDEF || memfre10) + if (++j>10) { + // we ran into 10 recursions deep - that does not look sound, bail out errout(E_ORECMAC); + } } - (void)strcpy(liste[rlist+i].replace, nfto); +#endif + // copy over the replacement string into free memory (using mx as pointer) + (void)strcpy(mx, nfto); + // replace the pointer to the (now obsolete) old replacement with the one we just created + // Note that due to the nature of the (more or less static) memory allocation, this is not + // being freed. Oh well... + liste[blist+i].replace = mx; + mx += strlen(mx)+1; + #ifdef DEBUG_RECMAC printf("FINAL: -%s=%s\n",liste[rlist+i].search,liste[rlist+i].replace); #endif } - if(!er) - er=pp_replace(fto,fti,rlist,rlist+i); + + if(!er) { + // safe-guard our memory allocations + mem = mx; + // only change (of two) from recursive: rlist is 0 there +#ifdef DEBUG_RECMAC + printf("replace macro: recursive=%d, blist=%d, -> b=%d\n", recursive, blist, blist+liste[n].p_anz); + printf(" from: %s\n", fti); +#endif + er=pp_replace(fto,fti,recursive ? 0 : blist,blist+liste[n].p_anz); +#ifdef DEBUG_RECMAC + printf(" to: %s\n", fto); +#endif + } /* if(flag) printf("sl=%d,",sl);*/ sl=(int)((long)y+1L-(long)t); /* if(flag) printf("sl=%d\n",sl);*/ rs=fto; -/* printf("->%s\n",fto);*/ +#ifdef DEBUG_RECMAC + printf("->%s\n",fto); +#endif } } - if(er) + if(er) { + mem = saved_mem; return(er); - } + } + } // end if(liste[n].p_anz), i.e. if it has parameters - d=(int)strlen(rs)-sl; + int d=(int)strlen(rs)-sl; - if(strlen(to)+d>=MAXLINE) + if(strlen(to)+d>=MAXLINE) { + mem = saved_mem; return(E_NOMEM); + } /* if(d<0) @@ -596,129 +681,153 @@ int pp_replace(char *to, char *ti, int a,int b) t[ll+d]=t[ll]; } */ - if(d) - (void)strcpy(t+sl+d,ti+sl); + if(d) { + // d can be positive or negative, so strcpy cannot be used, use memmove instead + (void)memmove(t+sl+d,t+sl, strlen(t) - sl + 1); + } - i=0; - while((c=rs[i])) + int i=0; + char c; + while((c=rs[i])) { t[i++]=c; - l=sl+d;/*=0;*/ - break; - } + } + // other change from recursive. there sl is missing from add + //*l=(recursive ? 0 : sl) + d;/*=0;*/ + *l=sl + d;/*=0;*/ + + mem = saved_mem; + + return (er); +} + +/** + * copy the input string pointed to by ti into + * an output string buffer pointed to by to, replacing all + * preprocessor definitions in the process. + * + * Note: this method is called recursively, with "a" being -1 + * when called from the "outside" respectively in a new context + * (like macro parameters) + * + * The "b" parameter denotes the index in the list from which on + * pseudo replacement entries are being created for replacement + * parameters + * + */ +int pp_replace(char *to, char *ti, int a,int b) +{ + char *t=to; + int l,n,sl,er=E_OK; + int ld; // length of name/token to analyse +/* + int flag=!strncmp(ti,"TOUT",4); + if(flag) printf("flag=%d\n",flag); +*/ + // t points to to, so copy input to output 1:1 + // then below work on the copy in-place + (void)strcpy(t,ti); + + // if there are replacements in the list of replacements + if(rlist) + { + // loop over the whole input + while(t[0]!='\0' && t[0] != ';') + { + // skip over the whitespace + // comment handling is NASTY NASTY NASTY + // but I currently don't see another way, as comments and colons + // can (and do) appear in preprocessor replacements + char quotefl = 0; + char commentfl = 0; + while((t[0] != 0) && (commentfl || ((!isalpha(t[0]) && t[0]!='_')))) { + if (t[0]=='\0') { + break; /*return(E_OK);*/ + } else + { + if (t[0] == ';' && !quotefl) { + commentfl = 1; + } + if (t[0] == ':' && !quotefl && !ca65 && !masm) { + // note that both ca65 and masm allow colons in comments + // so in these cases we cannot reset the comment handling here + commentfl = 0; + } + if (quotefl) { + // ignore other quotes within a quote + if (t[0] == quotefl) { + quotefl = 0; + } + } else + if (t[0] == '"' || t[0] == '\'') { + quotefl = t[0]; + } + t++; + ti++; + } + } + + // determine the length of the name + for(l=0;isalnum(t[l])||t[l]=='_';l++); + // store in ld + ld=l; +#ifdef DEBUG_RECMAC + printf("l=%d,a=%d,b=%d, t=%s\n",l,a, b ,t); +#endif + + // the following if() is executed when being called from an + // 'external' context, i.e. not from a recursion + if(a<0) + { + // when called from an external context, not by recursion + + // compute hashcode for the name for the search index entry (liste[n]) + n=hashindex[hashcode(t,l)]; + + // loop over all entries in linked list for hash code (think: hash collisions) + do // while(1); + { + // length of name of pp definition + sl=liste[n].s_len; + + // does pp definition match what we have found? + if(sl && (sl==l) && check_name(t, n)) + { + er = pp_replace_part(to, t, n, sl, 0, &l, b); + if (er != E_OK) { + return er; + } + break; } if(!n) break; - + + // next index in linked list for given hash code n=liste[n].nextindex; } while(1); } else { + // called when in recursive call + // loop over all the replacement entries from the given b down to 0 + // that allows to replace the parameters first (as they were added at + // the end of the list) for(n=b-1;n>=a;n--) { sl=liste[n].s_len; - if(sl && (sl==l)) + if(sl && (sl==l) && check_name(t, n)) { - i=0; - x=liste[n].search; - while(t[i]==*x++ && t[i]) - i++; - - if(i==sl) - { - rs=liste[n].replace; - if(liste[n].p_anz) - { - (void)strcpy(fti,liste[n].replace); - if(rlist+liste[n].p_anz>=ANZDEF || memfre=MAXLINE) - return(E_NOMEM); -/* - if(d<0) - { - y=t+sl+d; - x=t+sl; - while(*y++=*x++); - } - if(d>0) - { - for(ll=strlen(t);ll>=sl;ll--) - t[ll+d]=t[ll]; - } -*/ - if(d) - (void)strcpy(t+sl+d,ti+sl); - - i=0; - while((c=rs[i])) - t[i++]=c; - l+=d;/*0;*/ - break; - } + er = pp_replace_part(to, t, n, sl, 1, &l, b); + break; } } } + // advance input by length of name ti+=ld; + // advance output by l t+=l; - } - } + } /* end while(t[0] != 0) */ + } /* end if(rlist) */ return(E_OK); } @@ -752,14 +861,16 @@ int pp_open(char *name) fp=xfopen(name,"r"); + int l = strlen(name); + /* we have to alloc it dynamically to make the name survive another pp_open - it's used in the cross-reference list */ - flist[0].fname = malloc(strlen(name)+1); + flist[0].fname = malloc(l+1); if(!flist[0].fname) { fprintf(stderr,"Oops, no more memory!\n"); exit(1); } - (void)strcpy(flist[0].fname,name); + (void)strncpy(flist[0].fname,name,l+1); flist[0].fline=0; flist[0].bdepth=b_depth(); flist[0].filep=fp; @@ -826,14 +937,17 @@ int icl_open(char *tt) fsp++; + char *namep = s+i; + int len = strlen(namep); + /* we have to alloc it dynamically to make the name survive another pp_open - it's used in the cross-reference list */ - flist[fsp].fname = malloc(strlen(s+i)+1); + flist[fsp].fname = malloc(len+1); if(!flist[fsp].fname) { fprintf(stderr,"Oops, no more memory!\n"); exit(1); } - strcpy(flist[fsp].fname,s+i); + strncpy(flist[fsp].fname,namep, len+1); flist[fsp].fline=0; flist[fsp].bdepth=b_depth(); flist[fsp].flinep=NULL; @@ -843,6 +957,51 @@ int icl_open(char *tt) return(0); } +/* + * parses the current line for double-slash comments, + * handling single- and double-quotes appropriately + * shortens the line if a comment is found, returns + * the new line length. + */ +int double_slash_comments(char *line) { + int p = 0; + int qfl = 0; // if set, contains the current quote char (others are then ignored) + char c; + + while (line[p] != 0) { + c = line[p]; + if (c == qfl) { + // always in quote, c==0 not possible due to loop condition + // found matching quote char, so end of quote + qfl = 0; + } else + if ((c == '\'' || c == '"') && !qfl) { + // not in quote, but finding quote char + qfl = c; + } + if (c == '^' && qfl) { + // xa65 escape character in strings (quoted) + if (line[p+1] != 0) { + // skip the next char in test + p++; + } + } + if (c == '/' && !qfl) { + // found '/' outside quote + if (line[p+1] == '/') { + // gotcha + //printf("shorten at %d: %s\n", p, line); + // shorten line + line[p] = 0; + // return new length + return p; + } + } + p++; + } + return p; +} + int pgetline(char *t) { int c,er=E_OK; @@ -854,15 +1013,25 @@ int pgetline(char *t) filep =flist+fsp; do { - c=fgetline(in_line, MAXLINE, &rlen, flist[fsp].filep); - /* continuation lines */ - tlen = rlen; - while(c=='\n' && tlen && in_line[tlen-1]=='\\') { - c=fgetline(in_line + tlen-1, MAXLINE-tlen, &rlen, flist[fsp].filep); - tlen += rlen-1; + int is_continuation = 0; + tlen = 0; // start of current line in in_line[] + do { + c=fgetline(in_line + tlen, MAXLINE, &rlen, flist[fsp].filep); + + /* check for continuation lines */ + is_continuation = ((c == '\n') && (rlen > 0) && (in_line[tlen + rlen - 1]=='\\')); + if (is_continuation) { + // cut off the continuation character + rlen--; + in_line[tlen + rlen] = 0; } - if(in_line[0]=='#' || in_line[0] == altppchar) - { + rlen = double_slash_comments(in_line + tlen); + + tlen += rlen; + } while (is_continuation); + + if(in_line[0]=='#' || in_line[0] == altppchar) + { if (in_line[1]==' ') { /* cpp comment -- pp_comand doesn't handle this right */ er=pp_cpp(in_line+1); @@ -876,10 +1045,10 @@ int pgetline(char *t) } } } - } else + } else er=1; - if(c==EOF) { + if(c==EOF) { if (loopfl && fsp) { char bletch[MAXLINE]; sprintf(bletch, @@ -896,22 +1065,21 @@ int pgetline(char *t) errout(E_OPENPP); } - /* handle the double-slash comment (like in C++) */ - p = strchr(in_line, '/'); - if (p != NULL) { - if (p[1] == '/') { - *p = 0; /* terminate string */ - } - } - if(!er || loopfl) { in_line[0]='\0'; } er= (er==1) ? E_OK : er ; - if(!er) + if(!er) { +#ifdef DEBUG_REPLACE +// printf("<<<: %s\n", in_line); +#endif er=pp_replace(t,in_line,-1,rlist); +#ifdef DEBUG_REPLACE + printf(">>>: %s\n", t); +#endif + } if(!er && nff) er=E_NEWFILE; @@ -929,7 +1097,7 @@ int pgetline(char *t) /*************************************************************************/ /* this is the most disgusting code I have ever written, but Andre drove me -to it because I can't think of any other F$%Y#*U(%&Y##^#KING way to fix the +to it because I can't think of any other way to fix the last line bug ... a very irritated Cameron */ /* however, it also solved the problem of open #ifdefs not bugging out */ @@ -996,6 +1164,11 @@ int rgetc(FILE *fp) return(c-'\t'?c:' '); } +/** + * Note that the line returned is always zero-terminated, + * the rlen out parameter is just convenience, so that + * a further strlen() can be saved + */ int fgetline(char *t, int len, int *rlen, FILE *fp) { static int c,i; diff --git a/xa/src/xap.h b/xa/src/xap.h index ba5f0ba..156d725 100644 --- a/xa/src/xap.h +++ b/xa/src/xap.h @@ -30,6 +30,9 @@ void pp_end(void); int pgetline(char *t); Datei *pp_getidat(void); +/* needed for .include pseudo opcode */ +int icl_open(char*); + int ga_pp(void); int gm_pp(void); long gm_ppm(void); diff --git a/xa/src/xar.c b/xa/src/xar.c index 25d9aae..2b4f09b 100644 --- a/xa/src/xar.c +++ b/xa/src/xar.c @@ -34,18 +34,22 @@ File *afile = NULL; int rmode = RMODE_RELOC; +/* int r_set(int pc, int afl, int l) { -/*printf("set relocation @$%04x, l=%d, afl=%04x, segment=%d\n",pc, l, afl,segment);*/ if(segment==SEG_TEXT) return rt_set(pc,afl,l,0); if(segment==SEG_DATA) return rd_set(pc,afl,l,0); return 0; } +*/ int u_set(int pc, int afl, int label, int l) { /*printf("set relocation @$%04x, l=%d, afl=%04x, segment=%d, label=%d\n", pc, l, afl,segment, label);*/ - if((afl & A_FMASK) == (SEG_UNDEF<<8)) + if(((afl & A_FMASK) == (SEG_UNDEF<<8)) + || ((afl & A_FMASK) == (SEG_UNDEFZP<<8)) + ) { label = u_label(label); /* set label as undefined */ + } if(segment==SEG_TEXT) return rt_set(pc,afl,l,label); if(segment==SEG_DATA) return rd_set(pc,afl,l,label); return 0; @@ -77,7 +81,9 @@ int rt_set(int pc, int afl, int l, int lab) { /*printf("Warning: byte relocation in word value at PC=$%04x!\n",pc);*/ } if(l==1 && ((afl&A_MASK)==A_ADR)) { - if((afl & A_FMASK) != (SEG_ZERO<<8)) { + if(((afl & A_FMASK) != (SEG_ZERO<<8)) + && ((afl & A_FMASK) != (SEG_UNDEFZP<<8)) + ) { /*printf("afl=%04x\n",afl);*/ errout(W_ADRRELOC); } @@ -147,10 +153,16 @@ int rt_write(FILE *fp, int pc) { } fputc(pc2-pc, fp); pc=pc2; - fputc((afl>>8)&255, fp); - if((afile->rt.rlist[p].afl&A_FMASK)==(SEG_UNDEF<<8)) { + if((afile->rt.rlist[p].afl&A_FMASK)==(SEG_UNDEFZP<<8)) { + fputc( (((afl & ~A_FMASK)>>8)&255)|SEG_UNDEF, fp); + fputc(afile->rt.rlist[p].lab & 255, fp); + fputc((afile->rt.rlist[p].lab>>8) & 255, fp); + } else { + fputc( (afl>>8)&255, fp); + if((afile->rt.rlist[p].afl&A_FMASK)==(SEG_UNDEF<<8)) { fputc(afile->rt.rlist[p].lab & 255, fp); fputc((afile->rt.rlist[p].lab>>8) & 255, fp); + } } if((afl&A_MASK)==A_HIGH) fputc(afl&255,fp); } diff --git a/xa/src/xar.h b/xa/src/xar.h index 01aa887..947c618 100644 --- a/xa/src/xar.h +++ b/xa/src/xar.h @@ -25,7 +25,7 @@ extern File *alloc_file(void); /* jumps to r[td]_set, depending on segment */ -int r_set(int pc, int reloc, int len); +/*int r_set(int pc, int reloc, int len);*/ int u_set(int pc, int reloc, int label, int len); int rt_set(int pc, int reloc, int len, int label); diff --git a/xa/src/xar2.c b/xa/src/xar2.c index fcf87b1..5075917 100644 --- a/xa/src/xar2.c +++ b/xa/src/xar2.c @@ -111,11 +111,17 @@ int rd_write(FILE *fp, int pc) { } fputc(pc2-pc, fp); pc=pc2; - fputc((afl>>8)&255, fp); - if((afile->rd.rlist[p].afl&A_FMASK)==(SEG_UNDEF<<8)) { + if((afile->rd.rlist[p].afl&A_FMASK)==(SEG_UNDEFZP<<8)) { + fputc((((afl & ~A_FMASK)>>8)&255)|SEG_UNDEF, fp); + fputc(afile->rd.rlist[p].lab & 255, fp); + fputc((afile->rd.rlist[p].lab>>8) & 255, fp); + } else { + fputc((afl>>8)&255, fp); + if(((afile->rd.rlist[p].afl&A_FMASK)==(SEG_UNDEF<<8))) { fputc(afile->rd.rlist[p].lab & 255, fp); fputc((afile->rd.rlist[p].lab>>8) & 255, fp); - } + } + } if((afl&A_MASK)==A_HIGH) fputc(afl&255,fp); } p=afile->rd.rlist[p].next; diff --git a/xa/src/xat.c b/xa/src/xat.c index a08539b..4633564 100644 --- a/xa/src/xat.c +++ b/xa/src/xat.c @@ -22,9 +22,12 @@ /* enable this to turn on (copious) optimization output */ /* #define DEBUG_AM */ +#undef LISTING_DEBUG +#undef DEBUG_CONV #include #include +#include #include "xad.h" #include "xah.h" @@ -38,6 +41,7 @@ #include "xao.h" #include "xap.h" #include "xacharset.h" +#include "xalisting.h" int dsb_len = 0; @@ -48,10 +52,24 @@ static void tg_dez(signed char*,int*,int*); static void tg_hex(signed char*,int*,int*); static void tg_oct(signed char*,int*,int*); static void tg_bin(signed char*,int*,int*); +static int t_p2(signed char *t, int *ll, int fl, int *al); +//static void do_listing(signed char *listing, int listing_len, signed char *bincode, int bincode_len); + +void list_setbytes(int number_of_bytes_per_line); /* assembly mnemonics and pseudo-op tokens */ /* ina and dea don't work yet */ -static char *kt[] ={ +/* Note AF 20110624: added some ca65 compatibility pseudo opcodes, + * many are still missing (and will most likely never by supported in this + * code base). Potential candidates are .hibytes, .lobytes, .asciiz, + * .addr, .charmap, .dbyt, .faraddr, .bankbytes, .segment (at least for the known ones) + * .incbin is similar to our .bin, but with parameters reversed (argh...) + * I like the .popseg/.pushseg pair; + * .global/.globalzp is equivalent to forward-defining a label in the global block + * .export/.exportzp could be implemented with a commandline switch to NOT export + * global labels, where .exported labels would still be exported in an o65 file. + */ +char *kt[] ={ /* 1 2 3 4 5 6 7 8 9 10 */ "adc","and","asl","bbr","bbs","bcc","bcs","beq","bit","bmi", "bne","bpl","bra","brk","bvc","bvs","brl","clc","cld","cli", @@ -75,17 +93,30 @@ static char *kt[] ={ ".byt",".word",".asc",".dsb", ".(", ".)", "*=", ".text",".data",".bss", ".zero",".fopt", ".byte", ".end", ".list", ".xlist", ".dupb", ".blkb", ".db", ".dw", - ".align",".block", ".bend",".al",".as",".xl",".xs", ".bin", ".aasc" + ".align",".block", ".bend",".al",".as",".xl",".xs", ".bin", ".aasc", ".code", + ".include", ".import", ".importzp", ".proc", ".endproc", + ".zeropage", ".org", ".reloc", ".listbytes", + ".scope", ".endscope" }; +/* arithmetic operators (purely for listing, parsing is done programmatically */ +char *arith_ops[] = { + "", "+", "-", + "*", "/", + ">>", "<<", + "<", ">", "=" + "<=", ">=", "<>", + "&", "^", "|", + "&&", "||" +}; + +/* length of arithmetic operators indexed by operator number */ static int lp[]= { 0,1,1,1,1,2,2,1,1,1,2,2,2,1,1,1,2,2 }; /* index into token array for pseudo-ops */ /* last valid mnemonic */ #define Lastbef 93 -/* last valid token+1 */ -#define Anzkey 123 #define Kbyt Lastbef+1 #define Kword Lastbef+2 @@ -119,8 +150,31 @@ static int lp[]= { 0,1,1,1,1,2,2,1,1,1,2,2,2,1,1,1,2,2 }; #define Kbin Lastbef+28 #define Kaasc Lastbef+29 -#define Kreloc Anzkey /* *= (relocation mode) */ -#define Ksegment Anzkey+1 +#define Kcode Lastbef+30 /* gets remapped to Ktext */ + +/* 93 + 30 -> 123 */ + +#define Kinclude Lastbef+31 +#define Kimport Lastbef+32 +#define Kimportzp Lastbef+33 +#define Kproc Lastbef+34 /* mapped to Kopen */ +/* 93 + 35 -> 128 */ +#define Kendproc Lastbef+35 /* mapped to Kclose */ +#define Kzeropage Lastbef+36 /* mapped to Kzero */ +#define Korg Lastbef+37 /* mapped to Kpcdef - with parameter equivalent to "*=$abcd" */ +#define Krelocx Lastbef+38 /* mapped to Kpcdef - without parameter equivalent to "*=" */ +#define Klistbytes (Lastbef+39-256) +#define Kscope (Lastbef+40) /* mapped to Kopen */ +#define Kendscope (Lastbef+41) /* mapped to Kclose */ + +/* last valid token+1 */ +#define Anzkey Lastbef+42 /* define last valid token number; last define above plus one */ + + +#define Kreloc (Anzkey-256) /* *= (relocation mode) */ +#define Ksegment (Anzkey+1-256) /* this actually now is above 127, which might be a problem as char is signed ... */ + +int number_of_valid_tokens = Anzkey; /* array used for hashing tokens (26 entries, a-z) */ @@ -319,16 +373,21 @@ static int le[] ={ 1,2,2,2,2,2,2,2,3,3,3,2,3,3,3,2, static int opt[] ={ -1,-1,-1,-1,-1,-1,-1,-1,1,2,3,-1,4,5,-1,-1, /*new*/ -1,8,9,-1,-1,-1,-1,-1 }; /* abs -> zp */ +/*********************************************************************************************/ /* pass 1 */ int t_p1(signed char *s, signed char *t, int *ll, int *al) { static int er,l,n,v,nk,na1,na2,bl,am,sy,i,label,byte; /*,j,v2 ;*/ int afl = 0; + int tlen; /* token listing length, to adjust length that is returned */ + int inp; /* input pointer in t[] */ + unsigned char cast; /* notes and typical conventions ... er = error code am = addressing mode in use */ + cast='\0'; bl=0; *al = 0; @@ -338,22 +397,66 @@ int t_p1(signed char *s, signed char *t, int *ll, int *al) #ifdef DEBUG_AM fprintf(stderr, "- p1 %d starting -\n", pc[segment]); #endif - er=t_conv(s,t,&l,pc[segment],&nk,&na1,&na2,0,&byte); - /* leaving our token sequence in t */ + + /* As the t_p1 code below always works through the tokens + * from t_conv in such a way that it always produces a shorter + * result, the conversion below takes place "in place". + * This, however, means that the original token sequence, which + * would be useful for some assembler listing, is overwritten. + * While the original assumption was ok for a constrained + * environment like the Atari ST, this is no longer true. + * Converting the code below to have separate input and output + * areas would be error-prone, so we do some copy-magic here + * instead...*/ + /* we keep three bytes buffer for "T_LISTING" and the length of the + * token list + */ + t[0]=T_LISTING; + er=t_conv(s,t+6,&l,pc[segment],&nk,&na1,&na2,0,&byte); + tlen = l+6; + t[1]=tlen&255; + t[2]=(tlen>>8)&255; + t[3]=segment; + t[4]=pc[segment]&255; + t[5]=(pc[segment]>>8)&255; + /* now we have to duplicate the token sequence from the T_LISTING buffer + * to the end of "t", so we can then in-place convert it + * below. Non-overlapping, size is known in advance, so + * using memcpy is fine here + */ + inp = 0; + /* discard label definitions before copying the buffer */ + while (inp1) && (t[0]1) && (t[inp]base[SEG_TEXT] = pc[SEG_TEXT] = romaddr + h_length(); romable=1; } - if(!er) { @@ -362,34 +465,66 @@ fprintf(stderr, "- p1 %d starting -\n", pc[segment]); * pseudo-op dispatch (except .byt, .asc) * */ - n=t[0]; + // fix sign + n=t[0]; // & 0xff; + /* TODO: make that a big switch statement... */ /* maybe later. Cameron */ if(n==Kend || n==Klist || n==Kxlist) { *ll = 0; /* ignore */ } else - + if(n==Kinclude) { + *ll = 0; /* no output length */ + i=1; + if(t[i]=='\"') { + int k,j=0; + char binfname[255]; + i++; + k=t[i]+i+1; + i++; + while(i 255) + er = E_NOMEM; /* buffer overflow */ + } + binfname[j] = '\0'; + er=icl_open(binfname); + } else { + er=E_SYNTAX; + } + } else if(n==Kfopt) { if(romable==1) er=E_ROMOPT; t[0] = Kbyt; set_fopt(l,t,nk+1-na1+na2); *ll = 0; } else + if(n==Klistbytes) { + int p = 0; + if(!(er=a_term(t+1,&p,&l,pc[segment],&afl,&label,0))) { + er=E_OKDEF; + } + *ll = 3; + t[0] = Klistbytes; + t[1] = p & 0xff; + t[2] = (p >> 8) & 0xff; + //printf("Klistbytes p1: er=%d, l=%d\n", er, l); + } else if(n==Kpcdef) { int tmp; if(!(er=a_term(t+1,&tmp /*&pc[SEG_ABS]*/,&l,pc[segment],&afl,&label,0))) { i=1; - wval(i,tmp /*pc[SEG_ABS]*/); + wval(i,tmp /*pc[SEG_ABS]*/, 0); /* writes T_VALUE, 3 bytes value, plus one byte */ t[i++]=T_END; - *ll=6; + *ll=7; er=E_OKDEF; /*printf("set pc=%04x, oldsegment=%d, pc[segm]=%04x, ", pc[SEG_ABS], segment, pc[segment]); -printf(" wrote %02x %02x %02x %02x %02x %02x\n", - t[0],t[1],t[2],t[3],t[4],t[5]);*/ +printf(" wrote %02x %02x %02x %02x %02x %02x, %02x, %02x\n", + t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7]);*/ if(segment==SEG_TEXT) { pc[SEG_ABS] = tmp; r_mode(RMODE_ABS); @@ -407,7 +542,7 @@ printf(" wrote %02x %02x %02x %02x %02x %02x\n", segment, pc[segment], pc[SEG_ABS], pc[SEG_TEXT]);*/ t[0]=Kreloc; i=1; - wval(i,pc[SEG_TEXT]); + wval(i,pc[SEG_TEXT], 0); t[i++]=T_END; *ll=6; er=E_OKDEF; @@ -526,7 +661,6 @@ printf(" wrote %02x %02x %02x %02x %02x %02x\n", char binfnam[255]; int offset; int length; - int fstart; i = 1; j = 0; @@ -562,7 +696,7 @@ printf(" wrote %02x %02x %02x %02x %02x %02x\n", if (!er) { int k; - fstart = i; + //fstart = i; if(t[i]=='\"') { i++; k=t[i]+i+1; @@ -584,7 +718,7 @@ printf(" wrote %02x %02x %02x %02x %02x %02x\n", } /* three arguments only please */ - if (!er && t[i] != T_END) { + if (!er && t[i] != T_END && t[i] != T_COMMENT) { er = E_SYNTAX; } @@ -635,10 +769,10 @@ printf(" wrote %02x %02x %02x %02x %02x %02x\n", t[0]=Kdsb; i=1; bl=tmp=(tmp - (pc[segment] & (tmp-1))) & (tmp-1); - wval(i,tmp); + wval(i,tmp, 0); t[i++]=','; tmp2= 0xea; - wval(i,tmp2); /* nop opcode */ + wval(i,tmp2, 0); /* nop opcode */ t[i++]=T_END; *ll=9; er=E_OKDEF; @@ -653,11 +787,13 @@ printf(" wrote %02x %02x %02x %02x %02x %02x\n", er=E_ILLSEGMENT; } } else -/* optimization okay on pass 1: use 0 for fl */ + /* optimization okay on pass 1: use 0 for fl */ { #ifdef DEBUG_AM fprintf(stderr, "E_OK ... t_p2 xat.c\n"); #endif + /* this actually calls pass2 on the current tokenization stream, + * but without including the Klisting token listing */ er=t_p2(t,ll,(0 | byte), al); } @@ -665,11 +801,11 @@ fprintf(stderr, "E_OK ... t_p2 xat.c\n"); if(er==E_NODEF) { -/* - * no label was found from t_conv! - * try to figure out most likely length - * - */ + /* + * no label was found from t_conv! + * try to figure out most likely length + * + */ #ifdef DEBUG_AM fprintf(stderr, "E_NODEF pass1 xat.c\n"); @@ -685,20 +821,36 @@ fprintf(stderr, "E_NODEF pass1 xat.c\n"); if(n>=0 && n<=Lastbef) { - if(t[1]==T_END) + int inp = 1; /* input pointer */ + + if(t[inp]==T_END || t[inp]==T_COMMENT) { sy=0; /* implied */ + inp++; } else - if(t[1]=='#') + if(t[inp]=='#') { sy=1+nk; /* immediate */ + inp++; } else - if(t[1]=='(') + if(t[inp]=='(') { sy=7+nk; /* computed */ - } else + inp++; + } else { sy=4+nk; /* absolute or zero page */ + } + /* this actually finds the cast for all addressing modes, + but t_conv() only puts it there for immediate (#) or absolute/ + absolute indexed addressing modes */ + if (t[inp] == T_CAST) { + inp++; + cast = t[inp]; + inp++; + } + + /* length counter set to maximum length + 1 */ bl=Maxbyt+1; @@ -737,20 +889,20 @@ fprintf(stderr, "E_NODEF pass1 xat.c\n"); /* optimize operand length for 24-bit quantities */ /* look at cast byte from t_conv */ - if (t[l-1]!='@' && t[l-1] != '!') + if (cast!='@' && cast!= '!') { if(bl && !er && opt[am]>=0 && am>16) /* <<< NOTE! */ if(ct[n][opt[am]]>=0) am=opt[am]; } /* if ` is declared, force further optimization */ - if (t[l-1]=='`') { + if (cast=='`') { if (opt[am]<0 || ct[n][opt[am]]<0) errout(E_ADRESS); am=opt[am]; } /* if ! is declared, force to 16-bit quantity */ - if (t[l-1]=='!' && am>16 && opt[am]>=0 && bl) { + if (cast=='!' && am>16 && opt[am]>=0 && bl) { am=opt[am]; } @@ -774,6 +926,48 @@ fprintf(stderr, "E_NODEF pass1 xat.c\n"); /* .byt, .asc, .word, .dsb, .fopt pseudo-op dispatch */ } else + if(n==Kimportzp) { + int i; + *ll=0; /* no output */ + bl = 0; /* no output length */ + /* import labels; next follow a comma-separated list of labels that are + imported. Tokenizer has already created label entries, we only need to + set the flags appropriately */ + i=1; +/*printf("Kimport: t[i]=%d\n",t[i]);*/ + while(t[i]==T_LABEL) { + int n = (t[i+1] & 255) | (t[i+2] << 8); /* label number */ +/*printf("lg_import: %d\n",n);*/ + lg_importzp(n); + i+=3; + while (t[i]==' ') i++; + if (t[i]!=',') break; + i++; + while (t[i]==' ') i++; + } + er=E_NOLINE; + } else + if(n==Kimport) { + int i; + *ll=0; /* no output */ + bl = 0; /* no output length */ + /* import labels; next follow a comma-separated list of labels that are + imported. Tokenizer has already created label entries, we only need to + set the flags appropriately */ + i=1; +/*printf("Kimport: t[i]=%d\n",t[i]);*/ + while(t[i]==T_LABEL) { + int n = (t[i+1] & 255) | (t[i+2] << 8); /* label number */ +/*printf("lg_import: %d\n",n);*/ + lg_import(n); + i+=3; + while (t[i]==' ') i++; + if (t[i]!=',') break; + i++; + while (t[i]==' ') i++; + } + er=E_NOLINE; + } else if(n==Kbyt || n==Kasc || n==Kaasc) { #ifdef DEBUG_AM @@ -817,29 +1011,127 @@ fprintf(stderr, "guessing instruction length is %d\n", bl); if(segment==SEG_TEXT) pc[SEG_ABS]+=bl; if(segment==SEG_ABS) pc[SEG_TEXT]+=bl; + /* adjust length by token listing buffer length */ +#ifdef DEBUG_CONV + printf("converted: (er=%d, t=%p, ll=%d, tlen=%d):",er, t, *ll, tlen); + for(i=0;i<*ll;i++) + printf("%02x,",t[i] & 0xff); + printf("\n"); + printf("adjusted len=%d\n", *ll+tlen); +#endif + + *ll = *ll + tlen; return(er); } -/*t_pass 2*/ +/*********************************************************************************************/ +/* t_pass 2 + * + * *t is the token list as given from pass1 + * *ll is the returned length of bytes (doubles as + * input for whether OK or OKDEF status from pass1) + * fl defines if we allow zeropage optimization + * + * Conversion takes place "in place" in the *t array. + */ + +/** + * function called from the main loop, where "only" the + * undefined labels have to be resolved and the affected + * opcodes are assembled, the rest is passed through from + * pass1 (pass-through is done in t_p2, when *ll<0) + * As this is not called from p1, assume that we do not + * do length optimization + * + * *t is the input token list + * *ll is the input length of the token list, + * and the output of how many bytes of the buffer are to be taken + * into the file; note that for .dsb and .bin, this does NOT match + * the length in the internal data structures! + */ +int t_p2_l(signed char *t, int *ll, int *al) +{ + int er = E_OK; + int l = *ll; + + if (l < 0) l = -l; + +#if 0 + { + printf("t_p2_l (ll=%d, t=%p):", *ll, t); + for(int i=0;i l) + { + // that is corrupt data and should not happen + list_flush(); + printf("corrupt: t_p2_l (l=%d, tlen=%d, ll=%d, t=%p):", l, tlen, *ll, t); + for(int i=0;i0 bei E_OKDEF */ + if(*ll<0) /* <0 when E_OK, >0 when E_OKDEF */ { *ll=-*ll; bl=*ll; er=E_OK; + } else { n=t[0]; @@ -855,7 +1147,8 @@ int t_p2(signed char *t, int *ll, int fl, int *al) v2=v; } else { if( (!(er=l_get(n,&v2, &afl))) - && ((afl & A_FMASK)!=(SEG_UNDEF<<8)) ) + && ((afl & A_FMASK)!=(SEG_UNDEF<<8)) + && ((afl & A_FMASK)!=(SEG_UNDEFZP<<8)) ) { if(t[3]=='+') { @@ -910,7 +1203,7 @@ int t_p2(signed char *t, int *ll, int fl, int *al) { i=1; j=0; - while(!er && t[i]!=T_END) + while(!er && t[i]!=T_END && t[i] != T_COMMENT) { if(!(er=a_term(t+i,&v,&l,pc[segment],&afl,&label,1))) { @@ -921,7 +1214,7 @@ int t_p2(signed char *t, int *ll, int fl, int *al) t[j++]=(v>>8)&255; i+=l; - if(t[i]!=T_END && t[i]!=',') + if(t[i]!=T_END && t[i] != T_COMMENT && t[i]!=',') er=E_SYNTAX; else if(t[i]==',') @@ -1003,7 +1296,7 @@ int t_p2(signed char *t, int *ll, int fl, int *al) } /* three arguments only please */ - if (!er && t[i] != T_END) { + if (!er && t[i] != T_END && t[i] != T_COMMENT) { er = E_SYNTAX; } @@ -1052,7 +1345,7 @@ int t_p2(signed char *t, int *ll, int fl, int *al) } else if(n==Kasc || n==Kbyt || n==Kaasc) { i=1; j=0; - while(!er && t[i]!=T_END) + while(!er && t[i]!=T_END && t[i] != T_COMMENT) { if(t[i]=='\"') { @@ -1076,7 +1369,7 @@ int t_p2(signed char *t, int *ll, int fl, int *al) } } } - if(t[i]!=T_END && t[i]!=',') + if(t[i]!=T_END && t[i] != T_COMMENT && t[i]!=',') er=E_SYNTAX; else if(t[i]==',') @@ -1126,6 +1419,14 @@ int t_p2(signed char *t, int *ll, int fl, int *al) pc[segment] = npc; /*printf("Kreloc: newsegment=%d, pc[seg]=%04x\n", segment, pc[segment]);*/ } else + if(n==Klistbytes) { + int nbytes = (t[1] & 0xff) + (t[2] << 8); + //printf("Klistbytes --> er=%d, nbytes=%d\n", er, nbytes); + list_setbytes(nbytes); + l = 2; + *ll=0; + bl =0; + } else if(n==Ksegment) { segment = t[1]; *ll=0; @@ -1172,46 +1473,56 @@ fprintf(stderr, "Kdsb E_DSB %i\n", j); } dsb_len = 0; } else - if(n<=Lastbef) + if(n>=0 && n<=Lastbef) { - if((c=t[1])=='#') + int inp = 1; /* input pointer */ + signed char cast = '\0'; /* cast value */ + + c = t[inp]; + + if(c=='#') { - i=2; + inp++; + if (t[inp] == T_CAST) { + inp++; + cast = t[inp]; + inp++; + } sy=1; - if(!(er=a_term(t+i,vv,&l,pc[segment],&afl,&label,1))) + if(!(er=a_term(t+inp,vv,&l,pc[segment],&afl,&label,1))) { /* if(1) printf("a_term returns afl=%04x\n",afl); */ rlt[0] = afl; lab[0] = label; - i+=l; - if(t[i]!=T_END) + inp+=l; + if(t[inp]!=T_END && t[inp] != T_COMMENT) { - if(t[i]!=',') + if(t[inp]!=',') er=E_SYNTAX; else { - i++; + inp++; sy++; - if(!(er=a_term(t+i,vv+1,&l,pc[segment],&afl,&label,1))) + if(!(er=a_term(t+inp,vv+1,&l,pc[segment],&afl,&label,1))) { rlt[1] = afl; lab[1] = label; - i+=l; - if(t[i]!=T_END) + inp+=l; + if(t[inp]!=T_END && t[inp] != T_COMMENT) { - if(t[i]!=',') + if(t[inp]!=',') er=E_SYNTAX; else { - i++; + inp++; sy++; - if(!(er=a_term(t+i,vv+2,&l,pc[segment],&afl,&label,1))) + if(!(er=a_term(t+inp,vv+2,&l,pc[segment],&afl,&label,1))) { rlt[2] = afl; lab[2] = label; - i+=l; - if(t[i]!=T_END) + inp+=l; + if(t[inp]!=T_END && t[inp]!=T_COMMENT) er=E_SYNTAX; } } @@ -1221,38 +1532,48 @@ fprintf(stderr, "Kdsb E_DSB %i\n", j); } } } else - if(c==T_END) + if(c==T_END || c==T_COMMENT) { sy=0; } else if(c=='(') { + inp++; + if (t[inp] == T_CAST) { + inp++; + cast = t[inp]; + inp++; + } sy=7; - if(!(er=a_term(t+2,vv,&l,pc[segment],&afl,&label,1))) + if(!(er=a_term(t+inp,vv,&l,pc[segment],&afl,&label,1))) { + inp += l; rlt[0] = afl; lab[0] = label; - if(t[2+l]!=T_END) + if(t[inp]!=T_END && t[inp]!=T_COMMENT) { - if(t[2+l]==',') + if(t[inp]==',') { - if (tolower(t[3+l])=='x') + inp++; + if (tolower(t[inp])=='x') sy=8; else sy=13; } else - if(t[2+l]==')') + if(t[inp]==')') { - if(t[3+l]==',') + inp++; + if(t[inp]==',') { - if(tolower(t[4+l])=='y') + inp++; + if(tolower(t[inp])=='y') sy=9; else er=E_SYNTAX; } else - if(t[3+l]!=T_END) + if(t[inp]!=T_END && t[inp]!=T_COMMENT) er=E_SYNTAX; } } else @@ -1261,24 +1582,33 @@ fprintf(stderr, "Kdsb E_DSB %i\n", j); } else if(c=='[') { + inp++; + if (t[inp] == T_CAST) { + inp++; + cast = t[inp]; + inp++; + } sy=10; - if(!(er=a_term(t+2,vv,&l,pc[segment],&afl,&label,1))) + if(!(er=a_term(t+inp,vv,&l,pc[segment],&afl,&label,1))) { + inp += l; rlt[0] = afl; lab[0] = label; - if(t[2+l]!=T_END) + if(t[inp]!=T_END && t[inp]!=T_COMMENT) { - if(t[2+l]==']') + if(t[inp]==']') { - if(t[3+l]==',') + inp++; + if(t[inp]==',') { - if(tolower(t[4+l])=='y') + inp++; + if(tolower(t[inp])=='y') sy=11; else er=E_SYNTAX; } else - if(t[3+l]!=T_END) + if(t[inp]!=T_END && t[inp]!=T_COMMENT) er=E_SYNTAX; } } else @@ -1286,19 +1616,26 @@ fprintf(stderr, "Kdsb E_DSB %i\n", j); } } else { + if (t[inp] == T_CAST) { + inp++; + cast = t[inp]; + inp++; + } sy=4; - if(!(er=a_term(t+1,vv,&l,pc[segment],&afl,&label,1))) + if(!(er=a_term(t+inp,vv,&l,pc[segment],&afl,&label,1))) { + inp += l; rlt[0] = afl; lab[0] = label; - if(t[1+l]!=T_END) + if(t[inp]!=T_END && t[inp]!=T_COMMENT) { - if(t[1+l]==',') + if(t[inp]==',') { - if(tolower(t[2+l])=='y') + inp++; + if(tolower(t[inp])=='y') sy=6; else - if(tolower(t[2+l])=='s') + if(tolower(t[inp])=='s') sy=12; else sy=5; @@ -1343,7 +1680,7 @@ fprintf(stderr, "Kdsb E_DSB %i\n", j); /* only do optimization if we're being called in pass 1 -- never pass 2 */ /* look at cast byte */ - if (t[*ll-1]!='@') + if (cast!='@') { #ifdef DEBUG_AM fprintf(stderr, @@ -1354,9 +1691,12 @@ fprintf(stderr, /* terrible KLUDGE!!!! OH NOES!!!1! due to the way this is constructed, you must absolutely always specify @ to get an absolute long or it will absolutely always be optimized down */ - - if(bl && am>16 && - !er && !(vv[0]&0xff0000) && opt[am]>=0) +/* now also checks for negative overflow, resp. not-overflow */ + if(bl + && am>16 + && !er + && (((vv[0]&0xff8000)==0xff8000) || !(vv[0]&0xff0000)) + && opt[am]>=0) if(ct[n][opt[am]]>=0) am=opt[am]; #ifdef DEBUG_AM @@ -1364,10 +1704,10 @@ fprintf(stderr, "aftaa1: pc= %d, am = %d and vv[0] = %d, optimize = %d, bitmask = %d\n", pc[segment], am, vv[0], fl, (vv[0]&0xffff00)); #endif - if(t[*ll-1]!='!') { + if(cast!='!') { if(bl && !er && !(vv[0]&0xffff00) && opt[am]>=0) { if(ct[n][opt[am]]>=0) { - if (!fl || t[*ll-1]=='`') { + if (!fl || cast=='`') { am=opt[am]; } else { errout(W_FORLAB); @@ -1409,8 +1749,10 @@ fprintf(stderr, "byte length is now %d\n", bl); er=E_CMOS; } else { n65816++; - if(!w65816) + if(!w65816) { +fprintf(stderr,"n=%d, am=%d\n", n, am); er=E_65816; + } } } if(am!=0) @@ -1501,10 +1843,11 @@ fprintf(stderr, "address mode: %i address: %i\n", am, vv[0]); er=E_SYNTAX; } } - + } else er=E_SYNTAX; } + #ifdef DEBUG_AM fprintf(stderr, "-- endof P2\n"); #endif @@ -1515,6 +1858,11 @@ fprintf(stderr, "-- endof P2\n"); return(er); } +/*********************************************************************************************/ +/* helper function for the preprocessor, to compute an arithmetic value + * (e.g. for #if or #print). + * First tokenizes it, then calculates the value + */ int b_term(char *s, int *v, int *l, int pc) { static signed char t[MAXLINE]; @@ -1528,17 +1876,37 @@ int b_term(char *s, int *v, int *l, int pc) return(er); } -/* translate a string into a first-pass sequence of tokens */ +/*********************************************************************************************/ +/* translate a string into a first-pass sequence of tokens; + * Take the text from *s (stopping at \0 or ';'), tokenize it + * and write the result to *t, returning the length of the + * token sequence in *l + * + * Input params: + * s source input line + * t output token sequence buffer + * l return length of output token sequence here + * pc the current PC to set address labels to that value + * nk return number of comma in the parameters + * na1 asc text count returned + * na2 total byte count in asc texts returned + * af arithmetic flag: 0=do label definitions, parse opcodes and params; + * 1=only tokenize parameters, for b_term() call from the preprocessor + * for arithmetic conditions + * bytep ??? + */ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, - int *na1, int *na2, int af, int *bytep) /* Pass1 von s nach t */ -/* tr. pass1, from s to t */ + int *na1, int *na2, int af, int *bytep) { static int v,f; static int operand,o; int fl,afl; - int p,q,ud,n,ll,mk,er; - int m, uz, byte; - static unsigned char cast; + int p,q,ll,mk,er; + int ud; /* counts undefined labels */ + int n; /* label number to be passed between l_def (definition) and l_set (set the value) */ + int byte; + int uz; /* unused at the moment */ + /*static unsigned char cast;*/ /* ich verstehe deutsch, aber verstehen andere leute nicht; so, werde ich diese bemerkungen uebersetzen ... cameron */ @@ -1558,16 +1926,37 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, fl=0; /* 1 = pass text thru */ afl=0; /* pointer flag for label */ - while(s[p]==' ') p++; + while(isspace(s[p])) p++; n=T_END; - cast='\0'; + /*cast='\0';*/ if(!af) { while(s[p]!='\0' && s[p]!=';') { + //printf("CONV: %s\n", s); + if (s[p] == ':') { + // this is a ca65 unnamed label + if ((er = l_def((char*)s+p, &ll, &n, &f))) + break; + l_set(n,pc,segment); /* set as address value */ + t[q++]=T_DEFINE; + t[q++]=n&255; + t[q++]=(n>>8)&255; + n=0; + + p+=ll; + + while(isspace(s[p])) p++; + + // end of line + if (s[p] == 0 || s[p] == ';') { + break; + } + } + /* is keyword? */ if(!(er=t_keyword(s+p,&ll,&n))) break; @@ -1576,16 +1965,18 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, if(er && er!=E_NOKEY) break; - /* if so, try to understand as label */ + // if so, try to understand as label + // it returns the label number in n if((er=l_def((char*)s+p,&ll,&n,&f))) break; p+=ll; - while(s[p]==' ') p++; + while(isspace(s[p])) p++; if(s[p]=='=') { + /*printf("Found = @%s\n",s+p);*/ t[q++]=T_OP; t[q++]=n&255; t[q++]=(n>>8)&255; @@ -1594,6 +1985,17 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, ll=n=0; break; } else + if(s[p]==':' && s[p+1]=='=') /* support := label assignments (ca65 compatibility) */ + { + /*printf("Found := @%s\n", s+p);*/ + t[q++]=T_OP; + t[q++]=n&255; + t[q++]=(n>>8)&255; + t[q++]='='; + p+=2; + ll=n=0; + break; + } else if(f && s[p]!='\0' && s[p+1]=='=') { t[q++]=T_OP; @@ -1609,15 +2011,22 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, p++; while(s[p]==' ') p++; l_set(n,pc,segment); /* set as address value */ + t[q++]=T_DEFINE; + t[q++]=n&255; + t[q++]=(n>>8)&255; n=0; + } else { /* label ... syntax */ l_set(n,pc,segment); /* set as address value */ - n=0; + t[q++]=T_DEFINE; + t[q++]=n&255; + t[q++]=(n>>8)&255; + n=0; } } - if((n & 0xff) <=Lastbef) + if(n>=0 && n<=Lastbef) mk=1; /* 1= nur 1 Komma erlaubt *//* = only 1 comma ok */ } @@ -1674,10 +2083,14 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, if(operand) { /* are we forcing the operand into a particular - addressing mode? !, @, ` operators */ - if(s[p]=='!' || s[p]=='@' || s[p]=='`') + addressing mode? !, @, ` operators + Note these are not available in ca65, but we only + switch off "@" which are used for cheap local labels*/ + if(s[p]=='!' || (s[p]=='@' && !ca65) || s[p]=='`') { - cast=s[p]; + t[q++]=T_CAST; + t[q++]=s[p]; + /*cast=s[p];*/ operand= -operand+1; p++; } else @@ -1691,12 +2104,30 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, { t[q++]=s[p++]; } else - /* maybe it's a label */ - if(isalpha(s[p]) || s[p]=='_') + /* maybe it's a label + Note that for ca65 cheap local labels, we check for "@" */ + if(isalpha(s[p]) || s[p]=='_' || ((s[p]==':' || s[p]=='@') && ca65)) { - m=n; + + int p2 = 0; + if (n == (Klistbytes & 0xff)) { + // check for "unlimited" + // Note: this could be done by a more general "constants" handling, + // where in appropriate places (like the one here), constants are + // replaced by a pointer to a predefined constants info, e.g. using + // a T_CONSTANT. Which would also fix the listing of this constant + // (which is currently listed as "0") + static char *unlimited = "unlimited"; + while (s[p+p2] != 0 && unlimited[p2] != 0 && s[p+p2] == unlimited[p2]) p2++; + } + if (p2 == 9) { // length of "unlimited" + er = E_OK; + // found constant + wval(q, 0, 'd'); + p += p2; + } else { + //m=n; er=l_search((char*)s+p,&ll,&n,&v,&afl); - /* if(m==Kglobl || m==Kextzero) { if(er==E_NODEF) { @@ -1715,8 +2146,13 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, t[q++]=afl & 255; t[q++]=v & 255; t[q++]=(v>>8) & 255; + t[q++]=n & 255; /* cheap fix for listing */ + t[q++]=(n>>8) & 255; /* why is the label already resolved in t_conv? */ } else { - wval(q,v); + t[q++]=T_LABEL; + t[q++]=n & 255; + t[q++]=(n>>8) & 255; + /*wval(q,v, 0);*/ } } else if(er==E_NODEF) @@ -1730,36 +2166,57 @@ fprintf(stderr, "could not find %s\n", (char *)s+p); /* if(afl==SEG_ZEROUNDEF) uz++; */ - ud++; + ud++; // number of undefined labels er=E_OK; } p+=ll; + } } else - if(s[p]<='9' && s[p]>='0') + if(s[p]<='9' && (s[p]>'0' || (s[p] == '0' && !ctypes))) { tg_dez(s+p,&ll,&v); p+=ll; - wval(q,v); + wval(q,v, 'd'); } else - - /* handle encodings: hex, binary, octal, quoted strings */ + /* handle encodings: hex, binary, octal, quoted strings */ switch(s[p]) { + case '0': + // only gets here when "ctypes" is set, and starts with 0 + // we here check for the C stype "0xHEX" and "0OCTAL" encodings + if ('x' == tolower(s[p+1])) { + // c-style hex + tg_hex(s+p+2, &ll, &v); + p+=2+ll; + wval(q, v, '$'); + } else + if (isdigit(s[p+1])) { + // c-style octal if digit follows + tg_oct(s+p+1,&ll,&v); + p+=1+ll; + wval(q,v, '&'); + } else { + // else use decimal (0) + tg_dez(s+p,&ll,&v); + p+=ll; + wval(q,v, 'd'); + } + break; case '$': tg_hex(s+p+1,&ll,&v); p+=1+ll; - wval(q,v); + wval(q,v, '$'); break; case '%': tg_bin(s+p+1,&ll,&v); p+=1+ll; - wval(q,v); + wval(q,v, '%'); break; case '&': tg_oct(s+p+1,&ll,&v); p+=1+ll; - wval(q,v); + wval(q,v, '&'); break; case '\'': case '\"': @@ -1881,9 +2338,6 @@ fprintf(stderr, "could not find %s\n", (char *)s+p); { t[q++]=o; p+=lp[o]; -#if(0) - uz++; /* disable 8-bit detection */ -#endif } operand= -operand+1; } @@ -1892,6 +2346,7 @@ fprintf(stderr, "could not find %s\n", (char *)s+p); } } } +//printf("er=%d, ud=%d\n", er, ud); if(!er) { /* @@ -1905,33 +2360,62 @@ fprintf(stderr, "could not find %s\n", (char *)s+p); } */ byte = 0; - t[q++]=T_END; if(ud > 0) { er=E_NODEF; byte = 1; } } + + if (s[p] == ';') { + /* handle comments */ + /* first find out how long */ + int i; + for (i = p+1; s[i] != '\0'; i++); + i = i - p; /* actual length of the comment, including zero-byte terminator */ + /*if (i >= 1) {*/ + /* there actually is a comment */ + t[q++] = T_COMMENT; + t[q++] = i&255; + t[q++] = (i>>8)&255; + memcpy(t+q, s+p+1, i); /* also copy zero terminator, used in listing */ + q += i; + /*}*/ + } + t[q++]=T_END; /* FIXME: this is an unholy union of two "!" implementations :-( */ - t[q++]='\0'; - t[q++]=cast; + /* FIXME FIXME FIXME ... + if (operand==1) { + t[q++]='\0'; + t[q++]=cast; + } + */ *l=q; if(bytep) *bytep=byte; return(er); } +/********************************************************************************************* + * identifies a keyword in s, if it is found, starting with s[0] + * A keyword is either a mnemonic, or a pseudo-opcode + */ static int t_keyword(signed char *s, int *l, int *n) { - int i = 0, j = 0, hash; + int i = 0; // index into keywords + int j = 0; + int hash; + // keywords either start with a character, a "." or "*" if(!isalpha(s[0]) && s[0]!='.' && s[0]!='*' ) return(E_NOKEY); + // if first char is a character, use it as hash... if(isalpha(s[0])) hash=tolower(s[0])-'a'; else hash=26; + // check for "*=" if(s[0]=='*') { j=1; while(s[j] && isspace(s[j])) j++; @@ -1940,9 +2424,14 @@ static int t_keyword(signed char *s, int *l, int *n) j++; } } + + // no keyword yet found? if(!i) { + // get sub-table from hash code, and compare with table content + // (temporarily) redefine i as start index in opcode table, and hash as end index i=ktp[hash]; hash=ktp[hash+1]; + // check all entries in opcode table from start to end for that hash code while(i-