/* gri_sys.c: GRI-909 simulator interface

   Copyright (c) 2001-2005, Robert M Supnik

   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
   ROBERT M SUPNIK 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.

   Except as contained in this notice, the name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   18-Oct-02    RMS     Fixed bug in symbolic decode (found by Hans Pufal)
*/

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

extern DEVICE cpu_dev;
extern UNIT cpu_unit;
extern DEVICE tti_dev, tto_dev;
extern DEVICE hsr_dev, hsp_dev;
extern DEVICE rtc_dev;
extern REG cpu_reg[];
extern uint16 M[];
extern int32 sim_switches;

/* SCP data structures and interface routines

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

char sim_name[] = "GRI-909";

REG *sim_PC = &cpu_reg[0];

int32 sim_emax = 2;

DEVICE *sim_devices[] = {
    &cpu_dev,
    &tti_dev,
    &tto_dev,
    &hsr_dev,
    &hsp_dev,
    &rtc_dev,
    NULL
    };

const char *sim_stop_messages[] = {
    "Unknown error",
    "Unimplemented unit",
    "HALT instruction",
    "Breakpoint",
    "Invalid interrupt request"
     };

/* Binary loader

   Bootstrap loader format consists of blocks separated by zeroes.  Each
   word in the block has three frames: a control frame (ignored) and two
   data frames.  The user must specify the load address.  Switch -c means
   continue and load all blocks until end of tape.
*/

t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
{
int32 c;
uint32 org;
t_stat r;
char gbuf[CBUFSIZE];

if (*cptr != 0) {                                       /* more input? */
    cptr = get_glyph (cptr, gbuf, 0);                   /* get origin */
    org = get_uint (gbuf, 8, AMASK, &r);
    if (r != SCPE_OK) return r;
    if (*cptr != 0) return SCPE_ARG;                    /* no more */
    }
else org = 0200;                                        /* default 200 */

for (;;) {                                              /* until EOF */
    while ((c = getc (fileref)) == 0) ;                 /* skip starting 0's */
    if (c == EOF) break;                                /* EOF? done */
    for ( ; c != 0; ) {                                 /* loop until ctl = 0 */
                                                        /* ign ctrl frame */
        if ((c = getc (fileref)) == EOF)                /* get high byte */
            return SCPE_FMT;                            /* EOF is error */
        if (!MEM_ADDR_OK (org)) return SCPE_NXM;
        M[org] = ((c & 0377) << 8);                     /* store high */
        if ((c = getc (fileref)) == EOF)                /* get low byte */
            return SCPE_FMT;                            /* EOF is error */
        M[org] = M[org] | (c & 0377);                   /* store low */
        org = org + 1;                                  /* incr origin */
        if ((c = getc (fileref)) == EOF)                /* get ctrl frame */
            return SCPE_OK;                             /* EOF is ok */
        }                                               /* end block for */
    if (!(sim_switches & SWMASK ('C'))) return SCPE_OK;
    }                                                   /* end tape for */
return SCPE_OK;
}

/* Symbol tables */

#define F_V_FL  16                                      /* class flag */
#define F_M_FL  017
#define F_V_FO  000                                     /* function out */
#define F_V_FOI 001                                     /* FO, impl reg */
#define F_V_SF  002                                     /* skip function */
#define F_V_SFI 003                                     /* SF, impl reg */
#define F_V_RR  004                                     /* reg reg */
#define F_V_ZR  005                                     /* zero reg */
#define F_V_RS  006                                     /* reg self */
#define F_V_JC  010                                     /* jump cond */
#define F_V_JU  011                                     /* jump uncond */
#define F_V_RM  012                                     /* reg mem */
#define F_V_ZM  013                                     /* zero mem */
#define F_V_MR  014                                     /* mem reg */
#define F_V_MS  015                                     /* mem self */
#define F_2WD   010                                     /* 2 words */

