/* pdp11_io_lib.c: Unibus/Qbus common support routines

   Copyright (c) 1993-2017, 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.
*/

#if defined (VM_PDP10)                                  /* PDP10 version */
#include "pdp10_defs.h"

#elif defined (VM_VAX)                                  /* VAX version */
#include "vax_defs.h"

#else                                                   /* PDP-11 version */
#include "pdp11_defs.h"
#endif
#include "sim_sock.h"
#include "sim_tmxr.h"
#include "sim_ether.h"

extern int32 int_vec[IPL_HLVL][32];
#if !defined(VEC_SET)
#define VEC_SET 0
#endif
#if (VEC_SET != 0)
extern int32 int_vec_set[IPL_HLVL][32];                 /* bits to set in vector */
#endif
extern int32 (*int_ack[IPL_HLVL][32])(void);
extern t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md);
extern t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md);
extern DIB *iodibp[IOPAGESIZE >> 1];

extern t_stat build_dib_tab (void);

static void build_vector_tab (void);

#if !defined(UNIMEMSIZE)
#define UNIMEMSIZE      001000000                       /* 2**18 */
#endif

#define AUTO_MAXC       32              /* Maximum number of controllers */
#define AUTO_CSRBASE    0010
#define AUTO_CSRMAX    04000
#define AUTO_VECBASE    0300

/* Enable/disable autoconfiguration */

t_stat set_autocon (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (cptr != NULL)
    return SCPE_ARG;
if (autcon_enb == val)
    return SCPE_OK;
autcon_enb = val;
if (autcon_enb == 0) {
    sim_messagef (SCPE_OK, "Device auto configuration is now disabled.\n");
    sim_messagef (SCPE_OK, "Explicitly setting any address or vector value tells the system\n");
    sim_messagef (SCPE_OK, "that you are planning a specific configuration that may not use\n");
    sim_messagef (SCPE_OK, "use standard values.  You must explicitly specify bus address and\n");
    sim_messagef (SCPE_OK, "vector values for any device you enable or otherwise add to the\n");
    sim_messagef (SCPE_OK, "system configuration after this message is issued.  Changing the\n");
    sim_messagef (SCPE_OK, "number of lines on a terminal multiplexer is such a change.\n");
    sim_messagef (SCPE_OK, "To avoid complexities dealing with this, it is recommended that\n");
    sim_messagef (SCPE_OK, "you configure all devices which can use standard addresses before\n");
    sim_messagef (SCPE_OK, "changing the address or vector for an unusual device situation.\n");
    }
return auto_config (NULL, 0);
}

/* Show autoconfiguration status */

t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, "autoconfiguration ");
fprintf (st, autcon_enb? "enabled": "disabled");
return SCPE_OK;
}

/* Change device address */

t_stat set_addr (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;
uint32 newba;
t_stat r;

if (cptr == NULL)
    return SCPE_ARG;
if ((val == 0) || (uptr == NULL))
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL)
    return SCPE_IERR;
newba = (uint32) get_uint (cptr, DEV_RDX, IOPAGEBASE+IOPAGEMASK, &r); /* get new */
if (r != SCPE_OK)
    return r;
if ((newba < IOPAGEBASE) ||                             /* > IO page base? */
    (newba % ((uint32) val)))                           /* check modulus */
    return SCPE_ARG;
dibp->ba = newba;                                       /* store */
set_autocon (NULL, 0, NULL, NULL);                      /* autoconfig off */
return SCPE_OK;
}

/* Show device address */

t_stat show_addr (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
DEVICE *dptr;
DIB *dibp;
uint32 radix = DEV_RDX;

if (uptr == NULL)
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if ((dibp == NULL) || (dibp->ba < IOPAGEBASE))
    return SCPE_IERR;
if ((sim_switches & SWMASK('H')) || (sim_switch_number == 16))
    radix = 16;
if ((sim_switches & SWMASK('O')) || (sim_switch_number == 8))
    radix = 8;
fprintf (st, "address=");
fprint_val (st, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT);
if (radix != DEV_RDX) {
    fprintf (st, "(");
    fprint_val (st, (t_value) dibp->ba, radix, 32, PV_LEFT);
    fprintf (st, ")");
    }
if (dibp->lnt > 1) {
    fprintf (st, "-");
    fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, DEV_RDX, 32, PV_LEFT);
    if (radix != DEV_RDX) {
        fprintf (st, "(");
        fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, radix, 32, PV_LEFT);
        fprintf (st, ")");
        }
    }
if (dibp->ba < IOPAGEBASE + AUTO_CSRBASE + AUTO_CSRMAX)
    fprintf (st, "*");
return SCPE_OK;
}

/* Set address floating */

t_stat set_addr_flt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;

if (cptr != NULL)
    return SCPE_ARG;
if (uptr == NULL)
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
return auto_config (NULL, 0);                           /* autoconfigure */
}

/* Show device address */

t_stat show_mapped_addr (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, "address=%p-%p", desc, ((const char *)desc) + uptr->capac - 1);
return SCPE_OK;
}

/* Change device vector */

t_stat set_vec (UNIT *uptr, int32 arg, CONST char *cptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;
uint32 newvec;
t_stat r;

if (cptr == NULL)
    return SCPE_ARG;
if (uptr == NULL)
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL)
    return SCPE_IERR;
