all repos — mgba @ 3cf9446ba17e5fe7b0de4eff60fa063c55d7c6b2

mGBA Game Boy Advance Emulator

DS: A little more skeleton
Jeffrey Pfau jeffrey@endrift.com
Wed, 01 Jun 2016 19:14:18 -0700
commit

3cf9446ba17e5fe7b0de4eff60fa063c55d7c6b2

parent

290d5b77dd4f47e7f96981728eea5dc33bd29f1d

M CMakeLists.txtCMakeLists.txt

@@ -842,6 +842,7 @@ if(NOT QUIET)

message(STATUS "Platforms:") message(STATUS " Game Boy Advance: ${M_CORE_GBA}") message(STATUS " Game Boy: ${M_CORE_GB}") + message(STATUS " DS: ${M_CORE_DS}") message(STATUS "Features:") message(STATUS " Debuggers: ${USE_DEBUGGERS}") message(STATUS " CLI debugger: ${USE_EDITLINE}")
A src/ds/bios.c

@@ -0,0 +1,11 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "bios.h" + +mLOG_DEFINE_CATEGORY(DS_BIOS, "DS BIOS"); + +const uint32_t DS7_BIOS_CHECKSUM = 0x1280F0D5; +const uint32_t DS9_BIOS_CHECKSUM = 0x2AB23573;
A src/ds/bios.h

@@ -0,0 +1,24 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef DS_BIOS_H +#define DS_BIOS_H + +#include "util/common.h" + +#include "core/log.h" + +mLOG_DECLARE_CATEGORY(DS_BIOS); + +struct ARMCore; +void DS7Swi16(struct ARMCore* cpu, int immediate); +void DS7Swi32(struct ARMCore* cpu, int immediate); +void DS9Swi16(struct ARMCore* cpu, int immediate); +void DS9Swi32(struct ARMCore* cpu, int immediate); + +extern const uint32_t DS7_BIOS_CHECKSUM; +extern const uint32_t DS9_BIOS_CHECKSUM; + +#endif
M src/ds/core.csrc/ds/core.c

@@ -91,6 +91,14 @@ ds->sync = sync;

} static void _DSCoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) { + struct DS* ds = core->board; + struct VFile* bios = NULL; + if (core->opts.useBios && core->opts.bios) { + bios = VFileOpen(core->opts.bios, O_RDONLY); + } + if (bios) { + DSLoadBIOS(ds, bios); + } } static void _DSCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {

@@ -124,7 +132,8 @@ return DSLoadROM(core->board, vf);

} static bool _DSCoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) { - return false; + UNUSED(type); + return DSLoadBIOS(core->board, vf); } static bool _DSCoreLoadSave(struct mCore* core, struct VFile* vf) {
M src/ds/ds.csrc/ds/ds.c

@@ -8,11 +8,11 @@

#include "arm/decoder.h" #include "arm/debugger/debugger.h" #include "arm/isa-inlines.h" +#include "ds/bios.h" #include "util/crc32.h" #include "util/memory.h" #include "util/math.h" -#include "util/patch.h" #include "util/vfs.h" mLOG_DEFINE_CATEGORY(DS, "DS");

@@ -67,8 +67,11 @@ return;

} ds->arm9 = cpu; + ds->arm9->cp15.r1.c0 = ARMControlRegFillVE(0); + DS7InterruptHandlerInit(&ds->arm7->irqh); DS9InterruptHandlerInit(&ds->arm9->irqh); + DSMemoryInit(ds); ds->video.p = ds;

@@ -92,6 +95,7 @@ }

void DSDestroy(struct DS* ds) { DSUnloadROM(ds); + DSMemoryDeinit(ds); } void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) {

@@ -134,6 +138,9 @@ ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);

