1use crate::MemoryInfo;
2use crate::config::{Dipsw, PAGE_SHIFT};
3use crate::context::config;
4use alloc::sync::Arc;
5use core::num::NonZero;
6
7pub struct Dmem {
9 mode: usize,
10 config: &'static DmemConfig,
11}
12
13impl Dmem {
14 pub fn new(mi: &mut MemoryInfo) -> Arc<Self> {
21 let haddr = Self::reserve_phys(mi, 0x1000000.try_into().unwrap(), 1);
23
24 if haddr == 0 {
25 panic!("no available memory for high-address memory");
26 }
27
28 let mode = Self::load_mode(mi);
30
31 if (0x80C6C0C4u64 & (1 << mode)) != 0 {
32 panic!("game DMEM size is not configured");
33 }
34
35 let dc = DMEM_CONFIGS[mode].as_ref().unwrap();
37 let game = Self::reserve_phys(mi, dc.game_size, 0x8000000);
38
39 if game == 0 {
40 panic!("not enough memory for game DMEM");
41 }
42
43 let game_end = game + dc.game_size.get();
45
46 if (0x7F393733u64 & (1 << mode)) != 0 {
47 let align = if config().unknown_dmem1() == 0 {
49 0x8000000i64
50 } else {
51 0x200000i64
52 };
53
54 let size = dc.mini_size;
56 let mini = match dc.mini_shared {
57 true => (-align) as u64 & (game_end - (dc.fmem_max.get() + size)),
58 false => todo!(),
59 };
60
61 if mini == 0 {
62 panic!("not enough memory for mini-app DMEM");
63 }
64
65 }
67
68 if (0x7F393F3Bu64 & (1 << mode)) != 0 {
69 let size = dc.vsh_size;
70 let vsh = Self::reserve_phys(mi, size, 0x200000);
71
72 if vsh == 0 {
73 panic!("not enough memory for VSH DMEM");
74 }
75
76 }
79
80 if (0x47000703u64 & (1 << mode)) != 0 {
81 todo!()
82 }
83
84 if (0x47010703u64 & (1 << mode)) != 0 {
86 todo!()
87 }
88
89 let vision = Self::reserve_phys(mi, 0x1000000.try_into().unwrap(), 0x200000);
91
92 if vision == 0 {
93 panic!("not enough memory for vision DMEM");
94 }
95
96 let mut unk = dc.unk1;
99
100 if ((0x80C6C0C4u64 + 0x20212022u64) & (1 << mode)) == 0 {
101 loop {
102 let (size, align) = if unk < 0x200000 {
103 (unk, 0x4000)
105 } else {
106 (0x200000, 0x200000)
107 };
108
109 let addr = Self::reserve_phys(mi, size.try_into().unwrap(), align);
111
112 if addr == 0 {
113 panic!("failed to reserve memory");
115 }
116
117 unk -= size;
119
120 if unk == 0 {
121 break;
122 }
123 }
124 }
125
126 Self::adjust_pmap(mi, game_end - dc.fmem_max.get(), game_end);
127 Self::adjust_pmap(
128 mi,
129 haddr,
130 haddr + 0xC00000 + if mi.unk != 0 { 0x400000 } else { 0 },
131 );
132
133 mi.end_page = mi.physmap[mi.physmap_last + 1] >> PAGE_SHIFT;
134
135 Arc::new(Self { mode, config: dc })
136 }
137
138 pub fn mode(&self) -> usize {
139 self.mode
140 }
141
142 pub fn config(&self) -> &'static DmemConfig {
143 self.config
144 }
145
146 fn reserve_phys(mi: &mut MemoryInfo, size: NonZero<u64>, align: i64) -> u64 {
151 let mut i = mi.physmap_last;
152
153 loop {
154 let start = mi.physmap[i];
155 let end = mi.physmap[i + 1];
156 let addr = (end - size.get()) & ((-align) as u64);
157
158 if addr >= start {
159 let aligned_end = addr + size.get();
160
161 if (addr == start) && (aligned_end == end) {
163 mi.physmap.copy_within((i + 2).., i);
164 mi.physmap_last -= 2;
165 return addr;
166 }
167
168 if (addr != start) && (aligned_end != end) {
170 mi.physmap.copy_within(i..(mi.physmap_last + 2), i + 2);
171 mi.physmap[i + 1] = addr;
172 mi.physmap[i + 2] = aligned_end;
173 mi.physmap_last += 2;
174 return addr;
175 }
176
177 if addr != start {
179 assert_eq!(aligned_end, end);
180 mi.physmap[i + 1] = addr;
181 return addr;
182 }
183
184 mi.physmap[i] = aligned_end;
186
187 return addr;
188 }
189
190 if i < 2 {
192 break 0;
193 }
194
195 i -= 2;
196 }
197 }
198
199 fn adjust_pmap(mi: &mut MemoryInfo, start: u64, end: u64) {
204 let mut idx = 0;
207
208 if start > mi.physmap[0] {
209 let mut found = true;
211
212 for i in (2..).step_by(2) {
213 idx = i;
214
215 if idx > mi.physmap_last {
216 found = false;
217 break;
218 }
219
220 if mi.physmap[idx] >= start {
221 break;
222 }
223 }
224
225 let mut empty = false;
227
228 if found {
229 empty = mi.physmap[idx] == end;
230
231 if (mi.physmap[idx - 1] == start) && (mi.physmap[idx] == end) {
232 todo!()
233 }
234 }
235
236 if mi.physmap[idx - 1] == start {
237 todo!()
238 }
239
240 if !empty {
241 if !found {
242 mi.physmap[idx] = start;
243 mi.physmap[idx | 1] = end;
244 mi.physmap_last += 2;
245 return;
246 }
247
248 todo!()
249 }
250 } else {
251 todo!()
252 }
253
254 mi.physmap[idx] = start;
255 }
256
257 fn load_mode(mi: &MemoryInfo) -> usize {
262 let c = config();
264 let v = if c.unknown_dmem1() == 0 {
265 if c.dipsw(Dipsw::Unk97) {
266 3
268 } else if c.dipsw(Dipsw::Unk0) && !c.dipsw(Dipsw::Unk24) {
269 if c.dipsw(Dipsw::Unk16) && c.dipsw(Dipsw::Unk17) && (mi.unk & 3) == 3 {
271 6
273 } else {
274 todo!()
275 }
276 } else {
277 4
279 }
280 } else {
281 5
283 };
284
285 v + usize::try_from(mi.unk).unwrap() * 8
286 }
287}
288
289pub struct DmemConfig {
291 pub name: &'static str,
292 pub game_size: NonZero<u64>,
293 pub fmem_max: NonZero<u64>,
294 pub mini_size: u64,
295 pub mini_shared: bool,
296 pub vsh_size: NonZero<u64>,
297 pub unk1: u64,
298}
299
300static DMEM_CONFIGS: [Option<DmemConfig>; 21] = [
302 Some(DmemConfig {
303 name: "BC8 normal",
304 game_size: NonZero::new(0x148000000).unwrap(),
305 fmem_max: NonZero::new(0x40000000).unwrap(),
306 mini_size: 0x30000000,
307 mini_shared: true,
308 vsh_size: NonZero::new(0x17C00000).unwrap(),
309 unk1: 0x13600000,
310 }),
311 Some(DmemConfig {
312 name: "BC8 large",
313 game_size: NonZero::new(0x170000000).unwrap(),
314 fmem_max: NonZero::new(0x40000000).unwrap(),
315 mini_size: 0x30000000,
316 mini_shared: true,
317 vsh_size: NonZero::new(0x16C00000).unwrap(),
318 unk1: 0,
319 }),
320 None,
321 Some(DmemConfig {
322 name: "BC8 kratos",
323 game_size: NonZero::new(0x148000000).unwrap(),
324 fmem_max: NonZero::new(0x40000000).unwrap(),
325 mini_size: 0,
326 mini_shared: false,
327 vsh_size: NonZero::new(0x1CA00000).unwrap(),
328 unk1: 0xA00000,
329 }),
330 Some(DmemConfig {
331 name: "BC8 release",
332 game_size: NonZero::new(0x148000000).unwrap(),
333 fmem_max: NonZero::new(0x40000000).unwrap(),
334 mini_size: 0x30000000,
335 mini_shared: false,
336 vsh_size: NonZero::new(0x1A800000).unwrap(),
337 unk1: 0xA00000,
338 }),
339 Some(DmemConfig {
340 name: "BC8 CS",
341 game_size: NonZero::new(0x124000000).unwrap(),
342 fmem_max: NonZero::new(0x4000000).unwrap(),
343 mini_size: 0x58800000,
344 mini_shared: false,
345 vsh_size: NonZero::new(0x28200000).unwrap(),
346 unk1: 0,
347 }),
348 None,
349 None,
350 Some(DmemConfig {
351 name: "BC16 normal",
352 game_size: NonZero::new(0x148000000).unwrap(),
353 fmem_max: NonZero::new(0x40000000).unwrap(),
354 mini_size: 0x30000000,
355 mini_shared: false,
356 vsh_size: NonZero::new(0x1C200000).unwrap(),
357 unk1: 0x17FB00000,
358 }),
359 Some(DmemConfig {
360 name: "BC16 large",
361 game_size: NonZero::new(0x28C000000).unwrap(),
362 fmem_max: NonZero::new(0x5C000000).unwrap(),
363 mini_size: 0x30000000,
364 mini_shared: false,
365 vsh_size: NonZero::new(0x1B200000).unwrap(),
366 unk1: 0x3CB00000,
367 }),
368 Some(DmemConfig {
369 name: "BC16 mini-app large",
370 game_size: NonZero::new(0x148000000).unwrap(),
371 fmem_max: NonZero::new(0x40000000).unwrap(),
372 mini_size: 0x48000000,
373 mini_shared: false,
374 vsh_size: NonZero::new(0x19600000).unwrap(),
375 unk1: 0x13A700000,
376 }),
377 Some(DmemConfig {
378 name: "BC16 kratos",
379 game_size: NonZero::new(0x148000000).unwrap(),
380 fmem_max: NonZero::new(0x40000000).unwrap(),
381 mini_size: 0,
382 mini_shared: false,
383 vsh_size: NonZero::new(0x1CA00000).unwrap(),
384 unk1: 0x1F3C00000,
385 }),
386 Some(DmemConfig {
387 name: "BC16 release",
388 game_size: NonZero::new(0x148000000).unwrap(),
389 fmem_max: NonZero::new(0x40000000).unwrap(),
390 mini_size: 0x30000000,
391 mini_shared: false,
392 vsh_size: NonZero::new(0x1C200000).unwrap(),
393 unk1: 0x1FF600000,
394 }),
395 Some(DmemConfig {
396 name: "BC16 CS",
397 game_size: NonZero::new(0x324000000).unwrap(),
398 fmem_max: NonZero::new(0x4000000).unwrap(),
399 mini_size: 0x58000000,
400 mini_shared: false,
401 vsh_size: NonZero::new(0x28000000).unwrap(),
402 unk1: 0,
403 }),
404 None,
405 None,
406 Some(DmemConfig {
407 name: "GL8 normal",
408 game_size: NonZero::new(0x170000000).unwrap(),
409 fmem_max: NonZero::new(0x40000000).unwrap(),
410 mini_size: 0x70000000,
411 mini_shared: true,
412 vsh_size: NonZero::new(0x27C00000).unwrap(),
413 unk1: 0,
414 }),
415 None,
416 None,
417 Some(DmemConfig {
418 name: "GL8 kratos",
419 game_size: NonZero::new(0x170000000).unwrap(),
420 fmem_max: NonZero::new(0x40000000).unwrap(),
421 mini_size: 0x70000000,
422 mini_shared: true,
423 vsh_size: NonZero::new(0x27C00000).unwrap(),
424 unk1: 0xA00000,
425 }),
426 Some(DmemConfig {
427 name: "GL8 release",
428 game_size: NonZero::new(0x170000000).unwrap(),
429 fmem_max: NonZero::new(0x40000000).unwrap(),
430 mini_size: 0x70000000,
431 mini_shared: true,
432 vsh_size: NonZero::new(0x28000000).unwrap(),
433 unk1: 0xA00000,
434 }),
435];