Directive now only decodes dw after the code end.

Load/Store instructions now display what function/string/data they're loading inline.
This commit is contained in:
2020-10-28 19:55:36 +00:00
parent d73a9ff415
commit c7ab0e5e94
3 changed files with 95 additions and 87 deletions

View File

@@ -250,69 +250,56 @@ fn main() -> Result<(), anyhow::Error> {
) |
Instruction::Pseudo(
PseudoInstruction::B { target } | PseudoInstruction::Beqz { target, .. } | PseudoInstruction::Bnez { target, .. },
) => {
if let Some((target, prefix)) = functions
.get(*target)
.map(|func| (&func.name, ""))
.or_else(|| cur_func.and_then(|func| func.labels.get(target).map(|label| (label, "."))))
{
// TODO: Improve solution, removing the target like this isn't
// a good way of going about it.
let instruction = instruction.to_string();
#[allow(clippy::indexing_slicing)] // This can't panic, it's index is `..{0..len}`.
let instruction = &instruction[..instruction.rfind(' ').unwrap_or_else(|| instruction.len())];
print!("{instruction} {prefix}{target}");
} else {
print!("{instruction}");
}
) => match functions
.get(*target)
.map(|func| (&func.name, ""))
.or_else(|| cur_func.and_then(|func| func.labels.get(target).map(|label| (label, "."))))
{
Some((target, prefix)) => print!("{} {prefix}{target}", strip_last_arg(instruction)),
None => print!("{instruction}"),
},
_ => print!("{instruction}"),
}
// Check if we should have any comments with this instruction
match instruction {
// Comment loading address, loading and writing values of string and data
// TODO: Maybe check loads / writes to halfway between
// the strings / data.
Instruction::Pseudo(
PseudoInstruction::La { target: offset, .. } |
PseudoInstruction::Li32 { imm: offset, .. } |
PseudoInstruction::LbImm { offset, .. } |
PseudoInstruction::LbuImm { offset, .. } |
PseudoInstruction::LhImm { offset, .. } |
PseudoInstruction::LhuImm { offset, .. } |
PseudoInstruction::LwlImm { offset, .. } |
PseudoInstruction::LwImm { offset, .. } |
PseudoInstruction::LwrImm { offset, .. } |
PseudoInstruction::SbImm { offset, .. } |
PseudoInstruction::ShImm { offset, .. } |
PseudoInstruction::SwlImm { offset, .. } |
PseudoInstruction::SwImm { offset, .. } |
PseudoInstruction::SwrImm { offset, .. },
) => {
if let Some(string_idx) = strings_pos.get(Pos::ref_cast(offset)) {
print!(" # string_{string_idx}");
}
if let Some(data_idx) = data_pos.get(Pos::ref_cast(offset)) {
print!(" # data_{data_idx}");
}
PseudoInstruction::La { target, .. } |
PseudoInstruction::Li32 { imm: target, .. } |
PseudoInstruction::LbImm { offset: target, .. } |
PseudoInstruction::LbuImm { offset: target, .. } |
PseudoInstruction::LhImm { offset: target, .. } |
PseudoInstruction::LhuImm { offset: target, .. } |
PseudoInstruction::LwlImm { offset: target, .. } |
PseudoInstruction::LwImm { offset: target, .. } |
PseudoInstruction::LwrImm { offset: target, .. } |
PseudoInstruction::SbImm { offset: target, .. } |
PseudoInstruction::ShImm { offset: target, .. } |
PseudoInstruction::SwlImm { offset: target, .. } |
PseudoInstruction::SwImm { offset: target, .. } |
PseudoInstruction::SwrImm { offset: target, .. },
) => match strings_pos
.get(Pos::ref_cast(target))
.map(|idx| (idx, "string_"))
.or_else(|| data_pos.get(Pos::ref_cast(target)).map(|idx| (idx, "data_")))
{
Some((target, prefix)) => print!("{} {prefix}{target}", strip_last_arg(instruction)),
None => print!("{instruction}"),
},
// Comment `dw`s with both function and data
Instruction::Directive(Directive::Dw(offset) | Directive::DwRepeated { value: offset, .. }) => {
if let Some(func) = functions.get(Pos(*offset)) {
print!(" # {}", func.name);
}
if let Some(string_idx) = strings_pos.get(Pos::ref_cast(offset)) {
print!(" # string_{string_idx}");
}
if let Some(data_idx) = data_pos.get(Pos::ref_cast(offset)) {
print!(" # data_{data_idx}");
}
},
_ => print!("{instruction}"),
}
_ => (),
// Comment any `dw` instructions that are function, data or string pointers
if let Instruction::Directive(Directive::Dw(target) | Directive::DwRepeated { value: target, .. }) = instruction {
if let Some(func) = functions.get(Pos(*target)) {
print!(" # {}", func.name);
}
if let Some(string_idx) = strings_pos.get(Pos::ref_cast(target)) {
print!(" # string_{string_idx}");
}
if let Some(data_idx) = data_pos.get(Pos::ref_cast(target)) {
print!(" # data_{data_idx}");
}
}
// Append any comments in this line
@@ -336,3 +323,13 @@ fn main() -> Result<(), anyhow::Error> {
Ok(())
}
/// Helper function to extract the last argument from an instruction
// TODO: Use something better than this
fn strip_last_arg(instruction: &Instruction) -> String {
let mut instruction: String = instruction.to_string();
// Note: This can't panic
instruction.truncate(instruction.rfind(' ').unwrap_or(0));
instruction
}