cpu->gprs[ARM_SP] = DS9_SP_BASE_SVC; ARMSetPrivilegeMode(cpu, MODE_SYSTEM); cpu->gprs[ARM_SP] = DS9_SP_BASE; + + struct DS* ds = (struct DS*) cpu->master; + DSMemoryReset(ds); } static void DSProcessEvents(struct ARMCore* cpu) {

@@ -196,6 +203,35 @@ if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) {

return false; } return memcmp(signature, DS_ROM_MAGIC, sizeof(signature)) == 0; +} + +bool DSLoadBIOS(struct DS* ds, struct VFile* vf) { + size_t size = vf->size(vf); + void* data = NULL; + uint32_t crc; + if (size == DS7_SIZE_BIOS) { + data = vf->map(vf, size, MAP_READ); + } else if (size == 0x1000) { + data = vf->map(vf, size, MAP_READ); + } + if (!data) { + return false; + } + crc = doCrc32(data, size); + if (crc == DS7_BIOS_CHECKSUM) { + ds->bios7Vf = vf; + ds->memory.bios7 = data; + mLOG(DS, INFO, "Official DS ARM7 BIOS detected"); + } else if (crc == DS9_BIOS_CHECKSUM) { + ds->bios9Vf = vf; + ds->memory.bios9 = data; + mLOG(DS, INFO, "Official DS ARM9 BIOS detected"); + } else { + mLOG(DS, WARN, "BIOS checksum incorrect"); + vf->unmap(vf, data, size); + return false; + } + return true; } void DSGetGameCode(struct DS* ds, char* out) {
M src/ds/ds.hsrc/ds/ds.h

@@ -11,6 +11,7 @@

#include "arm/arm.h" #include "core/log.h" +#include "ds/memory.h" #include "ds/video.h" extern const uint32_t DS_ARM946ES_FREQUENCY;

@@ -54,6 +55,7 @@ struct mCPUComponent d;

struct ARMCore* arm7; struct ARMCore* arm9; + struct DSMemory memory; struct DSVideo video; struct mCoreSync* sync;

@@ -63,13 +65,15 @@

int springIRQ7; int springIRQ9; - uint32_t biosChecksum; + uint32_t bios7Checksum; + uint32_t bios9Checksum; int* keySource; struct mRTCSource* rtcSource; struct mRumble* rumble; - uint32_t romCrc32; struct VFile* romVf; + struct VFile* bios7Vf; + struct VFile* bios9Vf; struct mKeyCallback* keyCallback; };

@@ -120,6 +124,8 @@

bool DSLoadROM(struct DS* ds, struct VFile* vf); void DSUnloadROM(struct DS* ds); void DSApplyPatch(struct DS* ds, struct Patch* patch); + +bool DSLoadBIOS(struct DS* ds, struct VFile* vf); bool DSIsROM(struct VFile* vf); void DSGetGameCode(struct DS* ds, char* out);
A src/ds/memory.c

