// SPDX-License-Identifier: MIT // Copyright Murad Karammaev, Nikita Kuzmin use ux::{u2, u3, u4}; #[derive(Debug, PartialEq, Clone)] pub enum Register { R0, R1, } #[derive(Debug, PartialEq, Clone)] pub enum ShiftMode { Logical, Circular, } #[derive(Debug, PartialEq)] pub enum Instruction { Load { reg: Register, addr: u4, }, Store { reg: Register, addr: u4, }, LoadImmediate { low: bool, reg: Register, val: u4, }, NearJumpBackward { use_carry: bool, offset: u2, }, NearJumpForward { use_carry: bool, offset: u3, }, FarJump { reg: Register, use_carry: bool, }, CmpEqual, CmpGreater, CmpLess, Not, BitShift { right: bool, reg: Register, mode: ShiftMode, len: u3, }, Inc { reg: Register, }, Dec { reg: Register, }, Add, Sub, And, Or, Xor, Xnor, Complement { reg: Register, }, BitShiftVar { right: bool, mode: ShiftMode, }, Copy { reg_from: Register, }, Setc { carry: bool, }, Nop, Halt, Cload { reg: Register, }, Zero { reg: Register, }, Div, Mul, Swap, PortReady, ReadPort, WritePort, } pub fn decode(insn: u8) -> Instruction { match insn { 0b00000000..=0b00111111 => { let reg = if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 }; let addr = u4::new(insn & 0b00001111); match insn & 0b00100000 { 0 => Instruction::Load { reg, addr }, _ => Instruction::Store { reg, addr }, } } 0b01000000..=0b01111111 => Instruction::LoadImmediate { low: insn & 0b00100000 == 0, reg: if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 }, val: u4::new(insn & 0b00001111), }, 0b10001000..=0b10001111 => Instruction::NearJumpBackward { use_carry: insn & 0b00000100 != 0, offset: u2::new(insn & 0b00000011), }, 0b10010000..=0b10011111 => Instruction::NearJumpForward { use_carry: insn & 0b00001000 != 0, offset: u3::new(insn & 0b00000111), }, 0b10000000..=0b10000011 => Instruction::FarJump { reg: if insn & 0b00000010 == 0 { Register::R0 } else { Register::R1 }, use_carry: insn & 0b00000001 != 0, }, 0b10000100 => Instruction::CmpEqual, 0b10000101 => Instruction::CmpGreater, 0b10000110 => Instruction::CmpLess, 0b10000111 => Instruction::Not, 0b10100000..=0b10111111 => Instruction::BitShift { right: true, reg: if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 }, mode: if insn & 0b00001000 == 0 { ShiftMode::Logical } else { ShiftMode::Circular }, len: u3::new(insn & 0b00000111), }, 0b11000000..=0b11011111 => Instruction::BitShift { right: false, reg: if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 }, mode: if insn & 0b00001000 == 0 { ShiftMode::Logical } else { ShiftMode::Circular }, len: u3::new(insn & 0b00000111), }, 0b11100000..=0b11100001 => Instruction::Inc { reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 }, }, 0b11100010..=0b11100011 => Instruction::Dec { reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 }, }, 0b11100100 => Instruction::Add, 0b11100101 => Instruction::Sub, 0b11100110 => Instruction::And, 0b11100111 => Instruction::Or, 0b11101000 => Instruction::Xor, 0b11101001 => Instruction::Xnor, 0b11101010..=0b11101011 => Instruction::Complement { reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 }, }, 0b11101100..=0b11101111 => Instruction::BitShiftVar { right: insn & 0b00000010 == 0, mode: if insn & 0b00000001 == 0 { ShiftMode::Logical } else { ShiftMode::Circular }, }, 0b11110000..=0b11110001 => Instruction::Copy { reg_from: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 }, }, 0b11110010..=0b11110011 => Instruction::Setc { carry: insn & 0b00000001 != 0, }, 0b11110100 => Instruction::Nop, 0b11110101 => Instruction::Halt, 0b11110110..=0b11110111 => Instruction::Cload { reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 }, }, 0b11111000..=0b11111001 => Instruction::Zero { reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 }, }, 0b11111010 => Instruction::Div, 0b11111011 => Instruction::Mul, 0b11111100 => Instruction::Swap, 0b11111101 => Instruction::PortReady, 0b11111110 => Instruction::ReadPort, 0b11111111 => Instruction::WritePort, } } pub fn encode(insn: Instruction) -> u8 { match insn { Instruction::Load { reg, addr } => match reg { Register::R0 => u8::from(addr), Register::R1 => u8::from(addr) | 0b00010000, }, Instruction::Store { reg, addr } => match reg { Register::R0 => 0b00100000 | u8::from(addr), Register::R1 => 0b00110000 | u8::from(addr), }, Instruction::LoadImmediate { low, reg, val } => { let mut ret = 0b01000000u8; ret |= match low { true => 0, false => 0b00100000, }; ret |= match reg { Register::R0 => 0, Register::R1 => 0b00010000, }; ret | u8::from(val) } Instruction::NearJumpBackward { use_carry, offset } => match use_carry { false => 0b10001000 | u8::from(offset), true => 0b10001100 | u8::from(offset), }, Instruction::NearJumpForward { use_carry, offset } => match use_carry { false => 0b10010000 | u8::from(offset), true => 0b10011000 | u8::from(offset), }, Instruction::FarJump { reg, use_carry } => match reg { Register::R0 => 0b10000000 | if use_carry { 1 } else { 0 }, Register::R1 => 0b10000010 | if use_carry { 1 } else { 0 }, }, Instruction::CmpEqual => 0b10000100, Instruction::CmpGreater => 0b10000101, Instruction::CmpLess => 0b10000110, Instruction::Not => 0b10000111, Instruction::BitShift { right, reg, mode, len, } => { let mut ret = if right { 0b10100000 } else { 0b11000000 }; ret |= match reg { Register::R0 => 0, Register::R1 => 0b00010000, }; ret |= match mode { ShiftMode::Logical => 0, ShiftMode::Circular => 0b00001000, }; ret | u8::from(len) } Instruction::Inc { reg } => match reg { Register::R0 => 0b11100000, Register::R1 => 0b11100001, }, Instruction::Dec { reg } => match reg { Register::R0 => 0b11100010, Register::R1 => 0b11100011, }, Instruction::Add => 0b11100100, Instruction::Sub => 0b11100101, Instruction::And => 0b11100110, Instruction::Or => 0b11100111, Instruction::Xor => 0b11101000, Instruction::Xnor => 0b11101001, Instruction::Complement { reg } => match reg { Register::R0 => 0b11101010, Register::R1 => 0b11101011, }, Instruction::BitShiftVar { right, mode } => { let mut ret = 0b11101100; ret |= if right { 0 } else { 0b00000010 }; ret |= match mode { ShiftMode::Logical => 0, ShiftMode::Circular => 1, }; ret } Instruction::Copy { reg_from } => match reg_from { Register::R0 => 0b11110000, Register::R1 => 0b11110001, }, Instruction::Setc { carry } => 0b11110010 | u8::from(carry), Instruction::Nop => 0b11110100, Instruction::Halt => 0b11110101, Instruction::Cload { reg } => match reg { Register::R0 => 0b11110110, Register::R1 => 0b11110111, }, Instruction::Zero { reg } => match reg { Register::R0 => 0b11111000, Register::R1 => 0b11111001, }, Instruction::Div => 0b11111010, Instruction::Mul => 0b11111011, Instruction::Swap => 0b11111100, Instruction::PortReady => 0b11111101, Instruction::ReadPort => 0b11111110, Instruction::WritePort => 0b11111111, } } // This crate really needs more tests, but I can't be bothered #[cfg(test)] mod tests { use super::{decode, encode}; #[test] fn deterministic_instruction_encoding() { for i in 0..=255u8 { assert_eq!(i, encode(decode(i))); } } }