ppc750cl/disasm/src/formatter.rs

226 lines
6.2 KiB
Rust

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 nullable general-purpose register argument.
fn write_gpr0(&mut self, reg: u8) -> IOResult {
if reg != 0 {
self.write_gpr(reg)
} else {
write!(self.writer(), "0")
}
}
/// 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")?;
}
Ok(())
}
/// Sets the mnemonic 'a' suffix.
fn write_aa(&mut self, aa: u8) -> IOResult {
if aa != 0 {
write!(self.writer(), "a")?;
}
Ok(())
}
/// Sets the mnemonic 'l' suffix.
fn write_lk(&mut self, lk: u8) -> IOResult {
if lk != 0 {
write!(self.writer(), "l")?;
}
Ok(())
}
/// Sets the mnemonic '.' suffix.
fn write_rc(&mut self, rc: u8) -> IOResult {
if rc != 0 {
write!(self.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> 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,
"/* TODO */ {:X} {:X} {:X} {:X}\t",
(ins.code >> 24) as u8,
(ins.code >> 16) as u8,
(ins.code >> 8) as u8,
ins.code as u8
)
}
}
// https://stackoverflow.com/questions/44711012/how-do-i-format-a-signed-integer-to-a-sign-aware-hexadecimal-representation
struct ReallySigned<N: PrimInt>(N);
impl<N: PrimInt> LowerHex for ReallySigned<N> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let num = self.0.to_i32().unwrap();
let prefix = if f.alternate() { "0x" } else { "" };
let bare_hex = format!("{:x}", num.abs());
f.pad_integral(num >= 0, prefix, &bare_hex)
}
}
impl<N: PrimInt> UpperHex for ReallySigned<N> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let num = self.0.to_i32().unwrap();
let prefix = if f.alternate() { "0x" } else { "" };
let bare_hex = format!("{:X}", num.abs());
f.pad_integral(num >= 0, prefix, &bare_hex)
}
}