all repos — mgba @ eafcb0f5554ee6c21c0fb1ac73219ed6a2c99e1b

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", "ds.memory");
  16
  17static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
  18const uint32_t redzoneInstruction = 0xE7F0DEF0;
  19
  20static const uint32_t _vramMask[9] = {
  21	0x1FFFF,
  22	0x1FFFF,
  23	0x1FFFF,
  24	0x1FFFF,
  25	0x0FFFF,
  26	0x03FFF,
  27	0x03FFF,
  28	0x07FFF,
  29	0x03FFF
  30};
  31
  32static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t region);
  33static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t region);
  34static int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait);
  35
  36static unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset);
  37
  38static const char DS7_BASE_WAITSTATES[16] =        { 0, 0, 8, 0, 0, 0, 0, 0 };
  39static const char DS7_BASE_WAITSTATES_32[16] =     { 0, 0, 9, 0, 0, 1, 1, 0 };
  40static const char DS7_BASE_WAITSTATES_SEQ[16] =    { 0, 0, 1, 0, 0, 0, 0, 0 };
  41static const char DS7_BASE_WAITSTATES_SEQ_32[16] = { 0, 0, 2, 0, 0, 1, 1, 0 };
  42
  43static const char DS9_BASE_WAITSTATES[16] =        { 0, 0, 2, 6, 6, 7, 7, 6 };
  44static const char DS9_BASE_WAITSTATES_32[16] =     { 0, 0, 4, 6, 6, 9, 9, 6 };
  45static const char DS9_BASE_WAITSTATES_SEQ[16] =    { 0, 0, 1, 1, 1, 2, 2, 1 };
  46static const char DS9_BASE_WAITSTATES_SEQ_32[16] = { 0, 0, 2, 1, 1, 4, 4, 1 };
  47
  48void DSMemoryInit(struct DS* ds) {
  49	struct ARMCore* arm7 = ds->ds7.cpu;
  50	arm7->memory.load32 = DS7Load32;
  51	arm7->memory.load16 = DS7Load16;
  52	arm7->memory.load8 = DS7Load8;
  53	arm7->memory.loadMultiple = DS7LoadMultiple;
  54	arm7->memory.store32 = DS7Store32;
  55	arm7->memory.store16 = DS7Store16;
  56	arm7->memory.store8 = DS7Store8;
  57	arm7->memory.storeMultiple = DS7StoreMultiple;
  58	arm7->memory.stall = DSMemoryStall;
  59
  60	struct ARMCore* arm9 = ds->ds9.cpu;
  61	arm9->memory.load32 = DS9Load32;
  62	arm9->memory.load16 = DS9Load16;
  63	arm9->memory.load8 = DS9Load8;
  64	arm9->memory.loadMultiple = DS9LoadMultiple;
  65	arm9->memory.store32 = DS9Store32;
  66	arm9->memory.store16 = DS9Store16;
  67	arm9->memory.store8 = DS9Store8;
  68	arm9->memory.storeMultiple = DS9StoreMultiple;
  69	arm9->memory.stall = DSMemoryStall;
  70
  71	int i;
  72	for (i = 0; i < 8; ++i) {
  73		// TODO: Formalize
  74		ds->ds7.memory.waitstatesNonseq16[i] = DS7_BASE_WAITSTATES[i];
  75		ds->ds7.memory.waitstatesSeq16[i] = DS7_BASE_WAITSTATES_SEQ[i];
  76		ds->ds7.memory.waitstatesPrefetchNonseq16[i] = DS7_BASE_WAITSTATES[i];
  77		ds->ds7.memory.waitstatesPrefetchSeq16[i] = DS7_BASE_WAITSTATES_SEQ[i];
  78		ds->ds7.memory.waitstatesNonseq32[i] = DS7_BASE_WAITSTATES_32[i];
  79		ds->ds7.memory.waitstatesSeq32[i] = DS7_BASE_WAITSTATES_SEQ_32[i];
  80		ds->ds7.memory.waitstatesPrefetchNonseq32[i] = DS7_BASE_WAITSTATES_32[i];
  81		ds->ds7.memory.waitstatesPrefetchSeq32[i] = DS7_BASE_WAITSTATES_SEQ_32[i];
  82
  83		ds->ds9.memory.waitstatesNonseq16[i] = DS9_BASE_WAITSTATES[i];
  84		ds->ds9.memory.waitstatesSeq16[i] = DS9_BASE_WAITSTATES_SEQ[i];
  85		ds->ds9.memory.waitstatesPrefetchNonseq16[i] = DS9_BASE_WAITSTATES[i];
  86		ds->ds9.memory.waitstatesPrefetchSeq16[i] = DS9_BASE_WAITSTATES[i];
  87		ds->ds9.memory.waitstatesNonseq32[i] = DS9_BASE_WAITSTATES_32[i];
  88		ds->ds9.memory.waitstatesSeq32[i] = DS9_BASE_WAITSTATES_SEQ_32[i];
  89		ds->ds9.memory.waitstatesPrefetchNonseq32[i] = DS9_BASE_WAITSTATES_32[i];
  90		ds->ds9.memory.waitstatesPrefetchSeq32[i] = DS9_BASE_WAITSTATES_32[i];
  91	}
  92
  93	ds->ds9.memory.waitstatesPrefetchNonseq16[2] = 0;
  94	ds->ds9.memory.waitstatesPrefetchSeq16[2] = 0;
  95	ds->ds9.memory.waitstatesPrefetchNonseq32[2] = 0;
  96	ds->ds9.memory.waitstatesPrefetchSeq32[2] = 0;
  97
  98	for (; i < 256; ++i) {
  99		ds->ds7.memory.waitstatesNonseq16[i] = 0;
 100		ds->ds7.memory.waitstatesSeq16[i] = 0;
 101		ds->ds7.memory.waitstatesNonseq32[i] = 0;
 102		ds->ds7.memory.waitstatesSeq32[i] = 0;
 103
 104		ds->ds9.memory.waitstatesNonseq16[i] = 0;
 105		ds->ds9.memory.waitstatesSeq16[i] = 0;
 106		ds->ds9.memory.waitstatesNonseq32[i] = 0;
 107		ds->ds9.memory.waitstatesSeq32[i] = 0;
 108	}
 109
 110	ds->memory.bios7 = NULL;
 111	ds->memory.bios9 = NULL;
 112	ds->memory.wramBase = NULL;
 113	ds->memory.wram7 = NULL;
 114	ds->memory.ram = NULL;
 115	ds->memory.itcm = NULL;
 116	ds->memory.dtcm = NULL;
 117	ds->memory.rom = NULL;
 118
 119	ds->ds7.memory.activeRegion = -1;
 120	ds->ds9.memory.activeRegion = -1;
 121	ds->ds7.memory.io = ds->memory.io7;
 122	ds->ds9.memory.io = ds->memory.io9;
 123
 124	arm7->memory.activeRegion = 0;
 125	arm7->memory.activeMask = 0;
 126	arm7->memory.setActiveRegion = DS7SetActiveRegion;
 127	arm7->memory.activeSeqCycles32 = 0;
 128	arm7->memory.activeSeqCycles16 = 0;
 129	arm7->memory.activeNonseqCycles32 = 0;
 130	arm7->memory.activeNonseqCycles16 = 0;
 131
 132	arm9->memory.activeRegion = 0;
 133	arm9->memory.activeMask = 0;
 134	arm9->memory.setActiveRegion = DS9SetActiveRegion;
 135	arm9->memory.activeSeqCycles32 = 0;
 136	arm9->memory.activeSeqCycles16 = 0;
 137	arm9->memory.activeNonseqCycles32 = 0;
 138	arm9->memory.activeNonseqCycles16 = 0;
 139}
 140
 141void DSMemoryDeinit(struct DS* ds) {
 142	mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
 143	mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
 144	mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
 145	mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
 146	mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
 147}
 148
 149void DSMemoryReset(struct DS* ds) {
 150	if (ds->memory.wram) {
 151		mappedMemoryFree(ds->memory.wramBase, DS_SIZE_WORKING_RAM * 2 + 12);
 152	}
 153	// XXX: This hack lets you roll over the end of the WRAM block without
 154	// looping back to the beginning. It works by placing an undefined
 155	// instruction in a redzone at the very beginning and end of the buffer.
 156	// Using clever masking tricks, the ARM loop will mask the offset so that
 157	// either the middle of the passed-in buffer is the actual buffer, and
 158	// when the loop rolls over, it hits the redzone at the beginning, or the
 159	// start of the passed-in buffer matches the actual buffer, causing the
 160	// redzone at the end to be hit. This requires a lot of dead space in
 161	// the middle, and a fake (too large) mask, but it is very fast.
 162	ds->memory.wram = anonymousMemoryMap(DS_SIZE_WORKING_RAM * 2 + 12);
 163	ds->memory.wram[0] = redzoneInstruction;
 164	ds->memory.wram[1] = redzoneInstruction;
 165	ds->memory.wram[2] = redzoneInstruction;
 166	ds->memory.wram[DS_SIZE_WORKING_RAM >> 1] = redzoneInstruction;
 167	ds->memory.wram[(DS_SIZE_WORKING_RAM >> 1) + 1] = redzoneInstruction;
 168	ds->memory.wram[(DS_SIZE_WORKING_RAM >> 1) + 2] = redzoneInstruction;
 169	ds->memory.wramBase = &ds->memory.wram[DS_SIZE_WORKING_RAM >> 2];
 170
 171	if (ds->memory.wram7) {
 172		mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
 173	}
 174	ds->memory.wram7 = anonymousMemoryMap(DS7_SIZE_WORKING_RAM);
 175
 176	if (ds->memory.ram) {
 177		mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
 178	}
 179	ds->memory.ram = anonymousMemoryMap(DS_SIZE_RAM);
 180
 181	if (ds->memory.itcm) {
 182		mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
 183	}
 184	ds->memory.itcm = anonymousMemoryMap(DS9_SIZE_ITCM);
 185
 186	if (ds->memory.dtcm) {
 187		mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
 188	}
 189	ds->memory.dtcm = anonymousMemoryMap(DS9_SIZE_DTCM);
 190
 191	memset(ds->ds7.memory.dma, 0, sizeof(ds->ds7.memory.dma));
 192	memset(ds->ds9.memory.dma, 0, sizeof(ds->ds9.memory.dma));
 193	ds->ds7.memory.activeDMA = -1;
 194	ds->ds9.memory.activeDMA = -1;
 195
 196	// TODO: Correct size
 197	ds->memory.wramSize7 = 0x8000;
 198	ds->memory.wramBase7 = ds->memory.wram;
 199	ds->memory.wramSize9 = 0;
 200	ds->memory.wramBase9 = NULL;
 201
 202	ds->memory.slot1Owner = true;
 203	ds->memory.slot2Owner = true;
 204	ds->memory.slot1.savedataType = DS_SAVEDATA_AUTODETECT;
 205	ds->ds7.memory.slot1Access = true;
 206	ds->ds9.memory.slot1Access = false;
 207
 208	DSSPIReset(ds);
 209	DSSlot1Reset(ds);
 210
 211	DSVideoConfigureVRAM(ds, 0, 0, 1);
 212	DSVideoConfigureVRAM(ds, 1, 0, 1);
 213	DSVideoConfigureVRAM(ds, 2, 0, 1);
 214	DSVideoConfigureVRAM(ds, 3, 0, 1);
 215	DSVideoConfigureVRAM(ds, 4, 0, 1);
 216	DSVideoConfigureVRAM(ds, 5, 0, 1);
 217	DSVideoConfigureVRAM(ds, 6, 0, 1);
 218	DSVideoConfigureVRAM(ds, 7, 0, 1);
 219	DSVideoConfigureVRAM(ds, 8, 0, 1);
 220	DSConfigureWRAM(&ds->memory, 3);
 221
 222	if (!ds->memory.wram || !ds->memory.wram7 || !ds->memory.ram || !ds->memory.itcm || !ds->memory.dtcm) {
 223		DSMemoryDeinit(ds);
 224		mLOG(DS_MEM, FATAL, "Could not map memory");
 225	}
 226}
 227
 228static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
 229	struct DS* ds = (struct DS*) cpu->master;
 230	struct DSCoreMemory* memory = &ds->ds7.memory;
 231
 232	int newRegion = address >> DS_BASE_OFFSET;
 233
 234	memory->activeRegion = newRegion;
 235	switch (newRegion) {
 236	case DS_REGION_WORKING_RAM:
 237		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 238			cpu->memory.activeRegion = ds->memory.wram7;
 239			cpu->memory.activeMask = DS7_SIZE_WORKING_RAM - 1;
 240		} else if (ds->memory.wramSize7 == DS_SIZE_WORKING_RAM) {
 241			if (address & DS_SIZE_WORKING_RAM) {
 242				cpu->memory.activeRegion = ds->memory.wram;
 243			} else {
 244				cpu->memory.activeRegion = ds->memory.wramBase;
 245			}
 246			cpu->memory.activeMask = (ds->memory.wramSize7 << 1) - 1;
 247		} else {
 248			cpu->memory.activeRegion = ds->memory.wramBase;
 249			cpu->memory.activeMask = (ds->memory.wramSize7 - 1);
 250		}
 251		break;
 252	case DS7_REGION_BIOS:
 253		if (ds->memory.bios7) {
 254			cpu->memory.activeRegion = ds->memory.bios7;
 255			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
 256		} else {
 257			cpu->memory.activeRegion = _deadbeef;
 258			cpu->memory.activeMask = 0;
 259		}
 260		break;
 261	case DS_REGION_RAM:
 262		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 263			cpu->memory.activeRegion = ds->memory.ram;
 264			cpu->memory.activeMask = DS_SIZE_RAM - 1;
 265			break;
 266		}
 267		goto jump_error;
 268	case DS_REGION_VRAM:
 269		if (address < 0x06040000 && ds->memory.vram7[(address & 0x3FFFF) >> 17]) {
 270			// TODO: redzones
 271			cpu->memory.activeRegion = (uint32_t*) ds->memory.vram7[(address & 0x3FFFF) >> 17];
 272			cpu->memory.activeMask = 0x1FFFF;
 273			break;
 274		}
 275		// Fall through
 276	default:
 277	jump_error:
 278		memory->activeRegion = -1;
 279		cpu->memory.activeRegion = _deadbeef;
 280		cpu->memory.activeMask = 0;
 281		mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
 282		break;
 283	}
 284	cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion];
 285	cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion];
 286	cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion];
 287	cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion];
 288}
 289
 290uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 291	struct DS* ds = (struct DS*) cpu->master;
 292	struct DSMemory* memory = &ds->memory;
 293	uint32_t value = 0;
 294	int wait = ds->ds7.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 295
 296	switch (address >> DS_BASE_OFFSET) {
 297	case DS7_REGION_BIOS:
 298		LOAD_32(value, address & (DS7_SIZE_BIOS - 4), memory->bios7);
 299		break;
 300	case DS_REGION_WORKING_RAM:
 301		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 302			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 4), memory->wram7);
 303		} else {
 304			LOAD_32(value, address & (ds->memory.wramSize7 - 4), memory->wramBase7);
 305		}
 306		break;
 307	case DS_REGION_RAM:
 308		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 309			LOAD_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
 310			break;
 311		}
 312		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
 313		break;
 314	case DS_REGION_IO:
 315		value = DS7IORead32(ds, address & 0x00FFFFFC);
 316		break;
 317	case DS_REGION_VRAM:
 318		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 319			LOAD_32(value, address & 0x1FFFC, memory->vram7[(address & 0x3FFFF) >> 17]);
 320			break;
 321		}
 322		// Fall through
 323	default:
 324		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
 325		break;
 326	}
 327
 328	if (cycleCounter) {
 329		wait += 2;
 330		*cycleCounter += wait;
 331	}
 332	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 333	int rotate = (address & 3) << 3;
 334	return ROR(value, rotate);
 335}
 336
 337uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 338	struct DS* ds = (struct DS*) cpu->master;
 339	struct DSMemory* memory = &ds->memory;
 340	uint32_t value = 0;
 341	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 342
 343	switch (address >> DS_BASE_OFFSET) {
 344	case DS7_REGION_BIOS:
 345		LOAD_16(value, address & (DS7_SIZE_BIOS - 2), memory->bios7);
 346		break;
 347	case DS_REGION_WORKING_RAM:
 348		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 349			LOAD_16(value, address & (DS7_SIZE_WORKING_RAM - 2), memory->wram7);
 350		} else {
 351			LOAD_16(value, address & (ds->memory.wramSize7 - 2), memory->wramBase7);
 352		}
 353		break;
 354	case DS_REGION_RAM:
 355		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 356			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 357			break;
 358		}
 359		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
 360	case DS_REGION_IO:
 361		value = DS7IORead(ds, address & DS_OFFSET_MASK);
 362		break;
 363	case DS_REGION_VRAM:
 364		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 365			LOAD_16(value, address & 0x1FFFE, memory->vram7[(address & 0x3FFFF) >> 17]);
 366			break;
 367		}
 368		// Fall through
 369	default:
 370		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
 371		break;
 372	}
 373
 374	if (cycleCounter) {
 375		wait += 2;
 376		*cycleCounter += wait;
 377	}
 378	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 379	int rotate = (address & 1) << 3;
 380	return ROR(value, rotate);
 381}
 382
 383uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 384	struct DS* ds = (struct DS*) cpu->master;
 385	struct DSMemory* memory = &ds->memory;
 386	uint32_t value = 0;
 387	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 388
 389	switch (address >> DS_BASE_OFFSET) {
 390	case DS7_REGION_BIOS:
 391		value = ((uint8_t*) memory->bios7)[address & (DS7_SIZE_BIOS - 1)];
 392		break;
 393	case DS_REGION_WORKING_RAM:
 394		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 395			value = ((uint8_t*) memory->wram7)[address & (DS7_SIZE_WORKING_RAM - 1)];
 396		} else {
 397			value = ((uint8_t*) memory->wramBase7)[address & (ds->memory.wramSize7 - 1)];
 398		}
 399		break;
 400	case DS_REGION_RAM:
 401		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 402			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
 403			break;
 404		}
 405		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
 406		break;
 407	case DS_REGION_IO:
 408		value = (DS7IORead(ds, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF;
 409		break;
 410	default:
 411		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
 412		break;
 413	}
 414
 415	if (cycleCounter) {
 416		wait += 2;
 417		*cycleCounter += wait;
 418	}
 419	return value;
 420}
 421
 422void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
 423	struct DS* ds = (struct DS*) cpu->master;
 424	struct DSMemory* memory = &ds->memory;
 425	int wait = ds->ds7.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 426
 427	switch (address >> DS_BASE_OFFSET) {
 428	case DS_REGION_WORKING_RAM:
 429		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 430			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 4), memory->wram7);
 431		} else {
 432			STORE_32(value, address & (ds->memory.wramSize7 - 4), memory->wramBase7);
 433		}
 434		break;
 435	case DS_REGION_RAM:
 436		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 437			STORE_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
 438			break;
 439		}
 440		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
 441		break;
 442	case DS_REGION_IO:
 443		DS7IOWrite32(ds, address & DS_OFFSET_MASK, value);
 444		break;
 445	case DS_REGION_VRAM:
 446		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 447			STORE_32(value, address & 0x1FFFC, memory->vram7[(address & 0x3FFFF) >> 17]);
 448			break;
 449		}
 450		// Fall through
 451	default:
 452		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
 453		break;
 454	}
 455
 456	if (cycleCounter) {
 457		++wait;
 458		*cycleCounter += wait;
 459	}
 460}
 461
 462void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
 463	struct DS* ds = (struct DS*) cpu->master;
 464	struct DSMemory* memory = &ds->memory;
 465	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 466
 467	switch (address >> DS_BASE_OFFSET) {
 468	case DS_REGION_WORKING_RAM:
 469		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 470			STORE_16(value, address & (DS7_SIZE_WORKING_RAM - 2), memory->wram7);
 471		} else {
 472			STORE_16(value, address & (ds->memory.wramSize7 - 2), memory->wramBase7);
 473		}
 474		break;
 475	case DS_REGION_RAM:
 476		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 477			STORE_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
 478			break;
 479		}
 480		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
 481		break;
 482	case DS_REGION_IO:
 483		DS7IOWrite(ds, address & DS_OFFSET_MASK, value);
 484		break;
 485	case DS_REGION_VRAM:
 486		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 487			STORE_16(value, address & 0x1FFFE, memory->vram7[(address & 0x3FFFF) >> 17]);
 488			break;
 489		}
 490		// Fall through
 491	default:
 492		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
 493		break;
 494	}
 495
 496	if (cycleCounter) {
 497		++wait;
 498		*cycleCounter += wait;
 499	}
 500}
 501
 502void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
 503	struct DS* ds = (struct DS*) cpu->master;
 504	struct DSMemory* memory = &ds->memory;
 505	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 506
 507	switch (address >> DS_BASE_OFFSET) {
 508	case DS_REGION_WORKING_RAM:
 509		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 510			((uint8_t*) memory->wram7)[address & (DS7_SIZE_WORKING_RAM - 1)] = value;
 511		} else {
 512			((uint8_t*) memory->wramBase7)[address & (ds->memory.wramSize7 - 1)] = value;
 513		}
 514		break;
 515	case DS_REGION_RAM:
 516		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 517			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
 518			break;
 519		}
 520		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
 521	case DS_REGION_IO:
 522		DS7IOWrite8(ds, address & DS_OFFSET_MASK, value);
 523		break;
 524	default:
 525		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
 526		break;
 527	}
 528
 529	if (cycleCounter) {
 530		++wait;
 531		*cycleCounter += wait;
 532	}
 533}
 534
 535#define LDM_LOOP(LDM) \
 536	for (i = 0; i < 16; i += 4) { \
 537		if (UNLIKELY(mask & (1 << i))) { \
 538			LDM; \
 539			cpu->gprs[i] = value; \
 540			++wait; \
 541			wait += ws32[address >> DS_BASE_OFFSET]; \
 542			address += 4; \
 543		} \
 544		if (UNLIKELY(mask & (2 << i))) { \
 545			LDM; \
 546			cpu->gprs[i + 1] = value; \
 547			++wait; \
 548			wait += ws32[address >> DS_BASE_OFFSET]; \
 549			address += 4; \
 550		} \
 551		if (UNLIKELY(mask & (4 << i))) { \
 552			LDM; \
 553			cpu->gprs[i + 2] = value; \
 554			++wait; \
 555			wait += ws32[address >> DS_BASE_OFFSET]; \
 556			address += 4; \
 557		} \
 558		if (UNLIKELY(mask & (8 << i))) { \
 559			LDM; \
 560			cpu->gprs[i + 3] = value; \
 561			++wait; \
 562			wait += ws32[address >> DS_BASE_OFFSET]; \
 563			address += 4; \
 564		} \
 565	}
 566
 567#define STM_LOOP(STM) \
 568	for (i = 0; i < 16; i += 4) { \
 569		if (UNLIKELY(mask & (1 << i))) { \
 570			value = cpu->gprs[i]; \
 571			STM; \
 572			++wait; \
 573			wait += ws32[address >> DS_BASE_OFFSET]; \
 574			address += 4; \
 575		} \
 576		if (UNLIKELY(mask & (2 << i))) { \
 577			value = cpu->gprs[i + 1]; \
 578			STM; \
 579			++wait; \
 580			wait += ws32[address >> DS_BASE_OFFSET]; \
 581			address += 4; \
 582		} \
 583		if (UNLIKELY(mask & (4 << i))) { \
 584			value = cpu->gprs[i + 2]; \
 585			STM; \
 586			++wait; \
 587			wait += ws32[address >> DS_BASE_OFFSET]; \
 588			address += 4; \
 589		} \
 590		if (UNLIKELY(mask & (8 << i))) { \
 591			value = cpu->gprs[i + 3]; \
 592			STM; \
 593			++wait; \
 594			wait += ws32[address >> DS_BASE_OFFSET]; \
 595			address += 4; \
 596		} \
 597	}
 598
 599uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 600	struct DS* ds = (struct DS*) cpu->master;
 601	struct DSMemory* memory = &ds->memory;
 602	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
 603	uint32_t value;
 604	int wait = 0;
 605
 606	int i;
 607	int offset = 4;
 608	int popcount = 0;
 609	if (direction & LSM_D) {
 610		offset = -4;
 611		popcount = popcount32(mask);
 612		address -= (popcount << 2) - 4;
 613	}
 614
 615	if (direction & LSM_B) {
 616		address += offset;
 617	}
 618
 619	uint32_t addressMisalign = address & 0x3;
 620	address &= 0xFFFFFFFC;
 621
 622	switch (address >> DS_BASE_OFFSET) {
 623	case DS_REGION_WORKING_RAM:
 624		LDM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 625			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 626		} else {
 627			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wramBase7);
 628		});
 629		break;
 630	case DS_REGION_RAM:
 631		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 632			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 633		} else {
 634			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 635		});
 636		break;
 637	case DS_REGION_IO:
 638		LDM_LOOP(value = DS7IORead32(ds, address));
 639		break;
 640	case DS_REGION_VRAM:
 641		LDM_LOOP(if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 642			LOAD_32(value, address & 0x1FFFF, memory->vram7[(address & 0x3FFFF) >> 17]);
 643		} else {
 644			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 645		});
 646		break;
 647	default:
 648		mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 649		LDM_LOOP(value = 0);
 650	}
 651
 652	if (cycleCounter) {
 653		++wait;
 654		*cycleCounter += wait;
 655	}
 656
 657	if (direction & LSM_B) {
 658		address -= offset;
 659	}
 660
 661	if (direction & LSM_D) {
 662		address -= (popcount << 2) + 4;
 663	}
 664
 665	return address | addressMisalign;
 666}
 667
 668uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 669	struct DS* ds = (struct DS*) cpu->master;
 670	struct DSMemory* memory = &ds->memory;
 671	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
 672	uint32_t value;
 673	int wait = 0;
 674
 675	int i;
 676	int offset = 4;
 677	int popcount = 0;
 678	if (direction & LSM_D) {
 679		offset = -4;
 680		popcount = popcount32(mask);
 681		address -= (popcount << 2) - 4;
 682	}
 683
 684	if (direction & LSM_B) {
 685		address += offset;
 686	}
 687
 688	uint32_t addressMisalign = address & 0x3;
 689	address &= 0xFFFFFFFC;
 690
 691	switch (address >> DS_BASE_OFFSET) {
 692	case DS_REGION_WORKING_RAM:
 693		STM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 694			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 695		} else {
 696			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wramBase7);
 697		});
 698		break;
 699	case DS_REGION_RAM:
 700		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 701			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 702		} else {
 703			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 704		});
 705		break;
 706	case DS_REGION_IO:
 707		STM_LOOP(DS7IOWrite32(ds, address & DS_OFFSET_MASK, value));
 708		break;
 709	case DS_REGION_VRAM:
 710		STM_LOOP(if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 711			STORE_32(value, address & 0x1FFFF, memory->vram7[(address & 0x3FFFF) >> 17]);
 712		} else {
 713			mLOG(DS_MEM, STUB, "Unimplemented DS7 STM: %08X", address);
 714		});
 715		break;
 716	default:
 717		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 718		STM_LOOP();
 719		break;
 720	}
 721
 722	if (cycleCounter) {
 723		*cycleCounter += wait;
 724	}
 725
 726	if (direction & LSM_B) {
 727		address -= offset;
 728	}
 729
 730	if (direction & LSM_D) {
 731		address -= (popcount << 2) + 4;
 732	}
 733
 734	return address | addressMisalign;
 735}
 736
 737static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
 738	struct DS* ds = (struct DS*) cpu->master;
 739	struct DSCoreMemory* memory = &ds->ds9.memory;
 740
 741	int newRegion = address >> DS_BASE_OFFSET;
 742
 743	memory->activeRegion = newRegion;
 744	switch (newRegion) {
 745	case DS9_REGION_ITCM:
 746	case DS9_REGION_ITCM_MIRROR:
 747		if (address < ds->memory.itcmSize) {
 748			cpu->memory.activeRegion = ds->memory.itcm;
 749			cpu->memory.activeMask = DS9_SIZE_ITCM - 1;
 750			break;
 751		}
 752		goto jump_error;
 753	case DS_REGION_RAM:
 754		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 755			cpu->memory.activeRegion = ds->memory.ram;
 756			cpu->memory.activeMask = DS_SIZE_RAM - 1;
 757			break;
 758		}
 759		goto jump_error;
 760	case DS9_REGION_BIOS:
 761		// TODO: Mask properly
 762		if (ds->memory.bios9) {
 763			cpu->memory.activeRegion = ds->memory.bios9;
 764			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
 765		} else {
 766			cpu->memory.activeRegion = _deadbeef;
 767			cpu->memory.activeMask = 0;
 768		}
 769		cpu->memory.activeSeqCycles32 = 0;
 770		cpu->memory.activeSeqCycles16 = 0;
 771		cpu->memory.activeNonseqCycles32 = 0;
 772		cpu->memory.activeNonseqCycles16 = 0;
 773		return;
 774	default:
 775	jump_error:
 776		memory->activeRegion = -1;
 777		cpu->memory.activeRegion = _deadbeef;
 778		cpu->memory.activeMask = 0;
 779		mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
 780		return;
 781	}
 782	cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion];
 783	cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion];
 784	cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion];
 785	cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion];
 786}
 787
 788uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 789	struct DS* ds = (struct DS*) cpu->master;
 790	struct DSMemory* memory = &ds->memory;
 791	uint32_t value = 0;
 792	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 793
 794	switch (address >> DS_BASE_OFFSET) {
 795	case DS9_REGION_ITCM:
 796	case DS9_REGION_ITCM_MIRROR:
 797		if (address < memory->itcmSize) {
 798			LOAD_32(value, address & (DS9_SIZE_ITCM - 4), memory->itcm);
 799			break;
 800		}
 801		mLOG(DS_MEM, STUB, "Bad DS9 Load32: %08X", address);
 802		break;
 803	case DS_REGION_WORKING_RAM:
 804		if (ds->memory.wramSize9) {
 805			LOAD_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
 806			break;
 807		}
 808		mLOG(DS_MEM, STUB, "Bad DS9 Load32: %08X", address);
 809		break;
 810	case DS_REGION_RAM:
 811		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 812			LOAD_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
 813			break;
 814		}
 815		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 816			LOAD_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
 817			break;
 818		}
 819		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 820		break;
 821	case DS_REGION_IO:
 822		value = DS9IORead32(ds, address & 0x00FFFFFC);
 823		break;
 824	case DS9_REGION_PALETTE_RAM:
 825		LOAD_32(value, address & (DS9_SIZE_PALETTE_RAM - 4), ds->video.palette);
 826		break;
 827	case DS_REGION_VRAM: {
 828		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
 829		int i = 0;
 830		for (i = 0; i < 9; ++i) {
 831			if (mask & (1 << i)) {
 832				uint32_t newValue;
 833				LOAD_32(newValue, address & _vramMask[i], memory->vramBank[i]);
 834				value |= newValue;
 835			}
 836		}
 837		break;
 838	}
 839	case DS9_REGION_OAM:
 840		LOAD_32(value, address & (DS9_SIZE_OAM - 4), ds->video.oam.raw);
 841		break;
 842	case DS9_REGION_BIOS:
 843		// TODO: Fix undersized BIOS
 844		// TODO: Fix masking
 845		LOAD_32(value, address & (DS9_SIZE_BIOS - 4), memory->bios9);
 846		break;
 847	default:
 848		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 849			LOAD_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
 850			break;
 851		}
 852		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 853		break;
 854	}
 855
 856	if (cycleCounter) {
 857		wait += 2;
 858		*cycleCounter += wait;
 859	}
 860	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 861	int rotate = (address & 3) << 3;
 862	return ROR(value, rotate);
 863}
 864
 865uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 866	struct DS* ds = (struct DS*) cpu->master;
 867	struct DSMemory* memory = &ds->memory;
 868	uint32_t value = 0;
 869	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 870
 871	switch (address >> DS_BASE_OFFSET) {
 872	case DS9_REGION_ITCM:
 873	case DS9_REGION_ITCM_MIRROR:
 874		if (address < memory->itcmSize) {
 875			LOAD_16(value, address & (DS9_SIZE_ITCM - 2), memory->itcm);
 876			break;
 877		}
 878		mLOG(DS_MEM, STUB, "Bad DS9 Load16: %08X", address);
 879		break;
 880	case DS_REGION_WORKING_RAM:
 881		if (ds->memory.wramSize9) {
 882			LOAD_16(value, address & (ds->memory.wramSize9 - 2), memory->wramBase9);
 883			break;
 884		}
 885		mLOG(DS_MEM, STUB, "Bad DS9 Load16: %08X", address);
 886		break;
 887	case DS_REGION_RAM:
 888		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 889			LOAD_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
 890			break;
 891		}
 892		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 893			LOAD_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
 894			break;
 895		}
 896		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 897	case DS_REGION_IO:
 898		value = DS9IORead(ds, address & DS_OFFSET_MASK);
 899		break;
 900	case DS9_REGION_PALETTE_RAM:
 901		LOAD_16(value, address & (DS9_SIZE_PALETTE_RAM - 2), ds->video.palette);
 902		break;
 903	case DS_REGION_VRAM: {
 904		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
 905		int i = 0;
 906		for (i = 0; i < 9; ++i) {
 907			if (mask & (1 << i)) {
 908				uint32_t newValue;
 909				LOAD_16(newValue, address & _vramMask[i], memory->vramBank[i]);
 910				value |= newValue;
 911			}
 912		}
 913		break;
 914	}
 915	case DS9_REGION_OAM:
 916		LOAD_16(value, address & (DS9_SIZE_OAM - 2), ds->video.oam.raw);
 917		break;
 918	case DS9_REGION_BIOS:
 919		// TODO: Fix undersized BIOS
 920		// TODO: Fix masking
 921		LOAD_16(value, address & (DS9_SIZE_BIOS - 2), memory->bios9);
 922		break;
 923	default:
 924		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 925			LOAD_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
 926			break;
 927		}
 928		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 929		break;
 930	}
 931
 932	if (cycleCounter) {
 933		wait += 2;
 934		*cycleCounter += wait;
 935	}
 936	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 937	int rotate = (address & 1) << 3;
 938	return ROR(value, rotate);
 939}
 940
 941uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 942	struct DS* ds = (struct DS*) cpu->master;
 943	struct DSMemory* memory = &ds->memory;
 944	uint32_t value = 0;
 945	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 946
 947	switch (address >> DS_BASE_OFFSET) {
 948	case DS9_REGION_ITCM:
 949	case DS9_REGION_ITCM_MIRROR:
 950		if (address < memory->itcmSize) {
 951			value = ((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)];
 952			break;
 953		}
 954		mLOG(DS_MEM, STUB, "Bad DS9 Load8: %08X", address);
 955		break;
 956	case DS_REGION_WORKING_RAM:
 957		if (ds->memory.wramSize9) {
 958			value = ((uint8_t*) memory->wramBase9)[address & (memory->wramSize9 - 1)];
 959			break;
 960		}
 961		mLOG(DS_MEM, STUB, "Bad DS9 Load8: %08X", address);
 962		break;
 963	case DS_REGION_RAM:
 964		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 965			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
 966			break;
 967		}
 968		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 969			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
 970			break;
 971		}
 972		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
 973	case DS_REGION_IO:
 974		value = (DS9IORead(ds, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF;
 975		break;
 976	case DS_REGION_VRAM: {
 977		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
 978		int i = 0;
 979		for (i = 0; i < 9; ++i) {
 980			if (mask & (1 << i)) {
 981				value |= ((uint8_t*) memory->vramBank[i])[address & _vramMask[i]];
 982			}
 983		}
 984		break;
 985	}
 986	case DS9_REGION_BIOS:
 987		// TODO: Fix undersized BIOS
 988		// TODO: Fix masking
 989		value = ((uint8_t*) memory->bios9)[address & (DS9_SIZE_BIOS - 1)];
 990		break;
 991	default:
 992		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 993			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
 994			break;
 995		}
 996		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
 997		break;
 998	}
 999
