3887 lines
71 KiB
Plaintext
3887 lines
71 KiB
Plaintext
|
%{
|
||
|
/*
|
||
|
* 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 <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <time.h>
|
||
|
#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<outbinp; p++) {
|
||
|
if (output_hex)
|
||
|
puthex(*p, fbuf);
|
||
|
else
|
||
|
fputc(*p, fbuf);
|
||
|
check += *p;
|
||
|
}
|
||
|
if (output_hex) {
|
||
|
puthex(256-check, fbuf);
|
||
|
putc('\n', fbuf);
|
||
|
}
|
||
|
outbinp = outbin;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* put out one byte of hex
|
||
|
*/
|
||
|
void puthex(char byte, FILE *buf)
|
||
|
{
|
||
|
putc(hexadec[(byte >> 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<n; i++) {
|
||
|
lineout();
|
||
|
putc('\n', fout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Error handling - pass 1
|
||
|
*/
|
||
|
void lsterr1()
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i <= 4; i++)
|
||
|
if (err[i]) {
|
||
|
errorprt(i);
|
||
|
err[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Error handling - pass 2.
|
||
|
*/
|
||
|
void lsterr2(int lst)
|
||
|
{
|
||
|
int i;
|
||
|
for (i=0; i<FLAGS; i++)
|
||
|
if (err[i]) {
|
||
|
if (lst) {
|
||
|
lineout();
|
||
|
/* verbose inline error messages now,
|
||
|
* must override with `-t' to get old
|
||
|
* behaviour. -rjm
|
||
|
*/
|
||
|
if (terse_lst_errors)
|
||
|
putc(errlet[i], fout);
|
||
|
else
|
||
|
fprintf(fout,"*** %s error ***",
|
||
|
errname[i]);
|
||
|
putc('\n', fout);
|
||
|
}
|
||
|
err[i] = 0;
|
||
|
keeperr[i]++;
|
||
|
if (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<FLAGS; i++)
|
||
|
if (err[i])
|
||
|
j++;
|
||
|
if (expptr)
|
||
|
return(mopt || j);
|
||
|
if (eopt && j)
|
||
|
return(1);
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* moved out of %{..%} bit in parse routine because `bison -y'
|
||
|
* didn't like it... -rjm
|
||
|
*/
|
||
|
char *cp;
|
||
|
|
||
|
int list_tmp1,list_tmp2;
|
||
|
int equ_bad_label=0;
|
||
|
|
||
|
%}
|
||
|
|
||
|
%union {
|
||
|
struct item *itemptr;
|
||
|
int ival;
|
||
|
char *cval;
|
||
|
}
|
||
|
|
||
|
%token <cval> STRING
|
||
|
%token <itemptr> NOOPERAND
|
||
|
%token <itemptr> ARITHC
|
||
|
%token ADD
|
||
|
%token <itemptr> LOGICAL
|
||
|
%token <itemptr> AND
|
||
|
%token <itemptr> OR
|
||
|
%token <itemptr> XOR
|
||
|
%token <itemptr> BIT
|
||
|
%token CALL
|
||
|
%token <itemptr> INCDEC
|
||
|
%token <itemptr> DJNZ
|
||
|
%token EX
|
||
|
%token <itemptr> IM
|
||
|
%token PHASE
|
||
|
%token DEPHASE
|
||
|
%token <itemptr> IN
|
||
|
%token JP
|
||
|
%token <itemptr> JR
|
||
|
%token LD
|
||
|
%token <itemptr> OUT
|
||
|
%token <itemptr> PUSHPOP
|
||
|
%token <itemptr> RET
|
||
|
%token <itemptr> SHIFT
|
||
|
%token <itemptr> RST
|
||
|
%token <itemptr> REGNAME
|
||
|
%token <itemptr> ACC
|
||
|
%token <itemptr> C
|
||
|
%token <itemptr> RP
|
||
|
%token <itemptr> HL
|
||
|
%token <itemptr> INDEX
|
||
|
%token <itemptr> AF
|
||
|
%token <itemptr> SP
|
||
|
%token <itemptr> MISCREG
|
||
|
%token F
|
||
|
%token <itemptr> COND
|
||
|
%token <itemptr> SPCOND
|
||
|
%token <ival> NUMBER
|
||
|
%token <itemptr> UNDECLARED
|
||
|
%token END
|
||
|
%token ORG
|
||
|
%token DEFB
|
||
|
%token DEFS
|
||
|
%token DEFW
|
||
|
%token EQU
|
||
|
%token DEFL
|
||
|
%token <itemptr> LABEL
|
||
|
%token <itemptr> EQUATED
|
||
|
%token <itemptr> WASEQUATED
|
||
|
%token <itemptr> DEFLED
|
||
|
%token <itemptr> MULTDEF
|
||
|
%token <ival> MOD
|
||
|
%token <ival> SHL
|
||
|
%token <ival> SHR
|
||
|
%token <ival> NOT
|
||
|
%token <ival> LT
|
||
|
%token <ival> GT
|
||
|
%token <ival> EQ
|
||
|
%token <ival> LE
|
||
|
%token <ival> GE
|
||
|
%token <ival> NE
|
||
|
%token IF
|
||
|
%token ELSE
|
||
|
%token ENDIF
|
||
|
%token <itemptr> ARGPSEUDO
|
||
|
%token <itemptr> LIST
|
||
|
%token <itemptr> MINMAX
|
||
|
%token MACRO
|
||
|
%token <itemptr> MNAME
|
||
|
%token <itemptr> OLDMNAME
|
||
|
%token ARG
|
||
|
%token ENDM
|
||
|
%token MPARM
|
||
|
%token <ival> ONECHAR
|
||
|
%token <ival> TWOCHAR
|
||
|
|
||
|
%type <itemptr> label.part symbol
|
||
|
%type <ival> reg evenreg realreg mem pushable bcdesp bcdehlsp mar condition
|
||
|
%type <ival> 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; i<PARMMAX; i++) {
|
||
|
if (est2[i])
|
||
|
#ifdef M_DEBUG
|
||
|
fprintf (stderr, "[Freeing2 arg%u(%p)]\n", i, est2[i]),
|
||
|
#endif
|
||
|
free(est2[i]);
|
||
|
}
|
||
|
free(est2);
|
||
|
est2 = NULL;
|
||
|
}
|
||
|
|
||
|
while(yychar != '\n' && yychar != '\0') yychar = yylex();
|
||
|
list(dollarsign);
|
||
|
yyclearin;yyerrok;
|
||
|
}
|
||
|
;
|
||
|
|
||
|
colonordot:
|
||
|
':'
|
||
|
|
|
||
|
'.'
|
||
|
;
|
||
|
|
||
|
maybecolon:
|
||
|
/* empty */
|
||
|
|
|
||
|
':'
|
||
|
;
|
||
|
|
||
|
label.part:
|
||
|
/* empty */
|
||
|
{ $$ = NULL; }
|
||
|
|
|
||
|
colonordot symbol {
|
||
|
switch($2->i_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<PARMMAX; i++)
|
||
|
est2[i] = 0;
|
||
|
arg_flag++;
|
||
|
}
|
||
|
;
|
||
|
|
||
|
|
||
|
arg_on:
|
||
|
{ arg_flag++; }
|
||
|
;
|
||
|
|
||
|
arg_off:
|
||
|
{ arg_flag = 0; }
|
||
|
;
|
||
|
|
||
|
setqf:
|
||
|
{ quoteflag++; }
|
||
|
;
|
||
|
|
||
|
clrqf:
|
||
|
{ quoteflag = 0; }
|
||
|
|
||
|
;
|
||
|
|
||
|
%%
|
||
|
|
||
|
#define F_END 0
|
||
|
#define OTHER 1
|
||
|
#define SPACE 2
|
||
|
#define DIGIT 3
|
||
|
#define LETTER 4
|
||
|
#define STARTER 5
|
||
|
#define HEXIN 6
|
||
|
|
||
|
|
||
|
/*
|
||
|
* This is the table of character classes. It is used by the lexical
|
||
|
* analyser. (yylex())
|
||
|
*/
|
||
|
char charclass[] = {
|
||
|
F_END, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
|
||
|
OTHER, SPACE, OTHER, OTHER, OTHER, SPACE, OTHER, OTHER,
|
||
|
OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
|
||
|
OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
|
||
|
SPACE, OTHER, OTHER, HEXIN, HEXIN, OTHER, HEXIN, OTHER,
|
||
|
OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER,
|
||
|
DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT,
|
||
|
DIGIT, DIGIT, OTHER, OTHER, OTHER, OTHER, OTHER, STARTER,
|
||
|
STARTER,LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
|
||
|
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
|
||
|
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
|
||
|
LETTER, LETTER, LETTER, OTHER, OTHER, OTHER, OTHER, LETTER,
|
||
|
OTHER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
|
||
|
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
|
||
|
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
|
||
|
LETTER, LETTER, LETTER, OTHER, OTHER, OTHER, OTHER, OTHER,
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
* the following table tells which characters are parts of numbers.
|
||
|
* The entry is non-zero for characters which can be parts of numbers.
|
||
|
*/
|
||
|
char numpart[] = {
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
||
|
'8', '9', 0, 0, 0, 0, 0, 0,
|
||
|
0, 'A', 'B', 'C', 'D', 'E', 'F', 0,
|
||
|
'H', 0, 0, 0, 0, 0, 0, 'O',
|
||
|
0, 'Q', 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 'a', 'b', 'c', 'd', 'e', 'f', 0,
|
||
|
'h', 0, 0, 0, 0, 0, 0, 'o',
|
||
|
0, 'q', 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* the following table is a list of assembler mnemonics;
|
||
|
* for each mnemonic the associated machine-code bit pattern
|
||
|
* and symbol type are given.
|
||
|
*/
|
||
|
struct item keytab[] = {
|
||
|
{"a", 7, ACC, 0},
|
||
|
{"adc", 1, ARITHC, 0},
|
||
|
{"add", 0, ADD, 0},
|
||
|
{"af", 060, AF, 0},
|
||
|
{"and", 4, AND, 0},
|
||
|
{"ascii",0, DEFB, 0},
|
||
|
{"b", 0, REGNAME, 0},
|
||
|
{"bc", 0, RP, 0},
|
||
|
{"bit", 0145500,BIT, 0},
|
||
|
{"block",0, DEFS, 0},
|
||
|
{"byte",0, DEFB, 0},
|
||
|
{"c", 1, C, 0},
|
||
|
{"call",0315, CALL, 0},
|
||
|
{"ccf", 077, NOOPERAND, 0},
|
||
|
{"cmp", 7, LOGICAL, 0}, /* -cdk */
|
||
|
{"cond",0, IF, 0},
|
||
|
{"cp", 7, LOGICAL, 0},
|
||
|
{"cpd", 0166651,NOOPERAND, 0},
|
||
|
{"cpdr",0166671,NOOPERAND, 0},
|
||
|
{"cpi", 0166641,NOOPERAND, 0},
|
||
|
{"cpir",0166661,NOOPERAND, 0},
|
||
|
{"cpl", 057, NOOPERAND, 0},
|
||
|
{"d", 2, REGNAME, 0},
|
||
|
{"daa", 0047, NOOPERAND, 0},
|
||
|
{"db", 0, DEFB, 0},
|
||
|
{"de", 020, RP, 0},
|
||
|
{"dec", 1, INCDEC, 0},
|
||
|
{"defb",0, DEFB, 0},
|
||
|
{"defl",0, DEFL, 0},
|
||
|
{"defm",0, DEFB, 0},
|
||
|
{"defs",0, DEFS, 0},
|
||
|
{"defw",0, DEFW, 0},
|
||
|
{"dephase",0, DEPHASE, 0},
|
||
|
{"di", 0363, NOOPERAND, 0},
|
||
|
{"djnz",020, DJNZ, 0},
|
||
|
{"ds", 0, DEFS, 0},
|
||
|
{"dw", 0, DEFW, 0},
|
||
|
{"e", 3, REGNAME, 0},
|
||
|
{"ei", 0373, NOOPERAND, 0},
|
||
|
{"eject",1, LIST, 0},
|
||
|
{"elist",3, LIST, 0},
|
||
|
{"else",0, ELSE, 0},
|
||
|
{"end", 0, END, 0},
|
||
|
{"endc",0, ENDIF, 0},
|
||
|
{"endif",0, ENDIF, 0},
|
||
|
{"endm", 0, ENDM, 0},
|
||
|
{"eq", 0, EQ, 0},
|
||
|
{"equ", 0, EQU, 0},
|
||
|
{"ex", 0, EX, 0},
|
||
|
{"exx", 0331, NOOPERAND, 0},
|
||
|
{"f", 0, F, 0},
|
||
|
{"flist",4, LIST, 0},
|
||
|
{"ge", 0, GE, 0},
|
||
|
{"glist",5, LIST, 0},
|
||
|
{"gt", 0, GT, 0},
|
||
|
{"h", 4, REGNAME, 0},
|
||
|
{"halt",0166, NOOPERAND, 0},
|
||
|
{"hl", 040, HL, 0},
|
||
|
{"i", 0, MISCREG, 0},
|
||
|
{"if", 0, IF, 0},
|
||
|
{"im", 0166506,IM, 0},
|
||
|
{"in", 0333, IN, 0},
|
||
|
{"inc", 0, INCDEC, 0},
|
||
|
{"include", 3, ARGPSEUDO, 0},
|
||
|
{"ind", 0166652,NOOPERAND, 0},
|
||
|
{"indr",0166672,NOOPERAND, 0},
|
||
|
{"ini", 0166642,NOOPERAND, 0},
|
||
|
{"inir",0166662,NOOPERAND, 0},
|
||
|
{"ix", 0156440,INDEX, 0},
|
||
|
{"iy", 0176440,INDEX, 0},
|
||
|
{"jmp", 0303, JP, 0}, /* -cdk */
|
||
|
{"jp", 0303, JP, 0},
|
||
|
{"jr", 040, JR, 0},
|
||
|
{"l", 5, REGNAME, 0},
|
||
|
{"ld", 0, LD, 0},
|
||
|
{"ldd", 0166650,NOOPERAND, 0},
|
||
|
{"lddr",0166670,NOOPERAND, 0},
|
||
|
{"ldi", 0166640,NOOPERAND, 0},
|
||
|
{"ldir",0166660,NOOPERAND, 0},
|
||
|
{"le", 0, LE, 0},
|
||
|
{"list",0, LIST, 0},
|
||
|
{"lt", 0, LT, 0},
|
||
|
{"m", 070, COND, 0},
|
||
|
{"macro",0, MACRO, 0},
|
||
|
{"max", 1, MINMAX, 0},
|
||
|
{"min", 0, MINMAX, 0},
|
||
|
{"mlist",6, LIST, 0},
|
||
|
{"mod", 0, MOD, 0},
|
||
|
{"nc", 020, SPCOND, 0},
|
||
|
{"ne", 0, NE, 0},
|
||
|
{"neg", 0166504,NOOPERAND, 0},
|
||
|
{"nolist",-1, LIST, 0},
|
||
|
{"nop", 0, NOOPERAND, 0},
|
||
|
{"not", 0, NOT, 0},
|
||
|
{"nv", 040, COND, 0},
|
||
|
{"nz", 0, SPCOND, 0},
|
||
|
{"or", 6, OR, 0},
|
||
|
{"org", 0, ORG, 0},
|
||
|
{"otdr",0166673,NOOPERAND, 0},
|
||
|
{"otir",0166663,NOOPERAND, 0},
|
||
|
{"out", 0323, OUT, 0},
|
||
|
{"outd",0166653,NOOPERAND, 0},
|
||
|
{"outi",0166643,NOOPERAND, 0},
|
||
|
{"p", 060, COND, 0},
|
||
|
{"pe", 050, COND, 0},
|
||
|
{"phase",0, PHASE, 0},
|
||
|
{"po", 040, COND, 0},
|
||
|
{"pop", 0301, PUSHPOP, 0},
|
||
|
{"push", 0305, PUSHPOP, 0},
|
||
|
{"r", 010, MISCREG, 0},
|
||
|
{"read", 3, ARGPSEUDO, 0},
|
||
|
{"res", 0145600,BIT, 0},
|
||
|
{"ret", 0311, RET, 0},
|
||
|
{"reti",0166515,NOOPERAND, 0},
|
||
|
{"retn",0166505,NOOPERAND, 0},
|
||
|
{"rl", 2, SHIFT, 0},
|
||
|
{"rla", 027, NOOPERAND, 0},
|
||
|
{"rlc", 0, SHIFT, 0},
|
||
|
{"rlca",07, NOOPERAND, 0},
|
||
|
{"rld", 0166557,NOOPERAND, 0},
|
||
|
{"rmem",0, DEFS, 0},
|
||
|
{"rr", 3, SHIFT, 0},
|
||
|
{"rra", 037, NOOPERAND, 0},
|
||
|
{"rrc", 1, SHIFT, 0},
|
||
|
{"rrca",017, NOOPERAND, 0},
|
||
|
{"rrd", 0166547,NOOPERAND, 0},
|
||
|
{"rst", 0307, RST, 0},
|
||
|
{"rsym",1, ARGPSEUDO, 0},
|
||
|
{"sbc", 3, ARITHC, 0},
|
||
|
{"scf", 067, NOOPERAND, 0},
|
||
|
{"set", 0145700,BIT, 0},
|
||
|
{"shl", 0, SHL, 0},
|
||
|
{"shr", 0, SHR, 0},
|
||
|
{"sla", 4, SHIFT, 0},
|
||
|
{"sll", 6, SHIFT, 0}, /* Undocumented */
|
||
|
{"sp", 060, SP, 0},
|
||
|
{"space",2, LIST, 0},
|
||
|
{"sra", 5, SHIFT, 0},
|
||
|
{"srl", 7, SHIFT, 0},
|
||
|
{"sub", 2, LOGICAL, 0},
|
||
|
{"text",0, DEFB, 0},
|
||
|
{"title",0, ARGPSEUDO, 0},
|
||
|
{"v", 050, COND, 0},
|
||
|
{"word",0, DEFW, 0},
|
||
|
{"wsym",2, ARGPSEUDO, 0},
|
||
|
{"xor", 5, XOR, 0},
|
||
|
{"z", 010, SPCOND, 0}
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* user-defined items are tabulated in the following table.
|
||
|
*/
|
||
|
|
||
|
struct item itemtab[ITEMTABLESIZE];
|
||
|
struct item *itemmax = itemtab+ITEMTABLESIZE;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* lexical analyser, called by yyparse.
|
||
|
*/
|
||
|
int yylex()
|
||
|
{
|
||
|
int c;
|
||
|
char *p;
|
||
|
int radix;
|
||
|
int limit;
|
||
|
int leadinghex = 0;
|
||
|
|
||
|
if (arg_flag)
|
||
|
return(getarg());
|
||
|
loop switch(charclass[c = nextchar()]) {
|
||
|
case F_END:
|
||
|
if (expptr) {
|
||
|
popsi();
|
||
|
continue;
|
||
|
} else return(0);
|
||
|
|
||
|
case SPACE:
|
||
|
break;
|
||
|
case LETTER:
|
||
|
case STARTER:
|
||
|
p = tempbuf;
|
||
|
do {
|
||
|
if (p >= 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; i<FLAGS; i++) err[i] = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* print out an error message and die
|
||
|
*/
|
||
|
void error(char *as)
|
||
|
{
|
||
|
*linemax = 0;
|
||
|
fprintf(fout, "%s\n", linebuf);
|
||
|
fflush(fout);
|
||
|
fprintf(stderr, "%s\n", as) ;
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* alternate version
|
||
|
*/
|
||
|
void fileerror(char *as,char *filename)
|
||
|
{
|
||
|
*linemax = 0;
|
||
|
if (fout != NULL && fout != stdout)
|
||
|
fprintf(fout, "%s\n", linebuf);
|
||
|
fflush(fout);
|
||
|
fprintf(stderr, "%s `%s'\n", as, filename) ;
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* alternate alternate version
|
||
|
*/
|
||
|
void justerror(char *as)
|
||
|
{
|
||
|
fprintf(stderr, "%s\n", as) ;
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* output the symbol table
|
||
|
*/
|
||
|
void putsymtab()
|
||
|
{
|
||
|
struct item *tp, *fp;
|
||
|
int i, j, k, t, rows;
|
||
|
char c, c1 ;
|
||
|
|
||
|
if (!nitems)
|
||
|
return;
|
||
|
|
||
|
/* compact the table so unused and UNDECLARED entries are removed */
|
||
|
tp = &itemtab[-1];
|
||
|
for (fp = itemtab; fp<itemmax; fp++) {
|
||
|
if (fp->i_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; i<rows; i++) {
|
||
|
for(j=0; j<3; j++) {
|
||
|
k = rows*j+i;
|
||
|
if (k < nitems) {
|
||
|
tp = &itemtab[k];
|
||
|
t = tp->i_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<FLAGS; i++) numerr += keeperr[i];
|
||
|
if (numerr) {
|
||
|
fputs("\n\n\nError report:\n\n", fout);
|
||
|
fprintf(fout, "%6d error%s\n", DO_PLURAL(numerr));
|
||
|
line += 5;
|
||
|
} else {
|
||
|
fputs("\n\n\nStatistics:\n", fout);
|
||
|
line += 3;
|
||
|
}
|
||
|
|
||
|
for (i=0; i<FLAGS; i++)
|
||
|
if (keeperr[i]) {
|
||
|
lineout();
|
||
|
if (terse_lst_errors)
|
||
|
/* no plural on this because it would
|
||
|
* odd, I think. -rjm
|
||
|
*/
|
||
|
fprintf(fout, "%6d %c -- %s error\n",
|
||
|
keeperr[i], errlet[i], errname[i]);
|
||
|
else
|
||
|
/* can't use DO_PLURAL for this due to
|
||
|
* the %s in the middle... -rjm
|
||
|
*/
|
||
|
fprintf(fout, "%6d %s error%s\n",
|
||
|
keeperr[i], errname[i],
|
||
|
(keeperr[i]==1)?"":"s");
|
||
|
}
|
||
|
|
||
|
if (line > 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<ITEMTABLESIZE; c++)
|
||
|
if (itemtab[c].i_token == MPARM) {
|
||
|
itemtab[c].i_token = UNDECLARED;
|
||
|
}
|
||
|
inmlex = 0;
|
||
|
#ifdef M_DEBUG
|
||
|
fprintf(stderr,"exit 'mlex'\n") ;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* lexical analyser for the arguments of a macro call
|
||
|
*/
|
||
|
int getarg()
|
||
|
{
|
||
|
int c;
|
||
|
char *p;
|
||
|
static int comma;
|
||
|
|
||
|
*tempbuf = 0;
|
||
|
yylval.cval = tempbuf;
|
||
|
while(charclass[c = nextchar()] == SPACE);
|
||
|
|
||
|
switch(c) {
|
||
|
|
||
|
case '\0':
|
||
|
popsi();
|
||
|
case '\n':
|
||
|
case ';':
|
||
|
comma = 0;
|
||
|
return(skipline(c));
|
||
|
|
||
|
case ',':
|
||
|
if (comma) {
|
||
|
comma = 0;
|
||
|
return(',');
|
||
|
}
|
||
|
else {
|
||
|
comma++;
|
||
|
return(ARG);
|
||
|
}
|
||
|
|
||
|
case '\'':
|
||
|
p = tempbuf;
|
||
|
do switch (c = nextchar()) {
|
||
|
case '\0':
|
||
|
case '\n':
|
||
|
peekc = c;
|
||
|
*p = 0;
|
||
|
err[bflag]++;
|
||
|
return(ARG);
|
||
|
case '\'':
|
||
|
if ((c = nextchar()) != '\'') {
|
||
|
peekc = c;
|
||
|
*p = '\0';
|
||
|
comma++;
|
||
|
return(ARG);
|
||
|
}
|
||
|
default:
|
||
|
*p++ = c;
|
||
|
} while (p < tempmax);
|
||
|
error(symlong); /* doesn't return */
|
||
|
|
||
|
default: /* unquoted string */
|
||
|
p = tempbuf;
|
||
|
peekc = c;
|
||
|
do switch(c = nextchar()) {
|
||
|
case '\0':
|
||
|
case '\n':
|
||
|
case '\t':
|
||
|
case ' ':
|
||
|
case ',':
|
||
|
peekc = c;
|
||
|
*p = '\0';
|
||
|
comma++;
|
||
|
return(ARG);
|
||
|
default:
|
||
|
*p++ = c;
|
||
|
} while (p < tempmax);
|
||
|
}
|
||
|
|
||
|
/* in practice it can't get here, but FWIW and to satisfy
|
||
|
* -Wall... -rjm */
|
||
|
error("can't happen - in zmac.y:getarg(), infinite unquoted string!?");
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Add suffix to pathname if leafname doesn't already have a suffix.
|
||
|
* The suffix passed should not include an extension separator.
|
||
|
* The pathname passed should be in local format.
|
||
|
*/
|
||
|
void suffix_if_none (char *str, char *suff)
|
||
|
{
|
||
|
char *leafname, *extension;
|
||
|
|
||
|
leafname = strrchr (str, OS_DIR_SEP);
|
||
|
if (leafname == NULL)
|
||
|
{
|
||
|
leafname = str;
|
||
|
}
|
||
|
|
||
|
extension = strchr (leafname, OS_EXT_SEP);
|
||
|
if (extension == NULL)
|
||
|
{
|
||
|
size_t leafsize = strlen (leafname);
|
||
|
|
||
|
leafname[leafsize] = OS_EXT_SEP;
|
||
|
strcpy (leafname + leafsize + 1, suff);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Add or change pathname suffix.
|
||
|
* The suffix passed should not include an extension separator.
|
||
|
* The pathname passed should be in local format.
|
||
|
* If the leafname passed has more than one extension, the last is changed.
|
||
|
*/
|
||
|
void suffix (char *str, char *suff)
|
||
|
{
|
||
|
char *leafname, *extension;
|
||
|
|
||
|
leafname = strrchr (str, OS_DIR_SEP);
|
||
|
if (leafname == NULL)
|
||
|
{
|
||
|
leafname = str;
|
||
|
}
|
||
|
|
||
|
extension = strrchr (leafname, OS_EXT_SEP);
|
||
|
if (extension == NULL)
|
||
|
{
|
||
|
extension = leafname + strlen (leafname);
|
||
|
}
|
||
|
|
||
|
*extension = OS_EXT_SEP;
|
||
|
strcpy (extension + 1, suff);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Decanonicalise a canonical pathname.
|
||
|
* A canonical pathname uses '/' as the directory separator,
|
||
|
* '.' as the extension separator, ".." as the parent directory,
|
||
|
* "." as the current directory, and a leading '/' as the root
|
||
|
* directory (it would be more user-friendly not to use this!).
|
||
|
*/
|
||
|
void decanonicalise (char *pathname)
|
||
|
{
|
||
|
#if defined (MSDOS)
|
||
|
|
||
|
char *directory = pathname;
|
||
|
|
||
|
/* Just need to change all '/'s to '\'s */
|
||
|
|
||
|
while ((directory = strchr (directory, '/')) != NULL)
|
||
|
{
|
||
|
*directory = OS_DIR_SEP;
|
||
|
}
|
||
|
|
||
|
#elif defined (__riscos)
|
||
|
|
||
|
char *directory = pathname, *dirend;
|
||
|
|
||
|
/* First deal with leading '/' */
|
||
|
|
||
|
if (*directory == '/')
|
||
|
{
|
||
|
memmove (directory + 1, directory, strlen (directory) + 1);
|
||
|
*directory = '$';
|
||
|
++directory;
|
||
|
}
|
||
|
|
||
|
/* Then deal with non-leaf ".."s and "."s */
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
dirend = strchr (directory, '/');
|
||
|
if (dirend == NULL)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*dirend = '\0';
|
||
|
|
||
|
if (strcmp (directory, "..") == 0)
|
||
|
{
|
||
|
*directory = '^';
|
||
|
memmove (directory + 2, directory + 3, strlen (directory + 3) + 1);
|
||
|
dirend = directory + 1;
|
||
|
}
|
||
|
else if (strcmp (directory, ".") == 0)
|
||
|
{
|
||
|
memmove (directory, directory + 2, strlen (directory + 2) + 1);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
*dirend = '/';
|
||
|
directory = dirend + 1;
|
||
|
}
|
||
|
|
||
|
directory = pathname;
|
||
|
|
||
|
/* Finally, swap '/' and '.' */
|
||
|
|
||
|
while ((directory = strpbrk (directory, "/.")) != NULL)
|
||
|
{
|
||
|
if (*directory == '/')
|
||
|
{
|
||
|
*directory = OS_DIR_SEP;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*directory = OS_EXT_SEP;
|
||
|
}
|
||
|
++directory;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
/* Local form is canonical form */
|
||
|
|
||
|
UNUSED (pathname);
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* put out a byte to the macro file, keeping the offset
|
||
|
*/
|
||
|
void putm(char c)
|
||
|
{
|
||
|
#ifdef M_DEBUG
|
||
|
fputc (c, stderr);
|
||
|
#endif
|
||
|
mfptr++;
|
||
|
mfputc(c,mfile) ;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* get a byte from the macro file
|
||
|
*/
|
||
|
int getm()
|
||
|
{
|
||
|
int ch;
|
||
|
|
||
|
floc++;
|
||
|
ch = mfgetc(mfile) ;
|
||
|
if (ch == EOF) {
|
||
|
ch = 0;
|
||
|
fprintf(stderr,"bad macro read\n") ;
|
||
|
}
|
||
|
return(ch);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* pop standard input
|
||
|
*/
|
||
|
void popsi()
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (est)
|
||
|
{
|
||
|
for (i=0; i<PARMMAX; i++) {
|
||
|
if (est[i])
|
||
|
#ifdef M_DEBUG
|
||
|
fprintf (stderr, "[Freeing arg%u(%p)]\n", i, est[i]),
|
||
|
#endif
|
||
|
free(est[i]);
|
||
|
}
|
||
|
floc = est[FLOC];
|
||
|
free(est);
|
||
|
expptr--;
|
||
|
est = expptr ? (char **) expstack[expptr-1] : (char **) 0;
|
||
|
mfseek(mfile, (long)floc, 0);
|
||
|
if (lineptr > 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; i<s; i++) {
|
||
|
fread((char *)t, 1, sizeof *t, sfile);
|
||
|
if (tokenofitem(UNDECLARED) != UNDECLARED)
|
||
|
continue;
|
||
|
yylval.itemptr->i_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; ip<itemmax; ip++) {
|
||
|
if (ip->i_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; ip<itemmax; ip++) {
|
||
|
if (ip->i_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
|