all repos — mgba @ a662777ff1f40114c61b4ce044fe695d2d0d9e88

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			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 660		});
 661		break;
 662	default:
 663		mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 664		LDM_LOOP(value = 0);
 665	}
 666
 667	if (cycleCounter) {
 668		++wait;
 669		*cycleCounter += wait;
 670	}
 671
 672	if (direction & LSM_B) {
 673		address -= offset;
 674	}
 675
 676	if (direction & LSM_D) {
 677		address -= (popcount << 2) + 4;
 678	}
 679
 680	return address | addressMisalign;
 681}
 682
 683uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 684	struct DS* ds = (struct DS*) cpu->master;
 685	struct DSMemory* memory = &ds->memory;
 686	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
 687	uint32_t value;
 688	int wait = 0;
 689
 690	int i;
 691	int offset = 4;
 692	int popcount = 0;
 693	if (direction & LSM_D) {
 694		offset = -4;
 695		popcount = popcount32(mask);
 696		address -= (popcount << 2) - 4;
 697	}
 698
 699	if (direction & LSM_B) {
 700		address += offset;
 701	}
 702
 703	uint32_t addressMisalign = address & 0x3;
 704	address &= 0xFFFFFFFC;
 705
 706	switch (address >> DS_BASE_OFFSET) {
 707	case DS_REGION_WORKING_RAM:
 708		STM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 709			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 710		} else {
 711			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wramBase7);
 712		});
 713		break;
 714	case DS_REGION_RAM:
 715		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 716			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 717		} else {
 718			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 719		});
 720		break;
 721	case DS_REGION_IO:
 722		STM_LOOP(DS7IOWrite32(ds, address & DS_OFFSET_MASK, value));
 723		break;
 724	case DS_REGION_VRAM:
 725		STM_LOOP(if (address < 0x06040000 && memory->vram7[(address & 0x3FFFF) >> 17]) {
 726			STORE_32(value, address & 0x1FFFF, memory->vram7[(address & 0x3FFFF) >> 17]);
 727		} else {
 728			mLOG(DS_MEM, STUB, "Unimplemented DS7 STM: %08X", address);
 729		});
 730		break;
 731	default:
 732		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 733		STM_LOOP();
 734		break;
 735	}
 736
 737	if (cycleCounter) {
 738		*cycleCounter += wait;
 739	}
 740
 741	if (direction & LSM_B) {
 742		address -= offset;
 743	}
 744
 745	if (direction & LSM_D) {
 746		address -= (popcount << 2) + 4;
 747	}
 748
 749	return address | addressMisalign;
 750}
 751
 752static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
 753	struct DS* ds = (struct DS*) cpu->master;
 754	struct DSCoreMemory* memory = &ds->ds9.memory;
 755
 756	int newRegion = address >> DS_BASE_OFFSET;
 757
 758	memory->activeRegion = newRegion;
 759	switch (newRegion) {
 760	case DS9_REGION_ITCM:
 761	case DS9_REGION_ITCM_MIRROR:
 762		if (address < ds->memory.itcmSize) {
 763			cpu->memory.activeRegion = ds->memory.itcm;
 764			cpu->memory.activeMask = DS9_SIZE_ITCM - 1;
 765			break;
 766		}
 767		goto jump_error;
 768	case DS_REGION_RAM:
 769		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 770			cpu->memory.activeRegion = ds->memory.ram;
 771			cpu->memory.activeMask = DS_SIZE_RAM - 1;
 772			break;
 773		}
 774		goto jump_error;
 775	case DS9_REGION_BIOS:
 776		// TODO: Mask properly
 777		if (ds->memory.bios9) {
 778			cpu->memory.activeRegion = ds->memory.bios9;
 779			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
 780		} else {
 781			cpu->memory.activeRegion = _deadbeef;
 782			cpu->memory.activeMask = 0;
 783		}
 784		cpu->memory.activeSeqCycles32 = 0;
 785		cpu->memory.activeSeqCycles16 = 0;
 786		cpu->memory.activeNonseqCycles32 = 0;
 787		cpu->memory.activeNonseqCycles16 = 0;
 788		return;
 789	default:
 790	jump_error:
 791		memory->activeRegion = -1;
 792		cpu->memory.activeRegion = _deadbeef;
 793		cpu->memory.activeMask = 0;
 794		mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
 795		return;
 796	}
 797	cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion];
 798	cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion];
 799	cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion];
 800	cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion];
 801}
 802
 803uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 804	struct DS* ds = (struct DS*) cpu->master;
 805	struct DSMemory* memory = &ds->memory;
 806	uint32_t value = 0;
 807	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 808
 809	switch (address >> DS_BASE_OFFSET) {
 810	case DS9_REGION_ITCM:
 811	case DS9_REGION_ITCM_MIRROR:
 812		if (address < memory->itcmSize) {
 813			LOAD_32(value, address & (DS9_SIZE_ITCM - 4), memory->itcm);
 814			break;
 815		}
 816		mLOG(DS_MEM, STUB, "Bad DS9 Load32: %08X", address);
 817		break;
 818	case DS_REGION_WORKING_RAM:
 819		if (ds->memory.wramSize9) {
 820			LOAD_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
 821			break;
 822		}
 823		mLOG(DS_MEM, STUB, "Bad DS9 Load32: %08X", address);
 824		break;
 825	case DS_REGION_RAM:
 826		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 827			LOAD_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
 828			break;
 829		}
 830		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 831			LOAD_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
 832			break;
 833		}
 834		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 835		break;
 836	case DS_REGION_IO:
 837		value = DS9IORead32(ds, address & 0x00FFFFFC);
 838		break;
 839	case DS9_REGION_PALETTE_RAM:
 840		LOAD_32(value, address & (DS9_SIZE_PALETTE_RAM - 4), ds->video.palette);
 841		break;
 842	case DS_REGION_VRAM: {
 843		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
 844		int i = 0;
 845		for (i = 0; i < 9; ++i) {
 846			if (mask & (1 << i)) {
 847				uint32_t newValue;
 848				LOAD_32(newValue, address & _vramMask[i], memory->vramBank[i]);
 849				value |= newValue;
 850			}
 851		}
 852		break;
 853	}
 854	case DS9_REGION_OAM:
 855		LOAD_32(value, address & (DS9_SIZE_OAM - 4), ds->video.oam.raw);
 856		break;
 857	case DS_REGION_SLOT2:
 858	case DS_REGION_SLOT2_EX:
 859	case DS_REGION_SLOT2_SRAM:
 860		value = 0xFFFFFFFF;
 861		break;
 862	case DS9_REGION_BIOS:
 863		// TODO: Fix undersized BIOS
 864		// TODO: Fix masking
 865		LOAD_32(value, address & (DS9_SIZE_BIOS - 4), memory->bios9);
 866		break;
 867	default:
 868		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 869			LOAD_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
 870			break;
 871		}
 872		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 873		break;
 874	}
 875
 876	if (cycleCounter) {
 877		wait += 2;
 878		*cycleCounter += wait;
 879	}
 880	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 881	int rotate = (address & 3) << 3;
 882	return ROR(value, rotate);
 883}
 884
 885uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 886	struct DS* ds = (struct DS*) cpu->master;
 887	struct DSMemory* memory = &ds->memory;
 888	uint32_t value = 0;
 889	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 890
 891	switch (address >> DS_BASE_OFFSET) {
 892	case DS9_REGION_ITCM:
 893	case DS9_REGION_ITCM_MIRROR:
 894		if (address < memory->itcmSize) {
 895			LOAD_16(value, address & (DS9_SIZE_ITCM - 2), memory->itcm);
 896			break;
 897		}
 898		mLOG(DS_MEM, STUB, "Bad DS9 Load16: %08X", address);
 899		break;
 900	case DS_REGION_WORKING_RAM:
 901		if (ds->memory.wramSize9) {
 902			LOAD_16(value, address & (ds->memory.wramSize9 - 2), memory->wramBase9);
 903			break;
 904		}
 905		mLOG(DS_MEM, STUB, "Bad DS9 Load16: %08X", address);
 906		break;
 907	case DS_REGION_RAM:
 908		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 909			LOAD_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
 910			break;
 911		}
 912		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 913			LOAD_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
 914			break;
 915		}
 916		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 917	case DS_REGION_IO:
 918		value = DS9IORead(ds, address & DS_OFFSET_MASK);
 919		break;
 920	case DS9_REGION_PALETTE_RAM:
 921		LOAD_16(value, address & (DS9_SIZE_PALETTE_RAM - 2), ds->video.palette);
 922		break;
 923	case DS_REGION_VRAM: {
 924		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
 925		int i = 0;
 926		for (i = 0; i < 9; ++i) {
 927			if (mask & (1 << i)) {
 928				uint32_t newValue;
 929				LOAD_16(newValue, address & _vramMask[i], memory->vramBank[i]);
 930				value |= newValue;
 931			}
 932		}
 933		break;
 934	}
 935	case DS9_REGION_OAM:
 936		LOAD_16(value, address & (DS9_SIZE_OAM - 2), ds->video.oam.raw);
 937		break;
 938	case DS_REGION_SLOT2:
 939	case DS_REGION_SLOT2_EX:
 940	case DS_REGION_SLOT2_SRAM:
 941		value = 0xFFFF;
 942		break;
 943	case DS9_REGION_BIOS:
 944		// TODO: Fix undersized BIOS
 945		// TODO: Fix masking
 946		LOAD_16(value, address & (DS9_SIZE_BIOS - 2), memory->bios9);
 947		break;
 948	default:
 949		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 950			LOAD_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
 951			break;
 952		}
 953		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 954		break;
 955	}
 956
 957	if (cycleCounter) {
 958		wait += 2;
 959		*cycleCounter += wait;
 960	}
 961	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 962	int rotate = (address & 1) << 3;
 963	return ROR(value, rotate);
 964}
 965
 966uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 967	struct DS* ds = (struct DS*) cpu->master;
 968	struct DSMemory* memory = &ds->memory;
 969	uint32_t value = 0;
 970	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 971
 972	switch (address >> DS_BASE_OFFSET) {
 973	case DS9_REGION_ITCM:
 974	case DS9_REGION_ITCM_MIRROR:
 975		if (address < memory->itcmSize) {
 976			value = ((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)];
 977			break;
 978		}
 979		mLOG(DS_MEM, STUB, "Bad DS9 Load8: %08X", address);
 980		break;
 981	case DS_REGION_WORKING_RAM:
 982		if (ds->memory.wramSize9) {
 983			value = ((uint8_t*) memory->wramBase9)[address & (memory->wramSize9 - 1)];
 984			break;
 985		}
 986		mLOG(DS_MEM, STUB, "Bad DS9 Load8: %08X", address);
 987		break;
 988	case DS_REGION_RAM:
 989		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
 990			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
 991			break;
 992		}
 993		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 994			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
 995			break;
 996		}
 997		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
 998	case DS_REGION_IO:
 999		value = (DS9IORead(ds, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF;
1000		break;
1001	case DS_REGION_VRAM: {
1002		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1003		int i = 0;
1004		for (i = 0; i < 9; ++i) {
1005			if (mask & (1 << i)) {
1006				value |= ((uint8_t*) memory->vramBank[i])[address & _vramMask[i]];
1007			}
1008		}
1009		break;
1010	}
1011	case DS_REGION_SLOT2:
1012	case DS_REGION_SLOT2_EX:
1013	case DS_REGION_SLOT2_SRAM:
1014		value = 0xFF;
1015		break;
1016	case DS9_REGION_BIOS:
1017		// TODO: Fix undersized BIOS
1018		// TODO: Fix masking
1019		value = ((uint8_t*) memory->bios9)[address & (DS9_SIZE_BIOS - 1)];
1020		break;
1021	default:
1022		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1023			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
1024			break;
1025		}
1026		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
1027		break;
1028	}
1029
1030	if (cycleCounter) {
1031		wait += 2;
1032		*cycleCounter += wait;
1033	}
1034	return value;
1035}
1036
1037void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
1038	struct DS* ds = (struct DS*) cpu->master;
1039	struct DSMemory* memory = &ds->memory;
1040	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
1041
1042	switch (address >> DS_BASE_OFFSET) {
1043	case DS9_REGION_ITCM:
1044	case DS9_REGION_ITCM_MIRROR:
1045		if (address < memory->itcmSize) {
1046			STORE_32(value, address & (DS9_SIZE_ITCM - 4), memory->itcm);
1047			break;
1048		}
1049		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
1050		break;
1051	case DS_REGION_WORKING_RAM:
1052		if (ds->memory.wramSize9) {
1053			STORE_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1054			break;
1055		}
1056		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
1057		break;
1058	case DS_REGION_RAM:
1059		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1060			STORE_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
1061			break;
1062		}
1063		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1064			STORE_32(value, address & (DS_SIZE_RAM - 4), memory->ram);
1065			break;
1066		}
1067		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
1068		break;
1069	case DS_REGION_IO:
1070		DS9IOWrite32(ds, address & DS_OFFSET_MASK, value);
1071		break;
1072	case DS9_REGION_PALETTE_RAM:
1073		STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 4), ds->video.palette);
1074		ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16);
1075		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value);
1076		break;
1077	case DS_REGION_VRAM: {
1078		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1079		int i = 0;
1080		for (i = 0; i < 9; ++i) {
1081			if (mask & (1 << i)) {
1082				STORE_32(value, address & _vramMask[i], memory->vramBank[i]);
1083			}
1084		}
1085		break;
1086	}
1087	case DS9_REGION_OAM:
1088		STORE_32(value, address & (DS9_SIZE_OAM - 4), ds->video.oam.raw);
1089		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 4)) >> 1);
1090		ds->video.renderer->writeOAM(ds->video.renderer, ((address & (DS9_SIZE_OAM - 4)) >> 1) + 1);
1091		break;
1092	default:
1093		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1094			STORE_32(value, address & (DS9_SIZE_DTCM - 4), memory->dtcm);
1095			break;
1096		}
1097		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
1098		break;
1099	}
1100
1101	if (cycleCounter) {
1102		++wait;
1103		*cycleCounter += wait;
1104	}
1105}
1106
1107void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
1108	struct DS* ds = (struct DS*) cpu->master;
1109	struct DSMemory* memory = &ds->memory;
1110	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
1111
1112	switch (address >> DS_BASE_OFFSET) {
1113	case DS9_REGION_ITCM:
1114	case DS9_REGION_ITCM_MIRROR:
1115		if (address < memory->itcmSize) {
1116			STORE_16(value, address & (DS9_SIZE_ITCM - 2), memory->itcm);
1117			break;
1118		}
1119		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
1120		break;
1121	case DS_REGION_WORKING_RAM:
1122		if (ds->memory.wramSize9) {
1123			STORE_16(value, address & (ds->memory.wramSize9 - 2), memory->wramBase9);
1124			break;
1125		}
1126		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
1127		break;
1128	case DS_REGION_RAM:
1129		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1130			STORE_16(value, address & (DS9_SIZE_DTCM - 2), memory->dtcm);
1131			break;
1132		}
1133		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1134			STORE_16(value, address & (DS_SIZE_RAM - 2), memory->ram);
1135			break;
1136		}
1137		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
1138		break;
1139	case DS_REGION_IO:
1140		DS9IOWrite(ds, address & DS_OFFSET_MASK, value);
1141		break;
1142	case DS9_REGION_PALETTE_RAM:
1143		STORE_16(value, address & (DS9_SIZE_PALETTE_RAM - 2), ds->video.palette);
1144		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 2), value);
1145		break;
1146	case DS_REGION_VRAM: {
1147		unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1148		int i = 0;
1149		for (i = 0; i < 9; ++i) {
1150			if (mask & (1 << i)) {
1151				STORE_16(value, address & _vramMask[i], memory->vramBank[i]);
1152			}
1153		}
1154		break;
1155	}
1156	case DS9_REGION_OAM:
1157		STORE_16(value, address & (DS9_SIZE_OAM - 2), ds->video.oam.raw);
1158		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 2)) >> 1);
1159		break;
1160	default:
1161		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1162			STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1163			break;
1164		}
1165		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
1166		break;
1167	}
1168
1169	if (cycleCounter) {
1170		++wait;
1171		*cycleCounter += wait;
1172	}
1173}
1174
1175void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
1176	struct DS* ds = (struct DS*) cpu->master;
1177	struct DSMemory* memory = &ds->memory;
1178	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
1179
1180	switch (address >> DS_BASE_OFFSET) {
1181	case DS9_REGION_ITCM:
1182	case DS9_REGION_ITCM_MIRROR:
1183		if (address < memory->itcmSize) {
1184			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
1185			break;
1186		}
1187		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
1188		break;
1189	case DS_REGION_WORKING_RAM:
1190		if (ds->memory.wramSize9) {
1191			((uint8_t*) memory->wramBase9)[address & (ds->memory.wramSize9 - 1)] = value;
1192			break;
1193		}
1194		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
1195		break;
1196	case DS_REGION_RAM:
1197		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1198			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
1199			break;
1200		}
1201		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1202			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
1203			break;
1204		}
1205		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
1206	case DS_REGION_IO:
1207		DS9IOWrite8(ds, address & DS_OFFSET_MASK, value);
1208		break;
1209	default:
1210		if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1211			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
1212			break;
1213		}
1214		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
1215		break;
1216	}
1217
1218	if (cycleCounter) {
1219		++wait;
1220		*cycleCounter += wait;
1221	}
1222}
1223
1224uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
1225	struct DS* ds = (struct DS*) cpu->master;
1226	struct DSMemory* memory = &ds->memory;
1227	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
1228	uint32_t value;
1229	int wait = 0;
1230
1231	int i;
1232	int offset = 4;
1233	int popcount = 0;
1234	if (direction & LSM_D) {
1235		offset = -4;
1236		popcount = popcount32(mask);
1237		address -= (popcount << 2) - 4;
1238	}
1239
1240	if (direction & LSM_B) {
1241		address += offset;
1242	}
1243
1244	uint32_t addressMisalign = address & 0x3;
1245	address &= 0xFFFFFFFC;
1246
1247	switch (address >> DS_BASE_OFFSET) {
1248	case DS9_REGION_BIOS:
1249		// TODO: Fix undersized BIOS
1250		// TODO: Fix masking
1251		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_BIOS - 1), memory->bios9));
1252		break;
1253	case DS9_REGION_ITCM:
1254	case DS9_REGION_ITCM_MIRROR:
1255		LDM_LOOP(if (address < memory->itcmSize) {
1256			LOAD_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
1257		} else {
1258			mLOG(DS_MEM, STUB, "Bad DS9 LDM: %08X:%08X", address, value);
1259		});
1260		break;
1261	case DS_REGION_WORKING_RAM:
1262		LDM_LOOP(if (ds->memory.wramSize9) {
1263			LOAD_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1264		} else {
1265			mLOG(DS_MEM, STUB, "Bad DS9 LDM: %08X", address);
1266		});
1267		break;
1268	case DS_REGION_RAM:
1269		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1270			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1271		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1272			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
1273		} else {
1274			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
1275		});
1276		break;
1277	case DS_REGION_IO:
1278		LDM_LOOP(value = DS9IORead32(ds, address));
1279		break;
1280	case DS9_REGION_PALETTE_RAM:
1281		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette));
1282		break;
1283	case DS_REGION_VRAM:
1284		LDM_LOOP(unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1285		value = 0;
1286		int j = 0;
1287		for (j = 0; j < 9; ++j) {
1288			if (mask & (1 << j)) {
1289				uint32_t newValue;
1290				LOAD_32(newValue, address & _vramMask[j], memory->vramBank[j]);
1291				value |= newValue;
1292			}
1293		});
1294		break;
1295	case DS9_REGION_OAM:
1296		LDM_LOOP(LOAD_32(value, address & (DS9_SIZE_OAM - 1), ds->video.oam.raw));
1297		break;
1298	default:
1299		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1300			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1301		} else {
1302			value = 0;
1303			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
1304		});
1305		break;
1306	}
1307
1308	if (cycleCounter) {
1309		++wait;
1310		*cycleCounter += wait;
1311	}
1312
1313	if (direction & LSM_B) {
1314		address -= offset;
1315	}
1316
1317	if (direction & LSM_D) {
1318		address -= (popcount << 2) + 4;
1319	}
1320
1321	return address | addressMisalign;
1322}
1323
1324
1325uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
1326	struct DS* ds = (struct DS*) cpu->master;
1327	struct DSMemory* memory = &ds->memory;
1328	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
1329	uint32_t value;
1330	int wait = 0;
1331
1332	int i;
1333	int offset = 4;
1334	int popcount = 0;
1335	if (direction & LSM_D) {
1336		offset = -4;
1337		popcount = popcount32(mask);
1338		address -= (popcount << 2) - 4;
1339	}
1340
1341	if (direction & LSM_B) {
1342		address += offset;
1343	}
1344
1345	uint32_t addressMisalign = address & 0x3;
1346	address &= 0xFFFFFFFC;
1347
1348	switch (address >> DS_BASE_OFFSET) {
1349	case DS9_REGION_ITCM:
1350	case DS9_REGION_ITCM_MIRROR:
1351		STM_LOOP(if (address < memory->itcmSize) {
1352			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
1353		} else {
1354			mLOG(DS_MEM, STUB, "Bad DS9 STM: %08X:%08X", address, value);
1355		});
1356		break;
1357	case DS_REGION_WORKING_RAM:
1358		STM_LOOP(if (ds->memory.wramSize9) {
1359			STORE_32(value, address & (ds->memory.wramSize9 - 4), memory->wramBase9);
1360		} else {
1361			mLOG(DS_MEM, STUB, "Bad DS9 STM: %08X", address);
1362		});
1363		break;
1364	case DS_REGION_RAM:
1365		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1366			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1367		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1368			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
1369		} else {
1370			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1371		});
1372		break;
1373	case DS_REGION_IO:
1374		STM_LOOP(DS9IOWrite32(ds, address & DS_OFFSET_MASK, value));
1375		break;
1376	case DS9_REGION_PALETTE_RAM:
1377		STM_LOOP(STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette);
1378		ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16);
1379		ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value));
1380		break;
1381	case DS_REGION_VRAM:
1382		STM_LOOP(unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
1383		int j = 0;
1384		for (j = 0; j < 9; ++j) {
1385			if (mask & (1 << j)) {
1386				STORE_32(value, address & _vramMask[j], memory->vramBank[j]);
1387			}
1388		});
1389		break;
1390	case DS9_REGION_OAM:
1391		STM_LOOP(STORE_32(value, address & (DS9_SIZE_OAM - 1), ds->video.oam.raw);
1392		ds->video.renderer->writeOAM(ds->video.renderer, (address & (DS9_SIZE_OAM - 4)) >> 1);
1393		ds->video.renderer->writeOAM(ds->video.renderer, ((address & (DS9_SIZE_OAM - 4)) >> 1) + 1));
1394		break;
1395	default:
1396		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) {
1397			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1398		} else {
1399			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1400		});
1401		break;
1402	}
1403
1404	if (cycleCounter) {
1405		*cycleCounter += wait;
1406	}
1407
1408	if (direction & LSM_B) {
1409		address -= offset;
1410	}
1411
1412	if (direction & LSM_D) {
1413		address -= (popcount << 2) + 4;
1414	}
1415
1416	return address | addressMisalign;
1417}
1418
1419int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
1420	return wait;
1421}
1422
1423void DSConfigureWRAM(struct DSMemory* memory, uint8_t config) {
1424	switch (config & 3) {
1425	case 0:
1426		memory->wramSize7 = 0;
1427		memory->wramBase7 = NULL;
1428		memory->wramSize9 = DS_SIZE_WORKING_RAM;
1429		memory->wramBase9 = memory->wramBase;
1430		break;
1431	case 1:
1432		memory->wramSize7 = DS_SIZE_WORKING_RAM >> 1;
1433		memory->wramBase7 = memory->wram;
1434		memory->wramSize9 = DS_SIZE_WORKING_RAM >> 1;
1435		memory->wramBase9 = &memory->wramBase[DS_SIZE_WORKING_RAM >> 3];
1436		break;
1437	case 2:
1438		memory->wramSize7 = DS_SIZE_WORKING_RAM >> 1;
1439		memory->wramBase7 = &memory->wram[DS_SIZE_WORKING_RAM >> 3];
1440		memory->wramSize9 = DS_SIZE_WORKING_RAM >> 1;
1441		memory->wramBase9 = memory->wramBase;
1442		break;
1443	case 3:
1444		memory->wramSize7 = DS_SIZE_WORKING_RAM;
1445		memory->wramBase7 = memory->wramBase;
1446		memory->wramSize9 = 0;
1447		memory->wramBase9 = NULL;
1448		break;
1449	}
1450}
1451
1452void DSConfigureExternalMemory(struct DS* ds, uint16_t config) {
1453	// TODO: GBA params
1454	ds->memory.slot1Owner = config & 0x0800;
1455	ds->memory.slot2Owner = config & 0x0080;
1456	ds->memory.io7[DS7_REG_EXMEMSTAT >> 1] = config;
1457
1458	ds->ds7.memory.slot1Access = ds->memory.slot1Owner;
1459	ds->ds9.memory.slot1Access = !ds->memory.slot1Owner;
1460}
1461
1462static unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset) {
1463	unsigned mask = 0;
1464	offset &= 0x3FF;
1465	mask |= memory->vramMirror[0][offset & 0x3F] & memory->vramMode[0][offset >> 7];
1466	mask |= memory->vramMirror[1][offset & 0x3F] & memory->vramMode[1][offset >> 7];
1467	mask |= memory->vramMirror[2][offset & 0x3F] & memory->vramMode[2][offset >> 7];
1468	mask |= memory->vramMirror[3][offset & 0x3F] & memory->vramMode[3][offset >> 7];
1469	mask |= memory->vramMirror[4][offset & 0x3F] & memory->vramMode[4][offset >> 7];
1470	mask |= memory->vramMirror[5][offset & 0x3F] & memory->vramMode[5][offset >> 7];
1471	mask |= memory->vramMirror[6][offset & 0x3F] & memory->vramMode[6][offset >> 7];
1472	mask |= memory->vramMirror[7][offset & 0x3F] & memory->vramMode[7][offset >> 7];
1473	mask |= memory->vramMirror[8][offset & 0x3F] & memory->vramMode[8][offset >> 7];
1474	return mask;
1475}