1000	if (cycleCounter) {
1001		wait += 2;
1002		*cycleCounter += wait;
1003	}
1004	return value;
1005}
1006
1007void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
1008	struct DS* ds = (struct DS*) cpu->master;
1009	struct DSMemory* memory = &ds->memory;
1010	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
1011
1012	switch (address >> DS_BASE_OFFSET) {
1013	case DS9_REGION_ITCM:
1014	case DS9_REGION_ITCM_MIRROR:
1015		if (address < memory->itcmSize) {
1016			STORE_32(value, address & (DS9_SIZE_ITCM - 4), memory->itcm);
1017			break;
1018		}
1019		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
1020		break;
1021	case DS_REGION_WORKING_RAM:
1022		if (ds->memory.wramSize9) {
1023			STORE_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1024			break;
1025		}
1026		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
1027		break;
1028	case DS_REGION_RAM:
1029		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1030			STORE_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
1031			break;
1032		}
1033		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1034			STORE_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
1035			break;
1036		}
1037		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
1038		break;
1039	case DS_REGION_IO:
1040		DS9IOWrite32(ds, address & DS_OFFSET_MASK, value);
1041		break;
1042	case DS9_REGION_PALETTE_RAM:
1043		STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 4), ds->video.palette);
1044		ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16);
1045		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value);
1046		break;
1047	case DS_REGION_VRAM: {
1048		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1049		int i = 0;
1050		for (i = 0; i < 9; ++i) {
1051			if (mask & (1 << i)) {
1052				STORE_32(value, address & _vramMask[i], memory->vramBank[i]);
1053			}
1054		}
1055		break;
1056	}
1057	case DS9_REGION_OAM:
1058		STORE_32(value, address & (DS9_SIZE_OAM - 4), ds->video.oam.raw);
1059		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 4)) >> 1);
1060		ds->video.renderer->writeOAM(ds->video.renderer, ((address & (DS9_SIZE_OAM - 4)) >> 1) + 1);
1061		break;
1062	default:
1063		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1064			STORE_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
1065			break;
1066		}
1067		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
1068		break;
1069	}
1070
1071	if (cycleCounter) {
1072		++wait;
1073		*cycleCounter += wait;
1074	}
1075}
1076
1077void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
1078	struct DS* ds = (struct DS*) cpu->master;
1079	struct DSMemory* memory = &ds->memory;
1080	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
1081
1082	switch (address >> DS_BASE_OFFSET) {
1083	case DS9_REGION_ITCM:
1084	case DS9_REGION_ITCM_MIRROR:
1085		if (address < memory->itcmSize) {
1086			STORE_16(value, address & (DS9_SIZE_ITCM - 2), memory->itcm);
1087			break;
1088		}
1089		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
1090		break;
1091	case DS_REGION_WORKING_RAM:
1092		if (ds->memory.wramSize9) {
1093			STORE_16(value, address & (ds->memory.wramSize9 - 2), memory->wramBase9);
1094			break;
1095		}
1096		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
1097		break;
1098	case DS_REGION_RAM:
1099		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1100			STORE_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
1101			break;
1102		}
1103		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1104			STORE_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
1105			break;
1106		}
1107		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
1108		break;
1109	case DS_REGION_IO:
1110		DS9IOWrite(ds, address & DS_OFFSET_MASK, value);
1111		break;
1112	case DS9_REGION_PALETTE_RAM:
1113		STORE_16(value, address & (DS9_SIZE_PALETTE_RAM - 2), ds->video.palette);
1114		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 2), value);
1115		break;
1116	case DS_REGION_VRAM: {
1117		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1118		int i = 0;
1119		for (i = 0; i < 9; ++i) {
1120			if (mask & (1 << i)) {
1121				STORE_16(value, address & _vramMask[i], memory->vramBank[i]);
1122			}
1123		}
1124		break;
1125	}
1126	case DS9_REGION_OAM:
1127		STORE_16(value, address & (DS9_SIZE_OAM - 2), ds->video.oam.raw);
1128		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 2)) >> 1);
1129		break;
1130	default:
1131		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1132			STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1133			break;
1134		}
1135		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
1136		break;
1137	}
1138
1139	if (cycleCounter) {
1140		++wait;
1141		*cycleCounter += wait;
1142	}
1143}
1144
1145void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
1146	struct DS* ds = (struct DS*) cpu->master;
1147	struct DSMemory* memory = &ds->memory;
1148	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
1149
1150	switch (address >> DS_BASE_OFFSET) {
1151	case DS9_REGION_ITCM:
1152	case DS9_REGION_ITCM_MIRROR:
1153		if (address < memory->itcmSize) {
1154			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
1155			break;
1156		}
1157		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
1158		break;
1159	case DS_REGION_WORKING_RAM:
1160		if (ds->memory.wramSize9) {
1161			((uint8_t*) memory->wramBase9)[address & (ds->memory.wramSize9 - 1)] = value;
1162			break;
1163		}
1164		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
1165		break;
1166	case DS_REGION_RAM:
1167		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1168			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
1169			break;
1170		}
1171		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1172			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
1173			break;
1174		}
1175		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
1176	case DS_REGION_IO:
1177		DS9IOWrite8(ds, address & DS_OFFSET_MASK, value);
1178		break;
1179	default:
1180		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1181			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
1182			break;
1183		}
1184		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
1185		break;
1186	}
1187
1188	if (cycleCounter) {
1189		++wait;
1190		*cycleCounter += wait;
1191	}
1192}
1193
1194uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
1195	struct DS* ds = (struct DS*) cpu->master;
1196	struct DSMemory* memory = &ds->memory;
1197	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
1198	uint32_t value;
1199	int wait = 0;
1200
1201	int i;
1202	int offset = 4;
1203	int popcount = 0;
1204	if (direction & LSM_D) {
1205		offset = -4;
1206		popcount = popcount32(mask);
1207		address -= (popcount << 2) - 4;
1208	}
1209
1210	if (direction & LSM_B) {
1211		address += offset;
1212	}
1213
1214	uint32_t addressMisalign = address & 0x3;
1215	address &= 0xFFFFFFFC;
1216
1217	switch (address >> DS_BASE_OFFSET) {
1218	case DS9_REGION_ITCM:
1219	case DS9_REGION_ITCM_MIRROR:
1220		LDM_LOOP(if (address < memory->itcmSize) {
1221			LOAD_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
1222		} else {
1223			mLOG(DS_MEM, STUB, "Bad DS9 LDM: %08X:%08X", address, value);
1224		});
1225		break;
1226	case DS_REGION_WORKING_RAM:
1227		LDM_LOOP(if (ds->memory.wramSize9) {
1228			LOAD_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1229		} else {
1230			mLOG(DS_MEM, STUB, "Bad DS9 LDM: %08X", address);
1231		});
1232		break;
1233	case DS_REGION_RAM:
1234		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1235			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1236		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1237			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
1238		} else {
1239			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
1240		});
1241		break;
1242	case DS_REGION_IO:
1243		LDM_LOOP(value = DS9IORead32(ds, address));
1244		break;
1245	case DS9_REGION_PALETTE_RAM:
1246		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette));
1247		break;
1248	case DS_REGION_VRAM:
1249		LDM_LOOP(unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1250		value = 0;
1251		int i = 0;
1252		for (i = 0; i < 9; ++i) {
1253			if (mask & (1 << i)) {
1254				uint32_t newValue;
1255				LOAD_32(newValue, address & _vramMask[i], memory->vramBank[i]);
1256				value |= newValue;
1257			}
1258		});
1259		break;
1260	case DS9_REGION_OAM:
1261		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_OAM - 1), ds->video.oam.raw));
1262		break;
1263	default:
1264		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1265			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1266		} else {
1267			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
1268		});
1269		break;
1270	}
1271
1272	if (cycleCounter) {
1273		++wait;
1274		*cycleCounter += wait;
1275	}
1276
1277	if (direction & LSM_B) {
1278		address -= offset;
1279	}
1280
1281	if (direction & LSM_D) {
1282		address -= (popcount << 2) + 4;
1283	}
1284
1285	return address | addressMisalign;
1286}
1287
1288
1289uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
1290	struct DS* ds = (struct DS*) cpu->master;
1291	struct DSMemory* memory = &ds->memory;
1292	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
1293	uint32_t value;
1294	int wait = 0;
1295
1296	int i;
1297	int offset = 4;
1298	int popcount = 0;
1299	if (direction & LSM_D) {
1300		offset = -4;
1301		popcount = popcount32(mask);
1302		address -= (popcount << 2) - 4;
1303	}
1304
1305	if (direction & LSM_B) {
1306		address += offset;
1307	}
1308
1309	uint32_t addressMisalign = address & 0x3;
1310	address &= 0xFFFFFFFC;
1311
1312	switch (address >> DS_BASE_OFFSET) {
1313	case DS9_REGION_ITCM:
1314	case DS9_REGION_ITCM_MIRROR:
1315		STM_LOOP(if (address < memory->itcmSize) {
1316			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
1317		} else {
1318			mLOG(DS_MEM, STUB, "Bad DS9 STM: %08X:%08X", address, value);
1319		});
1320		break;
1321	case DS_REGION_WORKING_RAM:
1322		STM_LOOP(if (ds->memory.wramSize9) {
1323			STORE_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1324		} else {
1325			mLOG(DS_MEM, STUB, "Bad DS9 STM: %08X", address);
1326		});
1327		break;
1328	case DS_REGION_RAM:
1329		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1330			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1331		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1332			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
1333		} else {
1334			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1335		});
1336		break;
1337	case DS_REGION_IO:
1338		STM_LOOP(DS9IOWrite32(ds, address & DS_OFFSET_MASK, value));
1339		break;
1340	case DS9_REGION_PALETTE_RAM:
1341		STM_LOOP(STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette);
1342		ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16);
1343		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value));
1344		break;
1345	case DS_REGION_VRAM:
1346		STM_LOOP(unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1347		int i = 0;
1348		for (i = 0; i < 9; ++i) {
1349			if (mask & (1 << i)) {
1350				STORE_32(value, address & _vramMask[i], memory->vramBank[i]);
1351			}
1352		});
1353		break;
1354	case DS9_REGION_OAM:
1355		STM_LOOP(STORE_32(value, address & (DS9_SIZE_OAM - 1), ds->video.oam.raw);
1356		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 4)) >> 1);
1357		ds->video.renderer->writeOAM(ds->video.renderer, ((address & (DS9_SIZE_OAM - 4)) >> 1) + 1));
1358		break;
1359	default:
1360		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1361			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1362		} else {
1363			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1364		});
1365		break;
1366	}
1367
1368	if (cycleCounter) {
1369		*cycleCounter += wait;
1370	}
1371
1372	if (direction & LSM_B) {
1373		address -= offset;
1374	}
1375
1376	if (direction & LSM_D) {
1377		address -= (popcount << 2) + 4;
1378	}
1379
1380	return address | addressMisalign;
1381}
1382
1383int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
1384	return wait;
1385}
1386
1387void DSConfigureWRAM(struct DSMemory* memory, uint8_t config) {
1388	switch (config & 3) {
1389	case 0:
1390		memory->wramSize7 = 0;
1391		memory->wramBase7 = NULL;
1392		memory->wramSize9 = DS_SIZE_WORKING_RAM;
1393		memory->wramBase9 = memory->wramBase;
1394		break;
1395	case 1:
1396		memory->wramSize7 = DS_SIZE_WORKING_RAM >> 1;
1397		memory->wramBase7 = memory->wram;
1398		memory->wramSize9 = DS_SIZE_WORKING_RAM >> 1;
1399		memory->wramBase9 = &memory->wramBase[DS_SIZE_WORKING_RAM >> 3];
1400		break;
1401	case 2:
1402		memory->wramSize7 = DS_SIZE_WORKING_RAM >> 1;
1403		memory->wramBase7 = &memory->wram[DS_SIZE_WORKING_RAM >> 3];
1404		memory->wramSize9 = DS_SIZE_WORKING_RAM >> 1;
1405		memory->wramBase9 = memory->wramBase;
1406		break;
1407	case 3:
1408		memory->wramSize7 = DS_SIZE_WORKING_RAM;
1409		memory->wramBase7 = memory->wramBase;
1410		memory->wramSize9 = 0;
1411		memory->wramBase9 = NULL;
1412		break;
1413	}
1414}
1415
1416void DSConfigureExternalMemory(struct DS* ds, uint16_t config) {
1417	// TODO: GBA params
1418	ds->memory.slot1Owner = config & 0x0800;
1419	ds->memory.slot2Owner = config & 0x0080;
1420	ds->memory.io7[DS7_REG_EXMEMSTAT >> 1] = config;
1421
1422	ds->ds7.memory.slot1Access = ds->memory.slot1Owner;
1423	ds->ds9.memory.slot1Access = !ds->memory.slot1Owner;
1424}
1425
1426static unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset) {
1427	unsigned mask = 0;
1428	offset &= 0x3FF;
1429	mask |= memory->vramMirror[0][offset & 0x3F] & memory->vramMode[0][offset >> 7];
1430	mask |= memory->vramMirror[1][offset & 0x3F] & memory->vramMode[1][offset >> 7];
1431	mask |= memory->vramMirror[2][offset & 0x3F] & memory->vramMode[2][offset >> 7];
1432	mask |= memory->vramMirror[3][offset & 0x3F] & memory->vramMode[3][offset >> 7];
1433	mask |= memory->vramMirror[4][offset & 0x3F] & memory->vramMode[4][offset >> 7];
1434	mask |= memory->vramMirror[5][offset & 0x3F] & memory->vramMode[5][offset >> 7];
1435	mask |= memory->vramMirror[6][offset & 0x3F] & memory->vramMode[6][offset >> 7];
1436	mask |= memory->vramMirror[7][offset & 0x3F] & memory->vramMode[7][offset >> 7];
1437	mask |= memory->vramMirror[8][offset & 0x3F] & memory->vramMode[8][offset >> 7];
1438	return mask;
1439}