/* sel32_sys.c: SEL-32 Gould Concept/32 (orignal SEL-32) Simulator system interface.

   Copyright (c) 2018-2023, James C. Bevier
   Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   JAMES C. BEVIER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/* Concept 32 PSD Mode Trap/Interrupt Priorities */
/* Relative|Logical |Int Vect|TCW |IOCD|Description */
/* Priority|Priority|Location|Addr|Addr             */
/*   -                 080              Power Fail Safe Trap */
/*   -                 084              Power On Trap */
/*   -                 088              Memory Parity Trap */
/*   -                 08C              Nonpresent Memory Trap */
/*   -                 090              Undefined Instruction Trap */
/*   -                 094              Privilege Violation Trap */
/*   -                 098              Supervisor Call Trap (SVC) */
/*   -                 09C              Machine Check Trap */
/*   -                 0A0              System Check Trap */
/*   -                 0A4              Map Fault Trap */
/*   -                 0A8              CALM or Undefined IPU Instruction Trap */
/*   -                 0AC              Signal CPU or Signal IPU Trap */
/*   -                 0B0              Address Specification Trap */
/*   -                 0B4              Console Attention Trap */
/*   -                 0B8              Privlege Mode Halt Trap */
/*   -                 0BC              Arithmetic Exception Trap */
/*   -                 0C0              Cache Error Trap (V9 Only) */
/*   -                 0C4              Demand Page Fault Trap (V6&V9 Only) */
/*                                                                */
/*   0        00       100              External/software Interrupt 0 */
/*   1        01       104              External/software Interrupt 1 */
/*   2        02       108              External/software Interrupt 2 */
/*   3        03       10C              External/software Interrupt 3 */
/*   4        04       110    704  700  I/O Channel 0 interrupt */  
/*   5        05       114    70C  708  I/O Channel 1 interrupt */  
/*   6        06       118    714  710  I/O Channel 2 interrupt */  
/*   7        07       11C    71C  718  I/O Channel 3 interrupt */  
/*   8        08       120    724  720  I/O Channel 4 interrupt */  
/*   9        09       124    72C  728  I/O Channel 5 interrupt */  
/*   A        0A       128    734  730  I/O Channel 6 interrupt */  
/*   B        0B       12C    73C  738  I/O Channel 7 interrupt */  
/*   C        0C       130    744  740  I/O Channel 8 interrupt */  
/*   D        0D       134    74C  748  I/O Channel 9 interrupt */  
/*   E        0E       138    754  750  I/O Channel A interrupt */  
/*   F        0F       13C    75C  758  I/O Channel B interrupt */  
/*  10        10       140    764  760  I/O Channel C interrupt */  
/*  11        11       144    76C  768  I/O Channel D interrupt */  
/*  12        12       148    774  770  I/O Channel E interrupt */  
/*  13        13       14c    77C  778  I/O Channel F interrupt */  
/*  14        14       150              External/Software Interrupt */
/*  15        15       154              External/Software Interrupt */
/*  16        16       158              External/Software Interrupt */
/*  17        17       15C              External/Software Interrupt */
/*  18        18       160              Real-Time Clock Interrupt */
/*  19        19       164              External/Software Interrupt */
/*  1A        1A       1A8              External/Software Interrupt */
/*  1B        1B       1AC              External/Software Interrupt */
/*  1C        1C       1B0              External/Software Interrupt */
/* THRU      THRU     THRU                        THRU              */ 
/*  6C        6C       2B0              External/Software Interrupt */
/*  6D        6D       2B4              External/Software Interrupt */
/*  6E        6E       2B8              External/Software Interrupt */
/*  6F        6F       2BC              Interval Timer Interrupt */

/* IVL ------------> ICB   Trap/Interrupt Vector Location points to Interrupt Context Block */
/*                   Wd 0 - Old PSD Word 1  points to return location */
/*                   Wd 1 - Old PSD Word 2 */
/*                   Wd 2 - New PSD Word 1  points to first instruction of service routine */
/*                   Wd 3 - New PSD Word 2 */
/*                   Wd 4 - CPU Status word at time of interrupt/trap */
/*                   Wd 5 - N/U For Traps/Interrupts */

/* IVL ------------> ICB   XIO Interrupt Vector Location */
/*                   Wd 0 - Old PSD Word 1  points to return location */
/*                   Wd 1 - Old PSD Word 2 */
/*                   Wd 2 - New PSD Word 1  points to first instruction of service routine */
/*                   Wd 3 - New PSD Word 2 */
/*                   Wd 4 - Input/Output Command List Address (IOCL) for the Class F I/O CHannel */
/*                   Wd 5 - 24 bit real address of the channel status word */

/*-----------------------------------------------------------------------------------------------*/

/* Map image descriptor 32/77 */
/* |--------------------------------------| */
/* |0|1|2|3 4 5 6|7 8  9 10 11 12 13 14 15| */
/* |N|V|P|  n/u  | 9 bit map block entry  | */
/* |U| | |       |      32kb/block        | */
/* |             |  32 8kb maps per task  | */
/* |             |   1 mb address space   | */
/* |--------------------------------------| */

/* Map image descriptor 32/27 */
/* |--------------------------------------| */
/* |0|1|2|3|4|5 6 7 8  9 10 11 12 13 14 15| */
/* |V|P|P|P|P|    11 bit map block entry  | */
/* | |1|2|3|4|           8kb/block        | */
/* |         |    256 8kb maps per task   | */
/* |         |      2 mb address space    | */
/* |--------------------------------------| */

/* Map image descriptor  32/67, 32/87, 32/97 */
/* |--------------------------------------| */
/* |0|1|2|3|4|5 6 7 8  9 10 11 12 13 14 15| */
/* |V|P|P|P|P|    11 bit map block entry  | */
/* | |1|2|3|4|           2kb/block        | */
/* |         |    2048 8kb maps per task  | */
/* |         |      16 mb address space   | */
/* |--------------------------------------| */
/* BIT 0 = 0    Invalid map block (page) entry */
/*       = 1    Valid map block (page) entry */
/*     1 = 0    000-7ff of 8kb page is not write protected */
/*       = 1    000-7ff of 8kb page is write protected */
/*     2 = 0    800-fff of 8kb page is not write protected */
/*       = 1    800-fff of 8kb page is write protected */
/*     3 = 0    1000-17ff of 8kb page is not write protected */
/*       = 1    1000-17ff of 8kb page is write protected */
/*     4 = 0    1800-1fff of 8kb page is not write protected */
/*       = 1    1800-1fff of 8kb page is write protected */
/*  5-15 =      11 most significant bits of the 24 bit real address for page */

/* Map image descriptor V6 & V9 */
/* |--------------------------------------| */
/* |0|1|2|3|4|5 6 7 8  9 10 11 12 13 14 15| */
/* |V|P|P|M|M|    11 bit map block entry  | */
/* | |1|2|M|A|           2kb/map          | */
/* |         |    2048 8kb maps per task  | */
/* |         |      16 mb address space   | */
/* |--------------------------------------| */
/* BIT 0 = 0    Invalid map block (page) entry */
/*       = 1    Valid map block (page) entry */
/* */
/* PSD 1 BIT 0 -  Map Bit 1 - Map Bit 2 - Access state */
/* Priv Bits with ECO for Access Protection change */
/*     0              0           0     No access allowed to page */
/*     0              0           1     No access allowed to page */
/*     0              1           0     Read/Write/Execute access */
/*     0              1           1     Read/Execute access only */
/*O/S*/
/*     1              0           0     Read/Write/Execute access */
/*     1              0           1     Read/Execute access only */
/*     1              1           0     Read/Write/Execute access */
/*     1              1           1     Read/Execute access only */
/* Priv Bits without ECO for Access Protection change */
/*     0              0           0     No access allowed to page */
/*     0              0           1     Read/Execute access only */
/*     0              1           0     Read//Execute access only */
/*     0              1           1     Read/Write/Execute access */
/*O/S*/
/*     1              0           0     Read/Write/Execute only */
/*     1              0           1     Read/Execute access only */
/*     1              1           0     Read/Write/Execute access */
/*     1              1           1     Read/Write/Execute access */
/* */
/* BIT 3 = 0    (MM) A first write (modify) to the map block (page) has not occurred */
/*       = 1    (MM) A first write (modify) to the map block (page) has occurred */
/* BIT 4 = 0    (MA) A first read or write (access) to the map block (page) has not occurred */
/*       = 1    (MA) A first read or write (access) to the map block (page) has occurred */
/*  5-15 =      11 most significant bits of the 24 bit real address for page */

/* Note */
/* If a map is valid, a MAP (page) hit occurs and logical to physical translation occures */
/* If the map is not valid, a demand MAP (page) fault occures and the faulting page is provided */
/* P1 and P2 are used with Bit 0 of PSD to define the access rights */
/* A privilege violation trap occurres if access it denied */
/* Bits 5-15 contain the 11 most-significant bits of the physical address */
/* MSD 0 page limit is used to verify access to O/S pages */
/* CPIXPL page limit is used to verify access to user pages and page faults */
/* CPIX CPIX of user MPL offset */
/* Access to pages outside the limit registers results in a map fault */

/*-----------------------------------------------------------------------------------------------*/

#include "sel32_defs.h"
#include <ctype.h>

extern REG cpu_reg[];
extern uint32 SPAD[];
extern uint32 PSD[];
char *dump_mem(uint32 mp, int cnt);
char *dump_buf(uint8 *mp, int32 off, int cnt);

/* SCP data structures and interface routines

The interface between the simulator control package (SCP) and the
simulator consists of the following routines and data structures

sim_name            simulator name string
sim_devices[]       array of pointers to simulated devices
sim_PC              pointer to saved PC register descriptor
sim_interval        simulator interval to next event (in sel32_cpu.c)
sim_stop_messages[] array of pointers to stop messages
sim_instr()         instruction execution routine (in sel32_cpu.c)
sim_load()          binary loader routine
sim_emax            maximum number of words for examine

In addition, the simulator must supply routines to print and parse
architecture specific formats

fprint_sym          print symbolic output
fparse_sym          parse symbolic input
*/

char sim_name[] = "SEL-32";                 /* our simulator name */
REG *sim_PC = &cpu_reg[0];

int32 sim_emax = 4;                         /* maximum number of instructions/words to examine */

