HP 21xx/1000 Simulation Notes
                          =============================


------------------
Instruction Timing
------------------

Currently, the simulator treats all instructions as having the same execution
time: one event tick.  The interval timer is decremented once per instruction
just prior to calling the instruction executor.  DMA cycles are not counted
toward event time.  That means that interfaces that can assert SRQ continuously,
e.g., the 13175 Disc Interface, perform block transfers in zero time.

At some point, it may be desirable to recast event ticks as microseconds and add
variable consumption for certain instructions.  For that, the interval timer
would have to be adjusted AFTER the instruction execution, not before.

It would appear that adjusting the time before, as is currently done, would
result in "sim_activate" calls for DMA and CPU cycles producing different
delays.  Consider the current execution loop:

  loop
    if sim_interval <= 0
      then do_event_service

    if dma_request
      then do_dma_cycle

    sim_interval := sim_interval - 1

    do_machine_instruction
  end loop

If "sim_activate" is called during STC processing for a DMA cycle, the interval
timer is decremented before the next interval test is done.  However, if it is
called for a CPU cycle, the timer is not decremented before the next test.  This
would seem to imply that the event would be serviced one tick later for the CPU
cycle than for the DMA cycle.  But this doesn't happen.

Here is the DMA cycle trace (assuming a preceding STF 44) for a device that does
a "sim_activate (uptr, 2)" in response to STC:

  >>CPU   instr: - 0001 03361  103706  STC 6,C
  >>DCPC1    sr: Select code 44 asserted SRQ
  >>MC    iobus: Received data 000000 with signals IOI | STC | CLF | SIR
  >>MC    iobus: Returned data 125252 with signals (none)
  >>DCPC1  data: - 0000 00172  125252    dma write
  >>CPU   fetch: - 0001 03362  060172    instruction fetch
  >>CPU     reg: - **** 00000  000000    A 052525, B 177776, X 000000, Y 000000, E O i
  >>CPU   instr: - 0001 03362  060172  LDA 172
  >>CPU    data: - 0000 00172  125252    data read
  >>CPU   fetch: - 0001 03363  064242    instruction fetch
  >>CPU     reg: - **** 00000  000000    A 125252, B 177776, X 000000, Y 000000, E O i
  >>CPU   instr: - 0001 03363  064242  LDB 242
  >>CPU    data: - 0000 00242  125252    data read
  >>MC    iobus: Received data 000000 with signals ENF | SIR
  >>MC    iobus: Returned data 000000 with signals SRQ
  >>DCPC1    sr: Select code 44 asserted SRQ

And here is the CPU cycle trace:

  >>CPU instr: - 0000 00100  103744  STC 44,C
  >>MC  iobus: Received data 000000 with signals STC | CLF | SIR
  >>MC  iobus: Returned data 000000 with signals (none)
  >>CPU fetch: - 0000 00101  000000    instruction fetch
  >>CPU   reg: - **** 00000  000000    A 000000, B 000000, X 000000, Y 000000, E o i
  >>CPU instr: - 0000 00101  000000  NOP
  >>CPU fetch: - 0000 00102  000000    instruction fetch
  >>CPU   reg: - **** 00000  000000    A 000000, B 000000, X 000000, Y 000000, E o i
  >>CPU instr: - 0000 00102  000000  NOP
  >>MC  iobus: Received data 000000 with signals ENF | SIR
  >>MC  iobus: Returned data 000000 with signals SRQ

In both cases, two instructions execute before the event service is entered and
the flag is set.

If the decrement occurs after the instruction execution:

  loop
    if sim_interval <= 0
      then do_event_service

    if dma_request
      then do_dma_cycle

    do_machine_instruction

    sim_interval := sim_interval - 1
  end loop

...then a DMA cycle STC would execute two instructions before calling the event
service routine, but a CPU cycle STC would execute only one instruction.



----------------
Simulation Stops
----------------

STOP_UNIMPL  = Unimplemented instruction
STOP_UNSC    = I/O to an unassigned select code
STOP_UNDEF   = Undefined instruction
STOP_INDIR   = Indirect address loop
STOP_HALT    = Programmed halt
STOP_BRKPNT  = Breakpoint
STOP_NOCONN  = No connection on interprocessor link
STOP_NOTAPE  = No tape in paper tape reader
STOP_EOT     = End of tape in paper tape reader
NOTE_IOG     = I/O instruction executed
NOTE_INDINT  = Interrupt occurred while resolving indirects

I/O error returns from device service routines are supplied by:

  - DR  (SCPE_UNATT)
  - LPS (SCPE_UNATT, STOP_OFFLINE, STOP_PWROFF)
  - MS  (SCPE_UNATT, SCPE_IOERR)
  - MT  (SCPE_UNATT, SCPE_IOERR)
  - PTR (SCPE_UNATT, SCPE_IOERR)
  - PTP (SCPE_UNATT, SCPE_IOERR)
  - TTP (SCPE_IOERR)

DR is probably the only one that returning SCPE_UNATT makes sense, as it's a
fixed-disc drive and so is always "attached" (although it could report "not
ready" instead).

SCPE_IOERR is indicated for MTSE_IOERR, which is returned if ferror() returns
TRUE after a stream file I/O call.  This is translated to parity error status if
stop_ioe is not set.

SCPE_IOERR is returned by PTR for stream I/O errors as well as reading more than
the trailer limit past the physical EOF.  The latter is not conditional on
stop_ioe.

SCPE_IOERR is unconditionally returned by PTP and TTP for stream I/O errors.



-----------
CPU Tracing
-----------

The CPU should implement the following traces:

  INSTR -- Machine instructions executed
  DATA  -- Memory data accesses
  FETCH -- Memory instruction fetches
  REG   -- Register values
  OPND  -- Memory operand values
  EXEC  -- Matching instruction execution states

FETCH, INSTR, and REG tracing is implemented in the "sim_instr" routine.

There are two places where an instruction fetch is performed: in the "normal"
instruction path, and in the interrupt path.  Both are calls to ReadW.  The
paths merge immediately thereafter, and the REG and INSTR trace calls are made
there.  The forms are:

  >>CPU fetch: - ---- 10341  016200    instruction fetch
  >>CPU   reg: P 0000 00000  000000    A 123003, B 001340, X 000000, Y 000000, e O I
  >>CPU instr: U 0045 10341  016200  LDA 11200
  >>CPU instr: U 0045 10341  000011  interrupt
  >>CPU   reg: P 0000 00000  000000    MPF 00000, MPV 000000, MES 000000, MEV 000000

The REG trace prints A/B/E/O/I for 21xx machines and A/B/X/Y/E/O/I for 1000
machines.

EXEC replaces, and is a more general than, the existing OS, OSTBG, VMA, EMA,
VIS, and SIG traces.  OSTBG might remain a separate option (renamed as PTBG) but
would require that .TICK (105342) be filtered out of the rest of the OS
instruction range.

DATA traces requires a rewrite of the memory access routines to capture all
memory accesses.  Currently, there are individual routines for each type of
access:

  - ReadPW  : Read a word using a physical address
  - ReadB   : Read a byte using the current map
  - ReadBA  : Read a byte using the alternate map
  - ReadW   : Read a word using the current map
  - ReadWA  : Read a word using the alternate map
  - ReadIO  : Read a word using the specified map without protection
  - ReadTAB : Read a word using the current map without protection

...plus corresponding Writexx routines.  The routines are used for these types
of access:

  - ReadPW  : drc_boot (loader area), Readxx, Writexx routines
  - ReadB   : MBI, LBT, MBT, CBT, SFB, TRSLT, .TNAM
  - ReadBA  : MBF, MBW
  - ReadW   : memory reference instructions
  - ReadWA  : MWF, MWW, XLn, XCn, EMA/VMA, .IRT
  - ReadIO  : dma_cycle (portmaps), sim_stop (smap), EMA/VMA (smap and umap)
  - ReadTAB : sim_instr postlude (current map), hp_post_cmd (current map)

DMA accesses via ReadIO and WriteIO access memory locations 0 and 1 and not the
A and B registers.  sim_stop reads the instruction in the trap cell if an
interrupt is pending; the current map is indeterminate at this point, although
trap cells reside in page 0 locations 2-77 octal, and the system map base page
always has these locations mapped to page 0.  EMA/VMA instructions require both
explicit and current map accesses.

ReadTAB is used only to set the T register when simulation stops and when any
command completes that changes the M register.  It's identical to ReadW, except
that a DMS violation cannot occur.

Note: drc_boot can use ReadW instead of ReadPW, as DMS is guaranteed to be off
when it is called due to the RESET ALL that is done prior to calling.

The following actions do NOT want to trace the memory accesses:

  - sim stop with an interrupt pending
  - T register set
  - ibl_copy

ibl_copy could be moved to the memory module and access M [] directly.  The
others can call mem_examine with the switches parameter set to the value
appropriate to the mapping desired (the EXAMINE and DEPOSIT commands do A/B
mapping; there is no way to access page 0 locations 0/1).

The calling sequences for memory access are:

  uint16 ReadPW     (uint32 pa);
  uint8  ReadB      (uint32 va);
  uint8  ReadBA     (uint32 va);
  uint16 ReadW      (uint32 va);
  uint16 ReadWA     (uint32 va);
  uint16 ReadIO     (uint32 va, uint32 map);
  uint16 ReadTAB    (uint32 va);
  void   WritePW    (uint32 pa, uint32 dat);
  void   WriteB     (uint32 va, uint32 dat);
  void   WriteBA    (uint32 va, uint32 dat);
  void   WriteW     (uint32 va, uint32 dat);
  void   WriteWA    (uint32 va, uint32 dat);
  void   WriteIO    (uint32 va, uint32 dat, uint32 map);

The new common memory access routines are declared as:

  t_stat mem_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches);
  t_stat mem_deposit (t_value value,       t_addr address, UNIT *uptr, int32 switches);

  HP_WORD mem_read  (DEVICE *dptr, ACCESS_CLASS classification, uint32 logical_address);
  void    mem_write (DEVICE *dptr, ACCESS_CLASS classification, uint32 logical_address, HP_WORD value);

The following memory access classifications and DMS mappings are used:

  fetch    : current map
  data     : current map
  data_alt : alternate map
  abs_sys  : system map    (unprotected)
  abs_user : user map      (unprotected)
  dma_1    : port a map    (unprotected, no A/B access)
  dma_2    : port b map    (unprotected, no A/B access)



-------------
Trace Options
-------------

  Option Flag  Trace Description
  -----------  -----------------------------------------------------------
  TRACE_CMD    Interface or controller commands
  TRACE_INCO   Interface or controller command initiations and completions
  TRACE_CSRW   Interface control, status, read, and write actions
  TRACE_STATE  Command execution state changes
  TRACE_SERV   Unit service scheduling calls and entries
  TRACE_PSERV  Periodic unit service scheduling calls and entries
  TRACE_XFER   Data receptions and transmissions
  TRACE_FIFO   FIFO data buffer reads and writes
  TRACE_IOBUS  I/O bus signals and data words received and returned

  TRACE_INSTR  Instruction executions
  TRACE_DATA   Memory data accesses
  TRACE_FETCH  Memory instruction fetches
  TRACE_REG    Register values
  TRACE_OPND   Instruction operands
  TRACE_EXEC   Matching instruction execution states
  TRACE_SR     DMA service requests received

  DEB_CMDS     Command initiations and completions
  DEB_CPU      Words received from and sent to the CPU
  DEB_BUF      Data read from and written to the FIFO
  DEB_XFER     Data receptions and transmissions
  DEB_RWS      Tape reads, writes, and status returns
  DEB_RWSC     Disc read/write/status/control commands
  DEB_SERV     Unit service scheduling calls and entries



-----------
Trace Flags
-----------

Note that the CPU does not use the IOBUS flag.  One problem is that the CPU
device hosts SC 00 (interrupt system), SC 01 (overflow) and SC 04 (power fail),
so IOBUS would trace all of these together.  Even so, it might seem helpful to
trace operations.  However, there is no information in the received data or
signals to indicate which select code is being shown.  Also, each TBG interrupt
results in a half-dozen IOBUS transactions on the interrupt system (SC 00),
overflow (SC 01) and CIR (SC 04), so the unidentified traces quickly pile up.

A trace of this activity that yields more useful information is an EXEC trace
for the I/O instructions refererncing the lower select codes:

  SET CPU EXEC=102000;172070,DEBUG=EXEC

This traces I/O instructions targeted to select codes 00-07.

In the tables below, the values of global flags start at bit 0 and increase,
while private flags start at bit 30 and decrease.

 Flag Name   P/G   Trace Decription
-----------  ---   -----------------------------------------------------------
TRACE_CMD     G    Interface or controller commands
TRACE_INCO    G    Interface or controller command initiations and completions
TRACE_CSRW    G    Interface control, status, read, and write actions
TRACE_STATE   G    State changes
TRACE_SERV    G    Unit service scheduling calls and entries
TRACE_PSERV   G    Periodic unit service scheduling calls and entries
TRACE_XFER    G    Data receptions and transmissions
TRACE_FIFO    G    Data buffer reads and writes
TRACE_IOBUS   G    I/O bus signals and data words received and returned

DEB_CMDS      G    (old) command initiations and completions
DEB_CPU       G    (old) words received from and sent to the CPU
DEB_BUF       G    (old) data read from and written to the FIFO
DEB_RWSC      G    (old) disc read/write/status/control commands

TRACE_INSTR   P    Instruction executions
TRACE_DATA    P    Memory data accesses
TRACE_FETCH   P    Memory instruction fetches
TRACE_REG     P    Register values
TRACE_OPND    P    Instruction operands
TRACE_EXEC    P    Matching instruction execution states
TRACE_SR      P    DMA service requests received


        ----------------------- Global ----------------------------- | ------------------- Private -------------------
Device   CMD   INCO   CSRW   STATE  SERV   PSERV  FIFO  XFER   IOBUS | INSTR  DATA   FETCH   REG   OPND   EXEC    SR
------  -----  -----  -----  -----  -----  -----  ----  -----  ----- | -----  -----  -----  -----  -----  -----  -----
 CPU      -      -      -      -      x      -     -      -      -   |   x      x      x      x      x      x      -
 DMA1     x      -      x      -      -      -     -      -      x   |   -      x      -      -      -      -      x
 DMA2     x      -      x      -      -      -     -      -      x   |   -      x      -      -      -      -      x
 MP       -      -      -      -      -      -     -      -      x   |   -      -      -      -      -      -      -
 MEM      -      -      -      -      -      -     -      -      -   |   -      -      -      -      -      -      -

        ----------------------- Global ----------------------------- | ------- Deprecated -------
Device   CMD   INCO   CSRW   STATE  SERV   PSERV  FIFO  XFER   IOBUS | CMDS    CPU    BUF   RWSC
------  -----  -----  -----  -----  -----  -----  ----  -----  ----- | -----  -----  -----  -----
 BACI     -      -      x      -      x      x     x      x      x   |   -      -      -      -
 DA       x      x      x      x      x      -     x      x      x   |   -      -      -      -
 DC       x      x      x      x      x      -     x      x      x   |   -      -      -      -
 DPC      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 DPD      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 DQC      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 DQD      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 DRC      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 DRD      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 DS       -      -      -      -      x      -     -      -      x   |   x      x      x      x
 IPLI     x      -      x      -      -      x     -      x      x   |   -      -      -      -
 IPLO     x      -      x      x      -      x     -      x      x   |   -      -      -      -
 LPS      x      -      x      x      x      -     -      x      x   |   -      -      -      -
 LPT      x      -      x      -      x      -     -      x      x   |   -      -      -      -
 MC       -      -      -      -      -      -     -      x      x   |   -      -      -      -
 MPX      x      x      x      x      x      x     x      x      x   |   -      -      -      -
 MSC      -      -      -      -      -      -     -      -      x   |   x      x      -      x
 MSD      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 MTC      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 MTD      -      -      -      -      -      -     -      -      x   |   -      -      -      -
 MUX      -      -      x      -      -      x     -      -      x   |   -      -      -      -
 MUXL     -      -      x      -      x      -     -      x      x   |   -      -      -      -
 MUXC     -      -      x      -      -      -     -      x      x   |   -      -      -      -
 PIF      x      -      -      -      -      -     -      -      x   |   -      -      -      -
 PTP      -      -      x      -      x      -     -      x      x   |   -      -      -      -
 PTR      -      -      -      -      x      -     -      x      x   |   -      -      -      -
 TBG      -      -      x      -      -      x     -      -      x   |   -      -      -      -
 TTY      -      -      x      -      x      x     -      x      x   |   -      -      -      -



-----------------------
Symbolic Interpretation
-----------------------

SCP directly supports displaying or accepting data via the EXAMINE and DEPOSIT
commands in numeric format with a specified radix.  Symbolic interpretation
extends this to include printing and parsing in character or mnemonic formats
and is controlled by default and user-specified modes.

The following symbolic modes for printing and parsing are supported by including
one of the switches:

  Switch   Interpretation
  ------   -----------------------------------------
    -A     a single character in the right-hand byte
    -C     a two-character packed string
    -M     a CPU instruction mnemonic

When printing and parsing instruction mnemonics, an additional format switch may
be specified to indicate the radix of any instruction operands:

  Switch   Interpretation
  ------   -----------------------------------------
    -A     a single character in the right-hand byte
    -B     a binary value
    -O     an octal value
    -D     a decimal value
    -H     a hexadecimal value

The -A switch thus has two meanings.  In combination with -M, printing and
parsing of instruction mnemonics assumes single characters for any data operands
present.  By itself, printing and parsing of single characters occur.  The
numeric switches may also be used by themselves to print or parse a value in the
specified radix.

The -M and other mnemonic mode switches are mutually exclusive.  However, the -M
mode switch can be combined with one format switch to specify the display or
entry format for instruction parameters.  The format overrides are also mutually
exclusive.  However, this is not enforced by SCP and cannot be enforced by the
HP simulators, as there is no means to communicate errors back to the user.


Symbolic parsing of the command parameters is used when evaluating an expression
(EVAL command) or depositing to memory or registers (DEPOSIT command).  Before
parsing, the "get_radix" routine is called to obtain the radix to use for
numeric operands.  This routine is hooked by the "ex_get_radix" routine to
override the default radix with the -B (binary), -O (octal), -D (decimal), and
-H (hexadecimal) switches.  In the absence of any of these switches, the default
radix specified by the target is used.

