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