all repos — mgba @ 46f01969d89c570cf47f9945ff8553022094379b

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