diff --git a/include/mgba/internal/ds/gx.h b/include/mgba/internal/ds/gx.h index 03957be60..7795132de 100644 --- a/include/mgba/internal/ds/gx.h +++ b/include/mgba/internal/ds/gx.h @@ -143,7 +143,7 @@ struct DSGXRenderer { void (*invalidateTex)(struct DSGXRenderer* renderer, int slot); void (*setRAM)(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort); void (*drawScanline)(struct DSGXRenderer* renderer, int y); - void (*getScanline)(struct DSGXRenderer* renderer, int y, color_t** output); + void (*getScanline)(struct DSGXRenderer* renderer, int y, const color_t** output); uint16_t* tex[4]; uint16_t* texPal[6]; diff --git a/include/mgba/internal/ds/video.h b/include/mgba/internal/ds/video.h index 14aa68dd6..e0cff70ed 100644 --- a/include/mgba/internal/ds/video.h +++ b/include/mgba/internal/ds/video.h @@ -60,6 +60,18 @@ DECL_BITS(DSRegisterDISPCNT, ScreenBase, 27, 3); DECL_BIT(DSRegisterDISPCNT, BgExtPalette, 30); DECL_BIT(DSRegisterDISPCNT, ObjExtPalette, 31); +DECL_BITFIELD(DSRegisterDISPCAPCNT, uint32_t); +DECL_BITS(DSRegisterDISPCAPCNT, EVA, 0, 4); +DECL_BITS(DSRegisterDISPCAPCNT, EVB, 8, 4); +DECL_BITS(DSRegisterDISPCAPCNT, WriteBlock, 16, 2); +DECL_BITS(DSRegisterDISPCAPCNT, WriteOffset, 18, 2); +DECL_BITS(DSRegisterDISPCAPCNT, CaptureSize, 20, 2); +DECL_BIT(DSRegisterDISPCAPCNT, SourceA, 24); +DECL_BIT(DSRegisterDISPCAPCNT, SourceB, 25); +DECL_BITS(DSRegisterDISPCAPCNT, ReadOffset, 26, 2); +DECL_BITS(DSRegisterDISPCAPCNT, CaptureSource, 29, 2); +DECL_BIT(DSRegisterDISPCAPCNT, Enable, 31); + DECL_BITFIELD(DSRegisterPOWCNT1, uint16_t); // TODO DECL_BIT(DSRegisterPOWCNT1, Swap, 15); @@ -109,8 +121,8 @@ struct DSVideo { struct mTimingEvent event7; struct mTimingEvent event9; - // VCOUNT int vcount; + bool inCapture; uint16_t palette[1024]; uint16_t* vram; diff --git a/src/ds/gx.c b/src/ds/gx.c index b0a793793..e932a0514 100644 --- a/src/ds/gx.c +++ b/src/ds/gx.c @@ -19,7 +19,7 @@ static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer); static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot); static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort); static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y); -static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output); +static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output); static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry); @@ -1281,7 +1281,7 @@ static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y) // Nothing to do } -static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) { +static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) { UNUSED(renderer); UNUSED(y); *output = NULL; diff --git a/src/ds/gx/software.c b/src/ds/gx/software.c index bc82b87e5..e918530e5 100644 --- a/src/ds/gx/software.c +++ b/src/ds/gx/software.c @@ -20,7 +20,7 @@ static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer); static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot); static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort); static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y); -static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output); +static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output); static void _expandColor(uint16_t c15, uint8_t* r, uint8_t* g, uint8_t* b) { *r = ((c15 << 1) & 0x3E) | 1; @@ -503,7 +503,7 @@ static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int } } -static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) { +static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) { struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer; *output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y]; } diff --git a/src/ds/io.c b/src/ds/io.c index 7bc958d9c..c981c74b2 100644 --- a/src/ds/io.c +++ b/src/ds/io.c @@ -433,6 +433,14 @@ void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) { } else { uint16_t oldValue; switch (address) { + // Other video + case DS9_REG_DISPCAPCNT_LO: + value &= 0x1F1F; + break; + case DS9_REG_DISPCAPCNT_HI: + value &= 0xEF3F; + break; + // VRAM control case DS9_REG_VRAMCNT_A: case DS9_REG_VRAMCNT_C: diff --git a/src/ds/renderers/software.c b/src/ds/renderers/software.c index 83029de8d..4bfa0c1e9 100644 --- a/src/ds/renderers/software.c +++ b/src/ds/renderers/software.c @@ -384,7 +384,7 @@ static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* rend } if (TEST_LAYER_ENABLED(0)) { if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) { - color_t* scanline; + const color_t* scanline; gx->renderer->getScanline(gx->renderer, y, &scanline); uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; flags |= FLAG_TARGET_2 * softwareRenderer->bg[0].target2; diff --git a/src/ds/video.c b/src/ds/video.c index ab75406ec..1f7424a2f 100644 --- a/src/ds/video.c +++ b/src/ds/video.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -210,6 +211,65 @@ void DSVideoDeinit(struct DSVideo* video) { mappedMemoryFree(video->vram, DS_SIZE_VRAM); } +static void _performCapture(struct DSVideo* video, int y) { + DSRegisterDISPCAPCNT dispcap = video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_LO >> 1]; + dispcap |= video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] << 16; + // TODO: Check mode + int block = DSRegisterDISPCAPCNTGetWriteBlock(dispcap); + if (!video->p->memory.vramMode[block][4]) { + return; + } + uint16_t* vram = &video->vram[0x10000 * block + DSRegisterDISPCAPCNTGetWriteOffset(dispcap) * 0x4000]; + const color_t* pixelsA; + int width = DS_VIDEO_HORIZONTAL_PIXELS; + switch (DSRegisterDISPCAPCNTGetCaptureSize(dispcap)) { + case 0: + width = DS_VIDEO_HORIZONTAL_PIXELS / 2; + break; + case 1: + if (y >= 64) { + return; + } + case 2: + if (y >= 128) { + return; + } + default: + break; + } + + video->p->gx.renderer->getScanline(video->p->gx.renderer, y, &pixelsA); + /*if (DSRegisterDISPCAPCNTIsSourceA(dispcap)) { + // TODO: Process scanline regardless of output type + video->p->gx.renderer->getScanline(video->p->gx.renderer, y, &pixelsA); + } else { + size_t stride; + const void* pixels; + video->renderer->getPixels(video->renderer, &stride, &pixels); + pixelsA = &((const color_t*) pixels)[stride * y]; + }*/ + + uint16_t pixel; + int x; + // TODO: Blending + for (x = 0; x < width; ++x) { + color_t colorA = pixelsA[x]; +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + pixel = colorA & 0x1F; + pixel |= (colorA & 0xFFC0) >> 1; +#else + pixel = colorA; +#endif +#else + pixel = (colorA >> 9) & 0x7C00; + pixel |= (colorA >> 6) & 0x03E0; + pixel |= (colorA >> 3) & 0x001F; +#endif + STORE_16(pixel, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram); + } +} + void _startHdraw7(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct DSVideo* video = context; GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1]; @@ -285,9 +345,11 @@ void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) { switch (video->vcount) { case 0: DSFrameStarted(video->p); + video->inCapture = DSRegisterDISPCAPCNTIsEnable(video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] << 16); break; case DS_VIDEO_VERTICAL_PIXELS: video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat); + video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] = DSRegisterDISPCAPCNTClearEnable(video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] << 16) >> 16; if (video->frameskipCounter <= 0) { video->renderer->finishFrame(video->renderer); DSGXFlush(&video->p->gx); @@ -329,6 +391,9 @@ void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) { video->p->gx.renderer->drawScanline(video->p->gx.renderer, video->vcount + 48 - DS_VIDEO_VERTICAL_TOTAL_PIXELS); } } + if (video->inCapture) { + _performCapture(video, video->vcount); + } if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_HBLANK);