--------------------------------------------------------------------------------

For FASTTIME, the data phase activation was moved from the data phase service
to the PREADSTB/PWRITESTB processing.  This would synchronize buffer_word transfers
with channel service, so overrun could not occur.  Then the data phase activation
time would simply become the delay between channel transfer and controller pickup
and so could be minimal.  Without this, the FASTTIME data time is still the
synchronous transfer time and so cannot be too fast, or an overrun will occur.  As
a practical matter, though, the tape controller has the highest MPX priority, so
it will always be serviced immediately after SRn is asserted.

Here are the original comments from the ms_interface routine (hp3000_ms.c):

// THIS IS NO LONGER IMPLEMENTED; DO WE WANT TO REIMPLEMENT?

   In REALTIME mode, read data phase processing stores the next data word from
   the tape buffer into the data register, clears the data flag, sets the
   channel service request, and schedules the next data phase service.  PREADSTB
   returns the data register word to the multiplexer channel and sets the data
   flag.  Channel service must occur before the next controller service; if data
   phase processing starts with the data flag clear, an overrun (timing) error
   is indicated.  In this mode, as in hardware, the READNEXTWD signal is
   ignored.

   In FASTTIME mode, the sequence is the same, except that scheduling the next
   data phase service is delayed until the READNEXTWD signal is received.  As
   READNEXTWD is asserted concurrently with PREADSTB, the tape data transfer
   essentially slows down to match the channel data transfer rate, so an overrun
   cannot occur.  This allows the data transfer time to be reduced to as little
   as one instruction time.

   Write commands behave similarly.  TOGGLEOUTXFER assertion schedules the start
   phase.  PWRITESTB processing stores the channel data word in the data
   register and sets the data flag.  Data phase processing copies the word from
   the data register to the tape buffer, clears the data flag, and sets the
   channel service request.  In REALTIME mode, the next data phase is scheduled,
   and an underrun (timing) error occurs if phase processing begins with the
   data flag set.  In FASTTIME mode, data phase scheduling is delayed until
   PWRITESTB processing, so an underrun cannot occur.

// END

--------------------------------------------------------------------------------

Phase Wait Times
~~~~~~~~~~~~~~~~

idle -> wait:
  (not scheduled)

idle -> start:
  overhead (if no drive access)
  overhead + bot_start (if access at BOT)
  overhead + ir_start  (if access not at BOT)

wait -> start:
  overhead + bot_start (if access at BOT)
  overhead + ir_start  (if access not at BOT)

start -> stop
  ir_start (Clear_Controller)
  rewind_start (if rewinding at BOT)

start -> traverse:
  gaplen * data_xfer (if gap)
  rewind_start + (pos * rewind_rate) / bpi (if rewinding)

start -> data:
  2 * data_xfer (if no gap and not spacing)
  length * data_xfer (if no gap and spacing)

traverse -> data:
  2 * data_xfer (if not writing a gap)

traverse -> stop:
  ir_start (if writing a gap or error occurred)
  rewind_stop (if rewind)

traverse -> start:
  2 * data_xfer (if spacing and runaway)
  
data -> stop:
  (length - index) * data_xfer + ir_start (if read)
  ir_start (otherwise)

data -> data:
  2 * data_xfer (data transfer)

data -> start:
  2 * ir_start (FSF, BSF)


Rearranged by next state
~~~~~~~~~~~~~~~~~~~~~~~~

idle -> wait:
  (not scheduled)

*****

(any) -> error:
  ir_start (all failures)

*****

idle -> start:
  overhead (if no drive access)
  overhead + bot_start (if access at BOT)
  overhead + ir_start  (if access not at BOT)

wait -> start:
  overhead + bot_start (if access at BOT)
  overhead + ir_start  (if access not at BOT)

traverse -> start:
  2 * data_xfer (if spacing and runaway)
  
data -> start:
  2 * ir_start (FSF, BSF)

*****

start -> traverse:
  gaplen * data_xfer (if gap)
  rewind_start + (pos * rewind_rate) / bpi (if rewinding)

*****

start -> data:
  2 * data_xfer (if no gap and not spacing)
  length * data_xfer (if no gap and spacing)

traverse -> data:
  2 * data_xfer (if not writing a gap)

data -> data:
  2 * data_xfer (data transfer)

*****

start -> stop
  ir_start (Clear_Controller)
  rewind_start (if rewinding at BOT)

traverse -> stop:
  ir_start (if writing a gap or error occurred)
  rewind_stop (if rewind)

data -> stop:
  (length - index) * data_xfer + ir_start (if read)
  ir_start (otherwise)

==> maybe switch on next_phase with conditions on uptr->PHASE?

--------------------------------------------------------------------------------

   A master reset is generated either by an I/O Reset signal or a Programmed
   Master Clear (CIO bit 0).  It performs these actions:

    - asserts CLR to the microprocessor, restarting it from ROM address 0
    - clears the PAR ERR, XFER ERROR, INT ACT, and INT REQ flip-flops
    - clears the unit select flip-flops, selecting unit 0
    - sets the MASK flip-flop
    - clears the SIO OK, INXFER, OUTXFER, SR, and SRB flip-flops (same as CBL,
      except that ESR is not cleared)

--------------------------------------------------------------------------------