View File

@@ -94,7 +94,7 @@ impl Func<&'static str> {
Pos(0x80056280) => "zero_loop",
},
start_pos: Pos(0x80056270),
end_pos: Pos(0x80056384),
end_pos: Pos(0x80056388),
},
])
}

View File

@@ -1,7 +1,7 @@
//! Directives
// Imports
use super::{FromRawIter, Raw};
use super::{FromRawIter, Instruction, Raw};
use crate::game::exe::Pos;
use ascii::{AsciiChar, AsciiStr, AsciiString};
use AsciiChar::Null;
@@ -29,6 +29,41 @@ pub enum Directive {
Ascii(AsciiString),
}
impl Directive {
/// Decodes a `dw` instruction
pub fn decode_dw(first_raw: Raw, iter: &mut (impl Iterator<Item = Raw> + Clone)) -> Self {
let mut times_repeated = 0;
// Keep getting values until either eof or a different one
loop {
let mut cur_iter = iter.clone();
match cur_iter.next().map(|next_raw| next_raw.repr == first_raw.repr) {
// If we got a different value, keep fetching values until they're different
Some(true) => {
*iter = cur_iter;
times_repeated += 1;
},
// If we didn't get it or we got a different value, exit
// Note: No need t update the iterator, as it either returned `None` or
// a different raw.
None | Some(false) => match times_repeated {
// If the value didn't repeat, use a single `dw`
0 => break Self::Dw(first_raw.repr),
// Else return the table
_ => {
break Self::DwRepeated {
value: first_raw.repr,
len: times_repeated + 1,
}
},
},
}
}
}
}
/// Helper function to check if a string has null and if everything after the first
/// null is also null (or if there were no nulls).
@@ -52,6 +87,12 @@ impl FromRawIter for Directive {
// Get the first raw
let raw = iter.next()?;
// If we're past all the code, there are no more strings,
// so just decode a `dw`.
if raw.pos >= Instruction::CODE_END {
return Some((raw.pos, Self::decode_dw(raw, iter)));
}
// Try to get an ascii string from the raw and check for nulls
match AsciiString::from_ascii(raw.repr.to_ne_bytes()).map(check_nulls) {
// If we got a string with at least 1 non-null, but
@@ -101,37 +142,7 @@ impl FromRawIter for Directive {
// Else if it was full null, non-uniformly null or non-ascii,
// try to get a dw table
_ => {
let mut times_repeated = 0;
// Keep getting values until either eof or a different one
loop {
let mut cur_iter = iter.clone();
match cur_iter.next().map(|next_raw| next_raw.repr == raw.repr) {
// If we got a different value, keep fetching values until they're different
Some(true) => {
*iter = cur_iter;
times_repeated += 1;
},
// If we didn't get it or we got a different value, exit
// Note: No need t update the iterator, as it either returned `None` or
// a different raw.
None | Some(false) => match times_repeated {
// If the value didn't repeat, use a single `dw`
0 => break Some((raw.pos, Self::Dw(raw.repr))),
// Else return the table
_ => {
break Some((raw.pos, Self::DwRepeated {
value: raw.repr,
len: times_repeated + 1,
}))
},
},
}
}
},
_ => Some((raw.pos, Self::decode_dw(raw, iter))),
}
}
}