diff options
| author | iximeow <me@iximeow.net> | 2025-04-13 19:09:05 -0700 | 
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2025-04-13 19:09:05 -0700 | 
| commit | 2a7d0f4dd1b7ec13fa402cf7c18dc9f62e8c4b55 (patch) | |
| tree | ed33b45834dfd5e1171218d8cc32edc616049b3e | |
| parent | 2941adf64a78dbcc67b541c79c94144e7da0fddb (diff) | |
seeming good
| -rw-r--r-- | Cargo.toml | 12 | ||||
| -rw-r--r-- | src/lib.rs | 163 | ||||
| -rw-r--r-- | tests/from_brain.rs | 4 | 
3 files changed, 40 insertions, 139 deletions
| @@ -12,3 +12,15 @@ edition = "2021"  [dependencies]  yaxpeax-arch = { version = "0.3.1", default-features = false, features = [] }  "num-traits" = { version = "0.2", default-features = false } + +[features] +default = ["std", "fmt"] + +# yaxpeax-arch (and this crate) can drop some featuers and be no-std +std = ["alloc", "yaxpeax-arch/std"] + +# some yaxpeax-arch features require only alloc, not std +alloc = ["yaxpeax-arch/alloc"] + +# regardless of no-std, fmt impls are not necessarily required, and so are optional here +fmt = [] @@ -8,12 +8,12 @@  use yaxpeax_arch::{AddressDiff, Arch, Decoder, LengthedInstruction, Reader};  use yaxpeax_arch::StandardDecodeError as DecodeError; +#[cfg(feature = "fmt")] +mod display; +  #[derive(Debug)]  pub struct Hexagon; -// TODO: cfg -mod display; -  impl Arch for Hexagon {      type Word = u8;      /// V73 Section 3.3.7: @@ -74,6 +74,9 @@ macro_rules! decode_operand {      }  } +// some configurations leave some helpers unused. +// this probably should be public for programmatic inspection.. +#[allow(dead_code)]  impl Predicate {      fn reg(num: u8) -> Self {          assert!(num <= 0b11); @@ -286,6 +289,9 @@ enum RoundingMode {  }  impl RoundingMode { +    // if there's no `fmt` we don't need labels, for now. +    // if RoundingMode were inspectable this might be useful even without fmt? +    #[cfg(feature = "fmt")]      fn as_label(&self) -> &'static str {          match self {              RoundingMode::Round => ":rnd", @@ -401,14 +407,6 @@ pub enum Opcode {      #[doc(hidden)]      BUG, -    // V73 Section 10.9 -    // > NOTE: When a constant extender is explicitly specified with a GP-relative load/store, the -    // > processor ignores the value in GP and creates the effective address directly from the 32-bit -    // > constant value. -    // -    // TODO: similar special interpretation of constant extender on 32-bit immediate operands and -    // 32-bit jump/call target addresses. -      Nop,      // V73 page 214 ("Jump to address") @@ -885,7 +883,9 @@ pub enum Opcode {  }  impl Opcode { -    // TODO: move to cfg(fmt) +    // if there's no `fmt` we don't need labels, for now. +    // if RoundingMode were inspectable this might be useful even without fmt? +    #[cfg(feature = "fmt")]      fn cmp_str(&self) -> Option<&'static str> {          match self {              Opcode::JumpEq => { Some("cmp.eq") }, @@ -905,122 +905,6 @@ impl Opcode {      }  } -/// V73 Section 2.2: -/// > the Hexagon processor includes a set of 32-bit control registers that provide access to -/// > processor features such as the program counter, hardware loops, and vector predicates. -/// > -/// > unlike general registers, control registers are used as instruction operands only in the -/// > following cases: -/// > * instructions that require a specific control register as an operand -/// > * register transfer instructions -/// > -/// > NOTE: when a control register is used in a register transfer, the other operand must be a -/// > general register. -/// also V73 Section 2.2: -/// > the control registers have numeric aliases (C0 through C31). -/// -/// while the names are written out first, the numeric form of the register is probably what is -/// used more often... -/// -/// also, the `*LO/*HI` registers seem like they may be used in some circumstances as a pair -/// without the `LO/HI` suffixes, so there may need to be a `ControlRegPair` type too. -// TODO: figure out what of this needs to stick around -#[allow(dead_code)] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -struct ControlReg(u8); - -// TODO: figure out what of this needs to stick around -#[allow(dead_code)] -impl ControlReg { -    /// Loop start address register 0 -    const SA0: ControlReg = ControlReg(0); -    /// Loop count register 0 -    const LC0: ControlReg = ControlReg(1); -    /// Loop start address register 1 -    const SA1: ControlReg = ControlReg(2); -    /// Loop count register 1 -    const LC1: ControlReg = ControlReg(3); -    /// Predicate registers -    const PREDICATES: ControlReg = ControlReg(4); - -    // C5 is unused - -    /// Modifier register 0 -    const M0: ControlReg = ControlReg(6); -    /// Modifier register 1 -    const M1: ControlReg = ControlReg(7); -    /// User status register -    /// -    /// V73 Section 2.2.3: -    /// > USR stores the following status and control values: -    /// > * Cache prefetch enable -    /// > * Cache prefetch status -    /// > * Floating point modes -    /// > * Floating point status -    /// > * Hardware loop configuration -    /// > * Sticky Saturation overflow -    /// > -    /// > NOTE: A user control register transfer to USR cannot be gruoped in an instruction packet -    /// with a Floating point instruction. -    /// > NOTE: When a transfer to USR chagnes the enable trap bits [29:25], an isync instruction -    /// (Section 5.11) must execute before the new exception programming can take effect. -    const USR: ControlReg = ControlReg(8); -    /// Program counter -    const PC: ControlReg = ControlReg(9); -    /// User general pointer -    const UGP: ControlReg = ControlReg(10); -    /// Global pointer -    const GP: ControlReg = ControlReg(11); -    /// Circular start register 0 -    const CS0: ControlReg = ControlReg(12); -    /// Circular start register 1 -    const CS1: ControlReg = ControlReg(13); -    /// Cycle count registers -    /// -    /// according to V5x manual section 1.5, new in V5x -    const UPCYCLELO: ControlReg = ControlReg(14); -    /// Cycle count registers -    /// -    /// according to V5x manual section 1.5, new in V5x -    const UPCYCLEHI: ControlReg = ControlReg(15); -    /// Stack bounds register -    /// -    /// V73 Section 2.2.10: -    /// > The frame limit register (FRAMELIMIT) stores the low address of the memory area reserved -    /// > for the software stack (Section 7.3.1). -    const FRAMELIMIT: ControlReg = ControlReg(16); -    /// Stack smash register -    /// -    /// V73 Section 2.2.11: -    /// > The frame key register (FRAMEKEY) stores the key value that XOR-scrambles return -    /// > addresses when they are stored on the software tack (Section 7.3.2). -    const FRAMEKEY: ControlReg = ControlReg(17); -    /// Packet count registers -    /// -    /// v73 Section 2.2.12: -    /// > The packet count registers (PKTCOUNTLO to PKTCOUNTHI) store a 64-bit value containing the -    /// > current number of instruction packets exceuted since a PKTCOUNT registers was last -    /// > written to. -    const PKTCOUNTLO: ControlReg = ControlReg(18); -    /// Packet count registers -    const PKTCOUNTHI: ControlReg = ControlReg(19); - -    // C20-C29 are reserved - -    /// Qtimer registers -    /// -    /// V73 Section 2.2.13: -    /// > The QTimer registers (UTIMERLO to UTIMERHI) provide access to the QTimer global reference -    /// > count value. They enable Hexagon software to read the 64-bit time value without having to -    /// > perform an expensive advanced high-performance bus (AHB) load. -    /// > ... -    /// > These registers are read only – hardware automatically updates these registers to contain -    /// > the current QTimer value. -    const UTIMERLO: ControlReg = ControlReg(30); -    /// Qtimer registers -    const UTIMERHI: ControlReg = ControlReg(31); -} -  impl Instruction {      fn sources(&self) -> &[Operand] {          &self.sources[..self.sources_count as usize] @@ -1097,6 +981,21 @@ pub enum Operand {      /// way by actual code.      Gpr { reg: u8 },      /// `Cn`, a 32-bit control register `C<reg>` +    /// +    /// V73 Section 2.2: +    /// > the Hexagon processor includes a set of 32-bit control registers that provide access to +    /// > processor features such as the program counter, hardware loops, and vector predicates. +    /// > +    /// > unlike general registers, control registers are used as instruction operands only in the +    /// > following cases: +    /// > * instructions that require a specific control register as an operand +    /// > * register transfer instructions +    /// > +    /// > NOTE: when a control register is used in a register transfer, the other operand must be a +    /// > general register. +    /// +    /// also V73 Section 2.2: +    /// > the control registers have numeric aliases (C0 through C31).      Cr { reg: u8 },      /// `Sn`, a 32-bit supervisor register `S<reg>`      Sr { reg: u8 }, @@ -2940,7 +2839,6 @@ fn decode_instruction<                      let is_call = (inst >> 24) & 1 == 1;                      if is_call { -                        // TODO: extend                          handler.on_opcode_decoded(Opcode::Call)?;                          handler.inst_predicated(uu as u8, negated, false)?;                          opcode_check!(!dotnew); @@ -2963,7 +2861,7 @@ fn decode_instruction<                      return Err(DecodeError::InvalidOpcode);                  }                  _ => { -                    // TODO: exhaustive +                    opcode_check!(false);                  }              }          }, @@ -3155,7 +3053,6 @@ fn decode_instruction<                      // 000 -> swi                      // 001 -> cswi                      // 011 -> ciad -                    // TODO:                      static OPS: [Option<Opcode>; 8] = [                          Some(Opcode::Swi), Some(Opcode::Cswi), None, Some(Opcode::Ciad),                          None, None, None, None, @@ -3166,7 +3063,6 @@ fn decode_instruction<                  0b0100010 => {                      // 000 -> wait                      // 010 -> resume -                    // TODO:                      static OPS: [Option<Opcode>; 8] = [                          Some(Opcode::Wait), None, Some(Opcode::Resume), None,                          None, None, None, None, @@ -3178,7 +3074,6 @@ fn decode_instruction<                      // 000 -> stop                      // 001 -> start                      // 010 -> nmi -                    // TODO:                      static OPS: [Option<Opcode>; 8] = [                          Some(Opcode::Stop), Some(Opcode::Start), Some(Opcode::Nmi), None,                          None, None, None, None, @@ -3187,7 +3082,6 @@ fn decode_instruction<                      handler.on_source_decoded(Operand::gpr(reg_b16(inst)))?;                  }                  0b0100100 => { -                    // TODO: double check encoding and manual                      // 000 -> setimask                      // 011 -> siad                      static OPS: [Option<Opcode>; 8] = [ @@ -3245,7 +3139,6 @@ fn decode_instruction<                  }                  0b1101100 => {                      operand_check!(reg_b0(inst) == 0); -                    // TODO:                      handler.on_opcode_decoded(Opcode::Crswap)?;                      handler.on_source_decoded(Operand::gprpair(reg_b16(inst))?)?;                      handler.on_source_decoded(Operand::srpair(0)?)?; diff --git a/tests/from_brain.rs b/tests/from_brain.rs index 7812247..a72f561 100644 --- a/tests/from_brain.rs +++ b/tests/from_brain.rs @@ -878,7 +878,6 @@ fn inst_0110() {      test_display(&0b0110_0000110_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ p3 = sp2loop0($+0x24, r6) }");      test_display(&0b0110_0000111_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ p3 = sp3loop0($+0x24, r6) }"); -    // TODO: test signed (negative) offsets      test_display(&0b0110_0001001_00110_11_1_000101_00_10110u32.to_le_bytes(), "{ if (r6!=#0) jump:nt $-0x1ad4 }");      test_display(&0b0110_0001001_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6!=#0) jump:t $-0x1ad4 }");      test_display(&0b0110_0001011_00110_11_1_000101_00_10110u32.to_le_bytes(), "{ if (r6>=#0) jump:nt $-0x1ad4 }"); @@ -1263,7 +1262,6 @@ fn inst_1001() {      test_display(&0b1001_0111110_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r17:16 = memd(r2+#14592) }");      test_invalid(&0b1001_0001111_00010_11_0_00100_000_00011u32.to_le_bytes(), DecodeError::InvalidOpcode); -    // TODO: exercise these      test_display(&0b1001_1000001_00010_11_1000_00111_10000u32.to_le_bytes(), "{ r16 = membh(r2++#0xe:circ(m1)) }");      test_display(&0b1001_1000010_00010_11_1000_00111_10000u32.to_le_bytes(), "{ r17:16 = memh_fifo(r2++#0xe:circ(m1)) }");      test_display(&0b1001_1000011_00010_11_1000_00111_10000u32.to_le_bytes(), "{ r16 = memubh(r2++#0xe:circ(m1)) }"); @@ -2462,5 +2460,3 @@ fn inst_1111() {      test_display(&0b1111_1101000_00100_11_1_00011_001_00110u32.to_le_bytes(), "{ if (p1.new) r7:6 = contains(r4, r3) }");  } - -    // TODO: testcase for Rn=add(pc,#nn) | 