static CNTLR_IFN_IBUS check_status (CVPTR cvptr, UNIT *uptr, TAPELIB_CALL lib_call)
--> static t_bool call_simlib (CVPTR cvptr, UNIT *uptr, TAPELIB_CALL lib_call, t_mtrlnt parameter, CNTLR_IFN_IBUS *result)

use:
    if (call_simlib (cvptr, uptr, lib_read_fwd, TL_MAXREC, &outbound)) {
        ...
        }

or maybe:

    outbound = call_simlib (cvptr, uptr, lib_read_fwd, TL_MAXREC);

    if ((outbound & SCPE) == NOFLAGS) {
        ...
        }

...but "bad but recoverable" returns must have SCPE | SCPE_OK to differentiate
from good returns.  Then the decl would be:

  static CNTLR_IFN_IBUS call_simlib (CVPTR cvptr, UNIT *uptr, TAPELIB_CALL lib_call, t_mtrlnt parameter);

calls:
 - sim_tape_rdrecf (uptr, cvptr->buffer, &cvptr->length, TL_MAXREC);
 - sim_tape_wrgap (uptr, pptr->gap_size);
 - sim_tape_sprecf (uptr, &cvptr->length);
 - sim_tape_sprecr (uptr, &cvptr->length);
 - sim_tape_wrtmk (uptr);
 - sim_tape_wrrecf (uptr, cvptr->buffer, error_flag | cvptr->length);
 - sim_tape_rewind (uptr);

parameters are:
 - maximum record length (sim_tape_rdrecf, t_mtrlnt) -- [t_mtrlnt renames uint32]
 - gap size in tenths (sim_tape_wrgap, uint32)
 - error flag (sim_tape_wrrecf, t_mtrlnt)
other call parameters are inferred

--------------------------------------------------------------------------------

WRR: start -> traverse (if initial gap written) -> data -> stop

FSF: start -> traverse (if gap present) -> data (no transfer) -> stop

--------------------------------------------------------------------------------

confirm sim_tape EOM bug fix:

 - EOM at byte 0 first read
 - EOM at byte 0 second read
 - PEOF at first read
 - PEOF at second read

test XFERERROR in MS

--------------------------------------------------------------------------------

Tape library fatal errors:

MTSE_FMT:                                      /* illegal format */
   result = SCPE_FMT;                          /*   and stop with a Format error */

MTSE_UNATT:                                    /* unit unattached */
   result = SCPE_UNATT;                        /*   and stop with Unit not attached */

MTSE_INVRL:                                    /* invalid record length */
   result = SCPE_MTRLNT;                       /*   and stop with Invalid magtape record length */

MTSE_IOERR:                                    /* host system I/O error */
   result = SCPE_IOERR;                        /* this is a setting option for hp2100_ms (STOP_IOE) */

MTSE_WRP:                                      /* write protected (should never occur on a library call */
   result = SCPE_NO;                           /*   and stop with Read only operation not allowed */

default:                                       /* unanticipated error (e.g., a new error code was added) */
   result = SCPE_IERR;                         /*   and stop with an Internal error */

All of these return Tape Error status to abort the channel program.

Maybe add an SCPE function to return the error code via the event service call?
Resuming will continue into the Error_Phase to return Tape Error

Note that the host may retry, so must be prepared to issue the error again!

--------------------------------------------------------------------------------

DS controller does:

 - if uptr OR controller is Busy_State, then continue command
     if uptr is null AND controller unit phase is Idle_Phase, then exit
       else process; uptr = controller unit

 - else if CMRDY, then start command

MS controller does:

 - if uptr OR controller is Busy_State, then continue command
     always process; if uptr is null, then uptr = controller unit

 - else if CMRDY or CMXEQ, then start command

Maybe:
 - if uptr OR cvptr->state == Wait_State ?

MS call_controller:

 - [null] DRESETINT and CMRDY and ~IRQ (command held off until IAK)
    - start ONLY, NOT continue (cntlr may be busy, or selected unit may be rewinding!)
    - don't call if sio_busy, i.e., ~INTOK

 - [null] TOGGLESIOOK and ~SIOBUSY (poll drives held off by ~INTOK)
    - start and poll ONLY, NOT continue (selected unit may be rewinding!)
    - if sio_busy cleared, then controller can't be busy, although unit might be

 - [uptr] TOGGLEINXFER and INXFERFF (read command is waiting for channel start)
 - [uptr] TOGGLEOUTXFER and OUTXFERFF (write command is waiting for channel start)
    - continue command if selected unit is Wait_Phase, else command reject, NOT start
    - will enter continue if Wait_Phase because cntlr is busy
    - if cntlr not busy, must command reject (e.g., Control/SEL0 + Read)
    - won't enter continue or start; maybe reject if won't enter poll too?

 - [uptr] READNEXTWD (to abort a chained read that ended with DEVEND)
    - command reject ONLY (rejects because uptr->PHASE = Idle_Phase)
    - cntlr will be idle, because Read Record command has completed

