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