/* sel32_scsi.c: SEL-32 MFP SCSI Disk controller

   Copyright (c) 2018-2023, James C. Bevier
   Portions provided by Richard Cornwell 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.
*/

#include "sel32_defs.h"

/* uncomment to use fast sim_activate times when running UTX */
/* UTX gets an ioi error for dm0801 if slow times are used */
/* dm0801 is not even a valid unit number for UDP controller */
#define FAST_FOR_UTX

#if NUM_DEVS_SCSI > 0

#define UNIT_SCSI   UNIT_ATTABLE|UNIT_DISABLE

extern  uint32  SPAD[];                         /* cpu SPAD memory */

/* useful conversions */
/* Fill STAR value from cyl, trk, sec data */
#define CHS2STAR(c,h,s)         (((c<<16) & LMASK)|((h<<8) & 0xff00)|(s & 0xff))
/* convert STAR value to number of sectors */
#define STAR2SEC(star,spt,spc)  ((star&0xff)+(((star>>8)&0xff)*spt)+((star>>16)*spc))
/* convert STAR value to number of heads or tracks */
#define STAR2TRK(star,tpc)      ((star >> 16) * tpc + ((star >> 8) & 0x0ff))
/* convert STAR value to number of cylinders */
#define STAR2CYL(star)          ((star >> 16) & RMASK)
/* convert byte value to number of sectors mod sector size */
#define BYTES2SEC(bytes,ssize)  (((bytes) + (ssize-1)) >> 10)
/* get sectors per track for specified type */
#define SPT(type)               (scsi_type[type].spt)
/* get sectors per cylinderfor specified type */
#define SPC(type)               (scsi_type[type].spt*scsi_type[type].nhds)
/* get number of cylinders for specified type */
#define CYL(type)               (scsi_type[type].cyl)
/* get number of heads for specified type */
#define HDS(type)               (scsi_type[type].nhds)
/* get disk capacity in sectors for specified type */
#define CAP(type)               (CYL(type)*HDS(type)*SPT(type))
/* get number of bytes per sector for specified type */
#define SSB(type)               (scsi_type[type].ssiz*4)
/* get disk capacity in bytes for specified type */
#define CAPB(type)              (CAP(type)*SSB(type))
/* get disk geometry as STAR value for specified type */
#define GEOM(type)              (CHS2STAR(CYL(type),HDS(type),SPT(type)))

/* INCH command information */
/*
WD 0 - Data address
WD 1 - Flags - 0 -36 byte count

Data - 224 word INCH buffer address (SST)
WD 1 Drive 0 Attribute register
WD 2 Drive 1 Attribute register
WD 3 Drive 2 Attribute register
WD 4 Drive 3 Attribute register
WD 5 Drive 4 Attribute register
WD 6 Drive 5 Attribute register
WD 7 Drive 6 Attribute register
WD 8 Drive 7 Attribute register

Memory attribute register layout
bits 0-7 - Flags
        bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option
        bit  2   - 1=Cartridge module drive
        bit  3   - 0=Reserved
        bit  4   - 1=Drive not present
        bit  5   - 1=Dual Port
        bit  6   - 0=Blk size   00=768 byte blk
        bit  7   - 0=Blk size   01=1024 byte blk
bits 8-15 - sector count (sectors per track)(F16=16, F20=20)
bits 16-23 - MHD Head count (number of heads on MHD)
bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of
    mini-module)
*/


/* 224 word INCH Buffer layout */
/* 128 word subchannel status storage (SST) */
/*  66 words of program status queue (PSQ) */
/*  26 words of scratchpad */
/*   4 words of label buffer registers */

/* track label / sector label definations */
/*
    short lcyl;         cylinder
    char ltkn;          track
    char lid;           sector id
    char lflg1;         track/sector status flags
        bit 0           good
            1           alternate
            2           spare
            3           reserved
            4           flaw
            5           last track
            6           start of alternate
    char lflg2;
    short lspar1;
    short lspar2;
    short ldef1;
    int ldeallp;        DMAP block number trk0
    int lumapp;         UMAP block number sec1
    short ladef3;
    short laltcyl;
    char lalttk;        sectors per track
    char ldscnt;        number of heads
    char ldatrflg;      device attributes
        bit 0           n/u
            1           disk is mhd
            2           n/u
            3           n/u
            4           n/u
            5           dual ported
            6/7         00 768 bytes/blk
                        01 1024 bytes/blk
                        10 2048 bytes/blk
    char ldatrscnt;     sectors per track (again)
    char ldatrmhdc;     MHD head count
    char ldatrfhdc;     FHD head count
 */

#define CMD     u3
/* u3 */
/* in u3 is device command code and status */
#define DSK_CMDMSK      0x00ff                  /* Command being run */
#define DSK_STAR        0x0100                  /* STAR value in u4 */
#define DSK_NU          0x0200                  /* Not used */
#define DSK_READDONE    0x0400                  /* Read finished, end channel */
#define DSK_ENDDSK      0x0800                  /* Sensed end of disk */
#define DSK_SEEKING     0x1000                  /* Disk is currently seeking */
#define DSK_READING     0x2000                  /* Disk is reading data */
#define DSK_WRITING     0x4000                  /* Disk is writing data */
#define DSK_BUSY        0x8000                  /* Disk is busy */
/* commands */
#define DSK_INCH        0x00                    /* Initialize channel */
#define DSK_INCH2       0xF0                    /* Initialize channel for processing */
#define DSK_WD          0x01                    /* Write data */
#define DSK_RD          0x02                    /* Read data */
#define DSK_NOP         0x03                    /* No operation */
#define DSK_SNS         0x04                    /* Sense */
#define DSK_SCK         0x07                    /* Seek cylinder, track, sector */
#define DSK_TIC         0x08                    /* Transfer in channel */
//#define DSK_FNSK        0x0B                    /* Format for no skip */
#define DSK_RBLK        0x13                    /* Reassign Block */
#define DSK_LMR         0x1F                    /* Load mode register */
#define DSK_RWD         0x23                    /* Rewind */
//#define DSK_WSL         0x31                    /* Write sector label */
//#define DSK_RSL         0x32                    /* Read sector label */
//#define DSK_REL         0x33                    /* Release */
#define DSK_XEZ         0x37                    /* Rezero */
//#define DSK_ADV         0x43                    /* Advance Record */
//#define DSK_IHA         0x47                    /* Increment head address */
//#define DSK_SRM         0x4F                    /* Set reserve track mode */
//#define DSK_WTL         0x51                    /* Write track label */
//#define DSK_RTL         0x52                    /* Read track label */
#define DSK_RCAP        0x53                    /* Read Capacity */
//#define DSK_XRM         0x5F                    /* Reset reserve track mode */
//#define DSK_RAP         0xA2                    /* Read angular positions */
#define DSK_RES         0xA3                    /* Reserve Unit */
//#define DSK_TESS        0xAB                    /* Test STAR (subchannel target address register) */
#define DSK_INQ         0xB3                    /* Inquiry */
#define DSK_REL         0xC3                    /* Release Unit */
#define DSK_TCMD        0xD3                    /* Transfer Command Packet (specifies CDB to send) */
//#define DSK_ICH         0xFF                    /* Initialize Controller */
#define DSK_FRE         0xF3                    /* Reserved */
#define DSK_SID         0x80                    /* MFP status command */

#define STAR    u4
/* u4 - sector target address register (STAR) */
/* Holds the current cylinder, head(track), sector */
#define DISK_CYL        0xFFFF0000              /* cylinder mask */
#define DISK_TRACK      0x0000FF00              /* track mask */
#define DISK_SECTOR     0x000000ff              /* sector mask */

#define SNS     u5
/* u5 */
/* Sense byte 0  - mode register */
#define SNS_DROFF       0x80000000              /* Drive Carriage will be offset */
#define SNS_TRKOFF      0x40000000              /* Track offset: 0=positive, 1=negative */
#define SNS_RDTMOFF     0x20000000              /* Read timing offset = 1 */
#define SNS_RDSTRBT     0x10000000              /* Read strobe timing: 1=positive, 0=negative */
#define SNS_DIAGMOD     0x08000000              /* Diagnostic Mode ECC Code generation and checking */
#define SNS_RSVTRK      0x04000000              /* Reserve Track mode: 1=OK to write, 0=read only */
#define SNS_FHDOPT      0x02000000              /* FHD or FHD option = 1 */
#define SNS_TCMD        0x01000000              /* Presessing CMD cmd chain */

/* Sense byte 1 */
#define SNS_CMDREJ      0x800000                /* Command reject */
#define SNS_INTVENT     0x400000                /* Unit intervention required */
#define SNS_SPARE1      0x200000                /* Spare */
#define SNS_EQUCHK      0x100000                /* Equipment check */
#define SNS_DATCHK      0x080000                /* Data Check */
#define SNS_OVRRUN      0x040000                /* Data overrun/underrun */
#define SNS_DSKFERR     0x020000                /* Disk format error */
#define SNS_DEFTRK      0x010000                /* Defective track encountered */

/* Sense byte 2 */
#define SNS_LAST        0x8000                  /* Last track flag encountered */
#define SNS_AATT        0x4000                  /* At Alternate track */
#define SNS_WPER        0x2000                  /* Write protection error */
#define SNS_WRL         0x1000                  /* Write lock error */
#define SNS_MOCK        0x0800                  /* Mode check */
#define SNS_INAD        0x0400                  /* Invalid memory address */
#define SNS_RELF        0x0200                  /* Release fault */
#define SNS_CHER        0x0100                  /* Chaining error */

/* Sense byte 3 */
#define SNS_REVL        0x80                    /* Revolution lost */
#define SNS_DADE        0x40                    /* Disc addressing or seek error */
#define SNS_BUCK        0x20                    /* Buffer check */
#define SNS_ECCS        0x10                    /* ECC error in sector label */
#define SNS_ECCD        0x08                    /* ECC error iin data */
#define SNS_ECCT        0x04                    /* ECC error in track label */
#define SNS_RTAE        0x02                    /* Reserve track access error */
#define SNS_UESS        0x01                    /* Uncorrectable ECC error */

#define CHS     u6
/* u6 holds the current cyl, hd, sec for the drive */

/* this attribute information is provided by the INCH command */
/* for each device and is not used.  It is reconstructed from */
/* the disk_t structure data for the assigned disk */
/*
bits 0-7 - Flags
        bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option
        bit  2   - 1=Cartridge module drive
        bit  3   - 0=Reserved
        bit  4   - 1=Drive not present
        bit  5   - 1=Dual Port
        bit  6   - 0=Reserved  00 768 byte sec
        bit  7   - 0=Reserved  01 1024 byte sec
bits 8-15 - sector count (sectors per track)(F16=16, F20=20)
bits 16-23 - MHD Head count (number of heads on MHD)
bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of
    mini-module)
*/

/* INCH addr    up7 */

/* disk definition structure */
struct scsi_t
{
    const char  *name;                          /* Device ID Name */
    uint16      nhds;                           /* Number of heads */
    uint16      ssiz;                           /* sector size in words */
    uint16      spt;                            /* # sectors per track(head) */
    uint16      ucyl;                           /* Number of cylinders used */
    uint16      cyl;                            /* Number of cylinders on disk */
    uint8       type;                           /* Device type code */
    /* bit 1 mhd */
    /* bits 6/7 = 0 768 byte blk */             /* not used on UDP/DPII */
    /*          = 1 1024 byte blk */            /* not used on UDP/DPII */
}