#define F_FO    (F_V_FO << F_V_FL)
#define F_FOI   (F_V_FOI << F_V_FL)
#define F_SF    (F_V_SF << F_V_FL)
#define F_SFI   (F_V_SFI << F_V_FL)
#define F_RR    (F_V_RR << F_V_FL)
#define F_ZR    (F_V_ZR << F_V_FL)
#define F_RS    (F_V_RS << F_V_FL)
#define F_JC    (F_V_JC << F_V_FL)
#define F_JU    (F_V_JU << F_V_FL)
#define F_RM    (F_V_RM << F_V_FL)
#define F_ZM    (F_V_ZM << F_V_FL)
#define F_MR    (F_V_MR << F_V_FL)
#define F_MS    (F_V_MS << F_V_FL)

struct fnc_op {
    uint32      inst;                                   /* instr prot */
    uint32      imask;                                  /* instr mask */
    uint32      oper;                                   /* operator */
    uint32      omask;                                  /* oper mask */
    };

static const int32 masks[] = {
    0176000, 0176077, 0000077, 0176077,
    0000300, 0176300, 0000300, 0177777,
    0000077, 0177777, 0000377, 0176377,
    0176300, 0176377
    };

/* Instruction mnemonics

   Order is critical, as some instructions are more precise versions of
   others.  For example, JU must precede JC, otherwise, JU will be decoded
   as JC 0,ETZ,dst.  There are some ambiguities, eg, what is 02-xxxx-06?
   Priority is as follows:

   FO (02-xxxx-rr)
   SF (rr-xxxx-02)
   MR (06-xxxx-rr)
   RM (rr-xxxx-06)
   JC (rr-xxxx-03)
   RR 
*/

static const char *opcode[] = {
 "FOM", "FOA", "FOI", "FO",                             /* FOx before FO */
 "SFM", "SFA", "SFI", "SF",                             /* SFx before SF */
 "ZM", "ZMD", "ZMI", "ZMID",                            /* ZM before RM */
 "MS", "MSD", "MSI", "MSID",
 "RM", "RMD", "RMI", "RMID",
 "MR", "MRD", "MRI", "MRID",
 "JO", "JOD", "JN", "JND",                              /* JU before JC */
 "JU", "JUD", "JC", "JCD",
 "ZR", "ZRC", "RR", "RRC",                              /* ZR before RR */
 "RS", "RSC",
 NULL
 };

static const uint32 opc_val[] = {
 0004000+F_FOI, 0004013+F_FOI, 0004004+F_FOI, 0004000+F_FO,
 0000002+F_SFI, 0026002+F_SFI, 0010002+F_SFI, 0000002+F_SF,
 0000006+F_ZM, 0000106+F_ZM, 0000206+F_ZM, 0000306+F_ZM,
 0014006+F_MS, 0014106+F_MS, 0014206+F_MS, 0014306+F_MS,
 0000006+F_RM, 0000106+F_RM, 0000206+F_RM, 0000306+F_RM,
 0014000+F_MR, 0014100+F_MR, 0014200+F_MR, 0014300+F_MR,
 0037003+F_JU, 0037103+F_JU, 0037203+F_JU, 0037303+F_JU,
 0000403+F_JU, 0000503+F_JU, 0000003+F_JC, 0000103+F_JC,
 0000000+F_ZR, 0000200+F_ZR, 0000000+F_RR, 0000200+F_RR,
 0000000+F_RS, 0000200+F_RS
 };

/* Unit mnemonics.  All 64 units are decoded, most just to octal integers */

static const char *unsrc[64] = {
 "0", "IR", "2", "TRP", "ISR", "MA", "MB", "SC",        /* 00 - 07 */
 "SWR", "AX", "AY", "AO", "14", "15", "16", "MSR",      /* 10 - 17 */
 "20", "21", "22", "23", "BSW", "BPK", "26", "27",      /* 20 - 27 */
 "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "36", "37",  /* 30 - 37 */
 "40", "41", "42", "43", "44", "45", "46", "47",
 "50", "51", "52", "53", "54", "55", "56", "57",
 "60", "61", "62", "63", "64", "65", "66", "67",
 "70", "71", "72", "73", "74", "RTC", "HSR", "TTI"      /* 70 - 77 */
 };