The HP simulators provide explicit support the -A (single character), -C
(two-character string), and -M (instruction mnemonic) override switches.  In the
absence of any of these switches, a leading apostrophe (') implies -A, a leading
quotation mark (") implies -C, and a leading alphabetic character implies -M.
The 3000 simulator supports several additional types of mnemonic mode switches
(-E, -ER, -I, and -T) that are variants of the -M switch.

For evaluation and depositing to memory, the "parse_sym" routine is called
unconditionally.  For depositing to registers, "parse_sym" is called if the
register has the REG_VMIO or any user REG flags set.  If the flags are not set,
or if the routine is called and returns an SCPE error, then the "get_uint"
routine is called to parse the numeric value.

The parameters to "parse_sym" vary, depending on the calling context:

  Parameter   EVAL             DEPOSIT address   DEPOSIT register
  ---------   --------------   ---------------   ----------------------------
  char *      argument         argument          argument
  t_addr      0                address           user flags (H) and radix (L)
  UNIT *      CPU unit         specified unit    NULL
  t_value *   returned value   returned value    returned value
  int32       switches         switches          switches and SIM_SW_REG

Note that while symbolic parsing is always used for address values, parsing for
register values requires the presence of a user flag on the REG entry.  If no
user flag is present, only numeric parsing is supported.  The user flags
currently employed are:

  Flag      Meaning
  --------  ---------------------------------------------------
  REG_A     default display format is -A (one ASCII character)
  REG_C     default display format is -C (two ASCII characters)
  REG_M     default display format is -M (instruction mnemonic)
  REG_T     default display format is -T (status mnemonic)
  REG_X     allow symbolic overrides

REG_X does not imply a default parse or display format; it is used only to
trigger the call to "parse_sym" to permit symbolic parsing.

REG flags specify symbolic defaults for display.  They do not specify parsing
formats.  This is because the user has no way of determining the applied flags
when entering arguments; the user manuals merely state that specified registers
support symbolic entry and display.

Consider two registers, only one of which has the REG_A flag.  The "DEPOSIT REG1
0" and "DEPOSIT REG2 0" commands would store different values for the two
registers (ASCII "0" and numeric 0, respectively) that would not be apparent to
the user. So instead, REG_A would cause EXAMINE REG to display in ASCII, but
DEPOSIT REG would accept the argument in the default numeric radix specified by
the REG entry, unless the -A or ' override was used.

----

Symbolic printing is used when evaluating an expression (EVAL command),
examining memory or registers (EXAMINE command), and to print the next
instruction when the simulator stops.  For all cases but the last, the
"get_radix" routine is called before printing to obtain the radix to use for
numeric operands.  This routine is hooked by the "ex_get_radix" routine to
override the default radix with the -B (binary), -O (octal), -D (decimal), and
-H (hexadecimal) switches.  In the absence of any of these switches, the default
radix specified by the source is used.

For examining registers, "fprint_sym" is called if the register has the REG_VMIO
or any user REG flags set; otherwise, it is called unconditionally.  If the
flags are not set, or if the routine is called and returns an SCPE error, then
the "fprint_val" routine is called to print a numeric value.

The parameters to "fprint_sym" vary, depending on the calling context:

  Parameter   EVAL             EXAMINE address   EXAMINE register         Simulation stop
  ---------   --------------   ---------------   ----------------------   ----------------
  FILE *      output stream    output stream     output stream            output stream
  t_addr      0                address           user flags | radix       program counter
  t_value *   supplied value   memory value      register value           memory value
  UNIT *      CPU unit         specified unit    NULL                     NULL
  int32       switches         switches          switches | SIM_SW_REG    -M | SIM_SW_STOP

-----

NOTE: The -B switch is already used by several simulators with the EXAMINE
      command.  For example, the PDP11 uses it to indicate byte display instead
      of word display.  So we cannot use -B as a global switch to display in
      binary.

      Moreover, when formatting a register, there is no way to obtain the
      register descriptor's "width" and "flags" field.  So, for example, EX -B
      <reg> will use the CPU's data width (16 bits) rather than the register's
      width.  More distressing, EX -H <reg> gives different results, depending
      on whether or not it is passed to "fprint_sym".  This means that adding
      REG_VMIO to all registers is undesirable.  For instance, if the register
      width is 4 and the flags include PV_LEFT, then the display will be "000F"
      if REG_VMIO is present and "F" if not.

      Things don't work much better for memory accesses.  Entering "D -B 0 10"
      stores 2 in location 0, but "D -B 0 12" stores 12 (octal) with no error.
      The "get_uint" call fails as expected and returns SCPE_ARG, but returning
      that from "parse_sym" causes "dep_addr" to call "get_uint" with the
      default radix, which succeeds.

      Conclusion: -B cannot be added at the "fprint_sym" and "parse_sym" level,
      and only symbolic interpretations that don't require register information
      and will fail on fallback can be supported.  Probably want to eliminate -B
      as a user option and add it to SCP as a local patch if it is not accepted
      upstream.


mnemonic overrides:

 shift  (10)    ASL, RRR    override
 iopon  (10)    LAI, SAI    override
 iopop  (10)    LAI, SAI    override
 iopo   (10)    LAI, SAI    override
 addrs  (addr)  memoryops   no override
 datas  (data)  dataops     override   CRC, TRSLT, ILIST, PRFEI, PRFIO, and DPOLY

The 1000 has only a very few operands, and they're all numeric.

The 3000 has many operands, and they're all numeric except possibly immediate
operands, which can be one alpha character (0-255).  So -C with -M is useless in
all cases.

for 1000:

 EX [ -A | -B | -O | -D | -H ] <addr>     as character or numeric with radix override
 EX -C <addr>                             as string
 EX -M [ -A | -B | -O | -D | -H ] <addr>  as mnemonic with character or numeric operand radix override

 modes:     -C, -M, (numeric)
 formats:   -A, -B, -O, -D, -H

for 3000:

 EX [ -A | -B | -O | -D | -H ] <addr>     as character or numeric with radix override
 EX -C <addr>                             as string
 EX -E [ -A | -B | -O | -D | -H ] <addr>  as I/O channel order
 EX -I [ -A | -B | -O | -D | -H ] <addr>  as EDIT subop
 EX -M [ -A | -B | -O | -D | -H ] <addr>  as mnemonic with character or numeric operand radix override
 EX -T [ -A | -B | -O | -D | -H ] <addr>  as status

 modes:     -C, -E, -I, -M, -T, (numeric)
 formats:   -A, -B, -O, -D, -H

YES:

In the absence of any command-line switches, the EXAMINE command displays a
numeric value in the CPU's data radix, which defaults to octal but may be set to
a different radix with the SET CPU [ OCT | DEC | HEX ] command.  If one of the
mutually exclusive mode switches is given, the value is displayed in the
specified form.  Any numeric operands present are displayed in a default radix
that depends on the operand unless overridden by one of the mutually exclusive
format switches.

If the -C switch is specified, the value is displayed as two characters
separated by a comma. Alphanumeric and symbol characters are displayed in single
quotation marks, control characters are displayed in symbolic form, and
characters above 128 decimal are displayed in escaped numeric form.

If the -M switch is specified, either alone or combined with a format switch,
the value is displayed as a CPU machine instruction mnemonic (if it is defined
and implemented in the current firmware set) or as a numeric value in the CPU's
data radix (if not).  Instruction operands are displayed as follows:

 - Operand memory addresses and IOG select codes use the CPU’s address radix,
   which is octal.

 - EAU shift and rotate counts and IOP index displacements use decimal unless
   overridden by a switch on the command line.

 - IOP and SIS operand data values use the CPU's data radix, which defaults to
   octal but may be set to a different radix or overridden by a switch on the
   command line.

When entering values to deposit in the absence of switches, a leading '
(apostrophe) implies -A, a leading " (quotation mark) implies -C, a leading
alphabetic character implies -M, and a leading digit implies a numeric value in
the CPU's data radix.  If one of the format switches is given, a value in the
specified form is expected.  If a single character is supplied with -C, the low
byte of the resulting value will be zero; follow the character with a space to
pad the low byte with a blank.

If the -M switch is specified, either alone or combined with a format switch, a
defined and implemented machine instruction mnemonic is expected.  Instruction
operands are parsed using the same rules as above.

--> Need to define meaning of "DEP -H CCE" vs. "DEP -M CCE" vs. "DEP -M -H CCE".
Should DEPOSIT -H 0 CCE deposit 0x0cce [YES], even though CCE is a valid
mnemonic, or does the -H apply to numeric operands (e.g., RRR F vs. RRR 17 vs.
RRR 15)?  Seems as though -D/-O/-H should apply to any /numbers/ specified, or
to the numeric operands of mnemonic instructions if -M is included.



-------------
Power Control
-------------

New meanings:

 - RESET -P CPU  -->  power-on PRESET; sends ~PON | POPIO | CRS to all
   interfaces (ioa_PONPRESET)

 - RESET CPU  -->  operator PRESET; sends PON | POPIO | CRS to all interfaces
   (ioa_PRESET)

 - RESET -P <dev>  -->  power-on reset of peripheral (not interface)

 - RESET <dev>  -->  reset of peripheral (not interface)

 - RESET -P  -->  power-on reset of all interfaces and peripherals

 - RESET  -->  reset of all interfaces and peripherals

New commands:

 - POWER CPU FAIL --> CPU power off, IRQ4 if ARS

 - POWER CPU RESTORE --> CPU power on, power-on PRESET to interfaces, IRQ4 if
   ARS

 - POWER <dev> FAIL --> peripheral (not interface) power off

 - POWER <dev> RESTORE --> peripheral (not interface) power on, power-on
   peripheral reset (RESET -P)

 - POWER FAIL --> all devices power off, IRQ4 if ARS

 - POWER RESTORE --> all devices power on, power-on PRESET to interfaces, IRQ4
   if ARS

Do we want to differentiate between POWER FAIL (power_failing) and POWER OFF
(power_off) and similarly between POWER RESTORE and POWER ON?  Currently, there
is no method analogous to holding PRESET down while powering up with ARS
enabled.  Maybe POWER ON CPU does that?  (POWER ON for other devices might be
identical to POWER RESTORE.)

Resets occur as follows:

 - sim startup: reset_all_p (0)

 - SET ENABLE/DISABLE: dptr->reset

 - RESET [ALL]: reset_all (0)

 - RESET <dev>: dptr->reset

 - RUN/BOOT: reset_all (0)

Problem: power on reset to CPU currently zeros memory, so POWER RESTORE cannot
call reset_all_p.  The 1000 zeros memory only if -MLOST, but there's no analog
to this, unless RESET -P is it.  If so, then POWER RESTORE needs another way to
do power-up initialization of the CPU but leave memory alone.

Problem: turning power off is really a per-unit operation.  For example, one
might turn power off on one of the four connected 7970 tape drives.  This would
not be the same as disabling the unit, which is equivalent to disconnecting the
cable.  In hardware, it is possible to differentiate between unit disconnected,
unit connected but powered off, and unit connected and powered on but offline.
Consider one interface line that is pulled up on the interface and grounded in
the device and another that is pulled down on the interface and pulled up by the
device.  The first line detects device connected; the second detects device
powered up.  We cannot provide this detection if the device reset routine is
reused for power off/on.

Solution: provide a power routine and a pointer to the routine in the DIB:

  t_stat (*io_power) (UNIT *uptr, POWER_STATE new_state);

Change POWER <device> command to <device> or <unit>.  If <device>, then uptr is
NULL; otherwise, uptr points at unit.  If io_power is NULL (i.e., not defined),
device does not handle power state changes.  In that case, only POWER <device>
is accepted, POWER OFF does nothing, and POWER ON does RESET -P.

The return status is SCPE_OK if the operation succeeded.  It could be SCPE_NOFNC
if the command is not allowed due to the current power state (e.g., POWER OFF
while power is already off).  The CPU routine returns SCPE_INCOMP if the routine
succeeds but the CPU must be run to complete the command (i.e., POWER RESTORE
with ARS enabled).

When all devices are failed or restored, individual routine errors must not stop
the full sequence from being run.  For instance, doing POWER OFF LPT followed by
POWER OFF should succeed, even though the LPT handler would return SCPE_NOFNC.
An error in this case would be reported only if none of the handlers succeeded.

~ARS/ARS is not identical to ~PFAR/PFAR.  With ~ARS, no IRQ4 occurs on power
down and CPU remains halted on power up, regardless of PFAR.  With ARS and
~PFAR, IRQ4 occurs on power down but CPU halts on power up.  With ARS and PFAR,
IRQ4 occurs on power down and power up, and CPU runs on power up.

However, there's little point to the ARS * ~PFAR case, as recovery from a power
failure is not possible once memory contents are lost.  This case might have
been useful with the 1000 "A"-model power supplies that had a "standby"
position that retained memory contents while CPU and I/O power was off.

Therefore, it should suffice to have ARS imply PFAR and do IRQ4 on power off and
on, and ~ARS imply ~PFAR and do neither.

-----

To permit exercise of the power-fail capabilities, and to permit removing power
from those devices that have visible state changes, new POWER FAIL (DOWN, OFF)
and POWER RESTORE (UP, ON) commands are needed.  Power can be manipulated on
individual devices or on all devices.

Consideration must be given to changing the power state of the device interface
versus changing the state of the device peripheral.  For example, cycling power
on a line printer changes the status return and clears any partial print buffer
received.  Cycling power on the interface (by cycling power on the CPU) changes
the control and flag flip-flop states without disturbing the printer.

Currently, the LPS and LPT devices support cycling power on the device (via
local SET LPx commands), while the CPU does not.  Power-on and operator PRESETs
may be asserted for individual devices, which has no hardware analog, or for all
devices.  A power-on PRESET is received via the device reset routine with the
"P" switch set.  In response, the routine calls the device interface routine by
asserting PON and POPIO.  Power-on initialization of the device occurs in the
reset routine, while that of the interface occurs in the interface routine.

In hardware, individual device peripherals may be power cycled via their power
switches, or all device interfaces may be power cycled together by cycling CPU
power via its power switch, or both actions may occur together via a main power
loss.  In simulation, these actions correspond to POWER <dev> FAIL, POWER CPU
FAIL, and POWER FAIL, respectively.

The CPU power state is communicated to the interfaces via the PON and POPIO
signals (HP 1000) and the PWR ON and PFWARN signals (HP 3000).  PON and PWR ON
both indicate that DC power is stable.  PFWARN indicates that power is failing.
POPIO with PON denied indicates that power is coming up (on the 3000, there is
no explicit I/O indication that power is restoring, although CPURST and IORESET
are pulsed at power-up as well as when the LOAD button is pressed).  PON and PWR
ON are asserted while power is on.

Notifying a device of a power state change can be modeled in three ways:

 * Calling a new device-specified power-notification routine.

 * Calling the existing device interface routine with the PON signal indicating
   the power state.

 * Calling the existing device reset routine with switches to indicate the power
   state.

In the first model, the DIB is extended by adding a new function pointer that
points at a device's power routine.  If the field is not set, or is set to NULL,
the device does not respond to power changes.  The POWER command would call the
appropriate routines via the DIB pointer, passing FALSE for power-down and TRUE
for power-up.  Calling the power routine would be equivalent to toggling the
main power switch on the device.  For the CPU, the routine would set up the
power fail interrupt if ARS is enabled.  For peripherals, the routine would
simply set the power state that is reflected in the status bits.

The second model communicates the power state via the existing IO_INTERFACE
routine.  The hardware PON and POPIO signal states represent four conditions, as
follows:

  PON  POPIO  State
  ---  -----  ---------------
   0     0    Power is off
   0     1    Power-on PRESET
   1     0    Power is on
   1     1    Operator PRESET

The POWER FAIL command sends ~PON and ~POPIO to all or specified devices.  A
device responds by setting its internal state to "power off" (or, in the case of
the CPU, to "power failing" if the CPU is running and ARS is enabled).  The
POWER RESTORE command sends ~PON and POPIO; this is the same as sending RESET
-P.  The device responds by setting its state to "power on" (or "power
restoring").

Note that an unpowered device may get subsequent calls with PON asserted as a
result of the CPU continuing to run.  So PON by itself cannot be used to
indicate the power state.

This requires redefinition of PON.  Currently, PON is dispatched only once
during power-on reset, rather than being included in every I/O cycle.  This has
to be changed to dispatch PON with every cycle.  PON is omitted in only two
cases: for a power-on PRESET (or RESET -P), and for a POWER FAIL.

Actually, the existing simulation of power-on PRESET is wrong.  RESET -P calls
"io_assert" with "ioa_PON", which sends PON | POPIO | CRS to the device, while
RESET uses "ioa_POPIO" which sends POPIO | CRS.  This is exactly backward from
the hardware action, which has POPIO without PON indicating a power-on PRESET.
So implementing this second model would move the simulation closer to the
hardware, although in hardware PON remains asserted while power is failing and
denies when power has failed.  It is the PWU signal from the power supply to the
CPU denying that indicates that power is failing, but PWU is not broadcast on
the I/O bus.

Because the absence of PON would indicate a power fail, detection in the
IO_INTERFACE routine must be indirect, e.g.:

  POWER_STATE state = power_off;                        /* or power_failing */

  while (working_set) {
      signal = IONEXTSIG (working_set);                 /* isolate the next signal */

      switch (signal) {                                 /* dispatch an I/O signal */
          case ioPOPIO:
              if ((inbound_signals & ioPON) == 0)       /* power restoring? */
                  /* power-on PRESET actions */

              /* other PRESET actions */
              state = power_on;                         /* power is on */
              break;

          case ioPON:
              state = power_on;                         /* powwer is on */
              break;
          }

      IOCLEARSIG (working_set, signal);                 /* remove the current signal from the set */
      }

  if (state == power_off)
      /* power fail actions */

In this model, the POWER command would call the designated IO_INTERFACE routine
(or all routines) and pass ~PON | ~POPIO for a FAIL and ~PON | POPIO | CRS for a
RESTORE.

The third model uses switches to indicate power failing and power restoring to
the device reset routine.  The switches should be "internal" (i.e., not
corresponding to a user-specified letter), so that the user cannot initiate
power recovery via RESET -P.  Candidates are SIM_SW_SHUT and SIM_SW_REST for
power fail and restore, respectively.

All three models fail to differentiate between interface power cycling and
device power cycling.  Model 2 really only handles interface power
notifications, and model 3 handles only device power notifications (unless
additional hidden switches are defined).  Model 1 could be adapted to handle
both cases, but it would need an additional parameter to indicate
interface/device power state.  Power failing the entire system would require all
power-notification routines to receive interface power notifications (from the
CPU power loss) followed by device power notifications (from peripheral power
loss).



The first model is somewhat closer to the hardware, in that powering a device
off does not generate any I/O backplane activity (as would occur with model 2),
and powering the CPU off denies PON to stop the CPU when DC power is about to
drop (rather than being the initiating event).  Even if model 1 is selected, the
PON definition should be changed to match the hardware, i.e., to differentiate
between power-on PRESET and operator PRESET.



=====================
I/O System Simulation
=====================

---------------------
Device User Interface
---------------------

Each device interface has a settable SC (select code) from 10-77 octal.  Select
codes 0-7 are assigned to the CPU, DCPC, Power Fail, and Memory Protect devices;
the assignments cannot be changed.

The select code of the highest-priority interrupting device is loaded into the
CPU's CIR register (Central Interrupt Register) and is available to the CPU
during interrupt processing.  The select code is also addressed by I/O
instructions and is used as an index into an array of I/O signal handlers.


---------------
Data Structures
---------------

Device Information Block:

  INTERFACE *interface()
  uint32     select_code
  uint32     card_index

Signals and Data Block:

  OUTBOUND_SET signals
  HP_WORD      data

Interface Routine:

  function (DIB pointer, INBOUND_SET inbound_signals, HP_WORD inbound_value)
    returns outbound-signals-and-data


---------------
Device Conflict
---------------

Before executing instructions, the simulator must check for device conflicts.
Each device's select code must be unique.  This is checked by the
hp_device_conflict() routine prior to execution.


---------------
Dispatch Arrays
---------------

Arrays are used for dispatching I/O signals to a selected device interface.
The elements of each array contain pointers to specific interface DIBs, or NULL
if no interface is assigned.  The arrays used are:

 - devs [64] (indexed by select code for I/O instruction dispatch)

Each of the arrays is set up during the device interface conflict check that is
performed in the instruction prelude.


--------------------
Priority Bit Vectors
--------------------

External interrupts are initiated by device interfacees asserting the FLG and
IRQ signals to the CPU.  Priority is established by the position of a given
interface in the select code order.

To avoid interrupt polling and service request array scanning, the CPU
maintains bit vectors that store the state of the requesting interfaces.  The
vectors used are:

 - service_request_set (64 bits)
     Indexed by select code for DCPC service requests; initial state is all bits
     clear.

 - interrupt_request_set (64 bits)
     Indexed by select code for pending interrupt requests; initial state is all
     bits clear.

 - priority_holdoff_set (64 bits)
     Indexed by select code for priority chain interruption; initial state is
     all bits clear.

To allow interfaces to be relocated, and to allow user changes in interface
state to be reflected correctly in the vectors, the vectors are set up in the
instruction prelude each time that execution begins.  The interrupt_state and
service_state of each device is examined, and the bit vectors are set
appropriately.  When a device interfaces requests an interrupt or DMA service,
it must call iop_interrupt() or multiplexer_service() to set the bit vectors
appropriately.


----------------------
I/O Signal Definitions
----------------------

The CPU communicates with device interfaces by calling the interface's signal
handler and passing a signal set.  Set members correspond to individual bits in
a 32-bit word, with assertion represented by a one-bit and denial represented by
a zero-bit.  Many of the hardware signals have direct counterparts in the signal
set.  Others are implied by the function calls and returns (e.g., some requests
and acknowledgements).

For efficiency, the simulator does not implement signal generation exactly as in
the hardware.  In hardware, ENF and SIR are periodic, PON is asserted
continuously, and most signals are common to all interfaces and are qualified at
the interface by the select code.  Under simulation, signals are sent only when
actions are to be taken and then only to the specific target device.  For
instance, PON is dispatched only once during power-on reset, rather than being
included in every I/O cycle.  SIR is dispatched only when flip-flops affecting
the PRL, IRQ, or SRQ signals are changed, rather than with every instruction.
ENF is sent only when the flag buffer is set or cleared, whereas in hardware,
ENF samples the flag buffer value at every T2 and sets the flag accordingly.

In one case, there is an intentional deviation.  In hardware, the FLG and IRQ
signals are always asserted together, and the interrupt source decoder uses FLGn
to establish the MSD of the select code and IRQn to establish the LSD.  In
simulation, FLG is asserted when an interrupt condition exists, and IRQ is
asserted if the interrupt flip-flop sets.  If FLG asserts without IRQ, then
either PRH or IEN is denied, which holds off the interrupt request.  In this
case, a bit is set in the pending interrupt vector; this avoids polling every
interface for an interrupt condition when PRH and IEN are both asserted again.

Signals are listed below as inbound (passed to the signal handler), outbound
(returned from the signal handler), or implied.

  Inbound Signals:

  Name     Meaning
  -----    --------------------------------
  CLC      Clear the control flip-flop
  STC      Set the control flip-flop
  CLF      Clear the flag flip-flop
  STF      Set the flag flip-flop
  SFC      Skip if the flag is clear
  SFS      Skip if the flag is set
  IOI      I/O data input
  IOO      I/O data output

  EDT      End of data transfer
  ENF      (periodic) Enable flag
  SIR      (periodic) Set interrupt request
  IEN      Interrupt system enabled
  IAK      Interrupt acknowledged
  PRH      Priority high

  CRS      Control reset
  POPIO    Power-on preset to I/O
  PON      Power on normal


  Outbound Signals:

  Name     Meaning
  -----    -----------------
  PRL      Priority low
  IRQ*     Interrupt request
  FLG*     Flag
  SRQ      Service request
  SKF      Skip on flag


  Implied Signals:

  Name     Meaning                              Implied by
  -----    -----------------------------------  ---------------------
  SCL*     Select code least-significant digit
  SCM*     Select code most-significant digit
  IOG      I/O group instruction
  RUN      Run flip-flop


  Unused Signals:

  Name  Meaning           Reason for Omission
  ----  ----------------  -----------------------------
  BIOI  Block I/O Input   Block input is not supported
  BIOO  Block I/O Output  Block output is not supported
  BIOS  Block I/O Strobe  Block I/O is not supported

When the "IOBUS" trace flag is set on a device, the signals and data word sent
to the device interface are printed on entry, and the signals and data word
returned, if non-null, are printed before exit.  The signals and data are
formatted into a symbolic representation via the fprint_signals() function to
produce a view into the information exchanged on the I/O bus.


--------------------
I/O System Interface
--------------------

  bool io_initialize (& interrupt_request_set, & interrupt_poll_set)

    Called in the instruction prelude.  Initializes the devs and irqs DIB
    pointer arrays and sets the interrupt_request_set and interrupt_poll_set bit
    vectors from the interrupt_state values in the device DIBs.  Returns True if
    successful and False if a device number or interrupt priority conflict
    exists.


/////////////

DIB structure:

  INTERFACE *interface()
  uint32     select_code
  uint32     card_index

STATUS_VALUE structure:

  t_stat   status
  HP_WORD  value

SIGNALS_VALUE structure:

  OUTBOUND_SET  signals
  HP_WORD       value

Device Interface:

  SIGNALS_VALUE dev_interface (DIB *, INBOUND_SET, HP_WORD)

  - processes inbound_signals and inbound_value
  - sets outbound.signals and outbound.value
  - calls io_dispatch (dev_sc, ioENF, 0) from event service to set flag

I/O Dispatcher:

  STATUS_VALUE io_dispatch (uint32 select_code, INBOUND_SET, HP_WORD)

  - if select code unoccupied, returns floating bus and CPU stop status
  - provides IOBUS tracing via devs [select_code]->flags
  - generates PRH, IEN signals
  - calls dibs [select_code]->interface
  - sets bit vectors based on PRL, IRQ, FLG, and SRQ outbound signals
  - increments P if SKF outbound

I/O Instruction Executor:

  t_stat cpu_iog (HP_WORD io_instruction, t_bool in_trap_cell)

  - called to execute an I/O Group instruction (e.g., STC 12,C)
  - also called by IOP and RTE OS instruction execution
  - does MP check
  - gets A/B register value for IOO
  - determines signals to use
  - calls io_dispatch with select code from instruction
  - sets A/B register value for IOI
  - returns status (e.g., STOP_UNSC)


----------------
Device Interface
----------------

Each device interface must provide an I/O signal handler and place a pointer to
the handler in the DIB.  This is the main interface between the CPU and the
device.  Each interface must also provide a reset routine.

The "dev" prefixes below are typically the same as the device name (e.g.,
ms_interface, ds_reset, etc.).

  SIGNALS_DATA dev_interface (DIB *, INBOUND_SET, HP_WORD)

//  Called by the IOP for Direct I/O and by the channels for Programmed I/O.
    Corresponds to asserting the SO or the CHANSO and CHANACK signals.
    Processes the supplied signals in order and modifies the device state
    appropriately.  The inbound_value is ignored if it is not indicated by the
    inbound_signals.  The return value is a restricted outbound signal set in
    the high word and an outbound value in the low word.  If the outbound value
    is not indicated by inbound_signals, the outbound value is zero.

  t_stat dev_reset (device_pointer)

    Called by the SCP for the RESET and RUN commands.  Corresponds to asserting
    the IORESET signal.  Clears the device state.


----------
Direct I/O
----------

I/O Group instructions are decoded and executed by the CPU and call the I/O
signal dispatcher with select code and signal set.  The dispatcher then calls
the indicated device interface.

The process is:

//

 1. The CPU calls iop_direct_io(), passing the device number obtained from the
    stack (or zero for SMSK), the I/O order determined by the instruction, and a
    value to be written for the CIO, WIO, and SMSK instructions.

 2. The IOP translates the I/O order into the corresponding signal.  For all
    instructions except SMSK, the IOP sets dibptr = devs[device_number].  If
    dibptr is NULL, the IOP sets the I/O Timer bit in CPX and returns a zero
    value.  If dibptr is valid, the IOP calls dibptr->dev_controller(), passing
    the dibptr, the translated signal, and the inbound value.  For SMSK, the IOP
    loops through the device list and calls each defined controller signal
    handler in turn with the DSETMASK signal and the mask value.

 3. The device controller processes the signal, updates the device state as
    indicated, and returns the requested outbound value for the TIO and RIO
    instructions.  The outbound signal set is ignored.

 4. The IOP returns the outbound value to the CPU.

 5. The CPU tests the I/O Timer bit in the CPX register.  If set, the CPU clears
    it and aborts the instruction by setting CCL in the Status register.  If
    clear, the instruction is completed normally.


-------
Details
-------

The IOG instruction executor calls the signal dispatcher with the following
signals:

  Instr  Signal Set
  -----  ---------------------
  HLT    (none)
  HLT,C  ioCLF | ioSIR
  STF    ioSTF | ioSIR
  CLF    ioCLF | ioSIR
  SFC    ioSFC | ioSIR
  SFC,C  ioSFC | ioCLF | ioSIR
  SFS    ioSFS | ioSIR
  SFS,C  ioSFS | ioCLF | ioSIR
  MI*    ioIOI
  MI*,C  ioIOI | ioCLF
  LI*    ioIOI
  LI*,C  ioIOI | ioCLF
  OT*    ioIOO
  OT*,C  ioIOO | ioCLF
  STC    ioSTC | ioSIR
  STC,C  ioSTC | ioCLF | ioSIR
  CLC    ioCLC | ioSIR
  CLC,C  ioCLC | ioCLF | ioSIR

In addition, the dispatcher adds the ioIEN signal if the interrupt system is on
and the ioPRH signal if priority is not inhibited to the selected interface.

The SFC and SFS instructions test the returned signal set for ioSKF and skip if
the signal is present.


-------------------
DMA Service Request
-------------------

- Virually all interfaces assert SRQ.

- Typically, SRQ is the Q-side of the Flag flip-flop, but not always.

- Asserting SRQ does nothing if the DMA channel is not configured for the select
  code and the DMA's Transfer Enable flip-flop is set (by STC).

- SRQ may be asserted before the DMA trasfer enable flip-flop is set, or the
  flip-flop may be set before SRQ is asserted.

- A DMA cycle is requested at the top of the instruction loop only if (a) the
  channel transfer is enabled and (b) the associated device's SRQ line is
  asserted.  Therefore, channel requests need only be calculated (a) in the DMA
  interface's STC handler, and (b) in the I/O dispatcher when SRQ is asserted.
  This implies that SRQs that result from event service routines must be passed
  through the I/O dispatcher by sending ENF | SIR.

- A DMA cycle may performed on channel 1, on channel 2, or on channel 1 and then
  channel 2.  If SRQ is asserted continuously, then cycles occur contiguously
  without intervening CPU cycles.

- Setting the DMA channel flag, either implicitly when the count expires or
  explicitly by a STF instruction, clears the transfer enable flip-flop.

- Even though there are at most two channels, a bit vector is required because
  multiple SRQs may be set before one is associated with a DMA channel.  For
  example, STF may be done on five interfaces, and then DMA configured to use
  two of the five.  At completion, DMA may be configured to use two of the
  remaining three devices, and the SRQs must assert automatically to start the
  transfers.

- The bit vectors must be reconfigured at DMA initialization time (in the
  instruction prelude), as the select codes may have been changed by the user.

- Because DMA requests must be checked for each instruction, it is desirable to
  test for the presence of a request rather than calculate requests at the top
  of the instruction loop.


DMA STC does:

    uint32 dma_request_set;
    uint32 dma_device [2] [2] = {   // bit is set ONLY when xferen FF set
        { 0, 0 }
        { 0, 0 }
        };

    dma_device [ch] [rank] = 1u << channel_select_code % 32;

    if (dma_device [ch] [rank] & service_request_set [rank])
        dma_request_set |= DMA_chan_REQUEST;


DMA STF does:

    dma_device [ch] [rank] = 0;


I/O dispatcher does:

    uint32 service_request_set [2] = { 0, 0 };

    if (outbound.signals & ioSRQ) {
        service_request_set [sc_rank] |= sc_bit;

        if (sc_bit & dma_device [ch1] [sc_rank])
            dma_request_set |= DMA_CHAN1_REQUEST;
        if (sc_bit & dma_device [ch2] [sc_rank])
            dma_request_set |= DMA_CHAN2_REQUEST;
        }

    else
        service_request_set [sc_rank] &= ~sc_bit;


Instruction loop does:

    if (dma_request_set)
        dma_service ();



---------------------------
IPL Process Synchronization
---------------------------

   Provide process synchronization via events.  One instance would wait for the
   event, and the other would signal the event to release the first.  Events
   should be allocated and released automatically as part of simulator or IPL
   device configuration.  Event names should be unique to a simulator pair
   configuration, so that more than one pair of instances can use
   synchronization concurrently.

   * One or two events?

     A simple approach is to use separate events to wait for the SP and the IOP.
     The difficulty with using a single event for both SP and IOP waits is that
     an instance may signal the event to release the wait and then wait itself
     for a signal from the other instance. i.e.:

       SP       IOP
       ======   ======
       wait     ...
       ...      signal
       ...      ...
       ...      wait
       signal   ...

     The danger is if the SP does not issue its wait before the IOP issues the
     signal, e.g., due to preemption.  In this case, the IOP wait will not wait
     because it has signaled itself.  That is:

       SP       IOP
       ======   ======
       ...      signal  <-- this signal beats the SP wait due to SP preemption
       ...      ...
       ...      wait    <-- this wait is already signaled and so does not wait!
       ...      ...
       wait     ...     <-- this wait waits forever, because the event is reset!
       ...      ...
       signal   ...

     With separate SP and IOP events, the IOP signals the IOP event and then
     waits on the SP event.  The SP does not wait on the IOP event because it is
     already signaled, and the SP proceeds to signal the SP event, which
     releases the IOP's wait.  Synchronization is maintained regardless of
     process execution time.

     However, the pattern above arises only in connection with the IOP program
     generation, and the IOP runs its loader after signalling the IOP event.
     The IOP cannot reach the SP wait until after it has received the data
     transfer, which does not start until the SP's wait receives the IOP event
     signal.  So if the first case above is not satisfied, due to SP preemption,
     the situation will be:

       SP       IOP
       ======   ======
       ...      signal
       ...      ...     <-- the IOP pauses here for the data transfer
       wait     ...
       ...      ...     <-- the data transfer occurs here
       ...      wait
       signal   ...

     Therefore, only a single event is necessary.

   * Wait/signal syntax?

     Process synchronization provides a "wait" command that waits for an event
     to be signaled and a "signal" command that signals the event.  These
     commands may be either top-level commands or IPL device modifiers.

     Top-level commands might be:

       WAIT
       SIGNAL

     Device modifiers might be:

       SET IPL WAIT
       SET IPL SIGNAL

     The above is nicer than SET IPLI WAIT, etc., and may be implemented either
     by creating a new IPL device to handle just these two commands, or by
     renaming the IPLI device to IPL and defining IPLI as the logical name.
     This permits SET IPL while externally presenting the device as IPLI.

     NOTE: if two events are used, then the wait and signal commands must either
     specify the events explicitly or determine the events implicitly.  For the
     former, "wait sp", "wait iop", "signal sp", and "signal iop" commands would
     be needed.  For the latter, the event can be determined by testing the
     CPU's UNIT_IOP flag.  If it is not set (implying the SP), "wait" waits for
     the IOP event, and "signal" signals the SP event.  If it is set (implying
     the IOP), "wait" waits for the SP event, and "signal" signals the IOP
     event.  This would require SET CPU IOP to be specified for the IOP, even
     when IOP firmware is not needed (e.g., for 2000 F), but this is not a
     hardship.


   * Wait/signal semantics?

     generate name and CreateEvent on 2nd IPL attach
     CloseHandle on 1st IPL detach
     SetEvent on SET IPL SIGNAL
     WaitForSingleObject on SET IPL WAIT
     loops while waiting; CTRL+C to exit loop before wait completes



   * Event naming?

     To permit the two instances to share events, either the event names must be
     known (e.g., "HP2100-IPL"), or the first instance must pass the event
     handle to the second instance.

     Known naming is simplest, but it is desirable to allow more than one pair
     of instances to be active.  This requires that the name be different for
     each pair but still be known to each pair independently.  A simple approach
     is to incorporate the lower of the two IP port numbers in the name, e.g.,
     "HP2100-IPLI-4020".  The port numbers attached to the IPLI and IPLO devices are
     known to both instances of a pair but must be unique to the pair.  The IPL
     devices may be attached in any order, so the lower number is known only
     when the second of the two devices is attached.  Naming and allocation
     would take place then, and deallocation would be needed when the first of
     the two devices is detached (because that device could be reattached with a
     lower number).

     Passing event handles might be easy when the IPL device is changed to use
     shared memory spaces rather than sockets.  Currently, though, the handle
     would have to be passed as an environment variable, which would need
     per-pair naming as above.  So the latter approach offers no (current)
     advantages over the former and has some disadvanatges (e.g., checking for
     environment overflow).

4.x port = "port number", ok if host = "localhost" or "127.0.0.1"
3.x ipp = port number, OK if ipa = 0 or 0x7f000001 (localhost)



---------------------
CS/80 Disc Simulation
---------------------

CS/80 drives were supported on RTE-6/VM only.  RTE-IVB provided MAC and ICD
support.

Initial implementation will be the subset of CS/80 that is used by the 12992J
Boot Loader ROM, the RTE-6VM boot extensions, the DVM33 disc driver, and the
direct control operations performed by SWTCH.

The boot loader does:

  - Unlisten
  - Untalk
  - Universal Device Clear
  - Parallel Poll (wait for drive 0 ready)
  - Universal Device Clear
  - Listen 0
  - Command: Set Unit 0, Locate and Read (defaults: address 0, length -1)
  - Unlisten
  - Talk 0
  - Execution: DCPC accepts 620 words (1240 bytes)

When DCPC completes, boot loader does JSB to 2055,I.

The boot extension does:

  - master reset (terminates read)
  - Untalk
  - Unlisten
  - Listen 0
  - Transparent: Cancel (no optional unit; terminates read)
  - Untalk
  - Unlisten
  - Parallel Poll (wait for drive 0 cancel)
  - Untalk
  - Unlisten
  - Talk 0
  - Reporting: QSTAT (0)
  - Untalk
  - Unlisten
  - Listen 0
  - Command: Set Unit 0, Set Volume 0, Set Length 65024 bytes, Set Address 2, Locate and Read
  - Untalk
  - Unlisten
  - Talk 0
  - Execution: DCPC accepts accepts 32512 words (65024 bytes)
  - Untalk
  - Unlisten
  - Talk 0
  - Reporting: QSTAT (0)
  - Untalk
  - Unlisten

After completion, the boot extension does JMP 3,I at 77460 to $STRT in SCHD6.

The RTE startup uses the DVM33 driver; it does:

  - Command: Set Unit 0, Set Volume 0, NOP, Set Length 2560 bytes, NOP, Set Address 288, NOP, Locate and Read
  - Execution: accepts 1280 words (2560 bytes)
  - Reporting: read QSTAT (0)
  - Command: Set Unit 0, Set Volume 0, NOP, Set Length 12288 bytes, NOP, Set Address 240, NOP, Locate and Read
  - Execution: accepts 6144 words (12288 bytes)
  - Reporting: read QSTAT (0)
  - Command: Set Unit 0, Set Volume 0, NOP, Set Length 7680 bytes, NOP, Set Address 162, NOP, Locate and Read
  - Execution: accepts 3840 words (7680 bytes)
  - Reporting: read QSTAT (0)
  - Command: Set Unit 0, Set Volume 0, NOP, Set Length 2044 bytes, NOP, Set Address 347, NOP, Locate and Read
  - Execution: accepts 1022 words (2044 bytes)
  - Reporting: read QSTAT (0)
  [...]
  - Command: Set Unit 0, Set Volume 0, NOP, Set Length 512 bytes, NOP, Set Address 4414, NOP, Locate and Write
  - Execution: sources 256 words (512 bytes)
  - Reporting: read QSTAT (0)
  [...]

The RTE-6/VM System Manager's Manual says, "For all CS/80 discs, set the unit
and volume number to 0.  For the CTD, set the unit number to 1 and volume to 0."

The Online Generator Reference Manual says, "The unit number is always 0 except
unit 2 for the HP 7907 removable platter and unit 1 for integrated CTDs.  For
the volume, always enter 0."

DVM33 issues the following commands internally:

  - Describe
  - Write File Mark
  - Locate and Read
  - Locate and Write
  - Unload
  - Set Options (auto sparing enabled, skip sparing, immediate reporting)
  - Read Run Log
  - Copy Data
  - Request Status
  - Release
  - Identify

Read Run Log is sent only for a tape drive and then only to examine the fourth
byte to see if the cartridge has been initialized.

Release is sent only if a release request appears in the status bytes.

Identify is sent only if an operation times out.  It is used to determine if the
device is busy or dead.

SWTCH calls the following subroutines in the CS/80 disc library:

  - XLCRD     Locate and read
  - XLCWR     Locate and write
  - XLCVF     Locate and verify
  - XRELS     Release
  - XXSPR     Run error rate test and spare

Locate and Read/Write/Verify are used for normal system installation operations.
A Release is performed only if an error is reported and one of the "release
requested" status bits is set.  An ERT and Spare is performed only if an error
reports Unrecoverable Data Error, Unrecoverable Data Overflow, or Marginal Data
status.

The complementary command array requests the following commands:

  - Set unit
  - Set volume
  - Set address (single-vector)
  - Set length

Complementary commands are applicable to a wide range of real-time, general
purpose, and diagnostic commands, but not all combinations of complementaries
are valid.  Table 2-1 on page 2-2 of the CS/80 Instruction Set manual details
the choices.

Other notes from the manual:

  - Except for CLEAR and CANCEL, a command message is valid only if it occurs
    during the command phase of a transaction (1-4).

  - Execution messages are valid only during the execution phase of a
    transaction which started with a command that calls for an execution
    message (1-4).

  - The device initiates reporting messages during the reporting phase of each
    transaction, during special reporting phases entered for power recovery, or
    to service internal requests (1-4).

  - The only means of clearing the QSTAT byte is by issuing the Request Status
    command or the Clear command (1-4).

  - In its idle state, the device is in the command-ready state.  When a command
    message is received, it is buffered, parsed, and validated as defined by the
    device firmware.  If the command and its parameters are valid, the device
    enters the execution state and begins to carry out the command.  If not, the
    device enters the reporting state and prepares an error status report (1-6).

  - Each unit within a device has its own set of programmable operating
    parameters.  The parameters are initially assigned power-on values.  New
    parameter values for a unit are established by a command message containing
    ONLY Complementary commands.  A single command may be used to update
    multiple parameter values (2-1).

  - There are two Complementary commands that do not produce the temporary
    override.  These are the Set Unit and Set Volume commands.  The values
    specified in these commands remain in effect even if the commands are
    included in a real-time, general purpose, or diagnostic command (2-1).

  - Set Unit must be the first byte of the command sequence.  If the opcode
    appears elsewhere in the message, an Illegal Opcode error will be reported.

  - If an illegal volume number is specifed with Set Volume, a Module Addressing
    status error is generated.

  - Set Length of 0 with a Locate and Read or Write specifes a seek only; it
    does not require an execution phase.

The manual suggests that the command bytes are all accepted, then they are
parsed and validated, and then they are executed.  That implies that an error
anywhere in the sequence inhibits the entire sequence, rather than commands
being executed from left to right and stopping at the first bad command.

However, SS/80 says, "When the peripheral's command decoder is working its way
through the complementary commands, it will stop at the first one which is
erroneous, or on the first illegal opcode it detects.  The remaining commands
following the error or illegal opcode will not be executed."  That seems to
imply that the ones before the error WILL be executed.

The fact that Set Unit is required to appear first, but Set Volume is not,
suggests that one may issue complementaries for several volumes of one unit.
And tha implies left-to-right execution.

Because complementaries only affect the current transaction unless the command
sequence ends with a complementary, another implication is that none of the unit
settings are altered if an error occurs in the sequence.  By the same token,
a bad opcode at the end of a sequence of complementaries must discard the
complementaries, as they would only be set if the command ended with a valid
complementary.  So, effectively, a bad sequence is ignored, except, perhaps the
initial Set Unit and Set Volume, which persist between commands.  But the latter
is not clear.

It also suggests that the three phases are per-volume, so the controller could
initiate command phases on each of the volumes of each of the units.  For
instance, a disc seek and a tape seek could begin concurrently.  SS/80 devices
explicitly do not support overlapping commands on units or volumes within a
single device.  Overlapping commands between devices is allowed.


Utility Commands
----------------

The Initiate Utility command directs the addressed device to execute an internal
utility.  The command has three forms:

  Opcode  Meaning
  ------  -------------------------------------
   30H    Utility has no execution phase
   31H    Utility has a receive execution phase
   32H    Utility has a send execution phase

Utilities are device-specific.  Unfortunately, some utilities are required by
drivers.  For example, the RTE driver initiates utility C5H (Read Error Log) on
the 9144 to determine if the installed cartridge is certified.  Also, the EXER
and TAPE programs initiate utility C3H (Read Revision Numbers) on startup.  So
we must provide at least these.

The 9144 utilities are:

  Opcode  Param  Description
  ------  -----  ------------------------
   32H     30H   Read Memory
   32H     36H   Return Servo Status
   30H     37H   Set Amplifier Gain
   32H     38H   Return Amplifier Gain
   31H     3AH   Send Servo Command
   30H     3BH   Set Retry Mode
   32H     C1H   Read Error Summary
   32H     C3H   Read Revision Numbers
   32H     C4H   Read Drive Tables
   32H     C5H   Read [Runtime] Error Log
   32H     C6H   Read Error Rate Test Log
   32H     C7H   Read Use Log
   30H     C8H   Pattern Error Rate Test
   30H     CDH   Clear Logs
   30H     CEH   Preset Drive
   31H     D1H   Receive User Pattern

The TAPE exerciser program CERT command issues the Pattern Error Rate Test
utility command (30 C8 01 02 02 00 = loop 1, type 2, area 2, data source 0).

These utilities are defined for the 795x and 796x disc drives:

  Opcode  Params     Description
  ------  --------  -----------------------------
   30H    C8H       Pattern Error Rate Test (ERT)
   30H    C9H       Read-Only ERT
   30H    CBH       Random Pattern ERT
   30H    CCH       Random Read-Only ERT
   30H    CDH       Clear Logs
   30H    CEH       Preset

   32H    BFH       Servo Test
   32H    C0H       Locate and Read Full Sector
   32H    C3H       Read ROM Revision Number
   32H    C4H, 01H  Read Spare Table
   32H    C5H       Read Run Time Log
   32H    C6H       Read Error Log
   32H    C7H       Read Fault Log
   32H    C8H       Pattern ERT
   32H    C9H       Read Only Test
   32H    CBH       Random Pattern ERT
   32H    CCH       Random Read Only ERT
   32H    D4H       Read Defect ESDI List

The Read Spare Table command is supported on the 791x, 794x, and 795x drives.
The format of the returned data block consists of a series of lists, one per
defined drive head, as follows:

  Header

  Bytes  Item (one set per head)
  -----  ---------------------------------
    1    Head number
    2    Number of sectors spared
    1    Number of spare tracks used
    1    Number of defective tracks spared

Item 3 is the total number of factory and field tracks spared, while item 4 is
just the number of field tracks spared.

  Bytes  Item (one set per track)
  -----  ---------------------------------
    1    Cylinder address (high)
    1    Cylinder address (low)
    1    Spare track used (ordinal)
           bit 7 = factory spare

As simulated disc drives will not have any defective sectors, the Read Spare
Table command will consist of one five-byte header for each head defined in the
drive, with all of the sparing fields set to zero.  As such, there will not be
any spared-track entries.


Terms
-----

One 12821 Disc Interface has from 1-4 devices connected to the bus, each of
which has its own bus address between 0-7.  Each device contains a controller
and from 1-7 units, each of which may be operated in parallel.  Each unit may
contain from 1-8 volumes, which are not capable of parallel operation.

Our implementation supports only a controller and 1-2 units per device.  Each
unit supports only one volume.

  CS/80 Reference   SIMH Reference
  ---------------   ----------------------------------
  Interface Card    DC device
  Device            DCn MODEL=
  Address           DCn BUS=
  Unit              DCn unit
  Volume            DCn unit (only Volume 0 supported)

Multiple CS/80 units may reside at the same bus address, e.g., 7946 unit 0 is
the disc and unit 1 is the tape.  Each CS/80 unit needs its own image file, but
SCP has no way to attach multiple files to a single SIMH unit.  So we must use
one SIMH unit per CS/80 unit (actually, one would be needed per-volume, but all
of the CS/80 drives we want to support have only one volume per unit).

There are a couple of ways of mapping this:

 1. DCn is unit 0 and DCn+4 is unit 1

This pairs pair SIMH units, e.g., DC0 is address 0 unit 0, while DC4 is address
0 unit 1.  The 12821A only allows four CS/80 devices to be connected, so DC0-DC3
could be paired with DC4-DC7, respectively.  Tape units have a WRITE PROTECT
mechanism (actually, a cam on the cartridge), although discs do not.  Model
numbers must be kept consistent between pairs.

One downside is that most devices have only one unit, so e.g. setting DC0 to
7945 would have to disable DC4, while setting it to 7946 would have to enable
DC4.  Another is the user would have to remember the stride between SIMH units
to get the proper tape images attached.

 2. DCn/2 is unit 0 and DCn/2+1 is unit 1

This pairs DC0/1, DC2/3, etc. rather than 0/4 and 1/5.  This might be slightly
easier to remember for tape units, but connecting for discs would use DC0/2/4/6.

 3. DCx BUS=0 is unit 0, DCy BUS=0 is unit 1

This assigns units in increasing order to any DC units with the same address,
e.g.:

  - DC0 BUS=0 is unit 0
  - DC1 BUS=0 is unit 1
  - DC2 BUS=1 is unit 0
  - DC3 BUS=0 us unit 2

This removes the pairing, so the user is in charge of assigning unit numbers,
albeit indirectly.  It also allows for more than two units, though I don't think
this matches any real devices (when it exists, unit 1 is either an integrated
CTD or the removable platter of a 7907 drive).  It also conceivably allows eight
single-unit drives, although again the 12821A hardware doesn't permit this,
probably because of bus loading.  Still, if the module is reused for the HP
3000, this restriction may not apply (i.e., a GIC may allow more than 4 HP-IB
drives).

To avoid conflicts, setting any DCx model would set the models of all units on
same bus address.  This would require setting the bus address before the model,
e.g.:

  SET DC0 BUS=0;7946        -- sets address 0 unit 0 model 7946
  SET DC1 BUS=0             -- sets address 0 unit 1 model 7946
  SET DC0 7945              -- resets address 0 units 0/1 model 7945
  SET DC1 BUS=1;9144        -- resets address 0 unit 1 model 9144

After the last pair of settings, DC0 is 7945 bus 0 unit 0, while DC1 is 9144 bus
1 unit 0.

One rather serious problem is that an attached unit might be renumbered.
Consider the DC1 BUS=0 unit with an attached file.  If DC0 BUS=0 is subsequently
assigned, then the attached file silently moves from unit 0 to unit 1.  More
troubling, the parameters associated with unit 0 (last address, status, etc.)
now become the property of the new unit 0.  Similarly, if DC0 is then disabled
or reassigned to a new address, the file associated with DC1 unit 1 now becomes
unit 0.  There could be a check to determine if any assignment causes a
reassignment of another unit that has an attached file, but then the rejection
error, e.g., "Command not allowed", would be confusing because it would be
caused by a different unit than the one being assigned.

 4. DC0 BUS=0;UNIT=<n> is unit <n>.

This requires an explicit change by the user and so is better than assigning
units indirectly.  Changing one unit cannot affect another.  Moreover, because
an explicit change is required, changing an attached unit can be rejected, and
the rejection applies to the unit specified.

This would require a check to prevent confllicting assignments.  The check could
be at SET DCn time, but then swapping units would require an intermediate
assignment.  Also, enabling a unit with a conflicting bus assignment would have
to be rejected, but then the unit could not be reassigned because one cannot SET
the parameters of a disabled unit.

Alternatively, a check could be made at I/O initialization time, and conflicts
could be reported then (as with I/O conflicts).  Just before execution begins,
PON is dispatched to all devices, and the check could be scheduled then.  An
error code from the check cannot be reported back to the I/O dispatcher, but a
unit could be scheduled at zero time with a special code to indicate a check,
and the unit service (which will run before the first instruction is executed)
could return the error code to stop execution.

(The "io_initialize" routine currently ignores the return value from
"io_dispach".  However, it could test "result.skip" and abort initialization if
anyone returns ioSKF.  This would avoid the need for a zero-time service
activation.)


Addressing
----------

The current DI implementation works with unit numbers, rather than bus
addresses.  So, for example, the "acceptor" bit reflects a unit number rather
than an address, as does a parallel poll bit.  This worked OK for ICD discs, as
units map 1:1 to devices, and the fact that calls were made on a unit basis
rather than a bus address basis didn't matter (although, of course, parallel
poll priority wasn't honored).

A problem arises for CS/80, where SIMH units correspond to CS/80 units and not
devices.  A controller resides at a bus address and controls all SIMH units that
specify that same bus address.  So we need to revise DI and DI_DA to interface
via bus addresses instead of unit numbers.

Devices map 1:1 to bus addresses, and there is one controller per device, so
controllers also map 1:1 to bus addresses.  ICD and CS/80 units map 1:1 to SIMH
units.  While each ICD controller manages only one unit, each CS/80 controller
may manage multiple units.  The CS/80 Programming Manual says that a device may
have up to seven units, while the Set Unit command format provides for 16 units,
with unit 15 reserved for the device controller.  However, we need only support
one or two units, as all devices of interest contain either a disc drive or a
disc-and-tape drive combination.  We also need to support only a single volume
on each unit, so all units are addressed as Volume 0.  The only multi-volume
device is the 7907 fixed/removable disc drive, which we do not need to simulate.

(The 7912, et. al., may be ordered with a option specifying separate controllers
for the disc and tape, each with its own bus address, but that case may be
simulated by a single-unit 7912 and a single-unit 9144.)

CS/80 device controllers are implemented by an array of state structures,
indexed by bus address.  Each controller maintains these state variables:

  Variable           Reset Value
  ----------------   -----------
  controller state    Reporting
  current unit            0
  current volume          0

CS/80 units are implemented by an array of state structures, indexed by bus
address and unit number.  Each CS/80 unit maintains these state variables:

  Variable                                      Reset Value
  ------------------------------------------   -------------
  SIMH unit number                               unchanged
  transaction state                                Idle
  last quick status code                          2 or 0
  last full status                                PF or 0
  current address                                    0
  current and default length                        -1
  current and default burst                        false
  current and default RPS                          false
  current and default retry time                     0
  current and default status mask                  false
  current and default release                        0
  current and default options                        0
  current and default return addressing mode   single vector

The SIMH unit number connects the CS/80 unit with a SIMH unit.  The bus address
is kept in the "flags" field by the interface, and the unit number is kept in
the "u3" field.  The fields are set whenever a SET DCn BUS=n command is entered.
The SIMH unit number field is used to specify the unit to activate or cancel as
needed.  Within the unit service routine, the bus and unit number are used to
access the unit state array.

Before each command phase, the default values are copied to the current values.
Any complementary commands set the current values.  If a complementary ends a
command phase, i.e., is tagged with EOI, the current values are copied to the
default values.  During execution, the current values are used and updated (so,
for example, the length may be decremented as each byte is transferred).


Controls
--------

Other than self-test and diagnostic initiation switches, the 7911, 7912, and
7914 have HP-IB address switches.  The tape cartridge has UNLOAD, push-button
SAVE, and puch-button RESTORE switches.

The 7941 and 7945 are the same.  The 7942 and 7946 have the same, including the
three tape switches.  There is also an ONLINE indicator to show that the drive
has power and is ready for operation.

Simulation device commands are:

  - ADDRESS=<n>
  - DIAGNOSTIC
  - HPIB
  - DRIVES=<filename>   (maybe, for additional drive types)

or perhaps:

  - DRIVE=<model>;<id>;<maxcyl>;<maxhead>;<maxsector>

Simulation unit commands are:

  - ATTACH [ -N | -R ] <filename>
  - BUS=<n>             (n = 0-7)
  - UNIT=<n>            (n = 0-1)
  - 7nnn                (perhaps enumerate all of them)
  - 9144
  - 9145

Disc drives with removable media are the 7907 and 7935.  All other disc drives
use fixed media.  There is no data on the CS/80 responses to commands when no
media is loaded.  However, the SS/80 manual suggests that Not Ready is
appropriate for no media loaded.  This is probably applicable to detached fixed
units too.  When media is loaded, SS/80 drives set the Power Fail bit and QSTAT
= 2 to tell the host to reconfigure the drive.

Tape drives are the 9144, 9145, and 35401.  Tape cartridges come in long and
short versions and must be initialized before first use.  DESCRIBE reports the
maximum block as either the long tape value, the short tape value, or zero if no
tape is installed.  A tape cartridge may be write-protected by rotating a cam on
the cartridge.  For simulation, protection is either explicitly specified by an
ATTACH -R or implicitly by setting the file system's read-only bit.  Write
Protect status is set only in response to a write command -- not when the media
is loaded.  An Unload command unloads the cartridge but does not eject it; this
must tbe done manually.  In simulation, Unload corresponds to a automatic
detach.

We have to support two units (0 and 1) per device to handle disc + tape
combinations, such as the 7946.  Using a tape on unit 1 is the only way to use a
single HPDrive instance to emulate a disc drive for system access and a tape
drive for file save/restore.


Cartridge Tape Images
---------------------

CTD images are written in HPDrive's fixed SIMH-compatible format to allow both
random access and file mark/character count support (FST does WFMs).  An
Initialize Media command writes the full image in the proper format; the FORMAT
command of the FORMC program will do this.  ATTACH -N will also initialize the
new image.  Adding the -S switch will create and initialize a short tape.

ATTACHing an existing tape should check that the image is formatted.  If not,
Unitialized Media status should be returned on read or write access.  An image
is unformatted if the file does not contain an integral number of tape records,
and each record does not occupy exactly 1040 bytes (1024-byte data block plus
leading and trailing gap markers and leading and trailing length-word markers).
In particular, a new file (created without the -N switch) and a "bare" image
(.hpi) are considered unformatted.  The check is made when the image is
ATTACHed, and a flag is set; this is because the status can be cleared but must
be reestablished when another access is attempted.  The flag is cleared if an
Initialize Media is done.

Pre-existing "shrunken" tape images that are shorter than the 150-foot or
600-foot lengths are a problem.  For disc images, random writes beyond the
current file length automatically fill in the intervening bytes with zeros.
This won't work with tape images, as the intervening blocks must be filled with
formatted tape records.  The obvious solution is to:

 1. Mark a shurnken image as uninitialized.

If SIMH were used by itself, this would not be a problem.  Creating a new image
with ATTACH -N creates a file of the correct size, as will an Initialize Media
command against an uninitialized image file.  Unfortunately, HPDrive generates
and can use shrunken images.  So we need to accommodate them, which we can do in
one of several ways with increasing complexity:

 2. Automatically extend a shrunken image to the proper size when the file is
    attached.

 3. Extend a shrunken image to the proper size if ATTACH -X is done, else mark
    the image as uninitialized.

 4. If ATTACH -X is done, extend a shrunken image to the proper size and allow
    reads and writes.  Otherwise, automatically mark a shrunken image as
    read-only and return No Data Found errors for reads beyond the EOF.

 5. Set a "high-water mark" at attachment and return blocks of zeros for reads
    beyond the mark; each time a write extends the mark, write empty records
    between the prior mark and the current mark.

Option 2 is the simplest to implement, as initialize_media need only be extended
to allow specification of the first block to initialize, rather than always
starting at block 0, and all of the work is done in dc_attach.  Once the file is
extended, random access is guaranteed.  The drawbacks are that the user gets his
file extended without asking, and it fails if the host file is read-only.

Option 3 is nearly as easy to implement as Option 2, and it solves the problem
of extending the file without asking the user.  However, it still prevents use
if the file is read-only.

Option 4 allows the use of shrunken files, either for reading if -X is not
specified or the host file is read-only, or for reading and writing after
extending to the proper length.  It requires the initialize_media extension
of Option 2, plus some additional, though minimal, consideration in read_block.

Option 5 automatically allows full reading and writing without user
intervention.  But it requires the same read accommodation as Option 4, and in
addition, initialize_media be extended to allow specification of both starting
and ending blocks, and write_block must check for writes beyond the high-water
mark and call initialize_media when one is detected.

Option 4 is probably the best compromise, as it is not clear how often shrunken
files will be attached.


Formatting, Initializing, and Certifying Cartridge Tapes
--------------------------------------------------------

The Initialize Media command must be used to initialize a cartridge tape before
it is used.  HPDrive and simhtool create new images containing blocks consisting
only of erase gaps.  Ansgar uses this to return No Data Found errors.  He argues
that this is the correct response for a newly initialized tape before it is
written.  I had originally implemented Initialize Media by writing a zero-filled
data record in each block.  Ansgar argues that this is wrong.

This method fails because the SIMH tape library reads through erase gaps, so
that initializing a tape in this way, then writing block 5, and then reading
block 0 will return the data from block 5 with no error.  To prevent this, a
method is needed that returns a positive error to the CTD simulator and stops at
the current block.

The method chosen is a private tape marker, which is a feature of the extended
SIMH tape format.  The simulator was modified to write a block containing marker
0x77777777 followed by a gap.  Detection of this marker while reading produces
the No Data Found error.

However, I do not think this is correct.  My reasons are:

 - The RTE-6/VM DVM33/DVN33 Reference Manual says on page 3-4 that the No Data
   Found error means that "A block accessed during read has not been written.
   This generally occurs on [a] tape that has not been initialized."  If the
   error occurs because initialization was not done, then reinitializing should
   stop the error, not guarantee that it occurs on every block access.

 - Using FST to back up files to a newly initialized tape produces an RTE error
   when the program attempts to read the tape directory:

     FST> GO
     TR     0 E   1 U 8 U
       SCODE 12 EQT   1  SUBCH  8
       ADDRESS  1 QSTAT 1
          377       0       0    2000
            0       0       0       0
            0       0

     Unknown archive format
     Do you want to write over this archive (Y/N)?

   The DVM33 (CS/80 driver) manual says on page 7-1 that a "TR" error is an
   "unrecoverable error," and the driver returns this code for "severe errors."
   It is strange that using a new certified HP tape cartridge straight out of
   the box would normally produce a severe error, especially as this condition
   is NOT mentioned in the FST manual.

 - Tape drive support was added to DVM33 in 1984.  In 1989, DVM33 was changed to
   detect No Data Found and report it as a severe error.  Prior to this, the
   error had been ignored and not reported to the calling program.  This is the
   last change listed in the source.  The SR was 2200045856 (not 2200045956 as
   listed in the driver source).  Again, it is strange that an error that is
   expected every time a new cartridge is used would not be detected in the
   driver until the omission was reported five years later.

 - The RTE-6/VM Utility Programs Reference Manual says on page 9-30 for the
   FOrmat command, "If the tape has not been certified (the initialized media
   flag on the tape has not been set), a tape certification process is
   performed.  This includes writing a known pattern on every block of the tape
   and then rereading the tape to determine if there are any defective areas on
   it."  The process continues with sparing bad blocks and finishes by setting
   the "initialied media" flag.  There is no mention of erasing the data blocks
   before completing the command.

   The SS/80 manual says, "The data pattern left on the medium after executing
   [the Initialize Media] command is device dependent; the host should never
   rely on the device to fill each block with zeros or some other byte," so
   presumably the "known pattern" remains on the tape in the data blocks after
   the command completes.

 - Using the system utility FORMC to FOrmat and then VErify a tape image aborts
   the program:

     /FORMC: TASK? VE
     /FORMC: CS80 DISK OR TAPE LU? 24
     /FORMC: VERIFY ENTIRE TAPE (Y,N)? Y

     /FORMC: COMMAND EXECUTING -  VE,24,0,16352

     /FORMC: ACCESS ERROR   2000B
        NO DATA FOUND
        IDENTIFICATION FIELD:    377B     QSTAT:  1
        PARAMETER FIELD P(1) THRU P(10):
             0B        0B        0B        0B        0B
     /FORMC: ABORTED

   The verify command is rejected if the tape media is uninitialized.  Once the
   tape has been initialized with the format command, the verify is accepted
   but then aborts immediately.  So a FORMAT immediately followed by a VERIFY
   causes a program abort.  This seems unexpected, especially as there is no
   mention of this in the manual.  Also, if it was expected, the program would
   report an error and return to the TASK? prompt, not abort.  Moreover, unless
   every block on the tape had been previously written, a full-tape verify would
   abort the program when it reaches a block that returns No Data Found.  So
   virtually no tape cartridge would pass a full-tape verify.

 - The "LINUS External Reference Specification" says on page 7 in the Locate and
   Read section, "Attempting to read a block which has never been written will
   terminate the transfer with a No Data Found media error."  But certifying a
   cartridge certainly does write to the data blocks, as described in the
   utilities manual.  Perhaps "has never been written" does not mean "has not
   been written by the user since initialization" but instead means "has never
   been written since formatting" at the factory.

 - The "7908 Disc/Tape Drive Service Manual" says on page 2-4 that the
   Initialize Media command is "Used to initialize all of the data fields of the
   undefined media area."  Presumably, those are the system-accessible blocks
   outside of the defined user area.


There are some odd things in the documentation:

 - The CS/80 manual says, "If an uncertified tape is loaded, an Uninitialized
   Media error will result."  But then why does DVM33 issue the Read Error Log
   utility to determine if the tape is certified, rather than simply doing a
   Locate and Read of zero bytes?

 - The CS/80 manual shows that No Data Found is NOT returned for a Locate and
   Verify command.  This implies that such blocks are ignored, although the
   command description does not mention this.

 - The SS/80 manual says, "The data pattern left on the medium after executing
   [the Initialize Media] command is device dependent; the host should never
   rely on the device to fill each block with zeros or some other byte."

Nomenclature:

 - Formatting: A degaussed tape is first "formatted" at the factory.  This
   writes the tape keys across the full width of the tape to establish the block
   structure but does not write the data frames within each block.  Nor does it
   set up the sparing table or other tape tables.

 - Initializing: A formatted tape is initialized by writing the spare table,
   error log, run log, etc.  Initializing does not write the data frames.

 - Certifying writes data frames and then reads them back to determine if any
   bad blocks exist.  There is no evidence that certification erases the data
   before completing.

3M sold both formatted (DC600HC) and unformatted (DC600A) tapes.  HP sold both
certified and uncertified tapes.  All HP tapes were preformatted at the factory.

The Initialize Media command can be invoked without certification (C = 1).
Presumably this would write the spare blocks table, error logs, etc. but not
write the data frames or populate the spare blocks table with bad blocks.

So maybe:

 - An unformatted (erased) tape access returns Uninitialized Media because there
   are no keys to read.

 - A formatted but uninitialized tape access returns Uninitialized Media because
   the blocks containing tape logs or spares tables are blank (erased).

 - An initialized but uncertified tape returns No Data Found if a read is
   attempted because the data areas are blank (erased) until they are written
   for the first time.

 - A certified tape returns whatever data was written during certification
   without error.

And for simulation:

 - A tape image containing only data record or tape mark blocks is certified.

 - A tape image containing one or more "erased mark" blocks with all other
   blocks, if any, containing data records or tape marks is initialized but not
   certified.

 - A newly created (zero-length) tape image or an image in which one or more
   blocks contains no recognizable tape format is uninitialized.

 - A new tape image file may be initialized or certified by including an
   optional command-line switch.  These operations consist of writing "erased"
   blocks or 1024-byte data records containing 0xFF bytes, respectively,
   throughout the file.

 - The Initialize Media command with C = 0 will certify an uncertified image;
   if the image is already certified, then nothing is done.  With C = 1, an
   uncertified image remains uncertified, and nothing is done; a certified image
   is reset to uncertified if Z = 1 or unchanged if Z = 0, and nothing else is
   done.

 - The Execute Utility C8 (Pattern Error Rate Test) command with Type = 2 will
   certify an image regardless of prior status.  With Type = 0 or 1, the command
   is rejected with a Parameter Error result (these utilities are not
   implemented).


Stuff from "7908 Disc/Tape Drives Engineering Reference Package" 07908-90907,
Tape Interface Board (TIB) section:

 - 3.7.1 Gap Detection [PDF page 333]

   Gaps (blank areas of the tape) are detected by monitoring whether the RDDATB
   line (buffered RDDATA) is toggling in a legitimate MFM-encoded fashion. The
   output of the gap detector is GAPX-L and is shown in Figure 3.7.2a for a
   blank (though preformatted) tape and in Figure 3.7.2b for a recorded tape.

   [Figure 3.7.2.a on PDF page 336 shows that a preformatted tape has keys but
   no data frames.]

 - 3.B Control Lines and Modes of Operation, Task Completion Code Register
   [PDF page 340]

   Bit D6 is shown as "Blank Data Frames (BLANK)".

 - Address 4, Task Completion Code Register [PDF page 345]

   D6 = BLANK bit is set to signify that the previous block was read but
        contained no user data. This tells the microprocessor that there is
        nothing to be gained by reading the TIB buffers as the data they contain
        are left over from the previous transfer.

 - 3.8.3 Modes of Operation, 3.8.3.3 Mode 2, Read from Tape to TIB [PDF page 352]

   After data has been read and buffered in RAM, BLocK ENable will be checked to
   see if the processor has decided that this block is of value. If the enable
   is not set, then the data will be ignored and a search will begin for the
   next key mark; this constitutes provision for a fine seek under close
   supervision by the microprocessor.

   On the other hand, if the block was enabled, but contained no previously
   written data, it will be assumed that a mistake has been made with regard to
   the host's directory, a BLANK completion code will be issued and the drive
   brought to a stop. With a previously written tape, if the block was enabled,
   the TIB would signal a SUCCeSsful completion and issue on Interrupt FLaG,
   then await further instructions, presumably a DMA transfer.

 - 3.8.3.5 Mode 4, Verify N Blocks [PDF page 354]

   If the block was enabled, the TIB will continue to read and verify the data
   stored on the tape using CRCs until an error is found, whether it be in a
   data frame or an ECC frame or even in the key mark. Any of these errors will
   cause the tape to be stopped and an Interrupt FLaG to be raised along with a
   completion code indicating a Verify ERRor. Blank data blocks are ignored so
   that key marks may be verified on new tapes.

(through page 357)

6D B6 DB = 01101101 10110110 11011011 (from cart, 011 repeats)
B6 6D DB = 11010110 01101101 11011011 (from manual)

From the "CS/80 External Exerciser Reference Manual" (5955-3462 July 1988):

 - Tape Error Rate Tests (page 1-3)

   Certification (CERT) is a special type of WTR ERT which tests every block
   address on the entire tape and automatically spares all those block addresses
   which have errors.

 - Tape Initialization (page 1-6)

   On the tape unit, initializing the media establishes the sparing technique.
   All sparing techniques involve altering the addressing scheme to eliminate
   defective blocks.

   The INIT MEDIA command sets aside one out of every 512 blocks on the tape as
   an available spare.

   A certification test is also permitted while initializing a tape. CERT
   locates all defective blocks and skip-spares them if possible.

 - CERT (Tape Exerciser Commands, page 3-2)

   This command performs a write-then-read operation on the entire tape, and
   skip-spares any block which has two or more frame errors or has an unreadable
   key.

 - INIT MEDIA (page 3-6)

   This utility performs an initialization routine on the tape cartridge
   currently installed in the tape drive. Two options are allowed for tape
   initialization. The first option defaults the sparing table so that one spare
   block is set aside out of every 512 blocks. The second option accesses the
   run log to determine whether or not the tape is certified; if the tape is not
   certified, then the CERT error rate test utility is performed automatically.
   If the tape was previously certified, then all jump spares are converted to
   skip spares (for optimizing throughput).

     INPUT FORMAT:

     Input the TEST name?
     [INIT MEDIA]
     This utility will destroy data,
     Do you wish to continue?
     [YES] or [NO]
     INIT MEDIA options are:
     1 = Reset spare table and label
         tape uncertified.
     2 = Read certification status.
         If tape is uncertified then
         certification utility is
         run.
         If tape is certified then
         convert jump spares to skip
         spares. Tape is not recertified.
     Input option number?


The LINUS External Reference Specification (October 1984) has a table on page 11
that describes the results of the Initialize Media command for each of the eight
possible CWZ option combinations and for the three initial tape conditions.
They are summarized here:

  Formatted tape:

   C   W   Z   Spare Table   Data Area   Result Status
  --- --- ---  -----------   ---------   -------------
   0   0   0   Populated     Certified   Certified
   0   0   1   Populated     Certified   Certified
   0   1   0   Cleared       Unaltered   Initialized
   0   1   1   Cleared       Unaltered   Initialized

   1   0   0   Preset        Unaltered   Initialized
   1   0   1   Preset        Unaltered   Initialized
   1   1   0   Cleared       Unaltered   Initialized
   1   1   1   Cleared       Unaltered   Initialized

  Initialized tape:

   C   W   Z   Spare Table   Data Area   Result Status
  --- --- ---  -----------   ---------   -------------
   0   0   0   Populated     Certified   Certified
   0   0   1   Populated     Certified   Certified
   0   1   0   Cleared       Unaltered   Initialized
   0   1   1   Cleared       Unaltered   Initialized

   1   0   0   Optimized     Unaltered   Initialized
   1   0   1   Preset        Unaltered   Initialized
   1   1   0   Cleared       Unaltered   Initialized
   1   1   1   Cleared       Unaltered   Initialized

  Certified tape:

   C   W   Z   Spare Table   Data Area   Result Status
  --- --- ---  -----------   ---------   -------------
   0   0   0   Optimized     Unaltered   Certified
   0   0   1   Populated     Certified   Recertified
   0   1   0   Cleared       Unaltered   Decertified
   0   1   1   Cleared       Unaltered   Decertified

   1   0   0   Optimized     Unaltered   Certified
   1   0   1   Preset        Unaltered   Decertified
   1   1   0   Cleared       Unaltered   Decertified
   1   1   1   Cleared       Unaltered   Decertified

  Action        Meaning
  -----------   -----------------------------------
  Cleared       Set up with an empty allocation
  Preset        Set up with a specified allocation
  Populated     Set up and then modified by testing
  Optimized     Changed from jump to skip sparing


The "HP 9145A CE Service Handbook" (09145-90039 July 1988) suggests on page 9-11
(PDF page 75) that C and Z set the options, and W is ignored.



INIT MEDIA does Initialize Media, options = 101 for spare table init
  (inhibit cert, alloc spares, reset spares) --> no-data markers

INIT MEDIA does Initialize Media, options = 000 for no spare table init
  (do cert, alloc spares, convert spares) --> FF data records

CERT does Initiate Utility, Pattern ERT (C8 01 02 02 00)
  (loop, type CERT, area entire, internal pattern) --> FF data records

Rik Bos tests:

 - new HP cert:
   - loc and read -> FF bytes, qstat 0
   - init media 07H -> qstat 0 after 7 seconds
   - loc and write -> qstat 1
   - req stat -> Uninitialized Media, DERRORS = D1 (Not Certified)







State Machine
-------------

Most commands do not have an execution state.  The only exceptions are:

  - Locate and Read
  - Cold Load Read
  - Locate and Write
  - Request Status
  - Describe
  - Initiate Utility
  - Read Loopback
  - Write Loopback

Receive secondary:
  idle -> opcode_wait

Receive opcode:
  parameters:
    opcode_wait -> parameter_wait
  no parameters, no EOI (complementary):
    opcode_wait -> execution -> opcode_wait
  no parameters, EOI:
    opcode_wait -> execution -> reporting
    opcode_wait -> read_execution -> reporting
    opcode_wait -> write_execution -> reporting

Receive parameter:
  count > 0:
    parameter_wait -> parameter_wait
  count = 0, no EOI:
    parameter_wait -> execution -> opcode_wait
  count = 0, EOI:
    parameter_wait -> execution -> reporting
    parameter_wait -> read_execution -> reporting
    parameter_wait -> write_execution -> reporting


Error Recovery
--------------

There are two basic types of CS/80 errors: "operational errors," such as an
uncorrectable data error, an undefined command opcode, or requesting an address
out of range, and "sequence errors," such as sending an undefined secondary or
sending messages in the wrong order.  Operational errors arise from the content
of valid messages, whereas sequence errors arise from the framework of messages.
The CS/80 documentation is somewhat vague on exactly how errors are handled, but
the basic idea appears to be to terminate the current command immediately and
then move to the reporting wait state.  Bytes are then sourced or sunk as needed
to allow the host to resynchronize at the reporting phase.  Also, once an
operational error has occured, a subsequent sequence error caused by the
unexpected shift to the reporting phase is suppressed.

It is also not clear what happens when a new command is issued after a QSTAT = 1
report.  There are documentation references that a host "must" issue a Request
Status command to pick up the error and clear the QSTAT value.  However, the
Request Status command description says that the report is "the cumulative
status of all transactions which have occurred since the status report was last
cleared," implying that commands may be executed while QSTAT = 1.  Also, the
SS/80 manual says that there is no holdoff on QSTAT = 1, implying that a new
command will be accepted.  However, this would be dangerous if, for example, a
failed Locate and Write (which stops at the failure point) is followed by a new
Locate and Write that expects to continue from the last (good) position.
Testing the behavior of real hardware may be needed here.

For operational errors occurring in the command phase (e.g., Address_Bounds,
Illegal_Opcode, or Illegal_Parameter) the SS/80 manual says that interpretation
stops at the first error and susbequent (complementary and/or real-time)
commands are ignored.  We call the "set_error" routine to set the specified
error status bit, set QSTAT = 1, and set the controller state to Reporting_Wait.
Command errors then set the controller opcode to Invalid.  If the error occurred
with the EOI byte, poll response is enabled to notify the host of the reporting
phase.  Otherwise, bytes received in the Reporting_Wait state are sunk by
"process_data" until EOI, whereupon the poll response is enabled.

Note that an illegal opcode in a transparent message generates a Message
Sequence error, not an Illegal Opcode error (per the CS/80 manual).

If an error causes a command with an execution phase (e.g., Locate and Read) to
be ignored, an unexpected execution message will be received.  This will be in
the wrong state (Reporting_Wait instead of Execution_Wait), which would normally
cause a Message Sequence error.  However, the error is suppressed because of the
earlier operational error; this is detected by controller opcode = Invalid.  In
this case, "process_secondary" changes the state either to Error_Source or
Error_Sink and set up the transfer as appropriate.  These states transition to
Reporting_Wait and enable the poll response once EOI is sent or received to
complete the execution phase.  The host will then pick up the QSTAT = 1 in the
reporting phase.

For operational errors occurring in the execution phase (e.g., Message Length,
Not Ready, or End of Volume), we call "set_error" and then optionally reset the
state to Error_Source or Error_Sink as dictated by the specific error condition.
If a partial write buffer is present, and the error does not preclude writing,
it is written before exiting the execution phase.

The only operational error in the reporting phase is not accepting the QSTAT
byte, i.e., the bus source routine returns a "no listeners" indication.  This
causes a Message Length error.  The documentation does not say if this leaves
the controller in the reporting state or returns it to the command ready state.
Presumably it does not matter, as untalking the controller in the middle of the
reporting message must only preface a Cancel or a Clear command.

During any phase, the host may asynchronously Untalk or Unlisten the device in
the middle of a transaction, i.e., while the controller is busy.  The
documentation does not mention this case, although DVM33 does exactly this to
obtain the initialization state of CTD media.  After accepting the first four
bytes of data in the execution phase of a 9144 Read Error Log command, the
driver Untalks the device and then issues a Cancel to clean up the transaction.
Whether this behavior results in a Message Length error or a Message Sequence
error is not specified, and it doesn't matter as the Cancel clears the status
indication.  We report a Message Length error for an unexpected Untalk or
Unlisten.

Note that the SS/80 manual says that Cancel and the various Clears are accepted
only in the command ready state, while the CS/80 manual says that, "The
recommended way to terminate a transaction is to terminate the message link,
then send the Cancel command."  That implies that Cancel is accepted in any
phase.


Recovery from Read Errors
-------------------------

The "read_block" routine reads from one or more blocks into the device buffer.
It returns FALSE if an error prevented any data from being read or TRUE if the
buffer contains data, even if an error occurred during the read.

The errors that can occur are:

 - End_of_File
 - End_of_Volume
 - Unrecoverable_Data
 - Unrecoverable_Data_Overflow

End_of_File occurs if a tape mark is read from a cartridge tape drive.
End_of_Volume occurs if a read would extend beyond the last block of the volume.
All disc I/O errors and all magnetic tape errors except MTSE_TMK are mapped to
Unrecoverable_Data.

Error recovery is complicated by the fact that the routine may be asked to read
more than one disc block at a time.  When executing a Copy Data command from
disc to tape, four disc blocks will be requested at a time, and any of the
errors can occur on the first block, on an intermediate block, or on the last
block.

Another complication is that the controller may or may not be in the middle of
an execution message when the error occurs.  Reads are performed wholly within
the Command_Wait state for Locate_and_Verify and Copy_Data, as well as for the
initial read for Locate_and_Read and Cold_Load_Read.  For the latter two,
additional reads occur in the Execution_Send state.

Still another complication is that the error response is different depending on
whether or not a Locate_and_Verify command is executing.  Unrecoverable_Data
errors normally are logged but do not stop the read process; these errors are
fatal for Locate_and_Verify.  End_of_File errors normally are fatal during a
read; they are ignored for Locate_and_Verify.

Recovery depends on the specific error and can take one of three forms: an abort
that stops the sequence immediately, an error that is logged but allows reading
to continue, and a read without errors that will fail immediately at the next
entry.  End_of_File causes aborts, Unrecoverable_Data logs the error and
continues, and End_of_Volume logs the error but also ends the read transaction
to prevent another entry.  End_of_Volume is also required to wrap the address to
block 0.

Note that End_of_File only occurs when reading tape units, and read_block never
reads more than one tape block per call.  Therefore, if an End_of_File occurs,
no data was read.

On each entry, the file is attached and positioned correctly.  A Not_Ready error
cannot occur because the unit must be attached to pass the validation check made
before the first read (or write) is issued, nor can it occur while a command is
executing because detaching is prohibited if the unit is active.

End_of_Volume can never be present on routine entry, because the address can
never point beyond the end of the volume.  If Set Address attempts it, the
command is rejected with Address Bounds, and if a read increments it, it wraps
to zero.  End_of_Volume is always set after the reads complete within a call, so
some data will always be read.

For commands with execution phases (e.g., Locate_and_Read), aborts must
terminate the phase by asserting EOI on the final byte sent.  If a read sequence
aborts before reading any data into the buffer, e.g., the first block addressed
contains a tape mark, an error byte of value 1 must be sent with EOI when the
execution phase begins.  If the tape mark occurs on the first block of a
subsequent read, the error byte tagged with EOI must also be sent.

Within an execution phase, errors that do not abort must not change the
controller state, so that the remaining data is sent.  CS/80 requires that reads
continue in the presence of data errors until the original request length is
satisfied.  End_of_Volume errors must shorten the remaining transfer length to
the amount of data read into the buffer so that the last valid data byte will be
tagged with EOI.  When the host issues the reporting message, the error will be
reported then.

Here are some examples that assume that the addressed volume has 100 blocks:

 * A Locate and Read is issued for three blocks starting at block 97.  The
   routine returns TRUE for all three reads, the state remains Execution_Send,
   and no error occurs. The address is reset to block 0.

 * A Locate and Read is issued for three blocks starting at block 98.  The
   routine reads block 98 and returns TRUE.  The routine reads block 99, an
   End_Of_Volume error is logged, the transfer length is shortened to the buffer
   count, the state remains Execution_Send, and the address is reset to block 0.
   The event service ends the transfer normally by tagging the last byte with
   EOI when the length is exhausted.  The third block is never read.

 * A Locate and Read is issued for two blocks starting at block 99.  The routine
   returns TRUE for the first read, an End_Of_Volume error is logged, the
   transfer length is shortened to the buffer count, the state remains
   Command_Wait, and the address is reset to block 0.  When the execution
   message arrives, the first block of data is sent.  The event service ends the
   transfer normally by tagging the last byte with EOI when the length is
   exhausted.  The second block is never read.

 * A Locate and Read is issued for three blocks starting at block 0.  The read
   of block 0 fails with a host I/O error.  The routine returns TRUE, the
   address increments to block 1, an Uncorrectable_Data error is logged, and the
   state remains Command_Wait.  After blocks 1 and 2 are read, the event service
   ends the transfer normally by tagging the last byte with EOI when the length
   is exhausted.

 * A Locate and Read is issued for three blocks starting at block 0.  The read
   of block 0 succeeds but block 1 fails with a host I/O error.  The routine
   returns TRUE, the address increments to block 2, an Uncorrectable_Data error
   is logged, and the state remains Execution_Send.  After block 2 is read, the
   event service ends the transfer normally by tagging the last byte with EOI
   when the length is exhausted.

 * A Locate and Read is issued for three blocks starting at block 0.  The read
   of block 0 succeeds but block 1 fails with a host I/O error.  The routine
   returns TRUE, the address increments to block 2, an Uncorrectable_Data error
   is logged, and the state remains Execution_Send.  The read of block 2 also
   fails with a host I/O error.  The routine returns TRUE, the address
   increments to block 3, an Uncorrectable_Data_Overflow error is logged, and
   the state remains Execution_Send.  The event service ends the transfer
   normally by tagging the last byte of block 2 with EOI when the length is
   exhausted.

 * A Locate and Read is issued for three blocks starting at block 0.  The
   routine reads block 0, which contains a tape mark.  The routine returns
   FALSE, an End_Of_File error is logged, the state changes to Error_Wait, and
   the address increments to 1.  When the execution message arrives, the state
   changes to Error_Source.  The event service ends the transfer by tagging an
   error byte with EOI.  The remaining blocks are never read.

 * A Locate and Read is issued for three blocks starting at block 0.  The
   routine reads block 0 and then block 1, but block 1 contains a tape mark.
   The routine returns FALSE, an End_Of_File error is logged, the state changes
   to Error_Wait, and the address increments to 2.  The event service detects
   the FALSE return and changes to Error_Source to supply the error byte.  The
   remaining blocks are never read.

 * A Locate and Verify is issued for three blocks starting at block 0.  The read
   of block 0 succeeds but block 1 fails with a host I/O error.  The routine
   returns FALSE despite data in the buffer, the address increments to 2, an
   Uncorrectable_Data error is logged, and the state remains Command_Wait.  The
   command executor detects the FALSE return and aborts the command.  The
   remaining blocks are never read.

 * A Locate and Verify is issued for three blocks starting at block 0.  The
   routine reads block 0 and then block 1, but block 1 contains a tape mark.
   The tape mark is ignored, and the routine returns TRUE although the buffer
   is empty, the address increments to 2, no errors are logged, and the state
   remains Command_Wait.  The remaining block is read, and the verify completes
   normally.

 * A Copy Data from disc to tape is issued for six blocks starting at block 98.
   The routine reads blocks 98 and 99 and returns TRUE.  An End_Of_Volume error
   is logged, the transfer length is shortened to the buffer count, the state
   remains Command_Wait, and the address is reset to block 0.  The command
   executor ends the copy normally when the length is exhausted.  The remaining
   blocks are never read.

 * A Copy Data from tape to disc is issued for six blocks starting at block 0.
   The routine copies blocks 0 and 1 successfully and then reads block 2, but
   block 2 contains a tape mark.  The routine returns FALSE, an End_Of_File
   error is logged, the state changes to Error_Wait, and the address is
   incremented to 3.  The command executor detects the FALSE return and aborts
   the command.  The remaining blocks are never read.

 * A Copy Data from tape to disc is issued for six blocks starting at block 0.
   The routine reads block 0, which contains a tape mark.  The routine returns
   FALSE, an End_Of_File error is logged, the state changes to Error_Wait, and
   the address is incremented to 1.  The command executor detects the FALSE
   return and aborts the command.  The remaining blocks are never read.

Error recovery during Copy Data depends on whether the routine returns TRUE or
FALSE. For TRUE, the corresponding write routine is called, and the loop
terminates because of Error_Wait.  For FALSE, no data was read, so the write is
skipped, and the loop terminates explicitly.


The read recovery rules may be summarized as follows:

 - An End_of_File error is ignored and returns TRUE for Locate_and_Verify,  For
   all other commands, it increments the address, changes to Error_Wait, and
   returns FALSE.

 - An End_of_Volume error resets the address to 0, retains the current state,
   shortens the transaction length to the buffer length, and returns TRUE.

 - An Unrecoverable_Data error sets the data_error flag, increments the address,
   retains the current state, and returns FALSE for Locate_and_Verify and TRUE
   otherwise.


Current Operational Values
--------------------------

Each CS/80 unit keeps two sets of operating values: a persistent set and a
current set.  Each command begins with identical sets, and the current set is
always used during a command.  A command sequence containing complementary
commands change the current set for the duration of the command only; the
current set values revert to the persistent set at the end of the command.
However, if the sequence ends with a complementary command (i.e., contains only
complementaries), then the persistent set values are changed to the current set.
When the device is powered on, or after a Clear command, both sets are
initialized to default values.

While this is the logical picture, in practice a command transaction may end in
one of several ways: with normal completion after the reporting phase, with
error completion after the reporting phase, or via a channel abort (e.g.,
untalking while the device is sending data).  Some commands complete
immediately, while others require a subsequent execution phase to transfer data.

The problem is that the host may not follow the transaction sequence, for
example skipping a required reporting phase.  This produces a Message Sequence
error but is otherwise recoverable.  So resetting the current set during the
reporting phase is not reliable.  Nor is resetting after an execution phase
completes, because again the host can abort the phase with an Untalk or
Unlisten.  If the current set is to be reverted reliably at the end of each
command, it is necessary to accommodate all of the possible ways that a command
can end.

A simpler and more reliable approach may be to revert the set at the start of
every command, rather than at the end.  Assuming that each unit starts out with
its current and persistent sets equal, then every unit that had processed a
command has an incorrect current set.  However, that set will not be referenced
until another command is issued to that unit.  Resetting the current set at the
start of such a command would have the same effect as resetting it at the end of
the prior command.

The start of a command is reliably indicated by reception of a device command
secondary.  On entry, the last unit number accessed for the device can be used
to reset the current set.  If that command includes a Set Unit complementary to
change the target unit, then that unit's current set will now be changed.  But
the next command directed to that new unit will reset its current set.  Each
command, therefore, starts with a correct current set, and resets occur reliably
because each command must start with a device command secondary.

One exception to this is the Copy Data command.  This command is directed to
unit 15 but changes the current sets of two units.  The next command entry will
reset unit 15's current set but units 0 and 1 (e.g.) will retain the wrong
current values.  This can be handled in the Copy Data routine itself.

A pathological command sequence containing a Set Unit after the current unit's
values have been changed (e.g., Set Length, Set Unit, Set Length) could not be
accommodated by resetting the last unit's current set at the next command entry.
However, this cannot occur, because a Set Unit command appearing anywhere other
than at the start of a sequence results in an Illegal Opcode error.  So at most
one unit's set must be reset per command.


Message Sequence Errors
-----------------------

Sequence errors occur when the required sequence of messages is not followed.
CS/80 requires that the Command-Reporting or Command-Execution-Reporting
messages appear in this order.  Violation results in a Message Sequence error.

It is not clear how the controller recovers from sequence errors.  It would seem
that the host that issued the "wrong" message would expect it to complete.  For
example, if the controller receives an unexpected execution message, it still
must satisfy that message, because the host will be sending or expecting to
receive some number of bytes.  In particular, if a command is executing, and the
"wrong" message arrives, the controller can't simply ignore the message and
continue with the command.

Imagine a device that has processed a Locate and Write command message and is
now waiting for an execution message to receive the data.  Instead, it receives
a Request Status command message.  This causes a Message Sequence error, but
then what happens?  Presumably, the host has forgotten what it was doing and is
now going to send an execution message to read the status bytes, followed by a
reporting message to obtain the QSTAT.  Clearly, the device can't discard the
Request Status command and then expect to receive bytes to continue the Locate
and Write.  Instead, it must absorb the full set of Command-Execution-Reporting
messages before the device and host resync in the reporting phase.

Note also that once a command message has been properly received, the command
either executes to completion before requesting the reporting message, or it
never gets started because it needs to transfer data in an execution message.
Either way, the command is not in a partially completed state that somehow must
be ameliorated (except...if a Set Burst has been done, then a partial read or
write could occur, as bursts are in 256-byte chunks, and a partial tape block
could have been transferred).

Also, unless a channel abort occurs, all messages execute to completion.  So, by
definition, sequence errors occur between messages; aborting a message in the
middle can be done only by Untalking or Unlistening the device and generally
cause Message Length errors because the full complement of bytes were not
transferred.  This means that there are six possibilities:

  1. Expecting a command message but receives an execution message.

  2. Expecting a command message but receives a reporting message.

  3. Expecting an execution message but receives a command message.

  4. Expecting an execution message but receives a reporting message.

  5. Expecting a reporting message but receives a command message.

  6. Expecting a reporting message but receives an execution message.

After setting a Message Sequence error (except for case 2), recoveries from
these cases proceed as follows:

  1. The state moves to Error_Source or Error_Sink to source one byte tagged
     with EOI or to sink as many bytes as the host sends.  After EOI, the state
     moves to Reporting_Wait.

  2. This case is permitted and represents a "bare" reporting message.  The
     state moves to Execution_Send to send the QSTAT byte.

  3. The state moves to Error_Wait, and all command bytes are sunk.  After EOI,
     poll is enabled to request either the execution or reporting message,
     depending on the command requirement.

  4. The state moves to Execution_Send to send the QSTAT byte.

  5. Same as case 3.

  6. Same as case 1.

Cases 3 and 5 are different for transparent commands.  These do not cause
Message Sequence errors ("transparent" means transparent to the device command
processor) and are executed normally.  Cancel and the several clears are
transparent commands and must be allowed to execute to terminate transactions.


Extra Stuff
-----------

// add a current usptr to device state?  saves recalc on each service entry?
//   set at Set Unit command?
//   can't remove dsptr->unit and use dsptr->usptr->unit because Copy Data?


// DO WE EVER SCHEDULE using the controller unit (unit 15, remapped to unit 0's uptr)?
//  --> YES, for execution phase for Request Status, Describe, etc. with unit = 15


// DO WE need to clear the unit states of unassigned units?
//   so that a scan of all units is valid?
//   memset (&devices [address].units [index], 0, sizeof (UNIT_STATE));


// implement tape character count option?  How does it even work?
// manual says reads partial record and skips to next record
// so length = 1024 could be one block or 1024 blocks (of one byte each)
// moreover, if multiple blocks are read, bytes are glued together in byte stream
//   with no indication where record boundaries were.


// OK to execute commands with qstat = 1; each command adds to the error list until it's cleared


// for Copy Data, DVM33 cache does not fault if disc is write protected!!!
//   but then, disc CANNOT be write protected in hardware....
// SOME QUESTIONS remain about which unit gets what errors....


// if we need to suppress sequence error, this compiles to movl, testl, je:
//if (error == Message_Sequence && TO_32_BITS (usptr->status, 0) != 0)
//    masked = TRUE;


/* 3000 does (module 43):
     IFC
     Amigo Clear
     Amigo Identify
     "bare" Reporting
     Locate and Read using prior Unit and Volume
     Locate and Write using Length 0
     Set Unit 15, Release 1:0
     Set Unit 0, Set Volume 0, Describe

not
seen
----
     CDB'BLK'DISPL      =  %22,  << SET BLOCK DISP COMMAND >>
     CDB'CANCEL         =  %11,  << CANCEL COMMAND >>
x    CDB'CLEAR          =  %10,  << [CI] CLEAR COMMAND >>
     CDB'DESCRIBE       =  %65,  << DESCRIBE COMMAND >>
     CDB'INIT'DIAG      =  %63,  << INIT DIAGNOSTIC COMMAND >>
     CDB'INIT'MEDIA     =  %67,  << INITIALIZE MEDIA COMMAND >>
     CDB'INIT'UTIL      =  %60,  << NO EXECUTING MSG >>
     CDB'NOP            =  %64,  << NO OPERATION COMMAND >>
     CDB'PARITY'OP      =    1,  << PARITY OPCODE COMMAND >>
     CDB'READ           =    0,  << LOCATE & READ COMMAND >>
     CDB'READ'LOOPBK    =   %2,  << READ LOOPBACK COMMAND >>
     CDB'RELEASE        =  %16,  << RELEASE COMMAND >>
     CDB'RELEASE'DENY   =  %17,  << RELEASE DENIED COMMAND >>
x    CDB'REQ'STATUS     =  %15,  << REQUEST STATUS COMMAND >>
     CDB'SET'LENGTH     =  %30,  << SET LENGTH COMMAND >>
     CDB'SET'MASK       =  %76,  << SET STATUS MASK CMD >>
     CDB'SET'RELEASE    =  %73,  << SET RELEASE COMMAND >>
     CDB'SET'RETADR     = %110,  << SET RET ADDR MODE CMD >>
     CDB'SET'RETRY      =  %72,  << SET RETRY TIME COMMAND >>
     CDB'SET'RPS        =  %71,  << SET RPS COMMAND >>
     CDB'SET'SNGL'VEC   =  %20,  << SET SNGL VEC ADDR CMD >>
     CDB'SET'3'VEC      =  %21,  << SET 3 VECTOR ADDR CMD >>
     CDB'SET'UNIT       =  %40,  << SET UNIT# COMMAND >>
x    CDB'SET'VOL        = %100,  << SET VOL# COMMAND >>
     CDB'SPARE'BLK      =   %6,  << SPARE BLOCK COMMAND >>
     CDB'VERIFY         =   %4,  << VERIFY COMMAND >>
     CDB'WRITE          =   %2,  << LOCATE & WRITE COMMAND >>
     CDB'WRT'LOOPBK     =   %3,  << WRITE LOOPBACK COMMAND >>
*/



----------------------------
Disc Simulation Partitioning
----------------------------

The goal is to support CS/80 drives on both the 1000 via the 12821A Disc
Interface and the 3000 via the 31262A General I/O Channel with no change to the
CS/80 protocol handler.  An associate goal is to support MAC drives on the 1000
via the 13175D interface and the 3000 via the 30229B Cartridge Disc Interface.
Both systems share the 13037 MAC Disc Controller.

A further goal is to support the ICD drives on the 1000 via the 12821A Disc
Interface, given that the ICD command set is a subset of the MAC command set. It
may also be desirable to support the 12745A MAC-to-HP-IB interface to allow MAC
drives to run via HP-IB on GIC-configured 3000 systems.

Ideally, the simulations would be layered and modular:

  1.1  12821 DI
  1.2  31262 GIC

   [ connect via HP-IB ]

  2.1  ICD controller
  2.2  CS/80 controller
  2.3  MAC controller

   [ connect via disc cabling ]

  3.1  Disc library

MAC addressing assigns each drive a logical unit number from 0-7, set by a
front-panel rotary switch.  ICD addressing assigns each drive an HP-IB address
from 0-7, set by a front-panel rotary switch.  Each MAC or ICD drive contains
one addressable unit, which corresponds to one SIMH unit whose settable model
number defines the drive characteristics.  Attaching the unit is equivalent to
installing a disc pack and making the drive ready.

CS/80 addressing assigns each drive an HP-IB address from 0-7, set by DIP
switches.  Each drive may contain one or more addressable units -- typically,
disc drives contain one unit, while disc/tape combination drives contain two
units.  Each addressable unit corresponds to one SIMH unit, so a given drive at
a unique HP-IB address may contain two SIMH units.  Each unit within the drive
is separately attachable and shares the same model number but has independent
characteristics.  Attaching a disc is equivalent to installing a disc pack for
drives with removable media (the 7907 and 7935) or applying power for drives
with fixed media (all other CS/80 drives).

MAC, ICD, and CS/80 controllers respond to different secondaries as well as
different command opcodes.


-----------------------------------
HP-IB Simulator to Drive Simulators
-----------------------------------

t_bool di_bus_source (CARD_ID card, uint8 data);

  Source a data byte to the bus.  Returns TRUE if the byte was accepted (i.e.,
  there were one or more listeners) and FALSE if it was not.  Called by the
  controller to send commands to devices, and called by the current talker to
  send data to the listener(s).  ATN and EOI should be asserted as required on
  the bus before calling.

  RTE requires that all devices on a given card use the same driver, so ICD
  discs, CS/80 discs, and Amigo tape drives must all use separate cards.
  Therefore, the type of protocol may be set statically.

  MPE allows a mix of devices on a GIC, although some restrictions apply.  So
  the protocol type may vary between devices on a common interface and must be
  determined at the device (SIMH unit) level.  A CS/80 disc, Amigo tape, and PCL
  line printer might share a single GIC.


t_bool **_bus_accept (uint32 unit, uint8 data);

  Accept a data byte from the bus.  Returns TRUE if the byte was accepted and
  FALSE if it was not.  Called by di_bus_source to handshake between source and
  acceptor. If ATN is asserted on the bus, the byte is a command; otherwise, it
  is data.  If EOI is asserted for a data byte, it is the last byte of a
  transmission.

  An acceptor monitors the bus for primaries.  As RTE requires all devices on a
  card to use the same protocol, the acceptor implements the secondary protocol
  for all devices (SIMH units).  Addressing to talk or listen is also recognized
  by the acceptor if the address matches a device on the bus.


void di_bus_control (CARD_ID card, uint32 unit, uint8 assert, uint8 deny);

  Set the control lines on the bus.  Called by the system controller to assert
  or deny REN or IFC, by the current controller to assert or deny SRQ, NRFD, or
  ATN and EOI (to conduct or conclude a parallel poll), and by the current
  listener to assert or deny NRFD.  All connected devices on the bus are
  notified of the changes.  It is not necessary to call di_bus_control for
  changes to ATN and EOI that accompany a command or data byte, as these are
  picked up when the data byte is examined.


void **_bus_respond (CARD_ID card, uint32 unit, uint8 new_cntl)

  Respond to changes in the control lines on the bus.  Called by di_bus_control
  to inform each connected device of a change in control state.


void di_poll_response (CARD_ID card, uint32 unit, FLIP_FLOP response);

  Set a device's poll response.  Called by a device to enable or disable its
  response to a future parallel poll.


--------------
From DI to GIC
--------------

The HP2100 disc interface allows only one type of protocol (Amigo or CS/80) to
reside on a given bus.  So for instance the DC device processes only CS/80
commands.

The HP3000 General Interface Channel allows multiple protocols on a single bus.
To accommodate this, each type of bus acceptor and responder must be called for
each bus broadcast.  It is up to the acceptor to decide if the broadcast is
meant for one of its devices.  The acceptor maintains a bitmap of device
addresses present on its bus and qualifies Talk and Listen addressing with the
present map.

The Amigo Identify command presents a bit of difficulty.  A given protocol
handler (e.g., CS/80) must differentiate between an Untalk and a Talk directed
to another handler's device.  Simply keeping track of "my talker" and "not my
talker" is not sufficient, because an Other Talk Address followed by a secondary
is NOT an Identify sequence.  Also, the Untalk and MSA must be sequential; any
intervening primary inhibits Identify recognition.

One option is to set a flag when Untalk is received and clear it when any other
primary or secondary is received.  Then, an Identify is recognized only if the
flag is set when MSA is received.


NO:
Therefore, the channel must record all Listen and Talk addressing but then
qualify those with the presence map to decide if a secondary or data is to be
recognized.  The channel state, in part, is then:

    uint32  present;      /*   bus address bitmap of devices present on the bus */
    uint32  listeners;    /*   bus address bitmap of the listeners */
    uint32  talker;       /*   bus address bitmap of the talker */

For secondaries and data, channel.listeners or channel.talker is ANDed with
channel.present to validate addressing.

No special Untalk or Unlisten handling is necessary; they are just processed as
Listen 31 or Talk 31, i.e., they set channel.listeners or channel.talker to just
bit 31 (except that Listen would OR the bit value, while Unlisten would store
the bit value).  This allows Untalk to be differentiated from Other Talk
Address for the Identify command.

The bus responder wants to reschedule a unit waiting on a data transfer that had
been held off by ATN or NRFD.  The channel.talker bitmap would have to be
converted to an index to determine the correct unit (via dsptr->units).

Routines: (not correct...)

t_bool cs80_bus_accept (uint8 data, BUS_SET signals);

  Each acceptor registers with the interface if any of its devices are connected
  to the bus.  For example, if a CS/80 disc is at address 0 and an Amigo tape is
  at address 3, both "cs80_bus_accept" and "tape_bus_accept" would set their
  respective bits in the interface's "acceptors" word.  A byte placed on the bus
  would then be sent to the two acceptors but not to "icd_bus_accept", whose bit
  is not set.

  The acceptor maintains a list of the listeners and talker on the bus, so it
  decodes (un)listen and (un)talk commands.


t_bool cs80_bus_decode (uint8 address, uint8 data);

  Each bus decoder receives commands and data directed to a specific HP-IB
  address.  The address indexes into an array of device controllers, one for
  each enabled device on the bus (a disabled device is disconnected).  Each
  device controller maintains a list of SIMH units that comprise the HP-IB
  device, so that the controller in combination with unit addressing can
  determine on which SIMH unit to schedule for a given operation.

  When the acceptor receives a universal command, it calls the bus decoder once
  for each address (0-7).  If the device is disabled, the decoder simply
  returns.  Otherwise, it processes the command.

  When the acceptor receives a listen or talk command, it calls the bus decoder
  for the addressed device.  For unlisten or untalk commands, it calls the bus
  decoder for each addressed device.  If a device is addressed to talk when a
  talk command arrives for another device, the acceptor first generates an
  untalk for the prior device before calling the decoder for the talk.

  Secondary commands are passed from the acceptor to the decoder by calling the
  latter once for each talker or listener on the bus.  Data received by the
  acceptor is passed to each listener.

-----

t_bool di_bus_source  (CARD_ID card, uint8 data, BUS_SET assertions);
void   di_bus_control (CARD_ID card, uint32 address, BUS_SET assert, BUS_SET deny);
t_bool di_bus_ready   (CARD_ID card);

t_bool **_bus_accept (uint8 data, BUS_SET signals);
void **_bus_respond (uint32 address, BUS_SET new_cntl);

-----

The existing implementation requires that the device modules (DA, DC) access a
few DI internals, specifically:

  - Testing if the card is ready for data before calling "di_bus_source":

      if (not (dix [dc].bus_cntl & (BUS_ATN | BUS_NRFD)))

    This is tested in the event service routine, so the bus state cannot be
    passed as a parameter.  This could be abstracted to a "di_bus_ready"
    function:

      t_bool di_bus_ready (CARD_ID card);


  - Adding EOI to a data byte before calling "di_bus_source":

      di [dc].bus_cntl |= BUS_EOI;

    New bus signal assertions (either 0, though maybe BUS_NONE would be better,
    or BUS_EOI could be passed as a new parameter to "di_bus_source":

      t_bool di_bus_source (CARD_ID card, uint8 data, uint8 signals);


  - Setting or clearing the unit's bit in the card "acceptors" field as a result
    of an ATTACH or DETACH call:

      di [dc].acceptors |= (1 << unit);
      di [dc].acceptors &= ~(1 << unit);

    The "acceptors" field is maintained entirely by the device module; it is
    only used by the interface.  "acceptors" is redefined to indicate which
    "**_bus_accept" routines to call, depending on the type of devices attached
    to the bus.  For the DI, only one type is allowed, so the acceptor for that
    type is hard_coded.  For a future GIC, multiple types are allowed, and the
    acceptors indicated by the corresponding bits in the "acceptors" field would
    determine which acceptors are called.  Would want to abstract this to a
    "gic_set_acceptor" call, rather than manipulating a GIC state field
    directly.


  - Accepting a bus command or data:

      if (dix [dc].bus_cntl & BUS_ATN)

    Bus signal state should be passed as a parameter to "*_bus_accept":

      t_bool **_bus_accept (uint32 address, uint8 data, uint8 signals);


  - Setting or clearing the unit's bit in the card's "listeners" or "talkers"
    field as a result of decoding a listen command:

      di [dc].listeners |= (1 << unit);
      di [dc].talker &= ~(1 << unit);

    ...or an unlisten command:

      di [dc].listeners = 0;

    ...or a talk command:

      di [dc].talker = (1 << unit);
      di [dc].listeners &= ~(1 << unit);

    ...or an untalk or other talk command:

      di [dc].talker &= ~(1 << unit);

    The "talker" and "listeners" fields are maintained entirely by the device
    module; they are only cleared by the interface during a reset.  "listeners"
    is also used by the interface to limit the data transfer bus acceptor calls
    to those acceptors currently listening.  These should instead be part of the
    device acceptor's state and manipulated there.


  - Accepting a listen secondary or a talk secondary:

      if (di [dc].listeners & (1 << unit))
      if (di [dc].talker & (1 << unit))

    The bus decode routine should check the associated device to see if its
    local state indicates talker or listener addressing.


  - Checking for no talkers or listeners to process an Identify command:

      if (di [dc].talker == 0 && di [dc].listeners == 0)


  - Checking for the last byte of a transfer:

      if (di [dc].bus_cntl & BUS_EOI)

    The bus decode routine should be passed the ATN and EOI signal state.


  - Performing an Interface Clear:

      di [dc].listeners = 0;
      di [dc].talker = 0;

    Interface clear should be handled by the bus acceptors.

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

Revise di.c to eliminate "di_poll_response" and incorporate it into a
"di_bus_control" call.

Currently, we call "di_poll_response" each time a device's PPR is enabled or
disabled.  When a parallel poll is not being conducted, these calls are
irrelevant and represent overhead.

An alternate solution would be:

  - The channel adds two fields to its state: "poll_response" and "poll_active".

  - The "xx_bus_respond" function result is redefined from void to BUS_DATA.

  - The "xx_bus_respond" routine sets "poll_active" true if ATN | EOI and false
    otherwise.  The routine returns the "poll_response" value (it will be
    ignored if a poll is not in progress).

  - A device sets its address bit in "poll_response" to enable PPR and clears
    its bit to disable PPR.  If PPR is being enabled and "poll_active" is true,
    the "di_bus_control" routine is called with no signals asserted or denied.
    This causes another parallel poll (because "poll_active" implies ATN | EOI).

  - For parallel polls, "di_bus_control" calls "xx_bus_respond" and passes the
    response to "di_bus_poll".

  - "di_poll_response" is removed.


--------------
Amigo Identify
--------------

The Identify sequence is not uniformly defined.  The CS/80 manual and the
September 1983 SS/80 manual say that it is:

   ATN  UNT     Untalk
   ATN  MSA     My secondary address
        DAB     ID data byte #1
   EOI  DAB     ID data byte #2
   ATN  OTA     Talk 30

...while the November 1985 SS/80 manual says it is:

   ATN  UNL     Unlisten
   ATN  OLA     Listen 30
   ATN  UNT     Untalk
   ATN  MSA     My secondary address
        DAB     ID data byte #1
   EOI  DAB     ID data byte #2
   ATN  OTA     Talk 30

...and the 7970 HP-IB interface manual says:

   ATN  UNL/IFC Unlisten or Interface Clear
   ATN  UNT     Untalk
   ATN  MSA     My secondary address
        DAB     ID data byte #1
   EOI  DAB     ID data byte #2

...and finally the Foxtrot manual says:

   ATN  UNL/IFC Unlisten or Interface Clear
   ATN  OLA     Listen 30
   ATN  UNT     Untalk
   ATN  MSA     My secondary address
        DAB     ID data byte #1
   EOI  DAB     ID data byte #2
   ATN  OTA     Talk 30

The Foxtrot manual says, "The ABI chip on tape drive's interface board handles
the Amigo Identify function entirely transparent to the drive's controller," so
presumably it requires the above sequence.  On the other hand, the SS/80 manual
says, "SS/80 devices have two addresses, a major address defined by the HP-IB
switch and a minor address which is always 31.  [...]  Thus when the host sends
Untalk, all the IEEE 488 devices stop talking and all of the HP discs get ready
to talk.  They first must receive the secondary that has their major address in
it."

The service manual for the 9134 disc drive, which uses the Intel 8291A GPIB
chip, says, "Note the non-standard sequence using untalk followed by a
secondary."  This is the key.  It is the IEEE-488 TE (extended talker) function,
which requires that the secondary address immediately follow the primary talk
address.  Any other primary command (universal, listen, or talk) prevents the
device from being addressed and therefore responding to the Identify.

This sequential requirement prevents problems with listen secondaries that also
could be interpreted as MSAs, e.g., 0 (Amigo Write Execution), 1 (Amigo tape
command), 5 (CS/80 command secondary), etc.  Normally, if bus protocol were
followed, this would not matter, because some other device or controller would
be addressed to talk.  But the RTE drivers for ICD and CS/80 discs do not
address the controller to talk. So a bus device will see a sequence such as:

   ATN  UNL     Unlisten
   ATN  UNT     Untalk
   ATN  OLA     Listen 0
   ATN  OSA     Secondary 05
   EOI  DAB     Set Unit 0
   ATN  UNL     Unlisten
   ATN  UNT     Untalk

If the Listen were absent, the Untalk + Secondary sequence would be recognized
as an Identify.  But the intervening Listen interrupts the TE sequence
requirement, preventing the problem.

It is interesting to note that the HP 3000 follows the later SS/80 description:

   ATN  OLA     Listen 30
   ATN  UNT     Untalk
   ATN  MSA     My secondary address
        DAB     ID data byte #1
   EOI  DAB     ID data byte #2
   ATN  OTA     Talk 30

...although the Listen 30 and Talk 30 are not relevant to TE addressing.  The
SS/80 manual says, "The peripheral will continue to send these two bytes as long
as the host asks for them," so the Talk 30 is used to allow the device
controller to exit the Identify function.

The ICD "XIDEN" subroutine also follows that sequence, except it omits the Talk
30 at the end.  The subroutine and the ICD driver does:

  CLC sc,C

    >>DA iobus: Received data 000000 with signals CLC | CLF | SIR
    >>DA  csrw: Master reset
    >>DA   buf: FIFO cleared
    >>DA iobus: Returned data 000000 with signals (none)

  LDA =B4003
  OTA sc

    >>DA iobus: Received data 004003 with signals IOO
    >>DA  csrw: Control is LBO | Talk | CIC
    >>DA iobus: Returned data 000000 with signals (none)

  STC sc

    >>DA iobus: Received data 000000 with signals STC | SIR
    >>DA iobus: Returned data 000000 with signals SRQ

  LDA =B476
  OTA sc,C

    >>DA iobus: Received data 000476 with signals IOO | CLF | SIR
    >>DA  inco: 3EH | ATN |     |      |     | Listen address
    >>DA  xfer: No bus acceptors
    >>DA iobus: Returned data 000000 with signals SRQ

  LDA =B737
  OTA sc,C

    >>DA iobus: Received data 000737 with signals IOO | CLF | SIR
    >>DA  inco: DFH | ATN |     |      |     | Talk address
    >>DA  xfer: Untalk
    >>DA iobus: Returned data 000000 with signals SRQ

  LDA =B100740+address
  OTA sc,C

    >>DA iobus: Received data 100741 with signals IOO | CLF | SIR
    >>DA  csrw: Data last byte out | ATN | 341 sent to bus
    >>DA  inco: E1H | ATN |     |      |     | Secondary command
    >>DA  inco: Unit 1 Amigo identify command initiated
    >>DA iobus: Returned data 000000 with signals SRQ

  SFS sc
  JMP *-1

    >>DA iobus: Received data 000000 with signals SFS
    >>DA iobus: Returned data 000000 with signals SKF

  CLC sc,C

    >>DA iobus: Received data 000000 with signals CLC | CLF | SIR
    >>DA  csrw: Master reset
    >>DA   buf: FIFO cleared
    >>DA iobus: Returned data 000000 with signals (none)

  LDA =B10413
  OTA sc

    >>DA iobus: Received data 010413 with signals IOO
    >>DA  csrw: Control is IRL | ATN | Packed | Talk | CIC
    >>DA iobus: Returned data 000000 with signals (none)

  CLC sc,C

    >>DA iobus: Received data 000000 with signals CLC | CLF | SIR
    >>DA  csrw: Master reset
    >>DA   buf: FIFO cleared
    >>DA iobus: Returned data 000000 with signals (none)

  LDA =B10015
  OTA sc

    >>DA iobus: Received data 010015 with signals IOO
    >>DA  csrw: Control is IRL | Packed | Listen | CIC
    >>DA  xfer: HP-IB card 0 changed ATN bus is now (none)
    >>DA  inco: 00H |     |     |      |     | Data
    >>DA iobus: Returned data 000000 with signals (none)

  STC sc

    >>DA iobus: Received data 000000 with signals STC | SIR
    >>DA iobus: Returned data 000000 with signals (none)

    >>DA  inco: 03H |     | EOI |      |     | Data
    >>DA   buf: Data 000003 tag EOI unloaded from FIFO [0] count 0
    >>DA  xfer: Device 1 parallel poll response enabled
    >>DA  inco: Unit 1 Amigo identify command completed

  SFS sc
  JMP *-1

    >>DA iobus: Received data 000000 with signals SFS
    >>DA iobus: Returned data 000000 with signals SKF

  LIA sc

    >>DA iobus: Received data 000000 with signals IOI
    >>DA  csrw: Data 000003 received from bus
    >>DA iobus: Returned data 000003 with signals (none)

  CLF sc

    >>DA iobus: Received data 000000 with signals CLF | SIR
    >>DA iobus: Returned data 000000 with signals (none)



-----------------------------------
Dual-Channel Port Controller (DCPC)
-----------------------------------

In hardware, DCPC will assert I/O signals for the following periods:

        ------------ Input ------------   ----------- Output ------------
  Sig    Normal Cycle      Last Cycle      Normal Cycle      Last Cycle
  ===   ==============   ==============   ==============   ==============
  IOI   T2-T3            T2-T3
  IOO                                        T3-T4            T3-T4
  STC *    T3                                T3               T3
  CLC *                     T3-T4                             T3-T4
  CLF      T3                                T3               T3
  EDT                          T4                                T4

   * if enabled by control word 1

This allows on-card hardware detection of coincident signals.  In particular,
IOO + CLF, STC + CLF, CLC + CLF, STC + CLC, and IOO + EDT.

Under version 3.8-1 simulation, the "dma_cycle" routine will call the device's
I/O signal handler multiple times with these signals in the indicated order:

  Normal input cycle:
    ioIOI
    ioCLF ( + ioSTC if enabled)

  Last input cycle:
    ioIOI
   (ioCLC if enabled)
    ioEDT

  Normal output cycle:
    ioIOO
    ioCLF ( + ioSTC if enabled)

  Last output cycle:
    ioIOO
   (ioCLF if ioSTC and ioCLC are disabled)
   (ioCLF + ioSTC if enabled)
   (ioCLF + ioCLC if enabled)
    ioEDT

A problem arises, however, in that certain I/O cards look for coincident signals
during DCPC transfers to indicate specific conditions:

  Card        Condition   Action
  ----------  ---------   -----------------------------------------------
  12566B LPS  STC + CLC   Inhibit setting of device command
  12821A DI   CLC + CLF   Master reset
  12821A DI   IOO + CLF   Inhibit setting of end-of-data-transfer flag
  12821A DI   IOO + EDT   Set last-byte-out flag
  12875A IPL  IOO + EDT   Delay DMA completion interrupt for TSB

For example, the 12566B Microcircuit Interface sends a device command pulse in
response to asserting STC and CLC sequentially but no pulse when the signals are
asserted concurrently.  With the current DCPC implementation and I/O model,
these actions will fail.

In addition to the above, the following devices respond to EDT:

  Card        Action
  ----------  -----------------------------------------------
  13175D DS   Set end-of-data flag
  12792C MPX  (debug message only)
  13183B MS   Clear data channel flag buffer and flag

To allow simulations of cards that act upon concurrent signals, the I/O signal
simulation must be revised to support them, and the DCPC simulation must be
revised to dispatch them.



----------------------------------------------
Remodeling the Remodeled HP 1000 I/O Structure
----------------------------------------------

For 3.8-1, the HP I/O structure was changed from an instruction-based model to a
signal-based model.  I/O backplane signals were dispatched to handlers in each
peripheral that took actions appropriate for the simulated device.  This allowed
non-standard behavior (such as the 12936A PIF) to be accommodated cleanly.
However, when implementing the 12821A Disc Interface, problems occurred with
this new model.

The issue was that the DI had actions that were based not only on individual
signals but also on several concurrent signals.  The existing model allowed only
for the CLF signal to be concurrent with IOI, IOO, STC, or CLC.  The DI acted
upon concurrence of CLC and CLF, IOO and CLF, and IOO and EDT.  During a DMA
transfer, all but the first pair were dispatched to the device sequentually,
resulting in failure of the simulation.

Initial consideration was given to remodeling the DMA cycle to output IOO and
CLF, but then the issue arose as to whether to send CLF several times during a
cycle or only once.  In hardware, CLF is asserted at T3, concurrently with IOO,
STC, and CLC.  Any of these three handlers may need to detect that concurrence,
and problems may arise if CLF takes an action that results in errors if it is
repeated during a single cycle.  Resolving this in any way other than sending
CLF three times and then depending on the individual handlers to detect this and
ignore multiple assertions would make the DMA cycle logic dependent on
particular peripheral simulations.  This is undesirable, as this is the reason
that the I/O simulation was remodeled originally.

Therefore, a revised model is implemented that redefines the signal parameter
from a single value to a set.  Each I/O cycle will make exactly one call on the
peripheral's signal handler, and several signals may be asserted concurrently.
In particular, a DMA cycle may assert up to five signals (e.g., IOO, CLF, STC,
CLC, and EDT), although one or two will be the usual case for programmed I/O.
In this way, each signal's code will be called exactly once for each cycle, and
signals are still processed sequentially within the handler in a defined order,
but each signal's code will be able to determine whether other signals are
asserted concurrently.

This does necessitate a change to all signal handlers again, but the result
should be a universal signal model that will accommodate all future peripheral
simulations without further change.

--> IOPRESET and IOPOWERON take handler and dibptr names.
    But handler is dibptr->io_handler.  Just pass dibptr.

--> change "data" to "stat_data"

--> use IODATA macro instead of "data & DMASK"

--> Add ioPON to complete set, rather than rely on SWMASK('P')?  Now is the time!
    Would still need that test, except that it should dispatch ioPON, ioPOPIO,
    and ioCRS.  IOPOWERON macro.

  CPU (1000)
    PON:    issue POPIO and CRS


  > Each handler is required to use IOADDSIR at the top to ensure that ioSIR is
    added to the passed set of signals if a signal affecting interrupts is
    included.  This effectively does:

      if (signals & IOIRQSET)
          signals = signals | ioSIR;

    ...incurring a small overhead for each call.  However, the signals that
    require ioSIR are known at call time, so this check shouldn't be needed.

    Option 1 would be to define the signals so that SIR is included where
    needed.  For instance:

      ioIOO = 0000020,
      ioSTC = 0000040 | ioSIR,
      ...

    The problem is that the signal handler "switch" statement can't use the
    ioNNN values because it dispatches the signals one at a time (so STC and SIR
    are dispatched separately).  This would necessitate defining two sets of
    signals:

      swIOO = 0000020,
      swSTC = 0000040,
      ...
      swSIR = 0100000

    and:

      ioIOO = swIOO,
      ioSTC = swSTC | swSIR,
      ...

    Now the callers would use the ioNNN values, and the "switch" statements
    would use the swNNN values.  The downsides are that every "switch" statement
    would have to be changed, and it would be all too easy to pass the wrong
    kind of value (e.g., calling with swSTC instead of ioSTC), as there's no
    type checking to prevent this (all enums are ints in C).

    Option 2 would be to retain the current definitions and move the IOADDSIR
    logic to "devdisp" in the CPU simulator.  The overhead would still be
    present, but the device signal handlers would be simplified.  The downside
    is that direct calls to the signal handlers would not work, e.g., calling
    one's own handler would be an error; the call would have to be to "devdisp"
    instead.



--------------------------------------
I/O Signal Handlers for Multiple Cards
--------------------------------------

SIMH peripheral simulators are usually coded to handle a single instance of an
I/O card or card set.  There is no inherent provision for multiple copies of a
given card, although cards are generally configurable for the maximum number of
similar connected devices allowed by the hardware.  For example, a disc
controller card simulation may allow connection of up to eight disc drives, but
a second controller card simulation with its complement of drives is not
supported, except by duplicating the simulation code and assigning different
device and function names.

In general, then, there is a one-to-one correspondence between a card (i.e.,
select code) and a DEVICE structure.  A good example is the common two-card disc
interface.  This simulation defines separate DEVICEs (e.g., DPD and DPC) for the
command and data cards, as well as separate I/O signal handlers.  This is
proper, as the cards are different and respond differently to I/O signals.

In a few cases, though, a peripheral may use two cards with identical or nearly
identical behaviors.  In this case, duplicating the I/O signal handler functions
is unnecessary, more difficult to maintain, and may result in significant code
bloat if the card operation is complex.

Current examples of this are the DMA device and the IPL device.  DMA consists of
two channels that are identical except for priority.  The IPL device consists of
two identical 12566B Microcircuit Interface cards -- one used as an input
device, and the other used as an output device.  In each case, a common signal
handler (or handlers, in the case of DMA that has primary and secondary select
codes) is employed, and card state, such as control and flag flip-flop values,
is kept in arrays indexed by a zero-based card number.  For DMA, the channel
number is derived from the last bit of the select code (6 => 0, 7 => 1).  For
IPL, the card number is derived from an explicit select code comparison (input
select code => 0, output select code => 1).  In addition, the IPL signal handler
needs the device pointer associated with the card to access the debug flags and
the unit pointer associated with the device.  Similarly, a common unit service
routine is used for both cards, which needs the card number and the debug flags
(via the device pointer) for the indicated card.

A simulation of the 12821A Disc Interface is planned that will support three
configurations: an interface with Amigo discs, an interface with CS/80 discs,
and and interface with Amigo tapes.  Each configuration will consist of a device
with multiple units.  Because the card is complex, a single signal handler for
all three interfaces is desirable.  (Fortunately, the unit handlers will be
specific to the devices, so there will be separate handlers for CS/80 disc,
Amigo disc, and Amigo tape units.)

To accommodate this, a slightly revised peripheral model is needed.  The current
one-to-one mapping of cards to DEVICEs to signal handlers is modified to provide
a many-to-one map of cards to a common signal handler.  The one-to-one map of
cards to DEVICEs remains.

To handle multiple cards, signal handlers must have additional information
beyond the currently supplied select code, I/O signals, and output data
parameters.  Specifically, a handler needs:

 - the device select code to set the correct bit in the PRL, IRQ, and SRQ arrays
 - the signal set to process
 - the data to output for an IOO signal
 - the card number to access the state variables for the referenced card
 - the device pointer to access the device name, flags, debug flags, and units
   for debug printouts and sim_activate() calls for the referenced device

With three or more devices, determining card number by testing select codes does
not scale well.  Card number is a property of the I/O card and is associated
with a given select code.  This suggests that the card number should be added to
the DIB, which currently contains the select code and the signal handler
function pointer.  The DIBs for single-card handlers would contain a card number
of 0, whereas the DIBs for multiple-card handlers would contain sequential card
numbers starting at 0.  State variables for the former would be scalars, whereas
state variables for the latter would be arrays indexed by card number.

Concerning implementation, option 1 would be to pass the card number and device
pointer as additional parameters, but there will be a lot of overhead, as most
peripheral simulations are for single cards where these extra parameters will
not be used.

Option 2 would be to keep the same three parameters and access the card number
and device pointer via a global array that is indexed by select code.  At first
glance, dptr[] is a likely candidate, if the array elements are expanded to
include the above information.  This fails, however, when signal handlers are
called from the device reset function as part of the PRESET action, as dptr[] is
not set up unless the CPU is executing instructions.  A workaround of
initializing the target dptr[] element in the device reset routine is
undesirable, as it ties each peripheral simulation to the CPU's dptr structure.

Option 3 would be to pass the device pointer as an additional parameter, as the
card number in the DIB may be accessed via the corresponding DEVICE's context
pointer.  This would allow the device reset routines to pass appropriate
pointers during PRESET handling.  However, some of the fixed devices, such as
the front panel and the DMA secondary addresses, do not have DEVICE structures,
so dummy DEVICEs would have to be added for each of these, just to provide the
DIB pointers needed to access the select codes.

Option 4 would be to replace the first select code parameter with an IOSTATE
structure containing a DIB pointer and a DEVICE pointer.  This structure would
be trivial to pass if dtab[] is changed from an array of handler function
pointers to an array of IOSTATE structures.  However, multiple card simulations
need the device pointer for debug printouts, and some of these are performed
outside of the signal handler, necessitating the definition of a device pointer
array indexed by card number.  Given that only multiple card devices need a
device pointer lookup, and that they will need to define their own lookup array
anyway, including the pointer in the IOSTATE structure is redundant and
introduces unnecessary overhead for the bulk of the devices.

The selected option is to replace the select code parameter with the device's
DIB pointer and to change dtab[] from an array of I/O signal handler pointers to
an array of DIB pointers.  This allows direct access to the card number, which
may then be used to index into a simulator-defined array of device pointers for
debug printouts, etc.  This change does require that all devices have DIBs,
though, so DIBs must be added for the CPU devices (CPU, DMA, null device, etc.).

In addition, the standard card flip-flops for single-device simulations are
changed from scalars to structure members for congruency with multiple-device
simulations.  For example, "sng_control" and "sng_flag" become "sng.control" and
"sng.flag" for congruency with "mult [0].control" and "mult [0].flag".  The
"setstdPRL/IRQ/SRQ" I/O helper macros are changed to accommodate this as well.



-------------------------------------------------
Problems with using "unit disable" as "power off"
-------------------------------------------------

The initial thought was to use the existing SET <unit> OFFLINE mechanism,
suitably renamed to SET <unit> POWEROFF, to indicate the power-off state.  The
intent of disabling units was to indicate that "the unit is not there," in Bob's
words.  That is precisely what occurs when a device cable is pulled, or the
device is powered off.  However, there are some operational issues that make
this less desirable than upon initial inspection.

The current unit-disable requirements are that files are detached and no I/O
operations are in progress.  When a unit is disabled, it is removed from the
device display, and no configuration changes to the unit (SET <unit> <mod>) are
allowed.  The file detach is important, because the device simulators do not
check explicitly for disabled units.  They depend, instead, on catching the
missing file attachment.  If a unit is allowed to be disabled without detaching,
then additional I/O operations may be started (and completed) on supposedly
"missing" units.

The semantics of a real powered-off device are a bit different.  Media remains
in place (e.g., a mag tape reel remains loaded on the drive), and state
modifications are allowed (e.g., a mag tape reel may have its write-enable ring
removed or replaced).  These imply that ATTACH and DETACH should still work, and
that unit display is still required (to see the results of the modifications).

A crucial function is the recovery of a powered-off device that has an I/O
operation initiated.  What should happen is that the operation should "hang" in
the "waiting for device response" state until power is reapplied, whereupon the
operation should complete.  In simulation terms, the SET <unit> POWERON
should reschedule the I/O event, with entry into the service routine allowing it
to complete.  Unfortunately, there is no existing mechanism to allow the device
to trap this condition (as there is with a local modifier, e.g., ONLINE, or an
ATTACH).  That would imply that polling must be done while power is off.

What might be better would be to separate the "is powered off" and "is not
present at all" functions by leaving the unit-disable as it is and adding a new,
local SET <unit> POWEROFF.  By its nature, the powered-off condition only
applies to devices containing at least one unit; there is no way to power off a
device that consists of just the interface card, other than pulling the card
from the card cage -- but this is defined as disabling the device.  Existing
simulations that depend on the file detach to trap the I/O operation would
continue to work, and power-off handling could be added on a local, per-device
basis.  SET POWERON could be trapped via the modifier validation routine (as
would SET ONLINE), and ATTACH would trap via the local attach routine.  Also,
being a local setting, we may impose any set of restrictions we want, without
running afoul of those simulations dependent on a particular unit-disable
behavior.

NOTE:  It may be possible to use SET <unit> DISABLED after all.  The basic
problem is that <dev> is also a synonym for <unit0>, so SET PTR DISABLED could
also mean SET PTR0 DISABLED.  However, if we do not use unit-disable for the
power-off condition, then it will only apply to multiple-unit devices.  In that
case, SET MSC DISABLED (e.g.) would disable the MSC/MSD device, and SET MSC0
DISABLED would disable unit 0.

So maybe we want:

 1. No card => SET <dev> DISABLED.

 2. No peripheral => SET <unit> DISABLED.

 3. No power or cable => SET <unit> POWEROFF.

 4. Device offline => SET <unit> OFFLINE.

 5. Device not ready => DETACH <unit>

 6. Device busy => internal simulation state.

 7. Device idle => internal simulation state.

SET <dev> DISABLED and SET <unit> DISABLED would revert to their 3.2-3
operation.  The local modifiers SET POWEROFF and SET OFFLINE would require that
I/O is quiescent only.  Powered-off and offline units would still display and
would carry the appellations "powered off" and "offline".  Conditions 2-4 would
be tested and handled in the service routine, and traps for all three would
cause a pending I/O operation to resume.



--------------------------------
Disabled, off-line, and all that
--------------------------------

A physical I/O device may be in one of several states.  The state of a given
device may be inferred by the CPU by the response of the device to various
tests.  In simulating devices, SIMH should seek to provide analogs to these
states:

 1. Device does not exist, i.e., no card in the I/O card cage.

 2. Device exists but is powered off or interface cable is disconnected.

 3. Device connected and powered on, but "online" switch is off.

 4. Device is online but not ready, e.g., out of paper.

 5. Device is ready but busy, e.g., print hammers are in motion.

 6. Device is idle.

SIMH currently simulates five of the six states above (although with some
semantic restrictions).

Each of the above states is characterized by certain device responses that may
be tested by a program running under simulation, e.g., a device diagnostic or
operating system.  The physical states listed produce these testable responses:

 1. With no interface card for the device in the I/O card cage, I/O instructions
    directed at that slot react as follows: the I/O backplane reads as -1 (all
    ones) on a 21MX, and 0 (all zeros) on earlier machines, and the LIA and MIA
    instructions behave accordingly; the OTA, CLC, CLF, STC, and STF
    instructions are effective NOPs; the SFC instruction always skips; and the
    SFS instruction never skips.

 2. A disconnected or powered-off device presents an I/O card that "works,"
    i.e., responds to the I/O instructions, but the response to any action that
    involves interaction with the missing device is specific to the device.  For
    example, the particular status bits that are on or off are dependent on the
    electrical characteristics of the card and device.

 3. An "offline" device typically sets a status bit flag.  Additional offline
    interaction, e.g., whether the device accepts commands or ignores them, is
    device-specific.

 4. A "not ready" device also typically sets a status bit flag.  Some not-ready
    conditions are self-correcting, e.g., a device that is powering up.  Others
    require operator intervention, e.g., a printer that is out of paper.

 5. A "busy" device is reflected in the device status, and the device commands
    that are accepted in this state are device-specific.  The busy state is
    exited automatically after the operation completes.

 6. The idle state is often indicated by the absence of other-state indications
    (e.g., the absence of "offline," "not ready," and "busy" status flags).

The set of states attributable to a given physical device may contain one or
many or all of these states.  For example, the time-base generator (CLK device)
possesses only the first state: it's either present, and therefore powered on,
or not; there's no cable, no offline switch, no media to be loaded, and no
commands to execute.  The paper tape punch possesses all states except the
third; the punch is always online. The line printer possesses all of the states.

Also, for some devices, one or more states may be indistinguishable.  For
instance, a magnetic tape may display no difference between having its cable
disconnected and being set offline; in either case, all communication with the
device (including device status) ceases.  In contrast, a printer may remain able
to communicate in the offline state, e.g., removing the paper while the printer
is offline is noted with an "out of paper" status indication.

SIMH version 3.2-3 provides three simulation environment commands to change I/O
device state.  These commands provide for disabling the device, disabling the
unit, and detaching the associated device image file.  Coupled with the "busy"
and "idle" conditions, which are managed by the device simulator, rather than by
the simulation environment, five distinct simulation states are possible.

Devices are disabled with the SET <dev> DISABLED command.  To disable a device,
all units associated with the device must be detached and have no active
operations.  Disabling (or enabling) a device causes it to be reset.  A disabled
device is removed from the I/O card dispatch table, so all I/O instructions are
routed to the null device ("nulio" routine).  A device that may be disabled is
marked with the DEV_DISABLE device flag, and a device that is currently disabled
has the DEV_DIS flag set.

Units are set disabled with the SET <unit> OFFLINE command (a misnomer).  To be
disabled, a unit must be detached and have no active operations.  A disabled
unit will not accept any state changes (e.g., from SET <unit> LOCKED to SET
<unit> WRITEENABLED).  A unit that may be disabled is marked with the
UNIT_DISABLE unit flag, and a unit that is currently disabled has the UNIT_DIS
flag set.

Note: this command is misnamed, because it does not set a unit offline in the
traditional sense, i.e., corresponding to physical state 3, but rather disables
a unit, as in physical state 2.

Attaching and detaching the device image file is done with ATTACH <unit>
<filename> and DETACH <unit>.  A disabled unit cannot be attached.  Simulator
action depends on checking and acting upon the "unit attached" flag.  A unit
that may be attached has the UNIT_ATTABLE unit flag, and a currently attached
unit has the UNIT_ATT flag set.

The six physical states map to the five simulation states as follows:

 1. No card in card cage => SET <dev> DISABLED.

 2. No power or cable => SET <unit> OFFLINE.

 3. Device offline => (no mapping).

 4. Device not ready => DETACH <unit>

 5. Device busy => internal simulation state.

 6. Device idle => internal simulation state.

Disabling a device with SET <dev> DISABLED is equivalent to removing the I/O
card from the card cage (state 1).  The simulated device I/O handler is replaced
by a "null handler" that responds as an empty slot.

Disabling a unit with the SET <unit> OFFLINE command is equivalent to powering
off or removing the interface cable from the unit (state 2).  A disabled unit
behaves identically to a detached one, unless the device simulator tests and
acts upon the UNIT_DIS flag.

DETACH <unit> maps to physical device not ready (state 4).  Attaching a file to
a simulated device is analogous to loading a physical device with media (paper,
magnetic tape, disc cartridge, etc.).

Each device simulation provides internal logic to handle physical states 5 and 6
(busy and idle).  Transitions between these states are usually initiated by
executing a device command with the STC instruction.

To complete the simulation mapping, a new simulation command is needed that
simulates pressing the "offline" button or equivalent on devices so equipped
(state 3).  To bring the command names into conformity with their actual
actions, the current OFFLINE/ONLINE command (state 2) should be renamed to
POWEROFF/POWERON (subject to change), and a new OFFLINE/ONLINE command (state 3)
should be implemented.  The POWEROFF command would still use UNIT_DISABLE and
UNIT_DIS flags, and a new UNIT_OFFLINE flag would be defined for the new OFFLINE
command.

The new OFFLINE/ONLINE commands would be implemented as local modifiers to an
affected device, so the presence or absence of the commands in the modifier
structure would determine if they are valid for a given device, and no analog to
UNIT_DISABLE would be needed.

The revised mapping would appear as follows:

 1. No card => SET <dev> DISABLED.

 2. No power or cable => SET <unit> POWEROFF.

 3. Device offline => SET <unit> OFFLINE.

 4. Device not ready => DETACH <unit>

 5. Device busy => internal simulation state.

 6. Device idle => internal simulation state.

Here are some examples of how this new simulator command mapping would relate to
physical device states:

  7900 disc:

    1. SET DPC DISABLED  => no 13210A interface in card cage.

    2. SET DPC0 POWEROFF => unit 0 cable disconnected or POWER switch off.

    3. SET DPC0 OFFLINE  => unit 0 LOAD/UNLOAD switch in UNLOAD position.

    4. DETACH DPC0       => unit 0 disc cartridge removed.

  7970 tape:

    1. SET MSC DISABLED  => no 13183A interface in card cage.

    2. SET MSC0 POWEROFF => unit 0 cable disconnected or POWER switch off.

    3. SET MSC0 OFFLINE  => unit 0 RESET button pressed, ONLINE light out.

    4. DETACH MSC0       => unit 0 tape reel removed.

  2607 printer:

    1. SET LPT DISABLED => no 12845B interface in card cage.

    2. SET LPT POWEROFF => cable disconnected or POWER switch off.

    3. SET LPT OFFLINE  => PRINT button up, light out.

    4. DETACH LPT       => printer paper removed.

There is also a semantic problem with the current implementation of the renamed
SET <unit> POWEROFF command.  As it stands, disabling a unit is not allowed if
the unit has an I/O operation in progress.  This is reasonable, as disconnecting
the interface cable on a device with an operation in progress surely would cause
problems.

However, there are two additional restrictions that do not exist with
the corresponding physical implementations: an attached image file must be
removed before disabling, and, once disabled, no state modifier commands are
accepted.

On a real device, turning the power off would allow the device media to remain
loaded and positioned.  For example, it is not required to remove the tape reel
before powering the device down, nor does doing so change the tape position.  A
real printer's cable may be disconnected and then reconnected without requiring
removal of its paper.  Under SIMH, however,  disabling and then re-enabling
forces a detach and attach, so media position is reset.

Additionally, a real device may still be manipulated manually when powered off.
For example, media may be changed, e.g., from one paper type to another.  A
magnetic tape's write-enable ring may be removed or installed.  SIMH doesn't
allow ATTACH/DETACH or SET <unit> <modifier> for disabled units.

Were these restrictions accurately modelled, the restriction sets would be:

 * SET <dev> DISABLED  -- DEV_DISABLE flag present, all units DETACHed and
                          quiescent (i.e., no active event timers).

 * SET <unit> POWEROFF -- UNIT_DISABLE flag present, all units quiescent.

 * SET <unit> OFFLINE  -- command modifier table entry present.

Notably, this would allow powered-off units to remain ATTACHed and would allow
in-process I/O requests to complete on a newly-OFFLINE device (device simulators
would check the UNIT_OFFLINE flag before starting a new I/O operation).  The
latter is how a real printer behaves: setting the printer offline while a line
is printing completes the print operation (hammer strikes, paper advance) before
going offline.


What happens when an OFFLINE, POWEROFF, or unattached unit receives a programmed
STC to start an I/O operation?  On a real device, CTL and CMD would set, "device
command" to the device would assert, and then the interface would wait for
"device flag" to assert.  That wouldn't happen until the device was set online,
power was restored, or media was restored.  At that point, the device would
notice "device command" and would respond with "device flag," setting FBF and
then FLG, and generating an interrupt.  Notably, the STC command would
be accepted, i.e., CTL and CMD would set, regardless of the power or online
status.

In the simulation model, the "device command" to "device flag" wait is simulated
with an I/O event timer.  The simulation needs to "hang" after "device command"
is asserted and resume after SET <unit> POWERON, SET <unit> ONLINE, or ATTACH
<unit> command is executed.  This may be accomplished either by not issuing the
"sim_activate" in the "devio" routine, or by not setting the device flag in the
"dev_svc" routine.  When any of the foregoing commands are detected, and an
operation is in progress (indicated by the CMD flip-flop being set), an
immediate "sim_activate" would be scheduled to complete the I/O call.  This
would set the FBF and FLG, completing the pending operation.

Assuming that the "sim_activate" call is made, the "dev_svc" routine should
check for non-attachment, offline, or power-off conditions.  If any of these
exist, FBF and FLG should not be set.  Instead, if STOP_IOE is set, then
SCPE_UNATT, STOP_OFFLINE, or STOP_POWEROFF should be returned.  If STOP_IOE is
not set, then "dev_svc" should just return SCPE_OK.

When the condition clears, CMD is checked, and if set, "sim_activate" should be
called to reschedule the operation.  This time, presuming all is OK, "dev_svc"
will set FBF and FLG, clear CMD, and complete the operation.

--> while ONLINE and ATTACH have user traps, POWERON doesn't!


In summary, a more accurate physical device model would be produced by:

 1. Renaming the existing SET <unit> OFFLINE/ONLINE command to SET <unit>
    POWEROFF/POWERON.

 2. Altering the restrictions on the SET <unit> POWEROFF command to allow
    an existing image file attachment to remain.

 3. Adding local SET <unit> OFFLINE/ONLINE commands and UNIT_OFFLINE flag
    definitions to affected devices.

 4. Modifying the HP device simulators to respond appropriately to the UNIT_DIS,
    UNIT_OFFLINE, and UNIT_ATT flags.



-------------------------------------
Loader, loader, who's got the loader?
-------------------------------------

Two very different bootstrap loader mechanisms are employed by the 21xx-series
(2114/15/16 and 2100) computers and the 1000-series computers.  These divergent
loader schemes are only partially supported by SIMH.

The 21xx systems use a 64-word area of memory at the top of existing memory to
hold the bootstrap loader.  This area is normally inaccesable (writes are
ignored, and reads return zero, just as if the memory didn't exist) but may be
enabled by the LOADER ENABLE front-panel button.  Bootstrapping requires
manually setting the P register to the appropriate loader-dependent starting
address, pressing the LOADER ENABLE button, and running the program.  Loader
protection is automatically restored when a HALT instruction is executed, when
the user manually halts the machine, or when the user toggles the LOADER ENABLE
button.

Nearly a dozen 2100 loader programs are defined, although only one loader may be
resident at a time.  To aid in switching among loaders, a "loader loader"
program (HP 24353-16001) is available to write a configured loader into the
reserved memory area.  Given the core-memory nature of 2100 systems, the loader
would be retained indefinitely until overwritten.

1000 systems have a selection of up to four boot loader ROMs that may be copied
into memory via a front-panel button.  The selection of ROMs installed varies
from machine to machine.  Pressing the front-panel IBL button loads the selected
ROM contents into the last 64 words of memory and sets the P register to the
the first word of the 64-word area.

SIMH provides device-specific BOOT commands that essentially provide the 1000
ROM mechanism for all CPU simulations.  While convenient and practical, this
presents two problems: those familiar with the 2100 boot loaders will not find
the mechanism simulated, and some 2100 diagnostics try to be helpful by testing
for the LOADER ENABLE button and refusing to continue until the button is turned
off.  Unfortunately, there is no way to do so in the current implementation.

I propose adding a mechanism to specify:

  * for the 2100, the type of loader that is resident in core
  * for the 2100, a LOADER ENABLE/DISABLE setting
  * for the 1000, the types of loaders in each of the four ROM sockets

For the 2100, loader types (using HP mnemonics) would be:

  * BBL      ("Basic Binary Loader" -- paper tape)
  * BBDL     ("Basic Binary Disc Loader" -- 2770 fixed disc)
  * BMDL7900 ("Basic Moving-head Disc Loader -- 7900 disc)
  * BMDL2883 ("Basic Moving-head Disc Loader -- 2883 disc)
  * BMDL7905 ("Basic Moving-head Disc Loader -- 7905 disc)
  * AMTL     ("Absolute Magnetic Tape Loader" -- 7970 mag tape)
  * AIOPL    ("Access IOP Loader" -- interprocessor link)

For the 1000, loader ROMs (using HP part numbers) would be:

  * 12992A -- 7900/7901/2883 disc
  * 12992B -- 7905/7906/7920/7925 disc (RPL)
  * 12992C -- 2644/2645/2648 mini-cartridge tape
  * 12992D -- 7970 magnetic tape
  * 12992E -- 9885 flexible disc (RPL)
  * 12992F -- 7900/7901 disc (RPL)
  * 12992H -- 7906H/7920H/7925H/9895 disc (RPL)
  * 12992J -- 7908/7911/7912/7914/7933/7935 CS-80 disc (RPL)
  * 12992K -- paper tape
  * 12992L -- 7974 magnetic tape

The 1000-M came with a K ROM soldered into position 0.  The 1000-E/F came with a
C ROM in socket 0 and a B ROM in socket 2.

The existing BOOT command would not be affected by this mechanism.

The mechanism could be exposed either as additional modifiers to the CPU device,
or a new pseudo-device could be created.  The boot loaders really are CPU
functions, but implementing them would add quite a few new modifiers to the two
dozen that currently exist.  Here are the two possibilities (the first set would
be valid for the 2100, and the second set for the 1000):

      As part of CPU         As a pseudo-device
  =======================    ===================
  SET CPU LOADERENABLE       SET LOADER ENABLE
  SET CPU LOADERDISABLE      SET LOADER DISABLE
  SET CPU LOADER=BBL         SET LOADER BBL
  SET CPU LOADER=BBDL        SET LOADER BBDL
  SET CPU LOADER=BMDL7900    SET LOADER BMDL7900
  SET CPU LOADER=BMDL2883    SET LOADER BMDL2883
  SET CPU LOADER=BMDL7905    SET LOADER BMDL7905
  SET CPU LOADER=AMTL        SET LOADER AMTL
  SET CPU LOADER=AIOPL       SET LOADER AIOPL

      As part of CPU         As a pseudo-device
  =======================    ===================
  SET CPU LOADERROM0=x       SET LOADER ROM0=x
  SET CPU LOADERROM1=x       SET LOADER ROM1=x
  SET CPU LOADERROM2=x       SET LOADER ROM2=x
  SET CPU LOADERROM3=x       SET LOADER ROM3=x

(where "x" is one of "B", "D", "F", or "K", with the potential for future
expansion).

For the 2100, SET CPU LOADERDISABLE would set the LWA of memory to (MEMSIZE - 1)
& 077700.  SET CPU LOADERENABLE would set LWA to (MEMSIZE - 1).  Accesses via
"ReadW" would return 0 and accesses via "WriteW" would do nothing if the address
requested was greater than LWA.  Doing a SET CPU LOADER=xxx would copy the
specified loader program into the upper 64 locations of memory.  Initially, the
BBL loader would be selected and copied into memory.

For the 1000, specifying the loader ROM in each socket replaces the hard-coded
assignments now used by the "cpu_boot" function.  LWA is always set to (MEMSIZE
- 1).

When accessing non-existent memory in HP 21xx/1000 systems, reads return 0, and
writes are ignored.  Currently, SIMH models this by zeroing all of simulated
memory beyond the LWA and preventing writes beyond the LWA. Therefore, the time
penalty for the check occurs on writes only.

Note that attempting to add a simple "if (va > LWA) return 0" check to "ReadW"
incurs an 18% increase in execution time for the 2100 core memory diagnostic,
even though the check adds just a "cmpl" and a "ja" instruction to the more than
70 instructions executed for each simulated 21xx instruction.  This must be some
sort of cache miss phenomenon.

Therefore, an implementation that does not suffer that penalty is to change the
existing "MEM_ADDR_OK" macro from a comparison to "MEMSIZE" (simulated memory
size) to a comparison to LWA, where the latter is the last word of addressable
memory plus one.  We also declare a 64-word array to serve as the shadow memory
for the loader area.

Now, disabling the loader copies the last 64 words of memory (i.e., MEMSIZE-64)
to the shadow array and sets LWA to MEMSIZE-64.  Enabling the loader copies the
shadow array to the last 64 words of memory and sets LWA to MEMSIZE.  Changing
the memory size changes LWA accordingly (if LWA & 07777 == 0, LWA = MEMSIZE,
else LWA = MEMSIZE-64).  Changing the CPU to a 21xx sets LWA to MEMSIZE-64
(loader is disabled).  Changing the CPU to a 1000 sets LWA to MEMSIZE (loader is
enabled).



----------
CPU Idling
----------

To support idling, the following device polling service routines must be
rewritten to poll synchronously:

 - TTY (console)
 - BACI
 - MUX (12920)
 - MPX (12792)

Each of these devices should poll at the same rate as the RTE clock, i.e., 10
milliseconds.  As the TTY device is always enabled, it should be the source of
calibrated time.  The TBG (CLK) should synchronize with the TTY poll when it is
set for a 10 millisecond period.  When set for other rates, it should calibrate
independently.

One slight problem is that STC TBG should give full first time period, as card
counters are cleared before STC.  Can't do this if we sync with TTY poll, and if
we reset TTY poll to match TBG, other units synched with TTY (e.g., BACI) will
become unsynched.  However, first time period will be short anyway, because we
don't know "real" count to use until a few calibration cycles.  So, in practice,
this occurs already and shouldn't be a problem (in diagnostic mode, the clock is
decoupled from real time, and each period is the same length, including the
first).

The TTY should export its current service time.  Other devices should schedule
their services initially on the remaining time in the TTY poll (via
sim_is_active).  That initially locks each device to the TTY so that its service
is called immediately after the TTY service.  When each is called, it
reschedules based on the exported TTY time.  This keeps the service routines
synchronized with the TTY poll.

Idling occurs only when the unit at head of event queue has the UNIT_IDLE flag.
However, UNIT_IDLE is needed only on the TTY and CLK (if not set for 10
milliseconds).  Other polled devices (BACI, etc.) always appear in the event
queue immediately after the TTY and so have no effect on the idle decision.



-----------------------------------
Modelling the HP 1000 I/O Structure
-----------------------------------

The HP 21xx/1000 computers implement a bidirectional I/O backplane with a 16-bit
data path.  Signals exchanged between the CPU, MP, DCPC, and I/O cards control
data transfer timing and interrupt request generation.  Execution of CPU I/O
Group instructions connect the backplane data bus to the CPU's internal S-bus
and generate the necessary control signals to operate the addressed I/O card.

I/O interfaces typically -- but not necessarily -- employ control, flag buffer,
flag, and interrupt request flip-flops.  A device transfer is started by setting
the control flip-flop.  The device signals completion by setting the flag buffer
flip-flop, which in turn sets the flag flip-flop.

The backplane signals involved in interrupt generation are PRH (priority from
higher-priority interfaces) and PRL (priority to lower-priority interfaces), IRQ
(interrupt request) and FLG (flag), and IAK (interrupt acknowledge).  If the
control, flag buffer, and flag flip-flops are set, and no higher-priority device
is currently interrupting, then the interrupt request flip-flop is set to
interrupt the CPU, and priority to lower-priority cards is denied.  The card
responds to the CPU interrupt acknowledgement by clearing the flag buffer
flip-flop, removing the interrupt request.

Currently, SIMH models the interface control, flag buffer, and flag flip-flops
explicitly, maintaining a global bit vector for each flip-flop type.  However,
the PRH/PRL, IRQ, and IAK signals are implicitly modelled -- PRL is the logical
AND of the control and flag flip-flops, IRQ is the logical AND of the control,
flag buffer, and flag flip-flops, and IAK clears the flag buffer.

The problem is that while most cards model these signals as indicated, there are
exceptions.  Notably, the 12892A/B Memory Protect card clears the flag and the
flag buffer on IAK, and the 12936A Privileged Interrupt Fence generates PRL as
the logical OR of the control and flag.  These exceptions must be special-cased
to work around the implicit assumptions.

In addition, SIMH bases I/O dispatch on the I/O instructions, rather than the
I/O signals.  This means that signals that affect interface cards but have no
equivalent instructions (e.g., power on preset, control reset) must be
special-cased as well in the dispatcher to provide "pseudo-instructions" for
these events.

There are some other suboptimal characteristics of the current design:

 - The STF/CLF and STC/CLC signals are dispatched together as ioFLG and ioCTL,
   necessitating additional decoding in each interface's I/O handler.

 - The IOI signal is split into ioLIX and ioMIX instructions, necessitating
   redundant handling in the interface.

 - The control, flag buffer, and flag states must be moved from the DIBs to the
   bit vectors when execution is started, and from the vectors to the DIBs when
   execution stops.

 - The CRS signal goes to the I/O handler, while POPIO goes to the device reset
   routine.  As POPIO and CRS are issued together, the CRS handler must be
   separate and then called from the I/O handler and reset routine.  However,
   the flip-flop states are in the bit vectors when the I/O handler is called
   and in the DIB when the reset routine is called.

 - A command flip-flop is modelled in all devices, even though it is used in
   very few interfaces.  Several of the current simulations model some other
   flip-flop as "command" to reuse this definition.


To allow simulation of interfaces with non-standard generation of interrupt
requests without resorting to special-case code, the I/O model is restructured
as follows:

 1. Interfaces are sent backplane signals instead of I/O instructions.

 2. Interrupt handling uses explicit PRL, IRQ, and IAK signals, rather than
    implicitly deriving these from control flag buffer, and flag interactions.

 3. CPU bit vectors are maintained for PRL, IRQ, and SRQ.

 4. Interfaces generate PRL, IRQ, and SRQ from their internal logic.

 5. Control, flag buffer, and flag flip-flops become internal states in the
    interface simulations.  Additional flip-flops (e.g., command) are just
    additional internal states.

 6. When simulation starts, bit vectors are set by calling each enabled device
    with the SIR (set interrupt request) signal to regenerate the PRL, IRQ, and
    SRQ signals.  No signal states need to be stored in the DIB, so none need to
    be saved when execution stops.

 7. PON (power on), POPIO (power on preset to I/O), and CRS (control reset) are
    modelled as follows:

     - PON is simulated by RESET -P <dev> and calls <dev>_reset(), which does
       any power-on-specific handling before dispatching POPIO to the signal
       handler.

     - POPIO is simulated by RESET <dev> and calls <dev>_reset(), which
       dispatches POPIO to the signal handler.  Within the handler, POPIO falls
       into CRS.

     - CRS is simulated by CLC 0 and dispatches CRS to the signal handler.

There are four possible implementations of the interface backplane signal handler:

 1. devio (select_code, signal, clear_flag, data)
      - case statement to handle signals (except ioCLF, ioSIR)
      - clear_flag TRUE handles ioCLF case
      - unconditionally calculate PRL, IRQ, SRQ to handle ioSIR case

    iodata = devdisp (dev, iosig, clf, ABREG [ab]);

    Advantages:
     - one call for STC sc,C

    Disadvantages:
     - ioCLF and ioSIR are implicit
     - PRL, etc. calculation done on signals that don't need it (e.g., ioSFS)

 2. devio (select_code, signal, data)
      - case statement to handle all signals
      - ioSTC, etc. do recursive call for ioSIR

    iodata = devdisp (dev, iosig, ABREG [ab]);
    if (clf) devdisp (dev, ioCLF, 0);

    Advantages:
     - uniform processing within case statement

    Disadvantages:
     - two calls for STC sc,C case
     - ioSIR calculations done twice (once on STC, again on CLF)



------------------
PON notes (review)
------------------

The remaining I/O case, power-on, must be considered in light of the existing
simulator commands and the "dev_reset" function.  From examining "scp.c", it
appears that the "dev_reset" function is called:

 * at simulator startup before the first command prompt

 * while processing a SET <dev> DISABLED or SET <dev> ENABLED command

 * while processing a RESET ALL or RESET <dev> command

 * while processing a RUN or BOOT command

From examining the various "dev_reset" routines in the existing HP device
simulators, it appears that this routine:

 * performs simulation-specific initialization (allocating heap space to
   simulate main memory, making two-card setups consistent)

 * performs device-specific power-on initialization (setting machine registers
   to zero, clearing buffer register values)

 * stops I/O operations in progress (clear CTL, set FLG, cancel pending
   events, etc.)

The last function (clearing I/O) is precisely what the PRESET action does.  For
the other two, they could either be combined into a unified "dev_reset"
execution, or differentiated by a command switch, e.g., "-P" for power-on reset.

Referring to the four instances of "dev_reset" calls above, it would appear that
simulator startup would want to invoke both simulation-specific and
power-on reset actions.  SET ENABLED would want to perform both as well, given
that the "dev_reset" routine isn't called at startup for a device that is
disabled.  SET DISABLED only needs the simulation-specific "hp_enbdis_pair" call
to ensure that disabling one card of a two-card setup disables the other as
well.  The RESET command is intended to bring the simulation to a known state,
so both simulation-specific and power-on actions should be invoked.  And given
that the RUN and BOOT commands are intended to proceed from a known state, they
too should invoke both.

So it would seem that, with the possible exception of SET DISABLED, it is not
necessary to separate out the simulator initialization function from the device
power-on function within the "dev_reset" routines.  Executing a single
"dev_reset" code path in response to any of these actions is acceptable.


Implementation of the four I/O events is not straightforward.  The problem is
that the simulator maintains two sets of device state bits (control, flag,
etc.).  One set is stored in the device's register set and points to variables
contained in the "device information block" (DIB).  These are referenced during
simulator stop and are accessed by referring to the DIB structure fields
directly.  The other set is stored in the CPU's flag arrays.  These are
referenced during simulator run and are accessed via bitfield macros.

Because power-on reset and PRESET are simulator-stopped commands, the device
flags will be in the DIBs, whereas the CLC 0 and CLC sc instruction executions
occur during simulator run, so the flags will be in the flag arrays.  Because
PRESET asserts CRS, device responses to PRESET and CLC 0 will likely be the
same.  However, because these events occur in contrasting simulator states (stop
and run, respectively), they cannot use the same code to access the flags.

There are basically two ways to implement the four events:

 * as four I/O dispatch states ("ioPON", "ioPOPIO", "ioCRS", and "ioCTL"), or

 * as I/O dispatch states for CRS and CLC and as procedures for power-on and
   PRESET.

Because power-on and PRESET both assert CRS, it is desirable to reuse the CRS
code for these events, but the differing flag locations hinders this.  There are
several approaches:

 1. Use four dispatch states and test each access in the dispatch routine to
    determine whether to access via DIBs or flag arrays.

 2. Use four dispatch states and save/restore the flags from the DIBs to the
    flag arrays before/after the dispatch call for power-on and PRESET.

 3. Use four dispatch states and do away with DIB storage by dynamically setting
    the flag array access pointers when the device select code is changed.

 4. Use two dispatch states and two procedures and duplicate the CRS dispatch
    code in the PRESET procedure.

 5. Use two dispatch states and two procedures and abstract the CRS dispatch
    code that does not involve flag access to subordinate procedures, called
    from both the dispatch and PRESET routines.

There are drawbacks to all of these:

 1. The multiple flag access tests would be executed each time a CLC instruction
    was simulated (presuming CLC 0 or CRS that falls into CLC sc).  That would
    slow down the simulator run performance.

 2. While PRESET affects all devices (or two large subsets thereof), power-on
    RESET may be directed to a single device, so DIB/array save/restores would
    have to be on a device-by-device basis.

 3. In the stopped state, the flags essentially become a property of the select
    code, rather than the device.  A change of select code could copy the flags
    to the new select code's flag array locations, thereby retaining the
    apparent device state.  However, with two devices temporarily assigned to
    the same select code, changing one would surreptitiously change the other.

 4. Duplicating code that may be quite involved (e.g., the 7970 interface issues
    a tape CLEAR command in response to CRS) will lead to maintenance problems,
    and clarity will be reduced by having I/O event code physically separated
    into multiple procedures and utilizing two unrelated flag-access mechanisms.

 5. Abstracting the non-flag code bits may lead to fracturing the clarity of the
    original I/O handling code if the logical flow has several code segments
    surrounding flag-access points (results in multiple "procedure slices" that
    must be executed in sequence to perform a single operation).

The general benefits of the all-dispatch approach is that all of the code
responding to I/O backplane signals is centralized and uses the same access
mechanisms.  Also, the implication of PRESET by power-on and of CRS by PRESET
may be expressed simply by having the I/O event cases lead into one another.

The benefit of the split dispatch/procedure approach is that the separate parts
may work directly on the relevant flag locations without the overhead of saving
and restoring.  Also, support for resetting a single device is inherent.

Code for the all-dispatch approach would look like this (assume that PON clears
the device buffer register, PRESET sets the device flag, CRS clears the device
command, and CLC clears the device control):

  switch (inst) {
  ...

  case ioPON:
      // do other power-on stuff here
      dev_buf = 0;
      // PON implies POPIO, so fall into next case

  case ioPOPIO:
      // do other PRESET stuff here
      setFLG (dev);
      // POPIO implies CRS, so fall into next case

  case ioCRS:
      // do other CLC 0 stuff here
      clrCMD (dev);
      // use CLC sc routine to complete device I/O stop

  case ioCTL:
      // do other CLC sc stuff here
      clrCTL (dev);

  ... }

Again, clarity of expression and simple reuse of states is the primary
advantage.

Code for the split dispatch/procedure approach would look like this:


  switch (inst) {
  ...

  case ioCRS:
      do_crs_stuff ();  // do other CRS stuff
      clrCMD (dev);
      // use CLC sc routine to complete device I/O stop

  case ioCTL:
      do_clc_stuff ();  // do other CLC stuff
      clrCTL (dev);

  ... }

  t_stat dev_preset (...);
  {
      // do other PRESET stuff here
      dev_dib.flg = 1;

      // POPIO implies CRS, so do CRS stuff
      do_crs_stuff ();
      dev_dib.cmd = 0;

      do_clc_stuff ();
      dev_dib.ctl = 0;

      return ...;
  }

  t_stat dev_reset (...);
  {
      // do other power-on stuff here
      dev_buf = 0;
      return dev_preset (...);  // PON implies POPIO
  }

The primary disadvantage is the potential for fracturing the CRS and CLC
dispatch flow if multiple flag-access points exist.  For instance, given:

  case ioCTL:
      ...
      switch (buf) {
         case FN_A:
         ...
         break;

         case FN_B:
         ...
         setCMD (dev);
         ...
         break;

      }
      ...
      setCTL (dev);
      ...

...and that CRS causes FN_B to occur, we would have:

  case ioCTL:
      sub_1 ();  // was ...
      switch (buf) {
         case FN_A:
         ...
         break;

         case FN_B:
         sub_2 ();  // was ...
         setCMD (dev);
         sub_3 ();  // was ...
         break;

      }
      sub_4 ();  // was ...
      setCTL (dev);
      sub_5 ();  // was ...

...in order to have:

  t_stat dev_preset (...);
  {
      sub_1 ();
      sub_2 ();

      dev_dib.cmd = 1;

      sub_3 ();
      sub_4 ();

      dev_dib.ctl = 1;

      sub_5 ();
      return ...;
  }

Approach #2 (save/restore DIBs) would seem to be the best.  It has these
advantages over the other methods:

 * The I/O dispatch code incurs no run-time penalty (the time penalty is in
   the save/restore operation, which occurs during simulator stop).

 * The DIBs are retained, so flags stay with the device during select code
   changes.

 * The code is clear.  There are no duplicated or extracted sections, the flag
   access method is uniform, I/O signal handling is localized, and the signal
   implications are expressed by the code flow.

Implementing approach #2 requires that the DIB flags be restored to the flag
arrays before dispatching PON or PRESET and then copied back to the DIBs upon
return.  This is simple for the PRESET command: the code to copy to and from the
DIBs already exists at the start and end of the "sim_instr" routine
(hp2100_cpu.c).  These two code sections may be extracted to procedures to do
this and called from the PRESET command routine.

Power-on is a bit more problematic.  The RESET command may be invoked to reset
all devices or just a single device.  As mentioned previously with PRESET, power
on has no analog to RESET <dev>, i.e., the 21xx system cannot be made to assert
PON to a single device.  So dispatching "ioPON" should be an all-or-nothing
affair.

One solution would be to have the "dev_reset" routine perform only the simulator
reset function ("Typically, RESET stops any in-progress I/O operation, clears
any interrupt request, and returns the device to a quiescent state").  Power-on
would be dispatched only when the CPU device was reset, and it would be
dispatched to all devices.

In fact, "cpu_reset" could call the "preset_cmd" function with a flag that says
to dispatch "ioPON" instead of "ioPOPIO" (as the PRESET command would do).  This
would allow reuse of the DIB save/restore and dispatching loop code.

QUESTION: would this allow "stopping any in-progress I/O operation?"  For that
matter, is CLC sc guaranteed to do that (if we wanted to reuse the CLC code)?


In summary, a more accurate physical device model would be produced by:

 1. Adding new I/O dispatch states "ioPON", "ioPOPIO", and "ioCRS".

 2. Altering the existing "CLC 0" routine to dispatch "ioCRS" to each device.

 3. Adding a new, VM-specific "PRESET [-I | -E]" command that will dispatch
    "ioPOPIO" to the appropriate devices with the necessary save/restore of
    DIB flags.

 4. Modifying "cpu_reset" to dispatch "ioPON" to each device via the preset
    command processor.

 5. Modifying each device's "dev_io" routine to implement the appropriate
    actions for each of the new I/O dispatch states.



---------------------------
MP and MEM Violation Checks
---------------------------

The Memory Protect (MP) and Memory Expansion Module (MEM) cards work closely
together to implement a protected mode.  Yet, both cards are optional, and each
can be used without the other.  Protection, however, requires MP; without it,
the MEM will do memory mapping, but no protection is offered.

When enabled, MP prohibits HLTs and any I/O instructions not directed at SC 01
(the overflow register), as well as writes to "protected memory."  Protected
memory is delineated by a lower bound of either location 0 or 2 and an upper
bound set by the value in the memory protect fence register (MPFR).  Any
attempted write access to the area LB <= M < UB is aborted, and an interrupt to
location 5 is generated.

I/O violations are checked automatically.  MP monitors the instruction register
(IR), and a violation is indicated if a HLT is present, or if the IOG signal is
asserted and the select code indicated by the IR is not 01.

Memory violation checks must be requested explicitly by the instruction set
firmware. The presence of an MPCK micro-order causes a check of the memory
address currently on the M Bus with the lower and upper protection bounds.  The
lower bound is established automatically by examining the IR.  If a JMP
instruction is present, or a JSB instruction is present and jumper W5 is out,
then the lower bound is location 0.  Otherwise, the lower bound is location 2.
The upper bound is established by the value in the MPFR.

MPCK provides one of two qualifiers for the MPCARRY (memory protect carry) and
the MPCND (memory protect conditional) signals.  MPCARRY is also qualified by a
comparison of the M Bus with the MPFR.  If the address is lower than the MPFR
value, MPCARRY is generated.  MPCND is also qualified by a comparison of the M
Bus with the lower bound as established above.  If the address is above the
lower bound, MPCND is generated.  A violation is indicated if MPCARRY and MPCND
are both asserted.

Violations set the MPVFF (memory protect violation).  This inhibits the CPU and
memory from altering the addressed contents.  It also sets the flag buffer and
flag to request an interrupt.  Violations are conditional on the MP control
flip-flop.  If control is not set, no violations are signalled.

Note that memory protection isn't strictly tied to write attempts.  Any use of
the MPCK micro-order will perform an address check, regardless of the state of
the memory controller.  All that's required for a check is a value on the M Bus,
a value in the IR, and an MPCK micro-order.  The instruction set firmware
provided by HP issues MPCKs on these instructions:

  all IOG instructions
  STA, STB
  JSB
  ISZ
  JMP
  DST (both M and M+1)

  SAX, SBX, SAY, SBY
  STX, STY
  SBS, CBS
  MVW
  SBT (as a word address)
  MBT (as a word address)
  JLY, JPY, JRS

  XMM, XSA, XSB
  DJP, SJP, DJS, SJS
  UJS, UJP
  MBF, MBI, MBW, MWF, MWI, MWW
  USA, USB, SSM, SYA, SYB, PAA, PAB, PBA, PBB

All of the above use a lower bound of 2, except for JMP, JLY, and JPY, which use
a lower bound of 0 (the JLY and JPY firmware routines store a JMP opcode in the
IR to cause the MP card to use 0 instead of 2).

MEM checks are made by the Memory Expansion Module concurrently with MP checks.
The MEM signals a mapping violation by asserting the MEV signal to the MP card.
MEV sets the MEVFF, the MPVFF, and the flag buffer and flag on the MP card.

MEV is asserted for a read, write, base-page, or privilege violation.  MEV is
qualified by DMAENB and CTL5, so MEV is inhibited for DMA cycles and if MP
control is not set.  In other words, an MEV cannot occur if MP is off or not
present.

A read violation occurs if the read-protect bit is set in the map register
corresponding to the currently accessed page.  Every read request is
checked unconditionally.  A write violation occurs if the write-protect bit is
set and MPCND is asserted.  As with MP, writes are checked for violations only
when the MPCK micro-order is employed.  That is, a write without MPCK will be
allowed to a write-protected page.  A base-page violation occurs if a write
(MPCND) is attempted to the unmapped portion of the base page.  The mapped
portion is indicated by the settings in the base-page fence register.  A
privilege violation occurs if MEM control signals CL0 or CL1 are asserted, or
if CL5 is asserted with S[15:13] = 001.  These occur for writes to the map
registers (MEU micro-order appears in the STORE field of a microinstruction),
and for writes to the MEM state or fence registers.

Read, write, and base-page violations are qualified by MAPON, so they cannot
occur if mapping is disabled.  Privilege violations are not qualified, so they
are inhibited only if MP is off (CTL5 is low).

Even though MEV may be inhibited in the presence of a violation, the violation
is still recorded in the MEM violation register.  The VR is clocked on every
memory read, on every MPCK use, and on every privilege violation.  The VR is
updated continuously until MP is turned off (CTL5 is lowered) or MEV is
asserted.  That is, a MP or MEM violation will "freeze" the VR, so that it can
be examined in an interrupt routine.

The VR also stores the state of the MEBEN (ME bus enabled) signal.  MEBEN is
asserted when mapping is on and the current access is not to the unmapped
portion of the base page.  MEBEN is sent to the memory controller to select
whether the memory address comes from the ME bus or from the M register
contents, i.e., whether the access is mapped or unmapped.  Note that even on
unmapped accesses, the protection bits for the base page are checked, so a read
from the unmapped portion can still generate a read violation if page 0 is
read-protected.