sources-request@mirror.UUCP (03/13/87)
Submitted by: cisden!lmc (Lyle McElhaney) Mod.sources: Volume 9, Issue 26 Archive-name: assem2/Part01 echo extracting - README sed 's/^X//' > README << '@FUNKY STUFF~' XThis is the second posting of the generic assembler. Some notes: X X- I broke it down into some modules to assist recompilation. X X- As several have noted, this is not a modern assembler; its modelled Xafter the vintage assemblers of my youth, where the ops were ops, and Xoperands were operands, and operands didn't go around trying to be ops, Xand.... (sorry - too many movies lately). Anyway, that was to make it Xgeneric; its more difficult the other way. Problem is that it makes doing Xthe 6805 and 6809 quite messy. I experimented with extending the current Xregime to the 6809, and am disappointed by the results (to put it mildly - Xashamed would be closer). Anyway, I have to send this off with the problem Xunsolved as of now. I'm open to ideas. X X- Several bug fixes from Mark Callaghan in Australia: VAX byte swapping (I Xdid the work on a Sun, and forgot about the other wretched machines); Xchecksumming the address & byte count (and negating it); a casting problem Xin putoutbin; and adding an error if no end statemnt is found (and Xsimulating it anyway). Thanks, Mark. X X- An error in the 6502 was fixed. X X- Made the instruction mask long. X X- Makeops is a filter that adds a sort of macro capability to the creating Xof the ops.h files. See 6809_ops.h (but ignore the content - its grotesque). X X- At this time I'm porting it to an AT. Will repost when that's complete. X X-------------------------------------------------------------------------- X XSimplicity itself: edit the Makefile, equating V to the machine number of Xthe assembler to be made, and say make. You've got an assembler. X XMachines available: 6502, 8085, 6803, 6809. X X6803reg.h has some specific register names for special hardware registers Xon the 6803. X XI would appreciate getting back any new ops.h files anyone cares to Xgenerate (or maybe regenerate). Send them to hao!cisden!lmc, or to X X Lyle McElhaney X 2489 W. Ridge Road X Littleton, CO 80120 X XPS: Equates in asm.h may need tweaking (like NORMBYTE, fer instance). @FUNKY STUFF~ echo extracting - Makefile sed 's/^X//' > Makefile << '@FUNKY STUFF~' XOBJS=asm.o instructions.o pseudos.o data.o XV=6502 XCFLAGS=-O X Xasm: $(OBJS) X cc $(OBJS) -o asm X X$(OBJS): asm.h X Xdata.o: ops.h X Xops.h: $V_ops.h makeops X makeops <$V_ops.h >ops.h X Xmakeops: makeops.o X cc makeops.o -o makeops X Xclean: X rm -f asm makeops ops.h *.o dist_asm* X Xdistrib: clean X shar README Makefile *.c *.[1-9] > dist_asm1 X shar *.h > dist_asm2 X ls -l dist* @FUNKY STUFF~ echo extracting - asm.c sed 's/^X//' > asm.c << '@FUNKY STUFF~' X#include "asm.h" X Xmain (argc, argv) X int argc; X char **argv; X{ X char *t1, *t2; X X srcin = stdin; X filename[0] = "stdin"; X binout = stdout; X getargs (argc, argv); X init (); X output = fopen ((t1 = mktemp ("/tmp/asm185XXXXXX")), "w+"); X input[0] = srcin; X if (process ()) reporterr (E_PASS1); X (void) fclose (input[0]); X init2 (); X rewind (output); X input[0] = output; X output = fopen ((t2 = mktemp ("/tmp/asm285XXXXXX")), "w+"); X if (process ()) reporterr (E_PASS2); X (void) fclose (input[0]); X (void) unlink (t1); X input[0] = output; X output = binout; X rewind (input[0]); X sort (); X (void) fclose (input[0]); X (void) unlink (t2); X (void) fclose (output); X if (xref) crossref (); X exit (errorstat); X} X X X/* Current arguments: X infile input file name (default stdin) X -lfile listing output to file (default no listing), X (default file is infile.l); X -ofile binary output to file (default binary to stdout), X (default file is infile.o); X -xfile cross reference output to listing file (default no xref); X*/ Xvoid getargs (argc, argv) X int argc; X char **argv; X{ X char *cp, *arg, *lname, *oname; X X while (--argc) { X arg = *(++argv); X if (*arg == '-') { X switch (*(++arg)) { X case 'l': X listing ++; X lname = ++arg; X break; X case 'o': X binary ++; X oname = ++arg; X break; X case 'x': X xref ++; X break; X } X } else { X name = arg; X if ((srcin = fopen (name, "r")) == NULL) X reporterr (E_INPUT); X filename[0] = strcpy (malloc ((unsigned) strlen (name) + 1), name); X if (cp = index (name, '.')) X *cp = '\0'; X } X } X if (binary) { X if (!*oname) { X (void) strcat (strcpy (buf, name), ".o"); X oname = buf; X } X binout = fopen (oname, "w"); X } X if (listing || xref) { X if (!*lname) { X (void) strcat (strcpy (buf, name), ".l"); X lname = buf; X } X list = fopen (lname, "w"); X } X return; X} X X Xvoid init () X{ X symbol *spt; X int i; X X lc = seghd->lc = 0; X symtab = (symbol *) malloc ((unsigned) sizeof (symbol) * NSYMS); X for (i = 0; i < NSYMS; i++) X symtab->name = (char *) 0; X for (spt = predef; *(spt->name); spt++) X insert (spt->name, spt->value, spt->type, spt->segp, NO); X end_found = 0; X return; X} X X Xvoid init2 () X{ X segmnt *sp; X Memad acc; X X acc = seghd->lc; X seghd->lc = 0; X for (sp = seghd->next; sp; sp = sp->next) { X acc += sp->lc; X sp->lc = sp->start = acc - sp->lc; X } X curseg = seghd; X lc = curseg->lc; X errorstat = X end_found = X 0; X return; X} X X X X/* Gross syntax rules: <and the rest of us simply obey> X Input line: [label] [op[optop] [operands]] X Comments: Blank lines and anything following ';' X White space is any combination of blank and/or tab chars. X Operands & optop cannot contain white space (except within strings) X and are separated by commas. There must be the exact number of X operands that the instruction expects. X optop can be anything, and is interpreted by the machine-specific X routine "optional". It is distinguished from op by starting X with a non-alphanumeric or upper-case character. X Label must start in col 1 if it exists. X*/ Xint process () X{ X char *ipt; X int i, done; X static char *zit = ""; X X pass ++; X lineno[0] = 0; Xcontproc: X while (fgets (buf, LINEBUFLEN, input[currinput]) != NULL) { X (void) strcpy (linecopy, buf); X linecopy[strlen(linecopy)-1] = 0; X if (pass == 1) fputs (buf, output); X lineno[currinput]++; X ipt = buf; X label = ipt; X op = opd = optop = zit; X while (islabel (*ipt)) ipt++; X if (iseol (*ipt)) X goto shortline; X *ipt++ = 0; X while (iswhite (*ipt)) ipt++; X if (iseol (*ipt)) { Xshortline: if (pass == 2) X listit (linecopy, lc, dummy, 0); X continue; X } X op = ipt; X while (isalnum (*ipt)) ipt++; X if (iseol (*ipt)) { X *ipt = 0; X goto parsed; X } X if (!iswhite(*ipt) && islower(*ipt)) X (void) strcpy ((optop = malloc (strlen (ipt) + 1)), ipt); X *ipt++ = 0; X while (iswhite (*ipt)) ipt++; X opd = ipt; X while (*ipt != '\n') ipt++; X *ipt = 0; Xparsed: X if ((i = opsrch (op, pseudotab, pseudolen)) >= 0) { X done = (pseudotab[i].action) (); X } else if ((i = opsrch (op, optab, instrlen)) >= 0) { X (void) (optab[i].action) (i); X } else { X reporterr (E_ILLOP); X listit (linecopy, lc, dummy, 0); X } X if (done) break; X } X if (!end_found) { X do_end(); X reporterr (E_NOEND); X } X if (currinput > 0) { X free (filename[currinput]); X filename[currinput] = NULL; X (void) fclose (input[currinput--]); X goto contproc; X } X return (errorstat); X} X Xint opsrch (op, table, hi) X char *op; X opdef table[]; X int hi; X{ X int lo, mid, i; X X/* Binary search - assumes that list extends from optab[0] through X optab[hi-2] inclusive. The hi and lo indicies always point to X ruled out table locations plus 1, to keep away from rounding X problems on the far side of zero. Whenever they come to within 1 X of each other, the jig is up (and gone......) X*/ X X lo = 0; X while (1) { X mid = (hi + lo) / 2; X if ((i = strcmp (table[mid - 1].name, op)) == 0) X return (--mid); X else if (i > 0) X hi = mid; X else X lo = mid; X if (hi - lo <= 1) break; X } X return (-1); X} X X Xvoid listit (line, xlc, binbuf, length) X char *line; X Memad xlc; X Word binbuf[]; X int length; X{ X int addit, i; X X addit = 0; X do { X listint (line, xlc + addit, &binbuf[addit], length - addit); X addit += listlen; X line = (char *)0; X } while (addit < length); X return; X} X X Xvoid listint (line, xlc, binbuf, length) X char *line; X Memad xlc; X Word *binbuf; X int length; X{ X int i; X X if (listing && liston) { X (void) fprintf (list, "%4ld %08x: ", lineno[currinput], xlc); X if (length > listlen) length = listlen; X for (i = 0; i < length; i++) X (void) fprintf (list, "%02x ", binbuf[i]); X for (; i < listlen; i++) X (void) fprintf (list, " "); X /* move up to an 8 byte (tab) boundary */ X for (i = 0; i < (81-3*listlen)%8; i++) X (void) fprintf (list, " "); X if (line) (void) fprintf (list, "%s", line); X (void) fprintf (list, "\n"); X } X return; X} X X X/* Binary output format: X X Intel standard hexadecimal format!!!! X X Output is a series of variable length records consisting of ascii X characters 0-9, A-F (0x30-0x39, 0x41-0x6). Each character contains X four bits of significant data; two such characters form a binary X byte of data, the binary equivalent to the hexadecimal ascii X characters. X X The data records have the format: X X / len covers this \ X +---+---+---+---+---+---+---+---+---+--------//--------+---+---+ X | : | len | address | 0 | 0 | data bytes | cksum | X +---+---+---+---+---+---+---+---+---+--------//--------+---+---+ X \ checksum covers this / X X Checksum computed such that sum of all bytes except ':' modulo X the bytelength is 0. X X End Record: X X +---+---+---+---+---+---+---+---+---+---+---+ X | : | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | f | f | X +---+---+---+---+---+---+---+---+---+---+---+ X X X*/ Xvoid putoutbin (binbuf, length) X Word binbuf[]; X int length; X{ X static Memad address; X static int count; X static Word checksum; X static Word array[BINBUFLEN]; X static Word *curpt = 0; X static Memad binlc = ~0; X int i; X X if (binlc != lc) { X if (curpt != 0) { X (void) fwrite (":", 1, 1, output); X putout ((Word) count); X putout ((Word) (address >> 8)); X putout ((Word) (address & 0xff)); X (void) fwrite ("00", 2, 1, output); X for (i = 0; i < count; i++) X putout (array[i]); X checksum += (count & 0xff) + X (address >> 8) + X (address & 0xff); X putout (-checksum); X if (length == 0) { X (void) fwrite (":00000001ff", 11, 1, output); X } X } X curpt = array; X count = 0; X address = binlc = lc; X checksum = 0; X } X for (i = 0; i < length; i++) { X *curpt++ = binbuf[i]; X checksum += binbuf[i]; X count++; X binlc ++; X } X if (count > BINBUFLEN - MAXBYTPERINS) X binlc = 0; X return; X} X X#define hex(x) x+(x<10?'0':'7') X Xvoid putout (c) X Word c; X{ X Word outc[2], t; X X t = c>>4; X c &= 0xf; X outc[0] = hex(t); X outc[1] = hex(c); X (void) fwrite ((char *)outc, 2, 1, output); X return; X} X X Xvoid insert (name, value, type, segment, mult) X char *name; X long value; X opdclass *type; X segmnt *segment; X int mult; X{ X int x, y; X symbol *sp; X X x = y = hash (name); X while ((sp = &symtab[x])->name != (char *) 0) { X if (strcmp (sp->name, name) == 0) { X if (!mult && value != sp->value && pass == 1) { X reporterr (E_MULT); X return; X } X break; X } X if (++x == y) reporterr (E_STOFLO); X if (x == NSYMS) x = 0; X } X sp->name = malloc ((unsigned) strlen (name) + 1); X (void) strcpy (sp->name, name); X sp->value = value; X sp->type = type; X sp->segp = segment; X return; X} X X Xint lookup (name) X char *name; X{ X int x, y; X X x = y = hash (name); X while (symtab[x].name != (char *) 0) { X if (strcmp (name, symtab[x].name) == 0) X return (x); X x++; X if (x == y) return (-1); X if (x == NSYMS) x = 0; X } X return (-1); X} X X Xint hash (string) X char *string; X{ X char *pt; X int tot; X X tot = 0; X for (pt = string; *pt; pt++) X tot += *pt; X if (NSYMS >= 256) X tot += (tot << 8); X return (tot % NSYMS); X} X X Xvoid reporterr (errcode) X int errcode; X{ X static char *elist[] = { X "", X "aborting at end of pass1", X "aborting at end of pass2", X "symbol table overflow", X "input file error", X "too many nested includes", X "include syntax error", X "illegal expression", X "undefined symbol", X "unknown op code", X "multiply defined symbol", X "insufficient operands", X "value too large for field", X "unsigned value negative", X "segment violation", X "opcode suffix syntax", X "no end statement - simulated", X }; X X if (pass == 1) { X if (list) X (void) fprintf (list, "----> Error in file %s, line %d: %s\n", filename[currinput], lineno[currinput], elist[errcode]); X (void) fprintf (stderr, "----> Error in file %s, line %d: %s\n", filename[currinput], lineno[currinput], elist[errcode]); X } else { X if (list) X (void) fprintf (list, "----> %s: %s\n", filename[currinput], elist[errcode]); X (void) fprintf (stderr, "----> %s: %s\n", filename[currinput], elist[errcode]); X } X (void) fflush (list); X if (errcode <= A_MAX) { X (void) fprintf (stderr, "asm: Abort error: %s\n", elist[errcode]); X exit (1); X } X errorstat++; X return; X} X X Xvoid sort () X{ X char c; X X while ((c = getc (input[0])) > 0) X putc (c, output); X return; X} X X Xvoid crossref () X{ X int i, j, k, gap; X symbol symtemp; X X (void) fprintf (list, "\n\n\n Cross Reference\n\n"); X k = 0; X for (i = 0; i < NSYMS; i++) X if (symtab[i].name && *symtab[i].name) X symtab[k++] = symtab[i]; X for (gap = k / 2; gap > 0; gap /= 2) X for (i = gap; i < k; i++) X for (j = i - gap; j >= 0; j -= gap) { X if (strcmp (symtab[j].name, X symtab[j+gap].name) <= 0) X break; X symtemp = symtab[j]; X symtab[j] = symtab[j+gap]; X symtab[j+gap] = symtemp; X } X for (i = 0; i < k; i++) X (void) fprintf (list, " %s: 0x%x\n", symtab[i].name, X symtab[i].value); X return; X} @FUNKY STUFF~ echo extracting - data.c sed 's/^X//' > data.c << '@FUNKY STUFF~' X#include "asm.h" X Xopdclass o_none = {0, 0, 0, 0}; Xinsclass i_noopd = {1, 0, &o_none, &o_none, 0, 0}; Xopdef pseudotab[] = { X "bss" , &i_noopd, 0x00, do_bss , X "data" , &i_noopd, 0x00, do_data , X "data2" , &i_noopd, 0x00, do_data2 , X "end" , &i_noopd, 0x00, do_end , X "equ" , &i_noopd, 0x00, do_equ , X "include", &i_noopd, 0x00, do_incl , X "listoff", &i_noopd, 0x00, do_loff , X "liston" , &i_noopd, 0x00, do_lon , X "org" , &i_noopd, 0x00, do_org , X "seg" , &i_noopd, 0x00, do_seg , X "set" , &i_noopd, 0x00, do_set , X "string" , &i_noopd, 0x00, do_string, X "" , &i_noopd, 0x00, 0 X}; X XFILE *input[INCLSTACK], *output, *list = (FILE *)0, *srcin, *binout; Xchar *name, *label, *op, *opd, *optop; Xchar buf[LINEBUFLEN], linecopy[LINEBUFLEN], *filename[INCLSTACK]; Xint lineno[INCLSTACK], ignerr, end_found; Xint errorstat=0, currinput=0, pass=0, liston=YES, listing=0, binary=0, xref=0; Xsymbol *symtab; Memad lc; Word *dummy; Xsegmnt segmain={0, 0, "", 0}; Xsegmnt *seghd = &segmain, *curseg = &segmain, *exprseg; X X#include "ops.h" X Xint listlen = BYTESPERLINE; Xint pseudolen = sizeof (pseudotab) / sizeof (opdef); Xint instrlen = sizeof (optab) / sizeof (opdef); @FUNKY STUFF~ echo extracting - instructions.c sed 's/^X//' > instructions.c << '@FUNKY STUFF~' X#include "asm.h" X Xint choiceinstr (ins) X int ins; X{ X choicedef *chpt; X int num, i, fits; X Long value; X static char *opdpt; X X if (label && *label) X insert (label, (long) lc, &o_none, curseg, NO); X chpt = (choicedef *)optab[ins].class; X num = chpt->field; X for (opdpt = opd; num > 1; num --) { X opdpt = index (opdpt, ','); X if (opdpt == NULL) { X reporterr (E_NOPS); X return (0); X } X opdpt++; X } X value = expr (&opdpt, &i, YES); X fits = i ? NO : X (value >= chpt->lorange && X value <= chpt->hirange && X curseg == exprseg); X if ((i = opsrch ((fits ? chpt->rname : chpt->nname), X optab, instrlen)) >= 0) { X (optab[i].action) (i); X } else { X reporterr (E_ILLOP); X } X return (0); X} X X Xint geninstr (ins) X int ins; X{ X static Acc cons = ~0L; X int nargs, length, i, j, k; X opdclass *oclass; X insclass *iclass; X Word itemtemp, obuf[MAXBYTPERINS]; X Acc mask; X char *nextopd; X itemu item; X X if (label && *label) X insert (label, (long) lc, &o_none, curseg, NO); X iclass = optab[ins].class; X length = iclass->length; X if (pass == 2) { X nargs = iclass->mopds; X for (i = 0; i < MAXBYTPERINS; i++) X obuf[i] = 0; X item.l = optab[ins].mask; X for (i = 0; i < MINBYTES; i++) X obuf[i] = item.s[i]; X item.l = 0L; X nextopd = opd; X for (j = 0; j < nargs; j++) { X if (j != 0) { X if (*nextopd != ',') { X reporterr (E_NOPS); X } else { X nextopd++; X } X } X oclass = iclass->type[j]; X item.l = expr (&nextopd, &ignerr, NO) + oclass->offset - X (oclass->relative ? lc : 0); X mask = cons >> (LONGLEN - oclass->length); X if (item.l < 0L && !oclass->signed) X reporterr (E_NEG); X if (((item.ls < 0L && oclass->signed) ? -item.ls : item.ls) & ~mask) X reporterr (E_TOOBIG); X item.l &= mask; X if (oclass->byteswapped) { X itemtemp = item.s[BYTPERLONG - 2]; X item.s[BYTPERLONG - 2] = item.s[BYTPERLONG - 1]; X item.s[BYTPERLONG - 1] = itemtemp; X } X i = LONGLEN - 8 - iclass->offset[j]; X item.l <<= (i % 8); X i /= 8; X k = 0; X if (i < 0) X k = -i, i = 0; X for (; k < MAXBYTPERINS && i < BYTPERLONG; k++, i++) X#ifdef NORMBYTE X obuf[k] |= item.s[i]; X#else X obuf[k] |= item.s[BYTPERLONG-1-i]; X#endif X } X if (optop) { X optional (optop, obuf); X free (optop); X } X putoutbin (obuf, length); X listit (linecopy, lc, obuf, length); X } X lc += length; X return (0); X} X Xlong expr (string, errind, test) X char **string; X int *errind; X int test; X{ X long exp, val; X int op, err; X int uniop = 0; X segmnt *seg1, *seg2; X X *errind = NO; X if (**string == '-') X uniop = 1; X else if (**string == '~') X uniop = 2; X if (uniop) X (*string)++; X exp = getval(string, &err, test, &seg1); X if (err) goto errorexit; X if (uniop == 2) X exp = ~exp; X else if (uniop == 1) X exp = -exp; X while (!isdelim(**string)) { X if ((op = getop (string)) == NOOP) { X if (!test) reporterr (E_EXPR); Xerrorexit: for (; !isdelim (**string); *string++) ; X *errind = YES; X return (0); X } X val = getval (string, &err, test, &seg2); X if (err) goto errorexit; X if (seg1 && seg2 && seg1 != seg2 && pass == 1) X reporterr (E_SEG); X switch (op) { X case PLUS: X exp += val; X break; X case MINUS: X exp -= val; X break; X case MULT: X exp *= val; X break; X case DIV: X exp /= val; X break; X case MOD: X exp %= val; X break; X case OR: X exp |= val; X break; X case AND: X exp &= val; X break; X case EXOR: X exp ^= val; X break; X case SHLF: X exp <<= val; X break; X case SHRT: X exp >>= val; X break; X default: X if (!test) reporterr (E_EXPR); X } X seg1 = seg2; X } X exprseg = seg1; X return (exp); X} X X X/* Snag literals and variable names. The literal classes are: X X [digit]....[digit] unsigned decimal number X 0[hexdigit]...[hexdigit]unsigned hexadecimal number X $ current location counter X '[char] single character, right just, zero fill X "[char][char] character pair X X Returns a 16 bit value. Unary operations taken care of in expr; X byte swapping done if required by geninstr. X*/ Xlong getval (strpt, errorret, test, segment) X segmnt **segment; X char **strpt; X int *errorret; X int test; X{ X long total; X char name[33], *npt; X int i; X X *segment = (segmnt *) 0; X *errorret = 0; X total = 0L; X if (isdigit (**strpt)) { X if (**strpt == '0') { X while (isxdigit (**strpt)) { X total *= 16; X total += xtod (**strpt); X (*strpt)++; X } X } else { X while (isdigit (**strpt)) { X total *= 10; X total += (Long)(**strpt - '0'); X (*strpt)++; X } X } X } else if (islabel (**strpt)) { X npt = name; X while (islabel (**strpt)) { X *npt++ = **strpt; X (*strpt)++; X } X *npt = '\0'; X if ((i = lookup (name)) == -1) { X if (pass == 2) X if (!test) reporterr (E_UNDEF); X *errorret = 1; X total = 0L; X } else { X total = symtab[i].value; X if (pass == 2 && symtab[i].segp) X total += (symtab[i].segp)->start; X *segment = symtab[i].segp; X } X } else if (**strpt == '\'') { X (*strpt)++; X if (**strpt == '\\') { X (*strpt)++; X total = escape (strpt); X } else { X total = **strpt; X (*strpt)++; X } X } else if (**strpt == '"') { X (*strpt)++; X if (**strpt == '\\') { X (*strpt)++; X total = escape (strpt); X } else { X total = **strpt; X (*strpt)++; X } X total <<= 8; X if (**strpt == '\\') { X (*strpt)++; X total |= escape (strpt); X } else { X total |= **strpt; X (*strpt)++; X } X } else if (**strpt == '$') { X total = lc; X *segment = curseg; X (*strpt)++; X } else { X if (!test) reporterr (E_EXPR); X *errorret = 1; X } X return (total); X} X X Xchar escape (st) X char **st; X{ X switch (*((*st)++)) { X case 'b': X return ('\b'); X case 'n': X return ('\n'); X case 'r': X return ('\r'); X case '^': X return ('\033'); X case 'f': X return ('\f'); X case 't': X return ('\t'); X case '?': X return ('\177'); X case '\\': X return ('\\'); X default: X (*st)--; X if (isxdigit (**st) && isxdigit (*(*st + 1))) { X *st += 2; X return (xtod (*(*st - 2)) << 4 | xtod (*(*st - 1))); X } else { X return (*(*st++)); X } X } X} X X Xint xtod (c) X char c; X{ X return ((int) c - (c > '9' ? (c > 'F' ? 'W' : '7') : '0')); X} X X Xint getop (string) X char **string; X{ X static char ops[] = OPSTRING; X char *k; X X for (k = ops; *k; k++) X if (*k == **string) { X (*string)++; X return ((int) (k - ops)); X } X (*string)++; X return (NOOP); X} @FUNKY STUFF~ echo extracting - makeops.c sed 's/^X//' > makeops.c << '@FUNKY STUFF~' X#include <stdio.h> X#include <ctype.h> X X#define iswhite(x) (x)==' '||(x)=='\t'||(x)==',' X#define skipword while(isalnum(*pt)||*pt=='_')pt++; X#define skipwhite while(iswhite(*pt))pt++; X Xstruct { X char *name, *str; X} entry[100]; X Xint ep, entryn = 0; Xchar buf[140], hold[2048]; Xchar *pt, *pt2, *name, *arg1, *arg2; X Xextern int strncmp(), strcmp(); X Xmain () X{ X while (gets (buf) > 0) { X if (strncmp (buf, "beginpattern", 12) == 0) { X pt = buf; X skipword; X skipwhite; X entry[entryn].name = (char *) malloc (strlen (pt) + 1); X strcpy (entry[entryn].name, pt); X pt = hold; X while (gets (buf) > 0 && strncmp (buf, "endpattern", 10) != 0) { X strcpy (pt, buf); X pt += strlen (pt); X *pt++ = '\n'; X } X *pt = '\0'; X entry[entryn].str = (char *) malloc (strlen (hold) + 1); X strcpy (entry[entryn].str, hold); X entryn++; X } else if (strncmp (buf, "pattern", 7) == 0) { X pt = buf; X skipword; X skipwhite; name = pt; skipword; *pt++ = 0; X skipwhite; arg1 = pt; skipword; *pt++ = 0; X skipwhite; arg2 = pt; skipword; *pt = 0; X for (ep = 0; ep < entryn; ep++) { X if (strcmp (name, entry[ep].name) == 0) { X for (pt = entry[ep].str; *pt; pt++) { X if (*pt == '~') { X switch (*(++pt)) { X case '1': X pt2 = arg1; X goto argout; X case '2': X pt2 = arg2; X argout: while (*pt2) { X putchar (*pt2++); X } X break; X default: X putchar ('~'); X putchar (*pt); X } X } else { X putchar (*pt); X } X } X goto nextch; X } X } X fprintf (stderr, "makeops: can't find macro %s\n", name); Xnextch: ; X } else { X puts (buf); X } X } X exit (0); X} @FUNKY STUFF~ echo extracting - pseudos.c sed 's/^X//' > pseudos.c << '@FUNKY STUFF~' X#include "asm.h" X Xint do_incl () X{ X if (pass == 1) { X char *ip; X X if (++currinput > INCLSTACK) X reporterr (E_INCL); X opd++; X if ((ip = index (opd, '"')) == 0) X reporterr (E_INCLSYN); X *ip = 0; X if ((input[currinput] = fopen (opd, "r")) == NULL) X reporterr (E_INPUT); X filename[currinput] = strcpy (malloc ((unsigned) strlen (opd) + 1), opd); X lineno[currinput] = 0; X } X return (0); X} X X Xint do_org () X{ X if (label && *label) X insert (label, (long) lc, &o_none, curseg, NO); X if (pass == 2) X listit (linecopy, lc, dummy, 0); X lc = (Memad) expr (&opd, &ignerr, NO); X return (0); X} X X Xint do_loff () X{ X liston = NO; X return (0); X} X X Xint do_lon () X{ X liston = YES; X return (0); X} X X Xint do_equ () X{ X long eqtemp; X X eqtemp = expr (&opd, &ignerr, NO); X if (label && *label) X insert (label, eqtemp, &o_none, exprseg, NO); X if (pass == 2) X listit (linecopy, eqtemp, dummy, 0); X return (0); X} X X Xint do_seg () X{ X segmnt *sp, *osp; X X curseg->lc = lc; X if (opd && *opd) { X for (sp = seghd; sp; osp = sp, sp = sp->next) { X if (strcmp (sp->name, opd) == 0) { X lc = sp->lc; X curseg = sp; X goto fin_seg; X } X } X } else { X lc = seghd->lc; X curseg = seghd; X } X curseg = osp->next = (segmnt *) malloc ((unsigned) sizeof (segmnt)); X curseg->name = strcpy (malloc ((unsigned) strlen (opd) + 1), opd); X curseg->lc = lc = 0; X curseg->next = (segmnt *) 0; Xfin_seg: X if (pass == 2) X listit (linecopy, lc, dummy, 0); X return (0); X} X X Xint do_set () X{ X long eqtemp; X X eqtemp = expr (&opd, &ignerr, NO); X if (label && *label) X insert (label, eqtemp, &o_none, exprseg, YES); X if (pass == 2) X listit (linecopy, eqtemp, dummy, 0); X return (0); X} X X Xint do_bss () X{ X if (label && *label) X insert (label, (long) lc, &o_none, curseg, NO); X if (pass == 2) X listit (linecopy, lc, dummy, 0); X lc += (Memad) expr (&opd, &ignerr, NO); X return (0); X} X X Xint do_end () X{ X end_found ++; X curseg->lc = lc; X if (pass == 2) { X listit (linecopy, lc, dummy, 0); X lc = 0xffff; X putoutbin (dummy, 0); X } X curseg = seghd; X return (1); X} X X Xint do_data () X{ X Word temp, templist[MAXBYTPERINS]; X char *tp; X int count; X X if (label && *label) X insert (label, (long) lc, &o_none, curseg, NO); X tp = opd; X count = 1; X if (*opd == ':') { X ++opd; X count = expr (&opd, &ignerr, NO); X if (*opd == ':') { X opd ++; X } else { X reporterr (E_EXPR); X count = 1; X opd = tp; X } X } X if (pass == 2) { X int i, j; X X temp = (Word) expr (&opd, &ignerr, NO); X for (i = 0; i < MAXBYTPERINS; i++) X templist[i] = temp; X listit (linecopy, lc, templist, count); X for (i = 0; i < count; i++) X putoutbin (&temp, 1); X } X lc += count; X return (0); X} X X Xint do_data2 () X{ X Long temp; X char *tp; X int count; X X if (label && *label) X insert (label, (long) lc, &o_none, curseg, NO); X tp = opd; X count = 1; X if (*opd == ':') { X ++opd; X count = expr (&opd, &ignerr, NO); X if (*opd == ':') { X opd ++; X } else { X reporterr (E_EXPR); X count = 1; X opd = tp; X } X } X if (pass == 2) { X int i; X X temp = (Long) expr (&opd, &ignerr, NO); X listit (linecopy, lc, (Word *)&temp, 2*count); X for (i = 0; i < count; i++) { X putoutbin ((Word *)&temp, 1); X } X } X lc += (2 * count); X return (0); X} X X Xint do_string () X{ X Word buf[120], *bp; X Word delim; X int len, i; X X if (label && *label) X insert (label, (long) lc, &o_none, curseg, NO); X delim = *opd++; X bp = buf; X while (*opd != delim) { X if (*opd != '\\') X *bp++ = *opd; X else { X ++opd; X *bp++ = escape (&opd); X } X opd++; X } X *bp++ = '\0'; X len = bp - buf; X if (pass == 2) { X listit (linecopy, lc, buf, len); X putoutbin (buf, len); X } X lc += len; X return (0); X} X X @FUNKY STUFF~ echo extracting - asm.1 sed 's/^X//' > asm.1 << '@FUNKY STUFF~' X.so /usr/lib/tmac/tmac.e X.TH ASM 1 "1 May 1986" X.SH NAME Xasm \- generic assembler X.SH SYNOPSIS X.B asm X[ X.B \-o Xbinname] [ X.B \-l Xlistname] [ X.B \-x Xxrefname] inputfile X.br X.SH DESCRIPTION XThe generic assembler is an attempt to build a quick-and-dirty meta- Xassembler, with which code for several types of microcomputers can be Xgenerated. It outputs absolute code in two passes, contains a minimum of Xpseudo-ops, and is quasi-portable between micro types. The opcodes and Xinstruction layout information is contained within tables, leaving the Xcode unchanged from micro to micro. There has been no effort expended to Xmake the assembler match any particular assembly format on the market, so Xdon't expect any miracles there. X XThe statup options specify various files for input and output. The basic Xoption is the name of the input file; normally it is suffixed by ".a", Xalthough this is not necessary. If no file is given, then input is taken Xfrom standard input. The X.B \-o Xoption introduces the name of the file to receive the binary Xoutput. If the option does not exist, the binary goes to standard output X(since the output is actually in ASCII format, it won't bolix your screen Xif you forget). If the option is not followed immediately by a filename, Xthen the binary will be output to the input file name suffixed by ".o". XThe format of the binary output is "Intel Standard Hexadecimal Format" and Xis documented in the source code. XThe X.B \-l Xoption similarly specifies the file for the assembly listing; if the Xoption is not given, no listing is produced. If the option is alone, then Xthe listing will go into the input file name suffixed with ".l". The X.B \-x Xoption specifies the file to receive the cross-reference; it defaults to Xthe same filename as the listing, and is deleted if the option is not Xgiven. X.pp XThe assembler boasts (or at least admits) the following features: X.in +.5i X.ip \(bu XTwo pass, all labels are defined both forward and backward. X.ip \(bu XAbsolute format code only, no relocation information available. X.ip \(bu XTable driven instruction formats, including single opcodes and fixed Xnumbers of operands. X.ip \(bu XExpression evaluation in operands is left-to-right, no precedence or Xparentheses. X.ip \(bu XPredefined symbols accomodated. X.ip \(bu XInstructions formats that vary with the size of certain of their operands Xare accomodated, with some restrictions. X.ip \(bu XPseudo-ops include org, equ, set (for changing manifest variables), bss, Xdata, data2 (two bytes!!), string, seg (for setting up data segments that Xare made contiguous right after pass 1) Xand end. X.ip \(bu XAtoms in operands include labels, ops add (+), subtract (-), multiply (*), Xinteger divide (/), mod (%), and (&), or (|), and exclusive or (^), Xdecimal constants, hexadecimal constants (with a leading zero), right Xjustified zero-filled single characters ('x) and double characters ("xx). XThe string pseudo-op supports strings surrounded by any unused delimiter. X.in -.5i X.pp XThe pseudo op syntax is as follows: X.in +.5i X.ip "[label] org expr XSet the value of the symbol in label to the current location counter; set Xthe location counter to the value of the expression. X.ip "label equ expr XSet the value of the symbol in label to the value of the expression. Any Xsuch value may only be changed by a "set" pseudo. X.ip "label set expr XReset the value of a symbol to the value of the expression. X.ip "[label] bss expr XSet the value of the symbol in the label to the current location counter, Xthen set the location counter to its current value plus the value of the Xexpression. X.ip "[label] data [:count:]expr XSet the value of the label to the current location counter, and then Xgenerate count (default 1) bytes of data, each containing the value of the Xexpression. Count must contain no forward referenced symbols. X.ip "[label] data2 [:count:]expr XSet the value of the label to the current location counter, and then Xgenerate count (default 1) double-bytes of data, each double-byte Xcontaining the value of the expression. XCount must contain no forward referenced symbols. X.ip "[label] string $string$ XSet the value of the label to the current location counter, and then Xgenerate the string as bytes from left to right, appending a single nul Xbyte. The string delimiter shown above ($) may be any character that Xdoesn't appear in the string itself, except blank or tab. The special Xescapes \\t (tab), \\r (carriage return), \\n (newline), \\b (backspace), X\\? (del), \\^ (escape), \\nn (nn is any hex pair) and \\\\ (\\ itself) Xare allowed. X.ip " seg [name] XDeclare a new or reuse an older data segment, which defines it's own Xlocation counter. The segment that is predefined at the beginning of the Xassembler may be returned to by a X.b seg Xwith no argument. Any number of segments may be invoked. Each segment's Xlocation counter starts at zero during pass 1. When pass 1 is complete, Xthe segments are "stacked" in the order that they were created, so that Xthe second segment's lc starts at the first location following the end of Xthe first segment's highest lc. These computed lc's are displayed in the Xpass 2 listing output. The name used may be any non-blank-containing Xstring; it is also independent of the symbol name space. At this time, Xthere are no other definable characteristics of segments. X.in -.5i X.SH "MACHINES SUPPORTED XWith this edition are included tables for the Motorola 6803 and 6809, the XIntel 8085 and 6502. X.SH "OTHER FILES X/tmp/asm* - intermediate files. X.SH AUTHOR XLyle McElhaney @FUNKY STUFF~ echo extracting - asm_ops.5 sed 's/^X//' > asm_ops.5 << '@FUNKY STUFF~' X.so /usr/lib/tmac/tmac.e X.TH ASM_OPS 5 "1 Nov 1986" X.SH NAME Xasm_ops \- generic assembler op-code tables X.SH DESCRIPTION XThe generic assembler X.b asm Xcan be made to assemble code for a number of different microprocessors. At Xthe time of this writing, codes have been developed for the Intel 8085, Xthe Motorola 6803, , the 6809 (with a lot of squeezing) and the 6502 X(Commodore 64). This manual page will describe the format of the ops.h Xfile, which contains the processor-specific parts of the assembler. The Xstructures described below are defined in the file asm.h. X XThe opd.h file consists in a series of structure initializations, that Xgenerate tables in the assembler when it is compiled. Most of the lines Xare of the form: X X table-type name = {value, ....., value}; X Xwhere the names are arbitrary (within the c-compiler naming restrictions) Xand the values may be integers, Flags (i.e., boolean-valued integers), Xstrings (actually pointers to strings), pointers to other structures Xwithin the file, and pointers to functions in the assembler itself. The Xtype Word refers an unsigned byte and Memad refers to a type holding any Xpossible memory address for the target machine. The type Acc refers to Xthe accumulator used in computing expression values; make it unsigned Xlong. If Acc is not 32 bits long, then some things will need changes, Xparticularly the mask field in the opdef structure. X XThe first structure is opdclass, which defines the kinds of operands that Xmay appear in an instruction, and their encoding in the corresponding Xinstruction word: X X.nf Xtypedef struct opdclassitem { /* This defines an instruction field */ X int length; /* length in bits of field */ X Flag signed; /* else unsigned */ X Flag byteswapped; /* data has bytes backwards */ X Flag relative; /* field is relative to $ */ X int offset; /* fixed value added to field */ X} opdclass; X.fi X XAn operand's X.b length Xrefers to the number of bits that are allocated for it Xin the instruction field. If that number is eight, then only numbers from X-128 through +127 (two's complement assumed) can fit in the field. If the Xnext flag, X.b signed , Xis set then the range becomes 0 through 255, in this Xexample. The X.b byteswapped Xflag is set if the bytes (in a multibyte field) Xare to be loaded within a 2 byte word in right-to-left order, rather then Xthe more conventional left-to-right. The X.b relative Xflag is set if the value Xto be placed in the field must first be decremented by the value of the Xlocation counter before insertion. Finally, X.b offset Xis an integer value Xto be added to the value of the field before it is inserted. As an Xexample, an entry for the 6803 reads: X Xopdclass o_rmem = { 8, YES, NO , YES, -2}; X XThis defines a field that is used in relative-mode instructions. The field Xis eight bits long, is signed, and is relative to the current lc. In Xaddition, it is expected to be decremented by two. Given all this, the Xlegal range of a value to be placed in this field must be from (lc)-126 Xthrough (lc)+129 inclusive, where (lc) is the current value of the Xlocation counter (which points to the first byte of the current Xinstruction). X XThe second "set" of structures, insclass, define an instruction type. XEvery generated instruction must fall within one of these types. They Xdefine the instruction structure (as a collection of fields) and the Xwritten form of its invocation: X X.nf Xtypedef struct insclassitem { /* This defines an instruction type */ X int length; /* instruction length in bytes */ X int mopds; /* number of operands expected */ X opdclass *type[MAXOPDS]; /* each operand's field type */ X int offset[MAXOPDS]; /* each operand's bit offset, X from right end of first byte */ X} insclass; X.fi X XThe X.b length Xof an instruction type is the number of bytes in the Xinstruction, including all the fields. The number of operands expected X.b mopd Xmay be 0, 1, or 2 (making this larger would involve changes to asm.h and Xasm.c). MAXOPDS enforces the current limit on operands to two. The Xmembers of the array X.btype Xare pointers to the appropriate opdclass defined Xabove. When the instruction is scanned, the first operand must fit the Xfield described in the structure pointed to be xxx.type[0], the second by Xxxx.type[1]. The array X.b offset Xdefines the amount of shifting to be done to Xproperly align the field in the instruction. An offset of zero states Xthat the field's rightmost bit should be placed in the rightmost bit of Xthe instruction's first byte; a negative offset requires the value to be Xshifted left that many bits, and a positive value must be shifted right. XAn example, again from the 6803, shows the format of a relative Xinstruction: X Xinsclass i_rel = {2, 1, &o_rmem, &o_none, 8, 0}; X XSuch an instruction is two bytes long, and contains one operand. This Xoperand is a relative memory operand (from the example above), and it must Xbe shifted to the right 8 bits (which puts it in the second byte of the Xinstruction exactly). The second operand must have an address even though Xits not used; o_none fills this requirement. X XAll this is leading, of course to the definition of individual Xinstructions. These are defined in the opdef structures: X X.nf Xtypedef struct opdefitem { /* Defines an instruction */ X char *name; /* instruction mnemonic */ X insclass *class; /* instruction type */ X Acc mask; /* mask for first byte of instruction */ X int (*action) (); /* action routine for assembly */ X} opdef; X.fi X XEach instruction has a X.b name Xthat is recognized during the source code Xscanning. It also has a pointer to the insclass X.b class Xthat defines both what the Xscanner should expect and how the finished instruction looks. The X.b mask Xis a value to be or'ed in with the assembled operand fields to complete the Xinstruction. It normally contains the instruction-unique bits known as the Xopcode. It is defined as an Acc type, and therefore the mask may exceed Xthe size of the instruction. Assume the most significant bits are aligned Xwhen assigning a value to X.b mask . XNote that this also limits the size of the mask (and the opcode-fixed bits of the Xinstruction) to the length of Acc. This is true of all fields in the Xinstruction; none may exceed the length of Acc in bits. X XFinally, the routine X.b action Xdefined to assemble the instruction Xmust be given. For all normal instructions, the routine is X.u geninstr , Xwhich generates all normal instructions from the table data. This field is Xdefined primarily so that pseudo-ops can also use the structure. X XNow, the opdef table is defined in a slightly different way than the other Xtables. The entries in the other tables are all referenced by pointers, Xso their order is of no consequence. The opdef table (named optab), on Xthe other hand, must be searched by the assembler. Therefore, each entry Xis a member of the array optab, and not a separate statement. The entries Xin the table may be in no particular order, however, since the table is Xsearched linearly for the nonce. X XAn example of a defined instruction for the 6803 is X X.nf Xopdef optab [] = X .... X "bra" , &i_rel , 0x20000000, geninstr, X .... X}; X.fi X XThe unconditional branch instruction has its format defined by the i_rel Xclass of instruction formats (which, as shown above, defines a two byte Xinstruction with one operand, etc.). The mask for the first byte of the Xinstruction (the opcode for a branch) is hex 20. It is generated by the Xgeninstr routine. Note that the X.b mask Xfield is assumed to be 32 bits long, in this instance. X XWhat of the microprocessors that have two different instructions that may Xbe used to perform an operation, such as the 6803 that can load a register Xfrom a memory location with either a two byte relative instruction or a Xthree byte extended instruction? The native assembler can generate the Xshortest instruction that will fulfill the effect; so can the generic Xassembler, under some circumstances. The third set of structures, called Xchoicedef, is used in this case: X X.nf Xtypedef struct chcitem { /* Defines the alternatives for instr choices */ X char *rname; /* restrictive mnemonic */ X char *nname; /* non-restrictive mnemonic */ X int field; /* operand that is restricted */ X int lorange, hirange; /* range of restriction inclusive */ X Flag relative; /* to current lc, else absolute value */ X} choicedef; X.fi X XAny choicedef that exists (there may be none, if the microprocessor has no Xsuch overlapping instructions) describes the tradeoff to be made. The X.b rname Xis the mnemonic that may be used in the restrictive case of the Xchoice (i.e., the one that is more desireable, usually leading to a Xsmaller instruction). X.b Nname Xis the mnemonic to be used otherwise. XPresumably, the two choices are mutually inclusive of all possibilities, Xso that the nname mnemonic may be substituted in all cases to achieve the Xdesired result. The field X.b field Xis either one or two, describing which Xfield the choice hinges on. The X.b lorange and X.b hirange Xvalues are the Xinclusive limits of the values that the field must fall within in order to Xqualify for the restrictive choice. Finally, the X.b relative Xflag states that Xthe ranges are or are not relative to the current location counter. (At Xthis point, the relative flag is not implemented.) X XThe infamous example: X X "add" , (insclass *)&c_add, 0x00, choiceinstr, X XThis entry in the optab table defines an pseudo instruction called add. It Xmay be expanded as the instruction adds, if conditions are right, or as Xaddz in any case. Instead of pointing to an instruction class, the second Xentry in the structure is the address of a choice structure, which is cast Xas an insclass pointer to keep c and lint happy. The mask is defaulted X(its value is not used), and the generating routine is changed to Xchoiceinstr, which handles the cases. The choicedef entry pointed to is: X Xchoicedef c_add = {"adds" , "addz" , 2, 0, 0xff, NO}; X XThis defines a choice of either the adds instruction or the addz Xinstruction to add a memory location to an accumulator. The second field, Xthe memory reference, is the key: if the reference is greater than or equal Xto zero, and less than or equal to hex ff (decimal 255), then adds can be Xused; otherwise only addz is allowed. X XAs I said above, the choice mechanism is restricted in the decisions that Xit can make in attempting to use the shortest instruction. Since the Xchoices are all made during the first pass, the expression in the deciding Xfield must be completely backward-defined. That means that all symbols in the Xexpression must be either constants, be predefined (see below) or have Xbeen defined in the code physically before the line where the choice Xresides. In addition, all symbols in the expression must be in the same Xsegment as the code being generated. This is not to say that using a Xchoice instruction containing a forward reference is an error; rather, the Xassembler, in the absence of required data to make the decision, will opt Xfor the "otherwise" form of the instruction, when it could be possible to Xuse the other. As Captain Kirk says, "Sie la vie." X XThe last set of entries in the ops.h file is a group of predefined symbols Xthat the assembler "knows" about for all assemblies on this Xmicroprocessor. An example of these definitions is: X X.nf Xsymbol predef[] = { X {"ra" , 0x0, &o_reg , (struct seg *)0 }, X {"rb" , 0x1, &o_reg , (struct seg *)0 }, X {"eq" , 0x7, &o_cond , (struct seg *)0 }, X {"nc" , 0x4, &o_cond , (struct seg *)0 }, X {"" , 0x0, &o_none , (struct seg *)0 }, X}; X.fi X XThese predefine the symbols ra, rb, eq, and nc for use in instructions Xthat reference register a, register b, and the branch conditions eq and nc X(no carry). Each is given a value, points to an operand type (which is not Xcurrently used), and defined in the null segment (that is, the only Xpredefined, default segment). Note the null entry at the end of the table. X XAfter all the tables a routine called "optional" must be defined. Optional Xgets called whenever some non-lowercase or non-alphanumeric character is Xencountered in the opcode field. when this happens, collection of the Xopcode is terminated. After the rest of the instruction is fully encoded, Xoptional gets called as: X X optional (optop, obuf); X Xwhere optop is the string of characters that follow the opcode, and obuf is Xan array of Words that contains the binary result of the instruction Xassembly. Optional should modify obuf to achieve whatever results may be Xdeemed necessary. If there are no requirements for changing the Xinstruction at this point, then a null optional can be used: X X optional (optop, obuf) Word *obuf; char *optop; {} X XNow, given the above description it should be possible to define an ops.h Xfile to generate an assembler for most any machine. This scheme should Xwork for almost any 8 or 16 bit microprocessor, although its only been Xused with eights. Restrictions are enforced by the use of a long Xvariable for expression solution and for instruction compilation, and Xprobably other things that will become apparant when the assembler is Xported to some wierd machine. X XChoices for instruction format are arbitrary, of course. I use a branch Xconditional instruction with the conditional code as an operand to reduce Xthe size of the optab (simply laziness); it would be easy to have Xindividual branch instructions instead. X XFor the truely adventurous, I have put together a very simple macro Xprocessor to ease the pain of putting together tables for the more Xcomplicated machines. The 6809, for example, has many instructions that Xare of identical format, the only differences being in the constant mask Xpart of the op definition. (On the 6809, the memory reference instructions Xadd, adc, bit, sub, cmp, and so on; the various forms of the cmpy and cmpu Xinstructions form another similar group.) A pattern may be declared in the XXXXX_ops.h file in the form: X X beginpattern name X line...... X another line... X endpattern X XThe lines in the pattern are invoked by the statement X X pattern name,arg1,arg2 X XThe strings arg1 and arg2 are substituted directly into the lines to Xreplace the strings ~1 and ~2, respectively. That's all there is. Very Xsimple, really. Look at 6809_ops.h for examples. X XExpansion of the macros is done by the program makeops.c. Making the Xassembler, using the supplied makefile, will also build makeops, and Xfilter XXXX_ops.h through it to get ops.h, which is included in the Xcompilation of data.c. @FUNKY STUFF~