cpu/control.rs
1//! Emulates the principal central components of the TX-2.
2//!
3//! # TX-2 Functionality implemented in this module
4//!
5//! The functionality of this module is conceptually similar to the
6//! CPU of a modern computer, although on the real TX-2 these
7//! functions were split across several identifiably separate units
8//! ("elements").
9//!
10//!
11//!
12//! ## Functions of the In/Out Element
13//!
14//!
15//! Other functions of the In/Out element are implemented in the [io]
16//! module.
17//!
18//! # Functions of the Exchange Element
19//!
20//! - Remember the setting of the TSP (Toggle Start Point) register
21//!
22use std::collections::{BTreeMap, HashSet};
23use std::fmt::Write;
24use std::ops::BitAnd;
25use std::time::Duration;
26
27use tracing::{Level, event, event_enabled, span};
28
29mod op_configuration;
30mod op_index;
31mod op_io;
32mod op_jump;
33mod op_loadstore;
34#[cfg(test)]
35mod tests;
36mod timing;
37mod trap;
38
39use base::instruction::{Inst, Instruction, Opcode, SymbolicInstruction};
40use base::prelude::*;
41use base::subword;
42
43use super::alarm::{Alarm, AlarmDetails, AlarmKind, Alarmer, BadMemOp};
44use super::alarmunit::AlarmUnit;
45use super::context::Context;
46use super::diagnostics::{CurrentInstructionDiagnostics, DiagnosticFetcher};
47use super::exchanger::{
48 SystemConfiguration, exchanged_value_for_load, exchanged_value_for_store,
49 standard_plugboard_f_memory_settings,
50};
51use super::io::DeviceManager;
52use super::memory::{self, ExtraBits, MemoryMapped, MemoryOpFailure, MemoryUnit, MetaBitChange};
53use super::*;
54
55use trap::TrapCircuit;
56
57#[derive(Debug, PartialEq, Eq, Clone, Copy)]
58pub enum RunMode {
59 Running,
60 InLimbo,
61}
62
63#[derive(Debug, Eq, PartialEq)]
64pub(crate) enum ProgramCounterChange {
65 /// Change of current sequence.
66 SequenceChange(Unsigned6Bit),
67
68 // A TSD instruction ends in dismiss-and-wait. That is, the
69 // sequence is dismissed without its sequence number changing.
70 DismissAndWait(Address),
71
72 /// Immediate stop of execution, such as when an unmasked alarm is raised.
73 Stop(Address),
74
75 // Normal increment of the program counter.
76 CounterUpdate,
77
78 // Transfer control to another address (but without changing
79 // sequence).
80 Jump(Address),
81}
82
83/// Flags represent requests to run for instruction sequences (today
84/// one might describe these as threads). Some sequences are special:
85///
86/// | Sequence Number | Description |
87/// | --------------- | ----------- |
88/// | 0 | Sequence which is run to start the computer (e.g. when "CODABO" or "START OVER" is pressed). |
89/// |41-75| hardware devices |
90/// | 76 | Not for physical devices. |
91/// | 77 | Not for physical devices. |
92///
93/// The flag for sequences 76 and 77 may only be raised/lowered by
94/// program control.
95///
96/// The Lincoln Laboratory book "MIT Lincoln Laboratory: Technology in
97/// Support of National Security" states (on page 462) that The Xerox
98/// Alto also used sequences for I/O. See also
99/// <https://en.wikipedia.org/wiki/Xerox_Alto#Architecture>
100#[derive(Debug)]
101struct SequenceFlags {
102 flag_values: u64,
103 flag_changes: u64,
104}
105
106fn ones_of_value_as_vec(mut bits: u64) -> Vec<SequenceNumber> {
107 let popcount_or_zero: usize = usize::try_from(bits.count_ones()).unwrap_or(0usize);
108 let mut result = Vec::with_capacity(popcount_or_zero);
109 while bits != 0 {
110 let pos = bits.trailing_zeros();
111 result.push(SequenceNumber::try_from(pos).expect("SequenceNumber::MAX should be > 32"));
112 bits &= !(1 << pos);
113 }
114 result
115}
116
117#[test]
118fn test_ones_of_value_as_vec() {
119 assert!(ones_of_value_as_vec(0).is_empty());
120 assert_eq!(ones_of_value_as_vec(0b1), vec![u6!(0)]);
121 assert_eq!(ones_of_value_as_vec(0b101), vec![u6!(0), u6!(2)]);
122}
123
124impl SequenceFlags {
125 fn new() -> SequenceFlags {
126 // New instances start with no flags raised (i.e. in "Limbo",
127 // STARTOVER not running).
128 SequenceFlags {
129 flag_values: 0,
130 flag_changes: 0,
131 }
132 }
133
134 fn lower_all(&mut self) {
135 self.flag_changes |= self.flag_values;
136 self.flag_values = 0;
137 }
138
139 fn flagbit(flag: &SequenceNumber) -> u64 {
140 1_u64 << u64::from(*flag)
141 }
142
143 fn lower(&mut self, flag: &SequenceNumber) {
144 // We create a u16 from *flag in order to perform the
145 // comparison. Otherwise we get the compilation error
146 // "error[E0277]: can't compare `base::prelude::Unsigned6Bit`
147 // with `u16`".
148 #![allow(clippy::cmp_owned)]
149 assert!(u16::from(*flag) < 0o100_u16);
150 event!(Level::DEBUG, "Lowering flag {}", flag,);
151 let mask = SequenceFlags::flagbit(flag);
152 self.flag_values &= !mask;
153 self.flag_changes |= mask;
154 }
155
156 fn raise(&mut self, flag: &SequenceNumber) {
157 // We create a u16 from *flag in order to perform the
158 // comparison. Otherwise we get the compilation error
159 // "error[E0277]: can't compare `base::prelude::Unsigned6Bit`
160 // with `u16`".
161 #![allow(clippy::cmp_owned)]
162 assert!(u16::from(*flag) < 0o100_u16);
163 let mask = SequenceFlags::flagbit(flag);
164 self.flag_values |= mask;
165 self.flag_changes |= mask;
166
167 if event_enabled!(Level::DEBUG) {
168 // Build the debug message which we are going to use; this
169 // is conditional only because it allocates memory and so
170 // is likely to be expensive.
171 let seqs: String = ones_of_value_as_vec(self.flag_values).into_iter().fold(
172 String::new(),
173 |mut acc, v| {
174 write!(acc, "{:>02o} ", u64::from(v)).expect("write to string must succeed");
175 acc
176 },
177 );
178 event!(
179 Level::DEBUG,
180 "Raised flag {flag:o}; runnable sequences now {seqs}"
181 );
182 }
183 }
184
185 fn current_flag_state(&self, flag: &SequenceNumber) -> bool {
186 // We create a u16 from *flag in order to perform the
187 // comparison. Otherwise we get the compilation error
188 // "error[E0277]: can't compare `base::prelude::Unsigned6Bit`
189 // with `u16`".
190 #![allow(clippy::cmp_owned)]
191 assert!(u16::from(*flag) < 0o100_u16);
192 self.flag_values & SequenceFlags::flagbit(flag) != 0
193 }
194
195 /// Return the index of the highest-priority (lowest-numbered)
196 /// flag. If no flag is raised (the machine is in "Limbo"),
197 /// return None.
198 fn highest_priority_raised_flag(&self) -> Option<SequenceNumber> {
199 let n = self.flag_values.trailing_zeros();
200 if n == 64 {
201 None
202 } else {
203 Some(n.try_into().unwrap())
204 }
205 }
206
207 fn drain_flag_changes(&mut self) -> Vec<SequenceNumber> {
208 let result = ones_of_value_as_vec(self.flag_changes);
209 self.flag_changes = 0;
210 result
211 }
212}
213
214#[test]
215fn test_sequence_flags_highest_priority_raised() {
216 let mut flags = SequenceFlags::new();
217 assert!(flags.drain_flag_changes().is_empty());
218
219 flags.lower_all();
220 assert_eq!(flags.highest_priority_raised_flag(), None);
221 // Lowering all flags produces no change, they were already down.
222 assert!(flags.drain_flag_changes().is_empty());
223
224 flags.raise(&Unsigned6Bit::ZERO);
225 assert_eq!(
226 flags.highest_priority_raised_flag().map(i8::from),
227 Some(0_i8)
228 );
229 assert_eq!(flags.drain_flag_changes(), vec![u6!(0)]);
230 flags.raise(&Unsigned6Bit::ONE);
231 // 0 is still raised, so it still has the highest priority.
232 assert_eq!(
233 flags.highest_priority_raised_flag(),
234 Some(Unsigned6Bit::ZERO)
235 );
236 assert_eq!(flags.drain_flag_changes(), vec![u6!(1)]);
237
238 flags.lower(&SequenceNumber::ZERO);
239 assert_eq!(
240 flags.highest_priority_raised_flag(),
241 Some(Unsigned6Bit::ONE)
242 );
243 flags.lower(&SequenceNumber::ONE);
244 assert_eq!(flags.drain_flag_changes(), vec![u6!(0), u6!(1)]);
245 assert_eq!(flags.highest_priority_raised_flag(), None);
246
247 let four = SequenceNumber::try_from(4_i8).expect("valid test data");
248 let six = SequenceNumber::try_from(6_i8).expect("valid test data");
249 flags.raise(&four);
250 flags.raise(&six);
251 assert_eq!(flags.highest_priority_raised_flag(), Some(four));
252 flags.lower(&four);
253 assert_eq!(flags.highest_priority_raised_flag(), Some(six));
254 assert_eq!(flags.drain_flag_changes(), vec![u6!(4), u6!(6)]);
255}
256
257#[test]
258fn test_sequence_flags_current_flag_state() {
259 let mut flags = SequenceFlags::new();
260 let s52: SequenceNumber = u6!(0o52);
261 flags.lower_all();
262 assert!(!flags.current_flag_state(&s52), "flag 52 should be lowered");
263 flags.raise(&s52);
264 assert!(flags.current_flag_state(&s52), "flag 52 should be raised");
265 assert_eq!(flags.drain_flag_changes(), vec![u6!(0o52)]);
266}
267
268/// Registers from various elements of the TX-2.
269///
270/// This includes both programmer-accessible registers (Index
271/// registers for example) and non-programmer accessible registers
272/// (such as the N register).
273#[derive(Debug)]
274pub struct ControlRegisters {
275 pub diagnostic_only: CurrentInstructionDiagnostics,
276
277 // Arithmetic Element registers (A-E) are actually in V-memory.
278 /// Contents of the simulated N register.
279 pub n: Instruction,
280 pub n_sym: Option<SymbolicInstruction>,
281
282 /// The P register is the program counter for the current sequence.
283 pub p: Address,
284
285 /// Contents of the simulaterd Q register.
286 pub q: Address,
287
288 /// The k register (User Guide section 4-3.1) holds the current
289 /// sequence number (User Guide section 5-24). k is
290 /// `Option<SequenceNumber>` in order to allow the (emulated)
291 /// control unit to recognise a CODABO button as indicating a need
292 /// to change sequence from the control unit's initial state to
293 /// sequence 0.
294 ///
295 /// This likely doesn't reflect the actual operation of the TX-2
296 /// very well, and better understanding of the real operation of
297 /// the machine will likely change this.
298 ///
299 /// I think that perhaps section 12-2.6.2 of Volume 2 of the
300 /// technical manual may explain how the real TX-2 avoided this
301 /// problem, but I don't think I understand what that section says.
302 /// The text is:
303 ///
304 /// """12-2.6.2 XPS FLIP-FLOP LOGIC. This flip-floop inhibits the
305 /// X Memory strobe pulse into X when the register selected has the
306 /// same address or the current program counter, is not register 0,
307 /// and this is the first reference to this register since the last
308 /// sequence change. In this case all the cores of the register
309 /// are clearered and only "junk" (with a 50-50 chance of a bad
310 /// parity) would be strobed into X. If XPS¹, then a clear pulse
311 /// is substituted for the strobe pulse.
312 ///
313 /// The flip-flop is set whenever a sequence change occurs, and is
314 /// cleared the first time thereafter that the program counter
315 /// register is referenced during a PK cycle (if ever). See Fig
316 /// 12-8."""
317 pub k: Option<SequenceNumber>,
318
319 /// Start Point Register
320 spr: Address,
321
322 /// Index registers.
323 ///
324 /// Index register 0 is the Toggle Start point.
325 /// Index registers 40-77 are program counters for the sequences.
326 ///
327 /// The index registers form an 18-bit ring (as stated in the
328 /// description of the AUX instruction) and are described on page
329 /// 3-68 of the User Handbook (section 3-3.1) as being signed
330 /// integers.
331 pub index_regs: [Signed18Bit; 0o100], // AKA the X memory
332
333 /// the F memory
334 f_memory: [SystemConfiguration; 32],
335
336 /// The flags; these indicate which sequences are runnable.
337 flags: SequenceFlags,
338 current_sequence_is_runnable: bool,
339
340 /// `prev_hold` is set when the instruction we most previously
341 /// executed had the "hold" bit set.
342 prev_hold: bool,
343 // TODO: we may be able to eliminate prev_hold by moving the logic
344 // that's currently at the beginning of fetch_instruction() so
345 // that it occurs at the end of execute_instruction() instead.
346 // See the comment at the top of fetch_instruction().
347}
348
349impl ControlRegisters {
350 fn new(configuration_memory_config: ConfigurationMemorySetup) -> ControlRegisters {
351 let fmem = match configuration_memory_config {
352 ConfigurationMemorySetup::Uninitialised => {
353 let default_val = SystemConfiguration::from(0_u8);
354 [default_val; 32]
355 }
356 ConfigurationMemorySetup::StandardForTestingOnly => {
357 standard_plugboard_f_memory_settings()
358 }
359 };
360
361 let mut result = ControlRegisters {
362 diagnostic_only: CurrentInstructionDiagnostics {
363 current_instruction: Instruction::invalid(),
364 instruction_address: Address::from(u18!(0o777_777)),
365 },
366 n: Instruction::invalid(), // not a valid instruction
367 n_sym: None,
368 p: Address::default(),
369 q: Address::default(),
370 k: None, // not 0, so that we can recognise CODABO.
371 index_regs: [Signed18Bit::default(); 0o100],
372 f_memory: fmem,
373 flags: SequenceFlags::new(),
374 current_sequence_is_runnable: false,
375 prev_hold: false,
376 spr: Address::default(),
377 };
378 // Index register 0 always contains 0. This should still be
379 // true if we modify the behaviour of Address::default(),
380 // which is why we override it here.
381 result.index_regs[0] = Signed18Bit::ZERO;
382 result
383 }
384
385 fn previous_instruction_hold(&self) -> bool {
386 // We cannot just check the N register for this, because when
387 // a TSD instruction ends in "dismiss and wait", the N
388 // register contains the (possibly held) TSD instruction, but
389 // it's the hold bit of the _previous_ instruction that
390 // matters.
391 self.prev_hold
392 }
393
394 fn set_spr(&mut self, addr: &Address) {
395 self.spr = *addr;
396 }
397
398 fn get_index_register(&self, n: Unsigned6Bit) -> Signed18Bit {
399 let n = usize::from(n);
400 assert_eq!(self.index_regs[0], 0);
401 assert!(n < 0o100);
402 self.index_regs[n]
403 }
404
405 fn get_index_register_as_address(&mut self, n: Unsigned6Bit) -> Address {
406 let value: Signed18Bit = self.get_index_register(n);
407 Address::from(value.reinterpret_as_unsigned())
408 }
409
410 fn set_index_register(&mut self, n: Unsigned6Bit, value: &Signed18Bit) {
411 let n = usize::from(n);
412 assert_eq!(self.index_regs[0], 0);
413 assert_ne!(n, 0, "Index register 0 should be fixed at 0");
414 assert!(n < 0o100);
415 self.index_regs[n] = *value;
416 }
417
418 fn set_index_register_from_address(&mut self, n: Unsigned6Bit, addr: &Address) {
419 let value: Unsigned18Bit = Unsigned18Bit::from(*addr);
420 self.set_index_register(n, &value.reinterpret_as_signed());
421 }
422
423 fn get_f_mem(&self, n: Unsigned5Bit) -> SystemConfiguration {
424 // Use u8::from in order to be able to compare an Unsigned5Bit.
425 #![allow(clippy::cmp_owned)]
426 assert!(u8::from(n) < 0o37_u8);
427 assert_eq!(self.f_memory[0], SystemConfiguration::zero());
428 let pos: usize = n.into();
429 self.f_memory[pos]
430 }
431
432 #[must_use]
433 pub fn current_flag_state(&self, seq: &Unsigned6Bit) -> bool {
434 self.flags.current_flag_state(seq)
435 }
436}
437
438impl DiagnosticFetcher for &ControlRegisters {
439 fn diagnostics(self) -> CurrentInstructionDiagnostics {
440 self.diagnostic_only.clone()
441 }
442}
443
444#[derive(Clone, Copy, Debug)]
445pub enum ResetMode {
446 ResetTSP = 0,
447 Reset0 = 0o0377710,
448 Reset1 = 0o0377711,
449 Reset2 = 0o0377712,
450 Reset3 = 0o0377713,
451 Reset4 = 0o0377714,
452 Reset5 = 0o0377715,
453 Reset6 = 0o0377716,
454 Reset7 = 0o0377717,
455}
456
457impl ResetMode {
458 fn address(&self, ctx: &Context, mem: &mut MemoryUnit) -> Option<Address> {
459 use ResetMode::*;
460 const PHYSICAL_ADDRESS_BITS: u32 = 0o377_777;
461 match self {
462 Reset0 | Reset1 | Reset2 | Reset3 | Reset4 | Reset5 | Reset6 | Reset7 => {
463 let loc: Address = Address::from(Unsigned18Bit::try_from(*self as u32).unwrap());
464 match mem.fetch(ctx, &loc, &MetaBitChange::None) {
465 Ok((word, _)) => {
466 // word is 36 bits wide but we only want the bottom 17 bits.
467 let (left, right) = subword::split_halves(word);
468 if left != 0 {
469 // issue warning but otherwise ignore
470 event!(
471 Level::WARN,
472 "Ignoring non-zero left subword of reset register {:o}, containing {:o} (left side is {:o})",
473 loc,
474 word,
475 left
476 );
477 }
478 // We assume that reset operations don't implement deferred addressing.
479 let defer_bit = Unsigned18Bit::try_from(0o400_000).unwrap();
480 if right & defer_bit != 0 {
481 // issue warning but otherwise ignore
482 event!(
483 Level::WARN,
484 "Ignoring non-zero defer bit of reset register {:o}, containing {:o}",
485 loc,
486 word
487 );
488 }
489
490 let physical_address = Address::from(right & PHYSICAL_ADDRESS_BITS);
491 Some(physical_address)
492 }
493 Err(e) => {
494 panic!("failed to fetch reset {self:?}: {e}");
495 }
496 }
497 }
498 ResetTSP => None, // need to read the TSP toggle switch.
499 }
500 }
501}
502
503/// `ControlUnit` simulates the operation of the Control Element of the TX-2 computer.
504///
505#[derive(Debug)]
506pub struct ControlUnit {
507 regs: ControlRegisters,
508 trap: TrapCircuit,
509 alarm_unit: AlarmUnit,
510}
511
512fn sign_extend_index_value(index_val: &Signed18Bit) -> Unsigned36Bit {
513 let left = if index_val.is_negative() {
514 Unsigned18Bit::MAX
515 } else {
516 Unsigned18Bit::ZERO
517 };
518 subword::join_halves(left, index_val.reinterpret_as_unsigned())
519}
520
521#[derive(Debug, PartialEq, Eq, Default)]
522pub(crate) struct OpcodeResult {
523 program_counter_change: Option<ProgramCounterChange>,
524 poll_order_change: Option<SequenceNumber>,
525 output: Option<OutputEvent>,
526}
527
528#[test]
529fn test_opcode_result() {
530 let r = OpcodeResult::default();
531 assert!(r.program_counter_change.is_none());
532 assert!(r.poll_order_change.is_none());
533}
534
535#[derive(Debug, Clone, PartialEq, Eq)]
536pub enum PanicOnUnmaskedAlarm {
537 No,
538 Yes,
539}
540
541#[derive(Debug, Clone, PartialEq, Eq)]
542pub enum ConfigurationMemorySetup {
543 Uninitialised,
544 StandardForTestingOnly,
545}
546
547fn please_poll_soon(ctx: &Context, devices: &mut DeviceManager, seq: SequenceNumber) {
548 event!(
549 Level::TRACE,
550 "please_poll_soon: seq={:?}, requesting poll at {:?}",
551 seq,
552 &ctx.simulated_time
553 );
554 devices.update_poll_time(ctx, seq);
555}
556
557impl ControlUnit {
558 #[must_use]
559 pub fn new(
560 panic_on_unmasked_alarm: PanicOnUnmaskedAlarm,
561 configuration_memory_config: ConfigurationMemorySetup,
562 ) -> ControlUnit {
563 ControlUnit {
564 regs: ControlRegisters::new(configuration_memory_config),
565 trap: TrapCircuit::new(),
566 alarm_unit: AlarmUnit::new_with_panic(match panic_on_unmasked_alarm {
567 PanicOnUnmaskedAlarm::No => false,
568 PanicOnUnmaskedAlarm::Yes => true,
569 }),
570 }
571 }
572
573 #[must_use]
574 pub fn diagnostics(&self) -> &CurrentInstructionDiagnostics {
575 &self.regs.diagnostic_only
576 }
577
578 fn make_alarm(&self, details: AlarmDetails) -> Alarm {
579 Alarm {
580 sequence: self.regs.k,
581 details,
582 }
583 }
584
585 fn fire_details_if_not_masked(&mut self, alarm_details: AlarmDetails) -> Result<(), Alarm> {
586 let diagnostics: CurrentInstructionDiagnostics = self.diagnostics().clone();
587 self.alarm_unit
588 .fire_if_not_masked(self.make_alarm(alarm_details), diagnostics)
589 }
590
591 pub fn set_alarm_masked(&mut self, kind: AlarmKind, masked: bool) -> Result<(), Alarm> {
592 if masked {
593 self.alarm_unit.mask(kind, &self.regs.diagnostic_only)
594 } else {
595 self.alarm_unit.unmask(kind);
596 Ok(())
597 }
598 }
599
600 #[must_use]
601 pub fn unmasked_alarm_active(&self) -> bool {
602 self.alarm_unit.unmasked_alarm_active()
603 }
604
605 #[must_use]
606 pub fn get_status_of_alarm(&self, name: &str) -> Option<AlarmStatus> {
607 self.alarm_unit.get_status_of_alarm(name)
608 }
609
610 #[must_use]
611 pub fn get_alarm_statuses(&self) -> Vec<AlarmStatus> {
612 self.alarm_unit.get_alarm_statuses()
613 }
614
615 pub fn set_metabits_disabled(&mut self, disable: bool) {
616 self.trap.set_metabits_disabled(disable);
617 }
618
619 /// There are actually 9 different CODABO buttons (see page 5-18
620 /// of the User Guide). There are also 9 corresponding RESET
621 /// buttons. Each RESET button has a corresponding CODABO button.
622 /// See the `reset` method for address assignments.
623 ///
624 /// The CODABO operation leaves the Start Point Register set to
625 /// the selected start point. There are also 9 reset buttons
626 /// which perform a similar task.
627 ///
628 /// Pressing the main CODABO button (the one which uses the Toggle
629 /// Start Point register) will result in memory being cleared,
630 /// F-memory being set up in a standard way, and then a program
631 /// being read from the paper tape reader using the standard
632 /// readin program set up on the plugboard.
633 ///
634 /// The standard readin program executes the program it read in
635 /// from the address specified by the program. The program
636 /// executes as sequence 52 (PETR) with the PETR unit initially
637 /// turned off.
638 ///
639 /// The Users Handbook (page 5-18) states that flags are cleared
640 /// but it doesn't state anywhere that any registers are reset.
641 /// So we don't do that. But there's no unit test for that, since
642 /// I haven't found (or cannot recall) a piece of documentation
643 /// which states that this is so.
644 pub fn codabo(
645 &mut self,
646 ctx: &Context,
647 reset_mode: &ResetMode,
648 devices: &mut DeviceManager,
649 mem: &mut MemoryUnit,
650 ) -> Result<(), Alarm> {
651 // We probably don't need an equivalent of resetting the
652 // control flip-flops in an emulator. But if we did, that
653 // would happen here.
654 //
655 // On the other hand, the P register is described as an
656 // "18-bit flip-flop" in section 4-2.2 of the User Handbook,
657 // so perhaps all the registers in V memory are cleared by
658 // CODABO.
659 //
660 let span = span!(Level::ERROR,
661 "codabo",
662 reset_mode=?reset_mode);
663 let _enter = span.enter();
664 event!(Level::INFO, "Starting CODABO {:?}", &reset_mode);
665 // CODABO's first action is supposed to be "STOP" but the
666 // simulator has no analogue for "STOP". One reason we need
667 // to perform STOP is that PRESET has no effect unless the
668 // computer is stopped.
669 self.clear_alarms();
670 self.preset(ctx, devices)?;
671 self.startover(ctx, reset_mode, mem);
672 self.calaco();
673 event!(
674 Level::DEBUG,
675 "After CODABO, control unit contains {:#?}",
676 &self
677 );
678 Ok(())
679 }
680
681 /// Simulate the effect of the PRESET button. This is described
682 /// on page 5-19 of the Users Handbook. It:
683 /// 1. clears all flags
684 /// 2. Clears all "Connect" flip-flops
685 /// 3. Set all interlocks and indicators to their proper "PRESET" value.
686 ///
687 /// PRESET is supposedly interlocked so that it is ineffective
688 /// unless the machine is in the STOP state. But our simulation,
689 /// right now, has no representation for the STOP state.
690 fn preset(&mut self, ctx: &Context, devices: &mut DeviceManager) -> Result<(), Alarm> {
691 // 1. Clear all flags.
692 self.regs.flags.lower_all();
693 // 2. Clear all "Connect" flip-flops
694 self.disconnect_all_devices(ctx, devices)
695 // 3. Set all interlocks and indicators to their proper "PRESET" value.
696 // (interlocks are not simulated)
697 }
698
699 /// Simulate the effect of the CALACO button. This is described
700 /// on page 5-19 of the Users Handbook.
701 fn calaco(&mut self) {
702 self.clear_alarms();
703 // TODO: there is currently no simulator representation for
704 // STOP/START, but CALACO is supposed to perform START. This
705 // might be relevant because PRESET is supposet to have no
706 // effect unless the computer is in the stopped state.
707 }
708
709 fn clear_alarms(&mut self) {
710 self.alarm_unit.clear_all_alarms();
711 }
712
713 pub fn disconnect_all_devices(
714 &mut self,
715 ctx: &Context,
716 devices: &mut DeviceManager,
717 ) -> Result<(), Alarm> {
718 devices.disconnect_all(ctx, &mut self.alarm_unit)
719 }
720
721 /// There are 9 separate RESET buttons, for 8 fixed addresses and
722 /// another which uses the Toggle Start Point register. There
723 /// appear to be two Toggle Start Point switches, one on the front
724 /// panel and a second on a remote control unit. The
725 /// fixed-address RESET buttons correspond to the fixed
726 /// addresses 3777710 through 3777717, inclusive.
727 ///
728 /// RESET *only* loads the Start Point Register, nothing else.
729 pub fn reset(&mut self, ctx: &Context, reset_mode: &ResetMode, mem: &mut MemoryUnit) {
730 self.regs.set_spr(&match reset_mode.address(ctx, mem) {
731 Some(address) => address,
732 None => self.tsp(),
733 });
734 }
735
736 /// Handle press of STARTOVER (or part of the operation of
737 /// CODABO). STARTOVER does RESET and then raises flag zero.
738 pub fn startover(&mut self, ctx: &Context, reset_mode: &ResetMode, mem: &mut MemoryUnit) {
739 self.reset(ctx, reset_mode, mem);
740 self.regs.current_sequence_is_runnable = false;
741 self.regs.flags.raise(&SequenceNumber::ZERO);
742 self.change_sequence(mem, None, SequenceNumber::ZERO);
743 }
744
745 /// Return the value in the Toggle Start Register (TSP).
746 ///
747 /// The TSP is a set of toggle switches on the TX-2's console (see
748 /// the TX-2 Users Handbook section 4-2.2).
749 ///
750 /// For now, we haven't made it configurable (i.e. have not
751 /// emulated the hardware) yet. We just hard-code it to point at
752 /// the F-memory configuration routine (which does its job and
753 /// then invokes the standard tape reader).
754 ///
755 /// # Caveat
756 ///
757 /// We used to set this to point at the "Memory Clear / Memory
758 /// Smear" program in the plugboard (see
759 /// [`memory::get_standard_plugboard()`]), but that accesses
760 /// addresses which are not mapped (e.g. the gap between U-memory
761 /// and V-memory) and so should only be run qith QSAL disabled,
762 /// which is not our default config.
763 ///
764 /// We should double-check how the TX-2's TSP was normally set and
765 /// how the TX-2 behaved around this.
766 fn tsp(&self) -> Address {
767 // The operation of RESET (or CODABO) will copy this value
768 // into the zeroth index register (which the program counter
769 // placeholder for sequence 0).
770 memory::STANDARD_PROGRAM_INIT_CONFIG
771 }
772
773 fn trap_seq() -> Unsigned6Bit {
774 Unsigned6Bit::try_from(0o42).unwrap()
775 }
776
777 fn raise_trap(&mut self) {
778 self.regs.flags.raise(&Self::trap_seq());
779 }
780
781 fn change_sequence(
782 &mut self,
783 mem: &mut MemoryUnit,
784 prev_seq: Option<SequenceNumber>,
785 mut next_seq: SequenceNumber,
786 ) {
787 fn is_marked_placeholder(index_val: &Signed18Bit) -> bool {
788 index_val < &0
789 }
790
791 // If the "Trap on Change Sequence" is enabled and the new
792 // sequence is marked (bit 2.9 of its index register is set).
793 // Activate unit 0o42, unless that's the unit which is giving
794 // up control.
795 //
796 // I'm not sure what should happen for the alternative case,
797 // where a unit of higher priority than 0o42 is marked for
798 // trap-on-sequence-change.
799 if prev_seq == Some(next_seq) {
800 event!(
801 Level::WARN,
802 "change_sequence: old and new sequences are the same: {:>02o}",
803 u8::from(next_seq),
804 );
805 return;
806 }
807
808 event!(
809 Level::DEBUG,
810 "Changing sequence to {:>02o}",
811 u8::from(next_seq),
812 );
813
814 let trap_seq = Self::trap_seq();
815 let sequence_change_trap = self.trap.trap_on_changed_sequence()
816 && is_marked_placeholder(&self.regs.get_index_register(next_seq))
817 && self.regs.k != Some(trap_seq)
818 && next_seq > trap_seq;
819
820 let previous_sequence: Unsigned6Bit = match prev_seq {
821 None => Unsigned6Bit::ZERO,
822 Some(n) => n,
823 };
824 // Update the E register as specified in section 4-3.1 of the
825 // Users Handbook.
826 mem.set_e_register(join_halves(
827 join_quarters(
828 Unsigned9Bit::from(previous_sequence),
829 Unsigned9Bit::from(next_seq),
830 ),
831 Unsigned18Bit::from(self.regs.p),
832 ));
833
834 if sequence_change_trap {
835 self.regs.flags.raise(&trap_seq);
836 next_seq = trap_seq;
837 }
838 self.regs.k = Some(next_seq);
839 if let Some(prev) = prev_seq {
840 // Index register 0 never changes, it's always 0.
841 if prev_seq != Some(Unsigned6Bit::ZERO) {
842 let p = self.regs.p;
843 self.regs.set_index_register_from_address(prev, &p);
844 }
845 }
846 self.set_program_counter(ProgramCounterChange::SequenceChange(next_seq));
847 }
848
849 fn set_program_counter(&mut self, change: ProgramCounterChange) {
850 match change {
851 ProgramCounterChange::Stop(p) => {
852 let old_mark = self.regs.p.split().1;
853 let new_mark: bool = p.split().1;
854 assert_eq!(old_mark, new_mark);
855 self.regs.p = p;
856 }
857 ProgramCounterChange::SequenceChange(next_seq) => {
858 // According to the Technical Manual, page 12-6,
859 // change of seqeuence is the only time in which P₂.₉
860 // is altered.
861 //
862 // However, see also the arm for the
863 // [`ProgramCounterChange::CounterUpdate`] case
864 // below.
865 if next_seq != 0 {
866 self.regs.p = self.regs.get_index_register_as_address(next_seq);
867 event!(
868 Level::INFO,
869 "Changed sequence to {:>02o} with P={:>06o}",
870 u8::from(next_seq),
871 self.regs.p
872 );
873 } else {
874 // Index register 0 is always 0, but by setting
875 // the Toggle Status Register, the user can run
876 // sequence 0 from an arbitrary address. That
877 // address can't be stored in index register 0
878 // since that's always 0, so we use an internal
879 // "spr" register which is updated by the
880 // RESET/CODABO buttons. Here, we copy that saved
881 // value into P.
882 self.regs.p = self.regs.spr;
883 event!(
884 Level::INFO,
885 "Starting sequence 0 with P={:>06o}",
886 self.regs.p
887 );
888 }
889 }
890 ProgramCounterChange::CounterUpdate => {
891 // Discussion: does this change the top bit (2.9)?
892 //
893 // Reasons for the answer "no"
894 // --------------------------
895 // Volume 2 of the Technical Manual (section 12-2.3 "P
896 // REGISTER DRIVER LOGIC") states, """Information can
897 // be transferred into the P register only from the X
898 // Adder. In addition to this single transfer path,
899 // ther P register has a counter which can index the
900 // contents of the P register by one. Note that count
901 // circuit does not alter the contents of P₂.₉"""
902 //
903 // Since P₂.₉ is the sign bit, this means that the P
904 // register wraps rather than overflows.
905 //
906 // As a practical matter, this wrap-around case means
907 // that self.regs.p previously contained 377,777.
908 // That is the last instruction in V Memory. This is
909 // normally an unconditional JMP. So in the standard
910 // plugboard configuration, we're going to take the
911 // jmp, meaning that we're never going to fetch an
912 // instruction from the address we just computed.
913 // But, this case may be needed to cover non-standard
914 // plugboard configurations. According to the
915 // Technical Manual, page 12-6, change of seqeuence is
916 // the only time in which P₂.₉ is altered.
917 //
918 //
919 // Reasons for the answer "yes"
920 // ---------------------------
921 //
922 // The Instruction Execution chart on page 3-71 of the
923 // Users Handbook shows that the P register is updated
924 // to P+1 (see top row of table) and there is a
925 // reference to note 1. This note is on page 3073
926 // (section 3-3.3) and it says:
927 //
928 // In all expressions P + 1, P + 2, sums are reducible 2¹⁸.
929 // (777777 + 1) mod 2¹⁸ = 0.
930 //
931 // This seems to directly contradict section 12-2.3 of
932 // the Technical Manual (which we referenced in the
933 // "no" section above).
934 //
935 // This seems a clear statement that bit 2.9 is used
936 // as the most significant bit. It also seems to
937 // imply that if P=377777, P+1 will increment it to
938 // 400000.
939 let (_old_physical, old_mark) = self.regs.p.split();
940 let new_p = self.regs.p.successor(); // p now points at the next instruction.
941 let (_new_physical, new_mark) = new_p.split();
942 assert_eq!(old_mark, new_mark);
943 self.regs.p = new_p;
944 }
945 ProgramCounterChange::DismissAndWait(new_p) | ProgramCounterChange::Jump(new_p) => {
946 // Copy the value of P₂.₉ into `old_mark`.
947 let (_old_physical, old_mark) = self.regs.p.split();
948 // Update P, keeping the old value of P₂.₉.
949 self.regs.p = Address::join(new_p.into(), old_mark);
950 }
951 }
952 }
953
954 /// Consider whether a change of sequence is needed. If yes,
955 /// perform the change.
956 fn select_sequence(&mut self, mem: &mut MemoryUnit) -> RunMode {
957 if self.regs.previous_instruction_hold() {
958 event!(
959 Level::DEBUG,
960 concat!(
961 "Hold bit of previous instruction was set, ",
962 "so we will not consider a sequence change"
963 )
964 );
965 RunMode::Running
966 } else {
967 // Handle any possible change of sequence.
968 match self.regs.flags.highest_priority_raised_flag() {
969 None => {
970 // The current sequence's flag is no longer raised.
971 //
972 // This happens either because the sequence was
973 // dismissed (permanent or temporary drop-out) or IOSj
974 // 40000 ("LOWER FLAG J") had been issued. In the
975 // latter case, the current sequence should continue
976 // to run until another sequence's flag is raised.
977 if self.regs.current_sequence_is_runnable {
978 event!(
979 Level::DEBUG,
980 "No flag is raised, but the current sequence is still runnable"
981 );
982 RunMode::Running
983 } else {
984 event!(
985 Level::DEBUG,
986 "No flags raised and current sequence is not runnable: in LIMBO"
987 );
988 RunMode::InLimbo
989 }
990 }
991 Some(seq) => {
992 event!(Level::TRACE, "Highest-priority sequence is {}", seq);
993 if Some(seq) > self.regs.k
994 || (!self.regs.current_sequence_is_runnable && Some(seq) < self.regs.k)
995 {
996 self.change_sequence(mem, self.regs.k, seq);
997 RunMode::Running
998 } else {
999 // No sequence change, just carry on.
1000 RunMode::Running
1001 }
1002 }
1003 }
1004 }
1005 }
1006
1007 fn fetch_instruction(&mut self, ctx: &Context, mem: &mut MemoryUnit) -> Result<(), Alarm> {
1008 // TODO: This implementation begins the instruction fetch
1009 // operation by considering a possible change of sequence.
1010 // The TX-2 itself considers a sequence change as the PK cycle
1011 // is completed, in the resting state PK⁰⁰. So it might make
1012 // more sense to move the sequence-change logic to the end of
1013 // the instructino-execution implementation. That will likely
1014 // make it simpler to implement the "hold" bit and
1015 // dismiss-and-wait (i.e. cases where we don't increment the
1016 // sequence's program counter). When considering this option,
1017 // it's a good idea to re-read section 9-4 of Volume 2 of the
1018 // Technical Manual.
1019
1020 // If the previous instruction was held, we don't even scan
1021 // the flags. This follows the description of how control
1022 // handles flags in section 4-3.5 of the User Handbook (page
1023 // 4-8).
1024 // Calculate the address from which we will fetch the
1025 // instruction, and the increment the program counter.
1026 let p_physical_address = Address::from(self.regs.p.split().0);
1027 self.regs.diagnostic_only.instruction_address = p_physical_address;
1028 event!(
1029 Level::TRACE,
1030 "Fetching instruction from physical address {p_physical_address:>012o}"
1031 );
1032 // Actually fetch the instruction.
1033 let meta_op = if self.trap.set_metabits_of_instructions() {
1034 MetaBitChange::Set
1035 } else {
1036 MetaBitChange::None
1037 };
1038 let instruction_word: Unsigned36Bit = match mem.fetch(ctx, &p_physical_address, &meta_op) {
1039 Ok((inst, extra_bits)) => {
1040 if extra_bits.meta && self.trap.trap_on_marked_instruction() {
1041 self.raise_trap();
1042 }
1043 inst
1044 }
1045 Err(e) => match e {
1046 MemoryOpFailure::NotMapped(addr) => {
1047 self.alarm_unit.fire_if_not_masked(
1048 Alarm {
1049 sequence: self.regs.k,
1050 details: AlarmDetails::PSAL(
1051 u32::from(addr),
1052 "memory unit indicated physical address is not mapped".to_string(),
1053 ),
1054 },
1055 &self.regs.diagnostic_only,
1056 )?;
1057 // PSAL is masked, but we don't know what
1058 // instruction to execute, since we couldn't fetch
1059 // one. The program counter will not be updated
1060 // until we call execute_instruction(), so we
1061 // shouldn't increment it here. So we just return
1062 // a held instruction which is not otherwise
1063 // valid.
1064 Unsigned36Bit::from(Instruction::invalid())
1065 }
1066 MemoryOpFailure::ReadOnly(_, _) => unreachable!(),
1067 },
1068 };
1069 if let Some(k) = self.regs.k {
1070 event!(
1071 Level::TRACE,
1072 "Seq {:o} fetched instruction {:>012o} from physical address {:>012o}",
1073 k,
1074 instruction_word,
1075 p_physical_address
1076 );
1077 } else {
1078 event!(
1079 Level::ERROR,
1080 "Unspecified sequence fetched instruction {:>012o} from physical address {:>012o}",
1081 instruction_word,
1082 p_physical_address
1083 );
1084 }
1085 // Several things update the N register, so we update
1086 // `current_instruction` directly here instead of inside
1087 // update_n_register().
1088 self.regs.diagnostic_only.current_instruction = Instruction::from(instruction_word);
1089 self.update_n_register(instruction_word)?;
1090 Ok(())
1091 }
1092
1093 pub fn poll_hardware(
1094 &mut self,
1095 ctx: &Context,
1096 devices: &mut DeviceManager,
1097 mut run_mode: RunMode,
1098 ) -> Result<(RunMode, Option<Duration>), Alarm> {
1099 let (mut raised_flags, alarm, next_poll) = devices.poll(ctx, &mut self.alarm_unit)?;
1100 // If there are no hardware flags being raised, we may still
1101 // not be in limbo if there were already runnable sequences.
1102 // That is, if some sequence's flag was raised. The hardware
1103 // devices can raise flags but not lower them. Therefore if
1104 // run_mode was RunMode::Running on entry to this function, we
1105 // must return RunMode::Running.
1106 if raised_flags != 0 {
1107 run_mode = RunMode::Running;
1108 }
1109
1110 // For each newly-raised flag, raise the flag in self.flags.
1111 let raise_count = raised_flags.count_ones();
1112 if raise_count > 0 {
1113 event!(
1114 Level::DEBUG,
1115 "poll_hardware: {raise_count:2} new flag raises: {raised_flags:#o}"
1116 );
1117 } else {
1118 event!(Level::TRACE, "poll_hardware: no new flag raises");
1119 }
1120 for bitpos in 0.. {
1121 if raised_flags == 0 {
1122 break;
1123 }
1124 let mask = 1_u64 << bitpos;
1125 if raised_flags & mask != 0 {
1126 match SequenceNumber::try_from(bitpos) {
1127 Ok(unit) => {
1128 self.regs.flags.raise(&unit);
1129 }
1130 Err(_) => {
1131 break;
1132 }
1133 }
1134 }
1135 raised_flags &= !mask;
1136 }
1137
1138 // If a device raised an alarm, generate that alarm now. This
1139 // alarm was not an error return from the poll() method,
1140 // because we needed to ensure that all flag raised were
1141 // processed.
1142 if let Some(active) = alarm {
1143 event!(
1144 Level::INFO,
1145 "poll_hardware: an alarm is active: {:?}",
1146 active
1147 );
1148 Err(active)
1149 } else {
1150 Ok((run_mode, next_poll))
1151 }
1152 }
1153
1154 fn update_n_register(&mut self, instruction_word: Unsigned36Bit) -> Result<(), Alarm> {
1155 self.regs.n = Instruction::from(instruction_word);
1156 if let Ok(symbolic) = SymbolicInstruction::try_from(&self.regs.n) {
1157 self.regs.n_sym = Some(symbolic);
1158 Ok(()) // valid instruction
1159 } else {
1160 self.alarm_unit
1161 .fire_if_not_masked(self.invalid_opcode_alarm(), &self.regs.diagnostic_only)?;
1162 self.regs.n_sym = None;
1163 Ok(()) // invalid instruction, but OCSAL is masked.
1164 }
1165 }
1166
1167 fn invalid_opcode_alarm(&self) -> Alarm {
1168 Alarm {
1169 sequence: self.regs.k,
1170 details: AlarmDetails::OCSAL(
1171 self.regs.n,
1172 format!("invalid opcode {:#o}", self.regs.n.opcode_number()),
1173 ),
1174 }
1175 }
1176
1177 fn invalid_opr_subcode(&self, bit2dot7: bool) -> Alarm {
1178 let opcode = self.regs.n.opcode_number();
1179 let ch: char = if bit2dot7 { '1' } else { '0' };
1180 Alarm {
1181 sequence: self.regs.k,
1182 details: AlarmDetails::OCSAL(
1183 self.regs.n,
1184 format!(
1185 "Opcode OPR/IOS/AOP used with opcode {opcode:#o} and invalid subcode 1{ch}"
1186 ),
1187 ),
1188 }
1189 }
1190
1191 fn estimate_execute_time_ns(&self, orig_inst: &Instruction) -> u64 {
1192 let inst_from: Address = self.regs.p; // this is now P+1 but likely in the same memory type.
1193 let defer_from = match orig_inst.operand_address().split() {
1194 (true, physical) => Some(physical),
1195 (false, _) => None,
1196 };
1197
1198 // TODO: handle chains of deferred loads
1199 let operand_from: Option<Address> = match self.regs.n.operand_address().split() {
1200 (true, physical) => Some(physical),
1201 (false, _) => None,
1202 };
1203
1204 timing::estimate_instruction_ns(
1205 inst_from,
1206 orig_inst.opcode_number(),
1207 defer_from,
1208 operand_from,
1209 )
1210 }
1211
1212 /// Fetch and execute the next instruction pointed to by the P
1213 /// register. Returns the estimated number of nanoseconds needed
1214 /// to execute the instruction.
1215 pub fn execute_instruction(
1216 &mut self,
1217 ctx: &Context,
1218 devices: &mut DeviceManager,
1219 mem: &mut MemoryUnit,
1220 poll_order_change: &mut Option<SequenceNumber>,
1221 ) -> Result<(u64, RunMode, Option<OutputEvent>), (Alarm, Address)> {
1222 fn execute(
1223 ctx: &Context,
1224 prev_program_counter: Address,
1225 opcode: &Opcode,
1226 control: &mut ControlUnit,
1227 devices: &mut DeviceManager,
1228 mem: &mut MemoryUnit,
1229 ) -> Result<OpcodeResult, Alarm> {
1230 match opcode {
1231 Opcode::Aux => control.op_aux(ctx, mem),
1232 Opcode::Lda => control.op_lda(ctx, mem),
1233 Opcode::Ldb => control.op_ldb(ctx, mem),
1234 Opcode::Ldc => control.op_ldc(ctx, mem),
1235 Opcode::Ldd => control.op_ldd(ctx, mem),
1236 Opcode::Lde => control.op_lde(ctx, mem),
1237 Opcode::Sta => control.op_sta(ctx, mem),
1238 Opcode::Stb => control.op_stb(ctx, mem),
1239 Opcode::Stc => control.op_stc(ctx, mem),
1240 Opcode::Std => control.op_std(ctx, mem),
1241 Opcode::Ste => control.op_ste(ctx, mem),
1242 Opcode::Rsx => control.op_rsx(ctx, mem),
1243 Opcode::Skx => control.op_skx(ctx),
1244 Opcode::Dpx => control.op_dpx(ctx, mem),
1245 Opcode::Jmp => control.op_jmp(ctx, mem),
1246 Opcode::Jpx => control.op_jpx(ctx, mem),
1247 Opcode::Jnx => control.op_jnx(ctx, mem),
1248 Opcode::Skm => control.op_skm(ctx, mem),
1249 Opcode::Spg => control.op_spg(ctx, mem),
1250 Opcode::Opr => control.op_opr(ctx, mem, devices), // Usually IOS.
1251 Opcode::Tsd => control.op_tsd(ctx, devices, prev_program_counter, mem),
1252 Opcode::Sed => control.op_sed(ctx, mem),
1253 Opcode::Exx => Err(Alarm {
1254 sequence: control.regs.k,
1255 details: AlarmDetails::ROUNDTUITAL {
1256 explanation: "The emulator does not yet implement opcode EXX".to_string(),
1257 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/16",
1258 },
1259 }),
1260 Opcode::Adx => Err(Alarm {
1261 sequence: control.regs.k,
1262 details: AlarmDetails::ROUNDTUITAL {
1263 explanation: "The emulator does not yet implement opcode ADX".to_string(),
1264 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/18",
1265 },
1266 }),
1267 Opcode::Spf => Err(Alarm {
1268 sequence: control.regs.k,
1269 details: AlarmDetails::ROUNDTUITAL {
1270 explanation: "The emulator does not yet implement opcode SPF".to_string(),
1271 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/13",
1272 },
1273 }),
1274 Opcode::Flf | Opcode::Flg => Err(Alarm {
1275 sequence: control.regs.k,
1276 details: AlarmDetails::ROUNDTUITAL {
1277 explanation: "The emulator does not yet implement opcode {opcode}"
1278 .to_string(),
1279 // Note: that bug report covers two opcodes.
1280 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/14",
1281 },
1282 }),
1283 Opcode::Ite | Opcode::Ita | Opcode::Una | Opcode::Dsa => Err(Alarm {
1284 sequence: control.regs.k,
1285 details: AlarmDetails::ROUNDTUITAL {
1286 explanation: "The emulator does not yet implement opcode {opcode}"
1287 .to_string(),
1288 // Note: this bug report covers several opcodes.
1289 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/25",
1290 },
1291 }),
1292 Opcode::Jov => Err(Alarm {
1293 sequence: control.regs.k,
1294 details: AlarmDetails::ROUNDTUITAL {
1295 explanation: "The emulator does not yet implement opcode JOV".to_string(),
1296 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/11",
1297 },
1298 }),
1299 Opcode::Jpa => Err(Alarm {
1300 sequence: control.regs.k,
1301 details: AlarmDetails::ROUNDTUITAL {
1302 explanation: "The emulator does not yet implement opcode JPA".to_string(),
1303 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/9",
1304 },
1305 }),
1306 Opcode::Jna => Err(Alarm {
1307 sequence: control.regs.k,
1308 details: AlarmDetails::ROUNDTUITAL {
1309 explanation: "The emulator does not yet implement opcode JNA".to_string(),
1310 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/10",
1311 },
1312 }),
1313 Opcode::Exa => Err(Alarm {
1314 sequence: control.regs.k,
1315 details: AlarmDetails::ROUNDTUITAL {
1316 explanation: "The emulator does not yet implement opcode EXA".to_string(),
1317 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/143",
1318 },
1319 }),
1320 Opcode::Ins => Err(Alarm {
1321 sequence: control.regs.k,
1322 details: AlarmDetails::ROUNDTUITAL {
1323 explanation: "The emulator does not yet implement opcode INS".to_string(),
1324 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/26",
1325 },
1326 }),
1327 Opcode::Com => Err(Alarm {
1328 sequence: control.regs.k,
1329 details: AlarmDetails::ROUNDTUITAL {
1330 explanation: "The emulator does not yet implement opcode COM".to_string(),
1331 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/27",
1332 },
1333 }),
1334 Opcode::Cya | Opcode::Cyb | Opcode::Cab => Err(Alarm {
1335 sequence: control.regs.k,
1336 details: AlarmDetails::ROUNDTUITAL {
1337 explanation: "The emulator does not yet implement opcode {opcode}"
1338 .to_string(),
1339 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/24",
1340 },
1341 }),
1342 Opcode::Noa => Err(Alarm {
1343 sequence: control.regs.k,
1344 details: AlarmDetails::ROUNDTUITAL {
1345 explanation: "The emulator does not yet implement opcode NOA".to_string(),
1346 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/22",
1347 },
1348 }),
1349 Opcode::Nab => Err(Alarm {
1350 sequence: control.regs.k,
1351 details: AlarmDetails::ROUNDTUITAL {
1352 explanation: "The emulator does not yet implement opcode NAB".to_string(),
1353 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/23",
1354 },
1355 }),
1356 Opcode::Add => Err(Alarm {
1357 sequence: control.regs.k,
1358 details: AlarmDetails::ROUNDTUITAL {
1359 explanation: "The emulator does not yet implement opcode ADD".to_string(),
1360 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/28",
1361 },
1362 }),
1363 Opcode::Sca => Err(Alarm {
1364 sequence: control.regs.k,
1365 details: AlarmDetails::ROUNDTUITAL {
1366 explanation: "The emulator does not yet implement opcode SCA".to_string(),
1367 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/19",
1368 },
1369 }),
1370 Opcode::Scb => Err(Alarm {
1371 sequence: control.regs.k,
1372 details: AlarmDetails::ROUNDTUITAL {
1373 explanation: "The emulator does not yet implement opcode SCB".to_string(),
1374 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/20",
1375 },
1376 }),
1377 Opcode::Sab => Err(Alarm {
1378 sequence: control.regs.k,
1379 details: AlarmDetails::ROUNDTUITAL {
1380 explanation: "The emulator does not yet implement opcode SAB".to_string(),
1381 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/21",
1382 },
1383 }),
1384 Opcode::Tly => Err(Alarm {
1385 sequence: control.regs.k,
1386 details: AlarmDetails::ROUNDTUITAL {
1387 explanation: "The emulator does not yet implement opcode TLY".to_string(),
1388 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/32",
1389 },
1390 }),
1391 Opcode::Div => Err(Alarm {
1392 sequence: control.regs.k,
1393 details: AlarmDetails::ROUNDTUITAL {
1394 explanation: "The emulator does not yet implement opcode DIV".to_string(),
1395 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/31",
1396 },
1397 }),
1398 Opcode::Mul => Err(Alarm {
1399 sequence: control.regs.k,
1400 details: AlarmDetails::ROUNDTUITAL {
1401 explanation: "The emulator does not yet implement opcode MUL".to_string(),
1402 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/30",
1403 },
1404 }),
1405 Opcode::Sub => Err(Alarm {
1406 sequence: control.regs.k,
1407 details: AlarmDetails::ROUNDTUITAL {
1408 explanation: "The emulator does not yet implement opcode SUB".to_string(),
1409 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/29",
1410 },
1411 }),
1412 }
1413 }
1414
1415 if self.select_sequence(mem) == RunMode::InLimbo {
1416 return Ok((0, RunMode::InLimbo, None));
1417 }
1418
1419 let seq_desc = match self.regs.k {
1420 None => "none".to_string(),
1421 Some(n) => format!("{n:02o}"),
1422 };
1423
1424 // Fetch the current instruction into the N register.
1425 {
1426 let span = span!(Level::INFO,
1427 "fetch",
1428 seq=%seq_desc,
1429 p=?self.regs.p);
1430 let _enter = span.enter();
1431 self.fetch_instruction(ctx, mem)
1432 .map_err(|alarm| (alarm, self.regs.p))?;
1433 }
1434
1435 // Save the old program counter.
1436 let p = self.regs.p;
1437 self.set_program_counter(ProgramCounterChange::CounterUpdate);
1438
1439 let elapsed_time = self.estimate_execute_time_ns(&self.regs.n);
1440
1441 let result: Result<Option<OutputEvent>, (Alarm, Address)> =
1442 if let Some(sym) = self.regs.n_sym.as_ref() {
1443 let inst = sym.to_string();
1444 let span = span!(Level::INFO,
1445 "xop",
1446 seq=%seq_desc,
1447 p=?p,
1448 op=%sym.opcode());
1449 let _enter = span.enter();
1450 event!(Level::TRACE, "executing instruction {}", &sym);
1451 match execute(ctx, p, &sym.opcode(), self, devices, mem) {
1452 Ok(opcode_result) => {
1453 event!(
1454 Level::TRACE,
1455 "opcode_result.poll_order_change={:?}",
1456 &opcode_result.poll_order_change
1457 );
1458 if let Some(seq) = opcode_result.poll_order_change {
1459 *poll_order_change = Some(seq);
1460 please_poll_soon(ctx, devices, seq);
1461 }
1462 match opcode_result.program_counter_change {
1463 None => {
1464 // This is the usual case; the call to
1465 // set_program_counter above will have
1466 // incremented P.
1467 }
1468 Some(pc_change) => {
1469 // Instructions which return
1470 // ProgramCounterChange::CounterUpdate do
1471 // so in order perform a skip over the
1472 // next instruction.
1473 event!(Level::TRACE, "program counter change: {:?}", &pc_change);
1474 self.set_program_counter(pc_change);
1475 }
1476 }
1477 Ok(opcode_result.output)
1478 }
1479 Err(alarm) => {
1480 event!(Level::WARN, "instruction {} raised alarm {}", inst, alarm);
1481 match self
1482 .alarm_unit
1483 .fire_if_not_masked(alarm.clone(), &self.regs.diagnostic_only)
1484 {
1485 Err(alarm) => {
1486 event!(
1487 Level::DEBUG,
1488 "execute_instruction: unmasked_alarm_active: {}",
1489 self.unmasked_alarm_active()
1490 );
1491 self.set_program_counter(ProgramCounterChange::Stop(p));
1492 Err((alarm, p))
1493 }
1494 Ok(()) => {
1495 // The alarm is active but masked.
1496 event!(
1497 Level::DEBUG,
1498 "execute_instruction: alarm {:?} is active but masked",
1499 alarm
1500 );
1501 Ok(None) // no output event.
1502 }
1503 }
1504 }
1505 }
1506 } else {
1507 event!(
1508 Level::INFO,
1509 "fetched instruction {:?} is invalid",
1510 &self.regs.n
1511 );
1512 match self
1513 .alarm_unit
1514 .fire_if_not_masked(self.invalid_opcode_alarm(), &self.regs.diagnostic_only)
1515 {
1516 Err(e) => {
1517 self.set_program_counter(ProgramCounterChange::Stop(p));
1518 Err((e, p))
1519 }
1520 Ok(()) => Ok(None),
1521 }
1522 };
1523
1524 // We have completed an attempt to execute instruction. It
1525 // may not have been successful, so we cannot set the value of
1526 // prev_hold unconditionally. Determine whether this
1527 // instruction should be followed by a change of sequence.
1528 match result {
1529 Ok(maybe_output) => {
1530 let new_mode: RunMode = self.select_sequence(mem);
1531 Ok((elapsed_time, new_mode, maybe_output))
1532 }
1533 Err((alarm, address)) => Err((alarm, address)),
1534 }
1535 // self.regs.k now identifies the sequence we should be
1536 // running and self.regs.p contains its program counter.
1537 }
1538
1539 fn get_config(&self) -> SystemConfiguration {
1540 let cf = self.regs.n.configuration();
1541 self.regs.get_f_mem(cf)
1542 }
1543
1544 fn fetch_operand_from_address_with_exchange(
1545 &mut self,
1546 ctx: &Context,
1547 mem: &mut MemoryUnit,
1548 operand_address: &Address,
1549 existing_dest: &Unsigned36Bit,
1550 update_e: &UpdateE,
1551 ) -> Result<(Unsigned36Bit, ExtraBits), Alarm> {
1552 let (memword, extra) =
1553 self.fetch_operand_from_address_without_exchange(ctx, mem, operand_address, update_e)?;
1554 let exchanged = exchanged_value_for_load(&self.get_config(), &memword, existing_dest);
1555 Ok((exchanged, extra))
1556 }
1557
1558 fn fetch_operand_from_address_without_exchange(
1559 &mut self,
1560 ctx: &Context,
1561 mem: &mut MemoryUnit,
1562 operand_address: &Address,
1563 update_e: &UpdateE,
1564 ) -> Result<(Unsigned36Bit, ExtraBits), Alarm> {
1565 let meta_op: MetaBitChange = if self.trap.set_metabits_of_operands() {
1566 MetaBitChange::Set
1567 } else {
1568 MetaBitChange::None
1569 };
1570 match mem.fetch(ctx, operand_address, &meta_op) {
1571 Ok((word, extra_bits)) => {
1572 if extra_bits.meta && self.trap.trap_on_operand() {
1573 self.raise_trap();
1574 }
1575 if let UpdateE::Yes = update_e {
1576 mem.set_e_register(word);
1577 }
1578 Ok((word, extra_bits))
1579 }
1580 Err(MemoryOpFailure::NotMapped(addr)) => {
1581 self.alarm_unit.fire_if_not_masked(
1582 Alarm {
1583 sequence: self.regs.k,
1584 details: AlarmDetails::QSAL(
1585 self.regs.n,
1586 BadMemOp::Read(Unsigned36Bit::from(addr)),
1587 format!(
1588 "memory unit indicated address {operand_address:o} is not mapped",
1589 ),
1590 ),
1591 },
1592 &self.regs.diagnostic_only,
1593 )?;
1594 // QSAL is masked to we have to return some value, but
1595 // we don't know what the TX-2 did in this case.
1596 Err(self.alarm_unit.always_fire(Alarm {
1597 sequence: self.regs.k,
1598 details: AlarmDetails::ROUNDTUITAL {
1599 explanation: format!(
1600 "memory unit indicated address {operand_address:o} is not mapped and we don't know what to do when QSAL is masked",
1601 ),
1602 // Note: there are two different ways in which
1603 // we can raise a ROUNDTUITAL alarm referring
1604 // to this bug report URL.
1605 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/142",
1606 }
1607 }, &self.regs.diagnostic_only))
1608 }
1609 Err(MemoryOpFailure::ReadOnly(_, _)) => unreachable!(),
1610 }
1611 }
1612
1613 fn memory_store_without_exchange(
1614 &mut self,
1615 ctx: &Context,
1616 mem: &mut MemoryUnit,
1617 target: &Address,
1618 value: &Unsigned36Bit,
1619 update_e: &UpdateE,
1620 meta_op: &MetaBitChange,
1621 ) -> Result<(), Alarm> {
1622 event!(
1623 Level::TRACE,
1624 "memory_store_without_exchange: write @{:>06o} <- {:o}",
1625 target,
1626 value,
1627 );
1628 if let &UpdateE::Yes = update_e {
1629 // The E register gets updated to the value we want to
1630 // write, even if we cannot actually write it (see example
1631 // 10 on page 3-17 of the Users Handbook).
1632 mem.set_e_register(*value);
1633 }
1634 if let Err(e) = mem.store(ctx, target, value, meta_op) {
1635 self.alarm_unit.fire_if_not_masked(
1636 Alarm {
1637 sequence: self.regs.k,
1638 details: AlarmDetails::QSAL(
1639 self.regs.n,
1640 BadMemOp::Write(Unsigned36Bit::from(*target)),
1641 format!("memory store to address {target:#o} failed: {e}",),
1642 ),
1643 },
1644 &self.regs.diagnostic_only,
1645 )?;
1646 Ok(()) // QSAL is masked, so just carry on.
1647 } else {
1648 Ok(())
1649 }
1650 }
1651
1652 #[allow(clippy::too_many_arguments)]
1653 fn memory_store_with_exchange(
1654 &mut self,
1655 ctx: &Context,
1656 mem: &mut MemoryUnit,
1657 target: &Address,
1658 value: &Unsigned36Bit,
1659 existing: &Unsigned36Bit,
1660 update_e: &UpdateE,
1661 meta_op: &MetaBitChange,
1662 ) -> Result<(), Alarm> {
1663 self.memory_store_without_exchange(
1664 ctx,
1665 mem,
1666 target,
1667 &exchanged_value_for_store(&self.get_config(), value, existing),
1668 update_e,
1669 meta_op,
1670 )
1671 }
1672
1673 /// Determine the address of the operand for instructions that use
1674 /// indexing.
1675 fn operand_address_with_optional_defer_and_index(
1676 self: &mut ControlUnit,
1677 ctx: &Context,
1678 mem: &mut MemoryUnit,
1679 ) -> Result<Address, Alarm> {
1680 self.resolve_operand_address(ctx, mem, None)
1681 }
1682
1683 /// Determine the address of the operand for instructions that do
1684 /// not use indexing.
1685 fn operand_address_with_optional_defer_without_index(
1686 self: &mut ControlUnit,
1687 ctx: &Context,
1688 mem: &mut MemoryUnit,
1689 ) -> Result<Address, Alarm> {
1690 self.resolve_operand_address(ctx, mem, Some(Unsigned6Bit::ZERO))
1691 }
1692
1693 /// Resolve the address of the operand of the current instruction,
1694 /// leaving this address in the Q register.
1695 ///
1696 /// # Arguments
1697 ///
1698 /// * `ctx` - context data for the execution of the instruction.
1699 /// * `mem` - the memory unit (which we would use for deferred indexing).
1700 /// * `initial_index_override` - source of the j bits (index
1701 /// register number) to be used; this is specified when processing
1702 /// the `JNX` instruction for example. If the value is `None`,
1703 /// the value is instead taken from the N register.
1704 fn resolve_operand_address(
1705 self: &mut ControlUnit,
1706 ctx: &Context,
1707 mem: &mut MemoryUnit,
1708 initial_index_override: Option<Unsigned6Bit>,
1709 ) -> Result<Address, Alarm> {
1710 // The deferred addressing process may be performed more than
1711 // once, in other words it is a loop. This is explained in
1712 // section 9-7, "DEFERRED ADDRESSING CYCLES" of Volume 2 of
1713 // the technical manual.
1714 //
1715 // See also "TX-2 Introductory Notes" by A. Vanderburgh, 24
1716 // February 1959, available from the UMN collection (BCI61 Box
1717 // 8).
1718 let mut seen_deferred_addresses: HashSet<Address> = HashSet::new();
1719 let physical_address: Address = loop {
1720 let (defer, physical) = self.regs.n.operand_address().split();
1721 if !defer {
1722 break physical;
1723 }
1724
1725 // In effect, this loop emulates a non-ultimate deferred
1726 // address cycle.
1727 //
1728 // According to the description of PK3 on page 5-9 of the
1729 // User handbook, the deferred address calculation and
1730 // indexing occurs in (i.e. by modifying) the N register.
1731 // "TX-2 Introductory Notes" explains the same thing.
1732 //
1733 // JPX and JNX seem to be handled differently, but I don't
1734 // think I understand exactly what the difference is
1735 // supposed to be. "TX-2 Introductory Notes" points out
1736 // that in JPX and JNX, the j bits are used for something
1737 // other than indexing, so the "handled differently" may
1738 // just be that deferred addressing is the only way to use
1739 // indexing in combination with JPX and JNX.
1740 //
1741 // (Vol 2, page 12-9): It should also be noted that the
1742 // N₂.₉ bit is presented as an input to the X Adder only
1743 // when no deferred address cycles are called for. When
1744 // PI¹₂, the input to the X Adder from the N₂.₉ position
1745 // is forced to appear as a ZERO.
1746 event!(
1747 Level::TRACE,
1748 "deferred addressing: deferred address is {:o}",
1749 &physical
1750 );
1751 let meta_op = if self.trap.set_metabits_of_deferred_addresses() {
1752 MetaBitChange::Set
1753 } else {
1754 MetaBitChange::None
1755 };
1756 let fetched = match mem.fetch(ctx, &physical, &meta_op) {
1757 Err(e) => {
1758 let msg = || {
1759 format!(
1760 "address {:#o} out of range while fetching deferred address: {}",
1761 &physical, e
1762 )
1763 };
1764
1765 self.alarm_unit.fire_if_not_masked(
1766 Alarm {
1767 sequence: self.regs.k,
1768 details: AlarmDetails::QSAL(
1769 self.regs.n,
1770 BadMemOp::Read(Unsigned36Bit::from(physical)),
1771 msg(),
1772 ),
1773 },
1774 &self.regs.diagnostic_only,
1775 )?;
1776 // QSAL is masked. I don't know what the TX-2 did in this situation.
1777 return Err(self.alarm_unit.always_fire(
1778 Alarm {
1779 sequence: self.regs.k,
1780 details: AlarmDetails::ROUNDTUITAL {
1781 explanation: format!(
1782 "we don't know how to handle {} when QSAL is masked",
1783 msg()
1784 ),
1785 // Note: there are two different ways in
1786 // which we can raise a ROUNDTUITAL alarm
1787 // referring to this bug report URL.
1788 bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/142",
1789 },
1790 },
1791 &self.regs.diagnostic_only,
1792 ));
1793 }
1794 Ok((word, extra)) => {
1795 if extra.meta && self.trap.trap_on_deferred_address() {
1796 self.raise_trap();
1797 }
1798
1799 // The TX2 performs indexation on deferred
1800 // addreses. Indeed, the "TX-2 Introductory
1801 // Notes" by A. Vanderburg imply that the ability
1802 // to do this is the motivation for having
1803 // deferred addressing at all (see section
1804 // "Deferred Addressing" in that document).
1805 //
1806 // This idea is also supported by the fact that
1807 // the left subword of deferred addresses used in
1808 // plugboard programs can be nonzero, and on the
1809 // fact that the description of the SKM
1810 // instruction notes "SKM is therefore
1811 // non-indexable except through deferred
1812 // addressing".
1813 let (left, right) = subword::split_halves(word);
1814 let mask: Unsigned18Bit = Unsigned18Bit::from(63_u8);
1815 let j6: Unsigned6Bit = Unsigned6Bit::try_from(left.bitand(mask)).unwrap();
1816 let next = Address::from(right).index_by(self.regs.get_index_register(j6));
1817 event!(
1818 Level::TRACE,
1819 "deferred addressing: fetched full word is {:o},,{:o}; j={:o}, using {:o} as the next address",
1820 &left,
1821 &right,
1822 &j6,
1823 &next,
1824 );
1825 if !seen_deferred_addresses.insert(next) {
1826 // A `false` return indicates that the map
1827 // already contained `next`, meaning that we
1828 // have a loop.
1829 //
1830 // Detection of this kind of loop is not a
1831 // feature of the TX-2. The "TX-2
1832 // Introductory Notes" document explicitly
1833 // states, "In fact, you can inadvertantly
1834 // [sic] defer back to where you started and
1835 // get a loop less than one instruction long".
1836 return Err(self.alarm_unit.always_fire(
1837 Alarm {
1838 sequence: self.regs.k,
1839 details: AlarmDetails::DEFERLOOPAL { address: right },
1840 },
1841 &self.regs.diagnostic_only,
1842 ));
1843 }
1844 next
1845 }
1846 };
1847
1848 // We update the lower 18 bits (i.e. right half) of N with
1849 // the value we just loaded from memory.
1850 let unchanged_left = subword::left_half(Unsigned36Bit::from(self.regs.n));
1851 self.update_n_register(subword::join_halves(
1852 unchanged_left,
1853 Unsigned18Bit::from(fetched),
1854 ))?;
1855 };
1856
1857 // The defer bit in N is (now) not set. Emulate a regular or
1858 // ultimate address cycle. That is, add the index value to
1859 // the operand address. While the index_address field in the
1860 // instruction is unsigned (following the conventions in the
1861 // assembly source), the indexation operation itself uses
1862 // signed arithmetic (see the explanation in the doc comment
1863 // for the IndexBy trait).
1864 let j = match initial_index_override {
1865 None => self.regs.n.index_address(),
1866 Some(overridden) => overridden,
1867 };
1868 let delta = self.regs.get_index_register(j); // this is Xj.
1869
1870 // A number of things expect that the "most recent data
1871 // (memory) reference" is saved in register Q. `¹⁴JMP`
1872 // (a.k.a. `JPQ`) makes use of this, for example.
1873 self.regs.q = physical_address.index_by(delta);
1874
1875 // TODO: figure out if other parts of the system documentation
1876 // definitely expect the physical operand address to be
1877 // written back into the N register (in a
1878 // programmer-detectable way).
1879 //
1880 // "TX-2 Introductory Notes" states that this happens, but the
1881 // question is whether the programmer can detect it.
1882 Ok(self.regs.q)
1883 }
1884
1885 fn write_operand_metaop(&self) -> MetaBitChange {
1886 if self.trap.set_metabits_of_operands() {
1887 MetaBitChange::Set
1888 } else {
1889 MetaBitChange::None
1890 }
1891 }
1892
1893 fn memory_read_and_update_with_exchange<F>(
1894 &mut self,
1895 ctx: &Context,
1896 mem: &mut MemoryUnit,
1897 target: &Address,
1898 update_e: &UpdateE,
1899 transform: F,
1900 ) -> Result<(), Alarm>
1901 where
1902 F: FnOnce(Unsigned36Bit) -> Unsigned36Bit,
1903 {
1904 // We unconditionally perform the memory read. But in the
1905 // real TX-2 there are cases where the read may not actually
1906 // need to happen. For example the instruction `⁰STA T` is
1907 // implemented by a call to this function but does not need to
1908 // read from `target` although similar instructions might.
1909 // For example, `²STA T` modifies L(T) but not R(T) and so if
1910 // there were a hardware parity error affecting T, this
1911 // instruction would likely fail on the real hardware.
1912 match self.fetch_operand_from_address_without_exchange(ctx, mem, target, &UpdateE::No) {
1913 Ok((existing, _meta)) => {
1914 let newval = transform(existing);
1915 self.memory_store_with_exchange(
1916 ctx,
1917 mem,
1918 target,
1919 &newval,
1920 &existing,
1921 update_e,
1922 &self.write_operand_metaop(),
1923 )
1924 }
1925 Err(Alarm {
1926 sequence: _,
1927 details: AlarmDetails::QSAL(inst, BadMemOp::Read(addr), msg),
1928 }) => {
1929 // That read operation just failed. So we handle this
1930 // as a _write_ failure, meaning that we change
1931 // BadMemOp::Read to BadMemOp::Write.
1932 //
1933 // If fire_details_if_not_masked() returns (), QSAL is
1934 // masked, we just carry on (the `DPX` instruction has
1935 // no effect).
1936 self.fire_details_if_not_masked(AlarmDetails::QSAL(
1937 inst,
1938 BadMemOp::Write(addr),
1939 msg,
1940 ))
1941 }
1942 Err(other) => Err(other),
1943 }
1944 }
1945
1946 fn dismiss(&mut self, reason: &str) {
1947 if let Some(current_seq) = self.regs.k {
1948 event!(
1949 Level::DEBUG,
1950 "dismissing current sequence {current_seq:o} (reason: {reason}) while executing instruction from {:o}",
1951 self.regs.diagnostic_only.instruction_address,
1952 );
1953 self.regs.flags.lower(¤t_seq);
1954 self.regs.current_sequence_is_runnable = false;
1955 }
1956 }
1957
1958 fn dismiss_unless_held(&mut self, reason: &str) -> bool {
1959 if self.regs.n.is_held() {
1960 false
1961 } else {
1962 self.dismiss(reason);
1963 true
1964 }
1965 }
1966
1967 #[must_use]
1968 pub fn current_flag_state(&self, unit: &SequenceNumber) -> bool {
1969 self.regs.current_flag_state(unit)
1970 }
1971
1972 pub fn drain_alarm_changes(&mut self) -> BTreeMap<AlarmKind, AlarmStatus> {
1973 self.alarm_unit.drain_alarm_changes()
1974 }
1975
1976 pub fn drain_flag_changes(&mut self) -> Vec<(SequenceNumber, Signed18Bit)> {
1977 self.regs
1978 .flags
1979 .drain_flag_changes()
1980 .into_iter()
1981 .map(|seq| {
1982 let index_value: &Signed18Bit = self
1983 .regs
1984 .index_regs
1985 .get(usize::from(seq))
1986 .expect("sequences should all have valid index register values");
1987 (seq, *index_value)
1988 })
1989 .collect()
1990 }
1991
1992 #[must_use]
1993 pub fn inspect_registers(&self) -> &ControlRegisters {
1994 &self.regs
1995 }
1996}
1997
1998impl Default for ControlUnit {
1999 fn default() -> Self {
2000 Self::new(
2001 PanicOnUnmaskedAlarm::No,
2002 ConfigurationMemorySetup::Uninitialised,
2003 )
2004 }
2005}
2006
2007impl Alarmer for ControlUnit {
2008 fn fire_if_not_masked<F: DiagnosticFetcher>(
2009 &mut self,
2010 alarm_instance: Alarm,
2011 get_diags: F,
2012 ) -> Result<(), Alarm> {
2013 self.alarm_unit
2014 .fire_if_not_masked(alarm_instance, get_diags)
2015 }
2016
2017 fn always_fire<F: DiagnosticFetcher>(&mut self, alarm_instance: Alarm, get_diags: F) -> Alarm {
2018 self.alarm_unit.always_fire(alarm_instance, get_diags)
2019 }
2020}