scsi_type[] =
{
    /* Class F Disc Devices */
    /* MPX SCSI disks for SCSI controller */
    {"SD150",   9, 192, 24,   963,   967, 0x40},   /*0  8820  150M  208872 sec */
    {"SD300",   9, 192, 32,  1405,  1409, 0x40},   /*1  8828  300M  396674 sec */
    {"SD700",  15, 192, 35,  1542,  1546, 0x40},   /*2  8833  700M  797129 sec */
    {"SD1200", 15, 192, 49,  1927,  1931, 0x40},   /*3  8835 1200M 1389584 sec */
    {"SD2400", 19, 192, 59,  2703,  2707, 0x40},   /*4  8842 2400M 2909128 sec */
    {"SH1200", 15, 192, 50,  1868,  1872, 0x40},   /*5  8832 1200M 1395014 sec */
    {"SH2550", 19, 192, 55,  2703,  2707, 0x40},   /*6  8834 2550M 2909128 sec */
    {"SH5150", 21, 192, 83,  3707,  3711, 0x40},   /*7  0000 5150M 5581145 sec */
    {"8820",    9, 256, 18,   963,   967, 0x41},   /*8  8820  150M  156654 sec */
    {"8821",    9, 256, 36,   963,   967, 0x41},   /*9  8828  300M  313308 sec */
    {"8833",   18, 256, 20,  1542,  1546, 0x41},   /*10 8833  700M  556560 sec */
    {"8835",   18, 256, 20,  1927,  1931, 0x41},   /*11 8835 1200M  695160 sec */
    /* For UTX */
    {NULL, 0}
};

t_stat  scsi_preio(UNIT *uptr, uint16 chan);
t_stat  scsi_startcmd(UNIT *uptr, uint16 chan, uint8 cmd);
t_stat  scsi_haltio(UNIT *uptr);
t_stat  scsi_srv(UNIT *);
t_stat  scsi_boot(int32 unitnum, DEVICE *);
void    scsi_ini(UNIT *, t_bool);
t_stat  scsi_rschnlio(UNIT *uptr);
t_stat  scsi_reset(DEVICE *);
t_stat  scsi_attach(UNIT *, CONST char *);
t_stat  scsi_detach(UNIT *);
t_stat  scsi_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat  scsi_get_type(FILE * st, UNIT *uptr, int32 v, CONST void *desc);
t_stat  scsi_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const   char  *scsi_description (DEVICE *dptr);

/* One buffer per unit */
#define BUFFSIZE        (512)
uint8   scsi_buf[NUM_DEVS_SCSI][NUM_UNITS_SCSI][BUFFSIZE];
uint8   scsi_pcmd[NUM_DEVS_SCSI][NUM_UNITS_SCSI];

/* channel program information */
CHANP   sba_chp[NUM_UNITS_SCSI] = {0};

MTAB    scsi_mod[] = {
    {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE",
    &scsi_set_type, &scsi_get_type, NULL, "Type of disk"},
    {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr,
        &show_dev_addr, NULL, "Device channel address"},
    {0}
};

UNIT    sba_unit[] = {
/* SET_TYPE(0) SD150 */
    {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7600)},  /* 0 */
    {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7608)},  /* 1 */
};

DIB     sba_dib = {
    scsi_preio,     /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/  /* Pre Start I/O */
    scsi_startcmd,  /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */
    scsi_haltio,    /* t_stat (*halt_io)(UNIT *uptr) */         /* Halt I/O */
    NULL,           /* t_stat (*stop_io)(UNIT *uptr) */         /* Stop I/O */
    NULL,           /* t_stat (*test_io)(UNIT *uptr) */         /* Test I/O */
    NULL,           /* t_stat (*rsctl_io)(UNIT *uptr) */        /* Reset Controller */
    scsi_rschnlio,  /* t_stat (*rschnl_io)(UNIT *uptr) */       /* Reset Channel */
    NULL,           /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */  /* Process IOCL */
    scsi_ini,       /* void  (*dev_ini)(UNIT *, t_bool) */      /* init function */
    sba_unit,       /* UNIT* units */                           /* Pointer to units structure */
    sba_chp,        /* CHANP* chan_prg */                       /* Pointer to chan_prg structure */
    NULL,           /* IOCLQ *ioclq_ptr */                      /* IOCL entries, 1 per UNIT */
    NUM_UNITS_SCSI, /* uint8 numunits */                        /* number of units defined */
    0x38,           /* uint8 mask */                            /* 8 devices - device mask */
    0x7600,         /* uint16 chan_addr */                      /* parent channel address */
    0,              /* uint32 chan_fifo_in */                   /* fifo input index */
    0,              /* uint32 chan_fifo_out */                  /* fifo output index */
    {0}             /* uint32 chan_fifo[FIFO_SIZE] */           /* interrupt status fifo for channel */
};

DEVICE  sba_dev = {
    "SBA", sba_unit, NULL, scsi_mod,
    NUM_UNITS_SCSI, 16, 24, 4, 16, 32,
    NULL, NULL, &scsi_reset, &scsi_boot, &scsi_attach, &scsi_detach,
    /* ctxt is the DIB pointer */
    &sba_dib, DEV_BUF_NUM(0)|DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug,
    NULL, NULL, &scsi_help, NULL, NULL, &scsi_description
};

#if NUM_DEVS_SCSI > 1

/* channel program information */
CHANP   sbb_chp[NUM_UNITS_SCSI] = {0};

UNIT    sbb_unit[] = {
/* SET_TYPE(0) DM150 */
    {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7640)},  /* 0 */
    {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7648)},  /* 1 */
};

//DIB sdb_dib = {scsi_preio, scsi_startcmd, NULL, NULL, NULL,
//  scsi_ini, sdb_unit, sdb_chp, NUM_UNITS_SCSI, 0x0f, 0x0c00, 0, 0, 0};

DIB     sbb_dib = {
    scsi_preio,     /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/  /* Pre Start I/O */
    scsi_startcmd,  /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */
    scsi_haltio,    /* t_stat (*halt_io)(UNIT *uptr) */         /* Halt I/O */
    NULL,           /* t_stat (*stop_io)(UNIT *uptr) */         /* Stop I/O */
    NULL,           /* t_stat (*test_io)(UNIT *uptr) */         /* Test I/O */
    NULL,           /* t_stat (*rsctl_io)(UNIT *uptr) */        /* Reset Controller */
    scsi_rschnlio,  /* t_stat (*rschnl_io)(UNIT *uptr) */       /* Reset Channel */
    NULL,           /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */  /* Process IOCL */
    scsi_ini,       /* void  (*dev_ini)(UNIT *, t_bool) */      /* init function */
    sbb_unit,       /* UNIT* units */                           /* Pointer to units structure */
    sbb_chp,        /* CHANP* chan_prg */                       /* Pointer to chan_prg structure */
    NULL,           /* IOCLQ *ioclq_ptr */                      /* IOCL entries, 1 per UNIT */
    NUM_UNITS_SCSI, /* uint8 numunits */                        /* number of units defined */
    0x38,           /* uint8 mask */                            /* 2 devices - device mask */
    0x7600,         /* uint16 chan_addr */                      /* parent channel address */
    0,              /* uint32 chan_fifo_in */                   /* fifo input index */
    0,              /* uint32 chan_fifo_out */                  /* fifo output index */
    {0}             /* uint32 chan_fifo[FIFO_SIZE] */           /* interrupt status fifo for channel */
};

DEVICE  sbb_dev = {
    "SBB", sbb_unit, NULL, scsi_mod,
    NUM_UNITS_SCSI, 16, 24, 4, 16, 32,
    NULL, NULL, &scsi_reset, &scsi_boot, &scsi_attach, &scsi_detach,
    /* ctxt is the DIB pointer */
    &sbb_dib, DEV_BUF_NUM(1)|DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug,
    NULL, NULL, &scsi_help, NULL, NULL, &scsi_description
};
#endif

/* convert sector disk address to star values (c,h,s) */
uint32 scsisec2star(uint32 daddr, int type)
{
    int32 sec = daddr % scsi_type[type].spt;    /* get sector value */
    int32 spc = scsi_type[type].nhds * scsi_type[type].spt; /* sec per cyl */
    int32 cyl = daddr / spc;                    /* cylinders */
    int32 hds = (daddr % spc) / scsi_type[type].spt;    /* heads */ 

    /* now return the star value */
    return (CHS2STAR(cyl,hds,sec));             /* return STAR */
}

/* start a disk operation */
t_stat scsi_preio(UNIT *uptr, uint16 chan)
{
    DEVICE      *dptr = get_dev(uptr);
    uint16      chsa = GET_UADDR(uptr->CMD);
    int         unit = (uptr - dptr->units);

    sim_debug(DEBUG_CMD, dptr, "scsi_preio CMD %08x unit=%02x\n", uptr->CMD, unit);
    if ((uptr->CMD & 0xff00) != 0) {            /* just return if busy */
        return SNS_BSY;
    }
    sim_debug(DEBUG_CMD, dptr, "scsi_preio unit %02x chsa %04x OK\n", unit, chsa);
    return 0;                                   /* good to go */
}

t_stat scsi_startcmd(UNIT *uptr, uint16 chan,  uint8 cmd)
{
    uint16      chsa = GET_UADDR(uptr->CMD);
    DEVICE      *dptr = get_dev(uptr);
    int         unit = (uptr - dptr->units);
    CHANP       *chp = find_chanp_ptr(chsa);    /* find the chanp pointer */

    sim_debug(DEBUG_CMD, dptr,
        "scsi_startcmd unit %02x cmd %02x CMD %08x SNS %08x\n",
        unit, cmd, uptr->CMD, uptr->SNS);
    if ((uptr->flags & UNIT_ATT) == 0) {        /* unit attached status */
        uptr->SNS |= SNS_INTVENT;               /* unit intervention required */
        if (cmd != DSK_SNS)                     /* we are completed with unit check status */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
    }

    if ((uptr->CMD & DSK_CMDMSK) != 0) {
        uptr->CMD |= DSK_BUSY;                  /* Flag we we are busy */
        return SNS_BSY;
    }
    if ((uptr->CMD & 0xff00) != 0) {            /* if any status info, we are busy */
        return SNS_BSY;
    }
    sim_debug(DEBUG_CMD, dptr, "scsi_startcmd enter unit=%02x cmd %02x\n", unit, cmd);

    /* Unit is online, so process a command */
    switch (cmd) {

    case DSK_INCH:                              /* INCH 0x00 */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_startcmd starting INCH %06x cmd, chsa %04x MemBuf %08x cnt %04x\n",
            uptr->u4, chsa, chp->ccw_addr, chp->ccw_count);

        uptr->CMD |= DSK_INCH2;                 /* use 0xF0 for inch, just need int */
        /* leave the TCMD bit */
        uptr->SNS &= ~MASK24;                   /* clear all but old mode data */
#ifdef FAST_FOR_UTX
        sim_activate(uptr, 30);                 /* start things off */
#else
        sim_activate(uptr, 100);                /* start things off */
#endif
        return 0;
        break;

    case DSK_SCK:                               /* Seek command 0x07 */
    case DSK_XEZ:                               /* Rezero & Read IPL record 0x1f */
    case DSK_WD:                                /* Write command 0x01 */
    case DSK_RD:                                /* Read command 0x02 */
    case DSK_LMR:                               /* read mode register */
    case DSK_NOP:                               /* NOP 0x03 */
    case DSK_RCAP:                              /* Read Capacity 0x53 */
    /* Transfer Command Packet (specifies CDB to send) */
    case DSK_TCMD:                              /* Transfer command packet 0xD3 */
    case DSK_SID:                               /* channel Sense 0x80 */
        /* leave the TCMD bit */
        uptr->SNS &= ~MASK24;                   /* clear all but old mode data */
    case DSK_SNS:                               /* Sense 0x04 */
        uptr->CMD |= cmd;                       /* save cmd */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_startcmd starting disk cmd %02x chsa %04x\n", cmd, chsa);
        sim_activate(uptr, 100);                /* start things off */
        return 0;
        break;
    }
    sim_debug(DEBUG_CMD, dptr,
        "scsi_startcmd done with scsi_startcmd %02x chsa %04x SNS %08x\n",
        cmd, chsa, uptr->SNS);
    if (uptr->SNS & 0xff)                       /* any other cmd is error */
        return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
