all repos — mgba @ 5d3acef7fe566cc25fccae9fc2a75430faf5037f

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