ds_rom/rom/raw/
autoload_info.rs

1use std::{
2    fmt::Display,
3    mem::{align_of, size_of},
4};
5
6use bytemuck::{Pod, PodCastError, Zeroable};
7use serde::{Deserialize, Serialize};
8use snafu::{Backtrace, Snafu};
9
10use super::RawBuildInfoError;
11
12/// Info about an autoload block.
13#[repr(C)]
14#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Zeroable, Pod, Deserialize, Serialize)]
15pub struct AutoloadInfo {
16    /// Base address of the autoload module.
17    pub base_address: u32,
18    /// Size of the module's initialized area.
19    pub code_size: u32,
20    /// Size of the module's uninitialized area.
21    pub bss_size: u32,
22}
23
24/// Autoload kind.
25#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
26pub enum AutoloadKind {
27    /// Instruction TCM (Tightly Coupled Memory). Mainly used to make functions have fast and predictable load times.
28    Itcm,
29    /// Data TCM (Tightly Coupled Memory). Mainly used to make data have fast and predictable access times.
30    Dtcm,
31    /// Other autoload block of unknown purpose.
32    Unknown(AutoloadInfo),
33}
34
35/// Errors related to [`AutoloadInfo`].
36#[derive(Debug, Snafu)]
37pub enum RawAutoloadInfoError {
38    /// See [`RawBuildInfoError`].
39    #[snafu(transparent)]
40    RawBuildInfo {
41        /// Source error.
42        source: RawBuildInfoError,
43    },
44    /// Occurs when the input is not evenly divisible into a slice of [`AutoloadInfo`].
45    #[snafu(display("autoload infos must be a multiple of {} bytes:\n{backtrace}", size_of::<AutoloadInfo>()))]
46    InvalidSize {
47        /// Backtrace to the source of the error.
48        backtrace: Backtrace,
49    },
50    /// Occurs when the input is less aligned than [`AutoloadInfo`].
51    #[snafu(display("expected {expected}-alignment for autoload infos but got {actual}-alignment:\n{backtrace}"))]
52    Misaligned {
53        /// Expected alignment.
54        expected: usize,
55        /// Actual input alignment.
56        actual: usize,
57        /// Backtrace to the source of the error.
58        backtrace: Backtrace,
59    },
60}
61
62impl AutoloadInfo {
63    fn check_size(data: &'_ [u8]) -> Result<(), RawAutoloadInfoError> {
64        let size = size_of::<Self>();
65        if data.len() % size != 0 {
66            InvalidSizeSnafu {}.fail()
67        } else {
68            Ok(())
69        }
70    }
71
72    fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawAutoloadInfoError> {
73        match result {
74            Ok(build_info) => Ok(build_info),
75            Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
76                MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
77            }
78            Err(PodCastError::AlignmentMismatch) => panic!(),
79            Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
80            Err(PodCastError::SizeMismatch) => unreachable!(),
81        }
82    }
83
84    /// Reinterprets a `&[u8]` as a slice of [`AutoloadInfo`].
85    ///
86    /// # Errors
87    ///
88    /// This function will return an error if the input has the wrong size or alignment.
89    pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ [Self], RawAutoloadInfoError> {
90        Self::check_size(data)?;
91        let addr = data as *const [u8] as *const () as usize;
92        Self::handle_pod_cast(bytemuck::try_cast_slice(data), addr)
93    }
94
95    /// Returns the kind of this [`AutoloadInfo`].
96    pub fn kind(&self) -> AutoloadKind {
97        match self.base_address {
98            0x1ff8000 => AutoloadKind::Itcm,
99            0x27e0000 | 0x27c0000 | 0x23c0000 => AutoloadKind::Dtcm,
100            _ => AutoloadKind::Unknown(*self),
101        }
102    }
103
104    /// Creates a [`DisplayAutoloadInfo`] which implements [`Display`].
105    pub fn display(&self, indent: usize) -> DisplayAutoloadInfo {
106        DisplayAutoloadInfo { info: self, indent }
107    }
108}
109
110/// Can be used to display values inside [`AutoloadInfo`].
111pub struct DisplayAutoloadInfo<'a> {
112    info: &'a AutoloadInfo,
113    indent: usize,
114}
115
116impl Display for DisplayAutoloadInfo<'_> {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        let i = format!("{:indent$}", "", indent = self.indent);
119        let info = &self.info;
120        writeln!(f, "{i}Type .......... : {}", info.kind())?;
121        writeln!(f, "{i}Base address .. : {:#x}", info.base_address)?;
122        writeln!(f, "{i}Code size ..... : {:#x}", info.code_size)?;
123        writeln!(f, "{i}.bss size ..... : {:#x}", info.bss_size)?;
124        Ok(())
125    }
126}
127
128impl Display for AutoloadKind {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        match self {
131            AutoloadKind::Itcm => write!(f, "ITCM"),
132            AutoloadKind::Dtcm => write!(f, "DTCM"),
133            AutoloadKind::Unknown(_) => write!(f, "Unknown"),
134        }
135    }
136}