[mod.sources] v09i026: Generic assembler for micro's, Part01/02

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~