DS Video: Start hooking up GBA video
@@ -19,6 +19,9 @@
struct GBAVideoSoftwareRenderer engA; struct GBAVideoSoftwareRenderer engB; + DSRegisterDISPCNT dispcntA; + DSRegisterDISPCNT dispcntB; + color_t* outputBuffer; int outputBufferStride;
@@ -35,12 +35,32 @@ union GBAOAM oam[2];
uint16_t raw[1024]; }; +DECL_BITFIELD(DSRegisterDISPCNT, uint32_t); +DECL_BITS(DSRegisterDISPCNT, Mode, 0, 3); +DECL_BIT(DSRegisterDISPCNT, 3D, 3); +DECL_BIT(DSRegisterDISPCNT, TileObjMapping, 4); +DECL_BIT(DSRegisterDISPCNT, BitmapObj2D, 5); +DECL_BIT(DSRegisterDISPCNT, BitmapObjMapping, 6); +DECL_BIT(DSRegisterDISPCNT, ForcedBlank, 7); +DECL_BIT(DSRegisterDISPCNT, Bg0Enable, 8); +DECL_BIT(DSRegisterDISPCNT, Bg1Enable, 9); +DECL_BIT(DSRegisterDISPCNT, Bg2Enable, 10); +DECL_BIT(DSRegisterDISPCNT, Bg3Enable, 11); +DECL_BIT(DSRegisterDISPCNT, ObjEnable, 12); +DECL_BIT(DSRegisterDISPCNT, Win0Enable, 13); +DECL_BIT(DSRegisterDISPCNT, Win1Enable, 14); +DECL_BIT(DSRegisterDISPCNT, ObjwinEnable, 15); +DECL_BITS(DSRegisterDISPCNT, DispMode, 16, 2); +DECL_BITS(DSRegisterDISPCNT, VRAMBlock, 18, 2); +// TODO + struct DSVideoRenderer { void (*init)(struct DSVideoRenderer* renderer); void (*reset)(struct DSVideoRenderer* renderer); void (*deinit)(struct DSVideoRenderer* renderer); uint16_t (*writeVideoRegister)(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value); + void (*writePalette)(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value); void (*drawScanline)(struct DSVideoRenderer* renderer, int y); void (*finishFrame)(struct DSVideoRenderer* renderer);@@ -49,6 +69,10 @@ void (*putPixels)(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
uint16_t* palette; uint16_t* vram; + uint16_t* vramABG[32]; + uint16_t* vramAOBJ[32]; + uint16_t* vramBBG[32]; + uint16_t* vramBOBJ[32]; union DSOAM* oam; };@@ -64,6 +88,10 @@ int vcount;
uint16_t palette[1024]; uint16_t* vram; + uint16_t* vramABG[32]; + uint16_t* vramAOBJ[32]; + uint16_t* vramBBG[32]; + uint16_t* vramBOBJ[32]; union DSOAM oam; int32_t frameCounter;@@ -80,7 +108,7 @@ struct DSCommon;
void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value); struct DSMemory; -void DSVideoConfigureVRAM(struct DSMemory* memory, int index, uint8_t value); +void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value); CXX_GUARD_START
@@ -386,9 +386,9 @@ DS9IOWrite(ds, DS9_REG_VRAMCNT_G, 0x0300);
} void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) { - if (address <= DS9_REG_A_BLDY && (address > DS_REG_VCOUNT || address == DS9_REG_A_DISPCNT_LO || address == DS9_REG_B_DISPCNT_LO)) { + if (address <= DS9_REG_A_BLDY && (address > DS_REG_VCOUNT || address == DS9_REG_A_DISPCNT_LO || address == DS9_REG_A_DISPCNT_HI)) { value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value); - } else if (address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY && (address == DS9_REG_B_DISPCNT_LO || address == DS9_REG_B_DISPCNT_LO)) { + } else if (address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) { value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value); } else { switch (address) {@@ -397,18 +397,18 @@ case DS9_REG_VRAMCNT_A:
case DS9_REG_VRAMCNT_C: case DS9_REG_VRAMCNT_E: value &= 0x9F9F; - DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value & 0xFF); - DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A + 1, value >> 8); + DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF); + DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8); break; case DS9_REG_VRAMCNT_G: value &= 0x9F03; - DSVideoConfigureVRAM(&ds->memory, 6, value & 0xFF); + DSVideoConfigureVRAM(ds, 6, value & 0xFF); DSConfigureWRAM(&ds->memory, value >> 8); break; case DS9_REG_VRAMCNT_H: value &= 0x9F9F; - DSVideoConfigureVRAM(&ds->memory, 7, value & 0xFF); - DSVideoConfigureVRAM(&ds->memory, 8, value >> 8); + DSVideoConfigureVRAM(ds, 7, value & 0xFF); + DSVideoConfigureVRAM(ds, 8, value >> 8); break; case DS9_REG_EXMEMCNT:
@@ -181,15 +181,15 @@ ds->memory.slot2Owner = true;
ds->ds7.memory.slot1Access = true; ds->ds9.memory.slot1Access = false; - 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); + DSVideoConfigureVRAM(ds, 0, 0); + DSVideoConfigureVRAM(ds, 1, 0); + DSVideoConfigureVRAM(ds, 2, 0); + DSVideoConfigureVRAM(ds, 3, 0); + DSVideoConfigureVRAM(ds, 4, 0); + DSVideoConfigureVRAM(ds, 5, 0); + DSVideoConfigureVRAM(ds, 6, 0); + DSVideoConfigureVRAM(ds, 7, 0); + DSVideoConfigureVRAM(ds, 8, 0); DSConfigureWRAM(&ds->memory, 3); if (!ds->memory.wram || !ds->memory.wram7 || !ds->memory.ram || !ds->memory.itcm || !ds->memory.dtcm) {@@ -940,6 +940,8 @@ DS9IOWrite32(ds, address & DS_OFFSET_MASK, value);
break; case DS9_REGION_PALETTE_RAM: STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 4), ds->video.palette); + ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16); + ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value); break; case DS_REGION_VRAM: { unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);@@ -1006,6 +1008,7 @@ DS9IOWrite(ds, address & DS_OFFSET_MASK, value);
break; case DS9_REGION_PALETTE_RAM: STORE_16(value, address & (DS9_SIZE_PALETTE_RAM - 2), ds->video.palette); + ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 2), value); break; case DS_REGION_VRAM: { unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);@@ -1228,7 +1231,9 @@ mLOG(DS_MEM, STUB, "Unimplemented DS9 STM: %08X", address);
}); break; case DS9_REGION_PALETTE_RAM: - STM_LOOP(STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette)); + STM_LOOP(STORE_32(value, address & (DS9_SIZE_PALETTE_RAM - 1), ds->video.palette); + ds->video.renderer->writePalette(ds->video.renderer, (address & (DS9_SIZE_PALETTE_RAM - 4)) + 2, value >> 16); + ds->video.renderer->writePalette(ds->video.renderer, address & (DS9_SIZE_PALETTE_RAM - 4), value)); break; case DS_REGION_VRAM: STM_LOOP(unsigned mask = _selectVRAM(memory, address >> DS_VRAM_OFFSET);
@@ -5,10 +5,14 @@ * 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 <mgba/internal/ds/renderers/software.h> +#include <mgba/internal/arm/macros.h> +#include <mgba/internal/ds/io.h> + static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer); static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer); static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer); static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value); +static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value); static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y); static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer); static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);@@ -19,6 +23,7 @@ renderer->d.init = DSVideoSoftwareRendererInit;
renderer->d.reset = DSVideoSoftwareRendererReset; renderer->d.deinit = DSVideoSoftwareRendererDeinit; renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister; + renderer->d.writePalette = DSVideoSoftwareRendererWritePalette; renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline; renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame; renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;@@ -55,17 +60,155 @@ softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
} static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) { - mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value); + struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer; + if (address <= DS9_REG_A_BLDY) { + value = softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value); + } else if (address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) { + value = softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value); + } else { + mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value); + } + switch (address) { + case DS9_REG_A_DISPCNT_LO: + softwareRenderer->dispcntA &= 0xFFFF0000; + softwareRenderer->dispcntA |= value; + break; + case DS9_REG_A_DISPCNT_HI: + softwareRenderer->dispcntA &= 0x0000FFFF; + softwareRenderer->dispcntA |= value << 16; + break; + case DS9_REG_B_DISPCNT_LO: + softwareRenderer->dispcntB &= 0xFFFF0000; + softwareRenderer->dispcntB |= value; + break; + case DS9_REG_B_DISPCNT_HI: + softwareRenderer->dispcntB &= 0x0000FFFF; + softwareRenderer->dispcntB |= value << 16; + break; + } return value; } +static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) { + struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer; + if (address < 0x200) { + softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x1FF, value); + } else { + softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x1FF, value); + } +} + +static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) { + memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG)); + memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ)); + color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y]; + + int x; + switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) { + case 0: + for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { + row[x] = GBA_COLOR_WHITE; + } + return; + case 1: + softwareRenderer->engA.d.drawScanline(&softwareRenderer->engA.d, y); + return; + case 2: { + uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)]; + for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { + color_t color; + LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram); +#ifndef COLOR_16_BIT + unsigned color32 = 0; + color32 |= (color << 9) & 0xF80000; + color32 |= (color << 3) & 0xF8; + color32 |= (color << 6) & 0xF800; + color32 |= (color32 >> 5) & 0x070707; + color = color32; +#elif COLOR_5_6_5 + uint16_t color16 = 0; + color16 |= (color & 0x001F) << 11; + color16 |= (color & 0x03E0) << 1; + color16 |= (color & 0x7C00) >> 10; + color = color16; +#endif + softwareRenderer->row[x] = color; + } + break; + } + case 3: + break; + } + +#ifdef COLOR_16_BIT +#if defined(__ARM_NEON) && !defined(__APPLE__) + _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS); +#else + for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { + row[x] = softwareRenderer->row[x]; + } +#endif +#else + memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row)); +#endif +} + +static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) { + memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG)); + memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ)); + color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y]; + + int x; + switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) { + case 0: + for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { + row[x] = GBA_COLOR_WHITE; + } + return; + case 1: + softwareRenderer->engB.d.drawScanline(&softwareRenderer->engB.d, y); + return; + } + +#ifdef COLOR_16_BIT +#if defined(__ARM_NEON) && !defined(__APPLE__) + _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS); +#else + for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { + row[x] = softwareRenderer->row[x]; + } +#endif +#else + memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row)); +#endif +} + static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) { + struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer; + softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer; + softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS]; + softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride; + softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride; + + + _drawScanlineA(softwareRenderer, y); + _drawScanlineB(softwareRenderer, y); } static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) { + struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer; + softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d); + softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d); } static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) { + struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer; +#ifdef COLOR_16_BIT +#error Not yet supported +#else + *stride = softwareRenderer->outputBufferStride; + *pixels = softwareRenderer->outputBuffer; +#endif } static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
@@ -18,6 +18,7 @@ static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer);
static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer); static void DSVideoDummyRendererDeinit(struct DSVideoRenderer* renderer); static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value); +static void DSVideoDummyRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value); static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y); static void DSVideoDummyRendererFinishFrame(struct DSVideoRenderer* renderer); static void DSVideoDummyRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);@@ -102,6 +103,7 @@ .init = DSVideoDummyRendererInit,
.reset = DSVideoDummyRendererReset, .deinit = DSVideoDummyRendererDeinit, .writeVideoRegister = DSVideoDummyRendererWriteVideoRegister, + .writePalette = DSVideoDummyRendererWritePalette, .drawScanline = DSVideoDummyRendererDrawScanline, .finishFrame = DSVideoDummyRendererFinishFrame, .getPixels = DSVideoDummyRendererGetPixels,@@ -160,6 +162,10 @@ video->renderer->deinit(video->renderer);
video->renderer = renderer; renderer->palette = video->palette; renderer->vram = video->vram; + memcpy(renderer->vramABG, video->vramABG, sizeof(renderer->vramABG)); + memcpy(renderer->vramAOBJ, video->vramAOBJ, sizeof(renderer->vramAOBJ)); + memcpy(renderer->vramBBG, video->vramBBG, sizeof(renderer->vramBBG)); + memcpy(renderer->vramBOBJ, video->vramBOBJ, sizeof(renderer->vramBOBJ)); renderer->oam = &video->oam; video->renderer->init(video->renderer); }@@ -292,7 +298,8 @@ 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) { +void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value) { + struct DSMemory* memory = &ds->memory; 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]));@@ -308,6 +315,32 @@ for (i = 0; i < size; ++i) {
memory->vramMirror[index][i + j] = 1 << index; } } + switch (info.mode) { + case 0: + for (i = 0; i < size; ++i) { + ds->video.vramABG[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]; + ds->video.renderer->vramABG[offset + i] = ds->video.vramABG[offset + i]; + } + break; + case 1: + for (i = 0; i < size; ++i) { + ds->video.vramBBG[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]; + ds->video.renderer->vramBBG[offset + i] = ds->video.vramBBG[offset + i]; + } + break; + case 2: + for (i = 0; i < size; ++i) { + ds->video.vramAOBJ[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]; + ds->video.renderer->vramAOBJ[offset + i] = ds->video.vramAOBJ[offset + i]; + } + break; + case 3: + for (i = 0; i < size; ++i) { + ds->video.vramBOBJ[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]; + ds->video.renderer->vramBOBJ[offset + i] = ds->video.vramBOBJ[offset + i]; + } + break; + } } static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer) {@@ -328,6 +361,12 @@
static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) { UNUSED(renderer); return value; +} + +static void DSVideoDummyRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) { + UNUSED(value); + UNUSED(address); + UNUSED(value); } static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
@@ -108,7 +108,7 @@ _savestate = VFileOpen(perfOpts.savestate, O_RDONLY);
free(perfOpts.savestate); } - _outputBuffer = malloc(256 * 256 * 4); + _outputBuffer = malloc(256 * 384 * 4); if (perfOpts.csv) { puts("game_code,frames,duration,renderer"); }