#ifdef FAST_FOR_UTX
        sim_activate(uptr, 20);                 /* start things off */
#else
        sim_activate(uptr, 100);                /* start things off */
#endif
    return SNS_CHNEND|SNS_DEVEND;
}

/* Handle processing of disk requests. */
t_stat scsi_srv(UNIT *uptr)
{
    uint16          chsa = GET_UADDR(uptr->CMD);
    DEVICE          *dptr = get_dev(uptr);
    /* get pointer to Dev Info Blk for this device */
    CHANP           *chp = find_chanp_ptr(chsa);    /* get channel prog pointer */
    int             cmd = uptr->CMD & DSK_CMDMSK;
    int             type = GET_TYPE(uptr->flags);
    int             unit = (uptr - dptr->units);
    int             bufnum = GET_DEV_BUF(dptr->flags);
    int             len=0;
    int             i;
    uint32          cap = CAP(type);
    uint32          mema;                       /* memory address */
    uint8           ch;
    int32           ssize = scsi_type[type].ssiz*4; /* Size of one sector in bytes */
    uint32          tstart = 0;                 /* Location of start of cyl/track/sect in data */
    uint8           buf2[1024];
    uint8           buf[1024];

    sim_debug(DEBUG_DETAIL, &sba_dev,
        "scsi_srv entry unit %02x CMD %08x chsa %04x count %04x %x/%x/%x \n",
        unit, uptr->CMD, chsa, chp->ccw_count,
        STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff));

    if ((uptr->flags & UNIT_ATT) == 0) {        /* unit attached status */
        uptr->SNS |= SNS_INTVENT;               /* unit intervention required */
        if (cmd != DSK_SNS)                     /* we are completed with unit check status */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
    }

    sim_debug(DEBUG_CMD, dptr,
        "scsi_srv cmd=%02x chsa %04x count %04x SNS %02x\n",
        cmd, chsa, chp->ccw_count, uptr->SNS);
    switch (cmd) {
    case 0:                                     /* No command, stop disk */
        break;

    case DSK_INCH2:                             /* use 0xF0 for inch, just need int */
        len = chp->ccw_count;                   /* INCH command count */
        mema = chp->ccw_addr;                   /* get inch or buffer addr */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_srv starting INCH cmd, chsa %04x MemBuf %06x cnt %04x\n",
            chsa, chp->ccw_addr, chp->ccw_count);

        /* mema has IOCD word 1 contents.  For the MFP (scsi processor) */
        /* a pointer to the INCH buffer. The INCH buffer address must be */
        /* set for the parent channel as well as all other devices on the */
        /* channel.  Call set_inch() to do this for us. Just return OK and */
        /* channel software will use the status buffer addr */

        /* now call set_inch() function to write and test inch buffer addresses */
        /* 1-256 wd buffer is provided for 128 status dbl words */
        /* manual says 128 entries, but diag aborts if more than 1 */
