|
|
@ -3,10 +3,15 @@ |
|
|
|
|
|
|
|
use crate::instruction::{decode, encode, Instruction, Register, ShiftMode}; |
|
|
|
use regex::Regex; |
|
|
|
use std::error::Error; |
|
|
|
use std::{ |
|
|
|
error::Error, |
|
|
|
fmt::{Display, Formatter}, |
|
|
|
num, |
|
|
|
ops::RangeInclusive, |
|
|
|
}; |
|
|
|
use ux::{u2, u3, u4}; |
|
|
|
|
|
|
|
type InstructionDecoder = fn(&str, &Regex) -> Result<Instruction, Box<dyn Error>>; |
|
|
|
type InstructionDecoder = fn(&str, &Regex) -> Result<Instruction, AssemblerErrorKind>; |
|
|
|
|
|
|
|
const REGEX_REG: &str = r"(R[01])"; |
|
|
|
const REGEX_REG_HALF: &str = r"(R[01]\.[lh])"; |
|
|
@ -22,6 +27,170 @@ macro_rules! asm_entry { |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
enum AssemblerErrorKind { |
|
|
|
TooMuchInstructions, |
|
|
|
UnknownInstruction { |
|
|
|
instruction: String, |
|
|
|
}, |
|
|
|
InvalidRegister { |
|
|
|
register: String, |
|
|
|
}, |
|
|
|
InvalidRegisterHalf { |
|
|
|
register_half: String, |
|
|
|
}, |
|
|
|
InvalidCarry { |
|
|
|
carry: String, |
|
|
|
}, |
|
|
|
NumberOutOfRange { |
|
|
|
number: i128, |
|
|
|
range: RangeInclusive<i128>, |
|
|
|
}, |
|
|
|
InvalidInteger { |
|
|
|
parse_int_error: num::ParseIntError, |
|
|
|
}, |
|
|
|
InvalidRelativeJumpOffset { |
|
|
|
jump_offset: i8, |
|
|
|
}, |
|
|
|
MalformedRegisterToRegisterCopy, |
|
|
|
} |
|
|
|
|
|
|
|
impl AssemblerErrorKind { |
|
|
|
fn unknown_instruction(instruction: &str) -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::UnknownInstruction { |
|
|
|
instruction: instruction.to_string(), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn invalid_register(register: &str) -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::InvalidRegister { |
|
|
|
register: register.to_string(), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn invalid_register_half(register_half: &str) -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::InvalidRegisterHalf { |
|
|
|
register_half: register_half.to_string(), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn invalid_carry(carry: &str) -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::InvalidCarry { |
|
|
|
carry: carry.to_string(), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn number_out_of_range<T: Into<i128> + Copy>( |
|
|
|
number: T, |
|
|
|
range: RangeInclusive<T>, |
|
|
|
) -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::NumberOutOfRange { |
|
|
|
number: number.into(), |
|
|
|
// am I doing this right?
|
|
|
|
range: (*range.start()).into()..=(*range.end()).into(), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn invalid_relative_jump_offset(jump_offset: i8) -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::InvalidRelativeJumpOffset { jump_offset } |
|
|
|
} |
|
|
|
|
|
|
|
fn malformed_register_to_register_copy() -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::MalformedRegisterToRegisterCopy |
|
|
|
} |
|
|
|
|
|
|
|
fn to_assembler_error(&self, line: usize) -> AssemblerError { |
|
|
|
AssemblerError { |
|
|
|
line, |
|
|
|
error: self.clone(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl From<num::ParseIntError> for AssemblerErrorKind { |
|
|
|
fn from(parse_int_error: num::ParseIntError) -> AssemblerErrorKind { |
|
|
|
AssemblerErrorKind::InvalidInteger { parse_int_error } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Display for AssemblerErrorKind { |
|
|
|
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { |
|
|
|
write!( |
|
|
|
f, |
|
|
|
"{}", |
|
|
|
match &self { |
|
|
|
AssemblerErrorKind::TooMuchInstructions => String::from("too much instructions"), |
|
|
|
AssemblerErrorKind::UnknownInstruction { instruction } => |
|
|
|
format!("unknown instruction: '{}'", instruction), |
|
|
|
AssemblerErrorKind::InvalidRegister { register } => |
|
|
|
format!("invalid register: '{}'", register), |
|
|
|
AssemblerErrorKind::InvalidRegisterHalf { register_half } => |
|
|
|
format!("invalid register half: '{}'", register_half), |
|
|
|
AssemblerErrorKind::InvalidCarry { carry } => format!("invalid carry: '{}'", carry), |
|
|
|
AssemblerErrorKind::NumberOutOfRange { number, range } => format!( |
|
|
|
"number {} is out of range [{}, {}]", |
|
|
|
number, |
|
|
|
range.start(), |
|
|
|
range.end() |
|
|
|
), |
|
|
|
AssemblerErrorKind::InvalidInteger { parse_int_error } => |
|
|
|
format!("failed to parse integer: {}", parse_int_error), |
|
|
|
AssemblerErrorKind::InvalidRelativeJumpOffset { jump_offset } => |
|
|
|
format!("{} is not a valid relative jump offset", jump_offset), |
|
|
|
AssemblerErrorKind::MalformedRegisterToRegisterCopy => |
|
|
|
String::from("malformed register to register copy"), |
|
|
|
} |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Error for AssemblerErrorKind { |
|
|
|
fn description(&self) -> &str { |
|
|
|
match self { |
|
|
|
AssemblerErrorKind::TooMuchInstructions => "Too much instructions", |
|
|
|
AssemblerErrorKind::UnknownInstruction { .. } => "Unknown instruction", |
|
|
|
AssemblerErrorKind::InvalidRegister { .. } => "Invalid register", |
|
|
|
AssemblerErrorKind::InvalidRegisterHalf { .. } => "Invalid register half", |
|
|
|
AssemblerErrorKind::InvalidCarry { .. } => "Invalid carry", |
|
|
|
AssemblerErrorKind::NumberOutOfRange { .. } => "Number out of range", |
|
|
|
AssemblerErrorKind::InvalidInteger { .. } => "Failed to parse integer", |
|
|
|
AssemblerErrorKind::InvalidRelativeJumpOffset { .. } => "Invalid relative jump offset", |
|
|
|
AssemblerErrorKind::MalformedRegisterToRegisterCopy => { |
|
|
|
"Malformed register to register copy" |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
pub struct AssemblerError { |
|
|
|
line: usize, |
|
|
|
error: AssemblerErrorKind, |
|
|
|
} |
|
|
|
|
|
|
|
type AssemblerResult<T> = Result<T, AssemblerError>; |
|
|
|
|
|
|
|
impl Display for AssemblerError { |
|
|
|
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { |
|
|
|
write!(f, "Failed to assemble, line {}: {}", self.line, self.error) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Error for AssemblerError { |
|
|
|
fn source(&self) -> Option<&(dyn Error + 'static)> { |
|
|
|
Some(&self.error) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl AssemblerError { |
|
|
|
fn too_much_instructions(line: usize) -> AssemblerError { |
|
|
|
AssemblerError { |
|
|
|
line, |
|
|
|
error: AssemblerErrorKind::TooMuchInstructions, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub struct Assembler { |
|
|
|
table: Vec<(Regex, InstructionDecoder)>, |
|
|
|
} |
|
|
@ -111,17 +280,23 @@ impl Assembler { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn assemble(&self, input: &str) -> Result<[u8; 0x100], Box<dyn Error>> { |
|
|
|
pub fn assemble(&self, input: &str) -> AssemblerResult<[u8; 0x100]> { |
|
|
|
let mut ret = [0u8; 0x100]; |
|
|
|
let mut i: usize = 0; |
|
|
|
for line in input.lines().map(|line| line.trim()) { |
|
|
|
for (line_num, line) in input |
|
|
|
.lines() |
|
|
|
.map(|line| strip_comment(line).trim()) |
|
|
|
.enumerate() |
|
|
|
{ |
|
|
|
match line { |
|
|
|
"" => (), |
|
|
|
line => { |
|
|
|
if i > 0xFF { |
|
|
|
return Err("Too much instructions".into()); |
|
|
|
return Err(AssemblerError::too_much_instructions(line_num)); |
|
|
|
} |
|
|
|
let insn = self.line_to_insn(line)?; |
|
|
|
let insn = self |
|
|
|
.line_to_insn(line) |
|
|
|
.map_err(|e| e.to_assembler_error(line_num))?; |
|
|
|
ret[i] = encode(insn); |
|
|
|
i += 1; |
|
|
|
} |
|
|
@ -130,17 +305,30 @@ impl Assembler { |
|
|
|
Ok(ret) |
|
|
|
} |
|
|
|
|
|
|
|
fn line_to_insn(&self, line: &str) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn line_to_insn(&self, line: &str) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
for (regex, handler) in &self.table { |
|
|
|
if regex.is_match(line) { |
|
|
|
return handler(line, regex); |
|
|
|
} |
|
|
|
} |
|
|
|
Err("Unknown instruction".into()) |
|
|
|
Err(AssemblerErrorKind::unknown_instruction(line)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Default for Assembler { |
|
|
|
fn default() -> Self { |
|
|
|
Self::new() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn strip_comment(input: &str) -> &str { |
|
|
|
match input.find('#') { |
|
|
|
Some(x) => &input[0..x], |
|
|
|
None => input, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_u128(src: &str) -> Result<u128, Box<dyn Error>> { |
|
|
|
fn parse_u128(src: &str) -> Result<u128, num::ParseIntError> { |
|
|
|
if let Some(s) = src.strip_prefix("0b") { |
|
|
|
Ok(u128::from_str_radix(s, 2)?) |
|
|
|
} else if let Some(s) = src.strip_prefix("0o") { |
|
|
@ -152,14 +340,14 @@ fn parse_u128(src: &str) -> Result<u128, Box<dyn Error>> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_u8(src: &str) -> Result<u8, Box<dyn Error>> { |
|
|
|
fn parse_u8(src: &str) -> Result<u8, AssemblerErrorKind> { |
|
|
|
match parse_u128(src)? { |
|
|
|
x @ 0..=255 => Ok(x as u8), |
|
|
|
x => Err(format!("{} is not in range [0, 255]", x).into()), |
|
|
|
x => Err(AssemblerErrorKind::number_out_of_range(x as i128, 0..=255)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_i8(src: &str) -> Result<i8, Box<dyn Error>> { |
|
|
|
fn parse_i8(src: &str) -> Result<i8, AssemblerErrorKind> { |
|
|
|
let (sign, num): (i128, u128) = match src.strip_prefix('-') { |
|
|
|
Some(s) => (-1, parse_u128(s)?), |
|
|
|
None => (1, parse_u128(src)?), |
|
|
@ -167,78 +355,81 @@ fn parse_i8(src: &str) -> Result<i8, Box<dyn Error>> { |
|
|
|
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()), |
|
|
|
(_, x) => Err(AssemblerErrorKind::number_out_of_range( |
|
|
|
x as i128, |
|
|
|
-128..=127, |
|
|
|
)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_u4(src: &str) -> Result<u4, Box<dyn Error>> { |
|
|
|
fn parse_u4(src: &str) -> Result<u4, AssemblerErrorKind> { |
|
|
|
match parse_u8(src)? { |
|
|
|
x @ 0..=15 => Ok(u4::new(x)), |
|
|
|
x => Err(format!("{} is not in range [0, 15]", x).into()), |
|
|
|
x => Err(AssemblerErrorKind::number_out_of_range(x as i128, 0..=15)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_u3(src: &str) -> Result<u3, Box<dyn Error>> { |
|
|
|
fn parse_u3(src: &str) -> Result<u3, AssemblerErrorKind> { |
|
|
|
match parse_u8(src)? { |
|
|
|
x @ 0..=7 => Ok(u3::new(x)), |
|
|
|
x => Err(format!("{} is not in range [0, 7]", x).into()), |
|
|
|
x => Err(AssemblerErrorKind::number_out_of_range(x, 0..=7)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_reg(src: &str) -> Result<Register, Box<dyn Error>> { |
|
|
|
fn parse_reg(src: &str) -> Result<Register, AssemblerErrorKind> { |
|
|
|
match src { |
|
|
|
"R0" => Ok(Register::R0), |
|
|
|
"R1" => Ok(Register::R1), |
|
|
|
x => Err(format!("'{}' is not a valid register", x).into()), |
|
|
|
x => Err(AssemblerErrorKind::invalid_register(x)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_reg_half(src: &str) -> Result<(Register, bool), Box<dyn Error>> { |
|
|
|
fn parse_reg_half(src: &str) -> Result<(Register, bool), AssemblerErrorKind> { |
|
|
|
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()), |
|
|
|
x => Err(AssemblerErrorKind::invalid_register_half(x)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_carry(src: &str) -> Result<bool, Box<dyn Error>> { |
|
|
|
fn parse_carry(src: &str) -> Result<bool, AssemblerErrorKind> { |
|
|
|
match src { |
|
|
|
"0" => Ok(false), |
|
|
|
"1" => Ok(true), |
|
|
|
x => Err(format!("'{}' is an invalid carry value", x).into()), |
|
|
|
x => Err(AssemblerErrorKind::invalid_carry(x)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_byte(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_byte(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_mov_reg_mem(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_mov_mem_reg(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_mov_reg_half_imm(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_mov_reg_reg(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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())?; |
|
|
@ -249,7 +440,7 @@ fn parse_mov_reg_reg(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn E |
|
|
|
(Register::R1, Register::R0) => Ok(Instruction::Copy { |
|
|
|
reg_from: Register::R0, |
|
|
|
}), |
|
|
|
_ => Err("Malformed register to register copy".into()), |
|
|
|
_ => Err(AssemblerErrorKind::malformed_register_to_register_copy()), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -257,7 +448,7 @@ fn parse_jmp_impl( |
|
|
|
line: &str, |
|
|
|
regex: &Regex, |
|
|
|
use_carry: bool, |
|
|
|
) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
match parse_i8(regex.captures(line).unwrap().get(1).unwrap().as_str())? { |
|
|
|
x @ -3..=-1 => Ok(Instruction::NearJumpBackward { |
|
|
|
use_carry, |
|
|
@ -267,15 +458,15 @@ fn parse_jmp_impl( |
|
|
|
use_carry, |
|
|
|
offset: u3::new(x as u8), |
|
|
|
}), |
|
|
|
x => Err(format!("{} is not a valid relative jump offset", x).into()), |
|
|
|
x => Err(AssemblerErrorKind::invalid_relative_jump_offset(x)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_jmp(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_jmp(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_jmp_impl(line, regex, false) |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_jmpc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_jmpc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_jmp_impl(line, regex, true) |
|
|
|
} |
|
|
|
|
|
|
@ -283,16 +474,16 @@ fn parse_ajmp_impl( |
|
|
|
line: &str, |
|
|
|
regex: &Regex, |
|
|
|
use_carry: bool, |
|
|
|
) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_ajmp(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_ajmp_impl(line, regex, false) |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_ajmpc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_ajmpc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_ajmp_impl(line, regex, true) |
|
|
|
} |
|
|
|
|
|
|
@ -301,7 +492,7 @@ fn parse_shift( |
|
|
|
regex: &Regex, |
|
|
|
right: bool, |
|
|
|
mode: ShiftMode, |
|
|
|
) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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())?; |
|
|
@ -313,56 +504,58 @@ fn parse_shift( |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_shr(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_shr(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_shift(line, regex, true, ShiftMode::Logical) |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_shl(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_shl(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_shift(line, regex, false, ShiftMode::Logical) |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_rotr(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_rotr(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_shift(line, regex, true, ShiftMode::Circular) |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_rotl(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_rotl(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
parse_shift(line, regex, false, ShiftMode::Circular) |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_inc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> { |
|
|
|
fn parse_inc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_dec(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_compl(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_setc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_cload(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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>> { |
|
|
|
fn parse_zero(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> { |
|
|
|
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 crate::{ |
|
|
|
assembler::{strip_comment, Assembler}, |
|
|
|
instruction::{decode, Instruction, Register, ShiftMode}, |
|
|
|
}; |
|
|
|
use lazy_static::lazy_static; |
|
|
|
use std::fmt::{Binary, Display, LowerHex, Octal}; |
|
|
|
use ux::{u2, u3, u4}; |
|
|
@ -714,4 +907,60 @@ mod tests { |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn asm_assemble_empty() { |
|
|
|
assert_eq!(ASM.assemble("").unwrap(), [0u8; 256]); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn asm_assemble() { |
|
|
|
let expected = { |
|
|
|
let mut code = [0u8; 256]; |
|
|
|
code[0..6].copy_from_slice(&[0x4F, 0x53, 0xE4, 0x20, 0xF8, 0xF5]); |
|
|
|
code |
|
|
|
}; |
|
|
|
assert_eq!( |
|
|
|
ASM.assemble( |
|
|
|
r#" |
|
|
|
MOV R0.l, 0xF |
|
|
|
MOV R1.l, 0x3 |
|
|
|
ADD |
|
|
|
MOV [0], R0 |
|
|
|
ZERO R0 |
|
|
|
HALT |
|
|
|
"# |
|
|
|
) |
|
|
|
.unwrap(), |
|
|
|
expected |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn asm_strip_comment() { |
|
|
|
assert_eq!(strip_comment("MOV [2], R1 # comment"), "MOV [2], R1 "); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn asm_assemble_comments() { |
|
|
|
let expected = { |
|
|
|
let mut code = [0u8; 256]; |
|
|
|
code[0..6].copy_from_slice(&[0x4F, 0x53, 0xE4, 0x20, 0xF8, 0xF5]); |
|
|
|
code |
|
|
|
}; |
|
|
|
assert_eq!( |
|
|
|
ASM.assemble( |
|
|
|
r#" |
|
|
|
MOV R0.l, 0xF # R0 = 15 |
|
|
|
MOV R1.l, 0x3 # R1 = 3 |
|
|
|
ADD # R0 += R1 (18) |
|
|
|
MOV [0], R0 # MEM[0] = R0 (18) |
|
|
|
ZERO R0 # R0 = 0 |
|
|
|
HALT # EXIT |
|
|
|
"# |
|
|
|
) |
|
|
|
.unwrap(), |
|
|
|
expected |
|
|
|
); |
|
|
|
} |
|
|
|
} |