/* b5500_sys.c: Burroughs 5500 Simulator system interface.

   Copyright (c) 2016, Richard Cornwell

   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
   RICHARD CORNWELL 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.

*/

#include "sim_defs.h"
#include "b5500_defs.h"
#include "sim_card.h"
#include <ctype.h>

t_stat  parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw);

/* SCP data structures and interface routines

   sim_name             simulator name string
   sim_PC               pointer to saved PC register descriptor
   sim_emax             number of words for examine
   sim_devices          array of pointers to simulated devices
   sim_stop_messages    array of pointers to stop messages
   sim_load             binary loader
*/

char                sim_name[] = "B5500";

REG                *sim_PC = &cpu_reg[0];

int32               sim_emax = 1;

DEVICE             *sim_devices[] = {
    &cpu_dev,
    &chan_dev,
#if NUM_DEVS_CDR > 0
    &cdr_dev,
#endif
#if NUM_DEVS_CDP > 0
    &cdp_dev,
#endif
#if NUM_DEVS_LPR > 0
    &lpr_dev,
#endif
#if NUM_DEVS_CON > 0
    &con_dev,
#endif
#if NUM_DEVS_MT > 0
    &mt_dev,
#endif
#if NUM_DEVS_DR > 0
    &drm_dev,
#endif
#if NUM_DEVS_DSK > 0
    &esu_dev,
    &dsk_dev,
#endif
#if NUM_DEVS_DTC > 0
    &dtc_dev,
#endif
    NULL
};

/* Simulator stop codes */
const char         *sim_stop_messages[SCPE_BASE] = {
    0,
};

/* 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"},
    {0, 0}
};


uint8                parity_table[64] = {
    /* 0    1    2    3    4    5    6    7 */
    0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
    0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
    0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
    0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
    0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
    0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
    0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
    0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000
};

uint8           mem_to_ascii[64] = {
   /* x0   x1   x2   x3   x4   x5   x6   x7 */
     '0', '1', '2', '3', '4', '5', '6', '7',     /* 0x */
     '8', '9', '#', '@', '?', ':', '>', '}',     /* 1x */
     '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G',     /* 2x */
     'H', 'I', '.', '[', '&', '(', '<', '~',     /* 3x */
     '|', 'J', 'K', 'L', 'M', 'N', 'O', 'P',     /* 4x */
     'Q', 'R', '$', '*', '-', ')', ';', '{',     /* 5x */
     ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X',     /* 6x */
     'Y', 'Z', ',', '%', '!', '=', ']', '"'      /* 7x */
};

const char          con_to_ascii[64] = {
    '?', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', '0', '#', '@', ':', '>', '}',    /* 17 = box */
    ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', '!', ',', '%', '=', ']', '"',
    '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', '|', '$', '*', ')', ';', '{',     /* 57 = triangle */
    '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', '+', '.', '[', '(', '<', '~',     /* 37 = stop code */
};                              /* 72 = rec mark */
                                /* 75 = squiggle, 77 = del */

const char          ascii_to_con[128] = {
   /* Control                              */
     -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,     /* 0 - 37 */
   /* Control                              */
     -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
   /* Control                              */
     -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
   /* Control                              */
     -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
   /*sp    !    "    #    $    %    &    ' */
    020, 032, 037, 013, 053, 034, 060, 014,     /* 40 - 77 */
   /* (    )    *    +    ,    -    .    / */
    075, 055, 054, 072, 033, 040, 073, 021,
   /* 0    1    2    3    4    5    6    7 */
    012, 001, 002, 003, 004, 005, 006, 007,
   /* 8    9    :    ;    <    =    >    ? */
    010, 011, 015, 056, 076, 035, 016, 000,
   /* @    A    B    C    D    E    F    G */
    014, 061, 062, 063, 064, 065, 066, 067,     /* 100 - 137 */
   /* H    I    J    K    L    M    N    O */
    070, 071, 041, 042, 043, 044, 045, 046,
   /* P    Q    R    S    T    U    V    W */
    047, 050, 051, 022, 023, 024, 025, 026,
   /* X    Y    Z    [    \    ]    ^    _ */
    027, 030, 031, 074, 036, 036, 057, 000,
   /* `    a    b    c    d    e    f    g */
    035, 061, 062, 063, 064, 065, 066, 067,     /* 140 - 177 */
   /* h    i    j    k    l    m    n    o */
    070, 071, 041, 042, 043, 044, 045, 046,
   /* p    q    r    s    t    u    v    w */
    047, 050, 051, 022, 023, 024, 025, 026,
   /* x    y    z    {    |    }    ~   del*/
    027, 030, 031, 057, 052, 017,  -1,  -1
};


