all repos — mgba @ 7e38db453355e5864af5976ae9e0356d85bd00ce

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_RAM:
304		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
305			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
306			break;
307		}
308		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
309	default:
310		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
311		break;
312	}
313
314	if (cycleCounter) {
315		wait += 2;
316		*cycleCounter += wait;
317	}
318	return value;
319}
320
321void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
322	struct DS* ds = (struct DS*) cpu->master;
323	struct DSMemory* memory = &ds->memory;
324	int wait = ds->ds7.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
325
326	switch (address >> DS_BASE_OFFSET) {
327	case DS_REGION_WORKING_RAM:
328		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
329			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
330		} else {
331			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
332		}
333		break;
334	case DS_REGION_RAM:
335		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
336			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
337			break;
338		}
339		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
340		break;
341	case DS_REGION_IO:
342		DS7IOWrite32(ds, address & 0x00FFFFFF, value);
343		break;
344	default:
345		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
346		break;
347	}
348
349	if (cycleCounter) {
350		++wait;
351		*cycleCounter += wait;
352	}
353}
354
355void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
356	struct DS* ds = (struct DS*) cpu->master;
357	struct DSMemory* memory = &ds->memory;
358	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
359
360	switch (address >> DS_BASE_OFFSET) {
361	case DS_REGION_WORKING_RAM:
362		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
363			STORE_16(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
364		} else {
365			STORE_16(value, address & (ds->memory.wramSize7 - 1), memory->wram);
366		}
367		break;
368	case DS_REGION_RAM:
369		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
370			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
371			break;
372		}
373		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
374		break;
375	case DS_REGION_IO:
376		DS7IOWrite(ds, address & 0x00FFFFFF, value);
377		break;
378	default:
379		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
380		break;
381	}
382
383	if (cycleCounter) {
384		++wait;
385		*cycleCounter += wait;
386	}
387}
388
389void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
390	struct DS* ds = (struct DS*) cpu->master;
391	struct DSMemory* memory = &ds->memory;
392	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
393
394	switch (address >> DS_BASE_OFFSET) {
395	case DS_REGION_RAM:
396		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
397			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
398			break;
399		}
400		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
401	case DS_REGION_IO:
402		DS7IOWrite8(ds, address & 0x00FFFFFF, value);
403		break;
404	default:
405		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
406		break;
407	}
408
409	if (cycleCounter) {
410		++wait;
411		*cycleCounter += wait;
412	}
413}
414
415#define LDM_LOOP(LDM) \
416	for (i = 0; i < 16; i += 4) { \
417		if (UNLIKELY(mask & (1 << i))) { \
418			LDM; \
419			cpu->gprs[i] = value; \
420			++wait; \
421			wait += ws32[address >> DS_BASE_OFFSET]; \
422			address += 4; \
423		} \
424		if (UNLIKELY(mask & (2 << i))) { \
425			LDM; \
426			cpu->gprs[i + 1] = value; \
427			++wait; \
428			wait += ws32[address >> DS_BASE_OFFSET]; \
429			address += 4; \
430		} \
431		if (UNLIKELY(mask & (4 << i))) { \
432			LDM; \
433			cpu->gprs[i + 2] = value; \
434			++wait; \
435			wait += ws32[address >> DS_BASE_OFFSET]; \
436			address += 4; \
437		} \
438		if (UNLIKELY(mask & (8 << i))) { \
439			LDM; \
440			cpu->gprs[i + 3] = value; \
441			++wait; \
442			wait += ws32[address >> DS_BASE_OFFSET]; \
443			address += 4; \
444		} \
445	}
446
447#define STM_LOOP(STM) \
448	for (i = 0; i < 16; i += 4) { \
449		if (UNLIKELY(mask & (1 << i))) { \
450			value = cpu->gprs[i]; \
451			STM; \
452			++wait; \
453			wait += ws32[address >> DS_BASE_OFFSET]; \
454			address += 4; \
455		} \
456		if (UNLIKELY(mask & (2 << i))) { \
457			value = cpu->gprs[i + 1]; \
458			STM; \
459			++wait; \
460			wait += ws32[address >> DS_BASE_OFFSET]; \
461			address += 4; \
462		} \
463		if (UNLIKELY(mask & (4 << i))) { \
464			value = cpu->gprs[i + 2]; \
465			STM; \
466			++wait; \
467			wait += ws32[address >> DS_BASE_OFFSET]; \
468			address += 4; \
469		} \
470		if (UNLIKELY(mask & (8 << i))) { \
471			value = cpu->gprs[i + 3]; \
472			STM; \
473			++wait; \
474			wait += ws32[address >> DS_BASE_OFFSET]; \
475			address += 4; \
476		} \
477	}
478
479
480
481uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
482	struct DS* ds = (struct DS*) cpu->master;
483	struct DSMemory* memory = &ds->memory;
484	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
485	uint32_t value;
486	int wait = 0;
487
488	int i;
489	int offset = 4;
490	int popcount = 0;
491	if (direction & LSM_D) {
492		offset = -4;
493		popcount = popcount32(mask);
494		address -= (popcount << 2) - 4;
495	}
496
497	if (direction & LSM_B) {
498		address += offset;
499	}
500
501	uint32_t addressMisalign = address & 0x3;
502	address &= 0xFFFFFFFC;
503
504	switch (address >> DS_BASE_OFFSET) {
505	case DS_REGION_WORKING_RAM:
506		LDM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
507			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
508		} else {
509			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
510		});
511		break;
512	case DS_REGION_RAM:
513		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
514			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
515		} else {
516			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
517		});
518		break;
519	case DS_REGION_IO:
520		LDM_LOOP(value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16));
521		break;
522	default:
523		mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
524		LDM_LOOP(value = 0);
525	}
526
527	if (cycleCounter) {
528		++wait;
529		*cycleCounter += wait;
530	}
531
532	if (direction & LSM_B) {
533		address -= offset;
534	}
535
536	if (direction & LSM_D) {
537		address -= (popcount << 2) + 4;
538	}
539
540	return address | addressMisalign;
541}
542
543
544uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
545	struct DS* ds = (struct DS*) cpu->master;
546	struct DSMemory* memory = &ds->memory;
547	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
548	uint32_t value;
549	int wait = 0;
550
551	int i;
552	int offset = 4;
553	int popcount = 0;
554	if (direction & LSM_D) {
555		offset = -4;
556		popcount = popcount32(mask);
557		address -= (popcount << 2) - 4;
558	}
559
560	if (direction & LSM_B) {
561		address += offset;
562	}
563
564	uint32_t addressMisalign = address & 0x3;
565	address &= 0xFFFFFFFC;
566
567	switch (address >> DS_BASE_OFFSET) {
568	case DS_REGION_WORKING_RAM:
569		STM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
570			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
571		} else {
572			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
573		});
574		break;
575	case DS_REGION_RAM:
576		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
577			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
578		} else {
579			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
580		});
581		break;
582	default:
583		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
584		STM_LOOP();
585		break;
586	}
587
588	if (cycleCounter) {
589		*cycleCounter += wait;
590	}
591
592	if (direction & LSM_B) {
593		address -= offset;
594	}
595
596	if (direction & LSM_D) {
597		address -= (popcount << 2) + 4;
598	}
599
600	return address | addressMisalign;
601}
602
603static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
604	struct DS* ds = (struct DS*) cpu->master;
605	struct DSCoreMemory* memory = &ds->ds9.memory;
606
607	int newRegion = address >> DS_BASE_OFFSET;
608
609	memory->activeRegion = newRegion;
610	switch (newRegion) {
611	case DS9_REGION_ITCM:
612	case DS9_REGION_ITCM_MIRROR:
613		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
614			cpu->memory.activeRegion = ds->memory.itcm;
615			cpu->memory.activeMask = DS9_SIZE_ITCM - 1;
616			break;
617		}
618		goto jump_error;
619	case DS_REGION_RAM:
620		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
621			cpu->memory.activeRegion = ds->memory.ram;
622			cpu->memory.activeMask = DS_SIZE_RAM - 1;
623			break;
624		}
625		goto jump_error;
626	case DS9_REGION_BIOS:
627		// TODO: Mask properly
628		if (ds->memory.bios9) {
629			cpu->memory.activeRegion = ds->memory.bios9;
630			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
631		} else {
632			cpu->memory.activeRegion = _deadbeef;
633			cpu->memory.activeMask = 0;
634		}
635		break;
636	default:
637	jump_error:
638		memory->activeRegion = -1;
639		cpu->memory.activeRegion = _deadbeef;
640		cpu->memory.activeMask = 0;
641		mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
642		return;
643	}
644	cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion];
645	cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion];
646	cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion];
647	cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion];
648}
649
650uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
651	struct DS* ds = (struct DS*) cpu->master;
652	struct DSMemory* memory = &ds->memory;
653	uint32_t value = 0;
654	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
655
656	switch (address >> DS_BASE_OFFSET) {
657	case DS_REGION_RAM:
658		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
659			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
660			break;
661		}
662		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
663			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
664			break;
665		}
666		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
667		break;
668	case DS_REGION_IO:
669		value = DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
670		break;
671	default:
672		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
673		break;
674	}
675
676	if (cycleCounter) {
677		wait += 2;
678		*cycleCounter += wait;
679	}
680	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
681	int rotate = (address & 3) << 3;
682	return ROR(value, rotate);
683}
684
685uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
686	struct DS* ds = (struct DS*) cpu->master;
687	struct DSMemory* memory = &ds->memory;
688	uint32_t value = 0;
689	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
690
691	switch (address >> DS_BASE_OFFSET) {
692	case DS_REGION_RAM:
693		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
694			LOAD_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
695			break;
696		}
697		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
698			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
699			break;
700		}
701		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
702	case DS_REGION_IO:
703		value = DS9IORead(ds, address & 0x00FFFFFF);
704		break;
705	default:
706		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
707		break;
708	}
709
710	if (cycleCounter) {
711		wait += 2;
712		*cycleCounter += wait;
713	}
714	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
715	int rotate = (address & 1) << 3;
716	return ROR(value, rotate);
717}
718
719uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
720	struct DS* ds = (struct DS*) cpu->master;
721	struct DSMemory* memory = &ds->memory;
722	uint32_t value = 0;
723	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
724
725	switch (address >> DS_BASE_OFFSET) {
726	case DS_REGION_RAM:
727		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
728			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
729			break;
730		}
731		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
732			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
733			break;
734		}
735		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
736	default:
737		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
738		break;
739	}
740
741	if (cycleCounter) {
742		wait += 2;
743		*cycleCounter += wait;
744	}
745	return value;
746}
747
748void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
749	struct DS* ds = (struct DS*) cpu->master;
750	struct DSMemory* memory = &ds->memory;
751	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
752
753	switch (address >> DS_BASE_OFFSET) {
754	case DS9_REGION_ITCM:
755	case DS9_REGION_ITCM_MIRROR:
756		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
757			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
758			break;
759		}
760		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
761		break;
762	case DS_REGION_RAM:
763		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
764			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
765			break;
766		}
767		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
768			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
769			break;
770		}
771		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
772		break;
773	case DS_REGION_IO:
774		DS9IOWrite32(ds, address & 0x00FFFFFF, value);
775		break;
776	default:
777		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
778		break;
779	}
780
781	if (cycleCounter) {
782		++wait;
783		*cycleCounter += wait;
784	}
785}
786
787void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
788	struct DS* ds = (struct DS*) cpu->master;
789	struct DSMemory* memory = &ds->memory;
790	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
791
792	switch (address >> DS_BASE_OFFSET) {
793	case DS9_REGION_ITCM:
794	case DS9_REGION_ITCM_MIRROR:
795		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
796			STORE_16(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
797			break;
798		}
799		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
800		break;
801	case DS_REGION_RAM:
802		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
803			STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
804			break;
805		}
806		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
807			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
808			break;
809		}
810		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
811		break;
812	case DS_REGION_IO:
813		DS9IOWrite(ds, address & 0x00FFFFFF, value);
814		break;
815	default:
816		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
817		break;
818	}
819
820	if (cycleCounter) {
821		++wait;
822		*cycleCounter += wait;
823	}
824}
825
826void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
827	struct DS* ds = (struct DS*) cpu->master;
828	struct DSMemory* memory = &ds->memory;
829	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
830
831	switch (address >> DS_BASE_OFFSET) {
832	case DS9_REGION_ITCM:
833	case DS9_REGION_ITCM_MIRROR:
834		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
835			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
836			break;
837		}
838		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
839		break;
840	case DS_REGION_RAM:
841		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
842			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
843			break;
844		}
845		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
846			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
847			break;
848		}
849		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
850	case DS_REGION_IO:
851		DS9IOWrite8(ds, address & 0x00FFFFFF, value);
852		break;
853	default:
854		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
855		break;
856	}
857
858	if (cycleCounter) {
859		++wait;
860		*cycleCounter += wait;
861	}
862}
863
864uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
865	struct DS* ds = (struct DS*) cpu->master;
866	struct DSMemory* memory = &ds->memory;
867	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
868	uint32_t value;
869	int wait = 0;
870
871	int i;
872	int offset = 4;
873	int popcount = 0;
874	if (direction & LSM_D) {
875		offset = -4;
876		popcount = popcount32(mask);
877		address -= (popcount << 2) - 4;
878	}
879
880	if (direction & LSM_B) {
881		address += offset;
882	}
883
884	uint32_t addressMisalign = address & 0x3;
885	address &= 0xFFFFFFFC;
886
887	switch (address >> DS_BASE_OFFSET) {
888	case DS_REGION_RAM:
889		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
890			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
891		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
892			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
893		} else {
894			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
895		});
896		break;
897	default:
898		mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
899		LDM_LOOP(value = 0);
900		break;
901	}
902
903	if (cycleCounter) {
904		++wait;
905		*cycleCounter += wait;
906	}
907
908	if (direction & LSM_B) {
909		address -= offset;
910	}
911
912	if (direction & LSM_D) {
913		address -= (popcount << 2) + 4;
914	}
915
916	return address | addressMisalign;
917}
918
919
920uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
921	struct DS* ds = (struct DS*) cpu->master;
922	struct DSMemory* memory = &ds->memory;
923	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
924	uint32_t value;
925	int wait = 0;
926
927	int i;
928	int offset = 4;
929	int popcount = 0;
930	if (direction & LSM_D) {
931		offset = -4;
932		popcount = popcount32(mask);
933		address -= (popcount << 2) - 4;
934	}
935
936	if (direction & LSM_B) {
937		address += offset;
938	}
939
940	uint32_t addressMisalign = address & 0x3;
941	address &= 0xFFFFFFFC;
942
943	switch (address >> DS_BASE_OFFSET) {
944	case DS9_REGION_ITCM:
945	case DS9_REGION_ITCM_MIRROR:
946		STM_LOOP(if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
947			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
948		} else {
949			mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
950		});
951		break;
952	case DS_REGION_RAM:
953		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
954			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
955		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
956			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
957		} else {
958			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
959		});
960		break;
961	default:
962		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
963		STM_LOOP();
964		break;
965	}
966
967	if (cycleCounter) {
968		*cycleCounter += wait;
969	}
970
971	if (direction & LSM_B) {
972		address -= offset;
973	}
974
975	if (direction & LSM_D) {
976		address -= (popcount << 2) + 4;
977	}
978
979	return address | addressMisalign;
980}
981
982int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
983	return wait;
984}
985