summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock35
-rw-r--r--Cargo.toml14
-rw-r--r--src/lib.rs1958
4 files changed, 2008 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..cb493e1
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,35 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "num-traits"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "yaxpeax-arch"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1ba5c2f163fa2f866c36750c6c931566c6d93231ae9410083b0738953b609d5"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "yaxpeax-rx"
+version = "0.0.1"
+dependencies = [
+ "num-traits",
+ "yaxpeax-arch",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3d6fd56
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+
+name = "yaxpeax-rx"
+version = "0.0.1"
+authors = [ "iximeow <me@iximeow.net>" ]
+license = "0BSD"
+repository = "http://git.iximeow.net/yaxpeax-rx/"
+description = "Renesas RX decoders for the yaxpeax project"
+readme = "README.md"
+edition = "2021"
+
+[dependencies]
+yaxpeax-arch = { version = "0.2.7", default-features = false, features = [] }
+"num-traits" = { version = "0.2", default-features = false }
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..f7a1ef5
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,1958 @@
+use core::fmt;
+use core::cmp;
+
+use yaxpeax_arch::{AddressDiff, Arch, Decoder, LengthedInstruction, Reader, StandardDecodeError};
+
+#[derive(Debug)]
+pub struct RX;
+
+impl Arch for RX {
+ type Address = u32;
+ type Word = u8;
+ type Instruction = Instruction;
+ type DecodeError = StandardDecodeError;
+ type Decoder = InstDecoder;
+ type Operand = Operand;
+}
+
+pub struct Instruction {
+ opcode: Opcode,
+ operands: [Operand; 3],
+ length: u8,
+}
+
+impl Instruction {
+ pub fn opcode(&self) -> Opcode {
+ self.opcode
+ }
+
+ pub fn operands(&self) -> &[Operand] {
+ &self.operands[self.operand_count()]
+ }
+
+ pub fn length(&self) -> u8 {
+ self.length
+ }
+}
+
+impl Default for Instruction {
+ fn default() -> Instruction {
+ Instruction {
+ opcode: Opcode::NOP,
+ operands: [Operand::Nothing, Operand::Nothing, Operand::Nothing],
+ length: 1,
+ }
+ }
+}
+
+impl fmt::Display for Instruction {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.opcode())?;
+ let ops = self.operands();
+ if ops[0] != Operand::Nothing {
+ write!(f, " {}", ops[0])?;
+ }
+ if ops[1] != Operand::Nothing {
+ write!(f, ", {}", ops[1])?;
+ }
+ if ops[2] != Operand::Nothing {
+ write!(f, ", {}", ops[2])?;
+ }
+ Ok(())
+ }
+}
+
+impl LengthedInstruction for Instruction {
+ type Unit = AddressDiff<<RX as Arch>::Address>;
+ fn min_size() -> Self::Unit {
+ AddressDiff::from_const(1)
+ }
+ fn len(&self) -> Self::Unit {
+ AddressDiff::from_const(self.length as u32)
+ }
+}
+
+impl yaxpeax_arch::Instruction for Instruction {
+ // only know how to decode well-formed instructions at the moment
+ fn well_defined(&self) -> bool { true }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum SizeSpec {
+ B, W, L,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum PSWBit {
+ C,
+ Z,
+ S,
+ O,
+ I,
+ U,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ControlReg {
+ PSW,
+ USP,
+ FPSW,
+ BPSW,
+ BPC,
+ ISP,
+ FINTV,
+ INTB,
+ EXTB
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Operand {
+ /// one of the 16 32-bit general purpose registers: `R0 (sp)` through `R15`.
+ Register { num: u8 },
+ /// one of the 16 32-bit general purpose registers, but a smaller part of it. typically
+ /// sign-extended to 32b for processing.
+ Subreg { num: u8, width: SizeCode },
+ /// one of the 16 64-bit double-precision floating point registers: `DR0` through `DR15`.
+ DoubleReg { num: u8 },
+ DoubleRegLow { num: u8 },
+ DoubleRegHigh { num: u8 },
+ ControlReg { reg: ControlReg },
+ Deref { gpr: u8, disp: u32, width: SizeCode },
+ DerefIndexed { base: u8, index: u8, width: SizeCode },
+ DoubleRegisterRange { start_reg: u8, end_reg: u8 },
+ DoubleControlRegisterRange { start_reg: u8, end_reg: u8 },
+ ImmS(u8),
+ ImmB { imm: u8 },
+ ImmL { imm: u32 },
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum SizeCode {
+ B,
+ W,
+ L,
+ D,
+ UW,
+}
+
+impl SizeCode {
+ fn bytes(&self) -> u8 {
+ match self {
+ SizeCode::B => 1,
+ SizeCode::W => 2,
+ SizeCode::UW => 2,
+ SizeCode::L => 4,
+ SizeCode::D => 8,
+ }
+ }
+}
+
+impl fmt::Display for Operand {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Operand::GPR { num } => {
+ write!(f, "r{}", num)
+ },
+ Operand::ControlRg { reg } => {
+ fmt::Display::fmt(reg, f)
+ },
+ Operand::Deref { gpr, disp, .. } => {
+ if disp == 0 {
+ write!(f, "[r{}]", gpr)
+ } else {
+ write!(f, "{}[r{}]", disp, gpr)
+ }
+ },
+ Operand::RegisterRange { start_gpr, end_gpr } => {
+ write!(f, "r{}-r{}", start_gpr, end_gpr)
+ }
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Opcode {
+ BRA,
+ SUNTIL_B,
+ SUNTIL_W,
+ SUNTIL_L,
+ NOP,
+ FSUB,
+ FCMP,
+ FADD,
+ FMUL,
+ MOV,
+ DMOV,
+}
+
+impl fmt::Display for Opcode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[repr(u8)]
+pub enum RxVersion {
+ V1,
+ V2,
+ V3,
+}
+
+impl PartialOrd for RxVersion {
+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+ (self as u8).partial_cmp(other)
+ }
+}
+
+impl Ord for RxVersion {}
+
+#[test]
+fn versions_compare_right() {
+ assert!(RxVersion::V1 < RxVersion::V2);
+ assert!(RxVersion::V2 < RxVersion::V3);
+ assert!(RxVersion::V1 < RxVersion::V3);
+}
+
+#[derive(Debug)]
+pub struct InstDecoder {
+ version: RxVersion,
+}
+
+impl Default for InstDecoder {
+ fn default() -> Self {
+ InstDecoder { version: RxVersion::V3 }
+ }
+}
+
+impl InstDecoder {
+ fn num_to_psw_bit(&self, reg: u8) -> Result<Operand, <RX as Arch>::DecodeError> {
+ match reg {
+ 0b0000 => Ok(Operand::PSWBit { bit: PSWBit::C }),
+ 0b0001 => Ok(Operand::PSWBit { bit: PSWBit::Z }),
+ 0b0010 => Ok(Operand::PSWBit { bit: PSWBit::S }),
+ 0b0011 => Ok(Operand::PSWBit { bit: PSWBit::O }),
+ 0b1000 => Ok(Operand::PSWBit { bit: PSWBit::I }),
+ 0b1001 => Ok(Operand::PSWBit { bit: PSWBit::U }),
+ _ => Err(StandardDecodeError::InvalidOperand),
+ }
+ }
+
+ fn reg_to_control_reg(&self, reg: u8) -> Result<Operand, <RX as Arch>::DecodeError> {
+ match reg {
+ 0b0000 => { Ok(Operand::ControlReg { reg: ControlReg::PSW }) },
+ 0b0001 => { Ok(Operand::ControlReg { reg: ControlReg::PC }) },
+ 0b0010 => { Ok(Operand::ControlReg { reg: ControlReg::USP }) },
+ 0b0011 => { Ok(Operand::ControlReg { reg: ControlReg::FPSW}) },
+ 0b1000 => { Ok(Operand::ControlReg { reg: ControlReg::BPSW }) },
+ 0b1001 => { Ok(Operand::ControlReg { reg: ControlReg::BPC }) },
+ 0b1010 => { Ok(Operand::ControlReg { reg: ControlReg::ISP }) },
+ 0b1011 => { Ok(Operand::ControlReg { reg: ControlReg::FINTV }) },
+ 0b1100 => { Ok(Operand::ControlReg { reg: ControlReg::INTB }) },
+ 0b1101 => {
+ if self.version >= RxVersion::V3 {
+ Ok(Operand::ControlReg { reg: ControlReg::EXTB })
+ } else {
+ Err(StandardDecodeError::InvalidOperand)
+ }
+ },
+ _ => {
+ Err(StandardDecodeError::InvalidOperand)
+ }
+ }
+ }
+
+ fn reg_to_double_control_reg(&self, reg: u8) -> Result<Operand, <RX as Arch>::DecodeError> {
+ match reg {
+ 0b0000 => { Ok(Operand::ControlReg { reg: ControlReg::DPSW }) },
+ 0b0001 => { Ok(Operand::ControlReg { reg: ControlReg::DCMR }) },
+ 0b0010 => { Ok(Operand::ControlReg { reg: ControlReg::DECNT }) },
+ 0b0011 => { Ok(Operand::ControlReg { reg: ControlReg::DEPC }) },
+ _ => {
+ Err(StandardDecodeError::InvalidOperand)
+ }
+ }
+ }
+}
+
+trait DecodeHandler<T: Reader<<RX as Arch>::Address, <RX as Arch>::Word>> {
+ #[inline(always)]
+ fn read_u8(&mut self, words: &mut T) -> Result<u8, <RX as Arch>::DecodeError> {
+ let b = words.next()?;
+ self.on_word_read(b);
+ Ok(b)
+ }
+ #[inline(always)]
+ fn read_u16(&mut self, words: &mut T) -> Result<u16, <RX as Arch>::DecodeError> {
+ let mut buf = [0u8; 2];
+ words.next_n(&mut buf).ok().ok_or(StandardDecodeError::ExhaustedInput)?;
+ self.on_word_read(buf[0]);
+ self.on_word_read(buf[1]);
+ Ok(u16::from_le_bytes(buf));
+ }
+ #[inline(always)]
+ fn read_u24(&mut self, words: &mut T) -> Result<u32, <RX as Arch>::DecodeError> {
+ let mut buf = [0u8; 4];
+ // read into a four-byte buffer, filling the low 3 bytes so we can turn this whole thing
+ // into a u32 in-place
+ words.next_n(&mut buf[1..4]).ok().ok_or(StandardDecodeError::ExhaustedInput)?;
+ self.on_word_read(buf[0]);
+ self.on_word_read(buf[1]);
+ self.on_word_read(buf[2]);
+ Ok(u32::from_le_bytes(buf));
+ }
+ #[inline(always)]
+ fn read_u32(&mut self, words: &mut T) -> Result<u32, <RX as Arch>::DecodeError> {
+ let mut buf = [0u8; 4];
+ words.next_n(&mut buf).ok().ok_or(StandardDecodeError::ExhaustedInput)?;
+ self.on_word_read(buf[0]);
+ self.on_word_read(buf[1]);
+ self.on_word_read(buf[2]);
+ self.on_word_read(buf[3]);
+ Ok(u32::from_le_bytes(buf));
+ }
+ /// helper to decode the bits for an `ld`-style operand, with variants like "deref", "deref
+ /// with displacement", and "deref with larger displacement", into an `Operand`.
+ ///
+ /// it is not clear how `size` is handled, if at all, for instructions where `ld` would
+ /// indicate a register (non-deref). the manual does not indicate that sign or zero extension
+ /// of source registers is possible for register-register operand combinations, so the likely
+ /// cases are that either size!=L would trap, or is ignored and means full register width
+ /// regardless.
+ fn decode_mem_op(&mut self, rs: u8, ld: u8, size: SizeCode, words: &mut T) -> Result<Operand, <RX as Arch>::DecodeError> {
+ match ld {
+ 0b00 => {
+ Operand::Deref { gpr: rs, disp: 0, size }
+ },
+ 0b01 => {
+ let disp = self.read_u8(words)? as u32;
+ Operand::Deref { gpr: rs, disp, size }
+ }
+ 0b10 => {
+ let disp = self.read_u16(words)? as u32;
+ Operand::Deref { gpr: rs, disp, size }
+ }
+ _ => {
+ // callers (should all be internal) should never pass larger `ld`..
+ // it's not clear how ``
+ debug_assert!(ld == 0b11);
+ Operand::GPR { num: rs }
+ }
+ }
+ }
+ fn on_decode_start(&mut self) {}
+ fn on_decode_end(&mut self) {}
+ fn on_opcode_decoded(&mut self, _opcode: Opcode) -> Result<(), <RX as Arch>::DecodeError> { Ok(()) }
+ fn on_operand_decoded(&mut self, _number: u8, _operand: Operand) -> Result<(), <RX as Arch>::DecodeError> { Ok(()) }
+ fn on_word_read(&mut self, _word: <RX as Arch>::Word) {}
+}
+
+impl<T: yaxpeax_arch::Reader<<RX as Arch>::Address, <RX as Arch>::Word>> DecodeHandler<T> for Instruction {
+ fn on_decode_start(&mut self) {
+ self.length = 0;
+ self.opcode = Opcode::NOP;
+ self.operands = [Operand::Nothing, Operand::Nothing, Operand::Nothing];
+ }
+ fn on_opcode_decoded(&mut self, opcode: Opcode) -> Result<(), <RX as Arch>::DecodeError> {
+ self.opcode = opcode;
+ Ok(())
+ }
+ fn on_operand_decoded(&mut self, number: u8, operand: Operand) -> Result<(), <RX as Arch>::DecodeError> {
+ self.operands[number as usize] = operand;
+ Ok(())
+ }
+ fn on_word_read(&mut self, _word: <RX as Arch>::Word) {
+ self.length += 1;
+ }
+}
+
+impl Decoder<RX> for InstDecoder {
+ fn decode_into<T: Reader<<RX as Arch>::Address, <RX as Arch>::Word>>(&self, inst: &mut Instruction, words: &mut T) -> Result<(), <RX as Arch>::DecodeError> {
+ }
+}
+
+fn decode_inst<
+ T: Reader<<RX as Arch>::Address, <RX as Arch>::Word>,
+ H: DecodeHandler<T>,
+>(decoder: &<RX as Arch>::Decoder, handler: &mut H, words: &mut T) -> Result<(), <RX as Arch>::DecodeError> {
+ handler.on_decode_start();
+
+ let b: u8 = handler.read_u8(words)?;
+
+ if b == 0b0000_0000 {
+ handler.on_opcode_decoded(Opcode::BRK)?;
+ } else if b == 0b0000_0001 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if b == 0b0000_0010 {
+ handler.on_opcode_decoded(Opcode::RTS)?;
+ } else if b == 0b0000_0011 {
+ handler.on_opcode_decoded(Opcode::NOP)?;
+ } else if b == 0b0000_0100 {
+ handler.on_opcode_decoded(Opcode::BRA)?;
+ handler.on_operand_decoded(0, Operand::ImmA(handler.read_u24(words)?))?;
+ } else if b == 0b0000_0101 {
+ handler.on_opcode_decoded(Opcode::BSR)?;
+ handler.on_operand_decoded(0, Operand::ImmA(handler.read_u24(words)?))?;
+ } else if b == 0b0000_0110 {
+ let next: u8 = handler.read_u8(words)?;
+ let mi = (next >> 6) & 0b11;
+ let ld = next & 0b11;
+ let opc = (next >> 2) & 0b1111;
+
+ if opc < 0b0110 {
+ const OPC_TABLE: [Opcode; 6] = [Opcode::SUB, Opcode::CMP, Opcode::ADD, Opcode::MUL, Opcode::AND, Opcode::OR];
+
+ handler.on_opcode_decoded(OPC_TABLE[opc])?;
+ } else if opc == 0b1000 {
+ handler.on_opcode_decoded(match next {
+ 0b0_0000 => {
+ if mi == 0b10 && ld != 0b11 {
+ Opcode::SBB
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ }
+ 0b0_0001 => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ 0b0_0010 => {
+ if mi == 0b10 && ld != 0b11 {
+ Opcode::ADC
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ }
+ 0b0_0011 => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ 0b0_0100 => { Opcode::MAX },
+ 0b0_0101 => { Opcode::MIN },
+ 0b0_0110 => { Opcode::EMUL },
+ 0b0_0111 => { Opcode::EMULU },
+ 0b0_1000 => { Opcode::DIV },
+ 0b0_1001 => { Opcode::DIVU },
+ 0b0_1010 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b0_1011 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b0_1100 => { Opcode::TST },
+ 0b0_1101 => { Opcode::XOR },
+ 0b0_1110 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b0_1111 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_0000 => { Opcode::XCHG },
+ 0b1_0001 => { Opcode::ITOF },
+ 0b1_0010 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_0011 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_0100 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_0101 => if decoder.version >= RxVersion::V2 {
+ Opcode::UTOF
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ },
+ 0b1_0110 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_0111 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1000 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1001 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1010 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1011 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1100 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1101 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1110 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b1_1111 => { return Err(StandardDecodeError::InvalidOpcode); },
+ // any of the upper three bits being set here are invalid
+ _ => { return Err(StandardDecodeError::InvalidOpcode); },
+ })?;
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ // read operands
+ let next: u8 = handler.read_u8(words)?;
+
+ let rs = (next >> 4) & 0b1111;
+ let rd = next & 0b1111;
+
+ let size = [SizeCode::B, SizeCode::W, SizeCode::L, SizeCode::UW][mi as usize];
+ let src = handler.decode_mem_op(rs, ld, size, words);
+
+ handler.on_operand_decoded(0, src)?;
+ handler.on_operand_decoded(1, Operand::GPR { num: rd })?;
+ } else if opc == 0b0000_0111 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if opc < 0b0001_0000 {
+ handler.on_opcode_decoded(Opcode::BRA);
+ let disp = opc & 0b111;
+ // TODO: double-check the displacement offset thingy
+ handler.on_operand_decoded(0, Operand::ImmS(disp))?;
+ } else if opc < 0b0010_0000 {
+ handler.on_opcode_decoded(if opc & 0b0000_1000 == 0 {
+ Opcode::BEQ
+ } else {
+ Opcode::BNE
+ });
+ let disp = opc & 0b111;
+ // TODO: double-check the displacement offset thingy
+ handler.on_operand_decoded(0, Operand::ImmS(disp))?;
+ } else if opc < 0b0011_0000 {
+ // BCnd.B
+ let cond = opc & 0b1111;
+ const OPC_TABLE: &'static [Opcode] = [
+ Opcode::BEQ, Opcode::BNE, Opcode::BGEU, Opcode::BLTU,
+ Opcode::BGTU, Opcode::BLEU, Opcode::BPZ, Opcode::BN,
+ Opcode::BGE, Opcode::BLT, Opcode::BGT, Opcode::BLE,
+ Opcode::BO, Opcode::BNO, Opcode::BRA, /* no branch for cnd=1111 */
+ ];
+
+ if let Some(op) = OPC_TABLE.get(cond) {
+ handler.on_opcode_decoded(op);
+ handler.on_operand_decoded(0, Operand::ImmB { imm: handler.read_u8(words)? })?;
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ } else if opc < 0b0011_1000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if opc == 0b0011_1000 {
+ handler.on_opcode_decoded(Opcode::BRA)?;
+ handler.on_operand_decoded(0, Operand::ImmW(handler.read_u16(words)?))?;
+ } else if opc == 0b0011_1001 {
+ handler.on_opcode_decoded(Opcode::BSR)?;
+ handler.on_operand_decoded(0, Operand::ImmW(handler.read_u16(words)?))?;
+ } else if opc == 0b0011_1010 {
+ handler.on_opcode_decoded(Opcode::BEQ)?;
+ handler.on_operand_decoded(0, Operand::ImmW(handler.read_u16(words)?))?;
+ } else if opc == 0b0011_1011 {
+ handler.on_opcode_decoded(Opcode::BNE)?;
+ handler.on_operand_decoded(0, Operand::ImmW(handler.read_u16(words)?))?;
+ } else if opc < 0b0011_1111 {
+ // MOV.size
+ handler.on_opcode_decoded(Opcode::MOV)?;
+ let operands = handler.read_u8(words)?;
+ let rd = (operands >> 4) & 0b111;
+ let disp = (((operands >> 3) & 0b10000) | (operands & 0b1111)) as u8 as u32;
+ let imm = handler.read_u8(words)?;
+ match opc & 0b11 {
+ 0b00 => {
+ handler.on_operand_decoded(0, Operand::ImmB { imm: imm as u8 })?;
+ handler.on_operand_decoded(1, Operand::Deref { gpr: rd, disp, size: SizeCode::B })?;
+ }
+ 0b01 => {
+ handler.on_operand_decoded(0, Operand::ImmW { imm: imm as u8 as u16 })?;
+ handler.on_operand_decoded(1, Operand::Deref { gpr: rd, disp, size: SizeCode::W })?;
+ }
+ 0b10 => {
+ handler.on_operand_decoded(0, Operand::ImmL { imm: imm as u8 as u32 })?;
+ handler.on_operand_decoded(1, Operand::Deref { gpr: rd, disp, size: SizeCode::L })?;
+ }
+ _ => { unreachable!("sz=11 is rtsd") }
+ };
+ } else if opc == 0b0011_1111 {
+ handler.on_opcode_decoded(Opcode::RTSD)?;
+
+ let operands = handler.read_u8(words)?;
+ let imm = handler.read_u8(words)?;
+ let rd = operands >> 4;
+ let rd2 = operands & 0b1111;
+ handler.on_operand_decoded(0, Operand::RegisterRange { start_gpr: rd, end_gpr: rd2 })?;
+ } else if opc < 0b0110_0000 {
+ let code = (opc >> 2) & 0b111;
+ let opcode = [
+ Opcode::SUB, Opcode::CMP, Opcode::ADD, Opcode::MUL,
+ Opcode::AND, Opcode::OR, Opcode::MOVU, Opcode::MOVU,
+ ][code as usize];
+ handler.on_opcode_decoded(opcode)?;
+
+ let ld = opc & 0b11;
+ let operands = handler.read_u8(words)?;
+ let rs = operands >> 4;
+ let rd = operands & 0b1111;
+
+ let size = [
+ SizeSpec::L, SizeSpec::L, SizeSpec::L, SizeSpec::L,
+ SizeSpec::L, SizeSpec::L, SizeSpec::B, SizeSpec::W,
+ ][code as usize];
+ let src = handler.decode_mem_op(rs, ld, size, words)?;
+ handler.on_operand_decoded(0, src)?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ } else if opc < 0b0111_0000 {
+ // 0 1 1 0 ....
+ let opc = opc & 0b1111;
+ if opc < 0b1000 {
+ // 0 1 1 0 0 ...
+ // either sub/cmp/add/.. or rtsd
+ let operands = handler.read_u8(words)?;
+ if opc == 0b0111 {
+ handler.on_opcode_decoded(Opcode::RTSD)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm: operands })?;
+ } else {
+ let imm = operands >> 4;
+ let rd = operands & 0b1111;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ }
+ } else if opc < 0b1110 {
+ // 0 1 1 0 1 ...
+ handler.on_opcode_decoded(match opc >> 1 {
+ 0b100 => Opcode::SHLR,
+ 0b101 => Opcode::SHAR,
+ 0b110 => Opcode::SHLL,
+ _ => { unreachable!("implies opc=0b1111") }
+ })?;
+ let operands = handler.read_u8(words)?;
+ let imm = operands >> 3;
+ let rd = operands & 0b111;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ } else {
+ // 0 1 1 0 1 1 1 x
+ handler.on_opcode_decoded(if opc == 0b1110 {
+ Opcode::PUSHM
+ } else {
+ Opcode::POPM
+ })?;
+ let operands = handler.read_u8(words)?;
+ let reg_lo = operands >> 4;
+ let reg_hi = operands & 0b1111;
+ handler.on_operand_decoded(0, Operand::RegisterRange { start_gpr: reg_lo, end_gpr: reg_hi })?;
+ }
+ } else if opc < 0b0111_0100 {
+ // 0 1 1 1 0 0 li
+ handler.on_opcode_decoded(Opcode::ADD)?;
+ let operands = handler.read_u8(words)?;
+ let rs2 = operands >> 4;
+ let rd = operands & 0b1111;
+ let li = opc & 0b11;
+ let imm = match li {
+ 0b00 => {
+ handler.read_u32(words)?
+ }
+ 0b01 => {
+ handler.read_u8(words)? as i8 as i32 as u32
+ }
+ 0b10 => {
+ handler.read_u16(words)? as i16 as i32 as u32
+ }
+ _ => {
+ debug_assert!(li == 0b11, "li is at most the low two bits set");
+ (((handler.read_u24(words)? as i32) << 8) >> 8) as u32
+ }
+ };
+ handler.on_operand_decoded(0, Operand::ImmL { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rs2 })?;
+ handler.on_operand_decoded(2, Operand::Register { gpr: rd })?;
+ } else if opc < 0b0111_1000 {
+ // 0 1 1 1 0 1 li
+ let li = opc & 0b11;
+ let operands = handler.read_u8(words)?;
+ let opc = operands >> 4;
+ let opcode = match opc {
+ 0b0000 => Some(Opcode::CMP),
+ 0b0001 => Some(Opcode::MUL),
+ 0b0010 => Some(Opcode::AND),
+ 0b0011 => Some(Opcode::OR),
+ 0b0100 => {
+ if li != 0b01 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ Some(Opcode::MOV)
+ }
+ 0b0101 => {
+ if li != 0b01 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ Some(Opcode::CMP)
+ }
+ _ => {
+ // might still be a valid instruction, but we don't know the opcode for sure yet.
+ // or we might be able to discern the opcode, but operands need extra checking
+ // (like `int`).
+ None
+ }
+ };
+ let rd = operands & 0b1111;
+ let imm = match li {
+ 0b00 => {
+ handler.read_u32(words)?
+ }
+ 0b01 => {
+ handler.read_u8(words)? as i8 as i32 as u32
+ }
+ 0b10 => {
+ handler.read_u16(words)? as i16 as i32
+ }
+ _ => {
+ debug_assert!(li == 0b11, "li is at most the low two bits set");
+ ((handler.read_u24(words)? as i32) << 8) >> 8
+ }
+ };
+ if let Some(op) = opcode {
+ handler.on_opcode_decoded(op)?;
+ handler.on_operand_decoded(0, Operand::ImmL { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ } else {
+ if opc == 0b0110 {
+ // might be `int`, depends on rd
+ if rd == 0 {
+ handler.on_opcode_decoded(Opcode::INT)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm: imm as u8 })?;
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ } else if op == 0b0111 {
+ // might be `mvitpl`
+ if rd == 0 && (imm & 0b1111_0000 == 0) {
+ handler.on_opcode_decoded(Opcode::MVTIPL)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm: imm as u8 & 0b1111 })?;
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ } else if op == 0b1001 {
+ // might be `mvfdr`, or other new double-precision instructions
+ if decoder.version < RxVersion::V3 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ if rd != 0 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ if li == 0b01 {
+ if imm != 0b0001_1011 {
+ handler.on_opcode_decoded(Opcode::MVFDR)?;
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ } else if li == 0b10 {
+ let rs = imm as u8;
+ let rd = (imm >> 4) as u8;
+ let opc = (imm >> 8) as u8;
+ let rs2 = (imm >> 12) as u8;
+
+ let opcode = match opc {
+ 0b0000 => Opcode::DADD,
+ 0b0001 => Opcode::DSUB,
+ 0b0010 => Opcode::DMUL,
+ /*0011 is invalid*/
+ 0b0100 => Opcode::DDIV,
+ /*0101 and 0110 are invalid*/
+ 0b0111 => {
+ // unlike others, dcmp has variants selected by `rd`
+ let opc = match rd {
+ 0b0001 => Opcode::DCMPUN,
+ 0b0010 => Opcode::DCMPEQ,
+ 0b0100 => Opcode::DCMPLT,
+ 0b0110 => Opcode::DCMPLE,
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+ handler.on_opcode_decoded(opc)?;
+ handler.on_operand_decoded(0, Operand::DoubleReg { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rs2 })?;
+ }
+ 0b1100 => {
+ // dmovd or a few others, depending on "rs"
+ let opc = match rs {
+ 0b0000 => Opcode::DMOV,
+ 0b0001 => Opcode::DABS,
+ 0b0010 => Opcode::DNEG,
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+ handler.on_opcode_decoded(opc)?;
+ handler.on_operand_decoded(0, Operand::DoubleReg { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ }
+ 0b1101 => {
+ // dsqrt or others, depending on "rs"
+ let opc = match rs {
+ 0b0000 => Opcode::DSQRT,
+ 0b1000 => Opcode::DTOI,
+ 0b1001 => Opcode::DTOU,
+ 0b1100 => Opcode::DTOF,
+ 0b1101 => Opcode::DROUND,
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+ handler.on_opcode_decoded(opc)?;
+ handler.on_operand_decoded(0, Operand::DoubleReg { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ }
+ _ => {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ };
+ handler.on_opcode_decoded(opc)?;
+ handler.on_operand_decoded(0, Operand::DoubleReg { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rs2 })?;
+ handler.on_operand_decoded(2, Operand::DoubleReg { num: rd })?;
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ } else if op == 0b1010 {
+ // dpushm.l/dpopm.l
+ let opc = if rd == 0b000 {
+ Opcode::DPUSHM
+ } else if rd == 0b1000 {
+ Opcode::DPOP
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ };
+ handler.on_opcode_decoded(opc)?;
+ let rs = (imm as u8 >> 4) & 0b1111;
+ let nm = imm as u8 & 0b1111;
+ let end_reg = rs + nm + 1;
+ if end_reg > 15 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_operand_decoded(0, Operand::DoubleControlRegisterRange { start_reg: rs, end_reg })?;
+ } else if op == 0b1011 {
+ // dpushm.d/dpopm.d
+ let opc = if rd == 0b000 {
+ Opcode::DPUSHM
+ } else if rd == 0b1000 {
+ Opcode::DPOP
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ };
+ handler.on_opcode_decoded(opc)?;
+ let rs = (imm as u8 >> 4) & 0b1111;
+ let nm = imm as u8 & 0b1111;
+ let end_reg = rs + nm + 1;
+ if end_reg > 15 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_operand_decoded(0, Operand::DoubleRegisterRange { start_reg: rs, end_reg })?;
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ }
+ } else if opc < 0b0111_1110 {
+ // 0 1 1 1 1 xx _
+ // BSET, BCLR, BTST
+ let operands = handler.read_u8(words)?;
+ let imm = ((opc & 1) << 4) | (operands >> 4);
+ let rd = operands & 0b1111;
+ let opc = (match operands >> 1) & 0b11 {
+ 0b00 => Opcode::BSET,
+ 0b01 => Opcode::BCLR,
+ 0b10 => Opcode::BTST,
+ };
+ handler.on_opcode_decoded(opc)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opc < 0b0111_1111 {
+ // 0 1 1 1 1 1 1 0
+ let operands = handler.read_u8(words)?;
+ let opc = operands >> 4;
+ let rd = operands & 0b1111;
+ match opc {
+ 0b0000 => {
+ handler.on_opcode_decoded(Opcode::NOT)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b0001 => {
+ handler.on_opcode_decoded(Opcode::NEG)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b0010 => {
+ handler.on_opcode_decoded(Opcode::ABS)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b0011 => {
+ handler.on_opcode_decoded(Opcode::SAT)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b0100 => {
+ handler.on_opcode_decoded(Opcode::RORC)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b0101 => {
+ handler.on_opcode_decoded(Opcode::ROLC)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b1000 => {
+ handler.on_opcode_decoded(Opcode::PUSH)?;
+ handler.on_operand_decoded(0, Operand::Subreg { num: rd, size: SizeCode::B })?;
+ }
+ 0b1001 => {
+ handler.on_opcode_decoded(Opcode::PUSH)?;
+ handler.on_operand_decoded(0, Operand::Subreg { num: rd, size: SizeCode::W })?;
+ }
+ 0b1010 => {
+ handler.on_opcode_decoded(Opcode::PUSH)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b1011 => {
+ handler.on_opcode_decoded(Opcode::POP)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rd })?;
+ }
+ 0b1100 => {
+ handler.on_opcode_decoded(Opcode::PUSHC)?;
+ handler.on_operand_decoded(0, decoder.reg_to_control_reg(rd)?)?;
+ }
+ 0b1110 => {
+ handler.on_opcode_decoded(Opcode::POPC)?;
+ // can't popc into pc
+ if rd == 0b0001 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_operand_decoded(0, decoder.reg_to_control_reg(rd)?)?;
+ }
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ }
+ } else if opc < 0b1000_0000 {
+ // 0 1 1 1 1 1 1 1
+ let operands = handler.read_u8(words)?;
+ let opc = operands >> 4;
+ if opc < 0b1000 {
+ handler.on_operand_decoded(0, Operand::Register { num: operands & 0b1111 })?;
+ let opcode = match opc {
+ 0b0000 => Opcode::JMP,
+ 0b0001 => Opcode::JSR,
+ 0b0100 => Opcode::BRA,
+ 0b0101 => Opcode::BRA,
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+ handler.on_opcode_decoded(opcode)?;
+ } else if opc == 0b1000 {
+ // 0 1 1 1 1 1 1 1 | 1 0 0 0 ...
+ let opcode = [
+ Opcode::SUNTIL_B, Opcode::SUNTIL_W, Opcode::SUNTIL_L, Opcode::SCMPU,
+ Opcode::SWHILE_B, Opcode::SWHILE_W, Opcode::SWHILE_L, Opcode::SMOVU,
+ Opcode::SSTR_B, Opcode::SSTR_W, Opcode::SSTR_L, Opcode::SMOVB,
+ Opcode::RMPA_B, Opcode::RMPA_W, Opcode::RMPA_L, Opcode::SMOVF,
+ ][operands as usize & 0b1111];
+ handler.on_opcode_decoded(opcode)?;
+ } else if opc == 0b1001 {
+ // 0 1 1 1 1 1 1 1 | 1 0 0 1 ...
+ let opcode = match operands & 0b1111 {
+ 0b0011 => Opcode::SATR,
+ 0b0100 => Opcode::RTFI,
+ 0b0101 => Opcode::RTE,
+ 0b0110 => Opcode::WAIT,
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+ handler.on_opcode_decoded(opcode)?;
+ } else if opc == 0b1010 {
+ // setpsw
+ handler.on_operand_decoded(0, decoder.num_to_psw_bit(operands & 0b1111)?)?;
+ handler.on_opcode_decoded(Opcode::SETPSW)?;
+ } else if opc == 0b1011 {
+ // clrpsw
+ handler.on_operand_decoded(0, decoder.num_to_psw_bit(operands & 0b1111)?)?;
+ handler.on_opcode_decoded(Opcode::CLRPSW)?;
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ } else if opc < 0b1111_0000 {
+ // 1 0 x x x x x x MOV
+ // 1 1 x x <11 <11 MOV
+ let sz = (opc >> 4) & 0b11;
+ if sz == 0b11 {
+ // definitely `1 0 1 1`: `1 1 1 1` would fail the branch above
+ // disp[Rs], Rd
+ handler.on_opcode_decoded(Opcode::MOVU)?;
+ let sz = (opc >> 3) & 0b1 != 0 {
+ SizeCode::B
+ } else {
+ SizeCode::W
+ };
+ let operands = handler.read_u8(words)?;
+ let rs = operands & 0b111;
+ let rd = (operands >> 4) & 0b111;
+ let displo = (operands >> 3) & 1;
+ let dispmid = (operands >> 6) & 2;
+ let disphi = (opc & 0b111) << 2;
+ let disp = displo | dispmid | disphi;
+ handler.on_operand_decoded(0, Operand::Deref { gpr: rs, disp: disp as u32, width: sz })?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ } else {
+ handler.on_opcode_decoded(Opcode::MOV)?;
+ let sz = [SizeCode::B, SizeCode::W, SizeCode::L][sz as usize];
+ // sz = 00, 01, 10, one of two styles of `mov`.
+ if opc & 0b0100_0000 == 0 {
+ // disp is 5-bit embedded form
+ let operands = handler.read_u8(words)?;
+ let rs = operands & 0b111;
+ let rd = (operands >> 4) & 0b111;
+ let displo = (operands >> 3) & 1;
+ let dispmid = (operands >> 6) & 2;
+ let disphi = (opc & 0b111) << 2;
+ let disp = displo | dispmid | disphi;
+ if opc & 0b0000_1000 {
+ handler.on_operand_decoded(0, Operand::Register { gpr: rs })?;
+ handler.on_operand_decoded(1, Operand::Deref { gpr: rd, disp: disp as u32, width: sz })?;
+ } else {
+ handler.on_operand_decoded(0, Operand::Deref { gpr: rs, disp: disp as u32, width: sz })?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ }
+ } else {
+ // either operand may have displacement, may be mem-mem
+ let ldd = (opc >> 2) & 0b11;
+ let lds = opc & 0b11;
+ let operands = handler.read_u8(words)?;
+ match (ldd, lds) {
+ (0b11, 0b11) => {
+ // encoding 7, explicitly reg-reg, might involve sign/zero extension
+ let source_op = if sz == SizeSpec::L {
+ Operand::Register { gpr: rs }
+ } else {
+ Operand::Subreg { gpr: rs, sz }
+ };
+ handler.on_operand_decoded(0, source_op)?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ }
+ (ld, 0b11) => {
+ handler.on_operand_decoded(0, Operand::Register { gpr: rs })?;
+ handler.on_operand_decoded(1, handler.decode_mem_op(rd, ld, sz, words)?)?;
+ },
+ (0b11, ld) => {
+ handler.on_operand_decoded(0, handler.decode_mem_op(rs, ld, sz, words)?)?;
+ handler.on_operand_decoded(1, Operand::Register { gpr: rd })?;
+ },
+ (ldd, lds) => {
+ handler.on_operand_decoded(0, handler.decode_mem_op(rs, lds, sz, words)?)?;
+ handler.on_operand_decoded(1, handler.decode_mem_op(rd, ldd, sz, words)?)?;
+ }
+ }
+ }
+ }
+ } else if opc < 0b1111_0100 {
+ // 1 1 1 1 0 0 x x BSET/BCLR
+ let ld = opc & 0b11;
+ if ld == 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let operands = handler.read_u8(words)?;
+ let imm = operands & 0b111;
+ let rd = operands >> 4;
+
+ let operand = handler.decode_mem_op(rd, ld, SizeCode::B, words)?;
+
+ let opcode = if operands & 0b0000_1000 == 0 {
+ Opcode::BSET
+ } else {
+ Opcode::BCLR
+ };
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, operand)?;
+ } else if opc < 0b1111_1000 {
+ // 1 1 1 1 0 1 x x BTST/PUSH
+ let ld = opc & 0b11;
+ if ld == 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let operands = handler.read_u8(words)?;
+ let rs = operands >> 4;
+ if operands & 0b0000_1000 == 0 {
+ let imm = operands & 0b111;
+ handler.on_operand_decoded(1, handler.decode_mem_op(ld, rs, SizeCode::B, words)?)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_opcode_decoded(Opcode::BTST)?;
+ } else if operands & 0b0000_1100 == 0b0000_1000 {
+ let sz = match operands & 0b11 {
+ 0b00 => SizeCode::B,
+ 0b01 => SizeCode::W,
+ 0b10 => SizeCode::L,
+ _ => { unreachable!("checked for ld!=11 earlier"); }
+ };
+
+ handler.on_operand_decoded(0, handler.decode_mem_op(ld, rs, sz, words)?)?;
+ handler.on_opcode_decoded(Opcode::PUSH)?;
+ } else {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ } else if opc < 0b1111_1100 {
+ // 1 1 1 1 1 0 x x MOV (or v3+, DMOV)
+ let ld = opc & 0b11;
+ let operands = handler.read_u8(words)?;
+ let sz = operands & 0b11;
+ if sz == 0b11 {
+ if decoder.version < RxVersion::V3 {
+ // rxv1 or rxv2, no dmov yet, so this is an invalid `sz` for `mov`
+ return Err(StandardDecodeError::InvalidOperand);
+ } else {
+ // rxv3+, dmov.*
+ if operands != 0b0000_0011 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ let operands = handler.read_u8(words)?;
+ let rd = operands >> 4;
+ match operands & 0b1111 {
+ 0b0000 => {
+ handler.on_opcode_decoded(Opcode::DMOV);
+ handler.on_operand_decoded(1, Operand::DoubleRegLow { num: rd })?;
+ },
+ 0b0010 => {
+ handler.on_opcode_decoded(Opcode::DMOV);
+ handler.on_operand_decoded(1, Operand::DoubleRegHigh { num: rd })?;
+ },
+ 0b0011 => {
+ // not really sure what makes the immediate D size here. does hardware
+ // expand the 32b float into a 64b float?
+ handler.on_opcode_decoded(Opcode::DMOV);
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ },
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ }
+ handler.on_operand_decoded(0, Operand::ImmL { imm: handler.read_u32(words)? })?;
+ }
+ } else {
+ let rd = operands >> 4;
+ let li = (operands >> 2) & 0b11;
+ let sz = [SizeCode::B, SizeCode::W, SizeCode::L][sz as usize];
+ // note this is a bit of a lie: `li=10` is actually a 24b immediate, but for comparison
+ // here L is close enough..
+ let li_code = [SizeCode::B, SizeCode::W, SizeCode::L, SizeCode::L][li as usize];
+ // what happens when `li` is larger than `sz`...??? undefined!
+ if li_code.bytes() > sz.bytes() {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let operand = handler.decode_mem_op(ld, rd, sz, words)?;
+ let imm = match li {
+ 0b00 => {
+ handler.read_u32(words)?
+ }
+ 0b01 => {
+ handler.read_u8(words)? as i8 as i32 as u32
+ }
+ 0b10 => {
+ handler.read_u16(words)? as i16 as i32 as u32
+ }
+ _ => {
+ debug_assert!(li == 0b11, "li is at most the low two bits set");
+ (((handler.read_u24(words)? as i32) << 8) >> 8) as u32
+ }
+ };
+ handler.on_opcode_decoded(Opcode::MOV)?;
+ handler.on_operand_decoded(0, Operand::ImmL { imm })?;
+ handler.on_operand_decoded(1, operand)?;
+ }
+ } else if opc == 0b1111_1100 {
+ // many instructions
+ let operands = handler.read_u8(words)?;
+ let opc5 = (operands >> 2) & 0b11111;
+ let ld = operands & 0b11;
+
+ let registers = handler.read_u8(words)?;
+ let rd = registers & 0b1111;
+ let rs = registers >> 4;
+
+ if operands & 0b1000_0000 == 0 {
+ // 1 1 1 1 1 1 0 0 | 0 ..
+ if opc5 < 0b10010 {
+ // simple enough: `0 [ opc5 ] ld | [ rs ] [ rd ]`
+ let opcode = match opc5 {
+ 0b00000 => {
+ if ld != 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ Opcode::SBB
+ }
+ 0b00001 => {
+ if ld != 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ Opcode::NEG
+ }
+ 0b00010 => {
+ if ld != 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ Opcode::ADC
+ }
+ 0b00011 => {
+ if ld != 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ Opcode::ABS
+ }
+ 0b00100 => Opcode::MAX,
+ 0b00101 => Opcode::MIN,
+ 0b00110 => Opcode::EMUL,
+ 0b00111 => Opcode::EMULU,
+ 0b01000 => Opcode::DIV,
+ 0b01001 => Opcode::DIVU,
+ 0b01010 => { return Err(StandardDecodeError::InvalidOperand); },
+ 0b01011 => { return Err(StandardDecodeError::InvalidOperand); },
+ 0b01100 => Opcode::TST,
+ 0b01101 => Opcode::XOR,
+ 0b01110 => {
+ if ld != 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ Opcode::NOT
+ }
+ 0b01111 => { return Err(StandardDecodeError::InvalidOperand); },
+ 0b10000 => Opcode::XCHG,
+ _ => {
+ debug_assert!(opc5 == 0b10001, "checked opc is below 0b10010");
+ Opcode::ITOF
+ }
+ };
+ handler.on_opcode_decoded(opcode)?;
+ let source = handler.decode_mem_op(ld, rs, SizeCode::L, words)?;
+ handler.on_operand_decoded(0, source)?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opc5 < 0b10100 {
+ // opc is larger. decoding gets more custom..
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ handler.on_opcode_decoded(if opc5 & 1 == 0 {
+ Opcode::STZ
+ } else {
+ Opcode::STNZ
+ })?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opc5 < 0b10101 {
+ // 1 1 1 1 1 1 0 0 | 0 1 0 1 0 0 ..
+ // nothing here (yet?)
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if opc5 < 0b10110 {
+ // 1 1 1 1 1 1 0 0 | 0 1 0 1 0 1 ..
+ // utof
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ handler.on_opcode_decoded(Opcode::UTOF)?;
+ handler.on_operand_decoded(0, handler.decode_mem_op(ld, rs, SizeCode::L, words)?)?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opc5 < 0b11000 {
+ // 1 1 1 1 1 1 0 0 | 0 1 1 0 x x ..
+ // bfmov{,z}
+ if decoder.version < RxVersion::V3 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ let regs = handler.read_u8(words)?;
+ handler.on_opcode_decoded(if opc5 & 1 == 0 {
+ Opcode::BFMOVZ
+ } else {
+ Opcode::BFMOV
+ })?;
+ let rs = regs >> 4;
+ let rd = regs & 0b1111;
+
+ let bits = handler.read_u16(words)?;
+ handler.on_operand_decoded(0, Operand::BitfieldSpec { bf_spec: BitfieldSpec { bits } })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(2, Operand::Register { num: rd })?;
+ } else if opc5 < 0b11100 {
+ let opcode = [Opcode::BSET, Opcode::BCLR, Opcode::BTST, Opcode::BNOT][opc5 as usize & 0b11];
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, handler.decode_mem_op(ld, rd, SizeCode::B, words)?)?;
+ } else if opc5 == 0b11110 {
+ if decoder.version < RxVersion::V3 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ let regs = handler.read_u8(words)?;
+ let regs_lo = regs & 0b1111;
+ // this encoding of dmov.d just.. requires 1000 here? no reason as far as i can
+ // see, it Just Does.
+ if regs_lo != 0b1000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ let rd = regs >> 4;
+ let dest_op = handler.decode_mem_op(ld, rd, SizeCode::D, words)?;
+ let regs = handler.read_u8(words)?;
+ let rs = regs >> 4;
+ let regs_lo = regs & 0b1111;
+ // similarly, just requires that the low four bits here are all 0.
+ if regs_lo != 0b0000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ handler.on_opcode_decoded(Opcode::DMOV)?;
+ handler.on_operand_decoded(0, Operand::DoubleReg { num: rs })?;
+ handler.on_operand_decoded(1, dest_op)?;
+ } else {
+ // 1 1 1 1 1 1 0 0 | 0 1 1 1 1 l ...
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ } else {
+ // 1 1 1 1 1 1 0 0 | 1 ..
+ if opc5 < 0b10000 {
+ let opcode = match opc5 & 0b1111 {
+ 0b0000 => Opcode::FSUB,
+ 0b0001 => Opcode::FCMP,
+ 0b0010 => Opcode::FADD,
+ 0b0011 => Opcode::FMUL,
+ 0b0100 => Opcode::FDIV,
+ 0b0101 => Opcode::FTOI,
+ 0b0110 => Opcode::ROUND,
+ 0b1000 => {
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ Opcode::FSQRT
+ },
+ 0b1001 => {
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ Opcode::FTOU
+ },
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+ let regs = handler.read_u8(words)?;
+ let rs = regs >> 4;
+ let rd = regs & 0b1111;
+ let source = handler.decode_mem_op(ld, rs, SizeCode::D, words)?;
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, source)?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ } else if opc5 == 0b10010 {
+ // 1 1 1 1 1 1 0 0 | 1 1 0 0 1 0 ..
+ // dmovd
+ if decoder.version < RxVersion::V3 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ let operands = handler.read_u8(words)?;
+ let rs = operands >> 4;
+ if operands & 0b1111 != 0b1000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ let source = handler.decode_mem_op(ld, rs, SizeCode::D, words)?;
+ let operands = handler.read_u8(words)?;
+ let rd = operands >> 4;
+ if operands & 0b1111 != 0b0000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ handler.on_opcode_decoded(Opcode::DMOV)?;
+ handler.on_operand_decoded(0, source)?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ } else if opc5 < 0b11000 {
+ // 1 1 1 1 1 1 0 0 | 1 1 0 1 sz ..
+ // SCCnd.size
+ let operands = handler.read_u8(words)?;
+ let rd = operands >> 4;
+ let cnd = operands & 0b1111;
+ let sz = match opc5 & 0b11 {
+ 0b00 => SizeCode::B,
+ 0b01 => SizeCode::W,
+ 0b10 => SizeCode::L,
+ _ => {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ };
+ if cnd >= 0b1110 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ let opcode = [
+ Opcode::SCEQ, Opcode::SCNE, Opcode::SCGEU, Opcode::SCLTU,
+ Opcode::SCGTU, Opcode::SCLEU, Opcode::SCPZ, Opcode::SCN,
+ Opcode::SCGE, Opcode::SCLT, Opcode::SCGT, Opcode::SCLE,
+ Opcode::SCO, Opcode::SCNO, Opcode::NOP, Opcode::NOP // "NOP" is never reached: cnd>=1110, invalid above
+ ][cnd as usize];
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, handler.decode_mem_op(ld, rd, sz, words)?)?;
+ } else if opc5 >= 0b11000 {
+ // 1 1 1 1 1 1 0 0 | 1 1 1 [imm3] ..
+ let operands = handler.read_u8(words)?;
+ let rd = operands >> 4;
+ let cnd = operands & 0b1111;
+ let imm = opc5 & 0b111;
+
+ if ld == 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let opcode = if cnd == 0b1111 {
+ Opcode::BNOT
+ } else if cnd == 0b1110 {
+ } else {
+ [
+ Opcode::BMEQ, Opcode::BMNE, Opcode::BMGEU, Opcode::BMLTU,
+ Opcode::BMGTU, Opcode::BMLEU, Opcode::BMPZ, Opcode::BMN,
+ Opcode::BMGE, Opcode::BMLT, Opcode::BMGT, Opcode::BMLE,
+ Opcode::BMO, Opcode::BMNO, Opcode::NOP, Opcode::NOP // "NOP" is never reached: cnd>=1110, invalid above
+ ][cnd as usize]
+ };
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_operand_decoded(1, handler.decode_mem_op(ld, rd, SizeCode::B, words)?)?;
+ } else {
+ unreachable!("should be unreachable, fuzzing will tell..");
+ }
+ }
+ } else if opc == 0b1111_1101 {
+ // many instructions
+ // for *almost* everything under this category, the next byte also picks opcodes. some use
+ // bits here for operands though.
+ let opcode = handler.read_u8(words)?;
+
+ if opcode < 0b1000_0000 {
+ // 1 1 1 1 1 1 0 1 | 0 ....
+ if opcode < 0b0001_0000 {
+ let a = (opcode >> 3) & 1;
+ if a != 0 && decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let regs = handler.read_u8(words)?;
+ let rs = regs >> 4;
+ let rs2 = regs & 0b1111;
+
+ // hokey, but this does filter for mullh, emula, maclh, and emaca
+ if opcode & 0b010 != 0b000 && decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ let opcode = match opcode & 0b111 {
+ 0b000 => Opcode::MULHI,
+ 0b001 => Opcode::MULLO,
+ 0b010 => Opcode::MULLH,
+ 0b011 => Opcode::EMULA,
+ 0b100 => Opcode::MACHI,
+ 0b101 => Opcode::MACLO,
+ 0b110 => Opcode::MACLH,
+ _ => Opcode::EMACA,
+ };
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Opcode::Register { num: rs })?;
+ handler.on_operand_decoded(1, Opcode::Register { num: rs })?;
+ handler.on_operand_decoded(2, Opcode::Accumulator { num: a })?;
+ } else if opcode == 0b0001_0111 {
+ let operands = handler.read_u8(words)?;
+ let rs = operands & 0b1111;
+ let opc = (operands >> 4) & 0b111;
+ let a = operands >> 7;
+
+ if a != 0 && decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let opcode = match opc {
+ 0b000 => Opcode::MVTACHI,
+ 0b001 => Opcode::MVTACLO,
+ 0b011 => {
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ Opcode::MVTACGU
+ }
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Opcode::Register { num: rs })?;
+ handler.on_operand_decoded(1, Opcode::Accumulator { num: a })?;
+ } else if opcode & 0b1111_1110 == 0b0001_1000 {
+ // we can use the lowest bit of opcode to decide if this is racw/racl (0==r==racw)
+ let lr = opcode & 1;
+
+ let operands = handler.read_u8(words)?;
+ let rs = operands & 0b1111;
+ let imm = (operands >> 4) & 0b11;
+ let opc = (operands >> 6) & 1;
+ let a = operands >> 7;
+
+ if imm > 0b01 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ if a != 0 && decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let opcode = if opc == 0 {
+ if lr == 0 { Opcode::RACW } else { Opcode::RACL }
+ } else if decoder.version >= RxVersion::V2 {
+ if lr == 0 { Opcode::RDACW } else { Opcode::RDACL }
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ };
+ handler.on_opcode_decoded(opcode);
+ handler.on_operand_decoded(0, Operand::ImmB { imm: imm + 1 })?;
+ handler.on_operand_decoded(1, Operand::Accumulator { num: a })?;
+ } else if opcode & 0b1111_1110 == 0b0001_1110 {
+ let operands = handler.read_u8(words)?;
+ let rd = operands & 0b1111;
+ let immlo = (operands >> 6) & 1;
+ let immhi = opcode & 1;
+ let imm = (immhi << 1) | immlo;
+ let opc = (operands >> 4) & 0b11;
+ let a = operands >> 7;
+
+ let opcode = match opc {
+ 0b00 => Opcode::MVFACHI,
+ 0b01 => {
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ Opcode::MVFACLO
+ }
+ 0b10 => Opcode::MVFACMI,
+ _ => {
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ Opcode::MVFACGU
+ }
+ };
+
+ if imm != 0 && decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_operand_decoded(1, Operand::Accumulator { num: a })?;
+ handler.on_operand_decoded(2, Operand::Register { num: rd })?;
+ } else if opcode < 0b0100_0000 {
+ // 1 1 1 1 1 1 0 1 | 0 0 1 x x x x x
+ // nothing here yet
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if opcode < 0b0101_0000 {
+ // 1 1 1 1 1 1 0 1 | 0 1 0 0 a x x x
+ if opcode & 0b0000_0100 != 0 {
+ // nothing here, msbhi and friends require bit above xx to be 1
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ if decoder.version < RxVersion::V2 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ let a = (opcode >> 3) & 1;
+ let opc = opcode & 0b11;
+ let operands = handler.read_u8(words)?;
+ let rs = operands >> 4;
+ let rs2 = operands & 0b1111;
+
+ let opcode = [Opcode::MSBHI, Opcode::MSBLH, Opcode::MSBLO, Opcode::EMSBA][opc as usize];
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rs2 })?;
+ handler.on_operand_decoded(2, Operand::Accumulator { num: a })?;
+ } else if opcode < 0b0110_0000 {
+ // 1 1 1 1 1 1 0 1 | 0 1 0 1 x x x x
+ // nothing here yet either
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if opcode < 0b0110_1000 {
+ // 1 1 1 1 1 1 0 1 | 0 1 1 0 0 x x x
+ let operands = handler.read_u8(words)?;
+ let rd = operands & 0b1111;
+ let rs = operands >> 4;
+
+ let opcode = match opcode & 0b111 {
+ 0b000 => Opcode::SHLR,
+ 0b001 => Opcode::SHAR,
+ 0b010 => Opcode::SHLL,
+ 0b011 => { return Err(StandardDecodeError::InvalidOpcode); },
+ 0b100 => Opcode::ROTR,
+ 0b101 => Opcode::REVW,
+ 0b110 => Opcode::ROTL,
+ _ => Opcode::REVL,
+ };
+
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opcode == 0b0110_1000 {
+ let operands = handler.read_u8(words)?;
+ let cr = operands & 0b1111;
+ let rs = operands >> 4;
+
+ if cr == 0b0010 {
+ // can't move to pc
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_opcode_deoded(Opcode::MVTC)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, decoder.reg_to_control_reg(cr)?)?;
+ } else if opcode == 0b0110_1010 {
+ let operands = handler.read_u8(words)?;
+ let rd = operands & 0b1111;
+ let cr = operands >> 4;
+
+ handler.on_opcode_deoded(Opcode::MVFC)?;
+ handler.on_operand_decoded(0, decoder.reg_to_control_reg(cr)?)?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opcode < 0b0110_1100 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if opcode < 0b0111_0000 {
+ // 1 1 1 1 1 1 0 1 | 0 1 1 0 1 1 x x
+ // rotr/rotl
+ let operands = handler.read_u8(words)?;
+ let rd = operands & 0b1111;
+ let immlo = operands >> 4;
+ let immhi = opcode & 1;
+ let imm = (immhi << 4) | immlo;
+ handler.on_opcode_decoded(if opcode & 0b10 == 0 {
+ Opcode::ROTR
+ } else {
+ Opcode::ROTL
+ })?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opcode == 0b0111_0101 {
+ if decoder.version < RxVersion::V3 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ let operands = handler.read_u8(words)?;
+ if operands & 0b1111_0000 != 0b1000_0000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ let rd = operands & 0b1111;
+ let operands = handler.read_u8(words)?;
+ let opc = operands & 0b1111;
+ let rs = operands >> 4;
+
+ match opc {
+ 0b0000 => {
+ handler.on_opcode_decoded(Opcode::DMOV)?;
+ handler.on_operand_decoded(0, Operand::DoubleRegLow { num: rs })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ }
+ 0b0010 => {
+ handler.on_opcode_decoded(Opcode::DMOV)?;
+ handler.on_operand_decoded(0, Operand::DoubleRegHigh { num: rs })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ }
+ 0b0100 => {
+ handler.on_opcode_decoded(Opcode::MVFDC)?;
+ handler.on_operand_decoded(0, Operand::DoubleRegHigh { num: rs })?;
+ handler.on_operand_decoded(1, decoder.reg_to_double_control_reg(rd)?)?;
+ }
+ _ => { return Err(StandardDecodeError::InvalidOpcode) }
+ }
+ } else if opcode == 0b0111_0110 {
+ if decoder.version < RxVersion::V3 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ let operands = handler.read_u8(words)?;
+ let opc = operands >> 4;
+ let rs = operands & 0b1111;
+ let imm = handler.read_u8(words)?;
+
+ match opc {
+ 0b1100 => {
+ if imm != 0b0000_0000 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_opcode_decoded(Opcode::SAVE)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ }
+ 0b1101 => {
+ if imm != 0b0000_0000 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_opcode_decoded(Opcode::RSTR)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ }
+ 0b1110 => {
+ if rs != 0b0000_0000 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_opcode_decoded(Opcode::SAVE)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ }
+ 0b1111 => {
+ if rs != 0b0000_0000 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ handler.on_opcode_decoded(Opcode::RSTR)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ }
+ _ => { return Err(StandardDecodeError::InvalidOpcode) }
+ };
+ } else if opcode == 0b0111_0111 {
+ if decoder.version < RxVersion::V3 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+
+ let operands = handler.read_u8(words)?;
+ if operands & 0b1111_0000 != 0b1000_0000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ let rs = operands & 0b1111;
+ let operands = handler.read_u8(words)?;
+ let rd = operands >> 4;
+ let opc = operands & 0b1111;
+ match opc {
+ 0b0000 => {
+ // dmov.l from gpr to drlN (encoding 3)
+ handler.on_opcode_decoded(Opcode::DMOV)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleRegLow { num: rd })?;
+ }
+ 0b0010 => {
+ // dmov.l from gpr to drhN (encoding 2)
+ handler.on_opcode_decoded(Opcode::DMOV)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleRegHigh { num: rd })?;
+ }
+ 0b0011 => {
+ // mov.d from gpr to drhN (encoding 1) (... ? what ... )
+ handler.on_opcode_decoded(Opcode::DMOV)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ }
+ 0b0100 => {
+ // mvtdc
+ let cr = decoder.reg_to_double_control_reg(rd)?;
+ handler.on_opcode_decoded(Opcode::MVTDC)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, cr)?;
+ }
+ 0b1001 => {
+ // itod
+ handler.on_opcode_decoded(Opcode::ITOD)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ }
+ 0b1010 => {
+ // ftod
+ handler.on_opcode_decoded(Opcode::FTOD)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ }
+ 0b1101 => {
+ // utod
+ handler.on_opcode_decoded(Opcode::UTOD)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DoubleReg { num: rd })?;
+ }
+ }
+ } else if opcode < 0b0111_0000 {
+ // any unhandled cases below 0111_0000, which need more... careful handling.
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else {
+ let li = (opcode >> 2) & 0b11;
+ let opc_lo = opcode & 0b11;
+ if opc_lo == 0b00 {
+ // 1 1 1 1 1 1 0 1 | 0 1 1 1 xx 0 0 | ...
+ // <op> imm, reg
+ let opcode = handler.read_u8(words)?;
+ let rd = opcode & 0b1111;
+ let opcode = opcode >> 4;
+ let opcode = match opcode {
+ 0b0010 => Opcode::ADC,
+ 0b0100 => Opcode::MAX,
+ 0b0101 => Opcode::MIN,
+ 0b0110 => Opcode::EMUL,
+ 0b0111 => Opcode::EMULU,
+ 0b1000 => Opcode::DIV,
+ 0b1001 => Opcode::DIVU,
+ 0b1100 => Opcode::TST,
+ 0b1101 => Opcode::XOR,
+ 0b1110 => Opcode::STZ,
+ 0b1111 => Opcode::STNZ,
+ _ => { return Err(StandardDecodeError::InvalidOpcode) }
+ };
+ handler.on_opcode_decoded(opcode)?;
+ let imm = match li {
+ 0b00 => {
+ handler.read_u32(words)?
+ }
+ 0b01 => {
+ handler.read_u8(words)? as i8 as i32 as u32
+ }
+ 0b10 => {
+ handler.read_u16(words)? as i16 as i32 as u32
+ }
+ _ => {
+ debug_assert!(li == 0b11, "li is at most the low two bits set");
+ (((handler.read_u24(words)? as i32) << 8) >> 8) as u32
+ }
+ };
+ handler.on_operand_decoded(0, Operand::ImmL { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if opc_lo == 0b11 {
+ // 1 1 1 1 1 1 0 1 | 0 1 1 1 xx 1 1 | ...
+ // mvtc imm, creg
+ let opcode = handler.read_u8(words)?;
+ let rd = opcode & 0b1111;
+ let opcode = opcode >> 4;
+ if opcode != 0b0000 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ if rd == 0b0001 {
+ // can't set pc
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+ let cr = decoder.reg_to_control_reg(rd)?;
+ handler.on_opcode_decoded(Opcode::MVTC)?;
+ let imm = match li {
+ 0b00 => {
+ handler.read_u32(words)?
+ }
+ 0b01 => {
+ handler.read_u8(words)? as i8 as i32 as u32
+ }
+ 0b10 => {
+ handler.read_u16(words)? as i16 as i32 as u32
+ }
+ _ => {
+ debug_assert!(li == 0b11, "li is at most the low two bits set");
+ (((handler.read_u24(words)? as i32) << 8) >> 8) as u32
+ }
+ };
+ handler.on_operand_decoded(0, Operand::ImmL { imm })?;
+ handler.on_operand_decoded(1, cr)?;
+ } else if opc_lo == 0b10 {
+ // 1 1 1 1 1 1 0 1 | 0 1 1 1 xx 1 0 | ...
+ // <float op> imm, reg
+ if li != 0b00 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ let opcode = handler.read_u8(words)?;
+ let rd = opcode & 0b1111;
+ let opcode = opcode >> 4;
+ if opcode > 0b100 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ handler.on_opcode_decoded(
+ [Opcode::FSUB, Opcode::FCMP, Opcode::FADD, Opcode::FMUL, Opcode::FDIV][opcode as usize]
+ )?;
+ handler.on_operand_decoded(0, Operand::ImmL { imm: handler.read_u32(words)? })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd });
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ }
+ } else {
+ // 1 1 1 1 1 1 0 1 | 1 ....
+ // TODO:
+ let opc = (opcode >> 5) & 0b11;
+ let imm5 = opcode & 0b1_1111;
+
+ let operands = handler.read_u8(words)?;
+ let rd = operands & 0b1111;
+ let rs2 = operands >> 4;
+
+ if opc == 0b11 {
+ // bmcnd/bnot
+ let cnd = rs2;
+ let opcode = [
+ Opcode::BMEQ, Opcode::BMNE, Opcode::BMGEU, Opcode::BMLTU,
+ Opcode::BMGTU, Opcode::BMLEU, Opcode::BMPZ, Opcode::BMN,
+ Opcode::BMGE, Opcode::BMLT, Opcode::BMGT, Opcode::BMLE,
+ Opcode::BMO, Opcode::BMNO, Opcode::NOP, Opcode::BNOT // "NOP" is never reached: cnd>=1110, invalid above
+ ][cnd as usize];
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else {
+ let opcode = [Opcode::SHLR, Opcode::SHAR, Opcode::SHLL][opc as usize];
+ handler.on_opcode_decoded(opcode)?;
+ handler.on_operand_decoded(0, Operand::ImmB { imm: imm5 })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rs2 })?;
+ handler.on_operand_decoded(2, Operand::Register { num: rd })?;
+ }
+ }
+ } else if opc == 0b1111_1110 {
+ // mov
+ let operands = handler.read_u8(words)?;
+ let next_regs = handler.read_u8(words)?;
+ let ri = operands & 0b1111;
+ let rb = next_reg >> 4;
+ let rd = next_regs & 0b1111;
+ let sz = (operands >> 4) & 0b11;
+
+ if sz == 0b11 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ if operands < 0b0100 {
+ handler.on_opcode_decoded(Opcode::MOV)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::DerefIndexed { base: rb, index: ri, width: sz })?;
+ } else if operands < 0b1000 {
+ handler.on_opcode_decoded(Opcode::MOV)?;
+ handler.on_operand_decoded(0, Operand::DerefIndexed { base: rb, index: ri, width: sz })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else if operands < 0b1100 {
+ return Err(StandardDecodeError::InvalidOpcode);
+ } else if operands < 0b1110 {
+ if sz == 0b10 {
+ return Err(StandardDecodeError::InvalidOperand);
+ }
+
+ handler.on_opcode_decoded(Opcode::MOVU)?;
+ handler.on_operand_decoded(0, Operand::DerefIndexed { base: rb, index: ri, width: sz })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rd })?;
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ } else if opc == 0b1111_1111 {
+ // 3-operand instructions
+ let operands = handler.read_u8(words)?;
+ let rd = operands & 0b1111;
+ let opc = operands >> 4;
+ let extra_regs = handler.read_u8(words)?;
+ let rs2 = extra_regs & 0b1111;
+ let rs = extra_regs >> 4;
+
+ let opc = match opc {
+ 0b0000 => Opcode::SUB,
+ 0b0010 => Opcode::ADD,
+ 0b0011 => Opcode::MUL,
+ 0b0100 => Opcode::AND,
+ 0b0101 => Opcode::OR,
+ 0b0110 => {
+ if decoder.version >= RxVersion::V3 {
+ Opcode::XOR
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ },
+ 0b1000 => {
+ if decoder.version >= RxVersion::V2 {
+ Opcode::FSUB
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ },
+ 0b1010 => {
+ if decoder.version >= RxVersion::V2 {
+ Opcode::FADD
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ },
+ 0b1011 => {
+ if decoder.version >= RxVersion::V2 {
+ Opcode::FMUL
+ } else {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ },
+ _ => {
+ return Err(StandardDecodeError::InvalidOpcode);
+ }
+ };
+
+ handler.on_opcode_decoded(opc)?;
+ handler.on_operand_decoded(0, Operand::Register { num: rs })?;
+ handler.on_operand_decoded(1, Operand::Register { num: rs2 })?;
+ handler.on_operand_decoded(2, Operand::Register { num: rd })?;
+ } else {
+ unreachable!("fuzzing should show this to be unreachable");
+ }
+
+ Ok(())
+}