all repos — mgba @ 3341cc386567c7713aaa3ccab67677635293ba29

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