blob: ec9ff42f311827cd5ddaa990affb199afd365a84 [file] [log] [blame] [raw]
/*
* $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( &not_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( &not_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( &macro_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 (&macro_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);
}