cpu/control/
op_configuration.rs

1//! Implementations of "Configuration Memory Class" opcodes
2//!
3//! - SPF (unimplemented)
4//! - SPG: [`ControlUnit::op_spg`]
5//! - FLF (unimplemented)
6//! - FLG (unimplemented)
7
8use base::prelude::*;
9use base::subword;
10
11use super::Alarm;
12use super::context::Context;
13use super::control::{ControlUnit, OpcodeResult, UpdateE};
14use super::exchanger::SystemConfiguration;
15use super::memory::MemoryUnit;
16
17use tracing::{Level, event};
18
19impl ControlUnit {
20    /// Implements the SPG instruction.
21    pub(crate) fn op_spg(
22        &mut self,
23        ctx: &Context,
24        mem: &mut MemoryUnit,
25    ) -> Result<OpcodeResult, Alarm> {
26        let c = usize::from(self.regs.n.configuration());
27        let target = self.operand_address_with_optional_defer_and_index(ctx, mem)?;
28        let (word, _meta) =
29            self.fetch_operand_from_address_without_exchange(ctx, mem, &target, &UpdateE::Yes)?;
30        for (quarter_number, cfg_value) in subword::quarters(word).iter().rev().enumerate() {
31            let pos = c + quarter_number;
32            let newvalue = (*cfg_value).into();
33            if pos != 0 {
34                self.regs.f_memory[pos] = newvalue;
35            } else if newvalue != SystemConfiguration::zero() {
36                event!(
37                    Level::WARN,
38                    "Ignoring attempt to set system configuration 0 to {:?}",
39                    newvalue
40                );
41            }
42        }
43        Ok(OpcodeResult::default())
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::super::super::context::Context;
50    use super::super::super::control::{ConfigurationMemorySetup, PanicOnUnmaskedAlarm, UpdateE};
51    use super::super::super::exchanger::SystemConfiguration;
52    use super::super::super::memory::MetaBitChange;
53    use super::super::super::{MemoryConfiguration, MemoryUnit};
54    use base::instruction::{Opcode, SymbolicInstruction};
55    use base::prelude::*;
56    use core::time::Duration;
57
58    use super::ControlUnit;
59
60    fn make_ctx() -> Context {
61        Context {
62            simulated_time: Duration::new(42, 42),
63            real_elapsed_time: Duration::new(7, 12),
64        }
65    }
66
67    fn cfg_loc(n: u8) -> Unsigned5Bit {
68        Unsigned5Bit::try_from(n).expect("bad test data; config location out of range")
69    }
70
71    fn cfg_val(n: u16) -> SystemConfiguration {
72        SystemConfiguration::from(Unsigned9Bit::try_from(n).expect("bad system config number"))
73    }
74
75    /// Simulate an SPG instruction and return the configuration
76    /// values that got loaded, and the value held in the E register.
77    fn simulate_spg(
78        ctx: &Context,
79        cfg: u8,
80        configdata: Unsigned36Bit,
81    ) -> ([SystemConfiguration; 4], Unsigned36Bit) {
82        const COMPLAIN: &str = "failed to set up test data";
83
84        // Given... values for f-memory data loaded into memory
85        let mut control = ControlUnit::new(
86            PanicOnUnmaskedAlarm::Yes,
87            ConfigurationMemorySetup::Uninitialised,
88        );
89        let mut mem = MemoryUnit::new(
90            ctx,
91            &MemoryConfiguration {
92                with_u_memory: false,
93            },
94        );
95        let configdata_address = Address::from(u18!(0o100));
96        control
97            .memory_store_without_exchange(
98                ctx,
99                &mut mem,
100                &configdata_address,
101                &configdata,
102                &UpdateE::Yes,
103                &MetaBitChange::None,
104            )
105            .expect(COMPLAIN);
106
107        // When... we perform an SPG instruction
108        // inst encodes the instruction ⁰⁴SPG configdata_address.
109        let inst = SymbolicInstruction {
110            held: false,
111            configuration: Unsigned5Bit::try_from(cfg).expect(COMPLAIN),
112            opcode: Opcode::Spg,
113            index: Unsigned6Bit::ZERO,
114            operand_address: OperandAddress::direct(configdata_address),
115        };
116        control
117            .update_n_register(Instruction::from(&inst).bits())
118            .expect(COMPLAIN);
119        if let Err(e) = control.op_spg(ctx, &mut mem) {
120            panic!("SPG instruction failed: {e}");
121        }
122
123        (
124            [
125                control.regs.get_f_mem(cfg_loc(cfg)),
126                control.regs.get_f_mem(cfg_loc(cfg + 1)),
127                control.regs.get_f_mem(cfg_loc(cfg + 2)),
128                control.regs.get_f_mem(cfg_loc(cfg + 3)),
129            ],
130            mem.get_e_register(),
131        )
132    }
133
134    /// Verify that the SPG instruction loads the data into F-memory
135    /// in the correct order.
136    #[test]
137    fn op_spg_ordering() {
138        // The value 0o_410763_762761 is taken from Plugboard B,
139        // address 0o3777741.  This is what actually gets loaded into
140        // these system configuration slots by the boot code.
141        let word = u36!(0o_410763_762761);
142        let ctx = make_ctx();
143        let (values, e) = simulate_spg(&ctx, 4, word);
144
145        // For a word DDD_CCC_BBB_AAA loaded with ⁿSPG,
146        // F-memory location n should be loaded with AAA.
147        assert_eq!(values[0], cfg_val(0o761));
148        // F-memory location n+1 should be loaded with BBB.
149        assert_eq!(values[1], cfg_val(0o762));
150        // F-memory location n+2 should be loaded with CCC.
151        assert_eq!(values[2], cfg_val(0o763));
152        // F-memory location n+3 should be loaded with DDD.
153        assert_eq!(values[3], cfg_val(0o410));
154
155        assert_eq!(e, word, "E register was unaffected");
156    }
157
158    /// Verify that the SPG instruction will not modify system
159    /// configuration value 0.  I don't recall any mention in the
160    /// documentation of any alarm when an attempt is made to set slot
161    /// 0, so I assume this is just ignored.
162    #[test]
163    fn op_spg_zero_invariant() {
164        // The value 0o_410763_762761 happens to be taken from
165        // Plugboard B, address 0o3777741.  But the key point is that
166        // none of the quarters of the word are zero.
167        let word = u36!(0o_410763_762761);
168        let context = make_ctx();
169        let (values, e) = simulate_spg(&context, 0, word);
170
171        // For a word DDD_CCC_BBB_AAA loaded with ⁿSPG,
172        // F-memory location 0 should be unchanged
173        assert_eq!(values[0], cfg_val(0o0));
174        // F-memory location 1 should be loaded with BBB.
175        assert_eq!(values[1], cfg_val(0o762));
176        // F-memory location 2 should be loaded with CCC.
177        assert_eq!(values[2], cfg_val(0o763));
178        // F-memory location 3 should be loaded with DDD.
179        assert_eq!(values[3], cfg_val(0o410));
180
181        // SPG should set the E register.
182        assert_eq!(e, word, "E register was unaffected");
183    }
184}