///??   i = set_inch(uptr, mema, 128);          /* new address of 33 entries */
        i = set_inch(uptr, mema, 1);            /* new address of 1 entry */
        if ((i == SCPE_MEM) || (i == SCPE_ARG)) {   /* any error */
            /* we have error, bail out */
            uptr->CMD &= LMASK;                 /* remove old status bits & cmd */
            uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK;
            chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
            break;
        }
        uptr->CMD &= LMASK;                     /* remove old cmd */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_srv cmd INCH chsa %04x chsa %06x count %04x completed\n",
            chsa, mema, chp->ccw_count);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* return OK */
        break;

    case DSK_NOP:                               /* NOP 0x03 */
        uptr->CMD &= LMASK;                     /* remove old cmd */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_srv cmd NOP chsa %04x count %04x completed\n",
            chsa, chp->ccw_count);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* return OK */
        break;

    /* 3 status wds are to be returned */
    /* Wd 1 MMXXXXXX board model # assume 00 00 08 02*/
    /* Wd 2 MMXXXXXX board firmware model # assume 00 00 08 02*/
    /* Wd 3 MMXXXXXX board firmware revision # assume 00 00 00 14*/
    case DSK_SID: /* 0x80 */                    /* this is really for the MFP controller */
        /* send 12 byte Status ID data */
        /* Word 0 */        /* board mod 4324724 = 0x0041fd74 */
        ch = 0x00;
        chan_write_byte(chsa, &ch);             /* write byte 0 */
        ch = 0x41;
        chan_write_byte(chsa, &ch);             /* write byte 1 */
        ch = 0xfd;
        chan_write_byte(chsa, &ch);             /* write byte 2 */
        ch = 0x74;
        chan_write_byte(chsa, &ch);             /* write byte 3 */

        /* Word 1 */        /* firmware 4407519 = 0x004340df */
        ch = 0x00;
        chan_write_byte(chsa, &ch);             /* write byte 4 */
        ch = 0x43;
        chan_write_byte(chsa, &ch);             /* write byte 5 */
        ch = 0x40;
        chan_write_byte(chsa, &ch);             /* write byte 6 */
        ch = 0xdf;
        chan_write_byte(chsa, &ch);             /* write byte 7 */

        /* Word 2 */        /* firmware rev 4259588 = 0x0040ff04 */
        ch = 0x00;
        chan_write_byte(chsa, &ch);             /* write byte 8 */
        ch = 0x40;
        chan_write_byte(chsa, &ch);             /* write byte 9 */
        ch = 0xff;
        chan_write_byte(chsa, &ch);             /* write byte 10 */
        ch = 0x04;
        chan_write_byte(chsa, &ch);             /* write byte 11 */

        uptr->CMD &= LMASK;                     /* remove old status bits & cmd */
        sim_debug(DEBUG_CMD, dptr, "scsi_srv SID chan %02x: chnend|devend\n", chsa);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* return OK */
        break;

    case DSK_SNS: /* 0x4 */
        sim_debug(DEBUG_CMD, dptr, "scsi_startcmd CMD sense\n");

        /* bytes 0,1 - Cyl entry from CHS reg */
        ch = (uptr->CHS >> 24) & 0xff;
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b0 unit=%02x 1 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);
        ch = (uptr->CHS >> 16) & 0xff;
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b1 unit=%02x 2 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);
        /* byte 2 - Track entry from CHS reg */
        ch = (uptr->CHS >> 8) & 0xff;
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b2 unit=%02x 3 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);
        /* byte 3 - Sector entry from CHS reg */
        ch = (uptr->CHS) & 0xff;
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b3 unit=%02x 4 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);

        /* bytes 4 - mode reg, byte 0 of SNS */
        /* skip the TCMD bit */
        ch = (uptr->SNS >> 24) & 0xfe;          /* return the sense data */
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 1 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);
        /* bytes 5-7 - status bytes, bytes 1-3 of SNS */
        ch = (uptr->SNS >> 16) & 0xff;
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 2 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);
        ch = (uptr->SNS >> 8) & 0xff;
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 3 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);
        ch = (uptr->SNS) & 0xff;
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 4 %02x\n",
            unit, ch);
        chan_write_byte(chsa, &ch);

        uptr->CMD &= LMASK;                     /* remove old status bits & cmd */
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
        break;

    case DSK_SCK:                               /* Seek cylinder, track, sector 0x07 */
        /* If we are waiting on seek to finish, check if there yet. */
        if (uptr->CMD & DSK_SEEKING) {
            /* see if on cylinder yet */
            if (uptr->STAR == uptr->CHS) {
                /* we are on cylinder, seek is done */
                sim_debug(DEBUG_CMD, dptr, "scsi_srv seek on sector unit=%02x %06x %06x\n",
                    unit, uptr->STAR, uptr->CHS);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                /* we have already seeked to the required sector */
                /* we do not need to seek again, so move on */
                chan_end(chsa, SNS_DEVEND|SNS_CHNEND);
                return SCPE_OK;
                break;
            } else {
                /* we have wasted enough time, we there */
                /* we are on cylinder, seek is done */
                sim_debug(DEBUG_CMD, dptr, "scsi_srv seek over on cylinder unit=%02x %04x %04x\n",
                    unit, uptr->STAR, uptr->CHS);
                uptr->CHS = uptr->STAR;         /* we are there */
#ifdef FAST_FOR_UTX
                sim_activate(uptr, 20);         /* start things off */
#else
                sim_activate(uptr, 40);
#endif
                break;
            }
        }

        /* not seeking, so start a new seek */
        /* Read in 1-4 character seek code */
        for (i = 0; i < 4; i++) {
            if (chan_read_byte(chsa, &buf[i])) {
                if (i == 0) {
                    sim_debug(DEBUG_DETAIL, dptr,
                        "scsi_srv seek error unit=%02x star %02x %02x %02x %02x\n",
                        unit, buf[0], buf[1], buf[2], buf[3]);
                    /* we have error, bail out */
                    uptr->CMD &= LMASK;           /* remove old status bits & cmd */
                    uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK;
                    chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                    return SCPE_OK;
                    break;
                }
                /* done reading, see how many we read */
                if (i == 1) {
                    /* UTX wants to set seek STAR to zero */
                    buf[0] = buf[1] = buf[2] = buf[3] = 0;
                    break;
                }
                /* just read the next byte */
            }
        }
        /* else the cyl, trk, and sect are ready to update */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_srv STAR unit=%02x star %02x %02x %02x %02x\n",
            unit, buf[0], buf[1], buf[2], buf[3]);

        sim_debug(DEBUG_DETAIL, dptr,
            "scsi_srv seek unit=%02x star %02x%02x%02x%02x\n",
            unit, buf[0], buf[1], buf[2], buf[3]);

        /* save STAR (target sector) data in STAR */
        uptr->STAR = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]);

        sim_debug(DEBUG_DETAIL, dptr,
            "scsi_srv SEEK %08x sector %06x (%d) unit=%02x\n",
            uptr->CMD, uptr->STAR, uptr->STAR, unit);

        /* Check if seek valid */
        if (uptr->STAR >= CAP(type)) {
            sim_debug(DEBUG_CMD, dptr,
                "scsi_srv seek ERROR sector %06x unit=%02x\n",
                uptr->STAR, unit);

            uptr->CMD &= LMASK;                 /* remove old status bits & cmd */
            uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; /* set error status */

            /* we have an error, tell user */
            chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);  /* end command */
            break;
        }

        /* calc the new sector address of data */
        /* calculate file position in bytes of requested sector */
        /* file offset in bytes */
        tstart = uptr->STAR * SSB(type);

        sim_debug(DEBUG_DETAIL, dptr,
            "scsi_srv seek start %04x sector %06x\n",
            tstart, uptr->STAR);

        /* just seek to the location where we will r/w data */
        if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) {  /* seek r/w sec */
            uptr->CMD &= LMASK;                 /* remove old status bits & cmd */
            sim_debug(DEBUG_DETAIL, dptr, "scsi_srv Error on seek to %08x\n", tstart);
            chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
            return SCPE_OK;
        }

        /* Check if already on correct cylinder */
        /* if not, do a delay to slow things down */
        if (uptr->STAR != uptr->CHS) {
            /* Do a fake seek to kill time */
            uptr->CMD |= DSK_SEEKING;           /* show we are seeking */
            sim_debug(DEBUG_DETAIL, dptr,
                "scsi_srv seeking unit=%02x to sector %06x\n",
                unit, uptr->STAR);
#ifdef FAST_FOR_UTX
            /* making this value 40 or so create volume mount error on boot */
            sim_activate(uptr, 20);             /* start things off */
#else
            sim_activate(uptr, 40);
#endif
        } else {
            /* we are on cylinder/track/sector, so go on */
            sim_debug(DEBUG_DETAIL, dptr,
                "scsi_srv calc sect addr seek start %08x sector %06x\n",
                tstart, uptr->STAR);
            uptr->CHS = uptr->STAR;             /* set new sector position */
            uptr->CMD &= LMASK;                 /* remove old status bits & cmd */
            chan_end(chsa, SNS_DEVEND|SNS_CHNEND);
        }
        return SCPE_OK;

    case DSK_XEZ:                               /* Rezero & Read IPL record */

        sim_debug(DEBUG_CMD, dptr, "RD REZERO IPL unit=%02x seek 0\n", unit);
        /* Do a seek to 0 */
        uptr->STAR = 0;                         /* set STAR to 0, 0, 0 */
        uptr->CHS = 0;                          /* set current CHS to 0, 0, 0 */
        uptr->CMD &= LMASK;                     /* remove old cmd */
        uptr->CMD |= DSK_SCK;                   /* show as seek command */
        tstart = 0;                             /* byte offset is 0 */

        /* just seek to the location where we will r/w data */
        if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) {  /* do seek */
            sim_debug(DEBUG_EXP, dptr, "scsi_srv Error on seek to %04x\n", tstart);
            uptr->CMD &= LMASK;                 /* remove old status bits & cmd */
            chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
            return SCPE_OK;
        }
        /* we are on cylinder/track/sector zero, so go on */
        sim_debug(DEBUG_DETAIL, dptr, "scsi_srv done seek trk 0\n");
        uptr->CMD &= LMASK;                     /* remove old status bits & cmd */
        chan_end(chsa, SNS_DEVEND|SNS_CHNEND);
        return SCPE_OK;
        break;

    case DSK_LMR:
        sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x\n", unit);
        /* Read in 1 character of mode data */
        if (chan_read_byte(chsa, &buf[0])) {
            /* we have error, bail out */
            uptr->CMD &= LMASK;                 /* remove old status bits & cmd */
            uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK;
            chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
            break;
        }
        sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x old %x new %x\n",
            unit, (uptr->SNS)&0xff, buf[0]);
        uptr->CMD &= LMASK;                     /* remove old cmd */
        /* leave the TCMD bit */
        uptr->SNS &= (MASK24 | SNS_TCMD);       /* clear old mode data */
        /* do not change TCMD bit */
        uptr->SNS |= ((buf[0]&0xfe) << 24);     /* save mode value */
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
        break;

    case DSK_RD:                                /* 0x02 Read Data */
        if (uptr->SNS & SNS_TCMD) {
            /* we need to process a read TCMD data */
            int cnt = scsi_buf[bufnum][unit][4];    /* byte count of status to send */
            uint32  cyl = CYL(type);            /* number of cylinders */
            uint32  spt = SPT(type);            /* sectors per track */
            uint32  ssb = SSB(type);            /* sector size in bytes */
            int     bcnt;

            /* cnt has # bytes to return (0xf0) */
            uint8   pagecode = scsi_buf[bufnum][unit][2] & 0x3f;   /* get page code */
            uint8   pagecont = (scsi_buf[bufnum][unit][2] & 0xc0) >> 6; /* get page control */

            ch = scsi_buf[bufnum][unit][0];     /* return TCMD cmd */
            uptr->SNS &= ~SNS_TCMD;             /* show not presessing TCMD cmd chain */
            sim_debug(DEBUG_CMD, dptr,
                "scsi_srv processing TCMD read cmd %02x, chsa %04x tcma %06x cnt %04x\n",
                ch, chsa, chp->ccw_addr, chp->ccw_count);

            switch (ch) {
            case 0x25:                          /* read capacity */
                sim_debug(DEBUG_CMD, dptr,
                    "scsi_srv TCMD read call DSK_RCAP cmd %02x, chsa %04x tcma %06x cnt %04x\n",
                    ch, chsa, chp->ccw_addr, chp->ccw_count);
                goto read_cap;                  /* use IOCL cmd processing */
                break;

            case 0x28:                          /* read 10 byte cmd */
                /* blk is in bytes 2-5, sects is in 7-8 */
                sim_debug(DEBUG_CMD, dptr,
                    "scsi_srv TCMD call read cmd %02x, chsa %04x tcma %06x cnt %04x\n",
                    ch, chsa, chp->ccw_addr, chp->ccw_count);
                tstart = (scsi_buf[bufnum][unit][2] << 24) |    /* get sector address */
                    (scsi_buf[bufnum][unit][3] << 16) |
                    (scsi_buf[bufnum][unit][4] << 8) |
                    (scsi_buf[bufnum][unit][5]);
                bcnt = (scsi_buf[bufnum][unit][8] << 8) |   /* get transfer block count */
                    (scsi_buf[bufnum][unit][9]);
                sim_debug(DEBUG_CMD, dptr,
                    "scsi_srv TCMD call read DATA cmd %02x, chsa %04x buf addr %08x SA %08x cnt %02x\n",
                    ch, chsa, chp->ccw_addr, tstart, bcnt); 

                /* convert sect address to chs value */
                uptr->CHS = tstart;
                /* get byte address for seek */
                tstart = tstart * SSB(type);

                /* just seek to the location where we will r/w data */
                if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) {  /* do seek */
                    sim_debug(DEBUG_EXP, dptr, "scsi_srv read TCMD Error on seek to %04x\n", tstart);
                    uptr->CMD &= LMASK;         /* remove old status bits & cmd */
                    chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                    return SCPE_OK;
                }
                /* we are on cylinder/track/sector, get data to write */
                sim_debug(DEBUG_DETAIL, dptr, "scsi_srv TCMD done seek\n");
                tstart = uptr->CHS;             /* get sector offset back */
                goto doread;                    /* use IOCL cmd processing */
                break;

            case 0x1a:                          /* mode sense */
                for (i=0; i<cnt; i++) {
                    buf[i] = 0;                 /* clear buffer */
                }
                /* test for "special" mpx status request */
                if (cnt == 0x18 && scsi_buf[bufnum][unit][2] == 0x03) {
                    /* set some sense data from SH.DCSCI driver code */
                    buf[0] = 0xf0;              /* page length */
                    buf[4] = 0x81;              /* savable and page type 1 */
                    buf[8] = 0x91;
                    buf[12] = 0xf4;
                    buf[17] = (uint8)HDS(type); /* # of heads */
                    buf[23] = (uint8)SPT(type); /* Sect/track */
                    goto merge;                 /* go output data and return */
                }
                /* this is most likely UTX calling */
                pagecode = scsi_buf[bufnum][unit][2] & 0x3f;   /* get page code */
                pagecont = (scsi_buf[bufnum][unit][2] & 0xc0) >> 6; /* get page control */
                /* pagecont = 0 return current values */
                /* pagecont = 1 return changable values */
                /* pagecont = 2 return default values */
                /* pagecont = 3 return saved values */
                sim_debug(DEBUG_CMD, dptr,
                    "scsi_srv TCMD read call MOD SEN cmd %02x pgcd %02x pgco %1x chsa %04x tcma %06x cnt %04x\n",
                    ch, pagecode, pagecont, chsa, chp->ccw_addr, chp->ccw_count);
                buf[0] = 0xf0;                  /* page length */
                if (pagecode == 3) {
                    buf[2] = 0;                 /* 0x80 if write protected */
                    buf[3] = 0;                 /* block descriptor length */
//                  buf[4] = 0x83;              /* savable and page type 3 */
                    buf[4] = 0x03;              /* not savable and page type 3 */
                    buf[5] = 22;                /* 22 data bytes follow */
                    buf[6] = 0;                 /* tracks per zone ub */
                    buf[7] = 1;                 /* tracks per zone lb */
                    buf[8] = 0;                 /* alt sec per zone ub */
                    buf[9] = 1;                 /* alt sec per zone lb */
                    buf[10] = 0;                /* alt trks per zone ub */
                    buf[11] = 0;                /* alt trks per zone lb */
                    buf[12] = 0;                /* alt trks per unit ub */
                    buf[13] = 0;                /* alt trks per unit lb */
                    buf[14] = (uint8)((spt & 0xff00) >> 8); /* Sect/track */
                    buf[15] = (uint8)((spt & 0x00ff));  /* Sect/track */
                    buf[16] = (uint8)((ssb & 0xff00) >> 8); /* Sect size */
                    buf[17] = (uint8)((ssb & 0x00ff));  /* Sect size */
                    buf[18] = 0;                /* interleave ub */
                    buf[19] = 0;                /* interleave lb */
                    buf[20] = 0;                /* track skew factor ub */
                    buf[21] = 0;                /* track skew factor lb */
                    buf[22] = 0;                /* cyl skew factor ub */
                    buf[23] = 0;                /* cyl skew factor lb */
//                  buf[24] |= 0x80;            /* soft sectoring */
                    buf[24] |= 0x40;            /* hard sectoring */
//                  buf[24] |= 0x20;            /* drive removable */
//                  buf[24] |= 0x08;            /* inhibit save */
                    goto merge;                 /* go output data and return */
                }
                if (pagecode == 4) {
                    /* num cyl */
                    buf[2] = 0;                 /* 0x80 if write protected */
                    buf[3] = 0;                 /* block descriptor length */
//                  buf[4] = 0x84;              /* savable and page type 4 */
                    buf[4] = 0x04;              /* not savable and page type 4 */
                    buf[5] = 18;                /* 18 data bytes follow */
                    buf[6] = (uint8)((cyl & 0xff0000) >> 16);
                    buf[7] = (uint8)((cyl & 0x00ff00) >> 8);
                    buf[8] = (uint8)((cyl & 0x0000ff));
                    buf[9] = (uint8)HDS(type);  /* # of heads */
                    goto merge;                 /* go output data and return */
                }

            case 0x12:                          /* inquiry */
                /* size is 0x24 = 36 bytes */
                /* ssize has sector size in bytes */
                for (i=0; i<cnt; i++) {
                    buf[i] = 0;                 /* clear buffer */
                }
                /* set some sense data from SH.DCSCI driver code */
                buf[0] = 0xf0;                  /* page length */
                buf[4] = 0x81;                  /* savable and page type 1 */
                buf[8] = 0x91;
                buf[12] = 0xf4;
                buf[17] = (uint8)HDS(type);     /* # of heads */
                buf[23] = (uint8)SPT(type);     /* Sect/track */

merge:
                /* output response data */
                for (i=0; i<cnt; i++) {
                    if (chan_write_byte(chsa, &buf[i])) {
                        /* we have error, bail out */
                        uptr->CMD &= LMASK;     /* remove old status bits & cmd */
                        uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK;
                        chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                        return SCPE_OK;
                    }
                }
                sim_debug(DEBUG_DETAIL, dptr,
                "scsi_srv TCMD inq read data chsa=%02x data %02x%02x%02x%02x %02x%02x%02x%02x\n",
                chsa, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
                sim_debug(DEBUG_DETAIL, dptr,
                "scsi_srv TCMD inq read data chsa=%02x data %02x%02x%02x%02x %02x%02x%02x%02x\n",
                chsa, buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
                sim_debug(DEBUG_DETAIL, dptr,
                "scsi_srv TCMD inq read data chsa=%02x data %02x%02x%02x%02x %02x%02x%02x%02x\n",
                chsa, buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23]);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                return SCPE_OK;
                break;

            case 0x00:                          /* test unit ready */
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                sim_debug(DEBUG_CMD, dptr, "scsi_srv test unit ready cmd %02x unit %02x\n", ch, unit);
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                return SCPE_OK;
                break;

            default:                            /* bad or unsupported scsi command */
                sim_debug(DEBUG_CMD, dptr, "invalid scsi read command %02x unit %02x\n", ch, unit);
                uptr->SNS |= SNS_CMDREJ;
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                return SCPE_OK;
                break;
            }
        }

