Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* hp2100_ds.c: HP 2100 13037 disk controller simulator\r |
2 | \r | |
3 | Copyright (c) 2004-2007, 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 | ds 13037 disk controller\r | |
27 | \r | |
28 | 31-Dec-07 JDB Corrected and verified ioCRS action\r | |
29 | 20-Dec-07 JDB Corrected DPTR register definition from FLDATA to DRDATA\r | |
30 | 28-Dec-06 JDB Added ioCRS state to I/O decoders\r | |
31 | 03-Aug-06 JDB Fixed REQUEST STATUS command to clear status-1\r | |
32 | Removed redundant attached test in "ds_detach"\r | |
33 | 18-Mar-05 RMS Added attached test to detach routine\r | |
34 | 01-Mar-05 JDB Added SET UNLOAD/LOAD\r | |
35 | \r | |
36 | States of the controller: the controller uP runs all the time, but most of\r | |
37 | the time it is waiting for an event. The simulator only 'runs' the controller\r | |
38 | when there's an event to process: change in CPU interface state, change in\r | |
39 | disk state, or timeout. The controller has three states:\r | |
40 | \r | |
41 | - Idle. No operations other than seek or recalibrate are in progress, and\r | |
42 | the CPU interface is disconnected. The controller responds both to new\r | |
43 | commands and to drive attention interrupts.\r | |
44 | - Wait. No operations other than seek or recalibrate are in progress, but\r | |
45 | the CPU interface is connected. The controller responds to new commands\r | |
46 | but not to drive attention interrupts.\r | |
47 | - Busy. The controller is processing a command. The controller does not\r | |
48 | respond to new commands or to drive attention interrupts.\r | |
49 | \r | |
50 | The controller busy state is loosely related to the testable (visible) busy\r | |
51 | flop. If the visible busy flop is set, the controller is in the busy state;\r | |
52 | but the controller can also be busy (processing an invalid opcode or invalid\r | |
53 | unit) while visible busy is clear.\r | |
54 | \r | |
55 | Omissions: the following features are not implemented:\r | |
56 | \r | |
57 | - Drive hold. Since this is a single CPU implementation, the drives are\r | |
58 | always available to the CPU.\r | |
59 | - Spare, defective, protected. The disk files carry only data.\r | |
60 | - Formatting. The disk files carry only data.\r | |
61 | - ECC. Data errors are always uncorrectable.\r | |
62 | \r | |
63 | Reference:\r | |
64 | - 13037 Disc Controller Technical Information Package (13037-90902, Aug-1980)\r | |
65 | */\r | |
66 | \r | |
67 | #include "hp2100_defs.h"\r | |
68 | #include <math.h>\r | |
69 | \r | |
70 | #define DS_NUMDR 8 /* max drives */\r | |
71 | #define DS_DRMASK (DS_NUMDR - 1)\r | |
72 | #define DS_NUMWD 128 /* data words/sec */\r | |
73 | #define DS_NUMWDF 138 /* total words/sec */\r | |
74 | #define DS_FSYNC 0 /* sector offsets */\r | |
75 | #define DS_FCYL 1\r | |
76 | #define DS_FHS 2\r | |
77 | #define DS_FDATA 3\r | |
78 | #define DS_FIFO_SIZE 16 /* fifo size */\r | |
79 | #define DS_FIFO_EMPTY (ds_fifo_cnt == 0)\r | |
80 | #define ds_ctrl ds_unit[DS_NUMDR] /* ctrl thread */\r | |
81 | #define ds_timer ds_unit[DS_NUMDR + 1] /* timeout thread */\r | |
82 | #define GET_CURSEC(x,d) ((int32) fmod (sim_gtime() / ((double) (x)), \\r | |
83 | ((double) (drv_tab[d].sc))))\r | |
84 | \r | |
85 | /* Flags in the unit flags word */\r | |
86 | \r | |
87 | #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */\r | |
88 | #define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */\r | |
89 | #define UNIT_V_DTYPE (UNIT_V_UF + 2) /* disk type */\r | |
90 | #define UNIT_M_DTYPE 3\r | |
91 | #define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */\r | |
92 | #define UNIT_V_FMT (UNIT_V_UF + 5) /* format enabled */\r | |
93 | #define UNIT_WLK (1 << UNIT_V_WLK)\r | |
94 | #define UNIT_FMT (1 << UNIT_V_FMT)\r | |
95 | #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)\r | |
96 | #define UNIT_AUTO (1 << UNIT_V_AUTO)\r | |
97 | #define UNIT_UNLOAD (1 << UNIT_V_UNLOAD)\r | |
98 | #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)\r | |
99 | #define UNIT_WPR (UNIT_WLK | UNIT_RO) /* write prot */\r | |
100 | \r | |
101 | /* Parameters in the unit descriptor */\r | |
102 | \r | |
103 | #define FNC u3 /* function */\r | |
104 | #define CYL u4 /* current cylinder */\r | |
105 | #define STA u5 /* status */\r | |
106 | \r | |
107 | /* Arguments to subroutines */\r | |
108 | \r | |
109 | #define CLR_BUSY 0 /* clear visible busy */\r | |
110 | #define SET_BUSY 1 /* set visible busy */\r | |
111 | \r | |
112 | /* Command word - <12:8> are opcode, <7:0> are opcode dependent\r | |
113 | \r | |
114 | cold load read <7:6> = head\r | |
115 | <5:0> = sector\r | |
116 | set file mask <7:4> = retry count\r | |
117 | <3:0> = file mask (auto-seek options)\r | |
118 | commands with units <7> = hold flag\r | |
119 | <4:0> = unit number */\r | |
120 | \r | |
121 | #define DSC_V_OP 8 /* opcode */\r | |
122 | #define DSC_M_OP 037\r | |
123 | #define DSC_COLD 000 /* cold load read */\r | |
124 | #define DSC_RECAL 001 /* recalibrate */\r | |
125 | #define DSC_SEEK 002 /* seek */\r | |
126 | #define DSC_RSTA 003 /* request status */\r | |
127 | #define DSC_RSA 004 /* request sector addr */\r | |
128 | #define DSC_READ 005 /* read */\r | |
129 | #define DSC_RFULL 006 /* read full */\r | |
130 | #define DSC_VFY 007 /* verify */\r | |
131 | #define DSC_WRITE 010 /* write */\r | |
132 | #define DSC_WFULL 011 /* write full */\r | |
133 | #define DSC_CLEAR 012 /* clear */\r | |
134 | #define DSC_INIT 013 /* initialize */\r | |
135 | #define DSC_AREC 014 /* address record */\r | |
136 | #define DSC_RSYN 015 /* request syndrome */\r | |
137 | #define DSC_ROFF 016 /* read with offset */\r | |
138 | #define DSC_SFM 017 /* set file mask */\r | |
139 | #define DSC_RNOVFY 022 /* read no verify */\r | |
140 | #define DSC_WTIO 023 /* write TIO */\r | |
141 | #define DSC_RDA 024 /* request disk addr */\r | |
142 | #define DSC_END 025 /* end */\r | |
143 | #define DSC_WAKE 026 /* wakeup */\r | |
144 | #define DSC_ATN 035 /* pseudo: ATN */\r | |
145 | #define DSC_BADU 036 /* pseudo: bad unit */\r | |
146 | #define DSC_BADF 037 /* pseudo: bad opcode */\r | |
147 | #define DSC_NEXT 0040 /* state increment */\r | |
148 | #define DSC_2ND 0040 /* subcommand states */\r | |
149 | #define DSC_3RD 0100\r | |
150 | #define DSC_4TH 0140\r | |
151 | #define DSC_V_CHD 6 /* cold load head */\r | |
152 | #define DSC_M_CHD 03\r | |
153 | #define DSC_V_CSC 0 /* cold load sector */\r | |
154 | #define DSC_M_CSC 077\r | |
155 | #define DSC_V_RTY 4 /* retry count */\r | |
156 | #define DSC_M_RTY 017\r | |
157 | #define DSC_V_DECR 3 /* seek decrement */\r | |
158 | #define DSC_V_SPEN 2 /* enable sparing */\r | |
159 | #define DSC_V_CYLM 1 /* cylinder mode */\r | |
160 | #define DSC_V_AUTO 0 /* auto seek */\r | |
161 | #define DSC_V_HOLD 7 /* hold flag */\r | |
162 | #define DSC_V_UNIT 0 /* unit */\r | |
163 | #define DSC_M_UNIT 017\r | |
164 | #define DSC_V_SPAR 15 /* INIT spare */\r | |
165 | #define DSC_V_PROT 14 /* INIT protected */\r | |
166 | #define DSC_V_DFCT 13 /* INIT defective */\r | |
167 | \r | |
168 | #define DSC_HOLD (1u << DSC_V_HOLD)\r | |
169 | #define DSC_DECR (1u << DSC_V_DECR)\r | |
170 | #define DSC_SPEN (1u << DSC_V_SPEN)\r | |
171 | #define DSC_CYLM (1u << DSC_V_CYLM)\r | |
172 | #define DSC_AUTO (1u << DSC_V_AUTO)\r | |
173 | #define DSC_FMASK ((DSC_M_RTY << DSC_V_RTY)|DSC_DECR|\\r | |
174 | DSC_SPEN|DSC_CYLM|DSC_AUTO)\r | |
175 | #define DSC_GETOP(x) (((x) >> DSC_V_OP) & DSC_M_OP)\r | |
176 | #define DSC_GETUNIT(x) (((x) >> DSC_V_UNIT) & DSC_M_UNIT)\r | |
177 | #define DSC_GETCHD(x) (((x) >> DSC_V_CHD) & DSC_M_CHD)\r | |
178 | #define DSC_GETCSC(x) (((x) >> DSC_V_CSC) & DSC_M_CSC)\r | |
179 | #define DSC_SPAR (1u << DSC_V_SPAR)\r | |
180 | #define DSC_PROT (1u << DSC_V_PROT)\r | |
181 | #define DSC_DFCT (1u << DSC_V_DFCT)\r | |
182 | \r | |
183 | /* Command flags */\r | |
184 | \r | |
185 | #define CMF_UNDF 001 /* undefined */\r | |
186 | #define CMF_CLREC 002 /* clear eoc flag */\r | |
187 | #define CMF_CLRS 004 /* clear status */\r | |
188 | #define CMF_UIDLE 010 /* requires unit no */\r | |
189 | \r | |
190 | /* Cylinder words - 16b */\r | |
191 | \r | |
192 | /* Head/sector word */\r | |
193 | \r | |
194 | #define DSHS_V_HD 8 /* head */\r | |
195 | #define DSHS_M_HD 037\r | |
196 | #define DSHS_V_SC 0 /* sector */\r | |
197 | #define DSHS_M_SC 0377\r | |
198 | #define DSHS_HD (DSHS_M_HD << DSHS_V_HD)\r | |
199 | #define DSHS_SC (DSHS_M_SC << DSHS_V_SC)\r | |
200 | #define DSHS_GETHD(x) (((x) >> DSHS_V_HD) & DSHS_M_HD)\r | |
201 | #define DSHS_GETSC(x) (((x) >> DSHS_V_SC) & DSHS_M_SC)\r | |
202 | \r | |
203 | /* Status 1 */\r | |
204 | \r | |
205 | #define DS1_V_SPAR 15 /* spare - na */\r | |
206 | #define DS1_V_PROT 14 /* protected - na */\r | |
207 | #define DS1_V_DFCT 13 /* defective - na */\r | |
208 | #define DS1_V_STAT 8 /* status */\r | |
209 | #define DS1_OK (000 << DS1_V_STAT) /* normal */\r | |
210 | #define DS1_ILLOP (001 << DS1_V_STAT) /* illegal opcode */\r | |
211 | #define DS1_AVAIL (002 << DS1_V_STAT) /* available */\r | |
212 | #define DS1_CYLCE (007 << DS1_V_STAT) /* cyl compare err */\r | |
213 | #define DS1_UNCOR (010 << DS1_V_STAT) /* uncor data err */\r | |
214 | #define DS1_HSCE (011 << DS1_V_STAT) /* h/s compare err */\r | |
215 | #define DS1_IOPE (012 << DS1_V_STAT) /* IO oper err - na */\r | |
216 | #define DS1_EOCYL (014 << DS1_V_STAT) /* end cylinder */\r | |
217 | #define DS1_OVRUN (016 << DS1_V_STAT) /* overrun */\r | |
218 | #define DS1_CORDE (017 << DS1_V_STAT) /* correctible - na */\r | |
219 | #define DS1_ILLST (020 << DS1_V_STAT) /* illegal spare - na */\r | |
220 | #define DS1_DEFTK (021 << DS1_V_STAT) /* defective trk - na */\r | |
221 | #define DS1_ACCER (022 << DS1_V_STAT) /* access not rdy - na */\r | |
222 | #define DS1_S2ERR (023 << DS1_V_STAT) /* status 2 error */\r | |
223 | #define DS1_TKPER (026 << DS1_V_STAT) /* protected trk - na */\r | |
224 | #define DS1_UNAVL (027 << DS1_V_STAT) /* illegal unit */\r | |
225 | #define DS1_ATN (037 << DS1_V_STAT) /* attention */\r | |
226 | #define DS1_V_UNIT 0\r | |
227 | #define DS1_SPAR (1u << DS1_V_SPAR)\r | |
228 | #define DS1_PROT (1u << DS1_V_PROT)\r | |
229 | #define DS1_DFCT (1u << DS1_V_DFCT)\r | |
230 | \r | |
231 | /* Status 2, ^ = kept in unit status, * = dynamic */\r | |
232 | \r | |
233 | #define DS2_ERR 0100000 /* *error */\r | |
234 | #define DS2_V_ID 9 /* drive type */\r | |
235 | #define DS2_ATN 0000200 /* ^attention */\r | |
236 | #define DS2_RO 0000100 /* *read only */\r | |
237 | #define DS2_FRM 0000040 /* *format */\r | |
238 | #define DS2_FLT 0000020 /* fault - na */\r | |
239 | #define DS2_FS 0000010 /* ^first status */\r | |
240 | #define DS2_SC 0000004 /* ^seek error */\r | |
241 | #define DS2_NR 0000002 /* *not ready */\r | |
242 | #define DS2_BS 0000001 /* *busy */\r | |
243 | #define DS2_ALLERR (DS2_FLT|DS2_SC|DS2_NR|DS2_BS)\r | |
244 | \r | |
245 | /* Controller state */\r | |
246 | \r | |
247 | #define DS_IDLE 0 /* idle */\r | |
248 | #define DS_WAIT 1 /* command wait */\r | |
249 | #define DS_BUSY 2 /* busy */\r | |
250 | \r | |
251 | /* This controller supports four different disk drive types:\r | |
252 | \r | |
253 | type #sectors/ #surfaces/ #cylinders/\r | |
254 | surface cylinder drive\r | |
255 | \r | |
256 | 7905 48 3 411 =15MB\r | |
257 | 7906 48 4 411 =20MB\r | |
258 | 7920 48 5 823 =50MB\r | |
259 | 7925 64 9 823 =120MB\r | |
260 | \r | |
261 | In theory, each drive can be a different type. The size field in\r | |
262 | each unit selects the drive capacity for each drive and thus the\r | |
263 | drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE.\r | |
264 | \r | |
265 | The 7905 and 7906 have fixed and removable platters. Consequently,\r | |
266 | they are almost always accessed with cylinders limited to each\r | |
267 | platter. The 7920 and 7925 have multiple-platter packs, and so are\r | |
268 | almost always accessed with cylinders that span all surfaces.\r | |
269 | \r | |
270 | Disk image files are arranged as a linear set of tracks. To improve\r | |
271 | locality, tracks on the 7905 and 7906 images are grouped per-platter,\r | |
272 | i.e., all tracks on heads 0 and 1, followed by all tracks on head 2\r | |
273 | (and, for the 7906, head 3), whereas tracks on the 7920 and 7925 are\r | |
274 | sequential by cylinder and head number.\r | |
275 | \r | |
276 | This variable-access geometry is accomplished by defining a "heads\r | |
277 | per cylinder" value for the fixed and removable sections of each\r | |
278 | drive that indicates the number of heads that should be grouped for\r | |
279 | locality. The removable values are set to 2 on the 7905 and 7906,\r | |
280 | indicating that those drives typically use cylinders of two surfaces.\r | |
281 | They are set to the number of surfaces per drive for the 7920 and\r | |
282 | 7925, as those typically use cylinders encompassing the entire\r | |
283 | spindle.\r | |
284 | */\r | |
285 | \r | |
286 | #define GET_DA(x,y,z,t) \\r | |
287 | (((((y) < drv_tab[t].rh)? \\r | |
288 | (x) * drv_tab[t].rh + (y): \\r | |
289 | drv_tab[t].cyl * drv_tab[t].rh + \\r | |
290 | ((x) * drv_tab[t].fh + (y) - drv_tab[t].rh)) * \\r | |
291 | drv_tab[t].sc + (z)) * DS_NUMWD)\r | |
292 | \r | |
293 | #define D7905_DTYPE 0\r | |
294 | #define D7905_SECT 48\r | |
295 | #define D7905_SURF 3\r | |
296 | #define D7905_RH 2\r | |
297 | #define D7905_FH (D7905_SURF - D7905_RH)\r | |
298 | #define D7905_CYL 411\r | |
299 | #define D7905_ID (2 << DS2_V_ID)\r | |
300 | #define D7905_SIZE (D7905_SECT * D7905_SURF * D7905_CYL * DS_NUMWD)\r | |
301 | \r | |
302 | #define D7906_DTYPE 1\r | |
303 | #define D7906_SECT 48\r | |
304 | #define D7906_SURF 4\r | |
305 | #define D7906_RH 2\r | |
306 | #define D7906_FH (D7906_SURF - D7906_RH)\r | |
307 | #define D7906_CYL 411\r | |
308 | #define D7906_ID (0 << DS2_V_ID)\r | |
309 | #define D7906_SIZE (D7906_SECT * D7906_SURF * D7906_CYL * DS_NUMWD)\r | |
310 | \r | |
311 | #define D7920_DTYPE 2\r | |
312 | #define D7920_SECT 48\r | |
313 | #define D7920_SURF 5\r | |
314 | #define D7920_RH D7920_SURF\r | |
315 | #define D7920_FH (D7920_SURF - D7920_RH)\r | |
316 | #define D7920_CYL 823\r | |
317 | #define D7920_ID (1 << DS2_V_ID)\r | |
318 | #define D7920_SIZE (D7920_SECT * D7920_SURF * D7920_CYL * DS_NUMWD)\r | |
319 | \r | |
320 | #define D7925_DTYPE 3\r | |
321 | #define D7925_SECT 64\r | |
322 | #define D7925_SURF 9\r | |
323 | #define D7925_RH D7925_SURF\r | |
324 | #define D7925_FH (D7925_SURF - D7925_RH)\r | |
325 | #define D7925_CYL 823\r | |
326 | #define D7925_ID (3 << DS2_V_ID)\r | |
327 | #define D7925_SIZE (D7925_SECT * D7925_SURF * D7925_CYL * DS_NUMWD)\r | |
328 | \r | |
329 | struct drvtyp {\r | |
330 | uint32 sc; /* sectors */\r | |
331 | uint32 hd; /* surfaces */\r | |
332 | uint32 cyl; /* cylinders */\r | |
333 | uint32 size; /* #blocks */\r | |
334 | uint32 id; /* device type */\r | |
335 | uint32 rh; /* removable surfaces */\r | |
336 | uint32 fh; /* fixed surfaces */\r | |
337 | };\r | |
338 | \r | |
339 | static struct drvtyp drv_tab[] = {\r | |
340 | { D7905_SECT, D7905_SURF, D7905_CYL, D7905_SIZE, D7905_ID, D7905_RH, D7905_FH },\r | |
341 | { D7906_SECT, D7906_SURF, D7906_CYL, D7906_SIZE, D7906_ID, D7906_RH, D7906_FH },\r | |
342 | { D7920_SECT, D7920_SURF, D7920_CYL, D7920_SIZE, D7920_ID, D7920_RH, D7920_FH },\r | |
343 | { D7925_SECT, D7925_SURF, D7925_CYL, D7925_SIZE, D7925_ID, D7925_RH, D7925_FH },\r | |
344 | { 0 }\r | |
345 | };\r | |
346 | \r | |
347 | extern uint32 PC, SR;\r | |
348 | extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2];\r | |
349 | extern int32 sim_switches;\r | |
350 | \r | |
351 | uint32 ds_fifo[DS_FIFO_SIZE] = { 0 }; /* fifo */\r | |
352 | uint32 ds_fifo_ip = 0; /* insertion ptr */\r | |
353 | uint32 ds_fifo_rp = 0; /* removal ptr */\r | |
354 | uint32 ds_fifo_cnt = 0; /* count */\r | |
355 | uint32 ds_cmd = 0; /* command word */\r | |
356 | uint32 ds_sr1 = 0; /* status word 1 */\r | |
357 | uint32 ds_busy = 0; /* busy flag */\r | |
358 | uint32 ds_eoc = 0; /* end of cylinder */\r | |
359 | uint32 ds_eod = 0; /* end of data */\r | |
360 | uint32 ds_fmask = 0; /* file mask */\r | |
361 | uint32 ds_cmdf = 0; /* command follows */\r | |
362 | uint32 ds_cmdp = 0; /* command present */\r | |
363 | uint32 ds_cyl = 0; /* disk address: cyl */\r | |
364 | uint32 ds_hs = 0; /* disk address: hs */\r | |
365 | uint32 ds_vctr = 0; /* verify counter */\r | |
366 | uint32 ds_state = 0; /* controller state */\r | |
367 | uint32 ds_lastatn = 0; /* last atn intr */\r | |
368 | int32 ds_stime = 100; /* seek time */\r | |
369 | int32 ds_rtime = 100; /* inter-sector time */\r | |
370 | int32 ds_ctime = 3; /* command time */\r | |
371 | int32 ds_dtime = 1; /* dch time */\r | |
372 | int32 ds_tmo = 2749200; /* timeout = 1.74 sec */\r | |
373 | uint32 ds_ptr = 0; /* buffer ptr */\r | |
374 | uint16 dsxb[DS_NUMWDF]; /* sector buffer */\r | |
375 | \r | |
376 | static const uint32 ds_opflags[32] = { /* flags for ops */\r | |
377 | CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* cold read */\r | |
378 | CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* recalibrate */\r | |
379 | CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* seek */\r | |
380 | 0, /* read status */\r | |
381 | CMF_CLRS, /* read sector */\r | |
382 | CMF_CLRS|CMF_UIDLE, /* read */\r | |
383 | CMF_CLRS|CMF_UIDLE, /* read full */\r | |
384 | CMF_CLRS|CMF_UIDLE, /* verify */\r | |
385 | CMF_CLRS|CMF_UIDLE, /* write */\r | |
386 | CMF_CLRS|CMF_UIDLE, /* write full */\r | |
387 | CMF_CLRS, /* clear */\r | |
388 | CMF_CLRS|CMF_UIDLE, /* init */\r | |
389 | CMF_CLREC|CMF_CLRS, /* addr record */\r | |
390 | 0, /* read syndrome */\r | |
391 | CMF_CLRS|CMF_UIDLE, /* read offset */\r | |
392 | CMF_CLRS, /* set file mask */\r | |
393 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
394 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
395 | CMF_CLRS|CMF_UIDLE, /* read no verify */\r | |
396 | CMF_CLRS, /* write TIO */\r | |
397 | CMF_CLRS, /* read disk addr */\r | |
398 | CMF_CLRS, /* end */\r | |
399 | CMF_CLRS, /* wake */\r | |
400 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
401 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
402 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
403 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
404 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
405 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
406 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
407 | CMF_UNDF|CMF_CLRS, /* undefined */\r | |
408 | CMF_UNDF|CMF_CLRS /* undefined */\r | |
409 | };\r | |
410 | \r | |
411 | DEVICE ds_dev;\r | |
412 | int32 dsio (int32 inst, int32 IR, int32 dat);\r | |
413 | t_stat ds_svc_c (UNIT *uptr);\r | |
414 | t_stat ds_svc_u (UNIT *uptr);\r | |
415 | t_stat ds_svc_t (UNIT *uptr);\r | |
416 | t_stat ds_reset (DEVICE *dptr);\r | |
417 | t_stat ds_attach (UNIT *uptr, char *cptr);\r | |
418 | t_stat ds_detach (UNIT *uptr);\r | |
419 | t_stat ds_boot (int32 unitno, DEVICE *dptr);\r | |
420 | t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc);\r | |
421 | t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
422 | void ds_poll (void);\r | |
423 | void ds_docmd (uint32 cmd);\r | |
424 | void ds_doatn (void);\r | |
425 | uint32 ds_updds2 (UNIT *uptr);\r | |
426 | void ds_cmd_done (t_bool sf, uint32 sr1);\r | |
427 | void ds_wait_for_cpu (UNIT *uptr, uint32 newst);\r | |
428 | void ds_set_idle (void);\r | |
429 | void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy);\r | |
430 | void ds_reqad (uint16 *cyl, uint16 *hs);\r | |
431 | void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst);\r | |
432 | t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy);\r | |
433 | void ds_next_sec (UNIT *uptr);\r | |
434 | void ds_next_cyl (UNIT *uptr);\r | |
435 | t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy);\r | |
436 | void ds_start_wr (UNIT *uptr, t_bool vfy);\r | |
437 | void ds_cont_rd (UNIT *uptr, uint32 bsize);\r | |
438 | t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize);\r | |
439 | void ds_end_rw (UNIT *uptr, uint32 newst);\r | |
440 | t_stat ds_set_uncorr (UNIT *uptr);\r | |
441 | t_stat ds_reset_cmn (DEVICE *dptr);\r | |
442 | void ds_sched_atn (UNIT *uptr);\r | |
443 | uint32 ds_fifo_read (void);\r | |
444 | void ds_fifo_write (uint32 dat);\r | |
445 | void ds_fifo_reset (void);\r | |
446 | \r | |
447 | /* DS data structures\r | |
448 | \r | |
449 | ds_dev DS device descriptor\r | |
450 | ds_unit DS unit list\r | |
451 | ds_reg DS register list\r | |
452 | ds_mod DS modifier list\r | |
453 | */\r | |
454 | \r | |
455 | DIB ds_dib = { DS, 0, 0, 0, 0, 0, &dsio };\r | |
456 | \r | |
457 | UNIT ds_unit[] = {\r | |
458 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
459 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
460 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
461 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
462 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
463 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
464 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
465 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
466 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
467 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
468 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
469 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
470 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
471 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
472 | { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE |\r | |
473 | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) },\r | |
474 | { UDATA (&ds_svc_c, UNIT_DIS, 0) },\r | |
475 | { UDATA (&ds_svc_t, UNIT_DIS, 0) }\r | |
476 | };\r | |
477 | \r | |
478 | REG ds_reg[] = {\r | |
479 | { ORDATA (CMD, ds_cmd, 16) },\r | |
480 | { BRDATA (FIFO, ds_fifo, 8, 16, DS_FIFO_SIZE) },\r | |
481 | { ORDATA (SR1, ds_sr1, 16) },\r | |
482 | { ORDATA (VCTR, ds_vctr, 16) },\r | |
483 | { ORDATA (FMASK, ds_fmask, 8) },\r | |
484 | { ORDATA (CYL, ds_cyl, 16) },\r | |
485 | { ORDATA (HS, ds_hs, 16) },\r | |
486 | { ORDATA (STATE, ds_state, 2), REG_RO },\r | |
487 | { ORDATA (LASTA, ds_lastatn, 3) },\r | |
488 | { DRDATA (FIP, ds_fifo_ip, 4) },\r | |
489 | { DRDATA (FRP, ds_fifo_rp, 4) },\r | |
490 | { DRDATA (FCNT, ds_fifo_cnt, 5) },\r | |
491 | { FLDATA (CMD, ds_dib.cmd, 0), REG_HRO },\r | |
492 | { FLDATA (CTL, ds_dib.ctl, 0) },\r | |
493 | { FLDATA (FLG, ds_dib.flg, 0) },\r | |
494 | { FLDATA (FBF, ds_dib.fbf, 0) },\r | |
495 | { FLDATA (SRQ, ds_dib.srq, 0) },\r | |
496 | { FLDATA (BUSY, ds_busy, 0) },\r | |
497 | { FLDATA (CMDF, ds_cmdf, 0) },\r | |
498 | { FLDATA (CMDP, ds_cmdp, 0) },\r | |
499 | { FLDATA (EOC, ds_eoc, 0) },\r | |
500 | { FLDATA (EOD, ds_eod, 0) },\r | |
501 | { BRDATA (DBUF, dsxb, 8, 16, DS_NUMWDF) },\r | |
502 | { DRDATA (DPTR, ds_ptr, 8) },\r | |
503 | { DRDATA (CTIME, ds_ctime, 24), PV_LEFT + REG_NZ },\r | |
504 | { DRDATA (DTIME, ds_dtime, 24), PV_LEFT + REG_NZ },\r | |
505 | { DRDATA (STIME, ds_stime, 24), PV_LEFT + REG_NZ },\r | |
506 | { DRDATA (RTIME, ds_rtime, 24), PV_LEFT + REG_NZ },\r | |
507 | { DRDATA (TIMEOUT, ds_tmo, 31), PV_LEFT + REG_NZ },\r | |
508 | { URDATA (UCYL, ds_unit[0].CYL, 10, 10, 0,\r | |
509 | DS_NUMDR + 1, PV_LEFT | REG_HRO) },\r | |
510 | { URDATA (UFNC, ds_unit[0].FNC, 8, 8, 0,\r | |
511 | DS_NUMDR + 1, REG_HRO) },\r | |
512 | { URDATA (USTA, ds_unit[0].STA, 8, 16, 0,\r | |
513 | DS_NUMDR + 1, REG_HRO) },\r | |
514 | { URDATA (CAPAC, ds_unit[0].capac, 10, T_ADDR_W, 0,\r | |
515 | DS_NUMDR, PV_LEFT | REG_HRO) },\r | |
516 | { ORDATA (DEVNO, ds_dib.devno, 6), REG_HRO },\r | |
517 | { NULL }\r | |
518 | };\r | |
519 | \r | |
520 | MTAB ds_mod[] = {\r | |
521 | { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", ds_load_unload },\r | |
522 | { UNIT_UNLOAD, 0, "heads loaded", "LOADED", ds_load_unload },\r | |
523 | { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r | |
524 | { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },\r | |
525 | { UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL },\r | |
526 | { UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL },\r | |
527 | { (UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE) + UNIT_ATT,\r | |
528 | "7905", NULL, NULL },\r | |
529 | { (UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE) + UNIT_ATT,\r | |
530 | "7906", NULL, NULL },\r | |
531 | { (UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE) + UNIT_ATT,\r | |
532 | "7920", NULL, NULL },\r | |
533 | { (UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE) + UNIT_ATT,\r | |
534 | "7925", NULL, NULL },\r | |
535 | { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE),\r | |
536 | "7905", NULL, NULL },\r | |
537 | { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE),\r | |
538 | "7906", NULL, NULL },\r | |
539 | { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE),\r | |
540 | "7920", NULL, NULL },\r | |
541 | { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE),\r | |
542 | "7925", NULL, NULL },\r | |
543 | { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },\r | |
544 | { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },\r | |
545 | { (UNIT_AUTO+UNIT_DTYPE), (D7905_DTYPE << UNIT_V_DTYPE),\r | |
546 | NULL, "7905", &ds_set_size },\r | |
547 | { (UNIT_AUTO+UNIT_DTYPE), (D7906_DTYPE << UNIT_V_DTYPE),\r | |
548 | NULL, "7906", &ds_set_size },\r | |
549 | { (UNIT_AUTO+UNIT_DTYPE), (D7920_DTYPE << UNIT_V_DTYPE),\r | |
550 | NULL, "7920", &ds_set_size },\r | |
551 | { (UNIT_AUTO+UNIT_DTYPE), (D7925_DTYPE << UNIT_V_DTYPE),\r | |
552 | NULL, "7925", &ds_set_size },\r | |
553 | { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO",\r | |
554 | &hp_setdev, &hp_showdev, &ds_dev },\r | |
555 | { 0 }\r | |
556 | };\r | |
557 | \r | |
558 | DEVICE ds_dev = {\r | |
559 | "DS", ds_unit, ds_reg, ds_mod,\r | |
560 | DS_NUMDR + 2, 8, 27, 1, 8, 16,\r | |
561 | NULL, NULL, &ds_reset,\r | |
562 | &ds_boot, &ds_attach, &ds_detach,\r | |
563 | &ds_dib, DEV_DISABLE\r | |
564 | };\r | |
565 | \r | |
566 | /* IO instructions */\r | |
567 | \r | |
568 | int32 dsio (int32 inst, int32 IR, int32 dat)\r | |
569 | {\r | |
570 | uint32 dev = IR & I_DEVMASK;\r | |
571 | \r | |
572 | switch (inst) { /* case on opcode */\r | |
573 | \r | |
574 | case ioFLG: /* flag clear/set */\r | |
575 | if ((IR & I_HC) == 0) { setFLG (dev); } /* STF */\r | |
576 | break;\r | |
577 | \r | |
578 | case ioSFS: /* skip flag set */\r | |
579 | if (FLG (dev) != 0) PC = (PC + 1) & VAMASK;\r | |
580 | break;\r | |
581 | \r | |
582 | case ioSFC: /* skip flag clear */\r | |
583 | if (ds_busy == 0) PC = (PC + 1) & VAMASK;\r | |
584 | break;\r | |
585 | \r | |
586 | case ioOTX: /* output */\r | |
587 | if (ds_cmdf) { /* expecting command? */\r | |
588 | ds_cmd = dat; /* save command */\r | |
589 | ds_cmdf = 0;\r | |
590 | ds_cmdp = 1; /* command present */\r | |
591 | }\r | |
592 | else ds_fifo_write (dat); /* put in fifo */\r | |
593 | break;\r | |
594 | \r | |
595 | case ioMIX: /* merge */\r | |
596 | dat = dat | ds_fifo_read ();\r | |
597 | break;\r | |
598 | \r | |
599 | case ioLIX: /* load */\r | |
600 | dat = ds_fifo_read ();\r | |
601 | break;\r | |
602 | \r | |
603 | case ioCRS: /* control reset */\r | |
604 | clrCTL (dev); /* clear control */\r | |
605 | ds_cmdf = 0; /* not expecting command */\r | |
606 | ds_cmdp = 0; /* and none pending */\r | |
607 | ds_reset_cmn (&ds_dev); /* reset ctrl */\r | |
608 | break;\r | |
609 | \r | |
610 | case ioCTL: /* control clear/set */\r | |
611 | if (IR & I_CTL) { /* CLC */\r | |
612 | clrCTL (dev); /* clear control */\r | |
613 | ds_cmdf = 1; /* expecting command */\r | |
614 | ds_cmdp = 0; /* none pending */\r | |
615 | ds_fifo_reset (); /* clear fifo */\r | |
616 | }\r | |
617 | else { /* STC */\r | |
618 | setCTL (dev); /* set ctl */\r | |
619 | }\r | |
620 | break;\r | |
621 | \r | |
622 | case ioEDT: /* end of transfer */\r | |
623 | ds_eod = 1; /* flag end transfer */\r | |
624 | break;\r | |
625 | \r | |
626 | default:\r | |
627 | break;\r | |
628 | }\r | |
629 | \r | |
630 | if (IR & I_HC) { clrFSR (dev); } /* H/C option */\r | |
631 | ds_poll (); /* run the controller */\r | |
632 | return dat;\r | |
633 | }\r | |
634 | \r | |
635 | /* Run the controller polling loop, based on ds_state:\r | |
636 | \r | |
637 | IDLE commands and ATN interrupts\r | |
638 | WAIT commands only\r | |
639 | BUSY nothing\r | |
640 | */\r | |
641 | \r | |
642 | void ds_poll (void)\r | |
643 | {\r | |
644 | int32 dev = ds_dib.devno;\r | |
645 | \r | |
646 | if ((ds_state != DS_BUSY) && ds_cmdp) ds_docmd (ds_cmd);/* cmd pending? */\r | |
647 | if ((ds_state == DS_IDLE) && CTL (dev)) ds_doatn (); /* idle? check ATN */\r | |
648 | return;\r | |
649 | }\r | |
650 | \r | |
651 | /* Process a command - ctrl state is either IDLE or WAIT.\r | |
652 | \r | |
653 | - A drive may be processing a seek or recalibrate\r | |
654 | - The controller unit is idle\r | |
655 | - If the command can be processed, ds_state is set to BUSY, and\r | |
656 | the interface command buffer is cleared\r | |
657 | - If the command cannot be processed, ds_state is set to WAIT,\r | |
658 | and the command is retained in the interface command buffer */\r | |
659 | \r | |
660 | void ds_docmd (uint32 cmd)\r | |
661 | {\r | |
662 | uint32 op, f, dtyp, unum;\r | |
663 | \r | |
664 | op = DSC_GETOP (cmd); /* operation */\r | |
665 | f = ds_opflags[op]; /* flags */\r | |
666 | if (op == DSC_COLD) unum = 0; /* boot force unit 0 */\r | |
667 | else unum = DSC_GETUNIT (cmd); /* get unit */\r | |
668 | if ((f & CMF_UIDLE) && (unum < DS_NUMDR) && /* idle required */\r | |
669 | sim_is_active (&ds_unit[unum])) { /* but unit busy? */\r | |
670 | ds_state = DS_WAIT; /* wait */\r | |
671 | return;\r | |
672 | }\r | |
673 | ds_cmdp = 0; /* flush command */\r | |
674 | ds_state = DS_BUSY; /* ctrl is busy */\r | |
675 | if (f & CMF_CLRS) ds_sr1 = 0; /* clear status */\r | |
676 | if (f & CMF_CLREC) ds_eoc = 0; /* clear end cyl */\r | |
677 | if (f & CMF_UNDF) { /* illegal op? */\r | |
678 | ds_sched_ctrl_op (DSC_BADF, 0, CLR_BUSY); /* sched, clr busy */\r | |
679 | return;\r | |
680 | }\r | |
681 | switch (op) {\r | |
682 | \r | |
683 | /* Drive commands */\r | |
684 | \r | |
685 | case DSC_COLD: /* cold load read */\r | |
686 | ds_fmask = DSC_SPEN; /* sparing enabled */\r | |
687 | ds_cyl = 0; /* cylinder 0 */\r | |
688 | ds_hs = (DSC_GETCHD (ds_cmd) << DSHS_V_HD) | /* reformat hd/sec */\r | |
689 | (DSC_GETCSC (ds_cmd) << DSHS_V_SC);\r | |
690 | case DSC_RECAL: /* recalibrate */\r | |
691 | case DSC_SEEK: /* seek */\r | |
692 | case DSC_READ: /* read */\r | |
693 | case DSC_RFULL: /* read full */\r | |
694 | case DSC_ROFF: /* read offset */\r | |
695 | case DSC_RNOVFY: /* read no verify */\r | |
696 | case DSC_VFY: /* verify */\r | |
697 | case DSC_WRITE: /* write */\r | |
698 | case DSC_WFULL: /* write full */\r | |
699 | case DSC_INIT: /* init */\r | |
700 | ds_sr1 = unum; /* init status */\r | |
701 | if (unum >= DS_NUMDR) { /* invalid unit? */\r | |
702 | ds_sched_ctrl_op (DSC_BADU, unum, CLR_BUSY);/* sched, not busy */\r | |
703 | return;\r | |
704 | }\r | |
705 | if (op == DSC_INIT) ds_sr1 |= /* init? */\r | |
706 | ((cmd & DSC_SPAR)? DS1_SPAR: 0) | /* copy SPD to stat1 */\r | |
707 | ((cmd & DSC_PROT)? DS1_PROT: 0) |\r | |
708 | ((cmd & DSC_DFCT)? DS1_DFCT: 0);\r | |
709 | ds_unit[unum].FNC = op; /* save op */\r | |
710 | ds_unit[unum].STA &= ~DS2_ATN; /* clear ATN */\r | |
711 | sim_cancel (&ds_unit[unum]); /* cancel current */\r | |
712 | sim_activate (&ds_unit[unum], ds_ctime); /* schedule unit */\r | |
713 | ds_busy = 1; /* set visible busy */\r | |
714 | break;\r | |
715 | \r | |
716 | /* Read status commands */\r | |
717 | \r | |
718 | case DSC_RSTA: /* read status */\r | |
719 | dsxb[1] = ds_sr1; /* return SR1 */\r | |
720 | ds_sr1 = 0; /* clear SR1 */\r | |
721 | if (unum < DS_NUMDR) { /* return SR2 */\r | |
722 | dsxb[0] = ds_updds2 (&ds_unit[unum]);\r | |
723 | ds_unit[unum].STA &= ~DS2_FS; /* clear 1st */\r | |
724 | }\r | |
725 | else dsxb[0] = DS2_ERR|DS2_NR;\r | |
726 | ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */\r | |
727 | break;\r | |
728 | \r | |
729 | case DSC_RSA: /* read sector address */\r | |
730 | dtyp = GET_DTYPE (ds_unit[unum].flags); /* get unit type */\r | |
731 | dsxb[0] = GET_CURSEC (ds_dtime * DS_NUMWD, dtyp); /* rot position */\r | |
732 | ds_sched_ctrl_op (DSC_RSTA, 1, SET_BUSY); /* sched 1 wd, busy */\r | |
733 | break;\r | |
734 | \r | |
735 | case DSC_RDA: /* read disk address */\r | |
736 | ds_reqad (&dsxb[1], &dsxb[0]); /* return disk address */\r | |
737 | ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */\r | |
738 | break;\r | |
739 | \r | |
740 | case DSC_RSYN: /* read syndrome */\r | |
741 | dsxb[6] = ds_sr1; /* return SR1 */\r | |
742 | ds_reqad (&dsxb[5], &dsxb[4]); /* return disk address */\r | |
743 | dsxb[3] = dsxb[2] = dsxb[1] = dsxb[0] = 0; /* syndrome is 0 */\r | |
744 | ds_sched_ctrl_op (DSC_RSTA, 7, SET_BUSY); /* sched 7 wds, busy */\r | |
745 | break;\r | |
746 | \r | |
747 | /* Other controller commands */\r | |
748 | \r | |
749 | case DSC_SFM: /* set file mask */\r | |
750 | case DSC_CLEAR: /* clear */\r | |
751 | case DSC_AREC: /* address record */\r | |
752 | case DSC_WAKE: /* wakeup */\r | |
753 | case DSC_WTIO: /* write TIO */\r | |
754 | ds_sched_ctrl_op (op, 0, SET_BUSY); /* schedule, busy */\r | |
755 | break;\r | |
756 | \r | |
757 | case DSC_END: /* end */\r | |
758 | ds_set_idle (); /* idle ctrl */\r | |
759 | break;\r | |
760 | }\r | |
761 | \r | |
762 | return;\r | |
763 | }\r | |
764 | \r | |
765 | /* Check for attention */\r | |
766 | \r | |
767 | void ds_doatn (void)\r | |
768 | {\r | |
769 | uint32 i, dev;\r | |
770 | \r | |
771 | dev = ds_dib.devno; /* device num */\r | |
772 | for (i = 0; i < DS_NUMDR; i++) { /* intr disabled? */\r | |
773 | ds_lastatn = (ds_lastatn + 1) & DS_DRMASK; /* loop through units */\r | |
774 | if (ds_unit[ds_lastatn].STA & DS2_ATN) { /* ATN set? */\r | |
775 | ds_unit[ds_lastatn].STA &= ~DS2_ATN; /* clear ATN */\r | |
776 | setFLG (dev); /* request interrupt */\r | |
777 | ds_sr1 = DS1_ATN | ds_lastatn; /* set up status 1 */\r | |
778 | ds_state = DS_WAIT; /* block atn intrs */\r | |
779 | return;\r | |
780 | }\r | |
781 | }\r | |
782 | return;\r | |
783 | }\r | |
784 | \r | |
785 | /* Controller service\r | |
786 | \r | |
787 | The argument for the function, if any, is stored in uptr->CYL */\r | |
788 | \r | |
789 | t_stat ds_svc_c (UNIT *uptr)\r | |
790 | {\r | |
791 | uint32 op, dev;\r | |
792 | \r | |
793 | op = uptr->FNC;\r | |
794 | dev = ds_dib.devno;\r | |
795 | switch (op) {\r | |
796 | \r | |
797 | case DSC_AREC: /* address record */\r | |
798 | ds_wait_for_cpu (uptr, DSC_AREC|DSC_2ND); /* set flag, new state */\r | |
799 | break;\r | |
800 | case DSC_AREC | DSC_2ND: /* poll done */\r | |
801 | if (!DS_FIFO_EMPTY) { /* OTA ds? */\r | |
802 | ds_cyl = ds_fifo_read (); /* save cylinder */\r | |
803 | ds_wait_for_cpu (uptr, DSC_AREC|DSC_3RD); /* set flag, new state */\r | |
804 | }\r | |
805 | else sim_activate (uptr, ds_ctime); /* no, continue poll */\r | |
806 | break;\r | |
807 | case DSC_AREC | DSC_3RD: /* poll done */\r | |
808 | if (!DS_FIFO_EMPTY) { /* OTA ds? */\r | |
809 | ds_hs = ds_fifo_read (); /* save head/sector */\r | |
810 | ds_cmd_done (0, DS1_OK); /* op done, no flag */\r | |
811 | }\r | |
812 | else sim_activate (uptr, ds_ctime); /* no, continue poll */\r | |
813 | break;\r | |
814 | \r | |
815 | case DSC_RSTA: /* rd stat (all forms) */\r | |
816 | if (DS_FIFO_EMPTY) { /* fifo empty? */\r | |
817 | uptr->CYL--;\r | |
818 | ds_fifo_write (dsxb[uptr->CYL]); /* store next status */\r | |
819 | ds_wait_for_cpu (uptr, DSC_RSTA |\r | |
820 | (uptr->CYL? 0: DSC_2ND)); /* set flag, new state */\r | |
821 | }\r | |
822 | else sim_activate (uptr, ds_ctime); /* no, continue poll */\r | |
823 | break;\r | |
824 | case DSC_RSTA | DSC_2ND: /* poll done */\r | |
825 | if (DS_FIFO_EMPTY) ds_cmd_done (0, DS1_OK); /* op done? no flag */\r | |
826 | else sim_activate (uptr, ds_ctime); /* no, continue poll */\r | |
827 | break;\r | |
828 | \r | |
829 | case DSC_CLEAR: /* clear */\r | |
830 | ds_reset_cmn (&ds_dev); /* reset ctrl */\r | |
831 | clrCTL (dev); /* clear CTL, SRQ */\r | |
832 | clrSRQ (dev);\r | |
833 | ds_cmd_done (1, DS1_OK); /* op done, set flag */\r | |
834 | break;\r | |
835 | \r | |
836 | case DSC_SFM: /* set file mask */\r | |
837 | ds_fmask = ds_cmd & DSC_FMASK;\r | |
838 | ds_cmd_done (1, DS1_OK); /* op done, set flag */\r | |
839 | break;\r | |
840 | \r | |
841 | case DSC_WTIO: /* write I/O */\r | |
842 | ds_cmd_done (0, DS1_OK); /* op done, no flag */\r | |
843 | break;\r | |
844 | \r | |
845 | case DSC_WAKE: /* wakeup */\r | |
846 | ds_cmd_done (1, DS1_AVAIL); /* op done, set flag */\r | |
847 | break;\r | |
848 | \r | |
849 | case DSC_BADU: /* invalid unit */\r | |
850 | if (uptr->CYL > 10) ds_cmd_done (1, DS1_UNAVL); /* [11,16]? bad unit */\r | |
851 | else ds_cmd_done (1, DS1_S2ERR); /* else unit not ready */\r | |
852 | break;\r | |
853 | \r | |
854 | case DSC_BADF: /* invalid operation */\r | |
855 | ds_cmd_done (1, DS1_ILLOP); /* op done, set flag */\r | |
856 | break;\r | |
857 | \r | |
858 | default:\r | |
859 | return SCPE_IERR;\r | |
860 | }\r | |
861 | \r | |
862 | ds_poll (); /* run the controller */\r | |
863 | return SCPE_OK;\r | |
864 | }\r | |
865 | \r | |
866 | /* Timeout service */\r | |
867 | \r | |
868 | t_stat ds_svc_t (UNIT *uptr)\r | |
869 | {\r | |
870 | int32 i;\r | |
871 | \r | |
872 | for (i = 0; i < (DS_NUMDR + 1); i++) /* cancel all ops */\r | |
873 | sim_cancel (&ds_unit[i]);\r | |
874 | ds_set_idle (); /* idle the controller */\r | |
875 | ds_fmask = 0; /* clear file mask */\r | |
876 | ds_poll (); /* run the controller */\r | |
877 | return SCPE_OK;\r | |
878 | }\r | |
879 | \r | |
880 | /* Unit service */\r | |
881 | \r | |
882 | t_stat ds_svc_u (UNIT *uptr)\r | |
883 | {\r | |
884 | uint32 op, dev, dtyp;\r | |
885 | t_stat r;\r | |
886 | \r | |
887 | op = uptr->FNC;\r | |
888 | dev = ds_dib.devno;\r | |
889 | dtyp = GET_DTYPE (uptr->flags);\r | |
890 | switch (op) { /* case on function */\r | |
891 | \r | |
892 | /* Seek and recalibrate */\r | |
893 | \r | |
894 | case DSC_RECAL: /* recalibrate */\r | |
895 | if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */\r | |
896 | ds_start_seek (uptr, 0, DSC_RECAL|DSC_2ND); /* set up seek */\r | |
897 | ds_set_idle (); /* ctrl is idle */\r | |
898 | }\r | |
899 | else ds_cmd_done (1, DS1_S2ERR); /* not ready error */\r | |
900 | break;\r | |
901 | case DSC_RECAL | DSC_2ND: /* recal complete */\r | |
902 | uptr->STA = uptr->STA | DS2_ATN; /* set attention */\r | |
903 | break;\r | |
904 | \r | |
905 | case DSC_SEEK: /* seek */\r | |
906 | ds_wait_for_cpu (uptr, DSC_SEEK|DSC_2ND); /* set flag, new state */\r | |
907 | break;\r | |
908 | case DSC_SEEK | DSC_2ND: /* waiting for word 1 */\r | |
909 | if (!DS_FIFO_EMPTY) { /* OTA ds? */\r | |
910 | ds_cyl = ds_fifo_read (); /* save cylinder */\r | |
911 | ds_wait_for_cpu (uptr, DSC_SEEK|DSC_3RD); /* set flag, new state */\r | |
912 | }\r | |
913 | else sim_activate (uptr, ds_ctime); /* no, continue poll */\r | |
914 | break;\r | |
915 | case DSC_SEEK | DSC_3RD: /* waiting for word 2 */\r | |
916 | if (!DS_FIFO_EMPTY) { /* OTA ds? */\r | |
917 | ds_hs = ds_fifo_read (); /* save head/sector */\r | |
918 | if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */\r | |
919 | ds_start_seek (uptr, ds_cyl, DSC_SEEK|DSC_4TH); /* set up seek */\r | |
920 | ds_set_idle (); /* ctrl is idle */\r | |
921 | }\r | |
922 | else ds_cmd_done (1, DS1_S2ERR); /* else not ready error */\r | |
923 | }\r | |
924 | else sim_activate (uptr, ds_ctime); /* continue poll */\r | |
925 | break;\r | |
926 | case DSC_SEEK | DSC_4TH: /* seek complete */\r | |
927 | uptr->STA = uptr->STA | DS2_ATN; /* set attention */\r | |
928 | break;\r | |
929 | \r | |
930 | /* Read variants */\r | |
931 | \r | |
932 | case DSC_ROFF: /* read with offset */\r | |
933 | ds_wait_for_cpu (uptr, DSC_ROFF|DSC_2ND); /* set flag, new state */\r | |
934 | break;\r | |
935 | case DSC_ROFF | DSC_2ND: /* poll done */\r | |
936 | if (!DS_FIFO_EMPTY) { /* OTA ds? new state */\r | |
937 | ds_fifo_read (); /* drain fifo */\r | |
938 | uptr->FNC = DSC_READ;\r | |
939 | setFLG (dev); /* handshake */\r | |
940 | }\r | |
941 | sim_activate (uptr, ds_ctime); /* schedule unit */\r | |
942 | break;\r | |
943 | \r | |
944 | case DSC_COLD: /* cold load read */\r | |
945 | if ((uptr->flags & UNIT_UNLOAD) == 0) /* drive up? */\r | |
946 | ds_start_seek (uptr, 0, DSC_READ); /* set up seek */\r | |
947 | else ds_cmd_done (1, DS1_S2ERR); /* no, not ready error */\r | |
948 | break;\r | |
949 | \r | |
950 | case DSC_READ: /* read */\r | |
951 | if (r = ds_start_rd (uptr, 0, 1)) return r; /* new sector; error? */\r | |
952 | break;\r | |
953 | case DSC_READ | DSC_2ND: /* word transfer */\r | |
954 | ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */\r | |
955 | break;\r | |
956 | case DSC_READ | DSC_3RD: /* end of sector */\r | |
957 | ds_end_rw (uptr, DSC_READ); /* see if more to do */\r | |
958 | break;\r | |
959 | \r | |
960 | case DSC_RNOVFY: /* read, no verify */\r | |
961 | if (r = ds_start_rd (uptr, 0, 0)) return r; /* new sector; error? */\r | |
962 | break;\r | |
963 | case DSC_RNOVFY | DSC_2ND: /* word transfer */\r | |
964 | ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */\r | |
965 | break;\r | |
966 | case DSC_RNOVFY | DSC_3RD: /* end of sector */\r | |
967 | ds_end_rw (uptr, DSC_RNOVFY); /* see if more to do */\r | |
968 | break;\r | |
969 | \r | |
970 | case DSC_RFULL: /* read full */\r | |
971 | dsxb[DS_FSYNC] = 0100376; /* fill in header */\r | |
972 | dsxb[DS_FCYL] = uptr->CYL;\r | |
973 | dsxb[DS_FHS] = ds_hs; /* before h/s update */\r | |
974 | if (r = ds_start_rd (uptr, DS_FDATA, 0)) /* new sector; error? */\r | |
975 | return r;\r | |
976 | break;\r | |
977 | case DSC_RFULL | DSC_2ND: /* word transfer */\r | |
978 | ds_cont_rd (uptr, DS_NUMWDF); /* xfr wd, check end */\r | |
979 | break;\r | |
980 | case DSC_RFULL | DSC_3RD: /* end of sector */\r | |
981 | ds_end_rw (uptr, DSC_RFULL); /* see if more to do */\r | |
982 | break;\r | |
983 | \r | |
984 | case DSC_VFY: /* verify */\r | |
985 | ds_wait_for_cpu (uptr, DSC_VFY|DSC_2ND); /* set flag, new state */\r | |
986 | break;\r | |
987 | case DSC_VFY | DSC_2ND: /* poll done */\r | |
988 | if (!DS_FIFO_EMPTY) { /* OTA ds? */\r | |
989 | ds_vctr = ds_fifo_read (); /* save count */\r | |
990 | uptr->FNC = DSC_VFY | DSC_3RD; /* next state */\r | |
991 | sim_activate (uptr, ds_rtime); /* delay for transfer */\r | |
992 | }\r | |
993 | else sim_activate (uptr, ds_ctime); /* no, continue poll */\r | |
994 | break;\r | |
995 | case DSC_VFY | DSC_3RD: /* start sector */\r | |
996 | if (ds_start_rw (uptr, ds_dtime * DS_NUMWD, 1)) break;\r | |
997 | /* new sector; error? */\r | |
998 | ds_next_sec (uptr); /* increment hd, sc */\r | |
999 | break;\r | |
1000 | case DSC_VFY | DSC_4TH: /* end sector */\r | |
1001 | ds_vctr = (ds_vctr - 1) & DMASK; /* decrement count */\r | |
1002 | if (ds_vctr) ds_end_rw (uptr, DSC_VFY|DSC_3RD); /* more to do? */\r | |
1003 | else ds_cmd_done (1, DS1_OK); /* no, set done */\r | |
1004 | break;\r | |
1005 | \r | |
1006 | /* Write variants */\r | |
1007 | \r | |
1008 | case DSC_WRITE: /* write */\r | |
1009 | ds_start_wr (uptr, 1); /* new sector */\r | |
1010 | break;\r | |
1011 | case DSC_WRITE | DSC_2ND:\r | |
1012 | if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */\r | |
1013 | return r; /* error? */\r | |
1014 | break;\r | |
1015 | case DSC_WRITE | DSC_3RD: /* end sector */\r | |
1016 | ds_end_rw (uptr, DSC_WRITE); /* see if more to do */\r | |
1017 | break;\r | |
1018 | \r | |
1019 | case DSC_INIT: /* init */\r | |
1020 | ds_start_wr (uptr, 0); /* new sector */\r | |
1021 | break;\r | |
1022 | case DSC_INIT | DSC_2ND:\r | |
1023 | if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */\r | |
1024 | return r; /* error? */\r | |
1025 | break;\r | |
1026 | case DSC_INIT | DSC_3RD: /* end sector */\r | |
1027 | ds_end_rw (uptr, DSC_INIT); /* see if more to do */\r | |
1028 | break;\r | |
1029 | \r | |
1030 | case DSC_WFULL: /* write full */\r | |
1031 | ds_start_wr (uptr, 0); /* new sector */\r | |
1032 | break;\r | |
1033 | case DSC_WFULL | DSC_2ND:\r | |
1034 | if (r = ds_cont_wr (uptr, DS_FDATA, DS_NUMWDF)) /* write word */\r | |
1035 | return r; /* error */\r | |
1036 | break;\r | |
1037 | case DSC_WFULL | DSC_3RD:\r | |
1038 | ds_end_rw (uptr, DSC_WFULL); /* see if more to do */\r | |
1039 | break;\r | |
1040 | \r | |
1041 | default:\r | |
1042 | break;\r | |
1043 | }\r | |
1044 | \r | |
1045 | ds_poll ();\r | |
1046 | return SCPE_OK;\r | |
1047 | }\r | |
1048 | \r | |
1049 | /* Schedule timed wait for CPU response\r | |
1050 | \r | |
1051 | - Set flag to get CPU attention\r | |
1052 | - Set specified unit to 'newstate' and schedule\r | |
1053 | - Schedule timeout */\r | |
1054 | \r | |
1055 | void ds_wait_for_cpu (UNIT *uptr, uint32 newst)\r | |
1056 | {\r | |
1057 | uint32 dev = ds_dib.devno;\r | |
1058 | \r | |
1059 | setFLG (dev); /* set flag */\r | |
1060 | uptr->FNC = newst; /* new state */\r | |
1061 | sim_activate (uptr, ds_ctime); /* activate unit */\r | |
1062 | sim_cancel (&ds_timer); /* activate timeout */\r | |
1063 | sim_activate (&ds_timer, ds_tmo);\r | |
1064 | return;\r | |
1065 | }\r | |
1066 | \r | |
1067 | /* Set idle state\r | |
1068 | \r | |
1069 | - Controller is set to idle state\r | |
1070 | - Visible busy is cleared\r | |
1071 | - Timeout is cancelled */\r | |
1072 | \r | |
1073 | void ds_set_idle (void)\r | |
1074 | {\r | |
1075 | ds_busy = 0; /* busy clear */\r | |
1076 | ds_state = DS_IDLE; /* ctrl idle */\r | |
1077 | sim_cancel (&ds_timer); /* no timeout */\r | |
1078 | return;\r | |
1079 | }\r | |
1080 | \r | |
1081 | /* Set wait state\r | |
1082 | \r | |
1083 | - Set flag if required\r | |
1084 | - Set controller to wait state\r | |
1085 | - Clear visible busy\r | |
1086 | - Schedule timeout */\r | |
1087 | \r | |
1088 | void ds_cmd_done (t_bool sf, uint32 sr1)\r | |
1089 | {\r | |
1090 | uint32 dev = ds_dib.devno;\r | |
1091 | \r | |
1092 | if (sf) { setFLG (dev); } /* set host flag */\r | |
1093 | ds_busy = 0; /* clear visible busy */\r | |
1094 | ds_sr1 = ds_sr1 | sr1; /* final status */\r | |
1095 | ds_state = DS_WAIT; /* ctrl waiting */\r | |
1096 | sim_cancel (&ds_timer); /* activate timeout */\r | |
1097 | sim_activate (&ds_timer, ds_tmo);\r | |
1098 | return;\r | |
1099 | }\r | |
1100 | \r | |
1101 | /* Return drive status (status word 2) */\r | |
1102 | \r | |
1103 | uint32 ds_updds2 (UNIT *uptr)\r | |
1104 | {\r | |
1105 | uint32 sta;\r | |
1106 | uint32 dtyp = GET_DTYPE (uptr->flags);\r | |
1107 | \r | |
1108 | sta = drv_tab[dtyp].id | /* form status */\r | |
1109 | uptr->STA | /* static bits */\r | |
1110 | ((uptr->flags & UNIT_WPR)? DS2_RO: 0) | /* dynamic bits */\r | |
1111 | ((uptr->flags & UNIT_FMT)? DS2_FRM: 0) |\r | |
1112 | ((uptr->flags & UNIT_UNLOAD)? DS2_NR | DS2_BS: 0) |\r | |
1113 | (sim_is_active (uptr)? DS2_BS: 0);\r | |
1114 | if (sta & DS2_ALLERR) sta = sta | DS2_ERR; /* set error */\r | |
1115 | return sta;\r | |
1116 | }\r | |
1117 | \r | |
1118 | /* Schedule controller operation */\r | |
1119 | \r | |
1120 | void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy)\r | |
1121 | {\r | |
1122 | ds_ctrl.FNC = op; /* save op */\r | |
1123 | ds_ctrl.CYL = arg; /* save argument */\r | |
1124 | ds_busy = busy; /* set visible busy */\r | |
1125 | sim_activate (&ds_ctrl, ds_ctime); /* schedule */\r | |
1126 | sim_cancel (&ds_timer); /* activate timeout */\r | |
1127 | sim_activate (&ds_timer, ds_tmo);\r | |
1128 | return;\r | |
1129 | }\r | |
1130 | \r | |
1131 | /* Request address - if pending eoc, report cylinder + 1 */\r | |
1132 | \r | |
1133 | void ds_reqad (uint16 *cyl, uint16 *hs)\r | |
1134 | {\r | |
1135 | *cyl = ds_cyl + (ds_eoc? 1: 0);\r | |
1136 | *hs = ds_hs;\r | |
1137 | return;\r | |
1138 | }\r | |
1139 | \r | |
1140 | /* Start seek - schedule whether in bounds or out of bounds */\r | |
1141 | \r | |
1142 | void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst)\r | |
1143 | {\r | |
1144 | int32 t;\r | |
1145 | uint32 hd, sc;\r | |
1146 | uint32 dtyp = GET_DTYPE (uptr->flags);\r | |
1147 | \r | |
1148 | uptr->FNC = newst; /* set new state */\r | |
1149 | if (cyl >= drv_tab[dtyp].cyl) { /* out of bounds? */\r | |
1150 | t = 0; /* don't change cyl */\r | |
1151 | uptr->STA = uptr->STA | DS2_SC; /* set seek check */\r | |
1152 | }\r | |
1153 | else {\r | |
1154 | t = abs (uptr->CYL - cyl); /* delta cylinders */\r | |
1155 | uptr->CYL = cyl; /* put on cylinder */\r | |
1156 | hd = DSHS_GETHD (ds_hs); /* invalid head or sec? */\r | |
1157 | sc = DSHS_GETSC (ds_hs);\r | |
1158 | if ((hd >= drv_tab[dtyp].hd) ||\r | |
1159 | (sc >= drv_tab[dtyp].sc))\r | |
1160 | uptr->STA = uptr->STA | DS2_SC; /* set seek check */\r | |
1161 | else uptr->STA = uptr->STA & ~DS2_SC; /* clear seek check */\r | |
1162 | }\r | |
1163 | sim_activate (uptr, ds_stime * (t + 1)); /* schedule */\r | |
1164 | return;\r | |
1165 | }\r | |
1166 | \r | |
1167 | /* Start next sector for read or write\r | |
1168 | \r | |
1169 | - If error, set command done, return TRUE, nothing is scheduled\r | |
1170 | - If implicit seek, return TRUE, implicit seek is scheduled, but\r | |
1171 | state is not changed - we will return here when seek is done\r | |
1172 | - Otherwise, advance state, set position in file, schedule next state */\r | |
1173 | \r | |
1174 | t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy)\r | |
1175 | {\r | |
1176 | uint32 da, hd, sc;\r | |
1177 | uint32 dtyp = GET_DTYPE (uptr->flags);\r | |
1178 | \r | |
1179 | ds_eod = 0; /* init eod */\r | |
1180 | ds_ptr = 0; /* init buffer ptr */\r | |
1181 | if (uptr->flags & UNIT_UNLOAD) { /* drive down? */\r | |
1182 | ds_cmd_done (1, DS1_S2ERR);\r | |
1183 | return TRUE;\r | |
1184 | }\r | |
1185 | if (ds_eoc) { /* at end of cylinder? */\r | |
1186 | ds_next_cyl (uptr); /* auto seek to next */\r | |
1187 | return TRUE; /* or error */\r | |
1188 | }\r | |
1189 | if (vfy && ((uint32) uptr->CYL != ds_cyl)) { /* on wrong cylinder? */\r | |
1190 | if (ds_cyl >= drv_tab[dtyp].cyl) /* seeking to bad? */\r | |
1191 | ds_cmd_done (1, DS1_CYLCE); /* lose */\r | |
1192 | else ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek right cyl */\r | |
1193 | return TRUE;\r | |
1194 | }\r | |
1195 | hd = DSHS_GETHD (ds_hs);\r | |
1196 | sc = DSHS_GETSC (ds_hs);\r | |
1197 | if ((uint32) uptr->CYL >= drv_tab[dtyp].cyl) { /* valid cylinder? */\r | |
1198 | uptr->STA = uptr->STA | DS2_SC; /* set seek check */\r | |
1199 | ds_cmd_done (1, DS1_S2ERR); /* error */\r | |
1200 | return TRUE;\r | |
1201 | }\r | |
1202 | if ((hd >= drv_tab[dtyp].hd) || /* valid head, sector? */\r | |
1203 | (sc >= drv_tab[dtyp].sc)) {\r | |
1204 | ds_cmd_done (1, DS1_HSCE); /* no, error */\r | |
1205 | return TRUE;\r | |
1206 | }\r | |
1207 | da = GET_DA (uptr->CYL, hd, sc, dtyp); /* position in file */\r | |
1208 | sim_fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); /* set file pos */\r | |
1209 | uptr->FNC += DSC_NEXT; /* next state */\r | |
1210 | sim_activate (uptr, tm); /* activate unit */\r | |
1211 | return FALSE;\r | |
1212 | }\r | |
1213 | \r | |
1214 | /* Start next sector for read\r | |
1215 | \r | |
1216 | - Do common start for read and write\r | |
1217 | - If error, return, command has been terminated, nothing scheduled\r | |
1218 | - If implicit seek, return, seek scheduled\r | |
1219 | - If no error or seek, state has been advanced and unit scheduled\r | |
1220 | - Read sector\r | |
1221 | - If read error, terminate command and return, nothing scheduled\r | |
1222 | - If no error, advance head/sector, next state scheduled */\r | |
1223 | \r | |
1224 | t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy)\r | |
1225 | {\r | |
1226 | uint32 t;\r | |
1227 | \r | |
1228 | if (ds_start_rw (uptr, ds_rtime, vfy)) return SCPE_OK; /* new sec; err or seek? */\r | |
1229 | t = sim_fread (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref);\r | |
1230 | for (t = t + off ; t < DS_NUMWDF; t++) dsxb[t] = 0; /* fill sector */\r | |
1231 | if (ferror (uptr->fileref)) /* error? */\r | |
1232 | return ds_set_uncorr (uptr); /* say uncorrectable */\r | |
1233 | ds_next_sec (uptr); /* increment hd, sc */\r | |
1234 | return SCPE_OK;\r | |
1235 | }\r | |
1236 | \r | |
1237 | /* Start next sector for write\r | |
1238 | \r | |
1239 | - Do common start for read and write\r | |
1240 | - If error, return, command has been terminated, nothing scheduled\r | |
1241 | - If implicit seek, return, seek scheduled\r | |
1242 | - If no error or seek, state has been advanced and unit scheduled\r | |
1243 | - Clear buffer\r | |
1244 | - Set service request */\r | |
1245 | \r | |
1246 | void ds_start_wr (UNIT *uptr, t_bool vfy)\r | |
1247 | {\r | |
1248 | uint32 i;\r | |
1249 | uint32 dev = ds_dib.devno;\r | |
1250 | \r | |
1251 | if ((uptr->flags & UNIT_WPR) || /* write protected? */\r | |
1252 | (!vfy && ((uptr->flags & UNIT_FMT) == 0))) { /* format, not enbl? */\r | |
1253 | ds_cmd_done (1, DS1_S2ERR); /* error */\r | |
1254 | return;\r | |
1255 | }\r | |
1256 | if (ds_start_rw (uptr, ds_rtime, vfy)) return; /* new sec; err or seek? */\r | |
1257 | for (i = 0; i < DS_NUMWDF; i++) dsxb[i] = 0; /* clear buffer */\r | |
1258 | setSRQ (dev); /* request word */\r | |
1259 | return;\r | |
1260 | }\r | |
1261 | \r | |
1262 | /* Advance to next sector (but not next cylinder) */\r | |
1263 | \r | |
1264 | void ds_next_sec (UNIT *uptr)\r | |
1265 | {\r | |
1266 | uint32 dtyp = GET_DTYPE (uptr->flags);\r | |
1267 | \r | |
1268 | ds_hs = ds_hs + 1; /* increment sector */\r | |
1269 | if (DSHS_GETSC (ds_hs) < drv_tab[dtyp].sc) return; /* end of track? */\r | |
1270 | ds_hs = ds_hs & ~DSHS_SC; /* yes, wrap sector */\r | |
1271 | if (ds_fmask & DSC_CYLM) { /* cylinder mode? */\r | |
1272 | ds_hs = ds_hs + (1 << DSHS_V_HD); /* increment head */\r | |
1273 | if (DSHS_GETHD (ds_hs) < drv_tab[dtyp].hd) return; /* end of cyl? */\r | |
1274 | ds_hs = ds_hs & ~DSHS_HD; /* 0 head */\r | |
1275 | }\r | |
1276 | ds_eoc = 1; /* flag end cylinder */\r | |
1277 | return;\r | |
1278 | }\r | |
1279 | \r | |
1280 | /* Advance to next cylinder\r | |
1281 | \r | |
1282 | - If autoseek enabled, seek to cylinder +/- 1\r | |
1283 | - Otherwise, done with end of cylinder error */\r | |
1284 | \r | |
1285 | void ds_next_cyl (UNIT *uptr)\r | |
1286 | {\r | |
1287 | if (ds_fmask & DSC_AUTO) { /* auto seek allowed? */\r | |
1288 | if (ds_fmask & DSC_DECR) ds_cyl = (ds_cyl - 1) & DMASK;\r | |
1289 | else ds_cyl = (ds_cyl + 1) & DMASK;\r | |
1290 | ds_eoc = 0; /* clear end cylinder */\r | |
1291 | ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek, same state */\r | |
1292 | }\r | |
1293 | else ds_cmd_done (1, DS1_EOCYL); /* no, end of cyl err */\r | |
1294 | return;\r | |
1295 | }\r | |
1296 | \r | |
1297 | /* Transfer word for read\r | |
1298 | \r | |
1299 | - If end of data, terminate command, nothing scheduled\r | |
1300 | - Otherwise, transfer word, advance state if last word, schedule */\r | |
1301 | \r | |
1302 | void ds_cont_rd (UNIT *uptr, uint32 bsize)\r | |
1303 | {\r | |
1304 | uint32 dev = ds_dib.devno;\r | |
1305 | \r | |
1306 | if (ds_eod) ds_cmd_done (1, DS1_OK); /* DMA end? done */\r | |
1307 | else if (SRQ (dev)) { /* overrun? */\r | |
1308 | ds_cmd_done (1, DS1_OVRUN); /* set done */\r | |
1309 | return;\r | |
1310 | }\r | |
1311 | else { ds_fifo_write (dsxb[ds_ptr++]); /* next word */\r | |
1312 | setSRQ (dev); /* request service */\r | |
1313 | if (ds_ptr >= bsize) uptr->FNC += DSC_NEXT; /* sec done? next state */\r | |
1314 | sim_activate (uptr, ds_dtime); /* schedule */\r | |
1315 | }\r | |
1316 | return;\r | |
1317 | }\r | |
1318 | \r | |
1319 | /* Transfer word for write\r | |
1320 | \r | |
1321 | - Copy word from fifo to buffer\r | |
1322 | - If end of data, write buffer, terminate command, nothing scheduled\r | |
1323 | - If end of sector, write buffer, next state, schedule\r | |
1324 | - Otherwises, set service request, schedule */\r | |
1325 | \r | |
1326 | t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize)\r | |
1327 | {\r | |
1328 | uint32 i, dat;\r | |
1329 | uint32 dev = ds_dib.devno;\r | |
1330 | \r | |
1331 | if (SRQ (dev)) { /* overrun? */\r | |
1332 | ds_cmd_done (1, DS1_OVRUN); /* set done */\r | |
1333 | return SCPE_OK;\r | |
1334 | }\r | |
1335 | dsxb[ds_ptr++] = dat = ds_fifo_read (); /* next word */\r | |
1336 | if (ds_eod || (ds_ptr >= bsize)) { /* xfr or sector done? */\r | |
1337 | for (i = ds_ptr; i < bsize; i++) dsxb[i] = dat; /* fill sector */\r | |
1338 | sim_fwrite (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref);\r | |
1339 | if (ferror (uptr->fileref)) /* error on write? */\r | |
1340 | return ds_set_uncorr (uptr); /* uncorrectable */\r | |
1341 | ds_next_sec (uptr); /* increment hd, sc */\r | |
1342 | if (ds_eod) { /* end data? */\r | |
1343 | ds_cmd_done (1, DS1_OK); /* set done */\r | |
1344 | return SCPE_OK;\r | |
1345 | }\r | |
1346 | else uptr->FNC += DSC_NEXT; /* no, next state */\r | |
1347 | }\r | |
1348 | else { setSRQ (dev); } /* request next word */\r | |
1349 | sim_activate (uptr, ds_dtime); /* schedule */\r | |
1350 | return SCPE_OK;\r | |
1351 | }\r | |
1352 | \r | |
1353 | /* End sector for read or write\r | |
1354 | \r | |
1355 | - If end of data, terminate command, nothing scheduled\r | |
1356 | - If end of cylinder, schedule next cylinder\r | |
1357 | - Else schedule start of next sector */\r | |
1358 | \r | |
1359 | void ds_end_rw (UNIT *uptr, uint32 newst)\r | |
1360 | {\r | |
1361 | uptr->FNC = newst; /* new state */\r | |
1362 | if (ds_eod) ds_cmd_done (1, DS1_OK); /* done? */\r | |
1363 | else if (ds_eoc) ds_next_cyl (uptr); /* end cyl? seek */\r | |
1364 | else sim_activate (uptr, ds_rtime); /* normal transfer */\r | |
1365 | return;\r | |
1366 | }\r | |
1367 | \r | |
1368 | /* Report uncorrectable data error */\r | |
1369 | \r | |
1370 | t_stat ds_set_uncorr (UNIT *uptr)\r | |
1371 | {\r | |
1372 | sim_cancel (uptr); /* cancel any operation */\r | |
1373 | ds_cmd_done (1, DS1_UNCOR); /* done with error */\r | |
1374 | perror ("DS I/O error"); /* visible error */\r | |
1375 | clearerr (uptr->fileref);\r | |
1376 | ds_poll (); /* force poll */\r | |
1377 | return SCPE_IOERR;\r | |
1378 | }\r | |
1379 | \r | |
1380 | /* Fifo read */\r | |
1381 | \r | |
1382 | uint32 ds_fifo_read (void)\r | |
1383 | {\r | |
1384 | uint32 dat;\r | |
1385 | \r | |
1386 | if (ds_fifo_cnt == 0) return ds_fifo[ds_fifo_rp];\r | |
1387 | dat = ds_fifo[ds_fifo_rp++];\r | |
1388 | if (ds_fifo_rp >= DS_FIFO_SIZE) ds_fifo_rp = 0;\r | |
1389 | ds_fifo_cnt--;\r | |
1390 | return dat;\r | |
1391 | }\r | |
1392 | \r | |
1393 | void ds_fifo_write (uint32 dat)\r | |
1394 | {\r | |
1395 | ds_fifo[ds_fifo_ip++] = dat;\r | |
1396 | if (ds_fifo_ip >= DS_FIFO_SIZE) ds_fifo_ip = 0;\r | |
1397 | if (ds_fifo_cnt < DS_FIFO_SIZE) ds_fifo_cnt++;\r | |
1398 | return;\r | |
1399 | }\r | |
1400 | \r | |
1401 | void ds_fifo_reset (void)\r | |
1402 | {\r | |
1403 | uint32 i;\r | |
1404 | \r | |
1405 | ds_fifo_ip = ds_fifo_rp = ds_fifo_cnt = 0;\r | |
1406 | for (i = 0; i < DS_FIFO_SIZE; i++) ds_fifo[i] = 0;\r | |
1407 | return;\r | |
1408 | }\r | |
1409 | \r | |
1410 | /* Reset routine */\r | |
1411 | \r | |
1412 | t_stat ds_reset_cmn (DEVICE *dptr)\r | |
1413 | {\r | |
1414 | int32 i;\r | |
1415 | \r | |
1416 | ds_cmd = 0; /* clear command */\r | |
1417 | ds_cmdf = ds_cmdp = 0; /* clear commands flops */\r | |
1418 | ds_fifo_reset (); /* clear fifo */\r | |
1419 | ds_eoc = ds_eod = 0;\r | |
1420 | ds_busy = 0;\r | |
1421 | ds_state = DS_IDLE; /* ctrl idle */\r | |
1422 | ds_lastatn = 0;\r | |
1423 | ds_fmask = 0;\r | |
1424 | ds_ptr = 0;\r | |
1425 | ds_cyl = ds_hs = 0;\r | |
1426 | ds_vctr = 0;\r | |
1427 | for (i = 0; i < DS_NUMDR; i++) { /* loop thru drives */\r | |
1428 | sim_cancel (&ds_unit[i]); /* cancel activity */\r | |
1429 | ds_unit[i].FNC = 0; /* clear function */\r | |
1430 | ds_unit[i].CYL = 0;\r | |
1431 | ds_unit[i].STA = 0;\r | |
1432 | }\r | |
1433 | sim_cancel (&ds_ctrl);\r | |
1434 | sim_cancel (&ds_timer);\r | |
1435 | return SCPE_OK;\r | |
1436 | }\r | |
1437 | \r | |
1438 | t_stat ds_reset (DEVICE *dptr)\r | |
1439 | {\r | |
1440 | ds_dib.cmd = 0; /* clear cmd */\r | |
1441 | ds_dib.ctl = 0; /* clear ctl */\r | |
1442 | ds_dib.fbf = 1; /* set fbf */\r | |
1443 | ds_dib.flg = 1; /* set flg */\r | |
1444 | ds_dib.srq = 0; /* clear srq */\r | |
1445 | return ds_reset_cmn (dptr); /* do common reset */\r | |
1446 | }\r | |
1447 | \r | |
1448 | /* Device attach */\r | |
1449 | \r | |
1450 | t_stat ds_attach (UNIT *uptr, char *cptr)\r | |
1451 | {\r | |
1452 | uint32 i, p;\r | |
1453 | t_stat r;\r | |
1454 | \r | |
1455 | uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size;\r | |
1456 | r = attach_unit (uptr, cptr); /* attach unit */\r | |
1457 | if (r != SCPE_OK) return r; /* error? */\r | |
1458 | ds_load_unload (uptr, 0, NULL, NULL); /* if OK, load heads */\r | |
1459 | ds_sched_atn (uptr); /* schedule attention */\r | |
1460 | if (((uptr->flags & UNIT_AUTO) == 0) || /* static size? */\r | |
1461 | ((p = sim_fsize (uptr->fileref)) == 0)) return SCPE_OK; /* new file? */\r | |
1462 | for (i = 0; drv_tab[i].sc != 0; i++) { /* find best fit */\r | |
1463 | if (p <= (drv_tab[i].size * sizeof (uint16))) {\r | |
1464 | uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE);\r | |
1465 | uptr->capac = drv_tab[i].size;\r | |
1466 | return SCPE_OK;\r | |
1467 | }\r | |
1468 | }\r | |
1469 | return SCPE_OK;\r | |
1470 | }\r | |
1471 | \r | |
1472 | /* Device detach */\r | |
1473 | \r | |
1474 | t_stat ds_detach (UNIT *uptr)\r | |
1475 | {\r | |
1476 | ds_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads if attached */\r | |
1477 | return detach_unit (uptr);\r | |
1478 | }\r | |
1479 | \r | |
1480 | /* Load and unload heads */\r | |
1481 | \r | |
1482 | t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc)\r | |
1483 | {\r | |
1484 | if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to [un]load */\r | |
1485 | if (value == UNIT_UNLOAD) { /* unload heads? */\r | |
1486 | uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */\r | |
1487 | uptr->STA = DS2_ATN; /* update drive status */\r | |
1488 | ds_sched_atn (uptr); /* schedule attention */\r | |
1489 | }\r | |
1490 | else { /* load heads */\r | |
1491 | uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */\r | |
1492 | uptr->STA = DS2_ATN | DS2_FS; /* update drive status */\r | |
1493 | }\r | |
1494 | return SCPE_OK;\r | |
1495 | }\r | |
1496 | \r | |
1497 | /* Schedule attention interrupt if CTL set, not restore, and controller idle */\r | |
1498 | \r | |
1499 | void ds_sched_atn (UNIT *uptr)\r | |
1500 | {\r | |
1501 | int32 i;\r | |
1502 | \r | |
1503 | if (!ds_dib.ctl || (sim_switches & SIM_SW_REST)) return;\r | |
1504 | for (i = 0; i < (DS_NUMDR + 1); i++) { /* check units, ctrl */\r | |
1505 | if (sim_is_active (ds_dev.units + i)) return;\r | |
1506 | }\r | |
1507 | uptr->FNC = DSC_ATN; /* pseudo operation */\r | |
1508 | sim_activate (uptr, 1); /* do immediately */\r | |
1509 | return;\r | |
1510 | }\r | |
1511 | \r | |
1512 | /* Set size command validation routine */\r | |
1513 | \r | |
1514 | t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
1515 | {\r | |
1516 | if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r | |
1517 | uptr->capac = drv_tab[GET_DTYPE (val)].size;\r | |
1518 | return SCPE_OK;\r | |
1519 | }\r | |
1520 | \r | |
1521 | /* 13037 bootstrap routine (HP 12992B ROM) */\r | |
1522 | \r | |
1523 | const uint16 ds_rom[IBL_LNT] = {\r | |
1524 | 0017727, /* STRT JSB STAT ; get status */\r | |
1525 | 0002021, /* SSA,RSS ; is drive ready? */\r | |
1526 | 0027742, /* JMP DMA ; yes, set up DMA */\r | |
1527 | 0013714, /* AND B20 ; no, check status bits */\r | |
1528 | 0002002, /* SZA ; faulty or hard down? */\r | |
1529 | 0102030, /* HLT 30B ; HALT 30B */\r | |
1530 | 0027700, /* JMP STRT ; try again */\r | |
1531 | 0102011, /* ADR1 OCT 102011 */\r | |
1532 | 0102055, /* ADR2 OCT 102055 */\r | |
1533 | 0164000, /* CNT DEC -6144 */\r | |
1534 | 0000007, /* D7 OCT 7 */\r | |
1535 | 0001400, /* STCM OCT 1400 */\r | |
1536 | 0000020, /* B20 OCT 20 */\r | |
1537 | 0017400, /* STMS OCT 17400 */\r | |
1538 | 0000000, /* 9 NOP's */\r | |
1539 | 0000000,\r | |
1540 | 0000000,\r | |
1541 | 0000000,\r | |
1542 | 0000000,\r | |
1543 | 0000000,\r | |
1544 | 0000000,\r | |
1545 | 0000000,\r | |
1546 | 0000000,\r | |
1547 | 0000000, /* STAT NOP ; status check routine */\r | |
1548 | 0107710, /* CLC DC,C ; set command mode */\r | |
1549 | 0063713, /* LDA STCM ; get status command */\r | |
1550 | 0102610, /* OTA DC ; output status command */\r | |
1551 | 0102310, /* SFS DC ; wait for stat#1 word */\r | |
1552 | 0027733, /* JMP *-1 */\r | |
1553 | 0107510, /* LIB DC,C ; B-reg - status#1 word */\r | |
1554 | 0102310, /* SFS DC ; wait for stat#2 word */\r | |
1555 | 0027736, /* JMP *-1 */\r | |
1556 | 0103510, /* LIA DC,C ; A-reg - status#2 word */\r | |
1557 | 0127727, /* JMP STAT,I ; return */\r | |
1558 | 0067776, /* DMA LDB DMAC ; get DMA control word */\r | |
1559 | 0106606, /* OTB 6 ; output DMA ctrl word */\r | |
1560 | 0067707, /* LDB ADR1 ; get memory address */\r | |
1561 | 0106702, /* CLC 2 ; set memory addr mode */\r | |
1562 | 0106602, /* OTB 2 ; output mem addr to DMA */\r | |
1563 | 0102702, /* STC 2 ; set word count mode */\r | |
1564 | 0067711, /* LDB CNT ; get word count */\r | |
1565 | 0106602, /* OTB 2 ; output word cnt to DMA */\r | |
1566 | 0106710, /* CLC CLC DC ; set command follows */\r | |
1567 | 0102501, /* LIA 1 ; load switches */\r | |
1568 | 0106501, /* LIB 1 ; register settings */\r | |
1569 | 0013712, /* AND D7 ; isolate head number */\r | |
1570 | 0005750, /* BLF,CLE,SLB ; bit 12 = 0? */\r | |
1571 | 0027762, /* JMP *+3 ; no, manual boot */\r | |
1572 | 0002002, /* SZA ; yes, RPL, head# = 0? */\r | |
1573 | 0001000, /* ALS ; no, head# = 1 --> 2 */\r | |
1574 | 0001720, /* ALF,ALS ; form cold load */\r | |
1575 | 0001000, /* ALS ; command word */\r | |
1576 | 0103706, /* STC 6,C ; activate DMA */\r | |
1577 | 0103610, /* OTA DC,C ; output cold load cmd */\r | |
1578 | 0102310, /* SFS DC ; is cold load done? */\r | |
1579 | 0027766, /* JMP *-1 ; no, wait */\r | |
1580 | 0017727, /* JSB STAT ; yes, get status */\r | |
1581 | 0060001, /* LDA 1 ; get status word #1 */\r | |
1582 | 0013715, /* AND STMS ; isolate status bits */\r | |
1583 | 0002002, /* SZA ; is transfer ok? */\r | |
1584 | 0027700, /* JMP STRT ; no, try again */\r | |
1585 | 0117710, /* JSB ADR2,I ; yes, start program */\r | |
1586 | 0000010, /* DMAC ABS DC ; DMA command word */\r | |
1587 | 0170100, /* ABS -STRT */\r | |
1588 | };\r | |
1589 | \r | |
1590 | t_stat ds_boot (int32 unitno, DEVICE *dptr)\r | |
1591 | {\r | |
1592 | int32 dev;\r | |
1593 | \r | |
1594 | if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */\r | |
1595 | dev = ds_dib.devno; /* get data chan dev */\r | |
1596 | if (ibl_copy (ds_rom, dev)) return SCPE_IERR; /* copy boot to memory */\r | |
1597 | SR = (SR & (IBL_OPT | IBL_DS_HEAD)) | IBL_DS | IBL_MAN | (dev << IBL_V_DEV);\r | |
1598 | return SCPE_OK;\r | |
1599 | }\r |