diff options
| author | Grond <grond@grondhaus.net> | 2026-06-24 14:54:05 -0700 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2026-07-28 02:53:24 +0000 |
| commit | cb36e55f64ae1a941b20d2d6d6b5e546566072f8 (patch) | |
| tree | fc4c16f8478b899f0ca377715110157c5985ad53 /src | |
| parent | c4015e7157ad07ffdd37f794ee77c5695a001059 (diff) | |
Decode the correct number of operands for CMP/CMN (immediate/register)
This also incidentally fixes an issue where MOV/MVN instructions with a
shifted operand could be decoded as if they had 3 operands.
Diffstat (limited to 'src')
| -rw-r--r-- | src/armv7.rs | 67 |
1 files changed, 47 insertions, 20 deletions
diff --git a/src/armv7.rs b/src/armv7.rs index 8b55e65..2403eaa 100644 --- a/src/armv7.rs +++ b/src/armv7.rs @@ -2558,42 +2558,52 @@ impl Decoder<ARMv7> for InstDecoder { (Rn, Rd, shift_spec, Rm) }; - if shift_spec & 0xff0 == 0 { - if (0b1101 & opcode) == 0b1101 { + let last_operand = if shift_spec & 0xff0 == 0 { + // No shift, so the operand is just a register + Operand::Reg(Reg::from_u8(Rm)) + } else { + Operand::RegShift(RegShift::from_raw(shift_spec)) + }; + + match inst.opcode { + Opcode::MOV + |Opcode::MVN => { if self.should_is_must { if Rn != 0 { return Err(DecodeError::Nonconforming); } } - // MOV or MVN inst.operands = [ Operand::Reg(Reg::from_u8(Rd)), - Operand::Reg(Reg::from_u8(Rm)), + last_operand, Operand::Nothing, Operand::Nothing ]; - } else { + } + + Opcode::CMP + |Opcode::CMN => { + if self.should_is_must { + if Rd != 0 { + return Err(DecodeError::Nonconforming); + } + } inst.operands = [ - Operand::Reg(Reg::from_u8(Rd)), Operand::Reg(Reg::from_u8(Rn)), - Operand::Reg(Reg::from_u8(Rm)), + last_operand, + Operand::Nothing, Operand::Nothing ]; } - } else { - if self.should_is_must { - if opcode == 0b1101 && Rn != 0 { - // Rn "should" be zero - return Err(DecodeError::Nonconforming); - } - } - inst.operands = [ - Operand::Reg(Reg::from_u8(Rd)), - Operand::Reg(Reg::from_u8(Rn)), - Operand::RegShift(RegShift::from_raw(shift_spec)), - Operand::Nothing - ]; + _ => { + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(Rn)), + last_operand, + Operand::Nothing + ]; + } } } else { // known 0 because it and bit 5 are not both 1 --v @@ -2725,7 +2735,24 @@ impl Decoder<ARMv7> for InstDecoder { inst.opcode = Opcode::ADR; } match opcode { + // CMP/CMN (immediate) + 0b1010 | 0b1011 => { + // According to A8-368, there are 4 bits right above the immediate that + // are reserved and should be zero + if self.should_is_must && (word >> 12) as u8 & 0b1111 != 0 { + return Err(DecodeError::Nonconforming); + } + // compare has no destination register, only a source. + inst.operands = [ + Operand::Reg(Reg::from_u8(Rn)), + Operand::Imm32(imm), + Operand::Nothing, + Operand::Nothing, + ]; + } + // MOV (immediate) 0b1101 => { + // mov has no source *register*, only the immediate being moved. inst.operands = [ Operand::Reg(Reg::from_u8(Rd)), Operand::Imm32(imm), |
