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