1 /*
2 * vim: filetype=c:tabstop=4:ai:expandtab
3 * SPDX-License-Identifier: ICU
4 * SPDX-License-Identifier: Multics
5 * scspell-id: d49ab489-f62e-11ec-9ac1-80ee73e9b8e7
6 *
7 * ---------------------------------------------------------------------------
8 *
9 * Copyright (c) 2007-2013 Michael Mondy
10 * Copyright (c) 2012-2016 Harry Reed
11 * Copyright (c) 2013-2022 Charles Anthony
12 * Copyright (c) 2021-2025 The DPS8M Development Team
13 *
14 * This software is made available under the terms of the ICU License.
15 * See the LICENSE.md file at the top-level directory of this distribution.
16 *
17 * ---------------------------------------------------------------------------
18 *
19 * This source file may contain code comments that adapt, include, and/or
20 * incorporate Multics program code and/or documentation distributed under
21 * the Multics License. In the event of any discrepancy between code
22 * comments herein and the original Multics materials, the original Multics
23 * materials should be considered authoritative unless otherwise noted.
24 * For more details and historical background, see the LICENSE.md file at
25 * the top-level directory of this distribution.
26 *
27 * ---------------------------------------------------------------------------
28 */
29
30 //-V536
31
32 /*
33 * scu.c -- System Controller
34 *
35 * See AN70, section 8 and GB61.
36 *
37 * There were a few variations of SCs and SCUs:
38 * SCU -- Series 60 Level 66 Controller
39 * SC -- Level 68 System Controller
40 * 4MW SCU -- A later version of the Level 68 SC
41 *
42 * SCUs control access to memory.
43 * Each SCU owns a certain range of absolute memory.
44 * This emulator allows the CPU to access memory directly however.
45 * SCUs contain clocks.
46 * SCUS also contain facilities which allow CPUS and IOMs to communicate.
47 * CPUs or IOMS request access to memory via the SCU.
48 * CPUs use the CIOC instr to talk to IOMs and other CPUs via a SCU.
49 * IOMs use interrupts to ask a SCU to signal a CPU.
50 * Other Interesting instructions:
51 * read system controller reg and set system controller reg (RSCR & SSCR)
52 *
53 */
54
55 /*
56 * Physical Details & Interconnection -- AN70, section 8.
57 *
58 * SCUs have 8 ports.
59 * Active modules (CPUs and IOMs) have up to four of their ports
60 * connected to SCU ports.
61 *
62 * The 4MW SCU has eight on/off switches to enable or disable
63 * the ports. However, the associated registers allow for
64 * values of enabled, disabled, and program control.
65 *
66 * SCUs have stores (memory banks).
67 *
68 * SCUs have four sets of registers controlling interrupts. Only two
69 * of these sets, designated "A" and "B" are used. Each set has:
70 * Execute interrupt mask register -- 32 bits; enables/disables
71 * the corresponding execute interrupt cell
72 * Interrupt mask assignment register -- 9 bits total
73 * two parts: assigned bit, set of assigned ports (8 bits)
74 * In Multics, only one CPU will be assigned in either mask
75 * and no CPU appears in both. Earlier hardware versions had
76 * four 10-position rotary switches. Later hardware versions had
77 * two 9-position (0..7 and off) rotary switches.
78 *
79 * Config panel -- Level 68 6000 SCU
80 * -- from AM81
81 * store A and store B
82 * 3 position rotary switch: on line, maint, off line
83 * size: 32K, 64K, 128K, 256K
84 * exec interrupt mask assignment
85 * four 10-position rotary switches (A through D): off, 0 .. 7, M
86 * One switch for each program interrupt register
87 * Assign mask registers to system ports
88 * Normally assign one mask reg to each CPU
89 *
90 * AM81:
91 * " The EXECUTE INTERRUPT MASK ASSIGNMENT (EIMA) rotary switches
92 * determine where interrupts sent to memory are directed. The four EIMA
93 * rotary switches, one for each program interrupt register, are used to
94 * assign mask registers to system ports. The normal settings assign one
95 * mask register to each CPU configured.
96 *
97 * Each switch assigns mask registers as follows:
98 *
99 * Position
100 * OFF Unassigned
101 * 0 Assigned to port 0
102 * ...
103 * 7 Assigned to port 7
104 * M Assigned to maintenance panel
105 *
106 * Assignment of a mask register to a system port designates the
107 * port as a control port, and that port receives interrupt present
108 * signals. Up to four system ports can be designated as control
109 * ports. The normal settings assign one mask register to each CPU
110 * configured."
111 *
112 *
113 *
114 * Config panel -- Level 68 System Controller UNIT (4MW SCU)
115 * -- from AM81
116 * Store A, A1, B, B1 (online/offline)
117 * LWR Store Size
118 * PORT ENABLE
119 * Eight on/off switches
120 * Should be on for each port connected to a configured CPU
121 * mask/port assignment
122 * Two rotary switches (A & B); set to (off, 0..7)
123 * See EXEC INTERRUPT on the 6000 SCU
124 * When booting, one should be set to the port connected to
125 * the bootload CPU. The other should be off.
126 *
127 * If memory port B of CPU C goes to SCU D, then memory port B of all
128 * other CPUs *and* IOMs must go to SCU D. -- AN70, 8-4.
129 *
130 * The base address of the SCU is the actual memory size * the port
131 * assignment. -- AN70, 8-6.
132 *
133 * 43A239854 6000B Eng. Prod. Spec, 3.2.7 Interrupt Multiplex Word:
134 * "The IOM has the ability to set any of the 32 program interrupt
135 * cells located in the system controller containing the base address
136 * of the IOM. It should be noted that for any given IOM identity
137 * switch setting, the IOM can set only 8 of these program interrupt
138 * cells."
139 *
140 */
141
142 /*
143 * === Initialization and Booting -- Part 1 -- Operator's view
144 *
145 * Booting Instructions (GB61)
146 * First boot the BCE OS (Bootload command Environment). See below.
147 * A config deck is used
148 * Bootload SCU is the one with a base addr of zero.
149 * BCE is on a BCE/Multics System tape
150 * Booted from tape into the system via bootload console
151
152 */
153
154 /*
155 * 58009906 (DPS8)
156 * When CPU needs to address the SCU (for a write/read data cycle,
157 * for example), the ETMCM board int the CU of the CPU issues a $INT
158 * to the SCU. This signal is sent ... to the SCAMX active port
159 * control board in the SCU
160 */
161
162 // How? If one of the 32 interrupt cells is set in one of the SCs,
163 // our processor will have the interrupt present (XIP) line active.
164 // Perhaps faults are flagged in the same way via the SXC system
165 // controller command.
166
167 // TEMPORARY
168 // Each SCU owns a certain range of absolute memory.
169 // CPUs use the cioc instr to talk to IOMs and other CPUs via a SCU.
170 // IOMs use interrupts to ask a SCU to signal a CPU.
171 // read system controller reg and set system controller reg (rscr & sscr)
172 // Bootload SCU is the one with a base addr of zero.
173 // 58009906
174 // When CPU needs to address the SCU (for a write/read data cycle,
175 // for example), the ETMCM board int the CU of the CPU issues a $INT
176 // to the SCU. This signal is sent ... to the SCAMX active port
177 // control board in the
178 // -----------------------
179 // How? If one of the 32 interrupt cells is set in one of the SCs,
180 // our processor will have the interrupt present (XIP) line active.
181 // Perhaps faults are flagged in the same way via the SXC system
182 // controller command.
183
184 /*
185 * *** More (new) notes ***
186 *
187 * instr rmcm -- read mem controller mask register
188 * ... for the selected controller, if the processor has a mask register
189 * assigned ..
190 * instr smcm -- set mem controller mask register
191 * ... for the selected controller, if the processor has a mask register
192 * assigned, set it to C(AQ)
193 * instr smic
194 * turn on interrupt cells (any of 0..31)
195 * instr cioc -- connect i/o channel, pg 173
196 * SC addressed by Y sends a connect signal to the port specified
197 * by C(Y)33,35
198 * instr rscr & sscr -- Read/Store System Controller Register, pg 170
199 *
200 * 32 interrupt cells ... XIP
201 * mask info
202 * 8 mask registers
203 * 58009906
204 * =============
205 *
206 * AM81
207 * Every active device (CPU, IOM) must be able to access all SCUs
208 * Every SCU must have the same active device on the same SCU, so
209 * all SCUs must have the same PORT ENABLE settings
210 * Every active device must have the same SCU on the same port,
211 * so all active devices will have the same config panel settings.
212 * Ports must correspond -- port A on every CPU and IOM must either
213 * be connected to the same SCU or not connected to any SCU.
214 * IOMs should be on lower-numbered SCU ports than CPUs.
215 * Multics can have 16MW words of memory.
216 * CPUs have 8 ports, a..h.
217 * SCUs have 8 ports, 0..7.
218 *
219 *
220 * Level 68 6000 SCU Configuration Panel
221 * system control and monitor (cont&mon/mon/off)
222 * system boot control (on/off)
223 * alarm (disable/normal)
224 * maintenance panel mode (test/normal)
225 * store a
226 * mode (offline/maint/online)
227 * size (32k, 64k, 128k, 256k)
228 * store b
229 * mode (offline/maint/online)
230 * size (32k, 64k, 128k, 256k)
231 * execute interrupt mask assignment
232 * (A through D; off/0/1/2/3/4/5/6/7/m)
233 * [CAC] I interpret this as CPU [A..D] is connected to my port [0..7]
234 * address control
235 * lower store (a/b)
236 * offset (off, 16k, 32k, 64k)
237 * interlace (on/off)
238 * cycle port priority (on/off)
239 * port control (8 toggles) (enabled/prog cont/disable)
240 *
241 * The EXECUTE INTERRUPT MASK ASSIGNMENT (EIMA) rotary switches
242 * determine where interrupts sent to memory are directed. The four EIMA
243 * rotary switches, one for each program interrupt register, are used to
244 * assign mask registers to system ports. The normal settings assign one
245 * mask register to each CPU configured.
246 *
247 * Assignment of a mask register to a system port designates the port as a
248 * control port, and that port receives interrupt present signals. Up to four
249 * system ports can be designated as control ports. The normal settings
250 * assign one mask register to each cpu configured.
251 *
252 *
253 *
254 * Configuration rules for Multics:
255 *
256 * 1. Each CPU in the system must be connected to each SCU in the system
257 *
258 * 2. Each IOM in the system must be connected to each SCU in the system
259 *
260 * 3. Each SCU in the system must be connected to every CPU and IOM in the
261 * system.
262 *
263 * 4. Corresponding ports on all CPUs and IOMs must be connected to the same
264 * SCU. For example, port A on every CPU and IOM must be connected to the
265 * same SCU or not connected to any SCU.
266 *
267 * 5. Corresponding ports on all SCUs must be connected to the same active
268 * device (CPU or IOM). For example, if port 0 on any SCU is connected to
269 * IOM A, then port 0 on all SCUs must be connected to IOM A.
270 *
271 * 6. IOMs should be connected to lower-number SCU ports the CPUs.
272 *
273 * These rules are illustrated in Figure 3-5, where the port numbers for a
274 * small Multics system of 2 CPUS, 3 SCUs and 2 IOMs have been indicated
275 *
276 *
277 *
278 *
279 * ----------------- -----------------
280 * | | | |
281 * | CPU A | | CPU B |
282 * | | | |
283 * ----------------- -----------------
284 * | A | B | C | D | | A | B | C | D |
285 * ----------------- -----------------
286 * | | | | | |
287 * | | | | | -----------------
288 * | | | | | |
289 * | | -------------------------------)---)---------------- |
290 * | | | | | |
291 * -------------------- ----------------- | | | |
292 * | | | | | |
293 * | -----------------------------------)------------------- | | |
294 * | | | | | |
295 * | | | -------------------- | |
296 * | | | | | |
297 * ----------------- ----------------- -----------------
298 * | 7 | 6 | 5 | 4 | | 7 | 6 | 5 | 4 | | 7 | 6 | 5 | 4 |
299 * ----------------- ----------------- -----------------
300 * | | | | | |
301 * | SCU C | | SCU B | | SCU A |
302 * | | | | | |
303 * ----------------- ----------------- -----------------
304 * | 3 | 2 | 1 | 0 | | 3 | 2 | 1 | 0 | | 3 | 2 | 1 | 0 |
305 * ----------------- ----------------- -----------------
306 * | | | | | |
307 * | | | ----------- | |
308 * | | | | | |
309 * | -----------------------------------)--------- | | |
310 * | | | | | |
311 * ---------- -------------------------- | | | |
312 * | | | | | |
313 * | | ------------------------------)----)------------------------- |
314 * | | | | | |
315 * | | | | | ---------------------------
316 * | | | | | |
317 * ----------------- -----------------
318 * | A | B | C | D | | A | B | C | D |
319 * ----------------- -----------------
320 * | | | |
321 * | IOM A | | IOM B |
322 * | | | |
323 * ----------------- -----------------
324 *
325 *
326 *
327 *"During bootload, Multics requires a contiguous section of memory beginning at
328 * absolute address 0 and sufficiently large to contain all routines and data
329 * structures used during the first phase of Multics initialization (i.e.
330 * collection 1).
331 * The size of the section required varies among Multics release, and it also
332 * depends on the size of the SST segment, which is dependent on the parameters
333 * specified by the site on the SST config card. ... However
334 * 512 KW is adequate for all circumstances. There can be no "holes" in memory
335 * within this region. Beyond this region, "holes" can exist in memory."
336 *
337 *
338 */
339
340 /*
341 * From AN70-1 May84, pg 86 (8-6)
342 *
343 * RSCR SC_CFG bits 9-11 lower store size
344 *
345 * A DPS-8 SCU may have up to four store units attached to it. If this
346 * is the case, two store units form a pair of units. The size of a
347 * pair of units (or a single unit) is 32K * 2 ** (lower store size)
348 * above.
349 */
350
351 /*
352 * From AN70-1 May84, pg 86 (8-6)
353 *
354 * SCU ADDRESSING
355 *
356 * There are three ways in which an SCU is addressed. In the
357 * normal mode of operation (memory reading and writing), an active
358 * unit (IOM or CPU) translates an absolute address into a memory
359 * port (on it) and a relative memory address within the memory
360 * described by the memory port. The active module sends the
361 * address to the SCU on the proper memory port. If the active
362 * module is enabled by the port enable mask in the referenced SCU,
363 * the SCU will take the address given to it and provide the
364 * necessary memory access.
365 *
366 * The other two ways pertain to reading/setting control
367 * registers in the SCU itself. For each of these, it is still
368 * necessary to specify somehow the memory port on the CPU whose SCU
369 * registers are desired. For the RMCM, SMCM and SMIC instructions,
370 * this consists of providing a virtual address to the processor for
371 * which bits 1 and 2 are the memory port desired.
372 *
373 * The rscr and sscr instructions, though key off the final
374 * absolute address to determine the SCI (or SCU store unit)
375 * desired. Thus, software needs a way to translate a memory port
376 * number into an absolute address to reach the SCU. This is done
377 * with the paged segment scas, generated by int_scas (and
378 * init_scu). scas has a page corresponding to each SCU and to each
379 * store unit in each SCU. pmut$rscr and pmut$sscr use the memory
380 * port number desired to generate a virtual address into scas whose
381 * absolute address (courtesy of the ptws for sca) just happen to
382 * describe memory within that SCU.
383 *
384 * The cioc instruction (discussed below) also depends on the
385 * final absolute address of the target operand to identify the SCU
386 * to perform the operation. In the case of the cioc instruction,
387 * though, the has no particular impact in Multics software. All
388 * target operands for the cioc instruction when referencing IOMs
389 * are in the low order SCU. When referencing CPUS, the SCU
390 * performing the connecting has no real bearing.
391 *
392 * Inter-module communication
393 *
394 * As mentioned earlier, communication between active modules
395 * (CPUs and IOMs can only be performed through SCUs.
396 *
397 * CPUs communicate to IOMs and other CPUs via the cioc
398 * (connect i/o channel) instruction. The operand of the instruction
399 * is a word in memory. The SCU containing this operand is the SCU
400 * that performs the connect function. The word fetched from memory
401 * contains in its low order bits the identity of a port on the SCU
402 * to which this connection is to be sent. This only succeeds if the
403 * target port is enabled (port enable mask) on the SCU. When the
404 * target of the connection is an IOM; this generates a connect strobe
405 * to the IOM. The IOM examines its mailbox in memory to determine
406 * its course of action. When the target of the connect is another
407 * CPU, this generates a connect fault in the target processor. The
408 * target processor determines what course to follow on the basis
409 * of information in memory analyzed by software. When a connect is
410 * sent to a process (including the processor issuing the connect),
411 * the connect is deferred until the processor stops
412 * executing inhibited code (instructions with the inhibit bit set).
413 *
414 * Signals sent from an IOM to a CPU are much more involved.
415 * The basic flow is as follows. The IOM determines an interrupt
416 * number. (The interrupt number is a five bit value, from 0 to 31.
417 * The high order bits are the interrupt level.
418 *
419 * 0 - system fault
420 * 1 - terminate
421 * 2 - marker
422 * 3 - special
423 *
424 * The low order three bits determines the IOM and IOM channel
425 * group.
426 *
427 * 0 - IOM 0 channels 32-63
428 * 1 - IOM 1 channels 32-63
429 * 2 - IOM 2 channels 32-63
430 * 3 - IOM 3 channels 32-63
431 * 4 - IOM 0 channels 0-31
432 * 5 - IOM 1 channels 0-31
433 * 6 - IOM 2 channels 0-31
434 * 7 - IOM 3 channels 0-31
435 *
436 * It also takes the channel number in the group (0-31 meaning
437 * either channels 0-31 to 32-63) and sets the <channel number>th
438 * bit in the <interrupt number>th memory location in the interrupt
439 * mask word (IMW) array in memory. It then generates a word with
440 * the <interrupt number>th bit set and sends this to the bootload
441 * SCU with the SC (set execute cells) SCU command. This sets the
442 * execute interrupt cell register in the SCU and sends an XIP
443 * (execute interrupt present) signal to various processors
444 * connected to the SCU. (The details of this are covered in the
445 * next section.) One of the processors (the first to get to it)
446 * sends an XEC (execute interrupt cells) SCU command to the SCU who
447 * generated the XIP signal. The SCU provides the interrupt number
448 * to the processor, who uses it to determine the address of a fault
449 * pair in memory for the "fault" caused by this interrupt. The
450 * processing of the XEC command acts upon the highest priority
451 * (lowest number) bit in the execute interrupt cell register, and
452 * also resets this bit in the register.
453 *
454 * Interrupts Masks and Assignment
455 *
456 * The mechanism for determining which processors are candidates
457 * for receiving an interrupt from an IOM is an involved
458 * topic. First of all, a processor will not be interrupted as long
459 * as it is executing inhibited instructions (instructions with the
460 * inhibit bit set). Beyond this, though, lies the question of
461 * interrupt masks and mask assignment.
462 *
463 * Internal to the SCU are two sets of registers (A and B),
464 * each set consisting of the execute interrupt mask register and
465 * the interrupt mask assignment register. Each execute interrupt
466 * mask register is 32 bits long, with each bit enabling the
467 * corresponding bit in the execute interrupt cell register. Each
468 * interrupt mask assignment register has two parts, an assigned bit
469 * and a set of ports to which it is assigned (8 bits). When a bit
470 * is set in the execute interrupt sells register, the SCU ANDs this
471 * bit with the corresponding bit in each of the execute interrupt
472 * mask registers. If the corresponding bit of execute interrupt
473 * mask register A, for example, is on, the SCU then looks at the A
474 * interrupt mask assignment register. If this register is not
475 * assigned (enable), no further action takes place in regards to
476 * the A registers. (The B registers are still considered) (in
477 * parallel, by the way).) If the register is assigned (enabled)
478 * then interrupts will be send to all ports (processors) whose
479 * corresponding bit is set in the interrupt mask assignment
480 * register. This, only certain interrupts are allowed to be
481 * signalled at any given time (base on the contents of the execute
482 * interrupt mask registers) and only certain processors will
483 * receive these interrupts (as controlled by the interrupt mask
484 * assignment registers).
485 *
486 * In Multics, only one processor is listed in each of the two
487 * interrupt mask assignment registers, and no processor appears in
488 * both. Thus there is a one for one correspondence between
489 * interrupt masks that are assigned (interrupt mask registers whose
490 * assigned (enabled) bit is on) and processors who have an
491 * interrupt mask (SCU port number appears in an interrupt mask
492 * register). So, at any one time only two processors
493 * are eligible to receive interrupts. Other processors need not
494 * worry about masking interrupts.
495 *
496 * The contents of the interrupt mask registers may be
497 * obtained with the SCU configuration information with the rscr
498 * instruction and set with the sscr instruction.
499 *
500 * bits meaning
501 *
502 * 00-07 ports assigned to mask A (interrupt mask assignment A)
503 * 08-08 mask A is unassigned (disabled)
504 * 36-43 ports assigned to mask B (interrupt mask assignment B)
505 * 44-44 mask B is unassigned (disabled)
506 *
507 * The contents of a execute interrupt mask register are
508 * obtained with the rmcm or the rscr instruction and set with the
509 * smcm or the sscr instruction. The rmcm and smcm instruction only
510 * work if the processor making the request has a mask register
511 * assigned to it. If not, rmcm returns zero (no interrupt are
512 * enabled to it) and a smcm is ignored (actually the port mask
513 * setting is still done). The rscr and sscr instructions allow the
514 * examining/setting of the execute interrupt mask register for any
515 * port on a SCU; these have the same effect as smcm and rmcm if the
516 * SCU port being referenced does not have a mask assigned to it.
517 * The format of the data returned by these instructions is as
518 * follows.
519 *
520 * bits meaning
521 * 00-15 execute interrupt mask register 00-15
522 * 32-35 SCU port mask 0-3
523 * 36-51 execute interrupt mask register 16-31
524 * 68-71 SCU port mask 4-7
525 *
526 */
527
528 // SCU numbering:
529 //
530 // AM81-04, pg 49: "... the ports are listed in order of increasing base
531 // address, which corresponds to the order of mem config cards."
532 // pg 97: "mem port size state ... port as a value (a through h) that
533 // corresponds to the number of the active module port to which the
534 // system controller is connected.
535 //
536 // From this, I conclude;
537 // The SCU connected to port A (0) is SCUA, 1 B, 2 C, etc.
538 // SCUA starts at address 0, and the SCUs are sorted by increasing addresses.
539 //
540
541 // ============================================================================
542
543 #include <sys/time.h>
544 #include "dps8.h"
545 #include "dps8_sys.h"
546 #include "dps8_iom.h"
547 #include "dps8_cable.h"
548 #include "dps8_cpu.h"
549 #include "dps8_faults.h"
550 #include "dps8_scu.h"
551 #include "dps8_utils.h"
552 #if defined(THREADZ) || defined(LOCKLESS)
553 # include "threadz.h"
554 #endif
555
556 #define DBG_CTR 1
557
558 scu_t scu [N_SCU_UNITS_MAX];
559
560 #define N_SCU_UNITS 1 // Default
561
562 static UNIT scu_unit [N_SCU_UNITS_MAX] = {
563 #if defined(NO_C_ELLIPSIS)
564 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
565 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
566 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
567 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
568 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
569 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
570 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
571 { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
572 #else
573 [0 ... N_SCU_UNITS_MAX-1] = {
574 UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
575 }
576 #endif
577 };
578
579 #define UNIT_NUM(uptr) ((uptr) - scu_unit)
580
581 // Hardware configuration switches
582
583 // sscr and other instructions override these settings
584
585 static struct config_switches
586 {
587 uint mode; // program or manual
588 uint port_enable [N_SCU_PORTS]; // enable/disable
589 uint mask_enable [N_ASSIGNMENTS]; // enable/disable
590 uint mask_assignment [N_ASSIGNMENTS]; // assigned port number
591 uint lower_store_size; // In K words, power of 2; 32 - 4096
592 uint cyclic; // 7 bits
593 uint nea; // 8 bits
594 uint onl; // 4 bits
595 uint interlace; // 1 bit
596 uint lwr; // 1 bit
597 } config_switches [N_SCU_UNITS_MAX];
598
599 enum { MODE_MANUAL = 0, MODE_PROGRAM = 1 };
600
601 unsigned int gtod_warned = 0;
602
603 // ============================================================================
604
605 static t_stat scu_set_state (UNUSED UNIT * uptr, UNUSED int32 value,
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
606 const char * cptr, UNUSED void * desc)
607 {
608 sim_printf("Cannot modify STATE; try SET SCUn CONFIG%s%s.\r\n",
609 cptr ? "=" : "", cptr ? cptr : "");
610 return SCPE_ARG;
611 }
612
613 static t_stat scu_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
614 UNUSED int val, const UNUSED void * desc)
615 {
616 sim_printf("Number of SCU units in system is %d\r\n", scu_dev.numunits);
617 return SCPE_OK;
618 }
619
620 static t_stat scu_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
621 const char * cptr, UNUSED void * desc)
622 {
623 if (! cptr)
624 return SCPE_ARG;
625 int n = atoi (cptr);
626 if (n < 1 || n > N_SCU_UNITS_MAX)
627 return SCPE_ARG;
628 scu_dev.numunits = (uint) n;
629 return SCPE_OK;
630 }
631
632 static t_stat scu_show_state (UNUSED FILE * st, UNIT *uptr, UNUSED int val,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
633 UNUSED const void * desc)
634 {
635 #if defined(TESTING)
636 cpu_state_t * cpup = _cpup;
637 #endif
638 long scu_unit_idx = UNIT_NUM (uptr);
639 if (scu_unit_idx < 0 || scu_unit_idx >= (int) scu_dev.numunits)
640 {
641 sim_debug (DBG_ERR, & scu_dev,
642 "scu_show_state: Invalid unit number %ld\r\n",
643 (long) scu_unit_idx);
644 sim_printf ("error: Invalid unit number %ld\r\n", (long) scu_unit_idx);
645 return SCPE_ARG;
646 }
647
648 sim_printf ("SCU unit number %ld\r\n", (long) scu_unit_idx);
649 scu_t * scup = scu + scu_unit_idx;
650 sim_printf (" Mode %s\r\n",
651 config_switches[scu_unit_idx].mode ? "PROGRAM" : "MANUAL");
652
653 for (int i = 0; i < N_SCU_PORTS; i ++)
654 {
655 struct ports * pp = scup -> ports + i;
656
657 sim_printf (" Port %d %s dev_idx %d dev_port %d type %s\r\n",
658 i, scup->port_enable[i] ? "ENABLE " : "DISABLE",
659 pp->dev_idx, pp->dev_port[XXX_TEMP_SCU_SUBPORT],
660 pp->type == ADEV_NONE ? "NONE" :
661 pp->type == ADEV_CPU ? "CPU" :
662 pp->type == ADEV_IOM ? "IOM" :
663 "<enum broken>");
664 }
665 for (int i = 0; i < N_ASSIGNMENTS; i ++)
666 {
667 //struct interrupts * ip = scup -> interrupts + i;
668
669 sim_printf (" Cell %c\r\n", 'A' + i);
670 sim_printf (" exec_intr_mask %012o\r\n",
671 scup -> exec_intr_mask [i]);
672 sim_printf (" mask_enable %s\r\n",
673 scup -> mask_enable [i] ? "ENABLE" : "DISABLE");
674 sim_printf (" mask_assignment %d\r\n",
675 scup -> mask_assignment [i]);
676 sim_printf (" cells ");
677 for (int j = 0; j < N_CELL_INTERRUPTS; j ++)
678 sim_printf("%d", scup -> cells [j]);
679 sim_printf ("\r\n");
680 }
681 sim_printf("Lower store size: %d\r\n", scup -> lower_store_size);
682 sim_printf("Cyclic: %03o\r\n", scup -> cyclic);
683 sim_printf("NEA: %03o\r\n", scup -> nea);
684 sim_printf("Online: %02o\r\n", scup -> onl);
685 sim_printf("Interlace: %o\r\n", scup -> interlace);
686 sim_printf("Lower: %o\r\n", scup -> lwr);
687 sim_printf("ID: %o\r\n", scup -> id);
688 sim_printf("mode_reg: %06o\r\n", scup -> mode_reg);
689 sim_printf("Elapsed days: %d\r\n", scup -> elapsed_days);
690 sim_printf("Steady clock: %d\r\n", scup -> steady_clock);
691 sim_printf("Bullet time: %d\r\n", scup -> bullet_time);
692 sim_printf("Clock delta: %lld\r\n", (long long)scup -> clock_delta);
693 return SCPE_OK;
694 }
695
696 static t_stat scu_show_config (UNUSED FILE * st, UNUSED UNIT * uptr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
697 UNUSED int val, UNUSED const void * desc)
698 {
699 #if defined(TESTING)
700 cpu_state_t * cpup = _cpup;
701 #endif
702 static const char * map [N_SCU_PORTS] =
703 {
704 "0", "1", "2", "3", "4", "5", "6", "7"
705 };
706 long scu_unit_idx = UNIT_NUM (uptr);
707 if (scu_unit_idx < 0 || scu_unit_idx >= (int) scu_dev.numunits)
708 {
709 sim_debug (DBG_ERR, & scu_dev,
710 "scu_show_config: Invalid unit number %ld\r\n",
711 (long) scu_unit_idx);
712 sim_printf ("error: Invalid unit number %ld\r\n", (long) scu_unit_idx);
713 return SCPE_ARG;
714 }
715
716 sim_printf ("SCU unit number %ld\r\n", (long) scu_unit_idx);
717
718 struct config_switches * sw = config_switches + scu_unit_idx;
719
720 const char * mode = "<out of range>";
721 switch (sw -> mode)
722 {
723 case MODE_PROGRAM:
724 mode = "Program";
725 break;
726 case MODE_MANUAL:
727 mode = "Manual";
728 break;
729 }
730
731 sim_printf ("Mode: %s\r\n", mode);
732 sim_printf ("Port Enable: ");
733 for (int i = 0; i < N_SCU_PORTS; i ++)
734 sim_printf (" %3o", sw -> port_enable [i]);
735 sim_printf ("\r\n");
736 for (int i = 0; i < N_ASSIGNMENTS; i ++)
737 {
738 sim_printf ("Mask %c: %s\r\n",
739 'A' + i,
740 sw->mask_enable[i] ? (map[sw->mask_assignment[i]]) : "Off");
741 }
742 sim_printf ("Lower Store Size: %o\r\n", sw -> lower_store_size);
743 sim_printf ("Cyclic: %03o\r\n", sw -> cyclic);
744 sim_printf ("Non-existent address: %03o\r\n", sw -> nea);
745
746 return SCPE_OK;
747 }
748
749 //
750 // set scu0 config=<blah> [;<blah>]
751 //
752 // blah =
753 // mode= manual | program
754 // mask[A|B] = off | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
755 // portN = enable | disable
756 // lwrstoresize = 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096
757 // cyclic = n
758 // nea = n
759 //
760 // o nea is not implemented; will read as "nea off"
761 // o Multics sets cyclic priority explicitly; config
762 // switches are ignored.
763 // o STORE A, A1, B, B1 ONLINE/OFFLINE not implemented;
764 // will always read online.
765 // o store size if not enforced; a full memory complement
766 // is provided.
767 // o interlace not implemented; will read as 'off'
768 // o LOWER STORE A/B not implemented.
769 // o MASK is 'MASK/PORT ASSIGNMENT' analogous to the
770 // 'EXECUTE INTERRUPT MASK ASSIGNMENT of a 6000 SCU
771
772 static config_value_list_t cfg_mode_list [] =
773 {
774 { "manual", 0 },
775 { "program", 1 },
776 { NULL, 0 }
777 };
778
779 static config_value_list_t cfg_mask_list [] =
780 {
781 { "off", -1 },
782 { NULL, 0 }
783 };
784
785 static config_value_list_t cfg_able_list [] =
786 {
787 { "disable", 0 },
788 { "enable", 1 },
789 { NULL, 0 }
790 };
791
792 static config_value_list_t cfg_size_list [] =
793 {
794 { "32", 0 },
795 { "64", 1 },
796 { "128", 2 },
797 { "256", 3 },
798 { "512", 4 },
799 { "1024", 5 },
800 { "2048", 6 },
801 { "4096", 7 },
802 { "32K", 0 },
803 { "64K", 1 },
804 { "128K", 2 },
805 { "256K", 3 },
806 { "512K", 4 },
807 { "1024K", 5 },
808 { "2048K", 6 },
809 { "4096K", 7 },
810 { "1M", 5 },
811 { "2M", 6 },
812 { "4M", 7 },
813 { NULL, 0 }
814 };
815
816 static config_value_list_t cfg_on_off [] =
817 {
818 { "off", 0 },
819 { "on", 1 },
820 { "disable", 0 },
821 { "enable", 1 },
822 { NULL, 0 }
823 };
824
825 static config_list_t scu_config_list [] =
826 {
827 /* 0 */ { "mode", 1, 0, cfg_mode_list },
828 /* 1 */ { "maska", 0, N_SCU_PORTS - 1, cfg_mask_list },
829 /* 2 */ { "maskb", 0, N_SCU_PORTS - 1, cfg_mask_list },
830 /* 3 */ { "port0", 1, 0, cfg_able_list },
831 /* 4 */ { "port1", 1, 0, cfg_able_list },
832 /* 5 */ { "port2", 1, 0, cfg_able_list },
833 /* 6 */ { "port3", 1, 0, cfg_able_list },
834 /* 7 */ { "port4", 1, 0, cfg_able_list },
835 /* 8 */ { "port5", 1, 0, cfg_able_list },
836 /* 9 */ { "port6", 1, 0, cfg_able_list },
837 /* 10 */ { "port7", 1, 0, cfg_able_list },
838 /* 11 */ { "lwrstoresize", 0, 7, cfg_size_list },
839 /* 12 */ { "cyclic", 0, 0177, NULL },
840 /* 13 */ { "nea", 0, 0377, NULL },
841 // mask: 8 a_online, 4 a1_online, 2 b_online, 1, b1_online
842 /* 14 */ { "onl", 0, 017, NULL },
843 /* 15 */ { "int", 0, 1, NULL },
844 /* 16 */ { "lwr", 0, 1, NULL },
845
846 // Hacks
847
848 /* 17 */ { "elapsed_days", 0, 20000, NULL },
849 /* 18 */ { "steady_clock", 0, 1, cfg_on_off },
850 /* 19 */ { "bullet_time", 0, 1, cfg_on_off },
851 /* 20 */ { "clock_delta", -2147483648, 2147483647, NULL },
852 { NULL, 0, 0, NULL }
853 };
854
855 static t_stat scu_set_config (UNIT * uptr, UNUSED int32 value,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
856 const char * cptr, UNUSED void * desc)
857 {
858 #if defined(TESTING)
859 cpu_state_t * cpup = _cpup;
860 #endif
861 long scu_unit_idx = UNIT_NUM (uptr);
862 if (scu_unit_idx < 0 || scu_unit_idx >= (int) scu_dev.numunits)
863 {
864 sim_debug (DBG_ERR, & scu_dev,
865 "scu_set_config: Invalid unit number %ld\r\n", (long) scu_unit_idx);
866 sim_printf ("error: scu_set_config: Invalid unit number %ld\r\n",
867 (long) scu_unit_idx);
868 return SCPE_ARG;
869 }
870
871 struct config_switches * sw = config_switches + scu_unit_idx;
872
873 config_state_t cfg_state = { NULL, NULL };
874
875 for (;;)
876 {
877 int64_t v;
878 int rc = cfg_parse ("scu_set_config", cptr, scu_config_list,
879 & cfg_state, & v);
880 if (rc == -1) // done
881 break;
882
883 if (rc == -2) // error
884 {
885 cfg_parse_done (& cfg_state);
886 return SCPE_ARG;
887 }
888
889 const char * p = scu_config_list [rc].name;
890 if (strcmp (p, "mode") == 0)
891 sw -> mode = (uint) v;
892 else if (strcmp (p, "maska") == 0)
893 {
894 if (v == -1)
895 sw -> mask_enable [0] = false;
896 else
897 {
898 sw -> mask_enable [0] = true;
899 sw -> mask_assignment [0] = (uint) v;
900 }
901 }
902 else if (strcmp (p, "maskb") == 0)
903 {
904 if (v == -1)
905 sw -> mask_enable [1] = false;
906 else
907 {
908 sw -> mask_enable [1] = true;
909 sw -> mask_assignment [1] = (uint) v;
910 }
911 }
912 else if (strcmp (p, "port0") == 0)
913 sw -> port_enable [0] = (uint) v;
914 else if (strcmp (p, "port1") == 0)
915 sw -> port_enable [1] = (uint) v;
916 else if (strcmp (p, "port2") == 0)
917 sw -> port_enable [2] = (uint) v;
918 else if (strcmp (p, "port3") == 0)
919 sw -> port_enable [3] = (uint) v;
920 else if (strcmp (p, "port4") == 0)
921 sw -> port_enable [4] = (uint) v;
922 else if (strcmp (p, "port5") == 0)
923 sw -> port_enable [5] = (uint) v;
924 else if (strcmp (p, "port6") == 0)
925 sw -> port_enable [6] = (uint) v;
926 else if (strcmp (p, "port7") == 0)
927 sw -> port_enable [7] = (uint) v;
928 else if (strcmp (p, "lwrstoresize") == 0)
929 sw -> lower_store_size = (uint) v;
930 else if (strcmp (p, "cyclic") == 0)
931 sw -> cyclic = (uint) v;
932 else if (strcmp (p, "nea") == 0)
933 sw -> nea = (uint) v;
934 else if (strcmp (p, "onl") == 0)
935 sw -> onl = (uint) v;
936 else if (strcmp (p, "int") == 0)
937 sw -> interlace = (uint) v;
938 else if (strcmp (p, "lwr") == 0)
939 sw -> lwr = (uint) v;
940 else if (strcmp (p, "elapsed_days") == 0)
941 scu [scu_unit_idx].elapsed_days = (uint) v;
942 else if (strcmp (p, "steady_clock") == 0)
943 scu [scu_unit_idx].steady_clock = (uint) v;
944 else if (strcmp (p, "bullet_time") == 0)
945 scu [scu_unit_idx].bullet_time = (uint) v;
946 else if (strcmp (p, "clock_delta") == 0)
947 scu [scu_unit_idx].clock_delta = (int64_t) v;
948 else
949 {
950 sim_printf ("error: scu_set_config: invalid cfg_parse rc <%d>\r\n",
951 rc);
952 cfg_parse_done (& cfg_state);
953 return SCPE_ARG;
954 }
955 } // process statements
956 cfg_parse_done (& cfg_state);
957 return SCPE_OK;
958 }
959
960 static MTAB scu_mod [] =
961 {
962 {
963 MTAB_XTD | MTAB_VUN | \
964 MTAB_NMO | MTAB_VALR, /* Mask */
965 0, /* Match */
966 (char *) "CONFIG", /* Print string */
967 (char *) "CONFIG", /* Match string */
968 scu_set_config, /* Validation routine */
969 scu_show_config, /* Display routine */
970 NULL, /* Value descriptor */
971 NULL /* Help */
972 },
973 {
974 MTAB_XTD | MTAB_VDV | \
975 MTAB_NMO | MTAB_VALR, /* Mask */
976 0, /* Match */
977 (char *) "NUNITS", /* Print string */
978 (char *) "NUNITS", /* Match string */
979 scu_set_nunits, /* Validation routine */
980 scu_show_nunits, /* Display routine */
981 (char *) "Number of SCU units in the system", /* Value descriptor */
982 NULL /* Help */
983 },
984 {
985 MTAB_XTD | MTAB_VUN | \
986 MTAB_NMO | MTAB_VALR, /* Mask */
987 0, /* Match */
988 (char *) "STATE", /* Print string */
989 (char *) "STATE", /* Match string */
990 scu_set_state, /* Validation routine */
991 scu_show_state, /* Display routine */
992 (char *) "SCU unit internal state", /* Value descriptor */
993 NULL /* Help */
994 },
995 {
996 MTAB_XTD | MTAB_VUN | \
997 MTAB_NMO | MTAB_VALR, /* Mask */
998 0, /* Match */
999 (char *) "RESET", /* Print string */
1000 (char *) "RESET", /* Match string */
1001 scu_reset_unit, /* Validation routine */
1002 NULL, /* Display routine */
1003 (char *) "reset SCU unit", /* Value descriptor */
1004 NULL /* Help */
1005 },
1006 {
1007 0, 0, NULL, NULL, NULL, NULL, NULL, NULL
1008 }
1009 };
1010
1011 //static t_stat scu_reset (DEVICE *dptr);
1012
1013 static DEBTAB scu_dt [] =
1014 {
1015 { (char *) "TRACE", DBG_TRACE, NULL },
1016 { (char *) "NOTIFY", DBG_NOTIFY, NULL },
1017 { (char *) "INFO", DBG_INFO, NULL },
1018 { (char *) "ERR", DBG_ERR, NULL },
1019 { (char *) "WARN", DBG_WARN, NULL },
1020 { (char *) "DEBUG", DBG_DEBUG, NULL },
1021 { (char *) "INTR", DBG_INTR, NULL }, // Don't move as it messes up DBG messages
1022 { (char *) "ALL", DBG_ALL, NULL },
1023 { NULL, 0, NULL }
1024 };
1025
1026 DEVICE scu_dev =
1027 {
1028 (char *) "SCU", /* Name */
1029 scu_unit, /* Units */
1030 NULL, /* Registers */
1031 scu_mod, /* Modifiers */
1032 N_SCU_UNITS, /* #Units */
1033 10, /* Address radix */
1034 8, /* Address width */
1035 1, /* Address increment */
1036 8, /* Data radix */
1037 8, /* Data width */
1038 NULL, /* Examine routine */
1039 NULL, /* Deposit routine */
1040 & scu_reset, /* Reset routine */
1041 NULL, /* Boot routine */
1042 NULL, /* Attach routine */
1043 NULL, /* Detach routine */
1044 NULL, /* Context */
1045 DEV_DEBUG, /* Flags */
1046 0, /* Debug control flags */
1047 scu_dt, /* Debug flag names */
1048 NULL, /* Memory size change */
1049 NULL, /* Logical name */
1050 NULL, /* Help */
1051 NULL, /* Attach_help */
1052 NULL, /* Help_ctx */
1053 NULL, /* Description */
1054 NULL /* End */
1055 };
1056
1057 static void dump_intr_regs (char * ctx, uint scu_unit_idx)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1058 {
1059 #if defined(TESTING)
1060 scu_t * up = scu + scu_unit_idx;
1061 cpu_state_t * cpup = _cpup;
1062
1063 sim_debug (DBG_DEBUG, & scu_dev,
1064 "%s A: mask %011o enable %o assignment %o\r\n",
1065 ctx, up -> exec_intr_mask [0], up -> mask_enable [0],
1066 up -> mask_assignment [0]);
1067 sim_debug (DBG_DEBUG, & scu_dev,
1068 "%s B: mask %011o enable %o assignment %o\r\n",
1069 ctx, up -> exec_intr_mask [1], up -> mask_enable [1],
1070 up -> mask_assignment [1]);
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118 #endif
1119 }
1120
1121 void scu_unit_reset (int scu_unit_idx)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1122 {
1123 scu_t * up = scu + scu_unit_idx;
1124 struct config_switches * sw = config_switches + scu_unit_idx;
1125
1126 for (int i = 0; i < N_SCU_PORTS; i ++)
1127 {
1128 up -> port_enable [i] = sw -> port_enable [i];
1129 }
1130
1131 for (int i = 0; i < N_ASSIGNMENTS; i ++)
1132 {
1133 up -> mask_enable [i] = sw -> mask_enable [i];
1134 up -> mask_assignment [i] = sw -> mask_assignment [i];
1135 }
1136 up -> lower_store_size = sw -> lower_store_size;
1137 up -> cyclic = sw -> cyclic;
1138 up -> nea = sw -> nea;
1139 up -> onl = sw -> onl;
1140 up -> interlace = sw -> interlace;
1141 up -> lwr = sw -> lwr;
1142
1143 // This is to allow the CPU reset to update the memory map. IAC clears the
1144 // attached SCUs; they clear the attached IOMs.
1145
1146 for (uint port_num = 0; port_num < N_SCU_PORTS; port_num ++)
1147 {
1148 struct ports * portp = & scu [scu_unit_idx].ports [port_num];
1149 if (portp->type != ADEV_IOM)
1150 continue;
1151 //if (! scu [scu_unit_idx].port_enable [scu_port_num])
1152 //continue;
1153 iom_unit_reset_idx ((uint) portp->dev_idx);
1154 }
1155
1156 // CAC - These settings were reversed engineer from the code instead
1157 // of from the documentation. In case of issues, try fixing these, not the
1158 // code.
1159
1160 for (int i = 0; i < N_ASSIGNMENTS; i ++)
1161 {
1162 // XXX Hack for t4d
1163 up -> exec_intr_mask [i] = 037777777777;
1164 }
1165 }
1166
1167 t_stat scu_reset (UNUSED DEVICE * dptr)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1168 {
1169 // On reset, instantiate the config switch settings
1170
1171 for (int scu_unit_idx = 0; scu_unit_idx < N_SCU_UNITS_MAX; scu_unit_idx ++)
1172 scu_unit_reset (scu_unit_idx);
1173 return SCPE_OK;
1174 }
1175
1176 // ============================================================================
1177
1178 #if defined(THREADZ) || defined(LOCKLESS)
1179 static pthread_mutex_t clock_lock = PTHREAD_MUTEX_INITIALIZER;
1180 #endif
1181
1182 // The SCU clock is 52 bits long; fits in t_uint64
1183 static uint64 set_SCU_clock (cpu_state_t * cpup, uint scu_unit_idx)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1184 {
1185 #if defined(THREADZ) || defined(LOCKLESS)
1186 pthread_mutex_lock (& clock_lock);
1187 #endif
1188
1189 // The emulator supports two clock models: steady and real
1190 // In steady mode the time of day is coupled to the instruction clock,
1191 // allowing reproducible behavior. In real, the clock is
1192 // coupled to the actual time-of-day.
1193
1194 if (scu [0].steady_clock)
1195 {
1196 // The is a bit of code that is waiting for 5000 ms; this
1197 // fools into going faster
1198 #if defined(NEED_128)
1199 uint128 big = construct_128 (0, cpu.instrCnt);
1200 // Sync up the clock and the TR; see wiki page "CAC 08-Oct-2014"
1201 //big *= 4u;
1202 big = lshift_128 (big, 2);
1203 if (scu [0].bullet_time)
1204 big = multiply_128 (big, construct_128 (0, 10000u));
1205
1206 //big += scu [0].elapsed_days * 1000000llu * 60llu * 60llu * 24llu;
1207 uint128 days = construct_128 (0, scu[0].elapsed_days);
1208 days = multiply_128 (days, construct_128 (0, 1000000));
1209 days = multiply_128 (days, construct_128 (0, 60 * 60 * 24));
1210 big = add_128 (big, days);
1211 #else
1212 __uint128_t big = cpu.instrCnt;
1213 // Sync up the clock and the TR; see wiki page "CAC 08-Oct-2014"
1214 big *= 4u;
1215 //big /= 100u;
1216 if (scu [0].bullet_time)
1217 big *= 10000;
1218
1219 big += scu [0].elapsed_days * 1000000llu * 60llu * 60llu * 24llu;
1220 #endif
1221
1222 // Boot time
1223
1224 // load_fnp is complaining that FNP core image is more than 5 years old; try
1225 // moving the 'boot time' back to MR12.3 release date. (12/89 according to
1226 // https://www.multicians.org/chrono.html
1227
1228 // date -d "1990-01-01 00:00:00 -9" +%s
1229 // 631184400
1230 // For debugging MR12.3 and earlier with steady_clock, uncomment --
1231 // uint64 UNIX_secs = 631184400;
1232
1233 // Otherwise, we'll use the current time as the steady_clock starting point --
1234 uint64 UNIX_secs = (uint64)time(NULL);
1235
1236 #if defined(NEED_128)
1237 uint64 UNIX_usecs = UNIX_secs * 1000000llu + big.l;
1238 #else
1239 uint64 UNIX_usecs = UNIX_secs * 1000000llu + (uint64) big;
1240 #endif
1241 // now determine uSecs since Jan 1, 1901 ...
1242 uint64 Multics_usecs = 2177452800000000llu + UNIX_usecs;
1243
1244 // The casting to uint show be okay; both are 64 bit, so if
1245 // user_correction is <0, it will come out in the wash ok.
1246 Multics_usecs += (uint64) scu [scu_unit_idx].user_correction;
1247
1248 // The get calendar clock function is guaranteed to return
1249 // different values on successive calls.
1250
1251 if (scu [scu_unit_idx].last_time >= Multics_usecs)
1252 {
1253 sim_debug (DBG_TRACE, & scu_dev, "finagle clock\r\n");
1254 Multics_usecs = scu [scu_unit_idx].last_time + 1;
1255 }
1256 scu [scu_unit_idx].last_time = Multics_usecs;
1257 goto done;
1258 }
1259
1260 // The calendar clock consists of a 52-bit register which counts
1261 // microseconds and is readable as a double-precision integer by a
1262 // single instruction from any central processor. This rate is in
1263 // the same order of magnitude as the instruction processing rate of
1264 // the GE-645, so that timing of 10-instruction subroutines is
1265 // meaningful. The register is wide enough that overflow requires
1266 // several tens of years; thus it serves as a calendar containing
1267 // the number of microseconds since 0000 GMT, January 1, 1901
1268 /// Secs from Jan 1, 1901 to Jan 1, 1970 - 2 177 452 800 Seconds
1269 /// uSecs from Jan 1, 1901 to Jan 1, 1970 - 2 177 452 800 000 000 uSeconds
1270
1271 struct timeval now;
1272 gettimeofday(& now, NULL);
1273
1274 if (scu [scu_unit_idx].clock_delta) // Apply clock_delta correction.
1275 now.tv_sec += scu [scu_unit_idx].clock_delta;
1276
1277 uint64 UNIX_secs = (uint64) now.tv_sec;
1278 uint64 UNIX_usecs = UNIX_secs * 1000000LL + (uint64) now.tv_usec;
1279
1280 static uint64 last_UNIX_usecs = 0;
1281 if ( (!sim_quiet) && (UNIX_usecs < last_UNIX_usecs))
1282 {
1283 if (gtod_warned < 11)
1284 {
1285 sim_warn ("\rHost clock went backwards %llu uS!\r\n",
1286 (unsigned long long)(last_UNIX_usecs - UNIX_usecs));
1287 gtod_warned++;
1288 }
1289 else if (gtod_warned == 11)
1290 {
1291 sim_warn ("\rHost clock went backwards %llu uS! Suppressing further warnings.\r\n",
1292 (unsigned long long)(last_UNIX_usecs - UNIX_usecs));
1293 gtod_warned++;
1294 }
1295 }
1296 last_UNIX_usecs = UNIX_usecs;
1297
1298 // now determine uSecs since Jan 1, 1901 ...
1299 uint64 Multics_usecs = 2177452800000000LL + UNIX_usecs;
1300
1301 // The casting to uint show be okay; both are 64 bit, so if
1302 // user_correction is <0, it will come out in the wash ok.
1303 Multics_usecs += (uint64) scu [scu_unit_idx].user_correction;
1304
1305 if (scu [scu_unit_idx].last_time >= Multics_usecs)
1306 Multics_usecs = scu [scu_unit_idx].last_time + 1;
1307 scu [scu_unit_idx].last_time = Multics_usecs;
1308
1309 done:
1310 #if defined(THREADZ) || defined(LOCKLESS)
1311 pthread_mutex_unlock (& clock_lock);
1312 #endif
1313
1314 return scu [scu_unit_idx].last_time;
1315 }
1316
1317 //static char pcellb [N_CELL_INTERRUPTS + 1];
1318 static char * pcells (uint scu_unit_idx, char * buf)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1319 {
1320 for (uint i = 0; i < N_CELL_INTERRUPTS; i ++)
1321 {
1322 if (scu [scu_unit_idx].cells [i])
1323 buf [i] = '1';
1324 else
1325 buf [i] = '0';
1326 }
1327 buf [N_CELL_INTERRUPTS] = '\0';
1328 return buf;
1329 }
1330
1331 // Either an interrupt has arrived on a port, or a mask register has
1332 // been updated. Bring the CPU up date on the interrupts.
1333
1334 // threadz notes:
1335 //
1336 // deliver_interrupts is called either from a CPU instruction or from
1337 // IOM set_general_interrupt on the IOM thread.
1338 //
1339 // potential race conditions:
1340 // CPU variables: XIP
1341 // SCU variables: cells, mask_enable, exec_intr_mask, mask assignment
1342
1343 // Always called with SCU lock set
1344
1345 static void deliver_interrupts (cpu_state_t * cpup, uint scu_unit_idx)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1346 {
1347 #if defined(TESTING)
1348 {
1349 cpu_state_t * cpup = _cpup;
1350 sim_debug (DBG_DEBUG, & scu_dev, "deliver_interrupts %o\r\n", scu_unit_idx);
1351 }
1352 #endif
1353 #if defined(THREADZ) || defined(LOCKLESS)
1354 atomic_thread_fence (memory_order_seq_cst);
1355 #endif /* defined(THREADZ) || defined(LOCKLESS) */
1356 for (uint cpun = 0; cpun < cpu_dev.numunits; cpun ++)
1357 {
1358 cpus[cpun].events.XIP[scu_unit_idx] = false;
1359 }
1360
1361 // If the CIOC generates marker and terminate interrupts, they will be posted simultaneously.
1362 // Since the interrupts are recognized by priority and terminate has a higher priority then
1363 // marker, if will be delivered first. The following code will deliver marker before terminate.
1364
1365 #if defined(REORDER)
1366 for (uint jnum = 0; jnum < N_CELL_INTERRUPTS; jnum ++)
1367 {
1368 static const uint reorder[N_CELL_INTERRUPTS] = {
1369 0, 1, 2, 3, 4, 5, 6, 7,
1370 16, 17, 18, 29, 20, 21, 22, 23,
1371 8, 9, 10, 11, 12, 13, 14, 15,
1372 25, 25, 26, 27, 28, 29, 30, 31 };
1373 uint inum = reorder[jnum];
1374 if (! scu [scu_unit_idx].cells [inum])
1375 continue; //
1376 sim_debug (DBG_DEBUG, & scu_dev, "trying to deliver %d\r\n", inum);
1377 sim_debug (DBG_INTR, & scu_dev,
1378 "scu %u trying to deliver %d\r\n", scu_unit_idx, inum);
1379
1380 for (uint pima = 0; pima < N_ASSIGNMENTS; pima ++) // A, B
1381 {
1382 //sim_debug (DBG_DEBUG, & scu_dev,
1383 // "trying inum %u pima %u enable %u\r\n"
1384 // , inum, pima, scu [scu_unit_idx].mask_enable [pima]);
1385 if (scu [scu_unit_idx].mask_enable [pima] == 0)
1386 continue;
1387 uint mask = scu [scu_unit_idx].exec_intr_mask [pima];
1388 uint port = scu [scu_unit_idx].mask_assignment [pima];
1389 //sim_debug (DBG_DEBUG, & scu_dev,
1390 // "mask %u port %u type %u cells %o\r\n",
1391 // mask, port, scu [scu_unit_idx].ports [port].type,
1392 // scu [scu_unit_idx].cells [inum]);
1393 if (scu [scu_unit_idx].ports [port].type != ADEV_CPU)
1394 continue;
1395 if ((mask & (1u << (31 - inum))) != 0)
1396 {
1397 uint sn = 0;
1398 if (scu[scu_unit_idx].ports[port].is_exp)
1399 {
1400 sn = (uint) scu[scu_unit_idx].ports[port].xipmaskval;
1401 if (sn >= N_SCU_SUBPORTS)
1402 {
1403 sim_warn ("XIP mask not set; defaulting to subport 0\r\n");
1404 sn = 0;
1405 }
1406 }
1407 if (! cables->scu_to_cpu[scu_unit_idx][port][sn].in_use)
1408 {
1409 sim_warn ("bad scu_unit_idx %u\r\n", scu_unit_idx);
1410 continue;
1411 }
1412 uint cpu_unit_udx = cables->scu_to_cpu[scu_unit_idx][port][sn].cpu_unit_idx;
1413 # if defined(THREADZ) || defined(LOCKLESS)
1414 // Need to do this before setting XIP to avoid race condition
1415 // We are about to interrupt a CPU; this is done to either
1416 // Multics signalling, such has CAM cache clear
1417 // Adding a new CPU
1418 // Reading a deleted CPU
1419 // Starting an ISOLTS CPU
1420 // If it is a Multics signal, the target CPU will be marked as 'inMultics';
1421 // For the other cases, 'inMultics ' will not be set.
1422 // cpup != NULL means that this interrupt was generated by a CPU, rather
1423 // than an IOM; we need this to set the calling thread into slave mode.
1424 if ((! sys.sys_opts.nosync) && cpup && (! cpus[cpu_unit_udx].inMultics)) {
1425 // The interrupt is to start or restart a CPU.
1426 # ifdef SYNCTEST
1427 sim_printf ("CPU %c becomes clock master\r\n", 'A' + cpu_unit_udx);
1428 # endif
1429 becomeClockMaster (cpu_unit_udx);
1430 // Set this so that the calling thread will join the queue immediately.
1431 cpu.syncClockModePoll = 0;
1432 cpu.syncClockModeCache = true;
1433 __asm volatile ("");
1434 atomic_thread_fence (memory_order_seq_cst);
1435 }
1436 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1437 # if defined(TESTING)
1438 HDBGIntrSet (inum, cpu_unit_udx, scu_unit_idx, __func__);
1439 # endif
1440 createCPUThread((uint) cpu_unit_udx);
1441 # if !defined(NO_TIMEWAIT)
1442 wakeCPU ((uint) cpu_unit_udx);
1443 # endif
1444 sim_debug (DBG_DEBUG, & scu_dev,
1445 "interrupt set for CPU %d SCU %d\r\n",
1446 cpu_unit_udx, scu_unit_idx);
1447 # else // ! THREADZ
1448 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1449 sim_debug (DBG_DEBUG, & scu_dev, "interrupt set for CPU %d SCU %d\r\n", cpu_unit_udx, scu_unit_idx);
1450 sim_debug (DBG_INTR, & scu_dev,
1451 "XIP set for SCU %d\r\n", scu_unit_idx);
1452 # endif // ! THREADZ
1453 }
1454 }
1455 }
1456 #else // !REORDER
1457 for (uint inum = 0; inum < N_CELL_INTERRUPTS; inum ++)
1458 {
1459 if (! scu [scu_unit_idx].cells [inum])
1460 continue; //
1461 sim_debug (DBG_DEBUG, & scu_dev, "trying to deliver %d\r\n", inum);
1462 sim_debug (DBG_INTR, & scu_dev,
1463 "scu %u trying to deliver %d\r\n", scu_unit_idx, inum);
1464
1465 for (uint pima = 0; pima < N_ASSIGNMENTS; pima ++) // A, B
1466 {
1467 //sim_debug (DBG_DEBUG, & scu_dev,
1468 // "trying inum %u pima %u enable %u\r\n"
1469 // , inum, pima, scu [scu_unit_idx].mask_enable [pima]);
1470 if (scu [scu_unit_idx].mask_enable [pima] == 0)
1471 continue;
1472 uint mask = scu [scu_unit_idx].exec_intr_mask [pima];
1473 uint port = scu [scu_unit_idx].mask_assignment [pima];
1474 //sim_debug (DBG_DEBUG, & scu_dev,
1475 // "mask %u port %u type %u cells %o\r\n",
1476 // mask, port, scu [scu_unit_idx].ports [port].type,
1477 // scu [scu_unit_idx].cells [inum]);
1478 if (scu [scu_unit_idx].ports [port].type != ADEV_CPU)
1479 continue;
1480 if ((mask & (1u << (31 - inum))) != 0)
1481 {
1482 uint sn = 0;
1483 if (scu[scu_unit_idx].ports[port].is_exp)
1484 {
1485 sn = (uint) scu[scu_unit_idx].ports[port].xipmaskval;
1486 if (sn >= N_SCU_SUBPORTS)
1487 {
1488 sim_warn ("XIP mask not set; defaulting to subport 0\r\n");
1489 sn = 0;
1490 }
1491 }
1492 if (! cables->scu_to_cpu[scu_unit_idx][port][sn].in_use)
1493 {
1494 sim_warn ("bad scu_unit_idx %u\r\n", scu_unit_idx);
1495 continue;
1496 }
1497 uint cpu_unit_udx = cables->scu_to_cpu[scu_unit_idx][port][sn].cpu_unit_idx;
1498 # if defined(THREADZ) || defined(LOCKLESS)
1499 // Need to do this before setting XIP to avoid race condition
1500 // We are about to interrupt a CPU; this is done to either
1501 // Multics signalling, such has CAM cache clear
1502 // Adding a new CPU
1503 // Reading a deleted CPU
1504 // Starting an ISOLTS CPU
1505 // If it is a Multics signal, the target CPU will be marked as 'inMultics';
1506 // For the other cases, 'inMultics ' will not be set.
1507 // cpup != NULL means that this interrupt was generated by a CPU, rather
1508 // than an IOM; we need this to set the calling thread into slave mode.
1509 if ((! sys_opts.nosync) && cpup && (! cpus[cpu_unit_udx].inMultics)) {
1510 // The interrupt is to start or restart a CPU.
1511 # ifdef SYNCTEST
1512 sim_printf ("CPU %c becomes clock master\r\n", 'A' + cpu_unit_udx);
1513 # endif
1514 becomeClockMaster (cpu_unit_udx);
1515 }
1516 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1517 # if defined(TESTING)
1518 HDBGIntrSet (inum, cpu_unit_udx, scu_unit_idx, __func__);
1519 # endif
1520 # ifdef SYNCTEST
1521 if (cpus[cpu_unit_udx].rcfDelete) sim_printf ("Poking CPU %c in rcfDelete\r\n", 'A' + cpu_unit_udx);
1522 # endif
1523 createCPUThread((uint) cpu_unit_udx);
1524 # if !defined(NO_TIMEWAIT)
1525 wakeCPU ((uint) cpu_unit_udx);
1526 # endif
1527 sim_debug (DBG_DEBUG, & scu_dev,
1528 "interrupt set for CPU %d SCU %d\r\n",
1529 cpu_unit_udx, scu_unit_idx);
1530 # else // ! THREADZ
1531 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1532 sim_debug (DBG_DEBUG, & scu_dev, "interrupt set for CPU %d SCU %d\r\n", cpu_unit_udx, scu_unit_idx);
1533 sim_debug (DBG_INTR, & scu_dev,
1534 "XIP set for SCU %d\r\n", scu_unit_idx);
1535 # endif // ! THREADZ
1536 }
1537 }
1538 }
1539 #endif // REORDER
1540 }
1541
1542 t_stat scu_smic (cpu_state_t * cpup, uint scu_unit_idx, uint UNUSED cpu_unit_udx,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1543 uint UNUSED cpu_port_num, word36 rega)
1544 {
1545 #if defined(THREADZ) || defined(LOCKLESS)
1546 lock_scu ();
1547 #endif
1548 // smic can set cells but not reset them...
1549
1550 if (getbits36_1 (rega, 35))
1551 {
1552 for (uint i = 0; i < 16; i ++)
1553 {
1554 if (getbits36_1 (rega, i))
1555 scu [scu_unit_idx].cells [i + 16] = 1;
1556 }
1557 char pcellb [N_CELL_INTERRUPTS + 1];
1558 sim_debug (DBG_TRACE, & scu_dev,
1559 "SMIC low: Unit %u Cells: %s\r\n",
1560 scu_unit_idx, pcells (scu_unit_idx, pcellb));
1561 }
1562 else
1563 {
1564 for (uint i = 0; i < 16; i ++)
1565 {
1566 if (getbits36_1 (rega, i))
1567 scu [scu_unit_idx].cells [i] = 1;
1568 }
1569 char pcellb [N_CELL_INTERRUPTS + 1];
1570 sim_debug (DBG_TRACE, & scu_dev,
1571 "SMIC high: Unit %d Cells: %s\r\n",
1572 scu_unit_idx, pcells (scu_unit_idx, pcellb));
1573 }
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599 dump_intr_regs ("smic", scu_unit_idx);
1600 deliver_interrupts (cpup, scu_unit_idx);
1601 #if defined(THREADZ) || defined(LOCKLESS)
1602 unlock_scu ();
1603 #endif
1604 return SCPE_OK;
1605 }
1606
1607 // system controller and the function to be performed as follows:
1608 //
1609 // Effective Function
1610 // Address
1611 // y0000x C(system controller mode register) -> C(AQ)
1612 // y0001x C(system controller configuration switches) -> C(AQ)
1613 // y0002x C(mask register assigned to port 0) -> C(AQ)
1614 // y0012x C(mask register assigned to port 1) -> C(AQ)
1615 // y0022x C(mask register assigned to port 2) -> C(AQ)
1616 // y0032x C(mask register assigned to port 3) -> C(AQ)
1617 // y0042x C(mask register assigned to port 4) -> C(AQ)
1618 // y0052x C(mask register assigned to port 5) -> C(AQ)
1619 // y0062x C(mask register assigned to port 6) -> C(AQ)
1620 // y0072x C(mask register assigned to port 7) -> C(AQ)
1621 // y0003x C(interrupt cells) -> C(AQ)
1622 //
1623 // y0004x
1624 // or C(calendar clock) -> C(AQ)
1625 // y0005x
1626 //
1627 // y0006x
1628 // or C(store unit mode register) -> C(AQ)
1629 // y0007x
1630 //
1631 // where: y = value of C(TPR.CA)0,2 (C(TPR.CA)1,2 for the DPS 8M
1632 // processor) used to select the system controller
1633 // x = any octal digit
1634 //
1635
1636 t_stat scu_sscr (cpu_state_t * cpup, uint scu_unit_idx, UNUSED uint cpu_unit_udx,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1637 UNUSED uint cpu_port_num, word18 addr,
1638 word36 rega, word36 regq)
1639 {
1640 sim_debug (DBG_DEBUG, & scu_dev, "sscr SCU unit %o\r\n", scu_unit_idx);
1641
1642 // Only valid for a 4MW SCU
1643
1644 if (scu_unit_idx >= scu_dev.numunits)
1645 {
1646 // XXX should this be a store fault?
1647 sim_warn ("%s: scu_unit_idx out of range %d\r\n",
1648 __func__, scu_unit_idx);
1649 return SCPE_OK;
1650 }
1651
1652 // BCE uses clever addressing schemes to select SCUs; it appears we need
1653 // to be more selecting in picking out the function bits;
1654 //uint function = (addr >> 3) & 07777;
1655 uint function = (addr >> 3) & 07;
1656
1657 // See scs.incl.pl1
1658
1659 if (config_switches [scu_unit_idx].mode != MODE_PROGRAM)
1660 {
1661 sim_warn ("%s: SCU mode is 'MANUAL', not 'PROGRAM' -- sscr "
1662 "not allowed to set switches.\r\n",
1663 __func__);
1664 // XXX [CAC] Setting an unassigned register generates a STORE FAULT;
1665 // this probably should as well
1666 return SCPE_OK;
1667 }
1668
1669 // Not used by 4MW
1670
1671 switch (function)
1672 {
1673 case 00000: // Set system controller mode register
1674 {
1675 #if defined(THREADZ) || defined(LOCKLESS)
1676 lock_scu ();
1677 #endif
1678 scu [scu_unit_idx].id = (word4) getbits36_4 (regq, 50 - 36);
1679 scu [scu_unit_idx].mode_reg = getbits36_18 (regq, 54 - 36);
1680 #if defined(THREADZ) || defined(LOCKLESS)
1681 unlock_scu ();
1682 #endif
1683 }
1684 break;
1685
1686 case 00001: // Set system controller configuration register
1687 // (4MW SCU only)
1688 {
1689 sim_debug (DBG_DEBUG, & scu_dev,
1690 "sscr 1 %d A: %012"PRIo64" Q: %012"PRIo64"\r\n",
1691 scu_unit_idx, rega, regq);
1692 #if defined(THREADZ) || defined(LOCKLESS)
1693 lock_scu ();
1694 #endif
1695 scu_t * up = scu + scu_unit_idx;
1696 for (int maskab = 0; maskab < 2; maskab ++)
1697 {
1698 word9 mask = ((maskab ? regq : rega) >> 27) & 0777;
1699 if (mask & 01)
1700 {
1701 up -> mask_enable [maskab] = 0;
1702 sim_debug (DBG_DEBUG, & scu_dev,
1703 "sscr %u mask disable %d\r\n",
1704 scu_unit_idx, maskab);
1705 }
1706 else
1707 {
1708 up -> mask_enable [maskab] = 1;
1709 sim_debug (DBG_DEBUG, & scu_dev,
1710 "sscr %u mask enable %d\r\n",
1711 scu_unit_idx, maskab);
1712 for (int pn = 0; pn < N_SCU_PORTS; pn ++)
1713 {
1714 if ((2 << (N_SCU_PORTS - 1 - pn)) & mask)
1715 {
1716 up -> mask_assignment [maskab] = (uint) pn;
1717 break;
1718 }
1719 }
1720
1721 }
1722 sim_debug (DBG_INTR, & scu_dev,
1723 "SCU%u SSCR1 mask %c enable set to %u assigned to "
1724 "port %u\r\n",
1725 scu_unit_idx, 'a' + maskab, up->mask_enable[maskab],
1726 up->mask_assignment[maskab]);
1727 }
1728 // AN87-00A, pg 2-5, 2-6 specify which fields are and are not
1729 // settable.
1730
1731 //if (up -> lower_store_size != ((rega >> 24) & 07))
1732 //sim_printf ("??? The CPU tried to change the SCU store size\r\n");
1733 up -> lower_store_size = (rega >> 24) & 07;
1734 up -> cyclic = (regq >> 8) & 0177;
1735 up -> nea = (rega >> 6) & 0377;
1736 up -> onl = (rega >> 20) & 017;
1737 up -> interlace = (rega >> 5) & 1;
1738 up -> lwr = (rega >> 4) & 1;
1739 up -> port_enable [0] = (rega >> 3) & 01;
1740 up -> port_enable [1] = (rega >> 2) & 01;
1741 up -> port_enable [2] = (rega >> 1) & 01;
1742 up -> port_enable [3] = (rega >> 0) & 01;
1743 up -> port_enable [4] = (regq >> 3) & 01;
1744 up -> port_enable [5] = (regq >> 2) & 01;
1745 up -> port_enable [6] = (regq >> 1) & 01;
1746 up -> port_enable [7] = (regq >> 0) & 01;
1747
1748 #if defined(THREADZ) || defined(LOCKLESS)
1749 unlock_scu ();
1750 #endif
1751 // XXX A, A1, B, B1, INT, LWR not implemented. (AG87-00A pgs 2-5,
1752 // 2-6)
1753 break;
1754 }
1755
1756 case 00002: // Set mask register port 0
1757 //case 00012: // Set mask register port 1
1758 //case 00022: // Set mask register port 2
1759 //case 00032: // Set mask register port 3
1760 //case 00042: // Set mask register port 4
1761 //case 00052: // Set mask register port 5
1762 //case 00062: // Set mask register port 6
1763 //case 00072: // Set mask register port 7
1764 {
1765 #if defined(THREADZ) || defined(LOCKLESS)
1766 lock_scu ();
1767 #endif
1768 uint port_num = (addr >> 6) & 07;
1769 sim_debug (DBG_DEBUG, & scu_dev, "Set mask register port %d to "
1770 "%012"PRIo64",%012"PRIo64"\r\n",
1771 port_num, rega, regq);
1772
1773 // Find mask reg assigned to specified port
1774 int mask_num = -1;
1775 uint n_masks_found = 0;
1776 for (int p = 0; p < N_ASSIGNMENTS; p ++)
1777 {
1778 //if (scup -> interrupts [p].mask_assign.unassigned)
1779 if (scu [scu_unit_idx].mask_enable [p] == 0)
1780 continue;
1781 //if (scup -> interrupts [p].mask_assign.port == port_num)
1782 if (scu [scu_unit_idx ].mask_assignment [p] == port_num)
1783 {
1784 if (n_masks_found == 0)
1785 mask_num = p;
1786 n_masks_found ++;
1787 }
1788 }
1789
1790 if (! n_masks_found)
1791 {
1792 // According to bootload_tape_label.alm, this condition is OK
1793 sim_debug (DBG_WARN, & scu_dev,
1794 "%s: No masks assigned to cpu on port %d\r\n",
1795 __func__, port_num);
1796 #if defined(THREADZ) || defined(LOCKLESS)
1797 unlock_scu ();
1798 #endif
1799 return SCPE_OK;
1800 }
1801
1802 if (n_masks_found > 1)
1803 {
1804 // Not legal for Multics
1805 sim_debug (DBG_WARN, & scu_dev,
1806 "%s: Multiple masks assigned to cpu on port %d\r\n",
1807 __func__, port_num);
1808 }
1809
1810 // See AN87
1811 //scup -> interrupts[mask_num].exec_intr_mask = 0;
1812 scu [scu_unit_idx].exec_intr_mask [mask_num] = 0;
1813 scu [scu_unit_idx].exec_intr_mask [mask_num] |=
1814 ((word32) getbits36_16(rega, 0) << 16);
1815 scu [scu_unit_idx].exec_intr_mask [mask_num] |=
1816 getbits36_16(regq, 0);
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826 sim_debug (DBG_TRACE, & scu_dev,
1827 "SSCR Set mask unit %u port %u mask_num %u "
1828 "mask 0x%08x\r\n",
1829 scu_unit_idx, port_num, mask_num,
1830 scu [scu_unit_idx].exec_intr_mask [mask_num]);
1831 dump_intr_regs ("sscr set mask", scu_unit_idx);
1832 scu [scu_unit_idx].mask_enable [mask_num] = 1;
1833 sim_debug (DBG_INTR, & scu_dev,
1834 "SCU%u SSCR2 exec_intr mask %c set to 0x%08x"
1835 " and enabled.\r\n",
1836 scu_unit_idx, 'a' + mask_num,
1837 scu[scu_unit_idx].exec_intr_mask[mask_num]);
1838
1839 deliver_interrupts (cpup, scu_unit_idx);
1840 #if defined(THREADZ) || defined(LOCKLESS)
1841 unlock_scu ();
1842 #endif
1843 }
1844 break;
1845
1846 case 00003: // Set interrupt cells
1847 {
1848 #if defined(THREADZ) || defined(LOCKLESS)
1849 lock_scu ();
1850 #endif
1851 for (uint i = 0; i < 16; i ++)
1852 {
1853 scu [scu_unit_idx].cells [i] =
1854 getbits36_1 (rega, i) ? 1 : 0;
1855 scu [scu_unit_idx].cells [i + 16] =
1856 getbits36_1 (regq, i) ? 1 : 0;
1857 }
1858 char pcellb [N_CELL_INTERRUPTS + 1];
1859 sim_debug (DBG_TRACE, & scu_dev,
1860 "SSCR Set int. cells: Unit %u Cells: %s\r\n",
1861 scu_unit_idx, pcells (scu_unit_idx, pcellb));
1862 sim_debug (DBG_INTR, & scu_dev,
1863 "SCU%u SSCR3 Set int. cells %s\r\n",
1864 scu_unit_idx, pcells (scu_unit_idx, pcellb));
1865 dump_intr_regs ("sscr set interrupt cells", scu_unit_idx);
1866 deliver_interrupts (NULL, scu_unit_idx);
1867 #if defined(THREADZ) || defined(LOCKLESS)
1868 unlock_scu ();
1869 #endif
1870 }
1871 break;
1872
1873 case 00004: // Set calendar clock (4MW SCU only)
1874 case 00005:
1875 {
1876 // AQ: 20-35 clock bits 0-15, 36-71 clock bits 16-51
1877 word16 b0_15 = (word16) getbits36_16 (cpu.rA, 20);
1878 word36 b16_51 = cpu.rQ;
1879 uint64 new_clk = (((uint64) b0_15) << 36) | b16_51;
1880 #if defined(THREADZ) || defined(LOCKLESS)
1881 lock_scu ();
1882 #endif
1883 scu [scu_unit_idx].user_correction =
1884 (int64) (new_clk - set_SCU_clock (cpup, scu_unit_idx));
1885 #if defined(THREADZ) || defined(LOCKLESS)
1886 unlock_scu ();
1887 #endif
1888 //sim_printf ("sscr %o\r\n", function);
1889 }
1890 break;
1891
1892 case 00006: // Set unit mode register
1893 case 00007:
1894 // XXX See notes in AL39 sscr re: store unit selection
1895 //sim_printf ("sscr %o\r\n", function);
1896 sim_warn ("sscr set unit mode register\r\n");
1897 //return STOP_UNIMP;
1898 return SCPE_OK;
1899
1900 default:
1901 sim_warn ("sscr unhandled code\r\n");
1902 //return STOP_UNIMP;
1903 return SCPE_OK;
1904 //sim_printf ("sscr %o\r\n", function);
1905 }
1906 return SCPE_OK;
1907 }
1908
1909 t_stat scu_rscr (cpu_state_t * cpup, uint scu_unit_idx, uint cpu_unit_udx, word18 addr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1910 word36 * rega, word36 * regq)
1911 {
1912 // Only valid for a 4MW SCU
1913
1914 if (scu_unit_idx >= scu_dev.numunits)
1915 {
1916 sim_warn ("%s: scu_unit_idx out of range %d\r\n",
1917 __func__, scu_unit_idx);
1918 return SCPE_OK;
1919 }
1920
1921 // BCE uses clever addressing schemes to select SCUs; it appears we need
1922 // to be more selecting in picking out the function bits;
1923 //uint function = (addr >> 3) & 07777;
1924 uint function = (addr >> 3) & 07;
1925
1926 //sim_printf ("rscr %o\r\n", function);
1927
1928 // See scs.incl.pl1
1929
1930 switch (function)
1931 {
1932 case 00000: // Read system controller mode register
1933 {
1934 // AN-87
1935 // 0..0 -> A
1936 // 0..0 -> Q 36-49 (0-13)
1937 // ID -> Q 50-53 (14-17)
1938 // MODE REG -> Q 54-71 (18-35)
1939 //
1940 // ID: 0000 8034, 8035
1941 // 0001 Level 68 SC
1942 // 0010 Level 66 SCU
1943 // CAC: According to scr.incl.pl1. 0010 is a 4MW SCU
1944 // MODE REG: these fields are only used by T&D
1945 * rega = 0;
1946 //* regq = 0000002000000; // ID = 0010
1947 * regq = 0;
1948 #if defined(THREADZ) || defined(LOCKLESS)
1949 lock_scu ();
1950 #endif
1951 putbits36_4 (regq, 50 - 36, scu [scu_unit_idx].id);
1952 putbits36_18 (regq, 54 - 36, scu [scu_unit_idx].mode_reg);
1953 #if defined(THREADZ) || defined(LOCKLESS)
1954 unlock_scu ();
1955 #endif
1956 break;
1957 }
1958
1959 case 00001: // Read system controller configuration register
1960 {
1961 // AN-87, scr.incl.pl1
1962 //
1963 // SCU:
1964 // reg A:
1965 // MASK A | SIZE | A | A1 | B | B1 | PORT | 0 | MOD | NEA |
1966 // INT | LWR | PMR 0-3
1967 // reg Q:
1968 // MASK B | not used | CYCLIC PRIOR | not used | PMR 4-7
1969 //
1970 // MASK A/B (9 bits): EIMA switch setting for mask A/B. The
1971 // assigned port corresponds to the but position within the
1972 // field. A bit in position 9 indicates that the mask is
1973 // not assigned.
1974 // From scr.incl.pl1:
1975 // 400 => assigned to port 0
1976 // .
1977 // .
1978 // 002 => assigned to port 7
1979 // 001 => mask off */
1980
1981 //
1982 // SIZE (3 bits): Size of lower store
1983 // 000 = 32K ... 111 = 4M
1984 //
1985 // A A1 B B1 (1 bit): store unit A/A1/B/B1 online
1986 //
1987 // PORT (4 bits): Port number of the SCU port through which
1988 // the RSCR instruction was received
1989 //
1990 //struct config_switches * sw = config_switches + scu_unit_idx;
1991 sim_debug (DBG_DEBUG, & scu_dev, "rscr 1 %d\r\n", scu_unit_idx);
1992 #if defined(THREADZ) || defined(LOCKLESS)
1993 lock_scu ();
1994 #endif
1995 scu_t * up = scu + scu_unit_idx;
1996 word9 maskab [2];
1997 for (int i = 0; i < 2; i ++)
1998 {
1999 if (up -> mask_enable [i])
2000 {
2001 maskab [i] = (2 << (N_SCU_PORTS - 1 -
2002 up -> mask_assignment [i])) & 0777;
2003 }
2004 else
2005 maskab [i] = 0001;
2006 }
2007
2008 int scu_port_num = -1; // The port that the rscr instruction was
2009 // received on
2010
2011 for (int pn = 0; pn < N_SCU_PORTS; pn ++)
2012 {
2013 for (int sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2014 {
2015 if (cables->scu_to_cpu[scu_unit_idx][pn][sn].in_use &&
2016 cables->scu_to_cpu[scu_unit_idx][pn][sn].cpu_unit_idx ==
2017 cpu_unit_udx)
2018 {
2019 scu_port_num = pn;
2020 goto gotit;
2021 }
2022 }
2023 }
2024 gotit:;
2025 if (scu_port_num < 0)
2026 {
2027 #if defined(THREADZ) || defined(LOCKLESS)
2028 unlock_scu ();
2029 #endif
2030 sim_warn ("%s: can't find cpu port in the snarl of cables; "
2031 "scu_unit_no %d, cpu_unit_udx %d\r\n",
2032 __func__, scu_unit_idx, cpu_unit_udx);
2033 return SCPE_OK;
2034 }
2035
2036 // AN87, pg 2-5
2037 word36 a, q;
2038
2039 a = 0;
2040 // (data, starting bit position, number of bits, value)
2041 putbits36_9 (& a, 0, maskab [0]);
2042 putbits36_3 (& a, 9, (word3) up -> lower_store_size);
2043 putbits36_4 (& a, 12, (word4) up -> onl); // A, A1, B, B1 online
2044 putbits36_4 (& a, 16, (word4) scu_port_num);
2045 putbits36_1 (& a, 21, (word1) config_switches[scu_unit_idx].mode);
2046 putbits36_8 (& a, 22, (word8) up -> nea);
2047 putbits36_1 (& a, 30, (word1) up -> interlace);
2048 putbits36_1 (& a, 31, (word1) up -> lwr);
2049 // XXX INT, LWR not implemented. (AG87-00A pgs 2-5. 2-6)
2050 // interlace <- 0
2051 // lower <- 0
2052 // Looking at scr_util.list, I *think* the port order
2053 // 0,1,2,3.
2054 putbits36_1 (& a, 32, (word1) up -> port_enable [0]);
2055 putbits36_1 (& a, 33, (word1) up -> port_enable [1]);
2056 putbits36_1 (& a, 34, (word1) up -> port_enable [2]);
2057 putbits36_1 (& a, 35, (word1) up -> port_enable [3]);
2058 * rega = a;
2059
2060 q = 0;
2061 putbits36_9 (& q, 0, maskab [1]);
2062 // cyclic prior <- 0
2063 putbits36_7 (& q, 57-36, (word7) up -> cyclic & MASK7);
2064 // Looking at scr_util.list, I *think* the port order
2065 // 0,1,2,3.
2066 putbits36_1 (& q, 32, (word1) up -> port_enable [4]);
2067 putbits36_1 (& q, 33, (word1) up -> port_enable [5]);
2068 putbits36_1 (& q, 34, (word1) up -> port_enable [6]);
2069 putbits36_1 (& q, 35, (word1) up -> port_enable [7]);
2070 * regq = q;
2071
2072 #if defined(THREADZ) || defined(LOCKLESS)
2073 unlock_scu ();
2074 #endif
2075 sim_debug (DBG_DEBUG, & scu_dev,
2076 "rscr 1 %d A: %012"PRIo64" Q: %012"PRIo64"\r\n",
2077 scu_unit_idx, * rega, * regq);
2078 break;
2079 }
2080
2081 case 00002: // mask register
2082 {
2083 uint port_num = (addr >> 6) & MASK3;
2084 #if defined(THREADZ) || defined(LOCKLESS)
2085 lock_scu ();
2086 #endif
2087 scu_t * up = scu + scu_unit_idx;
2088 uint mask_contents = 0;
2089 if (up -> mask_assignment [0] == port_num)
2090 {
2091 mask_contents = up -> exec_intr_mask [0];
2092 }
2093 else if (up -> mask_assignment [1] == port_num)
2094 {
2095 mask_contents = up -> exec_intr_mask [1];
2096 }
2097 mask_contents &= MASK32;
2098
2099 * rega = 0;
2100 putbits36 (rega, 0, 16, (mask_contents >> 16) & MASK16);
2101 putbits36 (rega, 32, 1, up -> port_enable [0]);
2102 putbits36 (rega, 33, 1, up -> port_enable [1]);
2103 putbits36 (rega, 34, 1, up -> port_enable [2]);
2104 putbits36 (rega, 35, 1, up -> port_enable [3]);
2105
2106 * regq = 0;
2107 putbits36 (rega, 0, 16, (mask_contents >> 0) & MASK16);
2108 putbits36 (regq, 32, 1, up -> port_enable [4]);
2109 putbits36 (regq, 33, 1, up -> port_enable [5]);
2110 putbits36 (regq, 34, 1, up -> port_enable [6]);
2111 putbits36 (regq, 35, 1, up -> port_enable [7]);
2112
2113 #if defined(THREADZ) || defined(LOCKLESS)
2114 unlock_scu ();
2115 #endif
2116 sim_debug (DBG_TRACE, & scu_dev,
2117 "RSCR mask unit %u port %u assigns %u %u mask 0x%08x\r\n",
2118 scu_unit_idx, port_num, up -> mask_assignment [0],
2119 up -> mask_assignment [1],
2120 mask_contents);
2121 }
2122 break;
2123
2124 case 00003: // Interrupt cells
2125 {
2126 #if defined(THREADZ) || defined(LOCKLESS)
2127 lock_scu ();
2128 #endif
2129 scu_t * up = scu + scu_unit_idx;
2130 // * rega = up -> exec_intr_mask [0];
2131 // * regq = up -> exec_intr_mask [1];
2132 for (uint i = 0; i < N_CELL_INTERRUPTS; i ++)
2133 {
2134 word1 cell = up -> cells [i] ? 1 : 0;
2135 if (i < 16)
2136 putbits36_1 (rega, i, cell);
2137 else
2138 putbits36_1 (regq, i - 16, cell);
2139 }
2140 #if defined(THREADZ) || defined(LOCKLESS)
2141 unlock_scu ();
2142 #endif
2143 }
2144 break;
2145
2146 case 00004: // Get calendar clock (4MW SCU only)
2147 case 00005:
2148 {
2149 uint64 clk = set_SCU_clock (cpup, scu_unit_idx);
2150 cpu.rQ = clk & 0777777777777; // lower 36-bits of clock
2151 cpu.rA = (clk >> 36) & 0177777; // upper 16-bits of clock
2152 #if defined(TESTING)
2153 HDBGRegAW ("rscr get clock");
2154 HDBGRegQW ("rscr get clock");
2155 #endif
2156 }
2157 break;
2158
2159 case 00006: // SU Mode register
2160 case 00007: // SU Mode register
2161 {
2162 //sim_printf ("rscr SU Mode Register%o\r\n", function);
2163
2164 // Completely undocumented...
2165 // scr.incl.alm
2166 //" Structure scr_su
2167 //"
2168 // equ scr_su_size,2
2169 //
2170 //
2171 // equ scr_su.ZAC_line_word,1
2172 // equ scr_su.ZAC_line_shift,30
2173 // bool scr_su.ZAC_line_mask,000077
2174 // equ scr_su.syndrome_word,1
2175 // equ scr_su.syndrome_shift,22
2176 // bool scr_su.syndrome_mask,000377
2177 // equ scr_su.identification_word,1
2178 // equ scr_su.identification_shift,18
2179 // bool scr_su.identification_mask,000017
2180 // equ scr_su.EDAC_disabled_word,1
2181 // bool scr_su.EDAC_disabled,400000 " DL
2182 // equ scr_su.MINUS_5_VOLT_margin_word,1
2183 //" equ scr_su.MINUS_5_VOLT_margin_shift,11
2184 // bool scr_su.MINUS_5_VOLT_margin_mask,000003
2185 // equ scr_su.PLUS_5_VOLT_margin_word,1
2186 // equ scr_su.PLUS_5_VOLT_margin_shift,9
2187 // bool scr_su.PLUS_5_VOLT_margin_mask,000003
2188 // equ scr_su.spare_margin_word,1
2189 // equ scr_su.spare_margin_shift,7
2190 // bool scr_su.spare_margin_mask,000003
2191 // equ scr_su.PLUS_19_VOLT_margin_word,1
2192 //" equ scr_su.PLUS_19_VOLT_margin_shift,5
2193 // bool scr_su.PLUS_19_VOLT_margin_mask,000003
2194 // equ scr_su.SENSE_strobe_margin_word,1
2195 //" equ scr_su.SENSE_strobe_margin_shift,2
2196 // bool scr_su.SENSE_strobe_margin_mask,000003
2197 //" equ scr_su.maint_functions_enabled_word,1
2198 // bool scr_su.maint_functions_enabled,000001 " DL
2199
2200 // 1 1 1 2 2 2 2 3 3 3 3
2201 // 0 6 4 8 9 3 5 7 9 1 2 4 5
2202 // ------------------------------------------------------------------------------
2203 // | ZAC | synd | id | EDAC | 0 | -5 | +5 | spare | +19 | 0 | sense | 0 | maint |
2204 // ------------------------------------------------------------------------------
2205 // 6 8 4 1 4 2 2 2 2 1 2 1 1
2206
2207 // Okay, it looks safe to return 0.
2208
2209 * rega = 0;
2210 * regq = 0;
2211 }
2212 break;
2213
2214 default:
2215 sim_warn ("rscr %o\r\n", function);
2216 return SCPE_OK;
2217 }
2218 return SCPE_OK;
2219 }
2220
2221
2222
2223
2224
2225 int scu_cioc (uint cpu_unit_udx, uint scu_unit_idx, uint scu_port_num,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
2226 uint expander_command, uint sub_mask)
2227 {
2228
2229
2230
2231 #if defined(TESTING)
2232 cpu_state_t * cpup = _cpup;
2233 sim_debug (DBG_DEBUG, & scu_dev,
2234 "scu_cioc: Connect from %o sent to "
2235 "unit %o port %o exp %o mask %03o\r\n",
2236 cpu_unit_udx, scu_unit_idx, scu_port_num,
2237 expander_command, sub_mask);
2238 #endif
2239 #if defined(THREADZ) || defined(LOCKLESS)
2240 lock_scu ();
2241 #endif
2242 struct ports * portp = & scu [scu_unit_idx].ports [scu_port_num];
2243
2244 int rc = 0;
2245 if (! scu [scu_unit_idx].port_enable [scu_port_num])
2246 {
2247 sim_debug (DBG_ERR, & scu_dev,
2248 "scu_cioc: Connect sent to disabled port; dropping\r\n");
2249 sim_debug (DBG_ERR, & scu_dev,
2250 "scu_cioc: scu_unit_idx %u scu_port_num %u\r\n",
2251 scu_unit_idx, scu_port_num);
2252 rc = 1;
2253 goto done;
2254 }
2255
2256 if (expander_command == 1) // "set subport enables"
2257 {
2258 for (uint i = 0; i < N_SCU_SUBPORTS; i++)
2259 {
2260 portp->subport_enables [i] = !! (sub_mask & (0200u >> i));
2261 }
2262 goto done;
2263 }
2264
2265 if (expander_command == 2) // "set xipmask"
2266 {
2267 int cnt = 0;
2268 int val = -1;
2269 for (uint i = 0; i < N_SCU_SUBPORTS; i++)
2270 {
2271 portp->xipmask [i] = !! (sub_mask & (0200u >> i));
2272 if (portp->xipmask [i])
2273 {
2274 val = (int) i;
2275 cnt ++;
2276 }
2277 }
2278 if (cnt > 1)
2279 {
2280 sim_warn ("xip mask cnt > 1\r\n");
2281 val = -1;
2282 }
2283 portp->xipmaskval = val;
2284 goto done;
2285 }
2286
2287 if (portp -> type == ADEV_IOM)
2288 {
2289 int iom_unit_idx = portp->dev_idx;
2290 #if defined(THREADZ) || defined(LOCKLESS)
2291 unlock_scu ();
2292 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
2293 lock_iom ();
2294 lock_libuv ();
2295 # endif
2296 iom_interrupt (scu_unit_idx, (uint) iom_unit_idx);
2297 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
2298 unlock_libuv ();
2299 unlock_iom ();
2300 # endif
2301 return 0;
2302 #else // ! THREADZ
2303 if (sys_opts.iom_times.connect <= 0)
2304 {
2305 iom_interrupt (scu_unit_idx, (uint) iom_unit_idx);
2306 goto done;
2307 }
2308 else
2309 {
2310 //sim_printf ("scu_cioc: Queuing an IOM in %d cycles "
2311 //"(for the connect channel) %u %d\r\n",
2312 //sys_opts.iom_times.connect, scu_unit_idx, iom_unit_idx);
2313 sim_debug (DBG_INFO, & scu_dev,
2314 "scu_cioc: Queuing an IOM in %d cycles "
2315 "(for the connect channel)\r\n",
2316 sys_opts.iom_times.connect);
2317 // Stash the iom_interrupt call parameters
2318 iom_dev.units[iom_unit_idx].u3 = (int32) scu_unit_idx;
2319 iom_dev.units[iom_unit_idx].u4 = (int32) iom_unit_idx;
2320 int rc;
2321 if ((rc = sim_activate (& iom_dev.units [iom_unit_idx],
2322 sys_opts.iom_times.connect)) != SCPE_OK)
2323 {
2324 sim_warn ("sim_activate failed (%d)\r\n", rc);
2325 goto done;
2326 }
2327 goto done;
2328 }
2329 #endif // ! THREADZ
2330 }
2331 else if (portp -> type == ADEV_CPU)
2332 {
2333
2334 // by subport_enables
2335 if (portp->is_exp)
2336 {
2337 for (uint sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2338 {
2339 if (portp->subport_enables[sn])
2340 {
2341 if (! cables->
2342 scu_to_cpu[scu_unit_idx][scu_port_num][sn].in_use)
2343 {
2344 sim_warn ("Can't find CPU to interrupt\r\n");
2345 continue;
2346 }
2347 uint cpu_unit_udx = cables->
2348 scu_to_cpu[scu_unit_idx][scu_port_num][sn].cpu_unit_idx;
2349 setG7fault ((uint) cpu_unit_udx, FAULT_CON);
2350 }
2351 }
2352 }
2353 else
2354 {
2355 if (! cables->scu_to_cpu[scu_unit_idx][scu_port_num][0].in_use)
2356 {
2357 sim_warn ("Can't find CPU to interrupt\r\n");
2358 rc = 1;
2359 goto done;
2360 }
2361 uint cpu_unit_udx =
2362 cables->scu_to_cpu[scu_unit_idx][scu_port_num][0].cpu_unit_idx;
2363 setG7fault ((uint) cpu_unit_udx, FAULT_CON);
2364 }
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387 goto done;
2388 }
2389 else
2390 {
2391 sim_debug (DBG_ERR, & scu_dev,
2392 "scu_cioc: Connect sent to not-an-IOM or CPU; dropping\r\n");
2393 rc = 1;
2394 goto done;
2395 }
2396 done:
2397 #if defined(THREADZ) || defined(LOCKLESS)
2398 unlock_scu ();
2399 #endif
2400 return rc;
2401 }
2402
2403 // =============================================================================
2404
2405 // The SXC (set execute cells) SCU command.
2406
2407 // From AN70:
2408 // It then generates a word with
2409 // the <interrupt number>th bit set and sends this to the bootload
2410 // SCU with the SC (set execute cells) SCU command.
2411 //
2412
2413 int scu_set_interrupt (uint scu_unit_idx, uint inum)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
2414 {
2415 #if defined(TESTING)
2416 cpu_state_t * cpup = _cpup;
2417 #endif
2418 const char* moi = "SCU::interrupt";
2419
2420 if (inum >= N_CELL_INTERRUPTS)
2421 {
2422 sim_debug (DBG_WARN, & scu_dev,
2423 "%s: Bad interrupt number %d\r\n", moi, inum);
2424 return 1;
2425 }
2426
2427 #if defined(THREADZ) || defined(LOCKLESS)
2428 lock_scu ();
2429 #endif
2430 scu [scu_unit_idx].cells [inum] = 1;
2431 dump_intr_regs ("scu_set_interrupt", scu_unit_idx);
2432 deliver_interrupts (NULL, scu_unit_idx);
2433 #if defined(THREADZ) || defined(LOCKLESS)
2434 unlock_scu ();
2435 #endif
2436 return 0;
2437 }
2438
2439 // Scan a SCU for interrupts from highest to lowest. If an interrupt is
2440 // present, clear it, update the interrupt state bits and return the fault
2441 // pair address for the interrupt (2 * interrupt number). If no interrupt
2442 // is present, return 1.
2443 //
2444
2445 uint scu_get_highest_intr (uint scu_unit_idx)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
2446 {
2447 #if defined(TESTING)
2448 cpu_state_t * cpup = _cpup;
2449 #endif
2450 #if defined(THREADZ) || defined(LOCKLESS)
2451 lock_scu ();
2452 #endif
2453 // lower numbered cells have higher priority
2454 for (int inum = 0; inum < N_CELL_INTERRUPTS; inum ++)
2455 {
2456 for (uint pima = 0; pima < N_ASSIGNMENTS; pima ++) // A, B
2457 {
2458 if (scu [scu_unit_idx].mask_enable [pima] == 0)
2459 continue;
2460 uint mask = scu [scu_unit_idx].exec_intr_mask [pima];
2461 uint port = scu [scu_unit_idx].mask_assignment [pima];
2462 // if (scu [scu_unit_idx].ports [port].type != ADEV_CPU ||
2463 // scu [scu_unit_idx].ports [port].dev_idx != current_running_cpu_idx)
2464 if (scu[scu_unit_idx].ports[port].type != ADEV_CPU ||
2465 cpus[current_running_cpu_idx].scu_port[scu_unit_idx] != port)
2466 continue;
2467 if (scu [scu_unit_idx].cells [inum] &&
2468 (mask & (1u << (31 - inum))) != 0)
2469 {
2470 sim_debug (DBG_TRACE, & scu_dev,
2471 "scu_get_highest_intr inum %d pima %u mask 0%011o port %u cells 0%011o\r\n",
2472 inum, pima, mask, port, scu [scu_unit_idx].cells [inum]);
2473 scu [scu_unit_idx].cells [inum] = false;
2474 dump_intr_regs ("scu_get_highest_intr", scu_unit_idx);
2475 deliver_interrupts (NULL, scu_unit_idx);
2476 #if defined(THREADZ) || defined(LOCKLESS)
2477 unlock_scu ();
2478 #endif
2479 return (uint) inum * 2;
2480 }
2481 }
2482 }
2483 #if defined(THREADZ) || defined(LOCKLESS)
2484 unlock_scu ();
2485 #endif
2486 return 1;
2487 }
2488
2489 t_stat scu_reset_unit (UNIT * uptr, UNUSED int32 value,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
2490 UNUSED const char * cptr,
2491 UNUSED void * desc)
2492 {
2493 uint scu_unit_idx = (uint) (uptr - scu_unit);
2494 scu_unit_reset ((int) scu_unit_idx);
2495 return SCPE_OK;
2496 }
2497
2498 void scu_init (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
2499 {
2500 // One time only initializations
2501
2502 for (int u = 0; u < N_SCU_UNITS_MAX; u ++)
2503 {
2504 for (int p = 0; p < N_SCU_PORTS; p ++)
2505 {
2506 for (int s = 0; s < N_SCU_SUBPORTS; s ++)
2507 {
2508 scu[u].ports[p].dev_port[s] = -1;
2509 scu[u].ports[p].subport_enables[s] = false;
2510 scu[u].ports[p].xipmask[s] = false;
2511 // Invalid value for detecting uninitialized XIP mask.
2512 scu[u].ports[p].xipmaskval = N_SCU_SUBPORTS;
2513 }
2514 scu[u].ports[p].type = ADEV_NONE;
2515 scu[u].ports[p].is_exp = false;
2516 }
2517
2518 // ID: 0000 8034, 8035
2519 // 0001 Level 68 SC
2520 // 0010 Level 66 SCU
2521 scu [u].id = 02l; // 0b0010
2522 scu [u].mode_reg = 0; // used by T&D
2523 scu [u].elapsed_days = 0;
2524 }
2525
2526 }
2527
2528 t_stat scu_rmcm (uint scu_unit_idx, uint cpu_unit_udx, word36 * rega,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
2529 word36 * regq)
2530 {
2531 #if defined(TESTING)
2532 cpu_state_t * cpup = _cpup;
2533 #endif
2534 scu_t * up = scu + scu_unit_idx;
2535
2536 // Assume no mask register assigned
2537 * rega = 0;
2538 * regq = 0;
2539
2540 // Which port is cpu_unit_udx connected to? (i.e. which port did the
2541 // command come in on?
2542 int scu_port_num = -1; // The port that the rscr instruction was
2543 // received on
2544
2545 for (int pn = 0; pn < N_SCU_PORTS; pn ++)
2546 {
2547 for (int sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2548 {
2549 if (cables->scu_to_cpu[scu_unit_idx][pn][sn].in_use &&
2550 cables->scu_to_cpu[scu_unit_idx][pn][sn].cpu_unit_idx ==
2551 cpu_unit_udx)
2552 {
2553 scu_port_num = pn;
2554 goto gotit;
2555 }
2556 }
2557 }
2558
2559 gotit:;
2560
2561 //sim_printf ("rmcm scu_port_num %d\r\n", scu_port_num);
2562
2563 if (scu_port_num < 0)
2564 {
2565 sim_warn ("%s: can't find cpu port in the snarl of cables; "
2566 "scu_unit_no %d, cpu_unit_udx %d\r\n",
2567 __func__, scu_unit_idx, cpu_unit_udx);
2568 sim_debug (DBG_ERR, & scu_dev,
2569 "%s: can't find cpu port in the snarl of cables; "
2570 "scu_unit_no %d, cpu_unit_udx %d\r\n",
2571 __func__, scu_unit_idx, cpu_unit_udx);
2572 // Non 4MWs do a store fault
2573 return SCPE_OK;
2574 }
2575
2576 // A reg:
2577 // 0 15 16 31 32 35
2578 // IER 0-15 00000000 PER 0-3
2579 // Q reg:
2580 // 0 15 16 31 32 35
2581 // IER 16-32 00000000 PER 4-7
2582
2583 sim_debug (DBG_TRACE, & scu_dev, "rmcm selected scu port %u\r\n",
2584 scu_port_num);
2585 #if defined(THREADZ) || defined(LOCKLESS)
2586 lock_scu ();
2587 #endif
2588 uint mask_contents = 0;
2589 if (up -> mask_assignment [0] == (uint) scu_port_num)
2590 {
2591 mask_contents = up -> exec_intr_mask [0];
2592 sim_debug (DBG_TRACE, & scu_dev, "rmcm got mask %011o from pima A\r\n",
2593 mask_contents);
2594 }
2595 else if (up -> mask_assignment [1] == (uint) scu_port_num)
2596 {
2597 mask_contents = up -> exec_intr_mask [1];
2598 sim_debug (DBG_TRACE, & scu_dev, "rmcm got mask %011o from pima B\r\n",
2599 mask_contents);
2600 }
2601 mask_contents &= MASK32;
2602
2603 * rega = 0; //-V1048
2604 putbits36_16 (rega, 0, (mask_contents >> 16) & MASK16);
2605 putbits36_1 (rega, 32, (word1) up -> port_enable [0]);
2606 putbits36_1 (rega, 33, (word1) up -> port_enable [1]);
2607 putbits36_1 (rega, 34, (word1) up -> port_enable [2]);
2608 putbits36_1 (rega, 35, (word1) up -> port_enable [3]);
2609
2610 * regq = 0; //-V1048
2611 putbits36_16 (regq, 0, (mask_contents >> 0) & MASK16);
2612 putbits36_1 (regq, 32, (word1) up -> port_enable [4]);
2613 putbits36_1 (regq, 33, (word1) up -> port_enable [5]);
2614 putbits36_1 (regq, 34, (word1) up -> port_enable [6]);
2615 putbits36_1 (regq, 35, (word1) up -> port_enable [7]);
2616
2617 #if defined(THREADZ) || defined(LOCKLESS)
2618 unlock_scu ();
2619 #endif
2620 sim_debug (DBG_TRACE, & scu_dev,
2621 "RMCM returns %012"PRIo64" %012"PRIo64"\r\n",
2622 * rega, * regq);
2623 dump_intr_regs ("rmcm", scu_unit_idx);
2624 return SCPE_OK;
2625 }
2626
2627 t_stat scu_smcm (uint scu_unit_idx, uint cpu_unit_udx, word36 rega, word36 regq)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
2628 {
2629 #if defined(TESTING)
2630 cpu_state_t * cpup = _cpup;
2631 #endif
2632 sim_debug (DBG_TRACE, & scu_dev,
2633 "SMCM SCU unit %d CPU unit %d A %012"PRIo64" Q %012"PRIo64"\r\n",
2634 scu_unit_idx, cpu_unit_udx, rega, regq);
2635
2636 scu_t * up = scu + scu_unit_idx;
2637
2638 // Which port is cpu_unit_udx connected to? (i.e. which port did the
2639 // command come in on?
2640 int scu_port_num = -1; // The port that the rscr instruction was
2641 // received on
2642
2643 for (int pn = 0; pn < N_SCU_PORTS; pn ++)
2644 {
2645 for (int sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2646 {
2647 if (cables->scu_to_cpu[scu_unit_idx][pn][sn].in_use &&
2648 cables->scu_to_cpu[scu_unit_idx][pn][sn].cpu_unit_idx ==
2649 cpu_unit_udx)
2650 {
2651 scu_port_num = pn;
2652 goto gotit;
2653 }
2654 }
2655 }
2656 gotit:;
2657
2658 //sim_printf ("rmcm scu_port_num %d\r\n", scu_port_num);
2659
2660 if (scu_port_num < 0)
2661 {
2662 sim_warn ("%s: can't find cpu port in the snarl of cables; "
2663 "scu_unit_no %d, cpu_unit_udx %d\r\n",
2664 __func__, scu_unit_idx, cpu_unit_udx);
2665 return SCPE_OK;
2666 }
2667
2668 sim_debug (DBG_TRACE, & scu_dev, "SMCM SCU port num %d\r\n", scu_port_num);
2669
2670 // A reg:
2671 // 0 15 16 31 32 35
2672 // IER 0-15 00000000 PER 0-3
2673 // Q reg:
2674 // 0 15 16 31 32 35
2675 // IER 16-32 00000000 PER 4-7
2676
2677 uint imask =
2678 ((uint) getbits36_16(rega, 0) << 16) |
2679 ((uint) getbits36_16(regq, 0) << 0);
2680 #if defined(THREADZ) || defined(LOCKLESS)
2681 lock_scu ();
2682 #endif
2683 if (up -> mask_assignment [0] == (uint) scu_port_num)
2684 {
2685 up -> exec_intr_mask [0] = imask;
2686 sim_debug (DBG_TRACE, & scu_dev, "SMCM intr mask 0 set to %011o\r\n",
2687 imask);
2688 }
2689 else if (up -> mask_assignment [1] == (uint) scu_port_num)
2690 {
2691 up -> exec_intr_mask [1] = imask;
2692 sim_debug (DBG_TRACE, & scu_dev, "SMCM intr mask 1 set to %011o\r\n",
2693 imask);
2694 }
2695
2696 scu [scu_unit_idx].port_enable [0] = (uint) getbits36_1 (rega, 32);
2697 scu [scu_unit_idx].port_enable [1] = (uint) getbits36_1 (rega, 33);
2698 scu [scu_unit_idx].port_enable [2] = (uint) getbits36_1 (rega, 34);
2699 scu [scu_unit_idx].port_enable [3] = (uint) getbits36_1 (rega, 35);
2700 scu [scu_unit_idx].port_enable [4] = (uint) getbits36_1 (regq, 32);
2701 scu [scu_unit_idx].port_enable [5] = (uint) getbits36_1 (regq, 33);
2702 scu [scu_unit_idx].port_enable [6] = (uint) getbits36_1 (regq, 34);
2703 scu [scu_unit_idx].port_enable [7] = (uint) getbits36_1 (regq, 35);
2704
2705 dump_intr_regs ("smcm", scu_unit_idx);
2706 deliver_interrupts (NULL, scu_unit_idx);
2707 #if defined(THREADZ) || defined(LOCKLESS)
2708 unlock_scu ();
2709 #endif
2710
2711 return SCPE_OK;
2712 }