use crate::avr::avr_instr_info::AvrOpcode;
pub struct AvrMCEncoder;
impl AvrMCEncoder {
pub fn new() -> Self {
AvrMCEncoder
}
pub fn encode(opcode: AvrOpcode, rd: u8, rr: u8, imm: u16) -> Vec<u8> {
match opcode {
AvrOpcode::ADD => {
let word: u16 = 0x0C00
| ((rd as u16 & 0x1F) << 4)
| ((rd as u16 & 0x10) << 5)
| (rr as u16 & 0x0F)
| ((rr as u16 & 0x10) << 5);
let w = 0x0C00
| (((rd & 0x1F) as u16) << 4)
| (((rd & 0x10) as u16) << 5)
| ((rr & 0x0F) as u16)
| (((rr & 0x10) as u16) << 5);
vec![w as u8, (w >> 8) as u8]
}
AvrOpcode::SUB => {
let word: u16 = encode_two_reg(0x1800, rd, rr);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::AND => {
let word: u16 = encode_two_reg(0x2000, rd, rr);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::OR => {
let word: u16 = encode_two_reg(0x2800, rd, rr);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::EOR => {
let word: u16 = encode_two_reg(0x2400, rd, rr);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::INC => {
let word: u16 = 0x9403 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::DEC => {
let word: u16 = 0x940A | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::NEG => {
let word: u16 = 0x9401 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::COM => {
let word: u16 = 0x9400 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::LSL => {
let word: u16 = encode_two_reg(0x0C00, rd, rd);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::LSR => {
let word: u16 = 0x9406 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::ROR => {
let word: u16 = 0x9407 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::ASR => {
let word: u16 = 0x9405 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::SWAP => {
let word: u16 = 0x9402 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::MOV => {
let word: u16 = encode_two_reg(0x2C00, rd, rr);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::LDI => {
if rd < 16 {
return vec![0, 0]; }
let k = imm as u8;
let word: u16 = 0xE000u16
| ((k as u16 & 0xF0u16) << 4)
| (((rd - 16) & 0x0F) as u16) << 4
| (k & 0x0F) as u16;
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::NOP => vec![0, 0],
AvrOpcode::RJMP => {
let k = (imm as i16) & 0xFFF;
let word: u16 = 0xC000 | (k as u16 & 0xFFF);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::IJMP => vec![0x09, 0x94],
AvrOpcode::JMP => {
let addr = imm as u32 & 0x3FFFFF;
let word1: u16 =
0x940C | (((addr >> 18) & 0x3F) as u16) << 3 | ((addr >> 16) as u16 & 0x01);
let word2: u16 = (addr & 0xFFFF) as u16;
vec![
word1 as u8,
(word1 >> 8) as u8,
word2 as u8,
(word2 >> 8) as u8,
]
}
AvrOpcode::CALL => {
let addr = imm as u32 & 0x3FFFFF;
let word1: u16 =
0x940E | (((addr >> 18) & 0x3F) as u16) << 3 | ((addr >> 16) as u16 & 0x01);
let word2: u16 = (addr & 0xFFFF) as u16;
vec![
word1 as u8,
(word1 >> 8) as u8,
word2 as u8,
(word2 >> 8) as u8,
]
}
AvrOpcode::RCALL => {
let k = (imm as i16) & 0xFFF;
let word: u16 = 0xD000 | (k as u16 & 0xFFF);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::RET => vec![0x08, 0x95],
AvrOpcode::RETI => vec![0x18, 0x95],
AvrOpcode::IN => {
let a = imm as u8 & 0x3F;
let word: u16 = 0xB000u16
| ((a as u16 & 0x30u16) << 5)
| ((rd as u16 & 0x1Fu16) << 4)
| (a & 0x0F) as u16;
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::OUT => {
let a = imm as u8 & 0x3F;
let word: u16 = 0xB800u16
| ((a as u16 & 0x30u16) << 5)
| ((rr as u16 & 0x1Fu16) << 4)
| (a & 0x0F) as u16;
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::PUSH => {
let word: u16 = 0x920F | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::POP => {
let word: u16 = 0x900F | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::SBI => {
let a = imm as u8 & 0x1F;
let b = rd as u8 & 0x07;
let word: u16 = 0x9A00 | (a as u16) << 3 | (b as u16);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::CBI => {
let a = imm as u8 & 0x1F;
let b = rd as u8 & 0x07;
let word: u16 = 0x9800 | (a as u16) << 3 | (b as u16);
vec![word as u8, (word >> 8) as u8]
}
AvrOpcode::LDS => {
let addr = imm;
let word: u16 = 0x9000 | ((rd as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8, addr as u8, (addr >> 8) as u8]
}
AvrOpcode::STS => {
let addr = imm;
let word: u16 = 0x9200 | ((rr as u16 & 0x1F) << 4);
vec![word as u8, (word >> 8) as u8, addr as u8, (addr >> 8) as u8]
}
AvrOpcode::BREAK => vec![0x98, 0x95],
AvrOpcode::SLEEP => vec![0x88, 0x95],
AvrOpcode::WDR => vec![0xA8, 0x95],
_ => vec![0, 0],
}
}
pub fn instr_size(opcode: AvrOpcode) -> u8 {
match opcode {
AvrOpcode::JMP | AvrOpcode::CALL | AvrOpcode::LDS | AvrOpcode::STS => 4,
_ => 2,
}
}
pub fn is_32bit(opcode: AvrOpcode) -> bool {
Self::instr_size(opcode) == 4
}
pub fn encode_sequence(opcodes: &[(AvrOpcode, u8, u8, u16)]) -> Vec<u8> {
let mut bytes = Vec::new();
for &(op, rd, rr, imm) in opcodes {
bytes.extend(Self::encode(op, rd, rr, imm));
}
bytes
}
}
fn encode_two_reg(base: u16, rd: u8, rr: u8) -> u16 {
base | ((rd as u16 & 0x10) << 5)
| ((rd as u16 & 0x0F) << 4)
| ((rr as u16 & 0x10) << 1)
| (rr as u16 & 0x0F)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_nop() {
let bytes = AvrMCEncoder::encode(AvrOpcode::NOP, 0, 0, 0);
assert_eq!(bytes, vec![0x00, 0x00]);
}
#[test]
fn test_encode_ret() {
let bytes = AvrMCEncoder::encode(AvrOpcode::RET, 0, 0, 0);
assert_eq!(bytes, vec![0x08, 0x95]);
}
#[test]
fn test_encode_reti() {
let bytes = AvrMCEncoder::encode(AvrOpcode::RETI, 0, 0, 0);
assert_eq!(bytes, vec![0x18, 0x95]);
}
#[test]
fn test_encode_rjmp_zero_offset() {
let bytes = AvrMCEncoder::encode(AvrOpcode::RJMP, 0, 0, 0);
assert_eq!(bytes, vec![0x00, 0xC0]);
}
#[test]
fn test_encode_ijmp() {
let bytes = AvrMCEncoder::encode(AvrOpcode::IJMP, 0, 0, 0);
assert_eq!(bytes, vec![0x09, 0x94]);
}
#[test]
fn test_encode_break() {
let bytes = AvrMCEncoder::encode(AvrOpcode::BREAK, 0, 0, 0);
assert_eq!(bytes, vec![0x98, 0x95]);
}
#[test]
fn test_encode_sleep() {
let bytes = AvrMCEncoder::encode(AvrOpcode::SLEEP, 0, 0, 0);
assert_eq!(bytes, vec![0x88, 0x95]);
}
#[test]
fn test_encode_wdr() {
let bytes = AvrMCEncoder::encode(AvrOpcode::WDR, 0, 0, 0);
assert_eq!(bytes, vec![0xA8, 0x95]);
}
#[test]
fn test_encode_mov_r16_r16() {
let bytes = AvrMCEncoder::encode(AvrOpcode::MOV, 16, 16, 0);
assert_eq!(bytes, vec![0x20, 0x2E]);
}
#[test]
fn test_encode_push_r0() {
let bytes = AvrMCEncoder::encode(AvrOpcode::PUSH, 0, 0, 0);
assert_eq!(bytes, vec![0x0F, 0x92]);
}
#[test]
fn test_encode_pop_r0() {
let bytes = AvrMCEncoder::encode(AvrOpcode::POP, 0, 0, 0);
assert_eq!(bytes, vec![0x0F, 0x90]);
}
#[test]
fn test_instr_size_16bit() {
assert_eq!(AvrMCEncoder::instr_size(AvrOpcode::ADD), 2);
assert_eq!(AvrMCEncoder::instr_size(AvrOpcode::NOP), 2);
assert_eq!(AvrMCEncoder::instr_size(AvrOpcode::RJMP), 2);
}
#[test]
fn test_instr_size_32bit() {
assert_eq!(AvrMCEncoder::instr_size(AvrOpcode::JMP), 4);
assert_eq!(AvrMCEncoder::instr_size(AvrOpcode::CALL), 4);
assert_eq!(AvrMCEncoder::instr_size(AvrOpcode::LDS), 4);
assert_eq!(AvrMCEncoder::instr_size(AvrOpcode::STS), 4);
}
#[test]
fn test_is_32bit() {
assert!(!AvrMCEncoder::is_32bit(AvrOpcode::ADD));
assert!(AvrMCEncoder::is_32bit(AvrOpcode::JMP));
assert!(AvrMCEncoder::is_32bit(AvrOpcode::CALL));
}
#[test]
fn test_encode_call_addr() {
let bytes = AvrMCEncoder::encode(AvrOpcode::CALL, 0, 0, 0x1000);
assert_eq!(bytes.len(), 4);
assert_eq!(bytes[2], 0x00);
assert_eq!(bytes[3], 0x10);
}
#[test]
fn test_encode_jmp_addr() {
let bytes = AvrMCEncoder::encode(AvrOpcode::JMP, 0, 0, 0x2000);
assert_eq!(bytes.len(), 4);
}
#[test]
fn test_encode_ldi_r16() {
let bytes = AvrMCEncoder::encode(AvrOpcode::LDI, 16, 0, 0xAB);
assert_eq!(bytes[0], 0x0B);
assert_eq!(bytes[1], 0xEA);
}
#[test]
fn test_encode_ldi_invalid_rd() {
let bytes = AvrMCEncoder::encode(AvrOpcode::LDI, 0, 0, 0xFF);
assert_eq!(bytes, vec![0, 0]);
}
#[test]
fn test_encode_sequence() {
let seq = vec![(AvrOpcode::NOP, 0, 0, 0), (AvrOpcode::RET, 0, 0, 0)];
let bytes = AvrMCEncoder::encode_sequence(&seq);
assert_eq!(bytes, vec![0x00, 0x00, 0x08, 0x95]);
}
#[test]
fn test_encode_sub() {
let bytes = AvrMCEncoder::encode(AvrOpcode::SUB, 16, 17, 0);
assert_eq!(bytes.len(), 2);
}
#[test]
fn test_encode_add() {
let bytes = AvrMCEncoder::encode(AvrOpcode::ADD, 0, 1, 0);
assert_eq!(bytes.len(), 2);
}
#[test]
fn test_encode_neg() {
let bytes = AvrMCEncoder::encode(AvrOpcode::NEG, 0, 0, 0);
assert_eq!(bytes, vec![0x01, 0x94]);
}
#[test]
fn test_encode_com() {
let bytes = AvrMCEncoder::encode(AvrOpcode::COM, 0, 0, 0);
assert_eq!(bytes, vec![0x00, 0x94]);
}
}