big rewrite, everything half broken

This commit is contained in:
Richard Patel 2021-08-25 03:23:57 +02:00
parent ec727af8dd
commit 7a6b4df8d4
12 changed files with 1545 additions and 2849 deletions

45
Cargo.lock generated
View File

@ -83,6 +83,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "dtoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "either"
version = "1.6.1"
@ -211,6 +217,12 @@ version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
version = "0.4.4"
@ -314,6 +326,7 @@ version = "0.1.1"
dependencies = [
"num-traits",
"ppc750cl-macros",
"serde",
]
[[package]]
@ -340,8 +353,11 @@ dependencies = [
name = "ppc750cl-macros"
version = "0.1.1"
dependencies = [
"itertools",
"proc-macro2",
"quote",
"serde",
"serde_yaml",
"syn",
]
@ -499,24 +515,36 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.128"
version = "1.0.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1056a0db1978e9dbf0f6e4fca677f6f9143dc1c19de346f22cac23e422196834"
checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.128"
version = "1.0.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13af2fbb8b60a8950d6c72a56d2095c28870367cc8e10c55e9745bac4995a2c4"
checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_yaml"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6375dbd828ed6964c3748e4ef6d18e7a175d408ffe184bca01698d0c73f915a9"
dependencies = [
"dtoa",
"indexmap",
"serde",
"yaml-rust",
]
[[package]]
name = "sfmt"
version = "0.6.0"
@ -630,3 +658,12 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -11,3 +11,4 @@ repository = "https://github.com/terorie/ppc750cl"
[dependencies]
num-traits = "0.2.14"
ppc750cl-macros = { path = "../macros", version = "0.1.1" }
serde = "1.0.129"

View File

@ -5,208 +5,103 @@ use num_traits::PrimInt;
use crate::Ins;
/*
type IOResult = std::io::Result<()>;
pub trait AsmFormatter<W>
where
W: Write,
{
/// Returns the underlying writer.
fn writer(&mut self) -> &mut W;
fn write_ins(ins: &Ins);
}
/// Callback for custom styling before writing an instruction.
fn before_instruction(&mut self, _: &Ins) -> IOResult {
Ok(())
}
/// Callback for custom styling after writing an instruction.
fn after_instruction(&mut self, _: &Ins) -> IOResult {
Ok(())
}
pub struct SimpleFormatter();
impl<W: Write> SimpleFormatter {
/// Writes the instruction mnemonic.
fn write_mnemonic(&mut self, name: &str) -> IOResult {
write!(self.writer(), "{}", name)
fn write_mnemonic(writer: &mut W, name: &str) -> IOResult {
write!(writer, "{}", name)
}
/// Separates the instruction mnemonic and arguments.
fn write_opcode_separator(&mut self) -> IOResult {
write!(self.writer(), " ")
fn write_opcode_separator(writer: &mut W) -> IOResult {
write!(writer, " ")
}
/// Separates two instruction arguments (e.g. registers).
fn write_operand_separator(&mut self) -> IOResult {
write!(self.writer(), ", ")
fn write_operand_separator(writer: &mut W) -> IOResult {
write!(writer, ", ")
}
/// Writes a general-purpose register argument.
fn write_gpr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "r{}", reg)
fn write_gpr(writer: &mut W, reg: u8) -> IOResult {
write!(writer, "r{}", reg)
}
/// Writes a nullable general-purpose register argument.
fn write_gpr0(&mut self, reg: u8) -> IOResult {
fn write_gpr0(writer: &mut W, reg: u8) -> IOResult {
if reg != 0 {
self.write_gpr(reg)
Self::write_gpr(writer, reg)
} else {
write!(self.writer(), "0")
write!(writer, "0")
}
}
/// Writes a floating point register argument.
fn write_fpr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "f{}", reg)
fn write_fpr(writer: &mut W, reg: u8) -> IOResult {
write!(writer, "f{}", reg)
}
/// Writes a condition register argument.
fn write_cr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "cr{}", reg)
fn write_cr(writer: &mut W, reg: u8) -> IOResult {
write!(writer, "cr{}", reg)
}
/// Writes a paired-singles quantization register argument.
fn write_qr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "qr{}", reg)
fn write_qr(writer: &mut W, reg: u8) -> IOResult {
write!(writer, "qr{}", reg)
}
fn write_sr(&mut self, reg: u8) -> IOResult {
write!(self.writer(), "{}", reg)
fn write_sr(writer: &mut W, reg: u8) -> IOResult {
write!(writer, "{}", reg)
}
/// Sets the mnemonic 'o' suffix.
fn write_oe(&mut self, oe: u8) -> IOResult {
fn write_oe(writer: &mut W, oe: u8) -> IOResult {
if oe != 0 {
write!(self.writer(), "o")?;
write!(writer, "o")?;
}
Ok(())
}
/// Sets the mnemonic 'a' suffix.
fn write_aa(&mut self, aa: u8) -> IOResult {
fn write_aa(writer: &mut W, aa: u8) -> IOResult {
if aa != 0 {
write!(self.writer(), "a")?;
write!(writer, "a")?;
}
Ok(())
}
/// Sets the mnemonic 'l' suffix.
fn write_lk(&mut self, lk: u8) -> IOResult {
fn write_lk(writer: &mut W, lk: u8) -> IOResult {
if lk != 0 {
write!(self.writer(), "l")?;
write!(writer, "l")?;
}
Ok(())
}
/// Sets the mnemonic '.' suffix.
fn write_rc(&mut self, rc: u8) -> IOResult {
fn write_rc(writer: &mut W, rc: u8) -> IOResult {
if rc != 0 {
write!(self.writer(), ".")?;
write!(writer, ".")?;
}
Ok(())
}
/// Writes an unsigned immediate.
fn write_uimm<T: Into<u16>>(&mut self, uimm: T) -> IOResult {
let uimm = uimm.into();
if uimm < 16 {
write!(self.writer(), "{}", uimm)
} else {
write!(self.writer(), "{:#x}", uimm)
}
}
/// Writes a signed immediate.
fn write_simm<T: PrimInt + Into<i32> + Display>(&mut self, simm: T) -> IOResult {
let simm: i32 = simm.into();
if simm < 8 {
write!(self.writer(), "{}", simm)
} else {
write!(self.writer(), "{:#x}", ReallySigned(simm))
}
}
/// Writes an instruction-specific field like the compare mode.
fn write_mode<P: PrimInt + Display>(&mut self, mode: P) -> IOResult {
write!(self.writer(), "{}", mode)
}
fn write_fm(&mut self, fm: u16) -> IOResult {
write!(self.writer(), "{}", fm)
}
fn write_offset_unsigned_open<T: Into<u32>>(&mut self, offset: T) -> IOResult {
let offset = offset.into();
if offset < 15 {
write!(self.writer(), "{}(", offset)
} else {
write!(self.writer(), "{:#x}(", offset)
}
}
/// Writes an offset prefix.
///
/// The next write calls that follow should be:
/// - An operand (almost always a general-purpose register)
/// - `write_offset_close()`
fn write_offset_open<T: Into<i32>>(&mut self, offset: T) -> IOResult {
let offset = offset.into();
if -9 < offset && offset < 10 {
write!(self.writer(), "{}(", offset)
} else {
write!(self.writer(), "{:#x}(", ReallySigned(offset))
}
}
/// Closes an offset prefix.
fn write_offset_close(&mut self) -> IOResult {
write!(self.writer(), ")")
}
/// Writes a branch target given the jump offset and current program counter.
fn write_branch_target(&mut self, offset: i32, _: u32) -> IOResult {
write!(self.writer(), "{:#x}", ReallySigned(offset))
}
}
pub struct SimpleFormatter<W: Write> {
pub writer: W,
}
impl<W: Write> SimpleFormatter<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
}
impl<W: Write> AsmFormatter<W> for SimpleFormatter<W> {
fn writer(&mut self) -> &mut W {
&mut self.writer
}
}
pub struct DoldecompFormatter<W: Write> {
pub writer: W,
}
impl<W: Write> DoldecompFormatter<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
}
impl<W: Write> AsmFormatter<W> for DoldecompFormatter<W> {
fn writer(&mut self) -> &mut W {
&mut self.writer
}
fn before_instruction(&mut self, ins: &Ins) -> IOResult {
write!(
&mut self.writer,
"/* {:0>8X} {:0>2X} {:0>2X} {:0>2X} {:0>2X} */\t",
ins.addr,
(ins.code >> 24) as u8,
(ins.code >> 16) as u8,
(ins.code >> 8) as u8,
ins.code as u8
)
fn write_ins(ins: &Ins) {
todo!()
}
}
@ -230,22 +125,4 @@ impl<N: PrimInt> UpperHex for ReallySigned<N> {
f.pad_integral(num >= 0, prefix, &bare_hex)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_doldecomp_formatter() {
let buf = Vec::<u8>::new();
let mut formatter = DoldecompFormatter::new(buf);
let ins = Ins::new(0x48000007, 6);
ins.write_string(&mut formatter).unwrap();
assert_eq!(
String::from_utf8(formatter.writer).unwrap(),
"/* 00000006 48 00 00 07 */\tbla 0x4"
);
}
}
*/

View File