doread:
        /* normal disk read starts here */
        /* tstart has start of sector address in bytes */
        if ((uptr->CMD & DSK_READING) == 0) {   /* see if we are reading data */
            uptr->CMD |= DSK_READING;           /* read from disk starting */
            sim_debug(DEBUG_CMD, dptr,
                "SCSI READ starting unit=%02x CMD %08x count %04x\n",
                unit, uptr->CMD, chp->ccw_count);
        }

        if (uptr->CMD & DSK_READING) {          /* see if we are reading data */
            tstart = uptr->CHS;                 /* get sector offset */

            sim_debug(DEBUG_CMD, dptr,
                "SCSI READ reading CMD %08x chsa %04x tstart %04x buffer %06x count %04x\n",
                uptr->CMD, chsa, tstart, chp->ccw_addr, chp->ccw_count);

            /* just seek to the location where we will r/w data */
            if ((sim_fseek(uptr->fileref, tstart*SSB(type), SEEK_SET)) != 0) {  /* seek r/w sec */
                sim_debug(DEBUG_DETAIL, dptr,
                    "scsi_srv READ, Error on seek to %08x\n", tstart*SSB(type));
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                return SCPE_OK;
            }

            /* read in a sector of data from disk */
            if ((len=sim_fread(buf, 1, ssize, uptr->fileref)) != ssize) {
                sim_debug(DEBUG_CMD, dptr,
                    "Error %08x on read %04x of diskfile sector %06x\n",
                    len, ssize, tstart);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                break;
            }

            sim_debug(DEBUG_CMD, dptr, "scsi_srv after READ chsa %04x count %04x\n",
                chsa, chp->ccw_count);

            /* process the sector of data */
            for (i=0; i<len; i++) {
                ch = buf[i];                    /* get a char from buffer */
                if (chan_write_byte(chsa, &ch)) {   /* put a byte to memory */
                    sim_debug(DEBUG_DATA, dptr,
                        "SCSI Read %04x bytes leaving %04x from diskfile sector %06x\n",
                        i, chp->ccw_count, tstart);
                    uptr->CMD &= LMASK;         /* remove old status bits & cmd */
                    chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                    return SCPE_OK;
                }
            }

            sim_debug(DEBUG_CMD, dptr,
                "SCSI READ %04x bytes leaving %4x to be read to %06x from diskfile sector %06x\n",
                ssize, chp->ccw_count, chp->ccw_addr+4, tstart);

            /* see if we are done reading data */
            if (test_write_byte_end(chsa)) {
                sim_debug(DEBUG_DATA, dptr,
                    "SCSI Read complete for read from diskfile sector %06x\n",
                    uptr->CHS);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                break;
            }

            /* tstart has file offset in sectors */
            tstart++;                           /* bump to next sector */
            uptr->CHS = tstart;                 /* new position */
            /* see if over end of disk */
            if (tstart >= (uint32)CAP(type)) {
                /* EOM reached, abort */
                sim_debug(DEBUG_CMD, dptr,
                    "SCSI Read reached EOM for read from disk @ sector %06x\n",
                    tstart);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                uptr->CHS = 0;                  /* reset cylinder position */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                break;
            }

            sim_debug(DEBUG_DATA, dptr,
                "SCSI sector read complete, %x bytes to go from diskfile sector %06x\n",
                chp->ccw_count, uptr->CHS);
#ifdef FAST_FOR_UTX
            sim_activate(uptr, 15);             /* start things off */
#else
            sim_activate(uptr, 10);             /* wait to read next sector */
#endif
            break;
        }
        break;

    case DSK_WD:            /* Write Data */
        if (uptr->SNS & SNS_TCMD) {
            /* we need to process a write TCMD data */
            /* cnt has # bytes to read */
            int cnt = chp->ccw_count;           /* byte count of status to read */

            ch = scsi_buf[bufnum][unit][0];     /* return TCMD cmd */
            uptr->SNS &= ~SNS_TCMD;             /* show not presessing TCMD cmd chain */
            sim_debug(DEBUG_CMD, dptr,
                "scsi_srv processing TCMD write cmd %02x, chsa %04x tcma %06x cnt %04x\n",
                ch, chsa, chp->ccw_addr, chp->ccw_count);

            switch (ch) {

            case 0x4:                           /* write 6 byte cmd, format disk  */
                if (scsi_buf[bufnum][unit][2] == 10) {
                    if ((--scsi_buf[bufnum][unit][3]) > 0) {
                        uptr->SNS |= SNS_TCMD;   /* show still presessing TCMD cmd chain */
                        sim_activate(uptr, 200000);  /* wait a while */
                        return SCPE_OK;
                        break;
                    }
                    scsi_buf[bufnum][unit][2] = 0;  /* show done */
                }
                /* MPX cmd data 04 18 00 00 00 00 */
                /* Format unit */
                len = 0;
                for (i=0; i<cnt; i++) {
                    if (chan_read_byte(chsa, &ch)) {/* get a byte from memory */
                        /* if error on reading 1st byte, we are done writing */
                        if (i == 0) {
                            uptr->CMD &= LMASK; /* remove old status bits & cmd */
                            sim_debug(DEBUG_CMD, dptr,
                                "SCSI Read %04x bytes from MPX buffer\n", len);
                            chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                            return SCPE_OK;
                        }
                        ch = 0;                 /* finish out the sector with zero */
                        len++;                  /* show we have no more data to write */
                    }
                    buf2[i] = ch;               /* save the char */
                }
                sim_debug(DEBUG_CMD, dptr, "SCSI CMD 4 %04x bytes read from MPX buffer\n", cnt);
                sim_debug(DEBUG_CMD, dptr,
                "SCSI Disk format buf2 %02x%02x%02x%02x\n", buf2[0], buf2[1], buf2[2], buf2[3]);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                return SCPE_OK;
                break;

            case 0x15:                          /* write 6 byte cmd, format disk  */
                /* MODE select */
                len = 0;
                for (i=0; i<cnt; i++) {
                    if (chan_read_byte(chsa, &ch)) {/* get a byte from memory */
                        /* if error on reading 1st byte, we are done writing */
                        if (i == 0) {
                            uptr->CMD &= LMASK; /* remove old status bits & cmd */
                            sim_debug(DEBUG_CMD, dptr,
                                "SCSI Read %04x bytes from MPX buffer\n", len);
                            chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                            return SCPE_OK;
                        }
                        ch = 0;                 /* finish out the sector with zero */
                        len++;                  /* show we have no more data to write */
                    }
                    buf2[i] = ch;               /* save the char */
                }
                sim_debug(DEBUG_CMD, dptr, "SCSI Format %04x bytes to status buffer\n", cnt);
                sim_debug(DEBUG_CMD, dptr,
        "SCSI Disk format buf2 %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
                buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7],
                buf2[8], buf2[9], buf2[10], buf2[11], buf2[12], buf2[13], buf2[14], buf2[15]);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                return SCPE_OK;
                break;

            case 0x2a:                          /* write 10 byte cmd */
                tstart = (scsi_buf[bufnum][unit][2] << 24) |    /* get sector address */
                    (scsi_buf[bufnum][unit][3] << 16) |
                    (scsi_buf[bufnum][unit][4] << 8) |
                    (scsi_buf[bufnum][unit][5]);
                sim_debug(DEBUG_CMD, dptr,
                    "scsi_srv TCMD call write DATA cmd %02x, chsa %04x addr %08x data %08x %08x\n",
                    ch, chsa, chp->ccw_addr, RMW(chp->ccw_addr), RMW(chp->ccw_addr+4)); 

                /* convert sect address to chs value */
                uptr->CHS = tstart;             /* set seek sector address */
                /* get byte address for seek */
                tstart = tstart * SSB(type);

                /* just seek to the location where we will r/w data */
                if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) {  /* do seek */
                    sim_debug(DEBUG_EXP, dptr, "scsi_srv TCMD Error on seek to %04x\n", tstart);
                    uptr->CMD &= LMASK;         /* remove old status bits & cmd */
                    chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                    return SCPE_OK;
                }
                /* we are on cylinder/track/sector, get data to write */
                sim_debug(DEBUG_DETAIL, dptr, "scsi_srv TCMD done seek\n");
                tstart = uptr->CHS;             /* get sector offset */
                goto dowrite;
                break;

            default:                            /* bad or unsupported scsi command */
                sim_debug(DEBUG_CMD, dptr, "invalid scsi write command %02x unit %02x\n", ch, unit);
                uptr->SNS |= SNS_CMDREJ;
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                return SCPE_OK;
                break;
            }
            break;
        }
