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