1 /*
2 * vim: filetype=c:tabstop=4:ai:expandtab
3 * SPDX-License-Identifier: ICU
4 * SPDX-License-Identifier: Multics
5 * scspell-id: 4182f303-f62e-11ec-9475-80ee73e9b8e7
6 *
7 * ---------------------------------------------------------------------------
8 *
9 * Copyright (c) 2007-2013 Michael Mondy
10 * Copyright (c) 2012-2016 Harry Reed
11 * Copyright (c) 2013-2016 Charles Anthony
12 * Copyright (c) 2017 Michal Tomek
13 * Copyright (c) 2021-2025 The DPS8M Development Team
14 *
15 * This software is made available under the terms of the ICU License.
16 * See the LICENSE.md file at the top-level directory of this distribution.
17 *
18 * ---------------------------------------------------------------------------
19 *
20 * This source file may contain code comments that adapt, include, and/or
21 * incorporate Multics program code and/or documentation distributed under
22 * the Multics License. In the event of any discrepancy between code
23 * comments herein and the original Multics materials, the original Multics
24 * materials should be considered authoritative unless otherwise noted.
25 * For more details and historical background, see the LICENSE.md file at
26 * the top-level directory of this distribution.
27 *
28 * ---------------------------------------------------------------------------
29 */
30
31 #include <stdio.h>
32
33 #include "dps8.h"
34 #include "dps8_sys.h"
35 #include "dps8_iom.h"
36 #include "dps8_cable.h"
37 #include "dps8_cpu.h"
38 #include "dps8_faults.h"
39 #include "dps8_scu.h"
40 #include "dps8_append.h"
41 #include "dps8_ins.h"
42 #include "dps8_utils.h"
43 #if defined(THREADZ) || defined(LOCKLESS)
44 # include "threadz.h"
45 #endif
46
47 #define DBG_CTR cpu.cycleCnt
48
49 /*
50 * FAULT RECOGNITION
51 * For the discussion following, the term "function" is defined as a major processor functional
52 * cycle. Examples are: APPEND CYCLE, CA CYCLE, INSTRUCTION FETCH CYCLE, OPERAND STORE CYCLE,
53 * DIVIDE EXECUTION CYCLE. Some of these cycles are discussed in various sections of this manual.
54 * Faults in groups 1 and 2 cause the processor to abort all functions immediately by entering a
55 * FAULT CYCLE. Faults in group 3 cause the processor to "close out" current functions without
56 * taking any irrevocable action (such as setting PTW.U in an APPEND CYCLE or modifying an
57 * indirect word in a CA CYCLE), then to discard any pending functions (such as an APPEND CYCLE
58 * needed during a CA CYCLE), and to enter a FAULT CYCLE. Faults in group 4 cause the processor
59 * to suspend overlapped operation, to complete current and pending functions for the current
60 * instruction, and then to enter a FAULT CYCLE. Faults in groups 5 or 6 are normally detected
61 * during virtual address formation and instruction decode. These faults cause the processor to
62 * suspend overlapped operation, to complete the current and pending instructions, and to enter
63 * a FAULT CYCLE. If a fault in a higher priority group is generated by the execution of the
64 * current or pending instructions, that higher priority fault will take precedence and the
65 * group 5 or 6 fault will be lost. If a group 5 or 6 fault is detected during execution of the
66 * current instruction (e.g., an access violation, out of segment bounds, fault during certain
67 * interruptible EIS instructions), the instruction is considered "complete" upon detection of
68 * the fault. Faults in group 7 are held and processed (with interrupts) at the completion of
69 * the current instruction pair. Group 7 faults are inhibitable by setting bit 28 of the
70 * instruction word. Faults in groups 3 through 6 must wait for the system controller to
71 * acknowledge the last access request before entering the FAULT CYCLE.
72 */
73
74 /*
75 * Table 7-1. List of Faults
76 *
77 * Decimal fault Octal (1) Fault Fault name Priority Group
78 * number fault address mnemonic
79 * 0 ; 0 ; sdf ; Shutdown ; 27 ; 7
80 * 1 ; 2 ; str ; Store ; 10 ; 4
81 * 2 ; 4 ; mme ; Master mode entry 1 ; 11 ; 5
82 * 3 ; 6 ; f1 ; Fault tag 1 ; 17 ; 5
83 * 4 ; 10 ; tro ; Timer runout ; 26 ; 7
84 * 5 ; 12 ; cmd ; Command ; 9 ; 4
85 * 6 ; 14 ; drl ; Derail ; 15 ; 5
86 * 7 ; 16 ; luf ; Lockup ; 5 ; 4
87 * 8 ; 20 ; con ; Connect ; 25 ; 7
88 * 9 ; 22 ; par ; Parity ; 8 ; 4
89 * 10 ; 24 ; ipr ; Illegal procedure ; 16 ; 5
90 * 11 ; 26 ; onc ; Operation not complete ; 4 ; 2
91 * 12 ; 30 ; suf ; Startup ; 1 ; 1
92 * 13 ; 32 ; ofl ; Overflow ; 7 ; 3
93 * 14 ; 34 ; div ; Divide check ; 6 ; 3
94 * 15 ; 36 ; exf ; Execute ; 2 ; 1
95 * 16 ; 40 ; df0 ; Directed fault 0 ; 20 ; 6
96 * 17 ; 42 ; df1 ; Directed fault 1 ; 21 ; 6
97 * 18 ; 44 ; df2 ; Directed fault 2 ; 22 ; 6
98 * 19 ; 46 ; df3 ; Directed fault 3 ; 23 ; 6
99 * 20 ; 50 ; acv ; Access violation ; 24 ; 6
100 * 21 ; 52 ; mme2 ; Master mode entry 2 ; 12 ; 5
101 * 22 ; 54 ; mme3 ; Master mode entry 3 ; 13 ; 5
102 * 23 ; 56 ; mme4 ; Master mode entry 4 ; 14 ; 5
103 * 24 ; 60 ; f2 ; Fault tag 2 ; 18 ; 5
104 * 25 ; 62 ; f3 ; Fault tag 3 ; 19 ; 5
105 * 26 ; 64 ; ; Unassigned ; ;
106 * 27 ; 66 ; ; Unassigned ; ;
107 */
108
109 #if !defined(QUIET_UNUSED)
110 static dps8faults _faultsP[] = { // sorted by priority
111 // number address mnemonic name Priority Group
112 { 12, 030, "suf", "Startup", 1, 1, false },
113 { 15, 036, "exf", "Execute", 2, 1, false },
114 { 31, 076, "trb", "Trouble", 3, 2, false },
115 { 11, 026, "onc", "Operation not complete", 4, 2, false },
116 { 7, 016, "luf", "Lockup", 5, 4, false },
117 { 14, 034, "div", "Divide check", 6, 3, false },
118 { 13, 032, "ofl", "Overflow", 7, 3, false },
119 { 9, 022, "par", "Parity", 8, 4, false },
120 { 5, 012, "cmd", "Command", 9, 4, false },
121 { 1, 2, "str", "Store", 10, 4, false },
122 { 2, 4, "mme", "Master mode entry 1", 11, 5, false },
123 { 21, 052, "mme2", "Master mode entry 2", 12, 5, false },
124 { 22, 054, "mme3", "Master mode entry 3", 13, 5, false },
125 { 23, 056, "mme4", "Master mode entry 4", 14, 5, false },
126 { 6, 014, "drl", "Derail", 15, 5, false },
127 { 10, 024, "ipr", "Illegal procedure", 16, 5, false },
128 { 3, 06, "f1", "Fault tag 1", 17, 5, false },
129 { 24, 060, "f2", "Fault tag 2", 18, 5, false },
130 { 25, 062, "f3", "Fault tag 3", 19, 5, false },
131 { 16, 040, "df0", "Directed fault 0", 20, 6, false },
132 { 17, 042, "df1", "Directed fault 1", 21, 6, false },
133 { 18, 044, "df2", "Directed fault 2", 22, 6, false },
134 { 19, 046, "df3", "Directed fault 3", 23, 6, false },
135 { 20, 050, "acv", "Access violation", 24, 6, false },
136 { 8, 020, "con", "Connect", 25, 7, false },
137 { 4, 010, "tro", "Timer runout", 26, 7, false },
138 { 0, 0, "sdf", "Shutdown", 27, 7, false },
139 { 26, 064, "???", "Unassigned", -1, -1, false },
140 { 27, 066, "???", "Unassigned", -1, -1, false },
141 { -1, -1, NULL, NULL, -1, -1, false }
142 };
143
144 static dps8faults _faults[] = { // sorted by number
145 // number address mnemonic name Priority Group
146 { 0, 0, "sdf", "Shutdown", 27, 7, false },
147 { 1, 2, "str", "Store", 10, 4, false },
148 { 2, 4, "mme", "Master mode entry 1", 11, 5, false },
149 { 3, 06, "f1", "Fault tag 1", 17, 5, false },
150 { 4, 010, "tro", "Timer runout", 26, 7, false },
151 { 5, 012, "cmd", "Command", 9, 4, false },
152 { 6, 014, "drl", "Derail", 15, 5, false },
153 { 7, 016, "luf", "Lockup", 5, 4, false },
154 { 8, 020, "con", "Connect", 25, 7, false },
155 { 9, 022, "par", "Parity", 8, 4, false },
156 { 10, 024, "ipr", "Illegal procedure", 16, 5, false },
157 { 11, 026, "onc", "Operation not complete", 4, 2, false },
158 { 12, 030, "suf", "Startup", 1, 1, false },
159 { 13, 032, "ofl", "Overflow", 7, 3, false },
160 { 14, 034, "div", "Divide check", 6, 3, false },
161 { 15, 036, "exf", "Execute", 2, 1, false },
162 { 16, 040, "df0", "Directed fault 0", 20, 6, false },
163 { 17, 042, "df1", "Directed fault 1", 21, 6, false },
164 { 18, 044, "df2", "Directed fault 2", 22, 6, false },
165 { 19, 046, "df3", "Directed fault 3", 23, 6, false },
166 { 20, 050, "acv", "Access violation", 24, 6, false },
167 { 21, 052, "mme2", "Master mode entry 2", 12, 5, false },
168 { 22, 054, "mme3", "Master mode entry 3", 13, 5, false },
169 { 23, 056, "mme4", "Master mode entry 4", 14, 5, false },
170 { 24, 060, "f2", "Fault tag 2", 18, 5, false },
171 { 25, 062, "f3", "Fault tag 3", 19, 5, false },
172 { 26, 064, "???", "Unassigned", -1, -1, false },
173 { 27, 066, "???", "Unassigned", -1, -1, false },
174 { 28, 070, "???", "Unassigned", -1, -1, false },
175 { 29, 072, "???", "Unassigned", -1, -1, false },
176 { 30, 074, "???", "Unassigned", -1, -1, false },
177 { 31, 076, "trb", "Trouble", 3, 2, false },
178 { -1, -1, NULL, NULL, -1, -1, false }
179 };
180 #endif /* if !defined(QUIET_UNUSED) */
181
182 char * faultNames [N_FAULTS] =
183 {
184 "Shutdown",
185 "Store",
186 "Master mode entry 1",
187 "Fault tag 1",
188 "Timer runout",
189 "Command",
190 "Derail",
191 "Lockup",
192 "Connect",
193 "Parity",
194 "Illegal procedure",
195 "Operation not complete",
196 "Startup",
197 "Overflow",
198 "Divide check",
199 "Execute",
200 "Directed fault 0",
201 "Directed fault 1",
202 "Directed fault 2",
203 "Directed fault 3",
204 "Access violation",
205 "Master mode entry 2",
206 "Master mode entry 3",
207 "Master mode entry 4",
208 "Fault tag 2",
209 "Fault tag 3",
210 "Unassigned 26",
211 "Unassigned 27",
212 "Unassigned 28",
213 "Unassigned 29",
214 "Unassigned 30",
215 "Trouble"
216 };
217 //bool pending_fault = false; // true when a fault has been signalled, but not processed
218
219 #if !defined(QUIET_UNUSED)
220 static bool port_interrupts[8] = { false, false, false, false, false, false, false, false };
221 #endif /* if !defined(QUIET_UNUSED) */
222
223 //-----------------------------------------------------------------------------
224 // *** Constants, unchanging lookup tables, etc
225
226 #if !defined (QUIET_UNUSED)
227 static int fault2group[32] = {
228 // from AL39, page 7-3
229 7, 4, 5, 5, 7, 4, 5, 4,
230 7, 4, 5, 2, 1, 3, 3, 1,
231 6, 6, 6, 6, 6, 5, 5, 5,
232 5, 5, 0, 0, 0, 0, 0, 2
233 };
234
235 static int fault2prio[32] = {
236 // from AL39, page 7-3
237 27, 10, 11, 17, 26, 9, 15, 5,
238 25, 8, 16, 4, 1, 7, 6, 2,
239 20, 21, 22, 23, 24, 12, 13, 14,
240 18, 19, 0, 0, 0, 0, 0, 3
241 };
242 #endif /* if !defined(QUIET_UNUSED) */
243
244 /*
245 * fault handler(s).
246 */
247
248 #if defined(TESTING)
249 // We stash a few things for debugging; they are accessed by emCall.
250 static word18 fault_ic;
251 static word15 fault_psr;
252 static char fault_msg [1024];
253
254 void emCallReportFault (void)
/* ![[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)
*/
255 {
256 cpu_state_t * cpup = _cpup;
257 sim_printf ("fault report:\r\n");
258 sim_printf (" fault number %d (%o)\r\n", cpu . faultNumber, cpu . faultNumber);
259 sim_printf (" subfault number %llu (%llo)\r\n", (unsigned long long) cpu.subFault.bits,
260 (unsigned long long)cpu.subFault.bits);
261 sim_printf (" faulting address %05o:%06o\r\n", fault_psr, fault_ic);
262 sim_printf (" msg %s\r\n", fault_msg);
263 }
264 #endif /* if defined(TESTING) */
265
266 void clearFaultCycle (cpu_state_t * cpup)
/* ![[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)
*/
267 {
268 cpu . bTroubleFaultCycle = false;
269 }
270
271 /*
272
273 Faults in groups 1 and 2 cause the processor to abort all functions immediately
274 by entering a FAULT CYCLE.
275
276 Faults in group 3 cause the processor to "close out" current functions without
277 taking any irrevocable action (such as setting PTW.U in an APPEND CYCLE or
278 modifying an indirect word in a CA CYCLE), then to discard any pending
279 functions (such as an APPEND CYCLE needed during a CA CYCLE), and to enter a
280 FAULT CYCLE.
281
282 Faults in group 4 cause the processor to suspend overlapped operation, to
283 complete current and pending functions for the current instruction, and then to
284 enter a FAULT CYCLE.
285
286 Faults in groups 5 or 6 are normally detected during virtual address formation
287 and instruction decode. These faults cause the processor to suspend overlapped
288 operation, to complete the current and pending instructions, and to enter a
289 FAULT CYCLE. If a fault in a higher priority group is generated by the
290 execution of the current or pending instructions, that higher priority fault
291 will take precedence and the group 5 or 6 fault will be lost. If a group 5 or 6
292 fault is detected during execution of the current instruction (e.g., an access
293 violation, out of segment bounds, fault during certain interruptible EIS
294 instructions), the instruction is considered "complete" upon detection of the
295 fault.
296
297 Faults in group 7 are held and processed (with interrupts) at the completion
298 of the current instruction pair.
299
300 Group 7 faults are inhibitable by setting bit 28 of the instruction word.
301
302 Faults in groups 3 through 6 must wait for the system controller to acknowledge
303 the last access request before entering the FAULT CYCLE.
304
305 After much rumination here are my thoughts for fault processing .....
306
307 For now, at least, we must remember a few things:
308
309 1) We only have 1 cpu so we have few & limited async faults - shutdown, TRO,
310 etc.
311 2) We have no overlapping instruction execution
312 3) Because of 2) we have no pending instructions
313 4) We have no system controller to wait for
314
315 Group 1 & 2 faults can be processed immediately and then proceed to next
316 instruction as long as no transfer prevents us from returning from the XED pair.
317
318 Group 3 faults will probably also execute immediately since a G3 fault causes
319 "the processor to "close out" current functions without taking any irrevocable
320 action (such as setting PTW.U in an APPEND CYCLE or modifying an indirect word
321 in a CA CYCLE), then to discard any pending functions (such as an APPEND CYCLE
322 needed during a CA CYCLE), and to enter a FAULT CYCLE."
323
324 Group 4 faults will probably also execute immediately since a G4 fault causes
325 "the processor to suspend overlapped operation, to complete current and pending
326 functions for the current instruction, and then to enter a FAULT CYCLE."
327
328 Group 5 & 6 faults will probably also execute immediately because "if a group 5
329 or 6 fault is detected during execution of the current instruction (e.g., an
330 access violation, out of segment bounds, fault during certain interruptible EIS
331 instructions), the instruction is considered "complete" upon detection of the
332 fault." However, remember "If a fault in a higher priority group is generated
333 by the execution of the current or pending instructions, that higher priority
334 fault will take precedence and the group 5 or 6 fault will be lost. If a group
335 5 or 6 fault is detected during execution of the current instruction (e.g., an
336 access violation, out of segment bounds, fault during certain interruptible EIS
337 instructions), the instruction is considered "complete" upon detection of the
338 fault."
339
340 For further justification of immediate execution since "Faults in groups 3
341 through 6 must wait for the system controller to acknowledge the last access
342 request before entering the FAULT CYCLE."
343
344 Group 7 faults will be processed after next even instruction decode instruction
345 decode, but before instruction execution. In this way we can actually use
346 bit-28 tp inhibit interrupts
347
348 */
349
350 #if defined(LOOPTRC)
351 # include <time.h>
352 void elapsedtime (void);
353 #endif /* if defined(LOOPRTC) */
354
355 #if !defined(NEED_128)
356 const _fault_subtype fst_zero = (_fault_subtype) {.bits=0};
357 const _fault_subtype fst_acv9 = (_fault_subtype) {.fault_acv_subtype=ACV9};
358 const _fault_subtype fst_acv15 = (_fault_subtype) {.fault_acv_subtype=ACV15};
359 const _fault_subtype fst_ill_mod = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_MOD};
360 const _fault_subtype fst_ill_proc = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_PROC};
361 const _fault_subtype fst_ill_dig = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_DIG};
362 const _fault_subtype fst_ill_op = (_fault_subtype) {.fault_ipr_subtype=FR_ILL_OP};
363 const _fault_subtype fst_str_oob = (_fault_subtype) {.fault_str_subtype=flt_str_oob};
364 const _fault_subtype fst_str_nea = (_fault_subtype) {.fault_str_subtype=flt_str_nea};
365 const _fault_subtype fst_str_ptr = (_fault_subtype) {.fault_str_subtype=flt_str_ill_ptr};
366 const _fault_subtype fst_cmd_lprpn = (_fault_subtype) {.fault_cmd_subtype=flt_cmd_lprpn_bits};
367 const _fault_subtype fst_cmd_ctl = (_fault_subtype) {.fault_cmd_subtype=flt_cmd_not_control};
368 const _fault_subtype fst_onc_nem = (_fault_subtype) {.fault_onc_subtype=flt_onc_nem};
369 #endif /* if !defined(NEED_128) */
370 // CANFAULT
371 void doFault (_fault faultNumber, _fault_subtype subFault,
/* ![[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)
*/
372 const char * faultMsg)
373 {
374 cpu_state_t * cpup = _cpup;
375
376 #if defined(LOOPTRC)
377 if (faultNumber == FAULT_TRO)
378 {
379 elapsedtime ();
380 sim_printf (" TRO PSR:IC %05o:%06o\r\n", cpu.PPR.PSR, cpu.PPR.IC);
381 }
382 else if (faultNumber == FAULT_ACV)
383 {
384 elapsedtime ();
385 sim_printf (" ACV %012llo PSR:IC %05o:%06o\r\n", subFault.bits, cpu.PPR.PSR, cpu.PPR.IC);
386 }
387 #endif /* if defined(LOOPRTC) */
388 //if (current_running_cpu_idx)
389 //sim_printf ("Fault %d(0%0o), sub %ld(0%lo), dfc %c, '%s'\r\n",
390 //faultNumber, faultNumber, subFault, subFault,
391 //cpu . bTroubleFaultCycle ? 'Y' : 'N', faultMsg);
392 //if (current_running_cpu_idx)
393 //sim_printf ("xde %d xdo %d\r\n", cpu.cu.xde, cpu.cu.xdo);
394 sim_debug (DBG_FAULT, & cpu_dev,
395 "Fault %d(0%0o), sub %"PRIu64"(0%"PRIo64"), dfc %c, '%s'\r\n",
396 faultNumber, faultNumber, subFault.bits, subFault.bits,
397 cpu . bTroubleFaultCycle ? 'Y' : 'N', faultMsg);
398 #if defined(PROFILER)
399 # if !defined(GNU_ATOMICS)
400 # error PROFILER requires GNU_ATOMICS
401 # endif /* if !defined(GNU_ATOMICS) */
402 //__atomic_add_fetch (& cpu.faults[faultNumber], 1u, __ATOMIC_ACQUIRE);
403 (void)atomic_fetch_add_explicit(&cpu.faults[faultNumber], 1u, memory_order_acquire);
404 #endif /* if defined(PROFILER) */
405 #if defined(TESTING)
406 HDBGFault (faultNumber, subFault, faultMsg, "");
407 #endif /* if defined(TESTING) */
408 #if !defined(SPEED)
409 if_sim_debug (DBG_FAULT, & cpu_dev)
410 traceInstruction (DBG_FAULT);
411 #endif /* if !defined(SPEED) */
412
413 PNL (cpu.DACVpDF = faultNumber >= FAULT_DF0 && faultNumber <= FAULT_ACV;)
414
415 #if defined(TESTING)
416 // some debugging support stuff
417 fault_psr = cpu . PPR.PSR;
418 fault_ic = cpu . PPR.IC;
419 strcpy (fault_msg, faultMsg);
420 #endif /* if defined(TESTING) */
421
422 //if (faultNumber < 0 || faultNumber > 31)
423 if (faultNumber & ~037U) // quicker?
424 {
425 sim_printf ("fault(out-of-range): %d %llo '%s'\r\n",
426 faultNumber, (unsigned long long)subFault.bits,
427 faultMsg ? faultMsg : "?");
428 sim_warn ("fault out-of-range\r\n");
429 faultNumber = FAULT_TRB; // XXX Really? this is a simulator bug, not a trouble fault
430 }
431
432 cpu.faultNumber = faultNumber;
433 cpu.subFault = subFault;
434 cpu.faultCnt [faultNumber] ++;
435
436 // "The occurrence of a fault or interrupt sets the cache-to-register mode bit to OFF." a:AL39/cmr1
437 CPTUR (cptUseCMR);
438 cpu.CMR.csh_reg = 0;
439
440 // Increment FCT
441
442 word3 FCT = cpu.cu.APUCycleBits & MASK3;
443 FCT = (FCT + 1u) & MASK3;
444 cpu.cu.APUCycleBits = (word12) ((cpu.cu.APUCycleBits & 07770) | FCT);
445
446 // Set fault register bits
447
448 CPTUR (cptUseFR);
449 if (faultNumber == FAULT_IPR)
450 {
451
452
453
454
455
456
457
458
459
460
461 cpu . faultRegister [0] |= subFault.bits;
462
463 }
464 else if (faultNumber == FAULT_ONC && subFault.fault_onc_subtype == flt_onc_nem)
465 {
466 cpu . faultRegister [0] |= FR_NEM;
467 }
468 else if (faultNumber == FAULT_STR)
469 {
470 if (subFault.fault_str_subtype == flt_str_oob)
471 cpu . faultRegister [0] |= FR_OOB;
472 //else if (subFault.fault_str_subtype == flt_str_ill_ptr)
473 //cpu . faultRegister [0] |= ?; // XXX
474 //else if (subFault.fault_str_subtype == flt_str_nea)
475 //cpu . faultRegister [0] |= ?; // XXX
476 }
477 else if (faultNumber == FAULT_CON)
478 {
479 switch (subFault.fault_con_subtype)
480 {
481 case con_a:
482 cpu . faultRegister [0] |= FR_CON_A;
483 break;
484 case con_b:
485 cpu . faultRegister [0] |= FR_CON_B;
486 break;
487 case con_c:
488 cpu . faultRegister [0] |= FR_CON_C;
489 break;
490 case con_d:
491 cpu . faultRegister [0] |= FR_CON_D;
492 break;
493 default:
494 sim_warn ("FAULT_CON can't map port %lo\r\n", (long unsigned) subFault.fault_con_subtype);
495 break;
496 }
497 }
498
499 // Set cu word1 fault bits
500
501 cpu . cu . IRO_ISN = 0;
502 cpu . cu . OEB_IOC = 0;
503 cpu . cu . EOFF_IAIM = 0;
504 cpu . cu . ORB_ISP = 0;
505 cpu . cu . ROFF_IPR = 0;
506 cpu . cu . OWB_NEA = 0;
507 cpu . cu . WOFF_OOB = 0;
508 cpu . cu . NO_GA = 0;
509 cpu . cu . OCB = 0;
510 cpu . cu . OCALL = 0;
511 cpu . cu . BOC = 0;
512 DPS8M_ (cpu . cu . PTWAM_ER = 0;)
513 cpu . cu . CRT = 0;
514 cpu . cu . RALR = 0;
515 cpu . cu . SDWAM_ER = 0;
516 cpu . cu . OOSB = 0;
517 cpu . cu . PARU = 0;
518 cpu . cu . PARL = 0;
519 cpu . cu . ONC1 = 0;
520 cpu . cu . ONC2 = 0;
521 cpu . cu . IA = 0;
522 cpu . cu . IACHN = 0;
523 cpu . cu . CNCHN = (faultNumber == FAULT_CON) ? subFault.fault_con_subtype & MASK3 : 0;
524
525 // Set control unit 'fault occurred during instruction fetch' flag
526 cpu . cu . FIF = cpu . cycle == FETCH_cycle ? 1 : 0;
527 cpu . cu . FI_ADDR = (word5) faultNumber;
528
529 // XXX Under what conditions should this be set?
530 // Assume no
531 // Reading Multics source, it seems like Multics is setting this bit; I'm going
532 // to assume that the h/w also sets it to 0, and the s/w has to explicitly set it on.
533 cpu . cu . rfi = 0;
534
535 // Try to decide if this a MIF fault (fault during EIS instruction)
536 // EIS instructions are not used in fault/interrupt pairs, so the
537 // only time an EIS instruction could be executing is during EXEC_cycle.
538 // I am also assuming that only multi-word EIS instructions are of interest.
539 // Testing faultNumber fixes ISOLTS 890-04a
540 // fixes 890-04a and 791 / 792
541 SC_I_MIF (cpu.cycle == EXEC_cycle &&
542 (cpu.currentInstruction.info->ndes > 0 ||
543 (faultNumber == FAULT_IPR && (subFault.fault_ipr_subtype & FR_ILL_OP_CONST) &&
544 cpu.currentInstruction.opcodeX &&
545 (cpu.currentInstruction.opcode & 0410) == 0)));
546 sim_debug (DBG_TRACEEXT, & cpu_dev, "MIF %o\r\n", TST_I_MIF);
547
548
549
550
551
552
553
554
555
556
557
558
559 if (faultNumber == FAULT_ACV)
560 {
561 // This is annoyingly inefficient since the subFault value
562 // is bitwise the same as the upper half of CU word1;
563 // if the upper half were not broken out, then this would be
564 // cpu . cu . word1_upper_half = subFault.
565
566 if (subFault.fault_acv_subtype & ACV0)
567 cpu . cu . IRO_ISN = 1;
568 if (subFault.fault_acv_subtype & ACV1)
569 cpu . cu . OEB_IOC = 1;
570 if (subFault.fault_acv_subtype & ACV2)
571 cpu . cu . EOFF_IAIM = 1;
572 if (subFault.fault_acv_subtype & ACV3)
573 cpu . cu . ORB_ISP = 1;
574 if (subFault.fault_acv_subtype & ACV4)
575 cpu . cu . ROFF_IPR = 1;
576 if (subFault.fault_acv_subtype & ACV5)
577 cpu . cu . OWB_NEA = 1;
578 if (subFault.fault_acv_subtype & ACV6)
579 cpu . cu . WOFF_OOB = 1;
580 if (subFault.fault_acv_subtype & ACV7)
581 cpu . cu . NO_GA = 1;
582 if (subFault.fault_acv_subtype & ACV8)
583 cpu . cu . OCB = 1;
584 if (subFault.fault_acv_subtype & ACV9)
585 cpu . cu . OCALL = 1;
586 if (subFault.fault_acv_subtype & ACV10)
587 cpu . cu . BOC = 1;
588 if (subFault.fault_acv_subtype & ACV11)
589 cpu . cu . PTWAM_ER = 1;
590 if (subFault.fault_acv_subtype & ACV12)
591 cpu . cu . CRT = 1;
592 if (subFault.fault_acv_subtype & ACV13)
593 cpu . cu . RALR = 1;
594 if (subFault.fault_acv_subtype & ACV14)
595 cpu . cu . SDWAM_ER = 1;
596 if (subFault.fault_acv_subtype & ACV15)
597 cpu . cu . OOSB = 1;
598 }
599 else if (faultNumber == FAULT_STR)
600 {
601 if (subFault.fault_str_subtype == flt_str_oob)
602 cpu . cu . WOFF_OOB = 1;
603 //else if (subFault.fault_str_subtype == flt_str_ill_ptr)
604 //cpu . cu . ??? = 1; // XXX
605 else if (subFault.fault_str_subtype == flt_str_nea)
606 cpu . cu . OWB_NEA = 1;
607 }
608 else if (faultNumber == FAULT_IPR)
609 {
610 if (subFault.fault_ipr_subtype & FR_ILL_OP_CONST)
611 cpu . cu . OEB_IOC = 1;
612 if (subFault.fault_ipr_subtype & FR_ILL_MOD_CONST)
613 cpu . cu . EOFF_IAIM = 1;
614 if (subFault.fault_ipr_subtype & FR_ILL_SLV_CONST)
615 cpu . cu . ORB_ISP = 1;
616 if (subFault.fault_ipr_subtype & FR_ILL_DIG)
617 cpu . cu . ROFF_IPR = 1;
618 }
619 else if (faultNumber == FAULT_CMD)
620 {
621 if (subFault.fault_cmd_subtype == flt_cmd_lprpn_bits)
622 cpu . cu . IA = 0;
623 else if (subFault.fault_cmd_subtype == flt_cmd_not_control)
624 cpu . cu . IA = 010;
625 }
626
627 L68_ (
628 // History registers
629 // IHRRS; AL39 pg 47
630 // History register lock control. If this bit is set ON, set STROBE ยข
631 // (bit 30, key k) OFF, locking the history registers for all faults
632 // including the floating faults.
633 CPTUR (cptUseMR);
634 if (cpu.MR.emr && cpu.MR.ihrrs)
635 {
636 cpu.MR.ihr = 0;
637 }
638 )
639 DPS8M_ (
640 // History registers
641 // IHRRS; AL39 pg 49
642 // Additional resetting of bit 30. If bit 31 = 1, the following faults also
643 // reset bit 30:
644 // Lock Up
645 // Parity
646 // Command
647 // Store
648 // Illegal Procedure
649 // Shutdown
650 if (cpu.MR.emr && cpu.MR.ihrrs)
651 {
652 if (faultNumber == FAULT_LUF ||
653 faultNumber == FAULT_PAR ||
654 faultNumber == FAULT_CMD ||
655 faultNumber == FAULT_STR ||
656 faultNumber == FAULT_IPR ||
657 faultNumber == FAULT_SDF)
658 {
659 cpu.MR.ihr = 0;
660 }
661 }
662 // Enable History Registers. This bit will be reset by ... an Op Not
663 // Complete fault. It may be reset by other faults (see bit 31).
664 if (faultNumber == FAULT_ONC)
665 {
666 cpu.MR.ihr = 0;
667 }
668 )
669
670 // If already in a FAULT CYCLE then signal trouble fault
671
672 if (cpu.cycle == FAULT_EXEC_cycle)
673 {
674 sim_debug (DBG_CYCLE, & cpu_dev, "Changing fault number to Trouble fault\r\n");
675
676 cpu.faultNumber = FAULT_TRB;
677 cpu.cu.FI_ADDR = FAULT_TRB;
678 cpu.subFault.bits = 0; // XXX ???
679 if (cpu.bTroubleFaultCycle)
680 {
681 #if !defined(THREADZ) && !defined(LOCKLESS)
682 # if !defined(PANEL68)
683 if ((! sample_interrupts (cpup)) &&
684 (sim_qcount () == 0)) // XXX If clk_svc is implemented it will
685 // break this logic
686 {
687 sim_printf \
688 ("Fault cascade @0%06o with no interrupts pending and no events in queue\r\n",
689 cpu.PPR.IC);
690 sim_printf("\r\nCycles = %"PRId64"\r\n", cpu.cycleCnt);
691 sim_printf("\r\nInstructions = %"PRId64"\r\n", cpu.instrCnt);
692 //stop_reason = STOP_FLT_CASCADE;
693 longjmp (cpu.jmpMain, JMP_STOP);
694 }
695 # endif /* if !defined(PANEL68) */
696 #endif
697 }
698 else
699 {
700 //-- f = &_faults[FAULT_TRB];
701 cpu . bTroubleFaultCycle = true;
702 }
703 }
704 else
705 {
706 cpu . bTroubleFaultCycle = false;
707 }
708
709 // If doInstruction faults, the instruction cycle counter doesn't get
710 // bumped.
711 if (cpu . cycle == EXEC_cycle)
712 cpu.instrCnt ++;
713
714 cpu . cycle = FAULT_cycle;
715 sim_debug (DBG_CYCLE, & cpu_dev, "Setting cycle to FAULT_cycle\r\n");
716 longjmp (cpu.jmpMain, JMP_REENTRY);
717 #if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC)
718 /*NOTREACHED*/ /* unreachable */
719 abort(); /* not reached */
720 #endif
721 }
722
723 void do_FFV_fault (cpu_state_t * cpup, uint fault_number, const char * fault_msg)
/* ![[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)
*/
724 {
725 sim_debug (DBG_FAULT, & cpu_dev,
726 "Floating fault %d '%s'\r\n",
727 fault_number, fault_msg);
728 #if !defined(SPEED)
729 if_sim_debug (DBG_FAULT, & cpu_dev)
730 traceInstruction (DBG_FAULT);
731 #endif /* if !defined(SPEED) */
732
733 if (fault_number < 1 || fault_number > 3)
734 {
735 sim_printf ("floating fault(out-of-range): %d '%s'\r\n",
736 fault_number, fault_msg ? fault_msg : "?");
737 sim_warn ("fault out-of-range\r\n");
738 }
739
740 cpu.FFV_fault_number = fault_number;
741 cpu.faultNumber = fault_number;
742
743 // "The occurrence of a fault or interrupt sets the
744 // cache-to-register mode bit to OFF." a:AL39/cmr1
745 CPTUR (cptUseCMR);
746 cpu.CMR.csh_reg = 0;
747
748 // Increment FCT
749
750 word3 FCT = cpu.cu.APUCycleBits & MASK3;
751 FCT = (FCT + 1) & MASK3;
752 cpu.cu.APUCycleBits = (word12) ((cpu.cu.APUCycleBits & 07770) | FCT);
753
754 // Set fault register bits
755 CPTUR (cptUseFR);
756 cpu.faultRegister [0] = 0;
757
758 // Set cu word1 fault bits
759
760 cpu.cu.IRO_ISN = 0;
761 cpu.cu.OEB_IOC = 0;
762 cpu.cu.EOFF_IAIM = 0;
763 cpu.cu.ORB_ISP = 0;
764 cpu.cu.ROFF_IPR = 0;
765 cpu.cu.OWB_NEA = 0;
766 cpu.cu.WOFF_OOB = 0;
767 cpu.cu.NO_GA = 0;
768 cpu.cu.OCB = 0;
769 cpu.cu.OCALL = 0;
770 cpu.cu.BOC = 0;
771 // FFVs are L68 only, so we don't need this:
772 //# if defined(DPS8M)
773 //cpu.cu.PTWAM_ER = 0;
774 //# endif /* if defined(DPS8M) */
775 cpu.cu.CRT = 0;
776 cpu.cu.RALR = 0;
777 cpu.cu.SDWAM_ER = 0;
778 cpu.cu.OOSB = 0;
779 cpu.cu.PARU = 0;
780 cpu.cu.PARL = 0;
781 cpu.cu.ONC1 = 0;
782 cpu.cu.ONC2 = 0;
783 cpu.cu.IA = 0;
784 cpu.cu.IACHN = 0;
785 cpu.cu.CNCHN = 0;
786
787 // Set control unit 'fault occurred during instruction fetch' flag
788 cpu.cu.FIF = 0;
789 cpu.cu.FI_ADDR = (word5) fault_number & MASK5;
790
791 // XXX Under what conditions should this be set?
792 // Assume no
793 // Reading Multics source, it seems like Multics is setting this bit; I'm going
794 // to assume that the h/w also sets it to 0, and the s/w has to explicitly set it on.
795 cpu.cu.rfi = 0;
796
797 // Try to decide if this a MIF fault (fault during EIS instruction)
798 // EIS instructions are not used in fault/interrupt pairs, so the
799 // only time an EIS instruction could be executing is during EXEC_cycle.
800 // I am also assuming that only multi-word EIS instructions are of interest.
801
802 SC_I_MIF (cpu.cycle == EXEC_cycle &&
803 cpu.currentInstruction.info->ndes > 0);
804 sim_debug (DBG_TRACEEXT, & cpu_dev, "MIF %o\r\n", TST_I_MIF);
805
806
807 // History registers
808 // IHRRS; AL39 pg 47
809 // History register lock control. If this bit is set ON, set STROBE ยข
810 // (bit 30, key k) OFF, locking the history registers for all faults
811 // including the floating faults.
812 CPTUR (cptUseMR);
813 if (cpu.MR.emr && cpu.MR.ihrrs)
814 {
815 cpu.MR.ihr = 0;
816 }
817
818 if (cpu.cycle == FAULT_EXEC_cycle)
819 {
820 cpu.faultNumber = FAULT_TRB;
821 cpu.cu.FI_ADDR = FAULT_TRB;
822 cpu.subFault.bits = 0; // XXX ???
823 if (cpu.bTroubleFaultCycle)
824 {
825 #if !defined(THREADZ) && !defined(LOCKLESS)
826 # if !defined(PANEL68)
827 if ((! sample_interrupts (cpup)) &&
828 (sim_qcount () == 0)) // XXX If clk_svc is implemented it will
829 // break this logic
830 {
831 sim_printf \
832 ("Fault cascade @0%06o with no interrupts pending and no events in queue\r\n",
833 cpu.PPR.IC);
834 sim_printf("\r\nCycles = %"PRId64"\r\n", cpu.cycleCnt);
835 sim_printf("\r\nInstructions = %"PRId64"\r\n", cpu.instrCnt);
836 longjmp (cpu.jmpMain, JMP_STOP);
837 }
838 # endif /* if !defined(PANEL68) */
839 #endif /* if !defined(THREADZ) && !defined(LOCKLESS) */
840 }
841 else
842 {
843 cpu.bTroubleFaultCycle = true;
844 }
845 cpu.cycle = FAULT_cycle;
846 sim_debug (DBG_CYCLE, & cpu_dev, "Setting cycle to FAULT_cycle\r\n");
847 longjmp (cpu.jmpMain, JMP_REENTRY);
848 }
849 cpu.bTroubleFaultCycle = false;
850
851 // If doInstruction faults, the instruction cycle counter doesn't get
852 // bumped.
853 if (cpu . cycle == EXEC_cycle)
854 cpu.instrCnt ++;
855
856 cpu.is_FFV = true;
857 cpu.cycle = FAULT_cycle;
858 longjmp (cpu.jmpMain, JMP_REENTRY);
859 }
860
861 void dlyDoFault (_fault faultNumber, _fault_subtype subFault,
/* ![[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)
*/
862 const char * faultMsg)
863 {
864 cpu_state_t * cpup = _cpup;
865 cpu.dlyFlt = true;
866 cpu.dlyFltNum = faultNumber;
867 cpu.dlySubFltNum = subFault;
868 cpu.dlyCtx = faultMsg;
869 }
870
871 //
872 // return true if group 7 faults are pending ...
873 //
874
875 // Note: The DIS code assumes that the only G7 fault is TRO. Adding any
876 // other G7 faults will potentially require changing the DIS code.
877
878 bool bG7Pending (cpu_state_t * cpup)
/* ![[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)
*/
879 {
880 if (cpu.tweaks.l68_mode)
881 return cpu.g7Faults != 0 || cpu.FFV_faults != 0; // L68
882
883 return cpu.g7Faults != 0; // DPS8M
884 }
885
886 bool bG7PendingNoTRO (cpu_state_t * cpup)
/* ![[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)
*/
887 {
888 if (cpu.tweaks.l68_mode)
889 return (cpu.g7Faults & (~ (1u << FAULT_TRO))) != 0 || cpu.FFV_faults != 0; // L68
890
891 return (cpu.g7Faults & (~ (1u << FAULT_TRO))) != 0; // DPS8M
892 }
893
894 void setG7fault (uint cpuNo, _fault faultNo)
/* ![[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)
*/
895 {
896 cpu_state_t * cpup = &cpus[cpuNo];
897 sim_debug (DBG_FAULT, & cpu_dev, "setG7fault CPU %d fault %d (%o)\r\n",
898 cpuNo, faultNo, faultNo);
899 #if defined(THREADZ) || defined(LOCKLESS)
900 //__atomic_or_fetch (& cpup->g7FaultsPreset, (1u << faultNo), __ATOMIC_ACQUIRE);
901 (void)atomic_fetch_or_explicit(&cpup->g7FaultsPreset, (1u << faultNo), memory_order_acquire);
902 #else
903 cpup->g7FaultsPreset |= (1u << faultNo);
904 #endif
905 #if defined(THREADZ) || defined(LOCKLESS)
906 if (cpuNo != current_running_cpu_idx)
907 wakeCPU(cpuNo);
908 #endif
909 }
910
911 void set_FFV_fault (cpu_state_t * cpup, uint f_fault_no)
/* ![[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)
*/
912 {
913 sim_debug (DBG_FAULT, & cpu_dev, "set_FFV_fault CPU f_fault_no %u\r\n",
914 f_fault_no);
915 // Map fault number (2/4/6) to bit mask 01/02/04
916 cpu.FFV_faults_preset |= 1u << ((f_fault_no / 2) - 1);
917 }
918
919 void clearTROFault (cpu_state_t * cpup)
/* ![[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)
*/
920 {
921 cpu . g7Faults &= ~(1u << FAULT_TRO);
922 }
923
924 void doG7Fault (cpu_state_t * cpup, bool allowTR)
/* ![[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)
*/
925 {
926 // sim_printf ("doG7fault %08o [%"PRId64"]\r\n", cpu . g7Faults, cpu.cycleCnt);
927 // if (cpu . g7Faults)
928 // {
929 // sim_debug (DBG_FAULT, & cpu_dev, "doG7Fault %08o\r\n", cpu . g7Faults);
930 // }
931 // According AL39, Table 7-1. List of Faults, priority of connect is 25
932 // and priority of Timer runout is 26, lower number means higher priority
933 #if defined(THREADZ) || defined(LOCKLESS)
934 lock_scu ();
935 #endif
936 if (cpu.g7Faults & (1u << FAULT_CON))
937 {
938 cpu.g7Faults &= ~(1u << FAULT_CON);
939
940 #if defined(THREADZ) || defined(LOCKLESS)
941 unlock_scu ();
942 #endif
943 doFault (FAULT_CON, fst_zero, "Connect");
944 }
945
946 if (allowTR && (cpu.g7Faults & (1u << FAULT_TRO)))
947 {
948 cpu . g7Faults &= ~(1u << FAULT_TRO);
949
950 //sim_printf("timer runout %12o\r\n",cpu.PPR.IC);
951 #if defined(THREADZ) || defined(LOCKLESS)
952 unlock_scu ();
953 #endif
954 doFault (FAULT_TRO, fst_zero, "Timer runout");
955 }
956
957 // Strictly speaking EXF isn't a G7 fault, but if we treat is as one,
958 // we are allowing the current instruction to complete, simplifying
959 // implementation
960 if (cpu . g7Faults & (1u << FAULT_EXF))
961 {
962 cpu . g7Faults &= ~(1u << FAULT_EXF);
963
964 #if defined(THREADZ) || defined(LOCKLESS)
965 unlock_scu ();
966 #endif
967 doFault (FAULT_EXF, fst_zero, "Execute fault");
968 }
969
970 if (cpu.tweaks.l68_mode) { // L68
971 if (cpu.FFV_faults & 1u) // FFV + 2 OC TRAP
972 {
973 cpu.FFV_faults &= ~1u;
974 #if defined(THREADZ) || defined(LOCKLESS)
975 unlock_scu ();
976 #endif
977 do_FFV_fault (cpup, 1, "OC TRAP");
978 }
979 if (cpu.FFV_faults & 2u) // FFV + 4 CU HISTORY OVERFLOW TRAP
980 {
981 cpu.FFV_faults &= ~2u;
982 #if defined(THREADZ) || defined(LOCKLESS)
983 unlock_scu ();
984 #endif
985 do_FFV_fault (cpup, 2, "CU HIST OVF TRAP");
986 }
987 if (cpu.FFV_faults & 4u) // FFV + 6 ADR TRAP
988 {
989 cpu.FFV_faults &= ~4u;
990 #if defined(THREADZ) || defined(LOCKLESS)
991 unlock_scu ();
992 #endif
993 do_FFV_fault (cpup, 3, "ADR TRAP");
994 }
995 }
996 #if defined(THREADZ) || defined(LOCKLESS)
997 unlock_scu ();
998 #endif
999 doFault (FAULT_TRB, (_fault_subtype) {.bits=cpu.g7Faults}, "Dazed and confused in doG7Fault");
1000 }
1001
1002 void advanceG7Faults (cpu_state_t * cpup)
/* ![[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)
*/
1003 {
1004 // This is potentially stale cached, but we don't care, we will see eventually ZZZ
1005 if (!cpu.g7FaultsPreset && !cpu.FFV_faults_preset)
1006 return;
1007
1008 #if defined(THREADZ) || defined(LOCKLESS)
1009 lock_scu ();
1010 uint zero = 0;
1011 //__atomic_exchange (& cpu.g7FaultsPreset, & zero, & cpu.g7Faults, __ATOMIC_ACQUIRE);
1012 cpu.g7Faults = atomic_exchange_explicit(&cpu.g7FaultsPreset, zero, memory_order_acquire);
1013 #else
1014 cpu.g7Faults |= cpu.g7FaultsPreset;
1015 cpu.g7FaultsPreset = 0;
1016 #endif
1017 L68_ (
1018 cpu.FFV_faults |= cpu.FFV_faults_preset;
1019 cpu.FFV_faults_preset = 0;
1020 )
1021 #if defined(THREADZ) || defined(LOCKLESS)
1022 unlock_scu ();
1023 #endif
1024 }