From 53b84e435f80e9cb3c9cea1862a4374cd9dd155e Mon Sep 17 00:00:00 2001 From: shattered Date: Wed, 13 Feb 2013 13:45:24 -0800 Subject: [PATCH 01/94] Initial commit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..59c4c7d --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +macro11 +======= + +A MACRO-11 assembler for the PDP-11 in portable C source code. Copyright (c) 2001, Richard Krehbiel \ No newline at end of file From 3b8e1419173cc1dd8d3ec180a8b16b9eed3a105d Mon Sep 17 00:00:00 2001 From: Sergey Svishchev Date: Fri, 15 Feb 2013 23:44:59 +0400 Subject: [PATCH 02/94] Initial import of 2001 code from http://www.dbit.com/pub/linux/macro11/ --- CHANGES | 12 + LICENSE | 30 + README | 128 ++ TODO | 57 + depends | 0 dumpobj.c | 603 +++++ dumpobj.dsp | 114 + macro11.c | 6198 +++++++++++++++++++++++++++++++++++++++++++++++++++ macro11.dsp | 180 ++ macro11.dsw | 53 + macro11.h | 39 + makefile | 38 + mlb.c | 319 +++ mlb.h | 62 + object.c | 861 +++++++ object.h | 210 ++ rad50.c | 117 + rad50.h | 43 + stream2.c | 380 ++++ stream2.h | 110 + util.c | 172 ++ util.h | 56 + 22 files changed, 9782 insertions(+) create mode 100644 CHANGES create mode 100644 LICENSE create mode 100644 README create mode 100644 TODO create mode 100644 depends create mode 100644 dumpobj.c create mode 100644 dumpobj.dsp create mode 100644 macro11.c create mode 100644 macro11.dsp create mode 100644 macro11.dsw create mode 100644 macro11.h create mode 100644 makefile create mode 100644 mlb.c create mode 100644 mlb.h create mode 100644 object.c create mode 100644 object.h create mode 100644 rad50.c create mode 100644 rad50.h create mode 100644 stream2.c create mode 100644 stream2.h create mode 100644 util.c create mode 100644 util.h diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..9fa1e30 --- /dev/null +++ b/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/LICENSE b/LICENSE new file mode 100644 index 0000000..b5b4413 --- /dev/null +++ b/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/README b/README new file mode 100644 index 0000000..d29502e --- /dev/null +++ b/README @@ -0,0 +1,128 @@ +A MACRO-11 assembler for the PDP-11 in portable C source code. + +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. + + +Files: + macro11.c This is the vast bulk of the code + object.c Functions for writing RT-11 compatible .OBJ files + mlb.c Classes (!) for reading RT-11 macro libraries + stream2.c Functions for managing input streams and buffers + rad50.c Functions for converting text to and from RAD50 + util.c A few general utility fuctions + + macro11.h, object.h, mlb.h, stream2.h, rad50.h, util.h + types and symbols exported from the associated sources. + + dumpobj.c A program I wrote to examine the output from + RT-11's MACRO.SAV program and compare it with my + own output. + + makefile, depends + A simple makfile for Linux; simple enough, it + should be convertible to any Unix. + + macro11.dsp, dumpobj.dsp, macro11.dsw + Visual Studio 6 projects + + README This file + + LICENSE The copyright notice and license + + CHANGES A list of changes from previous versions + + TODO A list of things that may need fixing + + +Notes: + +Sorry, I am a believer in 4 column hardware tabs, for a number of +reasons, mostly regarding editing convenience. (I did untabify this +README file though.) + +The bulk of my development was done in Microsoft Visual Studio 6, but +I also have a Linux partition and the Cygnus Windows toolkit, so you +can send me "patches" and I will be able to apply them with the +"patch" utility. + +The macro11 command line: + +macro11 [options...] files... + +Options: + -v Prints program version. + + -e opt .ENABL option. Implemented options are AMA, GBL, + and also .LIST options ME, BEX, and + MD, though the status of listing control is + presently very poor. + + -d opt .DSABL option; same options as -e. + + -m macname Gives a macro library name. + Up to 32 macro libraries may be specified, one per + -m option. + Note: unlike MACRO.SAV, SYSMAC.SML is not + automatically included; you must name it. + + -p macpath For any .MCALL directive, macro11 will + first search -m macro libraries, then it will + search the MCALL path for a file named .MAC + to locate the body of the macro. The MCALL path + is an environment variable containing directory + names separated by delimiters (":" for Unix-style + targets; ";" for Windows). The -p command line + options appends a directory name to the MCALL + path. + + -o objname Gives the name of the object file. No extension + is assumed; if you want .OBJ you have to say it. + With no -o option, no object file is generated. + + -l lstname Gives the name of a listing file. The name "-" + may be given to write the listing to stdout. No + extension is assumed; if you want .LST you have to + say it. With no -l option, no listing file is + written. + + -x Tells macro11 not to assemble anything, but rather + to simply extract all the macros in all the -m + macro libraries into individual .MAC files in the + current directory. This should be the last option + given, as none following will be processed. + + files... Any number of input files. They will be assembled + as if they were concatenated together. + + +You may define the MCALL environment variable prior to invoking +macro11, as a path to directories containing macros. diff --git a/TODO b/TODO new file mode 100644 index 0000000..d922637 --- /dev/null +++ b/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/depends b/depends new file mode 100644 index 0000000..e69de29 diff --git a/dumpobj.c b/dumpobj.c new file mode 100644 index 0000000..680810c --- /dev/null +++ b/dumpobj.c @@ -0,0 +1,603 @@ +/* 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]; + +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 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); + 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); +} + +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; + break; + case 04: + rad50name(cp+i+2, name); + printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); + i += 6; + 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; + 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; + 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; + break; + case 014: + rad50name(cp+i+2, name); + + printf("\tPSECT displaced%s %o=%s+%o\n", byte, addr, name, word); + i += 6; + 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; + 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; + break; + + case 017: + 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; + + 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); + } + + return EXIT_SUCCESS; +} diff --git a/dumpobj.dsp b/dumpobj.dsp new file mode 100644 index 0000000..cbb76d1 --- /dev/null +++ b/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/macro11.c b/macro11.c new file mode 100644 index 0000000..daecfa9 --- /dev/null +++ b/macro11.c @@ -0,0 +1,6198 @@ +/* + 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 += sym->value; + offset -= DOT + 2; + } + } + 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, -128, + 0)) + 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/macro11.dsp b/macro11.dsp new file mode 100644 index 0000000..0a4ddfd --- /dev/null +++ b/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/macro11.dsw b/macro11.dsw new file mode 100644 index 0000000..09378a5 --- /dev/null +++ b/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/macro11.h b/macro11.h new file mode 100644 index 0000000..fd8d8f4 --- /dev/null +++ b/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/makefile b/makefile new file mode 100644 index 0000000..6e9d81c --- /dev/null +++ b/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/mlb.c b/mlb.c new file mode 100644 index 0000000..32b5cb7 --- /dev/null +++ b/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/mlb.h b/mlb.h new file mode 100644 index 0000000..9ec4b68 --- /dev/null +++ b/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/object.c b/object.c new file mode 100644 index 0000000..2d97bbb --- /dev/null +++ b/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/object.h b/object.h new file mode 100644 index 0000000..9411a03 --- /dev/null +++ b/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/rad50.c b/rad50.c new file mode 100644 index 0000000..6d6f2c5 --- /dev/null +++ b/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/rad50.h b/rad50.h new file mode 100644 index 0000000..9a60a01 --- /dev/null +++ b/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/stream2.c b/stream2.c new file mode 100644 index 0000000..edfbcf2 --- /dev/null +++ b/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/stream2.h b/stream2.h new file mode 100644 index 0000000..0c28440 --- /dev/null +++ b/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/util.c b/util.c new file mode 100644 index 0000000..6d8320d --- /dev/null +++ b/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/util.h b/util.h new file mode 100644 index 0000000..d6e7790 --- /dev/null +++ b/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 */ From 6a2afc3fa7e80cd043d6ba50586d6e9e458c501e Mon Sep 17 00:00:00 2001 From: Sergey Svishchev Date: Fri, 15 Feb 2013 23:50:38 +0400 Subject: [PATCH 03/94] Import J Hoppe's 20090427 release from http://retrocmp.com/tools/macro-11-on-windows --- CHANGES | 26 + TODO | 6 + assemble.c | 1500 ++++++++++ assemble.h | 17 + assemble_aux.c | 748 +++++ assemble_aux.h | 91 + assemble_globals.c | 96 + assemble_globals.h | 84 + dumpobj.c | 1026 +++---- extree.c | 701 +++++ extree.h | 66 + listing.c | 172 ++ listing.h | 65 + macro11.c | 6453 +++----------------------------------------- macro11.h | 5 +- macros.c | 550 ++++ macros.h | 71 + makefile | 7 +- mlb.c | 369 ++- mlb.h | 31 +- object.c | 942 ++++--- object.h | 380 ++- parse.c | 856 ++++++ parse.h | 54 + rad50.c | 105 +- rad50.h | 12 +- rept_irpc.c | 371 +++ rept_irpc.h | 26 + stream2.c | 387 +-- stream2.h | 125 +- symbols.c | 478 ++++ symbols.h | 361 +++ util.c | 211 +- util.h | 48 +- 34 files changed, 8705 insertions(+), 7735 deletions(-) create mode 100644 assemble.c create mode 100644 assemble.h create mode 100644 assemble_aux.c create mode 100644 assemble_aux.h create mode 100644 assemble_globals.c create mode 100644 assemble_globals.h create mode 100644 extree.c create mode 100644 extree.h create mode 100644 listing.c create mode 100644 listing.h create mode 100644 macros.c create mode 100644 macros.h create mode 100644 parse.c create mode 100644 parse.h create mode 100644 rept_irpc.c create mode 100644 rept_irpc.h create mode 100644 symbols.c create mode 100644 symbols.h diff --git a/CHANGES b/CHANGES index 9fa1e30..41908d7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,29 @@ + +19.4.2009: JH + version 0.3 + - bugfix: Illegal labels and illegal opcodes are processed as + "implied .WORD" directives. + Expression errors in "do_word()" did not process any input character, + so parser did go into an endless loop. + - Switchable syntax extensions with -yxx options: + symbol len can be adjusted with "-ysl" command line option. + "-yus" option allows underscore "_" char in symbols. + This was needed to process code generated by my favorite disassembler. + - command line help added (-h option) + +17.4.2009: JH + version 0.3 + - ".INCLUDE" re-enabled + - refactoring: big 6000+ lines "macro11.c" split into 10 modules. + +15.4.2009: JH + Begin rework by Joerg Hoppe (j_hoppe@t-online.de) + All my changes are marked with "/*JH: .. */" comments + + +----------- Richard Krebiehls entries ------------------ + + 15-July-2001 version 0.2 removed references to snprintf from dumpobj.c and diff --git a/TODO b/TODO index d922637..9cf9f53 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,9 @@ + +listing format errors: ignore whitespace of input + +documentation: print supported directives + +--------------------------------------- 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 diff --git a/assemble.c b/assemble.c new file mode 100644 index 0000000..bb9cd42 --- /dev/null +++ b/assemble.c @@ -0,0 +1,1500 @@ +#define ASSEMBLE__C + + +#include +#include + +#include "assemble.h" /* my own definitions */ + +#include "assemble_globals.h" +#include "assemble_aux.h" + +#include "util.h" + +#include "mlb.h" +#include "object.h" +#include "listing.h" +#include "parse.h" +#include "symbols.h" +#include "extree.h" +#include "macros.h" +#include "rept_irpc.h" + +#include "rad50.h" + + + + +/* 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. */ + +static 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 unsatisfied 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 != SECTION_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 = SYMBOLFLAG_PERMANENT | SYMBOLFLAG_DEFINITION | local; + SYMBOL *sym; + + ncp = skipwhite(ncp); + if (*ncp == ':') { /* Colon, for symbol definition? */ + ncp++; + /* maybe it's a global definition */ + if (*ncp == ':') { + flag |= SYMBOLFLAG_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 = SYMBOLFLAG_DEFINITION | local; + cp++; + if (*cp == '=') { + flags |= SYMBOLFLAG_GLOBAL; /* Global definition */ + cp++; + } + if (*cp == ':') { + flags |= SYMBOLFLAG_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 & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) == SYMBOLFLAG_GLOBAL) { + report(stack->top, "Can't ORG to external location\n"); + } else if (sym->flags & SYMBOLFLAG_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, SYMBOLFLAG_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 SECTION_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 = (int) 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, SYMBOLFLAG_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), SYMBOLFLAG_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, SYMBOLFLAG_DEFINITION | local, &absolute_section, &symbol_st); + free_addr_mode(&mode); + free(label); + + return 1; + } + + case P_INCLUDE: + { + 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 = SECTION_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 |= SYMBOLFLAG_GLOBAL | (op->value == P_WEAK ? SYMBOLFLAG_WEAK : 0); + } else + sym = add_sym(label, 0, + SYMBOLFLAG_GLOBAL | (op->value == P_WEAK ? SYMBOLFLAG_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 SECTION_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 */ + /*JH: fall through in case of illegal opcode, illegal label! */ + free(label); + + return do_word(stack, tr, cp, 2); +} + +/* assemble_stack assembles the input stack. It returns the error + count. */ + +int assemble_stack( + STACK *stack, + TEXT_RLD *tr) +{ + int res; + int errcount = 0; + + while ((res = assemble(stack, tr)) >= 0) { + list_flush(); + if (res == 0) + errcount++; /* Count an error */ + } + + return errcount; +} diff --git a/assemble.h b/assemble.h new file mode 100644 index 0000000..fcde6e8 --- /dev/null +++ b/assemble.h @@ -0,0 +1,17 @@ + +#ifndef ASSEMBLE__H +#define ASSEMBLE__H + + +#include "stream2.h" +#include "object.h" + + + +#define DOT (current_pc->value) /* Handy reference to the current location */ + +int assemble_stack( + STACK *stack, + TEXT_RLD *tr); + +#endif diff --git a/assemble_aux.c b/assemble_aux.c new file mode 100644 index 0000000..2457129 --- /dev/null +++ b/assemble_aux.c @@ -0,0 +1,748 @@ + +/* + Smaller operators for assemble +*/ + +#include +#include +#include + +#include "util.h" + +#include "assemble_aux.h" /* my own definitions */ + +#include "assemble_globals.h" +#include "macros.h" +#include "assemble.h" +#include "listing.h" +#include "symbols.h" +#include "parse.h" + + +/* Allocate a new section */ + +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; +} + + + +/* This is called by places that are about to store some code, or + which want to manually update DOT. */ + +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 */ + +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); +} + +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); +} + + +/* free_addr_mode frees the storage consumed by an addr_mode */ + +void free_addr_mode( + ADDR_MODE *mode) +{ + if (mode->offset) + free_tree(mode->offset); + mode->offset = NULL; +} + +/* Get the register indicated by the expression */ + +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 == SECTION_REGISTER) { + reg = expr->data.symbol->value; + return reg; + } + + return NO_REG; +} + + +/* + 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 & SYMBOLFLAG_LOCAL)) { /* Unless it's a + local symbol, */ + sym = add_sym(value->data.symbol->label, 0, SYMBOLFLAG_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. */ + +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; + } +} + +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 & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) == SYMBOLFLAG_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 */ + +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 & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) == SYMBOLFLAG_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--; + } +} + + +/* 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. +*/ + +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 & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) == SYMBOLFLAG_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. */ + +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. +*/ + +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; +} + + +/* write_globals writes out the GSD prior to the second assembly pass */ + +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 & SYMBOLFLAG_GLOBAL) && sym->section == psect) { + gsd_global(&gsd, sym->label, + (sym-> + flags & SYMBOLFLAG_DEFINITION ? GLOBAL_DEF : 0) | ((sym-> + flags & SYMBOLFLAG_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); +} diff --git a/assemble_aux.h b/assemble_aux.h new file mode 100644 index 0000000..98ab12a --- /dev/null +++ b/assemble_aux.h @@ -0,0 +1,91 @@ + +#ifndef ASSEMBLE_AUX__H +#define ASSEMBLE_AUX__H + +#include "stream2.h" +#include "object.h" +#include "extree.h" + +#define NO_REG 0777 + + +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; + +void push_cond( + int ok, + STREAM *str); +void pop_cond( + int to); + +int express_sym_offset( + EX_TREE *value, + SYMBOL **sym, + unsigned *offset); + +void change_dot( + TEXT_RLD *tr, + int size); + +int store_word( + STREAM *str, + TEXT_RLD *tr, + int size, + unsigned word); +int store_limits( + STREAM *str, + TEXT_RLD *tr); +void store_value( + STACK *stack, + TEXT_RLD *tr, + int size, + EX_TREE *value); + +int do_word( + STACK *stack, + TEXT_RLD *tr, + char *cp, + int size); + +SECTION *new_section( + void); +void go_section( + TEXT_RLD *tr, + SECTION *sect); + +void free_addr_mode( + ADDR_MODE *mode); + +int eval_defined( + EX_TREE *value); +int eval_undefined( + EX_TREE *value); + + +void mode_extension( + TEXT_RLD *tr, + ADDR_MODE *mode, + STREAM *str); +int check_branch( + STACK *stack, + unsigned offset, + int min, + int max); +unsigned get_register( + EX_TREE *expr); + +void write_globals( + FILE *obj); +void migrate_implicit( + void); + + + +#endif diff --git a/assemble_globals.c b/assemble_globals.c new file mode 100644 index 0000000..18c7f32 --- /dev/null +++ b/assemble_globals.c @@ -0,0 +1,96 @@ + +#define ASSEMBLE_GLOBALS__C + + +#include "assemble_globals.h" /* own definitions */ + +#include "object.h" + + +/* 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 suppressed = 0; /* Assembly suppressed by failed conditional */ + + +MLB *mlbs[MAX_MLBS]; /* macro libraries specified on the + command line */ +int nr_mlbs = 0; /* Number of macro libraries */ + +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 */ + +/* The following are dummy psects for symbols which have meaning to +the assembler: */ + +SECTION register_section = { + "", SECTION_REGISTER, 0, 0 +}; /* the section containing the registers */ + +SECTION pseudo_section = { + "", SECTION_PSEUDO, 0, 0 +}; /* the section containing the + pseudo-operations */ + +SECTION instruction_section = { + ". ABS.", SECTION_INSTRUCTION, 0, 0 +}; /* the section containing instructions */ + +SECTION macro_section = { + "", SECTION_SYSTEM, 0, 0, 0 +}; /* Section for macros */ + +/* These are real psects that get written out to the object file */ + +SECTION absolute_section = { + ". ABS.", SECTION_SYSTEM, PSECT_GBL | PSECT_COM, 0, 0, 0 +}; /* The default + absolute section */ + +SECTION blank_section = { + "", 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 */ diff --git a/assemble_globals.h b/assemble_globals.h new file mode 100644 index 0000000..c657b4e --- /dev/null +++ b/assemble_globals.h @@ -0,0 +1,84 @@ + +#ifndef ASSEMBLE_GLOBALS__H +#define ASSEMBLE_GLOBALS__H + + +#include "mlb.h" +#include "symbols.h" +#include "extree.h" +//#include "stream2.h" +//#include "object.h" + + + +#define MAX_MLBS 32 /* number of macro libraries */ + +#define MAX_CONDS 256 +typedef struct cond { + int ok; /* What the condition evaluated to */ + char *file; /* What file and line it occurred */ + int line; +} COND; + + +#ifndef ASSEMBLE_GLOBALS__C +/* GLOBAL VARIABLES */ +extern int pass; /* The current assembly pass. 0 = first pass */ +extern int stmtno; /* The current source line number */ +extern int radix; /* The current input conversion radix */ +extern int lsb; /* The current local symbol section identifier */ +extern int last_lsb; /* The last block in which a macro + automatic label was created */ + +extern int last_locsym; /* The last local symbol number generated */ + +extern int enabl_debug; /* Whether assembler debugging is enabled */ + +extern int enabl_ama; /* When set, chooses absolute (037) versus + PC-relative */ +/* (067) addressing mode */ +extern int enabl_lsb; /* When set, stops non-local symbol + definitions from delimiting local + symbol sections. */ + +extern int enabl_gbl; /* Implicit definition of global symbols */ + +extern int suppressed; /* Assembly suppressed by failed conditional */ + +extern MLB *mlbs[MAX_MLBS]; /* macro libraries specified on the command line */ +extern int nr_mlbs; /* Number of macro libraries */ + +extern COND conds[MAX_CONDS]; /* Stack of recent conditions */ +extern int last_cond; /* 0 means no stacked cond. */ + +extern SECTION *sect_stack[32]; /* 32 saved sections */ +extern int sect_sp; /* Stack pointer */ + +extern char *module_name; /* The module name (taken from the 'TITLE'); */ + +extern char *ident; /* .IDENT name */ + +extern EX_TREE *xfer_address; /* The transfer address */ + +extern SYMBOL *current_pc; /* The current program counter */ + +extern unsigned last_dot_addr; /* Last coded PC... */ +extern SECTION *last_dot_section; /* ...and it's program section */ + +/* The following are dummy psects for symbols which have meaning to + the assembler: */ +extern SECTION register_section; +extern SECTION pseudo_section; /* the section containing the pseudo-operations */ +extern SECTION instruction_section; /* the section containing instructions */ +extern SECTION macro_section; /* Section for macros */ + +/* These are real psects that get written out to the object file */ +extern SECTION absolute_section; /* The default absolute section */ +extern SECTION blank_section; +extern SECTION *sections[256]; /* Array of sections in the order they were defined */ +extern int sector; /* number of such sections */ + +#endif + + +#endif diff --git a/dumpobj.c b/dumpobj.c index 680810c..221fbe4 100644 --- a/dumpobj.c +++ b/dumpobj.c @@ -47,557 +47,639 @@ DAMAGE. #define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) -int psectid = 0; -char *psects[256]; +int psectid = 0; +char *psects[256]; +FILE *bin = NULL; +int badbin = 0; +int xferad = 1; -char *readrec(FILE *fp, int *len) +char *readrec( + FILE *fp, + int *len) { - int c, i; - int chksum; - char *buf; + int c, + i; + int chksum; + char *buf; - chksum = 0; - - while(c = fgetc(fp), c != EOF && c == 0) - ; + chksum = 0; - if(c == EOF) - return NULL; + while (c = fgetc(fp), c != EOF && c == 0) ; - if(c != 1) - { - fprintf(stderr, "Improperly formatted OBJ file (1)\n"); - return NULL; // Not a properly formatted file. - } + if (c == EOF) + return NULL; - chksum -= c; + if (c != 1) { + fprintf(stderr, "Improperly formatted OBJ file (1)\n"); + return NULL; // Not a properly formatted file. + } - c = fgetc(fp); - if(c != 0) - { - fprintf(stderr, "Improperly formatted OBJ file (2)\n"); - return NULL; // Not properly formatted - } + chksum -= c; - chksum -= c; // even though for 0 the checksum isn't changed... + c = fgetc(fp); + if (c != 0) { + fprintf(stderr, "Improperly formatted OBJ file (2)\n"); + return NULL; // Not properly formatted + } - c = fgetc(fp); - if(c == EOF) - { - fprintf(stderr, "Improperly formatted OBJ file (3)\n"); - return NULL; - } - *len = c; + chksum -= c; // even though for 0 the checksum isn't changed... - chksum -= c; + c = fgetc(fp); + if (c == EOF) { + fprintf(stderr, "Improperly formatted OBJ file (3)\n"); + return NULL; + } + *len = c; - c = fgetc(fp); - if(c == EOF) - { - fprintf(stderr, "Improperly formatted OBJ file (4)\n"); - return NULL; - } + chksum -= c; - *len += (c << 8); + c = fgetc(fp); + if (c == EOF) { + fprintf(stderr, "Improperly formatted OBJ file (4)\n"); + return NULL; + } - chksum -= c; + *len += (c << 8); - *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 - } + chksum -= c; - i = fread(buf, 1, *len, fp); - if(i < *len) - { - free(buf); - fprintf(stderr, "Improperly formatted OBJ file (6)\n"); - return NULL; - } + *len -= 4; // Subtract header and length bytes from length + if (*len < 0) { + fprintf(stderr, "Improperly formatted OBJ file (5)\n"); + return NULL; + } - for(i = 0; i < *len; i++) - { - chksum -= (buf[i] & 0xff); - } + buf = malloc(*len); + if (buf == NULL) { + fprintf(stderr, "Out of memory allocating %d bytes\n", *len); + return NULL; // Bad alloc + } - c = fgetc(fp); - c &= 0xff; - chksum &= 0xff; + i = fread(buf, 1, *len, fp); + if (i < *len) { + free(buf); + fprintf(stderr, "Improperly formatted OBJ file (6)\n"); + return NULL; + } - if(c != chksum) - { - free(buf); - fprintf(stderr, "Bad record checksum, " - "calculated=%d, recorded=%d\n", chksum, c); - return NULL; - } + for (i = 0; i < *len; i++) + chksum -= (buf[i] & 0xff); - return buf; + 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) +void dump_bytes( + char *buf, + int len) { - int i, j; + 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); - } + 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, ""); + 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); - } + for (j = i; j < len && j < i + 8; j++) { + int c = buf[j] & 0xff; - putchar('\n'); - } + if (!isprint(c)) + c = '.'; + putchar(c); + } + + putchar('\n'); + } } -void dump_words(unsigned addr, char *buf, int len) +void dump_words( + unsigned addr, + char *buf, + int len) { - int i, j; + int i, + j; - for(i = 0; i < len; i += 8) - { - printf("\t%6.6o: ", addr); + 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); - } + for (j = i; j < len && j < i + 8; j += 2) + if (len - j >= 2) { + unsigned word = WORD(buf + j); - printf("%*s", (i+8 - j) * 7 / 2, ""); + printf("%6.6o ", word); + } else + printf("%3.3o ", buf[j] & 0xff); - for(j = i; j < len && j < i+8; j++) - { - int c = buf[j] & 0xff; - if(!isprint(c)) - c = '.'; - putchar(c); - } + printf("%*s", (i + 8 - j) * 7 / 2, ""); - putchar('\n'); - addr += 8; - } + 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 trim(char *buf) +void dump_bin( + unsigned addr, + char *buf, + int len) { - char *cp; + 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(cp = buf + strlen(buf); cp > buf; cp--) - { - if(cp[-1] != ' ') - break; - } - *cp = 0; + 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. */ } -char **all_gsds = NULL; -int nr_gsds = 0; -int gsdsize = 0; - -void add_gsdline(char *line) +void trim( + char *buf) { - 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); - } - } + char *cp; - all_gsds[nr_gsds++] = line; + for (cp = buf + strlen(buf); cp > buf; cp--) + if (cp[-1] != ' ') + break; + *cp = 0; } -void got_gsd(char *cp, int len) +char **all_gsds = NULL; +int nr_gsds = 0; +int gsdsize = 0; + +void add_gsdline( + char *line) { - 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); - 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); - } + 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; } -int compare_gsdlines(const void *p1, const void *p2) +void got_gsd( + char *cp, + int len) { - const char * const *l1 = p1, * const *l2 = p2; + int i; + char *gsdline; - return strcmp(*l1, *l2); + 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); + } } -void got_endgsd(char *cp, int len) +int compare_gsdlines( + const void *p1, + const void *p2) { - int i; + const char *const *l1 = p1, + *const *l2 = p2; - 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); + return strcmp(*l1, *l2); } -unsigned last_text_addr = 0; - -void got_text(char *cp, int len) +void got_endgsd( + char *cp, + int len) { - unsigned addr = WORD(cp+2); + int i; - last_text_addr = addr; + qsort(all_gsds, nr_gsds, sizeof(char *), compare_gsdlines); - printf("TEXT ADDR=%o LEN=%o\n", last_text_addr, len-4); + printf("GSD:\n"); - dump_words(last_text_addr, cp+4, len-4); + for (i = 0; i < nr_gsds; i++) { + fputs(all_gsds[i], stdout); + free(all_gsds[i]); + } + + printf("ENDGSD\n"); + + free(all_gsds); } -void rad50name(char *cp, char *name) +unsigned last_text_addr = 0; + +void got_text( + char *cp, + int len) { - unrad50(WORD(cp), name); - unrad50(WORD(cp+2), name+3); - name[6] = 0; - trim(name); + 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 got_rld(char *cp, int len) +void rad50name( + char *cp, + char *name) { - 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; - break; - case 04: - rad50name(cp+i+2, name); - printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); - i += 6; - 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; - 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; - 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; - break; - case 014: - rad50name(cp+i+2, name); - - printf("\tPSECT displaced%s %o=%s+%o\n", byte, addr, name, word); - i += 6; - 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; - 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; - break; - - case 017: - 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; - } - } - + unrad50(WORD(cp), name); + unrad50(WORD(cp + 2), name + 3); + name[6] = 0; + trim(name); } -void got_isd(char *cp, int len) +void got_rld( + char *cp, + int len) { - printf("ISD len=%o\n"); + 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_endmod(char *cp, int len) +void got_isd( + char *cp, + int len) { - printf("ENDMOD\n"); + printf("ISD len=%o\n"); } -void got_libhdr(char *cp, int len) +void got_endmod( + char *cp, + int len) { - printf("LIBHDR\n"); + printf("ENDMOD\n"); } -void got_libend(char *cp, int len) +void got_libhdr( + char *cp, + int len) { - printf("LIBEND\n"); + printf("LIBHDR\n"); } -int main(int argc, char *argv[]) +void got_libend( + char *cp, + int len) { - int len; - FILE *fp; - char *cp; - - fp = fopen(argv[1], "rb"); - if(fp == 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); - } - - return EXIT_SUCCESS; + 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/extree.c b/extree.c new file mode 100644 index 0000000..ef3c814 --- /dev/null +++ b/extree.c @@ -0,0 +1,701 @@ +#define EXTREE__C + +#include +#include +#include + +#include "extree.h" /* my own definitions */ + +#include "util.h" +#include "assemble_globals.h" +#include "object.h" + + +/* 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. */ + +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. */ +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; + + /* I'd prefer this behavior, but MACRO.SAV is a bit too primitive. */ +#if 0 + /* 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 & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) == SYMBOLFLAG_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 & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) != SYMBOLFLAG_GLOBAL + && sym->section->type != SECTION_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; +} + + +/* Allocate an EX_TREE */ + +EX_TREE *new_ex_tree( + void) +{ + EX_TREE *tr = memcheck(malloc(sizeof(EX_TREE))); + + return tr; +} + + +/* Create an EX_TREE representing a parse error */ + +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 */ + +EX_TREE *new_ex_lit( + unsigned value) +{ + EX_TREE *tp; + + tp = new_ex_tree(); + tp->type = EX_LIT; + tp->data.lit = value; + + return tp; +} diff --git a/extree.h b/extree.h new file mode 100644 index 0000000..bd4818f --- /dev/null +++ b/extree.h @@ -0,0 +1,66 @@ + +#ifndef EXTREE__H +#define EXTREE__H + +#include "symbols.h" + +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; + + +EX_TREE *new_ex_tree( + void); +void free_tree( + EX_TREE *tp); + +EX_TREE *new_ex_lit( + unsigned value); +EX_TREE *ex_err( + EX_TREE *tp, + char *cp); + +EX_TREE *evaluate( + EX_TREE *tp, + int undef); + + +#endif diff --git a/listing.c b/listing.c new file mode 100644 index 0000000..0a88292 --- /dev/null +++ b/listing.c @@ -0,0 +1,172 @@ +#define LISTING__C + +#include +#include +#include +#include + +#include "listing.h" /* my own definitions */ + +#include "util.h" +#include "assemble_globals.h" + + +/* GLOBAL VARIABLES */ + +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 */ + +static char *listline; /* Source lines */ + +static char *binline; /* for octal expansion */ + +FILE *lstfile = NULL; + + + + +/* 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 */ + +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); + } +} + +/* list_flush produces a buffered list line. */ + +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 */ + +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); + } +} + + + +/* reports errors */ +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); + } +} diff --git a/listing.h b/listing.h new file mode 100644 index 0000000..9d7a9ea --- /dev/null +++ b/listing.h @@ -0,0 +1,65 @@ + +#ifndef LISTING__H +#define LISTING__H + +#include "stream2.h" + +/* + 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; + + +/* GLOBAL VARIABLES */ +#ifndef LISTING__C +extern int list_md; /* option to list macro/rept definition = yes */ + +extern int list_me; /* option to list macro/rept expansion = yes */ + +extern int list_bex; /* option to show binary */ + +extern int list_level; /* Listing control level. .LIST + increments; .NLIST decrements */ + +//extern char *listline; /* Source lines */ + +extern FILE *lstfile; + +#endif + + +void list_word( + STREAM *str, + unsigned addr, + unsigned value, + int size, + char *flags); + +void list_value( + STREAM *str, + unsigned word); + +void list_source( + STREAM *str, + char *cp); + +void list_flush( + void); + +void report( + STREAM *str, + char *fmt, + ...); + + +#endif diff --git a/macro11.c b/macro11.c index daecfa9..07bceba 100644 --- a/macro11.c +++ b/macro11.c @@ -1,5 +1,8 @@ +#define MACRO11__C + + /* - Assembler compatible with MACRO-11. + Assembler compatible with MACRO-11. Copyright (c) 2001, Richard Krehbiel All rights reserved. @@ -34,6165 +37,329 @@ 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 */ +#include "assemble_globals.h" +#include "assemble.h" +#include "assemble_aux.h" +#include "listing.h" +#include "object.h" +#include "symbols.h" -#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 += sym->value; - offset -= DOT + 2; - } - } - 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, -128, - 0)) - 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) +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; + 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[]) +//JH: +static void print_version( + FILE *strm) { - 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; + fprintf(strm, "macro11 - portable MACRO11 assembler for DEC PDP-11\n"); + fprintf(strm, " Version %s\n", VERSIONSTR); + fprintf(strm, " Copyright 2001 Richard Krehbiel,\n"); + fprintf(strm, " modified 2009 by Joerg Hoppe.\n"); +} + + +//JH: +static void print_help( + void) +{ + printf("\n"); + print_version(stdout); + printf("\n"); + printf("Usage:\n"); + printf(" macro11 [-o ] [-l []] \n"); + printf(" [-h] [-v][-e