blob: 03e806eac956b439fcd2a06cec78440cef84dc90 [file] [log] [blame] [raw]
/*
* (C) Copyright 2002, Brian Knittel.
* You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
* RISK basis, there is no warranty of fitness for any purpose, and the rest of the
* usual yada-yada. Please keep this notice and the copyright in any distributions
* or modifications.
*
* This is not a supported product, but I welcome bug reports and fixes.
* Mail to sim@ibm1130.org
*/
#define VERSION "ASM1130 CROSS ASSEMBLER V1.22"
/********************************************************************************************
* ASM1130 - IBM 1130 Cross Assembler
*
* Version
* 1.22 - 2012Nov26 - When the -d argument is specified on the command line, % and < in sources
* act like ( and ), since in the DMS sources these characters need to be interpreted
* this way.
* 1.21 - 2012Oct25 - Fixed bug in XFLC. With no argument, it should spit out a zero
* value, thus reserving core for a variable. It was not, so separate
* variables ended up sharing the same core address.
* 1.20 - 2011Mar25 - Made hex output in listings uppercase
* 1.19 - 2008Nov25 - Set binout[4] in SBRK cards (probably irrelevant)
* 1.18 - 2006Sep09 - Fixed opcode for BOD instruction, error found by Luiz Carlos Orsoni
* 1.17 - 2004Dec14 - Made DEC, DECS output 0 if no argument is given
* 1.16 - 2004Nov12 - Output WAIT instruction for LIBF if output mode is not binary
* 1.15 - 2004Nov03 - Added -f option to offset displayed assembly address in listing
* This is useful when viewing listings of code that is assembled to
* one address but actually loaded to another (e.g. some APL\1130 modules)
* 1.14 - 2004Oct22 - Fixed problem with BSS complaining about negative
* sizes. This may be a fundamental problem with my using
* 32-bit expressions, but for now, it appears that just
* truncating the BSS size to 16 bits is sufficient to build DMS.
* 1.13 - 2004Jun05 - Fixed sign extension of constants in expressions. Statements
* like LD /FFFF were being assembled incorrectly.
* 1.12 - 2004Jun04 - Made WAIT instruction take a displacement value.
* Doesn't affect operation, but these are used as indicators
* in the IBM one-card diagnostic programs.
* Also -- should mention that the .IPL directive was
* removed some time ago. To create bootable cards,
* use -b flag to create binary output, and post-process the
* binary output with program "mkboot"
* 1.11 - 2004May22 - Added CMP, DCM, and DECS instructions for 1800,
* thanks to Kevin Everets.
* 1.10 - 2003Dec08 - Fixed opcode value for XCH instruction, thanks to
* Roger Simpson.
* 1.09 - 2003Aug03 - Added fxwrite so asm will write little-endian files
* on all CPUs.
* 1.08 - 2003Mar18 - Fixed bug that complained about valid MDX displacement of +127
* 1.07 - 2003Jan05 - Filenames are now left in lower case. SYMBOLS.SYS stays all upper case
* 1.06 - 2002May02 - Fixed bug in ebdic constants (data goes into low byte)
* First stab at adding ISS level # info, this is iffy
* 1.05 - 2002Apr24 - Made negative BSS size a warning not an error, as it
* it's looking like it happens twice in PTMASMBL.
* This version still doesn't do fixed point numbers and
* negative floats may be wrong.
* 1.04 - 2002Apr18 - Added binary (card loader format) output, removed
* interim IPL output formats and moved that to MKBOOT.
* Enhanced relocatable code handling. Added floating
* point constants, but don't know how to make fixed point
* constants yet. Made amenable to syntax variations found
* in the DMS sources. Doesn't properly handle ILS
* modules yet and ISS is probably wrong.
* 1.03 - 2002Apr10 - numerous fixes, began to add relative/absolute support
* 1.02 - 2002Feb26 - replaced "strupr" with "upcase" for compatibility
* 1.01 - 2002Feb25 - minor compiler compatibility changes
* 1.00 - 2002Feb01 - first release. Tested only under Win32.
* ---------------------------------------------------------------------------------
*
* Usage:
* asm1130 [-bdvsx] [-o[file]] [-l[file] [-fXXXX]] [-rN.M] file...
*
* Description:
* -b binary output (.bin, relocatable absolute format)
* -d compile with DMS source character adjustments: % is interpreted as ( and < is intrepreted as ).
* -v verbose
* -s print symbol table
* -x print cross references
* -o output file (default is name of first source file + extension .out or .bin)
* -l listing file (default is name of first source file + extension .lst)
* -y preload system symbol table SYMBOLS.SYS (from the current directory)
* -w write the system symbol table SYMBOLS.SYS in the current directory
* -W same as -w but don't prompt to confirm overwriting existing file
* -r set DMS release to release N version M, for sbrk cards
* -f Apply offset XXXX (hex) to APPARENT assembly address in leftmost column of listing
*
* Listing and symbol table output can be turned on by *LIST directives in the source, too
* Listing file default extension is .LST
*
* Input files can use strict IBM 1130 Assembler column format, or loose formatting
* with tabs, or any mix on a line-by-line basis. Input files default extension is .ASM.
*
* Strict specification is:
*
* label columns 1 - 5
* opcode 7 - 10
* tag 12
* index 13
* arguments 15 - 51
*
* Loose, indicated by presence of ascii tab character(s):
*
* label<tab>opcode<tab>index and format indicators<tab>arguments
*
* In both cases, the IBM convention that the arguments section ends with the
* first nonblank applies. This means that ".DC 1, 2, 3" assembles only the 1!
*
* Output file format is that used by the LOAD command in my 1130
* simulator. Lines are any of the following. All values are in hex:
*
* @addr load address for subsequent words is addr
* Znwords Zero the next "nwords" and increment load address by nwords.
* =addr set IAR register to address addr (a convenience)
* value load value at load address and increment load address
*
* Output file default extension is .OUT or .BIN for binary assemblies
*
* Note: this version does not handle relative assembly, and so doesn't carry
* absolute/relative indication through expression calculation.
*
* Seems to work. Was able to assemble the resident monitor OK.
* >>> Look for "bug here" though, for things to check out.
*
* Notes:
* We assume that the computer on which the assembler runs uses ANSI floating point.
* Also, the assembly of floating point values may be incorrect on non-Intel
* architectures, this needs to be investigated.
*
* org_advanced tells whether * in an expression refers to the address AFTER the
* instruction (1 or 2 words, depending on length). This is the case for opcodes
* but not all directives.
*
* Revision History
* 16Apr02 1.03 Added sector break, relocation flag output
* 02Apr02 1.02 Fixed bug in BOSC: it CAN be a short instruction.
* Added directives for 1130 and 1800 IPL output formats
* Added conditional assembly directives
********************************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <setjmp.h>
#include <time.h>
#include <ctype.h>
#include "util_io.h"
/********************************************************************************************
* DEFINITIONS
********************************************************************************************/
/* I have found some IBM source code where @ and ' seem interchangable (likely due to the
* use of 026 keypunches).
* Comment out this define to make @ and ' different in symbol names, keep to make equivalent
*/
#if defined(VMS)
# include <unistd.h> /* to pick up 'unlink' */
#endif
#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b)))
#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
#define MAX(a,b) (((a) >= (b)) ? (a) : (b))
#ifndef _WIN32
int strnicmp (char *a, char *b, int n);
int strcmpi (char *a, char *b);
#endif
#define FIX_ATS
#define DMSVERSION "V2M12" /* required 5 characters on sector break card col 67-71 */
#define DOLLAREXIT "/38" /* hmmm, are these really fixed absolutely in all versions? */
#define DOLLARDUMP "/3F"
#define SYSTEM_TABLE "SYMBOLS.SYS"
#define BOOL int
#define TRUE 1
#define FALSE 0
#define ISTV 0x33 /* magic number from DMS R2V12 monitorm symbol @ISTV */
#define MAXLITERALS 300
#define MAXENTRIES 14
#define LINEFORMAT " %4ld | %s"
#define LEFT_MARGIN " |"
/* XXXX XXXX XXXX XXXX XXXX XXXX */
/* org w1 w2 w3 w4 w5 */
/* XXXX 1111 2222 3333 4444 LLLL | */
/* 12345678901234567890123456789012 */
typedef enum {ABSOLUTE = 0, RELATIVE = 1, LIBF = 2, CALL = 3} RELOC;
typedef struct tag_symbol { /* symbol table entry: */
char *name; /* name of symbol */
int value; /* value (absolute) */
int pass; /* defined during pass # */
int defined; /* definition state, see #defines below */
RELOC relative; /* ABSOLUTE = absolute, RELATIVE = relative */
struct tag_symbol *next; /* next symbol in list */
struct tag_xref *xrefs; /* cross references */
} SYMBOL, *PSYMBOL;
#define S_UNDEFINED 0 /* values of 'defined' */
#define S_PROVISIONAL 1 /* usually an expression with forward references */
#define S_DEFINED 2 /* ordering must be undef < prov < def */
typedef struct tag_xref { /* cross reference entry */
char *fname; /* filename */
int lno; /* line number */
BOOL definition; /* true = definition, false = reference */
struct tag_xref *next; /* next reference */
} XREF, *PXREF;
typedef struct tag_expr { /* expression result: absolute or relative */
int value;
RELOC relative;
} EXPR;
typedef enum {PROGTYPE_ABSOLUTE = 1, PROGTYPE_RELOCATABLE = 2, PROGTYPE_LIBF = 3, PROGTYPE_CALL = 4,
PROGTYPE_ISSLIBF = 5, PROGTYPE_ISSCALL = 6, PROGTYPE_ILS = 7} PROGTYPE;
typedef enum {SUBTYPE_INCORE = 0, SUBTYPE_FORDISK = 1, SUBTYPE_ARITH = 2,
SUBTYPE_FORNONDISK = 3, SUBTYPE_FUNCTION=8} SUBTYPE;
typedef enum {INTMODE_UNSPECIFIED = 0, INTMODE_MATCHREAL = 0x0080, INTMODE_ONEWORD = 0x0090} INTMODE;
typedef enum {REALMODE_UNSPECIFIED = 0, REALMODE_STANDARD = 0x0001, REALMODE_EXTENDED = 0x0002} REALMODE;
#define OP_INDEXED 0x0300 /* 1130 opcode modifier bits */
#define OP_LONG 0x0400
#define OP_INDIRECT 0x0080
typedef enum {OUTMODE_LOAD, OUTMODE_BINARY} OUTMODE;
#ifdef _WIN32
# define OUTWRITEMODE "wb" /* write outfile in binary mode */
# define ENDLINE "\r\n" /* explictly write CR/LF */
#else
# define OUTWRITEMODE "w" /* use native mode */
# define ENDLINE "\n"
#endif
/********************************************************************************************
* GLOBALS
********************************************************************************************/
/* command line syntax */
char *usestr =
"Usage: asm1130 [-bdpsvwxy8] [-o[file]] [-l[file] [-fXXXX]] [-rN.M] file...\n\n"
"-b binary (relocatable format) output; default is simulator LOAD format\n"
"-d interpret % and < as ( and ), for assembling DMS sources\n"
"-p count passes required; no assembly output is created with this flag"
"-s add symbol table to listing\n"
"-v verbose mode\n"
"-w write system symbol table as SYMBOLS.SYS\n"
"-W same as -w but do not confirm overwriting previous file\n"
"-x add cross reference table to listing\n"
"-y preload system symbol table SYMBOLS.SYS\n"
"-8 enable IBM 1800 instructions\n" /* (alternately, rename or link executable to asm1800.exe) */
"-o set output file; default is first input file + .out or .bin\n"
"-l create listing file; default is first input file + .lst\n"
"-r set dms version to VN RM for system SBRK cards\n"
"-f apply offset XXXX (hex) to APPARENT assembly address listing\n";
BOOL verbose = FALSE; /* verbose mode flag */
BOOL tabformat = FALSE; /* TRUE if tabs were seen in the file */
BOOL enable_1800 = FALSE; /* TRUE if 1800 mode is enabled by flag or executable name */
int listoffset = 0; /* offset to use for listing assembly address column */
int pass; /* current assembler pass (1 or 2) */
char curfn[256]; /* current input file name */
char progname[8]; /* base name of primary input file */
char *outfn = NULL; /* output file name */
int lno; /* current input file line number */
BOOL preload = FALSE; /* preload system symbol table */
BOOL savetable = FALSE; /* write system symbol table */
BOOL saveprompt = TRUE; /* prompt before overwriting */
int nerrors = 0; /* count of errors */
int nwarnings = 0; /* count of warnings */
FILE *fin = NULL; /* current input file */
FILE *fout = NULL; /* output file stream */
OUTMODE outmode = OUTMODE_LOAD; /* output file mode */
int outcols = 0; /* columns written in using card output */
int maxiplcols = 80;
char cardid[9]; /* characters used for IPL card ID */
FILE *flist = NULL; /* listing file stream */
char *listfn = NULL; /* listing filename */
BOOL do_list = FALSE; /* flag: create listing */
BOOL passcount = FALSE; /* flag: count passes only */
BOOL list_on = TRUE; /* listing is currently enabled */
BOOL do_xref = FALSE; /* cross reference listing */
BOOL do_syms = FALSE; /* symbol table listing */
BOOL ended = FALSE; /* end of current file */
BOOL hasforward = FALSE; /* true if there are any forward references */
char listline[350]; /* output listing line */
BOOL line_error; /* already saw an error on current line */
RELOC relocate = RELATIVE; /* relocatable assembly mode */
BOOL assembled = FALSE; /* true if any output has been generated */
int nwout; /* number of words written on current line */
int org = 0; /* output address (origin) */
int org_advanced; /* if nonzero, value of * is (instruction address + org_advanced) during evaluation */
int pta = -1; /* program transfer address */
BOOL cexpr = FALSE; /* "C" expression syntax (nonstandard, not enabled by default) */
PSYMBOL symbols = NULL; /* the symbol table (linear search) */
BOOL check_control = TRUE; /* check for control cards */
PROGTYPE progtype = PROGTYPE_RELOCATABLE; /* program type */
INTMODE intmode = INTMODE_UNSPECIFIED; /* integer mode */
REALMODE realmode = REALMODE_UNSPECIFIED; /* real mode */
int nintlevels = 0; /* # of interrupt levels for ISS */
int intlevel_primary = 0; /* primary level for ISS and level for ILS */
int intlevel_secondary = 0; /* secondary level for ISS */
int iss_number = 0; /* ISS number */
PSYMBOL entry[MAXENTRIES]; /* entries for subroutines */
int nentries = 0;
int ndefined_files = 0;
struct lit { /* accumulated literals waiting to be output */
int value; /* constant value */
int tagno; /* constant symbol tag number (e.g. _L001) */
BOOL hex; /* constant was expressed in hex */
BOOL even; /* constant was operand of a double-width instruction (e.g. AD) */
} literal[MAXLITERALS];
int n_literals = 0, lit_tag = 0;
BOOL requires_even_address; /* target of current instruction */
BOOL dmes_saved; /* odd character left over from dmes ending in ' */
int dmes_savew;
char opfield[256]; /* extracted operand field from source line */
char dmsversion[12] = DMSVERSION; /* version number for SBRK cards */
const char whitespace[] = " \t"; /* whitespace */
int ascii_to_ebcdic_table[128] =
{
/* */
0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f,
/* */
0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f,
/* spac ! " # $ % & ' ( ) * + , - . / */
0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f,
/* @ A B C D E F G H I J K L M N O */
0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,
/* P Q R S T U V W X Y Z [ \ ] & _ */
0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d,
/* a b c d e f g h i j k l m n o */
0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96,
/* p q r s t u v w x y z { | } ~ */
0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07,
};
/* note that by default, ascii_to_ebcdic_table maps % and < to the EBCDIC codes for these characters.
* If the -d command line switch is used to enable DMS mode, these characters map to ( and )
* Another way to look at the -d switch is that it interprets % and < as if they had been typed
* on an 026 Commercial keypunch.
*/
int ascii_to_1403_table[128] =
{ /* 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f */
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
0x7f,0x7f,0x7f,0x7f,0x62,0x7f,0x15,0x0b, 0x57,0x2f,0x23,0x6d,0x16,0x61,0x6e,0x4c,
0x49,0x40,0x01,0x02,0x43,0x04,0x45,0x46, 0x07,0x08,0x7f,0x7f,0x7f,0x4a,0x7f,0x7f,
0x7f,0x64,0x25,0x26,0x67,0x68,0x29,0x2a, 0x6b,0x2c,0x58,0x19,0x1a,0x5b,0x1c,0x5d,
0x5e,0x1f,0x20,0x0d,0x0e,0x4f,0x10,0x51, 0x52,0x13,0x54,0x7f,0x7f,0x7f,0x7f,0x7f,
0x7f,0x64,0x25,0x26,0x67,0x68,0x29,0x2a, 0x6b,0x2c,0x58,0x19,0x1a,0x5b,0x1c,0x5d,
0x5e,0x1f,0x20,0x0d,0x0e,0x4f,0x10,0x51, 0x52,0x13,0x54,0x7f,0x7f,0x7f,0x7f,0x7f
};
#include "../ibm1130_conout.h" /* conout_to_ascii_table */
#include "../ibm1130_prtwheel.h" /* 1132 printer printwheel data */
/********************************************************************************************
* PROTOTYPES
********************************************************************************************/
void init (int argc, char **argv);
void bail (char *msg);
void flag (char *arg);
void proc (char *fname);
void startpass (int n);
void errprintf (char *fmt, ...);
void asm_error (char *fmt, ...);
void asm_warning (char *fmt, ...);
char *astring (char *str);
PSYMBOL lookup_symbol (char *name, BOOL define);
void add_xref (PSYMBOL s, BOOL definition);
int get_symbol (char *name);
void set_symbol (char *name, int value, int known, RELOC relative);
char * gtok (char **pc, char *tok);
char *skipbl (char *c);
void sym_list (void);
void xref_list (void);
void listhdr (void);
int getexpr (char *pc, BOOL undefined_ok, EXPR *expr);
void passreport (void);
void listout (BOOL reset);
void output_literals (BOOL eof);
char *upcase (char *str);
void prep_line (char *line);
int ascii_to_hollerith (int ch);
char *detab (char *str);
void preload_symbols (void);
void save_symbols (void);
void bincard_init (void);
void bincard_writecard (char *sbrk_text);
void bincard_writedata (void);
void bincard_flush (void);
void bincard_sbrk (char *line);
void bincard_setorg (int neworg);
void bincard_writew (int word, RELOC relative);
void bincard_endcard (void);
void handle_sbrk (char *line);
void bincard_typecard (void);
void namecode (unsigned short *words, char *tok);
int signextend (int v);
/********************************************************************************************
* main routine
********************************************************************************************/
int main (int argc, char **argv)
{
int i, sawfile = FALSE;
init(argc, argv); /* initialize, process flags */
startpass(1); /* first pass, process files */
for (i = 1; i < argc; i++)
if (*argv[i] != '-')
proc(argv[i]), sawfile = TRUE;
if (! sawfile) /* should have seen at least one file */
bail(usestr);
if (passcount) {
passreport();
return 0;
}
startpass(2); /* second pass, process files again */
for (i = 1; i < argc; i++)
if (*argv[i] != '-')
proc(argv[i]);
if (outmode == OUTMODE_LOAD) {
if (pta >= 0) /* write start address to the load file */
fprintf(fout, "=%04X" ENDLINE, pta & 0xFFFF);
}
else
bincard_endcard();
if (flist) {
if (nerrors || nwarnings) { /* summarize (or summarise) */
if (nerrors == 0)
fprintf(flist, "There %s ", (nwarnings == 1) ? "was" : "were");
else
fprintf(flist, "\nThere %s %d error%s %s",
(nerrors == 1) ? "was" : "were", nerrors, (nerrors == 1) ? "" : "s", nwarnings ? "and " : "");
if (nwarnings > 0)
fprintf(flist, "%d warning%s ", nwarnings, (nwarnings == 1) ? "" : "s");
fprintf(flist, "in this assembly\n");
}
else
fprintf(flist, "\nThere were no errors in this assembly\n");
}
if (flist) { /* finish the listing */
if (pta >= 0)
fprintf(flist, "\nProgram transfer address = %04X\n", pta);
if (do_xref)
xref_list();
else if (do_syms)
sym_list();
}
if (savetable)
save_symbols();
return 0; /* all done */
}
/********************************************************************************************
* init - initialize assembler, process command line flags
********************************************************************************************/
void init (int argc, char **argv)
{
int i;
enable_1800 = strstr(argv[0], "1800") != NULL; /* if "1800" appears in the executable name, enable 1800 extensions */
for (i = 1; i < argc; i++) /* process command line switches */
if (*argv[i] == '-')
flag(argv[i]+1);
}
/********************************************************************************************
* flag - process one command line switch
********************************************************************************************/
void flag (char *arg)
{
int major, minor;
while (*arg) {
switch (*arg++) {
case 'o': /* output (load) file name */
if (! *arg)
bail(usestr);
outfn = arg;
return;
case 'p':
passcount = TRUE;
break;
case 'v': /* mumble while running */
verbose = TRUE;
break;
case 'x': /* print cross reference table */
do_xref = TRUE;
break;
case 's': /* print symbol table */
do_syms = TRUE;
break;
case 'l': /* listing file name */
listfn = (* arg) ? arg : NULL;
do_list = TRUE;
return;
case 'W':
saveprompt = FALSE;
/* fall through */
case 'w':
savetable = TRUE;
break;
case 'y':
preload = TRUE;
break;
case 'b':
outmode = OUTMODE_BINARY;
break;
case '8':
enable_1800 = TRUE;
break;
case 'r':
if (sscanf(arg, "%d.%d", &major, &minor) != 2)
bail(usestr);
sprintf(dmsversion, "V%01.1dM%02.2d", major, minor);
return;
case 'f':
if (sscanf(arg, "%x", &listoffset) != 1)
bail(usestr);
if (listoffset & 0x8000) /* sign extend from 16 to (int) bits */
listoffset |= ~0x7FFF;
return;
case 'd': /* DMS source code mode: treat % and < as ( and ) */
ascii_to_ebcdic_table['%'] = ascii_to_ebcdic_table['(']; /* an alternate intpretation of -d is that it interprets */
ascii_to_ebcdic_table['<'] = ascii_to_ebcdic_table[')']; /* % and < as if from an 026 Commercial keypunch */
break;
default:
bail(usestr);
break;
}
}
}
/********************************************************************************************
* bail - print error message on stderr (only) and exit
********************************************************************************************/
void bail (char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
/********************************************************************************************
* errprintf - print error message to stderr
********************************************************************************************/
void errprintf (char *fmt, ...)
{
va_list args;
va_start(args, fmt); /* get pointer to argument list */
vfprintf(stderr, fmt, args); /* write errors to terminal (stderr) */
va_end(args);
}
/********************************************************************************************
* asm_error - report an error to listing file and to user's console
********************************************************************************************/
void asm_error (char *fmt, ...)
{
va_list args;
if (pass == 1) /* only print on pass 2 */
return;
va_start(args, fmt); /* get pointer to argument list */
fprintf(stderr, "E: %s (%d): ", curfn, lno);
vfprintf(stderr, fmt, args); /* write errors to terminal (stderr) */
putc('\n', stderr);
if (flist != NULL && list_on) {
listout(FALSE);
line_error = TRUE;
fprintf(flist, "**** Error: ");
vfprintf(flist, fmt, args); /* write errors to listing file */
putc('\n', flist);
}
nerrors++;
va_end(args);
}
/********************************************************************************************
* asm_warning - same but warnings are not counted
********************************************************************************************/
void asm_warning (char *fmt, ...)
{
va_list args;
if (pass == 1) /* only print on pass 2 */
return;
va_start(args, fmt); /* get pointer to argument list */
fprintf(stderr, "W: %s (%d): ", curfn, lno);
vfprintf(stderr, fmt, args); /* write errors to terminal (stderr) */
putc('\n', stderr);
if (flist != NULL && list_on) {
listout(FALSE);
line_error = TRUE;
fprintf(flist, "**** Warning: ");
vfprintf(flist, fmt, args); /* write errors to listing file */
putc('\n', flist);
}
nwarnings++;
}
/********************************************************************************************
* sym_list - print the symbol table
********************************************************************************************/
void sym_list (void)
{
PSYMBOL s;
int n = 5;
if (symbols == NULL || flist == NULL)
return;
fprintf(flist, "\n=== SYMBOL TABLE ==============================================================\n");
for (s = symbols, n = 0; s != NULL; s = s->next) {
if (n >= 5) {
putc('\n', flist);
n = 0;
}
else if (n > 0)
fprintf(flist, " ");
fprintf(flist, "%-6s ", s->name);
if (s->defined == S_DEFINED)
fprintf(flist, "%04X%s", s->value & 0xFFFF, s->relative ? "R" : " ");
else
fprintf(flist, "UUUU ");
n++;
}
fprintf(flist, "\n");
}
/********************************************************************************************
* passreport - report # of passes required for assembly on the 1130
********************************************************************************************/
void passreport (void)
{
PSYMBOL s;
for (s = symbols; s != NULL; s = s->next) {
if (s->defined == S_UNDEFINED || s->defined == S_PROVISIONAL) {
printf("There are undefined symbols. Cannot determine pass requirement.\n");
return;
}
}
if (hasforward)
printf("There are forward references. Two passes are required.\n");
else
printf("There are no forward references. Only one pass is required.\n");
}
/********************************************************************************************
* xref_list - print the cross-reference table
********************************************************************************************/
void xref_list (void)
{
int n = 0;
PXREF x;
PSYMBOL s;
if (flist == NULL || symbols == NULL)
return;
fprintf(flist, "\n=== CROSS REFERENCES ==========================================================\n");
if (symbols == NULL || flist == NULL)
return;
fprintf(flist, "Name Val Defd Referenced\n");
for (s = symbols; s != NULL; s = s->next) {
fprintf(flist, "%-5s %04X%s", s->name, s->value & 0xFFFF, s->relative ? "R" : " ");
for (x = s->xrefs; x != NULL; x = x->next)
if (x->definition)
break;
if (x == NULL)
fprintf(flist, "----");
else
fprintf(flist, " %4d", x->lno);
for (n = 0, x = s->xrefs; x != NULL; x = x->next) {
if (x->definition)
continue;
if (n >= 12) {
n = 0;
fprintf(flist, "\n ");
}
fprintf(flist, " %4d", x->lno);
n++;
}
putc('\n', flist);
}
}
/********************************************************************************************
* listhdr - print a banner header in the listing file. Since it's not paginated
* at this time, this is not used often.
********************************************************************************************/
void listhdr (void)
{
time_t t;
time(&t);
fprintf(flist, "%s -- %s -- ", VERSION, dmsversion);
if (listoffset != 0) /* be sure to note difference in the listing */
fprintf(flist, "LIST OFFSET %04X -- ", listoffset & 0xFFFF);
fprintf(flist, "%s\n", ctime(&t));
}
/********************************************************************************************
* astring - allocate a copy of a string
********************************************************************************************/
char *astring (char *str)
{
static char *s = NULL;
if (s != NULL)
if (strcmp(s, str) == 0) /* if same as immediately previous allocation */
return s; /* return same pointer (why did I do this?) */
if ((s = malloc(strlen(str)+1)) == NULL)
bail("out of memory");
strcpy(s, str);
return s;
}
/********************************************************************************************
* lookup_symbol - get pointer to a symbol.
* If define is TRUE, creates and marks 'undefined' if not previously defined.
********************************************************************************************/
PSYMBOL lookup_symbol (char *name, BOOL define)
{
PSYMBOL s, n, prv = NULL;
int c;
char *at;
if (strlen(name) > 5) { /* (sigh) */
asm_error("Symbol '%s' is longer than 5 letters", name);
name[5] = '\0';
}
#ifdef FIX_ATS
while ((at = strchr(name, '@')) != NULL)
*at = '\'';
#endif
/* search sorted list of symbols */
for (s = symbols; s != NULL; prv = s, s = s->next) {
c = strcmpi(s->name, name);
if (c == 0)
return s;
if (c > 0)
break;
}
if (! define)
return NULL; /* not found */
if ((n = malloc(sizeof(SYMBOL))) == NULL)
bail("out of memory");
n->name = astring(name); /* symbol was undefined -- add it now */
n->value = 0;
n->defined = FALSE;
n->xrefs = NULL;
n->defined = FALSE;
n->next = s; /* link in alpha order */
if (prv == NULL) /* we stopped before first item in list */
symbols = n;
else
prv->next = n; /* insert after item before place we stopped */
return n;
}
/********************************************************************************************
* add_xref - add a cross reference entry to a symbol
********************************************************************************************/
void add_xref (PSYMBOL s, BOOL definition)
{
PXREF x, prv = NULL, n;
if (pass == 1 || ! do_xref) /* define only during 2nd pass and only if listing was requested */
return;
for (x = s->xrefs; x != NULL; prv = x, x = x->next)
if (strcmpi(x->fname, curfn) == 0 && x->lno == lno)
return; /* ignore multiple refs on same line */
if ((n = malloc(sizeof(XREF))) == NULL)
bail("out of memory");
n->fname = astring(curfn);
n->lno = lno;
n->definition = definition;
n->next = x; /* link at end of existing list */
if (prv == NULL)
s->xrefs = n;
else
prv->next = n;
}
/********************************************************************************************
* get_symbol - get a symbol value, defining if necessary
********************************************************************************************/
int get_symbol (char *name)
{
PSYMBOL s;
s = lookup_symbol(name, TRUE); /* lookup, define if necessary */
if (pass == 2) /* should be defined by now */
if (! s->defined)
asm_error("Symbol '%s' is undefined", name);
add_xref(s, FALSE); /* note the reference */
return s->value;
}
/********************************************************************************************
* set_symbol - set a symbol value. Known = TRUE means we really know the value;
* FALSE means we're calculating it with forward referenced values or something like
* that.
********************************************************************************************/
void set_symbol (char *name, int value, int known, RELOC relative)
{
PSYMBOL s;
char *at;
if (strlen(name) > 5) {
asm_error("Symbol '%s' is longer than 5 letters", name);
name[5] = '\0';
}
#ifdef FIX_ATS
while ((at = strchr(name, '@')) != NULL)
*at = '\'';
#endif
s = lookup_symbol(name, TRUE);
if (s->defined == S_DEFINED) /* once defined, it should not change */
if (s->value != value)
asm_error("Symbol '%s' %s", name, (s->pass == pass) ? "is multiply defined" : "changed between passes");
s->value = value;
s->relative = relative;
s->defined = known ? S_DEFINED : S_PROVISIONAL;
s->pass = pass;
if (! known)
hasforward = TRUE;
add_xref(s, TRUE); /* record the place of definition */
}
/********************************************************************************************
* skipbl - return pointer to first nonblank character in string s
********************************************************************************************/
char *skipbl (char *s)
{
while (*s && *s <= ' ')
s++;
return s;
}
/********************************************************************************************
* gtok - extracts a whitespace-delimited token from the string pointed to by *pc;
* stores the token into the buffer tok and returns pointer to same. Returns NULL
* when there are no tokens. Best to call repeatedly with a pointer to the source
* buffer, e.g.
* char *pbuf = buf;
* while (gtok(&pbuf, token) != NULL) ...
********************************************************************************************/
char * gtok (char **pc, char *tok)
{
char *s = *pc, *otok = tok;
while (*s && *s <= ' ') /* skip blanks */
s++;
if (! *s) { /* no tokens to be found */
*tok = '\0';
*pc = s;
return NULL;
}
while (*s > ' ') /* save nonblanks into 'tok' */
*tok++ = *s++;
*tok = '\0'; /* terminate */
*pc = s; /* adjust caller's pointer */
return otok; /* return pointer to token */
}
/* listing format:
*
* ADDR CODE SOURCE
* 0000 0000 0000 0000 0000 | XXXXXXXXXXXXXXXXX
*/
/********************************************************************************************
* trim - remove trailing whitespace from string s
********************************************************************************************/
char *trim (char *s)
{
char *os = s, *nb;
for (nb = s-1; *s; s++)
if (*s > ' ')
nb = s;
nb[1] = '\0';
return os;
}
/********************************************************************************************
* listout - emit current constructed output listing line held in "listline" and
* if "reset" is true, prepare listline for second and subsequent listing lines
* for a given input statement.
********************************************************************************************/
void listout (BOOL reset)
{
if (flist && list_on && ! line_error) {
trim(listline);
fputs(listline, flist);
putc('\n', flist);
if (reset)
sprintf(listline, LEFT_MARGIN, org);
}
}
/********************************************************************************************
* storew - store a word in the output medium (hex or binary file). Most of the time
* writew is used. Advances the origin!
********************************************************************************************/
void storew (int word, RELOC relative)
{
if (pass == 2) { /* save in output (load) file. */
switch (outmode) {
case OUTMODE_BINARY:
bincard_writew(word, relative);
break;
case OUTMODE_LOAD:
fprintf(fout, " %04X%s" ENDLINE, word & 0xFFFF,
(relative == ABSOLUTE) ? "" : (relative == RELATIVE) ? "R" :
(relative == LIBF) ? "L" : (relative == CALL) ? "$" : "?");
break;
default:
bail("in storew, can't happen");
}
}
if (relative != LIBF)
org++;
assembled = TRUE; /* remember that we wrote something */
}
/********************************************************************************************
* setw - store a word value in the current listing output line in position 'pos'.
********************************************************************************************/
void setw (int pos, int word, RELOC relative)
{
char tok[10], *p;
int i;
if (flist == NULL || ! list_on)
return;
sprintf(tok, "%04X", word & 0xFFFF);
for (i = 0, p = listline + 5*pos; i < 4; i++)
p[i] = tok[i];
if (relative == RELATIVE)
p[i] = 'R';
else if (relative != ABSOLUTE)
p[i] = '*';
}
/********************************************************************************************
* writew - emit an assembled word value. Words are also displayed in the listing file.
* if relative is true, a relocation entry should be recorded.
********************************************************************************************/
void writew (int word, RELOC relative)
{ /* first, the listing stuff... */
if (nwout == 0) { /* on first output word, display address in column 0 */
setw(0, org+listoffset, FALSE);
}
else if (nwout >= 4) { /* if 4 words have already been written, start new line */
listout(TRUE);
nwout = 0;
}
nwout++;
setw(nwout, word, relative); /* display word in the listing line */
storew(word, relative); /* write it to the output medium */
}
/********************************************************************************************
* setorg - take note of new load address
********************************************************************************************/
void setorg (int neworg)
{
if (pass == 2) {
setw(0, neworg+listoffset, FALSE); /* display in listing file in column 0 */
if (outmode == OUTMODE_LOAD) { /* write new load address to the output file */
fprintf(fout, "@%04X%s" ENDLINE, neworg & 0xFFFF, relocate ? "R" : "");
}
else {
bincard_setorg(neworg);
}
}
org = neworg;
}
/********************************************************************************************
* org_even - force load address to an even address
********************************************************************************************/
void org_even (void)
{
if (org & 1)
setorg(org+1);
}
/********************************************************************************************
* tabtok - get the token in tab-delimited column number i, from source string c,
* saving in string 'tok'. If save is nonnull, we copy the entire remainder of
* the input string in buffer 'save' (in contrast to 'tok' which gets only the
* first whitespace delimited token).
********************************************************************************************/
void tabtok (char *c, char *tok, int i, char *save)
{
*tok = '\0';
while (--i >= 0) { /* skip to i'th tab-delimited field */
if ((c = strchr(c, '\t')) == NULL) {
if (save) /* was none */
*save = '\0';
return;
}
c++;
}
while (*c == ' ') /* skip leading blanks */
c++;
if (save != NULL) /* save copy of entire remainder */
strcpy(save, c);
while (*c > ' ') { /* take up to any whitespace */
if (*c == '(') { /* if we start with a paren, take all up to closing paren including spaces */
while (*c && *c != ')')
*tok++ = *c++;
}
else if (*c == '.') { /* period means literal character following */
*tok++ = *c++;
if (*c)
*tok++ = *c++;
}
else
*tok++ = *c++;
}
*tok = '\0';
}
/********************************************************************************************
* coltok - extract a token from string c, saving to buffer tok, by examining
* columns ifrom through ito only. If save is nonnull, the entire remainder
* of the input from ifrom to the end is saved there. In this routine
* if condense is true, we save all nonwhite characters in the column range;
* not the usual thing. This helps us coalesce the format, tag, & index things
* nto one string for the simple minded parser. If condense is FALSE, we terminate
* on the first nonblank, except that if we start with a (, we take up to ) and
* then terminate on a space.
*
* ifrom and ito on entry are column numbers, not indices; we change that right away
********************************************************************************************/
void coltok (char *c, char *tok, int ifrom, int ito, BOOL condense, char *save)
{
char *otok = tok;
int i;
ifrom--;
ito--;
for (i = 0; i < ifrom; i++) {
if (c[i] == '\0') { /* line ended before this column */
*tok = '\0';
if (save)
*save = '\0';
return;
}
}
if (save) /* save from ifrom on */
strcpy(save, c+i);
if (condense) {
for (; i <= ito; i++) { /* save only nonwhite characters */
if (c[i] > ' ')
*tok++ = c[i];
}
}
else {
if (c[i] == ' ' && save != NULL)/* if it starts with a space, it's empty */
*save = '\0';
while (i <= ito) { /* take up to any whitespace */
if (c[i] <= ' ')
break;
else if (c[i] == '(') { /* starts with paren? take to close paren */
while (i <= ito && c[i]) {
if ((*tok++ = c[i++]) == ')')
break;
}
}
else if (c[i] == '.') { /* period means literal character following */
*tok++ = c[i++];
if (i <= ito && c[i])
*tok++ = c[i++];
}
else
*tok++ = c[i++];
}
}
*tok = '\0';
trim(otok);
}
/********************************************************************************************
* opcode table
********************************************************************************************/
/* modifiers for the opcode definition table: */
#define L "L" /* long */
#define X "X" /* absolute displacement */
#define I "I" /* indirect */
#define IDX "0123" /* indexed (some LDX commands in the DMS source say LDX L0, so accept 0 */
#define E "E" /* even address */
#define NONE ""
#define ALL L X I IDX /* hope non-Microsoft C accepts and concatenates strings like this */
#define ANY "\xFF"
#define NUMS "0123456789"
#define IS_DBL 0x0001 /* double word operand implies even address */
#define IS_ABS 0x0002 /* always uses absolute addressing mode (implied X) */
#define NO_IDX 0x0004 /* even with 1 or 2 modifier, this is not really indexed (for STX/LDX) */
#define NO_ARGS 0x0008 /* statement takes no arguments */
#define IS_1800 0x0010 /* 1800-only directive or instruction, flagged if 1800 mode is not enabled */
#define TRAP 0x1000 /* debug this instruction */
struct tag_op { /* OPCODE TABLE */
char *mnem;
int opcode;
void (*handler)(struct tag_op *op, char *label, char *mods, char *arg);
char *mods_allowed;
char *mods_implied;
int flags;
};
/* special opcode handlers */
void std_op (struct tag_op *op, char *label, char *mods, char *arg);
void b_op (struct tag_op *op, char *label, char *mods, char *arg);
void bsc_op (struct tag_op *op, char *label, char *mods, char *arg);
void bsi_op (struct tag_op *op, char *label, char *mods, char *arg);
void mdx_op (struct tag_op *op, char *label, char *mods, char *arg);
void shf_op (struct tag_op *op, char *label, char *mods, char *arg);
void x_aif (struct tag_op *op, char *label, char *mods, char *arg);
void x_aifb (struct tag_op *op, char *label, char *mods, char *arg);
void x_ago (struct tag_op *op, char *label, char *mods, char *arg);
void x_agob (struct tag_op *op, char *label, char *mods, char *arg);
void x_anop (struct tag_op *op, char *label, char *mods, char *arg);
void x_abs (struct tag_op *op, char *label, char *mods, char *arg);
void x_call (struct tag_op *op, char *label, char *mods, char *arg);
void x_dsa (struct tag_op *op, char *label, char *mods, char *arg);
void x_file (struct tag_op *op, char *label, char *mods, char *arg);
void x_link (struct tag_op *op, char *label, char *mods, char *arg);
void x_libf (struct tag_op *op, char *label, char *mods, char *arg);
void x_org (struct tag_op *op, char *label, char *mods, char *arg);
void x_opt (struct tag_op *op, char *label, char *mods, char *arg);
void x_ces (struct tag_op *op, char *label, char *mods, char *arg);
void x_bes (struct tag_op *op, char *label, char *mods, char *arg);
void x_bss (struct tag_op *op, char *label, char *mods, char *arg);
void x_dc (struct tag_op *op, char *label, char *mods, char *arg);
void x_dec (struct tag_op *op, char *label, char *mods, char *arg);
void x_decs (struct tag_op *op, char *label, char *mods, char *arg);
void x_ebc (struct tag_op *op, char *label, char *mods, char *arg);
void x_end (struct tag_op *op, char *label, char *mods, char *arg);
void x_ent (struct tag_op *op, char *label, char *mods, char *arg);
void x_epr (struct tag_op *op, char *label, char *mods, char *arg);
void x_equ (struct tag_op *op, char *label, char *mods, char *arg);
void x_exit (struct tag_op *op, char *label, char *mods, char *arg);
void x_ils (struct tag_op *op, char *label, char *mods, char *arg);
void x_iss (struct tag_op *op, char *label, char *mods, char *arg);
void x_libr (struct tag_op *op, char *label, char *mods, char *arg);
void x_lorg (struct tag_op *op, char *label, char *mods, char *arg);
void x_dmes (struct tag_op *op, char *label, char *mods, char *arg);
void x_dn (struct tag_op *op, char *label, char *mods, char *arg);
void x_dump (struct tag_op *op, char *label, char *mods, char *arg);
void x_pdmp (struct tag_op *op, char *label, char *mods, char *arg);
void x_hdng (struct tag_op *op, char *label, char *mods, char *arg);
void x_list (struct tag_op *op, char *label, char *mods, char *arg);
void x_spac (struct tag_op *op, char *label, char *mods, char *arg);
void x_spr (struct tag_op *op, char *label, char *mods, char *arg);
void x_ejct (struct tag_op *op, char *label, char *mods, char *arg);
void x_trap (struct tag_op *op, char *label, char *mods, char *arg);
void x_xflc (struct tag_op *op, char *label, char *mods, char *arg);
struct tag_op ops[] = {
".OPT", 0, x_opt, NONE, NONE, 0, /* non-IBM extensions */
"TRAP", 0, x_trap, NONE, NONE, 0, /* assembler breakpoint trap */
".CES", 0, x_ces, NONE, NONE, 0, /* lets us specify simulated console entry switch values for startup */
"ABS", 0, x_abs, NONE, NONE, 0,
"BES", 0, x_bes, E, NONE, 0, /* standard pseudo-ops */
"BSS", 0, x_bss, E, NONE, 0,
"DC", 0, x_dc, NONE, NONE, 0,
"DEC", 0, x_dec, E, E, IS_DBL,
"DECS", 0, x_decs, E, E, IS_DBL, /* this is an IBM 1800 directive */
"DMES", 0, x_dmes, ANY, NONE, 0,
"DN", 0, x_dn, NONE, NONE, 0,
"DSA", 0, x_dsa, NONE, NONE, 0,
"DUMP", 0, x_dump, NONE, NONE, 0,
"EBC", 0, x_ebc, NONE, NONE, 0,
"EJCT", 0, x_ejct, NONE, NONE, 0,
"END", 0, x_end, NONE, NONE, 0,
"ENT", 0, x_ent, NONE, NONE, 0,
"EPR", 0, x_epr, NONE, NONE, 0,
"EQU", 0, x_equ, NONE, NONE, 0,
"EXIT", 0, x_exit, NONE, NONE, 0, /* alias for call $exit since we don't have macros yet */
"FILE", 0, x_file, NONE, NONE, 0,
"HDNG", 0, x_hdng, ANY, NONE, 0,
"ILS", 0, x_ils, NUMS, NONE, 0,
"ISS", 0, x_iss, NUMS, NONE, 0,
"LIBF", 0, x_libf, NONE, NONE, 0,
"LIBR", 0, x_libr, NONE, NONE, 0,
"LINK", 0, x_link, NONE, NONE, 0,
"LIST", 0, x_list, NONE, NONE, 0,
"LORG", 0, x_lorg, NONE, NONE, 0,
"ORG", 0, x_org, NONE, NONE, 0,
"PDMP", 0, x_pdmp, NONE, NONE, 0,
"SPAC", 0, x_spac, NONE, NONE, 0,
"SPR", 0, x_spr, NONE, NONE, 0,
"XFLC", 0, x_xflc, NONE, NONE, 0,
"A", 0x8000, std_op, ALL, NONE, 0, /* standard addressing ops */
"AD", 0x8800, std_op, ALL, NONE, IS_DBL,
"AND", 0xE000, std_op, ALL, NONE, 0,
"BSI", 0x4000, bsi_op, ALL, NONE, 0,
"CALL", 0x4000, x_call, ALL, L, 0, /* alias for BSI L, or external call */
"CMP", 0xB000, std_op, ALL, NONE, IS_1800, /* this is an IBM 1800-only instruction */
"DCM", 0xB800, std_op, ALL, NONE, IS_1800, /* this is an IBM 1800-only instruction */
"D" , 0xA800, std_op, ALL, NONE, 0,
"EOR", 0xF000, std_op, ALL, NONE, 0,
"LD", 0xC000, std_op, ALL, NONE, 0,
"LDD", 0xC800, std_op, ALL, NONE, IS_DBL,
"LDS", 0x2000, std_op, NONE, NONE, IS_ABS,
"LDX", 0x6000, std_op, ALL, NONE, IS_ABS|NO_IDX,
"M", 0xA000, std_op, ALL, NONE, 0,
"MDX", 0x7000, mdx_op, ALL, NONE, 0,
"MDM", 0x7000, mdx_op, L, L, 0, /* like MDX L */
"NOP", 0x1000, std_op, NONE, NONE, NO_ARGS,
"OR", 0xE800, std_op, ALL, NONE, 0,
"S", 0x9000, std_op, ALL, NONE, 0,
"SD", 0x9800, std_op, ALL, NONE, IS_DBL,
"STD", 0xD800, std_op, ALL, NONE, IS_DBL,
"STO", 0xD000, std_op, ALL, NONE, 0,
"STS", 0x2800, std_op, ALL, NONE, 0,
"STX", 0x6800, std_op, ALL, NONE, NO_IDX,
"WAIT", 0x3000, std_op, NONE, NONE, IS_ABS,
"XCH", 0x18D0, std_op, NONE, NONE, 0, /* same as RTE 16, 18C0 + 10 */
"XIO", 0x0800, std_op, ALL, NONE, IS_DBL,
"BSC", 0x4800, bsc_op, ALL, NONE, 0, /* branch family */
"BOSC", 0x4840, bsc_op, ALL, NONE, 0, /* is BOSC always long form? No. */
"SKP", 0x4800, bsc_op, NONE, NONE, 0, /* alias for BSC one word version */
"B", 0x4800, b_op, ALL, NONE, 0, /* alias for MDX or BSC L */
"BC", 0x4802, std_op, ALL, L, 0, /* alias for BSC L */
"BN", 0x4828, std_op, ALL, L, 0, /* alias for BSC L */
"BNN", 0x4810, std_op, ALL, L, 0, /* alias for BSC L */
"BNP", 0x4808, std_op, ALL, L, 0, /* alias for BSC L */
"BNZ", 0x4820, std_op, ALL, L, 0, /* alias for BSC L */
"BO", 0x4801, std_op, ALL, L, 0, /* alias for BSC L */
"BOD", 0x4804, std_op, ALL, L, 0, /* alias for BSC L */
"BP", 0x4830, std_op, ALL, L, 0, /* alias for BSC L */
"BZ", 0x4818, std_op, ALL, L, 0, /* alias for BSC L */
"RTE", 0x18C0, shf_op, IDX X, X, 0, /* shift family */
"SLA", 0x1000, shf_op, IDX X, X, 0,
"SLC", 0x10C0, shf_op, IDX X, X, 0,
"SLCA", 0x1040, shf_op, IDX X, X, 0,
"SLT", 0x1080, shf_op, IDX X, X, 0,
"SRA", 0x1800, shf_op, IDX X, X, 0,
"SRT", 0x1880, shf_op, IDX X, X, 0,
"AIF", 0, x_aif, NONE, NONE, 0, /* assemble if */
"AIFB", 0, x_aifb, NONE, NONE, 0, /* assemble if */
"AGO", 0, x_ago, NONE, NONE, 0, /* assemble goto */
"AGOB", 0, x_agob, NONE, NONE, 0, /* assemble goto */
"ANOP", 0, x_anop, NONE, NONE, 0, /* assemble target */
NULL /* end of table */
};
/********************************************************************************************
* addextn - apply file extension 'extn' to filename 'fname' and put result in 'outbuf'
* if outbuf is NULL, we allocate a buffer
********************************************************************************************/
char *addextn (char *fname, char *extn, char *outbuf)
{
char *buf, line[500], *c;
buf = (outbuf == NULL) ? line : outbuf;
strcpy(buf, fname); /* create listfn from first source filename (e.g. xxx.lst); */
if ((c = strrchr(buf, '\\')) == NULL)
if ((c = strrchr(buf, '/')) == NULL)
if ((c = strrchr(buf, ':')) == NULL)
c = buf;
if ((c = strrchr(c, '.')) == NULL)
strcat(buf, extn);
else
strcpy(c, extn);
return (outbuf == NULL) ? astring(line) : outbuf;
}
/********************************************************************************************
* controlcard - examine an assembler control card (* in column 1)
********************************************************************************************/
BOOL controlcard (char *line)
{
if (strnicmp(line, "*LIST", 5) == 0) { /* turn on listing file even if not specified on command line */
do_list = list_on = TRUE;
return TRUE;
}
if (strnicmp(line, "*XREF", 5) == 0) {
do_xref = TRUE;
return TRUE;
}
if (strnicmp(line, "*PRINT SYMBOL TABLE", 19) == 0) {
do_syms = TRUE;
return TRUE;
}
if (strnicmp(line, "*SAVE SYMBOL TABLE", 18) == 0) {
savetable = TRUE;
return TRUE;
}
if (strnicmp(line, "*SYSTEM SYMBOL TABLE", 20) == 0) {
preload = TRUE;
preload_symbols();
return TRUE;
}
return FALSE;
}
/********************************************************************************************
* stuff - insert characters into a line
********************************************************************************************/
void stuff (char *buf, char *tok, int maxchars)
{
while (*tok) {
*buf++ = *tok++;
if (maxchars)
if (--maxchars <= 0)
break;
}
}
/********************************************************************************************
* format_line - construct a source code input line from components
********************************************************************************************/
void format_line (char *buf, char *label, char *op, char *mods, char *args, char *remarks)
{
int i;
if (tabformat) {
sprintf(buf, "%s\t%s\t%s\t%s\t%s", label, op, mods, args, remarks);
}
else {
for (i = 0; i < 72; i++)
buf[i] = ' ';
buf[i] = '\0';
stuff(buf+20, label, 5);
stuff(buf+26, op, 4);
stuff(buf+31, mods, 2);
stuff(buf+34, args, 72-34);
}
}
/********************************************************************************************
* lookup_op - find an opcode
********************************************************************************************/
struct tag_op * lookup_op (char *mnem)
{
struct tag_op *op;
int i;
for (op = ops; op->mnem != NULL; op++) {
if ((i = strcmp(op->mnem, mnem)) == 0)
return op;
if (i > 0)
break;
}
return NULL;
}
/********************************************************************************************
* bincard - routines to write IBM 1130 Card object format
********************************************************************************************/
unsigned short bincard[54]; /* the 54 data words that can fit on a binary format card */
char binflag[45]; /* the relocation flags of the 45 buffered object words (0, 1, 2, 3) */
int bincard_n = 0; /* number of object words stored in bincard (0-45) */
int bincard_seq = 0; /* card output sequence number */
int bincard_org = 0; /* origin of current card-full */
int bincard_maxaddr = 0;
BOOL bincard_first = TRUE; /* TRUE when we're to write the program type card */
/********************************************************************************************
* bincard_init - prepare a new object data output card
********************************************************************************************/
void bincard_init (void)
{
memset(bincard, 0, sizeof(bincard)); /* clear card data */
memset(binflag, 0, sizeof(binflag)); /* clear relocation data */
bincard_n = 0; /* no data */
bincard[0] = bincard_org; /* store load address */
bincard_maxaddr = MAX(bincard_maxaddr, bincard_org-1); /* save highest address written-to (this may be a BSS) */
}
/********************************************************************************************
* binard_writecard - emit a card. sbrk_text = NULL for normal data cards, points to comment text for sbrk card
* note: sbrk_text if not NULL MUST be a writeable buffer of at LEAST 71 characters
********************************************************************************************/
void bincard_writecard (char *sbrk_text)
{
unsigned short binout[80];
char ident[12];
int i, j;
/* this is an SBRK record */
if (sbrk_text != NULL) { /* sbrk card has 4 binary words followed by comment text */
for (j = 66; j < 71; j++) /* be sure input columns 67..71 are nonblank (have version number) */
if (sbrk_text[j] <= ' ')
break;
if (j < 71) /* sbrk card didn't have the info, stuff in current release */
for (j = 0; j < 5; j++)
sbrk_text[66+j] = dmsversion[j];
binout[0] = 0;
binout[1] = 0;
binout[2] = 0;
binout[3] = 0x1000;
// V1.19 - in previous versions of asm1130, binout[4] was left unset, random
binout[4] = 0;
sbrk_text += 5; /* start at the real column 6 (after *SBRK */
for (j = 5; j < 72; j++)
binout[j] = (*sbrk_text) ? ascii_to_hollerith(*sbrk_text++) : 0;
}
else { /* binary card format packs 54 words into 72 columns */
for (i = j = 0; i < 54; i += 3, j += 4) {
binout[j ] = ( bincard[i] & 0xFFF0);
binout[j+1] = ((bincard[i] << 12) & 0xF000) | ((bincard[i+1] >> 4) & 0x0FF0);
binout[j+2] = ((bincard[i+1] << 8) & 0xFF00) | ((bincard[i+2] >> 8) & 0x00F0);
binout[j+3] = ((bincard[i+2] << 4) & 0xFFF0);
}
}
sprintf(ident, "%08ld", ++bincard_seq); /* append sequence text */
memmove(ident, progname, MIN(strlen(progname), 4));
for (i = 0; i < 8; i++)
binout[j++] = ascii_to_hollerith(ident[i]);
fxwrite(binout, sizeof(binout[0]), 80, fout); /* write card image */
}
/********************************************************************************************
* binard_writedata - emit an object data card
********************************************************************************************/
void bincard_writedata (void)
{
unsigned short rflag = 0;
int i, j, nflag = 0;
bincard[1] = 0; /* checksum */
bincard[2] = 0x0A00 | bincard_n; /* data card type + word count */
for (i = 0, j = 3; i < bincard_n; i++) { /* construct relocation indicator bitmap */
if (nflag == 8) {
bincard[j++] = rflag;
rflag = 0;
nflag = 0;
}
rflag = (rflag << 2) | (binflag[i] & 3);
nflag++;
}
if (nflag > 0)
bincard[j] = rflag << (16 - 2*nflag);
bincard_writecard(FALSE); /* emit the card */
}
/********************************************************************************************
* bincard_flush - flush any pending binary data
********************************************************************************************/
void bincard_flush (void)
{
if (bincard_n > 0)
bincard_writedata();
bincard_init();
}
/********************************************************************************************
* bincard_sbrk - emit an SBRK card
********************************************************************************************/
void bincard_sbrk (char *line)
{
if (bincard_first)
bincard_typecard();
else
bincard_flush();
bincard_writecard(line);
}
/********************************************************************************************
* bincard_setorg - set the origin
********************************************************************************************/
void bincard_setorg (int neworg)
{
bincard_org = neworg; /* set origin for next card */
bincard_flush(); /* flush any current data & store origin */
}
/********************************************************************************************
* bincard_endcard - write end of program card
********************************************************************************************/
void bincard_endcard (void)
{
bincard_flush();
bincard[0] = (bincard_maxaddr + 2) & ~1; /* effective length: add 1 to max origin, then 1 more to round up */
bincard[1] = 0;
bincard[2] = 0x0F00;
bincard[3] = pta & 0xFFFF;
bincard_writecard(NULL);
}
/********************************************************************************************
* bincard_typecard - write the program type
********************************************************************************************/
void bincard_typecard (void)
{
int i;
if (! bincard_first)
return;
bincard_first = FALSE;
memset(bincard, 0, sizeof(bincard));
bincard[2] = (unsigned short) ((progtype << 8) | intmode | realmode);
/* all indices not listed are documented as 'reserved' */
switch (progtype) {
case PROGTYPE_ABSOLUTE:
case PROGTYPE_RELOCATABLE:
/* bincard[ 4] = 0; // length of common (fortran only) */
bincard[ 5] = 0x0003;
/* bincard[ 6] = 0; // length of work area (fortran only) */
bincard[ 8] = ndefined_files;
namecode(&bincard[9], progname);
bincard[11] = (pta < 0) ? 0 : pta;
break;
case PROGTYPE_LIBF:
case PROGTYPE_CALL:
bincard[ 5] = 3*nentries;
for (i = 0; i < nentries; i++) {
namecode(&bincard[9+3*i], entry[i]->name);
bincard[11+3*i] = entry[i]->value;
}
break;
case PROGTYPE_ISSLIBF:
case PROGTYPE_ISSCALL:
bincard[ 5] = 6+nintlevels;
namecode(&bincard[9], entry[0]->name);
bincard[11] = entry[0]->value;
bincard[12] = iss_number + ISTV; /* magic number ISTV is 0x33 in DMS R2V12 */
bincard[13] = iss_number;
bincard[14] = nintlevels;
bincard[15] = intlevel_primary;
bincard[16] = intlevel_secondary;
bincard[29] = 1;
break;
case PROGTYPE_ILS:
bincard[ 2] = (unsigned short) (progtype << 8);
bincard[ 5] = 4;
bincard[12] = intlevel_primary;
break;
default:
bail("in bincard_typecard, can't happen");
}
bincard[1] = 0; /* checksum */
bincard_writecard(NULL);
bincard_init();
}
/********************************************************************************************
* bincard_writew - write a word to the current output card.
********************************************************************************************/
void bincard_writew (int word, RELOC relative)
{
if (pass != 2)
return;
if (bincard_first)
bincard_typecard();
else if (bincard_n >= 45) /* flush full card buffer */
bincard_flush();
binflag[bincard_n] = relative & 3; /* store relocation bits and data word */
bincard[9+bincard_n++] = word;
if (relative != LIBF) {
bincard_maxaddr = MAX(bincard_maxaddr, bincard_org);
bincard_org++;
}
}
/********************************************************************************************
* writetwo - notification that we are about to write two words which must stay together
********************************************************************************************/
void writetwo (void)
{
if (pass == 2 && outmode == OUTMODE_BINARY && bincard_n >= 44)
bincard_flush();
}
/********************************************************************************************
* handle_sbrk - handle an SBRK directive.
* This was not part of the 1130 assembler; they must have assembled DMS on a 360 using a cross assembler
********************************************************************************************/
void handle_sbrk (char *line)
{
char rline[90];
if (pass != 2)
return;
strncpy(rline, line, 81); /* get a copy and pad it if necessary to 80 characters */
rline[80] = '\0';
while (strlen(rline) < 80)
strcat(rline, " ");
switch (outmode) {
case OUTMODE_LOAD:
fprintf(fout, "#SBRK%s\n", trim(rline+5));
case OUTMODE_BINARY:
bincard_sbrk(rline);
break;
default:
bail("in handle_sbrk, can't happen");
}
}
/********************************************************************************************
* namecode - turn a string into a two-word packed name
********************************************************************************************/
void namecode (unsigned short *words, char *tok)
{
long val = 0;
int i, ch;
for (i = 0; i < 5; i++) { /* pick up bits */
if (*tok)
ch = *tok++;
else
ch = ' ';
val = (val << 6) | (ascii_to_ebcdic_table[ch] & 0x3F);
}
words[0] = (unsigned short) (val >> 16);
words[1] = (unsigned short) val;
}
/********************************************************************************************
* parse_line - parse one input line.
********************************************************************************************/
void parse_line (char *line)
{
char label[100], mnem[100], arg[200], mods[20], *c;
struct tag_op *op;
if (line[0] == '/' && line[1] == '/') /* job control card? probably best to ignore it */
return;
if (line[0] == '*') { /* control card comment or comment in tab-format file */
if (check_control) /* pay attention to control cards only at top of file */
if (! controlcard(line))
check_control = FALSE; /* first non-control card shuts off sensitivity to them */
if (strnicmp(line+1, "SBRK", 4) == 0)
handle_sbrk(line);
return;
}
check_control = FALSE; /* non-control card, consider them no more */
label[0] = '\0'; /* prepare to extract fields */
mods[0] = '\0';
mnem[0] = '\0';
arg[0] = '\0';
if (tabformat || strchr(line, '\t') != NULL) { /* if input line has tabs, parse loosely */
tabformat = TRUE; /* this is a tab-formatted file */
for (c = line; *c && *c <= ' '; c++) /* find first nonblank */
;
if (*c == '*' || ! *c) /* ignore as a comment */
return;
tabtok(line, label, 0, NULL);
tabtok(line, mnem, 1, NULL);
tabtok(line, mods, 2, NULL);
tabtok(line, arg, 3, opfield);
}
else { /* if no tabs, use strict card-column format */
if (line[20] == '*') /* comment */
return;
line[72] = '\0'; /* clip off sequence */
coltok(line, label, 21, 25, TRUE, NULL);
coltok(line, mnem, 27, 30, TRUE, NULL);
coltok(line, mods, 32, 33, TRUE, NULL);
coltok(line, arg, 35, 72, FALSE, opfield);
}
if (*label) /* display org in any line with a label */
setw(0, org+listoffset, FALSE);
if (! *mnem) { /* label w/o mnemonic, just define the symbol */
if (*label)
set_symbol(label, org, TRUE, relocate);
return;
}
if ((op = lookup_op(mnem)) == NULL) { /* look up mnemonic */
if (*label)
set_symbol(label, org, TRUE, relocate);/* at least define the label */
asm_error("Unknown opcode '%s'", mnem);
return;
}
if (op->flags & TRAP) /* assembler debugging breakpoint */
x_trap(op, label, mods, arg);
if (*op->mods_allowed != '\xFF') { /* validate modifiers against list of allowed characters */
for (c = mods; *c; ) {
if (strchr(op->mods_allowed, *c) == NULL) {
asm_warning("Modifier '%c' not permitted", *c);
strcpy(c, c+1); /* remove it and keep parsing */
}
else
c++;
}
}
strcat(mods, op->mods_implied); /* tack on implied modifiers */
if (strchr(mods, 'I')) /* indirect implies long */
strcat(mods, "L");
requires_even_address = op->flags & IS_DBL;
org_advanced = strchr(mods, 'L') ? 2 : 1; /* by default, * means address + 1 or 2. Sometimes it doesn't */
(op->handler)(op, label, mods, arg);
if ((op->flags & IS_1800) && ! enable_1800)
asm_warning("%s is IBM 1800-specific; use the -8 command line option", op->mnem);
}
/********************************************************************************************
* get one input line from current file or macro
********************************************************************************************/
BOOL get_line (char *buf, int nbuf, BOOL onelevel)
{
char *retval;
if (ended) /* we hit the END command */
return FALSE;
/* if macro active, return line from macro buffer, otherwise read from file */
/* do not pop end-of-macro if onelevel is TRUE */
if ((retval = fgets(buf, nbuf, fin)) == NULL)
return FALSE;
lno++; /* count the line */
return TRUE;
}
/********************************************************************************************
* proc - process one pass of one source file
********************************************************************************************/
void proc (char *fname)
{
char line[256], *c;
int i;
if (strchr(fname, '.') == NULL) /* if input file has no extension, */
addextn(fname, ".asm", curfn); /* set appropriate file extension */
else
strcpy(curfn, fname); /* otherwise use extension specified */
/* let's leave filename case alone even if it doesn't matter */
/*#if (defined(_WIN32) || defined(VMS)) */
/* upcase(curfn); // only force uppercase of name on Windows and VMS */
/*#endif */
if (progname[0] == '\0') { /* pick up primary filename */
if ((c = strrchr(curfn, '\\')) == NULL)
if ((c = strrchr(curfn, '/')) == NULL)
if ((c = strrchr(curfn, ':')) == NULL)
c = curfn;
strncpy(progname, c, sizeof(progname)); /* take name after path */
progname[sizeof(progname)-1] = '\0';
if ((c = strchr(progname, '.')) != NULL)/* remove extension */
*c = '\0';
}
lno = 0; /* reset global input line number */
ended = FALSE; /* have not seen END statement */
if (listfn == NULL) /* if list file name is undefined, */
listfn = addextn(fname, ".lst", NULL); /* create from first filename */
if (verbose)
fprintf(stderr, "--- Starting file %s pass %d\n", curfn, pass);
if ((fin = fopen(curfn, "r")) == NULL) {
perror(curfn); /* oops */
exit(1);
}
if (flist) { /* put banner in listing file */
strcpy(listline,"=== FILE ======================================================================");
for (i = 9, c = curfn; *c;)
listline[i++] = *c++;
listline[i] = ' ';
fputs(listline, flist);
putc('\n', flist);
list_on = TRUE;
}
/* read all lines till EOF or END statement */
while (get_line(line, sizeof(line), FALSE)) {
prep_line(line); /* preform standard line prep */
parse_line(line); /* parse */
listout(FALSE); /* complete the listing */
}
fclose(fin);
if (n_literals > 0) { /* force out any pending literal constants at end of file */
output_literals(TRUE);
listout(FALSE);
}
}
/********************************************************************************************
* prep_line - prepare input line for parsing
********************************************************************************************/
void prep_line (char *line)
{
char *c;
upcase(line); /* uppercase it */
nwout = 0; /* number of words output so far */
line_error = FALSE; /* no errors on this line so far */
for (c = line; *c; c++) { /* truncate at newline */
if (*c == '\r' || *c == '\n') {
*c = '\0';
break;
}
}
if (flist && list_on) { /* construct beginning of listing line */
if (tabformat)
sprintf(listline, LINEFORMAT, lno, detab(line));
else {
if (strlen(line) > 20) /* get the part where the commands start */
c = line+20;
else
c = "";
sprintf(listline, LINEFORMAT, lno, c);
stuff(listline, line, 20); /* stuff the left margin in to the left side */
}
}
}
/********************************************************************************************
* opcmp - operand name comparison routine for qsort
********************************************************************************************/
int opcmp (const void *a, const void *b)
{
return strcmp(((struct tag_op *) a)->mnem, ((struct tag_op *) b)->mnem);
}
/********************************************************************************************
* preload_symbols - load a saved symbol table
********************************************************************************************/
void preload_symbols (void)
{
FILE *fd;
char str[200], sym[20];
int v;
static BOOL preloaded_already = FALSE;
if (pass > 1 || preloaded_already)
return;
preloaded_already = TRUE;
if ((fd = fopen(SYSTEM_TABLE, "r")) == NULL) /* read the system symbol tabl */
perror(SYSTEM_TABLE);
else {
while (fgets(str, sizeof(str), fd) != NULL) {
if (sscanf(str, "%s %x", sym, &v) == 2)
set_symbol(sym, v, TRUE, FALSE);
}
fclose(fd);
}
}
/********************************************************************************************
* save_symbols - save a symbol table
********************************************************************************************/
void save_symbols (void)
{
FILE *fd;
char str[20];
PSYMBOL s;
if (relocate) {
fprintf(stderr, "Can't save symbol table unless ABS assembly\n");
return;
}
if ((fd = fopen(SYSTEM_TABLE, "r")) != NULL) {
fclose(fd);
if (saveprompt) {
printf("Overwrite system symbol table %s? ", SYSTEM_TABLE);
fgets(str, sizeof(str), stdin);
if (str[0] != 'y' && str[0] != 'Y')
return;
}
unlink(SYSTEM_TABLE);
}
if ((fd = fopen(SYSTEM_TABLE, "w")) == NULL) {
perror(SYSTEM_TABLE);
return;
}
for (s = symbols; s != NULL; s = s->next)
fprintf(fd, "%-5s %04X\n", s->name, s->value);
fclose(fd);
}
/********************************************************************************************
* startpass - initialize data structures, prepare to start a pass
********************************************************************************************/
void startpass (int n)
{
int nops;
struct tag_op *p;
pass = n; /* reset globals: pass number */
nerrors = 0; /* error count */
org = 0; /* load address (origin) */
lno = 0; /* input line number */
relocate = TRUE; /* relocatable assembly mode */
assembled = FALSE; /* true if any output has been generated */
list_on = do_list; /* listing enable */
dmes_saved = FALSE; /* partial character strings output */
n_literals = 0; /* literal values pending output */
lit_tag = 0;
if (pass == 1) { /* first pass only */
for (nops = 0, p = ops; p->mnem != NULL; p++, nops++) /* count opcodes */
;
qsort(ops, nops, sizeof(*p), opcmp); /* sort the opcode table */
if (preload)
preload_symbols();
}
else { /* second pass only */
if (outfn == NULL)
outfn = addextn(curfn, (outmode == OUTMODE_LOAD) ? ".out" : ".bin" , NULL);
if ((fout = fopen(outfn, OUTWRITEMODE)) == NULL) { /* open output file */
perror(outfn);
exit(1);
}
if (do_list) { /* open listing file */
if ((flist = fopen(listfn, "w")) == NULL) {
perror(listfn);
exit(1);
}
listhdr(); /* print banner */
}
}
}
/********************************************************************************************
* x_dc - DC define constant directive
********************************************************************************************/
void x_dc (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
/* char *tok; */
org_advanced = 1; /* assume * means this address+1 */
/* doesn't make sense, but I think I found DMS listings to support it */
if (strchr(mods, 'E') != NULL) /* force even address */
org_even();
setw(0, org+listoffset, FALSE); /* display org in listing line */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
getexpr(arg, FALSE, &expr);
writew(expr.value, expr.relative); /* store value */
}
/********************************************************************************************
* x_dec - DEC define double word constant directive.
********************************************************************************************/
/* wd[0]: 8 unused bits | characteristic (= exponent+128)
* wd[1]: sign + 15 msb of mantissa in 2's complement
* wd[2]: 16 lsb of mantissa
* NOTE: these are wrong with Fixed point numbers */
void convert_double_to_extended (double d, unsigned short *wd)
{
int neg, exp;
unsigned long mantissa;
unsigned char *byte = (unsigned char *) &d;
if (d == 0.) {
wd[0] = wd[1] = wd[2] = 0;
return;
}
/* 7 6 5 4 0 */
/* d = ansi real*8 SXXX XXXX XXXX MMMM MMMM MMMM MMMM MMMM ... MMMM MMMM */
neg = byte[7] & 0x80;
exp = ((byte[7] & 0x7F) << 4) | ((byte[6] & 0xF0) >> 4); /* extract exponent */
exp -= 1023; /* remove bias */
exp++; /* shift to account for implied 1 we added */
/* get 32 bits worth of mantissa. add the implied point */
mantissa = 0x80000000L | ((byte[6] & 0x0F) << 27) | (byte[5] << 19) | (byte[4] << 11) | (byte[3] << 3) | ((byte[2] & 0xE0) >> 5);
if (mantissa & (0x80000000L >> 31)) /* keep 31 bits, round if necessary */
mantissa += (0x80000000L >> 31);
mantissa >>= (32-31); /* get into low 31 bits */
/* now turn into IBM 1130 extended precision */
exp += 128;
if (neg)
mantissa = (unsigned long) (- (long) mantissa); /* two's complement */
wd[0] = (unsigned short) (exp & 0xFF);
wd[1] = (unsigned short) ((neg ? 0x8000 : 0) | ((mantissa >> (31-15)) & 0x7FFF));
wd[2] = (unsigned short) (mantissa & 0xFFFF);
}
/********************************************************************************************
* convert_double_to_standard -
********************************************************************************************/
void convert_double_to_standard (double d, unsigned short *wd)
{
int neg, exp;
unsigned long mantissa;
unsigned char *byte = (unsigned char *) &d;
if (d == 0.) {
wd[0] = wd[1] = 0;
return;
}
/* 7 6 5 4 0 */
/* d = ansi real*8 SXXX XXXX XXXX MMMM MMMM MMMM MMMM MMMM ... MMMM MMMM */
neg = byte[7] & 0x80;
exp = ((byte[7] & 0x7F) << 4) | ((byte[6] & 0xF0) >> 4); /* extract exponent */
exp -= 1023; /* remove bias */
exp++; /* shift to account for implied 1 we added */
/* get 32 bits worth of mantissa. add the implied point */
mantissa = 0x80000000L | ((byte[6] & 0x0F) << 27) | (byte[5] << 19) | (byte[4] << 11) | (byte[3] << 3) | ((byte[2] & 0xE0) >> 5);
/* if (mantissa & (0x80000000L >> 23)) // keep 23 bits, round if necessary */
/* mantissa += (0x80000000L >> 23); */
/* DEBUG */
/* printf("%8.4lf: %08lX %d\n", d, mantissa, exp); */
mantissa >>= (32-23); /* get into low 23 bits */
/* now turn into IBM 1130 standard precision */
exp += 128;
if (neg)
mantissa = (unsigned long) (- (long) mantissa); /* two's complement */
wd[0] = (unsigned short) ((neg ? 0x8000 : 0) | ((mantissa >> (23-15)) & 0x7FFF));
wd[1] = (unsigned short) ((mantissa & 0x00FF) << 8) | (exp & 0xFF);
/* DEBUG */
/* printf(" D %04x%04X\n", wd[0], wd[1]); */
}
/********************************************************************************************
* convert_double_to_fixed -
********************************************************************************************/
void convert_double_to_fixed (double d, unsigned short *wd, int bexp)
{
int neg, exp, rshift;
unsigned long mantissa;
unsigned char *byte = (unsigned char *) &d;
if (d == 0.) {
wd[0] = wd[1] = 0;
return;
}
/* note: we assume that this computer uses ANSI floating point */
/* 7 6 5 4 0 */
/* d = ansi real*8 SXXX XXXX XXXX MMMM MMMM MMMM MMMM MMMM ... MMMM MMMM */
neg = byte[7] & 0x80;
exp = ((byte[7] & 0x7F) << 4) | ((byte[6] & 0xF0) >> 4); /* extract exponent */
exp -= 1023; /* remove bias */
exp++; /* shift to account for implied 1 we added */
/* get 32 bits worth of mantissa. add the implied point */
mantissa = 0x80000000L | ((byte[6] & 0x0F) << 27) | (byte[5] << 19) | (byte[4] << 11) | (byte[3] << 3) | ((byte[2] & 0xE0) >> 5);
mantissa >>= 1; /* shift it out of the sign bit */
/* DEBUG */
/* printf("%8.4lf: %08lX %d\n", d, mantissa, exp); */
rshift = bexp - exp;
if (rshift > 0) {
mantissa >>= rshift;
}
else if (rshift < 0) {
mantissa >>= (-rshift);
asm_warning("Fixed point overflow");
}
if (neg)
mantissa = (unsigned long) (- (long) mantissa); /* two's complement */
/* DEBUG */
/* printf(" B %08lX\n", mantissa); */
wd[0] = (unsigned short) ((mantissa >> 16) & 0xFFFF); /* return all of the bits; no exponent here */
wd[1] = (unsigned short) (mantissa & 0xFFFF);
}
/********************************************************************************************
* getDconstant -
********************************************************************************************/
void getDconstant (char *tok, unsigned short *wd)
{
unsigned long l;
char *b, *fmt;
double d;
int bexp;
BOOL fixed = FALSE;
wd[0] = 0;
wd[1] = 0;
tok = skipbl(tok);
if (! *tok)
return; /* no argument is the same as 0 */
if (strchr(tok, '.') == NULL && strchr(tok, 'B') == NULL && strchr(tok, 'E') == NULL) {
fmt = "%ld"; /* not a floating point number -- parse as a 32-bit integer */
if (*tok == '/') { /* I don't see that this is legal but can't hurt to allow it */
fmt = "%lx";
tok++;
}
if (sscanf(tok, fmt, &l) != 1)
asm_error("Syntax error in constant");
else {
wd[0] = (unsigned short) ((l >> 16) & 0xFFFF); /* high word */
wd[1] = (unsigned short) (l & 0xFFFF); /* low word */
}
}
else {
if ((b = strchr(tok, 'B')) != NULL) {
fixed = TRUE;
bexp = atoi(b+1); /* get binary exponent, after the B */
*b = '\0'; /* truncate at the B */
}
if (sscanf(tok, "%lg", &d) != 1)
asm_error("Syntax error in constant");
else if (fixed)
convert_double_to_fixed(d, wd, bexp);
else
convert_double_to_standard(d, wd);
}
}
/********************************************************************************************
* x_dec - If the input value is an integer with no decimal point and no B or E,
* DEC generates a double INTEGER value.
* IBM documentation ranges from ambiguous to wrong on this point, but
* examination of the DMS microfiche supports this.
********************************************************************************************/
void x_dec (struct tag_op *op, char *label, char *mods, char *arg)
{
unsigned short wd[2];
org_advanced = 2; /* assume * means address after this location, since it's +1 for dc? */
org_even(); /* even address is implied */
setw(0, org+listoffset, FALSE); /* display the origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
getDconstant(arg, wd);
writew(wd[0], FALSE); /* write hiword, then loword */
writew(wd[1], FALSE);
}
/********************************************************************************************
* DECS directive. Writes just the high word of a DEC value
********************************************************************************************/
void x_decs (struct tag_op *op, char *label, char *mods, char *arg)
{
unsigned short wd[2];
org_advanced = 1; /* assume * means address after this location */
setw(0, org+listoffset, FALSE); /* display the origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
getDconstant(arg, wd);
writew(wd[0], FALSE); /* write hiword ONLY */
}
/********************************************************************************************
* x_xflc - extended precision constant. (Note: if there is no argument, we must write a zero value)
********************************************************************************************/
void x_xflc (struct tag_op *op, char *label, char *mods, char *arg)
{
char *tok, *b;
double d;
int bexp, fixed;
unsigned short wd[3];
org_advanced = 2; /* who knows? */
setw(0, org+listoffset, FALSE); /* display the origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if ((tok = strtok(arg, ",")) == NULL) { /* pick up value */
tok = "0"; /* if there is no argument at all, spit out a zero */
/* fprintf(stderr, ">>> ENCOUNTERED XFLC WITH NO ARGUMENT IN %s -- THIS WAS BUGGY BEFORE\n", curfn); */
}
bexp = 0; /* parse the value */
if ((b = strchr(tok, 'B')) != NULL) {
bexp = atoi(b+1);
fixed = TRUE;
*b = '\0'; /* truncate at the b */
asm_warning("Fixed point extended floating constant?");
}
if (sscanf(tok, "%lg", &d) != 1) {
asm_error("Syntax error in constant");
d = 0.;
}
convert_double_to_extended(d, wd);
writew(wd[0], ABSOLUTE);
writew(wd[1], ABSOLUTE);
writew(wd[2], ABSOLUTE);
}
/********************************************************************************************
* x_equ - EQU directive
********************************************************************************************/
void x_equ (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
org_advanced = 0; /* * means this address, not incremented */
getexpr(arg, FALSE, &expr);
setw(0, expr.value, expr.relative); /* show this as address */
if (*label) /* EQU is all about defining labels, better have one */
set_symbol(label, expr.value, TRUE, expr.relative);
/* else // IBM assembler doesn't complain about this */
/* asm_error("EQU without label?"); */
}
/********************************************************************************************
* x_lorg - LORG directive -- output queued literal values
********************************************************************************************/
void x_lorg (struct tag_op *op, char *label, char *mods, char *arg)
{
org_advanced = 0; /* * means this address (not used, though) */
output_literals(FALSE); /* generate .DC's for queued literal values */
}
/********************************************************************************************
* x_abs - ABS directive
********************************************************************************************/
void x_abs (struct tag_op *op, char *label, char *mods, char *arg)
{
if (assembled)
asm_error("ABS must be first statement");
relocate = ABSOLUTE;
switch (progtype) {
case PROGTYPE_ABSOLUTE:
case PROGTYPE_RELOCATABLE:
progtype = PROGTYPE_ABSOLUTE; /* change program type, still assumed to be mainline */
break;
case PROGTYPE_LIBF:
case PROGTYPE_CALL:
case PROGTYPE_ISSLIBF:
case PROGTYPE_ISSCALL:
case PROGTYPE_ILS:
asm_error("ABS not allowed with LIBF, ENT, ILS or ISS");
break;
default:
bail("in x_libr, can't happen");
}
}
/********************************************************************************************
* x_call - ORG pseudo-op
********************************************************************************************/
void x_call (struct tag_op *op, char *label, char *mods, char *arg)
{
unsigned short words[2];
static struct tag_op *bsi = NULL;
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (! *arg) {
asm_error("CALL missing argument");
return;
}
if (pass == 1) { /* it will take two words in any case */
org += 2;
return;
}
setw(0, org+listoffset, FALSE); /* display origin */
if (lookup_symbol(arg, FALSE) != NULL) {/* it's a defined symbol? */
if (bsi == NULL)
if ((bsi = lookup_op("BSI")) == NULL)
bail("Can't find BSI op");
(bsi->handler)(bsi, "", "L", arg);
}
else if (outmode == OUTMODE_BINARY) {
namecode(words, arg); /* emit namecode for loader */
writetwo();
writew(words[0], CALL);
writew(words[1], ABSOLUTE);
}
else {
writew(0x3000, 0); /* write two WAIT commands */
writew(0x3000, 0);
asm_warning("CALL <libroutine> is not valid for simulator load output format, emitting WAIT");
}
}
/********************************************************************************************
* x_org - ORG directive
********************************************************************************************/
void x_org (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
org_advanced = 0; /* * means this address */
if (*label) /* label is defined BEFORE the new origin is set!!! */
set_symbol(label, org, TRUE, relocate);
if (getexpr(arg, FALSE, &expr) != S_DEFINED)
return;
setorg(expr.value); /* set origin to this value */
}
/********************************************************************************************
* x_end - END directive
********************************************************************************************/
void x_end (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
org_advanced = 0; /* * means this address */
if (*arg) { /* they're specifing the program start address */
if (getexpr(arg, FALSE, &expr) == S_DEFINED)
pta = expr.value;
}
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
setw(0, org+listoffset, FALSE); /* display origin */
ended = TRUE; /* assembly is done, stop reading file */
}
/********************************************************************************************
* x_ent - ENT op
********************************************************************************************/
void x_ent (struct tag_op *op, char *label, char *mods, char *arg)
{
PSYMBOL s;
org_advanced = 0; /* * means this address */
if (pass < 2)
return;
/* if (*label) // define label */
/* set_symbol(label, org, TRUE, relocate); */
/* */
/* setw(0, org+listoffset, FALSE); // display origin */
if (! *arg)
asm_error("No entry label specified");
else if ((s = lookup_symbol(arg, FALSE)) == NULL)
asm_error("Entry symbol %s not defined", arg);
else if (nentries >= MAXENTRIES)
asm_error("Too many entries, limit is %d", MAXENTRIES);
else
entry[nentries++] = s; /* save symbol pointer */
switch (progtype) {
case PROGTYPE_ABSOLUTE:
asm_error("ENT not allowed with ABS");
break;
case PROGTYPE_RELOCATABLE:
progtype = PROGTYPE_CALL;
break;
case PROGTYPE_LIBF:
case PROGTYPE_CALL:
case PROGTYPE_ISSLIBF:
case PROGTYPE_ISSCALL:
break;
case PROGTYPE_ILS:
asm_error("Can't mix ENT and ILS, can you?");
break;
default:
bail("in x_libr, can't happen");
}
}
/********************************************************************************************
* declare a libf-type subprogram
********************************************************************************************/
void x_libr (struct tag_op *op, char *label, char *mods, char *arg)
{
switch (progtype) {
case PROGTYPE_ABSOLUTE:
asm_error("LIBR not allowed with ABS");
break;
case PROGTYPE_RELOCATABLE:
case PROGTYPE_LIBF:
case PROGTYPE_CALL:
progtype = PROGTYPE_LIBF;
break;
case PROGTYPE_ISSLIBF:
case PROGTYPE_ISSCALL:
progtype = PROGTYPE_ISSLIBF;
break;
case PROGTYPE_ILS:
asm_error("Can't use LIBR in an ILS");
break;
default:
bail("in x_libr, can't happen");
}
}
/********************************************************************************************
* x_ils - ILS directive
********************************************************************************************/
void x_ils (struct tag_op *op, char *label, char *mods, char *arg)
{
switch (progtype) {
case PROGTYPE_ABSOLUTE:
asm_error("ILS not allowed with ABS");
break;
case PROGTYPE_RELOCATABLE:
case PROGTYPE_ILS:
progtype = PROGTYPE_ILS;
break;
case PROGTYPE_LIBF:
case PROGTYPE_CALL:
asm_error("Invalid placement of ILS");
break;
case PROGTYPE_ISSLIBF:
case PROGTYPE_ISSCALL:
break;
default:
bail("in x_libr, can't happen");
}
intlevel_primary = atoi(mods);
}
/********************************************************************************************
* x_iss - ISS directive
********************************************************************************************/
void x_iss (struct tag_op *op, char *label, char *mods, char *arg)
{
char *tok;
switch (progtype) {
case PROGTYPE_ABSOLUTE:
asm_error("ISS not allowed with ABS");
break;
case PROGTYPE_RELOCATABLE:
case PROGTYPE_CALL:
case PROGTYPE_ISSCALL:
progtype = PROGTYPE_ISSCALL;
break;
case PROGTYPE_LIBF:
case PROGTYPE_ISSLIBF:
progtype = PROGTYPE_ISSLIBF;
break;
case PROGTYPE_ILS:
asm_error("Can't mix ISS and ILS");
default:
bail("in x_libr, can't happen");
}
iss_number = atoi(mods); /* get ISS number */
opfield[16] = '\0'; /* be sure not to look too far into this */
nintlevels = 0; /* # of interrupt levels for ISS */
intlevel_primary = 0; /* primary level for ISS and level for ILS */
intlevel_secondary = 0; /* secondary level for ISS */
if ((tok = strtok(opfield, " ")) == NULL)
asm_error("ISS missing entry label");
else
x_ent(NULL, label, "", arg); /* process as an ENT */
if ((tok = strtok(NULL, " ")) != NULL) { /* get associated levels */
nintlevels++;
intlevel_primary = atoi(tok);
}
if ((tok = strtok(NULL, " ")) != NULL) {
nintlevels++;
intlevel_secondary = atoi(tok);
}
}
/********************************************************************************************
* x_spr -
********************************************************************************************/
void x_spr (struct tag_op *op, char *label, char *mods, char *arg)
{
realmode = REALMODE_STANDARD;
}
/********************************************************************************************
* x_epr -
********************************************************************************************/
void x_epr (struct tag_op *op, char *label, char *mods, char *arg)
{
realmode = REALMODE_EXTENDED;
}
/********************************************************************************************
* x_dsa -
********************************************************************************************/
void x_dsa (struct tag_op *op, char *label, char *mods, char *arg)
{
unsigned short words[2];
setw(0, org+listoffset, FALSE); /* display origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (! *arg) {
asm_error("DSA missing filename");
}
else if (outmode == OUTMODE_BINARY) {
namecode(words, arg);
writetwo();
writew(words[0], CALL); /* special relocation bits here 3 and 1 */
writew(words[1], RELATIVE);
}
else {
writew(0, 0); /* write two zeroes */
writew(0, 0);
asm_warning("DSA is not valid for simulator load output format, emitting 0's");
}
}
/********************************************************************************************
* x_link -
********************************************************************************************/
void x_link (struct tag_op *op, char *label, char *mods, char *arg)
{
unsigned short words[2];
char nline[128];
setw(0, org+listoffset, FALSE); /* display origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (! *arg) {
asm_error("LINK missing program name");
}
else {
format_line(nline, label, "CALL", "", "$LINK", "");
parse_line(nline);
if (outmode == OUTMODE_BINARY) {
namecode(words, arg);
writew(words[0], ABSOLUTE); /* special relocation bits here 3 and 1 */
writew(words[1], ABSOLUTE);
}
else {
writew(0x3000, 0); /* write two WAIT commands */
writew(0x3000, 0);
asm_warning("LINK is not valid for simulator load output format, emitting WAIT");
}
}
}
/********************************************************************************************
* x_libf -
********************************************************************************************/
void x_libf (struct tag_op *op, char *label, char *mods, char *arg)
{
unsigned short words[2];
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (! *arg) {
asm_error("LIBF missing argument");
return;
}
if (pass == 1) { /* it will take one word in any case */
org++;
return;
}
setw(0, org+listoffset, FALSE); /* display origin */
if (outmode == OUTMODE_BINARY) {
namecode(words, arg); /* emit namecode for loader */
writetwo();
writew(words[0], LIBF); /* this one does NOT advance org! */
writew(words[1], ABSOLUTE);
}
else {
writew(0x3000, 0); /* write a WAIT command */
asm_warning("LIBF is not valid for simulator load output format, emitting WAIT");
}
}
/********************************************************************************************
* x_file -
********************************************************************************************/
void x_file (struct tag_op *op, char *label, char *mods, char *arg)
{
int i, n, r;
EXPR vals[5];
char *tok;
for (i = 0; i < 5; i++) {
if ((tok = strtok(arg, ",")) == NULL) {
asm_error("FILE has insufficient arguments");
return;
}
arg = NULL; /* for next strtok call */
if (i == 3) {
if (strcmpi(tok, "U") != 0)
asm_error("Argument 4 must be the letter U");
}
else if (getexpr(tok, FALSE, &vals[i]) == S_DEFINED) {
if (i <= 3 && vals[i].relative)
asm_error("Argument %d must be absolute", i+1);
else if (pass == 2 && vals[i].value == 0)
asm_error("Argument %d must be nonzero", i+1);
}
}
writew(vals[0].value, ABSOLUTE);
writew(vals[1].value, ABSOLUTE);
writew(vals[2].value, ABSOLUTE);
writew(vals[4].value, vals[i].relative);
writew(0, ABSOLUTE);
n = MAX(1, vals[2].value);
r = 320/n;
writew(r, ABSOLUTE);
r = MAX(1, r);
writew((16*vals[1].value)/r, ABSOLUTE);
if (pass == 2)
ndefined_files++;
}
/********************************************************************************************
* x_trap - place to set a breakpoint
********************************************************************************************/
void x_trap (struct tag_op *op, char *label, char *mods, char *arg)
{
/* debugging breakpoint */
}
/********************************************************************************************
* x_ces - .CES directive (nonstandard). Specify a value for the console entry
* switches. When this program is loaded into the simulator, the switches will
* be set accordingly. Handy for bootstraps and other programs that read
* the switches.
********************************************************************************************/
void x_ces (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
if (outmode != OUTMODE_LOAD) /* this works only in our loader format */
return;
if (getexpr(arg, FALSE, &expr) != S_DEFINED)
return;
if (pass == 2)
fprintf(fout, "S%04X" ENDLINE, expr.value & 0xFFFF);
}
/********************************************************************************************
* x_bss - BSS directive - reserve space in core
********************************************************************************************/
void x_bss (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
org_advanced = 0; /* * means this address */
if (! *arg) {
expr.value = 0;
expr.relative = ABSOLUTE;
}
else if (getexpr(arg, FALSE, &expr) != S_DEFINED)
return;
if (strchr(mods, 'E') != NULL) /* force even address */
org_even();
if (expr.relative)
asm_error("BSS size must be an absolute value");
setw(0, org+listoffset, FALSE); /* display origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
expr.value &= 0xFFFF; /* truncate to 16 bits */
if (expr.value & 0x8000) {
asm_warning("Negative BSS size (%ld, /%04X)", (long)(short)expr.value, expr.value);
}
else if (expr.value > 0) {
if (outmode == OUTMODE_LOAD) {
org += expr.value; /* advance the origin by appropriate number of words */
if (pass == 2) /* emit new load address in output file */
fprintf(fout, "@%04X%s" ENDLINE, org & 0xFFFF, relocate ? "R" : "");
}
else {
org += expr.value; /* advance the origin by appropriate number of words */
if (pass == 2)
bincard_setorg(org);
}
}
}
/********************************************************************************************
* x_bes - Block Ended by Symbol directive. Like BSS but label gets address AFTER the space, instead of first address
********************************************************************************************/
void x_bes (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
org_advanced = 0; /* * means this address */
if (! *arg) { /* arg field = space */
expr.value = 0;
expr.relative = ABSOLUTE;
}
else if (getexpr(arg, FALSE, &expr) != S_DEFINED)
return;
if (strchr(mods, 'E') != NULL && (org & 1) != 0)
org_even(); /* force even address */
if (expr.relative)
asm_error("BES size must be an absolute value");
if (expr.value < 0)
asm_warning("Negative BES size");
else if (expr.value > 0) {
setw(0, org+expr.value+listoffset, FALSE); /* display NEW origin */
if (outmode == OUTMODE_LOAD) {
org += expr.value; /* advance the origin */
if (pass == 2) /* emit new load address in output file */
fprintf(fout, "@%04X%s" ENDLINE, org & 0xFFFF, relocate ? "R" : "");
}
else {
org += expr.value; /* advance the origin */
bincard_setorg(org);
}
}
if (*label) /* NOW define the label */
set_symbol(label, org, TRUE, relocate);
}
/********************************************************************************************
* x_dmes - DMES define message directive. Various encodings, none pretty.
********************************************************************************************/
int dmes_wd;
int dmes_nc;
enum {CODESET_CONSOLE, CODESET_1403, CODESET_1132, CODESET_EBCDIC} dmes_cs;
void stuff_dmes (int ch, int rpt);
void x_dmes (struct tag_op *op, char *label, char *mods, char *arg)
{
int rpt;
char *c = opfield;
BOOL cont = FALSE;
if (dmes_saved) { /* previous DMES had an odd character saved */
dmes_wd = dmes_savew;
dmes_nc = 1; /* stick it into the outbut buffer */
}
else
dmes_nc = dmes_wd = 0; /* clear output buffer */
trim(opfield); /* remove trailing blanks from rest of input line (use whole thing) */
setw(0, org+listoffset, FALSE); /* display origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (strchr(mods, '1') != NULL) /* determine the encoding scheme */
dmes_cs = CODESET_1403;
else if (strchr(mods, '2') != NULL)
dmes_cs = CODESET_1132;
else if (strchr(mods, '0') != NULL || ! *mods)
dmes_cs = CODESET_CONSOLE;
else {
asm_error("Invalid printer code in tag field");
dmes_cs = CODESET_EBCDIC;
}
while (*c) { /* pick up characters */
if (*c == '\'') { /* quote (') is the escape character */
c++;
rpt = 0; /* get repeat count */
while (BETWEEN(*c, '0', '9')) {
rpt = rpt*10 + *c++ - '0';
}
if (rpt <= 0) /* no count = insert one copy */
rpt = 1;
switch (*c) { /* handle escape codes */
case '\'':
stuff_dmes(*c, 1);
break;
case 'E':
*c = '\0'; /* end */
break;
case 'X':
case 'S':
stuff_dmes(' ', rpt);
break;
case 'F':
stuff_dmes(*++c, rpt); /* repeat character */
break;
case ' ':
case '\0':
cont = TRUE;
*c = '\0'; /* end */
break;
case 'T':
if (dmes_cs != CODESET_CONSOLE) {
badcode: asm_error("Invalid ' escape for selected printer");
break;
}
stuff_dmes(0x41, -rpt); /* tab */
break;
case 'D':
if (dmes_cs != CODESET_CONSOLE) goto badcode;
stuff_dmes(0x11, -rpt); /* backspace */
break;
case 'B':
if (dmes_cs != CODESET_CONSOLE) goto badcode;
stuff_dmes(0x05, -rpt); /* black */
break;
case 'A':
if (dmes_cs != CODESET_CONSOLE) goto badcode;
stuff_dmes(0x09, -rpt); /* red */
break;
case 'R':
if (dmes_cs != CODESET_CONSOLE) goto badcode;
stuff_dmes(0x81, -rpt); /* return */
break;
case 'L':
if (dmes_cs != CODESET_CONSOLE) goto badcode;
stuff_dmes(0x03, -rpt); /* line feed */
break;
default:
asm_error("Invalid ' escape in DMES");
*c = '\0';
break;
}
}
else /* just copy literal character */
stuff_dmes(*c, 1);
if (*c)
c++;
}
dmes_saved = FALSE;
if (dmes_nc) { /* odd number of characters */
if (cont) {
dmes_saved = TRUE;
dmes_savew = dmes_wd; /* save for next time */
}
else
stuff_dmes(' ', 1); /* pad with a space to force out even # of characters */
}
}
/********************************************************************************************
* stuff_dmes - insert 'rpt' copies of character 'ch' into output words
********************************************************************************************/
void stuff_dmes (int ch, int rpt)
{
int nch, i; /* nch is translated output value */
if (rpt < 0) { /* negative repeat means no translation needed */
rpt = -rpt;
nch = ch;
}
else {
switch (dmes_cs) {
case CODESET_CONSOLE:
nch = 0x21;
for (i = 0; i < 256; i++) {
if (conout_to_ascii[i] == ch) {
nch = i;
break;
}
}
break;
case CODESET_EBCDIC:
nch = ascii_to_ebcdic_table[ch & 0x7F];
if (nch == 0)
nch = 0x7F;
break;
case CODESET_1403:
nch = ascii_to_1403_table[ch & 0x7F];
if (nch == 0)
nch = 0x7F;
break;
case CODESET_1132:
nch = 0x40;
for (i = 0; i < WHEELCHARS_1132; i++) {
if (codewheel1132[i].ascii == ch) {
nch = codewheel1132[i].ebcdic;
break;
}
}
break;
default:
bail("bad cs in x_dmes, can't happen");
break;
}
}
while (--rpt >= 0) { /* pack them into words, output when we have two */
if (dmes_nc == 0) {
dmes_wd = (nch & 0xFF) << 8;
dmes_nc = 1;
}
else {
dmes_wd |= (nch & 0xFF);
writew(dmes_wd, FALSE);
dmes_nc = 0;
}
}
}
/********************************************************************************************
* x_ebc - handle EBCDIC string definition (delimited with periods)
********************************************************************************************/
void x_ebc (struct tag_op *op, char *label, char *mods, char *arg)
{
char *p;
/* setw(0, org+listoffset, FALSE); */
if (*label)
set_symbol(label, org, TRUE, relocate);
p = trim(opfield); /* remove trailing blanks from rest of input line (use whole thing) */
if (*p != '.') {
asm_error("EBC data must start with .");
return;
}
p++; /* skip leading period */
dmes_nc = dmes_wd = 0; /* clear output buffer (we're borrowing the DMES packer) */
dmes_cs = CODESET_EBCDIC;
while (*p && *p != '.') /* store packed ebcdic */
stuff_dmes(*p++, 1);
if (dmes_nc) /* odd number of characters */
stuff_dmes(' ', 1); /* pad with a space to force out even # of characters */
if (*p != '.')
asm_error("EBC missing closing .");
}
/********************************************************************************************
* x_dn - define name DN directive. Pack 5 characters into two words. This by the
* way is the reason the language Forth is not Fourth.
********************************************************************************************/
void x_dn (struct tag_op *op, char *label, char *mods, char *arg)
{
unsigned short words[2];
setw(0, org+listoffset, FALSE); /* display origin */
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
namecode(words, arg);
writew(words[0], ABSOLUTE);
writew(words[1], ABSOLUTE);
}
/********************************************************************************************
* x_dump - DUMP directive - pretend we saw "call $dump, call $exit"
********************************************************************************************/
void x_dump (struct tag_op *op, char *label, char *mods, char *arg)
{
x_pdmp(op, label, mods, arg);
x_exit(NULL, "", "", ""); /* compile "call $exit" */
}
/********************************************************************************************
* x_pdmp - PDMP directive - like DUMP but without the call $exit
********************************************************************************************/
void x_pdmp (struct tag_op *op, char *label, char *mods, char *arg)
{
char nline[200], *tok;
EXPR addr[3];
int i;
for (i = 0, tok = strtok(arg, ","); i < 3 && tok != NULL; i++, tok = strtok(NULL, ",")) {
if (getexpr(tok, FALSE, addr+i) != S_DEFINED) {
addr[i].value = (i == 1) ? 0x3FFF : 0;
addr[i].relative = ABSOLUTE;
}
}
org_advanced = 0; /* * means this address+1 */
format_line(nline, label, "BSI", "L", DOLLARDUMP, "");
parse_line(nline); /* compile "call $dump" */
writew(addr[2].value, ABSOLUTE); /* append arguments (0, start, end address) */
writew(addr[0].value, addr[0].relative);
writew(addr[1].value, addr[1].relative);
}
/********************************************************************************************
* x_hdng - HDNG directive
********************************************************************************************/
void x_hdng (struct tag_op *op, char *label, char *mods, char *arg)
{
char *c;
/* label is not entered into the symbol table */
if (flist == NULL || ! list_on) {
line_error = TRUE; /* inhibit listing: don't print the HDNG statement */
return;
}
line_error = TRUE; /* don't print the statement */
c = skipbl(opfield);
trim(c);
fprintf(flist, "\f%s\n\n", c); /* print page header */
}
/********************************************************************************************
* x_list - LIST directive. enable or disable listing
********************************************************************************************/
void x_list (struct tag_op *op, char *label, char *mods, char *arg)
{
BOOL on;
/* label is not entered into the symbol table */
line_error = TRUE; /* don't print the LIST statement */
if (flist == NULL || ! list_on) {
return;
}
if (strcmpi(arg, "ON") == 0)
on = TRUE;
else if (strcmpi(arg, "OFF") == 0)
on = FALSE;
else
on = do_list;
list_on = on;
}
/********************************************************************************************
* x_spac - SPAC directive. Put blank lines in listing
********************************************************************************************/
void x_spac (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
/* label is not entered into the symbol table */
if (flist == NULL || ! list_on) {
line_error = TRUE; /* don't print the SPAC statement */
return;
}
if (getexpr(arg, FALSE, &expr) != S_DEFINED)
return;
line_error = TRUE; /* don't print the statement */
while (--expr.value >= 0)
putc('\n', flist);
}
/********************************************************************************************
* x_ejct - EJCT directive - put formfeed in listing
********************************************************************************************/
void x_ejct (struct tag_op *op, char *label, char *mods, char *arg)
{
/* label is not entered into the symbol table */
if (flist == NULL || ! list_on) {
line_error = TRUE; /* don't print the EJCT statement */
return;
}
line_error = TRUE; /* don't print the statement */
putc('\f', flist);
}
/********************************************************************************************
* basic_opcode - construct a standard opcode value from op table entry and modifier chars
********************************************************************************************/
int basic_opcode (struct tag_op *op, char *mods)
{
int opcode = op->opcode; /* basic code value */
if (strchr(mods, '1') != 0) /* indexing */
opcode |= 0x0100;
else if (strchr(mods, '2') != 0)
opcode |= 0x0200;
else if (strchr(mods, '3') != 0)
opcode |= 0x0300;
if (strchr(mods, 'L')) { /* two-word format */
opcode |= OP_LONG;
if (strchr(mods, 'I') != 0) /* and indirect to boot */
opcode |= OP_INDIRECT;
}
return opcode;
}
/********************************************************************************************
* std_op - assemble a vanilla opcode
********************************************************************************************/
void std_op (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
int opcode = basic_opcode(op, mods);
BOOL val_ok = FALSE;
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (*arg && ! (op->flags & NO_ARGS)) { /* get value argument */
if (getexpr(arg, FALSE, &expr) == S_DEFINED)
val_ok = TRUE;
}
else {
expr.value = 0;
expr.relative = FALSE;
}
if (opcode & OP_LONG) { /* two-word format, just write code and value */
writew(opcode, FALSE);
writew(expr.value, expr.relative);
}
else { /* one-word format */
if (strchr(mods, 'I') != 0)
asm_error("Indirect mode not permitted on one-word instructions");
if (val_ok && ! (strchr(mods, 'X') || (op->flags & IS_ABS) || ((opcode & OP_INDEXED) && ! (op->flags & NO_IDX))))
expr.value -= (org+1); /* compute displacement */
if (expr.value < -128 || expr.value > 127) {/* check range */
asm_error("Offset of %d is too large", expr.value);
expr.value = 0;
}
writew(opcode | (expr.value & 0x00FF), FALSE);/* that's the code */
}
}
/********************************************************************************************
* mdx_op - assemble a MDX family instruction
********************************************************************************************/
void mdx_op (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR dest, incr = {0, FALSE};
int opcode = basic_opcode(op, mods);
char *tok;
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if ((tok = strtok(arg, ",")) == NULL) { /* argument format is dest[,increment] */
/* asm_error("Destination not specified"); // seems not to be an error, IBM omits it sometimes */
dest.value = 0;
dest.relative = ABSOLUTE;
}
else
getexpr(tok, FALSE, &dest); /* parse the address */
tok = strtok(NULL, ","); /* look for second argument */
if (opcode & OP_LONG) { /* two word format */
if (opcode & OP_INDEXED) { /* format: MDX 2 dest */
if (tok != NULL)
asm_error("This format takes only one argument");
}
else { /* format: MDX dest,increment */
if (opcode & OP_INDIRECT)
asm_error("Indirect can't be used without indexing");
if (tok == NULL) {
/* asm_error("This format takes two arguments"); */
incr.value = 0;
incr.relative = ABSOLUTE;
}
else
getexpr(tok, FALSE, &incr);
if (incr.value < -128 || incr.value > 127) /* displacement style (fixed in ver 1.08) */
asm_error("Invalid increment value (8 bits signed)");
opcode |= (incr.value & 0xFF);
}
writew(opcode, ABSOLUTE);
writew(dest.value, dest.relative);
}
else { /* one word format MDX val */
if (tok != NULL)
asm_error("This format takes only one argument");
if (! (strchr(mods, 'X') || (opcode & OP_INDEXED)))
dest.value -= (org+1); /* compute displacement */
if (dest.value < -128 || dest.value > 127)
asm_error("Offset/Increment of %d is too large", dest.value);
writew(opcode | (dest.value & 0xFF), FALSE);
}
}
/********************************************************************************************
* bsi_op - BSI long instruction is like a BSC L, short is standard
********************************************************************************************/
void bsi_op (struct tag_op *op, char *label, char *mods, char *arg)
{
if (strchr(mods, 'L') || strchr(mods, 'I'))
bsc_op(op, label, mods, arg);
else
std_op(op, label, mods, arg);
}
/********************************************************************************************
* b_op - branch; use short or long version
********************************************************************************************/
void b_op (struct tag_op *op, char *label, char *mods, char *arg)
{
static struct tag_op *mdx = NULL;
if (strchr(mods, 'L') || strchr(mods, 'I')) {
bsi_op(op, label, mods, arg);
return;
}
if (mdx == NULL)
if ((mdx = lookup_op("MDX")) == NULL)
bail("Can't find MDX op");
(mdx->handler)(mdx, label, mods, arg);
}
/********************************************************************************************
* bsc_op - compute a BSC family instruction
********************************************************************************************/
void bsc_op (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR dest;
int opcode = basic_opcode(op, mods);
char *tok, *tests;
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (opcode & OP_LONG) { /* two word format */
if ((tok = strtok(arg, ",")) == NULL) { /* format is BSC dest[,tests] */
asm_error("Destination not specified");
dest.value = 0;
dest.relative = ABSOLUTE;
}
else
getexpr(tok, FALSE, &dest);
tests = strtok(NULL, ","); /* get test characters */
}
else
tests = arg; /* short format is BSC tests */
if (tests != NULL) { /* stick in the testing bits */
for (; *tests; tests++) {
switch (*tests) {
/* bit 0x40 is the BOSC bit */
case 'Z': opcode |= 0x20; break;
case '-': opcode |= 0x10; break;
case '+':
case '&': opcode |= 0x08; break;
case 'E': opcode |= 0x04; break;
case 'C': opcode |= 0x02; break;
case 'O': opcode |= 0x01; break;
default:
asm_error("Invalid test flag: '%c'", *tests);
}
}
}
writew(opcode, ABSOLUTE); /* emit code */
if (opcode & OP_LONG)
writew(dest.value, dest.relative);
}
/********************************************************************************************
* shf_op - assemble a shift instruction
********************************************************************************************/
void shf_op (struct tag_op *op, char *label, char *mods, char *arg)
{
EXPR expr;
int opcode = basic_opcode(op, mods);
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
if (opcode & OP_INDEXED) { /* shift value comes from index register */
expr.value = 0;
expr.relative = ABSOLUTE;
}
else
getexpr(arg, FALSE, &expr);
if (expr.relative) {
asm_error("Shift value is a relative address");
expr.relative = ABSOLUTE;
}
if (expr.value < 0 || expr.value > 32) { /* check range */
asm_error("Shift count of %d is invalid", expr.value);
expr.value = 0;
}
writew(opcode | (expr.value & 0x3F), FALSE); /* put shift count into displacement field */
}
/********************************************************************************************
* x_mdm - MDM instruction
********************************************************************************************/
void x_mdm (struct tag_op *op, char *label, char *mods, char *arg)
{
int opcode = basic_opcode(op, mods);
if (*label) /* define label */
set_symbol(label, org, TRUE, relocate);
/* oh dear: bug here */
asm_error("'%s' is not yet supported", op->mnem);
}
/********************************************************************************************
* x_exit - EXIT directive. Assembler manual says it treats like CALL $EXIT, but
* object code reveals the truth: jump to $EXIT, which is a small value, so we can use LDX.
********************************************************************************************/
void x_exit (struct tag_op *op, char *label, char *mods, char *arg)
{
char nline[120];
format_line(nline, label, "LDX", "X", DOLLAREXIT, "");
parse_line(nline);
}
/********************************************************************************************
* x_opt - .OPT directive. Nonstandard. Possible values:
*
* .OPT CEXPR - use C precedence in evaluating expressions rather than strict left-right. The real
* 1130 assembler was left-to-right. Enabling CEXPR enables precedence and makes the
* assembler NON STANDARD.
********************************************************************************************/
void x_opt (struct tag_op *op, char *label, char *mods, char *arg)
{
char *tok;
org_advanced = 0; /* * means this address */
if (*label) {
asm_error("Label not permitted on .OPT statement");
return;
}
/* look for OPT arguments */
for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) {
if (strcmp(tok, "CEXPR") == 0) {
cexpr = TRUE; /* use C expression precedence (untested) */
}
else
asm_error("Unknown .OPT: '%s'", tok);
}
}
/********************************************************************************************
* askip - skip input lines until a line with the target label appears
********************************************************************************************/
void askip (char *target)
{
char nline[200], cur_label[20], *c;
while (get_line(nline, sizeof(nline), TRUE)) { /* read next line (but don't exit a macro) */
listout(FALSE); /* end listing of previous input line */
prep_line(nline); /* preform standard line prep */
strncpy(cur_label, nline, 6); /* get first 5 characters */
cur_label[5] = '\0';
for (c = cur_label; *c > ' '; c++) /* truncate at first whitespace */
;
*c = '\0';
/* stop if there's a match */
if ((target == NULL) ? (cur_label[0] == '\0') : strcmp(target, cur_label) == 0) {
parse_line(nline); /* process this line */
return;
}
}
if (target != NULL)
asm_error("Label %s not found", target);
}
/********************************************************************************************
* x_aif - process conditional assembly jump
********************************************************************************************/
void x_aif (struct tag_op *op, char *label, char *mods, char *arg)
{
char *target, *tok;
EXPR expr1, expr2;
BOOL istrue;
enum {OP_EQ, OP_LT, OP_GT, OP_NE, OP_LE, OP_GE} cmp_op;
/* label is not entered into the symbol table */
arg = skipbl(arg);
if (*arg != '(') {
asm_error("AIF operand must start with (");
return;
}
arg++; /* skip the paren */
/* normally whitespace is never found in the arg string (see tabtok and coltok). */
/* However, spaces inside parens are permitted. */
if ((tok = strtok(arg, whitespace)) == NULL) {
asm_error("AIF missing first expression");
return;
}
getexpr(tok, FALSE, &expr1);
if ((tok = strtok(NULL, whitespace)) == NULL) {
asm_error("AIF missing conditional operator");
return;
}
if (strcmp(tok, "EQ") == 0)
cmp_op = OP_EQ;
else if (strcmp(tok, "LT") == 0)
cmp_op = OP_LT;
else if (strcmp(tok, "GT") == 0)
cmp_op = OP_GT;
else if (strcmp(tok, "NE") == 0)
cmp_op = OP_NE;
else if (strcmp(tok, "LE") == 0)
cmp_op = OP_LE;
else if (strcmp(tok, "GE") == 0)
cmp_op = OP_GE;
else {
asm_error("AIF: %s is not a valid conditional operator", tok);
return;
}
if ((tok = strtok(NULL, ")")) == NULL) {
asm_error("AIF missing second expression");
return;
}
getexpr(tok, FALSE, &expr2);
switch (cmp_op) { /* test the condition */
case OP_EQ: istrue = expr1.value == expr2.value; break;
case OP_LT: istrue = expr1.value < expr2.value; break;
case OP_GT: istrue = expr1.value > expr2.value; break;
case OP_NE: istrue = expr1.value != expr2.value; break;
case OP_LE: istrue = expr1.value <= expr2.value; break;
case OP_GE: istrue = expr1.value >= expr2.value; break;
default: bail("in aif, can't happen");
}
/* After the closing paren coltok and tabtok guarantee we will have no whitespace */
if ((target = strtok(arg, ",")) == NULL) /* get target label */
asm_warning("Missing target label");
if (istrue)
askip(target); /* skip to the target */
}
/********************************************************************************************
* x_aifb - conditional assembly jump back (macro only)
********************************************************************************************/
void x_aifb (struct tag_op *op, char *label, char *mods, char *arg)
{
asm_error("aifb valid in macros only and not implemented in any case");
}
/********************************************************************************************
* x_ago
********************************************************************************************/
void x_ago (struct tag_op *op, char *label, char *mods, char *arg)
{
char *target;
/* label is not entered into the symbol table */
/* handle differently in a macro */
if ((target = strtok(arg, ",")) == NULL) /* get target label */
asm_warning("Missing target label");
askip(target); /* skip to the target */
}
/********************************************************************************************
* x_agob -
********************************************************************************************/
void x_agob (struct tag_op *op, char *label, char *mods, char *arg)
{
asm_error("agob valid in macros only and not implemented in any case");
}
/********************************************************************************************
* x_anop -
********************************************************************************************/
void x_anop (struct tag_op *op, char *label, char *mods, char *arg)
{
/* label is not entered into the symbol table */
/* do nothing else */
}
/********************************************************************************************
* expression parser, borrowed from older code, no comments, sorry
********************************************************************************************/
char *exprptr, *oexprptr;
#define GETNEXT (*exprptr++)
#define UNGET --exprptr
#define LETTER 0 /* character types */
#define DIGIT 1
#define ETC 2
#define ILL 3
#define SPACE 4
#define MULOP 5
#define ADDOP 6
#define EXPOP 7
int getnb (void);
void c_expr (EXPR *ap);
void c_expr_m (EXPR *ap);
void c_expr_e (EXPR *ap);
void c_expr_u (EXPR *ap);
void c_term (EXPR *ap);
int c_number (int c, int r, int nchar);
int digit (int c, int r);
int c_esc (int c);
void exprerr (int n);
void a1130_expr (EXPR *ap);
void a1130_term (EXPR *ap);
char ctype[128] = { /* character types */
/*^0ABCDEFG */ ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL,
/*^HIJKLMNO */ ILL, SPACE, SPACE, ILL, SPACE, SPACE, ILL, ILL,
/*^PQRSTUVW */ ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL,
/*^XYZ */ ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL,
/* !"#$%&' */ SPACE, ETC, ETC, LETTER, LETTER, MULOP, MULOP, LETTER, /* $ # @ and ' are letters here */
/* ()*+,-./ */ ETC, ETC, MULOP, ADDOP, ETC, ADDOP, ETC, MULOP,
/* 01234567 */ DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT,
/* 89:;<=>? */ DIGIT, DIGIT, ETC, ETC, MULOP, ETC, MULOP, ETC,
/* @ABCDEFG */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
/* HIJKLMNO */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
/* PQRSTUVW */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
/* XYZ[\]^_ */ LETTER, LETTER, LETTER, ETC, ETC, ETC, EXPOP, LETTER,
/* `abcdefg */ ETC, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
/* hijklmno */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
/* pqrstuvw */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
/* xyz{|}~ */ LETTER, LETTER, LETTER, ETC, ADDOP, ETC, ETC, ETC
};
char *errstr[] = {
"Missing exponent", /* 0 */
"Undefined symbol", /* 1 */
"Division by zero", /* 2 */
"Illegal operator", /* 3 */
") expected", /* 4 */
"Char expected after '", /* 5 */
"Char expected after .", /* 6 */
"Number expected after =", /* 7 */
"Syntax error", /* 8 */
"Number syntax", /* 9 */
"Char expected after \\", /* 10 */
"Relocation error" /* 11 */
};
int getnb () {
int c;
if (cexpr) { /* in C mode, handle normally */
while (ctype[(c = GETNEXT)] == SPACE)
;
} /* in 1130 mode, a space terminates the expression. Here, eat the rest */
else if ((c = GETNEXT) == ' ') {
while ((c = GETNEXT) != '\0')
;
}
return c;
}
int symbest, exprerrno;
jmp_buf exprjmp;
/********************************************************************************************
* getexpr
********************************************************************************************/
int getexpr (char *pc, BOOL undefined_ok, EXPR *pval)
{
symbest = S_DEFINED; /* assume no questionable symbols */
pval->value = 0;
pval->relative = ABSOLUTE;
if (! *pc) /* blank expression is same as zero, ok? */
return S_DEFINED;
if (setjmp(exprjmp) != 0) { /* encountered a syntax error & bailed */
pval->value = 0;
pval->relative = ABSOLUTE;
return S_UNDEFINED;
}
exprptr = oexprptr = pc; /* make global the buffer pointer */
c_expr(pval);
if (GETNEXT) /* expression should have been entirely eaten */
exprerr(8); /* if characters are left, it's an error */
if (pval->relative < 0 || pval->relative > 1)
exprerr(11); /* has to work out to an absolute or a single relative term */
if (symbest == S_DEFINED) /* tell how it came out */
return S_DEFINED;
pval->value = 0;
pval->relative = ABSOLUTE;
return (pass == 1 && undefined_ok) ? S_PROVISIONAL : S_UNDEFINED;
}
/********************************************************************************************
* output_literals - construct .DC assembler lines to assemble pending literal
* constant values that have accumulated.
********************************************************************************************/
void output_literals (BOOL eof)
{
char line[120], label[12], num[20];
int i;
for (i = 0; i < n_literals; i++) { /* generate DC statements for any pending literal constants */
if (literal[i].even && literal[i].hex) /* create the value string */
sprintf(num, "/%08lX", literal[i].value);
else if (literal[i].even)
sprintf(num, "%ld", literal[i].value);
else if (literal[i].hex)
sprintf(num, "/%04X", literal[i].value & 0xFFFF);
else
sprintf(num, "%d", literal[i].value);
sprintf(label, "_L%03d", literal[i].tagno);
format_line(line, label, literal[i].even ? "DEC" : "DC", "", num, "GENERATED LITERAL CONSTANT");
if (eof) {
eof = FALSE; /* at end of file, for first literal, only prepare blank line */
sprintf(listline, LEFT_MARGIN, org);
}
else
listout(TRUE); /* push out any pending line(s) */
if (flist && list_on) /* this makes stuff appear in the listing */
sprintf(listline, LEFT_MARGIN " %s", detab(line));
nwout = 0;
parse_line(line); /* assemble the constant definition */
}
n_literals = 0; /* clear list */
}
/********************************************************************************************
* a1130_term - extract one term of an expression using 1130 assembler syntax (no precedence!)
********************************************************************************************/
void a1130_term (EXPR *ap)
{
PSYMBOL s;
char token[80], *t;
int c;
if (cexpr) { /* use C syntax and operator precedence */
c_term(ap);
return;
}
c = GETNEXT;
if (ctype[c] == DIGIT) { /* number */
ap->value = signextend(c_number(c,10,-1));
ap->relative = ABSOLUTE;
}
else if (c == '+') { /* unary + */
a1130_term(ap);
}
else if (c == '-') { /* unary - */
a1130_term(ap);
ap->value = - ap->value;
}
else if (c == '/') { /* / starts a hex constant */
ap->value = signextend(c_number(c,16,-1));
ap->relative = ABSOLUTE;
}
else if (c == '*') { /* asterisk alone = org */
ap->value = org + org_advanced; /* here is where that offset matters! */
ap->relative = relocate;
}
else if (c == '.') { /* EBCDIC constant */
c = GETNEXT;
if (c == '\0') {
UNGET;
c = ' ';
}
c = ascii_to_ebcdic_table[c];
ap->value = c; /* VALUE IS IN LOW BYTE!!! */
ap->relative = ABSOLUTE;
}
else if (ctype[c] == LETTER) { /* symbol */
t = token;
do {
*t++ = c;
c = GETNEXT;
} while (ctype[c] == LETTER || ctype[c] == DIGIT);
UNGET;
*t++ = '\0';
s = lookup_symbol(token, TRUE);
add_xref(s, FALSE);
ap->value = s->value;
ap->relative = s->relative;
symbest = MIN(symbest, s->defined); /* this goes to lowest value (undefined < provisional < defined) */
if (pass == 2 && s->defined != S_DEFINED)
exprerr(1);
}
else
exprerr(8);
}
/********************************************************************************************
* signextend - sign-extend a 16-bit constant value to whatever "int" is.
********************************************************************************************/
int signextend (int v)
{
v &= 0xFFFF; /* clip to 16 bits (this may not be necessary, but best to be safe?) */
if (v & 0x8000) /* if sign bit is set */
v |= ~0xFFFF; /* sign extend */
return v;
}
/********************************************************************************************
* c_expr - evalate an expression using C syntax
********************************************************************************************/
void c_expr (EXPR *ap)
{
int c;
EXPR rop;
c_expr_m(ap); /* get combined multiplicative terms */
for (;;) { /* handle +/- precedence operators */
if (ctype[c=getnb()] != ADDOP) {
UNGET;
break;
}
c_expr_m(&rop); /* right hand operand */
switch (c) {
case '+':
ap->value += rop.value;
ap->relative += rop.relative;
break;
case '-':
ap->value -= rop.value;
ap->relative -= rop.relative;
break;
case '|':
if (ap->relative || rop.relative)
exprerr(11);
ap->value = ((long) (ap->value)) | ((long) rop.value);
break;
default:
printf("In expr, can't happen\n");
}
}
}
/********************************************************************************************
* c_expr_m - get multiplicative precedence terms. Again, this is not usually used
********************************************************************************************/
void c_expr_m (EXPR *ap)
{
int c;
EXPR rop;
c_expr_e(ap); /* get exponential precedence term */
for (;;) { /* get operator */
c = getnb();
if ((c=='<') || (c=='>'))
if (c != getnb()) /* << or >> */
exprerr(3);
if (ctype[c] != MULOP) {
UNGET;
break;
}
c_expr_e(&rop); /* right hand operand */
switch(c) {
case '*':
if (ap->relative && rop.relative)
exprerr(11);
ap->value *= rop.value;
ap->relative = (ap->relative || rop.relative) ? RELATIVE : ABSOLUTE;
break;
case '/':
if (rop.value == 0)
exprerr(2);
if (ap->relative || rop.relative)
exprerr(11);
ap->value /= rop.value;
break;
case '%':
if (rop.value == 0)
exprerr(2);
if (ap->relative || rop.relative)
exprerr(11);
ap->value = ((long) (ap->value)) % ((long) rop.value);
break;
case '&':
if (ap->relative || rop.relative)
exprerr(11);
ap->value = ((long) (ap->value)) & ((long) rop.value);
break;
case '>':
if (ap->relative || rop.relative)
exprerr(11);
ap->value = ((long) (ap->value)) >> ((long) rop.value);
break;
case '<':
if (ap->relative || rop.relative)
exprerr(11);
ap->value = ((long) (ap->value)) << ((long) rop.value);
break;
default:
printf("In expr_m, can't happen\n");
}
}
}
/********************************************************************************************
* c_expr_e - get exponential precedence terms. Again, this is not usually used
********************************************************************************************/
void c_expr_e (EXPR *ap)
{
int c, i, v;
EXPR rop;
c_expr_u(ap);
for (;;) {
c = getnb();
if (ctype[c] != EXPOP) {
UNGET;
break;
}
c_expr_u(&rop);
switch(c) {
case '^':
if (ap->relative || rop.relative)
exprerr(11);
v = ap->value;
ap->value = 1;
for (i = 0; i < rop.value; i++)
ap->value *= v;
break;
default:
printf("In expr_e, can't happen\n");
}
}
}
/********************************************************************************************
* c_expr_u - get unary precedence terms. Again, this is not usually used
********************************************************************************************/
void c_expr_u (EXPR *ap)
{
int c;
if ((c = getnb()) == '!') {
a1130_term(ap);
ap->value = ~ ((long)(ap->value));
if (ap->relative)
exprerr(11);
}
else if (c == '-') {
a1130_term(ap);
ap->value = - ap->value;
if (ap->relative)
exprerr(11);
}
else {
UNGET;
a1130_term(ap);
}
}
/********************************************************************************************
* c_term - get basic operand or parenthesized expression. Again, this is not usually used
********************************************************************************************/
void c_term (EXPR *ap)
{
int c, cc;
PSYMBOL s;
char token[80], *t;
ap->relative = ABSOLUTE; /* assume absolute */
if ((c = getnb()) == '(') { /* parenthesized expr */
c_expr(ap); /* start over at the top! */
if ((cc = getnb()) != ')')
exprerr(4);
}
else if (c == '\'') { /* single quote: char */
if ((c = GETNEXT) == '\0')
c = ' ';
ap->value = c_esc(c);
}
else if (ctype[c] == DIGIT) { /* number */
ap->value = signextend(c_number(c,10,-1));
}
else if (c == '0') { /* 0 starts a hex or octal constant */
if ((c = GETNEXT) == 'x') {
c = GETNEXT;
ap->value = signextend(c_number(c,16,-1));
}
else {
ap->value = signextend(c_number(c,8,-1));
}
}
else if (c == '*') { /* asterisk alone = org */
ap->value = org + org_advanced;
ap->relative = relocate;
}
else if (ctype[c] == LETTER) { /* symbol */
t = token;
do {
*t++ = c;
c = GETNEXT;
} while (ctype[c] == LETTER || ctype[c] == DIGIT);
UNGET;
*t++ = '\0';
s = lookup_symbol(token, TRUE);
ap->value = s->value;
ap->relative = s->relative;
add_xref(s, FALSE);
symbest = MIN(symbest, s->defined); /* this goes to lowest value (undefined < provisional < defined) */
if (pass == 2 && s->defined != S_DEFINED)
exprerr(1);
}
else
exprerr(8);
}
/********************************************************************************************
* c_number - get a C format constant value. Again, this is not usually used
********************************************************************************************/
int c_number (int c, int r, int nchar)
{
int v, n;
nchar--;
if (c == '/' && ! cexpr) { /* special radix stuff */
r = 16;
c = GETNEXT;
}
else if (r == 10 && c == '0' && cexpr) { /* accept C style 0x## also */
c = GETNEXT;
if (c == 'x') {
r = 16;
c = GETNEXT;
}
else {
r = 8;
UNGET;
c = '0';
}
}
n = 0; /* decode number */
while ((nchar-- != 0) && (v = digit(c, r)) >= 0) {
if (v >= r) /* out of range! */
exprerr(9);
n = r*n + v;
c = GETNEXT;
if (c == '.') { /* maybe make it decimal? */
c = GETNEXT;
break;
}
}
UNGET;
return (n);
}
/********************************************************************************************
* digit - get digit value of character c in radix r
********************************************************************************************/
int digit (int c, int r)
{
if (r == 16) {
if (c >= 'A' && c <= 'F')
return (c - 'A' + 10);
}
if (c >= '0' && c <= '9')
return (c - '0');
return (-1);
}
/********************************************************************************************
* c_esc - handle C character escape
********************************************************************************************/
int c_esc (int c)
{
if (c != '\\') /* not escaped */
return(c);
if ((c = GETNEXT) == '\0') /* must be followed by something */
exprerr(10);
if ((c >= 'A') && (c <= 'Z')) /* handle upper case */
c += 'a'-'A';
if (ctype[c] == LETTER) /* control character abbrevs */
switch (c) {
case 'b': c = '\b'; break; /* backspace */
case 'e': c = 27 ; break; /* escape */
case 'f': c = '\f'; break; /* formfeed */
case 'n': c = '\n'; break; /* newline */
case 'r': c = '\r'; break; /* return */
case 't': c = '\t'; break; /* horiz. tab */
}
else if (ctype[c] == DIGIT) { /* get character by the numbers */
c = c_number(c,8,3); /* force octal */
}
return c;
}
/********************************************************************************************
* exprerr - note an expression syntax error. Longjumps back to caller with failure code
********************************************************************************************/
void exprerr (int n)
{
char msg[256];
int nex = exprptr-oexprptr;
strncpy(msg, oexprptr, nex); /* show where the problem was */
msg[nex] = '\0';
strcat(msg, " << ");
strcat(msg, errstr[n]);
asm_error(msg);
exprerrno = n;
longjmp(exprjmp, 1);
}
/********************************************************************************************
* upcase - force a string to uppercase (ASCII)
********************************************************************************************/
char *upcase (char *str)
{
char *s;
for (s = str; *s; s++) {
if (*s >= 'a' && *s <= 'z')
*s -= 32;
}
return str;
}
/********************************************************************************************
* hollerith table for IPL card ident field
********************************************************************************************/
typedef struct {
int hollerith;
char ascii;
} CPCODE;
static CPCODE cardcode_029[] =
{
0x0000, ' ',
0x8000, '&', /* + in 026 Fortran */
0x4000, '-',
0x2000, '0',
0x1000, '1',
0x0800, '2',
0x0400, '3',
0x0200, '4',
0x0100, '5',
0x0080, '6',
0x0040, '7',
0x0020, '8',
0x0010, '9',
0x9000, 'A',
0x8800, 'B',
0x8400, 'C',
0x8200, 'D',
0x8100, 'E',
0x8080, 'F',
0x8040, 'G',
0x8020, 'H',
0x8010, 'I',
0x5000, 'J',
0x4800, 'K',
0x4400, 'L',
0x4200, 'M',
0x4100, 'N',
0x4080, 'O',
0x4040, 'P',
0x4020, 'Q',
0x4010, 'R',
0x3000, '/',
0x2800, 'S',
0x2400, 'T',
0x2200, 'U',
0x2100, 'V',
0x2080, 'W',
0x2040, 'X',
0x2020, 'Y',
0x2010, 'Z',
0x0820, ':',
0x0420, '#', /* = in 026 Fortran */
0x0220, '@', /* ' in 026 Fortran */
0x0120, '\'',
0x00A0, '=',
0x0060, '"',
0x8820, 'c', /* cent */
0x8420, '.',
0x8220, '<', /* ) in 026 Fortran */
0x8120, '(',
0x80A0, '+',
0x8060, '|',
0x4820, '!',
0x4420, '$',
0x4220, '*',
0x4120, ')',
0x40A0, ';',
0x4060, 'n', /* not */
0x2820, 'x', /* what? */
0x2420, ',',
0x2220, '%', /* ( in 026 Fortran */
0x2120, '_',
0x20A0, '>',
0x2060, '>',
};
/********************************************************************************************
* ascii_to_hollerith -
********************************************************************************************/
int ascii_to_hollerith (int ch)
{
int i;
for (i = 0; i < sizeof(cardcode_029) / sizeof(CPCODE); i++)
if (cardcode_029[i].ascii == ch)
return cardcode_029[i].hollerith;
return 0;
}
/********************************************************************************************
* detab - replace tabs with spaces for listing files
********************************************************************************************/
char *detab (char *instr)
{
static char outstr[256];
char *out = outstr;
int col = 0;
while (*instr) {
if (*instr == '\t') {
do {
*out++ = ' ';
col++;
}
while (col & 7);
}
else {
*out++ = *instr;
col++;
}
instr++;
}
*out = '\0';
return outstr;
}
#ifndef _WIN32
/********************************************************************************************
* routines provided by Microsoft C but not others
********************************************************************************************/
int strnicmp (char *a, char *b, int n)
{
int ca, cb;
for (;;) {
if (--n < 0) /* still equal after n characters? quit now */
return 0;
if ((ca = *a) == 0) /* get character, stop on null terminator */
return *b ? -1 : 0;
if (ca >= 'a' && ca <= 'z') /* fold lowercase to uppercase */
ca -= 32;
cb = *b;
if (cb >= 'a' && cb <= 'z')
cb -= 32;
if ((ca -= cb) != 0) /* if different, return comparison */
return ca;
a++, b++;
}
}
int strcmpi (char *a, char *b)
{
int ca, cb;
for (;;) {
if ((ca = *a) == 0) /* get character, stop on null terminator */
return *b ? -1 : 0;
if (ca >= 'a' && ca <= 'z') /* fold lowercase to uppercase */
ca -= 32;
cb = *b;
if (cb >= 'a' && cb <= 'z')
cb -= 32;
if ((ca -= cb) != 0) /* if different, return comparison */
return ca;
a++, b++;
}
}
#endif