extern crate core; use core::fmt; use yaxpeax_arch::AddressDiff; use yaxpeax_arch::Arch; use yaxpeax_arch::Decoder; use yaxpeax_arch::LengthedInstruction; use yaxpeax_arch::Reader; use yaxpeax_arch::StandardDecodeError; #[cfg_attr(feature="use-serde", derive(Serialize, Deserialize))] #[derive(Debug)] pub struct SM83; impl Arch for SM83 { type Address = u16; type Word = u8; type Instruction = Instruction; type DecodeError = StandardDecodeError; type Decoder = InstDecoder; type Operand = Operand; } #[derive(Debug, Copy, Clone)] pub struct Instruction { opcode: Opcode, operands: [Operand; 2], length: u8, } impl Instruction { pub fn opcode(&self) -> Opcode { self.opcode } pub fn operands(&self) -> &[Operand; 2] { &self.operands } pub fn length(&self) -> u8 { self.length } } impl Default for Instruction { fn default() -> Instruction { Instruction { opcode: Opcode::NOP, operands: [Operand::Nothing, Operand::Nothing], length: 1, } } } impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.opcode())?; let ops = self.operands(); if ops[0] != Operand::Nothing { write!(f, " {}", ops[0])?; } if ops[1] != Operand::Nothing { write!(f, ", {}", ops[1])?; } Ok(()) } } impl LengthedInstruction for Instruction { type Unit = AddressDiff<::Address>; fn min_size() -> Self::Unit { AddressDiff::from_const(1) } fn len(&self) -> Self::Unit { AddressDiff::from_const(self.length as u16) } } impl yaxpeax_arch::Instruction for Instruction { // only decode well-formed instructions (for now???) fn well_defined(&self) -> bool { true } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Operand { A, B, C, D, E, H, L, AF, BC, DE, HL, SP, DerefHL, DerefBC, DerefDE, DerefDecHL, DerefIncHL, DerefHighC, DerefHighD8(u8), SPWithOffset(i8), Bit(u8), D8(u8), I8(i8), R8(i8), A16(u16), D16(u16), CondC, CondNC, CondZ, CondNZ, Nothing, } impl fmt::Display for Operand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Operand::A => write!(f, "a"), Operand::B => write!(f, "b"), Operand::C => write!(f, "c"), Operand::D => write!(f, "d"), Operand::E => write!(f, "e"), Operand::H => write!(f, "h"), Operand::L => write!(f, "l"), Operand::AF => write!(f, "af"), Operand::BC => write!(f, "bc"), Operand::DE => write!(f, "de"), Operand::HL => write!(f, "hl"), Operand::SP => write!(f, "sp"), Operand::DerefHL => write!(f, "[hl]"), Operand::DerefBC => write!(f, "[bc]"), Operand::DerefDE => write!(f, "[de]"), Operand::DerefDecHL => write!(f, "[hl-]"), Operand::DerefIncHL => write!(f, "[hl+]"), Operand::DerefHighC => write!(f, "[0xff00 + c]"), Operand::DerefHighD8(imm) => write!(f, "[$ff00 + ${:02x}]", imm), Operand::SPWithOffset(imm) => { if *imm == -128 { write!(f, "[sp - $80]") } else if *imm >= 0 { write!(f, "[sp + ${:02x}]", imm) } else { write!(f, "[sp - ${:02x}]", -imm) } } Operand::Bit(imm) => write!(f, "{}", imm), Operand::D8(imm) => write!(f, "${:02x}", imm), Operand::D16(imm) => write!(f, "${:04x}", imm), Operand::I8(imm) => { if *imm == -128 { write!(f, "-0x80") } else if *imm >= 0 { write!(f, "${:02x}", imm) } else { write!(f, "-${:02x}", -imm) } } Operand::R8(imm) => { if *imm == -128 { write!(f, "$-0x80") } else if *imm >= 0 { write!(f, "$+${:02x}", imm) } else { write!(f, "$-${:02x}", -imm) } } Operand::A16(addr) => write!(f, "[${:4x}]", addr), Operand::CondC => write!(f, "C"), Operand::CondNC => write!(f, "nC"), Operand::CondZ => write!(f, "Z"), Operand::CondNZ => write!(f, "nZ"), Operand::Nothing => write!(f, "nothing (BUG: should not be shown)"), } } } #[derive(Debug, Copy, Clone)] enum OperandSpec { A, B, C, D, E, H, L, AF, BC, DE, HL, SP, DerefHL, DerefBC, DerefDE, DerefDecHL, DerefIncHL, DerefHighC, DerefHighD8, SPWithOffset, D8, R8, I8, A16, D16, CondC, CondNC, CondZ, CondNZ, Bit(u8), Imm(u8), Nothing, } #[derive(Debug, Copy, Clone)] pub enum Opcode { NOP, LD, DEC, INC, ADC, ADD, SBC, SUB, AND, XOR, OR, CP, POP, PUSH, JP, JR, CALL, RET, RETI, HALT, RST, STOP, RLCA, RRCA, RLA, RRA, DAA, CPL, SCF, CCF, DI, EI, LDH, RLC, RRC, RL, RR, SLA, SRA, SWAP, SRL, BIT, RES, SET, } impl fmt::Display for Opcode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Opcode::NOP => write!(f, "nop"), Opcode::LD => write!(f, "ld"), Opcode::DEC => write!(f, "dec"), Opcode::INC => write!(f, "inc"), Opcode::ADC => write!(f, "adc"), Opcode::ADD => write!(f, "add"), Opcode::SBC => write!(f, "sbc"), Opcode::SUB => write!(f, "sub"), Opcode::AND => write!(f, "and"), Opcode::XOR => write!(f, "xor"), Opcode::OR => write!(f, "or"), Opcode::CP => write!(f, "cp"), Opcode::POP => write!(f, "pop"), Opcode::PUSH => write!(f, "push"), Opcode::JP => write!(f, "jp"), Opcode::JR => write!(f, "jr"), Opcode::CALL => write!(f, "call"), Opcode::RET => write!(f, "ret"), Opcode::RETI => write!(f, "reti"), Opcode::HALT => write!(f, "halt"), Opcode::RST => write!(f, "rst"), Opcode::STOP => write!(f, "stop"), Opcode::RLCA => write!(f, "rlca"), Opcode::RRCA => write!(f, "rrca"), Opcode::RLA => write!(f, "rla"), Opcode::RRA => write!(f, "rra"), Opcode::DAA => write!(f, "daa"), Opcode::CPL => write!(f, "cpl"), Opcode::SCF => write!(f, "scf"), Opcode::CCF => write!(f, "ccf"), Opcode::DI => write!(f, "di"), Opcode::EI => write!(f, "ei"), Opcode::LDH => write!(f, "ldh"), Opcode::RLC => write!(f, "rlc"), Opcode::RRC => write!(f, "rrc"), Opcode::RL => write!(f, "rl"), Opcode::RR => write!(f, "rr"), Opcode::SLA => write!(f, "sla"), Opcode::SRA => write!(f, "sra"), Opcode::SWAP => write!(f, "swap"), Opcode::SRL => write!(f, "srl"), Opcode::BIT => write!(f, "bit"), Opcode::RES => write!(f, "res"), Opcode::SET => write!(f, "set"), } } } #[derive(Debug)] pub struct InstDecoder { } impl Default for InstDecoder { fn default() -> Self { InstDecoder { } } } // main operand map is used for ld, arithmetic, and $CB-prefixed instructions const OPMAP: [OperandSpec; 8] = [ OperandSpec::B, OperandSpec::C, OperandSpec::D, OperandSpec::E, OperandSpec::H, OperandSpec::L, OperandSpec::DerefHL, OperandSpec::A, ]; const UPPER_INSTRUCTIONS: [(Option, [OperandSpec; 2]); 64] = [ // 0xc0 (Some(Opcode::RET), [OperandSpec::CondNZ, OperandSpec::Nothing]), (Some(Opcode::POP), [OperandSpec::BC, OperandSpec::Nothing]), (Some(Opcode::JP), [OperandSpec::CondNZ, OperandSpec::D16]), (Some(Opcode::JP), [OperandSpec::D16, OperandSpec::Nothing]), (Some(Opcode::CALL), [OperandSpec::CondNZ, OperandSpec::D16]), (Some(Opcode::PUSH), [OperandSpec::BC, OperandSpec::Nothing]), (Some(Opcode::ADD), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x00), OperandSpec::Nothing]), // 0xc8 (Some(Opcode::RET), [OperandSpec::CondZ, OperandSpec::Nothing]), (Some(Opcode::RET), [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::JP), [OperandSpec::CondNC, OperandSpec::D16]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::CALL), [OperandSpec::CondZ, OperandSpec::D16]), (Some(Opcode::CALL), [OperandSpec::D16, OperandSpec::Nothing]), (Some(Opcode::ADC), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x08), OperandSpec::Nothing]), // 0xd0 (Some(Opcode::RET), [OperandSpec::CondNC, OperandSpec::Nothing]), (Some(Opcode::POP), [OperandSpec::DE, OperandSpec::Nothing]), (Some(Opcode::JP), [OperandSpec::CondNC, OperandSpec::D16]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::CALL), [OperandSpec::CondNC, OperandSpec::D16]), (Some(Opcode::PUSH), [OperandSpec::DE, OperandSpec::Nothing]), (Some(Opcode::SUB), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x10), OperandSpec::Nothing]), // 0xd8 (Some(Opcode::RET), [OperandSpec::CondC, OperandSpec::Nothing]), (Some(Opcode::RETI), [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::JP), [OperandSpec::CondC, OperandSpec::D16]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::CALL), [OperandSpec::CondC, OperandSpec::D16]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::SBC), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x18), OperandSpec::Nothing]), // 0xe0 (Some(Opcode::LDH), [OperandSpec::DerefHighD8, OperandSpec::A]), (Some(Opcode::POP), [OperandSpec::HL, OperandSpec::Nothing]), (Some(Opcode::LDH), [OperandSpec::DerefHighC, OperandSpec::A]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::PUSH), [OperandSpec::HL, OperandSpec::Nothing]), (Some(Opcode::AND), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x20), OperandSpec::Nothing]), // 0xe8 (Some(Opcode::ADD), [OperandSpec::SP, OperandSpec::I8]), (Some(Opcode::JP), [OperandSpec::HL, OperandSpec::Nothing]), (Some(Opcode::LD), [OperandSpec::A16, OperandSpec::A]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::XOR), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x28), OperandSpec::Nothing]), // 0xf0 (Some(Opcode::LDH), [OperandSpec::A, OperandSpec::DerefHighD8]), (Some(Opcode::POP), [OperandSpec::AF, OperandSpec::Nothing]), (Some(Opcode::LDH), [OperandSpec::A, OperandSpec::DerefHighC]), (Some(Opcode::DI), [OperandSpec::Nothing, OperandSpec::Nothing]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::PUSH), [OperandSpec::AF, OperandSpec::Nothing]), (Some(Opcode::OR), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x30), OperandSpec::Nothing]), // 0xf8 (Some(Opcode::LD), [OperandSpec::HL, OperandSpec::SPWithOffset]), (Some(Opcode::LD), [OperandSpec::SP, OperandSpec::HL]), (Some(Opcode::LD), [OperandSpec::A, OperandSpec::A16]), (Some(Opcode::EI), [OperandSpec::Nothing, OperandSpec::Nothing]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (None, [OperandSpec::Nothing, OperandSpec::Nothing]), (Some(Opcode::CP), [OperandSpec::D8, OperandSpec::Nothing]), (Some(Opcode::RST), [OperandSpec::Imm(0x38), OperandSpec::Nothing]), ]; impl Decoder for InstDecoder { fn decode_into::Address, ::Word>>(&self, inst: &mut Instruction, words: &mut T) -> Result<(), ::DecodeError> { let opc: u8 = words.next()?; inst.length = 1; let high = ((opc >> 3) & 0b111) as usize; let low = (opc & 0b111) as usize; // early part of the table is more varied, more special cases if opc < 0x40 { let (opcode, operands) = match low { 0 => { // special case on high match high { 0 => { (Opcode::NOP, [OperandSpec::Nothing, OperandSpec::Nothing]) }, 1 => { (Opcode::LD, [OperandSpec::A16, OperandSpec::SP]) }, 2 => { (Opcode::STOP, [OperandSpec::Nothing, OperandSpec::Nothing]) }, 3 => { (Opcode::JR, [OperandSpec::R8, OperandSpec::Nothing]) }, 4 => { (Opcode::JR, [OperandSpec::CondNZ, OperandSpec::R8]) }, 5 => { (Opcode::JR, [OperandSpec::CondZ, OperandSpec::R8]) }, 6 => { (Opcode::JR, [OperandSpec::CondNC, OperandSpec::R8]) }, 7 => { (Opcode::JR, [OperandSpec::CondC, OperandSpec::R8]) }, _ => { unreachable!("impossible bit pattern"); } } } 1 => { // also special case, but fewer if high & 1 == 0 { let opcode = Opcode::LD; let ops = [[ OperandSpec::BC, OperandSpec::DE, OperandSpec::HL, OperandSpec::SP, ][high as usize >> 1], OperandSpec::D16]; (opcode, ops) } else { let opcode = Opcode::ADD; let ops = [OperandSpec::HL, [ OperandSpec::BC, OperandSpec::DE, OperandSpec::HL, OperandSpec::SP, ][high as usize >> 1]]; (opcode, ops) } } 2 => { let op = Opcode::LD; if high & 1 == 0 { let op0 = [ OperandSpec::DerefBC, OperandSpec::DerefDE, OperandSpec::DerefIncHL, OperandSpec::DerefDecHL, ][high as usize >> 1]; (op, [op0, OperandSpec::A]) } else { let op1 = [ OperandSpec::DerefBC, OperandSpec::DerefDE, OperandSpec::DerefIncHL, OperandSpec::DerefDecHL, ][high as usize >> 1]; (op, [OperandSpec::A, op1]) } } 3 => { if high & 1 == 0 { let op0 = [ OperandSpec::BC, OperandSpec::DE, OperandSpec::HL, OperandSpec::SP, ][high as usize >> 1]; (Opcode::INC, [op0, OperandSpec::Nothing]) } else { let op0 = [ OperandSpec::BC, OperandSpec::DE, OperandSpec::HL, OperandSpec::SP, ][high as usize >> 1]; (Opcode::DEC, [op0, OperandSpec::Nothing]) } } 4 => { let op = Opcode::INC; let op0 = [ OperandSpec::B, OperandSpec::C, OperandSpec::D, OperandSpec::E, OperandSpec::H, OperandSpec::L, OperandSpec::DerefHL, OperandSpec::A, ][high as usize]; (op, [op0, OperandSpec::Nothing]) } 5 => { let op = Opcode::DEC; let op0 = [ OperandSpec::B, OperandSpec::C, OperandSpec::D, OperandSpec::E, OperandSpec::H, OperandSpec::L, OperandSpec::DerefHL, OperandSpec::A, ][high as usize]; (op, [op0, OperandSpec::Nothing]) } 6 => { let op = Opcode::LD; let op0 = [ OperandSpec::B, OperandSpec::C, OperandSpec::D, OperandSpec::E, OperandSpec::H, OperandSpec::L, OperandSpec::DerefHL, OperandSpec::A, ][high as usize]; (op, [op0, OperandSpec::D8]) } 7 => { // special cases here too let op = [ Opcode::RLCA, Opcode::RRCA, Opcode::RLA, Opcode::RRA, Opcode::DAA, Opcode::CPL, Opcode::SCF, Opcode::CCF, ][high as usize]; (op, [OperandSpec::Nothing, OperandSpec::Nothing]) } _ => { unreachable!("impossible bit pattern"); } }; inst.opcode = opcode; interpret_operands(words, inst, operands)?; return Ok(()); } else if opc < 0x80 { if opc == 0x76 { inst.opcode = Opcode::HALT; inst.operands = [Operand::Nothing, Operand::Nothing]; return Ok(()); } else { inst.opcode = Opcode::LD; interpret_operands(words, inst, [OPMAP[high], OPMAP[low]])?; return Ok(()); } } else if opc < 0xc0 { const OPCODES: [Opcode; 8] = [ Opcode::ADD, Opcode::ADC, Opcode::SUB, Opcode::SBC, Opcode::AND, Opcode::XOR, Opcode::OR, Opcode::CP, ]; inst.opcode = OPCODES[high]; let operands = [OPMAP[low], OperandSpec::Nothing]; interpret_operands(words, inst, operands)?; return Ok(()); } else { if opc == 0xcb { // sm83 special CB-prefixed instructions let opc: u8 = words.next()?; inst.length += 1; if opc < 0x40 { let high = (opc >> 3) & 0b111; let low = opc & 0b111; const LOW_OP: [OperandSpec; 8] = [ OperandSpec::B, OperandSpec::C, OperandSpec::D, OperandSpec::E, OperandSpec::H, OperandSpec::L, OperandSpec::DerefHL, OperandSpec::A, ]; let operands = [LOW_OP[low as usize], OperandSpec::Nothing]; const OPCODES: [Opcode; 8] = [ Opcode::RLC, Opcode::RRC, Opcode::RL, Opcode::RR, Opcode::SLA, Opcode::SRA, Opcode::SWAP, Opcode::SRL, ]; inst.opcode = OPCODES[high as usize]; interpret_operands(words, inst, operands)?; return Ok(()); } else { let bit = (opc >> 3) & 0b111; let low = opc & 0b111; const LOW_OP: [OperandSpec; 8] = [ OperandSpec::B, OperandSpec::C, OperandSpec::D, OperandSpec::E, OperandSpec::H, OperandSpec::L, OperandSpec::DerefHL, OperandSpec::A, ]; let operands = [OperandSpec::Bit(bit), LOW_OP[low as usize]]; const OPCODES: [Opcode; 3] = [ Opcode::BIT, Opcode::RES, Opcode::SET, ]; inst.opcode = OPCODES[(opc >> 6) as usize - 1]; interpret_operands(words, inst, operands)?; return Ok(()); } } else { // there is no special thing we can do here. do a table lookup. let (maybe_opcode, operands) = UPPER_INSTRUCTIONS[opc as usize - 0xc0]; if let Some(opcode) = maybe_opcode { inst.opcode = opcode; interpret_operands(words, inst, operands)?; return Ok(()); } else { return Err(StandardDecodeError::InvalidOpcode); } } } } } fn interpret_operands::Address, ::Word>>(words: &mut T, inst: &mut Instruction, operands: [OperandSpec; 2]) -> Result<(), ::DecodeError> { inst.operands[0] = interpret_operand(words, inst, operands[0])?; inst.operands[1] = interpret_operand(words, inst, operands[1])?; Ok(()) } fn interpret_operand::Address, ::Word>>(words: &mut T, inst: &mut Instruction, operand: OperandSpec) -> Result::DecodeError> { let operand = match operand { OperandSpec::A => Operand::A, OperandSpec::B => Operand::B, OperandSpec::C => Operand::C, OperandSpec::D => Operand::D, OperandSpec::E => Operand::E, OperandSpec::H => Operand::H, OperandSpec::L => Operand::L, OperandSpec::AF => Operand::AF, OperandSpec::BC => Operand::BC, OperandSpec::DE => Operand::DE, OperandSpec::HL => Operand::HL, OperandSpec::SP => Operand::SP, OperandSpec::DerefHL => Operand::DerefHL, OperandSpec::DerefBC => Operand::DerefBC, OperandSpec::DerefDE => Operand::DerefDE, OperandSpec::DerefDecHL => Operand::DerefDecHL, OperandSpec::DerefIncHL => Operand::DerefIncHL, OperandSpec::D8 => { let imm = words.next()?; inst.length += 1; Operand::D8(imm) } OperandSpec::DerefHighC => { Operand::DerefHighC } OperandSpec::DerefHighD8 => { let imm = words.next()?; inst.length += 1; Operand::DerefHighD8(imm) } OperandSpec::R8 => { let imm = words.next()?; inst.length += 1; Operand::R8(imm as i8) } OperandSpec::I8 => { let imm = words.next()?; inst.length += 1; Operand::I8(imm as i8) } OperandSpec::A16 => { let imm = (words.next()? as u16) | ((words.next()? as u16) << 8); inst.length += 2; Operand::A16(imm as u16) } OperandSpec::D16 => { let imm = (words.next()? as u16) | ((words.next()? as u16) << 8); inst.length += 2; Operand::D16(imm as u16) } OperandSpec::SPWithOffset => { let imm = words.next()?; inst.length += 1; Operand::SPWithOffset(imm as i8) } OperandSpec::Imm(u) => { Operand::D8(u) } OperandSpec::Bit(u) => { Operand::Bit(u) } OperandSpec::CondC => Operand::CondC, OperandSpec::CondNC => Operand::CondNC, OperandSpec::CondZ => Operand::CondZ, OperandSpec::CondNZ => Operand::CondNZ, OperandSpec::Nothing => Operand::Nothing, }; Ok(operand) }