DEVICE *sim_devices[] = {
        &cpu_dev,
#ifdef USE_IPU_THREAD
        &ipu_dev,
#endif
#ifdef NUM_DEVS_IOP
        &iop_dev,                           /* IOP channel controller */
#endif
#ifdef NUM_DEVS_MFP
        &mfp_dev,                           /* MFP channel controller */
#endif
#ifdef NUM_DEVS_RTOM
        &rtc_dev,
        &itm_dev,
#endif
#ifdef NUM_DEVS_CON
        &con_dev,
#endif
#ifdef NUM_DEVS_CDR
        &cdr_dev,
#endif
#ifdef NUM_DEVS_CDP
        &cdp_dev,
#endif
#ifdef NUM_DEVS_LPR
        &lpr_dev,
#endif
#ifdef NUM_DEVS_MT
        &mta_dev,
#if NUM_DEVS_MT > 1
        &mtb_dev,
#endif
#endif
#ifdef NUM_DEVS_DISK
        &dda_dev,
#if NUM_DEVS_DISK > 1
        &ddb_dev,
#endif
#endif
#ifdef NUM_DEVS_SCFI
        &sda_dev,
#if NUM_DEVS_SCFI > 1
        &sdb_dev,
#endif
#endif
#ifdef NUM_DEVS_HSDP
        &dpa_dev,
#if NUM_DEVS_HSDP > 1
        &dpb_dev,
#endif
#endif
#ifdef NUM_DEVS_SCSI
        &sba_dev,
#if NUM_DEVS_SCSI > 1
        &sbb_dev,
#endif
#endif
#ifdef NUM_DEVS_ETHER
        &ec_dev,
#endif
#ifdef NUM_DEVS_COM
        &coml_dev,
        &com_dev,
#endif
       NULL };

/* Simulator debug controls */
DEBTAB              dev_debug[] = {
    {"CMD", DEBUG_CMD, "Show command execution to devices"},
    {"DATA", DEBUG_DATA, "Show data transfers"},
    {"DETAIL", DEBUG_DETAIL, "Show details about device"},
    {"EXP", DEBUG_EXP, "Show exception information"},
    {"INST", DEBUG_INST, "Show instruction execution"},
    {"XIO", DEBUG_XIO, "Show XIO I/O instructions"},
    {"IRQ", DEBUG_IRQ, "Show interrupt requests"},
    {"TRAP", DEBUG_TRAP, "Show trap requests"},
    {0, 0}
};

const char *sim_stop_messages[SCPE_BASE] = {
       "Unknown error",
       "IO device not ready",
       "HALT instruction",
       "Breakpoint",
       "IPU Reset request",
       "Invalid instruction",
       "Invalid I/O operation",
       "Waiting for CPU to run",
       "I/O Check opcode",
       "Memory management trap during trap",
};

#define PRINTABLE(x) ((x < 32) || (x > 126)) ? '.' : x

static char line[257];
/* function to dump SEL32 memory up to 16 bytes with side by side ascii values */
char *dump_mem(uint32 mp, int cnt)
{
    char    buff[257];
    uint32  ma = mp;                            /* save memory address */
    char    *cp = &line[0];                     /* output buffer */
    int     cc=0, ch, bp=0, bl=cnt;

    if (cnt > 16)
        bl = 16;                                /* stop at 16 chars */

    while (bp < bl) {
        if (!bp) {
            cc = sprintf(cp, " %06x : ", ma);    /* output location address */
            cp += cc;                           /* next print location */
        }
        ch = RMB(ma) & 0xff;                    /* get a char from memory */
        ma++;                                   /* next loc */
        cc += sprintf(cp, "%02x", ch);          /* print out current char */
        cp += 2;                                /* next print location */
        buff[bp++] = PRINTABLE(ch);             /* get printable version of char */
        if (!(bp % 4)) {                        /* word boundry yet? */
            cc += sprintf(cp, " ");             /* space between words */
            cp += 1;                            /* next print location */
        }
    }

    while (bp < 16) {
        cc += sprintf(cp, " ");                 /* print out one space */
        cp += 1;                                /* next print location */
        buff[bp++] = 0x20;                      /* blank char buffer */
        if (!(bp % 4)) {
            cc += sprintf(cp, " ");             /* space between words */
            cp += 1;                            /* next print location */
        }
    }
    buff[bp] = 0;                               /* terminate line */
    cc += sprintf(cp, "|%s|\n", buff);          /* print out ascii text */
    return (line);                              /* return pointer to caller */
}

/* function to dump caller buffer upto 16 bytes with side by side ascii values */
/* off is offset in buffer to start */
char *dump_buf(uint8 *mp, int32 off, int cnt)
{
    char    buff[257];
    uint32  ma = off;                           /* save memory address */
    char    *cp = &line[0];                     /* output buffer */
    int     cc=0, ch, bp=0, bl=cnt;

    if (cnt > 16)
        bl = 16;                                /* stop at 16 chars */

    while (bp < bl) {
        if (!bp) {
            cc = sprintf(cp, " %06x : ", ma);    /* output location offset */
            cp += cc;                           /* next print location */
        }
        ch = mp[ma++] & 0xff;                   /* get a char from memory */
        cc += sprintf(cp, "%02x", ch);          /* print out current char */
        cp += 2;                                /* next print location */
        buff[bp++] = PRINTABLE(ch);             /* get printable version of char */
        if (!(bp % 4)) {                        /* word boundry yet? */
            cc += sprintf(cp, " ");             /* space between words */
            cp += 1;                            /* next print location */
        }
    }

    while (bp < 16) {
        cc += sprintf(cp, " ");                 /* print out one space */
        cp += 1;                                /* next print location */
        buff[bp++] = 0x20;                      /* blank char buffer */
        if (!(bp % 4)) {
            cc += sprintf(cp, " ");             /* space between words */
            cp += 1;                            /* next print location */
        }
    }
    buff[bp] = 0;                               /* terminate line */
    cc += sprintf(cp, "|%s|\n", buff);          /* print out ascii text */
    return (line);                              /* return pointer to caller */
}

/*
 * get_word - function to load a 32 bit word from the input file 
 * return 1 - OK
 * return 0 - error or eof
 */
int get_word(FILE *fileref, uint32 *word)
{
    unsigned char cbuf[4];

    /* read in the 4 chars */
    if (sim_fread(cbuf, 1, 4, fileref) != 4)
        return 1;                           /* read error or eof */
    /* byte swap while reading data */
    *word = ((cbuf[0]) << 24) | ((cbuf[1]) << 16) |
            ((cbuf[2]) << 8) | ((cbuf[3]));
    return 0;                               /* all OK */
}

#ifdef NO_TAP_FOR_NOW
/*
 * get_halfword - function to load a 16 bit halfword from the input file 
 * return 1 - OK
 * return 0 - error or eof
 */
int get_halfword(FILE *fileref, uint16 *word)
{
    unsigned char cbuf[2];

    /* read in the 2 chars */
    if (sim_fread(cbuf, 1, 2, fileref) != 2)
        return 1;                           /* read error or eof */
    /* byte swap while reading data */
    *word = ((uint16)(cbuf[0]) << 8) | ((uint16)(cbuf[1]));
    return 0;                               /* all OK */
}
#endif

/* load a binary file into memory starting at loc 0 */
/* return SCPE_OK on load complete */
t_stat load_mem (FILE *fileref)
{
    uint32 data;
    uint32 ma = 0;  /* start at mem add 0 */

    /* read the file until the end */
    for ( ;; ) {
        if (get_word(fileref, &data))       /* get 32 bits of data */
            return SCPE_OK;                 /* load is complete, return */
        M[ma++] = data;                     /* put data in memory */
    }
    return SCPE_OK;                         /* never here */
}

#ifdef NO_TAP_FOR_NOW
/* load tap formated tape into memory */
/* return SCPE_OK on load complete */
t_stat load_tap (FILE *fileref)
{
    uint32 bdata, edata;
    uint16 hdata;
    uint32 ma = 0;                          /* start loading at loc 0 */
    int32 wc;

    for ( ;; ) {                            /* loop until EOF read */
        /* look for record byte count or zero for EOF */
        if (get_word(fileref, &bdata))      /* read 4 bytes of data */
            return SCPE_FMT;                /* must be error, exit */
        wc = (int32)(bdata);                /* byte count in tape record */
        wc = (wc + 1)/2;                    /* change byte count into hw count */
        if (wc == 0)
            return SCPE_OK;                 /* eof found, return */
        /* copy data to memory in 16 bit halfwords */
        while (wc-- != 0) {
            if (get_halfword(fileref, &hdata))  /* get 16 bits of data */
                return SCPE_FMT;            /* must be error, exit */
            ((uint16*)M)[ma++] = hdata;     /* put the hw into memory */
        }
        /* look only for record byte count */
        if (get_word(fileref, &edata))      /* read 4 bytes of data */
            return SCPE_FMT;                /* must be error, exit */
        /* the before and after byte count must be equal */
        if (bdata != edata)
            return SCPE_FMT;                /* must be error, exit */
    }
    return SCPE_OK;                         /* never here */
}
#endif

/* ICL formats */
/***********************************************
 *
 * *DEVXX=FCILCASA (,N)
 *
 * *DEV     defines a controller definition entry
 * XX       hex address that will be used by I/O instructions to address controller
 * =        required delimiter for following 8 hex characters
 * F        flag used for I/O emulation by CPU, not used but must be zero.
 * C        defines the class of controller:
 *          0 = Line Printer
 *          1 = Card Reader
 *          2 = Teletype
 *          3 = Interval Timer
 *          4 = Panel
 *          5-D Unassigned
 *          E = All Others
 *          F = Extended I/O
 * IL       Controller interrupt priority level of Service Interrupt (0x14 - 0x23)
 * CA       Controller address defined by hardware switches on controller
 * SA       Lowest controller device subaddress.  Usually zero when only 1 device configured
 *              The subaddress field (SA) must reflect the following for TLC controller:
 *              00 = Card Reader
 *              01 = Teletype
 *              02 = Line Printer
 * ()       denotes optional parameter
 * ,NN      2 digit hexx number of devices configured on the controller`
 *
 ***********************************************
 *
 * *INTXX=RS
 * *INT     defines interrupt definition entry
 * XX       Hex interrupt priority level to be defined
 * =        required delimiter for following 2 hex characters
 * R        Hex RTOM board number to which the interrupt XX is assigned
 * S        1's complement of the hex subaddress of the RTOM board
 *              assigned to the interrupt XX
 *
 *          RTOM physical controller address 0x79 is RTOM board number 1, RTOM
 *              address 0x7A is board number 2, etc.
 *          Real-Time Clock is connected tp subaddress 6 on RTOM board
 *          Interval Timer is connected to subaddress 4 on RTOM board
 *          RTOM physical address must be 0x79 or above to be able to
 *              support up to seven RTOM boards for maximum configuration
 *              of 112 interrupt levels (7 x 16).
 *
 ***********************************************
 *
 * *END     Defines the last record of the Initial Configuration Load file.
 *
 ***********************************************
*/

