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