Skip to main content

gcrecomp_runtime/memory/
ram.rs

1//! Main RAM Simulation (24MB)
2//!
3//! This module provides the main RAM implementation for the GameCube recompiler runtime.
4//! The GameCube has 24MB of main RAM, mapped to addresses 0x80000000-0x817FFFFF.
5//!
6//! # Memory Optimizations
7//! - Removed redundant `size` field (can be derived from `data.len()`)
8//! - All read/write functions use `#[inline]` for hot-path optimization
9//! - Explicit bounds checks with early returns for better branch prediction
10//!
11//! # Address Translation
12//! GameCube uses 24-bit addressing for RAM (addresses 0x00000000-0x00FFFFFF).
13//! The upper 8 bits are masked off to get the physical RAM offset.
14
15use anyhow::Result;
16
17/// Main RAM implementation (24MB).
18///
19/// # Memory Layout
20/// - `data`: 24MB byte array (heap allocation required for large size)
21/// - No redundant `size` field (derived from `data.len()`)
22///
23/// # Address Space
24/// GameCube RAM is mapped to:
25/// - Physical addresses: 0x00000000-0x00FFFFFF (24MB)
26/// - Virtual addresses: 0x80000000-0x817FFFFF (24MB)
27#[derive(Debug)]
28pub struct Ram {
29    /// RAM data (24MB)
30    data: Vec<u8>,
31}
32
33impl Ram {
34    /// Create a new RAM instance with 24MB of memory.
35    ///
36    /// # Returns
37    /// `Ram` - Initialized RAM instance with all bytes set to 0
38    ///
39    /// # Examples
40    /// ```rust
41    /// let ram = Ram::new();
42    /// ```
43    #[inline] // Constructor - simple, may be inlined
44    pub fn new() -> Self {
45        const RAM_SIZE: usize = 24usize * 1024usize * 1024usize; // 24MB
46        Self {
47            data: vec![0u8; RAM_SIZE],
48        }
49    }
50
51    /// Read a single byte from RAM.
52    ///
53    /// # Arguments
54    /// * `address` - 32-bit address (24-bit addressing, upper 8 bits masked)
55    ///
56    /// # Returns
57    /// `Result<u8>` - Byte value at address, or error if out of bounds
58    ///
59    /// # Errors
60    /// Returns error if address is out of bounds (>= 24MB)
61    ///
62    /// # Examples
63    /// ```rust
64    /// let value = ram.read_u8(0x80000000)?;
65    /// ```
66    #[inline(always)] // Hot path - always inline for performance
67    pub fn read_u8(&self, address: u32) -> Result<u8> {
68        let addr: usize = (address & 0x00FFFFFFu32) as usize; // 24-bit addressing
69        if addr < self.data.len() {
70            Ok(self.data[addr])
71        } else {
72            anyhow::bail!("RAM read out of bounds: 0x{:08X}", address);
73        }
74    }
75
76    /// Read a 16-bit word (big-endian) from RAM.
77    ///
78    /// # Arguments
79    /// * `address` - 32-bit address (must be aligned, but we don't enforce it)
80    ///
81    /// # Returns
82    /// `Result<u16>` - 16-bit value at address, or error if out of bounds
83    ///
84    /// # Errors
85    /// Returns error if address+1 is out of bounds
86    ///
87    /// # Examples
88    /// ```rust
89    /// let value = ram.read_u16(0x80000000)?;
90    /// ```
91    #[inline] // Hot path - may be inlined
92    pub fn read_u16(&self, address: u32) -> Result<u16> {
93        let low: u8 = self.read_u8(address)?;
94        let high: u8 = self.read_u8(address.wrapping_add(1u32))?;
95        Ok(u16::from_be_bytes([high, low]))
96    }
97
98    /// Read a 32-bit word (big-endian) from RAM.
99    ///
100    /// # Arguments
101    /// * `address` - 32-bit address (must be aligned, but we don't enforce it)
102    ///
103    /// # Returns
104    /// `Result<u32>` - 32-bit value at address, or error if out of bounds
105    ///
106    /// # Errors
107    /// Returns error if address+3 is out of bounds
108    ///
109    /// # Examples
110    /// ```rust
111    /// let value = ram.read_u32(0x80000000)?;
112    /// ```
113    #[inline] // Hot path - may be inlined
114    pub fn read_u32(&self, address: u32) -> Result<u32> {
115        let bytes: [u8; 4] = [
116            self.read_u8(address)?,
117            self.read_u8(address.wrapping_add(1u32))?,
118            self.read_u8(address.wrapping_add(2u32))?,
119            self.read_u8(address.wrapping_add(3u32))?,
120        ];
121        Ok(u32::from_be_bytes(bytes))
122    }
123
124    /// Write a single byte to RAM.
125    ///
126    /// # Arguments
127    /// * `address` - 32-bit address (24-bit addressing, upper 8 bits masked)
128    /// * `value` - Byte value to write
129    ///
130    /// # Returns
131    /// `Result<()>` - Success, or error if out of bounds
132    ///
133    /// # Errors
134    /// Returns error if address is out of bounds (>= 24MB)
135    ///
136    /// # Examples
137    /// ```rust
138    /// ram.write_u8(0x80000000, 0x42)?;
139    /// ```
140    #[inline(always)] // Hot path - always inline for performance
141    pub fn write_u8(&mut self, address: u32, value: u8) -> Result<()> {
142        let addr: usize = (address & 0x00FFFFFFu32) as usize;
143        if addr < self.data.len() {
144            self.data[addr] = value;
145            Ok(())
146        } else {
147            anyhow::bail!("RAM write out of bounds: 0x{:08X}", address);
148        }
149    }
150
151    /// Write a 16-bit word (big-endian) to RAM.
152    ///
153    /// # Arguments
154    /// * `address` - 32-bit address (must be aligned, but we don't enforce it)
155    /// * `value` - 16-bit value to write
156    ///
157    /// # Returns
158    /// `Result<()>` - Success, or error if out of bounds
159    ///
160    /// # Errors
161    /// Returns error if address+1 is out of bounds
162    ///
163    /// # Examples
164    /// ```rust
165    /// ram.write_u16(0x80000000, 0x1234)?;
166    /// ```
167    #[inline] // Hot path - may be inlined
168    pub fn write_u16(&mut self, address: u32, value: u16) -> Result<()> {
169        let bytes: [u8; 2] = value.to_be_bytes();
170        self.write_u8(address, bytes[0])?;
171        self.write_u8(address.wrapping_add(1u32), bytes[1])?;
172        Ok(())
173    }
174
175    /// Write a 32-bit word (big-endian) to RAM.
176    ///
177    /// # Arguments
178    /// * `address` - 32-bit address (must be aligned, but we don't enforce it)
179    /// * `value` - 32-bit value to write
180    ///
181    /// # Returns
182    /// `Result<()>` - Success, or error if out of bounds
183    ///
184    /// # Errors
185    /// Returns error if address+3 is out of bounds
186    ///
187    /// # Examples
188    /// ```rust
189    /// ram.write_u32(0x80000000, 0x12345678)?;
190    /// ```
191    #[inline] // Hot path - may be inlined
192    pub fn write_u32(&mut self, address: u32, value: u32) -> Result<()> {
193        let bytes: [u8; 4] = value.to_be_bytes();
194        self.write_u8(address, bytes[0])?;
195        self.write_u8(address.wrapping_add(1u32), bytes[1])?;
196        self.write_u8(address.wrapping_add(2u32), bytes[2])?;
197        self.write_u8(address.wrapping_add(3u32), bytes[3])?;
198        Ok(())
199    }
200
201    /// Read multiple bytes from RAM.
202    ///
203    /// # Arguments
204    /// * `address` - 32-bit address (24-bit addressing, upper 8 bits masked)
205    /// * `len` - Number of bytes to read
206    ///
207    /// # Returns
208    /// `Result<Vec<u8>>` - Byte vector, or error if out of bounds
209    ///
210    /// # Errors
211    /// Returns error if address+len is out of bounds
212    ///
213    /// # Examples
214    /// ```rust
215    /// let data = ram.read_bytes(0x80000000, 1024)?;
216    /// ```
217    #[inline] // May be inlined for small lengths
218    pub fn read_bytes(&self, address: u32, len: usize) -> Result<Vec<u8>> {
219        let addr: usize = (address & 0x00FFFFFFu32) as usize;
220        if addr.wrapping_add(len) <= self.data.len() {
221            Ok(self.data[addr..addr.wrapping_add(len)].to_vec())
222        } else {
223            anyhow::bail!("RAM read out of bounds: 0x{:08X} len {}", address, len);
224        }
225    }
226
227    /// Write multiple bytes to RAM.
228    ///
229    /// # Arguments
230    /// * `address` - 32-bit address (24-bit addressing, upper 8 bits masked)
231    /// * `data` - Byte slice to write
232    ///
233    /// # Returns
234    /// `Result<()>` - Success, or error if out of bounds
235    ///
236    /// # Errors
237    /// Returns error if address+data.len() is out of bounds
238    ///
239    /// # Examples
240    /// ```rust
241    /// ram.write_bytes(0x80000000, &[0x42, 0x43, 0x44])?;
242    /// ```
243    #[inline] // May be inlined for small lengths
244    pub fn write_bytes(&mut self, address: u32, data: &[u8]) -> Result<()> {
245        let addr: usize = (address & 0x00FFFFFFu32) as usize;
246        if addr.wrapping_add(data.len()) <= self.data.len() {
247            self.data[addr..addr.wrapping_add(data.len())].copy_from_slice(data);
248            Ok(())
249        } else {
250            anyhow::bail!(
251                "RAM write out of bounds: 0x{:08X} len {}",
252                address,
253                data.len()
254            );
255        }
256    }
257}
258
259impl Default for Ram {
260    #[inline] // Simple default implementation
261    fn default() -> Self {
262        Self::new()
263    }
264}