| /* |
| * $Id: macro1.c,v 1.73 2003/10/23 22:49:45 phil Exp $ |
| * |
| * TODO: |
| * have flex() use nextfiodec()?? (what if legal in repeat???) |
| * "flex<SP><SP><SP>x" should give right justified result??? |
| * squawk if nextfiodec called in a repeat w/ a delim? |
| * |
| * forbid variables/constants in macros |
| * forbid text in repeat?? |
| * forbid start in repeat or macro |
| * use same error TLA's as MACRO??? |
| * IPA error for overbar on LHS of = |
| * variables returns value?? |
| * |
| * macro addressing: labels defined during macro are local use only???? |
| * spacewar expects this??? (is it wrong?) |
| * |
| * self-feeding lines: \n legal anywhere \t is |
| * read next token into "token" buffer -- avoid saving "line"? |
| * remove crocks from "define" |
| * list title (first line of file) should not be parsed as source? |
| * incorrect listing for bare "start" |
| * list only 4 digits for address column |
| * |
| * other; |
| * note variables in symbol dump, xref? |
| * no "permenant" symbols; flush -p? rename .ini? |
| * keep seperate macro/pseudo table? |
| * verify bad input(?) detected |
| * implement dimension pseudo? |
| * remove crocks for '/' and ','? |
| */ |
| |
| /* |
| * Program: MACRO1 |
| * File: macro1.c |
| * Author: Gary A. Messenbrink <gary@netcom.com> (macro8) |
| * MACRO7 modifications: Bob Supnik <bob.supnik@ljo.dec.com> |
| * MACRO1 modifications: Bob Supnik <bob.supnik@ljo.dec.com> |
| * slashed to be more like real MACRO like by Phil Budne <phil@ultimate.com> |
| * |
| * Purpose: A 2 pass PDP-1 assembler |
| * |
| * NAME |
| * macro1 - a PDP-1 assembler. |
| * |
| * SYNOPSIS: |
| * macro1 [ -d -p -m -r -s -x ] inputfile inputfile... |
| * |
| * DESCRIPTION |
| * This is a cross-assembler to for PDP-1 assembly language programs. |
| * It will produce an output file in rim format only. |
| * A listing file is always produced and with an optional symbol table |
| * and/or a symbol cross-reference (concordance). The permanent symbol |
| * table can be output in a form that may be read back in so a customized |
| * permanent symbol table can be produced. Any detected errors are output |
| * to a separate file giving the filename in which they were detected |
| * along with the line number, column number and error message as well as |
| * marking the error in the listing file. |
| * The following file name extensions are used: |
| * .mac source code (input) |
| * .lst assembly listing (output) |
| * .rim assembly output in DEC's rim format (output) |
| * .prm permanent symbol table in form suitable for reading after |
| * the EXPUNGE pseudo-op. |
| * .sym "symbol punch" tape (for DDT, or reloading into macro) |
| * |
| * OPTIONS |
| * -d Dump the symbol table at end of assembly |
| * -p Generate a file with the permanent symbols in it. |
| * (To get the current symbol table, assemble a file than has only |
| * START in it.) |
| * -x Generate a cross-reference (concordance) of user symbols. |
| * -r Output a tape using only RIM format (else output block loader) |
| * -s Output a symbol dump tape (loader + loader blocks) |
| * -S file |
| * Read a symbol tape back in |
| * |
| * DIAGNOSTICS |
| * Assembler error diagnostics are output to an error file and inserted |
| * in the listing file. Each line in the error file has the form |
| * |
| * <filename>:<line>:<col> : error: <message> at Loc = <loc> |
| * |
| * An example error message is: |
| * |
| * bintst.7:17:9 : error: undefined symbol "UNDEF" at Loc = 07616 |
| * |
| * The error diagnostics put in the listing start with a two character |
| * error code (if appropriate) and a short message. A carat '^' is |
| * placed under the item in error if appropriate. |
| * An example error message is: |
| * |
| * 17 07616 3000 DAC UNDEF |
| * UD undefined ^ |
| * 18 07617 1777 TAD I DUMMY |
| * |
| * Undefined symbols are marked in the symbol table listing by prepending |
| * a '?' to the symbol. Redefined symbols are marked in the symbol table |
| * listing by prepending a '#' to the symbol. Examples are: |
| * |
| * #REDEF 04567 |
| * SWITCH 07612 |
| * ?UNDEF 00000 |
| * |
| * Refer to the code for the diagnostic messages generated. |
| * |
| * REFERENCES: |
| * This assembler is based on the pal assember by: |
| * Douglas Jones <jones@cs.uiowa.edu> and |
| * Rich Coon <coon@convexw.convex.com> |
| * |
| * COPYRIGHT NOTICE: |
| * This is free software. There is no fee for using it. You may make |
| * any changes that you wish and also give it away. If you can make |
| * a commercial product out of it, fine, but do not put any limits on |
| * the purchaser's right to do the same. If you improve it or fix any |
| * bugs, it would be nice if you told me and offered me a copy of the |
| * new version. |
| * |
| * |
| * Amendments Record: |
| * Version Date by Comments |
| * ------- ------- --- --------------------------------------------------- |
| * v1.0 12Apr96 GAM Original |
| * v1.1 18Nov96 GAM Permanent symbol table initialization error. |
| * v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. |
| * v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). |
| * v1.4 29Nov96 GAM Fixed bug in checksum generation. |
| * v2.1 08Dec96 GAM Added concordance processing (cross reference). |
| * v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). |
| * v2.3 2Feb97 GAM Fixed paging problem in cross reference output. |
| * v3.0 14Feb97 RMS MACRO8X features. |
| * ? RMS MACRO7 |
| * ? RMS MACRO1 released w/ lispswre |
| * ? RMS MACRO1 released w/ tools |
| * ? RMS MACRO1 released w/ ddt1 |
| * 2003 PLB major reworking |
| */ |
| |
| |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define LINELEN 96 |
| #define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ |
| #define NAMELEN 128 |
| #define SYMBOL_COLUMNS 5 |
| #define SYMLEN 7 |
| /*#define SYMSIG 4 /* EXP: significant chars in a symbol */ |
| #define SYMBOL_TABLE_SIZE 8192 |
| #define MAC_MAX_ARGS 20 |
| #define MAC_MAX_LENGTH 8192 |
| #define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ |
| |
| #define MAX_LITERALS 1000 |
| #define MAX_CONSTANTS 10 /* max number of "constants" blocks */ |
| |
| #define XREF_COLUMNS 8 |
| |
| #define ADDRESS_FIELD 0007777 |
| #define INDIRECT_BIT 0010000 |
| #define OP_CODE 0760000 |
| |
| #define CONCISE_LC 072 |
| #define CONCISE_UC 074 |
| |
| /* Macro to get the number of elements in an array. */ |
| #define DIM(a) (sizeof(a)/sizeof(a[0])) |
| |
| #define ISBLANK(c) ((c==' ') || (c=='\f')) |
| #define ISEND(c) ((c=='\0')|| (c=='\n') || (c == '\t')) |
| #define ISDONE(c) ((c=='/') || ISEND(c)) |
| |
| #define ISOVERBAR(c) (c == '\\' || c == '~') |
| |
| /* Macros for testing symbol attributes. Each macro evaluates to non-zero */ |
| /* (true) if the stated condtion is met. */ |
| /* Use these to test attributes. The proper bits are extracted and then */ |
| /* tested. */ |
| #define M_DEFINED(s) (((s) & DEFINED) == DEFINED) |
| #define M_DUPLICATE(s) (((s) & DUPLICATE) == DUPLICATE) |
| #define M_FIXED(s) (((s) & FIXED) == FIXED) |
| #define M_LABEL(s) (((s) & LABEL) == LABEL) |
| #define M_PSEUDO(s) (((s) & PSEUDO) == PSEUDO) |
| #define M_EPSEUDO(s) (((s) & EPSEUDO) == EPSEUDO) |
| #define M_MACRO(s) (((s) & MACRO) == MACRO) |
| #define M_NOTRDEF(s) (((s) & NOTRDEF) != 0) |
| |
| typedef unsigned char BOOL; |
| typedef unsigned char BYTE; |
| typedef int WORD32; |
| |
| #ifndef FALSE |
| #define FALSE 0 |
| #define TRUE (!FALSE) |
| #endif |
| |
| /* Line listing styles. Used to control listing of lines. */ |
| enum linestyle_t |
| { |
| LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL, LINE_LOC |
| }; |
| typedef enum linestyle_t LINESTYLE_T; |
| |
| /* Symbol Types. */ |
| /* Note that the names that have FIX as the suffix contain the FIXED bit */ |
| /* included in the value. */ |
| enum symtyp |
| { |
| UNDEFINED = 0000, |
| DEFINED = 0001, |
| FIXED = 0002, |
| LABEL = 0010 | DEFINED, |
| REDEFINED = 0020 | DEFINED, |
| DUPLICATE = 0040 | DEFINED, |
| PSEUDO = 0100 | FIXED | DEFINED, |
| EPSEUDO = 0200 | FIXED | DEFINED, |
| MACRO = 0400 | DEFINED, |
| DEFFIX = DEFINED | FIXED, |
| NOTRDEF = (MACRO | PSEUDO | LABEL | FIXED) & ~DEFINED |
| }; |
| typedef enum symtyp SYMTYP; |
| |
| enum pseudo_t { |
| DECIMAL, |
| DEFINE, |
| FLEX, |
| CONSTANTS, |
| OCTAL, |
| REPEAT, |
| START, |
| CHAR, |
| VARIABLES, |
| TEXT, |
| NOINPUT, |
| EXPUNGE |
| }; |
| typedef enum pseudo_t PSEUDO_T; |
| |
| struct sym_t |
| { |
| SYMTYP type; |
| char name[SYMLEN]; |
| WORD32 val; |
| WORD32 xref_index; |
| WORD32 xref_count; |
| }; |
| typedef struct sym_t SYM_T; |
| |
| struct emsg_t |
| { |
| char *list; |
| char *file; |
| }; |
| typedef struct emsg_t EMSG_T; |
| |
| struct errsave_t |
| { |
| char *mesg; |
| WORD32 col; |
| }; |
| typedef struct errsave_t ERRSAVE_T; |
| |
| /*----------------------------------------------------------------------------*/ |
| |
| /* Function Prototypes */ |
| |
| int binarySearch( char *name, int start, int symbol_count ); |
| int compareSymbols( const void *a, const void *b ); |
| SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); |
| SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); |
| void errorLexeme( EMSG_T *mesg, WORD32 col ); |
| void errorMessage( EMSG_T *mesg, WORD32 col ); |
| void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); |
| SYM_T eval( void ); |
| SYM_T *evalSymbol( void ); |
| void getArgs( int argc, char *argv[] ); |
| SYM_T getExpr( void ); |
| WORD32 getExprs( void ); |
| WORD32 incrementClc( void ); |
| WORD32 literal( WORD32 value ); |
| BOOL isLexSymbol(); |
| char *lexemeToName( char *name, WORD32 from, WORD32 term ); |
| void listLine( void ); |
| SYM_T *lookup( char *name, int type ); |
| void moveToEndOfLine( void ); |
| void next(int); |
| void onePass( void ); |
| void printCrossReference( void ); |
| void printErrorMessages( void ); |
| void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); |
| void printPageBreak( void ); |
| void printPermanentSymbolTable( void ); |
| void printSymbolTable( void ); |
| BOOL pseudo( PSEUDO_T val ); |
| void punchLocObject( WORD32 loc, WORD32 val ); |
| void punchOutObject( WORD32 loc, WORD32 val ); |
| void punchLeader( WORD32 count ); |
| void punchLoader( void ); |
| void flushLoader( void ); |
| void readLine( void ); |
| void saveError( char *mesg, WORD32 cc ); |
| void topOfForm( char *title, char *sub_title ); |
| void constants(void); |
| void variables(void); |
| void eob(void); |
| void dump_symbols(void); |
| |
| /*----------------------------------------------------------------------------*/ |
| |
| /* Table of pseudo-ops (directives) which are used to setup the symbol */ |
| /* table on startup */ |
| SYM_T pseudos[] = |
| { |
| { PSEUDO, "consta", CONSTANTS }, |
| { PSEUDO, "define", DEFINE }, /* Define macro. */ |
| { PSEUDO, "repeat", REPEAT }, |
| { PSEUDO, "start", START }, /* Set starting address. */ |
| { PSEUDO, "variab", VARIABLES }, |
| { PSEUDO, "text", TEXT }, |
| { PSEUDO, "noinpu", NOINPUT }, |
| { PSEUDO, "expung", EXPUNGE }, |
| /* the following can appear in expressions: */ |
| { EPSEUDO, "charac", CHAR }, |
| { EPSEUDO, "decima", DECIMAL }, /* base 10. */ |
| { EPSEUDO, "flexo", FLEX }, |
| { EPSEUDO, "octal", OCTAL }, /* Read literal constants in base 8. */ |
| }; |
| |
| /* Symbol Table */ |
| /* The table is put in lexical order on startup, so symbols can be */ |
| /* inserted as desired into the initial table. */ |
| #define DIO 0320000 |
| #define JMP 0600000 |
| SYM_T permanent_symbols[] = |
| { |
| /* Memory Reference Instructions */ |
| { DEFFIX, "and", 0020000 }, |
| { DEFFIX, "ior", 0040000 }, |
| { DEFFIX, "xor", 0060000 }, |
| { DEFFIX, "xct", 0100000 }, |
| { DEFFIX, "lac", 0200000 }, |
| { DEFFIX, "lio", 0220000 }, |
| { DEFFIX, "dac", 0240000 }, |
| { DEFFIX, "dap", 0260000 }, |
| { DEFFIX, "dip", 0300000 }, |
| { DEFFIX, "dio", 0320000 }, |
| { DEFFIX, "dzm", 0340000 }, |
| { DEFFIX, "add", 0400000 }, |
| { DEFFIX, "sub", 0420000 }, |
| { DEFFIX, "idx", 0440000 }, |
| { DEFFIX, "isp", 0460000 }, |
| { DEFFIX, "sad", 0500000 }, |
| { DEFFIX, "sas", 0520000 }, |
| { DEFFIX, "mul", 0540000 }, |
| { DEFFIX, "mus", 0540000 }, /* for spacewar */ |
| { DEFFIX, "div", 0560000 }, |
| { DEFFIX, "dis", 0560000 }, /* for spacewar */ |
| { DEFFIX, "jmp", 0600000 }, |
| { DEFFIX, "jsp", 0620000 }, |
| { DEFFIX, "skip", 0640000 }, /* for spacewar */ |
| { DEFFIX, "cal", 0160000 }, |
| { DEFFIX, "jda", 0170000 }, |
| { DEFFIX, "i", 0010000 }, |
| { DEFFIX, "skp", 0640000 }, |
| { DEFFIX, "law", 0700000 }, |
| { DEFFIX, "iot", 0720000 }, |
| { DEFFIX, "opr", 0760000 }, |
| { DEFFIX, "nop", 0760000 }, |
| /* Shift Instructions */ |
| { DEFFIX, "ral", 0661000 }, |
| { DEFFIX, "ril", 0662000 }, |
| { DEFFIX, "rcl", 0663000 }, |
| { DEFFIX, "sal", 0665000 }, |
| { DEFFIX, "sil", 0666000 }, |
| { DEFFIX, "scl", 0667000 }, |
| { DEFFIX, "rar", 0671000 }, |
| { DEFFIX, "rir", 0672000 }, |
| { DEFFIX, "rcr", 0673000 }, |
| { DEFFIX, "sar", 0675000 }, |
| { DEFFIX, "sir", 0676000 }, |
| { DEFFIX, "scr", 0677000 }, |
| { DEFFIX, "1s", 0000001 }, |
| { DEFFIX, "2s", 0000003 }, |
| { DEFFIX, "3s", 0000007 }, |
| { DEFFIX, "4s", 0000017 }, |
| { DEFFIX, "5s", 0000037 }, |
| { DEFFIX, "6s", 0000077 }, |
| { DEFFIX, "7s", 0000177 }, |
| { DEFFIX, "8s", 0000377 }, |
| { DEFFIX, "9s", 0000777 }, |
| /* Skip Microinstructions */ |
| { DEFFIX, "sza", 0640100 }, |
| { DEFFIX, "spa", 0640200 }, |
| { DEFFIX, "sma", 0640400 }, |
| { DEFFIX, "szo", 0641000 }, |
| { DEFFIX, "spi", 0642000 }, |
| { DEFFIX, "szs", 0640000 }, |
| { DEFFIX, "szf", 0640000 }, |
| /*{ DEFFIX, "clo", 0651600 },*/ |
| |
| /* Operate Microinstructions */ |
| { DEFFIX, "clf", 0760000 }, |
| { DEFFIX, "stf", 0760010 }, |
| { DEFFIX, "cla", 0760200 }, |
| /*{ DEFFIX, "lap", 0760300 },*/ |
| { DEFFIX, "hlt", 0760400 }, |
| { DEFFIX, "xx", 0760400 }, |
| { DEFFIX, "cma", 0761000 }, |
| { DEFFIX, "clc", 0761200 }, |
| { DEFFIX, "lat", 0762200 }, |
| { DEFFIX, "cli", 0764000 }, |
| /* IOT's */ |
| /*{ DEFFIX, "ioh", 0730000 },*/ |
| { DEFFIX, "rpa", 0730001 }, |
| { DEFFIX, "rpb", 0730002 }, |
| { DEFFIX, "rrb", 0720030 }, |
| { DEFFIX, "ppa", 0730005 }, |
| { DEFFIX, "ppb", 0730006 }, |
| { DEFFIX, "tyo", 0730003 }, |
| { DEFFIX, "tyi", 0720004 }, |
| { DEFFIX, "dpy", 0730007 }, /* for spacewar, munching squares! */ |
| { DEFFIX, "lsm", 0720054 }, |
| { DEFFIX, "esm", 0720055 }, |
| { DEFFIX, "cbs", 0720056 }, |
| { DEFFIX, "lem", 0720074 }, |
| { DEFFIX, "eem", 0724074 }, |
| { DEFFIX, "cks", 0720033 }, |
| }; /* End-of-Symbols for Permanent Symbol Table */ |
| |
| /* Global variables */ |
| SYM_T *symtab; /* Symbol Table */ |
| int symbol_top; /* Number of entries in symbol table. */ |
| |
| #define LOADERBASE 07751 |
| |
| /* make it relocatable (DDT expects it at 7751) */ |
| #define LOADER_IN LOADERBASE |
| #define LOADER_B (LOADERBASE+06) |
| #define LOADER_A (LOADERBASE+07) |
| #define LOADER_CK (LOADERBASE+025) |
| #define LOADER_EN1 (LOADERBASE+026) |
| |
| WORD32 loader[] = { |
| 0730002, /* in, rpb */ |
| 0320000+LOADER_A, /* dio a */ |
| 0100000+LOADER_A, /* xct a */ |
| 0320000+LOADER_CK, /* dio ck */ |
| 0730002, /* rpb */ |
| 0320000+LOADER_EN1, /* dio en1 */ |
| 0730002, /* b, rpb */ |
| 0000000, /* a, xx */ |
| 0210000+LOADER_A, /* lac i a */ |
| 0400000+LOADER_CK, /* add ck */ |
| 0240000+LOADER_CK, /* dac ck */ |
| 0440000+LOADER_A, /* idx a */ |
| 0520000+LOADER_EN1, /* sas en1 */ |
| 0600000+LOADER_B, /* jmp b */ |
| 0200000+LOADER_CK, /* lac ck */ |
| 0400000+LOADER_EN1, /* add en1 */ |
| 0730002, /* rpb */ |
| 0320000+LOADER_CK, /* dio ck */ |
| 0520000+LOADER_CK, /* sas ck */ |
| 0760400, /* hlt */ |
| 0600000+LOADER_IN /* jmp in */ |
| /* ck, 0 */ |
| /* en1, 0 */ |
| }; |
| |
| #define LOADERBUFSIZE 0100 /* <=0100, power of 2*/ |
| #define LOADERBUFMASK (LOADERBUFSIZE-1) /* for block alignment */ |
| |
| WORD32 loaderbuf[LOADERBUFSIZE]; |
| WORD32 loaderbufcount; |
| WORD32 loaderbufstart; |
| |
| /*----------------------------------------------------------------------------*/ |
| |
| WORD32 *xreftab; /* Start of the concordance table. */ |
| |
| ERRSAVE_T error_list[20]; |
| int save_error_count; |
| |
| char s_detected[] = "detected"; |
| char s_error[] = "error"; |
| char s_errors[] = "errors"; |
| char s_no[] = "No"; |
| char s_page[] = "Page"; |
| char s_symtable[] = "Symbol Table"; |
| char s_xref[] = "Cross Reference"; |
| |
| /* Assembler diagnostic messages. */ |
| /* Some attempt has been made to keep continuity with the PAL-III and */ |
| /* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ |
| /* exists, then the indicator is put in the listing as the first two */ |
| /* characters of the diagnostic message. The PAL-III indicators where used */ |
| /* when there was a choice between using MACRO-8 and PAL-III indicators. */ |
| /* The character pairs and their meanings are: */ |
| /* DT Duplicate Tag (symbol) */ |
| /* IC Illegal Character */ |
| /* ID Illegal Redefinition of a symbol. An attempt was made to give */ |
| /* a symbol a new value not via =. */ |
| /* IE Illegal Equals An equal sign was used in the wrong context, */ |
| /* (e.g., A+B=C, or TAD A+=B) */ |
| /* II Illegal Indirect An off page reference was made, but a literal */ |
| /* could not be generated because the indirect bit was already set. */ |
| /* IR Illegal Reference (address is not on current page or page zero) */ |
| /* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ |
| /* RD ReDefintion of a symbol */ |
| /* ST Symbol Table full */ |
| /* UA Undefined Address (undefined symbol) */ |
| /* VR Value Required */ |
| /* ZE Zero Page Exceeded (see above, or out of space) */ |
| EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; |
| EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; |
| EMSG_T illegal_character = { "IC illegal char", "illegal character" }; |
| EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; |
| EMSG_T label_syntax = { "IC label syntax", "label syntax" }; |
| EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; |
| EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; |
| EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; |
| EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; |
| EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; |
| EMSG_T illegal_reference = { "IR off page", "illegal reference" }; |
| EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; |
| EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; |
| EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; |
| EMSG_T value_required = { "VR value required", "value required" }; |
| EMSG_T literal_gen_off = { "lit generation off", |
| "literal generation disabled" }; |
| EMSG_T literal_overflow = { "PE page exceeded", |
| "current page literal capacity exceeded" }; |
| EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; |
| EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; |
| EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; |
| EMSG_T illegal_vfd_value = { "width out of range", |
| "VFD field width not in range" }; |
| EMSG_T no_literal_value = { "no value", "No literal value" }; |
| EMSG_T text_string = { "no delimiter", |
| "Text string delimiters not matched" }; |
| EMSG_T lt_expected = { "'<' expected", "'<' expected" }; |
| EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; |
| EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; |
| EMSG_T bad_dummy_arg = { "bad dummy arg", |
| "Bad dummy argument following DEFINE" }; |
| EMSG_T macro_too_long = { "macro too long", "Macro too long" }; |
| EMSG_T no_virtual_memory = { "out of memory", |
| "Insufficient memory for macro" }; |
| EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; |
| EMSG_T define_in_repeat = { "define in a repeat", "Define in a repeat" }; |
| |
| /*----------------------------------------------------------------------------*/ |
| |
| FILE *errorfile; |
| FILE *infile; |
| FILE *listfile; |
| FILE *listsave; |
| FILE *objectfile; |
| FILE *objectsave; |
| |
| char filename[NAMELEN]; |
| char listpathname[NAMELEN]; |
| char sympathname[NAMELEN]; |
| char objectpathname[NAMELEN]; |
| char *pathname; |
| char permpathname[NAMELEN]; |
| |
| WORD32 mac_count; /* Total macros defined. */ |
| |
| /* |
| * malloced macro bodies, indexed by sym->val dummies are evaluated at |
| * invocation time, and value saved in "args"; if recursive macros are |
| * desired (without conditionals, how would you escape?), keep a name |
| * list here and move symbols to "macinv" |
| */ |
| struct macdef { |
| int nargs; /* number of args */ |
| SYM_T args[MAC_MAX_ARGS+1]; /* symbol for each and one for "r" */ |
| char body[1]; /* malloc'ed accordingly */ |
| } *mac_defs[MAC_TABLE_LENGTH]; |
| |
| struct macinv { /* current macro invocation */ |
| char mac_line[LINELEN]; /* Saved macro invocation line. */ |
| WORD32 mac_cc; /* Saved cc after macro invocation. */ |
| char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ |
| struct macdef *defn; /* pointer to definition for dummies */ |
| struct macinv *prev; /* previous invocation in stack */ |
| } *curmacro; /* macro stack */ |
| |
| int nrepeats; /* count of nested repeats */ |
| |
| int list_lineno; |
| int list_pageno; |
| char list_title[LINELEN]; |
| BOOL list_title_set; /* Set if TITLE pseudo-op used. */ |
| char line[LINELEN]; /* Input line. */ |
| int lineno; /* Current line number. */ |
| int page_lineno; /* print line number on current page. */ |
| WORD32 listed; /* Listed flag. */ |
| WORD32 listedsave; |
| |
| WORD32 cc; /* Column Counter (char position in line). */ |
| WORD32 clc; /* Location counter */ |
| BOOL end_of_input; /* End of all input files. */ |
| int errors; /* Number of errors found so far. */ |
| BOOL error_in_line; /* TRUE if error on current line. */ |
| int errors_pass_1; /* Number of errors on pass 1. */ |
| int filix_curr; /* Index in argv to current input file. */ |
| int filix_start; /* Start of input files in argv. */ |
| int lexstartprev; /* Where previous lexeme started. */ |
| int lextermprev; /* Where previous lexeme ended. */ |
| int lexstart; /* Index of current lexeme on line. */ |
| int lexterm; /* Index of character after current lexeme. */ |
| int overbar; /* next saw an overbar in last token */ |
| |
| int nconst; /* number of "constants" blocks */ |
| int lit_count[MAX_CONSTANTS]; /* # of lits in each block in pass 1 */ |
| WORD32 lit_loc[MAX_CONSTANTS]; /* Base of literal blocks */ |
| |
| int noinput; /* don't punch loader */ |
| |
| int nvars; /* number of variables */ |
| WORD32 vars_addr; /* address of "variables" */ |
| WORD32 vars_end; /* end of "variables" */ |
| |
| /* pass 2 only; */ |
| int nlit; /* number of literals in litter[] */ |
| WORD32 litter[MAX_LITERALS]; /* literals */ |
| |
| WORD32 maxcc; /* Current line length. */ |
| BOOL nomac_exp; /* No macro expansion */ |
| WORD32 pass; /* Number of current pass. */ |
| BOOL print_permanent_symbols; |
| WORD32 radix; /* Default number radix. */ |
| BOOL rim_mode; /* RIM mode output. */ |
| BOOL sym_dump; /* punch symbol tape */ |
| int save_argc; /* Saved argc. */ |
| char **save_argv; /* Saved *argv[]. */ |
| WORD32 start_addr; /* Saved start address. */ |
| BOOL symtab_print; /* Print symbol table flag */ |
| BOOL xref; |
| |
| SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ |
| |
| /* initial data from SIMH v3.0 pdp1_stddev.c (different encoding of UC/LC) */ |
| #define UC 0100 /* Upper case */ |
| #define LC 0200 |
| #define CHARBITS 077 |
| #define BC LC|UC /* both case bits */ |
| #define BAD 014 /* unused concise code */ |
| |
| unsigned char ascii_to_fiodec[128] = { |
| BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, |
| BC|075, BC|036, BAD, BAD, BAD, BC|077, BAD, BAD, |
| BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, |
| BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, |
| BC|000, UC|005, UC|001, UC|004, BAD, BAD, UC|006, UC|002, |
| LC|057, LC|055, UC|073, UC|054, LC|033, LC|054, LC|073, LC|021, |
| LC|020, LC|001, LC|002, LC|003, LC|004, LC|005, LC|006, LC|007, |
| LC|010, LC|011, BAD, BAD, UC|007, UC|033, UC|010, UC|021, |
| LC|040, UC|061, UC|062, UC|063, UC|064, UC|065, UC|066, UC|067, |
| UC|070, UC|071, UC|041, UC|042, UC|043, UC|044, UC|045, UC|046, |
| UC|047, UC|050, UC|051, UC|022, UC|023, UC|024, UC|025, UC|026, |
| UC|027, UC|030, UC|031, UC|057, LC|056, UC|055, UC|011, UC|040, |
| UC|020, LC|061, LC|062, LC|063, LC|064, LC|065, LC|066, LC|067, |
| LC|070, LC|071, LC|041, LC|042, LC|043, LC|044, LC|045, LC|046, |
| LC|047, LC|050, LC|051, LC|022, LC|023, LC|024, LC|025, LC|026, |
| LC|027, LC|030, LC|031, BAD, UC|056, BAD, UC|003, BC|075 |
| }; |
| |
| /* for symbol punch tape conversion only!! */ |
| char fiodec_to_ascii[64] = { |
| 0, '1', '2', '3', '4', '5', '6', '7', |
| '8', '9', 0, 0, 0, 0, 0, 0, |
| '0', 0, 's', 't', 'u', 'v', 'w', 'x', |
| 'y', 'z', 0, 0, 0, 0, 0, 0, |
| 0, 'j', 'k', 'l', 'm', 'n', 'o', 'p', |
| 'q', 'r', 0, 0, 0, 0, 0, 0, |
| 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', |
| 'h', 'i', 0, 0, 0, 0, 0, 0 }; |
| |
| /* used at startup & for expunge */ |
| void |
| init_symtab(void) { |
| /* Place end marker in symbol table. */ |
| symtab[0] = sym_undefined; |
| symbol_top = 0; |
| } |
| |
| /* Function: main */ |
| /* Synopsis: Starting point. Controls order of assembly. */ |
| int |
| main( int argc, char *argv[] ) |
| { |
| int ix; |
| int space; |
| |
| save_argc = argc; |
| save_argv = argv; |
| |
| /* Set the default values for global symbols. */ |
| print_permanent_symbols = FALSE; |
| nomac_exp = TRUE; |
| rim_mode = FALSE; /* default to loader tapes */ |
| sym_dump = FALSE; |
| noinput = FALSE; |
| |
| symtab_print = FALSE; |
| xref = FALSE; |
| pathname = NULL; |
| |
| /* init symbol table before processing arguments, so we can |
| * load symbol punch tapes on the fly |
| */ |
| |
| /* |
| * Setup the error file in case symbol table overflows while |
| * installing the permanent symbols. |
| */ |
| errorfile = stderr; |
| pass = 0; /* required for symbol table init */ |
| symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); |
| |
| if( symtab == NULL ) { |
| fprintf( stderr, "Could not allocate memory for symbol table.\n"); |
| exit( -1 ); |
| } |
| |
| init_symtab(); |
| |
| /* Enter the pseudo-ops into the symbol table */ |
| for( ix = 0; ix < DIM( pseudos ); ix++ ) |
| defineSymbol( pseudos[ix].name, pseudos[ix].val, pseudos[ix].type, 0 ); |
| |
| /* Enter the predefined symbols into the table. */ |
| /* Also make them part of the permanent symbol table. */ |
| for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) |
| defineSymbol( permanent_symbols[ix].name, |
| permanent_symbols[ix].val, |
| permanent_symbols[ix].type, 0 ); |
| |
| /* Get the options and pathnames */ |
| getArgs( argc, argv ); |
| |
| /* Do pass one of the assembly */ |
| pass = 1; |
| onePass(); |
| errors_pass_1 = errors; |
| |
| /* Set up for pass two */ |
| objectfile = fopen( objectpathname, "wb" ); |
| objectsave = objectfile; |
| |
| listfile = fopen( listpathname, "w" ); |
| listsave = listfile; |
| |
| /* XXX punch title into tape! */ |
| punchLeader( 0 ); |
| if (!rim_mode) { |
| punchLoader(); |
| punchLeader(5); |
| } |
| |
| if (nlit > 0) |
| constants(); /* implied "constants"? */ |
| |
| /* Do pass two of the assembly */ |
| errors = 0; |
| save_error_count = 0; |
| |
| if( xref ) { |
| /* Get the amount of space that will be required for the concordance */ |
| for( space = 0, ix = 0; ix < symbol_top; ix++ ) { |
| symtab[ix].xref_index = space; /* Index into concordance table. */ |
| space += symtab[ix].xref_count + 1; |
| symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ |
| } |
| /* Allocate & clear the necessary space. */ |
| xreftab = (WORD32 *) calloc( space, sizeof( WORD32 )); |
| } |
| pass = 2; |
| onePass(); |
| |
| objectfile = objectsave; |
| |
| /* Works great for trailer. */ |
| punchLeader( 1 ); |
| |
| /* undo effects of NOLIST for any following output to listing file. */ |
| listfile = listsave; |
| |
| /* Display value of error counter. */ |
| if( errors == 0 ) { |
| fprintf( listfile, "\n %s %s %s\n", s_no, s_errors, s_detected ); |
| } |
| else { |
| fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, |
| ( errors == 1 ? s_error : s_errors )); |
| fprintf( listfile, "\n %d %s %s\n", errors, s_detected, |
| ( errors == 1 ? s_error : s_errors )); |
| } |
| |
| if( symtab_print ) |
| printSymbolTable(); |
| |
| if( print_permanent_symbols ) |
| printPermanentSymbolTable(); |
| |
| if( xref ) |
| printCrossReference(); |
| |
| fclose( objectfile ); |
| fclose( listfile ); |
| if( errors == 0 && errors_pass_1 == 0 ) { |
| /* after closing objectfile -- we reuse the FILE *!! */ |
| if (sym_dump) |
| dump_symbols(); |
| } |
| else |
| remove( objectpathname ); |
| |
| return( errors != 0 ); |
| } /* main() */ |
| |
| /* read a word from a binary punch file */ |
| WORD32 |
| getw(FILE *f) |
| { |
| int i, c; |
| WORD32 w; |
| |
| w = 0; |
| for (i = 0; i < 3;) { |
| c = getc(f); |
| if (c == -1) |
| return -1; |
| if (c & 0200) { /* ignore if ch8 not punched */ |
| w <<= 6; |
| w |= c & 077; |
| i++; |
| } |
| } |
| return w; |
| } |
| |
| /* |
| * "permute zone bits" like MACRO does for proper sorting |
| * (see routine "per" in MACRO) -- it's what DDT expects |
| * |
| * it's it's own inverse! |
| */ |
| |
| WORD32 |
| permute(WORD32 name) |
| { |
| WORD32 temp; |
| |
| temp = name & 0202020; /* get zone bits */ |
| temp = ((temp << 1) & 0777777) | ((temp >> 17) & 1); /* rotate left */ |
| name ^= temp; /* flip zone bits */ |
| name ^= 0400000; /* toggle sign */ |
| return name; |
| } |
| |
| /* add a symbol from a "symbol punch" tape */ |
| void |
| addsym(WORD32 sym, WORD32 val) |
| { |
| char name[4]; |
| |
| sym = permute(sym); |
| name[0] = fiodec_to_ascii[(sym >>12) & 077]; |
| name[1] = fiodec_to_ascii[(sym >> 6) & 077]; |
| name[2] = fiodec_to_ascii[sym & 077]; |
| name[3] = '\0'; |
| defineSymbol( name, val, LABEL, 0); |
| } |
| |
| void |
| read_symbols(char *fname) |
| { |
| FILE *f; |
| |
| f = fopen(fname, "rb"); |
| if (!f) { |
| perror(fname); |
| exit(1); |
| } |
| |
| /* skip loader */ |
| for (;;) { |
| WORD32 w; |
| |
| w = getw(f); |
| if (w == -1) |
| goto err; /* XXX complain? */ |
| if ((w & OP_CODE) == JMP) |
| break; |
| if ((w & OP_CODE) != DIO) |
| goto err; /* XXX complain? */ |
| w = getw(f); |
| if (w == -1) |
| goto err; /* XXX complain? */ |
| } |
| |
| |
| /* XXX should push block reader down into a co-routine */ |
| for (;;) { |
| WORD32 start, end, sum; |
| |
| start = getw(f); |
| if ((start & OP_CODE) == JMP) { |
| fclose(f); |
| return; |
| } |
| |
| if (start == -1 || (start & OP_CODE) != DIO) |
| goto err; |
| |
| end = getw(f); |
| if (end == -1 || (end & OP_CODE) != DIO) |
| goto err; /* XXX complain? */ |
| |
| sum = start + end; |
| while (start < end) { |
| WORD32 sym, val; |
| sym = getw(f); |
| if (sym == -1) |
| goto err; |
| sum += sym; |
| start++; |
| /* XXX handle block boundaries? */ |
| if (start >= end) |
| goto err; |
| val = getw(f); |
| if (val == -1) |
| goto err; |
| /*printf("%06o %06o\n", sym, val);*/ |
| addsym(sym, val); |
| sum += val; |
| start++; |
| } |
| start = getw(f); /* eat checksum XXX verify? */ |
| if (start == -1) |
| goto err; |
| /* roll over all the overflows at once */ |
| if (sum & ~0777777) { |
| sum = (sum & 0777777) + (sum >> 18); |
| if (sum & 01000000) /* one more time */ |
| sum++; |
| } |
| if (start != sum) |
| goto err; |
| } |
| err: |
| fprintf(stderr, "error reading symbol file %s\n", fname); |
| exit(1); |
| } |
| |
| /* Function: getArgs */ |
| /* Synopsis: Parse command line, set flags accordingly and setup input and */ |
| /* output files. */ |
| void getArgs( int argc, char *argv[] ) |
| { |
| WORD32 len; |
| WORD32 ix, jx; |
| |
| /* Set the defaults */ |
| infile = NULL; |
| listfile = NULL; |
| listsave = NULL; |
| objectfile = NULL; |
| objectsave = NULL; |
| |
| for( ix = 1; ix < argc; ) |
| { |
| if( argv[ix][0] == '-' ) |
| { |
| char *switches = argv[ix++]; |
| for( jx = 1; switches[jx] != 0; jx++ ) |
| { |
| switch( switches[jx] ) |
| { |
| case 'd': |
| symtab_print = TRUE; |
| break; |
| |
| case 'r': |
| rim_mode = TRUE; /* punch pure rim-mode tapes */ |
| break; |
| |
| case 's': |
| sym_dump = TRUE; |
| break; |
| |
| case 'm': |
| nomac_exp = FALSE; |
| break; |
| |
| case 'p': |
| print_permanent_symbols = TRUE; |
| break; |
| |
| case 'x': |
| xref = TRUE; |
| break; |
| |
| case 'S': |
| if (ix <= argc) |
| read_symbols(argv[ix++]); |
| break; |
| |
| default: |
| fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); |
| fprintf( stderr, " -d -- dump symbol table\n" ); |
| fprintf( stderr, " -m -- output macro expansions\n" ); |
| fprintf( stderr, " -p -- output permanent symbols to file\n" ); |
| fprintf( stderr, " -r -- output RIM format file\n" ); |
| fprintf( stderr, " -s -- output symbol punch tape to file\n" ); |
| fprintf( stderr, " -S file -- read symbol punch tape\n" ); |
| fprintf( stderr, " -x -- output cross reference to file\n" ); |
| fflush( stderr ); |
| exit( -1 ); |
| } /* end switch */ |
| } /* end for */ |
| } |
| else |
| { |
| filix_start = ix; |
| pathname = argv[ix]; |
| break; |
| } |
| } /* end for */ |
| |
| if( pathname == NULL ) |
| { |
| fprintf( stderr, "%s: no input file specified\n", argv[0] ); |
| exit( -1 ); |
| } |
| |
| len = strlen( pathname ); |
| if( len > NAMELEN - 5 ) |
| { |
| fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); |
| exit( -1 ); |
| } |
| |
| /* Now make the pathnames */ |
| /* Find last '.', if it exists. */ |
| jx = len - 1; |
| while( pathname[jx] != '.' && pathname[jx] != '/' |
| && pathname[jx] != '\\' && jx >= 0 ) |
| { |
| jx--; |
| } |
| |
| switch( pathname[jx] ) |
| { |
| case '.': |
| break; |
| |
| case '/': |
| case '\\': |
| jx = len; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Add the pathname extensions. */ |
| strncpy( objectpathname, pathname, jx ); |
| objectpathname[jx] = '\0'; |
| strcat( objectpathname, ".rim"); |
| |
| strncpy( listpathname, pathname, jx ); |
| listpathname[jx] = '\0'; |
| strcat( listpathname, ".lst" ); |
| |
| strncpy( permpathname, pathname, jx ); |
| permpathname[jx] = '\0'; |
| strcat( permpathname, ".prm" ); |
| |
| strncpy( sympathname, pathname, jx ); |
| sympathname[jx] = '\0'; |
| strcat( sympathname, ".sym" ); |
| |
| /* Extract the filename from the path. */ |
| if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) |
| pathname[1] = '\\'; /* MS-DOS style pathname */ |
| |
| jx = len - 1; |
| while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) |
| jx--; |
| strcpy( filename, &pathname[jx + 1] ); |
| } /* getArgs() */ |
| |
| |
| int |
| invokeMacro(int index) |
| { |
| struct macinv *mip; |
| struct macdef *mdp; |
| int jx; |
| |
| mdp = mac_defs[index]; |
| if (mdp == NULL || mdp->body[0] == '\0') |
| return 0; |
| |
| /* Find arguments. */ |
| while (ISBLANK(line[lexstart])) |
| next(0); |
| |
| mip = calloc(1, sizeof(struct macinv)); |
| if (!mip) { |
| fprintf(stderr, "could not allocate memory for macro invocation\n"); |
| exit(1); |
| } |
| mip->defn = mdp; |
| |
| /* evaluate args, saving values in SYM_T entries in defn. |
| * (cannot have recursive macros) |
| */ |
| mdp->args[0].val = clc; /* r is location at start */ |
| for( jx = 1; !ISDONE(line[lexstart]) && jx <= MAC_MAX_ARGS; ) { |
| WORD32 val; |
| |
| next(0); |
| if (ISDONE(line[lexstart])) |
| break; |
| |
| if (line[lexstart] == ',') |
| next(0); |
| |
| while( ISBLANK( line[lexstart] )) |
| next(0); |
| |
| if (ISDONE(line[lexstart])) |
| break; |
| |
| val = getExprs(); |
| |
| /* ignore excess values silently? */ |
| if (jx <= mdp->nargs) |
| mdp->args[jx].val = val; |
| jx++; |
| } /* end for */ |
| |
| /* XXX complain if too few actuals? -- nah */ |
| while (jx <= mdp->nargs) |
| mdp->args[jx++].val = 0; |
| |
| strcpy(mip->mac_line, line); /* save line */ |
| mip->mac_cc = cc; /* save position in line */ |
| mip->mac_ptr = mdp->body; |
| mip->prev = curmacro; /* push the old entry */ |
| curmacro = mip; /* step up to the plate! */ |
| return 1; |
| } |
| |
| /* process input; used by onePass and repeat */ |
| void |
| processLine() { |
| if (!list_title_set) { |
| char *cp; |
| |
| /* assert(sizeof(title) >= sizeof(line)); */ |
| strcpy(list_title, line); |
| |
| if ((cp = strchr(list_title, '\n'))) |
| *cp = '\0'; |
| |
| if (list_title[0]) { |
| list_title_set = TRUE; |
| fprintf(stderr, "%s - pass %d\n", list_title, pass ); |
| /* XXX punch title into tape banner (until an '@' seen) */ |
| } |
| return; |
| } |
| |
| for (;;) { |
| int jx; |
| SYM_T evalue; |
| |
| next(0); |
| if( end_of_input ) |
| return; |
| |
| if( ISEND( line[lexstart] )) { |
| if (line[lexstart] != '\t') |
| return; |
| continue; |
| } |
| if (line[lexstart] == '/') /* comment? */ |
| return; /* done */ |
| |
| /* look ahead for 'exp/' */ |
| /* skip until whitespace or terminator */ |
| for( jx = lexstart; jx < maxcc; jx++ ) |
| if( ISBLANK(line[jx]) || ISDONE(line[jx])) |
| break; |
| if( line[jx] == '/') { /* EXP/ set location */ |
| WORD32 newclc; |
| |
| newclc = getExprs(); |
| |
| /* Do not change Current Location Counter if an error occurred. */ |
| if( !error_in_line ) |
| clc = newclc; |
| |
| printLine( line, newclc, 0, LINE_LOC ); |
| cc = jx + 1; |
| next(0); /* discard slash */ |
| continue; |
| } |
| |
| switch( line[lexterm] ) { |
| case ',': |
| if( isLexSymbol()) { |
| WORD32 val; |
| SYM_T *sym; |
| char name[SYMLEN]; |
| |
| /* Use lookup so symbol will not be counted as reference. */ |
| sym = lookup(lexemeToName(name, lexstart, lexterm), UNDEFINED); |
| |
| if (curmacro) { |
| /* relative during macro expansion!! */ |
| val = clc - curmacro->defn->args[0].val; |
| } |
| else |
| val = clc; |
| |
| if( M_DEFINED( sym->type )) { |
| if( sym->val != val && pass == 2 ) |
| errorSymbol( &duplicate_label, sym->name, lexstart ); |
| sym->type |= DUPLICATE; /* XXX never used! */ |
| } |
| /* Must call define on pass 2 to generate concordance. */ |
| defineLexeme( lexstart, lexterm, val, LABEL ); |
| } |
| else if (isdigit(line[lexstart])) { /* constant, */ |
| int i; |
| WORD32 val = 0; |
| |
| for( i = lexstart; i < lexterm; i++ ) { |
| if( isdigit( line[i] )) { |
| int digit; |
| digit = line[i] - '0'; |
| if( digit >= radix ) { |
| errorLexeme( &number_not_radix, i ); |
| val = 0; |
| break; |
| } |
| val = val * radix + digit; |
| } |
| else { |
| errorLexeme( ¬_a_number, lexstart ); |
| val = 0; |
| break; |
| } |
| } |
| if (i == lexterm) { |
| if( clc != val && pass == 2 ) |
| errorLexeme( &duplicate_label, lexstart); /* XXX */ |
| } |
| } |
| else |
| errorLexeme( &label_syntax, lexstart ); |
| next(0); /* skip comma */ |
| continue; |
| |
| case '=': |
| if( isLexSymbol()) { |
| WORD32 start, term, val; |
| |
| start = lexstart; |
| term = lexterm; |
| next(0); /* skip symbol */ |
| next(0); /* skip trailing = */ |
| val = getExprs(); |
| defineLexeme( start, term, val, DEFINED ); |
| printLine( line, 0, val, LINE_VAL ); |
| } |
| else { |
| errorLexeme( &symbol_syntax, lexstartprev ); |
| next(0); /* skip symbol */ |
| next(0); /* skip trailing = */ |
| getExprs(); /* skip expression */ |
| } |
| continue; |
| } /* switch on terminator */ |
| |
| if( isLexSymbol()) { |
| SYM_T *sym; |
| WORD32 val; |
| |
| sym = evalSymbol(); |
| val = sym->val; |
| if( M_MACRO(sym->type)) { |
| if (!invokeMacro(val)) |
| next(0); /* bad defn? or body is empty! */ |
| continue; |
| } /* macro invocation */ |
| else if( M_PSEUDO(sym->type)) { /* NO EPSEUDOs */ |
| pseudo( (PSEUDO_T)val & 0777777 ); |
| continue; |
| } /* pseudo */ |
| } /* macro, or non-char pseudo */ |
| |
| evalue = getExpr(); |
| if (evalue.type != PSEUDO) { /* not a bare pseudo-op? */ |
| if (line[lexstart] == ',') { /* EXP, */ |
| if(evalue.val != clc && pass == 2 ) |
| errorLexeme( &duplicate_label, lexstart); /* XXX */ |
| } |
| else if (line[lexstart] == '/') { /* EXP/ */ |
| clc = evalue.val; |
| printLine( line, clc, 0, LINE_LOC ); |
| next(0); |
| } |
| else { |
| punchOutObject( clc, evalue.val & 0777777); /* punch it! */ |
| incrementClc(); |
| } |
| } |
| } /* forever */ |
| } |
| |
| /* Function: onePass */ |
| /* Synopsis: Do one assembly pass. */ |
| void onePass() { |
| int ix; |
| |
| clc = 4; /* Default location is 4 */ |
| start_addr = 0; /* No starting address. */ |
| nconst = 0; /* No constant blocks seen */ |
| nvars = 0; /* No variables seen */ |
| |
| while (curmacro) { /* pop macro stack */ |
| struct macinv *mp; |
| |
| mp = curmacro->prev; |
| free(curmacro); |
| curmacro = mp; |
| } |
| |
| for( ix = 0; ix < mac_count; ix++) { |
| if (mac_defs[ix]) |
| free( mac_defs[ix] ); |
| mac_defs[ix] = NULL; |
| } |
| mac_count = 0; /* No macros defined. */ |
| |
| listed = TRUE; |
| lineno = 0; |
| list_pageno = 0; |
| list_lineno = 0; |
| list_title_set = FALSE; |
| page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ |
| radix = 8; /* Initial radix is octal (base 8). */ |
| |
| /* Now open the first input file. */ |
| end_of_input = FALSE; |
| filix_curr = filix_start; /* Initialize pointer to input files. */ |
| if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { |
| fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], |
| save_argv[filix_curr] ); |
| exit( -1 ); |
| } |
| |
| for (;;) { |
| readLine(); |
| if (end_of_input) { |
| eob(); |
| fclose( infile ); |
| return; |
| } |
| processLine(); |
| } /* forever */ |
| } /* onePass */ |
| |
| |
| /* Function: getExprs */ |
| /* Synopsys: gutted like a fish */ |
| WORD32 getExprs() |
| { |
| SYM_T sym; |
| |
| sym = getExpr(); |
| if (sym.type == PSEUDO) |
| errorMessage( &value_required, lexstart ); /* XXX wrong pointer? */ |
| |
| return sym.val & 0777777; |
| } /* getExprs */ |
| |
| |
| SYM_T getExpr() |
| { |
| SYM_T sym; |
| |
| sym = eval(); |
| |
| /* Here we assume the current lexeme is the operator separating the */ |
| /* previous operator from the next, if any. */ |
| |
| for (;;) { |
| int space; |
| /* |
| * falling out of switch breaks loop and returns from routine |
| * so if you want to keep going, you must "continue"!! |
| */ |
| space = FALSE; |
| switch( line[lexstart] ) { |
| case ' ': |
| space = TRUE; |
| /* fall */ |
| case '+': /* add */ |
| next(1); /* skip operator */ |
| if (space && ISEND(line[lexstart])) /* tollerate a trailing space */ |
| return sym; |
| sym.val += eval().val; /* XXX look at type? */ |
| sym.type = DEFINED; |
| if( sym.val >= 01000000 ) |
| sym.val = ( sym.val + 1 ) & 0777777; |
| continue; |
| |
| case '-': /* subtract */ |
| next(1); /* skip over the operator */ |
| sym.val += eval().val ^ 0777777; /* XXX look at type? */ |
| sym.type = DEFINED; |
| if( sym.val >= 01000000 ) |
| sym.val = ( sym.val + 1 ) & 0777777; |
| continue; |
| |
| case '*': /* multiply */ |
| next(1); /* skip over the operator */ |
| sym.val *= eval().val; |
| sym.type = DEFINED; |
| if( sym.val >= 01000000 ) |
| sym.val = ( sym.val + 1 ) & 0777777; |
| continue; |
| |
| #if 0 |
| case '%': /* divide !??? */ |
| /* |
| * neither '%' nor the divide symbol appear in FIO-DEC, |
| * does any known program use such an operator? |
| * Easily confused for "MOD", which is how C uses '%'! |
| */ |
| next(1); |
| sym.val /= eval().val; |
| sym.type = DEFINED; |
| continue; |
| #endif |
| |
| case '&': /* and */ |
| next(1); /* skip over the operator */ |
| sym.val &= eval().val; |
| sym.type = DEFINED; |
| continue; |
| |
| case '!': /* or */ |
| next(1); /* skip over the operator */ |
| sym.val |= eval().val; |
| sym.type = DEFINED; |
| continue; |
| |
| case '/': |
| case ')': |
| case ']': |
| case ':': |
| case ',': |
| break; |
| |
| case '=': |
| errorMessage( &illegal_equals, lexstart ); |
| moveToEndOfLine(); |
| sym.val = 0; |
| break; |
| |
| default: |
| if (!ISEND(line[lexstart])) { |
| errorMessage( &illegal_expression, lexstart ); |
| moveToEndOfLine(); |
| sym.val = 0; |
| break; |
| } |
| } /* switch */ |
| break; /* break loop!! */ |
| } /* "forever" */ |
| return( sym ); |
| } /* getExpr */ |
| |
| /* |
| * return fio-dec code for next char |
| * embeds shifts as needed |
| */ |
| int |
| nextfiodec(int *ccase, int delim) |
| { |
| unsigned char c; |
| |
| for (;;) { |
| if (cc >= maxcc) { |
| if (delim == -1) |
| return -1; |
| |
| /* XXX MUST NOT BE IN A REPEAT!! */ |
| readLine(); /* danger will robinson! */ |
| if (end_of_input) |
| return -1; |
| } |
| c = line[cc]; |
| switch (c) { |
| case '\n': |
| c = '\r'; |
| break; |
| case '\r': |
| continue; |
| } |
| break; |
| } |
| |
| if (delim != -1 && c == delim) { |
| if (*ccase == LC) { |
| cc++; /* eat delim */ |
| return -1; |
| } |
| *ccase = LC; |
| return CONCISE_LC; /* shift down first */ |
| } |
| |
| if (c > 0177) { /* non-ascii */ |
| errorMessage( &illegal_character, cc ); |
| c = 0; /* space?! */ |
| } |
| |
| c = ascii_to_fiodec[c&0177]; |
| if (c == BAD) { |
| errorMessage( &illegal_character, cc ); |
| c = 0; /* space?! */ |
| } |
| |
| if (!(c & *ccase)) { /* char not in current case? */ |
| *ccase ^= BC; /* switch case */ |
| if (*ccase == LC) |
| return CONCISE_LC; /* shift down */ |
| else |
| return CONCISE_UC; /* shift up */ |
| } |
| cc++; |
| return c & CHARBITS; |
| } |
| |
| /* |
| * Function: flex |
| * Synopsis: Handle data for "flexo" pseudo |
| * handle upper case by doing shifts |
| */ |
| |
| WORD32 flex() |
| { |
| WORD32 w; |
| int shift; |
| int ccase; |
| |
| if (line[lexstart] == ' ') /* always? */ |
| next(0); |
| |
| /* original version appears to take next 3 characters, |
| * REGARDLESS of what they are (tab, newline, space?)! |
| */ |
| w = 0; |
| ccase = LC; /* current case */ |
| for (shift = 12; shift >= 0; shift -= 6) { |
| unsigned char c; |
| if( lexstart >= maxcc ) |
| break; |
| |
| c = line[lexstart]; |
| if (c == '\t' || c == '\n') { |
| if (ccase == LC) |
| break; |
| c = CONCISE_LC; /* shift down first */ |
| } |
| else { |
| if (c > 0177) { /* non-ascii */ |
| errorMessage( &illegal_character, lexstart ); |
| c = 0; |
| } |
| |
| c = ascii_to_fiodec[c&0177]; |
| if (c == BAD) { |
| errorMessage( &illegal_character, lexstart ); |
| c = 0; |
| } |
| |
| if (!(c & ccase)) { /* char not in current case? */ |
| ccase ^= BC; /* switch case */ |
| if (ccase == LC) |
| c = CONCISE_LC; /* shift down */ |
| else |
| c = CONCISE_UC; /* shift up */ |
| } |
| else |
| lexstart++; |
| } |
| w |= (c & CHARBITS) << shift; |
| } |
| /* error to get here w/ case == UC? nah. shift down could be next */ |
| return w; |
| } /* flex */ |
| |
| /* |
| * Function: getChar |
| * Synopsis: Handle data for "char" pseudo |
| */ |
| |
| WORD32 getChar() |
| { |
| unsigned char c, pos; |
| |
| if( cc >= maxcc ) |
| return 0; /* XXX error? */ |
| pos = line[cc++]; |
| if (pos != 'l' && pos != 'm' && pos != 'r') { |
| errorMessage( &illegal_character, lexstart ); |
| return 0; |
| } |
| |
| if( cc >= maxcc ) |
| return 0; /* XXX error? */ |
| |
| c = line[cc++]; |
| if (c > 0177) { |
| errorMessage( &illegal_character, lexstart ); |
| c = 0; |
| } |
| |
| c = ascii_to_fiodec[c]; |
| if (c == BAD) { |
| errorMessage( &illegal_character, lexstart ); |
| c = 0; |
| } |
| |
| if (!(c & LC)) { /* upper case only char? */ |
| c = CONCISE_UC; /* take a shift up */ |
| cc--; /* and leave char for next luser */ |
| } |
| |
| c &= CHARBITS; |
| switch (pos) { |
| case 'l': return c << 12; |
| case 'm': return c << 6; |
| case 'r': return c; |
| } |
| /* should not happen */ |
| return 0; |
| } /* flex */ |
| |
| /* Function: eval */ |
| /* Synopsis: Get the value of the current lexeme, and advance.*/ |
| SYM_T eval2() |
| { |
| WORD32 digit; |
| WORD32 from; |
| SYM_T *sym; |
| WORD32 val; |
| SYM_T sym_eval; |
| |
| sym_eval.type = DEFINED; |
| sym_eval.name[0] = '\0'; |
| sym_eval.val = sym_eval.xref_index = sym_eval.xref_count = 0; |
| |
| val = 0; |
| |
| if( isLexSymbol()) { |
| sym = evalSymbol(); |
| if(!M_DEFINED( sym->type )) { |
| if( pass == 2 ) |
| errorSymbol( &undefined_symbol, sym->name, lexstart ); |
| next(1); |
| return( *sym ); |
| } |
| else if( M_PSEUDO(sym->type) || M_EPSEUDO(sym->type)) { |
| switch (sym->val) { |
| case DECIMAL: |
| radix = 10; |
| sym_eval.type = PSEUDO; |
| sym_eval.val = 0; /* has zero as a value! */ |
| break; |
| case OCTAL: |
| radix = 8; |
| sym_eval.type = PSEUDO; |
| sym_eval.val = 0; /* has zero as a value */ |
| break; |
| case FLEX: |
| next(1); /* skip keyword */ |
| sym_eval.val = flex(); |
| break; |
| case CHAR: |
| next(1); /* skip keyword */ |
| sym_eval.val = getChar(); |
| break; |
| default: |
| errorSymbol( &value_required, sym->name, lexstart ); |
| sym_eval.type = sym->type; |
| sym_eval.val = 0; |
| break; |
| } |
| next(1); |
| return( sym_eval ); |
| } |
| else if( M_MACRO( sym->type )) |
| { |
| if( pass == 2 ) |
| { |
| errorSymbol( &misplaced_symbol, sym->name, lexstart ); |
| } |
| sym_eval.type = sym->type; |
| sym_eval.val = 0; |
| next(1); |
| return( sym_eval ); |
| } |
| else |
| { |
| next(1); |
| return( *sym ); |
| } |
| } /* symbol */ |
| else if( isdigit( line[lexstart] )) { |
| from = lexstart; |
| val = 0; |
| while( from < lexterm ) { |
| if( isdigit( line[from] )) { |
| digit = line[from++] - '0'; |
| if( digit >= radix ) { |
| errorLexeme( &number_not_radix, from - 1 ); |
| val = 0; |
| break; |
| } |
| val = val * radix + digit; |
| } |
| else { |
| errorLexeme( ¬_a_number, lexstart ); |
| val = 0; |
| break; |
| } |
| } |
| next(1); |
| sym_eval.val = val; |
| return( sym_eval ); |
| } /* digit */ |
| else { |
| switch( line[lexstart] ) { |
| case '.': /* Value of Current Location Counter */ |
| val = clc; |
| next(1); |
| break; |
| case '(': /* Generate literal */ |
| next(1); /* Skip paren */ |
| val = getExprs(); /* recurse */ |
| if( line[lexstart] == ')' ) |
| next(1); /* Skip end paren */ |
| sym_eval.val = literal(val); |
| return sym_eval; |
| case '[': /* parens!! */ |
| next(1); |
| sym_eval.val = getExprs(); /* mutual recursion */ |
| if( line[lexstart] == ']' ) |
| next(1); /* Skip close bracket */ |
| else |
| errorMessage( &illegal_character, lexstart ); |
| return sym_eval; |
| default: |
| switch( line[lexstart] ) { |
| case '=': |
| errorMessage( &illegal_equals, lexstart ); |
| moveToEndOfLine(); |
| break; |
| default: |
| errorMessage( &illegal_character, lexstart ); |
| break; |
| } /* error switch */ |
| val = 0; /* On error, set value to zero. */ |
| next(1); /* Go past illegal character. */ |
| } /* switch on first char */ |
| } /* not symbol or number */ |
| sym_eval.val = val; |
| return( sym_eval ); |
| } /* eval2 */ |
| |
| |
| SYM_T eval() { |
| SYM_T sym; |
| |
| switch (line[lexstart]) { |
| case '-': /* unary - */ |
| next(1); |
| sym = eval2(); /* skip op */ |
| sym.val ^= 0777777; |
| break; |
| case '+': /* unary + */ |
| next(1); /* skip op */ |
| /* fall */ |
| default: |
| sym = eval2(); |
| } |
| return sym; |
| } |
| |
| /* Function: incrementClc */ |
| /* Synopsis: Set the next assembly location. Test for collision with */ |
| /* the literal tables. */ |
| WORD32 incrementClc() |
| { |
| clc = (( clc + 1 ) & ADDRESS_FIELD ); |
| return( clc ); |
| } /* incrementClc */ |
| |
| |
| /* Function: readLine */ |
| /* Synopsis: Get next line of input. Print previous line if needed. */ |
| void readLine() |
| { |
| BOOL ffseen; |
| WORD32 ix; |
| WORD32 iy; |
| char inpline[LINELEN]; |
| |
| /* XXX panic if nrepeats > 0 (if self-feeding, do the backup here?) */ |
| |
| listLine(); /* List previous line if needed. */ |
| error_in_line = FALSE; /* No error in line. */ |
| |
| if(curmacro && *curmacro->mac_ptr == '\0') { /* end of macro? */ |
| struct macinv *mp; |
| |
| listed = TRUE; /* Already listed. */ |
| |
| /* Restore invoking line. */ |
| strcpy(line, curmacro->mac_line); |
| cc = lexstartprev = curmacro->mac_cc; /* Restore cc. */ |
| maxcc = strlen( line ); /* Restore maxcc. */ |
| |
| mp = curmacro->prev; /* pop stack */ |
| free(curmacro); |
| curmacro = mp; |
| |
| return; |
| } /* end of macro */ |
| |
| cc = 0; /* Initialize column counter. */ |
| lexstartprev = 0; |
| if( curmacro ) { /* Inside macro? */ |
| char mc; |
| |
| maxcc = 0; |
| do { |
| |
| mc = *curmacro->mac_ptr++; /* Next character. */ |
| /* watch for overflow? how could it?? */ |
| line[maxcc++] = mc; |
| } while( !ISEND( mc )); /* note: terminates on tab?! */ |
| line[maxcc] = '\0'; |
| listed = nomac_exp; |
| return; |
| } /* inside macro */ |
| |
| lineno++; /* Count lines read. */ |
| listed = FALSE; /* Mark as not listed. */ |
| READ_LINE: |
| if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { |
| filix_curr++; /* Advance to next file. */ |
| if( filix_curr < save_argc ) { /* More files? */ |
| fclose( infile ); |
| if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { |
| fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], |
| save_argv[filix_curr] ); |
| exit( -1 ); |
| } |
| list_title_set = FALSE; |
| goto READ_LINE; |
| } |
| else |
| end_of_input = TRUE; |
| } /* fgets failed */ |
| |
| ffseen = FALSE; |
| for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) { |
| if( inpline[ix] == '\f' ) { |
| if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); |
| ffseen = TRUE; |
| } |
| else |
| line[iy++] = inpline[ix]; |
| } |
| line[iy] = '\0'; |
| |
| /* If the line is terminated by CR-LF, remove, the CR. */ |
| if( line[iy - 2] == '\r' ) { |
| iy--; |
| line[iy - 1] = line[iy - 0]; |
| line[iy] = '\0'; |
| } |
| maxcc = iy; /* Save the current line length. */ |
| } /* readLine */ |
| |
| |
| /* Function: listLine */ |
| /* Synopsis: Output a line to the listing file. */ |
| void listLine() |
| /* generate a line of listing if not already done! */ |
| { |
| if( listfile != NULL && listed == FALSE ) |
| { |
| printLine( line, 0, 0, LINE ); |
| } |
| } /* listLine */ |
| |
| |
| /* Function: printPageBreak */ |
| /* Synopsis: Output a Top of Form and listing header if new page necessary. */ |
| void printPageBreak() |
| { |
| if( page_lineno >= LIST_LINES_PER_PAGE ) |
| /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ |
| { |
| topOfForm( list_title, NULL ); |
| } |
| } /* printPageBreak */ |
| |
| |
| /* Function: printLine */ |
| /* Synopsis: Output a line to the listing file with new page if necessary. */ |
| void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) |
| { |
| if( listfile == NULL ) |
| { |
| save_error_count = 0; |
| return; |
| } |
| |
| printPageBreak(); |
| |
| list_lineno++; |
| page_lineno++; |
| switch( linestyle ) |
| { |
| default: |
| case LINE: |
| fprintf( listfile, "%5d ", lineno ); |
| fputs( line, listfile ); |
| listed = TRUE; |
| break; |
| |
| case LINE_VAL: |
| if( !listed ) |
| { |
| fprintf( listfile, "%5d %6.6o ", lineno, val ); |
| fputs( line, listfile ); |
| listed = TRUE; |
| } |
| else |
| { |
| fprintf( listfile, " %6.6o\n", val ); |
| } |
| break; |
| |
| case LINE_LOC: |
| if( !listed ) |
| { |
| fprintf( listfile, "%5d %5.5o ", lineno, loc ); |
| fputs( line, listfile ); |
| listed = TRUE; |
| } |
| else |
| { |
| fprintf( listfile, " %5.5o\n", loc ); |
| } |
| break; |
| |
| case LINE_LOC_VAL: |
| if( !listed ) |
| { |
| fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); |
| fputs( line, listfile ); |
| listed = TRUE; |
| } |
| else |
| { |
| fprintf( listfile, " %5.5o %6.6o\n", loc, val ); |
| } |
| break; |
| |
| case LOC_VAL: |
| fprintf( listfile, " %5.5o %6.6o\n", loc, val ); |
| break; |
| } |
| printErrorMessages(); |
| } /* printLine */ |
| |
| |
| /* Function: printErrorMessages */ |
| /* Synopsis: Output any error messages from the current list of errors. */ |
| void printErrorMessages() |
| { |
| WORD32 ix; |
| WORD32 iy; |
| |
| if( listfile != NULL ) |
| { |
| /* If any errors, display them now. */ |
| for( iy = 0; iy < save_error_count; iy++ ) |
| { |
| printPageBreak(); |
| fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); |
| if( error_list[iy].col >= 0 ) |
| { |
| for( ix = 0; ix < error_list[iy].col; ix++ ) |
| { |
| if( line[ix] == '\t' ) |
| { |
| putc( '\t', listfile ); |
| } |
| else |
| { |
| putc( ' ', listfile ); |
| } |
| } |
| fputs( "^", listfile ); |
| list_lineno++; |
| page_lineno++; |
| } |
| fputs( "\n", listfile ); |
| } |
| } |
| save_error_count = 0; |
| } /* printErrorMessages */ |
| |
| |
| /* Function: punchObject */ |
| /* Synopsis: Put one character to object file */ |
| void punchObject( WORD32 val ) |
| { |
| val &= 0377; |
| if( objectfile != NULL ) |
| fputc( val, objectfile ); |
| } /* punchObject */ |
| |
| /* Function: punchTriplet */ |
| /* Synopsis: Output 18b word as three 6b characters with ho bit set. */ |
| void punchTriplet( WORD32 val ) |
| { |
| punchObject((( val >> 12) & 077) | 0200 ); |
| punchObject((( val >> 6 ) & 077) | 0200 ); |
| punchObject(( val & 077) | 0200 ); |
| } /* punchTriplet */ |
| |
| void |
| eob() { |
| /* in case no "start" in file (an error?) */ |
| } |
| |
| /* Function: punchLeader */ |
| /* Synopsis: Generate 2 feet of leader on object file, as per DEC */ |
| /* documentation. Paper tape has 10 punches per inch. */ |
| void punchLeader( WORD32 count ) |
| { |
| WORD32 ix; |
| |
| /* If value is zero, set to the default of 2 feet of leader. */ |
| count = ( count == 0 ) ? 240 : count; |
| |
| if( objectfile != NULL ) |
| { |
| for( ix = 0; ix < count; ix++ ) |
| { |
| fputc( 0, objectfile ); |
| } |
| } |
| } /* punchLeader */ |
| |
| /* Function: punchOutObject */ |
| /* Synopsis: Output the current line and then then punch value to the */ |
| /* object file. */ |
| void punchOutObject( WORD32 loc, WORD32 val ) |
| { |
| printLine( line, loc, val, LINE_LOC_VAL ); |
| punchLocObject( loc, val ); |
| } /* punchOutObject */ |
| |
| |
| /* Function: punchLocObjectRIM */ |
| /* Synopsis: Output the word in RIM mode */ |
| void punchLocObjectRIM( WORD32 loc, WORD32 val ) |
| { |
| punchTriplet( DIO | loc ); |
| punchTriplet( val ); |
| } /* punchLocObject */ |
| |
| /* punch loader in RIM mode */ |
| void |
| punchLoader() { |
| int i; |
| |
| if (noinput) |
| return; |
| |
| for (i = 0; i < DIM(loader); i++) |
| punchLocObjectRIM(LOADERBASE+i, loader[i]); |
| punchTriplet( JMP | LOADERBASE ); |
| } |
| |
| /* |
| * flush out loader buffer; output a block: |
| * DIO start |
| * DIO end+1 |
| * .... data .... |
| * sum |
| */ |
| #define PW(X) { WORD32 x = X; sum += x; punchTriplet(x); } |
| void |
| flushLoader() { |
| WORD32 sum; |
| int i; |
| |
| if (loaderbufcount == 0) |
| return; |
| |
| sum = 0; |
| PW( DIO | loaderbufstart ); |
| PW( DIO | loaderbufstart + loaderbufcount ); |
| for (i = 0; i < loaderbufcount; i++) |
| PW( loaderbuf[i] ); |
| |
| /* roll over all the overflows at once */ |
| if (sum & ~0777777) |
| sum = (sum & 0777777) + (sum >> 18); |
| if (sum & 01000000) /* one more time */ |
| sum++; |
| PW( sum ); |
| |
| punchLeader(5); |
| loaderbufcount = 0; |
| } |
| |
| void punchLocObject( WORD32 loc, WORD32 val ) |
| { |
| if (!rim_mode) { |
| if ((loc & LOADERBUFMASK) == 0 || /* full/force alignment */ |
| loaderbufcount > 0 && |
| loc != loaderbufstart + loaderbufcount) /* disjoint */ |
| flushLoader(); |
| if (loaderbufcount == 0) |
| loaderbufstart = loc; |
| loaderbuf[loaderbufcount++] = val; |
| } |
| else |
| punchLocObjectRIM( loc, val ); |
| } |
| |
| /* Function: literal */ |
| /* Synopsis: Add a value to the literal pool */ |
| WORD32 |
| literal( WORD32 value ) |
| { |
| int i; |
| |
| if (nconst >= MAX_CONSTANTS) { |
| fprintf(stderr, "too many 'constants'; increase MAX_CONSTANTS\n"); |
| exit(1); |
| } |
| |
| if (pass == 1) { |
| if (++lit_count[nconst] == MAX_LITERALS) { |
| fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); |
| exit(1); |
| } |
| return lit_count[nconst]; |
| } |
| |
| #if 1 |
| /* |
| * pool constants; makes for a shorter tape |
| * (but "middle" constants blocks can't shrink) |
| */ |
| for (i = 0; i < nlit; i++) |
| if (litter[i] == value) |
| return lit_loc[nconst] + i; |
| #endif |
| |
| /* paranoia */ |
| if (nlit == MAX_LITERALS) { |
| fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); |
| exit(1); |
| } |
| |
| /* not found, save it */ |
| litter[nlit] = value; |
| |
| /* use base for this block, determined on pass1 */ |
| return lit_loc[nconst] + nlit++; |
| } /* literal */ |
| |
| |
| /* Function: printSymbolTable */ |
| /* Synopsis: Output the symbol table. */ |
| /* XXX now prints FIXED symbols too */ |
| void printSymbolTable() |
| { |
| int ix; |
| int symbol_lines; |
| SYM_T *sym; |
| char mark; |
| |
| symbol_lines = 0; |
| for (ix = 0, sym = symtab; ix < symbol_top; ix++, sym++) { |
| if (M_FIXED(sym->type) || M_PSEUDO(sym->type) || |
| M_MACRO(sym->type) || M_EPSEUDO(sym->type)) |
| continue; |
| |
| if (symbol_lines == 0) { |
| topOfForm( list_title, s_symtable ); |
| symbol_lines = LIST_LINES_PER_PAGE; |
| } |
| |
| switch( sym->type & ( DEFINED | REDEFINED )) { |
| case UNDEFINED: |
| mark = '?'; |
| break; |
| |
| case REDEFINED: |
| mark = '#'; |
| break; |
| |
| default: |
| mark = ' '; |
| break; |
| } |
| fprintf( listfile, "%c%-6.6s %6.6o\n", mark, sym->name, sym->val ); |
| symbol_lines--; |
| } |
| } /* printSymbolTable */ |
| |
| |
| /* Function: printPermanentSymbolTable */ |
| /* Synopsis: Output the permanent symbol table to a file suitable for */ |
| /* being input after the EXPUNGE pseudo-op. */ |
| void printPermanentSymbolTable() |
| { |
| int ix; |
| FILE *permfile; |
| |
| if(( permfile = fopen( permpathname, "w" )) == NULL ) |
| { |
| exit( 2 ); |
| } |
| |
| fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); |
| fprintf( permfile, " expunge\n/\n" ); |
| |
| for( ix = 0; ix < symbol_top; ix++ ) |
| { |
| int type = symtab[ix].type; |
| if( M_FIXED(type) && !M_PSEUDO(type) && !M_EPSEUDO(type) ) |
| fprintf( permfile, "\t%s=%o\n", |
| symtab[ix].name, symtab[ix].val ); |
| } |
| fclose( permfile ); |
| } /* printPermanentSymbolTable */ |
| |
| |
| /* Function: printCrossReference */ |
| /* Synopsis: Output a cross reference (concordance) for the file being */ |
| /* assembled. */ |
| void printCrossReference() |
| { |
| int ix; |
| int xc; |
| int xc_index; |
| int xc_refcount; |
| int xc_cols; |
| SYM_T *sym; |
| |
| /* Force top of form for first page. */ |
| page_lineno = LIST_LINES_PER_PAGE; |
| |
| list_lineno = 0; |
| |
| for( ix = 0, sym = symtab; ix < symbol_top; ix++, sym++ ) { |
| if (M_FIXED(sym->type) && xreftab[sym->xref_index] == 0) |
| continue; |
| list_lineno++; |
| page_lineno++; |
| if( page_lineno >= LIST_LINES_PER_PAGE ) |
| topOfForm( list_title, s_xref ); |
| |
| fprintf( listfile, "%5d", list_lineno ); |
| |
| /* Get reference count & index into concordance table for this symbol */ |
| xc_refcount = sym->xref_count; |
| xc_index = sym->xref_index; |
| /* Determine how to label symbol on concordance. */ |
| /* XXX flag variables? */ |
| switch( sym->type & ( DEFINED | REDEFINED )) { |
| case UNDEFINED: |
| fprintf( listfile, " U "); |
| break; |
| |
| case REDEFINED: |
| fprintf( listfile, " M %5d ", xreftab[xc_index] ); |
| break; |
| |
| default: |
| fprintf( listfile, " A %5d ", xreftab[xc_index] ); |
| break; |
| } |
| fprintf( listfile, "%-6.6s ", sym->name ); |
| |
| /* Output the references, 8 numbers per line after symbol name. */ |
| for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { |
| if( xc_cols >= XREF_COLUMNS ) { |
| xc_cols = 0; |
| page_lineno++; |
| if( page_lineno >= LIST_LINES_PER_PAGE ) |
| topOfForm( list_title, s_xref); |
| list_lineno++; |
| fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); |
| } |
| fprintf( listfile, " %5d", xreftab[xc_index + xc] ); |
| } |
| fprintf( listfile, "\n" ); |
| } /* for */ |
| } /* printCrossReference */ |
| |
| |
| /* Function: topOfForm */ |
| /* Synopsis: Prints title and sub-title on top of next page of listing. */ |
| void topOfForm( char *title, char *sub_title ) |
| { |
| char temp[10]; |
| |
| list_pageno++; |
| strcpy( temp, s_page ); |
| sprintf( temp, "%s %d", s_page, list_pageno ); |
| |
| if (!listfile) |
| return; |
| |
| /* Output a top of form if not the first page of the listing. */ |
| if( list_pageno > 1 ) |
| fprintf( listfile, "\f" ); |
| |
| fprintf( listfile, "\n %-63s %10s\n", title, temp ); |
| |
| /* Reset the current page line counter. */ |
| page_lineno = 1; |
| if( sub_title != NULL ) |
| { |
| fprintf( listfile, "%80s\n", sub_title ); |
| page_lineno++; |
| } |
| else |
| { |
| fprintf( listfile, "\n" ); |
| page_lineno++; |
| } |
| fprintf( listfile, "\n" ); |
| page_lineno++; |
| } /* topOfForm */ |
| |
| |
| /* Function: lexemeToName */ |
| /* Synopsis: Convert the current lexeme into a string. */ |
| char *lexemeToName( char *name, WORD32 from, WORD32 term ) |
| { |
| int to; |
| |
| to = 0; |
| while( from < term && to < SYMLEN-1) { |
| char c = line[from++]; |
| if (ISOVERBAR(c)) |
| continue; |
| name[to++] = c; |
| } |
| name[to] = '\0'; |
| |
| return( name ); |
| } /* lexemeToName */ |
| |
| /* Function: defineLexeme */ |
| /* Synopsis: Put lexeme into symbol table with a value. */ |
| SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ |
| WORD32 term, /* end+1 of lexeme being defined. */ |
| WORD32 val, /* value of lexeme being defined. */ |
| SYMTYP type ) /* how symbol is being defined. */ |
| { |
| char name[SYMLEN]; |
| |
| lexemeToName( name, start, term); |
| return( defineSymbol( name, val, type, start )); |
| } /* defineLexeme */ |
| |
| |
| /* Function: defineSymbol */ |
| /* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ |
| /* not already in table. */ |
| SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) |
| { |
| SYM_T *sym; |
| WORD32 xref_count; |
| |
| if( strlen( name ) < 1 ) |
| { |
| return( &sym_undefined ); /* Protect against non-existent names. */ |
| } |
| sym = lookup( name, type ); |
| xref_count = 0; /* Set concordance for normal defintion. */ |
| |
| if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) |
| { |
| if( pass == 2 ) |
| { |
| errorSymbol( &redefined_symbol, sym->name, start ); |
| type = type | REDEFINED; |
| sym->xref_count++; /* Referenced symbol, count it. */ |
| xref_count = sym->xref_count; |
| /* moved inside "if pass2" -plb 10/2/03 allow redefinition |
| * of predefined symbols during pass1 |
| */ |
| return ( sym ); |
| } |
| } |
| |
| if( pass == 2 && xref ) |
| { |
| /* Put the definition line number in the concordance table. */ |
| /* Defined symbols are not counted as references. */ |
| if (sym->xref_index >= 0) { /* beware macro dummies */ |
| xreftab[sym->xref_index] = lineno; |
| /* Put the line number in the concordance table. */ |
| xreftab[sym->xref_index + xref_count] = lineno; |
| } |
| } |
| |
| /* Now set the value and the type. */ |
| sym->val = val & 0777777; |
| sym->type = type; |
| return( sym ); |
| } /* defineSymbol */ |
| |
| |
| /* Function: lookup */ |
| /* Synopsis: Find a symbol in table. If not in table, enter symbol in */ |
| /* table as undefined. Return address of symbol in table. */ |
| SYM_T *lookup( char *name, int type ) |
| { |
| int ix; /* Insertion index */ |
| int lx; /* Left index */ |
| int rx; /* Right index */ |
| SYM_T *best; /* best match */ |
| SYM_T *sym; |
| |
| /* YIKES! Search dummies (and "R") before anything else!! */ |
| if (curmacro && curmacro->defn) { |
| struct macdef *mdp = curmacro->defn; |
| int i; |
| |
| for (i = 0, sym = mdp->args; i <= mdp->nargs; i++, sym++) |
| if (strcmp(name, sym->name) == 0) |
| return sym; |
| } |
| |
| lx = 0; |
| rx = symbol_top - 1; |
| best = NULL; |
| while (lx <= rx) { |
| int mx = (lx + rx) / 2; /* Find center of search area. */ |
| int compare; |
| |
| sym = symtab + mx; |
| |
| compare = strcmp(name, sym->name); |
| if (compare < 0) |
| rx = mx - 1; |
| else if (compare > 0) |
| lx = mx + 1; |
| else { /* match */ |
| if (overbar && !M_DEFINED(sym->type) && pass == 2) { |
| sym->type = DEFINED; |
| sym->val = vars_addr++; |
| nvars++; |
| } |
| return sym; /* return exact match */ |
| } /* match */ |
| |
| /* save best non-exact match; MACRO returns last defined n-x match! */ |
| if ((M_PSEUDO(sym->type)||M_EPSEUDO(sym->type)||M_MACRO(sym->type)) && |
| strncmp(name, sym->name, 3) == 0) |
| best = sym; |
| } /* while */ |
| |
| /* return best match (pseudo or macro) if any for lookups (not defns) */ |
| if (best && type == UNDEFINED) |
| return best; |
| |
| /* Must put symbol in table if index is negative. */ |
| ix = lx; /* insertion point */ |
| if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { |
| errorSymbol( &symbol_table_full, name, lexstart ); |
| exit( 1 ); |
| } |
| |
| for( rx = symbol_top; rx >= ix; rx-- ) |
| symtab[rx + 1] = symtab[rx]; |
| |
| symbol_top++; |
| |
| /* Enter the symbol as UNDEFINED with a value of zero. */ |
| sym = symtab + ix; |
| strcpy( sym->name, name ); |
| sym->type = UNDEFINED; |
| sym->val = 0; |
| sym->xref_count = 0; |
| if( xref && pass == 2 && sym->xref_index >= 0) |
| xreftab[sym->xref_index] = 0; |
| |
| if (overbar) |
| nvars++; |
| |
| return sym; |
| } /* lookup */ |
| |
| /* Function: compareSymbols */ |
| /* Synopsis: Used to presort the symbol table when starting assembler. */ |
| int compareSymbols( const void *a, const void *b ) |
| { |
| return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); |
| } /* compareSymbols */ |
| |
| /* Function: evalSymbol */ |
| /* Synopsis: Get the pointer for the symbol table entry if exists. */ |
| /* If symbol doesn't exist, return a pointer to the undefined sym */ |
| SYM_T *evalSymbol() |
| { |
| char name[SYMLEN]; |
| SYM_T *sym; |
| |
| sym = lookup( lexemeToName( name, lexstart, lexterm ), UNDEFINED); |
| |
| sym->xref_count++; /* Count the number of references to symbol. */ |
| |
| if( xref && pass == 2 && sym->xref_index >= 0) |
| { |
| /* Put the line number in the concordance table. */ |
| xreftab[sym->xref_index + sym->xref_count] = lineno; |
| } |
| |
| return( sym ); |
| } /* evalSymbol */ |
| |
| |
| /* Function: moveToEndOfLine */ |
| /* Synopsis: Move the parser input to the end of the current input line. */ |
| void moveToEndOfLine() |
| { |
| while( !ISEND( line[cc] )) cc++; /* XXX wrong! will stop on a tab! */ |
| lexstart = cc; |
| lexterm = cc; |
| lexstartprev = lexstart; |
| } /* moveToEndOfLine */ |
| |
| /* frame the next token in "line" with lexstart and lexterm indicies */ |
| void |
| next(int op) { |
| char c; |
| |
| /* Save start column of previous lexeme for diagnostic messages. */ |
| lexstartprev = lexstart; |
| lextermprev = lexterm; |
| |
| c = line[cc]; |
| if (c == ' ') { |
| /* eat spaces */ |
| do { |
| c = line[++cc]; |
| } while (c == ' '); |
| if (op) /* looking for operators? */ |
| cc--; /* return one */ |
| } |
| |
| overbar = 0; |
| lexstart = cc; |
| c = line[cc]; |
| if( isalnum(c) || ISOVERBAR(c)) { |
| if (ISOVERBAR(c)) |
| overbar = 1; |
| do { |
| c = line[++cc]; |
| if (ISOVERBAR(c)) |
| overbar = 1; |
| } while (isalnum(c) || ISOVERBAR(c)); |
| } |
| else if(!ISDONE(c) || c == '\t') /* not end of line, or comment */ |
| cc++; /* advance past all punctuation */ |
| lexterm = cc; |
| } /* next */ |
| |
| BOOL isLexSymbol() |
| { |
| int ix; |
| |
| /* XXX alpha within first 4? 3?? */ |
| for( ix = lexstart; ix < lexterm; ix++ ) |
| if(isalpha(line[ix])) |
| return TRUE; /* any position will do! */ |
| return FALSE; |
| } /* isLexSymbol */ |
| |
| /* |
| * from macro manual (F-36BP), p.18; |
| * |
| * "A macro-instruction definition consists of four parts; |
| * the pseudo-instruction _define_, the _macro instruction name_ |
| * amd _dummy symbol list,_ the _body_, and the pseudo-instruction |
| * _terminate_. Each part is followed by at least one tabulation or |
| * carriage return." |
| * |
| * and in the next paragraph; |
| * |
| * "The name is terminated by a _space_ or by a _tab_ or _cr_ |
| * if there is no dummy symbol list." |
| * |
| * This accepts tabs and/or a newline after define |
| * (but will accept a space), and only accepts spaces |
| * between macro and dummy names. |
| */ |
| |
| void |
| defineMacro() { |
| int lexstartsave; /* point to macro name */ |
| int index; /* point to error char */ |
| int error; /* error boolean */ |
| int i; |
| int count; |
| WORD32 length; |
| WORD32 value; |
| char termin[SYMLEN]; |
| char args[MAC_MAX_ARGS][SYMLEN]; /* macro & arg names */ |
| char body[MAC_MAX_LENGTH + 1]; |
| struct macdef *mdp; |
| SYM_T *sym; |
| |
| if (nrepeats) { |
| /* we can call readLine, so throw up hands now */ |
| errorLexeme( &define_in_repeat, lexstartprev ); |
| return; |
| } |
| |
| while (line[lexstart] == ' ' || line[lexstart] == '\t') |
| next(0); |
| |
| /* not a tab or space */ |
| if (ISEND(line[lexstart])) { /* newline or EOS? */ |
| /* crock; next token should invisibly skip over line boundaries? */ |
| readLine(); |
| next(0); |
| while (line[lexstart] == ' ' || line[lexstart] == '\t') |
| next(0); |
| } |
| |
| /* XXX pick up macro name out here */ |
| |
| count = 0; |
| index = 0; |
| error = FALSE; |
| lexstartsave = lexstart; |
| while (!ISDONE(line[lexstart]) && count < MAC_MAX_ARGS) { |
| if (!isalnum(line[lexstart]) && index == 0) |
| index = lexstart; /* error pointer */ |
| lexemeToName( args[count++], lexstart, lexterm ); |
| /* XXX error if NOT a comma (& not first dummy) ? */ |
| if (line[lexterm] == ',') |
| next(0); /* eat the comma */ |
| next(0); |
| if (line[lexstart] == ' ') |
| next(0); |
| } |
| if( count == 0 ) { /* No macro name. */ |
| errorMessage( &no_macro_name, lexstartsave ); |
| error = TRUE; |
| } |
| else if( index ) { /* Bad argument name. */ |
| errorMessage( &bad_dummy_arg, index ); |
| error = TRUE; |
| } |
| else if( mac_count >= MAC_TABLE_LENGTH ) { |
| errorMessage( ¯o_table_full, lexstartsave ); |
| error = TRUE; |
| } |
| else { |
| value = mac_count++; /* sym value is index into mac */ |
| defineSymbol( args[0], value, MACRO, lexstartsave ); |
| } |
| |
| for( length = 0;; ) { |
| readLine(); |
| if (end_of_input) |
| break; |
| next(0); |
| while (line[lexstart] == ' ' || line[lexstart] == '\t') |
| next(0); |
| |
| lexemeToName( termin, lexstart, lexterm ); /* just look at line? */ |
| if (strncmp( termin, "term", 4 ) == 0) |
| break; |
| |
| if (!error) { |
| int ll = strlen(line); |
| int allblank = FALSE; |
| |
| /* don't save blank lines! */ |
| for( i = 0; i < ll && allblank; i++ ) |
| if(!ISBLANK(line[i])) |
| allblank = FALSE; |
| |
| if (allblank) /* nothing but air? */ |
| continue; /* skip it! */ |
| |
| if ((length + ll + 1) >= MAC_MAX_LENGTH ) { |
| errorMessage (¯o_too_long, lexstart ); |
| error = TRUE; |
| continue; |
| } |
| |
| strcpy(body+length, line); |
| length += ll; |
| } |
| } /* for */ |
| if( error ) |
| return; |
| |
| mdp = calloc(1, sizeof(struct macdef) + length); |
| if (mdp == NULL) { |
| fprintf(stderr, "error allocating memory for macro definition\n"); |
| exit(1); |
| } |
| mac_defs[value] = mdp; |
| |
| strncpy(mdp->body, body, length); |
| mdp->body[length] = '\0'; |
| mdp->nargs = count - 1; |
| |
| /* |
| * save dummy names |
| * symbol slot 0 reserved for "r" symbol |
| * move SYM_T entries to macinv to allow recursion |
| */ |
| sym = mdp->args; |
| sym->type = DEFINED; |
| strcpy(sym->name, "R"); |
| sym->val = 0; |
| sym->xref_index = -1; /* ??? allow xref? */ |
| sym++; |
| |
| for (i = 1; i <= mdp->nargs; i++, sym++) { |
| sym->type = DEFINED; |
| strcpy(sym->name, args[i]); |
| sym->val = 0; |
| sym->xref_index = -1; /* don't xref!! */ |
| } |
| } /* defineMacro */ |
| |
| /* VARIABLES pseudo-op */ |
| void |
| variables() { |
| /* XXX error if "variables" already seen (in this pass) */ |
| /* XXX error if different address on pass 2 */ |
| if (pass == 2) |
| printLine( line, clc, 0, LINE_LOC ); |
| vars_addr = clc; |
| vars_end = clc = (clc + nvars) & ADDRESS_FIELD; |
| if (pass == 2) |
| printLine( line, clc, 0, LINE_LOC); |
| } |
| |
| /* TEXT pseudo-op */ |
| void |
| text(void) |
| { |
| char delim; |
| WORD32 w; |
| int count; |
| int ccase; |
| /* XXX error in repeat!! */ |
| do { |
| if (cc == maxcc) { |
| /* XXX EOL before delim found!!! */ |
| fprintf(stderr, "FIX ME!\n"); |
| return; |
| } |
| delim = line[cc++]; |
| } while (delim == ' '); /* others? NL */ |
| |
| w = count = 0; |
| ccase = LC; |
| for (;;) { |
| int c = nextfiodec(&ccase, delim); |
| if (c == -1) |
| break; |
| w |= c << ((2-count)*6); |
| if (++count == 3) { |
| punchOutObject(clc, w); /* punch it! */ |
| incrementClc(); |
| count = w = 0; |
| } |
| } |
| if (count > 0) { |
| punchOutObject(clc, w); /* punch remainder */ |
| incrementClc(); |
| } |
| } |
| |
| /* CONSTANTS pseudo-op */ |
| void |
| constants(void) { |
| int i; |
| |
| /* XXX illegal inside macro (curmacro != NULL) */ |
| |
| if (pass == 1) { |
| lit_loc[nconst] = clc; |
| |
| /* just use addition?! */ |
| for (i = 0; i < lit_count[nconst]; i++) |
| incrementClc(); |
| |
| nconst++; |
| return; |
| } |
| |
| /* pass 2: */ |
| /* XXX complain if clc != lit_base[nconst]? */ |
| |
| for (i = 0; i < lit_count[nconst]; i++) { |
| if (i < nlit) |
| punchOutObject( clc, litter[i] & 0777777); /* punch it! */ |
| incrementClc(); |
| } |
| |
| nconst++; |
| nlit = 0; /* litter[] now empty */ |
| } /* constants */ |
| |
| |
| /* process pseudo-ops |
| * return FALSE if line scan should end (no longer used) |
| */ |
| BOOL pseudo( PSEUDO_T val ) |
| { |
| int count; |
| int repeatstart; |
| |
| switch( (PSEUDO_T) val ) { |
| case CONSTANTS: |
| next(0); /* Skip symbol */ |
| constants(); |
| break; |
| |
| case VARIABLES: |
| next(0); /* Skip symbol */ |
| variables(); |
| break; |
| |
| case DEFINE: |
| next(0); /* Skip symbol */ |
| defineMacro(); |
| return FALSE; |
| break; |
| |
| case REPEAT: |
| next(0); /* Skip symbol */ |
| |
| /* NOTE!! constant followed by SPACE picked up as expression!! */ |
| count = getExprs() & ADDRESS_FIELD; |
| /* XXX error if sign bit set? */ |
| |
| /* allow comma, but do not require */ |
| if( line[lexstart] == ',') |
| next(0); |
| |
| nrepeats++; |
| repeatstart = lexstart; /* save line start */ |
| while (count-- > 0) { |
| cc = repeatstart; /* reset input pointer */ |
| processLine(); /* recurse! */ |
| } |
| cc = maxcc; |
| nrepeats--; |
| |
| return FALSE; |
| break; |
| |
| case START: |
| next(0); /* Skip symbol */ |
| /* XXX illegal in macro or repeat */ |
| flushLoader(); |
| if (!ISDONE(line[lexstart])) { |
| if (line[lexstart] == ' ') |
| next(0); |
| start_addr = getExprs() & ADDRESS_FIELD; |
| next(0); |
| printLine( line, 0, start_addr, LINE_VAL ); |
| /* MACRO punches 4" of leader */ |
| punchTriplet(JMP | start_addr); |
| /* MACRO punches 24" of leader? */ |
| } |
| /* |
| * handle multiple tapes concatenated into one file!! |
| * have command line option?? treat "start" as EOF?? |
| */ |
| list_title_set = FALSE; |
| return FALSE; |
| |
| case TEXT: |
| /* NOTE!! no next()! */ |
| text(); |
| break; |
| |
| case NOINPUT: |
| next(0); /* Skip symbol */ |
| noinput = TRUE; |
| break; |
| |
| case EXPUNGE: |
| next(0); /* Skip symbol */ |
| if (pass == 1) |
| init_symtab(); |
| break; |
| |
| default: |
| break; |
| } /* end switch for pseudo-ops */ |
| return TRUE; /* keep scanning */ |
| } /* pseudo */ |
| |
| |
| /* Function: errorLexeme */ |
| /* Synopsis: Display an error message using the current lexical element. */ |
| void errorLexeme( EMSG_T *mesg, WORD32 col ) |
| { |
| char name[SYMLEN]; |
| |
| errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); |
| } /* errorLexeme */ |
| |
| |
| /* Function: errorSymbol */ |
| /* Synopsis: Display an error message with a given string. */ |
| void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) |
| { |
| char linecol[12]; |
| char *s; |
| |
| if( pass == 2 ) |
| { |
| s = ( name == NULL ) ? "" : name ; |
| errors++; |
| sprintf( linecol, ":%d:%d", lineno, col + 1 ); |
| fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", |
| filename, linecol, mesg->file, s, clc ); |
| saveError( mesg->list, col ); |
| } |
| error_in_line = TRUE; |
| } /* errorSymbol */ |
| |
| |
| /* Function: errorMessage */ |
| /* Synopsis: Display an error message without a name argument. */ |
| void errorMessage( EMSG_T *mesg, WORD32 col ) |
| { |
| char linecol[12]; |
| |
| if( pass == 2 ) |
| { |
| errors++; |
| sprintf( linecol, ":%d:%d", lineno, col + 1 ); |
| fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", |
| filename, linecol, mesg->file, clc ); |
| saveError( mesg->list, col ); |
| } |
| error_in_line = TRUE; |
| } /* errorMessage */ |
| |
| /* Function: saveError */ |
| /* Synopsis: Save the current error in a list so it may displayed after the */ |
| /* the current line is printed. */ |
| void saveError( char *mesg, WORD32 col ) |
| { |
| if( save_error_count < DIM( error_list )) |
| { |
| error_list[save_error_count].mesg = mesg; |
| error_list[save_error_count].col = col; |
| save_error_count++; |
| } |
| error_in_line = TRUE; |
| |
| if( listed ) |
| printErrorMessages(); |
| } /* saveError */ |
| |
| /* create a "symbol punch" for DDT */ |
| /* MUST be called after object file closed; we reuse the FILE*! */ |
| |
| void |
| dump_symbols(void) { |
| int ix; |
| WORD32 addr; |
| |
| objectfile = fopen( sympathname, "wb" ); |
| if (!objectfile) { |
| perror(sympathname); |
| return; |
| } |
| |
| punchLeader(0); |
| punchLoader(); |
| punchLeader(5); |
| |
| /* XXX fudge addr -- get count, and subtract 2N from 07750? */ |
| addr = 05000; |
| |
| for( ix = 0; ix < symbol_top; ix++ ) { |
| int i, type; |
| WORD32 name; |
| |
| type = symtab[ix].type; |
| if (M_FIXED(type) || M_PSEUDO(type) || M_MACRO(type)) |
| continue; |
| |
| name = 0; |
| for (i = 0; i < 3; i++) { |
| char c; |
| |
| c = symtab[ix].name[i]; |
| /* XXX leave on NUL? */ |
| |
| c = ascii_to_fiodec[tolower(c) & 0177]; |
| /* XXX check for BAD entries? */ |
| |
| /* XXX OR in val<<(3-i)*6?? */ |
| name <<= 6; |
| name |= c & CHARBITS; |
| } |
| punchLocObject(addr++, permute(name)); |
| punchLocObject(addr++, symtab[ix].val); |
| } |
| flushLoader(); |
| punchTriplet( JMP ); /* ??? */ |
| punchLeader(0); |
| fclose(objectfile); |
| } |