From 06f67c4cf0ec412f85db37828cda855ab52f4970 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Sun, 1 Nov 2020 05:51:55 +0000 Subject: [PATCH] `Data` and `DataTable` now acknowledge stacked / 'specialized' data locations. Improved replacement of addresses with their labels somewhat. Grealy extended the known data locations with hardware locations. --- dcb-tools/src/decompiler/main.rs | 45 +++- dcb/src/game/exe/data.rs | 28 +- dcb/src/game/exe/data/table.rs | 12 +- resources/known_data.yaml | 435 ++++++++++++++++++++++++++++--- 4 files changed, 462 insertions(+), 58 deletions(-) diff --git a/dcb-tools/src/decompiler/main.rs b/dcb-tools/src/decompiler/main.rs index 1531f85..f9284db 100644 --- a/dcb-tools/src/decompiler/main.rs +++ b/dcb-tools/src/decompiler/main.rs @@ -236,7 +236,7 @@ fn main() -> Result<(), anyhow::Error> { .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)), + Some((target, prefix)) => print!("{}", set_instruction_immediate(instruction, format_args!(" {prefix}{target}"))), None => print!("{instruction}"), }, @@ -263,11 +263,11 @@ fn main() -> Result<(), anyhow::Error> { { Some((start_pos, name)) => { if start_pos == Pos(*target) { - print!("{} {}", strip_last_arg(instruction), name); + print!("{}", set_instruction_immediate(instruction, format_args!(" {name}"))); } else { let offset = Pos(*target) - start_pos; if offset > 0 { - print!("{} {} + {offset:#x}", strip_last_arg(instruction), name); + print!("{}", set_instruction_immediate(instruction, format_args!(" {name} + {offset:#x}"))); } } }, @@ -281,10 +281,14 @@ fn main() -> Result<(), anyhow::Error> { if let Instruction::Directive(Directive::Dw(target)) = instruction { if let Some(func) = functions.get(Pos(*target)) { print!(" # {}", func.name); - } - if let Some(data) = data_pos.get(Pos(*target)) { + } else if let Some(data) = data_pos.get(Pos(*target)) { if data.pos == Pos(*target) { print!(" # {}", data.name); + } else { + let offset = Pos(*target) - data.pos; + if offset > 0 { + print!(" # {} + {offset:#x}", data.name); + } } } } @@ -303,12 +307,29 @@ fn main() -> Result<(), anyhow::Error> { Ok(()) } - -/// Helper function to extract the last argument from an instruction +/// Helper function to modify the immediate 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 +fn set_instruction_immediate(instruction: &Instruction, fmt: std::fmt::Arguments) -> String { + use std::fmt::Write; + + // Get the string as a string + let mut s: String = instruction.to_string(); + + // If the instruction is of the form `.. , imm(..)`, then grab + // the `(..)` + #[allow(clippy::indexing_slicing)] // This can't panic + let parens = s.rfind('(').map(|idx| s[idx..].to_string()); + + // Truncate the string from it's last argument and write the fmt + // Note: We use ' ' instead of ',' to support single argument instructions. + s.truncate(s.rfind(' ').unwrap_or(0)); + s.write_fmt(fmt).expect("Unable to write format to string"); + + // If we had a paren, write it + // Note: This technically shouldn't be needed, but just in case. + if let Some(parens) = parens { + s.push_str(&parens); + } + + s } diff --git a/dcb/src/game/exe/data.rs b/dcb/src/game/exe/data.rs index 284adbc..d3e335a 100644 --- a/dcb/src/game/exe/data.rs +++ b/dcb/src/game/exe/data.rs @@ -17,7 +17,7 @@ pub use table::DataTable; // Imports use crate::game::exe::Pos; -use std::borrow::Borrow; +use std::{borrow::Borrow, cmp::Ordering}; /// Data location #[derive(Clone, Debug)] @@ -41,7 +41,13 @@ impl Data { /// Returns the end position of this data #[must_use] pub fn end_pos(&self) -> Pos { - self.pos + self.kind.size() + self.pos + self.size() + } + + /// Returns the size, in bytes, of this data + #[must_use] + pub fn size(&self) -> u32 { + self.kind.size() } } @@ -67,18 +73,24 @@ impl std::hash::Hash for Data { } } -/// Only the position matters for the order +/// Simply defers to the [`Ord`] impl. impl PartialOrd for Data { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { // Delegate to `eq` since we have a total order. Some(self.cmp(other)) } } -/// Only the position matters for the order +/// The position is the main determining factor for each +/// data, but as 'specialized' data segments may exist, +/// when 2 data locations have the same position, the larger +/// one is put first. impl Ord for Data { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - // Only compare the start position - self.pos.cmp(&other.pos) + fn cmp(&self, other: &Self) -> Ordering { + match self.pos.cmp(&other.pos) { + // Note: We reverse so the larger size will be first. + Ordering::Equal => self.size().cmp(&other.size()).reverse(), + cmp => cmp, + } } } diff --git a/dcb/src/game/exe/data/table.rs b/dcb/src/game/exe/data/table.rs index 169c047..ca2de6a 100644 --- a/dcb/src/game/exe/data/table.rs +++ b/dcb/src/game/exe/data/table.rs @@ -28,7 +28,9 @@ use std::{collections::BTreeSet, convert::TryInto, fs::File, iter::FromIterator} /// Data table /// /// Stores all data locations sorted by their address. -/// Also guarantees all data locations are unique and non-overlapping. +/// Data locations may be 'specialized', that is, a large data +/// location may have several smaller data locations inside of it, +/// as long as they only belong to the larger data location. #[derive(PartialEq, Eq, Clone, Debug)] #[derive(serde::Serialize, serde::Deserialize)] pub struct DataTable(BTreeSet); @@ -42,8 +44,8 @@ impl FromIterator for DataTable { impl DataTable { /// Merges two data tables, discarding duplicates from `other`. /// - /// This can be useful when combining known functions and heuristically - /// discovered function, as the known functions are always kept, and the + /// This can be useful when combining known data locations and heuristically + /// discovered data locations, as the known functions are always kept, and the /// duplicate discovered ones are discarded. #[must_use] pub fn merge(self, other: Self) -> Self { @@ -52,10 +54,12 @@ impl DataTable { DiscardingSortedMergeIter::new(self.0.into_iter(), other.0.into_iter()).collect() } - /// Retrieves the data location containing `pos` + /// Retrieves the smallest data location containing `pos` #[must_use] pub fn get(&self, pos: Pos) -> Option<&Data> { // Find the closest one and check if it contains `pos` + // Note: We search from the end to make sure we grab the + // smaller locations first. self.0.range(..=pos).next_back().filter(|data| pos <= data.end_pos()) } } diff --git a/resources/known_data.yaml b/resources/known_data.yaml index c48a6c6..6f59251 100644 --- a/resources/known_data.yaml +++ b/resources/known_data.yaml @@ -1,28 +1,20 @@ --- -- name: I_STAT - desc: Interrupt status register - pos: 0x1f801070 +- name: I_STAT_PTR + pos: 0x80070aac kind: Word -- name: I_MASK - desc: Interrupt mask register - pos: 0x1f801074 +- name: I_MASK_PTR + pos: 0x80070ab0 kind: Word -- name: DPCR - desc: DMA Control register - pos: 0x1f8010f0 +- name: DPCR_PTR + pos: 0x80070ab4 kind: Word -- name: DICR - desc: DMA Interrupt register - pos: 0x1f8010f4 +- name: ZeroStart + desc: "Start of the zero section in `start`" + pos: 0x80077a08 kind: Word -- name: Timer0 - pos: 0x1f801100 - kind: Word -- name: Timer1 - pos: 0x1f801110 - kind: Word -- name: Timer2 - pos: 0x1f801120 +- name: HeapStart + desc: Start of the heap + pos: 0x801ddf38 kind: Word - name: something1_data2 pos: 0x80010000 @@ -55,8 +47,22 @@ ty: Word len: 8 +# Expansion region 1 +- name: ExpansionRegion1Header + pos: 0x1f00000 + kind: + Array: + ty: Word + len: 0x40 +- name: ExpansionRegion1Data + pos: 0x1f00100 + kind: + Array: + ty: Word + len: 0x1ffC0 + +# Scratchpad - name: Scratchpad - dec: "" pos: 0x1f800000 kind: Array: @@ -64,22 +70,383 @@ len: 0x400 # Memory control 1 +- name: Expansion1BaseAddress + pos: 0x1f801000 + kind: Word +- name: Expansion2BaseAddress + pos: 0x1f801004 + kind: Word +- name: Expansion1DelaySize + pos: 0x1f801008 + kind: Word +- name: Expansion3DelaySize + pos: 0x1f80100c + kind: Word +- name: BIOS_ROM + pos: 0x1f801010 + kind: Word +- name: SPU_DELAY + pos: 0x1f801014 + kind: Word +- name: CDROM_DELAY + pos: 0x1f801018 + kind: Word +- name: Expansion2DelaySize + pos: 0x1f80101c + kind: Word +- name: COM_DELAY + pos: 0x1f801020 + kind: Word -# -- name: I_STAT_PTR - pos: 0x80070aac +# Peripheral I/O Ports +- name: JOY_DATA + desc: "Joypad / Memory card data" + pos: 0x1f801040 kind: Word -- name: I_MASK_PTR - pos: 0x80070ab0 +- name: JOY_STAT + desc: "Joypad / Memory card status" + pos: 0x1f801044 kind: Word -- name: DPCR_PTR - pos: 0x80070ab4 +- name: JOY_MODE + desc: "Joypad / Memory card mode" + pos: 0x1f801048 + kind: HalfWord +- name: JOY_CTRL + desc: "Joypad / Memory card control" + pos: 0x1f80104a + kind: HalfWord +- name: JOY_BAUD + desc: "Joypad / Memory card baud-rate" + pos: 0x1f80104e + kind: HalfWord + +- name: SIO_DATA + desc: "Serial port data" + pos: 0x1f801050 kind: Word -- name: ZeroStart - desc: "Start of the zero section in `start`" - pos: 0x80077a08 +- name: SIO_STAT + desc: "Serial port status" + pos: 0x1f801054 kind: Word -- name: HeapStart - desc: Start of the heap - pos: 0x801ddf38 +- name: SIO_MODE + desc: "Serial port mode" + pos: 0x1f801058 + kind: HalfWord +- name: SIO_CTRL + desc: "Serial port control" + pos: 0x1f80105a + kind: HalfWord +- name: SIO_MISC + desc: "Serial port internal register" + pos: 0x1f80105c + kind: HalfWord +- name: SIO_BAUD + desc: "Serial port internal baud-rate" + pos: 0x1f80105e + kind: HalfWord + +# Memory Control 2 +- name: Ram_SIZE + pos: 0x1f801060 kind: Word + +# Interrupt control +- name: I_STAT + desc: Interrupt status register + pos: 0x1f801070 + kind: Word +- name: I_MASK + desc: Interrupt mask register + pos: 0x1f801074 + kind: Word + +# DMA Registers +- name: DMA_MDECin + desc: DMA0 channel 0 + pos: 0x1f801080 + kind: + Array: + ty: Word + len: 4 +- name: DMA_MDECout + desc: DMA1 channel 1 + pos: 0x1f801090 + kind: + Array: + ty: Word + len: 4 +- name: DMA_GPU + desc: DMA2 channel 2 + pos: 0x1f8010a0 + kind: + Array: + ty: Word + len: 4 +- name: DMA_CDROM + desc: DMA3 channel 3 + pos: 0x1f8010b0 + kind: + Array: + ty: Word + len: 4 +- name: DMA_SPU + desc: DMA4 channel 4 + pos: 0x1f8010c0 + kind: + Array: + ty: Word + len: 4 +- name: DMA_PIO + desc: DMA5 channel 5, Expansion port + pos: 0x1f8010d0 + kind: + Array: + ty: Word + len: 4 +- name: DMA_OTC + desc: DMA5 channel 5, Reverse clear OT (GPU) + pos: 0x1f8010e0 + kind: + Array: + ty: Word + len: 4 +- name: DPCR + desc: DMA Control register + pos: 0x1f8010f0 + kind: Word +- name: DICR + desc: DMA Interrupt register + pos: 0x1f8010f4 + kind: Word +- name: DMA_Unknown0 + pos: 0x1f8010f8 + kind: Word +- name: DMA_Unknown1 + pos: 0x1f8010fc + kind: Word + +# Timers / Root counters +- name: Timer0 + desc: Dotclock + pos: 0x1f801100 + kind: Word +- name: Timer1 + desc: Horizontal Retrace + pos: 0x1f801110 + kind: Word +- name: Timer2 + desc: 1/8 System clock + pos: 0x1f801120 + kind: Word + +# CDRom Registers +# TODO: Figure out what Index means for these. + +# GPU Registers +- name: GP0_SEND_OR_GPU_READ + desc: GP0 Sender / GPU Responses + pos: 0x1f801810 + kind: Word +- name: GP1_SEND_OR_GPU_STAT + desc: GP1 Sender / GPU Status register + pos: 0x1f801814 + kind: Word + +# MDEC Registers +- name: MDEC_CommandParameter_Or_DataResponse_Register + pos: 0x1f801820 + kind: Word +- name: MDEC_ControlReset_Or_StatusRegister_Register + pos: 0x1f801824 + kind: Word + +# SPU Voice registers +# TODO: Find a better way of expressing this +# interleaved memory layout. +- name: SPU_Voices + pos: 0x1f801c00 + kind: + Array: + ty: Word + len: 0x180 + +# SPU Control registers +- name: SPU_MainVolumeLR + pos: 0x1f801d80 + kind: Word +- name: SPU_ReverbOutputVolumeLR + pos: 0x1f801d84 + kind: Word +- name: SPU_VoiceKeyOn + pos: 0x1f801d88 + kind: Word +- name: SPU_VoiceKeyOff + pos: 0x1f801d8c + kind: Word +- name: SPU_VoiceChannelFMMode + pos: 0x1f801d90 + kind: Word +- name: SPU_VoiceChannelNoiseMode + pos: 0x1f801d94 + kind: Word +- name: SPU_VoiceChannelReverbMode + pos: 0x1f801d98 + kind: Word +- name: SPU_VoiceChannelOnOffStatus + pos: 0x1f801d9c + kind: Word +- name: SPU_Unknown0 + pos: 0x1f801da0 + kind: HalfWord +- name: SPU_SoundRamReverbWorkAreaStartAddress + pos: 0x1f801da2 + kind: HalfWord +- name: SPU_SoundRamIrqAddress + pos: 0x1f801da4 + kind: HalfWord +- name: SPU_SoundRamDataTransferAddress + pos: 0x1f801da6 + kind: HalfWord +- name: SPU_SoundRamDataTransferFifo + pos: 0x1f801da8 + kind: HalfWord +- name: SPU_CNT + desc: SPU Control Register + pos: 0x1f801daa + kind: HalfWord +- name: SPU_SoundRamDataTransferControl + pos: 0x1f801dac + kind: HalfWord +- name: SPU_STAT + desc: SPU Status Register + pos: 0x1f801dae + kind: HalfWord +- name: SPU_CDVolumeLR + pos: 0x1f801db0 + kind: Word +- name: SPU_ExternVolumeLR + pos: 0x1f801db4 + kind: Word +- name: SPU_CurrentMainVolumeLR + pos: 0x1f801db8 + kind: Word +- name: SPU_Unknown1 + pos: 0x1f801db8 + kind: Word + +# SPU Reverb configuration area +- name: SPU_dAPF1 + desc: SPU Reverb APF offset 1 + pos: 0x1f801dc0 + kind: HalfWord +- name: SPU_dAPF2 + desc: SPU Reverb APF offset 2 + pos: 0x1f801dc2 + kind: HalfWord +- name: SPU_vIIR + desc: SPU Reflection volume 1 + pos: 0x1f801dc4 + kind: HalfWord +- name: SPU_vCOMB1 + desc: SPU Comb volume 1 + pos: 0x1f801dc6 + kind: HalfWord +- name: SPU_vCOMB2 + desc: SPU Comb volume 2 + pos: 0x1f801dc8 + kind: HalfWord +- name: SPU_vCOMB3 + desc: SPU Comb volume 3 + pos: 0x1f801dca + kind: HalfWord +- name: SPU_vCOMB4 + desc: SPU Comb volume 4 + pos: 0x1f801dcc + kind: HalfWord +- name: SPU_vWALL + desc: SPU Reflection volume 2 + pos: 0x1f801dce + kind: HalfWord +- name: SPU_vAPF1 + desc: SPU Reverb APF volume 1 + pos: 0x1f801dd0 + kind: HalfWord +- name: SPU_vAPF2 + desc: SPU Reverb APF volume 2 + pos: 0x1f801dd2 + kind: HalfWord +- name: SPU_mSAME + desc: SPU Reverb same-side reflection address 1 (L/R) + pos: 0x1f801dd4 + kind: Word +- name: SPU_mCOMB1 + desc: SPU Comb address 1 (L/R) + pos: 0x1f801dd8 + kind: Word +- name: SPU_mCOMB2 + desc: SPU Comb address 2 (L/R) + pos: 0x1f801ddc + kind: Word +- name: SPU_dSAME + desc: SPU Reverb same-side reflection address 2 (L/R) + pos: 0x1f801de0 + kind: Word +- name: SPU_mDIFF + desc: SPU Reverb different-side reflection address 1 (L/R) + pos: 0x1f801de4 + kind: Word +- name: SPU_mCOMB3 + desc: SPU Comb address 3 (L/R) + pos: 0x1f801de8 + kind: Word +- name: SPU_mCOMB4 + desc: SPU Comb address 4 (L/R) + pos: 0x1f801dec + kind: Word +- name: SPU_dDIFF + desc: SPU Reverb different-side reflection address 2 (L/R) + pos: 0x1f801df0 + kind: Word +- name: SPU_mAPF1 + desc: SPU Reverb APF address 1 (L/R) + pos: 0x1f801df4 + kind: Word +- name: SPU_mAPF2 + desc: SPU Reverb APF address 2 (L/R) + pos: 0x1f801df8 + kind: Word +- name: SPU_vIN + desc: SPU Reverb input volume (L/R) + pos: 0x1f801dfc + kind: Word + +# SPU internal registers +- name: SPU_VoiceCurrentVolumeLR + desc: Voices current volume + pos: 0x1f801e00 + kind: + Array: + ty: Word + len: 0x60 +- name: SPU_Unknown2 + pos: 0x1f801e60 + kind: + Array: + ty: Word + len: 0x4 +- name: SPU_Unknown3 + pos: 0x1f801e80 + kind: + Array: + ty: Word + len: 0x60 + +# TODO: Expansion region 2 + +# BIOS Region +- name: BIOS + pos: 0x1fc00000 + kind: + Array: + ty: Word + len: 0x20000