%{ /* * zmac -- macro cross-assembler for the Zilog Z80 microprocessor * * Bruce Norskog 4/78 * * Last modification 2000-07-01 by mgr * * This assembler is modeled after the Intel 8080 macro cross-assembler * for the Intel 8080 by Ken Borgendale. The major features are: * 1. Full macro capabilities * 2. Conditional assembly * 3. A very flexible set of listing options and pseudo-ops * 4. Symbol table output * 5. Error report * 6. Elimination of sequential searching * 7. Commenting of source * 8. Facilities for system definiton files * * (Revision history is now in ChangeLog. -rjm) */ #define ZMAC_VERSION "1.3" /* #define ZMAC_BETA "b4" */ #include #include #include #include #include #include "mio.h" #include "getoptn.h" #define UNUSED(var) ((void) var) #if defined (__riscos__) && !defined (__riscos) #define __riscos #endif #ifdef __riscos #include "swis.h" #define DDEUtils_Prefix 0x42580 #define DDEUtils_ThrowbackStart 0x42587 #define DDEUtils_ThrowbackSend 0x42588 #define DDEUtils_ThrowbackEnd 0x42589 #endif #ifndef OS_DIR_SEP #if defined (MSDOS) #define OS_DIR_SEP '\\' #elif defined (__riscos) #define OS_DIR_SEP '.' #else #define OS_DIR_SEP '/' #endif #endif #ifndef OS_EXT_SEP #if defined (__riscos) #define OS_EXT_SEP '/' #else #define OS_EXT_SEP '.' #endif #endif /* * DEBUG turns on pass reporting. * Macro debug and Token debug enables. #define DEBUG #define M_DEBUG #define T_DEBUG */ #define ITEMTABLESIZE 2000 #define TEMPBUFSIZE 200 #define LINEBUFFERSIZE 200 #define EMITBUFFERSIZE 200 #define MAXSYMBOLSIZE 40 #define IFSTACKSIZE 20 #define MAXIFS 1024 #define TITLELEN 50 #define BINPERLINE 16 #define PARMMAX 25 #define MAXEXP 25 #define SYMMAJIC 07203 #define NEST_IN 8 #define loop for(;;) void yyerror(char *err) { UNUSED (err); /* we will do our own error printing */ /* printf ("Oops! %s\n", err); */ } struct item { char *i_string; int i_value; int i_token; int i_uses; int i_equbad; }; FILE *fout, *fbuf, *fin[NEST_IN], *now_file ; int pass2; /* set when pass one completed */ int dollarsign ; /* location counter */ int olddollar ; /* kept to put out binary */ /* program counter save for PHASE/DEPHASE */ int phdollar, phbegin, phaseflag ; char *src_name[NEST_IN] ; int linein[NEST_IN] ; int now_in ; #define bflag 0 /* balance error */ #define eflag 1 /* expression error */ #define fflag 2 /* syntax error */ #define iflag 3 /* bad digits */ #define mflag 4 /* multiply defined */ #define pflag 5 /* phase error */ #define uflag 6 /* undeclared used */ #define vflag 7 /* value out of range */ #define oflag 8 /* phase/dephase error */ #define frflag 9 /* double forward ref. via equ error */ #define zflag 10 /* Z80-only instruction (when `-z' option in use) */ #define orgflag 11 /* retrograde org error (when `-h' option not in use) */ #define FLAGS 12 /* number of flags */ char err[FLAGS]; int keeperr[FLAGS]; char errlet[FLAGS]="BEFIMPUVORZG"; char *errname[FLAGS]={ "Balance", "Expression", "Syntax", "Digit", "Mult. def.", "Phase", "Undeclared", "Value", "Phase/Dephase", "Forward ref. to EQU with forward ref.", "Z80-specific instruction", "Retrograde ORG" }; char *warnname[]={ "Symbol length exceeded", "Non-standard syntax", "Could replace JP with JR", "Could replace LD A, 0 with XOR A if flags unimportant", "Could replace RLC A with RLCA if S, Z and P/V flags unimportant", "Could replace RRC A with RRCA if S, Z and P/V flags unimportant", "Could replace RL A with RLA if S, Z and P/V flags unimportant", "Could replace RR A with RRA if S, Z and P/V flags unimportant", "Could replace SLA A with ADD A, A if H and P/V flags unimportant" }; /* for "0 symbols", "1 symbol", "2 symbols", etc. */ #define DO_PLURAL(x) (x),((x)==1)?"":"s" char linebuf[LINEBUFFERSIZE]; char *lineptr; char *linemax = linebuf+LINEBUFFERSIZE; char outbin[BINPERLINE]; char *outbinp = outbin; char *outbinm = outbin+BINPERLINE; char emitbuf[EMITBUFFERSIZE]; char *emitptr; char ifstack[IFSTACKSIZE]; char *ifptr; char *ifstmax = ifstack+IFSTACKSIZE-1; char expif[MAXIFS]; char *expifp; char *expifmax = expif+MAXIFS; char hexadec[] = "0123456789ABCDEF" ; char *expstack[MAXEXP]; int expptr; int nitems; int linecnt; int nbytes; int invented; char tempbuf[TEMPBUFSIZE]; char *tempmax = tempbuf+TEMPBUFSIZE-1; char inmlex; char arg_flag; char quoteflag; int parm_number; int exp_number; char symlong[] = "Symbol too long"; int disp; #define FLOC PARMMAX #define TEMPNUM PARMMAX+1 char **est; char **est2; char *floc; int mfptr; FILE *mfile; char *title; char titlespace[TITLELEN]; char *timp; char *sourcef; /* changed to cope with filenames longer than 14 chars -rjm 1998-12-15 */ char src[1024]; char bin[1024]; char mtmp[1024]; char listf[1024]; char writesyms[1024]; #ifdef __riscos char riscos_thbkf[1024]; #endif char bopt = 1, edef = 1, eopt = 1, fdef = 0, fopt = 0, gdef = 1, gopt = 1, iopt = 0 , /* list include files */ lstoff = 0, lston = 0, /* flag to force listing on */ lopt = 0, mdef = 0, mopt = 0, nopt = 1, /* line numbers on as default */ oldoopt = 0, popt = 1, /* form feed as default page eject */ sopt = 0, /* turn on symbol table listing */ output_hex = 0, /* `-h', output .hex rather than .bin -rjm */ output_8080_only = 0, /* `-z', output 8080-compat. ops only -rjm */ show_error_line = 0, /* `-S', show line which caused error -rjm */ terse_lst_errors = 0, /* `-t', terse errors in listing -rjm */ continuous_listing = 1, /* `-d', discontinuous - with page breaks */ suggest_optimise = 0, /* `-O', suggest optimisations -mgr */ #ifdef __riscos riscos_thbk = 0, /* `-T', RISC OS throwback -mep */ #endif output_amsdos = 0, /* `-A', AMSDOS binary file output -mep */ saveopt; char xeq_flag = 0; int xeq; time_t now; int line; int page = 1; int had_errors = 0; /* if program had errors, do exit(1) */ #ifdef __riscos int riscos_throwback_started = 0; #endif int not_seen_org = 1; int first_org_store = 0; struct stab { char t_name[MAXSYMBOLSIZE+1]; int t_value; int t_token; }; /* * push back character */ int peekc; /* function prototypes */ int addtoline(int ac); int iflist(void); int yylex(void); int tokenofitem(int deftoken); int nextchar(void); int skipline(int ac); void usage(void); int main(int argc, char *argv[]); int getarg(void); int getm(void); void yyerror(char *err); void emit(int num, ...); void emit1(int opcode,int regvalh,int data16,int type); void emitdad(int rp1,int rp2); void emitjr(int opcode,int expr); void emitjp(int opcode,int expr); void putbin(int v); void flushbin(void); void puthex(char byte, FILE *buf); void list(int optarg); void lineout(void); void eject(void); void space(int n); void lsterr1(void); void lsterr2(int lst); void errorprt(int errnum); void warnprt(int warnnum, int warnoff); void list1(void); void interchange(int i, int j); void custom_qsort(int m, int n); void setvars(void); void error(char *as); void fileerror(char *as,char *filename); void justerror(char *as); void putsymtab(void); void erreport(void); void mlex(void); void suffix_if_none(char *str,char *suff); void suffix(char *str,char *suff); void decanonicalise(char *str); void putm(char c); void popsi(void); char *getlocal(int c, int n); void insymtab(char *name); void outsymtab(char *name); void copyname(char *st1, char *st2); void next_source(char *sp); void doatexit (void); #ifdef __riscos void riscos_set_csd(char *sp); void riscos_throwback(int severity, char *file, int line, char *error); #endif /* * add a character to the output line buffer */ int addtoline(int ac) { /* check for EOF from stdio */ if (ac == -1) ac = 0 ; if (inmlex) return(ac); if (lineptr >= linemax) error("line buffer overflow"); *lineptr++ = ac; return(ac); } /* * put values in buffer for outputing */ void emit(int bytes, ...) { va_list ap; unsigned char *oldemitptr=(unsigned char *)emitptr; int c; va_start(ap,bytes); while (--bytes >= 0) if (emitptr >= &emitbuf[EMITBUFFERSIZE]) error("emit buffer overflow"); else { *emitptr++ = va_arg(ap,int); } if (output_8080_only) { /* test for Z80-specific ops. These start with one of * sixteen byte values, listed below. The values were * taken from "A Z80 Workshop Manual" by E. A. Parr. -rjm */ /* As far as I can tell from my own literature * review, 0x02, 0x0a, 0x12 and 0x1a are valid * 8080 opcodes (LDAX/STAX B/D) -mgr */ c=*oldemitptr; if (/* c==0x02 || */ c==0x08 || /* c==0x0a || */ c==0x10 || /* c==0x12 || */ c==0x18 || /* c==0x1a || */ c==0x20 || c==0x28 || c==0x30 || c==0x38 || c==0xcb || c==0xd9 || c==0xdd || c==0xed || c==0xfd) err[zflag]++; } va_end(ap); } /* for emitted data - as above, without 8080 test. * Duplicating the code was easier than putting an extra arg in all * those emit()s. :-} Hopefully this isn't too unbearably nasty. -rjm */ void dataemit(int bytes, ...) { va_list ap; va_start(ap,bytes); while (--bytes >= 0) if (emitptr >= &emitbuf[EMITBUFFERSIZE]) error("emit buffer overflow"); else { *emitptr++ = va_arg(ap,int); } va_end(ap); } void emit1(int opcode,int regvalh,int data16,int type) { if ((regvalh & 0x8000)) { /* extra brackets to silence -Wall */ if ((type & 1) == 0 && (disp > 127 || disp < -128)) err[vflag]++; switch(type) { case 0: if (opcode & 0x8000) emit(4, regvalh >> 8, opcode >> 8, disp, opcode); else emit(3, regvalh >> 8, opcode, disp); break; case 1: emit(2, regvalh >> 8, opcode); break; case 2: if (data16 > 255 || data16 < -128) err[vflag]++; emit(4, regvalh >> 8, opcode, disp, data16); break; case 5: emit(4, regvalh >> 8, opcode, data16, data16 >> 8); } } else switch(type) { case 0: if (opcode & 0100000) emit(2, opcode >> 8, opcode); else emit(1, opcode); break; case 1: if (opcode & 0100000) emit(2, opcode >> 8, opcode); else emit(1, opcode); break; case 2: if (data16 > 255 || data16 < -128) err[vflag]++; emit(2, opcode, data16); break; case 3: if (data16 >255 || data16 < -128) err[vflag]++; emit(2, opcode, data16); break; case 5: if (opcode & 0100000) emit(4, opcode >> 8, opcode, data16, data16 >> 8); else emit(3, opcode, data16, data16 >> 8); } } void emitdad(int rp1,int rp2) { if (rp1 & 0x8000) emit(2,rp1 >> 8, rp2 + 9); else emit(1,rp2 + 9); } void emitjr(int opcode,int expr) { disp = expr - dollarsign - 2; if (disp > 127 || disp < -128) err[vflag]++; emit(2, opcode, disp); } void emitjp(int opcode,int expr) { if (suggest_optimise && pass2 && opcode <= 0xda && !output_8080_only) { disp = expr - dollarsign - 2; if (disp <= 127 && disp >= -128) warnprt (2, 0); } emit(3, opcode, expr, expr >> 8); } /* * put out a byte of binary */ void putbin(int v) { if(!pass2 || !bopt) return; *outbinp++ = v; if (outbinp >= outbinm) flushbin(); } /* * output one line of binary in INTEL standard form */ void flushbin() { char *p; int check=outbinp-outbin; if (!pass2 || !bopt) return; nbytes += check; if (check) { if (output_hex) { putc(':', fbuf); puthex(check, fbuf); puthex(olddollar>>8, fbuf); puthex(olddollar, fbuf); puthex(0, fbuf); } check += (olddollar >> 8) + olddollar; olddollar += (outbinp-outbin); for (p=outbin; p> 4) & 017], buf); putc(hexadec[byte & 017], buf); } /* * put out a line of output -- also put out binary */ void list(int optarg) { char * p; int i; int lst; if (!expptr) linecnt++; addtoline('\0'); if (pass2) { lst = iflist(); if (lst) { lineout(); if (nopt) fprintf(fout, "%4d:\t", linein[now_in]); puthex(optarg >> 8, fout); puthex(optarg, fout); fputs(" ", fout); for (p = emitbuf; (p < emitptr) && (p - emitbuf < 4); p++) { puthex(*p, fout); } for (i = 4 - (p-emitbuf); i > 0; i--) fputs(" ", fout); putc('\t', fout); fputs(linebuf, fout); } if (bopt) { for (p = emitbuf; p < emitptr; p++) putbin(*p); } p = emitbuf+4; while (lst && gopt && p < emitptr) { lineout(); if (nopt) putc('\t', fout); fputs(" ", fout); for (i = 0; (i < 4) && (p < emitptr);i++) { puthex(*p, fout); p++; } putc('\n', fout); } lsterr2(lst); } else lsterr1(); dollarsign += emitptr - emitbuf; emitptr = emitbuf; lineptr = linebuf; } /* * keep track of line numbers and put out headers as necessary */ void lineout() { if (continuous_listing) { line = 1; return; } if (line == 60) { if (popt) putc('\014', fout); /* send the form feed */ else fputs("\n\n\n\n\n", fout); line = 0; } if (line == 0) { fprintf(fout, "\n\n%s %s\t%s\t Page %d\n\n\n", &timp[4], &timp[20], title, page++); line = 4; } line++; } /* * cause a page eject */ void eject() { if (pass2 && !continuous_listing && iflist()) { if (popt) { putc('\014', fout); /* send the form feed */ } else { while (line < 65) { line++; putc('\n', fout); } } } line = 0; } /* * space n lines on the list file */ void space(int n) { int i ; if (pass2 && iflist()) for (i = 0; i 4) errorprt(i); } fflush(fout); /* to avoid putc(har) mix bug */ } /* * print diagnostic to error terminal */ void errorprt(int errnum) { had_errors=1; fprintf(stderr,"%s:%d: %s error\n", src_name[now_in], linein[now_in], errname[errnum]); if(show_error_line) fprintf(stderr, "%s\n", linebuf); #ifdef __riscos if (riscos_thbk) riscos_throwback (1, src_name[now_in], linein[now_in], errname[errnum]); #endif } /* * print warning to error terminal */ void warnprt(int warnnum, int warnoff) { fprintf(stderr,"%s:%d: warning: %s\n", src_name[now_in], linein[now_in] + warnoff, warnname[warnnum]); /* Offset needed if warning issued while line is being parsed */ #ifdef __riscos if (riscos_thbk) riscos_throwback (0, src_name[now_in], linein[now_in] + warnoff, warnname[warnnum]); #endif /* if(show_error_line) Can't show line because it isn't necessarily complete fprintf(stderr, "%s\n", linebuf); */ } /* * list without address -- for comments and if skipped lines */ void list1() { int lst; addtoline('\0'); lineptr = linebuf; if (!expptr) linecnt++; if (pass2) { if ((lst = iflist())) { lineout(); if (nopt) fprintf(fout, "%4d:\t", linein[now_in]); fprintf(fout, "\t\t%s", linebuf); lsterr2(lst); } } else lsterr1(); } /* * see if listing is desired */ int iflist() { int i, j; if (lston) return(1) ; if (lopt) return(0); if (*ifptr && !fopt) return(0); if (!lstoff && !expptr) return(1); j = 0; for (i=0; i STRING %token NOOPERAND %token ARITHC %token ADD %token LOGICAL %token AND %token OR %token XOR %token BIT %token CALL %token INCDEC %token DJNZ %token EX %token IM %token PHASE %token DEPHASE %token IN %token JP %token JR %token LD %token OUT %token PUSHPOP %token RET %token SHIFT %token RST %token REGNAME %token ACC %token C %token RP %token HL %token INDEX %token AF %token SP %token MISCREG %token F %token COND %token SPCOND %token NUMBER %token UNDECLARED %token END %token ORG %token DEFB %token DEFS %token DEFW %token EQU %token DEFL %token LABEL %token EQUATED %token WASEQUATED %token DEFLED %token MULTDEF %token MOD %token SHL %token SHR %token NOT %token LT %token GT %token EQ %token LE %token GE %token NE %token IF %token ELSE %token ENDIF %token ARGPSEUDO %token LIST %token MINMAX %token MACRO %token MNAME %token OLDMNAME %token ARG %token ENDM %token MPARM %token ONECHAR %token TWOCHAR %type label.part symbol %type reg evenreg realreg mem pushable bcdesp bcdehlsp mar condition %type spcondition noparenexpr parenexpr expression lxexpression %left '|' OR %left '^' XOR %left '&' AND %left '=' EQ NE %left '<' '>' LT GT LE GE %left SHL SHR %left '+' '-' %left '*' '/' '%' MOD %right '!' '~' NOT UNARY %% statements: /* Empty file! */ | statements statement ; statement: label.part '\n' { if ($1) list(dollarsign); else list1(); } | label.part operation '\n' { list(dollarsign); } | symbol EQU expression '\n' { /* a forward reference to a label in the expression cannot * be fixed in a forward reference to the EQU; * it would need three passes. -rjm */ if(!pass2 && equ_bad_label) { /* this indicates that the equ has an incorrect * (i.e. pass 1) value. */ $1->i_equbad = 1; } else { /* but if 2nd pass or no forward reference, it's ok. */ $1->i_equbad = 0; } equ_bad_label=0; switch($1->i_token) { case UNDECLARED: case WASEQUATED: $1->i_token = EQUATED; $1->i_value = $3; break; case EQUATED: if ($1->i_value == $3) break; /* Allow benign redefinition -mgr */ /* Drop-through intentional */ default: err[mflag]++; $1->i_token = MULTDEF; } list($3); } | symbol DEFL expression '\n' { switch($1->i_token) { case UNDECLARED: case DEFLED: $1->i_token = DEFLED; $1->i_value = $3; break; default: err[mflag]++; $1->i_token = MULTDEF; } list($3); } | symbol MINMAX expression ',' expression '\n' { switch ($1->i_token) { case UNDECLARED: case DEFLED: $1->i_token = DEFLED; if ($2->i_value) /* max */ list($1->i_value = ($3 > $5? $3:$5)); else list($1->i_value = ($3 < $5? $3:$5)); break; default: err[mflag]++; $1->i_token = MULTDEF; list($1->i_value); } } | IF expression '\n' { /* all $2's here were yypc[2].ival before. * I think the idea was perhaps to allow constants * only...? Anyway, it now allows any expression - * which would seem to make sense given the definition * above, right? :-) -rjm */ if (ifptr >= ifstmax) error("Too many ifs"); else { if (pass2) { *++ifptr = *expifp++; if (*ifptr != !($2)) err[pflag]++; } else { if (expifp >= expifmax) error("Too many ifs!"); *expifp++ = !($2); *++ifptr = !($2); } } saveopt = fopt; fopt = 1; list($2); fopt = saveopt; } | ELSE '\n' { /* FIXME: it would be nice to spot repeated ELSEs, but how? */ *ifptr = !*ifptr; saveopt = fopt; fopt = 1; list1(); fopt = saveopt; } | ENDIF '\n' { if (ifptr == ifstack) err[bflag]++; else --ifptr; list1(); } | label.part END '\n' { list(dollarsign); peekc = 0; } | label.part END expression '\n' { xeq_flag++; xeq = $3; list($3); peekc = 0; } | label.part DEFS expression '\n' { if ($3 < 0) err[vflag]++; list(dollarsign); if ($3) { flushbin(); dollarsign += $3; olddollar = dollarsign; /* if it's not hex output though, we also need * to output zeroes as appropriate. -rjm */ if(!output_hex && pass2) { int f; for (f=0;f<($3);f++) fputc(0, fbuf); } } } | ARGPSEUDO arg_on ARG arg_off '\n' { list1(); switch ($1->i_value) { case 0: /* title */ lineptr = linebuf; cp = tempbuf; title = titlespace; while ((*title++ = *cp++) && (title < &titlespace[TITLELEN])); *title = 0; title = titlespace; break; case 1: /* rsym */ if (pass2) break; insymtab(tempbuf); break; case 2: /* wsym */ strcpy(writesyms, tempbuf); break; case 3: /* include file */ if (*tempbuf == '"' || *tempbuf == '\'') { if (tempbuf[strlen (tempbuf) - 1] == '"' || tempbuf[strlen (tempbuf) - 1] == '\'') tempbuf[strlen (tempbuf) - 1] = 0; next_source(tempbuf + 1) ; } else { next_source(tempbuf) ; } break ; } } | ARGPSEUDO arg_on arg_off '\n' { fprintf(stderr,"ARGPSEUDO error\n"); err[fflag]++; list(dollarsign); } | LIST '\n' { list_tmp1=$1->i_value; list_tmp2=1; goto dolopt; } | LIST expression '\n' { list_tmp1=$1->i_value; list_tmp2=$2; dolopt: linecnt++; if (pass2) { lineptr = linebuf; switch (list_tmp1) { case 0: /* list */ if (list_tmp2 < 0) lstoff = 1; if (list_tmp2 > 0) lstoff = 0; break; case 1: /* eject */ if (list_tmp2) eject(); break; case 2: /* space */ if ((line + list_tmp2) > 60) eject(); else space(list_tmp2); break; case 3: /* elist */ eopt = edef; if (list_tmp2 < 0) eopt = 0; if (list_tmp2 > 0) eopt = 1; break; case 4: /* fopt */ fopt = fdef; if (list_tmp2 < 0) fopt = 0; if (list_tmp2 > 0) fopt = 1; break; case 5: /* gopt */ gopt = gdef; if (list_tmp2 < 0) gopt = 1; if (list_tmp2 > 0) gopt = 0; break; case 6: /* mopt */ mopt = mdef; if (list_tmp2 < 0) mopt = 0; if (list_tmp2 > 0) mopt = 1; } } } | UNDECLARED MACRO parm.list '\n' { $1->i_token = MNAME; $1->i_value = mfptr; #ifdef M_DEBUG fprintf (stderr, "[UNDECLARED MACRO %s]\n", $1->i_string); #endif mfseek(mfile, (long)mfptr, 0); list1(); mlex() ; parm_number = 0; } | OLDMNAME MACRO { $1->i_token = MNAME; #ifdef M_DEBUG fprintf (stderr, "[OLDNAME MACRO %s]\n", $1->i_string); #endif while (yychar != ENDM && yychar) { while (yychar != '\n' && yychar) yychar = yylex(); list1(); yychar = yylex(); } while (yychar != '\n' && yychar) yychar = yylex(); list1(); yychar = yylex(); } | label.part MNAME al arg.list '\n' { #ifdef M_DEBUG fprintf (stderr, "[MNAME %s]\n", $2->i_string); #endif $2->i_uses++ ; arg_flag = 0; parm_number = 0; list(dollarsign); expptr++; est = est2; est2 = NULL; est[FLOC] = floc; est[TEMPNUM] = (char *)(long)exp_number++; floc = (char *)(long)($2->i_value); mfseek(mfile, (long)floc, 0); } | error { err[fflag]++; quoteflag = 0; arg_flag = 0; parm_number = 0; if (est2) { int i; for (i=0; ii_token) { case UNDECLARED: if (pass2) err[pflag]++; else { $2->i_token = LABEL; $2->i_value = dollarsign; } break; case LABEL: if (!pass2) { $2->i_token = MULTDEF; err[mflag]++; } else if ($2->i_value != dollarsign) err[pflag]++; break; default: err[mflag]++; $2->i_token = MULTDEF; } } | symbol maybecolon { switch($1->i_token) { case UNDECLARED: if (pass2) err[pflag]++; else { $1->i_token = LABEL; $1->i_value = dollarsign; } break; case LABEL: if (!pass2) { $1->i_token = MULTDEF; err[mflag]++; } else if ($1->i_value != dollarsign) err[pflag]++; break; default: err[mflag]++; $1->i_token = MULTDEF; } } ; operation: NOOPERAND { emit1($1->i_value, 0, 0, 1); } | JP expression { emitjp(0303, $2); } | CALL expression { emit(3, 0315, $2, $2 >> 8); } | RST expression { int a = $2, doneerr=0; /* added support for normal RST form -rjm */ if (a >= 8) { if ((a&7)!=0) doneerr=1,err[vflag]++; a >>= 3; } if ((a > 7 || a < 0) && !doneerr) /* don't give two errs... */ err[vflag]++; emit(1, $1->i_value + (a << 3)); } | ADD expression { emit1(0306, 0, $2, 3); if (pass2) warnprt (1, 0); } | ADD ACC ',' expression { emit1(0306, 0, $4, 3); } | ARITHC expression { emit1(0306 + ($1->i_value << 3), 0, $2, 3); if (pass2) warnprt (1, 0); } | ARITHC ACC ',' expression { emit1(0306 + ($1->i_value << 3), 0, $4, 3); } | LOGICAL expression { emit1(0306 | ($1->i_value << 3), 0, $2, 3); } | AND expression { emit1(0306 | ($1->i_value << 3), 0, $2, 3); } | OR expression { emit1(0306 | ($1->i_value << 3), 0, $2, 3); } | XOR expression { emit1(0306 | ($1->i_value << 3), 0, $2, 3); } | LOGICAL ACC ',' expression /* -cdk */ { emit1(0306 | ($1->i_value << 3), 0, $4, 3); if (pass2) warnprt (1, 0); } | AND ACC ',' expression /* -cdk */ { emit1(0306 | ($1->i_value << 3), 0, $4, 3); if (pass2) warnprt (1, 0); } | OR ACC ',' expression /* -cdk */ { emit1(0306 | ($1->i_value << 3), 0, $4, 3); if (pass2) warnprt (1, 0); } | XOR ACC ',' expression /* -cdk */ { emit1(0306 | ($1->i_value << 3), 0, $4, 3); if (pass2) warnprt (1, 0); } | ADD reg { emit1(0200 + ($2 & 0377), $2, 0, 0); if (pass2) warnprt (1, 0); } | ADD ACC ',' reg { emit1(0200 + ($4 & 0377), $4, 0, 0); } | ARITHC reg { emit1(0200 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); if (pass2) warnprt (1, 0); } | ARITHC ACC ',' reg { emit1(0200 + ($1->i_value << 3) + ($4 & 0377), $4, 0, 0); } | LOGICAL reg { emit1(0200 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); } | AND reg { emit1(0200 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); } | OR reg { emit1(0200 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); } | XOR reg { emit1(0200 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); } | LOGICAL ACC ',' reg /* -cdk */ { emit1(0200 + ($1->i_value << 3) + ($4 & 0377), $4, 0, 0); if (pass2) warnprt (1, 0); } | AND ACC ',' reg /* -cdk */ { emit1(0200 + ($1->i_value << 3) + ($4 & 0377), $4, 0, 0); if (pass2) warnprt (1, 0); } | OR ACC ',' reg /* -cdk */ { emit1(0200 + ($1->i_value << 3) + ($4 & 0377), $4, 0, 0); if (pass2) warnprt (1, 0); } | XOR ACC ',' reg /* -cdk */ { emit1(0200 + ($1->i_value << 3) + ($4 & 0377), $4, 0, 0); if (pass2) warnprt (1, 0); } | SHIFT reg { if (suggest_optimise && pass2 && ($2 & 0377) == 7 && $1->i_value <= 4) warnprt ($1->i_value + 4, 0); if (pass2 && $1->i_value == 6) warnprt (1, 0); emit1(0145400 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); } | INCDEC reg { emit1($1->i_value + (($2 & 0377) << 3) + 4, $2, 0, 0); } | ARITHC HL ',' bcdehlsp { if ($1->i_value == 1) emit(2,0355,0112+$4); else emit(2,0355,0102+$4); } | ADD mar ',' bcdesp { emitdad($2,$4); } | ADD mar ',' mar { if ($2 != $4) { fprintf(stderr,"ADD mar, mar error\n"); err[fflag]++; } emitdad($2,$4); } | INCDEC evenreg { emit1(($1->i_value << 3) + ($2 & 0377) + 3, $2, 0, 1); } | PUSHPOP pushable { emit1($1->i_value + ($2 & 0377), $2, 0, 1); } | BIT expression ',' reg { if ($2 < 0 || $2 > 7) err[vflag]++; emit1($1->i_value + (($2 & 7) << 3) + ($4 & 0377), $4, 0, 0); } | JP condition ',' expression { emitjp(0302 + $2, $4); } | JP '(' mar ')' { emit1(0351, $3, 0, 1); } | JP mar { emit1(0351, $2, 0, 1); if (pass2) warnprt (1, 0); } | CALL condition ',' expression { emit(3, 0304 + $2, $4, $4 >> 8); } | JR expression { emitjr(030,$2); } | JR spcondition ',' expression { emitjr($1->i_value + $2, $4); } | DJNZ expression { emitjr($1->i_value, $2); } | RET { emit(1, $1->i_value); } | RET condition { emit(1, 0300 + $2); } | LD reg ',' reg { if (($2 & 0377) == 6 && ($4 & 0377) == 6) { fprintf(stderr,"LD reg, reg error\n"); err[fflag]++; } emit1(0100 + (($2 & 7) << 3) + ($4 & 7),$2 | $4, 0, 0); } | LD reg ',' noparenexpr { if (suggest_optimise && pass2 && $4 == 0 && ($2 & 0377) == 7) warnprt (3, 0); emit1(6 + (($2 & 0377) << 3), $2, $4, 2); } | LD reg ',' '(' RP ')' { if ($2 != 7) { fprintf(stderr,"LD reg, (RP) error\n"); err[fflag]++; } else emit(1, 012 + $5->i_value); } | LD reg ',' parenexpr { if ($2 != 7) { fprintf(stderr,"LD reg, (expr) error\n"); err[fflag]++; } else emit(3, 072, $4, $4 >> 8); } | LD '(' RP ')' ',' ACC { emit(1, 2 + $3->i_value); } | LD parenexpr ',' ACC { emit(3, 062, $2, $2 >> 8); } | LD reg ',' MISCREG { if ($2 != 7) { fprintf(stderr,"LD reg, MISCREG error\n"); err[fflag]++; } else emit(2, 0355, 0127 + $4->i_value); } | LD MISCREG ',' ACC { emit(2, 0355, 0107 + $2->i_value); } | LD evenreg ',' lxexpression { emit1(1 + ($2 & 060), $2, $4, 5); } | LD evenreg ',' parenexpr { if (($2 & 060) == 040) emit1(052, $2, $4, 5); else emit(4, 0355, 0113 + $2, $4, $4 >> 8); } | LD parenexpr ',' evenreg { if (($4 & 060) == 040) emit1(042, $4, $2, 5); else emit(4, 0355, 0103 + $4, $2, $2 >> 8); } | LD evenreg ',' mar { if ($2 != 060) { fprintf(stderr,"LD evenreg error\n"); err[fflag]++; } else emit1(0371, $4, 0, 1); } | EX RP ',' HL { if ($2->i_value != 020) { fprintf(stderr,"EX RP, HL error\n"); err[fflag]++; } else emit(1, 0353); } | EX AF ',' AF setqf '\'' clrqf { emit(1, 010); } | EX '(' SP ')' ',' mar { emit1(0343, $6, 0, 1); } | IN realreg ',' parenexpr { if ($2 != 7) { fprintf(stderr,"IN reg, (expr) error\n"); err[fflag]++; } else { if ($4 < 0 || $4 > 255) err[vflag]++; emit(2, $1->i_value, $4); } } | IN realreg ',' '(' C ')' { emit(2, 0355, 0100 + ($2 << 3)); } | IN F ',' '(' C ')' { emit(2, 0355, 0160); } | OUT parenexpr ',' ACC { if ($2 < 0 || $2 > 255) err[vflag]++; emit(2, $1->i_value, $2); } | OUT '(' C ')' ',' realreg { emit(2, 0355, 0101 + ($6 << 3)); } | IM expression { if ($2 > 2 || $2 < 0) err[vflag]++; else emit(2, $1->i_value >> 8, $1->i_value + (($2 + ($2 > 0)) << 3)); } | PHASE expression { if (phaseflag) { err[oflag]++; } else { phaseflag = 1; phdollar = dollarsign; dollarsign = $2; phbegin = dollarsign; } } | DEPHASE { if (!phaseflag) { err[oflag]++; } else { phaseflag = 0; dollarsign = phdollar + dollarsign - phbegin; } } | ORG expression { if (not_seen_org) first_org_store=yyvsp[0].ival; not_seen_org=0; if (phaseflag) { err[oflag]++; dollarsign = phdollar + dollarsign - phbegin; phaseflag = 0; } if ($2-dollarsign) { flushbin(); if (pass2 && !output_hex && dollarsign != 0) { if (yyvsp[0].ival < dollarsign) { err[orgflag]++; } else { int f; for (f=0;f<(yyvsp[0].ival - dollarsign);f++) fputc(0, fbuf); } } olddollar = $2; dollarsign = $2; } } | DEFB db.list | DEFW dw.list | ENDM ; parm.list: | parm.element | parm.list ',' parm.element ; parm.element: UNDECLARED { $1->i_token = MPARM; if (parm_number >= PARMMAX) error("Too many parameters"); $1->i_value = parm_number++; } ; arg.list: /* empty */ | arg.element | arg.list ',' arg.element ; arg.element: ARG { cp = malloc(strlen(tempbuf)+1); #ifdef M_DEBUG fprintf (stderr, "[Arg%u(%p): %s]\n", parm_number, cp, tempbuf); #endif est2[parm_number++] = cp; strcpy(cp, tempbuf); } ; reg: realreg | mem ; realreg: REGNAME { $$ = $1->i_value; } | ACC { $$ = $1->i_value; } | C { $$ = $1->i_value; } ; mem: '(' HL ')' { $$ = 6; } | '(' INDEX expression ')' { disp = $3; $$ = ($2->i_value & 0177400) | 6; } | '(' INDEX ')' { disp = 0; $$ = ($2->i_value & 0177400) | 6; } ; evenreg: bcdesp | mar ; pushable: RP { $$ = $1->i_value; } | AF { $$ = $1->i_value; } | mar ; bcdesp: RP { $$ = $1->i_value; } | SP { $$ = $1->i_value; } ; bcdehlsp: bcdesp | HL { $$ = $1->i_value; } ; mar: HL { $$ = $1->i_value; } | INDEX { $$ = $1->i_value; } ; condition: spcondition | COND { $$ = $1->i_value; } ; spcondition: SPCOND { $$ = $1->i_value; } | C { $$ = 030; } ; db.list: db.list.element | db.list ',' db.list.element ; db.list.element: TWOCHAR { dataemit(2, $1, $1>>8); } | STRING { cp = $1; while (*cp != '\0') dataemit(1,*cp++); } | expression { if ($1 < -128 || $1 > 255) err[vflag]++; dataemit(1, $1 & 0377); } ; dw.list: dw.list.element | dw.list ',' dw.list.element ; dw.list.element: expression { dataemit(2, $1, $1>>8); } ; lxexpression: noparenexpr | TWOCHAR ; expression: parenexpr | noparenexpr ; parenexpr: '(' expression ')' { $$ = $2; } ; noparenexpr: LABEL { $$ = $1->i_value; $1->i_uses++ ; } | NUMBER | ONECHAR | EQUATED { $$ = $1->i_value; $1->i_uses++ ; } | WASEQUATED { $$ = $1->i_value; $1->i_uses++ ; if ($1->i_equbad) { /* forward reference to equ with a forward * reference of its own cannot be resolved * in two passes. -rjm */ err[frflag]++; } } | DEFLED { $$ = $1->i_value; $1->i_uses++ ; } | '$' { $$ = dollarsign; } | UNDECLARED { err[uflag]++; equ_bad_label=1; $$ = 0; } | MULTDEF { $$ = $1->i_value; } | expression '+' expression { $$ = $1 + $3; } | expression '-' expression { $$ = $1 - $3; } | expression '/' expression { if ($3 == 0) err[eflag]++; else $$ = $1 / $3; } | expression '*' expression { $$ = $1 * $3; } | expression '%' expression { if ($3 == 0) err[eflag]++; else $$ = $1 % $3; } | expression MOD expression { if ($3 == 0) err[eflag]++; else $$ = $1 % $3; } | expression '&' expression { $$ = $1 & $3; } | expression AND expression { $$ = $1 & $3; } | expression '|' expression { $$ = $1 | $3; } | expression OR expression { $$ = $1 | $3; } | expression '^' expression { $$ = $1 ^ $3; } | expression XOR expression { $$ = $1 ^ $3; } | expression SHL expression { $$ = $1 << $3; } | expression SHR expression { $$ = (($1 >> 1) & 077777) >> ($3 - 1); } | expression '<' expression { $$ = $1 < $3; } | expression '=' expression { $$ = $1 == $3; } | expression '>' expression { $$ = $1 > $3; } | expression LT expression { $$ = $1 < $3; } | expression EQ expression { $$ = $1 == $3; } | expression GT expression { $$ = $1 > $3; } | expression LE expression { $$ = $1 <= $3; } | expression GE expression { $$ = $1 >= $3; } | expression NE expression { $$ = $1 != $3; } | '[' expression ']' { $$ = $2; } | NOT expression { $$ = ~$2; } | '~' expression { $$ = ~$2; } | '!' expression { $$ = !$2; } | '+' expression %prec UNARY { $$ = $2; } | '-' expression %prec UNARY { $$ = -$2; } ; symbol: UNDECLARED | LABEL | MULTDEF | EQUATED | WASEQUATED | DEFLED ; al: { int i; if (expptr >= MAXEXP) error("Macro expansion level"); est2 = (char **) malloc((PARMMAX +4) * sizeof(char *)); expstack[expptr] = (char *)est2 ; for (i=0; i= tempmax) error(symlong); *p++ = (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c; while ((c = nextchar()) == '$') ; } while (charclass[c]==LETTER || charclass[c]==DIGIT); if (p - tempbuf > MAXSYMBOLSIZE) { if (pass2) warnprt (0, 1); p = tempbuf + MAXSYMBOLSIZE; } *p++ = '\0'; peekc = c; return(tokenofitem(UNDECLARED)); case HEXIN: { int corig = c; if (*ifptr) return (skipline(c)); while ((c = nextchar ()) == '$'); if (!numpart[c]) { peekc = c; return (corig); } leadinghex = 1; /* fall through */ } case DIGIT: if (*ifptr) return (skipline(c)); p = tempbuf; do { if (p >= tempmax) error(symlong); *p++ = (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c; while ((c = nextchar()) == '$'); } while(numpart[c]); peekc = c; if (leadinghex) { *p++ = 'h'; } *p-- = '\0'; switch(*p) { case 'o': case 'q': radix = 8; limit = 020000; *p = '\0'; break; case 'd': radix = 10; limit = 6553; *p = '\0'; break; case 'h': radix = 16; limit = 010000; *p = '\0'; break; case 'b': radix = 2; limit = 077777; *p = '\0'; break; default: radix = 10; limit = 6553; p++; break; } /* * tempbuf now points to the number, null terminated * with radix 'radix'. */ yylval.ival = 0; p = tempbuf; do { c = *p - (*p > '9' ? ('a' - 10) : '0'); if (c >= radix) { err[iflag]++; yylval.ival = 0; break; } if (yylval.ival < limit || (radix == 10 && yylval.ival == 6553 && c < 6) || (radix == 2 && yylval.ival == limit)) yylval.ival = yylval.ival * radix + c; else { err[vflag]++; yylval.ival = 0; break; } } while(*++p != '\0'); return(NUMBER); default: if (*ifptr) return(skipline(c)); switch(c) { int corig; case ';': return(skipline(c)); case '\'': if (quoteflag) return('\''); case '"': corig = c; p = tempbuf; p[1] = 0; do switch(c = nextchar()) { case '\0': case '\n': err[bflag]++; goto retstring; case '\'': case '"': if (c == corig && (c = nextchar()) != corig) { retstring: peekc = c; *p = '\0'; if ((p-tempbuf) >2) { yylval.cval = tempbuf; return(STRING); } else if (p-tempbuf == 2) { p = tempbuf; yylval.ival = *p++ ; yylval.ival |= *p<<8; return(TWOCHAR); } else { p = tempbuf; yylval.ival = *p++; return(ONECHAR); } } default: *p++ = c; } while (p < tempmax); /* * if we break out here, our string is longer than * our input line */ error("string buffer overflow"); case '<': corig = c; switch (c = nextchar ()) { case '=': return LE; case '<': return SHL; case '>': return NE; default: peekc = c; return corig; } /* break; suppress "unreachable" warning for tcc */ case '>': corig = c; switch (c = nextchar ()) { case '=': return GE; case '>': return SHR; default: peekc = c; return corig; } /* break; suppress "unreachable" warning for tcc */ case '!': corig = c; switch (c = nextchar ()) { case '=': return NE; default: peekc = c; return corig; } /* break; suppress "unreachable" warning for tcc */ case '=': corig = c; switch (c = nextchar ()) { case '=': return EQ; default: peekc = c; return corig; } /* break; suppress "unreachable" warning for tcc */ default: return(c); } } } /* * return the token associated with the string pointed to by * tempbuf. if no token is associated with the string, associate * deftoken with the string and return deftoken. * in either case, cause yylval to point to the relevant * symbol table entry. */ int tokenofitem(int deftoken) { char *p; struct item * ip; int i; int r, l, u, hash; #ifdef T_DEBUG fputs("'tokenofitem entry' ", stderr) ; fputs(tempbuf, stderr) ; #endif if (strcmp (tempbuf, "cmp") == 0 || strcmp (tempbuf, "jmp") == 0 || strcmp (tempbuf, "v") == 0 || strcmp (tempbuf, "nv") == 0) if (pass2) warnprt (1, 1); /* * binary search */ l = 0; u = (sizeof keytab/sizeof keytab[0])-1; while (l <= u) { i = (l+u)/2; ip = &keytab[i]; if ((r = strcmp(tempbuf, ip->i_string)) == 0) goto found; if (r < 0) u = i-1; else l = i+1; } /* * hash into item table */ hash = 0; p = tempbuf; while (*p) hash += *p++; hash %= ITEMTABLESIZE; ip = &itemtab[hash]; loop { if (ip->i_token == 0) break; if (strcmp(tempbuf, ip->i_string) == 0) goto found; if (++ip >= itemmax) ip = itemtab; } if (!deftoken) { i = 0 ; goto token_done ; } if (++nitems > ITEMTABLESIZE-20) error("item table overflow"); ip->i_string = malloc(strlen(tempbuf)+1); ip->i_token = deftoken; ip->i_uses = 0; ip->i_equbad = 0; strcpy(ip->i_string, tempbuf); found: if (*ifptr) { if (ip->i_token == ENDIF) { i = ENDIF ; goto token_done ; } if (ip->i_token == ELSE) { /* We must only honour the ELSE if it is not in a nested failed IF/ELSE */ char forbid = 0; char *ifstackptr; for (ifstackptr = ifstack; ifstackptr != ifptr; ++ifstackptr) { if (*ifstackptr) { forbid = 1; break; } } if (!forbid) { i = ELSE; goto token_done; } } if (ip->i_token == IF) { if (ifptr >= ifstmax) error("Too many ifs"); else *++ifptr = 1; } i = skipline(' '); goto token_done ; } yylval.itemptr = ip; i = ip->i_token; if (i == EQU) equ_bad_label=0; token_done: #ifdef T_DEBUG fputs("\t'tokenofitem exit'\n", stderr) ; #endif return(i) ; } /* * interchange two entries in the item table -- used by custom_qsort */ void interchange(int i, int j) { struct item *fp, *tp; struct item temp; fp = &itemtab[i]; tp = &itemtab[j]; temp.i_string = fp->i_string; temp.i_value = fp->i_value; temp.i_token = fp->i_token; temp.i_uses = fp->i_uses; temp.i_equbad = fp->i_equbad; fp->i_string = tp->i_string; fp->i_value = tp->i_value; fp->i_token = tp->i_token; fp->i_uses = tp->i_uses; fp->i_equbad = tp->i_equbad; tp->i_string = temp.i_string; tp->i_value = temp.i_value; tp->i_token = temp.i_token; tp->i_uses = temp.i_uses; tp->i_equbad = temp.i_equbad; } /* * quick sort -- used by putsymtab to sort the symbol table */ void custom_qsort(int m, int n) { int i, j; if (m < n) { i = m; j = n+1; loop { do i++; while(strcmp(itemtab[i].i_string, itemtab[m].i_string) < 0); do j--; while(strcmp(itemtab[j].i_string, itemtab[m].i_string) > 0); if (i < j) interchange(i, j); else break; } interchange(m, j); custom_qsort(m, j-1); custom_qsort(j+1, n); } } /* * get the next character */ int nextchar() { int c, ch; static char *earg; if (peekc != -1) { c = peekc; peekc = -1; return(c); } start: if (earg) { if (*earg) return(addtoline(*earg++)); earg = 0; } if (expptr) { if ((ch = getm()) == '\1') { /* expand argument */ ch = getm() - 'A'; if (ch >= 0 && ch < PARMMAX && est[ch]) earg = est[ch]; goto start; } if (ch == '\2') { /* local symbol */ ch = getm() - 'A'; if (ch >= 0 && ch < PARMMAX && est[ch]) { earg = est[ch]; goto start; } earg = getlocal(ch, (int)(long)est[TEMPNUM]); goto start; } return(addtoline(ch)); } ch = getc(now_file) ; /* if EOF, check for include file */ if (ch == EOF) { while (ch == EOF && now_in) { fclose(fin[now_in]) ; free(src_name[now_in]) ; now_file = fin[--now_in] ; ch = getc(now_file) ; } if (linein[now_in] < 0) { lstoff = 1 ; linein[now_in] = -linein[now_in] ; } else { lstoff = 0 ; } if (pass2 && iflist()) { lineout() ; fprintf(fout, "**** %s ****\n", src_name[now_in]) ; } } if (ch == '\n') { linein[now_in]++ ; } return(addtoline(ch)) ; } /* * skip to rest of the line -- comments and if skipped lines */ int skipline(int ac) { int c; c = ac; while (c != '\n' && c != '\0') c = nextchar(); return('\n'); } void usage() { printf( "zmac " ZMAC_VERSION #ifdef ZMAC_BETA ZMAC_BETA #endif ", a Z80 macro cross-assembler.\n" "Public domain by Bruce Norskog and others.\n" "\n" #ifdef __riscos "usage: zmac [--help] [--version] [-AbcdefghilLmnOpsStTz]\n" #else "usage: zmac [--help] [--version] [-AbcdefghilLmnOpsStz]\n" #endif " [-o outfile] [-x listfile] [filename]\n" "\n" " --help give this usage help.\n" " --version report version number.\n" " -A output AMSDOS binary file rather than default binary file.\n" " -b don't generate the m/c output at all.\n" " -c make the listing continuous, i.e. don't generate any\n" " page breaks or page headers. (This is the default.)\n" " -d make the listing discontinuous.\n" " -e omit the `error report' section in the listing.\n" " -f list instructions not assembled due to `if' expressions being\n" " false. (Normally these are not shown in the listing.)\n" " -g list only the first line of equivalent hex for a source line.\n" " -h output CP/M-ish Intel hex format (using extension `.hex')\n" " rather than default binary file (extension `.bin').\n" " -i don't list files included with `include'.\n" " -l don't generate a listing at all.\n" " -L generate listing; overrides any conflicting options.\n" " -m list macro expansions.\n" " -n omit line numbers from listing.\n" " -o assemble output to `outfile'.\n" " -O suggest possible optimisations (as warnings).\n" " -p use linefeeds for page break in listing rather than ^L.\n" " -s omit the symbol table from the listing.\n" " -S show relevant line when reporting errors.\n" " -t give terse (single-letter) error codes in listing.\n" #ifdef __riscos " -T enable DDE throwback for reporting warnings and errors.\n" #endif " -x generate listing to `listfile' (`-' for stdout).\n" " -z accept 8080-compatible instructions only; flag any\n" " Z80-specific ones as errors.\n"); } int main(int argc, char *argv[]) { struct item *ip; int i, c; #ifdef ZMAC_BETA printf ("*** THIS IS A BETA VERSION; NOT FOR GENERAL DISTRIBUTION ***\n"); #endif if(argc==1) usage(),exit(0); if(argc>=2) { if(strcmp(argv[1],"--help")==0) usage(),exit(0); else if(strcmp(argv[1],"--version")==0) puts("zmac " ZMAC_VERSION #ifdef ZMAC_BETA ZMAC_BETA #endif ),exit(0); } fout = stdout ; fin[0] = stdin ; now_file = stdin ; *bin = *listf = 0; optnerr = 0; while((c = getoptn(argc,argv, #ifdef __riscos "AbcdefghilLmno:OpsStTx:z" #else "AbcdefghilLmno:OpsStx:z" #endif )) != EOF) { switch(c) { case 'A': /* AMSDOS binary -mep */ output_amsdos = 1; output_hex = 0; break; case 'b': /* no binary */ bopt = 0; break; case 'c': /* continuous listing */ continuous_listing = 1; break; case 'd': /* discontinuous listing */ continuous_listing = 0; break; case 'e': /* error list only */ eopt = 0; edef = 0; break; case 'f': /* print if skipped lines */ fopt++; fdef++; break; case 'g': /* do not list extra code */ gopt = 0; gdef = 0; break; case 'h': /* output .hex not .bin -rjm */ output_hex = 1; output_amsdos = 0; break; case 'i': /* do not list include files */ iopt = 1 ; break; case 'l': /* no list */ lopt++; break; case 'L': /* force listing of everything */ lston++; break; case 'm': /* print macro expansions */ mdef++; mopt++; break; case 'n': /* put line numbers off */ nopt-- ; break; case 'o': /* specify m/c output file */ strcpy(bin, optnarg); break; case 'O': /* suggest optimisations */ suggest_optimise = 1; break; case 'p': /* put out four \n's for eject */ popt-- ; break; case 's': /* don't produce a symbol list */ sopt++; break; case 'S': /* show line which caused error */ show_error_line = 1; break; case 't': /* terse error messages in listing */ terse_lst_errors = 1; break; #ifdef __riscos case 'T': /* RISC OS throwback -mep */ riscos_thbk = 1; break; #endif case 'x': /* specify listing file */ if(strcmp(optnarg, "-") == 0) oldoopt++; /* list to stdout (old `-o') */ else strcpy(listf, optnarg); break; case 'z': /* 8080-compatible ops only */ output_8080_only = 1; break; case '?': default: /* error */ justerror("Unknown option or missing argument"); break; } } if(optnind != argc-1) justerror("Missing, extra or mispositioned argument"); atexit (doatexit); sourcef = argv[optnind]; strcpy(src, sourcef); if ((now_file = fopen(src, "r")) == NULL) { /* If filename has no pre-existing suffix, then try .z */ suffix_if_none (src, "z"); if ((now_file = fopen(src, "r")) == NULL) fileerror("Cannot open source file", src); } now_in = 0 ; fin[now_in] = now_file ; src_name[now_in] = src ; #ifdef __riscos riscos_set_csd(src); /* -mep */ #endif /* If we haven't got a bin file filename, then create one from the * source filename (.hex extension if option -h is specified). */ if (*bin == 0) { strcpy(bin, sourcef); if (output_hex) suffix(bin,"hex"); else suffix(bin,"bin"); } if (bopt) if (( fbuf = fopen(bin, output_hex ? "w" : "wb")) == NULL) fileerror("Cannot create binary file", bin); if (output_amsdos) for(i=0; i<128; i++) putc(0,fbuf); /* -mep */ if (!lopt && !oldoopt) { /* If we've not got a filename for the listing file * (-x option) then create one from the source filename * (.lst extension) */ if( *listf == 0 ) { strcpy(listf, sourcef); suffix(listf,"lst"); } if ((fout = fopen(listf, "w")) == NULL) fileerror("Cannot create list file", listf); } else fout = stdout ; strcpy(mtmp, sourcef); suffix(mtmp,"tmp"); mfile = mfopen(mtmp,"w+b") ; if (mfile == NULL) { fileerror("Cannot create temp file", mtmp); } /* * get the time */ time(&now); timp = ctime(&now); timp[16] = 0; timp[24] = 0; title = sourcef; /* * pass 1 */ #ifdef DEBUG fputs("DEBUG-pass 1\n", stderr) ; #endif setvars(); yyparse(); pass2++; ip = &itemtab[-1]; while (++ip < itemmax) { /* reset use count */ ip->i_uses = 0 ; /* set macro names, equated and defined names */ switch (ip->i_token) { case MNAME: ip->i_token = OLDMNAME; break; case EQUATED: ip->i_token = WASEQUATED; break; case DEFLED: ip->i_token = UNDECLARED; break; } } setvars(); fseek(now_file, (long)0, 0); #ifdef DEBUG fputs("DEBUG- pass 2\n", stderr) ; #endif yyparse(); if (bopt) { flushbin(); if (output_hex) { putc(':', fbuf); if (xeq_flag) { puthex(0, fbuf); puthex(xeq >> 8, fbuf); puthex(xeq, fbuf); puthex(1, fbuf); puthex(255-(xeq >> 8)-xeq, fbuf); } else for (i = 0; i < 10; i++) putc('0', fbuf); putc('\n', fbuf); } if (output_amsdos) { char leafname[] = "FILENAMEBIN"; unsigned int chk; unsigned int filelen = dollarsign - first_org_store; if (filelen & 0x7f) { putc (0x1a, fbuf); /* CP/M EOF char */ } rewind(fbuf); chk=0; putc(0,fbuf); for(i=0;i<11;i++) { putc(leafname[i],fbuf); chk+=leafname[i]; } for(i=0;i<6;i++) putc(0,fbuf); putc(2,fbuf); /* Unprotected binary */ chk+=2; putc(0,fbuf); putc(0,fbuf); putc(first_org_store & 0xFF,fbuf); chk+=first_org_store & 0xFF; putc(first_org_store >> 8,fbuf); chk+=first_org_store >> 8; putc(0,fbuf); putc(filelen & 0xFF,fbuf); chk+=filelen & 0xFF; putc(filelen >> 8,fbuf); chk+=filelen >> 8; /* Next bit should be entry address really */ putc(first_org_store & 0xFF,fbuf); chk+=first_org_store & 0xFF; putc(first_org_store >> 8,fbuf); chk+=first_org_store >> 8; for(i=28;i<64;i++) putc(0,fbuf); putc(filelen & 0xFF,fbuf); chk+=filelen & 0xFF; putc(filelen >> 8,fbuf); chk+=filelen >> 8; putc(0,fbuf); /* this would be used if length>64K */ putc(chk & 0xFF,fbuf); putc(chk >> 8,fbuf); } fflush(fbuf); } if (!lopt) fflush(fout); if (*writesyms) outsymtab(writesyms); if (eopt) erreport(); if (!lopt && !sopt) putsymtab(); if (!lopt) { eject(); fflush(fout); } exit(had_errors); return(had_errors); /* main () does return int, after all... */ } void doatexit (void) { #ifdef __riscos if (riscos_throwback_started) { _swix(DDEUtils_ThrowbackEnd,0); } _swix(DDEUtils_Prefix,1,0); /* Unset CSD */ #endif } /* * set some data values before each pass */ void setvars() { int i; peekc = -1; linein[now_in] = linecnt = 0; exp_number = 0; emitptr = emitbuf; lineptr = linebuf; ifptr = ifstack; expifp = expif; *ifptr = 0; dollarsign = 0; olddollar = 0; phaseflag = 0; for (i=0; ii_token == UNDECLARED) { nitems--; continue; } if (fp->i_token == 0) continue; tp++; if (tp != fp) { tp->i_string = fp->i_string; tp->i_value = fp->i_value; tp->i_token = fp->i_token; tp->i_uses = fp->i_uses ; tp->i_equbad = fp->i_equbad ; } } tp++; tp->i_string = "{"; /* sort the table */ custom_qsort(0, nitems-1); title = "** Symbol Table **"; rows = (nitems+3) / 3; if (rows+5+line > 60) eject(); lineout(); fprintf(fout,"\n\n\nSymbol Table:\n\n"); line += 4; for (i=0; ii_token; c = ' ' ; if (t == EQUATED || t == DEFLED) c = '=' ; if (tp->i_uses == 0) c1 = '+' ; else c1 = ' ' ; fprintf(fout, "%-15s%c%4x%c ", tp->i_string, c, tp->i_value & 0xffff, c1); } } lineout(); putc('\n', fout); } } /* * put out error report */ void erreport() { int i, numerr; if (line > 50) eject(); lineout(); numerr = 0; for (i=0; i 55) eject(); lineout(); fprintf(fout, "\n%6d\tsymbol%s\n", DO_PLURAL(nitems)); fprintf(fout, "%6d\tbyte%s\n", DO_PLURAL(nbytes)); line += 2; if (mfptr) { if (line > 53) eject(); lineout(); fprintf(fout, "\n%6d\tmacro call%s\n", DO_PLURAL(exp_number)); fprintf(fout, "%6d\tmacro byte%s\n", DO_PLURAL(mfptr)); fprintf(fout, "%6d\tinvented symbol%s\n", DO_PLURAL(invented/2)); line += 3; } } /* * lexical analyser for macro definition */ void mlex() { char *p; int c; int t; /* * move text onto macro file, changing formal parameters */ #ifdef M_DEBUG fprintf(stderr,"enter 'mlex'\n") ; #endif inmlex++; c = nextchar(); loop { switch(charclass[c]) { case DIGIT: while (numpart[c]) { putm(c); c = nextchar(); } continue; case STARTER: case LETTER: t = 0; p = tempbuf+MAXSYMBOLSIZE+2; do { if (p >= tempmax) error(symlong); *p++ = c; if (t < MAXSYMBOLSIZE) tempbuf[t++] = (c >= 'A' && c <= 'Z') ? c+'a'-'A' : c; else if (pass2) warnprt (0, 1); c = nextchar(); } while (charclass[c]==LETTER || charclass[c]==DIGIT); tempbuf[t] = 0; *p++ = '\0'; p = tempbuf+MAXSYMBOLSIZE+2; t = tokenofitem(0); if (t != MPARM) while (*p) putm(*p++); else { if (*(yylval.itemptr->i_string) == '?') putm('\2'); else putm('\1'); putm(yylval.itemptr->i_value + 'A'); } if (t == ENDM) goto done; continue; case F_END: if (expptr) { popsi(); c = nextchar(); continue; } goto done; default: if (c == '\n') { linecnt++; } if (c != '\1') putm(c); c = nextchar(); } } /* * finish off the file entry */ done: while(c != EOF && c != '\n' && c != '\0') c = nextchar(); linecnt++; putm('\n'); putm('\n'); putm(0); for (c=0; c linebuf) lineptr--; } } /* * return a unique name for a local symbol * c is the parameter number, n is the macro number. */ char *getlocal(int c, int n) { static char local_label[10]; invented++; if (c >= 26) c += 'a' - '0'; sprintf(local_label, "?%c%04d", c+'a', n) ; return(local_label); } /* * read in a symbol table */ void insymtab(char *name) { struct stab *t; int s, i; FILE *sfile; t = (struct stab *) tempbuf; decanonicalise (name); if ((sfile = fopen(name, "rb")) == NULL) return; fread((char *)t, 1, sizeof *t, sfile); if (t->t_value != SYMMAJIC) { fclose (sfile); return; } s = t->t_token; for (i=0; ii_token = t->t_token; yylval.itemptr->i_value = t->t_value; if (t->t_token == MACRO) yylval.itemptr->i_value += mfptr; } while ((s = fread(tempbuf, 1, TEMPBUFSIZE, sfile)) > 0) { mfptr += s; mfwrite(tempbuf, 1, s, mfile) ; } fclose (sfile); } /* * write out symbol table */ void outsymtab(char *name) { struct stab *t; struct item *ip; int i; FILE *sfile; t = (struct stab *) tempbuf; decanonicalise (name); if ((sfile = fopen(name, "wb")) == NULL) return; for (ip=itemtab; ipi_token == UNDECLARED) { ip->i_token = 0; nitems--; } } copyname(title, (char *)t); t->t_value = SYMMAJIC; t->t_token = nitems; fwrite((char *)t, 1, sizeof *t, sfile); for (ip=itemtab; ipi_token != 0) { t->t_token = ip->i_token; t->t_value = ip->i_value; copyname(ip->i_string, (char *)t); fwrite((char *)t, 1, sizeof *t, sfile); } } mfseek(mfile, (long)0, 0); while((i = mfread(tempbuf, 1, TEMPBUFSIZE, mfile) ) > 0) fwrite(tempbuf, 1, i, sfile); fclose (sfile); } /* * copy a name into the symbol file */ void copyname(char *st1, char *st2) { char *s1, *s2; int i; i = (MAXSYMBOLSIZE+2) & ~01; s1 = st1; s2 = st2; while((*s2++ = *s1++)) i--; /* -Wall-ishness :-) -RJM */ while(--i > 0) *s2++ = '\0'; } /* get the next source file */ void next_source(char *sp) { if(now_in == NEST_IN -1) error("Too many nested includes") ; decanonicalise (sp); if ((now_file = fopen(sp, "r")) == NULL) { #ifdef __riscos if (riscos_thbk) riscos_throwback(2,src_name[now_in],linein[now_in],"Cannot open include file"); #endif fileerror("Cannot open include file", sp) ; } if (pass2 && iflist()) { lineout() ; fprintf(fout, "**** %s ****\n",sp) ; } /* save the list control flag with the current line number */ if (lstoff) linein[now_in] = - linein[now_in] ; /* no list if include files are turned off */ lstoff |= iopt ; /* save the new file descriptor. */ fin[++now_in] = now_file ; /* start with line 0 */ linein[now_in] = 0 ; /* save away the file name */ src_name[now_in] = malloc(strlen(sp)+1) ; strcpy(src_name[now_in],sp) ; } #ifdef __riscos /* * On entry sp should point to the full pathname of a file in RISC OS form. * Searches for the last dot, and sets the local CSD to that path. * Does not corrupt the string. */ void riscos_set_csd(char *sp) { char *s1 = strrchr (sp, '.'); if (s1 != NULL) { *s1=0; _swix(DDEUtils_Prefix,1,sp); *s1='.'; } } void riscos_throwback(int severity, char *file, int line, char *error) { if (riscos_throwback_started==0) { riscos_throwback_started=1; *riscos_thbkf=0; _swix(DDEUtils_ThrowbackStart,0); } if (strcmp(file, riscos_thbkf)!=0) { _swix(DDEUtils_ThrowbackSend,4+1,0,file); /* Notify of a change of file */ strcpy(riscos_thbkf,file); } _swix(DDEUtils_ThrowbackSend,32+16+8+4+1,1,file,line,severity,error); } #endif