@@ -0,0 +1,583 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "memory.h" + +#include "ds/ds.h" +#include "util/math.h" + +mLOG_DEFINE_CATEGORY(DS_MEM, "DS Memory"); + +#define LDM_LOOP(LDM) \ + for (i = 0; i < 16; i += 4) { \ + if (UNLIKELY(mask & (1 << i))) { \ + LDM; \ + cpu->gprs[i] = value; \ + ++wait; \ + address += 4; \ + } \ + if (UNLIKELY(mask & (2 << i))) { \ + LDM; \ + cpu->gprs[i + 1] = value; \ + ++wait; \ + address += 4; \ + } \ + if (UNLIKELY(mask & (4 << i))) { \ + LDM; \ + cpu->gprs[i + 2] = value; \ + ++wait; \ + address += 4; \ + } \ + if (UNLIKELY(mask & (8 << i))) { \ + LDM; \ + cpu->gprs[i + 3] = value; \ + ++wait; \ + address += 4; \ + } \ + } + +#define STM_LOOP(STM) \ + for (i = 0; i < 16; i += 4) { \ + if (UNLIKELY(mask & (1 << i))) { \ + value = cpu->gprs[i]; \ + STM; \ + ++wait; \ + address += 4; \ + } \ + if (UNLIKELY(mask & (2 << i))) { \ + value = cpu->gprs[i + 1]; \ + STM; \ + ++wait; \ + address += 4; \ + } \ + if (UNLIKELY(mask & (4 << i))) { \ + value = cpu->gprs[i + 2]; \ + STM; \ + ++wait; \ + address += 4; \ + } \ + if (UNLIKELY(mask & (8 << i))) { \ + value = cpu->gprs[i + 3]; \ + STM; \ + ++wait; \ + address += 4; \ + } \ + } + + +static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb + +static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t region); +static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t region); +static int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait); + +static const int DMA_OFFSET[] = { 1, -1, 0, 1 }; + +void DSMemoryInit(struct DS* ds) { + struct ARMCore* arm7 = ds->arm7; + arm7->memory.load32 = DS7Load32; + arm7->memory.load16 = DS7Load16; + arm7->memory.load8 = DS7Load8; + arm7->memory.loadMultiple = DS7LoadMultiple; + arm7->memory.store32 = DS7Store32; + arm7->memory.store16 = DS7Store16; + arm7->memory.store8 = DS7Store8; + arm7->memory.storeMultiple = DS7StoreMultiple; + arm7->memory.stall = DSMemoryStall; + + struct ARMCore* arm9 = ds->arm9; + arm9->memory.load32 = DS9Load32; + arm9->memory.load16 = DS9Load16; + arm9->memory.load8 = DS9Load8; + arm9->memory.loadMultiple = DS9LoadMultiple; + arm9->memory.store32 = DS9Store32; + arm9->memory.store16 = DS9Store16; + arm9->memory.store8 = DS9Store8; + arm9->memory.storeMultiple = DS9StoreMultiple; + arm9->memory.stall = DSMemoryStall; + + ds->memory.bios7 = NULL; + ds->memory.bios9 = NULL; + ds->memory.wram = NULL; + ds->memory.ram = NULL; + ds->memory.rom = NULL; + + ds->memory.activeRegion7 = -1; + ds->memory.activeRegion9 = -1; + + arm7->memory.activeRegion = 0; + arm7->memory.activeMask = 0; + arm7->memory.setActiveRegion = DS7SetActiveRegion; + arm7->memory.activeSeqCycles32 = 0; + arm7->memory.activeSeqCycles16 = 0; + arm7->memory.activeNonseqCycles32 = 0; + arm7->memory.activeNonseqCycles16 = 0; + + arm9->memory.activeRegion = 0; + arm9->memory.activeMask = 0; + arm9->memory.setActiveRegion = DS9SetActiveRegion; + arm9->memory.activeSeqCycles32 = 0; + arm9->memory.activeSeqCycles16 = 0; + arm9->memory.activeNonseqCycles32 = 0; + arm9->memory.activeNonseqCycles16 = 0; +} + +void DSMemoryDeinit(struct DS* ds) { + mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM); + mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM); +} + +void DSMemoryReset(struct DS* ds) { + if (ds->memory.wram) { + mappedMemoryFree(ds->memory.wram, DS_SIZE_WORKING_RAM); + } + ds->memory.wram = anonymousMemoryMap(DS_SIZE_WORKING_RAM); + + if (ds->memory.ram) { + mappedMemoryFree(ds->memory.ram, DS_SIZE_RAM); + } + ds->memory.ram = anonymousMemoryMap(DS_SIZE_RAM); + + memset(ds->memory.dma7, 0, sizeof(ds->memory.dma7)); + memset(ds->memory.dma9, 0, sizeof(ds->memory.dma9)); + ds->memory.activeDMA7 = -1; + ds->memory.activeDMA9 = -1; + ds->memory.nextDMA = INT_MAX; + ds->memory.eventDiff = 0; + + if (!ds->memory.wram || !ds->memory.ram) { + DSMemoryDeinit(ds); + mLOG(DS_MEM, FATAL, "Could not map memory"); + } +} + +static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t address) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + + int newRegion = address >> DS_BASE_OFFSET; + + memory->activeRegion7 = newRegion; + switch (newRegion) { + case DS7_REGION_BIOS: + cpu->memory.activeRegion = memory->bios7; + cpu->memory.activeMask = DS7_SIZE_BIOS - 1; + break; + default: + mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address); + return; + } +} + +uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value = 0; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + wait += 2; + *cycleCounter += wait; + } + // Unaligned 32-bit loads are "rotated" so they make some semblance of sense + int rotate = (address & 3) << 3; + return ROR(value, rotate); +} + +uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value = 0; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + wait += 2; + *cycleCounter += wait; + } + // Unaligned 16-bit loads are "unpredictable", TODO: See what DS does + int rotate = (address & 1) << 3; + return ROR(value, rotate); +} + +uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value = 0; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + wait += 2; + *cycleCounter += wait; + } + return value; +} + +void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } +} + +void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } +} + +void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } +} + +uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value; + int wait = 0; + + int i; + int offset = 4; + int popcount = 0; + if (direction & LSM_D) { + offset = -4; + popcount = popcount32(mask); + address -= (popcount << 2) - 4; + } + + if (direction & LSM_B) { + address += offset; + } + + uint32_t addressMisalign = address & 0x3; + address &= 0xFFFFFFFC; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } + + if (direction & LSM_B) { + address -= offset; + } + + if (direction & LSM_D) { + address -= (popcount << 2) + 4; + } + + return address | addressMisalign; +} + + +uint32_t DS7StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { + struct DS* ds = (struct ds*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value; + int wait = 0; + + int i; + int offset = 4; + int popcount = 0; + if (direction & LSM_D) { + offset = -4; + popcount = popcount32(mask); + address -= (popcount << 2) - 4; + } + + if (direction & LSM_B) { + address += offset; + } + + uint32_t addressMisalign = address & 0x3; + address &= 0xFFFFFFFC; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + *cycleCounter += wait; + } + + if (direction & LSM_B) { + address -= offset; + } + + if (direction & LSM_D) { + address -= (popcount << 2) + 4; + } + + return address | addressMisalign; +} + +static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + + int newRegion = address >> DS_BASE_OFFSET; + + memory->activeRegion7 = newRegion; + switch (newRegion) { + case DS9_REGION_BIOS: + // TODO: Mask properly + if (memory->bios9) { + cpu->memory.activeRegion = memory->bios9; + cpu->memory.activeMask = DS9_SIZE_BIOS - 1; + } else { + cpu->memory.activeRegion = _deadbeef; + cpu->memory.activeMask = 0; + } + break; + default: + mLOG(DS_MEM, FATAL, "Jumped to invalid address: %08X", address); + return; + } +} + +uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value = 0; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + wait += 2; + *cycleCounter += wait; + } + // Unaligned 32-bit loads are "rotated" so they make some semblance of sense + int rotate = (address & 3) << 3; + return ROR(value, rotate); +} + +uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value = 0; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + wait += 2; + *cycleCounter += wait; + } + // Unaligned 16-bit loads are "unpredictable", TODO: See what DS does + int rotate = (address & 1) << 3; + return ROR(value, rotate); +} + +uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value = 0; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + wait += 2; + *cycleCounter += wait; + } + return value; +} + +void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } +} + +void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } +} + +void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + int wait = 0; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } +} + +uint32_t DS9LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { + struct DS* ds = (struct DS*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value; + int wait = 0; + + int i; + int offset = 4; + int popcount = 0; + if (direction & LSM_D) { + offset = -4; + popcount = popcount32(mask); + address -= (popcount << 2) - 4; + } + + if (direction & LSM_B) { + address += offset; + } + + uint32_t addressMisalign = address & 0x3; + address &= 0xFFFFFFFC; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + ++wait; + *cycleCounter += wait; + } + + if (direction & LSM_B) { + address -= offset; + } + + if (direction & LSM_D) { + address -= (popcount << 2) + 4; + } + + return address | addressMisalign; +} + + +uint32_t DS9StoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { + struct DS* ds = (struct ds*) cpu->master; + struct DSMemory* memory = &ds->memory; + uint32_t value; + int wait = 0; + + int i; + int offset = 4; + int popcount = 0; + if (direction & LSM_D) { + offset = -4; + popcount = popcount32(mask); + address -= (popcount << 2) - 4; + } + + if (direction & LSM_B) { + address += offset; + } + + uint32_t addressMisalign = address & 0x3; + address &= 0xFFFFFFFC; + + switch (address >> DS_BASE_OFFSET) { + default: + break; + } + + if (cycleCounter) { + *cycleCounter += wait; + } + + if (direction & LSM_B) { + address -= offset; + } + + if (direction & LSM_D) { + address -= (popcount << 2) + 4; + } + + return address | addressMisalign; +} + +int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) { + return wait; +} +
A src/ds/memory.h

