src/ds/memory.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "memory.h"
7
8#include "arm/macros.h"
9
10#include "ds/ds.h"
11#include "util/math.h"
12#include "util/memory.h"
13
14mLOG_DEFINE_CATEGORY(DS_MEM, "DS Memory");
15
16#define LDM_LOOP(LDM) \
17 for (i = 0; i < 16; i += 4) { \
18 if (UNLIKELY(mask & (1 << i))) { \
19 LDM; \
20 cpu->gprs[i] = value; \
21 ++wait; \
22 address += 4; \
23 } \
24 if (UNLIKELY(mask & (2 << i))) { \
25 LDM; \
26 cpu->gprs[i + 1] = value; \
27 ++wait; \
28 address += 4; \
29 } \
30 if (UNLIKELY(mask & (4 << i))) { \
31 LDM; \
32 cpu->gprs[i + 2] = value; \
33 ++wait; \
34 address += 4; \
35 } \
36 if (UNLIKELY(mask & (8 << i))) { \
37 LDM; \
38 cpu->gprs[i + 3] = value; \
39 ++wait; \
40 address += 4; \
41 } \
42 }
43
44#define STM_LOOP(STM) \
45 for (i = 0; i < 16; i += 4) { \
46 if (UNLIKELY(mask & (1 << i))) { \
47 value = cpu->gprs[i]; \
48 STM; \
49 ++wait; \
50 address += 4; \
51 } \
52 if (UNLIKELY(mask & (2 << i))) { \
53 value = cpu->gprs[i + 1]; \
54 STM; \
55 ++wait; \
56 address += 4; \
57 } \
58 if (UNLIKELY(mask & (4 << i))) { \
59 value = cpu->gprs[i + 2]; \
60 STM; \
61 ++wait; \
62 address += 4; \
63 } \
64 if (UNLIKELY(mask & (8 << i))) { \
65 value = cpu->gprs[i + 3]; \
66 STM; \
67 ++wait; \
68 address += 4; \
69 } \
70 }
71
72
73static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
74
75static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t region);
76static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t region);
77static int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait);
78
79static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
80
81void DSMemoryInit(struct DS* ds) {
82 struct ARMCore* arm7 = ds->arm7;
83 arm7->memory.load32 = DS7Load32;
84 arm7->memory.load16 = DS7Load16;
85 arm7->memory.load8 = DS7Load8;
86 arm7->memory.loadMultiple = DS7LoadMultiple;
87 arm7->memory.store32 = DS7Store32;
88 arm7->memory.store16 = DS7Store16;
89 arm7->memory.store8 = DS7Store8;
90 arm7->memory.storeMultiple = DS7StoreMultiple;
91 arm7->memory.stall = DSMemoryStall;
92
93 struct ARMCore* arm9 = ds->arm9;
94 arm9->memory.load32 = DS9Load32;
95 arm9->memory.load16 = DS9Load16;
96 arm9->memory.load8 = DS9Load8;
97 arm9->memory.loadMultiple = DS9LoadMultiple;
98 arm9->memory.store32 = DS9Store32;
99 arm9->memory.store16 = DS9Store16;
100 arm9->memory.store8 = DS9Store8;
101 arm9->memory.storeMultiple = DS9StoreMultiple;
102 arm9->memory.stall = DSMemoryStall;
103
104 ds->memory.bios7 = NULL;
105 ds->memory.bios9 = NULL;
106 ds->memory.wram = NULL;
107 ds->memory.ram = NULL;
108 ds->memory.rom = NULL;
109
110 ds->memory.activeRegion7 = -1;
111 ds->memory.activeRegion9 = -1;
112
113 arm7->memory.activeRegion = 0;
114 arm7->memory.activeMask = 0;
115 arm7->memory.setActiveRegion = DS7SetActiveRegion;
116 arm7->memory.activeSeqCycles32 = 0;
117 arm7->memory.activeSeqCycles16 = 0;
118 arm7->memory.activeNonseqCycles32 = 0;
119 arm7->memory.activeNonseqCycles16 = 0;
120
121 arm9->memory.activeRegion = 0;
122 arm9->memory.activeMask = 0;
123 arm9->memory.setActiveRegion = DS9SetActiveRegion;
124 arm9->memory.activeSeqCycles32 = 0;
125 arm9->memory.activeSeqCycles16 = 0;
126 arm9->memory.activeNonseqCycles32 = 0;
127 arm9->memory.activeNonseqCycles16 = 0;
128}
129
130void DSMemoryDeinit(struct DS* ds) {
131 mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
132 mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
133}
134
135void DSMemoryReset(struct DS* ds) {
136 if (ds->memory.wram) {
137 mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
138 }
139 ds->memory.wram = anonymousMemoryMap(DS_SIZE_WORKING_RAM);
140
141 if (ds->memory.ram) {
142 mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
143 }
144 ds->memory.ram = anonymousMemoryMap(DS_SIZE_RAM);
145
146 memset(ds->memory.dma7, 0, sizeof(ds->memory.dma7));
147 memset(ds->memory.dma9, 0, sizeof(ds->memory.dma9));
148 ds->memory.activeDMA7 = -1;
149 ds->memory.activeDMA9 = -1;
150 ds->memory.nextDMA = INT_MAX;
151 ds->memory.eventDiff = 0;
152
153 if (!ds->memory.wram || !ds->memory.ram) {
154 DSMemoryDeinit(ds);
155 mLOG(DS_MEM, FATAL, "Could not map memory");
156 }
157}
158
159static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
160 struct DS* ds = (struct DS*) cpu->master;
161 struct DSMemory* memory = &ds->memory;
162
163 int newRegion = address >> DS_BASE_OFFSET;
164
165 memory->activeRegion7 = newRegion;
166 switch (newRegion) {
167 case DS_REGION_RAM:
168 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
169 cpu->memory.activeRegion = memory->ram;
170 cpu->memory.activeMask = DS_SIZE_RAM - 1;
171 return;
172 }
173 break;
174 case DS7_REGION_BIOS:
175 if (memory->bios7) {
176 cpu->memory.activeRegion = memory->bios9;
177 cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
178 } else {
179 cpu->memory.activeRegion = _deadbeef;
180 cpu->memory.activeMask = 0;
181 }
182 return;
183 default:
184 break;
185 }
186 cpu->memory.activeRegion = _deadbeef;
187 cpu->memory.activeMask = 0;
188 mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
189 return;
190}
191
192uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
193 struct DS* ds = (struct DS*) cpu->master;
194 struct DSMemory* memory = &ds->memory;
195 uint32_t value = 0;
196 int wait = 0;
197
198 switch (address >> DS_BASE_OFFSET) {
199 case DS_REGION_RAM:
200 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
201 LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
202 }
203 break;
204 default:
205 break;
206 }
207
208 if (cycleCounter) {
209 wait += 2;
210 *cycleCounter += wait;
211 }
212 // Unaligned 32-bit loads are "rotated" so they make some semblance of sense
213 int rotate = (address & 3) << 3;
214 return ROR(value, rotate);
215}
216
217uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
218 struct DS* ds = (struct DS*) cpu->master;
219 struct DSMemory* memory = &ds->memory;
220 uint32_t value = 0;
221 int wait = 0;
222
223 switch (address >> DS_BASE_OFFSET) {
224 default:
225 break;
226 }
227
228 if (cycleCounter) {
229 wait += 2;
230 *cycleCounter += wait;
231 }
232 // Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
233 int rotate = (address & 1) << 3;
234 return ROR(value, rotate);
235}
236
237uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
238 struct DS* ds = (struct DS*) cpu->master;
239 struct DSMemory* memory = &ds->memory;
240 uint32_t value = 0;
241 int wait = 0;
242
243 switch (address >> DS_BASE_OFFSET) {
244 default:
245 break;
246 }
247
248 if (cycleCounter) {
249 wait += 2;
250 *cycleCounter += wait;
251 }
252 return value;
253}
254
255void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
256 struct DS* ds = (struct DS*) cpu->master;
257 struct DSMemory* memory = &ds->memory;
258 int wait = 0;
259
260 switch (address >> DS_BASE_OFFSET) {
261 default:
262 break;
263 }
264
265 if (cycleCounter) {
266 ++wait;
267 *cycleCounter += wait;
268 }
269}
270
271void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
272 struct DS* ds = (struct DS*) cpu->master;
273 struct DSMemory* memory = &ds->memory;
274 int wait = 0;
275
276 switch (address >> DS_BASE_OFFSET) {
277 default:
278 break;
279 }
280
281 if (cycleCounter) {
282 ++wait;
283 *cycleCounter += wait;
284 }
285}
286
287void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
288 struct DS* ds = (struct DS*) cpu->master;
289 struct DSMemory* memory = &ds->memory;
290 int wait = 0;
291
292 switch (address >> DS_BASE_OFFSET) {
293 default:
294 break;
295 }
296
297 if (cycleCounter) {
298 ++wait;
299 *cycleCounter += wait;
300 }
301}
302
303uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
304 struct DS* ds = (struct DS*) cpu->master;
305 struct DSMemory* memory = &ds->memory;
306 uint32_t value;
307 int wait = 0;
308
309 int i;
310 int offset = 4;
311 int popcount = 0;
312 if (direction & LSM_D) {
313 offset = -4;
314 popcount = popcount32(mask);
315 address -= (popcount << 2) - 4;
316 }
317
318 if (direction & LSM_B) {
319 address += offset;
320 }
321
322 uint32_t addressMisalign = address & 0x3;
323 address &= 0xFFFFFFFC;
324
325 switch (address >> DS_BASE_OFFSET) {
326 default:
327 break;
328 }
329
330 if (cycleCounter) {
331 ++wait;
332 *cycleCounter += wait;
333 }
334
335 if (direction & LSM_B) {
336 address -= offset;
337 }
338
339 if (direction & LSM_D) {
340 address -= (popcount << 2) + 4;
341 }
342
343 return address | addressMisalign;
344}
345
346
347uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
348 struct DS* ds = (struct DS*) cpu->master;
349 struct DSMemory* memory = &ds->memory;
350 uint32_t value;
351 int wait = 0;
352
353 int i;
354 int offset = 4;
355 int popcount = 0;
356 if (direction & LSM_D) {
357 offset = -4;
358 popcount = popcount32(mask);
359 address -= (popcount << 2) - 4;
360 }
361
362 if (direction & LSM_B) {
363 address += offset;
364 }
365
366 uint32_t addressMisalign = address & 0x3;
367 address &= 0xFFFFFFFC;
368
369 switch (address >> DS_BASE_OFFSET) {
370 default:
371 break;
372 }
373
374 if (cycleCounter) {
375 *cycleCounter += wait;
376 }
377
378 if (direction & LSM_B) {
379 address -= offset;
380 }
381
382 if (direction & LSM_D) {
383 address -= (popcount << 2) + 4;
384 }
385
386 return address | addressMisalign;
387}
388
389static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
390 struct DS* ds = (struct DS*) cpu->master;
391 struct DSMemory* memory = &ds->memory;
392
393 int newRegion = address >> DS_BASE_OFFSET;
394
395 memory->activeRegion9 = newRegion;
396 switch (newRegion) {
397 case DS_REGION_RAM:
398 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
399 cpu->memory.activeRegion = memory->ram;
400 cpu->memory.activeMask = DS_SIZE_RAM - 1;
401 return;
402 }
403 break;
404 case DS9_REGION_BIOS:
405 // TODO: Mask properly
406 if (memory->bios9) {
407 cpu->memory.activeRegion = memory->bios9;
408 cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
409 } else {
410 cpu->memory.activeRegion = _deadbeef;
411 cpu->memory.activeMask = 0;
412 }
413 return;
414 default:
415 break;
416 }
417 cpu->memory.activeRegion = _deadbeef;
418 cpu->memory.activeMask = 0;
419 mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
420 return;
421}
422
423uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
424 struct DS* ds = (struct DS*) cpu->master;
425 struct DSMemory* memory = &ds->memory;
426 uint32_t value = 0;
427 int wait = 0;
428
429 switch (address >> DS_BASE_OFFSET) {
430 case DS_REGION_RAM:
431 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
432 LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
433 break;
434 }
435 mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
436 break;
437 default:
438 mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
439 break;
440 }
441
442 if (cycleCounter) {
443 wait += 2;
444 *cycleCounter += wait;
445 }
446 // Unaligned 32-bit loads are "rotated" so they make some semblance of sense
447 int rotate = (address & 3) << 3;
448 return ROR(value, rotate);
449}
450
451uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
452 struct DS* ds = (struct DS*) cpu->master;
453 struct DSMemory* memory = &ds->memory;
454 uint32_t value = 0;
455 int wait = 0;
456
457 switch (address >> DS_BASE_OFFSET) {
458 case DS_REGION_RAM:
459 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
460 LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
461 break;
462 }
463 mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
464 default:
465 mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
466 break;
467 }
468
469 if (cycleCounter) {
470 wait += 2;
471 *cycleCounter += wait;
472 }
473 // Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
474 int rotate = (address & 1) << 3;
475 return ROR(value, rotate);
476}
477
478uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
479 struct DS* ds = (struct DS*) cpu->master;
480 struct DSMemory* memory = &ds->memory;
481 uint32_t value = 0;
482 int wait = 0;
483
484 switch (address >> DS_BASE_OFFSET) {
485 case DS_REGION_RAM:
486 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
487 value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
488 break;
489 }
490 mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
491 default:
492 mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
493 break;
494 }
495
496 if (cycleCounter) {
497 wait += 2;
498 *cycleCounter += wait;
499 }
500 return value;
501}
502
503void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
504 struct DS* ds = (struct DS*) cpu->master;
505 struct DSMemory* memory = &ds->memory;
506 int wait = 0;
507
508 switch (address >> DS_BASE_OFFSET) {
509 case DS_REGION_RAM:
510 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
511 STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
512 break;
513 }
514 mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
515 break;
516 default:
517 mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
518 break;
519 }
520
521 if (cycleCounter) {
522 ++wait;
523 *cycleCounter += wait;
524 }
525}
526
527void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
528 struct DS* ds = (struct DS*) cpu->master;
529 struct DSMemory* memory = &ds->memory;
530 int wait = 0;
531
532 switch (address >> DS_BASE_OFFSET) {
533 case DS_REGION_RAM:
534 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
535 STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
536 break;
537 }
538 mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
539 break;
540 default:
541 mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
542 break;
543 }
544
545 if (cycleCounter) {
546 ++wait;
547 *cycleCounter += wait;
548 }
549}
550
551void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
552 struct DS* ds = (struct DS*) cpu->master;
553 struct DSMemory* memory = &ds->memory;
554 int wait = 0;
555
556 switch (address >> DS_BASE_OFFSET) {
557 case DS_REGION_RAM:
558 if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
559 ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
560 break;
561 }
562 mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
563 default:
564 mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
565 break;
566 }
567
568 if (cycleCounter) {
569 ++wait;
570 *cycleCounter += wait;
571 }
572}
573
574uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
575 struct DS* ds = (struct DS*) cpu->master;
576 struct DSMemory* memory = &ds->memory;
577 uint32_t value;
578 int wait = 0;
579
580 int i;
581 int offset = 4;
582 int popcount = 0;
583 if (direction & LSM_D) {
584 offset = -4;
585 popcount = popcount32(mask);
586 address -= (popcount << 2) - 4;
587 }
588
589 if (direction & LSM_B) {
590 address += offset;
591 }
592
593 uint32_t addressMisalign = address & 0x3;
594 address &= 0xFFFFFFFC;
595
596 switch (address >> DS_BASE_OFFSET) {
597 case DS_REGION_RAM:
598 LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
599 LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
600 } else {
601 mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
602 });
603 break;
604 default:
605 mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
606 LDM_LOOP(value = 0);
607 break;
608 }
609
610 if (cycleCounter) {
611 ++wait;
612 *cycleCounter += wait;
613 }
614
615 if (direction & LSM_B) {
616 address -= offset;
617 }
618
619 if (direction & LSM_D) {
620 address -= (popcount << 2) + 4;
621 }
622
623 return address | addressMisalign;
624}
625
626
627uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
628 struct DS* ds = (struct DS*) cpu->master;
629 struct DSMemory* memory = &ds->memory;
630 uint32_t value;
631 int wait = 0;
632
633 int i;
634 int offset = 4;
635 int popcount = 0;
636 if (direction & LSM_D) {
637 offset = -4;
638 popcount = popcount32(mask);
639 address -= (popcount << 2) - 4;
640 }
641
642 if (direction & LSM_B) {
643 address += offset;
644 }
645
646 uint32_t addressMisalign = address & 0x3;
647 address &= 0xFFFFFFFC;
648
649 switch (address >> DS_BASE_OFFSET) {
650 case DS_REGION_RAM:
651 STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
652 STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
653 } else {
654 mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
655 });
656 break;
657 default:
658 mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
659 STM_LOOP();
660 break;
661 }
662
663 if (cycleCounter) {
664 *cycleCounter += wait;
665 }
666
667 if (direction & LSM_B) {
668 address -= offset;
669 }
670
671 if (direction & LSM_D) {
672 address -= (popcount << 2) + 4;
673 }
674
675 return address | addressMisalign;
676}
677
678int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
679 return wait;
680}
681