all repos — mgba @ 81812fa81858c6297de788fc8b5e3186dd743646

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 DS_REGION_RAM:
 665		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 666			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 667			break;
 668		}
 669		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 670			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 671			break;
 672		}
 673		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 674		break;
 675	case DS_REGION_IO:
 676		value = DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
 677		break;
 678	case DS9_REGION_BIOS:
 679		// TODO: Fix undersized BIOS
 680		// TODO: Fix masking
 681		LOAD_32(value, address & (DS9_SIZE_BIOS - 1), memory->bios9);
 682		break;
 683	default:
 684		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
 685		break;
 686	}
 687
 688	if (cycleCounter) {
 689		wait += 2;
 690		*cycleCounter += wait;
 691	}
 692	// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
 693	int rotate = (address & 3) << 3;
 694	return ROR(value, rotate);
 695}
 696
 697uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 698	struct DS* ds = (struct DS*) cpu->master;
 699	struct DSMemory* memory = &ds->memory;
 700	uint32_t value = 0;
 701	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 702
 703	switch (address >> DS_BASE_OFFSET) {
 704	case DS_REGION_RAM:
 705		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 706			LOAD_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 707			break;
 708		}
 709		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 710			LOAD_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 711			break;
 712		}
 713		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 714	case DS_REGION_IO:
 715		value = DS9IORead(ds, address & 0x00FFFFFF);
 716		break;
 717	case DS9_REGION_BIOS:
 718		// TODO: Fix undersized BIOS
 719		// TODO: Fix masking
 720		LOAD_16(value, address & (DS9_SIZE_BIOS - 1), memory->bios9);
 721		break;
 722	default:
 723		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address);
 724		break;
 725	}
 726
 727	if (cycleCounter) {
 728		wait += 2;
 729		*cycleCounter += wait;
 730	}
 731	// Unaligned 16-bit loads are "unpredictable", TODO: See what DS does
 732	int rotate = (address & 1) << 3;
 733	return ROR(value, rotate);
 734}
 735
 736uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
 737	struct DS* ds = (struct DS*) cpu->master;
 738	struct DSMemory* memory = &ds->memory;
 739	uint32_t value = 0;
 740	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 741
 742	switch (address >> DS_BASE_OFFSET) {
 743	case DS_REGION_RAM:
 744		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 745			value = ((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)];
 746			break;
 747		}
 748		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 749			value = ((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)];
 750			break;
 751		}
 752		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
 753	case DS9_REGION_BIOS:
 754		// TODO: Fix undersized BIOS
 755		// TODO: Fix masking
 756		value = ((uint8_t*) memory->bios9)[address & (DS9_SIZE_BIOS - 1)];
 757		break;
 758	default:
 759		mLOG(DS_MEM, STUB, "Unimplemented DS9 Load8: %08X", address);
 760		break;
 761	}
 762
 763	if (cycleCounter) {
 764		wait += 2;
 765		*cycleCounter += wait;
 766	}
 767	return value;
 768}
 769
 770void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {
 771	struct DS* ds = (struct DS*) cpu->master;
 772	struct DSMemory* memory = &ds->memory;
 773	int wait = ds->ds9.memory.waitstatesNonseq32[address >> DS_BASE_OFFSET];
 774
 775	switch (address >> DS_BASE_OFFSET) {
 776	case DS9_REGION_ITCM:
 777	case DS9_REGION_ITCM_MIRROR:
 778		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 779			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 780			break;
 781		}
 782		mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
 783		break;
 784	case DS_REGION_RAM:
 785		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 786			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 787			break;
 788		}
 789		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 790			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 791			break;
 792		}
 793		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
 794		break;
 795	case DS_REGION_IO:
 796		DS9IOWrite32(ds, address & 0x00FFFFFF, value);
 797		break;
 798	default:
 799		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
 800		break;
 801	}
 802
 803	if (cycleCounter) {
 804		++wait;
 805		*cycleCounter += wait;
 806	}
 807}
 808
 809void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {
 810	struct DS* ds = (struct DS*) cpu->master;
 811	struct DSMemory* memory = &ds->memory;
 812	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 813
 814	switch (address >> DS_BASE_OFFSET) {
 815	case DS9_REGION_ITCM:
 816	case DS9_REGION_ITCM_MIRROR:
 817		if (address < (512 << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 818			STORE_16(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 819			break;
 820		}
 821		mLOG(DS_MEM, STUB, "Bad DS9 Store16: %08X:%04X", address, value);
 822		break;
 823	case DS_REGION_RAM:
 824		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 825			STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 826			break;
 827		}
 828		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 829			STORE_16(value, address & (DS_SIZE_RAM - 1), memory->ram);
 830			break;
 831		}
 832		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
 833		break;
 834	case DS_REGION_IO:
 835		DS9IOWrite(ds, address & 0x00FFFFFF, value);
 836		break;
 837	default:
 838		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value);
 839		break;
 840	}
 841
 842	if (cycleCounter) {
 843		++wait;
 844		*cycleCounter += wait;
 845	}
 846}
 847
 848void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {
 849	struct DS* ds = (struct DS*) cpu->master;
 850	struct DSMemory* memory = &ds->memory;
 851	int wait = ds->ds9.memory.waitstatesNonseq16[address >> DS_BASE_OFFSET];
 852
 853	switch (address >> DS_BASE_OFFSET) {
 854	case DS9_REGION_ITCM:
 855	case DS9_REGION_ITCM_MIRROR:
 856		if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 857			((uint8_t*) memory->itcm)[address & (DS9_SIZE_ITCM - 1)] = value;
 858			break;
 859		}
 860		mLOG(DS_MEM, STUB, "Bad DS9 Store8: %08X:%02X", address, value);
 861		break;
 862	case DS_REGION_RAM:
 863		if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 864			((uint8_t*) memory->dtcm)[address & (DS9_SIZE_DTCM - 1)] = value;
 865			break;
 866		}
 867		if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 868			((uint8_t*) memory->ram)[address & (DS_SIZE_RAM - 1)] = value;
 869			break;
 870		}
 871		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
 872	case DS_REGION_IO:
 873		DS9IOWrite8(ds, address & 0x00FFFFFF, value);
 874		break;
 875	default:
 876		mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value);
 877		break;
 878	}
 879
 880	if (cycleCounter) {
 881		++wait;
 882		*cycleCounter += wait;
 883	}
 884}
 885
 886uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 887	struct DS* ds = (struct DS*) cpu->master;
 888	struct DSMemory* memory = &ds->memory;
 889	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
 890	uint32_t value;
 891	int wait = 0;
 892
 893	int i;
 894	int offset = 4;
 895	int popcount = 0;
 896	if (direction & LSM_D) {
 897		offset = -4;
 898		popcount = popcount32(mask);
 899		address -= (popcount << 2) - 4;
 900	}
 901
 902	if (direction & LSM_B) {
 903		address += offset;
 904	}
 905
 906	uint32_t addressMisalign = address & 0x3;
 907	address &= 0xFFFFFFFC;
 908
 909	switch (address >> DS_BASE_OFFSET) {
 910	case DS_REGION_RAM:
 911		LDM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 912			LOAD_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 913		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 914			LOAD_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 915		} else {
 916			mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
 917		});
 918		break;
 919	default:
 920		mLOG(DS_MEM, STUB, "Unimplemented DS9 LDM: %08X", address);
 921		LDM_LOOP(value = 0);
 922		break;
 923	}
 924
 925	if (cycleCounter) {
 926		++wait;
 927		*cycleCounter += wait;
 928	}
 929
 930	if (direction & LSM_B) {
 931		address -= offset;
 932	}
 933
 934	if (direction & LSM_D) {
 935		address -= (popcount << 2) + 4;
 936	}
 937
 938	return address | addressMisalign;
 939}
 940
 941
 942uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {
 943	struct DS* ds = (struct DS*) cpu->master;
 944	struct DSMemory* memory = &ds->memory;
 945	char* ws32 = ds->ds9.memory.waitstatesNonseq32;
 946	uint32_t value;
 947	int wait = 0;
 948
 949	int i;
 950	int offset = 4;
 951	int popcount = 0;
 952	if (direction & LSM_D) {
 953		offset = -4;
 954		popcount = popcount32(mask);
 955		address -= (popcount << 2) - 4;
 956	}
 957
 958	if (direction & LSM_B) {
 959		address += offset;
 960	}
 961
 962	uint32_t addressMisalign = address & 0x3;
 963	address &= 0xFFFFFFFC;
 964
 965	switch (address >> DS_BASE_OFFSET) {
 966	case DS9_REGION_ITCM:
 967	case DS9_REGION_ITCM_MIRROR:
 968		STM_LOOP(if (address < (512U << ARMTCMControlGetVirtualSize(cpu->cp15.r9.i))) {
 969			STORE_32(value, address & (DS9_SIZE_ITCM - 1), memory->itcm);
 970		} else {
 971			mLOG(DS_MEM, STUB, "Bad DS9 Store32: %08X:%08X", address, value);
 972		});
 973		break;
 974	case DS_REGION_RAM:
 975		STM_LOOP(if ((address & ~(DS9_SIZE_DTCM - 1)) == DS9_BASE_DTCM) {
 976			STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm);
 977		} else if ((address & (DS_SIZE_RAM - 1)) < DS_SIZE_RAM) {
 978			STORE_32(value, address & (DS_SIZE_RAM - 1), memory->ram);
 979		} else {
 980			mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 981		});
 982		break;
 983	default:
 984		mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
 985		STM_LOOP();
 986		break;
 987	}
 988
 989	if (cycleCounter) {
 990		*cycleCounter += wait;
 991	}
 992
 993	if (direction & LSM_B) {
 994		address -= offset;
 995	}
 996
 997	if (direction & LSM_D) {
 998		address -= (popcount << 2) + 4;
 999	}
1000
1001	return address | addressMisalign;
1002}
1003
1004int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) {
1005	return wait;
1006}
1007