diff --git a/xa/man/xa.1 b/xa/man/xa.1 index 8dc5e88..e1a8e66 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 @@ -67,9 +70,18 @@ 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 "="), and also the "cheap local labels" using +the "@" character. This, however, disables the "@" 24-bit enforcement. +.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. @@ -339,7 +351,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 @@ -538,6 +550,9 @@ 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". +.TP .B .align value Aligns the current segment to a byte boundary (2, 4 or 256) as specified by .B diff --git a/xa/src/Makefile b/xa/src/Makefile index dc15ea6..08fb114 100644 --- a/xa/src/Makefile +++ b/xa/src/Makefile @@ -1,4 +1,4 @@ -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 #CFLAGS=-W -Wall -pedantic -ansi #-g #CFLAGS=-W -Wall -ansi -O2 diff --git a/xa/src/xa.c b/xa/src/xa.c index 3a54e6c..836c885 100644 --- a/xa/src/xa.c +++ b/xa/src/xa.c @@ -61,7 +61,10 @@ /* exported globals */ int ncmos, cmosfl, w65816, n65816; -int masm = 0; +/* compatibility flags */ +int masm = 0; /* MASM */ +int ca65 = 0; /* CA65 */ + int nolink = 0; int romable = 0; int romaddr = 0; @@ -71,10 +74,12 @@ int crossref = 0; char altppchar; /* local variables */ + static char out[MAXLINE]; static time_t tim1, tim2; static FILE *fpout, *fperr, *fplab; static int ner = 0; +static int ner_max = 20; static int align = 1; @@ -91,6 +96,7 @@ static int 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; @@ -191,6 +197,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,7 +642,7 @@ 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++]; ll=l; @@ -827,6 +854,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" @@ -844,6 +872,8 @@ static void usage(int default816, FILE *fp) " -l filename sets labellist filename, default is none\n" " -r adds crossreference list to labellist (if `-l' given)\n" " -M allow ``:'' to appear in comments for MASM compatibility\n" + " -Xcompatset set compatibility flags for other assemblers, known values are:\n" + " MASM, CA65\n" " -R start assembler in relocating mode\n"); fprintf(fp, " -Llabel defines `label' as absolute, undefined label even when linking\n" @@ -900,7 +930,7 @@ static char *ertxt[] = { "NewFile", "CMOS-Befehl", "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 +969,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", @@ -1062,9 +1091,20 @@ static int getline(char *s) comcom = 1; if (c=='\0') break; /* hkfl = comcom = 0 */ - if (c==':' && !hkfl && (!comcom || !masm)) { - gl=1; - break; + if (c==':' && !hkfl) { + /* if the next char is a "=" - so that we have a ":=" - and we + we have ca65 compatibility, we ignore the colon */ + if (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; + } + } } 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 +1171,30 @@ 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 }, + { 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..fd4d7cf 100644 --- a/xa/src/xa.h +++ b/xa/src/xa.h @@ -22,7 +22,7 @@ #include "xah.h" /* For SEG_MAX */ extern int ncmos, cmosfl, w65816, n65816; -extern int masm, nolink; +extern int masm, ca65, nolink; extern int noglob; extern int showblk; extern int relmode; diff --git a/xa/src/xaa.c b/xa/src/xaa.c index 73dd861..ac65795 100644 --- a/xa/src/xaa.c +++ b/xa/src/xaa.c @@ -180,6 +180,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; } } diff --git a/xa/src/xah.h b/xa/src/xah.h index 8b98bc8..710c64e 100644 --- a/xa/src/xah.h +++ b/xa/src/xah.h @@ -20,15 +20,22 @@ #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 200000L /* temporary memory buffer from Pass1 to Pass 2 */ typedef struct LabOcc { struct LabOcc *next; diff --git a/xa/src/xal.c b/xa/src/xal.c index 5598c75..3fcfade 100644 --- a/xa/src/xal.c +++ b/xa/src/xal.c @@ -50,6 +50,12 @@ static int b_get(int*); static int b_test(int); static int ll_def(char *s, int *n, int b); +static int b_new(void); +static void cll_init(); +static int cll_get(); +static void cll_clear(); +static int cll_getcur(); + /* local variables */ /* @@ -69,6 +75,7 @@ static Labtab *ltp; int l_init(void) { + cll_init(); return 0; #if 0 int er; @@ -139,10 +146,49 @@ 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 + */ int lg_set(char *s ) { int n, er; - er = ll_search(s,&n); + er = ll_search(s,&n, 0); if(er==E_OK) { fprintf(stderr,"Warning: global label doubly defined!\n"); @@ -156,52 +202,73 @@ int lg_set(char *s ) { return er; } +/**********************************************************************************/ int l_def(char *s, int *l, int *x, int *f) { int n,er,b,i=0; + int 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=0; /* when set, clear the cheap local label block */ 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=1; /* 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) { + /* clear cheap local labels */ + cll_clear(); + } - if(!isalpha(s[i]) && s[i]!='_') + if(!isalpha(s[i]) && s[i]!='_' && !(ca65 && isdigit(s[i]) ) ) er=E_SYNTAX; else { - er=ll_search(s+i,&n); + 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,7 +280,7 @@ 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))) /* store the label in the table of labels */ { ltp=afile->la.lt+n; *l=ltp->len+i; @@ -229,15 +296,23 @@ 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; + int 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=0; + if (s[0]=='@') { + cll_fl=1; /* also used as offset to the label length, so must be 1 */ + s++; + } + + er=ll_search(s,&n, cll_fl); +/*printf("l_search: lab=%s(l=%d, afl=%d, er=%d, n=%d, cll_fl=%d, cll_cur=%d)\n",s,*l, *afl,er,n, cll_fl, cll_getcur());*/ if(er==E_OK) { ltp=afile->la.lt+n; - *l=ltp->len; + *l=ltp->len + cll_fl; if(ltp->fl == 1) { l_get(n,v,afl);/* *v=lt[n].val;*/ @@ -251,12 +326,17 @@ int l_search(char *s, int *l, int *x, int *v, int *afl) } else { - b_get(&b); + if(cll_fl) { + b=cll_get(); + } else { + b_get(&b); + } + er=ll_def(s,x,b); /* ll_def(...,*v); */ ltp=afile->la.lt+(*x); - *l=ltp->len; + *l=ltp->len + cll_fl; if(!er) { @@ -401,8 +481,14 @@ static int ll_def(char *s, int *n, int b) /* definiert naechstes Label 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 + */ +int ll_search(char *s, int *n, int cll_fl) /* search Label in Tabelle ,nr->n */ { int i,j=0,k,er=E_NODEF,hash; @@ -411,7 +497,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 +507,21 @@ 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) { + if (ltp->blk == cll_getcur()) { + er=E_OK; + break; + } + } 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 +546,7 @@ int ll_pdef(char *t) { int n; - if(ll_search(t,&n)==E_OK) + if(ll_search(t,&n, 0)==E_OK) { ltp=afile->la.lt+n; if(ltp->fl) @@ -496,9 +591,22 @@ 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 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) { @@ -509,6 +617,11 @@ int b_init(void) return(E_OK); } +int b_new(void) +{ + return ++blk; +} + int b_depth(void) { return bi; @@ -519,19 +632,25 @@ int ga_blk(void) return(blk); } +/** + * open a new block scope + */ int b_open(void) { int er=E_BLKOVR; if(bi=0) @@ -562,6 +687,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 +701,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; diff --git a/xa/src/xal.h b/xa/src/xal.h index 98a1d75..af78995 100644 --- a/xa/src/xal.h +++ b/xa/src/xal.h @@ -42,7 +42,7 @@ int l_search(char *s, int *l, int *x, int *v, int *afl); void l_set(int n, int v, int afl); 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, int cll_fl); int ll_pdef(char *t); int b_open(void); diff --git a/xa/src/xap.c b/xa/src/xap.c index da8e3c3..3b5c58b 100644 --- a/xa/src/xap.c +++ b/xa/src/xap.c @@ -130,7 +130,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,0) ? 1 : 0 ); return(0); } diff --git a/xa/src/xat.c b/xa/src/xat.c index a08539b..821a05d 100644 --- a/xa/src/xat.c +++ b/xa/src/xat.c @@ -75,7 +75,7 @@ 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" }; @@ -85,7 +85,7 @@ static int lp[]= { 0,1,1,1,1,2,2,1,1,1,2,2,2,1,1,1,2,2 }; /* last valid mnemonic */ #define Lastbef 93 /* last valid token+1 */ -#define Anzkey 123 +#define Anzkey 124 #define Kbyt Lastbef+1 #define Kword Lastbef+2 @@ -119,6 +119,8 @@ 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 Kcode Lastbef+30 /* gets remapped to Ktext */ + #define Kreloc Anzkey /* *= (relocation mode) */ #define Ksegment Anzkey+1 @@ -349,6 +351,7 @@ fprintf(stderr, "- p1 %d starting -\n", pc[segment]); printf("\n"); */ /* if text/data produced, then no more fopt allowed in romable mode */ + /* TODO: need to check, Kbyte is being remapped to Kbyt. What is the effect here? */ if((romable>1) && (t[0]base[SEG_TEXT] = pc[SEG_TEXT] = romaddr + h_length(); romable=1; @@ -1536,7 +1539,8 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, static int v,f; static int operand,o; int fl,afl; - int p,q,ud,n,ll,mk,er; + int p,q,ud,ll,mk,er; + int n; /* label number to be passed between l_def (definition) and l_set (set the value) */ int m, uz, byte; static unsigned char cast; @@ -1586,6 +1590,7 @@ static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, if(s[p]=='=') { + /*printf("Found = @%s\n",s+p);*/ t[q++]=T_OP; t[q++]=n&255; t[q++]=(n>>8)&255; @@ -1594,6 +1599,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; @@ -1674,8 +1690,10 @@ 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]; operand= -operand+1; @@ -1691,12 +1709,12 @@ 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]=='@' && ca65)) { m=n; er=l_search((char*)s+p,&ll,&n,&v,&afl); - /* if(m==Kglobl || m==Kextzero) { if(er==E_NODEF) { @@ -1961,6 +1979,7 @@ static int t_keyword(signed char *s, int *l, int *n) if(i==Kdw) i=Kword; if(i==Kblock) i=Kopen; if(i==Kbend) i=Kclose; + if(i==Kcode) i=Ktext; *l=j;