yaxpeax-arm-0.3.0/.cargo_vcs_info.json0000644000000001360000000000100132700ustar { "git": { "sha1": "1e052c9c855bad7a051c8c2ad336b6b0170a1098" }, "path_in_vcs": "" }yaxpeax-arm-0.3.0/.gitignore000064400000000000000000000000231046102023000140430ustar 00000000000000target/ Cargo.lock yaxpeax-arm-0.3.0/CHANGELOG000064400000000000000000000113311046102023000132710ustar 00000000000000## 0.3.0 * major version bump of yaxpeax-arch (0.2.7 -> 0.3.1) - while there is no direct API breakage of yaxpeax-arch, typical use of the yaxpeax crates would have issues with their yaxpeax-arch dependency not matching the transitive dependency through specific decoder crates. so, major version bump it is! ## 0.2.6 * aarch64: add support for `udf` (`0000_0000_0000_0000_`) - thank you @martin-fink! ## 0.2.5 * fix decoding armv8 a64 hint instructions - in some cases the displayed hint instruction was incorrect - additionally, the `op1` field in decoded instructions included two more bits than it should - thanks to belovdv for the patch! * improve decoding of armv8 a64 msr instructions - decoding of the `PstateField` immediate value was just terribly wrong. - this should be improved to more precisely match section `C5.1.3` "architectural hints, barriers and CLREX, and PSTATE access", but least fields are decoded properly. ## 0.2.4 * fix `no_std` builds * several fixes relating to arm- and thumb-mode v7 branch offset decoding - thanks to both ehntoo and jam1garner for these! * make `ShiftStyle::into_shift` public. it was private as an oversight; this is a useful conversion involved in programmatic handling of instructions. - thanks to wscp for catching this! ## 0.2.3 * add differential tests, initial impl only tests against capstone ## 0.2.2 * fix docs typo. ## 0.2.1 * README updates. ## 0.2.0 * added documentation to all public members. * fix tests incorrectly expecting `lsl #0` in memory operands where it would have no effect * remove some redundant `a64` operand kinds: - `RegOffset` is now `RegPreIndex` with no writeback. - `Offset` was a duplicate of `PCOffset`, and all uses have been so-renamed. ## 0.1.4 * add or fix many a64 instructions, extend support up through armv8.5. - some disassembly and optional elements of instruction text does not match the arm manual, but instruction data should be correct across the board. - SVE and SVE2 are not yet supported, and return `IncompleteDecoder` errors. ## 0.1.3 * same as 0.1.2, but fixes a few compilation warnings that snuck in too. ## 0.1.2 * fix some instructions in aarch64 decoding panicking with "unreachable" instead of returning an `Err(DecodeError::Incomplete)`. similarly, some instructions panicked instead of returning `InvalidOpcode` or `InvalidOperand` as they should. * fix some instructions in armv7 decoding or display panicking instead of displaying a valid instruction, or returning a decode error. ... subsequently yanked because some compilation warnings ended up in this version. ## 0.1.1 * fix incorrect `yaxpeax_arch::Arch` impl for `std`-enabled builds (DecodeError did not implement `std::error::Error` in those build environments) ## 0.1.0 * target yaxpeax-arch 0.2.0 ## 0.0.6 * fix incorrect markdown in README.md ## 0.0.5 * add thumb/thumb2 decoders these decoders are considered "mostly correct" and "mostly complete". notably, system instructions are less well-tested (particularly `mrs`, `cps`, and similar), and the NEON extensions are still unsupported. NEON in particular yield `DecodeError::Incomplete`. if you, the user, see `Incomplete`, that is a `yaxpeax-arm` error! `yaxpeax-arm` differs from the ARM manual's assembly syntax in a few ways: * `.w` suffixes are best-effort. * `addw`/`subw` are written `add.w`/`sub.w` for consistency with other wide instructions. * `adr` with added/subtracted offset are shown as `add`/`sub` * testing is not as exhaustive as is possible for ARM; errors may yet exist at this time, if-then instructions (`IT`) are decoded, but the decoder has no notion of "in an `IT` block or not", so following conditional instructions are not yet conditional. this all said, for armv4/thumb decoding, the implementation here is probably close to correct. for armv6+/thumb2, aside from these notes, the implementation here is ... hopefully ... close to correct. * add clear rules by which `yaxpeax-arm` will be versioned 0.1 or 1.0. (see README) ## 0.0.4 * bump yaxpeax-arch, no functional change ## 0.0.3 `Instruction`, `Opcode`, and `Operand` are now a useful model of decoded operations. * `Operands`, plural, the enum of operands in the instruction, is now gone in favor of `Operand` that describe one operand of the instruction * `Instruction` no longer include operand attributes like "add", "index", or "wback" * better (not quite complete) support for non-user instructions and coprocessor instructions * instructions operating on status registers through masks are now directly supported * first brush of arm-version-specific decoder feature selection * fix several preindex/postindex reversals ## 0.0.2 last revison that's sufficient for disassembly and really nothing else yaxpeax-arm-0.3.0/Cargo.toml0000644000000022570000000000100112740ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "yaxpeax-arm" version = "0.3.0" authors = ["iximeow "] description = "arm decoders for the yaxpeax project" readme = "README.md" keywords = [ "disassembler", "decoder", "armv7", "armv8", ] license = "0BSD" repository = "http://git.iximeow.net/yaxpeax-arm/" [lib] [dependencies.bitvec] version = "1.0.1" [dependencies.serde] version = "1.0" optional = true [dependencies.serde_derive] version = "1.0" optional = true [dependencies.yaxpeax-arch] version = "0.3.1" features = [] default-features = false [features] default = [ "std", "use-serde", ] std = ["yaxpeax-arch/std"] use-serde = [ "yaxpeax-arch/use-serde", "serde", "serde_derive", ] yaxpeax-arm-0.3.0/Cargo.toml.orig000064400000000000000000000013721046102023000147520ustar 00000000000000[package] name = "yaxpeax-arm" version = "0.3.0" authors = ["iximeow "] license = "0BSD" repository = "http://git.iximeow.net/yaxpeax-arm/" description = "arm decoders for the yaxpeax project" keywords = ["disassembler", "decoder", "armv7", "armv8"] edition = "2018" [lib] [workspace] members = ["differential-tests"] [dependencies] yaxpeax-arch = { version = "0.3.1", default-features = false, features = [] } bitvec = "1.0.1" "serde" = { version = "1.0", optional = true } "serde_derive" = { version = "1.0", optional = true } [features] default = ["std", "use-serde"] # opt-in for std-related Error impl - necessary to `?`-unwrap `DecodeError`. std = ["yaxpeax-arch/std"] use-serde = ["yaxpeax-arch/use-serde", "serde", "serde_derive"] yaxpeax-arm-0.3.0/Makefile000064400000000000000000000003411046102023000135160ustar 00000000000000test: test-fast test-exhaustive test-fast: test-std test-no-std test-exhaustive: cargo test -- --ignored cargo test --no-default-features -- --ignored test-std: cargo test test-no-std: cargo test --no-default-features yaxpeax-arm-0.3.0/README.md000064400000000000000000000102111046102023000133320ustar 00000000000000## yaxpeax-arm [![crate](https://img.shields.io/crates/v/yaxpeax-arm.svg?logo=rust)](https://crates.io/crates/yaxpeax-arm) [![documentation](https://docs.rs/yaxpeax-arm/badge.svg)](https://docs.rs/yaxpeax-arm) `yaxpeax-arm` provides implementations of decoders for the armv7, v7/thumb, v7/thumb2, and aarch64/a64 instruction sets. the entrypoint to begin decoding is either `armv7::InstDecoder` (`ARMv7::Decoder::default()`) or `armv8::InstDecoder` (`ARMv8::Decoder::default()`). `default` for both decoders is to decode in ARM mode. `default` will try to decode as permissively as possible, even attempting to produce some kind of instruction for `UNPREDICTABLE` patterns, where possible. for armv7 and below, `default_thumb` produces a similarly permissive set of rules, but for decoding thumb/thumb2 instructions. ARMv7 and thumb mode instructions decode to the same structure: `armv7::Instruction`. this is not known to introduce ambiguities, and if you must discern thumb vs non-thumb origins, `armv7::Instruction::thumb` will reflect the decoder state when decoding the instruction of interest. additionally, `armv7::Instruction::w` reports if the instruction was a wide (32-bit) thum instruction. for all ARMv7 instructions, `armv7::Instruction::s()` reports if the instruction will update status flags. if `s` is in error, that is a decoder bug, please report it. ### features * `#[no_std]` * very fast * pretty small? ### `#[no_std]` `yaxpeax-arm` supports use in `no_std` environments. to build `yaxpeax-arm` in `no_std` environments, add `default-features = false` to the crate's depdency line. this disables the `std` feature, and removes the little integration with `std` that `yaxpeax-arm` optionally provides. ### very fast `yaxpeax-arm` hasn't been exhaustively benchmarked, but loose tests suggest that it's at least as fast as other high-quality `arm` disassemblers, like [`capstone`](https://github.com/capstone-engine/capstone) or [`bad64`](https://github.com/yrp604/bad64). more comprehensive benchmarks to come. ### pretty small? similarly to decode speed, the size of a compiled `yaxpeax-arm` hasn't been closely profiled, but at minimum it's 20% the size of `yaxpeax-x86`, with `armv7` and `armv8` code being entirely independent - using only one architecture should allow the other's code to be dead-code-eliminated. `yaxpeax-arm` compiles in release mode in only a few seconds. ## stability 0.1 and 1.0 versions are considered significant indicators of feature-completeness and stability. the specific guidelines by which `yaxpeax-arm` will be considered stable are listed below. ### 0.1 checklist - [/] support `NEON` (SIMD before SVE supported in ARMv8!) - [x] adjust `yaxpeax-arch` so `min_length` can be contingent on the mode of `InstDecoder` - currently `min_length` is always 4, which is incorrect for `Thumb` modes. conversely, selecting "2" would be flagrantly wrong for `ARM` modes. - [ ] address all in-tree TODO ### 1.0 checklist - [ ] support `SVE` and `SVE2` - [ ] support per-version decode flags, so decoding an armv4, armv5, or armv7 instruction - [ ] fully support `should_is_must` to control how pedantic decoding should be - [ ] fully support reporting `unpredictable` encodings as `DecodeError::Unpredictable` if required - [ ] exhaustively test armv7 and armv8 instructions against other decoders - existing thumb test suite is derived from enumerating thumb instructions, but is missing some ### ! user beware ! * armv7 NEON support is still nonexistent ## arch notes: ### Register Names Reproduced from [infocenter.arm.com](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0473c/CJAJBFHC.html): | Name | Maps To | Meaning | | ---- | ------- | ------- | | r0-r15 | r0-r15 | These are the the registers! | | a1-a4 | r0-r3 | Argument, result, or scratch registers | | v1-v8 | r4-r11 | Variable registers | | sb | r9 | Static base register | | fp | r11 | Frame pointer\* | | ip | r12 | Intra-procedure call register | | sp | r13 | Stack pointer | | lr | r14 | link register | | pc | r15 | program counter | \* `fp` does not appear to be explicitly referenced in ARM documentation, and mapping to r11 looks to be OS (Windows/Linux?) convention. yaxpeax-arm-0.3.0/goodfile000064400000000000000000000006301046102023000135720ustar 00000000000000Build.dependencies({"git", "make", "rustc", "cargo"}) Build.metric( "nightly version", string.gsub(Build.check_output({"rustc", "--version"}).stdout, '^%s*(.*)%s*$', '%1') ) Step.start("crate") Step.push("build") Build.run({"cargo", "build"}) Step.advance("test") Build.run({"cargo", "test"}, {name="test stdlib/fmt"}) Build.run({"cargo", "test", "--no-default-features"}, {name="test nostdlib/fmt"}) yaxpeax-arm-0.3.0/src/armv7/thumb.rs000064400000000000000000007036271046102023000154070ustar 00000000000000use yaxpeax_arch::Arch; use crate::armv7::{ ARMv7, ConditionCode, DecodeError, CReg, Reg, Reader, RegShift, Operand, Opcode, Instruction, InstDecoder, 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) -> Result { if imm & 0b1100_0000_0000 == 0 { let ty = (imm >> 8) & 0b11; let imm_low = (imm & 0b11111111) as u32; match ty { 0b00 => { Ok(imm_low) } 0b01 => { if imm_low == 0 { return Err(DecodeError::Unpredictable); } Ok((imm_low << 16) | imm_low) } 0b10 => { if imm_low == 0 { return Err(DecodeError::Unpredictable); } Ok((imm_low << 24) | (imm_low << 8)) } 0b11 => { if imm_low == 0 { return Err(DecodeError::Unpredictable); } Ok((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); Ok(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::Address, ::Word>>(decoder: &InstDecoder, inst: &mut Instruction, words: &mut 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 word_bytes = [0u8; 2]; words.next_n(&mut word_bytes)?; let word = u16::from_le_bytes(word_bytes); let instr = word; let mut instr2 = bitarr![u16, Lsb0; 0u16; 16]; instr2[0..16].store(word); let opword = instr2[11..].load::(); // `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 mut word_bytes = [0u8; 2]; words.next_n(&mut word_bytes)?; let lower = u16::from_le_bytes(word_bytes); let mut lower2 = bitarr![u16, Lsb0; 0u16; 16]; lower2[0..16].store(lower); let op2 = &instr2[4..11]; if opword == 0b11101 { // 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::(); // TODO: double check if op2[2] { // `Load/store dual, load/store exclusive, table branch` (`A6-236`) let op1op2 = (instr2[7..9].load::() << 2) | instr2[4..6].load::(); let imm8 = lower2[..8].load::(); let rd = lower2[8..12].load::(); let rt = lower2[12..16].load::(); // 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 `[, #]`, 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 `[, #]`, 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::(); 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::(); 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 `, 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::() { 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::()), // # ? what's the syntax here? #? 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), // # ? what's the syntax here? #? Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!(); } } } else { // `L == 1` match instr2[7..9].load::() { 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::(); let s = instr2[4]; let rn = instr2[0..4].load::(); let rd = lower2[8..12].load::(); let imm3 = lower2[12..15].load::(); let imm2 = lower2[6..8].load::(); let tp = lower2[4..6].load::(); let rm = lower2[0..4].load::(); 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::(); let rd = lower2[8..12].load::(); 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::(); let rd = lower2[8..12].load::(); 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::(); let rd = lower2[8..12].load::(); 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::(); let rd = lower2[8..12].load::(); 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::(); let rd = lower2[8..12].load::(); 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::(); let rd = lower2[8..12].load::(); 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 // op1 == 01, op2 == 1xxxxxx // this means `assert!(instr2[10])` return decode_table_a6_30(decoder, inst, instr2, lower2); } } else if opword == 0b11110 { // op1 == 0b10 // interpret `op1 == 0b10` lines in table `A6-9` on `A6-228`: if !lower2[15] { // 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::(); let i = instr2[10..11].load::(); let s = instr2[4]; let rn = instr2[0..4].load::(); let imm3 = lower2[12..15].load::(); let rd = lower2[8..12].load::(); let imm8 = lower2[0..8].load::(); 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 // aka table `A6-12` let op = instr2[4..9].load::(); let i = instr2[10..11].load::(); inst.s = false; inst.set_w(false); let rn = instr2[0..4].load::(); let imm3 = lower2[12..15].load::(); let rd = lower2[8..12].load::(); let imm8 = lower2[0..8].load::(); 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 // encoding T3 // handle "add = TRUE" and "add = FALSE" by calling this add/sub inst.opcode = Opcode::ADD; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(15)), Operand::Imm32(imm as u32), 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 // encoding T2 // handle "add = TRUE" and "add = FALSE" by calling this add/sub inst.opcode = Opcode::SUB; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(15)), Operand::Imm32(imm as u32), 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::() << 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 { // A6.3 op == 1 // `Branches and miscellaneous control` (`A6-233`) let imm8 = lower2[0..8].load::(); let op2 = lower2[8..12].load::(); let op1 = lower2[12..15].load::(); let op = instr2[4..11].load::(); 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 let imm11 = lower2[0..11].load::(); let imm6 = instr2[0..6].load::(); let s = instr2[10..11].load::(); let j1 = lower2[13..14].load::(); let j2 = lower2[11..12].load::(); let imm = (imm11 as i32) | ((imm6 as i32) << 11) | ((j1 as i32) << 17) | ((j2 as i32) << 18) | ((s as i32) << 19); let imm = (imm << 12) >> 12; inst.condition = ConditionCode::build(((instr >> 6) & 0b1111) as u8); inst.opcode = Opcode::B; inst.operands = [ Operand::BranchThumbOffset(imm), 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::(); 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? if let Some(op) = Reg::from_sysm(R, sysm as u8) { // TODO: from_sysm should succeed? op } else { return Err(DecodeError::InvalidOperand); }, 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::(); let R = instr2[4]; inst.opcode = Opcode::MSR; inst.operands = [ // TODO: is this the appropriate? if let Some(op) = Reg::from_sysm(R, mask) { // TODO: from_sysm should succeed? op } else { return Err(DecodeError::InvalidOperand); }, 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::(); let R = instr2[4]; inst.opcode = Opcode::MSR; inst.operands = [ // TODO: is this the appropriate? if let Some(op) = Reg::from_sysm(R, mask) { // TODO: from_sysm should succeed? op } else { return Err(DecodeError::InvalidOperand); }, 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::(); let op2 = lower2[0..8].load::(); if op1 != 0b000 { // `CPS (Thumb)` (`B9-1964`) // v6T2 // encoding T2 let mode = lower2[0..5].load::(); let m = lower2[8]; let aif = lower2[5..8].load::(); let imod = lower2[9..11].load::(); if !m && imod == 0b00 { // unreachable; would be a hint } if !m && mode != 0 { decoder.unpredictable()?; } if imod < 0b10 { if imod == 0b01 { decoder.unpredictable()?; } inst.opcode = Opcode::CPS_modeonly; inst.operands = [ Operand::Imm12(mode), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { inst.opcode = Opcode::CPS(imod == 0b11); inst.operands = [ Operand::Imm12(aif), if m { Operand::Imm12(mode) } else { Operand::Nothing }, Operand::Nothing, Operand::Nothing, ]; } } else { if op2 >= 0b11110000 { // `DBG` (`A8-378`) let option = lower2[0..4].load::(); 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::(); 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::(); if imm8 == 0 { // `ERET` (`B9-1968`) // v6T2 // if > 8) & 0b1111) as u8; inst.opcode = Opcode::MRS; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), if let Some(op) = Reg::from_sysm(r != 0, sysm as u8) { // TODO: from_sysm should succeed? op } else { return Err(DecodeError::InvalidOperand); }, 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: ""? if let Some(op) = Reg::from_sysm(false, 0) { // TODO: from_sysm should succeed? op } else { return Err(DecodeError::InvalidOperand); }, 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: ""? if let Some(op) = Reg::from_sysm(r != 0, 0) { // TODO: from_sysm should succeed? op } else { return Err(DecodeError::InvalidOperand); }, 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 { let imm11 = lower2[0..11].load::(); let imm10 = instr2[0..10].load::(); let j1 = lower2[13..14].load::(); let j2 = lower2[11..12].load::(); let s = instr2[10..11].load::(); let i1 = 0x1 ^ s ^ j1; let i2 = 0x1 ^ s ^ j2; let imm = (imm11 as i32) | ((imm10 as i32) << 11) | ((i2 as i32) << 21) | ((i1 as i32) << 22) | ((s as i32) << 23); let imm = (imm << 8) >> 8; inst.operands = [ Operand::BranchThumbOffset(imm), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; if op1 & 0b101 == 0b001 { // `Branch` (`A8-332`) // T4 encoding // v6T2 inst.opcode = Opcode::B; } else if op1 & 0b101 == 0b100 { // `Branch with Link and Exchange` (`A8-346`) // `UNDEFINED` in v4T // v5T // Undefined if low bit of imm10 is set ("H") if imm11 & 0x1 != 0 { return Err(DecodeError::Undefined); } inst.opcode = Opcode::BLX; } else if op1 & 0b101 == 0b101 { // `Brach with Link` (`A8-346`) // v4T inst.opcode = Opcode::BL; } else { // Permanently undefined by A6-13 return Err(DecodeError::Undefined); } } } } 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::(); let op1 = instr2[5..8].load::(); 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::(); let rt = lower2[12..16].load::(); let rn = instr2[0..4].load::(); let op1 = instr2[7..9].load::(); // load {byte, halfword, word} in table `A6-9` let size = instr2[5..7].load::(); 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::(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm12, u, 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::(); let imm2 = lower2[4..6].load::(); 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::(); 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::(); 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::(); 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::(); inst.opcode = opcode; inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm12, u, 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::(); 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::(); 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 { 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::(); let imm2 = lower2[4..6].load::(); 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::(); 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::(); 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::(); 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::(); 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::(); 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::(); 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::()]; let rd = lower2[8..12].load::(); let rm = lower2[0..4].load::(); 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::(); // `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::(); let rotate = lower2[1..3].load::() << 2; let rd = lower2[8..12].load::(); 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::(); let rotate = lower2[1..3].load::() << 2; let rd = lower2[8..12].load::(); 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::(); if op2 < 0b0100 { // `Parallel addition and subtraction, signed` let op1 = instr2[4..7].load::(); let op2 = lower2[4..6].load::(); if op1 == 0 || op1 > 0b100 || op2 == 0b11 { return Err(DecodeError::InvalidOpcode); } let opcode_idx = (op1 - 1) * 3 + op2; let rn = instr2[0..4].load::(); let rd = lower2[8..12].load::(); let rm = lower2[0..4].load::(); 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::(); let op2 = lower2[4..6].load::(); if op1 > 0b100 || op2 == 0b11 { return Err(DecodeError::InvalidOpcode); } if op1 == 0 { return Err(DecodeError::InvalidOpcode); } let opcode_idx = (op1 - 1) * 3 + op2; let rn = instr2[0..4].load::(); let rd = lower2[8..12].load::(); let rm = lower2[0..4].load::(); 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::(); let rd = lower2[8..12].load::(); let rm = lower2[0..4].load::(); let op1 = instr2[4..6].load::(); let op2 = lower2[4..6].load::(); 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`) let op1 = instr2[4..7].load::(); let op2 = lower2[4..8].load::(); let rn = instr2[0..4].load::(); let rdlo = lower2[12..16].load::(); let rd = lower2[8..12].load::(); let rm = lower2[0..4].load::(); match op1 { 0b000 => { if op2 == 0b0000 { inst.opcode = Opcode::SMULL; inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else { return Err(DecodeError::InvalidOpcode); } } 0b001 => { if op2 == 0b1111 { inst.opcode = Opcode::SDIV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } else { return Err(DecodeError::InvalidOpcode); } } 0b010 => { if op2 == 0b0000 { inst.opcode = Opcode::UMULL; inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else { return Err(DecodeError::InvalidOpcode); } } 0b011 => { if op2 == 0b1111 { inst.opcode = Opcode::UDIV; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } else { return Err(DecodeError::InvalidOpcode); } } 0b100 => { if op2 == 0b0000 { inst.opcode = Opcode::SMLAL; inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else if op2 & 0b1100 == 0b1000 { inst.opcode = Opcode::SMLAL_halfword(lower2[5], lower2[4]); inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else if op2 & 0b1110 == 0b1100 { inst.opcode = Opcode::SMLALD(lower2[4]); inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else { return Err(DecodeError::InvalidOpcode); } } 0b101 => { if op2 == 0b1100 || op2 == 0b1101 { inst.opcode = Opcode::SMLSLD(lower2[4]); inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else { return Err(DecodeError::InvalidOpcode); } } 0b110 => { if op2 == 0b0000 { inst.opcode = Opcode::UMLAL; inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else if op2 == 0b0110 { inst.opcode = Opcode::UMAAL; inst.operands = [ Operand::Reg(Reg::from_u8(rdlo)), Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), ]; } else { return Err(DecodeError::InvalidOpcode); } } _ => { return Err(DecodeError::InvalidOpcode); } } } else { // `Multiply, multiply accumulate, and absolute difference` (`A6-247`) let op1 = instr2[4..7].load::(); let op2 = lower2[4..6].load::(); let rm = lower2[0..4].load::(); let rd = lower2[8..12].load::(); let ra = lower2[12..16].load::(); let rn = instr2[0..4].load::(); if ra == 0b1111 { if op1 == 0b000 { if op2 == 0b00 { inst.opcode = Opcode::MUL; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } else if op2 == 0b01{ inst.opcode = Opcode::MLS; // a == 15, unpredictable decoder.unpredictable()?; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Reg(Reg::from_u8(ra)), ]; } else { return Err(DecodeError::InvalidOpcode); } } else if op1 == 0b001 { // `SMULBB, SMULBT, SMULTB, SMULTT on page A8-645` inst.opcode = Opcode::SMUL(lower2[5], lower2[4]); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } else if op1 == 0b011 { if op2 >= 0b10 { return Err(DecodeError::InvalidOpcode); } inst.opcode = Opcode::SMULW(lower2[4]); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } else { if op2 >= 0b10 { return Err(DecodeError::InvalidOpcode); } if op1 == 0b111 && op2 == 0b00 { return Err(DecodeError::InvalidOpcode); } if op1 == 0b110 { decoder.unpredictable()?; } inst.opcode = [ Opcode::MUL, // already handled Opcode::UDF, // already handled Opcode::SMUAD, Opcode::UDF, // already handled Opcode::SMUSD, Opcode::SMMUL, Opcode::SMMLS, Opcode::USAD8, ][op1]; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Nothing, ]; } } else { if op1 == 0b000 { if op2 == 0b00 { inst.opcode = Opcode::MLA; } else if op2 == 0b01{ inst.opcode = Opcode::MLS; } else { return Err(DecodeError::InvalidOpcode); } inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Reg(Reg::from_u8(ra)), ]; } else if op1 == 0b001 { // `SMULBB, SMULBT, SMULTB, SMULTT on page A8-645` inst.opcode = Opcode::SMLA(lower2[5], lower2[4]); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Reg(Reg::from_u8(ra)), ]; } else if op1 == 0b011 { if op2 >= 0b10 { return Err(DecodeError::InvalidOpcode); } inst.opcode = Opcode::SMLAW(lower2[4]); inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Reg(Reg::from_u8(ra)), ]; } else { if op2 >= 0b10 { return Err(DecodeError::InvalidOpcode); } if op1 == 0b111 && op2 == 0b00 { return Err(DecodeError::InvalidOpcode); } if op1 == 0b110 { decoder.unpredictable()?; } inst.opcode = [ Opcode::MUL, // already handled Opcode::UDF, // already handled Opcode::SMLAD, Opcode::UDF, // already handled Opcode::SMLSD, Opcode::SMMLA, Opcode::SMMLS, Opcode::USADA8, ][op1]; inst.operands = [ Operand::Reg(Reg::from_u8(rd)), Operand::Reg(Reg::from_u8(rn)), Operand::Reg(Reg::from_u8(rm)), Operand::Reg(Reg::from_u8(ra)), ]; } } } } } } else { // `Coprocessor, Advanced SIMD, and Floating-point instructions` (`A6-249`) // v6T2 // op1 == 11, op2 == 1xxxxxx // this means `assert!(instr2[10])` return decode_table_a6_30(decoder, inst, instr2, lower2); } } } 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::(); let rm = instr2[3..6].load::(); let imm5 = instr2[6..11].load::(); 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::(); let rm = instr2[3..6].load::(); let imm5 = instr2[6..11].load::(); 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::(); let rm = instr2[3..6].load::(); let imm5 = instr2[6..11].load::(); 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::(); let rm = instr2[6..9].load::(); let rd = instr2[0..3].load::(); let rn = instr2[3..6].load::(); 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::(); let rd = instr2[8..11].load::(); 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::(); let rd = instr2[8..11].load::(); 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::(); let rdn = instr2[8..11].load::(); 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::(); let rdn = instr2[8..11].load::(); 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::(); // `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::(); let rm = instr2[3..6].load::(); 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::() | (instr2[7..8].load::() << 3); let rm = instr2[3..7].load::(); 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::() | (instr2[7..8].load::() << 3); let rm = instr2[3..7].load::(); 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::() | (instr2[7..8].load::() << 3); let rm = instr2[3..7].load::(); 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::() | (instr2[7..8].load::() << 3); let rm = instr2[3..7].load::(); 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::() | (instr2[7..8].load::() << 3); let rm = instr2[3..7].load::(); 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::(); 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::(); 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::(); let rt = instr2[8..11].load::(); 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::(); let op_a = instr2[12..].load::(); // `Load/store single data item` on page `A6-225` // v4T let rt = instr2[0..3].load::(); let rn = instr2[3..6].load::(); let rm = instr2[6..9].load::(); let imm5 = instr2[6..11].load::(); 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::(); let imm8 = instr2[..8].load::(); 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::(); let imm8 = instr2[..8].load::(); 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::(); let imm8 = instr2[..8].load::(); 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::(); if opcode_bits < 0b0000100 { // `Add Immediate to SP` (`A8-314`) // v4T // encoding T2 let imm7 = instr2[..7].load::(); 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::(); 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::(); let imm5 = instr2[3..8].load::(); 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::(); let rm = instr2[3..6].load::(); 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::(); let rm = instr2[3..6].load::(); 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::(); let rm = instr2[3..6].load::(); 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::(); let rm = instr2[3..6].load::(); 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::(); let imm5 = instr2[3..8].load::(); 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::(); let reglist = instr2[0..8].load::() | (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::(); 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::(); 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::(); let imm5 = instr2[3..8].load::(); 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::(); let rm = instr2[3..6].load::(); 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::(); let rm = instr2[3..6].load::(); 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::(); let rm = instr2[3..6].load::(); 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::(); let imm5 = instr2[3..8].load::(); 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::(); let reglist = instr2[0..8].load::() | (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::(); 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::(); let opa = instr2[4..8].load::(); 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::(); let reglist = instr2[0..8].load::(); 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::(); let reglist = instr2[0..8].load::(); 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::(); if opcode < 0b1110 { // `B` (`A8-332`) // v4T inst.opcode = Opcode::B; let imm = instr2[0..8].load::() 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::()), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } else { // `SVC` (`A8-721`) // v4T inst.opcode = Opcode::SVC; inst.operands = [ Operand::Imm32(instr2[..8].load::()), 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::(); let imm = ((imm as i32) << 21) >> 21; inst.operands = [ Operand::BranchThumbOffset(imm), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } } Ok(()) } fn decode_table_a6_30(decoder: &InstDecoder, inst: &mut Instruction, instr2: BitArray<[u16; 1], Lsb0>, lower2: BitArray<[u16; 1], Lsb0>) -> Result<(), DecodeError> { // implementation of table `A6-30 Coprocessor, Advanced SIMD, and Floating-point instructions` let op1 = instr2[4..10].load::(); if op1 & 0b11_1110 == 0b00_0000 { inst.opcode = Opcode::UDF; inst.operands = [ Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; return Err(DecodeError::InvalidOpcode); } else if op1 & 0b110000 == 0b110000 { // TODO: `Advanced SIMD data-processing instructions on A7-259` return Err(DecodeError::Incomplete); } else { let coproc = lower2[8..12].load::(); if coproc & 0b1110 != 0b1010 { // `not 101x` rows if op1 == 0b000100 { // `MCRR, MCRR2 on page A8-479` let crm = lower2[0..4].load::(); let opc1 = lower2[4..8].load::(); let rt = lower2[12..16].load::(); let rt2 = instr2[0..4].load::(); if instr2[12] { inst.opcode = Opcode::MCRR2(coproc, opc1); } else { inst.opcode = Opcode::MCRR(coproc, opc1); } inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rt2)), Operand::CReg(CReg::from_u8(crm)), Operand::Nothing, ]; } else if op1 == 0b000101 { // `MRRC, MRRC2 on page A8-495` let crm = lower2[0..4].load::(); let opc1 = lower2[4..8].load::(); let rt = lower2[12..16].load::(); let rt2 = instr2[0..4].load::(); // manual typo!! the manual spells the operands // `, , , , ` // but the operand name is `opc1`! // // this is a very uninteresting typo, but fun to spot nonetheless if instr2[12] { inst.opcode = Opcode::MRRC2(coproc, opc1); } else { inst.opcode = Opcode::MRRC(coproc, opc1); } inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rt2)), Operand::CReg(CReg::from_u8(crm)), Operand::Nothing, ]; } else { if op1 & 1 == 0 { // `STC, STC2 on page A8-663` let p = instr2[8]; let u = instr2[7]; let w = instr2[5]; let rn = instr2[0..4].load::(); let crd = lower2[12..16].load::(); let imm8 = lower2[0..8].load::(); if instr2[12] { if instr2[6] { inst.opcode = Opcode::STC2L(coproc); } else { inst.opcode = Opcode::STC2(coproc); } } else { if instr2[6] { inst.opcode = Opcode::STCL(coproc); } else { inst.opcode = Opcode::STC(coproc); } } inst.operands = [ Operand::CReg(CReg::from_u8(crd)), if p { Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm8 << 2, u, w, ) } else { if w { Operand::RegDerefPostindexOffset( Reg::from_u8(rn), imm8 << 2, u, false, // TODO: wback? this is true? not true? ) } else { Operand::RegDeref(Reg::from_u8(rn)) } }, if !p && !w { Operand::CoprocOption(imm8 as u8) } else { Operand::Nothing }, Operand::Nothing, ]; } else { // `LDC, LDC2 (immediate or literal) on A8-393 or A8-395` let p = instr2[8]; let u = instr2[7]; let w = instr2[5]; let rn = instr2[0..4].load::(); let crd = lower2[12..16].load::(); let imm8 = lower2[0..8].load::(); if rn == 0b1111 { // `LDC, LDC2 (literal) on A8-395` // notable for rejecting writeback if w { decoder.unpredictable()?; } } else { // `LDC, LDC2 (immediate) on A8-393` } if instr2[12] { if instr2[6] { inst.opcode = Opcode::LDC2L(coproc); } else { inst.opcode = Opcode::LDC2(coproc); } } else { if instr2[6] { inst.opcode = Opcode::LDCL(coproc); } else { inst.opcode = Opcode::LDC(coproc); } } inst.operands = [ Operand::CReg(CReg::from_u8(crd)), if p { Operand::RegDerefPreindexOffset( Reg::from_u8(rn), imm8 << 2, u, w, ) } else { if w { Operand::RegDerefPostindexOffset( Reg::from_u8(rn), imm8 << 2, u, false, // TODO: wback? this is true? not true? ) } else { Operand::RegDeref(Reg::from_u8(rn)) } }, if !p && !w { Operand::CoprocOption(imm8 as u8) } else { Operand::Nothing }, Operand::Nothing, ]; } } } else { // `101x` rows return Err(DecodeError::Incomplete); } } Ok(()) } yaxpeax-arm-0.3.0/src/armv7.rs000064400000000000000000005156531046102023000142700ustar 00000000000000/// Manual references in this crate, both figure and page numbers, are with respect to the document /// `DDI0406C_d_armv7ar_arm.pdf` /// `sha256: 294668ae6480133b32d85e9567cc77c5eb0e1232decdf42cac7ab480e884f6e0` //#[cfg(feature="use-serde")] //use serde::{Serialize, Deserialize}; use core::fmt::{self, Display, Formatter}; use yaxpeax_arch::{Arch, AddressDiff, Decoder, LengthedInstruction, Reader, ReadError}; #[allow(deprecated)] use yaxpeax_arch::{Colorize, NoColors, ShowContextual, YaxColors}; mod thumb; // opcode, s, w, cond /// a struct for the combined display of an opcode and possible suffixes. /// /// this includes the opcode, its optional `.s` suffix, optional `.w` suffix, and condition code, /// if any. pub struct ConditionedOpcode(pub Opcode, pub bool, pub bool, pub ConditionCode); impl Display for ConditionedOpcode { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "{}{}{}{}", self.0, if self.1 { "s" } else { "" }, if self.2 { ".w" } else { "" }, self.3) } } /// a context impl to display `arm` instructions with no additional context (no symbol name /// information, offset names, etc). this impl results in `some_instruction.contextualize(...)` /// displaying an instruction the same way its `Display` impl would. pub struct NoContext; #[allow(deprecated)] fn reg_name_colorize(reg: Reg, colors: &Y) -> impl fmt::Display { match reg.number() { 0 => colors.register("r0"), 1 => colors.register("r1"), 2 => colors.register("r2"), 3 => colors.register("r3"), 4 => colors.register("r4"), 5 => colors.register("r5"), 6 => colors.register("r6"), 7 => colors.register("r7"), 8 => colors.register("r8"), 9 => colors.register("sb"), 10 => colors.register("r10"), 11 => colors.register("fp"), 12 => colors.register("ip"), 13 => colors.register("sp"), 14 => colors.register("lr"), 15 => colors.program_counter("pc"), _ => { unreachable!(); } } } #[allow(non_snake_case)] #[allow(deprecated)] impl ShowContextual for Instruction { fn contextualize(&self, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { match self.opcode { Opcode::IT => { if let (Operand::Imm32(cond), Operand::Imm32(mask)) = (&self.operands[0], &self.operands[1]) { let inv = cond & 1 == 1; let condition = ConditionCode::build(*cond as u8); if mask & 0b0001 != 0 { // three flags write!( out, "it{}{}{} {}", if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" }, if inv ^ ((mask & 0b0100) != 0) { "e" } else { "t" }, if inv ^ ((mask & 0b0010) != 0) { "e" } else { "t" }, condition, )?; } else if mask & 0b0010 != 0 { // two flags write!( out, "it{}{} {}", if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" }, if inv ^ ((mask & 0b0100) != 0) { "e" } else { "t" }, condition, )?; } else if mask & 0b0100 != 0 { // one flag write!( out, "it{} {}", if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" }, condition, )?; } else { // no flags write!(out, "it {}", condition)?; } // if the condition is AL, it won't get displayed. append it here. if *cond == 14 { write!(out, "al")?; } return Ok(()); } else { panic!("impossible it operand"); } } Opcode::CPS(_) => { if let Operand::Imm12(aif) = &self.operands[0] { write!( out, "{} {}{}{}", &self.opcode, if aif & 0b100 != 0 { "a" } else { "" }, if aif & 0b010 != 0 { "i" } else { "" }, if aif & 0b001 != 0 { "f" } else { "" }, )?; if let Operand::Imm12(mode) = &self.operands[1] { write!(out, ", #{:x}", mode)?; } return Ok(()); } else { panic!("impossible cps operand"); } } Opcode::SETEND => { if let Operand::Imm12(i) = &self.operands[0] { return write!(out, "setend {}", if *i == 0 { "le" } else { "be" }); } else { panic!("impossible setend operand"); } } Opcode::LDR => { match self.operands { // TODO: should this be PostindexOffset? [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Reg { bits: 13 }, 4, true, false), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(Opcode::POP, self.s(), self.w(), self.condition).colorize(colors, out)?; return write!(out, " {{{}}}", reg_name_colorize(Rt, colors)); }, _ => {} } }, Opcode::STR => { match self.operands { // TODO: should this be PreindexOffset? [Operand::Reg(Rt), Operand::RegDerefPreindexOffset(Reg { bits: 13 }, 4, false, true), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(Opcode::PUSH, self.s(), self.w(), self.condition).colorize(colors, out)?; return write!(out, " {{{}}}", reg_name_colorize(Rt, colors)); }, _ => {} } }, Opcode::LDM(true, false, false, _usermode) => { // TODO: what indicates usermode in the ARM syntax? match self.operands { [Operand::RegWBack(Reg { bits: 13 }, true), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(Opcode::POP, self.s(), self.w(), self.condition).colorize(colors, out)?; write!(out, " ")?; return format_reg_list(out, list, colors); } _ => {} } } Opcode::STM(false, true, false, _usermode) => { // TODO: what indicates usermode in the ARM syntax? match self.operands { [Operand::RegWBack(Reg { bits: 13 }, true), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(Opcode::PUSH, self.s(), self.w(), self.condition).colorize(colors, out)?; write!(out, " ")?; return format_reg_list(out, list, colors); } _ => {} } } _ => {} } match self.opcode { // TODO: [add, pre, usermode] Opcode::STM(_add, _pre, _wback, _usermode) | Opcode::LDM(_add, _pre, _wback, _usermode) => { match self.operands { [Operand::RegWBack(Rr, wback), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(self.opcode, self.s(), self.w(), self.condition).colorize(colors, out)?; write!( out, " {}{}, ", reg_name_colorize(Rr, colors), if wback { "!" } else { "" } )?; return format_reg_list(out, list, colors); }, _ => { unreachable!(); } } }, Opcode::STCL(coproc) => { write!(out, "stcl p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::STC(coproc) => { write!(out, "stc p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::STC2L(coproc) => { write!(out, "stc2l p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::STC2(coproc) => { write!(out, "stc2 p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::LDC(coproc) => { write!(out, "ldc p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::LDCL(coproc) => { write!(out, "ldcl p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::LDC2(coproc) => { write!(out, "ldc2 p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::LDC2L(coproc) => { write!(out, "ldc2l p{}", coproc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::MRRC2(coproc, opc) => { write!(out, "mrrc2 p{}, {}", coproc, opc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::MCRR2(coproc, opc) => { write!(out, "mcrr2 p{}, {}", coproc, opc)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } Opcode::MRC2(coproc, opc1, opc2) => { write!(out, "mrc2 p{}, {}", coproc, opc1)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } write!(out, ", {}", opc2)?; Ok(()) } Opcode::MCR2(coproc, opc1, opc2) => { write!(out, "mcr2 p{}, {}", coproc, opc1)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } write!(out, ", {}", opc2)?; Ok(()) } Opcode::CDP2(coproc, opc1, opc2) => { write!(out, "cdp2 p{}, {}", coproc, opc1)?; let ops = self.operands.iter(); for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } write!(out, ", {}", opc2)?; Ok(()) } _ => { ConditionedOpcode(self.opcode, self.s(), self.w(), self.condition).colorize(colors, out)?; let mut ops = self.operands.iter(); if let Some(first_op) = ops.next() { if let Operand::Nothing = first_op { return Ok(()); } write!(out, " ")?; first_op.colorize(colors, out)?; } else { return Ok(()); } for op in ops { if let Operand::Nothing = op { break; } write!(out, ", ")?; op.colorize(colors, out)?; } Ok(()) } } } } #[allow(deprecated)] impl Colorize for ConditionedOpcode { fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { match self.0 { Opcode::UDF | Opcode::Invalid => { write!(out, "{}", colors.invalid_op(self)) }, Opcode::TBB | Opcode::TBH | Opcode::CBZ | Opcode::CBNZ | Opcode::IT | Opcode::B | Opcode::BL | Opcode::BLX | Opcode::BX | Opcode::BXJ => { write!(out, "{}", colors.control_flow_op(self)) }, Opcode::AND | Opcode::EOR | Opcode::ORR | Opcode::ORN | Opcode::LSL | Opcode::LSR | Opcode::ROR | Opcode::ASR | Opcode::RRX | Opcode::BIC | Opcode::ADR | Opcode::SUB | Opcode::RSB | Opcode::ADD | Opcode::ADC | Opcode::SBC | Opcode::RSC | Opcode::QADD | Opcode::QSUB | Opcode::QDADD | Opcode::QDSUB | 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::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::CLZ | Opcode::MUL | Opcode::MLA | Opcode::UMAAL | Opcode::MLS | Opcode::UMULL | Opcode::UMLAL | Opcode::SMLSD | Opcode::SMMLA | Opcode::SMMLS | Opcode::USADA8 | Opcode::USAD8 | Opcode::SDIV | Opcode::UDIV | Opcode::SMLALD(_) | Opcode::SMLSLD(_) | Opcode::SMLAD | Opcode::SMUSD | Opcode::SMMUL | Opcode::SMULW(_) | Opcode::SMUAD | Opcode::SMULL | Opcode::SMUL(_, _) | Opcode::SMAL(_, _) | Opcode::SMLA(_, _) | Opcode::SMLAW(_) | Opcode::SMLAL | Opcode::SMLAL_halfword(_, _) => { write!(out, "{}", colors.arithmetic_op(self)) }, Opcode::PUSH | Opcode::POP => { write!(out, "{}", colors.stack_op(self)) }, Opcode::TST | Opcode::TEQ | Opcode::CMP | Opcode::CMN => { write!(out, "{}", colors.comparison_op(self)) }, Opcode::LDRSH | Opcode::LDRSHT | Opcode::LDRSB | Opcode::LDRSBT | Opcode::STRD | Opcode::LDRD | Opcode::LDREXH | Opcode::STREXH | Opcode::LDREXB | Opcode::STREXB | Opcode::LDREXD | Opcode::STREXD | Opcode::LDREX | Opcode::STREX | Opcode::LDM(false, false, _, _) | Opcode::LDM(false, true, _, _) | Opcode::LDM(true, false, _, _) | Opcode::LDM(true, true, _, _) | Opcode::STM(false, false, _, _) | Opcode::STM(false, true, _, _) | Opcode::STM(true, false, _, _) | Opcode::STM(true, true, _, _) | Opcode::LDR | Opcode::STR | Opcode::LDRH | Opcode::STRH | Opcode::LDRB | Opcode::STRB | Opcode::LDRT | Opcode::STRT | Opcode::LDRHT | Opcode::STRHT | Opcode::LDRBT | Opcode::STRBT | Opcode::SWP | Opcode::SWPB | Opcode::MSR | Opcode::MRS | Opcode::CLREX | Opcode::SXTAB | Opcode::SXTAB16 | Opcode::SXTAH | Opcode::SXTB | Opcode::SXTB16 | Opcode::SXTH | Opcode::UXTAB | Opcode::UXTAB16 | Opcode::UXTAH | Opcode::UXTB | Opcode::UXTB16 | Opcode::UXTH | Opcode::PKHTB | Opcode::PKHBT | Opcode::REV | Opcode::REV16 | Opcode::REVSH | Opcode::SSAT | Opcode::SSAT16 | Opcode::SBFX | Opcode::USAT | Opcode::USAT16 | Opcode::UBFX | Opcode::BFI | Opcode::BFC | Opcode::RBIT | Opcode::SEL | Opcode::MOV | Opcode::MOVT | Opcode::MVN => { write!(out, "{}", colors.data_op(self)) }, Opcode::HINT | Opcode::NOP | Opcode::PLD | Opcode::PLI | Opcode::ISB | Opcode::DMB | Opcode::DSB | Opcode::CSDB | Opcode::SRS(_, _) | Opcode::BKPT => { write!(out, "{}", colors.misc_op(self)) }, Opcode::DBG | Opcode::CPS(_) | Opcode::CPS_modeonly | Opcode::SETEND | Opcode::ENTERX | Opcode::LEAVEX | Opcode::YIELD | Opcode::WFE | Opcode::WFI | Opcode::SEV | Opcode::ERET | Opcode::RFE(_, _) | Opcode::HVC | Opcode::SVC | Opcode::SMC | Opcode::LDC(_) | Opcode::LDCL(_) | Opcode::LDC2(_) | Opcode::LDC2L(_) | Opcode::STC(_) | Opcode::STCL(_) | Opcode::STC2(_) | Opcode::STC2L(_) | Opcode::MCRR2(_, _) | Opcode::MCR2(_, _, _) | Opcode::MRRC2(_, _) | Opcode::MRC2(_, _, _) | Opcode::MCRR(_, _) | Opcode::MRRC(_, _) | Opcode::CDP2(_, _, _) => { write!(out, "{}", colors.platform_op(self)) }, } } } impl Display for Opcode { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { match self { Opcode::LDRSH => { write!(f, "ldrsh") }, Opcode::LDRSHT => { write!(f, "ldrsht") }, Opcode::LDRSB => { write!(f, "ldrsb") }, Opcode::LDRSBT => { write!(f, "ldrsbt") }, Opcode::STRD => { write!(f, "strd") }, Opcode::LDRD => { write!(f, "ldrd") }, Opcode::LDC(_) => { write!(f, "ldc") }, Opcode::LDCL(_) => { write!(f, "ldcl") }, Opcode::LDC2(_) => { write!(f, "ldc2") }, Opcode::LDC2L(_) => { write!(f, "ldc2l") }, Opcode::STC(_) => { write!(f, "stc") }, Opcode::STCL(_) => { write!(f, "stcl") }, Opcode::STC2(_) => { write!(f, "stc2") }, Opcode::STC2L(_) => { write!(f, "stc2l") }, Opcode::MCRR2(_, _) => { write!(f, "mcrr2") }, Opcode::MCR2(_, _, _) => { write!(f, "mcr2") }, Opcode::MRRC2(_, _) => { write!(f, "mrrc2") }, Opcode::MRC2(_, _, _) => { write!(f, "mrc2") }, Opcode::MCRR(_, _) => { write!(f, "mcrr") }, Opcode::MRRC(_, _) => { write!(f, "mrrc") }, Opcode::CDP2(_, _, _) => { write!(f, "cdp2") }, Opcode::SRS(p, u) => { write!(f, "srs{}{}", if *u { "i" } else { "d" }, if *p { "b" } else { "a" }) }, Opcode::RFE(p, u) => { write!(f, "rfe{}{}", if *u { "i" } else { "d" }, if *p { "b" } else { "a" }) }, Opcode::ERET => { write!(f, "eret") }, Opcode::HVC => { write!(f, "hvc") }, Opcode::BKPT => { write!(f, "bkpt") }, Opcode::SMC => { write!(f, "smc") }, Opcode::MOVT => { write!(f, "movt") }, Opcode::QADD => { write!(f, "qadd") }, Opcode::QSUB => { write!(f, "qsub") }, Opcode::QDADD => { write!(f, "qdadd") }, Opcode::QDSUB => { write!(f, "qdsub") }, Opcode::Invalid => { write!(f, "invalid") }, Opcode::POP => { write!(f, "pop") }, Opcode::PUSH => { write!(f, "push") }, Opcode::B => { write!(f, "b") }, Opcode::BL => { write!(f, "bl") }, Opcode::BLX => { write!(f, "blx") }, Opcode::BX => { write!(f, "bx") }, Opcode::BXJ => { write!(f, "bxj") }, Opcode::CLZ => { write!(f, "clz") }, Opcode::AND => { write!(f, "and") }, Opcode::EOR => { write!(f, "eor") }, Opcode::SUB => { write!(f, "sub") }, Opcode::RSB => { write!(f, "rsb") }, Opcode::ADD => { write!(f, "add") }, Opcode::ADC => { write!(f, "adc") }, Opcode::SBC => { write!(f, "sbc") }, Opcode::RSC => { write!(f, "rsc") }, Opcode::TST => { write!(f, "tst") }, Opcode::TEQ => { write!(f, "teq") }, Opcode::CMP => { write!(f, "cmp") }, Opcode::CMN => { write!(f, "cmn") }, Opcode::ORR => { write!(f, "orr") }, Opcode::MOV => { write!(f, "mov") }, Opcode::MSR => { write!(f, "msr") }, Opcode::MRS => { write!(f, "mrs") }, Opcode::BIC => { write!(f, "bic") }, Opcode::MVN => { write!(f, "mvn") }, Opcode::LSL => { write!(f, "lsl") }, Opcode::LSR => { write!(f, "lsr") }, Opcode::ASR => { write!(f, "asr") }, Opcode::RRX => { write!(f, "rrx") }, Opcode::ROR => { write!(f, "ror") }, Opcode::ADR => { write!(f, "adr") }, Opcode::LDREXH => { write!(f, "ldrexh") }, Opcode::STREXH => { write!(f, "strexh") }, Opcode::LDREXB => { write!(f, "ldrexb") }, Opcode::STREXB => { write!(f, "strexb") }, Opcode::LDREXD => { write!(f, "ldrexd") }, Opcode::STREXD => { write!(f, "strexd") }, Opcode::LDREX => { write!(f, "ldrex") }, Opcode::STREX => { write!(f, "strex") }, Opcode::LDM(false, false, _, _) => { write!(f, "ldmda") }, Opcode::LDM(false, true, _, _) => { write!(f, "ldmdb") }, // TODO: seems like these are backwards Opcode::LDM(true, false, _, _) => { write!(f, "ldm") }, Opcode::LDM(true, true, _, _) => { write!(f, "ldmia") }, Opcode::STM(false, false, _, _) => { write!(f, "stmda") }, Opcode::STM(false, true, _, _) => { write!(f, "stmdb") }, // TODO: seems like these are backwards Opcode::STM(true, false, _, _) => { write!(f, "stm") }, Opcode::STM(true, true, _, _) => { write!(f, "stmia") }, Opcode::LDR => { write!(f, "ldr") }, Opcode::STR => { write!(f, "str") }, Opcode::LDRH => { write!(f, "ldrh") }, Opcode::STRH => { write!(f, "strh") }, Opcode::LDRB => { write!(f, "ldrb") }, Opcode::STRB => { write!(f, "strb") }, Opcode::LDRT => { write!(f, "ldrt") }, Opcode::STRT => { write!(f, "strt") }, Opcode::LDRHT => { write!(f, "ldrht") }, Opcode::STRHT => { write!(f, "strht") }, Opcode::LDRBT => { write!(f, "ldrbt") }, Opcode::STRBT => { write!(f, "strbt") }, Opcode::SWP => { write!(f, "swp") }, Opcode::SWPB => { write!(f, "swpb") }, Opcode::SDIV => { write!(f, "sdiv") }, Opcode::UDIV => { write!(f, "udiv") }, Opcode::MUL => { write!(f, "mul") }, Opcode::MLA => { write!(f, "mla") }, Opcode::UMAAL => { write!(f, "umaal") }, Opcode::MLS => { write!(f, "mls") }, Opcode::UMULL => { write!(f, "umull") }, Opcode::UMLAL => { write!(f, "umlal") }, Opcode::SMULL => { write!(f, "smull") }, Opcode::SMLA(first, second) => { write!(f, "smla{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) } Opcode::SMLAL => { write!(f, "smlal") }, Opcode::SMLAL_halfword(first, second) => { write!(f, "smlal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) } Opcode::SMUL(first, second) => { write!(f, "smul{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) }, Opcode::SMAL(first, second) => { write!(f, "smal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) }, Opcode::SMLAW(second) => { write!(f, "smlaw{}", if *second { "t" } else { "b" }) }, Opcode::SMULW(second) => { write!(f, "smulw{}", if *second { "t" } else { "b" }) }, Opcode::SMLALD(second) => { write!(f, "smlald{}", if *second { "t" } else { "b" }) }, Opcode::SMLSLD(second) => { write!(f, "smlsld{}", if *second { "t" } else { "b" }) }, Opcode::SMLSD => { write!(f, "smlsd") }, Opcode::SMMLA => { write!(f, "smmla") }, Opcode::SMMLS => { write!(f, "smmls") }, Opcode::USADA8 => { write!(f, "usada8") }, Opcode::USAD8 => { write!(f, "usad8") }, Opcode::SMLAD => { write!(f, "smlad") }, Opcode::SMUSD => { write!(f, "smusd") }, Opcode::SMMUL => { write!(f, "smmul") }, Opcode::SMUAD => { write!(f, "smuad") }, Opcode::TBB => { write!(f, "tbb") }, Opcode::TBH => { write!(f, "tbh") }, Opcode::UDF => { write!(f, "udf") }, Opcode::SVC => { write!(f, "svc") }, Opcode::WFE => { write!(f, "wfe") }, Opcode::WFI => { write!(f, "wfi") }, Opcode::SEV => { write!(f, "sev") }, Opcode::CSDB => { write!(f, "csdb") }, Opcode::YIELD => { write!(f, "yield") }, Opcode::HINT => { write!(f, "hint") }, Opcode::NOP => { write!(f, "nop") }, Opcode::LEAVEX => { write!(f, "leavex") }, Opcode::ENTERX => { write!(f, "enterx") }, Opcode::CLREX => { write!(f, "clrex") }, Opcode::DSB => { write!(f, "dsb") }, Opcode::DMB => { write!(f, "dmb") }, Opcode::ISB => { write!(f, "isb") }, Opcode::SXTH => { write!(f, "sxth") }, Opcode::UXTH => { write!(f, "uxth") }, Opcode::SXTB16 => { write!(f, "sxtb16") }, Opcode::UXTB16 => { write!(f, "uxtb16") }, Opcode::SXTB => { write!(f, "sxtb") }, Opcode::UXTB => { write!(f, "uxtb") }, Opcode::SXTAH => { write!(f, "sxtah") }, Opcode::UXTAH => { write!(f, "uxtah") }, Opcode::SXTAB16 => { write!(f, "sxtab16") }, Opcode::UXTAB16 => { write!(f, "uxtab16") }, Opcode::SXTAB => { write!(f, "sxtab") }, Opcode::UXTAB => { write!(f, "uxtab") }, Opcode::CBZ => { write!(f, "cbz") }, Opcode::CBNZ => { write!(f, "cbnz") }, Opcode::SETEND => { write!(f, "setend") }, Opcode::CPS(disable) => { write!(f, "cps{}", if *disable { "id" } else { "ie" }) }, Opcode::CPS_modeonly => { write!(f, "cps") }, Opcode::REV => { write!(f, "rev") }, Opcode::REV16 => { write!(f, "rev16") }, Opcode::REVSH => { write!(f, "revsh") }, Opcode::IT => { write!(f, "it") }, Opcode::PKHTB => { write!(f, "pkhtb") }, Opcode::PKHBT => { write!(f, "pkhbt") }, Opcode::ORN => { write!(f, "orn") }, Opcode::SSAT => { write!(f, "ssat") }, Opcode::SSAT16 => { write!(f, "ssat16") }, Opcode::SBFX => { write!(f, "sbfx") }, Opcode::USAT => { write!(f, "usat") }, Opcode::USAT16 => { write!(f, "usat16") }, Opcode::UBFX => { write!(f, "ubfx") }, Opcode::BFI => { write!(f, "bfi") }, Opcode::BFC => { write!(f, "bfc") }, Opcode::DBG => { write!(f, "dbg") }, Opcode::PLD => { write!(f, "pld") }, Opcode::PLI => { write!(f, "pli") }, Opcode::RBIT => { write!(f, "rbit") }, Opcode::SEL => { write!(f, "sel") }, Opcode::SADD16 => { write!(f, "sadd16") }, Opcode::QADD16 => { write!(f, "qadd16") }, Opcode::SHADD16 => { write!(f, "shadd16") }, Opcode::SASX => { write!(f, "sasx") }, Opcode::QASX => { write!(f, "qasx") }, Opcode::SHASX => { write!(f, "shasx") }, Opcode::SSAX => { write!(f, "ssax") }, Opcode::QSAX => { write!(f, "qsax") }, Opcode::SHSAX => { write!(f, "shsax") }, Opcode::SSUB16 => { write!(f, "ssub16") }, Opcode::QSUB16 => { write!(f, "qsub16") }, Opcode::SHSUB16 => { write!(f, "shsub16") }, Opcode::SADD8 => { write!(f, "sadd8") }, Opcode::QADD8 => { write!(f, "qadd8") }, Opcode::SHADD8 => { write!(f, "shadd8") }, Opcode::SSUB8 => { write!(f, "ssub8") }, Opcode::QSUB8 => { write!(f, "qsub8") }, Opcode::SHSUB8 => { write!(f, "shsub8") }, Opcode::UADD16 => { write!(f, "uadd16") }, Opcode::UQADD16 => { write!(f, "uqadd16") }, Opcode::UHADD16 => { write!(f, "uhadd16") }, Opcode::UASX => { write!(f, "uasx") }, Opcode::UQASX => { write!(f, "uqasx") }, Opcode::UHASX => { write!(f, "uhasx") }, Opcode::USAX => { write!(f, "usax") }, Opcode::UQSAX => { write!(f, "uqsax") }, Opcode::UHSAX => { write!(f, "uhsax") }, Opcode::USUB16 => { write!(f, "usub16") }, Opcode::UQSUB16 => { write!(f, "uqsub16") }, Opcode::UHSUB16 => { write!(f, "uhsub16") }, Opcode::UADD8 => { write!(f, "uadd8") }, Opcode::UQADD8 => { write!(f, "uqadd8") }, Opcode::UHADD8 => { write!(f, "uhadd8") }, Opcode::USUB8 => { write!(f, "usub8") }, Opcode::UQSUB8 => { write!(f, "uqsub8") }, Opcode::UHSUB8 => { write!(f, "uhsub8") }, } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[allow(non_camel_case_types)] #[allow(missing_docs)] pub enum Opcode { Invalid, /* * These two don't really have direct encodings, but are for the specific instances * where the semantics of the original instruction are the same as push (specifically * ldm/stm/mov that write to the stack and increment/decrement appropriately */ POP, PUSH, B, BL, BLX, BX, BXJ, AND, EOR, SUB, RSB, ADD, ADC, SBC, RSC, TST, TEQ, CMP, CMN, ORR, MOV, BIC, MVN, LSL, LSR, ASR, RRX, ROR, ADR, MSR, MRS, CLZ, LDREXH, STREXH, LDREXB, STREXB, LDREXD, STREXD, LDREX, STREX, LDM(bool, bool, bool, bool), STM(bool, bool, bool, bool), LDR, STR, LDRH, STRH, LDRB, STRB, LDRSH, LDRSHT, LDRSB, LDRSBT, STRD, LDRD, LDC(u8), LDCL(u8), LDC2(u8), LDC2L(u8), STC(u8), STCL(u8), STC2(u8), STC2L(u8), MCRR2(u8, u8), MCR2(u8, u8, u8), MRRC2(u8, u8), MCRR(u8, u8), MRRC(u8, u8), MRC2(u8, u8, u8), CDP2(u8, u8, u8), SRS(bool, bool), RFE(bool, bool), LDRT, STRT, LDRHT, STRHT, LDRBT, STRBT, SWP, SWPB, MUL, MLA, UMAAL, MLS, UMULL, UMLAL, SMULL, SMUL(bool, bool), SMLA(bool, bool), SMLAL, SMLAL_halfword(bool, bool), SMAL(bool, bool), SMLAW(bool), ERET, BKPT, HVC, SMC, MOVT, QDSUB, QDADD, QSUB, QADD, TBB, TBH, UDF, SVC, WFE, WFI, SEV, CSDB, YIELD, HINT, NOP, LEAVEX, ENTERX, CLREX, DSB, DMB, ISB, SXTH, UXTH, SXTB16, UXTB16, SXTB, UXTB, SXTAH, UXTAH, SXTAB16, UXTAB16, SXTAB, UXTAB, CBZ, CBNZ, SETEND, CPS(bool), CPS_modeonly, REV, REV16, REVSH, IT, PKHTB, PKHBT, ORN, SSAT, SSAT16, SBFX, USAT, USAT16, UBFX, BFI, BFC, DBG, PLD, PLI, RBIT, SEL, SADD16, QADD16, SHADD16, SASX, QASX, SHASX, SSAX, QSAX, SHSAX, SSUB16, QSUB16, SHSUB16, SADD8, QADD8, SHADD8, SSUB8, QSUB8, SHSUB8, UADD16, UQADD16, UHADD16, UASX, UQASX, UHASX, USAX, UQSAX, UHSAX, USUB16, UQSUB16, UHSUB16, UADD8, UQADD8, UHADD8, USUB8, UQSUB8, UHSUB8, SMLSD, SMMLA, SMMLS, USADA8, USAD8, SMLAD, SMUSD, SMMUL, SMULW(bool), SMUAD, SDIV, UDIV, SMLALD(bool), SMLSLD(bool), } static DATA_PROCESSING_OPCODES: [Opcode; 16] = [ Opcode::AND, Opcode::EOR, Opcode::SUB, Opcode::RSB, Opcode::ADD, Opcode::ADC, Opcode::SBC, Opcode::RSC, Opcode::TST, Opcode::TEQ, Opcode::CMP, Opcode::CMN, Opcode::ORR, Opcode::MOV, Opcode::BIC, Opcode::MVN ]; /// a struct describiing a shifted register operand. this is primarily interesting in that it can /// be translated to a `RegShiftStyle` for further interpretation. #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[repr(transparent)] pub struct RegShift { data: u16 } impl RegShift { /// convert an instruction's `RegShift` operand into something more appropriate for /// programmatic use. pub fn into_shift(&self) -> RegShiftStyle { if self.data & 0b10000 == 0 { RegShiftStyle::RegImm(RegImmShift { data: self.data }) } else { RegShiftStyle::RegReg(RegRegShift { data: self.data }) } } /// don't use this. it's for armv7 testing only. #[doc(hidden)] pub fn from_raw(data: u16) -> Self { RegShift { data } } } /// an enum describing one of two ways a shifted register operand may be shifted. pub enum RegShiftStyle { /// a register shifted by an immediate. RegImm(RegImmShift), /// a register shifted by a register. RegReg(RegRegShift), } /// a register shifted by a register. #[repr(transparent)] pub struct RegRegShift { data: u16 } /// the way a shift operation is carried out. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ShiftStyle { /// left-shift the value, filling in zeroes. LSL = 0, /// right-shift the value, filling in zeroes. LSR = 1, /// arithmetic shift right, filling with the top bit of the value (sign-extending). ASR = 2, /// rotate-right, filling with bits shifted out of the value. ROR = 3, } impl Display for ShiftStyle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ShiftStyle::LSL => write!(f, "lsl"), ShiftStyle::LSR => write!(f, "lsr"), ShiftStyle::ASR => write!(f, "asr"), ShiftStyle::ROR => write!(f, "ror"), } } } impl ShiftStyle { fn from(bits: u8) -> ShiftStyle { match bits { 0b00 => ShiftStyle::LSL, 0b01 => ShiftStyle::LSR, 0b10 => ShiftStyle::ASR, 0b11 => ShiftStyle::ROR, _ => unreachable!("bad ShiftStyle index") } } } impl RegRegShift { /// the general-purpose register, an amount to shift the shiftee. pub fn shifter(&self) -> Reg { Reg::from_u8((self.data >> 8) as u8 & 0b1111) } /// the way in which this register is shifted. pub fn stype(&self) -> ShiftStyle { ShiftStyle::from((self.data >> 5) as u8 & 0b11) } /// the general-purpose register to be shifted. pub fn shiftee(&self) -> Reg { Reg::from_u8(self.data as u8 & 0b1111) } } /// a register shifted by an immediate. #[repr(transparent)] pub struct RegImmShift { data: u16 } impl RegImmShift { /// the immediate this register is shifted by. pub fn imm(&self) -> u8 { (self.data >> 7) as u8 & 0b11111 } /// the way in which this register is shifted. pub fn stype(&self) -> ShiftStyle { ShiftStyle::from((self.data >> 5) as u8 & 0b11) } /// the general-purpose register to be shifted. pub fn shiftee(&self) -> Reg { Reg::from_u8(self.data as u8 & 0b1111) } } /// a struct describing an `arm` register. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(transparent)] pub struct Reg { bits: u8 } impl Reg { #[allow(non_snake_case)] fn from_sysm(R: bool, M: u8) -> Option { /* * Is one of: * • _, encoded with R==0. * • ELR_hyp, encoded with R==0. * • SPSR_, encoded with R==1. * For a full description of the encoding of this field, see Encoding and use of Banked register * transfer * instructions on page B9-1959. * */ if R == false { [ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(8))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(9))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(10))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(11))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(12))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(14))), None, Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(8))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(9))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(10))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(11))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(12))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(14))), None, Some(Operand::BankedReg(Bank::Irq, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Irq, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Svc, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Svc, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Abt, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Abt, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Und, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Und, Reg::from_u8(14))), None, None, None, None, Some(Operand::BankedReg(Bank::Mon, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Mon, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Hyp, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Hyp, Reg::from_u8(14))), ][M as usize] } else { if M == 0b01110 { Some(Operand::BankedSPSR(Bank::Fiq)) } else if M == 0b10000 { Some(Operand::BankedSPSR(Bank::Irq)) } else if M == 0b10010 { Some(Operand::BankedSPSR(Bank::Svc)) } else if M == 0b10100 { Some(Operand::BankedSPSR(Bank::Abt)) } else if M == 0b10110 { Some(Operand::BankedSPSR(Bank::Und)) } else if M == 0b11100 { Some(Operand::BankedSPSR(Bank::Mon)) } else if M == 0b11110 { Some(Operand::BankedSPSR(Bank::Hyp)) } else { None } } } /// create a new `Reg` with the specified number. /// /// panics if `bits` is out of range (16 or above). pub fn from_u8(bits: u8) -> Reg { if bits > 0b1111 { panic!("register number out of range"); } Reg { bits } } /// get the number of this register. the returned value will be between 0 and 15. pub fn number(&self) -> u8 { self.bits } } /// a control register. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(transparent)] pub struct CReg { bits: u8 } impl Display for CReg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "c{}", self.bits) } } impl CReg { /// create a new `CReg` with the specified number. /// /// panics if `bits` is out of range (16 or above). pub fn from_u8(bits: u8) -> CReg { if bits > 0b1111 { panic!("register number out of range"); } CReg { bits } } /// get the number of this register. the returned value will be between 0 and 15. pub fn number(&self) -> u8 { self.bits } } impl Display for StatusRegMask { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { StatusRegMask::CPSR_C => write!(f, "cpsr_c"), StatusRegMask::CPSR_X => write!(f, "cpsr_x"), StatusRegMask::CPSR_XC => write!(f, "cpsr_xc"), StatusRegMask::APSR_G => write!(f, "apsr_g"), StatusRegMask::CPSR_SC => write!(f, "cpsr_sc"), StatusRegMask::CPSR_SX => write!(f, "cpsr_sx"), StatusRegMask::CPSR_SXC => write!(f, "cpsr_sxc"), StatusRegMask::APSR_NZCVQ => write!(f, "apsr_nzcvq"), StatusRegMask::CPSR_FC => write!(f, "cpsr_fc"), StatusRegMask::CPSR_FX => write!(f, "cpsr_fx"), StatusRegMask::CPSR_FXC => write!(f, "cpsr_fxc"), StatusRegMask::APSR_NZCVQG => write!(f, "apsr_nzcvqg"), StatusRegMask::CPSR_FSC => write!(f, "cpsr_fsc"), StatusRegMask::CPSR_FSX => write!(f, "cpsr_fsx"), StatusRegMask::CPSR_FSXC => write!(f, "cpsr_fsxc"), StatusRegMask::SPSR => write!(f, "spsr"), StatusRegMask::SPSR_C => write!(f, "spsr_c"), StatusRegMask::SPSR_X => write!(f, "spsr_x"), StatusRegMask::SPSR_XC => write!(f, "spsr_xc"), StatusRegMask::SPSR_S => write!(f, "spsr_s"), StatusRegMask::SPSR_SC => write!(f, "spsr_sc"), StatusRegMask::SPSR_SX => write!(f, "spsr_sx"), StatusRegMask::SPSR_SXC => write!(f, "spsr_sxc"), StatusRegMask::SPSR_F => write!(f, "spsr_f"), StatusRegMask::SPSR_FC => write!(f, "spsr_fc"), StatusRegMask::SPSR_FX => write!(f, "spsr_fx"), StatusRegMask::SPSR_FXC => write!(f, "spsr_fxc"), StatusRegMask::SPSR_FS => write!(f, "spsr_fs"), StatusRegMask::SPSR_FSC => write!(f, "spsr_fsc"), StatusRegMask::SPSR_FSX => write!(f, "spsr_fsx"), StatusRegMask::SPSR_FSXC => write!(f, "spsr_fsxc"), } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[allow(non_camel_case_types)] #[allow(missing_docs)] pub enum StatusRegMask { // Note 0b0000 is unused (as is 0b10000) CPSR_C = 0b0001, CPSR_X = 0b0010, CPSR_XC = 0b0011, APSR_G = 0b0100, CPSR_SC = 0b0101, CPSR_SX = 0b0110, CPSR_SXC = 0b0111, APSR_NZCVQ = 0b1000, CPSR_FC = 0b1001, CPSR_FX = 0b1010, CPSR_FXC = 0b1011, APSR_NZCVQG = 0b1100, CPSR_FSC = 0b1101, CPSR_FSX = 0b1110, CPSR_FSXC = 0b1111, SPSR = 0b10000, SPSR_C = 0b10001, SPSR_X = 0b10010, SPSR_XC = 0b10011, SPSR_S = 0b10100, SPSR_SC = 0b10101, SPSR_SX = 0b10110, SPSR_SXC = 0b10111, SPSR_F = 0b11000, SPSR_FC = 0b11001, SPSR_FX = 0b11010, SPSR_FXC = 0b11011, SPSR_FS = 0b11100, SPSR_FSC = 0b11101, SPSR_FSX = 0b11110, SPSR_FSXC = 0b11111, } impl StatusRegMask { fn from_raw(raw: u8) -> Result { if raw == 0 { // invalid status reg mask value return Err(DecodeError::InvalidOperand); } Ok([ StatusRegMask::CPSR_C, // actually unreachable StatusRegMask::CPSR_C, StatusRegMask::CPSR_X, StatusRegMask::CPSR_XC, StatusRegMask::APSR_G, StatusRegMask::CPSR_SC, StatusRegMask::CPSR_SX, StatusRegMask::CPSR_SXC, StatusRegMask::APSR_NZCVQ, StatusRegMask::CPSR_FC, StatusRegMask::CPSR_FX, StatusRegMask::CPSR_FXC, StatusRegMask::APSR_NZCVQG, StatusRegMask::CPSR_FSC, StatusRegMask::CPSR_FSX, StatusRegMask::CPSR_FSXC, StatusRegMask::SPSR, StatusRegMask::SPSR_C, StatusRegMask::SPSR_X, StatusRegMask::SPSR_XC, StatusRegMask::SPSR_S, StatusRegMask::SPSR_SC, StatusRegMask::SPSR_SX, StatusRegMask::SPSR_SXC, StatusRegMask::SPSR_F, StatusRegMask::SPSR_FC, StatusRegMask::SPSR_FX, StatusRegMask::SPSR_FXC, StatusRegMask::SPSR_FS, StatusRegMask::SPSR_FSC, StatusRegMask::SPSR_FSX, StatusRegMask::SPSR_FSXC, ][raw as usize]) } } /// an operand in an `arm` instruction. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Operand { /// a general-purpose register. Reg(Reg), /// a general-purpose register, with writeback. these generally imply an increment by width of /// a memort operand, depending on the instruction. RegWBack(Reg, bool), /// a list of registers specified as a bitmask from bits 0 to 15. RegList(u16), /// a memory access, dereferencing a general-purpose register. RegDeref(Reg), /// a memory access, dereferencing a shifted general-purpose register with register or /// immediate offset. RegShift(RegShift), /// a memory access of a register, post-indexed a register shifted by register or immediate. /// the first bool indicates if the shifted-register is added or subtracted ot the base /// register, while the second bool indicates if the resulting address is written back to the /// base register. RegDerefPostindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback /// a memory access of a register, pre-indexed with a register shifted by register or /// immediate. the first bool indicates if the shifted-register is added or subtracted ot the /// base register, while the second bool indicates if the resulting address is written back to /// the base register. RegDerefPreindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback /// a memory access of a register, post-indexed with an immediate. the first bool indicates if /// the shifted-register is added or subtracted ot the base register, while the second bool /// indicates if the resulting address is written back to the base register. RegDerefPostindexOffset(Reg, u16, bool, bool), // add/sub, wback /// a memory access of a register, pre-indexed with an immediate. the first bool indicates if /// the shifted-register is added or subtracted ot the base register, while the second bool /// indicates if the resulting address is written back to the base register. RegDerefPreindexOffset(Reg, u16, bool, bool), // add/sub, wback /// a memory access of a register, post-indexed with a register. the first bool indicates if the /// shifted-register is added or subtracted ot the base register, while the second bool /// indicates if the resulting address is written back to the base register. RegDerefPostindexReg(Reg, Reg, bool, bool), // add/sub, wback /// a memory access of a register, pre-indexed with a register. the first bool indicates if the /// shifted-register is added or subtracted ot the base register, while the second bool /// indicates if the resulting address is written back to the base register. RegDerefPreindexReg(Reg, Reg, bool, bool), // add/sub, wback /// a 12-bit immediate, stored in a `u16`. Imm12(u16), /// a 32-bit immediate, stored in a `u32`. Imm32(u32), /// a pc-relative branch, with 32-bit signed offset, left-shifted by 2. BranchOffset(i32), /// a pc-relative branch, with 32-bit signed offset, left-shifted by 1. BranchThumbOffset(i32), /// a coprocessor index. Coprocessor(u8), /// a coprocessor option number. CoprocOption(u8), /// an `arm` control register. CReg(CReg), /// an `arm` banked register, either `usr` (general-purpose) bank or one of the alternate sets /// of `arm` registers. BankedReg(Bank, Reg), /// `spsr` in some `arm` register bank. BankedSPSR(Bank), /// a mask of bits for the `spsr` register. StatusRegMask(StatusRegMask), /// the `apsr` register. APSR, /// the `spsr` register. SPSR, /// the `cpsr` register. CPSR, /// "no operand". since an instruction's `operands` array is always four entries, this is used /// to fill space, if any, after recording an instruction's extant operands. Nothing, } /// a register bank for a register in `armv7` or below. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum Bank { Usr, Fiq, Irq, Svc, Abt, Und, Mon, Hyp, } impl Display for Bank { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Bank::Usr => write!(f, "usr"), Bank::Fiq => write!(f, "fiq"), Bank::Irq => write!(f, "irq"), Bank::Svc => write!(f, "svc"), Bank::Abt => write!(f, "abt"), Bank::Und => write!(f, "und"), Bank::Mon => write!(f, "mon"), Bank::Hyp => write!(f, "hyp"), } } } #[allow(deprecated)] impl Colorize for Operand { fn colorize(&self, colors: &Y, f: &mut T) -> fmt::Result { match self { Operand::RegList(list) => { format_reg_list(f, *list, colors) } Operand::BankedReg(bank, reg) => { write!(f, "{}_{}", reg_name_colorize(*reg, colors), bank) }, Operand::BankedSPSR(bank) => { write!(f, "spsr_{}", bank) }, Operand::Reg(reg) => { write!(f, "{}", reg_name_colorize(*reg, colors)) } Operand::RegDeref(reg) => { write!(f, "[{}]", reg_name_colorize(*reg, colors)) } Operand::RegShift(shift) => { format_shift(f, *shift, colors) } Operand::RegDerefPostindexRegShift(reg, shift, add, wback) => { format_reg_shift_mem(f, *reg, *shift, *add, false, *wback, colors) } Operand::RegDerefPreindexRegShift(reg, shift, add, wback) => { format_reg_shift_mem(f, *reg, *shift, *add, true, *wback, colors) } Operand::RegDerefPostindexOffset(reg, offs, add, wback) => { format_reg_imm_mem(f, *reg, *offs, *add, false, *wback, colors) } Operand::RegDerefPreindexOffset(reg, offs, add, wback) => { format_reg_imm_mem(f, *reg, *offs, *add, true, *wback, colors) } Operand::RegDerefPostindexReg(reg, offsreg, add, wback) => { write!(f, "[{}], {}{}{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors), if *wback { "!" } else { "" }) } Operand::RegDerefPreindexReg(reg, offsreg, add, wback) => { write!(f, "[{}, {}{}]{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors), if *wback { "!" } else { "" }) } Operand::Imm12(imm) => { write!(f, "{:#x}", imm) } Operand::Imm32(imm) => { write!(f, "{:#x}", imm) } Operand::BranchOffset(imm) => { if *imm < 0 { write!(f, "$-{:#x}", (-*imm) * 4) } else { write!(f, "$+{:#x}", *imm * 4) } } Operand::BranchThumbOffset(imm) => { if *imm < 0 { write!(f, "$-{:#x}", (-*imm) * 2) } else { write!(f, "$+{:#x}", *imm * 2) } } Operand::Coprocessor(num) => { write!(f, "p{}", num) } Operand::CoprocOption(num) => { write!(f, "{{{:#x}}}", num) } Operand::RegWBack(reg, wback) => { if *wback { write!(f, "{}!", reg_name_colorize(*reg, colors)) } else { write!(f, "{}", reg_name_colorize(*reg, colors)) } } Operand::CReg(creg) => { write!(f, "{}", creg) } Operand::StatusRegMask(mask) => { write!(f, "{}", mask) } Operand::APSR => write!(f, "apsr"), Operand::SPSR => write!(f, "spsr"), Operand::CPSR => write!(f, "cpsr"), Operand::Nothing => { panic!("tried to print Nothing operand") }, } } } /// a `armv7` or below instruction. #[derive(Debug, PartialEq, Eq)] pub struct Instruction { /// the condition code for this instruction, defaults to `AL` if the instruction is /// unconditional. pub condition: ConditionCode, /// the opcode of this instruction. pub opcode: Opcode, /// operands for the decoded instruction. operands are populated from index 0, to 1, 2, and 3. /// operands from the instruction are non-`Operand::Nothing`. pub operands: [Operand; 4], /// does this instruction update flags, while variants that do not update flags exist? pub s: bool, /// is this a 32-bit thumb instruction? pub wide: bool, /// and if it is a 32-bit thumb instruction, should the .w suffix be shown? pub thumb_w: bool, /// and generally speaking, was this just a thumb-encoded instruction? pub thumb: bool, } /// the kinds of errors possibly encountered in trying to decode an `armv7` or below instruction. #[derive(Debug, PartialEq, Copy, Clone)] pub enum DecodeError { /// the input was insufficient to decode a full instruction. for non-thumb instructions, this means /// the input was not at least four bytes long. for thumb instructions, the input was either /// not two bytes, or not four bytes, depending on how much the instruction would need. ExhaustedInput, /// the instruction encodes an opcode that is not valid. InvalidOpcode, /// the instruction encodes an operand that is not valid. InvalidOperand, /// `yaxpeax-arm` doesn't know how to decode this, but it may be a valid instruction. the /// instruction decoder is not complete, sorry. :( /// /// in practice this typically indicates some kinds of coprocessor instruction, or `ARMv7` SIMD /// instruction. Incomplete, /// the instruction includes reserved bits that were not set as required. Nonconforming, /// the input encodes an instruction that is explicitly undefined. Undefined, /// the input encodes an instruction with unpredictable behavior. Unpredictable, } impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use yaxpeax_arch::DecodeError; f.write_str(self.description()) } } #[cfg(feature = "std")] extern crate std; #[cfg(feature = "std")] impl std::error::Error for DecodeError { fn description(&self) -> &str { ::description(self) } } impl From for DecodeError { fn from(_e: ReadError) -> DecodeError { DecodeError::ExhaustedInput } } impl yaxpeax_arch::DecodeError for DecodeError { fn data_exhausted(&self) -> bool { self == &DecodeError::ExhaustedInput } fn bad_opcode(&self) -> bool { self == &DecodeError::InvalidOpcode } fn bad_operand(&self) -> bool { self == &DecodeError::InvalidOperand || self == &DecodeError::Unpredictable } fn description(&self) -> &'static str { match self { DecodeError::ExhaustedInput => "exhausted input", DecodeError::InvalidOpcode => "invalid opcode", DecodeError::InvalidOperand => "invalid operand", DecodeError::Incomplete => "incomplete decoder", DecodeError::Nonconforming => "invalid reserved bits", DecodeError::Undefined => "undefined encoding", DecodeError::Unpredictable => "unpredictable instruction", } } } impl yaxpeax_arch::Instruction for Instruction { // TODO: this is wrong!! fn well_defined(&self) -> bool { true } } impl Default for Instruction { fn default() -> Self { Instruction { condition: ConditionCode::AL, opcode: Opcode::Invalid, operands: [Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing], s: false, thumb_w: false, wide: false, thumb: false, } } } impl Instruction { fn set_s(&mut self, value: bool) { self.s = value; } /// does this instruction set status flags? pub fn s(&self) -> bool { self.s } pub(crate) fn set_w(&mut self, value: bool) { self.thumb_w = value; } /// was this instruction encoded in `thumb` mode and still 4 bytes, *and* requires a `.w` /// suffix on the opcode? pub fn w(&self) -> bool { self.thumb_w } pub(crate) fn set_wide(&mut self, value: bool) { self.wide = value; } /// was this instruction encoded in `thumb` mode and still 4 bytes? pub fn wide(&self) -> bool { self.wide } pub(crate) fn set_thumb(&mut self, value: bool) { self.thumb = value; } /// was this instruction encoded in `thumb` mode? pub fn thumb(&self) -> bool { self.thumb } } #[allow(deprecated)] fn format_reg_list(f: &mut T, mut list: u16, colors: &Y) -> Result<(), fmt::Error> { write!(f, "{{")?; let mut i = 0; let mut tail = false; while i < 16 { let present = (list & 1) == 1; if present { if tail { write!(f, ", ")?; } else { tail = true; } write!(f, "{}", reg_name_colorize(Reg::from_u8(i), colors))?; } i += 1; list >>= 1; } write!(f, "}}") } #[allow(non_snake_case)] #[allow(deprecated)] fn format_shift(f: &mut T, shift: RegShift, colors: &Y) -> Result<(), fmt::Error> { match shift.into_shift() { RegShiftStyle::RegImm(imm_shift) => { if imm_shift.imm() == 0 && imm_shift.stype() == ShiftStyle::LSL { write!(f, "{}", reg_name_colorize(imm_shift.shiftee(), colors)) } else { write!(f, "{}, {} {}", reg_name_colorize(imm_shift.shiftee(), colors), imm_shift.stype(), imm_shift.imm()) } } RegShiftStyle::RegReg(reg_shift) => { write!(f, "{}, {} {}", reg_name_colorize(reg_shift.shiftee(), colors), reg_shift.stype(), reg_name_colorize(reg_shift.shifter(), colors)) }, } } #[allow(non_snake_case)] #[allow(deprecated)] fn format_reg_shift_mem(f: &mut T, Rd: Reg, shift: RegShift, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { let op = if add { "" } else { "-" }; match (pre, wback) { (true, true) => { write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?; format_shift(f, shift, colors)?; write!(f, "]!") }, (true, false) => { write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?; format_shift(f, shift, colors)?; write!(f, "]") }, (false, true) => { unreachable!("preindex with writeback is not an ARM addressing mode. this is a decoder bug."); }, (false, false) => { write!(f, "[{}], {}", reg_name_colorize(Rd, colors), op)?; format_shift(f, shift, colors) } } } #[allow(non_snake_case)] #[allow(deprecated)] fn format_reg_imm_mem(f: &mut T, Rn: Reg, imm: u16, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { if imm != 0 { let op = if add { "" } else { "-" }; match (pre, wback) { (true, true) => { write!(f, "[{}, {}{:#x}]!", reg_name_colorize(Rn, colors), op, imm) }, (true, false) => { write!(f, "[{}, {}{:#x}]", reg_name_colorize(Rn, colors), op, imm) }, (false, _) => { write!(f, "[{}], {}{:#x}", reg_name_colorize(Rn, colors), op, imm) } } } else { match (pre, wback) { (true, true) => { write!(f, "[{}]!", reg_name_colorize(Rn, colors)) }, (true, false) => { write!(f, "[{}]", reg_name_colorize(Rn, colors)) }, (false, _) => { write!(f, "[{}]", reg_name_colorize(Rn, colors)) } } } } impl Display for Instruction { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { #[allow(deprecated)] self.contextualize(&NoColors, 0, Some(&NoContext), f) } } impl LengthedInstruction for Instruction { type Unit = AddressDiff<::Address>; fn min_size() -> Self::Unit { // TODO: this is contingent on the decoder mode... AddressDiff::from_const(4) } fn len(&self) -> Self::Unit { if self.thumb && !self.wide { AddressDiff::from_const(2) } else { AddressDiff::from_const(4) } } } /// a condition code for am `armv7` or below instruction. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[allow(missing_docs)] pub enum ConditionCode { EQ, NE, HS, LO, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL } impl Display for ConditionCode { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { match self { ConditionCode::EQ => write!(f, "eq"), ConditionCode::NE => write!(f, "ne"), ConditionCode::HS => write!(f, "hs"), ConditionCode::LO => write!(f, "lo"), ConditionCode::MI => write!(f, "mi"), ConditionCode::PL => write!(f, "pl"), ConditionCode::VS => write!(f, "vs"), ConditionCode::VC => write!(f, "vc"), ConditionCode::HI => write!(f, "hi"), ConditionCode::LS => write!(f, "ls"), ConditionCode::GE => write!(f, "ge"), ConditionCode::LT => write!(f, "lt"), ConditionCode::GT => write!(f, "gt"), ConditionCode::LE => write!(f, "le"), ConditionCode::AL => Ok(()) } } } impl ConditionCode { fn build(value: u8) -> ConditionCode { match value { 0b0000 => ConditionCode::EQ, 0b0001 => ConditionCode::NE, 0b0010 => ConditionCode::HS, 0b0011 => ConditionCode::LO, 0b0100 => ConditionCode::MI, 0b0101 => ConditionCode::PL, 0b0110 => ConditionCode::VS, 0b0111 => ConditionCode::VC, 0b1000 => ConditionCode::HI, 0b1001 => ConditionCode::LS, 0b1010 => ConditionCode::GE, 0b1011 => ConditionCode::LT, 0b1100 => ConditionCode::GT, 0b1101 => ConditionCode::LE, 0b1110 => ConditionCode::AL, _ => { // this means the argument `value` must never be outside [0,15] // which itself means this function shouldn't be public unreachable!(); } } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] enum DecodeMode { User, FIQ, IRQ, Supervisor, Monitor, Abort, Hyp, Undefined, System, /// Catch-all mode to try decoding all ARM instructions. Some instructions are `UNDEFINED` or /// `UNPREDICTABLE` in some modes, but `Any` will attempt to decode all. Any, } impl Default for DecodeMode { fn default() -> Self { DecodeMode::Any } } impl DecodeMode { fn is_user(&self) -> bool { match self { DecodeMode::Any | DecodeMode::User => true, _ => false } } #[allow(dead_code)] fn is_supervisor(&self) -> bool { match self { DecodeMode::Any | DecodeMode::Supervisor => true, _ => false } } fn is_hyp(&self) -> bool { match self { DecodeMode::Any | DecodeMode::Hyp => true, _ => false } } fn is_system(&self) -> bool { match self { DecodeMode::Any | DecodeMode::System => true, _ => false } } fn is_any(&self) -> bool { match self { DecodeMode::Any => true, _ => false, } } } #[derive(Debug, PartialEq, Eq)] #[allow(non_camel_case_types)] enum ARMVersion { v4, v5, v6, v6t2, v7, v7ve, v7vese, Any, } impl Default for ARMVersion { fn default() -> Self { ARMVersion::Any } } // nothing checks/rejects by arm version yet, but.. soon.... /// a struct with decode configuration for `ARMv7` and below. the same decoder is used for `thumb` /// and non-`thumb` modes, and the same instruction struct is used for decoded instructions in /// either mode. /// /// NOTE: helper functions here create `InstDecoder` for specific revisions, extensions, or lack /// thereof, in the supported instruction set. `yaxpeax-arm` does not actually honor these settings /// yet. this means any `InstDecoder` will decode all known instructions through the latest `ARMv7` /// extensions. #[allow(unused)] #[derive(Debug)] pub struct InstDecoder { mode: DecodeMode, version: ARMVersion, should_is_must: bool, thumb: bool, } impl Default for InstDecoder { fn default() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::Any, should_is_must: true, thumb: false, } } } impl InstDecoder { /// set the decoder to decoding in thumb mode as the specified bool provides; `true` means /// "yes, decode in `thumb` mode", where `false` means to decode as a normal `arm` instruction. pub fn set_thumb_mode(&mut self, thumb: bool) { self.thumb = thumb; } /// set the decoder to decoding in thumb mode as the specified bool provides; `true` means /// "yes, decode in `thumb` mode", where `false` means to decode as a normal `arm` instruction. /// /// (this consumes and returns the `InstDecoder` to support use in chained calls.)` pub fn with_thumb_mode(mut self, thumb: bool) -> Self { self.set_thumb_mode(thumb); self } /// initialize a new `arm` `InstDecoder` with default ("everything") support, but in `thumb` /// mode. pub fn default_thumb() -> Self { Self::default().with_thumb_mode(true) } /// create an `InstDecoder` that supports only instructions through to `ARMv4`. pub fn armv4() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v4, should_is_must: true, thumb: false, } } /// create an `InstDecoder` that supports only instructions through to `ARMv5`. pub fn armv5() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v5, should_is_must: true, thumb: false, } } /// create an `InstDecoder` that supports only instructions through to `ARMv6`. pub fn armv6() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v6, should_is_must: true, thumb: false, } } /// create an `InstDecoder` that supports only instructions through to `ARMv6t2`. pub fn armv6t2() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v6t2, should_is_must: true, thumb: false, } } /// create an `InstDecoder` that supports only instructions through to `ARMv6t2` in thumb mode. pub fn armv6t2_thumb() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v6t2, should_is_must: true, thumb: true, } } /// create an `InstDecoder` that supports only instructions through to `ARMv7`. pub fn armv7() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7, should_is_must: true, thumb: false, } } /// create an `InstDecoder` that supports only instructions through to `ARMv7` in thumb mode. pub fn armv7_thumb() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7, should_is_must: true, thumb: true, } } /// create an `InstDecoder` that supports only instructions through to `ARMv7ve`. pub fn armv7ve() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7ve, should_is_must: true, thumb: false, } } /// create an `InstDecoder` that supports only instructions through to `ARMv7ve` in thumb mode. pub fn armv7ve_thumb() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7ve, should_is_must: true, thumb: true, } } /// create an `InstDecoder` that supports only instructions through to `ARMv7vese`. pub fn armv7vese() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7vese, should_is_must: true, thumb: false, } } fn unpredictable(&self) -> Result<(), DecodeError> { if self.mode != DecodeMode::Any { Err(DecodeError::Unpredictable) } else { Ok(()) } } } #[allow(non_snake_case)] impl Decoder for InstDecoder { fn decode_into::Address, ::Word>>(&self, inst: &mut Instruction, words: &mut T) -> Result<(), ::DecodeError> { inst.set_w(false); inst.set_wide(false); if self.thumb { return thumb::decode_into(&self, inst, words); } else { inst.set_thumb(false); } let mut word_bytes = [0u8; 4]; words.next_n(&mut word_bytes)?; let word = u32::from_le_bytes(word_bytes); let (cond, opc_upper) = { let top_byte = word >> 24; ( ((top_byte >> 4) & 0xf) as u8, ((top_byte >> 1) & 0x7) as u8 ) }; if cond == 0b1111 { // unconditional instructions, section A5.7/page A5-214 inst.condition = ConditionCode::AL; let op1 = (word >> 20) as u8; if op1 >= 0b1000_0000 { match (op1 >> 5) & 0b11 { 0b00 => { match op1 & 0b101 { 0b000 | 0b101 => { return Err(DecodeError::InvalidOpcode); } 0b100 => { // SRS (see table A5.7, op1 = 0b100xx1x0, page A5-214) if !self.mode.is_any() && self.mode.is_hyp() { return Err(DecodeError::Undefined); } if self.should_is_must { if word & 0x000fffe0 != 0x000d0500 { return Err(DecodeError::Nonconforming); } } let puxw = (word >> 21) & 0b1111; let P = puxw & 0b1000 != 0; let U = puxw & 0b0100 != 0; let W = puxw & 0b0001 != 0; inst.opcode = Opcode::SRS(P, U); inst.operands = [ Operand::RegWBack(Reg::from_u8(13), W), Operand::Imm32(word & 0b1111), Operand::Nothing, Operand::Nothing, ]; }, 0b001 => { // RFE (see table A5.7, op1 = 0b100xx0x1, page A5-214) if !self.mode.is_any() && self.mode.is_hyp() { return Err(DecodeError::Undefined); } if self.should_is_must { if word & 0xffff != 0x0a00 { return Err(DecodeError::Nonconforming); } } let puxw = (word >> 21) & 0b1111; let P = puxw & 0b1000 != 0; let U = puxw & 0b0100 != 0; let W = puxw & 0b0001 != 0; inst.opcode = Opcode::RFE(P, U); inst.operands = [ Operand::RegWBack(Reg::from_u8((word >> 16) as u8 & 0b1111), W), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!("op1 mask is 0b101 but somehow we got an invalid pattern"); } } } 0b01 => { inst.opcode = Opcode::BLX; let operand = ((word & 0xffffff) as i32) << 8 >> 7; inst.operands = [ Operand::BranchThumbOffset( operand | ( ((word >> 24) & 0b1) as i32 ) ), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b10 => { // op1=0b110xxxxx, see table A5-23 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 coproc = (word >> 8) as u8 & 0b1111; if coproc & 0b1110 == 0b1010 { // TODO: `UNDEFINED` return Err(DecodeError::InvalidOpcode); } 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::MRRC2(coproc, opc1); } else { inst.opcode = Opcode::MCRR2(coproc, opc1); } 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 coproc = (word >> 8) as u8 & 0b1111; let imm8 = word & 0b11111111; if coproc & 0b1110 == 0b1010 { return Err(DecodeError::InvalidOpcode); } if (word >> 20) & 0b00001 == 0 { // op=110xxxx0, STC // page A8-663 if pudw & 0b0010 != 0 { inst.opcode = Opcode::STC2L(coproc); } else { inst.opcode = Opcode::STC2(coproc); } } else { // op=110xxxx1, LDC // page A8-393 if pudw & 0b0010 != 0 { inst.opcode = Opcode::LDC2L(coproc); } else { inst.opcode = Opcode::LDC2(coproc); } } 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 { // preindex has no wback Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), (imm8 << 2) as u16, U, false) } else { Operand::RegDeref(Reg::from_u8(Rn)) } }, if !P && !W { // TODO: not sure what ldc2{l}'s