summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2025-04-12 19:08:06 -0700
committeriximeow <me@iximeow.net>2025-04-12 19:08:06 -0700
commitdc94ca8e9fb134f19e12977503a2b776d0864cb6 (patch)
treefcdc39d4c43a86bb3528a94ebc25f381df1187c5
parent7d077f45b7e4a9776ac516da36381861e0d7e7bd (diff)
initial list of extendable instructions, fix memub/memuh decode errors
-rw-r--r--src/lib.rs183
-rw-r--r--tests/from_brain.rs222
2 files changed, 347 insertions, 58 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 4e2f935..9010218 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1255,16 +1255,31 @@ impl Operand {
Self::ImmU32 { imm: num }
}
- fn immext(
- i: u32, extender: &mut Option<u32>, unextended: impl FnOnce(u32) -> Self
+ fn with_extension(
+ i: u32, extender: &mut Option<u32>,
+ extended: impl FnOnce(u32) -> Result<Self, yaxpeax_arch::StandardDecodeError>,
+ no_extender: impl FnOnce(u32) -> Result<Self, yaxpeax_arch::StandardDecodeError>,
) -> Result<Self, yaxpeax_arch::StandardDecodeError> {
if let Some(extender) = extender.take() {
+ eprintln!("i : {:04x}", i);
+ eprintln!("extender: {:04x}", extender);
operand_check!(i & !0x3f == 0);
- Ok(Self::Immext { imm: i | (extender << 6) })
+ extended(i | (extender << 6))
} else {
- Ok(unextended(i))
+ no_extender(i)
}
}
+
+ fn immext(
+ i: u32, extender: &mut Option<u32>,
+ unextended: impl FnOnce(u32) -> Result<Self, yaxpeax_arch::StandardDecodeError>,
+ ) -> Result<Self, yaxpeax_arch::StandardDecodeError> {
+ Self::with_extension(
+ i, extender,
+ |imm| Ok(Self::Immext { imm }),
+ unextended
+ )
+ }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -1544,7 +1559,10 @@ fn reg_b16(inst: u32) -> u8 { ((inst >> 16) & 0b11111) as u8 }
fn decode_store_ops<
T: Reader<<Hexagon as Arch>::Address, <Hexagon as Arch>::Word>,
H: DecodeHandler<T>,
->(handler: &mut H, opbits: u8, srcreg: u8, dest_op: impl Fn(u8) -> Operand) -> Result<(), DecodeError> {
+>(
+ handler: &mut H, opbits: u8, srcreg: u8,
+ dest_op: impl FnOnce(u8) -> Result<Operand, DecodeError>
+) -> Result<(), DecodeError> {
if opbits == 0b101 {
handler.on_source_decoded(Operand::gpr_new(srcreg & 0b111))?;
let opbits = (srcreg >> 3) & 0b11;
@@ -1553,33 +1571,33 @@ fn decode_store_ops<
Some(Opcode::StoreMemw), None,
];
handler.on_opcode_decoded(decode_opcode!(OPS[opbits as usize]))?;
- handler.on_dest_decoded(dest_op(opbits))?;
+ handler.on_dest_decoded(dest_op(opbits)?)?;
} else {
match opbits {
0b000 => {
handler.on_opcode_decoded(Opcode::StoreMemb)?;
handler.on_source_decoded(Operand::gpr(srcreg))?;
- handler.on_dest_decoded(dest_op(0))?;
+ handler.on_dest_decoded(dest_op(0)?)?;
}
0b010 => {
handler.on_opcode_decoded(Opcode::StoreMemh)?;
handler.on_source_decoded(Operand::gpr(srcreg))?;
- handler.on_dest_decoded(dest_op(1))?;
+ handler.on_dest_decoded(dest_op(1)?)?;
}
0b011 => {
handler.on_opcode_decoded(Opcode::StoreMemh)?;
handler.on_source_decoded(Operand::gpr_high(srcreg))?;
- handler.on_dest_decoded(dest_op(1))?;
+ handler.on_dest_decoded(dest_op(1)?)?;
}
0b100 => {
handler.on_opcode_decoded(Opcode::StoreMemw)?;
handler.on_source_decoded(Operand::gpr(srcreg))?;
- handler.on_dest_decoded(dest_op(2))?;
+ handler.on_dest_decoded(dest_op(2)?)?;
}
0b110 => {
handler.on_opcode_decoded(Opcode::StoreMemd)?;
handler.on_source_decoded(Operand::gprpair(srcreg)?)?;
- handler.on_dest_decoded(dest_op(3))?;
+ handler.on_dest_decoded(dest_op(3)?)?;
}
_ => {
return Err(DecodeError::InvalidOpcode);
@@ -1594,39 +1612,42 @@ fn decode_store_ops<
fn decode_load_ops<
T: Reader<<Hexagon as Arch>::Address, <Hexagon as Arch>::Word>,
H: DecodeHandler<T>,
->(handler: &mut H, opbits: u8, dstreg: u8, src_op: impl Fn(u8) -> Operand) -> Result<(), DecodeError> {
+>(
+ handler: &mut H, opbits: u8, dstreg: u8,
+ src_op: impl FnOnce(u8) -> Result<Operand, DecodeError>
+) -> Result<(), DecodeError> {
// for loads, there's none of the carveout for `R<n>.new` or high register halves.
// just mem{b,ub,h,uh,w,d}
match opbits {
0b000 => {
handler.on_opcode_decoded(Opcode::LoadMemb)?;
handler.on_dest_decoded(Operand::gpr(dstreg))?;
- handler.on_source_decoded(src_op(0))?;
+ handler.on_source_decoded(src_op(0)?)?;
}
0b001 => {
handler.on_opcode_decoded(Opcode::LoadMemub)?;
handler.on_dest_decoded(Operand::gpr(dstreg))?;
- handler.on_source_decoded(src_op(0))?;
+ handler.on_source_decoded(src_op(0)?)?;
}
0b010 => {
handler.on_opcode_decoded(Opcode::LoadMemh)?;
handler.on_dest_decoded(Operand::gpr(dstreg))?;
- handler.on_source_decoded(src_op(1))?;
+ handler.on_source_decoded(src_op(1)?)?;
}
0b011 => {
handler.on_opcode_decoded(Opcode::LoadMemuh)?;
handler.on_dest_decoded(Operand::gpr(dstreg))?;
- handler.on_source_decoded(src_op(1))?;
+ handler.on_source_decoded(src_op(1)?)?;
}
0b100 => {
handler.on_opcode_decoded(Opcode::LoadMemw)?;
handler.on_dest_decoded(Operand::gpr(dstreg))?;
- handler.on_source_decoded(src_op(2))?;
+ handler.on_source_decoded(src_op(2)?)?;
}
0b110 => {
handler.on_opcode_decoded(Opcode::LoadMemd)?;
handler.on_dest_decoded(Operand::gprpair(dstreg)?)?;
- handler.on_source_decoded(src_op(3))?;
+ handler.on_source_decoded(src_op(3)?)?;
}
_ => {
return Err(DecodeError::InvalidOpcode);
@@ -1715,10 +1736,16 @@ fn decode_packet<
}
}
+ if extender.is_some() {
+ eprintln!("extended: {:08x}", inst);
+ }
+
let iclass = (inst >> 28) & 0b1111;
if iclass == 0b0000 {
- extender = Some((inst & 0x3fff) | ((inst >> 2) & 0xfff));
+ eprintln!("instruction: {:08x}", inst);
+ extender = Some((inst & 0x3fff) | ((inst >> 2) & 0x3ffc000));
+ eprintln!("extender: {:08x}", extender.unwrap());
} else {
handler.start_instruction();
decode_instruction(decoder, handler, inst, &mut extender)?;
@@ -1731,7 +1758,9 @@ fn decode_packet<
// > normally ensures that only valid constant extenders are generated.
//
// the extender must extend the instruction word that follows it.
- opcode_check!(extender.is_none());
+ if extender.is_some() {
+ eprintln!("unconsumed extender: {:x}", extender.unwrap());
+ }
handler.end_instruction();
}
@@ -2095,7 +2124,7 @@ fn decode_instruction<
handler.on_opcode_decoded(decode_opcode!(OPCODES[min_op as usize]))?;
handler.on_source_decoded(Operand::RegShiftedReg { base: sssss, index: reg_mid, shift: ii })?;
- if min_op == 0b001 || min_op == 0b011 || min_op == 0b110 {
+ if min_op == 0b110 {
handler.on_dest_decoded(Operand::gprpair(reg_low)?)?;
} else {
handler.on_dest_decoded(Operand::gpr(reg_low))?;
@@ -2223,10 +2252,17 @@ fn decode_instruction<
let iiiiii = i | i5;
decode_store_ops(handler, opbits as u8, srcreg as u8, |shamt| {
- Operand::RegOffset {
- base: sssss as u8,
- offset: iiiiii << shamt
- }
+ Operand::with_extension(
+ iiiiii << shamt, extender,
+ |offset| Ok(Operand::RegOffset {
+ base: sssss as u8,
+ offset,
+ }),
+ |offset| Ok(Operand::RegOffset {
+ base: sssss as u8,
+ offset,
+ }),
+ )
})?;
} else {
let pred_reg = (inst >> 11) & 0b11;
@@ -2236,10 +2272,17 @@ fn decode_instruction<
let iiiiii = (inst >> 5) & 0b111111;
decode_load_ops(handler, opbits as u8, dstreg as u8, |shamt| {
- Operand::RegOffset {
- base: sssss as u8,
- offset: iiiiii << shamt
- }
+ Operand::with_extension(
+ iiiiii << shamt, extender,
+ |offset| Ok(Operand::RegOffset {
+ base: sssss as u8,
+ offset,
+ }),
+ |offset| Ok(Operand::RegOffset {
+ base: sssss as u8,
+ offset,
+ }),
+ )
})?;
}
} else {
@@ -2255,9 +2298,13 @@ fn decode_instruction<
let ttttt = reg_b8(inst);
decode_store_ops(handler, opbits, ttttt, |shamt| {
- Operand::GpOffset {
- offset: i << shamt
- }
+ Operand::with_extension(
+ i << shamt, extender,
+ |imm| Ok(Operand::Immext { imm }),
+ |offset| Ok(Operand::GpOffset {
+ offset,
+ }),
+ )
})?;
} else {
let i_lo = (inst >> 5) & 0b1111_1111;
@@ -2265,7 +2312,13 @@ fn decode_instruction<
let ddddd = reg_b0(inst);
decode_load_ops(handler, opbits, ddddd, |shamt| {
- Operand::GpOffset { offset: i << shamt }
+ Operand::with_extension(
+ i << shamt, extender,
+ |imm| Ok(Operand::Immext { imm }),
+ |offset| Ok(Operand::GpOffset {
+ offset,
+ }),
+ )
})?;
}
}
@@ -3118,7 +3171,7 @@ fn decode_instruction<
handler.on_source_decoded(
Operand::immext(
i as u32, extender,
- |i: u32| Operand::imm_i8(i as i8)
+ |i: u32| Ok(Operand::imm_i8(i as i8))
)?
)?;
handler.on_source_decoded(Operand::gpr(sssss))?;
@@ -3127,7 +3180,7 @@ fn decode_instruction<
handler.on_source_decoded(
Operand::immext(
i as u32, extender,
- |i: u32| Operand::imm_i8(i as i8)
+ |i: u32| Ok(Operand::imm_i8(i as i8))
)?
)?;
}
@@ -3221,11 +3274,21 @@ fn decode_instruction<
handler.on_opcode_decoded(opcode)?;
handler.on_dest_decoded(Operand::gpr(ddddd))?;
if opcode == Opcode::Sub {
- handler.on_source_decoded(Operand::imm_i16(i))?;
+ handler.on_source_decoded(
+ Operand::immext(
+ i as u32, extender,
+ |i: u32| Ok(Operand::imm_i16(i as i16))
+ )?
+ )?;
handler.on_source_decoded(Operand::gpr(sssss))?;
} else {
handler.on_source_decoded(Operand::gpr(sssss))?;
- handler.on_source_decoded(Operand::imm_i16(i))?;
+ handler.on_source_decoded(
+ Operand::immext(
+ i as u32, extender,
+ |i: u32| Ok(Operand::imm_i16(i as i16))
+ )?
+ )?;
}
}
0b0111 => {
@@ -3241,7 +3304,12 @@ fn decode_instruction<
handler.on_opcode_decoded(Opcode::TransferImmediate)?;
handler.on_dest_decoded(Operand::gpr(ddddd))?;
- handler.on_source_decoded(Operand::imm_i16(i))?;
+ handler.on_source_decoded(
+ Operand::immext(
+ i as u32, extender,
+ |i: u32| Ok(Operand::imm_i16(i as i16))
+ )?
+ )?;
}
0b1010 |
0b1011 => {
@@ -4333,11 +4401,22 @@ fn decode_instruction<
static OPCODES: [Option<(Opcode, bool, u8)>; 16] = [
None, Some((Membh, false, 0x01)), Some((MemhFifo, true, 0x01)), Some((Memubh, false, 0x01)),
Some((MembFifo, true, 0x00)), Some((Memubh, true, 0x02)), None, Some((Membh, true, 0x02)),
- Some((Memb, false, 0x03)), Some((Memub, true, 0x03)), Some((Memh, false, 0x03)), Some((Memuh, true, 0x03)),
- Some((Memw, false, 0x03)), None, Some((Memd, true, 0x03)), None,
+ Some((Memb, false, 0x00)), Some((Memub, false, 0x00)), Some((Memh, false, 0x01)), Some((Memuh, false, 0x01)),
+ Some((Memw, false, 0x02)), None, Some((Memd, true, 0x03)), None,
];
- let (op, wide, samt) = decode_opcode!(OPCODES[op as usize]);
- handler.on_source_decoded(Operand::RegOffset { base: sssss, offset: (i as u32) << samt })?;
+ let (op, wide, shamt) = decode_opcode!(OPCODES[op as usize]);
+ // the manuals through V79 do not describe constant extenders being applicable
+ // to memb_fifo or meubh or ... but it turns out each of these opcodes include
+ // an `apply_extension()` clause in their behavior, so this seems like a
+ // deficiency in the manual rather. assume this operand can be extended for all
+ // opcodes.
+ handler.on_source_decoded(
+ Operand::with_extension(
+ (i as u32) << shamt, extender,
+ |offset| Ok(Operand::RegOffset { base: sssss, offset }),
+ |offset| Ok(Operand::RegOffset { base: sssss, offset }),
+ )?
+ )?;
if !wide {
handler.on_dest_decoded(Operand::gpr(ddddd))?;
} else {
@@ -4465,7 +4544,7 @@ fn decode_instruction<
let i11 = i_low | (i_mid << 8) | (i_high << 9);
decode_store_ops(handler, opc, ttttt, |shamt| {
- Operand::RegOffset { base: sssss, offset: i11 << shamt }
+ Ok(Operand::RegOffset { base: sssss, offset: i11 << shamt })
})?;
}
@@ -4498,11 +4577,11 @@ fn decode_instruction<
if (inst >> 1) & 1 == 0 {
let iiii = (inst >> 3) & 0b1111;
decode_store_ops(handler, minbits, ttttt, |shamt| {
- Operand::RegOffsetCirc { base: xxxxx, offset: iiii << shamt, mu: u }
+ Ok(Operand::RegOffsetCirc { base: xxxxx, offset: iiii << shamt, mu: u })
})?;
} else {
decode_store_ops(handler, minbits, ttttt, |_shamt| {
- Operand::RegCirc { base: xxxxx, mu: u }
+ Ok(Operand::RegCirc { base: xxxxx, mu: u })
})?;
}
}
@@ -4518,12 +4597,12 @@ fn decode_instruction<
opcode_check!(inst & 2 == 0);
let iiii = ((inst >> 3) & 0b1111) as u32;
decode_store_ops(handler, minbits, ttttt, |shamt| {
- Operand::RegOffset { base: xxxxx, offset: iiii << shamt }
+ Ok(Operand::RegOffset { base: xxxxx, offset: iiii << shamt })
})?;
} else {
let uuuuuu = (inst & 0b111111) as u16;
decode_store_ops(handler, minbits, ttttt, |_shamt| {
- Operand::RegStoreAssign { base: xxxxx, addr: uuuuuu }
+ Ok(Operand::RegStoreAssign { base: xxxxx, addr: uuuuuu })
})?;
}
} else {
@@ -4534,7 +4613,7 @@ fn decode_instruction<
let iiii = (inst >> 3) & 0b1111;
let dotnew = (inst >> 7) & 1 == 1;
decode_store_ops(handler, minbits, ttttt, |shamt| {
- Operand::RegOffset { base: xxxxx, offset: iiii << shamt }
+ Ok(Operand::RegOffset { base: xxxxx, offset: iiii << shamt })
})?;
handler.inst_predicated(vv as u8, negated, dotnew)?;
}
@@ -4548,7 +4627,7 @@ fn decode_instruction<
if (inst >> 7) & 1 == 0 {
let u = (inst >> 13) & 1;
decode_store_ops(handler, minbits, ttttt, |_shamt| {
- Operand::RegMemIndexed { base: xxxxx, mu: u as u8 }
+ Ok(Operand::RegMemIndexed { base: xxxxx, mu: u as u8 })
})?;
} else {
let i_hi = (inst >> 13) & 1;
@@ -4556,7 +4635,7 @@ fn decode_instruction<
let i = ((i_hi << 1) | i_lo) as u8;
let uuuuuu = (inst & 0b111111) as i16;
decode_store_ops(handler, minbits, ttttt, |_shamt| {
- Operand::RegShiftOffset { base: xxxxx, shift: i, offset: uuuuuu }
+ Ok(Operand::RegShiftOffset { base: xxxxx, shift: i, offset: uuuuuu })
})?;
}
},
@@ -4569,7 +4648,7 @@ fn decode_instruction<
if (inst >> 7) & 1 == 0 {
let u = (inst >> 13) & 1;
decode_store_ops(handler, minbits, ttttt, |_shamt| {
- Operand::RegMemIndexedBrev { base: xxxxx, mu: u as u8 }
+ Ok(Operand::RegMemIndexedBrev { base: xxxxx, mu: u as u8 })
})?;
} else {
// 1010|1111...|.....|PP|1...
@@ -4581,7 +4660,7 @@ fn decode_instruction<
let i6 = ((ii_high << 4) | iiii) as i8;
let dotnew = (inst >> 13) & 1 == 1;
decode_store_ops(handler, minbits, ttttt, |_shamt| {
- Operand::Absolute { addr: i6 }
+ Ok(Operand::Absolute { addr: i6 })
})?;
handler.inst_predicated(vv as u8, negated, dotnew)?;
}
diff --git a/tests/from_brain.rs b/tests/from_brain.rs
index 072e39d..03751c5 100644
--- a/tests/from_brain.rs
+++ b/tests/from_brain.rs
@@ -18,6 +18,216 @@ fn test_invalid(bytes: &[u8], expected: DecodeError) {
assert_eq!(err, expected);
}
+// mix of seen-in-the-wild extenders and stuff i made up
+#[test]
+fn extenders() {
+ /*
+ * extendable instructions covered in this test:
+ * // it turns out these are encoded by applying an extender to the `mem{b,ub,...}(gp+#u16)` forms
+ * [ ]: Rd = mem{b,ub,h,uh,w,d}(##U32)
+ * // predicated loads
+ * [ ]: if ([!]Pt[.new]) Rd = mem{b,ub,h,uh,w,d} (Rs + ##U32)
+ * [ ]: Rd = mem{b,ub,h,uh,w,d} (Rs + ##U32)
+ * [ ]: Rd = mem{b,ub,h,uh,w,d} (Re=##U32)
+ * [ ]: Rd = mem{b,ub,h,uh,w,d} (Rt<<#u2 + ##U32)
+ * [ ]: if ([!]Pt[.new]) Rd = mem{b,ub,h,uh,w,d} (##U32)
+ * // it turns out these are encoded by applying an extender to the `mem{b,ub,...}(gp+#u16)` forms
+ * [ ]: mem{b,h,w,d}(##U32) = Rs[.new]
+ * // predicated stores
+ * [ ]: if ([!]Pt[.new]) mem{b,h,w,d}(Rs + ##U32) = Rt[.new]
+ * [ ]: mem{b,h,w,d}(Rs + ##U32) = Rt[.new]
+ * [ ]: mem{b,h,w,d}(Rd=##U32) = Rt[.new]
+ * [ ]: mem{b,h,w,d}(Ru<<#u2 + ##U32) = Rt[.new]
+ * [ ]: if ([!]Pt[.new]) mem{b,h,w,d}(##U32) = Rt[.new]
+ * [ ]: [if [!]Ps] memw(Rs + #u6) = ##U32 // constant store
+ * [ ]: memw(Rs + Rt<<#u2) = ##U32 // constant store
+ * [ ]: if (cmp.xx(Rs.new,##U32)) jump:hint target
+ * [ ]: Rd = ##u32
+ * [ ]: Rdd = combine(Rs,##u32)
+ * [ ]: Rdd = combine(##u32,Rs)
+ * [ ]: Rdd = combine(##u32,#s8)
+ * [ ]: Rdd = combine(#s8,##u32)
+ * [ ]: Rd = mux(Pu, Rs,##u32)
+ * [ ]: Rd = mux(Pu, ##u32, Rs)
+ * [ ]: Rd = mux(Pu,##u32,#s8)
+ * [ ]: if ([!]Pu[.new]) Rd = add(Rs,##u32)
+ * [ ]: if ([!]Pu[.new]) Rd = ##u32
+ * [ ]: Pd = [!]cmp.eq (Rs,##u32)
+ * [ ]: Pd = [!]cmp.gt (Rs,##u32)
+ * [ ]: Pd = [!]cmp.gtu (Rs,##u32)
+ * [ ]: Rd = [!]cmp.eq(Rs,##u32)
+ * [ ]: Rd = and(Rs,##u32)
+ * [ ]: Rd = or(Rs,##u32)
+ * [ ]: Rd = sub(##u32,Rs)
+ * [ ]: Rd = add(Rs,##s32)
+ * [ ]: Rd = mpyi(Rs,##u32)
+ * [ ]: Rd += mpyi(Rs,##u32)
+ * [ ]: Rd -= mpyi(Rs,##u32)
+ * [ ]: Rx += add(Rs,##u32)
+ * [ ]: Rx -= add(Rs,##u32)
+ * [x]: Rd = ##u32
+ * [ ]: Rd = add(Rs,##s32)
+ * [ ]: jump (PC + ##s32)
+ * [ ]: call (PC + ##s32)
+ * [ ]: if ([!]Pu) call (PC + ##s32)
+ * [ ]: Pd = spNloop0(PC+##s32,Rs/#U10)
+ * [ ]: loop0/1 (PC+##s32,#Rs/#U10)
+ * [ ]: Rd = add(pc,##s32)
+ * [ ]: Rd = add(##u32,mpyi(Rs,#u6))
+ * [ ]: Rd = add(##u32,mpyi(Rs,Rt))
+ * [ ]: Rd = add(Rs,add(Rt,##u32))
+ * [x]: Rd = add(Rs,sub(##u32,Rt))
+ * [x]: Rd = sub(##u32,add(Rs,Rt))
+ * [x]: Rd = or(Rs,and(Rt,##u32))
+ * [ ]: Rx = add/sub/and/or (##u32,asl/asr/lsr(Rx,#U5))
+ * [ ]: Rx = add/sub/and/or (##u32,asl/asr/lsr(Rs,Rx))
+ * [ ]: Rx = add/sub/and/or (##u32,asl/asr/lsr(Rx,Rs))
+ * [ ]: Pd = cmpb/h.{eq,gt,gtu} (Rs,##u32)
+ */
+
+ // HELP! not sure how extenders combined with shifted constants should work.
+ // for example: `Rdd=memd(gp+#u16:3)` can be extended. if u16 is `0...1111`, where the low
+ // three bits are all set, the address used for gp-relative addressing would be `1111000`. as
+ // an extended constant, would this also be `1111000` and illegal for extension as bits other
+ // than the low six are set? or is this `000111` with an unaligned address but otherwise
+ // extended and legal for execution?
+ //
+ // i am going to assume that the shifted immediate is used.
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xc6, 0x00, 0x48,
+ ], "{ memb(##0x20f) = r6 }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xc6, 0x40, 0x48,
+ ], "{ memh(##0x21e) = r6 }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xc6, 0x80, 0x48,
+ ], "{ memw(##0x23c) = r6 }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xc6, 0xa0, 0x48,
+ ], "{ memb(##0x20f) = r6.new }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xce, 0xa0, 0x48,
+ ], "{ memh(##0x21e) = r6.new }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xd6, 0xa0, 0x48,
+ ], "{ memw(##0x23c) = r6.new }");
+ // the immediate of 1111 << 3 shifts a 1 out of the low 6 bits and invalidates the operand.
+ test_invalid(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xc6, 0xc0, 0x48,
+ ], DecodeError::InvalidOperand);
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x07, 0xc6, 0xc0, 0x48,
+ ], "{ memd(##0x238) = r7:6 }");
+
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x00, 0x49,
+ ], "{ r6 = memb(##0x20f) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x20, 0x49,
+ ], "{ r6 = memub(##0x20f) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x40, 0x49,
+ ], "{ r6 = memh(##0x21e) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x60, 0x49,
+ ], "{ r6 = memuh(##0x21e) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x80, 0x49,
+ ], "{ r6 = memw(##0x23c) }");
+ // the immediate of 1111 << 3 shifts a 1 out of the low 6 bits and invalidates the operand.
+ test_invalid(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0xc0, 0x49,
+ ], DecodeError::InvalidOperand);
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc0, 0xc0, 0x49,
+ ], "{ r7:6 = memd(##0x238) }");
+
+ // HELP! it is somewhat unfortunate that extended offsets don't get the ## treatment like
+ // immediates. having different operands for all immediate-extended forms seems like a kind of
+ // bad idea though.
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x05, 0x91,
+ ], "{ r6 = memb(r5+#527) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x25, 0x91,
+ ], "{ r6 = memub(r5+#527) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x45, 0x91,
+ ], "{ r6 = memh(r5+#542) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x65, 0x91,
+ ], "{ r6 = memuh(r5+#542) }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x85, 0x91,
+ ], "{ r6 = memw(r5+#572) }");
+ // the immediate of 1111 << 3 shifts a 1 out of the low 6 bits and invalidates the operand.
+ test_invalid(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0xc5, 0x91,
+ ], DecodeError::InvalidOperand);
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc0, 0xc5, 0x91,
+ ], "{ r7:6 = memd(r5+#568) }");
+
+
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0x0f, 0xc6, 0x00, 0x48,
+ ], "{ memb(##0x20f) = r6 }");
+ test_display(&[
+ 0x08, 0x40, 0x00, 0x00,
+ 0xe6, 0xc1, 0x00, 0x49,
+ ], "{ r6 = memb(##0x20f) }");
+
+ test_display(&[
+ 0x0f, 0x40, 0x92, 0x6e, // r15 = syscfg
+ 0x04, 0x40, 0xe1, 0x0f, // extender
+ 0xf1, 0xc0, 0x00, 0x78, // r17 = #whatever
+ ], "{ r15 = syscfg; r17 = ##0xfe100107 }");
+
+ // the fact that something generated this packet is a little hilarious
+ test_display(&[
+ 0x00, 0x40, 0x00, 0x00, // extender (0)
+ 0xf1, 0xc1, 0x91, 0x76, // r17 = or(r17, #whatever)
+ ], "{ r17 = or(r17, ##0xf) }");
+
+ test_display(&[
+ 0x04, 0x40, 0x00, 0x00, // extender (4 == 0x100)
+ 0xf1, 0xc1, 0x91, 0x76, // r17 = or(r17, #whatever)
+ ], "{ r17 = or(r17, ##0x10f) }");
+
+ test_display(&[
+ 0x04, 0x40, 0x00, 0x00, // extender (4 == 0x100)
+ 0xf1, 0xc1, 0x51, 0x76, // r17 = sub(#whatever, r17)
+ ], "{ r17 = sub(##0x10f, r17) }");
+
+ test_display(&[
+ 0x04, 0x40, 0x00, 0x00, // extender (4 == 0x100)
+ 0xf1, 0xc1, 0x11, 0x76, // r17 = and(r17, #whatever)
+ ], "{ r17 = and(r17, ##0x10f) }");
+}
+
// mentioned in the V62 manual, not later?
// not sure if these are still what they seem in later versions, but until demonstrated
// otherwise...
@@ -126,7 +336,7 @@ fn inst_0011() {
test_invalid(&0b0011_1000111_00100_11_1_0_0010_101_11111u32.to_le_bytes(), DecodeError::InvalidOpcode);
test_display(&0b0011_1010000_00100_11_1_0_0010_100_11111u32.to_le_bytes(), "{ lr = memb(r4 + r2<<3) }");
- test_display(&0b0011_1010001_00100_11_1_0_0010_100_11110u32.to_le_bytes(), "{ r31:30 = memub(r4 + r2<<3) }");
+ test_display(&0b0011_1010001_00100_11_1_0_0010_100_11110u32.to_le_bytes(), "{ fp = memub(r4 + r2<<3) }");
test_display(&0b0011_1011010_00100_11_1_0_0010_100_11110u32.to_le_bytes(), "{ memh(r4 + r2<<3) = fp }");
test_display(&0b0011_1011011_00100_11_1_0_0010_100_11110u32.to_le_bytes(), "{ memh(r4 + r2<<3) = r30.h }");
@@ -694,11 +904,11 @@ fn inst_1001() {
test_display(&0b1001_0110101_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r17:16 = memubh(r2+#7296) }");
test_invalid(&0b1001_0000110_00010_11_0_00100_000_00011u32.to_le_bytes(), DecodeError::InvalidOpcode);
test_display(&0b1001_0110111_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r17:16 = membh(r2+#7296) }");
- test_display(&0b1001_0111000_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memb(r2+#14592) }");
- test_display(&0b1001_0111001_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r17:16 = memub(r2+#14592) }");
- test_display(&0b1001_0111010_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memh(r2+#14592) }");
- test_display(&0b1001_0111011_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r17:16 = memuh(r2+#14592) }");
- test_display(&0b1001_0111100_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memw(r2+#14592) }");
+ test_display(&0b1001_0111000_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memb(r2+#1824) }");
+ test_display(&0b1001_0111001_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memub(r2+#1824) }");
+ test_display(&0b1001_0111010_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memh(r2+#3648) }");
+ test_display(&0b1001_0111011_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memuh(r2+#3648) }");
+ test_display(&0b1001_0111100_00010_11_1001_00_000_10000u32.to_le_bytes(), "{ r16 = memw(r2+#7296) }");
test_invalid(&0b1001_0001101_00010_11_0_00100_000_00011u32.to_le_bytes(), DecodeError::InvalidOpcode);
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);