1use std::fmt::{self, Debug, Display, Formatter};
62
63#[cfg(test)]
64use test_strategy::{Arbitrary, proptest};
65
66use super::bitselect::{BitPos, BitSelector};
67use super::prelude::*;
68use super::subword;
69mod format;
70
71pub const DEFER_BIT: Unsigned36Bit = Unsigned36Bit {
72 bits: 0o400_000_u64,
73};
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
81pub enum NonexistentBitPos {
82 Zero,
83 Thirteen,
84 Fourteen,
85 Fifteen,
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
89pub enum SkmBitPos {
90 Value(BitPos), Meta, Parity, ParityCircuit, Nonexistent(NonexistentBitPos),
104}
105
106impl Display for SkmBitPos {
107 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
108 match self {
109 SkmBitPos::Nonexistent(NonexistentBitPos::Zero) => f.write_str("0"),
110 SkmBitPos::Value(bitpos) => write!(f, "{bitpos}"),
111 SkmBitPos::Meta => f.write_str("10"),
112 SkmBitPos::Parity => f.write_str("11"),
113 SkmBitPos::ParityCircuit => f.write_str("12"),
114 SkmBitPos::Nonexistent(NonexistentBitPos::Thirteen) => f.write_str("13"),
115 SkmBitPos::Nonexistent(NonexistentBitPos::Fourteen) => f.write_str("14"),
116 SkmBitPos::Nonexistent(NonexistentBitPos::Fifteen) => f.write_str("15"),
117 }
118 }
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
126pub struct SkmBitSelector {
127 pub quarter: Quarter,
128 pub bitpos: SkmBitPos,
129}
130
131impl Display for SkmBitSelector {
174 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
175 write!(f, "{}.{}", self.quarter, self.bitpos)
176 }
177}
178
179impl SkmBitSelector {
180 #[must_use]
181 pub fn to_value_bit(self) -> Option<BitSelector> {
182 match self.bitpos {
183 SkmBitPos::Value(b) => Some(BitSelector {
184 quarter: self.quarter,
185 bitpos: b,
186 }),
187 _ => None,
188 }
189 }
190}
191
192#[must_use]
195pub fn index_address_to_bit_selection(index_address: Unsigned6Bit) -> SkmBitSelector {
196 let j: u8 = u8::from(index_address);
197 let bitpos: SkmBitPos = match j & 0b1111_u8 {
198 0 => SkmBitPos::Nonexistent(NonexistentBitPos::Zero),
199 1 => SkmBitPos::Value(BitPos::B1),
200 2 => SkmBitPos::Value(BitPos::B2),
201 3 => SkmBitPos::Value(BitPos::B3),
202 4 => SkmBitPos::Value(BitPos::B4),
203 5 => SkmBitPos::Value(BitPos::B5),
204 6 => SkmBitPos::Value(BitPos::B6),
205 7 => SkmBitPos::Value(BitPos::B7),
206 8 => SkmBitPos::Value(BitPos::B8),
207 9 => SkmBitPos::Value(BitPos::B9),
208 10 => SkmBitPos::Meta,
209 11 => SkmBitPos::Parity,
210 12 => SkmBitPos::ParityCircuit,
211 13 => SkmBitPos::Nonexistent(NonexistentBitPos::Thirteen),
212 14 => SkmBitPos::Nonexistent(NonexistentBitPos::Fourteen),
213 15 => SkmBitPos::Nonexistent(NonexistentBitPos::Fifteen),
214 _ => unreachable!(),
215 };
216 SkmBitSelector {
217 quarter: match (j >> 4) & 0b11 {
218 0 => Quarter::Q4,
219 1 => Quarter::Q1,
220 2 => Quarter::Q2,
221 3 => Quarter::Q3,
222 _ => unreachable!(),
223 },
224 bitpos,
225 }
226}
227
228#[derive(Clone, Copy)]
230pub struct Instruction(Unsigned36Bit);
231
232impl Instruction {
233 #[must_use]
235 pub fn invalid() -> Instruction {
236 Instruction(Unsigned36Bit::ZERO)
237 }
238
239 #[must_use]
240 pub fn bits(&self) -> Unsigned36Bit {
241 self.0
242 }
243}
244
245impl From<Unsigned36Bit> for Instruction {
246 fn from(w: Unsigned36Bit) -> Instruction {
247 Instruction(w)
248 }
249}
250
251impl From<Instruction> for Unsigned36Bit {
252 fn from(inst: Instruction) -> Unsigned36Bit {
253 inst.0
254 }
255}
256
257impl Debug for Instruction {
258 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
259 let lhs = match SymbolicInstruction::try_from(self) {
260 Ok(sym) => format!("{sym}"),
261 Err(e) => format!("(invalid instruction: {e})"),
262 };
263 write!(f, "{:<40} {}", lhs, self.0)
264 }
265}
266
267pub trait Inst {
270 fn is_held(&self) -> bool;
272
273 fn configuration(&self) -> Unsigned5Bit;
284
285 fn index_address(&self) -> Unsigned6Bit;
289
290 fn opcode_number(&self) -> u8;
292
293 fn is_deferred_addressing(&self) -> bool;
295
296 fn operand_address_and_defer_bit(&self) -> Unsigned18Bit;
299
300 fn operand_address(&self) -> OperandAddress;
301}
302
303impl Inst for Instruction {
304 fn is_held(&self) -> bool {
305 const HOLD_BIT: u64 = 1_u64 << 35;
306 !(self.0 & HOLD_BIT).is_zero()
307 }
308
309 fn configuration(&self) -> Unsigned5Bit {
310 let val: u8 = u8::try_from((self.0 >> 30) & 31_u64).unwrap();
311 Unsigned5Bit::try_from(val).unwrap()
312 }
313
314 fn opcode_number(&self) -> u8 {
315 u8::try_from((self.0 >> 24) & 63_u64).unwrap()
316 }
317
318 fn index_address(&self) -> Unsigned6Bit {
319 Unsigned6Bit::try_from((self.0 >> 18) & 63_u64).unwrap()
320 }
321
322 fn is_deferred_addressing(&self) -> bool {
323 self.0 & DEFER_BIT != 0
324 }
325
326 fn operand_address(&self) -> OperandAddress {
327 let bits: Unsigned18Bit = subword::right_half(self.0);
328 OperandAddress::from_raw_bits(bits)
329 }
330
331 fn operand_address_and_defer_bit(&self) -> Unsigned18Bit {
332 let bits: u32 = u32::try_from(self.0 & 0o777_777).unwrap();
333 Unsigned18Bit::try_from(bits).unwrap() }
335}
336
337#[repr(u8)]
346#[cfg_attr(test, derive(Arbitrary))]
347#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
348pub enum Opcode {
349 Opr = 0o4,
371 Jmp = 0o5,
372 Jpx = 0o6,
373 Jnx = 0o7,
374 Aux = 0o10,
375 Rsx = 0o11,
376 Skx = 0o12,
377 Exx = 0o14,
379 Adx = 0o15,
380 Dpx = 0o16,
381 Skm = 0o17,
382 Lde = 0o20,
383 Spf = 0o21,
384 Spg = 0o22,
385 Lda = 0o24,
387 Ldb = 0o25,
388 Ldc = 0o26,
389 Ldd = 0o27,
390 Ste = 0o30,
391 Flf = 0o31,
392 Flg = 0o32,
393 Sta = 0o34,
395 Stb = 0o35,
396 Stc = 0o36,
397 Std = 0o37,
398 Ite = 0o40,
399 Ita = 0o41,
400 Una = 0o42,
401 Sed = 0o43,
402
403 Jov = 0o45,
414
415 Jpa = 0o46,
416 Jna = 0o47,
417 Exa = 0o54,
422 Ins = 0o55,
423 Com = 0o56,
424 Tsd = 0o57,
425 Cya = 0o60,
426 Cyb = 0o61,
427 Cab = 0o62,
428 Noa = 0o64,
430 Dsa = 0o65,
431 Nab = 0o66,
432 Add = 0o67,
433 Sca = 0o70,
434 Scb = 0o71,
435 Sab = 0o72,
436 Tly = 0o74,
438 Div = 0o75,
439 Mul = 0o76,
440 Sub = 0o77,
441}
442
443impl Opcode {
444 #[must_use]
445 pub fn number(&self) -> u8 {
446 *self as u8
447 }
448
449 #[must_use]
450 pub fn hold_is_implicit(&self) -> bool {
451 matches!(self, Opcode::Lde | Opcode::Ite | Opcode::Jpx | Opcode::Jnx)
452 }
453}
454
455impl TryFrom<u8> for Opcode {
456 type Error = DisassemblyFailure;
457 fn try_from(opcode: u8) -> Result<Opcode, DisassemblyFailure> {
458 use Opcode::*;
459 #[allow(clippy::match_same_arms)] match opcode {
461 0 | 1 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
463 2 => {
464 Err(DisassemblyFailure::UnimplementedOpcode(opcode))
466 }
467 3 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
468 0o4 => Ok(Opr),
469 0o5 => Ok(Jmp),
470 0o6 => Ok(Jpx),
471 0o7 => Ok(Jnx),
472
473 0o10 => Ok(Aux),
474 0o11 => Ok(Rsx),
475 0o12 => Ok(Skx),
476 0o13 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
477 0o14 => Ok(Exx),
478 0o15 => Ok(Adx),
479 0o16 => Ok(Dpx),
480 0o17 => Ok(Skm),
481
482 0o20 => Ok(Lde),
483 0o21 => Ok(Spf),
484 0o22 => Ok(Spg),
485 0o23 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
486 0o24 => Ok(Lda),
487 0o25 => Ok(Ldb),
488 0o26 => Ok(Ldc),
489 0o27 => Ok(Ldd),
490
491 0o30 => Ok(Ste),
492 0o31 => Ok(Flf),
493 0o32 => Ok(Flg),
494 0o33 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
495 0o34 => Ok(Sta),
496 0o35 => Ok(Stb),
497 0o36 => Ok(Stc),
498 0o37 => Ok(Std),
499
500 0o40 => Ok(Ite),
501 0o41 => Ok(Ita),
502 0o42 => Ok(Una),
503 0o43 => Ok(Sed),
504 0o44 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
505 0o45 => Ok(Jov),
506 0o46 => Ok(Jpa),
507 0o47 => Ok(Jna),
508
509 0o50..=0o53 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
510 0o54 => Ok(Exa),
511 0o55 => Ok(Ins),
512 0o56 => Ok(Com),
513 0o57 => Ok(Tsd),
514
515 0o60 => Ok(Cya),
516 0o61 => Ok(Cyb),
517 0o62 => Ok(Cab),
518 0o63 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
519 0o64 => Ok(Noa),
520 0o65 => Ok(Dsa),
521 0o66 => Ok(Nab),
522 0o67 => Ok(Add),
523
524 0o70 => Ok(Sca),
525 0o71 => Ok(Scb),
526 0o72 => Ok(Sab),
527 0o73 => Err(DisassemblyFailure::InvalidOpcode(opcode)),
528 0o74 => Ok(Tly),
529 0o75 => Ok(Div),
530 0o76 => Ok(Mul),
531 0o77 => Ok(Sub),
532 _ => Err(DisassemblyFailure::InvalidOpcode(opcode)),
533 }
534 }
535}
536
537#[cfg_attr(test, derive(Arbitrary))]
542#[derive(Debug, PartialEq, Eq, Clone, Copy)]
543pub struct OperandAddress(Address);
544
545impl Default for OperandAddress {
546 fn default() -> OperandAddress {
547 OperandAddress(Address::ZERO)
548 }
549}
550
551impl OperandAddress {
552 #[must_use]
553 pub fn is_deferred(&self) -> bool {
554 let (_, deferred) = self.0.split();
555 deferred
556 }
557
558 #[must_use]
559 pub fn split(&self) -> (bool, Address) {
560 let (physical, deferred) = self.0.split();
561 (deferred, Address::from(physical))
562 }
563
564 #[must_use]
565 pub fn bits(&self) -> Unsigned18Bit {
566 Unsigned18Bit::from(self.0)
567 }
568
569 #[must_use]
570 pub fn direct(address: Address) -> OperandAddress {
571 let (_, defer) = address.split();
572 assert!(!defer);
573 Self(address)
574 }
575
576 #[must_use]
577 pub fn deferred(address: Address) -> OperandAddress {
578 let (bits, _) = address.split();
579 Self(Address::join(bits, true))
580 }
581
582 fn from_raw_bits(bits: Unsigned18Bit) -> OperandAddress {
583 Self(Address::from(bits))
584 }
585}
586
587#[cfg_attr(test, derive(Arbitrary))]
589#[derive(Debug, PartialEq, Eq)]
590pub struct SymbolicInstruction {
591 pub held: bool,
592 pub configuration: Unsigned5Bit,
593 pub opcode: Opcode,
594 pub index: Unsigned6Bit,
595 pub operand_address: OperandAddress,
596}
597
598impl SymbolicInstruction {
599 #[must_use]
600 pub fn opcode(&self) -> Opcode {
601 self.opcode
602 }
603}
604
605impl Inst for SymbolicInstruction {
606 fn is_held(&self) -> bool {
607 self.held
608 }
609
610 fn configuration(&self) -> Unsigned5Bit {
611 self.configuration
612 }
613
614 fn index_address(&self) -> Unsigned6Bit {
615 self.index
616 }
617
618 fn opcode_number(&self) -> u8 {
619 self.opcode.number()
620 }
621
622 fn is_deferred_addressing(&self) -> bool {
623 self.operand_address.is_deferred()
624 }
625
626 fn operand_address(&self) -> OperandAddress {
627 self.operand_address
628 }
629
630 fn operand_address_and_defer_bit(&self) -> Unsigned18Bit {
631 self.operand_address.bits()
632 }
633}
634
635impl From<&SymbolicInstruction> for Instruction {
636 fn from(s: &SymbolicInstruction) -> Instruction {
637 let hold_bit: u64 = if s.is_held() { 1 << 35 } else { 0 };
638 let cf_bits: u64 = (u64::from(s.configuration()) & 31_u64) << 30;
639 let op_bits: u64 = (u64::from(s.opcode_number()) & 63) << 24;
640 let index_bits: u64 = (u64::from(s.index_address())) << 18;
641 let address_and_defer_bits: u64 = s.operand_address_and_defer_bit().into();
642 let val: u64 = hold_bit | cf_bits | op_bits | index_bits | address_and_defer_bits;
643 Instruction(Unsigned36Bit::try_from(val).unwrap())
644 }
645}
646
647#[cfg(test)]
648#[proptest]
649fn reversible_disassembly(input: SymbolicInstruction) {
650 let inst: Instruction = Instruction::from(&input);
651 let bits = inst.bits();
652 match SymbolicInstruction::try_from(&inst) {
653 Ok(symbolic_disassembly) => {
654 assert_eq!(symbolic_disassembly, input);
655 }
656 Err(e) => {
657 panic!("input {input:?} assembled to {bits} but that could not be disassembled ({e})");
658 }
659 }
660}
661
662#[derive(PartialEq, Eq)]
665pub enum DisassemblyFailure {
666 InvalidOpcode(u8),
669
670 UnimplementedOpcode(u8),
676}
677
678impl Debug for DisassemblyFailure {
679 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
680 let opcode = match self {
681 DisassemblyFailure::InvalidOpcode(n) => {
682 f.write_str("InvalidOpcode")?;
683 n
684 }
685 DisassemblyFailure::UnimplementedOpcode(n) => {
686 f.write_str("UnimplementedOpcode")?;
687 n
688 }
689 };
690 write!(f, "(0{opcode:o})")
691 }
692}
693
694impl TryFrom<&Instruction> for SymbolicInstruction {
695 type Error = DisassemblyFailure;
696 fn try_from(inst: &Instruction) -> Result<SymbolicInstruction, DisassemblyFailure> {
697 Ok(SymbolicInstruction {
698 operand_address: inst.operand_address(),
699 index: inst.index_address(),
700 opcode: Opcode::try_from(inst.opcode_number())?,
701 configuration: inst.configuration(),
702 held: inst.is_held(),
703 })
704 }
705}
706
707#[cfg(test)]
717pub fn disassemble_word(w: Unsigned36Bit) -> Result<SymbolicInstruction, DisassemblyFailure> {
718 let inst: Instruction = Instruction(w);
719 dbg!(inst.operand_address());
720 SymbolicInstruction::try_from(&inst)
721}
722
723#[cfg(test)]
724mod tests {
725 use super::*;
726
727 fn config_value(n: u8) -> Unsigned5Bit {
728 Unsigned5Bit::try_from(n).expect("valid test data")
729 }
730
731 #[test]
732 fn test_instruction_jmp() {
733 let inst = Instruction(Unsigned36Bit::from(0o0_500_377_750_u32));
734 assert_eq!(
735 inst.operand_address(),
736 OperandAddress::direct(Address::from(u18!(0o377_750u32))),
737 "wrong address"
738 );
739 assert!(!inst.is_deferred_addressing(), "wrong dismiss");
740 assert_eq!(inst.index_address(), 0, "wrong index");
741 assert_eq!(inst.opcode_number(), 5, "wrong opcode");
742 assert!(inst.configuration().is_zero(), "wrong cf");
743 assert!(!inst.is_held(), "wrong held");
744 }
745
746 #[test]
747 fn test_instruction_held() {
748 assert!(!Instruction(Unsigned36Bit::ZERO).is_held());
749 assert!(
750 Instruction(
751 Unsigned36Bit::try_from(1_u64 << 35).expect("test data should be in-range")
752 )
753 .is_held()
754 );
755 }
756
757 #[test]
758 fn test_opcode_value_round_trip() {
759 for opcode_number in 0..u8::MAX {
760 if let Ok(opcode) = Opcode::try_from(opcode_number) {
761 assert_eq!(opcode_number, opcode.number());
762 }
763 }
764 }
765
766 #[test]
767 fn test_disassemble_jmp() {
768 assert_eq!(
769 disassemble_word(u36!(0o000_500_377_750)),
770 Ok(SymbolicInstruction {
771 operand_address: OperandAddress::direct(Address::from(u18!(0o_377_750))),
772 index: Unsigned6Bit::ZERO,
773 opcode: Opcode::Jmp,
774 configuration: config_value(0),
775 held: false,
776 })
777 );
778 }
779
780 #[test]
781 fn test_disassemble_jpx() {
782 assert_eq!(
783 disassemble_word(
784 Unsigned36Bit::try_from(0o700_603_377_752_u64)
785 .expect("test data should be in range")
786 ),
787 Ok(SymbolicInstruction {
788 operand_address: OperandAddress::direct(Address::from(u18!(0o0_377_752))),
789 index: Unsigned6Bit::try_from(3_u8).unwrap(),
790 opcode: Opcode::Jpx,
791 configuration: config_value(24), held: true,
793 })
794 );
795
796 assert_eq!(
797 disassemble_word(
798 Unsigned36Bit::try_from(0o360_601_377_751_u64)
799 .expect("test data should be in range")
800 ),
801 Ok(SymbolicInstruction {
802 operand_address: OperandAddress::direct(Address::from(u18!(0o0_377_751))),
803 index: Unsigned6Bit::ONE,
804 opcode: Opcode::Jpx,
805 configuration: config_value(30),
806 held: false,
807 })
808 );
809 }
810
811 #[test]
812 fn test_disassemble_dpx() {
813 assert_eq!(
816 disassemble_word(u36!(0o011_600_777_605_u64)),
817 Ok(SymbolicInstruction {
818 operand_address: OperandAddress::deferred(Address::from(u18!(0o377_605))),
819 index: Unsigned6Bit::try_from(0_u8).unwrap(),
820 opcode: Opcode::Dpx,
821 configuration: config_value(1),
822 held: false,
823 })
824 );
825 }
826
827 #[test]
828 fn test_assemble_rsx() {
829 const EXPECTED: u64 = 0o011_154_000_005_u64;
832 let sym = SymbolicInstruction {
833 held: false,
834 configuration: Unsigned5Bit::try_from(1_u8).expect("valid configuraiton field"),
835 opcode: Opcode::Rsx,
836 index: Unsigned6Bit::try_from(0o54_u8).expect("valid index field"),
837 operand_address: OperandAddress::direct(Address::from(u18!(5))),
838 };
839 let got: u64 = u64::from(Instruction::from(&sym).bits());
840 assert_eq!(
841 got, EXPECTED,
842 "Mismatch in assembly of {:?}: expected 0o{:012o}, got 0o{:012o}",
843 &sym, EXPECTED, got
844 );
845 }
846}