use std::fmt::{self, Write}; pub type Register = u8; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Size { Byte, Word, Long, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Sign { Signed, Unsigned, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Direction { FromTarget, ToTarget, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ShiftDirection { Right, Left, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum XRegister { DReg(u8), AReg(u8), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum BaseRegister { None, PC, AReg(u8), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct IndexRegister { pub xreg: XRegister, pub scale: u8, pub size: Size, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum RegOrImmediate { DReg(u8), Immediate(u8), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ControlRegister { VBR, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Condition { True, False, High, LowOrSame, CarryClear, CarrySet, NotEqual, Equal, OverflowClear, OverflowSet, Plus, Minus, GreaterThanOrEqual, LessThan, GreaterThan, LessThanOrEqual, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Target { Immediate(u32), DirectDReg(Register), DirectAReg(Register), IndirectAReg(Register), IndirectARegInc(Register), IndirectARegDec(Register), IndirectRegOffset(BaseRegister, Option, i32), IndirectMemoryPreindexed(BaseRegister, Option, i32, i32), IndirectMemoryPostindexed(BaseRegister, Option, i32, i32), IndirectMemory(u32, Size), } #[derive(Clone, Debug, PartialEq, Eq)] pub enum Instruction { ABCD(Target, Target), ADD(Target, Target, Size), ADDA(Target, Register, Size), ADDX(Target, Target, Size), AND(Target, Target, Size), ANDtoCCR(u8), ANDtoSR(u16), ASd(Target, Target, Size, ShiftDirection), Bcc(Condition, i32), BRA(i32), BSR(i32), BCHG(Target, Target, Size), BCLR(Target, Target, Size), BSET(Target, Target, Size), BTST(Target, Target, Size), BFCHG(Target, RegOrImmediate, RegOrImmediate), BFCLR(Target, RegOrImmediate, RegOrImmediate), BFEXTS(Target, RegOrImmediate, RegOrImmediate, Register), BFEXTU(Target, RegOrImmediate, RegOrImmediate, Register), BFFFO(Target, RegOrImmediate, RegOrImmediate, Register), BFINS(Register, Target, RegOrImmediate, RegOrImmediate), BFSET(Target, RegOrImmediate, RegOrImmediate), BFTST(Target, RegOrImmediate, RegOrImmediate), BKPT(u8), CHK(Target, Register, Size), CLR(Target, Size), CMP(Target, Target, Size), CMPA(Target, Register, Size), DBcc(Condition, Register, i16), DIVW(Target, Register, Sign), DIVL(Target, Option, Register, Sign), EOR(Target, Target, Size), EORtoCCR(u8), EORtoSR(u16), EXG(Target, Target), EXT(Register, Size, Size), ILLEGAL, JMP(Target), JSR(Target), LEA(Target, Register), LINK(Register, i32), LSd(Target, Target, Size, ShiftDirection), MOVE(Target, Target, Size), MOVEA(Target, Register, Size), MOVEfromSR(Target), MOVEtoSR(Target), MOVEfromCCR(Target), MOVEtoCCR(Target), MOVEC(Target, ControlRegister, Direction), MOVEM(Target, Size, Direction, u16), MOVEP(Register, Register, i16, Size, Direction), MOVEQ(u8, Register), MOVEUSP(Target, Direction), MULW(Target, Register, Sign), MULL(Target, Option, Register, Sign), NBCD(Target), NEG(Target, Size), NEGX(Target, Size), NOP, NOT(Target, Size), OR(Target, Target, Size), ORtoCCR(u8), ORtoSR(u16), PEA(Target), RESET, ROd(Target, Target, Size, ShiftDirection), ROXd(Target, Target, Size, ShiftDirection), RTE, RTR, RTS, RTD(i16), Scc(Condition, Target), STOP(u16), SBCD(Target, Target), SUB(Target, Target, Size), SUBA(Target, Register, Size), SUBX(Target, Target, Size), SWAP(Register), TAS(Target), TST(Target, Size), TRAP(u8), TRAPV, UNLK(Register), UnimplementedA(u16), UnimplementedF(u16), } pub fn sign_extend_to_long(value: u32, from: Size) -> i32 { match from { Size::Byte => ((value as u8) as i8) as i32, Size::Word => ((value as u16) as i16) as i32, Size::Long => value as i32, } } impl Size { pub fn in_bytes(&self) -> u32 { match self { Size::Byte => 1, Size::Word => 2, Size::Long => 4, } } pub fn in_bits(&self) -> u32 { match self { Size::Byte => 8, Size::Word => 16, Size::Long => 32, } } } impl fmt::Display for Sign { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Sign::Signed => write!(f, "s"), Sign::Unsigned => write!(f, "u"), } } } impl fmt::Display for ShiftDirection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ShiftDirection::Right => write!(f, "r"), ShiftDirection::Left => write!(f, "l"), } } } impl fmt::Display for Size { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Size::Byte => write!(f, "b"), Size::Word => write!(f, "w"), Size::Long => write!(f, "l"), } } } impl fmt::Display for Condition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Condition::True => write!(f, "t"), Condition::False => write!(f, "f"), Condition::High => write!(f, "hi"), Condition::LowOrSame => write!(f, "ls"), Condition::CarryClear => write!(f, "cc"), Condition::CarrySet => write!(f, "cs"), Condition::NotEqual => write!(f, "ne"), Condition::Equal => write!(f, "eq"), Condition::OverflowClear => write!(f, "oc"), Condition::OverflowSet => write!(f, "os"), Condition::Plus => write!(f, "p"), Condition::Minus => write!(f, "m"), Condition::GreaterThanOrEqual => write!(f, "ge"), Condition::LessThan => write!(f, "lt"), Condition::GreaterThan => write!(f, "gt"), Condition::LessThanOrEqual => write!(f, "le"), } } } impl fmt::Display for ControlRegister { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ControlRegister::VBR => write!(f, "%vbr"), } } } impl fmt::Display for RegOrImmediate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { RegOrImmediate::DReg(reg) => write!(f, "%d{}", reg), RegOrImmediate::Immediate(value) => write!(f, "#{:#02x}", value), } } } impl fmt::Display for XRegister { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { XRegister::DReg(reg) => write!(f, "d{}", reg), XRegister::AReg(reg) => write!(f, "a{}", reg), } } } impl fmt::Display for BaseRegister { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BaseRegister::None => Ok(()), BaseRegister::PC => write!(f, "%pc"), BaseRegister::AReg(reg) => write!(f, "%a{}", reg), } } } fn fmt_index_disp(index: &Option) -> String { match index { Some(index) => { let mut result = format!(", %{}", index.xreg); if index.scale != 0 { write!(result, "<< {}", index.scale).unwrap(); } result }, None => "".to_string(), } } impl fmt::Display for Target { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Target::Immediate(value) => write!(f, "#{:#08x}", value), Target::DirectDReg(reg) => write!(f, "%d{}", reg), Target::DirectAReg(reg) => write!(f, "%a{}", reg), Target::IndirectAReg(reg) => write!(f, "(%a{})", reg), Target::IndirectARegInc(reg) => write!(f, "(%a{})+", reg), Target::IndirectARegDec(reg) => write!(f, "-(%a{})", reg), Target::IndirectRegOffset(base_reg, index_reg, offset) => { let index_str = fmt_index_disp(index_reg); write!(f, "(#{:04x}, {}{})", offset, base_reg, index_str) }, Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => { let index_str = fmt_index_disp(index_reg); write!(f, "([{}{}#{:08x}] + #{:08x})", base_reg, index_str, base_disp, outer_disp) }, Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => { let index_str = fmt_index_disp(index_reg); write!(f, "([{}#{:08x}]{} + #{:08x})", base_reg, base_disp, index_str, outer_disp) }, Target::IndirectMemory(value, size) => if *size == Size::Word { write!(f, "(#{:04x})", value) } else { write!(f, "(#{:08x})", value) }, } } } fn fmt_movem_mask(mut mask: u16, target: &Target) -> String { let mut output = vec![]; match target { Target::IndirectARegDec(_) => { for i in (0..8).rev() { if (mask & 0x01) != 0 { output.push(format!("%a{}", i)); } mask >>= 1; } for i in (0..8).rev() { if (mask & 0x01) != 0 { output.push(format!("%d{}", i)); } mask >>= 1; } }, _ => { for i in 0..8 { if (mask & 0x01) != 0 { output.push(format!("%d{}", i)); } mask >>= 1; } for i in 0..8 { if (mask & 0x01) != 0 { output.push(format!("%a{}", i)); } mask >>= 1; } }, } output.join("/") } impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Instruction::ABCD(src, dest) => write!(f, "abcd\t{}, {}", src, dest), Instruction::ADD(src @ Target::Immediate(_), dest, size) => write!(f, "addi{}\t{}, {}", size, src, dest), Instruction::ADD(src, dest, size) => write!(f, "add{}\t{}, {}", size, src, dest), Instruction::ADDA(target, reg, size) => write!(f, "adda{}\t{}, %a{}", size, target, reg), Instruction::ADDX(src, dest, size) => write!(f, "addx{}\t{}, {}", size, src, dest), Instruction::AND(src @ Target::Immediate(_), dest, size) => write!(f, "andi{}\t{}, {}", size, src, dest), Instruction::AND(src, dest, size) => write!(f, "and{}\t{}, {}", size, src, dest), Instruction::ANDtoCCR(value) => write!(f, "andib\t#{:#02x}, %ccr", value), Instruction::ANDtoSR(value) => write!(f, "andiw\t#{:#04x}, %sr", value), Instruction::ASd(src, dest, size, dir) => write!(f, "as{}{}\t{}, {}", dir, size, src, dest), Instruction::Bcc(cond, offset) => write!(f, "b{}\t{}", cond, offset), Instruction::BRA(offset) => write!(f, "bra\t{}", offset), Instruction::BSR(offset) => write!(f, "bra\t{}", offset), Instruction::BCHG(src, dest, size) => write!(f, "bchg{}\t{}, {}", size, src, dest), Instruction::BCLR(src, dest, size) => write!(f, "bclr{}\t{}, {}", size, src, dest), Instruction::BSET(src, dest, size) => write!(f, "bset{}\t{}, {}", size, src, dest), Instruction::BTST(src, dest, size) => write!(f, "btst{}\t{}, {}", size, src, dest), Instruction::BFCHG(target, offset, width) => write!(f, "bfchg\t{}, {}, {}", target, offset, width), Instruction::BFCLR(target, offset, width) => write!(f, "bfclr\t{}, {}, {}", target, offset, width), Instruction::BFEXTS(target, offset, width, reg) => write!(f, "bfexts\t{}, {}, {}, %d{}", target, offset, width, reg), Instruction::BFEXTU(target, offset, width, reg) => write!(f, "bfextu\t{}, {}, {}, %d{}", target, offset, width, reg), Instruction::BFFFO(target, offset, width, reg) => write!(f, "bfffo\t{}, {}, {}, %d{}", target, offset, width, reg), Instruction::BFINS(reg, target, offset, width) => write!(f, "bfins\t%d{}, {}, {}, {}", reg, target, offset, width), Instruction::BFSET(target, offset, width) => write!(f, "bfset\t{}, {}, {}", target, offset, width), Instruction::BFTST(target, offset, width) => write!(f, "bftst\t{}, {}, {}", target, offset, width), Instruction::BKPT(value) => write!(f, "bkpt\t{}", value), Instruction::CHK(target, reg, size) => write!(f, "chk{}\t{}, %d{}", size, target, reg), Instruction::CLR(target, size) => write!(f, "clr{}\t{}", size, target), Instruction::CMP(src @ Target::Immediate(_), dest, size) => write!(f, "cmpi{}\t{}, {}", size, src, dest), Instruction::CMP(src, dest, size) => write!(f, "cmp{}\t{}, {}", size, src, dest), Instruction::CMPA(target, reg, size) => write!(f, "cmpa{}\t{}, %a{}", size, target, reg), Instruction::DBcc(cond, reg, offset) => write!(f, "db{}\t%d{}, {}", cond, reg, offset), Instruction::DIVW(src, dest, sign) => write!(f, "div{}w\t{}, %d{}", sign, src, dest), Instruction::DIVL(src, desth, destl, sign) => { let opt_reg = desth.map(|reg| format!("%d{}:", reg)).unwrap_or_else(|| "".to_string()); write!(f, "div{}l\t{}, {}%d{}", sign, src, opt_reg, destl) }, Instruction::EOR(src @ Target::Immediate(_), dest, size) => write!(f, "eori{}\t{}, {}", size, src, dest), Instruction::EOR(src, dest, size) => write!(f, "eor{}\t{}, {}", size, src, dest), Instruction::EORtoCCR(value) => write!(f, "eorib\t#{:#02x}, %ccr", value), Instruction::EORtoSR(value) => write!(f, "eoriw\t#{:#04x}, %sr", value), Instruction::EXG(src, dest) => write!(f, "exg\t{}, {}", src, dest), Instruction::EXT(reg, from_size, to_size) => write!(f, "ext{}{}\t%d{}", if *from_size == Size::Byte && *to_size == Size::Long { "b" } else { "" }, to_size, reg), Instruction::ILLEGAL => write!(f, "illegal"), Instruction::JMP(target) => write!(f, "jmp\t{}", target), Instruction::JSR(target) => write!(f, "jsr\t{}", target), Instruction::LEA(target, reg) => write!(f, "lea\t{}, %a{}", target, reg), Instruction::LINK(reg, offset) => write!(f, "link\t%a{}, {}", reg, offset), Instruction::LSd(src, dest, size, dir) => write!(f, "ls{}{}\t{}, {}", dir, size, src, dest), Instruction::MOVE(src, dest, size) => write!(f, "move{}\t{}, {}", size, src, dest), Instruction::MOVEA(target, reg, size) => write!(f, "movea{}\t{}, %a{}", size, target, reg), Instruction::MOVEfromSR(target) => write!(f, "movew\t%sr, {}", target), Instruction::MOVEtoSR(target) => write!(f, "movew\t{}, %sr", target), Instruction::MOVEfromCCR(target) => write!(f, "moveb\t%ccr, {}", target), Instruction::MOVEtoCCR(target) => write!(f, "moveb\t{}, %ccr", target), Instruction::MOVEC(target, reg, dir) => match dir { Direction::ToTarget => write!(f, "movec\t{}, {}", reg, target), Direction::FromTarget => write!(f, "movec\t{}, {}", target, reg), }, Instruction::MOVEM(target, size, dir, mask) => match dir { Direction::ToTarget => write!(f, "movem{}\t{}, {}", size, fmt_movem_mask(*mask, target), target), Direction::FromTarget => write!(f, "movem{}\t{}, {}", size, target, fmt_movem_mask(*mask, target)), }, Instruction::MOVEP(dreg, areg, offset, size, dir) => match dir { Direction::ToTarget => write!(f, "movep{}\t%d{}, ({}, %a{})", size, dreg, areg, offset), Direction::FromTarget => write!(f, "movep{}\t({}, %a{}), %d{}", size, areg, offset, dreg), }, Instruction::MOVEQ(value, reg) => write!(f, "moveq\t#{:#02x}, %d{}", value, reg), Instruction::MOVEUSP(target, dir) => match dir { Direction::ToTarget => write!(f, "movel\t%usp, {}", target), Direction::FromTarget => write!(f, "movel\t{}, %usp", target), }, Instruction::MULW(src, dest, sign) => write!(f, "mul{}w\t{}, %d{}", sign, src, dest), Instruction::MULL(src, desth, destl, sign) => { let opt_reg = desth.map(|reg| format!("%d{}:", reg)).unwrap_or_else(|| "".to_string()); write!(f, "mul{}l\t{}, {}%d{}", sign, src, opt_reg, destl) }, Instruction::NBCD(target) => write!(f, "nbcd\t{}", target), Instruction::NEG(target, size) => write!(f, "neg{}\t{}", size, target), Instruction::NEGX(target, size) => write!(f, "negx{}\t{}", size, target), Instruction::NOP => write!(f, "nop"), Instruction::NOT(target, size) => write!(f, "not{}\t{}", size, target), Instruction::OR(src @ Target::Immediate(_), dest, size) => write!(f, "ori{}\t{}, {}", size, src, dest), Instruction::OR(src, dest, size) => write!(f, "or{}\t{}, {}", size, src, dest), Instruction::ORtoCCR(value) => write!(f, "orib\t#{:#02x}, %ccr", value), Instruction::ORtoSR(value) => write!(f, "oriw\t#{:#04x}, %sr", value), Instruction::PEA(target) => write!(f, "pea\t{}", target), Instruction::RESET => write!(f, "reset"), Instruction::ROd(src, dest, size, dir) => write!(f, "ro{}{}\t{}, {}", dir, size, src, dest), Instruction::ROXd(src, dest, size, dir) => write!(f, "rox{}{}\t{}, {}", dir, size, src, dest), Instruction::RTE => write!(f, "rte"), Instruction::RTR => write!(f, "rtr"), Instruction::RTS => write!(f, "rts"), Instruction::RTD(offset) => write!(f, "rtd\t{}", offset), Instruction::SBCD(src, dest) => write!(f, "sbcd\t{}, {}", src, dest), Instruction::Scc(cond, target) => write!(f, "s{}\t{}", cond, target), Instruction::STOP(value) => write!(f, "stop\t#{:#04x}", value), Instruction::SUB(src @ Target::Immediate(_), dest, size) => write!(f, "subi{}\t{}, {}", size, src, dest), Instruction::SUB(src, dest, size) => write!(f, "sub{}\t{}, {}", size, src, dest), Instruction::SUBA(target, reg, size) => write!(f, "suba{}\t{}, %a{}", size, target, reg), Instruction::SUBX(src, dest, size) => write!(f, "subx{}\t{}, {}", size, src, dest), Instruction::SWAP(reg) => write!(f, "swap\t%d{}", reg), Instruction::TAS(target) => write!(f, "tas\t{}", target), Instruction::TST(target, size) => write!(f, "tst{}\t{}", size, target), Instruction::TRAP(num) => write!(f, "trap\t{}", num), Instruction::TRAPV => write!(f, "trapv"), Instruction::UNLK(reg) => write!(f, "unlk\t%a{}", reg), Instruction::UnimplementedA(ins) => write!(f, "coproc_a\t{:#06x}", ins), Instruction::UnimplementedF(ins) => write!(f, "coproc_f\t{:#06x}", ins), } } }