blob: 8ecdbd71b3ef0d046f59dc930cb09efa25271057 [file] [log] [blame] [raw]
/* sigma_rad.c: Sigma 7211/7212 or 7231/7232 fixed head disk simulator
Copyright (c) 2007-2008, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
rad 7211/7212 or 7231/7232 fixed head disk
The RAD is a head-per-track disk. To minimize overhead, the entire RAD
is buffered in memory.
Transfers are always done a sector at a time.
*/
#include "sigma_io_defs.h"
#include <math.h>
/* Constants */
#define RAD_7212 0 /* ctlr type */
#define RAD_7232 1
#define RAD_NUMDR 4 /* drives/ctlr */
#define RAD_WDSC 256 /* words/sector */
#define RAD_WDMASK (RAD_WDSC - 1)
#define RAD_SCTK1 82 /* sectors/track */
#define RAD_SCTK3 12
#define RAD_TKUN1 64 /* tracks/unit */
#define RAD_TKUN3 512
#define RAD_WDUNDF (RAD_WDSC*RAD_SCTK1*RAD_TKUN1) /* dflt words/unit */
#define RAD_WDUN (RAD_WDSC*rad_tab[rad_model].sctk*rad_tab[rad_model].tkun)
#define RAD_N_WLK 16 /* num wlk switches */
/* Address bytes */
#define RADA_V_TK1 7 /* track offset */
#define RADA_M_TK1 0xFF
#define RADA_V_SC1 0 /* sector offset */
#define RADA_M_SC1 0x7F
#define RADA_V_TK3 4
#define RADA_M_TK3 0x3FF
#define RADA_V_SC3 0
#define RADA_M_SC3 0xF
#define RADA_GETTK(x) (((x) >> rad_tab[rad_model].tk_v) & rad_tab[rad_model].tk_m)
#define RADA_GETSC(x) (((x) >> rad_tab[rad_model].sc_v) & rad_tab[rad_model].sc_m)
/* Address bad flag */
#define RADA_INV 0x80
/* Status byte 3 is current sector */
/* Status byte 4 (7212 only) is failing sector */
#define RADS_NBY1 4 /* num status bytes */
#define RADS_NBY3 3
/* Device state */
#define RADS_INIT 0x101
#define RADS_END 0x102
#define RADS_WRITE 0x01
#define RADS_READ 0x02
#define RADS_SEEK 0x03
#define RADS_SENSE 0x04
#define RADS_CHECK 0x05
#define RADS_RDEES 0x12
/* Device status */
#define RADV_OVR 0x80 /* overrun - NI */
#define RADV_BADS 0x20 /* bad sector */
#define RADV_WPE 0x10
#define GET_PSC(x) ((int32) fmod (sim_gtime() / ((double) (x * RAD_WDSC)), \
((double) rad_tab[rad_model].sctk)))
/* Model table */
typedef struct {
uint32 tk_v; /* track extract */
uint32 tk_m;
uint32 sc_v; /* sector extract */
uint32 sc_m;
uint32 sctk; /* sectors/track */
uint32 tkun; /* tracks/unit */
uint32 nbys; /* bytes of status */
} rad_t;
static rad_t rad_tab[] = {
{ RADA_V_TK1, RADA_M_TK1, RADA_V_SC1, RADA_M_SC1, RAD_SCTK1, RAD_TKUN1, RADS_NBY1 },
{ RADA_V_TK3, RADA_M_TK3, RADA_V_SC3, RADA_M_SC3, RAD_SCTK3, RAD_TKUN3, RADS_NBY3 }
};
uint32 rad_model = RAD_7212; /* model */
uint32 rad_cmd = 0; /* state */
uint32 rad_flags = 0; /* status flags */
uint32 rad_ad = 0; /* rad address */
uint32 rad_wlk = 0; /* write lock */
uint32 rad_time = 2; /* inter-word time */
extern uint32 chan_ctl_time;
uint32 rad_disp (uint32 op, uint32 dva, uint32 *dvst);
uint32 rad_tio_status (uint32 un);
uint32 rad_tdv_status (uint32 un);
t_stat rad_chan_err (uint32 st);
t_stat rad_svc (UNIT *uptr);
t_stat rad_reset (DEVICE *dptr);
t_stat rad_settype (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat rad_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);
t_bool rad_inv_ad (uint32 *da);
t_bool rad_inc_ad (void);
t_bool rad_end_sec (UNIT *uptr, uint32 lnt, uint32 exp, uint32 st);
/* RAD data structures
rad_dev RAD device descriptor
rad_unit RAD unit descriptor
rad_reg RAD register list
*/
dib_t rad_dib = { DVA_RAD, &rad_disp };
UNIT rad_unit[] = {
{ UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
UNIT_MUSTBUF+UNIT_DISABLE, RAD_WDUNDF) },
{ UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, RAD_WDUNDF) },
{ UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, RAD_WDUNDF) },
{ UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, RAD_WDUNDF) }
};
REG rad_reg[] = {
{ HRDATA (CMD, rad_cmd, 9) },
{ HRDATA (FLAGS, rad_flags, 8) },
{ HRDATA (ADDR, rad_ad, 15) },
{ HRDATA (WLK, rad_wlk, RAD_N_WLK) },
{ DRDATA (TIME, rad_time, 24), PV_LEFT },
{ FLDATA (MODEL, rad_model, 0), REG_HRO },
{ HRDATA (DEVNO, rad_dib.dva, 12), REG_HRO },
{ NULL }
};
MTAB rad_mod[] = {
{ MTAB_XTD | MTAB_VDV, RAD_7212, NULL, "7211",
&rad_settype, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, RAD_7212, NULL, "7212",
&rad_settype, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, RAD_7232, NULL, "7231",
&rad_settype, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, RAD_7232, NULL, "7232",
&rad_settype, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL,
NULL, &rad_showtype, NULL },
{ MTAB_XTD|MTAB_VDV, 0, "CHAN", "CHAN",
&io_set_dvc, &io_show_dvc, NULL },
{ MTAB_XTD|MTAB_VDV, 0, "DVA", "DVA",
&io_set_dva, &io_show_dva, NULL },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "CSTATE", NULL,
NULL, &io_show_cst, NULL },
{ 0 }
};
DEVICE rad_dev = {
"RAD", rad_unit, rad_reg, rad_mod,
RAD_NUMDR, 16, 21, 1, 16, 32,
NULL, NULL, &rad_reset,
&io_boot, NULL, NULL,
&rad_dib, DEV_DISABLE
};
/* RAD: IO dispatch routine */
uint32 rad_disp (uint32 op, uint32 dva, uint32 *dvst)
{
uint32 i;
uint32 un = DVA_GETUNIT (dva);
UNIT *uptr;
if ((un >= RAD_NUMDR) || /* inv unit num? */
(rad_unit[un].flags & UNIT_DIS)) /* disabled unit? */
return DVT_NODEV;
switch (op) { /* case on op */
case OP_SIO: /* start I/O */
*dvst = rad_tio_status (un); /* get status */
if ((*dvst & (DVS_CST|DVS_DST)) == 0) { /* ctrl + dev idle? */
rad_cmd = RADS_INIT; /* start dev thread */
sim_activate (&rad_unit[un], chan_ctl_time);
}
break;
case OP_TIO: /* test status */
*dvst = rad_tio_status (un); /* return status */
break;
case OP_TDV: /* test status */
*dvst = rad_tdv_status (un); /* return status */
break;
case OP_HIO: /* halt I/O */
chan_clr_chi (rad_dib.dva); /* clr int*/
*dvst = rad_tio_status (un); /* get status */
if ((*dvst & DVS_CST) != 0) { /* ctrl busy? */
for (i = 0; i < RAD_NUMDR; i++) { /* find busy unit */
uptr = &rad_unit[i];
if (sim_is_active (uptr)) { /* active? */
sim_cancel (uptr); /* stop */
chan_uen (rad_dib.dva); /* uend */
} /* end if active */
} /* end for */
}
break;
case OP_AIO: /* acknowledge int */
chan_clr_chi (rad_dib.dva); /* clr int */
*dvst = rad_tdv_status (0); /* status like TDV */
break;
default:
*dvst = 0;
return SCPE_IERR;
}
return 0;
}
/* Unit service - this code assumes the entire disk is buffered */
t_stat rad_svc (UNIT *uptr)
{
uint32 i, sc, da, cmd, wd, wd1, c[4], gp;
uint32 *fbuf = (uint32 *) uptr->filebuf;
uint32 st;
int32 t;
switch (rad_cmd) {
case RADS_INIT: /* init state */
st = chan_get_cmd (rad_dib.dva, &cmd); /* get command */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
if ((cmd == 0) || /* invalid cmd? */
((cmd > RADS_CHECK) && (cmd != RADS_RDEES))) {
chan_uen (rad_dib.dva); /* uend */
return SCPE_OK;
}
rad_flags = 0; /* clear status */
rad_cmd = cmd & 0x7; /* next state */
if ((cmd == RADS_SEEK) || (cmd == RADS_SENSE)) /* seek or sense? */
sim_activate (uptr, chan_ctl_time); /* schedule soon */
else { /* data transfer */
sc = RADA_GETSC (rad_ad); /* new sector */
t = sc - GET_PSC (rad_time); /* delta to new */
if (t < 0) /* wrap around? */
t = t + rad_tab[rad_model].sctk;
sim_activate (uptr, t * rad_time * RAD_WDSC); /* schedule op */
}
return SCPE_OK;
case RADS_END: /* end state */
st = chan_end (rad_dib.dva); /* set channel end */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
if (st == CHS_CCH) { /* command chain? */
rad_cmd = RADS_INIT; /* restart thread */
sim_activate (uptr, chan_ctl_time);
}
return SCPE_OK; /* done */
case RADS_SEEK: /* seek */
c[0] = c[1] = 0;
for (i = 0, st = 0; (i < 2) && (st != CHS_ZBC); i++) {
st = chan_RdMemB (rad_dib.dva, &c[i]); /* get byte */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
}
rad_ad = ((c[0] & 0x7F) << 8) | c[1]; /* new address */
if (((i != 2) || (st != CHS_ZBC)) && /* length error? */
chan_set_chf (rad_dib.dva, CHF_LNTE)) /* care? */
return SCPE_OK;
break;
case RADS_SENSE: /* sense */
c[0] = ((rad_ad >> 8) & 0x7F) | (rad_inv_ad (NULL)? RADA_INV: 0);
c[1] = rad_ad & 0xFF; /* address */
c[2] = GET_PSC (rad_time); /* curr sector */
c[3] = 0;
for (i = 0, st = 0; (i < rad_tab[rad_model].nbys) && (st != CHS_ZBC); i++) {
st = chan_WrMemB (rad_dib.dva, c[i]); /* store char */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
}
if (((i != rad_tab[rad_model].nbys) || (st != CHS_ZBC)) &&
chan_set_chf (rad_dib.dva, CHF_LNTE)) /* length error? */
return SCPE_OK;
break;
case RADS_WRITE: /* write */
gp = (RADA_GETSC (rad_ad) * RAD_N_WLK) / /* write lock group */
rad_tab[rad_model].tkun;
if ((rad_wlk >> gp) & 1) { /* write lock set? */
rad_flags |= RADV_WPE; /* set status */
chan_uen (rad_dib.dva); /* uend */
return SCPE_OK;
} /* fall through */
if (rad_inv_ad (&da)) { /* invalid addr? */
chan_uen (rad_dib.dva); /* uend */
return SCPE_OK;
}
for (i = 0, st = 0; i < RAD_WDSC; da++, i++) { /* write */
if (st != CHS_ZBC) { /* chan active? */
st = chan_RdMemW (rad_dib.dva, &wd); /* get data */
if (CHS_IFERR (st)) { /* channel error? */
rad_inc_ad (); /* da increments */
return rad_chan_err (st);
}
}
else wd = 0;
fbuf[da] = wd; /* store in buffer */
if (da >= uptr->hwmark) /* update length */
uptr->hwmark = da + 1;
}
if (rad_end_sec (uptr, i, RAD_WDSC, st)) /* transfer done? */
return SCPE_OK;
break;
/* Must be done by bytes to get precise miscompare */
case RADS_CHECK: /* write check */
if (rad_inv_ad (&da)) { /* invalid addr? */
chan_uen (rad_dib.dva); /* uend */
return SCPE_OK;
}
for (i = 0, st = 0; (i < (RAD_WDSC * 4)) && (st != CHS_ZBC); ) {
st = chan_RdMemB (rad_dib.dva, &wd); /* read sector */
if (CHS_IFERR (st)) { /* channel error? */
rad_inc_ad (); /* da increments */
return rad_chan_err (st);
}
wd1 = (fbuf[da] >> (24 - ((i % 4) * 8))) & 0xFF; /* byte */
if (wd != wd1) { /* check error? */
rad_inc_ad (); /* da increments */
chan_set_chf (rad_dib.dva, CHF_XMDE); /* set xmt err flag */
chan_uen (rad_dib.dva); /* force uend */
return SCPE_OK;
}
da = da + ((++i % 4) == 0); /* every 4th byte */
}
if (rad_end_sec (uptr, i, RAD_WDSC * 4, st)) /* transfer done? */
return SCPE_OK;
break;
case RADS_READ: /* read */
if (rad_inv_ad (&da)) { /* invalid addr? */
chan_uen (rad_dib.dva); /* uend */
return SCPE_OK;
}
for (i = 0, st = 0; (i < RAD_WDSC) && (st != CHS_ZBC); da++, i++) {
st = chan_WrMemW (rad_dib.dva, fbuf[da]); /* store in mem */
if (CHS_IFERR (st)) { /* channel error? */
rad_inc_ad (); /* da increments */
return rad_chan_err (st);
}
}
if (rad_end_sec (uptr, i, RAD_WDSC, st)) /* transfer done? */
return SCPE_OK;
break;
}
rad_cmd = RADS_END; /* op done, next state */
sim_activate (uptr, chan_ctl_time);
return SCPE_OK;
}
/* Common read/write sector end routine
case 1 - more to transfer, not end disk - reschedule, return TRUE
case 2 - more to transfer, end disk - uend, return TRUE
case 3 - transfer done, length error - uend, return TRUE
case 4 - transfer done, no length error - return FALSE (sched end state)
*/
t_bool rad_end_sec (UNIT *uptr, uint32 lnt, uint32 exp, uint32 st)
{
if (st != CHS_ZBC) { /* end record? */
if (rad_inc_ad ()) /* inc addr, ovf? */
chan_uen (rad_dib.dva); /* uend */
else sim_activate (uptr, rad_time * 16); /* no, next sector */
return TRUE;
}
rad_inc_ad (); /* just incr addr */
if ((lnt != exp) && /* length error? */
chan_set_chf (rad_dib.dva, CHF_LNTE)) /* do we care? */
return TRUE;
return FALSE; /* cmd done */
}
/* RAD status routine */
uint32 rad_tio_status (uint32 un)
{
uint32 i, st;
st = DVS_AUTO; /* flags */
if (sim_is_active (&rad_unit[un])) /* active => busy */
st |= DVS_DBUSY;
else if ((rad_unit[un].flags & UNIT_ATT) == 0) /* not att => offl */
st |= DVS_DOFFL;
for (i = 0; i < RAD_NUMDR; i++) { /* loop thru units */
if (sim_is_active (&rad_unit[i])) { /* active? */
st |= (DVS_CBUSY |(CC2 << DVT_V_CC)); /* ctrl is busy */
return st;
}
}
return st;
}
uint32 rad_tdv_status (uint32 un)
{
uint32 st;
st = rad_flags;
if (rad_inv_ad (NULL)) /* bad address? */
st |= RADV_BADS;
return st;
}
/* Validate disk address */
t_bool rad_inv_ad (uint32 *da)
{
uint32 tk = RADA_GETTK (rad_ad);
uint32 sc = RADA_GETSC (rad_ad);
if ((tk >= rad_tab[rad_model].tkun) || /* bad sec or trk? */
(sc >= rad_tab[rad_model].sctk)) {
return TRUE;
}
if (da) /* return word addr */
*da = ((tk * rad_tab[rad_model].sctk) + sc) * RAD_WDSC;
return FALSE;
}
/* Increment disk address */
t_bool rad_inc_ad (void)
{
uint32 tk = RADA_GETTK (rad_ad);
uint32 sc = RADA_GETSC (rad_ad);
sc = sc + 1; /* sector++ */
if (sc >= rad_tab[rad_model].sctk) { /* overflow? */
sc = 0; /* wrap sector */
tk = tk + 1; /* track++ */
}
rad_ad = ((tk << rad_tab[rad_model].tk_v) | /* rebuild rad_ad */
(sc << rad_tab[rad_model].sc_v));
if (tk >= rad_tab[rad_model].tkun) /* overflow? */
return TRUE;
return FALSE;
}
/* Channel error */
t_stat rad_chan_err (uint32 st)
{
chan_uen (rad_dib.dva); /* uend */
if (st < CHS_ERR)
return st;
return SCPE_OK;
}
/* Reset routine */
t_stat rad_reset (DEVICE *dptr)
{
uint32 i;
for (i = 0; i < RAD_NUMDR; i++)
sim_cancel (&rad_unit[i]); /* stop dev thread */
rad_cmd = 0;
rad_flags = 0;
rad_ad = 0;
chan_reset_dev (rad_dib.dva); /* clr int, active */
return SCPE_OK;
}
/* Set controller type */
t_stat rad_settype (UNIT *uptr, int32 val, char *cptr, void *desc)
{
uint32 i;
for (i = 0; i < RAD_NUMDR; i++) { /* all units unatt? */
if (rad_unit[i].flags & UNIT_ATT)
return SCPE_ALATT;
}
rad_model = val; /* update model */
rad_reset (&rad_dev); /* reset */
for (i = 0; i < RAD_NUMDR; i++) /* update capacity */
rad_unit[i].capac = RAD_WDUN;
return SCPE_OK;
}
/* Show controller type */
t_stat rad_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)
{
fprintf (st, (rad_model == RAD_7212)? "7211/7212": "7231/7232");
return SCPE_OK;
}