all repos — mgba @ 75e6fcaecb1f8a2944e2fbf655b315e13b5a5b66

mGBA Game Boy Advance Emulator

src/ds/memory.c (view raw)

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