| 1 | /******************************************************************************/ |
| 2 | /* */ |
| 3 | /* Program: PAL (BART version) */ |
| 4 | /* File: pal.c */ |
| 5 | /* Author: Gary A. Messenbrink <gary@netcom.com> */ |
| 6 | /* */ |
| 7 | /* Purpose: A 2 pass PDP-8 pal-like assembler. */ |
| 8 | /* */ |
| 9 | /* PAL(1) */ |
| 10 | /* */ |
| 11 | /* NAME */ |
| 12 | /* pal - a PDP/8 pal-like assembler. */ |
| 13 | /* */ |
| 14 | /* SYNOPSIS: */ |
| 15 | /* pal [ -d -l -p -r -x ] inputfile */ |
| 16 | /* */ |
| 17 | /* DESCRIPTION */ |
| 18 | /* This is a cross-assembler to for PDP/8 assembly language programs. */ |
| 19 | /* It will produce an output file in bin format, rim format, and using the */ |
| 20 | /* appropriate pseudo-ops, a combination of rim and bin formats. */ |
| 21 | /* A listing file is always produced and with an optional symbol table */ |
| 22 | /* and/or a symbol cross-reference (concordance). The permanent symbol */ |
| 23 | /* table can be output in a form that may be read back in so a customized */ |
| 24 | /* permanent symbol table can be produced. Any detected errors are output */ |
| 25 | /* to a separate file giving the filename in which they were detected */ |
| 26 | /* along with the line number, column number and error message as well as */ |
| 27 | /* marking the error in the listing file. */ |
| 28 | /* The following file name extensions are used: */ |
| 29 | /* .pal source code (input) */ |
| 30 | /* .lst assembly listing (output) */ |
| 31 | /* .bin assembly output in DEC's bin format (output) */ |
| 32 | /* .rim assembly output in DEC's rim format (output) */ |
| 33 | /* .err assembly errors detected (if any) (output) */ |
| 34 | /* .prm permanent symbol table in form suitable for reading after */ |
| 35 | /* the EXPUNGE pseudo-op. */ |
| 36 | /* */ |
| 37 | /* OPTIONS */ |
| 38 | /* -d Dump the symbol table at end of assembly */ |
| 39 | /* -l Allow generation of literals (default is no literal generation) */ |
| 40 | /* -p Generate a file with the permanent symbols in it. */ |
| 41 | /* (To get the current symbol table, assemble a file than has only */ |
| 42 | /* a $ in it.) */ |
| 43 | /* -r Produce output in rim format (default is bin format) */ |
| 44 | /* -x Generate a cross-reference (concordance) of user symbols. */ |
| 45 | /* */ |
| 46 | /* DIAGNOSTICS */ |
| 47 | /* Assembler error diagnostics are output to an error file and inserted */ |
| 48 | /* in the listing file. Each line in the error file has the form */ |
| 49 | /* */ |
| 50 | /* <filename>(<line>:<col>) : error: <message> at Loc = <loc> */ |
| 51 | /* */ |
| 52 | /* An example error message is: */ |
| 53 | /* */ |
| 54 | /* bintst.pal(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ |
| 55 | /* */ |
| 56 | /* The error diagnostics put in the listing start with a two character */ |
| 57 | /* error code (if appropriate) and a short message. A carat '^' is */ |
| 58 | /* placed under the item in error if appropriate. */ |
| 59 | /* An example error message is: */ |
| 60 | /* */ |
| 61 | /* 17 07616 3000 DCA UNDEF */ |
| 62 | /* UD undefined ^ */ |
| 63 | /* 18 07617 1777 TAD I DUMMY */ |
| 64 | /* */ |
| 65 | /* When an indirect is generated, an at character '@' is placed after the */ |
| 66 | /* the instruction value in the listing as an indicator as follows: */ |
| 67 | /* */ |
| 68 | /* 14 03716 1777@ TAD OFFPAG */ |
| 69 | /* */ |
| 70 | /* Undefined symbols are marked in the symbol table listing by prepending */ |
| 71 | /* a '?' to the symbol. Redefined symbols are marked in the symbol table */ |
| 72 | /* listing by prepending a '#' to the symbol. Examples are: */ |
| 73 | /* */ |
| 74 | /* #REDEF 04567 */ |
| 75 | /* SWITCH 07612 */ |
| 76 | /* ?UNDEF 00000 */ |
| 77 | /* */ |
| 78 | /* Refer to the code for the diagnostic messages generated. */ |
| 79 | /* */ |
| 80 | /* BUGS */ |
| 81 | /* Only a minimal effort has been made to keep the listing format */ |
| 82 | /* anything like the PAL-8 listing format. */ |
| 83 | /* The operation of the conditional assembly pseudo-ops may not function */ |
| 84 | /* exactly as the DEC versions. I did not have any examples of these so */ |
| 85 | /* the implementation is my interpretation of how they should work. */ |
| 86 | /* */ |
| 87 | /* The RIMPUNch and BINPUNch pseudo-ops do not change the binary output */ |
| 88 | /* file type that was specified on startup. This was intentional and */ |
| 89 | /* and allows rim formatted data to be output prior to the actual binary */ |
| 90 | /* formatted data. On UN*X style systems, the same effect can be achieved */ |
| 91 | /* by using the "cat" command, but on DOS/Windows systems, doing this was */ |
| 92 | /* a major chore. */ |
| 93 | /* */ |
| 94 | /* The floating point input does not generate values exactly as the DEC */ |
| 95 | /* compiler does. I worked out several examples by hand and believe that */ |
| 96 | /* this implementation is slightly more accurate. If I am mistaken, */ |
| 97 | /* let me know and, if possible, a better method of generating the values. */ |
| 98 | /* */ |
| 99 | /* BUILD and INSTALLATION */ |
| 100 | /* This program has been built and successfully executed on: */ |
| 101 | /* a. Linux (80486 CPU)using gcc */ |
| 102 | /* b. RS/6000 (AIX 3.2.5) */ |
| 103 | /* c. Borland C++ version 3.1 (large memory model) */ |
| 104 | /* d. Borland C++ version 4.52 (large memory model) */ |
| 105 | /* with no modifications to the source code. */ |
| 106 | /* */ |
| 107 | /* On UNIX type systems, store the the program as the pal command */ |
| 108 | /* and on PC type systems, store it as pal.exe */ |
| 109 | /* */ |
| 110 | /* HISTORICAL NOTE: */ |
| 111 | /* This assembler was written to support the fleet of PDP-8 systems */ |
| 112 | /* used by the Bay Area Rapid Transit System. As of early 1997, */ |
| 113 | /* this includes about 40 PDP-8/E systems driving the train destination */ |
| 114 | /* signs in passenger stations. */ |
| 115 | /* */ |
| 116 | /* REFERENCES: */ |
| 117 | /* This assembler is based on the pal assember by: */ |
| 118 | /* Douglas Jones <jones@cs.uiowa.edu> and */ |
| 119 | /* Rich Coon <coon@convexw.convex.com> */ |
| 120 | /* */ |
| 121 | /* DISCLAIMER: */ |
| 122 | /* See the symbol table for the set of pseudo-ops supported. */ |
| 123 | /* See the code for pseudo-ops that are not standard for PDP/8 assembly. */ |
| 124 | /* Refer to DEC's "Programming Languages (for the PDP/8)" for complete */ |
| 125 | /* documentation of pseudo-ops. */ |
| 126 | /* Refer to DEC's "Introduction to Programming (for the PDP/8)" or a */ |
| 127 | /* lower level introduction to the assembly language. */ |
| 128 | /* */ |
| 129 | /* WARRANTY: */ |
| 130 | /* If you don't like it the way it works or if it doesn't work, that's */ |
| 131 | /* tough. You're welcome to fix it yourself. That's what you get for */ |
| 132 | /* using free software. */ |
| 133 | /* */ |
| 134 | /* COPYRIGHT NOTICE: */ |
| 135 | /* This is free software. There is no fee for using it. You may make */ |
| 136 | /* any changes that you wish and also give it away. If you can make */ |
| 137 | /* a commercial product out of it, fine, but do not put any limits on */ |
| 138 | /* the purchaser's right to do the same. If you improve it or fix any */ |
| 139 | /* bugs, it would be nice if you told me and offered me a copy of the */ |
| 140 | /* new version. */ |
| 141 | /* */ |
| 142 | /* */ |
| 143 | /* Amendments Record: */ |
| 144 | /* Version Date by Comments */ |
| 145 | /* ------- ------- --- --------------------------------------------------- */ |
| 146 | /* v1.0 12Apr96 GAM Original */ |
| 147 | /* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ |
| 148 | /* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ |
| 149 | /* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ |
| 150 | /* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ |
| 151 | /* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ |
| 152 | /* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ |
| 153 | /* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ |
| 154 | /* */ |
| 155 | /******************************************************************************/ |
| 156 | |
| 157 | #include <ctype.h> |
| 158 | #include <stdio.h> |
| 159 | #include <stdlib.h> |
| 160 | #include <string.h> |
| 161 | |
| 162 | #define LINELEN 96 |
| 163 | #define LIST_LINES_PER_PAGE 55 /* Includes 5 line page header. */ |
| 164 | #define NAMELEN 128 |
| 165 | #define SYMBOL_COLUMNS 5 |
| 166 | #define SYMLEN 7 |
| 167 | #define SYMBOL_TABLE_SIZE 1024 |
| 168 | #define TITLELEN 63 |
| 169 | #define XREF_COLUMNS 8 |
| 170 | |
| 171 | #define ADDRESS_FIELD 00177 |
| 172 | #define FIELD_FIELD 070000 |
| 173 | #define INDIRECT_BIT 00400 |
| 174 | #define LAST_PAGE_LOC 00177 |
| 175 | #define OP_CODE 07000 |
| 176 | #define PAGE_BIT 00200 |
| 177 | |
| 178 | #ifdef PAGE_SIZE |
| 179 | #undef PAGE_SIZE |
| 180 | #endif |
| 181 | #define PAGE_SIZE 00200 |
| 182 | |
| 183 | #define PAGE_FIELD 07600 |
| 184 | #define PAGE_ZERO_END 00200 |
| 185 | |
| 186 | /* Macro to get the number of elements in an array. */ |
| 187 | #define DIM(a) (sizeof(a)/sizeof(a[0])) |
| 188 | |
| 189 | /* Macro to get the address plus one of the end of an array. */ |
| 190 | #define BEYOND(a) ((a) + DIM(A)) |
| 191 | |
| 192 | #define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>')) |
| 193 | #define isend(c) ((c=='\0')|| (c=='\n')) |
| 194 | #define isdone(c) ((c=='/') || (isend(c)) || (c==';')) |
| 195 | |
| 196 | /* Macros for testing symbol attributes. Each macro evaluates to non-zero */ |
| 197 | /* (true) if the stated condtion is met. */ |
| 198 | /* Use these to test attributes. The proper bits are extracted and then */ |
| 199 | /* tested. */ |
| 200 | #define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) |
| 201 | #define M_DEFINED(s) ((s & DEFINED) == DEFINED) |
| 202 | #define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) |
| 203 | #define M_FIXED(s) ((s & FIXED) == FIXED) |
| 204 | #define M_LABEL(s) ((s & LABEL) == LABEL) |
| 205 | #define M_MRI(s) ((s & MRI) == MRI) |
| 206 | #define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) |
| 207 | #define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) |
| 208 | #define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) |
| 209 | #define M_UNDEFINED(s) (!M_DEFINED(s)) |
| 210 | |
| 211 | /* This macro is used to test symbols by the conditional assembly pseudo-ops. */ |
| 212 | #define M_DEF(s) (M_DEFINED(s)) |
| 213 | #define M_COND(s) (M_DEFINED(s)) |
| 214 | #define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) |
| 215 | |
| 216 | typedef unsigned char BOOL; |
| 217 | typedef unsigned char BYTE; |
| 218 | typedef short int WORD16; |
| 219 | typedef long int WORD32; |
| 220 | |
| 221 | #ifndef FALSE |
| 222 | #define FALSE 0 |
| 223 | #define TRUE (!FALSE) |
| 224 | #endif |
| 225 | |
| 226 | /* Line listing styles. Used to control listing of lines. */ |
| 227 | enum linestyle_t |
| 228 | { |
| 229 | LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL |
| 230 | }; |
| 231 | typedef enum linestyle_t LINESTYLE_T; |
| 232 | |
| 233 | /* Symbol Types. */ |
| 234 | /* Note that the names that have FIX as the suffix contain the FIXED bit */ |
| 235 | /* included in the value. */ |
| 236 | /* */ |
| 237 | /* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ |
| 238 | /* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ |
| 239 | /* defined or undefined. The condition bit is set when the symbol is defined */ |
| 240 | /* during pass 1 and reset on pass 2 at the location the symbol was defined */ |
| 241 | /* during pass 1. When processing conditionals during pass 2, if the symbol */ |
| 242 | /* is defined and the condition bit is set, the symbol is treated as if it */ |
| 243 | /* were undefined. This gives consistent behavior of the conditional */ |
| 244 | /* pseudo-ops during both pass 1 and pass 2. */ |
| 245 | enum symtyp |
| 246 | { |
| 247 | UNDEFINED = 0000, |
| 248 | DEFINED = 0001, |
| 249 | FIXED = 0002, |
| 250 | MRI = 0004 | DEFINED, |
| 251 | LABEL = 0010 | DEFINED, |
| 252 | REDEFINED = 0020 | DEFINED, |
| 253 | DUPLICATE = 0040 | DEFINED, |
| 254 | PSEUDO = 0100 | FIXED | DEFINED, |
| 255 | CONDITION = 0200 | DEFINED, |
| 256 | MRIFIX = MRI | FIXED | DEFINED, |
| 257 | DEFFIX = DEFINED | FIXED |
| 258 | }; |
| 259 | typedef enum symtyp SYMTYP; |
| 260 | |
| 261 | enum pseudo_t |
| 262 | { |
| 263 | BANK, BINPUNCH, DECIMAL, DUBL, EJECT, ENPUNCH, EXPUNGE, FIELD, |
| 264 | FIXMRI, FIXTAB, FLTG, IFDEF, IFNDEF, IFNZERO, IFZERO, NOPUNCH, |
| 265 | OCTAL, PAGE, PAUSE, RELOC, RIMPUNCH, SEGMNT, TEXT, TITLE, |
| 266 | XLIST, ZBLOCK |
| 267 | }; |
| 268 | typedef enum pseudo_t PSEUDO_T; |
| 269 | |
| 270 | struct sym_t |
| 271 | { |
| 272 | SYMTYP type; |
| 273 | char name[SYMLEN]; |
| 274 | WORD16 val; |
| 275 | WORD16 xref_index; |
| 276 | WORD16 xref_count; |
| 277 | }; |
| 278 | typedef struct sym_t SYM_T; |
| 279 | |
| 280 | struct lpool_t |
| 281 | { |
| 282 | WORD16 error; /* True if error message has been printed. */ |
| 283 | WORD16 loc; |
| 284 | WORD16 pool[PAGE_SIZE]; |
| 285 | }; |
| 286 | typedef struct lpool_t LPOOL_T; |
| 287 | |
| 288 | struct emsg_t |
| 289 | { |
| 290 | char *list; |
| 291 | char *file; |
| 292 | }; |
| 293 | typedef struct emsg_t EMSG_T; |
| 294 | |
| 295 | struct errsave_t |
| 296 | { |
| 297 | char *mesg; |
| 298 | WORD16 col; |
| 299 | }; |
| 300 | typedef struct errsave_t ERRSAVE_T; |
| 301 | |
| 302 | struct fltg_ |
| 303 | { |
| 304 | WORD16 exponent; |
| 305 | WORD32 mantissa; |
| 306 | }; |
| 307 | typedef struct fltg_ FLTG_T; |
| 308 | |
| 309 | /*----------------------------------------------------------------------------*/ |
| 310 | |
| 311 | /* Function Prototypes */ |
| 312 | |
| 313 | int binarySearch( char *name, int start, int symbol_count ); |
| 314 | int compareSymbols( const void *a, const void *b ); |
| 315 | void conditionFalse( void ); |
| 316 | void conditionTrue( void ); |
| 317 | SYM_T *defineLexeme( WORD16 start, WORD16 term, WORD16 val, SYMTYP type ); |
| 318 | SYM_T *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start); |
| 319 | void endOfBinary( void ); |
| 320 | void errorLexeme( EMSG_T *mesg, WORD16 col ); |
| 321 | void errorMessage( EMSG_T *mesg, WORD16 col ); |
| 322 | void errorSymbol( EMSG_T *mesg, char *name, WORD16 col ); |
| 323 | SYM_T *eval( void ); |
| 324 | WORD32 evalDubl( WORD32 initial_value ); |
| 325 | FLTG_T *evalFltg( void ); |
| 326 | SYM_T *evalSymbol( void ); |
| 327 | void getArgs( int argc, char *argv[] ); |
| 328 | WORD32 getDublExpr( void ); |
| 329 | WORD32 getDublExprs( void ); |
| 330 | FLTG_T *getFltgExpr( void ); |
| 331 | FLTG_T *getFltgExprs( void ); |
| 332 | SYM_T *getExpr( void ); |
| 333 | WORD16 getExprs( void ); |
| 334 | WORD16 incrementClc( void ); |
| 335 | void inputDubl( void ); |
| 336 | void inputFltg( void ); |
| 337 | WORD16 insertLiteral( LPOOL_T *pool, WORD16 value ); |
| 338 | char *lexemeToName( char *name, WORD16 from, WORD16 term ); |
| 339 | void listLine( void ); |
| 340 | SYM_T *lookup( char *name ); |
| 341 | void moveToEndOfLine( void ); |
| 342 | void nextLexBlank( void ); |
| 343 | void nextLexeme( void ); |
| 344 | void normalizeFltg( FLTG_T *fltg ); |
| 345 | void onePass( void ); |
| 346 | void printCrossReference( void ); |
| 347 | void printErrorMessages( void ); |
| 348 | void printLine(char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle); |
| 349 | void printPageBreak( void ); |
| 350 | void printPermanentSymbolTable( void ); |
| 351 | void printSymbolTable( void ); |
| 352 | BOOL pseudoOperators( PSEUDO_T val ); |
| 353 | void punchChecksum( void ); |
| 354 | void punchLocObject( WORD16 loc, WORD16 val ); |
| 355 | void punchLiteralPool( LPOOL_T *p, WORD16 lpool_page ); |
| 356 | void punchOutObject( WORD16 loc, WORD16 val ); |
| 357 | void punchLeader( WORD16 count ); |
| 358 | void punchObject( WORD16 val ); |
| 359 | void punchOrigin( WORD16 loc ); |
| 360 | void readLine( void ); |
| 361 | void saveError( char *mesg, WORD16 cc ); |
| 362 | BOOL testForLiteralCollision( WORD16 loc ); |
| 363 | void topOfForm( char *title, char *sub_title ); |
| 364 | |
| 365 | /*----------------------------------------------------------------------------*/ |
| 366 | |
| 367 | /* Table of pseudo-ops (directives) which are used to setup the symbol */ |
| 368 | /* table on startup and when the EXPUNGE pseudo-op is executed. */ |
| 369 | SYM_T pseudo[] = |
| 370 | { |
| 371 | { PSEUDO, "BANK", BANK }, /* Like field, select some 32K out of 128K*/ |
| 372 | { PSEUDO, "BINPUN", BINPUNCH }, /* Output in Binary Loader format. */ |
| 373 | { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ |
| 374 | { PSEUDO, "DUBL", DUBL }, /* Ignored (unsupported). */ |
| 375 | { PSEUDO, "EJECT", EJECT }, /* Eject a page in the listing. */ |
| 376 | { PSEUDO, "ENPUNC", ENPUNCH }, /* Turn on object code generation. */ |
| 377 | { PSEUDO, "EXPUNG", EXPUNGE }, /* Remove all symbols from symbol table. */ |
| 378 | { PSEUDO, "FIELD", FIELD }, /* Set origin to memory field. */ |
| 379 | { PSEUDO, "FIXMRI", FIXMRI }, /* Like =, but creates mem ref instruction*/ |
| 380 | { PSEUDO, "FIXTAB", FIXTAB }, /* Mark current symbols as permanent. */ |
| 381 | { PSEUDO, "FLTG", FLTG }, /* Ignored (unsupported). */ |
| 382 | { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ |
| 383 | { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ |
| 384 | { PSEUDO, "IFNZER", IFNZERO }, /* Assemble if symbol value is not 0. */ |
| 385 | { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ |
| 386 | { PSEUDO, "NOPUNC", NOPUNCH }, /* Turn off object code generation. */ |
| 387 | { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ |
| 388 | { PSEUDO, "PAGE", PAGE }, /* Set orign to page +1 or page n (0..37).*/ |
| 389 | { PSEUDO, "PAUSE", PAUSE }, /* Ignored */ |
| 390 | { PSEUDO, "RELOC", RELOC }, /* Assemble to run at a different address.*/ |
| 391 | { PSEUDO, "RIMPUN", RIMPUNCH }, /* Output in Read In Mode format. */ |
| 392 | { PSEUDO, "SEGMNT", SEGMNT }, /* Like page, but with page size=1K words.*/ |
| 393 | { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ |
| 394 | { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ |
| 395 | { PSEUDO, "XLIST", XLIST }, /* Toggle listing generation. */ |
| 396 | { PSEUDO, "ZBLOCK", ZBLOCK } /* Zero a block of memory. */ |
| 397 | }; |
| 398 | |
| 399 | /* Symbol Table */ |
| 400 | /* The table is put in lexical order on startup, so symbols can be */ |
| 401 | /* inserted as desired into the initial table. */ |
| 402 | SYM_T permanent_symbols[] = |
| 403 | { |
| 404 | /* Memory Reference Instructions */ |
| 405 | { MRIFIX, "AND", 00000 }, /* LOGICAL AND */ |
| 406 | { MRIFIX, "TAD", 01000 }, /* TWO'S COMPLEMENT ADD */ |
| 407 | { MRIFIX, "ISZ", 02000 }, /* INCREMENT AND SKIP IF ZERO */ |
| 408 | { MRIFIX, "DCA", 03000 }, /* DEPOSIT AND CLEAR ACC */ |
| 409 | { MRIFIX, "I", 00400 }, /* INDIRECT ADDRESSING */ |
| 410 | { MRIFIX, "JMP", 05000 }, /* JUMP */ |
| 411 | { MRIFIX, "JMS", 04000 }, /* JUMP TO SUBROUTINE */ |
| 412 | { MRIFIX, "Z", 00000 }, /* PAGE ZERO ADDRESS */ |
| 413 | /* Floating Point Interpreter Instructions */ |
| 414 | { MRIFIX, "FEXT", 00000 }, /* FLOATING EXIT */ |
| 415 | { MRIFIX, "FADD", 01000 }, /* FLOATING ADD */ |
| 416 | { MRIFIX, "FSUB", 02000 }, /* FLOATING SUBTRACT */ |
| 417 | { MRIFIX, "FMPY", 03000 }, /* FLOATING MULTIPLY */ |
| 418 | { MRIFIX, "FDIV", 04000 }, /* FLOATING DIVIDE */ |
| 419 | { MRIFIX, "FGET", 05000 }, /* FLOATING GET */ |
| 420 | { MRIFIX, "FPUT", 06000 }, /* FLOATING PUT */ |
| 421 | { FIXED, "FNOR", 07000 }, /* FLOATING NORMALIZE */ |
| 422 | { FIXED, "FEXT", 00000 }, /* EXIT FROM FLOATING POINT INTERPRETER */ |
| 423 | { FIXED, "SQUARE", 00001 }, /* SQUARE C(FAC) */ |
| 424 | { FIXED, "SQROOT", 00002 }, /* TAKE SQUARE ROOT OF C(FAC) */ |
| 425 | /* Group 1 Operate Microinstrcutions */ |
| 426 | { FIXED, "NOP", 07000 }, /* NO OPERATION */ |
| 427 | { FIXED, "IAC", 07001 }, /* INCREMENT AC */ |
| 428 | { FIXED, "RAL", 07004 }, /* ROTATE AC AND LINK LEFT ONE */ |
| 429 | { FIXED, "RTL", 07006 }, /* ROTATE AC AND LINK LEFT TWO */ |
| 430 | { FIXED, "RAR", 07010 }, /* ROTATE AC AND LINK RIGHT ONE */ |
| 431 | { FIXED, "RTR", 07012 }, /* ROTATE AC AND LINK RIGHT TWO */ |
| 432 | { FIXED, "CML", 07020 }, /* COMPLEMENT LINK */ |
| 433 | { FIXED, "CMA", 07040 }, /* COMPLEMEMNT AC */ |
| 434 | { FIXED, "CLL", 07100 }, /* CLEAR LINK */ |
| 435 | { FIXED, "CLA", 07200 }, /* CLEAR AC */ |
| 436 | /* Group 2 Operate Microinstructions */ |
| 437 | { FIXED, "BSW", 07002 }, /* Swap bytes in AC (PDP/8e) */ |
| 438 | { FIXED, "HLT", 07402 }, /* HALT THE COMPUTER */ |
| 439 | { FIXED, "OSR", 07404 }, /* INCLUSIVE OR SR WITH AC */ |
| 440 | { FIXED, "SKP", 07410 }, /* SKIP UNCONDITIONALLY */ |
| 441 | { FIXED, "SNL", 07420 }, /* SKIP ON NON-ZERO LINK */ |
| 442 | { FIXED, "SZL", 07430 }, /* SKIP ON ZERO LINK */ |
| 443 | { FIXED, "SZA", 07440 }, /* SKIP ON ZERO AC */ |
| 444 | { FIXED, "SNA", 07450 }, /* SKIP ON NON=ZERO AC */ |
| 445 | { FIXED, "SMA", 07500 }, /* SKIP MINUS AC */ |
| 446 | { FIXED, "SPA", 07510 }, /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */ |
| 447 | /* Combined Operate Microinstructions */ |
| 448 | { FIXED, "CIA", 07041 }, /* COMPLEMENT AND INCREMENT AC */ |
| 449 | { FIXED, "STL", 07120 }, /* SET LINK TO 1 */ |
| 450 | { FIXED, "GLK", 07204 }, /* GET LINK (PUT LINK IN AC BIT 11) */ |
| 451 | { FIXED, "STA", 07240 }, /* SET AC TO -1 */ |
| 452 | { FIXED, "LAS", 07604 }, /* LOAD ACC WITH SR */ |
| 453 | /* MQ Instructions (PDP/8e) */ |
| 454 | { FIXED, "MQL", 07421 }, /* Load MQ from AC, then clear AC. */ |
| 455 | { FIXED, "MQA", 07501 }, /* Inclusive OR MQ with AC */ |
| 456 | { FIXED, "SWP", 07521 }, /* Swap AC and MQ */ |
| 457 | { FIXED, "ACL", 07701 }, /* Load MQ into AC */ |
| 458 | /* Program Interrupt */ |
| 459 | { FIXED, "IOT", 06000 }, |
| 460 | { FIXED, "ION", 06001 }, /* TURN INTERRUPT PROCESSOR ON */ |
| 461 | { FIXED, "IOF", 06002 }, /* TURN INTERRUPT PROCESSOR OFF */ |
| 462 | /* Program Interrupt, PDP-8/e */ |
| 463 | { FIXED, "SKON", 06000 }, /* Skip if interrupt on and turn int off. */ |
| 464 | { FIXED, "SRQ", 06003 }, /* Skip on interrupt request. */ |
| 465 | { FIXED, "GTF", 06004 }, /* Get interrupt flags. */ |
| 466 | { FIXED, "RTF", 06005 }, /* Restore interrupt flags. */ |
| 467 | { FIXED, "SGT", 06006 }, /* Skip on greater than flag. */ |
| 468 | { FIXED, "CAF", 06007 }, /* Clear all flags. */ |
| 469 | /* Keyboard/Reader */ |
| 470 | { FIXED, "KSF", 06031 }, /* SKIP ON KEYBOARD FLAG */ |
| 471 | { FIXED, "KCC", 06032 }, /* CLEAR KEYBOARD FLAG */ |
| 472 | { FIXED, "KRS", 06034 }, /* READ KEYBOARD BUFFER (STATIC) */ |
| 473 | { FIXED, "KRB", 06036 }, /* READ KEYBOARD BUFFER & CLEAR FLAG */ |
| 474 | /* Teleprinter/Punch */ |
| 475 | { FIXED, "TSF", 06041 }, /* SKIP ON TELEPRINTER FLAG */ |
| 476 | { FIXED, "TCF", 06042 }, /* CLEAR TELEPRINTER FLAG */ |
| 477 | { FIXED, "TPC", 06044 }, /* LOAD TELEPRINTER & PRINT */ |
| 478 | { FIXED, "TLS", 06046 }, /* LOAD TELPRINTER & CLEAR FLAG */ |
| 479 | /* High Speed Paper Tape Reader */ |
| 480 | { FIXED, "RSF", 06011 }, /* SKIP ON READER FLAG */ |
| 481 | { FIXED, "RRB", 06012 }, /* READ READER BUFFER AND CLEAR FLAG */ |
| 482 | { FIXED, "RFC", 06014 }, /* READER FETCH CHARACTER */ |
| 483 | /* PC8-E High Speed Paper Tape Reader & Punch */ |
| 484 | { FIXED, "RPE", 06010 }, /* Set interrupt enable for reader/punch */ |
| 485 | { FIXED, "PCE", 06020 }, /* Clear interrupt enable for rdr/punch */ |
| 486 | { FIXED, "RCC", 06016 }, /* Read reader buffer, clear flags & buf, */ |
| 487 | /* and fetch character. */ |
| 488 | /* High Speed Paper Tape Punch */ |
| 489 | { FIXED, "PSF", 06021 }, /* SKIP ON PUNCH FLAG */ |
| 490 | { FIXED, "PCF", 06022 }, /* CLEAR ON PUNCH FLAG */ |
| 491 | { FIXED, "PPC", 06024 }, /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */ |
| 492 | { FIXED, "PLS", 06026 }, /* LOAD PUNCH BUFFER AND CLEAR FLAG */ |
| 493 | /* DECtape Transport Type TU55 and DECtape Control Type TC01 */ |
| 494 | { FIXED, "DTRA", 06761 }, /* Contents of status register is ORed */ |
| 495 | /* into AC bits 0-9 */ |
| 496 | { FIXED, "DTCA", 06762 }, /* Clear status register A, all flags */ |
| 497 | /* undisturbed */ |
| 498 | { FIXED, "DTXA", 06764 }, /* Status register A loaded by exclusive */ |
| 499 | /* OR from AC. If AC bit 10=0, clear */ |
| 500 | /* error flags; if AC bit 11=0, DECtape */ |
| 501 | /* control flag is cleared. */ |
| 502 | { FIXED, "DTLA", 06766 }, /* Combination of DTCA and DTXA */ |
| 503 | { FIXED, "DTSF", 06771 }, /* Skip if error flag is 1 or if DECtape */ |
| 504 | /* control flag is 1 */ |
| 505 | { FIXED, "DTRB", 06772 }, /* Contents of status register B is */ |
| 506 | /* ORed into AC */ |
| 507 | { FIXED, "DTLB", 06774 }, /* Memory field portion of status */ |
| 508 | /* register B loaded from AC bits 6-8 */ |
| 509 | /* Disk File and Control, Type DF32 */ |
| 510 | { FIXED, "DCMA", 06601 }, /* CLEAR DISK MEMORY REQUEST AND */ |
| 511 | /* INTERRUPT FLAGS */ |
| 512 | { FIXED, "DMAR", 06603 }, /* LOAD DISK FROM AC, CLEAR AC READ */ |
| 513 | /* INTO CORE, CLEAR INTERRUPT FLAG */ |
| 514 | { FIXED, "DMAW", 06605 }, /* LOAD DISK FROM AC, WRITE ONTO DISK */ |
| 515 | /* FROM CORE, CLEAR INTERRUPT FLAG */ |
| 516 | { FIXED, "DCEA", 06611 }, /* CLEAR DISK EXTENDED ADDRESS AND */ |
| 517 | { FIXED, "DSAC", 06612 }, /* SKIP IF ADDRESS CONFIRMED FLAG = 1 */ |
| 518 | /* MEMORY ADDRESS EXTENSION REGISTER */ |
| 519 | { FIXED, "DEAL", 06615 }, /* CLEAR DISK EXTENDED ADDRESS AND */ |
| 520 | /* MEMORY ADDRESS EXTENSION REGISTER */ |
| 521 | /* AND LOAD SAME FROM AC */ |
| 522 | { FIXED, "DEAC", 06616 }, /* CLEAR AC, LOAD AC FROM DISK EXTENDED */ |
| 523 | /* ADDRESS REGISTER, SKIP IF ADDRESS */ |
| 524 | /* CONFIRMED FLAG = 1 */ |
| 525 | { FIXED, "DFSE", 06621 }, /* SKIP IF PARITY ERROR, DATA REQUEST */ |
| 526 | /* LATE, OR WRITE LOCK SWITCH FLAG = 0 */ |
| 527 | /* (NO ERROR) */ |
| 528 | { FIXED, "DFSC", 06622 }, /* SKIP IF COMPLETION FLAG = 1 (DATA */ |
| 529 | /* TRANSFER COMPLETE) */ |
| 530 | { FIXED, "DMAC", 06626 }, /* CLEAR AC, LOAD AC FROM DISK MEMORY */ |
| 531 | /* ADDRESS REGISTER */ |
| 532 | /* Disk File and Control, Type RF08 */ |
| 533 | { FIXED, "DCIM", 06611 }, |
| 534 | { FIXED, "DIML", 06615 }, |
| 535 | { FIXED, "DIMA", 06616 }, |
| 536 | { FIXED, "DISK", 06623 }, |
| 537 | { FIXED, "DCXA", 06641 }, |
| 538 | { FIXED, "DXAL", 06643 }, |
| 539 | { FIXED, "DXAC", 06645 }, |
| 540 | { FIXED, "DMMT", 06646 }, |
| 541 | /* Memory Extension Control, Type 183 */ |
| 542 | { FIXED, "CDF", 06201 }, /* CHANGE DATA FIELD */ |
| 543 | { FIXED, "CIF", 06202 }, /* CHANGE INSTRUCTION FIELD */ |
| 544 | { FIXED, "CDI", 06203 }, /* Change data & instrution field. */ |
| 545 | { FIXED, "RDF", 06214 }, /* READ DATA FIELD */ |
| 546 | { FIXED, "RIF", 06224 }, /* READ INSTRUCTION FIELD */ |
| 547 | { FIXED, "RIB", 06234 }, /* READ INTERRUPT BUFFER */ |
| 548 | { FIXED, "RMF", 06224 }, /* RESTORE MEMORY FIELD */ |
| 549 | /* Memory Parity, Type MP8/I (MP8/L) */ |
| 550 | { FIXED, "SMP", 06101 }, /* SKIP IF MEMORY PARITY FLAG = 0 */ |
| 551 | { FIXED, "CMP", 06104 }, /* CLEAR MEMORY PAIRTY FLAG */ |
| 552 | /* Memory Parity, Type MP8-E (PDP8/e) */ |
| 553 | { FIXED, "DPI", 06100 }, /* Disable parity interrupt. */ |
| 554 | { FIXED, "SNP", 06101 }, /* Skip if no parity error. */ |
| 555 | { FIXED, "EPI", 06103 }, /* Enable parity interrupt. */ |
| 556 | { FIXED, "CNP", 06104 }, /* Clear parity error flag. */ |
| 557 | { FIXED, "CEP", 06106 }, /* Check for even parity. */ |
| 558 | { FIXED, "SPO", 06107 }, /* Skip on parity option. */ |
| 559 | /* Data Communications Systems, Type 680I */ |
| 560 | { FIXED, "TTINCR", 06401 }, /* The content of the line select */ |
| 561 | /* register is incremented by one. */ |
| 562 | { FIXED, "TTI", 06402 }, /* The line status word is read and */ |
| 563 | /* sampled. If the line is active for */ |
| 564 | /* the fourth time, the line bit is */ |
| 565 | /* shifted into the character assembly */ |
| 566 | /* word. If the line bit is active for */ |
| 567 | /* a number of times less than four, */ |
| 568 | /* the count is incremented. If the */ |
| 569 | /* line is not active, the active/inac- */ |
| 570 | /* tive status of the line is recorded */ |
| 571 | { FIXED, "TTO", 06404 }, /* The character in the AC is shifted */ |
| 572 | /* right one position, zeros are shifted */ |
| 573 | /* into vacated positions, and the orig- */ |
| 574 | /* inal content of AC11 is transferred */ |
| 575 | /* out of the computer on the TTY line. */ |
| 576 | { FIXED, "TTCL", 06411 }, /* The line select register is cleared. */ |
| 577 | { FIXED, "TTSL", 06412 }, /* The line select register is loaded by */ |
| 578 | /* an OR transfer from the content of */ |
| 579 | /* of AC5-11, the the AC is cleared. */ |
| 580 | { FIXED, "TTRL", 06414 }, /* The content of the line select regis- */ |
| 581 | /* ter is read into AC5-11 by an OR */ |
| 582 | /* transfer. */ |
| 583 | { FIXED, "TTSKP", 06421 }, /* Skip if clock flag is a 1. */ |
| 584 | { FIXED, "TTXON", 06424 }, /* Clock 1 is enabled to request a prog- */ |
| 585 | /* ram interrupt and clock 1 flag is */ |
| 586 | /* cleared. */ |
| 587 | { FIXED, "TTXOF", 06422 }, /* Clock 1 is disabled from causing a */ |
| 588 | /* program interrupt and clock 1 flag */ |
| 589 | /* is cleared. */ |
| 590 | }; /* End-of-Symbols for Permanent Symbol Table */ |
| 591 | |
| 592 | /* Global variables */ |
| 593 | SYM_T *symtab; /* Symbol Table */ |
| 594 | int symbol_top; /* Number of entries in symbol table. */ |
| 595 | |
| 596 | SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ |
| 597 | int number_of_fixed_symbols; |
| 598 | |
| 599 | /*----------------------------------------------------------------------------*/ |
| 600 | |
| 601 | WORD16 *xreftab; /* Start of the concordance table. */ |
| 602 | |
| 603 | ERRSAVE_T error_list[20]; |
| 604 | int save_error_count; |
| 605 | |
| 606 | LPOOL_T pz; /* Storage for page zero constants. */ |
| 607 | LPOOL_T cp; /* Storage for current page constants. */ |
| 608 | |
| 609 | char s_detected[] = "detected"; |
| 610 | char s_error[] = "error"; |
| 611 | char s_errors[] = "errors"; |
| 612 | char s_no[] = "No"; |
| 613 | char s_page[] = "Page"; |
| 614 | char s_symtable[] = "Symbol Table"; |
| 615 | char s_xref[] = "Cross Reference"; |
| 616 | |
| 617 | /* Assembler diagnostic messages. */ |
| 618 | /* Some attempt has been made to keep continuity with the PAL-III and */ |
| 619 | /* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ |
| 620 | /* exists, then the indicator is put in the listing as the first two */ |
| 621 | /* characters of the diagnostic message. The PAL-III indicators where used */ |
| 622 | /* when there was a choice between using MACRO-8 and PAL-III indicators. */ |
| 623 | /* The character pairs and their meanings are: */ |
| 624 | /* DT Duplicate Tag (symbol) */ |
| 625 | /* IC Illegal Character */ |
| 626 | /* ID Illegal Redefinition of a symbol. An attempt was made to give */ |
| 627 | /* a symbol a new value not via =. */ |
| 628 | /* IE Illegal Equals An equal sign was used in the wrong context, */ |
| 629 | /* (e.g., A+B=C, or TAD A+=B) */ |
| 630 | /* II Illegal Indirect An off page reference was made, but a literal */ |
| 631 | /* could not be generated because the indirect bit was already set. */ |
| 632 | /* IR Illegal Reference (address is not on current page or page zero) */ |
| 633 | /* ND No $ (the program terminator) at end of file. */ |
| 634 | /* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ |
| 635 | /* RD ReDefintion of a symbol */ |
| 636 | /* ST Symbol Table full */ |
| 637 | /* UA Undefined Address (undefined symbol) */ |
| 638 | /* ZE Zero Page Exceeded (see above, or out of space) */ |
| 639 | EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; |
| 640 | EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; |
| 641 | EMSG_T illegal_character = { "IC illegal char", "illegal character" }; |
| 642 | EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; |
| 643 | EMSG_T label_syntax = { "IC label syntax", "label syntax" }; |
| 644 | EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; |
| 645 | EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; |
| 646 | EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; |
| 647 | EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; |
| 648 | EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; |
| 649 | EMSG_T illegal_reference = { "IR off page", "illegal reference" }; |
| 650 | EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; |
| 651 | EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; |
| 652 | EMSG_T literal_overflow = { "PE page exceeded", |
| 653 | "current page literal capacity exceeded" }; |
| 654 | EMSG_T pz_literal_overflow = { "ZE page exceeded", |
| 655 | "page zero capacity exceeded" }; |
| 656 | EMSG_T dubl_overflow = { "dubl overflow", "DUBL value overflow" }; |
| 657 | EMSG_T fltg_overflow = { "fltg overflow", "FLTG value overflow" }; |
| 658 | EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; |
| 659 | EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; |
| 660 | EMSG_T end_of_file = { "ND no $ at EOF", "No $ at End-of-File" }; |
| 661 | EMSG_T no_pseudo_op = { "not implemented", |
| 662 | "not implemented pseudo-op" }; |
| 663 | EMSG_T illegal_field_value = { "expr out of range", |
| 664 | "field value not in range of 0 through 7" }; |
| 665 | EMSG_T literal_gen_off = { "literals off", |
| 666 | "literal generation is off" }; |
| 667 | EMSG_T no_literal_value = { "no value", "no literal value" }; |
| 668 | EMSG_T text_string = { "no delimiter", |
| 669 | "text string delimiters not matched" }; |
| 670 | EMSG_T in_rim_mode = { "not OK in rim mode" |
| 671 | "FIELD pseudo-op not valid in RIM mode" }; |
| 672 | EMSG_T lt_expected = { "'<' expected", "'<' expected" }; |
| 673 | EMSG_T symbol_table_full = { "ST Symbol Tbl Full", |
| 674 | "Symbol Table Full" }; |
| 675 | |
| 676 | /*----------------------------------------------------------------------------*/ |
| 677 | |
| 678 | FILE *errorfile; |
| 679 | FILE *infile; |
| 680 | FILE *listfile; |
| 681 | FILE *listsave; |
| 682 | FILE *objectfile; |
| 683 | FILE *objectsave; |
| 684 | |
| 685 | char errorpathname[NAMELEN]; |
| 686 | char filename[NAMELEN]; |
| 687 | char listpathname[NAMELEN]; |
| 688 | char objectpathname[NAMELEN]; |
| 689 | char *pathname; |
| 690 | char permpathname[NAMELEN]; |
| 691 | |
| 692 | int list_lineno; |
| 693 | int list_pageno; |
| 694 | char list_title[LINELEN]; |
| 695 | BOOL list_title_set; /* Set if TITLE pseudo-op used. */ |
| 696 | char line[LINELEN]; /* Input line. */ |
| 697 | int lineno; /* Current line number. */ |
| 698 | int page_lineno; /* print line number on current page. */ |
| 699 | WORD16 listed; /* Listed flag. */ |
| 700 | WORD16 listedsave; |
| 701 | |
| 702 | WORD16 cc; /* Column Counter (char position in line). */ |
| 703 | WORD16 checksum; /* Generated checksum */ |
| 704 | BOOL binary_data_output; /* Set true when data has been output. */ |
| 705 | WORD16 clc; /* Location counter */ |
| 706 | WORD16 cplc; /* Current page literal counter. */ |
| 707 | char delimiter; /* Character immediately after eval'd term. */ |
| 708 | int errors; /* Number of errors found so far. */ |
| 709 | BOOL error_in_line; /* TRUE if error on current line. */ |
| 710 | int errors_pass_1; /* Number of errors on pass 1. */ |
| 711 | WORD16 field; /* Current field */ |
| 712 | WORD16 fieldlc; /* location counter without field portion. */ |
| 713 | BOOL fltg_input; /* TRUE when doing floating point input. */ |
| 714 | BOOL indirect_generated; /* TRUE if an off page address generated. */ |
| 715 | WORD16 lexstartprev; /* Where previous lexeme started. */ |
| 716 | WORD16 lextermprev; /* Where previous lexeme ended. */ |
| 717 | WORD16 lexstart; /* Index of current lexeme on line. */ |
| 718 | WORD16 lexterm; /* Index of character after current lexeme. */ |
| 719 | BOOL literals_on; /* Generate literals, defaults to none */ |
| 720 | WORD16 maxcc; /* Current line length. */ |
| 721 | BOOL overflow; /* Overflow flag for math routines. */ |
| 722 | WORD16 pass; /* Number of current pass. */ |
| 723 | BOOL print_permanent_symbols; |
| 724 | WORD16 pzlc; /* Page Zero literal counter. */ |
| 725 | WORD16 radix; /* Default number radix. */ |
| 726 | WORD16 reloc; /* The relocation distance. */ |
| 727 | BOOL rim_mode; /* Generate rim format, defaults to bin */ |
| 728 | BOOL symtab_print; /* Print symbol table flag */ |
| 729 | BOOL xref; |
| 730 | |
| 731 | FLTG_T fltg_ac; /* Value holder for evalFltg() */ |
| 732 | SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ |
| 733 | SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ |
| 734 | SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ |
| 735 | |
| 736 | |
| 737 | /******************************************************************************/ |
| 738 | /* */ |
| 739 | /* Function: main */ |
| 740 | /* */ |
| 741 | /* Synopsis: Starting point. Controls order of assembly. */ |
| 742 | /* */ |
| 743 | /******************************************************************************/ |
| 744 | int main( int argc, char *argv[] ) |
| 745 | { |
| 746 | int ix; |
| 747 | int space; |
| 748 | |
| 749 | /* Set the default values for global symbols. */ |
| 750 | binary_data_output = FALSE; |
| 751 | fltg_input = FALSE; |
| 752 | literals_on = FALSE; |
| 753 | print_permanent_symbols = FALSE; |
| 754 | rim_mode = FALSE; |
| 755 | symtab_print = FALSE; |
| 756 | xref = FALSE; |
| 757 | pathname = NULL; |
| 758 | |
| 759 | /* Get the options and pathnames */ |
| 760 | getArgs( argc, argv ); |
| 761 | |
| 762 | /* Setup the error file in case symbol table overflows while installing the */ |
| 763 | /* permanent symbols. */ |
| 764 | errorfile = fopen( errorpathname, "w" ); |
| 765 | errors = 0; |
| 766 | save_error_count = 0; |
| 767 | pass = 0; /* This is required for symbol table initialization. */ |
| 768 | symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); |
| 769 | |
| 770 | if( symtab == NULL ) |
| 771 | { |
| 772 | fprintf( stderr, "Could not allocate memory for symbol table.\n"); |
| 773 | exit( -1 ); |
| 774 | } |
| 775 | |
| 776 | /* Place end marker in symbol table. */ |
| 777 | symtab[0] = sym_undefined; |
| 778 | symbol_top = 0; |
| 779 | number_of_fixed_symbols = symbol_top; |
| 780 | fixed_symbols = &symtab[symbol_top - 1]; |
| 781 | |
| 782 | /* Enter the pseudo-ops into the symbol table */ |
| 783 | for( ix = 0; ix < DIM( pseudo ); ix++ ) |
| 784 | { |
| 785 | defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); |
| 786 | } |
| 787 | |
| 788 | /* Enter the predefined symbols into the table. */ |
| 789 | /* Also make them part of the permanent symbol table. */ |
| 790 | for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) |
| 791 | { |
| 792 | defineSymbol( permanent_symbols[ix].name, |
| 793 | permanent_symbols[ix].val, |
| 794 | permanent_symbols[ix].type | DEFFIX , 0 ); |
| 795 | } |
| 796 | |
| 797 | number_of_fixed_symbols = symbol_top; |
| 798 | fixed_symbols = &symtab[symbol_top - 1]; |
| 799 | |
| 800 | /* Do pass one of the assembly */ |
| 801 | checksum = 0; |
| 802 | pass = 1; |
| 803 | onePass(); |
| 804 | errors_pass_1 = errors; |
| 805 | |
| 806 | /* Set up for pass two */ |
| 807 | rewind( infile ); |
| 808 | errorfile = fopen( errorpathname, "w" ); |
| 809 | objectfile = fopen( objectpathname, "wb" ); |
| 810 | objectsave = objectfile; |
| 811 | |
| 812 | listfile = fopen( listpathname, "w" ); |
| 813 | listsave = NULL; |
| 814 | |
| 815 | punchLeader( 0 ); |
| 816 | checksum = 0; |
| 817 | |
| 818 | /* Do pass two of the assembly */ |
| 819 | errors = 0; |
| 820 | save_error_count = 0; |
| 821 | |
| 822 | if( xref ) |
| 823 | { |
| 824 | /* Get the amount of space that will be required for the concordance. */ |
| 825 | for( space = 0, ix = 0; ix < symbol_top; ix++ ) |
| 826 | { |
| 827 | symtab[ix].xref_index = space; /* Index into concordance table. */ |
| 828 | space += symtab[ix].xref_count + 1; |
| 829 | symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ |
| 830 | |
| 831 | } |
| 832 | /* Allocate the necessary space. */ |
| 833 | xreftab = (WORD16 *) malloc( sizeof( WORD16 ) * space ); |
| 834 | |
| 835 | /* Clear the cross reference space. */ |
| 836 | for( ix = 0; ix < space; ix++ ) |
| 837 | { |
| 838 | xreftab[ix] = 0; |
| 839 | } |
| 840 | } |
| 841 | pass = 2; |
| 842 | onePass(); |
| 843 | |
| 844 | /* Undo effects of NOPUNCH for any following checksum */ |
| 845 | objectfile = objectsave; |
| 846 | punchChecksum(); |
| 847 | |
| 848 | /* Works great for trailer. */ |
| 849 | punchLeader( 1 ); |
| 850 | |
| 851 | /* undo effects of XLIST for any following output to listing file. */ |
| 852 | if( listfile == NULL ) |
| 853 | { |
| 854 | listfile = listsave; |
| 855 | } |
| 856 | |
| 857 | /* Display value of error counter. */ |
| 858 | if( errors == 0 ) |
| 859 | { |
| 860 | fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); |
| 861 | } |
| 862 | else |
| 863 | { |
| 864 | fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, |
| 865 | ( errors == 1 ? s_error : s_errors )); |
| 866 | fprintf( listfile, "\n %d %s %s\n", errors, s_detected, |
| 867 | ( errors == 1 ? s_error : s_errors )); |
| 868 | fprintf( stderr, " %d %s %s\n", errors, s_detected, |
| 869 | ( errors == 1 ? s_error : s_errors )); |
| 870 | } |
| 871 | |
| 872 | if( symtab_print ) |
| 873 | { |
| 874 | printSymbolTable(); |
| 875 | } |
| 876 | |
| 877 | if( print_permanent_symbols ) |
| 878 | { |
| 879 | printPermanentSymbolTable(); |
| 880 | } |
| 881 | |
| 882 | if( xref ) |
| 883 | { |
| 884 | printCrossReference(); |
| 885 | } |
| 886 | |
| 887 | fclose( objectfile ); |
| 888 | fclose( listfile ); |
| 889 | fclose( errorfile ); |
| 890 | if( errors == 0 && errors_pass_1 == 0 ) |
| 891 | { |
| 892 | remove( errorpathname ); |
| 893 | } |
| 894 | |
| 895 | return( errors != 0 ); |
| 896 | } /* main() */ |
| 897 | |
| 898 | /******************************************************************************/ |
| 899 | /* */ |
| 900 | /* Function: getArgs */ |
| 901 | /* */ |
| 902 | /* Synopsis: Parse command line, set flags accordingly and setup input and */ |
| 903 | /* output files. */ |
| 904 | /* */ |
| 905 | /******************************************************************************/ |
| 906 | void getArgs( int argc, char *argv[] ) |
| 907 | { |
| 908 | WORD16 len; |
| 909 | WORD16 ix, jx; |
| 910 | |
| 911 | /* Set the defaults */ |
| 912 | errorfile = NULL; |
| 913 | infile = NULL; |
| 914 | listfile = NULL; |
| 915 | listsave = NULL; |
| 916 | objectfile = NULL; |
| 917 | objectsave = NULL; |
| 918 | |
| 919 | for( ix = 1; ix < argc; ix++ ) |
| 920 | { |
| 921 | if( argv[ix][0] == '-' ) |
| 922 | { |
| 923 | for( jx = 1; argv[ix][jx] != 0; jx++ ) |
| 924 | { |
| 925 | switch( argv[ix][jx] ) |
| 926 | { |
| 927 | case 'd': |
| 928 | symtab_print = TRUE; |
| 929 | break; |
| 930 | |
| 931 | case 'r': |
| 932 | rim_mode = TRUE; |
| 933 | break; |
| 934 | |
| 935 | case 'l': |
| 936 | literals_on = TRUE; |
| 937 | break; |
| 938 | |
| 939 | case 'p': |
| 940 | print_permanent_symbols = TRUE; |
| 941 | break; |
| 942 | |
| 943 | case 'x': |
| 944 | xref = TRUE; |
| 945 | break; |
| 946 | |
| 947 | default: |
| 948 | fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); |
| 949 | fprintf( stderr, " -d -- dump symbol table\n" ); |
| 950 | fprintf( stderr, " -l -- generate literals\n" ); |
| 951 | fprintf( stderr, " -r -- output rim format file\n" ); |
| 952 | fprintf( stderr, " -p -- output permanent symbols to file\n" ); |
| 953 | fprintf( stderr, " -x -- output cross reference to file\n" ); |
| 954 | fflush( stderr ); |
| 955 | exit( -1 ); |
| 956 | } /* end switch */ |
| 957 | } /* end for */ |
| 958 | } |
| 959 | else |
| 960 | { |
| 961 | if( pathname != NULL ) |
| 962 | { |
| 963 | fprintf( stderr, "%s: too many input files\n", argv[0] ); |
| 964 | exit( -1 ); |
| 965 | } |
| 966 | pathname = &argv[ix][0]; |
| 967 | } |
| 968 | } /* end for */ |
| 969 | |
| 970 | if( pathname == NULL ) |
| 971 | { |
| 972 | fprintf( stderr, "%s: no input file specified\n", argv[0] ); |
| 973 | exit( -1 ); |
| 974 | } |
| 975 | |
| 976 | len = strlen( pathname ); |
| 977 | if( len > NAMELEN - 5 ) |
| 978 | { |
| 979 | fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); |
| 980 | exit( -1 ); |
| 981 | } |
| 982 | |
| 983 | /* Now open the input file. */ |
| 984 | if(( infile = fopen( pathname, "r" )) == NULL ) |
| 985 | { |
| 986 | fprintf( stderr, "%s: cannot open \"%s\"\n", argv[0], pathname ); |
| 987 | exit( -1 ); |
| 988 | } |
| 989 | |
| 990 | /* Now make the pathnames */ |
| 991 | /* Find last '.', if it exists. */ |
| 992 | jx = len - 1; |
| 993 | while( pathname[jx] != '.' && pathname[jx] != '/' |
| 994 | && pathname[jx] != '\\' && jx >= 0 ) |
| 995 | { |
| 996 | jx--; |
| 997 | } |
| 998 | |
| 999 | switch( pathname[jx] ) |
| 1000 | { |
| 1001 | case '.': |
| 1002 | break; |
| 1003 | |
| 1004 | case '/': |
| 1005 | case '\\': |
| 1006 | jx = len; |
| 1007 | break; |
| 1008 | |
| 1009 | default: |
| 1010 | break; |
| 1011 | } |
| 1012 | |
| 1013 | /* Add the pathname extensions. */ |
| 1014 | strncpy( objectpathname, pathname, jx ); |
| 1015 | objectpathname[jx] = '\0'; |
| 1016 | strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); |
| 1017 | |
| 1018 | strncpy( listpathname, pathname, jx ); |
| 1019 | listpathname[jx] = '\0'; |
| 1020 | strcat( listpathname, ".lst" ); |
| 1021 | |
| 1022 | strncpy( errorpathname, pathname, jx ); |
| 1023 | errorpathname[jx] = '\0'; |
| 1024 | strcat( errorpathname, ".err" ); |
| 1025 | |
| 1026 | strncpy( permpathname, pathname, jx ); |
| 1027 | permpathname[jx] = '\0'; |
| 1028 | strcat( permpathname, ".prm" ); |
| 1029 | |
| 1030 | /* Extract the filename from the path. */ |
| 1031 | if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) |
| 1032 | { |
| 1033 | pathname[1] = '\\'; /* MS-DOS style pathname */ |
| 1034 | } |
| 1035 | |
| 1036 | jx = len - 1; |
| 1037 | while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) |
| 1038 | { |
| 1039 | jx--; |
| 1040 | } |
| 1041 | strcpy( filename, &pathname[jx + 1] ); |
| 1042 | |
| 1043 | } /* getArgs() */ |
| 1044 | |
| 1045 | |
| 1046 | /******************************************************************************/ |
| 1047 | /* */ |
| 1048 | /* Function: onePass */ |
| 1049 | /* */ |
| 1050 | /* Synopsis: Do one assembly pass. */ |
| 1051 | /* */ |
| 1052 | /******************************************************************************/ |
| 1053 | void onePass() |
| 1054 | { |
| 1055 | char name[SYMLEN]; |
| 1056 | WORD16 newclc; |
| 1057 | BOOL scanning_line; |
| 1058 | WORD16 start; |
| 1059 | SYM_T *sym; |
| 1060 | WORD16 term; |
| 1061 | WORD16 val; |
| 1062 | |
| 1063 | clc = 0200; /* Default starting address is 200 octal. */ |
| 1064 | field = 0; |
| 1065 | fieldlc = 0; |
| 1066 | reloc = 0; |
| 1067 | cp.loc = 00200; /* Points to end of page for [] operands. */ |
| 1068 | pz.loc = 00200; /* Points to end of page for () operands. */ |
| 1069 | cp.error = FALSE; |
| 1070 | pz.error = FALSE; |
| 1071 | listed = TRUE; |
| 1072 | lineno = 0; |
| 1073 | list_pageno = 0; |
| 1074 | list_lineno = 0; |
| 1075 | list_title_set = FALSE; |
| 1076 | radix = 8; /* Initial radix is octal (base 8). */ |
| 1077 | |
| 1078 | while( TRUE ) |
| 1079 | { |
| 1080 | readLine(); |
| 1081 | nextLexeme(); |
| 1082 | |
| 1083 | scanning_line = TRUE; |
| 1084 | while( scanning_line ) |
| 1085 | { |
| 1086 | if( isend( line[lexstart] )) |
| 1087 | { |
| 1088 | scanning_line = FALSE; |
| 1089 | } |
| 1090 | else |
| 1091 | { |
| 1092 | switch( line[lexstart] ) |
| 1093 | { |
| 1094 | case '/': |
| 1095 | scanning_line = FALSE; |
| 1096 | break; |
| 1097 | |
| 1098 | case ';': |
| 1099 | nextLexeme(); |
| 1100 | break; |
| 1101 | |
| 1102 | case '$': |
| 1103 | endOfBinary(); |
| 1104 | return; |
| 1105 | |
| 1106 | case '*': |
| 1107 | nextLexeme(); /* Skip '*', (set origin symbol) */ |
| 1108 | newclc = ((getExpr())->val & 07777 ) | field; |
| 1109 | /* Do not change Current Location Counter if an error occurred. */ |
| 1110 | if( !error_in_line ) |
| 1111 | { |
| 1112 | if(( newclc & 07600 ) != ( clc & 07600 )) |
| 1113 | { |
| 1114 | /* Current page has changed. */ |
| 1115 | punchLiteralPool( &cp, clc - 1 ); |
| 1116 | } |
| 1117 | clc = newclc - reloc; |
| 1118 | fieldlc = clc & 07777; |
| 1119 | |
| 1120 | if( !rim_mode ) |
| 1121 | { |
| 1122 | /* Not rim mode, put out origin. */ |
| 1123 | punchOrigin( clc ); |
| 1124 | } |
| 1125 | printLine( line, 0, fieldlc, LINE_VAL ); |
| 1126 | } |
| 1127 | break; |
| 1128 | |
| 1129 | default: |
| 1130 | switch( line[lexterm] ) |
| 1131 | { |
| 1132 | case ',': |
| 1133 | if( isalpha( line[lexstart] )) |
| 1134 | { |
| 1135 | /* Use lookup so symbol will not be counted as reference. */ |
| 1136 | sym = lookup( lexemeToName( name, lexstart, lexterm )); |
| 1137 | if( M_DEFINED( sym->type )) |
| 1138 | { |
| 1139 | if( sym->val != clc && pass == 2 ) |
| 1140 | { |
| 1141 | errorSymbol( &duplicate_label, sym->name, lexstart ); |
| 1142 | } |
| 1143 | sym->type = sym->type | DUPLICATE; |
| 1144 | } |
| 1145 | /* Must call define on pass 2 to generate concordance. */ |
| 1146 | defineLexeme( lexstart, lexterm, ( clc+reloc ), LABEL ); |
| 1147 | } |
| 1148 | else |
| 1149 | { |
| 1150 | errorLexeme( &label_syntax, lexstart ); |
| 1151 | } |
| 1152 | nextLexeme(); /* skip label */ |
| 1153 | nextLexeme(); /* skip comma */ |
| 1154 | break; |
| 1155 | |
| 1156 | case '=': |
| 1157 | if( isalpha( line[lexstart] )) |
| 1158 | { |
| 1159 | start = lexstart; |
| 1160 | term = lexterm; |
| 1161 | delimiter = line[lexterm]; |
| 1162 | nextLexBlank(); /* skip symbol */ |
| 1163 | nextLexBlank(); /* skip trailing = */ |
| 1164 | val = getExprs(); |
| 1165 | defineLexeme( start, term, val, DEFINED ); |
| 1166 | printLine( line, 0, val, LINE_VAL ); |
| 1167 | } |
| 1168 | else |
| 1169 | { |
| 1170 | errorLexeme( &symbol_syntax, lexstartprev ); |
| 1171 | nextLexeme(); /* skip symbol */ |
| 1172 | nextLexeme(); /* skip trailing = */ |
| 1173 | getExprs(); /* skip expression */ |
| 1174 | } |
| 1175 | break; |
| 1176 | |
| 1177 | default: |
| 1178 | if( isalpha( line[lexstart] )) |
| 1179 | { |
| 1180 | sym = evalSymbol(); |
| 1181 | val = sym->val; |
| 1182 | if( M_PSEUDO( sym->type )) |
| 1183 | { |
| 1184 | nextLexeme(); /* Skip symbol */ |
| 1185 | scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 ); |
| 1186 | } |
| 1187 | else |
| 1188 | { |
| 1189 | /* Identifier is not a pseudo-op, interpret as load value */ |
| 1190 | punchOutObject( clc, getExprs() & 07777 ); |
| 1191 | incrementClc(); |
| 1192 | } |
| 1193 | } |
| 1194 | else |
| 1195 | { |
| 1196 | /* Identifier is a value, interpret as load value */ |
| 1197 | punchOutObject( clc, getExprs() & 07777 ); |
| 1198 | incrementClc(); |
| 1199 | } |
| 1200 | break; |
| 1201 | } /* end switch */ |
| 1202 | break; |
| 1203 | } /* end switch */ |
| 1204 | } /* end if */ |
| 1205 | } /* end while( scanning_line ) */ |
| 1206 | } /* end while( TRUE ) */ |
| 1207 | } /* onePass() */ |
| 1208 | |
| 1209 | |
| 1210 | /******************************************************************************/ |
| 1211 | /* */ |
| 1212 | /* Function: getExprs */ |
| 1213 | /* */ |
| 1214 | /* Synopsis: Or together a list of blank separated expressions, from the */ |
| 1215 | /* current lexeme onward. Leave the current lexeme as */ |
| 1216 | /* the last one in the list. */ |
| 1217 | /* */ |
| 1218 | /******************************************************************************/ |
| 1219 | WORD16 getExprs() |
| 1220 | { |
| 1221 | SYM_T *symv; |
| 1222 | SYM_T *symt; |
| 1223 | WORD16 temp; |
| 1224 | SYMTYP temp_type; |
| 1225 | WORD16 value; |
| 1226 | SYMTYP value_type; |
| 1227 | |
| 1228 | symv = getExpr(); |
| 1229 | value = symv->val; |
| 1230 | value_type = symv->type; |
| 1231 | |
| 1232 | while( TRUE ) |
| 1233 | { |
| 1234 | if( isdone( line[lexstart] )) |
| 1235 | { |
| 1236 | return( value ); |
| 1237 | } |
| 1238 | switch( line[lexstart] ) |
| 1239 | { |
| 1240 | case ')': |
| 1241 | case ']': |
| 1242 | return( value ); |
| 1243 | |
| 1244 | default: |
| 1245 | break; |
| 1246 | } |
| 1247 | |
| 1248 | /* Interpret space as logical or */ |
| 1249 | symt = getExpr(); |
| 1250 | temp = symt->val & 07777; |
| 1251 | temp_type = symt->type; |
| 1252 | |
| 1253 | switch( value_type ) |
| 1254 | { |
| 1255 | case MRI: |
| 1256 | case MRIFIX: |
| 1257 | /* Previous symbol was a Memory Reference Instruction. */ |
| 1258 | switch( temp_type ) |
| 1259 | { |
| 1260 | case MRI: |
| 1261 | case MRIFIX: |
| 1262 | /* Current symbol is also a Memory Reference Instruction. */ |
| 1263 | value |= temp; /* Just OR the MRI instructions. */ |
| 1264 | break; |
| 1265 | |
| 1266 | default: |
| 1267 | /* Now have the address part of the MRI instruction. */ |
| 1268 | if( temp < 00200 ) |
| 1269 | { |
| 1270 | value |= temp; /* Page zero MRI. */ |
| 1271 | } |
| 1272 | else if( (( fieldlc + reloc ) & 07600 ) <= temp |
| 1273 | && temp <= (( fieldlc + reloc ) | 0177 )) |
| 1274 | { |
| 1275 | value |= ( PAGE_BIT | (temp & ADDRESS_FIELD )); /* Current page MRI */ |
| 1276 | } |
| 1277 | else |
| 1278 | { |
| 1279 | if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) |
| 1280 | { |
| 1281 | /* Already indirect, can't generate */ |
| 1282 | errorSymbol( &illegal_indirect, symt->name, lexstartprev ); |
| 1283 | } |
| 1284 | else |
| 1285 | { |
| 1286 | if( literals_on ) |
| 1287 | { |
| 1288 | /* Now fix off page reference. */ |
| 1289 | /* Search current page literal pool for needed value. */ |
| 1290 | /* Set Indirect Current Page */ |
| 1291 | value |= ( 00600 | insertLiteral( &cp, temp )); |
| 1292 | indirect_generated = TRUE; |
| 1293 | } |
| 1294 | else |
| 1295 | { |
| 1296 | errorSymbol( &illegal_reference, symt->name, lexstartprev ); |
| 1297 | value |= ( temp & 0177 ); |
| 1298 | } |
| 1299 | } |
| 1300 | } |
| 1301 | break; |
| 1302 | } |
| 1303 | break; |
| 1304 | |
| 1305 | default: |
| 1306 | value |= temp; /* Normal 12 bit value. */ |
| 1307 | break; |
| 1308 | } |
| 1309 | } /* end while */ |
| 1310 | } /* getExprs() */ |
| 1311 | |
| 1312 | |
| 1313 | /******************************************************************************/ |
| 1314 | /* */ |
| 1315 | /* Function: getExpr */ |
| 1316 | /* */ |
| 1317 | /* Synopsis: Get an expression, from the current lexeme onward, leave the */ |
| 1318 | /* current lexeme as the one after the expression. Expressions */ |
| 1319 | /* contain terminal symbols (identifiers) separated by operators. */ |
| 1320 | /* */ |
| 1321 | /******************************************************************************/ |
| 1322 | SYM_T *getExpr() |
| 1323 | { |
| 1324 | delimiter = line[lexterm]; |
| 1325 | |
| 1326 | if( line[lexstart] == '-' ) |
| 1327 | { |
| 1328 | nextLexBlank(); |
| 1329 | sym_getexpr = *(eval()); |
| 1330 | sym_getexpr.val = ( - sym_getexpr.val ); |
| 1331 | } |
| 1332 | else |
| 1333 | { |
| 1334 | sym_getexpr = *(eval()); |
| 1335 | } |
| 1336 | |
| 1337 | |
| 1338 | if( is_blank( delimiter )) |
| 1339 | { |
| 1340 | return( &sym_getexpr ); |
| 1341 | } |
| 1342 | |
| 1343 | /* Here we assume the current lexeme is the operator separating the */ |
| 1344 | /* previous operator from the next, if any. */ |
| 1345 | while( TRUE ) |
| 1346 | { |
| 1347 | /* assert line[lexstart] == delimiter */ |
| 1348 | if( is_blank( delimiter )) |
| 1349 | { |
| 1350 | return( &sym_getexpr ); |
| 1351 | } |
| 1352 | |
| 1353 | switch( line[lexstart] ) |
| 1354 | { |
| 1355 | case '+': /* add */ |
| 1356 | nextLexBlank(); /* skip over the operator */ |
| 1357 | sym_getexpr.val += (eval())->val; |
| 1358 | break; |
| 1359 | |
| 1360 | case '-': /* subtract */ |
| 1361 | nextLexBlank(); /* skip over the operator */ |
| 1362 | sym_getexpr.val -= (eval())->val; |
| 1363 | break; |
| 1364 | |
| 1365 | case '^': /* multiply */ |
| 1366 | nextLexBlank(); /* skip over the operator */ |
| 1367 | sym_getexpr.val *= (eval())->val; |
| 1368 | break; |
| 1369 | |
| 1370 | case '%': /* divide */ |
| 1371 | nextLexBlank(); /* skip over the operator */ |
| 1372 | sym_getexpr.val /= (eval())->val; |
| 1373 | break; |
| 1374 | |
| 1375 | case '&': /* and */ |
| 1376 | nextLexBlank(); /* skip over the operator */ |
| 1377 | sym_getexpr.val &= (eval())->val; |
| 1378 | break; |
| 1379 | |
| 1380 | case '!': /* or */ |
| 1381 | nextLexBlank(); /* skip over the operator */ |
| 1382 | sym_getexpr.val |= (eval())->val; |
| 1383 | break; |
| 1384 | |
| 1385 | default: |
| 1386 | if( isend( line[lexstart] )) |
| 1387 | { |
| 1388 | return( &sym_getexpr ); |
| 1389 | } |
| 1390 | |
| 1391 | switch( line[lexstart] ) |
| 1392 | { |
| 1393 | case '/': |
| 1394 | case ';': |
| 1395 | case ')': |
| 1396 | case ']': |
| 1397 | case '<': |
| 1398 | break; |
| 1399 | |
| 1400 | case '=': |
| 1401 | errorMessage( &illegal_equals, lexstart ); |
| 1402 | moveToEndOfLine(); |
| 1403 | sym_getexpr.val = 0; |
| 1404 | break; |
| 1405 | |
| 1406 | default: |
| 1407 | errorMessage( &illegal_expression, lexstart ); |
| 1408 | moveToEndOfLine(); |
| 1409 | sym_getexpr.val = 0; |
| 1410 | break; |
| 1411 | } |
| 1412 | return( &sym_getexpr ); |
| 1413 | } |
| 1414 | } /* end while */ |
| 1415 | } /* getExpr() */ |
| 1416 | |
| 1417 | |
| 1418 | /******************************************************************************/ |
| 1419 | /* */ |
| 1420 | /* Function: eval */ |
| 1421 | /* */ |
| 1422 | /* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ |
| 1423 | /* */ |
| 1424 | /******************************************************************************/ |
| 1425 | SYM_T *eval() |
| 1426 | { |
| 1427 | WORD16 digit; |
| 1428 | WORD16 from; |
| 1429 | WORD16 loc; |
| 1430 | SYM_T *sym; |
| 1431 | WORD16 val; |
| 1432 | |
| 1433 | val = 0; |
| 1434 | |
| 1435 | delimiter = line[lexterm]; |
| 1436 | if( isalpha( line[lexstart] )) |
| 1437 | { |
| 1438 | sym = evalSymbol(); |
| 1439 | if( M_UNDEFINED( sym->type ) && pass == 2 ) |
| 1440 | { |
| 1441 | errorSymbol( &undefined_symbol, sym->name, lexstart ); |
| 1442 | nextLexeme(); |
| 1443 | return( sym ); |
| 1444 | } |
| 1445 | else |
| 1446 | { |
| 1447 | nextLexeme(); |
| 1448 | return( sym ); |
| 1449 | } |
| 1450 | } |
| 1451 | else if( isdigit( line[lexstart] )) |
| 1452 | { |
| 1453 | from = lexstart; |
| 1454 | val = 0; |
| 1455 | while( from < lexterm ) |
| 1456 | { |
| 1457 | if( isdigit( line[from] )) |
| 1458 | { |
| 1459 | digit = (WORD16) line[from++] - (WORD16) '0'; |
| 1460 | if( digit < radix ) |
| 1461 | { |
| 1462 | val = val * radix + digit; |
| 1463 | } |
| 1464 | else |
| 1465 | { |
| 1466 | errorLexeme( &number_not_radix, from - 1 ); |
| 1467 | val = 0; |
| 1468 | from = lexterm; |
| 1469 | } |
| 1470 | } |
| 1471 | else |
| 1472 | { |
| 1473 | errorLexeme( ¬_a_number, lexstart ); |
| 1474 | val = 0; |
| 1475 | from = lexterm; |
| 1476 | } |
| 1477 | } |
| 1478 | nextLexeme(); |
| 1479 | sym_eval.val = val; |
| 1480 | return( &sym_eval ); |
| 1481 | } |
| 1482 | else |
| 1483 | { |
| 1484 | switch( line[lexstart] ) |
| 1485 | { |
| 1486 | case '"': /* Character literal */ |
| 1487 | if( cc + 2 < maxcc ) |
| 1488 | { |
| 1489 | val = line[lexstart + 1] | 0200; |
| 1490 | delimiter = line[lexstart + 2]; |
| 1491 | cc = lexstart + 2; |
| 1492 | } |
| 1493 | else |
| 1494 | { |
| 1495 | errorMessage( &no_literal_value, lexstart ); |
| 1496 | } |
| 1497 | nextLexeme(); |
| 1498 | break; |
| 1499 | |
| 1500 | case '.': /* Value of Current Location Counter */ |
| 1501 | val = clc + reloc; |
| 1502 | nextLexeme(); |
| 1503 | break; |
| 1504 | |
| 1505 | case '[': /* Generate literal on page zero. */ |
| 1506 | if( !literals_on ) |
| 1507 | { |
| 1508 | errorMessage( &literal_gen_off, lexstart ); |
| 1509 | } |
| 1510 | nextLexBlank(); /* Skip bracket */ |
| 1511 | val = (getExpr())->val & 07777; |
| 1512 | if( line[lexstart] == ']' ) |
| 1513 | { |
| 1514 | nextLexBlank(); /* Skip end bracket */ |
| 1515 | } |
| 1516 | else |
| 1517 | { |
| 1518 | /* errorMessage( "parens", lexstart ); */ |
| 1519 | } |
| 1520 | sym_eval.val = literals_on ? insertLiteral( &pz, val ) : 0; |
| 1521 | return( &sym_eval ); |
| 1522 | |
| 1523 | case '(': /* Generate literal on current page. */ |
| 1524 | if( !literals_on ) |
| 1525 | { |
| 1526 | errorMessage( &literal_gen_off, lexstart ); |
| 1527 | } |
| 1528 | |
| 1529 | nextLexBlank(); /* Skip paren */ |
| 1530 | val = getExprs() & 07777; |
| 1531 | |
| 1532 | if( line[lexstart] == ')' ) |
| 1533 | { |
| 1534 | nextLexBlank(); /* Skip end paren */ |
| 1535 | } |
| 1536 | else |
| 1537 | { |
| 1538 | /* errorMessage( "parens", NULL ); */ |
| 1539 | } |
| 1540 | |
| 1541 | loc = literals_on ? insertLiteral( &cp, val ) : 0; |
| 1542 | sym_eval.val = loc + (( clc + reloc ) & 077600 ); |
| 1543 | return( &sym_eval ); |
| 1544 | |
| 1545 | default: |
| 1546 | switch( line[lexstart] ) |
| 1547 | { |
| 1548 | case '=': |
| 1549 | errorMessage( &illegal_equals, lexstart ); |
| 1550 | moveToEndOfLine(); |
| 1551 | break; |
| 1552 | |
| 1553 | default: |
| 1554 | errorMessage( &illegal_character, lexstart ); |
| 1555 | break; |
| 1556 | } |
| 1557 | val = 0; /* On error, set value to zero. */ |
| 1558 | nextLexBlank(); /* Go past illegal character. */ |
| 1559 | } |
| 1560 | } |
| 1561 | sym_eval.val = val; |
| 1562 | return( &sym_eval ); |
| 1563 | } /* eval() */ |
| 1564 | |
| 1565 | |
| 1566 | /******************************************************************************/ |
| 1567 | /* */ |
| 1568 | /* Function: inputDubl */ |
| 1569 | /* */ |
| 1570 | /* Synopsis: Get the value of the current lexeme as a double word. */ |
| 1571 | /* */ |
| 1572 | /******************************************************************************/ |
| 1573 | void inputDubl() |
| 1574 | { |
| 1575 | WORD32 dublvalue; |
| 1576 | BOOL scanning_line; |
| 1577 | |
| 1578 | scanning_line = TRUE; |
| 1579 | do |
| 1580 | { |
| 1581 | while( scanning_line ) |
| 1582 | { |
| 1583 | if( isend( line[lexstart] )) |
| 1584 | { |
| 1585 | scanning_line = FALSE; |
| 1586 | } |
| 1587 | else |
| 1588 | { |
| 1589 | switch( line[lexstart] ) |
| 1590 | { |
| 1591 | case '/': |
| 1592 | scanning_line = FALSE; |
| 1593 | break; |
| 1594 | |
| 1595 | case ';': |
| 1596 | nextLexeme(); |
| 1597 | break; |
| 1598 | |
| 1599 | case '+': |
| 1600 | delimiter = line[lexterm]; |
| 1601 | nextLexBlank(); |
| 1602 | case '-': |
| 1603 | default: |
| 1604 | if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) |
| 1605 | { |
| 1606 | dublvalue = getDublExprs(); |
| 1607 | punchOutObject( clc, (WORD16)(( dublvalue >> 12 ) & 07777 )); |
| 1608 | incrementClc(); |
| 1609 | punchOutObject( clc, (WORD16)( dublvalue & 07777 )); |
| 1610 | incrementClc(); |
| 1611 | } |
| 1612 | else |
| 1613 | { |
| 1614 | return; /* Non-numeric input, back to assembly. */ |
| 1615 | } |
| 1616 | break; |
| 1617 | } /* end switch */ |
| 1618 | } /* end if */ |
| 1619 | |
| 1620 | if( error_in_line ) |
| 1621 | { |
| 1622 | return; /* Error occurred, exit DUBL input mode. */ |
| 1623 | } |
| 1624 | } /* end while( scanning_line ) */ |
| 1625 | readLine(); |
| 1626 | nextLexeme(); |
| 1627 | scanning_line = TRUE; |
| 1628 | } |
| 1629 | while( TRUE ); |
| 1630 | } /* inputDubl() */ |
| 1631 | |
| 1632 | |
| 1633 | /******************************************************************************/ |
| 1634 | /* */ |
| 1635 | /* Function: getDublExprs */ |
| 1636 | /* */ |
| 1637 | /* Synopsis: Get a DUBL expression. */ |
| 1638 | /* */ |
| 1639 | /******************************************************************************/ |
| 1640 | WORD32 getDublExprs() |
| 1641 | { |
| 1642 | WORD32 dublvalue; |
| 1643 | |
| 1644 | dublvalue = getDublExpr(); |
| 1645 | |
| 1646 | while( TRUE ) |
| 1647 | { |
| 1648 | if( isdone( line[lexstart] )) |
| 1649 | { |
| 1650 | return( dublvalue ); |
| 1651 | } |
| 1652 | else |
| 1653 | { |
| 1654 | errorMessage( &illegal_expression, lexstart - 1 ); |
| 1655 | return( 0 ); |
| 1656 | } |
| 1657 | } /* end while */ |
| 1658 | } /* getDublExprs() */ |
| 1659 | |
| 1660 | |
| 1661 | /******************************************************************************/ |
| 1662 | /* */ |
| 1663 | /* Function: getDublExpr */ |
| 1664 | /* */ |
| 1665 | /* Synopsis: Get the value of the current lexeme as a double word. The */ |
| 1666 | /* number is always considered to have a decimal radix. */ |
| 1667 | /* */ |
| 1668 | /******************************************************************************/ |
| 1669 | WORD32 getDublExpr() |
| 1670 | { |
| 1671 | WORD32 dublvalue; |
| 1672 | |
| 1673 | delimiter = line[lexterm]; |
| 1674 | if( line[lexstart] == '-' ) |
| 1675 | { |
| 1676 | nextLexBlank(); |
| 1677 | dublvalue = evalDubl( 0 ); |
| 1678 | nextLexeme(); |
| 1679 | /* Test for any value greater than 23 bits in length. */ |
| 1680 | if( (unsigned long int)dublvalue > 040000000L ) |
| 1681 | { |
| 1682 | errorMessage( &dubl_overflow, lexstart ); |
| 1683 | dublvalue = 0; |
| 1684 | } |
| 1685 | dublvalue = -dublvalue; |
| 1686 | } |
| 1687 | else |
| 1688 | { |
| 1689 | dublvalue = evalDubl( 0 ); |
| 1690 | nextLexeme(); |
| 1691 | /* Test for any value greater than 23 bits in length. */ |
| 1692 | if( (unsigned long int)dublvalue > 037777777L ) |
| 1693 | { |
| 1694 | errorMessage( &dubl_overflow, lexstart ); |
| 1695 | dublvalue = 0; |
| 1696 | } |
| 1697 | } |
| 1698 | |
| 1699 | if( is_blank( delimiter )) |
| 1700 | { |
| 1701 | return( dublvalue ); |
| 1702 | } |
| 1703 | |
| 1704 | /* Here we assume the current lexeme is the operator separating the */ |
| 1705 | /* previous operator from the next, if any. */ |
| 1706 | while( TRUE ) |
| 1707 | { |
| 1708 | /* assert line[lexstart] == delimiter */ |
| 1709 | if( is_blank( delimiter )) |
| 1710 | { |
| 1711 | errorMessage( &illegal_expression, lexstart ); |
| 1712 | moveToEndOfLine(); |
| 1713 | dublvalue = 0; |
| 1714 | return( dublvalue ); |
| 1715 | } |
| 1716 | |
| 1717 | switch( line[lexstart] ) |
| 1718 | { |
| 1719 | case '+': /* add */ |
| 1720 | case '-': /* subtract */ |
| 1721 | case '^': /* multiply */ |
| 1722 | case '%': /* divide */ |
| 1723 | case '&': /* and */ |
| 1724 | case '!': /* or */ |
| 1725 | errorMessage( &illegal_expression, lexstart ); |
| 1726 | moveToEndOfLine(); |
| 1727 | dublvalue = 0; |
| 1728 | break; |
| 1729 | |
| 1730 | default: |
| 1731 | if( isend( line[lexstart] )) |
| 1732 | { |
| 1733 | return( dublvalue ); |
| 1734 | } |
| 1735 | |
| 1736 | switch( line[lexstart] ) |
| 1737 | { |
| 1738 | case '/': |
| 1739 | case ';': |
| 1740 | break; |
| 1741 | |
| 1742 | default: |
| 1743 | errorMessage( &illegal_expression, lexstart ); |
| 1744 | moveToEndOfLine(); |
| 1745 | dublvalue = 0; |
| 1746 | break; |
| 1747 | } |
| 1748 | return( dublvalue ); |
| 1749 | } |
| 1750 | } /* end while */ |
| 1751 | } /* getDublExpr() */ |
| 1752 | |
| 1753 | |
| 1754 | /******************************************************************************/ |
| 1755 | /* */ |
| 1756 | /* Function: evalDubl */ |
| 1757 | /* */ |
| 1758 | /* Synopsis: Get the value of the current lexeme as a double word. The */ |
| 1759 | /* number is always considered to have a decimal radix. */ |
| 1760 | /* */ |
| 1761 | /******************************************************************************/ |
| 1762 | WORD32 evalDubl( WORD32 initial_value ) |
| 1763 | { |
| 1764 | WORD32 digit; |
| 1765 | WORD16 from; |
| 1766 | WORD32 dublvalue; |
| 1767 | WORD32 olddublvalue; |
| 1768 | |
| 1769 | overflow = FALSE; |
| 1770 | delimiter = line[lexterm]; |
| 1771 | from = lexstart; |
| 1772 | dublvalue = initial_value; |
| 1773 | |
| 1774 | while( from < lexterm ) |
| 1775 | { |
| 1776 | if( isdigit( line[from] )) |
| 1777 | { |
| 1778 | olddublvalue = dublvalue; |
| 1779 | digit = (WORD32)( line[from++] - '0' ); |
| 1780 | dublvalue = dublvalue * 10 + digit; |
| 1781 | if( dublvalue < olddublvalue ) |
| 1782 | { |
| 1783 | overflow = TRUE; |
| 1784 | } |
| 1785 | } |
| 1786 | else |
| 1787 | { |
| 1788 | errorLexeme( ¬_a_number, from ); |
| 1789 | dublvalue = 0; |
| 1790 | from = lexterm; |
| 1791 | } |
| 1792 | } |
| 1793 | return( dublvalue ); |
| 1794 | } /* evalDubl() */ |
| 1795 | |
| 1796 | |
| 1797 | /******************************************************************************/ |
| 1798 | /* */ |
| 1799 | /* Function: inputFltg */ |
| 1800 | /* */ |
| 1801 | /* Synopsis: Get the value of the current lexeme as a Floating Point const. */ |
| 1802 | /* */ |
| 1803 | /******************************************************************************/ |
| 1804 | void inputFltg() |
| 1805 | { |
| 1806 | FLTG_T *fltg; |
| 1807 | BOOL scanning_line; |
| 1808 | |
| 1809 | fltg_input = TRUE; /* Set lexeme scanner for floating point. */ |
| 1810 | scanning_line = TRUE; |
| 1811 | while( TRUE ) |
| 1812 | { |
| 1813 | while( scanning_line ) |
| 1814 | { |
| 1815 | if( isend( line[lexstart] )) |
| 1816 | { |
| 1817 | scanning_line = FALSE; |
| 1818 | } |
| 1819 | else |
| 1820 | { |
| 1821 | switch( line[lexstart] ) |
| 1822 | { |
| 1823 | case '/': |
| 1824 | scanning_line = FALSE; |
| 1825 | break; |
| 1826 | |
| 1827 | case ';': |
| 1828 | nextLexeme(); |
| 1829 | break; |
| 1830 | |
| 1831 | case '+': |
| 1832 | delimiter = line[lexterm]; |
| 1833 | nextLexBlank(); |
| 1834 | case '-': |
| 1835 | default: |
| 1836 | if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) |
| 1837 | { |
| 1838 | fltg = getFltgExprs(); |
| 1839 | punchOutObject( clc, ( fltg->exponent & 07777 )); |
| 1840 | incrementClc(); |
| 1841 | punchOutObject( clc, (WORD16)(( fltg->mantissa >> 12 ) & 07777 )); |
| 1842 | incrementClc(); |
| 1843 | punchOutObject( clc, (WORD16)( fltg->mantissa & 07777 )); |
| 1844 | incrementClc(); |
| 1845 | } |
| 1846 | else |
| 1847 | { |
| 1848 | fltg_input = FALSE; /* Reset lexeme scanner. */ |
| 1849 | return; /* Non-numeric input, back to assembly. */ |
| 1850 | } |
| 1851 | break; |
| 1852 | } /* end switch */ |
| 1853 | } /* end if */ |
| 1854 | |
| 1855 | if( error_in_line ) |
| 1856 | { |
| 1857 | fltg_input = FALSE; /* Reset lexeme scanner. */ |
| 1858 | return; /* Error occurred, exit FLTG input mode. */ |
| 1859 | } |
| 1860 | } /* end while( scanning_line ) */ |
| 1861 | readLine(); |
| 1862 | nextLexeme(); |
| 1863 | scanning_line = TRUE; |
| 1864 | } |
| 1865 | } /* inputFltg() */ |
| 1866 | |
| 1867 | |
| 1868 | /******************************************************************************/ |
| 1869 | /* */ |
| 1870 | /* Function: getFltgExprs */ |
| 1871 | /* */ |
| 1872 | /* Synopsis: Get a FLTG expression. */ |
| 1873 | /* */ |
| 1874 | /******************************************************************************/ |
| 1875 | FLTG_T *getFltgExprs() |
| 1876 | { |
| 1877 | FLTG_T *fltg; |
| 1878 | |
| 1879 | fltg = getFltgExpr(); |
| 1880 | |
| 1881 | while( TRUE ) |
| 1882 | { |
| 1883 | if( isdone( line[lexstart] )) |
| 1884 | { |
| 1885 | return( fltg ); |
| 1886 | } |
| 1887 | else |
| 1888 | { |
| 1889 | errorMessage( &illegal_expression, lexstart - 1 ); |
| 1890 | return( 0 ); |
| 1891 | } |
| 1892 | } /* end while */ |
| 1893 | } /* getFltgExprs() */ |
| 1894 | |
| 1895 | |
| 1896 | /******************************************************************************/ |
| 1897 | /* */ |
| 1898 | /* Function: getFltgExpr */ |
| 1899 | /* */ |
| 1900 | /* Synopsis: Get the value of the current lexeme as a double word. The */ |
| 1901 | /* number is always considered to have a decimal radix. */ |
| 1902 | /* */ |
| 1903 | /******************************************************************************/ |
| 1904 | FLTG_T *getFltgExpr() |
| 1905 | { |
| 1906 | FLTG_T *fltg; |
| 1907 | |
| 1908 | delimiter = line[lexterm]; |
| 1909 | fltg = evalFltg(); |
| 1910 | /* Test for any value greater than 23 bits in length. */ |
| 1911 | if( (unsigned long int)fltg->mantissa> 077777777L ) |
| 1912 | { |
| 1913 | errorMessage( &fltg_overflow, lexstart ); |
| 1914 | } |
| 1915 | |
| 1916 | if( is_blank( delimiter )) |
| 1917 | { |
| 1918 | return( fltg ); |
| 1919 | } |
| 1920 | |
| 1921 | /* Here we assume the current lexeme is the operator separating the */ |
| 1922 | /* previous operator from the next, if any. */ |
| 1923 | while( TRUE ) |
| 1924 | { |
| 1925 | /* assert line[lexstart] == delimiter */ |
| 1926 | if( is_blank( delimiter )) |
| 1927 | { |
| 1928 | errorMessage( &illegal_expression, lexstart ); |
| 1929 | moveToEndOfLine(); |
| 1930 | fltg = 0; |
| 1931 | return( fltg ); |
| 1932 | } |
| 1933 | |
| 1934 | switch( line[lexstart] ) |
| 1935 | { |
| 1936 | case '+': /* add */ |
| 1937 | case '-': /* subtract */ |
| 1938 | case '^': /* multiply */ |
| 1939 | case '%': /* divide */ |
| 1940 | case '&': /* and */ |
| 1941 | case '!': /* or */ |
| 1942 | errorMessage( &illegal_expression, lexstart ); |
| 1943 | moveToEndOfLine(); |
| 1944 | fltg = NULL; |
| 1945 | break; |
| 1946 | |
| 1947 | default: |
| 1948 | if( isend( line[lexstart] )) |
| 1949 | { |
| 1950 | return( fltg ); |
| 1951 | } |
| 1952 | |
| 1953 | switch( line[lexstart] ) |
| 1954 | { |
| 1955 | case '/': |
| 1956 | case ';': |
| 1957 | break; |
| 1958 | |
| 1959 | default: |
| 1960 | errorMessage( &illegal_expression, lexstart ); |
| 1961 | moveToEndOfLine(); |
| 1962 | fltg = NULL; |
| 1963 | break; |
| 1964 | } |
| 1965 | return( fltg ); |
| 1966 | } |
| 1967 | } /* end while */ |
| 1968 | } /* getFltgExpr() */ |
| 1969 | |
| 1970 | |
| 1971 | /******************************************************************************/ |
| 1972 | /* */ |
| 1973 | /* Function: evalFltg */ |
| 1974 | /* */ |
| 1975 | /* Synopsis: Get the value of the current lexeme as a floating point value. */ |
| 1976 | /* Floating point input is alwasy considered decimal. */ |
| 1977 | /* The general format of a floating point number is: */ |
| 1978 | /* +-ddd.dddE+-dd where each d is a decimal digit. */ |
| 1979 | /* */ |
| 1980 | /******************************************************************************/ |
| 1981 | FLTG_T *evalFltg() |
| 1982 | { |
| 1983 | BYTE current_state; |
| 1984 | BYTE current_col; |
| 1985 | WORD16 exponent; |
| 1986 | FLTG_T *fltg; |
| 1987 | WORD32 input_value; |
| 1988 | BOOL negate; |
| 1989 | BOOL negate_exponent; |
| 1990 | BYTE next_state; |
| 1991 | int right_digits; |
| 1992 | |
| 1993 | /* This uses a lexical analyzer to parse the floating point format. */ |
| 1994 | static BYTE state_table[][10] = |
| 1995 | { |
| 1996 | /* 0 1 2 3 4 5 6 Oolumn index */ |
| 1997 | /* + - d . E sp p State Comment */ |
| 1998 | { 2, 1, 3, 4, 10, 10, 10 }, /* 0 Initial state. */ |
| 1999 | { 11, 11, 3, 4, 11, 11, 11 }, /* 1 - */ |
| 2000 | { 11, 11, 3, 4, 11, 11, 11 }, /* 2 + */ |
| 2001 | { 10, 10, 10, 4, 6, 10, 10 }, /* 3 # (+-ddd) */ |
| 2002 | { 11, 11, 5, 11, 11, 10, 10 }, /* 4 . (+-ddd.) */ |
| 2003 | { 11, 11, 11, 11, 6, 10, 11 }, /* 5 # (+-ddd.ddd) */ |
| 2004 | { 8, 7, 9, 11, 11, 11, 11 }, /* 6 E (+-ddd.dddE) */ |
| 2005 | { 11, 11, 9, 11, 11, 11, 11 }, /* 7 - (+-ddd.dddE- */ |
| 2006 | { 11, 11, 9, 11, 11, 11, 11 }, /* 8 + (+-ddd.dddE+ */ |
| 2007 | { 11, 11, 11, 11, 11, 10, 11 } /* 9 # (+-ddd.dddE+-dd */ |
| 2008 | /* 10 Completion state */ |
| 2009 | /* 11 Error state. */ |
| 2010 | }; |
| 2011 | |
| 2012 | delimiter = line[lexterm]; |
| 2013 | fltg = &fltg_ac; |
| 2014 | fltg->exponent = 0; |
| 2015 | fltg->mantissa = 0; |
| 2016 | input_value = 0; |
| 2017 | negate = FALSE; |
| 2018 | negate_exponent = FALSE; |
| 2019 | exponent = 0; |
| 2020 | right_digits = 0; |
| 2021 | current_state = 0; |
| 2022 | |
| 2023 | while( TRUE ) |
| 2024 | { |
| 2025 | /* Classify character. This is the column index. */ |
| 2026 | switch( line[lexstart] ) |
| 2027 | { |
| 2028 | case '+': |
| 2029 | current_col = 0; |
| 2030 | break; |
| 2031 | |
| 2032 | case '-': |
| 2033 | current_col = 1; |
| 2034 | break; |
| 2035 | |
| 2036 | case '.': |
| 2037 | current_col = 3; |
| 2038 | break; |
| 2039 | |
| 2040 | case 'E': case 'e': |
| 2041 | current_col = 4; |
| 2042 | break; |
| 2043 | |
| 2044 | default: |
| 2045 | if( isdigit( line[lexstart] )) |
| 2046 | { |
| 2047 | current_col = 2; |
| 2048 | } |
| 2049 | else if( isdone( line[lexstart] )) |
| 2050 | { |
| 2051 | current_col = 5; |
| 2052 | } |
| 2053 | else |
| 2054 | { |
| 2055 | current_col = 6; |
| 2056 | } |
| 2057 | break; |
| 2058 | } |
| 2059 | |
| 2060 | next_state = state_table[current_state][current_col]; |
| 2061 | |
| 2062 | switch( next_state ) |
| 2063 | { |
| 2064 | case 1: /* - */ |
| 2065 | negate = TRUE; |
| 2066 | case 2: /* + */ |
| 2067 | delimiter = line[lexterm]; /* Move past the + or - character. */ |
| 2068 | nextLexBlank(); |
| 2069 | break; |
| 2070 | |
| 2071 | case 3: /* Number (+-ddd) */ |
| 2072 | input_value = evalDubl( 0 ); /* Integer part of the number. */ |
| 2073 | nextLexeme(); /* Move past previous lexeme. */ |
| 2074 | break; |
| 2075 | |
| 2076 | case 4: |
| 2077 | delimiter = line[lexterm]; |
| 2078 | nextLexBlank(); /* Move past the . character. */ |
| 2079 | break; |
| 2080 | |
| 2081 | case 5: /* . (+-ddd.ddd) */ |
| 2082 | /* Fractional part of the number. */ |
| 2083 | input_value = evalDubl( input_value ); |
| 2084 | right_digits = lexterm - lexstart;/* Digit count to right of decimal. */ |
| 2085 | nextLexeme(); /* Move past previous lexeme. */ |
| 2086 | break; |
| 2087 | |
| 2088 | case 6: /* E (+-ddd.dddE) */ |
| 2089 | delimiter = line[lexterm]; /* Move past the E. */ |
| 2090 | nextLexBlank(); |
| 2091 | break; |
| 2092 | |
| 2093 | case 7: /* - (+-ddd.dddE-) */ |
| 2094 | negate_exponent = TRUE; |
| 2095 | case 8: /* + (+-ddd.dddE+) */ |
| 2096 | delimiter = line[lexterm]; /* Move past the + or - character. */ |
| 2097 | nextLexBlank(); |
| 2098 | break; |
| 2099 | |
| 2100 | case 9: /* # (+-ddd.dddE+-dd) */ |
| 2101 | exponent = (int)evalDubl( 0 ); /* Exponent of floating point number. */ |
| 2102 | if( negate_exponent ) { exponent = - exponent; } |
| 2103 | nextLexeme(); /* Move past previous lexeme. */ |
| 2104 | break; |
| 2105 | |
| 2106 | case 10: /* Floating number parsed, convert */ |
| 2107 | /* the number. */ |
| 2108 | /* Get the exponent for the number as input. */ |
| 2109 | exponent -= right_digits; |
| 2110 | |
| 2111 | /* Remove trailing zeros and adjust the exponent accordingly. */ |
| 2112 | while(( input_value % 10 ) == 0 ) |
| 2113 | { |
| 2114 | input_value /= 10; |
| 2115 | exponent++; |
| 2116 | } |
| 2117 | |
| 2118 | /* Convert the number to floating point. The number is calculated with */ |
| 2119 | /* a 27 bit mantissa to improve precision. The extra 3 bits are */ |
| 2120 | /* discarded after the result has been calculated. */ |
| 2121 | fltg->exponent = 26; |
| 2122 | fltg->mantissa = input_value << 3; |
| 2123 | normalizeFltg( fltg ); |
| 2124 | |
| 2125 | |
| 2126 | while( exponent != 0 ) |
| 2127 | { |
| 2128 | if( exponent < 0 ) |
| 2129 | { |
| 2130 | /* Decimal point is to the left. */ |
| 2131 | fltg->mantissa /= 10; |
| 2132 | normalizeFltg( fltg ); |
| 2133 | exponent++; |
| 2134 | } |
| 2135 | else if( exponent > 0 ) |
| 2136 | { |
| 2137 | /* Decimal point is to the right. */ |
| 2138 | fltg->mantissa *= 10; |
| 2139 | normalizeFltg( fltg ); |
| 2140 | exponent--; |
| 2141 | } |
| 2142 | } |
| 2143 | |
| 2144 | /* Discard the extra precsion used for calculating the number. */ |
| 2145 | fltg->mantissa >>= 3; |
| 2146 | fltg->exponent -= 3; |
| 2147 | if( negate ) |
| 2148 | { |
| 2149 | fltg->mantissa = (- fltg->mantissa ) & 077777777L; |
| 2150 | } |
| 2151 | return( fltg ); |
| 2152 | |
| 2153 | case 11: /* Error in format. */ |
| 2154 | /* Not a properly constructued floating point number. */ |
| 2155 | return( fltg ); |
| 2156 | default: |
| 2157 | break; |
| 2158 | } |
| 2159 | /* Set state for next pass through the loop. */ |
| 2160 | current_state = next_state; |
| 2161 | } |
| 2162 | } /* evalFltg() */ |
| 2163 | |
| 2164 | |
| 2165 | |
| 2166 | /******************************************************************************/ |
| 2167 | /* */ |
| 2168 | /* Function: normalizeFltg */ |
| 2169 | /* */ |
| 2170 | /* Synopsis: Normalize a PDP-8 double precision floating point number. */ |
| 2171 | /* */ |
| 2172 | /******************************************************************************/ |
| 2173 | void normalizeFltg( FLTG_T *fltg ) |
| 2174 | { |
| 2175 | /* Normalize the floating point number. */ |
| 2176 | if( fltg->mantissa != 0 ) |
| 2177 | { |
| 2178 | if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 ) |
| 2179 | { |
| 2180 | while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 ) |
| 2181 | |
| 2182 | { |
| 2183 | fltg->mantissa <<= 1; |
| 2184 | fltg->exponent--; |
| 2185 | } |
| 2186 | } |
| 2187 | else |
| 2188 | { |
| 2189 | while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 ) |
| 2190 | { |
| 2191 | fltg->mantissa >>= 1; |
| 2192 | fltg->exponent++; |
| 2193 | } |
| 2194 | } |
| 2195 | } |
| 2196 | else |
| 2197 | { |
| 2198 | fltg->exponent = 0; |
| 2199 | } |
| 2200 | return; |
| 2201 | } |
| 2202 | |
| 2203 | |
| 2204 | /******************************************************************************/ |
| 2205 | /* */ |
| 2206 | /* Function: incrementClc */ |
| 2207 | /* */ |
| 2208 | /* Synopsis: Set the next assembly location. Test for collision with */ |
| 2209 | /* the literal tables. */ |
| 2210 | /* */ |
| 2211 | /******************************************************************************/ |
| 2212 | WORD16 incrementClc() |
| 2213 | { |
| 2214 | testForLiteralCollision( clc ); |
| 2215 | |
| 2216 | /* Incrementing the location counter is not to change field setting. */ |
| 2217 | clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); |
| 2218 | fieldlc = clc & 07777; |
| 2219 | return( clc ); |
| 2220 | } /* incrementClc() */ |
| 2221 | |
| 2222 | |
| 2223 | /******************************************************************************/ |
| 2224 | /* */ |
| 2225 | /* Function: testForLiteralCollision */ |
| 2226 | /* */ |
| 2227 | /* Synopsis: Test the given location for collision with the literal tables. */ |
| 2228 | /* */ |
| 2229 | /******************************************************************************/ |
| 2230 | BOOL testForLiteralCollision( WORD16 loc ) |
| 2231 | { |
| 2232 | WORD16 pagelc; |
| 2233 | BOOL result = FALSE; |
| 2234 | WORD16 tmppage; |
| 2235 | |
| 2236 | tmppage = loc & 07600; |
| 2237 | pagelc = loc & 00177; |
| 2238 | |
| 2239 | if( tmppage == 0 ) |
| 2240 | { |
| 2241 | if( pagelc >= pz.loc && !pz.error ) |
| 2242 | { |
| 2243 | errorMessage( &pz_literal_overflow, -1 ); |
| 2244 | pz.error = TRUE; |
| 2245 | result = TRUE; |
| 2246 | } |
| 2247 | } |
| 2248 | else |
| 2249 | { |
| 2250 | if( pagelc >= cp.loc && !cp.error ) |
| 2251 | { |
| 2252 | errorMessage( &literal_overflow, -1 ); |
| 2253 | cp.error = TRUE; |
| 2254 | result = TRUE; |
| 2255 | } |
| 2256 | } |
| 2257 | return( result ); |
| 2258 | } /* testForLiteralCollision() */ |
| 2259 | |
| 2260 | |
| 2261 | /******************************************************************************/ |
| 2262 | /* */ |
| 2263 | /* Function: readLine */ |
| 2264 | /* */ |
| 2265 | /* Synopsis: Get next line of input. Print previous line if needed. */ |
| 2266 | /* */ |
| 2267 | /******************************************************************************/ |
| 2268 | void readLine() |
| 2269 | { |
| 2270 | WORD16 ix; |
| 2271 | WORD16 iy; |
| 2272 | char inpline[LINELEN]; |
| 2273 | |
| 2274 | listLine(); /* List previous line if needed. */ |
| 2275 | lineno++; /* Count lines read. */ |
| 2276 | indirect_generated = FALSE; /* Mark no indirect address generated. */ |
| 2277 | listed = FALSE; /* Mark as not listed. */ |
| 2278 | cc = 0; /* Initialize column counter. */ |
| 2279 | lexstartprev = 0; |
| 2280 | |
| 2281 | if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) |
| 2282 | { |
| 2283 | inpline[0] = '$'; |
| 2284 | inpline[1] = '\n'; |
| 2285 | inpline[2] = '\0'; |
| 2286 | error_in_line = TRUE; |
| 2287 | } |
| 2288 | else |
| 2289 | { |
| 2290 | error_in_line = FALSE; |
| 2291 | } |
| 2292 | |
| 2293 | /* Remove any tabs from the input line by inserting the required number */ |
| 2294 | /* of spaces to simulate 8 character tab stops. */ |
| 2295 | for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) |
| 2296 | { |
| 2297 | switch( inpline[ix] ) |
| 2298 | { |
| 2299 | case '\t': |
| 2300 | do |
| 2301 | { |
| 2302 | line[iy] = ' '; |
| 2303 | iy++; |
| 2304 | } |
| 2305 | while(( iy % 8 ) != 0 ); |
| 2306 | break; |
| 2307 | |
| 2308 | default: |
| 2309 | line[iy] = inpline[ix]; |
| 2310 | iy++; |
| 2311 | break; |
| 2312 | } |
| 2313 | } |
| 2314 | line[iy] = '\0'; |
| 2315 | |
| 2316 | /* If the line is terminated by CR-LF, remove, the CR. */ |
| 2317 | if( line[iy - 2] == '\r' ) |
| 2318 | { |
| 2319 | iy--; |
| 2320 | line[iy - 1] = line[iy - 0]; |
| 2321 | line[iy] = '\0'; |
| 2322 | } |
| 2323 | maxcc = iy; /* Save the current line length. */ |
| 2324 | } /* readLine() */ |
| 2325 | |
| 2326 | |
| 2327 | /******************************************************************************/ |
| 2328 | /* */ |
| 2329 | /* Function: listLine */ |
| 2330 | /* */ |
| 2331 | /* Synopsis: Output a line to the listing file. */ |
| 2332 | /* */ |
| 2333 | /******************************************************************************/ |
| 2334 | void listLine() |
| 2335 | /* generate a line of listing if not already done! */ |
| 2336 | { |
| 2337 | if( listfile != NULL && listed == FALSE ) |
| 2338 | { |
| 2339 | printLine( line, 0, 0, LINE ); |
| 2340 | } |
| 2341 | } /* listLine() */ |
| 2342 | |
| 2343 | |
| 2344 | /******************************************************************************/ |
| 2345 | /* */ |
| 2346 | /* Function: printPageBreak */ |
| 2347 | /* */ |
| 2348 | /* Synopsis: Output a Top of Form and listing header if new page necessary. */ |
| 2349 | /* */ |
| 2350 | /******************************************************************************/ |
| 2351 | void printPageBreak() |
| 2352 | { |
| 2353 | if( page_lineno >= LIST_LINES_PER_PAGE ) |
| 2354 | /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ |
| 2355 | { |
| 2356 | if( !list_title_set ) |
| 2357 | { |
| 2358 | strcpy( list_title, line ); |
| 2359 | if( list_title[strlen(list_title) - 1] == '\n' ) |
| 2360 | { |
| 2361 | list_title[strlen(list_title) - 1] = '\0'; |
| 2362 | } |
| 2363 | if( strlen( list_title ) > TITLELEN ) |
| 2364 | { |
| 2365 | list_title[TITLELEN] = '\0'; |
| 2366 | } |
| 2367 | list_title_set = TRUE; |
| 2368 | } |
| 2369 | topOfForm( list_title, NULL ); |
| 2370 | |
| 2371 | } |
| 2372 | } /* printPageBreak() */ |
| 2373 | |
| 2374 | |
| 2375 | /******************************************************************************/ |
| 2376 | /* */ |
| 2377 | /* Function: printLine */ |
| 2378 | /* */ |
| 2379 | /* Synopsis: Output a line to the listing file with new page if necessary. */ |
| 2380 | /* */ |
| 2381 | /******************************************************************************/ |
| 2382 | void printLine( char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle ) |
| 2383 | { |
| 2384 | if( listfile == NULL ) |
| 2385 | { |
| 2386 | save_error_count = 0; |
| 2387 | return; |
| 2388 | } |
| 2389 | |
| 2390 | printPageBreak(); |
| 2391 | |
| 2392 | list_lineno++; |
| 2393 | page_lineno++; |
| 2394 | switch( linestyle ) |
| 2395 | { |
| 2396 | default: |
| 2397 | case LINE: |
| 2398 | fprintf(listfile, "%5d ", lineno ); |
| 2399 | fputs( line, listfile ); |
| 2400 | listed = TRUE; |
| 2401 | break; |
| 2402 | |
| 2403 | case LINE_VAL: |
| 2404 | fprintf(listfile, "%5d %4.4o ", lineno, val ); |
| 2405 | fputs( line, listfile ); |
| 2406 | listed = TRUE; |
| 2407 | break; |
| 2408 | |
| 2409 | case LINE_LOC_VAL: |
| 2410 | if( !listed ) |
| 2411 | { |
| 2412 | if( indirect_generated ) |
| 2413 | { |
| 2414 | fprintf( listfile, "%5d %5.5o %4.4o@ ", lineno, loc, val ); |
| 2415 | } |
| 2416 | else |
| 2417 | { |
| 2418 | fprintf( listfile, "%5d %5.5o %4.4o ", lineno, loc, val ); |
| 2419 | } |
| 2420 | fputs( line, listfile ); |
| 2421 | listed = TRUE; |
| 2422 | } |
| 2423 | else |
| 2424 | { |
| 2425 | fprintf( listfile, " %5.5o %4.4o\n", loc, val ); |
| 2426 | } |
| 2427 | break; |
| 2428 | |
| 2429 | case LOC_VAL: |
| 2430 | fprintf( listfile, " %5.5o %4.4o\n", loc, val ); |
| 2431 | break; |
| 2432 | } |
| 2433 | printErrorMessages(); |
| 2434 | } /* printLine() */ |
| 2435 | |
| 2436 | |
| 2437 | /******************************************************************************/ |
| 2438 | /* */ |
| 2439 | /* Function: printErrorMessages */ |
| 2440 | /* */ |
| 2441 | /* Synopsis: Output any error messages from the current list of errors. */ |
| 2442 | /* */ |
| 2443 | /******************************************************************************/ |
| 2444 | void printErrorMessages() |
| 2445 | { |
| 2446 | WORD16 ix; |
| 2447 | WORD16 iy; |
| 2448 | |
| 2449 | if( listfile != NULL ) |
| 2450 | { |
| 2451 | /* If any errors, display them now. */ |
| 2452 | for( iy = 0; iy < save_error_count; iy++ ) |
| 2453 | { |
| 2454 | printPageBreak(); |
| 2455 | fprintf( listfile, "%-18.18s", error_list[iy].mesg ); |
| 2456 | if( error_list[iy].col >= 0 ) |
| 2457 | { |
| 2458 | for( ix = 0; ix < error_list[iy].col; ix++ ) |
| 2459 | { |
| 2460 | if( line[ix] == '\t' ) |
| 2461 | { |
| 2462 | putc( '\t', listfile ); |
| 2463 | } |
| 2464 | else |
| 2465 | { |
| 2466 | putc( ' ', listfile ); |
| 2467 | } |
| 2468 | } |
| 2469 | fputs( "^", listfile ); |
| 2470 | list_lineno++; |
| 2471 | page_lineno++; |
| 2472 | } |
| 2473 | fputs( "\n", listfile ); |
| 2474 | } |
| 2475 | } |
| 2476 | save_error_count = 0; |
| 2477 | } /* printErrorMessages() */ |
| 2478 | |
| 2479 | |
| 2480 | /******************************************************************************/ |
| 2481 | /* */ |
| 2482 | /* Function: endOfBinary */ |
| 2483 | /* */ |
| 2484 | /* Synopsis: Outputs both literal tables at the end of a binary segment. */ |
| 2485 | /* */ |
| 2486 | /******************************************************************************/ |
| 2487 | void endOfBinary() |
| 2488 | { |
| 2489 | /* Points to end of page for () operands. */ |
| 2490 | punchLiteralPool( &cp, clc - 1 ); |
| 2491 | /* Points to end of page zero for [] operands. */ |
| 2492 | punchLiteralPool( &pz, 0 ); |
| 2493 | if( error_in_line ) |
| 2494 | { |
| 2495 | listed = TRUE; |
| 2496 | clc = ( clc & 070000 ) + (( clc - 1 ) & 07777 ); |
| 2497 | errorMessage( &end_of_file, -1 ); |
| 2498 | clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); |
| 2499 | } |
| 2500 | else |
| 2501 | { |
| 2502 | listLine(); /* List line if not done yet. */ |
| 2503 | } |
| 2504 | return; |
| 2505 | } /* endOfBinary() */ |
| 2506 | |
| 2507 | |
| 2508 | /******************************************************************************/ |
| 2509 | /* */ |
| 2510 | /* Function: punchChecksum */ |
| 2511 | /* */ |
| 2512 | /* Synopsis: Output a checksum if the current mode requires it and an */ |
| 2513 | /* object file exists. */ |
| 2514 | /* */ |
| 2515 | /******************************************************************************/ |
| 2516 | void punchChecksum() |
| 2517 | { |
| 2518 | /* If the assembler has output any BIN data output the checksum. */ |
| 2519 | if( binary_data_output && !rim_mode ) |
| 2520 | { |
| 2521 | punchLocObject( 0, checksum ); |
| 2522 | } |
| 2523 | binary_data_output = FALSE; |
| 2524 | checksum = 0; |
| 2525 | } /* punchChecksum() */ |
| 2526 | |
| 2527 | |
| 2528 | /******************************************************************************/ |
| 2529 | /* */ |
| 2530 | /* Function: punchLeader */ |
| 2531 | /* */ |
| 2532 | /* Synopsis: Generate 2 feet of leader on object file, as per DEC */ |
| 2533 | /* documentation. Paper tape has 10 punches per inch. */ |
| 2534 | /* */ |
| 2535 | /******************************************************************************/ |
| 2536 | void punchLeader( WORD16 count ) |
| 2537 | { |
| 2538 | WORD16 ix; |
| 2539 | |
| 2540 | /* If value is zero, set to the default of 2 feet of leader. */ |
| 2541 | count = ( count == 0 ) ? 240 : count; |
| 2542 | |
| 2543 | if( objectfile != NULL ) |
| 2544 | { |
| 2545 | for( ix = 0; ix < count; ix++ ) |
| 2546 | { |
| 2547 | fputc( 0200, objectfile ); |
| 2548 | } |
| 2549 | } |
| 2550 | } /* punchLeader() */ |
| 2551 | |
| 2552 | |
| 2553 | /******************************************************************************/ |
| 2554 | /* */ |
| 2555 | /* Function: punchOrigin */ |
| 2556 | /* */ |
| 2557 | /* Synopsis: Output an origin to the object file. */ |
| 2558 | /* */ |
| 2559 | /******************************************************************************/ |
| 2560 | void punchOrigin( WORD16 loc ) |
| 2561 | { |
| 2562 | punchObject((( loc >> 6 ) & 0077 ) | 0100 ); |
| 2563 | punchObject( loc & 0077 ); |
| 2564 | } /* punchOrigin() */ |
| 2565 | |
| 2566 | |
| 2567 | /******************************************************************************/ |
| 2568 | /* */ |
| 2569 | /* Function: punchObject */ |
| 2570 | /* */ |
| 2571 | /* Synopsis: Put one character to object file and include it in checksum. */ |
| 2572 | /* */ |
| 2573 | /******************************************************************************/ |
| 2574 | void punchObject( WORD16 val ) |
| 2575 | { |
| 2576 | val &= 0377; |
| 2577 | if( objectfile != NULL ) |
| 2578 | { |
| 2579 | fputc( val, objectfile ); |
| 2580 | } |
| 2581 | checksum += val; |
| 2582 | binary_data_output = TRUE; |
| 2583 | } /* punchObject() */ |
| 2584 | |
| 2585 | |
| 2586 | /******************************************************************************/ |
| 2587 | /* */ |
| 2588 | /* Function: punchOutObject */ |
| 2589 | /* */ |
| 2590 | /* Synopsis: Output the current line and then then punch value to the */ |
| 2591 | /* object file. */ |
| 2592 | /* */ |
| 2593 | /******************************************************************************/ |
| 2594 | void punchOutObject( WORD16 loc, WORD16 val ) |
| 2595 | { |
| 2596 | printLine( line, ( field | loc ), val, LINE_LOC_VAL ); |
| 2597 | punchLocObject( loc, val ); |
| 2598 | } /* punchOutObject() */ |
| 2599 | |
| 2600 | /******************************************************************************/ |
| 2601 | /* */ |
| 2602 | /* Function: punchLocObject */ |
| 2603 | /* */ |
| 2604 | /* Synopsis: Output the word (with origin if rim format) to the object file.*/ |
| 2605 | /* */ |
| 2606 | /******************************************************************************/ |
| 2607 | void punchLocObject( WORD16 loc, WORD16 val ) |
| 2608 | { |
| 2609 | if( rim_mode ) |
| 2610 | { |
| 2611 | punchOrigin( loc ); |
| 2612 | } |
| 2613 | punchObject(( val >> 6 ) & 0077 ); |
| 2614 | punchObject( val & 0077 ); |
| 2615 | } /* punchLocObject() */ |
| 2616 | |
| 2617 | |
| 2618 | /******************************************************************************/ |
| 2619 | /* */ |
| 2620 | /* Function: punchLiteralPool */ |
| 2621 | /* */ |
| 2622 | /* Synopsis: Output the current page data. */ |
| 2623 | /* */ |
| 2624 | /******************************************************************************/ |
| 2625 | void punchLiteralPool( LPOOL_T *p, WORD16 lpool_page ) |
| 2626 | { |
| 2627 | WORD16 loc; |
| 2628 | WORD16 tmplc; |
| 2629 | |
| 2630 | lpool_page &= 07600; |
| 2631 | |
| 2632 | if( p->loc < 00200 ) |
| 2633 | { |
| 2634 | if( !rim_mode ) |
| 2635 | { |
| 2636 | /* Put out origin if not in rim mode. */ |
| 2637 | punchOrigin( p->loc | lpool_page ); |
| 2638 | } |
| 2639 | /* Put the literals in the object file. */ |
| 2640 | for( loc = p->loc; loc < 00200; loc++ ) |
| 2641 | { |
| 2642 | tmplc = loc + lpool_page; |
| 2643 | printLine( line, (field | tmplc), p->pool[loc], LOC_VAL ); |
| 2644 | punchLocObject( tmplc, p->pool[loc] ); |
| 2645 | } |
| 2646 | p->error = FALSE; |
| 2647 | p->loc = 00200; |
| 2648 | } |
| 2649 | } /* punchLiteralPool() */ |
| 2650 | |
| 2651 | |
| 2652 | /******************************************************************************/ |
| 2653 | /* */ |
| 2654 | /* Function: insertLiteral */ |
| 2655 | /* */ |
| 2656 | /* Synopsis: Add a value to the given literal pool if not already in pool. */ |
| 2657 | /* Return the location of the value in the pool. */ |
| 2658 | /* */ |
| 2659 | /******************************************************************************/ |
| 2660 | WORD16 insertLiteral( LPOOL_T *pool, WORD16 value ) |
| 2661 | { |
| 2662 | WORD16 ix; |
| 2663 | LPOOL_T *p; |
| 2664 | |
| 2665 | p = pool; |
| 2666 | |
| 2667 | /* If page zero is the current page, make sure that literals are inserted */ |
| 2668 | /* in the page zero literal table. */ |
| 2669 | if(( clc & 07600 ) == 0 ) |
| 2670 | { |
| 2671 | p = &pz; |
| 2672 | } |
| 2673 | |
| 2674 | /* Search the literal pool for any occurence of the needed value. */ |
| 2675 | ix = PAGE_SIZE - 1; |
| 2676 | while( ix >= p->loc && p->pool[ix] != value ) |
| 2677 | { |
| 2678 | ix--; |
| 2679 | } |
| 2680 | |
| 2681 | /* Check if value found in literal pool. If not, then insert value. */ |
| 2682 | if( ix < p->loc ) |
| 2683 | { |
| 2684 | (p->loc)--; |
| 2685 | p->pool[p->loc] = value; |
| 2686 | ix = p->loc; |
| 2687 | } |
| 2688 | return( ix ); |
| 2689 | } /* insertLiteral() */ |
| 2690 | |
| 2691 | |
| 2692 | /******************************************************************************/ |
| 2693 | /* */ |
| 2694 | /* Function: printSymbolTable */ |
| 2695 | /* */ |
| 2696 | /* Synopsis: Output the symbol table. */ |
| 2697 | /* */ |
| 2698 | /******************************************************************************/ |
| 2699 | void printSymbolTable() |
| 2700 | { |
| 2701 | int col; |
| 2702 | int cx; |
| 2703 | char *fmt; |
| 2704 | int ix; |
| 2705 | char mark; |
| 2706 | int page; |
| 2707 | int row; |
| 2708 | int symbol_base; |
| 2709 | int symbol_lines; |
| 2710 | |
| 2711 | symbol_base = number_of_fixed_symbols; |
| 2712 | |
| 2713 | for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) |
| 2714 | { |
| 2715 | topOfForm( list_title, s_symtable ); |
| 2716 | symbol_lines = LIST_LINES_PER_PAGE - page_lineno; |
| 2717 | |
| 2718 | for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) |
| 2719 | { |
| 2720 | list_lineno++; |
| 2721 | page_lineno++; |
| 2722 | fprintf( listfile, "%5d", list_lineno ); |
| 2723 | |
| 2724 | for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) |
| 2725 | { |
| 2726 | /* Get index of symbol for the current line and column */ |
| 2727 | cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; |
| 2728 | cx += symbol_base; |
| 2729 | |
| 2730 | /* Make sure that there is a symbol to be printed. */ |
| 2731 | if( number_of_fixed_symbols <= cx && cx < symbol_top ) |
| 2732 | { |
| 2733 | switch( symtab[cx].type & LABEL ) |
| 2734 | { |
| 2735 | case LABEL: |
| 2736 | fmt = " %c%-6.6s %5.5o "; |
| 2737 | break; |
| 2738 | |
| 2739 | default: |
| 2740 | fmt = " %c%-6.6s %4.4o "; |
| 2741 | break; |
| 2742 | } |
| 2743 | |
| 2744 | switch( symtab[cx].type & ( DEFINED | REDEFINED )) |
| 2745 | { |
| 2746 | case UNDEFINED: |
| 2747 | mark = '?'; |
| 2748 | break; |
| 2749 | |
| 2750 | case REDEFINED: |
| 2751 | mark = '#'; |
| 2752 | break; |
| 2753 | |
| 2754 | default: |
| 2755 | mark = ' '; |
| 2756 | break; |
| 2757 | } |
| 2758 | fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); |
| 2759 | ix++; |
| 2760 | } |
| 2761 | } |
| 2762 | fprintf( listfile, "\n" ); |
| 2763 | } |
| 2764 | } |
| 2765 | } /* printSymbolTable() */ |
| 2766 | |
| 2767 | |
| 2768 | /******************************************************************************/ |
| 2769 | /* */ |
| 2770 | /* Function: printPermanentSymbolTable */ |
| 2771 | /* */ |
| 2772 | /* Synopsis: Output the permanent symbol table to a file suitable for */ |
| 2773 | /* being input after the EXPUNGE pseudo-op. */ |
| 2774 | /* */ |
| 2775 | /******************************************************************************/ |
| 2776 | void printPermanentSymbolTable() |
| 2777 | { |
| 2778 | int ix; |
| 2779 | FILE *permfile; |
| 2780 | char *s_type; |
| 2781 | |
| 2782 | if(( permfile = fopen( permpathname, "w" )) == NULL ) |
| 2783 | { |
| 2784 | exit( 2 ); |
| 2785 | } |
| 2786 | |
| 2787 | fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); |
| 2788 | fprintf( permfile, " EXPUNGE\n/\n" ); |
| 2789 | /* Print the memory reference instructions first. */ |
| 2790 | s_type = "FIXMRI"; |
| 2791 | for( ix = 0; ix < symbol_top; ix++ ) |
| 2792 | { |
| 2793 | if( M_MRI( symtab[ix].type )) |
| 2794 | { |
| 2795 | fprintf( permfile, "%-7s %s=%4.4o\n", |
| 2796 | s_type, symtab[ix].name, symtab[ix].val ); |
| 2797 | } |
| 2798 | } |
| 2799 | |
| 2800 | s_type = " "; |
| 2801 | for( ix = 0; ix < symbol_top; ix++ ) |
| 2802 | { |
| 2803 | if( M_FIXED( symtab[ix].type )) |
| 2804 | { |
| 2805 | if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) |
| 2806 | { |
| 2807 | fprintf( permfile, "%-7s %s=%4.4o\n", |
| 2808 | s_type, symtab[ix].name, symtab[ix].val ); |
| 2809 | } |
| 2810 | } |
| 2811 | } |
| 2812 | fprintf( permfile, "/\n FIXTAB\n" ); |
| 2813 | fclose( permfile ); |
| 2814 | } /* printPermanentSymbolTable() */ |
| 2815 | |
| 2816 | |
| 2817 | /******************************************************************************/ |
| 2818 | /* */ |
| 2819 | /* Function: printCrossReference */ |
| 2820 | /* */ |
| 2821 | /* Synopsis: Output a cross reference (concordance) for the file being */ |
| 2822 | /* assembled. */ |
| 2823 | /* */ |
| 2824 | /******************************************************************************/ |
| 2825 | void printCrossReference() |
| 2826 | { |
| 2827 | int ix; |
| 2828 | int symbol_base; |
| 2829 | int xc; |
| 2830 | int xc_index; |
| 2831 | int xc_refcount; |
| 2832 | int xc_cols; |
| 2833 | |
| 2834 | /* Force top of form for first page. */ |
| 2835 | page_lineno = LIST_LINES_PER_PAGE; |
| 2836 | |
| 2837 | list_lineno = 0; |
| 2838 | symbol_base = number_of_fixed_symbols; |
| 2839 | |
| 2840 | for( ix = symbol_base; ix < symbol_top; ix++ ) |
| 2841 | { |
| 2842 | list_lineno++; |
| 2843 | page_lineno++; |
| 2844 | if( page_lineno >= LIST_LINES_PER_PAGE ) |
| 2845 | { |
| 2846 | topOfForm( list_title, s_xref ); |
| 2847 | } |
| 2848 | |
| 2849 | fprintf( listfile, "%5d", list_lineno ); |
| 2850 | |
| 2851 | /* Get reference count & index into concordance table for this symbol. */ |
| 2852 | xc_refcount = symtab[ix].xref_count; |
| 2853 | xc_index = symtab[ix].xref_index; |
| 2854 | /* Determine how to label symbol on concordance. */ |
| 2855 | switch( symtab[ix].type & ( DEFINED | REDEFINED )) |
| 2856 | { |
| 2857 | case UNDEFINED: |
| 2858 | fprintf( listfile, " U "); |
| 2859 | break; |
| 2860 | |
| 2861 | case REDEFINED: |
| 2862 | fprintf( listfile, " M %5d ", xreftab[xc_index] ); |
| 2863 | break; |
| 2864 | |
| 2865 | default: |
| 2866 | fprintf( listfile, " A %5d ", xreftab[xc_index] ); |
| 2867 | break; |
| 2868 | } |
| 2869 | fprintf( listfile, "%-6.6s ", symtab[ix].name ); |
| 2870 | |
| 2871 | /* Output the references, 8 numbers per line after symbol name. */ |
| 2872 | for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) |
| 2873 | { |
| 2874 | if( xc_cols >= XREF_COLUMNS ) |
| 2875 | { |
| 2876 | xc_cols = 0; |
| 2877 | page_lineno++; |
| 2878 | if( page_lineno >= LIST_LINES_PER_PAGE ) |
| 2879 | { |
| 2880 | topOfForm( list_title, s_xref); |
| 2881 | } |
| 2882 | list_lineno++; |
| 2883 | fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); |
| 2884 | } |
| 2885 | fprintf( listfile, " %5d", xreftab[xc_index + xc] ); |
| 2886 | } |
| 2887 | fprintf( listfile, "\n" ); |
| 2888 | } |
| 2889 | } /* printCrossReference() */ |
| 2890 | |
| 2891 | |
| 2892 | /******************************************************************************/ |
| 2893 | /* */ |
| 2894 | /* Function: topOfForm */ |
| 2895 | /* */ |
| 2896 | /* Synopsis: Prints title and sub-title on top of next page of listing. */ |
| 2897 | /* */ |
| 2898 | /******************************************************************************/ |
| 2899 | void topOfForm( char *title, char *sub_title ) |
| 2900 | { |
| 2901 | char temp[10]; |
| 2902 | |
| 2903 | list_pageno++; |
| 2904 | strcpy( temp, s_page ); |
| 2905 | sprintf( temp, "%s %d", s_page, list_pageno ); |
| 2906 | |
| 2907 | /* Output a top of form if not the first page of the listing. */ |
| 2908 | if( list_pageno > 1 ) |
| 2909 | { |
| 2910 | fprintf( listfile, "\f" ); |
| 2911 | } |
| 2912 | fprintf( listfile, "\n\n\n %-63s %10s\n", title, temp ); |
| 2913 | |
| 2914 | /* Reset the current page line counter. */ |
| 2915 | page_lineno = 3; |
| 2916 | if( sub_title != NULL ) |
| 2917 | { |
| 2918 | fprintf( listfile, "%80s\n", sub_title ); |
| 2919 | page_lineno++; |
| 2920 | } |
| 2921 | else |
| 2922 | { |
| 2923 | fprintf( listfile, "\n" ); |
| 2924 | page_lineno++; |
| 2925 | } |
| 2926 | fprintf( listfile, "\n" ); |
| 2927 | page_lineno++; |
| 2928 | } /* topOfForm() */ |
| 2929 | |
| 2930 | |
| 2931 | /******************************************************************************/ |
| 2932 | /* */ |
| 2933 | /* Function: lexemeToName */ |
| 2934 | /* */ |
| 2935 | /* Synopsis: Convert the current lexeme into a string. */ |
| 2936 | /* */ |
| 2937 | /******************************************************************************/ |
| 2938 | char *lexemeToName( char *name, WORD16 from, WORD16 term ) |
| 2939 | { |
| 2940 | WORD16 to; |
| 2941 | |
| 2942 | to = 0; |
| 2943 | |
| 2944 | while( from < term && to < ( SYMLEN - 1 )) |
| 2945 | { |
| 2946 | name[to++] = toupper( line[from++] ); |
| 2947 | } |
| 2948 | |
| 2949 | while( to < SYMLEN ) |
| 2950 | { |
| 2951 | name[to++] = '\0'; |
| 2952 | } |
| 2953 | return( name ); |
| 2954 | } /* lexemeToName() */ |
| 2955 | |
| 2956 | /******************************************************************************/ |
| 2957 | /* */ |
| 2958 | /* Function: defineLexeme */ |
| 2959 | /* */ |
| 2960 | /* Synopsis: Put lexeme into symbol table with a value. */ |
| 2961 | /* */ |
| 2962 | /******************************************************************************/ |
| 2963 | SYM_T *defineLexeme( WORD16 start, /* start of lexeme being defined. */ |
| 2964 | WORD16 term, /* end+1 of lexeme being defined. */ |
| 2965 | WORD16 val, /* value of lexeme being defined. */ |
| 2966 | SYMTYP type ) /* how symbol is being defined. */ |
| 2967 | { |
| 2968 | char name[SYMLEN]; |
| 2969 | |
| 2970 | lexemeToName( name, start, term); |
| 2971 | return( defineSymbol( name, val, type, start )); |
| 2972 | } /* defineLexeme() */ |
| 2973 | |
| 2974 | |
| 2975 | /******************************************************************************/ |
| 2976 | /* */ |
| 2977 | /* Function: defineSymbol */ |
| 2978 | /* */ |
| 2979 | /* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ |
| 2980 | /* not already in table. */ |
| 2981 | /* */ |
| 2982 | /******************************************************************************/ |
| 2983 | SYM_T *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start ) |
| 2984 | { |
| 2985 | SYM_T *sym; |
| 2986 | WORD16 xref_count; |
| 2987 | |
| 2988 | if( strlen( name ) < 1 ) |
| 2989 | { |
| 2990 | return( &sym_undefined ); /* Protect against non-existent names. */ |
| 2991 | } |
| 2992 | sym = lookup( name ); |
| 2993 | if( M_FIXED( sym->type )) |
| 2994 | { |
| 2995 | return( sym ); /* Can't modify permanent symbols. */ |
| 2996 | } |
| 2997 | |
| 2998 | xref_count = 0; /* Set concordance for normal defintion. */ |
| 2999 | |
| 3000 | if( M_DEFINED( sym->type )) |
| 3001 | { |
| 3002 | if( pass == 2 && sym->val != val ) |
| 3003 | { |
| 3004 | /* Generate diagnostic if redefining a symbol. */ |
| 3005 | if( M_REDEFINED( sym->type )) |
| 3006 | { |
| 3007 | errorSymbol( &redefined_symbol, sym->name, start ); |
| 3008 | } |
| 3009 | type = type | REDEFINED; |
| 3010 | sym->xref_count++; /* Referenced suymbol, count it. */ |
| 3011 | xref_count = sym->xref_count; |
| 3012 | } |
| 3013 | } |
| 3014 | |
| 3015 | if( pass == 2 && xref ) |
| 3016 | { |
| 3017 | /* Put the definition line number in the concordance table. */ |
| 3018 | /* Defined symbols are not counted as references. */ |
| 3019 | xreftab[sym->xref_index] = lineno; |
| 3020 | /* Put the line number in the concordance table. */ |
| 3021 | xreftab[sym->xref_index + xref_count] = lineno; |
| 3022 | } |
| 3023 | |
| 3024 | /* Now set the value and the type. */ |
| 3025 | sym->val = ( type == LABEL) ? val : val & 07777; |
| 3026 | sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; |
| 3027 | return( sym ); |
| 3028 | } /* defineSymbol() */ |
| 3029 | |
| 3030 | |
| 3031 | /******************************************************************************/ |
| 3032 | /* */ |
| 3033 | /* Function: lookup */ |
| 3034 | /* */ |
| 3035 | /* Synopsis: Find a symbol in table. If not in table, enter symbol in */ |
| 3036 | /* table as undefined. Return address of symbol in table. */ |
| 3037 | /* */ |
| 3038 | /******************************************************************************/ |
| 3039 | SYM_T *lookup( char *name ) |
| 3040 | { |
| 3041 | int ix; /* Insertion index */ |
| 3042 | int lx; /* Left index */ |
| 3043 | int rx; /* Right index */ |
| 3044 | |
| 3045 | /* First search the permanent symbols. */ |
| 3046 | lx = 0; |
| 3047 | ix = binarySearch( name, lx, number_of_fixed_symbols ); |
| 3048 | |
| 3049 | /* If symbol not in permanent symbol table. */ |
| 3050 | if( ix < 0 ) |
| 3051 | { |
| 3052 | /* Now try the user symbol table. */ |
| 3053 | ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); |
| 3054 | |
| 3055 | /* If symbol not in user symbol table. */ |
| 3056 | if( ix < 0 ) |
| 3057 | { |
| 3058 | /* Must put symbol in table if index is negative. */ |
| 3059 | ix = ~ix; |
| 3060 | if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) |
| 3061 | { |
| 3062 | errorSymbol( &symbol_table_full, name, lexstart ); |
| 3063 | exit( 1 ); |
| 3064 | } |
| 3065 | |
| 3066 | for( rx = symbol_top; rx >= ix; rx-- ) |
| 3067 | { |
| 3068 | symtab[rx + 1] = symtab[rx]; |
| 3069 | } |
| 3070 | symbol_top++; |
| 3071 | |
| 3072 | /* Enter the symbol as UNDEFINED with a value of zero. */ |
| 3073 | strcpy( symtab[ix].name, name ); |
| 3074 | symtab[ix].type = UNDEFINED; |
| 3075 | symtab[ix].val = 0; |
| 3076 | symtab[ix].xref_count = 0; |
| 3077 | if( xref && pass == 2 ) |
| 3078 | { |
| 3079 | xreftab[symtab[ix].xref_index] = 0; |
| 3080 | } |
| 3081 | } |
| 3082 | } |
| 3083 | |
| 3084 | return( &symtab[ix] ); /* Return the location of the symbol. */ |
| 3085 | } /* lookup() */ |
| 3086 | |
| 3087 | |
| 3088 | /******************************************************************************/ |
| 3089 | /* */ |
| 3090 | /* Function: binarySearch */ |
| 3091 | /* */ |
| 3092 | /* Synopsis: Searches the symbol table within the limits given. If the */ |
| 3093 | /* symbol is not in the table, it returns the insertion point. */ |
| 3094 | /* */ |
| 3095 | /******************************************************************************/ |
| 3096 | int binarySearch( char *name, int start, int symbol_count ) |
| 3097 | { |
| 3098 | int lx; /* Left index */ |
| 3099 | int mx; /* Middle index */ |
| 3100 | int rx; /* Right index */ |
| 3101 | int compare; /* Results of comparison */ |
| 3102 | |
| 3103 | lx = start; |
| 3104 | rx = symbol_count - 1; |
| 3105 | while( lx <= rx ) |
| 3106 | { |
| 3107 | mx = ( lx + rx ) / 2; /* Find center of search area. */ |
| 3108 | |
| 3109 | compare = strcmp( name, symtab[mx].name ); |
| 3110 | |
| 3111 | if( compare < 0 ) |
| 3112 | { |
| 3113 | rx = mx - 1; |
| 3114 | } |
| 3115 | else if( compare > 0 ) |
| 3116 | { |
| 3117 | lx = mx + 1; |
| 3118 | } |
| 3119 | else |
| 3120 | { |
| 3121 | return( mx ); /* Found a match in symbol table. */ |
| 3122 | } |
| 3123 | } /* end while */ |
| 3124 | return( ~lx ); /* Return insertion point. */ |
| 3125 | } /* binarySearch() */ |
| 3126 | |
| 3127 | |
| 3128 | /******************************************************************************/ |
| 3129 | /* */ |
| 3130 | /* Function: compareSymbols */ |
| 3131 | /* */ |
| 3132 | /* Synopsis: Used to presort the symbol table when starting assembler. */ |
| 3133 | /* */ |
| 3134 | /******************************************************************************/ |
| 3135 | int compareSymbols( const void *a, const void *b ) |
| 3136 | { |
| 3137 | return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); |
| 3138 | } /* compareSymbols() */ |
| 3139 | |
| 3140 | |
| 3141 | /******************************************************************************/ |
| 3142 | /* */ |
| 3143 | /* Function: evalSymbol */ |
| 3144 | /* */ |
| 3145 | /* Synopsis: Get the pointer for the symbol table entry if exists. */ |
| 3146 | /* If symbol doesn't exist, return a pointer to the undefined sym */ |
| 3147 | /* */ |
| 3148 | /******************************************************************************/ |
| 3149 | SYM_T *evalSymbol() |
| 3150 | { |
| 3151 | char name[SYMLEN]; |
| 3152 | SYM_T *sym; |
| 3153 | |
| 3154 | sym = lookup( lexemeToName( name, lexstart, lexterm )); |
| 3155 | |
| 3156 | sym->xref_count++; /* Count the number of references to symbol. */ |
| 3157 | |
| 3158 | if( xref && pass == 2 ) |
| 3159 | { |
| 3160 | /* Put the line number in the concordance table. */ |
| 3161 | xreftab[sym->xref_index + sym->xref_count] = lineno; |
| 3162 | } |
| 3163 | |
| 3164 | return( sym ); |
| 3165 | } /* evalSymbol() */ |
| 3166 | |
| 3167 | |
| 3168 | /******************************************************************************/ |
| 3169 | /* */ |
| 3170 | /* Function: moveToEndOfLine */ |
| 3171 | /* */ |
| 3172 | /* Synopsis: Move the parser input to the end of the current input line. */ |
| 3173 | /* */ |
| 3174 | /******************************************************************************/ |
| 3175 | void moveToEndOfLine() |
| 3176 | { |
| 3177 | while( !isend( line[cc] )) cc++; |
| 3178 | lexstart = cc; |
| 3179 | lexterm = cc; |
| 3180 | lexstartprev = lexstart; |
| 3181 | } /* moveToEndOfLine() */ |
| 3182 | |
| 3183 | /******************************************************************************/ |
| 3184 | /* */ |
| 3185 | /* Function: nextLexeme */ |
| 3186 | /* */ |
| 3187 | /* Synopsis: Get the next lexical element from input line. */ |
| 3188 | /* */ |
| 3189 | /******************************************************************************/ |
| 3190 | void nextLexeme() |
| 3191 | { |
| 3192 | /* Save start column of previous lexeme for diagnostic messages. */ |
| 3193 | lexstartprev = lexstart; |
| 3194 | lextermprev = lexterm; |
| 3195 | |
| 3196 | while( is_blank( line[cc] )) { cc++; } |
| 3197 | lexstart = cc; |
| 3198 | |
| 3199 | if( isalnum( line[cc] )) |
| 3200 | { |
| 3201 | while( isalnum( line[cc] )) { cc++; } |
| 3202 | } |
| 3203 | else if( isend( line[cc] )) |
| 3204 | { |
| 3205 | /* End-of-Line, don't advance cc! */ |
| 3206 | } |
| 3207 | else |
| 3208 | { |
| 3209 | switch( line[cc] ) |
| 3210 | { |
| 3211 | case '"': /* Quoted letter */ |
| 3212 | if( cc + 2 < maxcc ) |
| 3213 | { |
| 3214 | cc++; |
| 3215 | cc++; |
| 3216 | } |
| 3217 | else |
| 3218 | { |
| 3219 | errorMessage( &no_literal_value, lexstart ); |
| 3220 | cc++; |
| 3221 | } |
| 3222 | break; |
| 3223 | |
| 3224 | case '/': /* Comment, don't advance cc! */ |
| 3225 | break; |
| 3226 | |
| 3227 | default: /* All other punctuation. */ |
| 3228 | cc++; |
| 3229 | break; |
| 3230 | } |
| 3231 | } |
| 3232 | lexterm = cc; |
| 3233 | } /* nextLexeme() */ |
| 3234 | |
| 3235 | |
| 3236 | /******************************************************************************/ |
| 3237 | /* */ |
| 3238 | /* Function: nextLexBlank */ |
| 3239 | /* */ |
| 3240 | /* Synopsis: Used to prevent illegal blanks in expressions. */ |
| 3241 | /* */ |
| 3242 | /******************************************************************************/ |
| 3243 | void nextLexBlank() |
| 3244 | { |
| 3245 | nextLexeme(); |
| 3246 | if( is_blank( delimiter )) |
| 3247 | { |
| 3248 | errorMessage( &illegal_blank, lexstart - 1 ); |
| 3249 | } |
| 3250 | delimiter = line[lexterm]; |
| 3251 | } /* nextLexBlank() */ |
| 3252 | |
| 3253 | |
| 3254 | |
| 3255 | /******************************************************************************/ |
| 3256 | /* */ |
| 3257 | /* Function: pseudoOperators */ |
| 3258 | /* */ |
| 3259 | /* Synopsis: Process pseudo-ops (directives). */ |
| 3260 | /* */ |
| 3261 | /******************************************************************************/ |
| 3262 | BOOL pseudoOperators( PSEUDO_T val ) |
| 3263 | { |
| 3264 | int count; |
| 3265 | int delim; |
| 3266 | int index; |
| 3267 | int ix; |
| 3268 | int lexstartsave; |
| 3269 | WORD16 newfield; |
| 3270 | WORD16 oldclc; |
| 3271 | int pack; |
| 3272 | BOOL status; |
| 3273 | SYM_T *sym; |
| 3274 | FILE *temp; |
| 3275 | int term; |
| 3276 | WORD16 value; |
| 3277 | |
| 3278 | status = TRUE; |
| 3279 | switch( (PSEUDO_T) val ) |
| 3280 | { |
| 3281 | case BANK: |
| 3282 | errorSymbol( &no_pseudo_op, "BANK", lexstartprev ); |
| 3283 | /* should select a different 32K out of 128K */ |
| 3284 | break; |
| 3285 | |
| 3286 | case BINPUNCH: |
| 3287 | /* If there has been data output and this is a mode switch, set up to */ |
| 3288 | /* output data in BIN mode. */ |
| 3289 | if( binary_data_output && rim_mode ) |
| 3290 | { |
| 3291 | cp.loc = 00200; /* Clear the literal tables. */ |
| 3292 | cp.error = FALSE; |
| 3293 | pz.loc = 00200; |
| 3294 | pz.error = FALSE; |
| 3295 | punchLeader( 8 ); /* Generate a short leader/trailer. */ |
| 3296 | checksum = 0; |
| 3297 | binary_data_output = FALSE; |
| 3298 | } |
| 3299 | rim_mode = FALSE; |
| 3300 | break; |
| 3301 | |
| 3302 | case DECIMAL: |
| 3303 | radix = 10; |
| 3304 | break; |
| 3305 | |
| 3306 | case DUBL: |
| 3307 | inputDubl(); |
| 3308 | break; |
| 3309 | |
| 3310 | case EJECT: |
| 3311 | page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ |
| 3312 | status = FALSE; /* This will force reading of next line */ |
| 3313 | break; |
| 3314 | |
| 3315 | case ENPUNCH: |
| 3316 | if( pass == 2 ) |
| 3317 | { |
| 3318 | objectfile = objectsave; |
| 3319 | } |
| 3320 | break; |
| 3321 | |
| 3322 | case EXPUNGE: /* Erase symbol table */ |
| 3323 | if( pass == 1 ) |
| 3324 | { |
| 3325 | symtab[0] = sym_undefined; |
| 3326 | symbol_top = 0; |
| 3327 | number_of_fixed_symbols = symbol_top; |
| 3328 | fixed_symbols = &symtab[symbol_top - 1]; |
| 3329 | |
| 3330 | /* Enter the pseudo-ops into the symbol table. */ |
| 3331 | for( ix = 0; ix < DIM( pseudo ); ix++ ) |
| 3332 | { |
| 3333 | defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); |
| 3334 | } |
| 3335 | number_of_fixed_symbols = symbol_top; |
| 3336 | fixed_symbols = &symtab[symbol_top - 1]; |
| 3337 | } |
| 3338 | break; |
| 3339 | |
| 3340 | case FIELD: |
| 3341 | punchLiteralPool( &cp, clc - 1 ); |
| 3342 | punchLiteralPool( &pz, 0 ); |
| 3343 | newfield = field >> 12; |
| 3344 | lexstartsave = lexstartprev; |
| 3345 | if( isdone( line[lexstart] )) |
| 3346 | { |
| 3347 | newfield += 1; /* Blank FIELD directive. */ |
| 3348 | } |
| 3349 | else |
| 3350 | { |
| 3351 | newfield = (getExpr())->val; /* FIELD with argument. */ |
| 3352 | } |
| 3353 | |
| 3354 | if( rim_mode ) |
| 3355 | { |
| 3356 | errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields. */ |
| 3357 | } |
| 3358 | else if( newfield > 7 || newfield < 0 ) |
| 3359 | { |
| 3360 | errorMessage( &illegal_field_value, lexstartprev ); |
| 3361 | } |
| 3362 | else |
| 3363 | { |
| 3364 | value = (( newfield & 0007 ) << 3 ) | 00300; |
| 3365 | punchObject( value ); |
| 3366 | checksum -= value; /* Field punches are not added to checksum. */ |
| 3367 | field = newfield << 12; |
| 3368 | } |
| 3369 | |
| 3370 | clc = 0200 | field; |
| 3371 | fieldlc = clc & 07777; |
| 3372 | |
| 3373 | if( !rim_mode ) |
| 3374 | { |
| 3375 | punchOrigin( clc ); |
| 3376 | } |
| 3377 | break; |
| 3378 | |
| 3379 | case FIXMRI: |
| 3380 | if( line[lexterm] == '=' && isalpha( line[lexstart] )) |
| 3381 | { |
| 3382 | lexstartsave = lexstart; |
| 3383 | term = lexterm; |
| 3384 | nextLexeme(); /* Skip symbol. */ |
| 3385 | nextLexeme(); /* Skip trailing = */ |
| 3386 | defineLexeme( lexstartsave, term, getExprs(), MRI ); |
| 3387 | } |
| 3388 | else |
| 3389 | { |
| 3390 | errorLexeme( &symbol_syntax, lexstart ); |
| 3391 | nextLexeme(); /* Skip symbol. */ |
| 3392 | nextLexeme(); /* Skip trailing = */ |
| 3393 | (void) getExprs(); /* Skip expression. */ |
| 3394 | } |
| 3395 | break; |
| 3396 | |
| 3397 | case FIXTAB: |
| 3398 | /* Mark all current symbols as permanent symbols. */ |
| 3399 | for( ix = 0; ix < symbol_top; ix++ ) |
| 3400 | { |
| 3401 | symtab[ix].type = symtab[ix].type | FIXED; |
| 3402 | } |
| 3403 | number_of_fixed_symbols = symbol_top; |
| 3404 | fixed_symbols = &symtab[symbol_top - 1]; |
| 3405 | |
| 3406 | /* Re-sort the symbol table */ |
| 3407 | qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols ); |
| 3408 | break; |
| 3409 | |
| 3410 | case FLTG: |
| 3411 | inputFltg(); |
| 3412 | /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */ |
| 3413 | break; |
| 3414 | |
| 3415 | case IFDEF: |
| 3416 | if( isalpha( line[lexstart] )) |
| 3417 | { |
| 3418 | sym = evalSymbol(); |
| 3419 | nextLexeme(); |
| 3420 | if( M_DEFINED_CONDITIONALLY( sym->type )) |
| 3421 | { |
| 3422 | conditionTrue(); |
| 3423 | } |
| 3424 | else |
| 3425 | { |
| 3426 | conditionFalse(); |
| 3427 | } |
| 3428 | } |
| 3429 | else |
| 3430 | { |
| 3431 | errorLexeme( &label_syntax, lexstart ); |
| 3432 | } |
| 3433 | break; |
| 3434 | |
| 3435 | case IFNDEF: |
| 3436 | if( isalpha( line[lexstart] )) |
| 3437 | { |
| 3438 | sym = evalSymbol(); |
| 3439 | nextLexeme(); |
| 3440 | if( M_DEFINED_CONDITIONALLY( sym->type )) |
| 3441 | { |
| 3442 | conditionFalse(); |
| 3443 | } |
| 3444 | else |
| 3445 | { |
| 3446 | conditionTrue(); |
| 3447 | } |
| 3448 | } |
| 3449 | else |
| 3450 | { |
| 3451 | errorLexeme( &label_syntax, lexstart ); |
| 3452 | } |
| 3453 | break; |
| 3454 | |
| 3455 | case IFNZERO: |
| 3456 | if( (getExpr())->val == 0 ) |
| 3457 | { |
| 3458 | conditionFalse(); |
| 3459 | } |
| 3460 | else |
| 3461 | { |
| 3462 | conditionTrue(); |
| 3463 | } |
| 3464 | break; |
| 3465 | |
| 3466 | case IFZERO: |
| 3467 | if( (getExpr())->val == 0 ) |
| 3468 | { |
| 3469 | conditionTrue(); |
| 3470 | } |
| 3471 | else |
| 3472 | { |
| 3473 | conditionFalse(); |
| 3474 | } |
| 3475 | break; |
| 3476 | |
| 3477 | case NOPUNCH: |
| 3478 | if( pass == 2 ) |
| 3479 | { |
| 3480 | objectfile = NULL; |
| 3481 | } |
| 3482 | break; |
| 3483 | |
| 3484 | case OCTAL: |
| 3485 | radix = 8; |
| 3486 | break; |
| 3487 | |
| 3488 | case PAGE: |
| 3489 | punchLiteralPool( &cp, clc - 1 ); |
| 3490 | oldclc = clc; |
| 3491 | if( isdone( line[lexstart] )) |
| 3492 | { |
| 3493 | clc = ( clc + 0177 ) & 077600; /* No argumnet. */ |
| 3494 | fieldlc = clc & 07777; |
| 3495 | } |
| 3496 | else |
| 3497 | { |
| 3498 | value = (getExpr())->val; |
| 3499 | clc = ( field << 12 ) + (( value & 037 ) << 7 ); |
| 3500 | fieldlc = clc & 07777; |
| 3501 | } |
| 3502 | testForLiteralCollision( clc ); |
| 3503 | |
| 3504 | if( !rim_mode && clc != oldclc ) |
| 3505 | { |
| 3506 | punchOrigin( clc ); |
| 3507 | } |
| 3508 | break; |
| 3509 | |
| 3510 | case PAUSE: |
| 3511 | break; |
| 3512 | |
| 3513 | case RELOC: |
| 3514 | if( isdone( line[lexstart] )) |
| 3515 | { |
| 3516 | reloc = 0; /* Blank RELOC directive. */ |
| 3517 | } |
| 3518 | else |
| 3519 | { |
| 3520 | value = (getExpr())->val; /* RELOC with argument. */ |
| 3521 | reloc = value - ( clc + reloc ); |
| 3522 | } |
| 3523 | break; |
| 3524 | |
| 3525 | case RIMPUNCH: |
| 3526 | /* If the assembler has output any BIN data, output the literal tables */ |
| 3527 | /* and the checksum for what has been assembled and setup for RIM mode. */ |
| 3528 | if( binary_data_output && !rim_mode ) |
| 3529 | { |
| 3530 | endOfBinary(); |
| 3531 | punchChecksum(); |
| 3532 | punchLeader( 8 ); /* Generate a short leader/trailer. */ |
| 3533 | } |
| 3534 | rim_mode = TRUE; |
| 3535 | break; |
| 3536 | |
| 3537 | case SEGMNT: |
| 3538 | punchLiteralPool( &cp, clc - 1 ); |
| 3539 | if( isdone( line[lexstart] )) |
| 3540 | { /* No argument. */ |
| 3541 | clc = ( clc & 06000 ) + 02000; |
| 3542 | fieldlc = clc & 07777; |
| 3543 | } |
| 3544 | else |
| 3545 | { |
| 3546 | getExpr(); |
| 3547 | clc = ( val & 003 ) << 10; |
| 3548 | fieldlc = clc & 07777; |
| 3549 | } |
| 3550 | if( !rim_mode ) |
| 3551 | { |
| 3552 | punchOrigin( clc ); |
| 3553 | } |
| 3554 | testForLiteralCollision( clc ); |
| 3555 | break; |
| 3556 | |
| 3557 | case TEXT: |
| 3558 | delim = line[lexstart]; |
| 3559 | pack = 0; |
| 3560 | count = 0; |
| 3561 | index = lexstart + 1; |
| 3562 | while( line[index] != delim && !isend( line[index] )) |
| 3563 | { |
| 3564 | pack = ( pack << 6 ) | ( line[index] & 077 ); |
| 3565 | count++; |
| 3566 | if( count > 1 ) |
| 3567 | { |
| 3568 | punchOutObject( clc, pack ); |
| 3569 | incrementClc(); |
| 3570 | count = 0; |
| 3571 | pack = 0; |
| 3572 | } |
| 3573 | index++; |
| 3574 | } |
| 3575 | |
| 3576 | if( count != 0 ) |
| 3577 | { |
| 3578 | punchOutObject( clc, pack << 6 ); |
| 3579 | incrementClc(); |
| 3580 | } |
| 3581 | else |
| 3582 | { |
| 3583 | punchOutObject( clc, 0 ); |
| 3584 | incrementClc(); |
| 3585 | } |
| 3586 | |
| 3587 | if( isend( line[index] )) |
| 3588 | { |
| 3589 | cc = index; |
| 3590 | lexterm = cc; |
| 3591 | errorMessage( &text_string, cc ); |
| 3592 | } |
| 3593 | else |
| 3594 | { |
| 3595 | cc = index + 1; |
| 3596 | lexterm = cc; |
| 3597 | } |
| 3598 | nextLexeme(); |
| 3599 | break; |
| 3600 | |
| 3601 | case TITLE: |
| 3602 | delim = line[lexstart]; |
| 3603 | ix = lexstart + 1; |
| 3604 | /* Find string delimiter. */ |
| 3605 | do |
| 3606 | { |
| 3607 | if( list_title[ix] == delim && list_title[ix + 1] == delim ) |
| 3608 | { |
| 3609 | ix++; |
| 3610 | } |
| 3611 | ix++; |
| 3612 | } while( line[ix] != delim && !isend(line[ix]) ); |
| 3613 | |
| 3614 | if( !isend( line[ix] ) ) |
| 3615 | { |
| 3616 | count = 0; |
| 3617 | ix = lexstart + 1; |
| 3618 | do |
| 3619 | { |
| 3620 | if( list_title[ix] == delim && list_title[ix + 1] == delim ) |
| 3621 | { |
| 3622 | ix++; |
| 3623 | } |
| 3624 | list_title[count] = line[ix]; |
| 3625 | count++; |
| 3626 | ix++; |
| 3627 | } while( line[ix] != delim && !isend(line[ix]) ); |
| 3628 | |
| 3629 | if( strlen( list_title ) > TITLELEN ) |
| 3630 | { |
| 3631 | list_title[TITLELEN] = '\0'; |
| 3632 | } |
| 3633 | |
| 3634 | cc = ix + 1; |
| 3635 | lexterm = cc; |
| 3636 | page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ |
| 3637 | list_title_set = TRUE; |
| 3638 | } |
| 3639 | else |
| 3640 | { |
| 3641 | cc = ix; |
| 3642 | lexterm = cc; |
| 3643 | errorMessage( &text_string, cc ); |
| 3644 | } |
| 3645 | |
| 3646 | nextLexeme(); |
| 3647 | break; |
| 3648 | |
| 3649 | case XLIST: |
| 3650 | if( isdone( line[lexstart] )) |
| 3651 | { |
| 3652 | temp = listfile; /* Blank XLIST directive. */ |
| 3653 | listfile = listsave; |
| 3654 | listsave = temp; |
| 3655 | } |
| 3656 | else |
| 3657 | { |
| 3658 | if( (getExpr())->val == 0 ) |
| 3659 | { |
| 3660 | if( listfile == NULL ) |
| 3661 | { |
| 3662 | listfile = listsave; |
| 3663 | listsave = NULL; |
| 3664 | } |
| 3665 | } |
| 3666 | else |
| 3667 | { |
| 3668 | if( listfile != NULL ) |
| 3669 | { |
| 3670 | listsave = listfile; |
| 3671 | listfile = NULL; |
| 3672 | } |
| 3673 | } |
| 3674 | } |
| 3675 | break; |
| 3676 | |
| 3677 | case ZBLOCK: |
| 3678 | value = (getExpr())->val; |
| 3679 | if( value < 0 ) |
| 3680 | { |
| 3681 | errorMessage( &zblock_too_small, lexstartprev ); |
| 3682 | } |
| 3683 | else if( value + ( clc & 07777 ) - 1 > 07777 ) |
| 3684 | { |
| 3685 | errorMessage( &zblock_too_large, lexstartprev ); |
| 3686 | } |
| 3687 | else |
| 3688 | { |
| 3689 | for( ; value > 0; value-- ) |
| 3690 | { |
| 3691 | punchOutObject( clc, 0 ); |
| 3692 | incrementClc(); |
| 3693 | } |
| 3694 | } |
| 3695 | |
| 3696 | break; |
| 3697 | |
| 3698 | default: |
| 3699 | break; |
| 3700 | } /* end switch for pseudo-ops */ |
| 3701 | return( status ); |
| 3702 | } /* pseudoOperators() */ |
| 3703 | |
| 3704 | |
| 3705 | /******************************************************************************/ |
| 3706 | /* */ |
| 3707 | /* Function: conditionFalse */ |
| 3708 | /* */ |
| 3709 | /* Synopsis: Called when a false conditional has been evaluated. */ |
| 3710 | /* Lex should be the opening <; ignore all text until */ |
| 3711 | /* the closing >. */ |
| 3712 | /* */ |
| 3713 | /******************************************************************************/ |
| 3714 | void conditionFalse() |
| 3715 | { |
| 3716 | int level; |
| 3717 | |
| 3718 | if( line[lexstart] == '<' ) |
| 3719 | { |
| 3720 | /* Invariant: line[cc] is the next unexamined character. */ |
| 3721 | level = 1; |
| 3722 | while( level > 0 ) |
| 3723 | { |
| 3724 | if( isend( line[cc] )) |
| 3725 | { |
| 3726 | readLine(); |
| 3727 | } |
| 3728 | else |
| 3729 | { |
| 3730 | switch( line[cc] ) |
| 3731 | { |
| 3732 | case '>': |
| 3733 | level--; |
| 3734 | cc++; |
| 3735 | break; |
| 3736 | |
| 3737 | case '<': |
| 3738 | level++; |
| 3739 | cc++; |
| 3740 | break; |
| 3741 | |
| 3742 | case '$': |
| 3743 | level = 0; |
| 3744 | cc++; |
| 3745 | break; |
| 3746 | |
| 3747 | default: |
| 3748 | cc++; |
| 3749 | break; |
| 3750 | } /* end switch */ |
| 3751 | } /* end if */ |
| 3752 | } /* end while */ |
| 3753 | nextLexeme(); |
| 3754 | } |
| 3755 | else |
| 3756 | { |
| 3757 | errorMessage( <_expected, lexstart ); |
| 3758 | } |
| 3759 | } /* conditionFalse() */ |
| 3760 | |
| 3761 | /******************************************************************************/ |
| 3762 | /* */ |
| 3763 | /* Function: conditionTrue */ |
| 3764 | /* */ |
| 3765 | /* Synopsis: Called when a true conditional has been evaluated. */ |
| 3766 | /* Lex should be the opening <; skip it and setup for */ |
| 3767 | /* normal assembly. */ |
| 3768 | /* */ |
| 3769 | /******************************************************************************/ |
| 3770 | void conditionTrue() |
| 3771 | { |
| 3772 | if( line[lexstart] == '<' ) |
| 3773 | { |
| 3774 | nextLexeme(); /* Skip the opening '<' */ |
| 3775 | } |
| 3776 | else |
| 3777 | { |
| 3778 | errorMessage( <_expected, lexstart ); |
| 3779 | } |
| 3780 | } /* conditionTrue() */ |
| 3781 | |
| 3782 | |
| 3783 | /******************************************************************************/ |
| 3784 | /* */ |
| 3785 | /* Function: errorLexeme */ |
| 3786 | /* */ |
| 3787 | /* Synopsis: Display an error message using the current lexical element. */ |
| 3788 | /* */ |
| 3789 | /******************************************************************************/ |
| 3790 | void errorLexeme( EMSG_T *mesg, WORD16 col ) |
| 3791 | { |
| 3792 | char name[SYMLEN]; |
| 3793 | |
| 3794 | errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); |
| 3795 | } /* errorLexeme() */ |
| 3796 | |
| 3797 | |
| 3798 | /******************************************************************************/ |
| 3799 | /* */ |
| 3800 | /* Function: errorSymbol */ |
| 3801 | /* */ |
| 3802 | /* Synopsis: Display an error message with a given string. */ |
| 3803 | /* */ |
| 3804 | /******************************************************************************/ |
| 3805 | void errorSymbol( EMSG_T *mesg, char *name, WORD16 col ) |
| 3806 | { |
| 3807 | char linecol[12]; |
| 3808 | char *s; |
| 3809 | |
| 3810 | if( pass == 2 ) |
| 3811 | { |
| 3812 | s = ( name == NULL ) ? "" : name ; |
| 3813 | errors++; |
| 3814 | sprintf( linecol, "(%d:%d)", lineno, col + 1 ); |
| 3815 | fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", |
| 3816 | filename, linecol, mesg->file, s, clc ); |
| 3817 | saveError( mesg->list, col ); |
| 3818 | } |
| 3819 | error_in_line = TRUE; |
| 3820 | } /* errorSymbol() */ |
| 3821 | |
| 3822 | |
| 3823 | /******************************************************************************/ |
| 3824 | /* */ |
| 3825 | /* Function: errorMessage */ |
| 3826 | /* */ |
| 3827 | /* Synopsis: Display an error message without a name argument. */ |
| 3828 | /* */ |
| 3829 | /******************************************************************************/ |
| 3830 | void errorMessage( EMSG_T *mesg, WORD16 col ) |
| 3831 | { |
| 3832 | char linecol[12]; |
| 3833 | |
| 3834 | if( pass == 2 ) |
| 3835 | { |
| 3836 | errors++; |
| 3837 | sprintf( linecol, "(%d:%d)", lineno, col + 1 ); |
| 3838 | fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", |
| 3839 | filename, linecol, mesg->file, clc ); |
| 3840 | saveError( mesg->list, col ); |
| 3841 | } |
| 3842 | error_in_line = TRUE; |
| 3843 | } /* errorMessage() */ |
| 3844 | |
| 3845 | /******************************************************************************/ |
| 3846 | /* */ |
| 3847 | /* Function: saveError */ |
| 3848 | /* */ |
| 3849 | /* Synopsis: Save the current error in a list so it may displayed after the */ |
| 3850 | /* the current line is printed. */ |
| 3851 | /* */ |
| 3852 | /******************************************************************************/ |
| 3853 | void saveError( char *mesg, WORD16 col ) |
| 3854 | { |
| 3855 | if( save_error_count < DIM( error_list )) |
| 3856 | { |
| 3857 | error_list[save_error_count].mesg = mesg; |
| 3858 | error_list[save_error_count].col = col; |
| 3859 | save_error_count++; |
| 3860 | } |
| 3861 | error_in_line = TRUE; |
| 3862 | |
| 3863 | if( listed ) |
| 3864 | { |
| 3865 | printErrorMessages(); |
| 3866 | } |
| 3867 | } /* saveError() */ |
| 3868 | /* End-of-File */ |