/* Example device entry
 * *DEV04=0E140100,04
 *      The controller is "E" class
 *      CPU command device address will be 0x04
 *      The priority of the Service Interrupt is 0x14
 *      The first device has suaddress of 00 and there are 4 devices defined
 *      There will be four devices defined in SPAD. The I/O commands (CD and TD)
 *          will address the devices as 0x04, 0x05, 0x06, and 0x07.
 *      The physical address of the controller is 0x10.
 *      Assigning SI address of 0x14 means:
 *          The transfer Interrupt location for priority 0x14 is 0x100.
 *          The Service Interrupt vector location for priority 0x14 is 0x140.
 *          The emulation IOCD will be stored at loation 0x700.
 *          The interrupt control instructions (DI, DI, RI, AI, DAI) will control
 *              the interrupt of the controller by addressing priority 0x14.
 *
 *  Example interrupt entry (RTOM)
 *  *INT28=16
 *      The interrupt control instructions (DI, EI, RI, AI, DAI) will control
 *          the interrupt on the RTOM by addressing priority 0x28.
 *      The RTOM board is 1
 *      The subaddress on the board is 0x06 (jumpered locic subaddress is 9)
 */

/*
 * Example ICL file
 * *DEV04=0E150400,02       Cartridge disc with two platters
 * *DEV08=0E160800,04       Moving head disc
 * *DEV10=0E181000,04       9-Track magnetic tape
 * *DEV20=0E1A2000,10       GPMC with 16 terminals
 * *DEV60=0E1E6000,08       ADS
 * *DEV78=01207800          Primary card reader
 * *DEV7A=00217802          Primary line printer
 * *DEV7E=02237801          Primary Teletype
 * *INT00=1F                Power fail/Auto restart
 * *INT01=1E                System Overide
 * *INT12=1D                Memory parity
 * *INT13=1C                Console Interrupt
 * *INT24=1B                Nonpresent memory
 * *INT25=1A                Undefined instruction trap
 * *INT26=19                Privlege violation
 * *INT27=18                Call Monitor
 * *INT28=16                Real-time clock
 * *INT29=17                Arithmetic exception
 * *INT2A=15                External interrupt
 * *INT2B=14                External interrupt
 * *INT2C=13                External interrupt
 * *INT2D=12                External interrupt
 * *END
 */
 
/* process two hex input characters into a number
 * pt - input char pointer
 * val - word oiunter when number will be saved
 * return SCPE_OK for OK
 * or SCPE_ARG for arg error (bad number)
 */
t_value get_2hex(char *pt, uint32 *val)
{
    int32 hexval;
    uint32 c1 = sim_toupper((uint32)pt[0]); /* first hex char */
    uint32 c2 = sim_toupper((uint32)pt[1]); /* next hex char */

    if (isdigit(c1))                        /* digit */
        hexval = c1 - (uint32)'0';          /* get value */
    else
    if (isxdigit(c1))                       /* hex digit */
        hexval = c1 - (uint32)'A' + 10;     /* get hex value */
    else
        return SCPE_ARG;                    /* oops, error */
    hexval <<= 4;                           /* move to upper nibble */
    if (isdigit(c2))                        /* digit */
        hexval += c2 - (uint32)'0';         /* get value */
    else
    if (isxdigit(c2))                       /* hex digit */
        hexval += c2 - (uint32)'A' + 10;    /* get hex value */
    else
        return SCPE_ARG;                    /* oops, error */
    *val = hexval;                          /* return value to caller */
    return SCPE_OK;                         /* all OK */
}

/* load an ICL file and configure SPAD interupt and device entries */
/* SPAD keyword will not be set and will be set when MPX or UTX is loaded */
/* return SCPE_OK on load complete */
t_stat load_icl(FILE *fileref)
{
    char        *cp;                        /* work pointer in buf[] */
    uint32      sa;                         /* spad address */
    uint32      dev;                        /* device entry */
    uint32      intr;                       /* interrupt entry */
    uint32      data;                       /* entry data */
    uint32      cls;                        /* device class */
    uint32      ivl;                        /* Interrupt Vector Location */
    uint32      i;                          /* just a tmp */
    char        buf[120];                   /* input buffer */

    /* read file input records until the end */
    while (fgets(&buf[0], 120, fileref) != 0) {
        /* skip any white spaces */
        for (cp = &buf[0]; *cp == ' ' || *cp == '\t'; cp++);
        if (*cp++ != '*')
            continue;                       /* if line does not start with *, ignore */
        if (sim_strncasecmp(cp, "END", 3) == 0) {
            return SCPE_OK;                 /* we are done */
        }
        else
        if (sim_strncasecmp(cp, "DEV", 3) == 0) {
            /* process device entry */
            /*
            |----+----+----+----+----+----+----+----|
            |Flgs|CLS |0|Int Lev|0|Phy Adr|Sub Addr |
            |----+----+----+----+----+----+----+----|
            */
            for (cp += 3; *cp == ' ' || *cp == '\t'; cp++);  /* skip white spaces */
            if (get_2hex(cp, &dev) != SCPE_OK)      /* get the device address */
                return SCPE_ARG;            /* unknown input, argument error */
            if (dev > 0x7f)                 /* devices are 0-7f (0-127) */
                return SCPE_ARG;            /* argument error */
            sa = dev + 0x00;                /* device entry spad address is dev# + 0x00 */
            cp += 2;                        /* skip the 2 processed chars */
            if (*cp++ != '=')               /* must have = sign */
                return SCPE_ARG;            /* unknown input, argument error */
            if (get_2hex(cp, &cls) != SCPE_OK)  /* get unused '0" and class */
                return SCPE_ARG;            /* unknown input, argument error */
            cp += 2;                        /* skip the 2 processed chars */
            if (get_2hex(cp, &intr) != SCPE_OK) /* get the interrupt level value */
                return SCPE_ARG;            /* unknown input, argument error */
            if (intr > 0x6f)                /* ints are 0-6f (0-111) */
                return SCPE_ARG;            /* argument error */
            /* put class and 1's intr in place */
            dev = ((~intr & 0x7f) << 16) | ((cls & 0x0f) << 24);
            cp += 2;                        /* skip the 2 processed chars */
            if (get_2hex(cp, &data) != SCPE_OK) /* get the selbus physical address */
                return SCPE_ARG;            /* unknown input, argument error */
            if (data > 0x7f)                /* address is 0-7f (0-127) */
                return SCPE_ARG;            /* argument error */
            dev |= (data & 0x7f) << 8;      /* insert the physical address */
            cp += 2;                        /* skip the 2 processed chars */
            if (get_2hex(cp, &data) != SCPE_OK) /* get the starting sub address 0-ff (255) */
                return SCPE_ARG;            /* unknown input, argument error */
            if (data > 0x7f)                /* sub address is 0-ff (0-256) */
                return SCPE_ARG;            /* argument error */
            if ((cls & 0xf) != 0xf)         /* sub addr must be zero for class F */
                dev |= (data & 0xff);       /* insert the starting sub address for non f class */
            SPAD[sa] = dev;                 /* put the first device entry into the spad */
            /* see if there is an optional device count for class 'E' I/O */
            if ((cls & 0xf) == 0xe) {
                cp += 2;                    /* skip the 2 processed chars */
                if (*cp++ == ',') {         /* must have comma if optional parameters */
                    /* check for optional sub addr cnt */
                    if (get_2hex(cp, &data) != SCPE_OK) /* get the count */
                        return SCPE_ARG;    /* unknown input, argument error */
                    if (data > 0x10)        /* sub address is max of 16 */
                        return SCPE_ARG;    /* argument error */
                    for (i=0; i<data-1; i++) {  /* 1st is already stored, redice cnt by 1 */
                        /* each of the devices will share the same interrupt */
                        SPAD[++sa] = ++dev; /* put next device entry in spad for the controller */
                    }   /* done with 'E' class device with multiple devices */
                }
            }   /* done with device entry */
            /* now create an interrupt entry for the controller */
            /*
            |----+----+----+----+----+----+----+----|
            |   Flags |0|Int Lev|      Int IVL      |
            |----+----+----+----+----+----+----+----|
            */
/* TODO call function here to create 32/7x IVL location for interrupt */
/* if (CPU_MODEL < MODEL_27) get_IVL(intr, &ivl); */
            sa = intr + 0x80;               /* interrupt entry spad address is int# + 0x80 */
            ivl = (intr << 2) + 0x100;      /* default IVL base is 0x100 for Concept machines */
            intr = (intr << 16) | ivl;      /* combine int level and ivl */
            SPAD[sa] = intr;                /* put the device interrupt entry into the spad */
        }
        else
        if (sim_strncasecmp(cp, "INT", 3) == 0) {
            /* process interrupt entry */
            /*
            |----+----+----+----+----+----+----+----|
            |   Flags |1RRR|SSSS|      Int IVL      |
            |----+----+----+----+----+----+----+----|
            */
            for (cp += 3; *cp == ' ' || *cp == '\t'; cp++);  /* skip white spaces */
            if (get_2hex(cp, &intr) != SCPE_OK) /* get the interrupt level value */
                return SCPE_ARG;            /* unknown input, argument error */
            if (intr > 0x6f)                /* ints are 0-6f (0-111) */
                return SCPE_ARG;            /* argument error */
            sa = intr + 0x80;               /* interrupt entry spad address is int# + 0x80 */
/* TODO call function here to create 32/7x IVL location for interrupt */
/* if (CPU_MODEL < MODEL_27) get_IVL(intr, &ivl); */
            ivl = (intr << 2) + 0x100;      /* default IVL base is 0x100 for Concept machines */
            cp += 2;                        /* skip the 2 processed chars */
            if (*cp++ != '=')               /* must have = sign */
                return SCPE_ARG;            /* unknown input, argument error */
            if (get_2hex(cp, &data) != SCPE_OK)
                return SCPE_ARG;            /* unknown input, argument error */
            /* first digit is 3 ls bits of RTOM addr 0x79 is 001 */
            intr = 0x00800000 | ((data & 0x70) << 16);  /* put the RTOM 3 LSBs into entry */
            /* second digit is subaddress on RTOM board for interrupt connection, ~6 = 9 */
            intr |= (data & 0xf) << 16;     /* put in 1's comp of RTOM subaddress */
            /* add in the correct IVL for 32/7x or concelt machines */
            intr |= ivl;                    /* set the IVL location */
            SPAD[sa] = intr;                /* put the interrupt entry into the spad */
        }
        else
            return SCPE_ARG;                /* unknown input, argument error */
    }
    return SCPE_OK;                         /* file done */
}


/* Load a file image into memory.  */
/* file.mem files are binary files created with the makecode utility */
#ifdef NO_TAP_FOR_NOW
/* file.tap files are TAP formatted binary files created from tape images */
#endif
/* data is raw binary memory data and is loaded starting at loc 0 */

