From e7407770cb84a61d7c519de2f21822b9d4e25d77 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 26 Dec 2017 16:35:15 -0500 Subject: [PATCH] GB Video: Improved window implementation --- include/mgba/internal/gb/renderers/software.h | 2 ++ src/gb/renderers/software.c | 36 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/mgba/internal/gb/renderers/software.h b/include/mgba/internal/gb/renderers/software.h index 038c660b8..abf437ab0 100644 --- a/include/mgba/internal/gb/renderers/software.h +++ b/include/mgba/internal/gb/renderers/software.h @@ -31,6 +31,8 @@ struct GBVideoSoftwareRenderer { uint8_t wy; uint8_t wx; uint8_t currentWy; + int lastY; + bool hasWindow; GBRegisterLCDC lcdc; enum GBModel model; diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 1a6688578..8ec3444e3 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -38,6 +38,10 @@ static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) { } } +static bool _inWindow(struct GBVideoSoftwareRenderer* renderer) { + return GBRegisterLCDCIsWindow(renderer->lcdc) && GB_VIDEO_HORIZONTAL_PIXELS + 7 > renderer->wx; +} + void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { renderer->d.init = GBVideoSoftwareRendererInit; renderer->d.deinit = GBVideoSoftwareRendererDeinit; @@ -65,6 +69,8 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G softwareRenderer->scx = 0; softwareRenderer->wy = 0; softwareRenderer->currentWy = 0; + softwareRenderer->lastY = 0; + softwareRenderer->hasWindow = false; softwareRenderer->wx = 0; softwareRenderer->model = model; } @@ -74,11 +80,31 @@ static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { UNUSED(softwareRenderer); } +static void GBVideoSoftwareRendererUpdateWindow(struct GBVideoSoftwareRenderer* renderer, bool before, bool after) { + if (renderer->lastY >= GB_VIDEO_VERTICAL_PIXELS || after == before) { + return; + } + if (renderer->lastY >= renderer->wy) { + if (!after) { + renderer->currentWy -= renderer->lastY; + renderer->hasWindow = true; + } else { + if (!renderer->hasWindow) { + renderer->currentWy = renderer->lastY + 1 - renderer->wy; + } else { + renderer->currentWy += renderer->lastY; + } + } + } +} + static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; + bool wasWindow = _inWindow(softwareRenderer); switch (address) { case REG_LCDC: softwareRenderer->lcdc = value; + GBVideoSoftwareRendererUpdateWindow(softwareRenderer, wasWindow, _inWindow(softwareRenderer)); break; case REG_SCY: softwareRenderer->scy = value; @@ -88,9 +114,11 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* break; case REG_WY: softwareRenderer->wy = value; + GBVideoSoftwareRendererUpdateWindow(softwareRenderer, wasWindow, _inWindow(softwareRenderer)); break; case REG_WX: softwareRenderer->wx = value; + GBVideoSoftwareRendererUpdateWindow(softwareRenderer, wasWindow, _inWindow(softwareRenderer)); break; } return value; @@ -134,6 +162,7 @@ static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, ui static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; + softwareRenderer->lastY = y; uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP]; if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; @@ -142,7 +171,8 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i memset(&softwareRenderer->row[startX], 0, endX - startX); } if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) { - if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) { + int wy = softwareRenderer->wy + softwareRenderer->currentWy; + if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy <= y && endX >= softwareRenderer->wx - 7) { if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y); } @@ -152,7 +182,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i maps += GB_SIZE_MAP; } if (!softwareRenderer->d.disableWIN) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, y - wy); } } else if (!softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y); @@ -201,7 +231,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) { _clearScreen(softwareRenderer); } + softwareRenderer->lastY = GB_VIDEO_VERTICAL_PIXELS; softwareRenderer->currentWy = 0; + softwareRenderer->hasWindow = false; } static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy) {