all repos — mgba @ fc74ad9ceb102a046db417dc7382b8954a7cc85e

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