all repos — mgba @ 9b86abec09dc3f979b1e463fd42a76c9ce8f5273

mGBA Game Boy Advance Emulator

DS Video: VRAM configuration
Vicki Pfau vi@endrift.com
Thu, 16 Feb 2017 00:46:01 -0800
commit

9b86abec09dc3f979b1e463fd42a76c9ce8f5273

parent

bb83e78af97e3da21522a75c7349d6b52e86804a

M include/mgba/internal/ds/memory.hinclude/mgba/internal/ds/memory.h

@@ -65,7 +65,8 @@ };

enum { DS_OFFSET_MASK = 0x00FFFFFF, - DS_BASE_OFFSET = 24 + DS_BASE_OFFSET = 24, + DS_VRAM_OFFSET = 14 }; mLOG_DECLARE_CATEGORY(DS_MEM);

@@ -81,6 +82,10 @@ uint32_t* wram7;

uint32_t* rom; 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;
M include/mgba/internal/ds/video.hinclude/mgba/internal/ds/video.h

@@ -52,6 +52,9 @@

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
M src/ds/ds.csrc/ds/ds.c

@@ -224,9 +224,9 @@

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);
M src/ds/io.csrc/ds/io.c

@@ -263,6 +263,15 @@ }

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);
M src/ds/memory.csrc/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 @@ // TODO: Correct size

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 @@ break;

} 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 @@ }

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 @@ }

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 @@ break;

} 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 @@ break;

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 @@ break;

} 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 @@ }

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 @@ }

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 @@ break;

} 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; +}
M src/ds/video.csrc/ds/video.c

@@ -7,6 +7,7 @@ #include <mgba/internal/ds/video.h>

#include <mgba/core/sync.h> #include <mgba/internal/ds/ds.h> +#include <mgba/internal/ds/memory.h> #include <mgba/internal/gba/video.h> #include <mgba-util/memory.h>

@@ -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 @@ if (video->vram) {

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 @@ dscore->memory.io[DS_REG_DISPSTAT >> 1] &= 0x7;

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; + } + } +}