support mnemonic suffixes

This commit is contained in:
Richard Patel 2021-08-29 09:02:31 +02:00
parent 756e23f240
commit 40142dcd9b
5 changed files with 156 additions and 56 deletions

View File

@ -1,4 +1,4 @@
use std::fmt::{Display, LowerHex, UpperHex, Formatter};
use std::fmt::{Display, Formatter, LowerHex, UpperHex};
use num_traits::PrimInt;
@ -14,8 +14,7 @@ impl Display for FormattedIns {
impl FormattedIns {
fn fmt_ins(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mnemonic = self.0.op.mnemonic();
write!(f, "{} ", mnemonic)?;
write!(f, "{}{} ", self.0.op.mnemonic(), self.0.modifiers())?;
let fields = self.0.fields();
let mut writing_offset = false;
for (i, field) in fields.iter().enumerate() {

View File

@ -1,23 +1,26 @@
use std::fmt::Formatter;
use std::ops::Range;
use num_traits::AsPrimitive;
use ppc750cl_macros::{fields, ins_impl, opcodes};
pub use crate::iter::{disasm_iter, DisasmIterator};
pub mod formatter;
mod iter;
pub mod prelude {
pub use crate::formatter::FormattedIns;
pub use crate::Field;
pub use crate::Field::*;
pub use crate::Ins;
pub use crate::formatter::FormattedIns;
pub use crate::Opcode::*;
pub use crate::{
Bit, BranchDest, CRBit, CRField, Offset, OpaqueU, Simm, Uimm, FPR, GPR, GQR, SPR, SR,
};
}
use ppc750cl_macros::{fields, ins_impl, opcodes};
//pub use crate::formatter::AsmFormatter;
//use crate::formatter::SimpleFormatter;
pub use crate::iter::{disasm_iter, DisasmIterator};
macro_rules! field_arg {
($name:ident, $typ:ident) => {
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
@ -25,6 +28,21 @@ macro_rules! field_arg {
};
}
#[inline(always)]
fn bit(x: u32, idx: usize) -> bool {
((x >> (32 - idx - 1)) & 1) == 1
}
#[inline(always)]
fn bits<F>(x: u32, range: Range<usize>) -> F
where
F: 'static + std::marker::Copy,
u32: AsPrimitive<F>,
{
let masked: u32 = (x >> (32 - range.end)) & ((1 << range.len()) - 1);
masked.as_()
}
// General-purpose register.
field_arg!(GPR, u8);
// Floating-point register (direct or paired-singles mode).
@ -55,10 +73,48 @@ field_arg!(OpaqueU, u32);
// Generate the Field enum and impls.
fields!();
#[derive(Debug, Default)]
pub struct Modifiers {
pub oe: bool,
pub rc: bool,
pub lk: bool,
pub aa: bool,
}
impl std::fmt::Display for Modifiers {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if self.aa {
write!(f, "a")?;
}
if self.lk {
write!(f, "l")?;
}
if self.oe {
write!(f, "o")?;
}
if self.rc {
write!(f, ".")?;
}
Ok(())
}
}
// Generate the Opcode enum and impls.
// TODO This could be made more readable with a derive over an empty enum.
opcodes!();
impl Opcode {
/// Detects the opcode of a machine code instruction.
pub fn detect(code: u32) -> Self {
Self::_detect(code) // auto-generated
}
/// Prints the basic mnemonic of an opcode.
pub fn mnemonic(self) -> &'static str {
self._mnemonic() // auto-generated
}
}
impl Default for Opcode {
fn default() -> Self {
Opcode::Illegal
@ -95,6 +151,11 @@ impl Ins {
self._fields() // auto-generated
}
/// Gets the modifiers of an instruction.
pub fn modifiers(&self) -> Modifiers {
self._modifiers() // auto-generated
}
/// Gets the defs of an instruction.
pub fn defs(&self) -> Vec<Field> {
self._defs() // auto-generated
@ -104,6 +165,16 @@ impl Ins {
pub fn uses(&self) -> Vec<Field> {
self._uses() // auto-generated
}
/// Gets the given bit from the machine code instruction.
pub fn bit(&self, idx: usize) -> bool {
bit(self.code, idx)
}
/// Gets the given range of btis from the machine code instruction.
pub fn bits(&self, range: Range<usize>) -> u32 {
bits(self.code, range)
}
}
ins_impl!();

View File

@ -4,6 +4,10 @@ macro_rules! assert_asm {
($ins:ident, $disasm:literal) => {{
assert_eq!(format!("{}", FormattedIns($ins)), $disasm)
}};
($code:literal, $disasm:literal) => {{
let ins = Ins::new($code, 0x8000_0000);
assert_eq!(format!("{}", FormattedIns(ins)), $disasm)
}};
}
#[test]
@ -25,6 +29,13 @@ fn test_ins_addi() {
assert_eq!(ins.defs(), vec![rD(GPR(0))]);
assert_eq!(ins.uses(), vec![rA(GPR(1))]);
assert_asm!(ins, "addi r0, r1, 0x140");
assert_asm!(0x38010008, "addi r0, r1, 0x8");
assert_asm!(0x38010010, "addi r0, r1, 0x10");
assert_asm!(0x38010018, "addi r0, r1, 0x18");
assert_asm!(0x38010140, "addi r0, r1, 0x140");
assert_asm!(0x38049000, "addi r0, r4, -0x7000");
//assert_asm!(0x38a00000, "li r5, 0");
}
#[test]
@ -45,48 +56,26 @@ fn test_ins_psq_lx() {
assert_eq!(ins.uses(), vec![rB(GPR(0))]);
}
/*
#[test]
fn test_ins_addc() {
assert_asm!(0x7c002014, "addc r0, r0, r4");
}
#[test]
fn test_ins_adde() {
assert_asm!(0x7c006114, "adde r0, r0, r12");
}
#[test]
fn test_ins_addi() {
assert_asm!(0x38010008, "addi r0, r1, 0x8");
assert_asm!(0x38010010, "addi r0, r1, 0x10");
assert_asm!(0x38010018, "addi r0, r1, 0x18");
assert_asm!(0x38010140, "addi r0, r1, 0x140");
assert_asm!(0x38049000, "addi r0, r4, -28672");
assert_asm!(0x38a00000, "li r5, 0");
}
#[test]
fn test_ins_addic() {
assert_asm!(0x3060ffff, "addic r3, r0, -1");
assert_asm!(0x3060ffff, "addic r3, r0, -0x1");
assert_asm!(0x30840800, "addic r4, r4, 0x800");
assert_asm!(0x30a50008, "addic r5, r5, 0x8");
assert_asm!(0x37DF001C, "addic. r30, r31, 0x1c");
assert_asm!(0x37E06278, "addic. r31, r0, 0x6278");
assert_asm!(0x37E3FFFF, "addic. r31, r3, -1");
}
#[test]
fn test_ins_addic_() {
assert_asm!(0x341D001C, "addic. r0, r29, 0x1c");
assert_asm!(0x37E3FFFF, "addic. r31, r3, -0x1");
}
#[test]
fn test_ins_addis() {
assert_asm!(0x3C030000, "addis r0, r3, 0");
assert_asm!(0x3C030000, "addis r0, r3, 0x0");
assert_asm!(0x3C038000, "addis r0, r3, 0x8000");
assert_asm!(0x3D00EFCE, "lis r8, 0xefce");
//assert_asm!(0x3D00EFCE, "lis r8, 0xefce");
}
#[test]
@ -100,6 +89,8 @@ fn test_ins_and() {
assert_asm!(0x7C001839, "and. r0, r0, r3");
}
/*
#[test]
fn test_ins_andc() {
assert_asm!(0x7C001878, "andc r0, r0, r3");

View File

@ -8,6 +8,12 @@ use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use syn::LitInt;
macro_rules! token_stream {
($stream:ident) => {
TokenStream::from_iter($stream.into_iter())
};
}
#[derive(Default)]
pub(crate) struct BitRange(Range<u8>);
@ -159,9 +165,8 @@ impl Isa {
#ident,
})
})
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?
.into_iter();
let enum_variants = TokenStream::from_iter(enum_variants);
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
let enum_variants = token_stream!(enum_variants);
// Create functions.
let mnemonic_fn = self.gen_mnemonic_fn()?;
@ -194,12 +199,11 @@ impl Isa {
Opcode::#variant => #literal,
})
})
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?
.into_iter();
let match_arms = TokenStream::from_iter(match_arms);
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
let match_arms = token_stream!(match_arms);
// Create final function.
let mnemonic_fn = quote! {
pub fn mnemonic(self) -> &'static str {
fn _mnemonic(self) -> &'static str {
match self {
Opcode::Illegal => "<illegal>",
#match_arms
@ -226,12 +230,11 @@ impl Isa {
}
})
})
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?
.into_iter();
let if_chain = TokenStream::from_iter(if_chain);
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?;
let if_chain = token_stream!(if_chain);
// Generate function.
let func = quote! {
pub fn detect(code: u32) -> Self {
fn _detect(code: u32) -> Self {
#if_chain
Opcode::Illegal
}
@ -247,7 +250,7 @@ impl Isa {
enum_variants.push(field);
}
}
let enum_variants = TokenStream::from_iter(enum_variants.into_iter());
let enum_variants = token_stream!(enum_variants);
// Create final enum.
let field_enum = quote! {
@ -269,6 +272,7 @@ impl Isa {
let mut field_match_arms = Vec::new();
let mut def_match_arms = Vec::new();
let mut use_match_arms = Vec::new();
let mut modifier_match_arms = Vec::new();
for opcode in &self.opcodes {
// Generate fields of opcode.
// TODO Support mnemonics.
@ -280,13 +284,39 @@ impl Isa {
let variant = field.construct_variant_self();
fields.extend(quote! { #variant, })
}
let fields = TokenStream::from_iter(fields.into_iter());
let fields = token_stream!(fields);
// Emit match arm.
let ident = opcode.variant_identifier()?;
field_match_arms.push(quote! {
Opcode::#ident => vec![#fields],
});
// Generate modifiers.
let mut set_modifiers: Vec<TokenTree> = Vec::new();
for modifier in &opcode.modifiers {
set_modifiers.extend(match modifier.as_str() {
"OE" => quote! { m.oe = self.bit(21); },
"Rc" => quote! { m.rc = self.bit(31); },
"AA" => quote! { m.aa = self.bit(30); },
"LK" => quote! { m.lk = self.bit(31); },
_ => {
return Err(syn::Error::new(
Span::call_site(),
format!("unsupported modifier {}", modifier),
))
}
})
}
let set_modifiers = token_stream!(set_modifiers);
modifier_match_arms.push(quote! {
Opcode::#ident => {
let mut m: Modifiers = std::default::Default::default();
#set_modifiers
m
}
});
// Generate defs.
let mut defs = Vec::new();
for arg in &opcode.defs {
let field: &Field = field_by_name.get(arg).ok_or_else(|| {
@ -295,12 +325,13 @@ impl Isa {
let variant = field.construct_variant_self();
defs.extend(quote! { #variant, })
}
let defs = TokenStream::from_iter(defs.into_iter());
let defs = token_stream!(defs);
let ident = opcode.variant_identifier()?;
def_match_arms.push(quote! {
Opcode::#ident => vec![#defs],
});
// Generate uses.
let mut uses = Vec::new();
let mut special_uses = Vec::new();
for arg in &opcode.uses {
@ -329,9 +360,9 @@ impl Isa {
})
}
}
let uses = TokenStream::from_iter(uses.into_iter());
let uses = token_stream!(uses);
let ident = opcode.variant_identifier()?;
let special_uses = TokenStream::from_iter(special_uses.into_iter());
let special_uses = token_stream!(special_uses);
use_match_arms.push(quote! {
Opcode::#ident => {
let mut uses = vec![#uses];
@ -340,9 +371,10 @@ impl Isa {
},
});
}
let field_match_arms = TokenStream::from_iter(field_match_arms.into_iter());
let def_match_arms = TokenStream::from_iter(def_match_arms.into_iter());
let use_match_arms = TokenStream::from_iter(use_match_arms.into_iter());
let field_match_arms = token_stream!(field_match_arms);
let def_match_arms = token_stream!(def_match_arms);
let use_match_arms = token_stream!(use_match_arms);
let modifier_match_arms = token_stream!(modifier_match_arms);
// Generate final fields function.
let ins_impl = quote! {
impl Ins {
@ -369,6 +401,14 @@ impl Isa {
_ => todo!()
}
}
fn _modifiers(&self) -> Modifiers {
match self.op {
Opcode::Illegal => std::default::Default::default(),
#modifier_match_arms
_ => todo!()
}
}
}
};
Ok(ins_impl)

View File

@ -204,7 +204,6 @@ opcodes:
bitmask: 0xfc000000
pattern: 0x3c000000
args: [ "rD", "rA", "uimm" ]
side_effects: [ "Rc" ]
defs: [ "rD" ]
uses: [ "rA.nz" ]