newvec = (uint32) get_uint (cptr, DEV_RDX, 01000, &r);
if ((r != SCPE_OK) ||
    ((newvec + (dibp->vnum * 4)) >= 01000) ||           /* total too big? */
    (newvec & ((dibp->vnum > 1)? 07: 03)))              /* properly aligned value? */
    return SCPE_ARG;
dibp->vec = newvec;
set_autocon (NULL, 0, NULL, NULL);                      /* autoconfig off */
return SCPE_OK;
}

/* Show device vector */

t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, CONST void *desc)
{
DEVICE *dptr;
DIB *dibp;
uint32 vec, numvec, br_lvl, radix = DEV_RDX;

if (uptr == NULL)
    return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
    return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL)
    return SCPE_IERR;
if ((sim_switches & SWMASK('H')) || (sim_switch_number == 16))
    radix = 16;
if ((sim_switches & SWMASK('O')) || (sim_switch_number == 8))
    radix = 8;
vec = dibp->vec;
if (arg)
    numvec = arg;
else
    numvec = dibp->vnum;
if (vec == 0)
    fprintf (st, "no vector");
else {
#if (VEC_SET != 0)
    vec |= (int_vec_set[dibp->vloc / 32][dibp->vloc % 32] & ~3);
    vec &= (int_vec_set[dibp->vloc / 32][dibp->vloc % 32] | 0x1FF);
#endif
    fprintf (st, "vector=");
    fprint_val (st, (t_value) vec, DEV_RDX, 16, PV_LEFT);
    if (radix != DEV_RDX) {
        fprintf (st, "(");
        fprint_val (st, (t_value) vec, radix, 16, PV_LEFT);
        fprintf (st, ")");
        }
    if (numvec > 1) {
        fprintf (st, "-");
        fprint_val (st, (t_value) vec + (4 * (numvec - 1)), DEV_RDX, 16, PV_LEFT);
        if (radix != DEV_RDX) {
            fprintf (st, "(");
            fprint_val (st, (t_value) vec + (4 * (numvec - 1)), radix, 16, PV_LEFT);
            fprintf (st, ")");
            }
        }
    }
if (vec >= ((VEC_SET | AUTO_VECBASE) & ~3))
    fprintf (st, "*");
br_lvl = dibp->vloc / 32;
if (br_lvl < 4)                                         /* VAXen do 0-3, others 4-7 */
    br_lvl = br_lvl + 4;
fprintf (st, ", BR%d", br_lvl);
return SCPE_OK;
}

/* Show vector for terminal multiplexor */

t_stat show_vec_mux (FILE *st, UNIT *uptr, int32 arg, CONST void *desc)
{
const TMXR *mp = (const TMXR *) desc;

if ((mp == NULL) || (arg == 0))
    return SCPE_IERR;
return show_vec (st, uptr, ((mp->lines * 2) / arg), desc);
}

/* Init Unibus tables */

void init_ubus_tab (void)
{
size_t i, j;

build_vector_tab ();
for (i = 0; i < IPL_HLVL; i++) {                        /* clear intr tab */
    for (j = 0; j < 32; j++) {
        int_vec[i][j] = 0;
        int_ack[i][j] = NULL;
        }
    }
for (i = 0; i < (IOPAGESIZE >> 1); i++) {               /* clear dispatch tab */
    iodispR[i] = NULL;
    iodispW[i] = NULL;
    iodibp[i] = NULL;
    }
return;
}

/* Build Unibus tables */

