all repos — mgba @ 0053f85922a69fade4c543e53d8ef6e90c6e0a6b

mGBA Game Boy Advance Emulator

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 <mgba/internal/ds/memory.h>
  7
  8#include <mgba/internal/arm/macros.h>
  9
 10#include <mgba/internal/ds/ds.h>
 11#include <mgba/internal/ds/io.h>
 12#include <mgba-util/math.h>
 13#include <mgba-util/memory.h>
 14
 15mLOG_DEFINE_CATEGORY(DS_MEM, "DS Memory");
 16
 17#define LDM_LOOP(LDM) \
 18	for (i = 0; i < 16; i += 4) { \
 19		if (UNLIKELY(mask & (1 << i))) { \
 20			LDM; \
 21			cpu->gprs[i] = value; \
 22			++wait; \
 23			address += 4; \
 24		} \
 25		if (UNLIKELY(mask & (2 << i))) { \
 26			LDM; \
 27			cpu->gprs[i + 1] = value; \
 28			++wait; \
 29			address += 4; \
 30		} \
 31		if (UNLIKELY(mask & (4 << i))) { \
 32			LDM; \
 33			cpu->gprs[i + 2] = value; \
 34			++wait; \
 35			address += 4; \
 36		} \
 37		if (UNLIKELY(mask & (8 << i))) { \
 38			LDM; \
 39			cpu->gprs[i + 3] = value; \
 40			++wait; \
 41			address += 4; \
 42		} \
 43	}
 44
 45#define STM_LOOP(STM) \
 46	for (i = 0; i < 16; i += 4) { \
 47		if (UNLIKELY(mask & (1 << i))) { \
 48			value = cpu->gprs[i]; \
 49			STM; \
 50			++wait; \
 51			address += 4; \
 52		} \
 53		if (UNLIKELY(mask & (2 << i))) { \
 54			value = cpu->gprs[i + 1]; \
 55			STM; \
 56			++wait; \
 57			address += 4; \
 58		} \
 59		if (UNLIKELY(mask & (4 << i))) { \
 60			value = cpu->gprs[i + 2]; \
 61			STM; \
 62			++wait; \
 63			address += 4; \
 64		} \
 65		if (UNLIKELY(mask & (8 << i))) { \
 66			value = cpu->gprs[i + 3]; \
 67			STM; \
 68			++wait; \
 69			address += 4; \
 70		} \
 71	}
 72
 73
 74static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
 75
 76static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t region);
 77static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t region);
 78static int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait);
 79
 80static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
 81
 82void DSMemoryInit(struct DS* ds) {
 83	struct ARMCore* arm7 = ds->ds7.cpu;
 84	arm7->memory.load32 = DS7Load32;
 85	arm7->memory.load16 = DS7Load16;
 86	arm7->memory.load8 = DS7Load8;
 87	arm7->memory.loadMultiple = DS7LoadMultiple;
 88	arm7->memory.store32 = DS7Store32;
 89	arm7->memory.store16 = DS7Store16;
 90	arm7->memory.store8 = DS7Store8;
 91	arm7->memory.storeMultiple = DS7StoreMultiple;
 92	arm7->memory.stall = DSMemoryStall;
 93
 94	struct ARMCore* arm9 = ds->ds9.cpu;
 95	arm9->memory.load32 = DS9Load32;
 96	arm9->memory.load16 = DS9Load16;
 97	arm9->memory.load8 = DS9Load8;
 98	arm9->memory.loadMultiple = DS9LoadMultiple;
 99	arm9->memory.store32 = DS9Store32;
100	arm9->memory.store16 = DS9Store16;
101	arm9->memory.store8 = DS9Store8;
102	arm9->memory.storeMultiple = DS9StoreMultiple;
103	arm9->memory.stall = DSMemoryStall;
104
105	ds->memory.bios7 = NULL;
106	ds->memory.bios9 = NULL;
107	ds->memory.wram = NULL;
108	ds->memory.wram7 = NULL;
109	ds->memory.ram = NULL;
110	ds->memory.itcm = NULL;
111	ds->memory.dtcm = NULL;
112	ds->memory.rom = NULL;
113
114	ds->ds7.memory.activeRegion = -1;
115	ds->ds9.memory.activeRegion = -1;
116	ds->ds7.memory.io = ds->memory.io7;
117	ds->ds9.memory.io = ds->memory.io9;
118
119	arm7->memory.activeRegion = 0;
120	arm7->memory.activeMask = 0;
121	arm7->memory.setActiveRegion = DS7SetActiveRegion;
122	arm7->memory.activeSeqCycles32 = 0;
123	arm7->memory.activeSeqCycles16 = 0;
124	arm7->memory.activeNonseqCycles32 = 0;
125	arm7->memory.activeNonseqCycles16 = 0;
126
127	arm9->memory.activeRegion = 0;
128	arm9->memory.activeMask = 0;
129	arm9->memory.setActiveRegion = DS9SetActiveRegion;
130	arm9->memory.activeSeqCycles32 = 0;
131	arm9->memory.activeSeqCycles16 = 0;
132	arm9->memory.activeNonseqCycles32 = 0;
133	arm9->memory.activeNonseqCycles16 = 0;
134}
135
136void DSMemoryDeinit(struct DS* ds) {
137	mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
138	mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
139	mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
140	mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
141	mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
142}
143
144void DSMemoryReset(struct DS* ds) {
145	if (ds->memory.wram) {
146		mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
147	}
148	ds->memory.wram = anonymousMemoryMap(DS_SIZE_WORKING_RAM);
149
150	if (ds->memory.wram7) {
151		mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
152	}
153	ds->memory.wram7 = anonymousMemoryMap(DS7_SIZE_WORKING_RAM);
154
155	if (ds->memory.ram) {
156		mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
157	}
158	ds->memory.ram = anonymousMemoryMap(DS_SIZE_RAM);
159
160	if (ds->memory.itcm) {
161		mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
162	}
163	ds->memory.itcm = anonymousMemoryMap(DS9_SIZE_ITCM);
164
165	if (ds->memory.dtcm) {
166		mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
167	}
168	ds->memory.dtcm = anonymousMemoryMap(DS9_SIZE_DTCM);
169
170	memset(ds->ds7.memory.dma, 0, sizeof(ds->ds7.memory.dma));
171	memset(ds->ds9.memory.dma, 0, sizeof(ds->ds9.memory.dma));
172	ds->ds7.memory.activeDMA = -1;
173	ds->ds9.memory.activeDMA = -1;
174
175	// TODO: Correct size
176	ds->memory.wramSize7 = 0x8000;
177	ds->memory.wramSize9 = 0;
178
179	if (!ds->memory.wram || !ds->memory.wram7 || !ds->memory.ram || !ds->memory.itcm || !ds->memory.dtcm) {
180		DSMemoryDeinit(ds);
181		mLOG(DS_MEM, FATAL, "Could not map memory");
182	}
183}
184
185static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
186	struct DS* ds = (struct DS*) cpu->master;
187	struct DSMemory* memory = &ds->memory;
188
189	int newRegion = address >> DS_BASE_OFFSET;
190
191	ds->ds7.memory.activeRegion = newRegion;
192	switch (newRegion) {
193	case DS_REGION_WORKING_RAM:
194		if (address >= DS7_BASE_WORKING_RAM || !memory->wramSize7) {
195			cpu->memory.activeRegion = memory->wram7;
196			cpu->memory.activeMask = DS7_SIZE_WORKING_RAM - 1;
197		} else {
198			cpu->memory.activeRegion = memory->wram;
199			cpu->memory.activeMask = ds->memory.wramSize7 - 1;
200		}
201		return;
202	case DS_REGION_RAM:
203		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
204			cpu->memory.activeRegion = memory->ram;
205			cpu->memory.activeMask = DS_SIZE_RAM - 1;
206			return;
207		}
208		break;
209	case DS7_REGION_BIOS:
210		if (memory->bios7) {
211			cpu->memory.activeRegion = memory->bios7;
212			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
213		} else {
214			cpu->memory.activeRegion = _deadbeef;
215			cpu->memory.activeMask = 0;
216		}
217		return;
218	default:
219		break;
220	}
221	cpu->memory.activeRegion = _deadbeef;
222	cpu->memory.activeMask = 0;
223	mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
224	return;
225}
226
227uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
228	struct DS* ds = (struct DS*) cpu->master;
229	struct DSMemory* memory = &ds->memory;
230	uint32_t value = 0;
231	int wait = 0;
232
233	switch (address >> DS_BASE_OFFSET) {
234	case DS7_REGION_BIOS:
235		LOAD_32(value, address & (DS7_SIZE_BIOS - 1), memory->bios7);
236		break;
237	case DS_REGION_WORKING_RAM:
238		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
239			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
240		} else {
241			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
242		}
243		break;
244	case DS_REGION_RAM:
245		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
246			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
247			break;
248		}
249		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
250		break;
251	case DS_REGION_IO:
252		value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
253		break;
254	default:
255		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
256		break;
257	}
258
259	if (cycleCounter) {
260		wait += 2;
261		*cycleCounter += wait;
262	}
263	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
264	int rotate = (address & 3) << 3;
265	return ROR(value, rotate);
266}
267
268uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
269	struct DS* ds = (struct DS*) cpu->master;
270	struct DSMemory* memory = &ds->memory;
271	uint32_t value = 0;
272	int wait = 0;
273
274	switch (address >> DS_BASE_OFFSET) {
275	case DS7_REGION_BIOS:
276		LOAD_16(value, address & (DS7_SIZE_BIOS - 1), memory->bios7);
277		break;
278	case DS_REGION_WORKING_RAM:
279		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
280			LOAD_16(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
281		} else {
282			LOAD_16(value, address & (ds->memory.wramSize7 - 1), memory->wram);
283		}
284		break;
285	case DS_REGION_RAM:
286		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
287			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
288			break;
289		}
290		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
291	case DS_REGION_IO:
292		value = DS7IORead(ds, address & 0x00FFFFFF);
293		break;
294	default:
295		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
296		break;
297	}
298
299	if (cycleCounter) {
300		wait += 2;
301		*cycleCounter += wait;
302	}
303	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
304	int rotate = (address & 1) << 3;
305	return ROR(value, rotate);
306}
307
308uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
309	struct DS* ds = (struct DS*) cpu->master;
310	struct DSMemory* memory = &ds->memory;
311	uint32_t value = 0;
312	int wait = 0;
313
314	switch (address >> DS_BASE_OFFSET) {
315	case DS_REGION_RAM:
316		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
317			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
318			break;
319		}
320		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
321	default:
322		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
323		break;
324	}
325
326	if (cycleCounter) {
327		wait += 2;
328		*cycleCounter += wait;
329	}
330	return value;
331}
332
333void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
334	struct DS* ds = (struct DS*) cpu->master;
335	struct DSMemory* memory = &ds->memory;
336	int wait = 0;
337
338	switch (address >> DS_BASE_OFFSET) {
339	case DS_REGION_WORKING_RAM:
340		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
341			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
342		} else {
343			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
344		}
345		break;
346	case DS_REGION_RAM:
347		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
348			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
349			break;
350		}
351		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
352		break;
353	case DS_REGION_IO:
354		DS7IOWrite32(ds, address & 0x00FFFFFF, value);
355		break;
356	default:
357		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
358		break;
359	}
360
361	if (cycleCounter) {
362		++wait;
363		*cycleCounter += wait;
364	}
365}
366
367void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
368	struct DS* ds = (struct DS*) cpu->master;
369	struct DSMemory* memory = &ds->memory;
370	int wait = 0;
371
372	switch (address >> DS_BASE_OFFSET) {
373	case DS_REGION_WORKING_RAM:
374		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
375			STORE_16(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
376		} else {
377			STORE_16(value, address & (ds->memory.wramSize7 - 1), memory->wram);
378		}
379		break;
380	case DS_REGION_RAM:
381		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
382			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
383			break;
384		}
385		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
386		break;
387	case DS_REGION_IO:
388		DS7IOWrite(ds, address & 0x00FFFFFF, value);
389		break;
390	default:
391		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
392		break;
393	}
394
395	if (cycleCounter) {
396		++wait;
397		*cycleCounter += wait;
398	}
399}
400
401void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
402	struct DS* ds = (struct DS*) cpu->master;
403	struct DSMemory* memory = &ds->memory;
404	int wait = 0;
405
406	switch (address >> DS_BASE_OFFSET) {
407	case DS_REGION_RAM:
408		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
409			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
410			break;
411		}
412		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
413	case DS_REGION_IO:
414		DS7IOWrite8(ds, address & 0x00FFFFFF, value);
415		break;
416	default:
417		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
418		break;
419	}
420
421	if (cycleCounter) {
422		++wait;
423		*cycleCounter += wait;
424	}
425}
426
427uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
428	struct DS* ds = (struct DS*) cpu->master;
429	struct DSMemory* memory = &ds->memory;
430	uint32_t value;
431	int wait = 0;
432
433	int i;
434	int offset = 4;
435	int popcount = 0;
436	if (direction & LSM_D) {
437		offset = -4;
438		popcount = popcount32(mask);
439		address -= (popcount << 2) - 4;
440	}
441
442	if (direction & LSM_B) {
443		address += offset;
444	}
445
446	uint32_t addressMisalign = address & 0x3;
447	address &= 0xFFFFFFFC;
448
449	switch (address >> DS_BASE_OFFSET) {
450	case DS_REGION_WORKING_RAM:
451		LDM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
452			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
453		} else {
454			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
455		});
456		break;
457	case DS_REGION_RAM:
458		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
459			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
460		} else {
461			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
462		});
463		break;
464	case DS_REGION_IO:
465		LDM_LOOP(value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16));
466		break;
467	default:
468		mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
469		LDM_LOOP(value = 0);
470	}
471
472	if (cycleCounter) {
473		++wait;
474		*cycleCounter += wait;
475	}
476
477	if (direction & LSM_B) {
478		address -= offset;
479	}
480
481	if (direction & LSM_D) {
482		address -= (popcount << 2) + 4;
483	}
484
485	return address | addressMisalign;
486}
487
488
489uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
490	struct DS* ds = (struct DS*) cpu->master;
491	struct DSMemory* memory = &ds->memory;
492	uint32_t value;
493	int wait = 0;
494
495	int i;
496	int offset = 4;
497	int popcount = 0;
498	if (direction & LSM_D) {
499		offset = -4;
500		popcount = popcount32(mask);
501		address -= (popcount << 2) - 4;
502	}
503
504	if (direction & LSM_B) {
505		address += offset;
506	}
507
508	uint32_t addressMisalign = address & 0x3;
509	address &= 0xFFFFFFFC;
510
511	switch (address >> DS_BASE_OFFSET) {
512	case DS_REGION_WORKING_RAM:
513		STM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
514			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
515		} else {
516			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
517		});
518		break;
519	case DS_REGION_RAM:
520		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
521			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
522		} else {
523			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
524		});
525		break;
526	default:
527		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
528		STM_LOOP();
529		break;
530	}
531
532	if (cycleCounter) {
533		*cycleCounter += wait;
534	}
535
536	if (direction & LSM_B) {
537		address -= offset;
538	}
539
540	if (direction & LSM_D) {
541		address -= (popcount << 2) + 4;
542	}
543
544	return address | addressMisalign;
545}
546
547static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
548	struct DS* ds = (struct DS*) cpu->master;
549	struct DSMemory* memory = &ds->memory;
550
551	int newRegion = address >> DS_BASE_OFFSET;
552
553	ds->ds9.memory.activeRegion = newRegion;
554	switch (newRegion) {
555	case DS9_REGION_ITCM:
556	case DS9_REGION_ITCM_MIRROR:
557		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
558			cpu->memory.activeRegion = memory->itcm;
559			cpu->memory.activeMask = DS9_SIZE_ITCM - 1;
560			return;
561		}
562		goto jump_error;
563	case DS_REGION_RAM:
564		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
565			cpu->memory.activeRegion = memory->ram;
566			cpu->memory.activeMask = DS_SIZE_RAM - 1;
567			return;
568		}
569		goto jump_error;
570	case DS9_REGION_BIOS:
571		// TODO: Mask properly
572		if (memory->bios9) {
573			cpu->memory.activeRegion = memory->bios9;
574			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
575		} else {
576			cpu->memory.activeRegion = _deadbeef;
577			cpu->memory.activeMask = 0;
578		}
579		return;
580	default:
581		break;
582	}
583
584jump_error:
585	cpu->memory.activeRegion = _deadbeef;
586	cpu->memory.activeMask = 0;
587	mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
588}
589
590uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
591	struct DS* ds = (struct DS*) cpu->master;
592	struct DSMemory* memory = &ds->memory;
593	uint32_t value = 0;
594	int wait = 0;
595
596	switch (address >> DS_BASE_OFFSET) {
597	case DS_REGION_RAM:
598		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
599			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
600			break;
601		}
602		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
603			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
604			break;
605		}
606		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
607		break;
608	case DS_REGION_IO:
609		value = DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
610		break;
611	default:
612		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
613		break;
614	}
615
616	if (cycleCounter) {
617		wait += 2;
618		*cycleCounter += wait;
619	}
620	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
621	int rotate = (address & 3) << 3;
622	return ROR(value, rotate);
623}
624
625uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
626	struct DS* ds = (struct DS*) cpu->master;
627	struct DSMemory* memory = &ds->memory;
628	uint32_t value = 0;
629	int wait = 0;
630
631	switch (address >> DS_BASE_OFFSET) {
632	case DS_REGION_RAM:
633		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
634			LOAD_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
635			break;
636		}
637		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
638			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
639			break;
640		}
641		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
642	case DS_REGION_IO:
643		value = DS9IORead(ds, address & 0x00FFFFFF);
644		break;
645	default:
646		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
647		break;
648	}
649
650	if (cycleCounter) {
651		wait += 2;
652		*cycleCounter += wait;
653	}
654	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
655	int rotate = (address & 1) << 3;
656	return ROR(value, rotate);
657}
658
659uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
660	struct DS* ds = (struct DS*) cpu->master;
661	struct DSMemory* memory = &ds->memory;
662	uint32_t value = 0;
663	int wait = 0;
664
665	switch (address >> DS_BASE_OFFSET) {
666	case DS_REGION_RAM:
667		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
668			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
669			break;
670		}
671		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
672			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
673			break;
674		}
675		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
676	default:
677		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
678		break;
679	}
680
681	if (cycleCounter) {
682		wait += 2;
683		*cycleCounter += wait;
684	}
685	return value;
686}
687
688void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
689	struct DS* ds = (struct DS*) cpu->master;
690	struct DSMemory* memory = &ds->memory;
691	int wait = 0;
692
693	switch (address >> DS_BASE_OFFSET) {
694	case DS9_REGION_ITCM:
695	case DS9_REGION_ITCM_MIRROR:
696		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
697			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
698			break;
699		}
700		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
701		break;
702	case DS_REGION_RAM:
703		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
704			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
705			break;
706		}
707		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
708			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
709			break;
710		}
711		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
712		break;
713	case DS_REGION_IO:
714		DS9IOWrite32(ds, address & 0x00FFFFFF, value);
715		break;
716	default:
717		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
718		break;
719	}
720
721	if (cycleCounter) {
722		++wait;
723		*cycleCounter += wait;
724	}
725}
726
727void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
728	struct DS* ds = (struct DS*) cpu->master;
729	struct DSMemory* memory = &ds->memory;
730	int wait = 0;
731
732	switch (address >> DS_BASE_OFFSET) {
733	case DS9_REGION_ITCM:
734	case DS9_REGION_ITCM_MIRROR:
735		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
736			STORE_16(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
737			break;
738		}
739		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
740		break;
741	case DS_REGION_RAM:
742		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
743			STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
744			break;
745		}
746		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
747			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
748			break;
749		}
750		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
751		break;
752	case DS_REGION_IO:
753		DS9IOWrite(ds, address & 0x00FFFFFF, value);
754		break;
755	default:
756		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
757		break;
758	}
759
760	if (cycleCounter) {
761		++wait;
762		*cycleCounter += wait;
763	}
764}
765
766void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
767	struct DS* ds = (struct DS*) cpu->master;
768	struct DSMemory* memory = &ds->memory;
769	int wait = 0;
770
771	switch (address >> DS_BASE_OFFSET) {
772	case DS9_REGION_ITCM:
773	case DS9_REGION_ITCM_MIRROR:
774		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
775			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
776			break;
777		}
778		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
779		break;
780	case DS_REGION_RAM:
781		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
782			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
783			break;
784		}
785		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
786			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
787			break;
788		}
789		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
790	case DS_REGION_IO:
791		DS9IOWrite8(ds, address & 0x00FFFFFF, value);
792		break;
793	default:
794		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
795		break;
796	}
797
798	if (cycleCounter) {
799		++wait;
800		*cycleCounter += wait;
801	}
802}
803
804uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
805	struct DS* ds = (struct DS*) cpu->master;
806	struct DSMemory* memory = &ds->memory;
807	uint32_t value;
808	int wait = 0;
809
810	int i;
811	int offset = 4;
812	int popcount = 0;
813	if (direction & LSM_D) {
814		offset = -4;
815		popcount = popcount32(mask);
816		address -= (popcount << 2) - 4;
817	}
818
819	if (direction & LSM_B) {
820		address += offset;
821	}
822
823	uint32_t addressMisalign = address & 0x3;
824	address &= 0xFFFFFFFC;
825
826	switch (address >> DS_BASE_OFFSET) {
827	case DS_REGION_RAM:
828		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
829			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
830		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
831			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
832		} else {
833			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
834		});
835		break;
836	default:
837		mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
838		LDM_LOOP(value = 0);
839		break;
840	}
841
842	if (cycleCounter) {
843		++wait;
844		*cycleCounter += wait;
845	}
846
847	if (direction & LSM_B) {
848		address -= offset;
849	}
850
851	if (direction & LSM_D) {
852		address -= (popcount << 2) + 4;
853	}
854
855	return address | addressMisalign;
856}
857
858
859uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
860	struct DS* ds = (struct DS*) cpu->master;
861	struct DSMemory* memory = &ds->memory;
862	uint32_t value;
863	int wait = 0;
864
865	int i;
866	int offset = 4;
867	int popcount = 0;
868	if (direction & LSM_D) {
869		offset = -4;
870		popcount = popcount32(mask);
871		address -= (popcount << 2) - 4;
872	}
873
874	if (direction & LSM_B) {
875		address += offset;
876	}
877
878	uint32_t addressMisalign = address & 0x3;
879	address &= 0xFFFFFFFC;
880
881	switch (address >> DS_BASE_OFFSET) {
882	case DS9_REGION_ITCM:
883	case DS9_REGION_ITCM_MIRROR:
884		STM_LOOP(if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
885			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
886		} else {
887			mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
888		});
889		break;
890	case DS_REGION_RAM:
891		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
892			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
893		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
894			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
895		} else {
896			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
897		});
898		break;
899	default:
900		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
901		STM_LOOP();
902		break;
903	}
904
905	if (cycleCounter) {
906		*cycleCounter += wait;
907	}
908
909	if (direction & LSM_B) {
910		address -= offset;
911	}
912
913	if (direction & LSM_D) {
914		address -= (popcount << 2) + 4;
915	}
916
917	return address | addressMisalign;
918}
919
920int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
921	return wait;
922}
923