First Commit of my working state
[simh.git] / HP2100 / hp2100_dq.c
CommitLineData
196ba1fc
PH
1/* hp2100_dq.c: HP 2100 12565A disk simulator\r
2\r
3 Copyright (c) 1993-2006, Bill McDermith\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of the author shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from the author.\r
25\r
26 dq 12565A 2883 disk system\r
27\r
28 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified)\r
29 01-Mar-05 JDB Added SET UNLOAD/LOAD\r
30 07-Oct-04 JDB Fixed enable/disable from either device\r
31 Shortened xtime from 5 to 3 (drive avg 156KW/second)\r
32 Fixed not ready/any error status\r
33 Fixed RAR model\r
34 21-Apr-04 RMS Fixed typo in boot loader (found by Dave Bryan)\r
35 26-Apr-04 RMS Fixed SFS x,C and SFC x,C\r
36 Fixed SR setting in IBL\r
37 Revised IBL loader\r
38 Implemented DMA SRQ (follows FLG)\r
39 25-Apr-03 RMS Fixed bug in status check\r
40 10-Nov-02 RMS Added boot command, rebuilt like 12559/13210\r
41 09-Jan-02 WOM Copied dp driver and mods for 2883\r
42\r
43 Differences between 12559/13210 and 12565 controllers\r
44 - 12565 stops transfers on address miscompares; 12559/13210 only stops writes\r
45 - 12565 does not set error on positioner busy\r
46 - 12565 does not set positioner busy if already on cylinder\r
47 - 12565 does not need eoc logic, it will hit an invalid head number\r
48\r
49 The controller's "Record Address Register" (RAR) contains the CHS address of\r
50 the last Position or Load Address command executed. The RAR is shared among\r
51 all drives on the controller. In addition, each drive has an internal\r
52 position register that contains the last cylinder and head position\r
53 transferred to the drive during Position command execution (sector operations\r
54 always start with the RAR sector position).\r
55\r
56 In a real drive, the address field of the sector under the head is read and\r
57 compared to the RAR. When they match, the target sector is under the head\r
58 and is ready for reading or writing. If a match doesn't occur, an Address\r
59 Error is indicated. In the simulator, the address field is obtained from the\r
60 drive's current position register during a read, i.e., the "on-disc" address\r
61 field is assumed to match the current position.\r
62\r
63 Reference:\r
64 - 12565A Disc Interface Kit Operating and Service Manual (12565-90003, Aug-1973)\r
65\r
66 The following implemented behaviors have been inferred from secondary sources\r
67 (diagnostics, operating system drivers, etc.), due to absent or contradictory\r
68 authoritative information; future correction may be needed:\r
69\r
70 1. Read Address command starts at the sector number in the RAR.\r
71*/\r
72\r
73#include "hp2100_defs.h"\r
74\r
75#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */\r
76#define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */\r
77#define UNIT_WLK (1 << UNIT_V_WLK)\r
78#define UNIT_UNLOAD (1 << UNIT_V_UNLOAD)\r
79#define FNC u3 /* saved function */\r
80#define DRV u4 /* drive number (DC) */\r
81#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */\r
82\r
83#define DQ_N_NUMWD 7\r
84#define DQ_NUMWD (1 << DQ_N_NUMWD) /* words/sector */\r
85#define DQ_NUMSC 23 /* sectors/track */\r
86#define DQ_NUMSF 20 /* tracks/cylinder */\r
87#define DQ_NUMCY 203 /* cylinders/disk */\r
88#define DQ_SIZE (DQ_NUMSF * DQ_NUMCY * DQ_NUMSC * DQ_NUMWD)\r
89#define DQ_NUMDRV 2 /* # drives */\r
90\r
91/* Command word */\r
92\r
93#define CW_V_FNC 12 /* function */\r
94#define CW_M_FNC 017\r
95#define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC)\r
96/* 000 /* unused */\r
97#define FNC_STA 001 /* status check */\r
98#define FNC_RCL 002 /* recalibrate */\r
99#define FNC_SEEK 003 /* seek */\r
100#define FNC_RD 004 /* read */\r
101#define FNC_WD 005 /* write */\r
102#define FNC_RA 006 /* read address */\r
103#define FNC_WA 007 /* write address */\r
104#define FNC_CHK 010 /* check */\r
105#define FNC_LA 013 /* load address */\r
106#define FNC_AS 014 /* address skip */\r
107\r
108#define FNC_SEEK1 020 /* fake - seek1 */\r
109#define FNC_SEEK2 021 /* fake - seek2 */\r
110#define FNC_SEEK3 022 /* fake - seek3 */\r
111#define FNC_CHK1 023 /* fake - check1 */\r
112#define FNC_LA1 024 /* fake - ldaddr1 */\r
113\r
114#define CW_V_DRV 0 /* drive */\r
115#define CW_M_DRV 01\r
116#define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV)\r
117\r
118/* Disk address words */\r
119\r
120#define DA_V_CYL 0 /* cylinder */\r
121#define DA_M_CYL 0377\r
122#define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL)\r
123#define DA_V_HD 8 /* head */\r
124#define DA_M_HD 037\r
125#define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD)\r
126#define DA_V_SC 0 /* sector */\r
127#define DA_M_SC 037\r
128#define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC)\r
129#define DA_CKMASK 0777 /* check mask */\r
130\r
131/* Status in dqc_sta[drv] - (d) = dynamic */\r
132\r
133#define STA_DID 0000200 /* drive ID (d) */\r
134#define STA_NRDY 0000100 /* not ready (d) */\r
135#define STA_EOC 0000040 /* end of cylinder */\r
136#define STA_AER 0000020 /* addr error */\r
137#define STA_FLG 0000010 /* flagged */\r
138#define STA_BSY 0000004 /* seeking */\r
139#define STA_DTE 0000002 /* data error */\r
140#define STA_ERR 0000001 /* any error */\r
141#define STA_ANYERR (STA_NRDY | STA_EOC | STA_AER | STA_FLG | STA_DTE)\r
142\r
143extern uint32 PC, SR;\r
144extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2];\r
145\r
146int32 dqc_busy = 0; /* cch xfer */\r
147int32 dqc_cnt = 0; /* check count */\r
148int32 dqc_stime = 100; /* seek time */\r
149int32 dqc_ctime = 100; /* command time */\r
150int32 dqc_xtime = 3; /* xfer time */\r
151int32 dqc_dtime = 2; /* dch time */\r
152int32 dqd_obuf = 0, dqd_ibuf = 0; /* dch buffers */\r
153int32 dqc_obuf = 0; /* cch buffers */\r
154int32 dqd_xfer = 0; /* xfer in prog */\r
155int32 dqd_wval = 0; /* write data valid */\r
156int32 dq_ptr = 0; /* buffer ptr */\r
157uint8 dqc_rarc = 0; /* RAR cylinder */\r
158uint8 dqc_rarh = 0; /* RAR head */\r
159uint8 dqc_rars = 0; /* RAR sector */\r
160uint8 dqc_ucyl[DQ_NUMDRV] = { 0 }; /* unit cylinder */\r
161uint8 dqc_uhed[DQ_NUMDRV] = { 0 }; /* unit head */\r
162uint16 dqc_sta[DQ_NUMDRV] = { 0 }; /* unit status */\r
163uint16 dqxb[DQ_NUMWD]; /* sector buffer */\r
164\r
165DEVICE dqd_dev, dqc_dev;\r
166int32 dqdio (int32 inst, int32 IR, int32 dat);\r
167int32 dqcio (int32 inst, int32 IR, int32 dat);\r
168t_stat dqc_svc (UNIT *uptr);\r
169t_stat dqd_svc (UNIT *uptr);\r
170t_stat dqc_reset (DEVICE *dptr);\r
171t_stat dqc_attach (UNIT *uptr, char *cptr);\r
172t_stat dqc_detach (UNIT* uptr);\r
173t_stat dqc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc);\r
174t_stat dqc_boot (int32 unitno, DEVICE *dptr);\r
175void dq_god (int32 fnc, int32 drv, int32 time);\r
176void dq_goc (int32 fnc, int32 drv, int32 time);\r
177\r
178/* DQD data structures\r
179\r
180 dqd_dev DQD device descriptor\r
181 dqd_unit DQD unit list\r
182 dqd_reg DQD register list\r
183*/\r
184\r
185DIB dq_dib[] = {\r
186 { DQD, 0, 0, 0, 0, 0, &dqdio },\r
187 { DQC, 0, 0, 0, 0, 0, &dqcio }\r
188 };\r
189\r
190#define dqd_dib dq_dib[0]\r
191#define dqc_dib dq_dib[1]\r
192\r
193UNIT dqd_unit = { UDATA (&dqd_svc, 0, 0) };\r
194\r
195REG dqd_reg[] = {\r
196 { ORDATA (IBUF, dqd_ibuf, 16) },\r
197 { ORDATA (OBUF, dqd_obuf, 16) },\r
198 { BRDATA (DBUF, dqxb, 8, 16, DQ_NUMWD) },\r
199 { DRDATA (BPTR, dq_ptr, DQ_N_NUMWD) },\r
200 { FLDATA (CMD, dqd_dib.cmd, 0) },\r
201 { FLDATA (CTL, dqd_dib.ctl, 0) },\r
202 { FLDATA (FLG, dqd_dib.flg, 0) },\r
203 { FLDATA (FBF, dqd_dib.fbf, 0) },\r
204 { FLDATA (SRQ, dqd_dib.srq, 0) },\r
205 { FLDATA (XFER, dqd_xfer, 0) },\r
206 { FLDATA (WVAL, dqd_wval, 0) },\r
207 { ORDATA (DEVNO, dqd_dib.devno, 6), REG_HRO },\r
208 { NULL }\r
209 };\r
210\r
211MTAB dqd_mod[] = {\r
212 { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO",\r
213 &hp_setdev, &hp_showdev, &dqd_dev },\r
214 { 0 }\r
215 };\r
216\r
217DEVICE dqd_dev = {\r
218 "DQD", &dqd_unit, dqd_reg, dqd_mod,\r
219 1, 10, DQ_N_NUMWD, 1, 8, 16,\r
220 NULL, NULL, &dqc_reset,\r
221 NULL, NULL, NULL,\r
222 &dqd_dib, DEV_DISABLE\r
223 };\r
224\r
225/* DQC data structures\r
226\r
227 dqc_dev DQC device descriptor\r
228 dqc_unit DQC unit list\r
229 dqc_reg DQC register list\r
230 dqc_mod DQC modifier list\r
231*/\r
232\r
233UNIT dqc_unit[] = {\r
234 { UDATA (&dqc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r
235 UNIT_DISABLE | UNIT_UNLOAD, DQ_SIZE) },\r
236 { UDATA (&dqc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r
237 UNIT_DISABLE | UNIT_UNLOAD, DQ_SIZE) }\r
238 };\r
239\r
240REG dqc_reg[] = {\r
241 { ORDATA (OBUF, dqc_obuf, 16) },\r
242 { ORDATA (BUSY, dqc_busy, 2), REG_RO },\r
243 { ORDATA (CNT, dqc_cnt, 9) },\r
244 { FLDATA (CMD, dqc_dib.cmd, 0) },\r
245 { FLDATA (CTL, dqc_dib.ctl, 0) },\r
246 { FLDATA (FLG, dqc_dib.flg, 0) },\r
247 { FLDATA (FBF, dqc_dib.fbf, 0) },\r
248 { FLDATA (SRQ, dqc_dib.srq, 0) },\r
249 { DRDATA (RARC, dqc_rarc, 8), PV_RZRO },\r
250 { DRDATA (RARH, dqc_rarh, 5), PV_RZRO },\r
251 { DRDATA (RARS, dqc_rars, 5), PV_RZRO },\r
252 { BRDATA (CYL, dqc_ucyl, 10, 8, DQ_NUMDRV), PV_RZRO },\r
253 { BRDATA (HED, dqc_uhed, 10, 5, DQ_NUMDRV), PV_RZRO },\r
254 { BRDATA (STA, dqc_sta, 8, 16, DQ_NUMDRV) },\r
255 { DRDATA (CTIME, dqc_ctime, 24), PV_LEFT },\r
256 { DRDATA (DTIME, dqc_dtime, 24), PV_LEFT },\r
257 { DRDATA (STIME, dqc_stime, 24), PV_LEFT },\r
258 { DRDATA (XTIME, dqc_xtime, 24), REG_NZ + PV_LEFT },\r
259 { URDATA (UFNC, dqc_unit[0].FNC, 8, 8, 0,\r
260 DQ_NUMDRV, REG_HRO) },\r
261 { ORDATA (DEVNO, dqc_dib.devno, 6), REG_HRO },\r
262 { NULL }\r
263 };\r
264\r
265MTAB dqc_mod[] = {\r
266 { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", dqc_load_unload },\r
267 { UNIT_UNLOAD, 0, "heads loaded", "LOADED", dqc_load_unload },\r
268 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r
269 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },\r
270 { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO",\r
271 &hp_setdev, &hp_showdev, &dqd_dev },\r
272 { 0 }\r
273 };\r
274\r
275DEVICE dqc_dev = {\r
276 "DQC", dqc_unit, dqc_reg, dqc_mod,\r
277 DQ_NUMDRV, 8, 24, 1, 8, 16,\r
278 NULL, NULL, &dqc_reset,\r
279 &dqc_boot, &dqc_attach, &dqc_detach,\r
280 &dqc_dib, DEV_DISABLE\r
281 };\r
282\r
283/* IO instructions */\r
284\r
285int32 dqdio (int32 inst, int32 IR, int32 dat)\r
286{\r
287int32 devd;\r
288\r
289devd = IR & I_DEVMASK; /* get device no */\r
290switch (inst) { /* case on opcode */\r
291\r
292 case ioFLG: /* flag clear/set */\r
293 if ((IR & I_HC) == 0) { setFSR (devd); } /* STF */\r
294 break;\r
295\r
296 case ioSFC: /* skip flag clear */\r
297 if (FLG (devd) == 0) PC = (PC + 1) & VAMASK;\r
298 break;\r
299\r
300 case ioSFS: /* skip flag set */\r
301 if (FLG (devd) != 0) PC = (PC + 1) & VAMASK;\r
302 break;\r
303\r
304 case ioOTX: /* output */\r
305 dqd_obuf = dat;\r
306 if (!dqc_busy || dqd_xfer) dqd_wval = 1; /* if !overrun, valid */\r
307 break;\r
308\r
309 case ioMIX: /* merge */\r
310 dat = dat | dqd_ibuf;\r
311 break;\r
312\r
313 case ioLIX: /* load */\r
314 dat = dqd_ibuf;\r
315 break;\r
316\r
317 case ioCRS: /* control reset (action unverif) */\r
318 case ioCTL: /* control clear/set */\r
319 if (IR & I_CTL) { /* CLC */\r
320 clrCTL (devd); /* clr ctl, cmd */\r
321 clrCMD (devd);\r
322 dqd_xfer = 0; /* clr xfer */\r
323 }\r
324 else { /* STC */\r
325 setCTL (devd); /* set ctl, cmd */\r
326 setCMD (devd);\r
327 if (dqc_busy && !dqd_xfer) /* overrun? */\r
328 dqc_sta[dqc_busy - 1] |= STA_DTE;\r
329 }\r
330 break;\r
331\r
332 default:\r
333 break;\r
334 }\r
335\r
336if (IR & I_HC) { clrFSR (devd); } /* H/C option */\r
337return dat;\r
338}\r
339\r
340int32 dqcio (int32 inst, int32 IR, int32 dat)\r
341{\r
342int32 devc, fnc, drv;\r
343\r
344devc = IR & I_DEVMASK; /* get device no */\r
345switch (inst) { /* case on opcode */\r
346\r
347 case ioFLG: /* flag clear/set */\r
348 if ((IR & I_HC) == 0) { setFSR (devc); } /* STF */\r
349 break;\r
350\r
351 case ioSFC: /* skip flag clear */\r
352 if (FLG (devc) == 0) PC = (PC + 1) & VAMASK;\r
353 break;\r
354\r
355 case ioSFS: /* skip flag set */\r
356 if (FLG (devc) != 0) PC = (PC + 1) & VAMASK;\r
357 break;\r
358\r
359 case ioOTX: /* output */\r
360 dqc_obuf = dat;\r
361 break;\r
362\r
363 case ioLIX: /* load */\r
364 dat = 0;\r
365 case ioMIX: /* merge */\r
366 break; /* no data */\r
367\r
368 case ioCRS: /* control reset (action unverif) */\r
369 case ioCTL: /* control clear/set */\r
370 if (IR & I_CTL) { /* CLC? */\r
371 clrCMD (devc); /* clr cmd, ctl */\r
372 clrCTL (devc); /* cancel non-seek */\r
373 if (dqc_busy) sim_cancel (&dqc_unit[dqc_busy - 1]);\r
374 sim_cancel (&dqd_unit); /* cancel dch */\r
375 dqd_xfer = 0; /* clr dch xfer */\r
376 dqc_busy = 0; /* clr busy */\r
377 }\r
378 else { /* STC */\r
379 setCTL (devc); /* set ctl */\r
380 if (!CMD (devc)) { /* cmd clr? */\r
381 setCMD (devc); /* set cmd, ctl */\r
382 drv = CW_GETDRV (dqc_obuf); /* get fnc, drv */\r
383 fnc = CW_GETFNC (dqc_obuf); /* from cmd word */\r
384 switch (fnc) { /* case on fnc */\r
385 case FNC_SEEK: case FNC_RCL: /* seek, recal */\r
386 case FNC_CHK: /* check */\r
387 dqc_sta[drv] = 0; /* clear status */\r
388 case FNC_STA: case FNC_LA: /* rd sta, load addr */\r
389 dq_god (fnc, drv, dqc_dtime); /* sched dch xfer */\r
390 break;\r
391 case FNC_RD: case FNC_WD: /* read, write */\r
392 case FNC_RA: case FNC_WA: /* rd addr, wr addr */\r
393 case FNC_AS: /* address skip */\r
394 dq_goc (fnc, drv, dqc_ctime); /* sched drive */\r
395 break;\r
396 } /* end case */\r
397 } /* end if !CMD */\r
398 } /* end else */\r
399 break;\r
400\r
401 default:\r
402 break;\r
403 }\r
404\r
405if (IR & I_HC) { clrFSR (devc); } /* H/C option */\r
406return dat;\r
407}\r
408\r
409/* Start data channel operation */\r
410\r
411void dq_god (int32 fnc, int32 drv, int32 time)\r
412{\r
413dqd_unit.DRV = drv; /* save unit */\r
414dqd_unit.FNC = fnc; /* save function */\r
415sim_activate (&dqd_unit, time);\r
416return;\r
417}\r
418\r
419/* Start controller operation */\r
420\r
421void dq_goc (int32 fnc, int32 drv, int32 time)\r
422{\r
423int32 t;\r
424\r
425if (t = sim_is_active (&dqc_unit[drv])) { /* still seeking? */\r
426 sim_cancel (&dqc_unit[drv]); /* cancel */\r
427 time = time + t; /* include seek time */\r
428 }\r
429dqc_sta[drv] = 0; /* clear status */\r
430dq_ptr = 0; /* init buf ptr */\r
431dqc_busy = drv + 1; /* set busy */\r
432dqd_xfer = 1; /* xfer in prog */\r
433dqc_unit[drv].FNC = fnc; /* save function */\r
434sim_activate (&dqc_unit[drv], time); /* activate unit */\r
435return;\r
436}\r
437\r
438/* Data channel unit service\r
439\r
440 This routine handles the data channel transfers. It also handles\r
441 data transfers that are blocked by seek in progress.\r
442\r
443 uptr->DRV = target drive\r
444 uptr->FNC = target function\r
445\r
446 Seek substates\r
447 seek - transfer cylinder\r
448 seek1 - transfer head/surface, sched drive\r
449 Recalibrate substates\r
450 rcl - clear cyl/head/surface, sched drive\r
451 Load address\r
452 la - transfer cylinder\r
453 la1 - transfer head/surface, finish operation\r
454 Status check - transfer status, finish operation\r
455 Check data\r
456 chk - transfer sector count, sched drive\r
457*/\r
458\r
459t_stat dqd_svc (UNIT *uptr)\r
460{\r
461int32 drv, devc, devd, st;\r
462\r
463drv = uptr->DRV; /* get drive no */\r
464devc = dqc_dib.devno; /* get cch devno */\r
465devd = dqd_dib.devno; /* get dch devno */\r
466switch (uptr->FNC) { /* case function */\r
467\r
468 case FNC_LA: /* arec, need cyl */\r
469 case FNC_SEEK: /* seek, need cyl */\r
470 if (CMD (devd)) { /* dch active? */\r
471 dqc_rarc = DA_GETCYL (dqd_obuf); /* set RAR from cyl word */\r
472 dqd_wval = 0; /* clr data valid */\r
473 setFSR (devd); /* set dch flg */\r
474 clrCMD (devd); /* clr dch cmd */\r
475 if (uptr->FNC == FNC_LA) uptr->FNC = FNC_LA1;\r
476 else uptr->FNC = FNC_SEEK1; /* advance state */\r
477 }\r
478 sim_activate (uptr, dqc_xtime); /* no, wait more */\r
479 break;\r
480\r
481 case FNC_LA1: /* arec, need hd/sec */\r
482 case FNC_SEEK1: /* seek, need hd/sec */\r
483 if (CMD (devd)) { /* dch active? */\r
484 dqc_rarh = DA_GETHD (dqd_obuf); /* set RAR from head */\r
485 dqc_rars = DA_GETSC (dqd_obuf); /* set RAR from sector */\r
486 dqd_wval = 0; /* clr data valid */\r
487 setFSR (devd); /* set dch flg */\r
488 clrCMD (devd); /* clr dch cmd */\r
489 if (uptr->FNC == FNC_LA1) {\r
490 setFSR (devc); /* set cch flg */\r
491 clrCMD (devc); /* clr cch cmd */\r
492 break; /* done if Load Address */\r
493 }\r
494 if (sim_is_active (&dqc_unit[drv])) break; /* if busy, seek check */\r
495 st = abs (dqc_rarc - dqc_ucyl[drv]) * dqc_stime;\r
496 if (st == 0) st = dqc_xtime; /* if on cyl, min time */\r
497 else dqc_sta[drv] = dqc_sta[drv] | STA_BSY; /* set busy */\r
498 dqc_ucyl[drv] = dqc_rarc; /* transfer RAR */\r
499 dqc_uhed[drv] = dqc_rarh;\r
500 sim_activate (&dqc_unit[drv], st); /* schedule op */\r
501 dqc_unit[drv].FNC = FNC_SEEK2; /* advance state */\r
502 }\r
503 else sim_activate (uptr, dqc_xtime); /* no, wait more */\r
504 break;\r
505\r
506 case FNC_RCL: /* recalibrate */\r
507 dqc_rarc = dqc_rarh = dqc_rars = 0; /* clear RAR */\r
508 if (sim_is_active (&dqc_unit[drv])) break; /* ignore if busy */\r
509 st = dqc_ucyl[drv] * dqc_stime; /* calc diff */\r
510 if (st == 0) st = dqc_xtime; /* if on cyl, min time */\r
511 else dqc_sta[drv] = dqc_sta[drv] | STA_BSY; /* set busy */\r
512 sim_activate (&dqc_unit[drv], st); /* schedule drive */\r
513 dqc_ucyl[drv] = dqc_uhed[drv] = 0; /* clear drive pos */\r
514 dqc_unit[drv].FNC = FNC_SEEK2; /* advance state */\r
515 break;\r
516\r
517 case FNC_STA: /* read status */\r
518 if (CMD (devd)) { /* dch active? */\r
519 if ((dqc_unit[drv].flags & UNIT_UNLOAD) == 0) /* drive up? */\r
520 dqd_ibuf = dqc_sta[drv] & ~STA_DID;\r
521 else dqd_ibuf = STA_NRDY;\r
522 if (dqd_ibuf & STA_ANYERR) /* errors? set flg */\r
523 dqd_ibuf = dqd_ibuf | STA_ERR;\r
524 if (drv) dqd_ibuf = dqd_ibuf | STA_DID;\r
525 setFSR (devd); /* set dch flg */\r
526 clrCMD (devd); /* clr dch cmd */\r
527 clrCMD (devc); /* clr cch cmd */\r
528 dqc_sta[drv] = dqc_sta[drv] & ~STA_ANYERR; /* clr sta flags */\r
529 }\r
530 else sim_activate (uptr, dqc_xtime); /* wait more */\r
531 break;\r
532\r
533 case FNC_CHK: /* check, need cnt */\r
534 if (CMD (devd)) { /* dch active? */\r
535 dqc_cnt = dqd_obuf & DA_CKMASK; /* get count */\r
536 dqd_wval = 0; /* clr data valid */\r
537 dq_goc (FNC_CHK1, drv, dqc_ctime); /* sched drv */\r
538 }\r
539 else sim_activate (uptr, dqc_xtime); /* wait more */\r
540 break;\r
541\r
542 default:\r
543 return SCPE_IERR;\r
544 }\r
545\r
546return SCPE_OK;\r
547}\r
548\r
549/* Drive unit service\r
550\r
551 This routine handles the data transfers.\r
552\r
553 Seek substates\r
554 seek2 - done\r
555 Recalibrate substate\r
556 rcl1 - done\r
557 Check data substates\r
558 chk1 - finish operation\r
559 Read\r
560 Read address\r
561 Address skip (read without header check)\r
562 Write\r
563 Write address\r
564*/\r
565\r
566#define GETDA(x,y,z) \\r
567 (((((x) * DQ_NUMSF) + (y)) * DQ_NUMSC) + (z)) * DQ_NUMWD\r
568\r
569t_stat dqc_svc (UNIT *uptr)\r
570{\r
571int32 da, drv, devc, devd, err;\r
572\r
573err = 0; /* assume no err */\r
574drv = uptr - dqc_dev.units; /* get drive no */\r
575devc = dqc_dib.devno; /* get cch devno */\r
576devd = dqd_dib.devno; /* get dch devno */\r
577if (uptr->flags & UNIT_UNLOAD) { /* drive down? */\r
578 setFSR (devc); /* set cch flg */\r
579 clrCMD (devc); /* clr cch cmd */\r
580 dqc_sta[drv] = 0; /* clr status */\r
581 dqc_busy = 0; /* ctlr is free */\r
582 dqd_xfer = dqd_wval = 0;\r
583 return SCPE_OK;\r
584 }\r
585switch (uptr->FNC) { /* case function */\r
586\r
587 case FNC_SEEK2: /* seek done */\r
588 if (dqc_ucyl[drv] >= DQ_NUMCY) { /* out of range? */\r
589 dqc_sta[drv] = dqc_sta[drv] | STA_BSY | STA_ERR; /* seek check */\r
590 dqc_ucyl[drv] = 0; /* seek to cyl 0 */\r
591 }\r
592 else dqc_sta[drv] = dqc_sta[drv] & ~STA_BSY; /* drive not busy */\r
593 case FNC_SEEK3:\r
594 if (dqc_busy || FLG (devc)) { /* ctrl busy? */\r
595 uptr->FNC = FNC_SEEK3; /* next state */\r
596 sim_activate (uptr, dqc_xtime); /* ctrl busy? wait */\r
597 }\r
598 else {\r
599 setFSR (devc); /* set cch flg */\r
600 clrCMD (devc); /* clr cch cmd */\r
601 }\r
602 return SCPE_OK;\r
603\r
604 case FNC_RA: /* read addr */\r
605 if (!CMD (devd)) break; /* dch clr? done */\r
606 if (dq_ptr == 0) dqd_ibuf = dqc_ucyl[drv]; /* 1st word? */\r
607 else if (dq_ptr == 1) { /* second word? */\r
608 dqd_ibuf = (dqc_uhed[drv] << DA_V_HD) | /* use drive head */\r
609 (dqc_rars << DA_V_SC); /* and RAR sector */\r
610 dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */\r
611 }\r
612 else break;\r
613 dq_ptr = dq_ptr + 1;\r
614 setFSR (devd); /* set dch flg */\r
615 clrCMD (devd); /* clr dch cmd */\r
616 sim_activate (uptr, dqc_xtime); /* sched next word */\r
617 return SCPE_OK;\r
618\r
619 case FNC_AS: /* address skip */\r
620 case FNC_RD: /* read */\r
621 case FNC_CHK1: /* check */\r
622 if (dq_ptr == 0) { /* new sector? */\r
623 if (!CMD (devd) && (uptr->FNC != FNC_CHK1)) break;\r
624 if ((dqc_rarc != dqc_ucyl[drv]) || /* RAR cyl miscompare? */\r
625 (dqc_rarh != dqc_uhed[drv]) || /* RAR head miscompare? */\r
626 (dqc_rars >= DQ_NUMSC)) { /* bad sector? */\r
627 dqc_sta[drv] = dqc_sta[drv] | STA_AER; /* no record found err */\r
628 break;\r
629 }\r
630 if (dqc_rarh >= DQ_NUMSF) { /* bad head? */\r
631 dqc_sta[drv] = dqc_sta[drv] | STA_EOC; /* end of cyl err */\r
632 break;\r
633 }\r
634 da = GETDA (dqc_rarc, dqc_rarh, dqc_rars); /* calc disk addr */\r
635 dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */\r
636 if (dqc_rars == 0) /* wrap? incr head */\r
637 dqc_uhed[drv] = dqc_rarh = dqc_rarh + 1;\r
638 if (err = fseek (uptr->fileref, da * sizeof (int16),\r
639 SEEK_SET)) break;\r
640 fxread (dqxb, sizeof (int16), DQ_NUMWD, uptr->fileref);\r
641 if (err = ferror (uptr->fileref)) break;\r
642 }\r
643 dqd_ibuf = dqxb[dq_ptr++]; /* get word */\r
644 if (dq_ptr >= DQ_NUMWD) { /* end of sector? */\r
645 if (uptr->FNC == FNC_CHK1) { /* check? */\r
646 dqc_cnt = (dqc_cnt - 1) & DA_CKMASK; /* decr count */\r
647 if (dqc_cnt == 0) break; /* if zero, done */\r
648 }\r
649 dq_ptr = 0; /* wrap buf ptr */\r
650 }\r
651 if (CMD (devd) && dqd_xfer) { /* dch on, xfer? */\r
652 setFSR (devd); /* set flag */\r
653 }\r
654 clrCMD (devd); /* clr dch cmd */\r
655 sim_activate (uptr, dqc_xtime); /* sched next word */\r
656 return SCPE_OK;\r
657\r
658 case FNC_WA: /* write address */\r
659 case FNC_WD: /* write */\r
660 if (dq_ptr == 0) { /* sector start? */\r
661 if (!CMD (devd) && !dqd_wval) break; /* xfer done? */\r
662 if (uptr->flags & UNIT_WPRT) { /* write protect? */\r
663 dqc_sta[drv] = dqc_sta[drv] | STA_FLG;\r
664 break; /* done */\r
665 }\r
666 if ((dqc_rarc != dqc_ucyl[drv]) || /* RAR cyl miscompare? */\r
667 (dqc_rarh != dqc_uhed[drv]) || /* RAR head miscompare? */\r
668 (dqc_rars >= DQ_NUMSC)) { /* bad sector? */\r
669 dqc_sta[drv] = dqc_sta[drv] | STA_AER; /* no record found err */\r
670 break;\r
671 }\r
672 if (dqc_rarh >= DQ_NUMSF) { /* bad head? */\r
673 dqc_sta[drv] = dqc_sta[drv] | STA_EOC; /* end of cyl err */\r
674 break;\r
675 }\r
676 }\r
677 dqxb[dq_ptr++] = dqd_wval? dqd_obuf: 0; /* store word/fill */\r
678 dqd_wval = 0; /* clr data valid */\r
679 if (dq_ptr >= DQ_NUMWD) { /* buffer full? */\r
680 da = GETDA (dqc_rarc, dqc_rarh, dqc_rars); /* calc disk addr */\r
681 dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */\r
682 if (dqc_rars == 0) /* wrap? incr head */\r
683 dqc_uhed[drv] = dqc_rarh = dqc_rarh + 1;\r
684 if (err = fseek (uptr->fileref, da * sizeof (int16),\r
685 SEEK_SET)) return TRUE;\r
686 fxwrite (dqxb, sizeof (int16), DQ_NUMWD, uptr->fileref);\r
687 if (err = ferror (uptr->fileref)) break;\r
688 dq_ptr = 0;\r
689 }\r
690 if (CMD (devd) && dqd_xfer) { /* dch on, xfer? */\r
691 setFSR (devd); /* set flag */\r
692 }\r
693 clrCMD (devd); /* clr dch cmd */\r
694 sim_activate (uptr, dqc_xtime); /* sched next word */\r
695 return SCPE_OK;\r
696\r
697 default:\r
698 return SCPE_IERR;\r
699 } /* end case fnc */\r
700\r
701setFSR (devc); /* set cch flg */\r
702clrCMD (devc); /* clr cch cmd */\r
703dqc_busy = 0; /* ctlr is free */\r
704dqd_xfer = dqd_wval = 0;\r
705if (err != 0) { /* error? */\r
706 perror ("DQ I/O error");\r
707 clearerr (uptr->fileref);\r
708 return SCPE_IOERR;\r
709 }\r
710return SCPE_OK;\r
711}\r
712\r
713/* Reset routine */\r
714\r
715t_stat dqc_reset (DEVICE *dptr)\r
716{\r
717int32 drv;\r
718\r
719hp_enbdis_pair (dptr, /* make pair cons */\r
720 (dptr == &dqd_dev)? &dqc_dev: &dqd_dev);\r
721dqd_ibuf = dqd_obuf = 0; /* clear buffers */\r
722dqc_busy = dqc_obuf = 0;\r
723dqd_xfer = dqd_wval = 0;\r
724dq_ptr = 0;\r
725dqc_rarc = dqc_rarh = dqc_rars = 0; /* clear RAR */\r
726dqc_dib.cmd = dqd_dib.cmd = 0; /* clear cmd */\r
727dqc_dib.ctl = dqd_dib.ctl = 0; /* clear ctl */\r
728dqc_dib.fbf = dqd_dib.fbf = 1; /* set fbf */\r
729dqc_dib.flg = dqd_dib.flg = 1; /* set flg */\r
730dqc_dib.srq = dqd_dib.srq = 1; /* srq follows flg */\r
731sim_cancel (&dqd_unit); /* cancel dch */\r
732for (drv = 0; drv < DQ_NUMDRV; drv++) { /* loop thru drives */\r
733 sim_cancel (&dqc_unit[drv]); /* cancel activity */\r
734 dqc_unit[drv].FNC = 0; /* clear function */\r
735 dqc_ucyl[drv] = dqc_uhed[drv] = 0; /* clear drive pos */\r
736 dqc_sta[drv] = 0; /* clear status */\r
737 }\r
738return SCPE_OK;\r
739}\r
740\r
741/* Attach routine */\r
742\r
743t_stat dqc_attach (UNIT *uptr, char *cptr)\r
744{\r
745t_stat r;\r
746\r
747r = attach_unit (uptr, cptr); /* attach unit */\r
748if (r == SCPE_OK) dqc_load_unload (uptr, 0, NULL, NULL);/* if OK, load heads */\r
749return r;\r
750}\r
751\r
752/* Detach routine */\r
753\r
754t_stat dqc_detach (UNIT* uptr)\r
755{\r
756dqc_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads */\r
757return detach_unit (uptr); /* detach unit */\r
758}\r
759\r
760/* Load and unload heads */\r
761\r
762t_stat dqc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc)\r
763{\r
764if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to load */\r
765if (value == UNIT_UNLOAD) /* unload heads? */\r
766 uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */\r
767else uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */\r
768return SCPE_OK;\r
769}\r
770\r
771/* 7900/7901/2883/2884 bootstrap routine (HP 12992A ROM) */\r
772\r
773const uint16 dq_rom[IBL_LNT] = {\r
774 0102501, /*ST LIA 1 ; get switches */\r
775 0106501, /* LIB 1 */\r
776 0013765, /* AND D7 ; isolate hd */\r
777 0005750, /* BLF,CLE,SLB */\r
778 0027741, /* JMP RD */\r
779 0005335, /* RBR,SLB,ERB ; <13>->E, set = 2883 */\r
780 0027717, /* JMP IS */\r
781 0102611, /*LP OTA CC ; do 7900 status to */\r
782 0103711, /* STC CC,C ; clear first seek */\r
783 0102310, /* SFS DC */\r
784 0027711, /* JMP *-1 */\r
785 0002004, /* INA ; get next drive */\r
786 0053765, /* CPA D7 ; all cleared? */\r
787 0002001, /* RSS */\r
788 0027707, /* JMP LP */\r
789 0067761, /*IS LDB SEEKC ; get seek comnd */\r
790 0106610, /* OTB DC ; issue cyl addr (0) */\r
791 0103710, /* STC DC,C ; to dch */\r
792 0106611, /* OTB CC ; seek cmd */\r
793 0103711, /* STC CC,C ; to cch */\r
794 0102310, /* SFS DC ; addr wd ok? */\r
795 0027724, /* JMP *-1 ; no, wait */\r
796 0006400, /* CLB */\r
797 0102501, /* LIA 1 ; get switches */\r
798 0002051, /* SEZ,SLA,RSS ; subchan = 1 or ISS */\r
799 0047770, /* ADB BIT9 ; head 2 */\r
800 0106610, /* OTB DC ; head/sector */\r
801 0103710, /* STC DC,C ; to dch */\r
802 0102311, /* SFS CC ; seek done? */\r
803 0027734, /* JMP *-1 ; no, wait */\r
804 0063731, /* LDA ISSRD ; get read read */\r
805 0002341, /* SEZ,CCE,RSS ; iss disc? */\r
806 0001100, /* ARS ; no, make 7900 read */\r
807 0067776, /*RD LDB DMACW ; DMA control */\r
808 0106606, /* OTB 6 */\r
809 0067762, /* LDB ADDR1 ; memory addr */\r
810 0077741, /* STB RD ; make non re-executable */\r
811 0106602, /* OTB 2 */\r
812 0102702, /* STC 2 ; flip DMA ctrl */\r
813 0067764, /* LDB COUNT ; word count */\r
814 0106602, /* OTB 2 */\r
815 0002041, /* SEZ,RSS */\r
816 0027766, /* JMP NW */\r
817 0102611, /* OTA CC ; to cch */\r
818 0103710, /* STC DC,C ; start dch */\r
819 0103706, /* STC 6,C ; start DMA */\r
820 0103711, /* STC CC,C ; start cch */\r
821 0037773, /* ISZ SK */\r
822 0027773, /* JMP SK */\r
823 0030000, /*SEEKC 030000 */\r
824 0102011, /*ADDR1 102011 */\r
825 0102055, /*ADDR2 102055 */\r
826 0164000, /*COUNT -6144. */\r
827 0000007, /*D7 7 */\r
828 0106710, /*NW CLC DC ; set 'next wd is cmd' flag */\r
829 0001720, /* ALF,ALF ; move to head number loc */\r
830 0001000, /*BIT9 ALS */\r
831 0103610, /* OTA DC,C ; output cold load cmd */\r
832 0103706, /* STC 6,C ; start DMA */\r
833 0102310, /* SFS DC ; done? */\r
834 0027773, /* JMP *-1 ; no, wait */\r
835 0117763, /*XT JSB ADDR2,I ; start program */\r
836 0120010, /*DMACW 120000+DC */\r
837 0000000 /* -ST */\r
838 };\r
839\r
840t_stat dqc_boot (int32 unitno, DEVICE *dptr)\r
841{\r
842int32 dev;\r
843\r
844if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */\r
845dev = dqd_dib.devno; /* get data chan dev */\r
846if (ibl_copy (dq_rom, dev)) return SCPE_IERR; /* copy boot to memory */\r
847SR = (SR & IBL_OPT) | IBL_DQ | (dev << IBL_V_DEV); /* set SR */\r
848return SCPE_OK;\r
849}\r