diff --git a/crossassemblers/macro11/.indent.pro b/crossassemblers/macro11/.indent.pro new file mode 100755 index 0000000..ebdb836 --- /dev/null +++ b/crossassemblers/macro11/.indent.pro @@ -0,0 +1,45 @@ +--blank-lines-after-commas +--blank-lines-after-declarations +--blank-lines-after-procedures +--braces-on-if-line +--braces-on-struct-decl-line +--break-before-boolean-operator +--break-function-decl-args +--break-function-decl-args-end +--case-indentation0 +--comment-indentation40 +--continuation-indentation8 +--continue-at-parentheses +--cuddle-do-while +--cuddle-else +--declaration-indentation16 +--dont-break-procedure-type +--dont-format-comments +--indent-level4 +--honour-newlines +--leave-optional-blank-lines +--line-comments-indentation0 +--line-length112 +--no-blank-lines-before-block-comments +--no-space-after-function-call-names +--no-tabs +--paren-indentation4 +--space-special-semicolon +--tab-size8 +-T ADDR_MODE +-T ARG +-T BUFFER +-T EX_TREE +-T FILE +-T MACRO +-T MACRO_STREAM +-T MLB +-T MLBENT +-T STACK +-T STREAM +-T SECTION +-T SYMBOL +-T SYMBOL_ITER +-T SYMBOL_TABLE +-T TEXT_COMPLEX +-T TEXT_RLD diff --git a/crossassemblers/macro11/CHANGES b/crossassemblers/macro11/CHANGES new file mode 100644 index 0000000..1f24aed --- /dev/null +++ b/crossassemblers/macro11/CHANGES @@ -0,0 +1,51 @@ +09.11.2015: Rhialto + version 0.4: + - Fixed various bugs. The most notable was extensive use- + after-free in the expression tree, which crashed on NetBSD but + apparently not on other systems. + - Lots of changes to make this MACRO11 more like the MACRO11 of + RSX-11M+ 4.6. I used Kermit-11 source files for comparison. + "The Manual" I'm refering to is + AA-KX10A-TC_PDP-11_MACRO-11_Reference_Manual_May88.pdf at + www.bitsavers.org/pdf/dec/pdp11/rsx11/RSX11Mplus_V4.x/4a/ and + I use an installed system to double-check. + +----------- Joerg Hoppe's entries ------------------ + +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 + mlb.c for portability + fixed a type cast warning in dumpobj.c compare_gsdlines + Removed strcasecmp from macro11.c for portability + Removed references to wnewmem.c from makefile (isn't needed) + makefile more compatible with non-gnu make and compiler + main prints version 0.2 + +14-July-2001 + First release, version 0.1. diff --git a/crossassemblers/macro11/LICENSE b/crossassemblers/macro11/LICENSE new file mode 100644 index 0000000..b5b4413 --- /dev/null +++ b/crossassemblers/macro11/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2001, Richard Krehbiel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +o Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +o Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/crossassemblers/macro11/Makefile b/crossassemblers/macro11/Makefile new file mode 100644 index 0000000..a330a95 --- /dev/null +++ b/crossassemblers/macro11/Makefile @@ -0,0 +1,82 @@ +##### +# +# Makefile for macro11 and dumpobj +# + +WARNS ?= -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow +CFLAGS ?= -O -ggdb -std=gnu99 $(WARNS) + +MACRO11_SRCS = macro11.c \ + assemble.c assemble_globals.c assemble_aux.c \ + extree.c listing.c macros.c parse.c rept_irpc.c symbols.c \ + mlb-rsx.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: git-info.h $(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 + +git-info.h: + ./make-git-info + +# Bootstrap dependency on the git header file, which otherwise +# gets generated too late. +macro11.o: git-info.h + +clean: + -rm -f $(MACRO11_OBJS) $(DUMPOBJ_OBJS) macro11 dumpobj + -rm -f *.d + -rm -f git-info.h + +# Since the only tests we have so far are for crashes, +# just try to assemble. Later, we will need expected/actual tests. + +# Test that all options requiring a value bail out if it's not present. +argtests: macro11 + @ for OPT in -e -d -m -p -o -l -ysl ; do \ + ./macro11 foo.mac $$OPT 2> /dev/null; \ + if (( $$? == 1 )); then echo PASS; else echo FAIL; fi; \ + echo " $$OPT missing value"; \ + ./macro11 foo.mac $$OPT -v 2> /dev/null; \ + if (( $$? == 1 )); then echo PASS; else echo FAIL; fi; \ + echo " $$OPT fol. by option"; \ + done + @ ./macro11 foo.mac $$OPT -x -v 2> /dev/null; \ + if (( $$? == 1 )); then echo PASS; else echo FAIL; fi; \ + echo " -x must be the last option" + +tests: macro11 argtests + @ ACTUAL=`./macro11 tests/test-undef.mac 2>&1`; \ + if [ "tests/test-undef.mac:1: ***ERROR MACRO .TTYOU not found" == "$$ACTUAL" ]; then echo PASS; else echo FAIL; fi; \ + echo " test-undef.mac" + +# Automatic dependency generation + +ifneq ($(MAKECMDGOALS),clean) +-include $(ALL_SRCS:.c=.d) +endif + +# Make .d files as side effect of compiling .c to .o +%.d %.o: %.c + $(CC) $(CFLAGS) -c -o $*.o $< + @set -e; rm -f $*.d; \ + $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o \1.d : ,g' < $@.$$$$ > $*.d; \ + rm -f $@.$$$$ diff --git a/crossassemblers/macro11/README b/crossassemblers/macro11/README new file mode 100644 index 0000000..a57ad01 --- /dev/null +++ b/crossassemblers/macro11/README @@ -0,0 +1,169 @@ +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. + +modified 2009 by Joerg Hoppe, +modified 2015 by Olaf 'Rhialto' Seibert. + +Files: + macro11.c Command line parsing and driving the passes. + assemble.c Handles all of the opcodes and directives. + assemble_aux.c Helper functions mostly called from assemble(). + assemble_globals.c Global variables for assemble(). + extree.c Expression tree: memory management and evaluation. + listing.c Listing generation. + macros.c Define macros and make them available as stream2s. + parse.c Basic parsing for numbers, labels, strings, expressions + rept_irpc.c stream2 subclass for processing .REPT and .IREPT. + object.c Functions for writing RSX-11 compatible .OBJ files. + mlb-rsx.c Classes (!) for reading RSX-11 macro libraries. + 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 A GNU makefile for Linux; simple enough, it + should be convertible to any Unix. + Contains automatic dependency generation. + + macro11.dsp, dumpobj.dsp, macro11.dsw + Visual Studio 6 projects. Out of date. + + 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 Richard's development was done in Microsoft Visual Studio 6, +but currently Olaf maintains it using a Unix (NetBSD) system. I build +with gcc and a lot of warning options, and from time to time check with +a Linux system which has a different gcc version, and clang. The Visual +Studio project files are out of date. + +Richard used the MACRO11 from RT-11 as reference, but I use the one +from RSX-11M+. It turns out there are some small file format +differences. I used the dumpobj command to compare the output of this +macro11 with the reference version when assembling Kermit-11 source +files, and currently there seem to be no significant differences. + +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. + + -I incpath For any .INCLUDE directive, macro11 will + search the INCLUDE path for a file named + to include. The INCLUDE path works like the MCALL + path. If not specified at all, the default is the + current directory. If contains a drive + and/or directory in RSX/RT-11 form + (DEV:[DIR]FILE.EXT) then the search is also tried + without DEV: and without DEV:[DIR]. + + -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. + This also works for extracting an object library + (.OLB) file. + + Various options starting with -y enable extensions: + + -ysl Allow longer symbols up to the given length. + The maximal allowed value is 64. + + -yus Allow underscore in symbol names. + + -yl1 Include the processing of the first pass in the + listing file. This may be useful for finding + phase errors. + + files... Any number of input files. They will be assembled + as if they were concatenated together. + + +You may define the MCALL and INCLUDE environment variable prior to +invoking macro11, as a path to directories containing macros or files to +be included, respectively. diff --git a/crossassemblers/macro11/TODO b/crossassemblers/macro11/TODO new file mode 100644 index 0000000..9cf9f53 --- /dev/null +++ b/crossassemblers/macro11/TODO @@ -0,0 +1,63 @@ + +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 +built, plus a significant chunk of our own code. + +The biggest missing feature is full featured listings. The .LIST and +.NLIST directives are ignored, as is .SBTTL. No table of contents is +accumulated or printed. No symbol cross referencing is done (most +likely I'll just write a CTAGS file, not a cross reference listing). + +Many errors still go unchecked. Off the top of my head, I recall that +object and listing file output errors are ignored. + +.FLT4 format may be inaccurate in the low bits. This is because IEEE +64 bit format has two fewer mantissa bits than 64 bit PDP-11 format. +Without writing soft-float routines, there's not much I can do abbout +it. + +Expression math is done in native width, almost certainly 32 bits. +Truncation to 16 bits is done only for listing and output. This may +make some output differ in the presence of 16-bit overflows and +underflows. I don't think this needs fixing. + +.REM blocks containing code can screw up .MACRO, .REPT, .IRP, .IRPC. +read_body in macro11.c would need to be able to parse and ignore .REM +blocks. + +Need to search a path for the .INCLUDE directive. Right now it only +takes a complete file name. And most likely, existing code will have +RT-11 style file names; I don't know what to do about that, except put +in a device name parser. + +Possible enhancements: + +It would be very simple to make macro11 resolve internal symbols with +more that 6 significant characters. Even so, only the first 6 would +be used for external symbols, and you have to be wary of existing code +that used (for example) .LOOKU rather than .LOOKUP, since these two +would become distinct. + +SYM = 0 +MOV SYM(R0),R0 ; macro11 could optimize SYM(R0) into just (R0) + +I dream of automatically fixing branches out of range. Easy when the +destination is backwards, difficult when it's forwards. I have this +idea: during the first assembly pass, all branches generate a long +branch if the target symbol is undefined, otherwise an "optimized" +branch (short or long) if the target is defined. Then keep a +128-instruction FIFO of generated instructions. Each FIFO entry is +tagged with context and symbol definition as they are pushed to the +FIFO. When an instruction gets pulled from the FIFO because it's more +than 128 words away, the FIFO is searched for long branches that point +to this location; any such are shortened, and any symbols defined +following their location in the stream are adjusted. In the second +assembly pass, the FIFOs aren't used because all jump distances are +known, and the right sized branch (JMP or Bcc) can be generated. + diff --git a/crossassemblers/macro11/assemble.c b/crossassemblers/macro11/assemble.c new file mode 100644 index 0000000..a837551 --- /dev/null +++ b/crossassemblers/macro11/assemble.c @@ -0,0 +1,1652 @@ +#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. */ + + if (!enabl_lc) { /* If lower case disabled, */ + upcase(line); /* turn it into upper case. */ + } + + 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 = get_next_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 *symb; + 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, &symb, &offset)) { + report(stack->top, "Illegal ORG\n"); + } else if ((symb->flags & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) == SYMBOLFLAG_GLOBAL) { + report(stack->top, "Can't ORG to external location\n"); + } else if (symb->flags & SYMBOLFLAG_UNDEFINED) { + report(stack->top, "Can't ORG to undefined sym\n"); + } else if (symb->section != current_pc->section) { + report(stack->top, "Can't ORG to alternate section " "(use PSECT)\n"); + } else { + DOT = symb->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 its 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); + + list_location(stack->top, DOT); + + 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: + if (sect_sp >= SECT_STACK_SIZE - 1) { + report(stack->top, "Too many saved sections for .SAVE\n"); + return 0; + } + sect_sp++; + sect_stack[sect_sp] = current_pc->section; + dot_stack[sect_sp] = DOT; + 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]); + DOT = dot_stack[sect_sp]; + list_location(stack->top, DOT); + if (!enabl_lsb) { + lsb = get_next_lsb(); + } + sect_sp--; + } + return 1; + + case P_NARG: + { + STREAM *str; + MACRO_STREAM *mstr; + int islocal; + + label = get_symbol(cp, &cp, &islocal); + + 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 | islocal, &absolute_section, + &symbol_st); + free(label); + list_value(stack->top, mstr->nargs); + return 1; + } + + case P_NCHR: + { + char *string; + int islocal; + + label = get_symbol(cp, &cp, &islocal); + + 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 | islocal, &absolute_section, + &symbol_st); + free(label); + free(string); + return 1; + } + + case P_NTYPE: + { + ADDR_MODE mode; + int islocal; + + label = get_symbol(cp, &cp, &islocal); + 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 | islocal, &absolute_section, &symbol_st); + free_addr_mode(&mode); + free(label); + + return 1; + } + + case P_INCLUDE: + { + char *name = getstring_fn(cp, &cp); + STREAM *incl; + char hitfile[FILENAME_MAX]; + + if (name == NULL) { + report(stack->top, "Bad .INCLUDE file name\n"); + return 0; + } + + my_searchenv(name, "INCLUDE", hitfile, sizeof(hitfile)); + free(name); + + if (hitfile[0] == '\0') { + report(stack->top, "Unable to find .INCLUDE file \"%s\"\n", name); + return 0; + } + + incl = new_file_stream(hitfile); + if (incl == NULL) { + report(stack->top, "Unable to open .INCLUDE file \"%s\"\n", hitfile); + return 0; + } + + 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_LIBRARY: + if (pass == 0) { + char hitfile[FILENAME_MAX]; + char *name = getstring_fn(cp, &cp); + + my_searchenv(name, "MCALL", hitfile, sizeof(hitfile)); + + if (hitfile[0]) { + mlbs[nr_mlbs] = mlb_open(hitfile, 0); + if (mlbs[nr_mlbs] == NULL) { + report(stack->top, "Unable to register macro library \"%s\"\n", hitfile); + } else { + nr_mlbs++; + } + } else { + report(stack->top, "Unable to locate macro library \"%s\"\n", name); + } + free(name); + } + return 1; + + 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) - 1); + my_searchenv(macfile, "MCALL", hitfile, sizeof(hitfile)); + if (hitfile[0]) + macstr = new_file_stream(hitfile); + else + macstr = NULL; + } + + 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 = get_next_lsb(); + } else if (strcmp(label, "GBL") == 0) { + enabl_gbl = 1; + } else if (strcmp(label, "LC") == 0) { + enabl_lc = 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) { + lsb = get_next_lsb(); + enabl_lsb = 0; + } else if (strcmp(label, "GBL") == 0) { + enabl_gbl = 0; + } else if (strcmp(label, "LC") == 0) { + enabl_lc = 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 || + strcmp(label, "NB") == 0) { + cp = skipwhite(cp); + if (EOL(*cp)) { + ok = 1; + } else { + char *thing, *end; + + thing = getstring(cp, &cp); + end = skipwhite(thing); + ok = (*end == 0); + free(thing); + } + if (label[0] == 'N') { + ok = !ok; + } + } else if (strcmp(label, "IDN") == 0 || + 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); + if (label[0] == 'D') { + ok = !ok; + } + free(thing1); + free(thing2); + } else if (strcmp(label, "P1") == 0) { + ok = (pass == 0); + } else if (strcmp(label, "P2") == 0) { + ok = (pass == 1); + } else { + int sword; + unsigned uword; + EX_TREE *tvalue = parse_expr(cp, 0); + + cp = tvalue->cp; + + if (tvalue->type != EX_LIT) { + report(stack->top, "Bad .IF expression\n"); + list_value(stack->top, 0); + free_tree(tvalue); + ok = FALSE; /* Pick something. */ + } else { + unsigned word; + + /* Convert to signed and unsigned words */ + sword = tvalue->data.lit & 0x7fff; + + /* FIXME I don't know if the following + is portable enough. */ + if (tvalue->data.lit & 0x8000) + sword |= ~0xFFFF; /* Render negative */ + + /* Reduce unsigned value to 16 bits */ + uword = tvalue->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; + else + ok = 0, word = 0; + + list_value(stack->top, word); + + free_tree(tvalue); + } + } + + 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++; + change_dot(tr, 0); + } + return 1; + + case P_ODD: + if (!(DOT & 1)) { + list_word(stack->top, DOT, 0, 1, ""); + DOT++; + change_dot(tr, 0); + } + return 1; + + case P_ASECT: + if (!enabl_lsb) { + lsb = get_next_lsb(); + } + go_section(tr, &absolute_section); + list_location(stack->top, DOT); + return 1; + + case P_CSECT: + case P_PSECT: + { + SYMBOL *sectsym; + SECTION *sect; + unsigned int old_flags = ~0u; + int unnamed_csect = 0; + + label = get_symbol(cp, &cp, NULL); + if (label == NULL) { + if (op->value == P_CSECT) { + label = memcheck(strdup(". BLK.")); + unnamed_csect = 1; + } else { + label = memcheck(strdup("")); /* Allow blank */ + } + } + + sectsym = lookup_sym(label, §ion_st); + if (sectsym) { + sect = sectsym->section; + free(label); + old_flags = sect->flags; + } 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); + + /* page 6-41 table 6-5 */ + if (op->value == P_PSECT) { + sect->flags |= PSECT_REL; + } else if (op->value == P_CSECT) { + if (unnamed_csect) { + sect->flags |= PSECT_REL; + } else { + sect->flags |= PSECT_REL | PSECT_COM | PSECT_GBL; + } + } + } + + cp = skipdelim(cp); + if (!EOL(*cp)) { + 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, "NOSAV") == 0) { + sect->flags &= ~PSECT_SAV; /* Is not root */ + } else if (strcmp(label, "OVR") == 0) { + sect->flags |= PSECT_COM; /* Is common */ + } else if (strcmp(label, "CON") == 0) { + sect->flags &= ~PSECT_COM; /* Concatenated */ + } 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); + } + /* If a section is declared a second time, and flags + * are given, then they must be identical to the + * first time. + * See page 6-38 of AA-KX10A-TC_PDP-11_MACRO-11_Reference_Manual_May88.pdf . + */ + if (old_flags != ~0u && sect->flags != old_flags) { + /* The manual also says that any different + * flags are ignored, and an error issued. + * Apparently, that isn't true. + * Kermit seems to do this in k11cmd.mac: + * .psect $pdata ; line 16 + * .psect $pdata ,ro,d,lcl,rel,con + * ; k11mac.mac, first pass only + * .psect $PDATA ,D ; line 1083 + * and ends up with + * $PDATA 001074 003 (RO,D,LCL,REL,CON) + */ + + /* + sect->flags = old_flags; + report(stack->top, "Program section flags not identical\n"); + */ + } + } + + if (!enabl_lsb) { + lsb = get_next_lsb(); + } + go_section(tr, sect); + list_location(stack->top, DOT); + + 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: + { + do { + cp = skipwhite(cp); + if (*cp == '<') { + EX_TREE *value; + /* A byte value */ + value = parse_unary_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 */ + } + { + char *radstr; + int i, len; + + /* + * Allocate storage sufficient for the rest of + * the line. + */ + radstr = memcheck(malloc(strlen(cp))); + len = 0; + + do { + cp = skipwhite(cp); + if (*cp == '<') { + EX_TREE *value; + /* A byte value */ + value = parse_unary_expr(cp, 0); + cp = value->cp; + if (value->type != EX_LIT) { + report(stack->top, "expression must be constant\n"); + radstr[len++] = 0; + } else if (value->data.lit >= 050) { + report(stack->top, "invalid character value %o\n", value->data.lit); + radstr[len++] = 0; + } else { + radstr[len++] = value->data.lit; + } + free_tree(value); + } else { + char quote = *cp++; + + while (*cp && *cp != '\n' && *cp != quote) { + int ch = ascii2rad50(*cp++); + + if (ch == -1) { + report(stack->top, "invalid character '%c'\n", cp[-1]); + radstr[len++] = 0; + } else { + radstr[len++] = ch; + } + + } + cp++; /* Skip closing quote */ + } + + cp = skipwhite(cp); + } while (!EOL(*cp)); + + for (i = 0; i < len; i += 3) { + int word = packrad50word(radstr + i, len - i); + store_word(stack->top, tr, 2, word); + } + + free(radstr); + } + 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 & 070) == 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; + } + + cp = skipwhite(cp); + 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 = NULL; + + /* 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 (%s)\n", + sym ? "not same section" + : "can't express offset"); + 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 (not literal; ABS section)\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 (can't express offset)\n"); + free_tree(value); + return 0; + } + /* Must be same section */ + if (sym->section != current_pc->section) { + report(stack->top, "Bad branch target (different section)\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 (not a literal)\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; + } + + if ((mode.type & 070) == 0) { + report(stack->top, "JSR Rn,Rm is illegal\n"); + /* But encode it anyway... */ + } + + 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; + + 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; + } + + 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); +} + +int get_next_lsb( + void) +{ + if (lsb_used) { + lsb_used = 0; + if (enabl_debug && lstfile) { + fprintf(lstfile, "get_next_lsb: lsb: %d becomes %d (= next_lsb)\n", lsb, next_lsb); + } + return next_lsb++; + } else { + if (enabl_debug && lstfile) { + fprintf(lstfile, "get_next_lsb: lsb: stays %d\n", lsb); + } + return lsb; + } +} + +/* 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/crossassemblers/macro11/assemble.h b/crossassemblers/macro11/assemble.h new file mode 100644 index 0000000..369a007 --- /dev/null +++ b/crossassemblers/macro11/assemble.h @@ -0,0 +1,19 @@ + +#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); +int get_next_lsb( + void); + +#endif diff --git a/crossassemblers/macro11/assemble_aux.c b/crossassemblers/macro11/assemble_aux.c new file mode 100644 index 0000000..a5b7180 --- /dev/null +++ b/crossassemblers/macro11/assemble_aux.c @@ -0,0 +1,777 @@ + +/* + 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: + { + if (!(value->data.symbol->flags & SYMBOLFLAG_LOCAL)) { /* Unless it's a + local symbol, */ + add_sym(value->data.symbol->label, 0, SYMBOLFLAG_GLOBAL, &absolute_section, &implicit_st); + } + } + break; + case EX_LIT: + case EX_SYM: + case EX_TEMP_SYM: // Impossible on this pass + 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. + } + isym->flags |= SYMBOLFLAG_IMPLICIT_GLOBAL; + 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) +{ + (void)tr; + + 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) +{ + int comma; + + if (size == 2 && (DOT & 1)) { + report(stack->top, ".WORD on odd boundary\n"); + store_word(stack->top, tr, 1, 0); /* Align it */ + } + + cp = skipwhite(cp); + + do { + if (cp[0] == ',') { + /* Empty expressions count as 0 */ + store_word(stack->top, tr, size, 0); + } else { + EX_TREE *value = parse_expr(cp, 0); + + if (value->cp > cp) { + store_value(stack, tr, size, value); + + cp = value->cp; + } else { + report(stack->top, "Invalid expression in .WORD\n"); + cp = ""; /* force loop to end */ + } + + free_tree(value); + } + } while (cp = skipdelim_comma(cp, &comma), !EOL(*cp)); + + if (comma) { + /* Trailing empty expressions count as 0 */ + store_word(stack->top, tr, size, 0); + } + + 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) { + for (isect = 0; isect < sector; isect++) { + psect = sections[isect]; + + psect->sector = isect; /* Assign it a sector */ + psect->pc = 0; /* Reset its PC for second pass */ + } + return; /* Nothing more 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 its 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 *lsym; + unsigned offset; + + if (!express_sym_offset(xfer_address, &lsym, &offset)) { + report(NULL, "Illegal program transfer address\n"); + } else { + gsd_xfer(&gsd, lsym->section->label, lsym->value + offset); + } + } + + gsd_flush(&gsd); + + gsd_end(&gsd); +} diff --git a/crossassemblers/macro11/assemble_aux.h b/crossassemblers/macro11/assemble_aux.h new file mode 100644 index 0000000..98ab12a --- /dev/null +++ b/crossassemblers/macro11/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/crossassemblers/macro11/assemble_globals.c b/crossassemblers/macro11/assemble_globals.c new file mode 100644 index 0000000..40742b3 --- /dev/null +++ b/crossassemblers/macro11/assemble_globals.c @@ -0,0 +1,101 @@ + +#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 lsb_used = 0; /* Whether there was a local symbol using this lsb */ +int next_lsb = 0; /* The number of the next local symbol block */ +int last_macro_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 enabl_lc = 1; /* If lowercase disabled, convert assembler + source to upper case. */ + +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[SECT_STACK_SIZE]; /* 32 saved sections */ +int dot_stack[SECT_STACK_SIZE]; /* 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, 0, 0 +}; /* the section containing the registers */ + +SECTION pseudo_section = { + "", SECTION_PSEUDO, 0, 0, 0, 0 +}; /* the section containing the + pseudo-operations */ + +SECTION instruction_section = { + ". ABS.", SECTION_INSTRUCTION, 0, 0, 0, 0 +}; /* the section containing instructions */ + +SECTION macro_section = { + "", SECTION_SYSTEM, 0, 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/crossassemblers/macro11/assemble_globals.h b/crossassemblers/macro11/assemble_globals.h new file mode 100644 index 0000000..ea5ff01 --- /dev/null +++ b/crossassemblers/macro11/assemble_globals.h @@ -0,0 +1,88 @@ + +#ifndef ASSEMBLE_GLOBALS__H +#define ASSEMBLE_GLOBALS__H + + +#include "mlb.h" +#include "symbols.h" +#include "extree.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; + +#define SECT_STACK_SIZE 32 + +#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 lsb_used; /* Whether there was a local symbol using this lsb */ +extern int next_lsb; /* The number of the next local symbol block */ +extern int last_macro_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 enabl_lc; /* If lowercase disabled, convert assembler + source to upper case. */ +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[SECT_STACK_SIZE]; /* 32 saved sections */ +extern int dot_stack[SECT_STACK_SIZE]; /* 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/crossassemblers/macro11/dumpobj.c b/crossassemblers/macro11/dumpobj.c new file mode 100644 index 0000000..7260784 --- /dev/null +++ b/crossassemblers/macro11/dumpobj.c @@ -0,0 +1,745 @@ +/* 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)) + +#define NPSECTS 256 + +int psectid = 0; +char *psects[NPSECTS]; +FILE *bin = NULL; +int badbin = 0; +int xferad = 1; + +char *readrec( + FILE *fp, + int *len) +{ + int c, + i; + int chksum; + char *buf; + + chksum = 0; + +#if RT11 + 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... */ +#endif /* RT11 */ + + c = fgetc(fp); + if (c == EOF) { +#if RT11 + fprintf(stderr, "Improperly formatted OBJ file (3)\n"); +#endif /* RT11 */ + 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; + +#if RT11 + *len -= 4; /* Subtract header and length bytes from length */ +#endif + 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); + +#if RT11 + c = fgetc(fp); + c &= 0xff; + chksum &= 0xff; + + if (c != chksum) { + free(buf); + fprintf(stderr, "Bad record checksum, " "calculated=$%04x, recorded=$%04x\n", chksum, c); + return NULL; + } +#else + if (*len & 1) { + /* skip 1 byte of padding */ + c = fgetc(fp); + if (c == EOF) { + free(buf); + fprintf(stderr, "EOF where padding byte should be\n"); + return NULL; + } + + } +#endif /* RT11 */ + + return buf; +} + +void dump_bytes( + char *buf, + int len) +{ + int i, + j; + + for (i = 0; i < len; i += 8) { + printf("\t%3.3o: ", i); + for (j = i; j < len && j < i + 8; j++) + printf("%3.3o ", buf[j] & 0xff); + + printf("%*s", (i + 8 - j) * 4, ""); + + for (j = i; j < len && j < i + 8; j++) { + int c = buf[j] & 0xff; + + if (!isprint(c)) + c = '.'; + putchar(c); + } + + putchar('\n'); + } +} + +void dump_words( + unsigned addr, + char *buf, + int len) +{ + int i, + j; + + for (i = 0; i < len; i += 8) { + printf("\t%6.6o: ", addr); + + for (j = i; j < len && j < i + 8; j += 2) + if (len - j >= 2) { + unsigned word = WORD(buf + j); + + printf("%6.6o ", word); + } else + printf("%3.3o ", buf[j] & 0xff); + + printf("%*s", (i + 8 - j) * 7 / 2, ""); + + for (j = i; j < len && j < i + 8; j++) { + int c = buf[j] & 0xff; + + if (!isprint(c)) + c = '.'; + putchar(c); + } + + putchar('\n'); + addr += 8; + } +} + +void dump_bin( + unsigned addr, + char *buf, + int len) +{ + int chksum; /* Checksum is negative sum of all + bytes including header and length */ + int FBR_LEAD1 = 1, + FBR_LEAD2 = 0; + int i; + unsigned hdrlen = len + 6; + + for (i = 0; i < 8; i++) + fputc(0, bin); + chksum = 0; + if (fputc(FBR_LEAD1, bin) == EOF) + return; /* All recs begin with 1,0 */ + chksum -= FBR_LEAD1; + if (fputc(FBR_LEAD2, bin) == EOF) + return; + chksum -= FBR_LEAD2; + + i = hdrlen & 0xff; /* length, lsb */ + chksum -= i; + if (fputc(i, bin) == EOF) + return; + + i = (hdrlen >> 8) & 0xff; /* length, msb */ + chksum -= i; + if (fputc(i, bin) == EOF) + return; + + i = addr & 0xff; /* origin, msb */ + chksum -= i; + if (fputc(i, bin) == EOF) + return; + + i = (addr >> 8) & 0xff; /* origin, lsb */ + chksum -= i; + if (fputc(i, bin) == EOF) + return; + + if ((len == 0) || (buf == NULL)) + return; /* end of tape block */ + + i = fwrite(buf, 1, len, bin); + if (i < len) + return; + + while (len > 0) { /* All the data bytes */ + chksum -= *buf++ & 0xff; + len--; + } + + chksum &= 0xff; + + fputc(chksum, bin); /* Followed by the checksum byte */ + + return; /* Worked okay. */ +} + +void trim( + char *buf) +{ + char *cp; + + for (cp = buf + strlen(buf); cp > buf; cp--) + if (cp[-1] != ' ') + break; + *cp = 0; +} + +char **all_gsds = NULL; +int nr_gsds = 0; +int gsdsize = 0; + +void add_gsdline( + char *line) +{ + if (nr_gsds >= gsdsize || all_gsds == NULL) { + gsdsize += 128; + all_gsds = realloc(all_gsds, gsdsize * sizeof(char *)); + if (all_gsds == NULL) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + } + + all_gsds[nr_gsds++] = line; +} + +void got_gsd( + char *cp, + int len) +{ + int i; + char *gsdline; + + for (i = 2; i < len; i += 8) { + char name[8]; + unsigned value; + unsigned flags; + + gsdline = malloc(256); + if (gsdline == NULL) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + unrad50(WORD(cp + i), name); + unrad50(WORD(cp + i + 2), name + 3); + name[6] = 0; + + value = WORD(cp + i + 6); + flags = cp[i + 4] & 0xff; + + switch (cp[i + 5] & 0xff) { + case 0: + sprintf(gsdline, "\tMODNAME %s=%o flags=%o\n", name, value, flags); + break; + case 1: + sprintf(gsdline, "\tCSECT %s=%o flags=%o\n", name, value, flags); + break; + case 2: + sprintf(gsdline, "\tISD %s=%o flags=%o\n", name, value, flags); + break; + case 3: + sprintf(gsdline, "\tXFER %s=%o flags=%o\n", name, value, flags); + xferad = value; + break; + case 4: + sprintf(gsdline, "\tGLOBAL %s=%o %s%s%s %s flags=%o\n", name, value, + flags & 01 ? "WEAK " : "", + flags & 04 ? "LIB " : "", + flags & 010 ? "DEF" : "REF", + flags & 040 ? "REL" : "ABS", + flags); + break; + case 5: + sprintf(gsdline, "\tPSECT %s=%o %s%s %s %s %s %s %s flags=%o\n", name, value, + flags & 01 ? "SAV " : "", + flags & 02 ? "LIB " : "", + flags & 04 ? "OVR" : "CON", + flags & 020 ? "RO" : "RW", + flags & 040 ? "REL" : "ABS", + flags & 0100 ? "GBL" : "LCL", + flags & 0200 ? "D" : "I", + flags); + psects[psectid] = strdup(name); + trim(psects[psectid++]); + psectid %= NPSECTS; + 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; + case 010: + sprintf(gsdline, "\tCompletion Routine Name %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; + + (void)cp; + (void)len; + + if (nr_gsds == 0) { + return; + } + + 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); + all_gsds = NULL; + nr_gsds = 0; + gsdsize = 0; +} + +unsigned last_text_addr = 0; + +void got_text( + char *cp, + int len) +{ + unsigned addr = WORD(cp + 2); + + last_text_addr = addr; + + printf("TEXT ADDR=%o LEN=%o\n", last_text_addr, len - 4); + + dump_words(last_text_addr, cp + 4, len - 4); + + if (bin) + dump_bin(last_text_addr, cp + 4, len - 4); +} + +void rad50name( + char *cp, + char *name) +{ + unrad50(WORD(cp), name); + unrad50(WORD(cp + 2), name + 3); + name[6] = 0; + trim(name); +} + +void got_rld( + char *cp, + int len) +{ + int i; + + printf("RLD\n"); + + for (i = 2; i < len;) { + unsigned addr; + unsigned word; + unsigned disp = cp[i + 1] & 0xff; + char name[8]; + char *byte; + + addr = last_text_addr + disp - 4; + + byte = ""; + if (cp[i] & 0200) + byte = " byte"; + + switch (cp[i] & 0x7f) { + case 01: + printf("\tInternal%s %o=%o\n", byte, addr, WORD(cp + i + 2)); + i += 4; + break; + case 02: + rad50name(cp + i + 2, name); + printf("\tGlobal%s %o=%s\n", byte, addr, name); + i += 6; + break; + case 03: + printf("\tInternal displaced%s %o=%o\n", byte, addr, WORD(cp + i + 2)); + i += 4; + badbin = 1; + break; + case 04: + rad50name(cp + i + 2, name); + printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); + i += 6; + badbin = 1; + break; + case 05: + rad50name(cp + i + 2, name); + word = WORD(cp + i + 6); + printf("\tGlobal plus offset%s %o=%s+%o\n", byte, addr, name, word); + i += 8; + badbin = 1; + break; + case 06: + rad50name(cp + i + 2, name); + word = WORD(cp + i + 6); + printf("\tGlobal plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); + i += 8; + badbin = 1; + break; + case 07: + rad50name(cp + i + 2, name); + word = WORD(cp + i + 6); + printf("\tLocation counter definition %s+%o\n", name, word); + i += 8; + + last_text_addr = word; + break; + case 010: + word = WORD(cp + i + 2); + printf("\tLocation counter modification %o\n", word); + i += 4; + + last_text_addr = word; + break; + case 011: + printf("\t.LIMIT %o\n", addr); + i += 2; + break; + + case 012: + rad50name(cp + i + 2, name); + printf("\tPSECT%s %o=%s\n", byte, addr, name); + i += 6; + badbin = 1; + break; + case 014: + rad50name(cp + i + 2, name); + + printf("\tPSECT displaced%s %o=%s\n", byte, addr, name); + i += 6; + badbin = 1; + break; + case 015: + rad50name(cp + i + 2, name); + word = WORD(cp + i + 6); + printf("\tPSECT plus offset%s %o=%s+%o\n", byte, addr, name, word); + i += 8; + badbin = 1; + break; + case 016: + rad50name(cp + i + 2, name); + word = WORD(cp + i + 6); + printf("\tPSECT plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); + i += 8; + badbin = 1; + break; + + case 017: + badbin = 1; + printf("\tComplex%s %o=", byte, addr); + i += 2; { + char *xp = cp + i; + int size; + + for (;;) { + size = 1; + switch (*xp) { + case 000: + fputs("nop ", stdout); + break; + case 001: + fputs("+ ", stdout); + break; + case 002: + fputs("- ", stdout); + break; + case 003: + fputs("* ", stdout); + break; + case 004: + fputs("/ ", stdout); + break; + case 005: + fputs("& ", stdout); + break; + case 006: + fputs("! ", stdout); + break; + case 010: + fputs("neg ", stdout); + break; + case 011: + fputs("^C ", stdout); + break; + case 012: + fputs("store ", stdout); + break; + case 013: + fputs("store{disp} ", stdout); + break; + + case 016: + rad50name(xp + 1, name); + printf("%s ", name); + size = 5; + break; + + case 017: + assert((xp[1] & 0377) < psectid); + printf("%s:%o ", psects[xp[1] & 0377], WORD(xp + 2)); + size = 4; + break; + + case 020: + printf("%o ", WORD(xp + 1)); + size = 3; + break; + default: + printf("**UNKNOWN COMPLEX CODE** %o\n", *xp & 0377); + return; + } + i += size; + if (*xp == 012 || *xp == 013) + break; + xp += size; + } + fputc('\n', stdout); + break; + } + + default: + printf("\t***Unknown RLD code %o\n", cp[i] & 0xff); + return; + } + } +} + +void got_isd( + char *cp, + int len) +{ + (void)cp; + printf("ISD len=%o\n", len); +} + +void got_endmod( + char *cp, + int len) +{ + (void)cp; + (void)len; + printf("ENDMOD\n"); +} + +void got_libhdr( + char *cp, + int len) +{ + (void)cp; + (void)len; + printf("LIBHDR\n"); +} + +void got_libend( + char *cp, + int len) +{ + (void)cp; + (void)len; + printf("LIBEND\n"); +} + +int main( + int argc, + char *argv[]) +{ + int len; + FILE *fp; + char *cp; + + if (argc < 2) { + fprintf(stderr, "Usage: dumpobj input.obj [ output.obj ]\n"); + exit(1); + } + + fp = fopen(argv[1], "rb"); + if (fp == NULL) + return EXIT_FAILURE; + if (argc > 2 && 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 %o\n", cp[0] & 0xff); + break; + } + + free(cp); + } + + if (bin) { + dump_bin(xferad, NULL, 0); + fclose(bin); + if (badbin) + fprintf(stderr, "Probable errors in binary file\n"); + } + + fclose(fp); + return EXIT_SUCCESS; +} diff --git a/crossassemblers/macro11/dumpobj.dsp b/crossassemblers/macro11/dumpobj.dsp new file mode 100644 index 0000000..cbb76d1 --- /dev/null +++ b/crossassemblers/macro11/dumpobj.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="dumpobj" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dumpobj - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dumpobj.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dumpobj.mak" CFG="dumpobj - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dumpobj - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dumpobj - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/macro11", BAAAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dumpobj - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "dumpobj___Release" +# PROP Intermediate_Dir "dumpobj___Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "dumpobj - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "dumpobj___Debug" +# PROP Intermediate_Dir "dumpobj___Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "dumpobj - Win32 Release" +# Name "dumpobj - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\dumpobj.c +# End Source File +# Begin Source File + +SOURCE=.\Rad50.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\Rad50.h +# End Source File +# Begin Source File + +SOURCE=.\util.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/crossassemblers/macro11/extree.c b/crossassemblers/macro11/extree.c new file mode 100644 index 0000000..37970cc --- /dev/null +++ b/crossassemblers/macro11/extree.c @@ -0,0 +1,775 @@ +#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. */ + +void print_tree( + FILE *printfile, + EX_TREE *tp, + int depth) +{ + SYMBOL *sym; + + if (tp == NULL) { + fprintf(printfile, "(null)"); + return; + } + + 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); + break; + + case EX_LIT: + case EX_SYM: + break; + + case EX_COM: + case EX_NEG: + free_tree(tp->data.child.left); + break; + + case EX_ERR: + if (tp->data.child.left) + free_tree(tp->data.child.left); + 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); + break; + } + free(tp); +} + +/* 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; +} + +SYMBOL *dup_symbol( + SYMBOL *sym) +{ + SYMBOL *res; + + if (sym == NULL) { + return NULL; + } + + res = memcheck(malloc(sizeof(SYMBOL))); + res->label = memcheck(strdup(sym->label)); + res->flags = sym->flags; + res->stmtno = sym->stmtno; + res->next = NULL; + res->section = sym->section; + res->value = sym->value; + + return res; +} + + +EX_TREE *dup_tree( + EX_TREE *tp) +{ + EX_TREE *res = NULL; + + if (tp == NULL) { + return NULL; + } + + res = new_ex_tree(); + res->type = tp->type; + res->cp = tp->cp; + + switch (tp->type) { + case EX_UNDEFINED_SYM: + case EX_TEMP_SYM: + res->data.symbol = dup_symbol(tp->data.symbol); + break; + + /* The symbol reference in EX_SYM is not freed in free_tree() */ + case EX_SYM: + res->data.symbol = tp->data.symbol; + break; + + case EX_LIT: + res->data.lit = tp->data.lit; + break; + + case EX_COM: + case EX_NEG: + case EX_ERR: + res->data.child.left = dup_tree(tp->data.child.left); + break; + + case EX_ADD: + case EX_SUB: + case EX_MUL: + case EX_DIV: + case EX_AND: + case EX_OR: + res->data.child.left = dup_tree(tp->data.child.left); + res->data.child.right = dup_tree(tp->data.child.right); + break; + } + + return res; +} + +#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 = dup_tree(tp); + break; + } + + case EX_LIT: + res = dup_tree(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_COM; + 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_TEMP_SYM || + (tp->type == EX_SYM && + (tp->data.symbol->flags & SYMBOLFLAG_DEFINITION))) { + /* 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 = dup_tree(tp); + 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_bin(EX_ADD, left, 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_bin(EX_SUB, left, 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_bin(EX_MUL, left, 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_bin(EX_DIV, left, 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_bin(EX_AND, left, 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_bin(EX_OR, left, right); + } + break; + default: + fprintf(stderr, "Invalid tree\n"); + return NULL; + } + + 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; +} + +/* Create an EX_TREE representing a binary expression */ + +EX_TREE *new_ex_bin( + int type, + EX_TREE *left, + EX_TREE *right) +{ + EX_TREE *tp; + + tp = new_ex_tree(); + tp->type = type; + tp->data.child.left = left; + tp->data.child.right = right; + + return tp; +} + diff --git a/crossassemblers/macro11/extree.h b/crossassemblers/macro11/extree.h new file mode 100644 index 0000000..ee72a00 --- /dev/null +++ b/crossassemblers/macro11/extree.h @@ -0,0 +1,70 @@ + +#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 *new_ex_bin( + int type, + EX_TREE *left, + EX_TREE *right); + +EX_TREE *evaluate( + EX_TREE *tp, + int undef); + + +#endif diff --git a/crossassemblers/macro11/listing.c b/crossassemblers/macro11/listing.c new file mode 100644 index 0000000..00187b2 --- /dev/null +++ b/crossassemblers/macro11/listing.c @@ -0,0 +1,184 @@ +#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; + +int list_pass_0 = 0;/* Also list what happens during the first pass */ + + + +/* do_list returns TRUE if listing is enabled. */ + +static int dolist( + void) +{ + int ok = lstfile != NULL && + (pass > 0 || list_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", (int)SIZEOF_MEMBER(LSTFORMAT, flag), "", (int)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) +{ + size_t col1 = offsetof(LSTFORMAT, source); + size_t col2 = offsetof(LSTFORMAT, pc); + + if (strlen(binline) >= col1) { + list_flush(); + listline[0] = 0; + binline[0] = 0; + sprintf(binline, "%*s %6.6o", (int)offsetof(LSTFORMAT, pc), "", addr); + padto(binline, offsetof(LSTFORMAT, words)); + } else if (strlen(binline) <= col2) { + sprintf(binline, "%*s%*d %6.6o", (int)SIZEOF_MEMBER(LSTFORMAT, flag), "", + (int)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", (int)SIZEOF_MEMBER(LSTFORMAT, flag), "", + (int)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); + } +} + + +/* Print just a line with the address to the listing file */ + +void list_location( + STREAM *str, + unsigned addr) +{ + if (dolist()) { + list_fit(str, addr); + } +} + + + +/* reports errors */ +void report( + STREAM *str, + char *fmt, + ...) +{ + va_list ap; + char *name = "**"; + int line = 0; + + if (!pass && list_pass_0 < 2) + 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/crossassemblers/macro11/listing.h b/crossassemblers/macro11/listing.h new file mode 100644 index 0000000..184edd5 --- /dev/null +++ b/crossassemblers/macro11/listing.h @@ -0,0 +1,69 @@ + +#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 FILE *lstfile; + +extern int list_pass_0; /* Also list what happens during the first pass */ + +#endif + + +void list_word( + STREAM *str, + unsigned addr, + unsigned value, + int size, + char *flags); + +void list_value( + STREAM *str, + unsigned word); + +void list_location( + 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/crossassemblers/macro11/macro11.c b/crossassemblers/macro11/macro11.c new file mode 100644 index 0000000..ef50f88 --- /dev/null +++ b/crossassemblers/macro11/macro11.c @@ -0,0 +1,441 @@ +#define MACRO11__C + + +/* + 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 "macro11.h" + +#include "util.h" + +#include "assemble_globals.h" +#include "assemble.h" +#include "assemble_aux.h" +#include "listing.h" +#include "object.h" +#include "symbols.h" + +#define stricmp strcasecmp + + +/* 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; +} + +/*JH:*/ +static void print_version( + FILE *strm) +{ + 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"); + fprintf(strm, " modified 2015 by Olaf 'Rhialto' Seibert.\n"); +} + +static void append_env( + char *envname, + char *value) +{ + char *env = getenv(envname); + char *temp; + + if (env == NULL) + env = ""; + + temp = memcheck(malloc(strlen(envname) + + 1 + + strlen(env) + + 1 + + strlen(value) + + 1)); + strcpy(temp, envname); + strcat(temp, "="); + strcat(temp, env); + strcat(temp, PATHSEP); + strcat(temp, value); + putenv(temp); +} + +/*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