Murad
2 years ago
7 changed files with 933 additions and 20 deletions
@ -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