t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp)
{
int32 i, idx, vec, hivec, ilvl, ibit;
DEVICE *cdptr;
size_t j;
const char *cdname;

if ((dptr == NULL) || (dibp == NULL))                   /* validate args */
    return SCPE_IERR;
dibp->dptr = dptr;                                      /* save back pointer */
if (dibp->vnum > VEC_DEVMAX)
    return SCPE_IERR;
vec = dibp->vec;
#if (VEC_SET != 0)
ilvl = dibp->vloc / 32;
ibit = dibp->vloc % 32;
if (vec)
    vec |= (int_vec_set[ilvl][ibit] & ~3);
#endif
/* hivec & cdhivec are first vector AFTER device */
hivec = vec + (dibp->vnum * 4 * (dibp->ulnt? dibp->lnt/dibp->ulnt:
                                 (dptr->numunits? dptr->numunits: 1)));
/* Check for vector conflict with any other enabled device.
 * Skip vector checks if device (currently) doesn't have a vector assigned.
 * Also skip if power-up reset to allow for auto-configure.
 */
if (vec && !(sim_switches & SWMASK ('P'))) {
    for (j = 0; vec && (cdptr = sim_devices[j]) != NULL; j++) {
        DIB *cdibp = (DIB *)(cdptr->ctxt);
        int32 cdvec, cdhivec;

        if (!cdibp || (cdptr->flags & DEV_DIS)) {
            continue;
            }
        cdvec = cdibp->vec;
#if (VEC_SET != 0)
        ilvl = cdibp->vloc / 32;
        ibit = cdibp->vloc % 32;
        if (cdvec)
            cdvec |= (int_vec_set[ilvl][ibit] & ~3);
#endif
        cdhivec = cdvec + (cdibp->vnum * 4 * 
                           (cdibp->ulnt? cdibp->lnt/cdibp->ulnt:
                            (cdptr->numunits? cdptr->numunits: 1)));
        if (cdptr == dptr || !cdvec || !dibp->vnum) {
            continue;
            }
        if (hivec <= cdvec || vec >= cdhivec) {
            continue;
            }
        cdname = cdptr? sim_dname(cdptr): NULL;
        if (!cdname) {
            cdname = "CPU";
        }
        return sim_messagef (SCPE_STOP, (DEV_RDX == 16) ? 
                                        "Device %s interrupt vector conflict with %s at 0x%X\n" :
                                        "Device %s interrupt vector conflict with %s at 0%o\n",
                             sim_dname (dptr), cdname, (int)dibp->vec);
        }
    }
/* Interrupt slot assignment and conflict check. */
for (i = 0; i < dibp->vnum; i++) {                      /* loop thru vec */
    idx = dibp->vloc + i;                               /* vector index */
    vec = dibp->vec? (dibp->vec + (i * 4)): 0;          /* vector addr */
    ilvl = idx / 32;
    ibit = idx % 32;
#if (VEC_SET != 0)
    if (vec)
        vec |= (int_vec_set[ilvl][ibit] & ~3);
#endif
    if ((int_ack[ilvl][ibit] && dibp->ack[i] &&         /* conflict? */
        (int_ack[ilvl][ibit] != dibp->ack[i])) ||
        (int_vec[ilvl][ibit] && vec &&
        (int_vec[ilvl][ibit] != vec))) {
        return sim_messagef (SCPE_STOP, "Device %s interrupt slot conflict at %d\n",
                             sim_dname (dptr), idx);
        }
    if (dibp->ack[i])
        int_ack[ilvl][ibit] = dibp->ack[i];
    else {
        if (vec)
            int_vec[ilvl][ibit] = vec;
        }
    }
/* Register(Deregister) I/O space address and check for conflicts */
for (i = 0; i < (int32) dibp->lnt; i = i + 2) {         /* create entries */
    idx = ((dibp->ba + i) & IOPAGEMASK) >> 1;           /* index into disp */
    if ((iodispR[idx] && dibp->rd &&                    /* conflict? */
        (iodispR[idx] != dibp->rd)) ||
        (iodispW[idx] && dibp->wr &&
        (iodispW[idx] != dibp->wr))) {
        for (j = 0; (cdptr = sim_devices[j]) != NULL; j++) { /* Find conflicting device */
            DIB *cdibp = (DIB *)(cdptr->ctxt);
            if ((cdptr->flags & DEV_DIS) || !cdibp || cdibp == dibp) {
                continue;
                }
            if ((iodispR[idx] && dibp->rd &&
                (iodispR[idx] != dibp->rd) &&
                (cdibp->rd == iodispR[idx])) ||
                (iodispW[idx] && dibp->wr &&
                (iodispW[idx] != dibp->wr) &&
                (cdibp->wr == iodispW[idx]))) {
                break;
                }
            }
        cdname = cdptr? sim_dname(cdptr): NULL;
        if (!cdname) {
            cdname = "CPU";
            }
        return sim_messagef (SCPE_STOP, (DEV_RDX == 16) ? 
                                        "Device %s address conflict with %s at 0x%X\n" :
                                        "Device %s address conflict with %s at 0%o\n",
                             sim_dname (dptr), cdname, (int)dibp->ba);
        }
    if ((dibp->rd == NULL) && (dibp->wr == NULL) && (dibp->vnum == 0)) {
        iodibp[idx] = NULL;                         /* deregister DIB */
        iodispR[idx] = NULL;                        /* and related dispatches */
        iodispW[idx] = NULL;
        }
    else {
        if (dibp->rd)
            iodispR[idx] = dibp->rd;                /* set rd dispatch */
        if (dibp->wr)
            iodispW[idx] = dibp->wr;                /* set wr dispatch */
        iodibp[idx] = dibp;                         /* remember DIB */
        }
    }
return SCPE_OK;
}

/* Show IO space */

t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
uint32 i, j;
DEVICE *dptr;
DIB *dibp;
uint32 maxaddr, maxname, maxdev;
int32 maxvec, vecwid;
int32 brbase = 0;
uint32 rdx = DEV_RDX;
char valbuf[40];
const char *vec_fmt = NULL;
char vec_fmt_buf[16];

if ((sim_switches & SWMASK('O')) || (sim_switch_number == 8))
    rdx = 8;
if ((sim_switches & SWMASK('H')) || (sim_switch_number == 16))
    rdx = 16;
vec_fmt = (rdx == 16) ? "X" : "o";

if (build_dib_tab ())                                   /* build IO page */
    return SCPE_OK;

maxaddr = 0;
maxvec = 0;
maxname = 0;
maxdev = 1;
#if defined (VM_VAX)
brbase = 4;
#endif

for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) {  /* loop thru entries */
    size_t l;
    if (iodibp[i] && (iodibp[i] != dibp)) {             /* new block? */
        dibp = iodibp[i];                               /* DIB for block */
        dptr = dibp->dptr;
        if ((dibp->ba+ dibp->lnt - 1) > maxaddr)
            maxaddr = dibp->ba+ dibp->lnt - 1;
        if (dibp->vec > maxvec)
            maxvec = dibp->vec;
        l = strlen (dptr? sim_dname (dptr): "CPU");
        if (l>maxname)
            maxname = (int32)l;
        j = (dibp->ulnt? dibp->lnt/dibp->ulnt:
             (dptr? dptr->numunits: 1));
        if (j > maxdev)
            maxdev = j;
        }                                               /* end if */
    }                                                   /* end for i */