dowrite:
        /* tstart has file offset in sectors */
        if ((uptr->CMD & DSK_WRITING) == 0) {   /* see if we are writing data */
            uptr->CMD |= DSK_WRITING;           /* write to disk starting */
            sim_debug(DEBUG_CMD, dptr,
                "SCSI WRITE starting unit=%02x CMD %02x write %4x from %06x to sector %06x\n",
                unit, uptr->CMD, chp->ccw_count, chp->ccw_addr, uptr->CHS);
        }
        if (uptr->CMD & DSK_WRITING) {          /* see if we are writing data */
            tstart = uptr->CHS;                 /* get sector offset */

            /* just seek to the location where we will r/w data */
            if ((sim_fseek(uptr->fileref, tstart*SSB(type), SEEK_SET)) != 0) {  /* seek r/w sec */
                sim_debug(DEBUG_EXP, dptr,
                    "scsi_srv WRITE, Error on seek to %08x\n", tstart*SSB(type));
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                return SCPE_OK;
            }

            /* process the next sector of data */
            len = 0;                            /* used here as a flag for short read */
            for (i=0; i<ssize; i++) {
                if (chan_read_byte(chsa, &ch)) {/* get a byte from memory */
                    /* if error on reading 1st byte, we are done writing */
                    if (i == 0) {
                        uptr->CMD &= LMASK;     /* remove old status bits & cmd */
                        sim_debug(DEBUG_CMD, dptr,
                            "SCSI Wrote %04x bytes to diskfile sector %06x\n",
                            ssize, tstart);
                        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);
                        return SCPE_OK;
                    }
                    ch = 0;                     /* finish out the sector with zero */
                    len++;                      /* show we have no more data to write */
                }
                buf2[i] = ch;                   /* save the char */
            }

            /* write the sector to disk */
            if ((i=sim_fwrite(buf2, 1, ssize, uptr->fileref)) != ssize) {
                sim_debug(DEBUG_CMD, dptr,
                    "Error %08x on write %04x bytes to diskfile sector %06x\n",
                    i, ssize, tstart);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                break;
            }
            if (len != 0) {                     /* see if done with write command */
                sim_debug(DEBUG_DATA, dptr,
                    "SCSI WroteB %04x bytes to diskfile sector %06x\n",
                    ssize, tstart);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* we done */
                break;
            }
            sim_debug(DEBUG_CMD, dptr,
                "SCSI WR to sec end %04x bytes end %04x to diskfile sector %06x\n",
                len, ssize, tstart);

            /* tstart has file offset in sectors */
            tstart++;                           /* bump to next sector */
            uptr->CHS = tstart;                 /* save new sector */
            /* see if over end of disk */
            if (tstart >= (uint32)CAP(type)) {
                /* EOM reached, abort */
                sim_debug(DEBUG_CMD, dptr,
                    "SCSI Write reached EOM for write to disk @ sector %06x\n",
                    uptr->CHS);
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                uptr->CHS = 0;                  /* reset cylinder position */
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                break;
            }
#ifdef FAST_FOR_UTX
            sim_activate(uptr, 20);             /* start things off */
#else
            sim_activate(uptr, 10);             /* keep writing */
#endif
            break;
        }
        break;

    case DSK_RCAP:                              /* Read Capacity 0x53 */
        /* return 8 bytes */
        /* wd 1 disk size in sectors */
        /* wd 2 is sector size in bytes */
        /* cap has disk capacity */
read_cap:                                       /* merge point from TCMD processing */
        for (i=0; i<4; i++) {
            /* I think they want cap-1, not cap?????????? */
            /* verified that MPX wants cap-1, else J.VFMT aborts */
            ch = ((cap-1) >> ((3-i)*8)) & 0xff; /* use cap-1 */
            if (chan_write_byte(chsa, &ch)) {   /* write byte to memory */
                /* we have error, bail out */
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK;
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                return SCPE_OK;
                break;
            }
        }
        /* ssize has sector size in bytes */
        for (i=0; i<4; i++) {
            ch = (((uint32)ssize) >> ((3-i)*8)) & 0xff;
            if (chan_write_byte(chsa, &ch)) {
                /* we have error, bail out */
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK;
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                return SCPE_OK;
                break;
            }
        }

        /* command is completed */
        uptr->CMD &= LMASK;                     /* remove old status bits & cmd */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_srv cmd RCAP chsa %04x capacity %06x secsize %03x completed\n",
            chsa, cap, ssize);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* return OK */
        break;

    /* Transfer Command Packet (specifies CDB to send) */
    /* address points to CDB */
    case DSK_TCMD:                              /* Transfer command packet 0xD3 */
        {
        uint32  mema;                           /* memory address */
        int32   i;

        uptr->SNS &= ~SNS_TCMD;                 /* show not presessing TCMD cmd chain */
        len = chp->ccw_count;                   /* INCH command count */
        mema = chp->ccw_addr;                   /* get inch or buffer addr */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_srv starting TCMD cmd, chsa %04x tcma %06x cnt %04x\n",
            chsa, chp->ccw_addr, chp->ccw_count);

        /* mema has IOCD word 1 contents. */
        /* len has the byte count from IOCD wd2 */
        len = chp->ccw_count;                   /* TCMD command count */

        for (i=0; i < len; i++) {
            if (chan_read_byte(chsa, &buf[i])) {
                /* we have error, bail out */
                uptr->CMD &= LMASK;             /* remove old status bits & cmd */
                uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK;
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
                return SCPE_OK;
                break;
            }
        }
        sim_debug(DEBUG_DETAIL, dptr,
    "scsi_srv TCMD data chsa=%02x data %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
            chsa, buf[0], buf[1], buf[2], buf[3], buf[4],
            buf[5], buf[6], buf[7], buf[8], buf[9]);

        /* save the CMD packet */
        for (i=0; i < len; i++) {
            scsi_buf[bufnum][unit][i] = buf[i]; /* save the cmd */
        }
        scsi_pcmd[bufnum][unit] = buf[0];       /* save the cmd */

        /* if this a disk format, do a big wait */
        if ((buf[0] == 4) && (buf[1] == 0x18)) {
            scsi_buf[bufnum][unit][2] = 10;     /* show Processing Format cmd */
            scsi_buf[bufnum][unit][3] = 10;     /* show Processing cmd */
        }

        /* see if just test unit ready */
        if ((buf[0] == 0) && (len == 6))
            uptr->SNS &= ~SNS_TCMD;             /* clear TCMD flag */
        else
            uptr->SNS |= SNS_TCMD;              /* show Processing CMD cmd chain */

        /* command is completed */
        uptr->CMD &= LMASK;                     /* remove old status bits & cmd */
        sim_debug(DEBUG_CMD, dptr,
            "scsi_srv cmd TCMD chsa %04x addr %06x count %04x completed\n",
            chsa, mema, chp->ccw_count);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* return OK */
        }
        return SCPE_OK;
        break;

    default:
        sim_debug(DEBUG_EXP, dptr, "invalid command %02x unit %02x\n", cmd, unit);
        uptr->SNS |= SNS_CMDREJ;
        uptr->CMD &= LMASK;                     /* remove old status bits & cmd */
        return SNS_CHNEND|STATUS_PCHK;
        break;
    }
    sim_debug(DEBUG_CMD, dptr,
        "scsi_srv done cmd %02x chsa %04x count %04x\n", cmd, chsa, chp->ccw_count);
    return SCPE_OK;
}

/* initialize the disk */
void scsi_ini(UNIT *uptr, t_bool f)
{
    DEVICE  *dptr = get_dev(uptr);
    int     i = GET_TYPE(uptr->flags);

    /* start out at sector 0 */
    uptr->CHS = 0;                              /* set CHS to cyl/hd/sec = 0 */
    uptr->STAR = 0;                             /* set STAR to cyl/hd/sec = 0 */
    uptr->CMD &= LMASK;                         /* remove old status bits & cmd */
    uptr->SNS = 0;                              /* clear any status */
    /* total sectors on disk */
    uptr->capac = CAP(i);                       /* disk size in sectors */
    sim_cancel(uptr);                           /* stop any timers */

    sim_debug(DEBUG_EXP, &sba_dev, "SCSI init device %s on unit SBA%04x cap %x %d\n",
        dptr->name, GET_UADDR(uptr->CMD), uptr->capac, uptr->capac);
}

/* handle rschnlio cmds for scsi */
t_stat  scsi_rschnlio(UNIT *uptr) {
    DEVICE  *dptr = get_dev(uptr);
    uint16  chsa = GET_UADDR(uptr->CMD);
    int     cmd = uptr->CMD & DSK_CMDMSK;

    sim_debug(DEBUG_EXP, dptr,
        "scsi_rschnl chsa %04x cmd = %02x\n", chsa, cmd);
    scsi_ini(uptr, 0);                          /* reset the unit */
    return SCPE_OK;
}

t_stat scsi_reset(DEVICE *dptr)
{
    UNIT    *uptr = dptr->units;
    uint16  chsa = GET_UADDR(uptr->CMD);

    /* add reset code here */
    sim_debug(DEBUG_EXP, dptr,
        "scsi_reset chsa %04x\n", chsa);
    return SCPE_OK;
}

/* create the disk file for the specified device */
int scsi_format(UNIT *uptr) {
    int         type = GET_TYPE(uptr->flags);
    DEVICE      *dptr = get_dev(uptr);
    int32       ssize = scsi_type[type].ssiz * 4;   /* disk sector size in bytes */
    uint32      tsize = scsi_type[type].spt;    /* get track size in sectors */
    uint32      csize = scsi_type[type].nhds * tsize;   /* get cylinder size in sectors */
    uint32      cyl = scsi_type[type].cyl;      /* get # cyl */
    uint32      cap = scsi_type[type].cyl * csize;  /* disk capacity in sectors */
    uint32      cylv = cyl;                     /* number of cylinders */
    uint8       *buff;
    int         i;
    t_stat      oldsw = sim_switches;           /* save switches */

                /* last sector address of disk (cyl * hds * spt) - 1 */
    uint32      laddr = CAP(type) - 1;          /* last sector of disk */

                /* last track address of disk (cyl * hds * spt) - spt */
    uint32      ltaddr = CAP(type)-SPT(type);   /* last sector of disk */

                /* get sector address of vendor defect table VDT */
                /* put data = 0xf0000000 0xf4000000 */
    int32       vaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-1) * SPT(type);

                /* get sector address of utx diag map (DMAP) track 0 pointer */
                /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */
                /* 0x9a000000 + (cyl-1), 0xf4000008 */
    int32       daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type);

                /* get sector address of utx flaw data (1 track long) */
                /* set trace data to zero */
    int32       faddr = (CYL(type)-4) * SPC(type) + (HDS(type)-3) * SPT(type);

                /* get sector address of utx flaw map sec 1 pointer */
                /* use this address for sec 1 label pointer */
    int32       uaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-4) * SPT(type);

                /* last user block available */
    int32       luaddr = (CYL(type)-4) * SPC(type);

                /* make up a UMAP with the partiton data for 9346 disk */
    uint32      umap[256] =
                {
                    /* try to makeup a utx dmap */
                    0x4e554d50,(cap-1),(uint32)(luaddr-1),0,0,0,0,0xe10,
                    0,0x5320,0,0x4e60,0x46,(uint32)luaddr,0,0xd360,
                    0x88,0x186b0,0x13a,0xd100,0x283,0,0,0,
                    0,0x22c2813e,0,0x06020000,0xf4,0,0x431b1c,0,
                };

                /* vendor flaw map in vaddr */
    uint32      vmap[2] = {0xf0000004, 0xf4000000};

                /* defect map */
    uint32      dmap[4] = {0xf0000000 | (cap-1), 0x8a000000 | daddr,
                    0x9a000000 | (cap-1), 0xf4000000};

                /* utx flaw map */
    uint32      fmap[4] = {0xf0000000 | (cap-1), 0x8a000000 | daddr,
                    0x9a000000 | ltaddr, 0xf4000000};

    /* see if -i or -n specified on attach command */
    if (!(sim_switches & SWMASK('N')) && !(sim_switches & SWMASK('I'))) {
        sim_switches = 0;                       /* simh tests 'N' & 'Y' switches */
        /* see if user wants to initialize the disk */
        if (!get_yn("Initialize disk? [Y] ", TRUE)) {
            sim_switches = oldsw;
            return 1;
        }
        sim_switches = oldsw;                   /* restore switches */
    }

