First Commit of my working state
[simh.git] / HP2100 / hp2100_dp.c
1 /* hp2100_dp.c: HP 2100 12557A/13210A disk simulator
2
3 Copyright (c) 1993-2006, 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 PURPOSE 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 dp 12557A 2871 disk subsystem
27 13210A 7900 disk subsystem
28
29 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified)
30 01-Mar-05 JDB Added SET UNLOAD/LOAD
31 07-Oct-04 JDB Fixed enable/disable from either device
32 Fixed ANY ERROR status for 12557A interface
33 Fixed unattached drive status for 12557A interface
34 Status cmd without prior STC DC now completes (12557A)
35 OTA/OTB CC on 13210A interface also does CLC CC
36 Fixed RAR model
37 Fixed seek check on 13210 if sector out of range
38 20-Aug-04 JDB Fixes from Dave Bryan
39 - Check status on unattached drive set busy and not ready
40 - Check status tests wrong unit for write protect status
41 - Drive on line sets ATN, will set FLG if polling
42 15-Aug-04 RMS Controller resumes polling for ATN interrupts after
43 read status (found by Dave Bryan)
44 22-Jul-04 RMS Controller sets ATN for all commands except
45 read status (found by Dave Bryan)
46 21-Apr-04 RMS Fixed typo in boot loader (found by Dave Bryan)
47 26-Apr-04 RMS Fixed SFS x,C and SFC x,C
48 Fixed SR setting in IBL
49 Fixed interpretation of SR<0>
50 Revised IBL loader
51 Implemented DMA SRQ (follows FLG)
52 25-Apr-03 RMS Revised for extended file support
53 Fixed bug(s) in boot (found by Terry Newton)
54 10-Nov-02 RMS Added BOOT command, fixed numerous bugs
55 15-Jan-02 RMS Fixed INIT handling (found by Bill McDermith)
56 10-Jan-02 RMS Fixed f(x)write call (found by Bill McDermith)
57 03-Dec-01 RMS Changed DEVNO to use extended SET/SHOW
58 24-Nov-01 RMS Changed STA to be an array
59 07-Sep-01 RMS Moved function prototypes
60 29-Nov-00 RMS Made variable names unique
61 21-Nov-00 RMS Fixed flag, buffer power up state
62
63 The simulator uses a number of state variables:
64
65 dpc_busy set to drive number + 1 when the controller is busy
66 of the unit in use
67 dpd_xfer set to 1 if the data channel is executing a data transfer
68 dpd_wval set to 1 by OTx if either !dpc_busy or dpd_xfer
69 dpc_poll set to 1 if attention polling is enabled
70
71 dpc_busy and dpd_xfer are set together at the start of a read, write, refine,
72 or init. When data transfers are complete (CLC DC), dpd_xfer is cleared, but the
73 operation is not necessarily over. When the operation is complete, dpc_busy
74 is cleared and the command channel flag is set.
75
76 dpc_busy && !dpd_xfer && STC DC (controller is busy, data channel transfer has
77 been terminated by CLC DC, but a word has been placed in the data channel buffer)
78 indicates data overrun.
79
80 dpd_wval is used in write operations to fill out the sector buffer with 0's
81 if only a partial sector has been transferred.
82
83 dpc_poll indicates whether seek completion polling can occur. It is cleared
84 by reset and CLC CC and set by issuance of a seek or completion of check status.
85
86 The controller's "Record Address Register" (RAR) contains the CHS address of
87 the last Seek or Address Record command executed. The RAR is shared among
88 all drives on the controller. In addition, each drive has an internal
89 position register that contains the last cylinder position transferred to the
90 drive during Seek command execution (data operations always start with the
91 RAR head and sector position).
92
93 In a real drive, the address field of the sector under the head is read and
94 compared to the RAR. When they match, the target sector is under the head
95 and is ready for reading or writing. If a match doesn't occur, an Address
96 Error is indicated. In the simulator, the address field is obtained from the
97 drive's current position register during a read, i.e., the "on-disc" address
98 field is assumed to match the current position.
99
100 References:
101 - 7900A Disc Drive Operating and Service Manual (07900-90002, Feb-1975)
102 - 13210A Disc Drive Interface Kit Operating and Service Manual
103 (13210-90003, Nov-1974)
104 - 12557A Cartridge Disc Interface Kit Operating and Service Manual
105 (12557-90001, Sep-1970)
106
107 The following implemented behaviors have been inferred from secondary sources
108 (diagnostics, operating system drivers, etc.), due to absent or contradictory
109 authoritative information; future correction may be needed:
110
111 1. Status bit 15 (ATTENTION) does not set bit 0 (ANY ERROR) on the 12557A.
112 2. Omitting STC DC before Status Check does not set DC flag but does poll.
113 */
114
115 #include "hp2100_defs.h"
116
117 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
118 #define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */
119 #define UNIT_WLK (1 << UNIT_V_WLK)
120 #define UNIT_UNLOAD (1 << UNIT_V_UNLOAD)
121 #define FNC u3 /* saved function */
122 #define DRV u4 /* drive number (DC) */
123 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */
124
125 #define DP_N_NUMWD 7
126 #define DP_NUMWD (1 << DP_N_NUMWD) /* words/sector */
127 #define DP_NUMSC2 12 /* sectors/srf 12557 */
128 #define DP_NUMSC3 24 /* sectors/srf 13210 */
129 #define DP_NUMSC (dp_ctype? DP_NUMSC3: DP_NUMSC2)
130 #define DP_NUMSF 4 /* surfaces/cylinder */
131 #define DP_NUMCY 203 /* cylinders/disk */
132 #define DP_SIZE2 (DP_NUMSF * DP_NUMCY * DP_NUMSC2 * DP_NUMWD)
133 #define DP_SIZE3 (DP_NUMSF * DP_NUMCY * DP_NUMSC3 * DP_NUMWD)
134 #define DP_NUMDRV 4 /* # drives */
135
136 /* Command word */
137
138 #define CW_V_FNC 12 /* function */
139 #define CW_M_FNC 017
140 #define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC)
141 #define FNC_STA 000 /* status check */
142 #define FNC_WD 001 /* write */
143 #define FNC_RD 002 /* read */
144 #define FNC_SEEK 003 /* seek */
145 #define FNC_REF 005 /* refine */
146 #define FNC_CHK 006 /* check */
147 #define FNC_INIT 011 /* init */
148 #define FNC_AR 013 /* address */
149 #define FNC_SEEK1 020 /* fake - seek1 */
150 #define FNC_SEEK2 021 /* fake - seek2 */
151 #define FNC_SEEK3 022 /* fake - seek3 */
152 #define FNC_CHK1 023 /* fake - check1 */
153 #define FNC_AR1 024 /* fake - arec1 */
154 #define CW_V_DRV 0 /* drive */
155 #define CW_M_DRV 03
156 #define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV)
157
158 /* Disk address words */
159
160 #define DA_V_CYL 0 /* cylinder */
161 #define DA_M_CYL 0377
162 #define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL)
163 #define DA_V_HD 8 /* head */
164 #define DA_M_HD 03
165 #define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD)
166 #define DA_V_SC 0 /* sector */
167 #define DA_M_SC2 017
168 #define DA_M_SC3 037
169 #define DA_M_SC (dp_ctype? DA_M_SC3: DA_M_SC2)
170 #define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC)
171 #define DA_CKMASK2 037 /* check mask */
172 #define DA_CKMASK3 077
173 #define DA_CKMASK (dp_ctype? DA_CKMASK3: DA_CKMASK2)
174
175 /* Status in dpc_sta[drv], (u) = unused in 13210, (d) = dynamic */
176
177 #define STA_ATN 0100000 /* attention (u) */
178 #define STA_1ST 0040000 /* first status */
179 #define STA_OVR 0020000 /* overrun */
180 #define STA_RWU 0010000 /* rw unsafe NI (u) */
181 #define STA_ACU 0004000 /* access unsafe NI */
182 #define STA_HUNT 0002000 /* hunting NI (12557) */
183 #define STA_PROT 0002000 /* protected (13210) */
184 #define STA_SKI 0001000 /* incomplete NI (u) */
185 #define STA_SKE 0000400 /* seek error */
186 /* 0000200 /* unused */
187 #define STA_NRDY 0000100 /* not ready (d) */
188 #define STA_EOC 0000040 /* end of cylinder */
189 #define STA_AER 0000020 /* addr error */
190 #define STA_FLG 0000010 /* flagged */
191 #define STA_BSY 0000004 /* seeking */
192 #define STA_DTE 0000002 /* data error */
193 #define STA_ERR 0000001 /* any error (d) */
194
195 #define STA_ERSET2 (STA_1ST | STA_OVR | STA_RWU | STA_ACU | \
196 STA_SKI | STA_SKE | STA_NRDY | \
197 STA_EOC | STA_AER | STA_DTE) /* 12557A error set */
198 #define STA_ERSET3 (STA_ATN | STA_1ST | STA_OVR | STA_RWU | STA_ACU | \
199 STA_SKI | STA_SKE | STA_NRDY | STA_EOC | STA_AER | \
200 STA_FLG | STA_BSY | STA_DTE) /* 13210A error set */
201 #define STA_ANYERR (dp_ctype ? STA_ERSET3 : STA_ERSET2)
202
203 #define STA_UNLOADED (dp_ctype ? (STA_NRDY | STA_BSY) : STA_NRDY)
204 #define STA_MBZ13 (STA_ATN | STA_RWU | STA_SKI) /* zero in 13210 */
205
206 extern uint32 PC, SR;
207 extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2];
208 extern int32 sim_switches;
209
210 int32 dp_ctype = 1; /* ctrl type */
211 int32 dpc_busy = 0; /* cch unit */
212 int32 dpc_poll = 0; /* cch poll enable */
213 int32 dpc_cnt = 0; /* check count */
214 int32 dpc_eoc = 0; /* end of cyl */
215 int32 dpc_stime = 100; /* seek time */
216 int32 dpc_ctime = 100; /* command time */
217 int32 dpc_xtime = 5; /* xfer time */
218 int32 dpc_dtime = 2; /* dch time */
219 int32 dpd_obuf = 0, dpd_ibuf = 0; /* dch buffers */
220 int32 dpc_obuf = 0; /* cch buffers */
221 int32 dpd_xfer = 0; /* xfer in prog */
222 int32 dpd_wval = 0; /* write data valid */
223 int32 dp_ptr = 0; /* buffer ptr */
224 uint8 dpc_rarc = 0; /* RAR cylinder */
225 uint8 dpc_rarh = 0; /* RAR head */
226 uint8 dpc_rars = 0; /* RAR sector */
227 uint8 dpc_ucyl[DP_NUMDRV] = { 0 }; /* unit cylinder */
228 uint16 dpc_sta[DP_NUMDRV] = { 0 }; /* status regs */
229 uint16 dpxb[DP_NUMWD]; /* sector buffer */
230
231 DEVICE dpd_dev, dpc_dev;
232 int32 dpdio (int32 inst, int32 IR, int32 dat);
233 int32 dpcio (int32 inst, int32 IR, int32 dat);
234 t_stat dpc_svc (UNIT *uptr);
235 t_stat dpd_svc (UNIT *uptr);
236 t_stat dpc_reset (DEVICE *dptr);
237 t_stat dpc_attach (UNIT *uptr, char *cptr);
238 t_stat dpc_detach (UNIT* uptr);
239 t_stat dpc_boot (int32 unitno, DEVICE *dptr);
240 void dp_god (int32 fnc, int32 drv, int32 time);
241 void dp_goc (int32 fnc, int32 drv, int32 time);
242 t_stat dpc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc);
243 t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc);
244 t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);
245
246 /* DPD data structures
247
248 dpd_dev DPD device descriptor
249 dpd_unit DPD unit list
250 dpd_reg DPD register list
251 */
252
253 DIB dp_dib[] = {
254 { DPD, 0, 0, 0, 0, 0, &dpdio },
255 { DPC, 0, 0, 0, 0, 0, &dpcio }
256 };
257
258 #define dpd_dib dp_dib[0]
259 #define dpc_dib dp_dib[1]
260
261 UNIT dpd_unit = { UDATA (&dpd_svc, 0, 0) };
262
263 REG dpd_reg[] = {
264 { ORDATA (IBUF, dpd_ibuf, 16) },
265 { ORDATA (OBUF, dpd_obuf, 16) },
266 { BRDATA (DBUF, dpxb, 8, 16, DP_NUMWD) },
267 { DRDATA (BPTR, dp_ptr, DP_N_NUMWD) },
268 { FLDATA (CMD, dpd_dib.cmd, 0) },
269 { FLDATA (CTL, dpd_dib.ctl, 0) },
270 { FLDATA (FLG, dpd_dib.flg, 0) },
271 { FLDATA (FBF, dpd_dib.fbf, 0) },
272 { FLDATA (SRQ, dpd_dib.srq, 0) },
273 { FLDATA (XFER, dpd_xfer, 0) },
274 { FLDATA (WVAL, dpd_wval, 0) },
275 { ORDATA (DEVNO, dpd_dib.devno, 6), REG_HRO },
276 { NULL }
277 };
278
279 MTAB dpd_mod[] = {
280 { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO",
281 &hp_setdev, &hp_showdev, &dpd_dev },
282 { 0 }
283 };
284
285 DEVICE dpd_dev = {
286 "DPD", &dpd_unit, dpd_reg, dpd_mod,
287 1, 10, DP_N_NUMWD, 1, 8, 16,
288 NULL, NULL, &dpc_reset,
289 NULL, NULL, NULL,
290 &dpd_dib, DEV_DISABLE
291 };
292
293 /* DPC data structures
294
295 dpc_dev DPC device descriptor
296 dpc_unit DPC unit list
297 dpc_reg DPC register list
298 dpc_mod DPC modifier list
299 */
300
301 UNIT dpc_unit[] = {
302 { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |
303 UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) },
304 { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |
305 UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) },
306 { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |
307 UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) },
308 { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |
309 UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) }
310 };
311
312 REG dpc_reg[] = {
313 { ORDATA (OBUF, dpc_obuf, 16) },
314 { ORDATA (BUSY, dpc_busy, 4), REG_RO },
315 { ORDATA (CNT, dpc_cnt, 5) },
316 { FLDATA (CMD, dpc_dib.cmd, 0) },
317 { FLDATA (CTL, dpc_dib.ctl, 0) },
318 { FLDATA (FLG, dpc_dib.flg, 0) },
319 { FLDATA (FBF, dpc_dib.fbf, 0) },
320 { FLDATA (SRQ, dpc_dib.srq, 0) },
321 { FLDATA (EOC, dpc_eoc, 0) },
322 { FLDATA (POLL, dpc_poll, 0) },
323 { DRDATA (RARC, dpc_rarc, 8), PV_RZRO },
324 { DRDATA (RARH, dpc_rarh, 2), PV_RZRO },
325 { DRDATA (RARS, dpc_rars, 5), PV_RZRO },
326 { BRDATA (CYL, dpc_ucyl, 10, 8, DP_NUMDRV), PV_RZRO },
327 { BRDATA (STA, dpc_sta, 8, 16, DP_NUMDRV) },
328 { DRDATA (CTIME, dpc_ctime, 24), PV_LEFT },
329 { DRDATA (DTIME, dpc_dtime, 24), PV_LEFT },
330 { DRDATA (STIME, dpc_stime, 24), PV_LEFT },
331 { DRDATA (XTIME, dpc_xtime, 24), REG_NZ | PV_LEFT },
332 { FLDATA (CTYPE, dp_ctype, 0), REG_HRO },
333 { URDATA (UFNC, dpc_unit[0].FNC, 8, 8, 0,
334 DP_NUMDRV, REG_HRO) },
335 { URDATA (CAPAC, dpc_unit[0].capac, 10, T_ADDR_W, 0,
336 DP_NUMDRV, PV_LEFT | REG_HRO) },
337 { ORDATA (DEVNO, dpc_dib.devno, 6), REG_HRO },
338 { NULL }
339 };
340
341 MTAB dpc_mod[] = {
342 { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", dpc_load_unload },
343 { UNIT_UNLOAD, 0, "heads loaded", "LOADED", dpc_load_unload },
344 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
345 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
346 { MTAB_XTD | MTAB_VDV, 1, NULL, "13210A",
347 &dp_settype, NULL, NULL },
348 { MTAB_XTD | MTAB_VDV, 0, NULL, "12557A",
349 &dp_settype, NULL, NULL },
350 { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL,
351 NULL, &dp_showtype, NULL },
352 { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO",
353 &hp_setdev, &hp_showdev, &dpd_dev },
354 { 0 }
355 };
356
357 DEVICE dpc_dev = {
358 "DPC", dpc_unit, dpc_reg, dpc_mod,
359 DP_NUMDRV, 8, 24, 1, 8, 16,
360 NULL, NULL, &dpc_reset,
361 &dpc_boot, &dpc_attach, &dpc_detach,
362 &dpc_dib, DEV_DISABLE
363 };
364
365 /* IO instructions */
366
367 int32 dpdio (int32 inst, int32 IR, int32 dat)
368 {
369 int32 devd;
370
371 devd = IR & I_DEVMASK; /* get device no */
372 switch (inst) { /* case on opcode */
373
374 case ioFLG: /* flag clear/set */
375 if ((IR & I_HC) == 0) { setFSR (devd); } /* STF */
376 break;
377
378 case ioSFC: /* skip flag clear */
379 if (FLG (devd) == 0) PC = (PC + 1) & VAMASK;
380 break;
381
382 case ioSFS: /* skip flag set */
383 if (FLG (devd) != 0) PC = (PC + 1) & VAMASK;
384 break;
385
386 case ioOTX: /* output */
387 dpd_obuf = dat;
388 if (!dpc_busy || dpd_xfer) dpd_wval = 1; /* if !overrun, valid */
389 break;
390
391 case ioMIX: /* merge */
392 dat = dat | dpd_ibuf;
393 break;
394
395 case ioLIX: /* load */
396 dat = dpd_ibuf;
397 break;
398
399 case ioCRS: /* control reset (action unverif) */
400 case ioCTL: /* control clear/set */
401 if (IR & I_CTL) { /* CLC */
402 clrCTL (devd); /* clr ctl, cmd */
403 clrCMD (devd);
404 dpd_xfer = 0; /* clr xfer */
405 }
406 else { /* STC */
407 if (!dp_ctype) setCTL (devd); /* 12557: set ctl */
408 setCMD (devd); /* set cmd */
409 if (dpc_busy && !dpd_xfer) /* overrun? */
410 dpc_sta[dpc_busy - 1] |= STA_OVR;
411 }
412 break;
413
414 default:
415 break;
416 }
417
418 if (IR & I_HC) { clrFSR (devd); } /* H/C option */
419 return dat;
420 }
421
422 int32 dpcio (int32 inst, int32 IR, int32 dat)
423 {
424 int32 i, devc, fnc, drv;
425 int32 devd = dpd_dib.devno;
426
427 devc = IR & I_DEVMASK; /* get device no */
428 switch (inst) { /* case on opcode */
429
430 case ioFLG: /* flag clear/set */
431 if ((IR & I_HC) == 0) { setFSR (devc); } /* STF */
432 break;
433
434 case ioSFC: /* skip flag clear */
435 if (FLG (devc) == 0) PC = (PC + 1) & VAMASK;
436 break;
437
438 case ioSFS: /* skip flag set */
439 if (FLG (devc) != 0) PC = (PC + 1) & VAMASK;
440 break;
441
442 case ioLIX: /* load */
443 dat = 0;
444 case ioMIX: /* merge */
445 for (i = 0; i < DP_NUMDRV; i++)
446 if (dpc_sta[i] & STA_ATN) dat = dat | (1 << i);
447 break;
448
449 case ioOTX: /* output */
450 dpc_obuf = dat;
451 if (!dp_ctype) break;
452 IR = IR | I_CTL; /* 13210 OTx causes CLC */
453
454 case ioCRS: /* control reset (action unverif) */
455 case ioCTL: /* control clear/set */
456 if (IR & I_CTL) { /* CLC? */
457 clrCTL (devc); /* clr cmd, ctl */
458 clrCMD (devc); /* cancel non-seek */
459 if (dpc_busy) sim_cancel (&dpc_unit[dpc_busy - 1]);
460 sim_cancel (&dpd_unit); /* cancel dch */
461 dpd_xfer = 0; /* clr dch xfer */
462 dpc_busy = 0; /* clr cch busy */
463 dpc_poll = 0; /* clr cch poll */
464 }
465 else { /* STC */
466 setCTL (devc); /* set ctl */
467 if (!CMD (devc)) { /* is cmd clr? */
468 setCMD (devc); /* set cmd */
469 drv = CW_GETDRV (dpc_obuf); /* get fnc, drv */
470 fnc = CW_GETFNC (dpc_obuf); /* from cmd word */
471 switch (fnc) { /* case on fnc */
472
473 case FNC_SEEK: /* seek */
474 dpc_poll = 1; /* enable polling */
475 dp_god (fnc, drv, dpc_dtime); /* sched dch xfr */
476 break;
477
478 case FNC_STA: /* rd sta */
479 if (dp_ctype) { clrFSR (devd); } /* 13210? clr dch flag */
480 case FNC_CHK: /* check */
481 case FNC_AR: /* addr rec */
482 dp_god (fnc, drv, dpc_dtime); /* sched dch xfr */
483 break;
484
485 case FNC_RD: case FNC_WD: /* read, write */
486 case FNC_REF: case FNC_INIT: /* refine, init */
487 dp_goc (fnc, drv, dpc_ctime); /* sched drive */
488 break;
489 } /* end case */
490 } /* end if */
491 } /* end else */
492 break;
493
494 default:
495 break;
496 }
497
498 if (IR & I_HC) { clrFSR (devc); } /* H/C option */
499 return dat;
500 }
501
502 /* Start data channel operation */
503
504 void dp_god (int32 fnc, int32 drv, int32 time)
505 {
506 dpd_unit.DRV = drv; /* save unit */
507 dpd_unit.FNC = fnc; /* save function */
508 sim_activate (&dpd_unit, time);
509 return;
510 }
511
512 /* Start controller operation */
513
514 void dp_goc (int32 fnc, int32 drv, int32 time)
515 {
516 int32 t;
517
518 if (t = sim_is_active (&dpc_unit[drv])) { /* still seeking? */
519 sim_cancel (&dpc_unit[drv]); /* stop seek */
520 dpc_sta[drv] = dpc_sta[drv] & ~STA_BSY; /* clear busy */
521 time = time + t; /* include seek time */
522 }
523 dp_ptr = 0; /* init buf ptr */
524 dpc_eoc = 0; /* clear end cyl */
525 dpc_busy = drv + 1; /* set busy */
526 dpd_xfer = 1; /* xfer in prog */
527 dpc_unit[drv].FNC = fnc; /* save function */
528 dpc_sta[drv] = dpc_sta[drv] & ~STA_ATN; /* clear ATN */
529 sim_activate (&dpc_unit[drv], time); /* activate unit */
530 return;
531 }
532
533 /* Data channel unit service
534
535 This routine handles the data channel transfers. It also handles
536 data transfers that are blocked by seek in progress.
537
538 uptr->DRV = target drive
539 uptr->FNC = target function
540
541 Seek substates
542 seek - transfer cylinder
543 seek1 - transfer head/surface
544 Address record
545 ar - transfer cylinder
546 ar1 - transfer head/surface, finish operation
547 Status check - transfer status, finish operation
548 Check data
549 chk - transfer sector count
550 */
551
552 t_stat dpd_svc (UNIT *uptr)
553 {
554 int32 i, drv, devc, devd, st;
555
556 drv = uptr->DRV; /* get drive no */
557 devc = dpc_dib.devno; /* get cch devno */
558 devd = dpd_dib.devno; /* get dch devno */
559 switch (uptr->FNC) { /* case function */
560
561 case FNC_AR: /* arec, need cyl */
562 case FNC_SEEK: /* seek, need cyl */
563 if (CMD (devd)) { /* dch active? */
564 dpc_rarc = DA_GETCYL (dpd_obuf); /* set RAR from cyl word */
565 dpd_wval = 0; /* clr data valid */
566 setFSR (devd); /* set dch flg */
567 clrCMD (devd); /* clr dch cmd */
568 if (uptr->FNC == FNC_AR) uptr->FNC = FNC_AR1;
569 else uptr->FNC = FNC_SEEK1; /* advance state */
570 }
571 sim_activate (uptr, dpc_xtime); /* no, wait more */
572 break;
573
574 case FNC_AR1: /* arec, need hd/sec */
575 case FNC_SEEK1: /* seek, need hd/sec */
576 if (CMD (devd)) { /* dch active? */
577 dpc_rarh = DA_GETHD (dpd_obuf); /* set RAR from head */
578 dpc_rars = DA_GETSC (dpd_obuf); /* set RAR from sector */
579 dpd_wval = 0; /* clr data valid */
580 setFSR (devd); /* set dch flg */
581 clrCMD (devd); /* clr dch cmd */
582 if (uptr->FNC == FNC_AR1) {
583 setFSR (devc); /* set cch flg */
584 clrCMD (devc); /* clr cch cmd */
585 dpc_sta[drv] = dpc_sta[drv] | STA_ATN; /* set drv attn */
586 break; /* done if Address Record */
587 }
588 if (sim_is_active (&dpc_unit[drv])) { /* if busy, */
589 dpc_sta[drv] = dpc_sta[drv] | STA_SKE; /* seek check */
590 break; /* allow prev seek to cmpl */
591 }
592 if ((dpc_rarc >= DP_NUMCY) || /* invalid cyl? */
593 (dp_ctype && (dpc_rars >= DP_NUMSC3))) { /* invalid sector? (13210A) */
594 dpc_sta[drv] = dpc_sta[drv] | STA_SKE; /* seek check */
595 sim_activate (&dpc_unit[drv], 1); /* schedule drive no-wait */
596 dpc_unit[drv].FNC = FNC_SEEK3; /* do immed compl w/poll */
597 break;
598 }
599 st = abs (dpc_rarc - dpc_ucyl[drv]) * dpc_stime;
600 if (st == 0) st = dpc_stime; /* min time */
601 dpc_ucyl[drv] = dpc_rarc; /* transfer RAR */
602 sim_activate (&dpc_unit[drv], st); /* schedule drive */
603 dpc_sta[drv] = (dpc_sta[drv] | STA_BSY) &
604 ~(STA_SKE | STA_SKI | STA_HUNT);
605 dpc_unit[drv].FNC = FNC_SEEK2; /* set operation */
606 }
607 else sim_activate (uptr, dpc_xtime); /* no, wait more */
608 break;
609
610 case FNC_STA: /* read status */
611 if (CMD (devd) || dp_ctype) { /* dch act or 13210? */
612 if ((dpc_unit[drv].flags & UNIT_UNLOAD) == 0) { /* drive up? */
613 dpd_ibuf = dpc_sta[drv] & ~STA_ERR; /* clear err */
614 if (dp_ctype) dpd_ibuf = /* 13210? */
615 (dpd_ibuf & ~(STA_MBZ13 | STA_PROT)) |
616 (dpc_unit[drv].flags & UNIT_WPRT? STA_PROT: 0);
617 }
618 else dpd_ibuf = STA_UNLOADED; /* not ready */
619 if (dpd_ibuf & STA_ANYERR) /* errors? set flg */
620 dpd_ibuf = dpd_ibuf | STA_ERR;
621 setFSR (devd); /* set dch flg */
622 clrCMD (devd); /* clr dch cmd */
623 clrCMD (devc); /* clr cch cmd */
624 }
625 dpc_sta[drv] = dpc_sta[drv] & /* clr sta flags */
626 ~(STA_ATN | STA_1ST | STA_OVR |
627 STA_RWU | STA_ACU | STA_EOC |
628 STA_AER | STA_FLG | STA_DTE);
629 dpc_poll = 1; /* enable polling */
630 for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */
631 if (dpc_sta[i] & STA_ATN) { /* any ATN set? */
632 setFSR (devc); /* set cch flg */
633 break;
634 }
635 }
636 break;
637
638 case FNC_CHK: /* check, need cnt */
639 if (CMD (devd)) { /* dch active? */
640 dpc_cnt = dpd_obuf & DA_CKMASK; /* get count */
641 dpd_wval = 0; /* clr data valid */
642 dp_goc (FNC_CHK1, drv, dpc_xtime); /* sched drv */
643 }
644 else sim_activate (uptr, dpc_xtime); /* wait more */
645 break;
646
647 default:
648 return SCPE_IERR;
649 }
650
651 return SCPE_OK;
652 }
653
654 /* Drive unit service
655
656 This routine handles the data transfers.
657
658 Seek substates
659 seek2 - done
660 Refine sector - erase sector, finish operation
661 Check data
662 chk1 - finish operation
663 Read
664 Write
665 */
666
667 #define GETDA(x,y,z) \
668 (((((x) * DP_NUMSF) + (y)) * DP_NUMSC) + (z)) * DP_NUMWD
669
670 t_stat dpc_svc (UNIT *uptr)
671 {
672 int32 da, drv, devc, devd, err;
673
674 err = 0; /* assume no err */
675 drv = uptr - dpc_dev.units; /* get drive no */
676 devc = dpc_dib.devno; /* get cch devno */
677 devd = dpd_dib.devno; /* get dch devno */
678 if (uptr->flags & UNIT_UNLOAD) { /* drive down? */
679 setFSR (devc); /* set cch flg */
680 clrCMD (devc); /* clr cch cmd */
681 dpc_sta[drv] = 0; /* clr status */
682 dpc_busy = 0; /* ctlr is free */
683 dpc_poll = 0; /* polling disabled */
684 dpd_xfer = 0;
685 dpd_wval = 0;
686 return SCPE_OK;
687 }
688 switch (uptr->FNC) { /* case function */
689
690 case FNC_SEEK2: /* positioning done */
691 dpc_sta[drv] = (dpc_sta[drv] | STA_ATN) & ~STA_BSY; /* fall into cmpl */
692 case FNC_SEEK3: /* seek complete */
693 if (dpc_poll) { /* polling enabled? */
694 setFSR (devc); /* set cch flg */
695 clrCMD (devc); /* clear cmd */
696 }
697 return SCPE_OK;
698
699 case FNC_REF: /* refine sector */
700 break; /* just a NOP */
701
702 case FNC_RD: /* read */
703 case FNC_CHK1: /* check */
704 if (dp_ptr == 0) { /* new sector? */
705 if (!CMD (devd) && (uptr->FNC != FNC_CHK1)) break;
706 if (dpc_rarc != dpc_ucyl[drv]) /* RAR cyl miscompare? */
707 dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* set flag, read */
708 if (dpc_rars >= DP_NUMSC) { /* bad sector? */
709 dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* set flag, stop */
710 break;
711 }
712 if (dpc_eoc) { /* end of cyl? */
713 dpc_sta[drv] = dpc_sta[drv] | STA_EOC;
714 break;
715 }
716 da = GETDA (dpc_rarc, dpc_rarh, dpc_rars); /* calc disk addr */
717 dpc_rars = (dpc_rars + 1) % DP_NUMSC; /* incr sector */
718 if (dpc_rars == 0) { /* wrap? */
719 dpc_rarh = dpc_rarh ^ 1; /* incr head */
720 dpc_eoc = ((dpc_rarh & 1) == 0); /* calc eoc */
721 }
722 if (err = fseek (uptr->fileref, da * sizeof (int16),
723 SEEK_SET)) break;
724 fxread (dpxb, sizeof (int16), DP_NUMWD, uptr->fileref);
725 if (err = ferror (uptr->fileref)) break;
726 }
727 dpd_ibuf = dpxb[dp_ptr++]; /* get word */
728 if (dp_ptr >= DP_NUMWD) { /* end of sector? */
729 if (uptr->FNC == FNC_CHK1) { /* check? */
730 dpc_cnt = (dpc_cnt - 1) & DA_CKMASK; /* decr count */
731 if (dpc_cnt == 0) break; /* stop at zero */
732 }
733 dp_ptr = 0; /* wrap buf ptr */
734 }
735 if (CMD (devd) && dpd_xfer) { /* dch on, xfer? */
736 setFSR (devd); /* set flag */
737 }
738 clrCMD (devd); /* clr dch cmd */
739 sim_activate (uptr, dpc_xtime); /* sched next word */
740 return SCPE_OK;
741
742 case FNC_INIT: /* init */
743 case FNC_WD: /* write */
744 if (dp_ptr == 0) { /* start sector? */
745 if (!CMD (devd) && !dpd_wval) break; /* xfer done? */
746 if (uptr->flags & UNIT_WPRT) { /* wr prot? */
747 dpc_sta[drv] = dpc_sta[drv] | STA_FLG; /* set status */
748 break; /* done */
749 }
750 if ((dpc_rarc != dpc_ucyl[drv]) || /* RAR cyl miscompare? */
751 (dpc_rars >= DP_NUMSC)) { /* bad sector? */
752 dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* address error */
753 break;
754 }
755 if (dpc_eoc) { /* end of cyl? */
756 dpc_sta[drv] = dpc_sta[drv] | STA_EOC; /* set status */
757 break; /* done */
758 }
759 }
760 dpxb[dp_ptr++] = dpd_wval? dpd_obuf: 0; /* store word/fill */
761 dpd_wval = 0; /* clr data valid */
762 if (dp_ptr >= DP_NUMWD) { /* buffer full? */
763 da = GETDA (dpc_rarc, dpc_rarh, dpc_rars); /* calc disk addr */
764 dpc_rars = (dpc_rars + 1) % DP_NUMSC; /* incr sector */
765 if (dpc_rars == 0) { /* wrap? */
766 dpc_rarh = dpc_rarh ^ 1; /* incr head */
767 dpc_eoc = ((dpc_rarh & 1) == 0); /* calc eoc */
768 }
769 if (err = fseek (uptr->fileref, da * sizeof (int16),
770 SEEK_SET)) break;
771 fxwrite (dpxb, sizeof (int16), DP_NUMWD, uptr->fileref);
772 if (err = ferror (uptr->fileref)) break; /* error? */
773 dp_ptr = 0; /* next sector */
774 }
775 if (CMD (devd) && dpd_xfer) { /* dch on, xfer? */
776 setFSR (devd); /* set flag */
777 }
778 clrCMD (devd); /* clr dch cmd */
779 sim_activate (uptr, dpc_xtime); /* sched next word */
780 return SCPE_OK;
781
782 default:
783 return SCPE_IERR;
784 } /* end case fnc */
785
786 dpc_sta[drv] = dpc_sta[drv] | STA_ATN; /* set ATN */
787 setFSR (devc); /* set cch flg */
788 clrCMD (devc); /* clr cch cmd */
789 dpc_busy = 0; /* ctlr is free */
790 dpd_xfer = dpd_wval = 0;
791 if (err != 0) { /* error? */
792 perror ("DP I/O error");
793 clearerr (uptr->fileref);
794 return SCPE_IOERR;
795 }
796 return SCPE_OK;
797 }
798
799 /* Reset routine */
800
801 t_stat dpc_reset (DEVICE *dptr)
802 {
803 int32 drv;
804
805 hp_enbdis_pair (dptr, /* make pair cons */
806 (dptr == &dpd_dev)? &dpc_dev: &dpd_dev);
807 dpd_ibuf = dpd_obuf = 0; /* clear buffers */
808 dpc_busy = dpc_obuf = 0;
809 dpc_eoc = 0;
810 dpc_poll = 0;
811 dpd_xfer = dpd_wval = 0;
812 dp_ptr = 0;
813 dpc_rarc = dpc_rarh = dpc_rars = 0; /* clear RAR */
814 dpc_dib.cmd = dpd_dib.cmd = 0; /* clear cmd */
815 dpc_dib.ctl = dpd_dib.ctl = 0; /* clear ctl */
816 dpc_dib.fbf = dpd_dib.fbf = 1; /* set fbf */
817 dpc_dib.flg = dpd_dib.flg = 1; /* set flg */
818 dpc_dib.srq = dpd_dib.flg = 1; /* srq follows flg */
819 sim_cancel (&dpd_unit); /* cancel dch */
820 for (drv = 0; drv < DP_NUMDRV; drv++) { /* loop thru drives */
821 sim_cancel (&dpc_unit[drv]); /* cancel activity */
822 dpc_unit[drv].FNC = 0; /* clear function */
823 dpc_ucyl[drv] = 0; /* clear drive pos */
824 if (dpc_unit[drv].flags & UNIT_ATT)
825 dpc_sta[drv] = dpc_sta[drv] & STA_1ST; /* first seek status */
826 else dpc_sta[drv] = 0; /* clear status */
827 }
828 return SCPE_OK;
829 }
830
831 /* Attach routine */
832
833 t_stat dpc_attach (UNIT *uptr, char *cptr)
834 {
835 t_stat r;
836
837 r = attach_unit (uptr, cptr); /* attach unit */
838 if (r == SCPE_OK) dpc_load_unload (uptr, 0, NULL, NULL);/* if OK, load heads */
839 return r;
840 }
841
842 /* Detach routine */
843
844 t_stat dpc_detach (UNIT* uptr)
845 {
846 dpc_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads */
847 return detach_unit (uptr); /* detach unit */
848 }
849
850 /* Load and unload heads */
851
852 t_stat dpc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc)
853 {
854 uint32 drv;
855
856 if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to load */
857
858 if (value == UNIT_UNLOAD) /* unload heads? */
859 uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */
860 else { /* load heads */
861 uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */
862 drv = uptr - dpc_dev.units; /* get drive no */
863 dpc_sta[drv] = dpc_sta[drv] | STA_ATN | STA_1ST; /* update status */
864 if (dpc_poll) { /* polling enabled? */
865 dpc_dib.fbf = 1; /* set fbf */
866 dpc_dib.flg = 1; /* set flg */
867 dpc_dib.srq = 1; /* srq follows flg */
868 }
869 }
870 return SCPE_OK;
871 }
872
873 /* Set controller type */
874
875 t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc)
876 {
877 int32 i;
878
879 if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG;
880 for (i = 0; i < DP_NUMDRV; i++) {
881 if (dpc_unit[i].flags & UNIT_ATT) return SCPE_ALATT;
882 }
883 for (i = 0; i < DP_NUMDRV; i++)
884 dpc_unit[i].capac = (val? DP_SIZE3: DP_SIZE2);
885 dp_ctype = val;
886 return SCPE_OK;
887 }
888
889 /* Show controller type */
890
891 t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)
892 {
893 if (dp_ctype) fprintf (st, "13210A");
894 else fprintf (st, "12557A");
895 return SCPE_OK;
896 }
897
898 /* 7900/7901 bootstrap routine (HP 12992F ROM) */
899
900 const uint16 dp_rom[IBL_LNT] = {
901 0106710, /*ST CLC DC ; clr dch */
902 0106711, /* CLC CC ; clr cch */
903 0017757, /* JSB STAT ; get status */
904 0067746, /*SK LDB SKCMD ; seek cmd */
905 0106610, /* OTB DC ; cyl # */
906 0103710, /* STC DC,C ; to dch */
907 0106611, /* OTB CC ; seek cmd */
908 0103711, /* STC CC,C ; to cch */
909 0102310, /* SFS DC ; addr wd ok? */
910 0027710, /* JMP *-1 ; no, wait */
911 0006400, /* CLB */
912 0102501, /* LIA 1 ; read switches */
913 0002011, /* SLA,RSS ; <0> set? */
914 0047747, /* ADB BIT9 ; head 2 = removable */
915 0106610, /* OTB DC ; head/sector */
916 0103710, /* STC DC,C ; to dch */
917 0102311, /* SFS CC ; seek done? */
918 0027720, /* JMP *-1 ; no, wait */
919 0017757, /* JSB STAT ; get status */
920 0067776, /* LDB DMACW ; DMA control */
921 0106606, /* OTB 6 */
922 0067750, /* LDB ADDR1 ; memory addr */
923 0106602, /* OTB 2 */
924 0102702, /* STC 2 ; flip DMA ctrl */
925 0067752, /* LDB CNT ; word count */
926 0106602, /* OTB 2 */
927 0063745, /* LDB RDCMD ; read cmd */
928 0102611, /* OTA CC ; to cch */
929 0103710, /* STC DC,C ; start dch */
930 0103706, /* STC 6,C ; start DMA */
931 0103711, /* STC CC,C ; start cch */
932 0102311, /* SFS CC ; done? */
933 0027737, /* JMP *-1 ; no, wait */
934 0017757, /* JSB STAT ; get status */
935 0027775, /* JMP XT ; done */
936 0037766, /*FSMSK 037766 ; status mask */
937 0004000, /*STMSK 004000 ; unsafe mask */
938 0020000, /*RDCMD 020000 ; read cmd */
939 0030000, /*SKCMD 030000 ; seek cmd */
940 0001000, /*BIT9 001000 ; head 2 select */
941 0102011, /*ADDR1 102011 */
942 0102055, /*ADDR2 102055 */
943 0164000, /*CNT -6144. */
944 0, 0, 0, 0, /* unused */
945 0000000, /*STAT 0 */
946 0002400, /* CLA ; status request */
947 0102611, /* OTC CC ; to cch */
948 0103711, /* STC CC,C ; start cch */
949 0102310, /* SFS DC ; done? */
950 0027763, /* JMP *-1 */
951 0102510, /* LIA DC ; get status */
952 0013743, /* AND FSMSK ; mask 15,14,3,0 */
953 0002003, /* SZA,RSS ; drive ready? */
954 0127757, /* JMP STAT,I ; yes */
955 0013744, /* AND STMSK ; fault? */
956 0002002, /* SZA */
957 0102030, /* HLT 30 ; yes */
958 0027700, /* JMP ST ; no, retry */
959 0117751, /*XT JSB ADDR2,I ; start program */
960 0120010, /*DMACW 120000+DC */
961 0000000 /* -ST */
962 };
963
964 t_stat dpc_boot (int32 unitno, DEVICE *dptr)
965 {
966 int32 dev;
967
968 if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */
969 dev = dpd_dib.devno; /* get data chan dev */
970 if (ibl_copy (dp_rom, dev)) return SCPE_IERR; /* copy boot to memory */
971 SR = (SR & IBL_OPT) | IBL_DP | (dev << IBL_V_DEV); /* set SR */
972 if (sim_switches & SWMASK ('R')) SR = SR | IBL_DP_REM; /* boot from removable? */
973 return SCPE_OK;
974 }