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