create write_asm macro
This commit is contained in:
parent
0c65617c69
commit
67efa31bbd
|
@ -14,7 +14,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust: [nightly]
|
||||
rust: [stable]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
sccache-path: /home/runner/.cache/sccache
|
||||
|
|
|
@ -80,6 +80,7 @@ name = "ppc750cl-macros"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ impl Fuzzer {
|
|||
fn disasm(x: u32) {
|
||||
let devnull = DevNull;
|
||||
let mut formatter = SimpleFormatter { writer: devnull };
|
||||
let ins = Ins::disasm(x);
|
||||
let ins = Ins::new(x, 0x8000_0000u32);
|
||||
ins.write_string(&mut formatter).unwrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,51 +1,68 @@
|
|||
use crate::Ins;
|
||||
use num_traits::PrimInt;
|
||||
use std::fmt::{LowerHex, UpperHex};
|
||||
use std::fmt::{Display, LowerHex, UpperHex};
|
||||
use std::io::Write;
|
||||
|
||||
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;
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
/// Writes the instruction mnemonic.
|
||||
fn write_mnemonic(&mut self, name: &str) -> IOResult {
|
||||
write!(self.writer(), "{}", name)
|
||||
}
|
||||
|
||||
/// Separates the instruction mnemonic and arguments.
|
||||
fn write_opcode_separator(&mut self) -> IOResult {
|
||||
write!(self.writer(), " ")
|
||||
}
|
||||
|
||||
/// Separates two instruction arguments (e.g. registers).
|
||||
fn write_operand_separator(&mut self) -> IOResult {
|
||||
write!(self.writer(), ", ")
|
||||
}
|
||||
|
||||
/// Writes a general-purpose register argument.
|
||||
fn write_gpr(&mut self, reg: u8) -> IOResult {
|
||||
write!(self.writer(), "r{}", reg)
|
||||
}
|
||||
|
||||
/// Writes a floating point register argument.
|
||||
fn write_fpr(&mut self, reg: u8) -> IOResult {
|
||||
write!(self.writer(), "f{}", reg)
|
||||
}
|
||||
|
||||
/// Writes a condition register argument.
|
||||
fn write_cr(&mut self, reg: u8) -> IOResult {
|
||||
write!(self.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_sr(&mut self, reg: u8) -> IOResult {
|
||||
write!(self.writer(), "{}", reg)
|
||||
}
|
||||
|
||||
/// Sets the mnemonic 'o' suffix.
|
||||
fn write_oe(&mut self, oe: u8) -> IOResult {
|
||||
if oe != 0 {
|
||||
write!(self.writer(), "o")?;
|
||||
|
@ -53,6 +70,7 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the mnemonic 'a' suffix.
|
||||
fn write_aa(&mut self, aa: u8) -> IOResult {
|
||||
if aa != 0 {
|
||||
write!(self.writer(), "a")?;
|
||||
|
@ -60,6 +78,7 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the mnemonic 'l' suffix.
|
||||
fn write_lk(&mut self, lk: u8) -> IOResult {
|
||||
if lk != 0 {
|
||||
write!(self.writer(), "l")?;
|
||||
|
@ -67,6 +86,7 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the mnemonic '.' suffix.
|
||||
fn write_rc(&mut self, rc: u8) -> IOResult {
|
||||
if rc != 0 {
|
||||
write!(self.writer(), ".")?;
|
||||
|
@ -74,14 +94,21 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes an unsigned immediate.
|
||||
fn write_uimm(&mut self, uimm: u16) -> IOResult {
|
||||
write!(self.writer(), "{:#x}", uimm)
|
||||
}
|
||||
|
||||
/// Writes a signed immediate.
|
||||
fn write_simm(&mut self, simm: i16) -> IOResult {
|
||||
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)
|
||||
}
|
||||
|
@ -90,13 +117,24 @@ where
|
|||
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(&mut self, offset: i16) -> IOResult {
|
||||
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: u32, pc: u32) -> IOResult {
|
||||
write!(self.writer(), "{:#x}", offset + pc)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SimpleFormatter<W: Write> {
|
||||
|
|
331
lib/src/isa.rs
331
lib/src/isa.rs
|
@ -1,3 +1,4 @@
|
|||
use crate::{bit, bits};
|
||||
use ppc750cl_macros::isa;
|
||||
|
||||
isa! {
|
||||
|
@ -224,3 +225,333 @@ isa! {
|
|||
"xori" & 0xfc000000 == 0x68000000;
|
||||
"xoris" & 0xfc000000 == 0x6c000000;
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
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, 26..31) {
|
||||
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, 26..31) {
|
||||
0b00001 => Opcode::PsNeg,
|
||||
0b00010 => Opcode::PsMr,
|
||||
0b00100 => Opcode::PsNabs,
|
||||
0b01000 => Opcode::PsAbs,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b10000 => match bits(x, 26..31) {
|
||||
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_1101 => 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_0000_0110 => Opcode::Dcbt,
|
||||
0b01_0000_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_1111_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, 26..31) {
|
||||
0b00 => Opcode::Fcmpu,
|
||||
0b01 => Opcode::Fcmpo,
|
||||
0b10 => Opcode::Mcrfs,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b00110 => match bits(x, 26..31) {
|
||||
0b001 => Opcode::Mtfsb1,
|
||||
0b010 => Opcode::Mtfsb0,
|
||||
0b100 => Opcode::Mtfsfi,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b00111 => match bits(x, 26..31) {
|
||||
0b10010 => Opcode::Mffs,
|
||||
0b10110 => Opcode::Mtfsf,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b01000 => match bits(x, 26..31) {
|
||||
0b0001 => Opcode::Fneg,
|
||||
0b0010 => Opcode::Fabs,
|
||||
0b0100 => Opcode::Fnabs,
|
||||
0b1000 => Opcode::Fmr,
|
||||
_ => 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
568
lib/src/lib.rs
568
lib/src/lib.rs
|
@ -5,6 +5,10 @@ use std::ops::Range;
|
|||
|
||||
use num_traits::AsPrimitive;
|
||||
|
||||
use ppc750cl_macros::write_asm;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod formatter;
|
||||
mod isa;
|
||||
|
||||
|
@ -15,6 +19,7 @@ pub use crate::isa::Opcode;
|
|||
#[derive(Default, Clone)]
|
||||
pub struct Ins {
|
||||
pub code: u32,
|
||||
pub addr: u32,
|
||||
pub op: Opcode,
|
||||
}
|
||||
|
||||
|
@ -33,15 +38,6 @@ where
|
|||
masked.as_()
|
||||
}
|
||||
|
||||
macro_rules! disasm_unreachable {
|
||||
($msg:expr $(,)?) => {{
|
||||
panic!(
|
||||
"internal error: entered unreachable code disassembling instruction 0x{:08x}",
|
||||
$msg
|
||||
)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! ins_bit {
|
||||
($func:ident, $idx:expr) => {
|
||||
fn $func(&self) -> u8 {
|
||||
|
@ -65,12 +61,9 @@ macro_rules! ins_field {
|
|||
}
|
||||
|
||||
impl Ins {
|
||||
fn new(code: u32, op: Opcode) -> Self {
|
||||
Ins { code, op }
|
||||
}
|
||||
|
||||
fn illegal() -> Self {
|
||||
Default::default()
|
||||
pub fn new(code: u32, addr: u32) -> Self {
|
||||
let op = Opcode::from_code(code);
|
||||
Self { code, addr, op }
|
||||
}
|
||||
|
||||
//ins_bit!(w, 21);
|
||||
|
@ -114,344 +107,6 @@ impl Ins {
|
|||
ins_field!(ps_l, u8, 17..20);
|
||||
ins_field!(ps_d, u16, 20..32);
|
||||
|
||||
pub fn disasm(x: u32) -> Self {
|
||||
let family = bits(x, 0..6);
|
||||
let mut ins = match family {
|
||||
0b000011 => Ins::new(x, Opcode::Twi),
|
||||
0b000100 => Self::disasm_cl_ext(x),
|
||||
0b000111..=0b001111 => Self::disasm_basic1(x),
|
||||
0b010000 => Ins::new(x, Opcode::Bc),
|
||||
0b010001 => Ins::new(x, Opcode::Sc),
|
||||
0b010010 => Ins::new(x, Opcode::B),
|
||||
0b010011 => Self::disasm_010011(x),
|
||||
0b010100..=0b011101 => Self::disasm_basic2(x),
|
||||
0b011111 => Self::disasm_011111(x),
|
||||
0b100000..=0b110111 => Self::disasm_basic3(x),
|
||||
0b111000..=0b111001 => Self::disasm_psq(x),
|
||||
0b111011 => Self::disasm_111011(x),
|
||||
0b111100..=0b111101 => Self::disasm_psq(x),
|
||||
0b111111 => Self::disasm_111111(x),
|
||||
_ => Self::illegal(),
|
||||
};
|
||||
if !ins.op.is_valid(x) {
|
||||
ins.op = Opcode::Illegal;
|
||||
}
|
||||
ins
|
||||
}
|
||||
|
||||
fn disasm_cl_ext(x: u32) -> Self {
|
||||
let op = match bits(x, 26..31) {
|
||||
0b00000 => match bits(x, 26..31) {
|
||||
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, 26..31) {
|
||||
0b00001 => Opcode::PsNeg,
|
||||
0b00010 => Opcode::PsMr,
|
||||
0b00100 => Opcode::PsNabs,
|
||||
0b01000 => Opcode::PsAbs,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b10000 => match bits(x, 26..31) {
|
||||
0b10000 => Opcode::PsMerge00,
|
||||
0b10001 => Opcode::PsMerge01,
|
||||
0b10010 => Opcode::PsMerge10,
|
||||
0b10011 => Opcode::PsMerge11,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b10110 => Opcode::DcbzL,
|
||||
// Unknown paired-singles key.
|
||||
_ => Opcode::Illegal,
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_basic1(x: u32) -> Self {
|
||||
let op = 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,
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_010011(x: u32) -> Self {
|
||||
let op = 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,
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_basic2(x: u32) -> Self {
|
||||
let op = 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,
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_011111(x: u32) -> Self {
|
||||
let op = 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_1101 => 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_0000_0110 => Opcode::Dcbt,
|
||||
0b01_0000_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_1111_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,
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_basic3(x: u32) -> Self {
|
||||
let op = 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),
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_psq(x: u32) -> Self {
|
||||
let op = match bits(x, 0..6) {
|
||||
0b111000 => Opcode::PsqL,
|
||||
0b111001 => Opcode::PsqLu,
|
||||
0b111100 => Opcode::PsqSt,
|
||||
0b111101 => Opcode::PsqStu,
|
||||
_ => disasm_unreachable!(x),
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_111011(x: u32) -> Self {
|
||||
let op = 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,
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn disasm_111111(x: u32) -> Self {
|
||||
let op = match bits::<u32>(x, 26..31) {
|
||||
0b00000 => match bits(x, 26..31) {
|
||||
0b00 => Opcode::Fcmpu,
|
||||
0b01 => Opcode::Fcmpo,
|
||||
0b10 => Opcode::Mcrfs,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b00110 => match bits(x, 26..31) {
|
||||
0b001 => Opcode::Mtfsb1,
|
||||
0b010 => Opcode::Mtfsb0,
|
||||
0b100 => Opcode::Mtfsfi,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b00111 => match bits(x, 26..31) {
|
||||
0b10010 => Opcode::Mffs,
|
||||
0b10110 => Opcode::Mtfsf,
|
||||
_ => Opcode::Illegal,
|
||||
},
|
||||
0b01000 => match bits(x, 26..31) {
|
||||
0b0001 => Opcode::Fneg,
|
||||
0b0010 => Opcode::Fabs,
|
||||
0b0100 => Opcode::Fnabs,
|
||||
0b1000 => Opcode::Fmr,
|
||||
_ => 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,
|
||||
};
|
||||
Ins::new(x, op)
|
||||
}
|
||||
|
||||
fn write_asm_form_reg123<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
where
|
||||
F: AsmFormatter<W>,
|
||||
|
@ -769,8 +424,9 @@ impl Ins {
|
|||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_lk(self.lk())?;
|
||||
out.write_aa(self.aa())?;
|
||||
// TODO absolute address
|
||||
write!(out.writer(), "0x{:x}", self.li())
|
||||
out.write_opcode_separator()?;
|
||||
out.write_branch_target(self.li(), self.addr)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_bc<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -781,14 +437,13 @@ impl Ins {
|
|||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_lk(self.lk())?;
|
||||
out.write_aa(self.aa())?;
|
||||
// TODO absolute address
|
||||
write!(
|
||||
out.writer(),
|
||||
"0x{:x}, 0x{:x}, 0x{:x}",
|
||||
self.bo(),
|
||||
self.bi(),
|
||||
self.li()
|
||||
)
|
||||
out.write_opcode_separator()?;
|
||||
write!(out.writer(), "{}", self.bo())?;
|
||||
out.write_operand_separator()?;
|
||||
write!(out.writer(), "{}", self.bi())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_branch_target(self.li(), self.addr)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_branch_cond_to_reg<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -813,13 +468,12 @@ impl Ins {
|
|||
},
|
||||
_ => disasm_unreachable!(self.code),
|
||||
};
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} 0x{:x}, 0x{:x}",
|
||||
name,
|
||||
self.bo(),
|
||||
self.bi()
|
||||
)
|
||||
out.write_mnemonic(name)?;
|
||||
out.write_opcode_separator()?;
|
||||
write!(out.writer(), "{}", self.bo())?;
|
||||
out.write_operand_separator()?;
|
||||
write!(out.writer(), "{}", self.bi())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_cmp<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -827,15 +481,16 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} crf{}, {}, r{}, r{}",
|
||||
self.op.mnemonic(),
|
||||
self.crf_d(),
|
||||
self.l() as u8,
|
||||
self.a(),
|
||||
self.b()
|
||||
)
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_cr(self.crf_d())?;
|
||||
out.write_operand_separator()?;
|
||||
write!(out.writer(), "{}", self.l())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.a())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.b())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_cmp_simm<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -843,15 +498,16 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} crf{}, {}, r{}, {}",
|
||||
self.op.mnemonic(),
|
||||
self.crf_d(),
|
||||
self.l() as u8,
|
||||
self.a(),
|
||||
self.simm()
|
||||
)
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_cr(self.crf_d())?;
|
||||
out.write_operand_separator()?;
|
||||
write!(out.writer(), "{}", self.l())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.a())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_simm(self.simm())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_cmp_uimm<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -859,15 +515,16 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} crf{}, {}, r{}, {}",
|
||||
self.op.mnemonic(),
|
||||
self.crf_d(),
|
||||
self.l() as u8,
|
||||
self.a(),
|
||||
self.uimm()
|
||||
)
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_cr(self.crf_d())?;
|
||||
out.write_operand_separator()?;
|
||||
write!(out.writer(), "{}", self.l())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.a())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_uimm(self.uimm())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_form_condreg1<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -887,7 +544,10 @@ impl Ins {
|
|||
},
|
||||
_ => disasm_unreachable!(self.code),
|
||||
};
|
||||
write!(out.writer(), "{} crf{}", name, self.crf_d())
|
||||
out.write_mnemonic(name)?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_cr(self.crf_d())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_form_condreg12<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -900,13 +560,12 @@ impl Ins {
|
|||
Opcode::Mcrfs => "mcrfs",
|
||||
_ => disasm_unreachable!(self.code),
|
||||
};
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} crf{}, crf{}",
|
||||
name,
|
||||
self.crf_d(),
|
||||
self.crf_s()
|
||||
)
|
||||
out.write_mnemonic(name)?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_cr(self.crf_d())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_cr(self.crf_s())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_form_condreg123<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -914,14 +573,14 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} crb{}, crb{}, crb{}",
|
||||
self.op.mnemonic(),
|
||||
self.crb_d(),
|
||||
self.crb_a(),
|
||||
self.crb_b()
|
||||
)
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_cr(self.crb_d())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_cr(self.crb_a())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_cr(self.crb_b())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_form_reg23<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -929,13 +588,12 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} r{}, r{}",
|
||||
self.op.mnemonic(),
|
||||
self.a(),
|
||||
self.b()
|
||||
)
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_gpr(self.a())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.b())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_form_reg213<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -943,7 +601,6 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
let name_suffix = if self.rc() != 0 { "." } else { "" };
|
||||
let name = match self.op {
|
||||
Opcode::Eqv => "eqv",
|
||||
Opcode::Nand => "nand",
|
||||
|
@ -961,15 +618,15 @@ impl Ins {
|
|||
Opcode::Srw => "srw",
|
||||
_ => disasm_unreachable!(self.code),
|
||||
};
|
||||
write!(
|
||||
out.writer(),
|
||||
"{}{} r{}, r{}, r{}",
|
||||
name,
|
||||
name_suffix,
|
||||
self.a(),
|
||||
self.s(),
|
||||
self.b()
|
||||
)
|
||||
out.write_mnemonic(name)?;
|
||||
out.write_rc(self.rc())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_gpr(self.a())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.s())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.b())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_rlw_imm<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -1065,13 +722,12 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} r{}, {}",
|
||||
self.op.mnemonic(),
|
||||
self.d(),
|
||||
self.sr()
|
||||
)
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_gpr(self.d())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_sr(self.sr())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_form_sr_reg1<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -1079,13 +735,12 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
write!(
|
||||
out.writer(),
|
||||
"{} {}, r{}",
|
||||
self.op.mnemonic(),
|
||||
self.sr(),
|
||||
self.s()
|
||||
)
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_sr(self.sr())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_gpr(self.s())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_asm_mtcrf<F, W>(&self, out: &mut F) -> std::io::Result<()>
|
||||
|
@ -1154,17 +809,14 @@ impl Ins {
|
|||
F: AsmFormatter<W>,
|
||||
W: Write,
|
||||
{
|
||||
out.write_mnemonic(self.op.mnemonic())?;
|
||||
out.write_opcode_separator()?;
|
||||
out.write_fpr(self.d())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_offset_unsigned_open(self.ps_d())?;
|
||||
out.write_gpr(self.a())?;
|
||||
out.write_offset_close()?;
|
||||
out.write_operand_separator()?;
|
||||
write!(out.writer(), "{}", self.w())?;
|
||||
out.write_operand_separator()?;
|
||||
out.write_qr(self.ps_l())?;
|
||||
write_asm!(out, self => {
|
||||
(op.mnemonic, rc, oe) -> mnemonic;
|
||||
(d) -> fpr;
|
||||
(ps_d) -> offset_unsigned;
|
||||
(a) -> gpr;
|
||||
(w) -> mode;
|
||||
(ps_l) -> qr;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1446,7 +1098,7 @@ mod tests {
|
|||
fn test_opcodes() {
|
||||
macro_rules! assert_op {
|
||||
($code:expr, $op:expr) => {{
|
||||
assert_eq!(Ins::disasm($code).op, $op)
|
||||
assert_eq!(Ins::new($code, 0x8000_0000u32).op, $op)
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -1469,10 +1121,10 @@ mod tests {
|
|||
fn test_to_string() {
|
||||
macro_rules! assert_asm {
|
||||
($code:expr, $disasm:expr) => {{
|
||||
assert_eq!(Ins::disasm($code).to_string(), $disasm)
|
||||
assert_eq!(Ins::new($code, 0x8000_0000u32).to_string(), $disasm)
|
||||
}};
|
||||
}
|
||||
assert_asm!(0x4c000000, "mcrf crf0, crf0");
|
||||
assert_asm!(0x4c000000, "mcrf cr0, cr0");
|
||||
assert_asm!(0x7c000278, "xor r0, r0, r0");
|
||||
assert_asm!(0x10000014, "ps_sum0 f0, f0, f0, f0");
|
||||
assert_asm!(0x10000032, "ps_mul f0, f0, f0");
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
macro_rules! disasm_unreachable {
|
||||
($msg:expr $(,)?) => {{
|
||||
panic!(
|
||||
"internal error: entered unreachable code disassembling instruction 0x{:08x}",
|
||||
$msg
|
||||
)
|
||||
}};
|
||||
}
|
|
@ -12,4 +12,5 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.28"
|
||||
syn = { version = "1.0.74", features = ["full"] }
|
||||
quote = "1.0.9"
|
||||
syn = { version = "1.0.74", features = ["full", "parsing"] }
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
use proc_macro::{quote, Delimiter, Group, Ident, Literal, Span, TokenStream, TokenTree};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use proc_macro2::{Delimiter, Group, Ident, Literal, Span, TokenStream, TokenTree};
|
||||
use quote::quote;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::{And, EqEq};
|
||||
use syn::{LitInt, LitStr, Token};
|
||||
use syn::token::{And, EqEq, Semi};
|
||||
use syn::{LitInt, LitStr};
|
||||
|
||||
struct Opcodes {
|
||||
opcodes: Punctuated<Opcode, Token![;]>,
|
||||
opcodes: Punctuated<Opcode, Semi>,
|
||||
}
|
||||
|
||||
impl Parse for Opcodes {
|
||||
|
@ -140,22 +142,20 @@ fn gen_mnemonic_fn(tokens: &mut Vec<TokenTree>, opcodes: &Opcodes) {
|
|||
tokens.push(TokenTree::Group(body));
|
||||
}
|
||||
|
||||
pub(crate) fn isa(input: TokenStream) -> TokenStream {
|
||||
let opcodes = syn::parse_macro_input!(input as Opcodes);
|
||||
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.
|
||||
let derives = quote!(#[derive(Debug, Copy, Clone, Eq, PartialEq)]);
|
||||
root.append(&mut derives.into_iter().collect());
|
||||
let enum_header = quote!(pub enum Opcode);
|
||||
root.append(&mut enum_header.into_iter().collect());
|
||||
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.append(&mut (quote!(Illegal = -1,).into_iter().collect()));
|
||||
enum_entries.extend(quote!(Illegal = -1,));
|
||||
// Append the actual opcodes.
|
||||
for opcode in &opcodes.opcodes {
|
||||
enum_entries.push(TokenTree::Ident(Ident::new(
|
||||
|
@ -169,19 +169,8 @@ pub(crate) fn isa(input: TokenStream) -> TokenStream {
|
|||
let enum_body = Group::new(Delimiter::Brace, TokenStream::from_iter(enum_entries));
|
||||
root.push(TokenTree::Group(enum_body));
|
||||
|
||||
// Default implementation.
|
||||
let opcode_default = quote! {
|
||||
impl Default for Opcode {
|
||||
fn default() -> Self {
|
||||
Opcode::Illegal
|
||||
}
|
||||
}
|
||||
};
|
||||
root.append(&mut opcode_default.into_iter().collect());
|
||||
|
||||
// impl Opcode block.
|
||||
let impl_opcode_header = quote!(impl Opcode);
|
||||
root.append(&mut impl_opcode_header.into_iter().collect());
|
||||
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);
|
||||
|
@ -191,21 +180,25 @@ pub(crate) fn isa(input: TokenStream) -> TokenStream {
|
|||
);
|
||||
root.push(TokenTree::Group(impl_opcode_body));
|
||||
|
||||
// impl ToString block.
|
||||
let to_string_trait_impl = quote! {
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
};
|
||||
root.append(&mut to_string_trait_impl.into_iter().collect());
|
||||
});
|
||||
|
||||
TokenStream::from_iter(root)
|
||||
Ok(TokenStream::from_iter(root))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
#![feature(proc_macro_quote)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[macro_use]
|
||||
mod isa;
|
||||
mod writer;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn isa(input: TokenStream) -> TokenStream {
|
||||
crate::isa::isa(input)
|
||||
pub fn isa(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = proc_macro2::TokenStream::from(input);
|
||||
let output = match crate::isa::isa(input) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
|
||||
};
|
||||
proc_macro::TokenStream::from(output)
|
||||
}
|
||||
|
||||
#[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) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
|
||||
};
|
||||
proc_macro::TokenStream::from(output)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
use std::iter::FromIterator;
|
||||
use std::string::ToString;
|
||||
|
||||
use proc_macro2::{TokenStream, TokenTree};
|
||||
use quote::quote;
|
||||
use quote::ToTokens;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::Semi;
|
||||
use syn::{Expr, Ident};
|
||||
|
||||
struct Arguments {
|
||||
formatter: Expr,
|
||||
ins: Expr,
|
||||
args: Punctuated<Argument, Semi>,
|
||||
}
|
||||
|
||||
impl Parse for Arguments {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let formatter = input.parse()?;
|
||||
input.parse::<syn::token::Comma>()?;
|
||||
let ins = input.parse()?;
|
||||
input.parse::<syn::token::FatArrow>()?;
|
||||
let content;
|
||||
syn::braced!(content in input);
|
||||
let args = Punctuated::parse_terminated(&content)?;
|
||||
Ok(Self {
|
||||
formatter,
|
||||
ins,
|
||||
args,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Argument {
|
||||
sources: Punctuated<Expr, syn::token::Comma>,
|
||||
target: Ident,
|
||||
}
|
||||
|
||||
impl Parse for Argument {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
syn::parenthesized!(content in input);
|
||||
let sources = content.parse_terminated(Expr::parse)?;
|
||||
input.parse::<syn::token::RArrow>()?;
|
||||
let target = input.parse()?;
|
||||
Ok(Self { sources, target })
|
||||
}
|
||||
}
|
||||
|
||||
impl Arguments {
|
||||
fn format_mnemonic(&self, tokens: &mut Vec<TokenTree>) {
|
||||
let arg = &self.args[0];
|
||||
assert!(!arg.sources.is_empty());
|
||||
self.format_call(tokens, &arg.target, self.ins_call(&arg.sources[0]))
|
||||
}
|
||||
|
||||
fn format_call(&self, tokens: &mut Vec<TokenTree>, method_arg: &Ident, args: TokenStream) {
|
||||
let method_name = format!("write_{}", method_arg.to_string());
|
||||
let method_name = Ident::new(&method_name, method_arg.span());
|
||||
let formatter = &self.formatter;
|
||||
tokens.extend(quote!(#formatter.#method_name(#args)?;))
|
||||
}
|
||||
|
||||
fn ins_call(&self, call: &Expr) -> TokenStream {
|
||||
let ins = &self.ins;
|
||||
quote!(#ins.#call())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write_asm(input: TokenStream) -> syn::Result<TokenStream> {
|
||||
let arguments: Arguments = syn::parse2(input)?;
|
||||
assert!(!arguments.args.is_empty());
|
||||
|
||||
let mut tokens = Vec::<TokenTree>::new();
|
||||
arguments.format_mnemonic(&mut tokens);
|
||||
let mut offset_open = false;
|
||||
for (i, arg) in arguments.args.iter().enumerate().skip(1) {
|
||||
// Separate operands from one another unless the last one was an offset.
|
||||
if !offset_open {
|
||||
if i == 1 {
|
||||
arguments.format_call(
|
||||
&mut tokens,
|
||||
&Ident::new("opcode_separator", arg.target.span()),
|
||||
quote!(),
|
||||
);
|
||||
} else {
|
||||
arguments.format_call(
|
||||
&mut tokens,
|
||||
&Ident::new("operand_separator", arg.target.span()),
|
||||
quote!(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Arguments to out.write_x(...);
|
||||
let format_args = arg.sources.iter().map(|src| arguments.ins_call(src));
|
||||
let format_args_punct: Punctuated<TokenStream, syn::token::Comma> =
|
||||
Punctuated::from_iter(format_args);
|
||||
// Create call.
|
||||
if arg.target.to_string().starts_with("offset") {
|
||||
// Offsets are a special case since we need to call close afterwards.
|
||||
if offset_open {
|
||||
return Err(syn::Error::new(
|
||||
arg.target.span(),
|
||||
"two consecutive offset arguments",
|
||||
));
|
||||
}
|
||||
arguments.format_call(
|
||||
&mut tokens,
|
||||
&Ident::new(&(arg.target.to_string() + "_open"), arg.target.span()),
|
||||
format_args_punct.to_token_stream(),
|
||||
);
|
||||
offset_open = true;
|
||||
} else {
|
||||
arguments.format_call(
|
||||
&mut tokens,
|
||||
&arg.target,
|
||||
format_args_punct.to_token_stream(),
|
||||
);
|
||||
if offset_open {
|
||||
arguments.format_call(
|
||||
&mut tokens,
|
||||
&Ident::new("offset_close", arg.target.span()),
|
||||
quote!(),
|
||||
);
|
||||
offset_open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TokenStream::from_iter(tokens.into_iter()))
|
||||
}
|
|
@ -12,7 +12,7 @@ fn main() {
|
|||
let stream = BufWriter::with_capacity(1_000_000, stdout_lock);
|
||||
let mut formatter = SimpleFormatter { writer: stream };
|
||||
loop {
|
||||
let ins = Ins::disasm(rng.next_u32());
|
||||
let ins = Ins::new(rng.next_u32(), 0);
|
||||
if ins.op == Opcode::Illegal {
|
||||
continue;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue