1 /* hp2100_disclib.c: HP MAC/ICD disc controller simulator library
2
3 Copyright (c) 2011-2018, J. David Bryan
4 Copyright (c) 2004-2011, Robert M. Supnik
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 Except as contained in this notice, the names of the authors shall not be
24 used in advertising or otherwise to promote the sale, use or other dealings
25 in this Software without prior written authorization from the authors.
26
27 30-Sep-18 JDB Replaced DMASK with D16_MASK
28 03-Aug-17 JDB Changed perror call for I/O errors to cprintf
29 22-Apr-17 JDB A failed sim_fseek call now causes a drive fault
30 09-Mar-17 JDB Added the simulator name to the "perror" message.
31 17-Jan-17 JDB Moved "hp2100_defs.h" inclusion to "hp2100_disclib.c"
32 13-May-16 JDB Modified for revised SCP API function parameter types
33 04-Mar-16 JDB Name changed to "hp2100_disclib" until HP 3000 integration
34 24-Dec-14 JDB Added casts for explicit downward conversions
35 27-Oct-14 JDB Corrected the relative movement calculation in start_seek
36 20-Dec-12 JDB sim_is_active() now returns t_bool
37 24-Oct-12 JDB Changed CNTLR_OPCODE to title case to avoid name clash
38 07-May-12 JDB Corrected end-of-track delay time logic
39 02-May-12 JDB First release
40 09-Nov-11 JDB Created disc controller common library from DS simulator
41
42 References:
43 - 13037 Disc Controller Technical Information Package (13037-90902, Aug-1980)
44 - HP 13365 Integrated Controller Programming Guide (13365-90901, Feb-1980)
45 - HP 1000 ICD/MAC Disc Diagnostic Reference Manual (5955-4355, Jun-1984)
46 - RTE-IVB System Manager's Manual (92068-90006, Jan-1983)
47 - DVR32 RTE Moving Head Driver source (92084-18711, Revision 5000)
48
49
50 This library provides common functions required by HP disc controllers. It
51 implements the 13037 MAC and 13365 ICD controller command sets used with the
52 7905/06/20/25 and 7906H/20H/25H disc drives.
53
54 The library is an adaptation of the code originally written by Bob Supnik
55 for the DS simulator. DS simulates a 13037 controller connected via a 13175
56 disc interface to an HP 1000 computer. To create the library, the functions
57 of the controller were separated from the functions of the interface. This
58 allows the library to work with other CPU interfaces, such as the 12821A
59 HP-IB disc interface, that use substantially different communication
60 protocols. The library functions implement the controller command set for
61 the drive units. The interface functions handle the transfer of commands and
62 data to and from the CPU.
63
64 As a result of this separation, the library does not handle the data transfer
65 between the controller and the interface directly. Instead, data is moved
66 between the interface and a sector buffer by the interface simulator, and
67 then the buffer is passed to the disc library for reading or writing. This
68 buffer is also used to pass disc commands and parameters to the controller,
69 and to receive status information from the controller. Only one buffer is
70 needed per interface, regardless of the number of controllers or units
71 handled, as a single interface cannot perform data transfers concurrently
72 with controller commands.
73
74 The library provides routines to prepare, start, and end commands, service
75 units, and poll drives for Attention status. In addition, routines are
76 provided to attach and detach disc images from drive units, load and unload
77 disc heads, classify commands, and provide opcode and phase name strings for
78 debugging.
79
80 Autosizing is supported when attaching a disc image. If enabled, the model
81 of the drive is set to match the disc image size. For example, if a 50 MB
82 disc image is attached to a unit set for autosizing, the unit's model will be
83 set to a 7920(H).
84
85 The interface simulator declares a structure that contains the state
86 variables for a controller. A MAC controller may handle multiple disc units.
87 An ICD controller handles only a single disc unit, but multiple controllers
88 may be employed to support several drives on a given interface. The type of
89 the controller (MAC or ICD) is contained in the structure, which is passed to
90 the disc library routines. The minor differences in controller action
91 between the two are handled internally. A macro (CNTLR_INIT) is provided to
92 initialize the structure.
93
94 The interface simulator also declares the sector buffer. The buffer is an
95 array containing DL_BUFSIZE 16-bit elements. The address of the buffer is
96 stored in the controller state structure. The controller maintains the
97 current index into the buffer, as well as the length of valid data stored
98 there. Other than setting the length when the controller places data into
99 the buffer and resetting the index at the start of a sector read or write,
100 the interface simulator is free to manipulate these values as desired.
101
102 In general, a user of the library is free to read any of the controller state
103 variable structure fields. Writing to the fields generally will interfere
104 with controller operations, with these exceptions:
105
106 Field Name Description
107 =========== ============================
108 status controller status
109 eod end of data flag
110 index data buffer index
111 length data buffer length
112 seek_time per-cylinder seek delay time
113 sector_time intersector delay time
114 cmd_time command response time
115 data_time data transfer response time
116 wait_time command wait time
117
118 In hardware, the controller executes in three basic states:
119
120 1. In the Poll Loop, which looks for commands and drive attention requests.
121
122 In each pass of the loop, the next CPU interface in turn is checked for a
123 command; if present, it is executed. If none are pending, all drives are
124 checked in turn until one is found with Attention status; if none are
125 found, the loop continues. If a drive is requesting attention, the
126 associated CPU interface is connected to check for a command; if present,
127 it is executed. If not, and the interface allows interrupts, an
128 interrupt request is made and the Command Wait Loop is entered. If
129 interrupts are not allowed, the Poll Loop continues.
130
131 2. In the Command Wait Loop, which looks for commands.
132
133 In each pass of the loop, the current CPU interface is checked for a
134 command; if present, it is executed. If not, the Command Wait Loop
135 continues. While in the loop, a 1.8 second timer is running. If it
136 expires before a command is received, the file mask is reset, and the
137 Poll Loop is entered.
138
139 3. In command execution, which processes the current command.
140
141 During command execution, the waits for input parameters, seek
142 completion, data transfers, and output status words are handled
143 internally. Each wait is governed by the 1.8 second timer; if it
144 expires, the command is aborted.
145
146 In simulation, these states are represented by the values cntlr_idle,
147 cntlr_wait, and cntlr_busy, respectively.
148
149 A MAC controller operates from one to eight drives, represented by an array
150 of one to eight units. When operating multiple units, a pointer to the first
151 unit of a contiguous array is passed, and the unit number present in the
152 command is used to index to the target unit.
153
154 A MAC controller emulation also requires an array of two contiguous auxiliary
155 units containing a controller unit and a command wait timeout unit. Commands
156 that do not access the drive, such as Address Record, are scheduled on the
157 controller unit to allow controller commands to execute while drive units are
158 seeking. The command wait timer limits the amount of time the controller
159 will wait for the interface to supply a command or parameter. A pointer to
160 the auxiliary unit array is set up during controller state variable
161 initialization. The auxiliary array may be separate or an extension of the
162 drive unit array.
163
164 An ICD controller manages a single unit corresponding to the drive in which
165 the controller is integrated. An interface declares a unit array
166 corresponding to the number of drives supported and passes the unit number to
167 use to the command preparation and start routines. Auxiliary units are not
168 used, and all commands are scheduled on the drive unit associated with a
169 given controller.
170
171 The library provides a unit service routine to handle all of the disc
172 commands. The routine is called from the interface service routine to handle
173 the common disc actions, while the interface routine handles actions specific
174 to the operation of the interface (such as data transfer).
175
176 The service routine schedules the unit to continue command execution under
177 these conditions:
178
179 1. A Seek or Recalibrate command is waiting for the seek completion.
180
181 2. A read or write command is waiting for the first data transfer of a
182 sector to start.
183
184 3. A read or write command is waiting for the next sector to start after
185 the final data transfer of the preceding sector.
186
187 4. A Verify command is waiting for the end of the current sector.
188
189 The library also provides controller and timer service routines for MAC
190 emulations. All three (unit, controller, and timer) must be called from
191 their respective interface service routines before any interface-specific
192 actions, if any, are taken.
193
194 On return from the library unit or controller service routines, the "wait"
195 field of the UNIT structure will be set to the activation time if the unit
196 is to be scheduled. The caller is responsible for activating the unit. If
197 the caller uses this feature, the field should be reset to zero before the
198 next service call.
199
200 The MAC timer unit is activated by the library, and its "wait" field is not
201 used. The timer starts when a command other than End, Seek, or Recalibrate
202 completes, or when the controller is waiting for the interface to supply or
203 accept a parameter during command execution. It stops when an End, Seek, or
204 Recalibrate command completes, a command is prepared for execution, or the
205 final parameter has been supplied or accepted by the interface during command
206 execution.
207
208 The controller maintains six variables in each drive's unit structure:
209
210 wait -- the current service activation time
211 pos -- the current byte offset into the disc image file
212 u3 (CYL) -- the current drive cylinder
213 u4 (STAT) -- the drive status (Status-2)
214 u5 (OP) -- the drive operation in process
215 u6 (PHASE) -- the current operation phase
216
217 These and other definitions are in the file hp_disclib.h, which must be
218 included in the interface simulator.
219
220 The controller library supports up to eight drives per MAC controller and one
221 drive per ICD controller. Unit numbers 0-7 represent valid drive addresses
222 for a MAC controller. The unit number field is ignored for an ICD
223 controller, and unit 0 is always implied. In simulation, MAC unit numbers
224 correspond one-for-one with device units, whereas one ICD controller is
225 associated with each of the several device units that are independently
226 addressed as unit 0.
227
228 The MAC controller firmware allows access to unit numbers 8-10 without
229 causing a Unit Unavailable error. Instead, the controller reports these
230 legal-but-invalid units as permanently offline.
231
232
233 Implementation notes:
234
235 1. The library does not simulate sector headers and trailers. Initialize
236 and Write Full Sector commands ignore the SPD bits and the supplied
237 header and trailer words. Read Full Sector fills in the header with the
238 current CHS address and sets the SPD bits to zero. The CRC and ECC words
239 in the trailer are returned as zeros. Programs that depend on drives
240 retaining the set values will fail.
241
242 2. The library does not simulate drive hold bits or support multiple CPU
243 interfaces connected to the same controller. CPU access to a valid drive
244 always succeeds.
245
246 3. The library does not simulate interface signals or function bus orders,
247 except for EOD (End of Data) and BUSY. The interface simulators must
248 decide for themselves what actions to take (e.g., interrupting the CPU)
249 on the basis of the controller state.
250
251 4. The command/sector buffer is an array of 16-bit elements. Byte-oriented
252 interface simulators, such as the 12821A HP-IB Disc Interface, must do
253 their own byte packing and unpacking.
254
255 5. The SAVE command does not save the "wait" and "pos" fields of the UNIT
256 structure automatically. To ensure that they are saved, they are
257 referenced by hidden, read-only registers.
258 */
259
260
261
262 #include <math.h>
263
264 #include "hp2100_defs.h" /* this must reflect the machine used */
265 #include "hp2100_disclib.h"
266
267
268
269 /* Command accessors */
270
271 #define DL_V_OPCODE 8 /* bits 12- 8: general opcode */
272 #define DL_V_HOLD 7 /* bits 7- 7: general hold flag */
273 #define DL_V_UNIT 0 /* bits 3- 0: general unit number */
274
275 #define DL_V_SPD 13 /* bits 15-13: Initialize S/P/D flags */
276 #define DL_V_CHEAD 6 /* bits 7- 6: Cold Load Read head number */
277 #define DL_V_CSECT 0 /* bits 5- 0: Cold Load Read sector number */
278 #define DL_V_FRETRY 4 /* bits 7- 4: Set File Mask retry count */
279 #define DL_V_FDECR 3 /* bits 3- 3: Set File Mask seek decrement */
280 #define DL_V_FSPEN 2 /* bits 2- 2: Set File Mask sparing enable */
281 #define DL_V_FCYLM 1 /* bits 1- 1: Set File Mask cylinder mode */
282 #define DL_V_FAUTSK 0 /* bits 0- 0: Set File Mask auto seek */
283
284 #define DL_V_FMASK 0 /* bits 3- 0: Set File Mask (flags combined) */
285
286
287 #define DL_M_OPCODE 037 /* opcode mask */
288 #define DL_M_UNIT 017 /* unit mask */
289
290 #define DL_M_SPD 007 /* S/P/D flags mask */
291 #define DL_M_CHEAD 003 /* Cold Load Read head number mask */
292 #define DL_M_CSECT 077 /* Cold Load Read sector number mask */
293 #define DL_M_FRETRY 017 /* Set File Mask retry count mask */
294 #define DL_M_FMASK 017 /* Set File Mask flags mask */
295
296
297 #define GET_OPCODE(c) (CNTLR_OPCODE) (((c) >> DL_V_OPCODE) & DL_M_OPCODE)
298 #define GET_UNIT(c) (((c) >> DL_V_UNIT) & DL_M_UNIT)
299
300 #define GET_SPD(c) (((c) >> DL_V_SPD) & DL_M_SPD)
301 #define GET_CHEAD(c) (((c) >> DL_V_CHEAD) & DL_M_CHEAD)
302 #define GET_CSECT(c) (((c) >> DL_V_CSECT) & DL_M_CSECT)
303 #define GET_FRETRY(c) (((c) >> DL_V_FRETRY) & DL_M_FRETRY)
304 #define GET_FMASK(c) (((c) >> DL_V_FMASK) & DL_M_FMASK)
305
306 #define DL_FDECR (1 << DL_V_FDECR)
307 #define DL_FSPEN (1 << DL_V_FSPEN)
308 #define DL_FCYLM (1 << DL_V_FCYLM)
309 #define DL_FAUTSK (1 << DL_V_FAUTSK)
310
311
312 /* Parameter accessors */
313
314 #define DL_V_HEAD 8 /* bits 12- 8: head number */
315 #define DL_V_SECTOR 0 /* bits 7- 0: sector number */
316
317 #define DL_M_HEAD 0017 /* head number mask */
318 #define DL_M_SECTOR 0377 /* sector number mask */
319
320 #define GET_HEAD(p) (((p) >> DL_V_HEAD) & DL_M_HEAD)
321 #define GET_SECTOR(p) (((p) >> DL_V_SECTOR) & DL_M_SECTOR)
322
323 #define SET_HEAD(c) (uint16) (((c)->head & DL_M_HEAD) << DL_V_HEAD)
324 #define SET_SECTOR(c) (uint16) (((c)->sector & DL_M_SECTOR) << DL_V_SECTOR)
325
326
327 /* Drive properties table.
328
329 In hardware, drives report their Drive Type numbers to the controller upon
330 receipt of a Request Status tag bus command. The drive type is used to
331 determine the legal range of head and sector addresses (the drive itself will
332 validate the cylinder address during seeks).
333
334 In simulation, we set up a table of drive properties and use the model ID as
335 an index into the table. The table is used to validate seek parameters and
336 to provide the mapping between CHS addresses and the linear byte addresses
337 required by the host file access routines.
338
339 The 7905/06(H) drives consist of removable and fixed platters, whereas the
340 7920(H)/25(H) drives have only removable multi-platter packs. As a result,
341 7905/06 drives are almost always accessed in platter mode, i.e., a given
342 logical disc area is fully contained on either the removable or fixed
343 platter, whereas the 7920/25 drives are almost always accessed in cylinder
344 mode with logical disc areas spanning some or all of the platters.
345
346 Disc image files are arranged as a linear set of tracks. To improve
347 locality of access, tracks in the 7905/06 images are grouped per-platter,
348 whereas tracks on the 7920 and 7925 are sequential by cylinder and head
349 number.
350
351 The simulator maps the tracks on the 7905/06 removable platter (heads 0 and
352 1) to the first half of the disc image, and the tracks on the fixed platter
353 (heads 2 and, for the 7906 only, 3) to the second half of the image. For the
354 7906(H), the cylinder-head order of the tracks is 0-0, 0-1, 1-0, 1-1, ...,
355 410-0, 410-1, 0-2, 0-3, 1-2, 1-3, ..., 410-2, 410-3. The 7905 order is the
356 same, except that head 3 tracks are omitted.
357
358 For the 7920(H)/25(H), all tracks appear in cylinder-head order, e.g., 0-0,
359 0-1, 0-2, 0-3, 0-4, 1-0, 1-1, ..., 822-2, 822-3, 822-4 for the 7920(H).
360
361 This variable-access geometry is accomplished by defining additional "heads
362 per cylinder" values for the fixed and removable sections of each drive that
363 indicates the number of heads that should be grouped for locality. The
364 removable values are set to 2 on the 7905 and 7906, indicating that those
365 drives typically use cylinders consisting of two heads. They are set to the
366 number of heads per drive for the 7920 and 7925, as those typically use
367 cylinders encompassing the entire pack.
368 */
369
370 #define D7905_RH 2
371 #define D7905_FH (D7905_HEADS - D7905_RH)
372
373 #define D7906_RH 2
374 #define D7906_FH (D7906_HEADS - D7906_RH)
375
376 #define D7920_RH D7920_HEADS
377 #define D7920_FH (D7920_HEADS - D7920_RH)
378
379 #define D7925_RH D7925_HEADS
380 #define D7925_FH (D7925_HEADS - D7925_RH)
381
382 typedef struct {
383 uint32 sectors; /* sectors per head */
384 uint32 heads; /* heads per cylinder*/
385 uint32 cylinders; /* cylinders per drive */
386 uint32 words; /* words per drive */
387 uint32 type; /* drive type */
388 uint32 remov_heads; /* number of removable-platter heads */
389 uint32 fixed_heads; /* number of fixed-platter heads */
390 } DRIVE_PROPERTIES;
391
392
393 static const DRIVE_PROPERTIES drive_props [] = {
394 { D7905_SECTS, D7905_HEADS, D7905_CYLS, D7905_WORDS, D7905_TYPE, D7905_RH, D7905_FH },
395 { D7906_SECTS, D7906_HEADS, D7906_CYLS, D7906_WORDS, D7906_TYPE, D7906_RH, D7906_FH },
396 { D7920_SECTS, D7920_HEADS, D7920_CYLS, D7920_WORDS, D7920_TYPE, D7920_RH, D7920_FH },
397 { D7925_SECTS, D7925_HEADS, D7925_CYLS, D7925_WORDS, D7925_TYPE, D7925_RH, D7925_FH }
398 };
399
400 #define PROPS_COUNT (sizeof (drive_props) / sizeof (drive_props [0]))
401
402
403 /* Convert a CHS address to a block offset.
404
405 A cylinder/head/sector address is converted into a linear block address that
406 may be used to calculate a byte offset to pass to the file access routines.
407 The conversion logic is:
408
409 if Head < removable_heads_per_cylinder then
410 tracks := Cylinder * removable_heads_per_cylinder + Head;
411 else
412 tracks := cylinders_per_drive * removable_heads_per_cylinder +
413 Cylinder * fixed_heads_per_cylinder + (Head - removable_heads_per_cylinder);
414
415 block := tracks * sectors_per_track + Sector;
416
417 byte_offset := block * words_per_sector * bytes_per_word;
418
419 The byte offset is calculated in two steps to allow for future controller
420 enhancements to support the CS/80 command set and its associated linear block
421 addressing mode.
422 */
423
424 #define TO_BLOCK(cylinder,head,sector,model) \
425 (((head) < drive_props [model].remov_heads \
426 ? (cylinder) * drive_props [model].remov_heads + (head) \
427 : drive_props [model].cylinders * drive_props [model].remov_heads \
428 + ((cylinder) * drive_props [model].fixed_heads + (head) - drive_props [model].remov_heads)) \
429 * drive_props [model].sectors + (sector))
430
431 #define TO_OFFSET(block) ((block) * DL_WPSEC * sizeof (uint16))
432
433
434 /* Estimate the current sector.
435
436 The sector currently passing under the disc heads is estimated from the
437 current simulator time (i.e., the count of instructions since startup) and
438 the simulated disc rotation time. The computation logic is:
439
440 per_sector_time := word_transfer_time * words_per_sector + intersector_time;
441
442 current_sector := (current_time / per_sector_time) MOD sectors_per_track;
443 */
444
445 #define GET_CURSEC(cvptr,uptr) \
446 ((uint16) fmod (sim_gtime() / (double) ((cvptr->data_time * DL_WPSEC + cvptr->sector_time)), \
447 (double) drive_props [GET_MODEL (uptr->flags)].sectors))
448
449
450 /* Command properties table.
451
452 The validity of each command for a specified controller type is checked
453 against the command properties table when it is prepared. The table also
454 includes the count of inbound and outbound properties, the class of the
455 command, and flags to indicate certain common actions that should be taken.
456 */
457
458 typedef struct {
459 uint32 params_in; /* count of input parameters */
460 uint32 params_out; /* count of output parameters */
461 CNTLR_CLASS classification; /* command classification */
462 t_bool valid [type_count]; /* per-type command validity */
463 t_bool clear_status; /* command clears the controller status */
464 t_bool unit_field; /* command has a unit field */
465 t_bool unit_check; /* command checks the unit number validity */
466 t_bool unit_access; /* command accesses the drive unit */
467 t_bool seek_wait; /* command waits for seek completion */
468 } DS_PROPS;
469
470 typedef const DS_PROPS *PRPTR;
471
472 #define T TRUE
473 #define F FALSE
474
475 static const DS_PROPS cmd_props [] = {
476 /* par par opcode valid for clear unit unit unit seek */
477 /* in out classification MAC ICD stat field check acces wait */
478 { 0, 0, class_read, { T, T }, T, F, T, T, F }, /* 00 = cold load read */
479 { 0, 0, class_control, { T, T }, T, T, T, T, T }, /* 01 = recalibrate */
480 { 2, 0, class_control, { T, T }, T, T, T, T, F }, /* 02 = seek */
481 { 0, 2, class_status, { T, T }, F, T, F, F, F }, /* 03 = request status */
482 { 0, 1, class_status, { T, T }, T, T, T, F, F }, /* 04 = request sector address */
483 { 0, 0, class_read, { T, T }, T, T, T, T, T }, /* 05 = read */
484 { 0, 0, class_read, { T, T }, T, T, T, T, T }, /* 06 = read full sector */
485 { 1, 0, class_read, { T, T }, T, T, T, T, T }, /* 07 = verify */
486 { 0, 0, class_write, { T, T }, T, T, T, T, T }, /* 10 = write */
487 { 0, 0, class_write, { T, T }, T, T, T, T, T }, /* 11 = write full sector */
488 { 0, 0, class_control, { T, T }, T, F, F, F, F }, /* 12 = clear */
489 { 0, 0, class_write, { T, T }, T, T, T, T, T }, /* 13 = initialize */
490 { 2, 0, class_control, { T, T }, T, F, F, F, F }, /* 14 = address record */
491 { 0, 7, class_status, { T, F }, F, F, F, F, F }, /* 15 = request syndrome */
492 { 1, 0, class_read, { T, T }, T, T, T, T, T }, /* 16 = read with offset */
493 { 0, 0, class_control, { T, T }, T, F, F, F, F }, /* 17 = set file mask */
494 { 0, 0, class_invalid, { F, F }, T, F, F, F, F }, /* 20 = invalid */
495 { 0, 0, class_invalid, { F, F }, T, F, F, F, F }, /* 21 = invalid */
496 { 0, 0, class_read, { T, T }, T, T, T, T, T }, /* 22 = read without verify */
497 { 1, 0, class_status, { T, F }, T, F, F, F, F }, /* 23 = load TIO register */
498 { 0, 2, class_status, { T, T }, F, F, F, F, F }, /* 24 = request disc address */
499 { 0, 0, class_control, { T, T }, T, F, F, F, F }, /* 25 = end */
500 { 0, 0, class_control, { T, F }, T, T, T, F, F } /* 26 = wakeup */
501 };
502
503
504 /* Auxiliary unit indices */
505
506 typedef enum {
507 controller = 0, /* controller unit index */
508 timer /* command wait timer index */
509 } AUX_INDEX;
510
511
512 /* Controller opcode names */
513
514 static const char invalid_name [] = "invalid";
515
516 static const char * const opcode_name [] = {
517 "cold load read", /* 00 */
518 "recalibrate", /* 01 */
519 "seek", /* 02 */
520 "request status", /* 03 */
521 "request sector address", /* 04 */
522 "read", /* 05 */
523 "read full sector", /* 06 */
524 "verify", /* 07 */
525 "write", /* 10 */
526 "write full sector", /* 11 */
527 "clear", /* 12 */
528 "initialize", /* 13 */
529 "address record", /* 14 */
530 "request syndrome", /* 15 */
531 "read with offset", /* 16 */
532 "set file mask", /* 17 */
533 invalid_name, /* 20 = invalid */
534 invalid_name, /* 21 = invalid */
535 "read without verify", /* 22 */
536 "load TIO register", /* 23 */
537 "request disc address", /* 24 */
538 "end", /* 25 */
539 "wakeup" /* 26 */
540 };
541
542 /* Controller phase names */
543
544 static const char * const phase_name [] = {
545 "start",
546 "data",
547 "end"
548 };
549
550
551
552 /* Disc library local controller routines */
553
554 static t_bool start_seek (CVPTR cvptr, UNIT *uptr, CNTLR_OPCODE next_opcode, CNTLR_PHASE next_phase);
555 static t_stat start_read (CVPTR cvptr, UNIT *uptr);
556 static void end_read (CVPTR cvptr, UNIT *uptr);
557 static void start_write (CVPTR cvptr, UNIT *uptr);
558 static t_stat end_write (CVPTR cvptr, UNIT *uptr);
559 static t_bool position_sector (CVPTR cvptr, UNIT *uptr, t_bool verify);
560 static void next_sector (CVPTR cvptr, UNIT *uptr);
561 static t_stat io_error (CVPTR cvptr, UNIT *uptr, CNTLR_STATUS status);
562
563 /* Disc library local utility routines */
564
565 static void set_address (CVPTR cvptr, uint32 index);
566 static void set_timer (CVPTR cvptr, FLIP_FLOP action);
567 static uint16 drive_status (UNIT *uptr);
568
569
570
571 /* Disc library global controller routines */
572
573
574 /* Prepare a command for execution.
575
576 On entry, the first word of the controller buffer contains the command to
577 prepare, the "cvptr" parameter points at the controller state variable
578 structure, and the "units" parameter points at the first unit of the unit
579 array. For a MAC controller, the "unit limit" parameter indicates the last
580 valid unit number, and the unit to use is taken from the unit field of the
581 command word. For an ICD controller, the parameter indicates the number
582 of the unit to use directly.
583
584 If a valid command was prepared for execution, the routine returns TRUE and
585 sets the controller state to "busy." If the command is illegal, the routine
586 returns FALSE and sets the controller state to "waiting." In the latter
587 case, the controller status will indicate the reason for the rejection.
588
589 The opcode and unit number (for MAC controllers) are obtained from the buffer
590 and checked for legality. If either is illegal, the controller status is set
591 appropriately, and the routine returns FALSE.
592
593 For a valid command and an available unit, the controller's opcode field is
594 set from the buffer, the length field is set to the number of inbound
595 parameter words expected, and the index field is set to 1 to point at the
596 first parameter entry in the buffer.
597 */
598
dl_prepare_command(CVPTR cvptr,UNIT * units,uint32 unit_limit)599 t_bool dl_prepare_command (CVPTR cvptr, UNIT *units, uint32 unit_limit)
600 {
601 uint32 unit;
602 PRPTR props;
603 CNTLR_OPCODE opcode;
604
605 set_timer (cvptr, CLEAR); /* stop the command wait timer */
606
607 opcode = GET_OPCODE (cvptr->buffer [0]); /* get the opcode from the command */
608
609 if (opcode > Last_Opcode) /* is the opcode invalid? */
610 props = &cmd_props [Invalid_Opcode]; /* undefined commands clear prior status */
611 else /* the opcode is potentially valid */
612 props = &cmd_props [opcode]; /* get the command properties */
613
614 if (cvptr->type == MAC) /* is this a MAC controller? */
615 if (props->unit_field) /* is the unit field defined for this command? */
616 unit = GET_UNIT (cvptr->buffer [0]); /* get the unit from the command */
617 else /* no unit specified in the command */
618 unit = 0; /* so the unit is always unit 0 */
619
620 else /* an ICD controller */
621 unit = unit_limit; /* uses the supplied unit number */
622
623 if (props->clear_status) { /* clear the prior controller status */
624 cvptr->status = normal_completion; /* if indicated for this command */
625 cvptr->spd_unit = SET_S1UNIT (unit); /* save the unit number for status requests */
626 }
627
628 if (cvptr->type <= last_type /* is the controller type legal, */
629 && props->valid [cvptr->type]) /* and the opcode defined for this controller? */
630 if (props->unit_check && unit > DL_MAXUNIT) /* if the unit number is checked and is illegal, */
631 dl_end_command (cvptr, unit_unavailable); /* end with a unit unavailable error */
632
633 else {
634 cvptr->state = cntlr_busy; /* legal unit, so controller is now busy */
635 cvptr->opcode = opcode; /* save the controller opcode */
636 cvptr->length = props->params_in; /* set the inbound parameter count */
637 cvptr->index = 1; /* point at the first parameter element (if any) */
638
639 if (cvptr->type == MAC && cvptr->length) { /* is this a MAC controller with inbound parameters? */
640 cvptr->aux [controller].OP = opcode; /* save the opcode */
641 cvptr->aux [controller].PHASE = data_phase; /* and set the phase for parameter pickup */
642 set_timer (cvptr, SET); /* start the timer to wait for the first parameter */
643 }
644
645 return TRUE; /* the command is now prepared for execution */
646 }
647
648 else /* the opcode is undefined */
649 dl_end_command (cvptr, illegal_opcode); /* so set bad opcode status */
650
651 return FALSE; /* the preparation has failed */
652 }
653
654
655 /* Start a command.
656
657 On entry, the controller's opcode field contains the command to start, and
658 the buffer contains the command word in element 0 and the parameters required
659 by the command, if any, beginning in element 1. The call parameters are the
660 same as those supplied to the "prepare command" routine.
661
662 If the command was started successfully, the routine returns a pointer to the
663 unit to be activated and sets that unit's "wait" field to the activation
664 time. The caller should activate the unit upon return to complete or
665 continue command processing. If the command did not start, the routine
666 returns NULL.
667
668 If a seek is in progress on a drive when a command accessing that drive is
669 started, the unit pointer is returned but the unit's "wait" field is set to
670 zero. In this case, the unit must not be activated (as it already is).
671 Instead, the unit's opcode and phase fields will have been set to start the
672 command automatically when the seek completes.
673
674 For commands that return status from the controller, the buffer will contain
675 the returned value(s), the buffer index will be zero, and the buffer length
676 will be set to the number of words returned in the buffer. These words must
677 be returned to the CPU via the interface.
678
679
680 Implementation notes:
681
682 1. A command must have been prepared by calling dl_prepare_command first.
683 After preparation, the controller's opcode will be valid, and the unit
684 number field will be legal (but not necessarily valid) for those commands
685 that check the unit.
686
687 Unit numbers 0-7 represent valid drive addresses. However, the MAC
688 controller firmware allows access to unit numbers 8-10 without causing a
689 Unit Unavailable error. Instead, the controller reports these units as
690 permanently offline.
691
692 2. Commands that check for a valid unit do some processing before failing
693 with a Status-2 (not ready) error if the unit is invalid. For example,
694 the Seek command accepts its parameters from the CPU and sets the CHS
695 values into the controller before failing.
696
697 3. In hardware, read, write, and recalibrate commands wait in an internal
698 loop for a pending seek completion and clear the resulting Attention
699 status before executing. In simulation, we change a seeking drive unit's
700 opcode and phase fields from seek completion to the start of the next
701 command. This eliminates the setting of the Attention status and begins
702 command execution automatically when the seek completes.
703
704 If the seek completed between the command preparation and start,
705 Attention will have been set. If the unit is idle on entry, we clear the
706 Attention status unilaterally (it doesn't matter whether or not it was
707 set; Attention always is clear when commands start).
708
709 4. The Seek and Cold Load Read commands do not check for a seek or
710 recalibrate in progress. If the heads are moving, the drive will reject
711 a seek command with a Seek Check error. The firmware does not test
712 explicitly for Access Not Ready before executing the command, so the
713 parameters (e.g., controller CHS addresses) are still set as though the
714 command had succeeded.
715
716 A Seek command will return to the Poll Loop with Seek Check status set.
717 When the seek in progress completes, the controller will interrupt with
718 Drive Attention status. The controller address will differ from the
719 drive address, so it's incumbent upon the caller to issue a Request
720 Status command after the seek, which will return Status-2 Error status.
721
722 A Cold Load Read command issues a seek to cylinder 0 and then begins a
723 read, which first waits for seek completion. The Seek Check error will
724 abort the command at this point with Status-2 Error status.
725
726 In simulation, a Seek command allows the seek in progress to complete
727 normally, whereas a Cold Load Read command modifies the unit command
728 and phase from the end phase of Seek or Recalibrate to the start
729 phase of Read, which will catch the Seek Check error as in hardware.
730
731 5. The Cold Load Read command checks if the drive is ready before setting
732 the file mask. Therefore, we normally defer setting the file mask until
733 the unit service is called. However, if a seek is in progress, then the
734 drive must be ready, so we set the file mask here.
735
736 6. ECC is not simulated, so the Request Syndrome command always returns zero
737 values for the displacement and patterns.
738
739 7. The Request Status, Request Sector Address, and Wakeup commands reference
740 drive units but are scheduled on the controller unit because they may be
741 issued while a drive is processing a seek.
742
743 8. The activation time is set to the intersector time (latency) for read and
744 write commands, and to the controller processing time for all others.
745 The read/write start time cannot be shorter than 20 instructions, or
746 DVR32 will be unable to start DCPC in time to avoid an over/underrun.
747 */
748
dl_start_command(CVPTR cvptr,UNIT * units,uint32 unit_limit)749 UNIT *dl_start_command (CVPTR cvptr, UNIT *units, uint32 unit_limit)
750 {
751 UNIT *uptr, *rptr;
752 uint32 unit;
753 PRPTR props;
754 t_bool is_seeking = FALSE;
755
756 props = &cmd_props [cvptr->opcode]; /* get the command properties */
757
758 if (cvptr->type == MAC) { /* is this a MAC controller? */
759 if (props->unit_field) /* is the unit field defined for this command? */
760 unit = GET_UNIT (cvptr->buffer [0]); /* get the unit number from the command */
761 else /* no unit is specified in the command */
762 unit = 0; /* so the unit number defaults to 0 */
763
764 if (unit > unit_limit) /* if the unit number is invalid, */
765 uptr = NULL; /* it does not correspond to a unit */
766 else if (props->unit_access) /* if the command accesses a drive, */
767 uptr = units + unit; /* get the address of the unit */
768 else /* the command accesses the controller only */
769 uptr = cvptr->aux + controller; /* so use the controller unit */
770 }
771
772 else { /* for an ICD controller, */
773 unit = 0; /* the unit value is ignored */
774 uptr = units + unit_limit; /* and we use the indicated unit */
775 }
776
777 if (props->unit_check && !uptr /* if the unit number is checked and is invalid */
778 || props->seek_wait && (drive_status (uptr) & DL_S2STOPS)) { /* or if we're waiting for an offline drive */
779 dl_end_command (cvptr, status_2_error); /* then the command ends with a Status-2 error */
780 uptr = NULL; /* prevent the command from starting */
781 }
782
783 else if (uptr) { /* otherwise, we have a valid unit */
784 uptr->wait = cvptr->cmd_time; /* most commands use the command delay */
785
786 if (props->unit_access) { /* does the command access the unit? */
787 is_seeking = sim_is_active (uptr); /* see if the unit is busy */
788
789 if (is_seeking) /* if a seek is in progress, */
790 uptr->wait = 0; /* set for no unit activation */
791
792 else { /* otherwise, the unit is idle */
793 uptr->STAT &= ~DL_S2ATN; /* clear the drive Attention status */
794
795 if (props->classification == class_read /* if a read command */
796 || props->classification == class_write) /* or a write command */
797 uptr->wait = cvptr->sector_time; /* schedule the sector start latency */
798 }
799 }
800 }
801
802 cvptr->index = 0; /* reset the buffer index */
803 cvptr->length = props->params_out; /* set the count of outbound parameters */
804 cvptr->eod = CLEAR; /* clear the end of data flag */
805
806
807 switch (cvptr->opcode) { /* dispatch the command */
808
809 case Cold_Load_Read:
810 cvptr->cylinder = 0; /* set the cylinder address to 0 */
811 cvptr->head = GET_CHEAD (cvptr->buffer [0]); /* set the head */
812 cvptr->sector = GET_CSECT (cvptr->buffer [0]); /* and sector from the command */
813
814 if (is_seeking) { /* if a seek is in progress, */
815 uptr->STAT |= DL_S2SC; /* a Seek Check occurs */
816 cvptr->file_mask = DL_FSPEN; /* enable sparing */
817 uptr->OP = Read; /* start the read on the seek completion */
818 uptr->PHASE = start_phase; /* and reset the command phase */
819 return uptr; /* to allow the seek to complete normally */
820 }
821
822 else /* the drive is not seeking */
823 uptr->wait = cvptr->cmd_time; /* the command starts with a seek, not a read */
824
825 break;
826
827
828 case Seek:
829 cvptr->cylinder = cvptr->buffer [1]; /* get the supplied cylinder */
830 cvptr->head = GET_HEAD (cvptr->buffer [2]); /* and head */
831 cvptr->sector = GET_SECTOR (cvptr->buffer [2]); /* and sector addresses */
832
833 if (is_seeking) { /* if a seek is in progress, */
834 uptr->STAT |= DL_S2SC; /* a Seek Check occurs */
835 dl_idle_controller (cvptr); /* return the controller to the idle condition */
836 return uptr; /* to allow the seek to complete normally */
837 }
838
839 break;
840
841
842 case Request_Status:
843 cvptr->buffer [0] = (uint16) (cvptr->spd_unit /* set the Status-1 value */
844 | SET_S1STAT (cvptr->status)); /* into the buffer */
845
846 if (cvptr->type == MAC) /* is this a MAC controller? */
847 if (unit > unit_limit) /* if the unit number is invalid */
848 rptr = NULL; /* it does not correspond to a unit */
849 else /* otherwise, the unit is valid */
850 rptr = &units [unit]; /* so get the address of the referenced unit */
851 else /* if not a MAC controller */
852 rptr = uptr; /* then the referenced unit is the current unit */
853
854 cvptr->buffer [1] = drive_status (rptr); /* set the Status-2 value */
855
856 if (rptr) /* if the unit is valid */
857 rptr->STAT &= ~DL_S2FS; /* clear the First Status bit */
858
859 cvptr->spd_unit = SET_S1UNIT (unit); /* save the unit number */
860
861 if (unit > DL_MAXUNIT) /* if the unit number is illegal, */
862 cvptr->status = unit_unavailable; /* the next status will be Unit Unavailable */
863 else /* a legal unit */
864 cvptr->status = normal_completion; /* clears the controller status */
865
866 break;
867
868
869 case Request_Disc_Address:
870 set_address (cvptr, 0); /* return the CHS values in buffer 0-1 */
871 break;
872
873
874 case Request_Sector_Address:
875 if (unit > unit_limit) /* if the unit number is invalid */
876 rptr = NULL; /* it does not correspond to a unit */
877 else /* otherwise, the unit is valid */
878 rptr = &units [unit]; /* so get the address of the referenced unit */
879
880 if (drive_status (rptr) & DL_S2NR) /* if the drive is not ready, */
881 dl_end_command (cvptr, status_2_error); /* terminate with not ready status */
882 else /* otherwise, the drive is ready */
883 cvptr->buffer [0] = GET_CURSEC (cvptr, rptr); /* so calculate the current sector address */
884 break;
885
886
887 case Request_Syndrome:
888 cvptr->buffer [0] = (uint16) (cvptr->spd_unit /* return the Status-1 value */
889 | SET_S1STAT (cvptr->status)); /* in buffer 0 */
890
891 set_address (cvptr, 1); /* return the CHS values in buffer 1-2 */
892
893 cvptr->buffer [3] = 0; /* the displacement is always zero */
894 cvptr->buffer [4] = 0; /* the syndrome is always zero */
895 cvptr->buffer [5] = 0;
896 cvptr->buffer [6] = 0;
897 break;
898
899
900 case Address_Record:
901 cvptr->cylinder = cvptr->buffer [1]; /* get the supplied cylinder */
902 cvptr->head = GET_HEAD (cvptr->buffer [2]); /* and head */
903 cvptr->sector = GET_SECTOR (cvptr->buffer [2]); /* and sector addresses */
904 cvptr->eoc = CLEAR; /* clear the end-of-cylinder flag */
905 break;
906
907
908 case Set_File_Mask:
909 cvptr->file_mask = GET_FMASK (cvptr->buffer [0]); /* get the supplied file mask */
910
911 if (cvptr->type == MAC) /* if this is a MAC controller, */
912 cvptr->retry = GET_FRETRY (cvptr->buffer [0]); /* the retry count is supplied too */
913 break;
914
915
916 case Initialize:
917 if (uptr) /* if the unit is valid, */
918 cvptr->spd_unit |= /* merge the SPD flags */
919 SET_S1SPD (GET_SPD (cvptr->buffer [0])); /* from the command word */
920 break;
921
922
923 case Verify:
924 cvptr->verify_count = cvptr->buffer [1]; /* get the supplied sector count */
925 break;
926
927
928 default: /* the remaining commands */
929 break; /* are handled by the service routines */
930 }
931
932
933 if (uptr) { /* if the command accesses a valid unit */
934 uptr->OP = cvptr->opcode; /* save the opcode in the unit */
935
936 if (cvptr->length) /* if the command has outbound parameters, */
937 uptr->PHASE = data_phase; /* set up the data phase for the transfer */
938 else /* if there are no parameters, */
939 uptr->PHASE = start_phase; /* set up the command phase for execution */
940
941 return uptr; /* return a pointer to the scheduled unit */
942 }
943
944 else
945 return NULL; /* the command did not start */
946 }
947
948
949 /* Complete a command.
950
951 The current command is completed with the indicated status. The command
952 result status is set, the controller enters the command wait state, and the
953 CPU timer is restarted.
954 */
955
dl_end_command(CVPTR cvptr,CNTLR_STATUS status)956 void dl_end_command (CVPTR cvptr, CNTLR_STATUS status)
957 {
958 cvptr->status = status; /* set the command result status */
959 cvptr->state = cntlr_wait; /* set the controller state to waiting */
960 set_timer (cvptr, SET); /* start the command wait timer */
961 return;
962 }
963
964
965 /* Poll the drives for Attention status.
966
967 If interrupts are enabled on the interface, this routine is called to check
968 if any drive is requesting attention. The routine returns TRUE if a drive is
969 requesting attention and FALSE if not.
970
971 Starting with the last unit requesting attention, each drive is checked in
972 sequence. If a drive has its Attention status set, the controller saves its
973 unit number, sets the result status to Drive Attention, and enters the
974 command wait state. The routine returns TRUE to indicate that an interrupt
975 should be generated. The next time the routine is called, the poll begins
976 with the last unit that requested attention, so that each unit is given an
977 equal chance to respond.
978
979 If no unit is requesting attention, the routine returns FALSE to indicate
980 that no interrupt should be generated.
981 */
982
dl_poll_drives(CVPTR cvptr,UNIT * units,uint32 unit_limit)983 t_bool dl_poll_drives (CVPTR cvptr, UNIT *units, uint32 unit_limit)
984 {
985 uint32 unit;
986
987 for (unit = 0; unit <= unit_limit; unit++) { /* check each unit in turn */
988 cvptr->poll_unit = /* start with the last unit checked */
989 (cvptr->poll_unit + 1) % (unit_limit + 1); /* and cycle back to unit 0 */
990
991 if (units [cvptr->poll_unit].STAT & DL_S2ATN) { /* if the unit is requesting attention, */
992 units [cvptr->poll_unit].STAT &= ~DL_S2ATN; /* clear the Attention status */
993 cvptr->spd_unit = SET_S1UNIT (cvptr->poll_unit); /* set the controller's unit number */
994 cvptr->status = drive_attention; /* and status */
995 cvptr->state = cntlr_wait; /* and wait for a command */
996 return TRUE; /* tell the caller to interrupt */
997 }
998 }
999
1000 return FALSE; /* no requests, so do not generate an interrupt */
1001 }
1002
1003
1004 /* Service the disc drive unit.
1005
1006 The unit service routine is called to execute scheduled controller commands
1007 for the specified unit. The actions to be taken depend on the current state
1008 of the controller and the unit.
1009
1010 In addition to the controller state variables supplied in the call, the
1011 service routine accesses these six variables in the UNIT structure:
1012
1013 wait -- the current service activation time
1014 pos -- the current byte offset into the disc image file
1015 u3 (CYL) -- the current drive cylinder
1016 u4 (STAT) -- the drive status (Status-2)
1017 u5 (OP) -- the drive operation in process
1018 u6 (PHASE) -- the current operation phase
1019
1020 The activation time is set non-zero if the service should be rescheduled.
1021 The caller is responsible upon return for activating the unit. The file
1022 offset indicates the byte position in the disc image file for the next read
1023 or write operation.
1024
1025 The drive cylinder gives the current location of the head positioner. This
1026 may differ from the cylinder value in the controller if the Address Record
1027 command has been used. The drive status maintains various per-drive
1028 conditions (e.g., the state of the read-only and format switches, drive
1029 ready, first status). The operation in process and operation phase define
1030 the action to be taken by this service routine.
1031
1032 Initially, the operation in process is set to the opcode field of the command
1033 when it is started. However, the operation in process may change during
1034 execution (the controller opcode never does). This is to aid code reuse in
1035 the service routine. For example, a Cold Load Read command is changed to a
1036 Read command once the seek portion is complete, and a Read Without Verify
1037 command is changed to a normal Read command after a track boundary is
1038 crossed.
1039
1040 The operation phase provides different substates for those commands that
1041 transfer data or that have different starting and ending actions. Three
1042 phases are defined: start, data, and end. Commands that do not transfer data
1043 to or from the CPU interface do not have data phases, and commands that
1044 complete upon first service do not have end phases. The service routine
1045 validates phase assignments and returns SCPE_IERR (Internal Error) if entry
1046 is made with an illegal operation phase or a phase that is not valid for a
1047 given operation.
1048
1049 An operation in the data phase is in the process of transferring data between
1050 the CPU and sector buffer. Because this process is interface-specific, the
1051 service routine does nothing (other than validate) in this phase. It is up
1052 to the caller to transition from the data phase to the end phase when the
1053 transfer is complete.
1054
1055 If an operation is completed, or an error has occurred, the controller state
1056 on return will be either idle or waiting, instead of busy. The caller should
1057 check the controller status to determine if normal completion or error
1058 recovery is appropriate.
1059
1060 If the command is continuing, the service activation time will be set
1061 appropriately. The caller should then call sim_activate to schedule the next
1062 service and clear the "wait" field in preparation for the next service call.
1063
1064
1065 Implementation notes:
1066
1067 1. The Cold Load Read and Seek commands check only the drive's Not Ready
1068 status because seeking clears a Seek Check. The other commands that
1069 access the unit (e.g., Read and Write) have already checked in the
1070 command start routine for Not Ready, Seek Check, or Fault status and
1071 terminated with a Status-2 error.
1072
1073 2. Several commands (e.g., Set File Mask, Address Record) are executed
1074 completely within the dl_start_command routine, so all we do here is
1075 finish the command with the expected status. The service routine is
1076 called only to provide the proper command execution delay.
1077
1078 3. If a host file system error occurs, the service routine returns SCPE_IERR
1079 to stop simulation. If simulation is resumed, the controller will behave
1080 as though an uncorrectable data error had occurred.
1081 */
1082
dl_service_drive(CVPTR cvptr,UNIT * uptr)1083 t_stat dl_service_drive (CVPTR cvptr, UNIT *uptr)
1084 {
1085 t_stat result = SCPE_OK;
1086 const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP;
1087
1088 switch ((CNTLR_PHASE) uptr->PHASE) { /* dispatch the phase */
1089
1090 case start_phase:
1091 switch (opcode) { /* dispatch the current operation */
1092
1093 case Recalibrate:
1094 case Seek:
1095 if (start_seek (cvptr, uptr, opcode, end_phase) /* start the seek; if it succeeded, */
1096 && (cvptr->type == MAC)) /* and this a MAC controller, */
1097 dl_idle_controller (cvptr); /* then go idle until it completes */
1098 break;
1099
1100
1101 case Cold_Load_Read:
1102 if (start_seek (cvptr, uptr, Read, start_phase)) /* start the seek; did it succeed? */
1103 cvptr->file_mask = DL_FSPEN; /* set sparing enabled now */
1104 break;
1105
1106
1107 case Read:
1108 case Read_With_Offset:
1109 case Read_Without_Verify:
1110 cvptr->length = DL_WPSEC; /* transfer just the data */
1111 result = start_read (cvptr, uptr); /* start the sector read */
1112 break;
1113
1114
1115 case Read_Full_Sector:
1116 cvptr->length = DL_WPFSEC; /* transfer the header/data/trailer */
1117 result = start_read (cvptr, uptr); /* start the sector read */
1118 break;
1119
1120
1121 case Verify:
1122 cvptr->length = 0; /* no data transfer needed */
1123 result = start_read (cvptr, uptr); /* start the sector read */
1124
1125 if (uptr->PHASE == data_phase) { /* did the read start successfully? */
1126 uptr->PHASE = end_phase; /* skip the data phase */
1127 uptr->wait = cvptr->sector_time /* reschedule for the intersector time */
1128 + cvptr->data_time * DL_WPSEC; /* plus the data read time */
1129 }
1130 break;
1131
1132
1133 case Write:
1134 case Initialize:
1135 cvptr->length = DL_WPSEC; /* transfer just the data */
1136 start_write (cvptr, uptr); /* start the sector write */
1137 break;
1138
1139
1140 case Write_Full_Sector:
1141 cvptr->length = DL_WPFSEC; /* transfer the header/data/trailer */
1142 start_write (cvptr, uptr); /* start the sector write */
1143 break;
1144
1145
1146 case Request_Status:
1147 case Request_Sector_Address:
1148 case Clear:
1149 case Address_Record:
1150 case Request_Syndrome:
1151 case Set_File_Mask:
1152 case Load_TIO_Register:
1153 case Request_Disc_Address:
1154 case End:
1155 case Wakeup:
1156 dl_service_controller (cvptr, uptr); /* the controller service handles these */
1157 break;
1158
1159
1160 default: /* we were entered with an invalid state */
1161 result = SCPE_IERR; /* return an internal (programming) error */
1162 break;
1163 } /* end of operation dispatch */
1164 break; /* end of start phase handlers */
1165
1166
1167 case data_phase:
1168 switch (opcode) { /* dispatch the current operation */
1169 case Read:
1170 case Read_Full_Sector:
1171 case Read_With_Offset:
1172 case Read_Without_Verify:
1173 case Write:
1174 case Write_Full_Sector:
1175 case Initialize:
1176 break; /* data transfers are handled by the caller */
1177
1178
1179 default: /* entered with an invalid state */
1180 result = SCPE_IERR; /* return an internal (programming) error */
1181 break;
1182 } /* end of operation dispatch */
1183 break; /* end of data phase handlers */
1184
1185
1186 case end_phase:
1187 switch (opcode) { /* dispatch the operation command */
1188
1189 case Recalibrate:
1190 case Seek:
1191 if (cvptr->type == ICD) /* is this an ICD controller? */
1192 dl_end_command (cvptr, drive_attention); /* seeks end with Drive Attention status */
1193 else /* if not an ICD controller, */
1194 uptr->STAT |= DL_S2ATN; /* set Attention in the unit status */
1195 break;
1196
1197
1198 case Read:
1199 case Read_Full_Sector:
1200 case Read_With_Offset:
1201 end_read (cvptr, uptr); /* end the sector read */
1202 break;
1203
1204
1205 case Read_Without_Verify:
1206 if (cvptr->sector == 0) /* have we reached the end of the track? */
1207 uptr->OP = Read; /* begin verifying the next time */
1208
1209 end_read (cvptr, uptr); /* end the sector read */
1210 break;
1211
1212
1213 case Verify:
1214 cvptr->verify_count = /* decrement the count */
1215 (cvptr->verify_count - 1) & D16_MASK; /* modulo 65536 */
1216
1217 if (cvptr->verify_count == 0) /* are there more sectors to verify? */
1218 cvptr->eod = SET; /* no, so terminate the command cleanly */
1219
1220 end_read (cvptr, uptr); /* end the sector read */
1221 break;
1222
1223
1224 case Write:
1225 case Write_Full_Sector:
1226 case Initialize:
1227 result = end_write (cvptr, uptr); /* end the sector write */
1228 break;
1229
1230
1231 case Request_Status:
1232 case Request_Sector_Address:
1233 case Request_Disc_Address:
1234 dl_service_controller (cvptr, uptr); /* the controller service handles these */
1235 break;
1236
1237
1238 default: /* we were entered with an invalid state */
1239 result = SCPE_IERR; /* return an internal (programming) error */
1240 break;
1241 } /* end of operation dispatch */
1242 break; /* end of end phase handlers */
1243 } /* end of phase dispatch */
1244
1245 return result; /* return the result of the service */
1246 }
1247
1248
1249 /* Service the controller unit.
1250
1251 The controller service routine is called to execute scheduled controller
1252 commands that do not access drive units. It is also called to obtain command
1253 parameters from the interface and to return command result values to the
1254 interface. The actions to be taken depend on the current state of the
1255 controller.
1256
1257 Controller commands are scheduled on a separate unit to allow concurrent
1258 processing while seeks are in progress. For example, a seek may be started
1259 on unit 0. While the seek is in progress, the CPU may request status from
1260 the controller. In between returning the first and second status words to
1261 the CPU, the seek may complete. Separating the controller unit allows seek
1262 completion to be handled while the controller is "busy" waiting for the CPU
1263 to indicate that it is ready for the second word.
1264
1265 For ICD controllers, the controller unit is not used, and all commands are
1266 scheduled on the drive unit. This is possible because ICD controllers always
1267 wait for seeks to complete before executing additional commands. To reduce
1268 code duplication, however, the drive unit service calls the controller
1269 service directly to handle controller commands.
1270
1271 The service routine validates phase assignments and returns SCPE_IERR
1272 (Internal Error) if entry is made with an illegal operation phase or a phase
1273 that is not valid for a given operation.
1274
1275 Implementation notes:
1276
1277 1. While the interface simulator is responsible for data phase transfers,
1278 the controller service routine is responsible for (re)starting and
1279 stopping the command wait timer for each parameter sent to and received
1280 from the interface.
1281 */
1282
dl_service_controller(CVPTR cvptr,UNIT * uptr)1283 t_stat dl_service_controller (CVPTR cvptr, UNIT *uptr)
1284 {
1285 t_stat result = SCPE_OK;
1286 const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP;
1287
1288 switch ((CNTLR_PHASE) uptr->PHASE) { /* dispatch the phase */
1289
1290 case start_phase:
1291 case end_phase:
1292 switch (opcode) { /* dispatch the current operation */
1293 case Request_Status:
1294 dl_end_command (cvptr, cvptr->status); /* the command completes with no status change */
1295 break;
1296
1297
1298 case Clear:
1299 dl_clear_controller (cvptr, uptr, soft_clear); /* clear the controller */
1300 dl_end_command (cvptr, normal_completion); /* the command is complete */
1301 break;
1302
1303
1304 case Request_Sector_Address:
1305 case Address_Record:
1306 case Request_Syndrome:
1307 case Set_File_Mask:
1308 case Load_TIO_Register:
1309 case Request_Disc_Address:
1310 dl_end_command (cvptr, normal_completion); /* the command is complete */
1311 break;
1312
1313
1314 case End:
1315 dl_idle_controller (cvptr); /* the command completes with the controller idle */
1316 break;
1317
1318
1319 case Wakeup:
1320 dl_end_command (cvptr, unit_available); /* the command completes with Unit Available status */
1321 break;
1322
1323
1324 default: /* we were entered with an invalid state */
1325 result = SCPE_IERR; /* return an internal (programming) error */
1326 break;
1327 } /* end of operation dispatch */
1328 break; /* end of start and end phase handlers */
1329
1330
1331 case data_phase:
1332 switch (opcode) { /* dispatch the current operation */
1333
1334 case Seek:
1335 case Verify:
1336 case Address_Record:
1337 case Read_With_Offset:
1338 case Load_TIO_Register:
1339 if (cvptr->length > 1) /* at least one more parameter to input? */
1340 set_timer (cvptr, SET); /* restart the timer for the next parameter */
1341 else /* this is the last one */
1342 set_timer (cvptr, CLEAR); /* so stop the command wait timer */
1343 break;
1344
1345
1346 case Request_Status:
1347 case Request_Sector_Address:
1348 case Request_Syndrome:
1349 case Request_Disc_Address:
1350 if (cvptr->length > 0) /* at least one more to parameter output? */
1351 set_timer (cvptr, SET); /* restart the timer for the next parameter */
1352 else /* this is the last one */
1353 set_timer (cvptr, CLEAR); /* so stop the command wait timer */
1354 break;
1355
1356
1357 default: /* we were entered with an invalid state */
1358 result = SCPE_IERR; /* return an internal (programming) error */
1359 break;
1360 } /* end of operation dispatch */
1361 break; /* end of data phase handlers */
1362 } /* end of phase dispatch */
1363
1364 return result; /* return the result of the service */
1365 }
1366
1367
1368 /* Service the command wait timer unit.
1369
1370 The command wait timer service routine is called if the command wait timer
1371 expires. This indicates that the CPU did not respond to a parameter transfer
1372 or did not issue a new command within the ~1.8 second timeout period. The
1373 timer is used with the MAC controller to ensure that a hung CPU does not tie
1374 up the controller, preventing it from servicing other CPUs or drives. ICD
1375 controllers do not use the command wait timer; they will wait forever, as
1376 each controller is dedicated to a single interface.
1377
1378 When a timeout occurs, the controller unit is cancelled in case the cause was
1379 a parameter timeout. Then the file mask is reset, and the controller is
1380 idled.
1381
1382 The interface is responsible for polling for a new command and for drive
1383 attention when a timeout occurs.
1384
1385 Implementation notes:
1386
1387 1. Only the controller unit may be active when the command wait timer
1388 expires. A unit is never active because the timer is cancelled when
1389 commands are executing and is restarted after the command completes.
1390 */
1391
dl_service_timer(CVPTR cvptr,UNIT * uptr)1392 t_stat dl_service_timer (CVPTR cvptr, UNIT *uptr)
1393 {
1394 sim_cancel (cvptr->aux); /* cancel any controller activation */
1395
1396 dl_idle_controller (cvptr); /* idle the controller */
1397 cvptr->file_mask = 0; /* clear the file mask */
1398
1399 return SCPE_OK;
1400 }
1401
1402
1403 /* Clear the controller.
1404
1405 The controller connected to the specified unit is cleared as directed. A MAC
1406 controller is connected to several units, so the unit is used to find the
1407 associated device and thereby the unit array. An ICD controller is connected
1408 only to the specified unit.
1409
1410 In hardware, four conditions clear the 13037 controller:
1411
1412 - an initial application of power
1413 - an assertion of the CLEAR signal by the CPU interface
1414 - a timeout of the command wait timer
1415 - a programmed Clear command
1416
1417 The first two conditions, called "hard clears," are equivalent and cause a
1418 firmware restart with the PWRON flag set. The 13175 interface for the HP
1419 1000 asserts the CLEAR signal in response to the backplane CRS signal if the
1420 PRESET ENABLE jumper is not installed (which is the usual case). The third
1421 condition also causes a firmware restart but with the PWRON flag clear. The
1422 last condition is executed in the command handler and therefore returns to
1423 the Command Wait Loop instead of the Poll Loop.
1424
1425 For a hard clear, the 13037 controller will:
1426
1427 - disconnect the CPU interface
1428 - zero the controller RAM (no drives held, last polled unit number reset)
1429 - issue a Controller Preset to clear all connected drives
1430 - clear the clock offset
1431 - clear the file mask
1432 - enter the Poll Loop (which clears the controller status)
1433
1434 For a timeout clear, the 13037 controller will:
1435
1436 - disconnect the CPU interface
1437 - clear the hold bits of any drives held by the interface that timed out
1438 - clear the clock offset
1439 - clear the file mask
1440 - enter the Poll Loop (which clears the controller status)
1441
1442 For a programmed "soft" clear, the 13037 controller will:
1443
1444 - clear the controller status
1445 - issue a Controller Preset to clear all connected drives
1446 - enter the Command Wait Loop
1447
1448 Controller Preset is a tag bus command that is sent to all drives connected
1449 to the controller. Each drive will:
1450
1451 - disconnect from the controller
1452 - clear its internal drive faults
1453 - clear its head and sector registers
1454 - clear its illegal head and sector flip-flops
1455 - reset its seek check, first status, drive fault, and attention status
1456
1457 In simulation, a hard clear occurs when a RESET -P or RESET command is
1458 issued, or a programmed CLC 0 instruction is executed. A soft clear occurs
1459 when a programmed Clear command is started. A timeout clear occurs when the
1460 command wait timer unit is serviced, but this action is handled in the timer
1461 unit service.
1462
1463 Because the controller execution state is implemented by scheduling command
1464 phases for the target or controller unit, a simulated firmware restart must
1465 abort any in-process activation. However, a firmware restart does not affect
1466 seeks in progress, so these must be allowed to continue to completion so that
1467 their Attention requests will be honored.
1468
1469
1470 Implementation notes:
1471
1472 1. The specific 13365 controller actions on hard or soft clears are not
1473 documented. Therefore, an ICD controller clear is handled as a MAC
1474 controller clear, except that only the current drive is preset (as an ICD
1475 controller manages only a single drive).
1476
1477 2. Neither hard nor soft clears affect the controller flags (e.g., EOC) or
1478 registers (e.g., cylinder address).
1479
1480 3. In simulation, an internal seek, such as an auto-seek during a Read
1481 command or the initial seek during a Cold Load Read command, will be
1482 aborted for a hard or timeout clear, whereas in hardware it would
1483 complete normally. This is OK, however, because an internal seek always
1484 clears the drive's Attention status on completion, so aborting the
1485 simulated seek is equivalent to an immediate seek completion.
1486
1487 4. In simulation, a Controller Preset only resets the specified status bits,
1488 as the remainder of the hardware actions are not implemented.
1489 */
1490
dl_clear_controller(CVPTR cvptr,UNIT * uptr,CNTLR_CLEAR clear_type)1491 t_stat dl_clear_controller (CVPTR cvptr, UNIT *uptr, CNTLR_CLEAR clear_type)
1492 {
1493 uint32 unit, unit_count;
1494 DEVICE *dptr = NULL;
1495
1496 if (clear_type == hard_clear) { /* is this a hard clear? */
1497 dl_idle_controller (cvptr); /* idle the controller */
1498 cvptr->file_mask = 0; /* clear the file mask */
1499 cvptr->poll_unit = 0; /* clear the last unit polled */
1500 }
1501
1502 if (cvptr->type == ICD) /* is this an ICD controller? */
1503 unit_count = 1; /* there is only one unit per controller */
1504
1505 else { /* a MAC controller clears all units */
1506 dptr = find_dev_from_unit (uptr); /* find the associated device */
1507
1508 if (dptr == NULL) /* the device doesn't exist?!? */
1509 return SCPE_IERR; /* this is an impossible condition! */
1510 else /* the device was found */
1511 unit_count = dptr->numunits; /* so get the number of units */
1512 }
1513
1514 for (unit = 0; unit < unit_count; unit++) { /* loop through the unit(s) */
1515 if (dptr) /* pick up the unit from the device? */
1516 uptr = dptr->units + unit; /* yes, so get the next unit */
1517
1518 if (!(uptr->flags & UNIT_DIS)) { /* is the unit enabled? */
1519 if (clear_type == hard_clear /* a hard clear cancels */
1520 && uptr->OP != Seek /* only if not seeking */
1521 && uptr->OP != Recalibrate) /* or recalibrating */
1522 sim_cancel (uptr); /* cancel the service */
1523
1524 uptr->STAT &= ~DL_S2CPS; /* do "Controller Preset" for the unit */
1525 }
1526 }
1527
1528 return SCPE_OK;
1529 }
1530
1531
1532 /* Idle the controller.
1533
1534 The command wait timer is turned off, the status is reset, and the controller
1535 is returned to the idle state (Poll Loop).
1536 */
1537
dl_idle_controller(CVPTR cvptr)1538 void dl_idle_controller (CVPTR cvptr)
1539 {
1540 cvptr->state = cntlr_idle; /* idle the controller */
1541 cvptr->status = normal_completion; /* the Poll Loop clears the status */
1542
1543 set_timer (cvptr, CLEAR); /* stop the command wait timer */
1544 return;
1545 }
1546
1547
1548
1549 /* Load or unload the drive heads.
1550
1551 In hardware, a drive's heads are loaded when a disc pack is installed and the
1552 RUN/STOP switch is set to RUN. The drive reports First Status when the heads
1553 load to indicate that the pack has potentially changed. Setting the switch
1554 to STOP unloads the heads. When the heads are unloaded, the drive reports
1555 Not Ready and Drive Busy status.
1556
1557 In simulation, the unit must be attached before the heads may be unloaded or
1558 loaded. As the heads should be automatically loaded when a unit is attached
1559 and unloaded when a unit is detached, this routine must be called after
1560 attaching and before detaching.
1561
1562
1563 Implementation notes:
1564
1565 1. The drive sets its Attention status bit when the heads load or unload.
1566 However, the ICD controller reports Attention only for head unloading.
1567
1568 2. Loading or unloading the heads clears Fault and Seek Check status.
1569
1570 3. If we are called during a RESTORE command, the unit's flags are not
1571 changed to avoid upsetting the state that was SAVEd.
1572 */
1573
dl_load_unload(CVPTR cvptr,UNIT * uptr,t_bool load)1574 t_stat dl_load_unload (CVPTR cvptr, UNIT *uptr, t_bool load)
1575 {
1576 if ((uptr->flags & UNIT_ATT) == 0) /* the unit must be attached to [un]load */
1577 return SCPE_UNATT; /* return "Unit not attached" if not */
1578
1579 else if (!(sim_switches & SIM_SW_REST)) /* modify the flags only if not restoring */
1580 if (load) { /* are we loading the heads? */
1581 uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* clear the unload flag */
1582 uptr->STAT = DL_S2FS; /* and set First Status */
1583
1584 if (cvptr->type != ICD) /* if this is not an ICD controller */
1585 uptr->STAT |= DL_S2ATN; /* set Attention status also */
1586 }
1587
1588 else { /* we are unloading the heads */
1589 uptr->flags = uptr->flags | UNIT_UNLOAD; /* set the unload flag */
1590 uptr->STAT = DL_S2ATN; /* and Attention status */
1591 }
1592
1593 return SCPE_OK;
1594 }
1595
1596
1597
1598 /* Disc library global utility routines */
1599
1600
1601 /* Classify the current controller opcode.
1602
1603 The controller opcode is classified as a read, write, control, or status
1604 command, and the classification is returned to the caller. If the opcode is
1605 illegal or undefined for the indicated controller, the classification is
1606 marked as invalid.
1607 */
1608
dl_classify(CNTLR_VARS cntlr)1609 CNTLR_CLASS dl_classify (CNTLR_VARS cntlr)
1610 {
1611 if (cntlr.type <= last_type /* if the controller type is legal */
1612 && cntlr.opcode <= Last_Opcode /* and the opcode is legal */
1613 && cmd_props [cntlr.opcode].valid [cntlr.type]) /* and is defined for this controller, */
1614 return cmd_props [cntlr.opcode].classification; /* then return the command classification */
1615 else /* the type or opcode is illegal */
1616 return class_invalid; /* so return an invalid classification */
1617 }
1618
1619
1620 /* Return the name of an opcode.
1621
1622 A string representing the supplied controller opcode is returned to the
1623 caller. If the opcode is illegal or undefined for the indicated controller,
1624 the string "invalid" is returned.
1625 */
1626
dl_opcode_name(CNTLR_TYPE controller,CNTLR_OPCODE opcode)1627 const char *dl_opcode_name (CNTLR_TYPE controller, CNTLR_OPCODE opcode)
1628 {
1629 if (controller <= last_type /* if the controller type is legal */
1630 && opcode <= Last_Opcode /* and the opcode is legal */
1631 && cmd_props [opcode].valid [controller]) /* and is defined for this controller, */
1632 return opcode_name [opcode]; /* then return the opcode name */
1633 else /* the type or opcode is illegal, */
1634 return invalid_name; /* so return an error indication */
1635 }
1636
1637
1638 /* Return the name of a command phase.
1639
1640 A string representing the supplied phase is returned to the caller. If the
1641 phase is illegal, the string "invalid" is returned.
1642 */
1643
dl_phase_name(CNTLR_PHASE phase)1644 const char *dl_phase_name (CNTLR_PHASE phase)
1645 {
1646 if (phase <= last_phase) /* if the phase is legal, */
1647 return phase_name [phase]; /* return the phase name */
1648 else /* the phase is illegal, */
1649 return invalid_name; /* so return an error indication */
1650 }
1651
1652
1653
1654 /* Disc library global VM routines */
1655
1656
1657 /* Attach a disc image file to a unit.
1658
1659 The file specified by the supplied filename is attached to the indicated
1660 unit. If the attach was successful, the heads are loaded on the drive.
1661
1662 If the drive is set to autosize, the size of the image file is compared to
1663 the table of drive capacities to determine which model of drive was used to
1664 create it. If the image file is new, then the previous drive model is
1665 retained.
1666 */
1667
dl_attach(CVPTR cvptr,UNIT * uptr,char * cptr)1668 t_stat dl_attach (CVPTR cvptr, UNIT *uptr, char *cptr)
1669 {
1670 uint32 id, size;
1671 t_stat result;
1672
1673 result = attach_unit (uptr, cptr); /* attach the unit */
1674
1675 if (result != SCPE_OK) /* did the attach fail? */
1676 return result; /* yes, so return the error status */
1677
1678 dl_load_unload (cvptr, uptr, TRUE); /* if the attach succeeded, load the heads */
1679
1680 if (uptr->flags & UNIT_AUTO) { /* is autosizing enabled? */
1681 size = sim_fsize (uptr->fileref) / sizeof (uint16); /* get the file size in words */
1682
1683 if (size > 0) /* a new file retains the current drive model */
1684 for (id = 0; id < PROPS_COUNT; id++) /* find the best fit to the drive models */
1685 if (size <= drive_props [id].words /* if the file size fits the drive capacity */
1686 || id == PROPS_COUNT - 1) { /* or this is the largest available drive */
1687 uptr->capac = drive_props [id].words; /* then set the capacity */
1688 uptr->flags = (uptr->flags & ~UNIT_MODEL) /* and the model */
1689 | SET_MODEL (id);
1690 break;
1691 }
1692 }
1693
1694 return SCPE_OK; /* the unit was successfully attached */
1695 }
1696
1697
1698 /* Detach a disc image file from a unit.
1699
1700 The heads are unloaded on the drive, and the attached file, if any, is
1701 detached.
1702 */
1703
dl_detach(CVPTR cvptr,UNIT * uptr)1704 t_stat dl_detach (CVPTR cvptr, UNIT *uptr)
1705 {
1706 dl_load_unload (cvptr, uptr, FALSE); /* unload the heads if attached */
1707 return detach_unit (uptr); /* and detach the unit */
1708 }
1709
1710
1711 /* Set the drive model.
1712
1713 This validation routine is called to set the model of disc drive associated
1714 with the specified unit. The "value" parameter indicates the model ID, and
1715 the unit capacity is set to the size indicated.
1716 */
1717
dl_set_model(UNIT * uptr,int32 value,char * cptr,void * desc)1718 t_stat dl_set_model (UNIT *uptr, int32 value, char *cptr, void *desc)
1719 {
1720 if (uptr->flags & UNIT_ATT) /* we cannot alter the disc model */
1721 return SCPE_ALATT; /* if the unit is attached */
1722
1723 if (value != UNIT_AUTO) /* if we are not autosizing */
1724 uptr->capac = drive_props [GET_MODEL (value)].words; /* set the capacity to the new value */
1725
1726 return SCPE_OK;
1727 }
1728
1729
1730
1731 /* Disc library local controller routines */
1732
1733
1734 /* Start a read operation on the current sector.
1735
1736 The current sector indicated by the controller address is read from the disc
1737 image file into the sector buffer in preparation for data transfer to the
1738 CPU. If the end of the track had been reached, and the file mask permits,
1739 an auto-seek is scheduled instead to allow the read to continue.
1740
1741 On entry, the end-of-data flag is checked. If it is set, the current read is
1742 completed. Otherwise, the buffer data offset and verify options are set up.
1743 For a Read Full Sector, the sync word is set from the controller type, and
1744 dummy cylinder and head-sector words are generated from the current location
1745 (as would be the case in the absence of track sparing).
1746
1747 The image file is positioned to the correct sector in preparation for
1748 reading. If the positioning requires a permitted seek, it is scheduled, and
1749 the routine returns with the operation phase unchanged to wait for seek
1750 completion before resuming the read (when the seek completes, the service
1751 routine will be entered, and we will be called again; this time, the
1752 end-of-cylinder flag will be clear and positioning will succeed). If
1753 positioning resulted in an error, the current read is terminated with the
1754 error status set.
1755
1756 If positioning succeeded within the same cylinder, the sector image is read
1757 into the buffer at an offset determined by the operation (Read Full Sector
1758 leaves room at the start of the buffer for the sector header). If the image
1759 file read did not return a full sector, the remainder of the buffer is padded
1760 with zeros. If the image read failed with a file system error, SCPE_IOERR is
1761 returned from the service routine to cause a simulation stop; resumption is
1762 handled as an Uncorrectable Data Error.
1763
1764 If the image was read correctly, the next sector address is updated, the
1765 operation phase is set for the data transfer, and the index of the first word
1766 to transfer is set.
1767
1768
1769 Implementation notes:
1770
1771 1. The length of the transfer required (cvptr->length) must be set before
1772 entry.
1773
1774 2. Entry while executing a Read Without Verify or Read Full Sector command
1775 inhibits address verification. The unit opcode is tested instead of the
1776 controller opcode because a Read Without Verify is changed to a Read to
1777 begin verifying after a track switch occurs.
1778 */
1779
start_read(CVPTR cvptr,UNIT * uptr)1780 static t_stat start_read (CVPTR cvptr, UNIT *uptr)
1781 {
1782 uint32 count, offset;
1783 t_bool verify;
1784 const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP;
1785
1786 if (cvptr->eod == SET) { /* is the end of data indicated? */
1787 dl_end_command (cvptr, normal_completion); /* complete the command */
1788 return SCPE_OK;
1789 }
1790
1791 if (opcode == Read_Full_Sector) { /* are we starting a Read Full Sector command? */
1792 if (cvptr->type == ICD) /* is this an ICD controller? */
1793 cvptr->buffer [0] = 0100377; /* ICD does not support ECC */
1794 else
1795 cvptr->buffer [0] = 0100376; /* MAC does support ECC */
1796
1797 set_address (cvptr, 1); /* set the current address into buffer 1-2 */
1798 offset = 3; /* start the data after the header */
1799 verify = FALSE; /* set for no address verification */
1800 }
1801
1802 else { /* it's another read command */
1803 offset = 0; /* data starts at the beginning */
1804 verify = (opcode != Read_Without_Verify); /* set for address verification unless it's a RWV */
1805 }
1806
1807 if (! position_sector (cvptr, uptr, verify)) /* position the sector */
1808 return SCPE_OK; /* a seek is in progress or an error occurred */
1809
1810 count = sim_fread (cvptr->buffer + offset, /* read the sector from the image */
1811 sizeof (uint16), DL_WPSEC, /* into the sector buffer */
1812 uptr->fileref);
1813
1814 for (count = count + offset; count < cvptr->length; count++) /* pad the sector as needed */
1815 cvptr->buffer [count] = 0; /* e.g., if reading from a new file */
1816
1817 if (ferror (uptr->fileref)) /* did a host file system error occur? */
1818 return io_error (cvptr, uptr, uncorrectable_data_error); /* set up the data error status and stop the simulation */
1819
1820 next_sector (cvptr, uptr); /* address the next sector */
1821
1822 uptr->PHASE = data_phase; /* set up the data transfer phase */
1823 cvptr->index = 0; /* reset the data index */
1824
1825 return SCPE_OK; /* the read was successfully started */
1826 }
1827
1828
1829 /* Finish a read operation on the current sector.
1830
1831 On entry, the end-of-data flag is checked. If it is set, the current read is
1832 completed. Otherwise, the command phase is reset to start the next sector,
1833 and the disc service is set to allow for the intersector delay.
1834
1835
1836 Implementation notes:
1837
1838 1. The CPU indicates the end of a read data transfer to an ICD controller by
1839 untalking the drive. The untalk is done by the driver as soon as the
1840 DCPC completion interrupt is processed. However, the time from the final
1841 DCPC transfer through driver entry to the point where the untalk is
1842 asserted on the bus varies from 80 instructions (RTE-6/VM with OS
1843 microcode and the buffer in the system map) to 152 instructions (RTE-IVB
1844 with the buffer in the user map). The untalk must occur before the start
1845 of the next sector, or the drive will begin the data transfer.
1846
1847 Normally, this is not a problem, as the driver clears the FIFO of any
1848 received data after DCPC completion. However, if the read terminates
1849 after the last sector of a track, and accessing the next sector would
1850 require an intervening seek, and the file mask disables auto-seeking or
1851 an enabled seek would move the positioner beyond the drive limits, then
1852 the controller will indicate an End of Cylinder error if the untalk does
1853 not arrive before the seek is initiated.
1854
1855 The RTE driver (DVA32) and various utilities that manage the disc
1856 directly (e.g., SWTCH) do not appear to account for these bogus errors,
1857 so the ICD controller hardware must avoid them in some unknown manner.
1858 We work around the issue by extending the intersector delay to allow time
1859 for a potential untalk whenever the next access would otherwise fail.
1860
1861 Note that this issue does not occur with writes because DCPC completion
1862 asserts EOI concurrently with the final data byte to terminate the
1863 command.
1864 */
1865
end_read(CVPTR cvptr,UNIT * uptr)1866 static void end_read (CVPTR cvptr, UNIT *uptr)
1867 {
1868 uint32 limit;
1869
1870 if (cvptr->eod == SET) /* is the end of data indicated? */
1871 dl_end_command (cvptr, normal_completion); /* complete the command */
1872
1873 else { /* reading continues */
1874 uptr->PHASE = start_phase; /* reset to the start phase */
1875 uptr->wait = cvptr->sector_time; /* delay for the intersector time */
1876
1877 if (cvptr->eoc == SET && cvptr->type == ICD) { /* seek will be required and controller is ICD? */
1878 if (!(cvptr->file_mask & DL_FAUTSK)) /* if auto-seek is disabled */
1879 limit = cvptr->cylinder; /* then the limit is the current cylinder */
1880 else if (cvptr->file_mask & DL_FDECR) /* else if enabled and decremental seek */
1881 limit = 0; /* then the limit is cylinder 0 */
1882 else /* else the enabled limit is the last cylinder */
1883 limit = drive_props [GET_MODEL (uptr->flags)].cylinders;
1884
1885 if (cvptr->cylinder == limit) /* is positioner at the limit? */
1886 uptr->wait = cvptr->eot_time; /* seek will fail; delay to allow CPU to untalk */
1887 }
1888 }
1889
1890 return;
1891 }
1892
1893
1894 /* Start a write operation on the current sector.
1895
1896 The current sector indicated by the controller address is positioned for
1897 writing from the sector buffer to the disc image file after data transfer
1898 from the CPU. If the end of the track had been reached, and the file mask
1899 permits, an auto-seek is scheduled instead to allow the write to continue.
1900
1901 On entry, if writing is not permitted, or formatting is required but not
1902 enabled, the command is terminated with an error. Otherwise, the disc image
1903 file is positioned to the correct sector in preparation for writing.
1904
1905 If the positioning requires a permitted seek, it is scheduled, and the
1906 routine returns with the operation phase unchanged to wait for seek
1907 completion before resuming the write (when the seek completes, the service
1908 routine will be entered, and we will be called again; this time, the
1909 end-of-cylinder flag will be clear and positioning will succeed). If
1910 positioning resulted in an error, the current write is terminated with the
1911 error status set.
1912
1913 If positioning succeeded within the same cylinder, the operation phase is set
1914 for the data transfer, and the index of the first word to transfer is set.
1915
1916
1917 Implementation notes:
1918
1919 1. Entry while executing a Write Full Sector or Initialize command inhibits
1920 address verification. In addition, the drive's FORMAT switch must be set
1921 to the enabled position for these commands to succeed.
1922 */
1923
start_write(CVPTR cvptr,UNIT * uptr)1924 static void start_write (CVPTR cvptr, UNIT *uptr)
1925 {
1926 const t_bool verify = (CNTLR_OPCODE) uptr->OP == Write; /* only Write verifies the sector address */
1927
1928 if ((uptr->flags & UNIT_WPROT) /* is the unit write protected, */
1929 || !verify && !(uptr->flags & UNIT_FMT)) /* or is formatting required but not enabled? */
1930 dl_end_command (cvptr, status_2_error); /* terminate the write with an error */
1931
1932 else if (position_sector (cvptr, uptr, verify)) { /* writing is permitted; position the sector */
1933 uptr->PHASE = data_phase; /* positioning succeeded; set up data transfer phase */
1934 cvptr->index = 0; /* reset the data index */
1935 }
1936
1937 return;
1938 }
1939
1940
1941 /* Finish a write operation on the current sector.
1942
1943 The current sector is written from the sector buffer to the disc image file
1944 at the current file position. The next sector address is then updated to
1945 allow writing to continue.
1946
1947 On entry, the drive is checked to ensure that it is ready for the write.
1948 Then the sector buffer is padded appropriately if a full sector of data was
1949 not transferred. The buffer is written to the disc image file at the
1950 position corresponding to the controller address as set when the sector was
1951 started. The write begins at a buffer offset determined by the command (a
1952 Write Full Sector has header words at the start of the buffer that are not
1953 written to the disc image).
1954
1955 If the image write failed with a file system error, SCPE_IOERR is returned
1956 from the service routine to cause a simulation stop; resumption is handled as
1957 an Uncorrectable Data Error. If the image was written correctly, the next
1958 sector address is updated. If the end-of-data flag is set, the current write
1959 is completed. Otherwise, the command phase is reset to start the next
1960 sector, and the disc service is scheduled to allow for the intersector delay.
1961
1962
1963 Implementation notes:
1964
1965 1. A partial sector is filled with 177777B words (ICD) or copies of the last
1966 word (MAC) per page 7-10 of the ICD/MAC Disc Diagnostic manual.
1967 */
1968
end_write(CVPTR cvptr,UNIT * uptr)1969 static t_stat end_write (CVPTR cvptr, UNIT *uptr)
1970 {
1971 uint32 count;
1972 uint16 pad;
1973 const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP;
1974 const uint32 offset = (opcode == Write_Full_Sector ? 3 : 0);
1975
1976 if (uptr->flags & UNIT_UNLOAD) { /* if the drive is not ready, */
1977 dl_end_command (cvptr, access_not_ready); /* terminate the command with an error */
1978 return SCPE_OK;
1979 }
1980
1981 if (cvptr->index < DL_WPSEC + offset) { /* was a partial sector transferred? */
1982 if (cvptr->type == ICD) /* an ICD controller */
1983 pad = D16_UMAX; /* pads the sector with -1 */
1984 else /* a MAC controller */
1985 pad = cvptr->buffer [cvptr->index - 1]; /* pads with the last word written */
1986
1987 for (count = cvptr->index; count < DL_WPSEC + offset; count++)
1988 cvptr->buffer [count] = pad; /* pad the sector buffer as needed */
1989 }
1990
1991 sim_fwrite (cvptr->buffer + offset, sizeof (uint16), /* write the sector to the file */
1992 DL_WPSEC, uptr->fileref);
1993
1994 if (ferror (uptr->fileref)) /* did a host file system error occur? */
1995 return io_error (cvptr, uptr, uncorrectable_data_error); /* set up the data error status and stop the simulation */
1996
1997 next_sector (cvptr, uptr); /* address the next sector */
1998
1999 if (cvptr->eod == SET) /* is the end of data indicated? */
2000 dl_end_command (cvptr, normal_completion); /* complete the command */
2001
2002 else { /* writing continues */
2003 uptr->PHASE = start_phase; /* reset to the start phase */
2004 uptr->wait = cvptr->sector_time; /* delay for the intersector time */
2005 }
2006
2007 return SCPE_OK;
2008 }
2009
2010
2011 /* Position the disc image file at the current sector.
2012
2013 The image file is positioned at the byte address corresponding to the drive's
2014 current cylinder and the controller's current head and sector addresses.
2015 Positioning may involve an auto-seek if a prior read or write addressed the
2016 final sector of a cylinder. If a seek is initiated or an error is detected,
2017 the routine returns FALSE to indicate that the positioning was not performed.
2018 If the file was positioned, the routine returns TRUE.
2019
2020 On entry, if the controller's end-of-cylinder flag is set, a prior read or
2021 write addressed the final sector in the current cylinder. If the file mask
2022 does not permit auto-seeking, the current command is terminated with an End
2023 of Cylinder error. Otherwise, the cylinder is incremented or decremented as
2024 directed by the file mask, and a seek to the new cylinder is started.
2025
2026 If the increment or decrement resulted in an out-of-bounds value, the seek
2027 will return Seek Check status, and the command is terminated with an error.
2028 If the seek is legal, the routine returns with the disc service scheduled for
2029 seek completion and the command state unchanged. When the service is
2030 reentered, the read or write will continue on the new cylinder.
2031
2032 If the EOC flag was not set, the drive's position is checked against the
2033 controller's position if address verification is requested. If they are
2034 different (as may occur with an Address Record command that specified a
2035 different location than the last Seek command), a seek is started to the
2036 correct cylinder, and the routine returns with the disc service scheduled for
2037 seek completion as above.
2038
2039 If the drive and controller positions agree or verification is not requested,
2040 the CHS addresses are validated against the drive limits. If they are
2041 invalid, Seek Check status is set, and the command is terminated with an
2042 error.
2043
2044 If the addresses are valid, the drive is checked to ensure that it is ready
2045 for positioning. If it is, the file is positioned to a byte offset in the
2046 image file that is calculated from the CHS address. If positioning succeeds,
2047 the disc service is scheduled to begin the data transfer, and the routine
2048 returns TRUE to indicate that the file position is set. If positioning fails
2049 with a host file system error, it is reported to the simulation console, and
2050 the routine returns FALSE to indicate that an AGC (drive positioner) fault
2051 occurred.
2052
2053
2054 Implementation notes:
2055
2056 1. The ICD controller returns an End of Cylinder error if an auto-seek
2057 results in a position beyond the drive limits. The MAC controller
2058 returns a Status-2 error. Both controllers set the Seek Check bit in the
2059 drive status word.
2060 */
2061
position_sector(CVPTR cvptr,UNIT * uptr,t_bool verify)2062 static t_bool position_sector (CVPTR cvptr, UNIT *uptr, t_bool verify)
2063 {
2064 uint32 block;
2065 uint32 model = GET_MODEL (uptr->flags);
2066
2067 if (cvptr->eoc == SET) /* are we at the end of a cylinder? */
2068 if (cvptr->file_mask & DL_FAUTSK) { /* is an auto-seek allowed? */
2069 if (cvptr->file_mask & DL_FDECR) /* is a decremental seek requested? */
2070 cvptr->cylinder = cvptr->cylinder - 1 & D16_MASK; /* decrease the cylinder address with wraparound */
2071 else /* an incremental seek is requested */
2072 cvptr->cylinder = cvptr->cylinder + 1 & D16_MASK; /* increase the cylinder address with wraparound */
2073
2074 start_seek (cvptr, uptr, /* start the auto-seek */
2075 (CNTLR_OPCODE) uptr->OP, /* with the current operation */
2076 (CNTLR_PHASE) uptr->PHASE); /* and phase unchanged */
2077
2078 if (uptr->STAT & DL_S2SC) /* did a seek check occur? */
2079 if (cvptr->type == ICD) /* is this ICD controller? */
2080 dl_end_command (cvptr, end_of_cylinder); /* report it as an End of Cylinder error */
2081 else /* it is a MAC controller */
2082 dl_end_command (cvptr, status_2_error); /* report it as a Status-2 error */
2083 }
2084
2085 else /* the file mask does not permit an auto-seek */
2086 dl_end_command (cvptr, end_of_cylinder); /* so terminate with an EOC error */
2087
2088 else if (verify && (uint32) uptr->CYL != cvptr->cylinder) { /* is the positioner on the wrong cylinder? */
2089 start_seek (cvptr, uptr, /* start a seek to the correct cylinder */
2090 (CNTLR_OPCODE) uptr->OP, /* with the current operation */
2091 (CNTLR_PHASE) uptr->PHASE); /* and phase unchanged */
2092
2093 if (uptr->STAT & DL_S2SC) /* did a seek check occur? */
2094 dl_end_command (cvptr, status_2_error); /* report a Status-2 error */
2095 }
2096
2097 else if (((uint32) uptr->CYL >= drive_props [model].cylinders) /* is the cylinder out of bounds? */
2098 || (cvptr->head >= drive_props [model].heads) /* or the head? */
2099 || (cvptr->sector >= drive_props [model].sectors)) { /* or the sector? */
2100 uptr->STAT = uptr->STAT | DL_S2SC; /* set Seek Check status */
2101 dl_end_command (cvptr, status_2_error); /* and terminate with an error */
2102 }
2103
2104 else if (uptr->flags & UNIT_UNLOAD) /* is the drive ready for positioning? */
2105 dl_end_command (cvptr, access_not_ready); /* terminate the command with an access error */
2106
2107 else { /* we are ready to position the image file */
2108 block = TO_BLOCK (uptr->CYL, cvptr->head, /* calculate the new block position */
2109 cvptr->sector, model); /* (for inspection only) */
2110 uptr->pos = TO_OFFSET (block); /* and then convert to a byte offset */
2111
2112 if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the image file position; if it failed */
2113 io_error (cvptr, uptr, status_2_error); /* then report it to the simulation console */
2114
2115 dl_load_unload (cvptr, uptr, FALSE); /* unload the heads */
2116 uptr->STAT |= DL_S2FAULT; /* and set Fault status */
2117 }
2118
2119 else { /* otherwise the seek succeeded */
2120 uptr->wait = cvptr->data_time; /* so delay for the data access time */
2121
2122 return TRUE; /* report that positioning was accomplished */
2123 }
2124 }
2125
2126 return FALSE; /* report that positioning failed or was deferred */
2127 }
2128
2129
2130 /* Address the next sector.
2131
2132 The controller's CHS address is incremented to point at the next sector. If
2133 the next sector number is valid, the routine returns. Otherwise, the sector
2134 number is reset to sector 0. If the file mask is set for cylinder mode, the
2135 head is incremented, and if the new head number is valid, the routine
2136 returns. If the head number is invalid, it is reset to head 0, and the
2137 end-of-cylinder flag is set. The EOC flag is also set if the file mask is
2138 set for surface mode.
2139
2140 The new cylinder address is not set here, because cylinder validation must
2141 only occur when the next sector is actually accessed. Otherwise, reading or
2142 writing the last sector on a track or cylinder with auto-seek disabled would
2143 cause an End of Cylinder error, even if the transfer ended with that sector.
2144 Instead, we set the EOC flag to indicate that a cylinder update is pending.
2145
2146 As a result of this deferred update method, the state of the EOC flag must be
2147 considered when returning the disc address to the CPU.
2148 */
2149
next_sector(CVPTR cvptr,UNIT * uptr)2150 static void next_sector (CVPTR cvptr, UNIT *uptr)
2151 {
2152 const uint32 model = GET_MODEL (uptr->flags); /* get the disc model */
2153
2154 cvptr->sector = cvptr->sector + 1; /* increment the sector number */
2155
2156 if (cvptr->sector < drive_props [model].sectors) /* are we at the end of the track? */
2157 return; /* no, so the next sector value is OK */
2158
2159 cvptr->sector = 0; /* wrap the sector number */
2160
2161 if (cvptr->file_mask & DL_FCYLM) { /* are we in cylinder mode? */
2162 cvptr->head = cvptr->head + 1; /* yes, so increment the head */
2163
2164 if (cvptr->head < drive_props [model].heads) /* are we at the end of the cylinder? */
2165 return; /* no, so the next head value is OK */
2166
2167 cvptr->head = 0; /* wrap the head number */
2168 }
2169
2170 cvptr->eoc = SET; /* set the end-of-cylinder flag to */
2171 return; /* indicate that an update is required */
2172 }
2173
2174
2175 /* Start a seek.
2176
2177 A seek is initiated on the indicated unit if the drive is ready and the
2178 cylinder, head, and sector values in the controller are valid for the current
2179 drive model. If the current operation is a recalibrate, a seek is initiated
2180 to cylinder 0 instead of the cylinder value stored in the controller. The
2181 routine returns TRUE if the drive was ready for the seek and FALSE if it was
2182 not.
2183
2184 If the controller cylinder is beyond the drive's limit, Seek Check status is
2185 set in the unit, and the heads are not moved. Otherwise, the relative
2186 cylinder position change is calculated, and the heads are moved to the new
2187 position.
2188
2189 If the controller head or sector is beyond the drive's limit, Seek Check
2190 status is set in the unit. Otherwise, Seek Check status is cleared, and the
2191 new file offset is calculated.
2192
2193 A seek check terminates the current command for an ICD controller. For a MAC
2194 controller, the seek check is noted in the drive status, but processing will
2195 continue until the drive sets Attention status.
2196
2197 Finally, the drive operation and phase are set to the supplied values before
2198 returning.
2199
2200
2201 Implementation notes:
2202
2203 1. EOC is not reset for recalibrate so that a reseek will return to the same
2204 location as was current when the recalibrate was done.
2205
2206 2. Calculation of the file offset is performed here simply to keep the unit
2207 position register available for inspection. The actual file positioning
2208 is done in position_sector.
2209
2210 3. In hardware, a seek to the current location will set Drive Busy status
2211 for 1.3 milliseconds (the head settling time). In simulation, disc
2212 service is scheduled as though a one-cylinder seek was requested.
2213 */
2214
start_seek(CVPTR cvptr,UNIT * uptr,CNTLR_OPCODE next_opcode,CNTLR_PHASE next_phase)2215 static t_bool start_seek (CVPTR cvptr, UNIT *uptr, CNTLR_OPCODE next_opcode, CNTLR_PHASE next_phase)
2216 {
2217 int32 delta;
2218 uint32 block, target_cylinder;
2219 const uint32 model = GET_MODEL (uptr->flags); /* get the drive model */
2220
2221 if (uptr->flags & UNIT_UNLOAD) { /* are the heads unloaded? */
2222 dl_end_command (cvptr, status_2_error); /* the seek ends with Status-2 error */
2223 return FALSE; /* as the drive was not ready */
2224 }
2225
2226 if ((CNTLR_OPCODE) uptr->OP == Recalibrate) /* is the unit recalibrating? */
2227 target_cylinder = 0; /* seek to cylinder 0 and don't reset the EOC flag */
2228
2229 else { /* it's a Seek command or an auto-seek request */
2230 target_cylinder = cvptr->cylinder; /* seek to the controller cylinder */
2231 cvptr->eoc = CLEAR; /* clear the end-of-cylinder flag */
2232 }
2233
2234 if (target_cylinder >= drive_props [model].cylinders) { /* is the cylinder out of bounds? */
2235 delta = 0; /* don't change the positioner */
2236 uptr->STAT = uptr->STAT | DL_S2SC; /* and set Seek Check status */
2237 }
2238
2239 else { /* the cylinder value is OK */
2240 delta = abs (uptr->CYL - (int32) target_cylinder); /* calculate the relative movement */
2241 uptr->CYL = target_cylinder; /* and move the positioner */
2242
2243 if ((cvptr->head >= drive_props [model].heads) /* if the head */
2244 || (cvptr->sector >= drive_props [model].sectors)) /* or the sector is out of bounds, */
2245 uptr->STAT = uptr->STAT | DL_S2SC; /* set Seek Check status */
2246
2247 else { /* the head and sector are OK */
2248 uptr->STAT = uptr->STAT & ~DL_S2SC; /* clear Seek Check status */
2249
2250 block = TO_BLOCK (uptr->CYL, cvptr->head, /* set up the new block position */
2251 cvptr->sector, model); /* (for inspection only) */
2252 uptr->pos = TO_OFFSET (block); /* and then convert to a byte offset */
2253 }
2254 }
2255
2256 if ((uptr->STAT & DL_S2SC) && cvptr->type == ICD) /* did a Seek Check occur for an ICD controller? */
2257 dl_end_command (cvptr, status_2_error); /* the command ends with a Status-2 error */
2258
2259 else { /* the seek was OK or this is a MAC controller */
2260 if (delta == 0) /* if the seek is to the same cylinder, */
2261 delta = 1; /* then schedule as a one-cylinder seek */
2262
2263 uptr->wait = cvptr->seek_time * delta; /* the seek delay is based on the relative movement */
2264 }
2265
2266 uptr->OP = next_opcode; /* set the next operation */
2267 uptr->PHASE = next_phase; /* and command phase */
2268 return TRUE; /* and report that the drive was ready */
2269 }
2270
2271
2272 /* Report a stream I/O error.
2273
2274 Errors indicated by the host file system are reported to the console, and
2275 simulation is stopped with an "I/O error" message. If the simulation is
2276 continued, the CPU will receive the supplied status indication from the
2277 controller.
2278 */
2279
io_error(CVPTR cvptr,UNIT * uptr,CNTLR_STATUS status)2280 static t_stat io_error (CVPTR cvptr, UNIT *uptr, CNTLR_STATUS status)
2281 {
2282 dl_end_command (cvptr, status); /* terminate the command with an error */
2283
2284 cprintf ("%s simulator disc library I/O error: %s\n", /* report the error to the console */
2285 sim_name, strerror (errno));
2286
2287 clearerr (uptr->fileref); /* clear the error */
2288
2289 return SCPE_IOERR; /* return an I/O error to stop the simulator */
2290 }
2291
2292
2293
2294 /* Disc library local utility routines */
2295
2296
2297 /* Set the current controller address into the buffer.
2298
2299 The controller's current cylinder, head, and sector are packed into two words
2300 and stored in the sector buffer, starting at the index specified. If the
2301 end-of-cylinder flag is set, the cylinder is incremented to reflect the
2302 auto-seek that will be attempted when the next sequential access is made.
2303
2304
2305 Implementation notes:
2306
2307 1. The 13037 firmware always increments the cylinder number if the EOC flag
2308 is set, rather than checking cylinder increment/decrement bit in the file
2309 mask.
2310 */
2311
set_address(CVPTR cvptr,uint32 index)2312 static void set_address (CVPTR cvptr, uint32 index)
2313 {
2314 cvptr->buffer [index] = (uint16) cvptr->cylinder /* update the cylinder if EOC is set */
2315 + (cvptr->eoc == SET ? 1 : 0);
2316
2317 cvptr->buffer [index + 1] = SET_HEAD (cvptr) /* merge the head and sector */
2318 | SET_SECTOR (cvptr);
2319
2320 return;
2321 }
2322
2323
2324 /* Start or stop the command wait timer.
2325
2326 A MAC controller uses a 1.8 second timer to ensure that it does not wait
2327 forever for a non-responding disc drive or CPU interface. In simulation, MAC
2328 interfaces supply an auxiliary timer unit that is activated when the command
2329 wait timer is started and cancelled when the timer is stopped.
2330
2331 ICD interfaces do not use the command wait timer or supply an auxiliary unit.
2332
2333
2334 Implementation notes:
2335
2336 1. Absolute activation is used because the timer is restarted between
2337 parameter word transfers.
2338 */
2339
set_timer(CVPTR cvptr,FLIP_FLOP action)2340 static void set_timer (CVPTR cvptr, FLIP_FLOP action)
2341 {
2342 if (cvptr->type == MAC) /* is this a MAC controller? */
2343 if (action == SET) /* should we start the timer? */
2344 sim_activate_abs (cvptr->aux + timer, /* activate the auxiliary unit */
2345 cvptr->wait_time);
2346 else /* we stop the timer */
2347 sim_cancel (cvptr->aux + timer); /* by canceling the unit */
2348 return;
2349 }
2350
2351
2352 /* Return the drive status (status word 2).
2353
2354 In hardware, the controller outputs the Address Unit command on the drive tag
2355 bus and the unit number on the drive control bus. The addressed drive then
2356 responds by setting its internal "selected" flag. The controller then
2357 outputs the Request Status command on the tag bug, and the selected drive
2358 returns its status on the control bus. If a drive is selected but the heads
2359 are unloaded, the drive returns Not Ready and Busy status. If no drive is
2360 selected, the control bus floats inactive. This is interpreted by the
2361 controller as Not Ready status (because the drive returns an inactive Ready
2362 status).
2363
2364 In simulation, an enabled but detached unit corresponds to "selected but
2365 heads unloaded," and a disabled unit corresponds to a non-existent unit.
2366
2367
2368 Implementation notes:
2369
2370 1. The Attention, Drive Fault, First Status, and Seek Check bits are stored
2371 in the unit status word. The other status bits are determined
2372 dynamically.
2373
2374 2. The Drive Busy bit is set if the unit service is scheduled. In hardware,
2375 this bit indicates that the heads are not positioned over a track, i.e.,
2376 that a seek is in progress. In simulation, the only time a Request
2377 Status command is allowed is either when the controller is waiting for
2378 seek completion or for a new command. In the latter case, unit service
2379 will not be scheduled, so activation can only be for seek completion.
2380 */
2381
drive_status(UNIT * uptr)2382 static uint16 drive_status (UNIT *uptr)
2383 {
2384 uint16 status;
2385 uint32 model;
2386
2387 if (uptr == NULL) /* if the unit is invalid */
2388 return DL_S2ERR | DL_S2NR; /* then it does not respond */
2389
2390 model = GET_MODEL (uptr->flags); /* get the drive model */
2391 status = (uint16) (drive_props [model].type | uptr->STAT); /* start with the drive type and unit status */
2392
2393 if (uptr->flags & UNIT_WPROT) /* is the write protect switch set? */
2394 status |= DL_S2RO; /* set the Protected status bit */
2395
2396 if (uptr->flags & UNIT_FMT) /* is the format switch enabled? */
2397 status |= DL_S2FMT; /* set the Format status bit */
2398
2399 if (uptr->flags & UNIT_DIS) /* is the unit non-existent? */
2400 status |= DL_S2NR; /* set the Not Ready bit */
2401
2402 else if (uptr->flags & UNIT_UNLOAD) /* are the heads unloaded? */
2403 status |= DL_S2NR | DL_S2BUSY; /* set the Not Ready and Drive Busy bits */
2404
2405 if (sim_is_active (uptr)) /* is the drive positioner moving? */
2406 status |= DL_S2BUSY; /* set the Drive Busy bit */
2407
2408 if (status & DL_S2ERRORS) /* are there any Status-2 errors? */
2409 status |= DL_S2ERR; /* set the Error bit */
2410
2411 return status; /* return the unit status */
2412 }
2413