sigma: Fixed bugs in multi-unit operation

The problems all stem from a single root cause: when an interrupt is
requested - via any one of dozens of mechanisms (uend, normal end,
special request, error, etc), the interrupt routine must be presented
with the full device address, including the unit, which is stored in the
channel data structures. This is so that AIO (acknowledge interrupt)
can retrieve the unit number from the channel data structures and
give it back to the invoking program.

Unfortunately, service routines, error routines, etc were simply using
the base device address, which is good enough for all channel operations
EXCEPT AIO. So the four multi-unit device (RAD, DK, DP, MT) have had
changes to reconstruct the full device address, with unit number, before
any calls into the channel are made.

DP, funnily enough, required the fewest changes. Ken was exactly right
about the need to OR the unit number into dva at the start of the unit
service routine. That had to be done in a few more places where my code
was being lazy.

RAD, DK, MT required many more changes, but they were mostly mechanical.
All references to xx_dib.dva had to be examined and replaced with a full
device address. The full device address had to be reconstructed from the
UNIT pointer and the base device address, through the magic of pointer
arithmetic.

Another sore spot was HIO. HIO stops all the active unit in its tracks
and does a channel UEND on it. The calculation of the unit number in the
UEND was not correct in a few places, notably if DP did a controller reset.

Fixes for the four modules; an updated bug history; and an expanded
design document; are attached.
This commit is contained in:
Bob Supnik 2022-07-03 13:56:46 -07:00 committed by Mark Pizzolato
parent bef5b821f0
commit 9258e25c0d
6 changed files with 161 additions and 106 deletions

View file

@ -114,6 +114,10 @@
108. DP: TIO status routine always returns DVS_AUTO instead of proper status.
109. DP: DP_SEEK definition is off by 1.
110. DP: reset does not properly initialize controller, seek threads.
111. DP: device address must include unit identifier everywhere, for interrupt generation.
112. DK: device address must include unit identifier everywhere, for interrupt generation.
113. MT: device address must include unit identifier everywhere, for interrupt generation.
114: RAD: device address must include unit identifier everywhere, for interrupt generation.
Diagnostic Notes

View file

