This repository has been archived on 2024-12-14. You can view files and clone it, but cannot push or open issues or pull requests.
rustboyadvance-ng/core/src/arm7tdmi/arm/display.rs
Michel Heily 879374a9b0 Refactor dir rustboyadvance-core -> core
Former-commit-id: 5af970f6d56d321472f2b91885e41ca113390986
Former-commit-id: 748e222a36362eb5ac8909068c32f2d3f98ca536
2020-05-30 13:43:37 +03:00

437 lines
13 KiB
Rust

use std::fmt;
#[cfg(feature = "debugger")]
use crate::bit::BitIndex;
#[cfg(feature = "debugger")]
use super::{ArmFormat, ArmInstruction};
use super::{AluOpCode, ArmCond, ArmHalfwordTransferType};
use crate::arm7tdmi::*;
impl fmt::Display for ArmCond {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArmCond::*;
match self {
EQ => write!(f, "eq"),
NE => write!(f, "ne"),
HS => write!(f, "cs"),
LO => write!(f, "cc"),
MI => write!(f, "mi"),
PL => write!(f, "pl"),
VS => write!(f, "vs"),
VC => write!(f, "vc"),
HI => write!(f, "hi"),
LS => write!(f, "ls"),
GE => write!(f, "ge"),
LT => write!(f, "lt"),
GT => write!(f, "gt"),
LE => write!(f, "le"),
AL => write!(f, ""), // the dissasembly should ignore this
}
}
}
impl fmt::Display for AluOpCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use AluOpCode::*;
match self {
AND => write!(f, "and"),
EOR => write!(f, "eor"),
SUB => write!(f, "sub"),
RSB => write!(f, "rsb"),
ADD => write!(f, "add"),
ADC => write!(f, "adc"),
SBC => write!(f, "sbc"),
RSC => write!(f, "rsc"),
TST => write!(f, "tst"),
TEQ => write!(f, "teq"),
CMP => write!(f, "cmp"),
CMN => write!(f, "cmn"),
ORR => write!(f, "orr"),
MOV => write!(f, "mov"),
BIC => write!(f, "bic"),
MVN => write!(f, "mvn"),
}
}
}
impl fmt::Display for BarrelShiftOpCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use BarrelShiftOpCode::*;
match self {
LSL => write!(f, "lsl"),
LSR => write!(f, "lsr"),
ASR => write!(f, "asr"),
ROR => write!(f, "ror"),
}
}
}
impl fmt::Display for ArmHalfwordTransferType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArmHalfwordTransferType::*;
match self {
UnsignedHalfwords => write!(f, "h"),
SignedHalfwords => write!(f, "sh"),
SignedByte => write!(f, "sb"),
}
}
}
fn is_lsl0(shift: &ShiftedRegister) -> bool {
if let ShiftRegisterBy::ByAmount(val) = shift.shift_by {
return !(val == 0 && shift.bs_op == BarrelShiftOpCode::LSL);
}
true
}
impl fmt::Display for ShiftedRegister {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let reg = reg_string(self.reg).to_string();
if !is_lsl0(&self) {
write!(f, "{}", reg)
} else {
match self.shift_by {
ShiftRegisterBy::ByAmount(imm) => write!(f, "{}, {} #{}", reg, self.bs_op, imm),
ShiftRegisterBy::ByRegister(rs) => {
write!(f, "{}, {} {}", reg, self.bs_op, reg_string(rs))
}
}
}
}
}
#[cfg(feature = "debugger")]
impl ArmInstruction {
fn fmt_bx(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "bx\t{Rn}", Rn = reg_string(self.rn()))
}
fn fmt_branch(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"b{link}{cond}\t{ofs:#x}",
link = if self.link_flag() { "l" } else { "" },
cond = self.cond(),
ofs = 8 + self.pc.wrapping_add(self.branch_offset() as Addr)
)
}
fn set_cond_mark(&self) -> &str {
if self.set_cond_flag() {
"s"
} else {
""
}
}
fn fmt_operand2(&self, f: &mut fmt::Formatter<'_>) -> Result<Option<u32>, fmt::Error> {
let operand2 = self.operand2();
match operand2 {
BarrelShifterValue::RotatedImmediate(_, _) => {
let value = operand2.decode_rotated_immediate().unwrap();
write!(f, "#{}\t; {:#x}", value, value)?;
Ok(Some(value as u32))
}
BarrelShifterValue::ShiftedRegister(shift) => {
write!(f, "{}", shift)?;
Ok(None)
}
_ => panic!("invalid operand2"),
}
}
fn fmt_data_processing(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use AluOpCode::*;
let opcode = self.opcode();
match opcode {
MOV | MVN => write!(
f,
"{opcode}{S}{cond}\t{Rd}, ",
opcode = opcode,
cond = self.cond(),
S = self.set_cond_mark(),
Rd = reg_string(self.rd())
),
CMP | CMN | TEQ | TST => write!(
f,
"{opcode}{cond}\t{Rn}, ",
opcode = opcode,
cond = self.cond(),
Rn = reg_string(self.rn())
),
_ => write!(
f,
"{opcode}{S}{cond}\t{Rd}, {Rn}, ",
opcode = opcode,
cond = self.cond(),
S = self.set_cond_mark(),
Rd = reg_string(self.rd()),
Rn = reg_string(self.rn())
),
}?;
self.fmt_operand2(f).unwrap();
Ok(())
}
fn auto_incremenet_mark(&self) -> &str {
if self.write_back_flag() {
"!"
} else {
""
}
}
fn fmt_rn_offset(&self, f: &mut fmt::Formatter<'_>, offset: BarrelShifterValue) -> fmt::Result {
write!(f, "[{Rn}", Rn = reg_string(self.rn()))?;
let (ofs_string, comment) = match offset {
BarrelShifterValue::ImmediateValue(value) => {
let value_for_commnet = if self.rn() == REG_PC {
value + self.pc + 8 // account for pipelining
} else {
value
};
(
format!("#{}", value),
Some(format!("\t; {:#x}", value_for_commnet)),
)
}
BarrelShifterValue::ShiftedRegister(shift) => (
format!(
"{}{}",
if shift.added.unwrap_or(true) { "" } else { "-" },
shift
),
None,
),
_ => panic!("bad barrel shifter"),
};
if self.pre_index_flag() {
write!(f, ", {}]{}", ofs_string, self.auto_incremenet_mark())?;
} else {
write!(f, "], {}", ofs_string)?;
}
if let Some(comment) = comment {
write!(f, "{}", comment)
} else {
Ok(())
}
}
fn fmt_ldr_str(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{mnem}{B}{T}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" },
B = if self.transfer_size() == 1 { "b" } else { "" },
cond = self.cond(),
T = if !self.pre_index_flag() && self.write_back_flag() {
"t"
} else {
""
},
Rd = reg_string(self.rd()),
)?;
self.fmt_rn_offset(f, self.ldr_str_offset())
}
fn fmt_ldm_stm(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{mnem}{inc_dec}{pre_post}{cond}\t{Rn}{auto_inc}, {{",
mnem = if self.load_flag() { "ldm" } else { "stm" },
inc_dec = if self.add_offset_flag() { 'i' } else { 'd' },
pre_post = if self.pre_index_flag() { 'b' } else { 'a' },
cond = self.cond(),
Rn = reg_string(self.rn()),
auto_inc = if self.write_back_flag() { "!" } else { "" }
)?;
let register_list = self.register_list();
let mut has_first = false;
for i in 0..16 {
if register_list.bit(i) {
if has_first {
write!(f, ", {}", reg_string(i))?;
} else {
write!(f, "{}", reg_string(i))?;
has_first = true;
}
}
}
write!(
f,
"}}{}",
if self.psr_and_force_user_flag() {
"^"
} else {
""
}
)
}
/// MRS - transfer PSR contents to a register
fn fmt_mrs(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"mrs{cond}\t{Rd}, {psr}",
cond = self.cond(),
Rd = reg_string(self.rd()),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }
)
}
/// MSR - transfer register/immediate contents to PSR
fn fmt_msr(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"msr{cond}\t{psr}, ",
cond = self.cond(),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" },
)?;
self.fmt_operand2(f).unwrap();
Ok(())
}
fn fmt_msr_flags(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"msr{cond}\t{psr}, ",
cond = self.cond(),
psr = if self.spsr_flag() { "SPSR_f" } else { "CPSR_f" },
)?;
if let Ok(Some(op)) = self.fmt_operand2(f) {
let psr = RegPSR::new(op & 0xf000_0000);
write!(
f,
"\t; N={} Z={} C={} V={}",
psr.N(),
psr.Z(),
psr.C(),
psr.V()
)?;
}
Ok(())
}
fn fmt_mul_mla(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.accumulate_flag() {
write!(
f,
"mla{S}{cond}\t{Rd}, {Rm}, {Rs}, {Rn}",
S = self.set_cond_mark(),
cond = self.cond(),
Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()),
Rn = reg_string(self.rn()),
)
} else {
write!(
f,
"mul{S}{cond}\t{Rd}, {Rm}, {Rs}",
S = self.set_cond_mark(),
cond = self.cond(),
Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()),
)
}
}
fn sign_mark(&self) -> &str {
if self.u_flag() {
"s"
} else {
"u"
}
}
fn fmt_mull_mlal(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{sign}{mnem}{S}{cond}\t{RdLo}, {RdHi}, {Rm}, {Rs}",
sign = self.sign_mark(),
mnem = if self.accumulate_flag() {
"mlal"
} else {
"mull"
},
S = self.set_cond_mark(),
cond = self.cond(),
RdLo = reg_string(self.rd_lo()),
RdHi = reg_string(self.rd_hi()),
Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()),
)
}
fn fmt_ldr_str_hs(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Ok(transfer_type) = self.halfword_data_transfer_type() {
write!(
f,
"{mnem}{type}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" },
cond = self.cond(),
type = transfer_type,
Rd = reg_string(self.rd()),
)?;
self.fmt_rn_offset(f, self.ldr_str_hs_offset().unwrap())
} else {
write!(f, "<undefined>")
}
}
fn fmt_swi(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"swi{cond}\t#{comm:#x}",
cond = self.cond(),
comm = self.swi_comment()
)
}
fn fmt_swp(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"swp{B}{cond}\t{Rd}, {Rm}, [{Rn}]",
B = if self.transfer_size() == 1 { "b" } else { "" },
cond = self.cond(),
Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()),
Rn = reg_string(self.rn()),
)
}
}
#[cfg(feature = "debugger")]
impl fmt::Display for ArmInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArmFormat::*;
match self.fmt {
BranchExchange => self.fmt_bx(f),
BranchLink => self.fmt_branch(f),
DataProcessing => self.fmt_data_processing(f),
SingleDataTransfer => self.fmt_ldr_str(f),
BlockDataTransfer => self.fmt_ldm_stm(f),
MoveFromStatus => self.fmt_mrs(f),
MoveToStatus => self.fmt_msr(f),
MoveToFlags => self.fmt_msr_flags(f),
Multiply => self.fmt_mul_mla(f),
MultiplyLong => self.fmt_mull_mlal(f),
HalfwordDataTransferImmediateOffset => self.fmt_ldr_str_hs(f),
HalfwordDataTransferRegOffset => self.fmt_ldr_str_hs(f),
SoftwareInterrupt => self.fmt_swi(f),
SingleDataSwap => self.fmt_swp(f),
Undefined => write!(f, "<Undefined>"),
}
}
}