static const char *undst[64] = {
 "0", "IR", "2", "TRP", "ISR", "5", "MB", "SC",         /* 00 - 07 */
 "SWR", "AX", "AY", "13", "EAO", "15", "16", "MSR",     /* 10 - 17 */
 "20", "21", "22", "23", "BSW", "BPK", "26", "27",      /* 20 - 27 */
 "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "36", "37",  /* 30 - 37 */
 "40", "41", "42", "43", "44", "45", "46", "47",
 "50", "51", "52", "53", "54", "55", "56", "57",
 "60", "61", "62", "63", "64", "65", "66", "67",
 "70", "71", "72", "73", "74", "RTC", "HSP", "TTO"      /* 70 - 77 */
 };

                                                        /* Operators */

static const char *opname[4] = {
 NULL, "P1", "L1", "R1"
 };

/* Conditions */

static const char *cdname[8] = {
 "NEVER", "ALWAYS", "ETZ", "NEZ", "LTZ", "GEZ", "LEZ", "GTZ"
 };

/* Function out/sense function */

static const char *fname[] = {
 "NOT",                                                 /* any SF */
 "POK", "LNK", "BOV",                                   /* SFM */
 "SOV", "AOV",                                          /* SFA */
 "IRDY", "ORDY",                                        /* any SF */
 "CLL", "STL", "CML", "HLT",                            /* FOM */
 "ICF", "ICO",                                          /* FOI */
 "ADD", "AND", "XOR", "OR",                             /* FOA */
 "INP", "IRDY", "ORDY", "STRT",                         /* any FO */
 NULL
 };

static const struct fnc_op fop[] = {
 { 0000002, 0000077, 001, 001 },                        /* NOT */
 { 0000002, 0176077, 010, 010 },                        /* POK */
 { 0000002, 0176077, 004, 004 },                        /* LNK */
 { 0000002, 0176077, 002, 002 },                        /* BOV */
 { 0026002, 0176077, 004, 004 },                        /* SOV */
 { 0026002, 0176077, 002, 002 },                        /* AOV */
 { 0000002, 0000077, 010, 010 },                        /* IRDY */
 { 0000002, 0000077, 002, 002 },                        /* ORDY */
 { 0004000, 0176077, 001, 003 },                        /* CLL */
 { 0004000, 0176077, 002, 003 },                        /* STL */
 { 0004000, 0176077, 003, 003 },                        /* CML */
 { 0004000, 0176077, 004, 004 },                        /* HLT */
 { 0004004, 0176077, 001, 001 },                        /* ICF */
 { 0004004, 0176077, 002, 002 },                        /* ICO */
 { 0004013, 0176077, 000, 014 },                        /* ADD */
 { 0004013, 0176077, 004, 014 },                        /* AND */
 { 0004013, 0176077, 010, 014 },                        /* XOR */
 { 0004013, 0176077, 014, 014 },                        /* OR */
 { 0004000, 0176000, 011, 011 },                        /* INP */
 { 0004000, 0176000, 010, 010 },                        /* IRDY */
 { 0004000, 0176000, 002, 002 },                        /* ORDY */
 { 0004000, 0176000, 001, 001 }                         /* STRT */
 };

/* Print opcode field for FO, SF */

void fprint_op (FILE *of, uint32 inst, uint32 op)
{
int32 i, nfirst;

for (i = nfirst = 0; fname[i] != NULL; i++) {
    if (((inst & fop[i].imask) == fop[i].inst) &&
        ((op & fop[i].omask) == fop[i].oper)) {
        op = op & ~fop[i].omask;
        if (nfirst) fputc (' ', of);
        nfirst = 1;
        fprintf (of, "%s", fname[i]);
        }
    }
if (op) fprintf (of, " %o", op);
return;
}

/* Symbolic decode

   Inputs:
        *of     =       output stream
        addr    =       current PC
        *val    =       pointer to data
        *uptr   =       pointer to unit 
        sw      =       switches
   Outputs:
        return  =       status code
*/

