all repos — mgba @ 742d08d182cc63fd535c1b163431822d5c7bcda2

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