First Commit of my working state
[simh.git] / I7094 / i7094_dsk.c
CommitLineData
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
224typedef 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
234const 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
247uint8 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
253uint8 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
262uint8 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
268uint8 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
277uint32 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
283uint32 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
288uint32 dsk_ch = CH_C; /* disk channel */\r
289uint32 dsk_acc = 0; /* access */\r
290uint32 dsk_mod = 0; /* module */\r
291uint32 dsk_sta = 0; /* disk state */\r
292uint32 dsk_mode = 0; /* I/O mode */\r
293uint32 dsk_wchk = 0; /* write check flag */\r
294uint32 dsk_ctime = 10; /* command time */\r
295uint32 dsk_stime = 1000; /* seek time */\r
296uint32 dsk_rtime = 100; /* rotational latency */\r
297uint32 dsk_wtime = 2; /* word time */\r
298uint32 dsk_gtime = 5; /* gap time */\r
299uint32 dsk_rbase = 0; /* record tracking */\r
300uint32 dsk_rptr = 0;\r
301uint32 dsk_rlim = 0;\r
302uint32 dsk_stop = 0;\r
303uint32 dsk_fmt_cntr = 0; /* format counter */\r
304t_uint64 dsk_rec = 0; /* rec/home addr (36b) */\r
305t_uint64 dsk_sns = 0; /* sense data (60b) */\r
306t_uint64 dsk_cmd = 0; /* BCD command (60b) */\r
307t_uint64 dsk_chob = 0; /* chan output buffer */\r
308uint32 dsk_chob_v = 0; /* valid */\r
309t_uint64 dsk_buf[DSK_BUFSIZ]; /* transfer buffer */\r
310\r
311extern uint32 ch_req;\r
312\r
313t_stat dsk_svc (UNIT *uptr);\r
314t_stat dsk_svc_sns (UNIT *uptr);\r
315t_stat dsk_reset (DEVICE *dptr);\r
316t_stat dsk_attach (UNIT *uptr, char *cptr);\r
317t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r
318t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit);\r
319t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 flags);\r
320t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd);\r
321t_stat dsk_uend (uint32 ch, t_uint64 stat);\r
322t_stat dsk_recad (uint32 trk, uint32 rec, uint32 acc, uint32 mod, t_uint64 *res);\r
323t_uint64 dsk_acc_atn (uint32 u);\r
324t_stat dsk_init_trk (UNIT *udptr, uint32 trk);\r
325t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp);\r
326t_stat dsk_wr_trk (UNIT *uptr, uint32 trk);\r
327t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc);\r
328t_bool dsk_qdone (uint32 ch);\r
329t_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
338DIB dsk_dib = { &dsk_chsel, &dsk_chwr };\r
339\r
340UNIT 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
374REG 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
405MTAB 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
422DEVICE 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
432t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit)\r
433{\r
434uint32 u;\r
435\r
436dsk_ch = ch;\r
437if (dsk_sta != DSK_IDLE) dsk_uend (ch, DSKS_INVS); /* not idle? seq check */\r
438\r
439switch (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
468dsk_sta = sel; /* set new state */\r
469return SCPE_OK;\r
470}\r
471\r
472/* Disk channel write, from 7909 channel program */\r
473\r
474t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 stopf)\r
475{\r
476if (stopf) dsk_stop = 1; /* stop? */\r
477\r
478else {\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
501return SCPE_OK;\r
502}\r
503\r
504/* New command - end of CTL sequence */\r
505\r
506t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd)\r
507{\r
508uint32 i, d, a, m, u, trk, dtyp, bcd[8];\r
509\r
510ch_req |= REQ_CH (ch); /* req ch for end */\r
511ch9_set_end (ch, 0); /* set end flag */\r
512dsk_sta = DSK_IDLE; /* ctrl is idle */\r
513\r
514for (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
521if (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
543else 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
615return dsk_uend (ch, DSKS_INVC); /* invalid opcode */\r
616}\r
617\r
618/* Sense unit service */\r
619\r
620t_stat dsk_svc_sns (UNIT *uptr)\r
621{\r
622t_uint64 dat;\r
623\r
624switch (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
652sim_activate (uptr, dsk_wtime); /* sched next */\r
653return SCPE_OK;\r
654}\r
655\r
656/* Seek, read, write unit service */\r
657\r
658t_stat dsk_svc (UNIT *uaptr)\r
659{\r
660uint32 i, dtyp, trk;\r
661uint8 fc, *format;\r
662t_uint64 rdat;\r
663UNIT *udptr;\r
664t_stat r;\r
665\r
666if (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
674udptr = dsk_dev.units + dsk_mod; /* data unit */\r
675if (udptr->flags & (UNIT_INOP0 << dsk_acc)) /* acc inoperative? */\r
676 return dsk_uend (dsk_ch, DSKS_ACCI); /* error */\r
677if ((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
682dtyp = GET_DTYPE (udptr->flags); /* get data drive type */\r
683trk = uaptr->TRK; /* get access track */\r
684\r
685switch (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
838sim_activate (uaptr, dsk_wtime);\r
839return 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
856t_stat dsk_init_trk (UNIT *udptr, uint32 trk)\r
857{\r
858uint32 k, da, dtyp, rlnt;\r
859\r
860dtyp = GET_DTYPE (udptr->flags); /* get drive type */\r
861da = DSK_DA (dsk_acc, trk, dtyp); /* get disk address */\r
862sim_fseek (udptr->fileref, da, SEEK_SET); /* read track */\r
863k = sim_fread (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref);\r
864if (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
870for ( ; k < dsk_tab[dtyp].wdspt; k++) dsk_buf[k] = 0; /* zero fill */\r
871dsk_rbase = T1STREC; /* record base */\r
872rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r
873dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */\r
874if ((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
878if (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
891while (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
903dsk_uend (dsk_ch, DSKS_NRCF); /* not found */\r
904return 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
918t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp)\r
919{\r
920uint32 rlnt;\r
921\r
922if (dsk_rptr < dsk_rlim) return SCPE_OK; /* record done? */\r
923if (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
929dsk_rbase = dsk_rlim; /* next record */\r
930rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r
931dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */\r
932if ((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
937if (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
943if (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
948ch9_set_end (dsk_ch, 0); /* set end */\r
949ch_req |= REQ_CH (dsk_ch); /* request channel */\r
950return ERR_ENDRC;\r
951}\r
952\r
953/* Write track back to file */\r
954\r
955t_stat dsk_wr_trk (UNIT *udptr, uint32 trk)\r
956{\r
957uint32 dtyp = GET_DTYPE (udptr->flags);\r
958uint32 da = DSK_DA (dsk_acc, trk, dtyp);\r
959\r
960sim_fseek (udptr->fileref, da, SEEK_SET);\r
961sim_fwrite (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref);\r
962if (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
968return SCPE_OK;\r
969}\r
970\r
971/* Synthesize right attention bit from (access * 10 + module) */\r
972\r
973t_uint64 dsk_acc_atn (uint32 u)\r
974{\r
975uint32 g, b;\r
976\r
977g = u / 4; /* bit group */\r
978b = u % 4; /* bit within group */\r
979return (DSKS_ATN0 >> ((g * 6) + (b? b + 1: 0)));\r
980}\r
981\r
982/* Get next format character */\r
983\r
984t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc)\r
985{\r
986uint32 cc = dsk_fmt_cntr % 6;\r
987\r
988if (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
993if ((cc == 5) && !dsk_stop) ch_req |= REQ_CH (dsk_ch); /* end of word? */\r
994if (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
998return TRUE;\r
999}\r
1000\r
1001/* Unusual end (set status and stop) */\r
1002\r
1003t_stat dsk_uend (uint32 ch, t_uint64 stat)\r
1004{\r
1005dsk_sns |= stat;\r
1006dsk_sns &= ~(DSKS_PCHK|DSKS_DCHK|DSKS_EXCC);\r
1007if (dsk_sns & DSKS_PALL) dsk_sns |= DSKS_PCHK;\r
1008if (dsk_sns & DSKS_DALL) dsk_sns |= DSKS_DCHK;\r
1009if (dsk_sns & DSKS_EALL) dsk_sns |= DSKS_EXCC;\r
1010ch9_set_end (ch, CHINT_UEND);\r
1011ch_req |= REQ_CH (ch);\r
1012dsk_sta = DSK_IDLE;\r
1013return SCPE_OK;\r
1014}\r
1015\r
1016/* Test for done */\r
1017\r
1018t_bool dsk_qdone (uint32 ch)\r
1019{\r
1020if (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
1024return FALSE;\r
1025}\r
1026\r
1027/* Reset */\r
1028\r
1029t_stat dsk_reset (DEVICE *dptr)\r
1030{\r
1031uint32 i;\r
1032UNIT *uptr;\r
1033\r
1034dsk_acc = 0;\r
1035dsk_mod = 0;\r
1036dsk_rec = 0;\r
1037dsk_mode = 0;\r
1038dsk_wchk = 0;\r
1039dsk_sns = 0;\r
1040dsk_cmd = 0;\r
1041dsk_sta = DSK_IDLE;\r
1042dsk_rbase = 0;\r
1043dsk_rptr = 0;\r
1044dsk_rlim = 0;\r
1045dsk_stop = 0;\r
1046dsk_fmt_cntr = 0;\r
1047dsk_chob = 0;\r
1048dsk_chob_v = 0;\r
1049for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0;\r
1050for (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
1056return SCPE_OK;\r
1057}\r
1058\r
1059/* Attach routine, test formating */\r
1060\r
1061t_stat dsk_attach (UNIT *uptr, char *cptr)\r
1062{\r
1063uint32 dtyp = GET_DTYPE (uptr->flags);\r
1064t_stat r;\r
1065\r
1066uptr->capac = dsk_tab[dtyp].size;\r
1067r = attach_unit (uptr, cptr);\r
1068if (r != SCPE_OK) return r;\r
1069uptr->TRK = 0;\r
1070uptr->SKF = 0;\r
1071uptr->flags &= ~(UNIT_INOP0|UNIT_INOP1);\r
1072return dsk_show_format (stdout, uptr, 0, NULL);\r
1073}\r
1074\r
1075/* Set disk size */\r
1076\r
1077t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r
1078{\r
1079uint32 dtyp = GET_DTYPE (val);\r
1080uint32 u = uptr - dsk_dev.units;\r
1081UNIT *u1;\r
1082\r
1083if (u & 1) return SCPE_ARG;\r
1084u1 = dsk_dev.units + u + 1;\r
1085if ((uptr->flags & UNIT_ATT) || (u1->flags & UNIT_ATT))\r
1086 return SCPE_ALATT;\r
1087if (val == TYPE_7320) u1->flags = (u1->flags & ~UNIT_DISABLE) | UNIT_DIS;\r
1088else {\r
1089 u1->flags = (u1->flags & ~UNIT_TYPE) | val | UNIT_DISABLE;\r
1090 u1->capac = dsk_tab[dtyp].size;\r
1091 }\r
1092uptr->capac = dsk_tab[dtyp].size;\r
1093return SCPE_OK;\r
1094}\r
1095\r
1096/* Show format */\r
1097\r
1098t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc)\r
1099{\r
1100uint32 a, t, k, u, tlim, dtyp, da;\r
1101uint32 rptr, rlnt, rlim, rec, ctptr, *format;\r
1102uint32 minrsz = DSK_BUFSIZ;\r
1103uint32 maxrsz = 0;\r
1104uint32 minrno = DSK_BUFSIZ;\r
1105uint32 maxrno = 0;\r
1106t_bool ctss;\r
1107t_uint64 dbuf[DSK_BUFSIZ];\r
1108DEVICE *dptr;\r
1109\r
1110if (uptr == NULL) return SCPE_IERR;\r
1111if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;\r
1112dptr = find_dev_from_unit (uptr);\r
1113u = uptr - dptr->units;\r
1114if (dptr == NULL) return SCPE_IERR;\r
1115\r
1116dtyp = GET_DTYPE (uptr->flags);\r
1117if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301))\r
1118 format = ctss_fmt_7320;\r
1119else format = ctss_fmt_1302;\r
1120for (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
1157if (val == 0) return SCPE_OK;\r
1158if (ctss) fprintf (st, "CTSS format\n");\r
1159else if ((minrno == maxrno) && (minrsz == maxrsz)) fprintf (st, \r
1160 "Valid fixed format, records/track = %d, record size = %d\n",\r
1161 minrno, minrsz);\r
1162else if (minrsz == maxrsz) fprintf (st,\r
1163 "Valid variable format, records/track = %d-%d, record size = %d\n",\r
1164 minrno, maxrno, minrsz);\r
1165else if (minrno == maxrno) fprintf (st,\r
1166 "Valid variable format, records/track = %d, record sizes = %d-%d\n",\r
1167 minrno, minrsz, maxrsz);\r
1168else fprintf (st,\r
1169 "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n",\r
1170 minrno, maxrno, minrsz, maxrsz);\r
1171return SCPE_OK;\r
1172}\r