/* 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 |