all repos — mgba @ e649be94f5399a7c29d023fbdb0b336834fc9860

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	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	memory->activeRegion9 = 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 & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
599			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
600			break;
601		}
602		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
603		break;
604	case DS_REGION_IO:
605		value = DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
606		break;
607	default:
608		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
609		break;
610	}
611
612	if (cycleCounter) {
613		wait += 2;
614		*cycleCounter += wait;
615	}
616	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
617	int rotate = (address & 3) << 3;
618	return ROR(value, rotate);
619}
620
621uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
622	struct DS* ds = (struct DS*) cpu->master;
623	struct DSMemory* memory = &ds->memory;
624	uint32_t value = 0;
625	int wait = 0;
626
627	switch (address >> DS_BASE_OFFSET) {
628	case DS_REGION_RAM:
629		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
630			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
631			break;
632		}
633		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
634	case DS_REGION_IO:
635		value = DS9IORead(ds, address & 0x00FFFFFF);
636		break;
637	default:
638		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
639		break;
640	}
641
642	if (cycleCounter) {
643		wait += 2;
644		*cycleCounter += wait;
645	}
646	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
647	int rotate = (address & 1) << 3;
648	return ROR(value, rotate);
649}
650
651uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
652	struct DS* ds = (struct DS*) cpu->master;
653	struct DSMemory* memory = &ds->memory;
654	uint32_t value = 0;
655	int wait = 0;
656
657	switch (address >> DS_BASE_OFFSET) {
658	case DS_REGION_RAM:
659		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
660			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
661			break;
662		}
663		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
664	default:
665		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
666		break;
667	}
668
669	if (cycleCounter) {
670		wait += 2;
671		*cycleCounter += wait;
672	}
673	return value;
674}
675
676void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
677	struct DS* ds = (struct DS*) cpu->master;
678	struct DSMemory* memory = &ds->memory;
679	int wait = 0;
680
681	switch (address >> DS_BASE_OFFSET) {
682	case DS9_REGION_ITCM:
683	case DS9_REGION_ITCM_MIRROR:
684		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
685			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
686			break;
687		}
688		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
689		break;
690	case DS_REGION_RAM:
691		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
692			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
693			break;
694		}
695		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
696		break;
697	case DS_REGION_IO:
698		DS9IOWrite32(ds, address & 0x00FFFFFF, value);
699		break;
700	default:
701		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
702		break;
703	}
704
705	if (cycleCounter) {
706		++wait;
707		*cycleCounter += wait;
708	}
709}
710
711void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
712	struct DS* ds = (struct DS*) cpu->master;
713	struct DSMemory* memory = &ds->memory;
714	int wait = 0;
715
716	switch (address >> DS_BASE_OFFSET) {
717	case DS9_REGION_ITCM:
718	case DS9_REGION_ITCM_MIRROR:
719		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
720			STORE_16(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
721			break;
722		}
723		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
724		break;
725	case DS_REGION_RAM:
726		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
727			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
728			break;
729		}
730		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
731		break;
732	case DS_REGION_IO:
733		DS9IOWrite(ds, address & 0x00FFFFFF, value);
734		break;
735	default:
736		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
737		break;
738	}
739
740	if (cycleCounter) {
741		++wait;
742		*cycleCounter += wait;
743	}
744}
745
746void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
747	struct DS* ds = (struct DS*) cpu->master;
748	struct DSMemory* memory = &ds->memory;
749	int wait = 0;
750
751	switch (address >> DS_BASE_OFFSET) {
752	case DS9_REGION_ITCM:
753	case DS9_REGION_ITCM_MIRROR:
754		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
755			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
756			break;
757		}
758		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
759		break;
760	case DS_REGION_RAM:
761		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
762			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
763			break;
764		}
765		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
766	case DS_REGION_IO:
767		DS9IOWrite8(ds, address & 0x00FFFFFF, value);
768		break;
769	default:
770		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
771		break;
772	}
773
774	if (cycleCounter) {
775		++wait;
776		*cycleCounter += wait;
777	}
778}
779
780uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
781	struct DS* ds = (struct DS*) cpu->master;
782	struct DSMemory* memory = &ds->memory;
783	uint32_t value;
784	int wait = 0;
785
786	int i;
787	int offset = 4;
788	int popcount = 0;
789	if (direction & LSM_D) {
790		offset = -4;
791		popcount = popcount32(mask);
792		address -= (popcount << 2) - 4;
793	}
794
795	if (direction & LSM_B) {
796		address += offset;
797	}
798
799	uint32_t addressMisalign = address & 0x3;
800	address &= 0xFFFFFFFC;
801
802	switch (address >> DS_BASE_OFFSET) {
803	case DS_REGION_RAM:
804		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
805			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
806		} else {
807			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
808		});
809		break;
810	default:
811		mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
812		LDM_LOOP(value = 0);
813		break;
814	}
815
816	if (cycleCounter) {
817		++wait;
818		*cycleCounter += wait;
819	}
820
821	if (direction & LSM_B) {
822		address -= offset;
823	}
824
825	if (direction & LSM_D) {
826		address -= (popcount << 2) + 4;
827	}
828
829	return address | addressMisalign;
830}
831
832
833uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
834	struct DS* ds = (struct DS*) cpu->master;
835	struct DSMemory* memory = &ds->memory;
836	uint32_t value;
837	int wait = 0;
838
839	int i;
840	int offset = 4;
841	int popcount = 0;
842	if (direction & LSM_D) {
843		offset = -4;
844		popcount = popcount32(mask);
845		address -= (popcount << 2) - 4;
846	}
847
848	if (direction & LSM_B) {
849		address += offset;
850	}
851
852	uint32_t addressMisalign = address & 0x3;
853	address &= 0xFFFFFFFC;
854
855	switch (address >> DS_BASE_OFFSET) {
856	case DS9_REGION_ITCM:
857	case DS9_REGION_ITCM_MIRROR:
858		STM_LOOP(if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
859			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
860		} else {
861			mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
862		});
863		break;
864	case DS_REGION_RAM:
865		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
866			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
867		} else {
868			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
869		});
870		break;
871	default:
872		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
873		STM_LOOP();
874		break;
875	}
876
877	if (cycleCounter) {
878		*cycleCounter += wait;
879	}
880
881	if (direction & LSM_B) {
882		address -= offset;
883	}
884
885	if (direction & LSM_D) {
886		address -= (popcount << 2) + 4;
887	}
888
889	return address | addressMisalign;
890}
891
892int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
893	return wait;
894}
895