all repos — mgba @ 60d49b4860162405afdd488ee3dac11fe5e925ab

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.ram = NULL;
108	ds->memory.rom = NULL;
109
110	ds->memory.activeRegion7 = -1;
111	ds->memory.activeRegion9 = -1;
112
113	arm7->memory.activeRegion = 0;
114	arm7->memory.activeMask = 0;
115	arm7->memory.setActiveRegion = DS7SetActiveRegion;
116	arm7->memory.activeSeqCycles32 = 0;
117	arm7->memory.activeSeqCycles16 = 0;
118	arm7->memory.activeNonseqCycles32 = 0;
119	arm7->memory.activeNonseqCycles16 = 0;
120
121	arm9->memory.activeRegion = 0;
122	arm9->memory.activeMask = 0;
123	arm9->memory.setActiveRegion = DS9SetActiveRegion;
124	arm9->memory.activeSeqCycles32 = 0;
125	arm9->memory.activeSeqCycles16 = 0;
126	arm9->memory.activeNonseqCycles32 = 0;
127	arm9->memory.activeNonseqCycles16 = 0;
128}
129
130void DSMemoryDeinit(struct DS* ds) {
131	mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
132	mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
133}
134
135void DSMemoryReset(struct DS* ds) {
136	if (ds->memory.wram) {
137		mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
138	}
139	ds->memory.wram = anonymousMemoryMap(DS_SIZE_WORKING_RAM);
140
141	if (ds->memory.ram) {
142		mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
143	}
144	ds->memory.ram = anonymousMemoryMap(DS_SIZE_RAM);
145
146	memset(ds->memory.dma7, 0, sizeof(ds->memory.dma7));
147	memset(ds->memory.dma9, 0, sizeof(ds->memory.dma9));
148	ds->memory.activeDMA7 = -1;
149	ds->memory.activeDMA9 = -1;
150	ds->memory.nextDMA = INT_MAX;
151	ds->memory.eventDiff = 0;
152
153	if (!ds->memory.wram || !ds->memory.ram) {
154		DSMemoryDeinit(ds);
155		mLOG(DS_MEM, FATAL, "Could not map memory");
156	}
157}
158
159static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
160	struct DS* ds = (struct DS*) cpu->master;
161	struct DSMemory* memory = &ds->memory;
162
163	int newRegion = address >> DS_BASE_OFFSET;
164
165	memory->activeRegion7 = newRegion;
166	switch (newRegion) {
167	case DS_REGION_RAM:
168		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
169			cpu->memory.activeRegion = memory->ram;
170			cpu->memory.activeMask = DS_SIZE_RAM - 1;
171			return;
172		}
173		break;
174	case DS7_REGION_BIOS:
175		if (memory->bios7) {
176			cpu->memory.activeRegion = memory->bios9;
177			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
178		} else {
179			cpu->memory.activeRegion = _deadbeef;
180			cpu->memory.activeMask = 0;
181		}
182		return;
183	default:
184		break;
185	}
186	cpu->memory.activeRegion = _deadbeef;
187	cpu->memory.activeMask = 0;
188	mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
189	return;
190}
191
192uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
193	struct DS* ds = (struct DS*) cpu->master;
194	struct DSMemory* memory = &ds->memory;
195	uint32_t value = 0;
196	int wait = 0;
197
198	switch (address >> DS_BASE_OFFSET) {
199	case DS_REGION_RAM:
200		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
201			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
202		}
203		break;
204	default:
205		break;
206	}
207
208	if (cycleCounter) {
209		wait += 2;
210		*cycleCounter += wait;
211	}
212	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
213	int rotate = (address & 3) << 3;
214	return ROR(value, rotate);
215}
216
217uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
218	struct DS* ds = (struct DS*) cpu->master;
219	struct DSMemory* memory = &ds->memory;
220	uint32_t value = 0;
221	int wait = 0;
222
223	switch (address >> DS_BASE_OFFSET) {
224	default:
225		break;
226	}
227
228	if (cycleCounter) {
229		wait += 2;
230		*cycleCounter += wait;
231	}
232	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
233	int rotate = (address & 1) << 3;
234	return ROR(value, rotate);
235}
236
237uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
238	struct DS* ds = (struct DS*) cpu->master;
239	struct DSMemory* memory = &ds->memory;
240	uint32_t value = 0;
241	int wait = 0;
242
243	switch (address >> DS_BASE_OFFSET) {
244	default:
245		break;
246	}
247
248	if (cycleCounter) {
249		wait += 2;
250		*cycleCounter += wait;
251	}
252	return value;
253}
254
255void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
256	struct DS* ds = (struct DS*) cpu->master;
257	struct DSMemory* memory = &ds->memory;
258	int wait = 0;
259
260	switch (address >> DS_BASE_OFFSET) {
261	default:
262		break;
263	}
264
265	if (cycleCounter) {
266		++wait;
267		*cycleCounter += wait;
268	}
269}
270
271void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
272	struct DS* ds = (struct DS*) cpu->master;
273	struct DSMemory* memory = &ds->memory;
274	int wait = 0;
275
276	switch (address >> DS_BASE_OFFSET) {
277	default:
278		break;
279	}
280
281	if (cycleCounter) {
282		++wait;
283		*cycleCounter += wait;
284	}
285}
286
287void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
288	struct DS* ds = (struct DS*) cpu->master;
289	struct DSMemory* memory = &ds->memory;
290	int wait = 0;
291
292	switch (address >> DS_BASE_OFFSET) {
293	default:
294		break;
295	}
296
297	if (cycleCounter) {
298		++wait;
299		*cycleCounter += wait;
300	}
301}
302
303uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
304	struct DS* ds = (struct DS*) cpu->master;
305	struct DSMemory* memory = &ds->memory;
306	uint32_t value;
307	int wait = 0;
308
309	int i;
310	int offset = 4;
311	int popcount = 0;
312	if (direction & LSM_D) {
313		offset = -4;
314		popcount = popcount32(mask);
315		address -= (popcount << 2) - 4;
316	}
317
318	if (direction & LSM_B) {
319		address += offset;
320	}
321
322	uint32_t addressMisalign = address & 0x3;
323	address &= 0xFFFFFFFC;
324
325	switch (address >> DS_BASE_OFFSET) {
326	default:
327		break;
328	}
329
330	if (cycleCounter) {
331		++wait;
332		*cycleCounter += wait;
333	}
334
335	if (direction & LSM_B) {
336		address -= offset;
337	}
338
339	if (direction & LSM_D) {
340		address -= (popcount << 2) + 4;
341	}
342
343	return address | addressMisalign;
344}
345
346
347uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
348	struct DS* ds = (struct DS*) cpu->master;
349	struct DSMemory* memory = &ds->memory;
350	uint32_t value;
351	int wait = 0;
352
353	int i;
354	int offset = 4;
355	int popcount = 0;
356	if (direction & LSM_D) {
357		offset = -4;
358		popcount = popcount32(mask);
359		address -= (popcount << 2) - 4;
360	}
361
362	if (direction & LSM_B) {
363		address += offset;
364	}
365
366	uint32_t addressMisalign = address & 0x3;
367	address &= 0xFFFFFFFC;
368
369	switch (address >> DS_BASE_OFFSET) {
370	default:
371		break;
372	}
373
374	if (cycleCounter) {
375		*cycleCounter += wait;
376	}
377
378	if (direction & LSM_B) {
379		address -= offset;
380	}
381
382	if (direction & LSM_D) {
383		address -= (popcount << 2) + 4;
384	}
385
386	return address | addressMisalign;
387}
388
389static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
390	struct DS* ds = (struct DS*) cpu->master;
391	struct DSMemory* memory = &ds->memory;
392
393	int newRegion = address >> DS_BASE_OFFSET;
394
395	memory->activeRegion9 = newRegion;
396	switch (newRegion) {
397	case DS_REGION_RAM:
398		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
399			cpu->memory.activeRegion = memory->ram;
400			cpu->memory.activeMask = DS_SIZE_RAM - 1;
401			return;
402		}
403		break;
404	case DS9_REGION_BIOS:
405		// TODO: Mask properly
406		if (memory->bios9) {
407			cpu->memory.activeRegion = memory->bios9;
408			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
409		} else {
410			cpu->memory.activeRegion = _deadbeef;
411			cpu->memory.activeMask = 0;
412		}
413		return;
414	default:
415		break;
416	}
417	cpu->memory.activeRegion = _deadbeef;
418	cpu->memory.activeMask = 0;
419	mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
420	return;
421}
422
423uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
424	struct DS* ds = (struct DS*) cpu->master;
425	struct DSMemory* memory = &ds->memory;
426	uint32_t value = 0;
427	int wait = 0;
428
429	switch (address >> DS_BASE_OFFSET) {
430	case DS_REGION_RAM:
431		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
432			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
433			break;
434		}
435		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
436		break;
437	default:
438		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
439		break;
440	}
441
442	if (cycleCounter) {
443		wait += 2;
444		*cycleCounter += wait;
445	}
446	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
447	int rotate = (address & 3) << 3;
448	return ROR(value, rotate);
449}
450
451uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
452	struct DS* ds = (struct DS*) cpu->master;
453	struct DSMemory* memory = &ds->memory;
454	uint32_t value = 0;
455	int wait = 0;
456
457	switch (address >> DS_BASE_OFFSET) {
458	case DS_REGION_RAM:
459		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
460			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
461			break;
462		}
463		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
464	default:
465		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
466		break;
467	}
468
469	if (cycleCounter) {
470		wait += 2;
471		*cycleCounter += wait;
472	}
473	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
474	int rotate = (address & 1) << 3;
475	return ROR(value, rotate);
476}
477
478uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
479	struct DS* ds = (struct DS*) cpu->master;
480	struct DSMemory* memory = &ds->memory;
481	uint32_t value = 0;
482	int wait = 0;
483
484	switch (address >> DS_BASE_OFFSET) {
485	case DS_REGION_RAM:
486		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
487			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
488			break;
489		}
490		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
491	default:
492		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
493		break;
494	}
495
496	if (cycleCounter) {
497		wait += 2;
498		*cycleCounter += wait;
499	}
500	return value;
501}
502
503void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
504	struct DS* ds = (struct DS*) cpu->master;
505	struct DSMemory* memory = &ds->memory;
506	int wait = 0;
507
508	switch (address >> DS_BASE_OFFSET) {
509	case DS_REGION_RAM:
510		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
511			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
512			break;
513		}
514		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
515		break;
516	default:
517		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
518		break;
519	}
520
521	if (cycleCounter) {
522		++wait;
523		*cycleCounter += wait;
524	}
525}
526
527void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
528	struct DS* ds = (struct DS*) cpu->master;
529	struct DSMemory* memory = &ds->memory;
530	int wait = 0;
531
532	switch (address >> DS_BASE_OFFSET) {
533	case DS_REGION_RAM:
534		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
535			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
536			break;
537		}
538		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
539		break;
540	default:
541		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
542		break;
543	}
544
545	if (cycleCounter) {
546		++wait;
547		*cycleCounter += wait;
548	}
549}
550
551void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
552	struct DS* ds = (struct DS*) cpu->master;
553	struct DSMemory* memory = &ds->memory;
554	int wait = 0;
555
556	switch (address >> DS_BASE_OFFSET) {
557	case DS_REGION_RAM:
558		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
559			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
560			break;
561		}
562		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
563	default:
564		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
565		break;
566	}
567
568	if (cycleCounter) {
569		++wait;
570		*cycleCounter += wait;
571	}
572}
573
574uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
575	struct DS* ds = (struct DS*) cpu->master;
576	struct DSMemory* memory = &ds->memory;
577	uint32_t value;
578	int wait = 0;
579
580	int i;
581	int offset = 4;
582	int popcount = 0;
583	if (direction & LSM_D) {
584		offset = -4;
585		popcount = popcount32(mask);
586		address -= (popcount << 2) - 4;
587	}
588
589	if (direction & LSM_B) {
590		address += offset;
591	}
592
593	uint32_t addressMisalign = address & 0x3;
594	address &= 0xFFFFFFFC;
595
596	switch (address >> DS_BASE_OFFSET) {
597	case DS_REGION_RAM:
598		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
599			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
600		} else {
601			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
602		});
603		break;
604	default:
605		mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
606		LDM_LOOP(value = 0);
607		break;
608	}
609
610	if (cycleCounter) {
611		++wait;
612		*cycleCounter += wait;
613	}
614
615	if (direction & LSM_B) {
616		address -= offset;
617	}
618
619	if (direction & LSM_D) {
620		address -= (popcount << 2) + 4;
621	}
622
623	return address | addressMisalign;
624}
625
626
627uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
628	struct DS* ds = (struct DS*) cpu->master;
629	struct DSMemory* memory = &ds->memory;
630	uint32_t value;
631	int wait = 0;
632
633	int i;
634	int offset = 4;
635	int popcount = 0;
636	if (direction & LSM_D) {
637		offset = -4;
638		popcount = popcount32(mask);
639		address -= (popcount << 2) - 4;
640	}
641
642	if (direction & LSM_B) {
643		address += offset;
644	}
645
646	uint32_t addressMisalign = address & 0x3;
647	address &= 0xFFFFFFFC;
648
649	switch (address >> DS_BASE_OFFSET) {
650	case DS_REGION_RAM:
651		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
652			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
653		} else {
654			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
655		});
656		break;
657	default:
658		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
659		STM_LOOP();
660		break;
661	}
662
663	if (cycleCounter) {
664		*cycleCounter += wait;
665	}
666
667	if (direction & LSM_B) {
668		address -= offset;
669	}
670
671	if (direction & LSM_D) {
672		address -= (popcount << 2) + 4;
673	}
674
675	return address | addressMisalign;
676}
677
678int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
679	return wait;
680}
681