diff --git a/Cargo.toml b/Cargo.toml index eb493dc..414ef1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ regex = "1.6.0" strum = "0.24.1" strum_macros = "0.24.3" thiserror = "1.0.32" +yaml-rust = "0.4.5" [lib] name = "mipsasm" diff --git a/src/assembler.rs b/src/assembler.rs index 7498a63..f0fe872 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -5,11 +5,11 @@ type J = ast::JTypeOp; type R = ast::RTypeOp; #[rustfmt::skip] -pub fn assemble(insts: Vec) -> Vec { - let mut bytes = vec![]; +pub fn assemble(insts: &mut Vec) { for inst in insts { - let i = match inst { - ast::Instruction::Immediate { op, rs, rt, imm } => match op { + let mut bytes = vec![]; + let b = match inst { + ast::Instruction::Immediate { op, rs, rt, imm, .. } => match op { I::Addi => 0b001000 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), I::Addiu => 0b001001 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), I::Andi => 0b001100 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), @@ -130,8 +130,49 @@ pub fn assemble(insts: Vec) -> Vec { I::Lh => 0b100001 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), I::Lhu => 0b100101 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), I::Li => { - bytes.push(0b001111 << 26 | rt.as_num() << 16 | imm.as_u32() >> 16); - 0b001101 << 26 | rt.as_num() << 21 | rt.as_num() << 16 | imm.as_u32() & 0xFFFF + let imm = imm.as_u32(); + // immediate can fit in 16 bits + if imm & 0xFFFF == imm { + // ori rt, zero, imm + 0b001101 << 26 | rt.as_num() << 16 | imm + } else { + // check if bottom 16 bits are 0 + if imm & 0xFFFF == 0 { + // lui rt, imm >> 16 + 0b001111 << 26 | rt.as_num() << 16 | imm >> 16 + // check if immediate is negative i16 + } else if imm & 0xFFFF8000 == 0xFFFF8000 { + // lui rt, imm >> 16 + bytes.push(0b001111 << 26 | rt.as_num() << 16 | imm >> 16); + // addiu rt, rt, imm & 0xFFFF + 0b001001 << 26 | rt.as_num() << 21 | rt.as_num() << 16 | imm & 0xFFFF + // immediate is positive + } else { + // lui rt, imm >> 16 + bytes.push(0b001111 << 26 | rt.as_num() << 16 | imm >> 16); + // ori rt, rt, imm & 0xFFFF + 0b001101 << 26 | rt.as_num() << 21 | rt.as_num() << 16 | imm & 0xFFFF + } + } + } + I::Liu => { + let imm = imm.as_u32(); + // immediate can fit in 16 bits + if imm & 0xFFFF == imm { + // ori rt, zero, imm + 0b001101 << 26 | rt.as_num() << 16 | imm + } else { + // check if bottom 16 bits are 0 + if imm & 0xFFFF == 0 { + // lui rt, imm >> 16 + 0b001111 << 26 | rt.as_num() << 16 | imm >> 16 + } else { + // lui rt, imm >> 16 + bytes.push(0b001111 << 26 | rt.as_num() << 16 | imm >> 16); + // ori rt, rt, imm & 0xFFFF + 0b001101 << 26 | rt.as_num() << 21 | rt.as_num() << 16 | imm & 0xFFFF + } + } } I::Ll => 0b110000 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), I::Lld => 0b110100 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), @@ -167,11 +208,11 @@ pub fn assemble(insts: Vec) -> Vec { I::Tnei => 0b000001 << 26 | rs.as_num() << 21 | 0b01110 << 16 | imm.as_u32(), I::Xori => 0b001110 << 26 | rs.as_num() << 21 | rt.as_num() << 16 | imm.as_u32(), } - ast::Instruction::Jump { op, target } => match op { + ast::Instruction::Jump { op, target, .. } => match op { J::J => 0b000010 << 26 | (target.as_u32() & 0x3FFFFFF) >> 2, J::Jal => 0b000011 << 26 | (target.as_u32() & 0x3FFFFFF) >> 2, } - ast::Instruction::Register { op, rs, rt, rd, sa } => match op { + ast::Instruction::Register { op, rs, rt, rd, sa, .. } => match op { R::Abs => { bytes.push(rs.as_num() << 16 | 0b000001 << 11 | 31 << 6 | 0b000011); bytes.push(rs.as_num() << 21 | 0b000001 << 16 | rd.as_num() << 11 | 0b100110); @@ -184,10 +225,10 @@ pub fn assemble(insts: Vec) -> Vec { R::AddS => 0b010001 << 26 | 0b10000 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | rd.as_num() << 6, R::AddD => 0b010001 << 26 | 0b10001 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | rd.as_num() << 6, R::And => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b100100, - R::Break => sa << 6 | 0b001101, + R::Break => *sa << 6 | 0b001101, R::Clear => rd.as_num() << 11 | 0b100001, - R::Cs => 0b010001 << 26 | 0b10000 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | 0b0000011 << 4 | sa as u32, - R::Cd => 0b010001 << 26 | 0b10001 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | 0b0000011 << 4 | sa as u32, + R::Cs => 0b010001 << 26 | 0b10000 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | 0b0000011 << 4 | *sa as u32, + R::Cd => 0b010001 << 26 | 0b10001 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | 0b0000011 << 4 | *sa as u32, R::CeilLS => 0b010001 << 26 | 0b10000 << 21 | rs.as_num() << 11 | rd.as_num() << 6 | 0b001010, R::CeilLD => 0b010001 << 26 | 0b10001 << 21 | rs.as_num() << 11 | rd.as_num() << 6 | 0b001010, R::CeilWS => 0b010001 << 26 | 0b10000 << 21 | rs.as_num() << 11 | rd.as_num() << 6 | 0b001110, @@ -316,14 +357,14 @@ pub fn assemble(insts: Vec) -> Vec { bytes.push(rt.as_num() << 21 | rs.as_num() << 16 | rd.as_num() << 11 | 0b010110); rd.as_num() << 21 | 0b000001 << 16 | rd.as_num() << 11 | 0b100101 } - R::Dsll => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b111000, - R::Dsll32 => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b111100, + R::Dsll => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b111000, + R::Dsll32 => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b111100, R::Dsllv => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b010100, - R::Dsra => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b111011, - R::Dsra32 => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b111111, + R::Dsra => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b111011, + R::Dsra32 => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b111111, R::Dsrav => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b010111, - R::Dsrl => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b111010, - R::Dsrl32 => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b111110, + R::Dsrl => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b111010, + R::Dsrl32 => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b111110, R::Dsrlv => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b010110, R::Dsub => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b101110, R::Dsubu => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b101111, @@ -420,7 +461,7 @@ pub fn assemble(insts: Vec) -> Vec { bytes.push(rt.as_num() << 21 | rs.as_num() << 16 | rd.as_num() << 11 | 0b101011); 0b001110 << 26 | rd.as_num() << 21 | rd.as_num() << 16 | 1 } - R::Sll => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6, + R::Sll => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6, R::Sllv => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b000100, R::Slt => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b101010, R::Sltu => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b101011, @@ -430,16 +471,16 @@ pub fn assemble(insts: Vec) -> Vec { } R::SqrtS => 0b010001 << 26 | 0b10000 << 21 | rs.as_num() << 11 | rd.as_num() << 6 | 0b000100, R::SqrtD => 0b010001 << 26 | 0b10001 << 21 | rs.as_num() << 11 | rd.as_num() << 6 | 0b000100, - R::Sra => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b000011, + R::Sra => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b000011, R::Srav => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b000111, - R::Srl => rt.as_num() << 16 | rd.as_num() << 11 | (sa as u32) << 6 | 0b000010, + R::Srl => rt.as_num() << 16 | rd.as_num() << 11 | (*sa as u32) << 6 | 0b000010, R::Srlv => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b000110, R::Sub => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b100010, R::Subu => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b100011, R::SubS => 0b010001 << 26 | 0b10000 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | rd.as_num() << 6 | 0b000001, R::SubD => 0b010001 << 26 | 0b10001 << 21 | rt.as_num() << 16 | rs.as_num() << 11 | rd.as_num() << 6 | 0b000001, R::Sync => 0b001111, - R::Syscall => sa << 6 | 0b001100, + R::Syscall => *sa << 6 | 0b001100, R::Teq => rs.as_num() << 21 | rt.as_num() << 16 | 0b110100, R::Tge => rs.as_num() << 21 | rt.as_num() << 16 | 0b110000, R::Tgeu => rs.as_num() << 21 | rt.as_num() << 16 | 0b110001, @@ -456,8 +497,9 @@ pub fn assemble(insts: Vec) -> Vec { R::TruncWD => 0b010001 << 26 | 0b10001 << 21 | rs.as_num() << 11 | rd.as_num() << 6 | 0b001101, R::Xor => rs.as_num() << 21 | rt.as_num() << 16 | rd.as_num() << 11 | 0b100110, } + ast::Instruction::Bytes { bytes: b } => *b, }; - bytes.push(i); + inst.push_bytes(&mut bytes); + inst.push_bytes(&mut vec![b]); } - bytes } diff --git a/src/ast.rs b/src/ast.rs index a7e7a12..2b86711 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,5 +1,5 @@ -use regex::Regex; use once_cell::sync::Lazy; +use regex::Regex; use std::convert::{From, TryFrom}; use std::fmt; use std::str::FromStr; @@ -50,6 +50,7 @@ impl Immediate { match self { Immediate::Short(i) => *i as u32, Immediate::Int(i) => *i, + Immediate::Long(i) => *i as u32, x => panic!("Cannot convert `{:?}` to u32", x), } } @@ -103,10 +104,15 @@ struct Signed(u16); // https://stackoverflow.com/a/44712309 impl fmt::LowerHex for Signed { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let val = self.0 as i16; + let val = self.0; let p = if f.alternate() { "0x" } else { "" }; - let x = format!("{:x}", val.abs()); - f.pad_integral(val >= 0, p, &x) + let abs_val = if val == i16::MAX.unsigned_abs() { + i16::MAX.unsigned_abs() + } else { + (val as i16).unsigned_abs() + }; + let x = format!("{:x}", abs_val); + f.pad_integral(val < i16::MAX.unsigned_abs(), p, &x) } } @@ -117,10 +123,12 @@ pub enum Instruction { rs: Register, rt: Register, imm: Immediate, + bytes: Vec, }, Jump { op: JTypeOp, target: Target, + bytes: Vec, }, Register { op: RTypeOp, @@ -128,11 +136,16 @@ pub enum Instruction { rt: Register, rd: Register, sa: u32, + bytes: Vec, + }, + Bytes { + bytes: u32, }, } type I = ITypeOp; type R = RTypeOp; +type J = JTypeOp; impl Instruction { pub fn has_delay_slot(&self) -> bool { @@ -168,6 +181,80 @@ impl Instruction { | I::Bc1tl ), Instruction::Register { op, .. } => matches!(op, R::Jr | R::Jalr), + Instruction::Bytes { .. } => false, + } + } + + pub fn push_bytes(&mut self, bytes: &mut Vec) { + match self { + Instruction::Immediate { bytes: b, .. } + | Instruction::Jump { bytes: b, .. } + | Instruction::Register { bytes: b, .. } => b.append(bytes), + _ => panic!(), + } + } + + pub fn get_bytes(&self) -> Vec { + match self { + Instruction::Immediate { bytes: b, .. } + | Instruction::Jump { bytes: b, .. } + | Instruction::Register { bytes: b, .. } => b.clone(), + Instruction::Bytes { bytes: b } => vec![*b], + } + } + + pub fn is_branch(&self) -> bool { + match &self { + Instruction::Immediate { op, .. } => matches!( + op, + I::Bltz + | I::Bgez + | I::Bltzl + | I::Bgezl + | I::Bltzal + | I::Bgezal + | I::Bltzall + | I::Bgezall + | I::Bal + | I::Bc0f + | I::Bc0t + | I::Bc0fl + | I::Bc0tl + | I::Bc1f + | I::Bc1t + | I::Bc1fl + | I::Bc1tl + | I::Beq + | I::Bne + | I::Beql + | I::Bnel + | I::Blez + | I::Blezl + | I::Bgtz + | I::Bgtzl + ), + _ => false, + } + } + + pub fn is_unconditional_jump(&self) -> bool { + matches!( + self, + Instruction::Jump { op: J::J, .. } | Instruction::Register { op: R::Jr, .. } + ) + } + + pub fn get_branch_offset(&self) -> i32 { + match &self { + Instruction::Immediate { imm, .. } => imm.as_u32() as i32, + _ => panic!(), + } + } + + pub fn get_jump_target(&self) -> Option { + match &self { + Instruction::Jump { target, .. } => Some(target.as_u32()), + _ => None, } } } @@ -180,6 +267,7 @@ impl fmt::Display for Instruction { rs, rt, imm: Immediate::Short(imm), + .. } => match op { I::Lb | I::Lbu @@ -270,12 +358,14 @@ impl fmt::Display for Instruction { rs, rt, imm: Immediate::Label(l), + .. } | Instruction::Immediate { op, rs, rt, imm: Immediate::LocalLabel(l), + .. } => match op { I::Beqz | I::Bgtz | I::Bgtzl | I::Blez | I::Blezl | I::Bnez => { write!(f, "{:11}${}, {}", op, rs, l) @@ -314,16 +404,25 @@ impl fmt::Display for Instruction { Instruction::Jump { op, target: Target::Address(target), + .. } => { write!(f, "{:11}{:#X?}", op, target) } Instruction::Jump { op, target: Target::Label(lbl), + .. } => { write!(f, "{:11}{}", op, lbl) } - Instruction::Register { op, rs, rt, rd, sa } => match op { + Instruction::Register { + op, + rs, + rt, + rd, + sa, + bytes, + } => match op { R::Sync => write!(f, "{}", op), R::Add | R::Addu @@ -392,7 +491,20 @@ impl fmt::Display for Instruction { write!(f, "{:11}${}", op, rd) } R::Cfc0 | R::Ctc0 | R::Dmfc0 | R::Dmtc0 | R::Mfc0 | R::Mtc0 => { - write!(f, "{:11}${}, {}", op, rt, Cop0Register::from(*rd)) + if let Ok(rd) = Cop0Register::try_from(*rd) { + write!(f, "{:11}${}, {}", op, rt, rd) + } else { + write!( + f, + "{:11}0x{:08x} {:>10} {:11}${}, {:#04x}", + ".word", + bytes.first().unwrap(), + "#", + op, + rt, + *rd as u32 + ) + } } R::Cfc1 | R::Ctc1 | R::Dmfc1 | R::Dmtc1 | R::Mfc1 | R::Mtc1 => { write!(f, "{:11}${}, ${}", op, rt, FloatRegister::from(*rd)) @@ -509,7 +621,8 @@ impl fmt::Display for Instruction { } e => panic!("{:?} not implemented", e), }, - e => panic!("Invalid instruction: {:?}", e), + Instruction::Bytes { bytes } => write!(f, "{:11}0x{:08x}", ".word", bytes), + e => panic!("Invalid instruction: {:?}", 0), } } } @@ -615,7 +728,6 @@ impl FromStr for Register { fn from_str(reg: &str) -> Result { let r = reg.trim().trim_start_matches('$'); - if REG_RE.find(r).is_some() { let r = r.trim_start_matches('r'); let r = r.parse::().unwrap(); @@ -924,9 +1036,11 @@ impl FromStr for Cop0Register { } } -impl From for Cop0Register { - fn from(reg: Register) -> Self { - Cop0Register::try_from(reg as u32).unwrap() +impl TryFrom for Cop0Register { + type Error = RegParseError; + + fn try_from(reg: Register) -> Result { + Cop0Register::try_from(reg as u32) } } @@ -1030,6 +1144,7 @@ pub enum ITypeOp { Dsubiu, Lli, Li, + Liu, Subi, Subiu, } diff --git a/src/bin/bin.rs b/src/bin/bin.rs index c5507fd..bcc30d2 100644 --- a/src/bin/bin.rs +++ b/src/bin/bin.rs @@ -1,11 +1,14 @@ +extern crate yaml_rust; + use clap::{Parser, ValueEnum}; -use mipsasm::Mipsasm; +use mipsasm::{get_bytes, Mipsasm}; use std::collections::HashMap; use std::error; use std::fs; use std::fs::File; use std::io::Write; use std::path::PathBuf; +use yaml_rust::YamlLoader; #[derive(Parser)] #[clap(author, version, about, long_about = None)] @@ -41,14 +44,21 @@ fn main() -> Result<(), Box> { None => String::new(), }; - // Parse symbols from string as format "name=0x12345678" - let symbols: HashMap<&str, u32> = HashMap::from_iter(syms.lines().map(|s| { - let mut parts = s.split('='); - let name = parts.next().unwrap().trim(); - let value = parts.next().unwrap(); - let value = u32::from_str_radix(value.replace("0x", "").trim(), 16).unwrap(); - (name, value) - })); + let yaml = YamlLoader::load_from_str(&syms).unwrap(); + let syms = yaml.first().map_or_else(HashMap::new, |y| { + y.as_hash() + .map(|hash| { + hash.iter() + .map(|(k, v)| { + ( + k.as_i64().unwrap_or_default() as u32, + v.as_str().unwrap_or_default(), + ) + }) + .collect() + }) + .unwrap_or_default() + }); let addr = cli.base_addr.replace("0x", ""); let addr = u32::from_str_radix(&addr, 16).unwrap_or_else(|_| { @@ -59,7 +69,7 @@ fn main() -> Result<(), Box> { match cli.mode { Mode::Asm => { let data: String = fs::read_to_string(cli.input_file)?.parse()?; - let output = match Mipsasm::new().base(addr).symbols(symbols).assemble(&data) { + let output = match Mipsasm::new().base(addr).symbols(syms).assemble(&data) { Ok(output) => output, Err(e) => { for err in e { @@ -69,11 +79,12 @@ fn main() -> Result<(), Box> { } }; + let output = get_bytes(&output); if let Some(output_file) = cli.output_file { - let mut bytes = vec![]; - for word in output { - bytes.append(&mut word.to_be_bytes().to_vec()); - } + let bytes: Vec = output + .iter() + .flat_map(|word| word.to_be_bytes().to_vec()) + .collect(); File::create(output_file)?.write_all(&bytes)?; } else { println!("{:08X?}", output); @@ -92,10 +103,7 @@ fn main() -> Result<(), Box> { break; } } - let output = Mipsasm::new() - .base(addr) - .symbols(symbols) - .disassemble(&words); + let output = Mipsasm::new().base(addr).symbols(syms).disassemble(&words); if let Some(output_file) = cli.output_file { let mut f = File::create(output_file)?; diff --git a/src/disassembler.rs b/src/disassembler.rs index 11b6fee..f583216 100644 --- a/src/disassembler.rs +++ b/src/disassembler.rs @@ -1,9 +1,48 @@ use crate::ast; -type R = ast::Register; +macro_rules! inst { + (Imm, $op:ident, $rs:expr, $rt:expr, $imm:expr, $bytes:expr) => { + ast::Instruction::Immediate { + op: ast::ITypeOp::$op, + rs: ast::Register::try_from($rs).unwrap(), + rt: ast::Register::try_from($rt).unwrap(), + imm: ast::Immediate::Short($imm as u16), + bytes: vec![$bytes], + } + }; + (Jump, $op:ident, $target:expr, $bytes:expr) => { + ast::Instruction::Jump { + op: ast::JTypeOp::$op, + target: ast::Target::Address($target), + bytes: vec![$bytes], + } + }; + (Reg, $op:ident, $rs:expr, $rt:expr, $rd:expr, $bytes:expr) => { + ast::Instruction::Register { + op: ast::RTypeOp::$op, + rs: ast::Register::try_from($rs).unwrap(), + rt: ast::Register::try_from($rt).unwrap(), + rd: ast::Register::try_from($rd).unwrap(), + sa: 0, + bytes: vec![$bytes], + } + }; + (Reg, $op:ident, $rs:expr, $rt:expr, $rd:expr, $sa:expr, $bytes:expr) => { + ast::Instruction::Register { + op: ast::RTypeOp::$op, + rs: ast::Register::try_from($rs).unwrap(), + rt: ast::Register::try_from($rt).unwrap(), + rd: ast::Register::try_from($rd).unwrap(), + sa: $sa, + bytes: vec![$bytes], + } + }; + (Bytes, $imm:expr) => { + ast::Instruction::Bytes { bytes: $imm } + }; +} -#[rustfmt::skip] -pub fn disassemble(bytes: Vec) -> Vec { +pub fn disassemble(bytes: Vec, base_addr: u32) -> Vec { let mut insts = vec![]; for inst in bytes { @@ -15,222 +54,219 @@ pub fn disassemble(bytes: Vec) -> Vec { let code = (inst >> 6) & 0xFFFFF; let funct = inst & 0x3F; let imm = inst & 0xFFFF; - let target = ((inst & 0x3FFFFFF) << 2) | 0x80000000; - + let target = ((inst & 0x3FFFFFF) << 2) | (base_addr & !0xFFFFFFF); + let i = match op { - 0 => { - match funct { - 0 => ast::Instruction::Register { op: ast::RTypeOp::Sll, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 2 => ast::Instruction::Register { op: ast::RTypeOp::Srl, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 3 => ast::Instruction::Register { op: ast::RTypeOp::Sra, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 4 => ast::Instruction::Register { op: ast::RTypeOp::Sllv, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 6 => ast::Instruction::Register { op: ast::RTypeOp::Srlv, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 7 => ast::Instruction::Register { op: ast::RTypeOp::Srav, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 8 => ast::Instruction::Register { op: ast::RTypeOp::Jr, rs: R::try_from(rs).unwrap(), rt: R::null(), rd: R::null(), sa: 0 }, - 9 => ast::Instruction::Register { op: ast::RTypeOp::Jalr, rs: R::try_from(rs).unwrap(), rt: R::null(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 12 => ast::Instruction::Register { op: ast::RTypeOp::Syscall, rs: R::null(), rt: R::null(), rd: R::null(), sa: code }, - 13 => ast::Instruction::Register { op: ast::RTypeOp::Break, rs: R::null(), rt: R::null(), rd: R::null(), sa: code }, - 15 => ast::Instruction::Register { op: ast::RTypeOp::Sync, rs: R::null(), rt: R::null(), rd: R::null(), sa: 0 }, - 16 => ast::Instruction::Register { op: ast::RTypeOp::Mfhi, rs: R::null(), rt: R::null(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 17 => ast::Instruction::Register { op: ast::RTypeOp::Mthi, rs: R::try_from(rs).unwrap(), rt: R::null(), rd: R::null(), sa: 0 }, - 18 => ast::Instruction::Register { op: ast::RTypeOp::Mflo, rs: R::null(), rt: R::null(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 19 => ast::Instruction::Register { op: ast::RTypeOp::Mtlo, rs: R::try_from(rs).unwrap(), rt: R::null(), rd: R::null(), sa: 0 }, - 20 => ast::Instruction::Register { op: ast::RTypeOp::Dsllv, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 22 => ast::Instruction::Register { op: ast::RTypeOp::Dsrlv, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 23 => ast::Instruction::Register { op: ast::RTypeOp::Dsrav, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 24 => ast::Instruction::Register { op: ast::RTypeOp::Mult, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 25 => ast::Instruction::Register { op: ast::RTypeOp::Multu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 26 => ast::Instruction::Register { op: ast::RTypeOp::Div, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 27 => ast::Instruction::Register { op: ast::RTypeOp::Divu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 28 => ast::Instruction::Register { op: ast::RTypeOp::Dmult, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 29 => ast::Instruction::Register { op: ast::RTypeOp::Dmultu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 30 => ast::Instruction::Register { op: ast::RTypeOp::Ddiv, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 31 => ast::Instruction::Register { op: ast::RTypeOp::Ddivu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 32 => ast::Instruction::Register { op: ast::RTypeOp::Add, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 33 => ast::Instruction::Register { op: ast::RTypeOp::Addu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 34 => ast::Instruction::Register { op: ast::RTypeOp::Sub, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 35 => ast::Instruction::Register { op: ast::RTypeOp::Subu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 36 => ast::Instruction::Register { op: ast::RTypeOp::And, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 37 => ast::Instruction::Register { op: ast::RTypeOp::Or, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 38 => ast::Instruction::Register { op: ast::RTypeOp::Xor, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 39 => ast::Instruction::Register { op: ast::RTypeOp::Nor, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 42 => ast::Instruction::Register { op: ast::RTypeOp::Slt, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 43 => ast::Instruction::Register { op: ast::RTypeOp::Sltu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 44 => ast::Instruction::Register { op: ast::RTypeOp::Dadd, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 45 => ast::Instruction::Register { op: ast::RTypeOp::Daddu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 46 => ast::Instruction::Register { op: ast::RTypeOp::Dsub, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 47 => ast::Instruction::Register { op: ast::RTypeOp::Dsubu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0 }, - 48 => ast::Instruction::Register { op: ast::RTypeOp::Tge, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 49 => ast::Instruction::Register { op: ast::RTypeOp::Tgeu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 50 => ast::Instruction::Register { op: ast::RTypeOp::Tlt, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 51 => ast::Instruction::Register { op: ast::RTypeOp::Tltu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 52 => ast::Instruction::Register { op: ast::RTypeOp::Teq, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 54 => ast::Instruction::Register { op: ast::RTypeOp::Tne, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: 0 }, - 56 => ast::Instruction::Register { op: ast::RTypeOp::Dsll, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 58 => ast::Instruction::Register { op: ast::RTypeOp::Dsrl, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 59 => ast::Instruction::Register { op: ast::RTypeOp::Dsra, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 60 => ast::Instruction::Register { op: ast::RTypeOp::Dsll32, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 62 => ast::Instruction::Register { op: ast::RTypeOp::Dsrl32, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - 63 => ast::Instruction::Register { op: ast::RTypeOp::Dsra32, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa }, - e => panic!("Invalid R-type instruction: {}", e), - } - } + 0 => match funct { + 0 => inst!(Reg, Sll, 0, rt, rd, sa, inst), + 2 => inst!(Reg, Srl, 0, rt, rd, sa, inst), + 3 => inst!(Reg, Sra, 0, rt, rd, sa, inst), + 4 => inst!(Reg, Sllv, rs, rt, rd, inst), + 6 => inst!(Reg, Srlv, rs, rt, rd, inst), + 7 => inst!(Reg, Srav, rs, rt, rd, inst), + 8 => inst!(Reg, Jr, rs, 0, 0, inst), + 9 => inst!(Reg, Jalr, rs, 0, rd, inst), + 12 => inst!(Reg, Syscall, 0, 0, 0, code, inst), + 13 => inst!(Reg, Break, 0, 0, 0, code, inst), + 15 => inst!(Reg, Sync, 0, 0, 0, code, inst), + 16 => inst!(Reg, Mfhi, 0, 0, rd, inst), + 17 => inst!(Reg, Mthi, rs, 0, 0, inst), + 18 => inst!(Reg, Mflo, 0, 0, rd, inst), + 19 => inst!(Reg, Mtlo, rs, 0, 0, inst), + 20 => inst!(Reg, Dsllv, rs, rt, rd, inst), + 22 => inst!(Reg, Dsrlv, rs, rt, rd, inst), + 23 => inst!(Reg, Dsrav, rs, rt, rd, inst), + 24 => inst!(Reg, Mult, rs, rt, 0, inst), + 25 => inst!(Reg, Multu, rs, rt, 0, inst), + 26 => inst!(Reg, Div, rs, rt, 0, inst), + 27 => inst!(Reg, Divu, rs, rt, 0, inst), + 28 => inst!(Reg, Dmult, rs, rt, 0, inst), + 29 => inst!(Reg, Dmultu, rs, rt, 0, inst), + 30 => inst!(Reg, Ddiv, rs, rt, 0, inst), + 31 => inst!(Reg, Ddivu, rs, rt, 0, inst), + 32 => inst!(Reg, Add, rs, rt, rd, inst), + 33 => inst!(Reg, Addu, rs, rt, rd, inst), + 34 => inst!(Reg, Sub, rs, rt, rd, inst), + 35 => inst!(Reg, Subu, rs, rt, rd, inst), + 36 => inst!(Reg, And, rs, rt, rd, inst), + 37 => inst!(Reg, Or, rs, rt, rd, inst), + 38 => inst!(Reg, Xor, rs, rt, rd, inst), + 39 => inst!(Reg, Nor, rs, rt, rd, inst), + 42 => inst!(Reg, Slt, rs, rt, rd, inst), + 43 => inst!(Reg, Sltu, rs, rt, rd, inst), + 44 => inst!(Reg, Dadd, rs, rt, rd, inst), + 45 => inst!(Reg, Daddu, rs, rt, rd, inst), + 46 => inst!(Reg, Dsub, rs, rt, rd, inst), + 47 => inst!(Reg, Dsubu, rs, rt, rd, inst), + 48 => inst!(Reg, Tge, rs, rt, 0, inst), + 49 => inst!(Reg, Tgeu, rs, rt, 0, inst), + 50 => inst!(Reg, Tlt, rs, rt, 0, inst), + 51 => inst!(Reg, Tltu, rs, rt, 0, inst), + 52 => inst!(Reg, Teq, rs, rt, 0, inst), + 54 => inst!(Reg, Tne, rs, rt, 0, inst), + 56 => inst!(Reg, Dsll, 0, rt, rd, sa, inst), + 58 => inst!(Reg, Dsrl, 0, rt, rd, sa, inst), + 59 => inst!(Reg, Dsra, 0, rt, rd, sa, inst), + 60 => inst!(Reg, Dsll32, 0, rt, rd, sa, inst), + 62 => inst!(Reg, Dsrl32, 0, rt, rd, sa, inst), + 63 => inst!(Reg, Dsra32, 0, rt, rd, sa, inst), + _ => inst!(Bytes, inst), + }, 1 => match rt { - 0 => ast::Instruction::Immediate { op: ast::ITypeOp::Bltz, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 1 => ast::Instruction::Immediate { op: ast::ITypeOp::Bgez, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 2 => ast::Instruction::Immediate { op: ast::ITypeOp::Bltzl, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 3 => ast::Instruction::Immediate { op: ast::ITypeOp::Bgezl, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 8 => ast::Instruction::Immediate { op: ast::ITypeOp::Tgei, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 9 => ast::Instruction::Immediate { op: ast::ITypeOp::Tgeiu, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 10 => ast::Instruction::Immediate { op: ast::ITypeOp::Tlti, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 11 => ast::Instruction::Immediate { op: ast::ITypeOp::Tltiu, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 12 => ast::Instruction::Immediate { op: ast::ITypeOp::Teqi, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 14 => ast::Instruction::Immediate { op: ast::ITypeOp::Tnei, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 16 => ast::Instruction::Immediate { op: ast::ITypeOp::Bltzal, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 17 => ast::Instruction::Immediate { op: ast::ITypeOp::Bgezal, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 18 => ast::Instruction::Immediate { op: ast::ITypeOp::Bltzall, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - 19 => ast::Instruction::Immediate { op: ast::ITypeOp::Bgezall, rs: R::try_from(rs).unwrap(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - e => panic!("Invalid I-type instruction: {}", e), - } - 2 => ast::Instruction::Jump { op: ast::JTypeOp::J, target: ast::Target::Address(target) }, - 3 => ast::Instruction::Jump { op: ast::JTypeOp::Jal, target: ast::Target::Address(target) }, - 4 => ast::Instruction::Immediate { op: ast::ITypeOp::Beq, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 5 => ast::Instruction::Immediate { op: ast::ITypeOp::Bne, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 6 => ast::Instruction::Immediate { op: ast::ITypeOp::Blez, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 7 => ast::Instruction::Immediate { op: ast::ITypeOp::Bgtz, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 8 => ast::Instruction::Immediate { op: ast::ITypeOp::Addi, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 9 => ast::Instruction::Immediate { op: ast::ITypeOp::Addiu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 10 => ast::Instruction::Immediate { op: ast::ITypeOp::Slti, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 11 => ast::Instruction::Immediate { op: ast::ITypeOp::Sltiu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 12 => ast::Instruction::Immediate { op: ast::ITypeOp::Andi, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 13 => ast::Instruction::Immediate { op: ast::ITypeOp::Ori, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 14 => ast::Instruction::Immediate { op: ast::ITypeOp::Xori, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 15 => ast::Instruction::Immediate { op: ast::ITypeOp::Lui, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, + 0 => inst!(Imm, Bltz, rs, 0, imm, inst), + 1 => inst!(Imm, Bgez, rs, 0, imm, inst), + 2 => inst!(Imm, Bltzl, rs, 0, imm, inst), + 3 => inst!(Imm, Bgezl, rs, 0, imm, inst), + 8 => inst!(Imm, Tgei, rs, 0, imm, inst), + 9 => inst!(Imm, Tgeiu, rs, 0, imm, inst), + 10 => inst!(Imm, Tlti, rs, 0, imm, inst), + 11 => inst!(Imm, Tltiu, rs, 0, imm, inst), + 12 => inst!(Imm, Teqi, rs, 0, imm, inst), + 14 => inst!(Imm, Tnei, rs, 0, imm, inst), + 16 => inst!(Imm, Bltzal, rs, 0, imm, inst), + 17 => inst!(Imm, Bgezal, rs, 0, imm, inst), + 18 => inst!(Imm, Bltzall, rs, 0, imm, inst), + 19 => inst!(Imm, Bgezall, rs, 0, imm, inst), + _ => inst!(Bytes, inst), + }, + 2 => inst!(Jump, J, target, inst), + 3 => inst!(Jump, Jal, target, inst), + 4 => inst!(Imm, Beq, rs, rt, imm, inst), + 5 => inst!(Imm, Bne, rs, rt, imm, inst), + 6 => inst!(Imm, Blez, rs, rt, imm, inst), + 7 => inst!(Imm, Bgtz, rs, rt, imm, inst), + 8 => inst!(Imm, Addi, rs, rt, imm, inst), + 9 => inst!(Imm, Addiu, rs, rt, imm, inst), + 10 => inst!(Imm, Slti, rs, rt, imm, inst), + 11 => inst!(Imm, Sltiu, rs, rt, imm, inst), + 12 => inst!(Imm, Andi, rs, rt, imm, inst), + 13 => inst!(Imm, Ori, rs, rt, imm, inst), + 14 => inst!(Imm, Xori, rs, rt, imm, inst), + 15 => inst!(Imm, Lui, rs, rt, imm, inst), 16 => match (rs, rt) { - (0, _) => ast::Instruction::Register { op: ast::RTypeOp::Mfc0, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (1, _) => ast::Instruction::Register { op: ast::RTypeOp::Dmfc0, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (2, _) => ast::Instruction::Register { op: ast::RTypeOp::Cfc0, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (4, _) => ast::Instruction::Register { op: ast::RTypeOp::Mtc0, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (5, _) => ast::Instruction::Register { op: ast::RTypeOp::Dmtc0, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (6, _) => ast::Instruction::Register { op: ast::RTypeOp::Ctc0, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (8, 0) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc0f, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - (8, 1) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc0t, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - (8, 2) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc0fl, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - (8, 3) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc0tl, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, + (0, _) => inst!(Reg, Mfc0, 0, rt, rd, inst), + (1, _) => inst!(Reg, Dmfc0, 0, rt, rd, inst), + (2, _) => inst!(Reg, Cfc0, 0, rt, rd, inst), + (4, _) => inst!(Reg, Mtc0, 0, rt, rd, inst), + (5, _) => inst!(Reg, Dmtc0, 0, rt, rd, inst), + (6, _) => inst!(Reg, Ctc0, 0, rt, rd, inst), + (8, 0) => inst!(Imm, Bc0f, 0, 0, imm, inst), + (8, 1) => inst!(Imm, Bc0t, 0, 0, imm, inst), + (8, 2) => inst!(Imm, Bc0fl, 0, 0, imm, inst), + (8, 3) => inst!(Imm, Bc0tl, 0, 0, imm, inst), (_, _) => match funct { - 1 => ast::Instruction::Register { op: ast::RTypeOp::Tlbr, rs: R::null(), rt: R::null(), rd: R::null(), sa: 0 }, - 2 => ast::Instruction::Register { op: ast::RTypeOp::Tlbwi, rs: R::null(), rt: R::null(), rd: R::null(), sa: 0 }, - 6 => ast::Instruction::Register { op: ast::RTypeOp::Tlbwr, rs: R::null(), rt: R::null(), rd: R::null(), sa: 0 }, - 8 => ast::Instruction::Register { op: ast::RTypeOp::Tlbp, rs: R::null(), rt: R::null(), rd: R::null(), sa: 0 }, - 24 => ast::Instruction::Register { op: ast::RTypeOp::Eret, rs: R::null(), rt: R::null(), rd: R::null(), sa: 0 }, - _ => panic!("Invalid R-type instruction: {}", funct), + 1 => inst!(Reg, Tlbr, 0, 0, 0, inst), + 2 => inst!(Reg, Tlbwi, 0, 0, 0, inst), + 6 => inst!(Reg, Tlbwr, 0, 0, 0, inst), + 8 => inst!(Reg, Tlbp, 0, 0, 0, inst), + 24 => inst!(Reg, Eret, 0, 0, 0, inst), + _ => inst!(Bytes, inst), }, - } + }, 17 => match (rs, rt) { - (0, _) => ast::Instruction::Register { op: ast::RTypeOp::Mfc1, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (1, _) => ast::Instruction::Register { op: ast::RTypeOp::Dmfc1, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (2, _) => ast::Instruction::Register { op: ast::RTypeOp::Cfc1, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (4, _) => ast::Instruction::Register { op: ast::RTypeOp::Mtc1, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (5, _) => ast::Instruction::Register { op: ast::RTypeOp::Dmtc1, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (6, _) => ast::Instruction::Register { op: ast::RTypeOp::Ctc1, rs: R::null(), rt: R::try_from(rt).unwrap(), rd: R::try_from(rd).unwrap(), sa: 0}, - (8, 0) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc1f, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - (8, 1) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc1t, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - (8, 2) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc1fl, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, - (8, 3) => ast::Instruction::Immediate { op: ast::ITypeOp::Bc1tl, rs: R::null(), rt: R::null(), imm: ast::Immediate::Short(imm as u16) }, + (0, _) => inst!(Reg, Mfc1, 0, rt, rd, inst), + (1, _) => inst!(Reg, Dmfc1, 0, rt, rd, inst), + (2, _) => inst!(Reg, Cfc1, 0, rt, rd, inst), + (4, _) => inst!(Reg, Mtc1, 0, rt, rd, inst), + (5, _) => inst!(Reg, Dmtc1, 0, rt, rd, inst), + (6, _) => inst!(Reg, Ctc1, 0, rt, rd, inst), + (8, 0) => inst!(Imm, Bc1f, 0, 0, imm, inst), + (8, 1) => inst!(Imm, Bc1t, 0, 0, imm, inst), + (8, 2) => inst!(Imm, Bc1fl, 0, 0, imm, inst), + (8, 3) => inst!(Imm, Bc1tl, 0, 0, imm, inst), (16, _) => match funct { - 0 => ast::Instruction::Register { op: ast::RTypeOp::AddS, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 1 => ast::Instruction::Register { op: ast::RTypeOp::SubS, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 2 => ast::Instruction::Register { op: ast::RTypeOp::MulS, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 3 => ast::Instruction::Register { op: ast::RTypeOp::DivS, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 4 => ast::Instruction::Register { op: ast::RTypeOp::SqrtS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 5 => ast::Instruction::Register { op: ast::RTypeOp::AbsS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 6 => ast::Instruction::Register { op: ast::RTypeOp::MovS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 7 => ast::Instruction::Register { op: ast::RTypeOp::NegS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 8 => ast::Instruction::Register { op: ast::RTypeOp::RoundLS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 9 => ast::Instruction::Register { op: ast::RTypeOp::TruncLS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 10 => ast::Instruction::Register { op: ast::RTypeOp::CeilLS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 11 => ast::Instruction::Register { op: ast::RTypeOp::FloorLS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 12 => ast::Instruction::Register { op: ast::RTypeOp::RoundWS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 13 => ast::Instruction::Register { op: ast::RTypeOp::TruncWS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 14 => ast::Instruction::Register { op: ast::RTypeOp::CeilWS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 15 => ast::Instruction::Register { op: ast::RTypeOp::FloorWS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 33 => ast::Instruction::Register { op: ast::RTypeOp::CvtDS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 36 => ast::Instruction::Register { op: ast::RTypeOp::CvtWS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 37 => ast::Instruction::Register { op: ast::RTypeOp::CvtLS, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 48..=63 => ast::Instruction::Register { op: ast::RTypeOp::Cs, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: funct & 0xF }, - e => panic!("Invalid instruction: {}", e), - - } + 0 => inst!(Reg, AddS, rd, rt, sa, inst), + 1 => inst!(Reg, SubS, rd, rt, sa, inst), + 2 => inst!(Reg, MulS, rd, rt, sa, inst), + 3 => inst!(Reg, DivS, rd, rt, sa, inst), + 4 => inst!(Reg, SqrtS, rd, 0, sa, inst), + 5 => inst!(Reg, AbsS, rd, 0, sa, inst), + 6 => inst!(Reg, MovS, rd, 0, sa, inst), + 7 => inst!(Reg, NegS, rd, 0, sa, inst), + 8 => inst!(Reg, RoundLS, rd, 0, sa, inst), + 9 => inst!(Reg, TruncLS, rd, 0, sa, inst), + 10 => inst!(Reg, CeilLS, rd, 0, sa, inst), + 11 => inst!(Reg, FloorLS, rd, 0, sa, inst), + 12 => inst!(Reg, RoundWS, rd, 0, sa, inst), + 13 => inst!(Reg, TruncWS, rd, 0, sa, inst), + 14 => inst!(Reg, CeilWS, rd, 0, sa, inst), + 15 => inst!(Reg, FloorWS, rd, 0, sa, inst), + 33 => inst!(Reg, CvtDS, rd, 0, sa, inst), + 36 => inst!(Reg, CvtWS, rd, 0, sa, inst), + 37 => inst!(Reg, CvtLS, rd, 0, sa, inst), + 48..=63 => inst!(Reg, Cs, rd, rt, 0, funct & 0xF, inst), + _ => inst!(Bytes, inst), + }, (17, _) => match funct { - 0 => ast::Instruction::Register { op: ast::RTypeOp::AddD, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 1 => ast::Instruction::Register { op: ast::RTypeOp::SubD, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 2 => ast::Instruction::Register { op: ast::RTypeOp::MulD, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 3 => ast::Instruction::Register { op: ast::RTypeOp::DivD, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 4 => ast::Instruction::Register { op: ast::RTypeOp::SqrtD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 5 => ast::Instruction::Register { op: ast::RTypeOp::AbsD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 6 => ast::Instruction::Register { op: ast::RTypeOp::MovD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 7 => ast::Instruction::Register { op: ast::RTypeOp::NegD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 8 => ast::Instruction::Register { op: ast::RTypeOp::RoundLD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 9 => ast::Instruction::Register { op: ast::RTypeOp::TruncLD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 10 => ast::Instruction::Register { op: ast::RTypeOp::CeilLD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 11 => ast::Instruction::Register { op: ast::RTypeOp::FloorLD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 12 => ast::Instruction::Register { op: ast::RTypeOp::RoundWD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 13 => ast::Instruction::Register { op: ast::RTypeOp::TruncWD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 14 => ast::Instruction::Register { op: ast::RTypeOp::CeilWD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 15 => ast::Instruction::Register { op: ast::RTypeOp::FloorWD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 32 => ast::Instruction::Register { op: ast::RTypeOp::CvtSD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 36 => ast::Instruction::Register { op: ast::RTypeOp::CvtWD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 37 => ast::Instruction::Register { op: ast::RTypeOp::CvtLD, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 48..=63 => ast::Instruction::Register { op: ast::RTypeOp::Cd, rs: R::try_from(rd).unwrap(), rt: R::try_from(rt).unwrap(), rd: R::null(), sa: funct & 0xF }, - e => panic!("Invalid instruction: {}", e), - } + 0 => inst!(Reg, AddD, rd, rt, sa, inst), + 1 => inst!(Reg, SubD, rd, rt, sa, inst), + 2 => inst!(Reg, MulD, rd, rt, sa, inst), + 3 => inst!(Reg, DivD, rd, rt, sa, inst), + 4 => inst!(Reg, SqrtD, rd, 0, sa, inst), + 5 => inst!(Reg, AbsD, rd, 0, sa, inst), + 6 => inst!(Reg, MovD, rd, 0, sa, inst), + 7 => inst!(Reg, NegD, rd, 0, sa, inst), + 8 => inst!(Reg, RoundLD, rd, 0, sa, inst), + 9 => inst!(Reg, TruncLD, rd, 0, sa, inst), + 10 => inst!(Reg, CeilLD, rd, 0, sa, inst), + 11 => inst!(Reg, FloorLD, rd, 0, sa, inst), + 12 => inst!(Reg, RoundWD, rd, 0, sa, inst), + 13 => inst!(Reg, TruncWD, rd, 0, sa, inst), + 14 => inst!(Reg, CeilWD, rd, 0, sa, inst), + 15 => inst!(Reg, FloorWD, rd, 0, sa, inst), + 32 => inst!(Reg, CvtSD, rd, 0, sa, inst), + 36 => inst!(Reg, CvtWD, rd, 0, sa, inst), + 37 => inst!(Reg, CvtLD, rd, 0, sa, inst), + 48..=63 => inst!(Reg, Cd, rd, rt, 0, funct & 0xF, inst), + _ => inst!(Bytes, inst), + }, (20, _) => match funct { - 32 => ast::Instruction::Register { op: ast::RTypeOp::CvtSW, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 33 => ast::Instruction::Register { op: ast::RTypeOp::CvtDW, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - e => panic!("Invalid instruction: {}", e), - } + 32 => inst!(Reg, CvtSW, rd, 0, sa, inst), + 33 => inst!(Reg, CvtDW, rd, 0, sa, inst), + _ => inst!(Bytes, inst), + }, (21, _) => match funct { - 32 => ast::Instruction::Register { op: ast::RTypeOp::CvtSL, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - 33 => ast::Instruction::Register { op: ast::RTypeOp::CvtDL, rs: R::try_from(rd).unwrap(), rt: R::null(), rd: R::try_from(sa).unwrap(), sa: 0 }, - e => panic!("Invalid instruction: {}", e), - } - (a, b) => panic!("Invalid instruction: {} {}", a, b), - } - 20 => ast::Instruction::Immediate { op: ast::ITypeOp::Beql, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 21 => ast::Instruction::Immediate { op: ast::ITypeOp::Bnel, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 22 => ast::Instruction::Immediate { op: ast::ITypeOp::Blezl, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 23 => ast::Instruction::Immediate { op: ast::ITypeOp::Bgtzl, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 24 => ast::Instruction::Immediate { op: ast::ITypeOp::Daddi, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 25 => ast::Instruction::Immediate { op: ast::ITypeOp::Daddiu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 26 => ast::Instruction::Immediate { op: ast::ITypeOp::Ldl, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 27 => ast::Instruction::Immediate { op: ast::ITypeOp::Ldr, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 32 => ast::Instruction::Immediate { op: ast::ITypeOp::Lb, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 33 => ast::Instruction::Immediate { op: ast::ITypeOp::Lh, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 34 => ast::Instruction::Immediate { op: ast::ITypeOp::Lwl, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 35 => ast::Instruction::Immediate { op: ast::ITypeOp::Lw, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 36 => ast::Instruction::Immediate { op: ast::ITypeOp::Lbu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 37 => ast::Instruction::Immediate { op: ast::ITypeOp::Lhu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 38 => ast::Instruction::Immediate { op: ast::ITypeOp::Lwr, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 39 => ast::Instruction::Immediate { op: ast::ITypeOp::Lwu, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 40 => ast::Instruction::Immediate { op: ast::ITypeOp::Sb, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 41 => ast::Instruction::Immediate { op: ast::ITypeOp::Sh, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 42 => ast::Instruction::Immediate { op: ast::ITypeOp::Swl, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 43 => ast::Instruction::Immediate { op: ast::ITypeOp::Sw, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 44 => ast::Instruction::Immediate { op: ast::ITypeOp::Sdl, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 45 => ast::Instruction::Immediate { op: ast::ITypeOp::Sdr, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 46 => ast::Instruction::Immediate { op: ast::ITypeOp::Swr, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 47 => ast::Instruction::Immediate { op: ast::ITypeOp::Cache, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 48 => ast::Instruction::Immediate { op: ast::ITypeOp::Ll, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 49 => ast::Instruction::Immediate { op: ast::ITypeOp::Lwc1, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 52 => ast::Instruction::Immediate { op: ast::ITypeOp::Lld, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 53 => ast::Instruction::Immediate { op: ast::ITypeOp::Ldc1, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 55 => ast::Instruction::Immediate { op: ast::ITypeOp::Ld, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 56 => ast::Instruction::Immediate { op: ast::ITypeOp::Sc, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 57 => ast::Instruction::Immediate { op: ast::ITypeOp::Swc1, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 60 => ast::Instruction::Immediate { op: ast::ITypeOp::Scd, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 61 => ast::Instruction::Immediate { op: ast::ITypeOp::Sdc1, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - 63 => ast::Instruction::Immediate { op: ast::ITypeOp::Sd, rs: R::try_from(rs).unwrap(), rt: R::try_from(rt).unwrap(), imm: ast::Immediate::Short(imm as u16) }, - e => panic!("Invalid instruction: {}", e), + 32 => inst!(Reg, CvtSL, rd, 0, sa, inst), + 33 => inst!(Reg, CvtDL, rd, 0, sa, inst), + _ => inst!(Bytes, inst), + }, + _ => inst!(Bytes, inst), + }, + 20 => inst!(Imm, Beql, rs, rt, imm, inst), + 21 => inst!(Imm, Bnel, rs, rt, imm, inst), + 22 => inst!(Imm, Blezl, rs, rt, imm, inst), + 23 => inst!(Imm, Bgtzl, rs, rt, imm, inst), + 24 => inst!(Imm, Daddi, rs, rt, imm, inst), + 25 => inst!(Imm, Daddiu, rs, rt, imm, inst), + 26 => inst!(Imm, Ldl, rs, rt, imm, inst), + 27 => inst!(Imm, Ldr, rs, rt, imm, inst), + 32 => inst!(Imm, Lb, rs, rt, imm, inst), + 33 => inst!(Imm, Lh, rs, rt, imm, inst), + 34 => inst!(Imm, Lwl, rs, rt, imm, inst), + 35 => inst!(Imm, Lw, rs, rt, imm, inst), + 36 => inst!(Imm, Lbu, rs, rt, imm, inst), + 37 => inst!(Imm, Lhu, rs, rt, imm, inst), + 38 => inst!(Imm, Lwr, rs, rt, imm, inst), + 39 => inst!(Imm, Lwu, rs, rt, imm, inst), + 40 => inst!(Imm, Sb, rs, rt, imm, inst), + 41 => inst!(Imm, Sh, rs, rt, imm, inst), + 42 => inst!(Imm, Swl, rs, rt, imm, inst), + 43 => inst!(Imm, Sw, rs, rt, imm, inst), + 44 => inst!(Imm, Sdl, rs, rt, imm, inst), + 45 => inst!(Imm, Sdr, rs, rt, imm, inst), + 46 => inst!(Imm, Swr, rs, rt, imm, inst), + 47 => inst!(Imm, Cache, rs, rt, imm, inst), + 48 => inst!(Imm, Ll, rs, rt, imm, inst), + 49 => inst!(Imm, Lwc1, rs, rt, imm, inst), + 52 => inst!(Imm, Lld, rs, rt, imm, inst), + 53 => inst!(Imm, Ldc1, rs, rt, imm, inst), + 55 => inst!(Imm, Ld, rs, rt, imm, inst), + 56 => inst!(Imm, Sc, rs, rt, imm, inst), + 57 => inst!(Imm, Swc1, rs, rt, imm, inst), + 60 => inst!(Imm, Scd, rs, rt, imm, inst), + 61 => inst!(Imm, Sdc1, rs, rt, imm, inst), + 63 => inst!(Imm, Sd, rs, rt, imm, inst), + _ => inst!(Bytes, inst), }; insts.push(i); diff --git a/src/lib.rs b/src/lib.rs index 8c7c86c..c6e1b4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,17 +12,21 @@ //! ## Example //! //! ```rust -//! use mipsasm::Mipsasm; +//! use mipsasm::{Mipsasm, get_bytes}; //! # use std::error::Error; //! # use mipsasm::ParserError; //! //! # fn main() -> Result<(), Vec> { //! let asm = "add $a0, $a1, $a2"; -//! let bin = Mipsasm::new().base(0x80000000).assemble(asm)?; -//! assert_eq!(bin, vec![0x00a62020]); +//! let inst = Mipsasm::new().base(0x80000000).assemble(asm)?; +//! let bytes: Vec = get_bytes(&inst); +//! assert_eq!(bytes, vec![0x00a62020]); //! -//! let insts = Mipsasm::new().base(0x80000000).disassemble(&bin); -//! assert_eq!(insts, vec!["add $a0, $a1, $a2"]); +//! let insts = Mipsasm::new().base(0x80000000).disassemble(&bytes); +//! assert_eq!(insts, vec!["func_80000000:", "add $a0, $a1, $a2"]); +//! +//! let insts = Mipsasm::new().base(0x80000000).debug().disassemble(&bytes); +//! assert_eq!(insts, vec!["add $a0, $a1, $a2"]); //! # Ok(()) //! # } //! ``` @@ -33,6 +37,7 @@ mod disassembler; mod error; mod parser; +pub use ast::Instruction; pub use error::ParserError; use std::collections::HashMap; @@ -40,7 +45,7 @@ use std::collections::HashMap; /// An instance of the assembler/disassembler pub struct Mipsasm<'a> { base_addr: u32, - syms: HashMap<&'a str, u32>, + syms: HashMap, debug: bool, } @@ -86,16 +91,16 @@ impl<'a> Mipsasm<'a> { /// use std::collections::HashMap; /// /// let mut mipsasm = Mipsasm::new(); - /// mipsasm.symbols(HashMap::from_iter(vec![("foo", 0x8000_0000)])); + /// mipsasm.symbols(HashMap::from_iter(vec![(0x8000_0000, "foo")])); /// ``` - pub fn symbols(&mut self, syms: HashMap<&'a str, u32>) -> &mut Mipsasm<'a> { + pub fn symbols(&mut self, syms: HashMap) -> &mut Mipsasm<'a> { self.syms = syms; self } /// Set the debug flag for the assembler. /// - /// When debug is set to true, the disassembler will print instructions with all extra whitespace stripped. + /// When debug is set to true, the disassembler will print instructions with all extra whitespace stripped and will not emit labels. /// This is used for testing and will most likely not be useful for other purposes. /// /// # Examples @@ -123,9 +128,11 @@ impl<'a> Mipsasm<'a> { /// addi $t0, $t1, 0x1234 /// "); /// ``` - pub fn assemble(&self, input: &str) -> Result, Vec> { + pub fn assemble(&self, input: &str) -> Result, Vec> { let mut parser = parser::Parser::new(input, self.base_addr, &self.syms); - Ok(assembler::assemble(parser.parse()?)) + let mut insts = parser.parse()?; + assembler::assemble(&mut insts); + Ok(insts) } /// Disassembles a set of MIPS instructions. @@ -137,17 +144,47 @@ impl<'a> Mipsasm<'a> { /// /// let mut mipsasm = Mipsasm::new(); /// let instructions = mipsasm.disassemble(&[0x00850018]); - /// assert_eq!(instructions, vec!["mult $a0, $a1"]); /// ``` pub fn disassemble(&self, input: &[u32]) -> Vec { - let mut x = disassembler::disassemble(input.to_vec()); + let mut x = disassembler::disassemble(input.to_vec(), self.base_addr); self.match_syms(&mut x); + if self.debug { x.iter() .map(|x| format!("{:?}", x)) .collect::>() } else { - x.iter().map(|x| x.to_string()).collect::>() + let mut out = vec![]; + let mut func_start = 0; + let mut function_ended = false; + + out.push(format!("{}:", self.get_sym(self.base_addr))); + + for i in 0..x.len() { + if function_ended { + out.push(x[i].to_string()); + func_start = i * 4; + function_ended = false; + if i < x.len() - 1 { + out.push(String::new()); + out.push(format!( + "{}:", + self.get_sym(self.base_addr + (i + 1) as u32 * 4) + )); + } + } else { + if x[i].is_unconditional_jump() + && (x[i].get_jump_target() < Some(func_start as u32 + self.base_addr) + || x[i].get_jump_target() > Some(i as u32 * 4 + self.base_addr)) + { + function_ended = true; + } + + out.push(x[i].to_string()); + } + } + + out } } @@ -157,15 +194,42 @@ impl<'a> Mipsasm<'a> { if let ast::Instruction::Jump { op, target: ast::Target::Address(addr), + bytes, } = i { - if let Some(sym) = self.syms.iter().find(|(_, v)| **v == *addr) { + if let Some(sym) = self.syms.get(addr) { *i = ast::Instruction::Jump { op: *op, - target: ast::Target::Label(sym.0.to_string()), + target: ast::Target::Label(sym.to_string()), + bytes: bytes.to_owned(), }; } } } } + + fn get_sym(&self, addr: u32) -> String { + if let Some(sym) = self.syms.get(&addr) { + sym.to_string() + } else { + format!("func_{:08x}", addr) + } + } +} + +/// Collects all of the bytes from a slice of Instructions and returns them in a vector +/// +/// # Examples +/// +/// ``` +/// use mipsasm::Mipsasm; +/// let mut mipsasm = Mipsasm::new(); +/// let instructions = mipsasm.assemble(" +/// add $t0, $t1, $t2 +/// addi $t0, $t1, 0x1234 +/// ").unwrap(); +/// assert_eq!(mipsasm::get_bytes(&instructions), vec![0x012a4020, 0x21281234]) +/// ``` +pub fn get_bytes(insts: &[Instruction]) -> Vec { + insts.iter().flat_map(|x| x.get_bytes()).collect() } diff --git a/src/parser.rs b/src/parser.rs index 01da77d..b0a31e6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,6 +8,50 @@ use std::collections::HashMap; use std::mem; use std::ops::Index; +macro_rules! inst { + (Imm, $op:expr, $rs:expr, $rt:expr, $imm:expr) => { + ast::Instruction::Immediate { + op: $op.parse().unwrap(), + rs: $rs, + rt: $rt, + imm: $imm, + bytes: vec![], + } + }; + (Jump, $op:expr, $target:expr) => { + ast::Instruction::Jump { + op: $op.parse().unwrap(), + target: $target, + bytes: vec![], + } + }; + (Reg, $op:expr, $rs:expr, $rt:expr, $rd:expr) => { + ast::Instruction::Register { + op: $op.parse().unwrap(), + rs: $rs, + rt: $rt, + rd: $rd, + sa: 0, + bytes: vec![], + } + }; + (Reg, $op:expr, $rs:expr, $rt:expr, $rd:expr, $sa:expr) => { + ast::Instruction::Register { + op: $op.parse().unwrap(), + rs: $rs, + rt: $rt, + rd: $rd, + sa: $sa, + bytes: vec![], + } + }; + (Bytes, $imm:expr) => { + ast::Instruction::Bytes { + bytes: ast::Immediate::Int($imm), + } + }; +} + // Regex to match anything after a comment static COMMENT_RE: Lazy = Lazy::new(|| Regex::new(r"(/{2}|;).*").unwrap()); static OFFSET_RE: Lazy = Lazy::new(|| Regex::new(r".+\s*\(").unwrap()); @@ -21,13 +65,13 @@ pub struct Parser<'a> { local_labels: HashMap, local_labels_dropped: HashMap>, base_addr: u32, - syms: &'a HashMap<&'a str, u32>, + syms: &'a HashMap, line_num: usize, errors: Vec, } impl<'a> Parser<'a> { - pub fn new(input: &'a str, base_addr: u32, syms: &'a HashMap<&'a str, u32>) -> Parser<'a> { + pub fn new(input: &'a str, base_addr: u32, syms: &'a HashMap) -> Parser<'a> { Parser { input: input.lines().collect(), insts: vec![], @@ -167,19 +211,15 @@ impl<'a> Parser<'a> { .parse() .unwrap(); if let Some(x) = OFFSET_RE.find(x) { - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: base, + Ok(inst!( + Imm, + op, + base, rt, - imm: self.parse_immediate::(&x.as_str()[..x.as_str().len() - 1])?, - }) + self.parse_immediate::(&x.as_str()[..x.as_str().len() - 1])? + )) } else { - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: base, - rt, - imm: self.parse_immediate::("0")?, - }) + Ok(inst!(Imm, op, base, rt, self.parse_immediate::("0")?)) } } // ----------------------------------------------------------------- @@ -199,19 +239,9 @@ impl<'a> Parser<'a> { )?; let imm = args.get(2).unwrap(); if op == "andi" || op == "ori" || op == "xori" { - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rt, - rs, - imm: self.parse_immediate::(imm)?, - }) + Ok(inst!(Imm, op, rs, rt, self.parse_immediate::(imm)?)) } else { - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rt, - rs, - imm: self.parse_immediate::(imm)?, - }) + Ok(inst!(Imm, op, rs, rt, self.parse_immediate::(imm)?)) } } // ----------------------------------------------------------------- @@ -226,12 +256,13 @@ impl<'a> Parser<'a> { |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; let imm = args.get(1).unwrap(); - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), + Ok(inst!( + Imm, + op, + ast::Register::null(), rt, - rs: ast::Register::null(), - imm: self.parse_immediate::(imm)?, - }) + self.parse_immediate::(imm)? + )) } // ----------------------------------------------------------------- // | op | rs | 00000 | offset | @@ -245,12 +276,13 @@ impl<'a> Parser<'a> { |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; let imm = args.get(1).unwrap(); - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rt: ast::Register::null(), + Ok(inst!( + Imm, + op, rs, - imm: self.parse_immediate::(imm)?, - }) + ast::Register::null(), + self.parse_immediate::(imm)? + )) } "bgez" | "bgezal" | "bgezall" | "bgezl" | "bltz" | "bltzal" | "bltzall" | "bltzl" | "beqz" | "bnez" | "beqzl" | "bnezl" | "bgtz" | "bgtzl" | "blez" | "blezl" => { @@ -271,12 +303,7 @@ impl<'a> Parser<'a> { if !imm.is_label() && imm.as_u32() % 4 != 0 { eprintln!("{}", warning!(self, UnalignedBranch, offset.to_string())); } - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rt: ast::Register::null(), - rs, - imm, - }) + Ok(inst!(Imm, op, rs, ast::Register::null(), imm)) } // ----------------------------------------------------------------- // | op | rs | rt | offset | @@ -305,12 +332,7 @@ impl<'a> Parser<'a> { if !imm.is_label() && imm.as_u32() % 4 != 0 { eprintln!("{}", warning!(self, UnalignedBranch, offset.to_string())); } - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rt, - rs, - imm, - }) + Ok(inst!(Imm, op, rs, rt, imm)) } // ----------------------------------------------------------------- // | op | target | @@ -331,22 +353,19 @@ impl<'a> Parser<'a> { if !target.is_label() && target.as_u32() % 4 != 0 { eprintln!("{}", warning!(self, UnalignedJump, target_str.to_string())); } - Ok(ast::Instruction::Jump { - op: op.parse().unwrap(), - target, - }) + Ok(inst!(Jump, op, target)) } // ----------------------------------------------------------------- // | SPECIAL | 0000 0000 0000 000 | stype | op | // ------6-------------------15-------------------5---------6------- // Format: op (stype = 0 implied) - "nop" | "sync" => Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd: ast::Register::null(), - rs: ast::Register::null(), - rt: ast::Register::null(), - sa: 0, - }), + "nop" | "sync" => Ok(inst!( + Reg, + op, + ast::Register::null(), + ast::Register::null(), + ast::Register::null() + )), // ----------------------------------------------------------------- // | SPECIAL | rs | rt | rd | 00000 | op | // ------6----------5---------5---------5---------5----------6------ @@ -367,13 +386,7 @@ impl<'a> Parser<'a> { let rt = args.get(2).unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd, - rs, - rt, - sa: 0, - }) + Ok(inst!(Reg, op, rs, rt, rd)) } // ----------------------------------------------------------------- // | SPECIAL | 00000 | rt | rd | sa | op | @@ -390,18 +403,19 @@ impl<'a> Parser<'a> { |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; let sa = args.get(2).unwrap().trim(); - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd, - rs: ast::Register::null(), + Ok(inst!( + Reg, + op, + ast::Register::null(), rt, - sa: if sa.ends_with('`') || !sa.contains("0x") { + rd, + if sa.ends_with('`') || !sa.contains("0x") { sa.trim_end_matches('`').parse::().unwrap() as u32 } else { let sa = sa.replace("0x", ""); i32::from_str_radix(&sa, 16).unwrap() as u32 - }, - }) + } + )) } // ----------------------------------------------------------------- // | SPECIAL | rs | rt | rd | 00000 | op | @@ -420,13 +434,7 @@ impl<'a> Parser<'a> { let rs = args.get(2).unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd, - rs, - rt, - sa: 0, - }) + Ok(inst!(Reg, op, rs, rt, rd)) } // ----------------------------------------------------------------- // | SPECIAL | code | op | @@ -444,13 +452,14 @@ impl<'a> Parser<'a> { return Err(error!(self, InvalidOperandCount, arg, 1, args.len())); }; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd: ast::Register::null(), - rs: ast::Register::null(), - rt: ast::Register::null(), - sa: code.as_u32(), - }) + Ok(inst!( + Reg, + op, + ast::Register::null(), + ast::Register::null(), + ast::Register::null(), + code.as_u32() + )) } // ----------------------------------------------------------------- // | SPECIAL | rs | rt | 0000 0000 00 | op | @@ -467,13 +476,7 @@ impl<'a> Parser<'a> { let rt = args.get(1).unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd: ast::Register::null(), - rs, - rt, - sa: 0, - }) + Ok(inst!(Reg, op, rs, rt, ast::Register::null())) } // ----------------------------------------------------------------- // | SPECIAL | rs | 00000 | rd | 00000 | op | @@ -493,24 +496,12 @@ impl<'a> Parser<'a> { |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; if args.len() == 1 { - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd: ast::Register::Ra, - rs, - rt: ast::Register::null(), - sa: 0, - }) + Ok(inst!(Reg, op, rs, ast::Register::null(), ast::Register::Ra)) } else { let rd = args.get(1).unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd: rs, - rs: rd, - rt: ast::Register::null(), - sa: 0, - }) + Ok(inst!(Reg, op, rd, ast::Register::null(), rs)) } } "abs" | "dabs" | "dmove" | "dneg" | "dnegu" | "move" | "neg" | "negu" | "not" => { @@ -523,13 +514,7 @@ impl<'a> Parser<'a> { let rs = args.get(1).unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd, - rs, - rt: ast::Register::null(), - sa: 0, - }) + Ok(inst!(Reg, op, rs, ast::Register::null(), rd)) } "b" | "bal" => { if args.len() != 1 { @@ -547,12 +532,13 @@ impl<'a> Parser<'a> { if !imm.is_label() && imm.as_u32() % 4 != 0 { eprintln!("{}", warning!(self, UnalignedBranch, offset.to_string())); } - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: ast::Register::null(), - rt: ast::Register::null(), - imm, - }) + Ok(inst!( + Imm, + op, + ast::Register::null(), + ast::Register::null(), + imm + )) } "li" => { if args.len() != 2 { @@ -561,13 +547,32 @@ impl<'a> Parser<'a> { let rt = args.first().unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - let imm = args.get(1).unwrap(); - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: ast::Register::null(), - rt, - imm: self.parse_immediate::(imm)?, - }) + let imm = self.parse_immediate::(args.get(1).unwrap())?; + if imm.as_u64() > 0xFFFFFFFF { + return Err(error!( + self, + InvalidImmediate, + args.get(1).unwrap().to_string() + )); + } + Ok(inst!(Imm, op, ast::Register::null(), rt, imm)) + } + "liu" => { + if args.len() != 2 { + return Err(error!(self, InvalidOperandCount, arg, 2, args.len())); + } + let rt = args.first().unwrap().parse().map_err( + |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), + )?; + let imm = self.parse_immediate::(args.get(1).unwrap())?; + if imm.as_u64() > 0xFFFFFFFF { + return Err(error!( + self, + InvalidImmediate, + args.get(1).unwrap().to_string() + )); + } + Ok(inst!(Imm, op, ast::Register::null(), rt, imm)) } "ddiv" | "ddivu" | "div" | "divu" => { if args.len() != 3 && args.len() != 2 { @@ -580,24 +585,12 @@ impl<'a> Parser<'a> { |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; if args.len() == 2 { - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd, - rs, - rt: ast::Register::null(), - sa: 0, - }) + Ok(inst!(Reg, op, rs, ast::Register::null(), rd)) } else { let rt = args.get(2).unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd, - rs, - rt, - sa: 0, - }) + Ok(inst!(Reg, op, rs, rt, rd)) } } // ----------------------------------------------------------------- @@ -617,13 +610,13 @@ impl<'a> Parser<'a> { let rs = args.first().unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd: ast::Register::null(), + Ok(inst!( + Reg, + op, rs, - rt: ast::Register::null(), - sa: 0, - }) + ast::Register::null(), + ast::Register::null() + )) } // ----------------------------------------------------------------- // | SPECIAL | 0000 0000 00 | rd | 00000 | op | @@ -636,13 +629,13 @@ impl<'a> Parser<'a> { let rd = args.first().unwrap().parse().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rd, - rs: ast::Register::null(), - rt: ast::Register::null(), - sa: 0, - }) + Ok(inst!( + Reg, + op, + ast::Register::null(), + ast::Register::null(), + rd + )) } "dli" => { if args.len() != 2 { @@ -652,12 +645,13 @@ impl<'a> Parser<'a> { |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; let imm = args.get(1).unwrap(); - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: ast::Register::null(), + Ok(inst!( + Imm, + op, + ast::Register::null(), rt, - imm: self.parse_immediate::(imm)?, - }) + self.parse_immediate::(imm)? + )) } // ----------------------------------------------------------------- // | COPz | op | bc | offset | @@ -678,12 +672,14 @@ impl<'a> Parser<'a> { if !imm.is_label() && imm.as_u32() % 4 != 0 { eprintln!("{}", warning!(self, UnalignedBranch, offset.to_string())); } - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: ast::Register::null(), - rt: ast::Register::null(), - imm, - }) + + Ok(inst!( + Imm, + op, + ast::Register::null(), + ast::Register::null(), + imm + )) } // ----------------------------------------------------------------- // | COPz | op | rt | rd | 0000 0000 000 | @@ -699,13 +695,8 @@ impl<'a> Parser<'a> { let rd = args.get(1).unwrap().parse::().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rs: ast::Register::null(), - rt, - rd: rd.into(), - sa: 0, - }) + + Ok(inst!(Reg, op, ast::Register::null(), rt, rd.into())) } // ----------------------------------------------------------------- // | COPz | op | rt | fs | 0000 0000 000 | @@ -721,25 +712,20 @@ impl<'a> Parser<'a> { let rd = args.get(1).unwrap().parse::().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rs: ast::Register::null(), - rt, - rd: rd.into(), - sa: 0, - }) + + Ok(inst!(Reg, op, ast::Register::null(), rt, rd.into())) } // ----------------------------------------------------------------- // | COPz |CO| 0000 0000 0000 0000 000 | op | // ------6------1-------------------19-----------------------6------ // Format: op - "eret" | "tlbp" | "tlbr" | "tlbwi" | "tlbwr" => Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rs: ast::Register::null(), - rt: ast::Register::null(), - rd: ast::Register::null(), - sa: 0, - }), + "eret" | "tlbp" | "tlbr" | "tlbwi" | "tlbwr" => Ok(inst!( + Reg, + op, + ast::Register::null(), + ast::Register::null(), + ast::Register::null() + )), // ----------------------------------------------------------------- // | op | base | ft | offset | // ------6----------5---------5-------------------16---------------- @@ -768,21 +754,26 @@ impl<'a> Parser<'a> { error!(self, InvalidRegister, e) })?; if let Some(x) = OFFSET_RE.find(x) { - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: base, - rt: ast::Register::from(ft), - imm: self.parse_immediate::(&x.as_str().replace('(', ""))?, - }) + Ok(inst!( + Imm, + op, + base, + ast::Register::from(ft), + self.parse_immediate::(&x.as_str().replace('(', ""))? + )) } else { - Ok(ast::Instruction::Immediate { - op: op.parse().unwrap(), - rs: base, - rt: ast::Register::from(ft), - imm: self.parse_immediate::("0")?, - }) + Ok(inst!( + Imm, + op, + base, + ast::Register::from(ft), + self.parse_immediate::("0")? + )) } } + ".word" => Ok(ast::Instruction::Bytes { + bytes: self.parse_immediate::(arg)?.as_u32(), + }), _ => match &op.to_lowercase()[..op.len() - 2] { // ----------------------------------------------------------------- // | COP1 | fmt | ft | fs | fd | op | @@ -805,13 +796,14 @@ impl<'a> Parser<'a> { let ft = args.get(2).unwrap().parse::().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rs: ast::Register::from(fs), - rt: ast::Register::from(ft), - rd: ast::Register::from(fd), - sa: 0, - }) + + Ok(inst!( + Reg, + op, + ast::Register::from(fs), + ast::Register::from(ft), + ast::Register::from(fd) + )) } // ----------------------------------------------------------------- // | COP1 | fmt | 00000 | fs | fd | op | @@ -833,13 +825,14 @@ impl<'a> Parser<'a> { let fs = args.get(1).unwrap().parse::().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - Ok(ast::Instruction::Register { - op: op.parse().unwrap(), - rs: ast::Register::from(fs), - rt: ast::Register::null(), - rd: ast::Register::from(fd), - sa: 0, - }) + + Ok(inst!( + Reg, + op, + ast::Register::from(fs), + ast::Register::null(), + ast::Register::from(fd) + )) } e => { // ----------------------------------------------------------------- @@ -860,15 +853,17 @@ impl<'a> Parser<'a> { let ft = args.get(1).unwrap().parse::().map_err( |ast::RegParseError::RegParseError(e)| error!(self, InvalidRegister, e), )?; - return Ok(ast::Instruction::Register { - op: format!("C.{}", op.chars().last().unwrap()).parse().unwrap(), - rs: ast::Register::from(fs), - rt: ast::Register::from(ft), - rd: ast::Register::null(), - sa: self.parse_float_cond( + + return Ok(inst!( + Reg, + format!("C.{}", op.chars().last().unwrap()), + ast::Register::from(fs), + ast::Register::from(ft), + ast::Register::null(), + self.parse_float_cond( op.split('.').collect::>().get(1).unwrap(), - )?, - }); + )? + )); } Err(error!(self, InvalidOpcode, op)) } @@ -885,6 +880,7 @@ impl<'a> Parser<'a> { rs, rt, imm: ast::Immediate::Label(lbl), + .. } = &self.insts[i].1 { let lbl_addr = self.labels.get(lbl.as_str()).unwrap(); @@ -895,12 +891,14 @@ impl<'a> Parser<'a> { rt: *rt, // Calculate the offset from the label to the current instruction imm, + bytes: vec![], }; } else if let ast::Instruction::Immediate { op, rs, rt, imm: ast::Immediate::LocalLabel(loc), + .. } = &self.insts[i].1 { let lbls = self.local_labels_dropped.get(loc.as_str()); @@ -920,6 +918,7 @@ impl<'a> Parser<'a> { rt: *rt, // Calculate the offset from the label to the current instruction imm, + bytes: vec![], }; continue 'a; } @@ -929,6 +928,7 @@ impl<'a> Parser<'a> { rs, rt, imm: ast::Immediate::Int(addr), + .. } = &self.insts[i].1 { if !op.to_string().starts_with('b') { @@ -952,10 +952,12 @@ impl<'a> Parser<'a> { rs: *rs, rt: *rt, imm: ast::Immediate::Short(((offset / 4) as i16) as u16), + bytes: vec![], }; } else if let ast::Instruction::Jump { op, target: ast::Target::Label(lbl), + .. } = &self.insts[i].1 { let lbl_addr = self.labels.get(lbl.as_str()); @@ -966,6 +968,7 @@ impl<'a> Parser<'a> { self.insts[i].1 = ast::Instruction::Jump { op: *op, target: ast::Target::Address(self.base_addr + *lbl_addr.unwrap() as u32 * 4), + bytes: vec![], }; } } @@ -1017,8 +1020,8 @@ impl<'a> Parser<'a> { } fn parse_target(&self, target: &str) -> Result { - if let Some(x) = self.syms.get(target) { - return Ok(ast::Target::Address(*x)); + if let Some(x) = self.syms.iter().find(|(_, v)| **v == target) { + return Ok(ast::Target::Address(*x.0)); } if target.starts_with("0x") { diff --git a/tests/common/mod.rs b/tests/common/mod.rs index c641107..5b559e2 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,7 +1,8 @@ -use mipsasm::Mipsasm; +use mipsasm::{get_bytes, Mipsasm}; pub fn asm(inst: &str) -> Vec { - Mipsasm::new().base(0x80000000).assemble(inst).unwrap() + let insts = Mipsasm::new().base(0x80000000).assemble(inst).unwrap(); + get_bytes(&insts) } pub fn disasm(inst: &[u32]) -> String { diff --git a/tests/cpu_inst_tests.rs b/tests/cpu_inst_tests.rs index d952bc4..2877529 100644 --- a/tests/cpu_inst_tests.rs +++ b/tests/cpu_inst_tests.rs @@ -1,5 +1,5 @@ mod common; -use mipsasm::Mipsasm; +use mipsasm::{get_bytes, Mipsasm}; use std::collections::HashMap; use common::{asm, disasm}; @@ -387,14 +387,15 @@ test!(test_jal, "jal 0x80000000", 0x0c000000); #[test] fn test_jal_sym() { - let mut x: HashMap<&str, u32> = HashMap::new(); - x.insert("func", 0x80123454); + let mut x: HashMap = HashMap::new(); + x.insert(0x80123454, "func"); let asm = Mipsasm::new() .base(0x80000000) .symbols(x.clone()) .assemble("jal func") .unwrap(); + let asm = get_bytes(&asm); assert_eq!(asm, vec![0x0c048d15]); let disasm = Mipsasm::new()