/* sigma_dp.c: moving head disk pack controller | |
Copyright (c) 2008-2017, 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. | |
dp moving head disk pack controller | |
13-Mar-17 RMS Fixed bug in selecting 3281 unit F (COVERITY) | |
Transfers are always done a sector at a time. | |
This module simulates five Sigma controller/disk pack pairs (7240, 7270; | |
7260, 7265, 7275) and one Telefile controller that supported different | |
disk models on the same controller (T3281/3282/3283/3286/3288). Due to | |
ambiguities in the documentation, the T3286 disk is not implemented. | |
Broadly speaking, the controllers fall into two families: the 7240/7270, | |
which returned 10 bytes for sense status; and the others, which returned | |
16 bytes. Each disk has two units: one for timing channel operations, and | |
one for timing asynchronous seek completions. The controller will not | |
start a new operation is it is busy (any of the main units active) or if | |
the target device is busy (its seek unit is active). | |
*/ | |
#include "sigma_io_defs.h" | |
#include <math.h> | |
#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ | |
#define UNIT_HWLK (1u << UNIT_V_HWLK) | |
#define UNIT_WPRT (UNIT_HWLK|UNIT_RO) /* write prot */ | |
#define UNIT_V_AUTO (UNIT_V_UF + 1) /* autosize */ | |
#define UNIT_AUTO (1u << UNIT_V_AUTO) | |
#define UNIT_V_DTYPE (UNIT_V_UF + 2) /* drive type */ | |
#define UNIT_M_DTYPE 0xF | |
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) | |
#define UDA u3 /* disk addr */ | |
#define UCMD u4 /* current command */ | |
#define UCTX u5 /* ctrl/ctx index */ | |
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) | |
/* Constants */ | |
#define DP_NUMCTL 2 /* number of controllers */ | |
#define DP_CTYPE 6 /* total ctrl types */ | |
#define DP_C7240 0 /* 7240 ctrl */ | |
#define DP_C7270 1 /* 7270 ctrl */ | |
#define DP_C7260 2 /* 7260 ctrl */ | |
#define DP_C7265 3 /* 7265 ctrl */ | |
#define DP_C7275 4 /* 7275 ctrl */ | |
#define DP_C3281 5 /* T3281 ctrl */ | |
#define DP_Q10B(x) ((x) <= DP_C7270) /* 10B or 16B? */ | |
#define DP_NUMDR_10B 8 /* drives/ctrl */ | |
#define DP_NUMDR_16B 15 | |
#define DP_CONT 0xF /* ctrl's drive # */ | |
#define DP_WDSC 256 /* words/sector */ | |
#define DP_BYHD 8 /* byte/header */ | |
#define DP_NUMDR ((uint32) ((DP_Q10B (ctx->dp_ctype))? DP_NUMDR_10B: DP_NUMDR_16B)) | |
#define DP_SEEK (DP_CONT) /* offset to seek units */ | |
/* Address bytes */ | |
#define DPA_V_CY 16 /* cylinder offset */ | |
#define DPA_M_CY 0x3FF | |
#define DPA_V_HD 8 /* head offset */ | |
#define DPA_M_HD 0x1F | |
#define DPA_V_SC 0 /* sector offset */ | |
#define DPA_M_SC 0x1F | |
#define DPA_GETCY(x) (((x) >> DPA_V_CY) & DPA_M_CY) | |
#define DPA_GETHD(x) (((x) >> DPA_V_HD) & DPA_M_HD) | |
#define DPA_GETSC(x) (((x) >> DPA_V_SC) & DPA_M_SC) | |
/* Sense order */ | |
#define DPS_NBY_10B 10 | |
#define DPS_NBY_16B 16 | |
#define DPS_NBY ((uint32) (DP_Q10B (ctx->dp_ctype)? DPS_NBY_10B: DPS_NBY_16B)) | |
/* Test mode */ | |
#define DPT_NBY_10B 1 /* bytes/test mode spec */ | |
#define DPT_NBY_16B 2 | |
#define DPT_NBY ((uint32) (DP_Q10B (ctx->dp_ctype)? DPT_NBY_10B: DPT_NBY_16B)) | |
/* Commands */ | |
#define DPS_INIT 0x100 | |
#define DPS_END 0x101 | |
#define DPS_WRITE 0x01 | |
#define DPS_READ 0x02 | |
#define DPS_SEEK 0x03 | |
#define DPS_SEEKI 0x83 | |
#define DPS_SENSE 0x04 | |
#define DPS_CHECK 0x05 | |
#define DPS_RSRV 0x07 | |
#define DPS_WHDR 0x09 | |
#define DPS_RHDR 0x0A | |
#define DPS_CRIOF 0x0F | |
#define DPS_RDEES 0x12 | |
#define DPS_TEST 0x13 | |
#define DPS_RLS 0x17 | |
#define DPS_CRION 0x1F | |
#define DPS_RLSA 0x23 | |
#define DPS_RECAL 0x33 | |
#define DPS_RECALI 0xB3 | |
/* Seek completion states */ | |
#define DSC_SEEK 0x00 /* seeking */ | |
#define DSC_SEEKI 0x80 /* seeking, then int */ | |
#define DSC_SEEKW 0x01 /* waiting to int */ | |
/* Device status - note that these are device independent */ | |
#define DPF_V_WCHK 0 | |
#define DPF_V_DPE 1 | |
#define DPF_V_SNZ 2 | |
#define DPF_V_EOC 3 | |
#define DPF_V_IVA 4 | |
#define DPF_V_PGE 5 | |
#define DPF_V_WPE 6 | |
#define DPF_V_AIM 7 | |
#define DPF_WCHK (1u << DPF_V_WCHK) /* wrt chk error */ | |
#define DPF_DPE (1u << DPF_V_DPE) /* data error */ | |
#define DPF_SNZ (1u << DPF_V_SNZ) /* sec# != 0 @ whdr */ | |
#define DPF_EOC (1u << DPF_V_EOC) /* end cylinder */ | |
#define DPF_IVA (1u << DPF_V_IVA) /* invalid addr */ | |
#define DPF_PGE (1u << DPF_V_PGE) /* prog error */ | |
#define DPF_WPE (1u << DPF_V_WPE) /* wrt prot err */ | |
#define DPF_AIM (1u << DPF_V_AIM) /* arm in motion */ | |
#define DPF_V_DIFF 16 | |
#define DPF_M_DIFF 0xFFFFu | |
#define DPF_DIFF (DPF_M_DIFF << DPF_V_DIFF) | |
/* Drive types */ | |
/* These controllers support many different disk drive types: | |
type #sectors/ #surfaces/ #cylinders/ | |
surface cylinder drive | |
7242 6 20 203 =24MB | |
7261 11 20 203 =45MB | |
7271 6 20 406 =48MB | |
3288 17 5 822 =67MB | |
7276 11 19 411 =86MB | |
7266 11 20 411 =90MB | |
3282 11 19 815 =170MB | |
3283 17 19 815 =263MB | |
On the T3281, each drive can be a different type. The size field | |
in each unit selects the drive capacity for each drive and thus | |
the drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. | |
*/ | |
#define DP_SZ(x) ((DPCY_##x) * (DPHD_##x) * (DPSC_##x) * DP_WDSC) | |
#define DP_ENT(x,y) (DP_##x), (DPCY_##x), (DPHD_##x), (DPSC_##x), (DP_C##y), (DPSZ_##x), (DPID_##x) | |
#define DP_7242 0 | |
#define DPCY_7242 203 | |
#define DPHD_7242 20 | |
#define DPSC_7242 6 | |
#define DPID_7242 0 | |
#define DPSZ_7242 DP_SZ(7242) | |
#define DP_7261 1 | |
#define DPCY_7261 203 | |
#define DPHD_7261 20 | |
#define DPSC_7261 11 | |
#define DPID_7261 (5u << 5) | |
#define DPSZ_7261 DP_SZ(7261) | |
#define DP_7271 2 | |
#define DPCY_7271 406 | |
#define DPHD_7271 20 | |
#define DPSC_7271 6 | |
#define DPID_7271 0 | |
#define DPSZ_7271 DP_SZ(7271) | |
#define DP_3288 3 | |
#define DPCY_3288 822 | |
#define DPHD_3288 5 | |
#define DPSC_3288 17 | |
#define DPID_3288 0 | |
#define DPSZ_3288 DP_SZ(3288) | |
#define DP_7276 4 | |
#define DPCY_7276 411 | |
#define DPHD_7276 19 | |
#define DPSC_7276 11 | |
#define DPID_7276 (7 << 5) | |
#define DPSZ_7276 DP_SZ(7276) | |
#define DP_7266 5 | |
#define DPCY_7266 411 | |
#define DPHD_7266 20 | |
#define DPSC_7266 11 | |
#define DPID_7266 (6 << 5) | |
#define DPSZ_7266 DP_SZ(7276) | |
#define DP_3282 6 | |
#define DPCY_3282 815 | |
#define DPHD_3282 19 | |
#define DPSC_3282 11 | |
#define DPID_3282 0 | |
#define DPSZ_3282 DP_SZ(3282) | |
#define DP_3283 7 | |
#define DPCY_3283 815 | |
#define DPHD_3283 19 | |
#define DPSC_3283 17 | |
#define DPID_3283 0 | |
#define DPSZ_3283 DP_SZ(3283) | |
#define GET_PSC(x,s) ((int32) fmod (sim_gtime() / ((double) (x * DP_WDSC)), \ | |
((double) (s)))) | |
typedef struct { | |
uint32 dp_ctype; /* controller type */ | |
uint32 dp_time; /* inter-word time */ | |
uint32 dp_stime; /* inter-track time */ | |
uint32 dp_flags; /* status flags */ | |
uint32 dp_ski; /* seek interrupts */ | |
uint32 dp_stopioe; /* stop on I/O error */ | |
uint32 dp_test; /* test mode */ | |
} DP_CTX; | |
typedef struct { | |
uint32 dtype; /* drive type */ | |
uint32 cy; /* cylinders */ | |
uint32 hd; /* heads */ | |
uint32 sc; /* sectors */ | |
uint32 ctype; /* controller */ | |
uint32 capac; /* capacity */ | |
uint32 id; /* ID */ | |
} DP_TYPE; | |
typedef struct { | |
uint32 byte; /* offset in array */ | |
uint32 mask; /* test mask */ | |
uint32 fpos; /* from position */ | |
uint32 tpos; /* to position */ | |
} DP_SNSTAB; | |
static char *dp_cname[] = { | |
"7240", "7270", "7260", "7275", "7265", "T3281" | |
}; | |
static uint32 dp_buf[DP_WDSC]; | |
extern uint32 chan_ctl_time; | |
uint32 dpa_disp (uint32 op, uint32 dva, uint32 *dvst); | |
uint32 dpb_disp (uint32 op, uint32 dva, uint32 *dvst); | |
uint32 dp_disp (uint32 cidx, uint32 op, uint32 dva, uint32 *dvst); | |
uint32 dp_tio_status (uint32 cidx, uint32 un); | |
uint32 dp_tdv_status (uint32 cidx, uint32 un); | |
uint32 dp_aio_status (uint32 cidx, uint32 un); | |
void dp_set_sense (UNIT *uptr, uint32 *c); | |
t_stat dp_chan_err (uint32 dva, uint32 st); | |
t_stat dp_svc (UNIT *uptr); | |
t_stat dps_svc (UNIT *uptr); | |
t_stat dp_reset (DEVICE *dptr); | |
t_bool dp_inv_ad (UNIT *uptr, uint32 *da); | |
t_bool dp_inc_ad (UNIT *uptr); | |
t_stat dp_read (UNIT *uptr, uint32 da); | |
t_stat dp_write (UNIT *uptr, uint32 da); | |
t_stat dp_ioerr (UNIT *uptr); | |
t_bool dp_test_mode (uint32 cidx); | |
t_bool dp_end_sec (UNIT *uptr, uint32 lnt, uint32 exp, uint32 st); | |
int32 dp_clr_int (uint32 cidx); | |
void dp_set_ski (uint32 cidx, uint32 un); | |
void dp_clr_ski (uint32 cidx, uint32 un); | |
t_stat dp_attach (UNIT *uptr, CONST char *cptr); | |
t_stat dp_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat dp_set_auto (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat dp_set_ctl (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat dp_show_ctl (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
static DP_TYPE dp_tab[] = { | |
{ DP_ENT (7242, 7240) }, | |
{ DP_ENT (7261, 7260) }, | |
{ DP_ENT (7271, 7270) }, | |
{ DP_ENT (3288, 3281) }, | |
{ DP_ENT (7276, 7275) }, | |
{ DP_ENT (7266, 7265) }, | |
{ DP_ENT (3282, 3281) }, | |
{ DP_ENT (3283, 3281) }, | |
{ 0, 0, 0, 0, 0, 0 }, | |
}; | |
static DP_SNSTAB dp_sense_10B[] = { | |
{ 7, 0x00FF0000, 16, 0 }, | |
{ 8, DPF_WCHK, DPF_V_WCHK, 6 }, | |
{ 8, DPF_SNZ, DPF_V_SNZ, 2 }, | |
{ 9, 0x01000000, 24, 0 }, | |
{ 0, 0, 0, 0 } | |
}; | |
static DP_SNSTAB dp_sense_16B[] = { | |
{ 8, DPF_WCHK, DPF_V_WCHK, 7 }, | |
{ 8, DPF_EOC, DPF_V_EOC, 3}, | |
{ 8, DPF_AIM, DPF_V_AIM, 2}, | |
{ 14, 0xFF000000, 24, 0 }, | |
{ 15, 0x00FF0000, 16, 0 }, | |
{ 0, 0, 0, 0 } | |
}; | |
/* Command table, indexed by command */ | |
#define C_10B (1u << DP_C7240) | (1u << DP_C7270) | |
#define C_16B (1u << DP_C7260) | (1u << DP_C7275) | \ | |
(1u << DP_C7265) | (1u << DP_C3281) | |
#define C_A (C_10B|C_16B) /* all */ | |
#define C_F (1u << (DP_CTYPE)) /* fast */ | |
#define C_C (1u << (DP_CTYPE + 1)) /* ctrl cmd */ | |
static uint16 dp_cmd[256] = { | |
0, C_A, C_A, C_A, C_A|C_F, C_A, 0, C_16B|C_F, | |
0, C_A, C_A, 0, 0, 0, 0, C_16B|C_F|C_C, | |
0, 0, C_A, C_A|C_F, 0, 0, 0, C_16B|C_F, | |
0, 0, 0, 0, 0, 0, 0, C_16B|C_F|C_C, | |
0, 0, 0, C_10B|C_F, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, C_A, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, C_A, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, C_16B, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0 | |
}; | |
/* DP data structures | |
dp_dev DP device descriptor | |
dp_unit DP unit descriptor | |
dp_reg DP register list | |
*/ | |
dib_t dp_dib[] = { | |
{ DVA_DPA, &dpa_disp }, | |
{ DVA_DPB, &dpb_disp } | |
}; | |
DP_CTX dp_ctx[] = { | |
{ DP_C7270, 1, 20 }, | |
{ DP_C7275, 1, 20 } | |
}; | |
UNIT dpa_unit[] = { | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DIS, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DIS, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DIS, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DIS, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DIS, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DIS, DPSZ_7271) }, | |
{ UDATA (&dp_svc, (DP_7271 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DIS, DPSZ_7271) }, | |
{ UDATA (&dp_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
}; | |
UNIT dpb_unit[] = { | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS, DPSZ_7276) }, | |
{ UDATA (&dp_svc, (DP_7276 << UNIT_V_DTYPE)+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS, DPSZ_7276) }, | |
{ UDATA (&dp_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dps_svc, UNIT_DIS, 0) }, | |
}; | |
REG dpa_reg[] = { | |
{ HRDATA (CTYPE, dp_ctx[0].dp_ctype, 1), REG_HRO }, | |
{ HRDATA (FLAGS, dp_ctx[0].dp_flags, 8) }, | |
{ GRDATA (DIFF, dp_ctx[0].dp_flags, 16, 16, 16) }, | |
{ HRDATA (SKI, dp_ctx[0].dp_ski, 16) }, | |
{ HRDATA (TEST, dp_ctx[0].dp_test, 16) }, | |
{ URDATA (ADDR, dpa_unit[0].UDA, 16, 32, 0, DP_NUMDR_16B, 0) }, | |
{ URDATA (CMD, dpa_unit[0].UCMD, 16, 10, 0, DP_NUMDR_16B, 0) }, | |
{ DRDATA (TIME, dp_ctx[0].dp_time, 24), PV_LEFT+REG_NZ }, | |
{ DRDATA (STIME, dp_ctx[0].dp_stime, 24), PV_LEFT+REG_NZ }, | |
{ FLDATA (STOP_IOE, dp_ctx[0].dp_stopioe, 0) }, | |
{ HRDATA (DEVNO, dp_dib[0].dva, 12), REG_HRO }, | |
{ NULL } | |
}; | |
REG dpb_reg[] = { | |
{ HRDATA (CTYPE, dp_ctx[1].dp_ctype, 1), REG_HRO }, | |
{ HRDATA (FLAGS, dp_ctx[1].dp_flags, 8) }, | |
{ GRDATA (DIFF, dp_ctx[1].dp_flags, 16, 16, 16) }, | |
{ HRDATA (SKI, dp_ctx[1].dp_ski, 16) }, | |
{ HRDATA (TEST, dp_ctx[1].dp_test, 16) }, | |
{ URDATA (ADDR, dpa_unit[1].UDA, 16, 32, 0, DP_NUMDR_16B, 0) }, | |
{ URDATA (CMD, dpa_unit[1].UCMD, 16, 10, 0, DP_NUMDR_16B, 0) }, | |
{ DRDATA (TIME, dp_ctx[1].dp_time, 24), PV_LEFT+REG_NZ }, | |
{ DRDATA (STIME, dp_ctx[1].dp_stime, 24), PV_LEFT+REG_NZ }, | |
{ FLDATA (STOP_IOE, dp_ctx[1].dp_stopioe, 0) }, | |
{ HRDATA (DEVNO, dp_dib[1].dva, 12), REG_HRO }, | |
{ NULL } | |
}; | |
MTAB dp_mod[] = { | |
{ MTAB_XTD|MTAB_VDV, DP_C7240, NULL, "7240", &dp_set_ctl, NULL, NULL }, | |
{ MTAB_XTD|MTAB_VDV, DP_C7270, NULL, "7270", &dp_set_ctl, NULL, NULL }, | |
{ MTAB_XTD|MTAB_VDV, DP_C7260, NULL, "7260", &dp_set_ctl, NULL, NULL }, | |
{ MTAB_XTD|MTAB_VDV, DP_C7275, NULL, "7275", &dp_set_ctl, NULL, NULL }, | |
{ MTAB_XTD|MTAB_VDV, DP_C7265, NULL, "7265", &dp_set_ctl, NULL, NULL }, | |
{ MTAB_XTD|MTAB_VDV, DP_C3281, NULL, "T3281", &dp_set_ctl, NULL, NULL }, | |
{ MTAB_XTD|MTAB_VDV, 0, "controller", NULL, NULL, &dp_show_ctl }, | |
{ UNIT_DTYPE, (DP_7242 << UNIT_V_DTYPE), "7242", NULL, NULL }, | |
{ UNIT_DTYPE, (DP_7261 << UNIT_V_DTYPE), "7261", NULL, NULL }, | |
{ UNIT_DTYPE, (DP_7271 << UNIT_V_DTYPE), "7271", NULL, NULL }, | |
{ UNIT_DTYPE, (DP_7276 << UNIT_V_DTYPE), "7276", NULL, NULL }, | |
{ UNIT_DTYPE, (DP_7266 << UNIT_V_DTYPE), "7266", NULL, NULL }, | |
{ (UNIT_DTYPE+UNIT_ATT), (DP_3288 << UNIT_V_DTYPE) + UNIT_ATT, | |
"3288", NULL, NULL }, | |
{ (UNIT_DTYPE+UNIT_ATT), (DP_3282 << UNIT_V_DTYPE) + UNIT_ATT, | |
"3282", NULL, NULL }, | |
{ (UNIT_DTYPE+UNIT_ATT), (DP_3282 << UNIT_V_DTYPE) + UNIT_ATT, | |
"3282", NULL, NULL }, | |
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (DP_3288 << UNIT_V_DTYPE), | |
"3288", NULL, NULL }, | |
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (DP_3282 << UNIT_V_DTYPE), | |
"3282", NULL, NULL }, | |
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (DP_3283 << UNIT_V_DTYPE), | |
"3283", NULL, NULL }, | |
{ (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, | |
{ UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", &dp_set_auto, NULL }, | |
{ (UNIT_AUTO+UNIT_DTYPE), (DP_3288 << UNIT_V_DTYPE), | |
NULL, "3288", &dp_set_size }, | |
{ (UNIT_AUTO+UNIT_DTYPE), (DP_3282 << UNIT_V_DTYPE), | |
NULL, "3282", &dp_set_size }, | |
{ (UNIT_AUTO+UNIT_DTYPE), (DP_3283 << UNIT_V_DTYPE), | |
NULL, "3283", &dp_set_size }, | |
{ UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL }, | |
{ UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", 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 dp_dev[] = { | |
{ | |
"DPA", dpa_unit, dpa_reg, dp_mod, | |
(2 * DP_NUMDR_16B) + 1, 16, 28, 1, 16, 32, | |
NULL, NULL, &dp_reset, | |
&io_boot, &dp_attach, NULL, | |
&dp_dib[0], DEV_DISABLE | |
}, | |
{ | |
"DPB", dpb_unit, dpb_reg, dp_mod, | |
(2 * DP_NUMDR_16B) + 1, 16, 28, 1, 16, 32, | |
NULL, NULL, &dp_reset, | |
&io_boot, &dp_attach, NULL, | |
&dp_dib[1], DEV_DISABLE | |
} | |
}; | |
/* DP: IO dispatch routine */ | |
uint32 dpa_disp (uint32 op, uint32 dva, uint32 *dvst) | |
{ | |
return dp_disp (0, op, dva, dvst); | |
} | |
uint32 dpb_disp (uint32 op, uint32 dva, uint32 *dvst) | |
{ | |
return dp_disp (1, op, dva, dvst); | |
} | |
uint32 dp_disp (uint32 cidx, uint32 op, uint32 dva, uint32 *dvst) | |
{ | |
uint32 un = DVA_GETUNIT (dva); | |
UNIT *dp_unit = dp_dev[cidx].units; | |
UNIT *uptr; | |
int32 iu; | |
uint32 i; | |
DP_CTX *ctx; | |
if (cidx >= DP_NUMCTL) /* inv ctrl num? */ | |
return DVT_NODEV; | |
ctx = &dp_ctx[cidx]; | |
if (((un < DP_NUMDR) && /* un valid and */ | |
((dp_unit[un].flags & UNIT_DIS) == 0)) || /* not disabled OR */ | |
((un == 0xF) && (ctx->dp_ctype == DP_C3281))) /* 3281 unit F? */ | |
uptr = dp_unit + un; /* un exists */ | |
else return DVT_NODEV; | |
switch (op) { /* case on op */ | |
case OP_SIO: /* start I/O */ | |
*dvst = dp_tio_status (cidx, un); /* get status */ | |
if ((*dvst & (DVS_CST|DVS_DST)) == 0) { /* ctrl + dev idle? */ | |
uptr->UCMD = DPS_INIT; /* start dev thread */ | |
sim_activate (uptr, chan_ctl_time); | |
} | |
break; | |
case OP_TIO: /* test status */ | |
*dvst = dp_tio_status (cidx, un); /* return status */ | |
break; | |
case OP_TDV: /* test status */ | |
*dvst = dp_tdv_status (cidx, un); /* return status */ | |
break; | |
case OP_HIO: /* halt I/O */ | |
*dvst = dp_tio_status (cidx, un); /* return status */ | |
if (un != 0xF) { /* not controller */ | |
if ((int32) un == chan_chk_chi (dva)) /* halt active ctlr int? */ | |
chan_clr_chi (dva); /* clear ctlr int */ | |
if (sim_is_active (uptr)) { /* chan active? */ | |
sim_cancel (uptr); /* stop unit */ | |
chan_uen (dva); /* uend */ | |
} | |
dp_clr_ski (cidx, un); /* clear seek int */ | |
sim_cancel (uptr + DP_SEEK); /* cancel seek compl */ | |
} | |
else { | |
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 */ | |
} | |
dp_clr_ski (cidx, i); /* clear seek int */ | |
sim_cancel (&dp_unit[i] + DP_SEEK); /* cancel seek compl */ | |
} | |
chan_clr_chi (dva); /* clear chan int */ | |
} | |
break; | |
case OP_AIO: /* acknowledge int */ | |
iu = dp_clr_int (cidx); /* clear int */ | |
*dvst = dp_aio_status (cidx, iu) | /* get status */ | |
(iu << DVT_V_UN); | |
break; | |
default: | |
*dvst = 0; | |
return SCPE_IERR; | |
} | |
return 0; | |
} | |
/* Unit service */ | |
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; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
int32 t, dc; | |
uint32 st, cmd, sc; | |
t_stat r; | |
if (uptr->UCMD == DPS_INIT) { /* init state? */ | |
st = chan_get_cmd (dva, &cmd); /* get command */ | |
if (CHS_IFERR (st)) /* channel error? */ | |
return dp_chan_err (dva, st); | |
ctx->dp_flags = 0; /* clear status */ | |
if (!(dp_cmd[cmd] & (1u << ctx->dp_ctype))) { /* cmd valid for dev? */ | |
ctx->dp_flags |= DPF_PGE; | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
if ((un == 0xF) && /* to controller? */ | |
!(dp_cmd[cmd] & C_C)) { /* not valid? */ | |
ctx->dp_flags |= DPF_PGE; | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
uptr->UCMD = cmd; /* save state */ | |
if (dp_cmd[cmd] & C_F) /* fast command? */ | |
sim_activate_abs (uptr, chan_ctl_time); /* schedule soon */ | |
else { /* data transfer */ | |
sc = DPA_GETSC (uptr->UDA); /* new sector */ | |
t = sc - GET_PSC (ctx->dp_time, dp_tab[dtype].sc); /* delta to new */ | |
if (t < 0) /* wrap around? */ | |
t = t + dp_tab[dtype].sc; | |
sim_activate_abs (uptr, t * ctx->dp_time * DP_WDSC); /* schedule op */ | |
} | |
sim_cancel (uptr + DP_SEEK); /* cancel rest of seek */ | |
return SCPE_OK; | |
} | |
else if (uptr->UCMD == DPS_END) { /* end state? */ | |
st = chan_end (dva); /* set channel end */ | |
if (CHS_IFERR (st)) /* channel error? */ | |
return dp_chan_err (dva, st); | |
if (st == CHS_CCH) { /* command chain? */ | |
uptr->UCMD = DPS_INIT; /* restart thread */ | |
sim_activate (uptr, chan_ctl_time); | |
} | |
return SCPE_OK; /* done */ | |
} | |
da = 0; | |
dc = 0; | |
switch (uptr->UCMD) { | |
case DPS_SEEK: /* seek */ | |
case DPS_SEEKI: | |
for (i = 0; i < 4; i++) | |
c[i] = 0; | |
for (i = 0, st = 0; (i < 4) && (st != CHS_ZBC); i++) { | |
st = chan_RdMemB (dva, &c[i]); /* get byte */ | |
if (CHS_IFERR (st)) /* channel error? */ | |
return dp_chan_err (dva, st); | |
} | |
da = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; | |
if (c[0] & 0xFC) /* hi 6b non-zero? */ | |
ctx->dp_flags |= DPF_PGE; /* set prog err */ | |
if (((i != 4) || (st != CHS_ZBC)) && /* length error? */ | |
chan_set_chf (dva, CHF_LNTE)) { /* care? */ | |
ctx->dp_flags |= DPF_PGE; /* set prog err */ | |
return SCPE_OK; | |
} | |
if (i < 4) { /* at least 4? */ | |
chan_uen (dva); | |
return SCPE_OK; | |
} | |
dc = DPA_GETCY (da); /* desired cyl */ | |
case DPS_RECAL: | |
case DPS_RECALI: | |
t = abs (DPA_GETCY (uptr->UDA) - dc); /* get cyl diff */ | |
ctx->dp_flags = (ctx->dp_flags & ~DPF_DIFF) | | |
((t & DPF_M_DIFF) << DPF_V_DIFF); /* save difference */ | |
if (t == 0) | |
t = 1; | |
uptr->UDA = da; /* save addr */ | |
sim_activate (uptr + DP_SEEK, t * ctx->dp_stime); | |
dp_unit[un + DP_SEEK].UCMD = /* sched seek */ | |
(chan_tst_cmf (dva, CMF_CCH)? DSC_SEEK: uptr->UCMD & 0x80); | |
break; /* sched end */ | |
case DPS_SENSE: /* sense */ | |
for (i = 0; i < DPS_NBY_16B; i++) | |
c[i] = 0; | |
c[0] = (uptr->UDA >> 24) & 0xFF; /* 0-3 = disk addr */ | |
c[1] = (uptr->UDA >> 16) & 0xFF; | |
c[2] = (uptr->UDA >> 8) & 0xFF; | |
c[3] = uptr->UDA & 0xFF; | |
c[4] = GET_PSC (ctx->dp_time, dp_tab[dtype].sc) | /* curr sector */ | |
((sim_is_active (uptr) && ((uptr->UCMD & 0x7F) == DPS_SEEK))? 0x80: 0); | |
if (!DP_Q10B (ctx->dp_ctype)) { /* 16B only */ | |
c[5] = un | dp_tab[dtype].id; /* unit # + id */ | |
if (ctx->dp_ctype == DP_C3281) /* 3281 only */ | |
c[7] = un; /* unique id */ | |
c[10] = (ctx->dp_ski >> 8) & 0xFF; /* seek intr */ | |
c[11] = ctx->dp_ski & 0xFF; | |
} | |
dp_set_sense (uptr, &c[0]); /* other bytes */ | |
for (i = 0, st = 0; (i < DPS_NBY) && (st != CHS_ZBC); i++) { | |
st = chan_WrMemB (dva, c[i]); /* store char */ | |
if (CHS_IFERR (st)) /* channel error? */ | |
return dp_chan_err (dva, st); | |
} | |
if ((i != DPS_NBY) || (st != CHS_ZBC)) { /* length error? */ | |
ctx->dp_flags |= DPF_PGE; /* set prog err */ | |
if (chan_set_chf (dva, CHF_LNTE)) /* do we care? */ | |
return SCPE_OK; | |
} | |
break; | |
case DPS_WRITE: /* write */ | |
if (uptr->flags & UNIT_RO) { /* write locked? */ | |
ctx->dp_flags |= DPF_WPE; | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
if (dp_inv_ad (uptr, &da)) { /* invalid addr? */ | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
for (i = 0, st = 0; i < DP_WDSC; i++) { /* sector loop */ | |
if (st != CHS_ZBC) { /* chan not done? */ | |
st = chan_RdMemW (dva, &wd); /* read word */ | |
if (CHS_IFERR (st)) { /* channel error? */ | |
dp_inc_ad (uptr); /* da increments */ | |
return dp_chan_err (dva, st); | |
} | |
} | |
else wd = 0; | |
dp_buf[i] = wd; /* store in buffer */ | |
} | |
if ((r = dp_write (uptr, da))) /* write buf, err? */ | |
return r; | |
if (dp_end_sec (uptr, DP_WDSC, DP_WDSC, st)) /* transfer done? */ | |
return SCPE_OK; /* err or cont */ | |
break; | |
/* Write header "writes" eight bytes per sector and throws them in the bit bucket */ | |
case DPS_WHDR: | |
if (uptr->flags & UNIT_RO) { /* write locked? */ | |
ctx->dp_flags |= DPF_WPE; | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
if (dp_inv_ad (uptr, &da)) { /* invalid addr? */ | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
if (DPA_GETSC (uptr->UDA) != 0) { /* must start at sec 0 */ | |
ctx->dp_flags |= DPF_SNZ; | |
chan_uen (dva); | |
return SCPE_OK; | |
} | |
for (i = 0, st = 0; (i < 8) && (st != CHS_ZBC); i++) { /* sector loop */ | |
if (st != CHS_ZBC) { /* chan not done? */ | |
st = chan_RdMemB (dva, &wd); /* read word */ | |
if (CHS_IFERR (st)) { /* channel error? */ | |
dp_inc_ad (uptr); /* da increments */ | |
return dp_chan_err (dva, st); | |
} | |
} | |
} | |
if (dp_end_sec (uptr, i, 8, st)) /* transfer done? */ | |
return SCPE_OK; /* err or cont */ | |
/* Write check must be done by bytes to get precise miscompare */ | |
case DPS_CHECK: /* write check */ | |
if (dp_inv_ad (uptr, &da)) { /* invalid addr? */ | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
if ((r = dp_read (uptr, da))) /* read buf, error? */ | |
return r; | |
for (i = 0, st = 0; (i < (DP_WDSC * 4)) && (st != CHS_ZBC); i++) { | |
st = chan_RdMemB (dva, &wd); /* read byte */ | |
if (CHS_IFERR (st)) { /* channel error? */ | |
dp_inc_ad (uptr); /* da increments */ | |
return dp_chan_err (dva, st); | |
} | |
wd1 = (dp_buf[i >> 2] >> (24 - ((i & 0x3) * 8))) & 0xFF; /* byte */ | |
if (wd != wd1) { /* check error? */ | |
dp_inc_ad (uptr); /* da increments */ | |
ctx->dp_flags |= DPF_WCHK; /* set status */ | |
chan_uen (dva); /* force uend */ | |
return SCPE_OK; | |
} | |
} | |
if (dp_end_sec (uptr, i, DP_WDSC * 4, st)) /* transfer done? */ | |
return SCPE_OK; /* err or cont */ | |
break; | |
case DPS_READ: /* read */ | |
if (dp_inv_ad (uptr, &da)) { /* invalid addr? */ | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
if ((r = dp_read (uptr, da))) /* read buf, error? */ | |
return r; | |
for (i = 0, st = 0; (i < DP_WDSC) && (st != CHS_ZBC); i++) { | |
st = chan_WrMemW (dva, dp_buf[i]); /* store in mem */ | |
if (CHS_IFERR (st)) { /* channel error? */ | |
dp_inc_ad (uptr); /* da increments */ | |
return dp_chan_err (dva, st); | |
} | |
} | |
if (dp_end_sec (uptr, i, DP_WDSC, st)) /* transfer done? */ | |
return SCPE_OK; /* err or cont */ | |
break; | |
/* Read header reads 8 bytes per sector */ | |
case DPS_RHDR: /* read header */ | |
if (dp_inv_ad (uptr, &da)) { /* invalid addr? */ | |
chan_uen (dva); /* uend */ | |
return SCPE_OK; | |
} | |
c[0] = c[5] = c[6] = c[7] = 0; | |
wd = DPA_GETCY (uptr->UDA); | |
c[1] = (wd >> 8) & 0xFF; | |
c[2] = wd & 0xFF; | |
c[3] = DPA_GETHD (uptr->UDA); | |
c[4] = DPA_GETSC (uptr->UDA); | |
for (i = 0, st = 0; (i < 8) && (st != CHS_ZBC); i++) { | |
st = chan_WrMemB (dva, c[i]); /* store in mem */ | |
if (CHS_IFERR (st)) { /* channel error? */ | |
dp_inc_ad (uptr); /* da increments */ | |
return dp_chan_err (dva, st); | |
} | |
} | |
if (dp_end_sec (uptr, i, 8, st)) /* transfer done? */ | |
return SCPE_OK; /* err or cont */ | |
break; | |
/* Test mode is not really implemented */ | |
case DPS_TEST: /* test mode */ | |
if (!dp_test_mode (cidx)) /* enter test mode */ | |
return SCPE_OK; | |
break; | |
case DPS_RSRV: /* reserve */ | |
case DPS_RLS: /* release */ | |
case DPS_RLSA: /* release */ | |
break; /* nop */ | |
} | |
uptr->UCMD = DPS_END; /* op done, next state */ | |
sim_activate (uptr, chan_ctl_time); | |
return SCPE_OK; | |
} | |
/* Seek completion service */ | |
t_stat dps_svc (UNIT *uptr) | |
{ | |
uint32 cidx = uptr->UCTX; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
UNIT *dp_unit = dp_dev[cidx].units; | |
uint32 un = uptr - dp_unit - DP_SEEK; | |
uint32 dtype = GET_DTYPE (dp_unit[un].flags); | |
if (uptr->UCMD != DSC_SEEK) { /* int? */ | |
if (chan_chk_chi (dp_dib[cidx].dva) >= 0) { /* ctl int pending? */ | |
sim_activate (uptr, ctx->dp_time * dp_tab[dtype].sc); | |
uptr->UCMD = DSC_SEEKW; | |
} | |
else dp_set_ski (cidx, un); | |
} | |
return SCPE_OK; | |
} | |
/* Common read/write sector end routine | |
case 1 - more to transfer, not end cylinder - reschedule, return TRUE | |
case 2 - more to transfer, end cylinder - 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 dp_end_sec (UNIT *uptr, uint32 lnt, uint32 exp, uint32 st) | |
{ | |
uint32 cidx = uptr->UCTX; | |
uint32 dva = dp_dib[cidx].dva; | |
uint32 dtype = GET_DTYPE (uptr->flags); | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
if (st != CHS_ZBC) { /* end record? */ | |
if (dp_inc_ad (uptr)) { /* inc addr, cross cyl? */ | |
ctx->dp_flags |= (DPF_IVA | DPF_EOC); | |
chan_uen (dva); /* uend */ | |
} | |
else sim_activate (uptr, ctx->dp_time * 16); /* no, next sector */ | |
return TRUE; | |
} | |
dp_inc_ad (uptr); /* just incr addr */ | |
if (lnt != exp) { /* length error at end? */ | |
if (exp == 8) /* hdr op? */ | |
ctx->dp_flags |= DPF_PGE; /* set PGE */ | |
if (chan_set_chf (dva, CHF_LNTE)) /* do we care? */ | |
return TRUE; | |
} | |
return FALSE; /* cmd done */ | |
} | |
/* DP status routines */ | |
/* The ctrl is busy if any drive is busy. | |
The device is busy if either the main unit or the seek unit is busy */ | |
uint32 dp_tio_status (uint32 cidx, uint32 un) | |
{ | |
uint32 i; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
UNIT *dp_unit = dp_dev[cidx].units; | |
uint32 stat = DVS_AUTO; | |
for (i = 0; i < DP_NUMDR; i++) { | |
if (sim_is_active (&dp_unit[i])) { | |
stat |= (DVS_CBUSY|(CC2 << DVT_V_CC)); | |
break; | |
} | |
} | |
if (sim_is_active (&dp_unit[un]) || | |
sim_is_active (&dp_unit[un + DP_SEEK])) | |
stat |= (DVS_DBUSY|(CC2 << DVT_V_CC)); | |
return DVS_AUTO; | |
} | |
uint32 dp_tdv_status (uint32 cidx, uint32 un) | |
{ | |
uint32 st; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
UNIT *dp_unit = dp_dev[cidx].units; | |
t_bool on_cyl; | |
st = 0; | |
on_cyl = !sim_is_active (&dp_unit[un + DP_SEEK]) || | |
(dp_unit[un + DP_SEEK].UCMD == DSC_SEEKW); | |
if (DP_Q10B (dp_ctx[cidx].dp_ctype)) | |
st = ((dp_ctx[cidx].dp_flags & DPF_IVA)? 0x20: 0) | | |
(on_cyl? 0x04: 0); | |
else st = ((dp_ctx[cidx].dp_flags & DPF_PGE)? 0x20: 0) | | |
((dp_ctx[cidx].dp_flags & DPF_WPE)? 0x08: 0); | |
return st; | |
} | |
uint32 dp_aio_status (uint32 cidx, uint32 un) | |
{ | |
uint32 st; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
UNIT *dp_unit = dp_dev[cidx].units; | |
t_bool on_cyl; | |
st = 0; | |
on_cyl = !sim_is_active (&dp_unit[un + DP_SEEK]) || | |
(dp_unit[un + DP_SEEK].UCMD == DSC_SEEKW); | |
if ((DP_Q10B (dp_ctx[cidx].dp_ctype)) && on_cyl) | |
st |= 0x04; | |
if (chan_chk_chi (dp_dib[cidx].dva) < 0) | |
st |= 0x08; | |
return st; | |
} | |
/* Set sense status */ | |
void dp_set_sense (UNIT *uptr, uint32 *c) | |
{ | |
uint32 cidx = uptr->UCTX; | |
UNIT *sptr = uptr + DP_SEEK; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
uint8 data; | |
DP_SNSTAB *tptr; | |
if (sim_is_active (sptr) && | |
(sptr->UCMD != DSC_SEEKW)) | |
ctx->dp_flags |= DPF_AIM; | |
else ctx->dp_flags &= ~DPF_AIM; | |
if (DP_Q10B (ctx->dp_ctype)) | |
tptr = dp_sense_10B; | |
else tptr = dp_sense_16B; | |
while (tptr->byte != 0) { | |
if (ctx->dp_flags & tptr->mask) { | |
data = (uint8) ((ctx->dp_flags & tptr->mask) >> tptr->fpos); | |
c[tptr->byte] |= (data << tptr->tpos); | |
} | |
} | |
return; | |
} | |
/* Validate disk address */ | |
t_bool dp_inv_ad (UNIT *uptr, uint32 *da) | |
{ | |
uint32 dtype = GET_DTYPE (uptr->flags); | |
uint32 cy = DPA_GETCY (uptr->UDA); | |
uint32 hd = DPA_GETHD (uptr->UDA); | |
uint32 sc = DPA_GETSC (uptr->UDA); | |
if ((cy >= dp_tab[dtype].cy) || | |
(hd >= dp_tab[dtype].hd) || | |
(sc >= dp_tab[dtype].sc)) | |
return TRUE; | |
if (da) /* return word addr */ | |
*da = ((((cy * dp_tab[dtype].hd) + hd) * dp_tab[dtype].sc) + sc) * DP_WDSC; | |
return FALSE; | |
} | |
/* Increment disk address */ | |
t_bool dp_inc_ad (UNIT *uptr) | |
{ | |
uint32 dtype = GET_DTYPE (uptr->flags); | |
uint32 cy = DPA_GETCY (uptr->UDA); | |
uint32 hd = DPA_GETHD (uptr->UDA); | |
uint32 sc = DPA_GETSC (uptr->UDA); | |
sc = sc + 1; /* sector++ */ | |
if (sc >= dp_tab[dtype].sc) { /* overflow? */ | |
sc = 0; /* wrap sector */ | |
hd = hd + 1; /* head++ */ | |
if (hd >= dp_tab[dtype].hd) /* overflow? */ | |
hd = 0; /* wrap heads */ | |
} | |
uptr->UDA = (cy << DPA_V_CY) | (hd << DPA_V_HD) | (sc << DPA_V_SC); | |
if ((hd == 0) && (sc == 0)) | |
return TRUE; | |
return FALSE; | |
} | |
/* Read and write sector */ | |
t_stat dp_read (UNIT *uptr, uint32 da) | |
{ | |
int32 err, awc; | |
err = fseek (uptr->fileref, da * sizeof (int32), SEEK_SET); | |
if (err == 0) { | |
awc = fxread (dp_buf, sizeof (uint32), DP_WDSC, uptr->fileref); | |
err = ferror (uptr->fileref); | |
for (; awc < DP_WDSC; awc++) /* fill buf */ | |
dp_buf[awc] = 0; | |
} | |
if (err != 0) | |
return dp_ioerr (uptr); | |
return SCPE_OK; | |
} | |
t_stat dp_write (UNIT *uptr, uint32 da) | |
{ | |
int32 err; | |
err = fseek (uptr->fileref, da * sizeof (int32), SEEK_SET); | |
if (err == 0) { | |
fxwrite (dp_buf, sizeof (uint32), DP_WDSC, uptr->fileref); | |
err = ferror (uptr->fileref); | |
} | |
if (err != 0) | |
return dp_ioerr (uptr); | |
return SCPE_OK; | |
} | |
t_stat dp_ioerr (UNIT *uptr) | |
{ | |
uint32 cidx = uptr->UCTX; | |
uint32 dva = dp_dib[cidx].dva; | |
sim_perror ("DP I/O error"); | |
clearerr (uptr->fileref); | |
dp_ctx[cidx].dp_flags |= DPF_DPE; /* set DPE flag */ | |
chan_set_chf (dva, CHF_XMDE); | |
chan_uen (dva); /* force uend */ | |
return SCPE_IOERR; | |
} | |
/* Test mode */ | |
t_bool dp_test_mode (uint32 cidx) | |
{ | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
uint32 dva = dp_dib[cidx].dva; | |
uint32 i, st, wd; | |
ctx->dp_test = 0; | |
for (i = 0, st = 0; i < DPT_NBY; i++) { /* sector loop */ | |
if (st != CHS_ZBC) { /* chan not done? */ | |
st = chan_RdMemB (dva, &wd); /* read word */ | |
if (CHS_IFERR (st)) { | |
dp_chan_err (dva, st); | |
return FALSE; | |
} | |
} | |
else wd = 0; | |
ctx->dp_test |= (wd & 0xFF) << (i * 8); | |
} | |
return TRUE; | |
} | |
/* Channel error */ | |
t_stat dp_chan_err (uint32 dva, uint32 st) | |
{ | |
chan_uen (dva); /* uend */ | |
if (st < CHS_ERR) | |
return st; | |
return SCPE_OK; | |
} | |
/* Clear controller/device interrupt */ | |
int32 dp_clr_int (uint32 cidx) | |
{ | |
int32 iu; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
if ((iu = chan_clr_chi (dp_dib[cidx].dva)) >= 0) { /* chan int? clear */ | |
if (ctx->dp_ski != 0) /* more int? */ | |
chan_set_dvi (dp_dib[cidx].dva); /* set INP */ | |
return iu; | |
} | |
for (iu = 0; iu < (int32) DP_NUMDR; iu++) { /* seek int? */ | |
if (ctx->dp_ski & (1u << iu)) { | |
dp_clr_ski (cidx, iu); /* clear */ | |
return iu; | |
} | |
} | |
return 0; | |
} | |
/* Set seek interrupt */ | |
void dp_set_ski (uint32 cidx, uint32 un) | |
{ | |
dp_ctx[cidx].dp_ski |= (1u << un); | |
chan_set_dvi (dp_dib[cidx].dva); /* set INP */ | |
return; | |
} | |
/* Clear seek interrupt */ | |
void dp_clr_ski (uint32 cidx, uint32 un) | |
{ | |
dp_ctx[cidx].dp_ski &= ~(1u << un); /* clear */ | |
if (dp_ctx[cidx].dp_ski != 0) /* more int? */ | |
chan_set_dvi (dp_dib[cidx].dva); /* set INP */ | |
else if (chan_chk_chi (dp_dib[cidx].dva) < 0) /* any int? */ | |
chan_clr_chi (dp_dib[cidx].dva); /* clr INP */ | |
return; | |
} | |
/* Reset routine */ | |
t_stat dp_reset (DEVICE *dptr) | |
{ | |
uint32 i; | |
uint32 cidx = dptr - dp_dev; | |
UNIT *dp_unit; | |
DP_CTX *ctx; | |
if (cidx >= DP_NUMCTL) | |
return SCPE_IERR; | |
dp_unit = dptr->units; | |
ctx = &dp_ctx[cidx]; | |
for (i = 0; i < DP_NUMDR_16B; i++) { | |
sim_cancel (&dp_unit[i]); /* stop dev thread */ | |
sim_cancel (&dp_unit[i + DP_SEEK]); /* stop seek thread */ | |
dp_unit[i].UDA = 0; | |
dp_unit[i].UCMD = 0; | |
dp_unit[i].UCTX = cidx; | |
} | |
ctx->dp_flags = 0; | |
ctx->dp_ski = 0; | |
ctx->dp_test = 0; | |
chan_reset_dev (dp_dib[cidx].dva); /* clr int, active */ | |
return SCPE_OK; | |
} | |
/* Device attach */ | |
t_stat dp_attach (UNIT *uptr, CONST char *cptr) | |
{ | |
uint32 i, p; | |
t_stat r; | |
uptr->capac = dp_tab[GET_DTYPE (uptr->flags)].capac; | |
r = attach_unit (uptr, cptr); /* attach unit */ | |
if (r != SCPE_OK) /* error? */ | |
return r; | |
if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ | |
return SCPE_OK; | |
p = sim_fsize (uptr->fileref); | |
if (p == 0) /* new file? */ | |
return SCPE_OK; | |
for (i = 0; dp_tab[i].sc != 0; i++) { | |
if ((dp_tab[i].ctype == DP_C3281) && /* only on 3281 */ | |
(p <= (dp_tab[i].capac * (uint32) sizeof (int32)))) { | |
uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); | |
uptr->capac = dp_tab[i].capac; | |
return SCPE_OK; | |
} | |
} | |
return SCPE_OK; | |
} | |
/* Set drive type command validation routine */ | |
t_stat dp_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
uint32 dtype = GET_DTYPE (val); | |
uint32 cidx = uptr->UCTX; | |
if (cidx >= DP_NUMCTL) /* valid ctrl idx? */ | |
return SCPE_IERR; | |
if (uptr->flags & UNIT_ATT) /* unattached? */ | |
return SCPE_ALATT; | |
if (dp_ctx[cidx].dp_ctype != DP_C3281) /* only on 3281 */ | |
return SCPE_NOFNC; | |
uptr->capac = dp_tab[dtype].capac; | |
return SCPE_OK; | |
} | |
/* Set unit autosize validation routine */ | |
t_stat dp_set_auto (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
uint32 cidx = uptr->UCTX; | |
if (cidx >= DP_NUMCTL) /* valid ctrl idx? */ | |
return SCPE_IERR; | |
if (uptr->flags & UNIT_ATT) /* unattached? */ | |
return SCPE_ALATT; | |
if (dp_ctx[cidx].dp_ctype != DP_C3281) /* only on 3281 */ | |
return SCPE_NOFNC; | |
return SCPE_OK; | |
} | |
/* Set controller type command validation routine */ | |
t_stat dp_set_ctl (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
uint32 i, new_dtyp, cidx = uptr->UCTX; | |
DP_CTX *ctx = &dp_ctx[cidx]; | |
UNIT *dp_unit = dp_dev[cidx].units; | |
if ((cidx >= DP_NUMCTL) || (val >= DP_CTYPE)) /* valid ctrl idx? */ | |
return SCPE_IERR; | |
if (val == dp_ctx[cidx].dp_ctype) /* no change? */ | |
return SCPE_OK; | |
for (i = 0; i < DP_NUMDR; i++) { /* all units detached? */ | |
if ((dp_unit[i].flags & UNIT_ATT) != 0) | |
return SCPE_ALATT; | |
} | |
for (i = new_dtyp = 0; dp_tab[i].sc != 0; i++) { /* find default capac */ | |
if (dp_tab[i].ctype == val) { | |
new_dtyp = i; | |
break; | |
} | |
} | |
if (dp_tab[new_dtyp].sc == 0) | |
return SCPE_IERR; | |
ctx->dp_ctype = val; /* new ctrl type */ | |
for (i = 0; i < DP_NUMDR_16B; i++) { | |
if (i >= DP_NUMDR) | |
dp_unit[i].flags = (dp_unit[i].flags & ~UNIT_DISABLE) | UNIT_DIS; | |
else dp_unit[i].flags = (dp_unit[i].flags | UNIT_DISABLE) & ~UNIT_DIS; | |
if (val != DP_C3281) | |
dp_unit[i].flags &= ~UNIT_AUTO; | |
dp_unit[i].flags = (dp_unit[i].flags & ~UNIT_DTYPE) | | |
(new_dtyp << UNIT_V_DTYPE); | |
dp_unit[i].capac = dp_tab[new_dtyp].capac; | |
} | |
return SCPE_OK; | |
} | |
t_stat dp_show_ctl (FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
{ | |
uint32 cidx = uptr->UCTX; | |
if (cidx >= DP_NUMCTL) /* valid ctrl idx? */ | |
return SCPE_IERR; | |
if (dp_ctx[cidx].dp_ctype >= DP_CTYPE) | |
return SCPE_IERR; | |
fprintf (st, "%s controller", dp_cname[dp_ctx[cidx].dp_ctype]); | |
return SCPE_OK; | |
} | |