@ -1,6 +1,6 @@
/* sigma_dk.c: 7250/7251-7252 cartridge disk simulator
Copyright (c) 2007-2008, Robert M Supnik
Copyright (c) 2007-2022, 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"),
@ -25,6 +25,8 @@
dk 7250/7251-7252 cartridge disk
02-Jul-2022 RMS Fixed bugs in multi-unit operation
Transfers are always done a sector at a time.
*/
@ -88,7 +90,7 @@ extern uint32 chan_ctl_time;
uint32 dk_disp (uint32 op, uint32 dva, uint32 *dvst);
uint32 dk_tio_status (uint32 un);
uint32 dk_tdv_status (uint32 un);
t_stat dk_chan_err (uint32 st);
t_stat dk_chan_err (uint32 dva, uint32 st);
t_stat dk_svc (UNIT *uptr);
t_stat dk_reset (DEVICE *dptr);
t_bool dk_inv_ad (uint32 *da);
@ -148,12 +150,17 @@ DEVICE dk_dev = {
&dk_dib, DEV_DISABLE
};
/* DK: IO dispatch routine */
/* DK: IO dispatch routine
For all calls except AIO, dva is the full channel/device/unit address
For AIO, the handler must return the unit number
*/
uint32 dk_disp (uint32 op, uint32 dva, uint32 *dvst)
{
uint32 i;
uint32 un = DVA_GETUNIT (dva);
int32 iu;
UNIT *uptr;
if ((un >= DK_NUMDR) || /* inv unit num? */
@ -178,22 +185,27 @@ switch (op) { /* case on op */
break;
case OP_HIO: /* halt I/O */
chan_clr_chi (dk_dib.dva); /* clr int */
chan_clr_chi (dva); /* clr int */
*dvst = dk_tio_status (un); /* get status */
if ((*dvst & DVS_CST) != 0) { /* ctrl busy? */
for (i = 0; i < DK_NUMDR; i++) { /* find busy unit */
uptr = &dk_unit[i];
if (sim_is_active (uptr)) { /* active? */
sim_cancel (uptr); /* stop */
chan_uen (dk_dib.dva); /* uend */
chan_uen (dk_dib.dva | i); /* uend on unit */
} /* end if active */
} /* end for */
}
break;
case OP_AIO: /* acknowledge int */
chan_clr_chi (dk_dib.dva); /* clr int */
*dvst = dk_tdv_status (un); /* status like TDV */
iu = chan_clr_chi (dk_dib.dva); /* clr int */
if (iu < 0) { /* no int? */
*dvst = 0;
return SCPE_IERR;
}
*dvst = dk_tdv_status (iu) | /* get status */
(iu << DVT_V_UN); /* or in unit */
break;
default:
@ -210,18 +222,20 @@ t_stat dk_svc (UNIT *uptr)
{
uint32 i, sc, da, wd, wd1, cmd, c[3];
uint32 *fbuf = (uint32 *) uptr->filebuf;
uint32 un = uptr - dk_unit;
uint32 dva = dk_dib.dva | un;
int32 t, dc;
uint32 st;
switch (dk_cmd) {
case DKS_INIT: /* init state */
st = chan_get_cmd (dk_dib.dva, &cmd); /* get command */
st = chan_get_cmd (dva, &cmd); /* get command */
if (CHS_IFERR (st)) /* channel error? */
return dk_chan_err (st);
return dk_chan_err (dva,st);
if ((cmd == 0) || /* invalid cmd? */
((cmd > DKS_CHECK) && (cmd != DKS_RDEES) && (cmd != DKS_TEST))) {
chan_uen (dk_dib.dva); /* uend */
chan_uen (dva); /* uend */
return SCPE_OK;
}
dk_flags = 0; /* clear status */
@ -240,9 +254,9 @@ switch (dk_cmd) {
return SCPE_OK;
case DKS_END: /* end state */
st = chan_end (dk_dib.dva); /* set channel end */
st = chan_end (dva); /* set channel end */
if (CHS_IFERR (st)) /* channel error? */
return dk_chan_err (st);
return dk_chan_err (dva,st);
if (st == CHS_CCH) { /* command chain? */
dk_cmd = DKS_INIT; /* restart thread */
sim_activate (uptr, chan_ctl_time);
@ -252,13 +266,13 @@ switch (dk_cmd) {
case DKS_SEEK: /* seek */
c[0] = c[1] = 0;
for (i = 0, st = 0; (i < 2) && (st != CHS_ZBC); i++) {
st = chan_RdMemB (dk_dib.dva, &c[i]); /* get byte */
st = chan_RdMemB (dva, &c[i]); /* get byte */
if (CHS_IFERR (st)) /* channel error? */
return dk_chan_err (st);
return dk_chan_err (dva,st);
}
dk_ad = ((c[0] & 0x7F) << 8) | c[1]; /* new address */
if (((i != 2) || (st != CHS_ZBC)) && /* length error? */
chan_set_chf (dk_dib.dva, CHF_LNTE)) /* care? */
chan_set_chf (dva, CHF_LNTE)) /* care? */
return SCPE_OK;
dc = DKA_GETTK (dk_ad); /* desired track */
t = abs (uptr->UTRK - dc); /* get track diff */
@ -272,7 +286,7 @@ switch (dk_cmd) {
case DKS_SEEK2: /* seek complete */
if (uptr->UTRK >= DK_TKUN) {
dk_flags |= DKV_BADS;
chan_uen (dk_dib.dva);
chan_uen (dva);
return SCPE_OK;
}
break; /* seek done */
@ -282,31 +296,31 @@ switch (dk_cmd) {
c[1] = dk_ad & 0xFF; /* address */
c[2] = GET_PSC (dk_time); /* curr sector */
for (i = 0, st = 0; (i < DKS_NBY) && (st != CHS_ZBC); i++) {
st = chan_WrMemB (dk_dib.dva, c[i]); /* store char */
st = chan_WrMemB (dva, c[i]); /* store char */
if (CHS_IFERR (st)) /* channel error? */
return dk_chan_err (st);
return dk_chan_err (dva,st);
}
if (((i != DKS_NBY) || (st != CHS_ZBC)) &&
chan_set_chf (dk_dib.dva, CHF_LNTE)) /* length error? */
chan_set_chf (dva, CHF_LNTE)) /* length error? */
return SCPE_OK;
break;
case DKS_WRITE: /* write */
if (uptr->flags & UNIT_RO) { /* write locked? */
dk_flags |= DKV_WPE; /* set status */
chan_uen (dk_dib.dva); /* uend */
chan_uen (dva); /* uend */
return SCPE_OK;
}
if (dk_inv_ad (&da)) { /* invalid addr? */
chan_uen (dk_dib.dva); /* uend */
chan_uen (dva); /* uend */
return SCPE_OK;
}
for (i = 0, st = 0; i < DK_WDSC; da++, i++) { /* sector loop */
if (st != CHS_ZBC) { /* chan not done? */
st = chan_RdMemW (dk_dib.dva, &wd); /* read word */
st = chan_RdMemW (dva, &wd); /* read word */
if (CHS_IFERR (st)) { /* channel error? */
dk_inc_ad (); /* da increments */
return dk_chan_err (st);
return dk_chan_err (dva,st);
}
}
else wd = 0;
@ -322,20 +336,20 @@ switch (dk_cmd) {
case DKS_CHECK: /* write check */
if (dk_inv_ad (&da)) { /* invalid addr? */
chan_uen (dk_dib.dva); /* uend */
chan_uen (dva); /* uend */
return SCPE_OK;
}
for (i = 0, st = 0; (i < (DK_WDSC * 4)) && (st != CHS_ZBC); ) {
st = chan_RdMemB (dk_dib.dva, &wd); /* read byte */
st = chan_RdMemB (dva, &wd); /* read byte */
if (CHS_IFERR (st)) { /* channel error? */
dk_inc_ad (); /* da increments */
return dk_chan_err (st);
return dk_chan_err (dva,st);
}
wd1 = (fbuf[da] >> (24 - ((i % 4) * 8))) & 0xFF; /* byte */
if (wd != wd1) { /* check error? */
dk_inc_ad (); /* da increments */
chan_set_chf (dk_dib.dva, CHF_XMDE); /* set xmt err flag */
chan_uen (dk_dib.dva); /* force uend */
chan_set_chf (dva, CHF_XMDE); /* set xmt err flag */
chan_uen (dva); /* force uend */
return SCPE_OK;
}
da = da + ((++i % 4) == 0); /* every 4th byte */
@ -346,14 +360,14 @@ switch (dk_cmd) {
case DKS_READ: /* read */
if (dk_inv_ad (&da)) { /* invalid addr? */
chan_uen (dk_dib.dva); /* uend */
chan_uen (dva); /* uend */
return SCPE_OK;
}
for (i = 0, st = 0; (i < DK_WDSC) && (st != CHS_ZBC); da++, i++) {
st = chan_WrMemW (dk_dib.dva, fbuf[da]); /* store in mem */
st = chan_WrMemW (dva, fbuf[da]); /* store in mem */
if (CHS_IFERR (st)) { /* channel error? */
dk_inc_ad (); /* da increments */
return dk_chan_err (st);
return dk_chan_err (dva,st);
}
}
if (dk_end_sec (uptr, i, DK_WDSC, st)) /* transfer done? */
@ -376,15 +390,18 @@ return SCPE_OK;
t_bool dk_end_sec (UNIT *uptr, uint32 lnt, uint32 exp, uint32 st)
{
uint32 un = uptr - dk_unit;
uint32 dva = dk_dib.dva | un;
if (st != CHS_ZBC) { /* end record? */
if (dk_inc_ad ()) /* inc addr, ovf? */
chan_uen (dk_dib.dva); /* uend */
chan_uen (dva); /* uend */
else sim_activate (uptr, dk_time * 16); /* no, next sector */
return TRUE;
}
dk_inc_ad (); /* just incr addr */
if ((lnt != exp) && /* length error? */
chan_set_chf (dk_dib.dva, CHF_LNTE)) /* do we care? */
chan_set_chf (dva, CHF_LNTE)) /* do we care? */
return TRUE;
return FALSE; /* cmd done */
}
@ -443,9 +460,9 @@ return FALSE;
/* Channel error */
t_stat dk_chan_err (uint32 st)
t_stat dk_chan_err (uint32 dva, uint32 st)
{
chan_uen (dk_dib.dva); /* uend */
chan_uen (dva); /* uend */
if (st < CHS_ERR)
return st;
return SCPE_OK;

View file

@ -25,6 +25,7 @@
dp moving head disk pack controller
02-Jul-2022 RMS Fixed bugs in multi-unit operation
28-Jun-22 RMS Fixed off-by-1 error in DP_SEEK definition (Ken Rector)
07-Jun-22 RMS Removed unused variables (V4)
06-Jun-22 RMS Fixed incorrect return in TIO status (Ken Rector)
@ -548,7 +549,11 @@ DEVICE dp_dev[] = {
}
};
/* DP: IO dispatch routine */
/* DP: IO dispatch routine
For all calls except AIO, dva is the full channel/device/unit address
For AIO, the handler must return the unit number
*/
uint32 dpa_disp (uint32 op, uint32 dva, uint32 *dvst)
{
@ -612,7 +617,7 @@ switch (op) { /* case on op */
for (i = 0; i < DP_NUMDR; i++) { /* do every unit */
if (sim_is_active (&dp_unit[i])) { /* chan active? */
sim_cancel (&dp_unit[i]); /* cancel */
chan_uen (dva); /* uend */
chan_uen ((dva & ~DVA_M_UNIT) | i); /* uend */
}
dp_clr_ski (cidx, i); /* clear seek int */
sim_cancel (&dp_unit[i + DP_SEEK]); /* cancel seek compl */
@ -635,16 +640,16 @@ switch (op) { /* case on op */
return 0;
}
/* Unit service */
/* Unit service - reconstruct full device address on entry */
t_stat dp_svc (UNIT *uptr)
{
uint32 i, da, wd, wd1, c[DPS_NBY_16B];
uint32 cidx = uptr->UCTX;
uint32 dva = dp_dib[cidx].dva;
uint32 dtype = GET_DTYPE (uptr->flags);
UNIT *dp_unit = dp_dev[cidx].units;
uint32 un = uptr - dp_unit;
uint32 dva = dp_dib[cidx].dva | un;
uint32 dtype = GET_DTYPE (uptr->flags);
DP_CTX *ctx = &dp_ctx[cidx];
int32 t, dc;
uint32 st, cmd, sc;
@ -932,7 +937,8 @@ return SCPE_OK;
t_bool dp_end_sec (UNIT *uptr, uint32 lnt, uint32 exp, uint32 st)
{
uint32 cidx = uptr->UCTX;
uint32 dva = dp_dib[cidx].dva;
uint32 un = uptr - dp_dev[cidx].units;
uint32 dva = dp_dib[cidx].dva | un;
DP_CTX *ctx = &dp_ctx[cidx];
if (st != CHS_ZBC) { /* end record? */
@ -1112,7 +1118,8 @@ return SCPE_OK;
t_stat dp_ioerr (UNIT *uptr)
{
uint32 cidx = uptr->UCTX;
uint32 dva = dp_dib[cidx].dva;
uint32 un = uptr - dp_dev[cidx].units;
uint32 dva = dp_dib[cidx].dva | un;
perror ("DP I/O error");
clearerr (uptr->fileref);

View file

@ -25,6 +25,8 @@
mt 7320 and 7322/7323 magnetic tape
02-Jul-2022 RMS Fixed bugs in multi-unit operation
07-Jun-22 RMS Removed unused variables (V4)
26-Mar-22 RMS Added extra case points for new MTSE definitions
13-Mar-17 RMS Annotated fall through in switch
@ -129,7 +131,7 @@ extern uint8 ebcdic_to_ascii[256];
uint32 mt_disp (uint32 op, uint32 dva, uint32 *dvst);
uint32 mt_tio_status (uint32 un);
uint32 mt_tdv_status (uint32 un);
t_stat mt_chan_err (uint32 st);
t_stat mt_chan_err (uint32 dva, uint32 st);
t_stat mtu_svc (UNIT *uptr);
t_stat mtr_svc (UNIT *uptr);
t_stat mt_reset (DEVICE *dptr);
@ -215,7 +217,11 @@ DEVICE mt_dev = {
&mt_dib, DEV_DISABLE | DEV_TAPE
};
/* Magtape: IO dispatch routine */
/* Magtape: IO dispatch routine
For all calls except AIO, dva is the full channel/device/unit address
For AIO, the handler must return the unit number
*/
uint32 mt_disp (uint32 op, uint32 dva, uint32 *dvst)
{
@ -256,7 +262,7 @@ switch (op) { /* case on op */
break;
case OP_AIO: /* acknowledge int */
un = mt_clr_int (mt_dib.dva); /* clr int, get unit */
un = mt_clr_int (mt_dib.dva); /* clr int, get unit and flag */
*dvst = (mt_tdv_status (un) & MTAI_MASK) | /* device status */
(un & MTAI_INT) | /* device int flag */
((un & DVA_M_UNIT) << DVT_V_UN); /* unit number */
@ -270,12 +276,13 @@ switch (op) { /* case on op */
return 0;
}
/* Unit service */
/* Unit service - reconstruct full unit address at entry */
t_stat mtu_svc (UNIT *uptr)
{
uint32 cmd = uptr->UCMD;
uint32 un = uptr - mt_unit;
uint32 dva = mt_dib.dva | un;
uint32 c;
uint32 st;
int32 t;
@ -287,9 +294,9 @@ if (cmd == MCM_INIT) { /* init state */
sim_activate (uptr, t); /* retry later */
return SCPE_OK;
}
st = chan_get_cmd (mt_dib.dva, &cmd); /* get command */
st = chan_get_cmd (dva, &cmd); /* get command */
if (CHS_IFERR (st)) /* channel error? */
return mt_chan_err (st);
return mt_chan_err (dva, st);
if ((cmd & 0x80) || /* invalid cmd? */
(mt_op[cmd] == 0)) {
uptr->UCMD = MCM_END; /* end state */
@ -299,7 +306,7 @@ if (cmd == MCM_INIT) { /* init state */
else { /* valid cmd */
if ((mt_op[cmd] & O_REV) && /* reverse op */
(mt_unit[un].UST & MTDV_BOT)) { /* at load point? */
chan_uen (mt_dib.dva); /* channel end */
chan_uen (dva); /* uend */
return SCPE_OK;
}
uptr->UCMD = cmd; /* unit state */
@ -312,9 +319,9 @@ if (cmd == MCM_INIT) { /* init state */
}
if (cmd == MCM_END) { /* end state */
st = chan_end (mt_dib.dva); /* set channel end */
st = chan_end (dva); /* set channel end */
if (CHS_IFERR (st)) /* channel error? */
return mt_chan_err (st);
return mt_chan_err (dva, st);
if (st == CHS_CCH) { /* command chain? */
uptr->UCMD = MCM_INIT; /* restart thread */
sim_activate (uptr, chan_ctl_time);
@ -331,7 +338,7 @@ if ((mt_op[cmd] & O_ATT) && /* op req att and */
if ((mt_op[cmd] & O_WRE) && /* write op and */
sim_tape_wrp (uptr)) { /* write protected? */
uptr->UST |= MTDV_WLE; /* set status */
chan_uen (mt_dib.dva); /* unusual end */
chan_uen (dva); /* uend */
return SCPE_OK;
}
@ -390,15 +397,15 @@ switch (cmd) { /* case on command */
mt_bptr = 0; /* init rec ptr */
}
c = mt_xb[mt_bptr++]; /* get char */
st = chan_WrMemB (mt_dib.dva, c); /* write to memory */
st = chan_WrMemB (dva, c); /* write to memory */
if (CHS_IFERR (st)) /* channel error? */
return mt_chan_err (st);
return mt_chan_err (dva, st);
if ((st != CHS_ZBC) && (mt_bptr != mt_blim)) { /* not done? */
sim_activate (uptr, mt_time); /* continue thread */
return SCPE_OK;
}
if (((st == CHS_ZBC) ^ (mt_bptr == mt_blim)) && /* length err? */
chan_set_chf (mt_dib.dva, CHF_LNTE)) /* uend taken? */
chan_set_chf (dva, CHF_LNTE)) /* uend taken? */
return SCPE_OK; /* finished */
break; /* normal end */
@ -412,23 +419,23 @@ switch (cmd) { /* case on command */
mt_bptr = mt_blim; /* init rec ptr */
}
c = mt_xb[--mt_bptr]; /* get char */
st = chan_WrMemBR (mt_dib.dva, c); /* write mem rev */
st = chan_WrMemBR (dva, c); /* write mem rev */
if (CHS_IFERR (st)) /* channel error? */
return mt_chan_err (st);
return mt_chan_err (dva, st);
if ((st != CHS_ZBC) && (mt_bptr != 0)) { /* not done? */
sim_activate (uptr, mt_time); /* continue thread */
return SCPE_OK;
}
if (((st == CHS_ZBC) ^ (mt_bptr == 0)) && /* length err? */
chan_set_chf (mt_dib.dva, CHF_LNTE)) /* uend taken? */
chan_set_chf (dva, CHF_LNTE)) /* uend taken? */
return SCPE_OK; /* finished */
break; /* normal end */
case MCM_WRITE: /* write */
st = chan_RdMemB (mt_dib.dva, &c); /* read char */
st = chan_RdMemB (dva, &c); /* read char */
if (CHS_IFERR (st)) { /* channel error? */
mt_flush_buf (uptr); /* flush buffer */
return mt_chan_err (st);
return mt_chan_err (dva, st);
}
mt_xb[mt_blim++] = c; /* store in buffer */
if (st != CHS_ZBC) { /* end record? */
@ -473,42 +480,45 @@ return SCPE_OK;
t_stat mt_map_err (UNIT *uptr, t_stat st)
{
uint32 un = uptr - mt_unit;
uint32 dva = mt_dib.dva | un;
switch (st) {
case MTSE_FMT: /* illegal fmt */
case MTSE_UNATT: /* not attached */
case MTSE_WRP: /* write protect */
default: /* unknown error*/
chan_set_chf (mt_dib.dva, CHF_XMME); /* set err, fall through */
chan_set_chf (dva, CHF_XMME); /* set err, fall through */
case MTSE_OK: /* no error */
chan_uen (mt_dib.dva); /* uend */
chan_uen (dva); /* uend */
return SCPE_IERR;
case MTSE_TMK: /* end of file */
uptr->UST |= MTDV_EOF; /* set eof flag */
chan_uen (mt_dib.dva); /* uend */
chan_uen (dva); /* uend */
return CHS_INACTV;
case MTSE_IOERR: /* IO error */
uptr->UST |= MTDV_DTE; /* set DTE flag */
chan_set_chf (mt_dib.dva, CHF_XMDE);
chan_uen (mt_dib.dva); /* force uend */
chan_set_chf (dva, CHF_XMDE);
chan_uen (dva); /* uend */
return SCPE_IOERR;
case MTSE_INVRL: /* invalid rec lnt */
uptr->UST |= MTDV_DTE; /* set DTE flag */
chan_set_chf (mt_dib.dva, CHF_XMDE);
chan_uen (mt_dib.dva); /* force uend */
chan_set_chf (dva, CHF_XMDE);
chan_uen (dva); /* uend */
return SCPE_MTRLNT;
case MTSE_RECE: /* record in error */
case MTSE_EOM: /* end of medium */
uptr->UST |= MTDV_DTE; /* set DTE flag */
return chan_set_chf (mt_dib.dva, CHF_XMDE); /* possible error */
return chan_set_chf (dva, CHF_XMDE); /* possible error */
case MTSE_BOT: /* reverse into BOT */
uptr->UST |= MTDV_BOT; /* set BOT */
chan_uen (mt_dib.dva); /* uend */
chan_uen (dva); /* uend */
return CHS_INACTV;
} /* end switch */
@ -555,9 +565,9 @@ return st;
/* Channel error */
t_stat mt_chan_err (uint32 st)
t_stat mt_chan_err (uint32 dva, uint32 st)
{
chan_uen (mt_dib.dva); /* uend */
chan_uen (dva); /* uend */
if (st < CHS_ERR)
return st;
return SCPE_OK;

View file

@ -1,6 +1,6 @@
/* sigma_rad.c: Sigma 7211/7212 or 7231/7232 fixed head disk simulator
Copyright (c) 2007-2008, Robert M Supnik
Copyright (c) 2007-2022, 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"),
@ -25,6 +25,8 @@
rad 7211/7212 or 7231/7232 fixed head disk
02-Jul-2022 RMS Fixed bugs in multi-unit operation
The RAD is a head-per-track disk. To minimize overhead, the entire RAD
is buffered in memory.
@ -121,7 +123,7 @@ 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_chan_err (uint32 dva, uint32 st);
t_stat rad_svc (UNIT *uptr);
t_stat rad_reset (DEVICE *dptr);
t_stat rad_settype (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
@ -189,12 +191,17 @@ DEVICE rad_dev = {
&rad_dib, DEV_DISABLE
};
/* RAD: IO dispatch routine */
/* RAD: IO dispatch routine
For all calls except AIO, dva is the full channel/device/unit address
For AIO, the handler must return the unit number
*/
uint32 rad_disp (uint32 op, uint32 dva, uint32 *dvst)
{
uint32 i;
uint32 un = DVA_GETUNIT (dva);
int32 iu;
UNIT *uptr;
if ((un >= RAD_NUMDR) || /* inv unit num? */
@ -219,22 +226,27 @@ switch (op) { /* case on op */
break;
case OP_HIO: /* halt I/O */
chan_clr_chi (rad_dib.dva); /* clr int*/
chan_clr_chi (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 */
chan_uen (rad_dib.dva | i); /* uend on drive */
} /* 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 */
iu = chan_clr_chi (rad_dib.dva); /* clr int */
if (iu < 0) { /* no int? */
*dvst = 0;
return SCPE_IERR;
}
*dvst = rad_tdv_status (iu) | /* status like TDV */
(iu << DVT_V_UN); /* or in unit */
break;
default:
@ -251,18 +263,20 @@ t_stat rad_svc (UNIT *uptr)
{
uint32 i, sc, da, cmd, wd, wd1, c[4], gp;
uint32 *fbuf = (uint32 *) uptr->filebuf;
uint32 un = uptr - rad_unit;
uint32 dva = rad_dib.dva | un;
uint32 st;
int32 t;
switch (rad_cmd) {
case RADS_INIT: /* init state */
st = chan_get_cmd (rad_dib.dva, &cmd); /* get command */
st = chan_get_cmd (dva, &cmd); /* get command */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
return rad_chan_err (dva, st);
if ((cmd == 0) || /* invalid cmd? */
((cmd > RADS_CHECK) && (cmd != RADS_RDEES))) {
chan_uen (rad_dib.dva); /* uend */
chan_uen (dva); /* uend */
return SCPE_OK;
}
rad_flags = 0; /* clear status */
@ -279,9 +293,9 @@ switch (rad_cmd) {
return SCPE_OK;
case RADS_END: /* end state */
st = chan_end (rad_dib.dva); /* set channel end */
st = chan_end (dva); /* set channel end */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
return rad_chan_err (dva, st);
if (st == CHS_CCH) { /* command chain? */
rad_cmd = RADS_INIT; /* restart thread */
sim_activate (uptr, chan_ctl_time);
@ -291,13 +305,13 @@ switch (rad_cmd) {
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 */
st = chan_RdMemB (dva, &c[i]); /* get byte */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
return rad_chan_err (dva, 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? */
chan_set_chf (dva, CHF_LNTE)) /* care? */
return SCPE_OK;
break;
@ -307,12 +321,12 @@ switch (rad_cmd) {
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 */
st = chan_WrMemB (dva, c[i]); /* store char */
if (CHS_IFERR (st)) /* channel error? */
return rad_chan_err (st);
return rad_chan_err (dva, st);
}
if (((i != rad_tab[rad_model].nbys) || (st != CHS_ZBC)) &&
chan_set_chf (rad_dib.dva, CHF_LNTE)) /* length error? */
chan_set_chf (dva, CHF_LNTE)) /* length error? */
return SCPE_OK;
break;
@ -321,19 +335,19 @@ switch (rad_cmd) {
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 */
chan_uen (dva); /* uend */
return SCPE_OK;
} /* fall through */
if (rad_inv_ad (&da)) { /* invalid addr? */
chan_uen (rad_dib.dva); /* uend */
chan_uen (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 */
st = chan_RdMemW (dva, &wd); /* get data */
if (CHS_IFERR (st)) { /* channel error? */
rad_inc_ad (); /* da increments */
return rad_chan_err (st);
return rad_chan_err (dva, st);
}
}
else wd = 0;
@ -349,20 +363,20 @@ switch (rad_cmd) {
case RADS_CHECK: /* write check */
if (rad_inv_ad (&da)) { /* invalid addr? */
chan_uen (rad_dib.dva); /* uend */
chan_uen (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 */
st = chan_RdMemB (dva, &wd); /* read sector */
if (CHS_IFERR (st)) { /* channel error? */
rad_inc_ad (); /* da increments */
return rad_chan_err (st);
return rad_chan_err (dva, 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 */
chan_set_chf (dva, CHF_XMDE); /* set xmt err flag */
chan_uen (dva); /* force uend */
return SCPE_OK;
}
da = da + ((++i % 4) == 0); /* every 4th byte */
@ -373,14 +387,14 @@ switch (rad_cmd) {
case RADS_READ: /* read */
if (rad_inv_ad (&da)) { /* invalid addr? */
chan_uen (rad_dib.dva); /* uend */
chan_uen (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 */
st = chan_WrMemW (dva, fbuf[da]); /* store in mem */
if (CHS_IFERR (st)) { /* channel error? */
rad_inc_ad (); /* da increments */
return rad_chan_err (st);
return rad_chan_err (dva, st);
}
}
if (rad_end_sec (uptr, i, RAD_WDSC, st)) /* transfer done? */
@ -403,15 +417,18 @@ return SCPE_OK;
t_bool rad_end_sec (UNIT *uptr, uint32 lnt, uint32 exp, uint32 st)
{
uint32 un = uptr - rad_unit;
uint32 dva = rad_dib.dva | un;
if (st != CHS_ZBC) { /* end record? */
if (rad_inc_ad ()) /* inc addr, ovf? */
chan_uen (rad_dib.dva); /* uend */
chan_uen (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? */
chan_set_chf (dva, CHF_LNTE)) /* do we care? */
return TRUE;
return FALSE; /* cmd done */
}
@ -483,9 +500,9 @@ return FALSE;
/* Channel error */
t_stat rad_chan_err (uint32 st)
t_stat rad_chan_err (uint32 dva, uint32 st)
{
chan_uen (rad_dib.dva); /* uend */
chan_uen (dva); /* uend */
if (st < CHS_ERR)
return st;
return SCPE_OK;