==> add a "continue" flag?  that plus NULL uptr sets uptr to current unit?

 - [null] PCONTSTB (start a new command)
    - start ONLY, NOT continue (selected unit may be rewinding!)

 - [uptr] ms_service (continue the command, regardless of CMRDY)
    - continue command ONLY, NOT start, regardless of CMRDY
    - [OK: will continue]

 - [null] ms_attach and SCPE_INCOMP (to poll drives ONLY)
 - [null] ms_set_online and SCPE_INCOMP (to poll drives ONLY)
    - SCMP_INCOMP sets only if Idle_State
    - poll ONLY, NOT start, NOT continue (selected unit may be rewinding!)
    - [OK: will not continue because controller is idle]
    - poll only if INTOK?

==> INTOK wants to be ~SIOBUSY * ~INTREQ * ~INTACT!

won't poll unless SIO OK is true and (INTREQ + INTACT) is false
waits in tight loop for (INTREQ + INTACT) to go false

--------------------------------------------------------------------------------

Command rejection reasons:

   reject (1000; occurs on OTA, not STC):
     - unit not ready for any command requiring tape motion (i.e., other than Clear or Select)
     - write command with no write ring
     - Select while the controller is busy
     - BSR or BSF with tape at load point

   Reject reasons (3000; 28C from 2, 3, 14, 29, 30)
    - unit not ready for any command requiring tape motion (i.e., other than Select)
    - write command with no write ring
    - illegal command opcode
    - illegal bits set in control word
    - TOGGLEOUTXFER without a write data command in process [*]
    - TOGGLEINXFER without a read data command in process
    - PCONTSTB with input_xfer or output_xfer set
    - a command other than Clear while the controller is busy

    [*] so a WFM Control order followed by a Write order fails!

--------------------------------------------------------------------------------

Flags:
  - CMRDY = command is ready for examination
  - CMXEQ = command is ready for execution
  - DTRDY = write data is ready
  - EOD   = the data transfer has ended
  - INTOK = ?
  - OVRUN = ?
  - XFRNG = a channel error terminates the command in process

The 1000 interface does CMRDY on OTA and CMXEQ on STC.
The 3000 interface does CMRDY | CMXEQ on PCONTSTB.
The command is checked for validity with CMRDY and executed with CMXEQ.

Functions:

  - IFIN  = read data has been provided
  - IFOUT = write data has been accepted
  - IFGTC = the command has started
  - RQSRV = channel service has been requested
  - DVEND = a read transfer has ended before the word count expired
  - STINT = an interrupt has been requested

WRTIO won't work, because some of the status is dynamic.

--------------------------------------------------------------------------------

HP 3000:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | -   -   -   -   -   - | unit  | 0   0   0   0 | command code  |  PIO word 2
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

HP 1000:

      15 |14  13  12 |11  10   9 | 8   7   6 | 5   4   3 | 2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | -   -   - |     unit      | S |         command code          |  OTx
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     S = enable unit select

--------------------------------------------------------------------------------

Disc timing uses:

  typedef struct {
      CNTLR_TYPE   type;                          /* controller type */
      DRIVE_TYPE   drive;                         /* drive type */
      int32        seek_one;                      /* track-to-track seek time */
      int32        seek_full;                     /* full-stroke seek time */
      int32        sector_full;                   /* full sector rotation time */
      int32        data_xfer;                     /* per-word data transfer time */
      int32        intersector_gap;               /* intersector gap time */
      int32        overhead;                      /* controller execution overhead */
      } DELAY_PROPS;

...and:

    DELAY_PROPS       *fastptr;                 /* pointer to the FASTTIME delays */
    const DELAY_PROPS *dlyptr;                  /* current delay property pointer */

...in CNTLR_VARS.  The controller uses dlyptr exclusively.  dl_set_timing sets dlyptr
either to fastptr or to point at the desired entry in real_times:

    static const DELAY_PROPS real_times [CNTLR_COUNT];

This works because all drives on a given controller have the same delay times.
That isn't true for the 3000 tape controller, which mixes 7970 B/E units.  But
we could set dlyptr in start_command, as the tape controller stays connected to
a unit until the command completes (or until a rewind is started).

Note: disc separates DRIVE_PROPS from DELAY_PROPS.

Tape uses:

  typedef struct {
      CNTLR_TYPE    controller;                           /* controller model */
      DRIVE_TYPE    drive;                                /* tape drive model */
      uint32        density;                              /* tape drive density code */
      uint32        bpi;                                  /* recording density in bits per inch */
      uint32        gap_size;                             /* erase gap size in tenths of an inch */
      uint32        rewind_ipi;                           /* rewind speed in time per inch */
      int32         cntlr_time;                           /* controller command start delay time */
      int32         rewind_time;                          /* rewind initiation time */
      int32         bot_time;                             /* beginning of tape gap traverse time */
      int32         ir_time;                              /* interrecord traverse time */
      int32         data_time;                            /* data byte transfer delay time */
      } DRIVE_PROPS;

The last six fields are the timing values.

--------------------------------------------------------------------------------

The 13037 disc controller is a better simulation model than the current tapelib
model.  The latter requires a lot of knowledge within the interface, whereas the
former absracts the operations.  We could model the tape controller the same way,
i.e., with CNTLR_FLAGs and an IFN_BUS return (these would exist only in
simulation, without hardware counterparts).

ds is:
  CNTLR_IFN_IBUS dl_controller (CVPTR cvptr, UNIT *uptr, CNTLR_FLAG_SET flags, CNTLR_IBUS data)

ms maybe:
  uint16 ml_controller (CVPTR cvptr, UNIT *uptr, uint16 data)

