Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* i7094_dsk.c: 7631 file control (disk/drum) simulator\r |
2 | \r | |
3 | Copyright (c) 2005-2006, 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 | dsk 7631 file control\r | |
27 | \r | |
28 | The 7631 is a controller for multiple serial bit stream devices such as\r | |
29 | disks or drums. It supports the\r | |
30 | \r | |
31 | 1301 fixed disk\r | |
32 | 1302 fixed disk\r | |
33 | 2302 fixed disk\r | |
34 | 7320 drum\r | |
35 | \r | |
36 | The 7631 supports variable record formatting, user-specified record numbering,\r | |
37 | and other complex features. Each track has\r | |
38 | \r | |
39 | home address 1: the track number, 4 BCD digits (implicit)\r | |
40 | home address 2: user-specified track identifier, 6 BCD chars\r | |
41 | records 1..n: variably formatted records, each consisting of\r | |
42 | record address: user-specified record identifier, 4 BCD digits\r | |
43 | and 2 BCD characters\r | |
44 | record data: 36b words\r | |
45 | \r | |
46 | To deal with this, the simulator provides a container of 500 (7320/1301) or\r | |
47 | 1000 (1302/2302) words per track. Each track starts with home address 2\r | |
48 | and then contains a variable number of records. Each record has a two-word\r | |
49 | header followed by data:\r | |
50 | \r | |
51 | word 0: record length without header\r | |
52 | word 1: record address\r | |
53 | word 2: start of data\r | |
54 | word 2+n-1: end of data\r | |
55 | word 2+n+2: start of next record\r | |
56 | \r | |
57 | A record length of 0 indicates end of valid data on the track.\r | |
58 | \r | |
59 | Orders to the 7631 are 10 BCD digits (60b), consisting of two words:\r | |
60 | \r | |
61 | word 0: op-op-access-module-d1-d2\r | |
62 | word 1: d3-d4-d5-d6-x-x\r | |
63 | \r | |
64 | Depending on the opcode, d1:d6 can be a track number plus home address 2,\r | |
65 | or a record number.\r | |
66 | \r | |
67 | Status from the 7631 is also 10 BCD digits (60b), with 36b in the first\r | |
68 | word, and 24b (plus 12b of zeroes) in the second.\r | |
69 | \r | |
70 | Because modules can have two access arms that seek independently, each\r | |
71 | module m is represented by two units: unit m for access 0 and unit m+10\r | |
72 | for access 1. This requires tricky bookkeeping to be sure that the\r | |
73 | service routine is using the 'right' unit.\r | |
74 | \r | |
75 | Limitations of the simulation:\r | |
76 | \r | |
77 | - HA2 and record address must be exactly 6 characters (one word)\r | |
78 | - Record lengths must be exact multiples of 6 characters\r | |
79 | - Seek timing is fixed rather than based on seek length\r | |
80 | */\r | |
81 | \r | |
82 | /* Definitions */\r | |
83 | \r | |
84 | #include "i7094_defs.h"\r | |
85 | #include <math.h>\r | |
86 | \r | |
87 | #define DSK_NUMDR 10 /* modules/controller */\r | |
88 | #define DSK_SNS (2 * DSK_NUMDR) /* dummy unit for sense */\r | |
89 | \r | |
90 | /* Drive geometry */\r | |
91 | \r | |
92 | #define DSK_WDSPT_7320 500 /* words/track */\r | |
93 | #define DSK_WDSPT_1301 500\r | |
94 | #define DSK_WDSPT_1302 1000\r | |
95 | #define DSK_WDSPT_2302 1000\r | |
96 | #define DSK_TRKPC_7320 400 /* tracks/cylinder */\r | |
97 | #define DSK_TRKPC_1301 40\r | |
98 | #define DSK_TRKPC_1302 40\r | |
99 | #define DSK_TRKPC_2302 40\r | |
100 | #define DSK_CYLPA_7320 1 /* cylinders/access */\r | |
101 | #define DSK_CYLPA_1301 250\r | |
102 | #define DSK_CYLPA_1302 250\r | |
103 | #define DSK_CYLPA_2302 250\r | |
104 | #define DSK_TRKPA_7320 (DSK_TRKPC_7320*DSK_CYLPA_7320) /* tracks/access */\r | |
105 | #define DSK_TRKPA_1301 (DSK_TRKPC_1301*DSK_CYLPA_1301)\r | |
106 | #define DSK_TRKPA_1302 (DSK_TRKPC_1302*DSK_CYLPA_1302)\r | |
107 | #define DSK_TRKPA_2302 (DSK_TRKPC_2302*DSK_CYLPA_2302)\r | |
108 | #define DSK_ACCPM_7320 1 /* access/module */\r | |
109 | #define DSK_ACCPM_1301 1\r | |
110 | #define DSK_ACCPM_1302 2\r | |
111 | #define DSK_ACCPM_2302 2\r | |
112 | #define DSK_FMCPT_7320 2868 /* format chars/track */\r | |
113 | #define DSK_FMCPT_1301 2868\r | |
114 | #define DSK_FMCPT_1302 5942\r | |
115 | #define DSK_FMCPT_2302 5942\r | |
116 | #define SIZE_7320 (DSK_WDSPT_7320*DSK_TRKPA_7320*DSK_ACCPM_7320)\r | |
117 | #define SIZE_1301 (DSK_WDSPT_1301*DSK_TRKPA_1301*DSK_ACCPM_1301)\r | |
118 | #define SIZE_1302 (DSK_WDSPT_1302*DSK_TRKPA_1302*DSK_ACCPM_1302)\r | |
119 | #define SIZE_2302 (DSK_WDSPT_2302*DSK_TRKPA_2302*DSK_ACCPM_2302)\r | |
120 | #define DSK_BUFSIZ (DSK_WDSPT_2302)\r | |
121 | #define DSK_DA(a,t,d) (((((a) * dsk_tab[d].trkpa) + (t)) * dsk_tab[d].wdspt) *\\r | |
122 | sizeof (t_uint64))\r | |
123 | \r | |
124 | /* Unit flags */\r | |
125 | \r | |
126 | #define UNIT_V_INOP0 (UNIT_V_UF + 0) /* acc 0 inoperative */\r | |
127 | #define UNIT_V_INOP1 (UNIT_V_UF + 1) /* acc 1 inoperative */\r | |
128 | #define UNIT_V_FMTE (UNIT_V_UF + 2) /* format enabled */\r | |
129 | #define UNIT_V_TYPE (UNIT_V_UF + 3) /* drive type */\r | |
130 | #define UNIT_M_TYPE 03\r | |
131 | #define UNIT_INOP0 (1 << UNIT_V_INOP0)\r | |
132 | #define UNIT_INOP1 (1 << UNIT_V_INOP1)\r | |
133 | #define UNIT_FMTE (1 << UNIT_V_FMTE)\r | |
134 | #define UNIT_TYPE (UNIT_M_TYPE << UNIT_V_TYPE)\r | |
135 | #define TYPE_7320 (0 << UNIT_V_TYPE)\r | |
136 | #define TYPE_1301 (1 << UNIT_V_TYPE)\r | |
137 | #define TYPE_1302 (2 << UNIT_V_TYPE)\r | |
138 | #define TYPE_2302 (3 << UNIT_V_TYPE)\r | |
139 | #define GET_DTYPE(x) (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE)\r | |
140 | #define TRK u3 /* track */\r | |
141 | #define SKF u4 /* seek in progress */\r | |
142 | \r | |
143 | /* Track/record structure */\r | |
144 | \r | |
145 | #define THA2 0 /* home address 2 */\r | |
146 | #define HA2_MASK 0777700000000 /* two chars checked */\r | |
147 | #define T1STREC 1 /* start of records */\r | |
148 | #define RLNT 0 /* record length */\r | |
149 | #define RADDR 1 /* record address */\r | |
150 | #define RDATA 2 /* start of data */\r | |
151 | #define REC_MASK 0171717177777 /* 4 digits, 2 chars */\r | |
152 | \r | |
153 | /* Command word (60b) - 10 BCD digits */\r | |
154 | \r | |
155 | #define OP1 0 /* opcode */\r | |
156 | #define OP2 1\r | |
157 | #define ACC 2 /* access */\r | |
158 | #define MOD 3 /* module */\r | |
159 | #define T1 4 /* track */\r | |
160 | #define T2 5\r | |
161 | #define T3 6\r | |
162 | #define T4 7\r | |
163 | \r | |
164 | /* Disk states */\r | |
165 | \r | |
166 | #define DSK_IDLE 0\r | |
167 | \r | |
168 | /* Status word (60b) */\r | |
169 | \r | |
170 | #define DSKS_PCHK 004000000000000000000 /* prog check */\r | |
171 | #define DSKS_DCHK 002000000000000000000 /* data check */\r | |
172 | #define DSKS_EXCC 001000000000000000000 /* exc cond */\r | |
173 | #define DSKS_INVS 000200000000000000000 /* invalid seq */\r | |
174 | #define DSKS_INVC 000040000000000000000 /* invalid opcode */\r | |
175 | #define DSKS_FMTC 000020000000000000000 /* format check */\r | |
176 | #define DSKS_NRCF 000010000000000000000 /* no record found */\r | |
177 | #define DSKS_INVA 000002000000000000000 /* invalid address */\r | |
178 | #define DSKS_RSPC 000000400000000000000 /* response check */\r | |
179 | #define DSKS_CMPC 000000200000000000000 /* compare check */\r | |
180 | #define DSKS_PARC 000000100000000000000 /* parity check */\r | |
181 | #define DSKS_ACCI 000000020000000000000 /* access inoperative */\r | |
182 | #define DSKS_ACCN 000000004000000000000 /* access not ready */\r | |
183 | #define DSKS_DSKE 000000002000000000000 /* disk error */\r | |
184 | #define DSKS_FILE 000000001000000000000 /* file error */\r | |
185 | #define DSKS_6B 000000000040000000000 /* six bit mode */\r | |
186 | #define DSKS_ATN0 000000000002000000000 /* attention start */\r | |
187 | #define DSKS_PALL 000777000000000000000\r | |
188 | #define DSKS_DALL 000000740000000000000\r | |
189 | #define DSKS_EALL 000000037000000000000\r | |
190 | #define DSKS_ALLERR 007777777000000000000\r | |
191 | \r | |
192 | /* Commands - opcode 0 */\r | |
193 | \r | |
194 | #define DSKC_NOP 0x00\r | |
195 | #define DSKC_RLS 0x04\r | |
196 | #define DSKC_8B 0x08\r | |
197 | #define DSKC_6B 0x09\r | |
198 | \r | |
199 | /* Commands - opcode 8 */\r | |
200 | \r | |
201 | #define DSKC_SEEK 0x0 /* seek */\r | |
202 | #define DSKC_SREC 0x2 /* single record */\r | |
203 | #define DSKC_WFMT 0x3 /* write format */\r | |
204 | #define DSKC_TNOA 0x4 /* track no addr */\r | |
205 | #define DSKC_CYL 0x5 /* cyl no addr */\r | |
206 | #define DSKC_WCHK 0x6 /* write check */\r | |
207 | #define DSKC_ACCI 0x7 /* set acc inoperative */\r | |
208 | #define DSKC_TWIA 0x8 /* track with addr */\r | |
209 | #define DSKC_THA 0x9 /* track home addr */\r | |
210 | \r | |
211 | /* CTSS record structure */\r | |
212 | \r | |
213 | #define CTSS_HA2 0676767676767 /* =HXXXXXX */\r | |
214 | #define CTSS_RLNT 435 /* data record */\r | |
215 | #define CTSS_D1LNT 31 /* padding */\r | |
216 | #define CTSS_D2LNT 14\r | |
217 | #define CTSS_D3LNT 16\r | |
218 | #define CTSS_DLLNT 1\r | |
219 | #define CTSS_RA1 2\r | |
220 | #define CTSS_RA2 8\r | |
221 | \r | |
222 | /* Data and declarations */\r | |
223 | \r | |
224 | typedef struct {\r | |
225 | char *name;\r | |
226 | uint32 accpm; /* acc/module: 1 or 2 */\r | |
227 | uint32 wdspt; /* wds/track: 500 or 1000 */\r | |
228 | uint32 trkpc; /* trks/cyl: 1 or 40 */\r | |
229 | uint32 trkpa; /* trks/acc: 400 or 10000 */\r | |
230 | uint32 fchpt; /* format ch/track */\r | |
231 | uint32 size;\r | |
232 | } DISK_TYPE;\r | |
233 | \r | |
234 | const DISK_TYPE dsk_tab[4] = {\r | |
235 | { "7320", DSK_ACCPM_7320, DSK_WDSPT_7320,\r | |
236 | DSK_TRKPC_7320, DSK_TRKPA_7320, DSK_FMCPT_7320, SIZE_7320 },\r | |
237 | { "1301", DSK_ACCPM_1301, DSK_WDSPT_1301,\r | |
238 | DSK_TRKPC_1301, DSK_TRKPA_1301, DSK_FMCPT_1301, SIZE_1301 },\r | |
239 | { "1302", DSK_ACCPM_1302, DSK_WDSPT_1302,\r | |
240 | DSK_TRKPC_1302, DSK_TRKPA_1302, DSK_FMCPT_1302, SIZE_1302 },\r | |
241 | { "2302", DSK_ACCPM_2302, DSK_WDSPT_2302,\r | |
242 | DSK_TRKPC_2302, DSK_TRKPA_2302, DSK_FMCPT_2302, SIZE_2302 }\r | |
243 | };\r | |
244 | \r | |
245 | /* 7320/1301 format track characters */\r | |
246 | \r | |
247 | uint8 fmt_thdr_7320[] = {\r | |
248 | 4, 4, 4, /* gap 1 */\r | |
249 | 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */\r | |
250 | 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */\r | |
251 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */\r | |
252 | };\r | |
253 | uint8 fmt_rhdr_7320[] = {\r | |
254 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */\r | |
255 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */\r | |
256 | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */\r | |
257 | 1, 1, 1, 1, 0 /* record ovhd */\r | |
258 | };\r | |
259 | \r | |
260 | /* 1302/2302 format track characters */\r | |
261 | \r | |
262 | uint8 fmt_thdr_1302[] = {\r | |
263 | 4, 4, 4, 4, 4, 4, /* gap 1 */\r | |
264 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */\r | |
265 | 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */\r | |
266 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */\r | |
267 | };\r | |
268 | uint8 fmt_rhdr_1302[] = {\r | |
269 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */\r | |
270 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */\r | |
271 | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */\r | |
272 | 1, 1, 1, 1, 1, 1, 1, 0 /* record ovhd */\r | |
273 | };\r | |
274 | \r | |
275 | /* CTSS 7320/1301 track format table */\r | |
276 | \r | |
277 | uint32 ctss_fmt_7320[] = {\r | |
278 | CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0\r | |
279 | };\r | |
280 | \r | |
281 | /* CTSS 1302/2302 track format table */\r | |
282 | \r | |
283 | uint32 ctss_fmt_1302[] = {\r | |
284 | CTSS_RLNT, CTSS_D1LNT, CTSS_D2LNT,\r | |
285 | CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0\r | |
286 | };\r | |
287 | \r | |
288 | uint32 dsk_ch = CH_C; /* disk channel */\r | |
289 | uint32 dsk_acc = 0; /* access */\r | |
290 | uint32 dsk_mod = 0; /* module */\r | |
291 | uint32 dsk_sta = 0; /* disk state */\r | |
292 | uint32 dsk_mode = 0; /* I/O mode */\r | |
293 | uint32 dsk_wchk = 0; /* write check flag */\r | |
294 | uint32 dsk_ctime = 10; /* command time */\r | |
295 | uint32 dsk_stime = 1000; /* seek time */\r | |
296 | uint32 dsk_rtime = 100; /* rotational latency */\r | |
297 | uint32 dsk_wtime = 2; /* word time */\r | |
298 | uint32 dsk_gtime = 5; /* gap time */\r | |
299 | uint32 dsk_rbase = 0; /* record tracking */\r | |
300 | uint32 dsk_rptr = 0;\r | |
301 | uint32 dsk_rlim = 0;\r | |
302 | uint32 dsk_stop = 0;\r | |
303 | uint32 dsk_fmt_cntr = 0; /* format counter */\r | |
304 | t_uint64 dsk_rec = 0; /* rec/home addr (36b) */\r | |
305 | t_uint64 dsk_sns = 0; /* sense data (60b) */\r | |
306 | t_uint64 dsk_cmd = 0; /* BCD command (60b) */\r | |
307 | t_uint64 dsk_chob = 0; /* chan output buffer */\r | |
308 | uint32 dsk_chob_v = 0; /* valid */\r | |
309 | t_uint64 dsk_buf[DSK_BUFSIZ]; /* transfer buffer */\r | |
310 | \r | |
311 | extern uint32 ch_req;\r | |
312 | \r | |
313 | t_stat dsk_svc (UNIT *uptr);\r | |
314 | t_stat dsk_svc_sns (UNIT *uptr);\r | |
315 | t_stat dsk_reset (DEVICE *dptr);\r | |
316 | t_stat dsk_attach (UNIT *uptr, char *cptr);\r | |
317 | t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
318 | t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit);\r | |
319 | t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 flags);\r | |
320 | t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd);\r | |
321 | t_stat dsk_uend (uint32 ch, t_uint64 stat);\r | |
322 | t_stat dsk_recad (uint32 trk, uint32 rec, uint32 acc, uint32 mod, t_uint64 *res);\r | |
323 | t_uint64 dsk_acc_atn (uint32 u);\r | |
324 | t_stat dsk_init_trk (UNIT *udptr, uint32 trk);\r | |
325 | t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp);\r | |
326 | t_stat dsk_wr_trk (UNIT *uptr, uint32 trk);\r | |
327 | t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc);\r | |
328 | t_bool dsk_qdone (uint32 ch);\r | |
329 | t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
330 | \r | |
331 | /* DSK data structures\r | |
332 | \r | |
333 | dsk_dev DSK device descriptor\r | |
334 | dsk_unit DSK unit descriptor\r | |
335 | dsk_reg DSK register list\r | |
336 | */\r | |
337 | \r | |
338 | DIB dsk_dib = { &dsk_chsel, &dsk_chwr };\r | |
339 | \r | |
340 | UNIT dsk_unit[] = {\r | |
341 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
342 | TYPE_2302, SIZE_2302) },\r | |
343 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
344 | TYPE_2302, SIZE_2302) },\r | |
345 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
346 | TYPE_7320, SIZE_7320) },\r | |
347 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
348 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r | |
349 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
350 | TYPE_2302, SIZE_2302) },\r | |
351 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
352 | TYPE_2302, SIZE_2302) },\r | |
353 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
354 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r | |
355 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
356 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r | |
357 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
358 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r | |
359 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
360 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r | |
361 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
362 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
363 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
364 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
365 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
366 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
367 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
368 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
369 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
370 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r | |
371 | { UDATA (&dsk_svc_sns, UNIT_DIS, 0) }\r | |
372 | };\r | |
373 | \r | |
374 | REG dsk_reg[] = {\r | |
375 | { ORDATA (STATE, dsk_sta, 6) },\r | |
376 | { ORDATA (ACCESS, dsk_acc, 1) },\r | |
377 | { ORDATA (MODULE, dsk_mod, 4) },\r | |
378 | { ORDATA (RECORD, dsk_rec, 36) },\r | |
379 | { ORDATA (MODE, dsk_mode, 4) },\r | |
380 | { ORDATA (SENSE, dsk_sns, 60) },\r | |
381 | { ORDATA (BCDCMD, dsk_cmd, 60) },\r | |
382 | { ORDATA (CHOB, dsk_chob, 36) },\r | |
383 | { FLDATA (CHOBV, dsk_chob_v, 0) },\r | |
384 | { FLDATA (STOP, dsk_stop, 0) },\r | |
385 | { DRDATA (FCNTR, dsk_fmt_cntr, 13) },\r | |
386 | { BRDATA (BUF, dsk_buf, 8, 36, DSK_BUFSIZ) },\r | |
387 | { DRDATA (RBASE, dsk_rbase, 10), REG_RO },\r | |
388 | { DRDATA (RPTR, dsk_rptr, 10), REG_RO },\r | |
389 | { DRDATA (RLIM, dsk_rlim, 10), REG_RO },\r | |
390 | { DRDATA (CHAN, dsk_ch, 3), REG_HRO },\r | |
391 | { DRDATA (STIME, dsk_stime, 24), REG_NZ + PV_LEFT },\r | |
392 | { DRDATA (RTIME, dsk_rtime, 24), REG_NZ + PV_LEFT },\r | |
393 | { DRDATA (WTIME, dsk_wtime, 24), REG_NZ + PV_LEFT },\r | |
394 | { DRDATA (GTIME, dsk_gtime, 24), REG_NZ + PV_LEFT },\r | |
395 | { DRDATA (CTIME, dsk_ctime, 24), REG_NZ + PV_LEFT },\r | |
396 | { URDATA (TRACK, dsk_unit[0].TRK, 10, 14, 0,\r | |
397 | 2 * DSK_NUMDR, PV_LEFT) },\r | |
398 | { URDATA (SEEKF, dsk_unit[0].SKF, 10, 1, 0,\r | |
399 | 2 * DSK_NUMDR, PV_LEFT | REG_HRO) },\r | |
400 | { URDATA (CAPAC, dsk_unit[0].capac, 10, T_ADDR_W, 0,\r | |
401 | DSK_NUMDR, PV_LEFT | REG_HRO) },\r | |
402 | { NULL }\r | |
403 | };\r | |
404 | \r | |
405 | MTAB dsk_mtab[] = {\r | |
406 | { UNIT_INOP0 + UNIT_INOP1, 0, "operational", "OPERATIONAL" },\r | |
407 | { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP0, "access 0 inoperative", NULL },\r | |
408 | { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP1, "access 1 inoperative", NULL },\r | |
409 | { UNIT_FMTE, UNIT_FMTE, "formating enabled", "FORMAT" },\r | |
410 | { UNIT_FMTE, 0, "formating disabled", "NOFORMAT" },\r | |
411 | { UNIT_TYPE, TYPE_7320, "7320", "7320", &dsk_set_size },\r | |
412 | { UNIT_TYPE, TYPE_1301, "1301", "1301", &dsk_set_size },\r | |
413 | { UNIT_TYPE, TYPE_1302, "1302", "1302", &dsk_set_size },\r | |
414 | { UNIT_TYPE, TYPE_2302, "2302", "2302", &dsk_set_size },\r | |
415 | { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,\r | |
416 | NULL, &ch_show_chan, NULL },\r | |
417 | { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, "FORMAT", NULL,\r | |
418 | NULL, &dsk_show_format, NULL },\r | |
419 | { 0 }\r | |
420 | };\r | |
421 | \r | |
422 | DEVICE dsk_dev = {\r | |
423 | "DSK", dsk_unit, dsk_reg, dsk_mtab,\r | |
424 | (DSK_NUMDR * 2) + 1, 10, 24, 1, 8, 36,\r | |
425 | NULL, NULL, &dsk_reset,\r | |
426 | NULL, &dsk_attach, NULL,\r | |
427 | &dsk_dib, DEV_DIS\r | |
428 | };\r | |
429 | \r | |
430 | /* Disk channel select, from 7909 channel program */\r | |
431 | \r | |
432 | t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit)\r | |
433 | {\r | |
434 | uint32 u;\r | |
435 | \r | |
436 | dsk_ch = ch;\r | |
437 | if (dsk_sta != DSK_IDLE) dsk_uend (ch, DSKS_INVS); /* not idle? seq check */\r | |
438 | \r | |
439 | switch (sel) {\r | |
440 | \r | |
441 | case CHSL_CTL: /* control */\r | |
442 | ch_req |= REQ_CH (ch); /* request channel */\r | |
443 | break;\r | |
444 | \r | |
445 | case CHSL_SNS: /* sense */\r | |
446 | if (sim_is_active (&dsk_unit[DSK_SNS])) /* already sensing? */\r | |
447 | return dsk_uend (ch, DSKS_INVS); /* sequence error */\r | |
448 | sim_activate (&dsk_unit[DSK_SNS], dsk_ctime); /* set timer */\r | |
449 | dsk_stop = 0;\r | |
450 | break;\r | |
451 | \r | |
452 | case CHSL_RDS: /* read */\r | |
453 | if (dsk_mode == DSKC_WFMT) /* write format? */\r | |
454 | return dsk_uend (ch, DSKS_INVS); /* sequence error */\r | |
455 | case CHSL_WRS: /* write */\r | |
456 | if (dsk_mode == 0) dsk_uend (ch, DSKS_INVS); /* no mode? seq check */\r | |
457 | if (dsk_mode == DSKC_WFMT) sel = CHSL_FMT; /* format? fake sel */\r | |
458 | u = (dsk_acc * DSK_NUMDR) + dsk_mod; /* access unit number */\r | |
459 | if (sim_is_active (&dsk_unit[u])) /* access in use? */\r | |
460 | return dsk_uend (ch, DSKS_ACCN); /* access not ready */\r | |
461 | sim_activate (&dsk_unit[u], dsk_rtime); /* rotational time */\r | |
462 | break;\r | |
463 | \r | |
464 | default: /* other */\r | |
465 | return STOP_ILLIOP;\r | |
466 | }\r | |
467 | \r | |
468 | dsk_sta = sel; /* set new state */\r | |
469 | return SCPE_OK;\r | |
470 | }\r | |
471 | \r | |
472 | /* Disk channel write, from 7909 channel program */\r | |
473 | \r | |
474 | t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 stopf)\r | |
475 | {\r | |
476 | if (stopf) dsk_stop = 1; /* stop? */\r | |
477 | \r | |
478 | else {\r | |
479 | val = val & DMASK;\r | |
480 | switch (dsk_sta) { /* case on state */\r | |
481 | \r | |
482 | case CHSL_CTL: /* control */\r | |
483 | dsk_cmd = val << 24;\r | |
484 | if (val & 0100000000000) { /* need 2nd word? */\r | |
485 | ch_req |= REQ_CH (ch); /* req ch for 2nd */\r | |
486 | dsk_sta = CHSL_CTL|CHSL_2ND; /* next state */\r | |
487 | return SCPE_OK;\r | |
488 | }\r | |
489 | return dsk_new_cmd (ch, dsk_cmd); /* no, do cmd */\r | |
490 | \r | |
491 | case CHSL_CTL|CHSL_2ND: /* 2nd control */\r | |
492 | dsk_cmd |= (val >> 12);\r | |
493 | return dsk_new_cmd (ch, dsk_cmd); /* do cmd */\r | |
494 | \r | |
495 | default:\r | |
496 | dsk_chob = val; /* store data */\r | |
497 | dsk_chob_v = 1; /* set valid */\r | |
498 | }\r | |
499 | }\r | |
500 | \r | |
501 | return SCPE_OK;\r | |
502 | }\r | |
503 | \r | |
504 | /* New command - end of CTL sequence */\r | |
505 | \r | |
506 | t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd)\r | |
507 | {\r | |
508 | uint32 i, d, a, m, u, trk, dtyp, bcd[8];\r | |
509 | \r | |
510 | ch_req |= REQ_CH (ch); /* req ch for end */\r | |
511 | ch9_set_end (ch, 0); /* set end flag */\r | |
512 | dsk_sta = DSK_IDLE; /* ctrl is idle */\r | |
513 | \r | |
514 | for (i = 0; i < 8; i++) { /* get chars from cmd */\r | |
515 | d = (uint32) (cmd >> (6 * (9 - i))) & BCD_MASK;\r | |
516 | if (d == BCD_ZERO) d = 0;\r | |
517 | else if (d == 0) d = BCD_ZERO; /* BCD zero cvt */\r | |
518 | bcd[i] = d;\r | |
519 | }\r | |
520 | \r | |
521 | if (bcd[OP1] == 0) { /* cmd = 0x? */\r | |
522 | \r | |
523 | switch (bcd[OP2]) { /* case on x */\r | |
524 | \r | |
525 | case DSKC_NOP: /* nop */\r | |
526 | case DSKC_RLS: /* release */\r | |
527 | break;\r | |
528 | \r | |
529 | case DSKC_8B: /* 8b mode */\r | |
530 | dsk_sns &= ~DSKS_6B;\r | |
531 | break;\r | |
532 | \r | |
533 | case DSKC_6B: /* 6b mode */\r | |
534 | dsk_sns |= DSKS_6B;\r | |
535 | break;\r | |
536 | \r | |
537 | default: /* unknown */\r | |
538 | return dsk_uend (ch, DSKS_INVC); /* invalid opcode */\r | |
539 | } /* end case op2 */\r | |
540 | return SCPE_OK;\r | |
541 | } /* end if */\r | |
542 | \r | |
543 | else if (bcd[OP1] == 8) { /* cmd = 8x? */\r | |
544 | \r | |
545 | a = bcd[ACC]; /* get access, */\r | |
546 | m = bcd[MOD]; /* module */\r | |
547 | u = (a * DSK_NUMDR) + m; /* unit for access */\r | |
548 | if ((m > DSK_NUMDR) || /* invalid module? */\r | |
549 | (dsk_unit[m].flags & UNIT_DIS)) /* disabled module? */\r | |
550 | return dsk_uend (ch, DSKS_ACCI);\r | |
551 | dtyp = GET_DTYPE (dsk_unit[m].flags); /* get drive type */\r | |
552 | if ((a >= dsk_tab[dtyp].accpm) || /* invalid access? */\r | |
553 | (dsk_unit[m].flags & (UNIT_INOP0 << a))) /* access inop? */\r | |
554 | return dsk_uend (ch, DSKS_ACCI);\r | |
555 | if ((bcd[T1] > 9) || (bcd[T2] > 9) || (bcd[T3] > 9) || (bcd[T4] > 9))\r | |
556 | trk = dsk_tab[dtyp].trkpa + 1; /* bad track */\r | |
557 | else trk = (((((bcd[T1] * 10) + bcd[T2]) * 10) + bcd[T3]) * 10) + bcd[T4];\r | |
558 | \r | |
559 | if (bcd[OP2] == DSKC_WCHK) { /* write check */\r | |
560 | if (dsk_mode == 0) /* no prior operation? */\r | |
561 | return dsk_uend (ch, DSKS_INVS);\r | |
562 | bcd[OP2] = dsk_mode; /* use prior mode */\r | |
563 | dsk_wchk = 1; /* set write check */\r | |
564 | }\r | |
565 | else dsk_wchk = 0;\r | |
566 | dsk_sns &= ~(DSKS_ALLERR | dsk_acc_atn (u)); /* clear err, atn */\r | |
567 | dsk_stop = 0; /* clear stop */\r | |
568 | \r | |
569 | switch (bcd[OP2]) {\r | |
570 | \r | |
571 | case DSKC_SEEK: /* seek */\r | |
572 | if ((trk >= dsk_tab[dtyp].trkpa) && /* inv track? */\r | |
573 | ((dtyp == TYPE_7320) || /* drum or not CE? */\r | |
574 | (bcd[T1] > 9) || (bcd[T2] != BCD_AT) ||\r | |
575 | (bcd[T3] > 9) || (bcd[T4] > 9)))\r | |
576 | return dsk_uend (ch, DSKS_INVA);\r | |
577 | if (sim_is_active (&dsk_unit[u])) /* selected acc busy? */\r | |
578 | return dsk_uend (ch, DSKS_ACCN);\r | |
579 | dsk_unit[u].SKF = 1; /* set seeking flag */\r | |
580 | dsk_unit[u].TRK = trk; /* sel acc on cyl */\r | |
581 | sim_activate (&dsk_unit[u], dsk_stime); /* seek */\r | |
582 | dsk_mode = 0; /* clear I/O mode */\r | |
583 | return SCPE_OK;\r | |
584 | \r | |
585 | case DSKC_ACCI: /* access inoperative */\r | |
586 | dsk_unit[m].flags |= (UNIT_INOP0 << a); /* set correct flag */\r | |
587 | dsk_mode = 0; /* clear I/O mode */\r | |
588 | return SCPE_OK;\r | |
589 | \r | |
590 | case DSKC_SREC: /* single record */\r | |
591 | break; /* no verification */\r | |
592 | \r | |
593 | case DSKC_WFMT: /* format */\r | |
594 | if (!(dsk_unit[m].flags & UNIT_FMTE)) /* format enabled? */\r | |
595 | return dsk_uend (ch, DSKS_FMTC); /* no, error */\r | |
596 | case DSKC_TNOA: /* track no addr */\r | |
597 | case DSKC_CYL: /* cyl no addr */\r | |
598 | case DSKC_TWIA: /* track with addr */\r | |
599 | case DSKC_THA: /* track home addr */\r | |
600 | if (trk != (uint32) dsk_unit[u].TRK) /* on track? */\r | |
601 | return dsk_uend (ch, DSKS_NRCF);\r | |
602 | break; \r | |
603 | \r | |
604 | default:\r | |
605 | return dsk_uend (ch, DSKS_INVC); /* invalid opcode */\r | |
606 | }\r | |
607 | \r | |
608 | dsk_acc = a; /* save access */\r | |
609 | dsk_mod = m; /* save module */\r | |
610 | dsk_rec = cmd & DMASK; /* save rec/home addr */\r | |
611 | dsk_mode = bcd[OP2]; /* save mode */\r | |
612 | return SCPE_OK;\r | |
613 | }\r | |
614 | \r | |
615 | return dsk_uend (ch, DSKS_INVC); /* invalid opcode */\r | |
616 | }\r | |
617 | \r | |
618 | /* Sense unit service */\r | |
619 | \r | |
620 | t_stat dsk_svc_sns (UNIT *uptr)\r | |
621 | {\r | |
622 | t_uint64 dat;\r | |
623 | \r | |
624 | switch (dsk_sta) { /* case on state */\r | |
625 | \r | |
626 | case CHSL_SNS: /* prepare data */\r | |
627 | dsk_buf[0] = (dsk_sns >> 24) & DMASK; /* buffer is 2 words */\r | |
628 | dsk_buf[1] = (dsk_sns << 12) & DMASK;\r | |
629 | dsk_rptr = 0;\r | |
630 | dsk_rlim = 2;\r | |
631 | dsk_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */\r | |
632 | break;\r | |
633 | \r | |
634 | case CHSL_SNS|CHSL_2ND: /* second state */\r | |
635 | if (dsk_rptr >= dsk_rlim) { /* end of buffer? */\r | |
636 | ch9_set_end (dsk_ch, 0); /* set end */\r | |
637 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r | |
638 | dsk_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */\r | |
639 | sim_activate (uptr, dsk_ctime); /* longer wait */\r | |
640 | return SCPE_OK;\r | |
641 | }\r | |
642 | dat = dsk_buf[dsk_rptr++]; /* get word */\r | |
643 | if (!dsk_stop) ch9_req_rd (dsk_ch, dat); /* send wd to chan */\r | |
644 | break;\r | |
645 | \r | |
646 | case CHSL_SNS|CHSL_3RD: /* 3rd state */\r | |
647 | if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */\r | |
648 | dsk_sta = CHSL_SNS; /* repeat sequence */\r | |
649 | break;\r | |
650 | }\r | |
651 | \r | |
652 | sim_activate (uptr, dsk_wtime); /* sched next */\r | |
653 | return SCPE_OK;\r | |
654 | }\r | |
655 | \r | |
656 | /* Seek, read, write unit service */\r | |
657 | \r | |
658 | t_stat dsk_svc (UNIT *uaptr)\r | |
659 | {\r | |
660 | uint32 i, dtyp, trk;\r | |
661 | uint8 fc, *format;\r | |
662 | t_uint64 rdat;\r | |
663 | UNIT *udptr;\r | |
664 | t_stat r;\r | |
665 | \r | |
666 | if (uaptr->SKF) { /* seeking? */\r | |
667 | uint32 u = uaptr - dsk_dev.units; /* get unit */\r | |
668 | uaptr->SKF = 0; /* seek done */\r | |
669 | dsk_sns |= dsk_acc_atn (u); /* set atn bit */\r | |
670 | ch9_set_atn (dsk_ch); /* set atn flag */\r | |
671 | return SCPE_OK;\r | |
672 | }\r | |
673 | \r | |
674 | udptr = dsk_dev.units + dsk_mod; /* data unit */\r | |
675 | if (udptr->flags & (UNIT_INOP0 << dsk_acc)) /* acc inoperative? */\r | |
676 | return dsk_uend (dsk_ch, DSKS_ACCI); /* error */\r | |
677 | if ((udptr->flags & UNIT_ATT) == 0) { /* not attached? */\r | |
678 | dsk_uend (dsk_ch, DSKS_ACCI); /* error */\r | |
679 | return SCPE_UNATT;\r | |
680 | }\r | |
681 | \r | |
682 | dtyp = GET_DTYPE (udptr->flags); /* get data drive type */\r | |
683 | trk = uaptr->TRK; /* get access track */\r | |
684 | \r | |
685 | switch (dsk_sta) { /* case on state */\r | |
686 | \r | |
687 | case CHSL_RDS: /* read start */\r | |
688 | if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */\r | |
689 | return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */\r | |
690 | }\r | |
691 | dsk_sta = CHSL_RDS|CHSL_2ND; /* next state */\r | |
692 | break;\r | |
693 | \r | |
694 | case CHSL_RDS|CHSL_2ND: /* read data transmit */\r | |
695 | if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */\r | |
696 | if (r != ERR_ENDRC) return r; /* error? */\r | |
697 | dsk_sta = CHSL_RDS|CHSL_3RD; /* next state */\r | |
698 | sim_activate (uaptr, dsk_gtime); /* gap time */\r | |
699 | return SCPE_OK;\r | |
700 | }\r | |
701 | rdat = dsk_buf[dsk_rptr++]; /* get word */\r | |
702 | if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */\r | |
703 | if (!dsk_stop) ch9_req_rd (dsk_ch, rdat); /* give to channel */\r | |
704 | break;\r | |
705 | \r | |
706 | case CHSL_RDS|CHSL_3RD: /* read end rec/trk */\r | |
707 | if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */\r | |
708 | dsk_sta = CHSL_RDS; /* repeat sequence */\r | |
709 | break;\r | |
710 | \r | |
711 | case CHSL_WRS: /* write start */\r | |
712 | if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */\r | |
713 | return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */\r | |
714 | }\r | |
715 | ch_req |= REQ_CH (dsk_ch); /* first request */\r | |
716 | dsk_sta = CHSL_WRS|CHSL_2ND; /* next state */\r | |
717 | dsk_chob = 0; /* clr, inval buffer */\r | |
718 | dsk_chob_v = 0;\r | |
719 | break;\r | |
720 | \r | |
721 | case CHSL_WRS|CHSL_2ND: /* write data transmit */\r | |
722 | if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */\r | |
723 | else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */\r | |
724 | if (dsk_wchk) { /* write check? */\r | |
725 | if (dsk_buf[dsk_rptr++] != dsk_chob) /* data mismatch? */\r | |
726 | return dsk_uend (dsk_ch, DSKS_CMPC); /* error */\r | |
727 | }\r | |
728 | else dsk_buf[dsk_rptr++] = dsk_chob; /* write, store word */\r | |
729 | if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */\r | |
730 | if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */\r | |
731 | if (r != ERR_ENDRC) return r; /* error? */\r | |
732 | dsk_sta = CHSL_WRS|CHSL_3RD; /* next state */\r | |
733 | sim_activate (uaptr, dsk_gtime); /* gap time */\r | |
734 | return SCPE_OK;\r | |
735 | }\r | |
736 | if (!dsk_stop) ch_req |= REQ_CH (dsk_ch); /* more to do */\r | |
737 | break;\r | |
738 | \r | |
739 | case CHSL_WRS|CHSL_3RD: /* write done */\r | |
740 | if (!dsk_wchk) { /* if write */\r | |
741 | if (r = dsk_wr_trk (udptr, trk)) return r; /* write track; err? */\r | |
742 | }\r | |
743 | if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */\r | |
744 | dsk_sta = CHSL_WRS; /* repeat sequence */\r | |
745 | break;\r | |
746 | \r | |
747 | /* Formatting takes place in five stages\r | |
748 | \r | |
749 | 1. Clear the track buffer, request the first word from the channel\r | |
750 | 2. Match characters against the fixed overhead (HA1, HA2, and gaps)\r | |
751 | 3. Match characters against the per-record overhead (RA and gaps)\r | |
752 | 4. Count the characters defining the record length\r | |
753 | 5. See if the next character is end or gap; if gap, return to stage 3\r | |
754 | \r | |
755 | This formating routine is not exact. It checks whether the format\r | |
756 | will fit in the container, not whether the format would fit on a\r | |
757 | real 7320, 1301, 1302, or 2302. */\r | |
758 | \r | |
759 | case CHSL_FMT: /* initialization */\r | |
760 | for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0;/* clear track buf */\r | |
761 | dsk_rbase = T1STREC; /* init record ptr */\r | |
762 | dsk_rptr = 0; /* init format ptr */\r | |
763 | dsk_fmt_cntr = 0; /* init counter */\r | |
764 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r | |
765 | dsk_sta = CHSL_FMT|CHSL_2ND; /* next state */\r | |
766 | dsk_chob = 0; /* clr, inval buffer */\r | |
767 | dsk_chob_v = 0;\r | |
768 | break;\r | |
769 | \r | |
770 | case CHSL_FMT|CHSL_2ND: /* match track header */\r | |
771 | if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301))\r | |
772 | format = fmt_thdr_7320;\r | |
773 | else format = fmt_thdr_1302;\r | |
774 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r | |
775 | if (fc != format[dsk_rptr++]) /* mismatch? */\r | |
776 | return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r | |
777 | if (format[dsk_rptr] == 0) { /* end format? */\r | |
778 | dsk_sta = CHSL_FMT|CHSL_3RD; /* next state */\r | |
779 | dsk_rptr = 0; /* reset format ptr */\r | |
780 | }\r | |
781 | break;\r | |
782 | \r | |
783 | case CHSL_FMT|CHSL_3RD: /* match record header */\r | |
784 | if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301))\r | |
785 | format = fmt_rhdr_7320;\r | |
786 | else format = fmt_rhdr_1302;\r | |
787 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r | |
788 | if (fc != format[dsk_rptr++]) /* mismatch? */\r | |
789 | return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r | |
790 | if (format[dsk_rptr] == 0) { /* end format? */\r | |
791 | dsk_sta = CHSL_FMT|CHSL_4TH; /* next state */\r | |
792 | dsk_rlim = 0; /* reset record ctr */\r | |
793 | }\r | |
794 | break;\r | |
795 | \r | |
796 | case CHSL_FMT|CHSL_4TH: /* count record size */\r | |
797 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r | |
798 | if (fc == BCD_ONE) dsk_rlim++; /* more record? */\r | |
799 | else {\r | |
800 | uint32 rsiz = dsk_rlim / 6; /* rec size words */\r | |
801 | if ((fc != BCD_TWO) || /* improper end? */\r | |
802 | (rsiz == 0) || /* smaller than min? */\r | |
803 | ((dsk_rlim % 6) != 0) || /* not multiple of 6? */\r | |
804 | ((dsk_rbase + rsiz + RDATA) >= dsk_tab[dtyp].wdspt))\r | |
805 | return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r | |
806 | dsk_buf[dsk_rbase + RLNT] = rsiz; /* record rec lnt */\r | |
807 | dsk_rbase = dsk_rbase + rsiz + RDATA; /* new rec start */\r | |
808 | dsk_sta = CHSL_FMT|CHSL_5TH; /* next state */\r | |
809 | }\r | |
810 | break;\r | |
811 | \r | |
812 | case CHSL_FMT|CHSL_5TH: /* record or track end */\r | |
813 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r | |
814 | if (fc == BCD_TWO) { /* back to record header? */\r | |
815 | dsk_rptr = 2; /* already done 2 chars */\r | |
816 | dsk_sta = CHSL_FMT|CHSL_3RD; /* record header state */\r | |
817 | }\r | |
818 | else if (fc != BCD_ONE) dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r | |
819 | else {\r | |
820 | if (!dsk_wchk) { /* actual write? */\r | |
821 | trk = trk - (trk % dsk_tab[dtyp].trkpc); /* cyl start */\r | |
822 | for (i = 0; i < dsk_tab[dtyp].trkpc; i++) { /* do all tracks */\r | |
823 | if (r = dsk_wr_trk (udptr, trk + i)) /* wr track; err? */\r | |
824 | return r;\r | |
825 | }\r | |
826 | }\r | |
827 | ch9_set_end (dsk_ch, 0); /* set end */\r | |
828 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r | |
829 | dsk_sta = DSK_IDLE; /* disk is idle */\r | |
830 | return SCPE_OK; /* done */\r | |
831 | }\r | |
832 | break;\r | |
833 | \r | |
834 | default:\r | |
835 | return SCPE_IERR;\r | |
836 | }\r | |
837 | \r | |
838 | sim_activate (uaptr, dsk_wtime);\r | |
839 | return SCPE_OK;\r | |
840 | }\r | |
841 | \r | |
842 | /* Initialize data transfer\r | |
843 | \r | |
844 | Inputs:\r | |
845 | udptr = pointer to data unit\r | |
846 | trk = track to read\r | |
847 | Outputs:\r | |
848 | dsk_buf contains track specified by trk\r | |
849 | dsk_rbase, dsk_rptr, dsk_rlim are initialized\r | |
850 | Errors:\r | |
851 | SCPE_IOERR = I/O error (fatal, uend)\r | |
852 | ERR_NRCF = no record found (HA2 or record number mismatch, uend)\r | |
853 | STOP_INVFMT = invalid format (fatal, uend)\r | |
854 | */\r | |
855 | \r | |
856 | t_stat dsk_init_trk (UNIT *udptr, uint32 trk)\r | |
857 | {\r | |
858 | uint32 k, da, dtyp, rlnt;\r | |
859 | \r | |
860 | dtyp = GET_DTYPE (udptr->flags); /* get drive type */\r | |
861 | da = DSK_DA (dsk_acc, trk, dtyp); /* get disk address */\r | |
862 | sim_fseek (udptr->fileref, da, SEEK_SET); /* read track */\r | |
863 | k = sim_fread (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref);\r | |
864 | if (ferror (udptr->fileref)) { /* error? */\r | |
865 | perror ("DSK I/O error");\r | |
866 | clearerr (udptr->fileref);\r | |
867 | dsk_uend (dsk_ch, DSKS_DSKE);\r | |
868 | return SCPE_IOERR;\r | |
869 | }\r | |
870 | for ( ; k < dsk_tab[dtyp].wdspt; k++) dsk_buf[k] = 0; /* zero fill */\r | |
871 | dsk_rbase = T1STREC; /* record base */\r | |
872 | rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r | |
873 | dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */\r | |
874 | if ((rlnt == 0) || (dsk_rlim >= dsk_tab[dtyp].wdspt)) { /* invalid record? */\r | |
875 | dsk_uend (dsk_ch, DSKS_FMTC);\r | |
876 | return STOP_INVFMT;\r | |
877 | }\r | |
878 | if (dsk_mode != DSKC_SREC) { /* not single record? */\r | |
879 | if (dsk_mode == DSKC_THA) dsk_rptr = 0; /* trk home addr? */\r | |
880 | else {\r | |
881 | if (((dsk_rec << 24) ^ dsk_buf[THA2]) & HA2_MASK) {\r | |
882 | dsk_uend (dsk_ch, DSKS_NRCF); /* invalid HA2 */\r | |
883 | return ERR_NRCF;\r | |
884 | }\r | |
885 | if (dsk_mode == DSKC_TWIA) /* track with addr? */\r | |
886 | dsk_rptr = dsk_rbase + RADDR; /* start at addr */\r | |
887 | else dsk_rptr = dsk_rbase + RDATA; /* else, at data */\r | |
888 | }\r | |
889 | return SCPE_OK;\r | |
890 | }\r | |
891 | while (rlnt != 0) { /* until end track */\r | |
892 | dsk_rptr = dsk_rbase + RDATA;\r | |
893 | if (((dsk_rec ^ dsk_buf[dsk_rbase + RADDR]) & REC_MASK) == 0)\r | |
894 | return SCPE_OK; /* rec found? done */\r | |
895 | dsk_rbase = dsk_rlim; /* next record */\r | |
896 | rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r | |
897 | dsk_rlim = dsk_rbase + rlnt + RDATA; /* limit */\r | |
898 | if (dsk_rlim >= dsk_tab[dtyp].wdspt) { /* invalid format? */\r | |
899 | dsk_uend (dsk_ch, DSKS_FMTC);\r | |
900 | return STOP_INVFMT;\r | |
901 | }\r | |
902 | }\r | |
903 | dsk_uend (dsk_ch, DSKS_NRCF); /* not found */\r | |
904 | return ERR_NRCF;\r | |
905 | }\r | |
906 | \r | |
907 | /* Check end of transfer\r | |
908 | \r | |
909 | Inputs:\r | |
910 | uptr = pointer to access unit\r | |
911 | dtyp = drive type\r | |
912 | Outputs:\r | |
913 | ERR_ENDRC = end of record/track/cylinder, end sent, ch req if required\r | |
914 | SCPE_OK = more to do, dsk_rbase, dsk_rptr, dsk_rlim may be updated\r | |
915 | STOP_INVFMT = invalid format (fatal, uend sent)\r | |
916 | */\r | |
917 | \r | |
918 | t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp)\r | |
919 | {\r | |
920 | uint32 rlnt;\r | |
921 | \r | |
922 | if (dsk_rptr < dsk_rlim) return SCPE_OK; /* record done? */\r | |
923 | if (dsk_stop || !ch9_qconn (dsk_ch) || /* stop or err disc or */\r | |
924 | (dsk_mode == DSKC_SREC)) { /* single record? */\r | |
925 | ch9_set_end (dsk_ch, 0); /* set end */\r | |
926 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r | |
927 | return ERR_ENDRC;\r | |
928 | }\r | |
929 | dsk_rbase = dsk_rlim; /* next record */\r | |
930 | rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r | |
931 | dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */\r | |
932 | if ((dsk_rbase >= dsk_tab[dtyp].wdspt) || /* invalid format? */\r | |
933 | (dsk_rlim >= dsk_tab[dtyp].wdspt)) {\r | |
934 | dsk_uend (dsk_ch, DSKS_FMTC);\r | |
935 | return STOP_INVFMT;\r | |
936 | }\r | |
937 | if (rlnt) { /* more on track? */\r | |
938 | if ((dsk_mode == DSKC_THA) || (dsk_mode == DSKC_TWIA))\r | |
939 | dsk_rptr = dsk_rbase + RADDR; /* start with addr */\r | |
940 | else dsk_rptr = dsk_rbase + RDATA; /* or data */\r | |
941 | return SCPE_OK;\r | |
942 | }\r | |
943 | if (dsk_mode == DSKC_CYL) { /* cylinder mode? */\r | |
944 | uaptr->TRK = (uaptr->TRK + 1) % dsk_tab[dtyp].trkpa; /* incr track */\r | |
945 | if (uaptr->TRK % dsk_tab[dtyp].trkpc) /* not cyl end? */\r | |
946 | return ERR_ENDRC; /* don't set end */\r | |
947 | }\r | |
948 | ch9_set_end (dsk_ch, 0); /* set end */\r | |
949 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r | |
950 | return ERR_ENDRC;\r | |
951 | }\r | |
952 | \r | |
953 | /* Write track back to file */\r | |
954 | \r | |
955 | t_stat dsk_wr_trk (UNIT *udptr, uint32 trk)\r | |
956 | {\r | |
957 | uint32 dtyp = GET_DTYPE (udptr->flags);\r | |
958 | uint32 da = DSK_DA (dsk_acc, trk, dtyp);\r | |
959 | \r | |
960 | sim_fseek (udptr->fileref, da, SEEK_SET);\r | |
961 | sim_fwrite (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref);\r | |
962 | if (ferror (udptr->fileref)) {\r | |
963 | perror ("DSK I/O error");\r | |
964 | clearerr (udptr->fileref);\r | |
965 | dsk_uend (dsk_ch, DSKS_DSKE);\r | |
966 | return SCPE_IOERR;\r | |
967 | }\r | |
968 | return SCPE_OK;\r | |
969 | }\r | |
970 | \r | |
971 | /* Synthesize right attention bit from (access * 10 + module) */\r | |
972 | \r | |
973 | t_uint64 dsk_acc_atn (uint32 u)\r | |
974 | {\r | |
975 | uint32 g, b;\r | |
976 | \r | |
977 | g = u / 4; /* bit group */\r | |
978 | b = u % 4; /* bit within group */\r | |
979 | return (DSKS_ATN0 >> ((g * 6) + (b? b + 1: 0)));\r | |
980 | }\r | |
981 | \r | |
982 | /* Get next format character */\r | |
983 | \r | |
984 | t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc)\r | |
985 | {\r | |
986 | uint32 cc = dsk_fmt_cntr % 6;\r | |
987 | \r | |
988 | if (cc == 0) { /* start of word? */\r | |
989 | if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */\r | |
990 | else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */\r | |
991 | }\r | |
992 | *fc = ((uint8) (dsk_chob >> ((5 - cc) * 6))) & 077; /* get character */\r | |
993 | if ((cc == 5) && !dsk_stop) ch_req |= REQ_CH (dsk_ch); /* end of word? */\r | |
994 | if (dsk_fmt_cntr++ >= dsk_tab[dtyp].fchpt) { /* track overflow? */\r | |
995 | dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r | |
996 | return FALSE;\r | |
997 | }\r | |
998 | return TRUE;\r | |
999 | }\r | |
1000 | \r | |
1001 | /* Unusual end (set status and stop) */\r | |
1002 | \r | |
1003 | t_stat dsk_uend (uint32 ch, t_uint64 stat)\r | |
1004 | {\r | |
1005 | dsk_sns |= stat;\r | |
1006 | dsk_sns &= ~(DSKS_PCHK|DSKS_DCHK|DSKS_EXCC);\r | |
1007 | if (dsk_sns & DSKS_PALL) dsk_sns |= DSKS_PCHK;\r | |
1008 | if (dsk_sns & DSKS_DALL) dsk_sns |= DSKS_DCHK;\r | |
1009 | if (dsk_sns & DSKS_EALL) dsk_sns |= DSKS_EXCC;\r | |
1010 | ch9_set_end (ch, CHINT_UEND);\r | |
1011 | ch_req |= REQ_CH (ch);\r | |
1012 | dsk_sta = DSK_IDLE;\r | |
1013 | return SCPE_OK;\r | |
1014 | }\r | |
1015 | \r | |
1016 | /* Test for done */\r | |
1017 | \r | |
1018 | t_bool dsk_qdone (uint32 ch)\r | |
1019 | {\r | |
1020 | if (dsk_stop || !ch9_qconn (ch)) { /* stop or err disc? */\r | |
1021 | dsk_sta = DSK_IDLE; /* disk is idle */\r | |
1022 | return TRUE;\r | |
1023 | }\r | |
1024 | return FALSE;\r | |
1025 | }\r | |
1026 | \r | |
1027 | /* Reset */\r | |
1028 | \r | |
1029 | t_stat dsk_reset (DEVICE *dptr)\r | |
1030 | {\r | |
1031 | uint32 i;\r | |
1032 | UNIT *uptr;\r | |
1033 | \r | |
1034 | dsk_acc = 0;\r | |
1035 | dsk_mod = 0;\r | |
1036 | dsk_rec = 0;\r | |
1037 | dsk_mode = 0;\r | |
1038 | dsk_wchk = 0;\r | |
1039 | dsk_sns = 0;\r | |
1040 | dsk_cmd = 0;\r | |
1041 | dsk_sta = DSK_IDLE;\r | |
1042 | dsk_rbase = 0;\r | |
1043 | dsk_rptr = 0;\r | |
1044 | dsk_rlim = 0;\r | |
1045 | dsk_stop = 0;\r | |
1046 | dsk_fmt_cntr = 0;\r | |
1047 | dsk_chob = 0;\r | |
1048 | dsk_chob_v = 0;\r | |
1049 | for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0;\r | |
1050 | for (i = 0; i <= (2 * DSK_NUMDR); i++) {\r | |
1051 | uptr = dsk_dev.units + i;\r | |
1052 | sim_cancel (uptr);\r | |
1053 | uptr->TRK = 0;\r | |
1054 | uptr->SKF = 0;\r | |
1055 | }\r | |
1056 | return SCPE_OK;\r | |
1057 | }\r | |
1058 | \r | |
1059 | /* Attach routine, test formating */\r | |
1060 | \r | |
1061 | t_stat dsk_attach (UNIT *uptr, char *cptr)\r | |
1062 | {\r | |
1063 | uint32 dtyp = GET_DTYPE (uptr->flags);\r | |
1064 | t_stat r;\r | |
1065 | \r | |
1066 | uptr->capac = dsk_tab[dtyp].size;\r | |
1067 | r = attach_unit (uptr, cptr);\r | |
1068 | if (r != SCPE_OK) return r;\r | |
1069 | uptr->TRK = 0;\r | |
1070 | uptr->SKF = 0;\r | |
1071 | uptr->flags &= ~(UNIT_INOP0|UNIT_INOP1);\r | |
1072 | return dsk_show_format (stdout, uptr, 0, NULL);\r | |
1073 | }\r | |
1074 | \r | |
1075 | /* Set disk size */\r | |
1076 | \r | |
1077 | t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
1078 | {\r | |
1079 | uint32 dtyp = GET_DTYPE (val);\r | |
1080 | uint32 u = uptr - dsk_dev.units;\r | |
1081 | UNIT *u1;\r | |
1082 | \r | |
1083 | if (u & 1) return SCPE_ARG;\r | |
1084 | u1 = dsk_dev.units + u + 1;\r | |
1085 | if ((uptr->flags & UNIT_ATT) || (u1->flags & UNIT_ATT))\r | |
1086 | return SCPE_ALATT;\r | |
1087 | if (val == TYPE_7320) u1->flags = (u1->flags & ~UNIT_DISABLE) | UNIT_DIS;\r | |
1088 | else {\r | |
1089 | u1->flags = (u1->flags & ~UNIT_TYPE) | val | UNIT_DISABLE;\r | |
1090 | u1->capac = dsk_tab[dtyp].size;\r | |
1091 | }\r | |
1092 | uptr->capac = dsk_tab[dtyp].size;\r | |
1093 | return SCPE_OK;\r | |
1094 | }\r | |
1095 | \r | |
1096 | /* Show format */\r | |
1097 | \r | |
1098 | t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
1099 | {\r | |
1100 | uint32 a, t, k, u, tlim, dtyp, da;\r | |
1101 | uint32 rptr, rlnt, rlim, rec, ctptr, *format;\r | |
1102 | uint32 minrsz = DSK_BUFSIZ;\r | |
1103 | uint32 maxrsz = 0;\r | |
1104 | uint32 minrno = DSK_BUFSIZ;\r | |
1105 | uint32 maxrno = 0;\r | |
1106 | t_bool ctss;\r | |
1107 | t_uint64 dbuf[DSK_BUFSIZ];\r | |
1108 | DEVICE *dptr;\r | |
1109 | \r | |
1110 | if (uptr == NULL) return SCPE_IERR;\r | |
1111 | if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;\r | |
1112 | dptr = find_dev_from_unit (uptr);\r | |
1113 | u = uptr - dptr->units;\r | |
1114 | if (dptr == NULL) return SCPE_IERR;\r | |
1115 | \r | |
1116 | dtyp = GET_DTYPE (uptr->flags);\r | |
1117 | if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301))\r | |
1118 | format = ctss_fmt_7320;\r | |
1119 | else format = ctss_fmt_1302;\r | |
1120 | for (a = 0, ctss = TRUE; a < dsk_tab[dtyp].accpm; a++) {\r | |
1121 | if (val) tlim = dsk_tab[dtyp].trkpa;\r | |
1122 | else tlim = 1;\r | |
1123 | for (t = 0; t < tlim; t++) {\r | |
1124 | da = DSK_DA (a, t, dtyp); /* get disk address */\r | |
1125 | sim_fseek (uptr->fileref, da, SEEK_SET); /* read track */\r | |
1126 | k = sim_fread (dbuf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, uptr->fileref);\r | |
1127 | if (ferror (uptr->fileref)) return SCPE_IOERR; /* error? */\r | |
1128 | for ( ; k < dsk_tab[dtyp].wdspt; k++) dbuf[k] = 0;\r | |
1129 | rptr = T1STREC;\r | |
1130 | rlnt = (uint32) dbuf[rptr + RLNT];\r | |
1131 | if (dbuf[THA2] != CTSS_HA2) ctss = FALSE;\r | |
1132 | if (rlnt == 0) {\r | |
1133 | if (a || t) fprintf (st,\r | |
1134 | "Unformatted track, unit = %d, access = %d, track = %d\n", u, a, t);\r | |
1135 | else fprintf (st, "Unit %d is unformatted\n", u);\r | |
1136 | return SCPE_OK;\r | |
1137 | }\r | |
1138 | for (rec = 0, ctptr = 0; rlnt != 0; rec++) {\r | |
1139 | if ((format[ctptr] == 0) || format[ctptr++] != rlnt)\r | |
1140 | ctss = FALSE;\r | |
1141 | rlim = rptr + rlnt + RDATA;\r | |
1142 | if (rlim >= dsk_tab[dtyp].wdspt) {\r | |
1143 | fprintf (st, "Invalid record length %d, unit = %d, access = %d, track = %d, record = %d\n",\r | |
1144 | rlnt, u, a, t, rec);\r | |
1145 | return SCPE_OK;\r | |
1146 | }\r | |
1147 | if (rlnt > maxrsz) maxrsz = rlnt;\r | |
1148 | if (rlnt < minrsz) minrsz = rlnt;\r | |
1149 | rptr = rlim;\r | |
1150 | rlnt = (uint32) dbuf[rptr + RLNT];\r | |
1151 | }\r | |
1152 | if (format[ctptr] != 0) ctss = FALSE;\r | |
1153 | if (rec > maxrno) maxrno = rec;\r | |
1154 | if (rec < minrno) minrno = rec;\r | |
1155 | }\r | |
1156 | }\r | |
1157 | if (val == 0) return SCPE_OK;\r | |
1158 | if (ctss) fprintf (st, "CTSS format\n");\r | |
1159 | else if ((minrno == maxrno) && (minrsz == maxrsz)) fprintf (st, \r | |
1160 | "Valid fixed format, records/track = %d, record size = %d\n",\r | |
1161 | minrno, minrsz);\r | |
1162 | else if (minrsz == maxrsz) fprintf (st,\r | |
1163 | "Valid variable format, records/track = %d-%d, record size = %d\n",\r | |
1164 | minrno, maxrno, minrsz);\r | |
1165 | else if (minrno == maxrno) fprintf (st,\r | |
1166 | "Valid variable format, records/track = %d, record sizes = %d-%d\n",\r | |
1167 | minrno, minrsz, maxrsz);\r | |
1168 | else fprintf (st,\r | |
1169 | "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n",\r | |
1170 | minrno, maxrno, minrsz, maxrsz);\r | |
1171 | return SCPE_OK;\r | |
1172 | }\r |