@ -1,570 +0,0 @@
#![allow(clippy::bad_bit_mask)]
use crate::{bit, bits};
use ppc750cl_macros::isa;
isa! {
"add" & 0xfc0007fe == 0x7c000214;
//"addc" & 0xfc0007fe == 0x7c00002a;
"addc" & 0x0 == 0x0;
"adde" & 0xfc0007fe == 0x7c000114;
"addi" & 0xfc000000 == 0x38000000;
"addic" & 0xfc000000 == 0x30000000;
"addic." & 0xfc000000 == 0x34000000;
"addis" & 0xfc000000 == 0x3c000000;
"addme" & 0xfc00fbfe == 0x7c0001d4;
"addze" & 0xfc00fbfe == 0x7c000194;
"and" & 0xfc0007fe == 0x7c000038;
"andc" & 0xfc0007fe == 0x7c000078;
"andi." & 0xfc000000 == 0x70000000;
"andis." & 0xfc000000 == 0x74000000;
"b" & 0xfc000000 == 0x48000000;
//"bc" & 0xfc000000 == 0x40000000;
"bc" & 0x0 == 0x0; // TODO
//"bcctr" & 0xfc00ffff == 0x4c000210;
"bcctr" & 0x0 == 0x0; // TODO
"bclr" & 0xfc00fffe == 0x4c000020;
"cmp" & 0xfc4007ff == 0x7c000000;
"cmpi" & 0xfc400000 == 0x2c000000;
"cmpl" & 0xfc4007ff == 0x7c000040;
"cmpli" & 0xfc400000 == 0x28000000;
"cntlzw" & 0xfc00fffe == 0x7c000034;
"crand" & 0xfc0007ff == 0x4c000202;
"crandc" & 0xfc0007ff == 0x4c000102;
"creqv" & 0xfc0007ff == 0x4c000242;
"crnand" & 0xfc0007ff == 0x4c0001c2;
"crnor" & 0xfc0007ff == 0x4c000042;
"cror" & 0xfc0007ff == 0x4c000382;
"crorc" & 0xfc0007ff == 0x4c000342;
"crxor" & 0xfc0007ff == 0x4c000182;
"dcbf" & 0xffe007ff == 0x7c0000ac;
"dcbi" & 0xffe007ff == 0x7c0003ac;
"dcbst" & 0xffe007ff == 0x7c00006c;
"dcbt" & 0xffe007ff == 0x7c00022c;
"dcbtst" & 0xffe007ff == 0x7c0001ec;
"dcbz" & 0xffe007ff == 0x7c0007ec;
"dcbz_l" & 0xffe007ff == 0x100007ec;
"divw" & 0xfc0003fe == 0x7c0003d6;
"divwu" & 0xfc0003fe == 0x7c000396;
"eciwx" & 0xfc0003ff == 0x7c00026c;
"ecowx" & 0xfc0003ff == 0x7c00036c;
"eieio" & 0xffffffff == 0x7c0006ac;
"eqv" & 0xfc0003fe == 0x7c000238;
"extsb" & 0xfc00fffe == 0x7c000774;
"extsh" & 0xfc00fffe == 0x7c000734;
//"fabs" & 0xfc1f07fe == 0xfc000734;
"fabs" & 0x0 == 0x0; // TODO
"fadd" & 0xfc0007fe == 0xfc00002a;
"fadds" & 0xfc0007fe == 0xec00002a;
"fcmpo" & 0xfc6007ff == 0xfc000040;
"fcmpu" & 0xfc6007ff == 0xfc000000;
"fctiw" & 0xfc1f07fe == 0xfc00001c;
"fctiwz" & 0xfc1f07fe == 0xfc00001e;
"fdiv" & 0xfc0007fe == 0xfc000024;
"fdivs" & 0xfc0007fe == 0xec000024;
"fmadd" & 0xfc00003e == 0xfc00003a;
"fmadds" & 0xfc00003e == 0xec00003a;
"fmr" & 0xfc1f07fe == 0xfc000090;
"fmsub" & 0xfc00003e == 0xfc000038;
"fmsubs" & 0xfc00003e == 0xec000038;
"fmul" & 0xfc00f83e == 0xfc000032;
"fmuls" & 0xfc00f83e == 0xec000032;
"fnabs" & 0xfc1f07fe == 0xfc000110;
//"fneg" & 0xfc1f07fe == 0xfc000050;
"fneg" & 0x0 == 0x0; // TODO
"fnmadd" & 0xfc00003e == 0xfc00003e;
"fnmadds" & 0xfc00003e == 0xec00003e;
"fnmsub" & 0xfc00003e == 0xfc00003c;
"fnmsubs" & 0xfc00003e == 0xec00003c;
"fres" & 0xfc1f07fe == 0xec000030;
"frsp" & 0xfc1f07fe == 0xfc000018;
"frsqrte" & 0xfc1f07fe == 0xfc000034;
"fsel" & 0xfc00003e == 0xfc00002e;
"fsub" & 0xfc0007fe == 0xfc000028;
"fsubs" & 0xfc0007fe == 0xec000028;
"icbi" & 0xffe007ff == 0x7c0007ac;
"isync" & 0xffffffff == 0x4c00012c;
"lbz" & 0xfc000000 == 0x88000000;
"lbzu" & 0xfc000000 == 0x8c000000;
"lbzux" & 0xfc0007ff == 0x7c0000ee;
"lbzx" & 0xfc0007ff == 0x7c0000ae;
"lfd" & 0xfc000000 == 0xc8000000;
"lfdu" & 0xfc000000 == 0xcc000000;
"lfdux" & 0xfc0007ff == 0x7c0004ee;
//"lfdx" & 0xfc0007ff == 0x7c00045e;
"lfdx" & 0x0 == 0x0;
"lfs" & 0xfc000000 == 0xc0000000;
"lfsu" & 0xfc000000 == 0xc4000000;
"lfsux" & 0xfc0007ff == 0x7c00046e;
"lfsx" & 0xfc0007ff == 0x7c00042e;
"lha" & 0xfc000000 == 0xa8000000;
"lhau" & 0xfc000000 == 0xac000000;
"lhaux" & 0xfc0007ff == 0x7c0002ee;
"lhax" & 0xfc0007ff == 0x7c0002ae;
"lhbrx" & 0xfc0007ff == 0x7c00062c;
"lhz" & 0xfc000000 == 0xa0000000;
"lhzu" & 0xfc000000 == 0xa4000000;
"lhzux" & 0xfc0007ff == 0x7c00026e;
"lhzx" & 0xfc0007ff == 0x7c00022e;
"lmw" & 0xfc000000 == 0xb8000000;
"lswi" & 0xfc0007ff == 0x7c0004aa;
"lswx" & 0xfc0007ff == 0x7c00042a;
"lwarx" & 0xfc0007ff == 0x7c000028;
"lwbrx" & 0xfc0007ff == 0x7c00042c;
"lwz" & 0xfc000000 == 0x80000000;
"lwzu" & 0xfc000000 == 0x84000000;
"lwzux" & 0xfc0007ff == 0x7c00006e;
"lwzx" & 0xfc0007ff == 0x7c00002e;
"mcrf" & 0xfc300fff == 0x4c000000;
"mcrfs" & 0xfc30ffff == 0xfc000080;
"mcrxr" & 0xfc30ffff == 0x7c000400;
"mfcr" & 0xfc1fffff == 0x7c000026;
//"mffs" & 0xfc1ffffe == 0x7c00048e;
"mffs" & 0x0 == 0x0; // TODO
"mfmsr" & 0xfc1fffff == 0x7c0000a6;
"mfspr" & 0xfc0007ff == 0x7c0002a6;
"mfsr" & 0xfc10ffff == 0x7c0004a6;
"mfsrin" & 0xfc1f07ff == 0x7c000526;
"mftb" & 0xfc0007ff == 0x7c0002e6;
"mtcrf" & 0xfc100fff == 0x7c000120;
"mtfsb0" & 0xfc1ffffe == 0xfc00008c;
"mtfsb1" & 0xfc1ffffe == 0xfc00004c;
"mtfsf" & 0xfe0107fe == 0xfc00058e;
"mtfsfi" & 0xfc7f0ffe == 0xfc00010c;
"mtmsr" & 0xfc1fffff == 0x7c000124;
"mtspr" & 0xfc0007ff == 0x7c0003a6;
"mtsr" & 0xfc10ffff == 0x7c0001a4;
"mtsrin" & 0xfc1f07ff == 0x7c0001e4;
//"mulhw" & 0xfc0007fe == 0x7c000096;
"mulhw" & 0x0 == 0x0;
//"mulhwu" & 0xfc0007fe == 0x7c000016;
"mulhwu" & 0x0 == 0x0;
"mulli" & 0xfc000000 == 0x1c000000;
"mullw" & 0xfc0003fe == 0x7c0001d6;
"nand" & 0xfc0007fe == 0x7c0003b8;
"neg" & 0xfc00fffe == 0x7c0000d0;
"nor" & 0xfc0007fe == 0x7c0000f8;
"or" & 0xfc0007fe == 0x7c000378;
"orc" & 0xfc0007fe == 0x7c000338;
"ori" & 0xfc000000 == 0x60000000;
"oris" & 0xfc000000 == 0x64000000;
"psq_l" & 0xfc000000 == 0xe0000000;
"psq_lu" & 0xfc000000 == 0xe4000000;
"psq_lux" & 0xfc00007f == 0x1000004c;
"psq_lx" & 0xfc00007f == 0x1000000c;
"psq_st" & 0xfc000000 == 0xf0000000;
"psq_stu" & 0xfc000000 == 0xf4000000;
"psq_stux" & 0xfc00007f == 0x1000004e;
"psq_stx" & 0xfc00007f == 0x1000000e;
"ps_abs" & 0xfc1f07fe == 0x10000210;
"ps_add" & 0xfc0007fe == 0x1000002a;
"ps_cmpo0" & 0xfc6007ff == 0x10000040;
"ps_cmpo1" & 0xfc6007ff == 0x100000c0;
"ps_cmpu0" & 0xfc6007ff == 0x10000000;
"ps_cmpu1" & 0xfc6007ff == 0x10000080;
"ps_div" & 0xfc0007fe == 0x10000024;
"ps_madd" & 0xfc00003e == 0x1000003a;
"ps_madds0" & 0xfc00003e == 0x1000001c;
"ps_madds1" & 0xfc00003e == 0x1000001e;
"ps_merge00" & 0xfc0007fe == 0x10000420;
"ps_merge01" & 0xfc0007fe == 0x10000460;
"ps_merge10" & 0xfc0007fe == 0x100004a0;
"ps_merge11" & 0xfc0007fe == 0x100004e0;
"ps_mr" & 0xfc1f07fe == 0x10000090;
"ps_msub" & 0xfc00003e == 0x10000038;
"ps_mul" & 0xfc00f83e == 0x10000032;
"ps_muls0" & 0xfc00f83e == 0x10000018;
"ps_muls1" & 0xfc00f83e == 0x1000001a;
"ps_nabs" & 0xfc1f07fe == 0x10000110;
"ps_neg" & 0xfc1f07fe == 0x10000050;
"ps_nmadd" & 0xfc00003e == 0x1000003e;
"ps_nmsub" & 0xfc00003e == 0x1000003c;
"ps_res" & 0xfc1f07fe == 0x10000030;
"ps_rsqrte" & 0xfc1f07fe == 0x10000034;
"ps_sel" & 0xfc00003e == 0x1000002e;
"ps_sub" & 0xfc0007fe == 0x10000028;
"ps_sum0" & 0xfc00003e == 0x10000014;
"ps_sum1" & 0xfc00003e == 0x10000016;
"rfi" & 0xfffff801 == 0x4c000000;
"rlwimi" & 0xfc000000 == 0x50000000;
"rlwinm" & 0xfc000000 == 0x54000000;
"rlwnm" & 0xfc000000 == 0x5c000000;
"sc" & 0xffffffff == 0x44000002;
"slw" & 0xfc0007fe == 0x7c000030;
"sraw" & 0xfc0007fe == 0x7c000630;
"srawi" & 0xfc0007fe == 0x7c000670;
"srw" & 0xfc0007fe == 0x7c000430;
"stb" & 0xfc000000 == 0x98000000;
"stbu" & 0xfc000000 == 0x9c000000;
"stbux" & 0xfc0003ff == 0x7c0001ee;
"stbx" & 0xfc0003ff == 0x7c0001ae;
"stfd" & 0xfc000000 == 0xd8000000;
"stfdu" & 0xfc000000 == 0xdc000000;
"stfdux" & 0xfc0007ff == 0x7c0005ee;
"stfdx" & 0xfc0007ff == 0x7c0005ae;
"stfiwx" & 0xfc0007ff == 0x7c0007ae;
"stfs" & 0xfc000000 == 0xd0000000;
"stfsu" & 0xfc000000 == 0xd4000000;
"stfsux" & 0xfc0007ff == 0x7c00056e;
"stfsx" & 0xfc0007ff == 0x7c00052e;
"sth" & 0xfc000000 == 0xb0000000;
"sthbrx" & 0xfc0007ff == 0x7c00072c;
"sthu" & 0xfc000000 == 0xb4000000;
"sthux" & 0xfc0007ff == 0x7c00036e;
"sthx" & 0xfc0007ff == 0x7c00032e;
"stmw" & 0xfc000000 == 0xbc000000;
"stswi" & 0xfc0007ff == 0x7c0005aa;
"stswx" & 0xfc0007ff == 0x7c00052a;
"stw" & 0xfc000000 == 0x90000000;
"stwbrx" & 0xfc0007ff == 0x7c00052c;
"stwcx." & 0xfc0007ff == 0x7c00012d;
"stwu" & 0xfc000000 == 0x94000000;
"stwux" & 0xfc0007ff == 0x7c00016e;
"stwx" & 0xfc0007ff == 0x7c00012e;
"subf" & 0xfc0003fe == 0x7c000050;
"subfc" & 0xfc0003fe == 0x7c000010;
"subfe" & 0xfc0003fe == 0x7c000110;
"subfic" & 0xfc000000 == 0x20000000;
"subfme" & 0xfc00fbfe == 0x7c0001d0;
"subfze" & 0xfc00fbfe == 0x7c000190;
"sync" & 0xffffffff == 0x7c0004ac;
"tlbie" & 0xffff07ff == 0x7c000264;
"tlbsync" & 0xffffffff == 0x7c00046c;
"tw" & 0xfc0007ff == 0x7c000008;
"twi" & 0xfc000000 == 0xc000000;
"xor" & 0xfc0007fe == 0x7c000278;
"xori" & 0xfc000000 == 0x68000000;
"xoris" & 0xfc000000 == 0x6c000000;
}
impl Opcode {
pub const BLR: u32 = 0x4c000020;
pub fn from_code(x: u32) -> Self {
let op = match bits(x, 0..6) {
0b000011 => Opcode::Twi,
0b000100 => Self::from_code_cl_ext(x),
0b000111..=0b001111 => Self::from_code_basic1(x),
0b010000 => Opcode::Bc,
0b010001 => Opcode::Sc,
0b010010 => Opcode::B,
0b010011 => Self::from_code_010011(x),
0b010100..=0b011101 => Self::from_code_basic2(x),
0b011111 => Self::from_code_011111(x),
0b100000..=0b110111 => Self::from_code_basic3(x),
0b111000..=0b111001 => Self::from_code_psq(x),
0b111011 => Self::from_code_111011(x),
0b111100..=0b111101 => Self::from_code_psq(x),
0b111111 => Self::from_code_111111(x),
_ => Opcode::Illegal,
};
if !op.is_valid(x) {
return Opcode::Illegal;
}
op
}
fn from_code_cl_ext(x: u32) -> Self {
match bits(x, 26..31) {
0b00000 => match bits(x, 21..26) {
0b00000 => Opcode::PsCmpu0,
0b00001 => Opcode::PsCmpo0,
0b00010 => Opcode::PsCmpu1,
0b00011 => Opcode::PsCmpo1,
_ => Opcode::Illegal,
},
0b00110 => {
if bit(x, 25) == 0 {
Opcode::PsqLx
} else {
Opcode::PsqLux
}
}
0b00111 => {
if bit(x, 25) == 0 {
Opcode::PsqStx
} else {
Opcode::PsqStux
}
}
0b01010 => Opcode::PsSum0,
0b01011 => Opcode::PsSum1,
0b01110 => Opcode::PsMadds0,
0b01111 => Opcode::PsMadds1,
0b10111 => Opcode::PsSel,
0b11100 => Opcode::PsMsub,
0b11101 => Opcode::PsMadd,
0b11110 => Opcode::PsNmsub,
0b11111 => Opcode::PsNmadd,
0b01100 => Opcode::PsMuls0,
0b01101 => Opcode::PsMuls1,
0b11001 => Opcode::PsMul,
0b10010 => Opcode::PsDiv,
0b10100 => Opcode::PsSub,
0b10101 => Opcode::PsAdd,
0b11000 => Opcode::PsRes,
0b11010 => Opcode::PsRsqrte,
0b01000 => match bits(x, 21..26) {
0b00001 => Opcode::PsNeg,
0b00010 => Opcode::PsMr,
0b00100 => Opcode::PsNabs,
0b01000 => Opcode::PsAbs,
_ => Opcode::Illegal,
},
0b10000 => match bits(x, 21..26) {
0b10000 => Opcode::PsMerge00,
0b10001 => Opcode::PsMerge01,
0b10010 => Opcode::PsMerge10,
0b10011 => Opcode::PsMerge11,
_ => Opcode::Illegal,
},
0b10110 => Opcode::DcbzL,
// Unknown paired-singles key.
_ => Opcode::Illegal,
}
}
fn from_code_basic1(x: u32) -> Self {
match bits(x, 0..6) {
0b000111 => Opcode::Mulli,
0b001000 => Opcode::Subfic,
0b001010 => Opcode::Cmpli,
0b001011 => Opcode::Cmpi,
0b001100 => Opcode::Addic,
0b001101 => Opcode::Addic_,
0b001110 => Opcode::Addi,
0b001111 => Opcode::Addis,
_ => Opcode::Illegal,
}
}
fn from_code_010011(x: u32) -> Self {
match bits(x, 21..27) {
0b000000 => Opcode::Mcrf,
0b000001 => Opcode::Bclr,
0b100001 => Opcode::Bcctr,
0b000011 => Opcode::Rfi,
0b001001 => Opcode::Isync,
0b000010 => Opcode::Crnor,
0b001000 => Opcode::Crandc,
0b001100 => Opcode::Crxor,
0b001110 => Opcode::Crnand,
0b010000 => Opcode::Crand,
0b010010 => Opcode::Creqv,
0b011010 => Opcode::Crorc,
0b011100 => Opcode::Cror,
_ => Opcode::Illegal,
}
}
fn from_code_basic2(x: u32) -> Self {
match bits(x, 0..6) {
0b10100 => Opcode::Rlwimi,
0b10101 => Opcode::Rlwinm,
0b10111 => Opcode::Rlwnm,
0b11000 => Opcode::Ori,
0b11001 => Opcode::Oris,
0b11010 => Opcode::Xori,
0b11011 => Opcode::Xoris,
0b11100 => Opcode::Andi_,
0b11101 => Opcode::Andis_,
_ => Opcode::Illegal,
}
}
fn from_code_011111(x: u32) -> Self {
match bits::<u32>(x, 21..31) {
0b00_0000_0000 => Opcode::Cmp,
0b00_0010_0000 => Opcode::Cmpl,
0b00_0000_0100 => Opcode::Tw,
0b00_0000_1000 => Opcode::Subfc,
0b00_0000_1010 => Opcode::Addc,
0b00_0000_1011 => Opcode::Mulhwu,
0b00_0001_0011 => Opcode::Mfcr,
0b00_0001_0100 => Opcode::Lwarx,
0b00_0001_0111 => Opcode::Lwzx,
0b00_0001_1000 => Opcode::Slw,
0b00_0001_1010 => Opcode::Cntlzw,
0b00_0001_1100 => Opcode::And,
0b00_0010_1000 => Opcode::Subf,
0b00_0011_0110 => Opcode::Dcbst,
0b00_0011_0111 => Opcode::Lwzux,
0b00_0011_1100 => Opcode::Andc,
0b00_0100_1011 => Opcode::Mulhw,
0b00_0101_0011 => Opcode::Mfmsr,
0b00_0101_0110 => Opcode::Dcbf,
0b00_0101_0111 => Opcode::Lbzx,
0b00_0110_1000 => Opcode::Neg,
0b00_0111_0111 => Opcode::Lbzux,
0b00_0111_1100 => Opcode::Nor,
0b00_1000_1000 => Opcode::Subfe,
0b00_1000_1010 => Opcode::Adde,
0b00_1001_0000 => Opcode::Mtcrf,
0b00_1001_0010 => Opcode::Mtmsr,
0b00_1001_0110 => Opcode::Stwcx_,
0b00_1001_0111 => Opcode::Stwx,
0b00_1011_0111 => Opcode::Stwux,
0b00_1100_1000 => Opcode::Subfze,
0b00_1100_1010 => Opcode::Addze,
0b00_1101_0010 => Opcode::Mtsr,
0b00_1101_0111 => Opcode::Stbx,
0b00_1110_1000 => Opcode::Subfme,
0b00_1110_1010 => Opcode::Addme,
0b00_1110_1011 => Opcode::Mullw,
0b00_1111_0010 => Opcode::Mtsrin,
0b00_1111_0110 => Opcode::Dcbtst,
0b00_1111_0111 => Opcode::Stbux,
0b01_0000_1010 => Opcode::Add,
0b01_0001_0110 => Opcode::Dcbt,
0b01_0001_0111 => Opcode::Lhzx,
0b01_0001_1100 => Opcode::Eqv,
0b01_0011_0010 => Opcode::Tlbie,
0b01_0011_0110 => Opcode::Eciwx,
0b01_0011_0111 => Opcode::Lhzux,
0b01_0011_1100 => Opcode::Xor,
0b01_0101_0011 => Opcode::Mfspr,
0b01_0101_0111 => Opcode::Lhax,
0b01_0111_0011 => Opcode::Mftb,
0b01_0111_0111 => Opcode::Lhaux,
0b01_1001_0111 => Opcode::Sthx,
0b01_1001_1100 => Opcode::Orc,
0b01_1011_0110 => Opcode::Ecowx,
0b01_1011_0111 => Opcode::Sthux,
0b01_1011_1100 => Opcode::Or,
0b01_1100_1011 => Opcode::Divwu,
0b01_1101_0011 => Opcode::Mtspr,
0b01_1101_0110 => Opcode::Dcbi,
0b01_1101_1100 => Opcode::Nand,
0b01_1110_1011 => Opcode::Divw,
0b10_0000_0000 => Opcode::Mcrxr,
0b10_0001_0101 => Opcode::Lswx,
0b10_0001_0110 => Opcode::Lwbrx,
0b10_0001_0111 => Opcode::Lfsx,
0b10_0001_1000 => Opcode::Srw,
0b10_0011_0110 => Opcode::Tlbsync,
0b10_0011_0111 => Opcode::Lfsux,
0b10_0101_0011 => Opcode::Mfsr,
0b10_0101_0101 => Opcode::Lswi,
0b10_0101_0110 => Opcode::Sync,
0b10_0101_0111 => Opcode::Lfdx,
0b10_0111_0111 => Opcode::Lfdux,
0b10_1001_0011 => Opcode::Mfsrin,
0b10_1001_0101 => Opcode::Stswx,
0b10_1001_0110 => Opcode::Stwbrx,
0b10_1001_0111 => Opcode::Stfsx,
0b10_1011_0111 => Opcode::Stfsux,
0b10_1101_0101 => Opcode::Stswi,
0b10_1101_0111 => Opcode::Stfdx,
0b10_1111_0111 => Opcode::Stfdux,
0b11_0001_0110 => Opcode::Lhbrx,
0b11_0001_1000 => Opcode::Sraw,
0b11_0011_1000 => Opcode::Srawi,
0b11_0101_0110 => Opcode::Eieio,
0b11_1001_0110 => Opcode::Sthbrx,
0b11_1001_1010 => Opcode::Extsh,
0b11_1011_1010 => Opcode::Extsb,
0b11_1101_0110 => Opcode::Icbi,
0b11_1101_0111 => Opcode::Stfiwx,
0b11_1111_0110 => Opcode::Dcbz,
_ => Opcode::Illegal,
}
}
fn from_code_basic3(x: u32) -> Self {
match bits(x, 0..6) {
0b100000 => Opcode::Lwz,
0b100001 => Opcode::Lwzu,
0b100010 => Opcode::Lbz,
0b100011 => Opcode::Lbzu,
0b100100 => Opcode::Stw,
0b100101 => Opcode::Stwu,
0b100110 => Opcode::Stb,
0b100111 => Opcode::Stbu,
0b101000 => Opcode::Lhz,
0b101001 => Opcode::Lhzu,
0b101010 => Opcode::Lha,
0b101011 => Opcode::Lhau,
0b101100 => Opcode::Sth,
0b101101 => Opcode::Sthu,
0b101110 => Opcode::Lmw,
0b101111 => Opcode::Stmw,
0b110000 => Opcode::Lfs,
0b110001 => Opcode::Lfsu,
0b110010 => Opcode::Lfd,
0b110011 => Opcode::Lfdu,
0b110100 => Opcode::Stfs,
0b110101 => Opcode::Stfsu,
0b110110 => Opcode::Stfd,
0b110111 => Opcode::Stfdu,
_ => disasm_unreachable!(x),
}
}
fn from_code_psq(x: u32) -> Self {
match bits(x, 0..6) {
0b111000 => Opcode::PsqL,
0b111001 => Opcode::PsqLu,
0b111100 => Opcode::PsqSt,
0b111101 => Opcode::PsqStu,
_ => disasm_unreachable!(x),
}
}
fn from_code_111011(x: u32) -> Self {
match bits(x, 26..31) {
0b10010 => Opcode::Fdivs,
0b10100 => Opcode::Fsubs,
0b10101 => Opcode::Fadds,
0b11000 => Opcode::Fres,
0b11001 => Opcode::Fmuls,
0b11100 => Opcode::Fmsubs,
0b11101 => Opcode::Fmadds,
0b11110 => Opcode::Fnmsubs,
0b11111 => Opcode::Fnmadds,
_ => Opcode::Illegal,
}
}
fn from_code_111111(x: u32) -> Self {
match bits::<u32>(x, 26..31) {
0b00000 => match bits(x, 24..26) {
0b00 => Opcode::Fcmpu,
0b01 => Opcode::Fcmpo,
0b10 => Opcode::Mcrfs,
_ => Opcode::Illegal,
},
0b00110 => match bits(x, 23..26) {
0b001 => Opcode::Mtfsb1,
0b010 => Opcode::Mtfsb0,
0b100 => Opcode::Mtfsfi,
_ => Opcode::Illegal,
},
0b00111 => match bits(x, 21..26) {
0b10010 => Opcode::Mffs,
0b10110 => Opcode::Mtfsf,
_ => Opcode::Illegal,
},
0b01000 => match bits(x, 22..26) {
0b0001 => Opcode::Fneg,
0b0010 => Opcode::Fmr,
0b0100 => Opcode::Fnabs,
0b1000 => Opcode::Fabs,
_ => Opcode::Illegal,
},
0b01100 => Opcode::Frsp,
0b01110 => Opcode::Fctiw,
0b01111 => Opcode::Fctiwz,
0b10010 => Opcode::Fdiv,
0b10100 => Opcode::Fsub,
0b10101 => Opcode::Fadd,
0b10111 => Opcode::Fsel,
0b11001 => Opcode::Fmul,
0b11010 => Opcode::Frsqrte,
0b11100 => Opcode::Fmsub,
0b11101 => Opcode::Fmadd,
0b11110 => Opcode::Fnmsub,
0b11111 => Opcode::Fnmadd,
_ => Opcode::Illegal,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
macro_rules! disasm_unreachable {
($msg:expr $(,)?) => {{
panic!(
"internal error: entered unreachable code disassembling instruction 0x{:08x}",
$msg
)
}};
}

3
disasm/src/prelude.rs Normal file
View File

@ -0,0 +1,3 @@
pub use crate::Field::*;
pub use crate::Ins;
pub use crate::Opcode::*;

952
disasm/tests/test_disasm.rs Normal file
View File

@ -0,0 +1,952 @@
use ppc750cl::prelude::*;
use ppc750cl::GPR;
#[test]
fn test_ins_addi() {
let ins = Ins::new(0x7c002014, 0x8000_0000u32);
assert_eq!(ins.op, Addc);
assert_eq!(ins.fields(), vec![rD(GPR(0)), rA(GPR(0)), rB(GPR(4))]);
}
/*
macro_rules! assert_asm {
($code:expr, $disasm:expr) => {{
assert_eq!(Ins::new($code, 0x8000_0000u32).to_string(), $disasm)
}};
}
#[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!(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");
}
#[test]
fn test_ins_addis() {
assert_asm!(0x3C030000, "addis r0, r3, 0");
assert_asm!(0x3C038000, "addis r0, r3, 0x8000");
assert_asm!(0x3D00EFCE, "lis r8, 0xefce");
}
#[test]
fn test_ins_addze() {
assert_asm!(0x7C000194, "addze r0, r0");
}
#[test]
fn test_ins_and() {
assert_asm!(0x7C001838, "and r0, r0, r3");
assert_asm!(0x7C001839, "and. r0, r0, r3");
}
#[test]
fn test_ins_andc() {
assert_asm!(0x7C001878, "andc r0, r0, r3");
}
#[test]
fn test_ins_andi_() {
assert_asm!(0x70000009, "andi. r0, r0, 9");
}
#[test]
fn test_ins_andis_() {
assert_asm!(0x77c802ff, "andis. r8, r30, 0x2ff");
}
#[test]
fn test_ins_b() {
assert_asm!(0x48000000, "b 0x0");
assert_asm!(0x48000004, "b 0x4");
assert_asm!(0x4800A5C9, "bl 0xa5c8");
assert_asm!(0x4823B4D9, "bl 0x23b4d8");
assert_asm!(0x4BE03C99, "bl -0x1fc368");
assert_asm!(0x4BDC1A59, "bl -0x23e5a8");
}
#[test]
fn test_ins_bc() {
assert_asm!(0x40800008, "bge 0x8");
assert_asm!(0x40802350, "bge 0x2350");
assert_asm!(0x4080FC7C, "bge -0x384");
assert_asm!(0x408100AC, "ble 0xac");
assert_asm!(0x4081F788, "ble -0x878");
assert_asm!(0x40821BA0, "bne 0x1ba0");
assert_asm!(0x4082E3C4, "bne -0x1c3c");
assert_asm!(0x408600D8, "bne cr1, 0xd8");
assert_asm!(0x4086FECC, "bne cr1, -0x134");
assert_asm!(0x409C000C, "bge cr7, 0xc");
// assert_asm!(0x40A10010, "ble+ 0x10");
assert_asm!(0x4180000C, "blt 0xc");
assert_asm!(0x4180F9C0, "blt -0x640");
assert_asm!(0x4181021C, "bgt 0x21c");
assert_asm!(0x4181FD80, "bgt -0x280");
assert_asm!(0x41822304, "beq 0x2304");
assert_asm!(0x4182FE3C, "beq -0x1c4");
assert_asm!(0x418401AC, "blt cr1, 0x1ac");
assert_asm!(0x4184FCE4, "blt cr1, -0x31c");
assert_asm!(0x418500C0, "bgt cr1, 0xc0");
assert_asm!(0x418502E4, "bgt cr1, 0x2e4");
assert_asm!(0x419A0138, "beq cr6, 0x138");
assert_asm!(0x419C0008, "blt cr7, 0x8");
assert_asm!(0x4200F560, "bdnz -0xaa0");
}
#[test]
fn test_ins_bcctr() {
assert_asm!(0x4E800420, "bctr");
assert_asm!(0x4E800421, "bctrl");
}
#[test]
fn test_ins_bclr() {
assert_asm!(0x4C800020, "bgelr");
assert_asm!(0x4C810020, "blelr");
assert_asm!(0x4C820020, "bnelr");
assert_asm!(0x4C9E0020, "bnelr cr7");
assert_asm!(0x4D800020, "bltlr");
assert_asm!(0x4D810020, "bgtlr");
assert_asm!(0x4D820020, "beqlr");
assert_asm!(0x4D860020, "beqlr cr1");
assert_asm!(0x4E800020, "blr");
assert_asm!(0x4E800021, "blrl");
}
#[test]
fn test_ins_cmp() {
assert_asm!(0x7C030000, "cmpw r3, r0");
}
#[test]
fn test_ins_cmpi() {
assert_asm!(0x2C050D00, "cmpwi r5, 0xd00");
assert_asm!(0x2F1F0000, "cmpwi cr6, r31, 0");
}
#[test]
fn test_ins_cmpl() {
assert_asm!(0x7C9A2040, "cmplw cr1, r26, r4");
}
#[test]
fn test_ins_cmpli() {
assert_asm!(0x2803FFF3, "cmplwi r3, 0xfff3");
assert_asm!(0x2884F8F0, "cmplwi cr1, r4, 0xf8f0");
}
#[test]
fn test_ins_cntlzw() {
assert_asm!(0x7C030034, "cntlzw r3, r0");
}
#[test]
fn test_ins_cror() {
assert_asm!(0x4C411382, "cror cr2, cr1, cr2");
}
#[test]
fn test_ins_dcbf() {
assert_asm!(0x7C0028AC, "dcbf 0, r5");
}
#[test]
fn test_ins_dcbi() {
assert_asm!(0x7C001BAC, "dcbi 0, r3");
}
#[test]
fn test_ins_dcbst() {
assert_asm!(0x7C00286C, "dcbst 0, r5");
}
#[test]
fn test_ins_dcbt() {
assert_asm!(0x7C001A2C, "dcbt 0, r3");
}
#[test]
fn test_ins_dcbz() {
assert_asm!(0x7C001FEC, "dcbz 0, r3");
}
#[test]
fn test_ins_dcbz_l() {
assert_asm!(0x10061FEC, "dcbz_l r6, r3");
}
#[test]
fn test_ins_divw() {
assert_asm!(0x7C8073D6, "divw r4, r0, r14");
}
#[test]
fn test_ins_divwu() {
assert_asm!(0x7C69E396, "divwu r3, r9, r28");
}
#[test]
fn test_ins_extsb() {
assert_asm!(0x7C650774, "extsb r5, r3");
assert_asm!(0x7C650775, "extsb. r5, r3");
}
#[test]
fn test_ins_extsh() {
assert_asm!(0x7C000734, "extsh r0, r0");
assert_asm!(0x7C000735, "extsh. r0, r0");
}
#[test]
fn test_ins_fabs() {
assert_asm!(0xFC000A10, "fabs f0, f1");
}
#[test]
fn test_ins_fadd() {
assert_asm!(0xFC00282A, "fadd f0, f0, f5");
}
#[test]
fn test_ins_fadds() {
assert_asm!(0xEC41602A, "fadds f2, f1, f12");
}
#[test]
fn test_ins_fcmpo() {
assert_asm!(0xFC00C840, "fcmpo cr0, f0, f25");
}
#[test]
fn test_ins_fcmpu() {
assert_asm!(0xFC00D000, "fcmpu cr0, f0, f26");
}
#[test]
fn test_ins_fctiwz() {
assert_asm!(0xFC20001E, "fctiwz f1, f0");
}
#[test]
fn test_ins_fdiv() {
assert_asm!(0xFC200024, "fdiv f1, f0, f0");
}
#[test]
fn test_ins_fdivs() {
assert_asm!(0xEC01F824, "fdivs f0, f1, f31");
}
#[test]
fn test_ins_fmadds() {
assert_asm!(0xEC0200FA, "fmadds f0, f2, f3, f0");
}
#[test]
fn test_ins_fmsub() {
assert_asm!(0xFC000028, "fsub f0, f0, f0");
}
#[test]
fn test_ins_fmsubs() {
assert_asm!(0xEC00B828, "fsubs f0, f0, f23");
}
#[test]
fn test_ins_fmul() {
assert_asm!(0xFC0000B2, "fmul f0, f0, f2");
assert_asm!(0xFC0000F2, "fmul f0, f0, f3");
}
#[test]
fn test_ins_fmuls() {
assert_asm!(0xEC0007B2, "fmuls f0, f0, f30");
}
#[test]
fn test_ins_fneg() {
assert_asm!(0xFCE00050, "fneg f7, f0");
}
#[test]
fn test_ins_fnmsub() {
assert_asm!(0xFCC640BC, "fnmsub f6, f6, f2, f8");
}
#[test]
fn test_ins_fnmsubs() {
assert_asm!(0xEC022B3C, "fnmsubs f0, f2, f12, f5");
}
#[test]
fn test_ins_fres() {
assert_asm!(0xEC000830, "fres f0, f1");
}
#[test]
fn test_ins_frsp() {
assert_asm!(0xFC000018, "frsp f0, f0");
}
#[test]
fn test_ins_frsqrte() {
assert_asm!(0xFC000834, "frsqrte f0, f1");
}
#[test]
fn test_ins_fsel() {
assert_asm!(0xFC01F82E, "fsel f0, f1, f0, f31");
}
#[test]
fn test_ins_fsub() {
assert_asm!(0xFC000828, "fsub f0, f0, f1");
}
#[test]
fn test_ins_fsubs() {
assert_asm!(0xEC000828, "fsubs f0, f0, f1");
}
#[test]
fn test_ins_icbi() {
assert_asm!(0x7C001FAC, "icbi 0, r3");
}
#[test]
fn test_ins_isync() {
assert_asm!(0x4C00012C, "isync");
}
#[test]
fn test_ins_lbz() {
assert_asm!(0x880104CC, "lbz r0, 0x4cc(r1)");
assert_asm!(0x8802801B, "lbz r0, -0x7fe5(r2)");
}
#[test]
fn test_ins_lbzu() {
assert_asm!(0x8D9DCA10, "lbzu r12, -0x35f0(r29)");
assert_asm!(0x8E3053EC, "lbzu r17, 0x53ec(r16)");
}
#[test]
fn test_ins_lbzux() {
assert_asm!(0x7C0400EE, "lbzux r0, r4, r0");
}
#[test]
fn test_ins_lbzx() {
assert_asm!(0x7C0300AE, "lbzx r0, r3, r0");
}
#[test]
fn test_ins_lfd() {
assert_asm!(0xC80140C8, "lfd f0, 0x40c8(r1)");
assert_asm!(0xC8028090, "lfd f0, -0x7f70(r2)");
}
#[test]
fn test_ins_lfdu() {
assert_asm!(0xCC03FFC0, "lfdu f0, -0x40(r3)");
}
#[test]
fn test_ins_lfdx() {
assert_asm!(0x7C0404AE, "lfdx f0, r4, r0");
}
#[test]
fn test_ins_lfs() {
assert_asm!(0xC001027C, "lfs f0, 0x27c(r1)");
assert_asm!(0xC0028000, "lfs f0, -0x8000(r2)");
}
#[test]
fn test_ins_lfsu() {
assert_asm!(0xC404FFF4, "lfsu f0, -0xc(r4)");
assert_asm!(0xC4170084, "lfsu f0, 0x84(r23)");
}
#[test]
fn test_ins_lfsux() {
assert_asm!(0x7C03846E, "lfsux f0, r3, r16");
}
#[test]
fn test_ins_lfsx() {
assert_asm!(0x7C03042E, "lfsx f0, r3, r0");
}
#[test]
fn test_ins_lha() {
assert_asm!(0xA861000E, "lha r3, 0xe(r1)");
assert_asm!(0xA80D9F64, "lha r0, -0x609c(r13)");
}
#[test]
fn test_ins_lhau() {
assert_asm!(0xAC060006, "lhau r0, 6(r6)");
assert_asm!(0xAC06FFFA, "lhau r0, -6(r6)");
}
#[test]
fn test_ins_lhax() {
assert_asm!(0x7C0402AE, "lhax r0, r4, r0");
}
#[test]
fn test_ins_lhz() {
assert_asm!(0xA00104D6, "lhz r0, 0x4d6(r1)");
assert_asm!(0xA00296DA, "lhz r0, -0x6926(r2)");
}
#[test]
fn test_ins_lhzu() {
assert_asm!(0xA40A0004, "lhzu r0, 4(r10)");
}
#[test]
fn test_ins_lhzux() {
assert_asm!(0x7C04026E, "lhzux r0, r4, r0");
}
#[test]
fn test_ins_lhzx() {
assert_asm!(0x7C03022E, "lhzx r0, r3, r0");
}
#[test]
fn test_ins_lmw() {
assert_asm!(0xBB210444, "lmw r25, 0x444(r1)");
}
#[test]
fn test_ins_lwbrx() {
assert_asm!(0x7D80242C, "lwbrx r12, 0, r4");
}
#[test]
fn test_ins_lwz() {
assert_asm!(0x800294F4, "lwz r0, -0x6b0c(r2)");
assert_asm!(0x80011254, "lwz r0, 0x1254(r1)");
}
#[test]
fn test_ins_lwzu() {
assert_asm!(0x84038608, "lwzu r0, -0x79f8(r3)");
assert_asm!(0x873E5058, "lwzu r25, 0x5058(r30)");
}
#[test]
fn test_ins_lwzux() {
assert_asm!(0x7C03006E, "lwzux r0, r3, r0");
}
#[test]
fn test_ins_lwzx() {
assert_asm!(0x7C03002E, "lwzx r0, r3, r0");
}
#[test]
fn test_ins_mfcr() {
assert_asm!(0x7C000026, "mfcr r0");
}
#[test]
fn test_ins_mffs() {
assert_asm!(0xFC00048E, "mffs f0");
}
#[test]
fn test_ins_mfmsr() {
assert_asm!(0x7C0000A6, "mfmsr r0");
}
#[test]
fn test_ins_mfspr() {
assert_asm!(0x7E1A02A6, "mfspr r16, 0x1a");
}
#[test]
fn test_ins_mfsr() {
assert_asm!(0x7E0004A6, "mfsr r16, 0");
}
#[test]
fn test_ins_mftb() {
assert_asm!(0x7C8C42E6, "mftb r4, 0x10c");
}
#[test]
fn test_ins_mtcrf() {
assert_asm!(0x7C6FF120, "mtcrf 255, r3");
}
/*
#[test]
fn test_ins_mtfsb0() {}
#[test]
fn test_ins_mtfsb1() {
assert_asm!(0xFFA0004C, "mtfsb1 0x1d");
}
*/
#[test]
fn test_ins_mtfsf() {
assert_asm!(0xFDFE058E, "mtfsf 255, f0");
assert_asm!(0xFDFEFD8E, "mtfsf 255, f31");
}
#[test]
fn test_ins_mtmsr() {
assert_asm!(0x7C000124, "mtmsr r0");
}
#[test]
fn test_ins_mtspr() {
assert_asm!(0x7E75FBA6, "mtspr 0x3f5, r19");
}
#[test]
fn test_ins_mtsr() {
assert_asm!(0x7E0001A4, "mtsr 0, r16");
}
#[test]
fn test_ins_mulhw() {
assert_asm!(0x7C7F2096, "mulhw r3, r31, r4");
}
#[test]
fn test_ins_mulhwu() {
assert_asm!(0x7C7D0016, "mulhwu r3, r29, r0");
}
#[test]
fn test_ins_mulli() {
assert_asm!(0x1C001880, "mulli r0, r0, 0x1880");
assert_asm!(0x1FBD0030, "mulli r29, r29, 0x30");
}
#[test]
fn test_ins_mullw() {
assert_asm!(0x7C7D01D6, "mullw r3, r29, r0");
}
#[test]
fn test_ins_nand() {
assert_asm!(0x7C7D03B8, "nand r29, r3, r0");
}
#[test]
fn test_ins_neg() {
assert_asm!(0x7C0600D0, "neg r0, r6");
}
#[test]
fn test_ins_nor() {
assert_asm!(0x7C0500F8, "nor r5, r0, r0");
}
#[test]
fn test_ins_or() {
assert_asm!(0x7C04DB78, "or r4, r0, r27");
}
#[test]
fn test_ins_orc() {
assert_asm!(0x7C042338, "orc r4, r0, r4");
}
#[test]
fn test_ins_ori() {
assert_asm!(0x60002204, "ori r0, r0, 0x2204");
}
#[test]
fn test_ins_oris() {
assert_asm!(0x67A06800, "oris r0, r29, 0x6800");
}
#[test]
fn test_ins_psq_l() {
assert_asm!(0xE02500AC, "psq_l f1, 0xac(r5), 0, qr0");
}
#[test]
fn test_ins_psq_lu() {
assert_asm!(0xE5435010, "psq_lu f10, 0x10(r3), 0, qr5");
}
#[test]
fn test_ins_psq_lx() {
assert_asm!(0x1000000C, "psq_lx f0, r0, r0, 0, qr0");
}
#[test]
fn test_ins_psq_st() {
assert_asm!(0xF1230210, "psq_st f9, 0x210(r3), 0, qr0");
assert_asm!(0xF1238008, "psq_st f9, 8(r3), 1, qr0");
}
#[test]
fn test_ins_psq_stu() {
assert_asm!(0xF40A0020, "psq_stu f0, 0x20(r10), 0, qr0");
}
#[test]
fn test_ins_psq_stx() {
assert_asm!(0x13E1000E, "psq_stx f31, r1, r0, 0, qr0");
}
#[test]
fn test_ins_ps_abs() {
assert_asm!(0x10A03210, "ps_abs f5, f6");
}
#[test]
fn test_ins_ps_add() {
assert_asm!(0x1006382A, "ps_add f0, f6, f7");
}
#[test]
fn test_ins_ps_cmpo0() {
assert_asm!(0x10070840, "ps_cmpo0 cr0, f7, f1");
}
#[test]
fn test_ins_ps_cmpu0() {
assert_asm!(0x10003000, "ps_cmpu0 cr0, f0, f6");
}
#[test]
fn test_ins_ps_cmpu1() {
assert_asm!(0x10003080, "ps_cmpu1 cr0, f0, f6");
}
#[test]
fn test_ins_ps_madd() {
assert_asm!(0x112141FA, "ps_madd f9, f1, f7, f8");
}
#[test]
fn test_ins_ps_madds0() {
assert_asm!(0x10AC299C, "ps_madds0 f5, f12, f6, f5");
}
#[test]
fn test_ins_ps_madds1() {
assert_asm!(0x110640DE, "ps_madds1 f8, f6, f3, f8");
}
#[test]
fn test_ins_ps_merge00() {
assert_asm!(0x10400420, "ps_merge00 f2, f0, f0");
}
#[test]
fn test_ins_ps_merge01() {
assert_asm!(0x10400C60, "ps_merge01 f2, f0, f1");
}
#[test]
fn test_ins_ps_merge10() {
assert_asm!(0x104004A0, "ps_merge10 f2, f0, f0");
}
#[test]
fn test_ins_ps_merge11() {
assert_asm!(0x10AA14E0, "ps_merge11 f5, f10, f2");
}
#[test]
fn test_ins_ps_msub() {
assert_asm!(0x10A53778, "ps_msub f5, f5, f29, f6");
}
#[test]
fn test_ins_ps_mul() {
assert_asm!(0x10000032, "ps_mul f0, f0, f0");
}
#[test]
fn test_ins_ps_muls0() {
assert_asm!(0x100002D8, "ps_muls0 f0, f0, f11");
}
#[test]
fn test_ins_ps_muls1() {
assert_asm!(0x10A2005A, "ps_muls1 f5, f2, f1");
}
#[test]
fn test_ins_ps_nabs() {
assert_asm!(0x10803210, "ps_abs f4, f6");
}
#[test]
fn test_ins_ps_neg() {
assert_asm!(0x10E03850, "ps_neg f7, f7");
}
#[test]
fn test_ins_ps_nmadd() {
assert_asm!(0x10CB30FE, "ps_nmadd f6, f11, f3, f6");
}
#[test]
fn test_ins_ps_nmsub() {
assert_asm!(0x107E083C, "ps_nmsub f3, f30, f0, f1");
}
#[test]
fn test_ins_ps_sel() {
assert_asm!(0x106428EE, "ps_sel f3, f4, f3, f5");
}
#[test]
fn test_ins_ps_sub() {
assert_asm!(0x10A92828, "ps_sub f5, f9, f5");
}
#[test]
fn test_ins_ps_sum0() {
assert_asm!(0x10230854, "ps_sum0 f1, f3, f1, f1");
}
#[test]
fn test_ins_ps_sum1() {
assert_asm!(0x10A12956, "ps_sum1 f5, f1, f5, f5");
}
#[test]
fn test_ins_rfi() {
assert_asm!(0x4C000064, "rfi");
}
#[test]
fn test_ins_rlwimi() {
assert_asm!(0x500306FE, "rlwimi r3, r0, 0, 0x1b, 0x1f");
assert_asm!(0x50032D74, "rlwimi r3, r0, 5, 0x15, 0x1a");
}
#[test]
fn test_ins_rlwinm() {
assert_asm!(0x54000423, "rlwinm. r0, r0, 0, 0x10, 0x11");
assert_asm!(0x54000432, "rlwinm r0, r0, 0, 0x10, 0x19");
}
#[test]
fn test_ins_rlwnm() {
assert_asm!(0x5D6A67FE, "rlwnm r10, r11, r12, 0x1f, 0x1f");
assert_asm!(0x5FC52EFE, "rlwnm r5, r30, r5, 0x1b, 0x1f");
}
#[test]
fn test_ins_sc() {
assert_asm!(0x44000002, "sc");
}
#[test]
fn test_ins_slw() {
assert_asm!(0x7C042830, "slw r4, r0, r5");
}
#[test]
fn test_ins_sraw() {
assert_asm!(0x7C043E30, "sraw r4, r0, r7");
}
#[test]
fn test_ins_srawi() {
assert_asm!(0x7C000E70, "srawi r0, r0, 1");
assert_asm!(0x7C001670, "srawi r0, r0, 2");
}
#[test]
fn test_ins_srw() {
assert_asm!(0x7C001C30, "srw r0, r0, r3");
}
#[test]
fn test_ins_stb() {
assert_asm!(0x980105EC, "stb r0, 0x5ec(r1)");
assert_asm!(0x98030000, "stb r0, 0(r3)");
}
#[test]
fn test_ins_stbu() {
assert_asm!(0x9D2A7428, "stbu r9, 0x7428(r10)");
assert_asm!(0x9D66FFFF, "stbu r11, -1(r6)");
}
#[test]
fn test_ins_stbux() {
assert_asm!(0x7C08F9EE, "stbux r0, r8, r31");
}
#[test]
fn test_ins_stbx() {
assert_asm!(0x7C03F9AE, "stbx r0, r3, r31");
}
#[test]
fn test_ins_stfd() {
assert_asm!(0xD80D97B0, "stfd f0, -0x6850(r13)");
assert_asm!(0xD8050090, "stfd f0, 0x90(r5)");
}
#[test]
fn test_ins_stfdu() {
assert_asm!(0xDC24FFC0, "stfdu f1, -0x40(r4)");
}
#[test]
fn test_ins_stfdx() {
assert_asm!(0x7C4405AE, "stfdx f2, r4, r0");
}
#[test]
fn test_ins_stfs() {
assert_asm!(0xD003086C, "stfs f0, 0x86c(r3)");
assert_asm!(0xD0038000, "stfs f0, -0x8000(r3)");
}
#[test]
fn test_ins_stfsx() {
assert_asm!(0x7C465D2E, "stfsx f2, r6, r11");
}
#[test]
fn test_ins_sth() {
assert_asm!(0xB0038A7C, "sth r0, -0x7584(r3)");
assert_asm!(0xB0035036, "sth r0, 0x5036(r3)");
}
#[test]
fn test_ins_sthbrx() {
assert_asm!(0x7C60072C, "sthbrx r3, 0, r0");
}
#[test]
fn test_ins_sthu() {
assert_asm!(0xB4055B88, "sthu r0, 0x5b88(r5)");
}
#[test]
fn test_ins_sthux() {
assert_asm!(0x7C03236E, "sthux r0, r3, r4");
}
#[test]
fn test_ins_sthx() {
assert_asm!(0x7C1C2B2E, "sthx r0, r28, r5");
}
#[test]
fn test_ins_stmw() {
assert_asm!(0xBFA202A4, "stmw r29, 0x2a4(r2)");
}
#[test]
fn test_ins_stw() {
assert_asm!(0x900140CC, "stw r0, 0x40cc(r1)");
assert_asm!(0x9003FFBC, "stw r0, -0x44(r3)");
}
#[test]
fn test_ins_stwbrx() {
assert_asm!(0x7C00FD2C, "stwbrx r0, 0, r31");
}
#[test]
fn test_ins_stwu() {
assert_asm!(0x9421EBC0, "stwu r1, -0x1440(r1)");
}
#[test]
fn test_ins_stwux() {
assert_asm!(0x7C01B96E, "stwux r0, r1, r23");
}
#[test]
fn test_ins_stwx() {
assert_asm!(0x7C03212E, "stwx r0, r3, r4");
}
#[test]
fn test_ins_subf() {
assert_asm!(0x7C051850, "subf r0, r5, r3");
assert_asm!(0x7C051851, "subf. r0, r5, r3");
}
#[test]
fn test_ins_subfc() {
assert_asm!(0x7C040010, "subfc r0, r4, r0");
}
#[test]
fn test_ins_subfe() {
assert_asm!(0x7C030110, "subfe r0, r3, r0");
}
#[test]
fn test_ins_subfic() {
assert_asm!(0x200602FF, "subfic r0, r6, 0x2ff");
}
#[test]
fn test_ins_subfze() {
assert_asm!(0x7C000190, "subfze r0, r0");
}
#[test]
fn test_ins_sync() {
assert_asm!(0x7C0004AC, "sync");
}
#[test]
fn test_ins_xor() {
assert_asm!(0x7C052A78, "xor r5, r0, r5");
}
#[test]
fn test_ins_xori() {
assert_asm!(0x68E71021, "xori r7, r7, 0x1021");
}
#[test]
fn test_ins_xoris() {
assert_asm!(0x6E3D8000, "xoris r29, r17, 0x8000");
}
*/

View File

@ -4,13 +4,16 @@ version = "0.1.1"
edition = "2018"
authors = ["Richard Patel <me@terorie.dev>"]
license = "GPL-3.0-or-later"
description = "Procedural macros for powerpc750cl"
description = "Auxiliary procedural macros for the ppc750cl disassembler"
repository = "https://github.com/terorie/ppc750cl"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.28"
quote = "1.0.9"
syn = { version = "1.0.74", features = ["full", "parsing"] }
itertools = "0.10.1"
proc-macro2 = "1.0"
quote = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
syn = { version = "1.0", features = ["full", "parsing"] }

View File

@ -1,215 +1,312 @@
use std::iter::FromIterator;
use std::ops::Range;
use proc_macro2::{Delimiter, Group, Ident, Literal, Span, TokenStream, TokenTree};
use itertools::Itertools;
use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::token::{And, EqEq, Semi};
use syn::{LitInt, LitStr};
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use syn::LitInt;
struct Opcodes {
opcodes: Punctuated<Opcode, Semi>,
}
#[derive(Default)]
pub(crate) struct BitRange(Range<u8>);
impl Parse for Opcodes {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
opcodes: Punctuated::parse_terminated(input)?,
})
impl<'de> Deserialize<'de> for BitRange {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let range_str: String = Deserialize::deserialize(deserializer)?;
if let Some((start_str, stop_str)) = range_str.split_once("..") {
let start = start_str.parse::<u8>().map_err(serde::de::Error::custom)?;
let stop = stop_str.parse::<u8>().map_err(serde::de::Error::custom)?;
Ok(Self(start..stop))
} else {
let bit_idx = range_str.parse::<u8>().map_err(serde::de::Error::custom)?;
Ok(Self(bit_idx..bit_idx))
}
}
}
#[derive(Debug)]
struct Opcode {
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Field {
name: String,
variant_name: String,
mask: u32,
bits: u32,
desc: String,
bits: BitRange,
signed: bool,
split: bool,
arg: String,
}
impl Parse for Opcode {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse::<LitStr>()?;
input.parse::<And>()?;
let mask = input.parse::<LitInt>()?;
input.parse::<EqEq>()?;
let bits = input.parse::<LitInt>()?;
Ok(Self {
name: name.value(),
variant_name: opcode_to_variant_name(&name)?,
mask: hex_from_lit_int(&mask)?,
bits: hex_from_lit_int(&bits)?,
})
impl Field {
fn variant_identifier(&self) -> TokenTree {
to_rust_ident(&self.name)
}
fn express_value(&self, code: TokenStream) -> TokenStream {
let mask_stop = self.bits.0.end;
let mask_size = self.bits.0.len();
quote! {
(((#code) >> (32 - #mask_stop)) & ((1 << #mask_size) - 1))
}
}
}
fn hex_from_lit_int(int: &LitInt) -> syn::Result<u32> {
let str = int.to_string();
let hex = match str.strip_prefix("0x") {
None => return Err(syn::Error::new(int.span(), "not a hex integer")),
Some(x) => x,
};
u32::from_str_radix(hex, 16).map_err(|e| syn::Error::new(int.span(), e))
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Opcode {
name: String,
desc: String,
bitmask: u32,
pattern: u32,
modifiers: Vec<String>,
side_effects: Vec<String>,
args: Vec<String>,
defs: Vec<String>,
uses: Vec<String>,
}
fn opcode_to_variant_name(opcode: &LitStr) -> syn::Result<String> {
impl Opcode {
fn variant_identifier(&self) -> syn::Result<TokenTree> {
to_rust_variant(&self.name)
}
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Mnemonic {
name: String,
opcode: String,
args: Vec<String>,
condition: String,
#[serde(rename = "match")]
matcher: Vec<MatchField>,
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct MatchField {
arg: String,
value: u32,
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Modifier {
name: String,
suffix: char,
}
#[derive(Deserialize, Default)]
#[serde(default)]
pub(crate) struct Isa {
fields: Vec<Field>,
opcodes: Vec<Opcode>,
mnemonics: Vec<Mnemonic>,
}
impl Isa {
pub(crate) fn gen_opcode_enum(&self) -> syn::Result<TokenStream> {
// Create enum variants.
let enum_variants = self
.opcodes
.iter()
.map(|opcode| -> syn::Result<TokenStream> {
let ident = opcode.variant_identifier()?;
Ok(quote! {
#ident,
})
})
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?
.into_iter();
let enum_variants = TokenStream::from_iter(enum_variants);
// Create functions.
let mnemonic_fn = self.gen_mnemonic_fn()?;
let detect_fn = self.gen_opcode_detect()?;
// Create final enum.
let opcode_enum = quote! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Opcode {
Illegal = -1,
#enum_variants
}
impl Opcode {
#mnemonic_fn
#detect_fn
}
};
Ok(opcode_enum)
}
fn gen_mnemonic_fn(&self) -> syn::Result<TokenStream> {
// Create match arms.
let match_arms = self
.opcodes
.iter()
.map(|opcode| {
let variant = opcode.variant_identifier()?;
let literal = Literal::string(&opcode.name);
Ok(quote! {
Opcode::#variant => #literal,
})
})
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?
.into_iter();
let match_arms = TokenStream::from_iter(match_arms);
// Create final function.
let mnemonic_fn = quote! {
pub fn mnemonic(self) -> &'static str {
match self {
Opcode::Illegal => "<illegal>",
#match_arms
}
}
};
Ok(mnemonic_fn)
}
pub(crate) fn gen_opcode_detect(&self) -> syn::Result<TokenStream> {
// Generate if chain.
let if_chain = self
.opcodes
.iter()
.map(|opcode| {
let bitmask_str = format!("{:>#8x}", opcode.bitmask);
let bitmask = LitInt::new(&bitmask_str, Span::call_site());
let pattern_str = format!("{:>#8x}", opcode.pattern);
let pattern = LitInt::new(&pattern_str, Span::call_site());
let identifier = opcode.variant_identifier()?;
Ok(quote! {
if code & #bitmask == #pattern {
return Opcode::#identifier;
}
})
})
.try_collect::<TokenStream, Vec<TokenStream>, syn::Error>()?
.into_iter();
let if_chain = TokenStream::from_iter(if_chain);
// Generate function.
let func = quote! {
pub fn detect(code: u32) -> Self {
#if_chain
Opcode::Illegal
}
};
Ok(func)
}
pub(crate) fn gen_field_enum(&self) -> syn::Result<TokenStream> {
// Create enum variants.
let enum_variants = self.fields.iter().map(|field| {
let ident = field.variant_identifier();
let arg = TokenTree::Ident(Ident::new(&field.arg, Span::call_site()));
quote! {
#ident(#arg),
}
});
let enum_variants = TokenStream::from_iter(enum_variants);
// Create final enum.
let field_enum = quote! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Field {
#enum_variants
}
};
Ok(field_enum)
}
pub(crate) fn gen_ins_impl(&self) -> syn::Result<TokenStream> {
// Map fields by name.
let mut field_by_name = HashMap::<String, &Field>::new();
for field in &self.fields {
field_by_name.insert(field.name.clone(), field);
}
// Generate match arms for each opcode.
let mut match_arms = Vec::new();
for opcode in &self.opcodes {
// Generate fields of opcode.
// TODO Support mnemonics.
let mut fields = Vec::new();
for arg in &opcode.args {
let field: &Field = field_by_name.get(arg).ok_or_else(|| {
syn::Error::new(Span::call_site(), format!("undefined field {}", arg))
})?;
let field_variant = field.variant_identifier();
let field_arg = TokenTree::Ident(Ident::new(&field.arg, Span::call_site()));
let value = field.express_value(quote!(self.code));
fields.extend(quote! {
Field::#field_variant(#field_arg(#value as _)),
})
}
let fields = TokenStream::from_iter(fields.into_iter());
// Emit match arm.
let ident = opcode.variant_identifier()?;
match_arms.push(quote! {
#ident => vec![#fields],
})
}
let match_arms = TokenStream::from_iter(match_arms.into_iter());
// Generate final fields function.
let ins_impl = quote! {
impl Ins {
fn _fields(&self) -> Vec<Field> {
match self.op {
Opcode::Illegal => vec![],
#match_arms
_ => todo!()
}
}
}
};
Ok(ins_impl)
}
}
/// Converts the given key into an identifier.
fn to_rust_ident(key: &str) -> TokenTree {
TokenTree::Ident(Ident::new(&key.replace(".", "_"), Span::call_site()))
}
/// Converts the given key into a struct variant key.
fn to_rust_variant(key: &str) -> syn::Result<TokenTree> {
Ok(TokenTree::Ident(Ident::new(
&to_rust_variant_str(key).map_err(|e| syn::Error::new(Span::call_site(), e))?,
Span::call_site(),
)))
}
fn to_rust_variant_str(key: &str) -> Result<String, String> {
let mut s = String::new();
let opcode_value = opcode.value();
let mut chars = opcode_value.chars();
let mut chars = key.chars();
loop {
// Make first char uppercase.
let c = match chars.next() {
None => return Ok(s),
Some(c) => c,
};
match c {
'a'..='z' => s.push(c.to_ascii_uppercase()),
_ => return Err(syn::Error::new(opcode.span(), "invalid opcode name")),
}
s.push(match c {
'a'..='z' => c.to_ascii_uppercase(),
'A'..='Z' => c,
_ => return Err(format!("invalid identifier: {}", key)),
});
loop {
let c = match chars.next() {
None => return Ok(s),
Some(c) => c,
};
match c {
match c.to_ascii_lowercase() {
'0'..='9' | 'a'..='z' => s.push(c),
'_' => break,
'.' => {
s.push('_');
break;
}
_ => {
return Err(syn::Error::new(
opcode.span(),
"invalid character in opcode name",
))
}
_ => return Err(format!("invalid character in opcode name: {}", key)),
}
}
}
}
fn gen_is_valid_fn(tokens: &mut Vec<TokenTree>, opcodes: &Opcodes) {
tokens.extend(quote!(pub fn is_valid(self, code: u32) -> bool));
let mut parts = Vec::<TokenTree>::new();
parts.extend(quote!(match self));
let mut match_parts = Vec::<TokenTree>::new();
match_parts.extend(quote!(Opcode::Illegal => false,));
for opcode in &opcodes.opcodes {
match_parts.extend(quote!(Opcode::));
match_parts.push(TokenTree::Ident(Ident::new(
&opcode.variant_name,
Span::call_site(),
)));
match_parts.extend(quote!(=> code &));
match_parts.push(TokenTree::Literal(Literal::u32_suffixed(opcode.mask)));
match_parts.extend(quote!(==));
match_parts.push(TokenTree::Literal(Literal::u32_suffixed(opcode.bits)));
match_parts.extend(quote!(,));
}
let match_body = Group::new(Delimiter::Brace, TokenStream::from_iter(match_parts));
parts.push(TokenTree::Group(match_body));
let body = Group::new(Delimiter::Brace, TokenStream::from_iter(parts));
tokens.push(TokenTree::Group(body));
}
fn gen_mnemonic_fn(tokens: &mut Vec<TokenTree>, opcodes: &Opcodes) {
tokens.extend(quote!(pub fn mnemonic(self) -> &'static str));
let mut parts = Vec::<TokenTree>::new();
parts.extend(quote!(match self));
let mut match_parts = Vec::<TokenTree>::new();
match_parts.extend(quote!(Opcode::Illegal => "<illegal>",));
for opcode in &opcodes.opcodes {
match_parts.extend(quote!(Opcode::));
match_parts.push(TokenTree::Ident(Ident::new(
&opcode.variant_name,
Span::call_site(),
)));
match_parts.extend(quote!(=>));
match_parts.push(TokenTree::Literal(Literal::string(&opcode.name)));
match_parts.extend(quote!(,));
}
let match_body = Group::new(Delimiter::Brace, TokenStream::from_iter(match_parts));
parts.push(TokenTree::Group(match_body));
let body = Group::new(Delimiter::Brace, TokenStream::from_iter(parts));
tokens.push(TokenTree::Group(body));
}
pub(crate) fn isa(input: TokenStream) -> syn::Result<TokenStream> {
let opcodes: Opcodes = syn::parse2(input)?;
// Assemble root stream.
let mut root = Vec::<TokenTree>::new();
// Define enum derives and header.
root.extend(quote!(#[derive(Debug, Copy, Clone, Eq, PartialEq)]));
root.extend(quote!(pub enum Opcode));
// Create entries.
// First entry is going to be the illegal entry.
let mut enum_entries = Vec::<TokenTree>::new();
enum_entries.extend(quote!(Illegal = -1,));
// Append the actual opcodes.
for opcode in &opcodes.opcodes {
enum_entries.push(TokenTree::Ident(Ident::new(
&opcode.variant_name,
Span::call_site(),
)));
enum_entries.extend(quote!(,));
}
// Create body.
let enum_body = Group::new(Delimiter::Brace, TokenStream::from_iter(enum_entries));
root.push(TokenTree::Group(enum_body));
// impl Opcode block.
root.extend(quote!(impl Opcode));
let mut impl_opcode_body_parts = Vec::<TokenTree>::new();
gen_is_valid_fn(&mut impl_opcode_body_parts, &opcodes);
gen_mnemonic_fn(&mut impl_opcode_body_parts, &opcodes);
let impl_opcode_body = Group::new(
Delimiter::Brace,
TokenStream::from_iter(impl_opcode_body_parts),
);
root.push(TokenTree::Group(impl_opcode_body));
// Extra code.
root.extend(quote! {
impl Default for Opcode {
fn default() -> Self {
Opcode::Illegal
}
}
impl std::string::ToString for Opcode {
fn to_string(&self) -> String {
let mnemonic = self.mnemonic();
mnemonic.to_owned()
}
}
});
Ok(TokenStream::from_iter(root))
}
#[cfg(test)]
mod tests {
use super::*;
fn _opcode_to_variant_name(input: &str) -> String {
opcode_to_variant_name(&LitStr::new(input, proc_macro2::Span::call_site())).unwrap()
}
#[test]
fn test_opcode_to_variant_name() {
assert_eq!(_opcode_to_variant_name("lol_lel."), "LolLel_");
assert_eq!(_opcode_to_variant_name("ps_nmsub"), "PsNmsub");
}
}

View File

@ -1,87 +1,150 @@
fields:
- name: "Rc"
desc: "record"
bits: "31"
# Immediates
- name: "simm"
arg: "Simm"
bits: "16..32"
signed: true
- name: "uimm"
arg: "Uimm"
bits: "16..32"
- name: "offset"
arg: "Offset"
bits: "16..32"
signed: true
# Branch fields
- name: "BO"
arg: "OpaqueU"
bits: "6..11"
- name: "BI"
arg: "OpaqueU"
bits: "11..16"
- name: "BD"
desc: "branch destination"
arg: "BranchDest"
bits: "16..30"
shift_left: 2
- name: "LI"
desc: "branch destination"
arg: "BranchDest"
bits: "6..30"
shift_left: 2
# Shift/rotate type fields
- name: "SH"
arg: "OpaqueU"
desc: "Shift"
bits: "16..21"
- name: "MB"
arg: "OpaqueU"
desc: "Mask start"
bits: "21..26"
- name: "ME"
arg: "OpaqueU"
desc: "Mask stop"
bits: "26..31"
# Branch bits
- name: "AA"
desc: "absolute"
bits: "30"
- name: "LK"
desc: "save to link register"
bits: "31"
# Arithmetic bits
- name: "OE"
desc: "overflow"
bits: "21"
# Registers
- name: "rS"
arg: "GPR"
bits: "6..11"
- name: "rD"
arg: "GPR"
bits: "6..11"
- name: "rA"
arg: "GPR"
bits: "11..16"
- name: "rA.nz"
arg: "GPR"
bits: "11..16"
- name: "rB"
arg: "GPR"
bits: "16..21"
- name: "rC"
arg: "GPR"
bits: "21..26"
- name: "sr"
arg: "SR"
bits: "12..16"
- name: "spr"
arg: "SPR"
bits: "11..21"
split: true
# Floating-point registers
- name: "frS"
arg: "FPR"
bits: "6..11"
- name: "frD"
arg: "FPR"
bits: "6..11"
- name: "frA"
arg: "FPR"
bits: "11..16"
- name: "frB"
arg: "FPR"
bits: "16..21"
- name: "frC"
arg: "FPR"
bits: "21..26"
# Condition registers
# TODO This looks swapped the wrong way around
- name: "crbD"
arg: "CRField"
bits: "6..11"
- name: "crbA"
arg: "CRField"
bits: "11..16"
- name: "crbB"
arg: "CRField"
bits: "16..21"
# Condition register fields
- name: "crfD"
desc: "condition register field"
arg: "CRBit"
bits: "6..9"
- name: "crfS"
desc: "condition register field"
arg: "CRBit"
bits: "11..14"
# Condition register misc
- name: "crm"
desc: "condition register field mask"
arg: "OpaqueU"
bits: "12..20"
- name: "cmpL"
arg: "OpaqueU"
bits: "10"
# Paired single fields
- name: "ps_l"
arg: "GQR"
bits: "17..20"
- name: "ps_W"
arg: "OpaqueU"
bits: "16"
# Misc
- name: "sr"
desc: "Segment register"
bits: "12..16"
- name: "spr"
desc: "Special-purpose register"
- name: "NB"
arg: "OpaqueU"
bits: "16..21"
- name: "tbr"
arg: "OpaqueU"
desc: "Time Base"
bits: "11..21"
split: true
- name: "mtfsf_FM"
arg: "OpaqueU"
desc: "Field Mask for mtfsf"
bits: "7..15"
- name: "mtfsf_IMM"
arg: "OpaqueU"
desc: "Immediate for mtfsfi"
bits: "16..20"
- name: "tw_TO"
arg: "OpaqueU"
desc: "Bitset for tw and twi"
bits: "6..11"
# TODO Add defs/uses for modifiers.
modifiers:
- name: "OE"
suffix: "o"
- name: "Rc"
suffix: "."
- name: "LK"
suffix: "l"
- name: "AA"
suffix: "a"
opcodes:
- name: "add"
@ -972,7 +1035,7 @@ opcodes:
desc: "Move to Condition Register from XER"
bitmask: 0xfc30ffff
pattern: 0x7c000400
args: [ "crfD", "xer" ]
args: [ "crfD" ]
defs: [ "crfD", "xer" ]
- name: "mfcr"
@ -1054,7 +1117,7 @@ opcodes:
bitmask: 0xfe0107fe
pattern: 0xfc00058e
modifier: [ "Rc" ]
args: [ "FM", "frB" ]
args: [ "mtfsf_FM", "frB" ]
uses: [ "frB" ]
- name: "mtfsfi"
@ -1062,7 +1125,7 @@ opcodes:
bitmask: 0xfc7f0ffe
pattern: 0xfc00010c
modifier: [ "Rc" ]
args: [ "crfD", "IMM" ]
args: [ "crfD", "mtfsf_IMM" ]
defs: [ "crfD" ]
- name: "mtmsr"
@ -1194,7 +1257,7 @@ opcodes:
desc: "Paired Single Quantized Load"
bitmask: 0xfc000000
pattern: 0xe0000000
args: [ "frD", "offset", "rA", "W", "ps_l" ]
args: [ "frD", "offset", "rA", "ps_W", "ps_l" ]
defs: [ "frD" ]
uses: [ "rA.nz" ]
@ -1202,7 +1265,7 @@ opcodes:
desc: "Paired Single Quantized Load with Update"
bitmask: 0xfc000000
pattern: 0xe4000000
args: [ "frD", "offset", "rA", "W", "ps_l" ]
args: [ "frD", "offset", "rA", "ps_W", "ps_l" ]
defs: [ "frD", "rA" ]
uses: [ "rA" ]
@ -1210,7 +1273,7 @@ opcodes:
desc: "Paired Single Quantized Load with Update Indexed"
bitmask: 0xfc00007f
pattern: 0x1000004c
args: [ "frD", "rA", "rB", "W", "ps_l" ]
args: [ "frD", "rA", "rB", "ps_W", "ps_l" ]
defs: [ "frD", "rA" ]
uses: [ "rA", "rB" ]
@ -1218,7 +1281,7 @@ opcodes:
desc: "Paired Single Quantized Load Indexed"
bitmask: 0xfc00007f
pattern: 0x1000000c
args: [ "frD", "rA", "rB", "W", "ps_l" ]
args: [ "frD", "rA", "rB", "ps_W", "ps_l" ]
defs: [ "frD" ]
uses: [ "rA.nz", "rB" ]
@ -1226,14 +1289,14 @@ opcodes:
desc: "Paired Single Quantized Store"
bitmask: 0xfc000000
pattern: 0xf0000000
args: [ "frS", "offset", "rA", "W", "ps_l" ]
args: [ "frS", "offset", "rA", "ps_W", "ps_l" ]
uses: [ "frS", "rA.nz" ]
- name: "psq_stu"
desc: "Paired Single Quantized Store with Update"
bitmask: 0xfc000000
pattern: 0xf4000000
args: [ "frS", "offset", "rA", "W", "ps_l" ]
args: [ "frS", "offset", "rA", "ps_W", "ps_l" ]
defs: [ "rA" ]
uses: [ "frS", "rA" ]
@ -1241,7 +1304,7 @@ opcodes:
desc: "Paired Single Quantized Store with Update Indexed"
bitmask: 0xfc00007f
pattern: 0x1000004e
args: [ "frS", "rA", "rB", "W", "ps_l" ]
args: [ "frS", "rA", "rB", "ps_W", "ps_l" ]
defs: [ "rA" ]
uses: [ "frS", "rA", "rB" ]
@ -1249,7 +1312,7 @@ opcodes:
desc: "Paired Single Quantized Store Indexed"
bitmask: 0xfc00007f
pattern: 0x1000000e
args: [ "frS", "rA", "rB", "W", "ps_l" ]
args: [ "frS", "rA", "rB", "ps_W", "ps_l" ]
uses: [ "frS", "rA.nz", "rB" ]
- name: "ps_abs"
@ -1855,14 +1918,14 @@ opcodes:
desc: "Trap Word"
bitmask: 0xfc0007ff
pattern: 0x7c000008
args: [ "TO", "rA", "rB" ]
args: [ "tw_TO", "rA", "rB" ]
uses: [ "rA", "rB" ]
- name: "twi"
desc: "Trap Word Immediate"
bitmask: 0xfc000000
pattern: 0xc0000000
args: [ "TO", "rA", "simm" ]
args: [ "tw_TO", "rA", "simm" ]
uses: [ "rA" ]
- name: "xor"
@ -1909,7 +1972,7 @@ mnemonics:
condition: "S == B"
- name: "nop"
opcode: "ori"
condition:
match:
- arg: "rA"
value: 0
- arg: "rS"
@ -1980,7 +2043,7 @@ mnemonics:
value: 0
- arg: "cmpL"
value: 0
- name: "cmplwi"
- name: "cmplwi"
opcode: "cmpli"
args: [ "rA", "uimm" ]
match:

View File

@ -1,22 +1,66 @@
#![feature(proc_macro_span, proc_macro_def_site)]
mod isa;
mod writer;
//mod writer;
use std::fs::File;
use proc_macro::Span;
use crate::isa::Isa;
#[proc_macro]
pub fn isa(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let output = match crate::isa::isa(input) {
pub fn opcodes(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
let isa = match load_isa() {
Ok(v) => v,
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
Err(err) => return err,
};
proc_macro::TokenStream::from(output)
match isa.gen_opcode_enum() {
Ok(v) => v.into(),
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
}
}
#[proc_macro]
pub fn write_asm(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let output = match crate::writer::write_asm(input) {
pub fn fields(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
let isa = match load_isa() {
Ok(v) => v,
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
Err(err) => return err,
};
proc_macro::TokenStream::from(output)
match isa.gen_field_enum() {
Ok(v) => v.into(),
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
}
}
#[proc_macro]
pub fn ins_impl(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
let isa = match load_isa() {
Ok(v) => v,
Err(err) => return err,
};
match isa.gen_ins_impl() {
Ok(v) => v.into(),
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
}
}
fn load_isa() -> Result<Isa, proc_macro::TokenStream> {
_load_isa().map_err(|err| {
proc_macro::TokenStream::from(
syn::Error::new(Span::def_site().into(), err).to_compile_error(),
)
})
}
fn _load_isa() -> Result<Isa, Box<dyn std::error::Error>> {
// Figure out YAML path.
let def_site = Span::def_site();
let rust_path = def_site.source_file().path();
let yaml_path = rust_path.parent().unwrap().join("isa.yaml");
// Open and deserialize YAML file.
let yaml_file = File::open(yaml_path).map_err(|e| syn::Error::new(def_site.into(), e))?;
let isa: Isa =
serde_yaml::from_reader(yaml_file).map_err(|e| syn::Error::new(def_site.into(), e))?;
Ok(isa)
}