use std::fmt; // use yaxpeax_arch::{Arch, AddressDiff, Decoder, LengthedInstruction}; use armv7::ConditionCode; use armv7::DecodeError; use armv7::Reg; use armv7::RegShift; use armv7::Operand; use armv7::Opcode; use armv7::Instruction; use armv7::InstDecoder; use armv7::StatusRegMask; use bitvec::prelude::*; #[allow(non_snake_case)] fn ROR_C(x: u32, shift: u16) -> (u32, bool) { // let m = shift % 32; // `shift` is known to be 31 or lower let m = shift; let result = (x >> m) | (x << (32 - m)); let carry_out = (result >> 31) & 1 != 0; (result, carry_out) } #[allow(non_snake_case)] fn ThumbExpandImm_C(imm: u16) -> u32 { if imm & 0b1100_0000_0000 == 0 { let ty = (imm >> 8) & 0b11; let imm_low = (imm & 0b11111111) as u32; match ty { 0b00 => { imm_low } 0b01 => { if imm_low == 0 { panic!("unpredictable"); } (imm_low << 16) | imm_low } 0b10 => { if imm_low == 0 { panic!("unpredictable"); } (imm_low << 24) | (imm_low << 8) } 0b11 => { if imm_low == 0 { panic!("unpredictable"); } (imm_low << 24) | (imm_low << 16) | (imm_low << 8) | imm_low } _ => { unreachable!("impossible bit pattern"); } } } else { let unrotated_value = ((1 << 7) | (imm & 0b1111111)) as u32; let rot = (imm >> 7) & 0b11111; // TODO: figure out what to do with carry_out let (imm32, _carry_out) = ROR_C(unrotated_value, rot); imm32 } } #[allow(non_snake_case)] fn DecodeImmShift(reg: u8, ty: u8, imm5: u8) -> RegShift { let imm = match ty { 0b00 => { imm5 }, 0b01 => { if imm5 == 0 { 32 } else { imm5 } }, 0b10 => { if imm5 == 0 { 32 } else { imm5 } }, 0b11 => { // ignores the `if ty == 11 && imm5 = 00000 then shift_t = RRX && shift_n = 1` imm5 } _ => { unreachable!("impossible bit pattern"); } }; RegShift::from_raw( 0b00000 | // `RegImm` reg as u16 | ((ty as u16) << 5)| ((imm as u16) << 7) ) } #[allow(non_snake_case)] pub fn decode_into<T: IntoIterator<Item=u8>>(decoder: &InstDecoder, inst: &mut Instruction, bytes: T) -> Result<(), DecodeError> { // these are cleared in `armv7::InstDecoder::decode_into`. // they must be reset when switching out of thumb decoding or decoding a new thumb instruction, // which that `decode_into` is the entrypoint for in all cases. // inst.set_w(false); // inst.set_wide(false); inst.set_thumb(true); let mut iter = bytes.into_iter(); let instr: u16 = ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) ) | ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) << 8 ); let mut instr2 = bitarr![Lsb0, u16; 0u16; 16]; instr2[0..16].store(instr); let opword = instr2[11..].load::<u16>(); // `A6.1 Thumb instruction set encoding` if opword >= 0b11101 { inst.set_w(true); inst.set_wide(true); // 32b instruction - `A6-228, 32-bit Thumb instruction encoding` // opword low bits 01, 10, and 11 correspond to `op1` in table `A6-9` let lower: u16 = ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) ) | ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) << 8 ); let mut lower2 = bitarr![Lsb0, u16; 0u16; 16]; lower2[0..16].store(lower); let op2 = &instr2[4..11]; if opword < 0b11110 { // op1 == 0b01 // interpret `op1 == 0b01` lines of table `A6-9` if !op2[6] { // `op2` is `0b00.. ` or `0b01..` if !op2[5] { // `op2` is `00xxxxx`, and is `Load/store`, either `multiple` or `dual` let rn = instr2[..4].load::<u8>(); // TODO: double check if op2[2] { // `Load/store dual, load/store exclusive, table branch` (`A6-236`) let op1op2 = (instr2[7..9].load::<u8>() << 2) | instr2[4..6].load::<u8>(); let imm8 = lower2[..8].load::<u16>(); let rd = lower2[8..12].load::<u8>(); let rt = lower2[12..16].load::<u8>(); // all only-wide, no w suffix inst.set_w(false); match op1op2 { 0b0000 => { // `STREX` (`A8-691`) // v6T2 if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rn == 15 { decoder.unpredictable()?; } if rd == rn || rd == rt { decoder.unpredictable()?; } inst.opcode = Opcode::STREX; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rt)), // preindex for `[<Rn>, #<imm>]`, no writeback. imm is zero // extended, so not signed; always add. Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm8 << 2, true, false, ), Operand::Nothing, ]; } 0b0001 => { // `LDREX` (`A8-433`) // v6T2 if rt == 13 || rt == 15 || rn == 15 { decoder.unpredictable()?; } // TODO: should_is_must() // rd == 0b1111 inst.opcode = Opcode::LDREX; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), // preindex for `[<Rn>, #<imm>]`, no writeback. imm is zero // extended, so not signed; always add. Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm8 << 2, true, false, ), Operand::Nothing, Operand::Nothing, ]; } 0b0010 | 0b0110 => { // `STRD (immediate)` (`A8-687`) // v6T2 // bit 5 (w) == 0 let w = false; let u = instr2[7]; let p = instr2[8]; // `if P == '0' && W == '0' then SEE "Related encodings"` -> this // would imply tbb/tbh, should be unreachable if rn == 15 || rt == 13 || rt == 15 || rd == 13 || rd == 15 { decoder.unpredictable()?; } inst.opcode = Opcode::STRD; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) } else { // p == 0 and w == 0 is impossible, would be tbb/tbh Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) }, Operand::Nothing, ]; } 0b0011 | 0b0111 => { // `LDRD (immediate)`/`(literal)` (`A8-687`) // bit 5 (w) == 0 let w = false; let u = instr2[7]; let p = instr2[8]; // `if P == '0' && W == '0' then SEE "Related encodings"` -> this // would imply tbb/tbh, should be unreachable if rt == 13 || rt == 15 || rd == 13 || rd == 15 || rd == rt { decoder.unpredictable()?; } if w && (rn == rt || rn == rd) { decoder.unpredictable()?; } if rn != 0b1111 { // `LDRD (immediate)` (`A8-427`) // v6T2 inst.opcode = Opcode::LDRD; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) } else { // p == 0 and w == 0 is impossible, would be tbb/tbh Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) }, Operand::Nothing, ]; } else { // `LDRD (literal)` (`A8-429`) // v6T2 if w { decoder.unpredictable()?; } // which because !(p == 0 && w == 0), we know p is true inst.opcode = Opcode::LDRD; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, false), Operand::Nothing, ]; } } 0b0100 => { // `STREX_` // v7 let op3 = lower2[4..8].load::<u8>(); let rt2 = rd; let rd = (imm8 & 0b1111) as u8; match op3 { 0b0100 => { // `STREXB` (`A8-693`) if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rn == 15 { decoder.unpredictable()?; } if rd == rn || rd == rt { decoder.unpredictable()?; } // TODO: should_is_must() // rt2 == 0b1111 inst.opcode = Opcode::STREXB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rt)), Operand::RegDeref(Reg::from_u8(rn)), Operand::Nothing, ]; } 0b0101 => { // `STREXH` (`A8-693`) if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rn == 15 { decoder.unpredictable()?; } if rd == rn || rd == rt { decoder.unpredictable()?; } // TODO: should_is_must() // rt2 == 0b1111 inst.opcode = Opcode::STREXH; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rt)), Operand::RegDeref(Reg::from_u8(rn)), Operand::Nothing, ]; } 0b0111 => { // `STREXD` (`A8-693`) if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rt2 == 13 || rt2 == 15 || rn == 15 { decoder.unpredictable()?; } if rd == rn || rd == rt || rd == rt2 { decoder.unpredictable()?; } inst.opcode = Opcode::STREXD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rt2)), Operand::RegDeref(Reg::from_u8(rn)), ]; } _ => { return Err(DecodeError::Undefined); } } } 0b0101 => { // `TBB`/`TBH`/`LDREX_` let op3 = lower2[4..8].load::<u8>(); let rt2 = rd; let rd = (imm8 & 0b1111) as u8; match op3 { 0b0000 => { // `TBB` // TODO: should_is_must() // rt == 0b1111 // rd == 0b0000 inst.opcode = Opcode::TBB; inst.operands = [ Operand::RegDerefPreindexReg( Reg::from_u8(rn), Reg::from_u8(rd), true, // add false, // no wback ), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b0001 => { // `TBH` // TODO: should_is_must() // rt == 0b1111 // rd == 0b0000 inst.opcode = Opcode::TBB; inst.operands = [ Operand::RegDerefPreindexRegShift( Reg::from_u8(rn), // want `<Rm>, LSL #1`, construct a raw shift // ourselves RegShift::from_raw( 0b10000 | // `RegImm` rd as u16 | // reg == rd (0b00 << 5) | // LSL (1 << 7) // shift == #1 ), true, // add false, // no wback ), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b0100 => { // `LDREXB` if rt == 13 || rt == 15 || rn == 15 { decoder.unpredictable()?; } // TODO: should_is_must() // rt2 == 0b1111 // rd == 0b1111 inst.opcode = Opcode::LDREXB; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDeref(Reg::from_u8(rn)), Operand::Nothing, Operand::Nothing, ]; } 0b0101 => { // `LDREXH` if rt == 13 || rt == 15 || rn == 15 { decoder.unpredictable()?; } // TODO: should_is_must() // rt2 == 0b1111 // rd == 0b1111 inst.opcode = Opcode::LDREXH; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDeref(Reg::from_u8(rn)), Operand::Nothing, Operand::Nothing, ]; } 0b0110 => { // `LDREXD` if rt == 13 || rt == 15 || rt2 == 13 || rt2 == 15 || rn == 15 { decoder.unpredictable()?; } // TODO: should_is_must() // rd == 0b1111 inst.opcode = Opcode::LDREXD; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rt2)), Operand::RegDeref(Reg::from_u8(rn)), Operand::Nothing, ]; } _ => { return Err(DecodeError::Undefined); } } } 0b1000 | 0b1010 | 0b1100 | 0b1110 => { // `STRD (immediate)` (`A8-687`) // v6T2 let w = instr2[5]; let u = instr2[7]; let p = instr2[8]; // `if P == '0' && W == '0' then SEE "Related encodings"` -> this // would imply tbb/tbh, should be unreachable if rn == 15 || rt == 13 || rt == 15 || rd == 13 || rd == 15 { decoder.unpredictable()?; } inst.opcode = Opcode::STRD; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) } else { // p == 0 and w == 0 is impossible, would be tbb/tbh Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) }, Operand::Nothing, ]; } 0b1001 | 0b1011 | 0b1101 | 0b1111 => { // `LDRD (immediate)` (`A8-687`) // v6T2 let w = instr2[5]; let u = instr2[7]; let p = instr2[8]; // `if P == '0' && W == '0' then SEE "Related encodings"` -> this // would imply tbb/tbh, should be unreachable if rt == 13 || rt == 15 || rd == 13 || rd == 15 || rd == rt { decoder.unpredictable()?; } if w && (rn == rt || rn == rd) { decoder.unpredictable()?; } if rn != 0b1111 { // `LDRD (immediate)` (`A8-427`) // v6T2 inst.opcode = Opcode::LDRD; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) } else { // p == 0 and w == 0 is impossible, would be tbb/tbh Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) }, Operand::Nothing, ]; } else { // `LDRD (literal)` (`A8-429`) // v6T2 if w { decoder.unpredictable()?; } // which because !(p == 0 && w == 0), we know p is true inst.opcode = Opcode::LDRD; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, false), Operand::Nothing, ]; } } _ => { unreachable!("impossible bit pattern"); } } } else { let w = instr2[5]; // `Load/store multiple` (`A6-235`) if !instr2[4] { // `L == 0` match instr2[7..9].load::<u8>() { 0b00 => { // `SRS (Thumb)` (`B9-1990`) // v6T2 inst.opcode = Opcode::SRS(false, true); // `srsdb` inst.operands = [ Operand::RegWBack(Reg::from_u8(13), w), Operand::Imm12(lower2[0..4].load::<u16>()), // #<mode> ? what's the syntax here? #<the literal>? Operand::Nothing, Operand::Nothing, ]; } 0b01 => { // `STM (STMIA, STMEA)` (`A8-665`) // v6T2 if rn == 15 || lower.count_ones() < 2 { decoder.unpredictable()?; } if w && (lower & (1 << rn)) != 0 { decoder.unpredictable()?; } inst.opcode = Opcode::STM( true, // add false, // preincrement w, // wback true, // usermode ); inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), w), Operand::RegList(lower), Operand::Nothing, Operand::Nothing, ]; } 0b10 => { // `STMDB`/`STMFD` (`A8-669`) // or `PUSH` (`A8-539`) // implied by instruction? inst.set_w(false); if w && rn == 0b1101 { // `PUSH` // v6T2 if lower.count_ones() < 2 { decoder.unpredictable()?; } inst.opcode = Opcode::PUSH; inst.operands = [ Operand::RegList(lower), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `STMDB` // v6T2 if rn == 15 || lower.count_ones() < 2 { decoder.unpredictable()?; } if w && (lower & (1 << rn)) != 0 { decoder.unpredictable()?; } inst.opcode = Opcode::STM( false, // decrement true, // preincrement w, // wback true, // usermode? ); inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), w), Operand::RegList(lower), Operand::Nothing, Operand::Nothing, ]; } } 0b11 => { // `SRS (Thumb)` (`B9-1990`) // v6T2 inst.opcode = Opcode::SRS(false, true); // `srsia` inst.operands = [ Operand::RegWBack(Reg::from_u8(13), w), Operand::Imm12(lower & 0b1111), // #<mode> ? what's the syntax here? #<the literal>? Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!(); } } } else { // `L == 1` match instr2[7..9].load::<u8>() { 0b00 => { // `RFE` (`B9-1986`) // v6T2 if rn == 15 { decoder.unpredictable()?; } inst.opcode = Opcode::RFE(false, true); inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), w), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b01 => { // `LDM/LDMIA/LDMFD (Thumb)` (`A8-397`) if w && rn == 0b1101 { // `POP` (`A8-535`) if lower.count_ones() < 2 || (lower & 0xc000) == 0xc000 { decoder.unpredictable()?; } inst.opcode = Opcode::POP; inst.operands = [ Operand::RegList(lower), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `LDM/LDMIA/LDMFD` if rn == 15 || lower.count_ones() < 2 { decoder.unpredictable()?; } if w && (lower & (1 << rn)) != 0 { decoder.unpredictable()?; } inst.opcode = Opcode::LDM(true, false, w, true); inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), w), Operand::RegList(lower), Operand::Nothing, Operand::Nothing, ]; } } 0b10 => { // `LDMDB/LDMEA` (`A8-403`) // wide is implied inst.set_w(false); if rn == 15 || lower.count_ones() < 2 { decoder.unpredictable()?; } if w && (lower & (1 << rn)) != 0 { decoder.unpredictable()?; } inst.opcode = Opcode::LDM(false, true, w, true); inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), w), Operand::RegList(lower), Operand::Nothing, Operand::Nothing, ]; } 0b11 => { // `RFE` (`B9-1986`) // v6T2 if rn == 15 { decoder.unpredictable()?; } inst.opcode = Opcode::RFE(true, false); inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), w), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!(); } } } } } else { // op2 is `01xxxxx` and is: // `Data-processing (shfited register)` (`A6-241`) // v6T2 let op = op2[1..5].load::<u8>(); let s = instr2[4]; let rn = instr2[0..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); let imm3 = lower2[12..15].load::<u16>(); let imm2 = lower2[6..8].load::<u16>(); let tp = lower2[4..6].load::<u16>(); let rm = lower2[0..4].load::<u16>(); let shift = RegShift::from_raw( 0b00000 | // reg-imm shift rm as u16 | (imm2 << 7) | (imm3 << 9) | tp << 5 ); let shift = Operand::RegShift(shift); inst.s = s; match op { 0b0000 => { if rd == 0b1111 && s { // `TST` (`A8-747`) // v6T2 inst.s = false; inst.opcode = Opcode::TST; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, Operand::Nothing, ]; } else { // `AND` (`A8-324`) // v6T2 inst.opcode = Opcode::AND; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } } 0b0001 => { // `BIC` (`A8-340`) // v6T2 inst.opcode = Opcode::BIC; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } 0b0010 => { if rn == 0b1111 { // `Move register and immediate shifts`, also `A6-241` let tp = (lower >> 4) & 0b11; let imm2 = (lower >> 6) & 0b11; let imm3 = (lower >> 12) & 0b111; let imm5 = (imm3 << 2) | imm2; match tp { 0b00 => { if imm5 == 0 { // `MOV (register, Thumb)` (`A8-487`) // encoding T3 inst.set_w(true); let rm = lower2[..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); inst.opcode = Opcode::MOV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else { // `LSL (immediate)` (`A8-469`) // encoding T2 inst.set_w(true); let rm = lower2[..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); inst.opcode = Opcode::LSL; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(imm5), Operand::Nothing, ]; } }, 0b01 => { // `LSR (immediate)` (`A8-473`) // encoding T2 inst.set_w(true); let rm = lower2[..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); inst.opcode = Opcode::LSR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(imm5), Operand::Nothing, ]; } 0b10 => { // `ASR (immediate)` (`A8-328`) // encoding T2 inst.set_w(true); let rm = lower2[..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); inst.opcode = Opcode::ASR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(imm5), Operand::Nothing, ]; } 0b11 => { if imm5 == 0 { // `RRX` (`A8-573`) // encoding T1 inst.set_w(false); let rm = lower2[..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); inst.opcode = Opcode::RRX; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else { // `ROR (immediate)` (`A8-569`) // encoding T1 inst.set_w(false); let rm = lower2[..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); inst.opcode = Opcode::ASR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(imm5), Operand::Nothing, ]; } } _ => { unreachable!("impossible bit pattern for `tp`"); } } } else { // `ORR` (`A8-519`) // v6T2 inst.opcode = Opcode::ORR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } } 0b0011 => { if rn == 0b1111 { // `MVN` (`A8-507`) // v6T2 inst.opcode = Opcode::MVN; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), shift, Operand::Nothing, Operand::Nothing, ]; } else { // `ORN` (`A8-515`) // v6T2 inst.set_w(false); inst.opcode = Opcode::ORN; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } } 0b0100 => { if rd == 0b1111 && s { // `TEQ` (`A8-741`) // v6T2 inst.s = false; inst.opcode = Opcode::TEQ; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, Operand::Nothing, ]; } else { // `EOR` (`A8-385`) // v6T2 inst.opcode = Opcode::EOR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } } 0b0110 => { // `PKH` (`A8-523`) // v6T2 // TODO: fix shift // TODO: check opcode inst.s = false; inst.opcode = if lower & 0b10000 != 0 { Opcode::PKHTB } else { Opcode::PKHBT }; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } 0b1000 => { if rd == 0b1111 && s { // `CMN` (`A8-364`) // v6T2 inst.s = false; inst.opcode = Opcode::CMN; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, Operand::Nothing, ]; } else { // `ADD` (`A8-308`) inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } } 0b1010 => { // `ADC` (`A8-300`) // v6T2 inst.opcode = Opcode::ADC; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } 0b1011 => { // `SBC` (`A8-595`) // v6T2 inst.opcode = Opcode::SBC; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } 0b1101 => { if rd == 0b1111 && s { // `CMP` (`A8-370`) // v6T2 inst.s = false; inst.opcode = Opcode::CMP; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, Operand::Nothing, ]; } else { // `SUB` (`A8-713`) // v6T2 inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } } 0b1110 => { // `RSB` (`A8-577`) inst.set_w(false); inst.opcode = Opcode::RSB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), shift, Operand::Nothing, ]; } _ => { // undefined encoding return Err(DecodeError::Undefined); } } } } else { // `Coprocessor, Advanced SIMD, and Floating-point instructions` (`A6-249`) // v6T2 eprintln!("TODO: coproc/simd/fp"); return Err(DecodeError::Incomplete); } } else if opword < 0b11111 { // op1 == 0b10 // interpret `op1 == 0b10` lines in table `A6-9` on `A6-228`: if lower & 0x80 == 0 { // op == 0 if !op2[5] { // `A6.3.1` `Data-processing (modified immediate)` (`A6-229`) // see `A6.3.2` for `Modified immediate constants in Thumb instructions` on how // to decode immediates // v6T2 let op = op2[1..5].load::<u8>(); let i = instr2[10..11].load::<u16>(); let s = instr2[4]; let rn = instr2[0..4].load::<u8>(); let imm3 = lower2[12..15].load::<u16>(); let rd = lower2[8..12].load::<u8>(); let imm8 = lower2[0..8].load::<u16>(); let imm = (i << 11) | (imm3 << 8) | imm8; inst.s = s; let imm = ThumbExpandImm_C(imm); match op { 0b0000 => { if rd == 0b1111 && s { // `TST` (`A8-745`) // v6T2 inst.s = false; inst.opcode = Opcode::TST; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } else { // `AND` (`A8-322`) // v6T2 inst.set_w(false); inst.opcode = Opcode::AND; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } } 0b0001 => { // `BIC` (`A8-338`) // v6T2 inst.set_w(false); inst.opcode = Opcode::BIC; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } 0b0010 => { if rn == 0b1111 { // `MOV` (`A8-485`) // v6T2 inst.opcode = Opcode::MOV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } else { // `ORR` (`A8-517`) // v6T2 inst.set_w(false); inst.opcode = Opcode::ORR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } } 0b0011 => { if rn == 0b1111 { // `MVN` (`A8-505`) // v6T2 inst.opcode = Opcode::MOV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } else { // `ORN` (`A8-513`) // v6T2 inst.set_w(false); inst.opcode = Opcode::ORN; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } } 0b0100 => { if rd == 0b1111 && s { // `TEQ` (`A8-739`) // v6T2 inst.s = false; inst.opcode = Opcode::TEQ; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } else { // `EOR` (`A8-383`) // v6T2 inst.set_w(false); inst.opcode = Opcode::EOR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } } 0b1000 => { if rd == 0b1111 && s { // `CMN` (`A8-362`) // v6T2 inst.s = false; inst.opcode = Opcode::CMN; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } else { // `ADD` (`A8-304`) // v6T2 inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } } 0b1010 => { // `ADC` (`A8-298`) // v6T2 inst.set_w(false); inst.opcode = Opcode::ADC; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } 0b1011 => { // `SBC` (`A8-593`) // v6T2 inst.set_w(false); inst.opcode = Opcode::SBC; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } 0b1101 => { if rd == 0b1111 && s { // `CMP` (`A8-368`) // v6T2 inst.s = false; inst.opcode = Opcode::CMP; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } else { // `SUB` (`A8-709`) // v6T2 inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } } 0b1110 => { // `RSB` (`A8-575`) // v6T2 inst.opcode = Opcode::RSB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } _ => { // undefined encoding return Err(DecodeError::Undefined); } } } else { // `Data-processing (plain binary immediate)` (`A6-232`) // v6T2 let op = instr2[4..9].load::<u8>(); let i = instr2[10..11].load::<u16>(); inst.s = false; inst.set_w(false); let rn = instr2[0..4].load::<u8>(); let imm3 = lower2[12..15].load::<u16>(); let rd = lower2[8..12].load::<u8>(); let imm8 = lower2[0..8].load::<u16>(); let imm = (i << 11) | (imm3 << 8) | imm8; match op { 0b00000 => { if rn != 0b1111 { // `ADD` (`A8-304`) // v6T2 // encoding T4 inst.set_w(true); inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } else { // `ADR` (`A8-320`) // v6T2 // TODO: add = TRUE; inst.opcode = Opcode::ADR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } } 0b00100 => { // `MOV` (`A8-485`) // encoding T3 inst.opcode = Opcode::MOV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm as u32 | ((rn as u32) << 16)), Operand::Nothing, Operand::Nothing, ]; } 0b01010 => { if rn != 0b1111 { // `SUB` (`A8-709`) // v6T2 // encoding T4 inst.set_w(true); inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(imm as u32), Operand::Nothing, ]; } else { // `ADR` (`A8-320`) // v6T2 // TODO: add = FALSE; inst.opcode = Opcode::ADR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, ]; } } 0b01100 => { // `MOVT` (`A8-492`) // v6T2 inst.opcode = Opcode::MOVT; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm as u32 | ((rn as u32) << 16)), Operand::Nothing, Operand::Nothing, ]; } 0b10000 => { // `SSAT` (`A8-653`) // v6T2 let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); let sh = 0; // from the opcode let shift = DecodeImmShift(rn, sh << 1, imm3_2 as u8); inst.opcode = Opcode::SSAT; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32((lower & 0b11111) as u32), Operand::RegShift(shift), Operand::Nothing, ]; } 0b10010 => { let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); if imm3_2 != 0 { let shift = DecodeImmShift(rn, instr2[5..6].load::<u8>() << 1, imm3_2 as u8); // `SSAT` // v6T2 inst.opcode = Opcode::SSAT; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32((lower & 0b11111) as u32), Operand::RegShift(shift), Operand::Nothing, ]; } else { // `SSAT16` // v6T2 inst.opcode = Opcode::SSAT16; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32((lower & 0b11111) as u32), Operand::Reg(Reg::from_u8(rn)), Operand::Nothing, ]; } } 0b10100 => { // `SBFX` (`A8-599`) // v6T2 inst.opcode = Opcode::SBFX; let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm12(imm3_2), Operand::Imm12((lower & 0b11111) + 1), ]; } 0b10110 => { if rn != 0b1111 { // `BFI` (`A8-336`) // v6T2 inst.opcode = Opcode::BFI; let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm12(imm3_2), // TODO: this is `msb` but the operand here should be `width` Operand::Imm12(lower & 0b11111), ]; } else { // `BFC` (`A8-334`) // v6T2 inst.opcode = Opcode::BFC; let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm12(imm3_2), // TODO: this is `msb` but the operand here should be `width` Operand::Imm32((lower & 0b1111) as u32), ]; } } 0b11000 => { // `USAT` (`A8-797`) // v6T2 let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); let sh = 0; // from the opcode let shift = DecodeImmShift(rn, sh << 1, imm3_2 as u8); inst.opcode = Opcode::USAT; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32((lower & 0b11111) as u32), Operand::RegShift(shift), Operand::Nothing, ]; } 0b11010 => { let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); if imm3_2 != 0 { let sh = 1; // from the opcode let shift = DecodeImmShift(rn, sh << 1, imm3_2 as u8); // `USAT` // v6T2 inst.opcode = Opcode::USAT; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32((lower & 0b11111) as u32), Operand::RegShift(shift), Operand::Nothing, ]; } else { // `USAT16` // v6T2 inst.opcode = Opcode::USAT16; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32((lower & 0b11111) as u32), Operand::Reg(Reg::from_u8(rn)), Operand::Nothing, ]; } } 0b11100 => { // `UBFX` (`A8-757`) // v6T2 inst.opcode = Opcode::UBFX; let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm12(imm3_2), Operand::Imm12((lower & 0b11111) + 1), ]; } _ => { return Err(DecodeError::Undefined); } } } } else { // op == 1 // `Branches and miscellaneous control` (`A6-233`) let imm8 = lower2[0..8].load::<u16>(); let op2 = lower2[8..11].load::<u8>(); let op1 = instr2[12..15].load::<u8>(); let op = instr2[4..11].load::<u8>(); if op1 & 0b101 == 0b000 { // TODO: This entire section appears wrong? what encoding is the conditional // branch, none of those line up with the above components, or provided // operands. // // the high bit of op is a sign bit, if a conditional branch. otherwise, it is // 0 for valid instructiosn other than `udf`, `hvc`, and `smc`. `Branch` is // ruled out as `op1` is `0x1`, so see if this is any of the misc // instructions: if op & 0b0111000 != 0b0111000 { // `Conditional branch` (`A8-332`) // v6T2 inst.condition = ConditionCode::build(((instr >> 8) & 0b1111) as u8); inst.opcode = Opcode::B; inst.operands = [ Operand::BranchThumbOffset(((instr & 0b11111111) + 1) as i8 as i32), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // some misc instruction, rule out `udf`, `hvc`, `smc`: if op < 0b1000000 { // misc instruction if op < 0b0111010 { // `MSR` in some form, slightly more work to figure this out let rn = instr2[0..4].load::<u8>(); if imm8 & 0b00100000 != 0 { // `MSR` (`B9-1980`) // v7VE let sysm = (((lower >> 4) & 1) << 4) | ((lower >> 8) & 0b1111); let R = instr2[4]; inst.opcode = Opcode::MSR; inst.operands = [ // TODO: is this the appropriate banked reg? Reg::from_sysm(R, sysm as u8).expect("from_sysm works"), Operand::Reg(Reg::from_u8(rn)), Operand::Nothing, Operand::Nothing, ]; } else { if op == 0b0111000 { if op2 & 0b0011 == 0b00 { // `Move to Special register, Application level` (`A8-501`) let mask = (lower >> 10) & 0b11; let spec_reg = match mask { 0b00 => { // TODO: generally "unpredictable" is // overridden by DecodeMode::Any but there's // nothing to salvage here? return Err(DecodeError::Unpredictable); } 0b01 => { StatusRegMask::APSR_G } 0b10 => { StatusRegMask::APSR_NZCVQ } 0b11 => { StatusRegMask::APSR_NZCVQG } _ => { unreachable!("impossible mask bits"); } }; inst.opcode = Opcode::MSR; inst.operands = [ Operand::StatusRegMask(spec_reg), Operand::Reg(Reg::from_u8(rn)), Operand::Nothing, Operand::Nothing, ]; } else { // `Move to Special register, System level` (`B9-1984`) let mask = lower2[8..12].load::<u8>(); let R = instr2[4]; inst.opcode = Opcode::MSR; inst.operands = [ // TODO: is this the appropriate? Reg::from_sysm(R, mask).expect("from_sysm works"), Operand::Reg(Reg::from_u8(rn)), Operand::Nothing, Operand::Nothing, ]; } } else { // `Move to Special register, System level` (`B9-1984`) let mask = lower2[8..12].load::<u8>(); let R = instr2[4]; inst.opcode = Opcode::MSR; inst.operands = [ // TODO: is this the appropriate? Reg::from_sysm(R, mask).expect("from_sysm works"), Operand::Reg(Reg::from_u8(rn)), Operand::Nothing, Operand::Nothing, ]; } } } else if op < 0b0111011 { // `Change Processor State, and hints` (`A6-234`) let op1 = lower2[8..11].load::<u8>(); let op2 = lower2[0..8].load::<u8>(); if op1 != 0b000 { // `CPS (Thumb)` (`B9-1964`) // v6T2 let _mode = lower & 0b11111; let _aif = (lower >> 5) & 0b111; let _m = (lower >> 8) & 1; let _imod = (lower >> 9) & 0b11; // TODO: no CPS instruction? yet? eprintln!("cps support is not complete"); return Err(DecodeError::Incomplete); // inst.opcode = Opcode::CPS; } else { if op2 >= 0b11110000 { // `DBG` (`A8-378`) let option = lower2[0..4].load::<u16>(); inst.opcode = Opcode::DBG; inst.operands = [ Operand::Imm12(option), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { match op2 { 0b00000000 => { // `NOP` (`A8-511`) // v6T2 // TODO: should_is_must inst.opcode = Opcode::NOP; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b00000001 => { // `YIELD` (`A8-1109`) // v7 inst.opcode = Opcode::YIELD; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b00000010 => { // `WFE` (`A8-1105`) // v7 inst.opcode = Opcode::WFE; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b00000011 => { // `WFI` (`A8-1107`) // v7 inst.opcode = Opcode::WFI; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b00000100 => { // `SEV` (`A8-607`) // v7 inst.opcode = Opcode::SEV; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b00010100 => { // `CSDB` (`A8-376`) // v6T2 inst.opcode = Opcode::CSDB; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } _ => { return Err(DecodeError::Undefined); } } } } } else if op < 0b0111100 { // `Miscellaneous control instructions` (`A6-235`) let op = (lower >> 4) & 0b1111; match op { 0b0000 => { // `ENTERX` (`A9-1116`) // ThumbEE inst.opcode = Opcode::LEAVEX; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; }, 0b0001 => { // `ENTERX` (`A9-1116`) // ThumbEE inst.opcode = Opcode::ENTERX; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; }, 0b0010 => { // `CLREX` (`A8-358`) // v7 inst.opcode = Opcode::CLREX; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; }, 0b0100 => { // `DSB` (`A8-381`) // v7 let option = lower & 0b1111; inst.opcode = Opcode::DSB; inst.operands = [ Operand::Imm12(option), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; }, 0b0101 => { // `DMB` (`A8-379`) // v7 let option = lower & 0b1111; inst.opcode = Opcode::DMB; inst.operands = [ Operand::Imm12(option), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; }, 0b0110 => { // `ISB` (`A8-390`) // v7 let option = lower & 0b1111; inst.opcode = Opcode::ISB; inst.operands = [ Operand::Imm12(option), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; }, _ => { return Err(DecodeError::Undefined); } } } else if op < 0b0111101 { // `BXJ` (`A8-352`) // v6T2 let rm = instr2[0..4].load::<u8>(); inst.opcode = Opcode::BXJ; inst.operands = [ Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else if op < 0b0111110 { // `ERET` or `SUBS PC, LR` // v6T2 // `v7VE` defines `ERET` here, identical to `subs pc, lr` with // `imm8 == 0`. `v7VE` does not change the behavior of this // instruction at `PL1`. let imm8 = lower2[0..8].load::<u16>(); if imm8 == 0 { // `ERET` (`B9-1968`) // v6T2 // if <v7VE, `subs pc, lr, #0` inst.opcode = Opcode::ERET; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `SUBS PC, LR (Thumb)` (`B9-1996`) // v6T2 inst.opcode = Opcode::SUB; inst.s = true; inst.operands = [ Operand::Reg(Reg::from_u8(15)), // pc Operand::Reg(Reg::from_u8(14)), // lr Operand::Imm12(imm8), Operand::Nothing, ]; } } else { // `op` is `0b0111110` or `0b0111111`, both are `MRS` but there's // some discerning to do still. let imm8 = lower & 0b11111111; if imm8 & 0b00100000 != 0 { // `MRS (Banked register)` (`B9-1978`) // v7VE let r = instr & 0b10000; let sysm = (lower & 0b10000) | (instr & 0b1111); let rd = ((lower >> 8) & 0b1111) as u8; inst.opcode = Opcode::MRS; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Reg::from_sysm(r != 0, sysm as u8).expect("from_sysm works"), Operand::Nothing, Operand::Nothing, ]; } else { if op == 0b0111110 { // `MRS` (`A8-497`) // v6T2 inst.opcode = Opcode::MRS; let rd = ((lower >> 8) & 0b1111) as u8; inst.opcode = Opcode::MRS; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), // TODO: "<spec_reg>"? Reg::from_sysm(false, 0).expect("from_sysm works"), Operand::Nothing, Operand::Nothing, ]; } else { // `MRS` (`B9-1976`) // v6T2 inst.opcode = Opcode::MRS; let rd = ((lower >> 8) & 0b1111) as u8; let r = (instr >> 4) & 1; inst.opcode = Opcode::MRS; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), // TODO: "<spec_reg>"? Reg::from_sysm(r != 0, 0).expect("from_sysm works"), Operand::Nothing, Operand::Nothing, ]; } } } } else { if op == 0b1111111 { if op1 == 0b000 { // `SMC` (aka `SMI`) (`B9-1988`) // "Security Extensions" let imm = instr & 0b1111; inst.opcode = Opcode::SMC; inst.operands = [ Operand::Imm12(imm), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `UDF` (`A8-759`) // All (first defined in issue `C.a`) // TODO: should this decode to an intentional `UDF` // instruction? return Err(DecodeError::Undefined); } } else if op == 0b1111110 { if op1 == 0b000 { // `HVC` (`B8-1970`) // v7VE let imm = lower & 0b1111_1111_1111; inst.opcode = Opcode::HVC; inst.operands = [ Operand::Imm12(imm), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // undefined, but by not being mentioned in the manual return Err(DecodeError::Undefined); } } else { // undefined, but by not being mentioned in the manual return Err(DecodeError::Undefined); } } } } else if op1 & 0b101 == 0b001 { // `Branch` (`A8-332`) // v6T2 // TODO: encoding T3? unclear let imm11 = lower2[0..11].load::<u32>(); let imm6 = instr2[0..6].load::<u32>(); let j1 = lower2[13..14].load::<u32>(); let j2 = lower2[11..12].load::<u32>(); let s = instr2[10..11].load::<u32>(); let imm = (imm11 as u32) | ((imm6 as u32) << 11) | ((j1 as u32) << 17) | ((j2 as u32) << 18) | ((s as u32) << 19); let imm = (imm << 12) >> 12; inst.opcode = Opcode::B; inst.operands = [ Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else if op1 & 0b101 == 0b100 { // `Branch with Link and Exchange` (`A8-346`) // `UNDEFINED` in v4T // v5T let imm11 = lower2[0..11].load::<u32>(); let imm10 = instr2[0..10].load::<u32>(); let j1 = lower2[13..14].load::<u32>(); let j2 = lower2[11..12].load::<u32>(); let s = instr2[10..11].load::<u32>(); let imm = (imm11 as u32) | ((imm10 as u32) << 11) | ((j1 as u32) << 21) | ((j2 as u32) << 22) | ((s as u32) << 23); let imm = (imm << 8) >> 8; inst.opcode = Opcode::BLX; inst.operands = [ Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `Brach with Link` (`A8-346`) // v4T let imm11 = lower2[0..11].load::<u32>(); let imm10 = instr2[0..10].load::<u32>(); let j1 = lower2[13..14].load::<u32>(); let j2 = lower2[11..12].load::<u32>(); let s = instr2[10..11].load::<u32>(); let imm = (imm11 as u32) | ((imm10 as u32) << 11) | ((j1 as u32) << 21) | ((j2 as u32) << 22) | ((s as u32) << 23); let imm = (imm << 8) >> 8; inst.opcode = Opcode::BL; inst.operands = [ Operand::Imm32(imm as u32), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } } } else { // working through table `A6-9 32-bit Thumb instruction encoding` // op1 == 0b11 if !op2[6] { // not coprocessor, advanced simd, or floating point instructions if !op2[5] { // loads, stores if !op2[0] { // store single item, or advanced simd load/store if !op2[4] { // `Store single data item` (`A6-240`) let rn = instr2[0..4].load::<u8>(); let op1 = instr2[5..8].load::<u8>(); let size_bits = op1 & 0b011; let has_imm12 = op1 & 0b100 != 0; let op2 = (lower >> 6) & 0b111111; match size_bits { 0b00 => { // `STRB_` if op2 == 0 { // `STRB (register)` (`A8-683`) // encoding T2 // v6T2 let rm = (lower & 0b1111) as u8; let imm2 = (lower >> 4) & 0b11; let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STRB; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexRegShift( Reg::from_u8(rn), RegShift::from_raw( // do things 0b00000 | // imm shift (imm2 << 7) | // imm rm as u16 | // shiftee (0b00 << 5) // shift style (lsl) ), true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else if (op2 & 0b111100) == 0b111000 { // `STRBT` (`A8-685`) // v6T2 let imm8 = lower & 0b1111_1111; let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STRBT; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm8, true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else { // `STRB (immediate, Thumb)` (`A8-679`) // encoding T2/T3 // v6T2 let (imm, p, u, w) = if has_imm12 { let imm12 = lower & 0b1111_1111_1111; let p = true; let u = true; let w = false; (imm12, p, u, w) } else { let imm8 = lower & 0b1111_1111; let puw = (lower >> 8) & 0b111; let p = puw & 0b100 != 0; let u = puw & 0b010 != 0; let w = puw & 0b001 != 0; (imm8, p, u, w) }; // assert!(puw != 0b110) // would be `strbt` let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STRB; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), // do the puw if p { Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm, u, // add w, // wback ) } else { Operand::RegDerefPostindexOffset( Reg::from_u8(rn), imm, u, // add w, // wback ) }, Operand::Nothing, Operand::Nothing, ]; } } 0b01 => { // `STRH_` // v6T2 if op2 == 0 { // `STRH (register)` (`A8-703`) let rm = (lower & 0b1111) as u8; let imm2 = (lower >> 4) & 0b11; let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STRH; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexRegShift( Reg::from_u8(rn), RegShift::from_raw( // do things 0b00000 | // imm shift (imm2 << 7) | // imm rm as u16 | // shiftee (0b00 << 5) // shift style (lsl) ), true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else if (op2 & 0b111100) == 0b111000 { // `STRHT` (`A8-705`) let imm8 = lower & 0b1111_1111; let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STRHT; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm8, true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else { // `STRH (immediate, Thumb)` (`A8-699`) // encoding T2/T3 // v6T2 let (imm, p, u, w) = if has_imm12 { let imm12 = lower & 0b1111_1111_1111; let p = true; let u = true; let w = false; (imm12, p, u, w) } else { let imm8 = lower & 0b1111_1111; let puw = (lower >> 8) & 0b111; let p = puw & 0b100 != 0; let u = puw & 0b010 != 0; let w = puw & 0b001 != 0; (imm8, p, u, w) }; // assert!(puw != 0b110) // would be `strbt` let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STRH; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), // do the puw if p { Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm, u, // add w, // wback ) } else { Operand::RegDerefPostindexOffset( Reg::from_u8(rn), imm, u, // add w, // wback ) }, Operand::Nothing, Operand::Nothing, ]; } } 0b10 => { // `STR_` if op2 == 0 { // `STR (register)` (`A8-677`) // v6T2 let rm = (lower & 0b1111) as u8; let imm2 = (lower >> 4) & 0b11; let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STR; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexRegShift( Reg::from_u8(rn), RegShift::from_raw( // do things 0b00000 | // imm shift (imm2 << 7) | // imm rm as u16 | // shiftee (0b00 << 5) // shift style (lsl) ), true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else if (op2 & 0b111100) == 0b111000 { // `STRT` (`A8-707`) let imm8 = lower & 0b1111_1111; let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STRT; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm8, true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else { // `STR (immediate, Thumb)` (`A8-673`) // encoding T3/T4 // v6T2 let (imm, p, u, w) = if has_imm12 { let imm12 = lower & 0b1111_1111_1111; let p = true; let u = true; let w = false; (imm12, p, u, w) } else { let imm8 = lower & 0b1111_1111; let puw = (lower >> 8) & 0b111; let p = puw & 0b100 != 0; let u = puw & 0b010 != 0; let w = puw & 0b001 != 0; (imm8, p, u, w) }; // assert!(puw != 0b110) // would be `strbt` let rt = ((lower >> 12) & 0b1111) as u8; inst.opcode = Opcode::STR; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), // do the puw if p { Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm, u, // add w, // wback ) } else { Operand::RegDerefPostindexOffset( Reg::from_u8(rn), imm, u, // add w, // wback ) }, Operand::Nothing, Operand::Nothing, ]; } } 0b11 => { return Err(DecodeError::Undefined); } _ => { unreachable!("impossible bit pattern"); } } } else { // `Advanced SIMD element or structure load/store instructions` // (`A7-273`) } } else { // this section is a merger of three tables: // `A6.3.9 Load byte, memory hints` // `A6.3.8 Load halfword, memory hints` // `A6.3.7 Load word` // reached by the `00xx001`, `00xx011`, `00xx101` rows of table `A6-9`. let op2 = lower2[6..12].load::<u16>(); let rt = lower2[12..16].load::<u8>(); let rn = instr2[0..4].load::<u8>(); let op1 = instr2[7..9].load::<u16>(); // load {byte, halfword, word} in table `A6-9` let size = instr2[5..7].load::<usize>(); if size == 0b11 { // `UNDEFINED` return Err(DecodeError::Undefined); } /* * trying to reorder tables a6-20, a6-19, and a6-18 to factor out operand * sizes where possible... * * op1 | op2 | rn | rt | size| `see` * 00 |000000|!=1111|!=1111| 00 |`LDRB (register) A8-423` * 00 |000000|!=1111|==1111| 00 |`PLD, PLDW (register) A8-529` * 00 |000000|!=1111|!=1111| 01 |`LDRH (register) A8-447` * 00 |000000|!=1111|==1111| 01 |`PLD, PLDW (register) A8-529` * 00 |000000|!=1111|------| 10 |`LDR (register, Thumb) A8-413` * * 00 |1100xx|!=1111|!=1111| 00 |`LDRB (immediate, Thumb) A8-417` * 00 |1100xx|!=1111|==1111| 00 |`PLD, PLDW (immediate) A8-525` * 00 |1100xx|!=1111|!=1111| 01 |`LDRH (immediate, Thumb) A8-441` * 00 |1100xx|!=1111|==1111| 01 |`PLD, PLDW (immediate) A8-525` * 00 |1100xx|!=1111|------| 10 |`LDR (immediate, Thumb) A8-407` * * 00 |1110xx|!=1111|------| 00 |`LDRBT A8-425` * 00 |1110xx|!=1111|------| 01 |`LDRHT A8-449` * 00 |1110xx|!=1111|------| 10 |`LDRT A8-467` * * 00 |1xx1xx|!=1111|------| 00 |`LDRB (immediate, Thumb) A8-417` * 00 |1xx1xx|!=1111|------| 01 |`LDRH (immediate, Thumb) A8-441` * 00 |1xx1xx|!=1111|------| 10 |`LDR (immediate, Thumb) A8-407` * * 01 |------|!=1111|!=1111| 00 |`LDRB (immediate, Thumb) A8-417` * 01 |------|!=1111|==1111| 00 |`PLD, PLDW (immediate) A8-525` * 01 |------|!=1111|!=1111| 01 |`LDRH (immediate, Thumb) A8-441` * 01 |------|!=1111|==1111| 01 |`PLD, PLDW (immediate) A8-525` * 01 |------|!=1111|------| 10 |`LDR (immediate, Thumb) A8-407` * * 0x |------|==1111|!=1111| 00 |`LDRB (literal) A8-421` * 0x |------|==1111|==1111| 00 |`PLD (literal) A8-527` * 0x |------|==1111|!=1111| 01 |`LDRH (literal) A8-445` * 0x |------|==1111|==1111| 01 |`PLD (literal) A8-527` * 0x |------|==1111|------| 10 |`LDR (literal) A8-411` * * 1x |------|------|------| 10 |`UNDEFINED (cite: A6.3.7)` * * 10 |000000|!=1111|!=1111| 00 |`LDRSB (register) A8-455` * 10 |000000|!=1111|!=1111| 01 |`LDRSH (register) A8-463` * 10 |000000|!=1111|==1111| 00 |`PLI (register) A8-553` * 10 |000000|!=1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` * * 10 |1100xx|!=1111|!=1111| 00 |`LDRSB (immediate) A8-451` * 10 |1100xx|!=1111|==1111| 00 |`PLI (immediate, literal) A8-531` * 10 |1100xx|!=1111|!=1111| 01 |`LDRSH (immediate) A8-459` * 10 |1100xx|!=1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` * * 10 |1110xx|!=1111|------| 00 |`LDRSBT A8-457` * 10 |1110xx|!=1111|------| 01 |`LDRSHT A8-465` * * 10 |1xx1xx|!=1111|------| 00 |`LDRSB (immediate) A8-451` * 10 |1xx1xx|!=1111|------| 01 |`LDRSH (immediate) A8-459` * * 11 |------|!=1111|!=1111| 00 |`LDRSB (immediate) A8-451` * 11 |------|!=1111|==1111| 00 |`PLI (immediate, literal) A8-531` * 11 |------|!=1111|!=1111| 01 |`LDRSH (immediate) A8-459` * 11 |------|!=1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` * 1x |------|==1111|!=1111| 00 |`LDRSB (literal) A8-453` * 1x |------|==1111|==1111| 00 |`PLI (immediate, literal) A8-531` * 1x |------|==1111|!=1111| 01 |`LDRSH (literal) A8-461` * 1x |------|==1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` */ if op1 == 0b00 { // op1 == bits 7:8 /* * op1 | op2 | rn | rt | size| `see` * 0x |------|==1111|!=1111| 00 |`LDRB (literal) A8-421` * 0x |------|==1111|==1111| 00 |`PLD (literal) A8-527` * 0x |------|==1111|!=1111| 01 |`LDRH (literal) A8-445` * 0x |------|==1111|==1111| 01 |`PLD (literal) A8-527` * 0x |------|==1111|------| 10 |`LDR (literal) A8-411` * * 00 |000000|!=1111|!=1111| 00 |`LDRB (register) A8-423` * 00 |000000|!=1111|==1111| 00 |`PLD, PLDW (register) A8-529` * 00 |000000|!=1111|!=1111| 01 |`LDRH (register) A8-447` * 00 |000000|!=1111|==1111| 01 |`PLD, PLDW (register) A8-529` * 00 |000000|!=1111|------| 10 |`LDR (register, Thumb) A8-413` * * 00 |1100xx|!=1111|!=1111| 00 |`LDRB (immediate, Thumb) A8-417` * 00 |1100xx|!=1111|==1111| 00 |`PLD, PLDW (immediate) A8-525` * 00 |1100xx|!=1111|!=1111| 01 |`LDRH (immediate, Thumb) A8-441` * 00 |1100xx|!=1111|==1111| 01 |`PLD, PLDW (immediate) A8-525` * 00 |1100xx|!=1111|------| 10 |`LDR (immediate, Thumb) A8-407` * * 00 |1110xx|!=1111|------| 00 |`LDRBT A8-425` * 00 |1110xx|!=1111|------| 01 |`LDRHT A8-449` * 00 |1110xx|!=1111|------| 10 |`LDRT A8-467` * * 00 |1xx1xx|!=1111|------| 00 |`LDRB (immediate, Thumb) A8-417` * 00 |1xx1xx|!=1111|------| 01 |`LDRH (immediate, Thumb) A8-441` * 00 |1xx1xx|!=1111|------| 10 |`LDR (immediate, Thumb) A8-407` */ if rn == 0b1111 { // `(literal)` let opcode = if rt == 0b1111 { [ Opcode::PLD, Opcode::PLD, Opcode::LDR, ][size] } else { [ Opcode::LDRB, Opcode::LDRH, Opcode::LDR, ][size] }; let u = false; // instr2[7], but known 0 here let imm12 = lower2[..12].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm12, false, false), // no add, no wback Operand::Nothing, Operand::Nothing, ]; } else { if op2 == 0b000000 { // `(register, Thumb)` let opcode = if rt == 0b1111 { [ Opcode::PLD, Opcode::PLD, Opcode::LDR, ][size] } else { [ Opcode::LDRB, Opcode::LDRH, Opcode::LDR, ][size] }; let rm = lower2[0..4].load::<u8>(); let imm2 = lower2[4..6].load::<u8>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexRegShift( Reg::from_u8(rn), RegShift::from_raw( 0b00000 | // `RegImm` rm as u16 | ((0 /* lsl */) << 5)| ((imm2 as u16) << 7) ), true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else if op2 & 0b111100 == 0b110000 { // `(immediate, Thumb)` let opcode = if rt == 0b1111 { [ Opcode::PLD, Opcode::PLD, Opcode::LDR, ][size] } else { [ Opcode::LDRB, Opcode::LDRH, Opcode::LDR, ][size] }; let w = lower2[8]; let u = lower2[9]; let p = lower2[10]; let imm8 = lower2[..8].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8, u, w) } else { Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8, u, false) }, Operand::Nothing, Operand::Nothing, ]; } else if op2 & 0b111100 == 0b111000 { // `(immediate, Thumb)` let opcode = if rt == 0b1111 { [ Opcode::PLD, Opcode::PLD, Opcode::LDRT, ][size] } else { [ Opcode::LDRBT, Opcode::LDRHT, Opcode::LDRT, ][size] }; let w = lower2[8]; let u = lower2[9]; let p = lower2[10]; let imm8 = lower2[..8].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8, u, w) } else { Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8, u, false) }, Operand::Nothing, Operand::Nothing, ]; } else if op2 & 0b100100 == 0b100100 { // `(immediate, Thumb)` let opcode = if rt == 0b1111 { [ Opcode::PLD, Opcode::PLD, Opcode::LDR, ][size] } else { [ Opcode::LDRB, Opcode::LDRH, Opcode::LDR, ][size] }; let w = lower2[8]; let u = lower2[9]; let p = lower2[10]; let imm8 = lower2[..8].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8, u, w) } else { Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8, u, false) }, Operand::Nothing, Operand::Nothing, ]; } else { // op2 =~ 0b1010xx or something? // nothing to try decoding as for a `decoder.undefined()?`, so // just error. return Err(DecodeError::Undefined); } } } else if op1 == 0b01 { // op1 == bits 7:8 /* * op1 | op2 | rn | rt | size| `see` * 0x |------|==1111|!=1111| 00 |`LDRB (literal) A8-421` * 0x |------|==1111|==1111| 00 |`PLD (literal) A8-527` * 0x |------|==1111|!=1111| 01 |`LDRH (literal) A8-445` * 0x |------|==1111|==1111| 01 |`PLD (literal) A8-527` * 0x |------|==1111|------| 10 |`LDR (literal) A8-411` * * 01 |------|!=1111|!=1111| 00 |`LDRB (immediate, Thumb) A8-417` * 01 |------|!=1111|==1111| 00 |`PLD, PLDW (immediate) A8-525` * 01 |------|!=1111|!=1111| 01 |`LDRH (immediate, Thumb) A8-441` * 01 |------|!=1111|==1111| 01 |`PLD, PLDW (immediate) A8-525` * 01 |------|!=1111|------| 10 |`LDR (immediate, Thumb) A8-407` */ if rn == 0b1111 { let opcode = if rt == 0b1111 { [ Opcode::PLD, Opcode::PLD, Opcode::LDR, ][size] } else { [ Opcode::LDRB, Opcode::LDRH, Opcode::LDR, ][size] }; let u = true; // instr2[7], but known 1 here let imm12 = lower2[..12].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm12, true, false), // add, no wback Operand::Nothing, Operand::Nothing, ]; } else { let opcode = if rt == 0b1111 { [ Opcode::PLD, Opcode::PLD, Opcode::LDR, ][size] } else { [ Opcode::LDRB, // encoding T2 Opcode::LDRH, // encoding T2 Opcode::LDR, // encoding T3 ][size] }; let imm12 = lower2[..12].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm12, true, false), // add, no wback Operand::Nothing, Operand::Nothing, ]; } } else if op1 == 0b10 { // op1 == bits 7:8 if size == 0b10 { return Err(DecodeError::Undefined); } /* * op1 | op2 | rn | rt | size| `see` * 1x |------|------|------| 10 |`UNDEFINED (cite: A6.3.7)` * 1x |------|==1111|!=1111| 00 |`LDRSB (literal) A8-453` * 1x |------|==1111|==1111| 00 |`PLI (immediate, literal) A8-531` * 1x |------|==1111|!=1111| 01 |`LDRSH (literal) A8-461` * 1x |------|==1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` * * 10 |000000|!=1111|!=1111| 00 |`LDRSB (register) A8-455` * 10 |000000|!=1111|!=1111| 01 |`LDRSH (register) A8-463` * 10 |000000|!=1111|==1111| 00 |`PLI (register) A8-553` * 10 |000000|!=1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` * * 10 |1100xx|!=1111|!=1111| 00 |`LDRSB (immediate) A8-451` * 10 |1100xx|!=1111|==1111| 00 |`PLI (immediate, literal) A8-531` * 10 |1100xx|!=1111|!=1111| 01 |`LDRSH (immediate) A8-459` * 10 |1100xx|!=1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` * * 10 |1110xx|!=1111|------| 00 |`LDRSBT A8-457` * 10 |1110xx|!=1111|------| 01 |`LDRSHT A8-465` * * 10 |1xx1xx|!=1111|------| 00 |`LDRSB (immediate) A8-451` * 10 |1xx1xx|!=1111|------| 01 |`LDRSH (immediate) A8-459` */ if rn == 0b1111 { // (literal) let opcode = if rt == 0b1111 { [ Opcode::PLI, Opcode::NOP, ][size] } else { [ Opcode::LDRSB, Opcode::LDRSH, ][size] }; let u = false; // instr[7] known false here let imm12 = lower2[..12].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(15), imm12, false, false), // add, no wback Operand::Nothing, Operand::Nothing, ]; } else { if op2 == 0b000000 { let opcode = if rt == 0b1111 { [ Opcode::PLI, Opcode::NOP, ][size] } else { [ Opcode::LDRSB, // encoding T2 Opcode::LDRSH, // encoding T2 ][size] }; let rm = lower2[0..4].load::<u8>(); let imm2 = lower2[4..6].load::<u8>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexRegShift( Reg::from_u8(rn), RegShift::from_raw( 0b00000 | // `RegImm` rm as u16 | ((0 /* lsl */) << 5)| ((imm2 as u16) << 7) ), true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else if op2 & 0b111100 == 0b110000 { let opcode = if rt == 0b1111 { [ Opcode::PLI, Opcode::NOP, ][size] } else { [ Opcode::LDRSB, // encoding T2 Opcode::LDRSH, // encoding T2 ][size] }; let w = lower2[8]; let u = lower2[9]; let p = lower2[10]; let imm8 = lower2[..8].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8, u, w) } else { Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8, u, false) }, Operand::Nothing, Operand::Nothing, ]; } else if op2 & 0b111100 == 0b111000 { let opcode = [ Opcode::LDRSBT, // encoding T1 Opcode::LDRSHT, // encoding T1 ][size]; let imm8 = lower2[..8].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8, true, false), // add, no wback Operand::Nothing, Operand::Nothing, ]; } else if op2 & 0b100100 == 0b100100 { let opcode = [ Opcode::LDRSB, // encoding T2 Opcode::LDRSH, // encoding T2 ][size]; let w = lower2[8]; let u = lower2[9]; let p = lower2[10]; let imm8 = lower2[..8].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), if p { Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8, u, w) } else { Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8, u, false) }, Operand::Nothing, Operand::Nothing, ]; } else { // op2 =~ 0b1010xx or something? // nothing to try decoding as for a `decoder.undefined()?`, so // just error. return Err(DecodeError::Undefined); } } } else { // op1 == bits 7:8 if size == 0b10 { return Err(DecodeError::Undefined); } // op1 == 0b11 /* * 1x |------|------|------| 10 |`UNDEFINED (cite: A6.3.7)` * 1x |------|==1111|!=1111| 00 |`LDRSB (literal) A8-453` * 1x |------|==1111|==1111| 00 |`PLI (immediate, literal) A8-531` * 1x |------|==1111|!=1111| 01 |`LDRSH (literal) A8-461` * 1x |------|==1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` * * 11 |------|!=1111|!=1111| 00 |`LDRSB (immediate) A8-451` * 11 |------|!=1111|==1111| 00 |`PLI (immediate, literal) A8-531` * 11 |------|!=1111|!=1111| 01 |`LDRSH (immediate) A8-459` * 11 |------|!=1111|==1111| 01 |`Unallocated memory hint (treat as NOP)` */ if rn == 0b1111 { // (literal) let opcode = if rt == 0b1111 { [ Opcode::PLI, Opcode::NOP, ][size] } else { [ Opcode::LDRSB, Opcode::LDRSH, ][size] }; let u = true; // instr[7] known true here let imm12 = lower2[..12].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(15), imm12, u, false), // add, no wback Operand::Nothing, Operand::Nothing, ]; } else { // (immediate) let opcode = if rt == 0b1111 { [ Opcode::PLI, Opcode::NOP, ][size] } else { [ Opcode::LDRSB, // encoding T1 Opcode::LDRSH, // encoding T1 ][size] }; let imm12 = lower2[..12].load::<u16>(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm12, true, false), // add, no wback Operand::Nothing, Operand::Nothing, ]; } } } } else { if !op2[4] { // `Data-processing (register)` (`A6-243`) let op1 = &instr2[4..8]; let op2 = &lower2[4..8]; let rn = instr2[0..4].load::<u8>(); if !op1[3] { // `LSL`, `LSR`, `ASR`, `ROR`, `SXTAH`, .... out of table `A6-24` if !op2[3] { // `LSL`, `LSR`, `ASR`, `ROR` // v6T2 let op = [ Opcode::LSL, Opcode::LSR, Opcode::ASR, Opcode::ROR, ][op2[1..3].load::<usize>()]; let rd = lower2[8..12].load::<u8>(); let rm = lower2[0..4].load::<u8>(); inst.opcode = op; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } else { let op1 = op1[0..3].load::<usize>(); // `SXTAH` and friends if op1 > 0b101 { return Err(DecodeError::Undefined); } if rn == 0b1111 { let op = [ Opcode::SXTH, Opcode::UXTH, Opcode::SXTB16, Opcode::UXTB16, Opcode::SXTB, Opcode::UXTB, ][op1]; let rm = lower2[..4].load::<u8>(); let rotate = lower2[1..3].load::<u8>() << 2;; let rd = lower2[8..12].load::<u8>(); inst.opcode = op; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm32(rotate as u32), Operand::Nothing, ]; } else { let op = [ Opcode::SXTAH, Opcode::UXTAH, Opcode::SXTAB16, Opcode::UXTAB16, Opcode::SXTAH, Opcode::UXTAB, ][op1]; let rm = lower2[..4].load::<u8>(); let rotate = lower2[1..3].load::<u8>() << 2;; let rd = lower2[8..12].load::<u8>(); inst.opcode = op; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm32(rotate as u32), ]; }; } } else { let op2 = op2.load::<u8>(); if op2 < 0b0100 { // `Parallel addition and subtraction, signed` let op1 = instr2[4..7].load::<usize>(); let op2 = lower2[4..6].load::<usize>(); if op1 == 0 || op1 > 0b100 || op2 == 0b11 { return Err(DecodeError::InvalidOpcode); } let opcode_idx = (op1 - 1) * 3 + op2; let rn = instr2[0..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); let rm = lower2[0..4].load::<u8>(); inst.opcode = [ Opcode::SADD16, Opcode::QADD16, Opcode::SHADD16, Opcode::SASX, Opcode::QASX, Opcode::SHASX, Opcode::SSAX, Opcode::QSAX, Opcode::SHSAX, Opcode::SSUB16, Opcode::QSUB16, Opcode::SHSUB16, Opcode::SADD8, Opcode::QADD8, Opcode::SHADD8, Opcode::SSUB8, Opcode::QSUB8, Opcode::SHSUB8, ][opcode_idx]; 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 < 0b1000 { // `Parallel addition and subtraction, unsigned` (`A6-244`) let op1 = instr2[4..7].load::<usize>(); let op2 = lower2[4..6].load::<usize>(); if op1 > 0b100 || op2 == 0b11 { return Err(DecodeError::InvalidOpcode); } let opcode_idx = (op1 - 1) * 3 + op2; let rn = instr2[0..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); let rm = lower2[0..4].load::<u8>(); inst.opcode = [ Opcode::UADD16, Opcode::UQADD16, Opcode::UHADD16, Opcode::UASX, Opcode::UQASX, Opcode::UHASX, Opcode::USAX, Opcode::UQSAX, Opcode::UHSAX, Opcode::USUB16, Opcode::UQSUB16, Opcode::UHSUB16, Opcode::UADD8, Opcode::UQADD8, Opcode::UHADD8, Opcode::USUB8, Opcode::UQSUB8, Opcode::UHSUB8, ][opcode_idx]; 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 < 0b1100 { // `Miscellaneous operations` (`A6-246`) let rn = instr2[0..4].load::<u8>(); let rd = lower2[8..12].load::<u8>(); let rm = lower2[0..4].load::<u8>(); let op1 = instr2[4..6].load::<u8>(); let op2 = lower2[4..6].load::<usize>(); match op1 { 0b00 => { inst.opcode = [ Opcode::QADD, Opcode::QDADD, Opcode::QSUB, Opcode::QDSUB, ][op2]; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Reg(Reg::from_u8(rn)), Operand::Nothing, ]; } 0b01 => { if rn != rm { decoder.unpredictable()?; } inst.opcode = [ Opcode::REV, Opcode::REV16, Opcode::RBIT, Opcode::REVSH, ][op2]; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } 0b10 => { if op2 != 0 { return Err(DecodeError::InvalidOpcode); } inst.opcode = Opcode::SEL; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } 0b11 => { if op2 != 0 { return Err(DecodeError::InvalidOpcode); } inst.opcode = Opcode::CLZ; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!("impossible bit pattern for op1"); } } } else { return Err(DecodeError::Undefined); } } } else { if op2[3] { // `Long multiply, long multiply accumulate, and divide` (`A6-248`) return Err(DecodeError::Incomplete); } else { // `Multiply, multiply accumulate, and absolute difference` (`A6-247`) return Err(DecodeError::Incomplete); } } } } else { // `Coprocessor, Advanced SIMD, and Floating-point instructions` (`A6-249`) return Err(DecodeError::Incomplete); } } } else { // 16b instruction - `A6-221, 16-bit Thumb instruction encoding` // `Table A6-1` if opword < 0b01000 { // `Shift (immediate), add, subtract, move, and compare` page `A6-222` // v4T let opcode = opword & 0b111; // TODO: `S` iff outside `IT` block inst.s = true; match opcode { 0b000 => { // LSL (immediate) // footnote: when opcode is 0, bits 8:6 are 0, encoding is `MOV`. see `A8-487`. let rd = instr2[0..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); let imm5 = instr2[6..11].load::<u16>(); inst.opcode = Opcode::LSL; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(imm5), Operand::Nothing, ]; } 0b001 => { /* LSR on page A8-473 */ let rd = instr2[0..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); let imm5 = instr2[6..11].load::<u16>(); let imm = if imm5 == 0 { 0x20 } else { imm5 }; inst.opcode = Opcode::LSR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(imm), Operand::Nothing, ]; } 0b010 => { /* ASR on page A8-328 */ let rd = instr2[0..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); let imm5 = instr2[6..11].load::<u16>(); let imm = if imm5 == 0 { 0x20 } else { imm5 }; inst.opcode = Opcode::ASR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(imm), Operand::Nothing, ]; } 0b011 => { /* ADD, SUB (register/immediate) */ let oplower = instr2[9..11].load::<u32>(); let rm = instr2[6..9].load::<u8>(); let rd = instr2[0..3].load::<u8>(); let rn = instr2[3..6].load::<u8>(); match oplower { 0b00 => { inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } 0b01 => { inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } 0b10 => { inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(rm as u32), Operand::Nothing, ]; } 0b11 => { inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Imm32(rm as u32), Operand::Nothing, ]; } _ => { unreachable!("impossible bit pattern"); } } } 0b100 => { /* MOV on page A8-485 */ let imm8 = instr2[0..8].load::<u32>(); let rd = instr2[8..11].load::<u8>(); inst.opcode = Opcode::MOV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm8 as u32), Operand::Nothing, Operand::Nothing, ]; } 0b101 => { /* CMP on page A8-368 */ inst.s = false; let imm8 = instr2[0..8].load::<u32>(); let rd = instr2[8..11].load::<u8>(); inst.opcode = Opcode::CMP; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm8 as u32), Operand::Nothing, Operand::Nothing, ]; } 0b110 => { /* ADD (immediate, Thumb) on page A8-304 */ let imm8 = instr2[0..8].load::<u32>(); let rdn = instr2[8..11].load::<u8>(); inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rdn)), Operand::Imm32(imm8 as u32), Operand::Nothing, Operand::Nothing, ]; } 0b111 => { /* SUB (immediate, Thumb) on page A8-709 */ let imm8 = instr2[0..8].load::<u32>(); let rdn = instr2[8..11].load::<u8>(); inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(rdn)), Operand::Imm32(imm8 as u32), Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!("impossible bit pattern"); } } } else if opword < 0b01001 { let opcode_bits = instr2[6..10].load::<u8>(); // `Data-processing` on page `A6-223` or `Special data instructions and branch and // exchange` on page `A6-224` if (instr >> 10) < 0b010001 { // `Data-processing` on page `A6-223` // v4T // TODO: condition inside IT block, no S inst.s = true; let rdn = instr2[0..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); if opcode_bits == 0b1101 { inst.opcode = Opcode::MUL; inst.operands = [ Operand::Reg(Reg::from_u8(rdn)), Operand::Reg(Reg::from_u8(rm)), Operand::Reg(Reg::from_u8(rdn)), Operand::Nothing, ]; } else if opcode_bits == 0b1001 { inst.opcode = Opcode::RSB; inst.operands = [ Operand::Reg(Reg::from_u8(rdn)), Operand::Reg(Reg::from_u8(rm)), Operand::Imm12(0), Operand::Nothing, ]; } else { let opcode = [ Opcode::AND, Opcode::EOR, Opcode::LSL, Opcode::LSR, Opcode::ASR, Opcode::ADC, Opcode::SBC, Opcode::ROR, Opcode::TST, Opcode::RSB, Opcode::CMP, Opcode::CMN, Opcode::ORR, Opcode::MUL, Opcode::BIC, Opcode::MVN, ][opcode_bits as usize]; inst.opcode = opcode; if opcode_bits == 8 || opcode_bits == 10 || opcode_bits == 11 { inst.s = false; } inst.operands = [ Operand::Reg(Reg::from_u8(rdn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } } else { // `Special data instructions and branch and exchange` on page `A6-224` match opcode_bits { 0b0000 => { // `Add Low Registers` (`A8-308`) // v6T2, `UNPREDICTABLE` in earlier versions let rdn = instr2[0..3].load::<u8>() | (instr2[7..8].load::<u8>() << 3); let rm = instr2[3..7].load::<u8>(); inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rdn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; }, 0b0001 | 0b0010 | 0b0011 => { // `Add High Registers` (`A8-308`) // v4T let rdn = instr2[0..3].load::<u8>() | (instr2[7..8].load::<u8>() << 3); let rm = instr2[3..7].load::<u8>(); inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rdn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; }, 0b0100 | 0b0101 | 0b0110 | 0b0111 => { // `Compare High Registers` (`A8-307`) // v4T let rn = instr2[0..3].load::<u8>() | (instr2[7..8].load::<u8>() << 3); let rm = instr2[3..7].load::<u8>(); inst.opcode = Opcode::CMP; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } 0b1000 => { // `Move Low Registers` (`A8-487`) // v6, `UNPREDICTABLE` in earlier versions // (encoding T1) let rd = instr2[0..3].load::<u8>() | (instr2[7..8].load::<u8>() << 3); let rm = instr2[3..7].load::<u8>(); inst.opcode = Opcode::MOV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } 0b1001 | 0b1010 | 0b1011 => { // `Move High Registers` (`A8-487`) // v4T let rd = instr2[0..3].load::<u8>() | (instr2[7..8].load::<u8>() << 3); let rm = instr2[3..7].load::<u8>(); inst.opcode = Opcode::MOV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; }, 0b1100 | 0b1101 => { // `Branch and Exchange` (`A8-350`) // v4T let rm = instr2[3..7].load::<u8>(); inst.opcode = Opcode::BX; inst.operands = [ Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b1110 | 0b1111 => { // `Branch and Link with Exchange` (`A8-348`) // v5T, `UNPREDICTABLE` in earlier versions let rm = instr2[3..7].load::<u8>(); inst.opcode = Opcode::BLX; inst.operands = [ Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!("bad bit pattern"); } } } } else if opword < 0b01010 { // `LDR (literal)` on page `A8-411` -- v4T let imm8 = instr2[0..8].load::<u16>(); let rt = instr2[8..11].load::<u8>(); inst.opcode = Opcode::LDR; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset( Reg::from_u8(0b1111), imm8 << 2, true, // add false, // no wback ), Operand::Nothing, Operand::Nothing, ]; } else if opword < 0b10100 { let op_b = instr2[9..12].load::<usize>(); let op_a = instr2[12..].load::<usize>(); // `Load/store single data item` on page `A6-225` // v4T let rt = instr2[0..3].load::<u8>(); let rn = instr2[3..6].load::<u8>(); let rm = instr2[6..9].load::<u8>(); let imm5 = instr2[6..11].load::<u16>(); if op_a == 0b0101 { let op = [ Opcode::STR, Opcode::STRH, Opcode::STRB, Opcode::LDRSB, Opcode::LDR, Opcode::LDRH, Opcode::LDRB, Opcode::LDRSH, ][op_b]; inst.opcode = op; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexReg( Reg::from_u8(rn), Reg::from_u8(rm), true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else { // opword is 0b0110, 0b0111, 0b1000, or 0b1001. opb bit 2 can be used to form a // three-bit index and select an opcode. operands are shared. except the last two. // those are sp-relative. let upper = op_a - 0b0110; let idx = (upper << 1) | (op_b >> 2); let op = [ Opcode::STR, Opcode::LDR, Opcode::STRB, Opcode::LDRB, Opcode::STRH, Opcode::LDRH, Opcode::STR, Opcode::LDR, ][idx]; inst.opcode = op; if idx < 6 { let shift = match idx >> 1 { 0b00 => 2, 0b01 => 0, 0b10 => 1, _ => { unreachable!("impossible bit pattern"); } }; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm5 << shift, true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } else { let rt = instr2[8..11].load::<u8>(); let imm8 = instr2[..8].load::<u16>(); inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset( Reg::from_u8(13), // sp imm8 << 2, true, // add false, // wback ), Operand::Nothing, Operand::Nothing, ]; } } } else if opword < 0b10101 { // `ADR` on page `A8-320` -- v4T let rd = instr2[8..11].load::<u8>(); let imm8 = instr2[..8].load::<u16>(); inst.opcode = Opcode::ADR; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Imm32(imm8 as u32 * 4), Operand::Nothing, Operand::Nothing, ]; } else if opword < 0b10110 { // `ADD (SP plus immediate)` on `A8-314` -- v4T let rd = instr2[8..11].load::<u8>(); let imm8 = instr2[..8].load::<u16>(); inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(13)), // sp Operand::Imm32(imm8 as u32 * 4), Operand::Nothing, ]; } else if opword < 0b11000 { // `Miscellaneous 16-bit instructions` on page `A6-226` let opcode_bits = instr2[5..12].load::<u16>(); if opcode_bits < 0b0000100 { // `Add Immediate to SP` (`A8-314`) // v4T // encoding T2 let imm7 = instr2[..7].load::<u32>(); inst.s = false; inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(13)), Operand::Reg(Reg::from_u8(13)), Operand::Imm32(imm7 << 2), Operand::Nothing, ]; } else if opcode_bits < 0b0001000 { // `Subtract Immediate to SP` (`A8-717`) // v4T let imm7 = instr2[..7].load::<u32>(); inst.s = false; inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(13)), Operand::Reg(Reg::from_u8(13)), Operand::Imm32(imm7 << 2), Operand::Nothing, ]; } else if opcode_bits < 0b0010000 { // `Compare and Branch on Zero` (`A8-354`) // v6T2 let op = instr2[11]; let rn = instr2[..3].load::<u8>(); let imm5 = instr2[3..8].load::<u16>(); let imm = (((instr >> 9) & 1) << 5) | imm5; inst.opcode = if op { Opcode::CBNZ } else { Opcode::CBZ }; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::BranchThumbOffset(imm as i32 + 1), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0010010 { // `Signed Extend Halfword` (`A8-735`) // v6 let rd = instr2[..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); inst.opcode = Opcode::SXTH; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0010100 { // `Signed Extend Byte` (`A8-731`) // v6 let rd = instr2[..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); inst.opcode = Opcode::SXTB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0010110 { // `Unsigned Extend Halfword` (`A8-817`) // v6 let rd = instr2[..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); inst.opcode = Opcode::UXTH; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0011000 { // `Unsigned Extend Byte` (`A8-813`) // v6 let rd = instr2[..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); inst.opcode = Opcode::UXTB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0100000 { // `Compare and Branch on Zero` (`A8-354`) // v6T2 let op = instr2[11]; let rn = instr2[..3].load::<u8>(); let imm5 = instr2[3..8].load::<u16>(); let imm = (((instr >> 9) & 1) << 5) | imm5; inst.opcode = if op { Opcode::CBNZ } else { Opcode::CBZ }; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::BranchThumbOffset(imm as i32 + 1), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0110000 { // `Push multiple registers` (`A8-539`) // v4T let m = instr2[8..9].load::<u16>(); let reglist = instr2[0..8].load::<u16>() | (m << (6 + 8)); inst.opcode = Opcode::PUSH; inst.operands = [ Operand::RegList(reglist), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0110010 { // undefined encoding between `PUSH` and `SETEND` return Err(DecodeError::Undefined); } else if opcode_bits < 0b0110011 { // opword == 0b0110010 // `Set Endianness` (`A8-605`) // v6 let e = instr2[3..4].load::<u16>(); inst.opcode = Opcode::SETEND; inst.operands = [ Operand::Imm12(e), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b0110100 { // opword == 0b0110011 // `Change Processor State` (`B9-1964`) // v6 let aif = instr2[0..3].load::<u16>(); let im = instr2[4]; inst.opcode = Opcode::CPS(im); inst.operands = [ Operand::Imm12(aif), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b1001000 { // undefined encoding between `CPS` and `CBNZ/CBZ` return Err(DecodeError::Undefined); } else if opcode_bits < 0b1010000 { // `Compare and Branch on Nonzero` (`A8-354`) // v6T2 let op = instr2[11]; let rn = instr2[0..3].load::<u8>(); let imm5 = instr2[3..8].load::<u16>(); let imm = (((instr >> 9) & 1) << 5) | imm5; inst.opcode = if op { Opcode::CBNZ } else { Opcode::CBZ }; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::BranchThumbOffset(imm as i32 + 1), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b1010010 { // `Byte-Reverse Word` (`A8-563`) // v6 let rd = instr2[0..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); inst.opcode = Opcode::REV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b1010100 { // `Byte-Reverse Packed Halfword` (`A8-565`) // v6 let rd = instr2[0..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); inst.opcode = Opcode::REV16; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b1010110 { // undefined encoding where `Byte-Reverse Signed Word` might go return Err(DecodeError::Undefined); } else if opcode_bits < 0b1011000 { // `Byte-Reverse Signed Halfword` (`A8-567`) // v6 let rd = instr2[0..3].load::<u8>(); let rm = instr2[3..6].load::<u8>(); inst.opcode = Opcode::REVSH; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b1100000 { // `Compare and Branch on Nonzero` (`A8-354`) // v6T2 let op = instr2[11]; let rn = instr2[0..3].load::<u8>(); let imm5 = instr2[3..8].load::<u16>(); let imm = (((instr >> 9) & 1) << 5) | imm5; inst.opcode = if op { Opcode::CBNZ } else { Opcode::CBZ }; inst.operands = [ Operand::Reg(Reg::from_u8(rn)), Operand::BranchThumbOffset(imm as i32 + 1), Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b1110000 { // `Pop Multiple Registers` (`A8-535`) // v4T let p = instr2[8..9].load::<u16>(); let reglist = instr2[0..8].load::<u16>() | (p << (7 + 8)); inst.opcode = Opcode::POP; inst.operands = [ Operand::RegList(reglist), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else if opcode_bits < 0b1111000 { // `Breakpoint` (`A8-344`) // v5 let imm8 = instr2[0..8].load::<u32>(); inst.opcode = Opcode::BKPT; inst.operands = [ Operand::Imm32(imm8), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `If-Then, and hints` (`A6-227`) let opb = instr2[0..4].load::<u32>(); let opa = instr2[4..8].load::<u32>(); if opb != 0 { // `IT` (`A8-391`) // v6T2 let firstcond = opa; let mask = opb; inst.opcode = Opcode::IT; if firstcond == 0b1111 { return Err(DecodeError::InvalidOperand); } inst.operands = [ Operand::Imm32(firstcond), Operand::Imm32(mask), Operand::Nothing, Operand::Nothing, ]; } else { match opa { 0b0000 => { // `NOP` (`A8-511`) // v6T2 inst.opcode = Opcode::NOP; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b0001 => { // `YIELD` (`A8-1109`) // v7 inst.opcode = Opcode::YIELD; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b0010 => { // `WFE` (`A8-1105`) // v7 inst.opcode = Opcode::WFE; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b0011 => { // `WFI` (`A8-1107`) // v7 inst.opcode = Opcode::WFI; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b0100 => { // `SEV` (`A8-607`) // v7 inst.opcode = Opcode::SEV; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } hint => { // `Other encodings in this space are unallocated hints. They execute // as NOPs, but software must not use them.` inst.opcode = Opcode::HINT; inst.operands = [ Operand::Imm32(hint), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } } } } } else if opword < 0b11001 { // `STM (STMIA, STMEA)` on page `A8-665` -- v4T let rn = instr2[8..11].load::<u8>(); let reglist = instr2[0..8].load::<u16>(); inst.opcode = Opcode::STM(true, true, false, true); // stmia, no wback, yes usermode inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), true), // always wback Operand::RegList(reglist as u16), Operand::Nothing, Operand::Nothing, ]; } else if opword < 0b11010 { // `LDM/LDMIA/LDMFD (Thumb)` on page `A8-397` -- v4T let rn = instr2[8..11].load::<u8>(); let reglist = instr2[0..8].load::<u16>(); let w = (reglist & (1 << rn)) == 0; inst.opcode = Opcode::LDM(true, false, false, true); // ldmia, no wback, yes usermode inst.operands = [ Operand::RegWBack(Reg::from_u8(rn), w), Operand::RegList(reglist as u16), Operand::Nothing, Operand::Nothing, ]; } else if opword < 0b11100 { // `Conditional branch, and Supervisor Call` on page `A6-227` let opcode = instr2[8..12].load::<u8>(); if opcode < 0b1110 { // `B` (`A8-332`) // v4T inst.opcode = Opcode::B; let imm = instr2[0..8].load::<u8>() as i8 as i32; inst.condition = ConditionCode::build(opcode); inst.operands = [ Operand::BranchThumbOffset(imm + 1), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else if opcode < 0b1111 { // `UDF` (`A8-759`) // v4T // first described in revision `C.a` inst.opcode = Opcode::UDF; inst.operands = [ Operand::Imm32(instr2[..8].load::<u32>()), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `SVC` (`A8-721`) // v4T inst.opcode = Opcode::SVC; inst.operands = [ Operand::Imm32(instr2[..8].load::<u32>()), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } } else { // `B` on page `A8-332` -- v4T // encoding T2 // v4T inst.opcode = Opcode::B; let imm = instr2[0..11].load::<u32>(); let imm = ((imm as i32) << 20) >> 20; inst.operands = [ Operand::BranchThumbOffset(imm), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } } Ok(()) }