diff options
-rw-r--r-- | src/display.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 45 | ||||
-rw-r--r-- | tests/from_brain.rs | 90 |
3 files changed, 69 insertions, 72 deletions
diff --git a/src/display.rs b/src/display.rs index e730cee..eb19107 100644 --- a/src/display.rs +++ b/src/display.rs @@ -932,7 +932,11 @@ impl fmt::Display for Operand { f.write_str("BUG (operand)") } Operand::PCRel32 { rel } => { - write!(f, "$+#{}", rel) + if *rel >= 0 { + write!(f, "$+{:#x}", *rel) + } else { + write!(f, "$-{:#x}", -*rel) + } } Operand::Gpr { reg } => { const NAMES: [&'static str; 32] = [ @@ -171,11 +171,11 @@ pub struct InstructionPacket { /// between parentheses is recorded as a "source", and operands not in parentheses are recorded as /// "destination". for the simplest instructions, `opcode(operand)` or `opcode(op0, op1, ...)`, /// there will be no destination, and all operands are in `sources`. for an instruction like -/// `R4 = add(R3, R5)`, `R4` is recorded as a destination, with `R3` and `R5` recorded as sources. +/// `r4 = add(r3, r5)`, `r4` is recorded as a destination, with `r3` and `r5` recorded as sources. /// /// an exception to the above are stores, which look something like /// ```text -/// memh(R4 + R2<<3) = R30 +/// memh(r4 + r2<<3) = r30 /// ``` /// in these cases the the operands are an `Operand::RegShiftedReg` describing the operand in /// parentheses, and an `Operand::Gpr` describing the source of the store on the right-hand side. @@ -184,11 +184,11 @@ pub struct InstructionPacket { /// branches set a predicate, while others only compare with a new register value and leave /// predicate registers unaffected. the former look like /// ```text -/// p0 = cmp.gtu(R15, #40); if (!p0.new) jump:t #354 +/// p0 = cmp.gtu(r15, #40); if (!p0.new) jump:t #354 /// ``` /// while the latter look like /// ```text -/// if (cmp.eq(R4.new, R2)) jump:t #812 +/// if (cmp.eq(r4.new, r2)) jump:t #812 /// ``` /// /// in the former case, there are two "destinations", `p0` and `PCRel32` for the jump target. `p0` @@ -238,7 +238,7 @@ pub struct InstructionPacket { /// ```text /// | Symbol | Meaning | /// |--------|---------| -/// | .sN | Bits `[N-1:0]` are treated as an N-bit signed number. For example, R0.s16 means the least significant 16 bits of R0 are treated as a 16-bit signed number. | +/// | .sN | Bits `[N-1:0]` are treated as an N-bit signed number. For example, r0.s16 means the least significant 16 bits of r0 are treated as a 16-bit signed number. | /// | .uN | Bits `[N-1:0]` are treated as an N-bit unsigned number. | /// | .H | The most significant 16 bits of a 32-bit register. | /// | .L | The least significant 16 bits of a 32-bit register. | @@ -1078,22 +1078,15 @@ impl yaxpeax_arch::Instruction for InstructionPacket { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Operand { Nothing, - /* - /// one of the 16 32-bit general purpose registers: `R0 (sp)` through `R15`. - Register { num: u8 }, - /// one of the 16 32-bit general purpose registers, but a smaller part of it. typically - /// sign-extended to 32b for processing. - Subreg { num: u8, width: SizeCode }, - */ PCRel32 { rel: i32 }, - /// `Rn`, a 32-bit register `R<reg>` + /// `rR`, a 32-bit register `r<reg>` /// /// V73 Section 2.1: - /// > thirty-two 32-bit general-purpose registers (named R0 through R31) + /// > thirty-two 32-bit general-purpose registers (named r0 through r31) /// - /// the last three, `R29, R30, R31` are, when possible, shown as `SP, FR, LR`. they are not + /// the last three, `r29, r30, r31` are, when possible, shown as `sp, fr, lr`. they are not /// necessarily required to serve the purposes of stack pointer, frame register, or link /// register. they are, however, described as such by the manual and almost certainly used that /// way by actual code. @@ -1102,18 +1095,18 @@ pub enum Operand { Cr { reg: u8 }, /// `Sn`, a 32-bit supervisor register `S<reg>` Sr { reg: u8 }, - /// `Rn.new`, the version of a 32-bit register `R<reg>` after being written in this instruction + /// `rN.new`, the version of a 32-bit register `R<reg>` after being written in this instruction /// packet. GprNew { reg: u8 }, - /// `Rn.L`, low 16 bits of `R<reg>` + /// `rN.l`, low 16 bits of `r<reg>` GprLow { reg: u8 }, - /// `Rn.H`, high 16 bits of `R<reg>` + /// `rN.h`, high 16 bits of `r<reg>` GprHigh { reg: u8 }, - /// the complex conjugate of `R<reg>`. this is only used in a few instructions performing - /// complex multiplies, and is displayed as `Rn*`. + /// the complex conjugate of `r<reg>`. this is only used in a few instructions performing + /// complex multiplies, and is displayed as `rN*`. GprConjugate { reg: u8 }, - /// `Rn:m`, register pair forming a 64-bit value + /// `rN:M`, register pair forming a 64-bit value /// /// V73 Section 2.1: /// > the general registers can be specified as a pair that represent a single 64-bit register. @@ -1121,18 +1114,18 @@ pub enum Operand { /// > NOTE: the first register in a register pair must always be odd-numbered, and the second must be /// > the next lower register. /// - /// from Table 2-2, note there is an entry of `R31:R30 (LR:FP)` + /// from Table 2-2, note there is an entry of `r31:r30 (lr:fp)` Gpr64b { reg_low: u8 }, - /// `Rn:m*`, a register pair interpreted as a complex number, conjugated + /// `rN:M*`, a register pair interpreted as a complex number, conjugated /// /// this is only used for forms of `cmpy*w` Gpr64bConjugate { reg_low: u8 }, - /// `Cn:m`, control register pair forming a 64-bit location + /// `cN:M`, control register pair forming a 64-bit location Cr64b { reg_low: u8 }, - /// `Sn:m`, control register pair forming a 64-bit location + /// `sN:M`, control register pair forming a 64-bit location Sr64b { reg_low: u8 }, - /// `Pn`, a predicate register + /// `pN`, a predicate register PredicateReg { reg: u8 }, RegOffset { base: u8, offset: u32 }, diff --git a/tests/from_brain.rs b/tests/from_brain.rs index f183fc6..3eb7df1 100644 --- a/tests/from_brain.rs +++ b/tests/from_brain.rs @@ -69,37 +69,37 @@ fn supervisor() { #[test] fn inst_0001() { - test_display(&0b0001_0110000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ r17 = #7; jump $+#8 }"); - test_display(&0b0001_0111000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ r7 = r17; jump $+#8 }"); + test_display(&0b0001_0110000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ r17 = #7; jump $+0x8 }"); + test_display(&0b0001_0111000_01001_11_00_0111_000_00100u32.to_le_bytes(), "{ r7 = r17; jump $+0x8 }"); - test_display(&0b0001_0011011_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ p1 = cmp.gtu(r17, #3); if (!p1.new) jump:nt $+#-508 }"); - test_display(&0b0001_0011101_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ p1 = tstbit(r17, #0x0); if (p1.new) jump:nt $+#-508 }"); - test_display(&0b0001_0011111_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ p1 = tstbit(r17, #0x0); if (!p1.new) jump:nt $+#-508 }"); - test_display(&0b0001_0011111_11001_11_10_0011_000_00010u32.to_le_bytes(), "{ p1 = tstbit(r17, #0x0); if (!p1.new) jump:t $+#-508 }"); + test_display(&0b0001_0011011_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ p1 = cmp.gtu(r17, #3); if (!p1.new) jump:nt $-0x1fc }"); + test_display(&0b0001_0011101_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ p1 = tstbit(r17, #0x0); if (p1.new) jump:nt $-0x1fc }"); + test_display(&0b0001_0011111_11001_11_00_0011_000_00010u32.to_le_bytes(), "{ p1 = tstbit(r17, #0x0); if (!p1.new) jump:nt $-0x1fc }"); + test_display(&0b0001_0011111_11001_11_10_0011_000_00010u32.to_le_bytes(), "{ p1 = tstbit(r17, #0x0); if (!p1.new) jump:t $-0x1fc }"); - test_display(&0b0001_0101011_11001_11_10_0111_000_00010u32.to_le_bytes(), "{ p0 = cmp.gtu(r17, r7); if (!p0.new) jump:t $+#-508 }"); - test_display(&0b0001_0101011_11001_11_11_0111_000_00010u32.to_le_bytes(), "{ p1 = cmp.gtu(r17, r7); if (!p1.new) jump:t $+#-508 }"); + test_display(&0b0001_0101011_11001_11_10_0111_000_00010u32.to_le_bytes(), "{ p0 = cmp.gtu(r17, r7); if (!p0.new) jump:t $-0x1fc }"); + test_display(&0b0001_0101011_11001_11_11_0111_000_00010u32.to_le_bytes(), "{ p1 = cmp.gtu(r17, r7); if (!p1.new) jump:t $-0x1fc }"); } #[test] fn inst_0010() { - test_display(&0b0010_00000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(r4.new, r2)) jump:nt $+#812 }"); - test_display(&0b0010_00000001_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(r4.new, r2)) jump:t $+#812 }"); - test_display(&0b0010_00000101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.eq(r4.new, r2)) jump:nt $+#812 }"); - test_display(&0b0010_00001001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(r4.new, r2)) jump:nt $+#812 }"); - test_display(&0b0010_00001101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(r4.new, r2)) jump:nt $+#812 }"); - test_display(&0b0010_00010001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gtu(r4.new, r2)) jump:nt $+#812 }"); - test_display(&0b0010_00010101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(r4.new, r2)) jump:nt $+#812 }"); - test_display(&0b0010_00011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(r2, r4.new)) jump:nt $+#812 }"); - test_display(&0b0010_00100101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(r2, r4.new)) jump:nt $+#812 }"); + test_display(&0b0010_00000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(r4.new, r2)) jump:nt $+0x32c }"); + test_display(&0b0010_00000001_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(r4.new, r2)) jump:t $+0x32c }"); + test_display(&0b0010_00000101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.eq(r4.new, r2)) jump:nt $+0x32c }"); + test_display(&0b0010_00001001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(r4.new, r2)) jump:nt $+0x32c }"); + test_display(&0b0010_00001101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(r4.new, r2)) jump:nt $+0x32c }"); + test_display(&0b0010_00010001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gtu(r4.new, r2)) jump:nt $+0x32c }"); + test_display(&0b0010_00010101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(r4.new, r2)) jump:nt $+0x32c }"); + test_display(&0b0010_00011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.gt(r2, r4.new)) jump:nt $+0x32c }"); + test_display(&0b0010_00100101_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(r2, r4.new)) jump:nt $+0x32c }"); test_invalid(&0b0010_00101101_0100_11_0_00010_100_10110u32.to_le_bytes(), DecodeError::InvalidOpcode); test_invalid(&0b0010_00110101_0100_11_0_00010_100_10110u32.to_le_bytes(), DecodeError::InvalidOpcode); test_invalid(&0b0010_00111101_0100_11_0_00010_100_10110u32.to_le_bytes(), DecodeError::InvalidOpcode); - test_display(&0b0010_01000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(r4.new, #2)) jump:nt $+#812 }"); - test_display(&0b0010_01010101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(r4.new, #2)) jump:t $+#812 }"); - test_display(&0b0010_01011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (tstbit(r4.new, #0)) jump:nt $+#812 }"); - test_display(&0b0010_01011101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!tstbit(r4.new, #0)) jump:t $+#812 }"); - test_display(&0b0010_01101101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(r4.new, #-1)) jump:t $+#812 }"); + test_display(&0b0010_01000001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (cmp.eq(r4.new, #2)) jump:nt $+0x32c }"); + test_display(&0b0010_01010101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gtu(r4.new, #2)) jump:t $+0x32c }"); + test_display(&0b0010_01011001_0100_11_0_00010_100_10110u32.to_le_bytes(), "{ if (tstbit(r4.new, #0)) jump:nt $+0x32c }"); + test_display(&0b0010_01011101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!tstbit(r4.new, #0)) jump:t $+0x32c }"); + test_display(&0b0010_01101101_0100_11_1_00010_100_10110u32.to_le_bytes(), "{ if (!cmp.gt(r4.new, #-1)) jump:t $+0x32c }"); } #[test] @@ -298,34 +298,34 @@ fn inst_0101() { test_display(&0b0101_011_1110_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ isync }"); test_display(&0b0101_011_1111_00000_11_0_10000_000_00010u32.to_le_bytes(), "{ unpause }"); - test_display(&0b0101_100_0000_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ jump $+#4 }"); - test_display(&0b0101_101_0000_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ call $+#4 }"); + test_display(&0b0101_100_0000_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ jump $+0x4 }"); + test_display(&0b0101_101_0000_00000_11_0_00000_000_00010u32.to_le_bytes(), "{ call $+0x4 }"); - test_display(&0b0101_110_0000_00000_11_0_00001_000_00010u32.to_le_bytes(), "{ if (p1) jump:nt $+#4 }"); - test_display(&0b0101_110_0001_00000_11_0_01001_000_00010u32.to_le_bytes(), "{ if (!p1.new) jump:nt $+#4 }"); - test_display(&0b0101_110_1001_00000_11_0_00001_000_00010u32.to_le_bytes(), "{ if (!p1) call $+#4 }"); + test_display(&0b0101_110_0000_00000_11_0_00001_000_00010u32.to_le_bytes(), "{ if (p1) jump:nt $+0x4 }"); + test_display(&0b0101_110_0001_00000_11_0_01001_000_00010u32.to_le_bytes(), "{ if (!p1.new) jump:nt $+0x4 }"); + test_display(&0b0101_110_1001_00000_11_0_00001_000_00010u32.to_le_bytes(), "{ if (!p1) call $+0x4 }"); test_invalid(&0b0101_110_1001_00000_11_0_01001_000_00010u32.to_le_bytes(), DecodeError::InvalidOpcode); } #[test] fn inst_0110() { - test_display(&0b0110_0000000_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ loop0($+#36, r6) }"); - test_display(&0b0110_0000001_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ loop1($+#36, r6) }"); + test_display(&0b0110_0000000_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ loop0($+0x24, r6) }"); + test_display(&0b0110_0000001_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ loop1($+0x24, r6) }"); test_invalid(&0b0110_0000010_00110_11_0_00010_000_01000u32.to_le_bytes(), DecodeError::InvalidOpcode); test_invalid(&0b0110_0000011_00110_11_0_00010_000_01000u32.to_le_bytes(), DecodeError::InvalidOpcode); - test_display(&0b0110_0000101_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ p3 = sp1loop0($+#36, r6) }"); - test_display(&0b0110_0000110_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ p3 = sp2loop0($+#36, r6) }"); - test_display(&0b0110_0000111_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ p3 = sp3loop0($+#36, r6) }"); + test_display(&0b0110_0000101_00110_11_0_00010_000_01000u32.to_le_bytes(), "{ p3 = sp1loop0($+0x24, r6) }"); + 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 $+#-6868 }"); - test_display(&0b0110_0001001_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6!=#0) jump:t $+#-6868 }"); - test_display(&0b0110_0001011_00110_11_1_000101_00_10110u32.to_le_bytes(), "{ if (r6>=#0) jump:nt $+#-6868 }"); - test_display(&0b0110_0001011_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6>=#0) jump:t $+#-6868 }"); - test_display(&0b0110_0001101_00110_11_1_000101_00_10110u32.to_le_bytes(), "{ if (r6==#0) jump:nt $+#-6868 }"); - test_display(&0b0110_0001101_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6==#0) jump:t $+#-6868 }"); - test_display(&0b0110_0001111_00110_11_1_000101_00_10110u32.to_le_bytes(), "{ if (r6<=#0) jump:nt $+#-6868 }"); - test_display(&0b0110_0001111_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6<=#0) jump:t $+#-6868 }"); + 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 }"); + test_display(&0b0110_0001011_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6>=#0) jump:t $-0x1ad4 }"); + test_display(&0b0110_0001101_00110_11_1_000101_00_10110u32.to_le_bytes(), "{ if (r6==#0) jump:nt $-0x1ad4 }"); + test_display(&0b0110_0001101_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6==#0) jump:t $-0x1ad4 }"); + test_display(&0b0110_0001111_00110_11_1_000101_00_10110u32.to_le_bytes(), "{ if (r6<=#0) jump:nt $-0x1ad4 }"); + test_display(&0b0110_0001111_00110_11_1_100101_00_10110u32.to_le_bytes(), "{ if (r6<=#0) jump:t $-0x1ad4 }"); test_display(&0b0110_0010001_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ c22 = r6 }"); test_display(&0b0110_0010010_00110_11_0_00000_000_01000u32.to_le_bytes(), "{ trace(r6) }"); @@ -337,11 +337,11 @@ fn inst_0110() { test_display(&0b0110_1000000_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ r23:22 = c7:6 }"); - test_display(&0b0110_1001000_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ loop0($+#36, #0xd2) }"); - test_display(&0b0110_1001001_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ loop1($+#36, #0xd2) }"); - test_display(&0b0110_1001101_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ p3 = sp1loop0($+#36, #0xd2) }"); - test_display(&0b0110_1001110_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ p3 = sp2loop0($+#36, #0xd2) }"); - test_display(&0b0110_1001111_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ p3 = sp3loop0($+#36, #0xd2) }"); + test_display(&0b0110_1001000_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ loop0($+0x24, #0xd2) }"); + test_display(&0b0110_1001001_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ loop1($+0x24, #0xd2) }"); + test_display(&0b0110_1001101_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ p3 = sp1loop0($+0x24, #0xd2) }"); + test_display(&0b0110_1001110_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ p3 = sp2loop0($+0x24, #0xd2) }"); + test_display(&0b0110_1001111_00110_11_0_000101_00_01010u32.to_le_bytes(), "{ p3 = sp3loop0($+0x24, #0xd2) }"); test_display(&0b0110_1010000_00110_11_0_000101_00_10110u32.to_le_bytes(), "{ r22 = m0 }"); test_display(&0b0110_1010010_01001_11_0_000101_00_10110u32.to_le_bytes(), "{ r22 = add(pc, #0x5) }"); |