support mnemonic suffixes
This commit is contained in:
parent
756e23f240
commit
40142dcd9b
|
@ -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() {
|
||||
|
|
|
@ -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!();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -204,7 +204,6 @@ opcodes:
|
|||
bitmask: 0xfc000000
|
||||
pattern: 0x3c000000
|
||||
args: [ "rD", "rA", "uimm" ]
|
||||
side_effects: [ "Rc" ]
|
||||
defs: [ "rD" ]
|
||||
uses: [ "rA.nz" ]
|
||||
|
||||
|
|
Loading…
Reference in New Issue