PDP11/VAX: pdp11_hk Fix by Bob Supnik for issue #47

Revised error handling to command-response model
Revised interrupt logic to follow the hardware
This commit is contained in:
Mark Pizzolato 2013-09-03 05:15:23 -07:00
parent 2722bc4864
commit 89024724d4

View file

@ -1,6 +1,6 @@
/* pdp11_hk.c - RK611/RK06/RK07 disk controller /* pdp11_hk.c - RK611/RK06/RK07 disk controller
Copyright (c) 1993-2012, Robert M Supnik Copyright (c) 1993-2013, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -25,9 +25,14 @@
hk RK611/RK06/RK07 disk hk RK611/RK06/RK07 disk
10-Dec-12 RMS Fixed interrupt logic 01-Sep-13 RMS Revised error handling to command-response model
Revised interrupt logic to follow the hardware
10-Jun-13 RMS Fixed bug to write through end of sector (Oleg Safiullin)
18-Apr-13 RMS Fixed ATN setting on errors
Changed wrong drive type to status, not fatal error
10-Dec-12 RMS Fixed interrupt logic and CCLR unit cancellation
19-Mar-12 RMS Fixed declaration of cpu_opt (Mark Pizzolato) 19-Mar-12 RMS Fixed declaration of cpu_opt (Mark Pizzolato)
29-Apr-07 RMS NOP and DCLR (at least) do not check drive type 29-Apr-07 RMS NOP and DCLR (at least) do not check drive type [wrong]
MR2 and MR3 only updated on NOP MR2 and MR3 only updated on NOP
17-Nov-05 RMS Removed unused variable 17-Nov-05 RMS Removed unused variable
13-Nov-05 RMS Fixed overlapped seek interaction with NOP, DCLR, PACK 13-Nov-05 RMS Fixed overlapped seek interaction with NOP, DCLR, PACK
@ -42,16 +47,13 @@
29-Dec-03 RMS Added 18b Qbus support 29-Dec-03 RMS Added 18b Qbus support
25-Apr-03 RMS Revised for extended file support 25-Apr-03 RMS Revised for extended file support
This is a somewhat abstracted implementation of the RK611, more closely The RK611 functions only in 18b Unibus systems with I/O maps. The Emulex
modelled on third party clones than DEC's own implementation. In particular, SC02/C was a Qbus work-alike with a unique extension to 22b addressing. It
the drive-to-controller serial communications system is simulated only at
a level equal to the Emulex SC21.
The RK611 functions only in 18b Unibus systems with I/O maps. The Emulex
SC02/C was a Qbus work-alike with a unique extension to 22b addressing. It
was only supported in Ultrix-11 and other third party software. was only supported in Ultrix-11 and other third party software.
This module includes ideas from a previous implementation by Fred Van Kempen. This module includes ideas from a previous implementation by Fred Van Kempen.
However, the interrupt logic has been rewritten to follow the bug-for-bug
peculiarities of the RK611 controller.
*/ */
#if defined (VM_PDP10) /* PDP10 version */ #if defined (VM_PDP10) /* PDP10 version */
@ -98,7 +100,7 @@ extern uint16 *M;
#define CYL u3 /* current cylinder */ #define CYL u3 /* current cylinder */
#define FNC u4 /* function */ #define FNC u4 /* function */
/* HKCS1 - 177440 - control/status 1 */ /* HKCS1 - 177440 - control/status 1 ^ = calculated dynamically */
#define CS1_GO CSR_GO /* go */ #define CS1_GO CSR_GO /* go */
#define CS1_V_FNC 1 /* function pos */ #define CS1_V_FNC 1 /* function pos */
@ -277,7 +279,7 @@ BITFIELD hk_ds_bits[] = {
#define ER_SKI 0000002 /* seek incomp */ #define ER_SKI 0000002 /* seek incomp */
#define ER_NXF 0000004 /* non-exec func */ #define ER_NXF 0000004 /* non-exec func */
#define ER_PAR 0000010 /* parity err */ #define ER_PAR 0000010 /* parity err */
#define ER_FER 0000020 /* format err NI */ #define ER_FER 0000020 /* format err */
#define ER_DTY 0000040 /* drive type err */ #define ER_DTY 0000040 /* drive type err */
#define ER_ECH 0000100 /* ECC hard err NI */ #define ER_ECH 0000100 /* ECC hard err NI */
#define ER_BSE 0000200 /* bad sector err NI */ #define ER_BSE 0000200 /* bad sector err NI */
@ -540,6 +542,9 @@ int32 hkmr2 = 0;
int32 hkmr3 = 0; int32 hkmr3 = 0;
int32 hkdc = 0; /* cylinder */ int32 hkdc = 0; /* cylinder */
int32 hkspr = 0; /* spare */ int32 hkspr = 0; /* spare */
int32 hkci = 0; /* ctlr interrupt */
int32 hkdi = 0; /* drive interrupt */
int32 hkei = 0; /* error interrupt */
int32 hk_cwait = 5; /* command time */ int32 hk_cwait = 5; /* command time */
int32 hk_swait = 10; /* seek time */ int32 hk_swait = 10; /* seek time */
int32 hk_rwait = 10; /* rotate time */ int32 hk_rwait = 10; /* rotate time */
@ -547,7 +552,7 @@ int32 hk_min2wait = 300; /* min time to 2nd int *
int16 hkdb[3] = { 0 }; /* data buffer silo */ int16 hkdb[3] = { 0 }; /* data buffer silo */
int16 hk_off[HK_NUMDR] = { 0 }; /* saved offset */ int16 hk_off[HK_NUMDR] = { 0 }; /* saved offset */
int16 hk_dif[HK_NUMDR] = { 0 }; /* cylinder diff */ int16 hk_dif[HK_NUMDR] = { 0 }; /* cylinder diff */
static uint8 reg_in_drive[16] = { static const uint8 reg_in_drive[16] = {
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
DEVICE hk_dev; DEVICE hk_dev;
@ -558,11 +563,12 @@ t_stat hk_reset (DEVICE *dptr);
t_stat hk_boot (int32 unitno, DEVICE *dptr); t_stat hk_boot (int32 unitno, DEVICE *dptr);
t_stat hk_attach (UNIT *uptr, char *cptr); t_stat hk_attach (UNIT *uptr, char *cptr);
t_stat hk_detach (UNIT *uptr); t_stat hk_detach (UNIT *uptr);
int32 hk_inta (void);
int32 hk_rdmr2 (int32 msg); int32 hk_rdmr2 (int32 msg);
int32 hk_rdmr3 (int32 msg); int32 hk_rdmr3 (int32 msg);
void update_hkcs (int32 flags, int32 drv); void update_hkcs (int32 flags, int32 drv);
void update_hkds (int32 drv); void update_hkds (int32 drv);
void hk_cmderr (int32 err, int32 drv); void hk_err (int32 cs1e, int32 cs2e, int32 drve, int32 drv);
void hk_go (int32 drv); void hk_go (int32 drv);
t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc);
@ -581,7 +587,7 @@ char *hk_description (DEVICE *dptr);
DIB hk_dib = { DIB hk_dib = {
IOBA_AUTO, IOLN_HK, &hk_rd, &hk_wr, IOBA_AUTO, IOLN_HK, &hk_rd, &hk_wr,
1, IVCL (HK), VEC_AUTO, { NULL }, IOLN_HK, 1, IVCL (HK), VEC_AUTO, { &hk_inta }, IOLN_HK,
}; };
UNIT hk_unit[] = { UNIT hk_unit[] = {
@ -618,6 +624,9 @@ REG hk_reg[] = {
{ GRDATAD (HKMR2, hkmr2, DEV_RDX, 16, 0, "maintenance register 2"), REG_RO }, { GRDATAD (HKMR2, hkmr2, DEV_RDX, 16, 0, "maintenance register 2"), REG_RO },
{ GRDATAD (HKMR3, hkmr3, DEV_RDX, 16, 0, "maintenance register 3"), REG_RO }, { GRDATAD (HKMR3, hkmr3, DEV_RDX, 16, 0, "maintenance register 3"), REG_RO },
{ GRDATAD (HKSPR, hkspr, DEV_RDX, 16, 0, "spare register") }, { GRDATAD (HKSPR, hkspr, DEV_RDX, 16, 0, "spare register") },
{ FLDATAD (HKCI, hkci, 0, "ctlr interrupt flop") },
{ FLDATAD (HKDI, hkdi, 0, "drive interrupt flop") },
{ FLDATAD (HKEI, hkei, 0, "error interrupt flop") },
{ FLDATAD (INT, IREQ (HK), INT_V_HK, "interrupt pending flag") }, { FLDATAD (INT, IREQ (HK), INT_V_HK, "interrupt pending flag") },
{ FLDATAD (ERR, hkcs1, CSR_V_ERR, "error flag (CSR<15>)") }, { FLDATAD (ERR, hkcs1, CSR_V_ERR, "error flag (CSR<15>)") },
{ FLDATAD (DONE, hkcs1, CSR_V_DONE, "device done flag (CSR1<7>)") }, { FLDATAD (DONE, hkcs1, CSR_V_DONE, "device done flag (CSR1<7>)") },
@ -625,7 +634,7 @@ REG hk_reg[] = {
{ DRDATAD (CTIME, hk_cwait, 24, "command time"), REG_NZ + PV_LEFT }, { DRDATAD (CTIME, hk_cwait, 24, "command time"), REG_NZ + PV_LEFT },
{ DRDATAD (STIME, hk_swait, 24, "seek time, per cylinder"), REG_NZ + PV_LEFT }, { DRDATAD (STIME, hk_swait, 24, "seek time, per cylinder"), REG_NZ + PV_LEFT },
{ DRDATAD (RTIME, hk_rwait, 24, "rotational delay"), REG_NZ + PV_LEFT }, { DRDATAD (RTIME, hk_rwait, 24, "rotational delay"), REG_NZ + PV_LEFT },
{ DRDATA (MIN2TIME, hk_min2wait, 24), REG_NZ + PV_LEFT + REG_HRO }, { DRDATAD (MIN2TIME, hk_min2wait, 24, "minimum time between DONE and ATA"), REG_NZ + PV_LEFT },
{ URDATA (FNC, hk_unit[0].FNC, DEV_RDX, 5, 0, { URDATA (FNC, hk_unit[0].FNC, DEV_RDX, 5, 0,
HK_NUMDR, REG_HRO) }, HK_NUMDR, REG_HRO) },
{ URDATA (CYL, hk_unit[0].CYL, DEV_RDX, 10, 0, { URDATA (CYL, hk_unit[0].CYL, DEV_RDX, 10, 0,
@ -696,8 +705,7 @@ int32 drv, i, j;
drv = GET_UNIT (hkcs2); /* get current unit */ drv = GET_UNIT (hkcs2); /* get current unit */
j = (PA >> 1) & 017; /* get reg offset */ j = (PA >> 1) & 017; /* get reg offset */
if (reg_in_drive[j] && (hk_unit[drv].flags & UNIT_DIS)) { /* nx disk */ if (reg_in_drive[j] && (hk_unit[drv].flags & UNIT_DIS)) { /* nx disk */
hkcs2 = hkcs2 | CS2_NED; /* set error flag */ hk_err (CS1_ERR|CS1_DONE, CS2_NED, 0, drv); /* set err, stop op */
update_hkcs (0, drv);
*data = 0; *data = 0;
return SCPE_OK; return SCPE_OK;
} }
@ -706,7 +714,7 @@ update_hkcs (0, drv); /* update status */
switch (j) { /* decode PA<4:1> */ switch (j) { /* decode PA<4:1> */
case 000: /* HKCS1 */ case 000: /* HKCS1 */
*data = hkcs1; *data = (hkcs1 & ~CS1_DI) | (hkdi? CS1_DI: 0); /* DI dynamic */
break; break;
case 001: /* HKWC */ case 001: /* HKWC */
@ -785,11 +793,14 @@ int32 drv, i, j, old_val = 0, new_val = 0;
drv = GET_UNIT (hkcs2); /* get current unit */ drv = GET_UNIT (hkcs2); /* get current unit */
j = (PA >> 1) & 017; /* get reg offset */ j = (PA >> 1) & 017; /* get reg offset */
if (reg_in_drive[j] && (hk_unit[drv].flags & UNIT_DIS)) { /* nx disk */
hk_err (CS1_ERR|CS1_DONE, CS2_NED, 0, drv); /* set err, stop op */
return SCPE_OK;
}
if ((hkcs1 & CS1_GO) && /* busy? */ if ((hkcs1 & CS1_GO) && /* busy? */
!(((j == 0) && (data & CS1_CCLR)) || /* not cclr or sclr? */ !(((j == 0) && (data & CS1_CCLR)) || /* not cclr or sclr? */
((j == 4) && (data & CS2_CLR)))) { ((j == 4) && (data & CS2_CLR)))) {
hkcs2 = hkcs2 | CS2_PGE; /* prog error */ hk_err (CS1_ERR|CS1_DONE, CS2_PGE, 0, drv); /* set err, stop op */
update_hkcs (0, drv);
return SCPE_OK; return SCPE_OK;
} }
@ -805,7 +816,7 @@ switch (j) { /* decode PA<4:1> */
hkda = hkdc = 0; hkda = hkdc = 0;
hkba = hkwc = 0; hkba = hkwc = 0;
hkspr = hkof = 0; hkspr = hkof = 0;
CLR_INT (HK); /* clr int */ hkci = hkdi = hkei = 0; /* clr int flops */
for (i = 0; i < HK_NUMDR; i++) { /* stop data xfr */ for (i = 0; i < HK_NUMDR; i++) { /* stop data xfr */
if (sim_is_active (&hk_unit[i]) && if (sim_is_active (&hk_unit[i]) &&
((hk_unit[i].FNC & CS1_M_FNC) >= FNC_XFER)) ((hk_unit[i].FNC & CS1_M_FNC) >= FNC_XFER))
@ -814,21 +825,15 @@ switch (j) { /* decode PA<4:1> */
drv = 0; drv = 0;
break; break;
} }
if (data & CS1_IE) { /* setting IE? */ if (((data & CS1_IE) != 0) && ((data & CS1_DONE) != 0)) {
if (data & CS1_DONE) { /* write to DONE+IE? */ sim_debug (HKDEB_INT, &hk_dev, "hk_wr(ctlr int)\n");
sim_debug (HKDEB_INT, &hk_dev, "hk_wr(SET_INT)\n"); hkci = 1; /* set ctlr intr */
SET_INT (HK);
}
}
else {
sim_debug (HKDEB_INT, &hk_dev, "hk_wr(CLR_INT)\n");
CLR_INT (HK); /* no, clr intr */
} }
hkcs1 = (hkcs1 & ~CS1_RW) | (data & CS1_RW); /* merge data */ hkcs1 = (hkcs1 & ~CS1_RW) | (data & CS1_RW); /* merge data */
if (SC02C) if (SC02C)
hkspr = (hkspr & ~CS1_M_UAE) | GET_UAE (hkcs1); hkspr = (hkspr & ~CS1_M_UAE) | GET_UAE (hkcs1);
if ((data & CS1_GO) && !(hkcs1 & CS1_ERR)) /* go? */ if (((data & CS1_GO) != 0) && ((hkcs1 & CS1_ERR) == 0))
hk_go (drv); hk_go (drv); /* go & ~err? */
new_val = hkcs1; new_val = hkcs1;
break; break;
@ -910,12 +915,12 @@ return SCPE_OK;
void hk_go (int32 drv) void hk_go (int32 drv)
{ {
int32 fnc, t; int32 fnc, t;
t_bool dte;
UNIT *uptr; UNIT *uptr;
static uint8 fnc_cdt[16] = { static uint8 fnc_dte[16] = {
0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
}; };
static uint8 fnc_nxf[16] = { static uint8 fnc_nxf[16] = {
0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0
}; };
@ -933,51 +938,65 @@ fnc = GET_FNC (hkcs1);
sim_debug (HKDEB_OPS, &hk_dev, ">>HK%d strt: fnc=%s, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n", sim_debug (HKDEB_OPS, &hk_dev, ">>HK%d strt: fnc=%s, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n",
drv, hk_funcs[fnc], hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc); drv, hk_funcs[fnc], hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc);
uptr = hk_dev.units + drv; /* get unit */ uptr = hk_dev.units + drv; /* get unit */
dte = ((hkcs1 & CS1_DT) !=0) != ((uptr->flags & UNIT_DTYPE) != 0);
if (fnc != FNC_NOP) /* !nop, clr msg sel */ if (fnc != FNC_NOP) /* !nop, clr msg sel */
hkmr = hkmr & ~MR_MS; hkmr = hkmr & ~MR_MS;
if (uptr->flags & UNIT_DIS) { /* nx unit? */ if (uptr->flags & UNIT_DIS) { /* nx unit? */
hkcs2 = hkcs2 | CS2_NED; /* set error flag */ hk_err (CS1_ERR|CS1_DONE, CS2_NED, 0, drv); /* set err, no op */
update_hkcs (CS1_DONE, drv); /* done */
return; return;
} }
if (fnc_cdt[fnc] && /* need dtype match? */ if ((hkcs1 & CS1_FMT) != 0) { /* 18b format? */
(((hkcs1 & CS1_DT) != 0) != ((uptr->flags & UNIT_DTYPE) != 0))) { hk_err (CS1_ERR|CS1_DONE, 0, ER_FER, drv); /* set err, no op */
hk_cmderr (ER_DTY, drv); /* type error */ return;
return; }
if (fnc_dte[fnc] && dte) { /* need drv match ? */
hker[drv] = hker[drv] | ER_DTY; /* drive type mismatch? */
hk_err (CS1_ERR|CS1_DONE, 0, ER_DTY, drv); /* set err, no op */
} }
if (fnc_nxf[fnc] && ((hkds[drv] & DS_VV) == 0)) { /* need vol valid? */ if (fnc_nxf[fnc] && ((hkds[drv] & DS_VV) == 0)) { /* need vol valid? */
hk_cmderr (ER_NXF, drv); /* non exec func */ hkds[drv] = hkds[drv] | DS_ATA; /* set ATTN */
hk_err (CS1_ERR|CS1_DI|CS1_DONE, 0, ER_NXF, drv); /* set err, no op */
return; return;
} }
if (fnc_att[fnc] && !(uptr->flags & UNIT_ATT)) { /* need attached? */ if (fnc_att[fnc] && !(uptr->flags & UNIT_ATT)) { /* need attached? */
hk_cmderr (ER_UNS, drv); /* unsafe */ hkds[drv] = hkds[drv] | DS_ATA; /* set ATTN */
hk_err (CS1_ERR|CS1_DI|CS1_DONE, 0, ER_UNS, drv); /* set err, no op */
return; return;
} }
if (fnc_rdy[fnc] && sim_is_active (uptr)) /* need inactive? */ if (fnc_rdy[fnc] && sim_is_active (uptr)) /* need inactive? */
return; return;
if (fnc_cyl[fnc] && /* need valid cyl */ if (fnc_cyl[fnc] && /* need valid cyl */
((GET_CY (hkdc) >= HK_CYL (uptr)) || /* bad cylinder */ ((GET_CY (hkdc) >= HK_CYL (uptr)) || /* bad cylinder */
(GET_SF (hkda) >= HK_NUMSF) || /* bad surface */ (GET_SF (hkda) >= HK_NUMSF))) { /* bad surface */
(GET_SC (hkda) >= HK_NUMSC))) { /* or bad sector? */ hk_err (CS1_ERR|CS1_DONE, 0, ER_SKI|ER_IAE, drv); /* set err, no op */
hk_cmderr (ER_IAE, drv); /* illegal addr */ return;
return; }
}
hkcs1 = (hkcs1 | CS1_GO) & ~CS1_DONE; /* set go, clear done */ hkcs1 = (hkcs1 | CS1_GO) & ~CS1_DONE; /* set go, clear done */
hkci = hkdi = hkei = 0; /* clear all intr */
CLR_INT (HK);
switch (fnc) { /* case on function */ switch (fnc) { /* case on function */
/* Instantaneous functions (unit may be busy, can't schedule thread) */ /* Instantaneous functions (unit may be busy, can't schedule thread,
can't overwrite unit function field) */
case FNC_NOP: /* no operation/select drive */ case FNC_NOP: /* no operation */
hkmr2 = hk_rdmr2 (GET_MS (hkmr)); /* get serial msgs */ hkmr2 = hk_rdmr2 (GET_MS (hkmr)); /* get serial msgs */
hkmr3 = hk_rdmr3 (GET_MS (hkmr)); hkmr3 = hk_rdmr3 (GET_MS (hkmr));
update_hkcs (CS1_DONE, drv); /* done */ if (dte) /* drive type err? */
hk_err (CS1_ERR|CS1_DONE, 0, ER_DTY, drv);
else update_hkcs (CS1_DONE, drv); /* done */
break; break;
case FNC_DCLR: /* drive clear */ case FNC_DCLR: /* drive clear */
hkds[drv] &= ~DS_ATA; /* clr ATA */ hkds[drv] &= ~DS_ATA; /* clr ATA */
hker[drv] = 0; /* clear errors */ hker[drv] = 0; /* clr err */
update_hkcs (CS1_DONE, drv); /* done */ if (dte) /* drive type err? */
hk_err (CS1_ERR|CS1_DONE, 0, ER_DTY, drv);
else update_hkcs (CS1_DONE, drv); /* done */
break; break;
case FNC_PACK: /* pack acknowledge */ case FNC_PACK: /* pack acknowledge */
@ -1012,15 +1031,19 @@ switch (fnc) { /* case on function */
case FNC_WCHK: /* write check */ case FNC_WCHK: /* write check */
case FNC_READ: /* read */ case FNC_READ: /* read */
case FNC_READH: /* read headers */ case FNC_READH: /* read headers */
if (GET_SC (hkda) >= HK_NUMSC) { /* invalid sector? */
hk_err (CS1_ERR|CS1_DONE, 0, ER_OPI, drv); /* set err, no op */
return;
}
hk_dif[drv] = hkdc - uptr->CYL; /* cyl diff */ hk_dif[drv] = hkdc - uptr->CYL; /* cyl diff */
t = abs (hk_dif[drv]); /* |cyl diff| */ t = abs (hk_dif[drv]); /* |cyl diff| */
sim_activate (uptr, hk_rwait + (hk_swait * t)); /* schedule */
uptr->FNC = fnc; /* save function */ uptr->FNC = fnc; /* save function */
sim_activate (uptr, hk_rwait + (hk_swait * t)); /* schedule */
uptr->CYL = hkdc; /* update cyl */ uptr->CYL = hkdc; /* update cyl */
return; return;
default: default:
hk_cmderr (ER_ILF, drv); /* not supported */ hk_err (CS1_ERR|CS1_DONE, 0, ER_ILF, drv); /* not supported */
break; break;
} }
return; return;
@ -1060,7 +1083,7 @@ switch (fnc) { /* case on function */
case FNC_OFFSET: /* offset */ case FNC_OFFSET: /* offset */
if (uptr->FNC & FNC_2ND) { /* 2nd int? */ if (uptr->FNC & FNC_2ND) { /* 2nd int? */
hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */ hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */
update_hkcs (CS1_DI, drv); /* drive intr */ update_hkcs (CS1_DI, drv); /* ATN set */
} }
else { else {
uptr->FNC = uptr->FNC | FNC_2ND; /* second state */ uptr->FNC = uptr->FNC | FNC_2ND; /* second state */
@ -1074,7 +1097,7 @@ switch (fnc) { /* case on function */
case FNC_SEEK: /* seek */ case FNC_SEEK: /* seek */
if (uptr->FNC & FNC_2ND) { /* 2nd int? */ if (uptr->FNC & FNC_2ND) { /* 2nd int? */
hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */ hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */
update_hkcs (CS1_DI, drv); /* drive intr */ update_hkcs (CS1_DI, drv); /* ATN set */
} }
else { else {
uptr->FNC = uptr->FNC | FNC_2ND; /* second state */ uptr->FNC = uptr->FNC | FNC_2ND; /* second state */
@ -1102,7 +1125,7 @@ switch (fnc) { /* case on function */
case FNC_WRITE: /* write */ case FNC_WRITE: /* write */
if (uptr->flags & UNIT_WPRT) { /* write locked? */ if (uptr->flags & UNIT_WPRT) { /* write locked? */
hk_cmderr (ER_WLE, drv); /* command error */ hk_err (CS1_ERR|CS1_DONE, 0, ER_WLE, drv); /* set err, stop op */
return SCPE_OK; return SCPE_OK;
} }
case FNC_WCHK: /* write check */ case FNC_WCHK: /* write check */
@ -1128,7 +1151,7 @@ switch (fnc) { /* case on function */
if (hkcs2 & CS2_UAI) { /* no addr inc? */ if (hkcs2 & CS2_UAI) { /* no addr inc? */
if ((t = Map_ReadW (ba, 2, &comp))) { /* get 1st wd */ if ((t = Map_ReadW (ba, 2, &comp))) { /* get 1st wd */
wc = 0; /* NXM, no xfr */ wc = 0; /* NXM, no xfr */
hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ hk_err (CS1_ERR, CS2_NEM, 0, drv);
} }
for (i = 0; i < wc; i++) for (i = 0; i < wc; i++)
hkxb[i] = comp; hkxb[i] = comp;
@ -1136,7 +1159,7 @@ switch (fnc) { /* case on function */
else { /* normal */ else { /* normal */
if ((t = Map_ReadW (ba, wc << 1, hkxb))) {/* get buf */ if ((t = Map_ReadW (ba, wc << 1, hkxb))) {/* get buf */
wc = wc - (t >> 1); /* NXM, adj wc */ wc = wc - (t >> 1); /* NXM, adj wc */
hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ hk_err (CS1_ERR, CS2_NEM, 0, drv);
} }
ba = ba + (wc << 1); /* adv ba */ ba = ba + (wc << 1); /* adv ba */
} }
@ -1156,13 +1179,13 @@ switch (fnc) { /* case on function */
if (hkcs2 & CS2_UAI) { /* no addr inc? */ if (hkcs2 & CS2_UAI) { /* no addr inc? */
if ((t = Map_WriteW (ba, 2, &hkxb[wc - 1]))) { if ((t = Map_WriteW (ba, 2, &hkxb[wc - 1]))) {
wc = 0; /* NXM, no xfr */ wc = 0; /* NXM, no xfr */
hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ hk_err (CS1_ERR, CS2_NEM, 0, drv);
} }
} }
else { /* normal */ else { /* normal */
if ((t = Map_WriteW (ba, wc << 1, hkxb))) {/* put buf */ if ((t = Map_WriteW (ba, wc << 1, hkxb))) {/* put buf */
wc = wc - (t >> 1); /* NXM, adj wc */ wc = wc - (t >> 1); /* NXM, adj wc */
hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ hk_err (CS1_ERR, CS2_NEM, 0, drv);
} }
ba = ba + (wc << 1); /* adv ba */ ba = ba + (wc << 1); /* adv ba */
} }
@ -1175,15 +1198,15 @@ switch (fnc) { /* case on function */
awc = wc; awc = wc;
for (wc = 0; wc < awc; wc++) { /* loop thru buf */ for (wc = 0; wc < awc; wc++) { /* loop thru buf */
if (Map_ReadW (ba, 2, &comp)) { /* read word */ if (Map_ReadW (ba, 2, &comp)) { /* read word */
hkcs2 = hkcs2 | CS2_NEM; /* set error */ hk_err (CS1_ERR, CS2_NEM, 0, drv);
break; break;
} }
if (comp != hkxb[wc]) { /* compare wd */ if (comp != hkxb[wc]) { /* compare wd */
hkcs2 = hkcs2 | CS2_WCE; /* set error */ hk_err (CS1_ERR, CS2_WCE, 0, drv);
break; break;
} }
if ((hkcs2 & CS2_UAI) if ((hkcs2 & CS2_UAI) == 0)
== 0) ba = ba + 2; ba = ba + 2;
} }
} /* end else wchk */ } /* end else wchk */
@ -1200,7 +1223,7 @@ switch (fnc) { /* case on function */
hkdc = da / HK_NUMSF; hkdc = da / HK_NUMSF;
if (err != 0) { /* error? */ if (err != 0) { /* error? */
hk_cmderr (ER_PAR, drv); /* set drive error */ hk_err (CS1_ERR|CS1_DONE, 0, ER_PAR, drv); /* set drive error */
perror ("HK I/O error"); perror ("HK I/O error");
clearerr (uptr->fileref); clearerr (uptr->fileref);
return SCPE_IOERR; return SCPE_IOERR;
@ -1216,9 +1239,12 @@ return SCPE_OK;
/* Controller status update /* Controller status update
Check for done transition
Update drive status Update drive status
Update HKCS1 Update HKCS1
Check for done transition
clock CI from IE
set DI if any ATN bits set
Check for DI set if no transition but DONE is set
Update interrupt request Update interrupt request
*/ */
@ -1228,21 +1254,24 @@ int32 i, old_hkcs1 = hkcs1, old_hkcs2 = hkcs2;
sim_debug (HKDEB_TRC, &hk_dev, "update_hkcs(flag=0%o, drv=%d)\n", flag, drv); sim_debug (HKDEB_TRC, &hk_dev, "update_hkcs(flag=0%o, drv=%d)\n", flag, drv);
update_hkds (drv); /* upd drv status */ update_hkds (drv); /* upd drv status */
hkcs1 = (hkcs1 & (CS1_DT|CS1_UAE|CS1_DONE|CS1_IE|CS1_SPA|CS1_FNC|CS1_GO)) | flag; hkcs1 = (hkcs1 & (CS1_ERR|CS1_DT|CS1_UAE|CS1_DONE|CS1_IE|CS1_SPA|CS1_FNC|CS1_GO)) |
if (hkcs1 & CS1_DONE) /* done? clear GO */ (flag & ~CS1_DI);
hkcs1 = hkcs1 & ~CS1_GO; if ((hkcs1 & CS1_DONE) != 0) { /* done? */
for (i = 0; i < HK_NUMDR; i++) { /* if ATA, set DI */ hkcs1 = hkcs1 & ~CS1_GO; /* clear go */
if (hkds[i] & DS_ATA) if ((old_hkcs1 & CS1_DONE) == 0) { /* done 0->1? */
hkcs1 = hkcs1 | CS1_DI; hkci = (hkcs1 & CS1_IE)? 1: 0; /* clk CI from IE */
} for (i = 0; i < HK_NUMDR; i++) { /* if ATA, set DI */
if (hker[drv] || (hkcs1 & (CS1_PAR | CS1_CTO)) || (hkcs2 & CS2_ERR)) if (hkds[i] & DS_ATA)
hkcs1 = hkcs1 | CS1_ERR; /* if err, set ERR */ hkdi = 1;
if (hkcs1 & CS1_IE) { /* intr enable? */ }
if (((hkcs1 & CS1_DONE) && ((old_hkcs1 & CS1_DONE) == 0)) ||
((hkcs1 & CS1_DI) && (hkcs1 & CS1_DONE))) { /* done 0->1 or DI&done? */
sim_debug (HKDEB_INT, &hk_dev, "update_hkcs(SET_INT)\n");
SET_INT (HK);
} }
else if ((flag & CS1_DI) != 0) /* done set; new ATN? */
hkdi = 1; /* set drv int */
}
else hkdi = 0; /* not done, clr DI */
if (((hkcs1 & CS1_IE) != 0) && (hkci || hkdi || hkei)) {/* int enab & set? */
sim_debug (HKDEB_INT, &hk_dev, "update_hkcs(SET_INT)\n");
SET_INT (HK);
} }
else { else {
sim_debug (HKDEB_INT, &hk_dev, "update_hkcs(CLR_INT)\n"); sim_debug (HKDEB_INT, &hk_dev, "update_hkcs(CLR_INT)\n");
@ -1253,8 +1282,12 @@ if (old_hkcs1 != hkcs1)
if (old_hkcs2 != hkcs2) if (old_hkcs2 != hkcs2)
sim_debug_bits (HKDEB_OPS, &hk_dev, hk_cs2_bits, old_hkcs2, hkcs2, 1); sim_debug_bits (HKDEB_OPS, &hk_dev, hk_cs2_bits, old_hkcs2, hkcs2, 1);
if (flag & CS1_DONE) { /* set done */ if (flag & CS1_DONE) { /* set done */
sim_debug (HKDEB_OPS, &hk_dev, ">>HK%d done: fnc=%s, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n", sim_debug (HKDEB_OPS, &hk_dev, ">>HK%d done: fnc=%s, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o, ci=%d, di=%d\n",
drv, hk_funcs[GET_FNC (hkcs1)], hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc); drv, hk_funcs[GET_FNC (hkcs1)], hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc, hkci, hkdi);
}
if (flag & CS1_DI) { /* set ATA? */
sim_debug (HKDEB_OPS, &hk_dev, ">>HK%d ATA: fnc=%s, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o, ci=%d, di=%d\n",
drv, hk_funcs[GET_FNC (hkcs1)], hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc, hkci, hkdi);
} }
return; return;
} }
@ -1271,38 +1304,47 @@ if (hk_unit[drv].flags & UNIT_DIS) { /* disabled? */
} }
sim_debug (HKDEB_TRC, &hk_dev, "update_hkds(drv=%d)\n", drv); sim_debug (HKDEB_TRC, &hk_dev, "update_hkds(drv=%d)\n", drv);
hkds[drv] = (hkds[drv] & (DS_VV | DS_PIP | DS_ATA)) | DS_VLD | DS_DRA; hkds[drv] = (hkds[drv] & (DS_VV | DS_PIP | DS_ATA)) | DS_VLD | DS_DRA;
if (hk_unit[drv].flags & UNIT_RK07)
hkds[drv] = hkds[drv] | DS_DT;
if (hk_unit[drv].flags & UNIT_ATT) { /* attached? */ if (hk_unit[drv].flags & UNIT_ATT) { /* attached? */
if (!sim_is_active (&hk_unit[drv])) /* not busy? */ if (!sim_is_active (&hk_unit[drv])) /* not busy? */
hkds[drv] = hkds[drv] | DS_RDY; /* set RDY */ hkds[drv] = hkds[drv] | DS_RDY; /* set RDY */
if (hker[drv]) /* err? set ATA */
hkds[drv] = hkds[drv] | DS_ATA;
if (hk_off[drv]) /* offset? set OF */ if (hk_off[drv]) /* offset? set OF */
hkds[drv] = hkds[drv] | DS_OF; hkds[drv] = hkds[drv] | DS_OF;
if (hk_unit[drv].flags & UNIT_WPRT) /* write locked? */ if (hk_unit[drv].flags & UNIT_WPRT) /* write locked? */
hkds[drv] = hkds[drv] | DS_WRL; /* set WRL */ hkds[drv] = hkds[drv] | DS_WRL; /* set WRL */
} }
else { else
hkds[drv] = hkds[drv] & ~(DS_PIP | DS_VV); /* no, clr PIP,VV */ hkds[drv] = hkds[drv] & ~(DS_PIP | DS_VV); /* no, clr PIP,VV */
hker[drv] = 0; /* no errors */
}
if (hk_unit[drv].flags & UNIT_RK07)
hkds[drv] = hkds[drv] | DS_DT;
if (old_ds != hkds[drv]) if (old_ds != hkds[drv])
sim_debug_bits (HKDEB_TRC, &hk_dev, hk_ds_bits, old_ds, hkds[drv], 1); sim_debug_bits (HKDEB_TRC, &hk_dev, hk_ds_bits, old_ds, hkds[drv], 1);
return; return;
} }
/* Set error and abort command */ /* Set errors */
void hk_cmderr (int32 err, int32 drv) void hk_err (int32 cs1e, int32 cs2e, int32 drve, int32 drv)
{ {
sim_debug (HKDEB_TRC, &hk_dev, "update_hkds(drv=%d, err=%d)\n", drv, err); sim_debug (HKDEB_TRC, &hk_dev, "hk_err(drv=%d, cs1e=%d, cs2e=%d, drve=%d)\n", drv, cs1e, cs2e, drve);
hker[drv] = hker[drv] | err; /* set error */ hker[drv] = hker[drv] | drve; /* set drv error */
hkds[drv] = hkds[drv] | DS_ATA; /* set attn */ hkcs2 = hkcs2 | cs2e; /* set cs2 err */
update_hkcs (CS1_DONE, drv); /* set done */ if ((cs1e & CS1_ERR) != 0) /* set combined err? */
hkei = 1; /* then set EI */
if ((cs1e & CS1_DONE) != 0) /* set done? */
update_hkcs (CS1_ERR|CS1_DONE, drv); /* stop now */
else
hkcs1 = hkcs1 | cs1e; /* no, just upd */
return; return;
} }
/* Interrupt routine */
int32 hk_inta (void)
{
hkci = hkdi = hkei = 0; /* clear all flops */
return hk_dib.vec; /* return vector */
}
/* Diagnostic registers /* Diagnostic registers
It's unclear whether the drivers actually use these values, but the It's unclear whether the drivers actually use these values, but the
@ -1421,12 +1463,13 @@ hkmr = hkmr2 = hkmr3 = 0;
hkda = hkdc = 0; hkda = hkdc = 0;
hkba = hkwc = 0; hkba = hkwc = 0;
hkof = hkspr = 0; hkof = hkspr = 0;
hkci = hkdi = hkei = 0; /* clear intr flops */
CLR_INT (HK); /* clear intr req */ CLR_INT (HK); /* clear intr req */
for (i = 0; i < HK_NUMDR; i++) { /* stop operations */ for (i = 0; i < HK_NUMDR; i++) { /* stop operations */
uptr = hk_dev.units + i; uptr = hk_dev.units + i;
sim_cancel (uptr); sim_cancel (uptr);
if (uptr->flags & UNIT_ATT) if (uptr->flags & UNIT_ATT)
hkds[i] = hkds[i] & DS_VV; hkds[i] = hkds[i] & (DS_VV | DS_DT);
else hkds[i] = 0; else hkds[i] = 0;
uptr->CYL = uptr->FNC = 0; /* clear state */ uptr->CYL = uptr->FNC = 0; /* clear state */
hk_dif[i] = 0; hk_dif[i] = 0;
@ -1446,18 +1489,23 @@ t_stat hk_attach (UNIT *uptr, char *cptr)
{ {
uint32 drv, p; uint32 drv, p;
t_stat r; t_stat r;
int32 old_hkds;
uptr->capac = HK_SIZE (uptr); uptr->capac = HK_SIZE (uptr);
r = attach_unit (uptr, cptr); /* attach unit */ r = attach_unit (uptr, cptr); /* attach unit */
if (r != SCPE_OK) /* error? */ if (r != SCPE_OK) /* error? */
return r; return r;
drv = (uint32) (uptr - hk_dev.units); /* get drv number */ drv = (uint32) (uptr - hk_dev.units); /* get drv number */
hkds[drv] = DS_ATA | DS_RDY | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); old_hkds = hkds[drv]; /* save hkds */
hkds[drv] = DS_ATA | DS_RDY |
((uptr->flags & UNIT_WPRT)? DS_WRL: 0) |
((uptr->flags & UNIT_DTYPE)? DS_DT: 0);
hker[drv] = 0; /* upd drv status */ hker[drv] = 0; /* upd drv status */
hk_off[drv] = 0; hk_off[drv] = 0;
hk_dif[drv] = 0; hk_dif[drv] = 0;
uptr->CYL = 0; uptr->CYL = 0;
update_hkcs (CS1_DI, drv); /* upd ctlr status */ if ((old_hkds & DS_ATA) == 0) /* ATN transition? */
update_hkcs (CS1_DI, drv); /* upd ctlr status */
p = sim_fsize (uptr->fileref); /* get file size */ p = sim_fsize (uptr->fileref); /* get file size */
if (p == 0) { /* new disk image? */ if (p == 0) { /* new disk image? */
@ -1483,18 +1531,21 @@ return SCPE_OK;
t_stat hk_detach (UNIT *uptr) t_stat hk_detach (UNIT *uptr)
{ {
uint32 drv; uint32 drv;
int32 old_hkds;
if (!(uptr->flags & UNIT_ATT)) /* attached? */ if (!(uptr->flags & UNIT_ATT)) /* attached? */
return SCPE_OK; return SCPE_OK;
drv = (uint32) (uptr - hk_dev.units); /* get drv number */ drv = (uint32) (uptr - hk_dev.units); /* get drv number */
hkds[drv] = (hkds[drv] & ~(DS_RDY | DS_WRL | DS_VV | DS_OF)) | DS_ATA; old_hkds = hkds[drv];
hkds[drv] = (hkds[drv] & ~(DS_RDY | DS_WRL | DS_VV | DS_OF | DS_PIP)) | DS_ATA;
if (sim_is_active (uptr)) { /* unit active? */ if (sim_is_active (uptr)) { /* unit active? */
sim_cancel (uptr); /* cancel operation */ sim_cancel (uptr); /* cancel operation */
hker[drv] = hker[drv] | ER_OPI; /* set drive error */ hker[drv] = hker[drv] | ER_OPI; /* set drive error */
if ((uptr->FNC & FNC_2ND) == 0) /* expecting done? */ if ((uptr->FNC & FNC_2ND) == 0) /* expecting done? */
update_hkcs (CS1_DONE, drv); /* set done */ update_hkcs (CS1_ERR|CS1_DONE, drv); /* set done */
} }
update_hkcs (CS1_DI, drv); /* request intr */ if ((old_hkds & DS_ATA) == 0) /* ATN transition? */
update_hkcs (CS1_DI, drv); /* upd ctlr status */
return detach_unit (uptr); return detach_unit (uptr);
} }