diff options
author | iximeow <me@iximeow.net> | 2025-04-12 19:08:06 -0700 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2025-04-12 19:08:06 -0700 |
commit | dc94ca8e9fb134f19e12977503a2b776d0864cb6 (patch) | |
tree | fcdc39d4c43a86bb3528a94ebc25f381df1187c5 | |
parent | 7d077f45b7e4a9776ac516da36381861e0d7e7bd (diff) |
initial list of extendable instructions, fix memub/memuh decode errors
-rw-r--r-- | src/lib.rs | 183 | ||||
-rw-r--r-- | tests/from_brain.rs | 222 |
2 files changed, 347 insertions, 58 deletions
@@ -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); |