GBA Video: Mark scanlines as dirty if they need to be updated

This commit is contained in:
Vicki Pfau 2017-08-02 18:17:51 -07:00
parent f61c0ce02f
commit ec25074cec
4 changed files with 146 additions and 52 deletions

View File

@ -11,6 +11,7 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/video.h> #include <mgba/internal/gba/video.h>
struct GBAVideoSoftwareSprite { struct GBAVideoSoftwareSprite {
@ -157,6 +158,9 @@ struct GBAVideoSoftwareRenderer {
int oamMax; int oamMax;
struct GBAVideoSoftwareSprite sprites[128]; struct GBAVideoSoftwareSprite sprites[128];
uint32_t scanlineDirty[5];
uint16_t ioCache[REG_SOUND1CNT_LO];
int start; int start;
int end; int end;
}; };

View File

@ -258,6 +258,7 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s
struct GBACore* gbacore = (struct GBACore*) core; struct GBACore* gbacore = (struct GBACore*) core;
gbacore->renderer.outputBuffer = buffer; gbacore->renderer.outputBuffer = buffer;
gbacore->renderer.outputBufferStride = stride; 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) { static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {

View File

@ -683,27 +683,39 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
GBAIOWrite32(gba, address & (OFFSET_MASK - 3), value); GBAIOWrite32(gba, address & (OFFSET_MASK - 3), value);
#define STORE_PALETTE_RAM \ #define STORE_PALETTE_RAM \
STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \
gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ if (oldValue != value) { \
wait += waitstatesRegion[REGION_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), value); 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 \ #define STORE_VRAM \
if ((address & 0x0001FFFF) < SIZE_VRAM) { \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ if (oldValue != value) { \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ 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 { \ } else { \
STORE_32(value, address & 0x00017FFC, gba->video.vram); \ LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ if (oldValue != value) { \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ 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]; wait += waitstatesRegion[REGION_VRAM];
#define STORE_OAM \ #define STORE_OAM \
STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \ LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \ if (oldValue != value) { \
gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); 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 \ #define STORE_CART \
wait += waitstatesRegion[address >> BASE_OFFSET]; \ wait += waitstatesRegion[address >> BASE_OFFSET]; \
@ -726,6 +738,7 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
struct GBAMemory* memory = &gba->memory; struct GBAMemory* memory = &gba->memory;
int wait = 0; int wait = 0;
int32_t oldValue;
char* waitstatesRegion = memory->waitstatesNonseq32; char* waitstatesRegion = memory->waitstatesNonseq32;
switch (address >> BASE_OFFSET) { switch (address >> BASE_OFFSET) {
@ -777,6 +790,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
struct GBAMemory* memory = &gba->memory; struct GBAMemory* memory = &gba->memory;
int wait = 0; int wait = 0;
int16_t oldValue;
switch (address >> BASE_OFFSET) { switch (address >> BASE_OFFSET) {
case REGION_WORKING_RAM: case REGION_WORKING_RAM:
@ -790,21 +804,33 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
GBAIOWrite(gba, address & (OFFSET_MASK - 1), value); GBAIOWrite(gba, address & (OFFSET_MASK - 1), value);
break; break;
case REGION_PALETTE_RAM: case REGION_PALETTE_RAM:
STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette);
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); 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; break;
case REGION_VRAM: case REGION_VRAM:
if ((address & 0x0001FFFF) < SIZE_VRAM) { if ((address & 0x0001FFFF) < SIZE_VRAM) {
STORE_16(value, address & 0x0001FFFE, gba->video.vram); LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram);
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); if (value != oldValue) {
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
}
} else { } else {
STORE_16(value, address & 0x00017FFE, gba->video.vram); LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram);
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); if (value != oldValue) {
STORE_16(value, address & 0x00017FFE, gba->video.vram);
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE);
}
} }
break; break;
case REGION_OAM: case REGION_OAM:
STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw);
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); 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; break;
case REGION_CART0: case REGION_CART0:
if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) { 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* cycleCo
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
struct GBAMemory* memory = &gba->memory; struct GBAMemory* memory = &gba->memory;
int wait = 0; int wait = 0;
uint16_t oldValue;
switch (address >> BASE_OFFSET) { switch (address >> BASE_OFFSET) {
case REGION_WORKING_RAM: case REGION_WORKING_RAM:
@ -865,8 +892,11 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address);
break; break;
} }
gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8); oldValue = gba->video.renderer->vram[(address & 0x1FFFE) >> 1];
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); 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; break;
case REGION_OAM: case REGION_OAM:
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address); 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
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
struct GBAMemory* memory = &gba->memory; struct GBAMemory* memory = &gba->memory;
uint32_t value; uint32_t value;
uint32_t oldValue;
char* waitstatesRegion = memory->waitstatesSeq32; char* waitstatesRegion = memory->waitstatesSeq32;
int i; int i;