/* Load a card image file into memory.  */

t_stat
sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag)
{
   /* Currently not implimented until I know format of load files */
    return SCPE_NOFNC;
}

#define TYPE_A  1       /* Full 12 bit opcode */
#define TYPE_B  2       /* 6 Bit Opcode with 6 bit field */
#define TYPE_C  3       /* 8 Bit opcode with 4 bit field */
#define TYPE_D  4       /* 2 bit opcode, 10 bit field */

/* Opcodes */
t_opcode  word_ops[] = {
/* Word mode opcodes */
       { WMOP_LITC,      TYPE_D, "LITC",},  /* Load literal */
       { WMOP_OPDC,      TYPE_D, "OPDC",},  /* Load operand */
       { WMOP_DESC,      TYPE_D, "DESC",},  /* Load Descriptor */
       { WMOP_DEL,       TYPE_A, "DEL", },  /* Delete top of stack */
       { WMOP_NOP,       TYPE_A, "NOP", },  /* Nop operation */
       { WMOP_XRT,       TYPE_A, "XRT", },  /* Set Variant */
       { WMOP_ADD,       TYPE_A, "ADD", },  /* Add */
       { WMOP_DLA,       TYPE_A, "DLA", },  /* Double Precision Add */
       { WMOP_PRL,       TYPE_A, "PRL", },  /* Program Release */
       { WMOP_LNG,       TYPE_A, "LNG", },  /* Logical Negate */
       { WMOP_CID,       TYPE_A, "CID", },  /* Conditional Integer Store Destructive */
       { WMOP_GEQ,       TYPE_A, "GEQ", },  /* B greater than or equal to A */
       { WMOP_BBC,       TYPE_A, "BBC", },  /* Branch Backward Conditional */
       { WMOP_BRT,       TYPE_A, "BRT", },  /* Branch Return */
       { WMOP_INX,       TYPE_A, "INX", },  /* Index */
       { WMOP_ITI,       TYPE_A, "ITI", },  /* Interrogate interrupt */
       { WMOP_LOR,       TYPE_A, "LOR", },  /* Logical Or */
       { WMOP_CIN,       TYPE_A, "CIN", },  /* Conditional Integer Store non-destructive */
       { WMOP_GTR,       TYPE_A, "GTR", },  /* B Greater than A */
       { WMOP_BFC,       TYPE_A, "BFC", },  /* Branch Forward Conditional */
       { WMOP_RTN,       TYPE_A, "RTN", },  /* Return normal */
       { WMOP_COC,       TYPE_A, "COC", },  /* Construct Operand Call */
       { WMOP_SUB,       TYPE_A, "SUB", },  /* Subtract */
       { WMOP_DLS,       TYPE_A, "DLS", },  /* Double Precision Subtract */
       { WMOP_MUL,       TYPE_A, "MUL", },  /* Multiply */
       { WMOP_DLM,       TYPE_A, "DLM", },  /* Double Precision Multiply */
       { WMOP_RTR,       TYPE_A, "RTR", },  /* Read Timer */
       { WMOP_LND,       TYPE_A, "LND", },  /* Logical And */
       { WMOP_STD,       TYPE_A, "STD", },  /* B Store Destructive */
       { WMOP_NEQ,       TYPE_A, "NEQ", },  /* B Not equal to A */
       { WMOP_SSN,       TYPE_A, "SSN", },  /* Set Sign Bit */
       { WMOP_XIT,       TYPE_A, "XIT", },  /* Exit */
       { WMOP_MKS,       TYPE_A, "MKS", },  /* Mark Stack */
       { WMOP_DIV,       TYPE_A, "DIV", },  /* Divide */
       { WMOP_DLD,       TYPE_A, "DLD", },  /* Double Precision Divide */
       { WMOP_COM,       TYPE_A, "COM", },  /* Communication operator */
       { WMOP_LQV,       TYPE_A, "LQV", },  /* Logical Equivalence */
       { WMOP_SND,       TYPE_A, "SND", },  /* B Store Non-destructive */
       { WMOP_XCH,       TYPE_A, "XCH", },  /* Exchange */
       { WMOP_CHS,       TYPE_A, "CHS", },  /* Change sign bit */
       { WMOP_RTS,       TYPE_A, "RTS", },  /* Return Special */
       { WMOP_CDC,       TYPE_A, "CDC", },  /* Construct descriptor call */
       { WMOP_FTC,       TYPE_A, "FTC", },  /* Transfer F Field to Core Field */
       { WMOP_MOP,       TYPE_A, "MOP", },  /* Reset Flag bit */
       { WMOP_LOD,       TYPE_A, "LOD", },  /* Load */
       { WMOP_DUP,       TYPE_A, "DUP", },  /* Duplicate */
       { WMOP_TOP,       TYPE_A, "TOP", },  /* Test Flag Bit */
       { WMOP_IOR,       TYPE_A, "IOR", },  /* I/O Release */
       { WMOP_LBC,       TYPE_A, "LBC", },  /* Word Branch Backward Conditional */
       { WMOP_SSF,       TYPE_A, "SSF", },  /* Set or Store S or F registers */
       { WMOP_HP2,       TYPE_A, "HP2", },  /* Halt P2 */
       { WMOP_LFC,       TYPE_A, "LFC", },  /* Word Branch Forward Conditional */
       { WMOP_ZP1,       TYPE_A, "ZP1", },  /* Conditional Halt */
       { WMOP_TUS,       TYPE_A, "TUS", },  /* Interrogate Peripheral Status */
       { WMOP_LLL,       TYPE_A, "LLL", },  /* Link List Look-up */
       { WMOP_IDV,       TYPE_A, "IDV", },  /* Integer Divide Integer */
       { WMOP_SFI,       TYPE_A, "SFI", },  /* Store for Interrupt */
       { WMOP_SFT,       TYPE_A, "SFT", },  /* Store for Test */
       { WMOP_FTF,       TYPE_A, "FTF", },  /* Transfer F Field to F Field */
       { WMOP_MDS,       TYPE_A, "MDS", },  /* Set Flag Bit */
       { WMOP_IP1,       TYPE_A, "IP1", },  /* Initiate P1 */
       { WMOP_ISD,       TYPE_A, "ISD", },  /* Interger Store Destructive */
       { WMOP_LEQ,       TYPE_A, "LEQ", },  /* B Less Than or Equal to A */
       { WMOP_BBW,       TYPE_A, "BBW", },  /* Banch Backward Conditional */
       { WMOP_IP2,       TYPE_A, "IP2", },  /* Initiate P2 */
       { WMOP_ISN,       TYPE_A, "ISN", },  /* Integer Store Non-Destructive */
       { WMOP_LSS,       TYPE_A, "LSS", },  /* B Less Than A */
       { WMOP_BFW,       TYPE_A, "BFW", },  /* Branch Forward Unconditional */
       { WMOP_IIO,       TYPE_A, "IIO", },  /* Initiate I/O */
       { WMOP_EQL,       TYPE_A, "EQL", },  /* B Equal A */
       { WMOP_SSP,       TYPE_A, "SSP", },  /* Reset Sign Bit */
       { WMOP_CMN,       TYPE_A, "CMN", },  /* Enter Character Mode In Line */
       { WMOP_IFT,       TYPE_A, "IFT", },  /* Test Initiate */
       { WMOP_CTC,       TYPE_A, "CTC", },  /* Transfer Core Field to Core Field */
       { WMOP_LBU,       TYPE_A, "LBU", },  /* Word Branch Backward Unconditional */
       { WMOP_LFU,       TYPE_A, "LFU", },  /* Word Branch Forward Unconditional */
       { WMOP_TIO,       TYPE_A, "TIO", },  /* Interrogate I/O Channels */
       { WMOP_RDV,       TYPE_A, "RDV", },  /* Remainder Divide */
       { WMOP_FBS,       TYPE_A, "FBS", },  /* Flag Bit Search */
       { WMOP_CTF,       TYPE_A, "CTF", },  /* Transfer Core Field to F Field */
       { WMOP_ISO,       TYPE_B, "ISO", },  /* Variable Field Isolate XX */
       { WMOP_CBD,       TYPE_C, "CBD", },  /* Non-Zero Field Branch Backward Destructive Xy */
       { WMOP_CBN,       TYPE_C, "CBN", },  /* Non-Zero Field Branch Backward Non-Destructive Xy */
       { WMOP_CFD,       TYPE_C, "CFD", },  /* Non-Zero Field Branch Forward Destructive Xy */
       { WMOP_CFN,       TYPE_B, "CFN", },  /* Non-Zero Field Branch Forward Non-Destructive Xy */
       { WMOP_DIA,       TYPE_B, "DIA", },  /* Dial A XX */
       { WMOP_DIB,       TYPE_B, "DIB", },  /* Dial B XX Upper 6 not Zero */
       { WMOP_TRB,       TYPE_B, "TRB", },  /* Transfer Bits XX */
       { WMOP_FCL,       TYPE_B, "FCL", },  /* Compare Field Low XX */
       { WMOP_FCE,       TYPE_B, "FCE", },  /* Compare Field Equal XX */
       { 0,              0,      NULL,  }
};

