all repos — mgba @ ec25074cec84c82e4105a6b84e8779b1cf3a937e

mGBA Game Boy Advance Emulator

GBA Video: Mark scanlines as dirty if they need to be updated
Vicki Pfau vi@endrift.com
Wed, 02 Aug 2017 18:17:51 -0700
commit

ec25074cec84c82e4105a6b84e8779b1cf3a937e

parent

f61c0ce02f47218f795803e0fc1562a35fd84411

M include/mgba/internal/gba/renderers/video-software.hinclude/mgba/internal/gba/renderers/video-software.h

@@ -11,6 +11,7 @@

CXX_GUARD_START #include <mgba/core/core.h> +#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/video.h> struct GBAVideoSoftwareSprite {

@@ -156,6 +157,9 @@

int oamDirty; int oamMax; struct GBAVideoSoftwareSprite sprites[128]; + + uint32_t scanlineDirty[5]; + uint16_t ioCache[REG_SOUND1CNT_LO]; int start; int end;
M src/gba/core.csrc/gba/core.c

@@ -258,6 +258,7 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {

struct GBACore* gbacore = (struct GBACore*) core; gbacore->renderer.outputBuffer = buffer; gbacore->renderer.outputBufferStride = stride; + memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty)); } static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
M src/gba/memory.csrc/gba/memory.c

@@ -683,27 +683,39 @@ #define STORE_IO \

GBAIOWrite32(gba, address & (OFFSET_MASK - 3), value); #define STORE_PALETTE_RAM \ - STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ - gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ - wait += waitstatesRegion[REGION_PALETTE_RAM]; \ - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); + LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ + if (oldValue != value) { \ + STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ + gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ + gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); \ + } \ + wait += waitstatesRegion[REGION_PALETTE_RAM]; #define STORE_VRAM \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ + LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \ + if (oldValue != value) { \ + STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ + } \ } else { \ - STORE_32(value, address & 0x00017FFC, gba->video.vram); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ + LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \ + if (oldValue != value) { \ + STORE_32(value, address & 0x00017FFC, gba->video.vram); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ + } \ } \ wait += waitstatesRegion[REGION_VRAM]; #define STORE_OAM \ - STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \ - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \ - gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); + LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \ + if (oldValue != value) { \ + STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \ + gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \ + gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); \ + } #define STORE_CART \ wait += waitstatesRegion[address >> BASE_OFFSET]; \

@@ -726,6 +738,7 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter) {

struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; int wait = 0; + int32_t oldValue; char* waitstatesRegion = memory->waitstatesNonseq32; switch (address >> BASE_OFFSET) {

@@ -777,6 +790,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter) {

struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; int wait = 0; + int16_t oldValue; switch (address >> BASE_OFFSET) { case REGION_WORKING_RAM:

@@ -790,21 +804,33 @@ case REGION_IO:

GBAIOWrite(gba, address & (OFFSET_MASK - 1), value); break; case REGION_PALETTE_RAM: - STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + if (oldValue != value) { + STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + } break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - STORE_16(value, address & 0x0001FFFE, gba->video.vram); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); + if (value != oldValue) { + STORE_16(value, address & 0x0001FFFE, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + } } else { - STORE_16(value, address & 0x00017FFE, gba->video.vram); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); + LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram); + if (value != oldValue) { + STORE_16(value, address & 0x00017FFE, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); + } } break; case REGION_OAM: - STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw); + if (value != oldValue) { + STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + } break; case REGION_CART0: if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) {

@@ -844,6 +870,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter) {

struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; int wait = 0; + uint16_t oldValue; switch (address >> BASE_OFFSET) { case REGION_WORKING_RAM:

@@ -865,8 +892,11 @@ // TODO: check BG mode

mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); break; } - gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + oldValue = gba->video.renderer->vram[(address & 0x1FFFE) >> 1]; + if (oldValue != (((uint8_t) value) | (value << 8))) { + gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + } break; case REGION_OAM: mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address);

@@ -1369,6 +1399,7 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) {

struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; uint32_t value; + uint32_t oldValue; char* waitstatesRegion = memory->waitstatesSeq32; int i;
M src/gba/renderers/video-software.csrc/gba/renderers/video-software.c

@@ -26,10 +26,6 @@ static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);

static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer); static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value); static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value); static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value); static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);

@@ -112,6 +108,9 @@ softwareRenderer->oamMax = 0;

softwareRenderer->mosaic = 0; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + memset(softwareRenderer->ioCache, 0, sizeof(softwareRenderer->ioCache)); + for (i = 0; i < 4; ++i) { struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i]; bg->index = i;