#if 0
    /* see if user wants to initialize the disk */
    if (!get_yn("Initialize disk? [Y] ", TRUE)) {
        return 1;
    }
#endif

    /* VDT  249264 (819/18/0) 0x3cdb0 for 9346 - 823/19/16 vaddr */
    /* MDT  249248 (819/17/0) 0x3cda0 for 9346 - 823/19/16 daddr */
    /* DMAP 249232 (819/16/0) 0x3cd90 for 9346 - 823/19/16 faddr */
    /* UMAP 249216 (819/15/0) 0x3cd80 for 9346 - 823/19/16 uaddr */

    /* seek to sector 0 */
    if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */
        fprintf (stderr, "Error on seek to 0\r\n");
    }

    /* get buffer for track data */
    if ((buff = (uint8 *)calloc(csize*ssize, sizeof(uint8))) == 0) {
        detach_unit(uptr);
        return SCPE_ARG;
    }
    sim_debug(DEBUG_CMD, dptr,
        "Creating disk file of trk size %04x bytes, capacity %d\n",
        tsize*ssize, cap*ssize);

    /* write zeros to each track of the disk */
    for (cyl = 0; cyl < cylv; cyl++) {
        if ((sim_fwrite(buff, 1, csize*ssize, uptr->fileref)) != csize*ssize) {
            sim_debug(DEBUG_CMD, dptr,
                "Error on write to diskfile cyl %04x\n", cyl);
            free(buff);                         /* free cylinder buffer */
            buff = 0;
            return 1;
        }
        if ((cyl % 100) == 0)
            fputc('.', stderr);
    }
    fputc('\r', stderr);
    fputc('\n', stderr);
    free(buff);                                 /* free cylinder buffer */
    buff = 0;

    /* byte swap the buffers for dmap and umap */
    for (i=0; i<2; i++) {
        vmap[i] = (((vmap[i] & 0xff) << 24) | ((vmap[i] & 0xff00) << 8) |
            ((vmap[i] & 0xff0000) >> 8) | ((vmap[i] >> 24) & 0xff));
    }
    for (i=0; i<4; i++) {
        dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) |
            ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff));
    }
    for (i=0; i<4; i++) {
        fmap[i] = (((fmap[i] & 0xff) << 24) | ((fmap[i] & 0xff00) << 8) |
            ((fmap[i] & 0xff0000) >> 8) | ((fmap[i] >> 24) & 0xff));
    }
    for (i=0; i<256; i++) {
        umap[i] = (((umap[i] & 0xff) << 24) | ((umap[i] & 0xff00) << 8) |
            ((umap[i] & 0xff0000) >> 8) | ((umap[i] >> 24) & 0xff));
    }

    /* now seek to end of disk and write the dmap data */
    /* setup dmap pointed to by track label 0 wd[3] = (cyl-4) * spt + (spt - 1) */

    /* write dmap data to last sector on disk */
    if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */
        sim_debug(DEBUG_CMD, dptr,
        "Error on last sector seek to sect %06x offset %06x\n",
        cap-1, (cap-1)*ssize);
        return 1;
    }
    if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) {
        sim_debug(DEBUG_CMD, dptr,
        "Error writing DMAP to sect %06x offset %06x\n",
        cap-1, (cap-1)*ssize);
        return 1;
    }

    /* seek to vendor label area VMAP */
    if ((sim_fseek(uptr->fileref, vaddr*ssize, SEEK_SET)) != 0) { /* seek VMAP */
        sim_debug(DEBUG_CMD, dptr,
        "Error on vendor map seek to sect %06x offset %06x\n",
        vaddr, vaddr*ssize);
        return 1;
    }
    if ((sim_fwrite((char *)&vmap, sizeof(uint32), 2, uptr->fileref)) != 2) {
        sim_debug(DEBUG_CMD, dptr,
        "Error writing VMAP to sect %06x offset %06x\n",
        vaddr, vaddr*ssize);
        return 1;
    }

    /* write DMAP to daddr that is the address in trk 0 label */
    if ((sim_fseek(uptr->fileref, daddr*ssize, SEEK_SET)) != 0) { /* seek DMAP */
        sim_debug(DEBUG_CMD, dptr,
        "Error on diag map seek to sect %06x offset %06x\n",
        daddr, daddr*ssize);
        return 1;
    }
    if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) {
        sim_debug(DEBUG_CMD, dptr,
        "Error writing DMAP to sect %06x offset %06x\n",
        daddr, daddr*ssize);
        return 1;
    }

    /* write dummy DMAP to faddr */
    if ((sim_fseek(uptr->fileref, faddr*ssize, SEEK_SET)) != 0) { /* seek DMAP */
        sim_debug(DEBUG_CMD, dptr,
        "Error on media flaw map seek to sect %06x offset %06x\n",
        faddr, faddr*ssize);
        return 1;
    }
    if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) {
        sim_debug(DEBUG_CMD, dptr,
        "Error writing flaw map to sect %06x offset %06x\n",
        faddr, faddr*ssize);
        return 1;
    }

    /* write UTX umap to uaddr */
    if ((sim_fseek(uptr->fileref, uaddr*ssize, SEEK_SET)) != 0) { /* seek UMAP */
        sim_debug(DEBUG_CMD, dptr,
        "Error on umap seek to sect %06x offset %06x\n",
        uaddr, uaddr*ssize);
        return 1;
    }
    if ((sim_fwrite((char *)&umap, sizeof(uint32), 256, uptr->fileref)) != 256) {
        sim_debug(DEBUG_CMD, dptr,
        "Error writing UMAP to sect %06x offsewt %06x\n",
        uaddr, uaddr*ssize);
        return 1;
    }

    printf("SCSI Disk %s has %x (%d) cyl, %x (%d) hds, %x (%d) sec\r\n",
        scsi_type[type].name, CYL(type), CYL(type), HDS(type), HDS(type),
        SPT(type), SPT(type));
    printf("writing to vmap sec %x (%d) bytes %x (%d)\n",
        vaddr, vaddr, (vaddr)*ssize, (vaddr)*ssize);
    printf("writing to flaw map sec %x (%d) bytes %x (%d)\n",
        faddr, faddr, (faddr)*ssize, (faddr)*ssize);
    printf("writing dmap to %x %d %x %d dmap to %x %d %x %d\n",
       cap-1, cap-1, (cap-1)*ssize, (cap-1)*ssize,
       daddr, daddr, daddr*ssize, daddr*ssize);
    printf("writing to umap sec %x (%d) bytes %x (%d)\n",
        uaddr, uaddr, (uaddr)*ssize, (uaddr)*ssize);

    /* seek home again */
    if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */
        fprintf (stderr, "Error on seek to 0\r\n");
        return 1;
    }
    return 0;
}