t_opcode  char_ops[] = {
/* Character Mode */
       { CMOP_EXC,       TYPE_A, "EXC", },  /* Exit Character Mode */
       { CMOP_CMX,       TYPE_A, "CMX", },  /* Exit Character Mode In Line */
       { CMOP_BSD,       TYPE_B, "BSD", },  /* Skip Bit Destiniation */
       { CMOP_BSS,       TYPE_B, "BSS", },  /* SKip Bit Source */
       { CMOP_RDA,       TYPE_B, "RDA", },  /* Recall Destination Address */
       { CMOP_TRW,       TYPE_B, "TRW", },  /* Transfer Words */
       { CMOP_SED,       TYPE_B, "SED", },  /* Set Destination Address */
       { CMOP_TDA,       TYPE_B, "TDA", },  /* Transfer Destination Address */
       { CMOP_TBN,       TYPE_B, "TBN", },  /* Transfer Blanks for Non-Numerics */
       { WMOP_ITI,       TYPE_A, "ITI", },  /* Interrogate interrupt */
       { WMOP_SFI,       TYPE_A, "SFI", },  /* Store for Interrupt */
       { WMOP_SFT,       TYPE_A, "SFT", },  /* Store for Test */
       { WMOP_ZP1,       TYPE_A, "ZP1", },  /* Conditional Halt */
       { WMOP_HP2,       TYPE_A, "HP2", },  /* Halt P2 */
       { CMOP_SDA,       TYPE_B, "SDA", },  /* Store Destination Address */
       { CMOP_SSA,       TYPE_B, "SSA", },  /* Store Source Address */
       { CMOP_SFD,       TYPE_B, "SFD", },  /* Skip Forward Destination */
       { CMOP_SRD,       TYPE_B, "SRD", },  /* Skip Reverse Destination */
       { CMOP_SES,       TYPE_B, "SES", },  /* Set Source Address */
       { CMOP_TEQ,       TYPE_B, "TEQ", },  /* Test for Equal */
       { CMOP_TNE,       TYPE_B, "TNE", },  /* Test for Not-Equal */
       { CMOP_TEG,       TYPE_B, "TEG", },  /* Test for Greater Or Equal */
       { CMOP_TGR,       TYPE_B, "TGR", },  /* Test For Greater */
       { CMOP_SRS,       TYPE_B, "SRS", },  /* Skip Reverse Source */
       { CMOP_SFS,       TYPE_B, "SFS", },  /* Skip Forward Source */
       { CMOP_TEL,       TYPE_B, "TEL", },  /* Test For Equal or Less */
       { CMOP_TLS,       TYPE_B, "TLS", },  /* Test For Less */
       { CMOP_TAN,       TYPE_B, "TAN", },  /* Test for Alphanumeric */
       { CMOP_BIT,       TYPE_B, "BIT", },  /* Test Bit */
       { CMOP_INC,       TYPE_B, "INC", },  /* Increase Tally */
       { CMOP_STC,       TYPE_B, "STC", },  /* Store Tally */
       { CMOP_SEC,       TYPE_B, "SEC", },  /* Set Tally */
       { CMOP_CRF,       TYPE_B, "CRF", },  /* Call repeat Field */
       { CMOP_JNC,       TYPE_B, "JNC", },  /* Jump Out Of Loop Conditional */
       { CMOP_JFC,       TYPE_B, "JFC", },  /* Jump Forward Conditional */
       { CMOP_JNS,       TYPE_B, "JNS", },  /* Jump out of loop unconditional */
       { CMOP_JFW,       TYPE_B, "JFW", },  /* Jump Forward Unconditional */
       { CMOP_RCA,       TYPE_B, "RCA", },  /* Recall Control Address */
       { CMOP_ENS,       TYPE_B, "ENS", },  /* End Loop */
       { CMOP_BNS,       TYPE_B, "BNS", },  /* Begin Loop */
       { CMOP_RSA,       TYPE_B, "RSA", },  /* Recall Source Address */
       { CMOP_SCA,       TYPE_B, "SCA", },  /* Store Control Address */
       { CMOP_JRC,       TYPE_B, "JRC", },  /* Jump Reverse Conditional */
       { CMOP_TSA,       TYPE_B, "TSA", },  /* Transfer Source Address */
       { CMOP_JRV,       TYPE_B, "JRV", },  /* Jump Reverse Unconditional */
       { CMOP_CEQ,       TYPE_B, "CEQ", },  /* Compare Equal */
       { CMOP_CNE,       TYPE_B, "CNE", },  /* COmpare for Not Equal */
       { CMOP_CEG,       TYPE_B, "CEG", },  /* Compare For Greater Or Equal */
       { CMOP_CGR,       TYPE_B, "CGR", },  /* Compare For Greater */
       { CMOP_BIS,       TYPE_B, "BIS", },  /* Set Bit */
       { CMOP_BIR,       TYPE_B, "BIR", },  /* Reet Bit */
       { CMOP_OCV,       TYPE_B, "OCV", },  /* Output Convert */
       { CMOP_ICV,       TYPE_B, "ICV", },  /* Input Convert */
       { CMOP_CEL,       TYPE_B, "CEL", },  /* Compare For Equal or Less */
       { CMOP_CLS,       TYPE_B, "CLS", },  /* Compare for Less */
       { CMOP_FSU,       TYPE_B, "FSU", },  /* Field Subtract */
       { CMOP_FAD,       TYPE_B, "FAD", },  /* Field Add */
       { CMOP_TRP,       TYPE_B, "TRP", },  /* Transfer Program Characters */
       { CMOP_TRN,       TYPE_B, "TRN", },  /* Transfer Numeric */
       { CMOP_TRZ,       TYPE_B, "TRZ", },  /* Transfer Zones */
       { CMOP_TRS,       TYPE_B, "TRS", },  /* Transfer Source Characters */
       { 0,              0,      NULL,  }
};


