From 9b86abec09dc3f979b1e463fd42a76c9ce8f5273 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 16 Feb 2017 00:46:01 -0800 Subject: [PATCH] DS Video: VRAM configuration --- include/mgba/internal/ds/memory.h | 7 ++- include/mgba/internal/ds/video.h | 3 + src/ds/ds.c | 2 +- src/ds/io.c | 9 +++ src/ds/memory.c | 98 ++++++++++++++++++++++++++++--- src/ds/video.c | 98 +++++++++++++++++++++++++++++++ 6 files changed, 207 insertions(+), 10 deletions(-) diff --git a/include/mgba/internal/ds/memory.h b/include/mgba/internal/ds/memory.h index e20e15680..f426ab66c 100644 --- a/include/mgba/internal/ds/memory.h +++ b/include/mgba/internal/ds/memory.h @@ -65,7 +65,8 @@ enum { enum { DS_OFFSET_MASK = 0x00FFFFFF, - DS_BASE_OFFSET = 24 + DS_BASE_OFFSET = 24, + DS_VRAM_OFFSET = 14 }; mLOG_DECLARE_CATEGORY(DS_MEM); @@ -82,6 +83,10 @@ struct DSMemory { uint16_t io7[DS7_REG_MAX >> 1]; uint16_t io9[DS9_REG_MAX >> 1]; + uint16_t vramMirror[9][0x40]; + uint16_t vramMode[9][8]; + uint16_t* vramBank[9]; + size_t romSize; size_t wramSize7; size_t wramSize9; diff --git a/include/mgba/internal/ds/video.h b/include/mgba/internal/ds/video.h index 9e0ce6394..c156bd6da 100644 --- a/include/mgba/internal/ds/video.h +++ b/include/mgba/internal/ds/video.h @@ -52,6 +52,9 @@ void DSVideoDeinit(struct DSVideo* video); struct DSCommon; void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value); +struct DSMemory; +void DSVideoConfigureVRAM(struct DSMemory* memory, int index, uint8_t value); + CXX_GUARD_START #endif diff --git a/src/ds/ds.c b/src/ds/ds.c index d3b6b3075..b15584243 100644 --- a/src/ds/ds.c +++ b/src/ds/ds.c @@ -224,9 +224,9 @@ void DS9Reset(struct ARMCore* cpu) { struct DS* ds = (struct DS*) cpu->master; mTimingClear(&ds->ds9.timing); CircleBufferInit(&ds->ds9.fifo, 64); + DSVideoReset(&ds->video); DSDMAReset(&ds->ds9); DS9IOInit(ds); - DSVideoReset(&ds->video); ds->activeCpu = cpu; mTimingSchedule(&ds->ds9.timing, &ds->slice, SLICE_CYCLES); diff --git a/src/ds/io.c b/src/ds/io.c index 0dfece57a..4eb8c206d 100644 --- a/src/ds/io.c +++ b/src/ds/io.c @@ -263,6 +263,15 @@ void DS9IOInit(struct DS* ds) { void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) { switch (address) { + case DS9_REG_VRAMCNT_A: + case DS9_REG_VRAMCNT_C: + case DS9_REG_VRAMCNT_E: + case DS9_REG_VRAMCNT_G: + DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A + 1, value & 0xFF); + // Fall through + case DS9_REG_VRAMCNT_I: + DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value >> 8); + break; default: { uint32_t v2 = DSIOWrite(&ds->ds9, address, value); diff --git a/src/ds/memory.c b/src/ds/memory.c index 1f2f93206..bb4ba520f 100644 --- a/src/ds/memory.c +++ b/src/ds/memory.c @@ -16,10 +16,24 @@ mLOG_DEFINE_CATEGORY(DS_MEM, "DS Memory"); static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb +static const uint32_t _vramMask[9] = { + 0x1FFFF, + 0x1FFFF, + 0x1FFFF, + 0x1FFFF, + 0x0FFFF, + 0x03FFF, + 0x03FFF, + 0x07FFF, + 0x03FFF +}; + 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 unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset); + static const char DS7_BASE_WAITSTATES[16] = { 0, 0, 8, 0, 0, 0, 0, 0 }; static const char DS7_BASE_WAITSTATES_32[16] = { 0, 0, 9, 0, 0, 1, 1, 0 }; static const char DS7_BASE_WAITSTATES_SEQ[16] = { 0, 0, 1, 0, 0, 0, 0, 0 }; @@ -160,6 +174,16 @@ void DSMemoryReset(struct DS* ds) { ds->memory.wramSize7 = 0x8000; ds->memory.wramSize9 = 0; + DSVideoConfigureVRAM(&ds->memory, 0, 0); + DSVideoConfigureVRAM(&ds->memory, 1, 0); + DSVideoConfigureVRAM(&ds->memory, 2, 0); + DSVideoConfigureVRAM(&ds->memory, 3, 0); + DSVideoConfigureVRAM(&ds->memory, 4, 0); + DSVideoConfigureVRAM(&ds->memory, 5, 0); + DSVideoConfigureVRAM(&ds->memory, 6, 0); + DSVideoConfigureVRAM(&ds->memory, 7, 0); + DSVideoConfigureVRAM(&ds->memory, 8, 0); + if (!ds->memory.wram || !ds->memory.wram7 || !ds->memory.ram || !ds->memory.itcm || !ds->memory.dtcm) { DSMemoryDeinit(ds); mLOG(DS_MEM, FATAL, "Could not map memory"); @@ -277,7 +301,7 @@ uint32_t DS7Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address); case DS_REGION_IO: - value = DS7IORead(ds, address & 0x00FFFFFF); + value = DS7IORead(ds, address & DS_OFFSET_MASK); break; default: mLOG(DS_MEM, STUB, "Unimplemented DS7 Load16: %08X", address); @@ -346,7 +370,7 @@ void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value); break; case DS_REGION_IO: - DS7IOWrite32(ds, address & 0x00FFFFFF, value); + DS7IOWrite32(ds, address & DS_OFFSET_MASK, value); break; default: mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value); @@ -380,7 +404,7 @@ void DS7Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value); break; case DS_REGION_IO: - DS7IOWrite(ds, address & 0x00FFFFFF, value); + DS7IOWrite(ds, address & DS_OFFSET_MASK, value); break; default: mLOG(DS_MEM, STUB, "Unimplemented DS7 Store16: %08X:%04X", address, value); @@ -413,7 +437,7 @@ void DS7Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo } mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value); case DS_REGION_IO: - DS7IOWrite8(ds, address & 0x00FFFFFF, value); + DS7IOWrite8(ds, address & DS_OFFSET_MASK, value); break; default: mLOG(DS_MEM, STUB, "Unimplemented DS7 Store8: %08X:%02X", address, value); @@ -690,6 +714,18 @@ uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { case DS_REGION_IO: value = DS9IORead32(ds, address & 0x00FFFFFC); break; + case DS_REGION_VRAM: { + unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET); + int i = 0; + for (i = 0; i < 9; ++i) { + if (mask & (1 << i)) { + uint32_t newValue; + LOAD_32(newValue, address & _vramMask[i], memory->vramBank[i]); + value |= newValue; + } + } + break; + } case DS9_REGION_BIOS: // TODO: Fix undersized BIOS // TODO: Fix masking @@ -739,8 +775,20 @@ uint32_t DS9Load16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } mLOG(DS_MEM, STUB, "Unimplemented DS9 Load16: %08X", address); case DS_REGION_IO: - value = DS9IORead(ds, address & 0x00FFFFFF); + value = DS9IORead(ds, address & DS_OFFSET_MASK); break; + case DS_REGION_VRAM: { + unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET); + int i = 0; + for (i = 0; i < 9; ++i) { + if (mask & (1 << i)) { + uint32_t newValue; + LOAD_16(newValue, address & _vramMask[i], memory->vramBank[i]); + value |= newValue; + } + } + break; + } case DS9_REGION_BIOS: // TODO: Fix undersized BIOS // TODO: Fix masking @@ -836,8 +884,18 @@ void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value); break; case DS_REGION_IO: - DS9IOWrite32(ds, address & 0x00FFFFFF, value); + DS9IOWrite32(ds, address & DS_OFFSET_MASK, value); break; + case DS_REGION_VRAM: { + unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET); + int i = 0; + for (i = 0; i < 9; ++i) { + if (mask & (1 << i)) { + STORE_32(value, address & _vramMask[i], memory->vramBank[i]); + } + } + break; + } default: if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) { STORE_32(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm); @@ -879,8 +937,18 @@ void DS9Store16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle mLOG(DS_MEM, STUB, "Unimplemented DS9 Store16: %08X:%04X", address, value); break; case DS_REGION_IO: - DS9IOWrite(ds, address & 0x00FFFFFF, value); + DS9IOWrite(ds, address & DS_OFFSET_MASK, value); break; + case DS_REGION_VRAM: { + unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET); + int i = 0; + for (i = 0; i < 9; ++i) { + if (mask & (1 << i)) { + STORE_16(value, address & _vramMask[i], memory->vramBank[i]); + } + } + break; + } default: if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) { STORE_16(value, address & (DS9_SIZE_DTCM - 1), memory->dtcm); @@ -921,7 +989,7 @@ void DS9Store8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo } mLOG(DS_MEM, STUB, "Unimplemented DS9 Store8: %08X:%02X", address, value); case DS_REGION_IO: - DS9IOWrite8(ds, address & 0x00FFFFFF, value); + DS9IOWrite8(ds, address & DS_OFFSET_MASK, value); break; default: if ((address & ~(DS9_SIZE_DTCM - 1)) == memory->dtcmBase) { @@ -1069,3 +1137,17 @@ int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait) { return wait; } +static unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset) { + unsigned mask = 0; + offset &= 0x3FF; + mask |= memory->vramMirror[0][offset & 0x3F] & memory->vramMode[0][offset >> 7]; + mask |= memory->vramMirror[1][offset & 0x3F] & memory->vramMode[1][offset >> 7]; + mask |= memory->vramMirror[2][offset & 0x3F] & memory->vramMode[2][offset >> 7]; + mask |= memory->vramMirror[3][offset & 0x3F] & memory->vramMode[3][offset >> 7]; + mask |= memory->vramMirror[4][offset & 0x3F] & memory->vramMode[4][offset >> 7]; + mask |= memory->vramMirror[5][offset & 0x3F] & memory->vramMode[5][offset >> 7]; + mask |= memory->vramMirror[6][offset & 0x3F] & memory->vramMode[6][offset >> 7]; + mask |= memory->vramMirror[7][offset & 0x3F] & memory->vramMode[7][offset >> 7]; + mask |= memory->vramMirror[8][offset & 0x3F] & memory->vramMode[8][offset >> 7]; + return mask; +} diff --git a/src/ds/video.c b/src/ds/video.c index f74bbc2b5..6986df8f7 100644 --- a/src/ds/video.c +++ b/src/ds/video.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -18,6 +19,75 @@ static void _startHdraw7(struct mTiming*, void* context, uint32_t cyclesLate); static void _startHblank9(struct mTiming*, void* context, uint32_t cyclesLate); static void _startHdraw9(struct mTiming*, void* context, uint32_t cyclesLate); +static const uint32_t _vramSize[9] = { + 0x20000, + 0x20000, + 0x20000, + 0x20000, + 0x10000, + 0x04000, + 0x04000, + 0x08000, + 0x04000 +}; + +const struct DSVRAMBankInfo { + int base; + uint32_t mirrorSize; + int mode; + int offset[4]; +} _vramInfo[9][8] = { + { // A + { 0x000, 0x40, 4 }, // LCDC + { 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG + { 0x000, 0x10, 2, { 0x00, 0x08, 0x80, 0x80 } }, // A-OBJ + }, + { // B + { 0x008, 0x40, 4 }, // LCDC + { 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG + { 0x000, 0x10, 2, { 0x00, 0x08, 0x80, 0x80 } }, // A-OBJ + }, + { // C + { 0x010, 0x40, 4 }, // LCDC + { 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG + {}, + {}, + { 0x000, 0x08, 1 }, // B-BG + }, + { // D + { 0x018, 0x40, 4 }, // LCDC + { 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG + {}, + {}, + { 0x000, 0x08, 3 }, // B-OBJ + }, + { // E + { 0x020, 0x40, 4 }, // LCDC + { 0x000, 0x20, 0 }, // A-BG + { 0x000, 0x10, 2 }, // A-OBJ + }, + { // F + { 0x024, 0x40, 4 }, // LCDC + { 0x000, 0x20, 0, { 0x00, 0x01, 0x04, 0x05 } }, // A-BG + { 0x000, 0x10, 2, { 0x00, 0x01, 0x04, 0x05 } }, // A-OBJ + }, + { // G + { 0x025, 0x40, 4 }, // LCDC + { 0x000, 0x20, 0 }, // A-BG + { 0x000, 0x10, 2 }, // A-OBJ + }, + { // H + { 0x026, 0x40, 4 }, // LCDC + { 0x000, 0x04, 1 }, // B-BG + { 0x000, 0x10, 2 }, // A-OBJ + }, + { // I + { 0x028, 0x40, 4 }, // LCDC + { 0x002, 0x04, 1 }, // B-BG + { 0x000, 0x01, 3 }, // B-OBJ + }, +}; + void DSVideoInit(struct DSVideo* video) { video->vram = NULL; video->frameskip = 0; @@ -48,6 +118,16 @@ void DSVideoReset(struct DSVideo* video) { mappedMemoryFree(video->vram, DS_SIZE_VRAM); } video->vram = anonymousMemoryMap(DS_SIZE_VRAM); + + video->p->memory.vramBank[0] = &video->vram[0x00000]; + video->p->memory.vramBank[1] = &video->vram[0x10000]; + video->p->memory.vramBank[2] = &video->vram[0x20000]; + video->p->memory.vramBank[3] = &video->vram[0x30000]; + video->p->memory.vramBank[4] = &video->vram[0x40000]; + video->p->memory.vramBank[5] = &video->vram[0x48000]; + video->p->memory.vramBank[6] = &video->vram[0x4A000]; + video->p->memory.vramBank[7] = &video->vram[0x4C000]; + video->p->memory.vramBank[8] = &video->vram[0x50000]; } void DSVideoDeinit(struct DSVideo* video) { @@ -174,3 +254,21 @@ void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) { dscore->memory.io[DS_REG_DISPSTAT >> 1] |= value; // TODO: Does a VCounter IRQ trigger on write? } + +void DSVideoConfigureVRAM(struct DSMemory* memory, int index, uint8_t value) { + struct DSVRAMBankInfo info = _vramInfo[index][value & 0x7]; + memset(&memory->vramMirror[index], 0, sizeof(memory->vramMirror[index])); + memset(&memory->vramMode[index], 0, sizeof(memory->vramMode[index])); + if (!(value & 0x80)) { + return; + } + uint32_t size = _vramSize[index] >> DS_VRAM_OFFSET; + memory->vramMode[index][info.mode] = 0xFFFF; + uint32_t offset = info.base + info.offset[(value >> 3) & 3]; + uint32_t i, j; + for (j = offset; j < 0x40; j += info.mirrorSize) { + for (i = 0; i < size; ++i) { + memory->vramMirror[index][i + j] = 1 << index; + } + } +}