1use std::fmt::{self, Binary, Display, Formatter, Octal};
14
15use tracing::{Level, event};
16
17use base::prelude::*;
18
19use super::memory::get_standard_plugboard;
20
21pub(crate) enum ExchangeDirection {
25 ME,
27 EM,
29}
30
31#[derive(Clone, Copy, Debug)]
35pub(crate) struct QuarterActivity(u8);
36
37impl QuarterActivity {
38 pub(crate) fn new(bits: u8) -> QuarterActivity {
39 assert_eq!(bits & !0b1111, 0);
40 QuarterActivity(bits)
41 }
42
43 pub(crate) fn is_active(&self, q: &u8) -> bool {
44 assert!(*q < 4, "invalid quarter {q}");
45 let mask = 1 << *q;
46 self.0 & mask != 0
47 }
48
49 pub(crate) fn first_active_quarter(&self) -> Option<u8> {
50 let n = self.0.trailing_zeros() as u8;
51 if n < 4 { Some(n) } else { None }
52 }
53
54 pub(crate) fn masked_by(&self, mask: u8) -> QuarterActivity {
55 QuarterActivity::new(self.0 & mask)
56 }
57}
58
59impl Binary for QuarterActivity {
60 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
61 write!(f, "{:b}", self.0)
62 }
63}
64
65#[test]
66fn test_quarteractivity_first_active_quarter() {
67 fn first_active_quarter(n: u8) -> Option<u8> {
68 QuarterActivity(n).first_active_quarter()
69 }
70 assert_eq!(first_active_quarter(0), None);
71 assert_eq!(first_active_quarter(0b0001), Some(0));
72 assert_eq!(first_active_quarter(0b0011), Some(0));
73 assert_eq!(first_active_quarter(0b1111), Some(0));
74 assert_eq!(first_active_quarter(0b1110), Some(1));
75 assert_eq!(first_active_quarter(0b0010), Some(1));
76 assert_eq!(first_active_quarter(0b1100), Some(2));
77 assert_eq!(first_active_quarter(0b0100), Some(2));
78 assert_eq!(first_active_quarter(0b1000), Some(3));
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq)]
82enum Permutation {
83 P0 = 0,
84 P1,
85 P2,
86 P3,
87 P4,
88 P5,
89 P6,
90 P7,
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq)]
94pub(crate) enum SubwordForm {
95 FullWord = 0, Halves = 1, ThreeOne = 2, Quarters = 3, }
100
101#[derive(Clone, Copy, Debug)]
120pub(crate) struct SystemConfiguration(Unsigned9Bit);
121
122impl From<u8> for SystemConfiguration {
123 fn from(n: u8) -> SystemConfiguration {
124 SystemConfiguration(Unsigned9Bit::from(n))
125 }
126}
127
128impl TryFrom<u16> for SystemConfiguration {
129 type Error = ConversionFailed;
130 fn try_from(n: u16) -> Result<SystemConfiguration, ConversionFailed> {
131 Unsigned9Bit::try_from(n).map(SystemConfiguration::from)
132 }
133}
134
135impl From<Unsigned9Bit> for SystemConfiguration {
136 fn from(n: Unsigned9Bit) -> SystemConfiguration {
137 SystemConfiguration(n)
138 }
139}
140
141impl From<SystemConfiguration> for Unsigned9Bit {
142 fn from(cfg: SystemConfiguration) -> Unsigned9Bit {
143 cfg.0
144 }
145}
146
147impl PartialEq for SystemConfiguration {
148 fn eq(&self, other: &SystemConfiguration) -> bool {
149 self.0.eq(&other.0)
150 }
151}
152
153impl Display for SystemConfiguration {
154 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
155 Octal::fmt(&self.0, f)
156 }
157}
158
159impl Octal for SystemConfiguration {
160 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
161 Octal::fmt(&self.0, f)
162 }
163}
164
165impl SystemConfiguration {
166 pub(crate) fn zero() -> SystemConfiguration {
167 SystemConfiguration(Unsigned9Bit::ZERO)
168 }
169
170 fn permutation(&self) -> Permutation {
171 match u16::from(self.0) & 0b111 {
172 0 => Permutation::P0,
173 1 => Permutation::P1,
174 2 => Permutation::P2,
175 3 => Permutation::P3,
176 4 => Permutation::P4,
177 5 => Permutation::P5,
178 6 => Permutation::P6,
179 7 => Permutation::P7,
180 _ => unreachable!(),
181 }
182 }
183
184 pub(crate) fn active_quarters(&self) -> QuarterActivity {
196 let act_field: u16 = (!(u16::from(self.0) >> 3)) & 0b1111;
205 let result = QuarterActivity::new(act_field as u8);
206 event!(
207 Level::TRACE,
208 "active_quarters: system configuration {:>03o} -> result {:?}",
209 self,
210 result
211 );
212 result
213 }
214
215 fn subword_form(&self) -> SubwordForm {
216 const MASK: u16 = 0o3 << 7;
217 match u16::from(self.0) & MASK {
218 0b000000000 => SubwordForm::FullWord, 0b010000000 => SubwordForm::Halves, 0b100000000 => SubwordForm::ThreeOne, 0b110000000 => SubwordForm::Quarters, _ => unreachable!(),
223 }
224 }
225}
226
227#[cfg(test)]
228macro_rules! assert_octal_eq {
229 ($left:expr_2021, $right:expr_2021 $(,)?) => {{
230 match (&$left, &$right) {
231 (left_val, right_val) => {
232 if !(*left_val == *right_val) {
233 panic!(
234 "Assertion failed: {:#>012o} != {:>#012o}",
235 left_val, right_val
236 );
237 }
238 }
239 }
240 }};
241}
242
243fn permutation_source(
255 permutation: &Permutation,
256 direction: &ExchangeDirection,
257 target_quarter: u8,
258) -> u8 {
259 match permutation {
260 Permutation::P0 => target_quarter % 4,
261 Permutation::P1 => match *direction {
262 ExchangeDirection::ME => (target_quarter + 1) % 4,
263 ExchangeDirection::EM => (target_quarter + 3) % 4,
264 },
265 Permutation::P2 => (target_quarter + 2) % 4,
266 Permutation::P3 => match *direction {
267 ExchangeDirection::ME => (target_quarter + 3) % 4,
268 ExchangeDirection::EM => (target_quarter + 1) % 4,
269 },
270 Permutation::P4 => target_quarter ^ 0b01,
271 Permutation::P5 => target_quarter ^ 0b11,
272 Permutation::P6 => match (direction, target_quarter) {
273 (&ExchangeDirection::ME, 3) => 2,
274 (&ExchangeDirection::ME, 2) => 1,
275 (&ExchangeDirection::ME, 1) => 3,
276 (&ExchangeDirection::ME, 0) => 0,
277 (&ExchangeDirection::EM, 3) => 1,
278 (&ExchangeDirection::EM, 2) => 3,
279 (&ExchangeDirection::EM, 1) => 2,
280 (&ExchangeDirection::EM, 0) => 0,
281 (_, _) => unreachable!(),
282 },
283 Permutation::P7 => match (direction, target_quarter) {
284 (&ExchangeDirection::ME, 3) => 1,
285 (&ExchangeDirection::ME, 2) => 3,
286 (&ExchangeDirection::ME, 1) => 2,
287 (&ExchangeDirection::ME, 0) => 0,
288 (&ExchangeDirection::EM, 3) => 2,
289 (&ExchangeDirection::EM, 2) => 1,
290 (&ExchangeDirection::EM, 1) => 3,
291 (&ExchangeDirection::EM, 0) => 0,
292 (_, _) => unreachable!(),
293 },
294 }
295}
296
297fn quarter_mask(n: u8) -> u64 {
302 assert!(n < 4);
303 0o777 << (n * 9)
304}
305
306fn apply_sign(word: u64, quarter_number_from: u8, quarter_number_to: u8) -> u64 {
307 let signbit = word & (0o400 << (quarter_number_from * 9));
308 let mask = quarter_mask(quarter_number_to);
309 if signbit == 0 {
310 word & !mask
311 } else {
312 word | mask
313 }
314}
315
316fn sign_extend_quarters(
317 w: Unsigned36Bit,
318 activity: QuarterActivity,
319 ordering: &[u8],
320) -> Unsigned36Bit {
321 let mut word: u64 = u64::from(w);
322 if let Some(mut last_active) = activity.first_active_quarter() {
323 for q in ordering {
324 if activity.is_active(q) {
325 last_active = *q;
326 } else {
327 word = apply_sign(word, last_active, *q);
328 }
329 }
330 Unsigned36Bit::try_from(word).expect("result should be in range (this is a bug)")
331 } else {
332 w
333 }
334}
335
336pub(crate) fn sign_extend(
345 form: &SubwordForm,
346 word: Unsigned36Bit,
347 quarter_activity: QuarterActivity,
348) -> Unsigned36Bit {
349 match form {
350 SubwordForm::FullWord => {
351 match quarter_activity.first_active_quarter() {
353 Some(q) => {
354 let extend_order: Vec<u8> = (q..(q + 4)).map(|q| q % 4).collect();
355 sign_extend_quarters(word, quarter_activity, &extend_order)
356 }
357 None => word,
358 }
359 }
360 SubwordForm::Halves => {
361 let left_activity = quarter_activity.masked_by(0b1100);
363 let sign_extended_on_lhs = match left_activity.first_active_quarter() {
364 None => word,
365 Some(first_active) => sign_extend_quarters(
366 word,
367 left_activity,
368 match first_active {
369 2 => &[2, 3],
370 3 => &[3, 2],
371 _ => unreachable!(),
372 },
373 ),
374 };
375 let right_activity = quarter_activity.masked_by(0b0011);
376 let sign_extended_on_rhs = match right_activity.first_active_quarter() {
377 None => word,
378 Some(first_active) => sign_extend_quarters(
379 word,
380 right_activity,
381 match first_active {
382 0 => &[0, 1],
383 1 => &[1, 0],
384 _ => unreachable!(),
385 },
386 ),
387 };
388 join_halves(
389 left_half(sign_extended_on_lhs),
390 right_half(sign_extended_on_rhs),
391 )
392 }
393 SubwordForm::ThreeOne => {
394 let left_activity = quarter_activity.masked_by(0b1110);
396 match left_activity.first_active_quarter() {
397 None => word,
398 Some(first_active) => sign_extend_quarters(
399 word,
400 left_activity,
401 match first_active {
402 1 => &[1, 2, 3],
403 2 => &[2, 3, 1],
404 3 => &[3, 1, 2],
405 _ => unreachable!(),
406 },
407 ),
408 }
409 }
410 SubwordForm::Quarters => {
411 word }
413 }
414}
415
416#[test]
417fn test_sign_extend_full_word() {
418 use SubwordForm::*;
419
420 assert_octal_eq!(
422 sign_extend(
423 &FullWord,
424 Unsigned36Bit::from(0_u8),
425 QuarterActivity::new(0b1111)
426 ),
427 u36!(0)
428 );
429 assert_octal_eq!(
430 sign_extend(
431 &FullWord,
432 u36!(0o300_000_000_000),
433 QuarterActivity::new(0b1111)
434 ),
435 u36!(0o300_000_000_000)
436 );
437 assert_octal_eq!(
438 sign_extend(
439 &FullWord,
440 u36!(0o000_300_000_000),
441 QuarterActivity::new(0b1111)
442 ),
443 u36!(0o000_300_000_000)
444 );
445 assert_octal_eq!(
446 sign_extend(
447 &FullWord,
448 u36!(0o000_000_300_000),
449 QuarterActivity::new(0b1111)
450 ),
451 u36!(0o000_000_300_000)
452 );
453 assert_octal_eq!(
454 sign_extend(
455 &FullWord,
456 u36!(0o000_000_000_300),
457 QuarterActivity::new(0b1111)
458 ),
459 u36!(0o000_000_000_300)
460 );
461
462 assert_octal_eq!(
464 sign_extend(
465 &FullWord,
466 u36!(0o444333222111),
467 QuarterActivity::new(0b0000)
468 ),
469 u36!(0o444333222111)
470 );
471
472 assert_octal_eq!(
474 sign_extend(
475 &FullWord,
476 u36!(0o727_003_002_001),
477 QuarterActivity::new(0b0111)
478 ),
479 u36!(0o000_003_002_001)
480 );
481 assert_octal_eq!(
482 sign_extend(
483 &FullWord,
484 u36!(0o004_727_002_001),
485 QuarterActivity::new(0b1011)
486 ),
487 u36!(0o004_000_002_001)
488 );
489 assert_octal_eq!(
490 sign_extend(
491 &FullWord,
492 u36!(0o004_003_727_001),
493 QuarterActivity::new(0b1101)
494 ),
495 u36!(0o004_003_000_001)
496 );
497 assert_octal_eq!(
498 sign_extend(
499 &FullWord,
500 u36!(0o004_003_002_727),
501 QuarterActivity::new(0b1110)
502 ),
503 u36!(0o004_003_002_000)
504 );
505
506 assert_octal_eq!(
508 sign_extend(
509 &FullWord,
510 u36!(0o272_403_002_001),
511 QuarterActivity::new(0b0111)
512 ),
513 u36!(0o777_403_002_001)
514 );
515 assert_octal_eq!(
516 sign_extend(
517 &FullWord,
518 u36!(0o004_272_402_001),
519 QuarterActivity::new(0b1011)
520 ),
521 u36!(0o004_777_402_001)
522 );
523 assert_octal_eq!(
524 sign_extend(
525 &FullWord,
526 u36!(0o004_003_272_401),
527 QuarterActivity::new(0b1101)
528 ),
529 u36!(0o004_003_777_401)
530 );
531 assert_octal_eq!(
532 sign_extend(
533 &FullWord,
534 u36!(0o404_003_002_272),
535 QuarterActivity::new(0b1110)
536 ),
537 u36!(0o404_003_002_777)
538 );
539
540 assert_octal_eq!(
542 sign_extend(
543 &FullWord,
544 u36!(0o727_003_002_727),
545 QuarterActivity::new(0b0110)
546 ),
547 u36!(0o000_003_002_000)
548 );
549 assert_octal_eq!(
550 sign_extend(
551 &FullWord,
552 u36!(0o727_727_002_001),
553 QuarterActivity::new(0b0011)
554 ),
555 u36!(0o000_000_002_001)
556 );
557 assert_octal_eq!(
558 sign_extend(
559 &FullWord,
560 u36!(0o004_727_727_001),
561 QuarterActivity::new(0b1001)
562 ),
563 u36!(0o004_000_000_001)
564 );
565 assert_octal_eq!(
566 sign_extend(
567 &FullWord,
568 u36!(0o004_003_727_727),
569 QuarterActivity::new(0b1100)
570 ),
571 u36!(0o004_003_000_000)
572 );
573
574 assert_octal_eq!(
576 sign_extend(
577 &FullWord,
578 u36!(0o727_727_727_001),
579 QuarterActivity::new(0b0001)
580 ),
581 u36!(0o000_000_000_001)
582 );
583 assert_octal_eq!(
584 sign_extend(
585 &FullWord,
586 u36!(0o727_727_002_727),
587 QuarterActivity::new(0b0010)
588 ),
589 u36!(0o000_000_002_000)
590 );
591 assert_octal_eq!(
592 sign_extend(
593 &FullWord,
594 u36!(0o727_003_727_727),
595 QuarterActivity::new(0b0100)
596 ),
597 u36!(0o000_003_000_000),
598 );
599 assert_octal_eq!(
600 sign_extend(
601 &FullWord,
602 u36!(0o004_727_727_727),
603 QuarterActivity::new(0b1000)
604 ),
605 u36!(0o004_000_000_000)
606 );
607
608 assert_octal_eq!(
610 sign_extend(
611 &FullWord,
612 u36!(0o727_727_727_401),
613 QuarterActivity::new(0b0001)
614 ),
615 u36!(0o777_777_777_401)
616 );
617 assert_octal_eq!(
618 sign_extend(
619 &FullWord,
620 u36!(0o727_727_402_727),
621 QuarterActivity::new(0b0010)
622 ),
623 u36!(0o777_777_402_777)
624 );
625 assert_octal_eq!(
626 sign_extend(
627 &FullWord,
628 u36!(0o727_403_727_727),
629 QuarterActivity::new(0b0100)
630 ),
631 u36!(0o777_403_777_777)
632 );
633 assert_octal_eq!(
634 sign_extend(
635 &FullWord,
636 u36!(0o404_727_727_727),
637 QuarterActivity::new(0b1000)
638 ),
639 u36!(0o404_777_777_777),
640 );
641}
642
643#[test]
644fn test_sign_extend_halves() {
645 use SubwordForm::*;
646
647 assert_octal_eq!(
649 sign_extend(&Halves, u36!(0), QuarterActivity::new(0b1111)),
650 u36!(0)
651 );
652 assert_octal_eq!(
653 sign_extend(
654 &Halves,
655 u36!(0o400_000_000_000),
656 QuarterActivity::new(0b1111)
657 ),
658 u36!(0o400_000_000_000)
659 );
660 assert_octal_eq!(
661 sign_extend(
662 &Halves,
663 u36!(0o000_400_000_000),
664 QuarterActivity::new(0b1111)
665 ),
666 u36!(0o000_400_000_000),
667 );
668 assert_octal_eq!(
669 sign_extend(
670 &Halves,
671 u36!(0o000_000_400_000),
672 QuarterActivity::new(0b1111)
673 ),
674 u36!(0o000_000_400_000)
675 );
676 assert_octal_eq!(
677 sign_extend(
678 &Halves,
679 u36!(0o000_000_000_400),
680 QuarterActivity::new(0b1111)
681 ),
682 u36!(0o000_000_000_400)
683 );
684
685 assert_octal_eq!(
687 sign_extend(
688 &Halves,
689 u36!(0o444_333_222_111),
690 QuarterActivity::new(0b0000)
691 ),
692 u36!(0o444_333_222_111)
693 );
694
695 assert_octal_eq!(
697 sign_extend(
698 &Halves,
699 u36!(0o004_003_002_001),
700 QuarterActivity::new(0b0101)
701 ),
702 u36!(0o000_003_000_001)
703 );
704 assert_octal_eq!(
705 sign_extend(
706 &Halves,
707 u36!(0o004_003_002_001),
708 QuarterActivity::new(0b0001)
709 ),
710 u36!(0o004_003_000_001)
711 );
712 assert_octal_eq!(
713 sign_extend(
714 &Halves,
715 u36!(0o004_003_002_001),
716 QuarterActivity::new(0b1010)
717 ),
718 u36!(0o004_000_002_000)
719 );
720 assert_octal_eq!(
721 sign_extend(
722 &Halves,
723 u36!(0o004_003_002_001),
724 QuarterActivity::new(0b0110)
725 ),
726 u36!(0o000_003_002_000)
727 );
728
729 assert_octal_eq!(
731 sign_extend(
732 &Halves,
733 u36!(0o004_403_202_001),
736 QuarterActivity::new(0b0110)
737 ),
738 u36!(0o777_403_202_000)
739 );
740 assert_octal_eq!(
741 sign_extend(
742 &Halves,
743 u36!(0o404_003_402_001),
744 QuarterActivity::new(0b1010)
745 ),
746 u36!(0o404_777_402_777)
747 );
748 assert_octal_eq!(
749 sign_extend(
750 &Halves,
751 u36!(0o004_403_002_401),
752 QuarterActivity::new(0b0101)
753 ),
754 u36!(0o777_403_777_401)
755 );
756 assert_octal_eq!(
757 sign_extend(
758 &Halves,
759 u36!(0o004_003_002_401),
760 QuarterActivity::new(0b0001)
761 ),
762 u36!(0o004_003_777_401)
763 );
764
765 assert_octal_eq!(
769 sign_extend(
770 &Halves,
771 u36!(0o004_003_002_001),
772 QuarterActivity::new(0b0001)
773 ),
774 u36!(0o004_003_000_001)
775 );
776 assert_octal_eq!(
777 sign_extend(
778 &Halves,
779 u36!(0o004_003_002_001),
780 QuarterActivity::new(0b0010)
781 ),
782 u36!(0o004_003_002_000)
783 );
784 assert_octal_eq!(
785 sign_extend(
786 &Halves,
787 u36!(0o004_003_002_001),
788 QuarterActivity::new(0b0100)
789 ),
790 u36!(0o000_003_002_001)
791 );
792 assert_octal_eq!(
793 sign_extend(
794 &Halves,
795 u36!(0o004_003_002_001),
796 QuarterActivity::new(0b1000)
797 ),
798 u36!(0o004_000_002_001)
799 );
800}
801
802fn fetch_quarter(source_quarter: u8, source: &u64) -> u64 {
809 (source & quarter_mask(source_quarter)) >> (source_quarter * 9)
810}
811
812#[test]
813fn test_fetch_quarter() {
814 assert_octal_eq!(fetch_quarter(2, &0o444333222111), 0o333);
815}
816
817fn permute(
820 permutation: &Permutation,
821 direction: &ExchangeDirection,
822 active_quarters: &QuarterActivity,
823 source: &Unsigned36Bit,
824 dest: &Unsigned36Bit,
825) -> Unsigned36Bit {
826 let mut result: u64 = (*dest).into();
827 let source_bits: u64 = u64::from(*source);
828 for target_quarter in 0_u8..4_u8 {
829 let source_quarter: u8 = permutation_source(permutation, direction, target_quarter);
830
831 let e_quarter: u8 = match *direction {
838 ExchangeDirection::ME => target_quarter,
839 ExchangeDirection::EM => source_quarter,
840 };
841 if active_quarters.is_active(&e_quarter) {
842 let value = fetch_quarter(source_quarter, &source_bits) << (target_quarter * 9);
845 let target_mask: u64 = quarter_mask(target_quarter);
846 result &= !target_mask;
847 result |= target_mask & value;
848 }
849 }
850 Unsigned36Bit::try_from(result).unwrap()
851}
852
853#[test]
854fn test_permute_p0() {
855 for direction in &[ExchangeDirection::ME, ExchangeDirection::EM] {
860 assert_octal_eq!(
861 permute(
862 &Permutation::P0,
863 direction,
864 &QuarterActivity::new(0b1111),
865 &u36!(0o444333222111),
866 &u36!(0o777666555444),
867 ),
868 u36!(0o444333222111),
869 );
870 assert_octal_eq!(
871 permute(
872 &Permutation::P0,
873 direction,
874 &QuarterActivity::new(0b1110),
875 &u36!(0o444333222111),
876 &u36!(0o777666555444),
877 ),
878 u36!(0o444333222444),
879 );
880 }
881}
882
883pub(crate) fn exchanged_value_for_load_without_sign_extension(
886 cfg: &SystemConfiguration,
887 source: &Unsigned36Bit,
888 dest: &Unsigned36Bit,
889) -> Unsigned36Bit {
890 permute(
891 &cfg.permutation(),
892 &ExchangeDirection::ME,
893 &cfg.active_quarters(),
894 source,
895 dest,
896 )
897}
898
899pub(crate) fn exchanged_value_for_load(
902 cfg: &SystemConfiguration,
903 source: &Unsigned36Bit,
904 dest: &Unsigned36Bit,
905) -> Unsigned36Bit {
906 let permuted_target = exchanged_value_for_load_without_sign_extension(cfg, source, dest);
907 sign_extend(&cfg.subword_form(), permuted_target, cfg.active_quarters())
908}
909
910pub(crate) fn exchanged_value_for_store(
920 cfg: &SystemConfiguration,
921 source: &Unsigned36Bit,
922 dest: &Unsigned36Bit,
923) -> Unsigned36Bit {
924 permute(
925 &cfg.permutation(),
926 &ExchangeDirection::EM,
927 &cfg.active_quarters(),
928 source,
929 dest,
930 )
931 }
934
935pub(crate) fn standard_plugboard_f_memory_settings() -> [SystemConfiguration; 0o40] {
936 let mut result: [SystemConfiguration; 0o40] = {
937 let default_val = SystemConfiguration::from(0_u8);
938 [default_val; 32]
939 };
940 let plugboard = get_standard_plugboard();
941 for (i, spg_word) in plugboard.iter().take(0o10).enumerate() {
942 for quarter in 0..4 {
943 let index = (i * 4) + quarter;
944 let value: u64 = u64::from(*spg_word) >> (quarter * 9);
945 match SystemConfiguration::try_from((value & 0o777) as u16) {
946 Ok(v) => {
947 event!(
948 Level::TRACE,
949 "F-memory index {:>03o} = {:>03o}",
950 index,
951 value
952 );
953 result[index] = v;
954 }
955 Err(_) => {
956 panic!("conversion input value should be <= 0o777");
957 }
958 }
959 }
960 }
961 result
962}
963
964#[cfg(test)]
965mod tests {
966 use super::*;
967
968 #[test]
969 fn test_system_configuration_standard_config() {
970 const STANDARD_CONFIG: [u16; 32] = [
973 0o000, 0o340, 0o342, 0o760, 0o761, 0o762, 0o763, 0o410, 0o411, 0o140, 0o142, 0o160,
974 0o161, 0o162, 0o163, 0o202, 0o200, 0o230, 0o232, 0o732, 0o733, 0o730, 0o731, 0o605,
975 0o600, 0o750, 0o670, 0o320, 0o333, 0o330, 0o331, 0o604,
976 ];
977
978 #[derive(Debug)]
979 struct Expectation {
980 config: SystemConfiguration,
981 perm: Permutation,
982 form: SubwordForm,
983 active: u8,
984 }
985 fn configval(n: u16) -> SystemConfiguration {
986 SystemConfiguration(Unsigned9Bit::try_from(n).expect("valid test data"))
987 }
988 use Permutation::*;
989 use SubwordForm::*;
990 let cases: [Expectation; 32] = [
991 Expectation {
994 config: configval(0),
995 perm: P0,
997 form: FullWord,
998 active: 0b1111,
999 },
1000 Expectation {
1001 config: configval(0o340),
1002 perm: P0,
1004 form: Halves,
1005 active: 0b0011,
1006 },
1007 Expectation {
1008 config: configval(0o342),
1009 perm: P2,
1011 form: Halves,
1012 active: 0b0011,
1013 },
1014 Expectation {
1015 config: configval(0o760),
1016 perm: P0,
1018 form: Quarters,
1019 active: 0b0001,
1020 },
1021 Expectation {
1023 config: configval(0o761),
1024 perm: P1,
1026 form: Quarters,
1027 active: 0b0001,
1028 },
1029 Expectation {
1030 config: configval(0o762),
1031 perm: P2,
1033 form: Quarters,
1034 active: 0b0001,
1035 },
1036 Expectation {
1037 config: configval(0o763),
1038 perm: P3,
1040 form: Quarters,
1041 active: 0b0001,
1042 },
1043 Expectation {
1044 config: configval(0o410),
1045 perm: P0,
1047 form: ThreeOne,
1048 active: 0b1110,
1049 },
1050 Expectation {
1052 config: configval(0o411),
1053 perm: P1,
1055 form: ThreeOne,
1056 active: 0b1110,
1057 },
1058 Expectation {
1059 config: configval(0o140),
1060 perm: P0,
1062 form: FullWord,
1063 active: 0b0011,
1064 },
1065 Expectation {
1066 config: configval(0o142),
1067 perm: P2,
1069 form: FullWord,
1070 active: 0b0011,
1071 },
1072 Expectation {
1073 config: configval(0o160),
1074 perm: P0,
1076 form: FullWord,
1077 active: 0b0001,
1078 },
1079 Expectation {
1081 config: configval(0o161),
1082 perm: P1,
1084 form: FullWord,
1085 active: 0b0001,
1086 },
1087 Expectation {
1088 config: configval(0o162),
1089 perm: P2,
1091 form: FullWord,
1092 active: 0b0001,
1093 },
1094 Expectation {
1095 config: configval(0o163),
1096 perm: P3,
1098 form: FullWord,
1099 active: 0b0001,
1100 },
1101 Expectation {
1102 config: configval(0o202),
1103 perm: P2,
1105 form: Halves,
1106 active: 0b1111,
1107 },
1108 Expectation {
1110 config: configval(0o200),
1111 perm: P0,
1113 form: Halves,
1114 active: 0b1111,
1115 },
1116 Expectation {
1117 config: configval(0o230),
1118 perm: P0,
1120 form: Halves,
1121 active: 0b1100,
1122 },
1123 Expectation {
1124 config: configval(0o232),
1125 perm: P2,
1127 form: Halves,
1128 active: 0b1100,
1129 },
1130 Expectation {
1131 config: configval(0o732),
1132 perm: P2,
1134 form: Quarters,
1135 active: 0b0100,
1136 },
1137 Expectation {
1139 config: configval(0o733),
1140 perm: P3,
1142 form: Quarters,
1143 active: 0b0100,
1144 },
1145 Expectation {
1146 config: configval(0o730),
1147 perm: P0,
1149 form: Quarters,
1150 active: 0b0100,
1151 },
1152 Expectation {
1153 config: configval(0o731),
1154 perm: P1,
1156 form: Quarters,
1157 active: 0b0100,
1158 },
1159 Expectation {
1160 config: configval(0o605),
1161 perm: P5,
1163 form: Quarters,
1164 active: 0b1111,
1165 },
1166 Expectation {
1168 config: configval(0o600),
1169 perm: P0,
1171 form: Quarters,
1172 active: 0b1111,
1173 },
1174 Expectation {
1175 config: configval(0o750),
1176 perm: P0,
1178 form: Quarters,
1179 active: 0b0010,
1180 },
1181 Expectation {
1182 config: configval(0o670),
1183 perm: P0,
1185 form: Quarters,
1186 active: 0b1000,
1187 },
1188 Expectation {
1189 config: configval(0o320),
1190 perm: P0,
1192 form: Halves,
1193 active: 0b0101,
1194 },
1195 Expectation {
1197 config: configval(0o333),
1198 perm: P3,
1200 form: Halves,
1201 active: 0b0100,
1202 },
1203 Expectation {
1204 config: configval(0o330),
1205 perm: P0,
1207 form: Halves,
1208 active: 0b0100,
1209 },
1210 Expectation {
1211 config: configval(0o331),
1212 perm: P1,
1214 form: Halves,
1215 active: 0b0100,
1216 },
1217 Expectation {
1218 config: configval(0o604),
1219 perm: P4,
1221 form: Quarters,
1222 active: 0b1111,
1223 },
1224 ];
1225
1226 for (index, case) in cases.iter().enumerate() {
1230 let cfg = Unsigned9Bit::try_from(STANDARD_CONFIG[index]).expect("valid test data");
1231 assert_eq!(
1232 cfg,
1233 Unsigned9Bit::from(case.config),
1234 "config in test case does not match standard config"
1235 );
1236 }
1237
1238 let f_memory = standard_plugboard_f_memory_settings();
1239 for (index, expectation) in cases.iter().enumerate() {
1240 let got = f_memory[index];
1241 assert_eq!(
1242 expectation.config, got,
1243 "config at index {} in test case does not match standard config: {:o} != {:o}",
1244 index, expectation.config, got,
1245 );
1246 }
1247
1248 for case in cases {
1249 assert_eq!(
1250 case.config.permutation(),
1251 case.perm,
1252 "non-matching permutation"
1253 );
1254 assert_eq!(
1255 case.config.subword_form(),
1256 case.form,
1257 "non-matching subword form"
1258 );
1259 for q in 0u8..4u8 {
1260 let expect_active = case.active & (1 << q) != 0;
1261 let got_active = case.config.active_quarters().is_active(&q);
1262 assert_eq!(
1263 got_active,
1264 expect_active,
1265 "expected quarter activity {:?}, got quarter activity {:?}",
1266 case.active,
1267 case.config.active_quarters()
1268 );
1269 }
1270 }
1271 }
1272}