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}