maxaddr = fprint_val (NULL, (t_value) dibp->ba, rdx, 32, PV_LEFT);
sprintf (vec_fmt_buf, "%s%s", "%03", vec_fmt);
sprintf (valbuf, vec_fmt_buf, maxvec);
vecwid = maxvec = (int32) strlen (valbuf);
if (vecwid < 3)
    vecwid = 3;
sprintf (valbuf, "%u", maxdev);
maxdev = (uint32)strlen (valbuf);

j = strlen ("Address");
i = (maxaddr*2)+3+1;
if (i <= j)
    i = 0;
else
    i -= j;
maxaddr = i+j;
fprintf (st, "%*.*sAddress%*.*s", i/2, i/2, " ", (i/2)+i%2, (i/2)+i%2, " ");

j = strlen ("Vector");
i = ((maxvec*2)+1+1);
if (i <= j)
    i = 0;
else
    i -= j;
maxvec = i+j;
fprintf (st, " %*.*sVector%*.*s", i/2, i/2, " ", (i/2)+i%2, (i/2)+i%2, " ");

fprintf (st, " BR %*.*s# Device\n", (maxdev -1), (maxdev-1), " ");
for (i = 0; i < maxaddr; i++)
    fputc ('-', st);
fprintf (st, " ");
for (i = 0; i < (uint32)maxvec; i++)
    fputc ('-', st);

fprintf (st, " -- ");
for (i=0; i < maxdev; i++) {
    fputc ('-', st);
}
fputc (' ', st);

i = strlen ("Device");
if (maxname < i)
    maxname = i;

for (i = 0; i < maxname; i++)
    fputc ('-', st);
fputc ('\n', st);

for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) {  /* loop thru entries */
    if (iodibp[i] && (iodibp[i] != dibp)) {             /* new block? */
        dibp = iodibp[i];                               /* DIB for block */
        dptr = dibp->dptr;                              /* locate device */
        fprint_val (st, (t_value) dibp->ba, rdx, 32, PV_LEFT);
        fprintf (st, " - ");
        fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, rdx, 32, PV_LEFT);
        fprintf (st, "%c ",                        /* print block entry */
            (dibp->ba < IOPAGEBASE + AUTO_CSRBASE + AUTO_CSRMAX)? '*': ' ');
        if (dibp->vec == 0)
            fprintf (st, "%*s", ((vecwid*2)+1+1), " ");
        else {
            sprintf (vec_fmt_buf, "%s%s", "%0*", vec_fmt);
            fprintf (st, vec_fmt_buf, vecwid, dibp->vec);
            if (dibp->vnum > 1) {
                sprintf (vec_fmt_buf, "%s%s", "-%0*", vec_fmt);
                fprintf (st, vec_fmt_buf, vecwid, dibp->vec + (4 *
                (dibp->ulnt? dibp->lnt/dibp->ulnt:
                                            (dptr? dptr->numunits: 1)) * dibp->vnum) - 4);
                }
            else
                fprintf (st, " %*s", vecwid, " ");
            fprintf (st, "%1s", (dibp->vnum >= AUTO_VECBASE)? "*": " ");
            }
        if (dibp->vnum || dibp->vloc)
            fprintf (st, " %2u", brbase + dibp->vloc/32);
        else
            fprintf (st, "   ");
        fprintf (st, " %*u %s\n", maxdev,  (dibp->ulnt? dibp->lnt/dibp->ulnt:
                                            (dptr? dptr->numunits: 1)),
                 dptr? sim_dname (dptr): "CPU");
        }                                               /* end if */
    }                                                   /* end for i */
return SCPE_OK;
}

static t_bool _map_description (char *buf, size_t buf_size, uint32 val, uint32 index, uint32 valid_mask)
{
t_bool ind_eq = (index == (val & ~valid_mask));
const char *desc = ind_eq ? "Value == Index" : "";
const char *valid = (val & valid_mask) ? "Valid" : "";

*buf = '\0';
if (*desc || *valid)
    snprintf (buf, buf_size, " (%s%s%s)", valid, (*valid && *desc) ? ", " : "", desc);
return ind_eq;
}

/* Display bus map registers */

t_stat show_bus_map (FILE *st, const char *cptr, uint32 *busmap, uint32 nmapregs, const char *busname, uint32 mapvalid)
{
t_stat r;
uint32 mr;
uint32 mstart = 0;
uint32 mend = nmapregs - 1;
uint32 same_val;
uint32 same_start;
t_bool ind_eq;
char same_desc[32];
char desc[32];

if (cptr) {
    mstart = mend = (uint32) get_uint (cptr, 16, nmapregs - 1, &r);
    if (r != SCPE_OK)
        return sim_messagef (SCPE_ARG, "Invalid %s Map Register: %s\n", busname, cptr);
    }
_map_description (desc, sizeof (desc), busmap[mstart], mstart, mapvalid);
fprintf (st, "%s-MAP[%04X] = %08X%s\n", busname, mstart, busmap[mstart], desc);
same_start = mstart;
same_val = busmap[mstart];
strcpy (same_desc, desc);
for (mr = mstart + 1; mr <= mend; mr++) {
    ind_eq = _map_description (desc, sizeof (desc), busmap[mr], mr, mapvalid);
    if (((same_val == busmap[mr]) && (0 == strcmp (desc, same_desc))) ||
        (ind_eq && (0 == strcmp (desc, same_desc)))) {
        same_val = busmap[mr];
        strcpy (same_desc, desc);
        continue;
        }
    if (same_start != mr - 1) {
        if (same_start + 1 == mr - 1)
            fprintf (st, "%s-MAP[%04X] same as above\n", busname, same_start + 1);
        else
            fprintf (st, "%s-MAP[%04X thru %04X] same as above\n", busname, same_start + 1, mr - 1);
        }
    fprintf (st, "%s-MAP[%04X] = %08X%s\n", busname, mr, busmap[mr], desc);
    same_start = mr;
    same_val = busmap[mr];
    strcpy (same_desc, desc);
    }
if ((same_start != mend) ||
    (0 != strcmp (same_desc, desc)))
    fprintf (st, "%s-MAP[%04X thru %04X] same as above\n", busname, same_start + 1, mend);
return SCPE_OK;
}

