aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrva3 <rva333@protonmail.com>2026-02-20 03:45:01 +0200
committeriximeow <me@iximeow.net>2026-02-22 03:08:10 +0000
commitfa24eedf78fb9fe2d87ac3f59763e25d3ba4462f (patch)
treef626db67bb33d750be7304c58dca3da260257f77
parent256c8128d82a5c5d99c1a114f3fb488b28d6b925 (diff)
support A1 encoding of several coprocessor instructions + SVC
initially was "implement MCR/MRC decoding", but expanded to include the whole coprocessor space for completeness, and then SVC is right there too. Neon instructions in this space are still rejected, as neon support is not really there yet. Co-authored-by: iximeow <me@iximeow.net>
-rw-r--r--CHANGELOG5
-rw-r--r--src/armv7.rs190
-rw-r--r--src/armv7/display.rs83
-rw-r--r--src/armv7/thumb.rs40
-rw-r--r--tests/armv7/mod.rs42
5 files changed, 269 insertions, 91 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 5f8a3c5..fa8ed2f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,11 @@
* ARMv7 instructions now impl Copy and Clone, matching A64 instructions and so
many enums/structures from decoded instructions.
+* ARMv7: expand support for coprocessor, SVC instructions. (thank you @rva333!)
+ * support non-Thumb encoding of SVC
+ * support non-`*2` forms of coprocessor instructions
+ * collapse `*2` encoding choice for coprocessor instructions into a paramter
+ on the opcode (rather than distinct opcodes)
## 0.4.0
diff --git a/src/armv7.rs b/src/armv7.rs
index 06dbecb..cb46d60 100644
--- a/src/armv7.rs
+++ b/src/armv7.rs
@@ -102,29 +102,23 @@ pub enum Opcode {
LDRSBT,
STRD,
LDRD,
- LDC(u8),
- LDCL(u8),
- LDC2(u8),
- LDC2L(u8),
- STC(u8),
- STCL(u8),
- STC2(u8),
- STC2L(u8),
- MCRR2(u8, u8),
+ LDC(u8, bool),
+ LDCL(u8, bool),
+ STC(u8, bool),
+ STCL(u8, bool),
+ MCRR(u8, u8, bool),
+ MRRC(u8, u8, bool),
/// > MCR (Move to Coprocessor from ARM Register)
///
/// fields here are `coproc`, `opcode_1`, `opcode_2`, and a bool indicating if the original
/// encoding was `MCR` or `MCR2` (`true` means `MCR2`).
MCR(u8, u8, u8, bool),
- MRRC2(u8, u8),
- MCRR(u8, u8),
- MRRC(u8, u8),
/// > MRC (Move to ARM Register from Coprocessor)
///
/// fields here are `coproc`, `opcode_1`, `opcode_2`, and a bool indicating if the original
/// encoding was `MRC` or `MRC2` (`true` means `MRC2`).
MRC(u8, u8, u8, bool),
- CDP2(u8, u8, u8),
+ CDP(u8, u8, u8, bool),
SRS(bool, bool),
RFE(bool, bool),
LDRT,
@@ -1372,9 +1366,9 @@ impl Decoder<ARMv7> for InstDecoder {
return Err(DecodeError::InvalidOperand);
}
if (word >> 20) & 0b00001 != 0 {
- inst.opcode = Opcode::MRRC2(coproc, opc1);
+ inst.opcode = Opcode::MRRC(coproc, opc1, true);
} else {
- inst.opcode = Opcode::MCRR2(coproc, opc1);
+ inst.opcode = Opcode::MCRR(coproc, opc1, true);
}
inst.operands = [
Operand::Reg(Reg::from_u8(Rt)),
@@ -1402,18 +1396,18 @@ impl Decoder<ARMv7> for InstDecoder {
// page A8-663
if pudw & 0b0010 != 0 {
- inst.opcode = Opcode::STC2L(coproc);
+ inst.opcode = Opcode::STCL(coproc, true);
} else {
- inst.opcode = Opcode::STC2(coproc);
+ inst.opcode = Opcode::STC(coproc, true);
}
} else {
// op=110xxxx1, LDC
// page A8-393
if pudw & 0b0010 != 0 {
- inst.opcode = Opcode::LDC2L(coproc);
+ inst.opcode = Opcode::LDCL(coproc, true);
} else {
- inst.opcode = Opcode::LDC2(coproc);
+ inst.opcode = Opcode::LDC(coproc, true);
}
}
@@ -1458,7 +1452,7 @@ impl Decoder<ARMv7> for InstDecoder {
if (word >> 4) & 1 == 0 {
// CDP2, page A8-356
let opc1 = (word >> 20) as u8 & 0b1111;
- inst.opcode = Opcode::CDP2(coproc, opc1, opc2);
+ inst.opcode = Opcode::CDP(coproc, opc1, opc2, true);
inst.operands = [
Operand::CReg(CReg::from_u8(Rt)),
Operand::CReg(CReg::from_u8(CRn)),
@@ -2905,12 +2899,164 @@ impl Decoder<ARMv7> for InstDecoder {
];
}
},
- 0b110 | 0b111 => {
+ 0b110 => {
// coprocessor instructions and supervisor call
// page A5-213
// low bit of 0b110 or 0b111 corresponds to high bit of op1
- return Err(DecodeError::Incomplete);
+ //
+ // op1=0b110xxxxx, see table A5-23
+ let coproc = (word >> 8) as u8 & 0b1111;
+
+ if coproc & 0b1110 == 0b1010 {
+ // Advanced SIMD, Floating-point, op1 = 0xxxxx
+ return Err(DecodeError::Incomplete);
+ }
+
+ if (word >> 20) & 0b11010 == 0b00000 {
+ // the `not 11000x0{0,1}` cases in table A5-23, MCRR or MRRC
+ // but first check that bit 2 of op1 is in fact 1:
+ if (word >> 20) & 0b00100 != 0 {
+ // actually MCRR or MRRC
+ let CRm = word as u8 & 0b1111;
+ let opc1 = (word >> 4) as u8 & 0b1111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let Rt2 = (word >> 16) as u8 & 0b1111;
+ if Rt == 15 || Rt2 == 15 || Rt == Rt2 {
+ // TODO: actually `UNPREDICTABLE`
+ return Err(DecodeError::InvalidOperand);
+ }
+ if (word >> 20) & 0b00001 != 0 {
+ inst.opcode = Opcode::MRRC(coproc, opc1, false);
+ } else {
+ inst.opcode = Opcode::MCRR(coproc, opc1, false);
+ }
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::Reg(Reg::from_u8(Rt2)),
+ Operand::CReg(CReg::from_u8(CRm)),
+ Operand::Nothing,
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ } else {
+ // STC or LDC
+ let pudw = (word >> 21) as u8 & 0b1111;
+ let Rn = (word >> 16) as u8 & 0b1111;
+ let CRd = (word >> 12) as u8 & 0b1111;
+ let imm8 = word & 0b11111111;
+
+ if (word >> 20) & 0b00001 == 0 {
+ // op=110xxxx0, STC
+ // page A8-663
+
+ if pudw & 0b0010 != 0 {
+ inst.opcode = Opcode::STCL(coproc, false);
+ } else {
+ inst.opcode = Opcode::STC(coproc, false);
+ }
+ } else {
+ // op=110xxxx1, LDC
+ // page A8-393
+
+ if pudw & 0b0010 != 0 {
+ inst.opcode = Opcode::LDCL(coproc, false);
+ } else {
+ inst.opcode = Opcode::LDC(coproc, false);
+ }
+ }
+
+ let P = pudw & 0b1000 != 0;
+ let U = pudw & 0b0100 != 0;
+ let W = pudw & 0b0001 != 0;
+
+ inst.operands = [
+ Operand::CReg(CReg::from_u8(CRd)),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), (imm8 << 2) as u16, U, W)
+ } else {
+ if W {
+ // postindex always has wback
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), (imm8 << 2) as u16, U, true)
+ } else {
+ Operand::RegDeref(Reg::from_u8(Rn))
+ }
+ },
+ if !P && !W {
+ // TODO: not sure what ldc2{l}'s <option> field really means?
+ // at this point the invalid encoding and mrrc/mcrr forms have
+ // been tested, so..
+ debug_assert!(U);
+ Operand::CoprocOption(imm8 as u8)
+ } else {
+ Operand::Nothing
+ },
+ Operand::Nothing,
+ ];
+ }
},
+ 0b111 => {
+ // coprocessor instructions and supervisor call
+ // page A5-213
+ // low bit of 0b110 or 0b111 corresponds to high bit of op1
+ //
+ // op1=0b111xxxxx, see table A5-22
+ let op1 = (word >> 20) & 0b111111;
+ if ((op1 >> 4) & 1) != 0 {
+ inst.opcode = Opcode::SVC;
+ let imm = word & 0xff_ff_ff;
+ inst.operands = [
+ Operand::Imm32(imm),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ return Ok(());
+ }
+
+ let coproc = (word >> 8) as u8 & 0b1111;
+
+ if coproc & 0b1110 == 0b1010 {
+ // Advanced SIMD, Floating-point, op1 = 0xxxxx
+ return Err(DecodeError::Incomplete);
+ }
+
+ if (word >> 4) & 1 == 0 {
+ // CDP, page A8-356
+ let CRm = word as u8 & 0b1111;
+ let opc2 = (word >> 5) as u8 & 0b111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let CRn = (word >> 16) as u8 & 0b1111;
+
+ let opc1 = (word >> 20) as u8 & 0b1111;
+ inst.opcode = Opcode::CDP(coproc, opc1, opc2, false);
+ inst.operands = [
+ Operand::CReg(CReg::from_u8(Rt)),
+ Operand::CReg(CReg::from_u8(CRn)),
+ Operand::CReg(CReg::from_u8(CRm)),
+ Operand::Nothing,
+ ];
+ } else {
+ // MCR/MRC, page A8-493
+ let CRm = word as u8 & 0b1111;
+ let opc2 = (word >> 5) as u8 & 0b111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let CRn = (word >> 16) as u8 & 0b1111;
+
+ let opc1 = (word >> 21) as u8 & 0b111;
+ if (word >> 20) & 1 == 0 {
+ inst.opcode = Opcode::MCR(coproc, opc1, opc2, false);
+ } else {
+ inst.opcode = Opcode::MRC(coproc, opc1, opc2, false);
+ }
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::CReg(CReg::from_u8(CRn)),
+ Operand::CReg(CReg::from_u8(CRm)),
+ Operand::Nothing,
+ ];
+ }
+ }
_ => { unreachable!("opc category is three bits"); }
}
Ok(())
diff --git a/src/armv7/display.rs b/src/armv7/display.rs
index f81e2cc..5ceedd6 100644
--- a/src/armv7/display.rs
+++ b/src/armv7/display.rs
@@ -462,18 +462,20 @@ pub(crate) fn visit_inst<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fm
_ => {}
}
}
- Opcode::STCL(coproc) |
- Opcode::STC(coproc) |
- Opcode::STC2L(coproc) |
- Opcode::STC2(coproc) |
- Opcode::LDC(coproc) |
- Opcode::LDCL(coproc) |
- Opcode::LDC2(coproc) |
- Opcode::LDC2L(coproc) => {
+ Opcode::STCL(coproc, _) |
+ Opcode::STC(coproc, _) |
+ Opcode::LDC(coproc, _) |
+ Opcode::LDCL(coproc, _) => {
out.span_start_opcode();
unsafe {
out.write_lt_8(instr.opcode.name())?;
}
+ if instr.condition != ConditionCode::AL {
+ let name = instr.condition.name();
+ // all condition codes are two characters long
+ out.write_char(name[0] as char)?;
+ out.write_char(name[1] as char)?;
+ }
out.span_end_opcode();
out.write_fixed_size(" p")?;
if coproc >= 10 {
@@ -497,12 +499,18 @@ pub(crate) fn visit_inst<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fm
return Ok(());
}
- Opcode::MRRC2(coproc, opc) |
- Opcode::MCRR2(coproc, opc) => {
+ Opcode::MRRC(coproc, opc, _) |
+ Opcode::MCRR(coproc, opc, _) => {
out.span_start_opcode();
unsafe {
out.write_lt_8(instr.opcode.name())?;
}
+ if instr.condition != ConditionCode::AL {
+ let name = instr.condition.name();
+ // all condition codes are two characters long
+ out.write_char(name[0] as char)?;
+ out.write_char(name[1] as char)?;
+ }
out.span_end_opcode();
out.write_char(' ')?;
@@ -527,11 +535,17 @@ pub(crate) fn visit_inst<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fm
}
Opcode::MRC(coproc, opc1, opc2, _) |
Opcode::MCR(coproc, opc1, opc2, _) |
- Opcode::CDP2(coproc, opc1, opc2) => {
+ Opcode::CDP(coproc, opc1, opc2, _) => {
out.span_start_opcode();
unsafe {
out.write_lt_8(instr.opcode.name())?;
}
+ if instr.condition != ConditionCode::AL {
+ let name = instr.condition.name();
+ // all condition codes are two characters long
+ out.write_char(name[0] as char)?;
+ out.write_char(name[1] as char)?;
+ }
out.span_end_opcode();
out.write_char(' ')?;
@@ -911,21 +925,15 @@ impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for ConditionedOpcode {
Opcode::HVC |
Opcode::SVC |
Opcode::SMC |
- Opcode::LDC(_) |
- Opcode::LDCL(_) |
- Opcode::LDC2(_) |
- Opcode::LDC2L(_) |
- Opcode::STC(_) |
- Opcode::STCL(_) |
- Opcode::STC2(_) |
- Opcode::STC2L(_) |
- Opcode::MCRR2(_, _) |
+ Opcode::LDC(_, _) |
+ Opcode::LDCL(_, _) |
+ Opcode::STC(_, _) |
+ Opcode::STCL(_, _) |
+ Opcode::MCRR(_, _, _) |
Opcode::MCR(_, _, _, _) |
- Opcode::MRRC2(_, _) |
+ Opcode::MRRC(_, _, _) |
Opcode::MRC(_, _, _, _) |
- Opcode::MCRR(_, _) |
- Opcode::MRRC(_, _) |
- Opcode::CDP2(_, _, _) => { write!(out, "{}", colors.platform_op(self)) },
+ Opcode::CDP(_, _, _, _) => { write!(out, "{}", colors.platform_op(self)) },
}
}
}
@@ -950,23 +958,24 @@ impl Opcode {
Opcode::LDRSBT => { "ldrsbt" },
Opcode::STRD => { "strd" },
Opcode::LDRD => { "ldrd" },
- Opcode::LDC(_) => { "ldc" },
- Opcode::LDCL(_) => { "ldcl" },
- Opcode::LDC2(_) => { "ldc2" },
- Opcode::LDC2L(_) => { "ldc2l" },
- Opcode::STC(_) => { "stc" },
- Opcode::STCL(_) => { "stcl" },
- Opcode::STC2(_) => { "stc2" },
- Opcode::STC2L(_) => { "stc2l" },
- Opcode::MCRR2(_, _) => { "mcrr2" },
+ Opcode::LDC(_, false) => { "ldc" },
+ Opcode::LDC(_, true) => { "ldc2" },
+ Opcode::LDCL(_, false) => { "ldcl" },
+ Opcode::LDCL(_, true) => { "ldc2l" },
+ Opcode::STC(_, false) => { "stc" },
+ Opcode::STC(_, true) => { "stc2" },
+ Opcode::STCL(_, false) => { "stcl" },
+ Opcode::STCL(_, true) => { "stc2l" },
Opcode::MCR(_, _, _, false) => { "mcr" },
Opcode::MCR(_, _, _, true) => { "mcr2" },
- Opcode::MRRC2(_, _) => { "mrrc2" },
Opcode::MRC(_, _, _, false) => { "mrc" },
Opcode::MRC(_, _, _, true) => { "mrc2" },
- Opcode::MCRR(_, _) => { "mcrr" },
- Opcode::MRRC(_, _) => { "mrrc" },
- Opcode::CDP2(_, _, _) => { "cdp2" },
+ Opcode::MCRR(_, _, false) => { "mcrr" },
+ Opcode::MCRR(_, _, true) => { "mcrr2" },
+ Opcode::MRRC(_, _, false) => { "mrrc" },
+ Opcode::MRRC(_, _, true) => { "mrrc2" },
+ Opcode::CDP(_, _, _, false) => { "cdp" },
+ Opcode::CDP(_, _, _, true) => { "cdp2" },
Opcode::SRS(true, true) => { "srsib" },
Opcode::SRS(false, true) => { "srsia" },
Opcode::SRS(true, false) => { "srsdb" },
diff --git a/src/armv7/thumb.rs b/src/armv7/thumb.rs
index e18cb38..a2809eb 100644
--- a/src/armv7/thumb.rs
+++ b/src/armv7/thumb.rs
@@ -4253,11 +4253,7 @@ fn decode_table_a6_30(decoder: &InstDecoder, inst: &mut Instruction, instr2: Bit
let opc1 = lower2[4..8].load::<u8>();
let rt = lower2[12..16].load::<u8>();
let rt2 = instr2[0..4].load::<u8>();
- if instr2[12] {
- inst.opcode = Opcode::MCRR2(coproc, opc1);
- } else {
- inst.opcode = Opcode::MCRR(coproc, opc1);
- }
+ inst.opcode = Opcode::MCRR(coproc, opc1, instr2[12]);
inst.operands = [
Operand::Reg(Reg::from_u8(rt)),
Operand::Reg(Reg::from_u8(rt2)),
@@ -4275,11 +4271,7 @@ fn decode_table_a6_30(decoder: &InstDecoder, inst: &mut Instruction, instr2: Bit
// but the operand name is `opc1`!
//
// this is a very uninteresting typo, but fun to spot nonetheless
- if instr2[12] {
- inst.opcode = Opcode::MRRC2(coproc, opc1);
- } else {
- inst.opcode = Opcode::MRRC(coproc, opc1);
- }
+ inst.opcode = Opcode::MRRC(coproc, opc1, instr2[12]);
inst.operands = [
Operand::Reg(Reg::from_u8(rt)),
Operand::Reg(Reg::from_u8(rt2)),
@@ -4296,18 +4288,10 @@ fn decode_table_a6_30(decoder: &InstDecoder, inst: &mut Instruction, instr2: Bit
let crd = lower2[12..16].load::<u8>();
let imm8 = lower2[0..8].load::<u16>();
- if instr2[12] {
- if instr2[6] {
- inst.opcode = Opcode::STC2L(coproc);
- } else {
- inst.opcode = Opcode::STC2(coproc);
- }
+ if instr2[6] {
+ inst.opcode = Opcode::STCL(coproc, instr2[12]);
} else {
- if instr2[6] {
- inst.opcode = Opcode::STCL(coproc);
- } else {
- inst.opcode = Opcode::STC(coproc);
- }
+ inst.opcode = Opcode::STC(coproc, instr2[12]);
}
inst.operands = [
Operand::CReg(CReg::from_u8(crd)),
@@ -4356,18 +4340,10 @@ fn decode_table_a6_30(decoder: &InstDecoder, inst: &mut Instruction, instr2: Bit
// `LDC, LDC2 (immediate) on A8-393`
}
- if instr2[12] {
- if instr2[6] {
- inst.opcode = Opcode::LDC2L(coproc);
- } else {
- inst.opcode = Opcode::LDC2(coproc);
- }
+ if instr2[6] {
+ inst.opcode = Opcode::LDCL(coproc, instr2[12]);
} else {
- if instr2[6] {
- inst.opcode = Opcode::LDCL(coproc);
- } else {
- inst.opcode = Opcode::LDC(coproc);
- }
+ inst.opcode = Opcode::LDC(coproc, instr2[12]);
}
inst.operands = [
Operand::CReg(CReg::from_u8(crd)),
diff --git a/tests/armv7/mod.rs b/tests/armv7/mod.rs
index b878dd1..8f413ca 100644
--- a/tests/armv7/mod.rs
+++ b/tests/armv7/mod.rs
@@ -559,32 +559,74 @@ fn test_unconditional() {
#[test]
fn test_coproc() {
test_armv5([0x12, 0x34, 0xcf, 0xfc], "stc2l p4, c3, [pc], {0x12}");
+ test_armv5([0x12, 0x34, 0xcf, 0xec], "stcl p4, c3, [pc], {0x12}");
+ test_armv5([0x12, 0x34, 0xcf, 0x0c], "stcleq p4, c3, [pc], {0x12}");
test_armv5([0x12, 0x3d, 0xcf, 0xfc], "stc2l p13, c3, [pc], {0x12}");
+ test_armv5([0x12, 0x3d, 0xcf, 0xec], "stcl p13, c3, [pc], {0x12}");
+ test_armv5([0x12, 0x3d, 0xcf, 0x0c], "stcleq p13, c3, [pc], {0x12}");
test_armv5([0x12, 0x34, 0xdf, 0xfc], "ldc2l p4, c3, [pc], {0x12}");
+ test_armv5([0x12, 0x34, 0xdf, 0xec], "ldcl p4, c3, [pc], {0x12}");
+ test_armv5([0x12, 0x34, 0xdf, 0x0c], "ldcleq p4, c3, [pc], {0x12}");
test_armv5([0x34, 0x78, 0xff, 0xfc], "ldc2l p8, c7, [pc], 0xd0");
+ test_armv5([0x34, 0x78, 0xff, 0xec], "ldcl p8, c7, [pc], 0xd0");
+ test_armv5([0x34, 0x78, 0xff, 0x0c], "ldcleq p8, c7, [pc], 0xd0");
test_armv5([0x34, 0x7d, 0xff, 0xfc], "ldc2l p13, c7, [pc], 0xd0");
+ test_armv5([0x34, 0x7d, 0xff, 0xec], "ldcl p13, c7, [pc], 0xd0");
+ test_armv5([0x34, 0x7d, 0xff, 0x0c], "ldcleq p13, c7, [pc], 0xd0");
test_invalid([0x34, 0x78, 0x1f, 0xfc]);
test_armv5([0x34, 0x78, 0x9f, 0xfc], "ldc2 p8, c7, [pc], {0x34}");
+ test_armv5([0x34, 0x78, 0x9f, 0xec], "ldc p8, c7, [pc], {0x34}");
+ test_armv5([0x34, 0x78, 0x9f, 0x0c], "ldceq p8, c7, [pc], {0x34}");
test_armv5([0x34, 0x7d, 0x9f, 0xfc], "ldc2 p13, c7, [pc], {0x34}");
+ test_armv5([0x34, 0x7d, 0x9f, 0xec], "ldc p13, c7, [pc], {0x34}");
+ test_armv5([0x34, 0x7d, 0x9f, 0x0c], "ldceq p13, c7, [pc], {0x34}");
test_armv5([0x34, 0x78, 0xbf, 0xfc], "ldc2 p8, c7, [pc], 0xd0");
+ test_armv5([0x34, 0x78, 0xbf, 0xec], "ldc p8, c7, [pc], 0xd0");
+ test_armv5([0x34, 0x78, 0xbf, 0x0c], "ldceq p8, c7, [pc], 0xd0");
test_armv5([0x34, 0x78, 0xbf, 0xfd], "ldc2 p8, c7, [pc, 0xd0]!");
+ test_armv5([0x34, 0x78, 0xbf, 0xed], "ldc p8, c7, [pc, 0xd0]!");
+ test_armv5([0x34, 0x78, 0xbf, 0x0d], "ldceq p8, c7, [pc, 0xd0]!");
test_armv5([0x34, 0x78, 0x9f, 0xfd], "ldc2 p8, c7, [pc, 0xd0]");
+ test_armv5([0x34, 0x78, 0x1f, 0xfd], "ldc2 p8, c7, [pc, -0xd0]");
+ test_armv5([0x34, 0x78, 0xdf, 0xfc], "ldc2l p8, c7, [pc], {0x34}");
test_armv6([0x34, 0x78, 0x5a, 0xfc], "mrrc2 p8, 3, r7, r10, c4");
+ test_armv6([0x34, 0x78, 0x5a, 0xec], "mrrc p8, 3, r7, r10, c4");
+ test_armv6([0x34, 0x78, 0x5a, 0x0c], "mrrceq p8, 3, r7, r10, c4");
test_armv6([0x34, 0x7d, 0x5a, 0xfc], "mrrc2 p13, 3, r7, r10, c4");
+ test_armv6([0x34, 0x7d, 0x5a, 0xec], "mrrc p13, 3, r7, r10, c4");
+ test_armv6([0x34, 0x7d, 0x5a, 0x0c], "mrrceq p13, 3, r7, r10, c4");
// Rt/Rt2 may not be r15
test_invalid([0x34, 0x78, 0x5f, 0xfc]);
+ test_invalid([0x34, 0x7d, 0x1f, 0x0c]); // op1 = 00000x => undefined
test_armv6([0x34, 0x78, 0x4a, 0xfc], "mcrr2 p8, 3, r7, r10, c4");
+ test_armv6([0x34, 0x78, 0x4a, 0xec], "mcrr p8, 3, r7, r10, c4");
+ test_armv6([0x34, 0x78, 0x4a, 0x0c], "mcrreq p8, 3, r7, r10, c4");
test_armv6([0x34, 0x7d, 0x4a, 0xfc], "mcrr2 p13, 3, r7, r10, c4");
+ test_armv6([0x34, 0x7d, 0x4a, 0xec], "mcrr p13, 3, r7, r10, c4");
+ test_armv6([0x34, 0x7d, 0x4a, 0x0c], "mcrreq p13, 3, r7, r10, c4");
// Rt/Rt2 may not be r15
test_invalid([0x34, 0x78, 0x4f, 0xfc]);
test_armv5([0x34, 0x78, 0x4f, 0xfe], "mcr2 p8, 2, r7, c15, c4, 1");
test_armv5([0x34, 0x7d, 0x4f, 0xfe], "mcr2 p13, 2, r7, c15, c4, 1");
+ test_armv5([0x34, 0x7d, 0x4f, 0xee], "mcr p13, 2, r7, c15, c4, 1");
+ test_armv5([0x34, 0x7d, 0x4f, 0x0e], "mcreq p13, 2, r7, c15, c4, 1");
test_armv5([0x34, 0x78, 0x5f, 0xfe], "mrc2 p8, 2, r7, c15, c4, 1");
test_armv5([0x34, 0x7d, 0x5f, 0xfe], "mrc2 p13, 2, r7, c15, c4, 1");
+ test_armv5([0x34, 0x7d, 0x5f, 0xee], "mrc p13, 2, r7, c15, c4, 1");
+ test_armv5([0x34, 0x7d, 0x5f, 0x0e], "mrceq p13, 2, r7, c15, c4, 1");
test_armv5([0x24, 0x78, 0x5f, 0xfe], "cdp2 p8, 5, c7, c15, c4, 1");
test_armv5([0x24, 0x7d, 0x5f, 0xfe], "cdp2 p13, 5, c7, c15, c4, 1");
+ test_armv5([0x24, 0x7d, 0x5f, 0xee], "cdp p13, 5, c7, c15, c4, 1");
+ test_armv5([0x24, 0x7d, 0x5f, 0x0e], "cdpeq p13, 5, c7, c15, c4, 1");
test_armv5([0x24, 0x78, 0x4f, 0xfe], "cdp2 p8, 4, c7, c15, c4, 1");
test_armv5([0x24, 0x7d, 0x4f, 0xfe], "cdp2 p13, 4, c7, c15, c4, 1");
+ test_armv5([0x24, 0x7d, 0x4f, 0xee], "cdp p13, 4, c7, c15, c4, 1");
+ test_armv5([0x24, 0x7d, 0x4f, 0x0e], "cdpeq p13, 4, c7, c15, c4, 1");
+}
+
+#[test]
+fn test_svc() {
+ test_display([0x24, 0x7d, 0x4f, 0x0f], "svceq 0x4f7d24");
}
#[test]