/* Print out an instruction */
void
print_opcode(FILE * of, uint16 val, int chr_mode)
{
    uint16      op;
    t_opcode   *tab = (chr_mode) ? char_ops: word_ops;

    op = val;
    while (tab->name != NULL) {
        switch(tab->type) {
        case TYPE_A:
                if (op != tab->op)
                   break;
                fputs(tab->name, of);
                fputs("       ",of);
                return;
        case TYPE_B:
                if ((op & 077) != tab->op)
                   break;
                fputs(tab->name, of);
                fputc(' ',of);
                fputc(' ',of);
                fprint_val(of, (val >> 6), 8, 6, 0);
                fputs("   ",of);
                return;
        case TYPE_C:
                if ((op & 0377) != tab->op)
                   break;
                fputs(tab->name, of);
                fputc(' ',of);
                fprint_val(of, (val >> 8), 8, 4, 0);
                fputs("   ",of);
                return;
        case TYPE_D:
                if ((op & 03) != tab->op)
                   break;
                fputs(tab->name, of);
                fputc(' ',of);
                fprint_val(of, (val >> 2), 8, 10, 0);
                fputc(' ',of);
                return;
        }
        tab++;
    }
    fprintf(of, "*%04o uuo ", op);
}

/* 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)
{
    t_value             inst = *val;
    int                 i;

    fputc(' ', of);
    fprint_val(of, inst, 8, 48, PV_RZRO);

    if (sw & SWMASK('W')) {     /* Word mode opcodes */
        fputs("   ", of);
        for (i = 36; i >= 0; i-=12) {
                uint16     op = (uint16)(inst >> i) & 07777;
                print_opcode(of, op, 0);
        }
    }
    if (sw & SWMASK('C')) {     /* Char mode opcodes */
        fputs("   ", of);
        for (i = 36; i >= 0; i-=12) {
                uint16     op = (uint16)(inst >> i) & 07777;
                print_opcode(of, op, 1);
        }
    }
    if (sw & SWMASK('B')) {     /* BCD mode */
        fputs("   '", of);
        for (i = 42; i >= 0; i-=6) {
            int                 ch;

            ch = (int)(inst >> i) & 077;
            fputc(mem_to_ascii[ch], of);
        }
        fputc('\'', of);
    }
    if (sw & SWMASK('F')) {     /* Floating point/Descriptor */
    }
    return SCPE_OK;
}