#define FMT_NONE 0
#define FMT_MEM 1
#ifdef NO_TAP_FOR_NOW
#define FMT_TAP 2
#endif
#define FMT_ICL 3
t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
{
    int32 fmt;

    fmt = FMT_NONE;                         /* no format */
    /* match the extension to .mem for this file */
    if (match_ext(fnam, "MEM"))
        fmt = FMT_MEM;                      /* we have binary format */
    else
#ifdef NO_TAP_FOR_NOW
    if (match_ext(fnam, "TAP"))
        fmt = FMT_TAP;                      /* we have tap tape format */
    else
#endif
    /* match the extension to .icl for this file */
    if (match_ext(fnam, "ICL"))
        fmt = FMT_ICL;                      /* we have initial configuration load (ICL) format */
    else
        return SCPE_FMT;                    /* format error */

    switch (fmt) {

    case FMT_MEM:                           /* binary memory image */
        return load_mem(fileref);

#ifdef NO_TAP_FOR_NOW
    case FMT_TAP:                           /* tape file image */
        return load_tap(fileref);
#endif

    case FMT_ICL:                           /* icl file image */
        return load_icl(fileref);

#ifdef NO_TAP_FOR_NOW
    case FMT_NONE:                          /* nothing */
#endif
    default:
        break;
    }
    return SCPE_FMT;                        /* format error */
}

/* Symbol tables */

/*
 * The SEL 32 supports the following instruction formats.
 * 
 * TYPE     Format   Normal     Base Mode
 *  A       ADR      d,[*]o,x   d,o[(b)],x  FC = extra
 *  B       BRA      [*]o,x     o[(b)],x
 *  C       IMM      d,o        d,o
 *  D       BIT      d,[*]o     d,o[(b)]
 *  E       ADR      [*]o,x     o[(b)],x  FC = extra
 *  HALFWORD
 *  F       REG      s,d        s,d         Half Word
 *  G       RG1      s          s
 *  H       HLF
 *  I       SHF      d,v        d,v
 *  K       RBT      d,b        d,b
 *  L       EXR      s          s
 *  M       IOP      n,b        n,b  
 *  N       SVC      n,b        n,b  
 */

#define TYPE_A          0
#define TYPE_B          1 
#define TYPE_C          2
#define TYPE_D          3 
#define TYPE_E          4
#define TYPE_F          5 
#define TYPE_G          6
#define TYPE_H          7 
#define TYPE_I          8 
#define TYPE_K          9 
#define TYPE_L          10 
#define TYPE_M          11 
#define TYPE_N          12 
#define H               0x10                /* halfword instruction */
/* all instruction unless specified as base/nobase only will be either */
#define B               0x20                /* base register mode only */
#define N               0x40                /* non base register mode only */
#define X               0x80                /* 32/55 or 32/75 only */

typedef struct _opcode {
       uint16       opbase;
       uint16       mask;
       uint8        type;
       const char   *name;
} t_opcode;

