all repos — mgba @ 1a0e44c014ad34bea30d237d27015d82d02cda4b

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, 2, 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, 1, 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_SLOT2:
 318	case DS_REGION_SLOT2_EX:
 319	case DS_REGION_SLOT2_SRAM:
 320		value = 0xFFFFFFFF;
 321		break;
 322	case DS_REGION_VRAM:
 323		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 324			LOAD_32(value, address & 0x1FFFC, memory->vram7[(address & 0x3FFFF) >> 17]);
 325			break;
 326		}
 327		// Fall through
 328	default:
 329		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
 330		break;
 331	}
 332
 333	if (cycleCounter) {
 334		wait += 2;
 335		*cycleCounter += wait;
 336	}
 337	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 338	int rotate = (address & 3) << 3;
 339	return ROR(value, rotate);
 340}
 341
 342uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 343	struct DS* ds = (struct DS*) cpu->master;
 344	struct DSMemory* memory = &ds->memory;
 345	uint32_t value = 0;
 346	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 347
 348	switch (address >> DS_BASE_OFFSET) {
 349	case DS7_REGION_BIOS:
 350		LOAD_16(value, address & (DS7_SIZE_BIOS - 2), memory->bios7);
 351		break;
 352	case DS_REGION_WORKING_RAM:
 353		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 354			LOAD_16(value, address & (DS7_SIZE_WORKING_RAM - 2), memory->wram7);
 355		} else {
 356			LOAD_16(value, address & (ds->memory.wramSize7 - 2), memory->wramBase7);
 357		}
 358		break;
 359	case DS_REGION_RAM:
 360		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 361			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 362			break;
 363		}
 364		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
 365	case DS_REGION_IO:
 366		value = DS7IORead(ds, address & DS_OFFSET_MASK);
 367		break;
 368	case DS_REGION_SLOT2:
 369	case DS_REGION_SLOT2_EX:
 370	case DS_REGION_SLOT2_SRAM:
 371		value = 0xFFFF;
 372		break;
 373	case DS_REGION_VRAM:
 374		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 375			LOAD_16(value, address & 0x1FFFE, memory->vram7[(address & 0x3FFFF) >> 17]);
 376			break;
 377		}
 378		// Fall through
 379	default:
 380		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
 381		break;
 382	}
 383
 384	if (cycleCounter) {
 385		wait += 2;
 386		*cycleCounter += wait;
 387	}
 388	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 389	int rotate = (address & 1) << 3;
 390	return ROR(value, rotate);
 391}
 392
 393uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 394	struct DS* ds = (struct DS*) cpu->master;
 395	struct DSMemory* memory = &ds->memory;
 396	uint32_t value = 0;
 397	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 398
 399	switch (address >> DS_BASE_OFFSET) {
 400	case DS7_REGION_BIOS:
 401		value = ((uint8_t*) memory->bios7)[address & (DS7_SIZE_BIOS - 1)];
 402		break;
 403	case DS_REGION_WORKING_RAM:
 404		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 405			value = ((uint8_t*) memory->wram7)[address & (DS7_SIZE_WORKING_RAM - 1)];
 406		} else {
 407			value = ((uint8_t*) memory->wramBase7)[address & (ds->memory.wramSize7 - 1)];
 408		}
 409		break;
 410	case DS_REGION_RAM:
 411		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 412			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
 413			break;
 414		}
 415		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
 416		break;
 417	case DS_REGION_IO:
 418		value = (DS7IORead(ds, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF;
 419		break;
 420	case DS_REGION_SLOT2:
 421	case DS_REGION_SLOT2_EX:
 422	case DS_REGION_SLOT2_SRAM:
 423		value = 0xFF;
 424		break;
 425	default:
 426		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
 427		break;
 428	}
 429
 430	if (cycleCounter) {
 431		wait += 2;
 432		*cycleCounter += wait;
 433	}
 434	return value;
 435}
 436
 437void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
 438	struct DS* ds = (struct DS*) cpu->master;
 439	struct DSMemory* memory = &ds->memory;
 440	int wait = ds->ds7.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 441
 442	switch (address >> DS_BASE_OFFSET) {
 443	case DS_REGION_WORKING_RAM:
 444		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 445			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 4), memory->wram7);
 446		} else {
 447			STORE_32(value, address & (ds->memory.wramSize7 - 4), memory->wramBase7);
 448		}
 449		break;
 450	case DS_REGION_RAM:
 451		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 452			STORE_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
 453			break;
 454		}
 455		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
 456		break;
 457	case DS_REGION_IO:
 458		DS7IOWrite32(ds, address & DS_OFFSET_MASK, value);
 459		break;
 460	case DS_REGION_VRAM:
 461		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 462			STORE_32(value, address & 0x1FFFC, memory->vram7[(address & 0x3FFFF) >> 17]);
 463			break;
 464		}
 465		// Fall through
 466	default:
 467		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
 468		break;
 469	}
 470
 471	if (cycleCounter) {
 472		++wait;
 473		*cycleCounter += wait;
 474	}
 475}
 476
 477void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
 478	struct DS* ds = (struct DS*) cpu->master;
 479	struct DSMemory* memory = &ds->memory;
 480	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 481
 482	switch (address >> DS_BASE_OFFSET) {
 483	case DS_REGION_WORKING_RAM:
 484		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 485			STORE_16(value, address & (DS7_SIZE_WORKING_RAM - 2), memory->wram7);
 486		} else {
 487			STORE_16(value, address & (ds->memory.wramSize7 - 2), memory->wramBase7);
 488		}
 489		break;
 490	case DS_REGION_RAM:
 491		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 492			STORE_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
 493			break;
 494		}
 495		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
 496		break;
 497	case DS_REGION_IO:
 498		DS7IOWrite(ds, address & DS_OFFSET_MASK, value);
 499		break;
 500	case DS_REGION_VRAM:
 501		if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 502			STORE_16(value, address & 0x1FFFE, memory->vram7[(address & 0x3FFFF) >> 17]);
 503			break;
 504		}
 505		// Fall through
 506	default:
 507		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
 508		break;
 509	}
 510
 511	if (cycleCounter) {
 512		++wait;
 513		*cycleCounter += wait;
 514	}
 515}
 516
 517void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
 518	struct DS* ds = (struct DS*) cpu->master;
 519	struct DSMemory* memory = &ds->memory;
 520	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 521
 522	switch (address >> DS_BASE_OFFSET) {
 523	case DS_REGION_WORKING_RAM:
 524		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 525			((uint8_t*) memory->wram7)[address & (DS7_SIZE_WORKING_RAM - 1)] = value;
 526		} else {
 527			((uint8_t*) memory->wramBase7)[address & (ds->memory.wramSize7 - 1)] = value;
 528		}
 529		break;
 530	case DS_REGION_RAM:
 531		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 532			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
 533			break;
 534		}
 535		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
 536	case DS_REGION_IO:
 537		DS7IOWrite8(ds, address & DS_OFFSET_MASK, value);
 538		break;
 539	default:
 540		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
 541		break;
 542	}
 543
 544	if (cycleCounter) {
 545		++wait;
 546		*cycleCounter += wait;
 547	}
 548}
 549
 550#define LDM_LOOP(LDM) \
 551	for (i = 0; i < 16; i += 4) { \
 552		if (UNLIKELY(mask & (1 << i))) { \
 553			LDM; \
 554			cpu->gprs[i] = value; \
 555			++wait; \
 556			wait += ws32[address >> DS_BASE_OFFSET]; \
 557			address += 4; \
 558		} \
 559		if (UNLIKELY(mask & (2 << i))) { \
 560			LDM; \
 561			cpu->gprs[i + 1] = value; \
 562			++wait; \
 563			wait += ws32[address >> DS_BASE_OFFSET]; \
 564			address += 4; \
 565		} \
 566		if (UNLIKELY(mask & (4 << i))) { \
 567			LDM; \
 568			cpu->gprs[i + 2] = value; \
 569			++wait; \
 570			wait += ws32[address >> DS_BASE_OFFSET]; \
 571			address += 4; \
 572		} \
 573		if (UNLIKELY(mask & (8 << i))) { \
 574			LDM; \
 575			cpu->gprs[i + 3] = value; \
 576			++wait; \
 577			wait += ws32[address >> DS_BASE_OFFSET]; \
 578			address += 4; \
 579		} \
 580	}
 581
 582#define STM_LOOP(STM) \
 583	for (i = 0; i < 16; i += 4) { \
 584		if (UNLIKELY(mask & (1 << i))) { \
 585			value = cpu->gprs[i]; \
 586			STM; \
 587			++wait; \
 588			wait += ws32[address >> DS_BASE_OFFSET]; \
 589			address += 4; \
 590		} \
 591		if (UNLIKELY(mask & (2 << i))) { \
 592			value = cpu->gprs[i + 1]; \
 593			STM; \
 594			++wait; \
 595			wait += ws32[address >> DS_BASE_OFFSET]; \
 596			address += 4; \
 597		} \
 598		if (UNLIKELY(mask & (4 << i))) { \
 599			value = cpu->gprs[i + 2]; \
 600			STM; \
 601			++wait; \
 602			wait += ws32[address >> DS_BASE_OFFSET]; \
 603			address += 4; \
 604		} \
 605		if (UNLIKELY(mask & (8 << i))) { \
 606			value = cpu->gprs[i + 3]; \
 607			STM; \
 608			++wait; \
 609			wait += ws32[address >> DS_BASE_OFFSET]; \
 610			address += 4; \
 611		} \
 612	}
 613
 614uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 615	struct DS* ds = (struct DS*) cpu->master;
 616	struct DSMemory* memory = &ds->memory;
 617	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
 618	uint32_t value;
 619	int wait = 0;
 620
 621	int i;
 622	int offset = 4;
 623	int popcount = 0;
 624	if (direction & LSM_D) {
 625		offset = -4;
 626		popcount = popcount32(mask);
 627		address -= (popcount << 2) - 4;
 628	}
 629
 630	if (direction & LSM_B) {
 631		address += offset;
 632	}
 633
 634	uint32_t addressMisalign = address & 0x3;
 635	address &= 0xFFFFFFFC;
 636
 637	switch (address >> DS_BASE_OFFSET) {
 638	case DS_REGION_WORKING_RAM:
 639		LDM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 640			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 641		} else {
 642			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wramBase7);
 643		});
 644		break;
 645	case DS_REGION_RAM:
 646		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 647			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 648		} else {
 649			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 650		});
 651		break;
 652	case DS_REGION_IO:
 653		LDM_LOOP(value = DS7IORead32(ds, address));
 654		break;
 655	case DS_REGION_VRAM:
 656		LDM_LOOP(if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 657			LOAD_32(value, address & 0x1FFFF, memory->vram7[(address & 0x3FFFF) >> 17]);
 658		} else {
 659			value = 0;
 660			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 661		});
 662		break;
 663	default:
 664		mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 665		LDM_LOOP(value = 0);
 666	}
 667
 668	if (cycleCounter) {
 669		++wait;
 670		*cycleCounter += wait;
 671	}
 672
 673	if (direction & LSM_B) {
 674		address -= offset;
 675	}
 676
 677	if (direction & LSM_D) {
 678		address -= (popcount << 2) + 4;
 679	}
 680
 681	return address | addressMisalign;
 682}
 683
 684uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 685	struct DS* ds = (struct DS*) cpu->master;
 686	struct DSMemory* memory = &ds->memory;
 687	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
 688	uint32_t value;
 689	int wait = 0;
 690
 691	int i;
 692	int offset = 4;
 693	int popcount = 0;
 694	if (direction & LSM_D) {
 695		offset = -4;
 696		popcount = popcount32(mask);
 697		address -= (popcount << 2) - 4;
 698	}
 699
 700	if (direction & LSM_B) {
 701		address += offset;
 702	}
 703
 704	uint32_t addressMisalign = address & 0x3;
 705	address &= 0xFFFFFFFC;
 706
 707	switch (address >> DS_BASE_OFFSET) {
 708	case DS_REGION_WORKING_RAM:
 709		STM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 710			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 711		} else {
 712			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wramBase7);
 713		});
 714		break;
 715	case DS_REGION_RAM:
 716		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 717			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 718		} else {
 719			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 720		});
 721		break;
 722	case DS_REGION_IO:
 723		STM_LOOP(DS7IOWrite32(ds, address & DS_OFFSET_MASK, value));
 724		break;
 725	case DS_REGION_VRAM:
 726		STM_LOOP(if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 727			STORE_32(value, address & 0x1FFFF, memory->vram7[(address & 0x3FFFF) >> 17]);
 728		} else {
 729			mLOG(DS_MEM, STUB, "Unimplemented DS7 STM: %08X", address);
 730		});
 731		break;
 732	default:
 733		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 734		STM_LOOP();
 735		break;
 736	}
 737
 738	if (cycleCounter) {
 739		*cycleCounter += wait;
 740	}
 741
 742	if (direction & LSM_B) {
 743		address -= offset;
 744	}
 745
 746	if (direction & LSM_D) {
 747		address -= (popcount << 2) + 4;
 748	}
 749
 750	return address | addressMisalign;
 751}
 752
 753static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
 754	struct DS* ds = (struct DS*) cpu->master;
 755	struct DSCoreMemory* memory = &ds->ds9.memory;
 756
 757	int newRegion = address >> DS_BASE_OFFSET;
 758
 759	memory->activeRegion = newRegion;
 760	switch (newRegion) {
 761	case DS9_REGION_ITCM:
 762	case DS9_REGION_ITCM_MIRROR:
 763		if (address < ds->memory.itcmSize) {
 764			cpu->memory.activeRegion = ds->memory.itcm;
 765			cpu->memory.activeMask = DS9_SIZE_ITCM - 1;
 766			break;
 767		}
 768		goto jump_error;
 769	case DS_REGION_RAM:
 770		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 771			cpu->memory.activeRegion = ds->memory.ram;
 772			cpu->memory.activeMask = DS_SIZE_RAM - 1;
 773			break;
 774		}
 775		goto jump_error;
 776	case DS9_REGION_BIOS:
 777		// TODO: Mask properly
 778		if (ds->memory.bios9) {
 779			cpu->memory.activeRegion = ds->memory.bios9;
 780			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
 781		} else {
 782			cpu->memory.activeRegion = _deadbeef;
 783			cpu->memory.activeMask = 0;
 784		}
 785		cpu->memory.activeSeqCycles32 = 0;
 786		cpu->memory.activeSeqCycles16 = 0;
 787		cpu->memory.activeNonseqCycles32 = 0;
 788		cpu->memory.activeNonseqCycles16 = 0;
 789		return;
 790	case DS_REGION_VRAM:
 791		if (address >= 0x06800000) {
 792			unsigned mask = _selectVRAM(&ds->memory, address >> DS_VRAM_OFFSET);
 793			if (mask) {
 794				int i = 0;
 795				for (i = 0; i < 9; ++i) {
 796					if (mask & (1 << i)) {
 797						cpu->memory.activeRegion = (uint32_t*) ds->memory.vramBank[i];
 798						cpu->memory.activeMask = _vramMask[i];
 799						break;
 800					}
 801				}
 802				break;
 803			}
 804		}
 805		// Fall through
 806	default:
 807	jump_error:
 808		memory->activeRegion = -1;
 809		cpu->memory.activeRegion = _deadbeef;
 810		cpu->memory.activeMask = 0;
 811		mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
 812		return;
 813	}
 814	cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion];
 815	cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion];
 816	cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion];
 817	cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion];
 818}
 819
 820uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 821	struct DS* ds = (struct DS*) cpu->master;
 822	struct DSMemory* memory = &ds->memory;
 823	uint32_t value = 0;
 824	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 825
 826	switch (address >> DS_BASE_OFFSET) {
 827	case DS9_REGION_ITCM:
 828	case DS9_REGION_ITCM_MIRROR:
 829		if (address < memory->itcmSize) {
 830			LOAD_32(value, address & (DS9_SIZE_ITCM - 4), memory->itcm);
 831			break;
 832		}
 833		mLOG(DS_MEM, STUB, "Bad DS9 Load32: %08X", address);
 834		break;
 835	case DS_REGION_WORKING_RAM:
 836		if (ds->memory.wramSize9) {
 837			LOAD_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
 838			break;
 839		}
 840		mLOG(DS_MEM, STUB, "Bad DS9 Load32: %08X", address);
 841		break;
 842	case DS_REGION_RAM:
 843		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 844			LOAD_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
 845			break;
 846		}
 847		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 848			LOAD_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
 849			break;
 850		}
 851		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 852		break;
 853	case DS_REGION_IO:
 854		value = DS9IORead32(ds, address & 0x00FFFFFC);
 855		break;
 856	case DS9_REGION_PALETTE_RAM:
 857		LOAD_32(value, address & (DS9_SIZE_PALETTE_RAM - 4), ds->video.palette);
 858		break;
 859	case DS_REGION_VRAM: {
 860		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
 861		int i = 0;
 862		for (i = 0; i < 9; ++i) {
 863			if (mask & (1 << i)) {
 864				uint32_t newValue;
 865				LOAD_32(newValue, address & _vramMask[i], memory->vramBank[i]);
 866				value |= newValue;
 867			}
 868		}
 869		break;
 870	}
 871	case DS9_REGION_OAM:
 872		LOAD_32(value, address & (DS9_SIZE_OAM - 4), ds->video.oam.raw);
 873		break;
 874	case DS_REGION_SLOT2:
 875	case DS_REGION_SLOT2_EX:
 876	case DS_REGION_SLOT2_SRAM:
 877		value = 0xFFFFFFFF;
 878		break;
 879	case DS9_REGION_BIOS:
 880		// TODO: Fix undersized BIOS
 881		// TODO: Fix masking
 882		LOAD_32(value, address & (DS9_SIZE_BIOS - 4), memory->bios9);
 883		break;
 884	default:
 885		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 886			LOAD_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
 887			break;
 888		}
 889		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 890		break;
 891	}
 892
 893	if (cycleCounter) {
 894		wait += 2;
 895		*cycleCounter += wait;
 896	}
 897	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 898	int rotate = (address & 3) << 3;
 899	return ROR(value, rotate);
 900}
 901
 902uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 903	struct DS* ds = (struct DS*) cpu->master;
 904	struct DSMemory* memory = &ds->memory;
 905	uint32_t value = 0;
 906	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 907
 908	switch (address >> DS_BASE_OFFSET) {
 909	case DS9_REGION_ITCM:
 910	case DS9_REGION_ITCM_MIRROR:
 911		if (address < memory->itcmSize) {
 912			LOAD_16(value, address & (DS9_SIZE_ITCM - 2), memory->itcm);
 913			break;
 914		}
 915		mLOG(DS_MEM, STUB, "Bad DS9 Load16: %08X", address);
 916		break;
 917	case DS_REGION_WORKING_RAM:
 918		if (ds->memory.wramSize9) {
 919			LOAD_16(value, address & (ds->memory.wramSize9 - 2), memory->wramBase9);
 920			break;
 921		}
 922		mLOG(DS_MEM, STUB, "Bad DS9 Load16: %08X", address);
 923		break;
 924	case DS_REGION_RAM:
 925		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 926			LOAD_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
 927			break;
 928		}
 929		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 930			LOAD_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
 931			break;
 932		}
 933		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 934	case DS_REGION_IO:
 935		value = DS9IORead(ds, address & DS_OFFSET_MASK);
 936		break;
 937	case DS9_REGION_PALETTE_RAM:
 938		LOAD_16(value, address & (DS9_SIZE_PALETTE_RAM - 2), ds->video.palette);
 939		break;
 940	case DS_REGION_VRAM: {
 941		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
 942		int i = 0;
 943		for (i = 0; i < 9; ++i) {
 944			if (mask & (1 << i)) {
 945				uint32_t newValue;
 946				LOAD_16(newValue, address & _vramMask[i], memory->vramBank[i]);
 947				value |= newValue;
 948			}
 949		}
 950		break;
 951	}
 952	case DS9_REGION_OAM:
 953		LOAD_16(value, address & (DS9_SIZE_OAM - 2), ds->video.oam.raw);
 954		break;
 955	case DS_REGION_SLOT2:
 956	case DS_REGION_SLOT2_EX:
 957	case DS_REGION_SLOT2_SRAM:
 958		value = 0xFFFF;
 959		break;
 960	case DS9_REGION_BIOS:
 961		// TODO: Fix undersized BIOS
 962		// TODO: Fix masking
 963		LOAD_16(value, address & (DS9_SIZE_BIOS - 2), memory->bios9);
 964		break;
 965	default:
 966		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 967			LOAD_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
 968			break;
 969		}
 970		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 971		break;
 972	}
 973
 974	if (cycleCounter) {
 975		wait += 2;
 976		*cycleCounter += wait;
 977	}
 978	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 979	int rotate = (address & 1) << 3;
 980	return ROR(value, rotate);
 981}
 982
 983uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 984	struct DS* ds = (struct DS*) cpu->master;
 985	struct DSMemory* memory = &ds->memory;
 986	uint32_t value = 0;
 987	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 988
 989	switch (address >> DS_BASE_OFFSET) {
 990	case DS9_REGION_ITCM:
 991	case DS9_REGION_ITCM_MIRROR:
 992		if (address < memory->itcmSize) {
 993			value = ((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)];
 994			break;
 995		}
 996		mLOG(DS_MEM, STUB, "Bad DS9 Load8: %08X", address);
 997		break;
 998	case DS_REGION_WORKING_RAM:
 999		if (ds->memory.wramSize9) {
1000			value = ((uint8_t*) memory->wramBase9)[address & (memory->wramSize9 - 1)];
1001			break;
1002		}
1003		mLOG(DS_MEM, STUB, "Bad DS9 Load8: %08X", address);
1004		break;
1005	case DS_REGION_RAM:
1006		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1007			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
1008			break;
1009		}
1010		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1011			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
1012			break;
1013		}
1014		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
1015	case DS_REGION_IO:
1016		value = (DS9IORead(ds, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF;
1017		break;
1018	case DS_REGION_VRAM: {
1019		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1020		int i = 0;
1021		for (i = 0; i < 9; ++i) {
1022			if (mask & (1 << i)) {
1023				value |= ((uint8_t*) memory->vramBank[i])[address & _vramMask[i]];
1024			}
1025		}
1026		break;
1027	}
1028	case DS_REGION_SLOT2:
1029	case DS_REGION_SLOT2_EX:
1030	case DS_REGION_SLOT2_SRAM:
1031		value = 0xFF;
1032		break;
1033	case DS9_REGION_BIOS:
1034		// TODO: Fix undersized BIOS
1035		// TODO: Fix masking
1036		value = ((uint8_t*) memory->bios9)[address & (DS9_SIZE_BIOS - 1)];
1037		break;
1038	default:
1039		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1040			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
1041			break;
1042		}
1043		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
1044		break;
1045	}
1046
1047	if (cycleCounter) {
1048		wait += 2;
1049		*cycleCounter += wait;
1050	}
1051	return value;
1052}
1053
1054void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
1055	struct DS* ds = (struct DS*) cpu->master;
1056	struct DSMemory* memory = &ds->memory;
1057	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
1058
1059	switch (address >> DS_BASE_OFFSET) {
1060	case DS9_REGION_ITCM:
1061	case DS9_REGION_ITCM_MIRROR:
1062		if (address < memory->itcmSize) {
1063			STORE_32(value, address & (DS9_SIZE_ITCM - 4), memory->itcm);
1064			break;
1065		}
1066		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
1067		break;
1068	case DS_REGION_WORKING_RAM:
1069		if (ds->memory.wramSize9) {
1070			STORE_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1071			break;
1072		}
1073		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
1074		break;
1075	case DS_REGION_RAM:
1076		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1077			STORE_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
1078			break;
1079		}
1080		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1081			STORE_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
1082			break;
1083		}
1084		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
1085		break;
1086	case DS_REGION_IO:
1087		DS9IOWrite32(ds, address & DS_OFFSET_MASK, value);
1088		break;
1089	case DS9_REGION_PALETTE_RAM:
1090		STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 4), ds->video.palette);
1091		ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16);
1092		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value);
1093		break;
1094	case DS_REGION_VRAM: {
1095		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1096		int i = 0;
1097		for (i = 0; i < 9; ++i) {
1098			if (mask & (1 << i)) {
1099				STORE_32(value, address & _vramMask[i], memory->vramBank[i]);
1100			}
1101		}
1102		break;
1103	}
1104	case DS9_REGION_OAM:
1105		STORE_32(value, address & (DS9_SIZE_OAM - 4), ds->video.oam.raw);
1106		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 4)) >> 1);
1107		ds->video.renderer->writeOAM(ds->video.renderer, ((address & (DS9_SIZE_OAM - 4)) >> 1) + 1);
1108		break;
1109	default:
1110		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1111			STORE_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
1112			break;
1113		}
1114		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
1115		break;
1116	}
1117
1118	if (cycleCounter) {
1119		++wait;
1120		*cycleCounter += wait;
1121	}
1122}
1123
1124void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
1125	struct DS* ds = (struct DS*) cpu->master;
1126	struct DSMemory* memory = &ds->memory;
1127	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
1128
1129	switch (address >> DS_BASE_OFFSET) {
1130	case DS9_REGION_ITCM:
1131	case DS9_REGION_ITCM_MIRROR:
1132		if (address < memory->itcmSize) {
1133			STORE_16(value, address & (DS9_SIZE_ITCM - 2), memory->itcm);
1134			break;
1135		}
1136		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
1137		break;
1138	case DS_REGION_WORKING_RAM:
1139		if (ds->memory.wramSize9) {
1140			STORE_16(value, address & (ds->memory.wramSize9 - 2), memory->wramBase9);
1141			break;
1142		}
1143		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
1144		break;
1145	case DS_REGION_RAM:
1146		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1147			STORE_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
1148			break;
1149		}
1150		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1151			STORE_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
1152			break;
1153		}
1154		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
1155		break;
1156	case DS_REGION_IO:
1157		DS9IOWrite(ds, address & DS_OFFSET_MASK, value);
1158		break;
1159	case DS9_REGION_PALETTE_RAM:
1160		STORE_16(value, address & (DS9_SIZE_PALETTE_RAM - 2), ds->video.palette);
1161		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 2), value);
1162		break;
1163	case DS_REGION_VRAM: {
1164		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1165		int i = 0;
1166		for (i = 0; i < 9; ++i) {
1167			if (mask & (1 << i)) {
1168				STORE_16(value, address & _vramMask[i], memory->vramBank[i]);
1169			}
1170		}
1171		break;
1172	}
1173	case DS9_REGION_OAM:
1174		STORE_16(value, address & (DS9_SIZE_OAM - 2), ds->video.oam.raw);
1175		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 2)) >> 1);
1176		break;
1177	default:
1178		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1179			STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1180			break;
1181		}
1182		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
1183		break;
1184	}
1185
1186	if (cycleCounter) {
1187		++wait;
1188		*cycleCounter += wait;
1189	}
1190}
1191
1192void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
1193	struct DS* ds = (struct DS*) cpu->master;
1194	struct DSMemory* memory = &ds->memory;
1195	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
1196
1197	switch (address >> DS_BASE_OFFSET) {
1198	case DS9_REGION_ITCM:
1199	case DS9_REGION_ITCM_MIRROR:
1200		if (address < memory->itcmSize) {
1201			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
1202			break;
1203		}
1204		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
1205		break;
1206	case DS_REGION_WORKING_RAM:
1207		if (ds->memory.wramSize9) {
1208			((uint8_t*) memory->wramBase9)[address & (ds->memory.wramSize9 - 1)] = value;
1209			break;
1210		}
1211		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
1212		break;
1213	case DS_REGION_RAM:
1214		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1215			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
1216			break;
1217		}
1218		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1219			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
1220			break;
1221		}
1222		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
1223	case DS_REGION_IO:
1224		DS9IOWrite8(ds, address & DS_OFFSET_MASK, value);
1225		break;
1226	default:
1227		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1228			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
1229			break;
1230		}
1231		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
1232		break;
1233	}
1234
1235	if (cycleCounter) {
1236		++wait;
1237		*cycleCounter += wait;
1238	}
1239}
1240
1241uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
1242	struct DS* ds = (struct DS*) cpu->master;
1243	struct DSMemory* memory = &ds->memory;
1244	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
1245	uint32_t value;
1246	int wait = 0;
1247
1248	int i;
1249	int offset = 4;
1250	int popcount = 0;
1251	if (direction & LSM_D) {
1252		offset = -4;
1253		popcount = popcount32(mask);
1254		address -= (popcount << 2) - 4;
1255	}
1256
1257	if (direction & LSM_B) {
1258		address += offset;
1259	}
1260
1261	uint32_t addressMisalign = address & 0x3;
1262	address &= 0xFFFFFFFC;
1263
1264	switch (address >> DS_BASE_OFFSET) {
1265	case DS9_REGION_BIOS:
1266		// TODO: Fix undersized BIOS
1267		// TODO: Fix masking
1268		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_BIOS - 1), memory->bios9));
1269		break;
1270	case DS9_REGION_ITCM:
1271	case DS9_REGION_ITCM_MIRROR:
1272		LDM_LOOP(if (address < memory->itcmSize) {
1273			LOAD_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
1274		} else {
1275			value = 0;
1276			mLOG(DS_MEM, STUB, "Bad DS9 LDM: %08X", address);
1277		});
1278		break;
1279	case DS_REGION_WORKING_RAM:
1280		LDM_LOOP(if (ds->memory.wramSize9) {
1281			LOAD_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1282		} else {
1283			value = 0;
1284			mLOG(DS_MEM, STUB, "Bad DS9 LDM: %08X", address);
1285		});
1286		break;
1287	case DS_REGION_RAM:
1288		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1289			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1290		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1291			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
1292		} else {
1293			value = 0;
1294			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
1295		});
1296		break;
1297	case DS_REGION_IO:
1298		LDM_LOOP(value = DS9IORead32(ds, address));
1299		break;
1300	case DS9_REGION_PALETTE_RAM:
1301		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette));
1302		break;
1303	case DS_REGION_VRAM:
1304		LDM_LOOP(unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1305		value = 0;
1306		int j = 0;
1307		for (j = 0; j < 9; ++j) {
1308			if (mask & (1 << j)) {
1309				uint32_t newValue;
1310				LOAD_32(newValue, address & _vramMask[j], memory->vramBank[j]);
1311				value |= newValue;
1312			}
1313		});
1314		break;
1315	case DS9_REGION_OAM:
1316		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_OAM - 1), ds->video.oam.raw));
1317		break;
1318	default:
1319		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1320			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1321		} else {
1322			value = 0;
1323			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
1324		});
1325		break;
1326	}
1327
1328	if (cycleCounter) {
1329		++wait;
1330		*cycleCounter += wait;
1331	}
1332
1333	if (direction & LSM_B) {
1334		address -= offset;
1335	}
1336
1337	if (direction & LSM_D) {
1338		address -= (popcount << 2) + 4;
1339	}
1340
1341	return address | addressMisalign;
1342}
1343
1344
1345uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
1346	struct DS* ds = (struct DS*) cpu->master;
1347	struct DSMemory* memory = &ds->memory;
1348	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
1349	uint32_t value;
1350	int wait = 0;
1351
1352	int i;
1353	int offset = 4;
1354	int popcount = 0;
1355	if (direction & LSM_D) {
1356		offset = -4;
1357		popcount = popcount32(mask);
1358		address -= (popcount << 2) - 4;
1359	}
1360
1361	if (direction & LSM_B) {
1362		address += offset;
1363	}
1364
1365	uint32_t addressMisalign = address & 0x3;
1366	address &= 0xFFFFFFFC;
1367
1368	switch (address >> DS_BASE_OFFSET) {
1369	case DS9_REGION_ITCM:
1370	case DS9_REGION_ITCM_MIRROR:
1371		STM_LOOP(if (address < memory->itcmSize) {
1372			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
1373		} else {
1374			mLOG(DS_MEM, STUB, "Bad DS9 STM: %08X:%08X", address, value);
1375		});
1376		break;
1377	case DS_REGION_WORKING_RAM:
1378		STM_LOOP(if (ds->memory.wramSize9) {
1379			STORE_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1380		} else {
1381			mLOG(DS_MEM, STUB, "Bad DS9 STM: %08X", address);
1382		});
1383		break;
1384	case DS_REGION_RAM:
1385		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1386			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1387		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1388			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
1389		} else {
1390			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1391		});
1392		break;
1393	case DS_REGION_IO:
1394		STM_LOOP(DS9IOWrite32(ds, address & DS_OFFSET_MASK, value));
1395		break;
1396	case DS9_REGION_PALETTE_RAM:
1397		STM_LOOP(STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette);
1398		ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16);
1399		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value));
1400		break;
1401	case DS_REGION_VRAM:
1402		STM_LOOP(unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1403		int j = 0;
1404		for (j = 0; j < 9; ++j) {
1405			if (mask & (1 << j)) {
1406				STORE_32(value, address & _vramMask[j], memory->vramBank[j]);
1407			}
1408		});
1409		break;
1410	case DS9_REGION_OAM:
1411		STM_LOOP(STORE_32(value, address & (DS9_SIZE_OAM - 1), ds->video.oam.raw);
1412		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 4)) >> 1);
1413		ds->video.renderer->writeOAM(ds->video.renderer, ((address & (DS9_SIZE_OAM - 4)) >> 1) + 1));
1414		break;
1415	default:
1416		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1417			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1418		} else {
1419			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1420		});
1421		break;
1422	}
1423
1424	if (cycleCounter) {
1425		*cycleCounter += wait;
1426	}
1427
1428	if (direction & LSM_B) {
1429		address -= offset;
1430	}
1431
1432	if (direction & LSM_D) {
1433		address -= (popcount << 2) + 4;
1434	}
1435
1436	return address | addressMisalign;
1437}
1438
1439int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
1440	return wait;
1441}
1442
1443void DSConfigureWRAM(struct DSMemory* memory, uint8_t config) {
1444	switch (config & 3) {
1445	case 0:
1446		memory->wramSize7 = 0;
1447		memory->wramBase7 = NULL;
1448		memory->wramSize9 = DS_SIZE_WORKING_RAM;
1449		memory->wramBase9 = memory->wramBase;
1450		break;
1451	case 1:
1452		memory->wramSize7 = DS_SIZE_WORKING_RAM >> 1;
1453		memory->wramBase7 = memory->wram;
1454		memory->wramSize9 = DS_SIZE_WORKING_RAM >> 1;
1455		memory->wramBase9 = &memory->wramBase[DS_SIZE_WORKING_RAM >> 3];
1456		break;
1457	case 2:
1458		memory->wramSize7 = DS_SIZE_WORKING_RAM >> 1;
1459		memory->wramBase7 = &memory->wram[DS_SIZE_WORKING_RAM >> 3];
1460		memory->wramSize9 = DS_SIZE_WORKING_RAM >> 1;
1461		memory->wramBase9 = memory->wramBase;
1462		break;
1463	case 3:
1464		memory->wramSize7 = DS_SIZE_WORKING_RAM;
1465		memory->wramBase7 = memory->wramBase;
1466		memory->wramSize9 = 0;
1467		memory->wramBase9 = NULL;
1468		break;
1469	}
1470}
1471
1472void DSConfigureExternalMemory(struct DS* ds, uint16_t config) {
1473	// TODO: GBA params
1474	ds->memory.slot1Owner = config & 0x0800;
1475	ds->memory.slot2Owner = config & 0x0080;
1476	ds->memory.io7[DS7_REG_EXMEMSTAT >> 1] = config;
1477
1478	ds->ds7.memory.slot1Access = ds->memory.slot1Owner;
1479	ds->ds9.memory.slot1Access = !ds->memory.slot1Owner;
1480}
1481
1482static unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset) {
1483	unsigned mask = 0;
1484	offset &= 0x3FF;
1485	mask |= memory->vramMirror[0][offset & 0x3F] & memory->vramMode[0][offset >> 7];
1486	mask |= memory->vramMirror[1][offset & 0x3F] & memory->vramMode[1][offset >> 7];
1487	mask |= memory->vramMirror[2][offset & 0x3F] & memory->vramMode[2][offset >> 7];
1488	mask |= memory->vramMirror[3][offset & 0x3F] & memory->vramMode[3][offset >> 7];
1489	mask |= memory->vramMirror[4][offset & 0x3F] & memory->vramMode[4][offset >> 7];
1490	mask |= memory->vramMirror[5][offset & 0x3F] & memory->vramMode[5][offset >> 7];
1491	mask |= memory->vramMirror[6][offset & 0x3F] & memory->vramMode[6][offset >> 7];
1492	mask |= memory->vramMirror[7][offset & 0x3F] & memory->vramMode[7][offset >> 7];
1493	mask |= memory->vramMirror[8][offset & 0x3F] & memory->vramMode[8][offset >> 7];
1494	return mask;
1495}