@@ -0,0 +1,163 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef DS_MEMORY_H +#define DS_MEMORY_H + +#include "util/common.h" + +#include "arm/arm.h" +#include "core/log.h" + +enum DSMemoryRegion { + DS7_REGION_BIOS = 0x0, + DS9_REGION_ITCM = 0x0, + DS_REGION_RAM = 0x2, + DS_REGION_WORKING_RAM = 0x3, + DS_REGION_IO = 0x4, + DS9_REGION_PALETTE_RAM = 0x5, + DS_REGION_VRAM = 0x6, + DS9_REGION_OAM = 0x7, + DS_REGION_SLOT2 = 0x8, + DS_REGION_SLOT2_EX = 0x9, + DS_REGION_SLOT2_SRAM = 0xA, + DS9_REGION_BIOS = 0xFF, +}; + +enum DSMemoryBase { + DS7_BASE_BIOS = 0x00000000, + DS9_BASE_ITCM = 0x00000000, + DS_BASE_RAM = 0x02000000, + DS_BASE_WORKING_RAM = 0x03000000, + DS_BASE_IO = 0x04000000, + DS9_BASE_PALETTE_RAM = 0x05000000, + DS_BASE_VRAM = 0x06000000, + DS9_BASE_OAM = 0x07000000, + DS_BASE_SLOT2 = 0x08000000, + DS_BASE_SLOT2_EX = 0x09000000, + DS9_BASE_BIOS = 0xFFFF0000, +}; + +enum { + DS7_SIZE_BIOS = 0x00004000, + DS9_SIZE_BIOS = 0x00008000, + DS_SIZE_RAM = 0x00400000, + DS_SIZE_WORKING_RAM = 0x00008000, + DS9_SIZE_PALETTE_RAM = 0x00000800, + DS9_SIZE_OAM = 0x00000800, + DS_SIZE_SLOT2 = 0x02000000, + DS_SIZE_SLOT2_SRAM = 0x00010000, +}; + +enum { + DS_OFFSET_MASK = 0x00FFFFFF, + DS_BASE_OFFSET = 24 +}; + +enum DSDMAControl { + DS_DMA_INCREMENT = 0, + DS_DMA_DECREMENT = 1, + DS_DMA_FIXED = 2, + DS_DMA_INCREMENT_RELOAD = 3 +}; + +enum DSDMATiming { + DS_DMA_TIMING_NOW = 0, + DS_DMA_TIMING_VBLANK = 1, + DS_DMA_TIMING_HBLANK = 2, + DS7_DMA_TIMING_SLOT1 = 2, + DS_DMA_TIMING_DISPLAY_START = 3, + DS7_DMA_TIMING_CUSTOM = 3, + DS_DMA_TIMING_MEMORY_DISPLAY = 4, + DS9_DMA_TIMING_SLOT1 = 5, + DS_DMA_TIMING_SLOT2 = 6, + DS_DMA_TIMING_GEOM_FIFO = 7, +}; + +mLOG_DECLARE_CATEGORY(DS_MEM); + +DECL_BITFIELD(DSDMARegister, uint16_t); +DECL_BITS(DSDMARegister, DestControl, 5, 2); +DECL_BITS(DSDMARegister, SrcControl, 7, 2); +DECL_BIT(DSDMARegister, Repeat, 9); +DECL_BIT(DSDMARegister, Width, 10); +DECL_BITS(DSDMARegister, Timing7, 12, 2); +DECL_BITS(DSDMARegister, Timing9, 11, 3); +DECL_BIT(DSDMARegister, DoIRQ, 14); +DECL_BIT(DSDMARegister, Enable, 15); + +struct DSDMA { + DSDMARegister reg; + + uint32_t source; + uint32_t dest; + int32_t count; + uint32_t nextSource; + uint32_t nextDest; + int32_t nextCount; + int32_t nextEvent; +}; + +struct DSMemory { + uint32_t* bios7; + uint32_t* bios9; + uint32_t* ram; + uint32_t* wram; + uint32_t* rom; + + size_t romSize; + + char waitstatesSeq32[256]; + char waitstatesSeq16[256]; + char waitstatesNonseq32[256]; + char waitstatesNonseq16[256]; + char waitstatesPrefetchSeq32[16]; + char waitstatesPrefetchSeq16[16]; + char waitstatesPrefetchNonseq32[16]; + char waitstatesPrefetchNonseq16[16]; + int activeRegion7; + int activeRegion9; + + struct DSDMA dma7[4]; + struct DSDMA dma9[4]; + int activeDMA7; + int activeDMA9; + int32_t nextDMA; + int32_t eventDiff; +}; + +struct DS; +void DSMemoryInit(struct DS* ds); +void DSMemoryDeinit(struct DS* ds); + +void DSMemoryReset(struct DS* ds); + +uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint32_t DS7Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter); + +void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter); +void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter); +void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter); + +uint32_t DS7LoadMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); +uint32_t DS7StoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); + +uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint32_t DS9Load8(struct ARMCore* cpu, uint32_t address, int* cycleCounter); + +void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter); +void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter); +void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter); + +uint32_t DS9LoadMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); +uint32_t DS9StoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); + +#endif