t_opcode  optab[] = {
    {  0x0000,  0xFFFF,   H|TYPE_H,   "HALT", },    /* Halt # * */
    {  0x0001,  0xFFFF,   H|TYPE_H,   "WAIT", },    /* Wait # * */
    {  0x0002,  0xFFFF,   H|TYPE_H,   "NOP", },     /* Nop # */
    {  0x0003,  0xFC0F,   H|TYPE_G,   "LCS", },     /* Load Control Switches */
    {  0x0004,  0xFC0F,   H|TYPE_G,   "ES", },      /* Extend Sign # */
    {  0x0005,  0xFC0F,   H|TYPE_G,   "RND", },     /* Round Register # */
    {  0x0006,  0xFFFF,   H|TYPE_H,   "BEI", },     /* Block External Interrupts # */
    {  0x0007,  0xFFFF,   H|TYPE_H,   "UEI", },     /* Unblock External Interrupts # */
    {  0x0008,  0xFFFF,   H|TYPE_H,   "EAE", },     /* Enable Arithmetic Exception Trap # */
    {  0x0009,  0xFC0F,   H|TYPE_G,   "RDSTS", },   /* Read CPU Status Word * */
    {  0x000A,  0xFFFF,   H|TYPE_H,   "SIPU", },    /* Signal IPU # */
    {  0x000B,  0xFC0F,   H|TYPE_F,   "RWCS", },    /* Read Writable Control Store # */
    {  0x000C,  0xFC0F,   H|TYPE_F,   "WWCS", },    /* Write Writable Control Store # */
    {  0x000D,  0xFFFF, N|H|TYPE_H,   "SEA", },     /* Set Extended Addressing # NBR Only */
    {  0x000E,  0xFFFF,   H|TYPE_H,   "DAE", },     /* Disable Arithmetic Exception Trap # */
    {  0x000F,  0xFFFF, N|H|TYPE_H,   "CEA", },     /* Clear Extended Addressing # NBR Only */
    {  0x0400,  0xFC0F,   H|TYPE_F,   "ANR", },     /* And Register # */
    {  0x0407,  0xFC0F,   H|TYPE_G,   "SMC", },     /* Shared Memory Control # */
    {  0x040A,  0xFC0F,   H|TYPE_G,   "CMC", },     /* Cache Memory Control # */
    {  0x040B,  0xFC0F,   H|TYPE_G,   "RPSWT", },   /* Read Processor Status Word Two # */
    {  0x0800,  0xFC0F,   H|TYPE_F,   "ORR", },     /* Or Register # */
    {  0x0808,  0xFC0F,   H|TYPE_F,   "ORRM", },    /* Or Register Masked # */
    {  0x0C00,  0xFC0F,   H|TYPE_F,   "EOR", },     /* Exclusive Or Register # */
    {  0x0C08,  0xFC0F,   H|TYPE_F,   "EORM", },    /* Exclusive Or Register Masked # */
    {  0x1000,  0xFC0F,   H|TYPE_F,   "CAR", },     /* Compare Arithmetic Register # */
    {  0x1008,  0xFC0F, B|H|TYPE_F,   "SACZ", },    /* Shift and Count Zeros # BR */
    {  0x1400,  0xFC0F,   H|TYPE_F,   "CMR", },     /* Compare masked with register */
    {  0x1800,  0xFC0C,   H|TYPE_K,   "SBR", },     /* Set Bit in Register # */
    {  0x1804,  0xFC0C, B|H|TYPE_K,   "ZBR", },     /* Zero Bit In register # BR */
    {  0x1808,  0xFC0C, B|H|TYPE_K,   "ABR", },     /* Add Bit In Register # BR */
    {  0x180C,  0xFC0C, B|H|TYPE_K,   "TBR", },     /* Test Bit in Register # BR */
    {  0x1C00,  0xFC0C, N|H|TYPE_K,   "ZBR", },     /* Zero Bit in Register # NBR */ /* CON SRABR */
    {  0x1C00,  0xFC60, B|H|TYPE_I,   "SRABR", },   /* Shift Right Arithmetic # BR */ /* CON ZBM */
    {  0x1C20,  0xFC60, B|H|TYPE_I,   "SRLBR", },   /* Shift Right Logical # BR */
    {  0x1C40,  0xFC60, B|H|TYPE_I,   "SLABR", },   /* Shift Left Arithmetic # BR */
    {  0x1C60,  0xFC60, B|H|TYPE_I,   "SLLBR", },   /* Shift Left Logical # BR */
    {  0x2000,  0xFC0C, N|H|TYPE_K,   "ABR", },     /* Add Bit in Register # NBR */ /* CON SRADBR */
    {  0x2000,  0xFC60, B|H|TYPE_I,   "SRADBR", },  /* Shift Right Arithmetic Double # BR */ /* CON ABR */
    {  0x2020,  0xFC60, B|H|TYPE_I,   "SRLDBR", },  /* Shift Left Logical Double # BR */
    {  0x2040,  0xFC60, B|H|TYPE_I,   "SLADBR", },  /* Shift Right Arithmetic Double # BR */
    {  0x2060,  0xFC60, B|H|TYPE_I,   "SLLDBR", },  /* Shift Left Logical Double # BR */
    {  0x2400,  0xFC0C, N|H|TYPE_K,   "TBR", },     /* Test Bit in Register # NBR */ /* CON SRCBR */
    {  0x2400,  0xFC60, B|H|TYPE_I,   "SRCBR", },   /* Shift Right Circular # BR */ /* CON TBR */
    {  0x2440,  0xFC60, B|H|TYPE_F,   "SLCBR", },   /* Shift Left Circular # BR */
    {  0x2800,  0xFC0F,   H|TYPE_G,   "TRSW", },    /* Transfer GPR to PSD */
    {  0x2802,  0xFC0F, B|H|TYPE_F,   "XCBR", },    /* Exchange Base Registers # BR Only */
    {  0x2804,  0xFC0F, B|H|TYPE_G,   "TCCR", },    /* Transfer CC to GPR # BR Only */
    {  0x2805,  0xFC0F, B|H|TYPE_G,   "TRCC", },    /* Transfer GPR to CC # BR */
    {  0x2808,  0xFF8F, B|H|TYPE_F,   "BSUB", },    /* Branch Subroutine # BR Only */
    {  0x2808,  0xFC0F, B|H|TYPE_F,   "CALL", },    /* Procedure Call # BR Only */
    {  0x280C,  0xFC0F, B|H|TYPE_G,   "TPCBR", },   /* Transfer Program Counter to Base # BR Only */
    {  0x280E,  0xFC7F, B|H|TYPE_G,   "RETURN", },  /* Procedure Return # BR Only */
    {  0x2C00,  0xFC0F,   H|TYPE_F,   "TRR", },     /* Transfer Register to Register # */
    {  0x2C01,  0xFC0F, B|H|TYPE_F,   "TRBR", },    /* Transfer GPR to BR # */
    {  0x2C02,  0xFC0F, B|H|TYPE_F,   "TBRR", },    /* Transfer BR to GPR # BR Only */
    {  0x2C03,  0xFC0F,   H|TYPE_F,   "TRC", },     /* Transfer Register Complement # */
    {  0x2C04,  0xFC0F,   H|TYPE_F,   "TRN", },     /* Transfer Register Negative # */
    {  0x2C05,  0xFC0F,   H|TYPE_F,   "XCR", },     /* Exchange Registers # */
    {  0x2C07,  0xFC0F,   H|TYPE_G,   "LMAP", },    /* Load MAP * */
    {  0x2C08,  0xFC0F,   H|TYPE_F,   "TRRM", },    /* Transfer Register to Register Masked # */
    {  0x2C09,  0xFC0F,   H|TYPE_G,   "SETCPU", },  /* Set CPU Mode # * */
    {  0x2C0A,  0xFC0F,   H|TYPE_F,   "TMAPR", },   /* Transfer MAP to Register # * */
    {  0x2C0B,  0xFC0F,   H|TYPE_F,   "TRCM", },    /* Transfer Register Complement Masked # */
    {  0x2C0C,  0xFC0F,   H|TYPE_F,   "TRNM", },    /* Transfer Register Negative Masked # */
    {  0x2C0D,  0xFC0F,   H|TYPE_F,   "XCRM", },    /* Exchange Registers Masked # */
    {  0x2C0E,  0xFC0F,   H|TYPE_F,   "TRSC", },    /* Transfer Register to Scratchpad # * */
    {  0x2C0F,  0xFC0F,   H|TYPE_F,   "TSCR", },    /* Transfer Scratchpad to Register # * */
    {  0x3000,  0xFC0F, X|H|TYPE_F,   "CALM", },    /* Call Monitor 32/55 # */
    {  0x3400,  0xFC00,   N|TYPE_D,   "LA", },      /* Load Address NBR Note! FW instruction */
    {  0x3800,  0xFC0F,   H|TYPE_F,   "ADR", },     /* Add Register to Register # */
    {  0x3801,  0xFC0F,   H|TYPE_F,   "ADRFW", },   /* Add Floating Point to Register # */
    {  0x3802,  0xFC0F, B|H|TYPE_F,   "MPR", },     /* Multiply Register BR # */
    {  0x3803,  0xFC0F,   H|TYPE_F,   "SURFW", },   /* Subtract Floating Point Register # */
    {  0x3804,  0xFC0F,   H|TYPE_F,   "DVRFW", },   /* Divide Floating Point Register # */
    {  0x3805,  0xFC0F,   H|TYPE_F,   "FIXW", },    /* Fix Floating Point Register # */
    {  0x3806,  0xFC0F,   H|TYPE_F,   "MPRFW", },   /* Multiply Floating Point Register # */
    {  0x3807,  0xFC0F,   H|TYPE_F,   "FLTW", },    /* Float Floating Point Register # */
    {  0x3808,  0xFC0F,   H|TYPE_F,   "ADRM", },    /* Add Register to Register Masked # */
    {  0x3809,  0xFC0F,   H|TYPE_F,   "ADRFD", },   /* Add Floating Point Register to Register # */
    {  0x380A,  0xFC0F, B|H|TYPE_F,   "DVR", },     /* Divide Register by Registier BR # */
    {  0x380B,  0xFC0F,   H|TYPE_F,   "SURFD", },   /* Subtract Floating Point Double # */
    {  0x380C,  0xFC0F,   H|TYPE_F,   "DVRFD", },   /* Divide Floating Point Double # */
    {  0x380D,  0xFC0F,   H|TYPE_F,   "FIXD", },    /* Fix Double Register # */
    {  0x380E,  0xFC0F,   H|TYPE_F,   "MPRFD", },   /* Multiply Double Register # */
    {  0x380F,  0xFC0F,   H|TYPE_F,   "FLTD", },    /* Float Double # */
    {  0x3C00,  0xFC0F,   H|TYPE_F,   "SUR", },     /* Subtract Register to Register # */
    {  0x3C08,  0xFC0F,   H|TYPE_F,   "SURM", },    /* Subtract Register to Register Masked # */
    {  0x4000,  0xFC0F, N|H|TYPE_F,   "MPR", },     /* Multiply Register to Register # NBR */
    {  0x4400,  0xFC0F, N|H|TYPE_F,   "DVR", },     /* Divide Register to Register # NBR */
    {  0x5000,  0xFC08,   B|TYPE_D,   "LABRM", },   /* Load Address BR Mode */
    {  0x5400,  0xFC08,   B|TYPE_A,   "STWBR", },   /* Store Base Register BR Only */
    {  0x5800,  0xFC08,   B|TYPE_A,   "SUABR", },   /* Subtract Base Register BR Only */
    {  0x5808,  0xFC08,   B|TYPE_D,   "LABR", },    /* Load Address Base Register BR Only */
    {  0x5C00,  0xFC08,   B|TYPE_A,   "LWBR", },    /* Load Base Register BR Only */
    {  0x5C08,  0xFF88,   B|TYPE_B,   "BSUBM", },   /* Branch Subroutine Memory BR Only */
    {  0x5C08,  0xFC08,   B|TYPE_B,   "CALLM", },   /* Call Memory BR Only */
    {  0x6000,  0xFC0F, N|H|TYPE_F,   "NOR", },     /* Normalize # NBR Only */
    {  0x6400,  0xFC0F, N|H|TYPE_F,   "NORD", },    /* Normalize Double #  NBR Only */
    {  0x6800,  0xFC0F, N|H|TYPE_F,   "SCZ", },     /* Shift and Count Zeros # */
    {  0x6C00,  0xFC40, N|H|TYPE_I,   "SRA", },     /* Shift Right Arithmetic # NBR */
    {  0x6C40,  0xFC40, N|H|TYPE_I,   "SLA", },     /* Shift Left Arithmetic # NBR */
    {  0x7000,  0xFC40, N|H|TYPE_I,   "SRL", },     /* Shift Right Logical # NBR */
    {  0x7040,  0xFC40, N|H|TYPE_I,   "SLL", },     /* Shift Left Logical # NBR */
    {  0x7400,  0xFC40, N|H|TYPE_I,   "SRC", },     /* Shift Right Circular # NBR */
    {  0x7440,  0xFC40, N|H|TYPE_I,   "SLC", },     /* Shift Left Circular # NBR */
    {  0x7800,  0xFC40, N|H|TYPE_I,   "SRAD", },    /* Shift Right Arithmetic Double # NBR */
    {  0x7840,  0xFC40, N|H|TYPE_I,   "SLAD", },    /* Shift Left Arithmetic Double # NBR */
    {  0x7C00,  0xFC40, N|H|TYPE_I,   "SRLD", },    /* Shift Right Logical Double # NBR */
    {  0x7C40,  0xFC40, N|H|TYPE_I,   "SLLD", },    /* Shift Left Logical Double # NBR */
    {  0x8000,  0xFC08,   TYPE_A,     "LEAR", },    /* Load Effective Address Real * */
    {  0x8400,  0xFC00,   TYPE_A,     "ANM", },     /* And Memory B,H,W,D */
    {  0x8800,  0xFC00,   TYPE_A,     "ORM", },     /* Or Memory B,H,W,D */
    {  0x8C00,  0xFC00,   TYPE_A,     "EOM", },     /* Exclusive Or Memory */
    {  0x9000,  0xFC00,   TYPE_A,     "CAM", },     /* Compare Arithmetic with Memory */
    {  0x9400,  0xFC00,   TYPE_A,     "CMM", },     /* Compare Masked with Memory */
    {  0x9800,  0xFC00,   TYPE_D,     "SBM", },     /* Set Bit in Memory */
    {  0x9C00,  0xFC00,   TYPE_D,     "ZBM", },     /* Zero Bit in Memory */
    {  0xA000,  0xFC00,   TYPE_D,     "ABM", },     /* Add Bit in Memory */
    {  0xA400,  0xFC00,   TYPE_D,     "TBM", },     /* Test Bit in Memory */
    {  0xA800,  0xFC00,   TYPE_B,     "EXM", },     /* Execute Memory */
    {  0xAC00,  0xFC00,   TYPE_A,     "L", },       /* Load B,H,W,D */
    {  0xB000,  0xFC00,   TYPE_A,     "LM", },      /* Load Masked B,H,W,D */
    {  0xB400,  0xFC00,   TYPE_A,     "LN", },      /* Load Negative B,H,W,D */
    {  0xB800,  0xFC00,   TYPE_A,     "ADM", },     /* Add Memory B,H,W,D */
    {  0xBC00,  0xFC00,   TYPE_A,     "SUM", },     /* Subtract Memory B,H,W,D */
    {  0xC000,  0xFC00,   TYPE_A,     "MPM", },     /* Multiply Memory B,H,W,D */
    {  0xC400,  0xFC00,   TYPE_A,     "DVM", },     /* Divide Memory B,H,W,D */
    {  0xC800,  0xFC0F,   TYPE_C,     "LI", },      /* Load Immediate */
    {  0xC801,  0xFC0F,   TYPE_C,     "ADI", },     /* Add Immediate */
    {  0xC802,  0xFC0F,   TYPE_C,     "SUI", },     /* Subtract Immediate */
    {  0xC803,  0xFC0F,   TYPE_C,     "MPI", },     /* Multiply Immediate */
    {  0xC804,  0xFC0F,   TYPE_C,     "DVI", },     /* Divide Immediate */
    {  0xC805,  0xFC0F,   TYPE_C,     "CI", },      /* Compare Immediate */
    {  0xC806,  0xFC0F,   TYPE_N,     "SVC", },     /* Supervisor Call */
    {  0xC807,  0xFC0F,   TYPE_G,     "EXR", },     /* Execute Register/ Right */
    {  0xC808,  0xFC0F, X|TYPE_A,     "SEM", },     /* Store External Map 32/7X * */
    {  0xC809,  0xFC0F, X|TYPE_A,     "LEM", },     /* Load External Map 32/7X * */
    {  0xC80A,  0xFC0F, X|TYPE_A,     "CEMA", },    /* Convert External Map 32/7X * */
    {  0xCC00,  0xFC08,   TYPE_A,     "LF", },      /* Load File */
    {  0xCC08,  0xFC08,   TYPE_A,     "LFBR", },    /* Load Base File */
    {  0xD000,  0xFC00, N|TYPE_A,     "LEA", },     /* Load Effective Address # NBR */
    {  0xD400,  0xFC00,   TYPE_A,     "ST", },      /* Store B,H,W,D */
    {  0xD800,  0xFC00,   TYPE_A,     "STM", },     /* Store Masked B,H,W,D */
    {  0xDC00,  0xFC08,   TYPE_A,     "STF", },     /* Store File */
    {  0xDC08,  0xFC08,   TYPE_A,     "STFBR", },   /* Store Base File */
    {  0xE000,  0xFC08,   TYPE_A,     "SUF", },     /* Subtract Floating Memory D,W */
    {  0xE008,  0xFC08,   TYPE_A,     "ADF", },     /* Add Floating Memory D,W */
    {  0xE400,  0xFC08,   TYPE_A,     "DVF", },     /* Divide Floating Memory D,W */
    {  0xE408,  0xFC08,   TYPE_A,     "MPF", },     /* Multiply Floating Memory D,W */
    {  0xE800,  0xFC00,   TYPE_A,     "ARM", },     /* Add Register to Memory B,H,W,D */
    {  0xEC00,  0xFF80,   TYPE_B,     "BU", },      /* Branch Unconditional */
    {  0xEC00,  0xFF80,   TYPE_A,     "BCT", },     /* Branch Condition True */
    {  0xEC80,  0xFF80,   TYPE_B,     "BS", },      /* Branch Condition True CC1 = 1 */
    {  0xED00,  0xFF80,   TYPE_B,     "BGT", },     /* Branch Condition True CC2 = 1 */
    {  0xED80,  0xFF80,   TYPE_B,     "BLT", },     /* Branch Condition True CC3 = 1 */
    {  0xEE00,  0xFF80,   TYPE_B,     "BEQ", },     /* Branch Condition True CC4 = 1 */
    {  0xEE80,  0xFF80,   TYPE_B,     "BGE", },     /* Branch Condition True CC2|CC4 = 1 */
    {  0xEF00,  0xFF80,   TYPE_B,     "BLE", },     /* Branch Condition True CC3|CC4 = 1 */
    {  0xEF80,  0xFF80,   TYPE_B,     "BANY", },    /* Branch Condition True CC1|CC2|CC3|CC4 */
    {  0xF000,  0XFF80,   TYPE_B,     "BFT", },     /* Branch Function True */
    {  0xF000,  0xFF80,   TYPE_A,     "BCF", },     /* Branch Condition False */
    {  0xF080,  0xFF80,   TYPE_B,     "BNS", },     /* Branch Condition False CC1 = 0 */
    {  0xF100,  0xFF80,   TYPE_B,     "BNP", },     /* Branch Condition False CC2 = 0 */
    {  0xF180,  0xFF80,   TYPE_B,     "BNN", },     /* Branch Condition False CC3 = 0 */
    {  0xF200,  0xFF80,   TYPE_B,     "BNE", },     /* Branch Condition False CC4 = 0 */
    {  0xF280,  0xFF80,   TYPE_B,     "BCF 5,", },  /* Branch Condition False CC2|CC4 = 0 */
    {  0xF300,  0xFF80,   TYPE_B,     "BCF 6,", },  /* Branch Condition False CC3|CC4 = 0 */
    {  0xF380,  0xFF80,   TYPE_B,     "BAZ", },     /* Branch Condition False CC1|CC2|CC3|CC4=0*/
    {  0xF400,  0xFC70,   TYPE_D,     "BIB", },     /* Branch after Incrementing Byte */
    {  0xF420,  0xFC70,   TYPE_D,     "BIH", },     /* Branch after Incrementing Half */
    {  0xF440,  0xFC70,   TYPE_D,     "BIW", },     /* Branch after Incrementing Word */
    {  0xF460,  0xFC70,   TYPE_D,     "BID", },     /* Branch after Incrementing Double */
    {  0xF800,  0xFF80,   TYPE_E,     "ZM", },      /* Zero Memory B,H,W,D */
    {  0xF880,  0xFF80,   TYPE_B,     "BL", },      /* Branch and Link */
    {  0xF900,  0xFCC0, X|TYPE_B,     "BRI", },     /* Branch and Reset Interrupt 32/55 * */
    {  0xF980,  0xFF80,   TYPE_B,     "LPSD", },    /* Load Program Status Double * */
    {  0xFA08,  0xFC00,   TYPE_B,     "JWCS", },    /* Jump to Writable Control Store * */
    {  0xFA80,  0xFF80,   TYPE_B,     "LPSDCM", },  /* LPSD and Change Map * */
    {  0xFB00,  0xFCC0, X|TYPE_A,     "TRP", },     /* Transfer Register to Protect Register 32/7X */
    {  0xFB80,  0xFCC0, X|TYPE_A,     "TPR", },     /* Transfer Protect Register to Register 32/7X */
    {  0xFC00,  0xFC07,   TYPE_L,     "EI", },      /* Enable Interrupt */
    {  0xFC01,  0xFC07,   TYPE_L,     "DI", },      /* Disable Interrupt */
    {  0xFC02,  0xFC07,   TYPE_L,     "RI", },      /* Request Interrupt */
    {  0xFC03,  0xFC07,   TYPE_L,     "AI", },      /* Activate Interrupt */
    {  0xFC04,  0xFC07,   TYPE_L,     "DAI", },     /* Deactivate Interrupt */
    {  0xFC05,  0xFC07,   TYPE_M,     "TD", },      /* Test Device */
    {  0xFC06,  0xFC07,   TYPE_M,     "CD", },      /* Command Device */
    {  0xFC17,  0xFC7F,   TYPE_C,     "SIO", },     /* Start I/O */
    {  0xFC1F,  0xFC7F,   TYPE_C,     "TIO", },     /* Test I/O */
    {  0xFC27,  0xFC7F,   TYPE_C,     "STPIO", },   /* Stop I/O */
    {  0xFC2F,  0xFC7F,   TYPE_C,     "RSCHNL", },  /* Reset Channel */
    {  0xFC37,  0xFC7F,   TYPE_C,     "HIO", },     /* Halt I/O */
    {  0xFC3F,  0xFC7F,   TYPE_C,     "GRIO", },    /* Grab Controller */
    {  0xFC47,  0xFC7F,   TYPE_C,     "RSCTL", },   /* Reset Controller */
    {  0xFC4F,  0xFC7F,   TYPE_C,     "ECWCS", },   /* Enable Channel WCS Load */
    {  0xFC5F,  0xFC7F,   TYPE_C,     "WCWCS", },   /* Write Channel WCS */
    {  0xFC67,  0xFC7F,   TYPE_C,     "ECI", },     /* Enable Channel Interrupt */
    {  0xFC6F,  0xFC7F,   TYPE_C,     "DCI", },     /* Disable Channel Interrupt */
    {  0xFC77,  0xFC7F,   TYPE_C,     "ACI", },     /* Activate Channel Interrupt */
    {  0xFC7F,  0xFC7F,   TYPE_C,     "DACI", },    /* Deactivate Channel Interrupt */
};

