Commit | Line | Data |
---|---|---|
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 | |
195 | struct 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 | |
212 | static struct drvtyp dp_tab[] = {\r | |
213 | { DP_DRV (4651) },\r | |
214 | { DP_DRV (4623) },\r | |
215 | { DP_DRV (4720) }\r | |
216 | };\r | |
217 | \r | |
218 | extern int32 dev_int, dev_enb, chan_req;\r | |
219 | extern int32 stop_inst;\r | |
220 | extern uint32 dma_ad[DMA_MAX];\r | |
221 | extern int32 sim_switches;\r | |
222 | \r | |
223 | uint32 dp_cw1 = 0; /* cmd word 1 */\r | |
224 | uint32 dp_cw2 = 0; /* cmd word 2 */\r | |
225 | uint32 dp_fnc = 0; /* saved function */\r | |
226 | uint32 dp_buf = 0; /* buffer */\r | |
227 | uint32 dp_otas = 0; /* state */\r | |
228 | uint32 dp_sta = 0; /* status */\r | |
229 | uint32 dp_defint = 0; /* deferred seek int */\r | |
230 | uint32 dp_ctype = TYPE_4651; /* controller type */\r | |
231 | uint32 dp_dma = 0; /* DMA/DMC */\r | |
232 | uint32 dp_eor = 0; /* end of range */\r | |
233 | uint32 dp_xip = 0; /* transfer in prog */\r | |
234 | uint32 dp_csum = 0; /* parity checksum */\r | |
235 | uint32 dp_rptr = 0; /* start of record */\r | |
236 | uint32 dp_wptr = 0; /* word ptr in record */\r | |
237 | uint32 dp_bctr = 0; /* format bit cntr */\r | |
238 | uint32 dp_gap = 0; /* format gap size */\r | |
239 | uint32 dp_stopioe = 1; /* stop on error */\r | |
240 | int32 dp_stime = 1000; /* seek per cylinder */\r | |
241 | int32 dp_xtime = 10; /* xfer per word */\r | |
242 | int32 dp_btime = 30; /* busy time */\r | |
243 | uint16 dpxb[DP_TRKLEN]; /* track buffer */\r | |
244 | \r | |
245 | int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev);\r | |
246 | t_stat dp_svc (UNIT *uptr);\r | |
247 | t_stat dp_reset (DEVICE *dptr);\r | |
248 | t_stat dp_attach (UNIT *uptr, char *cptr);\r | |
249 | t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
250 | t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
251 | t_stat dp_go (uint32 dma);\r | |
252 | t_stat dp_go1 (uint32 dat);\r | |
253 | t_stat dp_go2 (uint32 dat);\r | |
254 | t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);\r | |
255 | t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);\r | |
256 | t_bool dp_findrec (uint32 addr);\r | |
257 | t_stat dp_wrwd (UNIT *uptr, uint32 dat);\r | |
258 | t_stat dp_wrdone (UNIT *uptr, uint32 flg);\r | |
259 | t_stat dp_done (uint32 req, uint32 f);\r | |
260 | t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
261 | t_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 | |
271 | DIB dp_dib = { DP, DMC1, 1, &dpio };\r | |
272 | \r | |
273 | UNIT 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 | |
292 | REG 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 | |
327 | MTAB 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 | |
349 | DEVICE 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 | |
359 | int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev)\r | |
360 | {\r | |
361 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r | |
362 | int32 u;\r | |
363 | UNIT *uptr;\r | |
364 | \r | |
365 | switch (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 | |
475 | return dat;\r | |
476 | }\r | |
477 | \r | |
478 | /* Start new operation - recal, seek, read address, format, read/write */\r | |
479 | \r | |
480 | t_stat dp_go (uint32 fnc)\r | |
481 | {\r | |
482 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r | |
483 | \r | |
484 | if (dp_sta & STA_BUSY) return SCPE_OK; /* ignore if busy */\r | |
485 | dp_fnc = fnc; /* save function */\r | |
486 | dp_xip = 0; /* transfer not started */\r | |
487 | dp_eor = 0; /* not end of range */\r | |
488 | dp_csum = 0; /* init checksum */\r | |
489 | dp_otas = OTA_CW1; /* expect CW1 */\r | |
490 | dp_sta = (dp_sta | STA_BUSY | STA_RDY) & ~(STA_ALLERR | STA_EOR);\r | |
491 | if (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 | |
495 | return SCPE_OK;\r | |
496 | }\r | |
497 | \r | |
498 | /* Process command word 1 - recal, seek, read address, format, read/write */\r | |
499 | \r | |
500 | t_stat dp_go1 (uint32 dat)\r | |
501 | {\r | |
502 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r | |
503 | uint32 u = CW1_GETUNIT (dat);\r | |
504 | UNIT *uptr = dp_dev.units + u;\r | |
505 | \r | |
506 | dp_cw1 = dat; /* store CW1 */\r | |
507 | dp_otas = OTA_NOP; /* assume no CW2 */\r | |
508 | uptr->FNC = dp_fnc;\r | |
509 | if (sim_is_active (uptr)) /* still seeking? */\r | |
510 | return dp_done (1, STA_UNSER); /* unsafe */\r | |
511 | if (!(uptr->flags & UNIT_ATT)) /* not attached? */\r | |
512 | return dp_done (1, STA_OFLER); /* offline */\r | |
513 | \r | |
514 | switch (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 | |
537 | return SCPE_OK;\r | |
538 | }\r | |
539 | \r | |
540 | /* Process command word 2 - read/write only */\r | |
541 | \r | |
542 | t_stat dp_go2 (uint32 dat)\r | |
543 | {\r | |
544 | uint32 u = CW1_GETUNIT (dp_cw1);\r | |
545 | UNIT *uptr = dp_dev.units + u;\r | |
546 | \r | |
547 | dp_cw2 = dat; /* store CW2 */\r | |
548 | dp_otas = OTA_NOP; /* normal state */\r | |
549 | sim_activate (uptr, dp_xtime * 10); /* rotation timeout */\r | |
550 | dp_xip = u | XIP_SCHED; /* operation started */\r | |
551 | return SCPE_OK;\r | |
552 | }\r | |
553 | \r | |
554 | /* Unit service */\r | |
555 | \r | |
556 | t_stat dp_svc (UNIT *uptr)\r | |
557 | {\r | |
558 | int32 dcyl = 0; /* assume recalibrate */\r | |
559 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r | |
560 | uint32 h = CW1_GETHEAD (dp_cw1); /* head */\r | |
561 | int32 st;\r | |
562 | uint32 i, offs, lnt, ming, tpos;\r | |
563 | t_stat r;\r | |
564 | \r | |
565 | if (!(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 | |
570 | switch (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 | |
754 | dp_sta = dp_sta | STA_RDY; /* set ready */\r | |
755 | if (dp_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */\r | |
756 | sim_activate (uptr, dp_xtime); /* schedule word */\r | |
757 | return SCPE_OK;\r | |
758 | }\r | |
759 | \r | |
760 | /* Read track */\r | |
761 | \r | |
762 | t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)\r | |
763 | {\r | |
764 | uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;\r | |
765 | int32 l;\r | |
766 | \r | |
767 | fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);\r | |
768 | l = fxread (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);\r | |
769 | for ( ; l < DP_TRKLEN; l++) buf[l] = 0;\r | |
770 | if (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 | |
776 | return SCPE_OK;\r | |
777 | }\r | |
778 | \r | |
779 | /* Write track */\r | |
780 | \r | |
781 | t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)\r | |
782 | {\r | |
783 | uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;\r | |
784 | \r | |
785 | fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);\r | |
786 | fxwrite (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);\r | |
787 | if (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 | |
793 | return SCPE_OK;\r | |
794 | }\r | |
795 | \r | |
796 | /* Find record; true if found, false if not found */\r | |
797 | \r | |
798 | t_bool dp_findrec (uint32 addr)\r | |
799 | {\r | |
800 | dp_rptr = 0;\r | |
801 | \r | |
802 | do {\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 | |
808 | return 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 | |
813 | t_stat dp_wrwd (UNIT *uptr, uint32 dat)\r | |
814 | {\r | |
815 | uint32 lnt = dpxb[dp_rptr + REC_LNT];\r | |
816 | t_stat r;\r | |
817 | \r | |
818 | dp_csum = dp_csum ^ dat;\r | |
819 | if (dp_wptr < lnt) {\r | |
820 | dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat;\r | |
821 | return SCPE_OK;\r | |
822 | }\r | |
823 | if (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 | |
828 | dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* write csum */\r | |
829 | dpxb[dp_rptr + lnt + REC_OVHD] = 0; /* zap rest of track */\r | |
830 | if (r = dp_wrdone (uptr, STA_UNSER)) return r; /* dump track */\r | |
831 | return STOP_DPOVR;\r | |
832 | } \r | |
833 | \r | |
834 | /* Write done, dump track, clear busy */\r | |
835 | \r | |
836 | t_stat dp_wrdone (UNIT *uptr, uint32 flg)\r | |
837 | {\r | |
838 | dp_done (1, flg);\r | |
839 | return 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 | |
844 | t_stat dp_done (uint32 req, uint32 flg)\r | |
845 | {\r | |
846 | dp_xip = 0; /* clear xfr in prog */\r | |
847 | dp_sta = (dp_sta | flg) & ~(STA_BUSY | STA_MBZ); /* clear busy */\r | |
848 | if (req || dp_defint) SET_INT (INT_DP); /* if req, set intr */\r | |
849 | dp_defint = 0; /* clr def intr */\r | |
850 | return SCPE_OK;\r | |
851 | }\r | |
852 | \r | |
853 | /* Reset routine */\r | |
854 | \r | |
855 | t_stat dp_reset (DEVICE *dptr)\r | |
856 | {\r | |
857 | int32 i;\r | |
858 | \r | |
859 | dp_fnc = 0;\r | |
860 | dp_cw1 = 0;\r | |
861 | dp_cw2 = 0;\r | |
862 | dp_sta = 0;\r | |
863 | dp_buf = 0;\r | |
864 | dp_xip = 0;\r | |
865 | dp_eor = 0;\r | |
866 | dp_dma = 0;\r | |
867 | dp_csum = 0;\r | |
868 | dp_rptr = 0;\r | |
869 | dp_wptr = 0;\r | |
870 | dp_bctr = 0;\r | |
871 | dp_gap = 0;\r | |
872 | dp_defint = 0;\r | |
873 | for (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 | |
878 | CLR_INT (INT_DP); /* clear int, enb */\r | |
879 | CLR_ENB (INT_DP);\r | |
880 | return SCPE_OK;\r | |
881 | }\r | |
882 | \r | |
883 | /* Attach routine, test formating */\r | |
884 | \r | |
885 | t_stat dp_attach (UNIT *uptr, char *cptr)\r | |
886 | {\r | |
887 | t_stat r;\r | |
888 | r = attach_unit (uptr, cptr);\r | |
889 | if (r != SCPE_OK) return r;\r | |
890 | return dp_showformat (stdout, uptr, 0, NULL);\r | |
891 | }\r | |
892 | \r | |
893 | /* Set controller type */\r | |
894 | \r | |
895 | t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
896 | {\r | |
897 | int32 i;\r | |
898 | \r | |
899 | if ((val < 0) || (val >= DP_NUMTYP) || (cptr != NULL)) return SCPE_ARG;\r | |
900 | for (i = 0; i < DP_NUMDRV; i++) {\r | |
901 | if (dp_unit[i].flags & UNIT_ATT) return SCPE_ALATT;\r | |
902 | }\r | |
903 | for (i = 0; i < DP_NUMDRV; i++)\r | |
904 | dp_unit[i].capac = dp_tab[val].cap;\r | |
905 | dp_ctype = val;\r | |
906 | return SCPE_OK;\r | |
907 | }\r | |
908 | \r | |
909 | /* Show controller type */\r | |
910 | \r | |
911 | t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
912 | {\r | |
913 | if (dp_ctype >= DP_NUMTYP) return SCPE_IERR;\r | |
914 | fprintf (st, "%s", dp_tab[dp_ctype].name);\r | |
915 | return 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 | |
940 | t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
941 | {\r | |
942 | uint32 h, c, cntr, rptr;\r | |
943 | int32 i, nr, nw, inp;\r | |
944 | uint16 tbuf[DP_TRKLEN];\r | |
945 | float finp;\r | |
946 | t_stat r;\r | |
947 | \r | |
948 | if (uptr == NULL) return SCPE_IERR;\r | |
949 | if (cptr == NULL) return SCPE_ARG;\r | |
950 | if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT;\r | |
951 | inp = (int32) get_uint (cptr, 10, 2048, &r);\r | |
952 | if (r != SCPE_OK) return r;\r | |
953 | if (inp == 0) return SCPE_ARG;\r | |
954 | finp = (float) inp;\r | |
955 | if (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 | |
960 | else {\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 | |
965 | printf ("Proposed format: records/track = %d, record size = %d\n", nr, nw);\r | |
966 | if (!get_yn ("Formatting will destroy all data on this disk; proceed? [N]", FALSE))\r | |
967 | return SCPE_OK;\r | |
968 | for (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 | |
982 | printf ("Formatting complete\n");\r | |
983 | return SCPE_OK;\r | |
984 | }\r | |
985 | \r | |
986 | /* Show format */\r | |
987 | \r | |
988 | t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
989 | {\r | |
990 | uint32 c, h, rptr, rlnt, sec;\r | |
991 | uint32 minrec = DP_TRKLEN;\r | |
992 | uint32 maxrec = 0;\r | |
993 | uint32 minsec = DP_TRKLEN;\r | |
994 | uint32 maxsec = 0;\r | |
995 | uint16 tbuf[DP_TRKLEN];\r | |
996 | t_stat r;\r | |
997 | \r | |
998 | if (uptr == NULL) return SCPE_IERR;\r | |
999 | if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;\r | |
1000 | for (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 | |
1031 | if ((minrec == maxrec) && (minsec == maxsec)) fprintf (st, \r | |
1032 | "Valid fixed format, records/track = %d, record size = %d\n",\r | |
1033 | minsec, minrec);\r | |
1034 | else if (minrec == maxrec) fprintf (st,\r | |
1035 | "Valid variable format, records/track = %d-%d, record size = %d\n",\r | |
1036 | minsec, maxsec, minrec);\r | |
1037 | else if (minsec == maxsec) fprintf (st,\r | |
1038 | "Valid variable format, records/track = %d, record sizes = %d-%d\n",\r | |
1039 | minsec, minrec, maxrec);\r | |
1040 | else fprintf (st,\r | |
1041 | "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n",\r | |
1042 | minsec, maxsec, minrec, maxrec);\r | |
1043 | return SCPE_OK;\r | |
1044 | }\r |