all repos — mgba @ 36b3f07ed18afe430689933beb31c052e49b52bb

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 "memory.h"
  7
  8#include "arm/macros.h"
  9
 10#include "ds/ds.h"
 11#include "ds/io.h"
 12#include "util/math.h"
 13#include "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->arm7;
 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->arm9;
 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->memory.activeRegion7 = -1;
115	ds->memory.activeRegion9 = -1;
116
117	arm7->memory.activeRegion = 0;
118	arm7->memory.activeMask = 0;
119	arm7->memory.setActiveRegion = DS7SetActiveRegion;
120	arm7->memory.activeSeqCycles32 = 0;
121	arm7->memory.activeSeqCycles16 = 0;
122	arm7->memory.activeNonseqCycles32 = 0;
123	arm7->memory.activeNonseqCycles16 = 0;
124
125	arm9->memory.activeRegion = 0;
126	arm9->memory.activeMask = 0;
127	arm9->memory.setActiveRegion = DS9SetActiveRegion;
128	arm9->memory.activeSeqCycles32 = 0;
129	arm9->memory.activeSeqCycles16 = 0;
130	arm9->memory.activeNonseqCycles32 = 0;
131	arm9->memory.activeNonseqCycles16 = 0;
132}
133
134void DSMemoryDeinit(struct DS* ds) {
135	mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
136	mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
137	mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
138	mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
139	mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
140}
141
142void DSMemoryReset(struct DS* ds) {
143	if (ds->memory.wram) {
144		mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
145	}
146	ds->memory.wram = anonymousMemoryMap(DS_SIZE_WORKING_RAM);
147
148	if (ds->memory.wram7) {
149		mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
150	}
151	ds->memory.wram7 = anonymousMemoryMap(DS7_SIZE_WORKING_RAM);
152
153	if (ds->memory.ram) {
154		mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
155	}
156	ds->memory.ram = anonymousMemoryMap(DS_SIZE_RAM);
157
158	if (ds->memory.itcm) {
159		mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
160	}
161	ds->memory.itcm = anonymousMemoryMap(DS9_SIZE_ITCM);
162
163	if (ds->memory.dtcm) {
164		mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
165	}
166	ds->memory.dtcm = anonymousMemoryMap(DS9_SIZE_DTCM);
167
168	memset(ds->memory.dma7, 0, sizeof(ds->memory.dma7));
169	memset(ds->memory.dma9, 0, sizeof(ds->memory.dma9));
170	ds->memory.activeDMA7 = -1;
171	ds->memory.activeDMA9 = -1;
172	ds->memory.nextDMA = INT_MAX;
173	ds->memory.eventDiff = 0;
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	memory->activeRegion7 = 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	default:
252		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
253		break;
254	}
255
256	if (cycleCounter) {
257		wait += 2;
258		*cycleCounter += wait;
259	}
260	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
261	int rotate = (address & 3) << 3;
262	return ROR(value, rotate);
263}
264
265uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
266	struct DS* ds = (struct DS*) cpu->master;
267	struct DSMemory* memory = &ds->memory;
268	uint32_t value = 0;
269	int wait = 0;
270
271	switch (address >> DS_BASE_OFFSET) {
272	case DS7_REGION_BIOS:
273		LOAD_16(value, address & (DS7_SIZE_BIOS - 1), memory->bios7);
274		break;
275	case DS_REGION_WORKING_RAM:
276		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
277			LOAD_16(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
278		} else {
279			LOAD_16(value, address & (ds->memory.wramSize7 - 1), memory->wram);
280		}
281		break;
282	case DS_REGION_RAM:
283		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
284			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
285			break;
286		}
287		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
288	case DS_REGION_IO:
289		value = DS7IORead(ds, address & 0x00FFFFFF);
290		break;
291	default:
292		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
293		break;
294	}
295
296	if (cycleCounter) {
297		wait += 2;
298		*cycleCounter += wait;
299	}
300	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
301	int rotate = (address & 1) << 3;
302	return ROR(value, rotate);
303}
304
305uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
306	struct DS* ds = (struct DS*) cpu->master;
307	struct DSMemory* memory = &ds->memory;
308	uint32_t value = 0;
309	int wait = 0;
310
311	switch (address >> DS_BASE_OFFSET) {
312	case DS_REGION_RAM:
313		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
314			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
315			break;
316		}
317		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
318	default:
319		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
320		break;
321	}
322
323	if (cycleCounter) {
324		wait += 2;
325		*cycleCounter += wait;
326	}
327	return value;
328}
329
330void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
331	struct DS* ds = (struct DS*) cpu->master;
332	struct DSMemory* memory = &ds->memory;
333	int wait = 0;
334
335	switch (address >> DS_BASE_OFFSET) {
336	case DS_REGION_WORKING_RAM:
337		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
338			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
339		} else {
340			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
341		}
342		break;
343	case DS_REGION_RAM:
344		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
345			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
346			break;
347		}
348		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
349		break;
350	default:
351		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
352		break;
353	}
354
355	if (cycleCounter) {
356		++wait;
357		*cycleCounter += wait;
358	}
359}
360
361void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
362	struct DS* ds = (struct DS*) cpu->master;
363	struct DSMemory* memory = &ds->memory;
364	int wait = 0;
365
366	switch (address >> DS_BASE_OFFSET) {
367	case DS_REGION_WORKING_RAM:
368		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
369			STORE_16(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
370		} else {
371			STORE_16(value, address & (ds->memory.wramSize7 - 1), memory->wram);
372		}
373		break;
374	case DS_REGION_RAM:
375		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
376			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
377			break;
378		}
379		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
380		break;
381	case DS_REGION_IO:
382		DS7IOWrite(ds, address & 0x00FFFFFF, value);
383		break;
384	default:
385		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
386		break;
387	}
388
389	if (cycleCounter) {
390		++wait;
391		*cycleCounter += wait;
392	}
393}
394
395void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
396	struct DS* ds = (struct DS*) cpu->master;
397	struct DSMemory* memory = &ds->memory;
398	int wait = 0;
399
400	switch (address >> DS_BASE_OFFSET) {
401	case DS_REGION_RAM:
402		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
403			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
404			break;
405		}
406		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
407	case DS_REGION_IO:
408		DS7IOWrite8(ds, address & 0x00FFFFFF, value);
409		break;
410	default:
411		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
412		break;
413	}
414
415	if (cycleCounter) {
416		++wait;
417		*cycleCounter += wait;
418	}
419}
420
421uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
422	struct DS* ds = (struct DS*) cpu->master;
423	struct DSMemory* memory = &ds->memory;
424	uint32_t value;
425	int wait = 0;
426
427	int i;
428	int offset = 4;
429	int popcount = 0;
430	if (direction & LSM_D) {
431		offset = -4;
432		popcount = popcount32(mask);
433		address -= (popcount << 2) - 4;
434	}
435
436	if (direction & LSM_B) {
437		address += offset;
438	}
439
440	uint32_t addressMisalign = address & 0x3;
441	address &= 0xFFFFFFFC;
442
443	switch (address >> DS_BASE_OFFSET) {
444	case DS_REGION_WORKING_RAM:
445		LDM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
446			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
447		} else {
448			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
449		});
450		break;
451	case DS_REGION_RAM:
452		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
453			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
454		} else {
455			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
456		});
457		break;
458	default:
459		mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
460		LDM_LOOP(value = 0);
461	}
462
463	if (cycleCounter) {
464		++wait;
465		*cycleCounter += wait;
466	}
467
468	if (direction & LSM_B) {
469		address -= offset;
470	}
471
472	if (direction & LSM_D) {
473		address -= (popcount << 2) + 4;
474	}
475
476	return address | addressMisalign;
477}
478
479
480uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
481	struct DS* ds = (struct DS*) cpu->master;
482	struct DSMemory* memory = &ds->memory;
483	uint32_t value;
484	int wait = 0;
485
486	int i;
487	int offset = 4;
488	int popcount = 0;
489	if (direction & LSM_D) {
490		offset = -4;
491		popcount = popcount32(mask);
492		address -= (popcount << 2) - 4;
493	}
494
495	if (direction & LSM_B) {
496		address += offset;
497	}
498
499	uint32_t addressMisalign = address & 0x3;
500	address &= 0xFFFFFFFC;
501
502	switch (address >> DS_BASE_OFFSET) {
503	case DS_REGION_WORKING_RAM:
504		STM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
505			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
506		} else {
507			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
508		});
509		break;
510	case DS_REGION_RAM:
511		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
512			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
513		} else {
514			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
515		});
516		break;
517	default:
518		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
519		STM_LOOP();
520		break;
521	}
522
523	if (cycleCounter) {
524		*cycleCounter += wait;
525	}
526
527	if (direction & LSM_B) {
528		address -= offset;
529	}
530
531	if (direction & LSM_D) {
532		address -= (popcount << 2) + 4;
533	}
534
535	return address | addressMisalign;
536}
537
538static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
539	struct DS* ds = (struct DS*) cpu->master;
540	struct DSMemory* memory = &ds->memory;
541
542	int newRegion = address >> DS_BASE_OFFSET;
543
544	memory->activeRegion9 = newRegion;
545	switch (newRegion) {
546	case DS9_REGION_ITCM:
547	case DS9_REGION_ITCM_MIRROR:
548		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
549			cpu->memory.activeRegion = memory->itcm;
550			cpu->memory.activeMask = DS9_SIZE_ITCM - 1;
551			return;
552		}
553		goto jump_error;
554	case DS_REGION_RAM:
555		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
556			cpu->memory.activeRegion = memory->ram;
557			cpu->memory.activeMask = DS_SIZE_RAM - 1;
558			return;
559		}
560		goto jump_error;
561	case DS9_REGION_BIOS:
562		// TODO: Mask properly
563		if (memory->bios9) {
564			cpu->memory.activeRegion = memory->bios9;
565			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
566		} else {
567			cpu->memory.activeRegion = _deadbeef;
568			cpu->memory.activeMask = 0;
569		}
570		return;
571	default:
572		break;
573	}
574
575jump_error:
576	cpu->memory.activeRegion = _deadbeef;
577	cpu->memory.activeMask = 0;
578	mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
579}
580
581uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
582	struct DS* ds = (struct DS*) cpu->master;
583	struct DSMemory* memory = &ds->memory;
584	uint32_t value = 0;
585	int wait = 0;
586
587	switch (address >> DS_BASE_OFFSET) {
588	case DS_REGION_RAM:
589		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
590			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
591			break;
592		}
593		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
594		break;
595	default:
596		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
597		break;
598	}
599
600	if (cycleCounter) {
601		wait += 2;
602		*cycleCounter += wait;
603	}
604	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
605	int rotate = (address & 3) << 3;
606	return ROR(value, rotate);
607}
608
609uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
610	struct DS* ds = (struct DS*) cpu->master;
611	struct DSMemory* memory = &ds->memory;
612	uint32_t value = 0;
613	int wait = 0;
614
615	switch (address >> DS_BASE_OFFSET) {
616	case DS_REGION_RAM:
617		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
618			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
619			break;
620		}
621		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
622	case DS_REGION_IO:
623		value = DS9IORead(ds, address & 0x00FFFFFF);
624		break;
625	default:
626		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
627		break;
628	}
629
630	if (cycleCounter) {
631		wait += 2;
632		*cycleCounter += wait;
633	}
634	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
635	int rotate = (address & 1) << 3;
636	return ROR(value, rotate);
637}
638
639uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
640	struct DS* ds = (struct DS*) cpu->master;
641	struct DSMemory* memory = &ds->memory;
642	uint32_t value = 0;
643	int wait = 0;
644
645	switch (address >> DS_BASE_OFFSET) {
646	case DS_REGION_RAM:
647		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
648			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
649			break;
650		}
651		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
652	default:
653		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
654		break;
655	}
656
657	if (cycleCounter) {
658		wait += 2;
659		*cycleCounter += wait;
660	}
661	return value;
662}
663
664void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
665	struct DS* ds = (struct DS*) cpu->master;
666	struct DSMemory* memory = &ds->memory;
667	int wait = 0;
668
669	switch (address >> DS_BASE_OFFSET) {
670	case DS9_REGION_ITCM:
671	case DS9_REGION_ITCM_MIRROR:
672		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
673			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
674			break;
675		}
676		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
677		break;
678	case DS_REGION_RAM:
679		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
680			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
681			break;
682		}
683		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
684		break;
685	default:
686		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
687		break;
688	}
689
690	if (cycleCounter) {
691		++wait;
692		*cycleCounter += wait;
693	}
694}
695
696void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
697	struct DS* ds = (struct DS*) cpu->master;
698	struct DSMemory* memory = &ds->memory;
699	int wait = 0;
700
701	switch (address >> DS_BASE_OFFSET) {
702	case DS9_REGION_ITCM:
703	case DS9_REGION_ITCM_MIRROR:
704		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
705			STORE_16(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
706			break;
707		}
708		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
709		break;
710	case DS_REGION_RAM:
711		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
712			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
713			break;
714		}
715		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
716		break;
717	case DS_REGION_IO:
718		DS9IOWrite(ds, address & 0x00FFFFFF, value);
719		break;
720	default:
721		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
722		break;
723	}
724
725	if (cycleCounter) {
726		++wait;
727		*cycleCounter += wait;
728	}
729}
730
731void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
732	struct DS* ds = (struct DS*) cpu->master;
733	struct DSMemory* memory = &ds->memory;
734	int wait = 0;
735
736	switch (address >> DS_BASE_OFFSET) {
737	case DS9_REGION_ITCM:
738	case DS9_REGION_ITCM_MIRROR:
739		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
740			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
741			break;
742		}
743		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
744		break;
745	case DS_REGION_RAM:
746		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
747			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
748			break;
749		}
750		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
751	case DS_REGION_IO:
752		DS9IOWrite8(ds, address & 0x00FFFFFF, value);
753		break;
754	default:
755		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
756		break;
757	}
758
759	if (cycleCounter) {
760		++wait;
761		*cycleCounter += wait;
762	}
763}
764
765uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
766	struct DS* ds = (struct DS*) cpu->master;
767	struct DSMemory* memory = &ds->memory;
768	uint32_t value;
769	int wait = 0;
770
771	int i;
772	int offset = 4;
773	int popcount = 0;
774	if (direction & LSM_D) {
775		offset = -4;
776		popcount = popcount32(mask);
777		address -= (popcount << 2) - 4;
778	}
779
780	if (direction & LSM_B) {
781		address += offset;
782	}
783
784	uint32_t addressMisalign = address & 0x3;
785	address &= 0xFFFFFFFC;
786
787	switch (address >> DS_BASE_OFFSET) {
788	case DS_REGION_RAM:
789		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
790			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
791		} else {
792			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
793		});
794		break;
795	default:
796		mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
797		LDM_LOOP(value = 0);
798		break;
799	}
800
801	if (cycleCounter) {
802		++wait;
803		*cycleCounter += wait;
804	}
805
806	if (direction & LSM_B) {
807		address -= offset;
808	}
809
810	if (direction & LSM_D) {
811		address -= (popcount << 2) + 4;
812	}
813
814	return address | addressMisalign;
815}
816
817
818uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
819	struct DS* ds = (struct DS*) cpu->master;
820	struct DSMemory* memory = &ds->memory;
821	uint32_t value;
822	int wait = 0;
823
824	int i;
825	int offset = 4;
826	int popcount = 0;
827	if (direction & LSM_D) {
828		offset = -4;
829		popcount = popcount32(mask);
830		address -= (popcount << 2) - 4;
831	}
832
833	if (direction & LSM_B) {
834		address += offset;
835	}
836
837	uint32_t addressMisalign = address & 0x3;
838	address &= 0xFFFFFFFC;
839
840	switch (address >> DS_BASE_OFFSET) {
841	case DS9_REGION_ITCM:
842	case DS9_REGION_ITCM_MIRROR:
843		STM_LOOP(if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
844			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
845		} else {
846			mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
847		});
848		break;
849	case DS_REGION_RAM:
850		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
851			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
852		} else {
853			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
854		});
855		break;
856	default:
857		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
858		STM_LOOP();
859		break;
860	}
861
862	if (cycleCounter) {
863		*cycleCounter += wait;
864	}
865
866	if (direction & LSM_B) {
867		address -= offset;
868	}
869
870	if (direction & LSM_D) {
871		address -= (popcount << 2) + 4;
872	}
873
874	return address | addressMisalign;
875}
876
877int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
878	return wait;
879}
880