1. New Features in 2.10-2 The build procedures have changed. There is only one UNIX makefile. To compile without Ethernet support, simply type gmake {target|all} To compile with Ethernet support, type gmake USE_NETWORK=1 {target|all} The Mingw batch files require Mingw release 2 and invoke the Unix makefile. There are still separate batch files for compilation with or without Ethernet support. 1.1 SCP and Libraries - The EVAL command will evaluate a symbolic type-in and display it in numeric form. - The ! command (with no arguments) will launch the host operating system command shell. The ! command (with an argument) executes the argument as a host operating system command. (Code from Mark Pizzolato) - Telnet sessions now recognize BREAK. How a BREAK is transmitted dependent on the particular Telnet client. (Code from Mark Pizzolato) - The sockets library includes code for active connections as well as listening connections. - The RESTORE command will restore saved memory size, if the simulator supports dynamic memory resizing. 1.2 PDP-1 - The PDP-1 supports the Type 24 serial drum (based on recently discovered documents). 1.3 18b PDP's - The PDP-4 supports the Type 24 serial drum (based on recently discovered documents). 1.4 PDP-11 - The PDP-11 implements a stub DEUNA/DELUA (XU). The real XU module will be included in a later release. 1.5 PDP-10 - The PDP-10 implements a stub DEUNA/DELUA (XU). The real XU module will be included in a later release. 1.6 HP 2100 - The IOP microinstruction set is supported for the 21MX as well as the 2100. - The HP2100 supports the Access Interprocessor Link (IPL). 1.7 VAX - If the VAX console is attached to a Telnet session, BREAK is interpreted as console halt. - The SET/SHOW HISTORY commands enable and display a history of the most recently executed instructions. (Code from Mark Pizzolato) 1.8 Terminals Multiplexors - BREAK detection was added to the HP, DEC, and Interdata terminal multiplexors. 1.9 Interdata 16b and 32b - First release. UNIX is not yet working. 1.10 SDS 940 - First release. 2. Bugs Fixed in 2.10-2 - PDP-11 console must default to 7b for early UNIX compatibility. - PDP-11/VAX TMSCP emulator was using the wrong packet length for read/write end packets. - Telnet IAC+IAC processing was fixed, both for input and output (found by Mark Pizzolato). - PDP-11/VAX Ethernet setting flag bits wrong for chained descriptors (found by Mark Pizzolato). 3. New Features in 2.10 vs prior releases 3.1 SCP and Libraries - The VT emulation package has been replaced by the capability to remote the console to a Telnet session. Telnet clients typically have more complete and robust VT100 emulation. - Simulated devices may now have statically allocated buffers, in addition to dynamically allocated buffers or disk-based data stores. - The DO command now takes substitutable arguments (max 9). In command files, %n represents substitutable argument n. - The initial command line is now interpreted as the command name and substitutable arguments for a DO command. This is backward compatible to prior versions. - The initial command line parses switches. -Q is interpreted as quiet mode; informational messages are suppressed. - The HELP command now takes an optional argument. HELP <cmd> types help on the specified command. - Hooks have been added for implementing GUI-based consoles, as well as simulator-specific command extensions. A few internal data structures and definitions have changed. - Two new routines (tmxr_open_master, tmxr_close_master) have been added to sim_tmxr.c. The calling sequence for sim_accept_conn has been changed in sim_sock.c. - The calling sequence for the VM boot routine has been modified to add an additional parameter. - SAVE now saves, and GET now restores, controller and unit flags. - Library sim_ether.c has been added for Ethernet support. 3.2 VAX - Non-volatile RAM (NVR) can behave either like a memory or like a disk-based peripheral. If unattached, it behaves like memory and is saved and restored by SAVE and RESTORE, respectively. If attached, its contents are loaded from disk by ATTACH and written back to disk at DETACH and EXIT. - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape) has been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from vax_stddev.c and now references a common implementation file, dec_pt.h. - Examine and deposit switches now work on all devices, not just the CPU. - Device address conflicts are not detected until simulation starts. 3.3 PDP-11 - SHOW <device> VECTOR displays the device's interrupt vector. Most devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape), RK611/RK06/RK07 (cartridge disk), RX211 (double density floppy), and KW11P programmable clock have been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from pdp11_stddev.c and now references a common implementation file, dec_pt.h. - Device bootstraps now use the actual CSR specified by the SET ADDRESS command, rather than just the default CSR. Note that PDP-11 operating systems may NOT support booting with non-standard addresses. - Specifying more than 256KB of memory, or changing the bus configuration, causes all peripherals that are not compatible with the current bus configuration to be disabled. - Device address conflicts are not detected until simulation starts. 3.4 PDP-10 - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The RX211 (double density floppy) has been added; it is off by default. - The paper tape now references a common implementation file, dec_pt.h. - Device address conflicts are not detected until simulation starts. 3.5 PDP-1 - DECtape (then known as MicroTape) support has been added. - The line printer and DECtape can be disabled and enabled. 3.6 PDP-8 - The RX28 (double density floppy) has been added as an option to the existing RX8E controller. - SHOW <device> DEVNO displays the device's device number. Most devices allow the device number to be changed with SET <device> DEVNO=nnn. - Device number conflicts are not detected until simulation starts. 3.7 IBM 1620 - The IBM 1620 simulator has been released. 3.8 AltairZ80 - A hard drive has been added for increased storage. - Several bugs have been fixed. 3.9 HP 2100 - The 12845A has been added and made the default line printer (LPT). The 12653A has been renamed LPS and is off by default. It also supports the diagnostic functions needed to run the DCPC and DMS diagnostics. - The 12557A/13210A disk defaults to the 13210A (7900/7901). - The 12559A magtape is off by default. - New CPU options (EAU/NOEAU) enable/disable the extended arithmetic instructions for the 2116. These instructions are standard on the 2100 and 21MX. - New CPU options (MPR/NOMPR) enable/disable memory protect for the 2100 and 21MX. - New CPU options (DMS/NODMS) enable/disable the dynamic mapping instructions for the 21MX. - The 12539 timebase generator autocalibrates. 3.10 Simulated Magtapes - Simulated magtapes recognize end of file and the marker 0xFFFFFFFF as end of medium. Only the TMSCP tape simulator can generate an end of medium marker. - The error handling in simulated magtapes was overhauled to be consistent through all simulators. 3.11 Simulated DECtapes - Added support for RT11 image file format (256 x 16b) to DECtapes. 4. Bugs Fixed in 2.10 vs prior releases - TS11/TSV05 was not simulating the XS0_MOT bit, causing failures under VMS. In addition, two of the CTL options were coded interchanged. - IBM 1401 tape was not setting a word mark under group mark for load mode reads. This caused the diagnostics to crash. - SCP bugs in ssh_break and set_logon were fixed (found by Dave Hittner). - Numerous bugs in the HP 2100 extended arithmetic, floating point, 21MX, DMS, and IOP instructions were fixed. Bugs were also fixed in the memory protect and DMS functions. The moving head disks (DP, DQ) were revised to simulate the hardware more accurately. Missing functions in DQ (address skip, read address) were added. - PDP-10 tape wouldn't boot, and then wouldn't read (reported by Michael Thompson and Harris Newman, respectively) - PDP-1 typewriter is half duplex, with only one shift state for both input and output (found by Derek Peschel) 5. General Notes WARNING: V2.10 has reorganized and renamed some of the definition files for the PDP-10, PDP-11, and VAX. Be sure to delete all previous source files before you unpack the Zip archive, or unpack it into a new directory structure. WARNING: V2.10 has a new, more comprehensive save file format. Restoring save files from previous releases will cause 'invalid register' errors and loss of CPU option flags, device enable/ disable flags, unit online/offline flags, and unit writelock flags. WARNING: If you are using Visual Studio .NET through the IDE, be sure to turn off the /Wp64 flag in the project settings, or dozens of spurious errors will be generated. WARNING: Compiling Ethernet support under Windows requires extra steps; see the Ethernet readme file. Ethernet support is currently available only for Windows, Linux, NetBSD, and OpenBSD.
4498 lines
131 KiB
C
4498 lines
131 KiB
C
/*
|
|
* (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
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// ASM1130 - IBM 1130 Cross Assembler
|
|
//
|
|
// Version
|
|
// 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 [-bvsx] [-o[file]] [-l[file]] [-rN.M] file...
|
|
//
|
|
// Description:
|
|
// -b binary output (.bin, relocatable absolute format)
|
|
// -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
|
|
//
|
|
// 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:
|
|
// 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.
|
|
//
|
|
// Added special coldstart format directives:
|
|
//
|
|
// .IPL 1130,XXXXXXXX
|
|
// .IPL 1800,XXXXXXXX
|
|
//
|
|
// (these are not standard IBM)
|
|
//
|
|
// These directives cause the output file to be written in binary in either 1130 or
|
|
// 1800 IPL format. In 1130 format, the index bits are lost and the displacement
|
|
// is sign extended. In 1800 format, the data are punched 8 bits at a time into
|
|
// two columns per word. If an identifier is not given, data are punched into
|
|
// all 80 columns. If an identifier is given, data is punched in columns 0 through
|
|
// 72, and the identification XXXXXXXX is punched in columns 73 through 80. (If
|
|
// there are multiple output cards the last ident character is incremented). A
|
|
// warning is issued if 1130 assembly results in lost bits. These directives
|
|
// should be the first in the file as you don't want text and binary mixed in
|
|
// the same output file.
|
|
//
|
|
// 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>
|
|
|
|
// ---------------------------------------------------------------1------------------
|
|
// DEFINITIONS
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
// I have found some IBM source code where @ and ' seem interchangable.
|
|
// 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 VERSION "ASM1130 CROSS ASSEMBLER V1.07"
|
|
|
|
#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_1130, OUTMODE_1800, 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 [-bpsvwxy] [-o[file]] [-l[file]] [-rN.M] file...\n\n"
|
|
"-b binary (relocatable format) output; default is simulator LOAD format\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"
|
|
"-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";
|
|
|
|
BOOL verbose = FALSE; // verbose mode flag
|
|
BOOL tabformat = FALSE; // TRUE if tabs were seen in the file
|
|
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 TRUE, * means instruction addr+(value) during evaluation
|
|
int pta = -1; // program transfer address
|
|
BOOL cexpr = FALSE; // "C" expression syntax
|
|
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,
|
|
};
|
|
|
|
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 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);
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// main routine
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
int i, sawfile = FALSE;
|
|
|
|
for (i = 1; i < argc; i++) // process command line switches
|
|
if (*argv[i] == '-')
|
|
flag(argv[i]+1);
|
|
|
|
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
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// 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 'r':
|
|
if (sscanf(arg, "%d.%d", &major, &minor) != 2)
|
|
bail(usestr);
|
|
sprintf(dmsversion, "V%01.1dM%02.2d", major, minor);
|
|
return;
|
|
|
|
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 -- %s\n", VERSION, dmsversion, 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, 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, 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 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_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,
|
|
"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
|
|
"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, NO_ARGS,
|
|
"XCH", 0x1810, std_op, NONE, NONE, 0, // same as RTE 16
|
|
"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", 0x4840, 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;
|
|
|
|
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;
|
|
|
|
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]);
|
|
|
|
fwrite(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 assembled DMS on a 360
|
|
|
|
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);
|
|
}
|
|
|
|
// I don't know where I got this idea, but it's wrong...
|
|
// if (strchr(mods, '1') || strchr(mods, '2') || strchr(mods, '3')) { // index + X means ignore X
|
|
// if ((c = strchr(mods, 'X')) != NULL)
|
|
// strcpy(c, c+1); // remove the X
|
|
// }
|
|
|
|
if (*label) // display org in any line with a label
|
|
setw(0, org, 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);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// 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, FALSE); // display org in listing line
|
|
|
|
if (*label) // define label
|
|
set_symbol(label, org, TRUE, relocate);
|
|
|
|
// just one!?
|
|
getexpr(arg, FALSE, &expr);
|
|
writew(expr.value, expr.relative); // store value
|
|
|
|
// pick up values, comma delimited
|
|
// for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) {
|
|
// getexpr(tok, 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);
|
|
}
|
|
|
|
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]);
|
|
}
|
|
|
|
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;
|
|
}
|
|
// 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);
|
|
}
|
|
|
|
void getDconstant (char *tok, unsigned short *wd)
|
|
{
|
|
unsigned long l;
|
|
char *b, *fmt;
|
|
double d;
|
|
int bexp, fixed;
|
|
|
|
wd[0] = 0;
|
|
wd[1] = 0;
|
|
|
|
if (strchr(tok, '.') == NULL && strchr(tok, 'B') == NULL && strchr(tok, 'E') == NULL) {
|
|
fmt = "%ld";
|
|
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) { // no decimal means it's an integer?
|
|
asm_error("Syntax error in constant");
|
|
}
|
|
else {
|
|
wd[0] = (unsigned short) ((l >> 16) & 0xFFFF); // high word
|
|
wd[1] = (unsigned short) (l & 0xFFFF); // low word
|
|
}
|
|
return;
|
|
}
|
|
|
|
fixed = 0;
|
|
if ((b = strchr(tok, 'B')) != NULL) {
|
|
fixed = 1;
|
|
bexp = atoi(b+1);
|
|
*b = '\0'; // truncate at the b
|
|
}
|
|
if (sscanf(tok, "%lg", &d) != 1) {
|
|
asm_error("Syntax error in constant");
|
|
return;
|
|
}
|
|
|
|
if (fixed)
|
|
convert_double_to_fixed(d, wd, bexp);
|
|
else
|
|
convert_double_to_standard(d, wd);
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
// char *tok;
|
|
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, FALSE); // display the origin
|
|
|
|
if (*label) // define label
|
|
set_symbol(label, org, TRUE, relocate);
|
|
|
|
// just one!?
|
|
getDconstant(arg, wd);
|
|
writew(wd[0], FALSE); // write hiword, then loword
|
|
writew(wd[1], FALSE);
|
|
|
|
// pick up values, comma delimited
|
|
// for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) {
|
|
// getDconstant(tok, wd);
|
|
//
|
|
// writew(wd[0], FALSE); // write hiword, then loword
|
|
// writew(wd[1], FALSE);
|
|
}
|
|
|
|
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, FALSE); // display the origin
|
|
|
|
if (*label) // define label
|
|
set_symbol(label, org, TRUE, relocate);
|
|
// pick up values, comma delimited
|
|
for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) {
|
|
bexp = 0;
|
|
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 = FALSE; // * 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 = FALSE; // * 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, 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 {
|
|
namecode(words, arg); // emit namecode for loader
|
|
|
|
writetwo();
|
|
writew(words[0], CALL);
|
|
writew(words[1], ABSOLUTE);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// x_org - ORG directive
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
void x_org (struct tag_op *op, char *label, char *mods, char *arg)
|
|
{
|
|
EXPR expr;
|
|
|
|
org_advanced = FALSE; // * 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 = FALSE; // * 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, 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 = FALSE; // * means this address
|
|
|
|
if (pass < 2)
|
|
return;
|
|
|
|
// if (*label) // define label
|
|
// set_symbol(label, org, TRUE, relocate);
|
|
//
|
|
// setw(0, org, 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);
|
|
}
|
|
}
|
|
|
|
void x_spr (struct tag_op *op, char *label, char *mods, char *arg)
|
|
{
|
|
realmode = REALMODE_STANDARD;
|
|
}
|
|
|
|
void x_epr (struct tag_op *op, char *label, char *mods, char *arg)
|
|
{
|
|
realmode = REALMODE_EXTENDED;
|
|
}
|
|
|
|
void x_dsa (struct tag_op *op, char *label, char *mods, char *arg)
|
|
{
|
|
unsigned short words[2];
|
|
|
|
setw(0, org, FALSE); // display origin
|
|
|
|
if (*label) // define label
|
|
set_symbol(label, org, TRUE, relocate);
|
|
|
|
if (! *arg) {
|
|
asm_error("DSA missing filename");
|
|
}
|
|
else {
|
|
namecode(words, arg);
|
|
writetwo();
|
|
writew(words[0], CALL); // special relocation bits here 3 and 1
|
|
writew(words[1], RELATIVE);
|
|
}
|
|
}
|
|
|
|
void x_link (struct tag_op *op, char *label, char *mods, char *arg)
|
|
{
|
|
unsigned short words[2];
|
|
char nline[128];
|
|
|
|
setw(0, org, 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);
|
|
|
|
namecode(words, arg);
|
|
writew(words[0], ABSOLUTE); // special relocation bits here 3 and 1
|
|
writew(words[1], ABSOLUTE);
|
|
}
|
|
}
|
|
|
|
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 words in any case
|
|
org++;
|
|
return;
|
|
}
|
|
|
|
setw(0, org, FALSE); // display origin
|
|
|
|
namecode(words, arg); // emit namecode for loader
|
|
|
|
writetwo();
|
|
writew(words[0], LIBF); // this one does NOT advance org!
|
|
writew(words[1], ABSOLUTE);
|
|
}
|
|
|
|
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 = FALSE; // * 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, FALSE); // display origin
|
|
|
|
if (*label) // define label
|
|
set_symbol(label, org, TRUE, relocate);
|
|
|
|
if (expr.value < 0)
|
|
asm_warning("Negative BSS size");
|
|
|
|
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 = FALSE; // * 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, 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, 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, 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, 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 = FALSE; // * 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
|
|
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
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
void x_opt (struct tag_op *op, char *label, char *mods, char *arg)
|
|
{
|
|
char *tok;
|
|
|
|
org_advanced = FALSE; // * 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
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
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");
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
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
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
void a1130_term (EXPR *ap)
|
|
{
|
|
PSYMBOL s;
|
|
char token[80], *t;
|
|
int c;
|
|
|
|
if (cexpr) { // use C syntax
|
|
c_term(ap);
|
|
return;
|
|
}
|
|
|
|
c = GETNEXT;
|
|
|
|
if (ctype[c] == DIGIT) { /* number */
|
|
ap->value = 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 = 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);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// c_expr - evalate an expression
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
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 = c_number(c,10,-1);
|
|
}
|
|
else if (c == '0') { /* 0 starts a hex or octal constant */
|
|
if ((c = GETNEXT) == 'x') {
|
|
c = GETNEXT;
|
|
ap->value = c_number(c,16,-1);
|
|
}
|
|
else {
|
|
ap->value = 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, '>',
|
|
};
|
|
|
|
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
|
|
|
|
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
|