with:
 - ml_start_command  =>  (t_bool) ml_controller (cvptr, NULL, opcode)
 - ml_service_drive  =>  read_data = ml_controller (cvptr, uptr, write_data)
 - eliminate data_register and data_flag, and add buffer_word to interface

but how do we communicate EOT, XFERERROR, etc.?

--------------------------------------------------------------------------------

Controller unit:
 - 3000 needs to delay interrupt (for drive stop time) from command reject
   e.g., Read issued, but all 4 drives are rewinding
 - 3000 needs to delay Unit Select (but only if we can't complete immediately)
 - for non-HPIB, is dev.units [dev.numunits - 1]
 - for HPIB, is dev.units [cvptr->unit_selected]
 - MAYBE THIS ISN'T NEEDED (reject delay can be handled locally in MS device)

1000:
 - crystal osc is 7.2 MHz
 - OTA/B outputs the command to the interface; STC starts it
 - Unit Select completes within OTA time (no STC used)
 - Clear requires STC and completes "almost immediately but does not exceed 10 ms" (13183 page 3-4)
 - Rewind not at BOT clears interface busy within 560 us (sets command FF)
 - Rewind at BOT is "ignored" but takes 560 us (1 SCP) to clear interface busy
 - Command reject sets on OTA; STC is ignored if reject is set, and the flag will not be set

 - NOTE: CRCC/LRCC diag tests are made on both reads and writes; does LIA DC
   to pick up CRCC (upper) and LRCC (lower) bytes.  Also uses RRR (read reverse)
   for explicit check for test 23 subtest 3.  RRR is not currently supported by MSC.
   RRR must read the PRECEDING record; but does tape mark, e.g., read the same backward?

3000:
 - microprocessor clock period is 434 ns
 - All commands are output and executed with PCONTSTB, including Unit Select
 - Unit Select typically takes about 4 usec.
 - Clear isn't a command but rather a signal
 - Command reject interrupt occurs 8.33 msec (tape stop time) after status is posted

DS states:
 - Idle_Phase
 - Parameter_Phase
 - Seek_Phase
 - Rotate_Phase
 - Data_Phase
 - Intersector_Phase
 - End_Phase

MS states [?]:
 - Idle_Phase
 - Init_Phase
 - Start_Phase
 - Traverse_Phase
 - Data_Phase
 - End_Phase

--------------------------------------------------------------------------------

   The 3000 tape controller microinstruction time is 434 nanoseconds.

   A Select Unit command executes in about 10 instructions from PCONTSTB to
   select output and SR assertion, which is about 1-2 CPU instructions.

   The unit status comprises all fields except SIO OK, Interrupt Request, and
   the Error Field.

   Per page 2-15 of the maintenance manual, during the idle state when no SIO
   program is active, the interface continuously selects one unit after another
   to look for a change from Not Ready to Ready status.  Therefore, the tape
   unit selected bits will be seen to change continuously.  [tested by the
   diagnostic?]

   All error codes cause the subsystem to terminate the I/O program in progress
   (by not issuing an SR) and cause an interrupt.  All error codes are cleared
   to the No Error state whenever a new SIO program is started.

   => An error termination occurs by not asserting SR and asserting INTREQ.
   That's effectively what happens when an I/O program executes an End/Interrupt
   instruction.

   Rejects and unit ready cause an interrupt.  Once an interrupt is asserted,
   the controller sits in a tight loop waiting for the interrupt to be reset.
   When it is, the controller returns to the idle loop and looks for the next
   command.

   Reject reasons (28C from 2, 3, 14, 29, 30)
    - illegal command or reserved bits not 0
    - unit not ready for any command other than Select
    - write cmd (WRFWD) with no write ring
    - INXFER or OUTXFER set when expecting a control order
    - OUTXFER set after read cmd control order
    - INXFER set after write cmd control order
    - control order received after read cmd (order must be Read)
    - control order received after write cmd (order must be Write)

   Interrupt conditions (28AA from 7, 24)
    - a tape unit goes NRDY->RDY (only checked if no SIO program)
    - tape runaway error
    - all other "interrupt conditions" (F15 set, i.e., cmd exit with error)

   INTER routine sets the Interrupt Request FF and sets the "Clear Bus Logic"
   (-CLRIL) signal.  Then the controller sits in a tight loop until the
   interrupt is reset (the test is Interrupt Request OR Interrupt Active).  Once
   it is, it returns to the Wait loop (stop tape and then go to Continue loop).

   While the controller is waiting for INTACK, a PCONTSTB will store the control
   word and will set the CMD ENB flip-flop.  So when the INTACK arrives and the
   controller returns to the main loop, the command will be recognized.

   when is error status, e.g., Unit Interrupt, cleared?  microcode does:
     - set unit interrupt FF
     - loop while interrupt_request + interrupt_active
     - reset unit interrupt FF
     - go to idle loop
   so unit interrupt is cleared when IRQREQ and IRQACT are both reset:
     + master_clear
     + DCONTSTB with CN_RIN and interrupt_active CLEAR
     + DRESETINT
   NOTE: while they're set, the controller is non-responsive to commands.
     Once an interrupt is requested, the microcode sits in a tight loop
     until the interrupt status bit is cleared.  During this time, a new command
     will be accepted but will not run until interrupt servicing completes.

   Reset conditions:

     1. "Clear bus logic"
     2. "Master reset"

   A clear bus logic is generated either by a microprocessor D02 (XFACE) output
   to MB6 or by a master reset.  A CBL:

    - clears the ESR, SIO OK, INXFER, OUTXFER, SR, and SRB flip-flops

   A master reset is generated either by an I/O Reset signal or a Programmed
   Master Clear (CIO bit 0).  It performs these actions:

    - clears the PAR ERR, XFER ERROR, INT ACT, and INT REQ flip-flops
    - clears the unit select flip-flops, selecting unit 0
    - sets the MASK flip-flop
    - asserts CLR to the microprocessor, restarting it from ROM address 0
    - clears the SIO OK, INXFER, OUTXFER, SR, and SRB flip-flops (same as CBL,
      except that ESR is not cleared)

   The controller start code is entered at power up or after a master reset.
   It:
    - clears unit ready flip-flops and bus logic
    - selects unit 0
    - stops tape operations

   The controller continue loop:
    - sits in a loop until IRQ is clear

   The controller main loop:
    - if INXFER or OUTXFER FF is set, go to Reject (2)
    - if no PCONSTB, scans a unit for NRDY->RDY change and then loops

   If an interrupt is to be generated:
    - sets INTREQ FF
    - sits in a tight loop until IRQ is clear
    - stops tape operation
    - go to Continue loop

   Once a PCONSTB is seen:
    - clear status and odd byte FF
    - if Select:
      - select indicated unit
      - assert DSR
      - go to Continue
    - if unit not ready, go to Reject (3)
    - if reserved bits (8:11) are not 0000, go to Reject (3)
    - if RR, RRC, FSF, or FSR, go to RDFWD (14H)
    - if WR, GAP, WRZ, or WFM, go to WRFWD (29J)
    - if cmd = 01-03 (illegal cmd), go to Reject (3)
    - if BSR or BSF, go to RDBKD (9L)
    - if Write Status set, erase a bit of tape before continuing
    - must be REW or RWO; start rewinding and set unit offline if RWO
    - set DSR to get next command
    - return to Continue loop

   Scan loop:
    - returns to Main if SIO FF set
    - sits in a loop until IRQ is clear
    - select next unit in a round-robin fashion
    - if NRDY->RDY change, set interrupt, else return to main loop

   Read commands (RDR, RDC):
    - set DSR
    - if PCONSTB or OUTXFER, cmd reject
      else loop while waiting for INXFER
    - read and transfer data
    - if data overrun, set timing error but continue through record
    - if tape mark, set EOF, no error
    - if word count > record length, set DEVEND, no error

   Write commands (WR, WRZ, GAP, WFM):
    - if write protected, reject
    - if WR or WRZ, set DSR
      (if PE drive, WRZ decodes as WR)
      if PCONSTB or INXFER, cmd reject
      else loop while waiting for OUTXFER
    - if LP, write initial gap (3.75" NRZI/PE)
    - if WR or WRZ, read and transfer data
    - if GAP, write gap (3.75" NRZI/PE)
    - if WFM, write file mark

   BSF, BSR commands:
    - if at LP, ignore, else move

   Read commands and data chaining:
    - Control order, RR
    - Read order must follow; if Write or Control order, cmd reject
    - at EOR, if word count > record length, sets DEVEND, else sets DSR;
      returns to continue loop

   Per page 2-11 of the maintenance manual, if data chaining is active, and a
   chained read completes with a record shorter than the transfer length, a
   Command Reject will occur.  This is because DEVEND for a chained read leaves
   INXFER set, and the command wait loop checks for INXFER (or OUTXFER) set and
   will abort if it is.

   Hardware does not use EOT.

--------------------------------------------------------------------------------

debug logging:

DS  iobus: Received data 001057 with signals PWRITESTB | TOGGLEOUTXFER | EOT
DS   csrw: Control is 001000 (Seek)
DS  state: Controller unit Seek parameter phase interface entry
DS    ops: Unit 0 Seek to cylinder 410 head 2 sector 47
DS   cmds: Unit 0 Seek command completed with Normal Completion status
DS   serv: Unit 0 Seek seek phase delay 41000 service scheduled
DS    ops: Unit 0 Initialize of 6144 words (48 sectors)
DS   xfer: Unit 0 Initialize word 1 is 000000
DS   csrw: Status is Normal Completion | unit 0
DS   csrw: Channel program ended

MS   cmds: Channel program started
 >>   csrw: Channel program started
MS  iobus: Received data 000000 with signals ACKSR | PCONTSTB
 >> ok
MS   cmds: Unit 0 control is Write Record
 >>   csrw: Control is master clear
 >>   csrw: Control is Select Unit 2
 >>   csrw: Control is Write Record | unit 2
MS   serv: Unit 0 delay 10 initiation phase service scheduled
 >>   serv: Unit 0 Write Record initiation phase delay 10 service scheduled
MS   serv: Unit 0 Write Record initiation phase service entered
 >>  state: Unit 0 Write Record initiation phase service entered
MS   cmds: Unit 0 Write Record tape command started at position 6180
 >>   cmds: Unit 0 Write Record command started at position 6180
MS new:
 >>   xfer: Unit 0 Write Record word 1 is 041511
MS   xfer: Unit 0 write of 12-word record succeeded
 >>    ops: Unit 0 Write Record of 12 words
MS   cmds: Unit 0 Write Record tape command completed at position 6212
 >>   cmds: Unit 0 Write Record command completed at position 6212 with No Error status


for %s in (func cmd csrw phase serv xfer iobus) do findstr /r /c:"DS  *%s:" debug-ds.log > ds-%s.log
for %s in (func cmd csrw phase serv xfer iobus) do findstr /r /c:"MS  *%s:" debug-ms.log > ms-%s.log

--------------------------------------------------------------------------------

7970 status supplied by the drive:
 - SL = status online
 - SR = status ready (selected * online * ~loading * ~rewinding)
 - SLP = status load point (selected * online * at-load-point)
 - SD = status density SD2 (200), SD5 (556), SD8 (800), SD16 (1600) bpi (7970E only)
 - SRW = status rewind (selected * online * rewinding)
 - SFP = status file protect (selected * online * ~write-ring)
 - SET = status end-of-tape (selected * online * EOT-seen-forward (reset when EOT-seen-reverse)
 - SW = status write (selected * online * cmd-set-write * cmd-fwd)
 - S7 = status 7-track (7970B only)
 - EOB = end-of-block (7970E only)
 - TM = tape-mark (7970E only)
 - MTE = multiple-track-error (7970E only)
 - STE = single-track-error (7970E only)

--------------------------------------------------------------------------------

ANSI X3.22-1973  Recorded Magnetic Tape for Information Interchange (800 bpi NRZI)
ANSI X3.39-1973  Recorded Magnetic Tape for Information Interchange (1600 bpi PE)
ANSI X3.54-1976  Recorded Magnetic Tape for Information Interchange (6250 bpi GCR)

ECMA-36          Data Interchange on 9-track Phase-encoded Magnetic Tape at 63 bits/mm (1600 bpi)
ECMA-62          Data Interchange on 12.7mm 9-track Magnetic Tape

   CRCC calculation:

   from ECMA-12:
   =============
     accum := 0
     buffer (length + 1) := 0 (for write) or CRCC (from read)

     for next in 1 .. length loop
       byte := buffer (next)
       val := byte OR oddparity (byte) SHL 8

       temp := val XOR accum
       accum := (temp SHR 1) OR (temp AND 1 SHL 8) -- 9-bit right rotate

       if accum AND 8#400 = 1
         then accum := accum XOR 8#74
     end loop

     if writing
       then accum := accum XOR 8#727


   from 13181 diag (returns CRCC in upper byte):
   =============================================
     accum := 0
     buffer (length + 1) := 0 (for write) or CRCC (from read)

     for next in 1 .. length loop
       byte := buffer (next)
       val := byte OR oddparity (byte) SHL 8

       if accum AND 8#400 = 0
         then temp := val XOR accum
         else temp := val XOR accum XOR 8#74

   8553  15475 000040        CLE

   | - |     |15 |14 |13 |12 |11 |10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |

   8556  15476 001500        ERA           MOVE BIT 0 TO E

   | 0 |     | - |15 |14 |13 |12 |11 |10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |

   8557  15477 001727        ALF,ALF       SHIFT TO UPPER BYTE

   | 0 |     | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | - |15 |14 |13 |12 |11 |10 | 9 |

   8558  15500 001522        ERA,RAL       INSERT BIT 7 IN PARITY

   | 9 |     | 0 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | - |15 |14 |13 |12 |11 |10 |
   | 9 |     | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | - |15 |14 |13 |12 |11 |10 | 0 |

   8559  15501 001727        ALF,ALF       SHIFT TO LOWER BYTE

   | 9 |     | - |15 |14 |13 |12 |11 |10 | 0 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |  -- 9-bit right rotate

       accum := (temp SHR 1) OR (temp AND 1 SHL 8) -- 9-bit right rotate
     end loop

     temp = accum XOR 8#753

   | - |     | o | o | o | o | o | o | o | i | i | i | i | o | i | o | i | i |  = OCT 753
   | - |     |15 |14 |13 |12 |11 |10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |

   8488  15432 006500        CLB,CLE       CLEAR B AND E REG
   8490  15433 001727        ALF,ALF       MOVE TO UPPER BYTE

   | - |     | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |15 |14 |13 |12 |11 |10 | 9 | 8 |

   8491  15434 001640        ELA,CLE       DROP OUT PARITY

   | 7 |     | 6 | 5 | 4 | 3 | 2 | 1 | 0 |15 |14 |13 |12 |11 |10 | 9 | 8 | - |
   | - |     | 6 | 5 | 4 | 3 | 2 | 1 | 0 |15 |14 |13 |12 |11 |10 | 9 | 8 | - |

   8492  15435 001325        RAR,ERA       SAVE BIT 0

   | - |     | - | 6 | 5 | 4 | 3 | 2 | 1 | 0 |15 |14 |13 |12 |11 |10 | 9 | 8 |
   | 8 |     | - | - | 6 | 5 | 4 | 3 | 2 | 1 | 0 |15 |14 |13 |12 |11 |10 | 9 |

   8493  15436 001727        ALF,ALF       MOVE TO LOWER BYTE

   | 8 |     | 0 |15 |14 |13 |12 |11 |10 | 9 | - | - | 6 | 5 | 4 | 3 | 2 | 1 |

   8494  15437 001226        RAL,ELA       INSERT BIT 0

   | 8 |     |15 |14 |13 |12 |11 |10 | 9 | - | - | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
   |15 |     |14 |13 |12 |11 |10 | 9 | - | - | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 8 |

   8495  15440 001727        ALF,ALF       SHIFT TO UPPER BYTE

   |15 |     | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 8 |14 |13 |12 |11 |10 | 9 | - | - |

   13181 diag routine expects exact match to CRCC from tape (without parity bit).


   from 30215 microcode:
   =====================
     accum := 0
     buffer (length + 1) := 0 (for write) or CRCC (from read)

     for next in 1 .. length loop
       byte := buffer (next)
       val := byte OR oddparity (byte) SHL 8

       accum := val XOR accum ROR 1

       if accum AND 8#100000 = 0
         then accum := accum AND 8#377
         else accum := accum AND 8#377 OR 8#400 XOR 8#74
     end loop

     if writing
       then accum := accum XOR 8#327   -- gens own parity?
       else accum := accum XOR 8#727

--------------------------------------------------------------------------------

resolution:

 - cvars has only fast_controller_time, fast_start_time, fast_data_time, and fast_end_time (and fast_rewind_time?)
   these can be set by the user when FASTTIME; user cannot see or change REALTIME values
   power-on reset will set these values to defaults
 - cvars has HRO data_time and end_time
 - ml_start_command sets wait = controller_delay
 - start phase entered after controller delay
   calculates and sets wait = start time
   calculates and sets data_time, end_time as function of drive type, density, record size, etc.
 - data phase entered after start time (drive up to speed and positioned after IRG)
   sets wait = data time or end time if EOD
   if data, decrements end time by data time
 - end phase entered after end time (IRG and stop time)

   simulation action                hardware action
   -----------------------------    ----------------------------------
 * ml_start_command                 PCONTSTB
   ...wait for controller_time      microcode execution time
 * ml_service (start_phase)         tape motor started
   ...wait for start_time           ramp to speed, space over preamble
 * ml_service (data_phase)  <--+    read data bytes
   ...wait for data_time       |    space tape to next byte
              -----------------+
   ...wait for end_time             space over postamble, ramp to stop
 * ml_service (end_phase)           tape motor stopped


 - don't want SET <unit> 7970E, e.g., in common MTAB, or it'll appear as a choice for the 13181
   (ditto for 7974 and the 30115).  These MTABs want to be per-simulator, so only applicable
   choices appear.

   alternate would be to keep in common MTAB but set "mstring" and "pstring" to NULL during
   power-up reset if not enabled for the controller.

 - instead of SET 7974, maybe SET 7974-800 and SET 7974-1600?  elim need for separate
   SET <unit> DENSITY=800.

   alternate would be to use u3 as model-density word.  getting props would be easier
   if each model/density had its own index value


unit flags:
 - scp :  0-15 (16 bits)
 - user: 16-31 (15 bits)
    - mt 16-20 ( 5 bits)
    - ml 21-27 ( 3 bits) offline (1), model (2)
    - ms 28-31 ( 4 bits) free

--------------------------------------------------------------------------------

Delay time derivation:

   Tape drive specifications:

               Start   Stop    R/W   Rewind   Load    Byte  
               Time    Time   Speed  Speed   Speed    Time  
       Model   msec    msec   in/sec in/sec  in/sec   usec  
     +-------+-------+-------+------+-------+------+--------+
     | 7970B |  8.33 |  8.33 |  45  |  160  |  20  |  27.78 |
     | 7970E |  8.33 |  8.33 |  45  |  160  |  20  |  13.89 |
     | 7974A |       |       |      |       |      |        |
     | 7978A |       |       |      |       |      |        |
     +-------+-------+-------+------+-------+------+--------+

   Controller specifications:

               Rewind   Start    Start  
                Init    at BOT   at IRG 
       Model    usec     msec     msec   
     +-------+--------+--------+--------+
     | 13181 |   556  | 102.22 |   8.89 |
     | 13183 |   556  | 160.00 |  11.11 |
     | 30215 |        |        |        |
     | HP-IB |        |        |        |
     +-------+--------+--------+--------+

   1318x controller times are based on multiple Spacing Clock Pulses (SCPs) of
   555.56 microseconds each.


30115	7970B + 7970E, 3.5" gap
13181	7970B, 4.8" gap
13183	7970E, 3.0" gap
hp-ib	7970B or 7970E or 7974 or 7978

7970
 - start time = 8.33 msec
 - stop time  = 8.33 msec
 - read/write speed = 45 ips
 - rewind speed = 160 ips
 - rewind start/stop time = 0.7 sec
 - load speed = 20 ips
 - word xfer time = 55.56 usec (B), 28.78 usec (E)

PE
 - start gap = 3.0 in min (13183 = 7.2"; 30115 = 2.0" id burst + 3.75" erase gap)
 - interrecord gap = 0.5 in min 0.6 in nom
 - pre/postamble = 41 bytes
 - erase gap = 3.75" (13183 = 3.0"; 30115 = 3.75")

NRZI
 - start gap = 0.5 in min (13181 = 4.6"; 30115 = 3.75" erase gap)
 - interrecord gap = 0.6 in
 - erase gap = 3.75 in min (13181 = 4.8"; 30115 = 3.75")

all times could be controller-dependent EXCEPT for the 30115
30115 is only controller that supports two drive models
all others (incl. HP-IB) support only one type of drive

times that depend on the drive:
 - start time (PE has an extra 2.0" to start from BOT)
 - data xfer (depends on drive density setting)
 - irg (PE is longer due to pre/postamble)

Write time:
 > cntlr_delay
 + if BOT then bot_delay (+ idb_time if PE) else start_delay
 + pre_time (if PE)
 + data_time * recsize (* 1/2 if PE)
 + post_time (if PE)
 + stop_delay

1000 diag timing tests:
 - rewind init (cmd -> flag set)
 - write init (cmd -> flag clear @ 1.0x and set @ 1.25x)
 - clear (cmd -> flag set)
 - gap (cmd -> unit busy @ 1.0x and not busy @ 1.25x)
 - irg (read cmd -> flag clear @ 1.0x and set @ 2.0x)
 - byte time (min/max)

defined times:		B	E
 - controller delay	c	c
 - BOT gap time		c	c
 - ID burst time	0	n
 - start/stop time	c	c
 - pre/postamble time	0	n
 - data time @ 800bpi	x	x/2
 - rewind init time	c	c


   7970 properties:
    - load point search speed = 20 ips
    - read/write speed = 45 ips
    - rewind speed = 160 ips
    - rewind stop travel = 31"
    - read/write start/stop travel = 0.187" +/- 0.020"
    - read/write start/stop time = 6.6 msec nominal

   30215 specs:
    - data xfer rate = 72000 bytes/sec (E) or 36000 bytes/sec (B)
    - data xfer time = 13.889 usec/byte (E) or 27.778 usec/byte (B)
    - read start time = 8.33 msec max
    - write start time = 10.1 msec max
    - read/write stop time = 8.33 msec max
    - read start travel = 0.187" nominal
    - write start travel = 0.267" nominal
    - read/write stop travel = 0.187" nominal
    - irg length = 0.5" nominal
    - PE preamble and postamble length = 41 bytes
    - NRZI postamble = 8 bytes (CRCC and LRCC)

    - ID Burst length = 2" (PE)
    - erase gap length (including initial gap) = 3.75"
    - PE file mark length = 40 bytes
    - NRZI file mark length = 8 bytes

    - nominal IDB time = 44.445 msec = 17,778 instr
    - remaining IDB time after start = 2.511 msec = 1004 instr
    - nominal gap time (E) = 83.334 msec = 33,334 instr
    - nominal start/stop (IRG) time = 9.73 msec = 3892 instr
    - nominal pre/postamble time = 569.44 usec = 228 instr

   Note: a minimum of 1.7" of the 2" IDB overlaps the BOT reflective marker.  So
   only a maximum of 0.3" of the IDB appears after Load Point marker is cleared.
   So the additional time incurred by a BOT start = .113" = 2.511 msec.

    - bot start time = start + remaining IDB
    - non-bot start time = start


   30215 microcode delays (1 count = 434 nsec):
    - coast delay after command = 376B counts = 103 usec
    - read tape start delay = 45400B counts = 8333 usec
    - write tape start delay = 55400B counts = 10110 usec
    - tape stop delay = 45400B counts = 8333 usec

   Hardware timing at 45 IPS                  13181                  13183
   (based on 1580 instr/msec)          instr   msec    SCP   instr    msec    SCP
                                      --------------------   --------------------
   - BOT start delay        : btime = 161512  102.22   184   252800  160.00   288
   - motion cmd start delay : ctime =  14044    8.89    16    17556   11.11    20
   - GAP traversal time     : gtime = 175553  111.11   200   105333   66.67   120
   - IRG traversal time     : itime =  24885   15.75     -    27387   17.33     -
   - rewind initiation time : rtime =    878    0.56     1      878    0.56     1
   - data xfer time / word  : xtime =     88   55.56us   -       44  27.78us    -

   NOTE: The 13181-60001 Rev. 1629 tape diagnostic fails test 17B subtest 6 with
         "E116 BYTE TIME SHORT" if the correct data transfer time is used for
          13181A interface.  Set "xtime" to 115 (instructions) to pass that
          diagnostic.  Rev. 2040 of the tape diagnostic fixes this problem and
          passes with the correct data transfer time.

   PE bot time is motor start time + ID burst time (PE) + preamble time
   PE ir  time is motor start time + 1/2 IRG time + preamble time

   NRZI bot time is motor start time
   NRZI ir time is motor start time

   for NRZI, an erase gap follows the BOT marker and precedes the first data record

   rd/wr:
    start delay = if bot then bot_time else ir_time
    data delay  = data_time
    end delay   = ir_time

  rwnd:
    start delay = rew_time;
    end delay   = (byte_pos / bpi) / ips
    ips = 400000 instr/sec / 160 inches/sec = 2500 instr/inch

   Commands are grouped into three categories: read forward, read backward, and
   write (forward).  If the current and prior commands are in the same group,
   tape motion will continue if the new command arrives within 103 microseconds.

--------------------------------------------------------------------------------

Next:
 - create MX device for 1000; use tapelib, check with diag
 - add RRR for 1000 (call sim_tape_rdrecr but note buffer is fwd, not rev!)