mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-18 09:03:39 +00:00
1) The readme is out-of-date, and unreadable on github 2) Some tools have their own directories, some don't 3) Many tools have neither readme nor descriptions. 4) Some files are misplaced This reorganizes so that each tool has its own directory, even if it only has a single file (Hint: If you use a tool, please add/update READMEs) The master README is complete, and readable on github The tools are in alphabetical order within category. There are some cases where this probably isn't the right thing to do, e.g. where there are separate tools that do "to" and "from" conversions. Each tool has at least a 1-line description in the master readme This commit does not change any tool.
3069 lines
81 KiB
C
3069 lines
81 KiB
C
/*
|
||
* $Id: macro1.c,v 1.74 2003/10/23 23:29:17 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.
|
||
* 8Mar97 RMS MACRO7 released w/ sim8swre
|
||
* 13Mar97 RMS MACRO1 released w/ lispswre
|
||
* 28Nov01 RMS MACRO1 released w/ simtools
|
||
* 5Mar03 DP MACRO1 released w/ ddt1
|
||
* 2003 PLB major reworking, assembles MACRO, DDT
|
||
*/
|
||
|
||
|
||
#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);
|
||
}
|