mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-12 14:46:17 +00:00
Update ppc750cl (10x faster!) & upgrade deps
This commit is contained in:
@@ -499,12 +499,12 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
|
||||
executor.push(entry_addr, VM::new(), false);
|
||||
let result = executor.run(
|
||||
obj,
|
||||
|ExecCbData { executor, vm, result, ins_addr: _, section: _, ins, block_start: _ }| {
|
||||
|ExecCbData { executor, vm, result, ins_addr, section: _, ins: _, block_start: _ }| {
|
||||
match result {
|
||||
StepResult::Continue | StepResult::LoadStore { .. } => {
|
||||
return Ok(ExecCbResult::Continue);
|
||||
}
|
||||
StepResult::Illegal => bail!("Illegal instruction @ {:#010X}", ins.addr),
|
||||
StepResult::Illegal => bail!("Illegal instruction @ {}", ins_addr),
|
||||
StepResult::Jump(target) => {
|
||||
if let BranchTarget::Address(RelocationTarget::Address(addr)) = target {
|
||||
return Ok(ExecCbResult::Jump(addr));
|
||||
@@ -558,10 +558,10 @@ pub fn locate_bss_memsets(obj: &mut ObjInfo) -> Result<Vec<(u32, u32)>> {
|
||||
executor.push(entry_addr, VM::new(), false);
|
||||
executor.run(
|
||||
obj,
|
||||
|ExecCbData { executor: _, vm, result, ins_addr: _, section: _, ins, block_start: _ }| {
|
||||
|ExecCbData { executor: _, vm, result, ins_addr, section: _, ins: _, block_start: _ }| {
|
||||
match result {
|
||||
StepResult::Continue | StepResult::LoadStore { .. } => Ok(ExecCbResult::Continue),
|
||||
StepResult::Illegal => bail!("Illegal instruction @ {:#010X}", ins.addr),
|
||||
StepResult::Illegal => bail!("Illegal instruction @ {}", ins_addr),
|
||||
StepResult::Jump(_target) => Ok(ExecCbResult::End(())),
|
||||
StepResult::Branch(branches) => {
|
||||
for branch in branches {
|
||||
|
||||
@@ -19,7 +19,7 @@ pub mod tracker;
|
||||
pub mod vm;
|
||||
|
||||
pub fn disassemble(section: &ObjSection, address: u32) -> Option<Ins> {
|
||||
read_u32(section, address).map(|code| Ins::new(code, address))
|
||||
read_u32(section, address).map(Ins::new)
|
||||
}
|
||||
|
||||
pub fn read_u32(section: &ObjSection, address: u32) -> Option<u32> {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{btree_map, BTreeMap, BTreeSet},
|
||||
ops::Range,
|
||||
};
|
||||
@@ -43,54 +44,64 @@ pub enum TailCallResult {
|
||||
|
||||
type BlockRange = Range<SectionAddress>;
|
||||
|
||||
#[inline(always)]
|
||||
fn next_ins(section: &ObjSection, ins: &Ins) -> Option<Ins> { disassemble(section, ins.addr + 4) }
|
||||
|
||||
type InsCheck = dyn Fn(&Ins) -> bool;
|
||||
|
||||
#[inline(always)]
|
||||
fn check_sequence(
|
||||
section: &ObjSection,
|
||||
ins: &Ins,
|
||||
addr: SectionAddress,
|
||||
ins: Option<&Ins>,
|
||||
sequence: &[(&InsCheck, &InsCheck)],
|
||||
) -> Result<bool> {
|
||||
let mut found = false;
|
||||
|
||||
for &(first, second) in sequence {
|
||||
if first(ins) {
|
||||
if let Some(next) = next_ins(section, ins) {
|
||||
if second(&next)
|
||||
// Also check the following instruction, in case the scheduler
|
||||
// put something in between.
|
||||
|| (!next.is_branch() && matches!(next_ins(section, &next), Some(ins) if second(&ins)))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let Some(ins) =
|
||||
ins.map(Cow::Borrowed).or_else(|| disassemble(section, addr.address).map(Cow::Owned))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if !first(&ins) {
|
||||
continue;
|
||||
}
|
||||
let Some(next) = disassemble(section, addr.address + 4) else {
|
||||
continue;
|
||||
};
|
||||
if second(&next)
|
||||
// Also check the following instruction, in case the scheduler
|
||||
// put something in between.
|
||||
|| (!next.is_branch()
|
||||
&& matches!(disassemble(section, addr.address + 8), Some(ins) if second(&ins)))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(found)
|
||||
}
|
||||
|
||||
fn check_prologue_sequence(section: &ObjSection, ins: &Ins) -> Result<bool> {
|
||||
fn check_prologue_sequence(
|
||||
section: &ObjSection,
|
||||
addr: SectionAddress,
|
||||
ins: Option<&Ins>,
|
||||
) -> Result<bool> {
|
||||
#[inline(always)]
|
||||
fn is_mflr(ins: &Ins) -> bool {
|
||||
// mfspr r0, LR
|
||||
ins.op == Opcode::Mfspr && ins.field_rD() == 0 && ins.field_spr() == 8
|
||||
ins.op == Opcode::Mfspr && ins.field_rd() == 0 && ins.field_spr() == 8
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_stwu(ins: &Ins) -> bool {
|
||||
// stwu r1, d(r1)
|
||||
ins.op == Opcode::Stwu && ins.field_rS() == 1 && ins.field_rA() == 1
|
||||
ins.op == Opcode::Stwu && ins.field_rs() == 1 && ins.field_ra() == 1
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_stw(ins: &Ins) -> bool {
|
||||
// stw r0, d(r1)
|
||||
ins.op == Opcode::Stw && ins.field_rS() == 0 && ins.field_rA() == 1
|
||||
ins.op == Opcode::Stw && ins.field_rs() == 0 && ins.field_ra() == 1
|
||||
}
|
||||
check_sequence(section, ins, &[(&is_stwu, &is_mflr), (&is_mflr, &is_stw)])
|
||||
check_sequence(section, addr, ins, &[(&is_stwu, &is_mflr), (&is_mflr, &is_stw)])
|
||||
}
|
||||
|
||||
impl FunctionSlices {
|
||||
@@ -132,14 +143,14 @@ impl FunctionSlices {
|
||||
#[inline(always)]
|
||||
fn is_lwz(ins: &Ins) -> bool {
|
||||
// lwz r1, d(r)
|
||||
ins.op == Opcode::Lwz && ins.field_rD() == 1
|
||||
ins.op == Opcode::Lwz && ins.field_rd() == 1
|
||||
}
|
||||
|
||||
if is_lwz(ins) {
|
||||
self.has_r1_load = true;
|
||||
return Ok(()); // Possibly instead of a prologue
|
||||
}
|
||||
if check_prologue_sequence(section, ins)? {
|
||||
if check_prologue_sequence(section, addr, Some(ins))? {
|
||||
if let Some(prologue) = self.prologue {
|
||||
if prologue != addr && prologue != addr - 4 {
|
||||
bail!("Found duplicate prologue: {:#010X} and {:#010X}", prologue, addr)
|
||||
@@ -160,20 +171,20 @@ impl FunctionSlices {
|
||||
#[inline(always)]
|
||||
fn is_mtlr(ins: &Ins) -> bool {
|
||||
// mtspr LR, r0
|
||||
ins.op == Opcode::Mtspr && ins.field_rS() == 0 && ins.field_spr() == 8
|
||||
ins.op == Opcode::Mtspr && ins.field_rs() == 0 && ins.field_spr() == 8
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_addi(ins: &Ins) -> bool {
|
||||
// addi r1, r1, SIMM
|
||||
ins.op == Opcode::Addi && ins.field_rD() == 1 && ins.field_rA() == 1
|
||||
ins.op == Opcode::Addi && ins.field_rd() == 1 && ins.field_ra() == 1
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_or(ins: &Ins) -> bool {
|
||||
// or r1, rA, rB
|
||||
ins.op == Opcode::Or && ins.field_rD() == 1
|
||||
ins.op == Opcode::Or && ins.field_rd() == 1
|
||||
}
|
||||
|
||||
if check_sequence(section, ins, &[(&is_mtlr, &is_addi), (&is_or, &is_mtlr)])? {
|
||||
if check_sequence(section, addr, Some(ins), &[(&is_mtlr, &is_addi), (&is_or, &is_mtlr)])? {
|
||||
if let Some(epilogue) = self.epilogue {
|
||||
if epilogue != addr {
|
||||
bail!("Found duplicate epilogue: {:#010X} and {:#010X}", epilogue, addr)
|
||||
@@ -513,11 +524,12 @@ impl FunctionSlices {
|
||||
{
|
||||
// FIXME this is real bad
|
||||
if !self.has_conditional_blr {
|
||||
if let Some(ins) = disassemble(section, end.address - 4) {
|
||||
let ins_addr = end - 4;
|
||||
if let Some(ins) = disassemble(section, ins_addr.address) {
|
||||
if ins.op == Opcode::B {
|
||||
if let Some(RelocationTarget::Address(target)) = ins
|
||||
.branch_dest()
|
||||
.and_then(|addr| section_address_for(obj, end - 4, addr))
|
||||
.branch_dest(ins_addr.address)
|
||||
.and_then(|addr| section_address_for(obj, ins_addr, addr))
|
||||
{
|
||||
if self.function_references.contains(&target) {
|
||||
for branches in self.branches.values() {
|
||||
@@ -617,8 +629,7 @@ impl FunctionSlices {
|
||||
if self.prologue.is_none() {
|
||||
let mut current_address = function_start;
|
||||
while current_address < addr {
|
||||
let ins = disassemble(target_section, current_address.address).unwrap();
|
||||
match check_prologue_sequence(target_section, &ins) {
|
||||
match check_prologue_sequence(target_section, current_address, None) {
|
||||
Ok(true) => {
|
||||
log::debug!(
|
||||
"Prologue discovered @ {}; known tail call: {}",
|
||||
@@ -703,7 +714,7 @@ impl FunctionSlices {
|
||||
|
||||
#[inline]
|
||||
fn is_conditional_blr(ins: &Ins) -> bool {
|
||||
ins.op == Opcode::Bclr && ins.field_BO() & 0b10100 != 0b10100
|
||||
ins.op == Opcode::Bclr && ins.field_bo() & 0b10100 != 0b10100
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -219,8 +219,8 @@ impl Tracker {
|
||||
match ins.op {
|
||||
// addi rD, rA, SIMM
|
||||
Opcode::Addi | Opcode::Addic | Opcode::Addic_ => {
|
||||
let source = ins.field_rA();
|
||||
let target = ins.field_rD();
|
||||
let source = ins.field_ra() as usize;
|
||||
let target = ins.field_rd() as usize;
|
||||
if let Some(value) = self.gpr_address(obj, ins_addr, &vm.gpr[target].value)
|
||||
{
|
||||
if (source == 2
|
||||
@@ -258,7 +258,7 @@ impl Tracker {
|
||||
}
|
||||
// ori rA, rS, UIMM
|
||||
Opcode::Ori => {
|
||||
let target = ins.field_rA();
|
||||
let target = ins.field_ra() as usize;
|
||||
if let Some(value) = self.gpr_address(obj, ins_addr, &vm.gpr[target].value)
|
||||
{
|
||||
if let (Some(hi_addr), Some(lo_addr)) =
|
||||
|
||||
@@ -199,8 +199,8 @@ impl VM {
|
||||
}
|
||||
// add rD, rA, rB
|
||||
Opcode::Add => {
|
||||
let left = self.gpr[ins.field_rA()].value;
|
||||
let right = self.gpr[ins.field_rB()].value;
|
||||
let left = self.gpr[ins.field_ra() as usize].value;
|
||||
let right = self.gpr[ins.field_rb() as usize].value;
|
||||
let value = match (left, right) {
|
||||
(GprValue::Constant(left), GprValue::Constant(right)) => {
|
||||
GprValue::Constant(left.wrapping_add(right))
|
||||
@@ -215,20 +215,20 @@ impl VM {
|
||||
) => GprValue::Address(RelocationTarget::Address(right + left)),
|
||||
_ => GprValue::Unknown,
|
||||
};
|
||||
self.gpr[ins.field_rD()].set_direct(value);
|
||||
self.gpr[ins.field_rd() as usize].set_direct(value);
|
||||
}
|
||||
// addis rD, rA, SIMM
|
||||
Opcode::Addis => {
|
||||
if let Some(target) =
|
||||
relocation_target_for(obj, ins_addr, None /* TODO */).ok().flatten()
|
||||
{
|
||||
debug_assert_eq!(ins.field_rA(), 0);
|
||||
self.gpr[ins.field_rD()].set_hi(GprValue::Address(target), ins_addr);
|
||||
debug_assert_eq!(ins.field_ra(), 0);
|
||||
self.gpr[ins.field_rd() as usize].set_hi(GprValue::Address(target), ins_addr);
|
||||
} else {
|
||||
let left = if ins.field_rA() == 0 {
|
||||
let left = if ins.field_ra() == 0 {
|
||||
GprValue::Constant(0)
|
||||
} else {
|
||||
self.gpr[ins.field_rA()].value
|
||||
self.gpr[ins.field_ra() as usize].value
|
||||
};
|
||||
let value = match left {
|
||||
GprValue::Constant(value) => {
|
||||
@@ -236,11 +236,11 @@ impl VM {
|
||||
}
|
||||
_ => GprValue::Unknown,
|
||||
};
|
||||
if ins.field_rA() == 0 {
|
||||
if ins.field_ra() == 0 {
|
||||
// lis rD, SIMM
|
||||
self.gpr[ins.field_rD()].set_hi(value, ins_addr);
|
||||
self.gpr[ins.field_rd() as usize].set_hi(value, ins_addr);
|
||||
} else {
|
||||
self.gpr[ins.field_rD()].set_direct(value);
|
||||
self.gpr[ins.field_rd() as usize].set_direct(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,16 +251,16 @@ impl VM {
|
||||
if let Some(target) =
|
||||
relocation_target_for(obj, ins_addr, None /* TODO */).ok().flatten()
|
||||
{
|
||||
self.gpr[ins.field_rD()].set_lo(
|
||||
self.gpr[ins.field_rd() as usize].set_lo(
|
||||
GprValue::Address(target),
|
||||
ins_addr,
|
||||
self.gpr[ins.field_rA()],
|
||||
self.gpr[ins.field_ra() as usize],
|
||||
);
|
||||
} else {
|
||||
let left = if ins.field_rA() == 0 && ins.op == Opcode::Addi {
|
||||
let left = if ins.field_ra() == 0 && ins.op == Opcode::Addi {
|
||||
GprValue::Constant(0)
|
||||
} else {
|
||||
self.gpr[ins.field_rA()].value
|
||||
self.gpr[ins.field_ra() as usize].value
|
||||
};
|
||||
let value = match left {
|
||||
GprValue::Constant(value) => {
|
||||
@@ -271,19 +271,26 @@ impl VM {
|
||||
),
|
||||
_ => GprValue::Unknown,
|
||||
};
|
||||
if ins.field_rA() == 0 {
|
||||
if ins.field_ra() == 0 {
|
||||
// li rD, SIMM
|
||||
self.gpr[ins.field_rD()].set_direct(value);
|
||||
self.gpr[ins.field_rd() as usize].set_direct(value);
|
||||
} else {
|
||||
self.gpr[ins.field_rD()].set_lo(value, ins_addr, self.gpr[ins.field_rA()]);
|
||||
self.gpr[ins.field_rd() as usize].set_lo(
|
||||
value,
|
||||
ins_addr,
|
||||
self.gpr[ins.field_ra() as usize],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// subf rD, rA, rB
|
||||
// subfc rD, rA, rB
|
||||
Opcode::Subf | Opcode::Subfc => {
|
||||
self.gpr[ins.field_rD()].set_direct(
|
||||
match (self.gpr[ins.field_rA()].value, self.gpr[ins.field_rB()].value) {
|
||||
self.gpr[ins.field_rd() as usize].set_direct(
|
||||
match (
|
||||
self.gpr[ins.field_ra() as usize].value,
|
||||
self.gpr[ins.field_rb() as usize].value,
|
||||
) {
|
||||
(GprValue::Constant(left), GprValue::Constant(right)) => {
|
||||
GprValue::Constant((!left).wrapping_add(right).wrapping_add(1))
|
||||
}
|
||||
@@ -293,48 +300,54 @@ impl VM {
|
||||
}
|
||||
// subfic rD, rA, SIMM
|
||||
Opcode::Subfic => {
|
||||
self.gpr[ins.field_rD()].set_direct(match self.gpr[ins.field_rA()].value {
|
||||
GprValue::Constant(value) => GprValue::Constant(
|
||||
(!value).wrapping_add(ins.field_simm() as u32).wrapping_add(1),
|
||||
),
|
||||
_ => GprValue::Unknown,
|
||||
});
|
||||
self.gpr[ins.field_rd() as usize].set_direct(
|
||||
match self.gpr[ins.field_ra() as usize].value {
|
||||
GprValue::Constant(value) => GprValue::Constant(
|
||||
(!value).wrapping_add(ins.field_simm() as u32).wrapping_add(1),
|
||||
),
|
||||
_ => GprValue::Unknown,
|
||||
},
|
||||
);
|
||||
}
|
||||
// ori rA, rS, UIMM
|
||||
Opcode::Ori => {
|
||||
if let Some(target) =
|
||||
relocation_target_for(obj, ins_addr, None /* TODO */).ok().flatten()
|
||||
{
|
||||
self.gpr[ins.field_rA()].set_lo(
|
||||
self.gpr[ins.field_ra() as usize].set_lo(
|
||||
GprValue::Address(target),
|
||||
ins_addr,
|
||||
self.gpr[ins.field_rS()],
|
||||
self.gpr[ins.field_rs() as usize],
|
||||
);
|
||||
} else {
|
||||
let value = match self.gpr[ins.field_rS()].value {
|
||||
let value = match self.gpr[ins.field_rs() as usize].value {
|
||||
GprValue::Constant(value) => {
|
||||
GprValue::Constant(value | ins.field_uimm() as u32)
|
||||
}
|
||||
_ => GprValue::Unknown,
|
||||
};
|
||||
self.gpr[ins.field_rA()].set_lo(value, ins_addr, self.gpr[ins.field_rS()]);
|
||||
self.gpr[ins.field_ra() as usize].set_lo(
|
||||
value,
|
||||
ins_addr,
|
||||
self.gpr[ins.field_rs() as usize],
|
||||
);
|
||||
}
|
||||
}
|
||||
// or rA, rS, rB
|
||||
Opcode::Or => {
|
||||
if ins.field_rS() == ins.field_rB() {
|
||||
if ins.field_rs() == ins.field_rb() {
|
||||
// Register copy
|
||||
self.gpr[ins.field_rA()] = self.gpr[ins.field_rS()];
|
||||
self.gpr[ins.field_ra() as usize] = self.gpr[ins.field_rs() as usize];
|
||||
} else {
|
||||
let left = self.gpr[ins.field_rS()].value;
|
||||
let right = self.gpr[ins.field_rB()].value;
|
||||
let left = self.gpr[ins.field_rs() as usize].value;
|
||||
let right = self.gpr[ins.field_rb() as usize].value;
|
||||
let value = match (left, right) {
|
||||
(GprValue::Constant(left), GprValue::Constant(right)) => {
|
||||
GprValue::Constant(left | right)
|
||||
}
|
||||
_ => GprValue::Unknown,
|
||||
};
|
||||
self.gpr[ins.field_rA()].set_direct(value);
|
||||
self.gpr[ins.field_ra() as usize].set_direct(value);
|
||||
}
|
||||
}
|
||||
// cmp [crfD], [L], rA, rB
|
||||
@@ -342,34 +355,34 @@ impl VM {
|
||||
// cmpl [crfD], [L], rA, rB
|
||||
// cmpli [crfD], [L], rA, UIMM
|
||||
Opcode::Cmp | Opcode::Cmpi | Opcode::Cmpl | Opcode::Cmpli => {
|
||||
if ins.field_L() == 0 {
|
||||
let left_reg = ins.field_rA();
|
||||
if ins.field_l() == 0 {
|
||||
let left_reg = ins.field_ra() as usize;
|
||||
let left = self.gpr[left_reg].value;
|
||||
let (right, signed) = match ins.op {
|
||||
Opcode::Cmp => (self.gpr[ins.field_rB()].value, true),
|
||||
Opcode::Cmpl => (self.gpr[ins.field_rB()].value, false),
|
||||
Opcode::Cmp => (self.gpr[ins.field_rb() as usize].value, true),
|
||||
Opcode::Cmpl => (self.gpr[ins.field_rb() as usize].value, false),
|
||||
Opcode::Cmpi => (GprValue::Constant(ins.field_simm() as u32), true),
|
||||
Opcode::Cmpli => (GprValue::Constant(ins.field_uimm() as u32), false),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let crf = ins.field_crfD();
|
||||
self.cr[crf] = Cr { signed, left, right };
|
||||
self.gpr[left_reg].value = GprValue::ComparisonResult(crf as u8);
|
||||
let crf = ins.field_crfd();
|
||||
self.cr[crf as usize] = Cr { signed, left, right };
|
||||
self.gpr[left_reg].value = GprValue::ComparisonResult(crf);
|
||||
}
|
||||
}
|
||||
// rlwinm rA, rS, SH, MB, ME
|
||||
// rlwnm rA, rS, rB, MB, ME
|
||||
Opcode::Rlwinm | Opcode::Rlwnm => {
|
||||
let value = if let Some(shift) = match ins.op {
|
||||
Opcode::Rlwinm => Some(ins.field_SH() as u32),
|
||||
Opcode::Rlwnm => match self.gpr[ins.field_rB()].value {
|
||||
Opcode::Rlwinm => Some(ins.field_sh() as u32),
|
||||
Opcode::Rlwnm => match self.gpr[ins.field_rb() as usize].value {
|
||||
GprValue::Constant(value) => Some(value),
|
||||
_ => None,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
let mask = mask_value(ins.field_MB() as u32, ins.field_ME() as u32);
|
||||
match self.gpr[ins.field_rS()].value {
|
||||
let mask = mask_value(ins.field_mb() as u32, ins.field_me() as u32);
|
||||
match self.gpr[ins.field_rs() as usize].value {
|
||||
GprValue::Constant(value) => {
|
||||
GprValue::Constant(value.rotate_left(shift) & mask)
|
||||
}
|
||||
@@ -383,7 +396,7 @@ impl VM {
|
||||
} else {
|
||||
GprValue::Unknown
|
||||
};
|
||||
self.gpr[ins.field_rA()].set_direct(value);
|
||||
self.gpr[ins.field_ra() as usize].set_direct(value);
|
||||
}
|
||||
// b[l][a] target_addr
|
||||
// b[c][l][a] BO, BI, target_addr
|
||||
@@ -391,7 +404,7 @@ impl VM {
|
||||
// b[c]lr[l] BO, BI
|
||||
Opcode::B | Opcode::Bc | Opcode::Bcctr | Opcode::Bclr => {
|
||||
// HACK for `bla 0x60` in __OSDBJump
|
||||
if ins.op == Opcode::B && ins.field_LK() && ins.field_AA() {
|
||||
if ins.op == Opcode::B && ins.field_lk() && ins.field_aa() {
|
||||
return StepResult::Jump(BranchTarget::Unknown);
|
||||
}
|
||||
|
||||
@@ -409,7 +422,7 @@ impl VM {
|
||||
GprValue::Address(target) => BranchTarget::Address(target),
|
||||
GprValue::LoadIndexed { address, max_offset }
|
||||
// FIXME: avoids treating bctrl indirect calls as jump tables
|
||||
if !ins.field_LK() => {
|
||||
if !ins.field_lk() => {
|
||||
BranchTarget::JumpTable { address, size: max_offset.and_then(|n| n.checked_add(4)) }
|
||||
}
|
||||
_ => BranchTarget::Unknown,
|
||||
@@ -417,7 +430,7 @@ impl VM {
|
||||
}
|
||||
Opcode::Bclr => BranchTarget::Return,
|
||||
_ => {
|
||||
let value = ins.branch_dest().unwrap();
|
||||
let value = ins.branch_dest(ins_addr.address).unwrap();
|
||||
if let Some(target) = section_address_for(obj, ins_addr, value) {
|
||||
BranchTarget::Address(target)
|
||||
} else {
|
||||
@@ -427,7 +440,7 @@ impl VM {
|
||||
};
|
||||
|
||||
// If branching with link, use function call semantics
|
||||
if ins.field_LK() {
|
||||
if ins.field_lk() {
|
||||
return StepResult::Branch(vec![
|
||||
Branch {
|
||||
target: BranchTarget::Address(RelocationTarget::Address(ins_addr + 4)),
|
||||
@@ -439,7 +452,7 @@ impl VM {
|
||||
}
|
||||
|
||||
// Branch always
|
||||
if ins.op == Opcode::B || ins.field_BO() & 0b10100 == 0b10100 {
|
||||
if ins.op == Opcode::B || ins.field_bo() & 0b10100 == 0b10100 {
|
||||
return StepResult::Jump(branch_target);
|
||||
}
|
||||
|
||||
@@ -452,19 +465,19 @@ impl VM {
|
||||
vm: self.clone_all(),
|
||||
},
|
||||
// Branch taken
|
||||
Branch { target: branch_target, link: ins.field_LK(), vm: self.clone_all() },
|
||||
Branch { target: branch_target, link: ins.field_lk(), vm: self.clone_all() },
|
||||
];
|
||||
|
||||
// Use tracked CR to calculate new register values for branches
|
||||
let crf = ins.field_BI() >> 2;
|
||||
let crb = (ins.field_BI() & 3) as u8;
|
||||
let crf = (ins.field_bi() >> 2) as usize;
|
||||
let crb = ins.field_bi() & 3;
|
||||
let (f_val, t_val) =
|
||||
split_values_by_crb(crb, self.cr[crf].left, self.cr[crf].right);
|
||||
if ins.field_BO() & 0b11110 == 0b00100 {
|
||||
if ins.field_bo() & 0b11110 == 0b00100 {
|
||||
// Branch if false
|
||||
branches[0].vm.set_comparison_result(t_val, crf);
|
||||
branches[1].vm.set_comparison_result(f_val, crf);
|
||||
} else if ins.field_BO() & 0b11110 == 0b01100 {
|
||||
} else if ins.field_bo() & 0b11110 == 0b01100 {
|
||||
// Branch if true
|
||||
branches[0].vm.set_comparison_result(f_val, crf);
|
||||
branches[1].vm.set_comparison_result(t_val, crf);
|
||||
@@ -474,8 +487,8 @@ impl VM {
|
||||
}
|
||||
// lwzx rD, rA, rB
|
||||
Opcode::Lwzx => {
|
||||
let left = self.gpr[ins.field_rA()].address(obj, ins_addr);
|
||||
let right = self.gpr[ins.field_rB()].value;
|
||||
let left = self.gpr[ins.field_ra() as usize].address(obj, ins_addr);
|
||||
let right = self.gpr[ins.field_rb() as usize].value;
|
||||
let value = match (left, right) {
|
||||
(Some(address), GprValue::Range { min: _, max, .. })
|
||||
if /*min == 0 &&*/ max < u32::MAX - 4 && max & 3 == 0 =>
|
||||
@@ -492,12 +505,12 @@ impl VM {
|
||||
}
|
||||
_ => GprValue::Unknown,
|
||||
};
|
||||
self.gpr[ins.field_rD()].set_direct(value);
|
||||
self.gpr[ins.field_rd() as usize].set_direct(value);
|
||||
}
|
||||
// mtspr SPR, rS
|
||||
Opcode::Mtspr => match ins.field_spr() {
|
||||
8 => self.lr = self.gpr[ins.field_rS()].value,
|
||||
9 => self.ctr = self.gpr[ins.field_rS()].value,
|
||||
8 => self.lr = self.gpr[ins.field_rs() as usize].value,
|
||||
9 => self.ctr = self.gpr[ins.field_rs() as usize].value,
|
||||
_ => {}
|
||||
},
|
||||
// mfspr rD, SPR
|
||||
@@ -507,14 +520,14 @@ impl VM {
|
||||
9 => self.ctr,
|
||||
_ => GprValue::Unknown,
|
||||
};
|
||||
self.gpr[ins.field_rD()].set_direct(value);
|
||||
self.gpr[ins.field_rd() as usize].set_direct(value);
|
||||
}
|
||||
// rfi
|
||||
Opcode::Rfi => {
|
||||
return StepResult::Jump(BranchTarget::Unknown);
|
||||
}
|
||||
op if is_load_store_op(op) => {
|
||||
let source = ins.field_rA();
|
||||
let source = ins.field_ra() as usize;
|
||||
let mut result = StepResult::Continue;
|
||||
if let GprValue::Address(target) = self.gpr[source].value {
|
||||
if is_update_op(op) {
|
||||
@@ -549,13 +562,13 @@ impl VM {
|
||||
self.gpr[source].set_direct(GprValue::Unknown);
|
||||
}
|
||||
if is_load_op(op) {
|
||||
self.gpr[ins.field_rD()].set_direct(GprValue::Unknown);
|
||||
self.gpr[ins.field_rd() as usize].set_direct(GprValue::Unknown);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
_ => {
|
||||
for field in ins.defs() {
|
||||
if let Some(Argument::GPR(GPR(reg))) = field.argument() {
|
||||
for argument in ins.defs() {
|
||||
if let Argument::GPR(GPR(reg)) = argument {
|
||||
self.gpr[reg as usize].set_direct(GprValue::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use itertools::Itertools;
|
||||
use ppc750cl::{disasm_iter, Argument, Ins, Opcode};
|
||||
use ppc750cl::{Argument, Ins, InsIter, Opcode};
|
||||
|
||||
use crate::{
|
||||
obj::{
|
||||
@@ -67,15 +67,15 @@ where W: Write + ?Sized {
|
||||
|
||||
// Generate local jump labels
|
||||
if section.kind == ObjSectionKind::Code {
|
||||
for ins in disasm_iter(§ion.data, section.address as u32) {
|
||||
if let Some(address) = ins.branch_dest() {
|
||||
if ins.field_AA() || !section.contains(address) {
|
||||
for (addr, ins) in InsIter::new(§ion.data, section.address as u32) {
|
||||
if let Some(address) = ins.branch_dest(addr) {
|
||||
if ins.field_aa() || !section.contains(address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace section-relative jump relocations (generated by GCC)
|
||||
// These aren't always possible to express accurately in GNU assembler
|
||||
if matches!(relocations.get(&ins.addr), Some(reloc) if reloc.addend == 0) {
|
||||
if matches!(relocations.get(&addr), Some(reloc) if reloc.addend == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ where W: Write + ?Sized {
|
||||
target_symbol_idx = Some(symbol_idx);
|
||||
}
|
||||
if let Some(symbol_idx) = target_symbol_idx {
|
||||
relocations.insert(ins.addr, ObjReloc {
|
||||
relocations.insert(addr, ObjReloc {
|
||||
kind: match ins.op {
|
||||
Opcode::B => ObjRelocKind::PpcRel24,
|
||||
Opcode::Bc => ObjRelocKind::PpcRel14,
|
||||
@@ -243,10 +243,10 @@ fn write_code_chunk<W>(
|
||||
where
|
||||
W: Write + ?Sized,
|
||||
{
|
||||
for ins in disasm_iter(data, address) {
|
||||
let reloc = relocations.get(&ins.addr);
|
||||
let file_offset = section.file_offset + (ins.addr as u64 - section.address);
|
||||
write_ins(w, symbols, ins, reloc, file_offset, section.virtual_address)?;
|
||||
for (addr, ins) in InsIter::new(data, address) {
|
||||
let reloc = relocations.get(&addr);
|
||||
let file_offset = section.file_offset + (addr as u64 - section.address);
|
||||
write_ins(w, symbols, addr, ins, reloc, file_offset, section.virtual_address)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -254,6 +254,7 @@ where
|
||||
fn write_ins<W>(
|
||||
w: &mut W,
|
||||
symbols: &[ObjSymbol],
|
||||
addr: u32,
|
||||
mut ins: Ins,
|
||||
reloc: Option<&ObjReloc>,
|
||||
file_offset: u64,
|
||||
@@ -265,7 +266,7 @@ where
|
||||
write!(
|
||||
w,
|
||||
"/* {:08X} {:08X} {:02X} {:02X} {:02X} {:02X} */\t",
|
||||
ins.addr as u64 + section_vaddr.unwrap_or(0),
|
||||
addr as u64 + section_vaddr.unwrap_or(0),
|
||||
file_offset,
|
||||
(ins.code >> 24) & 0xFF,
|
||||
(ins.code >> 16) & 0xFF,
|
||||
@@ -290,10 +291,10 @@ where
|
||||
write!(w, ".4byte {:#010X} /* invalid */", ins.code)?;
|
||||
} else if is_illegal_instruction(ins.code) {
|
||||
let sins = ins.simplified();
|
||||
write!(w, ".4byte {:#010X} /* illegal: {} */", sins.ins.code, sins)?;
|
||||
write!(w, ".4byte {:#010X} /* illegal: {} */", ins.code, sins)?;
|
||||
} else {
|
||||
let sins = ins.simplified();
|
||||
write!(w, "{}{}", sins.mnemonic, sins.ins.suffix())?;
|
||||
write!(w, "{}", sins.mnemonic)?;
|
||||
|
||||
let mut writing_offset = false;
|
||||
for (i, arg) in sins.args.iter().enumerate() {
|
||||
|
||||
Reference in New Issue
Block a user