#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x)

t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
    UNIT *uptr, int32 sw)
{
int32 i, j;
uint32 inst, src, dst, op, bop;

inst = val[0];
if (sw & SWMASK ('A')) {                                /* ASCII? */
    if (inst > 0377) return SCPE_ARG;
    fprintf (of, FMTASC (inst & 0177));
    return SCPE_OK;
    }
if (sw & SWMASK ('C')) {                                /* characters? */
    fprintf (of, FMTASC ((inst >> 8) & 0177));
    fprintf (of, FMTASC (inst & 0177));
    return SCPE_OK;
    }
if (!(sw & SWMASK ('M'))) return SCPE_ARG;

/* Instruction decode */

inst = val[0];
src = I_GETSRC (inst);                                  /* get fields */
op = I_GETOP (inst);
dst = I_GETDST (inst);
bop = op >> 2;                                          /* bus op */
for (i = 0; opcode[i] != NULL; i++) {                   /* loop thru ops */
    j = (opc_val[i] >> F_V_FL) & F_M_FL;                /* get class */
    if ((opc_val[i] & DMASK) == (inst & masks[j])) {    /* match? */

        switch (j) {                                    /* case on class */

        case F_V_FO:                                    /* func out */
            fprintf (of, "%s ", opcode[i]);
            fprint_op (of, inst, op);
            fprintf (of, ",%s", undst[dst]);
            break;

        case F_V_FOI:                                   /* func out impl */
            fprintf (of, "%s ", opcode[i]);
            fprint_op (of, inst, op);
            break;

        case F_V_SF:                                    /* skip func */
            fprintf (of, "%s %s,", opcode[i], unsrc[src]);
            fprint_op (of, inst, op);
            break;

        case F_V_SFI:                                   /* skip func impl */
            fprintf (of, "%s ", opcode[i]);
            fprint_op (of, inst, op);
            break;

        case F_V_RR:                                    /* reg reg */
            if (strcmp (unsrc[src], undst[dst]) == 0) {
                if (bop) fprintf (of, "%s %s,%s", opcode[i + 2],
                    unsrc[src], opname[bop]);
                else fprintf (of, "%s %s", opcode[i + 2], unsrc[src]);
                }
            else {
                if (bop) fprintf (of, "%s %s,%s,%s", opcode[i],
                    unsrc[src], opname[bop], undst[dst]);
                else fprintf (of, "%s %s,%s", opcode[i],
                    unsrc[src], undst[dst]);
                }
            break;

        case F_V_ZR:                                    /* zero reg */
            if (bop) fprintf (of, "%s %s,%s", opcode[i],
                opname[bop], undst[dst]);
            else fprintf (of, "%s %s", opcode[i], undst[dst]);
            break;

        case F_V_JC:                                    /* jump cond */
            fprintf (of, "%s %s,%s,%o", opcode[i],
                unsrc[src], cdname[op >> 1], val[1]);
            break;

        case F_V_JU:                                    /* jump uncond */
            fprintf (of, "%s %o", opcode[i], val[1]);
            break;

        case F_V_RM:                                    /* reg mem */
            if (bop) fprintf (of, "%s %s,%s,%o", opcode[i],
                unsrc[src], opname[bop], val[1]);
            else fprintf (of, "%s %s,%o", opcode[i], unsrc[src], val[1]);
            break;

        case F_V_ZM:                                    /* zero mem */
            if (bop) fprintf (of, "%s %s,%o", opcode[i],
                opname[bop], val[1]);
            else fprintf (of, "%s %o", opcode[i], val[1]);
            break;

        case F_V_MR:                                    /* mem reg */
            if (bop) fprintf (of, "%s %o,%s,%s", opcode[i],
                val[1], opname[bop], undst[dst]);
            else fprintf (of, "%s %o,%s", opcode[i], val[1], undst[dst]);
            break;

        case F_V_MS:                                    /* mem self */
            if (bop) fprintf (of, "%s %o,%s", opcode[i],
                val[1], opname[bop]);
            else fprintf (of, "%s %o", opcode[i], val[1]);
            break;
            }                                           /* end case */

        return (j >= F_2WD)? -1: SCPE_OK;
        }                                               /* end if */
    }                                                   /* end for */
return SCPE_ARG;
}

/* Field parse routines

        get_fnc         get function field
        get_ma          get memory address
        get_sd          get source or dest
        get_op          get optional bus operator
*/

char *get_fnc (char *cptr, t_value *val)
{
char gbuf[CBUFSIZE];
int32 i;
t_value d;
t_stat r;
uint32 inst = val[0];
uint32 fncv = 0, fncm = 0;

while (*cptr) {
    cptr = get_glyph (cptr, gbuf, 0);                   /* get glyph */
    d = get_uint (gbuf, 8, 017, &r);                    /* octal? */
    if (r == SCPE_OK) {                                 /* ok? */
        if (d & fncm) return NULL;                      /* already filled? */
        fncv = fncv | d;                                /* save */
        fncm = fncm | d;                                /* field filled */
        }
    else {                                              /* symbol? */
        for (i = 0; fname[i] != NULL; i++) {            /* search table */
            if ((strcmp (gbuf, fname[i]) == 0) &&       /* match for inst? */
                ((inst & fop[i].imask) == fop[i].inst)) {
                if (fop[i].oper & fncm) return NULL;    /* already filled? */
                fncm = fncm | fop[i].omask;
                fncv = fncv | fop[i].oper;
                break;
                }
            }
        if (fname[i] == NULL) return NULL;
        }                                               /* end else */
    }                                                   /* end while */
val[0] = val[0] | (fncv << I_V_OP);                     /* store fnc */
return cptr;
}

char *get_ma (char *cptr, t_value *val, char term)
{
char gbuf[CBUFSIZE];
t_value d;
t_stat r;

cptr = get_glyph (cptr, gbuf, term);                    /* get glyph */
d = get_uint (gbuf, 8, DMASK, &r);                      /* [0,177777] */
if (r != SCPE_OK) return NULL;
val[1] = d;                                             /* second wd */
return cptr;
}

char *get_sd (char *cptr, t_value *val, char term, t_bool src)
{
char gbuf[CBUFSIZE];
int32 d;
t_stat r;

cptr = get_glyph (cptr, gbuf, term);                    /* get glyph */
for (d = 0; d < 64; d++) {                              /* symbol match? */
    if ((strcmp (gbuf, unsrc[d]) == 0) ||
        (strcmp (gbuf, undst[d]) == 0)) break;
    }
if (d >= 64) {                                          /* no, [0,63]? */
    d = get_uint (gbuf, 8, 077, &r);
    if (r != SCPE_OK) return NULL;
    }
val[0] = val[0] | (d << (src? I_V_SRC: I_V_DST));       /* or to inst */
return cptr;
}

char *get_op (char *cptr, t_value *val, char term)
{
char gbuf[CBUFSIZE], *tptr;
int32 i;

tptr = get_glyph (cptr, gbuf, term);                    /* get glyph */
for (i = 1; i < 4; i++) {                               /* symbol match? */
    if (strcmp (gbuf, opname[i]) == 0) {
        val[0] = val[0] | (i << (I_V_OP + 2));          /* or to inst */
        return tptr;
        }
    }
return cptr;                                            /* original ptr */
}

/* 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 (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
int32 i, j, k;
char *tptr, gbuf[CBUFSIZE];

while (isspace (*cptr)) cptr++;                         /* absorb spaces */
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
    if (cptr[0] == 0) return SCPE_ARG;                  /* must have 1 char */
    val[0] = (t_value) cptr[0] & 0177;
    return SCPE_OK;
    }
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* char string? */
    if (cptr[0] == 0) return SCPE_ARG;                  /* must have 1 char */
    val[0] = (((t_value) cptr[0] & 0177) << 8) |
              ((t_value) cptr[1] & 0177);
    return SCPE_OK;
    }

/* Instruction parse */

cptr = get_glyph (cptr, gbuf, 0);                       /* get opcode */
for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ;
if (opcode[i] == NULL) return SCPE_ARG;
val[0] = opc_val[i] & DMASK;                            /* get value */
j = (opc_val[i] >> F_V_FL) & F_M_FL;                    /* get class */

switch (j) {                                            /* case on class */

    case F_V_FO:                                        /* func out */
        tptr = strchr (cptr, ',');                      /* find dst */
        if (!tptr) return SCPE_ARG;                     /* none? */
        *tptr = 0;                                      /* split fields */
        cptr = get_fnc (cptr, val);                     /* fo # */
        if (!cptr) return SCPE_ARG;
        cptr = get_sd (tptr + 1, val, 0, FALSE);        /* dst */
        break;

    case F_V_FOI:                                       /* func out impl */
        cptr = get_fnc (cptr, val);                     /* fo # */
        break;

    case F_V_SF:                                        /* skip func */
        cptr = get_sd (cptr, val, ',', TRUE);           /* src */
        if (!cptr) return SCPE_ARG;

    case F_V_SFI:                                       /* skip func impl */
        cptr = get_fnc (cptr, val);                     /* fo # */
        break;

    case F_V_RR:                                        /* reg-reg */
        cptr = get_sd (cptr, val, ',', TRUE);           /* src */
        if (!cptr) return SCPE_ARG;
        cptr = get_op (cptr, val, ',');                 /* op */
        if (!cptr) return SCPE_ARG;
        cptr = get_sd (cptr, val, 0, FALSE);            /* dst */
        break;

    case F_V_ZR:                                        /* zero-reg */
        cptr = get_op (cptr, val, ',');                 /* op */
        if (!cptr) return SCPE_ARG;
        cptr = get_sd (cptr, val, 0, FALSE);            /* dst */
        break;

    case F_V_RS:                                        /* reg self */
        cptr = get_sd (cptr, val, ',', TRUE);           /* src */
        if (!cptr) return SCPE_ARG;
        val[0] = val[0] | I_GETSRC (val[0]);            /* duplicate */
        cptr = get_op (cptr, val, 0);                   /* op */
        break;

    case F_V_JC:                                        /* jump cond */
        cptr = get_sd (cptr, val, ',', TRUE);           /* src */
        if (!cptr) return SCPE_ARG;
        cptr = get_glyph (cptr, gbuf, ',');             /* cond */
        for (k = 0; k < 8; k++) {                       /* symbol? */
            if (strcmp (gbuf, cdname[k]) == 0) break;
            }
        if (k >= 8) return SCPE_ARG;
        val[0] = val[0] | (k << (I_V_OP + 1));          /* or to inst */

    case F_V_JU:                                        /* jump uncond */
        cptr = get_ma (cptr, val, 0);                   /* addr */
        break;

    case F_V_RM:                                        /* reg mem */
        cptr = get_sd (cptr, val, ',', TRUE);           /* src */
        if (!cptr) return SCPE_ARG;
    case F_V_ZM:                                        /* zero mem */
        cptr = get_op (cptr, val, ',');                 /* op */
        if (!cptr) return SCPE_ARG;
        cptr = get_ma (cptr, val, 0);                   /* addr */
        break;

    case F_V_MR:                                        /* mem reg */
        cptr = get_ma (cptr, val, ',');                 /* addr */
        if (!cptr) return SCPE_ARG;
        cptr = get_op (cptr, val, ',');                 /* op */
        if (!cptr) return SCPE_ARG;
        cptr = get_sd (cptr, val, 0, FALSE);            /* dst */
        break;

    case F_V_MS:                                        /* mem self */
        cptr = get_ma (cptr, val, ',');                 /* addr */
        if (!cptr) return SCPE_ARG;
        cptr = get_op (cptr, val, 0);                   /* op */
        break;
    }                                                   /* end case */

if (!cptr || (*cptr != 0)) return SCPE_ARG;             /* junk at end? */
return (j >= F_2WD)? -1: SCPE_OK;
}
