1 /* hp3000_iop.c: HP 3000 30003B I/O Processor simulator
2 
3    Copyright (c) 2016-2019, J. David Bryan
4 
5    Permission is hereby granted, free of charge, to any person obtaining a copy
6    of this software and associated documentation files (the "Software"), to deal
7    in the Software without restriction, including without limitation the rights
8    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9    copies of the Software, and to permit persons to whom the Software is
10    furnished to do so, subject to the following conditions:
11 
12    The above copyright notice and this permission notice shall be included in
13    all copies or substantial portions of the Software.
14 
15    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18    AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22    Except as contained in this notice, the name of the author shall not be used
23    in advertising or otherwise to promote the sale, use or other dealings in
24    this Software without prior written authorization from the author.
25 
26    IOP          HP 3000 Series III I/O Processor
27 
28    09-Dec-19    JDB     Replaced debugging macros with tracing macros
29    18-Feb-19    JDB     Continue iop_poll if first interface returns INTPOLLOUT
30    10-Oct-16    JDB     Renumbered debug flags to start at bit 0
31    03-Sep-16    JDB     Added "iop_assert_PFWARN" to warn devices of power loss
32    01-Aug-16    JDB     Added "iop_reset" to initialize the IOP
33    30-Jun-16    JDB     Changed REG type of filter array to BRDATA
34    08-Jun-16    JDB     Corrected %d format to %u for unsigned values
35    13-May-16    JDB     Modified for revised SCP API function parameter types
36    28-Aug-15    JDB     First release version
37    11-Dec-12    JDB     Created
38 
39    References:
40      - HP 3000 Series II/III System Reference Manual
41          (30000-90020, July 1978)
42      - HP 3000 Series III Engineering Diagrams Set
43          (30000-90141, April 1980)
44 
45 
46    The HP 30003B I/O Processor is an integral part of the HP 3000 system.  It
47    works in conjunction with the CPU and Multiplexer Channel to service the
48    device interfaces.  All I/O interfaces are connected to the IOP bus, which
49    transfers programmed I/O orders to the interfaces and handles memory reads
50    and writes between the interfaces and the CPU stack.  In addition, it
51    provides the memory interface for multiplexer channel transfers and fetches
52    I/O program orders from main memory for the channel.
53 
54    Interrupt requests are serviced by the IOP, which asserts an external
55    interrupt signal to the CPU.  Device controllers request interrupts via the
56    IOP, which prioritizes the requests and grants service to the
57    highest-priority interrupt.  While that interrupt is active, lower-priority
58    requests are held off until it becomes inactive, whereupon the next
59    highest-priority request is granted.  The device number of the interrupting
60    device is stored in the IOP's address register; this is used by the CPU
61    microcode to access the proper entry in the Device Reference Table, which
62    contains the starting address of the I/O handler.
63 
64    In hardware, a device requests an interrupt by asserting INTREQ to the IOP.
65    In response, the IOP polls the interfaces by asserting INTPOLLIN to determine
66    the highest-priority request.  The INTPOLLIN and INTPOLLOUT signals are
67    daisy-chained between interfaces, with the position of the interface in the
68    chain establishing its priority.  Interfaces that are not requesting or
69    processing interrupts pass INTPOLLIN to INTPOLLOUT.  The first interface in
70    the chain that has an interrupt request pending will inhibit INTPOLLOUT and
71    will set its Interrupt Active flip-flop.  As long as the interrupt is active,
72    an interface will break the poll chain by denying INTPOLLOUT.  This holds off
73    requests from lower-priority devices.
74 
75    To avoid scanning each interface's DIB for interrupt requests, the IOP
76    simulator maintains two 32-bit vectors: a global "iop_interrupt_request_set"
77    and a local "interrupt_poll_set".  Each bit is associated with an interrupt
78    priority number from 0-31.  The bits of the request set indicate which
79    interfaces are requesting interrupts, and the bits of the poll set indicate
80    which interfaces will break the poll chain when they are polled.  The lowest
81    set bit in each indicates the highest-priority interrupting device and the
82    highest-priority device handler currently executing, respectively.  An
83    interface requests an interrupt by asserting INTREQ to the IOP.  The IOP then
84    sets the request and poll bits corresponding to that interface's interrupt
85    priority number.  The CPU checks the request set periodically to determine if
86    an external interrupt is present.
87 
88    A device's DIB (Device Information Block) contains three values that pertain
89    to interrupts: the "interrupt_priority" value determines which bit is set or
90    cleared in the bit vectors, the "interrupt_request" flip-flop indicates that
91    the interface is requesting an interrupt from the IOP, and the
92    "interrupt_active" flip-flop indicates that the device's interrupt handler is
93    executing.  The two flip-flop values indicate one of four possible interrupt
94    states that are reflected in the associated bit of the bit vectors:
95 
96      Interrupt  Interrupt  Request  Poll
97       Request    Active      Set    Set   Interrupt State
98      ---------  ---------  -------  ----  ------------------------------------
99        CLEAR      CLEAR       0      0    Not interrupting
100         SET       CLEAR       1      1    Interrupt requested
101        CLEAR       SET        0      1    Interrupt acknowledged
102         SET        SET        1      1    Interrupt requested while in handler
103 
104    The "requested" state corresponds to the device interface asserting the
105    INTREQ signal to the IOP.  The "acknowledged" state corresponds to the IOP
106    conducting a poll via INTPOLL IN and INTPOLL OUT, and the device interface
107    responding by inhibiting INTPOLL OUT and asserting INTACK to the IOP.
108 
109    If both the request and active flip-flops are set, the device has requested a
110    second interrupt while the first is still being processed.  The ATC TDI, for
111    example, does this when a CIO is issued to acknowledge an interrupt before
112    the IXIT sends an ioRIN to the interface to reset the active flip-flop.
113 
114    Device interfaces maintain the states of their interrupt flip-flops for the
115    benefit of IOP initialization.  During the instruction execution prelude, the
116    IOP will reconstruct its bit vectors from the DIB values.  Thereafter, the
117    interfaces change their interrupt states in response to signals, and the IOP
118    adjusts the bit vectors as needed.  The only direct interaction needed is an
119    "iop_assert_INTREQ" call from the device interface when an interrupt is
120    initially requested.
121 
122    The IOP does not have a programmable interface.  It is manipulated directly
123    by the CPU microcode to issue direct I/O commands to the device interfaces,
124    and by the multiplexer channel to transfer data and I/O programs to and from
125    memory.
126 
127    Direct I/O instructions are sent via the IOP Bus to all device interfaces.
128    When executing I/O instructions, the CPU microcode writes a 16-bit command
129    word to the IOP, which then places bits 5-7 of that word onto the IOP Bus as
130    IOCMD0-2 as follows:
131 
132          CPU      Command  IOCMD  Generated               Action
133      Instruction   Word    0 1 2   Signal               Performed
134      -----------  -------  -----  ---------  --------------------------------
135          SIN      100000   0 0 0  DSETINT    Set interrupt request flip-flop
136          CIO      100400   0 0 1  DCONTSTB   Write a control word
137          SIO      101000   0 1 0  DSTARTIO   Start a channel program
138          WIO      101400   0 1 1  DWRITESTB  Write a data word
139         IXIT      102000   1 0 0  DRESETINT  Reset interrupt active flip-flop
140          TIO      102400   1 0 1  DSTATSTB   Read a status word
141         SMSK      103000   1 1 0  DSETMASK   Set the interrupt mask flip-flop
142          RIO      103400   1 1 1  DREADSTB   Read a data word
143 
144    The SIO instruction sends an SIO IOCMD to the device interface via the IOP
145    to begin execution of a channel program.  The program consists of two-word
146    programmed I/O orders, with each pair consisting of an I/O Control Word and
147    an I/O Address Word.  They are encoded as follows:
148 
149        IOCW        IOCW            IOAW        Generated           Action
150      0 1 2 3       4-15            0-15         Signal           Performed
151      -------  --------------  --------------  -----------  ---------------------
152      0 0 0 0  0 XXXXXXXXXXX   Jump Address        --       Unconditional Jump
153      0 0 0 0  1 XXXXXXXXXXX   Jump Address    SETJMP       Conditional Jump
154      0 0 0 1  0 XXXXXXXXXXX   Residue Count       --       Return Residue
155      0 0 0 1  1 XXXXXXXXXXX   Bank Address        --       Set Bank
156      0 0 1 0  X XXXXXXXXXXX   --- XXXXXX ---  SETINT       Interrupt
157      0 0 1 1  0 XXXXXXXXXXX   Status Value    TOGGLESIOOK  End without Interrupt
158      0 0 1 1  1 XXXXXXXXXXX   Status Value    SETINT       End with Interrupt
159      0 1 0 0  Control Word 1  Control Word 2  PCONTSTB     Control
160      0 1 0 1  X XXXXXXXXXXX   Status Value    PSTATSTB     Sense
161      C 1 1 0  Neg Word Count  Write Address   PWRITESTB    Write
162      C 1 1 1  Neg Word Count  Read Address    PREADSTB     Read
163 
164    The "Unconditional Jump," "Return Residue," and "Set Bank" orders are
165    executed entirely by the channel and are not sent to the device interface.
166 
167    The IOP simulator provides the capability to trace direct I/O commands and
168    interrupt requests, as well as memory accesses made on behalf of the
169    multiplexer channel.  Devices that periodically interrupt, such as the system
170    clock, may generate a large number of trace events.  To accommodate this, a
171    filter may be applied to remove trace events from devices that are not of
172    interest.
173 */
174 
175 
176 #include "hp3000_defs.h"
177 #include "hp3000_cpu.h"
178 #include "hp3000_cpu_ims.h"
179 #include "hp3000_io.h"
180 #include "hp3000_mem.h"
181 
182 
183 
184 /* Program constants */
185 
186 #define TRACE_ALL           D32_UMAX            /* enable tracing of all devices */
187 
188 
189 /* Debug flags.
190 
191    The FILTER macro tests if the supplied device number is to be filtered out of
192    the trace stream.  It returns the bit in the filter array corresponding to
193    the device number.  If the bit is set, the trace will be generated;
194    otherwise, it will be suppressed.
195 */
196 
197 #define DEB_DIO             (1u << 0)           /* trace direct I/O commands */
198 #define DEB_IRQ             (1u << 1)           /* trace interrupt requests */
199 
200 #define FILTER(d)           (1u << (d) % 32 & filter [(d) / 32])
201 
202 
203 /* IOP global data structures */
204 
205 const SIO_ORDER to_sio_order [] = {             /* translation of IOCW bits 0-4 to SIO_ORDER */
206     sioJUMP,                                    /*   00000 = Jump unconditionally */
207     sioJUMPC,                                   /*   00001 = Jump conditionally */
208     sioRTRES,                                   /*   00010 = Return residue */
209     sioSBANK,                                   /*   00011 = Set bank */
210     sioINTRP,                                   /*   00100 = Interrupt */
211     sioINTRP,                                   /*   00101 = Interrupt */
212     sioEND,                                     /*   00110 = End */
213     sioENDIN,                                   /*   00111 = End with interrupt */
214     sioCNTL,                                    /*   01000 = Control */
215     sioCNTL,                                    /*   01001 = Control */
216     sioSENSE,                                   /*   01010 = Sense */
217     sioSENSE,                                   /*   01011 = Sense */
218     sioWRITE,                                   /*   01100 = Write */
219     sioWRITE,                                   /*   01101 = Write */
220     sioREAD,                                    /*   01110 = Read */
221     sioREAD,                                    /*   01111 = Read */
222     sioJUMP,                                    /*   10000 = Jump unconditionally */
223     sioJUMPC,                                   /*   10001 = Jump conditionally */
224     sioRTRES,                                   /*   10010 = Return residue */
225     sioSBANK,                                   /*   10011 = Set bank */
226     sioINTRP,                                   /*   10100 = Interrupt */
227     sioINTRP,                                   /*   10101 = Interrupt */
228     sioEND,                                     /*   10110 = End */
229     sioENDIN,                                   /*   10111 = End with interrupt */
230     sioCNTL,                                    /*   11000 = Control */
231     sioCNTL,                                    /*   11001 = Control */
232     sioSENSE,                                   /*   11010 = Sense */
233     sioSENSE,                                   /*   11011 = Sense */
234     sioWRITEC,                                  /*   11100 = Write Chained */
235     sioWRITEC,                                  /*   11101 = Write Chained */
236     sioREADC,                                   /*   11110 = Read Chained */
237     sioREADC                                    /*   11111 = Read Chained */
238     };
239 
240 
241 const char *const sio_order_name [] = {         /* indexed by SIO_ORDER */
242     "Jump",                                     /*   sioJUMP   */
243     "Conditional Jump",                         /*   sioJUMPC  */
244     "Return Residue",                           /*   sioRTRES  */
245     "Set Bank",                                 /*   sioSBANK  */
246     "Interrupt",                                /*   sioINTRP  */
247     "End",                                      /*   sioEND    */
248     "End with Interrupt",                       /*   sioENDIN  */
249     "Control",                                  /*   sioCNTL   */
250     "Sense",                                    /*   sioSENSE  */
251     "Write",                                    /*   sioWRITE  */
252     "Write Chained",                            /*   sioWRITEC */
253     "Read",                                     /*   sioREAD   */
254     "Read Chained"                              /*   sioREADC  */
255     };
256 
257 
258 /* Global IOP state */
259 
260 uint32 iop_interrupt_request_set = 0;           /* the set of interfaces requesting interrupts */
261 
262 
263 /* Local IOP state */
264 
265 static uint32 IOA = 0;                          /* I/O Address Register */
266 
267 static uint32 interrupt_poll_set = 0;           /* the set of interfaces breaking the poll chain */
268 static DIB    *devs [DEVNO_MAX + 1];            /* index by device number for I/O instruction dispatch */
269 static DIB    *irqs [INTPRI_MAX + 1];           /* index by interrupt priority number for interrupt requests */
270 
271 static uint32 filter [4] = {                    /* filter bitmap for device numbers 0-127 */
272     TRACE_ALL,
273     TRACE_ALL,
274     TRACE_ALL,
275     TRACE_ALL
276     };
277 
278 
279 /* IOP local SCP support routines */
280 
281 static t_stat iop_reset       (DEVICE *dptr);
282 static t_stat iop_set_filter  (UNIT *uptr, int32 value, char  *cptr, void *desc);
283 static t_stat iop_show_filter (FILE *st,   UNIT  *uptr, int32 value, void *desc);
284 
285 
286 /* IOP SCP data structures */
287 
288 
289 /* Unit list */
290 
291 static UNIT iop_unit [] = {                     /* a dummy unit to satisfy SCP requirements */
292     { UDATA (NULL, 0, 0) }
293     };
294 
295 /* Register list.
296 
297 
298    Implementation notes:
299 
300     1. The "interrupt_poll_set", "devs", and "irqs" variables need not be SAVEd
301        or RESTOREd, as they are rebuilt during the instruction execution
302        prelude.
303 */
304 
305 static REG iop_reg [] = {
306 /*    Macro   Name    Location  Radix  Width  Depth   Flags  */
307 /*    ------  ------  --------  -----  -----  -----  ------- */
308     { ORDATA (IOA,    IOA,               8),         REG_RO  },
309     { BRDATA (FILTER, filter,     2,    32,     4),  REG_HRO },
310     { NULL }
311     };
312 
313 /* Modifier list */
314 
315 static MTAB iop_mod [] = {
316 /*    Entry Flags          Value  Print String  Match String  Validation       Display           Descriptor */
317 /*    -------------------  -----  ------------  ------------  ---------------  ----------------  ---------- */
318     { MTAB_XDV | MTAB_NMO,   1,   "FILTER",     "FILTER",     &iop_set_filter, &iop_show_filter, NULL       },
319     { MTAB_XDV | MTAB_NMO,   0,   "",           "NOFILTER",   &iop_set_filter, NULL,             NULL       },
320     { 0 }
321     };
322 
323 /* Debugging trace list */
324 
325 static DEBTAB iop_deb [] = {
326     { "DIO",  DEB_DIO   },                      /* direct I/O commands issued */
327     { "IRQ",  DEB_IRQ   },                      /* interrupt requests received */
328     { "DATA", DEB_MDATA },                      /* I/O data accesses to memory */
329     { NULL,   0         }
330     };
331 
332 /* Device descriptor */
333 
334 DEVICE iop_dev = {
335     "IOP",                                      /* device name */
336     iop_unit,                                   /* unit array */
337     iop_reg,                                    /* register array */
338     iop_mod,                                    /* modifier array */
339     1,                                          /* number of units */
340     8,                                          /* address radix */
341     PA_WIDTH,                                   /* address width */
342     1,                                          /* address increment */
343     8,                                          /* data radix */
344     DV_WIDTH,                                   /* data width */
345     NULL,                                       /* examine routine */
346     NULL,                                       /* deposit routine */
347     &iop_reset,                                 /* reset routine */
348     NULL,                                       /* boot routine */
349     NULL,                                       /* attach routine */
350     NULL,                                       /* detach routine */
351     NULL,                                       /* device information block pointer */
352     DEV_DEBUG,                                  /* device flags */
353     0,                                          /* debug control flags */
354     iop_deb,                                    /* debug flag name array */
355     NULL,                                       /* memory size change routine */
356     NULL                                        /* logical device name */
357     };
358 
359 
360 
361 /* IOP global routines */
362 
363 
364 
365 /* Initialize the I/O processor.
366 
367    This routine is called in the instruction prelude to set up the IOP data
368    structures prior to beginning execution.  It sets up two tables of DIB
369    pointers -- one indexed by device number, and a second indexed by interrupt
370    request number.  This allows fast access to the device interface routine by
371    the direct I/O instruction and interrupt poll processors, respectively.
372 
373    It also sets the interrupt request and poll bit vectors from the interrupt
374    flip-flop values in the device DIBs and clears the external interrupt flag if
375    there are no devices with active interrupts (as the user may have set the
376    flag or reset the interrupting device during a simulation stop).
377 
378    The value of the IOA register is returned.  This is zero unless a device
379    requesting an interrupt has been acknowledged but not yet serviced, in which
380    case the value is the device number.
381 */
382 
iop_initialize(void)383 uint32 iop_initialize (void)
384 {
385 const DEVICE *dptr;
386 DIB    *dibptr;
387 uint32 i, irq;
388 
389 iop_interrupt_request_set = 0;                          /* set all interrupt requests inactive */
390 interrupt_poll_set        = 0;                          /* set all poll continuity bits inactive */
391 
392 memset (devs, 0, sizeof devs);                          /* clear the device number table */
393 memset (irqs, 0, sizeof irqs);                          /*   and the interrupt request table */
394 
395 for (i = 0; sim_devices [i] != NULL; i++) {             /* loop through all of the devices */
396     dptr = sim_devices [i];                             /* get a pointer to the device */
397     dibptr = (DIB *) dptr->ctxt;                        /*   and to that device's DIB */
398 
399     if (dibptr && !(dptr->flags & DEV_DIS)) {           /* if the DIB exists and the device is enabled */
400         if (dibptr->device_number != DEVNO_UNUSED)      /*   then if the device number is valid */
401             devs [dibptr->device_number] = dibptr;      /*     then set the DIB pointer into the device dispatch table */
402 
403         if (dibptr->interrupt_priority != INTPRI_UNUSED) {  /* if the interrupt priority is valid */
404             irqs [dibptr->interrupt_priority] = dibptr;     /*   then set the pointer into the interrupt dispatch table */
405 
406             irq = 1 << dibptr->interrupt_priority;      /* get the associated interrupt request bit */
407 
408             if (dibptr->interrupt_request) {            /* if the interface is requesting an interrupt */
409                 iop_interrupt_request_set |= irq;       /*   then set the request bit */
410                 interrupt_poll_set        |= irq;       /*     and the poll bit */
411                 }
412 
413             else if (dibptr->interrupt_active)          /* otherwise if the interface has acknowledged an interrupt */
414                 interrupt_poll_set |= irq;              /*   then just set the poll bit */
415             }
416         }
417     }
418 
419 if (interrupt_poll_set == 0 || IOA == 0)                /* if no device has an active interrupt in progress */
420     CPX1 &= ~cpx1_EXTINTR;                              /*   then clear the interrupt flag */
421 
422 return IOA;
423 }
424 
425 
426 /* Poll the interfaces for an active interrupt request.
427 
428    This routine is called in the instruction loop when the request set indicates
429    that one or more interrupt requests are pending.  It polls the interface
430    asserting the highest-priority request.  If the interface acknowledges the
431    interrupt, the routine sets the "external interrupt" bit in the CPU's CPX1
432    register to initiate interrupt processing, sets the IOP's IOA register to the
433    the device number of the interrupting device, and returns that value to the
434    caller.
435 
436    In hardware, an interface requesting an interrupt with its Interrupt Mask
437    flip-flop set will assert a common INTREQ to the IOP.  In response, the IOP
438    polls the interfaces to determine the highest-priority request by asserting
439    INTPOLLIN.  The INTPOLLIN and INTPOLLOUT signals are daisy-chained between
440    interfaces, with the position of the interface in the chain establishing its
441    priority.  Interfaces that are not requesting interrupts pass INTPOLLIN to
442    INTPOLLOUT.  The first interface in the chain that has its Interrupt Request
443    flip-flop set will inhibit INTPOLLOUT, set its Interrupt Active flip-flop,
444    and assert INTACK to the IOP.
445 
446    To avoid polling interfaces in simulation, an interface will set the
447    Interrupt Request flip-flop in its DIB and then call iop_assert_INTREQ.  That
448    routine sets the request set and poll set bits corresponding to the interrupt
449    priority value in the DIB.
450 
451    In the instruction execution loop, if external interrupts are enabled (i.e.,
452    the I bit in the status word is set), and iop_interrupt_request_set has one
453    or more bits set, this routine is called to select the interrupt request to
454    service.
455 
456    The end of priority chain is marked by the highest-priority (lowest-order)
457    poll bit that is set.  When a poll is performed, a priority mask is generated
458    that contains just the highest-priority bit.  The device corresponding to
459    that bit will then be the recipient of the current interrupt acknowledgement
460    cycle.  After the interrupt request has been cleared, the poll bit will
461    prevent lower-priority interrupts from being serviced.
462 
463    For example:
464 
465      poll set      : ...0 0 1 0 0 1 0 0 0 0 0 0  (poll denied at INTPRI 6 and 9)
466      priority mask : ...0 0 0 0 0 1 0 0 0 0 0 0  (poll stops at INTPRI 6)
467 
468    The request is then ANDed with the priority mask to determine if a request is
469    to be granted:
470 
471      pri mask      : ...0 0 0 0 0 1 0 0 0 0 0 0  (allowed interrupt source)
472      request set   : ...0 0 1 0 0 1 0 0 0 0 0 0  (devices requesting interrupts)
473      ANDed value   : ...0 0 0 0 0 1 0 0 0 0 0 0  (request to grant = INTPRI 6)
474 
475    Once the interrupt request has been cleared, the poll state is:
476 
477      poll set      : ...0 0 1 0 0 1 0 0 0 0 0 0  (poll denied at INTPRI 6 and 9)
478      priority mask : ...0 0 0 0 0 1 0 0 0 0 0 0  (poll stops at INTPRI 6)
479      request set   : ...0 0 1 0 0 0 0 0 0 0 0 0  (devices requesting interrupts)
480      ANDed value   : ...0 0 0 0 0 0 0 0 0 0 0 0  (request to grant = none)
481 
482    INTPRI 9 will continue to be held off until INTPRI 6 completes its interrupt
483    handler and resets the Interrupt Active flip-flop on its interface, which
484    also clears the associated poll set bit.  At the next poll, INTPRI 9 will be
485    granted.
486 
487    This routine determines the request to grant, converts that back to an
488    interrupt priority number, and uses that to index into the table of DIBs.
489    The interface routine associated with the DIB is called with INTPOLLIN
490    asserted.
491 
492    If the interface still has its Interrupt Request flip-flop set, it
493    will assert INTACK and return the device number.  In response, the IOP will
494    save the device number in IOA, set the external interrupt bit in CPX1, and
495    return the device number to the CPU.  This will cause the CPU to service the
496    interrupt.
497 
498    However, if some condition has occurred between the time of the original
499    request and this poll, the interface will assert INTPOLLOUT.  In response,
500    the IOP will clear IOA and the associated bit in the poll set to cancel the
501    request.  If any other interfaces are asserting INTREQ, the poll continues
502    with the next-highest-priority interface in the chain.
503 
504    In either case, the associated bit in the request set is cleared.
505 
506 
507    Implementation notes:
508 
509     1. The hardware inhibits the interrupt poll if the EXTINT flip-flop is set.
510        This prevents a second interrupt from changing IOA until the microcode
511        signals its readiness by clearing EXTINT.  In simulation, entry with
512        cpx1_EXTINTR set returns IOA in lieu of conducting a poll.
513 
514     2. An interface that asserted INTREQ should respond to an INTPOLL by
515        asserting either INTACK or INTPOLLOUT.  If it does neither, then the
516        interface is inhibiting lower-priority interrupts but not performing an
517        interrupt itself.  We simulate this by clearing the request set bit but
518        not the poll set bit.
519 */
520 
iop_poll(void)521 uint32 iop_poll (void)
522 {
523 DIB         *dibptr;
524 SIGNALS_DATA outbound;
525 uint32       ipn, priority_mask, request_granted;
526 
527 if (CPX1 & cpx1_EXTINTR)                                /* if an external interrupt has been requested */
528     return IOA;                                         /*   then return the device number in lieu of polling */
529 
530 else do {                                               /* otherwise poll for an interrupt request */
531     priority_mask   = IOPRIORITY (interrupt_poll_set);              /* calculate the priority mask */
532     request_granted = priority_mask & iop_interrupt_request_set;    /*   and determine the request to grant */
533 
534     if (request_granted == 0)                           /* if no request was granted */
535         return 0;                                       /*   then return */
536     else                                                /* otherwise */
537         iop_interrupt_request_set &= ~priority_mask;    /*   clear the request */
538 
539     for (ipn = 0; !(request_granted & 1); ipn++)        /* determine the interrupt priority number */
540         request_granted = request_granted >> 1;         /*   by counting the bits until the set bit is reached */
541 
542     dibptr = irqs [ipn];                                /* get the DIB pointer for the request */
543 
544     outbound = dibptr->io_interface (dibptr, INTPOLLIN, 0); /* poll the interface that requested the interrupt */
545 
546     if (outbound & INTACK) {                            /* if the interface acknowledged the interrupt */
547         IOA = IODATA (outbound);                        /*   then save the returned device number */
548         CPX1 |= cpx1_EXTINTR;                           /*     and tell the CPU */
549 
550         tprintf (iop_dev, FILTER (dibptr->device_number) ? DEB_IRQ : 0,
551                  "Device number %u acknowledged interrupt request at priority %u\n",
552                  dibptr->device_number, ipn);
553 
554         break;                                          /* stop the poll at this interface */
555         }
556 
557     else if (outbound & INTPOLLOUT) {                   /* otherwise if the interface cancelled the request */
558         IOA = 0;                                        /*   then clear the device number */
559         interrupt_poll_set &= ~priority_mask;           /*     and the associated bit in the poll set */
560 
561         tprintf (iop_dev, FILTER (dibptr->device_number) ? DEB_IRQ : 0,
562                  "Device number %u canceled interrupt request at priority %u\n",
563                  dibptr->device_number, ipn);
564         }
565 
566     else {                                              /* otherwise the interface inhibited the poll */
567         IOA = 0;                                        /*   so clear the device number */
568 
569         tprintf (iop_dev, FILTER (dibptr->device_number) ? DEB_IRQ : 0,
570                  "Device number %u inhibited INTPOLLIN at priority %u\n",
571                  dibptr->device_number, ipn);
572 
573         break;                                          /* stop the poll at this interface */
574         }
575     }
576 
577 while (iop_interrupt_request_set != 0);                 /* continue polling while requests remain */
578 
579 return IOA;                                             /* return the interrupting device number */
580 }
581 
582 
583 /* Dispatch an I/O command to an interface.
584 
585    This routine is called by the CPU when executing direct I/O instructions
586    to send I/O orders to the indicated device interface.  It translates the
587    "io_cmd" value to the appropriate I/O signal and calls the signal handler of
588    the device interface indicated by the "device_number" with the supplied
589    "write_value".  The handler return value, if any, is returned as the function
590    value.  If the supplied device number does not correspond to an enabled
591    device, the I/O Timeout bit in CPX1 is set.
592 
593    A "Set Interrupt Mask" order is sent to all active interfaces; the supplied
594    device number is ignored.  If there are none, the I/O Timeout bit is set.
595    All of the other orders are sent only to the specified device.  A "Reset
596    Interrupt" order clears the corresponding bit from the poll set, unless there
597    is a request pending on the device (which may occur if a second interrupt was
598    requested while the first was still being processed).
599 
600 
601    Implementation notes:
602 
603     1. For a "Set Interrupt Mask" order, it would be faster to cycle through the
604        sim_devices array to find the active devices.  However, we use the devs
605        array so that interfaces are accessed in DEVNO order, which makes traces
606        easier to follow.  This is an acceptable tradeoff, as the SMSK
607        instruction is used infrequently.
608 */
609 
iop_direct_io(HP_WORD device_number,IO_COMMAND io_cmd,HP_WORD write_value)610 HP_WORD iop_direct_io (HP_WORD device_number, IO_COMMAND io_cmd, HP_WORD write_value)
611 {
612 static const INBOUND_SIGNAL cmd_to_signal [] = {        /* indexed by IO_COMMAND */
613     DSETINT,                                            /*   ioSIN  = set interrupt */
614     DCONTSTB,                                           /*   ioCIO  = control I/O */
615     DSTARTIO,                                           /*   ioSIO  = start I/O */
616     DWRITESTB,                                          /*   ioWIO  = write I/O */
617     DRESETINT,                                          /*   ioRIN  = reset interrupt */
618     DSTATSTB,                                           /*   ioTIO  = test I/O */
619     DSETMASK,                                           /*   ioSMSK = set interrupt mask */
620     DREADSTB                                            /*   ioRIO  = read I/O */
621     };
622 
623 static const char *const io_command_name [] = {         /* indexed by IO_COMMAND */
624     "Set Interrupt",                                    /*   ioSIN  */
625     "Control I/O",                                      /*   ioCIO  */
626     "Start I/O",                                        /*   ioSIO  */
627     "Write I/O",                                        /*   ioWIO  */
628     "Reset Interrupt",                                  /*   ioRIN  */
629     "Test I/O",                                         /*   ioTIO  */
630     "Set Interrupt Mask",                               /*   ioSMSK */
631     "Read I/O"                                          /*   ioRIO  */
632     };
633 
634 uint32       irq, devno;
635 t_bool       no_response;
636 DIB          *dibptr;
637 SIGNALS_DATA outbound = NO_SIGNALS;
638 
639 if (io_cmd == ioSMSK) {                                     /* if the I/O order is "Set Interrupt Mask" */
640     no_response = TRUE;                                     /*   then check for responding devices */
641 
642     for (devno = 0; devno <= DEVNO_MAX; devno++) {          /* loop through the device number list */
643         dibptr = devs [devno];                              /*   to get a device information block pointer */
644 
645         if (dibptr                                          /* if this device is defined */
646           && dibptr->interrupt_mask != INTMASK_UNUSED) {    /*   and uses the interrupt mask */
647 
648             tprintf (iop_dev, FILTER (devno) ? DEB_DIO : 0,
649                      "%s order sent to device number %u\n",
650                      io_command_name [io_cmd], devno);
651 
652             outbound =
653                dibptr->io_interface (dibptr, DSETMASK,      /* send the SET MASK signal to the device */
654                                      write_value);          /*   and supply the new mask value */
655 
656             if (outbound & INTREQ)                          /* if an interrupt request was asserted */
657                 iop_assert_INTREQ (dibptr);                 /*   then set it up */
658 
659             no_response = FALSE;                            /* at least one device has responded */
660             }
661         }
662 
663     if (no_response)                                        /* if no devices responded */
664         CPX1 |= cpx1_IOTIMER;                               /*   then indicate an I/O timeout */
665     }
666 
667 else {                                                      /* otherwise a device-specific order is present */
668     device_number = device_number & DEVNO_MASK;             /* restrict the device number to 0-127 */
669 
670     tprintf (iop_dev, FILTER (device_number) ? DEB_DIO : 0,
671              "%s order sent to device number %u\n",
672              io_command_name [io_cmd], device_number);
673 
674     dibptr = devs [device_number];                          /* get the device information block pointer */
675 
676     if (dibptr == NULL)                                     /* if the device not present */
677         CPX1 |= cpx1_IOTIMER;                               /*   then indicate an I/O timeout on access */
678 
679     else {                                                  /* otherwise call the device interface */
680         outbound =                                          /*   with the indicated signal and write value */
681            dibptr->io_interface (dibptr, cmd_to_signal [io_cmd],
682                                  write_value);
683 
684         if (outbound & INTREQ)                              /* if an interrupt request was asserted */
685             iop_assert_INTREQ (dibptr);                     /*   then set it up */
686 
687         if (outbound & SRn)                                 /* if a service request was asserted */
688             mpx_assert_SRn (dibptr);                        /*   then set it up */
689 
690         if (io_cmd == ioRIN                                 /* if this a "Reset Interrupt" order */
691           && dibptr->interrupt_priority != INTPRI_UNUSED) { /*   and the interrupt priority is valid */
692             irq = 1 << dibptr->interrupt_priority;          /*     then calculate the device bit */
693 
694             if ((iop_interrupt_request_set & irq) == 0)     /* if no request is pending for this device */
695                 interrupt_poll_set &= ~irq;                 /*   then clear the associated poll bit */
696             }
697         }
698     }
699 
700 return IODATA (outbound);                                   /* return the outbound data value */
701 }
702 
703 
704 /* Request an interrupt.
705 
706    This routine is called by device interfaces to request an external interrupt.
707    It corresponds in hardware to asserting the INTREQ signal.  The routine sets
708    the request and poll set bits corresponding to the interrupt priority number.
709 */
710 
iop_assert_INTREQ(DIB * dibptr)711 void iop_assert_INTREQ (DIB *dibptr)
712 {
713 uint32 irq;
714 
715 tprintf (iop_dev, FILTER (dibptr->device_number) ? DEB_IRQ : 0,
716          "Device number %u asserted INTREQ at priority %u\n",
717          dibptr->device_number, dibptr->interrupt_priority);
718 
719 if (dibptr->interrupt_priority != INTPRI_UNUSED) {      /* if the interrupt priority is valid */
720     irq = 1 << dibptr->interrupt_priority;              /*   then calculate the corresponding priority bit */
721 
722     iop_interrupt_request_set |= irq;                   /* set the request */
723     interrupt_poll_set        |= irq;                   /*   and the poll bits */
724     }
725 
726 return;
727 }
728 
729 
730 /* Warn devices of an impending power failure.
731 
732    This routine is called by the POWER FAIL command to send a warning
733    to all devices that power is about to fail.  It corresponds in hardware to
734    asserting the PFWARN signal.  Devices may process or ignore the signal as
735    appropriate.  If the device returns the INTREQ signal, an interrupt is
736    requested.
737 */
738 
iop_assert_PFWARN(void)739 void iop_assert_PFWARN (void)
740 {
741 uint32       devno;
742 DIB          *dibptr;
743 SIGNALS_DATA outbound;
744 
745 for (devno = 0; devno <= DEVNO_MAX; devno++) {          /* loop through the device number list */
746     dibptr = devs [devno];                              /*   and get the next device information block pointer */
747 
748     if (dibptr != NULL) {                               /* if this device is defined */
749         outbound =                                      /*   then send the PFWARN signal to the device interface */
750           dibptr->io_interface (dibptr, PFWARN, 0);
751 
752         if (outbound & INTREQ)                          /* if the device requested an interrupt */
753             iop_assert_INTREQ (dibptr);                 /*   then set it up */
754         }
755     }
756 
757 return;
758 }
759 
760 
761 
762 /* IOP local SCP support routines */
763 
764 
765 
766 /* Device reset routine.
767 
768    This routine is called for a RESET or RESET IOP command.  It is the
769    simulation equivalent of the IORESET signal, which is asserted by the front
770    panel LOAD and DUMP switches.
771 
772 
773    Implementation notes:
774 
775     1. In hardware, IORESET clears flip-flops associated with the state machines
776        that implement the interrupt poll, SO/SI handshake, and multiplexer
777        channel access.  In simulation, these are all represented by function
778        calls and, as such, are atomic.  Therefore, the only state variable that
779        IORESET clears is the external interrupt flip-flop, which is implemented
780        as its respective bit in the CPX1 register rather than as a separate
781        variable.  Setting IOA to 0 and calling iop_initialize clears this bit;
782        it also sets up the devs array, which is used by the POWER FAIL command.
783 
784     2. In hardware, IORESET also clears the IOP address parity error, system
785        parity error, and illegal address flip-flops.  However, these exist only
786        to assert XFERERROR to devices.  In simulation, XFERERROR is sent to a
787        device interface when the initiating condition is detected by the
788        multiplexer channel, so these are not represented by state variables.
789 */
790 
iop_reset(DEVICE * dptr)791 static t_stat iop_reset (DEVICE *dptr)
792 {
793 IOA = 0;                                                /* clear the I/O Address register and initialize */
794 iop_initialize ();                                      /*   which clears the external interrupt flip-flop */
795 
796 return SCPE_OK;
797 }
798 
799 
800 /* Set the trace omission filter.
801 
802    If the "value" parameter is 1, the filter array bits corresponding to the
803    device number(s) in the buffer referenced by the "cptr" parameter are set to
804    exclude those devices from the trace listing.  If the "value" parameter is 0,
805    the filter array is reset to include all devices.  The unit and descriptor
806    pointer parameters are not used.
807 
808    Each bit of the four, 32-bit filter array elements corresponds to a device
809    number from 0-127, with the LSB of the first element representing device 0,
810    and the MSB of the last element representing device 127.  A set bit enables
811    tracing of that device.  The filter starts out with all bits set, implying
812    that all devices are traced.  Specifying device numbers to filter out clears
813    the corresponding bits.
814 
815    Example filter commands:
816 
817       SET IOP FILTER=3         --  omit tracing for device 3.
818       SET IOP FILTER=4;7-9;11  --  omit tracing for devices 4, 7, 8, 9, and 11.
819       SET IOP FILTER=ALL       --  omit tracing for all devices
820       SET IOP NOFILTER         --  restore tracing for all devices
821 
822    On entry, the "cptr" parameter points to the first character of the range
823    specification, which may be either a semicolon-separated list of device
824    number ranges or the keyword ALL.  Each range is parsed and added to the new
825    filter array.  Once the entire array has been set, it is copied over the old
826    filter.  If an error occurs during parsing, the original filter set is not
827    disturbed.
828 */
829 
iop_set_filter(UNIT * uptr,int32 value,char * cptr,void * desc)830 static t_stat iop_set_filter (UNIT *uptr, int32 value, char *cptr, void *desc)
831 {
832 char   *tptr, *mptr;
833 t_addr dev, low, high;
834 t_stat result = SCPE_OK;
835 uint32 new_filter [4] = { TRACE_ALL, TRACE_ALL, TRACE_ALL, TRACE_ALL };
836 
837 if (value == 1) {                                       /* if we are setting the filter */
838     if ((cptr == NULL) || (*cptr == '\0'))              /*   then if a line range was not supplied */
839         return SCPE_MISVAL;                             /*     then report a "Missing value" error */
840 
841     mptr = (char *) malloc (strlen (cptr) + 2);         /* allocate space for the string, a semicolon, and a NUL */
842 
843     if (mptr == NULL)                                   /* if the allocation failed */
844         return SCPE_MEM;                                /*   report memory exhaustion */
845 
846     strcpy (mptr, cptr);                                /* copy over the existing command string */
847     tptr = strcat (mptr, ";");                          /*   and append a semicolon to make parsing easier */
848 
849     while (*tptr) {                                     /* parse the command string until it is exhausted */
850         tptr = get_range (NULL, tptr, &low, &high,      /* get a semicolon-separated device number range */
851                           10, 127, ';');                /*   in radix 10 with a maximum value of 127 */
852 
853         if (tptr == NULL || low > 127 || high > 127) {  /* if a parsing error occurred or a number was out of range */
854             result = SCPE_ARG;                          /*   then report an "Invalid argument" error */
855             break;                                      /*     and quit at this point */
856             }
857 
858         else for (dev = low; dev <= high; dev++)        /* otherwise loop through the range of device numbers */
859             new_filter [dev / 32] &= ~(1 << dev % 32);  /*   and clear each corresponding bit in the array */
860         }
861 
862     free (mptr);                                        /* deallocate the temporary string */
863     }
864 
865 else if (cptr != NULL)                                  /* otherwise we are clearing the filter */
866     return SCPE_2MARG;                                  /*   and no arguments are allowed or needed */
867 
868 if (result == SCPE_OK) {                                /* if the filter assignment was successful */
869     filter [0] = new_filter [0];                        /*   then copy */
870     filter [1] = new_filter [1];                        /*     the new filter set */
871     filter [2] = new_filter [2];                        /*       in place of */
872     filter [3] = new_filter [3];                        /*         the current filter set */
873     }
874 
875 return result;                                          /* return the result of the command */
876 }
877 
878 
879 /* Show the omission filter.
880 
881    The device numbers in the filter array are printed as a semicolon-separated
882    list on the stream designated by the "st" parameter.  The "uptr", "value",
883    and "desc" parameters are not used.
884 
885    Ranges are printed where possible to shorten the output.  This is
886    accomplished by tracking the starting and ending device numbers of a range of
887    bits in the filter and then printing that range when a device number bit not
888    in the filter is encountered.
889 */
890 
iop_show_filter(FILE * st,UNIT * uptr,int32 value,void * desc)891 static t_stat iop_show_filter (FILE *st, UNIT *uptr, int32 value, void *desc)
892 {
893 int32  group, low, high;
894 uint32 test_filter;
895 t_bool first = TRUE, in_range = FALSE;
896 
897 low = 0;                                                /* initialize the current starting value */
898 
899 for (group = 0; group < 4; group++) {                   /* the filter values are stored in four elements */
900     test_filter = filter [group];                       /* get the set of devices from current element */
901 
902     for (high = group * 32; high < group * 32 + 32; high++) {   /* loop through the represented device numbers */
903         if ((test_filter & 1) == 0) {                           /* if the current device is filtered out */
904             in_range = TRUE;                                    /*   then accumulate the omission range */
905             }
906 
907         else if (in_range) {                            /* otherwise if an omission range was accumulated */
908             if (first) {                                /*   then if this is the first range to be printed */
909                 fputs ("filter=", st);                  /*     then print a header to start */
910                 first = FALSE;
911                 }
912 
913             else                                        /* otherwise this is not the first range to be printed */
914                 fputc (';', st);                        /*   so print a separator after the prior range */
915 
916             if (low == high - 1)                        /* if the range is empty */
917                 fprintf (st, "%d", low);                /*   then print the single device number */
918 
919             else                                        /* otherwise a range was established */
920                 fprintf (st, "%d-%d", low, high - 1);   /*   so print the starting and ending device numbers */
921 
922             in_range = FALSE;                           /* start a new range */
923             low = high + 1;                             /*   from this device number onward */
924             }
925 
926         else                                            /* otherwise we are between ranges */
927             low = low + 1;                              /*   so increment the current starting value */
928 
929         test_filter = test_filter >> 1;                 /* shift the next filter bit into place for testing */
930         }
931     }
932 
933 if (first == TRUE)                                      /* if there is only a single range */
934     if (in_range)                                       /*   then if it's an omission range */
935         fprintf (st, "filter=%d-127\n", low);           /*     then report it */
936 
937     else                                                /* otherwise it's an inclusion range */
938         fputs ("no filter\n", st);                      /*   so report that no devices are filtered out */
939 
940 else                                                    /* otherwise one or more ranges has been printed */
941     fputc ('\n', st);                                   /*   so add a line terminator */
942 
943 return SCPE_OK;
944 }
945