From fa24eedf78fb9fe2d87ac3f59763e25d3ba4462f Mon Sep 17 00:00:00 2001 From: rva3 Date: Fri, 20 Feb 2026 03:45:01 +0200 Subject: 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 --- CHANGELOG | 5 ++ src/armv7.rs | 190 +++++++++++++++++++++++++++++++++++++++++++++------ src/armv7/display.rs | 83 ++++++++++++---------- src/armv7/thumb.rs | 40 +++-------- tests/armv7/mod.rs | 42 ++++++++++++ 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 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 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 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 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