blob: e51c3c1937a03d1a1012f34b3fcfc9debdd6a8db [file] [log] [blame] [raw]
/* i7090_disk.c: IBM 7090 Disk
Copyright (c) 2005-2016, Richard Cornwell
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNRWELL 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.
Support for 1301/1302/2302 disks and 7238 drums
Disks are represented in files as follows:
Since these drives supported variable format for each cylinder the
format is represented as one track per cylinder as follows:
0 data
1 header
2 Home Address
3 end of track
These codes are packed 4 per byte and used to control read/write of
data.
After one format track per cylinder there is one record of bytes per
track data for each track. First bytes are home address 2, followed
by record address, and record data to cover number in format. All data
is stored with the top 2 bits as zero.
Limitiation of this are that the address field for each record can not
be more then 16 bytes.
*/
#include "i7000_defs.h"
#ifdef NUM_DEVS_DSK
#define UNIT_DSK UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX
#define FORMAT_OK (1 << (UNIT_V_LOCAL+0))
#define HA2_OK (1 << (UNIT_V_LOCAL+1))
#define CTSS_BOOT (1 << (UNIT_V_MODE))
/* Device status information stored in u5 */
#define DSKSTA_CMD 0x0000100 /* Unit has recieved a cmd */
#define DSKSTA_DATA 0x0000200 /* Unit has finished cmd */
#define DSKSTA_WRITE 0x0000400 /* Last command was a write */
#define DSKSTA_CHECK 0x0000800 /* Doing a write check */
#define DSKSTA_CMSK 0x00000ff /* Command mask */
#define DSKSTA_ARGMSK 0x0fff000 /* Command argument */
#define DSKSTA_ARGSHFT 12
#define DSKSTA_SCAN 0x1000000 /* Scanning for header */
#define DSKSTA_SKIP 0x2000000 /* Skiping record */
#define DSKSTA_XFER 0x4000000 /* Tranfser current record */
#define DSKSTA_DIRTY 0x8000000 /* Buffer needs to be written */
#define FMT_DATA 0 /* Data */
#define FMT_HDR 1 /* Header */
#define FMT_HA2 2 /* Home address 2 */
#define FMT_END 3 /* End of track */
/* Disk commands */
#define DNOP 0x00 /* Nop */
#define DREL 0x04 /* Release */
#define DEBM 0x08 /* Eight Bit mode */
#define DSBM 0x09 /* Six bit mode */
#define DSEK 0x80 /* Seek */
#define DVSR 0x82 /* Prepare to Verify single record */
#define DWRF 0x83 /* Prepare to Format */
#define DVTN 0x84 /* Prepare to Verify track no addr */
#define DVCY 0x85 /* Prepare to Verify Cyl */
#define DWRC 0x86 /* Prepare to Write Check */
#define DSAI 0x87 /* Set Access Inoperative */
#define DVTA 0x88 /* Prepare to Verify track addr */
#define DVHA 0x89 /* Prepare to Verify home addr */
/* Disk sense codes */ /*01234 */
#define STAT_SIXBIT 0x00004 /* Disk in 6bit more. */
#define EXPT_FILECHK 0x10010 /* File control check error */
#define EXPT_DSKCHK 0x10020 /* Disk storage error */
#define STAT_NOTRDY 0x10040 /* Disk no ready */
#define STAT_OFFLINE 0x10080 /* Disk offline */
#define DATA_PARITY 0x20100 /* Data parity error */
#define DATA_CHECK 0x20200 /* Compare error */
#define DATA_RESPONSE 0x20400 /* Response check */
#define PROG_INVADDR 0x40800 /* Invalid seek address */
#define PROG_NOREC 0x41000 /* No record found */
#define PROG_FMTCHK 0x42000 /* Format check */
#define PROG_INVCODE 0x44000 /* Invalid code */
#define PROG_INVSEQ 0x48000 /* Invalid sequence */
#define MAXTRACK 6020 /* Max size per track */
uint32 dsk_cmd(UNIT *, uint16, uint16);
t_stat dsk_srv(UNIT *);
t_stat dsk_boot(int32, DEVICE *);
void dsk_ini(UNIT *, t_bool);
t_stat dsk_reset(DEVICE *);
t_stat dsk_set_module(UNIT * uptr, int32 val, CONST char *cptr,
void *desc);
t_stat dsk_get_module(FILE * st, UNIT * uptr, int32 v,
CONST void *desc);
t_stat dsk_set_type(UNIT * uptr, int32 val, CONST char *cptr,
void *desc);
t_stat dsk_get_type(FILE * st, UNIT * uptr, int32 v,
CONST void *desc);
t_stat dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *dsk_description (DEVICE *dptr);
int disk_rblock(UNIT * uptr, int track);
int disk_wblock(UNIT * uptr);
void disk_posterr(UNIT * uptr, uint32 error);
void disk_cmderr(UNIT * uptr, uint32 error);
int disk_cmd(UNIT * uptr);
int disk_write(UNIT * uptr, uint8 data, int chan,
int eor);
int disk_read(UNIT * uptr, uint8 * data, int chan);
int disk_format(UNIT * uptr, FILE * f, int cyl,
UNIT * base);
int bcd_to_track(uint32 addr);
/* Data buffer for track */
uint8 dbuffer[NUM_DEVS_DSK * 4][MAXTRACK];
/* Format buffer for cylinder */
uint8 fbuffer[NUM_DEVS_DSK * 4][MAXTRACK / 4];
/* Currently loaded format record */
uint16 fmt_cyl[NUM_DEVS_DSK * 4];
/* Currently read in track in buffer */
uint16 dtrack[NUM_DEVS_DSK * 4];
/* Arm position */
uint16 arm_cyl[NUM_DEVS_DSK * 4];
uint32 sense[NUM_CHAN * 2];
uint32 sense_unit[NUM_CHAN * 2];
uint8 cmd_buffer[NUM_CHAN]; /* Command buffer per channel */
uint8 cmd_mod[NUM_CHAN]; /* Command module per channel */
uint32 cmd_option[NUM_CHAN]; /* Command option per channel */
uint16 cmd_count[NUM_CHAN]; /* Number of chars recieved */
#ifdef I7010
extern uint8 chan_seek_done[NUM_CHAN]; /* Seek finished flag */
extern uint8 chan_io_status[NUM_CHAN]; /* Channel status flags */
#endif
/* Macro to help build the disk size table */
#define DISK_DEF(name, cyl, cylpertrk, acc, charpertrk, overhd, mod, dr) \
{ name, cyl, cylpertrk, ((charpertrk/128) + 1) * 128, \
acc, (((charpertrk/128) + 1) * 128)/4, \
acc * cyl * ((((charpertrk/128) + 1) * 128)/4), \
overhd, mod, dr }
struct disk_t
{
const char *name; /* Type Name */
int cyl; /* Number of cylinders */
int track; /* Number of tracks/cylinder */
unsigned int bpt; /* Max bytes per track */
int arms; /* Number of access arms */
int fbpt; /* Number of format bytes per track */
int fmtsz; /* Format size */
int overhd; /* Number of characters overhead on HA/RA */
int mods; /* Number of modules */
int datarate; /* us per chars */
}
disk_type[] =
{
DISK_DEF("1301", 254, 40, 1, 2880, 4, 1, 15),
DISK_DEF("1301-2", 254, 40, 1, 2880, 4, 2, 15),
DISK_DEF("1302", 254, 40, 2, 5940, 7, 1, 10),
DISK_DEF("1302-2", 254, 40, 2, 5940, 7, 2, 10),
DISK_DEF("2302", 254, 40, 2, 5940, 7, 2, 10),
DISK_DEF("7238", 1, 404, 1, 3270, 4, 1, 10),
DISK_DEF("7238-2", 1, 404, 1, 3270, 4, 2, 10), {
NULL, 0}
};
int unit_bit[] = {
/*0 1 2 3 4 5 6 7 8 9 A B C D E F */
19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 30, 30, 30, 30, 30, 30,
9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 30, 30, 30, 30, 30, 30
};
#define DSKSTA_BSY 0x02 /* Controller busy. */
#define DSKSTA_EIGHT 0x04 /* Controller in 8 bit mode */
#ifdef I7090
#define CH1 4
#define CH2 6
#endif
#ifdef I7070
#define CH1 5
#define CH2 6
#endif
#ifdef I7080
#define CH1 5
#define CH2 6
#endif
#ifndef CH1
#define CH1 1
#endif
#ifndef CH2
#define CH2 2
#endif
UNIT dsk_unit[] = {
{UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x000, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x102, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x204, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x306, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x408, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x500, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x602, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x704, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x806, 0},
{UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x908, 0},
/* Second set for arm two of each unit */
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
/* Third set for arm one of second module */
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
/* Fourth set for arm two of second module */
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
{UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0},
};
MTAB dsk_mod[] = {
{FORMAT_OK, 0, 0, "NOFORMAT", NULL, NULL, NULL, "Format not allowed"},
{FORMAT_OK, FORMAT_OK, "FORMAT", "FORMAT", NULL, NULL, NULL,
"Format allowed"},
{HA2_OK, 0, 0, "NOHA2", NULL, NULL, NULL, "No writing of Home Address"},
{HA2_OK, HA2_OK, "HA2", "HA2", NULL, NULL, NULL,
"Allow writing of Home Address"},
#ifdef I7090
{CTSS_BOOT, 0, 0, "IBSYS", NULL, NULL, NULL, "IBSYS Boot Card"},
{CTSS_BOOT, CTSS_BOOT, "CTSS", "CTSS", NULL, NULL, NULL, "CTSS Boot Card"},
#endif
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE",
&dsk_set_type, &dsk_get_type, NULL, "Type of disk"},
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "MODULE", "MODULE",
&dsk_set_module, &dsk_get_module, NULL, "Module number"},
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN",
&set_chan, &get_chan, NULL, "Channel number"},
#ifndef I7010
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "SELECT", "SELECT",
&chan9_set_select, &chan9_get_select, NULL, "Unit select"},
#endif
{0}
};
DEVICE dsk_dev = {
"DK", dsk_unit, NULL /* Registers */ , dsk_mod,
NUM_DEVS_DSK, 8, 15, 1, 8, 8,
NULL, NULL, &dsk_reset, &dsk_boot, NULL, NULL,
&dsk_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &dsk_help, NULL, NULL, &dsk_description
};
uint32 dsk_cmd(UNIT * uptr, uint16 cmd, uint16 dev)
{
int chan;
int u = (uptr->u3 >> 8) & 0xf;
UNIT *base = &dsk_unit[u];
int sel;
chan = UNIT_G_CHAN(dsk_unit[u].flags);
sel = (base->flags & UNIT_SELECT) ? 1 : 0;
#ifdef I7010
if (cmd & 0x100)
sense[(chan * 2) + sel] |= STAT_SIXBIT;
else
sense[(chan * 2) + sel] &= ~STAT_SIXBIT;
cmd_buffer[chan] = cmd & 0xff;
cmd_count[chan] = 2;
sim_debug(DEBUG_CHAN, &dsk_dev, "unit %d = cmd=%02x\n\r",
dev, cmd & 0xff);
#else
cmd_buffer[chan] = 0;
cmd_count[chan] = 0;
sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d cmd\n\r", dev);
#endif
cmd_option[chan] = 0;
cmd_mod[chan] = 0;
chan_clear(chan, DEV_SEL);
/* If device is not active start it working */
if (!sim_is_active(uptr))
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
t_stat dsk_srv(UNIT * uptr)
{
int chan;
int sel;
int schan;
int dev = uptr->u3 & 0xff;
int u = (uptr->u3 >> 8) & 0xf;
struct disk_t *dsk = &disk_type[uptr->u4];
UNIT *base = &dsk_unit[u];
uint8 ch = 0;
int eor = 0;
chan = UNIT_G_CHAN(base->flags);
sel = (base->flags & UNIT_SELECT) ? 1 : 0;
schan = (chan * 2) + sel;
/* Make sure channel is talking to us */
if (sel != chan_test(chan, CTL_SEL))
goto seeking;
/* Channel has disconnected, abort current operation. */
if (chan_test(chan, DEV_DISCO)) {
int i;
/* Scan all units and stop them if they are on this channel */
for (i = 0; i < NUM_DEVS_DSK; i++) {
int xchan = UNIT_G_CHAN(dsk_unit[i].flags);
int j;
if (xchan != chan)
continue;
for (j = 3 * NUM_DEVS_DSK + i; j >= 0; j -= NUM_DEVS_DSK) {
disk_wblock(&dsk_unit[j]); /* Flush block */
if (dsk_unit[j].u5 & DSKSTA_CMD) {
dsk_unit[j].u5 &=
~(DSKSTA_CMD | DSKSTA_XFER | DSKSTA_SCAN | DSKSTA_DATA);
sim_cancel(&dsk_unit[j]);
}
}
}
chan_clear(chan, (DEV_DISCO | DEV_WEOR | DEV_SEL));
sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d disconnecting\n\r", dev);
if (uptr->wait > 0)
sim_activate(uptr, us_to_ticks(100));
return SCPE_OK;
}
/* Call channel proccess to make sure all date is available. */
chan_proc();
/* Handle sending sense data */
if (chan_test(chan, CTL_SNS)) {
chan9_clear_error(chan, sel);
switch(cmd_count[chan]) {
case 0:
sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d chan sense=%05x\n", dev,
sense[schan]);
/* fall through */
case 1: case 2: case 3: case 4:
ch = (sense[schan] >> (4 * (4 - cmd_count[chan]))) & 0xF;
break;
case 9:
sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d unit sense=%08x\n", dev,
sense_unit[schan]);
eor = DEV_REOR;
/* fall through */
case 5: case 6: case 7: case 8:
ch = (sense_unit[schan] >> (4 * (9 - cmd_count[chan]))) & 0xF;
break;
}
if (ch & 010)
ch ^= 030;
sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d sense %d %02o\n\r", dev,
cmd_count[chan], ch);
cmd_count[chan]++;
switch(chan_write_char(chan, &ch, eor)) {
case DATA_OK:
if (eor == 0)
break;
case TIME_ERROR:
case END_RECORD:
sense[chan] &= STAT_SIXBIT;
sense_unit[chan] = 0;
chan_set(chan, CTL_END);
chan_clear(chan, DEV_SEL);
sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d sense eor\n\r", dev);
break;
}
sim_activate(uptr, us_to_ticks(20));
return SCPE_OK;
}
if (chan_test(chan, CTL_CNTL) && disk_cmd(uptr)) {
sim_activate(uptr, us_to_ticks(50));
return SCPE_OK;
}
/* Handle writing of data */
if (chan_test(chan, CTL_WRITE) && uptr->u5 & DSKSTA_CMD) {
/* Channel asking for end of record? */
if (chan_stat(chan, DEV_WEOR)) {
sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d -> weor\n\r", chan);
while (disk_write(uptr, 0x40, chan, 1) == 0) ;
disk_wblock(uptr); /* Flush block */
uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER);
uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER);
chan_set(chan, CTL_END|DEV_REOR);
sim_activate(uptr, us_to_ticks(100));
return SCPE_OK;
}
uptr->u5 |= DSKSTA_WRITE; /* Flag as write */
switch(chan_read_char(chan, &ch, 0)) {
case TIME_ERROR:
disk_posterr(uptr, DATA_RESPONSE);
sim_activate(uptr, us_to_ticks(100));
return SCPE_OK;
case END_RECORD:
/* If last word, fill with zeros until we get eor */
sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d eor\n\r",chan);
uptr->u5 |= DSKSTA_DATA;
while (disk_write(uptr, 0x40, chan, 1) == 0) ;
disk_wblock(uptr); /* Flush block */
uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER);
chan_set(chan, CTL_END);
sim_activate(uptr, us_to_ticks(100));
break;
case DATA_OK:
eor = disk_write(uptr, ch, chan, 0);
uptr->u5 |= DSKSTA_DATA;
if (eor == 1) {
/* Handle end of record */
sim_debug(DEBUG_CHAN, &dsk_dev,
"Disk chan %d end of track\n\r", chan);
if ((uptr->u5 & DSKSTA_CMSK) == DVSR &&
(uptr->u5 & DSKSTA_XFER) == 0)
disk_posterr(uptr, PROG_NOREC);
uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER);
chan_set(chan, DEV_REOR|CTL_END);
}
sim_activate(uptr, us_to_ticks(dsk->datarate));
return SCPE_OK;
}
}
/* Handle reading of data */
if (chan_test(chan, CTL_READ) && uptr->u5 & DSKSTA_CMD) {
/* Channel asking for end of record? */
if (chan_stat(chan, DEV_WEOR)) {
sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d -> weor\n\r", chan);
uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER);
chan_set(chan, CTL_END|DEV_REOR);
sim_activate(uptr, us_to_ticks(100));
return SCPE_OK;
}
eor = disk_read(uptr, &ch, chan);
/* Check if we got error during read */
if (eor == -1) {
sim_activate(uptr, us_to_ticks(100));
return SCPE_OK;
}
switch(chan_write_char(chan, &ch, (eor)?DEV_REOR:0)) {
case TIME_ERROR:
/* Flag as timming error */
disk_posterr(uptr, DATA_RESPONSE);
break;
case END_RECORD:
/* Fill the buffer until end of record */
sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d eor\n\r", chan);
if ((uptr->u5 & DSKSTA_CMSK) == DVSR &&
(uptr->u5 & DSKSTA_XFER) == 0)
disk_posterr(uptr, PROG_NOREC);
uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER);
chan_set(chan, CTL_END);
uptr->u5 |= DSKSTA_DATA;
break;
case DATA_OK:
uptr->u5 |= DSKSTA_DATA;
break;
}
sim_activate(uptr, us_to_ticks(dsk->datarate));
return SCPE_OK;
}
/* Handle read/write without a command */
if (chan_test(chan, CTL_WRITE|CTL_READ) &&
(uptr->u3 & 0xff) == cmd_mod[chan] &&
(uptr->u5 & (DSKSTA_DATA|DSKSTA_CMD)) == 0)
disk_posterr(uptr, PROG_INVSEQ);
seeking:
if (uptr->wait || uptr->u5 & DSKSTA_CMD)
sim_activate(uptr, us_to_ticks(100));
/* Handle seeking */
if (uptr->wait > 0) {
uptr->wait--;
if (uptr->wait == 0) {
sim_debug(DEBUG_EXP, &dsk_dev, "Seek done dev=%d\n", dev);
sense_unit[schan] |= 1 << unit_bit[dev];
#ifdef I7010
chan_seek_done[chan] = 1;
#else
chan9_set_attn(chan, sel);
#endif
}
}
return SCPE_OK;
}
/* Post a error on a given unit. */
void
disk_posterr(UNIT * uptr, uint32 error)
{
int chan;
int schan;
int sel;
int u = (uptr->u3 >> 8) & 0xf;
uptr = &dsk_unit[u];
uptr->u5 &= ~(DSKSTA_CMD | DSKSTA_XFER | DSKSTA_SCAN);
chan = UNIT_G_CHAN(uptr->flags);
sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
schan = (chan * 2) + sel;
sense[schan] |= error;
if (error != 0)
chan9_set_error(chan, SNS_UEND);
chan_set(chan, DEV_REOR|CTL_END);
sim_debug(DEBUG_DETAIL, &dsk_dev, "post err dev=%d err=%08x\n", u,error);
#ifdef I7010
if ((error & STAT_OFFLINE) == STAT_OFFLINE)
chan_io_status[chan] |= 01;
if ((error & STAT_NOTRDY) == STAT_NOTRDY)
chan_io_status[chan] |= 02;
if (error & (EXPT_FILECHK|EXPT_DSKCHK|DATA_PARITY|DATA_CHECK|
DATA_RESPONSE|PROG_INVADDR) & 0xFFFF)
chan_io_status[chan] |= 04;
if (error & (PROG_NOREC|PROG_FMTCHK|PROG_INVCODE|PROG_INVSEQ) & 0xffff)
chan_io_status[chan] |= 010;
#endif
}
/* Post error for command that could not be completed */
void
disk_cmderr(UNIT * uptr, uint32 error)
{
int chan;
int schan;
int sel;
int u = (uptr->u3 >> 8) & 0xf;
uptr = &dsk_unit[u];
uptr->u5 &= ~(DSKSTA_CMSK | DSKSTA_CMD | DSKSTA_CHECK | DSKSTA_WRITE);
chan = UNIT_G_CHAN(uptr->flags);
sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
schan = (chan * 2) + sel;
sense[schan] |= error;
if (error != 0)
chan9_set_error(chan, SNS_UEND);
chan_set(chan, DEV_REOR|CTL_END);
sim_debug(DEBUG_DETAIL, &dsk_dev, "cmd err dev=%d err=%08x\n", u,error);
#ifdef I7010
if ((error & STAT_OFFLINE) == STAT_OFFLINE)
chan_io_status[chan] |= 01;
if ((error & STAT_NOTRDY) == STAT_NOTRDY)
chan_io_status[chan] |= 02;
if (error & (EXPT_FILECHK|EXPT_DSKCHK|DATA_PARITY|DATA_CHECK|
DATA_RESPONSE|PROG_INVADDR) & 0xFFFF)
chan_io_status[chan] |= 04;
if (error & (PROG_NOREC|PROG_FMTCHK|PROG_INVCODE|PROG_INVSEQ) & 0xffff)
chan_io_status[chan] |= 010;
#endif
}
/* Process command */
int
disk_cmd(UNIT * uptr)
{
uint8 ch;
UNIT *base;
UNIT *up;
int chan;
int schan;
int sel;
int i;
int u;
int t;
int trk;
int cyl;
/* Get information on unit */
u = uptr - dsk_unit;
base = &dsk_unit[(uptr->u3 >> 8) & 0xf];
chan = UNIT_G_CHAN(base->flags);
sel = (base->flags & UNIT_SELECT) ? 1 : 0;
schan = (chan * 2) + sel;
/* Check if we have a command yet */
switch(chan_read_char(chan, &ch, 0)) {
case TIME_ERROR:
disk_cmderr(uptr, DATA_RESPONSE);
case END_RECORD:
return 0;
case DATA_OK:
sim_debug(DEBUG_DATA, &dsk_dev, "unit=%d data=%02o\n", u, ch);
break;
}
/* Place in command buffer */
switch(cmd_count[chan]) {
case 0:
case 1:
if (ch != 012)
cmd_buffer[chan] |= (ch & 0xf) << (4 * (1 - cmd_count[chan]));
break;
case 2:
case 3:
if (ch != 012)
cmd_mod[chan] |= (ch & 0xf) << (4 * (3 - cmd_count[chan]));
break;
case 4:
case 5:
case 6:
case 7:
if (ch != 012)
cmd_option[chan] |= (ch & 0xf) << (16 + (4 * (7 - cmd_count[chan])));
break;
case 8:
case 9:
cmd_option[chan] |= (ch & 0x3f) << (6 * (9 - cmd_count[chan]));
break;
}
/* Need at least two chars to determine command */
if (++cmd_count[chan] == 1)
return 1;
/* Check if we have enough digits */
switch(cmd_buffer[chan]) {
case DNOP: /* Nop */
case DREL: /* Release */
case DEBM: /* Eight Bit mode */
case DSBM: /* Six bit mode */
break; /* Yes */
case DSAI: /* Set Access Inoperative */
if (cmd_count[chan] <= 3)
return 1; /* Need more */
break;
case DSEK: /* Seek */
case DWRF: /* Prepare to Format */
case DVHA: /* Prepare to Verify home addr */
if (cmd_count[chan] <= 7)
return 1; /* Need more */
break;
case DVTA: /* Prepare to Verify track addr */
case DVTN: /* Prepare to Verify track no addr */
case DVCY: /* Prepare to Verify Cyl */
case DVSR: /* Prepare to Verify single record */
case DWRC: /* Prepare to Write Check */
if (cmd_count[chan] < 10)
return 1;
break;
}
/* Flag last item recieved */
chan_set(chan, DEV_REOR);
chan9_clear_error(chan, sel);
sim_debug(DEBUG_CMD, &dsk_dev, "unit=%d cmd=%02x %02x %04x %04o ",u,
cmd_buffer[chan], cmd_mod[chan], cmd_option[chan] >> 16,
cmd_option[chan] & 07777);
/* 40 36 32 2824 2016 6 0 */
/* 01 2 3 4 5 6 7 8 9 */
/* op | ac mod | t t | t t | HA2 | HA2 */
up = NULL;
sense[schan] &= STAT_SIXBIT;
switch (cmd_buffer[chan]) {
case DNOP: /* Nop */
case DREL: /* Release */
/* Should not happen, but if read or write, cpy will be
* expected so turn on command flag to catch disconnect */
sim_debug(DEBUG_CMD, &dsk_dev, "nop\n");
goto clear_drive;
case DEBM: /* Eight Bit mode */
/* Should not happen, but if read or write, cpy will be
* expected so turn on command flag to catch disconnect */
sim_debug(DEBUG_CMD, &dsk_dev, "eight bit mode\n");
sense[schan] &= ~STAT_SIXBIT;
goto clear_drive;
case DSBM: /* Six bit mode */
/* Should not happen, but if read or write, cpy will be
* expected so turn on command flag to catch disconnect */
sim_debug(DEBUG_CMD, &dsk_dev, "six bit mode\n");
sense[schan] |= STAT_SIXBIT;
clear_drive:
/* Scan all units and clear command */
for (i = 0; i < NUM_DEVS_DSK; i++) {
int xchan = UNIT_G_CHAN(dsk_unit[i].flags);
int j;
if (xchan != chan)
continue;
for (j = 3 * NUM_DEVS_DSK + i; j >= 0; j -= NUM_DEVS_DSK)
dsk_unit[j].u5 &= ~ DSKSTA_CMSK;
}
sim_activate(uptr, us_to_ticks(100));
return 1;
case DWRC: /* Prepare to Write Check */
sim_debug(DEBUG_CMD, &dsk_dev, "write check\n");
case DVSR: /* Prepare to Verify single record */
case DWRF: /* Prepare to Format */
case DVTN: /* Prepare to Verify track no addr */
case DVCY: /* Prepare to Verify Cyl */
case DVTA: /* Prepare to Verify track addr */
case DVHA: /* Prepare to Verify home addr */
case DSAI: /* Set Access Inoperative */
case DSEK: /* Seek */
break; /* Go find unit to operate on */
default:
sim_debug(DEBUG_CMD, &dsk_dev, " Unknown Command\n\r");
/* Flag as invalid command */
disk_cmderr(uptr, PROG_INVCODE);
return 1;
}
/* Find actual owner of this command */
for (i = 0; i < NUM_DEVS_DSK; i++) {
if ((dsk_unit[i].flags & (UNIT_SELECT | UNIT_CHAN)) !=
(base->flags & (UNIT_SELECT | UNIT_CHAN)))
continue;
/* Adjust for unit */
if ((0xff & dsk_unit[i].u3) == cmd_mod[chan])
up = &dsk_unit[i];
else if ((0xff & dsk_unit[i + NUM_DEVS_DSK].u3) == cmd_mod[chan])
up = &dsk_unit[i + NUM_DEVS_DSK];
else if ((0xff & dsk_unit[i + (NUM_DEVS_DSK * 2)].u3) == cmd_mod[chan])
up = &dsk_unit[i + (NUM_DEVS_DSK * 2)];
else if ((0xff & dsk_unit[i + (NUM_DEVS_DSK * 3)].u3) == cmd_mod[chan])
up = &dsk_unit[i + (NUM_DEVS_DSK * 3)];
else
continue;
/* Check if unit is busy */
if (cmd_buffer[chan] != DWRC && (up->u5 & DSKSTA_CMD || up->wait > 0)) {
sim_debug(DEBUG_CMD, &dsk_dev, "unit=%d busy\n", u);
if (up->wait > 5)
up->wait = 5;
disk_cmderr(uptr, STAT_NOTRDY);
return 1;
} else {
if (cmd_buffer[chan] == DWRC) {
if (up->u5 & DSKSTA_CMSK) {
up->u5 |= DSKSTA_CHECK;
up->u5 &= ~DSKSTA_DATA;
cmd_buffer[chan] = up->u5 & DSKSTA_CMSK;
} else {
disk_cmderr(up, PROG_INVSEQ);
return 0;
}
} else {
up->u5 &= ~(DSKSTA_CMSK|DSKSTA_CHECK|DSKSTA_WRITE|DSKSTA_DATA);
up->u5 |= cmd_buffer[chan];
}
break;
}
}
if (up == NULL) {
sim_debug(DEBUG_CMD, &dsk_dev, "invalid unit\n");
/* Flag as invalid command */
disk_cmderr(uptr, STAT_OFFLINE);
return 1;
}
/* Adjust to new unit */
u = up - dsk_unit;
base = &dsk_unit[(up->u3 >> 8) & 0xf];
chan = UNIT_G_CHAN(base->flags);
sel = (base->flags & UNIT_SELECT) ? 1 : 0;
schan = (chan * 2) + sel;
/* Clear unit attention */
sense_unit[schan] &= ~(1 << unit_bit[up->u3 & 0xff]);
/* Check if there is a unit here */
if ((base->flags & UNIT_ATT) == 0) {
disk_cmderr(uptr, STAT_OFFLINE);
return 1;
}
/* Find out track and cylinder of this operation */
trk = bcd_to_track(cmd_option[chan]);
if (chan_test(chan, CTL_PWRITE))
sim_debug(DEBUG_CMD, &dsk_dev, "write ");
if (chan_test(chan, CTL_PREAD))
sim_debug(DEBUG_CMD, &dsk_dev, "read ");
/* Do command */
switch (cmd_buffer[chan]) {
case DSAI: /* Set Access Inoperative */
detach_unit(base);
disk_cmderr(up, 0);
return 1;
case DSEK: /* Seek */
cyl = trk / disk_type[base->u4].track;
/* Check how far we have to move */
t = cyl - arm_cyl[u];
if (t < 0)
t = -t;
sim_debug(DEBUG_CMD, &dsk_dev,
"DSEK unit=%d %d cylinders to %d trk=%d\n", u, t, cyl, trk);
up->u5 &= ~(DSKSTA_CMSK | DSKSTA_CMD | DSKSTA_CHECK | DSKSTA_WRITE);
arm_cyl[u] = cyl; /* And cylinder */
if (cyl > disk_type[base->u4].cyl) {
disk_cmderr(up, PROG_INVADDR);
return 1;
}
/* Read in track buffer */
disk_rblock(up, trk);
/* Set up for seek wait */
up->wait = 0;
/* From documentation, it looks like seeks were a fixed time
* based on movement between cylinder groups */
if (t == 0) /* At cylinder, give attention */
up->wait = 2; /* Electronic select time */
else if (t > 50)
up->wait = (1800);
else if (t > 10)
up->wait = (1200);
else
up->wait = (300);
break;
case DWRF: /* Prepare to Format */
/* Verify ok to format */
if ((base->flags & FORMAT_OK) == 0) {
disk_cmderr(uptr, PROG_FMTCHK);
return 1;
}
cyl = trk / disk_type[base->u4].track;
/* Make sure positioned to correct track */
if (arm_cyl[u] != cyl) {
disk_cmderr(uptr, PROG_INVSEQ);
return 1;
}
/* Make sure head on valid cylinder */
if (cyl > disk_type[base->u4].cyl) {
disk_cmderr(up, PROG_INVADDR);
return 1;
}
fmt_cyl[u] = cyl;
sim_debug(DEBUG_CMD, &dsk_dev, "FMT unit=%d\n", u);
up->u5 |= DSKSTA_SCAN | DSKSTA_CMD | DSKSTA_WRITE; /* Flag as write */
up->u6 = 0;
chan_set(chan, DEV_SEL);
break;
case DVHA: /* Prepare to Verify home addr */
/* Verify HA2 ok to write */
if ((base->flags & HA2_OK) == 0) {
disk_cmderr(up, PROG_FMTCHK);
return 1;
}
/* fall through */
case DVTA: /* Prepare to Verify track addr */
case DVTN: /* Prepare to Verify track no addr */
case DVCY: /* Prepare to Verify Cyl */
switch (cmd_buffer[chan]) {
case DVTA:
sim_debug(DEBUG_CMD, &dsk_dev, "DVTA unit=%d ", u);
break;
case DVTN:
sim_debug(DEBUG_CMD, &dsk_dev, "DVTN unit=%d ", u);
break;
case DVCY:
sim_debug(DEBUG_CMD, &dsk_dev, "DVCY unit=%d ", u);
break;
case DVHA:
sim_debug(DEBUG_CMD, &dsk_dev, "DVHA unit=%d ", u);
break;
}
sim_debug(DEBUG_CMD, &dsk_dev, "trk=%d\n\r", trk);
/* Make sure head on valid cylinder */
if ((trk / disk_type[base->u4].track) > disk_type[base->u4].cyl) {
disk_cmderr(up, PROG_INVADDR);
return 1;
}
/* Make sure we have correct format for this cylinder in buffer */
disk_rblock(up, trk);
/* Start actual operations */
up->u5 |= DSKSTA_SCAN | DSKSTA_CMD;
up->u6 = 0;
chan_set(chan, DEV_SEL);
break;
case DVSR: /* Prepare to Verify single record */
/* Start actual operations */
up->u5 |= DSKSTA_SCAN | DSKSTA_CMD;
up->u6 = 0;
chan_set(chan, DEV_SEL);
break;
}
sim_activate(up, us_to_ticks(50));
return 0;
}
/* Print a format pattern */
void
print_format(UNIT * uptr)
{
struct disk_t *dsk = &disk_type[uptr->u4];
int i, j;
int u = uptr - dsk_unit;
int flag;
int lflag = -1;
sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d (%s) format: ", u, dsk->name);
i = 0;
j = 0;
do {
flag = fbuffer[u][i / 4];
flag >>= (i & 03) * 2;
flag &= 03;
if (lflag != flag) {
if (j != 0) {
switch(lflag) {
case FMT_DATA:
sim_debug(DEBUG_DETAIL, &dsk_dev, "DA(%d) ", j);
break;
case FMT_HDR:
sim_debug(DEBUG_DETAIL, &dsk_dev, "RA(%d) ", j);
break;
case FMT_HA2:
sim_debug(DEBUG_DETAIL, &dsk_dev, "HA2(%d) ", j);
break;
}
}
j = 1;
lflag = flag;
} else {
j++;
}
i++;
} while (flag != FMT_END);
sim_debug(DEBUG_DETAIL, &dsk_dev, "total=%d\n", i);
}
int
disk_rblock(UNIT * uptr, int trk)
{
int u = uptr - dsk_unit;
struct disk_t *dsk = &disk_type[uptr->u4];
UNIT *base = &dsk_unit[(uptr->u3 >> 8) & 0xf];
FILE *f = base->fileref;
int offset = 0;
int fbase = 0;
offset = dsk->cyl * dsk->track * dsk->bpt;
fbase = dsk->fmtsz;
offset *= u / (NUM_DEVS_DSK);
fbase *= u / (NUM_DEVS_DSK);
offset += dsk->fmtsz * dsk->mods * dsk->arms;
if (uptr->u5 & DSKSTA_DIRTY) {
disk_wblock(uptr);
}
if (arm_cyl[u] != fmt_cyl[u]) {
(void)sim_fseek(f, fbase + arm_cyl[u] * dsk->fbpt, SEEK_SET);
(void)sim_fread(fbuffer[u], 1, dsk->fbpt, f);
fmt_cyl[u] = arm_cyl[u];
print_format(uptr);
}
/* Read in actualy track data */
if (dtrack[u] != trk) {
sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d Read track %d\n", u,
trk);
(void)sim_fseek(f, offset + trk * dsk->bpt, SEEK_SET);
if (sim_fread(dbuffer[u], 1, dsk->bpt, f) != dsk->bpt)
memset(dbuffer[u], 0, dsk->bpt);
dtrack[u] = trk;
}
return 1;
}
int
disk_wblock(UNIT * uptr)
{
int u = uptr - dsk_unit;
struct disk_t *dsk = &disk_type[uptr->u4];
UNIT *base = &dsk_unit[(uptr->u3 >> 8) & 0xf];
FILE *f = base->fileref;
int offset = 0;
offset = dsk->cyl * dsk->track * dsk->bpt;
offset *= u / (NUM_DEVS_DSK);
offset += dsk->fmtsz * dsk->mods * dsk->arms;
/* Check if new format data */
if ((uptr->u5 & DSKSTA_CMSK) == DWRF) {
if (uptr->u5 & (DSKSTA_CHECK|DSKSTA_DIRTY)) {
switch (disk_format(uptr, f, arm_cyl[u], base)) {
case 2:
if (uptr->u5 & DSKSTA_CHECK) {
disk_posterr(uptr, PROG_FMTCHK|EXPT_DSKCHK);
break;
}
case 1:
disk_posterr(uptr, PROG_FMTCHK);
break;
case 0:
break;
}
uptr->u5 &= ~(DSKSTA_CHECK);
}
return 1;
}
if (uptr->u5 & DSKSTA_CHECK) {
uptr->u5 &= ~(DSKSTA_CHECK);
if ((uptr->u5 & DSKSTA_DIRTY) == 0)
return 1;
} else if ((uptr->u5 & DSKSTA_DIRTY) == 0)
return 1;
sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d Write track %d\n",
u, dtrack[u]);
/* Write in actualy track data */
(void)sim_fseek(f, offset + dtrack[u] * dsk->bpt, SEEK_SET);
(void)sim_fwrite(dbuffer[u], 1, dsk->bpt, f);
uptr->u5 &= ~DSKSTA_DIRTY;
return 1;
}
/* Convert a format pattern into a format track */
int
disk_format(UNIT * uptr, FILE * f, int cyl, UNIT * base)
{
uint8 tbuffer[MAXTRACK];
struct disk_t *dsk = &disk_type[uptr->u4];
int i, j;
int out = 0;
int u = uptr - dsk_unit;
uint8 ch;
int offset;
f = base->fileref;
offset = dsk->fmtsz;
offset *= u / (NUM_DEVS_DSK);
uptr->u5 &= ~(DSKSTA_DIRTY);
sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d (%s) format: ", u, dsk->name);
/* Scan over new format */
/* Convert format specification in place in dbuffer, then
* pack it into fbuffer and write to file */
/* Skip initial gap */
for (i = 0; i < MAXTRACK && dbuffer[u][i] == 04; i++) ;
if (i == MAXTRACK)
return 2; /* Failed if we hit end */
/* HA1 Gap */
for (j = i; i < MAXTRACK && dbuffer[u][i] == 03; i++) ;
if ((i - j) > 12)
return 1; /* HA1 too big */
if (dbuffer[u][i++] != 04)
return 2; /* Not gap */
for (j = i; i < MAXTRACK && dbuffer[u][i] == 03; i++) ;
if (i == MAXTRACK)
return 2; /* Failed if we hit end */
if (dbuffer[u][i++] != 04)
return 2; /* Not gap */
/* Size up HA2 gap */
for (j = i;
i < MAXTRACK && (dbuffer[u][i] == 03 || dbuffer[u][i] == 01);
i++) ;
j = i - j;
if (j < 6)
return 2;
j -= dsk->overhd; /* Remove overhead */
sim_debug(DEBUG_DETAIL, &dsk_dev, "HA2(%d) ", j);
for (; j > 0; j--)
tbuffer[out++] = FMT_HA2;
/* Now grab records */
while (i < MAXTRACK) {
ch = dbuffer[u][i++];
if (ch == 0x40)
break; /* End of record */
if (ch != 04 && ch != 02)
return 2; /* Not a gap */
for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ;
ch = dbuffer[u][i]; /* Should be RA */
/* Gap not long enough or eor */
if (ch == 0x40 || (i - j) < 11)
break;
/* Size up RA gap */
if (ch != 01 && ch != 03)
return 1; /* Not header */
for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ;
j = i - j;
if (j < 10)
return 2;
j -= dsk->overhd; /* Remove overhead */
sim_debug(DEBUG_DETAIL, &dsk_dev, "RA(%d) ", j);
for (; j > 0; j--)
tbuffer[out++] = FMT_HDR;
ch = dbuffer[u][i++];
if (ch == 0x40)
break; /* End of record */
if (ch != 04 && ch != 02)
return 1; /* End of RA Field */
ch = dbuffer[u][i];
if (ch != 01 && ch != 03)
return 1; /* Not gap */
for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ;
if ((i - j) < 10)
return 2; /* Gap not large enough */
ch = dbuffer[u][i++];
if (ch != 04 && ch != 02)
return 2; /* End of RA Gap */
ch = dbuffer[u][i]; /* Should be DA */
if (ch == 0x40)
break; /* End of record */
if (ch != 01 && ch != 03)
return 1; /* Not Record data */
for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ;
j = i - j;
if (j < 10)
return 2;
j -= dsk->overhd; /* Remove overhead */
sim_debug(DEBUG_DETAIL, &dsk_dev, "DA(%d) ", j);
for (; j > 0; j--)
tbuffer[out++] = FMT_DATA;
}
sim_debug(DEBUG_DETAIL, &dsk_dev, "total=%d\n", out);
/* If checking, don't update */
if (uptr->u5 & DSKSTA_CHECK)
return 0;
/* Put four END chars to end of buffer */
for (j = 4; j > 0; j--)
tbuffer[out++] = FMT_END;
/* Now grab every four characters and place them in next format location */
for (j = i = 0; j < out; i++) {
uint8 temp;
temp = (tbuffer[j++] & 03);
temp |= (tbuffer[j++] & 03) << 2;
temp |= (tbuffer[j++] & 03) << 4;
temp |= (tbuffer[j++] & 03) << 6;
fbuffer[u][i] = temp;
if (i > dsk->fbpt)
break;
}
fbuffer[u][dsk->fbpt-1] = (FMT_END<<6)|(FMT_END<<4)|(FMT_END<<2)|FMT_END;
/* Now write the buffer to the file */
(void)sim_fseek(f, offset + cyl * dsk->fbpt, SEEK_SET);
(void)sim_fwrite(fbuffer[u], 1, dsk->fbpt, f);
/* Make sure we did not pass size of track */
if (out > (int)dsk->bpt)
return 1; /* Too big for track */
return 0;
}
/* Handle writing of one character to disk */
int
disk_write(UNIT * uptr, uint8 data, int chan, int eor)
{
int u = uptr - dsk_unit;
UNIT *base = (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr;
int skip = 1;
int flag = -1;
uint8 cmd;
int schan;
schan = (chan * 2) + ((base->flags & UNIT_SELECT) ? 1 : 0);
cmd = (uptr->u5) & DSKSTA_CMSK;
if (uptr->u6 == 0 && cmd != DWRF) {
int cyl = dtrack[u] / disk_type[base->u4].track;
if (arm_cyl[u] != cyl) {
sim_debug(DEBUG_CMD, &dsk_dev, "cyl not equal %d %d %d\n\r",
u, arm_cyl[u], cyl);
disk_posterr(uptr, PROG_INVADDR);
return -1;
}
/* Verify that the home address matches */
if (cmd != DVHA && cmd != DVSR) {
uint16 t = cmd_option[chan] & 01717;
uint16 ha;
ha = (077 & dbuffer[u][0]) << 6;
ha |= 077 & dbuffer[u][1];
/* Mask out bits we ignore */
ha &= 01717;
/* Convert BCD 0 to Binary 0 */
if ((ha & 01700) == 01200)
ha &= 077;
if ((ha & 017) == 012)
ha &= 07700;
if ((t & 01700) == 01200)
t &= 077;
if ((t & 017) == 012)
t &= 07700;
sim_debug(DEBUG_CMD, &dsk_dev, "HA %04o(c) %04o(d)\n", t, ha);
if (ha != t) {
disk_posterr(uptr, PROG_NOREC);
return -1;
}
} else {
sim_debug(DEBUG_CMD, &dsk_dev, "HA ignored\n");
}
}
while (skip) {
flag = fbuffer[u][uptr->u6 / 4];
flag >>= (uptr->u6 & 03) * 2;
flag &= 03;
switch (uptr->u5 & DSKSTA_CMSK) {
case DWRF: /* Format */
if ((uint32)uptr->u6 > disk_type[uptr->u4].bpt) {
return 1;
}
if (uptr->u5 & DSKSTA_CHECK) {
if ((dbuffer[u][uptr->u6++] & 077) != (data & 077)) {
disk_posterr(uptr, DATA_CHECK);
return -1;
}
} else {
dbuffer[u][uptr->u6++] = data;
uptr->u5 |= DSKSTA_DIRTY;
}
return 0;
case DVTN: /* Verify track no addr */
if (flag == FMT_END) {
return 1;
}
if (flag == FMT_DATA)
skip = 0;
else
uptr->u6++;
break;
case DVTA: /* Verify track addr */
if (flag == FMT_END) {
return 1;
}
if (flag != FMT_HA2)
skip = 0;
else
uptr->u6++;
break;
case DVHA: /* Verify home addr */
if (flag == FMT_END) {
return 1;
}
skip = 0;
break;
case DVCY: /* Verify Cyl */
if (flag == FMT_END) {
/* Move to next track */
int trk = dtrack[u];
int cyl = trk / disk_type[uptr->u4].track;
uptr->u6 = 0;
if (eor)
return 1;
if ((++trk) / disk_type[uptr->u4].track != cyl)
return 1;
disk_rblock(uptr, trk);
} else if (flag != FMT_DATA)
uptr->u6++;
else
skip = 0;
break;
case DVSR: /* Verify single record */
if (flag == FMT_DATA && uptr->u5 & DSKSTA_XFER) {
skip = 0;
} else if (uptr->u5 & DSKSTA_XFER) {
/* Not in a Data record, and we were transfering, all done */
uptr->u5 &= ~DSKSTA_XFER;
uptr->u6 = 0;
return 1;
} else if (flag == FMT_END) {
/* If we hit the end, then no record found */
disk_posterr(uptr, PROG_NOREC);
return -1;
} else if (flag == FMT_HDR) {
uint8 ch;
uint32 match = 0;
int i;
for (i = 0; i < 4 && flag == FMT_HDR; i++) {
ch = dbuffer[u][uptr->u6++];
match <<= 4;
if (ch != 012)
match |= ch & 0xf;
flag = fbuffer[u][uptr->u6 / 4];
flag >>= (uptr->u6 & 03) * 2;
flag &= 03;
}
/* Short header, skip it, but should not have been made */
if (flag != FMT_HDR)
break;
match <<= 16;
/* Grab last two Characters */
while (flag == FMT_HDR) {
ch = dbuffer[u][uptr->u6++];
match = (match & 0xFFFF0000) | ((match & 0x3f) << 6) |
(ch & 077);
flag = fbuffer[u][uptr->u6 / 4];
flag >>= (uptr->u6 & 03) * 2;
flag &= 03;
}
if (flag != FMT_DATA)
break;
/* Compare values. */
if (match != cmd_option[chan])
break;
/* Got a match, exit loop */
uptr->u5 &= ~DSKSTA_SCAN;
uptr->u5 |= DSKSTA_XFER;
skip = 0;
} else {
uptr->u6++;
}
break;
}
}
data &= (sense[schan] & STAT_SIXBIT)?077:0277;
if (uptr->u5 & DSKSTA_CHECK) {
if (dbuffer[u][uptr->u6++] != data) {
fprintf(stderr, "Mismatch %d %03o != %03o\n\r",
uptr->u6-1, dbuffer[u][uptr->u6-1], data);
disk_posterr(uptr, DATA_CHECK);
}
} else {
dbuffer[u][uptr->u6++] = data;
uptr->u5 |= DSKSTA_DIRTY;
}
return 0;
}
/* Handle reading of one character to disk */
int
disk_read(UNIT * uptr, uint8 * data, int chan)
{
int u = uptr - dsk_unit;
UNIT *base = (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr;
int skip = 1;
int flag;
uint8 cmd;
int schan;
schan = (chan * 2) + ((base->flags & UNIT_SELECT) ? 1 : 0);
cmd = (uptr->u5) & DSKSTA_CMSK;
if (uptr->u6 == 0) {
int cyl = dtrack[u] / disk_type[base->u4].track;
if (arm_cyl[u] != cyl) {
disk_posterr(uptr, PROG_INVADDR);
return -1;
}
/* Verify that the home address matches */
if (cmd != DVHA && cmd != DVSR) {
uint16 t = cmd_option[chan] & 01717;
uint16 ha;
ha = (077 & dbuffer[u][0]) << 6;
ha |= 077 & dbuffer[u][1];
/* Mask out bits we ignore */
ha &= 01717;
/* Convert BCD 0 to Binary 0 */
if ((ha & 07700) == 01200)
ha &= 077;
if ((ha & 077) == 012)
ha &= 07700;
if ((t & 07700) == 01200)
t &= 077;
if ((t & 077) == 012)
t &= 07700;
sim_debug(DEBUG_CMD, &dsk_dev, "HA %04o(c) %04o(d)\n", t, ha);
if (ha != t) {
disk_posterr(uptr, PROG_NOREC);
return -1;
}
} else {
sim_debug(DEBUG_CMD, &dsk_dev, "HA ignored\n");
}
}
while (skip) {
flag = fbuffer[u][uptr->u6 / 4];
flag >>= (uptr->u6 & 03) * 2;
flag &= 03;
switch (cmd) {
case DWRF: /* Format */
disk_posterr(uptr, PROG_FMTCHK);
return 1;
case DVTN: /* Verify track no addr */
if (flag == FMT_END) {
uptr->u6 = 0;
return 1;
}
if (flag == FMT_DATA)
skip = 0;
else
uptr->u6++;
break;
case DVTA: /* Verify track addr */
if (flag == FMT_END) {
uptr->u6 = 0;
return 1;
}
if (flag != FMT_HA2)
skip = 0;
else
uptr->u6++;
break;
case DVHA: /* Prepare to Verify home addr */
if (flag == FMT_END) {
uptr->u6 = 0;
return 1;
}
skip = 0;
break;
case DVCY: /* Verify Cyl */
if (flag == FMT_END) {
/* Move to next track */
int trk = dtrack[u];
UNIT *base =
(u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr;
int cyl = trk / disk_type[base->u4].track;
uptr->u6 = 0;
if ((++trk) / disk_type[base->u4].track != cyl)
return 1;
disk_rblock(uptr, trk);
} else if (flag != FMT_DATA)
uptr->u6++;
else
skip = 0;
break;
case DVSR: /* Verify single record */
if (flag == FMT_DATA && uptr->u5 & DSKSTA_XFER) {
skip = 0;
} else if (uptr->u5 & DSKSTA_XFER) {
/* Not in a Data record, and we were transfering, all done */
uptr->u5 &= ~DSKSTA_XFER;
uptr->u6 = 0;
return 1;
} else if (flag == FMT_END) {
/* If we hit the end, then no record found */
disk_posterr(uptr, PROG_NOREC);
return -1;
} else if (flag == FMT_HDR) {
uint8 ch;
uint32 match = 0;
int i;
for (i = 0; i < 4 && flag == FMT_HDR; i++) {
ch = dbuffer[u][uptr->u6++];
match <<= 4;
if (ch != 012)
match |= ch & 0xf;
flag = fbuffer[u][uptr->u6 / 4];
flag >>= (uptr->u6 & 03) * 2;
flag &= 03;
}
/* Short header, skip it, but should not have been made */
if (flag != FMT_HDR)
break;
match <<= 16;
/* Grab last two Characters */
while (flag == FMT_HDR) {
ch = dbuffer[u][uptr->u6++];
match = (match & 0xFFFF0000) | ((match & 0x3f) << 6) |
(ch & 077);
flag = fbuffer[u][uptr->u6 / 4];
flag >>= (uptr->u6 & 03) * 2;
flag &= 03;
}
if (flag != FMT_DATA)
break;
/* Compare values. */
if (match != cmd_option[chan])
break;
/* Got a match, exit loop */
uptr->u5 &= ~DSKSTA_SCAN;
uptr->u5 |= DSKSTA_XFER;
skip = 0;
} else {
uptr->u6++;
}
break;
}
}
*data = dbuffer[u][uptr->u6++] & ((sense[schan] & STAT_SIXBIT)?077:0277);
/* Check if character is last in record */
flag = fbuffer[u][uptr->u6 / 4];
flag >>= (uptr->u6 & 03) * 2;
flag &= 03;
switch (cmd) {
case DVTN: /* Verify track no addr */
case DVTA: /* Verify track addr */
case DVHA: /* Prepare to Verify home addr */
if (flag == FMT_END) {
sim_debug(DEBUG_DATA, &dsk_dev, "eor\n");
return 1;
}
break;
case DVCY: /* Verify Cyl */
if (flag == FMT_END) {
/* Move to next track */
UNIT *base =
(u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr;
if (((dtrack[u]+1) / disk_type[base->u4].track) !=
(dtrack[u] / disk_type[base->u4].track)) {
sim_debug(DEBUG_DATA, &dsk_dev, "eor\n");
return 1;
}
}
break;
case DVSR: /* Verify single record */
if (flag != FMT_DATA) {
sim_debug(DEBUG_DATA, &dsk_dev, "eor\n");
return 1;
}
}
return 0;
}
/* Convert BCD track address to binary address */
int
bcd_to_track(uint32 addr)
{
int trk = 0;
int i;
for (i = 28; i >= 16; i -= 4) {
trk = (trk * 10) + ((addr >> i) & 0xf);
}
return trk;
}
/* Boot disk. Build boot card loader in memory and transfer to it */
t_stat
dsk_boot(int unit_num, DEVICE * dptr)
{
#ifdef I7090
UNIT *uptr = &dptr->units[unit_num];
int chan = UNIT_G_CHAN(uptr->flags) - 1;
int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
int dev = uptr->u3 & 0xff;
int msk = (chan / 2) | ((chan & 1) << 11);
extern uint16 IC;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_UNATT; /* attached? */
if (dev == 0)
dev = 012;
if (uptr->flags & CTSS_BOOT) {
/* Build CTSS boot program in memory */
/* Read first cylinder into B-Core */
M[0] = 0377777000100LL; /* IORT BOTTOM,,-1 */
M[1] = 0006000000001LL; /* TCOA * */
M[2] = 0007400400100LL; /* START TSX ENTER,4 */
M[0100] = 0076000000350LL; /* ENTER RICU */
M[0100] |= (chan + 1) << 9;
M[0101] = 0054000000120LL; /* RSCU READ */
M[0101] |= ((t_uint64) (msk)) << 24;
M[0102] = 0006000000102LL; /* TCOU * */
M[0102] |= ((t_uint64) (chan)) << 24;
M[0103] = 0476100000042LL; /* SEB */
M[0104] = 0450000000000LL; /* CAL 0 */
M[0105] = 0036100477777LL; /* ACL 32767,4 */
M[0106] = 0200001400105LL; /* TIX *-1,4,1 */
M[0107] = 0476100000041LL; /* SEA */
M[0110] = 0032200000131LL; /* ERA CHKSUM */
M[0111] = 0450100000046LL; /* ORA ULOC */
M[0112] = 0010000000132LL; /* TZE EXIT */
M[0113] = 0000000000002LL; /* HTR START */
M[0114] = 0101212001212LL;
M[0114] |= ((t_uint64) (dev)) << 12;
M[0115] = 0121212121212LL;
M[0116] = 0100512001212LL;
M[0116] |= ((t_uint64) (dev)) << 12;
M[0117] = 0121267671212LL;
M[0120] = 0700000000004LL; /* READ SMS 4 */
M[0120] |= sel;
M[0121] = 0200000000114LL; /* CTL SEEK */
M[0122] = 0500000200122LL; /* TCM *,,0 */
M[0123] = 0200000200116LL; /* CTLR CYLOP */
M[0124] = 0400007000125LL; /* CPYP *+1,,N */
IC = 02;
} else {
/* Build IBSYS Boot program in memory */
M[0] = 0000025000101LL; /* IOCD RSCQ,,21 */
M[1] = 0006000000001LL; /* TCOA * */
M[2] = 0002000000101LL; /* TRA RSCQ */
M[0101] = 0054000000115LL; /* RSCQ RSCC SMSQ Mod */
M[0101] |= ((t_uint64) (msk)) << 24;
M[0102] = 0064400000000LL; /* SCDQ SCDC 0 Mod */
M[0102] |= ((t_uint64) (msk)) << 24;
M[0103] = 0044100000000LL; /* LDI 0 */
M[0104] = 0405400007100LL; /* LFT 7100 */
M[0105] = 0002000000110LL; /* TRA *+3 */
M[0106] = 0006000000102LL; /* TCOQ TCOC SCDQ Mod */
M[0106] |= ((t_uint64) (chan)) << 24;
M[0107] = 0002000000003LL; /* TRA 3 Enter IBSYS */
M[0110] = 0076000000350LL; /* RICQ RICC ** Mod */
M[0110] |= (chan + 1) << 9;
M[0111] = 0500512001212LL; /*LDVCY DVCY Mod */
M[0111] |= ((t_uint64) (dev)) << 12;
M[0112] = 0121222440000LL; /* * */
M[0113] = 0501212001212LL; /*LDSEK DSEEK Mod */
M[0113] |= ((t_uint64) (dev)) << 12;
M[0114] = 0121200000000LL; /* * */
M[0115] = 0700000000016LL; /* SMSQ SMS 14 */
M[0115] |= sel;
M[0116] = 0200000000113LL; /* CTL LDSEK */
M[0117] = 0500000200117LL; /* TCM *,,, */
M[0120] = 0200000200111LL; /* CTLR LDVCY */
M[0121] = 0400001000122LL; /* CPYP *+1,,1 */
M[0122] = 0000000000122LL; /* WTR * */
M[0123] = 0100000000121LL; /* TCH *-2 */
M[0124] = 0500000000000LL; /* CPYD 0,,0 */
M[0125] = 0340000000125LL; /* TWT * */
IC = 0101;
}
return SCPE_OK;
#else
return SCPE_NOFNC;
#endif
}
void
dsk_ini(UNIT * uptr, t_bool f)
{
uptr->u5 = 0;
}
t_stat
dsk_reset(DEVICE * dptr)
{
int i;
int t;
for (i = 0; i < NUM_CHAN; i++) {
sense[i * 2] = sense[i * 2 + 1] = STAT_SIXBIT;
sense_unit[i * 2] = sense_unit[i * 2 + 1] = 0;
}
for (i = 0; i < NUM_DEVS_DSK; i++) {
dtrack[i] = 077777;
fmt_cyl[i] = 077777;
arm_cyl[i] = 0;
dtrack[i + NUM_DEVS_DSK] = 077777;
fmt_cyl[i + NUM_DEVS_DSK] = 077777;
arm_cyl[i + NUM_DEVS_DSK] = 0;
dtrack[i + (NUM_DEVS_DSK * 2)] = 077777;
fmt_cyl[i + (NUM_DEVS_DSK * 2)] = 077777;
arm_cyl[i + (NUM_DEVS_DSK * 2)] = 0;
dtrack[i + (NUM_DEVS_DSK * 3)] = 077777;
fmt_cyl[i + (NUM_DEVS_DSK * 3)] = 077777;
arm_cyl[i + (NUM_DEVS_DSK * 3)] = 0;
t = dptr->units[i].u4;
/* Fill in max capacity */
dptr->units[i + NUM_DEVS_DSK].u3 = 0xff;
dptr->units[i + (NUM_DEVS_DSK * 2)].u3 = 0xff;
dptr->units[i + (NUM_DEVS_DSK * 3)].u3 = 0xff;
dptr->units[i].capac = disk_type[t].mods * disk_type[t].bpt *
disk_type[t].arms * disk_type[t].track * disk_type[t].cyl;
if (disk_type[t].arms > 1)
dptr->units[i + NUM_DEVS_DSK].u3 =
0x10 | dptr->units[i].u3 | (i << 8);
if (disk_type[t].mods > 1) {
dptr->units[i + (NUM_DEVS_DSK * 2)].u3 = (i<<8) | (dptr->units[i].u3 + 1);
if (disk_type[t].arms > 1)
dptr->units[i + (NUM_DEVS_DSK * 3)].u3 = (i<<8) | 0x10 |
(dptr->units[i].u3 + 1);
}
}
return SCPE_OK;
}
/* Disk option setting commands */
t_stat
dsk_set_type(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
{
int i, u;
if (cptr == NULL)
return SCPE_ARG;
if (uptr == NULL)
return SCPE_IERR;
if (uptr->flags & UNIT_ATT)
return SCPE_ALATT;
for (i = 0; disk_type[i].name != 0; i++) {
if (strcmp(disk_type[i].name, cptr) == 0) {
uptr->u4 = i;
uptr[NUM_DEVS_DSK].u4 = i;
uptr[NUM_DEVS_DSK].u3 = 0xff;
uptr[NUM_DEVS_DSK * 2].u4 = i;
uptr[NUM_DEVS_DSK * 2].u3 = 0xff;
uptr[NUM_DEVS_DSK * 3].u4 = i;
uptr[NUM_DEVS_DSK * 3].u3 = 0xff;
uptr->capac = disk_type[i].mods * disk_type[i].bpt *
disk_type[i].arms * disk_type[i].track * disk_type[i].cyl;
/* Adjust other disks in case changed number arms/modules */
u = uptr->u3 & 0xf0f;
if (disk_type[uptr->u4].arms > 1)
uptr[NUM_DEVS_DSK].u3 = u | 0x10;
if (disk_type[uptr->u4].mods > 1) {
uptr[NUM_DEVS_DSK * 2].u3 = u + 1;
if (disk_type[uptr->u4].arms > 1)
uptr[NUM_DEVS_DSK * 3].u3 = (u + 1) | 0x10;
}
return SCPE_OK;
}
}
return SCPE_ARG;
}
t_stat
dsk_get_type(FILE * st, UNIT * uptr, int32 v, CONST void *desc)
{
if (uptr == NULL)
return SCPE_IERR;
fputs(disk_type[uptr->u4].name, st);
return SCPE_OK;
}
t_stat
dsk_set_module(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
{
int u;
if (cptr == NULL)
return SCPE_ARG;
if (uptr == NULL)
return SCPE_IERR;
if (uptr->flags & UNIT_ATT)
return SCPE_ALATT;
if (*cptr == '\0' || cptr[1] != '\0')
return SCPE_ARG;
if (*cptr < '0' || *cptr > '8')
return SCPE_ARG;
if (*cptr & 1)
return SCPE_ARG;
u = uptr->u3 & 0xf00;
uptr->u3 = u | (*cptr - '0');
uptr[NUM_DEVS_DSK].u3 = 0xff;
uptr[NUM_DEVS_DSK * 2].u3 = 0xff;
uptr[NUM_DEVS_DSK * 3].u3 = 0xff;
if (disk_type[uptr->u4].arms > 1)
uptr[NUM_DEVS_DSK].u3 = u | 0x10 | (*cptr - '0');
if (disk_type[uptr->u4].mods > 1) {
uptr[NUM_DEVS_DSK * 2].u3 = u | ((*cptr - '0') + 1);
if (disk_type[uptr->u4].arms > 1)
uptr[NUM_DEVS_DSK * 3].u3 = u | 0x10 | ((*cptr - '0') + 1);
}
return SCPE_OK;
}
t_stat
dsk_get_module(FILE * st, UNIT * uptr, int32 v, CONST void *desc)
{
if (uptr == NULL)
return SCPE_IERR;
fprintf(st, "Module=%d", uptr->u3 & 0xff);
return SCPE_OK;
}
t_stat dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr)
{
int i;
fprintf (st, "IBM 7631 Disk File Controller\n\n");
fprintf (st, "The IBM 7631 Disk File Controller supports several types of ");
fprintf (st, "disk drives and\ndrums. The drive must be formatted for use ");
fprintf (st, "of the system. This is handled by\nutilities provided by the ");
fprintf (st, "operating system. This will write a special format\ntrack.\n\n");
fprintf (st, "Use:\n\n");
fprintf (st, " sim> SET DKn TYPE=type\n");
fprintf (st, "Type can be: ");
for (i = 0; disk_type[i].name != 0; i++) {
fprintf(st, "%s", disk_type[i].name);
if (disk_type[i+1].name != 0)
fprintf(st, ", ");
}
fprintf (st, ".\nEach drive has the following storage capacity:\n\n");
for (i = 0; disk_type[i].name != 0; i++) {
int32 size = disk_type[i].mods * disk_type[i].bpt *
disk_type[i].arms * disk_type[i].track * disk_type[i].cyl;
char sm = 'K';
size /= 1024;
if (size > 5000) {
size /= 1024;
sm = 'M';
}
fprintf(st, " %-8s %4d%cB %d modules\n", disk_type[i].name, size, sm,
disk_type[i].mods);
}
fprintf (st, "\nTo enable formating the format switch must be set ");
fprintf (st, "to enable, and the Home\nAddress 2 write must be enabled.\n");
fprintf (st, "To do this:\n\n");
fprintf (st, " sim> SET DKn FORMAT HA2\n\n");
fprintf (st, "To prevent accidental formating of the drive use:\n\n");
fprintf (st, " sim> SET DKn NOFORMAT NOHA2\n\n");
help_set_chan_type(st, dptr, "IBM 7631 Disk File");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
return SCPE_OK;
}
const char *dsk_description (DEVICE *dptr)
{
return "IBM 7631 disk file controller";
}
#endif