1 /* hp2100_dq.c: HP 2100 12565A disk simulator
3 Copyright (c) 1993-2006, Bill McDermith
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
22 Except as contained in this notice, the name of the author 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 the author.
26 dq 12565A 2883 disk system
28 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified)
29 01-Mar-05 JDB Added SET UNLOAD/LOAD
30 07-Oct-04 JDB Fixed enable/disable from either device
31 Shortened xtime from 5 to 3 (drive avg 156KW/second)
32 Fixed not ready/any error status
34 21-Apr-04 RMS Fixed typo in boot loader (found by Dave Bryan)
35 26-Apr-04 RMS Fixed SFS x,C and SFC x,C
36 Fixed SR setting in IBL
38 Implemented DMA SRQ (follows FLG)
39 25-Apr-03 RMS Fixed bug in status check
40 10-Nov-02 RMS Added boot command, rebuilt like 12559/13210
41 09-Jan-02 WOM Copied dp driver and mods for 2883
43 Differences between 12559/13210 and 12565 controllers
44 - 12565 stops transfers on address miscompares; 12559/13210 only stops writes
45 - 12565 does not set error on positioner busy
46 - 12565 does not set positioner busy if already on cylinder
47 - 12565 does not need eoc logic, it will hit an invalid head number
49 The controller's "Record Address Register" (RAR) contains the CHS address of
50 the last Position or Load Address command executed. The RAR is shared among
51 all drives on the controller. In addition, each drive has an internal
52 position register that contains the last cylinder and head position
53 transferred to the drive during Position command execution (sector operations
54 always start with the RAR sector position).
56 In a real drive, the address field of the sector under the head is read and
57 compared to the RAR. When they match, the target sector is under the head
58 and is ready for reading or writing. If a match doesn't occur, an Address
59 Error is indicated. In the simulator, the address field is obtained from the
60 drive's current position register during a read, i.e., the "on-disc" address
61 field is assumed to match the current position.
64 - 12565A Disc Interface Kit Operating and Service Manual (12565-90003, Aug-1973)
66 The following implemented behaviors have been inferred from secondary sources
67 (diagnostics, operating system drivers, etc.), due to absent or contradictory
68 authoritative information; future correction may be needed:
70 1. Read Address command starts at the sector number in the RAR.
73 #include "hp2100_defs.h"
75 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
76 #define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */
77 #define UNIT_WLK (1 << UNIT_V_WLK)
78 #define UNIT_UNLOAD (1 << UNIT_V_UNLOAD)
79 #define FNC u3 /* saved function */
80 #define DRV u4 /* drive number (DC) */
81 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */
84 #define DQ_NUMWD (1 << DQ_N_NUMWD) /* words/sector */
85 #define DQ_NUMSC 23 /* sectors/track */
86 #define DQ_NUMSF 20 /* tracks/cylinder */
87 #define DQ_NUMCY 203 /* cylinders/disk */
88 #define DQ_SIZE (DQ_NUMSF * DQ_NUMCY * DQ_NUMSC * DQ_NUMWD)
89 #define DQ_NUMDRV 2 /* # drives */
93 #define CW_V_FNC 12 /* function */
95 #define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC)
97 #define FNC_STA 001 /* status check */
98 #define FNC_RCL 002 /* recalibrate */
99 #define FNC_SEEK 003 /* seek */
100 #define FNC_RD 004 /* read */
101 #define FNC_WD 005 /* write */
102 #define FNC_RA 006 /* read address */
103 #define FNC_WA 007 /* write address */
104 #define FNC_CHK 010 /* check */
105 #define FNC_LA 013 /* load address */
106 #define FNC_AS 014 /* address skip */
108 #define FNC_SEEK1 020 /* fake - seek1 */
109 #define FNC_SEEK2 021 /* fake - seek2 */
110 #define FNC_SEEK3 022 /* fake - seek3 */
111 #define FNC_CHK1 023 /* fake - check1 */
112 #define FNC_LA1 024 /* fake - ldaddr1 */
114 #define CW_V_DRV 0 /* drive */
116 #define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV)
118 /* Disk address words */
120 #define DA_V_CYL 0 /* cylinder */
121 #define DA_M_CYL 0377
122 #define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL)
123 #define DA_V_HD 8 /* head */
125 #define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD)
126 #define DA_V_SC 0 /* sector */
128 #define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC)
129 #define DA_CKMASK 0777 /* check mask */
131 /* Status in dqc_sta[drv] - (d) = dynamic */
133 #define STA_DID 0000200 /* drive ID (d) */
134 #define STA_NRDY 0000100 /* not ready (d) */
135 #define STA_EOC 0000040 /* end of cylinder */
136 #define STA_AER 0000020 /* addr error */
137 #define STA_FLG 0000010 /* flagged */
138 #define STA_BSY 0000004 /* seeking */
139 #define STA_DTE 0000002 /* data error */
140 #define STA_ERR 0000001 /* any error */
141 #define STA_ANYERR (STA_NRDY | STA_EOC | STA_AER | STA_FLG | STA_DTE)
143 extern uint32 PC
, SR
;
144 extern uint32 dev_cmd
[2], dev_ctl
[2], dev_flg
[2], dev_fbf
[2], dev_srq
[2];
146 int32 dqc_busy
= 0; /* cch xfer */
147 int32 dqc_cnt
= 0; /* check count */
148 int32 dqc_stime
= 100; /* seek time */
149 int32 dqc_ctime
= 100; /* command time */
150 int32 dqc_xtime
= 3; /* xfer time */
151 int32 dqc_dtime
= 2; /* dch time */
152 int32 dqd_obuf
= 0, dqd_ibuf
= 0; /* dch buffers */
153 int32 dqc_obuf
= 0; /* cch buffers */
154 int32 dqd_xfer
= 0; /* xfer in prog */
155 int32 dqd_wval
= 0; /* write data valid */
156 int32 dq_ptr
= 0; /* buffer ptr */
157 uint8 dqc_rarc
= 0; /* RAR cylinder */
158 uint8 dqc_rarh
= 0; /* RAR head */
159 uint8 dqc_rars
= 0; /* RAR sector */
160 uint8 dqc_ucyl
[DQ_NUMDRV
] = { 0 }; /* unit cylinder */
161 uint8 dqc_uhed
[DQ_NUMDRV
] = { 0 }; /* unit head */
162 uint16 dqc_sta
[DQ_NUMDRV
] = { 0 }; /* unit status */
163 uint16 dqxb
[DQ_NUMWD
]; /* sector buffer */
165 DEVICE dqd_dev
, dqc_dev
;
166 int32
dqdio (int32 inst
, int32 IR
, int32 dat
);
167 int32
dqcio (int32 inst
, int32 IR
, int32 dat
);
168 t_stat
dqc_svc (UNIT
*uptr
);
169 t_stat
dqd_svc (UNIT
*uptr
);
170 t_stat
dqc_reset (DEVICE
*dptr
);
171 t_stat
dqc_attach (UNIT
*uptr
, char *cptr
);
172 t_stat
dqc_detach (UNIT
* uptr
);
173 t_stat
dqc_load_unload (UNIT
*uptr
, int32 value
, char *cptr
, void *desc
);
174 t_stat
dqc_boot (int32 unitno
, DEVICE
*dptr
);
175 void dq_god (int32 fnc
, int32 drv
, int32 time
);
176 void dq_goc (int32 fnc
, int32 drv
, int32 time
);
178 /* DQD data structures
180 dqd_dev DQD device descriptor
181 dqd_unit DQD unit list
182 dqd_reg DQD register list
186 { DQD
, 0, 0, 0, 0, 0, &dqdio
},
187 { DQC
, 0, 0, 0, 0, 0, &dqcio
}
190 #define dqd_dib dq_dib[0]
191 #define dqc_dib dq_dib[1]
193 UNIT dqd_unit
= { UDATA (&dqd_svc
, 0, 0) };
196 { ORDATA (IBUF
, dqd_ibuf
, 16) },
197 { ORDATA (OBUF
, dqd_obuf
, 16) },
198 { BRDATA (DBUF
, dqxb
, 8, 16, DQ_NUMWD
) },
199 { DRDATA (BPTR
, dq_ptr
, DQ_N_NUMWD
) },
200 { FLDATA (CMD
, dqd_dib
.cmd
, 0) },
201 { FLDATA (CTL
, dqd_dib
.ctl
, 0) },
202 { FLDATA (FLG
, dqd_dib
.flg
, 0) },
203 { FLDATA (FBF
, dqd_dib
.fbf
, 0) },
204 { FLDATA (SRQ
, dqd_dib
.srq
, 0) },
205 { FLDATA (XFER
, dqd_xfer
, 0) },
206 { FLDATA (WVAL
, dqd_wval
, 0) },
207 { ORDATA (DEVNO
, dqd_dib
.devno
, 6), REG_HRO
},
212 { MTAB_XTD
| MTAB_VDV
, 1, "DEVNO", "DEVNO",
213 &hp_setdev
, &hp_showdev
, &dqd_dev
},
218 "DQD", &dqd_unit
, dqd_reg
, dqd_mod
,
219 1, 10, DQ_N_NUMWD
, 1, 8, 16,
220 NULL
, NULL
, &dqc_reset
,
222 &dqd_dib
, DEV_DISABLE
225 /* DQC data structures
227 dqc_dev DQC device descriptor
228 dqc_unit DQC unit list
229 dqc_reg DQC register list
230 dqc_mod DQC modifier list
234 { UDATA (&dqc_svc
, UNIT_FIX
| UNIT_ATTABLE
| UNIT_ROABLE
|
235 UNIT_DISABLE
| UNIT_UNLOAD
, DQ_SIZE
) },
236 { UDATA (&dqc_svc
, UNIT_FIX
| UNIT_ATTABLE
| UNIT_ROABLE
|
237 UNIT_DISABLE
| UNIT_UNLOAD
, DQ_SIZE
) }
241 { ORDATA (OBUF
, dqc_obuf
, 16) },
242 { ORDATA (BUSY
, dqc_busy
, 2), REG_RO
},
243 { ORDATA (CNT
, dqc_cnt
, 9) },
244 { FLDATA (CMD
, dqc_dib
.cmd
, 0) },
245 { FLDATA (CTL
, dqc_dib
.ctl
, 0) },
246 { FLDATA (FLG
, dqc_dib
.flg
, 0) },
247 { FLDATA (FBF
, dqc_dib
.fbf
, 0) },
248 { FLDATA (SRQ
, dqc_dib
.srq
, 0) },
249 { DRDATA (RARC
, dqc_rarc
, 8), PV_RZRO
},
250 { DRDATA (RARH
, dqc_rarh
, 5), PV_RZRO
},
251 { DRDATA (RARS
, dqc_rars
, 5), PV_RZRO
},
252 { BRDATA (CYL
, dqc_ucyl
, 10, 8, DQ_NUMDRV
), PV_RZRO
},
253 { BRDATA (HED
, dqc_uhed
, 10, 5, DQ_NUMDRV
), PV_RZRO
},
254 { BRDATA (STA
, dqc_sta
, 8, 16, DQ_NUMDRV
) },
255 { DRDATA (CTIME
, dqc_ctime
, 24), PV_LEFT
},
256 { DRDATA (DTIME
, dqc_dtime
, 24), PV_LEFT
},
257 { DRDATA (STIME
, dqc_stime
, 24), PV_LEFT
},
258 { DRDATA (XTIME
, dqc_xtime
, 24), REG_NZ
+ PV_LEFT
},
259 { URDATA (UFNC
, dqc_unit
[0].FNC
, 8, 8, 0,
260 DQ_NUMDRV
, REG_HRO
) },
261 { ORDATA (DEVNO
, dqc_dib
.devno
, 6), REG_HRO
},
266 { UNIT_UNLOAD
, UNIT_UNLOAD
, "heads unloaded", "UNLOADED", dqc_load_unload
},
267 { UNIT_UNLOAD
, 0, "heads loaded", "LOADED", dqc_load_unload
},
268 { UNIT_WLK
, 0, "write enabled", "WRITEENABLED", NULL
},
269 { UNIT_WLK
, UNIT_WLK
, "write locked", "LOCKED", NULL
},
270 { MTAB_XTD
| MTAB_VDV
, 1, "DEVNO", "DEVNO",
271 &hp_setdev
, &hp_showdev
, &dqd_dev
},
276 "DQC", dqc_unit
, dqc_reg
, dqc_mod
,
277 DQ_NUMDRV
, 8, 24, 1, 8, 16,
278 NULL
, NULL
, &dqc_reset
,
279 &dqc_boot
, &dqc_attach
, &dqc_detach
,
280 &dqc_dib
, DEV_DISABLE
283 /* IO instructions */
285 int32
dqdio (int32 inst
, int32 IR
, int32 dat
)
289 devd
= IR
& I_DEVMASK
; /* get device no */
290 switch (inst
) { /* case on opcode */
292 case ioFLG
: /* flag clear/set */
293 if ((IR
& I_HC
) == 0) { setFSR (devd
); } /* STF */
296 case ioSFC
: /* skip flag clear */
297 if (FLG (devd
) == 0) PC
= (PC
+ 1) & VAMASK
;
300 case ioSFS
: /* skip flag set */
301 if (FLG (devd
) != 0) PC
= (PC
+ 1) & VAMASK
;
304 case ioOTX
: /* output */
306 if (!dqc_busy
|| dqd_xfer
) dqd_wval
= 1; /* if !overrun, valid */
309 case ioMIX
: /* merge */
310 dat
= dat
| dqd_ibuf
;
313 case ioLIX
: /* load */
317 case ioCRS
: /* control reset (action unverif) */
318 case ioCTL
: /* control clear/set */
319 if (IR
& I_CTL
) { /* CLC */
320 clrCTL (devd
); /* clr ctl, cmd */
322 dqd_xfer
= 0; /* clr xfer */
325 setCTL (devd
); /* set ctl, cmd */
327 if (dqc_busy
&& !dqd_xfer
) /* overrun? */
328 dqc_sta
[dqc_busy
- 1] |= STA_DTE
;
336 if (IR
& I_HC
) { clrFSR (devd
); } /* H/C option */
340 int32
dqcio (int32 inst
, int32 IR
, int32 dat
)
342 int32 devc
, fnc
, drv
;
344 devc
= IR
& I_DEVMASK
; /* get device no */
345 switch (inst
) { /* case on opcode */
347 case ioFLG
: /* flag clear/set */
348 if ((IR
& I_HC
) == 0) { setFSR (devc
); } /* STF */
351 case ioSFC
: /* skip flag clear */
352 if (FLG (devc
) == 0) PC
= (PC
+ 1) & VAMASK
;
355 case ioSFS
: /* skip flag set */
356 if (FLG (devc
) != 0) PC
= (PC
+ 1) & VAMASK
;
359 case ioOTX
: /* output */
363 case ioLIX
: /* load */
365 case ioMIX
: /* merge */
368 case ioCRS
: /* control reset (action unverif) */
369 case ioCTL
: /* control clear/set */
370 if (IR
& I_CTL
) { /* CLC? */
371 clrCMD (devc
); /* clr cmd, ctl */
372 clrCTL (devc
); /* cancel non-seek */
373 if (dqc_busy
) sim_cancel (&dqc_unit
[dqc_busy
- 1]);
374 sim_cancel (&dqd_unit
); /* cancel dch */
375 dqd_xfer
= 0; /* clr dch xfer */
376 dqc_busy
= 0; /* clr busy */
379 setCTL (devc
); /* set ctl */
380 if (!CMD (devc
)) { /* cmd clr? */
381 setCMD (devc
); /* set cmd, ctl */
382 drv
= CW_GETDRV (dqc_obuf
); /* get fnc, drv */
383 fnc
= CW_GETFNC (dqc_obuf
); /* from cmd word */
384 switch (fnc
) { /* case on fnc */
385 case FNC_SEEK
: case FNC_RCL
: /* seek, recal */
386 case FNC_CHK
: /* check */
387 dqc_sta
[drv
] = 0; /* clear status */
388 case FNC_STA
: case FNC_LA
: /* rd sta, load addr */
389 dq_god (fnc
, drv
, dqc_dtime
); /* sched dch xfer */
391 case FNC_RD
: case FNC_WD
: /* read, write */
392 case FNC_RA
: case FNC_WA
: /* rd addr, wr addr */
393 case FNC_AS
: /* address skip */
394 dq_goc (fnc
, drv
, dqc_ctime
); /* sched drive */
405 if (IR
& I_HC
) { clrFSR (devc
); } /* H/C option */
409 /* Start data channel operation */
411 void dq_god (int32 fnc
, int32 drv
, int32 time
)
413 dqd_unit
.DRV
= drv
; /* save unit */
414 dqd_unit
.FNC
= fnc
; /* save function */
415 sim_activate (&dqd_unit
, time
);
419 /* Start controller operation */
421 void dq_goc (int32 fnc
, int32 drv
, int32 time
)
425 if (t
= sim_is_active (&dqc_unit
[drv
])) { /* still seeking? */
426 sim_cancel (&dqc_unit
[drv
]); /* cancel */
427 time
= time
+ t
; /* include seek time */
429 dqc_sta
[drv
] = 0; /* clear status */
430 dq_ptr
= 0; /* init buf ptr */
431 dqc_busy
= drv
+ 1; /* set busy */
432 dqd_xfer
= 1; /* xfer in prog */
433 dqc_unit
[drv
].FNC
= fnc
; /* save function */
434 sim_activate (&dqc_unit
[drv
], time
); /* activate unit */
438 /* Data channel unit service
440 This routine handles the data channel transfers. It also handles
441 data transfers that are blocked by seek in progress.
443 uptr->DRV = target drive
444 uptr->FNC = target function
447 seek - transfer cylinder
448 seek1 - transfer head/surface, sched drive
449 Recalibrate substates
450 rcl - clear cyl/head/surface, sched drive
452 la - transfer cylinder
453 la1 - transfer head/surface, finish operation
454 Status check - transfer status, finish operation
456 chk - transfer sector count, sched drive
459 t_stat
dqd_svc (UNIT
*uptr
)
461 int32 drv
, devc
, devd
, st
;
463 drv
= uptr
->DRV
; /* get drive no */
464 devc
= dqc_dib
.devno
; /* get cch devno */
465 devd
= dqd_dib
.devno
; /* get dch devno */
466 switch (uptr
->FNC
) { /* case function */
468 case FNC_LA
: /* arec, need cyl */
469 case FNC_SEEK
: /* seek, need cyl */
470 if (CMD (devd
)) { /* dch active? */
471 dqc_rarc
= DA_GETCYL (dqd_obuf
); /* set RAR from cyl word */
472 dqd_wval
= 0; /* clr data valid */
473 setFSR (devd
); /* set dch flg */
474 clrCMD (devd
); /* clr dch cmd */
475 if (uptr
->FNC
== FNC_LA
) uptr
->FNC
= FNC_LA1
;
476 else uptr
->FNC
= FNC_SEEK1
; /* advance state */
478 sim_activate (uptr
, dqc_xtime
); /* no, wait more */
481 case FNC_LA1
: /* arec, need hd/sec */
482 case FNC_SEEK1
: /* seek, need hd/sec */
483 if (CMD (devd
)) { /* dch active? */
484 dqc_rarh
= DA_GETHD (dqd_obuf
); /* set RAR from head */
485 dqc_rars
= DA_GETSC (dqd_obuf
); /* set RAR from sector */
486 dqd_wval
= 0; /* clr data valid */
487 setFSR (devd
); /* set dch flg */
488 clrCMD (devd
); /* clr dch cmd */
489 if (uptr
->FNC
== FNC_LA1
) {
490 setFSR (devc
); /* set cch flg */
491 clrCMD (devc
); /* clr cch cmd */
492 break; /* done if Load Address */
494 if (sim_is_active (&dqc_unit
[drv
])) break; /* if busy, seek check */
495 st
= abs (dqc_rarc
- dqc_ucyl
[drv
]) * dqc_stime
;
496 if (st
== 0) st
= dqc_xtime
; /* if on cyl, min time */
497 else dqc_sta
[drv
] = dqc_sta
[drv
] | STA_BSY
; /* set busy */
498 dqc_ucyl
[drv
] = dqc_rarc
; /* transfer RAR */
499 dqc_uhed
[drv
] = dqc_rarh
;
500 sim_activate (&dqc_unit
[drv
], st
); /* schedule op */
501 dqc_unit
[drv
].FNC
= FNC_SEEK2
; /* advance state */
503 else sim_activate (uptr
, dqc_xtime
); /* no, wait more */
506 case FNC_RCL
: /* recalibrate */
507 dqc_rarc
= dqc_rarh
= dqc_rars
= 0; /* clear RAR */
508 if (sim_is_active (&dqc_unit
[drv
])) break; /* ignore if busy */
509 st
= dqc_ucyl
[drv
] * dqc_stime
; /* calc diff */
510 if (st
== 0) st
= dqc_xtime
; /* if on cyl, min time */
511 else dqc_sta
[drv
] = dqc_sta
[drv
] | STA_BSY
; /* set busy */
512 sim_activate (&dqc_unit
[drv
], st
); /* schedule drive */
513 dqc_ucyl
[drv
] = dqc_uhed
[drv
] = 0; /* clear drive pos */
514 dqc_unit
[drv
].FNC
= FNC_SEEK2
; /* advance state */
517 case FNC_STA
: /* read status */
518 if (CMD (devd
)) { /* dch active? */
519 if ((dqc_unit
[drv
].flags
& UNIT_UNLOAD
) == 0) /* drive up? */
520 dqd_ibuf
= dqc_sta
[drv
] & ~STA_DID
;
521 else dqd_ibuf
= STA_NRDY
;
522 if (dqd_ibuf
& STA_ANYERR
) /* errors? set flg */
523 dqd_ibuf
= dqd_ibuf
| STA_ERR
;
524 if (drv
) dqd_ibuf
= dqd_ibuf
| STA_DID
;
525 setFSR (devd
); /* set dch flg */
526 clrCMD (devd
); /* clr dch cmd */
527 clrCMD (devc
); /* clr cch cmd */
528 dqc_sta
[drv
] = dqc_sta
[drv
] & ~STA_ANYERR
; /* clr sta flags */
530 else sim_activate (uptr
, dqc_xtime
); /* wait more */
533 case FNC_CHK
: /* check, need cnt */
534 if (CMD (devd
)) { /* dch active? */
535 dqc_cnt
= dqd_obuf
& DA_CKMASK
; /* get count */
536 dqd_wval
= 0; /* clr data valid */
537 dq_goc (FNC_CHK1
, drv
, dqc_ctime
); /* sched drv */
539 else sim_activate (uptr
, dqc_xtime
); /* wait more */
549 /* Drive unit service
551 This routine handles the data transfers.
558 chk1 - finish operation
561 Address skip (read without header check)
566 #define GETDA(x,y,z) \
567 (((((x) * DQ_NUMSF) + (y)) * DQ_NUMSC) + (z)) * DQ_NUMWD
569 t_stat
dqc_svc (UNIT
*uptr
)
571 int32 da
, drv
, devc
, devd
, err
;
573 err
= 0; /* assume no err */
574 drv
= uptr
- dqc_dev
.units
; /* get drive no */
575 devc
= dqc_dib
.devno
; /* get cch devno */
576 devd
= dqd_dib
.devno
; /* get dch devno */
577 if (uptr
->flags
& UNIT_UNLOAD
) { /* drive down? */
578 setFSR (devc
); /* set cch flg */
579 clrCMD (devc
); /* clr cch cmd */
580 dqc_sta
[drv
] = 0; /* clr status */
581 dqc_busy
= 0; /* ctlr is free */
582 dqd_xfer
= dqd_wval
= 0;
585 switch (uptr
->FNC
) { /* case function */
587 case FNC_SEEK2
: /* seek done */
588 if (dqc_ucyl
[drv
] >= DQ_NUMCY
) { /* out of range? */
589 dqc_sta
[drv
] = dqc_sta
[drv
] | STA_BSY
| STA_ERR
; /* seek check */
590 dqc_ucyl
[drv
] = 0; /* seek to cyl 0 */
592 else dqc_sta
[drv
] = dqc_sta
[drv
] & ~STA_BSY
; /* drive not busy */
594 if (dqc_busy
|| FLG (devc
)) { /* ctrl busy? */
595 uptr
->FNC
= FNC_SEEK3
; /* next state */
596 sim_activate (uptr
, dqc_xtime
); /* ctrl busy? wait */
599 setFSR (devc
); /* set cch flg */
600 clrCMD (devc
); /* clr cch cmd */
604 case FNC_RA
: /* read addr */
605 if (!CMD (devd
)) break; /* dch clr? done */
606 if (dq_ptr
== 0) dqd_ibuf
= dqc_ucyl
[drv
]; /* 1st word? */
607 else if (dq_ptr
== 1) { /* second word? */
608 dqd_ibuf
= (dqc_uhed
[drv
] << DA_V_HD
) | /* use drive head */
609 (dqc_rars
<< DA_V_SC
); /* and RAR sector */
610 dqc_rars
= (dqc_rars
+ 1) % DQ_NUMSC
; /* incr sector */
614 setFSR (devd
); /* set dch flg */
615 clrCMD (devd
); /* clr dch cmd */
616 sim_activate (uptr
, dqc_xtime
); /* sched next word */
619 case FNC_AS
: /* address skip */
620 case FNC_RD
: /* read */
621 case FNC_CHK1
: /* check */
622 if (dq_ptr
== 0) { /* new sector? */
623 if (!CMD (devd
) && (uptr
->FNC
!= FNC_CHK1
)) break;
624 if ((dqc_rarc
!= dqc_ucyl
[drv
]) || /* RAR cyl miscompare? */
625 (dqc_rarh
!= dqc_uhed
[drv
]) || /* RAR head miscompare? */
626 (dqc_rars
>= DQ_NUMSC
)) { /* bad sector? */
627 dqc_sta
[drv
] = dqc_sta
[drv
] | STA_AER
; /* no record found err */
630 if (dqc_rarh
>= DQ_NUMSF
) { /* bad head? */
631 dqc_sta
[drv
] = dqc_sta
[drv
] | STA_EOC
; /* end of cyl err */
634 da
= GETDA (dqc_rarc
, dqc_rarh
, dqc_rars
); /* calc disk addr */
635 dqc_rars
= (dqc_rars
+ 1) % DQ_NUMSC
; /* incr sector */
636 if (dqc_rars
== 0) /* wrap? incr head */
637 dqc_uhed
[drv
] = dqc_rarh
= dqc_rarh
+ 1;
638 if (err
= fseek (uptr
->fileref
, da
* sizeof (int16
),
640 fxread (dqxb
, sizeof (int16
), DQ_NUMWD
, uptr
->fileref
);
641 if (err
= ferror (uptr
->fileref
)) break;
643 dqd_ibuf
= dqxb
[dq_ptr
++]; /* get word */
644 if (dq_ptr
>= DQ_NUMWD
) { /* end of sector? */
645 if (uptr
->FNC
== FNC_CHK1
) { /* check? */
646 dqc_cnt
= (dqc_cnt
- 1) & DA_CKMASK
; /* decr count */
647 if (dqc_cnt
== 0) break; /* if zero, done */
649 dq_ptr
= 0; /* wrap buf ptr */
651 if (CMD (devd
) && dqd_xfer
) { /* dch on, xfer? */
652 setFSR (devd
); /* set flag */
654 clrCMD (devd
); /* clr dch cmd */
655 sim_activate (uptr
, dqc_xtime
); /* sched next word */
658 case FNC_WA
: /* write address */
659 case FNC_WD
: /* write */
660 if (dq_ptr
== 0) { /* sector start? */
661 if (!CMD (devd
) && !dqd_wval
) break; /* xfer done? */
662 if (uptr
->flags
& UNIT_WPRT
) { /* write protect? */
663 dqc_sta
[drv
] = dqc_sta
[drv
] | STA_FLG
;
666 if ((dqc_rarc
!= dqc_ucyl
[drv
]) || /* RAR cyl miscompare? */
667 (dqc_rarh
!= dqc_uhed
[drv
]) || /* RAR head miscompare? */
668 (dqc_rars
>= DQ_NUMSC
)) { /* bad sector? */
669 dqc_sta
[drv
] = dqc_sta
[drv
] | STA_AER
; /* no record found err */
672 if (dqc_rarh
>= DQ_NUMSF
) { /* bad head? */
673 dqc_sta
[drv
] = dqc_sta
[drv
] | STA_EOC
; /* end of cyl err */
677 dqxb
[dq_ptr
++] = dqd_wval
? dqd_obuf
: 0; /* store word/fill */
678 dqd_wval
= 0; /* clr data valid */
679 if (dq_ptr
>= DQ_NUMWD
) { /* buffer full? */
680 da
= GETDA (dqc_rarc
, dqc_rarh
, dqc_rars
); /* calc disk addr */
681 dqc_rars
= (dqc_rars
+ 1) % DQ_NUMSC
; /* incr sector */
682 if (dqc_rars
== 0) /* wrap? incr head */
683 dqc_uhed
[drv
] = dqc_rarh
= dqc_rarh
+ 1;
684 if (err
= fseek (uptr
->fileref
, da
* sizeof (int16
),
685 SEEK_SET
)) return TRUE
;
686 fxwrite (dqxb
, sizeof (int16
), DQ_NUMWD
, uptr
->fileref
);
687 if (err
= ferror (uptr
->fileref
)) break;
690 if (CMD (devd
) && dqd_xfer
) { /* dch on, xfer? */
691 setFSR (devd
); /* set flag */
693 clrCMD (devd
); /* clr dch cmd */
694 sim_activate (uptr
, dqc_xtime
); /* sched next word */
701 setFSR (devc
); /* set cch flg */
702 clrCMD (devc
); /* clr cch cmd */
703 dqc_busy
= 0; /* ctlr is free */
704 dqd_xfer
= dqd_wval
= 0;
705 if (err
!= 0) { /* error? */
706 perror ("DQ I/O error");
707 clearerr (uptr
->fileref
);
715 t_stat
dqc_reset (DEVICE
*dptr
)
719 hp_enbdis_pair (dptr
, /* make pair cons */
720 (dptr
== &dqd_dev
)? &dqc_dev
: &dqd_dev
);
721 dqd_ibuf
= dqd_obuf
= 0; /* clear buffers */
722 dqc_busy
= dqc_obuf
= 0;
723 dqd_xfer
= dqd_wval
= 0;
725 dqc_rarc
= dqc_rarh
= dqc_rars
= 0; /* clear RAR */
726 dqc_dib
.cmd
= dqd_dib
.cmd
= 0; /* clear cmd */
727 dqc_dib
.ctl
= dqd_dib
.ctl
= 0; /* clear ctl */
728 dqc_dib
.fbf
= dqd_dib
.fbf
= 1; /* set fbf */
729 dqc_dib
.flg
= dqd_dib
.flg
= 1; /* set flg */
730 dqc_dib
.srq
= dqd_dib
.srq
= 1; /* srq follows flg */
731 sim_cancel (&dqd_unit
); /* cancel dch */
732 for (drv
= 0; drv
< DQ_NUMDRV
; drv
++) { /* loop thru drives */
733 sim_cancel (&dqc_unit
[drv
]); /* cancel activity */
734 dqc_unit
[drv
].FNC
= 0; /* clear function */
735 dqc_ucyl
[drv
] = dqc_uhed
[drv
] = 0; /* clear drive pos */
736 dqc_sta
[drv
] = 0; /* clear status */
743 t_stat
dqc_attach (UNIT
*uptr
, char *cptr
)
747 r
= attach_unit (uptr
, cptr
); /* attach unit */
748 if (r
== SCPE_OK
) dqc_load_unload (uptr
, 0, NULL
, NULL
);/* if OK, load heads */
754 t_stat
dqc_detach (UNIT
* uptr
)
756 dqc_load_unload (uptr
, UNIT_UNLOAD
, NULL
, NULL
); /* unload heads */
757 return detach_unit (uptr
); /* detach unit */
760 /* Load and unload heads */
762 t_stat
dqc_load_unload (UNIT
*uptr
, int32 value
, char *cptr
, void *desc
)
764 if ((uptr
->flags
& UNIT_ATT
) == 0) return SCPE_UNATT
; /* must be attached to load */
765 if (value
== UNIT_UNLOAD
) /* unload heads? */
766 uptr
->flags
= uptr
->flags
| UNIT_UNLOAD
; /* indicate unload */
767 else uptr
->flags
= uptr
->flags
& ~UNIT_UNLOAD
; /* indicate load */
771 /* 7900/7901/2883/2884 bootstrap routine (HP 12992A ROM) */
773 const uint16 dq_rom
[IBL_LNT
] = {
774 0102501, /*ST LIA 1 ; get switches */
776 0013765, /* AND D7 ; isolate hd */
777 0005750, /* BLF,CLE,SLB */
778 0027741, /* JMP RD */
779 0005335, /* RBR,SLB,ERB ; <13>->E, set = 2883 */
780 0027717, /* JMP IS */
781 0102611, /*LP OTA CC ; do 7900 status to */
782 0103711, /* STC CC,C ; clear first seek */
783 0102310, /* SFS DC */
784 0027711, /* JMP *-1 */
785 0002004, /* INA ; get next drive */
786 0053765, /* CPA D7 ; all cleared? */
788 0027707, /* JMP LP */
789 0067761, /*IS LDB SEEKC ; get seek comnd */
790 0106610, /* OTB DC ; issue cyl addr (0) */
791 0103710, /* STC DC,C ; to dch */
792 0106611, /* OTB CC ; seek cmd */
793 0103711, /* STC CC,C ; to cch */
794 0102310, /* SFS DC ; addr wd ok? */
795 0027724, /* JMP *-1 ; no, wait */
797 0102501, /* LIA 1 ; get switches */
798 0002051, /* SEZ,SLA,RSS ; subchan = 1 or ISS */
799 0047770, /* ADB BIT9 ; head 2 */
800 0106610, /* OTB DC ; head/sector */
801 0103710, /* STC DC,C ; to dch */
802 0102311, /* SFS CC ; seek done? */
803 0027734, /* JMP *-1 ; no, wait */
804 0063731, /* LDA ISSRD ; get read read */
805 0002341, /* SEZ,CCE,RSS ; iss disc? */
806 0001100, /* ARS ; no, make 7900 read */
807 0067776, /*RD LDB DMACW ; DMA control */
809 0067762, /* LDB ADDR1 ; memory addr */
810 0077741, /* STB RD ; make non re-executable */
812 0102702, /* STC 2 ; flip DMA ctrl */
813 0067764, /* LDB COUNT ; word count */
815 0002041, /* SEZ,RSS */
816 0027766, /* JMP NW */
817 0102611, /* OTA CC ; to cch */
818 0103710, /* STC DC,C ; start dch */
819 0103706, /* STC 6,C ; start DMA */
820 0103711, /* STC CC,C ; start cch */
821 0037773, /* ISZ SK */
822 0027773, /* JMP SK */
823 0030000, /*SEEKC 030000 */
824 0102011, /*ADDR1 102011 */
825 0102055, /*ADDR2 102055 */
826 0164000, /*COUNT -6144. */
828 0106710, /*NW CLC DC ; set 'next wd is cmd' flag */
829 0001720, /* ALF,ALF ; move to head number loc */
830 0001000, /*BIT9 ALS */
831 0103610, /* OTA DC,C ; output cold load cmd */
832 0103706, /* STC 6,C ; start DMA */
833 0102310, /* SFS DC ; done? */
834 0027773, /* JMP *-1 ; no, wait */
835 0117763, /*XT JSB ADDR2,I ; start program */
836 0120010, /*DMACW 120000+DC */
840 t_stat
dqc_boot (int32 unitno
, DEVICE
*dptr
)
844 if (unitno
!= 0) return SCPE_NOFNC
; /* only unit 0 */
845 dev
= dqd_dib
.devno
; /* get data chan dev */
846 if (ibl_copy (dq_rom
, dev
)) return SCPE_IERR
; /* copy boot to memory */
847 SR
= (SR
& IBL_OPT
) | IBL_DQ
| (dev
<< IBL_V_DEV
); /* set SR */