use regs::{GeneralPurposeRegister, GeneralPurposeRegisterPair, IORegister, StatusFlag, PC}; #[derive(Debug)] pub enum DecodingError { UndefinedInstruction(u16), TruncatedInstruction, } #[derive(Debug)] pub enum IncrementMode { None, // [Z] PreDecrement, // [-Z] PostIncrement, // [Z+] ConstantOffset(u8), // e.g. [Z+q] } #[derive(Debug)] pub enum Instruction { ADC(GeneralPurposeRegister, GeneralPurposeRegister), ADD(GeneralPurposeRegister, GeneralPurposeRegister), ADIW(GeneralPurposeRegisterPair, u16), AND(GeneralPurposeRegister, GeneralPurposeRegister), ANDI(GeneralPurposeRegister, u8), ASR(GeneralPurposeRegister), BLD(GeneralPurposeRegister, u8), BREAK, BR_IF(i8, StatusFlag, bool), BST(GeneralPurposeRegister, u8), CALL(PC), CBI(IORegister, u8), CLR_FLAG(StatusFlag), CLR(GeneralPurposeRegister), // TODO: Find CLR flag thingy COM(GeneralPurposeRegister), CP(GeneralPurposeRegister, GeneralPurposeRegister), CPC(GeneralPurposeRegister, GeneralPurposeRegister), CPI(GeneralPurposeRegister, u8), CPSE(GeneralPurposeRegister, GeneralPurposeRegister), DEC(GeneralPurposeRegister), EICALL, EIJMP, ELPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement EOR(GeneralPurposeRegister, GeneralPurposeRegister), FMUL(GeneralPurposeRegister, GeneralPurposeRegister), FMULS(GeneralPurposeRegister, GeneralPurposeRegister), FMULSU(GeneralPurposeRegister, GeneralPurposeRegister), ICALL, IJMP, IN(GeneralPurposeRegister, IORegister), INC(GeneralPurposeRegister), JMP(PC), LAC(GeneralPurposeRegister), LAS(GeneralPurposeRegister), LAT(GeneralPurposeRegister), LD(GeneralPurposeRegisterPair, GeneralPurposeRegister, IncrementMode), LDI(GeneralPurposeRegister, u8), LDS8(GeneralPurposeRegister, u8), // Two variants, with u8 and u16 LDS16(GeneralPurposeRegister, u16), LPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement LSL(GeneralPurposeRegister), LSR(GeneralPurposeRegister), MOV(GeneralPurposeRegister, GeneralPurposeRegister), MOVW(GeneralPurposeRegisterPair, GeneralPurposeRegisterPair), MUL(GeneralPurposeRegister, GeneralPurposeRegister), MULS(GeneralPurposeRegister, GeneralPurposeRegister), MULSU(GeneralPurposeRegister, GeneralPurposeRegister), NEG(GeneralPurposeRegister), NOP, OR(GeneralPurposeRegister, GeneralPurposeRegister), ORI(GeneralPurposeRegister, u8), OUT(IORegister, GeneralPurposeRegister), POP(GeneralPurposeRegister), PUSH(GeneralPurposeRegister), RCALL(i16), // These are relative and have only 12 bits. RET, RETI, RJMP(i16), // These are relative and have only 12 bits. ROL(GeneralPurposeRegister), ROR(GeneralPurposeRegister), SBC(GeneralPurposeRegister, GeneralPurposeRegister), SBCI(GeneralPurposeRegister, u8), SBI(IORegister, u8), SBIC(IORegister, u8), SBIS(IORegister, u8), SBIW(GeneralPurposeRegisterPair, u8), SBR(GeneralPurposeRegister, u8), SBRC(GeneralPurposeRegister, u8), SBRS(GeneralPurposeRegister, u8), SER(GeneralPurposeRegister), SET_FLAG(StatusFlag), // TODO how is it really called? (CLR) SLEEP, SPM(IncrementMode), // Only supports PostIncrement ST(GeneralPurposeRegisterPair, GeneralPurposeRegister, IncrementMode), STS8(u8, GeneralPurposeRegister), // Two variants, u8/u16 STS16(u16, GeneralPurposeRegister), SUB(GeneralPurposeRegister, GeneralPurposeRegister), SUBI(GeneralPurposeRegister, u8), SWAP(GeneralPurposeRegister), TST(GeneralPurposeRegister), WDR, XCH(GeneralPurposeRegister), } impl Instruction { // TODO: There are more // LDS32 STS32 pub fn size(&self) -> usize { match *self { Instruction::JMP(_) | Instruction::CALL(_) => 4, Instruction::STS16(_, _) | Instruction::LDS16(_, _) => 4, _ => 2 } } pub fn cycles(&self) -> usize { // TODO: Implement this correctly. 2 } } pub fn decode(data: &[u8]) -> Result { if data.len() < 2 { return Err(DecodingError::TruncatedInstruction); } // Try to match 2b instructions without any parameters first. let v: u16 = ((data[1] as u16) << 8) | (data[0] as u16); // Load second u16 as well if possible let v2: Option = if data.len() >= 4 { Some(((data[3] as u16) << 8) | (data[2] as u16)) } else { None }; // Status reg operations (set and clear bits). match v & 0b1111_1111_1000_1111 { 0b1001_0100_1000_1000 => { // CLR_FLAG TODO HOW IS IT CALLED let idx = ((v & 0b0000_0000_0111_0000) >> 4) as u8; return Ok(Instruction::CLR_FLAG(StatusFlag::try_from_idx(idx).unwrap())); }, 0b1001_0100_0000_1000 => { // SET_FLAG TODO HOW IS IT CALLED let idx = ((v & 0b0000_0000_0111_0000) >> 4) as u8; return Ok(Instruction::SET_FLAG(StatusFlag::try_from_idx(idx).unwrap())); } _ => {} } // Other instructions with no parameters. match v { 0b1001_0101_1001_1000 => return Ok(Instruction::BREAK), 0b1001_0101_0001_1001 => return Ok(Instruction::EICALL), 0b1001_0100_0001_1001 => return Ok(Instruction::EIJMP), 0b1001_0101_1101_1000 => return Ok(Instruction::ELPM(0.into(), IncrementMode::None)), 0b1001_0101_0000_1001 => return Ok(Instruction::ICALL), 0b1001_0100_0000_1001 => return Ok(Instruction::IJMP), 0b1001_0101_1100_1000 => return Ok(Instruction::LPM(0.into(), IncrementMode::None)), 0b0000_0000_0000_0000 => return Ok(Instruction::NOP), 0b1001_0101_0000_1000 => return Ok(Instruction::RET), 0b1001_0101_0001_1000 => return Ok(Instruction::RETI), 0b1001_0101_1000_1000 => return Ok(Instruction::SLEEP), 0b1001_0101_1110_1000 => return Ok(Instruction::SPM(IncrementMode::None)), 0b1001_0101_1111_1000 => return Ok(Instruction::SPM(IncrementMode::PostIncrement)), 0b1001_0101_1010_1000 => return Ok(Instruction::WDR), _ => {} } // Now things will get ugly, we have a lot of different prefix sizes for // different instructions. /* 0000_0011_1: 0000_0011_0: */ match v & 0b1111_1111_0000_0000 { 0b0000_0011_0000_0000 => { // FMUL/FMULS/FMULSU/MULSU let d = ((v & 0b0111_0000) >> 4) as u8; let r = ((v & 0b0111)) as u8; let mode = v & 0b1000_1000; match mode { 0b0000_0000 => return Ok(Instruction::MULSU((d + 16).into(), (r + 16).into())), 0b0000_1000 => return Ok(Instruction::FMUL((d + 16).into(), (r + 16).into())), 0b1000_0000 => return Ok(Instruction::FMULS((d + 16).into(), (r + 16).into())), 0b1000_1000 => return Ok(Instruction::FMULSU((d + 16).into(), (r + 16).into())), _ => unreachable!(), } } _ => {} } /* 10010110: '1001 0110 KKdd KKKK' 00000001: '0000 0001 dddd rrrr' 00000010: '0000 0010 dddd rrrr' 10010111: '1001 0111 KKdd KKKK' 11101111: '1110 1111 dddd 1111' 10011010: '1001 1010 AAAA Abbb' 10011011: '1001 1011 AAAA Abbb' 10011001: '1001 1001 AAAA Abbb' 10011000: '1001 1000 AAAA Abbb' */ { let K = (((v & 0b1100_0000) >> 2) | (v & 0b1111)) as u8; let d = (v & 0b1111_0000 >> 4) as u8; let r = (v & 0b1111) as u8; let A = ((v & 0b1111_1000) >> 3) as u16; let b = (v & 0b0111) as u8; match v & 0b1111_1111_0000_0000 { 0b1001_0110_0000_0000 => return Ok(Instruction::ADIW(((d & 0b11) * 2 + 24).into(), K as u16)), 0b0000_0001_0000_0000 => return Ok(Instruction::MOVW((d * 2).into(), (r * 2).into())), 0b0000_0010_0000_0000 => return Ok(Instruction::MULS((d + 16).into(), (r + 16).into())), 0b1001_0111_0000_0000 => return Ok(Instruction::SBIW(((d & 0b11) * 2 + 24).into(), K)), 0b1110_1111_0000_0000 => return Ok(Instruction::SER((d + 16).into())), 0b1001_1010_0000_0000 => return Ok(Instruction::SBI(A, b)), 0b1001_1011_0000_0000 => return Ok(Instruction::SBIS(A, b)), 0b1001_1001_0000_0000 => return Ok(Instruction::SBIC(A, b)), 0b1001_1000_0000_0000 => return Ok(Instruction::CBI(A, b)), _ => {}, } } /* 1001001: 1001000: */ // Handle LD{X,Y,Z}'s (none, incre and decre, just no offset) { if v & 0b1110_1100_0000_0000 == 0b1000_0000_0000_0000 { let dr = (((v & 0b1_1111_0000) >> 4) as u8).into(); // mode: 00 -> None, 01 -> PostIncrement, 10 -> PreDecrement let mode = v & 0b11; let do_store = v & 0b0010_0000_0000 != 0; match (do_store, v & 0b0001_0000_0000_0000 > 0, (v >> 2) & 0b11, mode) { // LD X (false, true, 0b11, 0b00) => return Ok(Instruction::LD(26.into(), dr, IncrementMode::None)), (false, true, 0b11, 0b01) => return Ok(Instruction::LD(26.into(), dr, IncrementMode::PostIncrement)), (false, true, 0b11, 0b10) => return Ok(Instruction::LD(26.into(), dr, IncrementMode::PreDecrement)), // LD Y (false, false, 0b10, 0b00) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::None)), (false, true, 0b10, 0b01) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::PostIncrement)), (false, true, 0b10, 0b10) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::PreDecrement)), // LD Z (false, false, 0b00, 0b00) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::None)), (false, true, 0b00, 0b01) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::PostIncrement)), (false, true, 0b00, 0b10) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::PreDecrement)), // ST X (true, true, 0b11, 0b00) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::None)), (true, true, 0b11, 0b01) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::PostIncrement)), (true, true, 0b11, 0b10) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::PreDecrement)), // ST Y (true, false, 0b10, 0b00) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::None)), (true, true, 0b10, 0b01) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::PostIncrement)), (true, true, 0b10, 0b10) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::PreDecrement)), // ST Z (true, false, 0b00, 0b00) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::None)), (true, true, 0b00, 0b01) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::PostIncrement)), (true, true, 0b00, 0b10) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::PreDecrement)), // POP: 1001 000d dddd 1111 (false, true, 0b11, 0b11) => return Ok(Instruction::POP(dr)), // PUSH: 1001 001d dddd 1111 (true, true, 0b11, 0b11) => return Ok(Instruction::PUSH(dr)), // LAC: 1001 001r rrrr 0110 (true, true, 0b01, 0b10) => return Ok(Instruction::LAC(dr)), // LAS: 1001 001r rrrr 0101 (true, true, 0b01, 0b01) => return Ok(Instruction::LAS(dr)), // LAT: 1001 001r rrrr 0111 (true, true, 0b01, 0b11) => return Ok(Instruction::LAT(dr)), // XCH: 1001 001r rrrr 0100 (true, true, 0b01, 0b00) => return Ok(Instruction::XCH(dr)), // ELPM (2) 1001 000d dddd 0110 (false, true, 0b01, 0b10) => return Ok(Instruction::ELPM(dr, IncrementMode::None)), // ELPM (3) 1001 000d dddd 0111 (false, true, 0b01, 0b11) => return Ok(Instruction::ELPM(dr, IncrementMode::PostIncrement)), // LPM (2): 1001 000d dddd 0100 (false, true, 0b01, 0b00) => return Ok(Instruction::LPM(dr, IncrementMode::None)), // LPM (3): 1001 000d dddd 0101 (false, true, 0b01, 0b01) => return Ok(Instruction::LPM(dr, IncrementMode::PostIncrement)), // TODO: LDS32 STS32 // STS32 1001 001r rrrr 0000 kkkk kkkk kkkk kkkk (true, true, 0, 0) => { //STS32 match v2 { Some(k) => return Ok(Instruction::STS16(k, dr)), None => return Err(DecodingError::TruncatedInstruction), } }, // LDS32 1001 000d dddd 0000 kkkk kkkk kkkk kkkk (false, true, 0, 0) => { //LDS32 match v2 { Some(k) => return Ok(Instruction::LDS16(dr, k)), None => return Err(DecodingError::TruncatedInstruction), } } _ => {} } } } //LDX1 1001 000d dddd 1100 //LDX2 1001 000d dddd 1101 //LDX3 1001 000d dddd 1110 //LDY1 1000 000d dddd 1000 //LDY2 1001 000d dddd 1001 //LDY3 1001 000d dddd 1010 //LDZ1 1000 000d dddd 0000 //LDZ2 1001 000d dddd 0001 //LDZ3 1001 000d dddd 0010 //STY1 1000 001r rrrr 1000 //STY2 1001 001r rrrr 1001 //STY3 1001 001r rrrr 1010 match v & 0b1111_1110_0000_0000 { 0b1111_1100_0000_0000 | 0b1111_1110_0000_0000 => { // 1111110: // 1111111: // 1111 110r rrrr 0bbb let r = (((v >> 4) & 0b1_1111) as u8).into(); let b = (v & 0b111) as u8; if (v & 0b1000) == 0 { if ((v >> 9) & 1) == 0 { return Ok(Instruction::SBRC(r, b)); } else { return Ok(Instruction::SBRS(r, b)); } } }, // BLD '1111 100d dddd 0bbb' // BST '1111 101d dddd 0bbb' 0b1111_1000_0000_0000 | 0b1111_1010_0000_0000 => { let d = (((v >> 4) & 0b1_1111) as u8).into(); let b = (v & 0b111) as u8; if (v & 0b1000) == 0 { if ((v >> 9) & 1) == 9 { return Ok(Instruction::BLD(d, b)); } else { return Ok(Instruction::BST(d, b)); } } } 0b1001_0100_0000_0000 => { /* 1001010: */ let d = (((v & 0b0000_0001_1111_0000) >> 4) as u8).into(); // ASR '1001 010d dddd 0101 // COM '1001 010d dddd 0000' // DEC '1001 010d dddd 1010' // INC '1001 010d dddd 0011' // LSR '1001 010d dddd 0110' // NEG '1001 010d dddd 0001' // ROR '1001 010d dddd 0111' // SWP '1001 010d dddd 0010' match v & 0b1111 { 0b0101 => return Ok(Instruction::ASR(d)), 0b0000 => return Ok(Instruction::COM(d)), 0b1010 => return Ok(Instruction::DEC(d)), 0b0011 => return Ok(Instruction::INC(d)), 0b0110 => return Ok(Instruction::LSR(d)), 0b0001 => return Ok(Instruction::NEG(d)), 0b0111 => return Ok(Instruction::ROR(d)), 0b0010 => return Ok(Instruction::SWAP(d)), // CALL 1001 010k kkkk 111k kkkk kkkk kkkk kkkk' // JMP '1001 010k kkkk 110k kkkk kkkk kkkk kkkk' 0b1100 | 0b1101 | 0b1110 | 0b1111 => { // u32 instruction match v2 { None => return Err(DecodingError::TruncatedInstruction), Some(kl) => { let k = (((v & 0b1111_0000) as u32) << 12) | (kl as u32); if v & 0b10 == 0 { return Ok(Instruction::JMP(k)); } else { return Ok(Instruction::CALL(k)); } } } } _ => {}, } } _ => {}, } { let d5 = (((v >> 4) & 0b1_1111) as u8).into(); let r5 = (((v & 0b1111) | ((v >> 5) & 0b1_0000)) as u8).into(); match v & 0b1111_1100_0000_0000 { 0b0001_1000_0000_0000 => return Ok(Instruction::SUB(d5, r5)), 0b0001_1100_0000_0000 => { if d5 == r5 { return Ok(Instruction::ROL(d5)); } else { return Ok(Instruction::ADC(d5, r5)); } }, 0b0000_1100_0000_0000 => { if d5 == r5 { return Ok(Instruction::LSL(d5)); } else { return Ok(Instruction::ADD(d5, r5)); } }, 0b0000_1000_0000_0000 => return Ok(Instruction::SBC(d5, r5)), 0b0000_0100_0000_0000 => return Ok(Instruction::CPC(d5, r5)), 0b1001_1100_0000_0000 => return Ok(Instruction::MUL(d5, r5)), 0b0001_0100_0000_0000 => return Ok(Instruction::CP(d5, r5)), 0b0001_0000_0000_0000 => return Ok(Instruction::CPSE(d5, r5)), 0b0010_0100_0000_0000 => { if d5 == r5 { return Ok(Instruction::CLR(d5)); } else { return Ok(Instruction::EOR(d5, r5)); } } 0b0010_0000_0000_0000 => { if d5 == r5 { return Ok(Instruction::TST(d5)); } else { return Ok(Instruction::AND(d5, r5)); } } 0b0010_1000_0000_0000 => return Ok(Instruction::OR(d5, r5)), 0b0010_1100_0000_0000 => return Ok(Instruction::MOV(d5, r5)), // Branch instructions // 0b1111_0000_0000_0000 => // 0b1111_0100_0000_0000 => 0b1111_0100_0000_0000 | 0b1111_0000_0000_0000 => { let flag_should_be_set = ((v >> 10) & 1) == 1; let flag = StatusFlag::try_from_idx((v & 0b111) as u8).unwrap(); let mut k = (v >> 3) as i8; k = if k > (1 << 7) { (1 << 7) - k } else { k }; return Ok(Instruction::BR_IF(k, flag, flag_should_be_set)); } _ => {} } } { // OUT 1011 1AAr rrrr AAAA // IN '1011 0AAd dddd AAAA let r = (((v >> 4) & 0b1_1111) as u8).into(); let A = ((((v >> 5) & 0b11_0000) | (v & 0b1111)) as u8).into(); // STS return '1010 1kkk rrrr kkkk'.replace(' ', '') // LDS return '1010 0kkk dddd kkkk'.replace(' ', '') let r16 = (((v >> 4) & 0b1111) as u8).into(); let k = (((v >> 4) & 0b111_0000) | (v & 0b1111)) as u8; match v & 0b1111_1000_0000_0000 { 0b1011_1000_0000_0000 => return Ok(Instruction::OUT(A, r)), 0b1011_0000_0000_0000 => return Ok(Instruction::IN(r, A)), 0b1010_1000_0000_0000 => return Ok(Instruction::STS8(k, r16)), 0b1010_0000_0000_0000 => return Ok(Instruction::LDS8(r16, k)), _ => {} } } { // Rcall/jmp: 1101 kkkk kkkk kkkk // SBR 0110 KKKK dddd KKKK <- seems invalid!!! // ORI: 0110 KKKK dddd KKKK // ANDI 0111 KKKK dddd KKKK // CPI 0011 KKKK dddd KKKK // SUBI 0101 KKKK dddd KKKK // SBCI 0100 KKKK dddd KKKK // LDI 1110 KKKK dddd KKKK // 0110: // 0111: // 0011: // 0101: // 0100: // 1110: // 1100: // 1101: let k = (v & 0b0000_1111_1111_1111) as i16; let k: i16 = if (k & 0b1000_0000_0000) == 0b1000_0000_0000 { k - 0b1_0000_0000_0000 } else { k }; let K = (((v >> 4) & 0b1111_0000) | (v & 0b1111)) as u8; let d = (((v >> 4) & 0b1111) as u8 + 16u8).into(); match v & 0b1111_0000_0000_0000 { 0b0110_0000_0000_0000 => return Ok(Instruction::ORI(d, K)), 0b0111_0000_0000_0000 => return Ok(Instruction::ANDI(d, K)), 0b0011_0000_0000_0000 => return Ok(Instruction::CPI(d, K)), 0b0101_0000_0000_0000 => return Ok(Instruction::SUBI(d, K)), 0b0100_0000_0000_0000 => return Ok(Instruction::SBCI(d, K)), 0b1110_0000_0000_0000 => return Ok(Instruction::LDI(d, K)), 0b1100_0000_0000_0000 => return Ok(Instruction::RJMP(k)), 0b1101_0000_0000_0000 => return Ok(Instruction::RCALL(k)), _ => {} } } // return '10q0 qq0d dddd 1qqq'.replace(' ', '') LD Y IV // return '10q0 qq0d dddd 0qqq'.replace(' ', '') LD Z IV // return "10q0 qq1r rrrr 1qqq".replace(' ', '') ST Y IV // return "10q0 qq1r rrrr 0qqq".replace(' ', '') ST Z IV if (v >> 12) & 0b1101 == 0b1000 { let w = ((v >> 10) & 1) == 1; let dr = (((v >> 4) & 0b11111) as u8).into(); let q = (((v >> 8) & 0b10_0000) | ((v >> 7) & 0b1_1000) | (v & 0b111)) as u8; match (v & 0b1000, w) { (0b1000, false) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::ConstantOffset(q))), (0b1000, true) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::ConstantOffset(q))), (0b0000, false) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::ConstantOffset(q))), (0b0000, true) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::ConstantOffset(q))), _ => {}, } } Err(DecodingError::UndefinedInstruction(v)) }