all repos — mgba @ ae5547ea51aa7dbb6dc89e21f64a11ed91b9989b

mGBA Game Boy Advance Emulator

src/ds/memory.c (view raw)

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