/* attach the selected file to the disk */
t_stat scsi_attach(UNIT *uptr, CONST char *file) {
    uint16          chsa = GET_UADDR(uptr->CMD);
    int             type = GET_TYPE(uptr->flags);
    DEVICE          *dptr = get_dev(uptr);
    DIB             *dibp = 0;
    t_stat          r, s;
    uint32          ssize;                      /* sector size in bytes */
    uint32          info, good,zmap = 0x5a4d4150;   /* ZMAP */
    uint8           buff[1024];
    int             i, j;

                    /* last sector address of disk (cyl * hds * spt) - 1 */
    uint32          laddr = CAP(type) - 1;      /* last sector of disk */
                    /* defect map */
    uint32          dmap[4] = {0xf0000000 | (CAP(type)-1), 0x8a000000,
                        0x9a000000 | (CAP(type)-1), 0xf4000000};

    for (i=0; i<4; i++) {                       /* byte swap data for last sector */
        dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) |
            ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff));
    }

    uptr->SNS = 0;                              /* clear any status */

    /* see if valid disk entry */
    if (scsi_type[type].name == 0) {            /* does the assigned disk have a name */
        detach_unit(uptr);                      /* no, reject */
        return SCPE_FMT;                        /* error */
    }

    if (dptr->flags & DEV_DIS) {
        fprintf(sim_deb,
            "ERROR===ERROR\nSCSI Disk device %s disabled on system, aborting\r\n",
            dptr->name);
        printf("ERROR===ERROR\nSCSI Disk device %s disabled on system, aborting\r\n",
            dptr->name);
        return SCPE_UDIS;                       /* device disabled */
    }

    /* have simulator attach the file to the unit */
    if ((r = attach_unit(uptr, file)) != SCPE_OK)
        return r;

    uptr->capac = CAP(type);                    /* disk capacity in sectors */
    ssize = SSB(type);                          /* get sector size in bytes */
    for (i=0; i<(int)ssize; i++)
        buff[i] = 0;                            /* zero the buffer */

    sim_debug(DEBUG_CMD, dptr, "SCSI Disk %s %04x cyl %d hds %d sec %d ssiz %d capacity %d\n",
        scsi_type[type].name, chsa, scsi_type[type].cyl, scsi_type[type].nhds, 
        scsi_type[type].spt, ssize, uptr->capac); /* disk capacity */
    printf("SCSI Disk %s %04x cyl %d hds %d sec %d ssiz %d capacity %d\r\n",
        scsi_type[type].name, chsa, scsi_type[type].cyl, scsi_type[type].nhds, 
        scsi_type[type].spt, ssize, uptr->capac); /* disk capacity */

    /* see if -i or -n specified on attach command */
    if ((sim_switches & SWMASK('N')) || (sim_switches & SWMASK('I'))) {
        goto fmt;                               /* user wants new disk */
    }

    /* seek to end of disk */
    if ((sim_fseek(uptr->fileref, 0, SEEK_END)) != 0) {
        sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach SEEK end failed\n");
        printf( "SCSI Disk attach SEEK end failed\r\n");
        goto fmt;                               /* not setup, go format */
    }

    s = ftell(uptr->fileref);                   /* get current file position */
    if (s == 0) {
        sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach ftell failed s=%06d\n", s);
        printf("SCSI Disk attach ftell failed s=%06d\r\n", s);
        goto fmt;                               /* not setup, go format */
    }
    sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach ftell value s=%06d b=%06d CAP %06d\n", s/ssize, s, CAP(type));
    printf("SCSI Disk attach ftell value s=%06d b=%06d CAP %06d\r\n", s/ssize, s, CAP(type));

    if (((int)s/(int)ssize) < ((int)CAP(type))) {   /* full sized disk? */
        j = (CAP(type) - (s/ssize));            /* get # sectors to write */
        sim_debug(DEBUG_CMD, dptr,
            "SCSI Disk attach for MPX 1.X needs %04d more sectors added to disk\n", j);
        printf("SSFI Disk attach for MPX 1.X needs %04d more sectors added to disk\r\n", j);
        /* must be MPX 1.X disk, extend to MPX 3.X size */
        /* write sectors of zero to end of disk to fill it out */
        for (i=0; i<j; i++) {
            if ((r = sim_fwrite(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) {
                sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach fread ret = %04d\n", r);
                printf("SCSI Disk attach fread ret = %04d\r\n", r);
                goto fmt;                       /* not setup, go format */
            }
        }
        s = ftell(uptr->fileref);               /* get current file position */
        sim_debug(DEBUG_CMD, dptr,
            "SCSI Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\n", s/ssize, s);
        printf("SCSI Disk attach MPX 1.X  file extended & sized secs %06d bytes %06d\r\n", s/ssize, s);
    }

    /* seek last sector of disk */
    if ((sim_fseek(uptr->fileref, (CAP(type)-1)*ssize, SEEK_SET)) != 0) {
        sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach SEEK last sector failed\n");
        printf("SCSI Disk attach SEEK last sector failed\r\n");
        goto fmt;
    }

    /* see if there is disk size-1 in last sector of disk, if not add it */
    if ((r = sim_fread(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) {
        sim_debug(DEBUG_CMD, dptr, "SCSI Disk format fread error = %04d\n", r);
        printf("SCSI Disk format fread error = %04d\r\n", r);
add_size:
        if (ssize == 768) {
            /* assume we have MPX 1x, and go on */
            /* write dmap data to last sector on disk for mpx 1.x */
            if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */
                sim_debug(DEBUG_CMD, dptr,
                    "SCSI Error on last sector seek to sect %06d offset %06d bytes\n",
                    (CAP(type)-1), (CAP(type)-1)*ssize);
                printf("SCSI Error on last sector seek to sect %06d offset %06d bytes\r\n",
                    (CAP(type)-1), (CAP(type)-1)*ssize);
                goto fmt;
            }
            if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) {
                sim_debug(DEBUG_CMD, dptr,
                    "SCSI Error writing DMAP to sect %06x offset %06d bytes\n",
                    (CAP(type)-1), (CAP(type)-1)*ssize);
                printf("SCSI Error writing DMAP to sect %06x offset %06d bytes\r\n",
                    (CAP(type)-1), (CAP(type)-1)*ssize);
                goto fmt;
            }

            /* seek last sector of disk */
            if ((sim_fseek(uptr->fileref, (CAP(type))*ssize, SEEK_SET)) != 0) {
                sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach SEEK last sector failed\n");
                printf( "SCSI Disk attach SEEK last sector failed\r\n");
                goto fmt;
            }
            s = ftell(uptr->fileref);           /* get current file position */
            sim_debug(DEBUG_CMD, dptr,
                "SCSI Disk attach MPX file extended & sized secs %06d bytes %06d\n", s/ssize, s);
            printf("SCSI Disk attach MPX file extended & sized secs %06d bytes %06d\r\n", s/ssize, s);
            goto ldone;
        } else {
            /* error if UTX */
            detach_unit(uptr);                  /* if error, abort */
            return SCPE_FMT;                    /* error */
        }
    } else {
        /* if not disk size, go add it in for MPX, error if UTX */
        if ((buff[0] | buff[1] | buff[2] | buff[3]) == 0) {
            sim_debug(DEBUG_CMD, dptr,
                "SCSI Disk format0 buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n",
                buff[0], buff[1], buff[2], buff[3]);
            goto add_size;
        }
    }

    /* the last sector is used by UTX for a ZMAP, so if there we are good to go */
    info = (buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3];
    good = 0xf0000000 | (CAP(type)-1);
    /* check for 0xf0ssssss where ssssss is disk size-1 in sectors */
    if ((info != good) && (info != zmap)) {
        sim_debug(DEBUG_CMD, dptr,
            "SCSI Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n",
            buff[0], buff[1], buff[2], buff[3]);
        printf("SCSI Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\r\n",
            buff[0], buff[1], buff[2], buff[3]);
fmt:
        /* format the drive */
        if (scsi_format(uptr)) {
            detach_unit(uptr);                  /* if no space, error */
            return SCPE_FMT;                    /* error */
        }
    }
ldone:
    if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */
        detach_unit(uptr);                      /* if no space, error */
        return SCPE_FMT;                        /* error */
    }

    /* start out at sector 0 */
    uptr->CHS = 0;                              /* set CHS to cyl/hd/sec = 0 */

    sim_debug(DEBUG_CMD, dptr,
        "SCSI Attach %s %04x cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\n",
        scsi_type[type].name, chsa, CYL(type), HDS(type), SPT(type), SPC(type),  
        CAP(type), CAPB(type));

    printf("SCSI Attach %s %04x cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\r\n",
        scsi_type[type].name, chsa, CYL(type), HDS(type), SPT(type), SPC(type),  
        CAP(type), CAPB(type));

    sim_debug(DEBUG_CMD, dptr, "SCSI File %s at chsa %04x attached to %s is ready\n",
        file, chsa, scsi_type[type].name);
    printf("SCSI File %s at chsa %04x attached to %s is ready\r\n",
        file, chsa, scsi_type[type].name);

    /* check for valid configured disk */
    /* must have valid DIB and Channel Program pointer */
    dibp = (DIB *)dptr->ctxt;                   /* get the DIB pointer */
    if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (dibp->chan_prg == NULL)) {
        sim_debug(DEBUG_CMD, dptr,
            "ERROR===ERROR\nSCSI device %s not configured on system, aborting\n",
            dptr->name);
        printf("ERROR===ERROR\nSCSI device %s not configured on system, aborting\n",
            dptr->name);
        detach_unit(uptr);                      /* detach if error */
        return SCPE_UNATT;                      /* error */
    }
    set_devattn(chsa, SNS_DEVEND);
    return SCPE_OK;
}

/* detach a disk device */
t_stat scsi_detach(UNIT *uptr) {
    uptr->SNS = 0;                              /* clear sense data */
    uptr->CMD &= LMASK;                         /* no cmd and flags */
    return detach_unit(uptr);                   /* tell simh we are done with disk */
}

/* Handle haltio transfers for disk */
t_stat  scsi_haltio(UNIT *uptr) {
    uint16      chsa = GET_UADDR(uptr->CMD);
    DEVICE      *dptr = get_dev(uptr);
    int         cmd = uptr->CMD & DSK_CMDMSK;
    CHANP       *chp = find_chanp_ptr(chsa);    /* find the chanp pointer */

    sim_debug(DEBUG_DETAIL, dptr,
        "scsi_haltio enter chsa %04x cmd = %02x\n", chsa, cmd);

    /* terminate any input command */
    /* UTX wants SLI bit, but no unit exception */
    /* status must not have an error bit set */
    /* otherwise, UTX will panic with "bad status" */
    if ((uptr->CMD & DSK_CMDMSK) != 0) {        /* is unit busy */
        sim_debug(DEBUG_EXP, dptr,
            "scsi_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n",
            chsa, cmd, chp->ccw_count);
        sim_cancel(uptr);                       /* clear the input timer */
    } else {
        sim_debug(DEBUG_DETAIL, dptr,
            "scsi_haltio HIO I/O not busy chsa %04x cmd = %02x\n", chsa, cmd);
    }
    /* stop any I/O and post status and return error status */
    chp->ccw_flags &= ~(FLAG_DC|FLAG_CC);       /* stop any chaining */
    uptr->CMD &= LMASK;                         /* no cmd and flags */
    uptr->SNS &= ~MASK24;                       /* clear all but old mode data */
    sim_debug(DEBUG_EXP, dptr,
        "scsi_haltio HIO I/O stop chsa %04x cmd = %02x CHS %08x STAR %08x\n",
        chsa, cmd, uptr->CHS, uptr->STAR);
    chan_end(chsa, SNS_CHNEND|SNS_DEVEND);      /* force end */
    return SCPE_IOERR;
}

/* boot from the specified disk unit */
t_stat scsi_boot(int32 unit_num, DEVICE *dptr) {
    UNIT    *uptr = &dptr->units[unit_num];     /* find disk unit number */

    sim_debug(DEBUG_CMD, dptr, "SCSI Disk Boot dev/unit %04x\n", GET_UADDR(uptr->CMD));

    /* see if device disabled */
    if (dptr->flags & DEV_DIS) {
        printf("ERROR===ERROR\r\nSCSI Disk device %s disabled on system, aborting\r\n",
            dptr->name);
        return SCPE_UDIS;                       /* device disabled */
    }

    if ((uptr->flags & UNIT_ATT) == 0) {
        sim_debug(DEBUG_EXP, dptr, "SCSI Disk Boot attach error dev/unit %04x\n",
            GET_UADDR(uptr->CMD));
        printf("SCSI Disk Boot attach error dev/unit %04x\n", GET_UADDR(uptr->CMD));
        return SCPE_UNATT;                      /* attached? */
    }

    /* seek to sector 0 */
    if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */
        printf("SCSI Disk Boot Error on seek to 0\n");
    }

    SPAD[0xf4] = GET_UADDR(uptr->CMD);          /* put boot device chan/sa into spad */
    SPAD[0xf8] = 0xF000;                        /* show as F class device */
    return chan_boot(GET_UADDR(uptr->CMD), dptr);    /* boot the ch/sa */
}

/* Disk option setting commands */
t_stat scsi_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    int     i;

    if (cptr == NULL)                           /* any disk name input? */
        return SCPE_ARG;                        /* arg error */
    if (uptr == NULL)                           /* valid unit? */
        return SCPE_IERR;                       /* no, error */
    if (uptr->flags & UNIT_ATT)                 /* is unit attached? */
        return SCPE_ALATT;                      /* no, error */

    /* now loop through the units and find named disk */
    for (i = 0; scsi_type[i].name != 0; i++) {
        if (strcmp(scsi_type[i].name, cptr) == 0) {
            uptr->flags &= ~UNIT_TYPE;          /* clear the old UNIT type */
            uptr->flags |= SET_TYPE(i);         /* set the new type */
            /* set capacity of disk in sectors */
            uptr->capac = CAP(i);
            return SCPE_OK;
        }
    }
    return SCPE_ARG;
}

t_stat scsi_get_type(FILE * st, UNIT *uptr, int32 v, CONST void *desc)
{
    if (uptr == NULL)
        return SCPE_IERR;
    fputs("TYPE=", st);
    fputs(scsi_type[GET_TYPE(uptr->flags)].name, st);
    return SCPE_OK;
}

/* help information for disk */
t_stat scsi_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
    const char *cptr)
{
    int i;
    fprintf (st, "SEL-32 MFP SCSI Bus Disk Controller\r\n");
    fprintf (st, "Use:\r\n");
    fprintf (st, "    sim> SET %sn TYPE=type\r\n", dptr->name);
    fprintf (st, "Type can be: ");
    for (i = 0; scsi_type[i].name != 0; i++) {
        fprintf(st, "%s", scsi_type[i].name);
        if (scsi_type[i+1].name != 0)
        fprintf(st, ", ");
    }
    fprintf (st, ".\nEach drive has the following storage capacity:\r\n");
    for (i = 0; scsi_type[i].name != 0; i++) {
        int32   size = CAPB(i);                 /* disk capacity in bytes */
        size /= 1024;                           /* make KB */
        size = (10 * size) / 1024;              /* size in MB * 10 */
        fprintf(st, "      %-8s %4d.%1d MB cyl %3d hds %3d sec %3d blk %3d\r\n",
            scsi_type[i].name, size/10, size%10, CYL(i), HDS(i), SPT(i), SSB(i));
    }
    fprint_set_help(st, dptr);
    fprint_show_help(st, dptr);
    return SCPE_OK;
}

const char *scsi_description (DEVICE *dptr)
{
    return "SEL-32 MFP SCSI Disk Controller";
}

#endif