@@ -145,106 +144,162 @@ }

static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + if (softwareRenderer->ioCache[address >> 1] == value) { + return value; + } switch (address) { case REG_DISPCNT: softwareRenderer->dispcnt = value; GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG0CNT: value &= 0xDFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value); + if (softwareRenderer->bg[0].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG1CNT: value &= 0xDFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value); + if (softwareRenderer->bg[1].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2CNT: value &= 0xFFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value); + if (softwareRenderer->bg[2].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG3CNT: value &= 0xFFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value); + if (softwareRenderer->bg[3].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG0HOFS: value &= 0x01FF; softwareRenderer->bg[0].x = value; + if (softwareRenderer->bg[0].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG0VOFS: value &= 0x01FF; softwareRenderer->bg[0].y = value; + if (softwareRenderer->bg[0].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG1HOFS: value &= 0x01FF; softwareRenderer->bg[1].x = value; + if (softwareRenderer->bg[1].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG1VOFS: value &= 0x01FF; softwareRenderer->bg[1].y = value; + if (softwareRenderer->bg[1].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2HOFS: value &= 0x01FF; softwareRenderer->bg[2].x = value; + if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2VOFS: value &= 0x01FF; softwareRenderer->bg[2].y = value; + if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG3HOFS: value &= 0x01FF; softwareRenderer->bg[3].x = value; + if (softwareRenderer->bg[3].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG3VOFS: value &= 0x01FF; softwareRenderer->bg[3].y = value; + if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2PA: - GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2PB: - GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dmx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2PC: - GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2PD: - GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dmy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2X_LO: GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2X_HI: GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2Y_LO: GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2Y_HI: GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PA: - GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PB: - GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dmx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PC: - GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PD: - GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dmy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3X_LO: GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3X_HI: GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3Y_LO: GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3Y_HI: GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BLDCNT: GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);

@@ -260,6 +315,7 @@ if (softwareRenderer->bldb > 0x10) {

softwareRenderer->bldb = 0x10; } value &= 0x1F1F; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BLDY: value &= 0x1F;

@@ -270,6 +326,7 @@ if (softwareRenderer->bldy != value) {

softwareRenderer->bldy = value; softwareRenderer->blendDirty = true; } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN0H: softwareRenderer->winN[0].h.end = value;

@@ -283,6 +340,7 @@ if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS) {

softwareRenderer->winN[0].h.start = VIDEO_HORIZONTAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN1H: softwareRenderer->winN[1].h.end = value;

@@ -296,6 +354,7 @@ if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS) {

softwareRenderer->winN[1].h.start = VIDEO_HORIZONTAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN0V: softwareRenderer->winN[0].v.end = value;

@@ -309,6 +368,7 @@ if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS) {

softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN1V: softwareRenderer->winN[1].v.end = value;

@@ -322,19 +382,23 @@ if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS) {

softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WININ: value &= 0x3F3F; softwareRenderer->winN[0].control.packed = value; softwareRenderer->winN[1].control.packed = value >> 8; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WINOUT: value &= 0x3F3F; softwareRenderer->winout.packed = value; softwareRenderer->objwin.packed = value >> 8; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_MOSAIC: softwareRenderer->mosaic = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_GREENSWP: mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);

@@ -342,19 +406,23 @@ break;

default: mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address); } + softwareRenderer->ioCache[address >> 1] = value; return value; } static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { + struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; if (renderer->cache) { mTileCacheWriteVRAM(renderer->cache, address); } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); } static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + UNUSED(oam); softwareRenderer->oamDirty = 1; - UNUSED(oam); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); } static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {

@@ -369,6 +437,7 @@ }

if (renderer->cache) { mTileCacheWritePalette(renderer->cache, address); } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); } static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {

@@ -472,6 +541,10 @@

static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + if (!(softwareRenderer->scanlineDirty[y >> 5] & (1 << (y & 0x1F)))) { + return; + } + color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) { int x;

@@ -587,6 +660,7 @@ #endif

#else memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row)); #endif + softwareRenderer->scanlineDirty[y >> 5] &= ~(1 << (y & 0x1F)); } static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {

@@ -634,22 +708,6 @@ bg->multipalette = GBARegisterBGCNTGet256Color(value);

bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 11; bg->overflow = GBARegisterBGCNTGetOverflow(value); bg->size = GBARegisterBGCNTGetSize(value); -} - -static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dx = value; -} - -static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dmx = value; -} - -static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dy = value; -} - -static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dmy = value; } static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {