summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2025-04-13 19:09:05 -0700
committeriximeow <me@iximeow.net>2025-04-13 19:09:05 -0700
commit2a7d0f4dd1b7ec13fa402cf7c18dc9f62e8c4b55 (patch)
treeed33b45834dfd5e1171218d8cc32edc616049b3e
parent2941adf64a78dbcc67b541c79c94144e7da0fddb (diff)
seeming good
-rw-r--r--Cargo.toml12
-rw-r--r--src/lib.rs163
-rw-r--r--tests/from_brain.rs4
3 files changed, 40 insertions, 139 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8298389..7c1ab3e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 = []
diff --git a/src/lib.rs b/src/lib.rs
index 3b13910..115d78e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)