t_opcode           *
find_opcode(char *op, t_opcode * tab)
{
    while (tab->name != NULL) {
        if (*tab->name != '\0' && strcmp(op, tab->name) == 0)
            return tab;
        tab++;
    }
    return NULL;
}

/* 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;
    t_value             d;
    int                 opr;
    char                opcode[100];

    while (isspace(*cptr))
        cptr++;
    d = 0;
    if (sw & (SWMASK('W')|SWMASK('C'))) {
        t_opcode           *op;

        /* Grab opcode */
        cptr = get_glyph(cptr, opcode, 0);

        op = 0;
        opr = -1;
        if((op = find_opcode(opcode,
                (SWMASK('W') ? word_ops : char_ops))) == 0) {
            return SCPE_UNK;
        }

        while (*cptr == ' ' || *cptr == '\t')
            cptr++;
        /* Collect first argument if there is one */
        while (*cptr >= '0' && *cptr <= '7')
            opr = (opr << 3) + (*cptr++ - '0');
        /* Skip blanks */
        while (*cptr == ' ' || *cptr == '\t')
            cptr++;
        switch (op->type) {
        case TYPE_A:
                if (opr >= 0)
                   return SCPE_2MARG;
                *val = op->op;
                return SCPE_OK;

        case TYPE_B:
                if (opr < 0 || opr > 64)
                   return SCPE_ARG;
                *val = (opr << 6) | op->op;
                return SCPE_OK;

        case TYPE_C:
                if (opr < 0 || opr > 16)
                   return SCPE_ARG;
                *val = (opr << 8) | op->op;
                return SCPE_OK;

        case TYPE_D:
                if (opr < 0 || opr > 1024)
                   return SCPE_ARG;
                *val = (opr << 2) | op->op;
                return SCPE_OK;
        }
    } else if (sw & SWMASK('B')) {
        i = 0;
        while (*cptr != '\0' && i < 8) {
            d <<= 6;
            d |= (int)sim_ascii_to_six[0177 & *cptr];
            cptr++;
            i++;
        }
        d <<= 6 * (8 - i);
    } else {
        while (*cptr >= '0' && *cptr <= '7') {
            d <<= 3;
            d |= *cptr++ - '0';
        }
    }
    *val = d;
    return SCPE_OK;
}