View File

@ -26,10 +26,6 @@ static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer,
static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer); static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value); 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_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
static void GBAVideoSoftwareRendererWriteBGX_HI(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); static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
@ -112,6 +108,9 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
softwareRenderer->mosaic = 0; softwareRenderer->mosaic = 0;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
memset(softwareRenderer->ioCache, 0, sizeof(softwareRenderer->ioCache));
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i]; struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
bg->index = i; bg->index = i;
@ -145,106 +144,162 @@ static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
if (softwareRenderer->ioCache[address >> 1] == value) {
return value;
}
switch (address) { switch (address) {
case REG_DISPCNT: case REG_DISPCNT:
softwareRenderer->dispcnt = value; softwareRenderer->dispcnt = value;
GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer); GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG0CNT: case REG_BG0CNT:
value &= 0xDFFF; value &= 0xDFFF;
GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value); GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
if (softwareRenderer->bg[0].enabled) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG1CNT: case REG_BG1CNT:
value &= 0xDFFF; value &= 0xDFFF;
GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value); GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
if (softwareRenderer->bg[1].enabled) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG2CNT: case REG_BG2CNT:
value &= 0xFFFF; value &= 0xFFFF;
GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value); GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
if (softwareRenderer->bg[2].enabled) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG3CNT: case REG_BG3CNT:
value &= 0xFFFF; value &= 0xFFFF;
GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value); GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
if (softwareRenderer->bg[3].enabled) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG0HOFS: case REG_BG0HOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[0].x = value; softwareRenderer->bg[0].x = value;
if (softwareRenderer->bg[0].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG0VOFS: case REG_BG0VOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[0].y = value; softwareRenderer->bg[0].y = value;
if (softwareRenderer->bg[0].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG1HOFS: case REG_BG1HOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[1].x = value; softwareRenderer->bg[1].x = value;
if (softwareRenderer->bg[1].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG1VOFS: case REG_BG1VOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[1].y = value; softwareRenderer->bg[1].y = value;
if (softwareRenderer->bg[1].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG2HOFS: case REG_BG2HOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[2].x = value; softwareRenderer->bg[2].x = value;
if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG2VOFS: case REG_BG2VOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[2].y = value; softwareRenderer->bg[2].y = value;
if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG3HOFS: case REG_BG3HOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[3].x = value; softwareRenderer->bg[3].x = value;
if (softwareRenderer->bg[3].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG3VOFS: case REG_BG3VOFS:
value &= 0x01FF; value &= 0x01FF;
softwareRenderer->bg[3].y = value; softwareRenderer->bg[3].y = value;
if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) {
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
}
break; break;
case REG_BG2PA: case REG_BG2PA:
GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value); softwareRenderer->bg[2].dx = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG2PB: case REG_BG2PB:
GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value); softwareRenderer->bg[2].dmx = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG2PC: case REG_BG2PC:
GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value); softwareRenderer->bg[2].dy = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG2PD: case REG_BG2PD:
GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value); softwareRenderer->bg[2].dmy = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG2X_LO: case REG_BG2X_LO:
GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value); GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG2X_HI: case REG_BG2X_HI:
GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value); GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG2Y_LO: case REG_BG2Y_LO:
GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value); GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG2Y_HI: case REG_BG2Y_HI:
GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value); GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3PA: case REG_BG3PA:
GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value); softwareRenderer->bg[3].dx = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3PB: case REG_BG3PB:
GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value); softwareRenderer->bg[3].dmx = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3PC: case REG_BG3PC:
GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value); softwareRenderer->bg[3].dy = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3PD: case REG_BG3PD:
GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value); softwareRenderer->bg[3].dmy = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3X_LO: case REG_BG3X_LO:
GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value); GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3X_HI: case REG_BG3X_HI:
GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value); GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3Y_LO: case REG_BG3Y_LO:
GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value); GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BG3Y_HI: case REG_BG3Y_HI:
GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value); GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BLDCNT: case REG_BLDCNT:
GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value); GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
@ -260,6 +315,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
softwareRenderer->bldb = 0x10; softwareRenderer->bldb = 0x10;
} }
value &= 0x1F1F; value &= 0x1F1F;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_BLDY: case REG_BLDY:
value &= 0x1F; value &= 0x1F;
@ -270,6 +326,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
softwareRenderer->bldy = value; softwareRenderer->bldy = value;
softwareRenderer->blendDirty = true; softwareRenderer->blendDirty = true;
} }
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_WIN0H: case REG_WIN0H:
softwareRenderer->winN[0].h.end = value; softwareRenderer->winN[0].h.end = value;
@ -283,6 +340,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
softwareRenderer->winN[0].h.start = VIDEO_HORIZONTAL_PIXELS; softwareRenderer->winN[0].h.start = VIDEO_HORIZONTAL_PIXELS;
} }
} }
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_WIN1H: case REG_WIN1H:
softwareRenderer->winN[1].h.end = value; softwareRenderer->winN[1].h.end = value;
@ -296,6 +354,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
softwareRenderer->winN[1].h.start = VIDEO_HORIZONTAL_PIXELS; softwareRenderer->winN[1].h.start = VIDEO_HORIZONTAL_PIXELS;
} }
} }
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_WIN0V: case REG_WIN0V:
softwareRenderer->winN[0].v.end = value; softwareRenderer->winN[0].v.end = value;
@ -309,6 +368,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS; softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS;
} }
} }
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_WIN1V: case REG_WIN1V:
softwareRenderer->winN[1].v.end = value; softwareRenderer->winN[1].v.end = value;
@ -322,19 +382,23 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS; softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS;
} }
} }
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_WININ: case REG_WININ:
value &= 0x3F3F; value &= 0x3F3F;
softwareRenderer->winN[0].control.packed = value; softwareRenderer->winN[0].control.packed = value;
softwareRenderer->winN[1].control.packed = value >> 8; softwareRenderer->winN[1].control.packed = value >> 8;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_WINOUT: case REG_WINOUT:
value &= 0x3F3F; value &= 0x3F3F;
softwareRenderer->winout.packed = value; softwareRenderer->winout.packed = value;
softwareRenderer->objwin.packed = value >> 8; softwareRenderer->objwin.packed = value >> 8;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_MOSAIC: case REG_MOSAIC:
softwareRenderer->mosaic = value; softwareRenderer->mosaic = value;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
break; break;
case REG_GREENSWP: case REG_GREENSWP:
mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address); mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);
@ -342,19 +406,23 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
default: default:
mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address); mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
} }
softwareRenderer->ioCache[address >> 1] = value;
return value; return value;
} }
static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
if (renderer->cache) { if (renderer->cache) {
mTileCacheWriteVRAM(renderer->cache, address); mTileCacheWriteVRAM(renderer->cache, address);
} }
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
} }
static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
softwareRenderer->oamDirty = 1;
UNUSED(oam); UNUSED(oam);
softwareRenderer->oamDirty = 1;
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
} }
static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
@ -369,6 +437,7 @@ static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* render
if (renderer->cache) { if (renderer->cache) {
mTileCacheWritePalette(renderer->cache, address); mTileCacheWritePalette(renderer->cache, address);
} }
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
} }
static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) { static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {
@ -472,6 +541,10 @@ static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
if (!(softwareRenderer->scanlineDirty[y >> 5] & (1 << (y & 0x1F)))) {
return;
}
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) { if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
int x; int x;
@ -587,6 +660,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
#else #else
memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row)); memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
#endif #endif
softwareRenderer->scanlineDirty[y >> 5] &= ~(1 << (y & 0x1F));
} }
static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) { static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
@ -636,22 +710,6 @@ static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer*
bg->size = GBARegisterBGCNTGetSize(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) { static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
bg->refx = (bg->refx & 0xFFFF0000) | value; bg->refx = (bg->refx & 0xFFFF0000) | value;
bg->sx = bg->refx; bg->sx = bg->refx;