SEL32: Update Ping and ICMP support code to use correct packet size. SEL32: Update SetupNet script to support latest Fedora release. SEL32: Improve disk write speed. SEL32: Add .tap file reassignent support in sel32_mt.c.
2052 lines
96 KiB
C
2052 lines
96 KiB
C
/* 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
|