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