/* Instruction decode printing routine
   Inputs:
    *of       =       output stream
    val       =       16/32 bit instruction to print left justified
    sw        =       mode switches, 'M'=base mode, 'N'=nonbase mode
*/
const char *fc_type = "WHDHBBBB";   /* F & C bit values */

int fprint_inst(FILE *of, uint32 val, int32 sw)
{
    uint16   inst = (val >> 16) & 0xFFFF;
    int      i;
    int      mode = 0;                      /* assume non base mode instructions */
    t_opcode *tab;

    if ((PSD[0] & 0x02000000) || (sw & SWMASK('M'))) /* bit 6 is base mode */
        mode = 1;
    /* loop through the instruction table for an opcode match and get the type */ 
    for (tab = optab; tab->name != NULL; tab++) {
        if (tab->opbase == (inst & tab->mask)) {
            if (mode && (tab->type & (X | N)))
                continue;                   /* non basemode instruction in base mode, skip */
            if (!mode && (tab->type & B))
                continue;                   /* basemode instruction in nonbase mode, skip */

            /* TODO?  Maybe want to make sure MODEL is 32/7X for X type instructions */

            /* match found */
            fputs(tab->name, of);           /* output the base opcode */

            /* process the other fields of the instruction */
            switch(tab->type & 0xF) {
            /* memory reference instruction */
            case TYPE_A:                    /* r,[*]o[,x] or r,o[(b)][,x] */
            /* zero memory instruction */
            case TYPE_E:                    /* [*]o[,x] or o[(b)][,x] */
                /* append B, H, W, D to base instruction using F & C bits */
                i = (val & 3) | ((inst >> 1) & 04);
                if (((inst&0xfc00) == 0xe000) ||
                    ((inst&0xfc00) == 0xe400))
                    i &= ~4;                /* remove f bit from fpt instr */
                if (((inst&0xfc00) != 0xdc00) &&
                    ((inst&0xfc00) != 0xd000) &&
                    ((inst&0xfc00) != 0x5400) &&
                    ((inst&0xfc00) != 0x5800) &&
                    ((inst&0xfc00) != 0x5c00) &&
                    ((inst&0xfc00) != 0xcc00) &&
                    ((inst&0xfc00) != 0x8000))
                    fputc(fc_type[i], of);
                /* Fall through */

            /* BIx instructions or bit in memory reference instructions */
            case TYPE_D:                    /* r,[*]o[,x] or r,o[(b)],[,x] */
                if ((tab->type & 0xF) != TYPE_E) {
                    fputc(' ', of);
//                  fputc('R', of);
                    /* output the reg or bit number */
                    fputc('0'+((inst>>7) & 07), of);
                    fputc(',', of);
                }
                /* Fall through */

            /* branch instruction */
            case TYPE_B:                    /* [*]o[,x] or o[(b)],[,x] */
                if (((tab->type & 0xf) != TYPE_A) && ((tab->type & 0xf) != TYPE_D))
                    fputc(' ', of);
                if (mode) {
                    /* base reg mode */
                    fprint_val(of, val&0xffff, 16, 16, PV_LEFT);    /* output 16 bit offset */
                    if (inst & 07) {
                        fputc('(', of);
//                      fputc('B', of);
                        fputc(('0'+(inst & 07)), of);   /* output the base reg number */
                        fputc(')', of);
                    }
                    if (inst & 0x70) {
                        fputc(',', of);
//                      fputc('R', of);
                        fputc(('0'+((inst >> 4) & 07)), of);    /* output the index reg number */
                    }
                } else {
                    /* nonbase reg mode */
                    if (inst & 0x10)
                        fputc('*', of);     /* show indirection */
                    fprint_val(of, val&0x7ffff, 16, 19, PV_LEFT);   /* 19 bit offset */
                    if (inst & 0x60) {
                        fputc(',', of);     /* register coming */
//                      fputc('R', of);
                        if (tab->type != TYPE_D)
                            fputc('0'+((inst & 0x60) >> 5), of);    /* output the index reg number */
                        else { 
                            if ((inst & 0xfc00) != 0xf400)
                                fputc('0'+((inst & 0x60) >> 5), of);    /* output the index reg number */
                        }
                    }
                }
                break;

            /* immediate or XIO instructions */
            case TYPE_C:                    /* r,v */
                fputc(' ', of);
//              fputc('R', of);
                fputc('0'+((inst>>7) & 07), of);    /* index reg number */
                fputc(',', of);
                fprint_val(of, val&0xffff, 16, 16, PV_LEFT);    /* 16 bit imm val or chan/suba */
                break;

            /* reg - reg instructions */
            case TYPE_F:                    /* rs,rd */
                fputc(' ', of);
//              fputc('R', of);
                fputc('0'+((inst>>4) & 07), of);    /* src reg */
                fputc(',', of);
//              fputc('R', of);
                fputc('0'+((inst>>7) & 07), of);    /* dest reg */
                break;

            /* single reg instructions */
            case TYPE_G:                    /* op r */
                fputc(' ', of);
//              fputc('R', of);
                fputc('0'+((inst>>7) & 07), of);    /* output src/dest reg  num */
                break;

            /* just output the instruction */
            case TYPE_H:                    /* empty */
                break;

            /* reg and bit shift cnt */
            case TYPE_I:                    /* r,b */
                fputc(' ', of);
//              fputc('R', of);
                fputc('0'+((inst>>7) & 07), of);    /* reg number */
                fputc(',', of);
                fprint_val(of, inst&0x1f, 10, 5, PV_LEFT);  /* 5 bit shift count */
                break; 

            /* register bit operations */
            case TYPE_K:                    /* r,rb */
                fputc(' ', of);
//              fputc('R', of);
                fputc('0'+((inst>>4) & 07), of);    /* register number */
                fputc(',', of);
                i = ((inst & 3) << 3) | ((inst >> 7) & 07);
                fprint_val(of, i, 10, 5, PV_LEFT);  /* reg bit number to operate on */
                break; 

            /* interrupt control instructions */
            case TYPE_L:                    /* i */
                fputc(' ', of);
                fprint_val(of, (inst>>3)&0x7f, 16, 7, PV_RZRO); /* output 7 bit priority level value */
                break;

            /* CD/TD Class E I/O instructions */
            case TYPE_M:                    /* i,v */
                fputc(' ', of);
                fprint_val(of, (inst>>3)&0x7f, 16, 7, PV_RZRO); /* output 7 bit device address */
                fputc(',', of);
                fprint_val(of, (val&0xffff), 16, 16, PV_RZRO);  /* output 16 bit command code */
                break;

            /* SVC  instructions */
            case TYPE_N:                    /* i,v */
                fputc(' ', of);
                fprint_val(of, (val>>12)&0xf, 16, 4, PV_RZRO);  /* output 4 bit svc number */
                fputc(',', of);
                fprint_val(of, (val & 0xFFF), 16, 12, PV_LEFT); /* output 12 bit command code */
                break;

            default:
                /* FIXME - return error code here? */
//              /* fputs(" unknown type", of);  /* output error message */
//              return SCPE_ARG;            /* unknown type */
                break;
            }
            /* return the size of the instruction */
            return (tab->type & H) ? 2 : 4;
        }
    }
    /* FIXME - should we just return error here? or dump as hex data? */
    /* we get here if opcode not found, print data value */
    if (mode)
        fputs(" Binvld ", of);              /* output basemode error message */
    else
        fputs(" Ninvld ", of);              /* output non-basmode error message */
    fprint_val(of, val, 16, 32, PV_RZRO);   /* output unknown 32 bit instruction code */
    return 4;                               /* show as full word size */
}

