foxpy
2 years ago
8 changed files with 925 additions and 19 deletions
@ -1 +1,2 @@ |
|||||
target/ |
target/ |
||||
|
.idea/ |
||||
|
@ -0,0 +1,717 @@ |
|||||
|
// SPDX-License-Identifier: MIT
|
||||
|
// Copyright Murad Karammaev, Nikita Kuzmin
|
||||
|
|
||||
|
use crate::instruction::{decode, encode, Instruction, Register, ShiftMode}; |
||||
|
use regex::Regex; |
||||
|
use std::error::Error; |
||||
|
use ux::{u2, u3, u4}; |
||||
|
|
||||
|
type InstructionDecoder = fn(&str, &Regex) -> Result<Instruction, Box<dyn Error>>; |
||||
|
|
||||
|
const REGEX_REG: &str = r"(R[01])"; |
||||
|
const REGEX_REG_HALF: &str = r"(R[01]\.[lh])"; |
||||
|
const REGEX_NUM: &str = r"(\d{1,3}|0b[01]{1,8}|0o[0-7]{1,3}|0x[\da-fA-F]{1,2})"; |
||||
|
const REGEX_INUM: &str = r"(-?\d{1,3}|-?0b[01]{1,8}|-?0o[0-7]{1,3}|-?0x[\da-fA-F]{1,2})"; |
||||
|
const REGEX_ADDR: &str = r"\[(\d{1,3}|0b[01]{1,8}|0o[0-7]{1,3}|0x[\da-fA-F]{1,2})\]"; |
||||
|
const REGEX_COMMA: &str = r"\s*,\s*"; |
||||
|
const REGEX_CARRY: &str = r"([01])"; |
||||
|
|
||||
|
macro_rules! asm_entry { |
||||
|
($regex:expr, $handler:expr) => { |
||||
|
(Regex::new(&$regex).unwrap(), $handler) |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
pub struct Assembler { |
||||
|
table: Vec<(Regex, InstructionDecoder)>, |
||||
|
} |
||||
|
|
||||
|
impl Assembler { |
||||
|
pub fn new() -> Self { |
||||
|
Assembler { |
||||
|
table: vec![ |
||||
|
asm_entry!(format!(r"^BYTE\s{}$", REGEX_NUM), parse_byte), |
||||
|
asm_entry!( |
||||
|
format!(r"^MOV\s{}{}{}$", REGEX_REG, REGEX_COMMA, REGEX_ADDR), |
||||
|
parse_mov_reg_mem |
||||
|
), |
||||
|
asm_entry!( |
||||
|
format!(r"^MOV\s{}{}{}$", REGEX_ADDR, REGEX_COMMA, REGEX_REG), |
||||
|
parse_mov_mem_reg |
||||
|
), |
||||
|
asm_entry!( |
||||
|
format!(r"^MOV\s{}{}{}$", REGEX_REG_HALF, REGEX_COMMA, REGEX_NUM), |
||||
|
parse_mov_reg_half_imm |
||||
|
), |
||||
|
asm_entry!( |
||||
|
format!(r"^MOV\s{}{}{}", REGEX_REG, REGEX_COMMA, REGEX_REG), |
||||
|
parse_mov_reg_reg |
||||
|
), |
||||
|
asm_entry!(format!(r"^JMP\s{}$", REGEX_INUM), parse_jmp), |
||||
|
asm_entry!(format!(r"^JMPC\s{}$", REGEX_INUM), parse_jmpc), |
||||
|
asm_entry!(format!(r"^AJMP\s{}$", REGEX_REG), parse_ajmp), |
||||
|
asm_entry!(format!(r"^AJMPC\s{}$", REGEX_REG), parse_ajmpc), |
||||
|
asm_entry!(r"^EQ$", |_, _| Ok(Instruction::CmpEqual)), |
||||
|
asm_entry!(r"^GT$", |_, _| Ok(Instruction::CmpGreater)), |
||||
|
asm_entry!(r"^LE$", |_, _| Ok(Instruction::CmpLess)), |
||||
|
asm_entry!(r"^NOT$", |_, _| Ok(Instruction::Not)), |
||||
|
asm_entry!(r"^SHR$", |_, _| Ok(Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Logical, |
||||
|
right: true |
||||
|
})), |
||||
|
asm_entry!(r"^SHL$", |_, _| Ok(Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Logical, |
||||
|
right: false |
||||
|
})), |
||||
|
asm_entry!(r"^ROTR$", |_, _| Ok(Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Circular, |
||||
|
right: true |
||||
|
})), |
||||
|
asm_entry!(r"^ROTL$", |_, _| Ok(Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Circular, |
||||
|
right: false |
||||
|
})), |
||||
|
asm_entry!( |
||||
|
format!(r"^SHR\s{}{}{}$", REGEX_REG, REGEX_COMMA, REGEX_NUM), |
||||
|
parse_shr |
||||
|
), |
||||
|
asm_entry!( |
||||
|
format!(r"^SHL\s{}{}{}$", REGEX_REG, REGEX_COMMA, REGEX_NUM), |
||||
|
parse_shl |
||||
|
), |
||||
|
asm_entry!( |
||||
|
format!(r"^ROTR\s{}{}{}$", REGEX_REG, REGEX_COMMA, REGEX_NUM), |
||||
|
parse_rotr |
||||
|
), |
||||
|
asm_entry!( |
||||
|
format!(r"^ROTL\s{}{}{}$", REGEX_REG, REGEX_COMMA, REGEX_NUM), |
||||
|
parse_rotl |
||||
|
), |
||||
|
asm_entry!(format!(r"^INC\s{}$", REGEX_REG), parse_inc), |
||||
|
asm_entry!(format!(r"^DEC\s{}$", REGEX_REG), parse_dec), |
||||
|
asm_entry!(r"^ADD$", |_, _| Ok(Instruction::Add)), |
||||
|
asm_entry!(r"^SUB$", |_, _| Ok(Instruction::Sub)), |
||||
|
asm_entry!(r"^AND$", |_, _| Ok(Instruction::And)), |
||||
|
asm_entry!(r"^OR$", |_, _| Ok(Instruction::Or)), |
||||
|
asm_entry!(r"^XOR$", |_, _| Ok(Instruction::Xor)), |
||||
|
asm_entry!(r"^XNOR$", |_, _| Ok(Instruction::Xnor)), |
||||
|
asm_entry!(format!(r"^COMPL\s{}$", REGEX_REG), parse_compl), |
||||
|
asm_entry!(format!(r"^SETC\s{}$", REGEX_CARRY), parse_setc), |
||||
|
asm_entry!(r"^NOP$", |_, _| Ok(Instruction::Nop)), |
||||
|
asm_entry!(r"^HALT$", |_, _| Ok(Instruction::Halt)), |
||||
|
asm_entry!(format!(r"^CLOAD\s{}$", REGEX_REG), parse_cload), |
||||
|
asm_entry!(format!(r"^ZERO\s{}$", REGEX_REG), parse_zero), |
||||
|
asm_entry!(r"^DIV$", |_, _| Ok(Instruction::Div)), |
||||
|
asm_entry!(r"^MUL$", |_, _| Ok(Instruction::Mul)), |
||||
|
asm_entry!(r"^SWAP$", |_, _| Ok(Instruction::Swap)), |
||||
|
asm_entry!(r"^PRTCHK$", |_, _| Ok(Instruction::PortReady)), |
||||
|
asm_entry!(r"^PRTRD$", |_, _| Ok(Instruction::ReadPort)), |
||||
|
asm_entry!(r"^PRTWR$", |_, _| Ok(Instruction::WritePort)), |
||||
|
], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pub fn assemble(&self, input: &str) -> Result<[u8; 0x100], Box<dyn Error>> { |
||||
|
let mut ret = [0u8; 0x100]; |
||||
|
let mut i: usize = 0; |
||||
|
for line in input.lines().map(|line| line.trim()) { |
||||
|
match line { |
||||
|
"" => (), |
||||
|
line => { |
||||
|
if i > 0xFF { |
||||
|
return Err("Too much instructions".into()); |
||||
|
} |
||||
|
let insn = self.line_to_insn(line)?; |
||||
|
ret[i] = encode(insn); |
||||
|
i += 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
Ok(ret) |
||||
|
} |
||||
|
|
||||
|
fn line_to_insn(&self, line: &str) -> Result<Instruction, Box<dyn Error>> { |
||||
|
for (regex, handler) in &self.table { |
||||
|
if regex.is_match(line) { |
||||
|
return handler(line, regex); |
||||
|
} |
||||
|
} |
||||
|
Err("Unknown instruction".into()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_u128(src: &str) -> Result<u128, Box<dyn Error>> { |
||||
|
if let Some(s) = src.strip_prefix("0b") { |
||||
|
Ok(u128::from_str_radix(s, 2)?) |
||||
|
} else if let Some(s) = src.strip_prefix("0o") { |
||||
|
Ok(u128::from_str_radix(s, 8)?) |
||||
|
} else if let Some(s) = src.strip_prefix("0x") { |
||||
|
Ok(u128::from_str_radix(s, 16)?) |
||||
|
} else { |
||||
|
Ok(src.parse()?) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_u8(src: &str) -> Result<u8, Box<dyn Error>> { |
||||
|
match parse_u128(src)? { |
||||
|
x @ 0..=255 => Ok(x as u8), |
||||
|
x => Err(format!("{} is not in range [0, 255]", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_i8(src: &str) -> Result<i8, Box<dyn Error>> { |
||||
|
let (sign, num): (i128, u128) = match src.strip_prefix('-') { |
||||
|
Some(s) => (-1, parse_u128(s)?), |
||||
|
None => (1, parse_u128(src)?), |
||||
|
}; |
||||
|
match (sign, num) { |
||||
|
(1, x) if x <= 127 => Ok((x as i128 * sign) as i8), |
||||
|
(-1, x) if x <= 128 => Ok((x as i128 * sign) as i8), |
||||
|
(_, x) => Err(format!("{} is not in range [-128, 127]", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_u4(src: &str) -> Result<u4, Box<dyn Error>> { |
||||
|
match parse_u8(src)? { |
||||
|
x @ 0..=15 => Ok(u4::new(x)), |
||||
|
x => Err(format!("{} is not in range [0, 15]", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_u3(src: &str) -> Result<u3, Box<dyn Error>> { |
||||
|
match parse_u8(src)? { |
||||
|
x @ 0..=7 => Ok(u3::new(x)), |
||||
|
x => Err(format!("{} is not in range [0, 7]", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_reg(src: &str) -> Result<Register, Box<dyn Error>> { |
||||
|
match src { |
||||
|
"R0" => Ok(Register::R0), |
||||
|
"R1" => Ok(Register::R1), |
||||
|
x => Err(format!("'{}' is not a valid register", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_reg_half(src: &str) -> Result<(Register, bool), Box<dyn Error>> { |
||||
|
match src { |
||||
|
"R0.l" => Ok((Register::R0, true)), |
||||
|
"R0.h" => Ok((Register::R0, false)), |
||||
|
"R1.l" => Ok((Register::R1, true)), |
||||
|
"R1.h" => Ok((Register::R1, false)), |
||||
|
x => Err(format!("'{}' is not a valid register half", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_carry(src: &str) -> Result<bool, Box<dyn Error>> { |
||||
|
match src { |
||||
|
"0" => Ok(false), |
||||
|
"1" => Ok(true), |
||||
|
x => Err(format!("'{}' is an invalid carry value", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_byte(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
Ok(decode(parse_u8( |
||||
|
regex.captures(line).unwrap().get(1).unwrap().as_str(), |
||||
|
)?)) |
||||
|
} |
||||
|
|
||||
|
fn parse_mov_reg_mem(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let captures = regex.captures(line).unwrap(); |
||||
|
let reg = parse_reg(captures.get(1).unwrap().as_str())?; |
||||
|
let addr = parse_u4(captures.get(2).unwrap().as_str())?; |
||||
|
Ok(Instruction::Load { reg, addr }) |
||||
|
} |
||||
|
|
||||
|
fn parse_mov_mem_reg(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let captures = regex.captures(line).unwrap(); |
||||
|
let addr = parse_u4(captures.get(1).unwrap().as_str())?; |
||||
|
let reg = parse_reg(captures.get(2).unwrap().as_str())?; |
||||
|
Ok(Instruction::Store { reg, addr }) |
||||
|
} |
||||
|
|
||||
|
fn parse_mov_reg_half_imm(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let captures = regex.captures(line).unwrap(); |
||||
|
let (reg, low) = parse_reg_half(captures.get(1).unwrap().as_str())?; |
||||
|
let val = parse_u4(captures.get(2).unwrap().as_str())?; |
||||
|
Ok(Instruction::LoadImmediate { low, reg, val }) |
||||
|
} |
||||
|
|
||||
|
fn parse_mov_reg_reg(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let captures = regex.captures(line).unwrap(); |
||||
|
let reg_to = parse_reg(captures.get(1).unwrap().as_str())?; |
||||
|
let reg_from = parse_reg(captures.get(2).unwrap().as_str())?; |
||||
|
match (reg_to, reg_from) { |
||||
|
(Register::R0, Register::R1) => Ok(Instruction::Copy { |
||||
|
reg_from: Register::R1, |
||||
|
}), |
||||
|
(Register::R1, Register::R0) => Ok(Instruction::Copy { |
||||
|
reg_from: Register::R0, |
||||
|
}), |
||||
|
_ => Err("Malformed register to register copy".into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_jmp_impl( |
||||
|
line: &str, |
||||
|
regex: &Regex, |
||||
|
use_carry: bool, |
||||
|
) -> Result<Instruction, Box<dyn Error>> { |
||||
|
match parse_i8(regex.captures(line).unwrap().get(1).unwrap().as_str())? { |
||||
|
x @ -3..=-1 => Ok(Instruction::NearJumpBackward { |
||||
|
use_carry, |
||||
|
offset: u2::new(-x as u8), |
||||
|
}), |
||||
|
x @ 0..=7 => Ok(Instruction::NearJumpForward { |
||||
|
use_carry, |
||||
|
offset: u3::new(x as u8), |
||||
|
}), |
||||
|
x => Err(format!("{} is not a valid relative jump offset", x).into()), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn parse_jmp(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_jmp_impl(line, regex, false) |
||||
|
} |
||||
|
|
||||
|
fn parse_jmpc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_jmp_impl(line, regex, true) |
||||
|
} |
||||
|
|
||||
|
fn parse_ajmp_impl( |
||||
|
line: &str, |
||||
|
regex: &Regex, |
||||
|
use_carry: bool, |
||||
|
) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; |
||||
|
Ok(Instruction::FarJump { reg, use_carry }) |
||||
|
} |
||||
|
|
||||
|
fn parse_ajmp(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_ajmp_impl(line, regex, false) |
||||
|
} |
||||
|
|
||||
|
fn parse_ajmpc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_ajmp_impl(line, regex, true) |
||||
|
} |
||||
|
|
||||
|
fn parse_shift( |
||||
|
line: &str, |
||||
|
regex: &Regex, |
||||
|
right: bool, |
||||
|
mode: ShiftMode, |
||||
|
) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let captures = regex.captures(line).unwrap(); |
||||
|
let reg = parse_reg(captures.get(1).unwrap().as_str())?; |
||||
|
let len = parse_u3(captures.get(2).unwrap().as_str())?; |
||||
|
Ok(Instruction::BitShift { |
||||
|
right, |
||||
|
reg, |
||||
|
mode, |
||||
|
len, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
fn parse_shr(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_shift(line, regex, true, ShiftMode::Logical) |
||||
|
} |
||||
|
|
||||
|
fn parse_shl(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_shift(line, regex, false, ShiftMode::Logical) |
||||
|
} |
||||
|
|
||||
|
fn parse_rotr(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_shift(line, regex, true, ShiftMode::Circular) |
||||
|
} |
||||
|
|
||||
|
fn parse_rotl(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
parse_shift(line, regex, false, ShiftMode::Circular) |
||||
|
} |
||||
|
|
||||
|
fn parse_inc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; |
||||
|
Ok(Instruction::Inc { reg }) |
||||
|
} |
||||
|
|
||||
|
fn parse_dec(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; |
||||
|
Ok(Instruction::Dec { reg }) |
||||
|
} |
||||
|
|
||||
|
fn parse_compl(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; |
||||
|
Ok(Instruction::Complement { reg }) |
||||
|
} |
||||
|
|
||||
|
fn parse_setc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let carry = parse_carry(regex.captures(line).unwrap().get(1).unwrap().as_str())?; |
||||
|
Ok(Instruction::Setc { carry }) |
||||
|
} |
||||
|
|
||||
|
fn parse_cload(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; |
||||
|
Ok(Instruction::Cload { reg }) |
||||
|
} |
||||
|
|
||||
|
fn parse_zero(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
||||
|
let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; |
||||
|
Ok(Instruction::Zero { reg }) |
||||
|
} |
||||
|
|
||||
|
#[cfg(test)] |
||||
|
mod tests { |
||||
|
use crate::assembler::Assembler; |
||||
|
use crate::instruction::{decode, Instruction, Register, ShiftMode}; |
||||
|
use lazy_static::lazy_static; |
||||
|
use std::fmt::{Binary, Display, LowerHex, Octal}; |
||||
|
use ux::{u2, u3, u4}; |
||||
|
|
||||
|
lazy_static! { |
||||
|
static ref ASM: Assembler = Assembler::new(); |
||||
|
} |
||||
|
|
||||
|
enum IntegerFormat { |
||||
|
Decimal, |
||||
|
Binary, |
||||
|
Octal, |
||||
|
Hexadecimal, |
||||
|
None, |
||||
|
} |
||||
|
|
||||
|
struct PrintIntegers<T: Display + Binary + Octal + LowerHex> { |
||||
|
num: T, |
||||
|
format: IntegerFormat, |
||||
|
} |
||||
|
|
||||
|
impl<T: Display + Binary + Octal + LowerHex> PrintIntegers<T> { |
||||
|
fn new(num: T) -> Self { |
||||
|
PrintIntegers { |
||||
|
num, |
||||
|
format: IntegerFormat::Decimal, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
impl<T: Display + Binary + Octal + LowerHex> Iterator for PrintIntegers<T> { |
||||
|
type Item = String; |
||||
|
|
||||
|
fn next(&mut self) -> Option<Self::Item> { |
||||
|
match self.format { |
||||
|
IntegerFormat::Decimal => { |
||||
|
self.format = IntegerFormat::Binary; |
||||
|
Some(format!("{}", self.num)) |
||||
|
} |
||||
|
IntegerFormat::Binary => { |
||||
|
self.format = IntegerFormat::Octal; |
||||
|
Some(format!("{:#b}", self.num)) |
||||
|
} |
||||
|
IntegerFormat::Octal => { |
||||
|
self.format = IntegerFormat::Hexadecimal; |
||||
|
Some(format!("{:#o}", self.num)) |
||||
|
} |
||||
|
IntegerFormat::Hexadecimal => { |
||||
|
self.format = IntegerFormat::None; |
||||
|
Some(format!("{:#x}", self.num)) |
||||
|
} |
||||
|
IntegerFormat::None => None, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
macro_rules! make_test_insn_no_args { |
||||
|
($name:ident, $line:expr, $insn:expr) => { |
||||
|
#[test] |
||||
|
fn $name() { |
||||
|
assert_eq!(ASM.line_to_insn($line).unwrap(), $insn); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
make_test_insn_no_args!(asm_line_eq, "EQ", Instruction::CmpEqual); |
||||
|
make_test_insn_no_args!(asm_line_gt, "GT", Instruction::CmpGreater); |
||||
|
make_test_insn_no_args!(asm_line_le, "LE", Instruction::CmpLess); |
||||
|
make_test_insn_no_args!(asm_line_not, "NOT", Instruction::Not); |
||||
|
make_test_insn_no_args!(asm_line_add, "ADD", Instruction::Add); |
||||
|
make_test_insn_no_args!(asm_line_sub, "SUB", Instruction::Sub); |
||||
|
make_test_insn_no_args!(asm_line_and, "AND", Instruction::And); |
||||
|
make_test_insn_no_args!(asm_line_or, "OR", Instruction::Or); |
||||
|
make_test_insn_no_args!(asm_line_xor, "XOR", Instruction::Xor); |
||||
|
make_test_insn_no_args!(asm_line_xnor, "XNOR", Instruction::Xnor); |
||||
|
make_test_insn_no_args!(asm_line_nop, "NOP", Instruction::Nop); |
||||
|
make_test_insn_no_args!(asm_line_halt, "HALT", Instruction::Halt); |
||||
|
make_test_insn_no_args!(asm_line_div, "DIV", Instruction::Div); |
||||
|
make_test_insn_no_args!(asm_line_mul, "MUL", Instruction::Mul); |
||||
|
make_test_insn_no_args!(asm_line_swap, "SWAP", Instruction::Swap); |
||||
|
make_test_insn_no_args!(asm_line_prtchk, "PRTCHK", Instruction::PortReady); |
||||
|
make_test_insn_no_args!(asm_line_prtrd, "PRTRD", Instruction::ReadPort); |
||||
|
make_test_insn_no_args!(asm_line_prtwr, "PRTWR", Instruction::WritePort); |
||||
|
make_test_insn_no_args!( |
||||
|
asm_line_shr, |
||||
|
"SHR", |
||||
|
Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Logical, |
||||
|
right: true |
||||
|
} |
||||
|
); |
||||
|
make_test_insn_no_args!( |
||||
|
asm_line_shl, |
||||
|
"SHL", |
||||
|
Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Logical, |
||||
|
right: false |
||||
|
} |
||||
|
); |
||||
|
make_test_insn_no_args!( |
||||
|
asm_line_rotr, |
||||
|
"ROTR", |
||||
|
Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Circular, |
||||
|
right: true |
||||
|
} |
||||
|
); |
||||
|
make_test_insn_no_args!( |
||||
|
asm_line_rotl, |
||||
|
"ROTL", |
||||
|
Instruction::BitShiftVar { |
||||
|
mode: ShiftMode::Circular, |
||||
|
right: false |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_byte() { |
||||
|
for i in 0..=255u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!(ASM.line_to_insn(&format!("BYTE {}", j)).unwrap(), decode(i)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_mov_reg_mem() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0)] { |
||||
|
for i in 0..=15u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("MOV {}, [{}]", reg_str, j)) |
||||
|
.unwrap(), |
||||
|
Instruction::Load { |
||||
|
reg: reg.clone(), |
||||
|
addr: u4::new(i) |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_mov_mem_reg() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0)] { |
||||
|
for i in 0..=15u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("MOV [{}], {}", j, reg_str)) |
||||
|
.unwrap(), |
||||
|
Instruction::Store { |
||||
|
reg: reg.clone(), |
||||
|
addr: u4::new(i) |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_mov_reg_half_imm() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
for (low_str, low) in [("l", true), ("h", false)] { |
||||
|
for i in 0..=15 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("MOV {}.{}, {}", reg_str, low_str, j)) |
||||
|
.unwrap(), |
||||
|
Instruction::LoadImmediate { |
||||
|
low, |
||||
|
reg: reg.clone(), |
||||
|
val: u4::new(i) |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_jmp() { |
||||
|
for i in 0..=7u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("JMP {}", j)).unwrap(), |
||||
|
Instruction::NearJumpForward { |
||||
|
use_carry: false, |
||||
|
offset: u3::new(i) |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
for i in 1..=3u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("JMP -{}", j)).unwrap(), |
||||
|
Instruction::NearJumpBackward { |
||||
|
use_carry: false, |
||||
|
offset: u2::new(i) |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_jmpc() { |
||||
|
for i in 0..=7u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("JMPC {}", j)).unwrap(), |
||||
|
Instruction::NearJumpForward { |
||||
|
use_carry: true, |
||||
|
offset: u3::new(i) |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
for i in 1..=3u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("JMPC -{}", j)).unwrap(), |
||||
|
Instruction::NearJumpBackward { |
||||
|
use_carry: true, |
||||
|
offset: u2::new(i) |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_lime_ajmp() { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn("AJMP R0").unwrap(), |
||||
|
Instruction::FarJump { |
||||
|
reg: Register::R0, |
||||
|
use_carry: false |
||||
|
} |
||||
|
); |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn("AJMP R1").unwrap(), |
||||
|
Instruction::FarJump { |
||||
|
reg: Register::R1, |
||||
|
use_carry: false |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_lime_ajmpc() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("AJMPC {}", reg_str)).unwrap(), |
||||
|
Instruction::FarJump { |
||||
|
reg, |
||||
|
use_carry: true |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_shift_rotate() { |
||||
|
for (insn, right, mode) in [ |
||||
|
("SHR", true, ShiftMode::Logical), |
||||
|
("SHL", false, ShiftMode::Logical), |
||||
|
("ROTR", true, ShiftMode::Circular), |
||||
|
("ROTL", false, ShiftMode::Circular), |
||||
|
] { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
for i in 0..7u8 { |
||||
|
for j in PrintIntegers::new(i) { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("{} {}, {}", insn, reg_str, j)) |
||||
|
.unwrap(), |
||||
|
Instruction::BitShift { |
||||
|
right, |
||||
|
reg: reg.clone(), |
||||
|
mode: mode.clone(), |
||||
|
len: u3::new(i) |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_inc() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("INC {}", reg_str)).unwrap(), |
||||
|
Instruction::Inc { reg } |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_dec() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("DEC {}", reg_str)).unwrap(), |
||||
|
Instruction::Dec { reg } |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_compl() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("COMPL {}", reg_str)).unwrap(), |
||||
|
Instruction::Complement { reg } |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_setc() { |
||||
|
for (i, carry) in [(0, false), (1, true)] { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("SETC {}", i)).unwrap(), |
||||
|
Instruction::Setc { carry } |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_cload() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("CLOAD {}", reg_str)).unwrap(), |
||||
|
Instruction::Cload { reg } |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#[test] |
||||
|
fn asm_line_zero() { |
||||
|
for (reg_str, reg) in [("R0", Register::R0), ("R1", Register::R1)] { |
||||
|
assert_eq!( |
||||
|
ASM.line_to_insn(&format!("ZERO {}", reg_str)).unwrap(), |
||||
|
Instruction::Zero { reg } |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue