summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2020-01-28 01:13:07 -0800
committeriximeow <me@iximeow.net>2020-01-28 01:15:31 -0800
commit87ac71fd89bd0dda7ee0e7b74f11c381718e4f84 (patch)
tree963389985b36fad40114aa8c6bf212e99c6336c8
draw the owl
-rw-r--r--.gitignore1
-rw-r--r--Cargo.toml31
-rw-r--r--src/lib.rs2091
-rw-r--r--test/test.rs52
4 files changed, 2175 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.toml b/Cargo.toml
new file mode 100644
index 0000000..2e6e1f5
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,31 @@
+[package]
+authors = [ "iximeow <me@iximeow.net>" ]
+description = "instruction decoder for the renesas m16c architecture"
+edition = "2018"
+license = "0BSD"
+name = "yaxpeax-m16c"
+repository = "http://git.iximeow.net/yaxpeax-m16c/"
+version = "0.0.1"
+
+[dependencies]
+yaxpeax-arch = { version = "0.0.2", default-features = false, features = [] }
+"num-traits" = { version = "0.2", default-features = false }
+"serde" = { version = "1.0", optional = true }
+"serde_json" = { version = "1.0", optional = true }
+"serde_derive" = { version = "1.0", optional = true }
+
+[[test]]
+name = "test"
+path = "test/test.rs"
+
+[profile.release]
+opt-level = 3
+lto = true
+
+[features]
+default = ["std", "use-serde"]
+
+# opt-in for some apis that are really much nicer with String
+std = []
+
+use-serde = ["yaxpeax-arch/use-serde", "serde", "serde_derive"]
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..59b3940
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,2091 @@
+#![no_std]
+
+#![allow(non_snake_case)]
+
+#[cfg(feature="use-serde")]
+#[macro_use]
+extern crate serde_derive;
+
+use core::fmt;
+
+use yaxpeax_arch::{Arch, Decoder, LengthedInstruction};
+
+#[allow(non_camel_case_types)]
+#[derive(Debug, Copy, Clone)]
+pub enum Opcode {
+ ABS,
+ ADC(Size),
+ ADCF(Size),
+ ADD(Size),
+ ADJNZ(Size),
+ AND(Size),
+ BAND,
+ BCLR,
+ BMEQ,
+ BMGEU,
+ BMGT,
+ BMGTU,
+ BMLE,
+ BMLEU,
+ BMLTU,
+ BMLT,
+ BMNO,
+ BMGE,
+ BMN,
+ BMNE,
+ BMO,
+ BMPZ,
+ BNAND,
+ BNOR,
+ BNOT,
+ BNTST,
+ BNXOR,
+ BOR,
+ BRK,
+ BSET,
+ BTST,
+ BTSTC,
+ BTSTS,
+ BXOR,
+ CMP(Size),
+ DADC,
+ DADD,
+ DEC(Size),
+ DIV(Size),
+ DIVU(Size),
+ DIVX(Size),
+ DSBB,
+ DSUB,
+ ENTER,
+ EXITD,
+ EXTS,
+ GE,
+ INC(Size),
+ INT,
+ INTO,
+ JEQ,
+ JGE,
+ JGEU,
+ JGT,
+ JGTU,
+ JLE,
+ JLEU,
+ JLT,
+ JLTU,
+ JMPI,
+ JMPS,
+ JMP(Size),
+ JN,
+ JNE,
+ JNO,
+ JO,
+ JPZ,
+ JSRI,
+ JSRS,
+ JSR_A,
+ JSR_W,
+ LDC,
+ LDCTX,
+ LDE,
+ LDIPL,
+ LT,
+ MOV(Size),
+ MOVA,
+ MOVHH,
+ MOVHL,
+ MOVLH,
+ MOVLL,
+ MUL,
+ MULU(Size),
+ NEG,
+ NO,
+ NOP,
+ NOT(Size),
+ OR(Size),
+ POP,
+ POPC,
+ POPM,
+ PUSH(Size),
+ PUSHA,
+ PUSHC,
+ PUSHM,
+ REIT,
+ RMPA(Size),
+ ROLC,
+ RORC,
+ ROT(Size),
+ RTS,
+ SBB(Size),
+ SHA(Size),
+ SHL(Size),
+ SMOVB(Size),
+ SMOVF(Size),
+ SSTR(Size),
+ STC,
+ STCTX,
+ STE,
+ STNZ,
+ STX,
+ STZ,
+ STZX,
+ SUB(Size),
+ TST(Size),
+ UND,
+ WAIT,
+ XCHG(Size),
+ XOR(Size),
+}
+
+impl fmt::Display for Opcode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Opcode::ABS => write!(f, "abs"),
+ Opcode::ADC(size) => write!(f, "adc.{}", size),
+ Opcode::ADCF(size) => write!(f, "adcf.{}", size),
+ Opcode::ADD(size) => write!(f, "add.{}", size),
+ Opcode::ADJNZ(size) => write!(f, "adjnz.{}", size),
+ Opcode::AND(size) => write!(f, "and.{}", size),
+ Opcode::BAND => write!(f, "band"),
+ Opcode::BCLR => write!(f, "bclr"),
+ Opcode::BMEQ => write!(f, "bmeq"),
+ Opcode::BMGEU => write!(f, "bmgeu"),
+ Opcode::BMGT => write!(f, "bmgt"),
+ Opcode::BMGTU => write!(f, "bmgtu"),
+ Opcode::BMLE => write!(f, "bmle"),
+ Opcode::BMLEU => write!(f, "bmleu"),
+ Opcode::BMLTU => write!(f, "bmltu"),
+ Opcode::BMLT => write!(f, "bmlt"),
+ Opcode::BMNO => write!(f, "bmno"),
+ Opcode::BMGE => write!(f, "bmge"),
+ Opcode::BMN => write!(f, "bmn"),
+ Opcode::BMNE => write!(f, "bmne"),
+ Opcode::BMO => write!(f, "bmo"),
+ Opcode::BMPZ => write!(f, "bmpz"),
+ Opcode::BNAND => write!(f, "bnand"),
+ Opcode::BNOR => write!(f, "bnor"),
+ Opcode::BNOT => write!(f, "bnot"),
+ Opcode::BNTST => write!(f, "bntst"),
+ Opcode::BNXOR => write!(f, "bnxor"),
+ Opcode::BOR => write!(f, "bor"),
+ Opcode::BRK => write!(f, "brk"),
+ Opcode::BSET => write!(f, "bset"),
+ Opcode::BTST => write!(f, "btst"),
+ Opcode::BTSTC => write!(f, "btstc"),
+ Opcode::BTSTS => write!(f, "btsts"),
+ Opcode::BXOR => write!(f, "bxor"),
+ Opcode::CMP(size) => write!(f, "cmp.{}", size),
+ Opcode::DADC => write!(f, "dadc"),
+ Opcode::DADD => write!(f, "dadd"),
+ Opcode::DEC(size) => write!(f, "dec.{}", size),
+ Opcode::DIV(size) => write!(f, "div.{}", size),
+ Opcode::DIVU(size) => write!(f, "divu.{}", size),
+ Opcode::DIVX(size) => write!(f, "divx.{}", size),
+ Opcode::DSBB => write!(f, "dsbb"),
+ Opcode::DSUB => write!(f, "dsub"),
+ Opcode::ENTER => write!(f, "enter"),
+ Opcode::EXITD => write!(f, "exitd"),
+ Opcode::EXTS => write!(f, "exts"),
+ Opcode::GE => write!(f, "ge"),
+ Opcode::INC(size) => write!(f, "inc.{}", size),
+ Opcode::INT => write!(f, "int"),
+ Opcode::INTO => write!(f, "into"),
+ Opcode::JEQ => write!(f, "jeq"),
+ Opcode::JGE => write!(f, "jge"),
+ Opcode::JGEU => write!(f, "jgeu"),
+ Opcode::JGT => write!(f, "jgt"),
+ Opcode::JGTU => write!(f, "jgtu"),
+ Opcode::JLE => write!(f, "jle"),
+ Opcode::JLEU => write!(f, "jleu"),
+ Opcode::JLT => write!(f, "jlt"),
+ Opcode::JLTU => write!(f, "jltu"),
+ Opcode::JMPI => write!(f, "jmpi"),
+ Opcode::JMPS => write!(f, "jmps"),
+ Opcode::JMP(size) => write!(f, "jmp.{}", size),
+ Opcode::JN => write!(f, "jn"),
+ Opcode::JNE => write!(f, "jne"),
+ Opcode::JNO => write!(f, "jno"),
+ Opcode::JO => write!(f, "jo"),
+ Opcode::JPZ => write!(f, "jpz"),
+ Opcode::JSRI => write!(f, "jsri"),
+ Opcode::JSRS => write!(f, "jsrs"),
+ Opcode::JSR_A => write!(f, "jsr_a"),
+ Opcode::JSR_W => write!(f, "jsr_w"),
+ Opcode::LDC => write!(f, "ldc"),
+ Opcode::LDCTX => write!(f, "ldctx"),
+ Opcode::LDE => write!(f, "lde"),
+ Opcode::LDIPL => write!(f, "ldipl"),
+ Opcode::LT => write!(f, "lt"),
+ Opcode::MOV(size) => write!(f, "mov.{}", size),
+ Opcode::MOVA => write!(f, "mova"),
+ Opcode::MOVHH => write!(f, "movhh"),
+ Opcode::MOVHL => write!(f, "movhl"),
+ Opcode::MOVLH => write!(f, "movlh"),
+ Opcode::MOVLL => write!(f, "movll"),
+ Opcode::MUL => write!(f, "mul"),
+ Opcode::MULU(size) => write!(f, "mulu.{}", size),
+ Opcode::NEG => write!(f, "neg"),
+ Opcode::NO => write!(f, "no"),
+ Opcode::NOP => write!(f, "nop"),
+ Opcode::NOT(size) => write!(f, "not.{}", size),
+ Opcode::OR(size) => write!(f, "or.{}", size),
+ Opcode::POP => write!(f, "pop"),
+ Opcode::POPC => write!(f, "popc"),
+ Opcode::POPM => write!(f, "popm"),
+ Opcode::PUSH(size) => write!(f, "push.{}", size),
+ Opcode::PUSHA => write!(f, "pusha"),
+ Opcode::PUSHC => write!(f, "pushc"),
+ Opcode::PUSHM => write!(f, "pushm"),
+ Opcode::REIT => write!(f, "reit"),
+ Opcode::RMPA(size) => write!(f, "rmpa.{}", size),
+ Opcode::ROLC => write!(f, "rolc"),
+ Opcode::RORC => write!(f, "rorc"),
+ Opcode::ROT(size) => write!(f, "rot.{}", size),
+ Opcode::RTS => write!(f, "rts"),
+ Opcode::SBB(size) => write!(f, "sbb.{}", size),
+ Opcode::SHA(size) => write!(f, "sha.{}", size),
+ Opcode::SHL(size) => write!(f, "shl.{}", size),
+ Opcode::SMOVB(size) => write!(f, "smovb.{}", size),
+ Opcode::SMOVF(size) => write!(f, "smovf.{}", size),
+ Opcode::SSTR(size) => write!(f, "sstr.{}", size),
+ Opcode::STC => write!(f, "stc"),
+ Opcode::STCTX => write!(f, "stctx"),
+ Opcode::STE => write!(f, "ste"),
+ Opcode::STNZ => write!(f, "stnz"),
+ Opcode::STX => write!(f, "stx"),
+ Opcode::STZ => write!(f, "stz"),
+ Opcode::STZX => write!(f, "stzx"),
+ Opcode::SUB(size) => write!(f, "sub.{}", size),
+ Opcode::TST(size) => write!(f, "tst.{}", size),
+ Opcode::UND => write!(f, "und"),
+ Opcode::WAIT => write!(f, "wait"),
+ Opcode::XCHG(size) => write!(f, "xchg.{}", size),
+ Opcode::XOR(size) => write!(f, "xor.{}", size),
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum Register {
+ R0L,
+ R0H,
+ R1L,
+ R1H,
+ R0,
+ R1,
+ R2,
+ R3,
+ R2R0,
+ R3R1,
+ A0,
+ A1,
+ A1A0,
+ FB,
+ SB,
+ SP,
+ INTBL,
+ INTBH,
+ FLG,
+ ISP,
+}
+
+impl fmt::Display for Register {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Register::*;
+ match self {
+ R0L => write!(f, "r0l"),
+ R0H => write!(f, "r0h"),
+ R1L => write!(f, "r1l"),
+ R1H => write!(f, "r1h"),
+ R0 => write!(f, "r0"),
+ R1 => write!(f, "r1"),
+ R2 => write!(f, "r2"),
+ R3 => write!(f, "r3"),
+ R2R0 => write!(f, "r2r0"),
+ R3R1 => write!(f, "r3r1"),
+ A0 => write!(f, "a0"),
+ A1 => write!(f, "a1"),
+ A1A0 => write!(f, "a1a0"),
+ FB => write!(f, "fb"),
+ SB => write!(f, "sb"),
+ SP => write!(f, "sp"),
+ INTBL => write!(f, "intbl"),
+ INTBH => write!(f, "intbh"),
+ FLG => write!(f, "flg"),
+ ISP => write!(f, "isp"),
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum Operand {
+ Register(Register),
+ RegisterBit(Register, u8),
+ ImmediateI16(i16),
+ ImmediateU16(u16),
+ RegDerefBit(Register, i16),
+ RegDeref(Register, i16),
+ AbsoluteBit(u16),
+ Absolute(u32),
+ Displacement(i16),
+ RegList(u8),
+}
+
+impl fmt::Display for Operand {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Operand::*;
+ match self {
+ Register(reg) => write!(f, "{}", reg),
+ ImmediateI16(imm) => {
+ if *imm == core::i16::MIN {
+ write!(f, "#-8000h")
+ } else if *imm < 0 {
+ write!(f, "#-{:02x}h", -*imm)
+ } else {
+ write!(f, "#{:02x}h", *imm)
+ }
+ },
+ ImmediateU16(imm) => write!(f, "#{:02x}h", imm),
+ RegDeref(reg, offset) => {
+ if *offset == core::i16::MIN {
+ write!(f, "#-8000h")
+ } else if *offset == 0 {
+ write!(f, "[{}]", reg)
+ } else if *offset < 0 {
+ write!(f, "-{}[{}]", -offset, reg)
+ } else {
+ write!(f, "{}[{}]", offset, reg)
+ }
+ }
+ Displacement(offset) => {
+ if *offset == core::i16::MIN {
+ write!(f, "$-32768")
+ } else if *offset < 0 {
+ write!(f, "$-{}", -*offset)
+ } else {
+ write!(f, "$+{}", *offset)
+ }
+ }
+ _ => Ok(())
+ }
+ }
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Debug, Copy, Clone, PartialEq)]
+enum OperandSpec {
+ Nothing,
+ Zero,
+ Imm4,
+ Imm8,
+ Imm82, // Imm8, part t2. used in STZX.
+ Imm16,
+ Label8,
+ Bit0,
+ Bit1,
+ Bit2,
+ Bit3,
+ Bit4,
+ Bit5,
+ Bit6,
+ Bit7,
+ R0L,
+ R0H,
+ R1L,
+ R1H,
+ R0,
+ R1,
+ R2,
+ R3,
+ R2R0,
+ R3R1,
+ A0,
+ A1,
+ A1A0,
+ Bit_R0,
+ Bit_R1,
+ Bit_R2,
+ Bit_R3,
+ Bit_A0,
+ Bit_A1,
+ Bit_Deref_A0,
+ Bit_Deref_A1,
+ Bit_Disp8_A0,
+ Bit_Disp8_A1,
+ Bit_Disp8_SB,
+ Bit_Disp8_FB,
+ Bit_Disp16_A0,
+ Bit_Disp16_A1,
+ Bit_Disp16_SB,
+ Bit_Abs16,
+ Disp8_A0,
+ Disp8_A1,
+ Disp8_SB,
+ Disp8_FB,
+ Disp8_SP,
+ Disp16_A0,
+ Disp16_A1,
+ Disp16_SB,
+ Disp20_A0,
+ Disp20_A1,
+ Deref_A0,
+ Deref_A1,
+ Deref_A1A0,
+ Abs16,
+ Disp2_8_A0,
+ Disp2_8_A1,
+ Disp2_8_SB,
+ Disp2_8_FB,
+ Disp2_16_A0,
+ Disp2_16_A1,
+ Disp2_16_SB,
+ Abs2_16,
+ Abs20,
+ Disp8,
+ Disp16,
+ RegList,
+ INTBL,
+ INTBH,
+ FLG,
+ ISP,
+ SP,
+ SB,
+ FB,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum Size {
+ B, // 0 in almost(?) all places a size bit is present
+ W, // 1 in almost(?) all places a size bit is present
+ L, // for sha.l/shl.l
+ S, // for jmp.s, three bits.
+ A, // for jmp.a, twenty bit absolute.
+}
+
+impl fmt::Display for Size {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Size::B => write!(f, "b"),
+ Size::W => write!(f, "w"),
+ Size::L => write!(f, "l"),
+ Size::S => write!(f, "s"),
+ Size::A => write!(f, "a"),
+ }
+ }
+}
+
+impl Size {
+ fn as_bytes(&self) -> u8 {
+ match self {
+ Size::B => 1,
+ Size::W => 2,
+ Size::L => { panic!("should not have to get number of bytes for L"); },
+ Size::S => { panic!("should not have to get number of bytes for S"); },
+ Size::A => { panic!("should not have to get number of bytes for A"); },
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct Instruction {
+ pub opcode: Opcode,
+ operands: [OperandSpec; 3],
+ // we need both `dispabs` and `imm_wide` to handle the widest instructions we can see:
+ // LDE or STE with 20-bit displacement and 16-bit displacement.
+ // either an Imm16, an Imm8, or Imm82:Imm8. mutually exclusive with 20-bit disp/abs, so put those here too.
+ //
+ // Imm4 will sometimes cohabitate with two displacements (ADJNZ with displacement dest and
+ // label), so cram the label byte into here as well. accessed as `OperandSpec::Label8`.
+ imm_wide: u32,
+ dispabs: u16, // 8 or 16-bit displacement or absolute address
+ length: u8,
+}
+
+impl Instruction {
+ pub fn operand(&self, idx: u8) -> Option<Operand> {
+ use OperandSpec::*;
+ let operand = match self.operands[idx as usize] {
+ Nothing => { return None; },
+ Zero => Operand::ImmediateU16(0),
+ Imm4 => Operand::ImmediateI16(self.imm_wide as u8 as i8 as i16),
+ Imm8 => Operand::ImmediateI16(self.imm_wide as u8 as i8 as i16),
+ Imm16 => Operand::ImmediateI16(self.imm_wide as i16),
+ // Imm8 part 2 is used in STZX, and is the second byte of imm_wide
+ Imm82 |
+ // and Label8 is used in ADJNZ, also stored in the second byte of inn_wide
+ Label8 => Operand::ImmediateI16((self.imm_wide as i16) >> 8),
+ Bit0 => Operand::ImmediateU16(0),
+ Bit1 => Operand::ImmediateU16(1),
+ Bit2 => Operand::ImmediateU16(2),
+ Bit3 => Operand::ImmediateU16(3),
+ Bit4 => Operand::ImmediateU16(4),
+ Bit5 => Operand::ImmediateU16(5),
+ Bit6 => Operand::ImmediateU16(6),
+ Bit7 => Operand::ImmediateU16(7),
+ R0L => Operand::Register(Register::R0L),
+ R0H => Operand::Register(Register::R0H),
+ R1L => Operand::Register(Register::R1L),
+ R1H => Operand::Register(Register::R1H),
+ R0 => Operand::Register(Register::R0),
+ R1 => Operand::Register(Register::R1),
+ R2 => Operand::Register(Register::R2),
+ R3 => Operand::Register(Register::R3),
+ R2R0 => Operand::Register(Register::R2R0),
+ R3R1 => Operand::Register(Register::R3R1),
+ A0 => Operand::Register(Register::A0),
+ A1 => Operand::Register(Register::A1),
+ A1A0 => Operand::Register(Register::A1A0),
+ Bit_R0 => Operand::RegisterBit(Register::R0, self.dispabs as u8),
+ Bit_R1 => Operand::RegisterBit(Register::R1, self.dispabs as u8),
+ Bit_R2 => Operand::RegisterBit(Register::R2, self.dispabs as u8),
+ Bit_R3 => Operand::RegisterBit(Register::R3, self.dispabs as u8),
+ Bit_A0 => Operand::RegisterBit(Register::A0, self.dispabs as u8),
+ Bit_A1 => Operand::RegisterBit(Register::A1, self.dispabs as u8),
+ Bit_Deref_A0 => Operand::RegDerefBit(Register::A0, 0),
+ Bit_Deref_A1 => Operand::RegDerefBit(Register::A1, 0),
+ Bit_Disp8_A0 => Operand::RegDerefBit(Register::A0, self.dispabs as i16),
+ Bit_Disp8_A1 => Operand::RegDerefBit(Register::A1, self.dispabs as i16),
+ Bit_Disp8_SB => Operand::RegDerefBit(Register::SB, self.dispabs as i16),
+ Bit_Disp8_FB => Operand::RegDerefBit(Register::FB, self.dispabs as i16),
+ Bit_Disp16_A0 => Operand::RegDerefBit(Register::A0, self.dispabs as i16),
+ Bit_Disp16_A1 => Operand::RegDerefBit(Register::A1, self.dispabs as i16),
+ Bit_Disp16_SB => Operand::RegDerefBit(Register::SB, self.dispabs as i16),
+ Bit_Abs16 => Operand::AbsoluteBit(self.dispabs),
+ Disp8_A0 => Operand::RegDeref(Register::A0, self.dispabs as i8 as i16),
+ Disp8_A1 => Operand::RegDeref(Register::A1, self.dispabs as i8 as i16),
+ Disp8_SB => Operand::RegDeref(Register::SB, self.dispabs as i8 as i16),
+ Disp8_FB => Operand::RegDeref(Register::FB, self.dispabs as i8 as i16),
+ Disp8_SP => Operand::RegDeref(Register::SP, self.dispabs as i8 as i16),
+ Disp16_A0 => Operand::RegDeref(Register::A0, self.dispabs as i16),
+ Disp16_A1 => Operand::RegDeref(Register::A1, self.dispabs as i16),
+ Disp16_SB => Operand::RegDeref(Register::SB, self.dispabs as i16),
+ Disp20_A0 => Operand::RegDeref(Register::A0, self.imm_wide as i16),
+ Disp20_A1 => Operand::RegDeref(Register::A1, self.imm_wide as i16),
+ Deref_A0 => Operand::RegDeref(Register::A0, self.dispabs as i16),
+ Deref_A1 => Operand::RegDeref(Register::A1, self.dispabs as i16),
+ Deref_A1A0 => Operand::RegDeref(Register::A1A0, self.dispabs as i16),
+ Abs16 => Operand::Absolute(self.dispabs as u32),
+ Disp2_8_A0 => Operand::RegDeref(Register::A0, self.imm_wide as i8 as i16),
+ Disp2_8_A1 => Operand::RegDeref(Register::A1, self.imm_wide as i8 as i16),
+ Disp2_8_SB => Operand::RegDeref(Register::SB, self.imm_wide as i8 as i16),
+ Disp2_8_FB => Operand::RegDeref(Register::FB, self.imm_wide as i8 as i16),
+ Disp2_16_A0 => Operand::RegDeref(Register::A0, self.imm_wide as i16),
+ Disp2_16_A1 => Operand::RegDeref(Register::A1, self.imm_wide as i16),
+ Disp2_16_SB => Operand::RegDeref(Register::SB, self.imm_wide as i16),
+ Abs2_16 => Operand::Absolute(self.imm_wide),
+ Abs20 => Operand::Absolute(self.imm_wide),
+ Disp8 => Operand::Displacement(self.dispabs as u8 as i8 as i16),
+ Disp16 => Operand::Displacement(self.dispabs as i16),
+ RegList => Operand::RegList(self.imm_wide as u8),
+ INTBL => Operand::Register(Register::INTBL),
+ INTBH => Operand::Register(Register::INTBH),
+ FLG => Operand::Register(Register::FLG),
+ ISP => Operand::Register(Register::ISP),
+ SP => Operand::Register(Register::SP),
+ SB => Operand::Register(Register::SB),
+ FB => Operand::Register(Register::FB),
+ };
+ Some(operand)
+ }
+}
+
+#[cfg(feature="use-serde")]
+#[derive(Debug, Serialize, Deserialize)]
+pub struct M16C;
+
+#[cfg(not(feature="use-serde"))]
+#[derive(Debug)]
+pub struct M16C;
+
+impl Arch for M16C {
+ type Address = u32;
+ type Instruction = Instruction;
+ type DecodeError = DecodeError;
+ type Decoder = InstDecoder;
+ type Operand = Operand;
+}
+
+impl fmt::Display for Instruction {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.opcode)?;
+ match self.operand(0) {
+ None => return Ok(()),
+ Some(op) => { write!(f, " {}", op)?; }
+ }
+ match self.operand(1) {
+ None => return Ok(()),
+ Some(op) => { write!(f, ", {}", op)?; }
+ }
+ Ok(())
+ }
+}
+
+impl LengthedInstruction for Instruction {
+ type Unit = <M16C as Arch>::Address;
+
+ fn min_size() -> Self::Unit {
+ 1
+ }
+ fn len(&self) -> Self::Unit {
+ self.length as u32
+ }
+}
+
+impl Default for Instruction {
+ fn default() -> Self {
+ Instruction {
+ opcode: Opcode::NOP,
+ operands: [OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing],
+ imm_wide: 0,
+ dispabs: 0,
+ length: 0
+ }
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub enum DecodeError {
+ ExhaustedInput,
+ InvalidOpcode,
+ InvalidOperand,
+}
+
+impl fmt::Display for DecodeError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ DecodeError::ExhaustedInput => write!(f, "exhausted input"),
+ DecodeError::InvalidOpcode => write!(f, "invalid opcode"),
+ DecodeError::InvalidOperand => write!(f, "invalid operand"),
+ }
+ }
+}
+
+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 }
+}
+
+impl yaxpeax_arch::Instruction for Instruction {
+ // currently only accept instructions that are well-defined.
+ fn well_defined(&self) -> bool { true }
+}
+
+#[derive(Default, Debug)]
+pub struct InstDecoder {}
+
+impl fmt::Display for InstDecoder {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "m16c")
+ }
+}
+
+impl Decoder<Instruction> for InstDecoder {
+ type Error = DecodeError;
+
+ fn decode_into<T: IntoIterator<Item=u8>>(&self, inst: &mut Instruction, bytes: T) -> Result<(), DecodeError> {
+ let mut bytes_iter = bytes.into_iter();
+ decode(self, inst, &mut bytes_iter)
+ }
+}
+
+enum OperandCategory {
+ ADJNZ,
+ Op74,
+ Op76,
+ Op78,
+ Op7A,
+ Op7B,
+ Op7C,
+ Op7D,
+ Op7E,
+ OpEB,
+ SrcDestRegOrDeref,
+ Imm4Dest,
+ DispOpcodeLow3,
+}
+
+enum OperandInterpretation {
+ Just([OperandSpec; 3]),
+ Reinterpret(OperandCategory)
+}
+
+fn decode<T: Iterator<Item=u8>>(_decoder: &InstDecoder, inst: &mut Instruction, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ // IF there is a size bit, it is the low bit of the first byte.
+ let size = if byte & 1 == 0 {
+ Size::B
+ } else {
+ Size::W
+ };
+ use OperandInterpretation::*;
+ let (opcode, interpretation) = match byte {
+ 0b0000_0000 => (Opcode::BRK, Just([OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0000_0001 => (Opcode::MOV(Size::B), Just([OperandSpec::R0L, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0000_0010 => (Opcode::MOV(Size::B), Just([OperandSpec::R0L, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b0000_0011 => (Opcode::MOV(Size::B), Just([OperandSpec::R0L, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b0000_0100 => (Opcode::NOP, Just([OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0000_0101 => (Opcode::MOV(Size::B), Just([OperandSpec::R0H, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0000_0110 => (Opcode::MOV(Size::B), Just([OperandSpec::R0H, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b0000_0111 => (Opcode::MOV(Size::B), Just([OperandSpec::R0H, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b0000_1000 |
+ 0b0000_1001 |
+ 0b0000_1010 |
+ 0b0000_1011 |
+ 0b0000_1100 |
+ 0b0000_1101 |
+ 0b0000_1110 |
+ 0b0000_1111 => {
+ return Err(DecodeError::InvalidOperand);
+ },
+ 0b0001_0000 => (Opcode::AND(Size::B), Just([OperandSpec::R0H, OperandSpec::R0L, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0001_0001 => (Opcode::AND(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0001_0010 => (Opcode::AND(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0001_0011 => (Opcode::AND(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0001_0100 => (Opcode::AND(Size::B), Just([OperandSpec::R0L, OperandSpec::R0H, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0001_0101 => (Opcode::AND(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0001_0110 => (Opcode::AND(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0001_0111 => (Opcode::AND(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0001_1000 => (Opcode::OR(Size::B), Just([OperandSpec::R0H, OperandSpec::R0L, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0001_1001 => (Opcode::OR(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0001_1010 => (Opcode::OR(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0001_1011 => (Opcode::OR(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0001_1100 => (Opcode::OR(Size::B), Just([OperandSpec::R0L, OperandSpec::R0H, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0001_1101 => (Opcode::OR(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0001_1110 => (Opcode::OR(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0001_1111 => (Opcode::OR(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0010_0000 => (Opcode::ADD(Size::B), Just([OperandSpec::R0H, OperandSpec::R0L, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0010_0001 => (Opcode::ADD(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0010_0010 => (Opcode::ADD(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0010_0011 => (Opcode::ADD(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0010_0100 => (Opcode::ADD(Size::B), Just([OperandSpec::R0L, OperandSpec::R0H, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0010_0101 => (Opcode::ADD(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0010_0110 => (Opcode::ADD(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0010_0111 => (Opcode::ADD(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0010_1000 => (Opcode::SUB(Size::B), Just([OperandSpec::R0H, OperandSpec::R0L, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0010_1001 => (Opcode::SUB(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0010_1010 => (Opcode::SUB(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0010_1011 => (Opcode::SUB(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0010_1100 => (Opcode::SUB(Size::B), Just([OperandSpec::R0L, OperandSpec::R0H, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0010_1101 => (Opcode::SUB(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0010_1110 => (Opcode::SUB(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0010_1111 => (Opcode::SUB(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0011_0000 => (Opcode::MOV(Size::B), Just([OperandSpec::R0H, OperandSpec::A0, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0011_0001 => (Opcode::MOV(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::A0, OperandSpec::Nothing])),
+ 0b0011_0010 => (Opcode::MOV(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::A0, OperandSpec::Nothing])),
+ 0b0011_0011 => (Opcode::MOV(Size::B), Just([OperandSpec::Abs16, OperandSpec::A0, OperandSpec::Nothing])),
+ 0b0011_0100 => (Opcode::MOV(Size::B), Just([OperandSpec::R0L, OperandSpec::A1, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0011_0101 => (Opcode::MOV(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::A1, OperandSpec::Nothing])),
+ 0b0011_0110 => (Opcode::MOV(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::A1, OperandSpec::Nothing])),
+ 0b0011_0111 => (Opcode::MOV(Size::B), Just([OperandSpec::Abs16, OperandSpec::A1, OperandSpec::Nothing])),
+ 0b0011_1000 => (Opcode::CMP(Size::B), Just([OperandSpec::R0H, OperandSpec::R0L, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0011_1001 => (Opcode::CMP(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0011_1010 => (Opcode::CMP(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0011_1011 => (Opcode::CMP(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b0011_1100 => (Opcode::CMP(Size::B), Just([OperandSpec::R0L, OperandSpec::R0H, OperandSpec::Nothing])), // src is written R0L/R0H, what picks the register?
+ 0b0011_1101 => (Opcode::CMP(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0011_1110 => (Opcode::CMP(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0011_1111 => (Opcode::CMP(Size::B), Just([OperandSpec::Abs16, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b0100_0000 => (Opcode::BCLR, Just([OperandSpec::Bit0, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_0001 => (Opcode::BCLR, Just([OperandSpec::Bit1, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_0010 => (Opcode::BCLR, Just([OperandSpec::Bit2, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_0011 => (Opcode::BCLR, Just([OperandSpec::Bit3, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_0100 => (Opcode::BCLR, Just([OperandSpec::Bit4, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_0101 => (Opcode::BCLR, Just([OperandSpec::Bit5, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_0110 => (Opcode::BCLR, Just([OperandSpec::Bit6, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_0111 => (Opcode::BCLR, Just([OperandSpec::Bit7, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1000 => (Opcode::BSET, Just([OperandSpec::Bit0, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1001 => (Opcode::BSET, Just([OperandSpec::Bit1, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1010 => (Opcode::BSET, Just([OperandSpec::Bit2, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1011 => (Opcode::BSET, Just([OperandSpec::Bit3, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1100 => (Opcode::BSET, Just([OperandSpec::Bit4, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1101 => (Opcode::BSET, Just([OperandSpec::Bit5, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1110 => (Opcode::BSET, Just([OperandSpec::Bit6, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0100_1111 => (Opcode::BSET, Just([OperandSpec::Bit7, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0000 => (Opcode::BNOT, Just([OperandSpec::Bit0, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0001 => (Opcode::BNOT, Just([OperandSpec::Bit1, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0010 => (Opcode::BNOT, Just([OperandSpec::Bit2, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0011 => (Opcode::BNOT, Just([OperandSpec::Bit3, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0100 => (Opcode::BNOT, Just([OperandSpec::Bit4, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0101 => (Opcode::BNOT, Just([OperandSpec::Bit5, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0110 => (Opcode::BNOT, Just([OperandSpec::Bit6, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_0111 => (Opcode::BNOT, Just([OperandSpec::Bit7, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1000 => (Opcode::BTST, Just([OperandSpec::Bit0, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1001 => (Opcode::BTST, Just([OperandSpec::Bit1, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1010 => (Opcode::BTST, Just([OperandSpec::Bit2, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1011 => (Opcode::BTST, Just([OperandSpec::Bit3, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1100 => (Opcode::BTST, Just([OperandSpec::Bit4, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1101 => (Opcode::BTST, Just([OperandSpec::Bit5, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1110 => (Opcode::BTST, Just([OperandSpec::Bit6, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0101_1111 => (Opcode::BTST, Just([OperandSpec::Bit7, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b0110_0000 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_0001 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_0010 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_0011 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_0100 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_0101 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_0110 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_0111 => (Opcode::JMP(Size::S), Reinterpret(OperandCategory::DispOpcodeLow3)),
+ 0b0110_1000 => (Opcode::JGEU, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0110_1001 => (Opcode::JGTU, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0110_1010 => (Opcode::JEQ, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0110_1011 => (Opcode::JN, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0110_1100 => (Opcode::JLTU, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0110_1101 => (Opcode::JLEU, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0110_1110 => (Opcode::JNE, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0110_1111 => (Opcode::JPZ, Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b0111_0000 => (Opcode::MULU(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b0111_0001 => (Opcode::MULU(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b0111_0010 => (Opcode::MOV(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b0111_0011 => (Opcode::MOV(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b0111_0100 => (Opcode::NOP, Reinterpret(OperandCategory::Op74)),
+ 0b0111_0101 => (Opcode::NOP, Reinterpret(OperandCategory::Op74)),
+ 0b0111_0110 => (Opcode::NOP, Reinterpret(OperandCategory::Op76)),
+ 0b0111_0111 => (Opcode::NOP, Reinterpret(OperandCategory::Op76)),
+ 0b0111_1000 => (Opcode::NOP, Reinterpret(OperandCategory::Op78)),
+ 0b0111_1001 => (Opcode::NOP, Reinterpret(OperandCategory::Op78)),
+ 0b0111_1010 => (Opcode::NOP, Reinterpret(OperandCategory::Op7A)),
+ 0b0111_1011 => (Opcode::NOP, Reinterpret(OperandCategory::Op7B)),
+ 0b0111_1100 => (Opcode::NOP, Reinterpret(OperandCategory::Op7C)),
+ 0b0111_1101 => (Opcode::NOP, Reinterpret(OperandCategory::Op7D)),
+ 0b0111_1110 => (Opcode::NOP, Reinterpret(OperandCategory::Op7E)),
+ 0b0111_1111 => { return Err(DecodeError::InvalidOperand); },
+ 0b1000_0000 => (Opcode::TST(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1000_0001 => (Opcode::TST(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1000_0010 => (Opcode::PUSH(Size::B), Just([OperandSpec::R0L, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1000_0011 => (Opcode::ADD(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1000_0100 => (Opcode::ADD(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1000_0101 => (Opcode::ADD(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1000_0110 => (Opcode::ADD(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1000_0111 => (Opcode::ADD(Size::B), Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1000_1000 => (Opcode::XOR(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1000_1001 => (Opcode::XOR(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1000_1010 => (Opcode::PUSH(Size::B), Just([OperandSpec::R0H, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1000_1011 => (Opcode::SUB(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1000_1100 => (Opcode::SUB(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1000_1101 => (Opcode::SUB(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1000_1110 => (Opcode::SUB(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1000_1111 => (Opcode::SUB(Size::B), Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1001_0000 => (Opcode::AND(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1001_0001 => (Opcode::AND(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1001_0010 => (Opcode::POP, Just([OperandSpec::R0L, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1001_0011 => (Opcode::AND(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1001_0100 => (Opcode::AND(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1001_0101 => (Opcode::AND(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1001_0110 => (Opcode::AND(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1001_0111 => (Opcode::AND(Size::B), Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1001_1000 => (Opcode::OR(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1001_1001 => (Opcode::OR(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1001_1010 => (Opcode::POP, Just([OperandSpec::R0H, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1001_1011 => (Opcode::OR(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1001_1100 => (Opcode::OR(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1001_1101 => (Opcode::OR(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1001_1110 => (Opcode::OR(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1001_1111 => (Opcode::OR(Size::B), Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1010_0000 => (Opcode::ADD(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1010_0001 => (Opcode::ADD(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1010_0010 => (Opcode::MOV(Size::W), Just([OperandSpec::Imm16, OperandSpec::A0, OperandSpec::Nothing])),
+ 0b1010_0011 => (Opcode::INC(Size::B), Just([OperandSpec::R0H, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_0100 => (Opcode::INC(Size::B), Just([OperandSpec::R0L, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_0101 => (Opcode::INC(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_0110 => (Opcode::INC(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_0111 => (Opcode::INC(Size::B), Just([OperandSpec::Abs16, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_1000 => (Opcode::SUB(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1010_1001 => (Opcode::SUB(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1010_1010 => (Opcode::MOV(Size::W), Just([OperandSpec::Imm16, OperandSpec::A1, OperandSpec::Nothing])),
+ 0b1010_1011 => (Opcode::DEC(Size::B), Just([OperandSpec::R0H, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_1100 => (Opcode::DEC(Size::B), Just([OperandSpec::R0L, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_1101 => (Opcode::DEC(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_1110 => (Opcode::DEC(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1010_1111 => (Opcode::DEC(Size::B), Just([OperandSpec::Abs16, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1011_0000 => (Opcode::ADC(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1011_0001 => (Opcode::ADC(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1011_0010 => (Opcode::INC(Size::W), Just([OperandSpec::A0, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1011_0011 => (Opcode::MOV(Size::B), Just([OperandSpec::Zero, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1011_0100 => (Opcode::MOV(Size::B), Just([OperandSpec::Zero, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1011_0101 => (Opcode::MOV(Size::B), Just([OperandSpec::Zero, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1011_0110 => (Opcode::MOV(Size::B), Just([OperandSpec::Zero, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1011_0111 => (Opcode::MOV(Size::B), Just([OperandSpec::Zero, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1011_1000 => (Opcode::SBB(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1011_1001 => (Opcode::SBB(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1011_1010 => (Opcode::INC(Size::W), Just([OperandSpec::A1, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1011_1011 => (Opcode::NOT(Size::B), Just([OperandSpec::R0H, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1011_1100 => (Opcode::NOT(Size::B), Just([OperandSpec::R0L, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1011_1101 => (Opcode::NOT(Size::B), Just([OperandSpec::Disp8_SB, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1011_1110 => (Opcode::NOT(Size::B), Just([OperandSpec::Disp8_FB, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1011_1111 => (Opcode::NOT(Size::B), Just([OperandSpec::Abs16, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1100_0000 => (Opcode::CMP(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1100_0001 => (Opcode::CMP(size), Reinterpret(OperandCategory::SrcDestRegOrDeref)),
+ 0b1100_0010 => (Opcode::PUSH(Size::W), Just([OperandSpec::A0, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1100_0011 => (Opcode::MOV(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1100_0100 => (Opcode::MOV(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1100_0101 => (Opcode::MOV(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1100_0110 => (Opcode::MOV(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1100_0111 => (Opcode::MOV(Size::B), Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1100_1000 => (Opcode::ADD(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1100_1001 => (Opcode::ADD(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1100_1010 => (Opcode::PUSH(Size::W), Just([OperandSpec::A1, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1100_1011 => (Opcode::STZ, Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1100_1100 => (Opcode::STZ, Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1100_1101 => (Opcode::STZ, Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1100_1110 => (Opcode::STX, Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1100_1111 => (Opcode::STZ, Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1101_0000 => (Opcode::CMP(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1101_0001 => (Opcode::CMP(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1101_0010 => (Opcode::POP, Just([OperandSpec::A0, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1101_0011 => (Opcode::STNZ, Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1101_0100 => (Opcode::STNZ, Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1101_0101 => (Opcode::STNZ, Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1101_0110 => (Opcode::STNZ, Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1101_0111 => (Opcode::STNZ, Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1101_1000 => (Opcode::MOV(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1101_1001 => (Opcode::MOV(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1101_1010 => (Opcode::POP, Just([OperandSpec::A1, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1101_1011 => (Opcode::STZX, Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Imm82])),
+ 0b1101_1100 => (Opcode::STZX, Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Imm82])),
+ 0b1101_1101 => (Opcode::STZX, Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Imm82])),
+ 0b1101_1110 => (Opcode::STZX, Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Imm82])),
+ 0b1101_1111 => (Opcode::STZX, Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Imm82])),
+ 0b1110_0000 => (Opcode::ROT(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1110_0001 => (Opcode::ROT(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1110_0010 => (Opcode::MOV(Size::B), Just([OperandSpec::Imm8, OperandSpec::A0, OperandSpec::Nothing])),
+ 0b1110_0011 => (Opcode::CMP(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0H, OperandSpec::Nothing])),
+ 0b1110_0100 => (Opcode::CMP(Size::B), Just([OperandSpec::Imm8, OperandSpec::R0L, OperandSpec::Nothing])),
+ 0b1110_0101 => (Opcode::CMP(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_SB, OperandSpec::Nothing])),
+ 0b1110_0110 => (Opcode::CMP(Size::B), Just([OperandSpec::Imm8, OperandSpec::Disp8_FB, OperandSpec::Nothing])),
+ 0b1110_0111 => (Opcode::CMP(Size::B), Just([OperandSpec::Imm8, OperandSpec::Abs16, OperandSpec::Nothing])),
+ 0b1110_1000 => (Opcode::SHA(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1110_1001 => (Opcode::SHA(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1110_1010 => (Opcode::MOV(Size::B), Just([OperandSpec::Imm8, OperandSpec::A1, OperandSpec::Nothing])),
+ 0b1110_1011 => (Opcode::NOP, Reinterpret(OperandCategory::OpEB)), // Opcode will be discarded
+ 0b1110_1100 => (Opcode::PUSHM, Just([OperandSpec::RegList, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1110_1101 => (Opcode::POPM, Just([OperandSpec::RegList, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1110_1110 => (Opcode::JMPS, Just([OperandSpec::Imm8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1110_1111 => (Opcode::JSRS, Just([OperandSpec::Imm8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_0000 => (Opcode::SHA(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1111_0001 => (Opcode::SHA(size), Reinterpret(OperandCategory::Imm4Dest)),
+ 0b1111_0010 => (Opcode::DEC(Size::W), Just([OperandSpec::A0, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_0011 => (Opcode::RTS, Just([OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_0100 => (Opcode::JMP(Size::W), Just([OperandSpec::Disp16, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_0101 => (Opcode::JSR_W, Just([OperandSpec::Disp16, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_0110 => (Opcode::INTO, Just([OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_0111 => { return Err(DecodeError::InvalidOperand); },
+ 0b1111_1000 => (Opcode::ADJNZ(size), Reinterpret(OperandCategory::ADJNZ)),
+ 0b1111_1001 => (Opcode::ADJNZ(size), Reinterpret(OperandCategory::ADJNZ)),
+ 0b1111_1010 => (Opcode::DEC(Size::W), Just([OperandSpec::A1, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_1011 => (Opcode::REIT, Just([OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_1100 => (Opcode::JMP(Size::A), Just([OperandSpec::Abs20, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_1101 => (Opcode::JSR_A, Just([OperandSpec::Abs20, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_1110 => (Opcode::JMP(Size::B), Just([OperandSpec::Disp8, OperandSpec::Nothing, OperandSpec::Nothing])),
+ 0b1111_1111 => (Opcode::UND, Just([OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing])),
+ };
+ inst.opcode = opcode;
+ match interpretation {
+ Just(operands) => {
+ inst.operands = operands;
+
+ // in the base opcode map, if an immediate is specified it comes before bytes for
+ // displacement/absolute address that may be part of a destination.
+ //
+ // additionally, Imm8 or Imm16 is the first operand, if present. so look for that
+ // first.
+ if let OperandSpec::Imm8 = inst.operands[0] {
+ inst.imm_wide = read_imm(bytes, 1)? as u32;
+ inst.length += 1;
+ }
+
+ // now, read bytes for remaining operands, EXCEPT STZX which we read most but not all
+ // of. it specifies a second imm8 after optional disp/abs
+ for op in inst.operands.iter() {
+ use OperandSpec::*;
+ match op {
+ RegList => {
+ inst.imm_wide = read_imm(bytes, 1)? as u32;
+ inst.length += 1;
+ },
+ Disp8 |
+ Disp8_FB |
+ Disp8_SB => {
+ // have to sign extend in case these displacements are negative
+ inst.dispabs = read_imm(bytes, 1)? as i8 as i16 as u16;
+ inst.length += 1;
+ }
+ Disp16 |
+ Abs16 => {
+ inst.dispabs = read_imm(bytes, 2)? as u16;
+ inst.length += 2;
+ }
+ Abs20 => {
+ inst.dispabs = read_imm(bytes, 3)? as u16;
+ inst.length += 3;
+ }
+ Disp8_A0 |
+ Disp8_A1 |
+ Disp8_SP |
+ Disp16_A0 |
+ Disp16_A1 |
+ Disp16_SB |
+ Disp20_A0 |
+ Disp20_A1 |
+ Deref_A0 |
+ Deref_A1 |
+ Deref_A1A0 |
+ Disp2_8_A0 |
+ Disp2_8_A1 |
+ Disp2_8_SB |
+ Disp2_8_FB |
+ Disp2_16_A0 |
+ Disp2_16_A1 |
+ Disp2_16_SB |
+ Abs2_16 |
+ Bit_Disp8_A0 |
+ Bit_Disp8_A1 |
+ Bit_Disp8_SB |
+ Bit_Disp8_FB |
+ Bit_Disp16_A0 |
+ Bit_Disp16_A1 |
+ Bit_Disp16_SB |
+ Bit_Abs16 => {
+ panic!("unexpected operand spec for Just set of operands: {:?}", op);
+ }
+ // and other operands? yeah just ignore em
+ _ => {}
+ }
+ }
+
+ // now for STZX, really,...
+ if let OperandSpec::Imm82 = inst.operands[2] {
+ inst.imm_wide |= (bytes.next().ok_or(DecodeError::ExhaustedInput)? as u32) << 8;
+ inst.length += 1;
+ }
+
+ return Ok(());
+ },
+ Reinterpret(OperandCategory::Op74) => {
+ return decode_op74(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Op76) => {
+ return decode_op76(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Op78) => {
+ return decode_op78(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Op7A) => {
+ return decode_op7A(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Op7B) => {
+ return decode_op7B(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Op7C) => {
+ return decode_op7C(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Op7D) => {
+ return decode_op7D(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Op7E) => {
+ return decode_op7E(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::OpEB) => {
+ return decode_opEB(inst, size, bytes);
+ },
+ Reinterpret(OperandCategory::Imm4Dest) => {
+ let operands = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ inst.imm_wide = (operands as i8 >> 4) as i32 as u32;
+ inst.operands[0] = OperandSpec::Imm4;
+ inst.operands[1] = Operand_RegDerefDispAbs(operands & 0b1111, size, inst, bytes)?;
+ inst.operands[2] = OperandSpec::Nothing;
+ }
+ Reinterpret(OperandCategory::DispOpcodeLow3) => {
+ inst.dispabs = (byte & 0b111) as u16;
+ inst.operands[0] = OperandSpec::Disp8;
+ inst.operands[1] = OperandSpec::Nothing;
+ inst.operands[2] = OperandSpec::Nothing;
+ }
+ Reinterpret(OperandCategory::ADJNZ) => {
+ // ADJNZ has operands like any other [IMM4|DEST] operand, but takes an additional byte
+ // for the destination label.
+ let operands = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ inst.imm_wide = ((operands as i8) >> 4) as u32;
+ inst.operands[0] = OperandSpec::Imm4;
+ inst.operands[1] = Operand_RegDerefDispAbs(operands & 0b1111, size, inst, bytes)?;
+ inst.operands[2] = OperandSpec::Label8;
+ }
+ Reinterpret(OperandCategory::SrcDestRegOrDeref) => {
+ // these instructions can read two disp8/disp16, so the first one can be handled
+ // normally, but the second will need some fixing up...
+ let operands = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ inst.operands[0] = Operand_RegDerefDispAbs(operands >> 4, size, inst, bytes)?;
+ inst.operands[1] = Operand_second_RegDerefDispAbs(operands & 0b1111, size, inst, bytes)?;
+ inst.operands[2] = OperandSpec::Nothing;
+ }
+ }
+ Ok(())
+}
+
+fn decode_op74<T: Iterator<Item=u8>>(inst: &mut Instruction, size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ match byte >> 4 {
+ 0b0000 => {
+ inst.opcode = Opcode::STE;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Abs20;
+ inst.imm_wide = read_imm(bytes, 3)? & 0x0f_ff_ff;
+ inst.length += 3;
+ }
+ 0b0001 => {
+ inst.opcode = Opcode::STE;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Disp20_A0;
+ inst.imm_wide = read_imm(bytes, 3)? & 0x0f_ff_ff;
+ inst.length += 3;
+ }
+ 0b0010 => {
+ inst.opcode = Opcode::STE;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Deref_A1A0;
+ }
+ 0b0011 => {
+ inst.opcode = Opcode::MOV(size);
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Disp8_SP;
+ }
+ 0b0100 => {
+ inst.opcode = Opcode::PUSH(size);
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0101 => {
+ inst.opcode = Opcode::NEG;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0110 => {
+ inst.opcode = Opcode::ROT(size);
+ if size == Size::W && byte & 0b1111 == 0b0001 {
+ // invalid dest, would be R1
+ return Err(DecodeError::InvalidOperand);
+ } else if size == Size::B && byte & 0b1111 == 0b0011 {
+ // invalid dest, would be R1H
+ return Err(DecodeError::InvalidOperand);
+ }
+
+ inst.operands[0] = OperandSpec::R1H;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ }
+ 0b0111 => {
+ inst.opcode = Opcode::NOT(size);
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1000 => {
+ inst.opcode = Opcode::LDE;
+ inst.operands[0] = OperandSpec::Abs20;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ // careful! abs20 comes after dest disp/abs
+ inst.imm_wide = read_imm(bytes, 3)? & 0x0f_ff_ff;
+ inst.length += 3;
+ }
+ 0b1001 => {
+ inst.opcode = Opcode::LDE;
+ inst.operands[0] = OperandSpec::Disp20_A0;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ // careful! disp20 comes after dest disp/abs
+ inst.imm_wide = read_imm(bytes, 3)? & 0x0f_ff_ff;
+ inst.length += 3;
+ }
+ 0b1010 => {
+ inst.opcode = Opcode::LDE;
+ inst.operands[0] = OperandSpec::Deref_A1A0;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ }
+ 0b1011 => {
+ inst.opcode = Opcode::MOV(size);
+ inst.operands[0] = OperandSpec::Disp8_SP;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ // careful! disp8 comes after dest disp/abs
+ inst.dispabs = read_imm(bytes, 1)? as u16;
+ inst.length += 1;
+ }
+ 0b1100 => {
+ inst.opcode = Opcode::MOV(size);
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ // careful! imm comes after dest disp/abs
+ inst.imm_wide = read_imm(bytes, size.as_bytes())? as u32;
+ inst.length += size.as_bytes();
+ }
+ 0b1101 => {
+ inst.opcode = Opcode::POP;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1110 => {
+ inst.opcode = Opcode::SHL(size);
+ if size == Size::W && byte & 0b1111 == 0b0001 {
+ // invalid dest, would be R1
+ return Err(DecodeError::InvalidOperand);
+ } else if size == Size::B && byte & 0b1111 == 0b0011 {
+ // invalid dest, would be R1H
+ return Err(DecodeError::InvalidOperand);
+ }
+
+ inst.operands[0] = OperandSpec::R1H;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ }
+ 0b1111 => {
+ inst.opcode = Opcode::SHA(size);
+ if size == Size::W && byte & 0b1111 == 0b0001 {
+ // invalid dest, would be R1
+ return Err(DecodeError::InvalidOperand);
+ } else if size == Size::B && byte & 0b1111 == 0b0011 {
+ // invalid dest, would be R1H
+ return Err(DecodeError::InvalidOperand);
+ }
+
+ inst.operands[0] = OperandSpec::R1H;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ }
+ _ => {
+ unreachable!("opcode selector is four bits");
+ }
+ }
+
+ Ok(())
+}
+
+fn decode_op76<T: Iterator<Item=u8>>(inst: &mut Instruction, size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ let opc_selector = byte >> 4;
+ if opc_selector < 0b1001 {
+ inst.opcode = [
+ Opcode::TST(size), Opcode::XOR(size), Opcode::AND(size),
+ Opcode::OR(size), Opcode::ADD(size), Opcode::SUB(size),
+ Opcode::ADC(size), Opcode::SBB(size), Opcode::CMP(size)
+ ][opc_selector as usize];
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ } else {
+ match opc_selector {
+ 0b1001 => {
+ inst.opcode = Opcode::DIVX(size);
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ 0b1010 => {
+ inst.opcode = Opcode::ROLC;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1011 => {
+ inst.opcode = Opcode::RORC;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1100 => {
+ inst.opcode = Opcode::DIVU(size);
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ 0b1101 => {
+ inst.opcode = Opcode::DIV(size);
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1110 => {
+ inst.opcode = Opcode::ADCF(size);
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ 0b1111 => {
+ inst.opcode = Opcode::ABS;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ _ => {
+ unreachable!("opcode selector is four bits");
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn decode_op78<T: Iterator<Item=u8>>(inst: &mut Instruction, size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ inst.opcode = Opcode::MUL;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte >> 4, size, inst, bytes)?;
+ let dest_code = byte & 0b1111;
+ match (size, dest_code) {
+ (Size::B, 0b0001) |
+ (Size::W, 0b0010) |
+ (_, 0b0011) |
+ (_, 0b0101) => {
+ return Err(DecodeError::InvalidOperand);
+ }
+ _ => {}
+ }
+ inst.operands[1] = Operand_RegDerefDispAbs(dest_code, size, inst, bytes)?;
+
+ Ok(())
+}
+
+fn decode_op7A<T: Iterator<Item=u8>>(inst: &mut Instruction, size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ if byte >= 0b10000000 {
+ inst.opcode = Opcode::LDC;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = Operand_IntFlgSpSbFb((byte >> 4) & 0b111)?;
+ } else {
+ inst.opcode = Opcode::XCHG(size);
+ assert_eq!(size, Size::B);
+ if byte >= 0b01000000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ inst.operands[0] = [
+ OperandSpec::R0L, OperandSpec::R0H,
+ OperandSpec::R1L, OperandSpec::R1H,
+ ][(byte >> 4) as usize];
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ }
+
+ Ok(())
+}
+
+fn decode_op7B<T: Iterator<Item=u8>>(inst: &mut Instruction, size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ if byte >= 0b10000000 {
+ inst.opcode = Opcode::STC;
+ inst.operands[1] = Operand_IntFlgSpSbFb((byte >> 4) & 0b111)?;
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ } else {
+ inst.opcode = Opcode::XCHG(size);
+ assert_eq!(size, Size::W);
+ if byte >= 0b01000000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ inst.operands[0] = [
+ OperandSpec::R0, OperandSpec::R1,
+ OperandSpec::R2, OperandSpec::R3,
+ ][(byte >> 4) as usize];
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ }
+
+ Ok(())
+}
+
+fn decode_op7C<T: Iterator<Item=u8>>(inst: &mut Instruction, size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ match byte >> 4 {
+ op @ 0b0000 |
+ op @ 0b0001 |
+ op @ 0b0010 |
+ op @ 0b0011 => {
+ inst.opcode = [
+ Opcode::MOVLL, Opcode::MOVLH,
+ Opcode::MOVHL, Opcode::MOVHH,
+ ][op as usize];
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::R0L;
+ },
+ 0b0100 => {
+ inst.opcode = Opcode::MULU(size);
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = Operand_RegDerefDispAbs(byte >> 4, size, inst, bytes)?;
+ let dest_code = byte & 0b1111;
+ match (size, dest_code) {
+ (Size::B, 0b0001) |
+ (Size::W, 0b0010) |
+ (_, 0b0011) |
+ (_, 0b0101) => {
+ return Err(DecodeError::InvalidOperand);
+ }
+ _ => {}
+ }
+ // careful! imm comes after dest disp/abs
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ },
+ 0b0101 => {
+ inst.opcode = Opcode::MUL;
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = Operand_RegDerefDispAbs(byte >> 4, size, inst, bytes)?;
+ let dest_code = byte & 0b1111;
+ match (size, dest_code) {
+ (Size::B, 0b0001) |
+ (Size::W, 0b0010) |
+ (_, 0b0011) |
+ (_, 0b0101) => {
+ return Err(DecodeError::InvalidOperand);
+ }
+ _ => {}
+ }
+ // careful! imm comes after dest disp/abs
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ 0b0110 => {
+ // byte is 0b0110_xxxx
+ inst.opcode = Opcode::EXTS;
+ match byte & 0b1111 {
+ 0b0001 |
+ 0b0011 |
+ 0b0100 |
+ 0b0101 => {
+ return Err(DecodeError::InvalidOperand);
+ }
+ _ => {}
+ }
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ op @ 0b1000 |
+ op @ 0b1001 |
+ op @ 0b1010 |
+ op @ 0b1011 => {
+ inst.opcode = [
+ Opcode::MOVLL, Opcode::MOVLH,
+ Opcode::MOVHL, Opcode::MOVHH,
+ ][(op & 0b11) as usize];
+ inst.operands[0] = OperandSpec::R0L;
+ inst.operands[1] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ }
+ 0b1100 => {
+ inst.opcode = Opcode::EXTS;
+ let dest = match byte & 0b1111 {
+ 0b0010 |
+ 0b0011 |
+ 0b0101 => {
+ return Err(DecodeError::InvalidOperand);
+ }
+ 0b0000 => OperandSpec::R2R0,
+ 0b0001 => OperandSpec::R3R1,
+ 0b0100 => OperandSpec::A1A0,
+ code => {
+ Operand_RegDerefDispAbs(code, size, inst, bytes)?
+ }
+ };
+ inst.operands[0] = dest;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1110 => {
+ let code = byte & 0b1111;
+ if code & 0b111 < 0b100 {
+ match code {
+ op @ 0b0000 |
+ op @ 0b0001 |
+ op @ 0b0010 |
+ op @ 0b0011 => {
+ inst.opcode = [
+ Opcode::DIVU(size), Opcode::DIV(size),
+ Opcode::PUSH(size), Opcode::DIVX(size),
+ ][op as usize];
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = OperandSpec::Nothing;
+ inst.operands[2] = OperandSpec::Nothing;
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ op @ 0b1000 |
+ op @ 0b1001 |
+ op @ 0b1010 => {
+ inst.opcode = [
+ Opcode::SMOVF(size), Opcode::SMOVB(size),
+ Opcode::SSTR(size),
+ ][(op & 0b11) as usize];
+ inst.operands = [OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing];
+ }
+ 0b1011 => {
+ inst.opcode = Opcode::ADD(size);
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = OperandSpec::SP;
+ inst.operands[2] = OperandSpec::Nothing;
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ _ => { unreachable!("this should be an invalid bit pattern"); }
+ }
+ } else {
+ inst.opcode = [
+ Opcode::DADD, Opcode::DSUB,
+ Opcode::DADC, Opcode::DSBB,
+ ][(code & 0b11) as usize];
+ if code < 0b1000 {
+ // reg-reg variants of these opcodes
+ inst.operands[0] = OperandSpec::R0H;
+ inst.operands[1] = OperandSpec::R0L;
+ } else {
+ // reg-imm8 variants of these opcodes
+ inst.operands[0] = OperandSpec::Imm8;
+ inst.operands[1] = OperandSpec::R0L;
+ assert_eq!(size, Size::B);
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ }
+ }
+ 0b1111 => {
+ match byte & 0b1111 {
+ 0b0000 => {
+ inst.opcode = Opcode::LDCTX;
+ inst.operands[0] = OperandSpec::Abs16;
+ inst.dispabs = read_imm(bytes, 2)? as u16;
+ inst.length += 2;
+ inst.operands[1] = OperandSpec::Abs20;
+ inst.imm_wide = read_imm(bytes, 3)? & 0x0f_ff_ff;
+ inst.length += 3;
+ }
+ 0b0001 => {
+ inst.opcode = Opcode::RMPA(size);
+ inst.operands[0] = OperandSpec::Nothing;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0010 => {
+ inst.opcode = Opcode::ENTER;
+ inst.operands[0] = OperandSpec::Imm8;
+ inst.imm_wide = read_imm(bytes, 1)?;
+ inst.length += 1;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0011 => {
+ inst.opcode = Opcode::EXTS;
+ inst.operands[0] = OperandSpec::R0;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ }
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+
+ Ok(())
+}
+
+fn decode_op7D<T: Iterator<Item=u8>>(inst: &mut Instruction, size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ match byte >> 4 {
+ op @ 0b0000 |
+ op @ 0b0001 |
+ op @ 0b0010 |
+ op @ 0b0011 => {
+ inst.opcode = [
+ Opcode::JMPI, Opcode::JSRI,
+ Opcode::JMPI, Opcode::JSRI,
+ ][op as usize];
+ inst.operands[0] = Operand_RegDerefDisp20Abs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0100 => {
+ inst.opcode = Opcode::MULU(size);
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = Operand_RegDerefDispAbs(byte >> 4, size, inst, bytes)?;
+ let dest_code = byte & 0b1111;
+ match (size, dest_code) {
+ (Size::B, 0b0001) |
+ (Size::W, 0b0010) |
+ (_, 0b0011) |
+ (_, 0b0101) => {
+ return Err(DecodeError::InvalidOperand);
+ }
+ _ => {}
+ }
+ // careful! imm comes after dest disp/abs
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ },
+ 0b0101 => {
+ inst.opcode = Opcode::MUL;
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = Operand_RegDerefDispAbs(byte >> 4, size, inst, bytes)?;
+ let dest_code = byte & 0b1111;
+ match (size, dest_code) {
+ (Size::B, 0b0001) |
+ (Size::W, 0b0010) |
+ (_, 0b0011) |
+ (_, 0b0101) => {
+ return Err(DecodeError::InvalidOperand);
+ }
+ _ => {}
+ }
+ // careful! imm comes after dest disp/abs
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ 0b1001 => {
+ // byte is 0b1001_xxxx
+ if byte < 0b1001_1000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ // byte is 0b1001_1xxx
+
+ inst.opcode = Opcode::PUSHA;
+ // while the low eight forms of this encoding are invalid, they would have been rejected above.
+ inst.operands[0] = Operand_RegDerefDispAbs(byte & 0b1111, size, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1010 => {
+ if byte >= 0b1010_1000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ inst.opcode = Opcode::LDIPL;
+ inst.operands[0] = OperandSpec::Imm8;
+ inst.operands[1] = OperandSpec::Nothing;
+ inst.imm_wide = ((((byte & 0b111) as i8) << 5) >> 5) as i32 as u32;
+ }
+ 0b1011 => {
+ // byte is 0b1011_xxxx
+ inst.opcode = Opcode::ADD(size);
+ inst.operands[0] = OperandSpec::Imm8;
+ inst.operands[1] = OperandSpec::SP;
+ inst.imm_wide = ((((byte & 0b111) as i8) << 4) >> 4) as i32 as u32;
+ }
+ 0b1100 => {
+ inst.opcode = match byte & 0b1111 {
+ 0b1000 => Opcode::JLE,
+ 0b1001 => Opcode::JO,
+ 0b1010 => Opcode::JGE,
+ 0b1100 => Opcode::JGT,
+ 0b1101 => Opcode::JNO,
+ 0b1110 => Opcode::JLT,
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ };
+ inst.operands[0] = OperandSpec::Disp8;
+ inst.operands[1] = OperandSpec::Nothing;
+ inst.imm_wide = read_imm(bytes, 1)?;
+ inst.length += 1;
+ }
+ 0b1101 => {
+ let code = byte & 0b1111;
+ if code == 0b1011 || code == 0b1111 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ // the NOP here fill gaps that are invalid encodings as tested above.
+ inst.opcode = [
+ Opcode::BMGEU, Opcode::BMGTU, Opcode::BMEQ, Opcode::BMN,
+ Opcode::BMLTU, Opcode::BMLEU, Opcode::BMNE, Opcode::BMPZ,
+ Opcode::BMLE, Opcode::BMO, Opcode::GE, Opcode::NOP,
+ Opcode::BMGT, Opcode::NO, Opcode::LT, Opcode::NOP,
+ ][code as usize];
+ inst.operands[0] = OperandSpec::Nothing;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b1110 => {
+ let code = byte & 0b1111;
+ if code & 0b111 < 0b100 {
+ match code {
+ op @ 0b0000 |
+ op @ 0b0001 |
+ op @ 0b0010 |
+ op @ 0b0011 => {
+ inst.opcode = [
+ Opcode::DIVU(size), Opcode::DIV(size),
+ Opcode::PUSH(size), Opcode::DIVX(size),
+ ][op as usize];
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = OperandSpec::Nothing;
+ inst.operands[2] = OperandSpec::Nothing;
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ op @ 0b1000 |
+ op @ 0b1001 |
+ op @ 0b1010 => {
+ inst.opcode = [
+ Opcode::SMOVF(size), Opcode::SMOVB(size),
+ Opcode::SSTR(size),
+ ][(op & 0b11) as usize];
+ inst.operands = [OperandSpec::Nothing, OperandSpec::Nothing, OperandSpec::Nothing];
+ }
+ 0b1011 => {
+ inst.opcode = Opcode::ADD(size);
+ inst.operands[0] = if size == Size::W { OperandSpec::Imm16 } else { OperandSpec::Imm8 };
+ inst.operands[1] = OperandSpec::SP;
+ inst.operands[2] = OperandSpec::Nothing;
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ _ => { unreachable!("this should be an invalid bit pattern"); }
+ }
+ } else {
+ inst.opcode = [
+ Opcode::DADD, Opcode::DSUB,
+ Opcode::DADC, Opcode::DSBB,
+ ][(code & 0b11) as usize];
+ if code < 0b1000 {
+ // reg-reg variants of these opcodes
+ inst.operands[0] = OperandSpec::R1;
+ inst.operands[1] = OperandSpec::R0;
+ } else {
+ // reg-imm16 variants of these opcodes
+ inst.operands[0] = OperandSpec::Imm16;
+ inst.operands[1] = OperandSpec::R0;
+ assert_eq!(size, Size::W);
+ inst.imm_wide = read_imm(bytes, size.as_bytes())?;
+ inst.length += size.as_bytes();
+ }
+ }
+ }
+ 0b1111 => {
+ // byte is 0b1111_xxxx
+ match byte & 0b1111 {
+ 0b0000 => {
+ inst.opcode = Opcode::STCTX;
+ inst.operands[0] = OperandSpec::Abs16;
+ inst.dispabs = read_imm(bytes, 2)? as u16;
+ inst.length += 2;
+ inst.operands[1] = OperandSpec::Abs20;
+ inst.imm_wide = read_imm(bytes, 3)? & 0x0f_ff_ff;
+ inst.length += 3;
+ }
+ 0b0001 => {
+ inst.opcode = Opcode::RMPA(size);
+ inst.operands[0] = OperandSpec::Nothing;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0010 => {
+ inst.opcode = Opcode::EXITD;
+ inst.operands[0] = OperandSpec::Nothing;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0011 => {
+ inst.opcode = Opcode::WAIT;
+ inst.operands[0] = OperandSpec::Nothing;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ }
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+
+ Ok(())
+}
+
+fn decode_op7E<T: Iterator<Item=u8>>(inst: &mut Instruction, _size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ let op = byte >> 4;
+ let code = byte & 0b1111;
+ if op > 0b1101 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ if op == 0b0010 {
+ // handle BMCMD specifically
+ inst.operands[0] = Operand_BitRegDerefDispAbs(code, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ let cnd = read_imm(bytes, 1)? as u8;
+ inst.length += 1;
+ inst.opcode = match cnd {
+ 0b0000_0000 => Opcode::BMGEU,
+ 0b0000_0001 => Opcode::BMGTU,
+ 0b0000_0010 => Opcode::BMEQ,
+ 0b0000_0011 => Opcode::BMN,
+ 0b0000_0100 => Opcode::BMLE,
+ 0b0000_0101 => Opcode::BMO,
+ 0b0000_0110 => Opcode::BMGE,
+ 0b1111_1000 => Opcode::BMLTU,
+ 0b1111_1001 => Opcode::BMLEU,
+ 0b1111_1010 => Opcode::BMNE,
+ 0b1111_1011 => Opcode::BMPZ,
+ 0b1111_1100 => Opcode::BMGT,
+ 0b1111_1101 => Opcode::BMNO,
+ 0b1111_1110 => Opcode::BMLT,
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ };
+ } else {
+ inst.opcode = [
+ Opcode::BTSTC, Opcode::BTSTS, Opcode::NOP, // NOP is unreachable, tested above
+ Opcode::BNTST, Opcode::BAND, Opcode::BNAND,
+ Opcode::BOR, Opcode::BNOR, Opcode::BCLR,
+ Opcode::BSET, Opcode::BNOT, Opcode::BTST,
+ Opcode::BXOR, Opcode::BNXOR,
+ ][op as usize];
+ inst.operands[0] = Operand_BitRegDerefDispAbs(code, inst, bytes)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+
+ Ok(())
+}
+
+fn decode_opEB<T: Iterator<Item=u8>>(inst: &mut Instruction, _size: Size, bytes: &mut T) -> Result<(), DecodeError> {
+ let byte = bytes.next().ok_or(DecodeError::ExhaustedInput)?;
+ inst.length += 1;
+ let upper = byte >> 4;
+ let lower = byte & 0b1111;
+ if upper < 0b1000 {
+ // SHL, SHA, LDC, POPC, MOVA, PUSHC, FSET, or FCLR
+ match lower {
+ 0b0000 => {
+ // LDC
+ inst.opcode = Opcode::LDC;
+ inst.operands[0] = OperandSpec::Imm16;
+ inst.operands[1] = Operand_IntFlgSpSbFb((byte >> 4) & 0b111)?;
+ inst.imm_wide = read_imm(bytes, 2)?;
+ inst.length += 2;
+ }
+ 0b0001 => {
+ // SHL/SHA
+ inst.opcode = if upper & 2 == 0 { Opcode::SHL(Size::L) } else { Opcode::SHA(Size::L) };
+ inst.operands[0] = OperandSpec::R1H;
+ inst.operands[1] = if upper & 1 == 0 { OperandSpec::R2R0 } else { OperandSpec::R3R1 };
+ }
+ 0b0010 => {
+ // PUSHC
+ inst.opcode = Opcode::PUSHC;
+ inst.operands[0] = Operand_IntFlgSpSbFb((byte >> 4) & 0b111)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ },
+ 0b0011 => {
+ // POPC
+ inst.opcode = Opcode::POPC;
+ inst.operands[0] = Operand_IntFlgSpSbFb((byte >> 4) & 0b111)?;
+ inst.operands[1] = OperandSpec::Nothing;
+ }
+ 0b0100 => {
+ // FSET
+ }
+ 0b0101 => {
+ // FCLR
+ }
+ _ => {
+ // MOVA
+ inst.opcode = Opcode::MOVA;
+ let src = byte & 0b1111;
+ if src < 0b1000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ inst.operands[0] = Operand_RegDerefDispAbs(src, Size::W, inst, bytes)?;
+ let dest = byte >> 4;
+ if dest >= 0b1000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ inst.operands[1] = Operand_RegDerefDispAbs(dest, Size::W, inst, bytes)?;
+ }
+ }
+ } else {
+ // SHL Imm4, SHA Imm4, or INT
+ if upper >= 0b1100 {
+ inst.opcode = Opcode::INT;
+ inst.operands[0] = OperandSpec::Imm8;
+ inst.operands[1] = OperandSpec::Nothing;
+ inst.imm_wide = (byte & 0b0011_1111) as u32;
+ } else {
+ inst.opcode = if upper & 2 == 0 { Opcode::SHL(Size::L) } else { Opcode::SHA(Size::L) };
+ inst.operands[0] = OperandSpec::Imm4;
+ inst.imm_wide = lower as u32;
+ inst.operands[1] = if upper & 1 == 0 { OperandSpec::R2R0 } else { OperandSpec::R3R1 };
+ }
+ }
+
+ Ok(())
+}
+
+fn read_imm<T: Iterator<Item=u8>>(bytes: &mut T, mut size: u8) -> Result<u32, DecodeError> {
+ let mut imm: u32 = 0;
+ let mut offset = 0;
+
+ while size > 0 {
+ imm |= (bytes.next().ok_or(DecodeError::ExhaustedInput)? as u32) << (8 * offset);
+ offset += 1;
+ size -= 1;
+ }
+
+ Ok(imm)
+}
+
+
+fn Operand_BitRegDerefDispAbs<T: Iterator<Item=u8>>(code: u8, inst: &mut Instruction, bytes: &mut T) -> Result<OperandSpec, DecodeError> {
+ use OperandSpec::*;
+ // "bit,<reg>" addressing modes taking their bit selector from a following byte is left mostly
+ // unspoken in the manual, aside from the "bit,Rn" and "bit,An" modes taking an extra byte.
+ let (imm_size, spec) = match code {
+ 0b0000 => (1, Bit_R0),
+ 0b0001 => (1, Bit_R1),
+ 0b0010 => (1, Bit_R2),
+ 0b0011 => (1, Bit_R3),
+ 0b0100 => (1, Bit_A0),
+ 0b0101 => (1, Bit_A1),
+ 0b0110 => (0, Bit_Deref_A0),
+ 0b0111 => (0, Bit_Deref_A1),
+ 0b1000 => (1, Bit_Disp8_A0),
+ 0b1001 => (1, Bit_Disp8_A1),
+ 0b1010 => (1, Bit_Disp8_SB),
+ 0b1011 => (1, Bit_Disp8_FB),
+ 0b1100 => (2, Bit_Disp16_A0),
+ 0b1101 => (2, Bit_Disp16_A1),
+ 0b1110 => (2, Bit_Disp16_SB),
+ 0b1111 => (2, Bit_Abs16),
+ _ => { unreachable!("invalid code provided, >0b1111"); }
+ };
+
+ if imm_size > 0 {
+ inst.dispabs = bytes.next().ok_or(DecodeError::ExhaustedInput)? as u16;
+ inst.length += 1;
+
+ if imm_size == 2 {
+ inst.dispabs |= (bytes.next().ok_or(DecodeError::ExhaustedInput)? as u16) << 8;
+ inst.length += 1;
+ }
+ }
+
+ Ok(spec)
+
+}
+
+fn Operand_RegDerefDispAbs<T: Iterator<Item=u8>>(code: u8, size: Size, inst: &mut Instruction, bytes: &mut T) -> Result<OperandSpec, DecodeError> {
+ use OperandSpec::*;
+ let (imm_size, spec) = match code {
+ 0b0000 => (0, { if size == Size::B { R0L } else { R0 } }),
+ 0b0001 => (0, { if size == Size::B { R0H } else { R1 } }),
+ 0b0010 => (0, { if size == Size::B { R1L } else { R2 } }),
+ 0b0011 => (0, { if size == Size::B { R1H } else { R3 } }),
+ 0b0100 => (0, A0),
+ 0b0101 => (0, A1),
+ 0b0110 => (0, Deref_A0),
+ 0b0111 => (0, Deref_A1),
+ 0b1000 => (1, Disp8_A0),
+ 0b1001 => (1, Disp8_A1),
+ 0b1010 => (1, Disp8_SB),
+ 0b1011 => (1, Disp8_FB),
+ 0b1100 => (2, Disp16_A0),
+ 0b1101 => (2, Disp16_A1),
+ 0b1110 => (2, Disp16_SB),
+ 0b1111 => (2, Abs16),
+ _ => { unreachable!("invalid code provided, >0b1111"); }
+ };
+
+ if imm_size > 0 {
+ inst.dispabs = bytes.next().ok_or(DecodeError::ExhaustedInput)? as u16;
+ inst.length += 1;
+
+ if imm_size == 2 {
+ inst.dispabs |= (bytes.next().ok_or(DecodeError::ExhaustedInput)? as u16) << 8;
+ inst.length += 1;
+ }
+ }
+
+ Ok(spec)
+}
+
+fn Operand_second_RegDerefDispAbs<T: Iterator<Item=u8>>(code: u8, size: Size, inst: &mut Instruction, bytes: &mut T) -> Result<OperandSpec, DecodeError> {
+ use OperandSpec::*;
+ let (imm_size, spec) = match code {
+ 0b0000 => (0, { if size == Size::B { R0L } else { R0 } }),
+ 0b0001 => (0, { if size == Size::B { R0H } else { R1 } }),
+ 0b0010 => (0, { if size == Size::B { R1L } else { R2 } }),
+ 0b0011 => (0, { if size == Size::B { R1H } else { R3 } }),
+ 0b0100 => (0, A0),
+ 0b0101 => (0, A1),
+ 0b0110 => (0, Deref_A0),
+ 0b0111 => (0, Deref_A1),
+ 0b1000 => (1, Disp2_8_A0),
+ 0b1001 => (1, Disp2_8_A1),
+ 0b1010 => (1, Disp2_8_SB),
+ 0b1011 => (1, Disp2_8_FB),
+ 0b1100 => (2, Disp2_16_A0),
+ 0b1101 => (2, Disp2_16_A1),
+ 0b1110 => (2, Disp2_16_SB),
+ 0b1111 => (2, Abs2_16),
+ _ => { unreachable!("invalid code provided, >0b1111"); }
+ };
+
+ if imm_size > 0 {
+ inst.imm_wide = bytes.next().ok_or(DecodeError::ExhaustedInput)? as u32;
+ inst.length += 1;
+
+ if imm_size == 2 {
+ inst.imm_wide |= (bytes.next().ok_or(DecodeError::ExhaustedInput)? as u32) << 8;
+ inst.length += 1;
+ }
+ }
+
+ Ok(spec)
+}
+
+fn Operand_RegDerefDisp20Abs<T: Iterator<Item=u8>>(code: u8, size: Size, inst: &mut Instruction, bytes: &mut T) -> Result<OperandSpec, DecodeError> {
+ use OperandSpec::*;
+ let (imm_size, spec) = match code {
+ 0b0000 => (0, { if size == Size::B { R0L } else { R0 } }),
+ 0b0001 => (0, { if size == Size::B { R0H } else { R1 } }),
+ 0b0010 => (0, { if size == Size::B { R1L } else { R2 } }),
+ 0b0011 => (0, { if size == Size::B { R1H } else { R3 } }),
+ 0b0100 => (0, A0),
+ 0b0101 => (0, A1),
+ 0b0110 => (0, Deref_A0),
+ 0b0111 => (0, Deref_A1),
+ 0b1000 => (1, Disp8_A0),
+ 0b1001 => (1, Disp8_A1),
+ 0b1010 => (1, Disp8_SB),
+ 0b1011 => (1, Disp8_FB),
+ 0b1100 => (3, Disp20_A0),
+ 0b1101 => (3, Disp20_A1),
+ 0b1110 => (2, Disp16_SB),
+ 0b1111 => (2, Abs16),
+ _ => { unreachable!("invalid code provided, >0b1111"); }
+ };
+
+ match imm_size {
+ 1 => {
+ inst.dispabs = bytes.next().ok_or(DecodeError::ExhaustedInput)? as u16;
+ inst.length += 1;
+ }
+ 2 => {
+ inst.dispabs = bytes.next().ok_or(DecodeError::ExhaustedInput)? as u16;
+ inst.dispabs |= (bytes.next().ok_or(DecodeError::ExhaustedInput)? as u16) << 8;
+ inst.length += 2;
+ }
+ 3 => {
+ inst.imm_wide = bytes.next().ok_or(DecodeError::ExhaustedInput)? as u32;
+ inst.imm_wide |= (bytes.next().ok_or(DecodeError::ExhaustedInput)? as u32) << 8;
+ inst.imm_wide |= ((bytes.next().ok_or(DecodeError::ExhaustedInput)? & 0x0f) as u32) << 16;
+ inst.length += 3;
+ }
+ _ => {
+ unreachable!();
+ }
+ }
+
+ Ok(spec)
+}
+
+fn Operand_IntFlgSpSbFb(code: u8) -> Result<OperandSpec, DecodeError> {
+ use OperandSpec::*;
+
+ match code {
+ 0b001 => Ok(INTBL),
+ 0b010 => Ok(INTBH),
+ 0b011 => Ok(FLG),
+ 0b100 => Ok(ISP),
+ 0b101 => Ok(SP),
+ 0b110 => Ok(SB),
+ 0b111 => Ok(FB),
+ _ => Err(DecodeError::InvalidOperand)
+ }
+}
diff --git a/test/test.rs b/test/test.rs
new file mode 100644
index 0000000..2b59011
--- /dev/null
+++ b/test/test.rs
@@ -0,0 +1,52 @@
+use yaxpeax_arch::{Decoder, LengthedInstruction};
+use yaxpeax_m16c::InstDecoder;
+
+use std::fmt::Write;
+
+fn test_display(data: &[u8], expected: &'static str) {
+ test_display_under(&InstDecoder::default(), data, expected);
+}
+
+fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str) {
+ let mut hex = String::new();
+ for b in data {
+ write!(hex, "{:02x}", b).unwrap();
+ }
+ match decoder.decode(data.into_iter().map(|x| *x)) {
+ Ok(instr) => {
+ let text = format!("{}", instr);
+ assert!(
+ text == expected,
+ "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
+ hex,
+ instr,
+ decoder,
+ text,
+ expected
+ );
+ // while we're at it, test that the instruction is as long, and no longer, than its
+ // input
+ assert_eq!(instr.len() as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected);
+ },
+ Err(e) => {
+ assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ }
+ }
+}
+
+#[test]
+fn test() {
+ test_display(&[0x7c, 0xf2, 0x03], "enter #03h");
+ test_display(&[0x73, 0x1b, 0xfd], "mov.w r1, -3[fb]");
+ test_display(&[0xb6, 0xff], "mov.b #00h, -1[fb]");
+ test_display(&[0xe6, 0x4f, 0xff], "cmp.b #4fh, -1[fb]");
+ test_display(&[0x68, 0x0c], "jgeu $+12");
+ test_display(&[0x7d, 0xf2], "exitd");
+ test_display(&[0x32, 0xff], "mov.b -1[fb], a0");
+ test_display(&[0xa1, 0xb4, 0xfd], "add.w -3[fb], a0");
+ test_display(&[0xd8, 0xf6], "mov.b #-01h, [a0]");
+ test_display(&[0xa6, 0xff], "inc.b -1[fb]");
+ test_display(&[0xfe, 0xf1], "jmp.b $-15");
+ test_display(&[0x7d, 0xeb, 0x44, 0x66], "add.w #6644h, sp");
+ test_display(&[0x7c, 0xeb, 0x54], "add.b #54h, sp");
+}