/* Symbolic decode

   Inputs:
       *of      =       output stream
       addr     =       current PC
       *val     =       pointer to values
       *uptr    =       pointer to unit
       sw       =       switches
   Outputs:
       return   =       status code
*/
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
{
    int         i;
    int         l = 4;                      /* default to full words */
    int         rdx = 16;                   /* default radex is hex */
    uint32      num;

    if (sw & SIM_SW_STOP) {                 /* special processing for step */
        if (PSD[0] & 0x02000000) {          /* bit 6 is base mode */
            sw |= SWMASK('M');              /* display basemode */
            sw &= ~SWMASK('N');             /* no non-based display */
        } else {
            sw |= SWMASK('N');              /* display non-basemode */
            sw &= ~SWMASK('M');             /* no basemode display */
        }
    }
    if (addr & 0x02)
        l = 2;
    /* determine base for number output */
    if (sw & SWMASK ('D')) 
        rdx = 10;                           /* decimal */
    else
    if (sw & SWMASK ('O')) 
        rdx = 8;                            /* octal */
    else
    if (sw & SWMASK ('H')) 
        rdx = 16;                           /* hex */

    if (sw & SWMASK ('M')) {                /* machine base mode? */
        sw &= ~ SWMASK('B');                /* Can't do B and M at same time */
        sw &= ~ SWMASK('C');                /* Can't do C and M at same time */
        if (addr & 0x02)
            l = 2;
        else
            l = 4;
    } else
    if (sw & SWMASK('F')) {
        l = 4;                              /* words are 4 bytes */
    } else
    if (sw & SWMASK('W')) {
        l = 2;                              /* halfwords are 2 bytes */
    } else
    if (sw & SWMASK('B')) { 
        l = 1;                              /* bytes */
    }

    if (sw & SWMASK ('C')) {
        fputc('\'', of);                    /* opening apostorphe */
        for (i = 0; i < l; i++) {
            int ch = val[i] & 0xff;         /* get the char */
            if (ch >= 0x20 && ch <= 0x7f)   /* see if printable */
                fprintf(of, "%c", ch);      /* output the ascii char */
            else
                fputc('_', of);             /* use underscore for unprintable char */
        }
        fputc('\'', of);                    /* closing apostorphe */
    } else
    /* go print the symbolic instruction for base or nonbase mode */
    if (sw & (SWMASK('M') | SWMASK('N'))) { 
        num = 0;
        for (i = 0; i < l && i < 4; i++) {
            num |= (uint32)val[i] << ((l-i-1) * 8); /* collect 8-32 bit data value to print */
        }
        if (addr & 0x02)
            num <<= 16;                     /* use rt hw */
        l = fprint_inst(of, num, sw);       /* go print the instruction */
        if (((addr & 2) == 0) && (l == 2)) {    /* did we execute a left halfword instruction */
            fprintf(of, "; ");
            l = fprint_inst(of, num<<16, sw);   /* go print right halfword instruction */
            l = 4;                          /* next word address */
        }
    } else {
        /* print the numeric value of the memory data */
        num = 0;
        for (i = 0; i < l && i < 4; i++) 
            num |= (uint32)val[i] << ((l-i-1) * 8); /* collect 8-32 bit data value to print */
        fprint_val(of, num, rdx, l*8, PV_RZRO); /* print it in requested radix */
    }
    return -(l-1);                          /* will be negative if we did anything */
}

/* 
 * Collect offset in radix.
 */
t_stat get_off (CONST char *cptr, CONST char **tptr, uint32 radix, t_value *val, char *m)
{
    t_stat r = SCPE_OK;                     /* assume OK return */

    *m = 0;                                 /* left parend found flag if set */
    *val = (uint32)strtotv(cptr, tptr, radix);  /* convert to value */
    if (cptr == *tptr)
        r = SCPE_ARG;                       /* no argument found error */
    else {
        cptr = *tptr;                       /* where to start looking */
        while (sim_isspace(*cptr))
            cptr++;                         /* skip any spaces */
        if (*cptr++ == '(') {
           *m = 1;                          /* show we found a left parend */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any spaces */
        }
        *tptr = cptr;                       /* return next char pointer */
    }
    return r;                               /* return status */
}

/* 
 * Collect immediate in radix.
 */
t_stat get_imm (CONST char *cptr, CONST char **tptr, uint32 radix, t_value *val)
{
    t_stat r;

    r = SCPE_OK;
    *val = (uint32)strtotv (cptr, tptr, radix);
    if ((cptr == *tptr) || (*val > 0xffff))
        r = SCPE_ARG;
    else {
        cptr = *tptr;
       while (sim_isspace (*cptr)) cptr++;
     *tptr = cptr;
    }
    return r;
}

/* Symbolic input
   Inputs:
       *cptr      =       pointer to input string
       addr       =       current PC
       uptr       =       pointer to unit
       *val       =       pointer to output values
       sw         =       switches
   Outputs:
       status     =       error status
*/

