cpu/control/
op_loadstore.rs

1//! Implementations of "Load-Store Class" opcodes
2//! - LDA: [`ControlUnit::op_lda`]
3//! - LDB: [`ControlUnit::op_ldb`]
4//! - LDC: [`ControlUnit::op_ldc`]
5//! - LDD: [`ControlUnit::op_ldd`]
6//! - LDE: [`ControlUnit::op_lde`]
7//! - STA: [`ControlUnit::op_sta`]
8//! - STB: [`ControlUnit::op_stb`]
9//! - STC: [`ControlUnit::op_stc`]
10//! - STD: [`ControlUnit::op_std`]
11//! - STE: [`ControlUnit::op_ste`]
12//! - EXA: (unimplemented)
13
14use tracing::{Level, event};
15
16use super::alarm::Alarm;
17use super::context::Context;
18use super::control::{ControlUnit, MemoryUnit, OpcodeResult, UpdateE};
19use super::exchanger::exchanged_value_for_load;
20use base::prelude::*;
21
22impl ControlUnit {
23    /// Implements the LDA instruction (Opcode 024, User Handbook,
24    /// page 3-6).
25    pub(crate) fn op_lda(
26        &mut self,
27        ctx: &Context,
28        mem: &mut MemoryUnit,
29    ) -> Result<OpcodeResult, Alarm> {
30        let new_value = self.op_load_value(ctx, &mem.get_a_register(), mem, &UpdateE::Yes)?;
31        mem.set_a_register(new_value);
32        Ok(OpcodeResult::default())
33    }
34
35    /// Implements the LDB instruction (Opcode 025, User Handbook,
36    /// page 3-6).
37    pub(crate) fn op_ldb(
38        &mut self,
39        ctx: &Context,
40        mem: &mut MemoryUnit,
41    ) -> Result<OpcodeResult, Alarm> {
42        let new_value = self.op_load_value(ctx, &mem.get_b_register(), mem, &UpdateE::Yes)?;
43        mem.set_b_register(new_value);
44        Ok(OpcodeResult::default())
45    }
46
47    /// Implements the LDC instruction (Opcode 026, User Handbook,
48    /// page 3-6).
49    pub(crate) fn op_ldc(
50        &mut self,
51        ctx: &Context,
52        mem: &mut MemoryUnit,
53    ) -> Result<OpcodeResult, Alarm> {
54        let new_value = self.op_load_value(ctx, &mem.get_c_register(), mem, &UpdateE::Yes)?;
55        mem.set_c_register(new_value);
56        Ok(OpcodeResult::default())
57    }
58
59    /// Implements the LDD instruction (Opcode 027, User Handbook,
60    /// page 3-6).
61    pub(crate) fn op_ldd(
62        &mut self,
63        ctx: &Context,
64        mem: &mut MemoryUnit,
65    ) -> Result<OpcodeResult, Alarm> {
66        let new_value = self.op_load_value(ctx, &mem.get_d_register(), mem, &UpdateE::Yes)?;
67        mem.set_d_register(new_value);
68        Ok(OpcodeResult::default())
69    }
70
71    /// Implements the LDE instruction (Opcode 020, User Handbook,
72    /// page 3-6).
73    pub(crate) fn op_lde(
74        &mut self,
75        ctx: &Context,
76        mem: &mut MemoryUnit,
77    ) -> Result<OpcodeResult, Alarm> {
78        // LDE is a special case in that the the other instructions
79        // jam the loaded memory word into E (see example 2 for LDA)
80        // but LDE doesn't do this (you get the exchanged result in E
81        // instead).  This is why we use UpdateE::No.
82        let old_value = mem.get_e_register();
83        let new_value = self.op_load_value(ctx, &old_value, mem, &UpdateE::No)?;
84        mem.set_e_register(new_value);
85        Ok(OpcodeResult::default())
86    }
87
88    /// Implements the STA instruction (Opcode 034, User Handbook,
89    /// page 3-8).
90    pub(crate) fn op_sta(
91        &mut self,
92        ctx: &Context,
93        mem: &mut MemoryUnit,
94    ) -> Result<OpcodeResult, Alarm> {
95        self.op_store_ae_register(ctx, mem.get_a_register(), mem, &UpdateE::Yes)
96    }
97
98    /// Implements the STB instruction (Opcode 035, User Handbook,
99    /// page 3-8).
100    pub(crate) fn op_stb(
101        &mut self,
102        ctx: &Context,
103        mem: &mut MemoryUnit,
104    ) -> Result<OpcodeResult, Alarm> {
105        self.op_store_ae_register(ctx, mem.get_b_register(), mem, &UpdateE::Yes)
106    }
107
108    /// Implements the STC instruction (Opcode 036, User Handbook,
109    /// page 3-8).
110    pub(crate) fn op_stc(
111        &mut self,
112        ctx: &Context,
113        mem: &mut MemoryUnit,
114    ) -> Result<OpcodeResult, Alarm> {
115        self.op_store_ae_register(ctx, mem.get_c_register(), mem, &UpdateE::Yes)
116    }
117
118    /// Implements the STD instruction (Opcode 036, User Handbook,
119    /// page 3-8).
120    pub(crate) fn op_std(
121        &mut self,
122        ctx: &Context,
123        mem: &mut MemoryUnit,
124    ) -> Result<OpcodeResult, Alarm> {
125        self.op_store_ae_register(ctx, mem.get_d_register(), mem, &UpdateE::Yes)
126    }
127
128    /// Implements the STE instruction (Opcode 030, User Handbook,
129    /// page 3-8).
130    pub(crate) fn op_ste(
131        &mut self,
132        ctx: &Context,
133        mem: &mut MemoryUnit,
134    ) -> Result<OpcodeResult, Alarm> {
135        // STE is a special case in that it does not itself modify the
136        // E register.  See paragraph 1 on page 3-8 of the Users
137        // Handbook.
138        self.op_store_ae_register(ctx, mem.get_e_register(), mem, &UpdateE::No)
139    }
140
141    /// Implement opcodes ST{A,B,C,D,E}.
142    fn op_store_ae_register(
143        &mut self,
144        ctx: &Context,
145        register_value: Unsigned36Bit,
146        mem: &mut MemoryUnit,
147        update_e: &UpdateE,
148    ) -> Result<OpcodeResult, Alarm> {
149        let target: Address = self.operand_address_with_optional_defer_and_index(ctx, mem)?;
150        event!(
151            Level::TRACE,
152            "storing register value {register_value:o} at {target:o}"
153        );
154        self.memory_read_and_update_with_exchange(ctx, mem, &target, update_e, |_| register_value)
155            .map(|()| OpcodeResult::default())
156    }
157
158    /// Implement loads as if for opcodes LD{A,B,C,D,E}.
159    fn op_load_value(
160        &mut self,
161        ctx: &Context,
162        existing: &Unsigned36Bit,
163        mem: &mut MemoryUnit,
164        update_e: &UpdateE,
165    ) -> Result<Unsigned36Bit, Alarm> {
166        let target: Address = self.operand_address_with_optional_defer_and_index(ctx, mem)?;
167        let (memword, _extra) =
168            self.fetch_operand_from_address_without_exchange(ctx, mem, &target, update_e)?;
169        let exchanged = exchanged_value_for_load(&self.get_config(), &memword, existing);
170        Ok(exchanged)
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::super::super::context::Context;
177    use super::super::super::control::ConfigurationMemorySetup;
178    use super::super::super::exchanger::SystemConfiguration;
179    use super::super::super::memory::{MemoryMapped, MetaBitChange};
180    use super::super::super::{MemoryConfiguration, MemoryUnit};
181    use super::super::{ControlUnit, PanicOnUnmaskedAlarm, UpdateE};
182    use base::prelude::*;
183    use base::subword::right_half;
184    use core::time::Duration;
185
186    #[derive(Debug)]
187    enum ArithmeticUnitRegister {
188        // A, B, C, D are physically in the Arithmetic Element
189        A,
190        B,
191        C,
192        D,
193        // E is actually physically in the Exchange Element, but we
194        // use the same test infrastructure for LDE anyway.
195        E,
196    }
197
198    fn make_ctx() -> Context {
199        Context {
200            simulated_time: Duration::new(42, 42),
201            real_elapsed_time: Duration::new(7, 12),
202        }
203    }
204
205    fn get_register_value(mem: &MemoryUnit, which: ArithmeticUnitRegister) -> Unsigned36Bit {
206        use ArithmeticUnitRegister::*;
207        match which {
208            A => mem.get_a_register(),
209            B => mem.get_b_register(),
210            C => mem.get_c_register(),
211            D => mem.get_d_register(),
212            E => mem.get_e_register(),
213        }
214    }
215
216    fn set_up_load(
217        ctx: &Context,
218        a: Unsigned36Bit,
219        b: Unsigned36Bit,
220        c: Unsigned36Bit,
221        d: Unsigned36Bit,
222        e: Unsigned36Bit,
223    ) -> (ControlUnit, MemoryUnit) {
224        let control = ControlUnit::new(
225            PanicOnUnmaskedAlarm::Yes,
226            ConfigurationMemorySetup::StandardForTestingOnly,
227        );
228        let mut mem = MemoryUnit::new(
229            ctx,
230            &MemoryConfiguration {
231                with_u_memory: false,
232            },
233        );
234        mem.set_a_register(a);
235        mem.set_b_register(b);
236        mem.set_c_register(c);
237        mem.set_d_register(d);
238        mem.set_e_register(e);
239        (control, mem)
240    }
241
242    /// Simulate a load instruction; return (target register, e register)
243    #[allow(clippy::too_many_arguments)]
244    fn simulate_load(
245        ctx: &Context,
246        final_operand_address: Unsigned18Bit,
247        target_register: ArithmeticUnitRegister,
248        configuration: SystemConfiguration,
249        mem_word: Option<Unsigned36Bit>,
250        a: Unsigned36Bit,
251        b: Unsigned36Bit,
252        c: Unsigned36Bit,
253        d: Unsigned36Bit,
254        e: Unsigned36Bit,
255        j: Unsigned6Bit,
256        xj: Signed18Bit,
257        defer_index: Option<Signed18Bit>,
258    ) -> (Unsigned36Bit, Unsigned36Bit) {
259        let (mut control, mut mem) = set_up_load(ctx, a, b, c, d, e);
260        if let Some(w) = mem_word {
261            // Store the value to be loaded at final_operand_address.
262            control
263                .memory_store_without_exchange(
264                    ctx,
265                    &mut mem,
266                    &Address::from(final_operand_address),
267                    &w,
268                    &UpdateE::No,
269                    &MetaBitChange::None,
270                )
271                .expect("simulate_load should be able to set up the final operand");
272        }
273
274        // For deferred loads, store the defer value at 0o200.  Where
275        // the deferred load is indexed, compute the base address in
276        // R(M) so that the final result is `final_operand_address`.
277        //
278        //               Address | L              | R
279        // --------------------- | -              | -
280        // final_operand_address | L(source)      | R(source)
281        //                   200 | defer_index    | base
282        //
283        // base + index + Xⱼ = final_operand_address.
284        // The caller specifies Xⱼ and defer_index.
285        // So we set base to (final_operand_address - defer_index - Xⱼ).
286        let base: Signed18Bit = final_operand_address
287            .reinterpret_as_signed()
288            .checked_sub(defer_index.unwrap_or(Signed18Bit::ZERO))
289            .and_then(|x| x.checked_sub(xj))
290            .expect("test data caused arithmetic overflow");
291        let defer: Unsigned36Bit = join_halves(
292            defer_index.unwrap_or_default().reinterpret_as_unsigned(),
293            base.reinterpret_as_unsigned(),
294        );
295        control
296            .memory_store_without_exchange(
297                ctx,
298                &mut mem,
299                &Address::from(u18!(0o200)),
300                &defer,
301                &UpdateE::No,
302                &MetaBitChange::None,
303            )
304            .expect("simulate_load should be able to write to address 0o100");
305
306        control.regs.f_memory[1] = configuration;
307
308        let opcode = match target_register {
309            ArithmeticUnitRegister::A => Opcode::Lda,
310            ArithmeticUnitRegister::B => Opcode::Ldb,
311            ArithmeticUnitRegister::C => Opcode::Ldc,
312            ArithmeticUnitRegister::D => Opcode::Ldd,
313            ArithmeticUnitRegister::E => Opcode::Lde,
314        };
315        let inst = SymbolicInstruction {
316            held: false,
317            configuration: Unsigned5Bit::ONE,
318            opcode,
319            index: j,
320            operand_address: if defer_index.is_some() {
321                OperandAddress::deferred(Address::from(u18!(0o200)))
322            } else {
323                OperandAddress::direct(Address::from(final_operand_address))
324            },
325        };
326        control
327            .update_n_register(Instruction::from(&inst).bits())
328            .expect("should be able to set N register");
329
330        let result = match target_register {
331            ArithmeticUnitRegister::A => control.op_lda(ctx, &mut mem),
332            ArithmeticUnitRegister::B => control.op_ldb(ctx, &mut mem),
333            ArithmeticUnitRegister::C => control.op_ldc(ctx, &mut mem),
334            ArithmeticUnitRegister::D => control.op_ldd(ctx, &mut mem),
335            ArithmeticUnitRegister::E => control.op_lde(ctx, &mut mem),
336        };
337        if let Err(e) = result {
338            panic!("{opcode:?} instruction failed: {e}");
339        }
340        (
341            get_register_value(&mem, target_register),
342            mem.get_e_register(),
343        )
344    }
345
346    fn set_up_store<F>(
347        ctx: &Context,
348        mem_word: Unsigned36Bit,
349        working_address: &Address,
350        mut mem_init: F,
351    ) -> (ControlUnit, MemoryUnit)
352    where
353        F: FnMut(&mut MemoryUnit),
354    {
355        const COMPLAIN: &str = "failed to set up load/store test data";
356        let mut control = ControlUnit::new(
357            PanicOnUnmaskedAlarm::Yes,
358            ConfigurationMemorySetup::StandardForTestingOnly,
359        );
360        let mut mem = MemoryUnit::new(
361            ctx,
362            &MemoryConfiguration {
363                with_u_memory: false,
364            },
365        );
366        control
367            .memory_store_without_exchange(
368                ctx,
369                &mut mem,
370                working_address,
371                &mem_word,
372                &UpdateE::No,
373                &MetaBitChange::None,
374            )
375            .expect(COMPLAIN);
376        mem_init(&mut mem);
377        (control, mem)
378    }
379
380    #[allow(clippy::too_many_arguments)]
381    fn simulate_store(
382        ctx: &Context,
383        control: &mut ControlUnit,
384        mem: &mut MemoryUnit,
385        working_address: &Address,
386        j: Unsigned6Bit,
387        opcode: Opcode,
388        defer: bool,
389        configuration: SystemConfiguration,
390    ) -> (Unsigned36Bit, Unsigned36Bit) {
391        let complain = format!("failed to execute store instruction {opcode:?}");
392        control.regs.f_memory[1] = configuration;
393        let inst = SymbolicInstruction {
394            held: false,
395            configuration: Unsigned5Bit::ONE,
396            opcode,
397            index: j,
398            operand_address: if defer {
399                todo!("defer is not yet implemented");
400            } else {
401                OperandAddress::direct(*working_address)
402            },
403        };
404        control
405            .update_n_register(Instruction::from(&inst).bits())
406            .expect(&complain);
407        let f = match opcode {
408            Opcode::Ste => ControlUnit::op_ste,
409            _ => {
410                panic!("opcode {opcode:?} is not yet supported");
411            }
412        };
413        if let Err(e) = f(control, ctx, mem) {
414            panic!("{opcode:?} instruction failed: {e}");
415        }
416        match mem.fetch(ctx, working_address, &MetaBitChange::None) {
417            Ok((stored, _)) => (stored, mem.get_e_register()),
418            Err(e) => {
419                panic!("unable to retrieve the stored word: {e}");
420            }
421        }
422    }
423
424    /// This test is taken from example 1 on page 3-6 of the Users Handbook.
425    #[test]
426    fn test_lde_example_1() {
427        let context = make_ctx();
428        let input = u36!(0o444_333_222_111);
429        let (output, e) = simulate_load(
430            &context,
431            u18!(0o10000),
432            ArithmeticUnitRegister::E,
433            SystemConfiguration::zero(),
434            Some(input),
435            u36!(1),                           // a
436            u36!(2),                           // b
437            u36!(3),                           // c
438            u36!(4),                           // d
439            u36!(5),                           // e
440            u6!(1),                            // j
441            u18!(0o4).reinterpret_as_signed(), // Xⱼ
442            None,                              // No defer_index
443        );
444        assert_eq!(output, input);
445        assert_eq!(e, input);
446    }
447
448    /// This test is taken from example 2 on page 3-6 of the Users
449    /// Handbook, using register E.
450    #[test]
451    fn test_lde_example_2() {
452        let context = make_ctx();
453        let input = u36!(0o404_303_202_101);
454        let orig_e = u36!(0o545_535_525_515);
455        let (new_e, e) = simulate_load(
456            &context,
457            u18!(0o10000),
458            ArithmeticUnitRegister::E,
459            SystemConfiguration::from(u9!(0o340)), // 340 is standard configuration 1
460            Some(input),
461            u36!(0o141_131_121_111),           // a
462            u36!(0o242_232_222_212),           // b
463            u36!(0o343_333_323_313),           // c
464            u36!(0o444_434_424_414),           // d
465            orig_e,                            // e
466            u6!(1),                            // j
467            u18!(0o4).reinterpret_as_signed(), // Xⱼ
468            None,                              // No defer_index
469        );
470        // L(E) is unchanged, R(E) is loaded from the memory word.
471        assert_eq!(new_e, u36!(0o545_535_202_101));
472        assert_eq!(e, new_e);
473    }
474
475    /// This test is taken from example 2 on page 3-6 of the Users
476    /// Handbook, using register A.
477    #[test]
478    fn test_lda_example_2() {
479        let context = make_ctx();
480        let input = u36!(0o404_303_202_101);
481        let orig_e = u36!(0o545_535_525_515);
482        let (new_a, e) = simulate_load(
483            &context,
484            u18!(0o10000),
485            ArithmeticUnitRegister::A,
486            SystemConfiguration::from(u9!(0o340)), // 340 is standard configuration 1
487            Some(input),
488            u36!(0o141_131_121_111),           // a
489            u36!(0o242_232_222_212),           // b
490            u36!(0o343_333_323_313),           // c
491            u36!(0o444_434_424_414),           // d
492            orig_e,                            // e
493            u6!(1),                            // j
494            u18!(0o4).reinterpret_as_signed(), // Xⱼ
495            None,                              // No defer_index
496        );
497        // L(A) is unchanged, R(A) is loaded from R(memory word).
498        // E is set to the value of the memory word.
499        assert_eq!(new_a, u36!(0o141_131_202_101));
500        assert_eq!(e, input);
501    }
502
503    /// This test is taken from example 3 on page 3-6 of the Users
504    /// Handbook, using register C.
505    #[test]
506    fn test_ldc_example_3() {
507        let context = make_ctx();
508        // Notice that R(input) has the MSB set, i.e. it is negative.
509        let input = u36!(0o404_303_402_101);
510        let orig_e = u36!(0o545_535_525_515);
511        let (new_c, e) = simulate_load(
512            &context,
513            u18!(0o10000),
514            ArithmeticUnitRegister::C,
515            SystemConfiguration::from(u9!(0o140)), // 140 is standard configuration 11
516            Some(input),
517            u36!(0o141_131_121_111),           // a
518            u36!(0o242_232_222_212),           // b
519            u36!(0o343_333_323_313),           // c
520            u36!(0o444_434_424_414),           // d
521            orig_e,                            // e
522            u6!(1),                            // j
523            u18!(0o4).reinterpret_as_signed(), // Xⱼ
524            None,                              // No defer_index
525        );
526        // R(mem_word) ==> R(C)
527        // sign_bit(R(mem_word)) ==> L(C)
528        // mem_word ==> E
529        assert_eq!(new_c, u36!(0o777_777_402_101));
530        assert_eq!(e, input);
531    }
532
533    /// This test is taken from example 4 on page 3-6 of the Users
534    /// Handbook, using register D.
535    #[test]
536    fn test_ldd_example_4() {
537        let context = make_ctx();
538        let input = u36!(0o404_303_202_101);
539        let orig_e = u36!(0o545_535_525_515);
540        let orig_d = u36!(0o444_434_424_414);
541        let (new_d, new_e) = simulate_load(
542            &context,
543            u18!(0o10000),
544            ArithmeticUnitRegister::D,
545            SystemConfiguration::from(u9!(0o342)), // 342 is standard configuration 2
546            Some(input),
547            u36!(0o141_131_121_111),           // a
548            u36!(0o242_232_222_212),           // b
549            u36!(0o343_333_323_313),           // c
550            orig_d,                            // d
551            orig_e,                            // e
552            u6!(1),                            // j
553            u18!(0o4).reinterpret_as_signed(), // Xⱼ
554            None,                              // No defer_index
555        );
556        assert_eq!(new_d, u36!(0o444_434_404_303)); // D is set to L(orig_d)|L(input).
557        assert_eq!(new_e, input); // E is set to input
558    }
559
560    /// This test is taken from example 5 on page 3-7 of the Users
561    /// Handbook, using register B.
562    ///
563    /// The instruction is ²LDB₀ B, where B is the memory address of
564    /// the B register itself.
565    #[test]
566    fn test_ldb_example_5() {
567        let context = make_ctx();
568        // The input is actually register B.
569        let b_register_address: Unsigned18Bit = u18!(0o0377605);
570        let orig_b = u36!(0o242_232_222_212);
571        let (new_b, new_e) = simulate_load(
572            &context,
573            b_register_address,
574            ArithmeticUnitRegister::B,
575            SystemConfiguration::from(u9!(0o342)), // 342 is standard configuration 2
576            None,
577            u36!(0o141_131_121_111),           // a
578            orig_b,                            // b
579            u36!(0o343_333_323_313),           // c
580            u36!(0o444_434_424_414),           // d
581            u36!(0o545_535_525_515),           // e
582            u6!(1),                            // j
583            u18!(0o4).reinterpret_as_signed(), // Xⱼ
584            None,                              // No defer_index
585        );
586        // Register B ends up as L(orig_b)|R(orig_b).
587        assert_eq!(new_b, u36!(0o242_232_242_232));
588        assert_eq!(new_e, orig_b); // E is set to input
589    }
590
591    /// This test is taken from example 6 on page 3-7 of the Users
592    /// Handbook, using register D.
593    #[test]
594    fn test_ldd_example_6() {
595        let context = make_ctx();
596        // Note that the sign bit of Q4 of the input is set.
597        let input = u36!(0o404_303_202_101);
598        let orig_e = u36!(0o545_535_525_515);
599        let orig_d = u36!(0o444_434_424_414);
600        let (new_d, new_e) = simulate_load(
601            &context,
602            u18!(0o10000),
603            ArithmeticUnitRegister::D,
604            SystemConfiguration::from(u9!(0o163)), // 163 is standard configuration 16
605            Some(input),
606            u36!(0o141_131_121_111),           // a
607            u36!(0o242_232_222_212),           // b
608            u36!(0o343_333_323_313),           // c
609            orig_d,                            // d
610            orig_e,                            // e
611            u6!(1),                            // j
612            u18!(0o4).reinterpret_as_signed(), // Xⱼ
613            None,                              // No defer_index
614        );
615        // The result in D is that Q1 is set to Q4(input) and the
616        // remaining quarters are filled with sign-extension from
617        // Q4(input) (this sign bit is 1).
618        assert_eq!(new_d, u36!(0o777_777_777_404), "new value for D is wrong");
619        assert_eq!(new_e, input, "new value for E is wrong"); // E is set to input
620    }
621
622    #[test]
623    fn test_ste_example_1() {
624        let context = make_ctx();
625        let input = u36!(0o004_003_002_001);
626        let working_address: Address = Address::from(u18!(0o100));
627        let (mut control, mut mem) =
628            set_up_store(&context, u36!(0o444_333_222_111), &working_address, |mem| {
629                mem.set_e_register(input);
630            });
631        // The instruction actually uses System Configuration number
632        // 1, but we put Unsigned9Bit::ZERO in register F₁.
633        let (result, _e) = simulate_store(
634            &context,
635            &mut control,
636            &mut mem,
637            &working_address,
638            Unsigned6Bit::ZERO, // no indexing
639            Opcode::Ste,
640            false,
641            SystemConfiguration::from(Unsigned9Bit::ZERO),
642        );
643        assert_eq!(input, result);
644    }
645
646    /// This test resembles example 3 on page 3-8 of the Users
647    /// Handbook.
648    #[test]
649    fn test_ste_example_3() {
650        let context = make_ctx();
651        let initial_value_at_100 = u36!(0o004_003_002_001);
652        let initial_e = u36!(0o444_333_222_111);
653        let working_address: Address = Address::from(u18!(0o100));
654        let (mut control, mut mem) =
655            set_up_store(&context, initial_value_at_100, &working_address, |mem| {
656                mem.set_e_register(initial_e);
657            });
658
659        // The instruction actually uses System Configuration number
660        // 1, but we put 0342 (which is the value normally in F₂) in
661        // register F₁.  This gives the effect of `²STE₀ 100`.
662        //
663        // This configuration loads R(E) into L(100).  R(100) is unchanged.
664        let (result, e) = simulate_store(
665            &context,
666            &mut control,
667            &mut mem,
668            &working_address,
669            Unsigned6Bit::ZERO, // no indexing
670            Opcode::Ste,
671            false,
672            // This is configuration 2.
673            SystemConfiguration::from(u9!(0o342)),
674        );
675
676        // The E register should still hold the original value
677        // (0o004_003_002_001).
678        assert_eq!(e, initial_e, "E register should be unchanged");
679        // R(E) (which is 0o222_1111) should have been loaded into
680        // L(100) and R(100) (which is 0o002_001) should be unchanged.
681        let expected = u36!(0o222_111_002_001);
682        let expected2 = join_halves(right_half(initial_e), right_half(initial_value_at_100));
683        assert_eq!(expected, expected2, "test is internally inconsistent");
684        assert_eq!(result, expected, "incorrect value stored at 0o100");
685    }
686}