Subject: v07i097: Generic assembler for micros, Part01/02 Newsgroups: mod.sources Approved: mirror!rs Submitted by: seismo!cisden!lmc Mod.sources: Volume 7, Issue 97 Archive-name: micro.asm/Part01 Yet another assembler....... This one is for assembling absolute code (for prom loading, fer instance) for 8 and 16 bit microprocessors. I've used it successfully as a back-end for the small c compiler posted previously, and it works just fine, as long as you don't need any external subroutines loaded later on. Lints out as much as possible, but there are probably still some few errors within, particularly for 16 bit uPs. Feedback welcome. Also any newly generated machine tables. Shouldn't be too hard to add relocation stuff - I just didn't have that need. : This is a shar archieve. Extract with sh, not csh. : The rest of this file will extract: : 6803reg.h Makefile README asm.1 asm.c asm.h asm_ops.5 echo extracting - 6803reg.h sed 's/^X//' > 6803reg.h << '-FUNKY STUFF~' X X; control register & bit definition X; assumes operating mode 2 or 3 X Xp1ddr equ 0 ; port 1 data direction Xp2ddr equ 01 ; port 2 data direction Xp1dr equ 02 ; port 1 data Xp2dr equ 03 ; port 2 data X Xp2d_pc2 equ 080 Xp2d_pc1 equ 040 Xp2d_pc0 equ 020 Xp2d_p24 equ 010 Xscixmt equ p2d_p24 Xp2d_p23 equ 08 Xscircv equ p2d_p23 Xp2d_p22 equ 04 Xsciclock equ p2d_p22 Xp2d_p21 equ 02 Xtimerout equ p2d_p21 Xp2d_p20 equ 01 Xtimerin equ p2d_p20 X Xtcsr equ 08 ; timer control & status X Xtcs_icf equ 080 ; input capture flag Xtcs_ocf equ 040 ; output capture flag Xtcs_tof equ 020 ; timer overflow flag Xtcs_eici equ 010 ; enable input capture interrupt Xtcs_eoci equ 08 ; enable output capture flag Xtcs_etoi equ 04 ; enable timer overflow interrupt Xtcs_iedg equ 02 ; input edge Xtcs_olvl equ 01 ; output level X Xcounth equ 09 ; timer counter msb Xcountl equ 0a ; timer counter lsb Xoutcmh equ 0b ; timer output compare msb Xoutcml equ 0c ; timer output compare lsb Xincaph equ 0d ; input capture msb Xincapl equ 0e ; input capture lsb Xrmcr equ 010 ; SCI rate & mode control X Xrmc_cc1 equ 08 Xrmc_cc0 equ 04 Xrmc_cc equ 0c ; clock source and format Xrmc_ss1 equ 02 Xrmc_ss0 equ 01 Xrmc_ss equ 03 ; clock speed select 0-3, hi-lo X Xtrcsr equ 011 ; xmit/rcv control & status X Xtrc_rdf equ 080 ; rcv data reg full Xtrc_ofe equ 040 ; overrun or framing error Xtrc_tre equ 020 ; xmit data reg empty Xtrc_rie equ 010 ; rcv interrupt enable Xtrc_re equ 08 ; receiver enable Xtrc_tie equ 04 ; xmit interrupt enable Xtrc_te equ 02 ; transmitter enable Xtrc_wu equ 01 ; wakeup X Xscirdr equ 012 ; SCI rcv data Xscitdr equ 013 ; SCI xmit data Xramcr equ 014 ; RAM control X Xram_sb equ 080 ; stand-by power Xram_e equ 040 ; internal RAM enabled X Xstintram equ 080 Xenintram equ 0ff X X; interrupt vectors X Xvectors equ 0ff0 Xscivec equ vectors+0 Xtofvec equ vectors+02 Xtocvec equ vectors+04 Xticvec equ vectors+06 Xrq1vec equ vectors+08 Xswivec equ vectors+0a Xnmivec equ vectors+0c Xstartvec equ vectors+0e -FUNKY STUFF~ echo extracting - Makefile sed 's/^X//' > Makefile << '-FUNKY STUFF~' XCFLAGS=-O X Xasm: asm.o X cc asm.o -o asm X Xasm.o: asm.h ops.h X Xclean: X rm -f asm *.o -FUNKY STUFF~ echo extracting - README sed 's/^X//' > README << '-FUNKY STUFF~' XSimplicity itself: just link one of the XXXX_ops.h files to ops.h, Xand say make. You've got an assembler. 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 Contel, Inc. X 1101 W. Mineral Ave. X Littleton, CO 80120 -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 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 "OTHER FILES X/tmp/asm* - intermediate files. X.SH AUTHOR XLyle McElhaney -FUNKY STUFF~ echo extracting - asm.c sed 's/^X//' > asm.c << '-FUNKY STUFF~' X#include X 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}; X#include "ops.h" X X#define pseudolen sizeof(pseudotab)/sizeof(opdef) X XFILE *input[INCLSTACK], *output, *list; XFILE *srcin, *binout; X Xint errorstat = 0; Xchar *name; Xchar *label, *op, *opd; Xchar buf[100], linecopy[100]; Xchar *filename[INCLSTACK]; Xint lineno[INCLSTACK]; Xint currinput = 0; Xint pass = 0; Xint liston = YES; Xint listing = 0; Xint binary = 0; Xint xref = 0; Xint ignerr; Xsymbol *symtab; XMemad lc; XWord *dummy; Xsegmnt segmain = {0, 0, "", 0}; Xsegmnt *seghd = &segmain; Xsegmnt *curseg = &segmain; Xsegmnt *exprseg; 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 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 = 0; X return; X} X X X X/* Gross syntax rules: X Input line: [label] op [operands] X Comments: Blank lines and anything following ';' X White space is any combination of blank and/or tab chars. X Operands cannot contain white space (except within strings) X and are separated by commas. 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, sizeof buf, 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 = zit; X while (islabel (*ipt)) ipt++; X if (iseol (*ipt)) { X if (pass == 2) X listit (linecopy, lc, dummy, 0); X continue; X } X *ipt++ = 0; X while (iswhite (*ipt)) ipt++; X if (iseol (*ipt)) { X 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 *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, oplen)) >= 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 (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, tablen) X char *op; X int tablen; X opdef table[]; X{ X int i; X X for (i = 0; i < tablen; i++) X if (strcmp (table[i].name, op) == 0) X return (i); X return (-1); X} X 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_mem, 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_mem, 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_mem, 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_mem, 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 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_mem, 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 for (i = count; i > 0; i -= 3) { X j = i > MAXBYTPERINS ? MAXBYTPERINS : i; X listit (linecopy, lc, templist, j); X lc += j; X *linecopy = 0; X } X for (i = 0; i < count; i++) X putoutbin (&temp, 1); X } else 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_mem, 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 = expr (&opd, &ignerr, NO); X for (i = 0; i < count; i++) { X listit (linecopy, lc, (Word *)&temp, 2); X lc += 2; X *linecopy = 0; X putoutbin ((Word *)&temp, 1); X } X } else X lc += (count * 2); 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_mem, 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 for (bp = buf, i = 0; i < len; i += 3, bp += 3) { X listit (linecopy, lc + i, bp, len + buf - bp); X *linecopy = 0; X } X putoutbin (buf, len); X } X lc += len; X return (0); X} X 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_mem, 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), optab, oplen)) >= 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 int nargs, length, i, j, k; X opdclass *oclass; X insclass *iclass; X Word itemtemp, obuf[MAXBYTPERINS]; X unsigned long mask; X static unsigned long cons = ~0L; X union itemitem { X Word s[BYTPERLONG]; X unsigned long l; X long ls; X } item; X char *nextopd; X X if (label && *label) X insert (label, (long) lc, &o_mem, curseg, NO); X iclass = optab[ins].class; X length = iclass->length; X if (pass == 2) { X nargs = iclass->mopds; X item.l = 0L; X for (i = 0; i < MAXBYTPERINS; i++) X obuf[i] = 0; X *obuf = optab[ins].mask; 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 item.l <<= (LONGLEN - 8 - iclass->offset[j]); X for (k = 0; k < MAXBYTPERINS; k++) X obuf[k] |= item.s[k]; 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} X X Xvoid listit (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 %04x: ", lineno[currinput], xlc); X if (length > 3) length = 3; X for (i = 0; i < length; i++) X (void) fprintf (list, "%02x ", binbuf[i]); X for (i = length; i < MAXBYTPERINS; i++) X (void) fprintf (list, " "); X (void) fprintf (list, " %s\n", line); 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 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 }; X X if (pass == 1) { 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 (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 - asm.h sed 's/^X//' > asm.h << '-FUNKY STUFF~' X#include X#define YES 1 X#define NO 0 X X#define NSYMS 253 X#define INCLSTACK 10 X#define MAXBYTPERINS 3 X#define BYTPERLONG 4 X#define LONGLEN 32 X#define MAXOPDS 2 X#define NHEAD 5 /* words of overhead in binary header */ X#define BINBUFLEN 250 X X#define iswhite(c) (c==' '||c=='\t') X#define iseol(c) (c=='\0'||c==';'||c=='\n') X#define isdelim(c) (c==','||c==':'||iseol(c)||iswhite(c)) X#define islabel(c) (isalnum(c)||c=='_') X X#define A_MAX 6 /* highest error code causing immediate abort */ X#define E_PASS1 1 X#define E_PASS2 2 X#define E_STOFLO 3 X#define E_INPUT 4 X#define E_INCL 5 X#define E_INCLSYN 6 X#define E_EXPR 7 X#define E_UNDEF 8 X#define E_ILLOP 9 X#define E_MULT 10 X#define E_NOPS 11 X#define E_TOOBIG 12 X#define E_NEG 13 X#define E_SEG 14 X X#define NOOP -1 X#define PLUS 0 X#define MINUS 1 X#define MULT 2 X#define DIV 3 X#define MOD 4 X#define OR 5 X#define AND 6 X#define EXOR 7 X#define SHLF 8 X#define SHRT 9 X#define OPSTRING "+-*/%|&^<>" /* same order as definitions above */ X Xtypedef unsigned char Word; /* instruction word */ Xtypedef short Long; /* Long, that is, on an 8085: 16 bit data */ Xtypedef char Short; /* 8 bit data */ Xtypedef unsigned Long Memad; Xtypedef char Flag; X 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 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 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 Xtypedef struct opdefitem { /* Defines an instruction */ X char *name; /* instruction mnemonic */ X insclass *class; /* instruction type */ X Word mask; /* mask for first byte of instruction */ X int (*action) (); /* action routine for assembly */ X} opdef; X Xtypedef struct seg { /* Defines a location counter segment */ X Memad lc, start; /* segment's lc and starting loc */ X char *name; /* segment name */ X struct seg *next; /* pointer to next segment */ X} segmnt; X Xtypedef struct ltitem { /* Defines a label table entry */ X char *name; /* symbol name */ X long value; /* symbol's current value */ X opdclass *type; /* symbol type (defines fields it can fill) */ X segmnt *segp; /* pointer to segment value's defined in */ X} symbol; X Xextern char *malloc(); Xextern char *strcpy(); Xextern char *strcat(); Xextern char *mktemp(); Xextern char *index(); Xextern int geninstr(); Xextern int choiceinstr(); Xextern int do_bss(); Xextern int do_data(); Xextern int do_data2(); Xextern int do_end(); Xextern int do_equ(); Xextern int do_seg(); Xextern int do_set(); Xextern int do_org(); Xextern int do_string(); Xextern int do_incl(); Xextern int do_loff(); Xextern int do_lon(); Xextern void getargs (); Xextern void init (); Xextern void init2 (); Xextern int process (); Xextern void listit (); Xextern void insert (); Xextern void reporterr (); Xextern int hash (); Xextern long expr (); Xextern int getop (); Xextern long getval (); Xextern int lookup (); Xextern void putoutbin (); Xextern void crossref (); Xextern void sort (); Xextern void putout (); Xextern int xtod (); Xextern char escape (); -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, and the 6502 (Commodore 64). This manual page will Xdescribe the format of the ops.h file, which contains the processor-specific Xparts of the assembler. The structures described below are defined in the Xfile asm.h. X XThe opd.h file consists in a series of structure initializations, that Xgenerate tables in the assembler when it is compiled. All lines are of the Xform: 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. 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 6805 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 6805, 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 Word 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. Finally, 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. In addition, Xthe entries are all arranged in alphabetical order by X.b name . X XAn example of a defined instruction for the 6803 is X X.nf Xopdef optab [] = X .... X "bra" , &i_rel , 0x20, 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. X XFollowing the definition of optab is the statement X X#define oplen sizeof(optab)/sizeof(opdef) X Xwhich reveals the size of the optab to asm.c. 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 staes 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 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 XSo it goes. -FUNKY STUFF~