aboutsummaryrefslogtreecommitdiff
path: root/src/armv7/thumb.rs
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2020-12-06 13:37:14 -0800
committeriximeow <me@iximeow.net>2020-12-06 13:37:14 -0800
commit5e590eba9a1baf8a544f75ec87a58daa4da33726 (patch)
tree6eb47321bb6ef3ee9055c403b9cc0a1e355f7770 /src/armv7/thumb.rs
parentcf7c141256337dc12f19e98d34efa3aa606af368 (diff)
support more mul/div variants
Diffstat (limited to 'src/armv7/thumb.rs')
-rw-r--r--src/armv7/thumb.rs271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/armv7/thumb.rs b/src/armv7/thumb.rs
index e65fe05..2743c13 100644
--- a/src/armv7/thumb.rs
+++ b/src/armv7/thumb.rs
@@ -3029,8 +3029,279 @@ pub fn decode_into<T: IntoIterator<Item=u8>>(decoder: &InstDecoder, inst: &mut I
if op2[3] {
// `Long multiply, long multiply accumulate, and divide` (`A6-248`)
return Err(DecodeError::Incomplete);
+ let op1 = instr2[4..7].load::<usize>();
+ let op2 = lower2[4..8].load::<usize>();
+
+ let rn = instr2[0..4].load::<u8>();
+ let rdlo = lower2[12..16].load::<u8>();
+ let rd = lower2[8..12].load::<u8>();
+ let rm = lower2[0..4].load::<u8>();
+
+ match op1 {
+ 0b000 => {
+ if op2 == 0b0000 {
+ inst.opcode = Opcode::SMULL;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ 0b001 => {
+ if op2 == 0b1111 {
+ inst.opcode = Opcode::SDIV;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Nothing,
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ 0b010 => {
+ if op2 == 0b0000 {
+ inst.opcode = Opcode::UMULL;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ 0b011 => {
+ if op2 == 0b1111 {
+ inst.opcode = Opcode::UDIV;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Nothing,
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ 0b100 => {
+ if op2 == 0b0000 {
+ inst.opcode = Opcode::SMLAL;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else if op2 & 0b1100 == 0b1000 {
+ inst.opcode = Opcode::SMLAL_halfword(lower2[5], lower2[4]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else if op2 & 0b1110 == 0b1100 {
+ inst.opcode = Opcode::SMLALD(lower2[4]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ 0b101 => {
+ if op2 == 0b1100 || op2 == 0b1101 {
+ inst.opcode = Opcode::SMLSLD(lower2[4]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ 0b110 => {
+ if op2 == 0b0000 {
+ inst.opcode = Opcode::UMLAL;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else if op2 == 0b0110 {
+ inst.opcode = Opcode::UMAAL;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rdlo)),
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
} else {
// `Multiply, multiply accumulate, and absolute difference` (`A6-247`)
+ let op1 = instr2[4..7].load::<usize>();
+ let op2 = lower2[4..6].load::<usize>();
+ let rm = lower2[0..4].load::<u8>();
+ let rd = lower2[8..12].load::<u8>();
+ let ra = lower2[12..16].load::<u8>();
+ let rn = instr2[0..4].load::<u8>();
+
+ if ra == 0b1111 {
+ if op1 == 0b000 {
+ if op2 == 0b00 {
+ inst.opcode = Opcode::MUL;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Nothing,
+ ];
+ } else if op2 == 0b01{
+ inst.opcode = Opcode::MLS;
+ // a == 15, unpredictable
+ decoder.unpredictable()?;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Reg(Reg::from_u8(ra)),
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+
+ } else if op1 == 0b001 {
+ // `SMULBB, SMULBT, SMULTB, SMULTT on page A8-645`
+ inst.opcode = Opcode::SMUL(lower2[5], lower2[4]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Nothing,
+ ];
+ } else if op1 == 0b011 {
+ if op2 >= 0b10 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ inst.opcode = Opcode::SMULW(lower2[4]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Nothing,
+ ];
+ } else {
+ if op2 >= 0b10 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ if op1 == 0b111 && op2 == 0b00 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ if op1 == 0b110 {
+ decoder.unpredictable()?;
+ }
+ inst.opcode = [
+ Opcode::MUL, // already handled
+ Opcode::UDF, // already handled
+ Opcode::SMUAD,
+ Opcode::UDF, // already handled
+ Opcode::SMUSD,
+ Opcode::SMMUL,
+ Opcode::SMMLS,
+ Opcode::USAD8,
+ ][op1];
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Nothing,
+ ];
+ }
+ } else {
+ if op1 == 0b000 {
+ if op2 == 0b00 {
+ inst.opcode = Opcode::MLA;
+ } else if op2 == 0b01{
+ inst.opcode = Opcode::MLS;
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Reg(Reg::from_u8(ra)),
+ ];
+ } else if op1 == 0b001 {
+ // `SMULBB, SMULBT, SMULTB, SMULTT on page A8-645`
+ inst.opcode = Opcode::SMLA(lower2[5], lower2[4]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Reg(Reg::from_u8(ra)),
+ ];
+ } else if op1 == 0b011 {
+ if op2 >= 0b10 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ inst.opcode = Opcode::SMLAW(lower2[4]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Reg(Reg::from_u8(ra)),
+ ];
+ } else {
+ if op2 >= 0b10 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ if op1 == 0b111 && op2 == 0b00 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ if op1 == 0b110 {
+ decoder.unpredictable()?;
+ }
+ inst.opcode = [
+ Opcode::MUL, // already handled
+ Opcode::UDF, // already handled
+ Opcode::SMLAD,
+ Opcode::UDF, // already handled
+ Opcode::SMLSD,
+ Opcode::SMMLA,
+ Opcode::SMMLS,
+ Opcode::USADA8,
+ ][op1];
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(rd)),
+ Operand::Reg(Reg::from_u8(rn)),
+ Operand::Reg(Reg::from_u8(rm)),
+ Operand::Reg(Reg::from_u8(ra)),
+ ];
+ }
+
+ }
return Err(DecodeError::Incomplete);
}
}