all repos — mgba @ f7ed6ec99f2054dc9dbd3650da33f56d7998c45c

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 void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t region);
  20static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t region);
  21static int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait);
  22
  23static const char DS7_BASE_WAITSTATES[16] =        { 0, 0, 8, 0, 0, 0, 0, 0 };
  24static const char DS7_BASE_WAITSTATES_32[16] =     { 0, 0, 9, 0, 0, 1, 1, 0 };
  25static const char DS7_BASE_WAITSTATES_SEQ[16] =    { 0, 0, 1, 0, 0, 0, 0, 0 };
  26static const char DS7_BASE_WAITSTATES_SEQ_32[16] = { 0, 0, 2, 0, 0, 1, 1, 0 };
  27
  28static const char DS9_BASE_WAITSTATES[16] =        { 6, 6, 17, 6, 6, 7, 7, 6 };
  29static const char DS9_BASE_WAITSTATES_32[16] =     { 6, 6, 19, 6, 6, 9, 9, 6 };
  30static const char DS9_BASE_WAITSTATES_SEQ[16] =    { 1, 1,  1, 1, 1, 2, 2, 1 };
  31static const char DS9_BASE_WAITSTATES_SEQ_32[16] = { 1, 1,  3, 1, 1, 4, 4, 1 };
  32
  33void DSMemoryInit(struct DS* ds) {
  34	struct ARMCore* arm7 = ds->ds7.cpu;
  35	arm7->memory.load32 = DS7Load32;
  36	arm7->memory.load16 = DS7Load16;
  37	arm7->memory.load8 = DS7Load8;
  38	arm7->memory.loadMultiple = DS7LoadMultiple;
  39	arm7->memory.store32 = DS7Store32;
  40	arm7->memory.store16 = DS7Store16;
  41	arm7->memory.store8 = DS7Store8;
  42	arm7->memory.storeMultiple = DS7StoreMultiple;
  43	arm7->memory.stall = DSMemoryStall;
  44
  45	struct ARMCore* arm9 = ds->ds9.cpu;
  46	arm9->memory.load32 = DS9Load32;
  47	arm9->memory.load16 = DS9Load16;
  48	arm9->memory.load8 = DS9Load8;
  49	arm9->memory.loadMultiple = DS9LoadMultiple;
  50	arm9->memory.store32 = DS9Store32;
  51	arm9->memory.store16 = DS9Store16;
  52	arm9->memory.store8 = DS9Store8;
  53	arm9->memory.storeMultiple = DS9StoreMultiple;
  54	arm9->memory.stall = DSMemoryStall;
  55
  56	int i;
  57	for (i = 0; i < 8; ++i) {
  58		// TODO: Formalize
  59		ds->ds7.memory.waitstatesNonseq16[i] = DS7_BASE_WAITSTATES[i];
  60		ds->ds7.memory.waitstatesSeq16[i] = DS7_BASE_WAITSTATES_SEQ[i];
  61		ds->ds7.memory.waitstatesPrefetchNonseq16[i] = DS7_BASE_WAITSTATES[i];
  62		ds->ds7.memory.waitstatesPrefetchSeq16[i] = DS7_BASE_WAITSTATES_SEQ[i];
  63		ds->ds7.memory.waitstatesNonseq32[i] = DS7_BASE_WAITSTATES_32[i];
  64		ds->ds7.memory.waitstatesSeq32[i] = DS7_BASE_WAITSTATES_SEQ_32[i];
  65		ds->ds7.memory.waitstatesPrefetchNonseq32[i] = DS7_BASE_WAITSTATES_32[i];
  66		ds->ds7.memory.waitstatesPrefetchSeq32[i] = DS7_BASE_WAITSTATES_SEQ_32[i];
  67
  68		ds->ds9.memory.waitstatesNonseq16[i] = DS9_BASE_WAITSTATES[i];
  69		ds->ds9.memory.waitstatesSeq16[i] = DS9_BASE_WAITSTATES_SEQ[i];
  70		ds->ds9.memory.waitstatesPrefetchNonseq16[i] = DS9_BASE_WAITSTATES[i];
  71		ds->ds9.memory.waitstatesPrefetchSeq16[i] = DS9_BASE_WAITSTATES[i];
  72		ds->ds9.memory.waitstatesNonseq32[i] = DS9_BASE_WAITSTATES_32[i];
  73		ds->ds9.memory.waitstatesSeq32[i] = DS9_BASE_WAITSTATES_SEQ_32[i];
  74		ds->ds9.memory.waitstatesPrefetchNonseq32[i] = DS9_BASE_WAITSTATES_32[i];
  75		ds->ds9.memory.waitstatesPrefetchSeq32[i] = DS9_BASE_WAITSTATES_32[i];
  76	}
  77	for (; i < 256; ++i) {
  78		ds->ds7.memory.waitstatesNonseq16[i] = 0;
  79		ds->ds7.memory.waitstatesSeq16[i] = 0;
  80		ds->ds7.memory.waitstatesNonseq32[i] = 0;
  81		ds->ds7.memory.waitstatesSeq32[i] = 0;
  82
  83		ds->ds9.memory.waitstatesNonseq16[i] = 0;
  84		ds->ds9.memory.waitstatesSeq16[i] = 0;
  85		ds->ds9.memory.waitstatesNonseq32[i] = 0;
  86		ds->ds9.memory.waitstatesSeq32[i] = 0;
  87	}
  88
  89	ds->memory.bios7 = NULL;
  90	ds->memory.bios9 = NULL;
  91	ds->memory.wram = NULL;
  92	ds->memory.wram7 = NULL;
  93	ds->memory.ram = NULL;
  94	ds->memory.itcm = NULL;
  95	ds->memory.dtcm = NULL;
  96	ds->memory.rom = NULL;
  97
  98	ds->ds7.memory.activeRegion = -1;
  99	ds->ds9.memory.activeRegion = -1;
 100	ds->ds7.memory.io = ds->memory.io7;
 101	ds->ds9.memory.io = ds->memory.io9;
 102
 103	arm7->memory.activeRegion = 0;
 104	arm7->memory.activeMask = 0;
 105	arm7->memory.setActiveRegion = DS7SetActiveRegion;
 106	arm7->memory.activeSeqCycles32 = 0;
 107	arm7->memory.activeSeqCycles16 = 0;
 108	arm7->memory.activeNonseqCycles32 = 0;
 109	arm7->memory.activeNonseqCycles16 = 0;
 110
 111	arm9->memory.activeRegion = 0;
 112	arm9->memory.activeMask = 0;
 113	arm9->memory.setActiveRegion = DS9SetActiveRegion;
 114	arm9->memory.activeSeqCycles32 = 0;
 115	arm9->memory.activeSeqCycles16 = 0;
 116	arm9->memory.activeNonseqCycles32 = 0;
 117	arm9->memory.activeNonseqCycles16 = 0;
 118}
 119
 120void DSMemoryDeinit(struct DS* ds) {
 121	mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
 122	mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
 123	mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
 124	mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
 125	mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
 126}
 127
 128void DSMemoryReset(struct DS* ds) {
 129	if (ds->memory.wram) {
 130		mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM);
 131	}
 132	ds->memory.wram = anonymousMemoryMap(DS_SIZE_WORKING_RAM);
 133
 134	if (ds->memory.wram7) {
 135		mappedMemoryFree(ds->memory.wram7, DS7_SIZE_WORKING_RAM);
 136	}
 137	ds->memory.wram7 = anonymousMemoryMap(DS7_SIZE_WORKING_RAM);
 138
 139	if (ds->memory.ram) {
 140		mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM);
 141	}
 142	ds->memory.ram = anonymousMemoryMap(DS_SIZE_RAM);
 143
 144	if (ds->memory.itcm) {
 145		mappedMemoryFree(ds->memory.itcm, DS9_SIZE_ITCM);
 146	}
 147	ds->memory.itcm = anonymousMemoryMap(DS9_SIZE_ITCM);
 148
 149	if (ds->memory.dtcm) {
 150		mappedMemoryFree(ds->memory.dtcm, DS9_SIZE_DTCM);
 151	}
 152	ds->memory.dtcm = anonymousMemoryMap(DS9_SIZE_DTCM);
 153
 154	memset(ds->ds7.memory.dma, 0, sizeof(ds->ds7.memory.dma));
 155	memset(ds->ds9.memory.dma, 0, sizeof(ds->ds9.memory.dma));
 156	ds->ds7.memory.activeDMA = -1;
 157	ds->ds9.memory.activeDMA = -1;
 158
 159	// TODO: Correct size
 160	ds->memory.wramSize7 = 0x8000;
 161	ds->memory.wramSize9 = 0;
 162
 163	if (!ds->memory.wram || !ds->memory.wram7 || !ds->memory.ram || !ds->memory.itcm || !ds->memory.dtcm) {
 164		DSMemoryDeinit(ds);
 165		mLOG(DS_MEM, FATAL, "Could not map memory");
 166	}
 167}
 168
 169static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
 170	struct DS* ds = (struct DS*) cpu->master;
 171	struct DSCoreMemory* memory = &ds->ds7.memory;
 172
 173	int newRegion = address >> DS_BASE_OFFSET;
 174
 175	memory->activeRegion = newRegion;
 176	switch (newRegion) {
 177	case DS_REGION_WORKING_RAM:
 178		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 179			cpu->memory.activeRegion = ds->memory.wram7;
 180			cpu->memory.activeMask = DS7_SIZE_WORKING_RAM - 1;
 181		} else {
 182			cpu->memory.activeRegion = ds->memory.wram;
 183			cpu->memory.activeMask = ds->memory.wramSize7 - 1;
 184		}
 185		break;
 186	case DS7_REGION_BIOS:
 187		if (ds->memory.bios7) {
 188			cpu->memory.activeRegion = ds->memory.bios7;
 189			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
 190		} else {
 191			cpu->memory.activeRegion = _deadbeef;
 192			cpu->memory.activeMask = 0;
 193		}
 194		break;
 195	case DS_REGION_RAM:
 196		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 197			cpu->memory.activeRegion = ds->memory.ram;
 198			cpu->memory.activeMask = DS_SIZE_RAM - 1;
 199			break;
 200		}
 201	// Fall through
 202	default:
 203		memory->activeRegion = -1;
 204		cpu->memory.activeRegion = _deadbeef;
 205		cpu->memory.activeMask = 0;
 206		mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
 207		break;
 208	}
 209	cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion];
 210	cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion];
 211	cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion];
 212	cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion];
 213}
 214
 215uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 216	struct DS* ds = (struct DS*) cpu->master;
 217	struct DSMemory* memory = &ds->memory;
 218	uint32_t value = 0;
 219	int wait = ds->ds7.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 220
 221	switch (address >> DS_BASE_OFFSET) {
 222	case DS7_REGION_BIOS:
 223		LOAD_32(value, address & (DS7_SIZE_BIOS - 1), memory->bios7);
 224		break;
 225	case DS_REGION_WORKING_RAM:
 226		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 227			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 228		} else {
 229			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
 230		}
 231		break;
 232	case DS_REGION_RAM:
 233		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 234			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 235			break;
 236		}
 237		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
 238		break;
 239	case DS_REGION_IO:
 240		value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
 241		break;
 242	default:
 243		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
 244		break;
 245	}
 246
 247	if (cycleCounter) {
 248		wait += 2;
 249		*cycleCounter += wait;
 250	}
 251	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 252	int rotate = (address & 3) << 3;
 253	return ROR(value, rotate);
 254}
 255
 256uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 257	struct DS* ds = (struct DS*) cpu->master;
 258	struct DSMemory* memory = &ds->memory;
 259	uint32_t value = 0;
 260	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 261
 262	switch (address >> DS_BASE_OFFSET) {
 263	case DS7_REGION_BIOS:
 264		LOAD_16(value, address & (DS7_SIZE_BIOS - 1), memory->bios7);
 265		break;
 266	case DS_REGION_WORKING_RAM:
 267		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 268			LOAD_16(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 269		} else {
 270			LOAD_16(value, address & (ds->memory.wramSize7 - 1), memory->wram);
 271		}
 272		break;
 273	case DS_REGION_RAM:
 274		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 275			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 276			break;
 277		}
 278		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
 279	case DS_REGION_IO:
 280		value = DS7IORead(ds, address & 0x00FFFFFF);
 281		break;
 282	default:
 283		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address);
 284		break;
 285	}
 286
 287	if (cycleCounter) {
 288		wait += 2;
 289		*cycleCounter += wait;
 290	}
 291	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 292	int rotate = (address & 1) << 3;
 293	return ROR(value, rotate);
 294}
 295
 296uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 297	struct DS* ds = (struct DS*) cpu->master;
 298	struct DSMemory* memory = &ds->memory;
 299	uint32_t value = 0;
 300	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 301
 302	switch (address >> DS_BASE_OFFSET) {
 303	case DS_REGION_WORKING_RAM:
 304		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 305			value = ((uint8_t*) memory->wram7)[address & (DS7_SIZE_WORKING_RAM - 1)];
 306		} else {
 307			value = ((uint8_t*) memory->wram)[address & (ds->memory.wramSize7 - 1)];
 308		}
 309		break;
 310	case DS_REGION_RAM:
 311		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 312			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
 313			break;
 314		}
 315		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
 316	default:
 317		mLOG(DS_MEM, STUB, "Unimplemented DS7 Load8: %08X", address);
 318		break;
 319	}
 320
 321	if (cycleCounter) {
 322		wait += 2;
 323		*cycleCounter += wait;
 324	}
 325	return value;
 326}
 327
 328void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
 329	struct DS* ds = (struct DS*) cpu->master;
 330	struct DSMemory* memory = &ds->memory;
 331	int wait = ds->ds7.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 332
 333	switch (address >> DS_BASE_OFFSET) {
 334	case DS_REGION_WORKING_RAM:
 335		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 336			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 337		} else {
 338			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
 339		}
 340		break;
 341	case DS_REGION_RAM:
 342		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 343			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 344			break;
 345		}
 346		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
 347		break;
 348	case DS_REGION_IO:
 349		DS7IOWrite32(ds, address & 0x00FFFFFF, value);
 350		break;
 351	default:
 352		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
 353		break;
 354	}
 355
 356	if (cycleCounter) {
 357		++wait;
 358		*cycleCounter += wait;
 359	}
 360}
 361
 362void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
 363	struct DS* ds = (struct DS*) cpu->master;
 364	struct DSMemory* memory = &ds->memory;
 365	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 366
 367	switch (address >> DS_BASE_OFFSET) {
 368	case DS_REGION_WORKING_RAM:
 369		if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 370			STORE_16(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 371		} else {
 372			STORE_16(value, address & (ds->memory.wramSize7 - 1), memory->wram);
 373		}
 374		break;
 375	case DS_REGION_RAM:
 376		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 377			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 378			break;
 379		}
 380		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
 381		break;
 382	case DS_REGION_IO:
 383		DS7IOWrite(ds, address & 0x00FFFFFF, value);
 384		break;
 385	default:
 386		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value);
 387		break;
 388	}
 389
 390	if (cycleCounter) {
 391		++wait;
 392		*cycleCounter += wait;
 393	}
 394}
 395
 396void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
 397	struct DS* ds = (struct DS*) cpu->master;
 398	struct DSMemory* memory = &ds->memory;
 399	int wait = ds->ds7.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 400
 401	switch (address >> DS_BASE_OFFSET) {
 402	case DS_REGION_RAM:
 403		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 404			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
 405			break;
 406		}
 407		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
 408	case DS_REGION_IO:
 409		DS7IOWrite8(ds, address & 0x00FFFFFF, value);
 410		break;
 411	default:
 412		mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value);
 413		break;
 414	}
 415
 416	if (cycleCounter) {
 417		++wait;
 418		*cycleCounter += wait;
 419	}
 420}
 421
 422#define LDM_LOOP(LDM) \
 423	for (i = 0; i < 16; i += 4) { \
 424		if (UNLIKELY(mask & (1 << i))) { \
 425			LDM; \
 426			cpu->gprs[i] = value; \
 427			++wait; \
 428			wait += ws32[address >> DS_BASE_OFFSET]; \
 429			address += 4; \
 430		} \
 431		if (UNLIKELY(mask & (2 << i))) { \
 432			LDM; \
 433			cpu->gprs[i + 1] = value; \
 434			++wait; \
 435			wait += ws32[address >> DS_BASE_OFFSET]; \
 436			address += 4; \
 437		} \
 438		if (UNLIKELY(mask & (4 << i))) { \
 439			LDM; \
 440			cpu->gprs[i + 2] = value; \
 441			++wait; \
 442			wait += ws32[address >> DS_BASE_OFFSET]; \
 443			address += 4; \
 444		} \
 445		if (UNLIKELY(mask & (8 << i))) { \
 446			LDM; \
 447			cpu->gprs[i + 3] = value; \
 448			++wait; \
 449			wait += ws32[address >> DS_BASE_OFFSET]; \
 450			address += 4; \
 451		} \
 452	}
 453
 454#define STM_LOOP(STM) \
 455	for (i = 0; i < 16; i += 4) { \
 456		if (UNLIKELY(mask & (1 << i))) { \
 457			value = cpu->gprs[i]; \
 458			STM; \
 459			++wait; \
 460			wait += ws32[address >> DS_BASE_OFFSET]; \
 461			address += 4; \
 462		} \
 463		if (UNLIKELY(mask & (2 << i))) { \
 464			value = cpu->gprs[i + 1]; \
 465			STM; \
 466			++wait; \
 467			wait += ws32[address >> DS_BASE_OFFSET]; \
 468			address += 4; \
 469		} \
 470		if (UNLIKELY(mask & (4 << i))) { \
 471			value = cpu->gprs[i + 2]; \
 472			STM; \
 473			++wait; \
 474			wait += ws32[address >> DS_BASE_OFFSET]; \
 475			address += 4; \
 476		} \
 477		if (UNLIKELY(mask & (8 << i))) { \
 478			value = cpu->gprs[i + 3]; \
 479			STM; \
 480			++wait; \
 481			wait += ws32[address >> DS_BASE_OFFSET]; \
 482			address += 4; \
 483		} \
 484	}
 485
 486
 487
 488uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 489	struct DS* ds = (struct DS*) cpu->master;
 490	struct DSMemory* memory = &ds->memory;
 491	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
 492	uint32_t value;
 493	int wait = 0;
 494
 495	int i;
 496	int offset = 4;
 497	int popcount = 0;
 498	if (direction & LSM_D) {
 499		offset = -4;
 500		popcount = popcount32(mask);
 501		address -= (popcount << 2) - 4;
 502	}
 503
 504	if (direction & LSM_B) {
 505		address += offset;
 506	}
 507
 508	uint32_t addressMisalign = address & 0x3;
 509	address &= 0xFFFFFFFC;
 510
 511	switch (address >> DS_BASE_OFFSET) {
 512	case DS_REGION_WORKING_RAM:
 513		LDM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 514			LOAD_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 515		} else {
 516			LOAD_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
 517		});
 518		break;
 519	case DS_REGION_RAM:
 520		LDM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 521			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 522		} else {
 523			mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 524		});
 525		break;
 526	case DS_REGION_IO:
 527		LDM_LOOP(value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16));
 528		break;
 529	default:
 530		mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
 531		LDM_LOOP(value = 0);
 532	}
 533
 534	if (cycleCounter) {
 535		++wait;
 536		*cycleCounter += wait;
 537	}
 538
 539	if (direction & LSM_B) {
 540		address -= offset;
 541	}
 542
 543	if (direction & LSM_D) {
 544		address -= (popcount << 2) + 4;
 545	}
 546
 547	return address | addressMisalign;
 548}
 549
 550
 551uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 552	struct DS* ds = (struct DS*) cpu->master;
 553	struct DSMemory* memory = &ds->memory;
 554	char* ws32 = ds->ds7.memory.waitstatesNonseq32;
 555	uint32_t value;
 556	int wait = 0;
 557
 558	int i;
 559	int offset = 4;
 560	int popcount = 0;
 561	if (direction & LSM_D) {
 562		offset = -4;
 563		popcount = popcount32(mask);
 564		address -= (popcount << 2) - 4;
 565	}
 566
 567	if (direction & LSM_B) {
 568		address += offset;
 569	}
 570
 571	uint32_t addressMisalign = address & 0x3;
 572	address &= 0xFFFFFFFC;
 573
 574	switch (address >> DS_BASE_OFFSET) {
 575	case DS_REGION_WORKING_RAM:
 576		STM_LOOP(if (address >= DS7_BASE_WORKING_RAM || !ds->memory.wramSize7) {
 577			STORE_32(value, address & (DS7_SIZE_WORKING_RAM - 1), memory->wram7);
 578		} else {
 579			STORE_32(value, address & (ds->memory.wramSize7 - 1), memory->wram);
 580		});
 581		break;
 582	case DS_REGION_RAM:
 583		STM_LOOP(if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 584			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 585		} else {
 586			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 587		});
 588		break;
 589	default:
 590		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 591		STM_LOOP();
 592		break;
 593	}
 594
 595	if (cycleCounter) {
 596		*cycleCounter += wait;
 597	}
 598
 599	if (direction & LSM_B) {
 600		address -= offset;
 601	}
 602
 603	if (direction & LSM_D) {
 604		address -= (popcount << 2) + 4;
 605	}
 606
 607	return address | addressMisalign;
 608}
 609
 610static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
 611	struct DS* ds = (struct DS*) cpu->master;
 612	struct DSCoreMemory* memory = &ds->ds9.memory;
 613
 614	int newRegion = address >> DS_BASE_OFFSET;
 615
 616	memory->activeRegion = newRegion;
 617	switch (newRegion) {
 618	case DS9_REGION_ITCM:
 619	case DS9_REGION_ITCM_MIRROR:
 620		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 621			cpu->memory.activeRegion = ds->memory.itcm;
 622			cpu->memory.activeMask = DS9_SIZE_ITCM - 1;
 623			break;
 624		}
 625		goto jump_error;
 626	case DS_REGION_RAM:
 627		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 628			cpu->memory.activeRegion = ds->memory.ram;
 629			cpu->memory.activeMask = DS_SIZE_RAM - 1;
 630			break;
 631		}
 632		goto jump_error;
 633	case DS9_REGION_BIOS:
 634		// TODO: Mask properly
 635		if (ds->memory.bios9) {
 636			cpu->memory.activeRegion = ds->memory.bios9;
 637			cpu->memory.activeMask = DS9_SIZE_BIOS - 1;
 638		} else {
 639			cpu->memory.activeRegion = _deadbeef;
 640			cpu->memory.activeMask = 0;
 641		}
 642		break;
 643	default:
 644	jump_error:
 645		memory->activeRegion = -1;
 646		cpu->memory.activeRegion = _deadbeef;
 647		cpu->memory.activeMask = 0;
 648		mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address);
 649		return;
 650	}
 651	cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion];
 652	cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion];
 653	cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion];
 654	cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion];
 655}
 656
 657uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 658	struct DS* ds = (struct DS*) cpu->master;
 659	struct DSMemory* memory = &ds->memory;
 660	uint32_t value = 0;
 661	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 662
 663	switch (address >> DS_BASE_OFFSET) {
 664	case DS9_REGION_ITCM:
 665	case DS9_REGION_ITCM_MIRROR:
 666		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 667			LOAD_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 668			break;
 669		}
 670		mLOG(DS_MEM, STUB, "Bad DS9 Load32: %08X:%08X", address, value);
 671		break;
 672	case DS_REGION_RAM:
 673		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 674			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 675			break;
 676		}
 677		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 678			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 679			break;
 680		}
 681		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 682		break;
 683	case DS_REGION_IO:
 684		value = DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
 685		break;
 686	case DS9_REGION_BIOS:
 687		// TODO: Fix undersized BIOS
 688		// TODO: Fix masking
 689		LOAD_32(value, address & (DS9_SIZE_BIOS - 1), memory->bios9);
 690		break;
 691	default:
 692		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 693		break;
 694	}
 695
 696	if (cycleCounter) {
 697		wait += 2;
 698		*cycleCounter += wait;
 699	}
 700	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 701	int rotate = (address & 3) << 3;
 702	return ROR(value, rotate);
 703}
 704
 705uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 706	struct DS* ds = (struct DS*) cpu->master;
 707	struct DSMemory* memory = &ds->memory;
 708	uint32_t value = 0;
 709	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 710
 711	switch (address >> DS_BASE_OFFSET) {
 712	case DS9_REGION_ITCM:
 713	case DS9_REGION_ITCM_MIRROR:
 714		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 715			LOAD_16(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 716			break;
 717		}
 718		mLOG(DS_MEM, STUB, "Bad DS9 Load16: %08X:%08X", address, value);
 719		break;
 720	case DS_REGION_RAM:
 721		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 722			LOAD_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 723			break;
 724		}
 725		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 726			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 727			break;
 728		}
 729		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 730	case DS_REGION_IO:
 731		value = DS9IORead(ds, address & 0x00FFFFFF);
 732		break;
 733	case DS9_REGION_BIOS:
 734		// TODO: Fix undersized BIOS
 735		// TODO: Fix masking
 736		LOAD_16(value, address & (DS9_SIZE_BIOS - 1), memory->bios9);
 737		break;
 738	default:
 739		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 740		break;
 741	}
 742
 743	if (cycleCounter) {
 744		wait += 2;
 745		*cycleCounter += wait;
 746	}
 747	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 748	int rotate = (address & 1) << 3;
 749	return ROR(value, rotate);
 750}
 751
 752uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 753	struct DS* ds = (struct DS*) cpu->master;
 754	struct DSMemory* memory = &ds->memory;
 755	uint32_t value = 0;
 756	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 757
 758	switch (address >> DS_BASE_OFFSET) {
 759	case DS9_REGION_ITCM:
 760	case DS9_REGION_ITCM_MIRROR:
 761		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 762			value = ((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)];
 763			break;
 764		}
 765		mLOG(DS_MEM, STUB, "Bad DS9 Load8: %08X:%08X", address, value);
 766		break;
 767	case DS_REGION_RAM:
 768		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 769			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
 770			break;
 771		}
 772		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 773			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
 774			break;
 775		}
 776		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
 777	case DS9_REGION_BIOS:
 778		// TODO: Fix undersized BIOS
 779		// TODO: Fix masking
 780		value = ((uint8_t*) memory->bios9)[address & (DS9_SIZE_BIOS - 1)];
 781		break;
 782	default:
 783		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
 784		break;
 785	}
 786
 787	if (cycleCounter) {
 788		wait += 2;
 789		*cycleCounter += wait;
 790	}
 791	return value;
 792}
 793
 794void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
 795	struct DS* ds = (struct DS*) cpu->master;
 796	struct DSMemory* memory = &ds->memory;
 797	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 798
 799	switch (address >> DS_BASE_OFFSET) {
 800	case DS9_REGION_ITCM:
 801	case DS9_REGION_ITCM_MIRROR:
 802		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 803			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 804			break;
 805		}
 806		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
 807		break;
 808	case DS_REGION_RAM:
 809		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 810			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 811			break;
 812		}
 813		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 814			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 815			break;
 816		}
 817		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
 818		break;
 819	case DS_REGION_IO:
 820		DS9IOWrite32(ds, address & 0x00FFFFFF, value);
 821		break;
 822	default:
 823		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
 824		break;
 825	}
 826
 827	if (cycleCounter) {
 828		++wait;
 829		*cycleCounter += wait;
 830	}
 831}
 832
 833void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
 834	struct DS* ds = (struct DS*) cpu->master;
 835	struct DSMemory* memory = &ds->memory;
 836	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 837
 838	switch (address >> DS_BASE_OFFSET) {
 839	case DS9_REGION_ITCM:
 840	case DS9_REGION_ITCM_MIRROR:
 841		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 842			STORE_16(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 843			break;
 844		}
 845		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
 846		break;
 847	case DS_REGION_RAM:
 848		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 849			STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 850			break;
 851		}
 852		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 853			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 854			break;
 855		}
 856		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
 857		break;
 858	case DS_REGION_IO:
 859		DS9IOWrite(ds, address & 0x00FFFFFF, value);
 860		break;
 861	default:
 862		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
 863		break;
 864	}
 865
 866	if (cycleCounter) {
 867		++wait;
 868		*cycleCounter += wait;
 869	}
 870}
 871
 872void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
 873	struct DS* ds = (struct DS*) cpu->master;
 874	struct DSMemory* memory = &ds->memory;
 875	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 876
 877	switch (address >> DS_BASE_OFFSET) {
 878	case DS9_REGION_ITCM:
 879	case DS9_REGION_ITCM_MIRROR:
 880		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 881			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
 882			break;
 883		}
 884		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
 885		break;
 886	case DS_REGION_RAM:
 887		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 888			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
 889			break;
 890		}
 891		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 892			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
 893			break;
 894		}
 895		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
 896	case DS_REGION_IO:
 897		DS9IOWrite8(ds, address & 0x00FFFFFF, value);
 898		break;
 899	default:
 900		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
 901		break;
 902	}
 903
 904	if (cycleCounter) {
 905		++wait;
 906		*cycleCounter += wait;
 907	}
 908}
 909
 910uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 911	struct DS* ds = (struct DS*) cpu->master;
 912	struct DSMemory* memory = &ds->memory;
 913	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
 914	uint32_t value;
 915	int wait = 0;
 916
 917	int i;
 918	int offset = 4;
 919	int popcount = 0;
 920	if (direction & LSM_D) {
 921		offset = -4;
 922		popcount = popcount32(mask);
 923		address -= (popcount << 2) - 4;
 924	}
 925
 926	if (direction & LSM_B) {
 927		address += offset;
 928	}
 929
 930	uint32_t addressMisalign = address & 0x3;
 931	address &= 0xFFFFFFFC;
 932
 933	switch (address >> DS_BASE_OFFSET) {
 934	case DS_REGION_RAM:
 935		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 936			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 937		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 938			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 939		} else {
 940			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
 941		});
 942		break;
 943	default:
 944		mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
 945		LDM_LOOP(value = 0);
 946		break;
 947	}
 948
 949	if (cycleCounter) {
 950		++wait;
 951		*cycleCounter += wait;
 952	}
 953
 954	if (direction & LSM_B) {
 955		address -= offset;
 956	}
 957
 958	if (direction & LSM_D) {
 959		address -= (popcount << 2) + 4;
 960	}
 961
 962	return address | addressMisalign;
 963}
 964
 965
 966uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 967	struct DS* ds = (struct DS*) cpu->master;
 968	struct DSMemory* memory = &ds->memory;
 969	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
 970	uint32_t value;
 971	int wait = 0;
 972
 973	int i;
 974	int offset = 4;
 975	int popcount = 0;
 976	if (direction & LSM_D) {
 977		offset = -4;
 978		popcount = popcount32(mask);
 979		address -= (popcount << 2) - 4;
 980	}
 981
 982	if (direction & LSM_B) {
 983		address += offset;
 984	}
 985
 986	uint32_t addressMisalign = address & 0x3;
 987	address &= 0xFFFFFFFC;
 988
 989	switch (address >> DS_BASE_OFFSET) {
 990	case DS9_REGION_ITCM:
 991	case DS9_REGION_ITCM_MIRROR:
 992		STM_LOOP(if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 993			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 994		} else {
 995			mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
 996		});
 997		break;
 998	case DS_REGION_RAM:
 999		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
1000			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
1001		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
1002			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
1003		} else {
1004			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1005		});
1006		break;
1007	default:
1008		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
1009		STM_LOOP();
1010		break;
1011	}
1012
1013	if (cycleCounter) {
1014		*cycleCounter += wait;
1015	}
1016
1017	if (direction & LSM_B) {
1018		address -= offset;
1019	}
1020
1021	if (direction & LSM_D) {
1022		address -= (popcount << 2) + 4;
1023	}
1024
1025	return address | addressMisalign;
1026}
1027
1028int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
1029	return wait;
1030}
1031