/* Autoconfiguration

   The table reflects the MicroVAX 3900 microcode, with one field 
   addition:
      a valid flag marking the end of the list when the value is -1

   The table has been reviewed, extended and updated to reflect the 
   contents of the auto configure table in VMS sysgen (V5.5-2)

   A minus number of vectors indicates a field that should be 
   calculated but not placed in the DIB (RQ, TQ dynamic vectors)

   An amod value of 0 indicates that all addresses are FIXED
   An vmod value of 0 indicates that all vectors are FIXED */


typedef struct {
    const char  *dnam[AUTO_MAXC];
    int32       valid;
    int32       numv;
    uint32      amod;
    uint32      vmod;
    uint32      fixa[AUTO_MAXC];
    uint32      fixv[AUTO_MAXC];
    } AUTO_CON;

AUTO_CON auto_tab[] = {/*c  #v  am vm  fxa   fxv */
    { { "QBA" },         1,  0,  0, 0, 
        {017500} },                                     /* doorbell - fx CSR, no VEC */
    { { "MCTL" },        1,  0,  0, 0, 
        {012100} },                                     /* MSV11-P/MSV11-Q - fx CSR, no VEC */
    { { "KE" },          1,  0,  0, 0, 
        {017300} },                                     /* KE11-A - fx CSR, no VEC */
    { { "KG" },          1,  0,  0, 0, 
        {010700} },                                     /* KG11-A - fx CSR, no VEC */
    { { "RHA" },         1,  1,  0, 0, 
        {016700}, {0254} },                             /* RH11/RH70 - fx CSR, fx VEC */
    { { "RR" },          1,  1,  0, 0, 
        {016700}, {0254} },                             /* RP11/-C/-E - fx CSR, fx VEC */
    { { "RHB" },         1,  1,  0, 0, 
        {012440}, {0224} },                             /* RH11/RH70 - fx CSR, fx VEC */
    { { "RHC" },  1,  1,  0, 0, 
        {012040}, {0204} },                             /* RH11/RH70 - fx CSR, fx VEC */
    { { "CLK" },         1,  1,  0, 0, 
        {017546}, {0100} },                             /* KW11L - fx CSR, fx VEC */
    { { "PCLK" },        1,  1,  0, 0, 
        {012540}, {0104} },                             /* KW11P - fx CSR, fx VEC */
    { { "PTR" },         1,  1,  0, 0, 
        {017550}, {0070} },                             /* PC11 reader - fx CSR, fx VEC */
    { { "PTP" },         1,  1,  0, 0, 
        {017554}, {0074} },                             /* PC11 punch - fx CSR, fx VEC */
    { { "RK" },          1,  1,  0, 0, 
        {017400}, {0220} },                             /* RK11 - fx CSR, fx VEC */
    { { "TM" },          1,  1,  0, 0, 
        {012520}, {0224} },                             /* TM11 - fx CSR, fx VEC */
    { { "RC" },          1,  1,  0, 0, 
        {017440}, {0210} },                             /* RC11 - fx CSR, fx VEC */
    { { "RF" },          1,  1,  0, 0, 
        {017460}, {0204} },                             /* RF11 - fx CSR, fx VEC */
    { { "CR" },          1,  1,  0, 0, 
        {017160}, {0230} },                             /* CR11 - fx CSR, fx VEC */
    { { "HK" },          1,  1,  0, 0, 
        {017440}, {0210} },                             /* RK611 - fx CSR, fx VEC */
    { { "LPT" },         1,  1,  0, 0, 
        {017514, 004004, 004014, 004024, 004034}, 
        {0200,     0170,   0174,   0270,   0274} },     /* LP11 - fx CSR, fx VEC */
    { { "RB" },          1,  1,  0, 0, 
        {015606}, {0250} },                             /* RB730 - fx CSR, fx VEC */
    { { "RL" },          1,  1,  0, 0, 
        {014400}, {0160} },                             /* RL11 - fx CSR, fx VEC */
    { { "DCI" },         1,  2,  0, 8, 
        {014000, 014010, 014020, 014030, 
         014040, 014050, 014060, 014070, 
         014100, 014110, 014120, 014130, 
         014140, 014150, 014160, 014170, 
         014200, 014210, 014220, 014230, 
         014240, 014250, 014260, 014270,
         014300, 014310, 014320, 014330, 
         014340, 014350, 014360, 014370} },             /* DC11 - fx CSRs */
    { { NULL },          1,  1,  0, 4, 
        {015200, 015210, 015220, 015230, 
         015240, 015250, 015260, 015270,
         015300, 015310, 015320, 015330,
         015340, 015350, 015360, 015370} },             /* DN11 - fx CSRs */
    { { NULL },          1,  1,  0, 4, 
        {010500, 010510, 010520, 010530, 
         010540, 010550, 010560, 010570, 
         010600, 010610, 010620, 010630, 
         010640, 010650, 010660, 010670} },             /* DM11B - fx CSRs */
    { { NULL },          1,  2,  0, 8, 
        {007600, 007570, 007560, 007550, 
         007540, 007530, 007520, 007510,
         007500, 007470, 007460, 007450,
         007440, 007430, 007420, 007410} },             /* DR11C - fx CSRs */
    { { NULL },          1,  1,  0, 8, 
        {012600, 012604, 012610, 012614, 
         012620, 012624, 012620, 012624} },             /* PR611 - fx CSRs */
    { { NULL },          1,  1,  0, 8, 
        {017420, 017422, 017424, 017426, 
         017430, 017432, 017434, 017436} },             /* DT11 - fx CSRs */
    { { NULL },          1,  2,  0, 8,
      {016200, 016240} },                               /* DX11 */
    { { "TDC", "DLI" },  1,  2,  0, 8, 
        {016500, 016510, 016520, 016530,
         016540, 016550, 016560, 016570,
         016600, 016610, 016620, 016630,
         016740, 016750, 016760, 016770} },             /* KL11/DL11/DLV11/TU58 - fx CSRs */
    { { NULL },          1,  2,  0, 8, { 0 } },         /* DLV11J - fx CSRs */
    { { NULL },          1,  2,  8, 8 },                /* DJ11 */
    { { "DH" },          1,  2, 16, 8 },                /* DH11 */
    { { "VT" },          1,  4,  0, 8,
      {012000, 012010, 012020, 012030} },               /* VT11/GT40 - fx CSRs  */
    { { "VS60" },        1,  4,  0, 8,
      {012000} },                                       /* VS60/GT48 - fx CSRs  */
    { { NULL },          1,  2,  0, 8,
      {010400} },                                       /* LPS11 */
    { { NULL },          1,  2,  8, 8 },                /* DQ11 */
    { { NULL },          1,  2,  0, 8,
      {012400} },                                       /* KW11W */
    { { NULL },          1,  2,  8, 8 },                /* DU11 */
    { { "DUP" },         1,  2,  8, 8 },                /* DUP11 */
    { { NULL },          1,  3,  0, 8,
      {015000, 015040, 015100, 015140, }},              /* DV11 */
    { { NULL },          1,  2,  8, 8 },                /* LK11A */
    { { "DMC" }, 
                         1,  2,  8, 8 },                /* DMC11 */
    { { "DZ" },          1,  2,  8, 8 },                /* DZ11 */
    { { "KDP" },         1,  2,  8, 8 },                /* KMC11 */
    { { NULL },          1,  2,  8, 8 },                /* LPP11 */
    { { NULL },          1,  2,  8, 8 },                /* VMV21 */
    { { NULL },          1,  2, 16, 8 },                /* VMV31 */
    { { NULL },          1,  2,  8, 8 },                /* DWR70 */
    { { "RL", "RLB"},    1,  1,  8, 4, 
        {014400}, {0160} },                             /* RL11 */
    { { "TS", "TSB", "TSC", "TSD"}, 
                         1,  1,  0, 4,                  /* TS11 */
        {012520, 012524, 012530, 012534},
        {0224} },
    { { NULL },          1,  2, 16, 8,
        {010460} },                                     /* LPA11K */
    { { NULL },          1,  2,  8, 8 },                /* KW11C */
    { { NULL },          1,  1,  8, 8 },                /* reserved */
    { { "RX", "RY" },    1,  1,  8, 4, 
        {017170} , {0264} },                            /* RX11/RX211 */
    { { NULL },          1,  1,  8, 4 },                /* DR11W */
    { { NULL },          1,  1,  8, 4, 
        {012410, 012410}, {0124} },                     /* DR11B - fx CSRs,vec */
    { { "DMP" },         1,  2,  8, 8 },                /* DMP11 */
    { { "DPV" },         1,  2,  8, 8 },                /* DPV11 */
    { { NULL },          1,  2,  8, 8 },                /* ISB11 */
    { { NULL },          1,  2, 16, 8 },                /* DMV11 */
    { { "XU", "XUB" },   1,  1,  8, 4, 
        {014510}, {0120} },                             /* DEUNA */
    { { "XQ", "XQB" },   1, -1,  0, 4,
        {014440, 014460, 014520, 014540}, {0120} },     /* DEQNA */
    { { "RQ", "RQB", "RQC", "RQD" }, 
                         1, -1,  4, 4,                  /* RQDX3 */
        {012150}, {0154} },
    { { NULL },          1,  8, 32, 4 },                /* DMF32 */
    { { NULL },          1,  3, 16, 8 },                /* KMS11 */
    { { NULL },          1,  2,  0, 8,
        {004200, 004240, 004300, 004340} },             /* PLC11 */
    { { "UW" },          1,  1, 16, 4 },                /* VS100 */
    { { "TQ", "TQB" },   1, -1,  4, 4, 
        {014500}, {0260} },                             /* TQK50 */
    { { NULL },          1,  2, 16, 8 },                /* KMV11 */
    { { NULL },          1,  2,  0, 8,
        {004400, 004440, 004500, 004540} },             /* KTC32 */
    { { NULL },          1,  2,  0, 8,
        {004100} },                                     /* IEQ11 */
    { { "VH" },          1,  2, 16, 8 },                /* DHU11/DHQ11 */
    { { NULL },          1,  6, 32, 4 },                /* DMZ32 */
    { { NULL },          1,  6, 32, 4 },                /* CP132 */
    { { "TC" },          1,  1,  0, 0,
        {017340}, {0214} },                             /* TC11 */
    { { "TA" },          1,  1,  0, 0,
        {017500}, {0260} },                             /* TA11 */
    { { "QVSS" },        1,  2, 64, 8, 
        {017200} },                                     /* QVSS - fx CSR */
    { { NULL },          1,  1,  8, 4 },                /* VS31 */
    { { NULL },          1,  1,  0, 4,
        {016200} },                                     /* LNV11 - fx CSR */
    { { NULL },          1,  1, 16, 4 },                /* LNV21/QPSS */
    { { NULL },          1,  1,  8, 4, 
        {012570} },                                     /* QTA - fx CSR */
    { { NULL },          1,  1,  8, 4 },                /* DSV11 */
    { { NULL },          1,  2,  8, 8 },                /* CSAM */
    { { NULL },          1,  2,  8, 8 },                /* ADV11C */
    { { NULL },          1,  0,  8, 8, 
        {010440} },                                     /* AAV11/AAV11C */
    { { NULL },          1,  2,  8, 8, 
        {016400}, {0140} },                             /* AXV11C - fx CSR,vec */
    { { NULL },          1,  2,  4, 8, 
        {010420} },                                     /* KWV11C - fx CSR */
    { { NULL },          1,  2,  8, 8, 
        {016410} },                                     /* ADV11D - fx CSR */
    { { NULL },          1,  2,  8, 8, 
        {016420} },                                     /* AAV11D - fx CSR */
    { { "QDSS" },        1,  3,  0, 16,
        {017400, 017402, 017404, 017406, 
         017410, 017412, 017414, 017416} },             /* VCB02 - QDSS - fx CSR */
    { { NULL },          1, 16,  0, 4, 
        {004160, 004140, 004120} },                     /* DRV11J - fx CSR */
    { { NULL },          1,  2, 16, 8 },                /* DRQ3B */
    { { NULL },          1,  1,  8, 4 },                /* VSV24 */
    { { NULL },          1,  1,  8, 4 },                /* VSV21 */
    { { NULL },          1,  1,  8, 4 },                /* IBQ01 */
    { { NULL },          1,  1,  8, 8 },                /* IDV11A */
    { { NULL },          1,  0,  8, 8 },                /* IDV11B */
    { { NULL },          1,  0,  8, 8 },                /* IDV11C */
    { { NULL },          1,  1,  8, 8 },                /* IDV11D */
    { { NULL },          1,  2,  8, 8 },                /* IAV11A */
    { { NULL },          1,  0,  8, 8 },                /* IAV11B */
    { { NULL },          1,  2,  8, 8 },                /* MIRA */
    { { NULL },          1,  2, 16, 8 },                /* IEQ11 */
    { { NULL },          1,  2, 32, 8 },                /* ADQ32 */
    { { NULL },          1,  2,  8, 8 },                /* DTC04, DECvoice */
    { { NULL },          1,  1, 32, 4 },                /* DESNA */
    { { NULL },          1,  2,  4, 8 },                /* IGQ11 */
    { { NULL },          1,  2, 32, 8 },                /* KMV1F */
    { { NULL },          1,  1,  8, 4 },                /* DIV32 */
    { { NULL },          1,  2,  4, 8 },                /* DTCN5, DECvoice */
    { { NULL },          1,  2,  4, 8 },                /* DTC05, DECvoice */
    { { NULL },          1,  2,  8, 8 },                /* KWV32 (DSV11) */
    { { NULL },          1,  1, 64, 4 },                /* QZA */
    { { "CH" },          1,  1,  0, 0, 
        {04140}, {0270} },                              /* CH11 - CHAOS Net - fx CSR, fx VEC */
    { { "NG" },          1,  1,  0, 0, 
        {04040}, {0270} },                              /* NG - vector display */
    { { "DAZ" },         1,  1,  0, 0, 
        {00104} },                                      /* DAZ */
    { { "TV" },          1,  0,  0, 0, 
        {04100} },                                      /* TV - raster display */
    { { "MB" },          1,  1,  0, 0, 
        {04000}, {0374} },                              /* MB11 */
    { { NULL },         -1 }                            /* end table */
};

