GBA Video: Implement green swap (fixes #1609)

This commit is contained in:
Vicki Pfau 2020-08-31 22:28:19 -07:00
parent 6a97b1dbf5
commit c825c57281
5 changed files with 45 additions and 11 deletions

View File

@ -42,6 +42,7 @@ Emulation fixes:
- GBA Video: Fix Hblank timing - GBA Video: Fix Hblank timing
- GBA Video: Invalidate map cache when modifying BGCNT (fixes mgba.io/i/1846) - GBA Video: Invalidate map cache when modifying BGCNT (fixes mgba.io/i/1846)
- GBA Video: Don't draw sprites using unmapped VRAM in GL renderer (fixes mgba.io/i/1865) - GBA Video: Don't draw sprites using unmapped VRAM in GL renderer (fixes mgba.io/i/1865)
- GBA Video: Implement green swap (fixes mgba.io/i/1609)
- SM83: Emulate HALT bug - SM83: Emulate HALT bug
Other fixes: Other fixes:
- 3DS: Redo video sync to be more precise - 3DS: Redo video sync to be more precise

View File

@ -36,6 +36,23 @@ typedef uint32_t color_t;
#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9)) #define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9))
#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19)) #define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19))
#ifndef COLOR_16_BIT
#define M_COLOR_RED 0x000000FF
#define M_COLOR_GREEN 0x0000FF00
#define M_COLOR_BLUE 0x00FF0000
#define M_COLOR_ALPHA 0xFF000000
#elif defined(COLOR_5_6_5)
#define M_COLOR_RED 0x001F
#define M_COLOR_GREEN 0x07E0
#define M_COLOR_BLUE 0xF800
#define M_COLOR_ALPHA 0x0000
#else
#define M_COLOR_RED 0x001F
#define M_COLOR_GREEN 0x03E0
#define M_COLOR_BLUE 0x7C00
#define M_COLOR_ALPHA 0x1000
#endif
#ifndef PYCPARSE #ifndef PYCPARSE
static inline color_t mColorFrom555(uint16_t value) { static inline color_t mColorFrom555(uint16_t value) {
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT

View File

@ -114,6 +114,7 @@ struct GBAVideoSoftwareRenderer {
uint16_t bldy; uint16_t bldy;
GBAMosaicControl mosaic; GBAMosaicControl mosaic;
bool greenswap;
struct WindowN { struct WindowN {
struct GBAVideoWindowRegion h; struct GBAVideoWindowRegion h;

View File

@ -341,7 +341,7 @@ void GBAIOInit(struct GBA* gba) {
} }
void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
if (address < REG_SOUND1CNT_LO && (address > REG_VCOUNT || address == REG_DISPCNT)) { if (address < REG_SOUND1CNT_LO && (address > REG_VCOUNT || address < REG_DISPSTAT)) {
value = gba->video.renderer->writeVideoRegister(gba->video.renderer, address, value); value = gba->video.renderer->writeVideoRegister(gba->video.renderer, address, value);
} else { } else {
switch (address) { switch (address) {

View File

@ -122,6 +122,7 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
softwareRenderer->oamMax = 0; softwareRenderer->oamMax = 0;
softwareRenderer->mosaic = 0; softwareRenderer->mosaic = 0;
softwareRenderer->greenswap = false;
softwareRenderer->nextY = 0; softwareRenderer->nextY = 0;
softwareRenderer->objOffsetX = 0; softwareRenderer->objOffsetX = 0;
@ -179,6 +180,9 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
softwareRenderer->dispcnt = value; softwareRenderer->dispcnt = value;
GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer); GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
break; break;
case REG_GREENSWP:
softwareRenderer->greenswap = value & 1;
break;
case REG_BG0CNT: case REG_BG0CNT:
value &= 0xDFFF; value &= 0xDFFF;
GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value); GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
@ -389,9 +393,6 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
case REG_MOSAIC: case REG_MOSAIC:
softwareRenderer->mosaic = value; softwareRenderer->mosaic = value;
break; break;
case REG_GREENSWP:
mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);
break;
default: default:
mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address); mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
} }
@ -675,16 +676,30 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
} }
} }
if (softwareRenderer->greenswap) {
for (x = 0; x < GBA_VIDEO_HORIZONTAL_PIXELS; x += 4) {
row[x] = softwareRenderer->row[x] & (M_COLOR_RED | M_COLOR_BLUE);
row[x] |= softwareRenderer->row[x + 1] & M_COLOR_GREEN;
row[x + 1] = softwareRenderer->row[x + 1] & (M_COLOR_RED | M_COLOR_BLUE);
row[x + 1] |= softwareRenderer->row[x] & M_COLOR_GREEN;
row[x + 2] = softwareRenderer->row[x + 2] & (M_COLOR_RED | M_COLOR_BLUE);
row[x + 2] |= softwareRenderer->row[x + 3] & M_COLOR_GREEN;
row[x + 3] = softwareRenderer->row[x + 3] & (M_COLOR_RED | M_COLOR_BLUE);
row[x + 3] |= softwareRenderer->row[x + 2] & M_COLOR_GREEN;
}
} else {
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
for (x = 0; x < GBA_VIDEO_HORIZONTAL_PIXELS; x += 4) { for (x = 0; x < GBA_VIDEO_HORIZONTAL_PIXELS; x += 4) {
row[x] = softwareRenderer->row[x]; row[x] = softwareRenderer->row[x];
row[x + 1] = softwareRenderer->row[x + 1]; row[x + 1] = softwareRenderer->row[x + 1];
row[x + 2] = softwareRenderer->row[x + 2]; row[x + 2] = softwareRenderer->row[x + 2];
row[x + 3] = softwareRenderer->row[x + 3]; row[x + 3] = softwareRenderer->row[x + 3];
} }
#else #else
memcpy(row, softwareRenderer->row, GBA_VIDEO_HORIZONTAL_PIXELS * sizeof(*row)); memcpy(row, softwareRenderer->row, GBA_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
#endif #endif
}
} }
static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) { static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {