cpu/control/
op_index.rs

1//! Implementations of "Index Register Class" opcodes
2//! - RSX
3//! - DPX
4//! - EXX
5//! - AUX
6//! - ADX
7//! - SKX
8//! - JPX
9//! - JNX
10
11use tracing::{Level, event};
12
13use base::prelude::*;
14use base::subword;
15
16use super::alarm::{Alarm, AlarmDetails, Alarmer, BadMemOp};
17use super::context::Context;
18use super::control::{
19    ControlUnit, OpcodeResult, ProgramCounterChange, UpdateE, sign_extend_index_value,
20};
21use super::memory::{MemoryUnit, MetaBitChange};
22
23impl ControlUnit {
24    /// Implements the `DPX` instruction (Opcode 016, User Handbook,
25    /// page 3-16).
26    pub(crate) fn op_dpx(
27        &mut self,
28        ctx: &Context,
29        mem: &mut MemoryUnit,
30    ) -> Result<OpcodeResult, Alarm> {
31        let j = self.regs.n.index_address();
32        let xj: Unsigned36Bit = sign_extend_index_value(&self.regs.get_index_register(j));
33        let target: Address = self.operand_address_with_optional_defer_and_index(ctx, mem)?;
34
35        // DPX is trying to perform a write.  But to do this in some
36        // subword configurations, we need to read the existing value.
37        match self.fetch_operand_from_address_without_exchange(ctx, mem, &target, &UpdateE::No) {
38            Ok((dest, _meta)) => self
39                .memory_store_with_exchange(
40                    ctx,
41                    mem,
42                    &target,
43                    &xj,
44                    &dest,
45                    &UpdateE::Yes,
46                    &MetaBitChange::None,
47                )
48                .map(|()| OpcodeResult::default()),
49            Err(Alarm {
50                sequence: _,
51                details: AlarmDetails::QSAL(inst, BadMemOp::Read(addr), msg),
52            }) => {
53                // That read operation just failed.  So we handle this
54                // as a _write_ failure, meaning that we change
55                // BadMemOp::Read to BadMemOp::Write.
56                self.alarm_unit.fire_if_not_masked(
57                    Alarm {
58                        sequence: self.regs.k,
59                        details: AlarmDetails::QSAL(inst, BadMemOp::Write(addr), msg),
60                    },
61                    &self.regs.diagnostic_only,
62                )?;
63                Ok(OpcodeResult::default()) // QSAL is masked, we just carry on (the DPX instruction has no effect).
64            }
65            Err(other) => Err(other),
66        }
67    }
68
69    /// Implements the AUX instruction (Opcode 010, User Handbook,
70    /// page 3-22).
71    pub(crate) fn op_aux(
72        &mut self,
73        ctx: &Context,
74        mem: &mut MemoryUnit,
75    ) -> Result<OpcodeResult, Alarm> {
76        let j = self.regs.n.index_address();
77        // The AUX instruction is not indexed.
78        let source: Address = self.operand_address_with_optional_defer_without_index(ctx, mem)?;
79        // If j == 0 nothing is going to happen (X₀ is always 0) but
80        // we still need to make sure we raise an alarm if the memory
81        // access is invalid.
82        let (word, _extra) = self.fetch_operand_from_address_with_exchange(
83            ctx,
84            mem,
85            &source,
86            &Unsigned36Bit::ZERO,
87            &UpdateE::Yes,
88        )?;
89        if !j.is_zero() {
90            let m: Unsigned18Bit = subword::right_half(word);
91            let xj: Signed18Bit = self.regs.get_index_register(j);
92            let newvalue = xj.wrapping_add(m.reinterpret_as_signed());
93            event!(
94                Level::TRACE,
95                "added current operand {m:o} to current value {xj:o} yielding value {newvalue:o} for X{j:o}",
96            );
97            self.regs.set_index_register(j, &newvalue);
98        }
99        Ok(OpcodeResult::default())
100    }
101
102    /// Implements the RSX instruction (Opcode 011, User Handbook,
103    /// page 3-14).
104    pub(crate) fn op_rsx(
105        &mut self,
106        ctx: &Context,
107        mem: &mut MemoryUnit,
108    ) -> Result<OpcodeResult, Alarm> {
109        let j = self.regs.n.index_address();
110        let existing = join_halves(
111            Unsigned18Bit::ZERO,
112            self.regs.get_index_register(j).reinterpret_as_unsigned(),
113        );
114        // If j == 0 nothing is going to happen (X₀ is always 0) but
115        // we still need to make sure we raise an alarm if the memory
116        // access is invalid.  Se we need to perform the memory fetch
117        // even when j=0.
118        //
119        // As shown in the RSX opcode documentation (Users Handbook,
120        // page 3-14), the index bits in the instruction identify
121        // which index register we're modifying, so cannot be used for
122        // indexing.
123        let source: Address = self.operand_address_with_optional_defer_without_index(ctx, mem)?;
124        let (word, _extra) = self.fetch_operand_from_address_with_exchange(
125            ctx,
126            mem,
127            &source,
128            &existing,
129            &UpdateE::Yes,
130        )?;
131        if !j.is_zero() {
132            let xj: Signed18Bit = subword::right_half(word).reinterpret_as_signed();
133            self.regs.set_index_register(j, &xj);
134        }
135        Ok(OpcodeResult::default())
136    }
137
138    /// Implements the SKX instruction (Opcode 012, User Handbook,
139    /// page 3-24).
140    pub(crate) fn op_skx(&mut self, _ctx: &Context) -> Result<OpcodeResult, Alarm> {
141        let inst = &self.regs.n;
142        let j = inst.index_address();
143        // SKX does not cause an access to STUV memory; instead the
144        // operand is the full value of the operand and defer fields
145        // of the instruction.  This allows us to use SKX to mark a
146        // placeholder for use with TRAP 42.
147        let operand = inst.operand_address_and_defer_bit();
148        let config = u8::from(inst.configuration());
149        // NOTE: when we come to implement other configuration values,
150        // we may need to pay closer attention to the ordering of flag
151        // changes, since the TX-2 performed flag lowering and raising
152        // in that order.  For example (text is from section 7-8.3 of
153        // the TX-2 Technical Manual, Volume 1),
154        //
155        // if the CF4 bit is set in an SKX instruction, the flag of
156        // sequence J is raised (if such a flag exists).  Since flag
157        // raising occurs after a flag lowering caused by a dismiss,
158        // an SKX operaiton which dismisses and raises the flag of its
159        // own sequence will have no apparent effect on the flag.
160        //
161        match config {
162            0o0 | 0o10 => {
163                if j != 0 {
164                    // Xj is fixed at 0.
165                    self.regs
166                        .set_index_register(j, &operand.reinterpret_as_signed());
167                }
168                if config & 0o10 != 0 {
169                    self.regs.flags.raise(&j);
170                }
171                Ok(OpcodeResult::default())
172            }
173            // See comment above for config value 0o30 where the J
174            // bits indicate the current sequence (which the case
175            // where this instruction both lowers and raises the same
176            // flag in the same instruction).
177            0o1 => {
178                if j != 0 {
179                    // Xj is fixed at 0.
180                    // Calculate -T
181                    let t_negated = Signed18Bit::ZERO.wrapping_sub(operand.reinterpret_as_signed());
182                    self.regs.set_index_register(j, &t_negated);
183                }
184                Ok(OpcodeResult::default())
185            }
186            _ => Err(self.alarm_unit.always_fire(
187                Alarm {
188                    sequence: self.regs.k,
189                    details: AlarmDetails::ROUNDTUITAL {
190                        explanation: format!(
191                            "SKX configuration value {:#o} is not implemented yet",
192                            inst.configuration()
193                        ),
194                        bug_report_url: "https://github.com/TX-2/TX-2-simulator/issues/138",
195                    },
196                },
197                &self.regs.diagnostic_only,
198            )),
199        }
200    }
201
202    /// Implements the JPX (jump on positive index) opcode (06).
203    pub(crate) fn op_jpx(
204        &mut self,
205        ctx: &Context,
206        mem: &mut MemoryUnit,
207    ) -> Result<OpcodeResult, Alarm> {
208        let is_positive_address = |xj: &Signed18Bit| !xj.is_zero() && xj.is_positive();
209        self.impl_op_jpx_jnx(ctx, is_positive_address, mem)
210    }
211
212    /// Implements the JNX (jump on positive index) opcode (07).
213    pub(crate) fn op_jnx(
214        &mut self,
215        ctx: &Context,
216        mem: &mut MemoryUnit,
217    ) -> Result<OpcodeResult, Alarm> {
218        let is_negative_address = |xj: &Signed18Bit| xj.is_negative();
219        self.impl_op_jpx_jnx(ctx, is_negative_address, mem)
220    }
221
222    // Implements JPX (opcode 06) and JNX.  Note that these opcodes
223    // handle deferred addressing in a unique way.  This is described
224    // on page 5-9 of the User Handbook:
225    //
226    // EXCEPT when the operation is JNX or JPX (codes 6 and 7), for on
227    // these two operations the right half of N is used for the sign
228    // extended index increment (18 bits).  (BUT if the JNX or JPX is
229    // deferred, the increment is not addded until PK3 so N is not
230    // changed during PK1).
231    pub(crate) fn impl_op_jpx_jnx<F: FnOnce(&Signed18Bit) -> bool>(
232        &mut self,
233        ctx: &Context,
234        predicate: F,
235        mem: &mut MemoryUnit,
236    ) -> Result<OpcodeResult, Alarm> {
237        let j = self.regs.n.index_address();
238        let xj = self.regs.get_index_register(j);
239        let do_jump: bool = predicate(&xj);
240        event!(
241            Level::TRACE,
242            "Index register {:?} contains {:?}={} (decimal), we {} jump",
243            &j,
244            &xj,
245            i32::from(xj),
246            if do_jump { "will" } else { "won't" },
247        );
248
249        // Compute the jump target (which possibly involves indexing)
250        // before modifying Xj.  See note 3 on page 3-27 of the User
251        // Handbook, which says
252        //
253        // The address of a deferred JNX or JPX is completely
254        // determined before the index register is changed.  Therefore
255        // a ⁻¹JPXₐ|ₐ S would jump to Sₐ as defined by the original
256        // contents of Xₐ - if it jumps at all.
257        let target: Address = self.resolve_operand_address(ctx, mem, Some(Unsigned6Bit::ZERO))?;
258        let cf: Signed5Bit = self.regs.n.configuration().reinterpret_as_signed();
259        let new_xj: Signed18Bit = xj.wrapping_add(Signed18Bit::from(cf));
260        if !j.is_zero() {
261            event!(
262                Level::TRACE,
263                "Updating index register {:?} to {:?}",
264                &j,
265                &new_xj
266            );
267            self.regs.set_index_register(j, &new_xj);
268        }
269        if do_jump {
270            if !self.regs.n.is_held() {
271                self.dismiss("JPX/JNX; did jump, hold bit is clear");
272                if let Some(current_seq) = self.regs.k {
273                    self.regs.flags.lower(&current_seq);
274                    self.regs.current_sequence_is_runnable = false;
275                }
276            }
277            mem.set_e_register(subword::join_halves(
278                subword::left_half(mem.get_e_register()),
279                Unsigned18Bit::from(self.regs.p),
280            ));
281            Ok(OpcodeResult {
282                program_counter_change: Some(ProgramCounterChange::Jump(target)),
283                poll_order_change: None,
284                output: None,
285            })
286        } else {
287            Ok(OpcodeResult::default())
288        }
289    }
290}
291
292#[cfg(test)]
293mod tests {
294    use super::super::super::context::Context;
295    use super::super::super::control::{ConfigurationMemorySetup, PanicOnUnmaskedAlarm, UpdateE};
296    use super::super::super::exchanger::SystemConfiguration;
297    use super::super::super::memory::MetaBitChange;
298    use super::super::super::{MemoryConfiguration, MemoryUnit};
299    use base::instruction::{Opcode, SymbolicInstruction};
300    use base::prelude::*;
301    use core::time::Duration;
302
303    use super::ControlUnit;
304
305    fn make_ctx() -> Context {
306        Context {
307            simulated_time: Duration::new(42, 42),
308            real_elapsed_time: Duration::new(7, 12),
309        }
310    }
311
312    fn setup(
313        ctx: &Context,
314        j: Unsigned6Bit,
315        initial: Signed18Bit,
316        mem_setup: &[(Address, Unsigned36Bit)],
317        f_memory_setup: Option<&[(usize, SystemConfiguration)]>,
318    ) -> (ControlUnit, MemoryUnit) {
319        const COMPLAIN: &str = "failed to set up initial state";
320        let mut control = ControlUnit::new(
321            PanicOnUnmaskedAlarm::Yes,
322            ConfigurationMemorySetup::StandardForTestingOnly,
323        );
324        let mut mem = MemoryUnit::new(
325            ctx,
326            &MemoryConfiguration {
327                with_u_memory: false,
328            },
329        );
330        if j == 0 {
331            assert_eq!(initial, 0, "Cannot set X₀ to a nonzero value");
332        } else {
333            control.regs.set_index_register(j, &initial);
334        }
335        for (address, value) in mem_setup {
336            control
337                .memory_store_without_exchange(
338                    ctx,
339                    &mut mem,
340                    address,
341                    value,
342                    &UpdateE::No,
343                    &MetaBitChange::None,
344                )
345                .expect(COMPLAIN);
346        }
347        if let Some(f_mem_setup) = f_memory_setup {
348            for (config_num, config) in f_mem_setup {
349                control.regs.f_memory[*config_num] = *config;
350            }
351        }
352
353        (control, mem)
354    }
355
356    /// Simulate some AUX instructions and return the result
357    /// of the addition, and the final value in the E register.
358    ///
359    /// # Arguments
360    ///
361    /// * `j` - which index register to use
362    /// * `initial` - initial value of the X-register
363    /// * `addends` - items to add to `initial`
364    /// * `addend_lhs` - contents for the left subword of all added items
365    /// * `defer` - whether the operand should be deferred
366    /// * `cfg` - the system configuration to use when adding
367    fn simulate_aux(
368        ctx: &Context,
369        j: Unsigned6Bit,
370        initial: Signed18Bit,
371        addends: &[Unsigned36Bit],
372        defer: bool,
373        f_memory_setup: Option<&[(usize, SystemConfiguration)]>,
374        config_num: usize,
375    ) -> (Signed18Bit, Unsigned36Bit) {
376        const COMPLAIN: &str = "failed to set up AUX test data";
377
378        // Given...
379        const ADDEND_BASE: u32 = 0o101;
380        let mem_setup: Vec<(Address, Unsigned36Bit)> = addends
381            .iter()
382            .enumerate()
383            .map(|(offset, addend)| {
384                let addend_address = Address::from(
385                    u18!(ADDEND_BASE)
386                        .wrapping_add(Unsigned18Bit::try_from(offset).expect(COMPLAIN)),
387                );
388                (addend_address, *addend)
389            })
390            .collect();
391        let (mut control, mut mem) = setup(ctx, j, initial, &mem_setup, f_memory_setup);
392
393        // When... we perform a sequence of AUX instructions
394        let defer_address_bits = u18!(0o100);
395        for offset in 0..(addends.len()) {
396            let operand_address: OperandAddress = if defer {
397                let pos: u32 = ADDEND_BASE + u32::try_from(offset).expect(COMPLAIN);
398                let deferred = Unsigned18Bit::try_from(pos).expect(COMPLAIN);
399                let ignored_lhs = u18!(0o500);
400                // Set up the word at the deferred address
401                control
402                    .memory_store_without_exchange(
403                        ctx,
404                        &mut mem,
405                        &Address::from(u18!(0o100)),
406                        &join_halves(ignored_lhs, deferred),
407                        &UpdateE::No,
408                        &MetaBitChange::None,
409                    )
410                    .expect(COMPLAIN);
411                OperandAddress::deferred(Address::from(defer_address_bits))
412            } else {
413                OperandAddress::direct(Address::from(
414                    Unsigned18Bit::try_from(ADDEND_BASE + u32::try_from(offset).expect(COMPLAIN))
415                        .expect(COMPLAIN),
416                ))
417            };
418            let inst = SymbolicInstruction {
419                held: false,
420                configuration: Unsigned5Bit::try_from(config_num).expect(COMPLAIN),
421                opcode: Opcode::Aux,
422                index: j,
423                operand_address,
424            };
425            control
426                .update_n_register(Instruction::from(&inst).bits())
427                .expect(COMPLAIN);
428            if let Err(e) = control.op_aux(ctx, &mut mem) {
429                panic!("AUX instruction failed: {e}");
430            }
431        }
432        (control.regs.get_index_register(j), mem.get_e_register())
433    }
434
435    /// Check that AUX thinks that 0 + 1 = 1.
436    #[test]
437    fn op_aux_zero_plus_one_equals_one() {
438        let context = make_ctx();
439        let (sum, e) = simulate_aux(
440            &context,
441            Unsigned6Bit::ONE, // Use register X₁
442            Signed18Bit::ZERO,
443            &[Unsigned36Bit::ONE],
444            false,
445            Some(&[(1usize, SystemConfiguration::from(0_u8))]),
446            1,
447        );
448        assert_eq!(sum, Signed18Bit::ONE);
449        assert_eq!(e, Unsigned36Bit::ONE);
450    }
451
452    /// Check that AUX can correctly add negative values.
453    #[test]
454    fn op_aux_negative() {
455        const COMPLAIN: &str = "failed to set up AUX test data";
456        let context = make_ctx();
457        let minus_three = Signed36Bit::from(-3).reinterpret_as_unsigned();
458        let items_to_add = [Signed36Bit::from(-1).reinterpret_as_unsigned(), minus_three];
459        let (sum, e) = simulate_aux(
460            &context,
461            Unsigned6Bit::ONE,                                // Use register X₁
462            Signed18Bit::try_from(0o250077).expect(COMPLAIN), // initial value
463            &items_to_add,
464            false,
465            // System configuration 0o340 uses only the right-hand
466            // subword, which is how AUX behaves anyway - so this
467            // should make no difference.
468            Some(&[(1usize, SystemConfiguration::from(0o340_u8))]),
469            1usize,
470        );
471        assert_eq!(sum, Signed18Bit::try_from(0o250073).expect(COMPLAIN));
472        assert_eq!(e, minus_three);
473    }
474
475    /// This test is derived from example 1 on page 3-20 of the Users
476    /// Handbook.
477    #[test]
478    fn op_aux_example_1() {
479        let context = make_ctx();
480        let (sum, e) = simulate_aux(
481            &context,
482            Unsigned6Bit::ONE, // Use register X₁
483            u18!(0o000_111).reinterpret_as_signed(),
484            &[u36!(0o444_000_222_010)],
485            false,
486            Some(&[(1usize, SystemConfiguration::from(0_u8))]),
487            1,
488        );
489        assert_eq!(sum, u18!(0o222_121).reinterpret_as_signed());
490        assert_eq!(e, u36!(0o444_000_222_010));
491    }
492
493    /// This test is derived from example 2 on page 3-20 of the Users
494    /// Handbook.
495    #[test]
496    fn op_aux_example_2() {
497        let context = make_ctx();
498        let (sum, e) = simulate_aux(
499            &context,
500            Unsigned6Bit::ONE,               // Use register X₁
501            u18!(0).reinterpret_as_signed(), // initial
502            &[u36!(0o444_333_222_111)],
503            false,
504            Some(&[(2usize, SystemConfiguration::from(0o342_u8))]),
505            2,
506        );
507        assert_eq!(sum, u18!(0o444_333).reinterpret_as_signed());
508        assert_eq!(e, u36!(0o444_333_222_111));
509
510        let (sum, e) = simulate_aux(
511            &context,
512            Unsigned6Bit::ONE, // Use register X₁
513            u18!(0o010_111).reinterpret_as_signed(),
514            &[u36!(0o444_003_222_010)],
515            false,
516            Some(&[(2usize, SystemConfiguration::from(0o342_u8))]),
517            2,
518        );
519        assert_eq!(sum, u18!(0o454_114).reinterpret_as_signed());
520        assert_eq!(e, u36!(0o444_003_222_010));
521    }
522
523    /// This test is derived from example 3 on page 3-20 of the Users
524    /// Handbook.
525    #[test]
526    fn op_aux_example_3() {
527        let context = make_ctx();
528        let xj = Signed18Bit::from(3_i8);
529        // Q1(w) is 777, sign extended this is 777_776, which is -1.
530        // Adding that to X₁ which is 3, gives 2.
531        let w = u36!(0o444_333_000_776);
532        let (sum, e) = simulate_aux(
533            &context,
534            Unsigned6Bit::ONE, // Use register X₁
535            xj,
536            &[w],
537            false,
538            Some(&[(0o13usize, SystemConfiguration::from(0o160_u8))]),
539            0o13,
540        );
541        assert_eq!(sum, u18!(0o000_002).reinterpret_as_signed());
542        assert_eq!(e, u36!(0o444_333_000_776));
543    }
544
545    /// This test is derived from example 4 on page 3-20 of the Users
546    /// Handbook.
547    #[test]
548    fn op_aux_example_4() {
549        let context = make_ctx();
550        let xj = u18!(0o006_000).reinterpret_as_signed();
551        // Q2(w) is -2, sign extended though the value is 775_777,
552        // which is -2000 octal.
553        let w = u36!(0o044_333_775_000);
554        let (sum, e) = simulate_aux(
555            &context,
556            Unsigned6Bit::ONE, // Use register X₁
557            xj,
558            &[w],
559            false,
560            Some(&[(0o1usize, SystemConfiguration::from(0o350_u8))]),
561            1,
562        );
563        assert_eq!(
564            sum,
565            u18!(0o004_000).reinterpret_as_signed(),
566            "sum is incorrect"
567        );
568        assert_eq!(e, u36!(0o044_333_775_000), "Register E is incorrect");
569    }
570
571    /// This test is derived from example 5 on page 3-21 of the Users
572    /// Handbook.
573    #[test]
574    fn op_aux_example_5() {
575        let context = make_ctx();
576        let xj = u18!(0o654_321).reinterpret_as_signed(); // X₁
577        let w = u36!(0o040_030_020_010);
578        let (sum, e) = simulate_aux(
579            &context,
580            Unsigned6Bit::ONE, // Use register X₁
581            xj,                // value of X₁
582            &[w],
583            false,
584            // standard configuration 0o21 is 0o230.
585            Some(&[(0o21usize, SystemConfiguration::from(0o230_u8))]),
586            0o21,
587        );
588        // X₁ is unchanged
589        assert_eq!(sum, xj, "sum is incorrect");
590        assert_eq!(e, u36!(0o040_030_020_010), "Register E is incorrect");
591    }
592
593    /// This test is derived from example 6 on page 3-21 of the Users
594    /// Handbook.
595    #[test]
596    fn op_aux_example_6() {
597        let context = make_ctx();
598        let xj = u18!(0o222_111).reinterpret_as_signed(); // X₁
599        let w = u36!(0o242_232_000_212);
600        let (sum, e) = simulate_aux(
601            &context,
602            Unsigned6Bit::ONE, // Use register X₁
603            xj,                // value of X₁
604            &[w],
605            true, // deferred
606            // standard configuration 1 is 0o340.
607            Some(&[(0o1usize, SystemConfiguration::from(0o340_u8))]),
608            0o1,
609        );
610        assert_eq!(
611            sum,
612            u18!(0o222_323).reinterpret_as_signed(),
613            "sum is incorrect"
614        );
615        assert_eq!(e, w, "Register E is incorrect");
616    }
617
618    // TODO: add a test for AUX in which the operand is accessed via
619    // deferred addressing.
620
621    /// Check that AUX applies a configuration in which only q2 is
622    /// active.
623    #[test]
624    fn op_aux_q2_only() {
625        let context = make_ctx();
626        let (sum, _e) = simulate_aux(
627            &context,
628            Unsigned6Bit::ONE,                       // Use register X₁
629            u18!(0o300_555).reinterpret_as_signed(), // initial value
630            &[u36!(0o020_010)],
631            false,
632            Some(&[(1usize, SystemConfiguration::from(u9!(0o750)))]), // 0o750: q2 only
633            1usize,
634        );
635        // The sum should be formed from q2 of the initial value
636        // (0o300) plus q2 of the addend (0o020), yielding 0o320.
637        // q1 should be unchanged.
638        assert_eq!(sum, u18!(0o320_555).reinterpret_as_signed());
639    }
640
641    fn simulate_rsx(
642        ctx: &Context,
643        j: Unsigned6Bit,
644        initial: Signed18Bit,
645        mem_word: &Unsigned36Bit,
646        defer: bool,
647        f_memory_setup: Option<&[(usize, SystemConfiguration)]>,
648        config_num: usize,
649    ) -> (Signed18Bit, Unsigned36Bit) {
650        const COMPLAIN: &str = "failed to set up RSX test data";
651        let deferred = Unsigned18Bit::try_from(0o200).expect(COMPLAIN);
652        let mem_setup: Vec<(Address, Unsigned36Bit)> = vec![
653            (Address::from(u18!(0o100)), *mem_word),
654            (
655                Address::from(deferred),
656                join_halves(Unsigned18Bit::ZERO, u18!(0o100)),
657            ), // for deferred case
658        ];
659        let (mut control, mut mem) = setup(ctx, j, initial, &mem_setup, f_memory_setup);
660
661        let operand_address: OperandAddress = {
662            if defer {
663                OperandAddress::deferred(Address::from(u18!(0o200)))
664            } else {
665                OperandAddress::direct(Address::from(u18!(0o100)))
666            }
667        };
668
669        let inst = SymbolicInstruction {
670            held: false,
671            configuration: Unsigned5Bit::try_from(config_num).expect(COMPLAIN),
672            opcode: Opcode::Rsx,
673            index: j,
674            operand_address,
675        };
676        control
677            .update_n_register(Instruction::from(&inst).bits())
678            .expect(COMPLAIN);
679        if let Err(e) = control.op_rsx(ctx, &mut mem) {
680            panic!("RSX instruction failed: {e}");
681        }
682        (control.regs.get_index_register(j), mem.get_e_register())
683    }
684
685    /// Test case taken from example 1 on page 3-14 of the Users Handbook.
686    #[test]
687    fn op_rsx_example_1() {
688        const COMPLAIN: &str = "test data should be valid";
689        let context = make_ctx();
690        let w: Unsigned36Bit = u36!(0o444_333_222_111);
691        let (xj, e) = simulate_rsx(
692            &context,
693            Unsigned6Bit::ONE,
694            Signed18Bit::from(20_i8),
695            &w,
696            false,
697            Some(&[(1, SystemConfiguration::from(u9!(0o340)))]),
698            1,
699        );
700        assert_eq!(xj, Signed18Bit::try_from(0o222_111_i32).expect(COMPLAIN));
701        assert_eq!(e, w);
702    }
703
704    /// Test case taken from example 2 on page 3-14 of the Users Handbook.
705    #[test]
706    fn op_rsx_example_2() {
707        let context = make_ctx();
708        let w: Unsigned36Bit = u36!(0o444_333_222_111);
709        let (xj, e) = simulate_rsx(
710            &context,
711            Unsigned6Bit::ONE,
712            Signed18Bit::from(20_i8),
713            &w,
714            false,
715            Some(&[(2, SystemConfiguration::from(u9!(0o342)))]),
716            2,
717        );
718        assert_eq!(xj, u18!(0o444_333).reinterpret_as_signed());
719        assert_eq!(e, w);
720    }
721
722    /// Test case taken from example 3 on page 3-14 of the Users Handbook.
723    #[test]
724    fn op_rsx_example_3() {
725        let context = make_ctx();
726        let w: Unsigned36Bit = u36!(0o444_333_222_111);
727        let (xj, e) = simulate_rsx(
728            &context,
729            Unsigned6Bit::ONE,
730            u18!(0o505_404).reinterpret_as_signed(),
731            &w,
732            false,
733            Some(&[(3, SystemConfiguration::from(u9!(0o760)))]),
734            3,
735        );
736        assert_eq!(xj, u18!(0o505_111).reinterpret_as_signed());
737        assert_eq!(e, w);
738    }
739
740    /// Test case taken from example 4 on page 3-14 of the Users
741    /// Handbook, in this case with a quarter having the top bit set
742    #[test]
743    fn op_rsx_example_4_negative() {
744        let context = make_ctx();
745        // Because q1 has the top bit set, the `1` sign bit is
746        // extended through q2 of the destination index register.
747        let w: Unsigned36Bit = u36!(0o454_453_452_451);
748        let (xj, e) = simulate_rsx(
749            &context,
750            Unsigned6Bit::ONE,
751            u18!(0o202_101).reinterpret_as_signed(),
752            &w,
753            false,
754            Some(&[(13, SystemConfiguration::from(u9!(0o160)))]),
755            13,
756        );
757        assert_eq!(xj, u18!(0o777_451).reinterpret_as_signed());
758        assert_eq!(e, w);
759    }
760
761    /// Test case taken from example 4 on page 3-14 of the Users
762    /// Handbook, in this case with a quarter having the top bit clear
763    #[test]
764    fn op_rsx_example_4_positive() {
765        let context = make_ctx();
766        // Because q1 has the top bit unset, the `0` sign bit is
767        // extended through q2 of the destination index register.
768        let w: Unsigned36Bit = u36!(0o454_453_452_251);
769        let (xj, e) = simulate_rsx(
770            &context,
771            Unsigned6Bit::ONE,
772            u18!(0o402_101).reinterpret_as_signed(),
773            &w,
774            false,
775            Some(&[(13, SystemConfiguration::from(u9!(0o160)))]),
776            13,
777        );
778        assert_eq!(xj, u18!(0o000_251).reinterpret_as_signed());
779        assert_eq!(e, w);
780    }
781
782    /// Test case taken from example 5 on page 3-14 of the Users Handbook.
783    #[test]
784    fn op_rsx_example_5() {
785        let context = make_ctx();
786        let w: Unsigned36Bit = u36!(0o454_453_452_451);
787        let orig_xj = u18!(0o202_101).reinterpret_as_signed();
788        let (xj, e) = simulate_rsx(
789            &context,
790            Unsigned6Bit::ONE,
791            orig_xj,
792            &w,
793            false,
794            Some(&[(21, SystemConfiguration::from(u9!(0o230)))]),
795            21,
796        );
797        assert_eq!(xj, orig_xj); // unchanged
798        assert_eq!(e, w);
799    }
800
801    /// Test case taken from example 6 on page 3-15 of the Users Handbook.
802    #[test]
803    fn op_rsx_example_6() {
804        let context = make_ctx();
805        let w: Unsigned36Bit = u36!(0o454_453_452_451);
806        let orig_xj = u18!(0o202_101).reinterpret_as_signed();
807        let (xj, e) = simulate_rsx(
808            &context,
809            Unsigned6Bit::ONE,
810            orig_xj,
811            &w,
812            false,
813            // Nonstandard configuration
814            Some(&[(1, SystemConfiguration::from(u9!(0o030)))]),
815            1,
816        );
817        assert_eq!(xj, u18!(0o777_777).reinterpret_as_signed());
818        assert_eq!(e, w);
819    }
820
821    /// Test case taken from example 7 on page 3-15 of the Users Handbook.
822    #[test]
823    fn op_rsx_example_7() {
824        let context = make_ctx();
825        let w: Unsigned36Bit = u36!(0o454_453_452_251);
826        let (xj, e) = simulate_rsx(
827            &context,
828            Unsigned6Bit::ONE,
829            u18!(0o402_101).reinterpret_as_signed(),
830            &w,
831            true,
832            Some(&[(1, SystemConfiguration::from(u9!(0o340)))]),
833            1,
834        );
835        assert_eq!(xj, u18!(0o452_251).reinterpret_as_signed());
836        assert_eq!(e, w);
837    }
838
839    /// Test case taken from example 8 on page 3-15 of the Users Handbook.
840    #[test]
841    fn op_rsx_example_8() {
842        let context = make_ctx();
843        let w: Unsigned36Bit = u36!(0o454_453_452_451);
844        let (xj, e) = simulate_rsx(
845            &context,
846            Unsigned6Bit::ZERO, // X₀
847            Signed18Bit::ZERO,
848            &w,
849            false,
850            Some(&[(1, SystemConfiguration::from(u9!(0o340)))]),
851            1,
852        );
853        assert_eq!(xj, 0); // X₀ cannot be changed.
854        assert_eq!(e, w);
855    }
856}