#if !defined(DEV_NEXUS) 
#if defined(DEV_MBUS)
#define DEV_NEXUS DEV_MBUS
#else
#define DEV_NEXUS 0
#endif
#endif

#define MAX(a,b) (((a)>(b))? (a) : (b))

static void build_vector_tab (void)
{
static t_bool done = FALSE;
AUTO_CON *autp;
DEVICE *dptr;
uint32 j, k;

if (done)
    return;
/* Locate all Unibus/Qbus devices and make sure vector masks are set */
for (j = 0; (dptr = sim_devices[j]) != NULL; j++) {
    if ((dptr->flags & (DEV_UBUS | DEV_QBUS)) == 0)
        continue;
    for (autp = auto_tab; autp->valid >= 0; autp++) {
        for (k=0; autp->dnam[k]; k++) {
            if (!strcmp(dptr->name, autp->dnam[k])) {
#if (VEC_SET != 0)
                int32 ilvl, ibit;
                DIB *dibp;

                dibp = (DIB *)dptr->ctxt;
                ilvl = dibp->vloc / 32;
                ibit = dibp->vloc % 32;
                if (1) {
                    int v;
                    
                    for (v=0; v<MAX(dibp->vnum, 1); v++)
                        int_vec_set[ilvl][ibit+v] = VEC_SET;
                    }
#endif
                break;
                }
            }
        }
    }
done = TRUE;
}

