First Commit of my working state
[simh.git] / H316 / h316_dp.c
1 /* h316_dp.c: Honeywell 4623, 4651, 4720 disk simulator
2
3 Copyright (c) 2003-2005, 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 4623 disk subsystem
27 4651 disk subsystem
28 4720 disk subsystem
29
30 04-Sep-05 RMS Fixed missing return (found by Peter Schorn)
31 15-Jul-05 RMS Fixed bug in attach routine
32 01-Dec-04 RMS Fixed bug in skip on !seeking
33
34 The Honeywell disks have the unique characteristic of supporting variable
35 formatting, on a per track basis. To accomodate this, each track is
36 simulated as 2048 words, divided into records. (2048 words accomodates
37 the largest record of 1891 + 8 overhead words.) A record is structured
38 as follows:
39
40 word 0 record length n (0 = end of track)
41 word 1 record address (16b, uninterpreted by the simulator)
42 word 2 record extension (0 to 4 words of permitted 'overwrite')
43 word 3 first data word
44 :
45 word 3+n-1 last data word
46 word 3+n checksum word
47 word 4+n first extension word
48 :
49 word 7+n fourth extension word
50 word 8+n start of next record
51
52 Formatting is done in two ways. The SET DPn FORMAT=k command formats
53 unit n with k records per track, each record having the maximum allowable
54 record size and a standard record address; or with k words per record.
55 Alternately, the simulator allows programmatic formating. When a track
56 is formated, the program supplies record parameters as follows:
57
58 word 0 record address
59 words 1-n data words
60 word n+1 gap size in bits
61
62 To make this work, the simulator tracks the consumption of bits in the
63 track, against the track capacity in bits. Bit consumption is:
64
65 16.5 * 16 for overhead (including address and checksum)
66 n * 16 for data
67 'gap' for gap, which must be at least 5% of the record length
68 */
69
70 #include "h316_defs.h"
71 #include <math.h>
72
73 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
74 #define UNIT_WLK (1 << UNIT_V_WLK)
75 #define FNC u3 /* saved function */
76 #define CYL u4 /* actual cylinder */
77 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */
78 #define DP_TRKLEN 2048 /* track length, words */
79 #define DP_NUMDRV 8 /* max # drives */
80 #define DP_NUMTYP 3 /* # controller types */
81
82 /* Record format */
83
84 #define REC_LNT 0 /* length (unextended) */
85 #define REC_ADDR 1 /* address */
86 #define REC_EXT 2 /* extension (0-4) */
87 #define REC_DATA 3 /* start of data */
88 #define REC_OVHD 8 /* overhead words */
89 #define REC_MAXEXT 4 /* maximum extension */
90 #define REC_OVHD_WRDS 16.5 /* 16.5 words */
91 #define REC_OVHD_BITS ((16 * 16) + 8)
92
93 /* Status word, ^ = dynamic */
94
95 #define STA_BUSY 0100000 /* busy */
96 #define STA_RDY 0040000 /* ready */
97 #define STA_ADRER 0020000 /* address error */
98 #define STA_FMTER 0010000 /* format error */
99 #define STA_HNLER 0004000 /* heads not loaded (NI) */
100 #define STA_OFLER 0002000 /* offline */
101 #define STA_SEKER 0001000 /* seek error */
102 #define STA_MBZ 0000700
103 #define STA_WPRER 0000040 /* write prot error */
104 #define STA_UNSER 0000020 /* unsafe */
105 #define STA_CSMER 0000010 /* checksum error */
106 #define STA_DTRER 0000004 /* transfer rate error */
107 #define STA_ANYER 0000002 /* any error^ */
108 #define STA_EOR 0000001 /* end of record */
109 #define STA_ALLERR (STA_ADRER|STA_FMTER|STA_HNLER|STA_OFLER|STA_SEKER|\
110 STA_WPRER|STA_UNSER|STA_DTRER)
111
112 /* Functions */
113
114 #define FNC_SK0 0000 /* recalibrate */
115 #define FNC_SEEK 0001 /* seek */
116 #define FNC_RCA 0002 /* read current */
117 #define FNC_UNL 0004 /* unload */
118 #define FNC_FMT 0005 /* format */
119 #define FNC_RW 0006 /* read/write */
120 #define FNC_STOP 0010 /* stop format */
121 #define FNC_RDS 0011 /* read status */
122 #define FNC_DMA 0013 /* DMA/DMC */
123 #define FNC_AKI 0014 /* acknowledge intr */
124 #define FNC_IOBUS 0017 /* IO bus */
125 #define FNC_2ND 0020 /* second state */
126 #define FNC_3RD 0040 /* third state */
127 #define FNC_4TH 0060 /* fourth state */
128 #define FNC_5TH 0100 /* fifth state */
129
130 /* Command word 1 */
131
132 #define CW1_RW 0100000 /* read/write */
133 #define CW1_DIR 0000400 /* seek direction */
134 #define CW1_V_UNIT 11 /* unit select */
135 #define CW1_V_HEAD 6 /* head select */
136 #define CW1_V_OFFS 0 /* seek offset */
137 #define CW1_GETUNIT(x) (((x) >> CW1_V_UNIT) & dp_tab[dp_ctype].umsk)
138 #define CW1_GETHEAD(x) (((x) >> CW1_V_HEAD) & dp_tab[dp_ctype].hmsk)
139 #define CW1_GETOFFS(x) (((x) >> CW1_V_OFFS) & dp_tab[dp_ctype].cmsk)
140
141 /* OTA states */
142
143 #define OTA_NOP 0 /* normal */
144 #define OTA_CW1 1 /* expecting CW1 */
145 #define OTA_CW2 2 /* expecting CW2 */
146
147 /* Transfer state */
148
149 #define XIP_UMSK 007 /* unit mask */
150 #define XIP_SCHED 010 /* scheduled */
151 #define XIP_WRT 020 /* write */
152 #define XIP_FMT 040 /* format */
153
154 /* The H316/516 disk emulator supports three disk controllers:
155
156 controller units cylinders surfaces data words per track
157
158 4651 4 203 2 1908.25
159 4623 8 203 10 1816.5
160 4720 8 203 20 1908.25
161
162 Disk types may not be intermixed on the same controller.
163 */
164
165 #define TYPE_4651 0
166 #define UNIT_4651 4
167 #define CYL_4651 203
168 #define SURF_4651 2
169 #define WRDS_4651 1908.25
170 #define UMSK_4651 0003
171 #define HMSK_4651 0001
172 #define CMSK_4651 0377
173 #define CAP_4651 (CYL_4651*SURF_4651*DP_TRKLEN)
174
175 #define TYPE_4623 1
176 #define UNIT_4623 8
177 #define CYL_4623 203
178 #define SURF_4623 10
179 #define WRDS_4623 1816.5
180 #define UMSK_4623 0007
181 #define HMSK_4623 0017
182 #define CMSK_4623 0377
183 #define CAP_4623 (CYL_4623*SURF_4623*DP_TRKLEN)
184
185 #define TYPE_4720 2
186 #define UNIT_4720 8
187 #define CYL_4720 203
188 #define SURF_4720 20
189 #define WRDS_4720 1908.25
190 #define UMSK_4720 0007
191 #define HMSK_4720 0037
192 #define CMSK_4720 0377
193 #define CAP_4720 (CYL_4720*SURF_4720*DP_TRKLEN)
194
195 struct drvtyp {
196 char *name;
197 uint32 numu;
198 uint32 cyl;
199 uint32 surf;
200 uint32 cap;
201 uint32 umsk;
202 uint32 hmsk;
203 uint32 cmsk;
204 float wrds;
205 };
206
207 #define DP_DRV(d) \
208 #d, \
209 UNIT_##d, CYL_##d, SURF_##d, CAP_##d, \
210 UMSK_##d, HMSK_##d, CMSK_##d, WRDS_##d
211
212 static struct drvtyp dp_tab[] = {
213 { DP_DRV (4651) },
214 { DP_DRV (4623) },
215 { DP_DRV (4720) }
216 };
217
218 extern int32 dev_int, dev_enb, chan_req;
219 extern int32 stop_inst;
220 extern uint32 dma_ad[DMA_MAX];
221 extern int32 sim_switches;
222
223 uint32 dp_cw1 = 0; /* cmd word 1 */
224 uint32 dp_cw2 = 0; /* cmd word 2 */
225 uint32 dp_fnc = 0; /* saved function */
226 uint32 dp_buf = 0; /* buffer */
227 uint32 dp_otas = 0; /* state */
228 uint32 dp_sta = 0; /* status */
229 uint32 dp_defint = 0; /* deferred seek int */
230 uint32 dp_ctype = TYPE_4651; /* controller type */
231 uint32 dp_dma = 0; /* DMA/DMC */
232 uint32 dp_eor = 0; /* end of range */
233 uint32 dp_xip = 0; /* transfer in prog */
234 uint32 dp_csum = 0; /* parity checksum */
235 uint32 dp_rptr = 0; /* start of record */
236 uint32 dp_wptr = 0; /* word ptr in record */
237 uint32 dp_bctr = 0; /* format bit cntr */
238 uint32 dp_gap = 0; /* format gap size */
239 uint32 dp_stopioe = 1; /* stop on error */
240 int32 dp_stime = 1000; /* seek per cylinder */
241 int32 dp_xtime = 10; /* xfer per word */
242 int32 dp_btime = 30; /* busy time */
243 uint16 dpxb[DP_TRKLEN]; /* track buffer */
244
245 int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev);
246 t_stat dp_svc (UNIT *uptr);
247 t_stat dp_reset (DEVICE *dptr);
248 t_stat dp_attach (UNIT *uptr, char *cptr);
249 t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc);
250 t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);
251 t_stat dp_go (uint32 dma);
252 t_stat dp_go1 (uint32 dat);
253 t_stat dp_go2 (uint32 dat);
254 t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);
255 t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);
256 t_bool dp_findrec (uint32 addr);
257 t_stat dp_wrwd (UNIT *uptr, uint32 dat);
258 t_stat dp_wrdone (UNIT *uptr, uint32 flg);
259 t_stat dp_done (uint32 req, uint32 f);
260 t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc);
261 t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc);
262
263 /* DP data structures
264
265 dp_dev DP device descriptor
266 dp_unit DP unit list
267 dp_reg DP register list
268 dp_mod DP modifier list
269 */
270
271 DIB dp_dib = { DP, DMC1, 1, &dpio };
272
273 UNIT dp_unit[] = {
274 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
275 UNIT_ROABLE, CAP_4651) },
276 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
277 UNIT_ROABLE, CAP_4651) },
278 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
279 UNIT_ROABLE, CAP_4651) },
280 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
281 UNIT_ROABLE, CAP_4651) },
282 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
283 UNIT_ROABLE, CAP_4651) },
284 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
285 UNIT_ROABLE, CAP_4651) },
286 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
287 UNIT_ROABLE, CAP_4651) },
288 { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
289 UNIT_ROABLE, CAP_4651) }
290 };
291
292 REG dp_reg[] = {
293 { ORDATA (STA, dp_sta, 16) },
294 { ORDATA (BUF, dp_buf, 16) },
295 { ORDATA (FNC, dp_fnc, 4) },
296 { ORDATA (CW1, dp_cw1, 16) },
297 { ORDATA (CW2, dp_cw2, 16) },
298 { ORDATA (CSUM, dp_csum, 16) },
299 { FLDATA (BUSY, dp_sta, 15) },
300 { FLDATA (RDY, dp_sta, 14) },
301 { FLDATA (EOR, dp_eor, 0) },
302 { FLDATA (DEFINT, dp_defint, 0) },
303 { FLDATA (INTREQ, dev_int, INT_V_DP) },
304 { FLDATA (ENABLE, dev_enb, INT_V_DP) },
305 { BRDATA (TBUF, dpxb, 8, 16, DP_TRKLEN) },
306 { ORDATA (RPTR, dp_rptr, 11), REG_RO },
307 { ORDATA (WPTR, dp_wptr, 11), REG_RO },
308 { ORDATA (BCTR, dp_bctr, 15), REG_RO },
309 { ORDATA (GAP, dp_gap, 16), REG_RO },
310 { DRDATA (STIME, dp_stime, 24), REG_NZ + PV_LEFT },
311 { DRDATA (XTIME, dp_xtime, 24), REG_NZ + PV_LEFT },
312 { DRDATA (BTIME, dp_btime, 24), REG_NZ + PV_LEFT },
313 { FLDATA (CTYPE, dp_ctype, 0), REG_HRO },
314 { URDATA (UCYL, dp_unit[0].CYL, 10, 8, 0,
315 DP_NUMDRV, PV_LEFT | REG_HRO) },
316 { URDATA (UFNC, dp_unit[0].FNC, 8, 7, 0,
317 DP_NUMDRV, REG_HRO) },
318 { URDATA (CAPAC, dp_unit[0].capac, 10, T_ADDR_W, 0,
319 DP_NUMDRV, PV_LEFT | REG_HRO) },
320 { ORDATA (OTAS, dp_otas, 2), REG_HRO },
321 { ORDATA (XIP, dp_xip, 6), REG_HRO },
322 { ORDATA (CHAN, dp_dib.chan, 5), REG_HRO },
323 { FLDATA (STOP_IOE, dp_stopioe, 0) },
324 { NULL }
325 };
326
327 MTAB dp_mod[] = {
328 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
329 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
330 { MTAB_XTD | MTAB_VDV, TYPE_4623, NULL, "4623",
331 &dp_settype, NULL, NULL },
332 { MTAB_XTD | MTAB_VDV, TYPE_4651, NULL, "4651",
333 &dp_settype, NULL, NULL },
334 { MTAB_XTD | MTAB_VDV, TYPE_4720, NULL, "4720",
335 &dp_settype, NULL, NULL },
336 { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL,
337 NULL, &dp_showtype, NULL },
338 { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC",
339 &io_set_dmc, NULL, NULL },
340 { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA",
341 &io_set_dma, NULL, NULL },
342 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,
343 NULL, &io_show_chan, NULL },
344 { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "FORMAT", "FORMAT",
345 &dp_setformat, &dp_showformat, NULL },
346 { 0 }
347 };
348
349 DEVICE dp_dev = {
350 "DP", dp_unit, dp_reg, dp_mod,
351 DP_NUMDRV, 8, 24, 1, 8, 16,
352 NULL, NULL, &dp_reset,
353 NULL, &dp_attach, NULL,
354 &dp_dib, DEV_DISABLE
355 };
356
357 /* IOT routines */
358
359 int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev)
360 {
361 int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
362 int32 u;
363 UNIT *uptr;
364
365 switch (inst) { /* case on opcode */
366
367 case ioOCP: /* OCP */
368 switch (fnc) { /* case on function */
369
370 case FNC_SK0: case FNC_SEEK: case FNC_RCA: /* data transfer */
371 case FNC_UNL: case FNC_FMT: case FNC_RW:
372 dp_go (fnc); /* if !busy, start */
373 break;
374
375 case FNC_STOP: /* stop transfer */
376 if (dp_xip) { /* transfer in prog? */
377 uptr = dp_dev.units + (dp_xip & XIP_UMSK); /* get unit */
378 sim_cancel (uptr); /* stop operation */
379 if (dp_xip & (XIP_WRT|XIP_FMT)) /* write or format? */
380 dp_wrdone (uptr, /* write track */
381 ((dp_xip & XIP_FMT) && /* check fmt state */
382 (uptr->FNC != (FNC_FMT|FNC_2ND)))?
383 STA_DTRER: 0);
384 else dp_done (1, dp_csum? STA_CSMER: 0);/* no, just clr busy */
385 dp_xip = 0; /* clear flag */
386 }
387 dp_otas = OTA_NOP; /* clear state */
388 dp_sta = dp_sta & ~STA_BUSY; /* clear busy */
389 break;
390
391 case FNC_RDS: /* read status */
392 if (dp_sta & STA_BUSY) return dat; /* ignore if busy */
393 dp_sta = (dp_sta | STA_RDY) & ~(STA_MBZ | STA_ANYER);
394 if (dp_sta & STA_ALLERR) dp_sta = dp_sta | STA_ANYER;
395 dp_buf = dp_sta;
396 if (dp_dma && Q_DMA (ch)) SET_CH_REQ (ch); /* DMA? set chan req */
397 break;
398
399 case FNC_DMA: /* set DMA/DMC */
400 dp_dma = 1;
401 break;
402
403 case FNC_IOBUS: /* set IO bus */
404 dp_dma = 0;
405 break;
406
407 case FNC_AKI: /* ack intr */
408 CLR_INT (INT_DP);
409 break;
410
411 default: /* undefined */
412 return IOBADFNC (dat);
413 }
414 break;
415
416 case ioINA: /* INA */
417 if (fnc) return IOBADFNC (dat); /* fnc 0 only */
418 if (dp_sta & STA_RDY) { /* ready? */
419 dp_sta = dp_sta & ~STA_RDY; /* clear ready */
420 return IOSKIP (dat | dp_buf); /* ret buf, skip */
421 }
422 break;
423
424 case ioOTA: /* OTA */
425 if (fnc) return IOBADFNC (dat); /* fnc 0 only */
426 if (dp_sta & STA_RDY) { /* ready? */
427 dp_sta = dp_sta & ~STA_RDY; /* clear ready */
428 dp_buf = dat; /* store buf */
429 if (dp_otas == OTA_CW1) dp_go1 (dat); /* expecting CW1? */
430 else if (dp_otas == OTA_CW2) dp_go2 (dat); /* expecting CW2? */
431 return IOSKIP (dat);
432 }
433 break;
434
435 case ioSKS: /* SKS */
436 u = 7; /* assume unit 7 */
437 switch (fnc) {
438
439 case 000: /* ready */
440 if (dp_sta & STA_RDY) return IOSKIP (dat);
441 break;
442
443 case 001: /* !interrupting */
444 if (!TST_INTREQ (INT_DP)) return IOSKIP (dat);
445 break;
446
447 case 002: /* operational */
448 if (!(dp_sta & (STA_BUSY | STA_ALLERR))) return IOSKIP (dat);
449 break;
450
451 case 003: /* !error */
452 if (!(dp_sta & STA_ALLERR)) return IOSKIP (dat);
453 break;
454
455 case 004: /* !busy */
456 if (!(dp_sta & STA_BUSY)) return IOSKIP (dat);
457 break;
458
459 case 011: case 012: case 013: /* !not seeking 0-6 */
460 case 014: case 015: case 016: case 017:
461 u = fnc - 011;
462 case 007: /* !not seeking 7 */
463 if (!sim_is_active (&dp_unit[u]) || /* quiescent? */
464 (dp_unit[u].FNC != (FNC_SEEK | FNC_2ND)))
465 return IOSKIP (dat); /* seeking sets late */
466 break;
467 }
468 break;
469
470 case ioEND: /* end of range */
471 dp_eor = 1; /* transfer done */
472 break;
473 }
474
475 return dat;
476 }
477
478 /* Start new operation - recal, seek, read address, format, read/write */
479
480 t_stat dp_go (uint32 fnc)
481 {
482 int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
483
484 if (dp_sta & STA_BUSY) return SCPE_OK; /* ignore if busy */
485 dp_fnc = fnc; /* save function */
486 dp_xip = 0; /* transfer not started */
487 dp_eor = 0; /* not end of range */
488 dp_csum = 0; /* init checksum */
489 dp_otas = OTA_CW1; /* expect CW1 */
490 dp_sta = (dp_sta | STA_BUSY | STA_RDY) & ~(STA_ALLERR | STA_EOR);
491 if (dp_dma && Q_DMA (ch)) { /* DMA and DMA channel? */
492 SET_CH_REQ (ch); /* set channel request */
493 dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */
494 }
495 return SCPE_OK;
496 }
497
498 /* Process command word 1 - recal, seek, read address, format, read/write */
499
500 t_stat dp_go1 (uint32 dat)
501 {
502 int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
503 uint32 u = CW1_GETUNIT (dat);
504 UNIT *uptr = dp_dev.units + u;
505
506 dp_cw1 = dat; /* store CW1 */
507 dp_otas = OTA_NOP; /* assume no CW2 */
508 uptr->FNC = dp_fnc;
509 if (sim_is_active (uptr)) /* still seeking? */
510 return dp_done (1, STA_UNSER); /* unsafe */
511 if (!(uptr->flags & UNIT_ATT)) /* not attached? */
512 return dp_done (1, STA_OFLER); /* offline */
513
514 switch (dp_fnc) { /* case on function */
515
516 case FNC_SEEK: /* seek */
517 case FNC_SK0: /* recalibrate */
518 case FNC_UNL: /* unload */
519 sim_activate (uptr, dp_btime); /* quick timeout */
520 break;
521
522 case FNC_FMT: /* format */
523 if (uptr->flags & UNIT_WPRT) /* write protect? */
524 return dp_done (1, STA_WPRER); /* stop now */
525 case FNC_RCA: /* read current addr */
526 dp_xip = u | XIP_SCHED; /* operation started */
527 sim_activate (uptr, dp_xtime * 10); /* rotation timeout */
528 break;
529
530 case FNC_RW: /* read/write */
531 dp_otas = OTA_CW2; /* expect CW2 */
532 dp_sta = dp_sta | STA_RDY; /* set ready */
533 if (dp_dma && Q_DMA (ch)) SET_CH_REQ (ch); /* DMA? set chan request */
534 break;
535 }
536
537 return SCPE_OK;
538 }
539
540 /* Process command word 2 - read/write only */
541
542 t_stat dp_go2 (uint32 dat)
543 {
544 uint32 u = CW1_GETUNIT (dp_cw1);
545 UNIT *uptr = dp_dev.units + u;
546
547 dp_cw2 = dat; /* store CW2 */
548 dp_otas = OTA_NOP; /* normal state */
549 sim_activate (uptr, dp_xtime * 10); /* rotation timeout */
550 dp_xip = u | XIP_SCHED; /* operation started */
551 return SCPE_OK;
552 }
553
554 /* Unit service */
555
556 t_stat dp_svc (UNIT *uptr)
557 {
558 int32 dcyl = 0; /* assume recalibrate */
559 int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */
560 uint32 h = CW1_GETHEAD (dp_cw1); /* head */
561 int32 st;
562 uint32 i, offs, lnt, ming, tpos;
563 t_stat r;
564
565 if (!(uptr->flags & UNIT_ATT)) { /* not attached? */
566 dp_done (1, STA_OFLER); /* offline */
567 return IORETURN (dp_stopioe, SCPE_UNATT);
568 }
569
570 switch (uptr->FNC) { /* case on function */
571
572 case FNC_SEEK: /* seek, need cyl */
573 offs = CW1_GETOFFS (dp_cw1); /* get offset */
574 if (dp_cw1 & CW1_DIR) dcyl = uptr->CYL - offs; /* get desired cyl */
575 else dcyl = uptr->CYL + offs;
576 if ((offs == 0) || (dcyl < 0) ||
577 (dcyl >= (int32) dp_tab[dp_ctype].cyl))
578 return dp_done (1, STA_SEKER); /* bad seek? */
579
580 case FNC_SK0: /* recalibrate */
581 dp_sta = dp_sta & ~STA_BUSY; /* clear busy */
582 uptr->FNC = FNC_SEEK | FNC_2ND; /* next state */
583 st = (abs (dcyl - uptr->CYL)) * dp_stime; /* schedule seek */
584 if (st == 0) st = dp_stime;
585 uptr->CYL = dcyl; /* put on cylinder */
586 sim_activate (uptr, st);
587 return SCPE_OK;
588
589 case FNC_SEEK | FNC_2ND: /* seek, 2nd state */
590 if (dp_sta & STA_BUSY) dp_defint = 1; /* busy? queue intr */
591 else SET_INT (INT_DP); /* no, req intr */
592 return SCPE_OK;
593
594 case FNC_UNL: /* unload */
595 detach_unit (uptr); /* detach unit */
596 return dp_done (0, 0); /* clear busy, no intr */
597
598 case FNC_RCA: /* read current addr */
599 if (h >= dp_tab[dp_ctype].surf) /* invalid head? */
600 return dp_done (1, STA_ADRER); /* error */
601 if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */
602 return r;
603 dp_rptr = 0; /* init rec ptr */
604 if (dpxb[dp_rptr + REC_LNT] == 0) /* unformated? */
605 return dp_done (1, STA_ADRER); /* error */
606 tpos = (uint32) (fmod (sim_gtime () / (double) dp_xtime, DP_TRKLEN));
607 do { /* scan down track */
608 dp_buf = dpxb[dp_rptr + REC_ADDR]; /* get rec addr */
609 dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD;
610 } while ((dp_rptr < tpos) && (dpxb[dp_rptr + REC_LNT] != 0));
611 if (dp_dma) { /* DMA/DMC? */
612 if (Q_DMA (ch)) /* DMA? */
613 dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */
614 SET_CH_REQ (ch); /* request chan */
615 }
616 return dp_done (1, STA_RDY); /* clr busy, set rdy */
617
618 /* Formating takes place in five states:
619
620 init - clear track buffer, start at first record
621 address - store address word
622 data - store data word(s) until end of range
623 pause - wait for gap word or stop command
624 gap - validate gap word, advance to next record
625
626 Note that formating is stopped externally by an OCP command; the
627 track buffer is flushed at that point. If the stop does not occur
628 in the proper state (gap word received), a format error occurs.
629 */
630
631 case FNC_FMT: /* format */
632 for (i = 0; i < DP_TRKLEN; i++) dpxb[i] = 0; /* clear track */
633 dp_xip = dp_xip | XIP_FMT; /* format in progress */
634 dp_rptr = 0; /* init record ptr */
635 dp_gap = 0; /* no gap before first */
636 dp_bctr = (uint32) (16.0 * dp_tab[dp_ctype].wrds); /* init bit cntr */
637 uptr->FNC = uptr->FNC | FNC_2ND; /* address state */
638 break; /* set up next word */
639
640 case FNC_FMT | FNC_2ND: /* format, address word */
641 dp_wptr = 0; /* clear word ptr */
642 if (dp_bctr < (dp_gap + REC_OVHD_BITS + 16)) /* room for gap, record? */
643 return dp_wrdone (uptr, STA_FMTER); /* no, format error */
644 dp_bctr = dp_bctr - dp_gap - REC_OVHD_BITS; /* charge for gap, ovhd */
645 dpxb[dp_rptr + REC_ADDR] = dp_buf; /* store address */
646 uptr->FNC = FNC_FMT | FNC_3RD; /* data state */
647 if (dp_eor) { /* record done? */
648 dp_eor = 0; /* clear for restart */
649 if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */
650 }
651 break; /* set up next word */
652
653 case FNC_FMT | FNC_3RD: /* format, data word */
654 if (dp_sta & STA_RDY) /* timing failure? */
655 return dp_wrdone (uptr, STA_DTRER); /* write trk, err */
656 else { /* no, have word */
657 if (dp_bctr < 16) /* room for it? */
658 return dp_wrdone (uptr, STA_FMTER); /* no, error */
659 dp_bctr = dp_bctr - 16; /* charge for word */
660 dp_csum = dp_csum ^ dp_buf; /* update checksum */
661 dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_buf;/* store word */
662 dpxb[dp_rptr + REC_LNT]++; /* incr rec lnt */
663 dp_wptr++; /* incr word ptr */
664 }
665 if (dp_eor) { /* record done? */
666 dp_eor = 0; /* clear for restart */
667 if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */
668 dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* store checksum */
669 uptr->FNC = uptr->FNC | FNC_4TH; /* pause state */
670 sim_activate (uptr, 5 * dp_xtime); /* schedule pause */
671 return SCPE_OK; /* don't request word */
672 }
673 break; /* set up next word */
674
675 case FNC_FMT | FNC_4TH: /* format, pause */
676 uptr->FNC = FNC_FMT | FNC_5TH; /* gap state */
677 break; /* request word */
678
679 case FNC_FMT | FNC_5TH: /* format, gap word */
680 ming = ((16 * dp_wptr) + REC_OVHD_BITS) / 20; /* min 5% gap */
681 if (dp_buf < ming) /* too small? */
682 return dp_wrdone (uptr, STA_FMTER); /* yes, format error */
683 dp_rptr = dp_rptr + dp_wptr + REC_OVHD; /* next record */
684 uptr->FNC = FNC_FMT | FNC_2ND; /* address state */
685 if (dp_eor) { /* record done? */
686 dp_eor = 0; /* clear for restart */
687 if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */
688 }
689 dp_gap = dp_buf; /* save gap */
690 dp_csum = 0; /* clear checksum */
691 break; /* set up next word */
692
693 /* Read and write take place in two states:
694
695 init - read track into buffer, find record, validate parameters
696 data - (read) fetch data from buffer, stop on end of range
697 - (write) write data into buffer, flush on end of range
698 */
699
700 case FNC_RW: /* read/write */
701 if (h >= dp_tab[dp_ctype].surf) /* invalid head? */
702 return dp_done (1, STA_ADRER); /* error */
703 if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */
704 return r;
705 if (!dp_findrec (dp_cw2)) /* find rec; error? */
706 return dp_done (1, STA_ADRER); /* address error */
707 if ((dpxb[dp_rptr + REC_LNT] >= (DP_TRKLEN - dp_rptr - REC_OVHD)) ||
708 (dpxb[dp_rptr + REC_EXT] >= REC_MAXEXT)) { /* bad lnt or ext? */
709 dp_done (1, STA_UNSER); /* stop simulation */
710 return STOP_DPFMT; /* bad format */
711 }
712 uptr->FNC = uptr->FNC | FNC_2ND; /* next state */
713 if (dp_cw1 & CW1_RW) { /* write? */
714 if (uptr->flags & UNIT_WPRT) /* write protect? */
715 return dp_done (1, STA_WPRER); /* error */
716 dp_xip = dp_xip | XIP_WRT; /* write in progress */
717 dp_sta = dp_sta | STA_RDY; /* set ready */
718 if (dp_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */
719 }
720 else if (Q_DMA (ch)) /* read; DMA? */
721 dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */
722 sim_activate (uptr, dp_xtime); /* schedule word */
723 dp_wptr = 0; /* init word pointer */
724 return SCPE_OK;
725
726 case FNC_RW | FNC_2ND: /* read/write, word */
727 if (dp_cw1 & CW1_RW) { /* write? */
728 if (dp_sta & STA_RDY) /* timing failure? */
729 return dp_wrdone (uptr, STA_DTRER); /* yes, error */
730 if (r = dp_wrwd (uptr, dp_buf)) return r; /* wr word, error? */
731 if (dp_eor) { /* transfer done? */
732 dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum;
733 return dp_wrdone (uptr, 0); /* clear busy, intr req */
734 }
735 }
736 else { /* read? */
737 lnt = dpxb[dp_rptr + REC_LNT] + dpxb[dp_rptr + REC_EXT];
738 dp_buf = dpxb[dp_rptr + REC_DATA + dp_wptr];/* current word */
739 dp_csum = dp_csum ^ dp_buf; /* xor to csum */
740 if ((dp_wptr > lnt) || dp_eor) /* transfer done? */
741 return dp_done (1,
742 (dp_csum? STA_CSMER: 0) |
743 ((dp_wptr >= lnt)? STA_EOR: 0));
744 if (dp_sta & STA_RDY) /* data buf full? */
745 return dp_done (1, STA_DTRER); /* no, underrun */
746 dp_wptr++; /* next word */
747 }
748 break;
749
750 default:
751 return SCPE_IERR;
752 } /* end case */
753
754 dp_sta = dp_sta | STA_RDY; /* set ready */
755 if (dp_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */
756 sim_activate (uptr, dp_xtime); /* schedule word */
757 return SCPE_OK;
758 }
759
760 /* Read track */
761
762 t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)
763 {
764 uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;
765 int32 l;
766
767 fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);
768 l = fxread (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);
769 for ( ; l < DP_TRKLEN; l++) buf[l] = 0;
770 if (ferror (uptr->fileref)) {
771 perror ("DP I/O error");
772 clearerr (uptr->fileref);
773 dp_done (1, STA_UNSER);
774 return SCPE_IOERR;
775 }
776 return SCPE_OK;
777 }
778
779 /* Write track */
780
781 t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)
782 {
783 uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;
784
785 fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);
786 fxwrite (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);
787 if (ferror (uptr->fileref)) {
788 perror ("DP I/O error");
789 clearerr (uptr->fileref);
790 dp_done (1, STA_UNSER);
791 return SCPE_IOERR;
792 }
793 return SCPE_OK;
794 }
795
796 /* Find record; true if found, false if not found */
797
798 t_bool dp_findrec (uint32 addr)
799 {
800 dp_rptr = 0;
801
802 do {
803 if (dpxb[dp_rptr + REC_LNT] == 0) return FALSE;
804 if (dpxb[dp_rptr + REC_LNT] >= DP_TRKLEN) return TRUE;
805 if (dpxb[dp_rptr + REC_ADDR] == addr) return TRUE;
806 dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD;
807 } while (dp_rptr < DP_TRKLEN);
808 return FALSE;
809 }
810
811 /* Write next word to track buffer; return TRUE if ok, FALSE if next record trashed */
812
813 t_stat dp_wrwd (UNIT *uptr, uint32 dat)
814 {
815 uint32 lnt = dpxb[dp_rptr + REC_LNT];
816 t_stat r;
817
818 dp_csum = dp_csum ^ dat;
819 if (dp_wptr < lnt) {
820 dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat;
821 return SCPE_OK;
822 }
823 if (dp_wptr < (lnt + REC_MAXEXT)) {
824 dpxb[dp_rptr + REC_EXT]++;
825 dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat;
826 return SCPE_OK;
827 }
828 dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* write csum */
829 dpxb[dp_rptr + lnt + REC_OVHD] = 0; /* zap rest of track */
830 if (r = dp_wrdone (uptr, STA_UNSER)) return r; /* dump track */
831 return STOP_DPOVR;
832 }
833
834 /* Write done, dump track, clear busy */
835
836 t_stat dp_wrdone (UNIT *uptr, uint32 flg)
837 {
838 dp_done (1, flg);
839 return dp_wrtrk (uptr, dpxb, uptr->CYL, CW1_GETHEAD (dp_cw1));
840 }
841
842 /* Clear busy, set errors, request interrupt if required */
843
844 t_stat dp_done (uint32 req, uint32 flg)
845 {
846 dp_xip = 0; /* clear xfr in prog */
847 dp_sta = (dp_sta | flg) & ~(STA_BUSY | STA_MBZ); /* clear busy */
848 if (req || dp_defint) SET_INT (INT_DP); /* if req, set intr */
849 dp_defint = 0; /* clr def intr */
850 return SCPE_OK;
851 }
852
853 /* Reset routine */
854
855 t_stat dp_reset (DEVICE *dptr)
856 {
857 int32 i;
858
859 dp_fnc = 0;
860 dp_cw1 = 0;
861 dp_cw2 = 0;
862 dp_sta = 0;
863 dp_buf = 0;
864 dp_xip = 0;
865 dp_eor = 0;
866 dp_dma = 0;
867 dp_csum = 0;
868 dp_rptr = 0;
869 dp_wptr = 0;
870 dp_bctr = 0;
871 dp_gap = 0;
872 dp_defint = 0;
873 for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */
874 sim_cancel (&dp_unit[i]); /* cancel activity */
875 dp_unit[i].FNC = 0; /* clear function */
876 dp_unit[i].CYL = 0;
877 }
878 CLR_INT (INT_DP); /* clear int, enb */
879 CLR_ENB (INT_DP);
880 return SCPE_OK;
881 }
882
883 /* Attach routine, test formating */
884
885 t_stat dp_attach (UNIT *uptr, char *cptr)
886 {
887 t_stat r;
888 r = attach_unit (uptr, cptr);
889 if (r != SCPE_OK) return r;
890 return dp_showformat (stdout, uptr, 0, NULL);
891 }
892
893 /* Set controller type */
894
895 t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc)
896 {
897 int32 i;
898
899 if ((val < 0) || (val >= DP_NUMTYP) || (cptr != NULL)) return SCPE_ARG;
900 for (i = 0; i < DP_NUMDRV; i++) {
901 if (dp_unit[i].flags & UNIT_ATT) return SCPE_ALATT;
902 }
903 for (i = 0; i < DP_NUMDRV; i++)
904 dp_unit[i].capac = dp_tab[val].cap;
905 dp_ctype = val;
906 return SCPE_OK;
907 }
908
909 /* Show controller type */
910
911 t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)
912 {
913 if (dp_ctype >= DP_NUMTYP) return SCPE_IERR;
914 fprintf (st, "%s", dp_tab[dp_ctype].name);
915 return SCPE_OK;
916 }
917
918 /* Set drive format
919
920 There is no standard format for record addresses. This routine
921 provides two schemes:
922
923 -S sequential addressing (starting from 0)
924 default geometric addressing (8b: cylinder, 5b: head, 3b: sector)
925
926 This routine also supports formatting by record count or word count:
927
928 -R argument is records per track
929 default argument is words per record
930
931 The relationship between words per record (W), bits per track (B),
932 and records per track (R), is as follows:
933
934 W = (B / (R + ((R - 1) / 20))) - 16.5
935
936 where (R - 1) / 20 is the "5% gap" and 16.5 is the overhead, in words,
937 per record.
938 */
939
940 t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc)
941 {
942 uint32 h, c, cntr, rptr;
943 int32 i, nr, nw, inp;
944 uint16 tbuf[DP_TRKLEN];
945 float finp;
946 t_stat r;
947
948 if (uptr == NULL) return SCPE_IERR;
949 if (cptr == NULL) return SCPE_ARG;
950 if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT;
951 inp = (int32) get_uint (cptr, 10, 2048, &r);
952 if (r != SCPE_OK) return r;
953 if (inp == 0) return SCPE_ARG;
954 finp = (float) inp;
955 if (sim_switches & SWMASK ('R')) { /* format records? */
956 nr = inp;
957 nw = (int32) ((dp_tab[dp_ctype].wrds / (finp + ((finp - 1.0) / 20.0))) - REC_OVHD_WRDS);
958 if (nw <= 0) return SCPE_ARG;
959 }
960 else {
961 nw = inp; /* format words */
962 nr = (int32) ((((20.0 * dp_tab[dp_ctype].wrds) / (finp + REC_OVHD_WRDS)) + 1.0) / 21.0);
963 if (nr <= 0) return SCPE_ARG;
964 }
965 printf ("Proposed format: records/track = %d, record size = %d\n", nr, nw);
966 if (!get_yn ("Formatting will destroy all data on this disk; proceed? [N]", FALSE))
967 return SCPE_OK;
968 for (c = cntr = 0; c < dp_tab[dp_ctype].cyl; c++) {
969 for (h = 0; h < dp_tab[dp_ctype].surf; h++) {
970 for (i = 0; i < DP_TRKLEN; i++) tbuf[i] = 0;
971 rptr = 0;
972 for (i = 0; i < nr; i++) {
973 tbuf[rptr + REC_LNT] = nw & DMASK;
974 if (sim_switches & SWMASK ('S'))
975 tbuf[rptr + REC_ADDR] = cntr++;
976 else tbuf[rptr + REC_ADDR] = (c << 8) + (h << 3) + i;
977 rptr = rptr + nw + REC_OVHD;
978 }
979 if (r = dp_wrtrk (uptr, tbuf, c, h)) return r;
980 }
981 }
982 printf ("Formatting complete\n");
983 return SCPE_OK;
984 }
985
986 /* Show format */
987
988 t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc)
989 {
990 uint32 c, h, rptr, rlnt, sec;
991 uint32 minrec = DP_TRKLEN;
992 uint32 maxrec = 0;
993 uint32 minsec = DP_TRKLEN;
994 uint32 maxsec = 0;
995 uint16 tbuf[DP_TRKLEN];
996 t_stat r;
997
998 if (uptr == NULL) return SCPE_IERR;
999 if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;
1000 for (c = 0; c < dp_tab[dp_ctype].cyl; c++) {
1001 for (h = 0; h < dp_tab[dp_ctype].surf; h++) {
1002 if (r = dp_rdtrk (uptr, tbuf, c, h)) return r;
1003 rptr = 0;
1004 rlnt = tbuf[rptr + REC_LNT];
1005 if (rlnt == 0) {
1006 if (c || h) fprintf (st,
1007 "Unformatted track, cyl = %d, head = %d\n", c, h);
1008 else fprintf (st, "Disk is unformatted\n");
1009 return SCPE_OK;
1010 }
1011 for (sec = 0; rlnt != 0; sec++) {
1012 if ((rptr + rlnt + REC_OVHD) >= DP_TRKLEN) {
1013 fprintf (st, "Invalid record length %d, cyl = %d, head = %d, sect = %d\n",
1014 rlnt, c, h, sec);
1015 return SCPE_OK;
1016 }
1017 if (tbuf[rptr + REC_EXT] >= REC_MAXEXT) {
1018 fprintf (st, "Invalid record extension %d, cyl = %d, head = %d, sect = %d\n",
1019 tbuf[rptr + REC_EXT], c, h, sec);
1020 return SCPE_OK;
1021 }
1022 if (rlnt > maxrec) maxrec = rlnt;
1023 if (rlnt < minrec) minrec = rlnt;
1024 rptr = rptr + rlnt + REC_OVHD;
1025 rlnt = tbuf[rptr + REC_LNT];
1026 }
1027 if (sec > maxsec) maxsec = sec;
1028 if (sec < minsec) minsec = sec;
1029 }
1030 }
1031 if ((minrec == maxrec) && (minsec == maxsec)) fprintf (st,
1032 "Valid fixed format, records/track = %d, record size = %d\n",
1033 minsec, minrec);
1034 else if (minrec == maxrec) fprintf (st,
1035 "Valid variable format, records/track = %d-%d, record size = %d\n",
1036 minsec, maxsec, minrec);
1037 else if (minsec == maxsec) fprintf (st,
1038 "Valid variable format, records/track = %d, record sizes = %d-%d\n",
1039 minsec, minrec, maxrec);
1040 else fprintf (st,
1041 "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n",
1042 minsec, maxsec, minrec, maxrec);
1043 return SCPE_OK;
1044 }