621 lines
25 KiB
Rust
621 lines
25 KiB
Rust
|
|
use std::collections::HashMap;
|
|
|
|
use moa_core::Error;
|
|
use moa_core::parser::{self, AssemblyLine, AssemblyOperand, AssemblyParser};
|
|
|
|
use super::state::M68kType;
|
|
use super::instructions::Size;
|
|
|
|
|
|
#[repr(usize)]
|
|
#[derive(Copy, Clone)]
|
|
pub enum Disallow {
|
|
None = 0x0000,
|
|
NoDReg = 0x0001,
|
|
NoAReg = 0x0002,
|
|
NoIndirect = 0x0004,
|
|
NoIndirectPre = 0x0008,
|
|
NoIndirectPost = 0x0010,
|
|
NoIndirectOffset = 0x0020,
|
|
NoIndirectIndexReg = 0x0040,
|
|
NoIndirectImmediate = 0x0080,
|
|
NoImmediate = 0x0100,
|
|
NoPCRelative = 0x0200,
|
|
NoPCRelativeIndex = 0x0400,
|
|
|
|
NoRegs = 0x0003,
|
|
NoRegsOrImmediate = 0x0103,
|
|
NoRegsImmediateOrPC = 0x0703,
|
|
NoARegImmediateOrPC = 0x0702,
|
|
NoRegsPrePostOrImmediate = 0x011B,
|
|
NoImmediateOrPC = 0x0700,
|
|
OnlyAReg = 0x07FD,
|
|
}
|
|
|
|
impl Disallow {
|
|
pub fn check(&self, lineno: usize, disallow: Disallow) -> Result<(), Error> {
|
|
if (*self as usize) & (disallow as usize) == 0 {
|
|
Ok(())
|
|
} else {
|
|
Err(Error::new(&format!("error at line {}: invalid addressing mode for the instruction", lineno)))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum RelocationType {
|
|
Displacement,
|
|
Word(String),
|
|
Long(String),
|
|
}
|
|
|
|
pub struct Relocation {
|
|
pub rtype: RelocationType,
|
|
pub label: String,
|
|
pub index: usize,
|
|
pub from_origin: usize,
|
|
}
|
|
|
|
impl Relocation {
|
|
pub fn new(rtype: RelocationType, label: String, index: usize, from_origin: usize) -> Self {
|
|
Self {
|
|
rtype,
|
|
label,
|
|
index,
|
|
from_origin,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct M68kAssembler {
|
|
pub cputype: M68kType,
|
|
pub labels: HashMap<String, usize>,
|
|
pub output: Vec<u16>,
|
|
pub relocations: Vec<Relocation>,
|
|
pub current_origin: usize,
|
|
}
|
|
|
|
impl M68kAssembler {
|
|
pub fn new(cputype: M68kType) -> Self {
|
|
Self {
|
|
cputype,
|
|
labels: HashMap::new(),
|
|
output: vec![],
|
|
relocations: vec![],
|
|
current_origin: 0,
|
|
}
|
|
}
|
|
|
|
pub fn assemble(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
|
self.assemble_in_place(text)?;
|
|
Ok(self.output.iter().fold(vec![], |mut acc, item| {
|
|
acc.push((*item >> 8) as u8);
|
|
acc.push(*item as u8);
|
|
acc
|
|
}))
|
|
}
|
|
|
|
pub fn assemble_words(&mut self, text: &str) -> Result<Vec<u16>, Error> {
|
|
self.assemble_in_place(text)?;
|
|
Ok(self.output.clone())
|
|
}
|
|
|
|
pub fn assemble_in_place(&mut self, text: &str) -> Result<(), Error> {
|
|
let lines = self.parse(text)?;
|
|
|
|
for (lineno, line) in lines {
|
|
self.convert(lineno, &line)?;
|
|
}
|
|
|
|
self.apply_relocations()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn parse(&mut self, text: &str) -> Result<Vec<(usize, AssemblyLine)>, Error> {
|
|
let mut parser = AssemblyParser::new(text);
|
|
parser.parse()
|
|
}
|
|
|
|
fn apply_relocations(&mut self) -> Result<(), Error> {
|
|
for reloc in self.relocations.iter() {
|
|
match reloc.rtype {
|
|
RelocationType::Displacement => {
|
|
// TODO this doesn't yet take into accound the origin
|
|
let location = *self.labels.get(&reloc.label).ok_or_else(|| Error::new(&format!("error during relocation, label undefined {:?}", reloc.label)))?;
|
|
self.output[reloc.index] |= ((self.output[reloc.index] as i8 * 2 + 2) - (location as i8 * 2)) as u16 & 0x00ff;
|
|
},
|
|
_ => panic!("relocation type unimplemented"),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn convert(&mut self, lineno: usize, line: &AssemblyLine) -> Result<(), Error> {
|
|
match line {
|
|
AssemblyLine::Directive(name, list) => {
|
|
println!("skipping directive {} ({:?})", name, list);
|
|
},
|
|
AssemblyLine::Label(label) => {
|
|
println!("label {}", label);
|
|
self.labels.insert(label.clone(), self.output.len() - 1);
|
|
},
|
|
AssemblyLine::Instruction(name, list) => {
|
|
self.convert_instruction(lineno, name, list)?;
|
|
},
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn convert_instruction(&mut self, lineno: usize, mneumonic: &str, args: &[AssemblyOperand]) -> Result<(), Error> {
|
|
match mneumonic {
|
|
"bra" => {
|
|
let label = parser::expect_label(lineno, args)?;
|
|
self.output.push(0x6000);
|
|
self.relocations.push(Relocation::new(RelocationType::Displacement, label, self.output.len() - 1, self.current_origin));
|
|
},
|
|
"bsr" => {
|
|
let label = parser::expect_label(lineno, args)?;
|
|
self.output.push(0x6100);
|
|
self.relocations.push(Relocation::new(RelocationType::Displacement, label, self.output.len() - 1, self.current_origin));
|
|
},
|
|
"illegal" => {
|
|
self.output.push(0x4AFC);
|
|
},
|
|
|
|
"lea" => {
|
|
parser::expect_args(lineno, args, 2)?;
|
|
let reg = expect_address_register(lineno, &args[0])?;
|
|
let (effective_address, additional_words) = convert_target(lineno, &args[1], Size::Long, Disallow::NoRegsPrePostOrImmediate)?;
|
|
self.output.push(0x41C0 | (reg << 9) | effective_address);
|
|
self.output.extend(additional_words);
|
|
},
|
|
"nop" => {
|
|
self.output.push(0x4E71);
|
|
},
|
|
"rts" => {
|
|
self.output.push(0x4E75);
|
|
},
|
|
"rte" => {
|
|
self.output.push(0x4E73);
|
|
},
|
|
"rtr" => {
|
|
self.output.push(0x4E77);
|
|
},
|
|
"stop" => {
|
|
let immediate = parser::expect_immediate(lineno, &args[0])?;
|
|
self.output.push(0x4E72);
|
|
self.output.extend(convert_immediate(lineno, immediate, Size::Word)?);
|
|
},
|
|
"trapv" => {
|
|
self.output.push(0x4E76);
|
|
},
|
|
|
|
_ => {
|
|
self.convert_sized_instruction(lineno, mneumonic, args)?;
|
|
},
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn convert_sized_instruction(&mut self, lineno: usize, mneumonic: &str, args: &[AssemblyOperand]) -> Result<(), Error> {
|
|
let operation_size = get_size_from_mneumonic(mneumonic).ok_or_else(|| Error::new(&format!("error at line {}: expected a size specifier (b/w/l)", lineno)));
|
|
match &mneumonic[..mneumonic.len() - 1] {
|
|
"addi" => {
|
|
self.convert_common_immediate_instruction(lineno, 0x0600, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
"addai" => {
|
|
self.convert_common_immediate_instruction(lineno, 0x0600, args, operation_size?, Disallow::OnlyAReg)?;
|
|
},
|
|
"add" => {
|
|
self.convert_common_dreg_instruction(lineno, 0xD000, args, operation_size?, Disallow::None)?;
|
|
},
|
|
"adda" => {
|
|
self.convert_common_areg_instruction(lineno, 0xD000, args, operation_size?, Disallow::None)?;
|
|
},
|
|
"andi" => {
|
|
if !self.check_convert_flags_instruction(lineno, 0x23C, 0x27C, args)? {
|
|
self.convert_common_immediate_instruction(lineno, 0x0200, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
}
|
|
},
|
|
"and" => {
|
|
self.convert_common_dreg_instruction(lineno, 0xC000, args, operation_size?, Disallow::None)?;
|
|
},
|
|
"asr" | "asl" => {
|
|
self.convert_common_shift_instruction(lineno, mneumonic, 0xE000, args, operation_size?)?;
|
|
},
|
|
|
|
"clr" => {
|
|
self.convert_common_single_operand_instruction(lineno, 0x4200, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
"cmpi" => {
|
|
self.convert_common_immediate_instruction(lineno, 0x0C00, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
"cmp" => {
|
|
self.convert_common_dreg_instruction(lineno, 0xB000, args, operation_size?, Disallow::None)?;
|
|
},
|
|
|
|
"eori" => {
|
|
if !self.check_convert_flags_instruction(lineno, 0x0A3C, 0x0A7C, args)? {
|
|
self.convert_common_immediate_instruction(lineno, 0x0A00, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
}
|
|
},
|
|
"eor" => {
|
|
self.convert_common_dreg_instruction(lineno, 0xB000, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
|
|
"lsr" | "lsl" => {
|
|
self.convert_common_shift_instruction(lineno, mneumonic, 0xE008, args, operation_size?)?;
|
|
},
|
|
|
|
"move" | "movea" => {
|
|
let operation_size = operation_size?;
|
|
parser::expect_args(lineno, args, 2)?;
|
|
let (effective_address_left, additional_words_left) = convert_target(lineno, &args[0], operation_size, Disallow::None)?;
|
|
let (effective_address_right, additional_words_right) = convert_target(lineno, &args[1], operation_size, Disallow::None)?;
|
|
let effective_address_left = (effective_address_left >> 3) | (effective_address_left << 3);
|
|
self.output.push(encode_size_for_move(operation_size) | effective_address_left | effective_address_right);
|
|
self.output.extend(additional_words_left);
|
|
self.output.extend(additional_words_right);
|
|
},
|
|
|
|
"neg" => {
|
|
self.convert_common_single_operand_instruction(lineno, 0x4400, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
"negx" => {
|
|
self.convert_common_single_operand_instruction(lineno, 0x4000, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
"not" => {
|
|
self.convert_common_single_operand_instruction(lineno, 0x4600, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
|
|
"ori" => {
|
|
if !self.check_convert_flags_instruction(lineno, 0x003C, 0x007C, args)? {
|
|
self.convert_common_immediate_instruction(lineno, 0x0000, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
}
|
|
},
|
|
"or" => {
|
|
self.convert_common_dreg_instruction(lineno, 0x8000, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
|
|
"ror" | "rol" => {
|
|
self.convert_common_shift_instruction(lineno, mneumonic, 0xE018, args, operation_size?)?;
|
|
},
|
|
|
|
"roxr" | "roxl" => {
|
|
self.convert_common_shift_instruction(lineno, mneumonic, 0xE010, args, operation_size?)?;
|
|
},
|
|
|
|
"subi" => {
|
|
self.convert_common_immediate_instruction(lineno, 0x0400, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
|
},
|
|
"subai" => {
|
|
self.convert_common_immediate_instruction(lineno, 0x0400, args, operation_size?, Disallow::OnlyAReg)?;
|
|
},
|
|
"sub" => {
|
|
self.convert_common_dreg_instruction(lineno, 0x9000, args, operation_size?, Disallow::None)?;
|
|
},
|
|
"suba" => {
|
|
self.convert_common_areg_instruction(lineno, 0x9000, args, operation_size?, Disallow::None)?;
|
|
},
|
|
|
|
// TODO complete remaining instructions
|
|
_ => return Err(Error::new(&format!("unrecognized instruction at line {}: {:?}", lineno, mneumonic))),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn convert_common_immediate_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
|
|
parser::expect_args(lineno, args, 2)?;
|
|
let immediate = parser::expect_immediate(lineno, &args[0])?;
|
|
let (effective_address, additional_words) = convert_target(lineno, &args[1], operation_size, disallow)?;
|
|
self.output.push(opcode | encode_size(operation_size) | effective_address);
|
|
self.output.extend(convert_immediate(lineno, immediate, operation_size)?);
|
|
self.output.extend(additional_words);
|
|
Ok(())
|
|
}
|
|
|
|
fn convert_common_dreg_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
|
|
parser::expect_args(lineno, args, 2)?;
|
|
let (direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoAReg)?;
|
|
let (effective_address, additional_words) = convert_target(lineno, operand, operation_size, disallow)?;
|
|
self.output.push(opcode | encode_size(operation_size) | direction | (reg << 9) | effective_address);
|
|
self.output.extend(additional_words);
|
|
Ok(())
|
|
}
|
|
|
|
fn convert_common_areg_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
|
|
let size_bit = expect_a_instruction_size(lineno, operation_size)?;
|
|
parser::expect_args(lineno, args, 2)?;
|
|
//let (_direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoDReg)?;
|
|
let reg = expect_address_register(lineno, &args[1])?;
|
|
let (effective_address, additional_words) = convert_target(lineno, &args[0], operation_size, disallow)?;
|
|
self.output.push(opcode | size_bit | (0b11 << 6) | (reg << 9) | effective_address);
|
|
self.output.extend(additional_words);
|
|
Ok(())
|
|
}
|
|
|
|
fn convert_common_single_operand_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
|
|
parser::expect_args(lineno, args, 1)?;
|
|
let (effective_address, additional_words) = convert_target(lineno, &args[0], operation_size, disallow)?;
|
|
self.output.push(opcode | encode_size(operation_size) | effective_address);
|
|
self.output.extend(additional_words);
|
|
Ok(())
|
|
}
|
|
|
|
fn check_convert_flags_instruction(&mut self, lineno: usize, opcode_ccr: u16, opcode_sr: u16, args: &[AssemblyOperand]) -> Result<bool, Error> {
|
|
if let AssemblyOperand::Register(name) = &args[1] {
|
|
let opcode = match name.as_str() {
|
|
"ccr" => Some(opcode_ccr),
|
|
"sr" => Some(opcode_sr),
|
|
_ => None,
|
|
};
|
|
|
|
if let Some(opcode) = opcode {
|
|
parser::expect_args(lineno, args, 2)?;
|
|
let immediate = parser::expect_immediate(lineno, &args[0])?;
|
|
self.output.push(opcode);
|
|
self.output.extend(convert_immediate(lineno, immediate, Size::Word)?);
|
|
return Ok(true);
|
|
}
|
|
}
|
|
Ok(false)
|
|
}
|
|
|
|
fn convert_common_shift_instruction(&mut self, lineno: usize, mneumonic: &str, opcode: u16, args: &[AssemblyOperand], operation_size: Size) -> Result<(), Error> {
|
|
let dirstr = &mneumonic[mneumonic.len() - 2..mneumonic.len() - 1];
|
|
let direction = if dirstr == "r" {
|
|
0
|
|
} else if dirstr == "l" {
|
|
1 << 8
|
|
} else {
|
|
return Err(Error::new(&format!("error at line {}: expected direction of (l)eft or (r)ight, but found {:?}", lineno, dirstr)));
|
|
};
|
|
|
|
match &args {
|
|
[AssemblyOperand::Immediate(_), AssemblyOperand::Register(_)] => {
|
|
let mut immediate = parser::expect_immediate(lineno, &args[0])?;
|
|
if !(1..=8).contains(&immediate) {
|
|
return Err(Error::new(&format!("error at line {}: immediate value must be between 1 and 8, found {:?}", lineno, args)));
|
|
} else if immediate == 8 {
|
|
immediate = 0;
|
|
}
|
|
|
|
let reg = expect_data_register(lineno, &args[1])?;
|
|
self.output.push(opcode | ((immediate as u16) << 9) | direction | encode_size(operation_size) /*(0b0 << 5)*/ | reg);
|
|
},
|
|
[AssemblyOperand::Register(_), AssemblyOperand::Register(_)] => {
|
|
let bit_reg = expect_data_register(lineno, &args[0])?;
|
|
let reg = expect_data_register(lineno, &args[1])?;
|
|
self.output.push(opcode | (bit_reg << 9) | direction | encode_size(operation_size) | (0b1 << 5) | reg);
|
|
},
|
|
//[_] => {
|
|
// let (effective_address, additional_words) = convert_target(lineno, &args[0], Size::Word, Disallow::NoRegsImmediateOrPC)?;
|
|
// self.output.push(opcode | effective_address);
|
|
// self.output.extend(additional_words);
|
|
//},
|
|
_ => return Err(Error::new(&format!("error at line {}: unexpected addressing mode, found {:?}", lineno, args))),
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn convert_target(lineno: usize, operand: &AssemblyOperand, size: Size, disallow: Disallow) -> Result<(u16, Vec<u16>), Error> {
|
|
match operand {
|
|
AssemblyOperand::Register(name) => {
|
|
convert_register(lineno, name, disallow)
|
|
},
|
|
AssemblyOperand::Immediate(value) => {
|
|
disallow.check(lineno, Disallow::NoImmediate)?;
|
|
Ok((0b111100, convert_immediate(lineno, *value, size)?))
|
|
},
|
|
AssemblyOperand::Indirect(args) => {
|
|
convert_indirect(lineno, args, disallow)
|
|
},
|
|
AssemblyOperand::IndirectPost(args, operator) => {
|
|
disallow.check(lineno, Disallow::NoIndirectPost)?;
|
|
if args.len() == 1 && operator == "+" {
|
|
if let AssemblyOperand::Register(name) = &args[0] {
|
|
if name.starts_with('a') {
|
|
let reg = expect_reg_num(lineno, name)?;
|
|
return Ok(((0b011 << 3) | reg, vec![]));
|
|
}
|
|
}
|
|
}
|
|
Err(Error::new(&format!("error at line {}: post-increment operator can only be used with a single address register", lineno)))
|
|
},
|
|
AssemblyOperand::IndirectPre(operator, args) => {
|
|
disallow.check(lineno, Disallow::NoIndirectPre)?;
|
|
if args.len() == 1 && operator == "-" {
|
|
if let AssemblyOperand::Register(name) = &args[0] {
|
|
if name.starts_with('a') {
|
|
let reg = expect_reg_num(lineno, name)?;
|
|
return Ok(((0b100 << 3) | reg, vec![]));
|
|
} else if name == "sp" {
|
|
return Ok((0b100111, vec![]));
|
|
}
|
|
}
|
|
}
|
|
Err(Error::new(&format!("error at line {}: pre-decrement operator can only be used with a single address register", lineno)))
|
|
},
|
|
_ => Err(Error::new(&format!("not implemented: {:?}", operand))),
|
|
}
|
|
}
|
|
|
|
fn convert_register(lineno: usize, name: &str, disallow: Disallow) -> Result<(u16, Vec<u16>), Error> {
|
|
match name {
|
|
name if name.starts_with('d') => {
|
|
disallow.check(lineno, Disallow::NoDReg)?;
|
|
let reg = expect_reg_num(lineno, name)?;
|
|
Ok((/*(0b000 << 3)*/ reg, vec![]))
|
|
},
|
|
name if name.starts_with('a') => {
|
|
disallow.check(lineno, Disallow::NoAReg)?;
|
|
let reg = expect_reg_num(lineno, name)?;
|
|
Ok(((0b001 << 3) | reg, vec![]))
|
|
},
|
|
"sp" => {
|
|
disallow.check(lineno, Disallow::NoAReg)?;
|
|
Ok(((0b001 << 3) | 7, vec![]))
|
|
},
|
|
_ => Err(Error::new(&format!("error at line {}: invalid register {:?}", lineno, name))),
|
|
}
|
|
}
|
|
|
|
fn convert_indirect(lineno: usize, args: &[AssemblyOperand], disallow: Disallow) -> Result<(u16, Vec<u16>), Error> {
|
|
match &args {
|
|
[AssemblyOperand::Register(name)] => {
|
|
disallow.check(lineno, Disallow::NoIndirect)?;
|
|
let reg = expect_address_reg_num(lineno, name)?;
|
|
Ok(((0b010 << 3) | reg, vec![]))
|
|
},
|
|
[AssemblyOperand::Immediate(address)] => {
|
|
disallow.check(lineno, Disallow::NoIndirectImmediate)?;
|
|
if *address < u16::MAX as usize {
|
|
Ok((0b111000, convert_immediate(lineno, *address, Size::Word)?))
|
|
} else {
|
|
Ok((0b111001, convert_immediate(lineno, *address, Size::Long)?))
|
|
}
|
|
},
|
|
[AssemblyOperand::Immediate(offset), AssemblyOperand::Register(name)] => {
|
|
if name == "pc" {
|
|
disallow.check(lineno, Disallow::NoPCRelative)?;
|
|
Ok((0b111010, convert_immediate(lineno, *offset, Size::Word)?))
|
|
} else {
|
|
disallow.check(lineno, Disallow::NoIndirectOffset)?;
|
|
let reg = expect_address_reg_num(lineno, name)?;
|
|
Ok(((0b101 << 3) | reg, convert_immediate(lineno, *offset, Size::Word)?))
|
|
}
|
|
},
|
|
[AssemblyOperand::Immediate(offset), AssemblyOperand::Register(name), AssemblyOperand::Register(index)] => {
|
|
let index_reg = expect_reg_num(lineno, index)?;
|
|
let da_select = if index.starts_with('a') { 1 << 15 } else { 0 };
|
|
if name == "pc" {
|
|
disallow.check(lineno, Disallow::NoPCRelativeIndex)?;
|
|
Ok((0b111011, vec![da_select | (index_reg << 12) | ((*offset as u16) & 0xff)]))
|
|
} else {
|
|
disallow.check(lineno, Disallow::NoIndirectIndexReg)?;
|
|
let reg = expect_address_reg_num(lineno, name)?;
|
|
Ok(((0b110 << 3) | reg, vec![da_select | (index_reg << 12) | ((*offset as u16) & 0xff)]))
|
|
}
|
|
},
|
|
// TODO add the MC68020 address options
|
|
_ => {
|
|
Err(Error::new(&format!("error at line {}: expected valid indirect addressing mode, but found {:?}", lineno, args)))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn convert_reg_and_other(lineno: usize, args: &[AssemblyOperand], _disallow: Disallow) -> Result<(u16, u16, &AssemblyOperand), Error> {
|
|
match &args {
|
|
[AssemblyOperand::Register(reg), effective_address] => {
|
|
Ok(((0b1 << 8), expect_reg_num(lineno, reg)?, effective_address))
|
|
},
|
|
[effective_address, AssemblyOperand::Register(reg)] => {
|
|
Ok(((0b0 << 8), expect_reg_num(lineno, reg)?, effective_address))
|
|
},
|
|
_ => {
|
|
Err(Error::new(&format!("error at line {}: expected register and effective address, but found {:?}", lineno, args)))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn convert_immediate(lineno: usize, value: usize, size: Size) -> Result<Vec<u16>, Error> {
|
|
match size {
|
|
Size::Byte => {
|
|
if value <= u8::MAX as usize {
|
|
Ok(vec![value as u16])
|
|
} else {
|
|
Err(Error::new(&format!("error at line {}: immediate number is out of range; must be less than {}, but number is {:?}", lineno, u8::MAX, value)))
|
|
}
|
|
},
|
|
Size::Word => {
|
|
if value <= u16::MAX as usize {
|
|
Ok(vec![value as u16])
|
|
} else {
|
|
Err(Error::new(&format!("error at line {}: immediate number is out of range; must be less than {}, but number is {:?}", lineno, u16::MAX, value)))
|
|
}
|
|
},
|
|
Size::Long => Ok(vec![(value >> 16) as u16, value as u16]),
|
|
}
|
|
}
|
|
|
|
fn expect_data_register(lineno: usize, operand: &AssemblyOperand) -> Result<u16, Error> {
|
|
if let AssemblyOperand::Register(name) = operand {
|
|
if name.starts_with('d') {
|
|
return expect_reg_num(lineno, name);
|
|
}
|
|
}
|
|
Err(Error::new(&format!("error at line {}: expected a data register, but found {:?}", lineno, operand)))
|
|
}
|
|
|
|
fn expect_address_register(lineno: usize, operand: &AssemblyOperand) -> Result<u16, Error> {
|
|
if let AssemblyOperand::Register(name) = operand {
|
|
if name.starts_with('a') {
|
|
return expect_reg_num(lineno, name);
|
|
}
|
|
}
|
|
Err(Error::new(&format!("error at line {}: expected an address register, but found {:?}", lineno, operand)))
|
|
}
|
|
|
|
fn expect_address_reg_num(lineno: usize, name: &str) -> Result<u16, Error> {
|
|
if name.starts_with('a') {
|
|
return expect_reg_num(lineno, name);
|
|
}
|
|
Err(Error::new(&format!("error at line {}: expected an address register, but found {:?}", lineno, name)))
|
|
}
|
|
|
|
fn expect_reg_num(lineno: usize, name: &str) -> Result<u16, Error> {
|
|
if let Ok(number) = str::parse::<u16>(&name[1..]) {
|
|
if number <= 7 {
|
|
return Ok(number);
|
|
}
|
|
}
|
|
Err(Error::new(&format!("error at line {}: no such register {:?}", lineno, name)))
|
|
}
|
|
|
|
fn expect_a_instruction_size(lineno: usize, size: Size) -> Result<u16, Error> {
|
|
match size {
|
|
Size::Word => Ok(0),
|
|
Size::Long => Ok(0b1 << 8),
|
|
_ => Err(Error::new(&format!("error at line {}: address instructions can only be word or long size", lineno))),
|
|
}
|
|
}
|
|
|
|
|
|
fn get_size_from_mneumonic(s: &str) -> Option<Size> {
|
|
let size_ch = s.chars().last()?;
|
|
match size_ch {
|
|
'b' => Some(Size::Byte),
|
|
'w' => Some(Size::Word),
|
|
'l' => Some(Size::Long),
|
|
_ => None
|
|
}
|
|
}
|
|
|
|
fn encode_size(size: Size) -> u16 {
|
|
match size {
|
|
Size::Byte => 0b00 << 6,
|
|
Size::Word => 0b01 << 6,
|
|
Size::Long => 0b10 << 6,
|
|
}
|
|
}
|
|
|
|
fn encode_size_for_move(size: Size) -> u16 {
|
|
match size {
|
|
Size::Byte => 0b01 << 12,
|
|
Size::Word => 0b11 << 12,
|
|
Size::Long => 0b10 << 12,
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn encode_size_bit(size: Size) -> Result<u16, Error> {
|
|
match size {
|
|
Size::Word => Ok(0b01 << 6),
|
|
Size::Long => Ok(0b10 << 6),
|
|
_ => Err(Error::new(&format!("invalid size for this operation: {:?}", size)))
|
|
}
|
|
}
|
|
|