t_stat auto_config (const char *name, int32 nctrl)
{
uint32 csr = IOPAGEBASE + AUTO_CSRBASE;
uint32 vec = AUTO_VECBASE;
int32 numc;
AUTO_CON *autp;
DEVICE *dptr;
DIB *dibp;
uint32 j, k, jena, vmask, amask;

if (autcon_enb == 0)                                    /* enabled? */
    return SCPE_OK;
if (name) {                                             /* updating? */
    dptr = find_dev (name);
    if (dptr == NULL)
        return SCPE_ARG;
    dibp = (DIB *) dptr->ctxt;                          /* get DIB */
    if ((nctrl < 0) || (dibp == NULL))
        return SCPE_ARG;
    dibp->numc = nctrl;
    }
for (autp = auto_tab; autp->valid >= 0; autp++) {       /* loop thru table */
    if (autp->amod) {                                   /* floating csr? */
        amask = autp->amod - 1;
        csr = (csr + amask) & ~amask;                   /* align csr */
        }
    for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) {
        if (autp->dnam[j] == NULL)                      /* no device? */
            break;
        dptr = find_dev (autp->dnam[j]);                /* find ctrl */
        if ((dptr == NULL) ||                           /* enabled, not (nexus or unibus or qbus)? */
            (dptr->flags & DEV_DIS) ||
            (dptr->flags & DEV_NEXUS) ||
            !(dptr->flags & (DEV_UBUS | DEV_QBUS | DEV_Q18)) )
            continue;
        /* Sanity check that enabled devices can work on the current bus */
        if (!((UNIBUS && (dptr->flags & (DEV_UBUS | DEV_Q18))) ||
             ((!UNIBUS) && ((dptr->flags & DEV_QBUS) || 
                            ((dptr->flags & DEV_Q18) && (MEMSIZE <= UNIMEMSIZE)))))) {
            dptr->flags |= DEV_DIS;
            if (sim_switches & SWMASK ('P'))
                continue;
            return sim_messagef (SCPE_NOFNC, "%s device not compatible with system bus\n", sim_dname(dptr));
            }
        dibp = (DIB *) dptr->ctxt;                      /* get DIB */
        if (dibp == NULL)                               /* not there??? */
            return SCPE_IERR;
        numc = dibp->numc ? dibp->numc : 1;
        /* Identify how many devices earlier in the device list are 
           enabled and use that info to determine fixed address assignments */
        for (k=jena=0; k<j; k++) {
            DEVICE *kdptr = find_dev (autp->dnam[k]);
            
            if (kdptr && (!(kdptr->flags & DEV_DIS)))
                jena += ((DIB *)kdptr->ctxt)->numc ? ((DIB *)kdptr->ctxt)->numc : 1;
            }
        if (autp->fixa[jena])                           /* fixed csr avail? */
            dibp->ba = IOPAGEBASE + autp->fixa[jena];   /* use it */
        else {                                          /* no fixed left */
            dibp->ba = csr;                             /* set CSR */
            csr += (numc * autp->amod);                 /* next CSR */
            }                                           /* end else */
        if (autp->numv) {                               /* vec needed? */
            if (autp->fixv[jena]) {                     /* fixed vec avail? */
                if (autp->numv > 0)
                    dibp->vec = autp->fixv[jena];       /* use it */
                }
            else {                                      /* no fixed left */
                uint32 numv = abs (autp->numv);         /* get num vec */
                vmask = autp->vmod - 1;
                vec = (vec + vmask) & ~vmask;           /* align vector */
                if (autp->numv > 0)
                    dibp->vec = vec;                    /* set vector */
                vec += (numc * numv * 4);
                }                                       /* end else */
            }                                           /* end vec needed */
        }                                               /* end for j */
    if (autp->amod)                                     /* flt CSR? gap */
        csr = csr + 2;
    }                                                   /* end for i */
return SCPE_OK;
}

/* Factory bad block table creation routine

   This routine writes a DEC standard 144 compliant bad block table on the
   last track of the specified unit as described in: 
      EL-00144_B_DEC_STD_144_Disk_Standard_for_Recording_and_Handling_Bad_Sectors_Nov76.pdf
   The bad block table consists of 10 repetitions of the same table, 
   formatted as follows:

        words 0-1       pack id number
        words 2-3       cylinder/sector/surface specifications
         :
        words n-n+1     end of table (-1,-1)

   Inputs:
        uptr    =       pointer to unit
        sec     =       number of sectors per surface
        wds     =       number of words per sector
   Outputs:
        sta     =       status code
*/

#include "sim_disk.h"

t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds)
{
return sim_disk_pdp11_bad_block (uptr, sec, wds);
}