t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
    int        i;
    int        x;
    int        l = 4;                       /* default to full words */
    int        rdx = 16;                    /* default radex is hex */
    char       mod = 0;
    t_opcode   *tab;
    t_stat     r;
    uint32     num;
    uint32     max[5] = {0, 0xff, 0xffff, 0, 0xffffffff};
    CONST char *tptr;
    char       gbuf[CBUFSIZE];

    /* determine base for numbers */
    if (sw & SWMASK ('D')) 
        rdx = 10;                           /* decimal */
    else
    if (sw & SWMASK ('O')) 
        rdx = 8;                            /* octal */
    else
    if (sw & SWMASK ('H')) 
        rdx = 16;                           /* hex */

    /* set instruction size */
    if (sw & SWMASK('F')) {
        l = 4;
    } else
    if (sw & SWMASK('W')) {
        l = 2;
    }

    /* process a character string */
    if (sw & SWMASK ('C')) {
        cptr = get_glyph_quoted(cptr, gbuf, 0); /* Get string */
        for (i = 0; gbuf[i] != 0; i++) {
            val[i] = gbuf[i];               /* copy in the string */
        }
        return -(i - 1);
    }

    /* see if we are processing a nonbase instruction */
    if (sw & SWMASK ('N')) {
        /* process nonbased instruction */
        cptr = get_glyph(cptr, gbuf, 0);    /* Get uppercase opcode */
        l = strlen(gbuf);                   /* opcode length */
        /* try to find the opcode in the table */
        for (tab = optab; tab->name != NULL; tab++) {
            i = tab->type & 0xf;            /* get the instruction type */
            /* check for memory reference instruction */
            if (i == TYPE_A || i == TYPE_E) {
                /* test for base opcode name without B, H, W, D applied */
                if (sim_strncasecmp(tab->name, gbuf, l - 1) == 0)
                    break;                  /* found */
            } else
            /* test the full opcode name */
            if (sim_strcasecmp(tab->name, gbuf) == 0) 
                break;                      /* found */
        }
        if (tab->name == NULL)              /* see if anything found */
            return SCPE_ARG;                /* no, return invalid argument error */
        num = tab->opbase<<16;              /* get the base opcode value */

        /* process each instruction type */
        switch(i) {
        /* mem ref instruction */
        case TYPE_A:                        /* c r,[*]o[,x] */
        /* zero memory instruction */
        case TYPE_E:                        /* c [*]o[,x] */
            switch(gbuf[l]) {
            case 'B': num |= 0x80000; break;    /* byte, set F bit */
            case 'H': num |= 0x00001; break;    /* halfword */
            case 'W': num |= 0x00000; break;    /* word */
            case 'D': num |= 0x00002; break;    /* doubleword */
            default:
                return SCPE_ARG;            /* base op suffix error */
            }
            /* Fall through */

        /* BIx instructions or memory reference */
        case TYPE_D:                        /* r,[*]o[,x] */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip leading blanks */
            if (i != TYPE_E) {
                /* get reg number except for zero memory instruction */
                if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */
                    x = *cptr++ - '0';      /* get the reg# */
                    while (sim_isspace(*cptr))
                        cptr++;             /* skip any blanks */
                    if (*cptr++ != ',')     /* check for required comma */
                        return SCPE_ARG;    /* anything else is an argument error */
                    num |= x << 23;         /* position reg number in instruction */
                } else 
                    return SCPE_ARG;        /* invalid reg number is an argument error */
            }
            /* Fall through */

        /* branch instruction */
        case TYPE_B:                        /* [*]o[,x] */
        if (*cptr == '*') {                 /* test for indirection */
            num |= 0x100000;                /* set indirect flag */
            cptr++;                         /* skip past the '*' */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
        }
        if ((r = get_off(cptr, &tptr, 16, val, &mod)))  /* get operand address */
            return r;                       /* argument error if a problem */
        cptr = tptr;                        /* set pointer to returned next char pointer */
        if (*val > 0x7FFFF)                 /* 19 bit address max */
            return SCPE_ARG;                /* argument error */
        num |= *val;                        /* or our address into instruction */
        if (mod) {
            return SCPE_ARG;                /* if a '(' found, that is an arg error */
        }
        if (*cptr++ == ',') {               /* test for optional index reg number */
            if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */
                x = *cptr++ - '0';          /* get reg number */
                num |= x << 20;             /* position and put into instruction */
            } else 
                return SCPE_ARG;            /* reg# not 0-7, so arg error */
        }
        break;

        /* immediate or XIO instruction */
        case TYPE_C:                        /* r,v */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip spaces */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* get reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* next char need to be a comma */
                    return SCPE_ARG;        /* it's not, so arg error */
                num |= x << 23;             /* position and put into instruction */
            } else 
                return SCPE_ARG;            /* invalid reg#, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 16 bit immediate value */
                return r;                   /* return error from conversion */
            num |= *val;                    /* or in the 16 bit value */
            break;

        /* reg-reg instructions */
        case TYPE_F:                        /* r,r */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* test for required ',' */
                    return SCPE_ARG;        /* it's not there, so error */
                num |= x << 23;             /* insert first reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip more spaces */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip any spaces */
                num |= x << 20;             /* insert 2nd reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            break;

        /* single reg instructions */
        case TYPE_G:                        /* r */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                num |= x << 23;             /* insert first reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            break;

        /* opcode only instructions */
        case TYPE_H:                        /* empty */
            break;

        /* reg and bit shift instructions */
        case TYPE_I:                        /* r,b */
            while (sim_isspace (*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* test for required ',' */
                    return SCPE_ARG;        /* it's not there, so error */
                num |= (x << 23);           /* insert first reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, 10, val)))    /* get 5 bit shift value */
                return r;                   /* return error from conversion */
            if (*val > 0x1f)                /* 5 bit max count */
                return SCPE_ARG;            /* invalid shift count */
            num |= (*val << 16);            /* or in the 5 bit value */
            break; 

        /* register bit operations */
        case TYPE_K:                        /* r,rb */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* test for required ',' */
                    return SCPE_ARG;        /* it's not there, so error */
                num |= (x << 20);           /* insert reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, 10, val)))    /* get 5 bit bit number */
                return r;                   /* return error from conversion */
            if (*val > 0x1f)                /* 5 bit max count */
                return SCPE_ARG;            /* invalid bit count */
            x = *val / 8;                   /* get 2 bit byte number */
            num |= (x & 3) << 16;           /* insert 2 bit byte code into instruction */
            x = *val % 8;                   /* get bit in byte value */
            num |= (x & 7) << 23;           /* or in the bit value */
            break; 

        /* interrupt control instructions */
        case TYPE_L:                        /* i */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 7 bit bit number */
                return r;                   /* return error from conversion */
            if (*val > 0x7f)                /* 7 bit max count */
                return SCPE_ARG;            /* invalid value */
            num |= (*val & 0x7f) << 19;     /* or in the interrupt level */
            break;

        /* CD/TD Class E I/O instructions */
        case TYPE_M:                        /* d,v */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 7 bit bit number */
                return r;                   /* return error from conversion */
            if (*val > 0x7f)                /* 7 bit max count */
                return SCPE_ARG;            /* invalid value */
            num |= (*val & 0x7f) << 19;     /* or in the device address */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 16 bit command code */
                return r;                   /* return error from conversion */
            num |= *val;                    /* or in the 16 bit value */
            break;
        }
        return (tab->type & H) ? 2 : 4;     /* done with nonbased instructions */
    }

    /* see if we are processing a base mode instruction */
    if (sw & SWMASK ('M')) {                /* base mode? */
        /* process base mode instruction */
        cptr = get_glyph(cptr, gbuf, 0);    /* Get uppercase opcode */
        l = strlen(gbuf);                   /* save the num of char in opcode */
        /* loop through the instruction table for an opcode match and get the type */ 
        for (tab = optab; tab->name != NULL; tab++) {
            i = tab->type & 0xf;            /* get the type */
            /* check for memory reference instruction */
            if (i == TYPE_A || i == TYPE_E) {
                /* test for base opcode name without B, H, W, D applied */
                if (sim_strncasecmp(tab->name, gbuf, l - 1) == 0)
                    break;                  /* found */
            } else
            /* test the full opcode name */
            if (sim_strcasecmp(tab->name, gbuf) == 0) 
                break;                      /* found */
        }
        if (tab->name == NULL)              /* see if anything found */
            return SCPE_ARG;                /* no, return invalid argument error */
        num = tab->opbase<<16;              /* get the base opcode value */

        /* process each instruction type */
        switch(i) {
        /* mem ref instruction */
        case TYPE_A:                        /* c r,o[(b)][,x] */
        /* zero memory instruction */
        case TYPE_E:                        /* c o[(b)][,x] */
        switch(gbuf[l]) {
            case 'B': num |= 0x80000; break;    /* byte, set F bit */
            case 'H': num |= 0x00001; break;    /* halfword */
            case 'W': num |= 0x00000; break;    /* word */
            case 'D': num |= 0x00002; break;    /* doubleword */
            default:
                return SCPE_ARG;            /* base op suffix error */
        }
        /* Fall through */

        /* BIx instructions or memory reference */
        case TYPE_D:                        /* r,o[(b)],[,x] */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip leading blanks */
            if (i != TYPE_E) {
                /* get reg number except for zero memory instruction */
                if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */
                    x = *cptr++ - '0';      /* get the reg# */
                    while (sim_isspace(*cptr))
                        cptr++;             /* skip any blanks */
                    if (*cptr++ != ',')     /* check for required comma */
                        return SCPE_ARG;    /* anything else is an argument error */
                    num |= x << 23;         /* position reg number in instruction */
                } else 
                    return SCPE_ARG;        /* invalid reg number is an argument error */
            }
          /* Fall through */

        /* branch instruction */
        case TYPE_B:                        /* o[(b)],[,x] */
            if ((r = get_off(cptr, &tptr, 16, val, &mod)))  /* get offset */
                return r;                   /* argument error if a problem */
            cptr = tptr;                    /* set pointer to returned next char pointer */
            if (*val > 0xFFFF)              /* 16 bit offset max */
                return SCPE_ARG;            /* argument error */
            num |= *val;                    /* or offset into instruction */
            if (mod) {                      /* see if '(' found in input */
                if (*cptr >= '0' || *cptr <= '7') { /* base reg# 0-7 */
                    x = *cptr++ - '0';      /* get reg number */
                    while (sim_isspace(*cptr))
                        cptr++;             /* skip any spaces */
                    if (*cptr++ != ')')     /* test for closing right parend */
                        return SCPE_ARG;    /* arg error if not found */
                   num |= x << 16;          /* put base reg number into instruction */
                } else
                    return SCPE_ARG;        /* no '(' found, so arg error */
            }
            if (*cptr++ == ',') {           /* test for optional index reg number */
                if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */
                    x = *cptr++ - '0';      /* get reg number */
                    num |= x << 20;         /* position and put into instruction */
                } else 
                    return SCPE_ARG;        /* reg# not 0-7, so arg error */
            }
            break;

        /* immediate or XIO instruction */
        case TYPE_C:                        /* r,v */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip spaces */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* get reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* next char need to be a comma */
                    return SCPE_ARG;        /* it's not, so arg error */
                num |= x << 23;             /* position and put into instruction */
            } else 
                return SCPE_ARG;            /* invalid reg#, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 16 bit immediate value */
                return r;                   /* return error from conversion */
            num |= *val;                    /* or in the 16 bit value */
            break;

        /* reg-reg instructions */
        case TYPE_F:                        /* r,r */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* test for required ',' */
                    return SCPE_ARG;        /* it's not there, so error */
                num |= x << 23;             /* insert first reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip more spaces */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip any spaces */
                num |= x << 20;             /* insert 2nd reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            break;

        /* single reg instructions */
        case TYPE_G:                        /* r */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                num |= x << 23;             /* insert first reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            break;

        /* opcode only instructions */
        case TYPE_H:                        /* empty */
            break;

        /* reg and bit shift instructions */
        case TYPE_I:                        /* r,b */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* test for required ',' */
                    return SCPE_ARG;        /* it's not there, so error */
                num |= (x << 23);           /* insert first reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, 10, val)))    /* get 5 bit shift value */
                return r;                   /* return error from conversion */
            if (*val > 0x1f)                /* 5 bit max count */
                return SCPE_ARG;            /* invalid shift count */
            num |= (*val << 16);            /* or in the 5 bit value */
            break; 

        /* register bit operations */
        case TYPE_K:                        /* r,rb */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip blanks */
            if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */
                x = *cptr++ - '0';          /* calc reg# */
                while (sim_isspace(*cptr))
                    cptr++;                 /* skip spaces */
                if (*cptr++ != ',')         /* test for required ',' */
                    return SCPE_ARG;        /* it's not there, so error */
                num |= (x << 20);           /* insert reg# into instruction */
            } else 
                return SCPE_ARG;            /* reg# invalid, so arg error */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, 10, val)))    /* get 5 bit bit number */
                return r;                   /* return error from conversion */
            if (*val > 0x1f)                /* 5 bit max count */
                return SCPE_ARG;            /* invalid bit count */
            x = *val / 8;                   /* get 2 bit byte number */
            num |= (x & 3) << 16;           /* insert 2 bit byte code into instruction */
            x = *val % 8;                   /* get bit in byte value */
            num |= (x & 7) << 23;           /* or in the bit value */
            break; 

        /* interrupt control instructions */
        case TYPE_L:                        /* i */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 7 bit bit number */
                return r;                   /* return error from conversion */
            if (*val > 0x7f)                /* 7 bit max count */
                return SCPE_ARG;            /* invalid value */
            num |= (*val & 0x7f) << 19;     /* or in the interrupt level */
            break;

        /* CD/TD Class E I/O instructions */
        case TYPE_M:                        /* d,v */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 7 bit bit number */
                return r;                   /* return error from conversion */
            if (*val > 0x7f)                /* 7 bit max count */
                return SCPE_ARG;            /* invalid value */
            num |= (*val & 0x7f) << 19;     /* or in the device address */
            while (sim_isspace(*cptr))
                cptr++;                     /* skip any blanks */
            if ((r = get_imm(cptr, &tptr, rdx, val)))   /* get 16 bit command code */
                return r;                   /* return error from conversion */
            num |= *val;                    /* or in the 16 bit value */
            break;
        }
        return (tab->type & H) ? 2 : 4;     /* done with base mode insrructions */
    }

    /* get here for any other switch value */
    /* this code will get a value based on length specified in switches */
    num = get_uint(cptr, rdx, max[l], &r);  /* get the unsigned value */
    for (i = 0; i < l && i < 4; i++) 
        val[i] = (num >> ((l - (1 + i)) * 8)) & 0xff; /* get 1-4 bytes of data */
    return -(l-1);
}

