commit abf806277dcbf9b73836d6ddbaa163e7fe965725 Author: Bob Supnik Date: Sun Dec 28 17:18:00 2003 -0800 simh tools diff --git a/config11/config11.c b/config11/config11.c new file mode 100644 index 0000000..3b6c0f1 --- /dev/null +++ b/config11/config11.c @@ -0,0 +1,111 @@ +/* Program to configure the floating address space of a PDP-11 or VAX + + Copyright (c) 2002, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#include + +#define RANK_LNT 34 + +int csr, i, j; +unsigned int rank, num; +char *cp, *ocp, inp[100]; +unsigned char numctl[RANK_LNT]; +unsigned char modtab[RANK_LNT] = { + 0X07, 0X0f, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, + 0X07, 0X07, 0X07, 0X0f, 0X07, 0X07, 0X0f, 0X07, + 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X0f, + 0X07, 0X03, 0X1f, 0X0f, 0X0f, 0X03, 0X0F, 0x0F, + 0x1F, 0X1F }; +unsigned int fixtab[RANK_LNT] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0774400, 0770460, 0, + 0, 0777170, 0, 0772410, 0, 0, 0, 0, + 0774440, 0772150, 0, 0, 0, 0774500, 0, 0, + 0, 0 }; +char *namtab[RANK_LNT] = { + "DJ11", "DH11", "DQ11", "DU11", "DUP11", "LK11A", "DMC11", "DZ11", + "KMC11", "LPP11", "VMV21", "VMV31", "DWR70", "RL11", "LPA11K", "KW11C", + "rsvd", "RX11", "DR11W", "DR11B", "DMP11", "DPV11", "ISB11", "DMV11", + "DEUNA", "UDA50", "DMF32", "KMS11", "VS100", "TK50", "KMV11", "DHV11", + "DMZ32", "CP132" }; + +int main (int argc, char *argv[]) +{ +for ( ;; ) { + for (i = 0; i < RANK_LNT; i++) numctl[i] = 0; + printf ("Enter configuration data\n"); + for ( ;; ) { + printf ("Name:\t"); + if (gets (inp) == NULL) return 0; + if (*inp == 0) break; + for (cp = inp; *cp != 0; cp++) *cp = toupper (*cp); + for (rank = 0; rank < RANK_LNT; rank++) { + if (strcmp (inp, namtab[rank]) == 0) break; } + if (rank >= RANK_LNT) { + printf ("Unknown controller, valid names are:"); + for (i = 0; i < RANK_LNT; i++) { + if ((i & 07) == 0) printf ("\n"); + printf (" %s", namtab[i]); } + printf ("\n"); + continue; } + printf ("Number:\t"); + gets (inp); + errno = 0; + num = strtoul (inp, &ocp, 10); + if (errno || (inp == ocp)) { + printf ("Input error\n"); + continue; } + if (num > 8) { + printf ("Too many controllers\n"); + continue; } + numctl[rank] = num; + } + + printf ("\nRank\tName\tCtrl#\t CSR\n\n"); + csr = 0760010; + for (i = 0; i < RANK_LNT; i++) { + if (numctl[i] == 0) { + printf (" %02d\t%s\tgap\t%06o\n", i+1, namtab[i], csr); } + else { + if (fixtab[i]) + printf (" %02d\t%s\t 1\t%06o*\n", i+1, namtab[i], fixtab[i]); + else { + printf (" %02d\t%s\t 1\t%06o\n", i+1, namtab[i], csr); + csr = (csr + modtab[i] + 1) & ~modtab[i]; } + for (j = 1; j < numctl[i]; j++) { + printf ("\t\t %d\t%06o\n", j + 1, csr); + csr = (csr + modtab[i] + 1) & ~modtab[i]; } + printf (" %\t\tgap\t%06o\n", csr); + } + if ((i + 1) < RANK_LNT) csr = (csr + modtab[i+1] + 1) & ~modtab[i+1]; + } + printf ("\n\n"); + } +return 0; +} diff --git a/config11/config11.txt b/config11/config11.txt new file mode 100644 index 0000000..a2915eb --- /dev/null +++ b/config11/config11.txt @@ -0,0 +1,17 @@ +Config11 is a program for calculating the floating address space layout of +a PDP-11 or VAX. + +Config11 is an interactive program. When started, it repeatedly asks for +the names of controllers and the numbers of instances: + +> config11 +Name: UDA50 +Number: 2 +Name: DZ11 +Number: 2 +Name: + +To end input, simply type ENTER to the NAME: prompt. Config11 will then +print out the complete floating address table for the known Unibus and +Qbus devices. + diff --git a/converters/asc.c b/converters/asc.c new file mode 100644 index 0000000..44630f0 --- /dev/null +++ b/converters/asc.c @@ -0,0 +1,108 @@ +/* This program converts delimited files to Windoze + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define LAST_ANY 0 +#define LAST_CR 1 +#define LAST_LF 2 +#define MD_WIN 0 +#define MD_UNIX 1 +#define MD_MAC 2 + +void puteol (int mode, FILE *of) +{ +if (mode != MD_UNIX) putc ('\r', of); +if (mode != MD_MAC) putc ('\n', of); +return; +} + +int main (int argc, char *argv[]) +{ +int i, k, mc, lastc; +int mode; +char *s, *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: asc -muw file [file...]\n"); + exit (0); } + +s = argv[1]; +if ((s != NULL) && (*s++ == '-')) { + ++argv; --argc; + switch (*s) { + case 'm': case 'M': + mode = MD_MAC; break; + case 'u': case 'U': + mode = MD_UNIX; break; + case 'w': case 'W': + mode = MD_WIN; break; + default: + fprintf (stderr, "Bad option %c\n", *s); + return 0; } + } +else mode = MD_WIN; + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (lastc = LAST_ANY;;) { + k = getc (ifile); + if (k == EOF) break; + mc = k & 0177; + if (mc && (mc != 0177)) { + if (mc == 015) { + if (lastc == LAST_CR) puteol (mode, ofile); + lastc = LAST_CR; } + else if (mc == 012) { + puteol (mode, ofile); + lastc = LAST_LF; } + else { + if (lastc == LAST_CR) puteol (mode, ofile); + putc (mc, ofile); + lastc = LAST_ANY; } + } + } + if (lastc == LAST_CR) puteol (mode, ofile); + fclose (ifile); + fclose (ofile); + } +return 0; +} diff --git a/converters/asc.txt b/converters/asc.txt new file mode 100644 index 0000000..bb56baa --- /dev/null +++ b/converters/asc.txt @@ -0,0 +1,14 @@ +ASC is a simple utility for converting text files (delimited with +\r, \n, or \r\n) to Windows format (\r\n delimited), Unix format +(\n delimited) or Mac format (\r delimited). + +ASC is invoked from a DOS prompt with the command: + + > ASC {-m|u|w} file1 file2 ... + +The optional switch specifies Mac processing (-m), Unix processing +(-u), or Windows processing (-w). If no switch is specified, Windows +processing is performed. + +Each file is processed in turn. If the file is name.ext, the converted +file is name.new. diff --git a/converters/dtos8cvt.c b/converters/dtos8cvt.c new file mode 100644 index 0000000..8a0b4c0 --- /dev/null +++ b/converters/dtos8cvt.c @@ -0,0 +1,68 @@ +/* This program converts an OS8 DECtape image to simulator format + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define BLKSIZ 129 +int main (int argc, char *argv[]) +{ +int i, k; +unsigned short buf[BLKSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".dt8"); + else strcat (oname, ".dt8"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (;;) { + k = fread (buf, sizeof (short), BLKSIZ - 1, ifile); + if (k == 0) break; + for ( ; k < BLKSIZ; k++) buf[k] = 0; + fwrite (buf, sizeof (short), BLKSIZ, ofile); + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/dtos8cvt.txt b/converters/dtos8cvt.txt new file mode 100644 index 0000000..e4febaa --- /dev/null +++ b/converters/dtos8cvt.txt @@ -0,0 +1,13 @@ +dtos8cvt converts a PDP-8 DECtape image from OS/8 format to simulator format. + +OS/8 only uses 128 words out of 129 in a PDP-8 DECtape block. The simulator +requires all 129 words, in order to simulate the actions of the hardware, and +to deal with oddball software systems such as the PDP-8 Disk Monitor. + +dtos8cvt is invoked by + + dtos8cvt file1 file2 file3... + +Each file in turn is converted from OS/8 format to simulator format. The +input file can have any extension; the converted file will have a .dt8 +extension. \ No newline at end of file diff --git a/converters/gt7cvt.c b/converters/gt7cvt.c new file mode 100644 index 0000000..690728d --- /dev/null +++ b/converters/gt7cvt.c @@ -0,0 +1,97 @@ +/* This program converts a gt7 magtape dump to a SIMH magtape + + Copyright (c) 2002, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +unsigned char fzero[4] = { 0 }; + +int dump_rec (FILE *of, int bc, char *buf) +{ +unsigned char buc[4]; + +if (((bc == 1) && (buf[0] == 0xF)) || + ((bc == 2) && (buf[0] == 0xF) && (buf[1] == 0xF))) { + fwrite (fzero, sizeof (char), 4, of); + return 1; } +buc[0] = bc & 0xFF; +buc[1] = (bc >> 8) & 0xFF; +buc[2] = (bc >> 16) & 0xFF; +buc[3] = (bc >> 24) & 0xFF; +fwrite (buc, sizeof (char), 4, of); +fwrite (buf, sizeof (char), (bc + 1) & ~1, of); +fwrite (buc, sizeof (char), 4, of); +return 0; +} + +int main (int argc, char *argv[]) +{ +int i, ch, bc, rc, fc; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); + else strcat (oname, ".tap"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (bc = rc = fc = 0;;) { + ch = fgetc (ifile); + if (ch == EOF) break; + if (ch & 0x80) { + if (bc) { + if (dump_rec (ofile, bc, buf)) + printf ("End of file %d\n", ++fc); + else printf ("Record %d size %d\n", ++rc, bc); + } + bc = 0; } + buf[bc++] = ch & 0x3F; + } + fclose (ifile); + if (bc) dump_rec (ofile, bc, buf); + fwrite (fzero, sizeof (char), 4, ofile); + printf ("End of file %d\n", ++fc); + fclose (ofile); + } + +return 0; +} diff --git a/converters/littcvt.c b/converters/littcvt.c new file mode 100644 index 0000000..617d1ca --- /dev/null +++ b/converters/littcvt.c @@ -0,0 +1,69 @@ +/* This program removes the 4 header bytes from a Litt tape + + Copyright (c) 1993-1999, Robert M. Supnik, Compaq Computer Corporation + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK, OR COMPAQ COMPUTER CORPORATION, BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik, or Compaq + Computer Corporation, shall not be used in advertising or otherwise to + promote the sale, use or other dealings in this Software without prior + written authorization from both the author and Compaq Computer Corporation. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, bc; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fread (&bc, sizeof (int), 1, ifile); + for (;;) { + k = fread (buf, sizeof (char), FLPSIZ, ifile); + if (k == 0) break; + fwrite (buf, sizeof (char), k, ofile); } + fclose (ifile); + fclose (ofile); +} + +exit (0); +} diff --git a/converters/mtcvtfix.c b/converters/mtcvtfix.c new file mode 100644 index 0000000..bc0f922 --- /dev/null +++ b/converters/mtcvtfix.c @@ -0,0 +1,94 @@ +/* This program fixes a SIMH magtape containing a misread EOF + + Copyright (c) 2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, fc, rc; +unsigned int k, tbc; +unsigned char bc[4] = { 0 }; +unsigned char bceof[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 4, ifile); + if (k == 0) break; + tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | + ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + if (tbc) { + printf ("Record size = %d\n", tbc); + if (tbc > FLPSIZ) { + printf ("Record too big\n"); + return 0; } + k = fread (buf, sizeof (char), tbc, ifile); + for ( ; k < tbc; k++) buf[k] = 0; + fread (bc, sizeof (char), 4, ifile); + if (tbc > 1) { + fwrite (bc, sizeof (char), 4, ofile); + fwrite (buf, sizeof (char), tbc, ofile); + fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { + printf ("Record length = 1, ignored\n"); + } } + else { + fwrite (bceof, sizeof (char), 4, ofile); + if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/mtcvtodd.c b/converters/mtcvtodd.c new file mode 100644 index 0000000..e00c357 --- /dev/null +++ b/converters/mtcvtodd.c @@ -0,0 +1,87 @@ +/* This program converts an E11 magtape (with odd record sizes) to SIMH + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, tbc, ebc, fc, rc; +unsigned char bc[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 4, ifile); + if (k == 0) break; + tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | + ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + ebc = (tbc + 1) & ~1; + fwrite (bc, sizeof (char), 4, ofile); + if (tbc) { + printf ("Record size = %d\n", tbc); + if (tbc > FLPSIZ) { + printf ("Record too big\n"); + return 0; } + k = fread (buf, sizeof (char), tbc, ifile); + for ( ; k < ebc; k++) buf[k] = 0; + fread (bc, sizeof (char), 4, ifile); + fwrite (buf, sizeof (char), ebc, ofile); + fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/mtcvtv23.c b/converters/mtcvtv23.c new file mode 100644 index 0000000..3b150c5 --- /dev/null +++ b/converters/mtcvtv23.c @@ -0,0 +1,81 @@ +/* This program converts a pre V2.3 simulated magtape to a V2.3 magtape + + Copyright (c) 1993-1999, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, wc, fc, rc; +unsigned char bc[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); + else strcat (oname, ".tap"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 2, ifile); + if (k == 0) break; + wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + wc = (wc + 1) & ~1; + fwrite (bc, sizeof (char), 4, ofile); + if (wc) { + k = fread (buf, sizeof (char), wc, ifile); + for ( ; k < wc; k++) buf[k] =0; + fwrite (buf, sizeof (char), wc, ofile); + fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/mtcvtv23.txt b/converters/mtcvtv23.txt new file mode 100644 index 0000000..41b027d --- /dev/null +++ b/converters/mtcvtv23.txt @@ -0,0 +1,35 @@ +mtcvtv23 converts a tape images in .tpc format to .tap format. + +.tpc format tape images have the format + + 2 byte record length 1 + record 1 + 2 byte record length 2 + record 2 + : + 2 bytes = 0000 for end of file + +and so on. This is the format produced by various UNIX utilities +for dumping tape images. + +.tap format tape images have the format + + 4 byte record length 1 + record 1 + repeat of 4 byte record length 1 + 4 byte record length 2 + record 2 + repeat of 4 byte record length 2 + : + 4 bytes = 00000000 for end of file + +and so on. This is the tape format expected by simh, Tim Stark's TS10, +and John Wilson's E11. + +mtcvtv23 is invoked by + + mtcvtv23 file1 file2 file3... + +Each file in turn is converted from .tpc format to .tap format. The +input file can have any extension; the converted file will have a .tap +extension. \ No newline at end of file diff --git a/converters/sfmtcvt.c b/converters/sfmtcvt.c new file mode 100644 index 0000000..d558d44 --- /dev/null +++ b/converters/sfmtcvt.c @@ -0,0 +1,116 @@ +/* This program converts a Motorola S format PROM dump to a binary file + + Copyright (c) 1993-2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define HEX(a) ((a) >= 'A'? (a) - 'A' + 10: (a) - '0') +#define MAXA (2 << 14) +#define MAXR 4 + +int main (int argc, char *argv[]) +{ +int i, d, d1, j, k, astrt, dstrt, addr, maxaddr[MAXR]; +int numr, numf; +unsigned char data[MAXR][MAXA]; +char *s, *ppos, *cptr, line[256], oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +s = argv[1]; +if ((s != NULL) && (*s++ == '-')) { + ++argv; --argc; + switch (*s) { + case '1': + numr = 1; break; + case '2': + numr = 2; break; + case '4': + numr = 4; break; + default: + fprintf (stderr, "Bad option %c\n", *s); + return 0; } + } +else numr = 1; + +for (i = 1, numf = 0; i < argc; i++) { + ifile = fopen (argv[i], "r"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + astrt = 4; + maxaddr[numf] = 0; + for (;;) { + cptr = fgets (line, 256, ifile); + if (cptr == NULL) break; + if (line[0] != 'S') continue; + if (line[1] == '1') dstrt = 8; + else if (line[1] == '2') dstrt = 10; + else continue; + for (k = astrt, addr = 0; k < dstrt; k++) { + d = HEX (line[k]); + addr = (addr << 4) + d; } + if (addr >= MAXA) { + printf ("Address %o out of range\n", addr); + break; } + for (k = dstrt; k < (dstrt + 32); k = k + 2, addr++) { + d = HEX (line[k]); + d1 = HEX (line[k+1]); + data[numf][addr] = (d << 4) + d1; } + if (addr > maxaddr[numf]) maxaddr[numf] = addr; + } + fclose (ifile); + numf++; + if (numf >= numr) { + for (k = 0; k < numr; k++) { + if (maxaddr[k] != maxaddr[0]) { + printf ("Rom lengths don't match, file 1 = %d, file %d = %d\n", + maxaddr[0], k, maxaddr[k]); + return 0; } } + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".bin"); + else strcat (oname, ".bin"); + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + printf ("Output file: %s, ROM size is %d\n", oname, maxaddr[0]); + for (k = 0; k < maxaddr[0]; k++) { + for (j = numr - 1; j >= 0; j--) { + fwrite (&data[j][k], 1, 1, ofile); } } + fclose (ofile); + numf = 0; + } + } +if (numf) printf ("Unprocessed files\n"); +return 0; +} diff --git a/converters/tp512cvt.c b/converters/tp512cvt.c new file mode 100644 index 0000000..cd5af60 --- /dev/null +++ b/converters/tp512cvt.c @@ -0,0 +1,76 @@ +/* This program converts a tp data file to a simulated 512B blocked magtape + + Copyright (c) 1993-2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define BLKSIZ 512 +int main (int argc, char *argv[]) +{ +int i, k; +unsigned char buf[BLKSIZ]; +unsigned char tef[4] = { 0, 0, 0, 0 }; +unsigned char twc[4] = { 0, 2, 0, 0 }; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); + else strcat (oname, ".tap"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (;;) { + k = fread (buf, sizeof (char), BLKSIZ, ifile); + if (k == 0) break; + if (k != BLKSIZ) { + printf ("Short block, size = %d\n", k); + for ( ; k < BLKSIZ; k++) buf[k] = 0; } + fwrite (twc, sizeof (char), 4, ofile); + fwrite (buf, sizeof (char), BLKSIZ, ofile); + fwrite (twc, sizeof (char), 4, ofile); + } + fwrite (tef, sizeof (char), 4, ofile); + fwrite (tef, sizeof (char), 4, ofile); + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/crossassemblers/macro1.c b/crossassemblers/macro1.c new file mode 100644 index 0000000..a3d6799 --- /dev/null +++ b/crossassemblers/macro1.c @@ -0,0 +1,3068 @@ +/* + * $Id: macro1.c,v 1.74 2003/10/23 23:29:17 phil Exp $ + * + * TODO: + * have flex() use nextfiodec()?? (what if legal in repeat???) + * "flexx" should give right justified result??? + * squawk if nextfiodec called in a repeat w/ a delim? + * + * forbid variables/constants in macros + * forbid text in repeat?? + * forbid start in repeat or macro + * use same error TLA's as MACRO??? + * IPA error for overbar on LHS of = + * variables returns value?? + * + * macro addressing: labels defined during macro are local use only???? + * spacewar expects this??? (is it wrong?) + * + * self-feeding lines: \n legal anywhere \t is + * read next token into "token" buffer -- avoid saving "line"? + * remove crocks from "define" + * list title (first line of file) should not be parsed as source? + * incorrect listing for bare "start" + * list only 4 digits for address column + * + * other; + * note variables in symbol dump, xref? + * no "permenant" symbols; flush -p? rename .ini? + * keep seperate macro/pseudo table? + * verify bad input(?) detected + * implement dimension pseudo? + * remove crocks for '/' and ','? + */ + +/* + * Program: MACRO1 + * File: macro1.c + * Author: Gary A. Messenbrink (macro8) + * MACRO7 modifications: Bob Supnik + * MACRO1 modifications: Bob Supnik + * slashed to be more like real MACRO like by Phil Budne + * + * Purpose: A 2 pass PDP-1 assembler + * + * NAME + * macro1 - a PDP-1 assembler. + * + * SYNOPSIS: + * macro1 [ -d -p -m -r -s -x ] inputfile inputfile... + * + * DESCRIPTION + * This is a cross-assembler to for PDP-1 assembly language programs. + * It will produce an output file in rim format only. + * A listing file is always produced and with an optional symbol table + * and/or a symbol cross-reference (concordance). The permanent symbol + * table can be output in a form that may be read back in so a customized + * permanent symbol table can be produced. Any detected errors are output + * to a separate file giving the filename in which they were detected + * along with the line number, column number and error message as well as + * marking the error in the listing file. + * The following file name extensions are used: + * .mac source code (input) + * .lst assembly listing (output) + * .rim assembly output in DEC's rim format (output) + * .prm permanent symbol table in form suitable for reading after + * the EXPUNGE pseudo-op. + * .sym "symbol punch" tape (for DDT, or reloading into macro) + * + * OPTIONS + * -d Dump the symbol table at end of assembly + * -p Generate a file with the permanent symbols in it. + * (To get the current symbol table, assemble a file than has only + * START in it.) + * -x Generate a cross-reference (concordance) of user symbols. + * -r Output a tape using only RIM format (else output block loader) + * -s Output a symbol dump tape (loader + loader blocks) + * -S file + * Read a symbol tape back in + * + * DIAGNOSTICS + * Assembler error diagnostics are output to an error file and inserted + * in the listing file. Each line in the error file has the form + * + * :: : error: at Loc = + * + * An example error message is: + * + * bintst.7:17:9 : error: undefined symbol "UNDEF" at Loc = 07616 + * + * The error diagnostics put in the listing start with a two character + * error code (if appropriate) and a short message. A carat '^' is + * placed under the item in error if appropriate. + * An example error message is: + * + * 17 07616 3000 DAC UNDEF + * UD undefined ^ + * 18 07617 1777 TAD I DUMMY + * + * Undefined symbols are marked in the symbol table listing by prepending + * a '?' to the symbol. Redefined symbols are marked in the symbol table + * listing by prepending a '#' to the symbol. Examples are: + * + * #REDEF 04567 + * SWITCH 07612 + * ?UNDEF 00000 + * + * Refer to the code for the diagnostic messages generated. + * + * REFERENCES: + * This assembler is based on the pal assember by: + * Douglas Jones and + * Rich Coon + * + * COPYRIGHT NOTICE: + * This is free software. There is no fee for using it. You may make + * any changes that you wish and also give it away. If you can make + * a commercial product out of it, fine, but do not put any limits on + * the purchaser's right to do the same. If you improve it or fix any + * bugs, it would be nice if you told me and offered me a copy of the + * new version. + * + * + * Amendments Record: + * Version Date by Comments + * ------- ------- --- --------------------------------------------------- + * v1.0 12Apr96 GAM Original + * v1.1 18Nov96 GAM Permanent symbol table initialization error. + * v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. + * v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). + * v1.4 29Nov96 GAM Fixed bug in checksum generation. + * v2.1 08Dec96 GAM Added concordance processing (cross reference). + * v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). + * v2.3 2Feb97 GAM Fixed paging problem in cross reference output. + * v3.0 14Feb97 RMS MACRO8X features. + * 8Mar97 RMS MACRO7 released w/ sim8swre + * 13Mar97 RMS MACRO1 released w/ lispswre + * 28Nov01 RMS MACRO1 released w/ simtools + * 5Mar03 DP MACRO1 released w/ ddt1 + * 2003 PLB major reworking, assembles MACRO, DDT + */ + + +#include +#include +#include +#include + +#define LINELEN 96 +#define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ +#define NAMELEN 128 +#define SYMBOL_COLUMNS 5 +#define SYMLEN 7 +/*#define SYMSIG 4 /* EXP: significant chars in a symbol */ +#define SYMBOL_TABLE_SIZE 8192 +#define MAC_MAX_ARGS 20 +#define MAC_MAX_LENGTH 8192 +#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ + +#define MAX_LITERALS 1000 +#define MAX_CONSTANTS 10 /* max number of "constants" blocks */ + +#define XREF_COLUMNS 8 + +#define ADDRESS_FIELD 0007777 +#define INDIRECT_BIT 0010000 +#define OP_CODE 0760000 + +#define CONCISE_LC 072 +#define CONCISE_UC 074 + +/* Macro to get the number of elements in an array. */ +#define DIM(a) (sizeof(a)/sizeof(a[0])) + +#define ISBLANK(c) ((c==' ') || (c=='\f')) +#define ISEND(c) ((c=='\0')|| (c=='\n') || (c == '\t')) +#define ISDONE(c) ((c=='/') || ISEND(c)) + +#define ISOVERBAR(c) (c == '\\' || c == '~') + +/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ +/* (true) if the stated condtion is met. */ +/* Use these to test attributes. The proper bits are extracted and then */ +/* tested. */ +#define M_DEFINED(s) (((s) & DEFINED) == DEFINED) +#define M_DUPLICATE(s) (((s) & DUPLICATE) == DUPLICATE) +#define M_FIXED(s) (((s) & FIXED) == FIXED) +#define M_LABEL(s) (((s) & LABEL) == LABEL) +#define M_PSEUDO(s) (((s) & PSEUDO) == PSEUDO) +#define M_EPSEUDO(s) (((s) & EPSEUDO) == EPSEUDO) +#define M_MACRO(s) (((s) & MACRO) == MACRO) +#define M_NOTRDEF(s) (((s) & NOTRDEF) != 0) + +typedef unsigned char BOOL; +typedef unsigned char BYTE; +typedef int WORD32; + +#ifndef FALSE + #define FALSE 0 + #define TRUE (!FALSE) +#endif + +/* Line listing styles. Used to control listing of lines. */ +enum linestyle_t +{ + LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL, LINE_LOC +}; +typedef enum linestyle_t LINESTYLE_T; + +/* Symbol Types. */ +/* Note that the names that have FIX as the suffix contain the FIXED bit */ +/* included in the value. */ +enum symtyp +{ + UNDEFINED = 0000, + DEFINED = 0001, + FIXED = 0002, + LABEL = 0010 | DEFINED, + REDEFINED = 0020 | DEFINED, + DUPLICATE = 0040 | DEFINED, + PSEUDO = 0100 | FIXED | DEFINED, + EPSEUDO = 0200 | FIXED | DEFINED, + MACRO = 0400 | DEFINED, + DEFFIX = DEFINED | FIXED, + NOTRDEF = (MACRO | PSEUDO | LABEL | FIXED) & ~DEFINED +}; +typedef enum symtyp SYMTYP; + +enum pseudo_t { + DECIMAL, + DEFINE, + FLEX, + CONSTANTS, + OCTAL, + REPEAT, + START, + CHAR, + VARIABLES, + TEXT, + NOINPUT, + EXPUNGE +}; +typedef enum pseudo_t PSEUDO_T; + +struct sym_t +{ + SYMTYP type; + char name[SYMLEN]; + WORD32 val; + WORD32 xref_index; + WORD32 xref_count; +}; +typedef struct sym_t SYM_T; + +struct emsg_t +{ + char *list; + char *file; +}; +typedef struct emsg_t EMSG_T; + +struct errsave_t +{ + char *mesg; + WORD32 col; +}; +typedef struct errsave_t ERRSAVE_T; + +/*----------------------------------------------------------------------------*/ + +/* Function Prototypes */ + +int binarySearch( char *name, int start, int symbol_count ); +int compareSymbols( const void *a, const void *b ); +SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); +void errorLexeme( EMSG_T *mesg, WORD32 col ); +void errorMessage( EMSG_T *mesg, WORD32 col ); +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); +SYM_T eval( void ); +SYM_T *evalSymbol( void ); +void getArgs( int argc, char *argv[] ); +SYM_T getExpr( void ); +WORD32 getExprs( void ); +WORD32 incrementClc( void ); +WORD32 literal( WORD32 value ); +BOOL isLexSymbol(); +char *lexemeToName( char *name, WORD32 from, WORD32 term ); +void listLine( void ); +SYM_T *lookup( char *name, int type ); +void moveToEndOfLine( void ); +void next(int); +void onePass( void ); +void printCrossReference( void ); +void printErrorMessages( void ); +void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); +void printPageBreak( void ); +void printPermanentSymbolTable( void ); +void printSymbolTable( void ); +BOOL pseudo( PSEUDO_T val ); +void punchLocObject( WORD32 loc, WORD32 val ); +void punchOutObject( WORD32 loc, WORD32 val ); +void punchLeader( WORD32 count ); +void punchLoader( void ); +void flushLoader( void ); +void readLine( void ); +void saveError( char *mesg, WORD32 cc ); +void topOfForm( char *title, char *sub_title ); +void constants(void); +void variables(void); +void eob(void); +void dump_symbols(void); + +/*----------------------------------------------------------------------------*/ + +/* Table of pseudo-ops (directives) which are used to setup the symbol */ +/* table on startup */ +SYM_T pseudos[] = +{ + { PSEUDO, "consta", CONSTANTS }, + { PSEUDO, "define", DEFINE }, /* Define macro. */ + { PSEUDO, "repeat", REPEAT }, + { PSEUDO, "start", START }, /* Set starting address. */ + { PSEUDO, "variab", VARIABLES }, + { PSEUDO, "text", TEXT }, + { PSEUDO, "noinpu", NOINPUT }, + { PSEUDO, "expung", EXPUNGE }, +/* the following can appear in expressions: */ + { EPSEUDO, "charac", CHAR }, + { EPSEUDO, "decima", DECIMAL }, /* base 10. */ + { EPSEUDO, "flexo", FLEX }, + { EPSEUDO, "octal", OCTAL }, /* Read literal constants in base 8. */ +}; + +/* Symbol Table */ +/* The table is put in lexical order on startup, so symbols can be */ +/* inserted as desired into the initial table. */ +#define DIO 0320000 +#define JMP 0600000 +SYM_T permanent_symbols[] = +{ + /* Memory Reference Instructions */ + { DEFFIX, "and", 0020000 }, + { DEFFIX, "ior", 0040000 }, + { DEFFIX, "xor", 0060000 }, + { DEFFIX, "xct", 0100000 }, + { DEFFIX, "lac", 0200000 }, + { DEFFIX, "lio", 0220000 }, + { DEFFIX, "dac", 0240000 }, + { DEFFIX, "dap", 0260000 }, + { DEFFIX, "dip", 0300000 }, + { DEFFIX, "dio", 0320000 }, + { DEFFIX, "dzm", 0340000 }, + { DEFFIX, "add", 0400000 }, + { DEFFIX, "sub", 0420000 }, + { DEFFIX, "idx", 0440000 }, + { DEFFIX, "isp", 0460000 }, + { DEFFIX, "sad", 0500000 }, + { DEFFIX, "sas", 0520000 }, + { DEFFIX, "mul", 0540000 }, + { DEFFIX, "mus", 0540000 }, /* for spacewar */ + { DEFFIX, "div", 0560000 }, + { DEFFIX, "dis", 0560000 }, /* for spacewar */ + { DEFFIX, "jmp", 0600000 }, + { DEFFIX, "jsp", 0620000 }, + { DEFFIX, "skip", 0640000 }, /* for spacewar */ + { DEFFIX, "cal", 0160000 }, + { DEFFIX, "jda", 0170000 }, + { DEFFIX, "i", 0010000 }, + { DEFFIX, "skp", 0640000 }, + { DEFFIX, "law", 0700000 }, + { DEFFIX, "iot", 0720000 }, + { DEFFIX, "opr", 0760000 }, + { DEFFIX, "nop", 0760000 }, + /* Shift Instructions */ + { DEFFIX, "ral", 0661000 }, + { DEFFIX, "ril", 0662000 }, + { DEFFIX, "rcl", 0663000 }, + { DEFFIX, "sal", 0665000 }, + { DEFFIX, "sil", 0666000 }, + { DEFFIX, "scl", 0667000 }, + { DEFFIX, "rar", 0671000 }, + { DEFFIX, "rir", 0672000 }, + { DEFFIX, "rcr", 0673000 }, + { DEFFIX, "sar", 0675000 }, + { DEFFIX, "sir", 0676000 }, + { DEFFIX, "scr", 0677000 }, + { DEFFIX, "1s", 0000001 }, + { DEFFIX, "2s", 0000003 }, + { DEFFIX, "3s", 0000007 }, + { DEFFIX, "4s", 0000017 }, + { DEFFIX, "5s", 0000037 }, + { DEFFIX, "6s", 0000077 }, + { DEFFIX, "7s", 0000177 }, + { DEFFIX, "8s", 0000377 }, + { DEFFIX, "9s", 0000777 }, + /* Skip Microinstructions */ + { DEFFIX, "sza", 0640100 }, + { DEFFIX, "spa", 0640200 }, + { DEFFIX, "sma", 0640400 }, + { DEFFIX, "szo", 0641000 }, + { DEFFIX, "spi", 0642000 }, + { DEFFIX, "szs", 0640000 }, + { DEFFIX, "szf", 0640000 }, + /*{ DEFFIX, "clo", 0651600 },*/ + + /* Operate Microinstructions */ + { DEFFIX, "clf", 0760000 }, + { DEFFIX, "stf", 0760010 }, + { DEFFIX, "cla", 0760200 }, + /*{ DEFFIX, "lap", 0760300 },*/ + { DEFFIX, "hlt", 0760400 }, + { DEFFIX, "xx", 0760400 }, + { DEFFIX, "cma", 0761000 }, + { DEFFIX, "clc", 0761200 }, + { DEFFIX, "lat", 0762200 }, + { DEFFIX, "cli", 0764000 }, + /* IOT's */ + /*{ DEFFIX, "ioh", 0730000 },*/ + { DEFFIX, "rpa", 0730001 }, + { DEFFIX, "rpb", 0730002 }, + { DEFFIX, "rrb", 0720030 }, + { DEFFIX, "ppa", 0730005 }, + { DEFFIX, "ppb", 0730006 }, + { DEFFIX, "tyo", 0730003 }, + { DEFFIX, "tyi", 0720004 }, + { DEFFIX, "dpy", 0730007 }, /* for spacewar, munching squares! */ + { DEFFIX, "lsm", 0720054 }, + { DEFFIX, "esm", 0720055 }, + { DEFFIX, "cbs", 0720056 }, + { DEFFIX, "lem", 0720074 }, + { DEFFIX, "eem", 0724074 }, + { DEFFIX, "cks", 0720033 }, +}; /* End-of-Symbols for Permanent Symbol Table */ + +/* Global variables */ +SYM_T *symtab; /* Symbol Table */ +int symbol_top; /* Number of entries in symbol table. */ + +#define LOADERBASE 07751 + +/* make it relocatable (DDT expects it at 7751) */ +#define LOADER_IN LOADERBASE +#define LOADER_B (LOADERBASE+06) +#define LOADER_A (LOADERBASE+07) +#define LOADER_CK (LOADERBASE+025) +#define LOADER_EN1 (LOADERBASE+026) + +WORD32 loader[] = { + 0730002, /* in, rpb */ + 0320000+LOADER_A, /* dio a */ + 0100000+LOADER_A, /* xct a */ + 0320000+LOADER_CK, /* dio ck */ + 0730002, /* rpb */ + 0320000+LOADER_EN1, /* dio en1 */ + 0730002, /* b, rpb */ + 0000000, /* a, xx */ + 0210000+LOADER_A, /* lac i a */ + 0400000+LOADER_CK, /* add ck */ + 0240000+LOADER_CK, /* dac ck */ + 0440000+LOADER_A, /* idx a */ + 0520000+LOADER_EN1, /* sas en1 */ + 0600000+LOADER_B, /* jmp b */ + 0200000+LOADER_CK, /* lac ck */ + 0400000+LOADER_EN1, /* add en1 */ + 0730002, /* rpb */ + 0320000+LOADER_CK, /* dio ck */ + 0520000+LOADER_CK, /* sas ck */ + 0760400, /* hlt */ + 0600000+LOADER_IN /* jmp in */ + /* ck, 0 */ + /* en1, 0 */ +}; + +#define LOADERBUFSIZE 0100 /* <=0100, power of 2*/ +#define LOADERBUFMASK (LOADERBUFSIZE-1) /* for block alignment */ + +WORD32 loaderbuf[LOADERBUFSIZE]; +WORD32 loaderbufcount; +WORD32 loaderbufstart; + +/*----------------------------------------------------------------------------*/ + +WORD32 *xreftab; /* Start of the concordance table. */ + +ERRSAVE_T error_list[20]; +int save_error_count; + +char s_detected[] = "detected"; +char s_error[] = "error"; +char s_errors[] = "errors"; +char s_no[] = "No"; +char s_page[] = "Page"; +char s_symtable[] = "Symbol Table"; +char s_xref[] = "Cross Reference"; + +/* Assembler diagnostic messages. */ +/* Some attempt has been made to keep continuity with the PAL-III and */ +/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ +/* exists, then the indicator is put in the listing as the first two */ +/* characters of the diagnostic message. The PAL-III indicators where used */ +/* when there was a choice between using MACRO-8 and PAL-III indicators. */ +/* The character pairs and their meanings are: */ +/* DT Duplicate Tag (symbol) */ +/* IC Illegal Character */ +/* ID Illegal Redefinition of a symbol. An attempt was made to give */ +/* a symbol a new value not via =. */ +/* IE Illegal Equals An equal sign was used in the wrong context, */ +/* (e.g., A+B=C, or TAD A+=B) */ +/* II Illegal Indirect An off page reference was made, but a literal */ +/* could not be generated because the indirect bit was already set. */ +/* IR Illegal Reference (address is not on current page or page zero) */ +/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ +/* RD ReDefintion of a symbol */ +/* ST Symbol Table full */ +/* UA Undefined Address (undefined symbol) */ +/* VR Value Required */ +/* ZE Zero Page Exceeded (see above, or out of space) */ +EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; +EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; +EMSG_T illegal_character = { "IC illegal char", "illegal character" }; +EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; +EMSG_T label_syntax = { "IC label syntax", "label syntax" }; +EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; +EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; +EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; +EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; +EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; +EMSG_T illegal_reference = { "IR off page", "illegal reference" }; +EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; +EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; +EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; +EMSG_T value_required = { "VR value required", "value required" }; +EMSG_T literal_gen_off = { "lit generation off", + "literal generation disabled" }; +EMSG_T literal_overflow = { "PE page exceeded", + "current page literal capacity exceeded" }; +EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; +EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; +EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; +EMSG_T illegal_vfd_value = { "width out of range", + "VFD field width not in range" }; +EMSG_T no_literal_value = { "no value", "No literal value" }; +EMSG_T text_string = { "no delimiter", + "Text string delimiters not matched" }; +EMSG_T lt_expected = { "'<' expected", "'<' expected" }; +EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; +EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; +EMSG_T bad_dummy_arg = { "bad dummy arg", + "Bad dummy argument following DEFINE" }; +EMSG_T macro_too_long = { "macro too long", "Macro too long" }; +EMSG_T no_virtual_memory = { "out of memory", + "Insufficient memory for macro" }; +EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; +EMSG_T define_in_repeat = { "define in a repeat", "Define in a repeat" }; + +/*----------------------------------------------------------------------------*/ + +FILE *errorfile; +FILE *infile; +FILE *listfile; +FILE *listsave; +FILE *objectfile; +FILE *objectsave; + +char filename[NAMELEN]; +char listpathname[NAMELEN]; +char sympathname[NAMELEN]; +char objectpathname[NAMELEN]; +char *pathname; +char permpathname[NAMELEN]; + +WORD32 mac_count; /* Total macros defined. */ + +/* + * malloced macro bodies, indexed by sym->val dummies are evaluated at + * invocation time, and value saved in "args"; if recursive macros are + * desired (without conditionals, how would you escape?), keep a name + * list here and move symbols to "macinv" + */ +struct macdef { + int nargs; /* number of args */ + SYM_T args[MAC_MAX_ARGS+1]; /* symbol for each and one for "r" */ + char body[1]; /* malloc'ed accordingly */ +} *mac_defs[MAC_TABLE_LENGTH]; + +struct macinv { /* current macro invocation */ + char mac_line[LINELEN]; /* Saved macro invocation line. */ + WORD32 mac_cc; /* Saved cc after macro invocation. */ + char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ + struct macdef *defn; /* pointer to definition for dummies */ + struct macinv *prev; /* previous invocation in stack */ +} *curmacro; /* macro stack */ + +int nrepeats; /* count of nested repeats */ + +int list_lineno; +int list_pageno; +char list_title[LINELEN]; +BOOL list_title_set; /* Set if TITLE pseudo-op used. */ +char line[LINELEN]; /* Input line. */ +int lineno; /* Current line number. */ +int page_lineno; /* print line number on current page. */ +WORD32 listed; /* Listed flag. */ +WORD32 listedsave; + +WORD32 cc; /* Column Counter (char position in line). */ +WORD32 clc; /* Location counter */ +BOOL end_of_input; /* End of all input files. */ +int errors; /* Number of errors found so far. */ +BOOL error_in_line; /* TRUE if error on current line. */ +int errors_pass_1; /* Number of errors on pass 1. */ +int filix_curr; /* Index in argv to current input file. */ +int filix_start; /* Start of input files in argv. */ +int lexstartprev; /* Where previous lexeme started. */ +int lextermprev; /* Where previous lexeme ended. */ +int lexstart; /* Index of current lexeme on line. */ +int lexterm; /* Index of character after current lexeme. */ +int overbar; /* next saw an overbar in last token */ + +int nconst; /* number of "constants" blocks */ +int lit_count[MAX_CONSTANTS]; /* # of lits in each block in pass 1 */ +WORD32 lit_loc[MAX_CONSTANTS]; /* Base of literal blocks */ + +int noinput; /* don't punch loader */ + +int nvars; /* number of variables */ +WORD32 vars_addr; /* address of "variables" */ +WORD32 vars_end; /* end of "variables" */ + +/* pass 2 only; */ +int nlit; /* number of literals in litter[] */ +WORD32 litter[MAX_LITERALS]; /* literals */ + +WORD32 maxcc; /* Current line length. */ +BOOL nomac_exp; /* No macro expansion */ +WORD32 pass; /* Number of current pass. */ +BOOL print_permanent_symbols; +WORD32 radix; /* Default number radix. */ +BOOL rim_mode; /* RIM mode output. */ +BOOL sym_dump; /* punch symbol tape */ +int save_argc; /* Saved argc. */ +char **save_argv; /* Saved *argv[]. */ +WORD32 start_addr; /* Saved start address. */ +BOOL symtab_print; /* Print symbol table flag */ +BOOL xref; + +SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ + +/* initial data from SIMH v3.0 pdp1_stddev.c (different encoding of UC/LC) */ +#define UC 0100 /* Upper case */ +#define LC 0200 +#define CHARBITS 077 +#define BC LC|UC /* both case bits */ +#define BAD 014 /* unused concise code */ + +unsigned char ascii_to_fiodec[128] = { + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + BC|075, BC|036, BAD, BAD, BAD, BC|077, BAD, BAD, + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + BC|000, UC|005, UC|001, UC|004, BAD, BAD, UC|006, UC|002, + LC|057, LC|055, UC|073, UC|054, LC|033, LC|054, LC|073, LC|021, + LC|020, LC|001, LC|002, LC|003, LC|004, LC|005, LC|006, LC|007, + LC|010, LC|011, BAD, BAD, UC|007, UC|033, UC|010, UC|021, + LC|040, UC|061, UC|062, UC|063, UC|064, UC|065, UC|066, UC|067, + UC|070, UC|071, UC|041, UC|042, UC|043, UC|044, UC|045, UC|046, + UC|047, UC|050, UC|051, UC|022, UC|023, UC|024, UC|025, UC|026, + UC|027, UC|030, UC|031, UC|057, LC|056, UC|055, UC|011, UC|040, + UC|020, LC|061, LC|062, LC|063, LC|064, LC|065, LC|066, LC|067, + LC|070, LC|071, LC|041, LC|042, LC|043, LC|044, LC|045, LC|046, + LC|047, LC|050, LC|051, LC|022, LC|023, LC|024, LC|025, LC|026, + LC|027, LC|030, LC|031, BAD, UC|056, BAD, UC|003, BC|075 +}; + +/* for symbol punch tape conversion only!! */ +char fiodec_to_ascii[64] = { + 0, '1', '2', '3', '4', '5', '6', '7', + '8', '9', 0, 0, 0, 0, 0, 0, + '0', 0, 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', 0, 0, 0, 0, 0, 0, + 0, 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 0, 0, 0, 0, 0, 0, + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 0, 0, 0, 0, 0, 0 }; + +/* used at startup & for expunge */ +void +init_symtab(void) { + /* Place end marker in symbol table. */ + symtab[0] = sym_undefined; + symbol_top = 0; +} + +/* Function: main */ +/* Synopsis: Starting point. Controls order of assembly. */ +int +main( int argc, char *argv[] ) +{ + int ix; + int space; + + save_argc = argc; + save_argv = argv; + + /* Set the default values for global symbols. */ + print_permanent_symbols = FALSE; + nomac_exp = TRUE; + rim_mode = FALSE; /* default to loader tapes */ + sym_dump = FALSE; + noinput = FALSE; + + symtab_print = FALSE; + xref = FALSE; + pathname = NULL; + + /* init symbol table before processing arguments, so we can + * load symbol punch tapes on the fly + */ + + /* + * Setup the error file in case symbol table overflows while + * installing the permanent symbols. + */ + errorfile = stderr; + pass = 0; /* required for symbol table init */ + symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); + + if( symtab == NULL ) { + fprintf( stderr, "Could not allocate memory for symbol table.\n"); + exit( -1 ); + } + + init_symtab(); + + /* Enter the pseudo-ops into the symbol table */ + for( ix = 0; ix < DIM( pseudos ); ix++ ) + defineSymbol( pseudos[ix].name, pseudos[ix].val, pseudos[ix].type, 0 ); + + /* Enter the predefined symbols into the table. */ + /* Also make them part of the permanent symbol table. */ + for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type, 0 ); + + /* Get the options and pathnames */ + getArgs( argc, argv ); + + /* Do pass one of the assembly */ + pass = 1; + onePass(); + errors_pass_1 = errors; + + /* Set up for pass two */ + objectfile = fopen( objectpathname, "wb" ); + objectsave = objectfile; + + listfile = fopen( listpathname, "w" ); + listsave = listfile; + + /* XXX punch title into tape! */ + punchLeader( 0 ); + if (!rim_mode) { + punchLoader(); + punchLeader(5); + } + + if (nlit > 0) + constants(); /* implied "constants"? */ + + /* Do pass two of the assembly */ + errors = 0; + save_error_count = 0; + + if( xref ) { + /* Get the amount of space that will be required for the concordance */ + for( space = 0, ix = 0; ix < symbol_top; ix++ ) { + symtab[ix].xref_index = space; /* Index into concordance table. */ + space += symtab[ix].xref_count + 1; + symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ + } + /* Allocate & clear the necessary space. */ + xreftab = (WORD32 *) calloc( space, sizeof( WORD32 )); + } + pass = 2; + onePass(); + + objectfile = objectsave; + + /* Works great for trailer. */ + punchLeader( 1 ); + + /* undo effects of NOLIST for any following output to listing file. */ + listfile = listsave; + + /* Display value of error counter. */ + if( errors == 0 ) { + fprintf( listfile, "\n %s %s %s\n", s_no, s_errors, s_detected ); + } + else { + fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( listfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + } + + if( symtab_print ) + printSymbolTable(); + + if( print_permanent_symbols ) + printPermanentSymbolTable(); + + if( xref ) + printCrossReference(); + + fclose( objectfile ); + fclose( listfile ); + if( errors == 0 && errors_pass_1 == 0 ) { + /* after closing objectfile -- we reuse the FILE *!! */ + if (sym_dump) + dump_symbols(); + } + else + remove( objectpathname ); + + return( errors != 0 ); +} /* main() */ + +/* read a word from a binary punch file */ +WORD32 +getw(FILE *f) +{ + int i, c; + WORD32 w; + + w = 0; + for (i = 0; i < 3;) { + c = getc(f); + if (c == -1) + return -1; + if (c & 0200) { /* ignore if ch8 not punched */ + w <<= 6; + w |= c & 077; + i++; + } + } + return w; +} + +/* + * "permute zone bits" like MACRO does for proper sorting + * (see routine "per" in MACRO) -- it's what DDT expects + * + * it's it's own inverse! + */ + +WORD32 +permute(WORD32 name) +{ + WORD32 temp; + + temp = name & 0202020; /* get zone bits */ + temp = ((temp << 1) & 0777777) | ((temp >> 17) & 1); /* rotate left */ + name ^= temp; /* flip zone bits */ + name ^= 0400000; /* toggle sign */ + return name; +} + +/* add a symbol from a "symbol punch" tape */ +void +addsym(WORD32 sym, WORD32 val) +{ + char name[4]; + + sym = permute(sym); + name[0] = fiodec_to_ascii[(sym >>12) & 077]; + name[1] = fiodec_to_ascii[(sym >> 6) & 077]; + name[2] = fiodec_to_ascii[sym & 077]; + name[3] = '\0'; + defineSymbol( name, val, LABEL, 0); +} + +void +read_symbols(char *fname) +{ + FILE *f; + + f = fopen(fname, "rb"); + if (!f) { + perror(fname); + exit(1); + } + + /* skip loader */ + for (;;) { + WORD32 w; + + w = getw(f); + if (w == -1) + goto err; /* XXX complain? */ + if ((w & OP_CODE) == JMP) + break; + if ((w & OP_CODE) != DIO) + goto err; /* XXX complain? */ + w = getw(f); + if (w == -1) + goto err; /* XXX complain? */ + } + + + /* XXX should push block reader down into a co-routine */ + for (;;) { + WORD32 start, end, sum; + + start = getw(f); + if ((start & OP_CODE) == JMP) { + fclose(f); + return; + } + + if (start == -1 || (start & OP_CODE) != DIO) + goto err; + + end = getw(f); + if (end == -1 || (end & OP_CODE) != DIO) + goto err; /* XXX complain? */ + + sum = start + end; + while (start < end) { + WORD32 sym, val; + sym = getw(f); + if (sym == -1) + goto err; + sum += sym; + start++; + /* XXX handle block boundaries? */ + if (start >= end) + goto err; + val = getw(f); + if (val == -1) + goto err; + /*printf("%06o %06o\n", sym, val);*/ + addsym(sym, val); + sum += val; + start++; + } + start = getw(f); /* eat checksum XXX verify? */ + if (start == -1) + goto err; + /* roll over all the overflows at once */ + if (sum & ~0777777) { + sum = (sum & 0777777) + (sum >> 18); + if (sum & 01000000) /* one more time */ + sum++; + } + if (start != sum) + goto err; + } +err: + fprintf(stderr, "error reading symbol file %s\n", fname); + exit(1); +} + +/* Function: getArgs */ +/* Synopsis: Parse command line, set flags accordingly and setup input and */ +/* output files. */ +void getArgs( int argc, char *argv[] ) +{ + WORD32 len; + WORD32 ix, jx; + + /* Set the defaults */ + infile = NULL; + listfile = NULL; + listsave = NULL; + objectfile = NULL; + objectsave = NULL; + + for( ix = 1; ix < argc; ) + { + if( argv[ix][0] == '-' ) + { + char *switches = argv[ix++]; + for( jx = 1; switches[jx] != 0; jx++ ) + { + switch( switches[jx] ) + { + case 'd': + symtab_print = TRUE; + break; + + case 'r': + rim_mode = TRUE; /* punch pure rim-mode tapes */ + break; + + case 's': + sym_dump = TRUE; + break; + + case 'm': + nomac_exp = FALSE; + break; + + case 'p': + print_permanent_symbols = TRUE; + break; + + case 'x': + xref = TRUE; + break; + + case 'S': + if (ix <= argc) + read_symbols(argv[ix++]); + break; + + default: + fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); + fprintf( stderr, " -d -- dump symbol table\n" ); + fprintf( stderr, " -m -- output macro expansions\n" ); + fprintf( stderr, " -p -- output permanent symbols to file\n" ); + fprintf( stderr, " -r -- output RIM format file\n" ); + fprintf( stderr, " -s -- output symbol punch tape to file\n" ); + fprintf( stderr, " -S file -- read symbol punch tape\n" ); + fprintf( stderr, " -x -- output cross reference to file\n" ); + fflush( stderr ); + exit( -1 ); + } /* end switch */ + } /* end for */ + } + else + { + filix_start = ix; + pathname = argv[ix]; + break; + } + } /* end for */ + + if( pathname == NULL ) + { + fprintf( stderr, "%s: no input file specified\n", argv[0] ); + exit( -1 ); + } + + len = strlen( pathname ); + if( len > NAMELEN - 5 ) + { + fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); + exit( -1 ); + } + + /* Now make the pathnames */ + /* Find last '.', if it exists. */ + jx = len - 1; + while( pathname[jx] != '.' && pathname[jx] != '/' + && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + + switch( pathname[jx] ) + { + case '.': + break; + + case '/': + case '\\': + jx = len; + break; + + default: + break; + } + + /* Add the pathname extensions. */ + strncpy( objectpathname, pathname, jx ); + objectpathname[jx] = '\0'; + strcat( objectpathname, ".rim"); + + strncpy( listpathname, pathname, jx ); + listpathname[jx] = '\0'; + strcat( listpathname, ".lst" ); + + strncpy( permpathname, pathname, jx ); + permpathname[jx] = '\0'; + strcat( permpathname, ".prm" ); + + strncpy( sympathname, pathname, jx ); + sympathname[jx] = '\0'; + strcat( sympathname, ".sym" ); + + /* Extract the filename from the path. */ + if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) + pathname[1] = '\\'; /* MS-DOS style pathname */ + + jx = len - 1; + while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) + jx--; + strcpy( filename, &pathname[jx + 1] ); +} /* getArgs() */ + + +int +invokeMacro(int index) +{ + struct macinv *mip; + struct macdef *mdp; + int jx; + + mdp = mac_defs[index]; + if (mdp == NULL || mdp->body[0] == '\0') + return 0; + + /* Find arguments. */ + while (ISBLANK(line[lexstart])) + next(0); + + mip = calloc(1, sizeof(struct macinv)); + if (!mip) { + fprintf(stderr, "could not allocate memory for macro invocation\n"); + exit(1); + } + mip->defn = mdp; + + /* evaluate args, saving values in SYM_T entries in defn. + * (cannot have recursive macros) + */ + mdp->args[0].val = clc; /* r is location at start */ + for( jx = 1; !ISDONE(line[lexstart]) && jx <= MAC_MAX_ARGS; ) { + WORD32 val; + + next(0); + if (ISDONE(line[lexstart])) + break; + + if (line[lexstart] == ',') + next(0); + + while( ISBLANK( line[lexstart] )) + next(0); + + if (ISDONE(line[lexstart])) + break; + + val = getExprs(); + + /* ignore excess values silently? */ + if (jx <= mdp->nargs) + mdp->args[jx].val = val; + jx++; + } /* end for */ + + /* XXX complain if too few actuals? -- nah */ + while (jx <= mdp->nargs) + mdp->args[jx++].val = 0; + + strcpy(mip->mac_line, line); /* save line */ + mip->mac_cc = cc; /* save position in line */ + mip->mac_ptr = mdp->body; + mip->prev = curmacro; /* push the old entry */ + curmacro = mip; /* step up to the plate! */ + return 1; +} + +/* process input; used by onePass and repeat */ +void +processLine() { + if (!list_title_set) { + char *cp; + + /* assert(sizeof(title) >= sizeof(line)); */ + strcpy(list_title, line); + + if ((cp = strchr(list_title, '\n'))) + *cp = '\0'; + + if (list_title[0]) { + list_title_set = TRUE; + fprintf(stderr, "%s - pass %d\n", list_title, pass ); + /* XXX punch title into tape banner (until an '@' seen) */ + } + return; + } + + for (;;) { + int jx; + SYM_T evalue; + + next(0); + if( end_of_input ) + return; + + if( ISEND( line[lexstart] )) { + if (line[lexstart] != '\t') + return; + continue; + } + if (line[lexstart] == '/') /* comment? */ + return; /* done */ + + /* look ahead for 'exp/' */ + /* skip until whitespace or terminator */ + for( jx = lexstart; jx < maxcc; jx++ ) + if( ISBLANK(line[jx]) || ISDONE(line[jx])) + break; + if( line[jx] == '/') { /* EXP/ set location */ + WORD32 newclc; + + newclc = getExprs(); + + /* Do not change Current Location Counter if an error occurred. */ + if( !error_in_line ) + clc = newclc; + + printLine( line, newclc, 0, LINE_LOC ); + cc = jx + 1; + next(0); /* discard slash */ + continue; + } + + switch( line[lexterm] ) { + case ',': + if( isLexSymbol()) { + WORD32 val; + SYM_T *sym; + char name[SYMLEN]; + + /* Use lookup so symbol will not be counted as reference. */ + sym = lookup(lexemeToName(name, lexstart, lexterm), UNDEFINED); + + if (curmacro) { + /* relative during macro expansion!! */ + val = clc - curmacro->defn->args[0].val; + } + else + val = clc; + + if( M_DEFINED( sym->type )) { + if( sym->val != val && pass == 2 ) + errorSymbol( &duplicate_label, sym->name, lexstart ); + sym->type |= DUPLICATE; /* XXX never used! */ + } + /* Must call define on pass 2 to generate concordance. */ + defineLexeme( lexstart, lexterm, val, LABEL ); + } + else if (isdigit(line[lexstart])) { /* constant, */ + int i; + WORD32 val = 0; + + for( i = lexstart; i < lexterm; i++ ) { + if( isdigit( line[i] )) { + int digit; + digit = line[i] - '0'; + if( digit >= radix ) { + errorLexeme( &number_not_radix, i ); + val = 0; + break; + } + val = val * radix + digit; + } + else { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + break; + } + } + if (i == lexterm) { + if( clc != val && pass == 2 ) + errorLexeme( &duplicate_label, lexstart); /* XXX */ + } + } + else + errorLexeme( &label_syntax, lexstart ); + next(0); /* skip comma */ + continue; + + case '=': + if( isLexSymbol()) { + WORD32 start, term, val; + + start = lexstart; + term = lexterm; + next(0); /* skip symbol */ + next(0); /* skip trailing = */ + val = getExprs(); + defineLexeme( start, term, val, DEFINED ); + printLine( line, 0, val, LINE_VAL ); + } + else { + errorLexeme( &symbol_syntax, lexstartprev ); + next(0); /* skip symbol */ + next(0); /* skip trailing = */ + getExprs(); /* skip expression */ + } + continue; + } /* switch on terminator */ + + if( isLexSymbol()) { + SYM_T *sym; + WORD32 val; + + sym = evalSymbol(); + val = sym->val; + if( M_MACRO(sym->type)) { + if (!invokeMacro(val)) + next(0); /* bad defn? or body is empty! */ + continue; + } /* macro invocation */ + else if( M_PSEUDO(sym->type)) { /* NO EPSEUDOs */ + pseudo( (PSEUDO_T)val & 0777777 ); + continue; + } /* pseudo */ + } /* macro, or non-char pseudo */ + + evalue = getExpr(); + if (evalue.type != PSEUDO) { /* not a bare pseudo-op? */ + if (line[lexstart] == ',') { /* EXP, */ + if(evalue.val != clc && pass == 2 ) + errorLexeme( &duplicate_label, lexstart); /* XXX */ + } + else if (line[lexstart] == '/') { /* EXP/ */ + clc = evalue.val; + printLine( line, clc, 0, LINE_LOC ); + next(0); + } + else { + punchOutObject( clc, evalue.val & 0777777); /* punch it! */ + incrementClc(); + } + } + } /* forever */ +} + +/* Function: onePass */ +/* Synopsis: Do one assembly pass. */ +void onePass() { + int ix; + + clc = 4; /* Default location is 4 */ + start_addr = 0; /* No starting address. */ + nconst = 0; /* No constant blocks seen */ + nvars = 0; /* No variables seen */ + + while (curmacro) { /* pop macro stack */ + struct macinv *mp; + + mp = curmacro->prev; + free(curmacro); + curmacro = mp; + } + + for( ix = 0; ix < mac_count; ix++) { + if (mac_defs[ix]) + free( mac_defs[ix] ); + mac_defs[ix] = NULL; + } + mac_count = 0; /* No macros defined. */ + + listed = TRUE; + lineno = 0; + list_pageno = 0; + list_lineno = 0; + list_title_set = FALSE; + page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ + radix = 8; /* Initial radix is octal (base 8). */ + + /* Now open the first input file. */ + end_of_input = FALSE; + filix_curr = filix_start; /* Initialize pointer to input files. */ + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + + for (;;) { + readLine(); + if (end_of_input) { + eob(); + fclose( infile ); + return; + } + processLine(); + } /* forever */ +} /* onePass */ + + +/* Function: getExprs */ +/* Synopsys: gutted like a fish */ +WORD32 getExprs() +{ + SYM_T sym; + + sym = getExpr(); + if (sym.type == PSEUDO) + errorMessage( &value_required, lexstart ); /* XXX wrong pointer? */ + + return sym.val & 0777777; +} /* getExprs */ + + +SYM_T getExpr() +{ + SYM_T sym; + + sym = eval(); + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + + for (;;) { + int space; + /* + * falling out of switch breaks loop and returns from routine + * so if you want to keep going, you must "continue"!! + */ + space = FALSE; + switch( line[lexstart] ) { + case ' ': + space = TRUE; + /* fall */ + case '+': /* add */ + next(1); /* skip operator */ + if (space && ISEND(line[lexstart])) /* tollerate a trailing space */ + return sym; + sym.val += eval().val; /* XXX look at type? */ + sym.type = DEFINED; + if( sym.val >= 01000000 ) + sym.val = ( sym.val + 1 ) & 0777777; + continue; + + case '-': /* subtract */ + next(1); /* skip over the operator */ + sym.val += eval().val ^ 0777777; /* XXX look at type? */ + sym.type = DEFINED; + if( sym.val >= 01000000 ) + sym.val = ( sym.val + 1 ) & 0777777; + continue; + + case '*': /* multiply */ + next(1); /* skip over the operator */ + sym.val *= eval().val; + sym.type = DEFINED; + if( sym.val >= 01000000 ) + sym.val = ( sym.val + 1 ) & 0777777; + continue; + +#if 0 + case '%': /* divide !??? */ + /* + * neither '%' nor the divide symbol appear in FIO-DEC, + * does any known program use such an operator? + * Easily confused for "MOD", which is how C uses '%'! + */ + next(1); + sym.val /= eval().val; + sym.type = DEFINED; + continue; +#endif + + case '&': /* and */ + next(1); /* skip over the operator */ + sym.val &= eval().val; + sym.type = DEFINED; + continue; + + case '!': /* or */ + next(1); /* skip over the operator */ + sym.val |= eval().val; + sym.type = DEFINED; + continue; + + case '/': + case ')': + case ']': + case ':': + case ',': + break; + + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + sym.val = 0; + break; + + default: + if (!ISEND(line[lexstart])) { + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + sym.val = 0; + break; + } + } /* switch */ + break; /* break loop!! */ + } /* "forever" */ + return( sym ); +} /* getExpr */ + +/* + * return fio-dec code for next char + * embeds shifts as needed + */ +int +nextfiodec(int *ccase, int delim) +{ + unsigned char c; + + for (;;) { + if (cc >= maxcc) { + if (delim == -1) + return -1; + + /* XXX MUST NOT BE IN A REPEAT!! */ + readLine(); /* danger will robinson! */ + if (end_of_input) + return -1; + } + c = line[cc]; + switch (c) { + case '\n': + c = '\r'; + break; + case '\r': + continue; + } + break; + } + + if (delim != -1 && c == delim) { + if (*ccase == LC) { + cc++; /* eat delim */ + return -1; + } + *ccase = LC; + return CONCISE_LC; /* shift down first */ + } + + if (c > 0177) { /* non-ascii */ + errorMessage( &illegal_character, cc ); + c = 0; /* space?! */ + } + + c = ascii_to_fiodec[c&0177]; + if (c == BAD) { + errorMessage( &illegal_character, cc ); + c = 0; /* space?! */ + } + + if (!(c & *ccase)) { /* char not in current case? */ + *ccase ^= BC; /* switch case */ + if (*ccase == LC) + return CONCISE_LC; /* shift down */ + else + return CONCISE_UC; /* shift up */ + } + cc++; + return c & CHARBITS; +} + +/* + * Function: flex + * Synopsis: Handle data for "flexo" pseudo + * handle upper case by doing shifts + */ + +WORD32 flex() +{ + WORD32 w; + int shift; + int ccase; + + if (line[lexstart] == ' ') /* always? */ + next(0); + + /* original version appears to take next 3 characters, + * REGARDLESS of what they are (tab, newline, space?)! + */ + w = 0; + ccase = LC; /* current case */ + for (shift = 12; shift >= 0; shift -= 6) { + unsigned char c; + if( lexstart >= maxcc ) + break; + + c = line[lexstart]; + if (c == '\t' || c == '\n') { + if (ccase == LC) + break; + c = CONCISE_LC; /* shift down first */ + } + else { + if (c > 0177) { /* non-ascii */ + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + c = ascii_to_fiodec[c&0177]; + if (c == BAD) { + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + if (!(c & ccase)) { /* char not in current case? */ + ccase ^= BC; /* switch case */ + if (ccase == LC) + c = CONCISE_LC; /* shift down */ + else + c = CONCISE_UC; /* shift up */ + } + else + lexstart++; + } + w |= (c & CHARBITS) << shift; + } + /* error to get here w/ case == UC? nah. shift down could be next */ + return w; +} /* flex */ + +/* + * Function: getChar + * Synopsis: Handle data for "char" pseudo + */ + +WORD32 getChar() +{ + unsigned char c, pos; + + if( cc >= maxcc ) + return 0; /* XXX error? */ + pos = line[cc++]; + if (pos != 'l' && pos != 'm' && pos != 'r') { + errorMessage( &illegal_character, lexstart ); + return 0; + } + + if( cc >= maxcc ) + return 0; /* XXX error? */ + + c = line[cc++]; + if (c > 0177) { + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + c = ascii_to_fiodec[c]; + if (c == BAD) { + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + if (!(c & LC)) { /* upper case only char? */ + c = CONCISE_UC; /* take a shift up */ + cc--; /* and leave char for next luser */ + } + + c &= CHARBITS; + switch (pos) { + case 'l': return c << 12; + case 'm': return c << 6; + case 'r': return c; + } + /* should not happen */ + return 0; +} /* flex */ + +/* Function: eval */ +/* Synopsis: Get the value of the current lexeme, and advance.*/ +SYM_T eval2() +{ + WORD32 digit; + WORD32 from; + SYM_T *sym; + WORD32 val; + SYM_T sym_eval; + + sym_eval.type = DEFINED; + sym_eval.name[0] = '\0'; + sym_eval.val = sym_eval.xref_index = sym_eval.xref_count = 0; + + val = 0; + + if( isLexSymbol()) { + sym = evalSymbol(); + if(!M_DEFINED( sym->type )) { + if( pass == 2 ) + errorSymbol( &undefined_symbol, sym->name, lexstart ); + next(1); + return( *sym ); + } + else if( M_PSEUDO(sym->type) || M_EPSEUDO(sym->type)) { + switch (sym->val) { + case DECIMAL: + radix = 10; + sym_eval.type = PSEUDO; + sym_eval.val = 0; /* has zero as a value! */ + break; + case OCTAL: + radix = 8; + sym_eval.type = PSEUDO; + sym_eval.val = 0; /* has zero as a value */ + break; + case FLEX: + next(1); /* skip keyword */ + sym_eval.val = flex(); + break; + case CHAR: + next(1); /* skip keyword */ + sym_eval.val = getChar(); + break; + default: + errorSymbol( &value_required, sym->name, lexstart ); + sym_eval.type = sym->type; + sym_eval.val = 0; + break; + } + next(1); + return( sym_eval ); + } + else if( M_MACRO( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + next(1); + return( sym_eval ); + } + else + { + next(1); + return( *sym ); + } + } /* symbol */ + else if( isdigit( line[lexstart] )) { + from = lexstart; + val = 0; + while( from < lexterm ) { + if( isdigit( line[from] )) { + digit = line[from++] - '0'; + if( digit >= radix ) { + errorLexeme( &number_not_radix, from - 1 ); + val = 0; + break; + } + val = val * radix + digit; + } + else { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + break; + } + } + next(1); + sym_eval.val = val; + return( sym_eval ); + } /* digit */ + else { + switch( line[lexstart] ) { + case '.': /* Value of Current Location Counter */ + val = clc; + next(1); + break; + case '(': /* Generate literal */ + next(1); /* Skip paren */ + val = getExprs(); /* recurse */ + if( line[lexstart] == ')' ) + next(1); /* Skip end paren */ + sym_eval.val = literal(val); + return sym_eval; + case '[': /* parens!! */ + next(1); + sym_eval.val = getExprs(); /* mutual recursion */ + if( line[lexstart] == ']' ) + next(1); /* Skip close bracket */ + else + errorMessage( &illegal_character, lexstart ); + return sym_eval; + default: + switch( line[lexstart] ) { + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + break; + default: + errorMessage( &illegal_character, lexstart ); + break; + } /* error switch */ + val = 0; /* On error, set value to zero. */ + next(1); /* Go past illegal character. */ + } /* switch on first char */ + } /* not symbol or number */ + sym_eval.val = val; + return( sym_eval ); +} /* eval2 */ + + +SYM_T eval() { + SYM_T sym; + + switch (line[lexstart]) { + case '-': /* unary - */ + next(1); + sym = eval2(); /* skip op */ + sym.val ^= 0777777; + break; + case '+': /* unary + */ + next(1); /* skip op */ + /* fall */ + default: + sym = eval2(); + } + return sym; +} + +/* Function: incrementClc */ +/* Synopsis: Set the next assembly location. Test for collision with */ +/* the literal tables. */ +WORD32 incrementClc() +{ + clc = (( clc + 1 ) & ADDRESS_FIELD ); + return( clc ); +} /* incrementClc */ + + +/* Function: readLine */ +/* Synopsis: Get next line of input. Print previous line if needed. */ +void readLine() +{ + BOOL ffseen; + WORD32 ix; + WORD32 iy; + char inpline[LINELEN]; + + /* XXX panic if nrepeats > 0 (if self-feeding, do the backup here?) */ + + listLine(); /* List previous line if needed. */ + error_in_line = FALSE; /* No error in line. */ + + if(curmacro && *curmacro->mac_ptr == '\0') { /* end of macro? */ + struct macinv *mp; + + listed = TRUE; /* Already listed. */ + + /* Restore invoking line. */ + strcpy(line, curmacro->mac_line); + cc = lexstartprev = curmacro->mac_cc; /* Restore cc. */ + maxcc = strlen( line ); /* Restore maxcc. */ + + mp = curmacro->prev; /* pop stack */ + free(curmacro); + curmacro = mp; + + return; + } /* end of macro */ + + cc = 0; /* Initialize column counter. */ + lexstartprev = 0; + if( curmacro ) { /* Inside macro? */ + char mc; + + maxcc = 0; + do { + + mc = *curmacro->mac_ptr++; /* Next character. */ + /* watch for overflow? how could it?? */ + line[maxcc++] = mc; + } while( !ISEND( mc )); /* note: terminates on tab?! */ + line[maxcc] = '\0'; + listed = nomac_exp; + return; + } /* inside macro */ + + lineno++; /* Count lines read. */ + listed = FALSE; /* Mark as not listed. */ + READ_LINE: + if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { + filix_curr++; /* Advance to next file. */ + if( filix_curr < save_argc ) { /* More files? */ + fclose( infile ); + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + list_title_set = FALSE; + goto READ_LINE; + } + else + end_of_input = TRUE; + } /* fgets failed */ + + ffseen = FALSE; + for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) { + if( inpline[ix] == '\f' ) { + if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); + ffseen = TRUE; + } + else + line[iy++] = inpline[ix]; + } + line[iy] = '\0'; + + /* If the line is terminated by CR-LF, remove, the CR. */ + if( line[iy - 2] == '\r' ) { + iy--; + line[iy - 1] = line[iy - 0]; + line[iy] = '\0'; + } + maxcc = iy; /* Save the current line length. */ +} /* readLine */ + + +/* Function: listLine */ +/* Synopsis: Output a line to the listing file. */ +void listLine() +/* generate a line of listing if not already done! */ +{ + if( listfile != NULL && listed == FALSE ) + { + printLine( line, 0, 0, LINE ); + } +} /* listLine */ + + +/* Function: printPageBreak */ +/* Synopsis: Output a Top of Form and listing header if new page necessary. */ +void printPageBreak() +{ + if( page_lineno >= LIST_LINES_PER_PAGE ) + /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ + { + topOfForm( list_title, NULL ); + } +} /* printPageBreak */ + + +/* Function: printLine */ +/* Synopsis: Output a line to the listing file with new page if necessary. */ +void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) +{ + if( listfile == NULL ) + { + save_error_count = 0; + return; + } + + printPageBreak(); + + list_lineno++; + page_lineno++; + switch( linestyle ) + { + default: + case LINE: + fprintf( listfile, "%5d ", lineno ); + fputs( line, listfile ); + listed = TRUE; + break; + + case LINE_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %6.6o ", lineno, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %6.6o\n", val ); + } + break; + + case LINE_LOC: + if( !listed ) + { + fprintf( listfile, "%5d %5.5o ", lineno, loc ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o\n", loc ); + } + break; + + case LINE_LOC_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + } + break; + + case LOC_VAL: + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + break; + } + printErrorMessages(); +} /* printLine */ + + +/* Function: printErrorMessages */ +/* Synopsis: Output any error messages from the current list of errors. */ +void printErrorMessages() +{ + WORD32 ix; + WORD32 iy; + + if( listfile != NULL ) + { + /* If any errors, display them now. */ + for( iy = 0; iy < save_error_count; iy++ ) + { + printPageBreak(); + fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); + if( error_list[iy].col >= 0 ) + { + for( ix = 0; ix < error_list[iy].col; ix++ ) + { + if( line[ix] == '\t' ) + { + putc( '\t', listfile ); + } + else + { + putc( ' ', listfile ); + } + } + fputs( "^", listfile ); + list_lineno++; + page_lineno++; + } + fputs( "\n", listfile ); + } + } + save_error_count = 0; +} /* printErrorMessages */ + + +/* Function: punchObject */ +/* Synopsis: Put one character to object file */ +void punchObject( WORD32 val ) +{ + val &= 0377; + if( objectfile != NULL ) + fputc( val, objectfile ); +} /* punchObject */ + +/* Function: punchTriplet */ +/* Synopsis: Output 18b word as three 6b characters with ho bit set. */ +void punchTriplet( WORD32 val ) +{ + punchObject((( val >> 12) & 077) | 0200 ); + punchObject((( val >> 6 ) & 077) | 0200 ); + punchObject(( val & 077) | 0200 ); +} /* punchTriplet */ + +void +eob() { + /* in case no "start" in file (an error?) */ +} + +/* Function: punchLeader */ +/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ +/* documentation. Paper tape has 10 punches per inch. */ +void punchLeader( WORD32 count ) +{ + WORD32 ix; + + /* If value is zero, set to the default of 2 feet of leader. */ + count = ( count == 0 ) ? 240 : count; + + if( objectfile != NULL ) + { + for( ix = 0; ix < count; ix++ ) + { + fputc( 0, objectfile ); + } + } +} /* punchLeader */ + +/* Function: punchOutObject */ +/* Synopsis: Output the current line and then then punch value to the */ +/* object file. */ +void punchOutObject( WORD32 loc, WORD32 val ) +{ + printLine( line, loc, val, LINE_LOC_VAL ); + punchLocObject( loc, val ); +} /* punchOutObject */ + + +/* Function: punchLocObjectRIM */ +/* Synopsis: Output the word in RIM mode */ +void punchLocObjectRIM( WORD32 loc, WORD32 val ) +{ + punchTriplet( DIO | loc ); + punchTriplet( val ); +} /* punchLocObject */ + +/* punch loader in RIM mode */ +void +punchLoader() { + int i; + + if (noinput) + return; + + for (i = 0; i < DIM(loader); i++) + punchLocObjectRIM(LOADERBASE+i, loader[i]); + punchTriplet( JMP | LOADERBASE ); +} + +/* + * flush out loader buffer; output a block: + * DIO start + * DIO end+1 + * .... data .... + * sum + */ +#define PW(X) { WORD32 x = X; sum += x; punchTriplet(x); } +void +flushLoader() { + WORD32 sum; + int i; + + if (loaderbufcount == 0) + return; + + sum = 0; + PW( DIO | loaderbufstart ); + PW( DIO | loaderbufstart + loaderbufcount ); + for (i = 0; i < loaderbufcount; i++) + PW( loaderbuf[i] ); + + /* roll over all the overflows at once */ + if (sum & ~0777777) + sum = (sum & 0777777) + (sum >> 18); + if (sum & 01000000) /* one more time */ + sum++; + PW( sum ); + + punchLeader(5); + loaderbufcount = 0; +} + +void punchLocObject( WORD32 loc, WORD32 val ) +{ + if (!rim_mode) { + if ((loc & LOADERBUFMASK) == 0 || /* full/force alignment */ + loaderbufcount > 0 && + loc != loaderbufstart + loaderbufcount) /* disjoint */ + flushLoader(); + if (loaderbufcount == 0) + loaderbufstart = loc; + loaderbuf[loaderbufcount++] = val; + } + else + punchLocObjectRIM( loc, val ); +} + +/* Function: literal */ +/* Synopsis: Add a value to the literal pool */ +WORD32 +literal( WORD32 value ) +{ + int i; + + if (nconst >= MAX_CONSTANTS) { + fprintf(stderr, "too many 'constants'; increase MAX_CONSTANTS\n"); + exit(1); + } + + if (pass == 1) { + if (++lit_count[nconst] == MAX_LITERALS) { + fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); + exit(1); + } + return lit_count[nconst]; + } + +#if 1 + /* + * pool constants; makes for a shorter tape + * (but "middle" constants blocks can't shrink) + */ + for (i = 0; i < nlit; i++) + if (litter[i] == value) + return lit_loc[nconst] + i; +#endif + + /* paranoia */ + if (nlit == MAX_LITERALS) { + fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); + exit(1); + } + + /* not found, save it */ + litter[nlit] = value; + + /* use base for this block, determined on pass1 */ + return lit_loc[nconst] + nlit++; +} /* literal */ + + +/* Function: printSymbolTable */ +/* Synopsis: Output the symbol table. */ +/* XXX now prints FIXED symbols too */ +void printSymbolTable() +{ + int ix; + int symbol_lines; + SYM_T *sym; + char mark; + + symbol_lines = 0; + for (ix = 0, sym = symtab; ix < symbol_top; ix++, sym++) { + if (M_FIXED(sym->type) || M_PSEUDO(sym->type) || + M_MACRO(sym->type) || M_EPSEUDO(sym->type)) + continue; + + if (symbol_lines == 0) { + topOfForm( list_title, s_symtable ); + symbol_lines = LIST_LINES_PER_PAGE; + } + + switch( sym->type & ( DEFINED | REDEFINED )) { + case UNDEFINED: + mark = '?'; + break; + + case REDEFINED: + mark = '#'; + break; + + default: + mark = ' '; + break; + } + fprintf( listfile, "%c%-6.6s %6.6o\n", mark, sym->name, sym->val ); + symbol_lines--; + } +} /* printSymbolTable */ + + +/* Function: printPermanentSymbolTable */ +/* Synopsis: Output the permanent symbol table to a file suitable for */ +/* being input after the EXPUNGE pseudo-op. */ +void printPermanentSymbolTable() +{ + int ix; + FILE *permfile; + + if(( permfile = fopen( permpathname, "w" )) == NULL ) + { + exit( 2 ); + } + + fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); + fprintf( permfile, " expunge\n/\n" ); + + for( ix = 0; ix < symbol_top; ix++ ) + { + int type = symtab[ix].type; + if( M_FIXED(type) && !M_PSEUDO(type) && !M_EPSEUDO(type) ) + fprintf( permfile, "\t%s=%o\n", + symtab[ix].name, symtab[ix].val ); + } + fclose( permfile ); +} /* printPermanentSymbolTable */ + + +/* Function: printCrossReference */ +/* Synopsis: Output a cross reference (concordance) for the file being */ +/* assembled. */ +void printCrossReference() +{ + int ix; + int xc; + int xc_index; + int xc_refcount; + int xc_cols; + SYM_T *sym; + + /* Force top of form for first page. */ + page_lineno = LIST_LINES_PER_PAGE; + + list_lineno = 0; + + for( ix = 0, sym = symtab; ix < symbol_top; ix++, sym++ ) { + if (M_FIXED(sym->type) && xreftab[sym->xref_index] == 0) + continue; + list_lineno++; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + topOfForm( list_title, s_xref ); + + fprintf( listfile, "%5d", list_lineno ); + + /* Get reference count & index into concordance table for this symbol */ + xc_refcount = sym->xref_count; + xc_index = sym->xref_index; + /* Determine how to label symbol on concordance. */ + /* XXX flag variables? */ + switch( sym->type & ( DEFINED | REDEFINED )) { + case UNDEFINED: + fprintf( listfile, " U "); + break; + + case REDEFINED: + fprintf( listfile, " M %5d ", xreftab[xc_index] ); + break; + + default: + fprintf( listfile, " A %5d ", xreftab[xc_index] ); + break; + } + fprintf( listfile, "%-6.6s ", sym->name ); + + /* Output the references, 8 numbers per line after symbol name. */ + for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { + if( xc_cols >= XREF_COLUMNS ) { + xc_cols = 0; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + topOfForm( list_title, s_xref); + list_lineno++; + fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); + } + fprintf( listfile, " %5d", xreftab[xc_index + xc] ); + } + fprintf( listfile, "\n" ); + } /* for */ +} /* printCrossReference */ + + +/* Function: topOfForm */ +/* Synopsis: Prints title and sub-title on top of next page of listing. */ +void topOfForm( char *title, char *sub_title ) +{ + char temp[10]; + + list_pageno++; + strcpy( temp, s_page ); + sprintf( temp, "%s %d", s_page, list_pageno ); + + if (!listfile) + return; + + /* Output a top of form if not the first page of the listing. */ + if( list_pageno > 1 ) + fprintf( listfile, "\f" ); + + fprintf( listfile, "\n %-63s %10s\n", title, temp ); + + /* Reset the current page line counter. */ + page_lineno = 1; + if( sub_title != NULL ) + { + fprintf( listfile, "%80s\n", sub_title ); + page_lineno++; + } + else + { + fprintf( listfile, "\n" ); + page_lineno++; + } + fprintf( listfile, "\n" ); + page_lineno++; +} /* topOfForm */ + + +/* Function: lexemeToName */ +/* Synopsis: Convert the current lexeme into a string. */ +char *lexemeToName( char *name, WORD32 from, WORD32 term ) +{ + int to; + + to = 0; + while( from < term && to < SYMLEN-1) { + char c = line[from++]; + if (ISOVERBAR(c)) + continue; + name[to++] = c; + } + name[to] = '\0'; + + return( name ); +} /* lexemeToName */ + +/* Function: defineLexeme */ +/* Synopsis: Put lexeme into symbol table with a value. */ +SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ + WORD32 term, /* end+1 of lexeme being defined. */ + WORD32 val, /* value of lexeme being defined. */ + SYMTYP type ) /* how symbol is being defined. */ +{ + char name[SYMLEN]; + + lexemeToName( name, start, term); + return( defineSymbol( name, val, type, start )); +} /* defineLexeme */ + + +/* Function: defineSymbol */ +/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ +/* not already in table. */ +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) +{ + SYM_T *sym; + WORD32 xref_count; + + if( strlen( name ) < 1 ) + { + return( &sym_undefined ); /* Protect against non-existent names. */ + } + sym = lookup( name, type ); + xref_count = 0; /* Set concordance for normal defintion. */ + + if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) + { + if( pass == 2 ) + { + errorSymbol( &redefined_symbol, sym->name, start ); + type = type | REDEFINED; + sym->xref_count++; /* Referenced symbol, count it. */ + xref_count = sym->xref_count; + /* moved inside "if pass2" -plb 10/2/03 allow redefinition + * of predefined symbols during pass1 + */ + return ( sym ); + } + } + + if( pass == 2 && xref ) + { + /* Put the definition line number in the concordance table. */ + /* Defined symbols are not counted as references. */ + if (sym->xref_index >= 0) { /* beware macro dummies */ + xreftab[sym->xref_index] = lineno; + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + xref_count] = lineno; + } + } + + /* Now set the value and the type. */ + sym->val = val & 0777777; + sym->type = type; + return( sym ); +} /* defineSymbol */ + + +/* Function: lookup */ +/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ +/* table as undefined. Return address of symbol in table. */ +SYM_T *lookup( char *name, int type ) +{ + int ix; /* Insertion index */ + int lx; /* Left index */ + int rx; /* Right index */ + SYM_T *best; /* best match */ + SYM_T *sym; + + /* YIKES! Search dummies (and "R") before anything else!! */ + if (curmacro && curmacro->defn) { + struct macdef *mdp = curmacro->defn; + int i; + + for (i = 0, sym = mdp->args; i <= mdp->nargs; i++, sym++) + if (strcmp(name, sym->name) == 0) + return sym; + } + + lx = 0; + rx = symbol_top - 1; + best = NULL; + while (lx <= rx) { + int mx = (lx + rx) / 2; /* Find center of search area. */ + int compare; + + sym = symtab + mx; + + compare = strcmp(name, sym->name); + if (compare < 0) + rx = mx - 1; + else if (compare > 0) + lx = mx + 1; + else { /* match */ + if (overbar && !M_DEFINED(sym->type) && pass == 2) { + sym->type = DEFINED; + sym->val = vars_addr++; + nvars++; + } + return sym; /* return exact match */ + } /* match */ + + /* save best non-exact match; MACRO returns last defined n-x match! */ + if ((M_PSEUDO(sym->type)||M_EPSEUDO(sym->type)||M_MACRO(sym->type)) && + strncmp(name, sym->name, 3) == 0) + best = sym; + } /* while */ + + /* return best match (pseudo or macro) if any for lookups (not defns) */ + if (best && type == UNDEFINED) + return best; + + /* Must put symbol in table if index is negative. */ + ix = lx; /* insertion point */ + if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { + errorSymbol( &symbol_table_full, name, lexstart ); + exit( 1 ); + } + + for( rx = symbol_top; rx >= ix; rx-- ) + symtab[rx + 1] = symtab[rx]; + + symbol_top++; + + /* Enter the symbol as UNDEFINED with a value of zero. */ + sym = symtab + ix; + strcpy( sym->name, name ); + sym->type = UNDEFINED; + sym->val = 0; + sym->xref_count = 0; + if( xref && pass == 2 && sym->xref_index >= 0) + xreftab[sym->xref_index] = 0; + + if (overbar) + nvars++; + + return sym; +} /* lookup */ + +/* Function: compareSymbols */ +/* Synopsis: Used to presort the symbol table when starting assembler. */ +int compareSymbols( const void *a, const void *b ) +{ + return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); +} /* compareSymbols */ + +/* Function: evalSymbol */ +/* Synopsis: Get the pointer for the symbol table entry if exists. */ +/* If symbol doesn't exist, return a pointer to the undefined sym */ +SYM_T *evalSymbol() +{ + char name[SYMLEN]; + SYM_T *sym; + + sym = lookup( lexemeToName( name, lexstart, lexterm ), UNDEFINED); + + sym->xref_count++; /* Count the number of references to symbol. */ + + if( xref && pass == 2 && sym->xref_index >= 0) + { + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + sym->xref_count] = lineno; + } + + return( sym ); +} /* evalSymbol */ + + +/* Function: moveToEndOfLine */ +/* Synopsis: Move the parser input to the end of the current input line. */ +void moveToEndOfLine() +{ + while( !ISEND( line[cc] )) cc++; /* XXX wrong! will stop on a tab! */ + lexstart = cc; + lexterm = cc; + lexstartprev = lexstart; +} /* moveToEndOfLine */ + +/* frame the next token in "line" with lexstart and lexterm indicies */ +void +next(int op) { + char c; + + /* Save start column of previous lexeme for diagnostic messages. */ + lexstartprev = lexstart; + lextermprev = lexterm; + + c = line[cc]; + if (c == ' ') { + /* eat spaces */ + do { + c = line[++cc]; + } while (c == ' '); + if (op) /* looking for operators? */ + cc--; /* return one */ + } + + overbar = 0; + lexstart = cc; + c = line[cc]; + if( isalnum(c) || ISOVERBAR(c)) { + if (ISOVERBAR(c)) + overbar = 1; + do { + c = line[++cc]; + if (ISOVERBAR(c)) + overbar = 1; + } while (isalnum(c) || ISOVERBAR(c)); + } + else if(!ISDONE(c) || c == '\t') /* not end of line, or comment */ + cc++; /* advance past all punctuation */ + lexterm = cc; +} /* next */ + +BOOL isLexSymbol() +{ + int ix; + + /* XXX alpha within first 4? 3?? */ + for( ix = lexstart; ix < lexterm; ix++ ) + if(isalpha(line[ix])) + return TRUE; /* any position will do! */ + return FALSE; +} /* isLexSymbol */ + +/* + * from macro manual (F-36BP), p.18; + * + * "A macro-instruction definition consists of four parts; + * the pseudo-instruction _define_, the _macro instruction name_ + * amd _dummy symbol list,_ the _body_, and the pseudo-instruction + * _terminate_. Each part is followed by at least one tabulation or + * carriage return." + * + * and in the next paragraph; + * + * "The name is terminated by a _space_ or by a _tab_ or _cr_ + * if there is no dummy symbol list." + * + * This accepts tabs and/or a newline after define + * (but will accept a space), and only accepts spaces + * between macro and dummy names. + */ + +void +defineMacro() { + int lexstartsave; /* point to macro name */ + int index; /* point to error char */ + int error; /* error boolean */ + int i; + int count; + WORD32 length; + WORD32 value; + char termin[SYMLEN]; + char args[MAC_MAX_ARGS][SYMLEN]; /* macro & arg names */ + char body[MAC_MAX_LENGTH + 1]; + struct macdef *mdp; + SYM_T *sym; + + if (nrepeats) { + /* we can call readLine, so throw up hands now */ + errorLexeme( &define_in_repeat, lexstartprev ); + return; + } + + while (line[lexstart] == ' ' || line[lexstart] == '\t') + next(0); + + /* not a tab or space */ + if (ISEND(line[lexstart])) { /* newline or EOS? */ + /* crock; next token should invisibly skip over line boundaries? */ + readLine(); + next(0); + while (line[lexstart] == ' ' || line[lexstart] == '\t') + next(0); + } + + /* XXX pick up macro name out here */ + + count = 0; + index = 0; + error = FALSE; + lexstartsave = lexstart; + while (!ISDONE(line[lexstart]) && count < MAC_MAX_ARGS) { + if (!isalnum(line[lexstart]) && index == 0) + index = lexstart; /* error pointer */ + lexemeToName( args[count++], lexstart, lexterm ); + /* XXX error if NOT a comma (& not first dummy) ? */ + if (line[lexterm] == ',') + next(0); /* eat the comma */ + next(0); + if (line[lexstart] == ' ') + next(0); + } + if( count == 0 ) { /* No macro name. */ + errorMessage( &no_macro_name, lexstartsave ); + error = TRUE; + } + else if( index ) { /* Bad argument name. */ + errorMessage( &bad_dummy_arg, index ); + error = TRUE; + } + else if( mac_count >= MAC_TABLE_LENGTH ) { + errorMessage( ¯o_table_full, lexstartsave ); + error = TRUE; + } + else { + value = mac_count++; /* sym value is index into mac */ + defineSymbol( args[0], value, MACRO, lexstartsave ); + } + + for( length = 0;; ) { + readLine(); + if (end_of_input) + break; + next(0); + while (line[lexstart] == ' ' || line[lexstart] == '\t') + next(0); + + lexemeToName( termin, lexstart, lexterm ); /* just look at line? */ + if (strncmp( termin, "term", 4 ) == 0) + break; + + if (!error) { + int ll = strlen(line); + int allblank = FALSE; + + /* don't save blank lines! */ + for( i = 0; i < ll && allblank; i++ ) + if(!ISBLANK(line[i])) + allblank = FALSE; + + if (allblank) /* nothing but air? */ + continue; /* skip it! */ + + if ((length + ll + 1) >= MAC_MAX_LENGTH ) { + errorMessage (¯o_too_long, lexstart ); + error = TRUE; + continue; + } + + strcpy(body+length, line); + length += ll; + } + } /* for */ + if( error ) + return; + + mdp = calloc(1, sizeof(struct macdef) + length); + if (mdp == NULL) { + fprintf(stderr, "error allocating memory for macro definition\n"); + exit(1); + } + mac_defs[value] = mdp; + + strncpy(mdp->body, body, length); + mdp->body[length] = '\0'; + mdp->nargs = count - 1; + + /* + * save dummy names + * symbol slot 0 reserved for "r" symbol + * move SYM_T entries to macinv to allow recursion + */ + sym = mdp->args; + sym->type = DEFINED; + strcpy(sym->name, "R"); + sym->val = 0; + sym->xref_index = -1; /* ??? allow xref? */ + sym++; + + for (i = 1; i <= mdp->nargs; i++, sym++) { + sym->type = DEFINED; + strcpy(sym->name, args[i]); + sym->val = 0; + sym->xref_index = -1; /* don't xref!! */ + } +} /* defineMacro */ + +/* VARIABLES pseudo-op */ +void +variables() { + /* XXX error if "variables" already seen (in this pass) */ + /* XXX error if different address on pass 2 */ + if (pass == 2) + printLine( line, clc, 0, LINE_LOC ); + vars_addr = clc; + vars_end = clc = (clc + nvars) & ADDRESS_FIELD; + if (pass == 2) + printLine( line, clc, 0, LINE_LOC); +} + +/* TEXT pseudo-op */ +void +text(void) +{ + char delim; + WORD32 w; + int count; + int ccase; + /* XXX error in repeat!! */ + do { + if (cc == maxcc) { + /* XXX EOL before delim found!!! */ + fprintf(stderr, "FIX ME!\n"); + return; + } + delim = line[cc++]; + } while (delim == ' '); /* others? NL */ + + w = count = 0; + ccase = LC; + for (;;) { + int c = nextfiodec(&ccase, delim); + if (c == -1) + break; + w |= c << ((2-count)*6); + if (++count == 3) { + punchOutObject(clc, w); /* punch it! */ + incrementClc(); + count = w = 0; + } + } + if (count > 0) { + punchOutObject(clc, w); /* punch remainder */ + incrementClc(); + } +} + +/* CONSTANTS pseudo-op */ +void +constants(void) { + int i; + + /* XXX illegal inside macro (curmacro != NULL) */ + + if (pass == 1) { + lit_loc[nconst] = clc; + + /* just use addition?! */ + for (i = 0; i < lit_count[nconst]; i++) + incrementClc(); + + nconst++; + return; + } + + /* pass 2: */ + /* XXX complain if clc != lit_base[nconst]? */ + + for (i = 0; i < lit_count[nconst]; i++) { + if (i < nlit) + punchOutObject( clc, litter[i] & 0777777); /* punch it! */ + incrementClc(); + } + + nconst++; + nlit = 0; /* litter[] now empty */ +} /* constants */ + + +/* process pseudo-ops + * return FALSE if line scan should end (no longer used) + */ +BOOL pseudo( PSEUDO_T val ) +{ + int count; + int repeatstart; + + switch( (PSEUDO_T) val ) { + case CONSTANTS: + next(0); /* Skip symbol */ + constants(); + break; + + case VARIABLES: + next(0); /* Skip symbol */ + variables(); + break; + + case DEFINE: + next(0); /* Skip symbol */ + defineMacro(); + return FALSE; + break; + + case REPEAT: + next(0); /* Skip symbol */ + + /* NOTE!! constant followed by SPACE picked up as expression!! */ + count = getExprs() & ADDRESS_FIELD; + /* XXX error if sign bit set? */ + + /* allow comma, but do not require */ + if( line[lexstart] == ',') + next(0); + + nrepeats++; + repeatstart = lexstart; /* save line start */ + while (count-- > 0) { + cc = repeatstart; /* reset input pointer */ + processLine(); /* recurse! */ + } + cc = maxcc; + nrepeats--; + + return FALSE; + break; + + case START: + next(0); /* Skip symbol */ + /* XXX illegal in macro or repeat */ + flushLoader(); + if (!ISDONE(line[lexstart])) { + if (line[lexstart] == ' ') + next(0); + start_addr = getExprs() & ADDRESS_FIELD; + next(0); + printLine( line, 0, start_addr, LINE_VAL ); + /* MACRO punches 4" of leader */ + punchTriplet(JMP | start_addr); + /* MACRO punches 24" of leader? */ + } + /* + * handle multiple tapes concatenated into one file!! + * have command line option?? treat "start" as EOF?? + */ + list_title_set = FALSE; + return FALSE; + + case TEXT: + /* NOTE!! no next()! */ + text(); + break; + + case NOINPUT: + next(0); /* Skip symbol */ + noinput = TRUE; + break; + + case EXPUNGE: + next(0); /* Skip symbol */ + if (pass == 1) + init_symtab(); + break; + + default: + break; + } /* end switch for pseudo-ops */ + return TRUE; /* keep scanning */ +} /* pseudo */ + + +/* Function: errorLexeme */ +/* Synopsis: Display an error message using the current lexical element. */ +void errorLexeme( EMSG_T *mesg, WORD32 col ) +{ + char name[SYMLEN]; + + errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); +} /* errorLexeme */ + + +/* Function: errorSymbol */ +/* Synopsis: Display an error message with a given string. */ +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) +{ + char linecol[12]; + char *s; + + if( pass == 2 ) + { + s = ( name == NULL ) ? "" : name ; + errors++; + sprintf( linecol, ":%d:%d", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", + filename, linecol, mesg->file, s, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorSymbol */ + + +/* Function: errorMessage */ +/* Synopsis: Display an error message without a name argument. */ +void errorMessage( EMSG_T *mesg, WORD32 col ) +{ + char linecol[12]; + + if( pass == 2 ) + { + errors++; + sprintf( linecol, ":%d:%d", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", + filename, linecol, mesg->file, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorMessage */ + +/* Function: saveError */ +/* Synopsis: Save the current error in a list so it may displayed after the */ +/* the current line is printed. */ +void saveError( char *mesg, WORD32 col ) +{ + if( save_error_count < DIM( error_list )) + { + error_list[save_error_count].mesg = mesg; + error_list[save_error_count].col = col; + save_error_count++; + } + error_in_line = TRUE; + + if( listed ) + printErrorMessages(); +} /* saveError */ + +/* create a "symbol punch" for DDT */ +/* MUST be called after object file closed; we reuse the FILE*! */ + +void +dump_symbols(void) { + int ix; + WORD32 addr; + + objectfile = fopen( sympathname, "wb" ); + if (!objectfile) { + perror(sympathname); + return; + } + + punchLeader(0); + punchLoader(); + punchLeader(5); + + /* XXX fudge addr -- get count, and subtract 2N from 07750? */ + addr = 05000; + + for( ix = 0; ix < symbol_top; ix++ ) { + int i, type; + WORD32 name; + + type = symtab[ix].type; + if (M_FIXED(type) || M_PSEUDO(type) || M_MACRO(type)) + continue; + + name = 0; + for (i = 0; i < 3; i++) { + char c; + + c = symtab[ix].name[i]; + /* XXX leave on NUL? */ + + c = ascii_to_fiodec[tolower(c) & 0177]; + /* XXX check for BAD entries? */ + + /* XXX OR in val<<(3-i)*6?? */ + name <<= 6; + name |= c & CHARBITS; + } + punchLocObject(addr++, permute(name)); + punchLocObject(addr++, symtab[ix].val); + } + flushLoader(); + punchTriplet( JMP ); /* ??? */ + punchLeader(0); + fclose(objectfile); +} diff --git a/crossassemblers/macro11/changes b/crossassemblers/macro11/changes new file mode 100644 index 0000000..9fa1e30 --- /dev/null +++ b/crossassemblers/macro11/changes @@ -0,0 +1,12 @@ +15-July-2001 + version 0.2 + removed references to snprintf from dumpobj.c and + mlb.c for portability + fixed a type cast warning in dumpobj.c compare_gsdlines + Removed strcasecmp from macro11.c for portability + Removed references to wnewmem.c from makefile (isn't needed) + makefile more compatible with non-gnu make and compiler + main prints version 0.2 + +14-July-2001 + First release, version 0.1. diff --git a/crossassemblers/macro11/depends b/crossassemblers/macro11/depends new file mode 100644 index 0000000..e69de29 diff --git a/crossassemblers/macro11/dumpobj.c b/crossassemblers/macro11/dumpobj.c new file mode 100644 index 0000000..4efeb5f --- /dev/null +++ b/crossassemblers/macro11/dumpobj.c @@ -0,0 +1,677 @@ +/* Dump and interpret an object file. */ + +/* +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "rad50.h" + +#include "util.h" + +#define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) + +int psectid = 0; +char *psects[256]; +FILE *bin = NULL; +int badbin = 0; +int xferad = 1; + +char *readrec(FILE *fp, int *len) +{ + int c, i; + int chksum; + char *buf; + + chksum = 0; + + while(c = fgetc(fp), c != EOF && c == 0) + ; + + if(c == EOF) + return NULL; + + if(c != 1) + { + fprintf(stderr, "Improperly formatted OBJ file (1)\n"); + return NULL; // Not a properly formatted file. + } + + chksum -= c; + + c = fgetc(fp); + if(c != 0) + { + fprintf(stderr, "Improperly formatted OBJ file (2)\n"); + return NULL; // Not properly formatted + } + + chksum -= c; // even though for 0 the checksum isn't changed... + + c = fgetc(fp); + if(c == EOF) + { + fprintf(stderr, "Improperly formatted OBJ file (3)\n"); + return NULL; + } + *len = c; + + chksum -= c; + + c = fgetc(fp); + if(c == EOF) + { + fprintf(stderr, "Improperly formatted OBJ file (4)\n"); + return NULL; + } + + *len += (c << 8); + + chksum -= c; + + *len -= 4; // Subtract header and length bytes from length + if(*len < 0) + { + fprintf(stderr, "Improperly formatted OBJ file (5)\n"); + return NULL; + } + + buf = malloc(*len); + if(buf == NULL) + { + fprintf(stderr, "Out of memory allocating %d bytes\n", *len); + return NULL; // Bad alloc + } + + i = fread(buf, 1, *len, fp); + if(i < *len) + { + free(buf); + fprintf(stderr, "Improperly formatted OBJ file (6)\n"); + return NULL; + } + + for(i = 0; i < *len; i++) + { + chksum -= (buf[i] & 0xff); + } + + c = fgetc(fp); + c &= 0xff; + chksum &= 0xff; + + if(c != chksum) + { + free(buf); + fprintf(stderr, "Bad record checksum, " + "calculated=%d, recorded=%d\n", chksum, c); + return NULL; + } + + return buf; +} + +void dump_bytes(char *buf, int len) +{ + int i, j; + + for(i = 0; i < len; i += 8) + { + printf("\t%3.3o: ", i); + for(j = i; j < len && j < i+8; j++) + { + printf("%3.3o ", buf[j] & 0xff); + } + + printf("%*s", (i+8 - j) * 4, ""); + + for(j = i; j < len && j < i+8; j++) + { + int c = buf[j] & 0xff; + if(!isprint(c)) + c = '.'; + putchar(c); + } + + putchar('\n'); + } +} + +void dump_words(unsigned addr, char *buf, int len) +{ + int i, j; + + for(i = 0; i < len; i += 8) + { + printf("\t%6.6o: ", addr); + + for(j = i; j < len && j < i+8; j += 2) + { + if(len - j >= 2) + { + unsigned word = WORD(buf + j); + printf("%6.6o ", word); + } + else + printf("%3.3o ", buf[j] & 0xff); + } + + printf("%*s", (i+8 - j) * 7 / 2, ""); + + for(j = i; j < len && j < i+8; j++) + { + int c = buf[j] & 0xff; + if(!isprint(c)) + c = '.'; + putchar(c); + } + + putchar('\n'); + addr += 8; + } +} + +void dump_bin(unsigned addr, char *buf, int len) +{ + int chksum; /* Checksum is negative sum of all + bytes including header and length */ + int FBR_LEAD1 = 1, FBR_LEAD2 = 0; + int i; + unsigned hdrlen = len + 6; + + for(i = 0; i < 8; i++) fputc (0, bin); + chksum = 0; + if(fputc(FBR_LEAD1, bin) == EOF) return; /* All recs begin with 1,0 */ + chksum -= FBR_LEAD1; + if(fputc(FBR_LEAD2, bin) == EOF) return; + chksum -= FBR_LEAD2; + + i = hdrlen & 0xff; /* length, lsb */ + chksum -= i; + if(fputc(i, bin) == EOF) return; + + i = (hdrlen >> 8) & 0xff; /* length, msb */ + chksum -= i; + if(fputc(i, bin) == EOF) return; + + i = addr & 0xff; /* origin, msb */ + chksum -= i; + if(fputc(i, bin) == EOF) return; + + i = (addr >> 8) & 0xff; /* origin, lsb */ + chksum -= i; + if(fputc(i, bin) == EOF) return; + + if ((len == 0) || (buf == NULL)) return; /* end of tape block */ + + i = fwrite(buf, 1, len, bin); + if(i < len) return; + + while(len > 0) /* All the data bytes */ + { + chksum -= *buf++ & 0xff; + len--; + } + + chksum &= 0xff; + + fputc(chksum, bin); /* Followed by the checksum byte */ + + return; /* Worked okay. */ +} + +void trim(char *buf) +{ + char *cp; + + for(cp = buf + strlen(buf); cp > buf; cp--) + { + if(cp[-1] != ' ') + break; + } + *cp = 0; +} + +char **all_gsds = NULL; +int nr_gsds = 0; +int gsdsize = 0; + +void add_gsdline(char *line) +{ + if(nr_gsds >= gsdsize || all_gsds == NULL) + { + gsdsize += 128; + all_gsds = realloc(all_gsds, gsdsize * sizeof(char *)); + if(all_gsds == NULL) + { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + } + + all_gsds[nr_gsds++] = line; +} + +void got_gsd(char *cp, int len) +{ + int i; + char *gsdline; + + for(i = 2; i < len; i += 8) + { + char name[8]; + unsigned value; + unsigned flags; + + gsdline = malloc(256); + if(gsdline == NULL) + { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + unrad50(WORD(cp+i), name); + unrad50(WORD(cp+i+2), name+3); + name[6] = 0; + + value = WORD(cp+i+6); + flags = cp[i+4] & 0xff; + + switch(cp[i+5] & 0xff) + { + case 0: + sprintf(gsdline, + "\tMODNAME %s=%o flags=%o\n", name, value, flags); + break; + case 1: + sprintf(gsdline, + "\tCSECT %s=%o flags=%o\n", name, value, flags); + break; + case 2: + sprintf(gsdline, + "\tISD %s=%o flags=%o\n", name, value, flags); + break; + case 3: + sprintf(gsdline, + "\tXFER %s=%o flags=%o\n", name, value, flags); + xferad = value; + break; + case 4: + sprintf(gsdline, + "\tGLOBAL %s=%o %s flags=%o\n", + name, value, cp[i+4] & 8 ? "DEF" : "REF", flags); + break; + case 5: + sprintf(gsdline, + "\tPSECT %s=%o flags=%o\n", name, value, flags); + psects[psectid] = strdup(name); + trim(psects[psectid++]); + break; + case 6: + sprintf(gsdline, + "\tIDENT %s=%o flags=%o\n", name, value, flags); + break; + case 7: + sprintf(gsdline, + "\tVSECT %s=%o flags=%o\n", name, value, flags); + break; + default: + sprintf(gsdline, + "\t***Unknown GSD entry type %d flags=%o\n", + cp[i+5] & 0xff, flags); + break; + } + + gsdline = realloc(gsdline, strlen(gsdline)+1); + add_gsdline(gsdline); + } + +} + +int compare_gsdlines(const void *p1, const void *p2) +{ + const char * const *l1 = p1, * const *l2 = p2; + + return strcmp(*l1, *l2); +} + +void got_endgsd(char *cp, int len) +{ + int i; + + qsort(all_gsds, nr_gsds, sizeof(char *), compare_gsdlines); + + printf("GSD:\n"); + + for(i = 0; i < nr_gsds; i++) + { + fputs(all_gsds[i], stdout); + free(all_gsds[i]); + } + + printf("ENDGSD\n"); + + free(all_gsds); +} + +unsigned last_text_addr = 0; + +void got_text(char *cp, int len) +{ + unsigned addr = WORD(cp+2); + + last_text_addr = addr; + + printf("TEXT ADDR=%o LEN=%o\n", last_text_addr, len-4); + + dump_words(last_text_addr, cp+4, len-4); + + if (bin) dump_bin(last_text_addr, cp+4, len-4); +} + +void rad50name(char *cp, char *name) +{ + unrad50(WORD(cp), name); + unrad50(WORD(cp+2), name+3); + name[6] = 0; + trim(name); +} + +void got_rld(char *cp, int len) +{ + int i; + printf("RLD\n"); + + for(i = 2; i < len;) + { + unsigned addr; + unsigned word; + unsigned disp = cp[i+1] & 0xff; + char name[8]; + char *byte; + + addr = last_text_addr + disp - 4; + + byte = ""; + if(cp[i] & 0200) + byte = " byte"; + + switch(cp[i] & 0x7f) + { + case 01: + printf("\tInternal%s %o=%o\n", byte, addr, WORD(cp+i+2)); + i += 4; + break; + case 02: + rad50name(cp+i+2, name); + printf("\tGlobal%s %o=%s\n", byte, addr, name); + i += 6; + break; + case 03: + printf("\tInternal displaced%s %o=%o\n", byte, addr, WORD(cp+i+2)); + i += 4; + badbin = 1; + break; + case 04: + rad50name(cp+i+2, name); + printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); + i += 6; + badbin = 1; + break; + case 05: + rad50name(cp+i+2, name); + word = WORD(cp+i+6); + printf("\tGlobal plus offset%s %o=%s+%o\n", + byte, addr, name, word); + i += 8; + badbin = 1; + break; + case 06: + rad50name(cp+i+2, name); + word = WORD(cp+i+6); + printf("\tGlobal plus offset displaced%s %o=%s+%o\n", + byte, addr, name, word); + i += 8; + badbin = 1; + break; + case 07: + rad50name(cp+i+2, name); + word = WORD(cp+i+6); + printf("\tLocation counter definition %s+%o\n", + name, word); + i += 8; + + last_text_addr = word; + break; + case 010: + word = WORD(cp+i+2); + printf("\tLocation counter modification %o\n", word); + i += 4; + + last_text_addr = word; + break; + case 011: + printf("\t.LIMIT %o\n", addr); + i += 2; + break; + + case 012: + rad50name(cp+i+2, name); + printf("\tPSECT%s %o=%s\n", byte, addr, name); + i += 6; + badbin = 1; + break; + case 014: + rad50name(cp+i+2, name); + + printf("\tPSECT displaced%s %o=%s+%o\n", byte, addr, name, word); + i += 6; + badbin = 1; + break; + case 015: + rad50name(cp+i+2, name); + word = WORD(cp+i+6); + printf("\tPSECT plus offset%s %o=%s+%o\n", + byte, addr, name, word); + i += 8; + badbin = 1; + break; + case 016: + rad50name(cp+i+2, name); + word = WORD(cp+i+6); + printf("\tPSECT plus offset displaced%s %o=%s+%o\n", + byte, addr, name, word); + i += 8; + badbin = 1; + break; + + case 017: + badbin = 1; + printf("\tComplex%s %o=", byte, addr); + i += 2; + { + char *xp = cp + i; + int size; + for(;;) + { + size = 1; + switch(*xp) + { + case 000: + fputs("nop ", stdout); break; + case 001: + fputs("+ ", stdout); break; + case 002: + fputs("- ", stdout); break; + case 003: + fputs("* ", stdout); break; + case 004: + fputs("/ ", stdout); break; + case 005: + fputs("& ", stdout); break; + case 006: + fputs("! ", stdout); break; + case 010: + fputs("neg ", stdout); break; + case 011: + fputs("^C ", stdout); break; + case 012: + fputs("store ", stdout); break; + case 013: + fputs("store{disp} ", stdout); break; + + case 016: + rad50name(xp+1, name); + printf("%s ", name); + size = 5; + break; + + case 017: + assert((xp[1] & 0377) < psectid); + printf("%s:%o ", + psects[xp[1] & 0377], + WORD(xp+2)); + size = 4; + break; + + case 020: + printf("%o ", WORD(xp+1)); + size = 3; + break; + default: + printf("**UNKNOWN COMPLEX CODE** %o\n", *xp & 0377); + return; + } + i += size; + if(*xp == 012 || *xp == 013) + break; + xp += size; + } + fputc('\n', stdout); + break; + } + + default: + printf("\t***Unknown RLD code %o\n", cp[i] & 0xff); + return; + } + } + +} + +void got_isd(char *cp, int len) +{ + printf("ISD len=%o\n"); +} + +void got_endmod(char *cp, int len) +{ + printf("ENDMOD\n"); +} + +void got_libhdr(char *cp, int len) +{ + printf("LIBHDR\n"); +} + +void got_libend(char *cp, int len) +{ + printf("LIBEND\n"); +} + +int main(int argc, char *argv[]) +{ + int len; + FILE *fp; + char *cp; + + fp = fopen(argv[1], "rb"); + if(fp == NULL) + return EXIT_FAILURE; + if(argv[2]) + { + bin = fopen(argv[2], "wb"); + if(bin == NULL) return EXIT_FAILURE; + } + + while((cp = readrec(fp, &len)) != NULL) + { + switch(cp[0] & 0xff) + { + case 1: + got_gsd(cp, len); + break; + case 2: + got_endgsd(cp, len); + break; + case 3: + got_text(cp, len); + break; + case 4: + got_rld(cp, len); + break; + case 5: + got_isd(cp, len); + break; + case 6: + got_endmod(cp, len); + break; + case 7: + got_libhdr(cp, len); + break; + case 8: + got_libend(cp, len); + break; + default: + printf("Unknown record type %d\n", cp[0] & 0xff); + break; + } + + free(cp); + } + + if (bin) + { dump_bin (xferad, NULL, 0); + fclose (bin); + if (badbin) fprintf (stderr, "Probable errors in binary file\n"); + } + + fclose (fp); + return EXIT_SUCCESS; +} diff --git a/crossassemblers/macro11/dumpobj.dsp b/crossassemblers/macro11/dumpobj.dsp new file mode 100644 index 0000000..cbb76d1 --- /dev/null +++ b/crossassemblers/macro11/dumpobj.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="dumpobj" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dumpobj - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dumpobj.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dumpobj.mak" CFG="dumpobj - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dumpobj - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dumpobj - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/macro11", BAAAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dumpobj - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "dumpobj___Release" +# PROP Intermediate_Dir "dumpobj___Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "dumpobj - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "dumpobj___Debug" +# PROP Intermediate_Dir "dumpobj___Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "dumpobj - Win32 Release" +# Name "dumpobj - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\dumpobj.c +# End Source File +# Begin Source File + +SOURCE=.\Rad50.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\Rad50.h +# End Source File +# Begin Source File + +SOURCE=.\util.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/crossassemblers/macro11/license b/crossassemblers/macro11/license new file mode 100644 index 0000000..b5b4413 --- /dev/null +++ b/crossassemblers/macro11/license @@ -0,0 +1,30 @@ +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/crossassemblers/macro11/macro11.c b/crossassemblers/macro11/macro11.c new file mode 100644 index 0000000..8283e6f --- /dev/null +++ b/crossassemblers/macro11/macro11.c @@ -0,0 +1,6197 @@ +/* + Assembler compatible with MACRO-11. + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "macro11.h" + +#include "rad50.h" + +#include "object.h" + +#include "stream2.h" + +#include "mlb.h" + +#include "util.h" + +#define SYMMAX 6 /* I will honor this many character + symbols */ + +#define issym(c) (isalpha(c) || isdigit(c) || (c) == '.' || (c) == '$') + +/* Program sections: */ + +typedef struct section +{ + char *label; /* Section name */ + unsigned type; /* Section type */ +#define USER 1 /* user-defined */ +#define SYSTEM 2 /* A system symbol (like "."; value is an + enum) */ +#define INSTRUCTION 3 /* An instruction code (like "MOV"; value is + an enum) */ +#define PSEUDO 4 /* A pseudo-op (.PSECT, .TITLE, .MACRO, .IF; + value is an enum) */ +#define REGISTER 5 /* Symbol is a register (value 0=$0, value + 1=$1, ... $7) */ +#define USERMACRO 6 /* Symbol is a user macro */ + + unsigned flags; /* Flags, defined in object.h */ + unsigned pc; /* Current offset in the section */ + unsigned size; /* Current section size */ + unsigned sector; /* Used for complex relocation, and naught else */ +} SECTION; + +/* Symbol table entries */ + +typedef struct symbol +{ + char *label; /* Symbol name */ + unsigned value; /* Symbol value */ + int stmtno; /* Statement number of symbol's definition */ + unsigned flags; /* Symbol flags */ +#define PERMANENT 1 /* Symbol may not be redefined */ +#define GLOBAL 2 /* Symbol is global */ +#define WEAK 4 /* Symbol definition is weak */ +#define DEFINITION 8 /* Symbol is a global definition, not + reference */ +#define UNDEFINED 16 /* Symbol is a phony, undefined */ +#define LOCAL 32 /* Set if this is a local label (i.e. 10$) */ + + SECTION *section; /* Section in which this symbol is defined */ + struct symbol *next; /* Next symbol with the same hash value */ +} SYMBOL; + +/* Arguments given to macros or .IRP/.IRPC blocks */ + +typedef struct arg +{ + struct arg *next; /* Pointer in arg list */ + int locsym; /* Whether arg represents an optional + local symbol */ + char *label; /* Argument name */ + char *value; /* Default or active substitution */ +} ARG; + +/* A MACRO is a superstructure surrounding a SYMBOL. */ + +typedef struct macro +{ + SYMBOL sym; /* Surrounds a symbol, contains the macro + name */ + ARG *args; /* The argument list */ + BUFFER *text; /* The macro text */ +} MACRO; + +typedef struct ex_tree +{ + enum ex_type + { + EX_LIT=1, /* Expression is a literal value */ + EX_SYM=2, /* Expression has a symbol reference */ + EX_UNDEFINED_SYM=3, /* Expression is undefined sym reference */ + EX_TEMP_SYM=4, /* Expression is temp sym reference */ + + EX_COM=5, /* One's complement */ + EX_NEG=6, /* Negate */ + EX_ERR=7, /* Expression with an error */ + + EX_ADD=8, /* Add */ + EX_SUB=9, /* Subtract */ + EX_MUL=10, /* Multiply */ + EX_DIV=11, /* Divide */ + EX_AND=12, /* bitwise and */ + EX_OR=13 /* bitwise or */ + } type; + + char *cp; /* points to end of parsed expression */ + + union + { + struct + { + struct ex_tree *left, *right; /* Left, right children */ + } child; + unsigned lit; /* Literal value */ + SYMBOL *symbol; /* Symbol reference */ + } data; + +} EX_TREE; + +typedef struct addr_mode +{ + unsigned type; /* The bits that represent the addressing mode */ + /* bits 0:2 are register number */ + /* bit 3 is indirect */ + /* bits 4:6 are mode, where 0=Rn, 1=(Rn)+, + 2=-(Rn), 3=offset(Rn) */ + int rel; /* the addressing mode is PC-relative */ + EX_TREE *offset; /* Expression giving the offset */ +} ADDR_MODE; + +#define FALSE 0 /* Everybody needs FALSE and TRUE */ +#define TRUE 1 + +enum pseudo_ops +{ + P_ASCII, P_ASCIZ, P_ASECT, P_BLKB, P_BLKW, P_BYTE, P_CSECT, P_DSABL, + P_ENABL, P_END, P_ENDC, P_ENDM, P_ENDR, P_EOT, P_ERROR, P_EVEN, + P_FLT2, P_FLT4, P_GLOBL, P_IDENT, P_IF, P_IFF, P_IFT, P_IFTF, P_IIF, + P_IRP, P_IRPC, P_LIMIT, P_LIST, P_MCALL, P_MEXIT, P_NARG, P_NCHR, + P_NLIST, P_NTYPE, P_ODD, P_PACKED, P_PAGE, P_PRINT, P_PSECT, P_RADIX, + P_RAD50, P_REM, P_REPT, P_RESTORE, P_SAVE, P_SBTTL, P_TITLE, + P_WORD, P_MACRO, P_INCLU, P_WEAK, P_IFDF +}; + +enum instruction_ops +{ + I_ADC = 0005500, I_ADCB = 0105500, I_ADD = 0060000, I_ASH = 0072000, + I_ASHC = 0073000, I_ASL = 0006300, I_ASLB = 0106300, I_ASR = 0006200, + I_ASRB = 0106200, I_BCC = 0103000, I_BCS = 0103400, I_BEQ = 0001400, + I_BGE = 0002000, I_BGT = 0003000, I_BHI = 0101000, I_BHIS = 0103000, + I_BIC = 0040000, I_BICB = 0140000, I_BIS = 0050000, I_BISB = 0150000, + I_BIT = 0030000, I_BITB = 0130000, I_BLE = 0003400, I_BLO = 0103400, + I_BLOS = 0101400, I_BLT = 0002400, I_BMI = 0100400, I_BNE = 0001000, + I_BPL = 0100000, I_BPT = 0000003, I_BR = 0000400, I_BVC = 0102000, + I_BVS = 0102400, I_CALL = 0004700, I_CALLR = 0000100, I_CCC = 0000257, + I_CLC = 0000241, I_CLN = 0000250, I_CLR = 0005000, I_CLRB = 0105000, + I_CLV = 0000242, I_CLZ = 0000244, I_CMP = 0020000, I_CMPB = 0120000, + I_COM = 0005100, I_COMB = 0105100, I_DEC = 0005300, I_DECB = 0105300, + I_DIV = 0071000, I_EMT = 0104000, I_FADD = 0075000, I_FDIV = 0075030, + I_FMUL = 0075020, I_FSUB = 0075010, I_HALT = 0000000, I_INC = 0005200, + I_INCB = 0105200, I_IOT = 0000004, I_JMP = 0000100, I_JSR = 0004000, + I_MARK = 0006400, I_MED6X = 0076600, I_MED74C= 0076601, I_MFPD = 0106500, + I_MFPI = 0006500, I_MFPS = 0106700, I_MOV = 0010000, I_MOVB = 0110000, + I_MTPD = 0106600, I_MTPI = 0006600, I_MTPS = 0106400, I_MUL = 0070000, + I_NEG = 0005400, I_NEGB = 0105400, I_NOP = 0000240, I_RESET = 0000005, + I_RETURN= 0000207, I_ROL = 0006100, I_ROLB = 0106100, I_ROR = 0006000, + I_RORB = 0106000, I_RTI = 0000002, I_RTS = 0000200, I_RTT = 0000006, + I_SBC = 0005600, I_SBCB = 0105600, I_SCC = 0000277, I_SEC = 0000261, + I_SEN = 0000270, I_SEV = 0000262, I_SEZ = 0000264, I_SOB = 0077000, + I_SPL = 0000230, I_SUB = 0160000, I_SWAB = 0000300, I_SXT = 0006700, + I_TRAP = 0104400, I_TST = 0005700, I_TSTB = 0105700, I_WAIT = 0000001, + I_XFC = 0076700, I_XOR = 0074000, I_MFPT = 0000007, + /* CIS not implemented - maybe later */ + /* FPU */ + I_ABSD = 0170600, I_ABSF = 0170600, I_ADDD = 0172000, I_ADDF = 0172000, + I_CFCC = 0170000, I_CLRD = 0170400, I_CLRF = 0170400, I_CMPD = 0173400, + I_CMPF = 0173400, I_DIVD = 0174400, I_DIVF = 0174400, I_LDCDF = 0177400, + I_LDCFD = 0177400, I_LDCID = 0177000, I_LDCIF = 0177000, I_LDCLD = 0177000, + I_LDCLF = 0177000, I_LDD = 0172400, I_LDEXP = 0176400, I_LDF = 0172400, + I_LDFPS = 0170100, I_MODD = 0171400, I_MODF = 0171400, I_MULD = 0171000, + I_MULF = 0171000, I_NEGD = 0170700, I_NEGF = 0170700, I_SETD = 0170011, + I_SETF = 0170001, I_SETI = 0170002, I_SETL = 0170012, I_STA0 = 0170005, + I_STB0 = 0170006, I_STCDF = 0176000, I_STCDI = 0175400, I_STCDL = 0175400, + I_STCFD = 0176000, I_STCFI = 0175400, I_STCFL = 0175400, I_STD = 0174000, + I_STEXP = 0175000, I_STF = 0174000, I_STFPS = 0170200, I_STST = 0170300, + I_SUBD = 0173000, I_SUBF = 0173000, I_TSTD = 0170500, I_TSTF = 0170500 +}; + +enum operand_codes +{ + OC_MASK = 0xff00, /* mask over flags for operand types */ + OC_NONE = 0x0000, /* No operands */ + OC_1GEN = 0x0100, /* One general operand (CLR, TST, etc.) */ + OC_2GEN = 0x0200, /* Two general operand (MOV, CMP, etc.) */ + OC_BR = 0x0300, /* Branch */ + OC_ASH = 0x0400, /* ASH and ASHC (one gen, one reg) */ + OC_MARK = 0x0500, /* MARK instruction operand */ + OC_JSR = 0x0600, /* JSR, XOR (one reg, one gen) */ + OC_1REG = 0x0700, /* FADD, FSUB, FMUL, FDIV, RTS */ + OC_SOB = 0x0800, /* SOB */ + OC_1FIS = 0x0900, /* FIS (reg, gen) */ + OC_2FIS = 0x0a00, /* FIS (gen, reg) */ + OC__LAST = 0xff00 }; + +/* + format of a listing line + Interestingly, no instances of this struct are ever created. + It lives to be a way to layout the format of a list line. + I wonder if I should have bothered. +*/ + +typedef struct lstformat +{ + char flag[2]; /* Error flags */ + char line_number[6]; /* Line number */ + char pc[8]; /* Location */ + char words[8][3]; /* three instruction words */ + char source[1]; /* source line */ +} LSTFORMAT; + +#define SIZEOF_MEMBER(s, m) (sizeof((s *)0)->m) + +/* GLOBAL VARIABLES */ + +int pass = 0; /* The current assembly pass. 0 = first + pass. */ + +int stmtno = 0; /* The current source line number */ +int radix = 8; /* The current input conversion radix */ +int lsb = 0; /* The current local symbol section identifier */ +int last_lsb = 0; /* The last block in which a macro + automatic label was created */ +int last_locsym = 32768; /* The last local symbol number generated */ + +int enabl_debug = 0; /* Whether assembler debugging is enabled */ + +int enabl_ama = 0; /* When set, chooses absolute (037) versus + PC-relative */ + /* (067) addressing mode */ +int enabl_lsb = 0; /* When set, stops non-local symbol + definitions from delimiting local + symbol sections. */ + +int enabl_gbl = 1; /* Implicit definition of global symbols */ + +int list_md = 1; /* option to list macro/rept definition = yes */ + +int list_me = 1; /* option to list macro/rept expansion = yes */ + +int list_bex = 1; /* option to show binary */ + +int list_level = 1; /* Listing control level. .LIST + increments; .NLIST decrements */ + +char *listline; /* Source lines */ + +char *binline; /* for octal expansion */ + +FILE *lstfile = NULL; + +int suppressed = 0; /* Assembly suppressed by failed conditional */ + +#define MAX_MLBS 32 +MLB *mlbs[MAX_MLBS]; /* macro libraries specified on the + command line */ +int nr_mlbs = 0; /* Number of macro libraries */ + +typedef struct cond +{ + int ok; /* What the condition evaluated to */ + char *file; /* What file and line it occurred */ + int line; +} COND; + +#define MAX_CONDS 256 +COND conds[MAX_CONDS]; /* Stack of recent conditions */ +int last_cond; /* 0 means no stacked cond. */ + +SECTION *sect_stack[32]; /* 32 saved sections */ +int sect_sp; /* Stack pointer */ + +char *module_name = NULL; /* The module name (taken from the 'TITLE'); */ + +char *ident = NULL; /* .IDENT name */ + +EX_TREE *xfer_address = NULL; /* The transfer address */ + +SYMBOL *current_pc; /* The current program counter */ + +unsigned last_dot_addr; /* Last coded PC... */ +SECTION *last_dot_section; /* ...and it's program section */ + +#define DOT (current_pc->value) /* Handy reference to the current location */ + +/* The following are dummy psects for symbols which have meaning to + the assembler: */ + +SECTION register_section = +{ "", REGISTER, 0, 0 }; /* the section containing the registers */ + +SECTION pseudo_section = +{ "", PSEUDO, 0, 0 }; /* the section containing the + pseudo-operations */ + +SECTION instruction_section = +{ ". ABS.", INSTRUCTION, 0, 0 }; /* the section containing instructions */ + +SECTION macro_section = +{ "", SYSTEM, 0, 0, 0 }; /* Section for macros */ + +/* These are real psects that get written out to the object file */ + +SECTION absolute_section = +{ ". ABS.", SYSTEM, PSECT_GBL|PSECT_COM, 0, 0, 0}; /* The default + absolute section */ + +SECTION blank_section = +{ "", SYSTEM, PSECT_REL, 0, 0, 1}; /* The default relocatable section */ + +SECTION *sections[256] = { /* Array of sections in the order they were + defined */ + &absolute_section, &blank_section, }; + +int sector = 2; /* number of such sections */ + +SYMBOL *reg_sym[8]; /* Keep the register symbols in a handy array */ + +/* symbol tables */ + +#define HASH_SIZE 1023 + +typedef struct symbol_table +{ + SYMBOL *hash[HASH_SIZE]; +} SYMBOL_TABLE; + +SYMBOL_TABLE system_st; /* System symbols (Instructions, + pseudo-ops, registers) */ + +SYMBOL_TABLE section_st; /* Program sections */ + +SYMBOL_TABLE symbol_st; /* User symbols */ + +SYMBOL_TABLE macro_st; /* Macros */ + +SYMBOL_TABLE implicit_st; /* The symbols which may be implicit globals */ + +/* SYMBOL_ITER is used for iterating thru a symbol table. */ + +typedef struct symbol_iter +{ + int subscript; /* Current hash subscript */ + SYMBOL *current; /* Current symbol */ +} SYMBOL_ITER; + +/* EOL says whether a char* is pointing at the end of a line */ +#define EOL(c) (!(c) || (c) == '\n' || (c) == ';') + +/* reports errors */ +static void report(STREAM *str, char *fmt, ...) +{ + va_list ap; + char *name = "**"; + int line = 0; + + if(!pass) + return; /* Don't report now. */ + + if(str) + { + name = str->name; + line = str->line; + } + + fprintf(stderr, "%s:%d: ***ERROR ", name, line); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if(lstfile) + { + fprintf(lstfile, "%s:%d: ***ERROR ", name, line); + va_start(ap, fmt); + vfprintf(lstfile, fmt, ap); + va_end(ap); + } +} + +/* memcheck - crash out if a pointer (returned from malloc) is NULL. */ + +void *memcheck(void *ptr) +{ + if(ptr == NULL) + { + fprintf(stderr, "Out of memory.\n"); + exit(EXIT_FAILURE); + } + + return ptr; +} + +/* upcase turns a string to upper case */ + +static void upcase(char *str) +{ + while(*str) + { + *str = toupper(*str); + str++; + } +} + +/* hash_name hashes a name into a value from 0-HASH_SIZE */ + +static int hash_name(char *label) +{ + unsigned accum = 0; + + while(*label) + accum = (accum << 1) ^ *label++; + + accum %= HASH_SIZE; + + return accum; +} + +/* Allocate a new symbol. Does not add it to any symbol table. */ + +static SYMBOL *new_sym(char *label) +{ + SYMBOL *sym = memcheck(malloc(sizeof(SYMBOL))); + sym->label = memcheck(strdup(label)); + sym->section = NULL; + sym->value = 0; + sym->flags = 0; + return sym; +} + +/* Free a symbol. Does not remove it from any symbol table. */ + +static void free_sym(SYMBOL *sym) +{ + if(sym->label) + { + free(sym->label); + sym->label = NULL; + } + free(sym); +} + +/* remove_sym removes a symbol from it's symbol table. */ + +static void remove_sym(SYMBOL *sym, SYMBOL_TABLE *table) +{ + SYMBOL **prevp, *symp; + int hash; + + hash = hash_name(sym->label); + prevp = &table->hash[hash]; + while(symp = *prevp, symp != NULL && symp != sym) + prevp = &symp->next; + + if(symp) + *prevp = sym->next; +} + +/* lookup_sym finds a symbol in a table */ + +static SYMBOL *lookup_sym(char *label, SYMBOL_TABLE *table) +{ + unsigned hash; + SYMBOL *sym; + + hash = hash_name(label); + + sym = table->hash[hash]; + while(sym && strcmp(sym->label, label) != 0) + sym = sym->next; + + return sym; +} + +/* next_sym - returns the next symbol from a symbol table. Must be + preceeded by first_sym. Returns NULL after the last symbol. */ + +static SYMBOL *next_sym(SYMBOL_TABLE *table, SYMBOL_ITER *iter) +{ + if(iter->current) + iter->current = iter->current->next; + + while(iter->current == NULL) + { + if(iter->subscript >= HASH_SIZE) + return NULL; /* No more symbols. */ + iter->current = table->hash[iter->subscript]; + iter->subscript++; + } + + return iter->current; /* Got a symbol. */ +} + +/* first_sym - returns the first symbol from a symbol table. Symbols + are stored in random order. */ + +static SYMBOL *first_sym(SYMBOL_TABLE *table, SYMBOL_ITER *iter) +{ + iter->subscript = 0; + iter->current = NULL; + return next_sym(table, iter); +} + +/* add_table - add a symbol to a symbol table. */ + +static void add_table(SYMBOL *sym, SYMBOL_TABLE *table) +{ + int hash = hash_name(sym->label); + sym->next = table->hash[hash]; + table->hash[hash] = sym; +} + +/* add_sym - used throughout to add or update symbols in a symbol + table. */ + +static SYMBOL *add_sym(char *label, unsigned value, unsigned flags, + SECTION *section, SYMBOL_TABLE *table) +{ + SYMBOL *sym; + + sym = lookup_sym(label, table); + if(sym != NULL) + { + // A symbol registered as "undefined" can be changed. + + if((sym->flags & UNDEFINED) && + !(flags & UNDEFINED)) + { + sym->flags &= ~(PERMANENT|UNDEFINED); + } + + /* Check for compatible definition */ + else if(sym->section == section && + sym->value == value) + { + sym->flags |= flags; /* Merge flags quietly */ + return sym; /* 's okay */ + } + + if(!(sym->flags & PERMANENT)) + { + /* permit redefinition */ + sym->value = value; + sym->flags |= flags; + sym->section = section; + return sym; + } + + return NULL; /* Bad symbol redefinition */ + } + + sym = new_sym(label); + sym->flags = flags; + sym->stmtno = stmtno; + sym->section = section; + sym->value = value; + + add_table(sym, table); + + return sym; +} + +/* Allocate a new section */ + +static SECTION *new_section(void) +{ + SECTION *sect = memcheck(malloc(sizeof(SECTION))); + sect->flags = 0; + sect->size = 0; + sect->pc = 0; + sect->type = 0; + sect->sector = 0; + sect->label = NULL; + return sect; +} + +/* Allocate a new ARG */ + +static ARG *new_arg(void) +{ + ARG *arg = memcheck(malloc(sizeof(ARG))); + arg->locsym = 0; + arg->value = NULL; + arg->next = NULL; + arg->label = NULL; + return arg; +} + +/* Allocate a new macro */ + +static MACRO *new_macro(char *label) +{ + MACRO *mac = memcheck(malloc(sizeof(MACRO))); + + mac->sym.flags = 0; + mac->sym.label = label; + mac->sym.stmtno = stmtno; + mac->sym.next = NULL; + mac->sym.section = ¯o_section; + mac->sym.value = 0; + mac->args = NULL; + mac->text = NULL; + + return mac; +} + +/* Free a list of args (as for a macro, or a macro expansion) */ + +static void free_args(ARG *arg) +{ + ARG *next; + + while(arg) + { + next = arg->next; + if(arg->label) + { + free(arg->label); + arg->label = NULL; + } + if(arg->value) + { + free(arg->value); + arg->value = NULL; + } + free(arg); + arg = next; + } +} + +/* free a macro, it's args, it's text, etc. */ + +static void free_macro(MACRO *mac) +{ + if(mac->text) + { + free(mac->text); + } + free_args(mac->args); + free_sym(&mac->sym); +} + +/* do_list returns TRUE if listing is enabled. */ + +static int dolist(void) +{ + int ok = lstfile != NULL && pass > 0 && list_level > 0; + return ok; +} + +/* list_source saves a text line for later listing by list_flush */ + +static void list_source(STREAM *str, char *cp) +{ + if(dolist()) + { + int len = strcspn(cp, "\n"); + /* Save the line text away for later... */ + if(listline) + free(listline); + listline = memcheck(malloc(len + 1)); + memcpy(listline, cp, len); + listline[len] = 0; + + if(!binline) + binline = memcheck(malloc(sizeof(LSTFORMAT) + 16)); + + sprintf(binline, "%*s%*d", + SIZEOF_MEMBER(LSTFORMAT, flag), "", + SIZEOF_MEMBER(LSTFORMAT, line_number), str->line); + } +} + +/* padto adds blanks to the end of a string until it's the given + length. */ + +static void padto(char *str, int to) +{ + int needspace = to - strlen(str); + str += strlen(str); + while(needspace > 0) + *str++ = ' ', needspace--; + *str = 0; +} + +/* list_flush produces a buffered list line. */ + +static void list_flush(void) +{ + if(dolist()) + { + padto(binline, offsetof(LSTFORMAT, source)); + fputs(binline, lstfile); + fputs(listline, lstfile); + fputc('\n', lstfile); + listline[0] = 0; + binline[0] = 0; + } +} + +/* list_fit checks to see if a word will fit in the current listing + line. If not, it flushes and prepares another line. */ + +static void list_fit(STREAM *str, unsigned addr) +{ + int len = strlen(binline); + size_t col1 = offsetof(LSTFORMAT, source); + size_t col2 = offsetof(LSTFORMAT, pc); + + if(strlen(binline) >= col1) + { + int offset = offsetof(LSTFORMAT, pc); + list_flush(); + listline[0] = 0; + binline[0] = 0; + sprintf(binline, "%*s %6.6o", + offsetof(LSTFORMAT, pc), "", + addr); + padto(binline, offsetof(LSTFORMAT, words)); + } + else if(strlen(binline) <= col2) + { + sprintf(binline, "%*s%*d %6.6o", + SIZEOF_MEMBER(LSTFORMAT, flag), "", + SIZEOF_MEMBER(LSTFORMAT, line_number), str->line, + addr); + padto(binline, offsetof(LSTFORMAT, words)); + } +} + +/* list_value is used to show a computed value */ + +static void list_value(STREAM *str, unsigned word) +{ + if(dolist()) + { + /* Print the value and go */ + binline[0] = 0; + sprintf(binline, "%*s%*d %6.6o", + SIZEOF_MEMBER(LSTFORMAT, flag), "", + SIZEOF_MEMBER(LSTFORMAT, line_number), str->line, + word & 0177777); + } +} + +/* Print a word to the listing file */ + +void list_word(STREAM *str, unsigned addr, unsigned value, int size, + char *flags) +{ + if(dolist()) + { + list_fit(str, addr); + if(size == 1) + sprintf(binline + strlen(binline), " %3.3o%1.1s ", + value & 0377, flags); + else + sprintf(binline + strlen(binline), "%6.6o%1.1s ", + value & 0177777, flags); + } +} + +/* This is called by places that are about to store some code, or + which want to manually update DOT. */ + +static void change_dot(TEXT_RLD *tr, int size) +{ + if(size > 0) + { + if(last_dot_section != current_pc->section) + { + text_define_location(tr, current_pc->section->label, + ¤t_pc->value); + last_dot_section = current_pc->section; + last_dot_addr = current_pc->value; + } + if(last_dot_addr != current_pc->value) + { + text_modify_location(tr, ¤t_pc->value); + last_dot_addr = current_pc->value; + } + + /* Update for next time */ + last_dot_addr += size; + } + + if(DOT+size > current_pc->section->size) + current_pc->section->size = DOT+size; +} + +/* store_word stores a word to the object file and lists it to the + listing file */ + +static int store_word(STREAM *str, TEXT_RLD *tr, int size, + unsigned word) +{ + change_dot(tr, size); + list_word(str, DOT, word, size, ""); + return text_word(tr, &DOT, size, word); +} + +/* store_word stores a word to the object file and lists it to the + listing file */ + +static int store_displaced_word(STREAM *str, TEXT_RLD *tr, + int size, unsigned word) +{ + change_dot(tr, size); + list_word(str, DOT, word, size, "'"); + return text_displaced_word(tr, &DOT, size, word); +} + +static int store_global_displaced_offset_word(STREAM *str, TEXT_RLD *tr, + int size, + unsigned word, char *global) +{ + change_dot(tr, size); + list_word(str, DOT, word, size, "G"); + return text_global_displaced_offset_word(tr, &DOT, size, + word, global); +} + +static int store_global_offset_word(STREAM *str, TEXT_RLD *tr, + int size, + unsigned word, char *global) +{ + change_dot(tr, size); + list_word(str, DOT, word, size, "G"); + return text_global_offset_word(tr, &DOT, size, word, global); +} + +static int store_internal_word(STREAM *str, TEXT_RLD *tr, + int size, unsigned word) +{ + change_dot(tr, size); + list_word(str, DOT, word, size, ""); + return text_internal_word(tr, &DOT, size, word); +} + +static int store_psect_displaced_offset_word(STREAM *str, TEXT_RLD *tr, + int size, + unsigned word, char *name) +{ + change_dot(tr, size); + list_word(str, DOT, word, size, ""); + return text_psect_displaced_offset_word(tr, &DOT, size, word, name); +} + +static int store_psect_offset_word(STREAM *str, TEXT_RLD *tr, + int size, + unsigned word, char *name) +{ + change_dot(tr, size); + list_word(str, DOT, word, size, ""); + return text_psect_offset_word(tr, &DOT, size, word, name); +} + +static int store_limits(STREAM *str, TEXT_RLD *tr) +{ + change_dot(tr, 4); + list_word(str, DOT, 0, 2, ""); + list_word(str, DOT+2, 0, 2, ""); + return text_limits(tr, &DOT); +} + +/* skipwhite - used everywhere to advance a char pointer past spaces */ + +static char *skipwhite(char *cp) +{ + while(*cp == ' ' || *cp == '\t') + cp++; + return cp; +} + +/* skipdelim - used everywhere to advance between tokens. Whitespace + and one comma are allowed delims. */ + +static char *skipdelim(char *cp) +{ + cp = skipwhite(cp); + if(*cp == ',') + cp = skipwhite(cp+1); + return cp; +} + +/* Parse PDP-11 64-bit floating point format. */ +/* Give a pointer to "size" words to receive the result. */ +/* Note: there are probably degenerate cases that store incorrect + results. For example, I think rounding up a FLT2 might cause + exponent overflow. Sorry. */ +/* Note also that the full 49 bits of precision probably aren't + available on the source platform, given the widespread application + of IEEE floating point formats, so expect some differences. Sorry + again. */ + +int parse_float(char *cp, char **endp, int size, unsigned *flt) +{ + double d; /* value */ + double frac; /* fractional value */ + ulong64 ufrac; /* fraction converted to 49 bit + unsigned integer */ + int i; /* Number of fields converted by sscanf */ + int n; /* Number of characters converted by sscanf */ + int sexp; /* Signed exponent */ + unsigned exp; /* Unsigned excess-128 exponent */ + unsigned sign = 0; /* Sign mask */ + + i = sscanf(cp, "%lf%n", &d, &n); + if(i == 0) + return 0; /* Wasn't able to convert */ + + cp += n; + if(endp) + *endp = cp; + + if(d == 0.0) + { + flt[0] = flt[1] = flt[2] = flt[3] = 0; /* All-bits-zero equals zero */ + return 1; /* Good job. */ + } + + frac = frexp(d, &sexp); /* Separate into exponent and mantissa */ + if(sexp < -128 || sexp > 127) + return 0; /* Exponent out of range. */ + + exp = sexp + 128; /* Make excess-128 mode */ + exp &= 0xff; /* express in 8 bits */ + + if(frac < 0) + { + sign = 0100000; /* Negative sign */ + frac = -frac; /* fix the mantissa */ + } + + /* The following big literal is 2 to the 49th power: */ + ufrac = (ulong64) (frac * 72057594037927936.0); /* Align fraction bits */ + + /* Round from FLT4 to FLT2 */ + if(size < 4) + { + ufrac += 0x80000000; /* Round to nearest 32-bit + representation */ + + if(ufrac > 0x200000000000) /* Overflow? */ + { + ufrac >>= 1; /* Normalize */ + exp--; + } + } + + flt[0] = (unsigned) (sign | (exp << 7) | (ufrac >> 48) & 0x7F); + if(size > 1) + { + flt[1] = (unsigned) ((ufrac >> 32) & 0xffff); + if(size > 2) + { + flt[2] = (unsigned) ((ufrac >> 16) & 0xffff); + flt[3] = (unsigned) ((ufrac >> 0) & 0xffff); + } + } + + return 1; +} + +/* Allocate an EX_TREE */ + +static EX_TREE *new_ex_tree(void) +{ + EX_TREE *tr = memcheck(malloc(sizeof(EX_TREE))); + return tr; +} + + +/* Create an EX_TREE representing a parse error */ + +static EX_TREE *ex_err(EX_TREE *tp, char *cp) +{ + EX_TREE *errtp; + + errtp = new_ex_tree(); + errtp->cp = cp; + errtp->type = EX_ERR; + errtp->data.child.left = tp; + + return errtp; +} + +/* Create an EX_TREE representing a literal value */ + +static EX_TREE *new_ex_lit(unsigned value) +{ + EX_TREE *tp; + + tp = new_ex_tree(); + tp->type = EX_LIT; + tp->data.lit = value; + + return tp; +} + +/* The recursive-descent expression parser parse_expr. */ + +/* This parser was designed for expressions with operator precedence. + However, MACRO-11 doesn't observe any sort of operator precedence. + If you feel your source deserves better, give the operators + appropriate precedence values right here. */ + +#define ADD_PREC 1 +#define MUL_PREC 1 +#define AND_PREC 1 +#define OR_PREC 1 + +EX_TREE *parse_unary(char *cp); /* Prototype for forward calls */ + +EX_TREE *parse_binary(char *cp, char term, int depth) +{ + EX_TREE *leftp, *rightp, *tp; + + leftp = parse_unary(cp); + + while(leftp->type != EX_ERR) + { + cp = skipwhite(leftp->cp); + + if(*cp == term) + return leftp; + + switch(*cp) + { + case '+': + if(depth >= ADD_PREC) + return leftp; + + rightp = parse_binary(cp+1, term, ADD_PREC); + tp = new_ex_tree(); + tp->type = EX_ADD; + tp->data.child.left = leftp; + tp->data.child.right = rightp; + tp->cp = rightp->cp; + leftp = tp; + break; + + case '-': + if(depth >= ADD_PREC) + return leftp; + + rightp = parse_binary(cp+1, term, ADD_PREC); + tp = new_ex_tree(); + tp->type = EX_SUB; + tp->data.child.left = leftp; + tp->data.child.right = rightp; + tp->cp = rightp->cp; + leftp = tp; + break; + + case '*': + if(depth >= MUL_PREC) + return leftp; + + rightp = parse_binary(cp+1, term, MUL_PREC); + tp = new_ex_tree(); + tp->type = EX_MUL; + tp->data.child.left = leftp; + tp->data.child.right = rightp; + tp->cp = rightp->cp; + leftp = tp; + break; + + case '/': + if(depth >= MUL_PREC) + return leftp; + + rightp = parse_binary(cp+1, term, MUL_PREC); + tp = new_ex_tree(); + tp->type = EX_DIV; + tp->data.child.left = leftp; + tp->data.child.right = rightp; + tp->cp = rightp->cp; + leftp = tp; + break; + + case '!': + if(depth >= OR_PREC) + return leftp; + + rightp = parse_binary(cp+1, term, 2); + tp = new_ex_tree(); + tp->type = EX_OR; + tp->data.child.left = leftp; + tp->data.child.right = rightp; + tp->cp = rightp->cp; + leftp = tp; + break; + + case '&': + if(depth >= AND_PREC) + return leftp; + + rightp = parse_binary(cp+1, term, AND_PREC); + tp = new_ex_tree(); + tp->type = EX_AND; + tp->data.child.left = leftp; + tp->data.child.right = rightp; + tp->cp = rightp->cp; + leftp = tp; + break; + + default: + /* Some unknown character. Let caller decide if it's okay. */ + + return leftp; + + } /* end switch */ + } /* end while */ + + /* Can't be reached except by error. */ + return leftp; +} + +/* get_symbol is used all over the place to pull a symbol out of the + text. */ + +static char *get_symbol(char *cp, char **endp, int *islocal) +{ + int len; + char *symcp; + int digits = 0; + + cp = skipwhite(cp); /* Skip leading whitespace */ + + if(!issym(*cp)) + return NULL; + + digits = 0; + if(isdigit(*cp)) + digits = 2; /* Think about digit count */ + + for(symcp = cp + 1; issym(*symcp); symcp++) + { + if(!isdigit(*symcp)) /* Not a digit? */ + digits--; /* Make a note. */ + } + + if(digits == 2) + return NULL; /* Not a symbol, it's a digit string */ + + if(endp) + *endp = symcp; + + len = symcp - cp; + + /* Now limit length */ + if(len > SYMMAX) + len = SYMMAX; + + symcp = memcheck(malloc(len + 1)); + + memcpy(symcp, cp, len); + symcp[len] = 0; + upcase(symcp); + + if(islocal) + { + *islocal = 0; + + /* Turn to local label format */ + if(digits == 1) + { + if(symcp[len-1] == '$') + { + char *newsym = memcheck(malloc(32)); /* Overkill */ + sprintf(newsym, "%d$%d", strtol(symcp, NULL, 10), lsb); + free(symcp); + symcp = newsym; + if(islocal) + *islocal = LOCAL; + } + else + { + free(symcp); + return NULL; + } + } + } + else + { + /* disallow local label format */ + if(isdigit(*symcp)) + { + free(symcp); + return NULL; + } + } + + return symcp; +} + +/* + brackrange is used to find a range of text which may or may not be + bracketed. + + If the brackets are <>, then nested brackets are detected. + If the brackets are of the form ^/.../ no detection of nesting is + attempted. + + Using brackets ^<...< will mess this routine up. What in the world + are you thinking? +*/ + +int brackrange(char *cp, int *start, int *length, char **endp) +{ + char endstr[6]; + int endlen; + int nest; + int len; + + switch(*cp) + { + case '^': + endstr[0] = cp[1]; + strcpy(endstr+1, "\n"); + *start = 2; + endlen = 1; + break; + case '<': + strcpy(endstr, "<>\n"); + endlen = 1; + *start = 1; + break; + default: + return FALSE; + } + + cp += *start; + + len = 0; + nest = 1; + while(nest) + { + int sublen; + sublen = strcspn(cp+len, endstr); + if(cp[len+sublen] == '<') + nest++; + else + nest--; + len += sublen; + } + + *length = len; + if(endp) + *endp = cp + len + endlen; + + return 1; +} + +/* parse_unary parses out a unary operator or leaf expression. */ + +EX_TREE *parse_unary(char *cp) +{ + EX_TREE *tp; + + /* Skip leading whitespace */ + cp = skipwhite(cp); + + if(*cp == '%') /* Register notation */ + { + unsigned reg; + cp++; + reg = strtoul(cp, &cp, 8); + if(reg > 7) + return ex_err(NULL, cp); + + /* This returns references to the built-in register symbols */ + tp = new_ex_tree(); + tp->type = EX_SYM; + tp->data.symbol = reg_sym[reg]; + tp->cp = cp; + return tp; + } + + /* Unary negate */ + if(*cp == '-') + { + tp = new_ex_tree(); + tp->type = EX_NEG; + tp->data.child.left = parse_unary(cp+1); + tp->cp = tp->data.child.left->cp; + return tp; + } + + /* Unary + I can ignore. */ + if(*cp == '+') + return parse_unary(cp+1); + + if(*cp == '^') + { + int save_radix; + switch(tolower(cp[1])) + { + case 'c': + /* ^C, ones complement */ + tp = new_ex_tree(); + tp->type = EX_COM; + tp->data.child.left = parse_unary(cp+2); + tp->cp = tp->data.child.left->cp; + return tp; + case 'b': + /* ^B, binary radix modifier */ + save_radix = radix; + radix = 2; + tp = parse_unary(cp+2); + radix = save_radix; + return tp; + case 'o': + /* ^O, octal radix modifier */ + save_radix = radix; + radix = 8; + tp = parse_unary(cp+2); + radix = save_radix; + return tp; + case 'd': + /* ^D, decimal radix modifier */ + save_radix = radix; + radix = 10; + tp = parse_unary(cp+2); + radix = save_radix; + return tp; + case 'x': + /* An enhancement! ^X, hexadecimal radix modifier */ + save_radix = radix; + radix = 16; + tp = parse_unary(cp+2); + radix = save_radix; + return tp; + case 'r': + /* ^R, RAD50 literal */ + { + int start, len; + char *endcp; + unsigned value; + cp += 2; + if(brackrange(cp, &start, &len, &endcp)) + value = rad50(cp+start, NULL); + else + value = rad50(cp, &endcp); + tp = new_ex_lit(value); + tp->cp = endcp; + return tp; + } + case 'f': + /* ^F, single-word floating point literal indicator */ + { + unsigned flt[1]; + char *endcp; + if(!parse_float(cp+2, &endcp, 1, flt)) + { + tp = ex_err(NULL, cp+2); + } + else + { + tp = new_ex_lit(flt[0]); + tp->cp = endcp; + } + return tp; + } + } + + if(ispunct(cp[1])) + { + char *ecp; + /* oddly-bracketed expression like this: ^/expression/ */ + tp = parse_binary(cp+2, cp[1], 0); + ecp = skipwhite(tp->cp); + + if(*ecp != cp[1]) + return ex_err(tp, ecp); + + tp->cp = ecp + 1; + return tp; + } + } + + /* Bracketed subexpression */ + if(*cp == '<') + { + char *ecp; + tp = parse_binary(cp+1, '>', 0); + ecp = skipwhite(tp->cp); + if(*ecp != '>') + return ex_err(tp, ecp); + + tp->cp = ecp + 1; + return tp; + } + + /* Check for ASCII constants */ + + if(*cp == '\'') + { + /* 'x single ASCII character */ + cp++; + tp = new_ex_tree(); + tp->type = EX_LIT; + tp->data.lit = *cp & 0xff; + tp->cp = ++cp; + return tp; + } + + if(*cp == '\"') + { + /* "xx ASCII character pair */ + cp++; + tp = new_ex_tree(); + tp->type = EX_LIT; + tp->data.lit = (cp[0] & 0xff) | ((cp[1] & 0xff) << 8); + tp->cp = cp + 2; + return tp; + } + + /* Numeric constants are trickier than they need to be, */ + /* since local labels start with a digit too. */ + if(isdigit(*cp)) + { + char *label; + int local; + + if((label = get_symbol(cp, NULL, &local)) == NULL) + { + char *endcp; + unsigned long value; + int rad = radix; + + /* get_symbol returning NULL assures me that it's not a + local label. */ + + /* Look for a trailing period, to indicate decimal... */ + for(endcp = cp; isdigit(*endcp); endcp++) + ; + if(*endcp == '.') + rad = 10; + + value = strtoul(cp, &endcp, rad); + if(*endcp == '.') + endcp++; + + tp = new_ex_tree(); + tp->type = EX_LIT; + tp->data.lit = value; + tp->cp = endcp; + + return tp; + } + + free(label); + } + + /* Now check for a symbol */ + + { + char *label; + int local; + SYMBOL *sym; + + /* Optimization opportunity: I don't really need to call + get_symbol a second time. */ + + if(!(label = get_symbol(cp, &cp, &local))) + { + tp = ex_err(NULL, cp); /* Not a valid label. */ + return tp; + } + + sym = lookup_sym(label, &symbol_st); + if(sym == NULL) + { + /* A symbol from the "PST", which means an instruction + code. */ + sym = lookup_sym(label, &system_st); + } + + if(sym != NULL) + { + tp = new_ex_tree(); + tp->cp = cp; + tp->type = EX_SYM; + tp->data.symbol = sym; + + free(label); + return tp; + } + + /* The symbol was not found. Create an "undefined symbol" + reference. */ + sym = memcheck(malloc(sizeof(SYMBOL))); + sym->label = label; + sym->flags = UNDEFINED | local; + sym->stmtno = stmtno; + sym->next = NULL; + sym->section = &absolute_section; + sym->value = 0; + + tp = new_ex_tree(); + tp->cp = cp; + tp->type = EX_UNDEFINED_SYM; + tp->data.symbol = sym; + + return tp; + } +} + +/* Diagnostic: symflags returns a char* which gives flags I can use to + show the context of a symbol. */ + +static char *symflags(SYMBOL *sym) +{ + static char temp[8]; + char *fp = temp; + if(sym->flags & GLOBAL) + *fp++ = 'G'; + if(sym->flags & PERMANENT) + *fp++ = 'P'; + if(sym->flags & DEFINITION) + *fp++ = 'D'; + *fp = 0; + return fp; +} + +/* Diagnostic: print an expression tree. I used this in various + places to help me diagnose parse problems, by putting in calls to + print_tree when I didn't understand why something wasn't working. + This is currently dead code, nothing calls it; but I don't want it + to go away. Hopefully the compiler will realize when it's dead, and + eliminate it. */ + +static void print_tree(FILE *printfile, EX_TREE *tp, int depth) +{ + SYMBOL *sym; + + switch(tp->type) + { + case EX_LIT: + fprintf(printfile, "%o", tp->data.lit & 0177777); + break; + + case EX_SYM: + case EX_TEMP_SYM: + sym = tp->data.symbol; + fprintf(printfile, "%s{%s%o:%s}", tp->data.symbol->label, + symflags(sym), sym->value, sym->section->label); + break; + + case EX_UNDEFINED_SYM: + fprintf(printfile, "%s{%o:undefined}", tp->data.symbol->label, + tp->data.symbol->value); + break; + + case EX_COM: + fprintf(printfile, "^C<"); + print_tree(printfile, tp->data.child.left, depth+4); + fprintf(printfile, ">"); + break; + + case EX_NEG: + fprintf(printfile, "-<"); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('>', printfile); + break; + + case EX_ERR: + fprintf(printfile, "{expression error}"); + if(tp->data.child.left) + { + fputc('<', printfile); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('>', printfile); + } + break; + + case EX_ADD: + fputc('<', printfile); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('+', printfile); + print_tree(printfile, tp->data.child.right, depth+4); + fputc('>', printfile); + break; + + case EX_SUB: + fputc('<', printfile); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('-', printfile); + print_tree(printfile, tp->data.child.right, depth+4); + fputc('>', printfile); + break; + + case EX_MUL: + fputc('<', printfile); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('*', printfile); + print_tree(printfile, tp->data.child.right, depth+4); + fputc('>', printfile); + break; + + case EX_DIV: + fputc('<', printfile); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('/', printfile); + print_tree(printfile, tp->data.child.right, depth+4); + fputc('>', printfile); + break; + + case EX_AND: + fputc('<', printfile); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('&', printfile); + print_tree(printfile, tp->data.child.right, depth+4); + fputc('>', printfile); + break; + + case EX_OR: + fputc('<', printfile); + print_tree(printfile, tp->data.child.left, depth+4); + fputc('!', printfile); + print_tree(printfile, tp->data.child.right, depth+4); + fputc('>', printfile); + break; + } + + if(depth == 0) + fputc('\n', printfile); +} + +/* free_tree frees an expression tree. */ + +static void free_tree(EX_TREE *tp) +{ + switch(tp->type) + { + case EX_UNDEFINED_SYM: + case EX_TEMP_SYM: + free(tp->data.symbol->label); + free(tp->data.symbol); + case EX_LIT: + case EX_SYM: + free(tp); + break; + + case EX_COM: + case EX_NEG: + free_tree(tp->data.child.left); + free(tp); + break; + + case EX_ERR: + if(tp->data.child.left) + free_tree(tp->data.child.left); + free(tp); + break; + + case EX_ADD: + case EX_SUB: + case EX_MUL: + case EX_DIV: + case EX_AND: + case EX_OR: + free_tree(tp->data.child.left); + free_tree(tp->data.child.right); + free(tp); + break; + } +} + +/* new_temp_sym allocates a new EX_TREE entry of type "TEMPORARY + SYMBOL" (slight semantic difference from "UNDEFINED"). */ + +static EX_TREE *new_temp_sym(char *label, SECTION *section, unsigned value) +{ + SYMBOL *sym; + EX_TREE *tp; + + sym = memcheck(malloc(sizeof(SYMBOL))); + sym->label = memcheck(strdup(label)); + sym->flags = 0; + sym->stmtno = stmtno; + sym->next = NULL; + sym->section = section; + sym->value = value; + + tp = new_ex_tree(); + tp->type = EX_TEMP_SYM; + tp->data.symbol = sym; + + return tp; +} + +#define RELTYPE(tp) (((tp)->type == EX_SYM || (tp)->type == EX_TEMP_SYM) && \ + (tp)->data.symbol->section->flags & PSECT_REL) + +/* evaluate "evaluates" an EX_TREE, ideally trying to produce a + constant value, else a symbol plus an offset. */ + +static EX_TREE *evaluate(EX_TREE *tp, int undef) +{ + EX_TREE *res; + char *cp = tp->cp; + + switch(tp->type) + { + case EX_SYM: + { + SYMBOL *sym = tp->data.symbol; + + /* Change some symbols to "undefined" */ + + if(undef) + { + int change = 0; + +#if 0 /* I'd prefer this behavior, but + MACRO.SAV is a bit too + primitive. */ + /* A temporary symbol defined later is "undefined." */ + if(!(sym->flags & PERMANENT) && sym->stmtno > stmtno) + change = 1; +#endif + + /* A global symbol with no assignment is "undefined." */ + /* Go figure. */ + if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) + change = 1; + + if(change) + { + res = new_temp_sym(tp->data.symbol->label, + tp->data.symbol->section, tp->data.symbol->value); + res->type = EX_UNDEFINED_SYM; + break; + } + } + + /* Turn defined absolute symbol to a literal */ + if(!(sym->section->flags & PSECT_REL) && + (sym->flags & (GLOBAL|DEFINITION)) != GLOBAL && + sym->section->type != REGISTER) + { + res = new_ex_lit(sym->value); + break; + } + + /* Make a temp copy of any reference to "." since it might + change as complex relocatable expressions are written out + */ + if(strcmp(sym->label, ".") == 0) + { + res = new_temp_sym(".", sym->section, sym->value); + break; + } + + /* Copy other symbol reference verbatim. */ + res = new_ex_tree(); + res->type = EX_SYM; + res->data.symbol = tp->data.symbol; + res->cp = tp->cp; + break; + } + + case EX_LIT: + res = new_ex_tree(); + *res = *tp; + break; + + case EX_TEMP_SYM: + case EX_UNDEFINED_SYM: + /* Copy temp and undefined symbols */ + res = new_temp_sym(tp->data.symbol->label, + tp->data.symbol->section, + tp->data.symbol->value); + res->type = tp->type; + break; + + case EX_COM: + /* Complement */ + tp = evaluate(tp->data.child.left, undef); + if(tp->type == EX_LIT) + { + /* Complement the literal */ + res = new_ex_lit(~tp->data.lit); + free_tree(tp); + } + else + { + /* Copy verbatim. */ + res = new_ex_tree(); + res->type = EX_NEG; + res->cp = tp->cp; + res->data.child.left = tp; + } + + break; + + case EX_NEG: + tp = evaluate(tp->data.child.left, undef); + if(tp->type == EX_LIT) + { + /* negate literal */ + res = new_ex_lit((unsigned)-(int)tp->data.lit); + free_tree(tp); + } + else if(tp->type == EX_SYM || tp->type == EX_TEMP_SYM) + { + /* Make a temp sym with the negative value of the given + sym (this works for symbols within relocatable sections + too) */ + res = new_temp_sym("*TEMP", tp->data.symbol->section, + (unsigned)-(int)tp->data.symbol->value); + res->cp = tp->cp; + free_tree(tp); + } + else + { + /* Copy verbatim. */ + res = new_ex_tree(); + res->type = EX_NEG; + res->cp = tp->cp; + res->data.child.left = tp; + } + break; + + case EX_ERR: + /* Copy */ + res = ex_err(tp->data.child.left, tp->cp); + break; + + case EX_ADD: + { + EX_TREE *left, *right; + + left = evaluate(tp->data.child.left, undef); + right = evaluate(tp->data.child.right, undef); + + /* Both literals? Sum them and return result. */ + if(left->type == EX_LIT && right->type == EX_LIT) + { + res = new_ex_lit(left->data.lit + right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + /* Commutative: A+x == x+A. + Simplify by putting the literal on the right */ + if(left->type == EX_LIT) + { + EX_TREE *temp = left; + left = right; + right = temp; + } + + if(right->type == EX_LIT && /* Anything plus 0 == itself */ + right->data.lit == 0) + { + res = left; + free_tree(right); + break; + } + + /* Relative symbol plus lit is replaced with a temp sym + holding the sum */ + if(RELTYPE(left) && right->type == EX_LIT) + { + SYMBOL *sym = left->data.symbol; + res = new_temp_sym("*ADD", sym->section, sym->value + + right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + /* Associative: +y == A+ */ + /* and if x+y is constant, I can do that math. */ + if(left->type == EX_ADD && right->type == EX_LIT) + { + EX_TREE *leftright = left->data.child.right; + if(leftright->type == EX_LIT) + { + /* Do the shuffle */ + res = left; + leftright->data.lit += right->data.lit; + free_tree(right); + break; + } + } + + /* Associative: +y == A+ */ + /* and if y-x is constant, I can do that math. */ + if(left->type == EX_SUB && right->type == EX_LIT) + { + EX_TREE *leftright = left->data.child.right; + if(leftright->type == EX_LIT) + { + /* Do the shuffle */ + res = left; + leftright->data.lit = right->data.lit - leftright->data.lit; + free_tree(right); + break; + } + } + + /* Anything else returns verbatim */ + res = new_ex_tree(); + res->type = EX_ADD; + res->data.child.left = left; + res->data.child.right = right; + } + break; + + case EX_SUB: + { + EX_TREE *left, *right; + + left = evaluate(tp->data.child.left, undef); + right = evaluate(tp->data.child.right, undef); + + /* Both literals? Subtract them and return a lit. */ + if(left->type == EX_LIT && right->type == EX_LIT) + { + res = new_ex_lit(left->data.lit - right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + if(right->type == EX_LIT && /* Symbol minus 0 == symbol */ + right->data.lit == 0) + { + res = left; + free_tree(right); + break; + } + + /* A relocatable minus an absolute - make a new temp sym + to represent that. */ + if(RELTYPE(left) && + right->type == EX_LIT) + { + SYMBOL *sym = left->data.symbol; + res = new_temp_sym("*SUB", sym->section, + sym->value - right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + if(RELTYPE(left) && + RELTYPE(right) && + left->data.symbol->section == right->data.symbol->section) + { + /* Two defined symbols in the same psect. Resolve + their difference as a literal. */ + res = new_ex_lit(left->data.symbol->value - + right->data.symbol->value); + free_tree(left); + free_tree(right); + break; + } + + /* Associative: -y == A+ */ + /* and if x-y is constant, I can do that math. */ + if(left->type == EX_ADD && right->type == EX_LIT) + { + EX_TREE *leftright = left->data.child.right; + if(leftright->type == EX_LIT) + { + /* Do the shuffle */ + res = left; + leftright->data.lit -= right->data.lit; + free_tree(right); + break; + } + } + + /* Associative: -y == A- */ + /* and if x+y is constant, I can do that math. */ + if(left->type == EX_SUB && right->type == EX_LIT) + { + EX_TREE *leftright = left->data.child.right; + if(leftright->type == EX_LIT) + { + /* Do the shuffle */ + res = left; + leftright->data.lit += right->data.lit; + free_tree(right); + break; + } + } + + /* Anything else returns verbatim */ + res = new_ex_tree(); + res->type = EX_SUB; + res->data.child.left = left; + res->data.child.right = right; + } + break; + + case EX_MUL: + { + EX_TREE *left, *right; + + left = evaluate(tp->data.child.left, undef); + right = evaluate(tp->data.child.right, undef); + + /* Can only multiply if both are literals */ + if(left->type == EX_LIT && right->type == EX_LIT) + { + res = new_ex_lit(left->data.lit * right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + /* Commutative: A*x == x*A. + Simplify by putting the literal on the right */ + if(left->type == EX_LIT) + { + EX_TREE *temp = left; + left = right; + right = temp; + } + + if(right->type == EX_LIT && /* Symbol times 1 == symbol */ + right->data.lit == 1) + { + res = left; + free_tree(right); + break; + } + + if(right->type == EX_LIT && /* Symbol times 0 == 0 */ + right->data.lit == 0) + { + res = right; + free_tree(left); + break; + } + + /* Associative: *y == A* */ + /* If x*y is constant, I can do this math. */ + /* Is this safe? I will potentially be doing it */ + /* with greater accuracy than the target platform. */ + /* Hmmm. */ + + if(left->type == EX_MUL && right->type == EX_LIT) + { + EX_TREE *leftright = left->data.child.right; + if(leftright->type == EX_LIT) + { + /* Do the shuffle */ + res = left; + leftright->data.lit *= right->data.lit; + free_tree(right); + break; + } + } + + /* Anything else returns verbatim */ + res = new_ex_tree(); + res->type = EX_MUL; + res->data.child.left = left; + res->data.child.right = right; + } + break; + + case EX_DIV: + { + EX_TREE *left, *right; + + left = evaluate(tp->data.child.left, undef); + right = evaluate(tp->data.child.right, undef); + + /* Can only divide if both are literals */ + if(left->type == EX_LIT && right->type == EX_LIT) + { + res = new_ex_lit(left->data.lit / right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + if(right->type == EX_LIT && /* Symbol divided by 1 == symbol */ + right->data.lit == 1) + { + res = left; + free_tree(right); + break; + } + + /* Anything else returns verbatim */ + res = new_ex_tree(); + res->type = EX_DIV; + res->data.child.left = left; + res->data.child.right = right; + } + break; + + case EX_AND: + { + EX_TREE *left, *right; + + left = evaluate(tp->data.child.left, undef); + right = evaluate(tp->data.child.right, undef); + + /* Operate if both are literals */ + if(left->type == EX_LIT && right->type == EX_LIT) + { + res = new_ex_lit(left->data.lit & right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + /* Commutative: A&x == x&A. + Simplify by putting the literal on the right */ + if(left->type == EX_LIT) + { + EX_TREE *temp = left; + left = right; + right = temp; + } + + if(right->type == EX_LIT && /* Symbol AND 0 == 0 */ + right->data.lit == 0) + { + res = new_ex_lit(0); + free_tree(left); + free_tree(right); + break; + } + + if(right->type == EX_LIT && /* Symbol AND 0177777 == symbol */ + right->data.lit == 0177777) + { + res = left; + free_tree(right); + break; + } + + /* Anything else returns verbatim */ + res = new_ex_tree(); + res->type = EX_AND; + res->data.child.left = left; + res->data.child.right = right; + } + break; + + case EX_OR: + { + EX_TREE *left, *right; + + left = evaluate(tp->data.child.left, undef); + right = evaluate(tp->data.child.right, undef); + + /* Operate if both are literals */ + if(left->type == EX_LIT && right->type == EX_LIT) + { + res = new_ex_lit(left->data.lit | right->data.lit); + free_tree(left); + free_tree(right); + break; + } + + /* Commutative: A!x == x!A. + Simplify by putting the literal on the right */ + if(left->type == EX_LIT) + { + EX_TREE *temp = left; + left = right; + right = temp; + } + + if(right->type == EX_LIT && /* Symbol OR 0 == symbol */ + right->data.lit == 0) + { + res = left; + free_tree(right); + break; + } + + if(right->type == EX_LIT && /* Symbol OR 0177777 == 0177777 */ + right->data.lit == 0177777) + { + res = new_ex_lit(0177777); + free_tree(left); + free_tree(right); + break; + } + + /* Anything else returns verbatim */ + res = new_ex_tree(); + res->type = EX_OR; + res->data.child.left = left; + res->data.child.right = right; + } + break; + } + + res->cp = cp; + return res; +} + +/* + parse_expr - this gets called everywhere. It parses and evaluates + an arithmetic expression. +*/ + +EX_TREE *parse_expr(char *cp, int undef) +{ + EX_TREE *expr; + EX_TREE *value; + + expr = parse_binary(cp, 0, 0); /* Parse into a tree */ + value = evaluate(expr, undef); /* Perform the arithmetic */ + value->cp = expr->cp; /* Pointer to end of text is part of + the rootmost node */ + free_tree(expr); /* Discard parse in favor of + evaluation */ + + return value; +} + +/* free_addr_mode frees the storage consumed by an addr_mode */ + +static void free_addr_mode(ADDR_MODE *mode) +{ + if(mode->offset) + free_tree(mode->offset); + mode->offset = NULL; +} + +/* Get the register indicated by the expression */ + +#define NO_REG 0777 + +static unsigned get_register(EX_TREE *expr) +{ + unsigned reg; + + if(expr->type == EX_LIT && + expr->data.lit <= 7) + { + reg = expr->data.lit; + return reg; + } + + if(expr->type == EX_SYM && + expr->data.symbol->section->type == REGISTER) + { + reg = expr->data.symbol->value; + return reg; + } + + return NO_REG; +} + +/* get_mode - parse a general addressing mode. */ + +int get_mode(char *cp, char **endp, ADDR_MODE *mode) +{ + EX_TREE *value; + + mode->offset = NULL; + mode->rel = 0; + mode->type = 0; + + cp = skipwhite(cp); + + /* @ means "indirect," sets bit 3 */ + if(*cp == '@') + { + cp++; + mode->type |= 010; + } + + /* Immediate modes #imm and @#imm */ + if(*cp == '#') + { + cp++; + mode->type |= 027; + mode->offset = parse_expr(cp, 0); + if(endp) + *endp = mode->offset->cp; + return TRUE; + } + + /* Check for -(Rn) */ + + if(*cp == '-') + { + char *tcp = skipwhite(cp + 1); + if(*tcp++ == '(') + { + unsigned reg; + /* It's -(Rn) */ + value = parse_expr(tcp, 0); + reg = get_register(value); + if(reg == NO_REG || + (tcp = skipwhite(value->cp), *tcp++ != ')')) + { + free_tree(value); + return FALSE; + } + mode->type |= 040 | reg; + if(endp) + *endp = tcp; + free_tree(value); + return TRUE; + } + } + + /* Check for (Rn) */ + if(*cp == '(') + { + char *tcp; + unsigned reg; + value = parse_expr(cp + 1, 0); + reg = get_register(value); + + if(reg == NO_REG || + (tcp = skipwhite(value->cp), *tcp++ != ')')) + { + free_tree(value); + return FALSE; + } + + tcp = skipwhite(tcp); + if(*tcp == '+') + { + tcp++; /* It's (Rn)+ */ + if(endp) + *endp = tcp; + mode->type |= 020 | reg; + free_tree(value); + return TRUE; + } + + if(mode->type == 010) /* For @(Rn) there's an implied 0 offset */ + { + mode->offset = new_ex_lit(0); + mode->type |= 060 | reg; + free_tree(value); + if(endp) + *endp = tcp; + return TRUE; + } + + mode->type |= 010 | reg; /* Mode 10 is register indirect as + in (Rn) */ + free_tree(value); + if(endp) + *endp = tcp; + return TRUE; + } + + /* Modes with an offset */ + + mode->offset = parse_expr(cp, 0); + + cp = skipwhite(mode->offset->cp); + + if(*cp == '(') + { + unsigned reg; + /* indirect register plus offset */ + value = parse_expr(cp+1, 0); + reg = get_register(value); + if(reg == NO_REG || + (cp = skipwhite(value->cp), *cp++ != ')')) + { + free_tree(value); + return FALSE; /* Syntax error in addressing mode */ + } + + mode->type |= 060 | reg; + + free_tree(value); + + if(endp) + *endp = cp; + return TRUE; + } + + /* Plain old expression. */ + + if(endp) + *endp = cp; + + /* It might be a register, though. */ + if(mode->offset->type == EX_SYM) + { + SYMBOL *sym = mode->offset->data.symbol; + if(sym->section->type == REGISTER) + { + free_tree(mode->offset); + mode->offset = NULL; + mode->type |= sym->value; + return TRUE; + } + } + + /* It's either 067 (PC-relative) or 037 (absolute) mode, depending */ + /* on user option. */ + + if(mode->type & 010) /* Have already noted indirection? */ + { + mode->type |= 067; /* If so, then PC-relative is the only + option */ + mode->rel = 1; /* Note PC-relative */ + } + else if(enabl_ama) /* User asked for absolute adressing? */ + { + mode->type |= 037; /* Give it to him. */ + } + else + { + mode->type |= 067; /* PC-relative */ + mode->rel = 1; /* Note PC-relative */ + } + + return TRUE; +} + +/* + implicit_gbl is a self-recursive routine that adds undefined symbols + to the "implicit globals" symbol table. +*/ + +void implicit_gbl(EX_TREE *value) +{ + if(pass) + return; /* Only do this in first pass */ + + if(!enabl_gbl) + return; /* Option not enabled, don't do it. */ + + switch(value->type) + { + case EX_UNDEFINED_SYM: + { + SYMBOL *sym; + if(!(value->data.symbol->flags & LOCAL)) /* Unless it's a + local symbol, */ + { + sym = add_sym(value->data.symbol->label, + 0, GLOBAL, &absolute_section, &implicit_st); + } + } + break; + case EX_LIT: + case EX_SYM: + return; + case EX_ADD: + case EX_SUB: + case EX_MUL: + case EX_DIV: + case EX_AND: + case EX_OR: + implicit_gbl(value->data.child.right); + /* falls into... */ + case EX_COM: + case EX_NEG: + implicit_gbl(value->data.child.left); + break; + case EX_ERR: + if(value->data.child.left) + implicit_gbl(value->data.child.left); + break; + } +} + +/* Done between the first and second passes */ +/* Migrates the symbols from the "implicit" table into the main table. */ + +static void migrate_implicit(void) +{ + SYMBOL_ITER iter; + SYMBOL *isym, *sym; + + for(isym = first_sym(&implicit_st, &iter); + isym != NULL; + isym = next_sym(&implicit_st, &iter)) + { + sym = lookup_sym(isym->label, &symbol_st); + if(sym) + continue; // It's already in there. Great. + sym = add_sym(isym->label, isym->value, isym->flags, + isym->section, &symbol_st); + // Just one other thing - migrate the stmtno + sym->stmtno = isym->stmtno; + } +} + +static int express_sym_offset(EX_TREE *value, SYMBOL **sym, unsigned *offset) +{ + implicit_gbl(value); /* Translate tree's undefined syms + into global syms */ + + /* Internally relocatable symbols will have been summed down into + EX_TEMP_SYM's. */ + + if(value->type == EX_SYM || + value->type == EX_TEMP_SYM) + { + *sym = value->data.symbol; + *offset = 0; + return 1; + } + + /* What remains is external symbols. */ + + if(value->type == EX_ADD) + { + EX_TREE *left = value->data.child.left; + EX_TREE *right = value->data.child.right; + if((left->type != EX_SYM && + left->type != EX_UNDEFINED_SYM) || + right->type != EX_LIT) + return 0; /* Failed. */ + *sym = left->data.symbol; + *offset = right->data.lit; + return 1; + } + + if(value->type == EX_SUB) + { + EX_TREE *left = value->data.child.left; + EX_TREE *right = value->data.child.right; + if((left->type != EX_SYM && + left->type != EX_UNDEFINED_SYM) || + right->type != EX_LIT) + return 0; /* Failed. */ + *sym = left->data.symbol; + *offset = (unsigned)-(int)(right->data.lit); + return 1; + } + + return 0; +} + +/* + Translate an EX_TREE into a TEXT_COMPLEX suitable for encoding + into the object file. */ + +int complex_tree(TEXT_COMPLEX *tx, EX_TREE *tree) +{ + switch(tree->type) + { + case EX_LIT: + text_complex_lit(tx, tree->data.lit); + return 1; + + case EX_TEMP_SYM: + case EX_SYM: + { + SYMBOL *sym = tree->data.symbol; + if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) + { + text_complex_global(tx, sym->label); + } + else + { + text_complex_psect(tx, sym->section->sector, sym->value); + } + } + return 1; + + case EX_COM: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + text_complex_com(tx); + return 1; + + case EX_NEG: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + text_complex_neg(tx); + return 1; + + case EX_ADD: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + if(!complex_tree(tx, tree->data.child.right)) + return 0; + text_complex_add(tx); + return 1; + + case EX_SUB: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + if(!complex_tree(tx, tree->data.child.right)) + return 0; + text_complex_sub(tx); + return 1; + + case EX_MUL: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + if(!complex_tree(tx, tree->data.child.right)) + return 0; + text_complex_mul(tx); + return 1; + + case EX_DIV: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + if(!complex_tree(tx, tree->data.child.right)) + return 0; + text_complex_div(tx); + return 1; + + case EX_AND: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + if(!complex_tree(tx, tree->data.child.right)) + return 0; + text_complex_and(tx); + return 1; + + case EX_OR: + if(!complex_tree(tx, tree->data.child.left)) + return 0; + if(!complex_tree(tx, tree->data.child.right)) + return 0; + text_complex_or(tx); + return 1; + + default: + return 0; + } +} + +/* store a word which is represented by a complex expression. */ + +static void store_complex(STREAM *refstr, TEXT_RLD *tr, + int size, EX_TREE *value) +{ + TEXT_COMPLEX tx; + + change_dot(tr, size); /* About to store - update DOT */ + + implicit_gbl(value); /* Turn undefined symbols into globals */ + + text_complex_begin(&tx); /* Open complex expression */ + + if(!complex_tree(&tx, value)) /* Translate */ + { + report(refstr, "Invalid expression\n"); + store_word(refstr, tr, size, 0); + } + else + { + list_word(refstr, DOT, 0, size, "C"); + text_complex_commit(tr, &DOT, size, &tx, 0); + } +} + +/* store_complex_displaced is the same as store_complex but uses the + "displaced" RLD code */ + +static void store_complex_displaced(STREAM *refstr, TEXT_RLD *tr, + int size, + EX_TREE *value) +{ + TEXT_COMPLEX tx; + + change_dot(tr, size); + + implicit_gbl(value); /* Turn undefined symbols into globals */ + + text_complex_begin(&tx); + + if(!complex_tree(&tx, value)) + { + report(refstr, "Invalid expression\n"); + store_word(refstr, tr, size, 0); + } + else + { + list_word(refstr, DOT, 0, size, "C"); + text_complex_commit_displaced(tr, &DOT, size, &tx, 0); + } +} + +/* + mode_extension - writes the extension word required by an addressing + mode */ + +static void mode_extension(TEXT_RLD *tr, ADDR_MODE *mode, + STREAM *str) +{ + EX_TREE *value = mode->offset; + SYMBOL *sym; + unsigned offset; + + /* Also frees the mode. */ + + if(value == NULL) + { + free_addr_mode(mode); + return; + } + + if(value->type == EX_LIT) + { + if(mode->rel) /* PC-relative? */ + store_displaced_word(str, tr, 2, value->data.lit); + else + store_word(str, tr, 2, value->data.lit); /* Just a + known + value. */ + } + else if(express_sym_offset(value, &sym, &offset)) + { + if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) + { + /* Reference to a global symbol. */ + /* Global symbol plus offset */ + if(mode->rel) + store_global_displaced_offset_word(str, tr, + 2, offset, sym->label); + else + store_global_offset_word(str, tr, 2, offset, + sym->label); + } + else + { + /* Relative to non-external symbol. */ + if(current_pc->section == sym->section) + { + /* In the same section */ + if(mode->rel) + { + /* I can compute this myself. */ + store_word(str, tr, 2, + sym->value + offset - DOT - 2); + } + else + store_internal_word(str, tr, 2, sym->value+offset); + } + else + { + /* In a different section */ + if(mode->rel) + store_psect_displaced_offset_word(str, tr, 2, + sym->value+offset, sym->section->label); + else + store_psect_offset_word(str, tr, 2, + sym->value+offset, sym->section->label); + } + } + } + else + { + /* Complex relocation */ + + if(mode->rel) + store_complex_displaced(str, tr, 2, mode->offset); + else + store_complex(str, tr, 2, mode->offset); + } + + free_addr_mode(mode); +} + +/* eval_defined - take an EX_TREE and returns TRUE if the tree + represents "defined" symbols. */ + +int eval_defined(EX_TREE *value) +{ + switch(value->type) + { + case EX_LIT: + return 1; + case EX_SYM: + return 1; + case EX_UNDEFINED_SYM: + return 0; + case EX_AND: + return eval_defined(value->data.child.left) && + eval_defined(value->data.child.right); + case EX_OR: + return eval_defined(value->data.child.left) || + eval_defined(value->data.child.right); + default: + return 0; + } +} + +/* eval_undefined - take an EX_TREE and returns TRUE if it represents + "undefined" symbols. */ + +int eval_undefined(EX_TREE *value) +{ + switch(value->type) + { + case EX_UNDEFINED_SYM: + return 1; + case EX_SYM: + return 0; + case EX_AND: + return eval_undefined(value->data.child.left) && + eval_undefined(value->data.child.right); + case EX_OR: + return eval_undefined(value->data.child.left) || + eval_undefined(value->data.child.right); + default: + return 0; + } +} + +/* push_cond - a new conditional (.IF) block has been activated. Push + it's context. */ + +void push_cond(int ok, STREAM *str) +{ + last_cond++; + assert(last_cond < MAX_CONDS); + conds[last_cond].ok = ok; + conds[last_cond].file = memcheck(strdup(str->name)); + conds[last_cond].line = str->line; +} + +/* + pop_cond - pop stacked conditionals. */ + +void pop_cond(int to) +{ + while(last_cond > to) + { + free(conds[last_cond].file); + last_cond--; + } +} + +/* Parses a string from the input stream. */ +/* If not bracketed by <...> or ^/.../, then */ +/* the string is delimited by trailing comma or whitespace. */ +/* Allows nested <>'s */ + +char *getstring(char *cp, char **endp) +{ + int len; + int start; + char *str; + + if(!brackrange(cp, &start, &len, endp)) + { + start = 0; + len = strcspn(cp, " \t\n,;"); + if(endp) + *endp = cp + len; + } + + str = memcheck(malloc(len + 1)); + memcpy(str, cp + start, len); + str[len] = 0; + + return str; +} + +/* Get what would be the operation code from the line. */ +/* Used to find the ends of streams without evaluating them, like + finding the closing .ENDM on a macro definition */ + +SYMBOL *get_op(char *cp, char **endp) +{ + int local; + char *label; + SYMBOL *op; + + cp = skipwhite(cp); + if(EOL(*cp)) + return NULL; + + label = get_symbol(cp, &cp, &local); + if(label == NULL) + return NULL; /* No operation code. */ + + cp = skipwhite(cp); + if(*cp == ':') /* A label definition? */ + { + cp++; + if(*cp == ':') + cp++; /* Skip it */ + free(label); + label = get_symbol(cp, &cp, NULL); + if(label == NULL) + return NULL; + } + + op = lookup_sym(label, &system_st); + free(label); + + if(endp) + *endp = cp; + + return op; +} + +/* Here's where I pretend I'm a C++ compiler. :-/ */ + +/* *** derive a MACRO_STREAM from a BUFFER_STREAM with a few other args */ + +typedef struct macro_stream +{ + BUFFER_STREAM bstr; /* Base class: buffer stream */ + int nargs; /* Add number-of-macro-arguments */ + int cond; /* Add saved conditional stack */ +} MACRO_STREAM; + +/* macro_stream_delete is called when a macro expansion is + exhausted. The unique behavior is to unwind any stacked + conditionals. This allows a nested .MEXIT to work. */ + +void macro_stream_delete(STREAM *str) +{ + MACRO_STREAM *mstr = (MACRO_STREAM *)str; + pop_cond(mstr->cond); + buffer_stream_delete(str); +} + +STREAM_VTBL macro_stream_vtbl = { macro_stream_delete, + buffer_stream_gets, + buffer_stream_rewind }; + +STREAM *new_macro_stream(STREAM *refstr, BUFFER *buf, MACRO *mac, + ARG *args) +{ + MACRO_STREAM *mstr = memcheck(malloc(sizeof(MACRO_STREAM))); + + { + char *name = memcheck(malloc(strlen(refstr->name) + 32)); + sprintf(name, "%s:%d->%s", refstr->name, refstr->line, + mac->sym.label); + buffer_stream_construct(&mstr->bstr, buf, name); + free(name); + } + + mstr->bstr.stream.vtbl = ¯o_stream_vtbl; + /* Count the args and save their number */ + for(mstr->nargs = 0; args; args = args->next, mstr->nargs++) + ; + mstr->cond = last_cond; + return &mstr->bstr.stream; +} + +/* read_body fetches the body of .MACRO, .REPT, .IRP, or .IRPC into a + BUFFER. */ + +void read_body(STACK *stack, BUFFER *gb, char *name, + int called) +{ + int nest; + + /* Read the stream in until the end marker is hit */ + + /* Note: "called" says that this body is being pulled from a macro + library, and so under no circumstance should it be listed. */ + + nest = 1; + for(;;) + { + SYMBOL *op; + char *nextline; + char *cp; + + nextline = stack_gets(stack); /* Now read the line */ + if(nextline == NULL) /* End of file. */ + { + report(stack->top, "Macro body not closed\n"); + break; + } + + if(!called && (list_level - 1 + list_md) > 0) + { + list_flush(); + list_source(stack->top, nextline); + } + + op = get_op(nextline, &cp); + + if(op == NULL) /* Not a pseudo-op */ + { + buffer_append_line(gb, nextline); + continue; + } + if(op->section->type == PSEUDO) + { + if(op->value == P_MACRO || + op->value == P_REPT || + op->value == P_IRP || + op->value == P_IRPC) + nest++; + + if(op->value == P_ENDM || + op->value == P_ENDR) + { + nest--; + /* If there's a name on the .ENDM, then */ + /* close the body early if it matches the definition */ + if(name && op->value == P_ENDM) + { + cp = skipwhite(cp); + if(!EOL(*cp)) + { + char *label = get_symbol(cp, &cp, NULL); + if(label) + { + if(strcmp(label, name) == 0) + nest = 0; /* End of macro body. */ + free(label); + } + } + } + } + + if(nest == 0) + return; /* All done. */ + } + + buffer_append_line(gb, nextline); + } +} + +/* Diagnostic: dumpmacro dumps a macro definition to stdout. + I used this for debugging; it's not called at all right now, but + I hate to delete good code. */ + +void dumpmacro(MACRO *mac, FILE *fp) +{ + ARG *arg; + + fprintf(fp, ".MACRO %s ", mac->sym.label); + + for(arg = mac->args; arg != NULL; arg = arg->next) + { + fputs(arg->label, fp); + if(arg->value) + { + fputc('=', fp); + fputs(arg->value, fp); + } + fputc(' ', fp); + } + fputc('\n', fp); + + fputs(mac->text->buffer, fp); + + fputs(".ENDM\n", fp); +} + +/* defmacro - define a macro. */ +/* Also used by .MCALL to pull macro definitions from macro libraries */ + +MACRO *defmacro(char *cp, STACK *stack, int called) +{ + MACRO *mac; + ARG *arg, **argtail; + char *label; + + cp = skipwhite(cp); + label = get_symbol(cp, &cp, NULL); + if(label == NULL) + { + report(stack->top, "Invalid macro definition\n"); + return NULL; + } + + /* Allow redefinition of a macro; new definition replaces the old. */ + mac = (MACRO *)lookup_sym(label, ¯o_st); + if(mac) + { + /* Remove from the symbol table... */ + remove_sym(&mac->sym, ¯o_st); + free_macro(mac); + } + + mac = new_macro(label); + + add_table(&mac->sym, ¯o_st); + + argtail = &mac->args; + cp = skipdelim(cp); + + while(!EOL(*cp)) + { + arg = new_arg(); + if(arg->locsym = (*cp == '?')) /* special argument flag? */ + cp++; + arg->label = get_symbol(cp, &cp, NULL); + if(arg->label == NULL) + { + /* It turns out that I have code which is badly formatted + but which MACRO.SAV assembles. Sigh. */ + /* So, just quit defining arguments. */ + break; +#if 0 + report(str, "Illegal macro argument\n"); + remove_sym(&mac->sym, ¯o_st); + free_macro(mac); + return NULL; +#endif + } + + cp = skipwhite(cp); + if(*cp == '=') + { + /* Default substitution given */ + arg->value = getstring(cp+1, &cp); + if(arg->value == NULL) + { + report(stack->top, "Illegal macro argument\n"); + remove_sym(&mac->sym, ¯o_st); + free_macro(mac); + return NULL; + } + } + + /* Append to list of arguments */ + arg->next = NULL; + *argtail = arg; + argtail = &arg->next; + + cp = skipdelim(cp); + } + + /* Read the stream in until the end marker is hit */ + { + BUFFER *gb; + int levelmod = 0; + + gb = new_buffer(); + + if(!called && !list_md) + { + list_level--; + levelmod = 1; + } + + read_body(stack, gb, mac->sym.label, called); + + list_level += levelmod; + + if(mac->text != NULL) /* Discard old macro body */ + buffer_free(mac->text); + + mac->text = gb; + } + + return mac; +} + +/* find_arg - looks for an arg with the given name in the given + argument list */ + +static ARG *find_arg(ARG *arg, char *name) +{ + for(; arg != NULL; arg = arg->next) + { + if(strcmp(arg->label, name) == 0) + return arg; + } + + return NULL; +} + +/* subst_args - given a BUFFER and a list of args, generate a new + BUFFER with argument replacement having taken place. */ + +BUFFER *subst_args(BUFFER *text, ARG *args) +{ + char *in; + char *begin; + BUFFER *gb; + char *label; + ARG *arg; + + gb = new_buffer(); + + /* Blindly look for argument symbols in the input. */ + /* Don't worry about quotes or comments. */ + + for(begin = in = text->buffer; in < text->buffer + text->length;) + { + char *next; + + if(issym(*in)) + { + label = get_symbol(in, &next, NULL); + if(label) + { + if(arg = find_arg(args, label)) + { + /* An apostrophy may appear before or after the symbol. */ + /* In either case, remove it from the expansion. */ + + if(in > begin && in[-1] == '\'') + in --; /* Don't copy it. */ + if(*next == '\'') + next++; + + /* Copy prior characters */ + buffer_appendn(gb, begin, in-begin); + /* Copy replacement string */ + buffer_append_line(gb, arg->value); + in = begin = next; + --in; /* prepare for subsequent increment */ + } + free(label); + in = next; + } + else + in++; + } + else + in++; + } + + /* Append the rest of the text */ + buffer_appendn(gb, begin, in - begin); + + return gb; /* Done. */ +} + +/* eval_arg - the language allows an argument expression to be given + as "\expression" which means, evaluate the expression and + substitute the numeric value in the current radix. */ + +void eval_arg(STREAM *refstr, ARG *arg) +{ + /* Check for value substitution */ + + if(arg->value[0] == '\\') + { + EX_TREE *value = parse_expr(arg->value+1, 0); + unsigned word = 0; + char temp[10]; + if(value->type != EX_LIT) + { + report(refstr, "Constant value required\n"); + } + else + word = value->data.lit; + + free_tree(value); + + /* printf can't do base 2. */ + my_ultoa(word & 0177777, temp, radix); + free(arg->value); + arg->value = memcheck(strdup(temp)); + } + +} + +/* expandmacro - return a STREAM containing the expansion of a macro */ + +STREAM *expandmacro(STREAM *refstr, MACRO *mac, char *cp) +{ + ARG *arg, *args, *macarg; + char *label; + STREAM *str; + BUFFER *buf; + + args = NULL; + arg = NULL; + + /* Parse the arguments */ + + while(!EOL(*cp)) + { + char *nextcp; + /* Check for named argument */ + label = get_symbol(cp, &nextcp, NULL); + if(label && + (nextcp = skipwhite(nextcp), *nextcp == '=') && + (macarg = find_arg(mac->args, label))) + { + /* Check if I've already got a value for it */ + if(find_arg(args, label) != NULL) + { + report(refstr, "Duplicate submission of keyword " + "argument %s\n", label); + free(label); + free_args(args); + return NULL; + } + + arg = new_arg(); + arg->label = label; + nextcp = skipwhite(nextcp+1); + arg->value = getstring(nextcp, &nextcp); + } + else + { + if(label) + free(label); + + /* Find correct positional argument */ + + for(macarg = mac->args; macarg != NULL; macarg = macarg->next) + { + if(find_arg(args, macarg->label) == NULL) + break; /* This is the next positional arg */ + } + + if(macarg == NULL) + break; /* Don't pick up any more arguments. */ + + arg = new_arg(); + arg->label = memcheck(strdup(macarg->label)); /* Copy the name */ + arg->value = getstring(cp, &nextcp); + } + + arg->next = args; + args = arg; + + eval_arg(refstr, arg); /* Check for expression evaluation */ + + cp = skipdelim(nextcp); + } + + /* Now go back and fill in defaults */ + + { + int locsym; + if(last_lsb != lsb) + locsym = last_locsym = 32768; + else + locsym = last_locsym; + last_lsb = lsb; + + for(macarg = mac->args; macarg != NULL; macarg = macarg->next) + { + arg = find_arg(args, macarg->label); + if(arg == NULL) + { + arg = new_arg(); + arg->label = memcheck(strdup(macarg->label)); + if(macarg->locsym) + { + char temp[32]; + /* Here's where we generate local labels */ + sprintf(temp, "%d$", locsym++); + arg->value = memcheck(strdup(temp)); + } + else if(macarg->value) + { + arg->value = memcheck(strdup(macarg->value)); + } + else + arg->value = memcheck(strdup("")); + + arg->next = args; + args = arg; + } + } + + last_locsym = locsym; + } + + buf = subst_args(mac->text, args); + + str = new_macro_stream(refstr, buf, mac, args); + + free_args(args); + buffer_free(buf); + + return str; +} + +/* *** implement REPT_STREAM */ + +typedef struct rept_stream +{ + BUFFER_STREAM bstr; + int count; /* The current repeat countdown */ + int savecond; /* conditional stack level at time of + expansion */ +} REPT_STREAM; + +/* rept_stream_gets gets a line from a repeat stream. At the end of + each count, the coutdown is decreated and the stream is reset to + it's beginning. */ + +char *rept_stream_gets(STREAM *str) +{ + REPT_STREAM *rstr = (REPT_STREAM *)str; + char *cp; + + for(;;) + { + if((cp = buffer_stream_gets(str)) != NULL) + return cp; + + if(--rstr->count <= 0) + return NULL; + + buffer_stream_rewind(str); + } +} + +/* rept_stream_delete unwinds nested conditionals like .MEXIT does. */ + +void rept_stream_delete(STREAM *str) +{ + REPT_STREAM *rstr = (REPT_STREAM *)str; + pop_cond(rstr->savecond); /* complete unterminated + conditionals */ + buffer_stream_delete(&rstr->bstr.stream); +} + +/* The VTBL */ + +STREAM_VTBL rept_stream_vtbl = { rept_stream_delete, + rept_stream_gets, + buffer_stream_rewind }; + +/* expand_rept is called when a .REPT is encountered in the input. */ + +STREAM *expand_rept(STACK *stack, char *cp) +{ + EX_TREE *value; + BUFFER *gb; + REPT_STREAM *rstr; + int levelmod; + + value = parse_expr(cp, 0); + if(value->type != EX_LIT) + { + report(stack->top, ".REPT value must be constant\n"); + free_tree(value); + return NULL; + } + + gb = new_buffer(); + + levelmod = 0; + if(!list_md) + { + list_level--; + levelmod = 1; + } + + read_body(stack, gb, NULL, FALSE); + + list_level += levelmod; + + rstr = memcheck(malloc(sizeof(REPT_STREAM))); + { + char *name = memcheck(malloc(strlen(stack->top->name) + 32)); + sprintf(name, "%s:%d->.REPT", stack->top->name, stack->top->line); + buffer_stream_construct(&rstr->bstr, gb, name); + free(name); + } + + rstr->count = value->data.lit; + rstr->bstr.stream.vtbl = &rept_stream_vtbl; + rstr->savecond = last_cond; + + buffer_free(gb); + free_tree(value); + + return &rstr->bstr.stream; +} + +/* *** implement IRP_STREAM */ + +typedef struct irp_stream +{ + BUFFER_STREAM bstr; + char *label; /* The substitution label */ + char *items; /* The substitution items (in source code + format) */ + int offset; /* Current offset into "items" */ + BUFFER *body; /* Original body */ + int savecond; /* Saved conditional level */ +} IRP_STREAM; + +/* irp_stream_gets expands the IRP as the stream is read. */ +/* Each time an iteration is exhausted, the next iteration is + generated. */ + +char *irp_stream_gets(STREAM *str) +{ + IRP_STREAM *istr = (IRP_STREAM *)str; + char *cp; + BUFFER *buf; + ARG *arg; + + for(;;) + { + if((cp = buffer_stream_gets(str)) != NULL) + return cp; + + cp = istr->items + istr->offset; + + if(!*cp) + return NULL; /* No more items. EOF. */ + + arg = new_arg(); + arg->next = NULL; + arg->locsym = 0; + arg->label = istr->label; + arg->value = getstring(cp, &cp); + cp = skipdelim(cp); + istr->offset = cp - istr->items; + + eval_arg(str, arg); + buf = subst_args(istr->body, arg); + + free(arg->value); + free(arg); + buffer_stream_set_buffer(&istr->bstr, buf); + buffer_free(buf); + } +} + +/* irp_stream_delete - also pops the conditional stack */ + +void irp_stream_delete(STREAM *str) +{ + IRP_STREAM *istr = (IRP_STREAM *)str; + + pop_cond(istr->savecond); /* complete unterminated + conditionals */ + + buffer_free(istr->body); + free(istr->items); + free(istr->label); + buffer_stream_delete(str); +} + +STREAM_VTBL irp_stream_vtbl = { irp_stream_delete, irp_stream_gets, + buffer_stream_rewind }; + +/* expand_irp is called when a .IRP is encountered in the input. */ + +STREAM *expand_irp(STACK *stack, char *cp) +{ + char *label, *items; + BUFFER *gb; + int levelmod = 0; + IRP_STREAM *str; + + label = get_symbol(cp, &cp, NULL); + if(!label) + { + report(stack->top, "Illegal .IRP syntax\n"); + return NULL; + } + + cp = skipdelim(cp); + + items = getstring(cp, &cp); + if(!items) + { + report(stack->top, "Illegal .IRP syntax\n"); + free(label); + return NULL; + } + + gb = new_buffer(); + + levelmod = 0; + if(!list_md) + { + list_level--; + levelmod++; + } + + read_body(stack, gb, NULL, FALSE); + + list_level += levelmod; + + str = memcheck(malloc(sizeof(IRP_STREAM))); + { + char *name = memcheck(malloc(strlen(stack->top->name) + 32)); + sprintf(name, "%s:%d->.IRP", stack->top->name, stack->top->line); + buffer_stream_construct(&str->bstr, NULL, name); + free(name); + } + + str->bstr.stream.vtbl = &irp_stream_vtbl; + + str->body = gb; + str->items = items; + str->offset = 0; + str->label = label; + str->savecond = last_cond; + + return &str->bstr.stream; +} + +/* *** implement IRPC_STREAM */ + +typedef struct irpc_stream +{ + BUFFER_STREAM bstr; + char *label; /* The substitution label */ + char *items; /* The substitution items (in source code + format) */ + int offset; /* Current offset in "items" */ + BUFFER *body; /* Original body */ + int savecond; /* conditional stack at invocation */ +} IRPC_STREAM; + +/* irpc_stream_gets - same comments apply as with irp_stream_gets, but + the substitution is character-by-character */ + +char *irpc_stream_gets(STREAM *str) +{ + IRPC_STREAM *istr = (IRPC_STREAM *)str; + char *cp; + BUFFER *buf; + ARG *arg; + + for(;;) + { + if((cp = buffer_stream_gets(str)) != NULL) + return cp; + + cp = istr->items + istr->offset; + + if(!*cp) + return NULL; /* No more items. EOF. */ + + arg = new_arg(); + arg->next = NULL; + arg->locsym = 0; + arg->label = istr->label; + arg->value = memcheck(malloc(2)); + arg->value[0] = *cp++; + arg->value[1] = 0; + istr->offset = cp - istr->items; + + buf = subst_args(istr->body, arg); + + free(arg->value); + free(arg); + buffer_stream_set_buffer(&istr->bstr, buf); + buffer_free(buf); + } +} + +/* irpc_stream_delete - also pops contidionals */ + +void irpc_stream_delete(STREAM *str) +{ + IRPC_STREAM *istr = (IRPC_STREAM *)str; + pop_cond(istr->savecond); /* complete unterminated + conditionals */ + buffer_free(istr->body); + free(istr->items); + free(istr->label); + buffer_stream_delete(str); +} + +STREAM_VTBL irpc_stream_vtbl = { irpc_stream_delete, + irpc_stream_gets, + buffer_stream_rewind }; + +/* expand_irpc - called when .IRPC is encountered in the input */ + +STREAM *expand_irpc(STACK *stack, char *cp) +{ + char *label, *items; + BUFFER *gb; + int levelmod = 0; + IRPC_STREAM *str; + + label = get_symbol(cp, &cp, NULL); + if(!label) + { + report(stack->top, "Illegal .IRPC syntax\n"); + return NULL; + } + + cp = skipdelim(cp); + + items = getstring(cp, &cp); + if(!items) + { + report(stack->top, "Illegal .IRPC syntax\n"); + free(label); + return NULL; + } + + gb = new_buffer(); + + levelmod = 0; + if(!list_md) + { + list_level--; + levelmod++; + } + + read_body(stack, gb, NULL, FALSE); + + list_level += levelmod; + + str = memcheck(malloc(sizeof(IRPC_STREAM))); + { + char *name = memcheck(malloc(strlen(stack->top->name) + 32)); + sprintf(name, "%s:%d->.IRPC", stack->top->name, stack->top->line); + buffer_stream_construct(&str->bstr, NULL, name); + free(name); + } + + str->bstr.stream.vtbl = &irpc_stream_vtbl; + str->body = gb; + str->items = items; + str->offset = 0; + str->label = label; + str->savecond = last_cond; + + return &str->bstr.stream; +} + +/* go_section - sets current_pc to a new program section */ + +void go_section(TEXT_RLD *tr, SECTION *sect) +{ + if(current_pc->section == sect) + return; /* This is too easy */ + + /* save current PC value for old section */ + current_pc->section->pc = DOT; + + /* Set current section and PC value */ + current_pc->section = sect; + DOT = sect->pc; +} + +/* + store_value - used to store a value represented by an expression + tree into the object file. Used by do_word and .ASCII/.ASCIZ. +*/ + +static void store_value(STACK *stack, TEXT_RLD *tr, + int size, EX_TREE *value) +{ + SYMBOL *sym; + unsigned offset; + + implicit_gbl(value); /* turn undefined symbols into globals */ + + if(value->type == EX_LIT) + { + store_word(stack->top, tr, size, value->data.lit); + } + else if(!express_sym_offset(value, &sym, &offset)) + { + store_complex(stack->top, tr, size, value); + } + else + { + if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) + { + store_global_offset_word(stack->top, tr, size, + sym->value+offset, + sym->label); + } + else if(sym->section != current_pc->section) + { + store_psect_offset_word(stack->top, tr, size, + sym->value+offset, + sym->section->label); + } + else + { + store_internal_word(stack->top, tr, size, + sym->value+offset); + } + } +} + +/* do_word - used by .WORD, .BYTE, and implied .WORD. */ + +static int do_word(STACK *stack, TEXT_RLD *tr, char *cp, int size) +{ + + if(size == 2 && (DOT & 1)) + { + report(stack->top, ".WORD on odd boundary\n"); + store_word(stack->top, tr, 1, 0); /* Align it */ + } + + do + { + EX_TREE *value = parse_expr(cp, 0); + + store_value(stack, tr, size, value); + + cp = skipdelim(value->cp); + + free_tree(value); + + } while(cp = skipdelim(cp), !EOL(*cp)); + + return 1; +} + +/* + check_branch - check branch distance. +*/ + +static int check_branch(STACK *stack, unsigned offset, int min, int max) +{ + int s_offset; + /* Sign-extend */ + if(offset & 0100000) + s_offset = offset | ~0177777; + else + s_offset = offset & 077777; + if(s_offset > max || s_offset < min) + { + char temp[16]; + /* printf can't do signed octal. */ + my_ltoa(s_offset, temp, 8); + report(stack->top, + "Branch target out of range (distance=%s)\n", + temp); + return 0; + } + return 1; +} + +/* assemble - read a line from the input stack, assemble it. */ + +/* This function is way way too large, because I just coded most of + the operation code and pseudo-op handling right in line. */ + +int assemble(STACK *stack, TEXT_RLD *tr) +{ + char *cp; /* Parse character pointer */ + char *opcp; /* Points to operation mnemonic text */ + char *ncp; /* "next" cp */ + char *label; /* A label */ + char *line; /* The whole line */ + SYMBOL *op; /* The operation SYMBOL */ + int local; /* Whether a label is a local label or + not */ + + line = stack_gets(stack); + if(line == NULL) + return -1; /* Return code for EOF. */ + + cp = line; + + /* Frankly, I don't need to keep "line." But I found it quite + handy during debugging, to see what the whole operation was, + when I'm down to parsing the second operand and things aren't + going right. */ + + stmtno++; /* Increment statement number */ + + list_source(stack->top, line); /* List source */ + + if(suppressed) + { + /* Assembly is suppressed by unsatisfoed conditional. Look + for ending and enabling statements. */ + + op = get_op(cp, &cp); /* Look at operation code */ + + /* FIXME: this code will blindly look into .REM commentary and + find operation codes. Incidentally, so will read_body. */ + + if(op == NULL) + return 1; /* Not found. Don't care. */ + if(op->section->type != PSEUDO) + return 1; /* Not a pseudo-op. */ + switch(op->value) + { + case P_IF: + case P_IFDF: + suppressed++; /* Nested. Suppressed. */ + break; + case P_IFTF: + if(suppressed == 1) /* Reduce suppression from 1 to 0. */ + suppressed = 0; + break; + case P_IFF: + if(suppressed == 1) /* Can reduce suppression from 1 to 0. */ + { + if(!conds[last_cond].ok) + suppressed = 0; + } + break; + case P_IFT: + if(suppressed == 1) /* Can reduce suppression from 1 to 0. */ + { + if(conds[last_cond].ok) + suppressed = 0; + } + break; + case P_ENDC: + suppressed--; /* Un-nested. */ + if(suppressed == 0) + pop_cond(last_cond-1); /* Re-enabled. */ + break; + } + return 1; + } + + /* The line may begin with "label:[:]" */ + + opcp = cp; + if((label = get_symbol(cp, &ncp, &local)) != NULL) + { + int flag = PERMANENT|DEFINITION|local; + SYMBOL *sym; + + ncp = skipwhite(ncp); + if(*ncp == ':') /* Colon, for symbol definition? */ + { + ncp++; + /* maybe it's a global definition */ + if(*ncp == ':') + { + flag |= GLOBAL; /* Yes, include global flag */ + ncp++; + } + + sym = add_sym(label, DOT, flag, current_pc->section, &symbol_st); + cp = ncp; + + if(sym == NULL) + report(stack->top, "Illegal symbol definition %s\n", label); + + free(label); + + /* See if local symbol block should be incremented */ + if(!enabl_lsb && !local) + lsb++; + + cp = skipwhite(ncp); + opcp = cp; + label = get_symbol(cp, &ncp, NULL); /* Now, get what follows */ + } + } + + /* PSEUDO P_IIF jumps here. */ +reassemble: + cp = skipwhite(cp); + + if(EOL(*cp)) + return 1; /* It's commentary. All done. */ + + if(label) /* Something looks like a label. */ + { + /* detect assignment */ + + ncp = skipwhite(ncp); /* The pointer to the text that + follows the symbol */ + + if(*ncp == '=') + { + unsigned flags; + EX_TREE *value; + SYMBOL *sym; + + cp = ncp; + + /* Symbol assignment. */ + + flags = DEFINITION|local; + cp++; + if(*cp == '=') + { + flags |= GLOBAL; /* Global definition */ + cp++; + } + if(*cp == ':') + { + flags |= PERMANENT; + cp++; + } + + cp = skipwhite(cp); + + value = parse_expr(cp, 0); + + /* Special code: if the symbol is the program counter, + this is harder. */ + + if(strcmp(label, ".") == 0) + { + if(current_pc->section->flags & PSECT_REL) + { + SYMBOL *sym; + unsigned offset; + + /* Express the given expression as a symbol and an + offset. The symbol must not be global, the + section must = current. */ + + if(!express_sym_offset(value, &sym, &offset)) + { + report(stack->top, "Illegal ORG\n"); + } + else if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) + { + report(stack->top, + "Can't ORG to external location\n"); + } + else if(sym->flags & UNDEFINED) + { + report(stack->top, "Can't ORG to undefined sym\n"); + } + else if(sym->section != current_pc->section) + { + report(stack->top, + "Can't ORG to alternate section " + "(use PSECT)\n"); + } + else + { + DOT = sym->value + offset; + list_value(stack->top, DOT); + change_dot(tr, 0); + } + } + else + { + /* If the current section is absolute, the value + must be a literal */ + if(value->type != EX_LIT) + { + report(stack->top, + "Can't ORG to non-absolute location\n"); + free_tree(value); + free(label); + return 0; + } + DOT = value->data.lit; + list_value(stack->top, DOT); + change_dot(tr, 0); + } + free_tree(value); + free(label); + return 1; + } + + /* regular symbols */ + if(value->type == EX_LIT) + { + sym = add_sym(label, value->data.lit, + flags, &absolute_section, &symbol_st); + } + else if(value->type == EX_SYM || + value->type == EX_TEMP_SYM) + { + sym = add_sym(label, value->data.symbol->value, + flags, value->data.symbol->section, &symbol_st); + } + else + { + report(stack->top, "Complex expression cannot be assigned " + "to a symbol\n"); + + if(!pass) + { + /* This may work better in pass 2 - something in + RT-11 monitor needs the symbol to apear to be + defined even if I can't resolve it's value. */ + sym = add_sym(label, 0, UNDEFINED, + &absolute_section, &symbol_st); + } + else + sym = NULL; + } + + if(sym != NULL) + list_value(stack->top, sym->value); + + free_tree(value); + free(label); + + return sym != NULL; + } + + /* Try to resolve macro */ + + op = lookup_sym(label, ¯o_st); + if(op && + op->stmtno < stmtno) + { + STREAM *macstr; + + free(label); + + macstr = expandmacro(stack->top, (MACRO *)op, ncp); + + stack_push(stack, macstr); /* Push macro expansion + onto input stream */ + + return 1; + } + + /* Try to resolve instruction or pseudo */ + op = lookup_sym(label, &system_st); + if(op) + { + cp = ncp; + + free(label); /* Don't need this hanging around anymore */ + + switch(op->section->type) + { + case PSEUDO: + switch(op->value) + { + case P_ENDR: + case P_ENDM: + case P_SBTTL: + case P_LIST: + case P_NLIST: + case P_PRINT: + return 1; /* Accepted, ignored. (An obvious + need: get assembly listing + controls working. ) */ + + case P_IDENT: + { + char endc[6]; + int len; + + cp = skipwhite(cp); + endc[0] = *cp++; + endc[1] = '\n'; + endc[2] = 0; + len = strcspn(cp, endc); + if(len > 6) + len = 6; + + if(ident) /* An existing ident? */ + free(ident); /* Discard it. */ + + ident = memcheck(malloc(len + 1)); + memcpy(ident, cp, len); + ident[len] = 0; + upcase(ident); + + return 1; + } + + case P_RADIX: + { + int old_radix = radix; + radix = strtoul(cp, &cp, 10); + if(radix != 8 && radix != 10 && radix != 16 && + radix != 2) + { + radix = old_radix; + report(stack->top, "Illegal radix\n"); + return 0; + } + return 1; + } + + case P_FLT4: + case P_FLT2: + { + int ok = 1; + + while(!EOL(*cp)) + { + unsigned flt[4]; + if(parse_float(cp, &cp, + (op->value == P_FLT4 ? 4 : 2), + flt)) + { + /* Store the word values */ + store_word(stack->top, tr, 2, flt[0]); + store_word(stack->top, tr, 2, flt[1]); + if(op->value == P_FLT4) + { + store_word(stack->top, tr, + 2, flt[2]); + store_word(stack->top, tr, + 2, flt[3]); + } + } + else + { + report(stack->top, + "Bad floating point format\n"); + ok = 0; + } + cp = skipdelim(cp); + } + return ok; + } + + case P_ERROR: + report(stack->top, "%.*s\n", strcspn(cp, "\n"), cp); + return 0; + + case P_SAVE: + sect_sp++; + sect_stack[sect_sp] = current_pc->section; + return 1; + + case P_RESTORE: + if(sect_sp < 0) + { + report(stack->top, "No saved section for .RESTORE\n"); + return 0; + } + else + { + go_section(tr, sect_stack[sect_sp]); + sect_sp++; + } + return 1; + + case P_NARG: + { + STREAM *str; + MACRO_STREAM *mstr; + int local; + + label = get_symbol(cp, &cp, &local); + + if(label == NULL) + { + report(stack->top, "Bad .NARG syntax\n"); + return 0; + } + + /* Walk up the stream stack to find the + topmost macro stream */ + for(str = stack->top; + str != NULL && + str->vtbl != ¯o_stream_vtbl; + str = str->next) + ; + + if(!str) + { + report(str, ".NARG not within macro expansion\n"); + free(label); + return 0; + } + + mstr = (MACRO_STREAM *)str; + + add_sym(label, mstr->nargs, DEFINITION|local, + &absolute_section, &symbol_st); + free(label); + return 1; + } + + case P_NCHR: + { + char *string; + int local; + label = get_symbol(cp, &cp, &local); + + if(label == NULL) + { + report(stack->top, "Bad .NCHR syntax\n"); + return 0; + } + + cp = skipdelim(cp); + + string = getstring(cp, &cp); + + add_sym(label, strlen(string), + DEFINITION|local, + &absolute_section, &symbol_st); + free(label); + free(string); + return 1; + } + + case P_NTYPE: + { + ADDR_MODE mode; + int local; + + label = get_symbol(cp, &cp, &local); + if(label == NULL) + { + report(stack->top, "Bad .NTYPE syntax\n"); + return 0; + } + + cp = skipdelim(cp); + + if(!get_mode(cp, &cp, &mode)) + { + report(stack->top, + "Bad .NTYPE addressing mode\n"); + free(label); + return 0; + } + + add_sym(label, mode.type, DEFINITION|local, + &absolute_section, &symbol_st); + free_addr_mode(&mode); + free(label); + + return 1; + } + + case P_INCLU: + { + char *name = getstring(cp, &cp); + STREAM *incl; + + if(name == NULL) + { + report(stack->top, "Bad .INCLUDE file name\n"); + return 0; + } + + incl = new_file_stream(name); + if(incl == NULL) + { + report(stack->top, + "Unable to open .INCLUDE file %s\n", name); + free(name); + return 0; + } + + free(name); + + stack_push(stack, incl); + + return 1; + } + + case P_REM: + { + char quote[4]; + /* Read and discard lines until one with a + closing quote */ + + cp = skipwhite(cp); + quote[0] = *cp++; + quote[1] = '\n'; + quote[2] = 0; + + for(;;) + { + cp += strcspn(cp, quote); + if(*cp == quote[0]) + break; /* Found closing quote */ + cp = stack_gets(stack); /* Read next input line */ + if(cp == NULL) + break; /* EOF */ + } + } + return 1; + + case P_IRP: + { + STREAM *str = expand_irp(stack, cp); + if(str) + stack_push(stack, str); + return str != NULL; + } + + case P_IRPC: + { + STREAM *str = expand_irpc(stack, cp); + if(str) + stack_push(stack, str); + return str != NULL; + } + + case P_MCALL: + { + STREAM *macstr; + BUFFER *macbuf; + char *maccp; + int saveline; + MACRO *mac; + int i; + char macfile[FILENAME_MAX]; + char hitfile[FILENAME_MAX]; + + for(;;) + { + cp = skipdelim(cp); + + if(EOL(*cp)) + return 1; + + label = get_symbol(cp, &cp, NULL); + if(!label) + { + report(stack->top, "Illegal .MCALL format\n"); + return 0; + } + + /* See if that macro's already defined */ + if(lookup_sym(label, ¯o_st)) + { + free(label); /* Macro already + registered. No + prob. */ + cp = skipdelim(cp); + continue; + } + + /* Find the macro in the list of included + macro libraries */ + macbuf = NULL; + for(i = 0; i < nr_mlbs; i++) + { + if((macbuf = mlb_entry(mlbs[i], + label)) != NULL) + break; + } + if(macbuf != NULL) + { + macstr = new_buffer_stream(macbuf, label); + buffer_free(macbuf); + } + else + { + strncpy(macfile, label, sizeof(macfile)); + strncat(macfile, ".MAC", sizeof(macfile) - strlen(macfile)); + my_searchenv(macfile, "MCALL", hitfile, sizeof(hitfile)); + if(hitfile[0]) + macstr = new_file_stream(hitfile); + } + + if(macstr != NULL) + { + for(;;) + { + char *mlabel; + maccp = macstr->vtbl->gets(macstr); + if(maccp == NULL) + break; + mlabel = get_symbol(maccp, &maccp, NULL); + if(mlabel == NULL) + continue; + op = lookup_sym(mlabel, &system_st); + free(mlabel); + if(op == NULL) + continue; + if(op->value == P_MACRO) + break; + } + + if(maccp != NULL) + { + STACK macstack = { macstr }; + int savelist = list_level; + saveline = stmtno; + list_level = -1; + mac = defmacro(maccp, &macstack, TRUE); + if(mac == NULL) + { + report(stack->top, + "Failed to define macro " + "called %s\n", + label); + } + + stmtno = saveline; + list_level = savelist; + } + + macstr->vtbl->delete(macstr); + } + else + report(stack->top, + "MACRO %s not found\n", label); + + free(label); + } + } + return 1; + + case P_MACRO: + { + MACRO *mac = defmacro(cp, stack, FALSE); + return mac != NULL; + } + + case P_MEXIT: + { + STREAM *macstr; + + /* Pop a stream from the input. */ + /* It must be the first stream, and it must be */ + /* a macro, rept, irp, or irpc. */ + macstr = stack->top; + if(macstr->vtbl != ¯o_stream_vtbl && + macstr->vtbl != &rept_stream_vtbl && + macstr->vtbl != &irp_stream_vtbl && + macstr->vtbl != &irpc_stream_vtbl) + { + report(stack->top, ".MEXIT not within a macro\n"); + return 0; + } + + /* and finally, pop the macro */ + stack_pop(stack); + + return 1; + } + + case P_REPT: + { + STREAM *reptstr = expand_rept(stack, cp); + if(reptstr) + stack_push(stack, reptstr); + return reptstr != NULL; + } + + case P_ENABL: + + /* FIXME - add all the rest of the options. */ + while(!EOL(*cp)) + { + label = get_symbol(cp, &cp, NULL); + if(strcmp(label, "AMA") == 0) + enabl_ama = 1; + else if(strcmp(label, "LSB") == 0) + { + enabl_lsb = 1; + lsb++; + } + else if(strcmp(label, "GBL") == 0) + enabl_gbl = 1; + free(label); + cp = skipdelim(cp); + } + return 1; + + case P_DSABL: + + /* FIXME Ditto as for .ENABL */ + while(!EOL(*cp)) + { + label = get_symbol(cp, &cp, NULL); + if(strcmp(label, "AMA") == 0) + enabl_ama = 0; + else if(strcmp(label, "LSB") == 0) + enabl_lsb = 0; + else if(strcmp(label, "GBL") == 0) + enabl_gbl = 0; + free(label); + cp = skipdelim(cp); + } + return 1; + + case P_LIMIT: + store_limits(stack->top, tr); + return 1; + + case P_TITLE: + /* accquire module name */ + if(module_name != NULL) + { + free(module_name); + } + module_name = get_symbol(cp, &cp, NULL); + return 1; + + case P_END: + /* Accquire transfer address */ + cp = skipwhite(cp); + if(!EOL(*cp)) + { + if(xfer_address) + free_tree(xfer_address); + xfer_address = parse_expr(cp, 0); + } + return 1; + + case P_IFDF: + opcp = skipwhite(opcp); + cp = opcp + 3; /* Point cp at the "DF" or + "NDF" part */ + /* Falls into... */ + case P_IIF: + case P_IF: + { + EX_TREE *value; + int ok; + + label = get_symbol(cp, &cp, NULL); /* Get condition */ + cp = skipdelim(cp); + + if(strcmp(label, "DF") == 0) + { + value = parse_expr(cp, 1); + cp = value->cp; + ok = eval_defined(value); + free_tree(value); + } + else if(strcmp(label, "NDF") == 0) + { + value = parse_expr(cp, 1); + cp = value->cp; + ok = eval_undefined(value); + free_tree(value); + } + else if(strcmp(label, "B") == 0) + { + char *thing; + cp = skipwhite(cp); + if(!EOL(*cp)) + thing = getstring(cp, &cp); + else + thing = memcheck(strdup("")); + ok = (*thing == 0); + free(thing); + } + else if(strcmp(label, "NB") == 0) + { + char *thing; + cp = skipwhite(cp); + if(!EOL(*cp)) + thing = getstring(cp, &cp); + else + thing = memcheck(strdup("")); + ok = (*thing != 0); + free(thing); + } + else if(strcmp(label, "IDN") == 0) + { + char *thing1, *thing2; + thing1 = getstring(cp, &cp); + cp = skipdelim(cp); + if(!EOL(*cp)) + thing2 = getstring(cp, &cp); + else + thing2 = memcheck(strdup("")); + ok = (strcmp(thing1, thing2) == 0); + free(thing1); + free(thing2); + } + else if(strcmp(label, "DIF") == 0) + { + char *thing1, *thing2; + thing1 = getstring(cp, &cp); + cp = skipdelim(cp); + if(!EOL(*cp)) + thing2 = getstring(cp, &cp); + else + thing2 = memcheck(strdup("")); + ok = (strcmp(thing1, thing2) != 0); + free(thing1); + free(thing2); + } + else + { + int sword; + unsigned uword; + EX_TREE *value = parse_expr(cp, 0); + + cp = value->cp; + + if(value->type != EX_LIT) + { + report(stack->top, "Bad .IF expression\n"); + list_value(stack->top, 0); + free_tree(value); + ok = FALSE; /* Pick something. */ + } + else + { + unsigned word; + /* Convert to signed and unsigned words */ + sword = value->data.lit & 0x7fff; + + /* FIXME I don't know if the following + is portable enough. */ + if(value->data.lit & 0x8000) + sword |= ~0xFFFF; /* Render negative */ + + /* Reduce unsigned value to 16 bits */ + uword = value->data.lit & 0xffff; + + if(strcmp(label, "EQ") == 0 || + strcmp(label, "Z") == 0) + ok = (uword == 0), word = uword; + else if(strcmp(label, "NE") == 0 || + strcmp(label, "NZ") == 0) + ok = (uword != 0), word = uword; + else if(strcmp(label, "GT") == 0 || + strcmp(label, "G") == 0) + ok = (sword > 0), word = sword; + else if(strcmp(label, "GE") == 0) + ok = (sword >= 0), word = sword; + else if(strcmp(label, "LT") == 0 || + strcmp(label, "L") == 0) + ok = (sword < 0), word = sword; + else if(strcmp(label, "LE") == 0) + ok = (sword <= 0), word = sword; + + list_value(stack->top, word); + + free_tree(value); + } + } + + free(label); + + if(op->value == P_IIF) + { + stmtno++; /* the second half is a + separate statement */ + if(ok) + { + /* The "immediate if" */ + /* Only slightly tricky. */ + cp = skipdelim(cp); + label = get_symbol(cp, &ncp, &local); + goto reassemble; + } + return 1; + } + + push_cond(ok, stack->top); + + if(!ok) + suppressed++; /* Assembly + suppressed + until .ENDC */ + } + return 1; + + case P_IFF: + if(last_cond < 0) + { + report(stack->top, "No conditional block active\n"); + return 0; + } + if(conds[last_cond].ok) /* Suppress if last cond + is true */ + suppressed++; + return 1; + + case P_IFT: + if(last_cond < 0) + { + report(stack->top, "No conditional block active\n"); + return 0; + } + if(!conds[last_cond].ok) /* Suppress if last cond + is false */ + suppressed++; + return 1; + + case P_IFTF: + if(last_cond < 0) + { + report(stack->top, "No conditional block active\n"); + return 0; + } + return 1; /* Don't suppress. */ + + case P_ENDC: + if(last_cond < 0) + { + report(stack->top, "No conditional block active\n"); + return 0; + } + + pop_cond(last_cond-1); + return 1; + + case P_EVEN: + if(DOT & 1) + { + list_word(stack->top, DOT, 0, 1, ""); + DOT++; + } + return 1; + + case P_ODD: + if(!(DOT & 1)) + { + list_word(stack->top, DOT, 0, 1, ""); + DOT++; + } + return 1; + + case P_ASECT: + go_section(tr, &absolute_section); + return 1; + + case P_CSECT: + case P_PSECT: + { + SYMBOL *sectsym; + SECTION *sect; + + label = get_symbol(cp, &cp, NULL); + if(label == NULL) + label = memcheck(strdup("")); /* Allow blank */ + + sectsym = lookup_sym(label, §ion_st); + if(sectsym) + { + sect = sectsym->section; + free(label); + } + else + { + sect = new_section(); + sect->label = label; + sect->flags = 0; + sect->pc = 0; + sect->size = 0; + sect->type = USER; + sections[sector++] = sect; + sectsym = add_sym(label, 0, 0, sect, §ion_st); + } + + if(op->value == P_PSECT) + sect->flags |= PSECT_REL; + else if(op->value == P_CSECT) + sect->flags |= PSECT_REL|PSECT_COM|PSECT_GBL; + + while(cp = skipdelim(cp), !EOL(*cp)) + { + /* Parse section options */ + label = get_symbol(cp, &cp, NULL); + if(strcmp(label, "ABS") == 0) + { + sect->flags &= ~PSECT_REL; /* Not relative */ + sect->flags |= PSECT_COM; /* implies common */ + } + else if(strcmp(label, "REL") == 0) + { + sect->flags |= PSECT_REL; /* Is relative */ + } + else if(strcmp(label, "SAV") == 0) + { + sect->flags |= PSECT_SAV; /* Is root */ + } + else if(strcmp(label, "OVR") == 0) + { + sect->flags |= PSECT_COM; /* Is common */ + } + else if(strcmp(label, "RW") == 0) + { + sect->flags &= ~PSECT_RO; /* Not read-only */ + } + else if(strcmp(label, "RO") == 0) + { + sect->flags |= PSECT_RO; /* Is read-only */ + } + else if(strcmp(label, "I") == 0) + { + sect->flags &= ~PSECT_DATA; /* Not data */ + } + else if(strcmp(label, "D") == 0) + { + sect->flags |= PSECT_DATA; /* data */ + } + else if(strcmp(label, "GBL") == 0) + { + sect->flags |= PSECT_GBL; /* Global */ + } + else if(strcmp(label, "LCL") == 0) + { + sect->flags &= ~PSECT_GBL; /* Local */ + } + else + { + report(stack->top, + "Unknown flag %s given to " + ".PSECT directive\n", label); + free(label); + return 0; + } + + free(label); + } + + go_section(tr, sect); + + return 1; + } /* end PSECT code */ + break; + + case P_WEAK: + case P_GLOBL: + { + SYMBOL *sym; + while(!EOL(*cp)) + { + /* Loop and make definitions for + comma-separated symbols */ + label = get_symbol(cp, &ncp, NULL); + if(label == NULL) + { + report(stack->top, + "Illegal .GLOBL/.WEAK " + "syntax\n"); + return 0; + } + + sym = lookup_sym(label, &symbol_st); + if(sym) + { + sym->flags |= + GLOBAL| + (op->value == P_WEAK ? WEAK : 0); + } + else + sym = add_sym(label, 0, + GLOBAL| + (op->value == P_WEAK ? WEAK : 0), + &absolute_section, &symbol_st); + + free(label); + cp = skipdelim(ncp); + } + } + return 1; + + case P_WORD: + { + /* .WORD might be followed by nothing, which + is an implicit .WORD 0 */ + if(EOL(*cp)) + { + if(DOT & 1) + { + report(stack->top, ".WORD on odd " + "boundary\n"); + DOT++; /* Fix it, too */ + } + store_word(stack->top, tr, 2, 0); + return 1; + } + else + return do_word(stack, tr, cp, 2); + } + + case P_BYTE: + if(EOL(*cp)) + { + /* Blank .BYTE. Same as .BYTE 0 */ + store_word(stack->top, tr, 1, 0); + return 1; + } + else + return do_word(stack, tr, cp, 1); + + case P_BLKW: + case P_BLKB: + { + EX_TREE *value = parse_expr(cp, 0); + int ok = 1; + if(value->type != EX_LIT) + { + report(stack->top, + "Argument to .BLKB/.BLKW " + "must be constant\n"); + ok = 0; + } + else + { + list_value(stack->top, DOT); + DOT += value->data.lit * + (op->value == P_BLKW ? 2 : 1); + change_dot(tr, 0); + } + free_tree(value); + return ok; + } + + case P_ASCIZ: + case P_ASCII: + { + EX_TREE *value; + + do + { + cp = skipwhite(cp); + if(*cp == '<' || *cp == '^') + { + /* A byte value */ + value = parse_expr(cp, 0); + cp = value->cp; + store_value(stack, tr, 1, value); + free_tree(value); + } + else + { + char quote = *cp++; + while(*cp && *cp != '\n' && *cp != quote) + { + store_word(stack->top, tr, 1, *cp++); + } + cp++; /* Skip closing quote */ + } + + cp = skipwhite(cp); + } while(!EOL(*cp)); + + if(op->value == P_ASCIZ) + { + store_word(stack->top, tr, 1, 0); + } + + return 1; + } + + case P_RAD50: + + if(DOT & 1) + { + report(stack->top, ".RAD50 on odd " + "boundary\n"); + DOT++; /* Fix it */ + } + + while(!EOL(*cp)) + { + char endstr[6]; + int len; + char *radstr; + char *radp; + + endstr[0] = *cp++; + endstr[1] = '\n'; + endstr[2] = 0; + + len = strcspn(cp, endstr); + radstr = memcheck(malloc(len + 1)); + memcpy(radstr, cp, len); + radstr[len] = 0; + cp += len; + if(*cp && *cp != '\n') + cp++; + for(radp = radstr; *radp;) + { + unsigned rad; + rad = rad50(radp, &radp); + store_word(stack->top, tr, 2, rad); + } + free(radstr); + + cp = skipwhite(cp); + } + return 1; + + default: + report(stack->top, "Unimplemented directive %s\n", + op->label); + return 0; + + } /* end switch (PSEUDO operation) */ + + case INSTRUCTION: + { + /* The PC must always be even. */ + if(DOT & 1) + { + report(stack->top, + "Instruction on odd address\n"); + DOT++; /* ...and fix it... */ + } + + switch(op->flags & OC_MASK) + { + case OC_NONE: + /* No operands. */ + store_word(stack->top, tr, 2, op->value); + return 1; + + case OC_MARK: + /* MARK, EMT, TRAP */ + { + EX_TREE *value; + unsigned word; + + cp = skipwhite(cp); + if(*cp == '#') + cp++; /* Allow the hash, but + don't require it */ + value = parse_expr(cp, 0); + if(value->type != EX_LIT) + { + report(stack->top, + "Instruction requires " + "simple literal operand\n"); + word = op->value; + } + else + { + word = op->value | value->data.lit; + } + + store_word(stack->top, tr, 2, word); + free_tree(value); + } + return 1; + + case OC_1GEN: + /* One general addressing mode */ + { + ADDR_MODE mode; + unsigned word; + + if(!get_mode(cp, &cp, &mode)) + { + report(stack->top, + "Illegal addressing mode\n"); + return 0; + } + + if(op->value == 0100 && + (mode.type & 07) == 0) + { + report(stack->top, + "JMP Rn is illegal\n"); + /* But encode it anyway... */ + } + + /* Build instruction word */ + word = op->value | mode.type; + store_word(stack->top, tr, 2, word); + mode_extension(tr, &mode, stack->top); + } + return 1; + + case OC_2GEN: + /* Two general addressing modes */ + { + ADDR_MODE left, right; + unsigned word; + + if(!get_mode(cp, &cp, &left)) + { + report(stack->top, + "Illegal addressing mode\n"); + return 0; + } + + if(*cp++ != ',') + { + report(stack->top, "Illegal syntax\n"); + free_addr_mode(&left); + return 0; + } + + if(!get_mode(cp, &cp, &right)) + { + report(stack->top, + "Illegal addressing mode\n"); + free_addr_mode(&left); + return 0; + } + + /* Build instruction word */ + word = op->value | left.type << 6 | right.type; + store_word(stack->top, tr, 2, word); + mode_extension(tr, &left, stack->top); + mode_extension(tr, &right, stack->top); + } + return 1; + + case OC_BR: + /* branches */ + { + EX_TREE *value; + unsigned offset; + + value = parse_expr(cp, 0); + cp = value->cp; + + /* Relative PSECT or absolute? */ + if(current_pc->section->flags & PSECT_REL) + { + SYMBOL *sym; + + /* Can't branch unless I can + calculate the offset. */ + + /* You know, I *could* branch + between sections if I feed the + linker a complex relocation + expression to calculate the + offset. But I won't. */ + + if(!express_sym_offset(value, + &sym, + &offset) || + sym->section != current_pc->section) + { + report(stack->top, + "Bad branch target\n"); + store_word(stack->top, tr, + 2, op->value); + free_tree(value); + return 0; + } + + /* Compute the branch offset and + check for addressability */ + offset += sym->value; + offset -= DOT + 2; + } + else + { + if(value->type != EX_LIT) + { + report(stack->top, + "Bad branch target\n"); + store_word(stack->top, tr, + 2, op->value); + free_tree(value); + return 0; + } + + offset = value->data.lit - + (DOT + 2); + } + + if(!check_branch(stack, offset, -256, + 255)) + offset = 0; + + /* Emit the branch code */ + offset &= 0777;/* Reduce to 9 bits */ + offset >>= 1; /* Shift to become + word offset */ + + store_word(stack->top, tr, + 2, op->value | offset); + + free_tree(value); + } + return 1; + + case OC_SOB: + { + EX_TREE *value; + unsigned reg; + unsigned offset; + + value = parse_expr(cp, 0); + cp = value->cp; + + reg = get_register(value); + free_tree(value); + if(reg == NO_REG) + { + report(stack->top, + "Illegal addressing mode\n"); + return 0; + } + + cp = skipwhite(cp); + if(*cp++ != ',') + { + report(stack->top, "Illegal syntax\n"); + return 0; + } + + value = parse_expr(cp, 0); + cp = value->cp; + + /* Relative PSECT or absolute? */ + if(current_pc->section->flags & PSECT_REL) + { + SYMBOL *sym; + + if(!express_sym_offset(value, + &sym, &offset)) + { + report(stack->top, + "Bad branch target\n"); + free_tree(value); + return 0; + } + /* Must be same section */ + if(sym->section != current_pc->section) + { + report(stack->top, + "Bad branch target\n"); + free_tree(value); + offset = 0; + } + else + { + /* Calculate byte offset */ + offset += DOT + 2; + offset -= sym->value; + } + } + else + { + if(value->type != EX_LIT) + { + report(stack->top, "Bad branch " + "target\n"); + offset = 0; + } + else + { + offset = DOT + 2 - + value->data.lit; + } + } + + if(!check_branch(stack, offset, 0, 126)) + offset = 0; + + offset &= 0177; /* Reduce to 7 bits */ + offset >>= 1; /* Shift to become word offset */ + store_word(stack->top, tr, 2, + op->value | offset | (reg << 6)); + + free_tree(value); + } + return 1; + + case OC_ASH: + /* First op is gen, second is register. */ + { + ADDR_MODE mode; + EX_TREE *value; + unsigned reg; + unsigned word; + + if(!get_mode(cp, &cp, &mode)) + { + report(stack->top, "Illegal addressing mode\n"); + return 0; + } + + cp = skipwhite(cp); + if(*cp++ != ',') + { + report(stack->top, "Illegal addressing mode\n"); + free_addr_mode(&mode); + return 0; + } + value = parse_expr(cp, 0); + cp = value->cp; + + reg = get_register(value); + if(reg == NO_REG) + { + report(stack->top, + "Illegal addressing mode\n"); + free_tree(value); + free_addr_mode(&mode); + return 0; + } + + /* Instruction word */ + word = op->value | mode.type | (reg << 6); + store_word(stack->top, tr, 2, word); + mode_extension(tr, &mode, stack->top); + free_tree(value); + } + return 1; + + case OC_JSR: + /* First op is register, second is gen. */ + { + ADDR_MODE mode; + EX_TREE *value; + unsigned reg; + unsigned word; + + value = parse_expr(cp, 0); + cp = value->cp; + + reg = get_register(value); + if(reg == NO_REG) + { + report(stack->top, + "Illegal addressing mode\n"); + free_tree(value); + return 0; + } + + cp = skipwhite(cp); + if(*cp++ != ',') + { + report(stack->top, + "Illegal addressing mode\n"); + return 0; + } + + if(!get_mode(cp, &cp, &mode)) + { + report(stack->top, + "Illegal addressing mode\n"); + free_tree(value); + return 0; + } + word = op->value | mode.type | (reg << 6); + store_word(stack->top, tr, 2, word); + mode_extension(tr, &mode, stack->top); + free_tree(value); + } + return 1; + + case OC_1REG: + /* One register (RTS) */ + { + EX_TREE *value; + unsigned reg; + + value = parse_expr(cp, 0); + cp = value->cp; + reg = get_register(value); + if(reg == NO_REG) + { + report(stack->top, + "Illegal addressing mode\n"); + free_tree(value); + reg = 0; + } + + store_word(stack->top, tr, + 2, op->value | reg); + free_tree(value); + } + return 1; + + case OC_1FIS: + /* One one gen and one reg 0-3 */ + { + ADDR_MODE mode; + EX_TREE *value; + unsigned reg; + unsigned word; + + if(!get_mode(cp, &cp, &mode)) + { + report(stack->top, + "Illegal addressing mode\n"); + return 0; + } + + cp = skipwhite(cp); + if(*cp++ != ',') + { + report(stack->top, + "Illegal addressing mode\n"); + free_addr_mode(&mode); + return 0; + } + + value = parse_expr(cp, 0); + cp = value->cp; + + reg = get_register(value); + if(reg == NO_REG || reg > 4) + { + report(stack->top, + "Invalid destination register\n"); + reg = 0; + } + + word = op->value | mode.type | (reg << 6); + store_word(stack->top, tr, 2, word); + mode_extension(tr, &mode, stack->top); + free_tree(value); + } + return 1; + + case OC_2FIS: + /* One reg 0-3 and one gen */ + { + ADDR_MODE mode; + EX_TREE *value; + unsigned reg; + unsigned word; + int ok = 1; + + value = parse_expr(cp, 0); + cp = value->cp; + + reg = get_register(value); + if(reg == NO_REG || reg > 4) + { + report(stack->top, + "Illegal source register\n"); + reg = 0; + ok = 0; + } + + cp = skipwhite(cp); + if(*cp++ != ',') + { + report(stack->top, + "Illegal addressing mode\n"); + free_tree(value); + return 0; + } + + if(!get_mode(cp, &cp, &mode)) + { + report(stack->top, + "Illegal addressing mode\n"); + free_tree(value); + return 0; + } + + word = op->value | mode.type | (reg << 6); + store_word(stack->top, tr, 2, word); + mode_extension(tr, &mode, stack->top); + free_tree(value); + } + return 1; + + default: + report(stack->top, + "Unimplemented instruction format\n"); + return 0; + } /* end(handle an instruction) */ + } + break; + } /* end switch(section type) */ + } /* end if (op is a symbol) */ + } + + /* Only thing left is an implied .WORD directive */ + + free(label); + + return do_word(stack, tr, cp, 2); +} + +/* assemble_stack assembles the input stack. It returns the error + count. */ + +static int assemble_stack(STACK *stack, TEXT_RLD *tr) +{ + int res; + int count = 0; + + while((res = assemble(stack, tr)) >= 0) + { + list_flush(); + if(res == 0) + count++; /* Count an error */ + } + + return count; +} + +/* write_globals writes out the GSD prior to the second assembly pass */ + +static void write_globals(FILE *obj) +{ + GSD gsd; + SYMBOL *sym; + SECTION *psect; + SYMBOL_ITER sym_iter; + int isect; + + if(obj == NULL) + return; /* Nothing to do if no OBJ file. */ + + gsd_init(&gsd, obj); + + gsd_mod(&gsd, module_name); + + if(ident) + gsd_ident(&gsd, ident); + + /* write out each PSECT with it's global stuff */ + /* Sections must be written out in the order that they + appear in the assembly file. */ + for(isect = 0; isect < sector; isect++) + { + psect = sections[isect]; + + gsd_psect(&gsd, psect->label, psect->flags, psect->size); + psect->sector = isect; /* Assign it a sector */ + psect->pc = 0; /* Reset it's PC for second pass */ + + sym = first_sym(&symbol_st, &sym_iter); + while(sym) + { + if((sym->flags & GLOBAL) && + sym->section == psect) + { + gsd_global(&gsd, sym->label, + (sym->flags & DEFINITION ? GLOBAL_DEF : 0) | + ((sym->flags & WEAK) ? GLOBAL_WEAK : 0) | + ((sym->section->flags & PSECT_REL) ? GLOBAL_REL : 0) | + 0100, /* Looks undefined, but add it in anyway */ + sym->value); + } + sym = next_sym(&symbol_st, &sym_iter); + } + } + + /* Now write out the transfer address */ + if(xfer_address->type == EX_LIT) + { + gsd_xfer(&gsd, ". ABS.", xfer_address->data.lit); + } + else + { + SYMBOL *sym; + unsigned offset; + if(!express_sym_offset(xfer_address, &sym, &offset)) + { + report(NULL, "Illegal program transfer address\n"); + } + else + { + gsd_xfer(&gsd, sym->section->label, sym->value + offset); + } + } + + gsd_flush(&gsd); + + gsd_end(&gsd); +} + +/* add_symbols adds all the internal symbols. */ + +static void add_symbols(SECTION *current_section) +{ + current_pc = add_sym(".", 0, 0, current_section, &symbol_st); + + reg_sym[0] = add_sym("R0", 0, 0, ®ister_section, &system_st); + reg_sym[1] = add_sym("R1", 1, 0, ®ister_section, &system_st); + reg_sym[2] = add_sym("R2", 2, 0, ®ister_section, &system_st); + reg_sym[3] = add_sym("R3", 3, 0, ®ister_section, &system_st); + reg_sym[4] = add_sym("R4", 4, 0, ®ister_section, &system_st); + reg_sym[5] = add_sym("R5", 5, 0, ®ister_section, &system_st); + reg_sym[6] = add_sym("SP", 6, 0, ®ister_section, &system_st); + reg_sym[7] = add_sym("PC", 7, 0, ®ister_section, &system_st); + + add_sym(".ASCII", P_ASCII, 0, &pseudo_section, &system_st); + add_sym(".ASCIZ", P_ASCIZ, 0, &pseudo_section, &system_st); + add_sym(".ASECT", P_ASECT, 0, &pseudo_section, &system_st); + add_sym(".BLKB", P_BLKB, 0, &pseudo_section, &system_st); + add_sym(".BLKW", P_BLKW, 0, &pseudo_section, &system_st); + add_sym(".BYTE", P_BYTE, 0, &pseudo_section, &system_st); + add_sym(".CSECT", P_CSECT, 0, &pseudo_section, &system_st); + add_sym(".DSABL", P_DSABL, 0, &pseudo_section, &system_st); + add_sym(".ENABL", P_ENABL, 0, &pseudo_section, &system_st); + add_sym(".END", P_END, 0, &pseudo_section, &system_st); + add_sym(".ENDC", P_ENDC, 0, &pseudo_section, &system_st); + add_sym(".ENDM", P_ENDM, 0, &pseudo_section, &system_st); + add_sym(".ENDR", P_ENDR, 0, &pseudo_section, &system_st); + add_sym(".EOT", P_EOT, 0, &pseudo_section, &system_st); + add_sym(".ERROR", P_ERROR, 0, &pseudo_section, &system_st); + add_sym(".EVEN", P_EVEN, 0, &pseudo_section, &system_st); + add_sym(".FLT2", P_FLT2, 0, &pseudo_section, &system_st); + add_sym(".FLT4", P_FLT4, 0, &pseudo_section, &system_st); + add_sym(".GLOBL", P_GLOBL, 0, &pseudo_section, &system_st); + add_sym(".IDENT", P_IDENT, 0, &pseudo_section, &system_st); + add_sym(".IF", P_IF, 0, &pseudo_section, &system_st); + add_sym(".IFDF", P_IFDF, 0, &pseudo_section, &system_st); + add_sym(".IFNDF", P_IFDF, 0, &pseudo_section, &system_st); + add_sym(".IFF", P_IFF, 0, &pseudo_section, &system_st); + add_sym(".IFT", P_IFT, 0, &pseudo_section, &system_st); + add_sym(".IFTF", P_IFTF, 0, &pseudo_section, &system_st); + add_sym(".IIF", P_IIF, 0, &pseudo_section, &system_st); + add_sym(".IRP", P_IRP, 0, &pseudo_section, &system_st); + add_sym(".IRPC", P_IRPC, 0, &pseudo_section, &system_st); + add_sym(".LIMIT", P_LIMIT, 0, &pseudo_section, &system_st); + add_sym(".LIST", P_LIST, 0, &pseudo_section, &system_st); + add_sym(".MCALL", P_MCALL, 0, &pseudo_section, &system_st); + add_sym(".MEXIT", P_MEXIT, 0, &pseudo_section, &system_st); + add_sym(".NARG", P_NARG, 0, &pseudo_section, &system_st); + add_sym(".NCHR", P_NCHR, 0, &pseudo_section, &system_st); + add_sym(".NLIST", P_NLIST, 0, &pseudo_section, &system_st); + add_sym(".NTYPE", P_NTYPE, 0, &pseudo_section, &system_st); + add_sym(".ODD", P_ODD, 0, &pseudo_section, &system_st); + add_sym(".PACKE", P_PACKED, 0, &pseudo_section, &system_st); + add_sym(".PAGE", P_PAGE, 0, &pseudo_section, &system_st); + add_sym(".PRINT", P_PRINT, 0, &pseudo_section, &system_st); + add_sym(".PSECT", P_PSECT, 0, &pseudo_section, &system_st); + add_sym(".RADIX", P_RADIX, 0, &pseudo_section, &system_st); + add_sym(".RAD50", P_RAD50, 0, &pseudo_section, &system_st); + add_sym(".REM", P_REM, 0, &pseudo_section, &system_st); + add_sym(".REPT", P_REPT, 0, &pseudo_section, &system_st); + add_sym(".RESTO", P_RESTORE, 0, &pseudo_section, &system_st); + add_sym(".SAVE", P_SAVE, 0, &pseudo_section, &system_st); + add_sym(".SBTTL", P_SBTTL, 0, &pseudo_section, &system_st); + add_sym(".TITLE", P_TITLE, 0, &pseudo_section, &system_st); + add_sym(".WORD", P_WORD, 0, &pseudo_section, &system_st); + add_sym(".MACRO", P_MACRO, 0, &pseudo_section, &system_st); + add_sym(".WEAK", P_WEAK, 0, &pseudo_section, &system_st); + + add_sym("ADC", I_ADC, OC_1GEN, &instruction_section, &system_st); + add_sym("ADCB", I_ADCB, OC_1GEN, &instruction_section, &system_st); + add_sym("ADD", I_ADD, OC_2GEN, &instruction_section, &system_st); + add_sym("ASH", I_ASH, OC_ASH, &instruction_section, &system_st); + add_sym("ASHC", I_ASHC, OC_ASH, &instruction_section, &system_st); + add_sym("ASL", I_ASL, OC_1GEN, &instruction_section, &system_st); + add_sym("ASLB", I_ASLB, OC_1GEN, &instruction_section, &system_st); + add_sym("ASR", I_ASR, OC_1GEN, &instruction_section, &system_st); + add_sym("ASRB", I_ASRB, OC_1GEN, &instruction_section, &system_st); + add_sym("BCC", I_BCC, OC_BR, &instruction_section, &system_st); + add_sym("BCS", I_BCS, OC_BR, &instruction_section, &system_st); + add_sym("BEQ", I_BEQ, OC_BR, &instruction_section, &system_st); + add_sym("BGE", I_BGE, OC_BR, &instruction_section, &system_st); + add_sym("BGT", I_BGT, OC_BR, &instruction_section, &system_st); + add_sym("BHI", I_BHI, OC_BR, &instruction_section, &system_st); + add_sym("BHIS", I_BHIS, OC_BR, &instruction_section, &system_st); + add_sym("BIC", I_BIC, OC_2GEN, &instruction_section, &system_st); + add_sym("BICB", I_BICB, OC_2GEN, &instruction_section, &system_st); + add_sym("BIS", I_BIS, OC_2GEN, &instruction_section, &system_st); + add_sym("BISB", I_BISB, OC_2GEN, &instruction_section, &system_st); + add_sym("BIT", I_BIT, OC_2GEN, &instruction_section, &system_st); + add_sym("BITB", I_BITB, OC_2GEN, &instruction_section, &system_st); + add_sym("BLE", I_BLE, OC_BR, &instruction_section, &system_st); + add_sym("BLO", I_BLO, OC_BR, &instruction_section, &system_st); + add_sym("BLOS", I_BLOS, OC_BR, &instruction_section, &system_st); + add_sym("BLT", I_BLT, OC_BR, &instruction_section, &system_st); + add_sym("BMI", I_BMI, OC_BR, &instruction_section, &system_st); + add_sym("BNE", I_BNE, OC_BR, &instruction_section, &system_st); + add_sym("BPL", I_BPL, OC_BR, &instruction_section, &system_st); + add_sym("BPT", I_BPT, OC_NONE, &instruction_section, &system_st); + add_sym("BR", I_BR, OC_BR, &instruction_section, &system_st); + add_sym("BVC", I_BVC, OC_BR, &instruction_section, &system_st); + add_sym("BVS", I_BVS, OC_BR, &instruction_section, &system_st); + add_sym("CALL", I_CALL, OC_1GEN, &instruction_section, &system_st); + add_sym("CALLR", I_CALLR, OC_1GEN, &instruction_section, &system_st); + add_sym("CCC", I_CCC, OC_NONE, &instruction_section, &system_st); + add_sym("CLC", I_CLC, OC_NONE, &instruction_section, &system_st); + add_sym("CLN", I_CLN, OC_NONE, &instruction_section, &system_st); + add_sym("CLR", I_CLR, OC_1GEN, &instruction_section, &system_st); + add_sym("CLRB", I_CLRB, OC_1GEN, &instruction_section, &system_st); + add_sym("CLV", I_CLV, OC_NONE, &instruction_section, &system_st); + add_sym("CLZ", I_CLZ, OC_NONE, &instruction_section, &system_st); + add_sym("CMP", I_CMP, OC_2GEN, &instruction_section, &system_st); + add_sym("CMPB", I_CMPB, OC_2GEN, &instruction_section, &system_st); + add_sym("COM", I_COM, OC_1GEN, &instruction_section, &system_st); + add_sym("COMB", I_COMB, OC_1GEN, &instruction_section, &system_st); + add_sym("DEC", I_DEC, OC_1GEN, &instruction_section, &system_st); + add_sym("DECB", I_DECB, OC_1GEN, &instruction_section, &system_st); + add_sym("DIV", I_DIV, OC_ASH, &instruction_section, &system_st); + add_sym("EMT", I_EMT, OC_MARK, &instruction_section, &system_st); + add_sym("FADD", I_FADD, OC_1REG, &instruction_section, &system_st); + add_sym("FDIV", I_FDIV, OC_1REG, &instruction_section, &system_st); + add_sym("FMUL", I_FMUL, OC_1REG, &instruction_section, &system_st); + add_sym("FSUB", I_FSUB, OC_1REG, &instruction_section, &system_st); + add_sym("HALT", I_HALT, OC_NONE, &instruction_section, &system_st); + add_sym("INC", I_INC, OC_1GEN, &instruction_section, &system_st); + add_sym("INCB", I_INCB, OC_1GEN, &instruction_section, &system_st); + add_sym("IOT", I_IOT, OC_NONE, &instruction_section, &system_st); + add_sym("JMP", I_JMP, OC_1GEN, &instruction_section, &system_st); + add_sym("JSR", I_JSR, OC_JSR, &instruction_section, &system_st); + add_sym("MARK", I_MARK, OC_MARK, &instruction_section, &system_st); + add_sym("MED6X", I_MED6X, OC_NONE, &instruction_section, &system_st); + add_sym("MED74C", I_MED74C, OC_NONE, &instruction_section, &system_st); + add_sym("MFPD", I_MFPD, OC_1GEN, &instruction_section, &system_st); + add_sym("MFPI", I_MFPI, OC_1GEN, &instruction_section, &system_st); + add_sym("MFPS", I_MFPS, OC_1GEN, &instruction_section, &system_st); + add_sym("MOV", I_MOV, OC_2GEN, &instruction_section, &system_st); + add_sym("MOVB", I_MOVB, OC_2GEN, &instruction_section, &system_st); + add_sym("MTPD", I_MTPD, OC_1GEN, &instruction_section, &system_st); + add_sym("MTPI", I_MTPI, OC_1GEN, &instruction_section, &system_st); + add_sym("MTPS", I_MTPS, OC_1GEN, &instruction_section, &system_st); + add_sym("MUL", I_MUL, OC_ASH, &instruction_section, &system_st); + add_sym("NEG", I_NEG, OC_1GEN, &instruction_section, &system_st); + add_sym("NEGB", I_NEGB, OC_1GEN, &instruction_section, &system_st); + add_sym("NOP", I_NOP, OC_NONE, &instruction_section, &system_st); + add_sym("RESET", I_RESET, OC_NONE, &instruction_section, &system_st); + add_sym("RETURN", I_RETURN, OC_NONE, &instruction_section, &system_st); + add_sym("ROL", I_ROL, OC_1GEN, &instruction_section, &system_st); + add_sym("ROLB", I_ROLB, OC_1GEN, &instruction_section, &system_st); + add_sym("ROR", I_ROR, OC_1GEN, &instruction_section, &system_st); + add_sym("RORB", I_RORB, OC_1GEN, &instruction_section, &system_st); + add_sym("RTI", I_RTI, OC_NONE, &instruction_section, &system_st); + add_sym("RTS", I_RTS, OC_1REG, &instruction_section, &system_st); + add_sym("RTT", I_RTT, OC_NONE, &instruction_section, &system_st); + add_sym("SBC", I_SBC, OC_1GEN, &instruction_section, &system_st); + add_sym("SBCB", I_SBCB, OC_1GEN, &instruction_section, &system_st); + add_sym("SCC", I_SCC, OC_NONE, &instruction_section, &system_st); + add_sym("SEC", I_SEC, OC_NONE, &instruction_section, &system_st); + add_sym("SEN", I_SEN, OC_NONE, &instruction_section, &system_st); + add_sym("SEV", I_SEV, OC_NONE, &instruction_section, &system_st); + add_sym("SEZ", I_SEZ, OC_NONE, &instruction_section, &system_st); + add_sym("SOB", I_SOB, OC_SOB, &instruction_section, &system_st); + add_sym("SPL", I_SPL, OC_1REG, &instruction_section, &system_st); + add_sym("SUB", I_SUB, OC_2GEN, &instruction_section, &system_st); + add_sym("SWAB", I_SWAB, OC_1GEN, &instruction_section, &system_st); + add_sym("SXT", I_SXT, OC_1GEN, &instruction_section, &system_st); + add_sym("TRAP", I_TRAP, OC_MARK, &instruction_section, &system_st); + add_sym("TST", I_TST, OC_1GEN, &instruction_section, &system_st); + add_sym("TSTB", I_TSTB, OC_1GEN, &instruction_section, &system_st); + add_sym("WAIT", I_WAIT, OC_NONE, &instruction_section, &system_st); + add_sym("XFC", I_XFC, OC_NONE, &instruction_section, &system_st); + add_sym("XOR", I_XOR, OC_JSR, &instruction_section, &system_st); + add_sym("MFPT", I_MFPT, OC_NONE, &instruction_section, &system_st); + + add_sym("ABSD", I_ABSD, OC_1GEN, &instruction_section, &system_st); + add_sym("ABSF", I_ABSF, OC_1GEN, &instruction_section, &system_st); + add_sym("ADDD", I_ADDD, OC_1FIS, &instruction_section, &system_st); + add_sym("ADDF", I_ADDF, OC_1FIS, &instruction_section, &system_st); + add_sym("CFCC", I_CFCC, OC_NONE, &instruction_section, &system_st); + add_sym("CLRD", I_CLRD, OC_1GEN, &instruction_section, &system_st); + add_sym("CLRF", I_CLRF, OC_1GEN, &instruction_section, &system_st); + add_sym("CMPD", I_CMPD, OC_1FIS, &instruction_section, &system_st); + add_sym("CMPF", I_CMPF, OC_1FIS, &instruction_section, &system_st); + add_sym("DIVD", I_DIVD, OC_1FIS, &instruction_section, &system_st); + add_sym("DIVF", I_DIVF, OC_1FIS, &instruction_section, &system_st); + add_sym("LDCDF", I_LDCDF, OC_1FIS, &instruction_section, &system_st); + add_sym("LDCID", I_LDCID, OC_1FIS, &instruction_section, &system_st); + add_sym("LDCIF", I_LDCIF, OC_1FIS, &instruction_section, &system_st); + add_sym("LDCLD", I_LDCLD, OC_1FIS, &instruction_section, &system_st); + add_sym("LDCLF", I_LDCLF, OC_1FIS, &instruction_section, &system_st); + add_sym("LDD", I_LDD, OC_1FIS, &instruction_section, &system_st); + add_sym("LDEXP", I_LDEXP, OC_1FIS, &instruction_section, &system_st); + add_sym("LDF", I_LDF, OC_1FIS, &instruction_section, &system_st); + add_sym("LDFPS", I_LDFPS, OC_1GEN, &instruction_section, &system_st); + add_sym("MODD", I_MODD, OC_1FIS, &instruction_section, &system_st); + add_sym("MODF", I_MODF, OC_1FIS, &instruction_section, &system_st); + add_sym("MULD", I_MULD, OC_1FIS, &instruction_section, &system_st); + add_sym("MULF", I_MULF, OC_1FIS, &instruction_section, &system_st); + add_sym("NEGD", I_NEGD, OC_1GEN, &instruction_section, &system_st); + add_sym("NEGF", I_NEGF, OC_1GEN, &instruction_section, &system_st); + add_sym("SETD", I_SETD, OC_NONE, &instruction_section, &system_st); + add_sym("SETF", I_SETF, OC_NONE, &instruction_section, &system_st); + add_sym("SETI", I_SETI, OC_NONE, &instruction_section, &system_st); + add_sym("SETL", I_SETL, OC_NONE, &instruction_section, &system_st); + add_sym("STA0", I_STA0, OC_NONE, &instruction_section, &system_st); + add_sym("STB0", I_STB0, OC_NONE, &instruction_section, &system_st); + add_sym("STCDF", I_STCDF, OC_2FIS, &instruction_section, &system_st); + add_sym("STCDI", I_STCDI, OC_2FIS, &instruction_section, &system_st); + add_sym("STCDL", I_STCDL, OC_2FIS, &instruction_section, &system_st); + add_sym("STCFD", I_STCFD, OC_2FIS, &instruction_section, &system_st); + add_sym("STCFI", I_STCFI, OC_2FIS, &instruction_section, &system_st); + add_sym("STCFL", I_STCFL, OC_2FIS, &instruction_section, &system_st); + add_sym("STD", I_STD, OC_2FIS, &instruction_section, &system_st); + add_sym("STEXP", I_STEXP, OC_2FIS, &instruction_section, &system_st); + add_sym("STF", I_STF, OC_2FIS, &instruction_section, &system_st); + add_sym("STFPS", I_STFPS, OC_1GEN, &instruction_section, &system_st); + add_sym("STST", I_STST, OC_1GEN, &instruction_section, &system_st); + add_sym("SUBD", I_SUBD, OC_1FIS, &instruction_section, &system_st); + add_sym("SUBF", I_SUBF, OC_1FIS, &instruction_section, &system_st); + add_sym("TSTD", I_TSTD, OC_1GEN, &instruction_section, &system_st); + add_sym("TSTF", I_TSTF, OC_1GEN, &instruction_section, &system_st); + + /* FIXME: The CIS instructions are missing! */ + + add_sym(current_section->label, 0, 0, current_section, §ion_st); +} + +/* dump_all_macros is a diagnostic function that's currently not + used. I used it while debugging, and I haven't removed it. */ + +static void dump_all_macros(void) +{ + MACRO *mac; + SYMBOL_ITER iter; + + for(mac = (MACRO *)first_sym(¯o_st, &iter); + mac != NULL; mac = (MACRO *)next_sym(¯o_st, &iter)) + { + dumpmacro(mac, lstfile); + + printf("\n\n"); + } +} + +/* sym_hist is a diagnostic function that prints a histogram of the + hash table useage of a symbol table. I used this to try to tune + the hash function for better spread. It's not used now. */ + +static void sym_hist(SYMBOL_TABLE *st, char *name) +{ + int i; + SYMBOL *sym; + fprintf(lstfile, "Histogram for symbol table %s\n", name); + for(i = 0; i < 1023; i++) + { + fprintf(lstfile, "%4d: ", i); + for(sym = st->hash[i]; sym != NULL; sym = sym->next) + fputc('#', lstfile); + fputc('\n', lstfile); + } +} + +/* enable_tf is called by command argument parsing to enable and + disable named options. */ + +static void enable_tf(char *opt, int tf) +{ + if(strcmp(opt, "AMA") == 0) + enabl_ama = tf; + else if(strcmp(opt, "GBL") == 0) + enabl_gbl = tf; + else if(strcmp(opt, "ME") == 0) + list_me = tf; + else if(strcmp(opt, "BEX") == 0) + list_bex = tf; + else if(strcmp(opt, "MD") == 0) + list_md = tf; +} + +int main(int argc, char *argv[]) +{ + char *fnames[32]; + int nr_files = 0; + FILE *obj = NULL; + static char line[1024]; + TEXT_RLD tr; + char *macname = NULL; + char *objname = NULL; + char *lstname = NULL; + int arg; + int i; + STACK stack; + int count; + + for(arg = 1; arg < argc; arg++) + { + if(*argv[arg] == '-') + { + char *cp; + cp = argv[arg] + 1; + switch(tolower(*cp)) + { + case 'v': + fprintf(stderr, + "macro11 Copyright 2001 Richard Krehbiel\n" + "Version 0.2 July 15, 2001\n"); + break; + + case 'e': + /* Followed by options to enable */ + /* Since /SHOW and /ENABL option names don't overlap, + I consolidate. */ + upcase(argv[++arg]); + enable_tf(argv[arg], 1); + break; + + case 'd': + /* Followed by an option to disable */ + upcase(argv[++arg]); + enable_tf(argv[arg], 0); + break; + + case 'm': + /* Macro library */ + /* This option gives the name of an RT-11 compatible + macro library from which .MCALLed macros can be + found. */ + arg++; + mlbs[nr_mlbs] = mlb_open(argv[arg]); + if(mlbs[nr_mlbs] == NULL) + { + fprintf(stderr, + "Unable to register macro library %s\n", + argv[arg]); + exit(EXIT_FAILURE); + } + nr_mlbs++; + break; + + case 'p': /* P for search path */ + /* The -p option gives the name of a directory in + which .MCALLed macros may be found. */ + { + char *env = getenv("MCALL"); + char *temp; + + if(env == NULL) + env = ""; + + temp = memcheck(malloc(strlen(env) + + strlen(argv[arg+1]) + 8)); + strcpy(temp, "MCALL="); + strcat(temp, env); + strcat(temp, PATHSEP); + strcat(temp, argv[arg+1]); + putenv(temp); + arg++; + } + break; + + case 'o': + /* The -o option gives the object file name (.OBJ) */ + ++arg; + objname = argv[arg]; + break; + + case 'l': + /* The option -l gives the listing file name (.LST) */ + /* -l - enables listing to stdout. */ + lstname = argv[++arg]; + if(strcmp(lstname, "-") == 0) + lstfile = stdout; + else + lstfile = fopen(lstname, "w"); + break; + + case 'x': + /* The -x option invokes macro11 to expand the + contents of the registered macro libraries (see -m) + into individual .MAC files in the current + directory. No assembly of input is done. This + must be the last command line option. */ + { + int i; + for(i = 0; i < nr_mlbs; i++) + mlb_extract(mlbs[i]); + return EXIT_SUCCESS; + } + + default: + fprintf(stderr, "Unknown argument %s\n", argv[arg]); + exit(EXIT_FAILURE); + } + } + else + { + fnames[nr_files++] = argv[arg]; + } + } + + if(objname) + { + obj = fopen(objname, "wb"); + if(obj == NULL) + return EXIT_FAILURE; + } + + add_symbols(&blank_section); + + text_init(&tr, NULL, 0); + + module_name = memcheck(strdup("")); + + xfer_address = new_ex_lit(1); /* The undefined transfer address */ + + stack_init(&stack); + /* Push the files onto the input stream in reverse order */ + for(i = nr_files-1; i >= 0; --i) + { + STREAM *str = new_file_stream(fnames[i]); + if(str == NULL) + { + report(NULL, "Unable to open file %s\n", fnames[i]); + exit(EXIT_FAILURE); + } + stack_push(&stack, str); + } + + DOT = 0; + current_pc->section = &blank_section; + last_dot_section = NULL; + pass = 0; + stmtno = 0; + lsb = 0; + last_lsb = -1; + last_locsym = 32767; + last_cond = -1; + sect_sp = -1; + suppressed = 0; + + assemble_stack(&stack, &tr); + +#if 0 + if(enabl_debug) + dump_all_macros(); +#endif + + assert(stack.top == NULL); + + migrate_implicit(); /* Migrate the implicit globals */ + write_globals(obj); /* Write the global symbol dictionary */ + +#if 0 + sym_hist(&symbol_st, "symbol_st"); /* Draw a symbol table histogram */ +#endif + + + text_init(&tr, obj, 0); + + stack_init(&stack); /* Superfluous... */ + /* Re-push the files onto the input stream in reverse order */ + for(i = nr_files-1; i >= 0; --i) + { + STREAM *str = new_file_stream(fnames[i]); + if(str == NULL) + { + report(NULL, "Unable to open file %s\n", fnames[i]); + exit(EXIT_FAILURE); + } + stack_push(&stack, str); + } + + DOT = 0; + current_pc->section = &blank_section; + last_dot_section = NULL; + + pass = 1; + stmtno = 0; + lsb = 0; + last_lsb = -1; + last_locsym = 32767; + pop_cond(-1); + sect_sp = -1; + suppressed = 0; + + count = assemble_stack(&stack, &tr); + + text_flush(&tr); + + while(last_cond >= 0) + { + report(NULL, "%s:%d: Unterminated conditional\n", + conds[last_cond].file, conds[last_cond].line); + pop_cond(last_cond - 1); + count++; + } + + for(i = 0; i < nr_mlbs; i++) + mlb_close(mlbs[i]); + + write_endmod(obj); + + if(obj != NULL) + fclose(obj); + + if(count > 0) + fprintf(stderr, "%d Errors\n", count); + + if(lstfile && strcmp(lstname, "-") != 0) + fclose(lstfile); + + return count > 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/crossassemblers/macro11/macro11.dsp b/crossassemblers/macro11/macro11.dsp new file mode 100644 index 0000000..0a4ddfd --- /dev/null +++ b/crossassemblers/macro11/macro11.dsp @@ -0,0 +1,180 @@ +# Microsoft Developer Studio Project File - Name="macro11" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=macro11 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "macro11.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "macro11.mak" CFG="macro11 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "macro11 - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "macro11 - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/macro11", BAAAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "macro11 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# SUBTRACT LINK32 /profile +# Begin Special Build Tool +TargetPath=.\Release\macro11.exe +SOURCE="$(InputPath)" +PostBuild_Cmds=copy $(TargetPath) c:\bin +# End Special Build Tool + +!ELSEIF "$(CFG)" == "macro11 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "MEM_DEBUG" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /profile +# Begin Special Build Tool +TargetPath=.\Debug\macro11.exe +SOURCE="$(InputPath)" +PostBuild_Cmds=copy $(TargetPath) c:\bin +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "macro11 - Win32 Release" +# Name "macro11 - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\macro11.c +# End Source File +# Begin Source File + +SOURCE=.\mlb.c +# End Source File +# Begin Source File + +SOURCE=.\object.c +# End Source File +# Begin Source File + +SOURCE=.\rad50.c +# End Source File +# Begin Source File + +SOURCE=.\stream2.c +# End Source File +# Begin Source File + +SOURCE=.\util.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\macro11.h +# End Source File +# Begin Source File + +SOURCE=.\mlb.h +# End Source File +# Begin Source File + +SOURCE=.\object.h +# End Source File +# Begin Source File + +SOURCE=.\Rad50.h +# End Source File +# Begin Source File + +SOURCE=.\stream2.h +# End Source File +# Begin Source File + +SOURCE=.\util.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Source File + +SOURCE=.\Changes +# End Source File +# Begin Source File + +SOURCE=.\License +# End Source File +# Begin Source File + +SOURCE=.\Makefile +# End Source File +# Begin Source File + +SOURCE=.\Readme +# End Source File +# Begin Source File + +SOURCE=.\Todo +# End Source File +# End Target +# End Project diff --git a/crossassemblers/macro11/macro11.dsw b/crossassemblers/macro11/macro11.dsw new file mode 100644 index 0000000..09378a5 --- /dev/null +++ b/crossassemblers/macro11/macro11.dsw @@ -0,0 +1,53 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "dumpobj"=.\dumpobj.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/macro11", BAAAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "macro11"=.\macro11.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/macro11", BAAAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/macro11", BAAAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/crossassemblers/macro11/macro11.h b/crossassemblers/macro11/macro11.h new file mode 100644 index 0000000..fd8d8f4 --- /dev/null +++ b/crossassemblers/macro11/macro11.h @@ -0,0 +1,39 @@ +#ifndef MACRO11_H +#define MACRO11_H + +/* +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +extern void *memcheck(void *p); + +#endif diff --git a/crossassemblers/macro11/makefile b/crossassemblers/macro11/makefile new file mode 100644 index 0000000..6e9d81c --- /dev/null +++ b/crossassemblers/macro11/makefile @@ -0,0 +1,38 @@ +CFLAGS = -O -g + +MACRO11_SRCS = macro11.c mlb.c object.c stream2.c util.c rad50.c + +MACRO11_OBJS = $(MACRO11_SRCS:.c=.o) + +DUMPOBJ_SRCS = dumpobj.c rad50.c + +DUMPOBJ_OBJS = $(DUMPOBJ_SRCS:.c=.o) + +ALL_SRCS = $(MACRO11_SRCS) $(DUMPOBJ_SRCS) + +all: macro11 dumpobj + +tags: macro11 dumpobj + ctags *.c *.h + +macro11: $(MACRO11_OBJS) makefile + $(CC) $(CFLAGS) -o macro11 $(MACRO11_OBJS) -lm + +dumpobj: $(DUMPOBJ_OBJS) makefile + $(CC) $(CFLAGS) -o dumpobj $(DUMPOBJ_OBJS) + +MACRO11_OBJS: makefile +DUMPOBJ_OBJS: makefile + +clean: + -rm -f $(MACRO11_OBJS) $(DUMPOBJ_OBJS) macro11 dumpobj + +macro11.o: macro11.c macro11.h rad50.h object.h stream2.h \ + mlb.h util.h +mlb.o: mlb.c rad50.h stream2.h mlb.h macro11.h util.h +object.o: object.c rad50.h object.h +stream2.o: stream2.c macro11.h stream2.h +util.o: util.c util.h +rad50.o: rad50.c rad50.h +dumpobj.o: dumpobj.c rad50.h util.h +rad50.o: rad50.c rad50.h diff --git a/crossassemblers/macro11/mlb.c b/crossassemblers/macro11/mlb.c new file mode 100644 index 0000000..32b5cb7 --- /dev/null +++ b/crossassemblers/macro11/mlb.c @@ -0,0 +1,319 @@ +/* Routines for reading from an RT-11 macro library (like SYSMAC.SML) */ + +/* +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include +#include +#include +#include + +#include "rad50.h" + +#include "stream2.h" + +#include "mlb.h" + +#include "macro11.h" + +#include "util.h" + +#define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) + +/* BYTEPOS calculates the byte position within the macro libray file. + I use this to sort the entries by their start position, in order to + be able to calculate the entries' sizes, which isn't actually + stored in the directory. */ + +#define BYTEPOS(rec) ((WORD((rec)+4) & 32767) * 512 + (WORD((rec)+6) & 511)) + +extern FILE *lstfile; + +/* compare_position is the qsort callback function that compares byte + locations within the macro library */ +static int compare_position(const void *arg1, const void *arg2) +{ + const char *c1 = arg1, *c2 = arg2; + + if(BYTEPOS(c1) < BYTEPOS(c2)) + return -1; + if(BYTEPOS(c1) > BYTEPOS(c2)) + return 1; + return 0; +} + + +/* trim removes trailing blanks from a string. */ +static void trim(char *buf) +{ + char *cp = buf + strlen(buf); + while(--cp >= buf && *cp == ' ') + *cp = 0; +} + +/* mlb_open opens a file which is given to be a macro library. */ +/* Returns NULL on failure. */ + +MLB *mlb_open(char *name) +{ + MLB *mlb = memcheck(malloc(sizeof(MLB))); + char *buff; + unsigned entsize; + unsigned nr_entries; + unsigned start_block; + int i; + + mlb->directory = NULL; + + mlb->fp = fopen(name, "rb"); + if(mlb->fp == NULL) + { + mlb_close(mlb); + return NULL; + } + + buff = memcheck(malloc(044)); /* Size of MLB library header */ + + if(fread(buff, 1, 044, mlb->fp) < 044) + { + mlb_close(mlb); + free(buff); + return NULL; + } + + if(WORD(buff) != 01001) /* Is this really a macro library? */ + { + mlb_close(mlb); /* Nope. */ + return NULL; + } + + entsize = WORD(buff + 032); /* The size of each macro directory + entry */ + nr_entries = WORD(buff + 036); /* The number of directory entries */ + start_block = WORD(buff + 034); /* The start RT-11 block of the + directory */ + + free(buff); /* Done with that header. */ + + /* Allocate a buffer for the disk directory */ + buff = memcheck(malloc(nr_entries * entsize)); + fseek(mlb->fp, start_block * 512, SEEK_SET); /* Go to the directory */ + + /* Read the disk directory */ + if(fread(buff, entsize, nr_entries, mlb->fp) < nr_entries) + { + mlb_close(mlb); /* Sorry, read error. */ + free(buff); + return NULL; + } + + /* Shift occupied directory entries to the front of the array + before sorting */ + + { + int j; + for(i = 0, j = nr_entries; i < j; i++) + { + char *ent1, *ent2; + ent1 = buff + (i * entsize); + /* Unused entries have 0177777 0177777 for the RAD50 name, + which is not legal RAD50. */ + if(WORD(ent1) == 0177777 && + WORD(ent1 + 2) == 0177777) + { + while(--j > i && + (ent2 = buff + (j * entsize), + WORD(ent2) == 0177777 && + WORD(ent2+2) == 0177777)) + ; + if(j <= i) + break; /* All done. */ + memcpy(ent1, ent2, entsize); /* Move used entry + into unused entry's + space */ + memset(ent2, 0377, entsize); /* Mark entry unused */ + } + } + + /* Now i contains the actual number of entries. */ + + mlb->nentries = i; + + /* Sort the array by file position */ + + qsort(buff, i, entsize, compare_position); + + /* Now, allocate my in-memory directory */ + mlb->directory = memcheck(malloc(sizeof(MLBENT) * mlb->nentries)); + memset(mlb->directory, 0, sizeof(MLBENT) * mlb->nentries); + + /* Build in-memory directory */ + for(j = 0; j < i; j++) + { + char radname[16]; + char *ent; + + ent = buff + (j * entsize); + + unrad50(WORD(ent), radname); + unrad50(WORD(ent+2), radname+3); + radname[6] = 0; + + trim(radname); + + mlb->directory[j].label = memcheck(strdup(radname)); + mlb->directory[j].position = BYTEPOS(ent); + if(j < i-1) + { + mlb->directory[j].length = + BYTEPOS(ent + entsize) - BYTEPOS(ent); + } + else + { + unsigned long max; + char c; + fseek(mlb->fp, 0, SEEK_END); + max = ftell(mlb->fp); + /* Look for last non-zero */ + do + { + max--; + fseek(mlb->fp, max, SEEK_SET); + c = fgetc(mlb->fp); + } while(max > 0 && c == 0); + max++; + mlb->directory[j].length = max - BYTEPOS(ent); + } + } + + free(buff); + + } + + /* Done. Return the struct that represents the opened MLB. */ + return mlb; +} + +/* mlb_close discards MLB and closes the file. */ +void mlb_close(MLB *mlb) +{ + if(mlb) + { + int i; + if(mlb->directory) + { + for(i = 0; i < mlb->nentries; i++) + { + if(mlb->directory[i].label) + free(mlb->directory[i].label); + } + free(mlb->directory); + } + if(mlb->fp) + fclose(mlb->fp); + + free(mlb); + } +} + +/* mlb_entry returns a BUFFER containing the specified entry from the + macro library, or NULL if not found. */ + +BUFFER *mlb_entry(MLB *mlb, char *name) +{ + int i; + MLBENT *ent; + BUFFER *buf; + char *bp; + int c; + + for(i = 0; i < mlb->nentries; i++) + { + ent = &mlb->directory[i]; + if(strcmp(mlb->directory[i].label, name) == 0) + break; + } + + if(i >= mlb->nentries) + return NULL; + + /* Allocate a buffer to hold the text */ + buf = new_buffer(); + buffer_resize(buf, ent->length+1); /* Make it large enough */ + bp = buf->buffer; + + fseek(mlb->fp, ent->position, SEEK_SET); + + for(i = 0; i < ent->length; i++) + { + c = fgetc(mlb->fp); /* Get macro byte */ + if(c == '\r' || c == 0) /* If it's a carriage return or 0, + discard it. */ + continue; + *bp++ = c; + } + *bp++ = 0; /* Store trailing 0 delim */ + + /* Now resize that buffer to the length actually read. */ + buffer_resize(buf, bp - buf->buffer); + + return buf; +} + +/* mlb_extract - walk thru a macro library and store it's contents + into files in the current directory. + + See, I had decided not to bother writing macro library maintenance + tools, since the user can call macros directly from the file + system. But if you've already got a macro library without the + sources, you can use this to extract the entries and maintain them + in the file system from thence forward. +*/ + +void mlb_extract(MLB *mlb) +{ + int i; + FILE *fp; + BUFFER *buf; + + for(i = 0; i < mlb->nentries; i++) + { + char name[32]; + buf = mlb_entry(mlb, mlb->directory[i].label); + sprintf(name, "%s.MAC", mlb->directory[i].label); + fp = fopen(name, "w"); + fwrite(buf->buffer, 1, buf->length, fp); + fclose(fp); + buffer_free(buf); + } +} diff --git a/crossassemblers/macro11/mlb.h b/crossassemblers/macro11/mlb.h new file mode 100644 index 0000000..9ec4b68 --- /dev/null +++ b/crossassemblers/macro11/mlb.h @@ -0,0 +1,62 @@ +#ifndef MLB_H +#define MLB_H + +/* + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#include "stream2.h" + +/* Routines to open and read entries from a macro library */ + +typedef struct mlbent +{ + char *label; + unsigned long position; + int length; +} MLBENT; + +typedef struct mlb +{ + FILE *fp; + MLBENT *directory; + int nentries; +} MLB; + +extern MLB *mlb_open(char *name); +extern BUFFER *mlb_entry(MLB *mlb, char *name); +extern void mlb_close(MLB *mlb); +extern void mlb_extract(MLB *mlb); + +#endif /* MLB_H */ diff --git a/crossassemblers/macro11/object.c b/crossassemblers/macro11/object.c new file mode 100644 index 0000000..2d97bbb --- /dev/null +++ b/crossassemblers/macro11/object.c @@ -0,0 +1,861 @@ +/* + object.c - writes RT-11 compatible .OBJ files. + + Ref: RT-11 Software Support Manual, File Formats. +*/ + +/* + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#include +#include +#include +#include + +#include "rad50.h" + +#include "object.h" + +#include "macro11.h" + +/* + writerec writes "formatted binary records." + Each is preceeded by any number of 0 bytes, begins with a 1,0 pair, + followed by 2 byte length, followed by data, followed by 1 byte + negative checksum. +*/ + +static int writerec(FILE *fp, char *data, int len) +{ + int chksum; /* Checksum is negative sum of all + bytes including header and length */ + int i; + unsigned hdrlen = len + 4; + + if(fp == NULL) + return 1; /* Silently ignore this attempt to write. */ + + chksum = 0; + if(fputc(FBR_LEAD1, fp) == EOF) /* All recs begin with 1,0 */ + return 0; + chksum -= FBR_LEAD1; + if(fputc(FBR_LEAD2, fp) == EOF) + return 0; + chksum -= FBR_LEAD2; + + i = hdrlen & 0xff; /* length, lsb */ + chksum -= i; + if(fputc(i, fp) == EOF) + return 0; + + i = (hdrlen >> 8) & 0xff; /* length, msb */ + chksum -= i; + if(fputc(i, fp) == EOF) + return 0; + + i = fwrite(data, 1, len, fp); + if(i < len) + return 0; + + while(len > 0) /* All the data bytes */ + { + chksum -= *data++ & 0xff; + len--; + } + + chksum &= 0xff; + + fputc(chksum, fp); /* Followed by the checksum byte */ + + return 1; /* Worked okay. */ +} + +/* gsd_init - prepare a GSD prior to writing GSD records */ + +void gsd_init(GSD *gsd, FILE *fp) +{ + gsd->fp = fp; + gsd->buf[0] = OBJ_GSD; /* GSD records start with 1,0 */ + gsd->buf[1] = 0; + gsd->offset = 2; /* Offset for further additions */ +} + +/* gsd_flush - write buffered GSD records */ + +int gsd_flush(GSD *gsd) +{ + if(gsd->offset > 2) + { + if(!writerec(gsd->fp, gsd->buf, gsd->offset)) + return 0; + gsd_init(gsd, gsd->fp); + } + return 1; +} + +/* gsd_write - buffers a GSD record */ + +/* All GSD entries have the following 8 byte format: */ +/* 4 bytes RAD50 name */ +/* 1 byte flags */ +/* 1 byte type */ +/* 2 bytes value */ + +static int gsd_write(GSD *gsd, char *name, int flags, + int type, int value) +{ + char *cp; + unsigned radtbl[2]; + + if(gsd->offset > sizeof(gsd->buf) - 8) + { + if(!gsd_flush(gsd)) + return 0; + } + + rad50x2(name, radtbl); + + cp = gsd->buf + gsd->offset; + *cp++ = radtbl[0] & 0xff; + *cp++ = (radtbl[0] >> 8) & 0xff; + *cp++ = radtbl[1] & 0xff; + *cp++ = (radtbl[1] >> 8) & 0xff; + + *cp++ = flags; + *cp++ = type; + + *cp++ = value & 0xff; + *cp = (value >> 8) & 0xff; + + gsd->offset += 8; + + return 1; +} + +/* gsd_mod - Write module name to GSD */ + +int gsd_mod(GSD *gsd, char *modname) +{ + return gsd_write(gsd, modname, 0, GSD_MODNAME, 0); +} + +/* gsd_csect - Write a control section name & size to the GSD */ +int gsd_csect(GSD *gsd, char *sectname, int size) +{ + return gsd_write(gsd, sectname, 0, GSD_CSECT, size); +} + +/* gsd_intname - Write an internal symbol (ignored by RT-11 linker) */ +int gsd_intname(GSD *gsd, char *name, unsigned value) +{ + return gsd_write(gsd, name, 0, GSD_ISN, value); +} + +/* gsd_xfer - Write a program transfer address to GSD */ +int gsd_xfer(GSD *gsd, char *name, unsigned value) +{ + return gsd_write(gsd, name, 010, GSD_XFER, value); +} + +/* gsd_global - Write a global definition or reference to GSD */ +/* Caller must be aware of the proper flags. */ +int gsd_global(GSD *gsd, char *name, int flags, unsigned value) +{ + return gsd_write(gsd, name, flags, GSD_GLOBAL, value); +} + +/* Write a program section to the GSD */ +/* Caller must be aware of the proper flags. */ +int gsd_psect(GSD *gsd, char *name, int flags, int size) +{ + return gsd_write(gsd, name, flags, GSD_PSECT, size); +} + +/* Write program ident to GSD */ +int gsd_ident(GSD *gsd, char *name) +{ + return gsd_write(gsd, name, 0, GSD_IDENT, 0); +} + +/* Write virtual array declaration to GSD */ +int gsd_virt(GSD *gsd, char *name, int size) +{ + return gsd_write(gsd, name, 0, GSD_VSECT, size); +} + +/* Write ENDGSD record */ + +int gsd_end(GSD *gsd) +{ + gsd->buf[0] = OBJ_ENDGSD; + gsd->buf[1] = 0; + return writerec(gsd->fp, gsd->buf, 2); +} + +/* TEXT and RLD record handling */ + +/* TEXT records contain the plain binary of the program. An RLD + record refers to the prior TEXT record, giving relocation + information. */ + +/* text_init prepares a TEXT_RLD prior to writing */ + +void text_init(TEXT_RLD *tr, FILE *fp, unsigned addr) +{ + tr->fp = fp; + + tr->text[0] = OBJ_TEXT; /* text records begin with 3, 0 */ + tr->text[1] = 0; + tr->text[2] = addr & 0xff; /* and are followed by load address */ + tr->text[3] = (addr >> 8) & 0xff; + tr->txt_offset = 4; /* Here's where recording new text will begin */ + + tr->rld[0] = OBJ_RLD; /* RLD records begin with 4, 0 */ + tr->rld[1] = 0; + + tr->txt_addr = addr; + tr->rld_offset = 2; /* And are followed by RLD entries */ +} + +/* text_flush - flushes buffer TEXT and RLD records. */ + +int text_flush(TEXT_RLD *tr) +{ + + if(tr->txt_offset > 4) + { + if(!writerec(tr->fp, tr->text, tr->txt_offset)) + return 0; + } + + if(tr->rld_offset > 2) + { + if(!writerec(tr->fp, tr->rld, tr->rld_offset)) + return 0; + } + + return 1; +} + +/* Used to ensure that TEXT and RLD information will be in adjacent + records. If not enough space exists in either buffer, both are + flushed. */ + +static int text_fit(TEXT_RLD *tr, unsigned addr, + int txtsize, int rldsize) +{ + if(tr->txt_offset + txtsize <= sizeof(tr->text) && + tr->rld_offset + rldsize <= sizeof(tr->rld) && + (txtsize == 0 || tr->txt_addr + tr->txt_offset - 4 == addr)) + return 1; /* All's well. */ + + if(!text_flush(tr)) + return 0; + text_init(tr, tr->fp, addr); + + return 1; +} + +/* text_word_i - internal text_word. Used when buffer space is + already assured. */ + +static void text_word_i(TEXT_RLD *tr, unsigned w, int size) +{ + tr->text[tr->txt_offset++] = w & 0xff; + if(size > 1) + tr->text[tr->txt_offset++] = (w >> 8) & 0xff; +} + +/* text_word - write constant word to text */ + +int text_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word) +{ + if(!text_fit(tr, *addr, size, 0)) + return 0; + + text_word_i(tr, word, size); + + *addr += size; /* Update the caller's DOT */ + return 1; /* say "ok". */ +} + +/* rld_word - adds a word to the RLD information. */ + +static void rld_word(TEXT_RLD *tr, unsigned wd) +{ + tr->rld[tr->rld_offset++] = wd & 0xff; + tr->rld[tr->rld_offset++] = (wd >> 8) & 0xff; +} + +/* rld_byte - adds a byte to rld information. */ + +static void rld_byte(TEXT_RLD *tr, unsigned byte) +{ + tr->rld[tr->rld_offset++] = byte & 0xff; +} + +/* rld_code - write the typical RLD first-word code. Encodes the + given address as the offset into the prior TEXT record. */ + +static void rld_code(TEXT_RLD *tr, unsigned code, unsigned addr, int size) +{ + unsigned offset = addr - tr->txt_addr + 4; + rld_word(tr, code | offset << 8 | (size == 1 ? 0200 : 0)); +} + +/* rld_code_naddr - typical RLD entries refer to a text address. This + one is used when the RLD code does not. */ + +static void rld_code_naddr(TEXT_RLD *tr, unsigned code, int size) +{ + rld_word(tr, code | (size == 1 ? 0200 : 0)); +} + +/* write a word with a psect-relative value */ + +int text_internal_word(TEXT_RLD *tr, unsigned *addr, + int size, unsigned word) +{ + if(!text_fit(tr, *addr, size, 4)) + return 0; + + text_word_i(tr, word, size); + rld_code(tr, RLD_INT, *addr, size); + rld_word(tr, word); + + *addr += size; + + return 1; +} + +/* write a word which is an absolute reference to a global symbol */ + +int text_global_word(TEXT_RLD *tr, unsigned *addr, + int size, unsigned word, char *global) +{ + unsigned radtbl[2]; + + if(!text_fit(tr, *addr, size, 6)) + return 0; + + text_word_i(tr, word, size); + rld_code(tr, RLD_GLOBAL, *addr, size); + + rad50x2(global, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + + *addr += size; + + return 1; +} + +/* Write a word which is a PC-relative reference to an absolute address */ + +int text_displaced_word(TEXT_RLD *tr, + unsigned *addr, int size, unsigned word) +{ + if(!text_fit(tr, *addr, size, 4)) + return 0; + + text_word_i(tr, word, size); + rld_code(tr, RLD_INT_DISP, *addr, size); + rld_word(tr, word); + + *addr += size; + + return 1; +} + +/* write a word which is a PC-relative reference to a global symbol */ + +int text_global_displaced_word(TEXT_RLD *tr, + unsigned *addr, int size, + unsigned word, char *global) +{ + unsigned radtbl[2]; + + if(!text_fit(tr, *addr, size, 6)) + return 0; + + text_word_i(tr, word, size); + rld_code(tr, RLD_GLOBAL_DISP, *addr, size); + + rad50x2(global, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + + *addr += size; + + return 1; +} + +/* write a word which is an absolute reference to a global symbol plus + an offset */ + +/* Optimizes to text_global_word when the offset is zero. */ + +int text_global_offset_word(TEXT_RLD *tr, + unsigned *addr, int size, + unsigned word, char *global) +{ + unsigned radtbl[2]; + + if(word == 0) + return text_global_word(tr, addr, size, word, global); + + if(!text_fit(tr, *addr, size, 8)) + return 0; + + text_word_i(tr, word, size); + + rld_code(tr, RLD_GLOBAL_OFFSET, *addr, size); + + rad50x2(global, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + rld_word(tr, word); + + *addr += size; + + return 1; +} + +/* write a word which is a PC-relative reference to a global symbol + plus an offset */ + +/* Optimizes to text_global_displaced_word when the offset is zero. */ + +int text_global_displaced_offset_word(TEXT_RLD *tr, + unsigned *addr, int size, + unsigned word, char *global) +{ + unsigned radtbl[2]; + + if(word == 0) + return text_global_displaced_word(tr, addr, size, word, global); + + if(!text_fit(tr, *addr, size, 8)) + return 0; + + text_word_i(tr, word, size); + rld_code(tr, RLD_GLOBAL_OFFSET_DISP, *addr, size); + + rad50x2(global, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + rld_word(tr, word); + + *addr += size; + + return 1; +} + +/* Define current program counter, plus PSECT */ +/* Different because it must be the last RLD entry in a block. That's + because TEXT records themselves contain the current text + address. */ + +int text_define_location(TEXT_RLD *tr, char *name, unsigned *addr) +{ + unsigned radtbl[2]; + + if(!text_fit(tr, *addr, 0, 8)) /* No text space used */ + return 0; + + rld_code_naddr(tr, RLD_LOCDEF, 2); /* RLD code for "location + counter def" with no offset */ + + rad50x2(name, radtbl); + rld_word(tr, radtbl[0]); /* Set current section name */ + rld_word(tr, radtbl[1]); + rld_word(tr, *addr); /* Set current location addr */ + + if(!text_flush(tr)) /* Flush that block out. */ + return 0; + + text_init(tr, tr->fp, *addr); /* Set new text address */ + + return 1; +} + +/* Modify current program counter, assuming current PSECT */ +/* Location counter modification is similarly weird */ +/* (I wonder - why is this RLD code even here? TEXT records contain + thair own start address.) */ + +int text_modify_location(TEXT_RLD *tr, unsigned *addr) +{ + if(!text_fit(tr, *addr, 0, 4)) /* No text space used */ + return 0; + + rld_code_naddr(tr, RLD_LOCMOD, 2); /* RLD code for "location + counter mod" with no offset */ + rld_word(tr, *addr); /* Set current location addr */ + + if(!text_flush(tr)) /* Flush that block out. */ + return 0; + text_init(tr, tr->fp, *addr); /* Set new text address */ + + return 1; +} + +/* write two words containing program limits (the .LIMIT directive) */ + +int text_limits(TEXT_RLD *tr, unsigned *addr) +{ + if(!text_fit(tr, *addr, 4, 2)) + return 0; + + text_word_i(tr, 0, 2); + text_word_i(tr, 0, 2); + rld_code(tr, RLD_LIMITS, *addr, 2); + + *addr += 4; + + return 1; +} + +/* write a word which is the start address of a different PSECT */ + +int text_psect_word(TEXT_RLD *tr, + unsigned *addr, int size, + unsigned word, char *name) +{ + unsigned radtbl[2]; + + if(!text_fit(tr, *addr, size, 6)) + return 0; + + text_word_i(tr, word, size); + + rld_code(tr, RLD_PSECT, *addr, size); + + rad50x2(name, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + + *addr += size; + + return 1; +} + +/* write a word which is an offset from the start of a different PSECT */ + +/* Optimizes to text_psect_word when offset is zero */ + +int text_psect_offset_word(TEXT_RLD *tr, + unsigned *addr, int size, + unsigned word, char *name) +{ + unsigned radtbl[2]; + + if(word == 0) + return text_psect_word(tr, addr, size, word, name); + + if(!text_fit(tr, *addr, size, 8)) + return 0; + + text_word_i(tr, word, size); + + rld_code(tr, RLD_PSECT_OFFSET, *addr, size); + + rad50x2(name, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + rld_word(tr, word); + + *addr += size; + + return 1; +} + +/* write a word which is the address of a different PSECT, PC-relative */ + +int text_psect_displaced_word(TEXT_RLD *tr, unsigned *addr, + int size, unsigned word, char *name) +{ + unsigned radtbl[2]; + + if(!text_fit(tr, *addr, size, 6)) + return 0; + + text_word_i(tr, word, size); + + rld_code(tr, RLD_PSECT_DISP, *addr, size); + + rad50x2(name, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + + *addr += size; + + return 1; +} + +/* write a word which is an offset from the address of a different + PSECT, PC-relative */ + +/* Optimizes to text_psect_displaced_word when offset is zero */ + +int text_psect_displaced_offset_word(TEXT_RLD *tr, + unsigned *addr, int size, + unsigned word, char *name) +{ + unsigned radtbl[2]; + + if(word == 0) + return text_psect_displaced_word(tr, addr, size, word, name); + + if(!text_fit(tr, *addr, size, 8)) + return 0; + + text_word_i(tr, word, size); + + rld_code(tr, RLD_PSECT_OFFSET_DISP, *addr, size); + + rad50x2(name, radtbl); + rld_word(tr, radtbl[0]); + rld_word(tr, radtbl[1]); + rld_word(tr, word); + + *addr += size; + + return 1; +} + +/* complex relocation! */ + +/* A complex relocation expression is where a piece of code is fed to + the linker asking it to do some math for you, and store the result + in a program word. The code is a stack-based language. */ + +/* complex_begin initializes a TEXT_COMPLEX */ + +void text_complex_begin(TEXT_COMPLEX *tx) +{ + tx->len = 0; +} + +/* text_complex_fit checks if a complex expression will fit and + returns a pointer to it's location */ + +static char *text_complex_fit(TEXT_COMPLEX *tx, int size) +{ + int len; + + if(tx->len + size > sizeof(tx->accum)) + return NULL; /* Expression has grown too complex. */ + + len = tx->len; + + tx->len += size; + + return tx->accum + len; +} + +/* text_complex_byte stores a single byte. */ + +static int text_complex_byte(TEXT_COMPLEX *tx, unsigned byte) +{ + char *cp = text_complex_fit(tx, 1); + if(!cp) + return 0; + *cp = byte; + return 1; +} + +/* text_complex_add - add top two stack elements */ + +int text_complex_add(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_ADD); +} + +/* text_complex_sub - subtract top two stack elements. */ +/* You know, I think these function labels are self-explanatory... */ + +int text_complex_sub(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_SUB); +} + +int text_complex_mul(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_MUL); +} + +int text_complex_div(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_DIV); +} + +int text_complex_and(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_AND); +} + +int text_complex_or(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_OR); +} + +int text_complex_xor(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_XOR); +} + +int text_complex_com(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_COM); +} + +int text_complex_neg(TEXT_COMPLEX *tx) +{ + return text_complex_byte(tx, CPLX_NEG); +} + +/* text_complex_lit pushes a literal value to the stack. */ + +int text_complex_lit(TEXT_COMPLEX *tx, unsigned word) +{ + char *cp = text_complex_fit(tx, 3); + if(!cp) + return 0; + *cp++ = CPLX_CONST; + *cp++ = word & 0xff; + *cp = (word >> 8) & 0xff; + return 1; +} + +/* text_complex_global pushes the value of a global variable to the + stack */ + +int text_complex_global(TEXT_COMPLEX *tx, char *name) +{ + unsigned radtbl[2]; + char *cp = text_complex_fit(tx, 5); + if(!cp) + return 0; + + rad50x2(name, radtbl); + *cp++ = CPLX_GLOBAL; + *cp++ = radtbl[0] & 0xff; + *cp++ = (radtbl[0] >> 8) & 0xff; + *cp++ = radtbl[1] & 0xff; + *cp = (radtbl[1] >> 8) & 0xff; + return 1; +} + +/* text_complex_psect pushes the value of an offset into a PSECT to + the stack. */ + +/* What was not documented in the Software Support manual is that + PSECT "sect" numbers are assigned in the order they appear in the + source program, and the order they appear in the GSD. i.e. the + first PSECT GSD is assigned sector 0 (which is always the default + absolute section so that's a bad example), the next sector 1, + etc. */ + +int text_complex_psect(TEXT_COMPLEX *tx, unsigned sect, unsigned offset) +{ + char *cp = text_complex_fit(tx, 4); + if(!cp) + return 0; + *cp++ = CPLX_REL; + *cp++ = sect & 0xff; + *cp++ = offset & 0xff; + *cp = (offset >> 8) & 0xff; + return 1; +} + +/* text_complex_commit - store the result of the complex expression + and end the RLD code. */ + +int text_complex_commit(TEXT_RLD *tr, unsigned *addr, + int size, TEXT_COMPLEX *tx, unsigned word) +{ + int i; + + text_complex_byte(tx, CPLX_STORE); + + if(!text_fit(tr, *addr, size, tx->len + 2)) + return 0; + + rld_code(tr, RLD_COMPLEX, *addr, size); + + for(i = 0; i < tx->len; i++) + rld_byte(tr, tx->accum[i]); + + text_word_i(tr, word, size); + + *addr += size; + + return 1; +} + +/* text_complex_commit_displaced - store the result of the complex + expression, relative to the current PC, and end the RLD code */ + +int text_complex_commit_displaced(TEXT_RLD *tr, + unsigned *addr, int size, + TEXT_COMPLEX *tx, unsigned word) +{ + int i; + + text_complex_byte(tx, CPLX_STORE_DISP); + + if(!text_fit(tr, *addr, size, tx->len + 2)) + return 0; + + rld_code(tr, RLD_COMPLEX, *addr, size); + + for(i = 0; i < tx->len; i++) + rld_byte(tr, tx->accum[i]); + + text_word_i(tr, word, size); + + *addr += size; + + return 1; +} + +/* Write end-of-object-module to file. */ + +int write_endmod(FILE *fp) +{ + char endmod[2] = { OBJ_ENDMOD, 0 }; + return writerec(fp, endmod, 2); +} diff --git a/crossassemblers/macro11/object.h b/crossassemblers/macro11/object.h new file mode 100644 index 0000000..9411a03 --- /dev/null +++ b/crossassemblers/macro11/object.h @@ -0,0 +1,210 @@ +#ifndef OBJECT_H +#define OBJECT_H + +/* Object file constant definitions */ + +/* + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#define FBR_LEAD1 1 /* The byte value that defines the + beginning of a formatted binary + record */ +#define FBR_LEAD2 0 /* Followed by a 0 */ + /* Followed by two bytes length */ + /* Followed by (length-4) bytes data */ + /* Followed by a 1 byte checksum */ + /* which is the negative sum of all + preceeding bytes */ + +#define OBJ_GSD 01 /* GSD (Global symbol directory) */ +#define OBJ_ENDGSD 02 /* ENDGSD */ +#define OBJ_TEXT 03 /* TEXT */ +#define OBJ_RLD 04 /* RLD (Relocation directory) */ +#define OBJ_ISD 05 /* ISD (Internal symbol directory, + currently unused) */ +#define OBJ_ENDMOD 06 /* ENDMOD (End of object module) */ +#define OBJ_LIBHDR 07 /* LIBHDR (Object Library header) */ +#define OBJ_LIBEND 010 /* LIBEND (Object Library header end) */ + +#define GSD_MODNAME 00 /* Module name */ +#define GSD_CSECT 01 /* Control section name */ +#define GSD_ISN 02 /* Internal symbol name */ +#define GSD_XFER 03 /* Transfer address */ +#define GSD_GLOBAL 04 /* Global symbol definition/reference */ +#define GSD_PSECT 05 /* PSECT name */ +#define GSD_IDENT 06 /* IDENT */ +#define GSD_VSECT 07 /* VSECT (Virtual array declaration) */ + +#define GLOBAL_WEAK 01 /* GLOBAL is weak, else strong */ +#define GLOBAL_DEF 010 /* GLOBAL is definition, else reference */ +#define GLOBAL_REL 040 /* GLOBAL is relative, else absolute */ + +#define PSECT_SAV 001 /* PSECT is a root section, else overlay */ +#define PSECT_COM 004 /* PSECT is merged common area, else + contatenated */ +#define PSECT_RO 020 /* PSECT is read-only, else R/W */ +#define PSECT_REL 040 /* PSECT is relative, else absolute + (absolute implies PSECT_COM) */ +#define PSECT_GBL 0100 /* PSECT is overlay-global, else + overlay-local */ +#define PSECT_DATA 0200 /* PSECT contains data, else instructions */ + +#define RLD_INT 01 /* "Internal relocation" */ +#define RLD_GLOBAL 02 /* "Global relocation" */ +#define RLD_INT_DISP 03 /* "Internal displaced" */ +#define RLD_GLOBAL_DISP 04 /* "Global displaced" */ +#define RLD_GLOBAL_OFFSET 05 /* "Global additive" */ +#define RLD_GLOBAL_OFFSET_DISP 06 /* "Global additive displaced" */ +#define RLD_LOCDEF 07 /* "Location counter definition" */ +#define RLD_LOCMOD 010 /* "Location counter modification" */ +#define RLD_LIMITS 011 /* ".LIMIT" */ +#define RLD_PSECT 012 /* "P-sect" */ +#define RLD_PSECT_DISP 014 /* "P-sect displaced" */ +#define RLD_PSECT_OFFSET 015 /* "P-sect additive" */ +#define RLD_PSECT_OFFSET_DISP 016 /* "P-sect additive displaced" */ +#define RLD_COMPLEX 017 /* "Complex" */ + +#define RLD_BYTE 0200 /* RLD modifies a byte, else a word */ + +/* Note: complex relocation is not well documented (in particular, no effort + is made to define a section's "sector number"), but I'll just guess + it's a stack language. */ + +#define CPLX_NOP 00 /* NOP - used for padding */ +#define CPLX_ADD 01 +#define CPLX_SUB 02 +#define CPLX_MUL 03 +#define CPLX_DIV 04 +#define CPLX_AND 05 +#define CPLX_OR 06 +#define CPLX_XOR 07 +#define CPLX_NEG 010 +#define CPLX_COM 011 +#define CPLX_STORE 012 /* Store result, terminate complex string. */ +#define CPLX_STORE_DISP 013 /* Store result PC-relative, terminate */ +#define CPLX_GLOBAL 016 /* Followed by four bytes RAD50 global name */ +#define CPLX_REL 017 /* Followed by one byte "sector + number" and two bytes offset */ +#define CPLX_CONST 020 /* Followed by two bytes constant value */ + +typedef struct gsd +{ + FILE *fp; /* The file assigned for output */ + char buf[122]; /* space for 15 GSD entries */ + int offset; /* Current buffer for GSD entries */ +} GSD; + +void gsd_init(GSD *gsd, FILE *fp); +int gsd_flush(GSD *gsd); +int gsd_mod(GSD *gsd, char *modname); +int gsd_csect(GSD *gsd, char *sectname, int size); +int gsd_intname(GSD *gsd, char *name, unsigned value); +int gsd_xfer(GSD *gsd, char *name, unsigned value); +int gsd_global(GSD *gsd, char *name, int flags, unsigned value); +int gsd_psect(GSD *gsd, char *name, int flags, int size); +int gsd_ident(GSD *gsd, char *name); +int gsd_virt(GSD *gsd, char *name, int size); +int gsd_end(GSD *gsd); + +typedef struct text_rld +{ + FILE *fp; /* The object file, or NULL */ + char text[128]; /* text buffer */ + unsigned txt_addr; /* The base text address */ + int txt_offset; /* Current text offset */ + char rld[128]; /* RLD buffer */ + int rld_offset; /* Current RLD offset */ +} TEXT_RLD; + +void text_init(TEXT_RLD *tr, FILE *fp, unsigned addr); +int text_flush(TEXT_RLD *tr); +int text_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word); +int text_internal_word(TEXT_RLD *tr, unsigned *addr, int size, + unsigned word); +int text_global_word(TEXT_RLD *tr, unsigned *addr, int size, + unsigned word, char *global); +int text_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, + unsigned word); +int text_global_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, + unsigned word, char *global); +int text_global_offset_word(TEXT_RLD *tr, unsigned *addr, int size, + unsigned word, char *global); +int text_global_displaced_offset_word(TEXT_RLD *tr, unsigned *addr, + int size, unsigned word, + char *global); +int text_define_location(TEXT_RLD *tr, char *name, + unsigned *addr); +int text_modify_location(TEXT_RLD *tr, unsigned *addr); +int text_limits(TEXT_RLD *tr, unsigned *addr); +int text_psect_word(TEXT_RLD *tr, unsigned *addr, int size, + unsigned word, char *name); +int text_psect_offset_word(TEXT_RLD *tr, unsigned *addr, + int size, unsigned word, char *name); +int text_psect_displaced_word(TEXT_RLD *tr, unsigned *addr, + int size, unsigned word, char *name); +int text_psect_displaced_offset_word(TEXT_RLD *tr, unsigned *addr, + int size, unsigned word, + char *name); + +typedef struct text_complex +{ + char accum[126]; + int len; +} TEXT_COMPLEX; + +void text_complex_begin(TEXT_COMPLEX *tx); +int text_complex_add(TEXT_COMPLEX *tx); +int text_complex_sub(TEXT_COMPLEX *tx); +int text_complex_mul(TEXT_COMPLEX *tx); +int text_complex_div(TEXT_COMPLEX *tx); +int text_complex_and(TEXT_COMPLEX *tx); +int text_complex_or(TEXT_COMPLEX *tx); +int text_complex_xor(TEXT_COMPLEX *tx); +int text_complex_com(TEXT_COMPLEX *tx); +int text_complex_neg(TEXT_COMPLEX *tx); +int text_complex_lit(TEXT_COMPLEX *tx, unsigned word); +int text_complex_global(TEXT_COMPLEX *tx, char *name); +int text_complex_psect(TEXT_COMPLEX *tx, unsigned sect, + unsigned offset); +int text_complex_commit(TEXT_RLD *tr, unsigned *addr, + int size, TEXT_COMPLEX *tx, + unsigned word); +int text_complex_commit_displaced(TEXT_RLD *tr, unsigned *addr, + int size, TEXT_COMPLEX *tx, + unsigned word); + +int write_endmod(FILE *fp); + +#endif /* OBJECT_J */ diff --git a/crossassemblers/macro11/rad50.c b/crossassemblers/macro11/rad50.c new file mode 100644 index 0000000..6d6f2c5 --- /dev/null +++ b/crossassemblers/macro11/rad50.c @@ -0,0 +1,117 @@ +/* Functions to convert RAD50 to or from ASCII. */ + +/* +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#include +#include +#include + +#include "rad50.h" + +static char radtbl[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$. 0123456789"; + +/* rad50 converts from 0 to 3 ASCII (or EBCDIC, if your compiler is so + inclined) characters into a RAD50 word. */ + +unsigned rad50(char *cp, char **endp) +{ + unsigned long acc = 0; + char *rp; + + if(endp) + *endp = cp; + + if(!*cp) /* Got to check for end-of-string + manually, because strchr will call + it a hit. :-/ */ + return acc; + + rp = strchr(radtbl, toupper(*cp)); + if(rp == NULL) /* Not a RAD50 character */ + return acc; + acc = (rp - radtbl) * 03100; /* Convert */ + cp++; + + /* Now, do the same thing two more times... */ + + if(endp) + *endp = cp; + if(!*cp) + return acc; + rp = strchr(radtbl, toupper(*cp)); + if(rp == NULL) + return acc; + acc += (rp - radtbl) * 050; + + cp++; + if(endp) + *endp = cp; + if(!*cp) + return acc; + rp = strchr(radtbl, toupper(*cp)); + if(rp == NULL) + return acc; + acc += (rp - radtbl); + + cp++; + if(endp) + *endp = cp; + + return acc; /* Done. */ +} + +/* rad50x2 - converts from 0 to 6 characters into two words of RAD50. */ + +void rad50x2(char *cp, unsigned *rp) +{ + *rp++ = rad50(cp, &cp); + *rp = 0; + if(*cp) + *rp = rad50(cp, &cp); +} + +/* unrad50 - converts a RAD50 word to three characters of ASCII. */ + +void unrad50(unsigned word, char *cp) +{ + if(word < 0175000) /* Is it legal RAD50? */ + { + cp[0] = radtbl[word / 03100]; + cp[1] = radtbl[(word / 050) % 050]; + cp[2] = radtbl[word % 050]; + } + else + cp[0] = cp[1] = cp[2] = ' '; +} diff --git a/crossassemblers/macro11/rad50.h b/crossassemblers/macro11/rad50.h new file mode 100644 index 0000000..9a60a01 --- /dev/null +++ b/crossassemblers/macro11/rad50.h @@ -0,0 +1,43 @@ +#ifndef RAD50_H +#define RAD50_H + +/* +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +extern unsigned rad50(char *cp, char **endp); + +extern void rad50x2(char *cp, unsigned *rp); + +extern void unrad50(unsigned word, char *cp); + +#endif /* RAD50_H */ diff --git a/crossassemblers/macro11/stream2.c b/crossassemblers/macro11/stream2.c new file mode 100644 index 0000000..edfbcf2 --- /dev/null +++ b/crossassemblers/macro11/stream2.c @@ -0,0 +1,380 @@ +/* functions for managing a stack of file and buffer input streams. */ + +/* + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "macro11.h" + +#include "stream2.h" + +/* BUFFER functions */ + +/* new_buffer allocates a new buffer */ + +BUFFER *new_buffer(void) +{ + BUFFER *buf = memcheck(malloc(sizeof(BUFFER))); + buf->length = 0; + buf->size = 0; + buf->use = 1; + buf->buffer = NULL; + return buf; +} + +/* buffer_resize makes the buffer at least the requested size. */ +/* If the buffer is already larger, then it will attempt */ +/* to shrink it. */ + +void buffer_resize(BUFFER *buff, int size) +{ + buff->size = size; + buff->length = size; + + if(size == 0) + { + free(buff->buffer); + buff->buffer = NULL; + } + else + { + if(buff->buffer == NULL) + buff->buffer = memcheck(malloc(buff->size)); + else + buff->buffer = memcheck(realloc(buff->buffer, buff->size)); + } +} + +/* buffer_clone makes a copy of a buffer */ +/* Basically it increases the use count */ + +BUFFER *buffer_clone(BUFFER *from) +{ + if(from) + from->use++; + return from; +} + +/* buffer_free frees a buffer */ +/* It decreases the use count, and if zero, */ +/* frees the memory. */ + +void buffer_free(BUFFER *buf) +{ + if(buf) + { + if(--(buf->use) == 0) + { + free(buf->buffer); + free(buf); + } + } +} + +/* Append characters to the buffer. */ + +void buffer_appendn(BUFFER *buf, char *str, int len) +{ + int needed = buf->length + len + 1; + + if(needed >= buf->size) + { + buf->size = needed + GROWBUF_INCR; + + if(buf->buffer == NULL) + buf->buffer = memcheck(malloc(buf->size)); + else + buf->buffer = memcheck(realloc(buf->buffer, buf->size)); + } + + memcpy(buf->buffer + buf->length, str, len); + buf->length += len; + buf->buffer[buf->length] = 0; +} + +/* append a text line (zero or newline-delimited) */ + +void buffer_append_line(BUFFER *buf, char *str) +{ + char *nl; + if((nl = strchr(str, '\n')) != NULL) + buffer_appendn(buf, str, nl - str + 1); + else + buffer_appendn(buf, str, strlen(str)); +} + +/* Base STREAM class methods */ + +/* stream_construct initializes a newly allocated STREAM */ + +void stream_construct(STREAM *str, char *name) +{ + str->line = 0; + str->name = memcheck(strdup(name)); + str->next = NULL; + str->vtbl = NULL; +} + +/* stream_delete destroys and deletes (frees) a STREAM */ + +void stream_delete(STREAM *str) +{ + free(str->name); + free(str); +} + +/* *** class BUFFER_STREAM implementation */ + +/* STREAM::gets for a buffer stream */ + +char *buffer_stream_gets(STREAM *str) +{ + char *nl; + char *cp; + BUFFER_STREAM *bstr = (BUFFER_STREAM *)str; + BUFFER *buf = bstr->buffer; + + if(buf == NULL) + return NULL; /* No buffer */ + + if(bstr->offset >= buf->length) + return NULL; + + cp = buf->buffer + bstr->offset; + + /* Find the next line in preparation for the next call */ + + nl = memchr(cp, '\n', buf->length - bstr->offset); + + if(nl) + nl++; + + bstr->offset = nl - buf->buffer; + str->line++; + + return cp; +} + +/* STREAM::close for a buffer stream */ + +void buffer_stream_delete(STREAM *str) +{ + BUFFER_STREAM *bstr = (BUFFER_STREAM *)str; + buffer_free(bstr->buffer); + stream_delete(str); +} + +/* STREAM::rewind for a buffer stream */ + +void buffer_stream_rewind(STREAM *str) +{ + BUFFER_STREAM *bstr = (BUFFER_STREAM *)str; + bstr->offset = 0; + str->line = 0; +} + +/* BUFFER_STREAM vtbl */ + +STREAM_VTBL buffer_stream_vtbl = { buffer_stream_delete, + buffer_stream_gets, + buffer_stream_rewind }; + +void buffer_stream_construct(BUFFER_STREAM *bstr, BUFFER *buf, char *name) +{ + bstr->stream.vtbl = &buffer_stream_vtbl; + + bstr->stream.name = memcheck(strdup(name)); + + bstr->buffer = buffer_clone(buf); + bstr->offset = 0; + bstr->stream.line = 0; +} + +void buffer_stream_set_buffer(BUFFER_STREAM *bstr, BUFFER *buf) +{ + if(bstr->buffer) + buffer_free(bstr->buffer); + bstr->buffer = buffer_clone(buf); + bstr->offset = 0; +} + +/* new_buffer_stream clones the given buffer, gives it the name, */ +/* and creates a BUFFER_STREAM to reference it */ + +STREAM *new_buffer_stream(BUFFER *buf, char *name) +{ + BUFFER_STREAM *bstr = memcheck(malloc(sizeof(BUFFER_STREAM))); + + buffer_stream_construct(bstr, buf, name); + return &bstr->stream; +} + +/* *** FILE_STREAM implementation */ + +/* Implement STREAM::gets for a file stream */ + +static char *file_gets(STREAM *str) +{ + int i, c; + FILE_STREAM *fstr = (FILE_STREAM *)str; + + if(fstr->fp == NULL) + return NULL; + + if(feof(fstr->fp)) + return NULL; + + /* Read single characters, end of line when '\n' or '\f' hit */ + + i = 0; + while(c = fgetc(fstr->fp), + c != '\n' && c != '\f' && c != EOF) + { + if(c == 0) + continue; /* Don't buffer zeros */ + if(c == '\r') + continue; /* Don't buffer carriage returns either */ + if(i < STREAM_BUFFER_SIZE - 2) + fstr->buffer[i++] = c; + } + + fstr->buffer[i++] = '\n'; /* Silently transform formfeeds + into newlines */ + fstr->buffer[i] = 0; + + if(c == '\n') + fstr->stream.line++; /* Count a line */ + + return fstr->buffer; +} + +/* Implement STREAM::destroy for a file stream */ + +void file_destroy(STREAM *str) +{ + FILE_STREAM *fstr = (FILE_STREAM *)str; + fclose(fstr->fp); + free(fstr->buffer); + stream_delete(str); +} + +/* Implement STREAM::rewind for a file stream */ + +void file_rewind(STREAM *str) +{ + FILE_STREAM *fstr = (FILE_STREAM *)str; + rewind(fstr->fp); + str->line = 0; +} + +static STREAM_VTBL file_stream_vtbl = { file_destroy, file_gets, + file_rewind }; + +/* Prepare and open a stream from a file. */ + +STREAM *new_file_stream(char *filename) +{ + FILE *fp; + FILE_STREAM *str; + + fp = fopen(filename, "r"); + if(fp == NULL) + return NULL; + + str = memcheck(malloc(sizeof(FILE_STREAM))); + + str->stream.vtbl = &file_stream_vtbl; + str->stream.name = memcheck(strdup(filename)); + str->buffer = memcheck(malloc(STREAM_BUFFER_SIZE)); + str->fp = fp; + str->stream.line = 0; + + return &str->stream; +} + +/* STACK functions */ + +/* stack_init prepares a stack */ + +void stack_init(STACK *stack) +{ + stack->top = NULL; /* Too simple */ +} + +/* stack_pop removes and deletes the topmost STRAM on the stack */ + +void stack_pop(STACK *stack) +{ + STREAM *top = stack->top; + STREAM *next = top->next; + + top->vtbl->delete(top); + stack->top = next; +} + +/* stack_push pushes a STREAM onto the top of the stack */ + +void stack_push(STACK *stack, STREAM *str) +{ + str->next = stack->top; + stack->top = str; +} + +/* stack_gets calls vtbl->gets for the topmost stack entry. When + topmost streams indicate they're exhausted, they are popped and + deleted, until the stack is exhausted. */ + +char *stack_gets(STACK *stack) +{ + char *line; + + if(stack->top == NULL) + return NULL; + + while((line = stack->top->vtbl->gets(stack->top)) == NULL) + { + stack_pop(stack); + if(stack->top == NULL) + return NULL; + } + + return line; +} diff --git a/crossassemblers/macro11/stream2.h b/crossassemblers/macro11/stream2.h new file mode 100644 index 0000000..0c28440 --- /dev/null +++ b/crossassemblers/macro11/stream2.h @@ -0,0 +1,110 @@ +#ifndef STREAM2_H +#define STREAM2_H + +/* + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +struct stream; + +typedef struct stream_vtbl +{ + void (*delete)(struct stream *stream); // Destructor + char *(*gets)(struct stream *stream); // "gets" function + void (*rewind)(struct stream *stream); // "rewind" function +} STREAM_VTBL; + +typedef struct stream +{ + STREAM_VTBL *vtbl; // Pointer to dispatch table + char *name; // Stream name + int line; // Current line number in stream + struct stream *next; // Next stream in stack +} STREAM; + +typedef struct file_stream +{ + STREAM stream; // Base class + FILE *fp; // File pointer + char *buffer; // Line buffer +} FILE_STREAM; + +typedef struct buffer +{ + char *buffer; // Pointer to text + int size; // Size of buffer + int length; // Occupied size of buffer + int use; // Number of users of buffer +} BUFFER; + +#define GROWBUF_INCR 1024 // Buffers grow by leaps and bounds + +typedef struct buffer_stream +{ + STREAM stream; // Base class + BUFFER *buffer; // text buffer + int offset; // Current read offset +} BUFFER_STREAM; + +typedef struct stack +{ + STREAM *top; // Top of stacked stream pieces +} STACK; + +#define STREAM_BUFFER_SIZE 1024 // This limits the max size of an input line. +BUFFER *new_buffer(void); +BUFFER *buffer_clone(BUFFER *from); +void buffer_resize(BUFFER *buff, int size); +void buffer_free(BUFFER *buf); +void buffer_appendn(BUFFER *buf, char *str, int len); +void buffer_append_line(BUFFER *buf, char *str); + +STREAM *new_buffer_stream(BUFFER *buf, char *name); +void buffer_stream_set_buffer(BUFFER_STREAM *bstr, BUFFER *buf); + +/* Provide these so that macro11 can derive from a BUFFER_STREAM */ +extern STREAM_VTBL buffer_stream_vtbl; +void buffer_stream_construct(BUFFER_STREAM *bstr, BUFFER *buf, char *name); +char *buffer_stream_gets(STREAM *str); +void buffer_stream_delete(STREAM *str); +void buffer_stream_rewind(STREAM *str); + +STREAM *new_file_stream(char *filename); + +void stack_init(STACK *stack); +void stack_push(STACK *stack, STREAM *str); +void stack_pop(STACK *stack); +char *stack_gets(STACK *stack); + +#endif /* STREAM2_H */ diff --git a/crossassemblers/macro11/todo b/crossassemblers/macro11/todo new file mode 100644 index 0000000..d922637 --- /dev/null +++ b/crossassemblers/macro11/todo @@ -0,0 +1,57 @@ +I was not able to locate a Macro-11 language reference manual any more +recent than for RT11 version *3*, so I used that plus my recollection +of more modern features. It was enough to get the RT11 V5.4 kernel +built, plus a significant chunk of our own code. + +The biggest missing feature is full featured listings. The .LIST and +.NLIST directives are ignored, as is .SBTTL. No table of contents is +accumulated or printed. No symbol cross referencing is done (most +likely I'll just write a CTAGS file, not a cross reference listing). + +Many errors still go unchecked. Off the top of my head, I recall that +object and listing file output errors are ignored. + +.FLT4 format may be inaccurate in the low bits. This is because IEEE +64 bit format has two fewer mantissa bits than 64 bit PDP-11 format. +Without writing soft-float routines, there's not much I can do abbout +it. + +Expression math is done in native width, almost certainly 32 bits. +Truncation to 16 bits is done only for listing and output. This may +make some output differ in the presence of 16-bit overflows and +underflows. I don't think this needs fixing. + +.REM blocks containing code can screw up .MACRO, .REPT, .IRP, .IRPC. +read_body in macro11.c would need to be able to parse and ignore .REM +blocks. + +Need to search a path for the .INCLUDE directive. Right now it only +takes a complete file name. And most likely, existing code will have +RT-11 style file names; I don't know what to do about that, except put +in a device name parser. + +Possible enhancements: + +It would be very simple to make macro11 resolve internal symbols with +more that 6 significant characters. Even so, only the first 6 would +be used for external symbols, and you have to be wary of existing code +that used (for example) .LOOKU rather than .LOOKUP, since these two +would become distinct. + +SYM = 0 +MOV SYM(R0),R0 ; macro11 could optimize SYM(R0) into just (R0) + +I dream of automatically fixing branches out of range. Easy when the +destination is backwards, difficult when it's forwards. I have this +idea: during the first assembly pass, all branches generate a long +branch if the target symbol is undefined, otherwise an "optimized" +branch (short or long) if the target is defined. Then keep a +128-instruction FIFO of generated instructions. Each FIFO entry is +tagged with context and symbol definition as they are pushed to the +FIFO. When an instruction gets pulled from the FIFO because it's more +than 128 words away, the FIFO is searched for long branches that point +to this location; any such are shortened, and any symbols defined +following their location in the stream are adjusted. In the second +assembly pass, the FIFOs aren't used because all jump distances are +known, and the right sized branch (JMP or Bcc) can be generated. + diff --git a/crossassemblers/macro11/util.c b/crossassemblers/macro11/util.c new file mode 100644 index 0000000..6d8320d --- /dev/null +++ b/crossassemblers/macro11/util.c @@ -0,0 +1,172 @@ +/* Some generally useful routines */ +/* The majority of the non-portable code is in here. */ + +/* + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +#include +#include +#include + +#include "util.h" + +#ifdef WIN32 +#include +#include +#define stat _stat +#else +#include +#include +#endif + +/* Sure, the library typically provides some kind of + ultoa or _ultoa function. But since it's merely typical + and not standard, and since the function is so simple, + I'll write my own. + + It's significant feature is that it'll produce representations in + any number base from 2 to 36. +*/ + +char *my_ultoa(unsigned long val, char *buf, unsigned int base) +{ + static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char *strt = buf, *end; + + do + { + *buf++ = digits[val % base]; + val /= base; + } while(val != 0); + + *buf = 0; /* delimit */ + end = buf+1; + + /* Now reverse the bytes */ + + while(buf > strt) + { + char temp; + temp = *--buf; + *buf = *strt; + *strt++ = temp; + } + + return end; +} + +/* Ditto my_ultoa. This actually emits + a signed representation in other number bases. */ + +char *my_ltoa(long val, char *buf, unsigned int base) +{ + unsigned long uval; + + if(val < 0) + uval = -val, *buf++ = '-'; + else + uval = val; + + return my_ultoa(uval, buf, base); +} + +/* + _searchenv is a function provided by the MSVC library that finds + files which may be anywhere along a path which appears in an + environment variable. I duplicate that function for portability. + Note also that mine avoids destination buffer overruns. + + Note: uses strtok. This means it'll screw you up if you + expect your strtok context to remain intact when you use + this function. +*/ + +void my_searchenv(char *name, char *envname, char *hitfile, int hitlen) +{ + char *env; + char *envcopy; + char *cp; + + *hitfile = 0; /* Default failure indication */ + + /* Note: If the given name is absolute, then don't search the + path, but use it as is. */ + + if( +#ifdef WIN32 + strchr(name, ':') != NULL || /* Contain a drive spec? */ + name[0] == '\\' || /* Start with absolute ref? */ +#endif + name[0] == '/') /* Start with absolute ref? */ + { + strncpy(hitfile, name, hitlen); /* Copy to target */ + return; + } + + env = getenv(envname); + if(env == NULL) + return; /* Variable not defined, no search. */ + + envcopy = strdup(env); /* strtok destroys it's text + argument. I don't want the return + value from getenv destroyed. */ + + while((cp = strtok(envcopy, PATHSEP)) != NULL) + { + struct stat info; + char *concat = malloc(strlen(cp) + strlen(name) + 2); + if(concat == NULL) + { + free(envcopy); + return; + } + strcpy(concat, cp); + if(concat[strlen(concat)-1] != '/') + strcat(concat, "/"); + strcat(concat, name); + if(!stat(concat, &info)) + { + /* Copy the file name to hitfile. Assure that it's really + zero-delimited. */ + strncpy(hitfile, concat, hitlen-1); + hitfile[hitlen-1] = 0; + free(envcopy); + return; + } + } + + /* If I fall out of that loop, then hitfile indicates no match, + and return. */ +} diff --git a/crossassemblers/macro11/util.h b/crossassemblers/macro11/util.h new file mode 100644 index 0000000..d6e7790 --- /dev/null +++ b/crossassemblers/macro11/util.h @@ -0,0 +1,56 @@ +#ifndef UTIL_H +#define UTIL_H + +/* + +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +*/ + +char *my_ultoa(unsigned long val, char *buf, unsigned int base); +char *my_ltoa(long val, char *buf, unsigned int base); +void my_searchenv(char *name, char *envname, + char *hitfile, int hitlen); + +/* Cover a few platform-dependencies */ + +#ifdef WIN32 +typedef unsigned __int64 ulong64; +#define strdup _strdup +#define putenv _putenv +#define PATHSEP ";" +#else +typedef unsigned long long ulong64; +#define PATHSEP ":" +#endif + +#endif /* UTIL_H */ diff --git a/crossassemblers/macro7.c b/crossassemblers/macro7.c new file mode 100644 index 0000000..e0b38af --- /dev/null +++ b/crossassemblers/macro7.c @@ -0,0 +1,3083 @@ +/******************************************************************************/ +/* */ +/* Program: MACRO7 */ +/* File: macro7.c */ +/* Author: Gary A. Messenbrink */ +/* MACRO7 modifications: Bob Supnik (:) : error: at Loc = */ +/* */ +/* An example error message is: */ +/* */ +/* bintst.7(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ +/* */ +/* The error diagnostics put in the listing start with a two character */ +/* error code (if appropriate) and a short message. A carat '^' is */ +/* placed under the item in error if appropriate. */ +/* An example error message is: */ +/* */ +/* 17 07616 3000 DAC UNDEF */ +/* UD undefined ^ */ +/* 18 07617 1777 TAD I DUMMY */ +/* */ +/* When an indirect is generated, an at character '@' is placed after the */ +/* the instruction value in the listing as an indicator as follows: */ +/* */ +/* 14 03716 1777@ TAD OFFPAG */ +/* */ +/* Undefined symbols are marked in the symbol table listing by prepending */ +/* a '?' to the symbol. Redefined symbols are marked in the symbol table */ +/* listing by prepending a '#' to the symbol. Examples are: */ +/* */ +/* #REDEF 04567 */ +/* SWITCH 07612 */ +/* ?UNDEF 00000 */ +/* */ +/* Refer to the code for the diagnostic messages generated. */ +/* */ +/* REFERENCES: */ +/* This assembler is based on the pal assember by: */ +/* Douglas Jones and */ +/* Rich Coon */ +/* */ +/* COPYRIGHT NOTICE: */ +/* This is free software. There is no fee for using it. You may make */ +/* any changes that you wish and also give it away. If you can make */ +/* a commercial product out of it, fine, but do not put any limits on */ +/* the purchaser's right to do the same. If you improve it or fix any */ +/* bugs, it would be nice if you told me and offered me a copy of the */ +/* new version. */ +/* */ +/* */ +/* Amendments Record: */ +/* Version Date by Comments */ +/* ------- ------- --- --------------------------------------------------- */ +/* v1.0 12Apr96 GAM Original */ +/* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ +/* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ +/* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ +/* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ +/* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ +/* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ +/* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ +/* v3.0 14Feb97 RMS MACRO8X features. */ +/* */ +/******************************************************************************/ + +#include +#include +#include +#include + +#define LINELEN 96 +#define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ +#define NAMELEN 128 +#define SYMBOL_COLUMNS 5 +#define SYMLEN 7 +#define SYMBOL_TABLE_SIZE 8192 +#define MAC_MAX_ARGS 20 /* Must be < 26 */ +#define MAC_MAX_LENGTH 8192 +#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ +#define TITLELEN 63 +#define XREF_COLUMNS 8 + +#define ADDRESS_FIELD 0017777 +#define LIT_BASE 0017400 +#define INDIRECT_BIT 0020000 +#define LAST_PAGE_LOC 0017777 +#define OP_CODE 0740000 + +/* Macro to get the number of elements in an array. */ +#define DIM(a) (sizeof(a)/sizeof(a[0])) + +/* Macro to get the address plus one of the end of an array. */ +#define BEYOND(a) ((a) + DIM(A)) + +#define is_blank(c) ((c==' ') || (c=='\f') || (c=='>')) +#define isend(c) ((c=='\0')|| (c=='\n')) +#define isdone(c) ((c=='/') || (isend(c)) || (c=='\t')) + +/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ +/* (true) if the stated condtion is met. */ +/* Use these to test attributes. The proper bits are extracted and then */ +/* tested. */ +#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) +#define M_DEFINED(s) ((s & DEFINED) == DEFINED) +#define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) +#define M_FIXED(s) ((s & FIXED) == FIXED) +#define M_LABEL(s) ((s & LABEL) == LABEL) +#define M_MRI(s) ((s & MRI) == MRI) +#define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) +#define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) +#define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) +#define M_MACRO(s) ((s & MACRO) == MACRO) +#define M_UNDEFINED(s) (!M_DEFINED(s)) +#define M_NOTRDEF(s) ((s & NOTRDEF) != 0) + +/* This macro is used to test symbols by the conditional assembly pseudo-ops. */ +#define M_DEF(s) (M_DEFINED(s)) +#define M_COND(s) (M_DEFINED(s)) +#define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) + +typedef unsigned char BOOL; +typedef unsigned char BYTE; +typedef int WORD32; + +#ifndef FALSE + #define FALSE 0 + #define TRUE (!FALSE) +#endif + +/* Line listing styles. Used to control listing of lines. */ +enum linestyle_t +{ + LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL +}; +typedef enum linestyle_t LINESTYLE_T; + +/* Symbol Types. */ +/* Note that the names that have FIX as the suffix contain the FIXED bit */ +/* included in the value. */ +/* */ +/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ +/* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ +/* defined or undefined. The condition bit is set when the symbol is defined */ +/* during pass 1 and reset on pass 2 at the location the symbol was defined */ +/* during pass 1. When processing conditionals during pass 2, if the symbol */ +/* is defined and the condition bit is set, the symbol is treated as if it */ +/* were undefined. This gives consistent behavior of the conditional */ +/* pseudo-ops during both pass 1 and pass 2. */ +enum symtyp +{ + UNDEFINED = 0000, + DEFINED = 0001, + FIXED = 0002, + MRI = 0004 | DEFINED, + LABEL = 0010 | DEFINED, + REDEFINED = 0020 | DEFINED, + DUPLICATE = 0040 | DEFINED, + PSEUDO = 0100 | FIXED | DEFINED, + CONDITION = 0200 | DEFINED, + MACRO = 0400 | DEFINED, + MRIFIX = MRI | FIXED | DEFINED, + DEFFIX = DEFINED | FIXED, + NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED +}; +typedef enum symtyp SYMTYP; + +enum pseudo_t +{ + DECIMAL, DEFINE, EJECT, IFDEF, IFNDEF, IFNZERO, IFZERO, + LIST, NOLIST, OCTAL, START, TEXT, TITLE, VFD +}; +typedef enum pseudo_t PSEUDO_T; + +struct sym_t +{ + SYMTYP type; + char name[SYMLEN]; + WORD32 val; + WORD32 xref_index; + WORD32 xref_count; +}; +typedef struct sym_t SYM_T; + +struct lpool_t +{ + WORD32 error; /* True if error message has been printed. */ + WORD32 pool[LIT_BASE]; +}; +typedef struct lpool_t LPOOL_T; + +struct emsg_t +{ + char *list; + char *file; +}; +typedef struct emsg_t EMSG_T; + +struct errsave_t +{ + char *mesg; + WORD32 col; +}; +typedef struct errsave_t ERRSAVE_T; + +/*----------------------------------------------------------------------------*/ + +/* Function Prototypes */ + +int binarySearch( char *name, int start, int symbol_count ); +int copyMacLine( int length, int from, int term, int nargs ); +int compareSymbols( const void *a, const void *b ); +void conditionFalse( void ); +void conditionTrue( void ); +SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); +void endOfBinary( void ); +void errorLexeme( EMSG_T *mesg, WORD32 col ); +void errorMessage( EMSG_T *mesg, WORD32 col ); +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); +SYM_T *eval( void ); +SYM_T *evalSymbol( void ); +void getArgs( int argc, char *argv[] ); +SYM_T *getExpr( void ); +WORD32 getExprs( void ); +WORD32 incrementClc( void ); +WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); +char *lexemeToName( char *name, WORD32 from, WORD32 term ); +void listLine( void ); +SYM_T *lookup( char *name ); +void moveToEndOfLine( void ); +void nextLexBlank( void ); +void nextLexeme( void ); +void onePass( void ); +void printCrossReference( void ); +void printErrorMessages( void ); +void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); +void printPageBreak( void ); +void printPermanentSymbolTable( void ); +void printSymbolTable( void ); +BOOL pseudoOperators( PSEUDO_T val ); +void punchLocObject( WORD32 loc, WORD32 val ); +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); +void punchOutObject( WORD32 loc, WORD32 val ); +void punchLeader( WORD32 count ); +void punchObject( WORD32 val ); +void punchTriplet( WORD32 val ); +void readLine( void ); +void saveError( char *mesg, WORD32 cc ); +BOOL testForLiteralCollision( WORD32 loc ); +void topOfForm( char *title, char *sub_title ); + +/*----------------------------------------------------------------------------*/ + +/* Table of pseudo-ops (directives) which are used to setup the symbol */ +/* table on startup and when the EXPUNGE pseudo-op is executed. */ +SYM_T pseudo[] = +{ + { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ + { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ + { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ + { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ + { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ + { PSEUDO, "IFNZER", IFNZERO }, /* Assemble if symbol value is not 0. */ + { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ + { PSEUDO, "LIST", LIST }, /* Enable listing. */ + { PSEUDO, "NOLIST", NOLIST }, /* Disable listing. */ + { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ + { PSEUDO, "START", START }, /* Set starting address. */ + { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ + { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ + { PSEUDO, "VFD", VFD }, /* Variable field definition. */ +}; + +/* Symbol Table */ +/* The table is put in lexical order on startup, so symbols can be */ +/* inserted as desired into the initial table. */ +#define DAC 0040000 +#define JMP 0600000 +SYM_T permanent_symbols[] = +{ + /* Memory Reference Instructions */ + { MRIFIX, "CAL", 0000000 }, + { MRIFIX, "DAC", 0040000 }, + { MRIFIX, "JMS", 0100000 }, + { MRIFIX, "DZM", 0140000 }, + { MRIFIX, "LAC", 0200000 }, + { MRIFIX, "XOR", 0240000 }, + { MRIFIX, "ADD", 0300000 }, + { MRIFIX, "TAD", 0340000 }, + { MRIFIX, "XCT", 0400000 }, + { MRIFIX, "ISZ", 0440000 }, + { MRIFIX, "AND", 0500000 }, + { MRIFIX, "SAD", 0540000 }, + { MRIFIX, "JMP", 0600000 }, + { MRIFIX, "I", 0020000 }, + { DEFFIX, "EAE", 0640000 }, + { DEFFIX, "IOT", 0700000 }, + { DEFFIX, "OPR", 0740000 }, + { DEFFIX, "LAW", 0760000 }, + { DEFFIX, "LAM", 0777777 }, + /* EAE Microinstructions */ + { DEFFIX, "OSC", 0640001 }, + { DEFFIX, "LACS", 0641001 }, + { DEFFIX, "OMQ", 0640002 }, + { DEFFIX, "LACQ", 0641002 }, + { DEFFIX, "CMQ", 0640004 }, + { DEFFIX, "CLQ", 0650000 }, + { DEFFIX, "LMQ", 0652000 }, + { DEFFIX, "ABS", 0644000 }, + { DEFFIX, "GSM", 0664000 }, + { DEFFIX, "MUL", 0653122 }, + { DEFFIX, "MULS", 0657122 }, + { DEFFIX, "DIV", 0640323 }, + { DEFFIX, "DIVS", 0644323 }, + { DEFFIX, "IDIV", 0653323 }, + { DEFFIX, "IDIVS", 0657323 }, + { DEFFIX, "FRDIV", 0650323 }, + { DEFFIX, "FRDIVS", 0654323 }, + { DEFFIX, "NORM", 0640444 }, + { DEFFIX, "NORMS", 0660444 }, + { DEFFIX, "LRS", 0640500 }, + { DEFFIX, "LRSS", 0660500 }, + { DEFFIX, "LLS", 0640600 }, + { DEFFIX, "LLSS", 0660600 }, + { DEFFIX, "ALS", 0640700 }, + { DEFFIX, "ALSS", 0660700 }, + /* Operate Microinstructions */ + { DEFFIX, "NOP", 0740000 }, + { DEFFIX, "CMA", 0740001 }, + { DEFFIX, "CML", 0740002 }, + { DEFFIX, "OAS", 0740004 }, + { DEFFIX, "LAS", 0750004 }, + { DEFFIX, "RAL", 0740010 }, + { DEFFIX, "RCL", 0744010 }, + { DEFFIX, "RTL", 0742010 }, + { DEFFIX, "RAR", 0740020 }, + { DEFFIX, "RCR", 0744020 }, + { DEFFIX, "RTR", 0742020 }, + { DEFFIX, "HLT", 0740040 }, + { DEFFIX, "XX", 0740040 }, + { DEFFIX, "SMA", 0740100 }, + { DEFFIX, "SZA", 0740200 }, + { DEFFIX, "SNL", 0740400 }, + { DEFFIX, "SKP", 0741000 }, + { DEFFIX, "SPA", 0741100 }, + { DEFFIX, "SNA", 0741200 }, + { DEFFIX, "SZL", 0741400 }, + { DEFFIX, "CLL", 0744000 }, + { DEFFIX, "STL", 0744002 }, + { DEFFIX, "CLA", 0750000 }, + { DEFFIX, "CLC", 0750001 }, + { DEFFIX, "GLK", 0750010 }, + /* CPU IOT's */ + { DEFFIX, "CLSF", 0700001 }, + { DEFFIX, "IOF", 0700002 }, + { DEFFIX, "ION", 0700042 }, + { DEFFIX, "ITON", 0700062 }, + { DEFFIX, "CLOF", 0700004 }, + { DEFFIX, "CLON", 0700044 }, + { DEFFIX, "TTS", 0703301 }, + { DEFFIX, "SKP7", 0703341 }, + { DEFFIX, "CAF", 0703302 }, + { DEFFIX, "SEM", 0707701 }, + { DEFFIX, "EEM", 0707702 }, + { DEFFIX, "EMIR", 0707742 }, + { DEFFIX, "LEM", 0707704 }, + /* High Speed Paper Tape Reader */ + { DEFFIX, "RSF", 0700101 }, + { DEFFIX, "RRB", 0700112 }, + { DEFFIX, "RCF", 0700102 }, + { DEFFIX, "RSA", 0700104 }, + { DEFFIX, "RSB", 0700144 }, + /* High Speed Paper Tape Punch */ + { DEFFIX, "PSF", 0700201 }, + { DEFFIX, "PCF", 0700202 }, + { DEFFIX, "PSA", 0700204 }, + { DEFFIX, "PLS", 0700204 }, + { DEFFIX, "PSB", 0700244 }, + /* Keyboard */ + { DEFFIX, "KSF", 0700301 }, + { DEFFIX, "KRB", 0700312 }, + { DEFFIX, "IORS", 0700314 }, + /* Teleprinter */ + { DEFFIX, "TSF", 0700401 }, + { DEFFIX, "TCF", 0700402 }, + { DEFFIX, "TLS", 0700406 }, + /* Line Printer */ + { DEFINED, "LPSF", 0706501 }, + { DEFINED, "LPCB", 0706502 }, + { DEFINED, "LPB1", 0706566 }, + { DEFINED, "LPB2", 0706526 }, + { DEFINED, "LPB3", 0706546 }, + { DEFINED, "LPSE", 0706601 }, + { DEFINED, "LPCF", 0706602 }, + { DEFINED, "LPPB", 0706606 }, + { DEFINED, "LPLS", 0706626 }, + { DEFINED, "LPPS", 0706646 }, + /* Card Reader */ + { DEFFIX, "CRSF", 0706701 }, + { DEFFIX, "CRRB", 0706712 }, + { DEFFIX, "CRSA", 0706704 }, + { DEFFIX, "CRSB", 0706744 }, + /* DECtape */ + { DEFFIX, "MMDF", 0707501 }, + { DEFFIX, "MMEF", 0707541 }, + { DEFFIX, "MMRD", 0707512 }, + { DEFFIX, "MMWR", 0707504 }, + { DEFFIX, "MMBF", 0707601 }, + { DEFFIX, "MMRS", 0707612 }, + { DEFFIX, "MMLC", 0707604 }, + { DEFFIX, "MMSE", 0707644 }, +}; /* End-of-Symbols for Permanent Symbol Table */ + +/* Global variables */ +SYM_T *symtab; /* Symbol Table */ +int symbol_top; /* Number of entries in symbol table. */ + +SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ +int number_of_fixed_symbols; + +/*----------------------------------------------------------------------------*/ + +WORD32 *xreftab; /* Start of the concordance table. */ + +ERRSAVE_T error_list[20]; +int save_error_count; + +LPOOL_T cp; /* Storage for current page constants. */ + +char s_detected[] = "detected"; +char s_error[] = "error"; +char s_errors[] = "errors"; +char s_no[] = "No"; +char s_page[] = "Page"; +char s_symtable[] = "Symbol Table"; +char s_xref[] = "Cross Reference"; + +/* Assembler diagnostic messages. */ +/* Some attempt has been made to keep continuity with the PAL-III and */ +/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ +/* exists, then the indicator is put in the listing as the first two */ +/* characters of the diagnostic message. The PAL-III indicators where used */ +/* when there was a choice between using MACRO-8 and PAL-III indicators. */ +/* The character pairs and their meanings are: */ +/* DT Duplicate Tag (symbol) */ +/* IC Illegal Character */ +/* ID Illegal Redefinition of a symbol. An attempt was made to give */ +/* a symbol a new value not via =. */ +/* IE Illegal Equals An equal sign was used in the wrong context, */ +/* (e.g., A+B=C, or TAD A+=B) */ +/* II Illegal Indirect An off page reference was made, but a literal */ +/* could not be generated because the indirect bit was already set. */ +/* IR Illegal Reference (address is not on current page or page zero) */ +/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ +/* RD ReDefintion of a symbol */ +/* ST Symbol Table full */ +/* UA Undefined Address (undefined symbol) */ +/* ZE Zero Page Exceeded (see above, or out of space) */ +EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; +EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; +EMSG_T illegal_character = { "IC illegal char", "illegal character" }; +EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; +EMSG_T label_syntax = { "IC label syntax", "label syntax" }; +EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; +EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; +EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; +EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; +EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; +EMSG_T illegal_reference = { "IR off page", "illegal reference" }; +EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; +EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; +EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; +EMSG_T literal_gen_off = { "lit generation off", + "literal generation disabled" }; +EMSG_T literal_overflow = { "PE page exceeded", + "current page literal capacity exceeded" }; +EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; +EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; +EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; +EMSG_T illegal_vfd_value = { "width out of range", + "VFD field width not in range" }; +EMSG_T no_literal_value = { "no value", "No literal value" }; +EMSG_T text_string = { "no delimiter", + "Text string delimiters not matched" }; +EMSG_T lt_expected = { "'<' expected", "'<' expected" }; +EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; +EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; +EMSG_T bad_dummy_arg = { "bad dummy arg", + "Bad dummy argument following DEFINE" }; +EMSG_T macro_too_long = { "macro too long", "Macro too long" }; +EMSG_T no_virtual_memory = { "out of memory", + "Insufficient memory for macro" }; +EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; + +/*----------------------------------------------------------------------------*/ + +FILE *errorfile; +FILE *infile; +FILE *listfile; +FILE *listsave; +FILE *objectfile; +FILE *objectsave; + +char errorpathname[NAMELEN]; +char filename[NAMELEN]; +char listpathname[NAMELEN]; +char objectpathname[NAMELEN]; +char *pathname; +char permpathname[NAMELEN]; + +char mac_buffer[MAC_MAX_LENGTH + 1]; +char *mac_bodies[MAC_TABLE_LENGTH]; +char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; +int mac_arg_pos[26] = { 0 }; + +int list_lineno; +int list_pageno; +char list_title[4*LINELEN]; +BOOL list_title_set; /* Set if TITLE pseudo-op used. */ +char line[4*LINELEN]; /* Input line. */ +int lineno; /* Current line number. */ +char mac_line[4*LINELEN]; /* Saved macro invocation line. */ +int page_lineno; /* print line number on current page. */ +WORD32 listed; /* Listed flag. */ +WORD32 listedsave; + +WORD32 cc; /* Column Counter (char position in line). */ +WORD32 checksum; /* Generated checksum */ +BOOL binary_data_output; /* Set true when data has been output. */ +WORD32 clc; /* Location counter */ +char delimiter; /* Character immediately after eval'd term. */ +BOOL end_of_input; /* End of all input files. */ +int errors; /* Number of errors found so far. */ +BOOL error_in_line; /* TRUE if error on current line. */ +int errors_pass_1; /* Number of errors on pass 1. */ +int filix_curr; /* Index in argv to current input file. */ +int filix_start; /* Start of input files in argv. */ +BOOL indirect_generated; /* TRUE if an off page address generated. */ +WORD32 lexstartprev; /* Where previous lexeme started. */ +WORD32 lextermprev; /* Where previous lexeme ended. */ +WORD32 lexstart; /* Index of current lexeme on line. */ +WORD32 lexterm; /* Index of character after current lexeme. */ +WORD32 lit_loc; /* Base of literal pool. */ +WORD32 mac_cc; /* Saved cc after macro invocation. */ +WORD32 mac_count; /* Total macros defined. */ +char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ +WORD32 maxcc; /* Current line length. */ +BOOL nomac_exp; /* No macro expansions. */ +WORD32 pass; /* Number of current pass. */ +BOOL print_permanent_symbols; +WORD32 radix; /* Default number radix. */ +BOOL rim_mode; /* RIM mode output. */ +int save_argc; /* Saved argc. */ +char **save_argv; /* Saved *argv[]. */ +WORD32 start_addr; /* Saved start address. */ +BOOL symtab_print; /* Print symbol table flag */ +BOOL xref; + +SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ +SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ +SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ + + +/******************************************************************************/ +/* */ +/* Function: main */ +/* */ +/* Synopsis: Starting point. Controls order of assembly. */ +/* */ +/******************************************************************************/ +int main( int argc, char *argv[] ) +{ + int ix; + int space; + + save_argc = argc; + save_argv = argv; + + /* Set the default values for global symbols. */ + binary_data_output = FALSE; + print_permanent_symbols = FALSE; + nomac_exp = TRUE; + rim_mode = TRUE; + symtab_print = FALSE; + xref = FALSE; + pathname = NULL; + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) + { + mac_bodies[ix] = NULL; + } + + /* Get the options and pathnames */ + getArgs( argc, argv ); + + /* Setup the error file in case symbol table overflows while installing the */ + /* permanent symbols. */ + errorfile = fopen( errorpathname, "w" ); + errors = 0; + save_error_count = 0; + pass = 0; /* This is required for symbol table initialization. */ + symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); + + if( symtab == NULL ) + { + fprintf( stderr, "Could not allocate memory for symbol table.\n"); + exit( -1 ); + } + + /* Place end marker in symbol table. */ + symtab[0] = sym_undefined; + symbol_top = 0; + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Enter the pseudo-ops into the symbol table */ + for( ix = 0; ix < DIM( pseudo ); ix++ ) + { + defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); + } + + /* Enter the predefined symbols into the table. */ + /* Also make them part of the permanent symbol table. */ + for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) + { + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type, 0 ); + } + + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Do pass one of the assembly */ + checksum = 0; + pass = 1; + onePass(); + errors_pass_1 = errors; + + /* Set up for pass two */ + errorfile = fopen( errorpathname, "w" ); + objectfile = fopen( objectpathname, "wb" ); + objectsave = objectfile; + + listfile = fopen( listpathname, "w" ); + listsave = listfile; + + punchLeader( 0 ); + checksum = 0; + + /* Do pass two of the assembly */ + errors = 0; + save_error_count = 0; + + if( xref ) + { + /* Get the amount of space that will be required for the concordance. */ + for( space = 0, ix = 0; ix < symbol_top; ix++ ) + { + symtab[ix].xref_index = space; /* Index into concordance table. */ + space += symtab[ix].xref_count + 1; + symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ + + } + /* Allocate the necessary space. */ + xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); + + /* Clear the cross reference space. */ + for( ix = 0; ix < space; ix++ ) + { + xreftab[ix] = 0; + } + } + pass = 2; + onePass(); + + /* Undo effects of NOPUNCH for any following checksum */ + objectfile = objectsave; + + /* Works great for trailer. */ + punchLeader( 1 ); + + /* undo effects of NOLIST for any following output to listing file. */ + listfile = listsave; + + /* Display value of error counter. */ + if( errors == 0 ) + { + fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); + } + else + { + fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( listfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( stderr, " %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + } + + if( symtab_print ) + { + printSymbolTable(); + } + + if( print_permanent_symbols ) + { + printPermanentSymbolTable(); + } + + if( xref ) + { + printCrossReference(); + } + + fclose( objectfile ); + fclose( listfile ); + fclose( errorfile ); + if( errors == 0 && errors_pass_1 == 0 ) + { + remove( errorpathname ); + } + + return( errors != 0 ); +} /* main() */ + +/******************************************************************************/ +/* */ +/* Function: getArgs */ +/* */ +/* Synopsis: Parse command line, set flags accordingly and setup input and */ +/* output files. */ +/* */ +/******************************************************************************/ +void getArgs( int argc, char *argv[] ) +{ + WORD32 len; + WORD32 ix, jx; + + /* Set the defaults */ + errorfile = NULL; + infile = NULL; + listfile = NULL; + listsave = NULL; + objectfile = NULL; + objectsave = NULL; + + for( ix = 1; ix < argc; ix++ ) + { + if( argv[ix][0] == '-' ) + { + for( jx = 1; argv[ix][jx] != 0; jx++ ) + { + switch( argv[ix][jx] ) + { + case 'd': + symtab_print = TRUE; + break; + +/* case 'r': + rim_mode = TRUE; + break; +*/ + case 'm': + nomac_exp = FALSE; + break; + + case 'p': + print_permanent_symbols = TRUE; + break; + + case 'x': + xref = TRUE; + break; + + default: + fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); + fprintf( stderr, " -d -- dump symbol table\n" ); + fprintf( stderr, " -m -- output macro expansions\n" ); +/* fprintf( stderr, " -r -- output rim format file\n" ); */ + fprintf( stderr, " -p -- output permanent symbols to file\n" ); + fprintf( stderr, " -x -- output cross reference to file\n" ); + fflush( stderr ); + exit( -1 ); + } /* end switch */ + } /* end for */ + } + else + { + filix_start = ix; + pathname = argv[ix]; + break; + } + } /* end for */ + + if( pathname == NULL ) + { + fprintf( stderr, "%s: no input file specified\n", argv[0] ); + exit( -1 ); + } + + len = strlen( pathname ); + if( len > NAMELEN - 5 ) + { + fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); + exit( -1 ); + } + + /* Now make the pathnames */ + /* Find last '.', if it exists. */ + jx = len - 1; + while( pathname[jx] != '.' && pathname[jx] != '/' + && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + + switch( pathname[jx] ) + { + case '.': + break; + + case '/': + case '\\': + jx = len; + break; + + default: + break; + } + + /* Add the pathname extensions. */ + strncpy( objectpathname, pathname, jx ); + objectpathname[jx] = '\0'; + strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); + + strncpy( listpathname, pathname, jx ); + listpathname[jx] = '\0'; + strcat( listpathname, ".lst" ); + + strncpy( errorpathname, pathname, jx ); + errorpathname[jx] = '\0'; + strcat( errorpathname, ".err" ); + + strncpy( permpathname, pathname, jx ); + permpathname[jx] = '\0'; + strcat( permpathname, ".prm" ); + + /* Extract the filename from the path. */ + if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) + { + pathname[1] = '\\'; /* MS-DOS style pathname */ + } + + jx = len - 1; + while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + strcpy( filename, &pathname[jx + 1] ); + +} /* getArgs() */ + + +/******************************************************************************/ +/* */ +/* Function: onePass */ +/* */ +/* Synopsis: Do one assembly pass. */ +/* */ +/******************************************************************************/ +void onePass() +{ + BOOL blanks; + int ix; + int jx; + char name[SYMLEN]; + WORD32 newclc; + BOOL scanning_line; + WORD32 start; + SYM_T *sym; + WORD32 term; + WORD32 val; + + clc = 0100; /* Default starting address is 100 octal. */ + start_addr = 0100; /* No starting address. */ + lit_loc = LIT_BASE; /* Literal pool base. */ + mac_count = 0; /* No macros defined. */ + mac_ptr = NULL; /* Not in a macro. */ + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) + { + if ( mac_bodies[ix] ) + { + free( mac_bodies[ix] ); + } + } + cp.error = FALSE; + listed = TRUE; + lineno = 0; + list_pageno = 0; + list_lineno = 0; + list_title_set = FALSE; + page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ + radix = 8; /* Initial radix is octal (base 8). */ + + /* Now open the first input file. */ + end_of_input = FALSE; + filix_curr = filix_start; /* Initialize pointer to input files. */ + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + + while( TRUE ) + { + readLine(); + nextLexeme(); + + scanning_line = TRUE; + while( scanning_line ) + { + if( end_of_input ) + { + endOfBinary(); + fclose( infile ); + return; + } + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case '\t': + nextLexeme(); + break; + + default: + for( jx = lexstart; jx < maxcc; jx++ ) + { + if( is_blank( line[jx] ) || isdone( line[jx] )) break; + } + if( line[jx] == '/') + { + newclc = (getExpr())->val & 077777; + /* Do not change Current Location Counter if an error occurred. */ + if( !error_in_line ) + { + clc = newclc; + } + printLine( line, 0, newclc, LINE_VAL ); + cc = jx + 1; + nextLexeme(); + while( line[lexstart] == '\t' ) nextLexeme(); + break; + } + + switch( line[lexterm] ) + { + case ',': + if( isalpha( line[lexstart] )) + { + /* Use lookup so symbol will not be counted as reference. */ + sym = lookup( lexemeToName( name, lexstart, lexterm )); + if( M_DEFINED( sym->type )) + { + if( sym->val != clc && pass == 2 ) + { + errorSymbol( &duplicate_label, sym->name, lexstart ); + } + sym->type = sym->type | DUPLICATE; + } + /* Must call define on pass 2 to generate concordance. */ + defineLexeme( lexstart, lexterm, clc, LABEL ); + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + nextLexeme(); /* skip label */ + nextLexeme(); /* skip comma */ + while( line[lexstart] == '\t' ) nextLexeme(); + break; + + case '=': + if( isalpha( line[lexstart] )) + { + start = lexstart; + term = lexterm; + delimiter = line[lexterm]; + nextLexBlank(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + val = getExprs(); + defineLexeme( start, term, val, DEFINED ); + printLine( line, 0, val, LINE_VAL ); + } + else + { + errorLexeme( &symbol_syntax, lexstartprev ); + nextLexeme(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + getExprs(); /* skip expression */ + } + while( line[lexstart] == '\t' ) nextLexeme(); + break; + + default: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + val = sym->val; + if( M_MACRO( sym->type )) + { /* Find arguments. */ + blanks = TRUE; /* Expecting blanks. */ + for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) + { + if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; + else if( blanks ) + { + mac_arg_pos[jx++] = cc; + blanks = FALSE; + } + } /* end for */ + for( ; jx < MAC_MAX_ARGS; jx++ ) + { + mac_arg_pos[jx] = 0; + } + for( jx = 0; jx < LINELEN; jx++ ) + { + mac_line[jx] = line[jx]; + } + mac_cc = cc; /* Save line and position in line. */ + mac_ptr = mac_bodies[val]; + if( mac_ptr ) scanning_line = FALSE; + else nextLexeme(); + } /* end if macro */ + else if( M_PSEUDO( sym->type )) + { + nextLexeme(); /* Skip symbol */ + scanning_line = pseudoOperators( (PSEUDO_T)val & 0777777 ); + } + else + { + /* Identifier is not a pseudo-op, interpret as load value */ + punchOutObject( clc, getExprs() & 0777777 ); + incrementClc(); + } + } + else + { + /* Identifier is a value, interpret as load value */ + punchOutObject( clc, getExprs() & 0777777 ); + incrementClc(); + } + break; + } /* end switch */ + break; + } /* end switch */ + } /* end if */ + } /* end while( scanning_line ) */ + } /* end while( TRUE ) */ +} /* onePass() */ + + +/******************************************************************************/ +/* */ +/* Function: getExprs */ +/* */ +/* Synopsis: Or together a list of blank separated expressions, from the */ +/* current lexeme onward. Leave the current lexeme as */ +/* the last one in the list. */ +/* */ +/******************************************************************************/ +WORD32 getExprs() +{ + SYM_T *symv; + SYM_T *symt; + WORD32 temp; + SYMTYP temp_type; + WORD32 value; + SYMTYP value_type; + + symv = getExpr(); + value = symv->val; + value_type = symv->type; + + while( TRUE ) + { + if( isdone( line[lexstart] ) || line[lexstart] == ')' ) + { + return( value ); + } + + /* Interpret space as add */ + symt = getExpr(); + temp = symt->val & 0777777; + temp_type = symt->type; + + switch( value_type ) + { + case MRI: + case MRIFIX: + /* Previous symbol was a Memory Reference Instruction. */ + switch( temp_type ) + { + case MRI: + case MRIFIX: + /* Current symbol is also a Memory Reference Instruction. */ + value |= temp; /* Just OR the MRI instructions. */ + break; + + default: + /* Now have the address part of the MRI instruction. */ + if(( clc & 060000) == ( temp & 060000)) + { + value += ( temp & ADDRESS_FIELD ); /* In range MRI. */ + } + else + { + if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) + { + /* Already indirect, can't generate */ + errorSymbol( &illegal_indirect, symt->name, lexstartprev ); + } + else + { + /* Now fix off page reference. */ + /* Search current page literal pool for needed value. */ + /* Set Indirect */ + value += ( INDIRECT_BIT | insertLiteral( &cp, clc, temp & 077777)); + indirect_generated = TRUE; + } + } + break; + } + break; + + default: + value = value + temp; /* Normal 18 bit value. */ + if( value >= 0777777 ) value = ( value + 1 ) & 0777777; + break; + } + } /* end while */ +} /* getExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getExpr */ +/* */ +/* Synopsis: Get an expression, from the current lexeme onward, leave the */ +/* current lexeme as the one after the expression. Expressions */ +/* contain terminal symbols (identifiers) separated by operators. */ +/* */ +/******************************************************************************/ +SYM_T *getExpr() +{ + delimiter = line[lexterm]; + + if( line[lexstart] == '-' ) + { + nextLexBlank(); + sym_getexpr = *(eval()); + sym_getexpr.val = sym_getexpr.val ^ 0777777; + } + else + { + sym_getexpr = *(eval()); + } + + + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val += (eval())->val; + if( sym_getexpr.val >= 01000000 ) + { + sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; + } + break; + + case '-': /* subtract */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val = sym_getexpr.val + + ( (eval())->val ^ 0777777 ); + if( sym_getexpr.val >= 01000000 ) + { + sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; + } + break; + + case '^': /* multiply */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val *= (eval())->val; + break; + + case '%': /* divide */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val /= (eval())->val; + break; + + case '&': /* and */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val &= (eval())->val; + break; + + case '!': /* or */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val |= (eval())->val; + break; + + default: + if( isend( line[lexstart] )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '/': + case '\t': + case ')': + case '<': + case ':': + case ',': + break; + + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + } + return( &sym_getexpr ); + } + } /* end while */ +} /* getExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: eval */ +/* */ +/* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ +/* */ +/******************************************************************************/ +SYM_T *eval() +{ + WORD32 digit; + WORD32 from; + WORD32 loc; + SYM_T *sym; + WORD32 val; + + val = 0; + + delimiter = line[lexterm]; + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + if( M_UNDEFINED( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &undefined_symbol, sym->name, lexstart ); + } + nextLexeme(); + return( sym ); + } + else if( M_PSEUDO( sym->type )) + { + if( sym->val == DECIMAL ) + { + radix = 10; + } + else if( sym->val == OCTAL ) + { + radix = 8; + } + else if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else if( M_MACRO( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else + { + nextLexeme(); + return( sym ); + } + } + else if( isdigit( line[lexstart] )) + { + from = lexstart; + val = 0; + while( from < lexterm ) + { + if( isdigit( line[from] )) + { + digit = (WORD32) line[from++] - (WORD32) '0'; + if( digit < radix ) + { + val = val * radix + digit; + } + else + { + errorLexeme( &number_not_radix, from - 1 ); + val = 0; + from = lexterm; + } + } + else + { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + from = lexterm; + } + } + nextLexeme(); + sym_eval.val = val; + return( &sym_eval ); + } + else + { + switch( line[lexstart] ) + { + case '"': /* Character literal */ + if( cc + 2 < maxcc ) + { + val = line[lexstart + 1] | 0200; + delimiter = line[lexstart + 2]; + cc = lexstart + 2; + } + else + { + errorMessage( &no_literal_value, lexstart ); + } + nextLexeme(); + break; + + case '.': /* Value of Current Location Counter */ + val = clc; + nextLexeme(); + break; + + case '(': /* Generate literal on current page. */ + nextLexBlank(); /* Skip paren */ + val = getExprs() & 0777777; + + if( line[lexstart] == ')' ) + { + delimiter = line[lexterm]; + nextLexeme(); /* Skip end paren */ + } + else + { + /* errorMessage( "parens", NULL ); */ + } + + loc = insertLiteral( &cp, clc, val ); + sym_eval.val = loc + ( clc & 060000 ); + return( &sym_eval ); + + default: + switch( line[lexstart] ) + { + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + break; + + default: + errorMessage( &illegal_character, lexstart ); + break; + } + val = 0; /* On error, set value to zero. */ + nextLexBlank(); /* Go past illegal character. */ + } + } + sym_eval.val = val; + return( &sym_eval ); +} /* eval() */ + +/******************************************************************************/ +/* */ +/* Function: incrementClc */ +/* */ +/* Synopsis: Set the next assembly location. Test for collision with */ +/* the literal tables. */ +/* */ +/******************************************************************************/ +WORD32 incrementClc() +{ + testForLiteralCollision( clc ); + clc = (( clc + 1 ) & ADDRESS_FIELD ); + return( clc ); +} /* incrementClc() */ + + +/******************************************************************************/ +/* */ +/* Function: testForLiteralCollision */ +/* */ +/* Synopsis: Test the given location for collision with the literal tables. */ +/* */ +/******************************************************************************/ +BOOL testForLiteralCollision( WORD32 loc ) +{ + WORD32 pagelc; + BOOL result = FALSE; + + pagelc = loc & ADDRESS_FIELD; + if( ( pagelc >= lit_loc ) && ( lit_loc != LIT_BASE ) && !cp.error ) + { + errorMessage( &literal_overflow, -1 ); + cp.error = TRUE; + result = TRUE; + } + return( result ); +} /* testForLiteralCollision() */ + + +/******************************************************************************/ +/* */ +/* Function: readLine */ +/* */ +/* Synopsis: Get next line of input. Print previous line if needed. */ +/* */ +/******************************************************************************/ +void readLine() +{ + BOOL ffseen; + WORD32 ix; + WORD32 iy; + char mc; + char inpline[4*LINELEN]; + + listLine(); /* List previous line if needed. */ + indirect_generated = FALSE; /* Mark no indirect address generated. */ + error_in_line = FALSE; /* No error in line. */ + + if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ + { + mac_ptr = NULL; + for( ix = 0; ix < LINELEN; ix++ ) + { + line[ix] = mac_line[ix]; /* Restore invoking line. */ + } + cc = lexstartprev = mac_cc; /* Restore cc. */ + maxcc = strlen( line ); /* Restore maxcc. */ + listed = TRUE; /* Already listed. */ + return; + } + + cc = 0; /* Initialize column counter. */ + lexstartprev = 0; + if( mac_ptr ) /* Inside macro? */ + { + maxcc = 0; + do + { + mc = *mac_ptr++; /* Next character. */ + if( islower( mc )) /* Encoded argument number? */ + { + ix = mc - 'a'; /* Convert to index. */ + if( iy = mac_arg_pos[ix] ) + { + do /* Copy argument string. */ + { + line[maxcc++] = mac_line[iy++]; + } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && + ( !isdone( mac_line[iy] ))); + } + } + else /* Ordinary character, just copy. */ + { + line[maxcc++] = mc; + } + } while( !isend( mc )); + line[maxcc] = '\0'; + listed = nomac_exp; + return; + } + + lineno++; /* Count lines read. */ + listed = FALSE; /* Mark as not listed. */ +READ_LINE: + if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) + { + filix_curr++; /* Advance to next file. */ + if( filix_curr < save_argc ) /* More files? */ + { + fclose( infile ); + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + goto READ_LINE; + } + else + { + end_of_input = TRUE; + } + } + + ffseen = FALSE; + for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) + { + if( inpline[ix] == '\f' ) + { + if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); + ffseen = TRUE; + } + else + { + line[iy++] = inpline[ix]; + } + } + line[iy] = '\0'; + + /* If the line is terminated by CR-LF, remove, the CR. */ + if( line[iy - 2] == '\r' ) + { + iy--; + line[iy - 1] = line[iy - 0]; + line[iy] = '\0'; + } + maxcc = iy; /* Save the current line length. */ +} /* readLine() */ + + +/******************************************************************************/ +/* */ +/* Function: listLine */ +/* */ +/* Synopsis: Output a line to the listing file. */ +/* */ +/******************************************************************************/ +void listLine() +/* generate a line of listing if not already done! */ +{ + if( listfile != NULL && listed == FALSE ) + { + printLine( line, 0, 0, LINE ); + } +} /* listLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printPageBreak */ +/* */ +/* Synopsis: Output a Top of Form and listing header if new page necessary. */ +/* */ +/******************************************************************************/ +void printPageBreak() +{ + if( page_lineno >= LIST_LINES_PER_PAGE ) + /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ + { + if( !list_title_set ) + { + strcpy( list_title, line ); + if( list_title[strlen(list_title) - 1] == '\n' ) + { + list_title[strlen(list_title) - 1] = '\0'; + } + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + list_title_set = TRUE; + } + topOfForm( list_title, NULL ); + + } +} /* printPageBreak() */ + + +/******************************************************************************/ +/* */ +/* Function: printLine */ +/* */ +/* Synopsis: Output a line to the listing file with new page if necessary. */ +/* */ +/******************************************************************************/ +void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) +{ + if( listfile == NULL ) + { + save_error_count = 0; + return; + } + + printPageBreak(); + + list_lineno++; + page_lineno++; + switch( linestyle ) + { + default: + case LINE: + fprintf( listfile, "%5d ", lineno ); + fputs( line, listfile ); + listed = TRUE; + break; + + case LINE_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %6.6o ", lineno, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %6.6o\n", val ); + } + break; + + case LINE_LOC_VAL: + if( !listed ) + { + if( indirect_generated ) + { + fprintf( listfile, "%5d %5.5o %6.6o@ ", lineno, loc, val ); + } + else + { + fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); + } + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + } + break; + + case LOC_VAL: + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + break; + } + printErrorMessages(); +} /* printLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printErrorMessages */ +/* */ +/* Synopsis: Output any error messages from the current list of errors. */ +/* */ +/******************************************************************************/ +void printErrorMessages() +{ + WORD32 ix; + WORD32 iy; + + if( listfile != NULL ) + { + /* If any errors, display them now. */ + for( iy = 0; iy < save_error_count; iy++ ) + { + printPageBreak(); + fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); + if( error_list[iy].col >= 0 ) + { + for( ix = 0; ix < error_list[iy].col; ix++ ) + { + if( line[ix] == '\t' ) + { + putc( '\t', listfile ); + } + else + { + putc( ' ', listfile ); + } + } + fputs( "^", listfile ); + list_lineno++; + page_lineno++; + } + fputs( "\n", listfile ); + } + } + save_error_count = 0; +} /* printErrorMessages() */ + + +/******************************************************************************/ +/* */ +/* Function: endOfBinary */ +/* */ +/* Synopsis: Outputs both literal tables at the end of a binary segment. */ +/* */ +/******************************************************************************/ +void endOfBinary() +{ + punchLiteralPool( &cp, clc - 1 ); + if( start_addr >= 0) + { + punchTriplet( JMP | ( start_addr & 017777 )); + punchTriplet( 0 ); + } + return; +} /* endOfBinary() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLeader */ +/* */ +/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ +/* documentation. Paper tape has 10 punches per inch. */ +/* */ +/******************************************************************************/ +void punchLeader( WORD32 count ) +{ + WORD32 ix; + + /* If value is zero, set to the default of 2 feet of leader. */ + count = ( count == 0 ) ? 240 : count; + + if( objectfile != NULL ) + { + for( ix = 0; ix < count; ix++ ) + { + fputc( 0, objectfile ); + } + } +} /* punchLeader() */ + + +/******************************************************************************/ +/* */ +/* Function: punchObject */ +/* */ +/* Synopsis: Put one character to object file and include it in checksum. */ +/* */ +/******************************************************************************/ +void punchObject( WORD32 val ) +{ + val &= 0377; + if( objectfile != NULL ) + { + fputc( val, objectfile ); + } + checksum += val; + binary_data_output = TRUE; +} /* punchObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchOutObject */ +/* */ +/* Synopsis: Output the current line and then then punch value to the */ +/* object file. */ +/* */ +/******************************************************************************/ +void punchOutObject( WORD32 loc, WORD32 val ) +{ + printLine( line, loc, val, LINE_LOC_VAL ); + punchLocObject( loc, val ); +} /* punchOutObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLocObject */ +/* */ +/* Synopsis: Output the word (with origin if rim format) to the object file.*/ +/* */ +/******************************************************************************/ +void punchLocObject( WORD32 loc, WORD32 val ) +{ + punchTriplet( DAC | loc ); + punchTriplet( val ); +} /* punchLocObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchTriplet */ +/* */ +/* Synopsis: Output 18b word as three 6b characters with ho bit set. */ +/* */ +/******************************************************************************/ +void punchTriplet( WORD32 val ) +{ + punchObject((( val >> 12) & 077) | 0200 ); + punchObject((( val >> 6 ) & 077) | 0200 ); + punchObject(( val & 077) | 0200 ); +} /* punchTriplet */ + + +/******************************************************************************/ +/* */ +/* Function: punchLiteralPool */ +/* */ +/* Synopsis: Output the current page data. */ +/* */ +/******************************************************************************/ +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) +{ + WORD32 loc; + WORD32 tmplc; + + if( lit_loc < LIT_BASE ) + { + for( loc = lit_loc; loc < LIT_BASE; loc++ ) + { + tmplc = loc + ( lpool_page & 060000 ); + printLine( line, tmplc, p->pool[loc], LOC_VAL ); + punchLocObject( tmplc, p->pool[loc] ); + } + p->error = FALSE; + lit_loc = LIT_BASE; + } +} /* punchLiteralPool() */ + + +/******************************************************************************/ +/* */ +/* Function: insertLiteral */ +/* */ +/* Synopsis: Add a value to the given literal pool if not already in pool. */ +/* Return the location of the value in the pool. */ +/* */ +/******************************************************************************/ +WORD32 insertLiteral( LPOOL_T *p, WORD32 pool_page, WORD32 value ) +{ + WORD32 ix; + + /* Search the literal pool for any occurence of the needed value. */ + ix = LIT_BASE - 1; + while( ix >= lit_loc && p->pool[ix] != value ) + { + ix--; + } + + /* Check if value found in literal pool. If not, then insert value. */ + if( ix < lit_loc ) + { + lit_loc--; + p->pool[lit_loc] = value; + ix = lit_loc; + } + return( ix ); +} /* insertLiteral() */ + + +/******************************************************************************/ +/* */ +/* Function: printSymbolTable */ +/* */ +/* Synopsis: Output the symbol table. */ +/* */ +/******************************************************************************/ +void printSymbolTable() +{ + int col; + int cx; + char *fmt; + int ix; + char mark; + int page; + int row; + int symbol_base; + int symbol_lines; + + symbol_base = number_of_fixed_symbols; + + for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) + { + topOfForm( list_title, s_symtable ); + symbol_lines = LIST_LINES_PER_PAGE - page_lineno; + + for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) + { + list_lineno++; + page_lineno++; + fprintf( listfile, "%5d", list_lineno ); + + for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) + { + /* Get index of symbol for the current line and column */ + cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; + cx += symbol_base; + + /* Make sure that there is a symbol to be printed. */ + if( number_of_fixed_symbols <= cx && cx < symbol_top ) + { + switch( symtab[cx].type & LABEL ) + { + case LABEL: + fmt = " %c%-6.6s %5.5o "; + break; + + default: + fmt = " %c%-6.6s %4.4o "; + break; + } + + switch( symtab[cx].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + mark = '?'; + break; + + case REDEFINED: + mark = '#'; + break; + + default: + mark = ' '; + break; + } + fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); + ix++; + } + } + fprintf( listfile, "\n" ); + } + } +} /* printSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printPermanentSymbolTable */ +/* */ +/* Synopsis: Output the permanent symbol table to a file suitable for */ +/* being input after the EXPUNGE pseudo-op. */ +/* */ +/******************************************************************************/ +void printPermanentSymbolTable() +{ + int ix; + FILE *permfile; + char *s_type; + + if(( permfile = fopen( permpathname, "w" )) == NULL ) + { + exit( 2 ); + } + + fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); + fprintf( permfile, " EXPUNGE\n/\n" ); + /* Print the memory reference instructions first. */ + s_type = "FIXMRI"; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_MRI( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + + s_type = " "; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_FIXED( symtab[ix].type )) + { + if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + } + fprintf( permfile, "/\n FIXTAB\n" ); + fclose( permfile ); +} /* printPermanentSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printCrossReference */ +/* */ +/* Synopsis: Output a cross reference (concordance) for the file being */ +/* assembled. */ +/* */ +/******************************************************************************/ +void printCrossReference() +{ + int ix; + int symbol_base; + int xc; + int xc_index; + int xc_refcount; + int xc_cols; + + /* Force top of form for first page. */ + page_lineno = LIST_LINES_PER_PAGE; + + list_lineno = 0; + symbol_base = number_of_fixed_symbols; + + for( ix = symbol_base; ix < symbol_top; ix++ ) + { + list_lineno++; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref ); + } + + fprintf( listfile, "%5d", list_lineno ); + + /* Get reference count & index into concordance table for this symbol. */ + xc_refcount = symtab[ix].xref_count; + xc_index = symtab[ix].xref_index; + /* Determine how to label symbol on concordance. */ + switch( symtab[ix].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + fprintf( listfile, " U "); + break; + + case REDEFINED: + fprintf( listfile, " M %5d ", xreftab[xc_index] ); + break; + + default: + fprintf( listfile, " A %5d ", xreftab[xc_index] ); + break; + } + fprintf( listfile, "%-6.6s ", symtab[ix].name ); + + /* Output the references, 8 numbers per line after symbol name. */ + for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) + { + if( xc_cols >= XREF_COLUMNS ) + { + xc_cols = 0; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref); + } + list_lineno++; + fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); + } + fprintf( listfile, " %5d", xreftab[xc_index + xc] ); + } + fprintf( listfile, "\n" ); + } +} /* printCrossReference() */ + + +/******************************************************************************/ +/* */ +/* Function: topOfForm */ +/* */ +/* Synopsis: Prints title and sub-title on top of next page of listing. */ +/* */ +/******************************************************************************/ +void topOfForm( char *title, char *sub_title ) +{ + char temp[10]; + + list_pageno++; + strcpy( temp, s_page ); + sprintf( temp, "%s %d", s_page, list_pageno ); + + /* Output a top of form if not the first page of the listing. */ + if( list_pageno > 1 ) + { + fprintf( listfile, "\f" ); + } + fprintf( listfile, "\n %-63s %10s\n", title, temp ); + + /* Reset the current page line counter. */ + page_lineno = 1; + if( sub_title != NULL ) + { + fprintf( listfile, "%80s\n", sub_title ); + page_lineno++; + } + else + { + fprintf( listfile, "\n" ); + page_lineno++; + } + fprintf( listfile, "\n" ); + page_lineno++; +} /* topOfForm() */ + + +/******************************************************************************/ +/* */ +/* Function: lexemeToName */ +/* */ +/* Synopsis: Convert the current lexeme into a string. */ +/* */ +/******************************************************************************/ +char *lexemeToName( char *name, WORD32 from, WORD32 term ) +{ + WORD32 to; + + to = 0; + + while( from < term && to < ( SYMLEN - 1 )) + { + name[to++] = toupper( line[from++] ); + } + + while( to < SYMLEN ) + { + name[to++] = '\0'; + } + return( name ); +} /* lexemeToName() */ + +/******************************************************************************/ +/* */ +/* Function: defineLexeme */ +/* */ +/* Synopsis: Put lexeme into symbol table with a value. */ +/* */ +/******************************************************************************/ +SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ + WORD32 term, /* end+1 of lexeme being defined. */ + WORD32 val, /* value of lexeme being defined. */ + SYMTYP type ) /* how symbol is being defined. */ +{ + char name[SYMLEN]; + + lexemeToName( name, start, term); + return( defineSymbol( name, val, type, start )); +} /* defineLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: defineSymbol */ +/* */ +/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ +/* not already in table. */ +/* */ +/******************************************************************************/ +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) +{ + SYM_T *sym; + WORD32 xref_count; + + if( strlen( name ) < 1 ) + { + return( &sym_undefined ); /* Protect against non-existent names. */ + } + sym = lookup( name ); + xref_count = 0; /* Set concordance for normal defintion. */ + + if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) + { + if( pass == 2 ) + { + errorSymbol( &redefined_symbol, sym->name, start ); + type = type | REDEFINED; + sym->xref_count++; /* Referenced symbol, count it. */ + xref_count = sym->xref_count; + } + return ( sym ); + } + + if( pass == 2 && xref ) + { + /* Put the definition line number in the concordance table. */ + /* Defined symbols are not counted as references. */ + xreftab[sym->xref_index] = lineno; + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + xref_count] = lineno; + } + + /* Now set the value and the type. */ + sym->val = ( type == LABEL) ? val : val & 0777777; + sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; + return( sym ); +} /* defineSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: lookup */ +/* */ +/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ +/* table as undefined. Return address of symbol in table. */ +/* */ +/******************************************************************************/ +SYM_T *lookup( char *name ) +{ + int ix; /* Insertion index */ + int lx; /* Left index */ + int rx; /* Right index */ + + /* First search the permanent symbols. */ + lx = 0; + ix = binarySearch( name, lx, number_of_fixed_symbols ); + + /* If symbol not in permanent symbol table. */ + if( ix < 0 ) + { + /* Now try the user symbol table. */ + ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); + + /* If symbol not in user symbol table. */ + if( ix < 0 ) + { + /* Must put symbol in table if index is negative. */ + ix = ~ix; + if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) + { + errorSymbol( &symbol_table_full, name, lexstart ); + exit( 1 ); + } + + for( rx = symbol_top; rx >= ix; rx-- ) + { + symtab[rx + 1] = symtab[rx]; + } + symbol_top++; + + /* Enter the symbol as UNDEFINED with a value of zero. */ + strcpy( symtab[ix].name, name ); + symtab[ix].type = UNDEFINED; + symtab[ix].val = 0; + symtab[ix].xref_count = 0; + if( xref && pass == 2 ) + { + xreftab[symtab[ix].xref_index] = 0; + } + } + } + + return( &symtab[ix] ); /* Return the location of the symbol. */ +} /* lookup() */ + + +/******************************************************************************/ +/* */ +/* Function: binarySearch */ +/* */ +/* Synopsis: Searches the symbol table within the limits given. If the */ +/* symbol is not in the table, it returns the insertion point. */ +/* */ +/******************************************************************************/ +int binarySearch( char *name, int start, int symbol_count ) +{ + int lx; /* Left index */ + int mx; /* Middle index */ + int rx; /* Right index */ + int compare; /* Results of comparison */ + + lx = start; + rx = symbol_count - 1; + while( lx <= rx ) + { + mx = ( lx + rx ) / 2; /* Find center of search area. */ + + compare = strcmp( name, symtab[mx].name ); + + if( compare < 0 ) + { + rx = mx - 1; + } + else if( compare > 0 ) + { + lx = mx + 1; + } + else + { + return( mx ); /* Found a match in symbol table. */ + } + } /* end while */ + return( ~lx ); /* Return insertion point. */ +} /* binarySearch() */ + + +/******************************************************************************/ +/* */ +/* Function: compareSymbols */ +/* */ +/* Synopsis: Used to presort the symbol table when starting assembler. */ +/* */ +/******************************************************************************/ +int compareSymbols( const void *a, const void *b ) +{ + return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); +} /* compareSymbols() */ + +/******************************************************************************/ +/* */ +/* Function: copyMacLine */ +/* */ +/* Synopsis: Used to copy a macro line to the macro buffer. */ +/* */ +/******************************************************************************/ +int copyMacLine( int length, int from, int term, int nargs ) +{ + char name[SYMLEN]; + int ix; + int jx; + int kx; + BOOL bl; + + bl = TRUE; + for( ix = from; ix < term; ix++ ) + { + if( !is_blank( line[ix] ) || ( line[ix] == '\t' )) bl = FALSE; + } + if( bl || ( length < 0 )) return length; + if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; + for( ix = from; ix < term; ) + { + if( nargs && isalpha( line[ix] )) /* Start of symbol? */ + { + for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ + { + if( !isalnum( line[jx] )) break; + } + lexemeToName( name, ix, jx ); /* Make into name. */ + for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ + { + if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) + { + mac_buffer[length++] = 'a' + (char) kx; + for( ix++; ix < jx; ix++ ) + { + mac_buffer[length++] = 'z'; + } + break; + } /* end if strncmp */ + } /* end for kx */ + if( kx >= nargs ) + { + for ( ; ix < jx; ) + { + mac_buffer[length++] = toupper( line[ix++] ); + } + } + } /*end if nargs */ + else + { + mac_buffer[length++] = toupper( line[ix++] ); + } /* end else */ + } /* end for ix */ + mac_buffer[length++] = '\n'; + mac_buffer[length] = 0; + return length; +} + +/******************************************************************************/ +/* */ +/* Function: evalSymbol */ +/* */ +/* Synopsis: Get the pointer for the symbol table entry if exists. */ +/* If symbol doesn't exist, return a pointer to the undefined sym */ +/* */ +/******************************************************************************/ +SYM_T *evalSymbol() +{ + char name[SYMLEN]; + SYM_T *sym; + + sym = lookup( lexemeToName( name, lexstart, lexterm )); + + sym->xref_count++; /* Count the number of references to symbol. */ + + if( xref && pass == 2 ) + { + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + sym->xref_count] = lineno; + } + + return( sym ); +} /* evalSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: moveToEndOfLine */ +/* */ +/* Synopsis: Move the parser input to the end of the current input line. */ +/* */ +/******************************************************************************/ +void moveToEndOfLine() +{ + while( !isend( line[cc] )) cc++; + lexstart = cc; + lexterm = cc; + lexstartprev = lexstart; +} /* moveToEndOfLine() */ + +/******************************************************************************/ +/* */ +/* Function: nextLexeme */ +/* */ +/* Synopsis: Get the next lexical element from input line. */ +/* */ +/******************************************************************************/ +void nextLexeme() +{ + /* Save start column of previous lexeme for diagnostic messages. */ + lexstartprev = lexstart; + lextermprev = lexterm; + + while( is_blank( line[cc] ) || + (( line[cc] == '\t' ) && ( line[cc+1] == '\t' )) || + (( line[cc] == '\t' ) && isdone( line[cc+1] ))) { cc++; } + lexstart = cc; + + if( isalnum( line[cc] )) + { + while( isalnum( line[cc] )) { cc++; } + } + else if( isend( line[cc] )) + { + /* End-of-Line, don't advance cc! */ + } + else + { + switch( line[cc] ) + { + case '"': /* Quoted letter */ + if( cc + 2 < maxcc ) + { + cc++; + cc++; + } + else + { + errorMessage( &no_literal_value, lexstart ); + cc++; + } + break; + + case '/': /* Comment, don't advance cc! */ + break; + + default: /* All other punctuation. */ + cc++; + break; + } + } + lexterm = cc; +} /* nextLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: nextLexBlank */ +/* */ +/* Synopsis: Used to prevent illegal blanks in expressions. */ +/* */ +/******************************************************************************/ +void nextLexBlank() +{ + nextLexeme(); + if( is_blank( delimiter )) + { + errorMessage( &illegal_blank, lexstart - 1 ); + } + delimiter = line[lexterm]; +} /* nextLexBlank() */ + + + +/******************************************************************************/ +/* */ +/* Function: pseudoOperators */ +/* */ +/* Synopsis: Process pseudo-ops (directives). */ +/* */ +/******************************************************************************/ +BOOL pseudoOperators( PSEUDO_T val ) +{ + int count; + int delim; + int index; + int ix; + WORD32 length; + int level; + int lexstartsave; + int pack; + int pos; + int radixprev; + BOOL status; + SYM_T *sym; + WORD32 value; + WORD32 word; + int width; + static int mask_tab[19] = { 0000000, + 0000001, 0000003, 0000007, 0000017, 0000037, 0000077, + 0000177, 0000377, 0000777, 0001777, 0003777, 0007777, + 0017777, 0037777, 0077777, 0177777, 0377777, 0777777 }; + + status = TRUE; + switch( (PSEUDO_T) val ) + { + case DECIMAL: + radix = 10; + break; + + case DEFINE: + count = 0; + index = 0; + lexstartsave = lexstart; + while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && + ( count < MAC_MAX_ARGS )) + { + if ( !isalpha( line[lexstart] ) && ( index == 0 )) + { + index = lexstart; + } + lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); + nextLexeme(); + } + if( count == 0 ) /* No macro name. */ + { + errorMessage( &no_macro_name, lexstartsave ); + index = 1; + } + else if( index ) /* Bad argument name. */ + { + errorMessage( &bad_dummy_arg, index ); + } + else if( mac_count >= MAC_TABLE_LENGTH ) + { + errorMessage( ¯o_table_full, lexstartsave ); + index = 1; + } + else + { + value = mac_count; + mac_count++; /* Value is entry in mac_bodies. */ + defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); + } + if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) + { + readLine(); + nextLexeme(); + } + if( index ) + { + conditionFalse(); /* On error skip macro body. */ + } + else if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + index = lexstart + 1; + length = 0; + level = 1; + while( level > 0 ) + { + if( end_of_input ) + { + break; + } + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + length = copyMacLine( length, index, cc, count - 1 ); + readLine(); + index = 0; + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + length = copyMacLine( length, index, cc - 1, count - 1 ); + if( length < 0 ) + { + errorMessage (¯o_too_long, lexstart ); + } + else if( length == 0 ) + { + mac_bodies[value] = NULL; + } + else + { + mac_bodies[value] = (char *) malloc( length + 1 ); + if( mac_bodies[value] ) + { + strncpy( mac_bodies[value], mac_buffer, length ); + *( mac_bodies[value] + length ) = 0; + } + else + { + errorMessage( &no_virtual_memory, lexstart ); + } + } + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } /* end if */ + break; + + case EJECT: + page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ + status = FALSE; /* This will force reading of next line */ + break; + + case IFDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNZERO: + if( (getExpr())->val == 0 ) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + break; + + case IFZERO: + if( (getExpr())->val == 0 ) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + break; + + case LIST: + listfile = listsave; + break; + + case NOLIST: + listfile = NULL; + break; + + case OCTAL: + radix = 8; + break; + + case START: + if( !isdone( line[lexstart] )) + { + start_addr = (getExpr())->val & 077777; + nextLexeme(); + } + printLine( line, 0, start_addr, LINE_VAL ); + status = FALSE; + break; + + case TEXT: + delim = line[lexstart]; + pack = 0; + count = 0; + index = lexstart + 1; + while( line[index] != delim && !isend( line[index] )) + { + pack = ( pack << 6 ) | ( line[index] & 077 ); + count++; + if( count > 2 ) + { + punchOutObject( clc, pack ); + incrementClc(); + count = 0; + pack = 0; + } + index++; + } + + if( count == 2 ) + { + punchOutObject( clc, pack << 6 ); + } + else if (count == 1) + { + punchOutObject( clc, pack << 12 ); + } + else + { + punchOutObject( clc, 0 ); + } + incrementClc(); + + if( isend( line[index] )) + { + cc = index; + lexterm = cc; + errorMessage( &text_string, cc ); + } + else + { + cc = index + 1; + lexterm = cc; + } + nextLexeme(); + break; + + case TITLE: + delim = line[lexstart]; + ix = lexstart + 1; + /* Find string delimiter. */ + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( !isend( line[ix] ) ) + { + count = 0; + ix = lexstart + 1; + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + list_title[count] = line[ix]; + count++; + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + + cc = ix + 1; + lexterm = cc; + page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ + list_title_set = TRUE; + } + else + { + cc = ix; + lexterm = cc; + errorMessage( &text_string, cc ); + } + + nextLexeme(); + break; + + case VFD: + pos = 0; + word = 0; + radixprev = radix; + while( !isdone (line[lexstart] )) + { + lexstartsave = lexstart; + radix = 10; + width = (getExpr())->val; /* Get field width. */ + radix = radixprev; + if( (width <= 0) || ((width + pos) > 18) || (line[lexstart] != ':') ) + { + errorMessage( &illegal_vfd_value, lexstartsave ); + } + nextLexBlank(); /* Skip colon. */ + value = (getExpr())->val; /* Get field value. */ + if( line[lexterm] == ',' ) cc++; + nextLexeme(); /* Advance to next field. */ + pos = pos + width; + if( pos <= 18 ) + { + word = word | ((value & mask_tab[width]) << (18 - pos)); + } + } + punchOutObject( clc, word ); + incrementClc(); + break; + + default: + break; + } /* end switch for pseudo-ops */ + return( status ); +} /* pseudoOperators() */ + + +/******************************************************************************/ +/* */ +/* Function: conditionFalse */ +/* */ +/* Synopsis: Called when a false conditional has been evaluated. */ +/* Lex should be the opening <; ignore all text until */ +/* the closing >. */ +/* */ +/******************************************************************************/ +void conditionFalse() +{ + int level; + + if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + level = 1; + while( level > 0 ) + { + if( end_of_input ) + { + break; + } + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + readLine(); + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionFalse() */ + +/******************************************************************************/ +/* */ +/* Function: conditionTrue */ +/* */ +/* Synopsis: Called when a true conditional has been evaluated. */ +/* Lex should be the opening <; skip it and setup for */ +/* normal assembly. */ +/* */ +/******************************************************************************/ +void conditionTrue() +{ + if( line[lexstart] == '<' ) + { + nextLexeme(); /* Skip the opening '<' */ + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionTrue() */ + + +/******************************************************************************/ +/* */ +/* Function: errorLexeme */ +/* */ +/* Synopsis: Display an error message using the current lexical element. */ +/* */ +/******************************************************************************/ +void errorLexeme( EMSG_T *mesg, WORD32 col ) +{ + char name[SYMLEN]; + + errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); +} /* errorLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: errorSymbol */ +/* */ +/* Synopsis: Display an error message with a given string. */ +/* */ +/******************************************************************************/ +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) +{ + char linecol[12]; + char *s; + + if( pass == 2 ) + { + s = ( name == NULL ) ? "" : name ; + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", + filename, linecol, mesg->file, s, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: errorMessage */ +/* */ +/* Synopsis: Display an error message without a name argument. */ +/* */ +/******************************************************************************/ +void errorMessage( EMSG_T *mesg, WORD32 col ) +{ + char linecol[12]; + + if( pass == 2 ) + { + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", + filename, linecol, mesg->file, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorMessage() */ + +/******************************************************************************/ +/* */ +/* Function: saveError */ +/* */ +/* Synopsis: Save the current error in a list so it may displayed after the */ +/* the current line is printed. */ +/* */ +/******************************************************************************/ +void saveError( char *mesg, WORD32 col ) +{ + if( save_error_count < DIM( error_list )) + { + error_list[save_error_count].mesg = mesg; + error_list[save_error_count].col = col; + save_error_count++; + } + error_in_line = TRUE; + + if( listed ) + { + printErrorMessages(); + } +} /* saveError() */ +/* End-of-File */ diff --git a/crossassemblers/macro8x.c b/crossassemblers/macro8x.c new file mode 100644 index 0000000..69ac403 --- /dev/null +++ b/crossassemblers/macro8x.c @@ -0,0 +1,4251 @@ +/******************************************************************************/ +/* */ +/* Program: MACRO8X */ +/* File: macro8x.c */ +/* Author: Gary A. Messenbrink */ +/* MACRO8X modifications: Bob Supnik (:) : error: at Loc = */ +/* */ +/* An example error message is: */ +/* */ +/* bintst.pal(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ +/* */ +/* The error diagnostics put in the listing start with a two character */ +/* error code (if appropriate) and a short message. A carat '^' is */ +/* placed under the item in error if appropriate. */ +/* An example error message is: */ +/* */ +/* 17 07616 3000 DCA UNDEF */ +/* UD undefined ^ */ +/* 18 07617 1777 TAD I DUMMY */ +/* */ +/* When an indirect is generated, an at character '@' is placed after the */ +/* the instruction value in the listing as an indicator as follows: */ +/* */ +/* 14 03716 1777@ TAD OFFPAG */ +/* */ +/* Undefined symbols are marked in the symbol table listing by prepending */ +/* a '?' to the symbol. Redefined symbols are marked in the symbol table */ +/* listing by prepending a '#' to the symbol. Examples are: */ +/* */ +/* #REDEF 04567 */ +/* SWITCH 07612 */ +/* ?UNDEF 00000 */ +/* */ +/* Refer to the code for the diagnostic messages generated. */ +/* */ +/* BUGS */ +/* Only a minimal effort has been made to keep the listing format */ +/* anything like the PAL-8 listing format. */ +/* The operation of the conditional assembly pseudo-ops may not function */ +/* exactly as the DEC versions. I did not have any examples of these so */ +/* the implementation is my interpretation of how they should work. */ +/* */ +/* The RIMPUNch and BINPUNch pseudo-ops do not change the binary output */ +/* file type that was specified on startup. This was intentional and */ +/* and allows rim formatted data to be output prior to the actual binary */ +/* formatted data. On UN*X style systems, the same effect can be achieved */ +/* by using the "cat" command, but on DOS/Windows systems, doing this was */ +/* a major chore. */ +/* */ +/* The floating point input does not generate values exactly as the DEC */ +/* compiler does. I worked out several examples by hand and believe that */ +/* this implementation is slightly more accurate. If I am mistaken, */ +/* let me know and, if possible, a better method of generating the values. */ +/* */ +/* BUILD and INSTALLATION */ +/* This program has been built and successfully executed on: */ +/* a. Linux (80486 CPU)using gcc */ +/* b. RS/6000 (AIX 3.2.5) */ +/* c. Borland C++ version 3.1 (large memory model) */ +/* d. Borland C++ version 4.52 (large memory model) */ +/* with no modifications to the source code. */ +/* */ +/* On UNIX type systems, store the the program as the pal command */ +/* and on PC type systems, store it as pal.exe */ +/* */ +/* HISTORICAL NOTE: */ +/* This assembler was written to support the fleet of PDP-8 systems */ +/* used by the Bay Area Rapid Transit System. As of early 1997, */ +/* this includes about 40 PDP-8/E systems driving the train destination */ +/* signs in passenger stations. */ +/* */ +/* REFERENCES: */ +/* This assembler is based on the pal assember by: */ +/* Douglas Jones and */ +/* Rich Coon */ +/* */ +/* DISCLAIMER: */ +/* See the symbol table for the set of pseudo-ops supported. */ +/* See the code for pseudo-ops that are not standard for PDP/8 assembly. */ +/* Refer to DEC's "Programming Languages (for the PDP/8)" for complete */ +/* documentation of pseudo-ops. */ +/* Refer to DEC's "Introduction to Programming (for the PDP/8)" or a */ +/* lower level introduction to the assembly language. */ +/* */ +/* WARRANTY: */ +/* If you don't like it the way it works or if it doesn't work, that's */ +/* tough. You're welcome to fix it yourself. That's what you get for */ +/* using free software. */ +/* */ +/* COPYRIGHT NOTICE: */ +/* This is free software. There is no fee for using it. You may make */ +/* any changes that you wish and also give it away. If you can make */ +/* a commercial product out of it, fine, but do not put any limits on */ +/* the purchaser's right to do the same. If you improve it or fix any */ +/* bugs, it would be nice if you told me and offered me a copy of the */ +/* new version. */ +/* */ +/* */ +/* Amendments Record: */ +/* Version Date by Comments */ +/* ------- ------- --- --------------------------------------------------- */ +/* v1.0 12Apr96 GAM Original */ +/* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ +/* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ +/* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ +/* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ +/* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ +/* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ +/* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ +/* v3.0 14Feb97 RMS MACRO8X features. */ +/* v3.1 16Sep01 RMS Bug fixes: */ +/* - IFNZRO instead of IFNZERO */ +/* - allow blanks after symbol= */ +/* - remove field from label values */ +/* - don't include NOPUNCH data in checksum */ +/* */ +/******************************************************************************/ + +#include +#include +#include +#include + +#define LINELEN 96 +#define LIST_LINES_PER_PAGE 60 /* Includes 5 line page header. */ +#define NAMELEN 128 +#define SYMBOL_COLUMNS 5 +#define SYMLEN 7 +#define SYMBOL_TABLE_SIZE 8192 +#define MAC_MAX_ARGS 20 /* Must be < 26 */ +#define MAC_MAX_LENGTH 8192 +#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ +#define TITLELEN 63 +#define XREF_COLUMNS 8 + +#define ADDRESS_FIELD 00177 +#define FIELD_FIELD 070000 +#define INDIRECT_BIT 00400 +#define LAST_PAGE_LOC 00177 +#define OP_CODE 07000 +#define PAGE_BIT 00200 + +#ifdef PAGE_SIZE +#undef PAGE_SIZE +#endif +#define PAGE_SIZE 00200 + +#define PAGE_FIELD 07600 +#define PAGE_ZERO_END 00200 +#define TOTAL_PAGES (32 * 8) +#define GET_PAGE(x) (((x) >> 7) & (TOTAL_PAGES - 1)) + +/* Macro to get the number of elements in an array. */ +#define DIM(a) (sizeof(a)/sizeof(a[0])) + +/* Macro to get the address plus one of the end of an array. */ +#define BEYOND(a) ((a) + DIM(A)) + +#define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>')) +#define isend(c) ((c=='\0')|| (c=='\n')) +#define isdone(c) ((c=='/') || (isend(c)) || (c==';')) + +/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ +/* (true) if the stated condtion is met. */ +/* Use these to test attributes. The proper bits are extracted and then */ +/* tested. */ +#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) +#define M_DEFINED(s) ((s & DEFINED) == DEFINED) +#define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) +#define M_FIXED(s) ((s & FIXED) == FIXED) +#define M_LABEL(s) ((s & LABEL) == LABEL) +#define M_MRI(s) ((s & MRI) == MRI) +#define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) +#define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) +#define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) +#define M_MACRO(s) ((s & MACRO) == MACRO) +#define M_UNDEFINED(s) (!M_DEFINED(s)) +#define M_NOTRDEF(s) ((s & NOTRDEF) != 0) + +/* This macro is used to test symbols by the conditional assembly pseudo-ops. */ +#define M_DEF(s) (M_DEFINED(s)) +#define M_COND(s) (M_CONDITIONAL(s)) +#define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) + +typedef unsigned char BOOL; +typedef unsigned char BYTE; +typedef int WORD32; + +#ifndef FALSE + #define FALSE 0 + #define TRUE (!FALSE) +#endif + +/* Line listing styles. Used to control listing of lines. */ +enum linestyle_t +{ + LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL +}; +typedef enum linestyle_t LINESTYLE_T; + +/* Symbol Types. */ +/* Note that the names that have FIX as the suffix contain the FIXED bit */ +/* included in the value. */ +/* */ +/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ +/* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ +/* defined or undefined. The condition bit is set when the symbol is defined */ +/* during pass 1 and reset on pass 2 at the location the symbol was defined */ +/* during pass 1. When processing conditionals during pass 2, if the symbol */ +/* is defined and the condition bit is set, the symbol is treated as if it */ +/* were undefined. This gives consistent behavior of the conditional */ +/* pseudo-ops during both pass 1 and pass 2. */ +enum symtyp +{ + UNDEFINED = 0000, + DEFINED = 0001, + FIXED = 0002, + MRI = 0004 | DEFINED, + LABEL = 0010 | DEFINED, + REDEFINED = 0020 | DEFINED, + DUPLICATE = 0040 | DEFINED, + PSEUDO = 0100 | FIXED | DEFINED, + CONDITION = 0200, + MACRO = 0400 | DEFINED, + MRIFIX = MRI | FIXED | DEFINED, + DEFFIX = DEFINED | FIXED, + NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED +}; +typedef enum symtyp SYMTYP; + +enum pseudo_t +{ + BANK, BINPUNCH, DECIMAL, DEFINE, DUBL, EJECT, ENPUNCH, + EXPUNGE, FIELD, FIXTAB, FLTG, IFDEF, IFNDEF, IFNZERO, + IFZERO, LGM, LIST, LIT, LITBAS, NOLGM, NOPUNCH, + OCTAL, PAGE, PAUSE, RELOC, RIMPUNCH, TEXT, TITLE, + UNLIST, VFD, ZBLOCK +}; +typedef enum pseudo_t PSEUDO_T; + +struct sym_t +{ + SYMTYP type; + char name[SYMLEN]; + WORD32 val; + WORD32 xref_index; + WORD32 xref_count; +}; +typedef struct sym_t SYM_T; + +struct lpool_t +{ + WORD32 error; /* True if error message has been printed. */ + WORD32 pool[PAGE_SIZE]; +}; +typedef struct lpool_t LPOOL_T; + +struct emsg_t +{ + char *list; + char *file; +}; +typedef struct emsg_t EMSG_T; + +struct errsave_t +{ + char *mesg; + WORD32 col; +}; +typedef struct errsave_t ERRSAVE_T; + +struct fltg_ +{ + WORD32 exponent; + WORD32 mantissa; +}; +typedef struct fltg_ FLTG_T; + +/*----------------------------------------------------------------------------*/ + +/* Function Prototypes */ + +int binarySearch( char *name, int start, int symbol_count ); +int copyMacLine( int length, int from, int term, int nargs ); +int compareSymbols( const void *a, const void *b ); +void conditionFalse( void ); +void conditionTrue( void ); +SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); +void endOfBinary( void ); +void errorLexeme( EMSG_T *mesg, WORD32 col ); +void errorMessage( EMSG_T *mesg, WORD32 col ); +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); +SYM_T *eval( void ); +WORD32 evalDubl( WORD32 initial_value ); +FLTG_T *evalFltg( void ); +SYM_T *evalSymbol( void ); +void getArgs( int argc, char *argv[] ); +WORD32 getDublExpr( void ); +WORD32 getDublExprs( void ); +FLTG_T *getFltgExpr( void ); +FLTG_T *getFltgExprs( void ); +SYM_T *getExpr( void ); +WORD32 getExprs( void ); +WORD32 incrementClc( void ); +void inputDubl( void ); +void inputFltg( void ); +WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); +char *lexemeToName( char *name, WORD32 from, WORD32 term ); +void listLine( void ); +SYM_T *lookup( char *name ); +void moveToEndOfLine( void ); +void nextLexBlank( void ); +void nextLexeme( void ); +void normalizeFltg( FLTG_T *fltg ); +void onePass( void ); +void printCrossReference( void ); +void printErrorMessages( void ); +void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); +void printPageBreak( void ); +void printPermanentSymbolTable( void ); +void printSymbolTable( void ); +BOOL pseudoOperators( PSEUDO_T val ); +void punchChecksum( void ); +void punchLocObject( WORD32 loc, WORD32 val ); +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); +void punchOutObject( WORD32 loc, WORD32 val ); +void punchLeader( WORD32 count ); +void punchObject( WORD32 val ); +void punchOrigin( WORD32 loc ); +void readLine( void ); +void saveError( char *mesg, WORD32 cc ); +BOOL testForLiteralCollision( WORD32 loc ); +BOOL testZeroPool( WORD32 value ); +void topOfForm( char *title, char *sub_title ); + +/*----------------------------------------------------------------------------*/ + +/* Table of pseudo-ops (directives) which are used to setup the symbol */ +/* table on startup and when the EXPUNGE pseudo-op is executed. */ +SYM_T pseudo[] = +{ + { PSEUDO, "BANK", BANK }, /* Synonym for field in MACRO8X. */ + { PSEUDO, "BINPUN", BINPUNCH }, /* Output in Binary Loader format. */ + { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ + { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ + { PSEUDO, "DUBL", DUBL }, /* Ignored (unsupported). */ + { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ + { PSEUDO, "ENPUNC", ENPUNCH }, /* Turn on object code generation. */ + { PSEUDO, "EXPUNG", EXPUNGE }, /* Remove all symbols from symbol table. */ + { PSEUDO, "FIELD", FIELD }, /* Set origin to memory field. */ + { PSEUDO, "FIXTAB", FIXTAB }, /* Mark current symbols as permanent. */ + { PSEUDO, "FLTG", FLTG }, /* Ignored (unsupported). */ + { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ + { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ + { PSEUDO, "IFNZRO", IFNZERO }, /* Assemble if symbol value is not 0. */ + { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ + { PSEUDO, "LGM", LGM }, /* Enable link generation messages. */ + { PSEUDO, "LIST", LIST }, /* Enable listing. */ + { PSEUDO, "LIT", LIT }, /* Punch literal pool. */ + { PSEUDO, "LITBAS", LITBAS }, /* Set literal pool base. */ + { PSEUDO, "NOLGM", NOLGM }, /* Disable link generation messages. */ + { PSEUDO, "NOPUNC", NOPUNCH }, /* Turn off object code generation. */ + { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ + { PSEUDO, "PAGE", PAGE }, /* Set orign to page +1 or page n (0..37).*/ + { PSEUDO, "PAUSE", PAUSE }, /* Ignored */ + { PSEUDO, "RELOC", RELOC }, /* Assemble to run at a different address.*/ + { PSEUDO, "RIMPUN", RIMPUNCH }, /* Output in Read In Mode format. */ + { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ + { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ + { PSEUDO, "UNLIST", UNLIST }, /* Disable listing. */ + { PSEUDO, "VFD", VFD }, /* Variable field definition. */ + { PSEUDO, "ZBLOCK", ZBLOCK } /* Zero a block of memory. */ +}; + +/* Symbol Table */ +/* The table is put in lexical order on startup, so symbols can be */ +/* inserted as desired into the initial table. */ +SYM_T permanent_symbols[] = +{ + /* Memory Reference Instructions */ + { MRIFIX, "I", 00400 }, /* INDIRECT ADDRESSING */ + { MRIFIX, "Z", 00000 }, /* PAGE ZERO ADDRESS */ + { MRIFIX, "AND", 00000 }, /* LOGICAL AND */ + { MRIFIX, "TAD", 01000 }, /* TWO'S COMPLEMENT ADD */ + { MRIFIX, "ISZ", 02000 }, /* INCREMENT AND SKIP IF ZERO */ + { MRIFIX, "DCA", 03000 }, /* DEPOSIT AND CLEAR ACC */ + { MRIFIX, "JMS", 04000 }, /* JUMP TO SUBROUTINE */ + { MRIFIX, "JMP", 05000 }, /* JUMP */ + /* Floating Point Interpreter Instructions */ + { MRIFIX, "FEXT", 00000 }, /* FLOATING EXIT */ + { MRIFIX, "FADD", 01000 }, /* FLOATING ADD */ + { MRIFIX, "FSUB", 02000 }, /* FLOATING SUBTRACT */ + { MRIFIX, "FMPY", 03000 }, /* FLOATING MULTIPLY */ + { MRIFIX, "FDIV", 04000 }, /* FLOATING DIVIDE */ + { MRIFIX, "FGET", 05000 }, /* FLOATING GET */ + { MRIFIX, "FPUT", 06000 }, /* FLOATING PUT */ + { FIXED, "FNOR", 07000 }, /* FLOATING NORMALIZE */ + { FIXED, "FEXT", 00000 }, /* EXIT FROM FLOATING POINT INTERPRETER */ + { FIXED, "SQUARE", 00001 }, /* SQUARE C(FAC) */ + { FIXED, "SQROOT", 00002 }, /* TAKE SQUARE ROOT OF C(FAC) */ + /* Group 1 Operate Microinstrcutions */ + { FIXED, "OPR", 07000 }, /* OPERATE */ + { FIXED, "NOP", 07000 }, /* NO OPERATION */ + { FIXED, "IAC", 07001 }, /* INCREMENT AC */ + { FIXED, "RAL", 07004 }, /* ROTATE AC AND LINK LEFT ONE */ + { FIXED, "RTL", 07006 }, /* ROTATE AC AND LINK LEFT TWO */ + { FIXED, "RAR", 07010 }, /* ROTATE AC AND LINK RIGHT ONE */ + { FIXED, "RTR", 07012 }, /* ROTATE AC AND LINK RIGHT TWO */ + { FIXED, "CML", 07020 }, /* COMPLEMENT LINK */ + { FIXED, "CMA", 07040 }, /* COMPLEMEMNT AC */ + { FIXED, "CLL", 07100 }, /* CLEAR LINK */ + { FIXED, "CLA", 07200 }, /* CLEAR AC */ + /* Group 2 Operate Microinstructions */ + { FIXED, "BSW", 07002 }, /* Swap bytes in AC (PDP/8e) */ + { FIXED, "HLT", 07402 }, /* HALT THE COMPUTER */ + { FIXED, "OSR", 07404 }, /* INCLUSIVE OR SR WITH AC */ + { FIXED, "SKP", 07410 }, /* SKIP UNCONDITIONALLY */ + { FIXED, "SNL", 07420 }, /* SKIP ON NON-ZERO LINK */ + { FIXED, "SZL", 07430 }, /* SKIP ON ZERO LINK */ + { FIXED, "SZA", 07440 }, /* SKIP ON ZERO AC */ + { FIXED, "SNA", 07450 }, /* SKIP ON NON=ZERO AC */ + { FIXED, "SMA", 07500 }, /* SKIP MINUS AC */ + { FIXED, "SPA", 07510 }, /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */ + /* Combined Operate Microinstructions */ + { FIXED, "CIA", 07041 }, /* COMPLEMENT AND INCREMENT AC */ + { FIXED, "STL", 07120 }, /* SET LINK TO 1 */ + { FIXED, "GLK", 07204 }, /* GET LINK (PUT LINK IN AC BIT 11) */ + { FIXED, "STA", 07240 }, /* SET AC TO -1 */ + { FIXED, "LAS", 07604 }, /* LOAD ACC WITH SR */ + /* MQ Instructions (PDP/8e) */ + { FIXED, "MQL", 07421 }, /* Load MQ from AC, then clear AC. */ + { FIXED, "MQA", 07501 }, /* Inclusive OR MQ with AC */ + /* Program Interrupt */ + { FIXED, "IOT", 06000 }, + { FIXED, "ION", 06001 }, /* TURN INTERRUPT PROCESSOR ON */ + { FIXED, "IOF", 06002 }, /* TURN INTERRUPT PROCESSOR OFF */ + /* Program Interrupt, PDP-8/e */ + { FIXED, "SKON", 06000 }, /* Skip if interrupt on and turn int off. */ + { FIXED, "SRQ", 06003 }, /* Skip on interrupt request. */ + { FIXED, "GTF", 06004 }, /* Get interrupt flags. */ + { FIXED, "RTF", 06005 }, /* Restore interrupt flags. */ + { FIXED, "SGT", 06006 }, /* Skip on greater than flag. */ + { FIXED, "CAF", 06007 }, /* Clear all flags. */ + /* Keyboard/Reader */ + { FIXED, "KSF", 06031 }, /* SKIP ON KEYBOARD FLAG */ + { FIXED, "KCC", 06032 }, /* CLEAR KEYBOARD FLAG */ + { FIXED, "KRS", 06034 }, /* READ KEYBOARD BUFFER (STATIC) */ + { FIXED, "KRB", 06036 }, /* READ KEYBOARD BUFFER & CLEAR FLAG */ + /* Teleprinter/Punch */ + { FIXED, "TSF", 06041 }, /* SKIP ON TELEPRINTER FLAG */ + { FIXED, "TCF", 06042 }, /* CLEAR TELEPRINTER FLAG */ + { FIXED, "TPC", 06044 }, /* LOAD TELEPRINTER & PRINT */ + { FIXED, "TLS", 06046 }, /* LOAD TELPRINTER & CLEAR FLAG */ + /* High Speed Paper Tape Reader */ + { FIXED, "RSF", 06011 }, /* SKIP ON READER FLAG */ + { FIXED, "RRB", 06012 }, /* READ READER BUFFER AND CLEAR FLAG */ + { FIXED, "RFC", 06014 }, /* READER FETCH CHARACTER */ + /* PC8-E High Speed Paper Tape Reader & Punch */ + { FIXED, "RPE", 06010 }, /* Set interrupt enable for reader/punch */ + { FIXED, "PCE", 06020 }, /* Clear interrupt enable for rdr/punch */ + { FIXED, "RCC", 06016 }, /* Read reader buffer, clear flags & buf, */ + /* and fetch character. */ + /* High Speed Paper Tape Punch */ + { FIXED, "PSF", 06021 }, /* SKIP ON PUNCH FLAG */ + { FIXED, "PCF", 06022 }, /* CLEAR ON PUNCH FLAG */ + { FIXED, "PPC", 06024 }, /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */ + { FIXED, "PLS", 06026 }, /* LOAD PUNCH BUFFER AND CLEAR FLAG */ + /* DECtape Transport Type TU55 and DECtape Control Type TC01 */ + { FIXED, "DTRA", 06761 }, /* Contents of status register is ORed */ + /* into AC bits 0-9 */ + { FIXED, "DTCA", 06762 }, /* Clear status register A, all flags */ + /* undisturbed */ + { FIXED, "DTXA", 06764 }, /* Status register A loaded by exclusive */ + /* OR from AC. If AC bit 10=0, clear */ + /* error flags; if AC bit 11=0, DECtape */ + /* control flag is cleared. */ + { FIXED, "DTLA", 06766 }, /* Combination of DTCA and DTXA */ + { FIXED, "DTSF", 06771 }, /* Skip if error flag is 1 or if DECtape */ + /* control flag is 1 */ + { FIXED, "DTRB", 06772 }, /* Contents of status register B is */ + /* ORed into AC */ + { FIXED, "DTLB", 06774 }, /* Memory field portion of status */ + /* register B loaded from AC bits 6-8 */ + /* Disk File and Control, Type DF32 */ + { FIXED, "DCMA", 06601 }, /* CLEAR DISK MEMORY REQUEST AND */ + /* INTERRUPT FLAGS */ + { FIXED, "DMAR", 06603 }, /* LOAD DISK FROM AC, CLEAR AC READ */ + /* INTO CORE, CLEAR INTERRUPT FLAG */ + { FIXED, "DMAW", 06605 }, /* LOAD DISK FROM AC, WRITE ONTO DISK */ + /* FROM CORE, CLEAR INTERRUPT FLAG */ + { FIXED, "DCEA", 06611 }, /* CLEAR DISK EXTENDED ADDRESS AND */ + { FIXED, "DSAC", 06612 }, /* SKIP IF ADDRESS CONFIRMED FLAG = 1 */ + /* MEMORY ADDRESS EXTENSION REGISTER */ + { FIXED, "DEAL", 06615 }, /* CLEAR DISK EXTENDED ADDRESS AND */ + /* MEMORY ADDRESS EXTENSION REGISTER */ + /* AND LOAD SAME FROM AC */ + { FIXED, "DEAC", 06616 }, /* CLEAR AC, LOAD AC FROM DISK EXTENDED */ + /* ADDRESS REGISTER, SKIP IF ADDRESS */ + /* CONFIRMED FLAG = 1 */ + { FIXED, "DFSE", 06621 }, /* SKIP IF PARITY ERROR, DATA REQUEST */ + /* LATE, OR WRITE LOCK SWITCH FLAG = 0 */ + /* (NO ERROR) */ + { FIXED, "DFSC", 06622 }, /* SKIP IF COMPLETION FLAG = 1 (DATA */ + /* TRANSFER COMPLETE) */ + { FIXED, "DMAC", 06626 }, /* CLEAR AC, LOAD AC FROM DISK MEMORY */ + /* ADDRESS REGISTER */ + /* Disk File and Control, Type RF08 */ + { FIXED, "DCIM", 06611 }, + { FIXED, "DIML", 06615 }, + { FIXED, "DIMA", 06616 }, + /*{ FIXED, "DISK", 06623 },*/ + { FIXED, "DCXA", 06641 }, + { FIXED, "DXAL", 06643 }, + { FIXED, "DXAC", 06645 }, + { FIXED, "DMMT", 06646 }, + /* Memory Extension Control, Type 183 */ + { FIXED, "CDF", 06201 }, /* CHANGE DATA FIELD */ + { FIXED, "CIF", 06202 }, /* CHANGE INSTRUCTION FIELD */ + { FIXED, "CDI", 06203 }, /* Change data & instrution field. */ + { FIXED, "RDF", 06214 }, /* READ DATA FIELD */ + { FIXED, "RIF", 06224 }, /* READ INSTRUCTION FIELD */ + { FIXED, "RIB", 06234 }, /* READ INTERRUPT BUFFER */ + { FIXED, "RMF", 06244 }, /* RESTORE MEMORY FIELD */ + /* Memory Parity, Type MP8/I (MP8/L) */ + { FIXED, "SMP", 06101 }, /* SKIP IF MEMORY PARITY FLAG = 0 */ + { FIXED, "CMP", 06104 }, /* CLEAR MEMORY PAIRTY FLAG */ + /* Memory Parity, Type MP8-E (PDP8/e) */ + { FIXED, "DPI", 06100 }, /* Disable parity interrupt. */ + { FIXED, "SNP", 06101 }, /* Skip if no parity error. */ + { FIXED, "EPI", 06103 }, /* Enable parity interrupt. */ + { FIXED, "CNP", 06104 }, /* Clear parity error flag. */ + { FIXED, "CEP", 06106 }, /* Check for even parity. */ + { FIXED, "SPO", 06107 }, /* Skip on parity option. */ +}; /* End-of-Symbols for Permanent Symbol Table */ + +/* Global variables */ +SYM_T *symtab; /* Symbol Table */ +int symbol_top; /* Number of entries in symbol table. */ + +SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ +int number_of_fixed_symbols; + +/*----------------------------------------------------------------------------*/ + +WORD32 *xreftab; /* Start of the concordance table. */ + +ERRSAVE_T error_list[20]; +int save_error_count; + +LPOOL_T pz; /* Storage for page zero constants. */ +LPOOL_T cp; /* Storage for current page constants. */ +WORD32 lit_base[TOTAL_PAGES]; /* Literal base address for all pages. */ +WORD32 lit_loc[TOTAL_PAGES]; /* Literal current location for all pages. */ + +char s_detected[] = "detected"; +char s_error[] = "error"; +char s_errors[] = "errors"; +char s_no[] = "No"; +char s_page[] = "Page"; +char s_symtable[] = "Symbol Table"; +char s_xref[] = "Cross Reference"; + +/* Assembler diagnostic messages. */ +/* Some attempt has been made to keep continuity with the PAL-III and */ +/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ +/* exists, then the indicator is put in the listing as the first two */ +/* characters of the diagnostic message. The PAL-III indicators where used */ +/* when there was a choice between using MACRO-8 and PAL-III indicators. */ +/* The character pairs and their meanings are: */ +/* DT Duplicate Tag (symbol) */ +/* IC Illegal Character */ +/* ID Illegal Redefinition of a symbol. An attempt was made to give */ +/* a symbol a new value not via =. */ +/* IE Illegal Equals An equal sign was used in the wrong context, */ +/* (e.g., A+B=C, or TAD A+=B) */ +/* II Illegal Indirect An off page reference was made, but a literal */ +/* could not be generated because the indirect bit was already set. */ +/* IR Illegal Reference (address is not on current page or page zero) */ +/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ +/* RD ReDefintion of a symbol */ +/* ST Symbol Table full */ +/* UA Undefined Address (undefined symbol) */ +/* ZE Zero Page Exceeded (see above, or out of space) */ +EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; +EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; +EMSG_T illegal_character = { "IC illegal char", "illegal character" }; +EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; +EMSG_T label_syntax = { "IC label syntax", "label syntax" }; +EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; +EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; +EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; +EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; +EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; +EMSG_T illegal_reference = { "IR off page", "illegal reference" }; +EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; +EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; +EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; +EMSG_T literal_overflow = { "PE page exceeded", + "current page literal capacity exceeded" }; +EMSG_T pz_literal_overflow = { "ZE page exceeded", + "page zero capacity exceeded" }; +EMSG_T dubl_overflow = { "dubl overflow", "DUBL value overflow" }; +EMSG_T fltg_overflow = { "fltg overflow", "FLTG value overflow" }; +EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; +EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; +EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; +EMSG_T illegal_field_value = { "expr out of range", + "field value not in range of 0 through 7" }; +EMSG_T illegal_vfd_value = { "width out of range", + "VFD field width not in range" }; +EMSG_T no_literal_value = { "no value", "No literal value" }; +EMSG_T text_string = { "no delimiter", + "Text string delimiters not matched" }; +EMSG_T in_rim_mode = { "not OK in rim mode" + "FIELD pseudo-op not valid in RIM mode" }; +EMSG_T lt_expected = { "'<' expected", "'<' expected" }; +EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; +EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; +EMSG_T bad_dummy_arg = { "bad dummy arg", + "Bad dummy argument following DEFINE" }; +EMSG_T macro_too_long = { "macro too long", "Macro too long" }; +EMSG_T no_virtual_memory = { "out of memory", + "Insufficient memory for macro" }; +EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; + +/*----------------------------------------------------------------------------*/ + +FILE *errorfile; +FILE *infile; +FILE *listfile; +FILE *listsave; +FILE *objectfile; +FILE *objectsave; + +char errorpathname[NAMELEN]; +char filename[NAMELEN]; +char listpathname[NAMELEN]; +char objectpathname[NAMELEN]; +char *pathname; +char permpathname[NAMELEN]; + +char mac_buffer[MAC_MAX_LENGTH + 1]; +char *mac_bodies[MAC_TABLE_LENGTH]; +char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; +int mac_arg_pos[26] = { 0 }; + +int list_lineno; +int list_pageno; +char list_title[4*LINELEN]; +BOOL list_title_set; /* Set if TITLE pseudo-op used. */ +char line[4*LINELEN]; /* Input line. */ +int lineno; /* Current line number. */ +char mac_line[4*LINELEN]; /* Saved macro invocation line. */ +int page_lineno; /* print line number on current page. */ +WORD32 listed; /* Listed flag. */ +WORD32 listedsave; + +WORD32 cc; /* Column Counter (char position in line). */ +WORD32 checksum; /* Generated checksum */ +BOOL binary_data_output; /* Set true when data has been output. */ +WORD32 clc; /* Location counter */ +char delimiter; /* Character immediately after eval'd term. */ +int errors; /* Number of errors found so far. */ +BOOL error_in_line; /* TRUE if error on current line. */ +int errors_pass_1; /* Number of errors on pass 1. */ +WORD32 field; /* Current field */ +WORD32 fieldlc; /* location counter without field portion. */ +int filix_curr; /* Index in argv to current input file. */ +int filix_start; /* Start of input files in argv. */ +BOOL fltg_input; /* TRUE when doing floating point input. */ +BOOL indirect_generated; /* TRUE if an off page address generated. */ +WORD32 lexstartprev; /* Where previous lexeme started. */ +WORD32 lextermprev; /* Where previous lexeme ended. */ +WORD32 lexstart; /* Index of current lexeme on line. */ +WORD32 lexterm; /* Index of character after current lexeme. */ +WORD32 mac_cc; /* Saved cc after macro invocation. */ +WORD32 mac_count; /* Total macros defined. */ +BOOL nomac_exp; /* Print macro expansions. */ +char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ +WORD32 maxcc; /* Current line length. */ +BOOL lgm_flag; /* Link generated messages enable flag. */ +BOOL overflow; /* Overflow flag for math routines. */ +WORD32 pass; /* Number of current pass. */ +BOOL print_permanent_symbols; +WORD32 radix; /* Default number radix. */ +WORD32 reloc; /* The relocation distance. */ +BOOL rim_mode; /* Generate rim format, defaults to bin */ +int save_argc; /* Saved argc. */ +char **save_argv; /* Saved *argv[]. */ +BOOL symtab_print; /* Print symbol table flag */ +BOOL xref; + +FLTG_T fltg_ac; /* Value holder for evalFltg() */ +SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ +SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ +SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ + + +/******************************************************************************/ +/* */ +/* Function: main */ +/* */ +/* Synopsis: Starting point. Controls order of assembly. */ +/* */ +/******************************************************************************/ +int main( int argc, char *argv[] ) +{ + int ix; + int space; + + save_argc = argc; + save_argv = argv; + + /* Set the default values for global symbols. */ + binary_data_output = FALSE; + fltg_input = FALSE; + nomac_exp = TRUE; + print_permanent_symbols = FALSE; + rim_mode = FALSE; + symtab_print = FALSE; + xref = FALSE; + pathname = NULL; + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) + { + mac_bodies[ix] = NULL; + } + + /* Get the options and pathnames */ + getArgs( argc, argv ); + + /* Setup the error file in case symbol table overflows while installing the */ + /* permanent symbols. */ + errorfile = fopen( errorpathname, "w" ); + errors = 0; + save_error_count = 0; + pass = 0; /* This is required for symbol table initialization. */ + symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); + + if( symtab == NULL ) + { + fprintf( stderr, "Could not allocate memory for symbol table.\n"); + exit( -1 ); + } + + /* Place end marker in symbol table. */ + symtab[0] = sym_undefined; + symbol_top = 0; + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Enter the pseudo-ops into the symbol table */ + for( ix = 0; ix < DIM( pseudo ); ix++ ) + { + defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); + } + + /* Enter the predefined symbols into the table. */ + /* Also make them part of the permanent symbol table. */ + for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) + { + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type | DEFFIX , 0 ); + } + + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Do pass one of the assembly */ + checksum = 0; + pass = 1; + onePass(); + errors_pass_1 = errors; + fclose ( errorfile ); + + /* Set up for pass two */ + errorfile = fopen( errorpathname, "w" ); + objectfile = fopen( objectpathname, "wb" ); + objectsave = objectfile; + + listfile = fopen( listpathname, "w" ); + listsave = listfile; + + punchLeader( 0 ); + checksum = 0; + + /* Do pass two of the assembly */ + errors = 0; + save_error_count = 0; + + if( xref ) + { + /* Get the amount of space that will be required for the concordance. */ + for( space = 0, ix = 0; ix < symbol_top; ix++ ) + { + symtab[ix].xref_index = space; /* Index into concordance table. */ + space += symtab[ix].xref_count + 1; + symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ + + } + /* Allocate the necessary space. */ + xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); + + /* Clear the cross reference space. */ + for( ix = 0; ix < space; ix++ ) + { + xreftab[ix] = 0; + } + } + pass = 2; + onePass(); + + /* Undo effects of NOPUNCH for any following checksum */ + objectfile = objectsave; + punchChecksum(); + + /* Works great for trailer. */ + punchLeader( 1 ); + + /* undo effects of NOLIST for any following output to listing file. */ + listfile = listsave; + + /* Display value of error counter. */ + if( errors == 0 ) + { + fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); + } + else + { + fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( listfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( stderr, " %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + } + + if( symtab_print ) + { + printSymbolTable(); + } + + if( print_permanent_symbols ) + { + printPermanentSymbolTable(); + } + + if( xref ) + { + printCrossReference(); + } + + fclose( objectfile ); + fclose( listfile ); + fclose( errorfile ); + if( errors == 0 && errors_pass_1 == 0 ) + { + remove( errorpathname ); + } + + return( errors != 0 ); +} /* main() */ + +/******************************************************************************/ +/* */ +/* Function: getArgs */ +/* */ +/* Synopsis: Parse command line, set flags accordingly and setup input and */ +/* output files. */ +/* */ +/******************************************************************************/ +void getArgs( int argc, char *argv[] ) +{ + WORD32 len; + WORD32 ix, jx; + + /* Set the defaults */ + errorfile = NULL; + infile = NULL; + listfile = NULL; + listsave = NULL; + objectfile = NULL; + objectsave = NULL; + + for( ix = 1; ix < argc; ix++ ) + { + if( argv[ix][0] == '-' ) + { + for( jx = 1; argv[ix][jx] != 0; jx++ ) + { + switch( argv[ix][jx] ) + { + case 'd': + symtab_print = TRUE; + break; + + case 'm': + nomac_exp = FALSE; + break; + + case 'r': + rim_mode = TRUE; + break; + + case 'p': + print_permanent_symbols = TRUE; + break; + + case 'x': + xref = TRUE; + break; + + default: + fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); + fprintf( stderr, " -d -- dump symbol table\n" ); + fprintf( stderr, " -m -- print macro expansions\n" ); + fprintf( stderr, " -r -- output rim format file\n" ); + fprintf( stderr, " -p -- output permanent symbols to file\n" ); + fprintf( stderr, " -x -- output cross reference to file\n" ); + fflush( stderr ); + exit( -1 ); + } /* end switch */ + } /* end for */ + } + else + { + filix_start = ix; + pathname = argv[ix]; + break; + } + } /* end for */ + + if( pathname == NULL ) + { + fprintf( stderr, "%s: no input file specified\n", argv[0] ); + exit( -1 ); + } + + len = strlen( pathname ); + if( len > NAMELEN - 5 ) + { + fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); + exit( -1 ); + } + + /* Now make the pathnames */ + /* Find last '.', if it exists. */ + jx = len - 1; + while( pathname[jx] != '.' && pathname[jx] != '/' + && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + + switch( pathname[jx] ) + { + case '.': + break; + + case '/': + case '\\': + jx = len; + break; + + default: + break; + } + + /* Add the pathname extensions. */ + strncpy( objectpathname, pathname, jx ); + objectpathname[jx] = '\0'; + strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); + + strncpy( listpathname, pathname, jx ); + listpathname[jx] = '\0'; + strcat( listpathname, ".lst" ); + + strncpy( errorpathname, pathname, jx ); + errorpathname[jx] = '\0'; + strcat( errorpathname, ".err" ); + + strncpy( permpathname, pathname, jx ); + permpathname[jx] = '\0'; + strcat( permpathname, ".prm" ); + + /* Extract the filename from the path. */ + if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) + { + pathname[1] = '\\'; /* MS-DOS style pathname */ + } + + jx = len - 1; + while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + strcpy( filename, &pathname[jx + 1] ); + +} /* getArgs() */ + + +/******************************************************************************/ +/* */ +/* Function: onePass */ +/* */ +/* Synopsis: Do one assembly pass. */ +/* */ +/******************************************************************************/ +void onePass() +{ + BOOL blanks; + int ix; + int jx; + char name[SYMLEN]; + WORD32 newclc; + BOOL scanning_line; + WORD32 start; + SYM_T *sym; + WORD32 term; + WORD32 val; + + clc = 0200; /* Default starting address is 200 octal. */ + field = 0; + fieldlc = 0200; + reloc = 0; + for( ix = 0; ix < TOTAL_PAGES; ix++ ) + { + lit_loc[ix] = lit_base[ix] = 00200; + } + mac_count = 0; /* No macros defined. */ + mac_ptr = NULL; /* Not in a macro. */ + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) + { + if ( mac_bodies[ix] ) + { + free( mac_bodies[ix] ); + } + } + cp.error = FALSE; + pz.error = FALSE; + listed = TRUE; + lgm_flag = TRUE; + lineno = 0; + list_pageno = 0; + list_lineno = 0; + list_title_set = FALSE; + page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ + radix = 8; /* Initial radix is octal (base 8). */ + + /* Now open the first input file. */ + filix_curr = filix_start; /* Initialize pointer to input files. */ + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); + exit( -1 ); + } + + while( TRUE ) + { + readLine(); + nextLexeme(); + + scanning_line = TRUE; + while( scanning_line ) + { + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case ';': + nextLexeme(); + break; + + case '$': + endOfBinary(); + fclose( infile ); + return; + + case '*': + nextLexeme(); /* Skip '*', (set origin symbol) */ + newclc = ((getExpr())->val & 07777 ) | field; + /* Do not change Current Location Counter if an error occurred. */ + if( !error_in_line ) + { + if(( newclc & 07600 ) != ( clc & 07600 )) + { + /* Current page has changed. */ + punchLiteralPool( &cp, clc - 1 ); + } + clc = newclc - reloc; + fieldlc = clc & 07777; + + if( !rim_mode ) + { + /* Not rim mode, put out origin. */ + punchOrigin( clc ); + } + printLine( line, 0, fieldlc, LINE_VAL ); + } + break; + + default: + switch( line[lexterm] ) + { + case ',': + if( isalpha( line[lexstart] )) + { + /* Use lookup so symbol will not be counted as reference. */ + sym = lookup( lexemeToName( name, lexstart, lexterm )); + if( M_DEFINED( sym->type )) + { + if( sym->val != (clc & 07777) && pass == 2 ) + { + errorSymbol( &duplicate_label, sym->name, lexstart ); + } + sym->type = sym->type | DUPLICATE; + } + /* Must call define on pass 2 to generate concordance. */ + defineLexeme( lexstart, lexterm, ( clc+reloc ), LABEL ); + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + nextLexeme(); /* skip label */ + nextLexeme(); /* skip comma */ + break; + + case '=': + if( isalpha( line[lexstart] )) + { + start = lexstart; + term = lexterm; + delimiter = line[lexterm]; + nextLexBlank(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + val = getExprs(); + defineLexeme( start, term, val, DEFINED ); + printLine( line, 0, val, LINE_VAL ); + } + else + { + errorLexeme( &symbol_syntax, lexstartprev ); + nextLexeme(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + getExprs(); /* skip expression */ + } + break; + + default: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + val = sym->val; + if( M_MACRO( sym->type )) + { /* Find arguments. */ + blanks = TRUE; /* Expecting blanks. */ + for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) + { + if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; + else if( blanks ) + { + mac_arg_pos[jx++] = cc; + blanks = FALSE; + } + } /* end for */ + for( ; jx < MAC_MAX_ARGS; jx++ ) + { + mac_arg_pos[jx] = 0; + } + for( jx = 0; jx < LINELEN; jx++ ) + { + mac_line[jx] = line[jx]; + } + mac_cc = cc; /* Save line and position in line. */ + mac_ptr = mac_bodies[val]; + if( mac_ptr ) scanning_line = FALSE; + else nextLexeme(); + } /* end if macro */ + else if( M_PSEUDO( sym->type )) + { + nextLexeme(); /* Skip symbol */ + scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 ); + } + else + { + /* Identifier is not a pseudo-op, interpret as load value */ + punchOutObject( clc, getExprs() & 07777 ); + incrementClc(); + } + } + else + { + /* Identifier is a value, interpret as load value */ + punchOutObject( clc, getExprs() & 07777 ); + incrementClc(); + } + break; + } /* end switch */ + break; + } /* end switch */ + } /* end if */ + } /* end while( scanning_line ) */ + } /* end while( TRUE ) */ +} /* onePass() */ + + +/******************************************************************************/ +/* */ +/* Function: getExprs */ +/* */ +/* Synopsis: Or together a list of blank separated expressions, from the */ +/* current lexeme onward. Leave the current lexeme as */ +/* the last one in the list. */ +/* */ +/******************************************************************************/ +WORD32 getExprs() +{ + SYM_T *symv; + SYM_T *symt; + WORD32 temp; + SYMTYP temp_type; + WORD32 value; + SYMTYP value_type; + + symv = getExpr(); + value = symv->val; + value_type = symv->type; + + while( TRUE ) + { + if( isdone( line[lexstart] )) + { + return( value ); + } + switch( line[lexstart] ) + { + case ')': + case ']': + return( value ); + + default: + break; + } + + /* Interpret space as logical or */ + symt = getExpr(); + temp = symt->val & 07777; + temp_type = symt->type; + + switch( value_type ) + { + case MRI: + case MRIFIX: + /* Previous symbol was a Memory Reference Instruction. */ + switch( temp_type ) + { + case MRI: + case MRIFIX: + /* Current symbol is also a Memory Reference Instruction. */ + value |= temp; /* Just OR the MRI instructions. */ + break; + + default: + /* Now have the address part of the MRI instruction. */ + if( temp < 00200 ) + { + value |= temp; /* Page zero MRI. */ + } + else if( (( fieldlc + reloc ) & 07600 ) <= temp + && temp <= (( fieldlc + reloc ) | 0177 )) + { + value |= ( PAGE_BIT | (temp & ADDRESS_FIELD )); /* Current page MRI */ + } + else + { + if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) + { + /* Already indirect, can't generate */ + errorSymbol( &illegal_indirect, symt->name, lexstartprev ); + } + else + { + /* Now fix off page reference. */ + /* Search current page literal pool for needed value. */ + /* Set Indirect Current Page */ + if( testZeroPool( temp )) + { + value |= ( 00400 | insertLiteral( &pz, field, temp )); + } + else + { + value |= ( 00600 | insertLiteral( &cp, clc, temp )); + } + indirect_generated = TRUE; + } + } + break; + } + break; + + default: + value |= temp; /* Normal 12 bit value. */ + break; + } + } /* end while */ +} /* getExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getExpr */ +/* */ +/* Synopsis: Get an expression, from the current lexeme onward, leave the */ +/* current lexeme as the one after the expression. Expressions */ +/* contain terminal symbols (identifiers) separated by operators. */ +/* */ +/******************************************************************************/ +SYM_T *getExpr() +{ + delimiter = line[lexterm]; + + if( line[lexstart] == '-' ) + { + nextLexBlank(); + sym_getexpr = *(eval()); + sym_getexpr.val = ( - sym_getexpr.val ); + } + else + { + sym_getexpr = *(eval()); + } + + + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val += (eval())->val; + break; + + case '-': /* subtract */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val -= (eval())->val; + break; + + case '^': /* multiply */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val *= (eval())->val; + break; + + case '%': /* divide */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val /= (eval())->val; + break; + + case '&': /* and */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val &= (eval())->val; + break; + + case '!': /* or */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val |= (eval())->val; + break; + + default: + if( isend( line[lexstart] )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '/': + case ';': + case ')': + case ']': + case '<': + case ':': + case ',': + break; + + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + } + return( &sym_getexpr ); + } + } /* end while */ +} /* getExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: eval */ +/* */ +/* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ +/* */ +/******************************************************************************/ +SYM_T *eval() +{ + WORD32 digit; + WORD32 from; + WORD32 loc; + SYM_T *sym; + WORD32 val; + + val = 0; + + delimiter = line[lexterm]; + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + if( M_UNDEFINED( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &undefined_symbol, sym->name, lexstart ); + } + nextLexeme(); + return( sym ); + } + else if( M_PSEUDO( sym->type )) + { + if( sym->val == DECIMAL ) + { + radix = 10; + } + else if( sym->val == OCTAL ) + { + radix = 8; + } + else if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else if( M_MACRO( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else + { + nextLexeme(); + return( sym ); + } + } + else if( isdigit( line[lexstart] )) + { + from = lexstart; + val = 0; + while( from < lexterm ) + { + if( isdigit( line[from] )) + { + digit = (WORD32) line[from++] - (WORD32) '0'; + if( digit < radix ) + { + val = val * radix + digit; + } + else + { + errorLexeme( &number_not_radix, from - 1 ); + val = 0; + from = lexterm; + } + } + else + { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + from = lexterm; + } + } + nextLexeme(); + sym_eval.val = val; + return( &sym_eval ); + } + else + { + switch( line[lexstart] ) + { + case '"': /* Character literal */ + if( lexstart + 2 < maxcc ) + { + val = line[lexstart + 1] | 0200; + delimiter = line[lexstart + 2]; + cc = lexstart + 2; + } + else + { + errorMessage( &no_literal_value, lexstart ); + } + nextLexeme(); + break; + + case '.': /* Value of Current Location Counter */ + val = clc + reloc; + nextLexeme(); + break; + + case '[': /* Generate literal on page zero. */ + nextLexBlank(); /* Skip bracket */ + val = getExprs() & 07777; + if( line[lexstart] == ']' ) + { + delimiter = line[lexterm]; + nextLexeme(); /* Skip end bracket */ + } + else + { + /* errorMessage( "parens", lexstart ); */ + } + sym_eval.val = insertLiteral( &pz, field, val ); + return( &sym_eval ); + + case '(': /* Generate literal on current page. */ + nextLexBlank(); /* Skip paren */ + val = getExprs() & 07777; + + if( line[lexstart] == ')' ) + { + delimiter = line[lexterm]; + nextLexeme(); /* Skip end paren */ + } + else + { + /* errorMessage( "parens", NULL ); */ + } + if( testZeroPool( val )) + { + sym_eval.val = insertLiteral( &pz, field, val ); + } + else + { + loc = insertLiteral( &cp, clc, val ); + sym_eval.val = loc + (( clc + reloc ) & 077600 ); + } + return( &sym_eval ); + + default: + switch( line[lexstart] ) + { + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + break; + + default: + errorMessage( &illegal_character, lexstart ); + break; + } + val = 0; /* On error, set value to zero. */ + nextLexBlank(); /* Go past illegal character. */ + } + } + sym_eval.val = val; + return( &sym_eval ); +} /* eval() */ + + +/******************************************************************************/ +/* */ +/* Function: inputDubl */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. */ +/* */ +/******************************************************************************/ +void inputDubl() +{ + WORD32 dublvalue; + BOOL scanning_line; + + scanning_line = TRUE; + do + { + while( scanning_line ) + { + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case ';': + nextLexeme(); + break; + + case '+': + delimiter = line[lexterm]; + nextLexBlank(); + case '-': + default: + if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) + { + dublvalue = getDublExprs(); + punchOutObject( clc, (WORD32)(( dublvalue >> 12 ) & 07777 )); + incrementClc(); + punchOutObject( clc, (WORD32)( dublvalue & 07777 )); + incrementClc(); + } + else + { + return; /* Non-numeric input, back to assembly. */ + } + break; + } /* end switch */ + } /* end if */ + + if( error_in_line ) + { + return; /* Error occurred, exit DUBL input mode. */ + } + } /* end while( scanning_line ) */ + readLine(); + nextLexeme(); + scanning_line = TRUE; + } + while( TRUE ); +} /* inputDubl() */ + + +/******************************************************************************/ +/* */ +/* Function: getDublExprs */ +/* */ +/* Synopsis: Get a DUBL expression. */ +/* */ +/******************************************************************************/ +WORD32 getDublExprs() +{ + WORD32 dublvalue; + + dublvalue = getDublExpr(); + + while( TRUE ) + { + if( isdone( line[lexstart] )) + { + return( dublvalue ); + } + else + { + errorMessage( &illegal_expression, lexstart - 1 ); + return( 0 ); + } + } /* end while */ +} /* getDublExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getDublExpr */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. The */ +/* number is always considered to have a decimal radix. */ +/* */ +/******************************************************************************/ +WORD32 getDublExpr() +{ + WORD32 dublvalue; + + delimiter = line[lexterm]; + if( line[lexstart] == '-' ) + { + nextLexBlank(); + dublvalue = evalDubl( 0 ); + nextLexeme(); + /* Test for any value greater than 23 bits in length. */ + if( (unsigned long int)dublvalue > 040000000L ) + { + errorMessage( &dubl_overflow, lexstart ); + dublvalue = 0; + } + dublvalue = -dublvalue; + } + else + { + dublvalue = evalDubl( 0 ); + nextLexeme(); + /* Test for any value greater than 23 bits in length. */ + if( (unsigned long int)dublvalue > 037777777L ) + { + errorMessage( &dubl_overflow, lexstart ); + dublvalue = 0; + } + } + + if( is_blank( delimiter )) + { + return( dublvalue ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + dublvalue = 0; + return( dublvalue ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + case '-': /* subtract */ + case '^': /* multiply */ + case '%': /* divide */ + case '&': /* and */ + case '!': /* or */ + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + dublvalue = 0; + break; + + default: + if( isend( line[lexstart] )) + { + return( dublvalue ); + } + + switch( line[lexstart] ) + { + case '/': + case ';': + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + dublvalue = 0; + break; + } + return( dublvalue ); + } + } /* end while */ +} /* getDublExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: evalDubl */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. The */ +/* number is always considered to have a decimal radix. */ +/* */ +/******************************************************************************/ +WORD32 evalDubl( WORD32 initial_value ) +{ + WORD32 digit; + WORD32 from; + WORD32 dublvalue; + WORD32 olddublvalue; + + overflow = FALSE; + delimiter = line[lexterm]; + from = lexstart; + dublvalue = initial_value; + + while( from < lexterm ) + { + if( isdigit( line[from] )) + { + olddublvalue = dublvalue; + digit = (WORD32)( line[from++] - '0' ); + dublvalue = dublvalue * 10 + digit; + if( dublvalue < olddublvalue ) + { + overflow = TRUE; + } + } + else + { + errorLexeme( ¬_a_number, from ); + dublvalue = 0; + from = lexterm; + } + } + return( dublvalue ); +} /* evalDubl() */ + + +/******************************************************************************/ +/* */ +/* Function: inputFltg */ +/* */ +/* Synopsis: Get the value of the current lexeme as a Floating Point const. */ +/* */ +/******************************************************************************/ +void inputFltg() +{ + FLTG_T *fltg; + BOOL scanning_line; + + fltg_input = TRUE; /* Set lexeme scanner for floating point. */ + scanning_line = TRUE; + while( TRUE ) + { + while( scanning_line ) + { + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case ';': + nextLexeme(); + break; + + case '+': + delimiter = line[lexterm]; + nextLexBlank(); + case '-': + default: + if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) + { + fltg = getFltgExprs(); + punchOutObject( clc, ( fltg->exponent & 07777 )); + incrementClc(); + punchOutObject( clc, (WORD32)(( fltg->mantissa >> 12 ) & 07777 )); + incrementClc(); + punchOutObject( clc, (WORD32)( fltg->mantissa & 07777 )); + incrementClc(); + } + else + { + fltg_input = FALSE; /* Reset lexeme scanner. */ + return; /* Non-numeric input, back to assembly. */ + } + break; + } /* end switch */ + } /* end if */ + + if( error_in_line ) + { + fltg_input = FALSE; /* Reset lexeme scanner. */ + return; /* Error occurred, exit FLTG input mode. */ + } + } /* end while( scanning_line ) */ + readLine(); + nextLexeme(); + scanning_line = TRUE; + } +} /* inputFltg() */ + + +/******************************************************************************/ +/* */ +/* Function: getFltgExprs */ +/* */ +/* Synopsis: Get a FLTG expression. */ +/* */ +/******************************************************************************/ +FLTG_T *getFltgExprs() +{ + FLTG_T *fltg; + + fltg = getFltgExpr(); + + while( TRUE ) + { + if( isdone( line[lexstart] )) + { + return( fltg ); + } + else + { + errorMessage( &illegal_expression, lexstart - 1 ); + return( 0 ); + } + } /* end while */ +} /* getFltgExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getFltgExpr */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. The */ +/* number is always considered to have a decimal radix. */ +/* */ +/******************************************************************************/ +FLTG_T *getFltgExpr() +{ + FLTG_T *fltg; + + delimiter = line[lexterm]; + fltg = evalFltg(); + /* Test for any value greater than 23 bits in length. */ + if( (unsigned long int)fltg->mantissa> 077777777L ) + { + errorMessage( &fltg_overflow, lexstart ); + } + + if( is_blank( delimiter )) + { + return( fltg ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + fltg = 0; + return( fltg ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + case '-': /* subtract */ + case '^': /* multiply */ + case '%': /* divide */ + case '&': /* and */ + case '!': /* or */ + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + fltg = NULL; + break; + + default: + if( isend( line[lexstart] )) + { + return( fltg ); + } + + switch( line[lexstart] ) + { + case '/': + case ';': + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + fltg = NULL; + break; + } + return( fltg ); + } + } /* end while */ +} /* getFltgExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: evalFltg */ +/* */ +/* Synopsis: Get the value of the current lexeme as a floating point value. */ +/* Floating point input is alwasy considered decimal. */ +/* The general format of a floating point number is: */ +/* +-ddd.dddE+-dd where each d is a decimal digit. */ +/* */ +/******************************************************************************/ +FLTG_T *evalFltg() +{ + BYTE current_state; + BYTE current_col; + WORD32 exponent; + FLTG_T *fltg; + WORD32 input_value; + BOOL negate; + BOOL negate_exponent; + BYTE next_state; + int right_digits; + + /* This uses a lexical analyzer to parse the floating point format. */ + static BYTE state_table[][10] = + { + /* 0 1 2 3 4 5 6 Oolumn index */ + /* + - d . E sp p State Comment */ + { 2, 1, 3, 4, 10, 10, 10 }, /* 0 Initial state. */ + { 11, 11, 3, 4, 11, 11, 11 }, /* 1 - */ + { 11, 11, 3, 4, 11, 11, 11 }, /* 2 + */ + { 10, 10, 10, 4, 6, 10, 10 }, /* 3 # (+-ddd) */ + { 11, 11, 5, 11, 11, 10, 10 }, /* 4 . (+-ddd.) */ + { 11, 11, 11, 11, 6, 10, 11 }, /* 5 # (+-ddd.ddd) */ + { 8, 7, 9, 11, 11, 11, 11 }, /* 6 E (+-ddd.dddE) */ + { 11, 11, 9, 11, 11, 11, 11 }, /* 7 - (+-ddd.dddE- */ + { 11, 11, 9, 11, 11, 11, 11 }, /* 8 + (+-ddd.dddE+ */ + { 11, 11, 11, 11, 11, 10, 11 } /* 9 # (+-ddd.dddE+-dd */ + /* 10 Completion state */ + /* 11 Error state. */ + }; + + delimiter = line[lexterm]; + fltg = &fltg_ac; + fltg->exponent = 0; + fltg->mantissa = 0; + input_value = 0; + negate = FALSE; + negate_exponent = FALSE; + exponent = 0; + right_digits = 0; + current_state = 0; + + while( TRUE ) + { + /* Classify character. This is the column index. */ + switch( line[lexstart] ) + { + case '+': + current_col = 0; + break; + + case '-': + current_col = 1; + break; + + case '.': + current_col = 3; + break; + + case 'E': case 'e': + current_col = 4; + break; + + default: + if( isdigit( line[lexstart] )) + { + current_col = 2; + } + else if( isdone( line[lexstart] )) + { + current_col = 5; + } + else + { + current_col = 6; + } + break; + } + + next_state = state_table[current_state][current_col]; + + switch( next_state ) + { + case 1: /* - */ + negate = TRUE; + case 2: /* + */ + delimiter = line[lexterm]; /* Move past the + or - character. */ + nextLexBlank(); + break; + + case 3: /* Number (+-ddd) */ + input_value = evalDubl( 0 ); /* Integer part of the number. */ + nextLexeme(); /* Move past previous lexeme. */ + break; + + case 4: + delimiter = line[lexterm]; + nextLexBlank(); /* Move past the . character. */ + break; + + case 5: /* . (+-ddd.ddd) */ + /* Fractional part of the number. */ + input_value = evalDubl( input_value ); + right_digits = lexterm - lexstart;/* Digit count to right of decimal. */ + nextLexeme(); /* Move past previous lexeme. */ + break; + + case 6: /* E (+-ddd.dddE) */ + delimiter = line[lexterm]; /* Move past the E. */ + nextLexBlank(); + break; + + case 7: /* - (+-ddd.dddE-) */ + negate_exponent = TRUE; + case 8: /* + (+-ddd.dddE+) */ + delimiter = line[lexterm]; /* Move past the + or - character. */ + nextLexBlank(); + break; + + case 9: /* # (+-ddd.dddE+-dd) */ + exponent = (int)evalDubl( 0 ); /* Exponent of floating point number. */ + if( negate_exponent ) { exponent = - exponent; } + nextLexeme(); /* Move past previous lexeme. */ + break; + + case 10: /* Floating number parsed, convert */ + /* the number. */ + /* Get the exponent for the number as input. */ + exponent -= right_digits; + + /* Remove trailing zeros and adjust the exponent accordingly. */ + while(( input_value % 10 ) == 0 ) + { + input_value /= 10; + exponent++; + } + + /* Convert the number to floating point. The number is calculated with */ + /* a 27 bit mantissa to improve precision. The extra 3 bits are */ + /* discarded after the result has been calculated. */ + fltg->exponent = 26; + fltg->mantissa = input_value << 3; + normalizeFltg( fltg ); + + + while( exponent != 0 ) + { + if( exponent < 0 ) + { + /* Decimal point is to the left. */ + fltg->mantissa /= 10; + normalizeFltg( fltg ); + exponent++; + } + else if( exponent > 0 ) + { + /* Decimal point is to the right. */ + fltg->mantissa *= 10; + normalizeFltg( fltg ); + exponent--; + } + } + + /* Discard the extra precsion used for calculating the number. */ + fltg->mantissa >>= 3; + fltg->exponent -= 3; + if( negate ) + { + fltg->mantissa = (- fltg->mantissa ) & 077777777L; + } + return( fltg ); + + case 11: /* Error in format. */ + /* Not a properly constructued floating point number. */ + return( fltg ); + default: + break; + } + /* Set state for next pass through the loop. */ + current_state = next_state; + } +} /* evalFltg() */ + + + +/******************************************************************************/ +/* */ +/* Function: normalizeFltg */ +/* */ +/* Synopsis: Normalize a PDP-8 double precision floating point number. */ +/* */ +/******************************************************************************/ +void normalizeFltg( FLTG_T *fltg ) +{ + /* Normalize the floating point number. */ + if( fltg->mantissa != 0 ) + { + if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 ) + { + while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 ) + + { + fltg->mantissa <<= 1; + fltg->exponent--; + } + } + else + { + while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 ) + { + fltg->mantissa >>= 1; + fltg->exponent++; + } + } + } + else + { + fltg->exponent = 0; + } + return; +} + + +/******************************************************************************/ +/* */ +/* Function: incrementClc */ +/* */ +/* Synopsis: Set the next assembly location. Test for collision with */ +/* the literal tables. */ +/* */ +/******************************************************************************/ +WORD32 incrementClc() +{ + testForLiteralCollision( clc ); + + /* Incrementing the location counter is not to change field setting. */ + clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); + fieldlc = clc & 07777; + return( clc ); +} /* incrementClc() */ + + +/******************************************************************************/ +/* */ +/* Function: testForLiteralCollision */ +/* */ +/* Synopsis: Test the given location for collision with the literal tables. */ +/* */ +/******************************************************************************/ +BOOL testForLiteralCollision( WORD32 loc ) +{ + WORD32 pagelc; + WORD32 pageno; + BOOL result = FALSE; + + pageno = GET_PAGE (loc); + pagelc = loc & 00177; + + if( pageno == 0 ) + { + if( pagelc >= lit_loc[pageno] && !pz.error ) + { + errorMessage( &pz_literal_overflow, -1 ); + pz.error = TRUE; + result = TRUE; + } + } + else + { + if( pagelc >= lit_loc[pageno] && !cp.error ) + { + errorMessage( &literal_overflow, -1 ); + cp.error = TRUE; + result = TRUE; + } + } + return( result ); +} /* testForLiteralCollision() */ + + +/******************************************************************************/ +/* */ +/* Function: readLine */ +/* */ +/* Synopsis: Get next line of input. Print previous line if needed. */ +/* */ +/******************************************************************************/ +void readLine() +{ + BOOL ffseen; + WORD32 ix; + WORD32 iy; + char mc; + char inpline[4*LINELEN]; + + listLine(); /* List previous line if needed. */ + indirect_generated = FALSE; /* Mark no indirect address generated. */ + error_in_line = FALSE; /* No error in line. */ + + if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ + { + mac_ptr = NULL; + for( ix = 0; ix < LINELEN; ix++ ) + { + line[ix] = mac_line[ix]; /* Restore invoking line. */ + } + cc = lexstartprev = mac_cc; /* Restore cc. */ + maxcc = strlen( line ); /* Restore maxcc. */ + listed = TRUE; /* Already listed. */ + return; + } + + cc = 0; /* Initialize column counter. */ + lexstartprev = 0; + if( mac_ptr ) /* Inside macro? */ + { + maxcc = 0; + do + { + mc = *mac_ptr++; /* Next character. */ + if( islower( mc )) /* Encoded argument number? */ + { + ix = mc - 'a'; /* Convert to index. */ + if( iy = mac_arg_pos[ix] ) + { + do /* Copy argument string. */ + { + line[maxcc++] = mac_line[iy++]; + } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && + ( !isend( mac_line[iy] ))); + } + } + else /* Ordinary character, just copy. */ + { + line[maxcc++] = mc; + } + } while( !isend( mc )); + line[maxcc] = '\0'; + listed = nomac_exp; + return; + } + + lineno++; /* Count lines read. */ + listed = FALSE; /* Mark as not listed. */ +READ_LINE: + if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) + { + filix_curr++; /* Advance to next file. */ + if( filix_curr < save_argc ) /* More files? */ + { + fclose( infile ); + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + goto READ_LINE; + } + else + { + inpline[0] = '$'; + inpline[1] = '\n'; + inpline[2] = '\0'; + } + } + + /* Remove any tabs from the input line by inserting the required number */ + /* of spaces to simulate 8 character tab stops. */ + ffseen = FALSE; + for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) + { + switch( inpline[ix] ) + { + case '\t': + do + { + line[iy] = ' '; + iy++; + } + while(( iy % 8 ) != 0 ); + break; + + case '\f': + if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); + ffseen = TRUE; + break; + + default: + line[iy] = inpline[ix]; + iy++; + break; + } + } + line[iy] = '\0'; + + /* If the line is terminated by CR-LF, remove, the CR. */ + if( line[iy - 2] == '\r' ) + { + iy--; + line[iy - 1] = line[iy - 0]; + line[iy] = '\0'; + } + maxcc = iy; /* Save the current line length. */ +} /* readLine() */ + + +/******************************************************************************/ +/* */ +/* Function: listLine */ +/* */ +/* Synopsis: Output a line to the listing file. */ +/* */ +/******************************************************************************/ +void listLine() +/* generate a line of listing if not already done! */ +{ + if( listfile != NULL && listed == FALSE ) + { + printLine( line, 0, 0, LINE ); + } +} /* listLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printPageBreak */ +/* */ +/* Synopsis: Output a Top of Form and listing header if new page necessary. */ +/* */ +/******************************************************************************/ +void printPageBreak() +{ + if( page_lineno >= LIST_LINES_PER_PAGE ) + /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ + { + if( !list_title_set ) + { + strcpy( list_title, line ); + if( list_title[strlen(list_title) - 1] == '\n' ) + { + list_title[strlen(list_title) - 1] = '\0'; + } + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + list_title_set = TRUE; + } + topOfForm( list_title, NULL ); + + } +} /* printPageBreak() */ + + +/******************************************************************************/ +/* */ +/* Function: printLine */ +/* */ +/* Synopsis: Output a line to the listing file with new page if necessary. */ +/* */ +/******************************************************************************/ +void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) +{ + if( listfile == NULL ) + { + save_error_count = 0; + return; + } + + printPageBreak(); + + list_lineno++; + page_lineno++; + switch( linestyle ) + { + default: + case LINE: + fprintf( listfile, "%5d ", lineno ); + fputs( line, listfile ); + listed = TRUE; + break; + + case LINE_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %4.4o ", lineno, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %4.4o\n", val ); + } + break; + + case LINE_LOC_VAL: + if( !listed ) + { + if( indirect_generated && lgm_flag ) + { + fprintf( listfile, "%5d %5.5o %4.4o@ ", lineno, loc, val ); + } + else + { + fprintf( listfile, "%5d %5.5o %4.4o ", lineno, loc, val ); + } + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o %4.4o\n", loc, val ); + } + break; + + case LOC_VAL: + fprintf( listfile, " %5.5o %4.4o\n", loc, val ); + break; + } + printErrorMessages(); +} /* printLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printErrorMessages */ +/* */ +/* Synopsis: Output any error messages from the current list of errors. */ +/* */ +/******************************************************************************/ +void printErrorMessages() +{ + WORD32 ix; + WORD32 iy; + + if( listfile != NULL ) + { + /* If any errors, display them now. */ + for( iy = 0; iy < save_error_count; iy++ ) + { + printPageBreak(); + fprintf( listfile, "%-18.18s", error_list[iy].mesg ); + if( error_list[iy].col >= 0 ) + { + for( ix = 0; ix < error_list[iy].col; ix++ ) + { + if( line[ix] == '\t' ) + { + putc( '\t', listfile ); + } + else + { + putc( ' ', listfile ); + } + } + fputs( "^", listfile ); + list_lineno++; + page_lineno++; + } + fputs( "\n", listfile ); + } + } + save_error_count = 0; +} /* printErrorMessages() */ + + +/******************************************************************************/ +/* */ +/* Function: endOfBinary */ +/* */ +/* Synopsis: Outputs both literal tables at the end of a binary segment. */ +/* */ +/******************************************************************************/ +void endOfBinary() +{ + /* Points to end of page for () operands. */ + punchLiteralPool( &cp, clc - 1 ); + /* Points to end of page zero for [] operands. */ + punchLiteralPool( &pz, field ); + if( error_in_line ) + listLine(); /* List line if not done yet. */ + return; +} /* endOfBinary() */ + + +/******************************************************************************/ +/* */ +/* Function: punchChecksum */ +/* */ +/* Synopsis: Output a checksum if the current mode requires it and an */ +/* object file exists. */ +/* */ +/******************************************************************************/ +void punchChecksum() +{ + /* If the assembler has output any BIN data output the checksum. */ + if( binary_data_output && !rim_mode ) + { + punchLocObject( 0, checksum ); + } + binary_data_output = FALSE; + checksum = 0; +} /* punchChecksum() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLeader */ +/* */ +/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ +/* documentation. Paper tape has 10 punches per inch. */ +/* */ +/******************************************************************************/ +void punchLeader( WORD32 count ) +{ + WORD32 ix; + + /* If value is zero, set to the default of 2 feet of leader. */ + count = ( count == 0 ) ? 240 : count; + + if( objectfile != NULL ) + { + for( ix = 0; ix < count; ix++ ) + { + fputc( 0200, objectfile ); + } + } +} /* punchLeader() */ + + +/******************************************************************************/ +/* */ +/* Function: punchOrigin */ +/* */ +/* Synopsis: Output an origin to the object file. */ +/* */ +/******************************************************************************/ +void punchOrigin( WORD32 loc ) +{ + punchObject((( loc >> 6 ) & 0077 ) | 0100 ); + punchObject( loc & 0077 ); +} /* punchOrigin() */ + + +/******************************************************************************/ +/* */ +/* Function: punchObject */ +/* */ +/* Synopsis: Put one character to object file and include it in checksum. */ +/* */ +/******************************************************************************/ +void punchObject( WORD32 val ) +{ + val &= 0377; + if( objectfile != NULL ) + { + fputc( val, objectfile ); + checksum += val; + } + binary_data_output = TRUE; +} /* punchObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchOutObject */ +/* */ +/* Synopsis: Output the current line and then then punch value to the */ +/* object file. */ +/* */ +/******************************************************************************/ +void punchOutObject( WORD32 loc, WORD32 val ) +{ + printLine( line, ( field | loc ), val, LINE_LOC_VAL ); + punchLocObject( loc, val ); +} /* punchOutObject() */ + +/******************************************************************************/ +/* */ +/* Function: punchLocObject */ +/* */ +/* Synopsis: Output the word (with origin if rim format) to the object file.*/ +/* */ +/******************************************************************************/ +void punchLocObject( WORD32 loc, WORD32 val ) +{ + if( rim_mode ) + { + punchOrigin( loc ); + } + punchObject(( val >> 6 ) & 0077 ); + punchObject( val & 0077 ); +} /* punchLocObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLiteralPool */ +/* */ +/* Synopsis: Output the current page data. */ +/* */ +/******************************************************************************/ +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) +{ + WORD32 loc; + WORD32 pageno; + WORD32 tmplc; + + pageno = GET_PAGE (lpool_page); + lpool_page &= 07600; + + if(( lpool_page == 0 ) && ( p != &pz )) + { + return; /* Don't punch page 0 pool if not asked. */ + } + + if( lit_loc[pageno] < lit_base[pageno] ) + { + if( !rim_mode ) + { + /* Put out origin if not in rim mode. */ + punchOrigin( lit_loc[pageno] | lpool_page ); + } + /* Put the literals in the object file. */ + for( loc = lit_loc[pageno]; loc < lit_base[pageno]; loc++ ) + { + tmplc = loc + lpool_page; + printLine( line, (field | tmplc), p->pool[loc], LOC_VAL ); + punchLocObject( tmplc, p->pool[loc] ); + } + p->error = FALSE; + lit_base[pageno] = lit_loc[pageno]; + } +} /* punchLiteralPool() */ + + +/******************************************************************************/ +/* */ +/* Function: insertLiteral */ +/* */ +/* Synopsis: Add a value to the given literal pool if not already in pool. */ +/* Return the location of the value in the pool. */ +/* */ +/******************************************************************************/ +WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ) +{ + WORD32 ix; + WORD32 pageno; + LPOOL_T *p; + + p = pool; + pageno = GET_PAGE (pool_page); + + /* If page zero is the current page, make sure that literals are inserted */ + /* in the page zero literal table. */ + if(( pool_page & 07600 ) == 0 ) + { + p = &pz; + } + + /* Search the literal pool for any occurence of the needed value. */ + ix = lit_base[pageno] - 1; + while( ix >= lit_loc[pageno] && p->pool[ix] != value ) + { + ix--; + } + + /* Check if value found in literal pool. If not, then insert value. */ + if( ix < lit_loc[pageno] ) + { + lit_loc[pageno]--; + p->pool[lit_loc[pageno]] = value; + ix = lit_loc[pageno]; + } + return( ix ); +} /* insertLiteral() */ + +/******************************************************************************/ +/* */ +/* Function: testZeroPool */ +/* */ +/* Synopsis: Test for literal in page zero pool. */ +/* */ +/******************************************************************************/ +BOOL testZeroPool( WORD32 value ) +{ + WORD32 ix; + WORD32 pageno; + + pageno = GET_PAGE( field ); + for ( ix = lit_loc[pageno]; ix < lit_base[pageno]; ix++ ) + { + if( pz.pool[ix] == value ) return TRUE; + } + return FALSE; +} + + +/******************************************************************************/ +/* */ +/* Function: printSymbolTable */ +/* */ +/* Synopsis: Output the symbol table. */ +/* */ +/******************************************************************************/ +void printSymbolTable() +{ + int col; + int cx; + char *fmt; + int ix; + char mark; + int page; + int row; + int symbol_base; + int symbol_lines; + + symbol_base = number_of_fixed_symbols; + + for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) + { + topOfForm( list_title, s_symtable ); + symbol_lines = LIST_LINES_PER_PAGE - page_lineno; + + for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) + { + list_lineno++; + page_lineno++; + fprintf( listfile, "%5d", list_lineno ); + + for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) + { + /* Get index of symbol for the current line and column */ + cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; + cx += symbol_base; + + /* Make sure that there is a symbol to be printed. */ + if( number_of_fixed_symbols <= cx && cx < symbol_top ) + { + switch( symtab[cx].type & LABEL ) + { + case LABEL: + fmt = " %c%-6.6s %5.5o "; + break; + + default: + fmt = " %c%-6.6s %4.4o "; + break; + } + + switch( symtab[cx].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + mark = '?'; + break; + + case REDEFINED: + mark = '#'; + break; + + default: + mark = ' '; + break; + } + fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); + ix++; + } + } + fprintf( listfile, "\n" ); + } + } +} /* printSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printPermanentSymbolTable */ +/* */ +/* Synopsis: Output the permanent symbol table to a file suitable for */ +/* being input after the EXPUNGE pseudo-op. */ +/* */ +/******************************************************************************/ +void printPermanentSymbolTable() +{ + int ix; + FILE *permfile; + char *s_type; + + if(( permfile = fopen( permpathname, "w" )) == NULL ) + { + exit( 2 ); + } + + fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); + fprintf( permfile, " EXPUNGE\n/\n" ); + /* Print the memory reference instructions first. */ + s_type = " "; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_MRI( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + + s_type = " "; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_FIXED( symtab[ix].type )) + { + if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + } + fprintf( permfile, "/\n FIXTAB\n" ); + fclose( permfile ); +} /* printPermanentSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printCrossReference */ +/* */ +/* Synopsis: Output a cross reference (concordance) for the file being */ +/* assembled. */ +/* */ +/******************************************************************************/ +void printCrossReference() +{ + int ix; + int symbol_base; + int xc; + int xc_index; + int xc_refcount; + int xc_cols; + + /* Force top of form for first page. */ + page_lineno = LIST_LINES_PER_PAGE; + + list_lineno = 0; + symbol_base = number_of_fixed_symbols; + + for( ix = symbol_base; ix < symbol_top; ix++ ) + { + list_lineno++; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref ); + } + + fprintf( listfile, "%5d", list_lineno ); + + /* Get reference count & index into concordance table for this symbol. */ + xc_refcount = symtab[ix].xref_count; + xc_index = symtab[ix].xref_index; + /* Determine how to label symbol on concordance. */ + switch( symtab[ix].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + fprintf( listfile, " U "); + break; + + case REDEFINED: + fprintf( listfile, " M %5d ", xreftab[xc_index] ); + break; + + default: + fprintf( listfile, " A %5d ", xreftab[xc_index] ); + break; + } + fprintf( listfile, "%-6.6s ", symtab[ix].name ); + + /* Output the references, 8 numbers per line after symbol name. */ + for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) + { + if( xc_cols >= XREF_COLUMNS ) + { + xc_cols = 0; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref); + } + list_lineno++; + fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); + } + fprintf( listfile, " %5d", xreftab[xc_index + xc] ); + } + fprintf( listfile, "\n" ); + } +} /* printCrossReference() */ + + +/******************************************************************************/ +/* */ +/* Function: topOfForm */ +/* */ +/* Synopsis: Prints title and sub-title on top of next page of listing. */ +/* */ +/******************************************************************************/ +void topOfForm( char *title, char *sub_title ) +{ + char temp[10]; + + if (!listfile) return; + list_pageno++; + strcpy( temp, s_page ); + sprintf( temp, "%s %d", s_page, list_pageno ); + + /* Output a top of form if not the first page of the listing. */ + if( list_pageno > 1 ) + { + fprintf( listfile, "\f" ); + } + fprintf( listfile, "\n %-63s %10s\n", title, temp ); + + /* Reset the current page line counter. */ + page_lineno = 1; + if( sub_title != NULL ) + { + fprintf( listfile, "%80s\n", sub_title ); + page_lineno++; + } + else + { + fprintf( listfile, "\n" ); + page_lineno++; + } + fprintf( listfile, "\n" ); + page_lineno++; +} /* topOfForm() */ + + +/******************************************************************************/ +/* */ +/* Function: lexemeToName */ +/* */ +/* Synopsis: Convert the current lexeme into a string. */ +/* */ +/******************************************************************************/ +char *lexemeToName( char *name, WORD32 from, WORD32 term ) +{ + WORD32 to; + + to = 0; + + while( from < term && to < ( SYMLEN - 1 )) + { + name[to++] = toupper( line[from++] ); + } + + while( to < SYMLEN ) + { + name[to++] = '\0'; + } + return( name ); +} /* lexemeToName() */ + +/******************************************************************************/ +/* */ +/* Function: defineLexeme */ +/* */ +/* Synopsis: Put lexeme into symbol table with a value. */ +/* */ +/******************************************************************************/ +SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ + WORD32 term, /* end+1 of lexeme being defined. */ + WORD32 val, /* value of lexeme being defined. */ + SYMTYP type ) /* how symbol is being defined. */ +{ + char name[SYMLEN]; + + lexemeToName( name, start, term); + return( defineSymbol( name, val, type, start )); +} /* defineLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: defineSymbol */ +/* */ +/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ +/* not already in table. */ +/* */ +/******************************************************************************/ +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) +{ + SYM_T *sym; + WORD32 xref_count; + + val = val & 07777; + if( strlen( name ) < 1 ) + { + return( &sym_undefined ); /* Protect against non-existent names. */ + } + sym = lookup( name ); + xref_count = 0; /* Set concordance for normal defintion. */ + + if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) + { + if( pass == 2 ) + { + errorSymbol( &redefined_symbol, sym->name, start ); + type = type | REDEFINED; + sym->xref_count++; /* Referenced symbol, count it. */ + xref_count = sym->xref_count; + } + return ( sym ); + } + if( M_FIXED( sym->type )) + { + return ( sym ); + } + + if( pass == 2 && xref ) + { + /* Put the definition line number in the concordance table. */ + /* Defined symbols are not counted as references. */ + xreftab[sym->xref_index] = lineno; + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + xref_count] = lineno; + } + + /* Now set the value and the type. */ + sym->val = val; + sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; + return( sym ); +} /* defineSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: lookup */ +/* */ +/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ +/* table as undefined. Return address of symbol in table. */ +/* */ +/******************************************************************************/ +SYM_T *lookup( char *name ) +{ + int ix; /* Insertion index */ + int lx; /* Left index */ + int rx; /* Right index */ + + /* First search the permanent symbols. */ + lx = 0; + ix = binarySearch( name, lx, number_of_fixed_symbols ); + + /* If symbol not in permanent symbol table. */ + if( ix < 0 ) + { + /* Now try the user symbol table. */ + ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); + + /* If symbol not in user symbol table. */ + if( ix < 0 ) + { + /* Must put symbol in table if index is negative. */ + ix = ~ix; + if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) + { + errorSymbol( &symbol_table_full, name, lexstart ); + exit( 1 ); + } + + for( rx = symbol_top; rx >= ix; rx-- ) + { + symtab[rx + 1] = symtab[rx]; + } + symbol_top++; + + /* Enter the symbol as UNDEFINED with a value of zero. */ + strcpy( symtab[ix].name, name ); + symtab[ix].type = UNDEFINED; + symtab[ix].val = 0; + symtab[ix].xref_count = 0; + if( xref && pass == 2 ) + { + xreftab[symtab[ix].xref_index] = 0; + } + } + } + + return( &symtab[ix] ); /* Return the location of the symbol. */ +} /* lookup() */ + + +/******************************************************************************/ +/* */ +/* Function: binarySearch */ +/* */ +/* Synopsis: Searches the symbol table within the limits given. If the */ +/* symbol is not in the table, it returns the insertion point. */ +/* */ +/******************************************************************************/ +int binarySearch( char *name, int start, int symbol_count ) +{ + int lx; /* Left index */ + int mx; /* Middle index */ + int rx; /* Right index */ + int compare; /* Results of comparison */ + + lx = start; + rx = symbol_count - 1; + while( lx <= rx ) + { + mx = ( lx + rx ) / 2; /* Find center of search area. */ + + compare = strcmp( name, symtab[mx].name ); + + if( compare < 0 ) + { + rx = mx - 1; + } + else if( compare > 0 ) + { + lx = mx + 1; + } + else + { + return( mx ); /* Found a match in symbol table. */ + } + } /* end while */ + return( ~lx ); /* Return insertion point. */ +} /* binarySearch() */ + + +/******************************************************************************/ +/* */ +/* Function: compareSymbols */ +/* */ +/* Synopsis: Used to presort the symbol table when starting assembler. */ +/* */ +/******************************************************************************/ +int compareSymbols( const void *a, const void *b ) +{ + return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); +} /* compareSymbols() */ + +/******************************************************************************/ +/* */ +/* Function: copyMacLine */ +/* */ +/* Synopsis: Used to copy a macro line to the macro buffer. */ +/* */ +/******************************************************************************/ +int copyMacLine( int length, int from, int term, int nargs ) +{ + char name[SYMLEN]; + int ix; + int jx; + int kx; + BOOL bl; + + bl = TRUE; + for( ix = from; ix < term; ix++ ) + { + if( !is_blank( line[ix] )) bl = FALSE; + } + if( bl || ( length < 0 )) return length; + if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; + for( ix = from; ix < term; ) + { + if( nargs && isalpha( line[ix] )) /* Start of symbol? */ + { + for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ + { + if( !isalnum( line[jx] )) break; + } + lexemeToName( name, ix, jx ); /* Make into name. */ + for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ + { + if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) + { + mac_buffer[length++] = 'a' + (char) kx; + for( ix++; ix < jx; ix++ ) + { + mac_buffer[length++] = 'z'; + } + break; + } /* end if strncmp */ + } /* end for kx */ + if( kx >= nargs ) + { + for ( ; ix < jx; ) + { + mac_buffer[length++] = toupper( line[ix++] ); + } + } + } /*end if nargs */ + else + { + mac_buffer[length++] = toupper( line[ix++] ); + } /* end else */ + } /* end for ix */ + mac_buffer[length++] = '\n'; + mac_buffer[length] = 0; + return length; +} + +/******************************************************************************/ +/* */ +/* Function: evalSymbol */ +/* */ +/* Synopsis: Get the pointer for the symbol table entry if exists. */ +/* If symbol doesn't exist, return a pointer to the undefined sym */ +/* */ +/******************************************************************************/ +SYM_T *evalSymbol() +{ + char name[SYMLEN]; + SYM_T *sym; + + sym = lookup( lexemeToName( name, lexstart, lexterm )); + + sym->xref_count++; /* Count the number of references to symbol. */ + + if( xref && pass == 2 ) + { + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + sym->xref_count] = lineno; + } + + return( sym ); +} /* evalSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: moveToEndOfLine */ +/* */ +/* Synopsis: Move the parser input to the end of the current input line. */ +/* */ +/******************************************************************************/ +void moveToEndOfLine() +{ + while( !isend( line[cc] )) cc++; + lexstart = cc; + lexterm = cc; + lexstartprev = lexstart; +} /* moveToEndOfLine() */ + +/******************************************************************************/ +/* */ +/* Function: nextLexeme */ +/* */ +/* Synopsis: Get the next lexical element from input line. */ +/* */ +/******************************************************************************/ +void nextLexeme() +{ + /* Save start column of previous lexeme for diagnostic messages. */ + lexstartprev = lexstart; + lextermprev = lexterm; + + while( is_blank( line[cc] )) { cc++; } + lexstart = cc; + + if( isalnum( line[cc] )) + { + while( isalnum( line[cc] )) { cc++; } + } + else if( isend( line[cc] )) + { + /* End-of-Line, don't advance cc! */ + } + else + { + switch( line[cc] ) + { + case '"': /* Quoted letter */ + if( cc + 2 < maxcc ) + { + cc++; + cc++; + } + else + { + errorMessage( &no_literal_value, lexstart ); + cc++; + } + break; + + case '/': /* Comment, don't advance cc! */ + break; + + default: /* All other punctuation. */ + cc++; + break; + } + } + lexterm = cc; +} /* nextLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: nextLexBlank */ +/* */ +/* Synopsis: Used to prevent illegal blanks in expressions. */ +/* */ +/******************************************************************************/ +void nextLexBlank() +{ + nextLexeme(); + if( is_blank( delimiter )) + { + errorMessage( &illegal_blank, lexstart - 1 ); + } + delimiter = line[lexterm]; +} /* nextLexBlank() */ + + + +/******************************************************************************/ +/* */ +/* Function: pseudoOperators */ +/* */ +/* Synopsis: Process pseudo-ops (directives). */ +/* */ +/******************************************************************************/ +BOOL pseudoOperators( PSEUDO_T val ) +{ + int count; + int delim; + int index; + int ix; + WORD32 length; + int level; + int lexstartsave; + WORD32 newfield; + WORD32 oldclc; + int pack; + int pageno; + int pos; + int radixprev; + BOOL status; + SYM_T *sym; + WORD32 value; + WORD32 word; + int width; + static int mask_tab[13] = { 00000, + 00001, 00003, 00007, 00017, 00037, 00077, + 00177, 00377, 00777, 01777, 03777, 07777 }; + + status = TRUE; + switch( (PSEUDO_T) val ) + { + case BINPUNCH: + /* If there has been data output and this is a mode switch, set up to */ + /* output data in BIN mode. */ + if( binary_data_output && rim_mode ) + { + for( ix = 0; ix < TOTAL_PAGES; ix++) + { + lit_loc[ix] = lit_base[ix] = 00200; + } + cp.error = FALSE; + pz.error = FALSE; + punchLeader( 8 ); /* Generate a short leader/trailer. */ + checksum = 0; + binary_data_output = FALSE; + } + rim_mode = FALSE; + break; + + case DECIMAL: + radix = 10; + break; + + case DEFINE: + count = 0; + index = 0; + lexstartsave = lexstart; + while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && + ( count < MAC_MAX_ARGS )) + { + if ( !isalpha( line[lexstart] ) && ( index == 0 )) + { + index = lexstart; + } + lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); + nextLexeme(); + } + if( count == 0 ) /* No macro name. */ + { + errorMessage( &no_macro_name, lexstartsave ); + index = 1; + } + else if( index ) /* Bad argument name. */ + { + errorMessage( &bad_dummy_arg, index ); + } + else if( mac_count >= MAC_TABLE_LENGTH ) + { + errorMessage( ¯o_table_full, lexstartsave ); + index = 1; + } + else + { + value = mac_count; + mac_count++; /* Value is entry in mac_bodies. */ + defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); + } + if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) + { + readLine(); + nextLexeme(); + } + if( index ) + { + conditionFalse(); /* On error skip macro body. */ + } + else if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + index = lexstart + 1; + length = 0; + level = 1; + while( level > 0 ) + { + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + length = copyMacLine( length, index, cc, count - 1 ); + readLine(); + index = 0; + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + case '$': + level = 0; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + length = copyMacLine( length, index, cc - 1, count - 1 ); + if( length < 0 ) + { + errorMessage (¯o_too_long, lexstart ); + } + else if( length == 0 ) + { + mac_bodies[value] = NULL; + } + else + { + mac_bodies[value] = (char *) malloc( length + 1 ); + if( mac_bodies[value] ) + { + strncpy( mac_bodies[value], mac_buffer, length ); + *( mac_bodies[value] + length ) = 0; + } + else + { + errorMessage( &no_virtual_memory, lexstart ); + } + } + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } /* end if */ + break; + + case DUBL: + inputDubl(); + break; + + case EJECT: + page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ + status = FALSE; /* This will force reading of next line */ + break; + + case ENPUNCH: + if( pass == 2 ) + { + objectfile = objectsave; + } + break; + + case EXPUNGE: /* Erase symbol table */ + if( pass == 1 ) + { + symtab[0] = sym_undefined; + symbol_top = 0; + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Enter the pseudo-ops into the symbol table. */ + for( ix = 0; ix < DIM( pseudo ); ix++ ) + { + defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); + } + /* Enter I and Z into the symbol table. */ + for( ix = 0; ix < 2; ix++ ) + { + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type | DEFFIX , 0 ); + } + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + } + break; + + case BANK: + case FIELD: + punchLiteralPool( &cp, clc - 1 ); + punchLiteralPool( &pz, field ); + newfield = field >> 12; + lexstartsave = lexstartprev; + if( isdone( line[lexstart] )) + { + newfield += 1; /* Blank FIELD directive. */ + } + else + { + newfield = (getExpr())->val; /* FIELD with argument. */ + } + + if( rim_mode ) + { + errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields. */ + } + else if( newfield > 7 || newfield < 0 ) + { + errorMessage( &illegal_field_value, lexstartprev ); + } + else + { + value = (( newfield & 0007 ) << 3 ) | 00300; + punchObject( value ); + checksum -= value; /* Field punches are not added to checksum. */ + field = newfield << 12; + } + + clc = 0200 | field; + fieldlc = clc & 07777; + + if( !rim_mode ) + { + punchOrigin( clc ); + } + break; + + case FIXTAB: + /* Mark all current symbols as permanent symbols. */ + if( pass == 1) + { + for( ix = 0; ix < symbol_top; ix++ ) + { + symtab[ix].type = ( symtab[ix].type | FIXED ) & ~CONDITION; + if((( symtab[ix].val & 00777 ) == 0 ) && + ( symtab[ix].val <= 05000 ) && + M_DEFINED( symtab[ix].type ) && + !M_PSEUDO( symtab[ix].type ) && + !M_LABEL( symtab[ix].type ) && + !M_MACRO( symtab[ix].type )) + { + symtab[ix].type = symtab[ix].type | MRI; + } + } + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Re-sort the symbol table */ + qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols ); + } + break; + + case FLTG: + inputFltg(); + /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */ + break; + + case IFDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNZERO: + if( (getExpr())->val == 0 ) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + break; + + case IFZERO: + if( (getExpr())->val == 0 ) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + break; + + case LGM: + lgm_flag = TRUE; + break; + + case LIST: + listfile = listsave; + break; + + case LIT: + if( clc & 07600 ) + { + punchLiteralPool( &cp, clc ); + } + else + { + punchLiteralPool( &pz, field ); + } + if( !rim_mode ) + { + punchOrigin( clc ); + } + break; + + case LITBAS: + if( clc & 07600 ) + { + punchLiteralPool( &cp, clc ); + } + else + { + punchLiteralPool( &pz, field ); + } + if( !rim_mode ) + { + punchOrigin( clc ); + } + pageno = GET_PAGE (clc); + if( isdone( line[lexstart] )) + { + lit_loc[pageno] = lit_base[pageno] = 0200; + } + else + { + lit_loc[pageno] = lit_base[pageno] = (((getExpr())->val) & 0177) + 1; + } + break; + + case NOLGM: + lgm_flag = FALSE; + break; + + case NOPUNCH: + if( pass == 2 ) + { + objectfile = NULL; + } + break; + + case OCTAL: + radix = 8; + break; + + case PAGE: + punchLiteralPool( &cp, clc - 1 ); + oldclc = clc; + if( isdone( line[lexstart] )) + { + clc = ( clc + 0177 ) & 077600; /* No argument. */ + fieldlc = clc & 07777; + } + else + { + value = (getExpr())->val; + clc = field + (( value & 037 ) << 7 ); + fieldlc = clc & 07777; + } + testForLiteralCollision( clc ); + + if( !rim_mode && clc != oldclc ) + { + punchOrigin( clc ); + } + break; + + case PAUSE: + break; + + case RELOC: + if( isdone( line[lexstart] )) + { + reloc = 0; /* Blank RELOC directive. */ + } + else + { + value = (getExpr())->val; /* RELOC with argument. */ + reloc = value - ( clc + reloc ); + } + break; + + case RIMPUNCH: + /* If the assembler has output any BIN data, output the literal tables */ + /* and the checksum for what has been assembled and setup for RIM mode. */ + if( binary_data_output && !rim_mode ) + { + endOfBinary(); + punchChecksum(); + punchLeader( 8 ); /* Generate a short leader/trailer. */ + } + rim_mode = TRUE; + break; + + case TEXT: + delim = line[lexstart]; + pack = 0; + count = 0; + index = lexstart + 1; + while( line[index] != delim && !isend( line[index] )) + { + pack = ( pack << 6 ) | ( line[index] & 077 ); + count++; + if( count > 1 ) + { + punchOutObject( clc, pack ); + incrementClc(); + count = 0; + pack = 0; + } + index++; + } + + if( count != 0 ) + { + punchOutObject( clc, pack << 6 ); + incrementClc(); + } + else + { + punchOutObject( clc, 0 ); + incrementClc(); + } + + if( isend( line[index] )) + { + cc = index; + lexterm = cc; + errorMessage( &text_string, cc ); + } + else + { + cc = index + 1; + lexterm = cc; + } + nextLexeme(); + break; + + case TITLE: + delim = line[lexstart]; + ix = lexstart + 1; + /* Find string delimiter. */ + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( !isend( line[ix] ) ) + { + count = 0; + ix = lexstart + 1; + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + list_title[count] = line[ix]; + count++; + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + + cc = ix + 1; + lexterm = cc; + page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ + list_title_set = TRUE; + } + else + { + cc = ix; + lexterm = cc; + errorMessage( &text_string, cc ); + } + + nextLexeme(); + break; + + case UNLIST: + listfile = NULL; + break; + + case VFD: + pos = 0; + word = 0; + radixprev = radix; + while( !isdone (line[lexstart] )) + { + lexstartsave = lexstart; + radix = 10; + width = (getExpr())->val; /* Get field width. */ + radix = radixprev; + if( (width <= 0) || ((width + pos) > 12) || (line[lexstart] != ':') ) + { + errorMessage( &illegal_vfd_value, lexstartsave ); + } + nextLexBlank(); /* Skip colon. */ + value = (getExpr())->val; /* Get field value. */ + if( line[lexterm] == ',' ) cc++; + nextLexeme(); /* Advance to next field. */ + pos = pos + width; + if( pos <= 12 ) + { + word = word | ((value & mask_tab[width]) << (12 - pos)); + } + } + punchOutObject( clc, word ); + incrementClc(); + break; + + case ZBLOCK: + value = (getExpr())->val; + if( value < 0 ) + { + errorMessage( &zblock_too_small, lexstartprev ); + } + else if( value + ( clc & 07777 ) - 1 > 07777 ) + { + errorMessage( &zblock_too_large, lexstartprev ); + } + else + { + for( ; value > 0; value-- ) + { + punchOutObject( clc, 0 ); + incrementClc(); + } + } + + break; + + default: + break; + } /* end switch for pseudo-ops */ + return( status ); +} /* pseudoOperators() */ + + +/******************************************************************************/ +/* */ +/* Function: conditionFalse */ +/* */ +/* Synopsis: Called when a false conditional has been evaluated. */ +/* Lex should be the opening <; ignore all text until */ +/* the closing >. */ +/* */ +/******************************************************************************/ +void conditionFalse() +{ + int level; + + if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + level = 1; + while( level > 0 ) + { + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + readLine(); + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + case '$': + level = 0; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionFalse() */ + +/******************************************************************************/ +/* */ +/* Function: conditionTrue */ +/* */ +/* Synopsis: Called when a true conditional has been evaluated. */ +/* Lex should be the opening <; skip it and setup for */ +/* normal assembly. */ +/* */ +/******************************************************************************/ +void conditionTrue() +{ + if( line[lexstart] == '<' ) + { + nextLexeme(); /* Skip the opening '<' */ + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionTrue() */ + + +/******************************************************************************/ +/* */ +/* Function: errorLexeme */ +/* */ +/* Synopsis: Display an error message using the current lexical element. */ +/* */ +/******************************************************************************/ +void errorLexeme( EMSG_T *mesg, WORD32 col ) +{ + char name[SYMLEN]; + + errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); +} /* errorLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: errorSymbol */ +/* */ +/* Synopsis: Display an error message with a given string. */ +/* */ +/******************************************************************************/ +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) +{ + char linecol[12]; + char *s; + + if( pass == 2 ) + { + s = ( name == NULL ) ? "" : name ; + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", + filename, linecol, mesg->file, s, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: errorMessage */ +/* */ +/* Synopsis: Display an error message without a name argument. */ +/* */ +/******************************************************************************/ +void errorMessage( EMSG_T *mesg, WORD32 col ) +{ + char linecol[12]; + + if( pass == 2 ) + { + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", + filename, linecol, mesg->file, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorMessage() */ + +/******************************************************************************/ +/* */ +/* Function: saveError */ +/* */ +/* Synopsis: Save the current error in a list so it may displayed after the */ +/* the current line is printed. */ +/* */ +/******************************************************************************/ +void saveError( char *mesg, WORD32 col ) +{ + if( save_error_count < DIM( error_list )) + { + error_list[save_error_count].mesg = mesg; + error_list[save_error_count].col = col; + save_error_count++; + } + error_in_line = TRUE; + + if( listed ) + { + printErrorMessages(); + } +} /* saveError() */ +/* End-of-File */ diff --git a/extracters/backup/backup.c b/extracters/backup/backup.c new file mode 100644 index 0000000..8bc6649 --- /dev/null +++ b/extracters/backup/backup.c @@ -0,0 +1,636 @@ + +/* Dump contents of Tops-10 BACKUP tapes, which have been read + into a disk file. The "known good" way to do this is to use the + unix utility "dd", and a command line something like this: + + dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block + + the key thing is that this program expects a fixed block size of + 2720 bytes. If the tape actually has some other format, this + program probably won't succeed. You can use the unix utility "tcopy" + to inspect the contents of the tape. + + Here's the tcopy output from a good tape: + + tcopy /dev/rmt0 + file 0: block size 2720: 9917 records + file 0: eof after 9917 records: 26974240 bytes + eot + total length: 26974240 bytes + +*/ + +#include +#include +#include +#include +#include +#include "backup.h" + +#define bool long +#define false 0 +#define true 1 + +#define RAWSIZE (5*(32+512)) + +#define endof(s) (strchr(s, (char) 0)) + +FILE* source; /* Source "tape". */ + +bool eightbit = false; /* Status of -8 (eight-bit) flag. */ +bool copytape = false; /* Status of -c (copytape fmt) flag. */ +bool buildtree = false; /* Status of -d (build trees) flag. */ +bool interchange = false; /* Status of -i (interchange) flag. */ +bool binary = false; /* Status of -b (binary) flag. */ +bool timfmt = false; /* Status of -m (mts format) flag. */ +long verbose = 0; /* Status of -v (verbose) flag. */ + +char** argfiles; /* File spec's to extract. */ +long argcount; /* Number of them. */ + +unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */ + +long headlh[32], headrh[32]; /* Header block from tape. */ +long datalh[512], datarh[512]; /* Data block from tape. */ + +long prevSEQ; /* SEQ number of previous block. */ +long currentfilenumber; + +char deferbyte; /* Defered byte for output. */ +long defercount; /* Count of defered output bytes. */ + +bool extracting; +FILE* destination; + +/* Tape information: */ + +char systemname[100]; +char savesetname[100]; + +/* File information: */ + +long a_bsiz; /* For now. */ +long a_alls; +long a_mode; +long a_leng; + +char filedev[100]; /* Device: */ +char filedir[100]; /* [ufd] */ +char filename[100]; /* file name. */ +char fileext[100]; /* extension. */ + +char filespec[7][100]; /* [0]: device:ufd. */ + /* [1-5]: sfd's, stored directly here. */ + /* [6]: file.ext */ + +char cname[100]; /* Canonical name. */ + +/* unpackheader unpacks the header block from the raw stream. */ + +void unpackheader() { + unsigned char* rawptr; + long i, left, right; + unsigned char c; + + rawptr = &rawdata[0]; + + for (i = 0; i < 32; i++) { + left = *(rawptr++) << 10; + left |= *(rawptr++) << 2; + left |= (c = *(rawptr++)) >> 6; + right = (c & 077) << 12; + right |= *(rawptr++) << 4; + right |= *(rawptr++) & 017; + headlh[i] = left; + headrh[i] = right; + if(verbose>1) {printf("\n%i l=%d, r=%d",i,left,right);} + } +} + +/* unpackdata unpacks the data block from the raw stream. */ + +void unpackdata() { + unsigned char* rawptr; + long i, left, right; + unsigned char c; + + rawptr = &rawdata[32*5]; + + for (i = 0; i < 512; i++) { + left = *(rawptr++) << 10; + left |= *(rawptr++) << 2; + left |= (c = *(rawptr++)) >> 6; + right = (c & 077) << 12; + right |= *(rawptr++) << 4; + right |= *(rawptr++) & 017; + datalh[i] = left; + datarh[i] = right; + } +} + +/* pars_36bits reads 36 bits from a machine word. */ + +void pars_36bits(index, store) +long index; +char *store; +{ + long l, r; + + l = datalh[index]; + r = datarh[index]; + + store[0] = r & 0377; + store[1] = (r >> 8) & 0377; + store[2] = ((r >> 16) & 03) | ((l << 2) & 0374); + store[3] = (l >> 6) & 0377; + store[4] = (l >> 14) & 017; + store[5] = store[6] = store[7] = 0; +} + +/* pars_5chars reads five ASCII chars from a machine word. */ + +void pars_5chars(index, store) +long index; +char* store; +{ + long l, r; + + l = datalh[index]; + r = datarh[index]; + + store[0] = (0177 & (l >> 11)); + store[1] = (0177 & (l >> 4)); + store[2] = (0177 & ((l << 3) | ((r >> 15) & 017))); + store[3] = (0177 & (r >> 8)); + store[4] = (0177 & (r >> 1)); +} + +/* pars_asciz stores asciz text from data */ + +void pars_asciz(index, store) +long index; +char* store; +{ + long words; + + words = datarh[index++]; + while ((words--) > 0) { + pars_5chars(index++, store); + store += 5; + } + *store = (char) 0; +} + +/* pars_o_name parses an o$name block from data. */ + +void pars_o_name(index) +long index; +{ + long lastw; + + lastw = index + datarh[index]; + ++index; + while (index < lastw) { + switch (datalh[index]) { + case 0: index = lastw; break; + case 1: pars_asciz(index, filedev); break; + case 2: pars_asciz(index, filename); break; + case 3: pars_asciz(index, fileext); break; + case 32: pars_asciz(index, filedir); break; + case 33: pars_asciz(index, filespec[1]); break; + case 34: pars_asciz(index, filespec[2]); break; + case 35: pars_asciz(index, filespec[3]); break; + case 36: pars_asciz(index, filespec[4]); break; + case 37: pars_asciz(index, filespec[5]); break; + } + index += datarh[index]; + } +} + +void pars_o_attr(index) +long index; +{ + /* parse off file attribute block */ + ++index; + a_bsiz = datarh[index + A_BSIZ]; /* for now... */ + a_alls = datarh[index + A_ALLS]; /* for now... */ + a_mode = datarh[index + A_MODE]; /* for now... */ + a_leng = datarh[index + A_LENG]; /* for now... */ +} + +void pars_o_dirt(index) +long index; +{ + /* parse off directory attribute block */ +} + +void pars_o_sysn(index) +long index; +{ + pars_asciz(index, systemname); +} + +void pars_o_ssnm(index) +long index; +{ + pars_asciz(index, savesetname); +} + +void zerotapeinfo() { + systemname[0] = (char) 0; + savesetname[0] = (char) 0; +} + +void zerofileinfo() { + + filedev[0] = (char) 0; + filedir[0] = (char) 0; + filename[0] = (char) 0; + fileext[0] = (char) 0; + + filespec[0][0] = (char) 0; + filespec[1][0] = (char) 0; + filespec[2][0] = (char) 0; + filespec[3][0] = (char) 0; + filespec[4][0] = (char) 0; + filespec[5][0] = (char) 0; + filespec[6][0] = (char) 0; + + cname[0] = (char) 0; +} + +/* unpackinfo picks non-data information from data block. */ + +void unpackinfo() { + long index; + + unpackdata(); + + index = 0; + while (index < headrh[G_LND]) { + switch (datalh[index]) { + case 1: pars_o_name(index); break; + case 2: pars_o_attr(index); break; + case 3: pars_o_dirt(index); break; + case 4: pars_o_sysn(index); break; + case 5: pars_o_ssnm(index); break; + } + index += datarh[index]; + } +} + +void printtapeinfo() { + if (verbose) { + if (*savesetname != (char) 0) printf("Saveset name: %s\n", savesetname); + if (*systemname != (char) 0) printf("Written on: %s\n", systemname); + } +} + +void downcase(s) +char* s; +{ + while (*s != (char) 0) { + if (isupper(*s)) *s = tolower(*s); + s++; + } +} + +void buildfilenames() { + long i; + + if (*filedev != (char) 0) + sprintf(filespec[0], "%s:%s", filedev, filedir); + else + sprintf(filespec[0], "%s", filedir); + + sprintf(filespec[6], "%s.%s", filename, fileext); + + for(i = 0; i < 7; i++) + downcase(filespec[i]); + + sprintf(cname, "%s", filespec[0]); + for(i = 1; i < 6; i++) { + if (*filespec[i] != (char) 0) sprintf(endof(cname), ".%s", filespec[i]); + } + if (*cname != (char) 0) + sprintf(endof(cname), "..%s", filespec[6]); + else + sprintf(cname, "%s", filespec[6]); + +} + +void printfileinfo() { + + buildfilenames(); + printf("%3d %s", currentfilenumber, cname); + if (verbose) { + printf(" (%d) alloc:%d, mode:%o, len:%d", a_bsiz, a_alls, a_mode, a_leng); + } + printf("\n"); +} + +/* readblock reads one logical block from the input stream. */ +/* The header is unpacked into head{l,r}; the data is not. */ + +long blockn=0; + +void readblock() { + long i, bytes; + unsigned char bc[4]; + + i = fread(bc, sizeof(char), 4, source); + if (i == 0) return; + bytes = ((long) bc[1] << 8) | (bc[0]); + if (bytes == 0) return; + if (bytes != RAWSIZE) + fprintf(stderr, "backup: incorrect block size = %d\n", bytes); + i = fread(rawdata, sizeof(char), RAWSIZE, source); + blockn++; + while (i++ < RAWSIZE) rawdata[i] = (char) 0; + fread(bc, sizeof(char), 4, source); + unpackheader(); +} + +/* Disk file output routines: */ + +void WriteBlock() { + char buffer[5*512]; + char binbuf[8*512]; + long bufpos, index; + + for (index = 0; index < 5*512; index++) buffer[index] = 0; + for (index = 0; index < 8*512; index++) binbuf[index] = 0; + + unpackdata(); + if (binary) { + for (index = headrh[G_LND], bufpos = 0; + index < (headrh[G_LND] + headrh[G_SIZE]); index++) { + pars_36bits(index, &binbuf[bufpos]); + bufpos += 8; + } + (void) fwrite(binbuf, sizeof(char), bufpos, destination); + } + else { + for (index = headrh[G_LND], bufpos = 0; + index < (headrh[G_LND] + headrh[G_SIZE]); index++) { + pars_5chars(index, &buffer[bufpos]); + bufpos += 5; + } + + if (headlh[G_FLAGS] & GF_EOF) { + for (index = 1; (index < (eightbit ? 4 : 5)) && (bufpos > 0); index++) { + if (buffer[bufpos - 1] == (char) 0) bufpos--; + } + } + (void) fwrite(buffer, sizeof(char), bufpos, destination); + } +} + +/* OpenOutput opens the output file, according to -d and -i flags. */ + +bool OpenOutput() { + + struct stat statbuf; + char oname[100]; + long i; + + defercount = 0; + + if (interchange) { + destination = fopen(filespec[6], (binary? "wb": "w")); + } else if (!buildtree) { + for (i = 0; (i < sizeof (cname)) && cname[i]; i++) + if (cname[i] == ':') cname[i] = '.'; + destination = fopen(cname, (binary? "wb": "w")); + } else { +/* for(i = 0, oname[0] = (char) 0; i < 6; i++) { + if (*filespec[i] == (char) 0) break; + sprintf(endof(oname), "%s", filespec[i]); + if (stat(oname, &statbuf) != 0) { + if (mkdir(oname, 0777) != 0) { + fprintf(stderr, "backup: cannot create %s/\n", oname); + return(false); + } + } + sprintf(endof(oname), "/"); + } + sprintf(endof(oname), "%s", filespec[6]); + destination = fopen(oname, (binary? "wb": "w")); */ + fprintf(stderr, "backup: tree mode not supported\n"); + return(false); + } + + return(destination != NULL); +} + +void CloseOutput() { + /* Close output file after us. */ +} + +/* Argmatch checks if the current file matches the given argument: */ + +bool argmatch(arg) +char* arg; +{ + long target; + char* f; + char* p; + char* s; + + if (*arg == '#') { + (void) sscanf(arg, "#%d", &target); + return(target == currentfilenumber); + } + + if (*arg == '*') return(1); + + for (f = cname; *f != (char) 0; f++) { + for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++); + if (*s == (char) 0) return (true); + } + return (false); +} + +/* doextract performs the job of "backup -x ..." */ + +void doextract() { + long i; + + currentfilenumber = 0; + extracting = false; + while (!feof(source)) { + readblock(); + if (headrh[G_SEQ] == prevSEQ) continue; + + if (headrh[G_TYPE] == T_FILE) { + if (headlh[G_FLAGS] & GF_SOF) { + currentfilenumber++; + zerofileinfo(); + unpackinfo(); + buildfilenames(); + for (i = 0; i < argcount; i++) { + if (argmatch(argfiles[i])) { + if (*argfiles[i] == '#') { + /* Maybe do a pure shift here? */ + argfiles[i] = argfiles[--argcount]; + } + extracting = true; + break; + } + } + if (extracting) { + if (OpenOutput()) { + if (verbose) { + printf("Extracting %s", cname); + fflush(stdout); + } + } else { + fprintf(stderr, "backup: can't open %s for output\n", cname); + extracting = false; + } + } + } + if (extracting) { + WriteBlock(); + if (headlh[G_FLAGS] & GF_EOF) { + (void) fclose(destination); + extracting = false; + if (verbose) printf("\n"); + if (argcount == 0) + break; + } + } + } + prevSEQ = headrh[G_SEQ]; + } +} + +/* dodirectory performs the job of "backup -t ..." */ + +void dodirectory() { + + currentfilenumber = 0; + + while (!feof(source)) { + readblock(); + if (headrh[G_SEQ] == prevSEQ) continue; + + if (headrh[G_TYPE] == T_BEGIN) { + zerotapeinfo(); + unpackinfo(); + printtapeinfo(); + } + if (headrh[G_TYPE] == T_FILE) { + if (headlh[G_FLAGS] & GF_SOF) { + ++currentfilenumber; + zerofileinfo(); + unpackinfo(); + printfileinfo(); + } + } + prevSEQ = headrh[G_SEQ]; + } +} + +/* command decoder and dispatcher */ + +bool checkarg(arg) +char* arg; +{ + long i; + char c; + + if (*arg == '#') { + if (sscanf(arg, "#%d%c", &i, &c) != 1) { + fprintf(stderr, "backup: bad argument: %s\n", arg); + return(true); + } + } + return(false); +} + + +int main(argc, argv) +long argc; +char* argv[]; +{ + long i; + char* s, tapetype[4]; + bool namenext = false; + bool actgiven = false; + char action; + char* inputname = NULL; + + if (--argc > 0) { + for (s = *(++argv); *s != (char) 0; s++) + switch(*s) { + case '-': + break; + case '8': + eightbit = true; break; + case 'b': + binary = true; break; + case 'c': + copytape = true; break; + case 'd': + buildtree = true; break; + case 'f': + namenext = true; break; + case 'i': + interchange = true; break; + case 'm': + timfmt = true; break; + case 't': + case 'x': + action = *s; actgiven = true; break; + case 'v': + verbose++; break; + default: + fprintf(stderr, "backup: bad option %c\n", *s); + return 0; + } + } + if (namenext) { + if (--argc > 0) + inputname = *(++argv); + else { + fprintf(stderr, "backup: input file name missing\n"); + return 0; + } + } + + argfiles = ++argv; /* Keep rest of arguments. */ + argcount = --argc; /* ... and count 'em. */ + + for (i = 0; i < argcount; i++) { + if (checkarg(argfiles[i])) { + fprintf(stderr, "backup: error in argument %d = %s\n", i, argfiles[i]); + return 0; } } + + if (inputname == NULL) { + /* Use environment var. TAPE here? */ + fprintf(stderr, "backup: no input file given\n"); + return 0; + } + + if (strcmp(inputname, "-") != 0) { + if ((source = fopen(inputname, "rb")) == NULL) { + fprintf(stderr, "backup: can't open %s for input\n", inputname); + return 0; + } + fprintf (stderr, "backup: opening %s for input\n", inputname); + if (timfmt) fread (tapetype, sizeof(char), 4, source); + } else { + source = stdin; + } + + switch (action) { + case 't': dodirectory(); break; + case 'x': doextract(); break; + default: + fprintf(stderr, "backup: internal error in program\n"); + return 0; + } + +} + diff --git a/extracters/backup/backup.h b/extracters/backup/backup.h new file mode 100644 index 0000000..436782d --- /dev/null +++ b/extracters/backup/backup.h @@ -0,0 +1,80 @@ + +/* Record types: */ + +#define T_LABEL 1 /* Label. */ +#define T_BEGIN 2 /* Start of SaveSet. */ +#define T_END 3 /* End of SaveSet. */ +#define T_FILE 4 /* File data. */ +#define T_UFD 5 /* UFD data. */ +#define T_EOV 6 /* End of volume. */ +#define T_COMM 7 /* Comment. */ +#define T_CONT 8 /* Continuation. */ + +/* Offsets into header block: */ + +#define G_TYPE 0 /* Record type. */ +#define G_SEQ 1 /* Sequence #. */ +#define G_RTNM 2 /* Relative tape #. */ +#define G_FLAGS 3 /* Flags: */ +#define GF_EOF 0400000 /* End of file. */ +#define GF_RPT 0200000 /* Repeat of last record. */ +#define GF_NCH 0100000 /* Ignore checksum. */ +#define GF_SOF 0040000 /* Start of file. */ +#define G_CHECK 4 /* Checksum. */ +#define G_SIZE 5 /* Size of data in this block. */ +#define G_LND 6 /* Length of non-data block. */ + +/* Non-data block types: */ + +#define O_NAME 1 /* File name. */ +#define O_ATTR 2 /* File attributes. */ +#define O_DIRECT 3 /* Directory attributes. */ +#define O_SYSNAME 4 /* System name block. */ +#define O_SAVESET 5 /* SaveSet name block. */ + +/* Offsets in attribute block: */ + +#define A_FHLN 0 /* header length word */ +#define A_FLGS 1 /* flags */ +#define A_WRIT 2 /* creation date/time */ +#define A_ALLS 3 /* allocated size */ +#define A_MODE 4 /* mode */ +#define A_LENG 5 /* length */ +#define A_BSIZ 6 /* byte size */ +#define A_VERS 7 /* version */ +#define A_PROT 8 /* protection */ +#define A_ACCT 9 /* byte pointer account string */ +#define A_NOTE 10 /* byte pointer to anonotation string */ +#define A_CRET 11 /* creation date/time of this generation */ +#define A_REDT 12 /* last read date/time of this generation */ +#define A_MODT 13 /* monitor set last write date/time */ +#define A_ESTS 14 /* estimated size in words */ +#define A_RADR 15 /* requested disk address */ +#define A_FSIZ 16 /* maximum file size in words */ +#define A_MUSR 17 /* byte ptr to id of last modifier */ +#define A_CUSR 18 /* byte ptr to id of creator */ +#define A_BKID 19 /* byte ptr to save set of previous backup */ +#define A_BKDT 20 /* date/time of last backup */ +#define A_NGRT 21 /* number of generations to retain */ +#define A_NRDS 22 /* nbr opens for read this generation */ +#define A_NWRT 23 /* nbr opens for write this generation */ +#define A_USRW 24 /* user word */ +#define A_PCAW 25 /* privileged customer word */ +#define A_FTYP 26 /* file type and flags */ +#define A_FBSZ 27 /* byte sizes */ +#define A_FRSZ 28 /* record and block sizes */ +#define A_FFFB 29 /* application/customer word */ + +/* T_BEGIN, T_END & T_CONT header offsets: */ + +#define S_DATE 12 +#define S_FORMAT 13 +#define S_BVER 14 +#define S_MONTYP 15 +#define S_SVER 16 +#define S_APR 17 +#define S_DEVICE 18 +#define S_MTCHAR 19 +#define S_REELID 20 +#define S_LTYPE 21 + diff --git a/extracters/backup/backup.txt b/extracters/backup/backup.txt new file mode 100644 index 0000000..52c2aa8 --- /dev/null +++ b/extracters/backup/backup.txt @@ -0,0 +1,91 @@ +Note: This program has no attribution for authorship. I modified it + to deal with binary files. + + Bob Supnik + +Description: + + backup is a program that can list the contents of, and extract + files from a TOPS-10 backup tape. The command syntax is some- + what resembling that of tar. The command + + backup -t[v][f tape] + + lists the contents of 'tape'. The command + + backup -x[cdimv8][f tape] file1 file2 ... + + extracts all files that match either of the file arguments given. + + The names used for the created files will be the canonical names + from the tape, unless -d or -i are in effect. The canonical name + is a string of the format: device.p_pn.sfd1.sfd2..file.ext + +Arguments: + + 'tape' is the name of a tape drive, the name of a file or the + single character '-', meaning stdin. If omitted, the environment + variable TAPE will be consulted. + + + If the "tape" argument is actually a file, which was captured + from a tape (say, on a different machine). The "known good" way + to capture the contents of the tape is to use the unix utility + "dd", and a command line something like this: + + dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block + + the key thing is that this program expects a fixed block size of + 2720 bytes. If the tape actually has some other format, this + program probably won't succeed. You can use the unix utility "tcopy" + to inspect the contents of the tape. + + Here's the tcopy output from a good tape: + + tcopy /dev/rmt0 + file 0: block size 2720: 9917 records + file 0: eof after 9917 records: 26974240 bytes + eot + total length: 26974240 bytes + + + File arguments are either any substring of the canonical name + printed with 'backup -t ...', or a hash mark, and the number + of the file on the tape, or "*" to simply extract all the files. + +Options: + + -b Extract files in binary format. The files on tape have one + 36b word stored in five 8b frames, as specified in PDP-10 + "core dump" mode. The extracted files have one 36b word + stored, right justified, on one little-endian 64b word, as + expected by the simh simulators. + + -c The 'tape' is a disk file, in copytape format. + + -d Create a directory structure when restoring, giving files named + device:p_pn/sfd1/sfd2/file.ext, instead of the flat name space + resulting from using the canonical names. + + -f The disk image file is the next argument. + + -i Ignore device and directory info when building output file names. + + -m The disk image file has a 4 byte header specifying density, which + must be ignored. + + -v Verbose. Does the obvious. -vv does even more. + + -8 Tops-10 file was in eight-bit mode. + + +Bugs: + + We don't handle multiple tape savesets any good. + + We don't check for bad format of input. + + We don't handle checksums, or repeated blocks any good. + + + diff --git a/extracters/ckabstape.c b/extracters/ckabstape.c new file mode 100644 index 0000000..4ac8995 --- /dev/null +++ b/extracters/ckabstape.c @@ -0,0 +1,423 @@ +#include + +static unsigned char sixlut[64] = { +'@','A','B','C','D','E','F','G', +'H','I','J','K','L','M','N','O', +'P','Q','R','S','T','U','V','W', +'X','Y','Z','[','/',']','^','_', +' ','!','"','#','$','%','&','\'', +'(',')','*','+',',','-','.','/', +'0','1','2','3','4','5','6','7', +'8','9',':',';','<','=','>','?' +}; + +char sixbit(char c) +{ + return(sixlut[c & 0x3f]); +} + +void printb(unsigned char c) +{ + int i; + for(i=0; i<8; i++){ + if((c & 0x80) == 0) + printf("%c",'0'); + else + printf("%c",'1'); + c = c << 1; + } + printf("\n"); +} + +/* + * read an 18 bit paper tape frame + * + * bits 6,7 packed into high part of int so we can look at them + */ +unsigned int readframe() +{ + unsigned int i; + unsigned char c; + do{ + c = getchar(); + if(feof(stdin)) + exit(); + if((c & 0x80) == 0) printf("{nul}\n",c); + } while((c & 0x80) == 0); + + i = ((c & 0xc0)>>6)<<27; + i = i | (c & 0x3f)<<12; + do{ + c = getchar(); + if(feof(stdin)) + exit(); + if((c & 0x80) == 0) printf("{nul}\n",c); + } while((c & 0x80) == 0); + + i = i |((c & 0xc0)>>6)<<24; + i = i | (c & 0x3f)<<6; + + do{ + c = getchar(); + if(feof(stdin)) + exit(); + if((c & 0x80) == 0) printf("{nul}\n",c); + } while((c & 0x80) == 0); + + i = i |((c & 0xc0)>>6)<<21; + i = i | (c & 0x3f); + return i; +} + +disasm(unsigned int instr) +{ + char *idx; + if(instr & 0010000) idx = ",X"; else idx = " "; + + if((instr & 0700000) != 0700000) + switch(instr & 0740000){ + case(0000000): + printf("CAL%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0040000): + printf("DAC%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0100000): + printf("JMS%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0140000): + printf("DZM%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0200000): + printf("LAC%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0240000): + printf("XOR%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0300000): + printf("ADD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0340000): + printf("TAD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0400000): + printf("XCT%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0440000): + printf("ISZ%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0500000): + printf("AND%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0540000): + printf("SAD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0600000): + printf("JMP%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + default: + printf("???? "); + break; + } +/* + */ + if((instr & 0700000) == 0700000) + switch(instr){ + case(0700000): + printf("IOT "); + break; + case(0700002): + printf("IOF "); + break; + case(0700004): + printf("CLOF "); + break; + case(0700042): + printf("ION "); + break; + + case(0700101): + printf("RSF (hsrdr)"); + break; + case(0700102): + printf("RCF (hsrdr)"); + break; + case(0700104): + printf("RSA (hsrdr)"); + break; + case(0700112): + printf("RRB (hsrdr)"); + break; + case(0700144): + printf("RSB (hsrdr)"); + break; + + case(0700201): + printf("PSF (hsptp)"); + break; + case(0700202): + printf("PCF (hsptp)"); + break; + case(0700204): + printf("PSA (hsptp)"); + break; + case(0700244): + printf("PSB (hsptp)"); + break; + + case(0700301): + printf("KSF (ttykb)"); + break; + case(0700312): + printf("KRB (ttyrd)"); + break; + case(0700314): + printf("IORS "); + break; + case(0700322): + printf("KRS (ttykb)"); + break; + + case(0700401): + printf("TSF (ttyout)"); + break; + case(0700402): + printf("TCF (ttyout)"); + break; + case(0700406): + printf("TLS (ttyout)"); + break; + + + case(0703302): + printf("CAF "); + break; + + case(0707721): + printf("SBA "); + break; + case(0707722): + case(0707762): + printf("DBA "); + break; + case(0707724): + case(0707764): + printf("EBA "); + break; + +/* + */ + case(0720000): + printf("AAS "); + break; + case(0723000): + printf("AAC "); + break; + case(0725000): + printf("AXS "); + break; + case(0736000): + printf("CLLR "); + break; + case(0735000): + printf("CLX "); + break; + case(0722000): + printf("PAL "); + break; + case(0721000): + printf("PAX "); + break; + case(0730000): + printf("PLA "); + break; + case(0731000): + printf("PLX "); + break; + case(0724000): + printf("PXA "); + break; + case(0726000): + printf("PXL "); + break; + + +/* + * operate instructions + */ + case(0740000): + printf("NOP "); + break; + case(0740001): // CMA compliment AC + printf("CMA "); + break; + case(0740002): + printf("CML "); + break; + case(0740004): + printf("OAS "); + break; + case(0740010): + printf("RAL "); + break; + case(0740020): + printf("RAR "); + break; + case(0740030): + printf("IAC "); + break; + case(0740031): + printf("TCA "); + break; + case(0740040): + printf("HLT "); + break; + case(0740100): + printf("SMA "); + break; + case(0740200): + printf("SZA "); + break; + case(0740400): + printf("SNL "); + break; + case(0741000): + printf("SKP "); + break; + case(0741100): + printf("SPA "); + break; + case(0741200): + printf("SNA "); + break; + case(0741400): + printf("SZL "); + break; + case(0742010): + printf("RTL "); + break; + case(0742020): + printf("RTR "); + break; + case(0742030): + printf("SWHA "); + break; + case(0744000): + printf("CLL "); + break; + case(0744002): + printf("STL "); + break; + case(0744010): + printf("CCL "); + break; + case(0744020): + printf("RCL "); + break; + case(0750000): + printf("CLA "); + break; + case(0750001): + printf("LAS "); + break; + case(0750004): + printf("LAT "); + break; + case(0750010): + printf("GLK "); + break; + case(0760000): + printf("LAW "); + break; + default: + printf("??? "); + break; + } +} + +main() +{ + int totalblks = 0; + int badblks = 0; + unsigned int fullwd = 0; + unsigned int currentwd = 0; + int wds = 0; + int bytecnt = 0; + int col = 0; + int framecount; + unsigned int cksum; + unsigned int adr; + unsigned char c; + /* + * read 3 chars and pack them in a word + */ + do { + currentwd = readframe(); + printf("%010o ", currentwd); + printf("%c %c %c ",sixbit((currentwd & 0770000) >>12), + sixbit((currentwd & 007700) >>6), + sixbit(currentwd & 077) ); + col++; + if(col == 4){ + printf("\n"); + col = 0; + } + if(currentwd & 00010000000) { + printf("\n loader end ----\n"); + /* + * start looking for binary data frames + */ + while(1){ + /* + * frames start at the first 0x80 punched + * (check is in readframe..) + */ + + adr = readframe() & 0777777; // staring adr + cksum = adr; + printf("ADR: %010o\n", adr); + + framecount = readframe() & 0777777; // word count + cksum += framecount; + framecount = -(0xfffe0000 | framecount); // sign extend + printf("CNT: %010o (%d)\n", framecount, framecount); + + if((adr & 0700000) != 0){ + printf("FRAMECOUNT == 0 START ADR == %06o\n", adr); + printf("TOTAL BLKS %d TOTAL ERRS %d\n", totalblks, badblks); + exit(); + } + + currentwd = readframe() & 0777777; // checksum + cksum += currentwd; + printf("CKS: %06o\n", currentwd); + /* + * read all the data words + */ + while(framecount > 0){ + currentwd = readframe(); + cksum += currentwd & 0777777; + printf("%05o: %06o ",adr++, currentwd & 0777777); + disasm(currentwd & 0777777); + printf(" ; %c%c%c ",sixbit((currentwd & 0770000) >>12), + sixbit((currentwd & 007700) >>6), + sixbit(currentwd & 077) ); + printf("\n"); + framecount--; + } + + if(cksum&0777777){ + printf("****BAD CKSUM**** %06o\n",cksum&0777777); + badblks++; + } + + totalblks++; + } + } + } while(!feof(stdin)); + + if(bytecnt > 0){ + printf("%d chrs left\n", bytecnt); + } +} + diff --git a/extracters/mmdir.c b/extracters/mmdir.c new file mode 100644 index 0000000..d5744ac --- /dev/null +++ b/extracters/mmdir.c @@ -0,0 +1,105 @@ +/* This program dumps the directory of a simulated Interdata MDM tape + + Copyright (c) 1993-2002, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ +#include +#include +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ +int obj, i, k, fc, rc, tpos, sa, ea, fr, fq; +unsigned char b[53]; +unsigned char bca[4]; +unsigned int bc; +int preveof; +FILE *ifile; +#define MAXRLNT 65536 + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + return 0; } + printf ("Processing input file %s\n", argv[i]); + tpos = 0; rc = 1; fc = 0; preveof = 0; + for (;;) { + fseek (ifile, tpos, SEEK_SET); + k = fread (bca, 1, 4, ifile); + bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | + (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); + if ((k == 0) || (bc == 0xFFFFFFFF)) { + printf ("End of physical tape\n"); + break; } + if (bc & 0x80000000) { + printf ("Error marker at record %d\n", rc); + bc = bc & ~0x80000000; } + if (bc == 0) { + if (preveof) { + printf ("End of logical tape\n"); + break; } + preveof = 1; + fc++; obj++; + rc = 1; + tpos = tpos + 4; } + else if (bc > MAXRLNT) { + printf ("Invalid record length %d, terminating\n", bc); + break; } + else { + tpos = tpos + 8 + ((bc + 1) & ~1); + preveof = 0; + if (fc && (rc == 1)) { + if (bc != 52) { + printf ("Invalid record length %d, terminating\n", bc); + break; } + fread (b, 1, 52, ifile); + sa = (((unsigned int) b[18]) << 16) | + (((unsigned int) b[19]) << 8) | ((unsigned int) b[20]); + ea = (((unsigned int) b[21]) << 16) | + (((unsigned int) b[22]) << 8) | ((unsigned int) b[23]); + fr = b[27] >> 4; + fq = b[27] & 0xF; + printf ("%3d %c%c%c 06-%c%c%c", fc, + b[0], b[1], b[2], b[3], b[4], b[5]); + if (fr) printf ("F0%X", fr); + else printf (" "); + printf ("R%c%c%c%c %c%c ", + b[6], b[7], b[25], b[26], b[28], b[29]); + b[18] = b[51] = b[52] = 0; + printf ("%s%s %06X %06X %X\n", + &b[8], &b[30], sa, ea, fq); + } + rc++; } + } + fclose (ifile); + } +return 0; +} diff --git a/extracters/mtdump.c b/extracters/mtdump.c new file mode 100644 index 0000000..dcf2ad1 --- /dev/null +++ b/extracters/mtdump.c @@ -0,0 +1,130 @@ +/* This program dumps the format of a simulated magtape + + Copyright (c) 1993-2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#include +#include + +#define F_STD 0 +#define F_E11 1 +#define F_TPC 2 +#define F_TPF 3 + +int main (int argc, char *argv[]) +{ +int obj, i, k, fc, rc, tpos; +unsigned int bc, fmt, rlntsiz; +unsigned char *s, bca[4]; +int preveof; +FILE *ifile; +#define MAXRLNT 65536 + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: mtdump {-secf} file [file...]\n"); + exit (0); } + +s = argv[1]; +if ((s != NULL) && (*s++ == '-')) { + ++argv; --argc; + switch (*s) { + case 'S': case 's': + fmt = F_STD; rlntsiz = 4; break; + case 'E': case 'e': + fmt = F_E11; rlntsiz = 4; break; + case 'C': case 'c': + fmt = F_TPC; rlntsiz = 2; break; +/* case 'F': case 'f': +/* fmt = F_TPF; break; */ + default: + fprintf (stderr, "Bad option %c\n", *s); + return 0; + } + } +else { fmt = F_STD; + rlntsiz = 4; } + +for (i = 1; i < argc; i++) { + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + printf ("Processing input file %s\n", argv[i]); + tpos = 0; rc = 1; fc = 1; preveof = 0; + printf ("Processing tape file %d\n", fc); + for (obj = 1;;) { + fseek (ifile, tpos, SEEK_SET); + k = fread (bca, 1, rlntsiz, ifile); + switch (fmt) { + case F_STD: case F_E11: + bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | + (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); + break; + case F_TPC: + bc = (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); + break; + } + if ((k == 0) || (bc == 0xFFFFFFFF)) { + printf ("End of physical tape\n"); + break; } + if (bc & 0x80000000) { + printf ("Error marker at record %d\n", rc); + bc = bc & ~0x80000000; } + if (bc == 0) { + if (preveof) { + printf ("Obj %d, position %d, end of logical tape\n", obj, tpos); + break; } + preveof = 1; + printf ("Obj %d, position %d, end of tape file %d\n", obj, tpos, fc); + fc++; obj++; + rc = 1; + tpos = tpos + rlntsiz; } + else if (bc > MAXRLNT) { + printf ("Invalid record length %d, terminating dump\n", bc); + break; } + else { + if (preveof) printf ("Processing tape file %d\n", fc); + preveof = 0; + printf ("Obj %d, position %d, record %d, length = %d (0x%X)\n", + obj, tpos, rc, bc, bc); + rc++; obj++; + switch (fmt) { + case F_STD: + tpos = tpos + 8 + ((bc + 1) & ~1); break; + case F_E11: + tpos = tpos + 8 + bc; break; + case F_TPC: + tpos = tpos + 2 + ((bc + 1) & ~1); break; + } + } + } + fclose (ifile); + } + +return 0; +} diff --git a/extracters/mtdump.txt b/extracters/mtdump.txt new file mode 100644 index 0000000..0df43f0 --- /dev/null +++ b/extracters/mtdump.txt @@ -0,0 +1,38 @@ +mtdump dumps the record structure of a tape image file in SIMH, E11, or +TPC format. mtdump is invoked by + + mtdump {-sec} file1 file2 file3... + +where -s specifies SIMH format, -e E11 format, and -c TPC format. The +default is SIMH format. Output is to stdout, unless redirected. + +SIMH format is as follows: + + 4 byte record length 1 + record 1 + repeat of 4 byte record length 1 + 4 byte record length 2 + record 2 + repeat of 4 byte record length 2 + : + 4 bytes = 00000000 for end of file + +and so on. Records are padded to even byte length. + +E11 format is similar to SIMH format, but odd length records are not padded +to even length. + +TPC format is as follows: + + 2 byte record length 1 + record 1 + 2 byte record length 2 + record 2 + : + 2 bytes = 0000 for end of file + +and so on. Records are padded to even byte length. + +mmdir is a variant of mtdump that dumps the directory structure of an +Interdata MMD (multimedia diagnostic) tape. The tape must be in SMIH +format. diff --git a/extracters/rawcopy/RawCopy.c b/extracters/rawcopy/RawCopy.c new file mode 100644 index 0000000..cfaea91 --- /dev/null +++ b/extracters/rawcopy/RawCopy.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +static +char buf[1024*1024]; + +int main(int argc, char **argv) +{ +FILE *fin, *fout; +size_t bytesread, readsize = sizeof(buf), totalbytes = 0; + +if (argc < 3) + { + fprintf(stderr, "Usage: RawCopy \n"); + fprintf(stderr, "On Win32 environments, RAW devices have names for the format:\n"); + fprintf(stderr," CD Drives \\\\.\\CdRom0\n"); + fprintf(stderr," Hard Drives \\\\.\\PhysicalDrive0\n"); + exit(0); + } +if (NULL == (fin = fopen(argv[1], "rb"))) + { + fprintf(stderr, "Error Opening '%s' for input: %s\n", argv[1], strerror(errno)); + exit(errno); + } +if (NULL == (fout = fopen(argv[2], "wb"))) + { + fprintf(stderr, "Error Opening '%s' for output: %s\n", argv[2], strerror(errno)); + exit(errno); + } +fprintf(stderr, "Copying '%s' to '%s'\n", argv[1], argv[2]); +while (0 != (bytesread = fread(buf, 1, readsize, fin))) + { + if (bytesread != fwrite(buf, 1, bytesread, fout)) + { + fprintf(stderr, "Error Writing '%s': %s\n", strerror(errno)); + break; + } + else + totalbytes += bytesread; + if (0 == (totalbytes%(1024*1024))) + fprintf(stderr, "%6dMB Copied...\r", totalbytes/(1024*1024)); + } +fprintf(stderr, "\n"); +fprintf(stderr, "Total Data: %6.2f MBytes (%d bytes)\n", totalbytes/(1024.0*1024.0), totalbytes); +fclose(fin); +fclose(fout); +} diff --git a/extracters/rawcopy/RawCopy.txt b/extracters/rawcopy/RawCopy.txt new file mode 100644 index 0000000..1a2b20e --- /dev/null +++ b/extracters/rawcopy/RawCopy.txt @@ -0,0 +1,17 @@ +The attached program will make an ISO compatible CD Image on both Intel +Linux and Windows XP systems. + +It works with the VMS Hobbyist CD, and the result is bootable. It works +with all the other data CD's that I tried as well. + +The same exe works on Windows 2000, with one odd exception. It will +simply copy a CD image from any CD which has an ISO9660 file system on it, +but it fails to recognize a non-zero size for the \\.\CdRom0 device on these +systems when the Hobbist CD is installed. + +Meanwhile, this raw data copy operation is the "right" thing for this problem. +The raw copy result provides a disk image that the simulator can access. +On many platforms, the simulator could access the device directly just as +this program does. + +- Mark Pizzolato diff --git a/extracters/sdsdump.c b/extracters/sdsdump.c new file mode 100644 index 0000000..0e34a0e --- /dev/null +++ b/extracters/sdsdump.c @@ -0,0 +1,95 @@ +/* This program dumps the format of an SDS paper tape + + Copyright (c) 2002, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include + +int getfc (FILE *ifile) +{ +int k; +unsigned char by; + +k = fread (&by, sizeof (char), 1, ifile); +if (k == 0) { + printf ("End of physical tape\n"); + return -1; } +return by; +} + +int main (int argc, char *argv[]) +{ +int i, k, wc, pos, cc, inrec, wd, op, tag, addr; +FILE *ifile; +char *opstr[] = { + "HLT", "BRU", "EOM", NULL, NULL, NULL, "EOD", NULL, + "MIY", "BRI", "MIW", "POT", "ETR", NULL, "MRG", "EOR", + "NOP", NULL, "OVF", "EXU", NULL, NULL, NULL, NULL, + "YIM", NULL, "WIM", "PIN", NULL, "STA", "STB", "STX", + "SKS", "BRX", NULL, "BRM", NULL, NULL, "CPY", NULL, + "SKE", "BRR", "SKB", "SKN", "SUB", "ADD", "SUC", "ADC", + "SKR", "MIN", "XMA", "ADM", "MUL", "DIV", "RSH", "LSH", + "SKM", "LDX", "SKA", "SKG", "SKD", "LDB", "LDA", "EAX" }; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + printf ("Processing input file %s\n", argv[i]); + inrec = 0; wc = 1; + for (pos = wd = cc = 0; ;pos++) { + if ((k = getfc (ifile)) < 0) { + if (inrec) printf ("Format error\n"); + break; } + if (k == 0) { + if (inrec && cc) printf ("Incomplete word\n"); + inrec = 0; + continue; } + wd = (wd << 6) | (k & 077); + cc++; + if (cc >= 4) { + printf ("Pos = %d, cnt = %d: %08o", pos, wc, wd); + tag = (wd >> 21) & 07; + op = (wd >> 15) & 077; + addr = wd & 037777; + if (opstr[op] && ((op != 0) || (wd == 0))) { + if (wd & 040000) printf (" [%s* %o", opstr[op], addr); + else printf (" [%s %o", opstr[op], addr); + if (tag) printf (",%o", tag); + printf ("]"); } + printf ("\n"); + cc = wd = 0; + wc++; } + } + fclose (ifile); + } +return 0; +} diff --git a/ods2_readme.txt b/ods2_readme.txt new file mode 100644 index 0000000..4e72db1 --- /dev/null +++ b/ods2_readme.txt @@ -0,0 +1,175 @@ + ODS2 June '98 + + A Program to Read ODS2 Disk Volumes + + +Say, what is this? + ODS2 is a program to read VMS disk volumes written in VMS + ODS2 format. + +Why bother? + Well sometimes it is convenient to see what is on a VMS CD, + or copy files from VMS disk on another platform. Maybe in + future it could be used as a basis for a PC Bookreader + program, or possibly other software? + +What other platforms? + ODS2 is written in 'Standard C' with the intent that it can + be compiled and run on non-VMS systems. However this requires + a C compiler which has 32 bit 'int' types, 16 bit 'short' + types, and 8 bit 'char'. The current version does no special + 'endian' handling so it can only be run on systems which are + little-endian like the VAX. Fortunately that inludes Intel... + +Could it be made to run on big-endian systems? + Yes it could! This would probably best be done by defining + C macros or functions to extract words or long words from the + disk structures as appropriate. On a little-endian system these + would be simply pass-through, while on a big-endian system they + would reverse the byte order. I have not attempted this because + I don't have a suitable test system!! + +What else is needed? + Some operating system support is also required to read an + absolute disk sector from the target disk. This is NOT as + easy as it sounds. I do not currently have sufficient + documentation to set this up for as many platforms as I + would like!! However I do have modules to read disks under + VMS (easy!) and floppies and CD under OS2, Windows 95 and + Windows NT. + +How do I build it? + On a VMS system ODS2 can be compiled by executing the + procedure BUILD.COM. On other platforms you need to compile + Ods2.c, Rms.c, Direct.c, Access.c, Device.c, Cache.c, Vmstime.c, + and the appropriate Phy*.c routine for your system. On OS/2 I + compile using the gcc command:- + gcc -fdollars-in-identifiers ods2.c,rms.c,direct.c, + access.c,device.c,cache.c,phyos2.c,vmstime.c + +What can it do? + Basically ODS2 provides cut down DIRECTORY, COPY and + SEARCH commands for VMS volumes on non-VMS systems. These + can be used to find out what is on a VMS volume, and copy + files onto the local file sytem. + +What file types? + Basically ODS2 can only deal with sequential files. I do not + have information on how indexed file types are constructed, + and relative files are of limited interest. + +What about volume sets? + ODS2 does contain support for multi-volume sets. However there + is no checking that the correct volumes have been specified and + error handling is very fragile. You should ensure that you + specify volume set mount commands very carefully! + +What about ODS5? + Sorry, but I have no idea! I have not seen any ODS5 information + or specifications. Most likely this program will fall in a heap + when presented with an ODS5 disk volume. + +What about bugs? + There are plenty!! This code has been tested for several hours + by a single developer/user on one workstation (absolutely no + testing or input from any other person up to this point!). + Contrast this to the VMS filesystem which has had hundreds of + developers, millions of users, and has run on about 500,000 + systems over the last 20 years!! I would hope to fix some of + the more severe limitations and provide better error handling + fairly soon, perhaps even put some comments into the code? + Maybe the next release might have fewer bugs? + +It is free? + Yeap! It is provided 'as is' to help people in the VMS + community. However there is NO SUPPORT! I will try to fix + any reported problems, but as I really only get to work on + it while the kids are at Scouts on Monday nights, fixes + happen slowly! But if you have comments or suggestions then + please feel free to mail me! + +Can I use the code? + Yeap! You can use and modify the code provided that you + acknowledge the author in the source of any modules which use + any part of this code. I would also appreciate, where possible, + being sent any enhancements for my own use. + +Can I call the routines from my program? + You should be able to. Many of the modules follow an 'RMS' + sort of standard, and programs which only ever read RMS files + from C might be converted to read VMS volumes on another platform + without too much effort. In addition to the RMSish interface, + it would not be difficult to package up an $ASSIGN/$QIOW interface + for common file operations... + +What is the status of ODS2? + This is the first release - actually more like a prototype + than an real release! But it may generate useful feedback and + possibly be useful to others the way it is? However if you are + tempted to use this version for real file transfers, DO YOUR OWN + TESTING first! This program may not have encountered volumes and + files like yours and could easily generate garbage results!!! + +Is more work happening? + Yes! I find the program very useful moving stuff from my + VAXstation to my PC. I would like to be able to write ODS2 volumes + so that I can move files in the other direction! And yes I hope + to generally improve the code - particularly in the area of error + handling! + +Can I make a suggestion? + You sure can! If you see something which needs improvement then + let me know. It may be more interesting than whatever I am doing now! + In fact if I don't hear from anyone then I might even loose interest! + +Can I see a command sample? + Sure:- + C:> ODS2 + ODS2 v1.2 + $> mount E:,F: + %MOUNT-I-MOUNTED, Volume FOX1 mounted on E: + %MOUNT-I-MOUNTED, Volume FOX2 mounted on F: + $> direct/file E:[*...] + Directory E:[PNANKERVIS] + 3MONTH.LOWUSE;1 (51,20,2) + ACCTEST.COM;1 (137,4,1) + ACCTEST.EXE;1 (53,4,2) + ..... + $> set default E:[sys0.sysmgr] + $> dir [-.sys*...].% + ..... + $> copy *.c *.* + %COPY-S-COPIED, E:[SYS0.SYSMGR]ACCESS.C;1 copied to ACCESS.C (3 records) + .... + $> show time + 24-MAR-1998 20:15:23.5 + $> dismount E: + $> exit + +What commands are supported? + A summary is:- + mount DRIVE:[,DRIVE:...] + directory [/file|/size|/date] [FILE-SPEC] + copy FILE-SPEC OUTPUT-FILE + dismount DRIVE: + search FILE-SPEC STRING + set default DIR-SPEC + show default + show time + exit + Note - DRIVE: is normally the native system drive name, + for example D: might be a CD on a PC - xxx: might be + /dev/xxx on a Unix system. + - when a list of drives is specified on the mount command + they are assumed to contain a single valid volume set + (this is not validated!!) + - file-spec is in the usual VMS syntax and may contain + wildcards (for example A:[-.*obj%%...]*abc*.obj;-2) + +Who would write this? + Me! Maybe it will become the basis of something more? If you + have suggestions or want to know more then please mail me at + paulnank@au1.ibm.com + + Thanks for listening! + Paul Nankervis diff --git a/putr/putr.com b/putr/putr.com new file mode 100644 index 0000000..e0c09e7 Binary files /dev/null and b/putr/putr.com differ diff --git a/putr/putr.txt b/putr/putr.txt new file mode 100644 index 0000000..3f147e8 --- /dev/null +++ b/putr/putr.txt @@ -0,0 +1,930 @@ + PUTR V2.01 + FILE TRANSFER PROGRAM + Copyright (C) 1995-2001 by John Wilson + All rights reserved + Release date: 05-Sep-2001 + + PUTR is a copyrighted but freely distributable utility for + transferring files between various file systems and various + media. It is named after PUTR.SAV (Peripheral Utility Transfer + Routines), the utility which performs the same function under + the DEC TSS/8.24 operating system for the PDP-8. + + This program is a companion to the D Bit "Ersatz-11" PDP-11 + emulation package, which emulates a broad range of PDP-11 CPU + and peripheral models at greatly increased speed. A demo + version (with reduced features) is available for free download, + it may be used for 30-day commercial evaluation or unlimited + hobby use. See www.dbit.com for information. PUTR is generally + compatible with other emulators and coprocessors as well, and + can transfer files between PCs and real PDP-11s and PDP-8s using + a variety of media. Multiple foreign volumes can be mounted at + once, and files can be copied directly between them (or within a + single foreign volume) in one step without having to do separate + extract and insert operations using the PC disk as intermediate + storage. + + The file systems currently fully supported are RT-11, + RSTS/E, DOS/BATCH, XXDP+, OS/8 (and OS/78 etc.), PUTR (TSS/8 + DECtape utility), and PC-DOS. PUTR can also read Files-11 ODS-1 + file systems (i.e. disks from RSX-11M, RSX-11M-PLUS, IAS, and + P/OS) and TSS/8.24 file systems. COS-310 disks have the same + structure as OS/8 disks, however the text file format is + different and is currently not supported by PUTR. COS-310 + binary files may be transferred the same as OS/8 binary files. + + All file systems other than PC-DOS must be logically + mounted and assigned a logical device name before use. This is + done with the MOUNT command. PUTR is mainly intended to work on + logical devices contained in disk image files (normally a + block-by-block copy of the foreign disk), but it also supports + raw SCSI disks (through an ASPI driver), DEC TU58 tape drives + (plugged into a COM port), RX50 (including a double-sided + variant) and RX33 floppy disks or RX01/02/03 26-sector workalike + disks mounted on a 1.2 MB drive, and RX24, RX23, and RX26 720 + KB/1.44 MB/2.88 MB disks on an appropriate 3.5" drive (even + though none of these was supported by standard DEC controllers + on machines using these file systems). + + PUTR can format any of these types of floppy disks. Note + that most PC floppy controllers don't work correctly in single + density mode, but some chips made by SMC, WD, Goldstar, Intel + (but not the 82078), and National Semiconductor are known to + work. On a controller with a working single density mode, the + RX01 format is exactly compatible with genuine RX01 disks + (i.e. IBM 3740 format) mounted in an 8" drive connected in place + of a 1.2 MB one, using an adapter such as D Bit's "FDADAP" board + (see http://www.dbit.com/fdadap.html for information). The + "RX02" format used by PUTR is actually IBM System 34 format, + since DEC's 8" DD disks use a strange combination of SD headers + with non-standard ID marks, and DD data fields, that can't be + accessed with a standard PC FDC regardless of the software used. + The 26-sector 5.25" disk formats are compatible with those used + by Ersatz-11. + + + FILENAMES + ========= + + [ldevu:][dir][filename] + + As much as possible, the filename syntax is intended to look + similar to that of the host operating system for the volume on + which the file is located. + + The ldevu: parameter is the logical device and unit number where + the file is located. The device name is one or two letters with + an optional unit number in the range 0-255, so that DOS-style + drive names like "X:" or DEC-style drive names like "SY:" or + "DL0:" are possible. Logical devices are mounted using the + MOUNT command, or they may be PC-DOS drives (which are all + implicitly mounted under the native file system (i.e. PC-DOS)). + The device name syntax is intended to mimic that of the DEC + PDP-11 OSes for convenience only, at the moment no significance + is attached to the device name (different units of the same + device name have nothing to do with each other, and for example + DL0: need not be an RL01/02 image). This may change. If the + ldevu: parameter is omitted, the current logged-in logical + device is assumed (you log into a device by typing the device + name and a colon at the prompt, just like in PC-DOS). + + The dir parameter is the directory in which the file is located + (by default, the current working directory on that logical + device). With PC-DOS drives this is the usual sequence of + directory names separated by \ (or / if that isn't your + SWITCHAR). RT-11 drives use the same syntax as PC-DOS, to refer + to nested logical disk files (LD:); the default extension is + ".DSK". "." and ".." are fake directory names and have the same + meanings as under PC-DOS. Note that PUTR doesn't assume that it + can tell the difference between a directory and an ordinary file + (because under RT-11 there is no difference), so all pathname + elements must end in \ (except the final one in a CD command). + So if you want to list the parent directory, type "DIR ..\" + instead of "DIR ..". + + RSTS drives use directory names of the form [proj,prog], with + the project and programmer numbers in decimal. The + one-character logical names !, #, $, %, and & also work. + DOS/BATCH uses "[proj,prog]" directory names too but the numbers + are octal instead of decimal, and the directory name comes after + the filename instead of before it. + + Files-11 drives (RSX etc.) use either numeric directory names of + the form [proj,prog], with the project and programmer numbers in + octal, or named directories with names like [USER]. PUTR + supports tree-structured directories, with names like + [USER.FILES.OLD]. P/OS is currently the only DEC PDP-11 + operating system which implements tree-structured Files-11 ODS-1 + disks. + + The filename syntax depends on the file structure of the drive; + under all supported file structures it is of the form + "filename.ext", where the length of the filename and extension + have fixed limits for each OS. Note that the parsing and + wildcard matching routines make no assumptions about filename + syntax (except for adding default "*"s depending on whether they + see a "." anywhere in the filespec), so it is possible to fool + them. For example, typing "DIR ????????A.*" on a PC-DOS disk + will not give an error message, but it's impossible for anything + to match this spec. The wildcard parser is smarter than the one + in PC-DOS -- '*' and '?' really work the way they should (in + PC-DOS a '*' is equivalent to filling out the rest of the field + with '?'s instead of really matching 0 or more characters, and + in PC-DOS '?' will match a blank, which isn't true of most other + systems). + + Note that each filename on an ODS-1 disk contains a version + number, which PUTR interprets as a decimal number. Some systems + use octal but that's a function of the operating system and not + the disk structure itself, so there's no way for PUTR to + auto-detect this. PUTR's wildcard lookup code is currently + operating system independent and does not give special treatment + to version numbers, they are regarded as simply being part of + the filename string. So in order to access a file on an ODS-1 + disk, you must either use a wildcard that matches the ";n" on + the end of the filename, or explicitly specify a version number + (preceded by a semicolon). Otherwise PUTR's file search code + will not recognize it as a match against the file on the ODS-1 + disk. + + This also may cause trouble in output filenames. If X: is an + ODS-1 disk and C: is a DOS disk, the command "copy + x:[200,200]foo.txt;1 c:" will attempt to create a file named + "FOO.TXT;1" on the DOS C: drive. Most versions of DOS will + ignore everything after the first three letters of the filename + extension, however DR-DOS treats the semicolon character + specially, and considers everything after the semicolon to be a + password, which is used to lock the file. So on DR-DOS, the + above command will create a file which appears as "FOO.TXT" in a + directory listing, but you must use the name "FOO.TXT;1" to + access that file (or delete it). This can be worked around by + specifying the output filename explicitly, as in "copy + x:[200,200]foo.txt;1 c:foo.txt". + + + COMMANDS + ======== + + PUTR is an interactive program with a simple command line + interface. Many of the basic file-related commands which work + at a DOS command prompt, such as DIR or TYPE or COPY, will also + work when typed at PUTR's command prompt. PUTR extends these + commands by allowing any mixture of real DOS drive letters, and + PUTR's own logical device names, to be used in file + specifications. There are also additional commands which allow + special file operations to be performed, which have no + equivalent in the DOS command language. In many cases, the + syntax follows that of the equivalent commands in DEC operating + systems. + + A typical PUTR session starts with a MOUNT command for each + foreign volume which will be accessed, possibly preceded by + FORMAT and/or INITIALIZE commands to create these volumes if + they do not already exist. Then the user can use the familiar + CD, DIR, TYPE, COPY, COPY/B, and DELETE commands to manipulate + files. Finally, QUIT exits to DOS. + + When PUTR is run, it looks for an indirect command file named + "PUTR.INI", first in the current directory, then in the + directory where the executable is located (for PC-DOS 3.0 and + later only). If found, this file is opened as an indirect + command file before the first command prompt. This is a + convenient place to put SET commands, and MOUNT commands for the + disk image file(s) that you usually use. + + + ------------------------------------------------------------ + + ldevu: + + Simply typing a logical device name (with the colon) on a line + by itself, switches the "logged-in" drive to that device, just + as in PC-DOS or CP/M. This drive (and its current working + directory) will now appear in PUTR's prompt, and it will be used + as the default for all file specifications which do not include + an explicit drive name. + + + ------------------------------------------------------------ + + @ filename[.CMD] + + Redirects input so that commands come from the specified + indirect command file (with default extension ".CMD"). Indirect + files chain rather than nesting, so if an "@" command appears + inside an indirect file the file is closed and new input comes + from the specified file. At end of file input reverts to the + keyboard. + + + ------------------------------------------------------------ + + BOOT ldevu: + + On RT-11 volumes only, makes a volume bootable. The volume must + contain the needed .SYS files (monitor, system device handler, + TT: device handler), there are prompts that let you choose which + monitor to boot and which device handler the bootstrap should be + for. At each prompt you may enter a blank line to display a + list of all .SYS files on the volume, or a filename (default + extension ".SYS") to use. The action performed is exactly + equivalent to COPY/BOOT under RT-11. + + + ------------------------------------------------------------ + + CD ldevu:dir + + Changes the current working directory on the specified drive + (default is the current logged-in drive). If no directory is + given the current working directory is displayed. For RT-11 + drives, logical disks (*.DSK) are made to look like + subdirectories so you can CD into them too (and use CD .. to get + out). + + + ------------------------------------------------------------ + + CLS + + Clears the screen. + + + ------------------------------------------------------------ + + COPY[/BINARY][/OVERRIDE] ldevu:inspec ldevu:outspec + + Copies the specified file(s), converting between file systems if + necessary. Files are assumed to be ASCII (NULs are squeezed + out, two 12-bit words are unpacked to three 8-bit characters, + etc.) unless the /BINARY switch is given; this default may be + changed with the SET COPY command. If an output file has the + same name as an existing file in the same directory, then the + existing file is deleted, unless it is protected; the /OVERRIDE + switch overrides this protection. Not all file systems support + protection. In a binary copy between 12- and 16-bit media, the + 12-bit words are considered to be right-justified in 16-bit + words (PDP-11 byte order); the remaining four bits are ignored + on input and written as 0 on output. + + Additional COPY switches are useful when writing into a RSTS/E + file system. /CLUSTERSIZE:n writes the output file with the + specified cluster size, instead of the pack cluster size. This + value is ignored unless it is a power of two, between the pack + cluster size and 256, inclusive. Also the /CONTIGUOUS switch + writes the output file as a contiguous file. Ideally this + switch would work with DOS/BATCH volumes too, but it is not yet + implemented. + + Note that unlike the COPY commands in DOS or RT-11, this command + cannot concatenate multiple input files into one output file. + + + ------------------------------------------------------------ + + COPY/DEVICE[/FILES][/BINARY] ldevu:[inspec] ldevu:[outspec] + + Does an image copy from one device to another, or between a + device and a file. By default the copy is done in logical block + order, which is to say the 512-byte blocks are copied in order + starting at block 0. If you're copying a floppy disk (or an + image of one) then adding a /BINARY switch tells PUTR to copy in + physical sector order, starting with cylinder 0 head 0 sector 1, + and proceeding in sequential sector order, copying both surfaces + of each cylinder (if double sided) before proceeding to the next + cylinder. This copies all sectors of the disk, even those not + used by the DEC interleave schemes (on RX01, RX02, and RX03 + disks, DEC leaves out track 0), and the resulting image files + are more likely to be in the right order if they aren't really + DEC disks. This use of /BINARY is not affected by the SET COPY + command. + + + ------------------------------------------------------------ + + DELETE[/NOQUERY][/OVERRIDE] ldevu:filespec + + Deletes the specified file(s), prompting before each unless + /NOQUERY is specified. If a file is protected, it is not + deleted unless the /OVERRIDE switch is given. + + + ------------------------------------------------------------ + + DIRECTORY ldevu:wildcard [/FULL] + + Displays a directory of the indicated file(s). /FULL gives the + starting block numbers of files as well on OS/8 or RT-11 + volumes. + + + ------------------------------------------------------------ + + DISMOUNT ldevu: [/UNLOAD] + + Undoes the effect of a MOUNT command (q.v.). Closes any image + file associated with the logical device, or releases the COM + port or floppy drive or SCSI device, and deletes the logical + name definition. /UNLOAD ejects the disk on SCSI drives that + support this. + + + ------------------------------------------------------------ + + EXIT + + Returns to PC-DOS. + + + ------------------------------------------------------------ + + FORMAT d: /devtype /ostype + + Formats the disk in PC-DOS floppy drive d: to the type specified + by /devtype (/RX01, /RX02, /RX03, /RX23, /RX24, /RX26, /RX33, + /RX50, /RX52 -- default depends on drive type and is /RX50 for + 1.2 MB drives or if the drive type is unknown). The /ostype + switch is optional and tells PUTR to write a blank directory + structure for the specified OS (see the INITIALIZE command). It + may also affect the interleave and skew used for formating, if + appropriate for that OS. + + Note that for RX50 format, double density disks (e.g. PC 360 KB + disks) should be used rather than the PC high density 1.2 MB + disks. Opinion is divided as to whether the hub rings should be + peeled off before use -- as they age they may slide slightly out + of place so that the disk doesn't center properly, but then + again they're there to protect the disk and without them it's + more likely to get damaged. In any case the 1.2 MB disks don't + erase/rewrite properly in double density mode (RX50s are double + density disks), even if they appear to format OK (which might + happen if they were bulk erased first) they can't be trusted as + RX50s. + + + ------------------------------------------------------------ + + FORMAT dev:filename /devtype /ostype + + Creates an image file of the specified device type. The file is + initialized to zeros and an empty bad sector file is written if + appropriate (you will be prompted for a pack serial number for + device types that have them). As above, if /ostype is specified + then a blank directory is written. + + If no device type is given then PUTR assumes nothing and just + prompts for the file size and creates a zeroed file of that + size. The value entered for the size should be a decimal + integer, optionally followed by the units the size represents; + if no unit is specified then the number is assumed to be the + number of bytes. Examples of the possibilities are, "100" means + 100 bytes, "100 BLOCKS" means 100 blocks (512 bytes each), "100 + KB" means 100 KB, "100 MB" means 100 MB (1 MB = 2**20 bytes), + and "100 GB" would mean 100 GB (1 GB = 2**30 bytes) except that + that exceeds the 2 GB limit on the size of a single DOS file. + Note that only integers may be used, so you can't say "1.44 MB" + or "2.1 GB". Those might not mean what you intend anyway, note + that 2.1 GB is not equal to 2100 MB (unless you're talking about + "marketing" numbers where MB/GB mean 10**6/10**9 instead of the + usual 2**20/2**30), so it's probably better to spell it out in + smaller units. + + + ------------------------------------------------------------ + + INITIALIZE vol /devtype /ostype + + Writes a blank directory on the specified volume or image file + (only /OS8, /PUTR, and /RT11 are allowed as /ostype switches at + this time). Prompts for any needed information. "vol" is a + file, floppy drive, SCSI device, or COM port name the same as in + FORMAT and MOUNT. For initializing RT-11 volumes, there is a + "/SEGMENTS:n" switch which overrides the default number of + directory segments created. The default depends on size of + volume, using the same rules as with RT-11's own DUP.SAV (which + implements its INITIALIZE command). + + + ------------------------------------------------------------ + + MOUNT ldevu: image /devtype /ostype + + Mounts a disk image, and attaches it to logical device name for + later use in other PUTR commands. "ldevu:" is a logical device + name (1 or 2 letters) and optional unit number. The device name + is arbitrary, but future versions of PUTR may change so that the + device name dictates the emulation (i.e. "DL1:" must be an RL01 + or RL02) since that will make it possible to have useful + defaults for /devtype in case file size alone isn't enough to + tell. + + "image" is the location of the disk image. Possibilities are as + follows: + + [d:][path\]filename[.DSK] (name of a PC file) + + The specified file contains a raw image of the disk. + This is known as an "image file" or "container file", + and is commonly obtained by downloading a disk from a + live machine over a serial or network connection, or + taking a snapshot of a SCSI disk, although PUTR can + also create image files from scratch itself (see + FORMAT). + + d: (name of a PC floppy drive) + + The raw floppy disk is used to hold the volume. This + should not be confused with an image file on a DOS + diskette, the disk uses the foreign file system and in + most cases will interchange with the foreign machine. + In a MOUNT command, if the "ldevu:" logical device + name is omitted, the floppy name will be used. For + example you may "MOUNT B: /RT11 /RX50" and then "DIR + B:". + + SCSIhtl: (SCSI disk) + + This form allows a raw SCSI disk to be mounted using a + foreign file system. The "SCSIhtl:" device name + specifies a SCSI disk (accessed through an ASPI + manager) at host adapter "h" (A=0, B=1, etc.), target + "t", and LUN "l", where h, t, and l (as well as the + underscore character) are all optional (the defaults + are "SCSIA00:"). On most systems there will be only + one SCSI host adapter, and most disks have only one + logical unit, so in most cases the device name may be + something like "SCSI5:", which specifies SCSI target + #5. + + Note that some DOS ASPI drivers exist which make + non-SCSI devices look like SCSI drives. These should + work fine with PUTR, just pretend it's a SCSI drive. + An example is the ASPIATAP.SYS used with IDE/ATAPI + versions of the Iomega Zip drive, which makes the + drive look as if it's SCSI target 1, so + "SCSI1:" refers to the raw Zip drive in this case. + + CDROMx: (raw CD-ROM drive) + + This name can be used to open a raw CD-ROM, using the + usual MSCDEX.EXE interface (or NWCDEX.EXE under + DR-DOS). "x" is the CD-ROM drive's DOS drive letter. + Note that the MSCDEX emulation provided in Windows 95 + and Windows 98 does not allow access to the first + sixteen sectors of the disk, which will not allow + PDP-11 format disks to work, except for non-zero RT-11 + partitions. The workaround is to use plain DOS, or + else use a SCSI CD-ROM drive with the SCSI: device + type. + + COMn:bbbbb/DRIVE:u (TU58 tape drive) + + This specifies an actual DEC TU58 drive connected to + COM port "n", at baud rate "bbbbb" (default is 38400), + unit u (0 or 1, default is 0). + + The /devtype switch specifies the device type of the image; the + defaults are auto-sensed except for TU58 drives where obviously + the default is always /TU58. With image files the default + device type depends on the file size, and is the largest defined + device less than or equal to the file in length. With floppies + various reads are attempted to detect the disk type. Raw + CD-ROMs and SCSI disks always default to /MSCP since their size + probably has more to do with the physical drive than with + whatever DEC drive it's supposed to replace. + + Possible switches are as follows: + + /RX01 = RX01 disk, 5.25" RX01 workalike + /RX02 = 5.25" RX02 workalike + /RX03 = DS 5.25" RX02 workalike + + NOTE: for image files of the above devices, the file + size is used to determine which disk type it is and + whether the file is a block-by-block image (as + obtained by a "COPY/DEVICE/FILES DY0: FLOPPY.IMG" + command under RT-11, for example) which has had + interleave and skew applied to it by the PDP-11 device + driver that read it, or a sector-by-sector image (as + might be obtained by any simple program that reads the + sectors off sequentially and puts them in order into a + file). Block-by-block image files of 8" disks are + slightly smaller because the DEC interleave/skew + scheme skips track 0. + + /RX23 = 1.44 MB 3.5" disk + /RX24 = 720 KB 3.5" disk + /RX26 = 2.88 MB 3.5" disk + /RX33 = 1.2 MB 5.25" disk + /RX50 = 400 KB 5.25" disk + + NOTE: RX50 image files are the same size whether they + have interleave or not, so PUTR tries it both ways + while trying to guess the OS type. + + /RX52 = DS RX50, 800 KB 5.25" disk (PUTR's own name) + + The following switches are also supported: /MSCP, /RA60, /RA70, + /RA71, /RA72, /RA73, /RA80, /RA81, /RA82, /RA90, /RA92, /RD32, + /RD51, /RD52, /RD53, /RD54, /RK02, /RK05, /RK06, /RK07, /RL01, + /RL02, /RM02, /RM03, /RM05, /RM80, /RP02, /RP03, /RP04, /RP05, + /RP06, /RP07, /RS03, /RS04, /RS08, /TU56, /TU58. These switches + set the device type to the indicated DEC drive model. Since + there is no way to connect most of these devices directly to a + PC (unlike floppies), the effect of these switches is to specify + the exact size of the volume. Also, for the FORMAT command, + there is special handling of RL01, RL02, RK06, and RK07 drives + for building a blank bad sector track and (in the case of RT-11) + deducting the correct amount from the volume size to allow for + driver-implemented replacement blocks. The /MSCP switch means + that the image is a generic disk device whose actual size should + be used, instead of assuming the size matches some particular + model of DEC disk drive. + + The /ostype switch specifies the file structure on the device; + one of the switches below may be given. If none is specified + then PUTR performs tests to guess the disk format. If the + volume appears to have no known directory, or seems to be valid + for more than one of the supported file structures, then a + message is displayed and the volume is not mounted. Mounting a + disk with /FOREIGN would seem pointless (since no directory + operations are allowed), except that it makes it usable with the + COPY/DEVICE command so it can be used to transfer an image of + the disk to or from a file (or another identical disk). + + File structure switches: + + /FILES11 = Files-11 ODS-1, used by RSX-11M, RSX-11M-PLUS, + IAS, and P/OS. Support is currently read-only. + + /RSTSE = RSTS/E disk structure (RDS 0.0, 1.1, or 1.2). + + /RT11 = RT-11 disk structure (/PARTITION:n switch selects + MSCP partition number, default is 0). + + /OS8 = OS/8, OS/78, OS/278, COS-310 disk structure + (/PARTITION:n switch selects RK05 partition number, + default is 0). + + /PUTR = TSS/8 PUTR.SAV DECtape format, similar to OS/8 + format but with software 11:1 block interleave, + encoded 3-letter filename extensions, and a different + way of packing 7-bit ASCII characters into 12-bit + words. + + Note: even though the regular OS/8 format uses 128 + words of each 129-word DECtape block, the PUTR.SAV + format needs all 129 words (so a full 1474-block + DECtape image is 190,146 words or 380,292 bytes) + because some of the blocks are recorded in reverse, so + that the last 128 words of those blocks are used + rather than the first. + + /TSS8 = TSS/8.24 system disk format (DS32 or RS08). + Currently read-only. Works only with disks built with + 256-word segments, which is the most common size. + Probably not compatible with disks written by older + versions of the TSS/8 monitor, but nothing else was + available for testing. Filename extensions (chosen + from a fixed list of eleven) were a late addition, and + the base year for file dates was changed from 1964 to + 1974 somewhere along the way (the new format ran out + in 1984). + + The location of the MFD depends on the value of the + JOBMAX build parameter. This value may be specified + using the "/PARTITION:n" switch (where n is between 1 + and 25 decimal). If this switch is omitted, PUTR will + search every 4 K words until it finds what looks like + a valid MFD. + + /DOSBATCH = DOS/BATCH file structure. + + /XXDP = XXDP+ file structure, a variant of DOS/BATCH format + used in late versions of the XXDP+ diagnostic package + (earlier versions of XXDP used exactly the DOS/BATCH + format, so they should be mounted with /DOSBATCH). + + /FOREIGN = some other file structure, device is accessed in + 16-bit mode and the only possible operations are COPY + /DEV and COPY /FILE /DEV. + + There are a few miscellaneous MOUNT switches. /INTERLEAVE and + /NOINTERLEAVE tell whether an image file is interleaved (i.e. a + block-by-block copy of a floppy disk so the data are in block + order, not sector order) or non-interleaved (i.e. a + sector-by-sector copy so the data are in sector order). The + default is to guess based on the image file size (or prompt in + the case of FORMAT, which takes the same switches). + /PARTITION:n specifies the partition number for RT-11 MSCP disks + or OS/8 RK05 disks (default is 0). /RONLY and /RW tell whether + the device should be mounted for read-only or read/write access. + The default is read/write. + + All of the above switches may be given in any order. + + + ------------------------------------------------------------ + + QUIT + + Returns to PC-DOS. + + + ------------------------------------------------------------ + + SET x: type + + Sets the drive type for one of the four possible PC floppy + drives A:-D: (note that actual PCs rarely have more than one or + two floppy drives). The type must be RX01, RX02, RX03, RX50, + RX33, RX24, RX23, or RX26. The default value for each drive is + whatever was stored in CMOS memory by the ROM BIOS setup + utility. + + This command may be useful when the drive types stored in CMOS + RAM are incorrect for some reason. It's also helpful when an 8" + drive, or a real DEC RX50 drive, has been attached to the PC + using a D Bit "FDADAP" adapter, or something equivalent. There + is no standard for representing these drive types in CMOS RAM. + Using real RX50 drives (or other 300 RPM quad-density drives + such as the Tandon TM100-3 and TM100-4) is different from RX33s + (which is what PUTR calls regular PC 1.2 MB drives) because the + motor speed is slower, so the FDC chip must be programmed for a + lower data rate to match. + + + ------------------------------------------------------------ + + SET COPY {ASCII | BINARY} + + Sets the default transfer mode for the COPY command (initially + ASCII). Intended mainly for use in the PUTR.INI command file. + + + ------------------------------------------------------------ + + SET DISMOUNT {NOUNLOAD | UNLOAD} + + Sets the default unload action of DISMOUNTs, both explicit, and + implied by QUITting, or reMOUNTing the pseudo-device as + something else. Default is NOUNLOAD. Intended for use in + PUTR.INI with drive models where the disk shouldn't be left in + the drive when it's powered off. + + + ------------------------------------------------------------ + + SET FDC {COMPATICARD | GENERIC} + + Tells PUTR whether your floppy disk controller is a Micro + Solutions CompatiCard IV, or just a generic PC floppy + controller. If it is a CompatiCard, PUTR maintains the TG43 + signal (for 8" drives) by writing to port 3F0h; normal FDCs + have no writable port at that address and don't do anything when + it gets written so the default is COMPATICARD to cover + everything, but if your FDC does strange things when that port + is written you should SET FDC GENERIC to disable the writes. + + This setting doesn't matter if you're using the D Bit "FDADAP" + floppy disk adapter board (see http://www.dbit.com/fdadap.html) + to connect to an 8" drive. The board has an on-board + microcontroller which transparently monitors the drive bus and + drives the TG43 signal correctly for each drive without any + software help. + + + ------------------------------------------------------------ + + SET MORE {OFF | ON} + + Turns **MORE** processing on or off; the default is on, so + output pauses at the end of every screen. As above, intended + mainly for use in PUTR.INI. + + + ------------------------------------------------------------ + + SHOW ldevu: + + Shows the medium and file system types for the specified logical + device. + + + ------------------------------------------------------------ + + TYPE ldevu:wildcard + + Types the specified file(s) on the terminal, printing the + filename(s) first. + + + ------------------------------------------------------------ + + WIPEOUT ldevu:dir + + Writes zeros over all "< UNUSED >" areas so that disk image + files will compress smaller with GZIP (etc.) and/or transfer + faster with Kermit (etc.). Supported only for OS/8, PUTR, and + RT-11 volumes. + Example Operations + ======= ========== + + + This section gives sample commands to accomplish common + operations. In these examples the PUTR prompt is shown as + "(C:\)>", whereas on your system it will show the actual + logged-in drive and current working directory. + + + ------------------------------------------------------------ + + Formating RX50 disks (assuming B: is a 1.2 MB drive): + + (C:\)>format b: /rx50 + Press ENTER when ready... + (status is displayed during the format operation) + Format more disks (Y/N)? N + (or type Y to format more RX50s) + (C:\)>quit + + + ------------------------------------------------------------ + + Copying PDP-11 image files to RX50s (B: is the 1.2 MB drive): + + (C:\)>mount b: /rx50 /foreign + (C:\)>copy/file/dev disk1.dsk b: + (if you have more than one disk to copy, you may swap disks + and issue more COPY commands without having to repeat the + MOUNT command) + (C:\)>quit + + + ------------------------------------------------------------ + + Creating blank container files: + + Example 1 -- MSCP disk (user-selected size): + + (C:\)>format du0.dsk /mscp + Are you sure (Y/N)? Y + File size (bytes): 10000 blocks + (C:\)>quit + + + Example 2 -- RL02 (size known by PUTR, has pack serial number): + + (C:\)>format dl0.dsk /rl02 + Are you sure (Y/N)? Y + Serial # [1234512345]: 666 + (C:\)>quit + + + ------------------------------------------------------------ + + Create a bootable RT-11 disk image from its component files: + + All of the RT-11 files have been downloaded in binary mode, from + the PDP-11 into a PC directory named "C:\RT11". + + (C:\)>format du0.dsk /mscp /rt11 + Are you sure (Y/N)? Y + File size (bytes): 32 mb + Volume ID : + Owner name: + (C:\)>mount x: du0 /mscp + (C:\)>set more off + (avoids having to press space bar during the copy) + (C:\)>copy/b c:\rt11\*.* x: + (... lists all files being copied ...) + (C:\)>boot x: + Writing bootstrap on X:\ + + Monitor file [.SYS]: rt11xm + Device handler file [.SYS]: dlx + (substitute actual monitor and device handler filenames, + being careful to append "X" to the handler name when using + an XM monitor) + (C:\)>quit + File Formats + ==== ======= + + + Generally, the disk image files used by PUTR (typically with a + ".DSK" extension) simply contain a byte-by-byte snapshot of the + entire disk, in physical sector order. For 12-bit images + (PDP-8, DECmate series), each 12-bit word is stored + right-justified in a 16-bit word in PDP-11 byte order. + + There are several exceptions: + + RX01, RX02, "RX03": + + These may be accessed either as "block" images or as + "sector" images. Block images contain a snapshot of + the disk as seen through a PDP-8/PDP-11 device driver, + so there's no interleave, and track 0 does not appear + in the image file. Block images generally work the + same as images of any other type of drive, since DEC + used most styles of disks directly, without any kind + of soft interleave. Sector images are a snapshot of + the disk in raw sector order, including track 0, and + the data are in physical order, so soft interleave is + required to sort out the "cooked" blocks seen through + the host operating system. + + Note that these disks have the same low-level format, + and thus the same sector image file format, in both + PDP-8 and PDP-11 systems. PDP-11s simply treat the + bytes as bytes, while PDP-8s perform packing and + unpacking of the 12-bit words so that (typically) 64 + or 128 words are packed into the first 3/4 of the + sector, with the remainder ignored. + + RX50, "RX52": + + These disks have similar issues to the 8" disks, but + the details are different. The interleave schemes + wrap around the end of the disk to track 0, so track 0 + is used in both the "block" and "sector" image file + formats, and the image file size is the same either + way. On 12-bit systems, the words are not packed + together in the first 3/4 of the sector, they're + individually right-justified in a PDP-11 word, so four + bits of each word are wasted but the data go all the + way to the end of the sector. + + RK05 disk packs: + + RK05 drives were used on both 12- and 16-bit systems. + The same drives work with both formats, but the packs + are different. 12-bit packs have 16 sectors per track + (1.6 MW total), and 16-bit packs have 12 sectors per + track (1.2 MW total). In both cases the sectors are + 256 words each. Because of the 4 bits of padding for + every word in a 12-bit image, PUTR uses 3,276,800 + bytes to store a 12-bit RK05 image, vs. only 2,457,600 + bytes for a 16-bit RK05 image. + + 555, TU55, TU56 DECtape: + + DECtapes were used on a wide variety of machines with + different word sizes. PUTR stores DECtape image files + in three different formats. + + PDP-11s use DECtapes formatted in 18-bit mode (the + high 2 bits are ignored), giving 578 blocks of 256 + words each. PUTR stores those as byte-for-byte images + with an image file size of 295,936 bytes. + + PDP-8 DECtapes have 1474 blocks of 129 words each. + Most file systems actually use only 128 words per + block, but the DECtape hardware doesn't allow that + block size because it uses 6-bit mark codes which are + recorded in a single track alongside three data + tracks, so a block's data field must be a multiple of + 18 bits. OS/8 always uses only the first 128 words of + each sector, so the 129th word can be safely omitted + from an image file. With PUTR's usual packing (each + 12-bit word is right-justified in a 16-bit PDP-11 + word), this gives an image file size of 377,344 bytes. + + However, you may wish to include the 129th word just + to be safe. This is definitely required for TSS/8 + PUTR.SAV format tapes. Even though they use only 128 + words per block, they are recorded using an interleave + scheme in which 45% of the blocks are recorded + backwards, so the first 128 words of the block become + the last 128 words of the block when read forwards. + Therefore all 129 words must be preserved, because the + image file is considered to be accessed in the forward + direction (i.e. that's what you should do when taking + a snapshot of a tape). This gives a file size of + 380,292 bytes. + + PUTR tests the file size when opening an existing + image, and chooses the most appropriate size when + creating a new image file with the FORMAT command, so + generally it will do the right thing with all three + file sizes. + + + + Note: + + "D Bit", "Ersatz", and "E11" are trademarks of Digby's Bitpile, + Inc. All other trademarks are used for identification purposes + only and are the property of their respective owners. + + + + John Wilson + Digby's Bitpile, Inc. DBA D Bit + 11 Bank Street + Troy, NY 12180 + USA + +1 (518) 271-6824 + +1 (518) 272-3853 fax (call above number first) + + www.dbit.com diff --git a/putr/readme.txt b/putr/readme.txt new file mode 100644 index 0000000..e6ff429 --- /dev/null +++ b/putr/readme.txt @@ -0,0 +1,40 @@ +This directory contains PUTR.COM V2.01, a program for accessing DEC file +systems from DOS PCs. + +Supported file systems: +DOS/BATCH (both disks and DECtapes) +Files-11 (read only) +OS/8 +RSTS/E +RT-11 +TSS/8.24 DECtape (PUTR.SAV format) +TSS/8.24 system disk (read only) +XXDP+ + +Supported media: +disk image files (can apply/remove interleave for floppy image files) +RX33, RX50 floppies in 1.2MB drive +RX01/02/03 workalike floppies in 1.2MB drive +RX23, RX24, RX26 disks in 3.5" 720KB/1.44MB/2.88MB drive +RX01 real floppies in 8" drive with appropriate controller +TU58 drives plugged into PC COM port +SCSI disks through ASPI driver +CD-ROM +(Catweasel/ISA driver is unfinished and therefore undocumented) + +The program accepts a subset of the usual DOS commands, but allows you to +MOUNT non-DOS disks (with a fake drive letter or "ddu:" style name). Once +you've mounted a foreign disk, you can "log in" to it just like a DOS disk, +and TYPE, COPY (including COPY /B), DIR etc. will work (no RENAME yet, sorry). + +The program's basic point of pride is the fact that you can mount more than +one foreign disk at once, and they may be from different operating systems, +so you can copy file between foreign file systems w/o the intermediate step +of copying them into a DOS directory and back out again (especially handy if +space is tight and/or your files have dates before 01/01/1980). + +By the way, whatever your stinking web browser may tell you, "putr.doc" is +a plain ASCII file! This shouldn't be a problem when accessing this server +via HTTP, because the server's MIME types file has this set up properly. + +John Wilson wilson@dbit.com 05-Sep-2001