all repos — mgba @ 488decf83a8ff476830f8afa7b68bbd898a04a03

mGBA Game Boy Advance Emulator

src/ds/memory.c (view raw)

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