First Commit of my working state
[simh.git] / PDP11 / pdp11_hk.c
1 /* pdp11_hk.c - RK611/RK06/RK07 disk controller
2
3 Copyright (c) 1993-2007, Robert M Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PUHKOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 hk RK611/RK06/RK07 disk
27
28 29-Apr-07 RMS NOP and DCLR (at least) do not check drive type
29 MR2 and MR3 only updated on NOP
30 17-Nov-05 RMS Removed unused variable
31 13-Nov-05 RMS Fixed overlapped seek interaction with NOP, DCLR, PACK
32 16-Aug-05 RMS Fixed C++ declaration and cast problems
33 07-Jul-05 RMS Removed extraneous externs
34 18-Mar-05 RMS Added attached test to detach routine
35 03-Oct-04 RMS Revised Unibus interface
36 RMS Fixed state of output ready for M+
37 26-Mar-04 RMS Fixed warnings with -std=c99
38 25-Jan-04 RMS Revised for device debug support
39 04-Jan-04 RMS Changed sim_fsize calling sequence
40 29-Dec-03 RMS Added 18b Qbus support
41 25-Apr-03 RMS Revised for extended file support
42
43 This is a somewhat abstracted implementation of the RK611, more closely
44 modelled on third party clones than DEC's own implementation. In particular,
45 the drive-to-controller serial communications system is simulated only at
46 a level equal to the Emulex SC21.
47
48 The RK611 functions only in 18b Unibus systems with I/O maps. The Emulex
49 SC02/C was a Qbus work-alike with a unique extension to 22b addressing. It
50 was only supported in Ultrix-11 and other third party software.
51
52 This module includes ideas from a previous implementation by Fred Van Kempen.
53 */
54
55 #if defined (VM_PDP10) /* PDP10 version */
56 #error "RK611 is not supported on the PDP-10!"
57
58 #elif defined (VM_VAX) /* VAX version */
59 #include "vax_defs.h"
60
61 #else /* PDP-11 version */
62 #include "pdp11_defs.h"
63 extern int32 cpu_opt;
64 #endif
65
66 extern uint16 *M;
67
68 #define HK_NUMDR 8 /* #drives */
69 #define HK_NUMCY6 411 /* cyl/drive */
70 #define HK_NUMCY7 815 /* cyl/drive */
71 #define HK_NUMSF 3 /* tracks/cyl */
72 #define HK_NUMSC 22 /* sectors/track */
73 #define HK_NUMWD 256 /* words/sector */
74 #define RK06_SIZE (HK_NUMCY6*HK_NUMSF*HK_NUMSC*HK_NUMWD)
75 #define RK07_SIZE (HK_NUMCY7*HK_NUMSF*HK_NUMSC*HK_NUMWD)
76 #define HK_SIZE(x) (((x)->flags & UNIT_DTYPE)? RK07_SIZE: RK06_SIZE)
77 #define HK_CYL(x) (((x)->flags & UNIT_DTYPE)? HK_NUMCY7: HK_NUMCY6)
78 #define HK_MAXFR (1 << 16)
79
80 /* Flags in the unit flags word */
81
82 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
83 #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */
84 #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */
85 #define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */
86 #define UNIT_WLK (1 << UNIT_V_WLK)
87 #define UNIT_DTYPE (1 << UNIT_V_DTYPE)
88 #define UNIT_RK06 (0 << UNIT_V_DTYPE)
89 #define UNIT_RK07 (1 << UNIT_V_DTYPE)
90 #define UNIT_AUTO (1 << UNIT_V_AUTO)
91 #define UNIT_DUMMY (1 << UNIT_V_DUMMY)
92 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */
93
94 /* Parameters in the unit descriptor */
95
96 #define CYL u3 /* current cylinder */
97 #define FNC u4 /* function */
98
99 /* HKCS1 - 177440 - control/status 1 */
100
101 #define CS1_GO CSR_GO /* go */
102 #define CS1_V_FNC 1 /* function pos */
103 #define CS1_M_FNC 017 /* function mask */
104 #define CS1_FNC (CS1_M_FNC << CS1_V_FNC)
105 #define FNC_NOP 000 /* no operation */
106 #define FNC_PACK 001 /* pack acknowledge */
107 #define FNC_DCLR 002 /* drive clear */
108 #define FNC_UNLOAD 003 /* unload */
109 #define FNC_START 004 /* start */
110 #define FNC_RECAL 005 /* recalibrate */
111 #define FNC_OFFSET 006 /* offset */
112 #define FNC_SEEK 007 /* seek */
113 #define FNC_XFER 010
114 #define FNC_READ 010 /* read */
115 #define FNC_WRITE 011 /* write */
116 #define FNC_WRITEH 013 /* write w/ headers */
117 #define FNC_READH 012 /* read w/ headers */
118 #define FNC_WCHK 014 /* write check */
119 #define FNC_2ND 020 /* 2nd state flag */
120 #define CS1_SPA 0000040 /* spare */
121 #define CS1_IE CSR_IE /* int enable */
122 #define CS1_DONE CSR_DONE /* ready */
123 #define CS1_V_UAE 8 /* Unibus addr ext */
124 #define CS1_M_UAE 03
125 #define CS1_UAE (CS1_M_UAE << CS1_V_UAE)
126 #define CS1_DT 0002000 /* drive type */
127 #define CS1_CTO 0004000 /* ctrl timeout NI */
128 #define CS1_FMT 0010000 /* 16b/18b NI */
129 #define CS1_PAR 0020000 /* par err NI */
130 #define CS1_DI 0040000 /* drive intr */
131 #define CS1_ERR 0100000 /* error */
132 #define CS1_CCLR 0100000 /* ctrl clear */
133 #define CS1_RW (CS1_DT|CS1_UAE|CS1_IE|CS1_SPA|CS1_FNC)
134 #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC)
135 #define GET_UAE(x) (((x) >> CS1_V_UAE) & CS1_M_UAE)
136 #define PUT_UAE(x,n) (((x) & ~ CS1_UAE) | (((n) << CS1_V_UAE) & CS1_UAE))
137
138 /* HKWC - 177442 - word count */
139
140 /* HKBA - 177444 - base address */
141
142 #define BA_MBZ 0000001 /* must be zero */
143
144 /* HKDA - 177446 - sector/track */
145
146 #define DA_V_SC 0 /* sector pos */
147 #define DA_M_SC 037 /* sector mask */
148 #define DA_V_SF 8 /* track pos */
149 #define DA_M_SF 007 /* track mask */
150 #define DA_MBZ 0174340
151 #define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC)
152 #define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF)
153
154 /* HKCS2 - 177450 - control/status 2 */
155
156 #define CS2_V_UNIT 0 /* unit pos */
157 #define CS2_M_UNIT 07 /* unit mask */
158 #define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT)
159 #define CS2_RLS 0000010 /* release NI */
160 #define CS2_UAI 0000020 /* addr inhibit */
161 #define CS2_CLR 0000040 /* controller clear */
162 #define CS2_IR 0000100 /* input ready */
163 #define CS2_OR 0000200 /* output ready */
164 #define CS2_UFE 0000400 /* unit field err NI */
165 #define CS2_MDS 0001000 /* multidrive sel NI */
166 #define CS2_PGE 0002000 /* program err */
167 #define CS2_NEM 0004000 /* nx mem err */
168 #define CS2_NED 0010000 /* nx drive err */
169 #define CS2_PE 0020000 /* parity err NI */
170 #define CS2_WCE 0040000 /* write check err */
171 #define CS2_DLT 0100000 /* data late NI */
172 #define CS2_MBZ (CS2_CLR)
173 #define CS2_RW 0000037
174 #define CS2_ERR (CS2_UFE | CS2_MDS | CS2_PGE | CS2_NEM | \
175 CS2_NED | CS2_PE | CS2_WCE | CS2_DLT )
176 #define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT)
177
178 /* HKDS - 177452 - drive status ^ = calculated dynamically */
179
180 #define DS_DRA 0000001 /* ^drive avail */
181 #define DS_OF 0000004 /* ^offset mode */
182 #define DS_ACLO 0000010 /* ^AC LO NI */
183 #define DS_SPLS 0000020 /* ^speed loss NI */
184 #define DS_DOT 0000040 /* ^off track NI */
185 #define DS_VV 0000100 /* volume valid */
186 #define DS_RDY 0000200 /* ^drive ready */
187 #define DS_DT 0000400 /* ^drive type */
188 #define DS_WRL 0004000 /* ^write locked */
189 #define DS_PIP 0020000 /* pos in progress */
190 #define DS_ATA 0040000 /* attention active */
191 #define DS_VLD 0100000 /* ^status valid */
192 #define DS_MBZ 0013002
193
194 /* HKER - 177454 - error status */
195
196 #define ER_ILF 0000001 /* illegal func */
197 #define ER_SKI 0000002 /* seek incomp */
198 #define ER_NXF 0000004 /* non-exec func */
199 #define ER_PAR 0000010 /* parity err */
200 #define ER_FER 0000020 /* format err NI */
201 #define ER_DTY 0000040 /* drive type err */
202 #define ER_ECH 0000100 /* ECC hard err NI */
203 #define ER_BSE 0000200 /* bad sector err NI */
204 #define ER_HCR 0000400 /* hdr CRC err NI */
205 #define ER_AOE 0001000 /* addr ovflo err */
206 #define ER_IAE 0002000 /* invalid addr err */
207 #define ER_WLE 0004000 /* write lock err */
208 #define ER_DTE 0010000 /* drive time err NI */
209 #define ER_OPI 0020000 /* op incomplete */
210 #define ER_UNS 0040000 /* drive unsafe */
211 #define ER_DCK 0100000 /* data check NI */
212
213 /* HKAS - 177456 - attention summary/offset */
214
215 #define AS_U0 0000400 /* unit 0 flag */
216 #define AS_OF 0000277 /* offset mask */
217
218 /* HKDC - 177460 - desired cylinder */
219
220 #define DC_V_CY 0 /* cylinder pos */
221 #define DC_M_CY 0001777 /* cylinder mask */
222 #define DC_MBZ 0176000
223 #define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY)
224 #define GET_DA(c,fs) ((((GET_CY (c) * HK_NUMSF) + \
225 GET_SF (fs)) * HK_NUMSC) + GET_SC (fs))
226
227 /* Spare - 177462 - read/write */
228
229 #define XM_KMASK 0177700 /* Qbus XM key mask */
230 #define XM_KEY 0022000 /* Qbus XM "key" */
231 #define XM_MMASK 0000077 /* Qbus XM mask */
232 #define SC02C (!UNIBUS && ((hkspr & XM_KMASK) == XM_KEY))
233
234 /* HKDB - 177464 - read/write */
235
236 /* HKMR - 177466 - maintenance register 1 */
237
238 #define MR_V_MS 0 /* message select */
239 #define MR_M_MS 03
240 #define MR_MS (MR_M_MS << MR_V_MS)
241 #define GET_MS(x) (((x) >> MR_V_MS) & MR_M_MS)
242 #define MR_PAR 0000020 /* force even parity */
243 #define MR_DMD 0000040 /* diagnostic mode */
244 #define MR_RW 0001777
245
246 /* HKEC1 - 177470 - ECC status 1 - always reads as 0 */
247 /* HKEC2 - 177472 - ECC status 2 - always reads as 0 */
248
249 /* HKMR2 - 177474 - maintenance register 2 */
250
251 #define AX_V_UNIT 0 /* unit #, all msgs */
252 #define AX_PAR 0100000 /* parity, all msgs */
253
254 #define A0_DRA 0000040 /* drive avail */
255 #define A0_VV 0000100 /* vol valid */
256 #define A0_RDY 0000200 /* drive ready */
257 #define A0_DT 0000400 /* drive type */
258 #define A0_FMT 0001000 /* format NI */
259 #define A0_OF 0002000 /* offset mode */
260 #define A0_WRL 0004000 /* write lock */
261 #define A0_SPO 0010000 /* spindle on */
262 #define A0_PIP 0020000 /* pos in prog */
263 #define A0_ATA 0040000 /* attention */
264
265 #define A1_SRV 0000020 /* servo */
266 #define A1_HHM 0000040 /* heads home */
267 #define A1_BHM 0000100 /* brushes home */
268 #define A1_DOR 0000200 /* door latched */
269 #define A1_CAR 0000400 /* cartridge present */
270 #define A1_SPD 0001000 /* speed ok */
271 #define A1_FWD 0002000 /* seek fwd */
272 #define A1_REV 0004000 /* seek rev */
273 #define A1_LDH 0010000 /* loading heads NI */
274 #define A1_RTZ 0020000 /* return to zero */
275 #define A1_UNL 0040000 /* unloading heads */
276
277 #define A2_V_DIF 4 /* cyl diff */
278 #define A2_M_DIF 0777
279
280 #define A3_V_SNO 3 /* serial # */
281
282 /* HKMR3 - 177476 - maintenance register 3 */
283
284 #define B0_IAE 0000040 /* invalid addr */
285 #define B0_ACLO 0000100 /* AC LO NI */
286 #define B0_FLT 0000200 /* fault */
287 #define B0_NXF 0000400 /* non exec fnc */
288 #define B0_CDP 0001000 /* msg parity err */
289 #define B0_SKI 0002000 /* seek incomp */
290 #define B0_WLE 0004000 /* write lock err */
291 #define B0_SLO 0010000 /* speed low NI */
292 #define B0_OFT 0020000 /* off track NI */
293 #define B0_UNS 0040000 /* rw unsafe NI */
294
295 #define B1_SCE 0000020 /* sector err NI */
296 #define B1_NWC 0000040 /* no write curr NI */
297 #define B1_NWT 0000100 /* no write trans NI */
298 #define B1_HFL 0000200 /* head fault NI */
299 #define B1_MHS 0000400 /* multiselect NI */
300 #define B1_IDX 0001000 /* index err NI */
301 #define B1_TRI 0002000 /* tribit err NI */
302 #define B1_SVE 0004000 /* servo err NI */
303 #define B1_SKI 0010000 /* seek no mot */
304 #define B1_LIM 0020000 /* seek limit NI */
305 #define B1_SVU 0040000 /* servo unsafe NI */
306
307 #define B2_V_CYL 4 /* cylinder */
308
309 #define B3_V_SEC 4 /* sector */
310 #define B3_V_DHA 9 /* decoded head */
311
312 /* Read header */
313
314 #define RDH1_V_CYL 0 /* cylinder */
315 #define RDH2_V_SEC 0 /* sector */
316 #define RDH2_V_DHA 5 /* decoded head */
317 #define RDH2_GOOD 0140000 /* good sector flags */
318
319 /* Debug detail levels */
320
321 #define HKDEB_OPS 001 /* transactions */
322 #define HKDEB_RRD 002 /* reg reads */
323 #define HKDEB_RWR 004 /* reg writes */
324
325 extern int32 int_req[IPL_HLVL];
326 extern FILE *sim_deb;
327
328 uint16 *hkxb = NULL; /* xfer buffer */
329 int32 hkcs1 = 0; /* control/status 1 */
330 int32 hkwc = 0; /* word count */
331 int32 hkba = 0; /* bus address */
332 int32 hkda = 0; /* track/sector */
333 int32 hkcs2 = 0; /* control/status 2 */
334 int32 hkds[HK_NUMDR] = { 0 }; /* drive status */
335 int32 hker[HK_NUMDR] = { 0 }; /* error status */
336 int32 hkof = 0; /* offset */
337 int32 hkmr = 0; /* maint registers */
338 int32 hkmr2 = 0;
339 int32 hkmr3 = 0;
340 int32 hkdc = 0; /* cylinder */
341 int32 hkspr = 0; /* spare */
342 int32 hk_cwait = 5; /* command time */
343 int32 hk_swait = 10; /* seek time */
344 int32 hk_rwait = 10; /* rotate time */
345 int32 hk_min2wait = 300; /* min time to 2nd int */
346 int16 hkdb[3] = { 0 }; /* data buffer silo */
347 int16 hk_off[HK_NUMDR] = { 0 }; /* saved offset */
348 int16 hk_dif[HK_NUMDR] = { 0 }; /* cylinder diff */
349 static uint8 reg_in_drive[16] = {
350 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
351
352 DEVICE hk_dev;
353 t_stat hk_rd (int32 *data, int32 PA, int32 access);
354 t_stat hk_wr (int32 data, int32 PA, int32 access);
355 t_stat hk_svc (UNIT *uptr);
356 t_stat hk_reset (DEVICE *dptr);
357 t_stat hk_boot (int32 unitno, DEVICE *dptr);
358 t_stat hk_attach (UNIT *uptr, char *cptr);
359 t_stat hk_detach (UNIT *uptr);
360 int32 hk_rdmr2 (int32 msg);
361 int32 hk_rdmr3 (int32 msg);
362 void update_hkcs (int32 flags, int32 drv);
363 void update_hkds (int32 drv);
364 void hk_cmderr (int32 err, int32 drv);
365 void hk_go (int32 drv);
366 t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
367 t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc);
368
369 extern t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds);
370
371 /* HK data structures
372
373 hk_dev HK device descriptor
374 hk_unit HK unit list
375 hk_reg HK register list
376 hk_mod HK modifier list
377 */
378
379 DIB hk_dib = {
380 IOBA_HK, IOLN_HK, &hk_rd, &hk_wr,
381 1, IVCL (HK), VEC_HK, { NULL }
382 };
383
384 UNIT hk_unit[] = {
385 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
386 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) },
387 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
388 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) },
389 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
390 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) },
391 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
392 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) },
393 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
394 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) },
395 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
396 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) },
397 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
398 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) },
399 { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
400 UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }
401 };
402
403 REG hk_reg[] = {
404 { GRDATA (HKCS1, hkcs1, DEV_RDX, 16, 0) },
405 { GRDATA (HKWC, hkwc, DEV_RDX, 16, 0) },
406 { GRDATA (HKBA, hkba, DEV_RDX, 16, 0) },
407 { GRDATA (HKDA, hkda, DEV_RDX, 16, 0) },
408 { GRDATA (HKCS2, hkcs2, DEV_RDX, 16, 0) },
409 { BRDATA (HKDS, hkds, DEV_RDX, 16, HK_NUMDR) },
410 { BRDATA (HKER, hker, DEV_RDX, 16, HK_NUMDR) },
411 { BRDATA (HKDB, hkdb, DEV_RDX, 16, 3) },
412 { GRDATA (HKDC, hkdc, DEV_RDX, 16, 0) },
413 { GRDATA (HKOF, hkof, DEV_RDX, 8, 0) },
414 { GRDATA (HKMR, hkmr, DEV_RDX, 16, 0) },
415 { GRDATA (HKMR2, hkmr2, DEV_RDX, 16, 0), REG_RO },
416 { GRDATA (HKMR3, hkmr3, DEV_RDX, 16, 0), REG_RO },
417 { GRDATA (HKSPR, hkspr, DEV_RDX, 16, 0) },
418 { FLDATA (INT, IREQ (HK), INT_V_HK) },
419 { FLDATA (ERR, hkcs1, CSR_V_ERR) },
420 { FLDATA (DONE, hkcs1, CSR_V_DONE) },
421 { FLDATA (IE, hkcs1, CSR_V_IE) },
422 { DRDATA (CTIME, hk_cwait, 24), REG_NZ + PV_LEFT },
423 { DRDATA (STIME, hk_swait, 24), REG_NZ + PV_LEFT },
424 { DRDATA (RTIME, hk_rwait, 24), REG_NZ + PV_LEFT },
425 { DRDATA (MIN2TIME, hk_min2wait, 24), REG_NZ + PV_LEFT + REG_HRO },
426 { URDATA (FNC, hk_unit[0].FNC, DEV_RDX, 5, 0,
427 HK_NUMDR, REG_HRO) },
428 { URDATA (CYL, hk_unit[0].CYL, DEV_RDX, 10, 0,
429 HK_NUMDR, REG_HRO) },
430 { BRDATA (OFFSET, hk_off, DEV_RDX, 16, HK_NUMDR), REG_HRO },
431 { BRDATA (CYLDIF, hk_dif, DEV_RDX, 16, HK_NUMDR), REG_HRO },
432 { URDATA (CAPAC, hk_unit[0].capac, 10, T_ADDR_W, 0,
433 HK_NUMDR, PV_LEFT | REG_HRO) },
434 { GRDATA (DEVADDR, hk_dib.ba, DEV_RDX, 32, 0), REG_HRO },
435 { GRDATA (DEVVEC, hk_dib.vec, DEV_RDX, 16, 0), REG_HRO },
436 { NULL }
437 };
438
439 MTAB hk_mod[] = {
440 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
441 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
442 { UNIT_DUMMY, 0, NULL, "BADBLOCK", &hk_set_bad },
443 { (UNIT_DTYPE+UNIT_ATT), UNIT_RK06 + UNIT_ATT,
444 "RK06", NULL, NULL },
445 { (UNIT_DTYPE+UNIT_ATT), UNIT_RK07 + UNIT_ATT,
446 "RK07", NULL, NULL },
447 { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), UNIT_RK06,
448 "RK06", NULL, NULL },
449 { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), UNIT_RK07,
450 "RK07", NULL, NULL },
451 { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
452 { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },
453 { (UNIT_AUTO+UNIT_DTYPE), UNIT_RK06,
454 NULL, "RK06", &hk_set_size },
455 { (UNIT_AUTO+UNIT_DTYPE), UNIT_RK07,
456 NULL, "RK07", &hk_set_size },
457 { MTAB_XTD|MTAB_VDV, 0040, "ADDRESS", "ADDRESS",
458 &set_addr, &show_addr, NULL },
459 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
460 &set_vec, &show_vec, NULL },
461 { 0 }
462 };
463
464 DEBTAB hk_deb[] = {
465 { "OPS", HKDEB_OPS },
466 { "RRD", HKDEB_RRD },
467 { "RWR", HKDEB_RWR },
468 { NULL, 0 }
469 };
470
471 DEVICE hk_dev = {
472 "HK", hk_unit, hk_reg, hk_mod,
473 HK_NUMDR, DEV_RDX, 24, 1, DEV_RDX, 16,
474 NULL, NULL, &hk_reset,
475 &hk_boot, &hk_attach, &hk_detach,
476 &hk_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG, 0,
477 hk_deb, NULL, 0
478 };
479
480 /* I/O dispatch routines, I/O addresses 17777440 - 17777476 */
481
482 t_stat hk_rd (int32 *data, int32 PA, int32 access)
483 {
484 int32 drv, i, j;
485
486 drv = GET_UNIT (hkcs2); /* get current unit */
487 j = (PA >> 1) & 017; /* get reg offset */
488 if (reg_in_drive[j] && (hk_unit[drv].flags & UNIT_DIS)) { /* nx disk */
489 hkcs2 = hkcs2 | CS2_NED; /* set error flag */
490 update_hkcs (0, drv);
491 *data = 0;
492 return SCPE_OK;
493 }
494
495 update_hkcs (0, drv); /* update status */
496 switch (j) { /* decode PA<4:1> */
497
498 case 000: /* HKCS1 */
499 *data = hkcs1;
500 break;
501
502 case 001: /* HKWC */
503 *data = hkwc;
504 break;
505
506 case 002: /* HKBA */
507 *data = hkba = hkba & ~BA_MBZ;
508 break;
509
510 case 003: /* HKDA */
511 *data = hkda = hkda & ~DA_MBZ;
512 break;
513
514 case 004: /* HKCS2 */
515 *data = hkcs2 = (hkcs2 & ~CS2_MBZ) | CS2_IR;
516 break;
517
518 case 005: /* HKDS */
519 *data = hkds[drv];
520 break;
521
522 case 006: /* HKER */
523 *data = hker[drv];
524 break;
525
526 case 007: /* HKAS */
527 *data = hkof;
528 for (i = 0; i < HK_NUMDR; i++)
529 if (hkds[i] & DS_ATA) *data = *data | (AS_U0 << i);
530 break;
531
532 case 010: /* HKDC */
533 *data = hkdc = hkdc & ~DC_MBZ;
534 break;
535
536 case 011: /* spare */
537 *data = hkspr;
538 break;
539
540 case 012: /* HKDB */
541 *data = hkdb[0]; /* top of silo */
542 hkdb[0] = hkdb[1]; /* ripple silo */
543 hkdb[1] = hkdb[2];
544 hkdb[2] = 0; /* just for READH */
545 break;
546
547 case 013: /* HKMR */
548 *data = hkmr;
549 break;
550
551 case 014: /* HKEC1 */
552 case 015: /* HKEC2 */
553 *data = 0; /* no ECC */
554 break;
555
556 case 016: /* HKMR2 */
557 *data = hkmr2;
558 break;
559
560 case 017: /* HKMR3 */
561 *data = hkmr3;
562 break;
563 }
564
565 if (DEBUG_PRI (hk_dev, HKDEB_RRD))
566 fprintf (sim_deb, ">>HK%d read: reg%d=%o\n", drv, j, *data);
567 return SCPE_OK;
568 }
569
570 t_stat hk_wr (int32 data, int32 PA, int32 access)
571 {
572 int32 drv, i, j;
573 UNIT *uptr;
574
575 drv = GET_UNIT (hkcs2); /* get current unit */
576 uptr = hk_dev.units + drv; /* get unit */
577 j = (PA >> 1) & 017; /* get reg offset */
578 if ((hkcs1 & CS1_GO) && /* busy? */
579 !(((j == 0) && (data & CS1_CCLR)) || /* not cclr or sclr? */
580 ((j == 4) && (data & CS2_CLR)))) {
581 hkcs2 = hkcs2 | CS2_PGE; /* prog error */
582 update_hkcs (0, drv);
583 return SCPE_OK;
584 }
585
586 if (DEBUG_PRI (hk_dev, HKDEB_RWR))
587 fprintf (sim_deb, ">>HK%d write: reg%d=%o\n", drv, j, data);
588 switch (j) { /* decode PA<4:1> */
589
590 case 000: /* HKCS1 */
591 if (data & CS1_CCLR) { /* controller reset? */
592 hkcs1 = CS1_DONE; /* CS1 = done */
593 hkcs2 = CS2_IR; /* CS2 = ready */
594 hkmr = hkmr2 = hkmr3 = 0; /* maint = 0 */
595 hkda = hkdc = 0;
596 hkba = hkwc = 0;
597 hkspr = hkof = 0;
598 CLR_INT (HK); /* clr int */
599 for (i = 0; i < HK_NUMDR; i++) { /* stop data xfr */
600 if (sim_is_active (&hk_unit[i]) &&
601 ((uptr->FNC & CS1_M_FNC) >= FNC_XFER))
602 sim_cancel (&hk_unit[i]);
603 }
604 drv = 0;
605 break;
606 }
607 if (data & CS1_IE) { /* setting IE? */
608 if (data & CS1_DONE) SET_INT (HK); /* write to DONE+IE? */
609 }
610 else CLR_INT (HK); /* no, clr intr */
611 hkcs1 = (hkcs1 & ~CS1_RW) | (data & CS1_RW); /* merge data */
612 if (SC02C) hkspr = (hkspr & ~CS1_M_UAE) | GET_UAE (hkcs1);
613 if ((data & CS1_GO) && !(hkcs1 & CS1_ERR)) /* go? */
614 hk_go (drv);
615 break;
616
617 case 001: /* HKWC */
618 hkwc = data;
619 break;
620
621 case 002: /* HKBA */
622 hkba = data & ~BA_MBZ;
623 break;
624
625 case 003: /* HKDA */
626 hkda = data & ~DA_MBZ;
627 break;
628
629 case 004: /* HKCS2 */
630 if (data & CS2_CLR) hk_reset (&hk_dev); /* init? */
631 else hkcs2 = (hkcs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR;
632 drv = GET_UNIT (hkcs2);
633 break;
634
635 case 007: /* HKAS */
636 hkof = data & AS_OF;
637 break;
638
639 case 010: /* HKDC */
640 hkdc = data & ~DC_MBZ;
641 break;
642
643 case 011: /* spare */
644 hkspr = data;
645 if (SC02C) hkcs1 = PUT_UAE (hkcs1, hkspr & 03); /* SC02C? upd UAE */
646 break;
647
648 case 012: /* HKDB */
649 hkdb[0] = data;
650 break;
651
652 case 013: /* HKMR */
653 hkmr = data & MR_RW;
654 break;
655
656 default: /* all others RO */
657 break;
658 } /* end switch */
659
660 update_hkcs (0, drv); /* update status */
661 return SCPE_OK;
662 }
663
664 /* Initiate operation - go set, not previously set */
665
666 void hk_go (int32 drv)
667 {
668 int32 fnc, t;
669 UNIT *uptr;
670
671 static uint8 fnc_cdt[16] = {
672 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
673 };
674
675 static uint8 fnc_nxf[16] = {
676 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0
677 };
678 static uint8 fnc_att[16] = {
679 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
680 };
681 static uint8 fnc_rdy[16] = {
682 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
683 };
684 static uint8 fnc_cyl[16] = {
685 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0
686 };
687
688 fnc = GET_FNC (hkcs1);
689 if (DEBUG_PRI (hk_dev, HKDEB_OPS)) fprintf (sim_deb,
690 ">>HK%d strt: fnc=%o, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n",
691 drv, fnc, hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc);
692 uptr = hk_dev.units + drv; /* get unit */
693 if (fnc != FNC_NOP) hkmr = hkmr & ~MR_MS; /* !nop, clr msg sel */
694 if (uptr->flags & UNIT_DIS) { /* nx unit? */
695 hkcs2 = hkcs2 | CS2_NED; /* set error flag */
696 update_hkcs (CS1_DONE, drv); /* done */
697 return;
698 }
699 if (fnc_cdt[fnc] &&
700 (((hkcs1 & CS1_DT) != 0) != /* need dtype match? */
701 ((uptr->flags & UNIT_DTYPE) != 0))) {
702 hk_cmderr (ER_DTY, drv); /* type error */
703 return;
704 }
705 if (fnc_nxf[fnc] && ((hkds[drv] & DS_VV) == 0)) { /* need vol valid? */
706 hk_cmderr (ER_NXF, drv); /* non exec func */
707 return;
708 }
709 if (fnc_att[fnc] && !(uptr->flags & UNIT_ATT)) { /* need attached? */
710 hk_cmderr (ER_UNS, drv); /* unsafe */
711 return;
712 }
713 if (fnc_rdy[fnc] && sim_is_active (uptr)) return; /* need inactive? */
714 if (fnc_cyl[fnc] && /* need valid cyl */
715 ((GET_CY (hkdc) >= HK_CYL (uptr)) || /* bad cylinder */
716 (GET_SF (hkda) >= HK_NUMSF) || /* bad surface */
717 (GET_SC (hkda) >= HK_NUMSC))) { /* or bad sector? */
718 hk_cmderr (ER_IAE, drv); /* illegal addr */
719 return;
720 }
721
722 hkcs1 = (hkcs1 | CS1_GO) & ~CS1_DONE; /* set go, clear done */
723 switch (fnc) { /* case on function */
724
725 /* Instantaneous functions (unit may be busy, can't schedule thread) */
726
727 case FNC_NOP: /* no operation */
728 hkmr2 = hk_rdmr2 (GET_MS (hkmr)); /* get serial msgs */
729 hkmr3 = hk_rdmr3 (GET_MS (hkmr));
730 update_hkcs (CS1_DONE, drv); /* done */
731 break;
732
733 case FNC_DCLR: /* drive clear */
734 hkds[drv] &= ~DS_ATA; /* clr ATA */
735 hker[drv] = 0; /* clear errors */
736 update_hkcs (CS1_DONE, drv); /* done */
737 break;
738
739 case FNC_PACK: /* pack acknowledge */
740 hkds[drv] = hkds[drv] | DS_VV; /* set volume valid */
741 update_hkcs (CS1_DONE, drv); /* done */
742 break;
743
744 /* "Fast functions" finish in less than 15 usec */
745
746 case FNC_START: /* start spindle */
747 case FNC_UNLOAD: /* unload */
748 uptr->FNC = fnc; /* save function */
749 sim_activate (uptr, hk_cwait); /* schedule */
750 return;
751
752 /* Positioning functions provide two interrupts - an immediate interrupt
753 on ctrl done and a second one (if ctrl ready) when the seek is complete */
754
755 case FNC_OFFSET: /* offset mode */
756 case FNC_RECAL: /* recalibrate */
757 case FNC_SEEK: /* seek */
758 hkds[drv] = hkds[drv] | DS_PIP; /* set positioning */
759 uptr->FNC = fnc; /* save function */
760 sim_activate (uptr, hk_cwait); /* schedule */
761 return;
762
763 /* Data transfer functions lock the controller for the duration */
764
765 case FNC_WRITEH: /* write headers */
766 case FNC_WRITE: /* write */
767 hk_off[drv] = 0; /* clr offset */
768 case FNC_WCHK: /* write check */
769 case FNC_READ: /* read */
770 case FNC_READH: /* read headers */
771 hk_dif[drv] = hkdc - uptr->CYL; /* cyl diff */
772 t = abs (hk_dif[drv]); /* |cyl diff| */
773 sim_activate (uptr, hk_rwait + (hk_swait * t)); /* Schedule */
774 uptr->FNC = fnc; /* save function */
775 uptr->CYL = hkdc; /* update cyl */
776 return;
777
778 default:
779 hk_cmderr (ER_ILF, drv); /* not supported */
780 break;
781 }
782 return;
783 }
784
785 /* Service unit timeout
786
787 Complete movement or data transfer command
788 Unit must exist - can't remove an active unit
789 Unit must be attached - detach cancels in progress operations
790 */
791
792 t_stat hk_svc (UNIT *uptr)
793 {
794 int32 i, t, dc, fnc, err;
795 int32 wc, awc, da;
796 uint32 drv, ba;
797 uint16 comp;
798
799 drv = (uint32) (uptr - hk_dev.units); /* get drv number */
800 fnc = uptr->FNC & CS1_M_FNC; /* get function */
801 switch (fnc) { /* case on function */
802
803 /* Fast commands - start spindle only provides one interrupt
804 because ATTACH implicitly spins up the drive */
805
806 case FNC_UNLOAD: /* unload */
807 hk_detach (uptr); /* detach unit */
808 case FNC_START: /* start spindle */
809 update_hkcs (CS1_DONE, drv); /* done */
810 break;
811
812 /* Positioning commands provide two interrupts, an immediate controller done
813 and a delayed drive interrupt */
814
815 case FNC_OFFSET: /* offset */
816 if (uptr->FNC & FNC_2ND) { /* 2nd int? */
817 hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */
818 update_hkcs (CS1_DI, drv); /* drive intr */
819 }
820 else {
821 uptr->FNC = uptr->FNC | FNC_2ND; /* second state */
822 hk_off[drv] = hkof & AS_OF; /* save offset */
823 sim_activate (uptr, hk_min2wait); /* wait for compl */
824 update_hkcs (CS1_DONE, drv); /* done */
825 }
826 break;
827
828 case FNC_RECAL: /* recalibrate */
829 case FNC_SEEK: /* seek */
830 if (uptr->FNC & FNC_2ND) { /* 2nd int? */
831 hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */
832 update_hkcs (CS1_DI, drv); /* drive intr */
833 }
834 else {
835 uptr->FNC = uptr->FNC | FNC_2ND; /* second state */
836 hk_off[drv] = 0; /* clr offset */
837 dc = (fnc == FNC_SEEK)? hkdc: 0; /* get cyl */
838 hk_dif[drv] = dc - uptr->CYL; /* cyl diff */
839 t = abs (hk_dif[drv]) * hk_swait; /* |cyl diff| */
840 if (t < hk_min2wait) t = hk_min2wait; /* min time */
841 uptr->CYL = dc; /* save cyl */
842 sim_activate (uptr, t); /* schedule */
843 update_hkcs (CS1_DONE, drv); /* done */
844 }
845 break;
846
847 /* Data transfer commands only generate one interrupt */
848
849 case FNC_READH:
850 hkdb[0] = uptr->CYL << RDH1_V_CYL; /* first word */
851 hkdb[1] = (GET_SC (hkda) << RDH2_V_SEC) | /* second word */
852 (1 << (GET_SF (hkda) + RDH2_V_DHA)) | RDH2_GOOD;
853 hkdb[2] = hkdb[0] ^ hkdb[1]; /* checksum */
854 update_hkcs (CS1_DONE, drv); /* done */
855 break;
856
857 case FNC_WRITE: /* write */
858 if (uptr->flags & UNIT_WPRT) { /* write locked? */
859 hk_cmderr (ER_WLE, drv); /* command error */
860 return SCPE_OK;
861 }
862 case FNC_WCHK: /* write check */
863 case FNC_READ: /* read */
864 if (SC02C) ba = ((hkspr & XM_MMASK) << 16) | hkba; /* 22b addr? */
865 else ba = (GET_UAE (hkcs1) << 16) | hkba; /* no, 18b addr */
866 da = GET_DA (hkdc, hkda) * HK_NUMWD; /* get disk addr */
867 wc = 0200000 - hkwc; /* get true wc */
868
869 if ((da + wc) > HK_SIZE (uptr)) { /* disk overrun? */
870 hker[drv] = hker[drv] | ER_AOE; /* set err */
871 hkds[drv] = hkds[drv] | DS_ATA; /* set attn */
872 wc = HK_SIZE (uptr) - da; /* trim xfer */
873 if (da >= HK_SIZE (uptr)) { /* none left? */
874 update_hkcs (CS1_DONE, drv); /* then done */
875 break;
876 }
877 }
878
879 err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET);
880 if (uptr->FNC == FNC_WRITE) { /* write? */
881 if (hkcs2 & CS2_UAI) { /* no addr inc? */
882 if (t = Map_ReadW (ba, 2, &comp)) { /* get 1st wd */
883 wc = 0; /* NXM, no xfr */
884 hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */
885 }
886 for (i = 0; i < wc; i++) hkxb[i] = comp;
887 }
888 else { /* normal */
889 if (t = Map_ReadW (ba, wc << 1, hkxb)) { /* get buf */
890 wc = wc - (t >> 1); /* NXM, adj wc */
891 hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */
892 }
893 ba = ba + (wc << 1); /* adv ba */
894 }
895 awc = (wc + (HK_NUMWD - 1)) & ~(HK_NUMWD - 1);
896 for (i = wc; i < awc; i++) hkxb[i] = 0; /* fill buf */
897 if (wc && !err) { /* write buf */
898 fxwrite (hkxb, sizeof (uint16), wc, uptr->fileref);
899 err = ferror (uptr->fileref);
900 }
901 } /* end if wr */
902 else if (uptr->FNC == FNC_READ) { /* read? */
903 i = fxread (hkxb, sizeof (uint16), wc, uptr->fileref);
904 err = ferror (uptr->fileref);
905 for ( ; i < wc; i++) hkxb[i] = 0; /* fill buf */
906 if (hkcs2 & CS2_UAI) { /* no addr inc? */
907 if (t = Map_WriteW (ba, 2, &hkxb[wc - 1])) {
908 wc = 0; /* NXM, no xfr */
909 hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */
910 }
911 }
912 else { /* normal */
913 if (t = Map_WriteW (ba, wc << 1, hkxb)) { /* put buf */
914 wc = wc - (t >> 1); /* NXM, adj wc */
915 hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */
916 }
917 ba = ba + (wc << 1); /* adv ba */
918 }
919 } /* end if read */
920 else { /* wchk */
921 i = fxread (hkxb, sizeof (uint16), wc, uptr->fileref);
922 err = ferror (uptr->fileref);
923 for ( ; i < wc; i++) hkxb[i] = 0; /* fill buf */
924 awc = wc;
925 for (wc = 0; wc < awc; wc++) { /* loop thru buf */
926 if (Map_ReadW (ba, 2, &comp)) { /* read word */
927 hkcs2 = hkcs2 | CS2_NEM; /* set error */
928 break;
929 }
930 if (comp != hkxb[wc]) { /* compare wd */
931 hkcs2 = hkcs2 | CS2_WCE; /* set error */
932 break;
933 }
934 if ((hkcs2 & CS2_UAI) == 0) ba = ba + 2;
935 }
936 } /* end else wchk */
937
938 hkwc = (hkwc + wc) & 0177777; /* final word count */
939 hkba = (ba & 0177777) & ~BA_MBZ; /* lower 16b */
940 hkcs1 = PUT_UAE (hkcs1, ba >> 16); /* upper 2b */
941 if (SC02C) /* SC02C? upper 6b */
942 hkspr = (hkspr & ~XM_MMASK) | ((ba >> 16) & XM_MMASK);
943 da = da + wc + (HK_NUMWD - 1);
944 da = da / HK_NUMWD;
945 hkda = da % HK_NUMSC;
946 da = da / HK_NUMSC;
947 hkda = hkda | ((da % HK_NUMSF) << DA_V_SF);
948 hkdc = da / HK_NUMSF;
949
950 if (err != 0) { /* error? */
951 hk_cmderr (ER_PAR, drv); /* set drive error */
952 perror ("HK I/O error");
953 clearerr (uptr->fileref);
954 return SCPE_IOERR;
955 }
956
957 case FNC_WRITEH: /* write headers stub */
958 update_hkcs (CS1_DONE, drv); /* set done */
959 break;
960 } /* end case func */
961
962 return SCPE_OK;
963 }
964
965 /* Controller status update
966
967 Check for done transition
968 Update drive status
969 Update HKCS1
970 Update interrupt request
971 */
972
973 void update_hkcs (int32 flag, int32 drv)
974 {
975 int32 i;
976
977 update_hkds (drv); /* upd drv status */
978 if (flag & CS1_DONE) hkcs1 = hkcs1 & ~CS1_GO; /* clear go */
979 if (hkcs1 & CS1_IE) { /* intr enable? */
980 if (((flag & CS1_DONE) && ((hkcs1 & CS1_DONE) == 0)) ||
981 ((flag & CS1_DI) && (hkcs1 & CS1_DONE))) /* done 0->1 or DI? */
982 SET_INT (HK);
983 }
984 else CLR_INT (HK);
985 hkcs1 = (hkcs1 & (CS1_DT|CS1_UAE|CS1_DONE|CS1_IE|CS1_SPA|CS1_FNC|CS1_GO)) | flag;
986 for (i = 0; i < HK_NUMDR; i++) { /* if ATA, set DI */
987 if (hkds[i] & DS_ATA) hkcs1 = hkcs1 | CS1_DI;
988 }
989 if (hker[drv] | (hkcs1 & (CS1_PAR | CS1_CTO)) | /* if err, set ERR */
990 (hkcs2 & CS2_ERR)) hkcs1 = hkcs1 | CS1_ERR;
991 if ((flag & CS1_DONE) && /* set done && debug? */
992 (DEBUG_PRI (hk_dev, HKDEB_OPS)))
993 fprintf (sim_deb,
994 ">>HK%d done: fnc=%o, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n",
995 drv, GET_FNC (hkcs1), hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc);
996 return;
997 }
998
999 /* Drive status update */
1000
1001 void update_hkds (int32 drv)
1002 {
1003 if (hk_unit[drv].flags & UNIT_DIS) { /* disabled? */
1004 hkds[drv] = hker[drv] = 0; /* all clear */
1005 return;
1006 }
1007 hkds[drv] = (hkds[drv] & (DS_VV | DS_PIP | DS_ATA)) | DS_VLD | DS_DRA;
1008 if (hk_unit[drv].flags & UNIT_ATT) { /* attached? */
1009 if (!sim_is_active (&hk_unit[drv])) /* not busy? */
1010 hkds[drv] = hkds[drv] | DS_RDY; /* set RDY */
1011 if (hker[drv]) hkds[drv] = hkds[drv] | DS_ATA; /* err? set ATA */
1012 if (hk_off[drv]) hkds[drv] = hkds[drv] | DS_OF; /* offset? set OF */
1013 if (hk_unit[drv].flags & UNIT_WPRT) /* write locked? */
1014 hkds[drv] = hkds[drv] | DS_WRL; /* set WRL */
1015 }
1016 else {
1017 hkds[drv] = hkds[drv] & ~(DS_PIP | DS_VV); /* no, clr PIP,VV */
1018 hker[drv] = 0; /* no errors */
1019 }
1020 if (hk_unit[drv].flags & UNIT_RK07) hkds[drv] = hkds[drv] | DS_DT;
1021 return;
1022 }
1023
1024 /* Set error and abort command */
1025
1026 void hk_cmderr (int32 err, int32 drv)
1027 {
1028 hker[drv] = hker[drv] | err; /* set error */
1029 hkds[drv] = hkds[drv] | DS_ATA; /* set attn */
1030 update_hkcs (CS1_DONE, drv); /* set done */
1031 return;
1032 }
1033
1034 /* Diagnostic registers
1035
1036 It's unclear whether the drivers actually use these values, but the
1037 Emulex controller bothers to implement them, so we will too */
1038
1039 int32 hk_mrpar (int32 v)
1040 {
1041 int32 bit, wrk;
1042
1043 wrk = v & 077777; /* par on 15b */
1044 v = wrk | ((hkmr & MR_PAR)? 0: AX_PAR); /* even/odd */
1045 while (wrk) { /* while 1's */
1046 bit = wrk & (-wrk); /* lowest 1 */
1047 wrk = wrk & ~bit; /* clear */
1048 v = v ^ AX_PAR; /* xor parity */
1049 }
1050 return v;
1051 }
1052
1053 int32 hk_rdmr2 (int32 msg)
1054 {
1055 int32 drv = GET_UNIT (hkcs2);
1056 int32 v = drv << AX_V_UNIT;
1057 UNIT *uptr = hk_dev.units + drv;
1058 int32 fnc = uptr->FNC & CS1_M_FNC;
1059
1060 switch (msg) {
1061
1062 case 0: /* message A0 */
1063 v = v | ((hkds[drv] & DS_ATA)? A0_ATA: 0) |
1064 ((hkds[drv] & DS_PIP)? A0_PIP: 0) |
1065 ((uptr->flags & UNIT_WPRT)? A0_WRL: 0) |
1066 ((hk_off[drv])? A0_OF: 0) |
1067 ((uptr->flags & UNIT_RK07)? A0_DT: 0) |
1068 ((hkds[drv] & DS_VV)? A0_VV: 0) | A0_DRA;
1069 if (uptr->flags & UNIT_ATT) v = v | A0_SPO |
1070 (!sim_is_active (uptr)? A0_RDY: 0);
1071 break;
1072
1073 case 1: /* message A1 */
1074 if (uptr->flags & UNIT_ATT) {
1075 if (sim_is_active (uptr)) {
1076 if (fnc == FNC_UNLOAD) v = v | A1_UNL;
1077 else if (fnc == FNC_RECAL) v = v | A1_RTZ;
1078 else if (fnc == FNC_SEEK) {
1079 if (hk_dif[drv] < 0) v = v | A1_REV;
1080 if (hk_dif[drv] > 0) v = v | A1_FWD;
1081 }
1082 }
1083 v = v | (A1_SPD|A1_CAR|A1_DOR|A1_HHM|A1_SRV);
1084 }
1085 else v = v | A1_HHM;
1086 break;
1087
1088 case 2: /* message A2 */
1089 if (hkds[drv] & DS_OF)
1090 v = v | ((hk_off[drv] & A2_M_DIF) << A2_V_DIF);
1091 else v = v | ((hk_dif[drv] & A2_M_DIF) << A2_V_DIF);
1092 break;
1093
1094 case 3: /* message A3 */
1095 v = v | ((012340 + v) << A3_V_SNO);
1096 break;
1097 }
1098
1099 return hk_mrpar (v);
1100 }
1101
1102 int32 hk_rdmr3 (int32 msg)
1103 {
1104 int32 drv = GET_UNIT (hkcs2);
1105 int32 v = msg & 03;
1106
1107 switch (msg) {
1108
1109 case 0: /* message B0 */
1110 v = v | ((hker[drv] & ER_WLE)? (B0_WLE | B0_FLT): 0) |
1111 ((hker[drv] & ER_SKI)? (B0_SKI | B0_FLT): 0) |
1112 ((hker[drv] & ER_NXF)? (B0_NXF | B0_FLT): 0) |
1113 ((hker[drv] & ER_IAE)? (B0_IAE | B0_FLT): 0);
1114 break;
1115
1116 case 1: /* message B1 */
1117 v = v | ((hker[drv] & ER_SKI)? B1_SKI: 0) |
1118 ((hker[drv] & ER_UNS)? B1_SVE: 0);
1119 break;
1120
1121 case 2: /* message B2 */
1122 v = v | (hk_unit[drv].CYL << B2_V_CYL);
1123 break;
1124
1125 case 3: /* message B3 */
1126 v = v | (GET_SC (hkda) << B3_V_SEC) |
1127 (1 << (GET_SF (hkda) + B3_V_DHA));
1128 break;
1129 }
1130
1131 return hk_mrpar (v);
1132 }
1133
1134 /* Device reset */
1135
1136 t_stat hk_reset (DEVICE *dptr)
1137 {
1138 int32 i;
1139 UNIT *uptr;
1140
1141 hkcs1 = CS1_DONE; /* set done */
1142 hkcs2 = CS2_IR; /* clear state */
1143 hkmr = hkmr2 = hkmr3 = 0;
1144 hkda = hkdc = 0;
1145 hkba = hkwc = 0;
1146 hkof = hkspr = 0;
1147 CLR_INT (HK); /* clear intr req */
1148 for (i = 0; i < HK_NUMDR; i++) { /* stop operations */
1149 uptr = hk_dev.units + i;
1150 sim_cancel (uptr);
1151 if (uptr->flags & UNIT_ATT) hkds[i] = hkds[i] & DS_VV;
1152 else hkds[i] = 0;
1153 uptr->CYL = uptr->FNC = 0; /* clear state */
1154 hk_dif[i] = 0;
1155 hk_off[i] = 0;
1156 hker[i] = 0;
1157 } /* clear errors */
1158 if (hkxb == NULL) hkxb = (uint16 *) calloc (HK_MAXFR, sizeof (uint16));
1159 if (hkxb == NULL) return SCPE_MEM;
1160 return SCPE_OK;
1161 }
1162
1163 /* Device attach */
1164
1165 t_stat hk_attach (UNIT *uptr, char *cptr)
1166 {
1167 uint32 drv, p;
1168 t_stat r;
1169
1170 uptr->capac = HK_SIZE (uptr);
1171 r = attach_unit (uptr, cptr); /* attach unit */
1172 if (r != SCPE_OK) return r; /* error? */
1173 drv = (uint32) (uptr - hk_dev.units); /* get drv number */
1174 hkds[drv] = DS_ATA | DS_RDY | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0);
1175 hker[drv] = 0; /* upd drv status */
1176 hk_off[drv] = 0;
1177 hk_dif[drv] = 0;
1178 uptr->CYL = 0;
1179 update_hkcs (CS1_DI, drv); /* upd ctlr status */
1180
1181 p = sim_fsize (uptr->fileref); /* get file size */
1182 if (p == 0) { /* new disk image? */
1183 if (uptr->flags & UNIT_RO) return SCPE_OK;
1184 return pdp11_bad_block (uptr, HK_NUMSC, HK_NUMWD);
1185 }
1186 if ((uptr->flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */
1187 if (p > (RK06_SIZE * sizeof (uint16))) {
1188 uptr->flags = uptr->flags | UNIT_RK07;
1189 uptr->capac = RK07_SIZE;
1190 }
1191 else {
1192 uptr->flags = uptr->flags & ~UNIT_RK07;
1193 uptr->capac = RK06_SIZE;
1194 }
1195 return SCPE_OK;
1196 }
1197
1198 /* Device detach */
1199
1200 t_stat hk_detach (UNIT *uptr)
1201 {
1202 uint32 drv;
1203
1204 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */
1205 drv = (uint32) (uptr - hk_dev.units); /* get drv number */
1206 hkds[drv] = (hkds[drv] & ~(DS_RDY | DS_WRL | DS_VV | DS_OF)) | DS_ATA;
1207 if (sim_is_active (uptr)) { /* unit active? */
1208 sim_cancel (uptr); /* cancel operation */
1209 hker[drv] = hker[drv] | ER_OPI; /* set drive error */
1210 if ((uptr->FNC & FNC_2ND) == 0) /* expecting done? */
1211 update_hkcs (CS1_DONE, drv); /* set done */
1212 }
1213 update_hkcs (CS1_DI, drv); /* request intr */
1214 return detach_unit (uptr);
1215 }
1216
1217 /* Set size command validation routine */
1218
1219 t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
1220 {
1221 if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
1222 uptr->capac = val? RK07_SIZE: RK06_SIZE;
1223 return SCPE_OK;
1224 }
1225
1226 /* Set bad block routine */
1227
1228 t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc)
1229 {
1230 return pdp11_bad_block (uptr, HK_NUMSC, HK_NUMWD);
1231 }
1232
1233 #if defined (VM_PDP11)
1234
1235 /* Device bootstrap - does not clear CSR when done */
1236
1237 #define BOOT_START 02000 /* start */
1238 #define BOOT_ENTRY (BOOT_START + 002) /* entry */
1239 #define BOOT_UNIT (BOOT_START + 010) /* unit number */
1240 #define BOOT_CSR (BOOT_START + 014) /* CSR */
1241 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
1242
1243 static const uint16 boot_rom[] = {
1244 0042115, /* "MD" */
1245 0012706, BOOT_START, /* mov #boot_start, sp */
1246 0012700, 0000000, /* mov #unit, r0 */
1247 0012701, 0177440, /* mov #HKCS1, r1 */
1248 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */
1249 0010061, 0000010, /* mov r0, 10(r1) ; set unit */
1250 0016102, 0000012, /* mov 12(r1), r2 ; drv typ */
1251 0100375, /* bpl .-4 ; valid? */
1252 0042702, 0177377, /* bic #177377, r2 ; clr rest */
1253 0006302, /* asl r2 ; move */
1254 0006302, /* asl r2 */
1255 0012703, 0000003, /* mov #pack+go, r3 */
1256 0050203, /* bis r2, r3 ; merge type */
1257 0010311, /* mov r3, (r1); ; pack ack */
1258 0105711, /* tstb (r1) ; wait */
1259 0100376, /* bpl .-2 */
1260 0012761, 0177000, 0000002, /* mov #-512.,2(r1) ; set wc */
1261 0005061, 0000004, /* clr 4(r1) ; clr ba */
1262 0005061, 0000006, /* clr 6(r1) ; clr da */
1263 0005061, 0000020, /* clr 20(r1) ; clr cyl */
1264 0012703, 0000021, /* mov #read+go, r3 */
1265 0050203, /* bis r2, r3 ; merge type */
1266 0010311, /* mov r3, (r1); ; read */
1267 0105711, /* tstb (r1) ; wait */
1268 0100376, /* bpl .-2 */
1269 0005002, /* clr R2 */
1270 0005003, /* clr R3 */
1271 0012704, BOOT_START+020, /* mov #start+020, r4 */
1272 0005005, /* clr R5 */
1273 0005007 /* clr PC */
1274 };
1275
1276 t_stat hk_boot (int32 unitno, DEVICE *dptr)
1277 {
1278 int32 i;
1279 extern int32 saved_PC;
1280
1281 for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
1282 M[BOOT_UNIT >> 1] = unitno & CS2_M_UNIT;
1283 M[BOOT_CSR >> 1] = hk_dib.ba & DMASK;
1284 saved_PC = BOOT_ENTRY;
1285 return SCPE_OK;
1286 }
1287
1288 #else
1289
1290 t_stat hk_boot (int32 unitno, DEVICE *dptr)
1291 {
1292 return SCPE_NOFNC;
1293 }
1294
1295 #endif