- Fixed a where the VM excuted one loop regardless of instructions. - Use the full 32 bits if an instruction is less.
147 lines
No EOL
3.6 KiB
Rust
147 lines
No EOL
3.6 KiB
Rust
use crate::assembler::opcode_parser::opcode;
|
|
use crate::assembler::operand_parser::integer_operand;
|
|
use crate::assembler::register_parser::register;
|
|
use crate::assembler::Token;
|
|
use nom::*;
|
|
use nom::types::CompleteStr;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub struct AssemblerInstruction {
|
|
opcode: Token,
|
|
operand1: Option<Token>,
|
|
operand2: Option<Token>,
|
|
operand3: Option<Token>,
|
|
}
|
|
|
|
impl AssemblerInstruction {
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
let mut results = vec![];
|
|
|
|
match self.opcode.to_owned() {
|
|
Token::Opcode { code } => match code {
|
|
_ => {
|
|
results.push(code.into());
|
|
}
|
|
},
|
|
_ => {
|
|
println!("Incorrect opcode!");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
for operand in &[&self.operand1, &self.operand2, &self.operand3] {
|
|
if let Some(token) = operand {
|
|
AssemblerInstruction::extract_operand(token, &mut results)
|
|
}
|
|
}
|
|
|
|
while results.len() < 4 {
|
|
results.push(0);
|
|
}
|
|
|
|
results
|
|
}
|
|
|
|
fn extract_operand(t: &Token, results: &mut Vec<u8>) {
|
|
match t {
|
|
Token::Register { reg_num } => {
|
|
results.push(*reg_num);
|
|
}
|
|
Token::Number { value } => {
|
|
let conv = *value as u16;
|
|
let byte1 = conv;
|
|
let byte2 = conv >> 8;
|
|
|
|
results.push(byte2 as u8);
|
|
results.push(byte1 as u8);
|
|
}
|
|
_ => {
|
|
println!("Opcode found in operand field");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
named!(instruction_two<CompleteStr, AssemblerInstruction>,
|
|
do_parse!(
|
|
o: opcode >>
|
|
opt!(multispace) >>
|
|
(
|
|
AssemblerInstruction{
|
|
opcode: o,
|
|
operand1: None,
|
|
operand2: None,
|
|
operand3: None
|
|
}
|
|
)
|
|
)
|
|
);
|
|
|
|
named!(pub instruction<CompleteStr, AssemblerInstruction>,
|
|
do_parse!(
|
|
ins: alt!(
|
|
instruction_one |
|
|
instruction_two
|
|
) >>
|
|
(
|
|
ins
|
|
)
|
|
)
|
|
);
|
|
|
|
named!(instruction_one<CompleteStr, AssemblerInstruction>,
|
|
do_parse!(
|
|
o: opcode >>
|
|
r: register >>
|
|
i: integer_operand >>
|
|
(
|
|
AssemblerInstruction{
|
|
opcode: o,
|
|
operand1: Some(r),
|
|
operand2: Some(i),
|
|
operand3: None
|
|
}
|
|
)
|
|
)
|
|
);
|
|
|
|
#[cfg(test)]
|
|
mod instruction_parser_test {
|
|
use super::*;
|
|
use crate::assembler::Opcode;
|
|
|
|
#[test]
|
|
fn test_parse_instruction_form_one() {
|
|
let result = instruction_one(CompleteStr("load $0 #100\n"));
|
|
assert_eq!(
|
|
result,
|
|
Ok((
|
|
CompleteStr(""),
|
|
AssemblerInstruction {
|
|
opcode: Token::Opcode { code: Opcode::LOAD },
|
|
operand1: Some(Token::Register { reg_num: 0 }),
|
|
operand2: Some(Token::Number { value: 100 }),
|
|
operand3: None
|
|
}
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_instruction_form_two() {
|
|
let result = instruction_two(CompleteStr("hlt\n"));
|
|
assert_eq!(
|
|
result,
|
|
Ok((
|
|
CompleteStr(""),
|
|
AssemblerInstruction {
|
|
opcode: Token::Opcode { code: Opcode::HLT },
|
|
operand1: None,
|
|
operand2: None,
|
|
operand3: None
|
|
}
|
|
))
|
|
);
|
|
}
|
|
} |