DS Video: Add extended BG palettes

This commit is contained in:
Vicki Pfau 2017-02-24 19:00:50 -08:00
parent 242e3c6ec5
commit a9ca1221f2
9 changed files with 595 additions and 67 deletions

View File

@ -27,6 +27,11 @@ struct DSVideoSoftwareRenderer {
int outputBufferStride;
uint32_t row[DS_VIDEO_HORIZONTAL_PIXELS];
color_t extPaletteA[16384];
color_t extPaletteB[16384];
color_t variantPaletteA[16384];
color_t variantPaletteB[16384];
};
void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer);

View File

@ -57,11 +57,15 @@ DECL_BITS(DSRegisterDISPCNT, TileBoundary, 20, 2);
DECL_BITS(DSRegisterDISPCNT, CharBase, 24, 3);
DECL_BITS(DSRegisterDISPCNT, ScreenBase, 27, 3);
// TODO
DECL_BIT(DSRegisterDISPCNT, BgExtPalette, 30);
DECL_BIT(DSRegisterDISPCNT, ObjExtPalette, 31);
DECL_BITFIELD(DSRegisterPOWCNT1, uint16_t);
// TODO
DECL_BIT(DSRegisterPOWCNT1, Swap, 15);
DECL_BIT(GBARegisterBGCNT, ExtPaletteSlot, 13);
struct DSVideoRenderer {
void (*init)(struct DSVideoRenderer* renderer);
void (*reset)(struct DSVideoRenderer* renderer);
@ -70,6 +74,7 @@ struct DSVideoRenderer {
uint16_t (*writeVideoRegister)(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
void (*writePalette)(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
void (*writeOAM)(struct DSVideoRenderer* renderer, uint32_t oam);
void (*invalidateExtPal)(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
void (*drawScanline)(struct DSVideoRenderer* renderer, int y);
void (*finishFrame)(struct DSVideoRenderer* renderer);
@ -80,8 +85,12 @@ struct DSVideoRenderer {
uint16_t* vram;
uint16_t* vramABG[32];
uint16_t* vramAOBJ[32];
uint16_t* vramABGExtPal[4];
uint16_t* vramAOBJExtPal[4];
uint16_t* vramBBG[32];
uint16_t* vramBOBJ[32];
uint16_t* vramBBGExtPal[4];
uint16_t* vramBOBJExtPal[4];
union DSOAM* oam;
};
@ -99,8 +108,12 @@ struct DSVideo {
uint16_t* vram;
uint16_t* vramABG[32];
uint16_t* vramAOBJ[32];
uint16_t* vramABGExtPal[4];
uint16_t* vramAOBJExtPal[4];
uint16_t* vramBBG[32];
uint16_t* vramBOBJ[32];
uint16_t* vramBBGExtPal[4];
uint16_t* vramBOBJExtPal[4];
union DSOAM oam;
int32_t frameCounter;
@ -117,7 +130,7 @@ struct DSCommon;
void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value);
struct DSMemory;
void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value);
void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value, uint8_t oldValue);
CXX_GUARD_START

View File

@ -42,6 +42,8 @@ struct GBAVideoSoftwareBackground {
int16_t dmy;
int32_t sx;
int32_t sy;
color_t* extPalette;
color_t* variantPalette;
};
enum BlendEffect {
@ -116,7 +118,7 @@ struct GBAVideoSoftwareRenderer {
uint32_t* temporaryBuffer;
GBARegisterDISPCNT dispcnt;
uint32_t dispcnt;
uint32_t row[256];
uint32_t spriteLayer[256];

View File

@ -424,24 +424,28 @@ void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
} else if (address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) {
value = ds->video.renderer->writeVideoRegister(ds->video.renderer, address, value);
} else {
uint16_t oldValue;
switch (address) {
// VRAM control
case DS9_REG_VRAMCNT_A:
case DS9_REG_VRAMCNT_C:
case DS9_REG_VRAMCNT_E:
oldValue = ds->memory.io9[address >> 1];
value &= 0x9F9F;
DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF);
DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8);
DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A, value & 0xFF, oldValue & 0xFF);
DSVideoConfigureVRAM(ds, address - DS9_REG_VRAMCNT_A + 1, value >> 8, oldValue >> 8);
break;
case DS9_REG_VRAMCNT_G:
oldValue = ds->memory.io9[address >> 1];
value &= 0x9F03;
DSVideoConfigureVRAM(ds, 6, value & 0xFF);
DSVideoConfigureVRAM(ds, 6, value & 0xFF, oldValue & 0xFF);
DSConfigureWRAM(&ds->memory, value >> 8);
break;
case DS9_REG_VRAMCNT_H:
oldValue = ds->memory.io9[address >> 1];
value &= 0x9F9F;
DSVideoConfigureVRAM(ds, 7, value & 0xFF);
DSVideoConfigureVRAM(ds, 8, value >> 8);
DSVideoConfigureVRAM(ds, 7, value & 0xFF, oldValue & 0xFF);
DSVideoConfigureVRAM(ds, 8, value >> 8, oldValue >> 8);
break;
case DS9_REG_EXMEMCNT:

View File

@ -202,15 +202,15 @@ void DSMemoryReset(struct DS* ds) {
DSSPIReset(ds);
DSSlot1Reset(ds);
DSVideoConfigureVRAM(ds, 0, 0);
DSVideoConfigureVRAM(ds, 1, 0);
DSVideoConfigureVRAM(ds, 2, 0);
DSVideoConfigureVRAM(ds, 3, 0);
DSVideoConfigureVRAM(ds, 4, 0);
DSVideoConfigureVRAM(ds, 5, 0);
DSVideoConfigureVRAM(ds, 6, 0);
DSVideoConfigureVRAM(ds, 7, 0);
DSVideoConfigureVRAM(ds, 8, 0);
DSVideoConfigureVRAM(ds, 0, 0, 1);
DSVideoConfigureVRAM(ds, 1, 0, 1);
DSVideoConfigureVRAM(ds, 2, 0, 1);
DSVideoConfigureVRAM(ds, 3, 0, 1);
DSVideoConfigureVRAM(ds, 4, 0, 1);
DSVideoConfigureVRAM(ds, 5, 0, 1);
DSVideoConfigureVRAM(ds, 6, 0, 1);
DSVideoConfigureVRAM(ds, 7, 0, 1);
DSVideoConfigureVRAM(ds, 8, 0, 1);
DSConfigureWRAM(&ds->memory, 3);
if (!ds->memory.wram || !ds->memory.wram7 || !ds->memory.ram || !ds->memory.itcm || !ds->memory.dtcm) {

View File

@ -4,6 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/ds/renderers/software.h>
#include "gba/renderers/software-private.h"
#include <mgba/internal/arm/macros.h>
#include <mgba/internal/ds/io.h>
@ -14,11 +15,61 @@ static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool engB, int slot) {
color_t* palette;
color_t* variantPalette;
struct GBAVideoSoftwareRenderer* softwareRenderer;
uint16_t* vram;
if (!engB) {
palette = &renderer->extPaletteA[slot * 4096];
variantPalette = &renderer->variantPaletteA[slot * 4096];
softwareRenderer = &renderer->engA;
vram = renderer->d.vramABGExtPal[slot];
} else {
palette = &renderer->extPaletteB[slot * 4096];
variantPalette = &renderer->variantPaletteB[slot * 4096];
softwareRenderer = &renderer->engB;
vram = renderer->d.vramBBGExtPal[slot];
}
if (!vram) {
return false;
}
int i;
for (i = 0; i < 4096; ++i) {
uint16_t value = vram[i];
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
unsigned color = 0;
color |= (value & 0x001F) << 11;
color |= (value & 0x03E0) << 1;
color |= (value & 0x7C00) >> 10;
#else
unsigned color = value;
#endif
#else
unsigned color = 0;
color |= (value << 3) & 0xF8;
color |= (value << 6) & 0xF800;
color |= (value << 9) & 0xF80000;
color |= (color >> 5) & 0x070707;
#endif
palette[i] = color;
if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
variantPalette[i] = _brighten(color, softwareRenderer->bldy);
} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
variantPalette[i] = _darken(color, softwareRenderer->bldy);
}
}
return true;
}
void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
renderer->d.init = DSVideoSoftwareRendererInit;
renderer->d.reset = DSVideoSoftwareRendererReset;
@ -26,6 +77,7 @@ void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
@ -75,6 +127,25 @@ static void DSVideoSoftwareRendererUpdateDISPCNTA(struct DSVideoSoftwareRenderer
fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
}
softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
softwareRenderer->engA.dispcnt |= softwareRenderer->dispcntA & 0xFFFF000;
if (DSRegisterDISPCNTIsBgExtPalette(softwareRenderer->dispcntA)) {
int i;
for (i = 0; i < 4; ++i) {
// TODO: Regenerate on change
int slot = i;
if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->engA.bg[i].control)) {
slot += 2;
}
if (softwareRenderer->engA.bg[i].extPalette != &softwareRenderer->extPaletteA[slot * 4096] && _regenerateExtPalette(softwareRenderer, false, slot)) {
softwareRenderer->engA.bg[i].extPalette = &softwareRenderer->extPaletteA[slot * 4096];
}
}
} else {
softwareRenderer->engA.bg[0].extPalette = NULL;
softwareRenderer->engA.bg[1].extPalette = NULL;
softwareRenderer->engA.bg[2].extPalette = NULL;
softwareRenderer->engA.bg[3].extPalette = NULL;
}
uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG0CNT, softwareRenderer->engA.bg[0].control);
@ -96,6 +167,7 @@ static void DSVideoSoftwareRendererUpdateDISPCNTA(struct DSVideoSoftwareRenderer
}
static void DSVideoSoftwareRendererUpdateDISPCNTB(struct DSVideoSoftwareRenderer* softwareRenderer) {
// TODO: Share code with DISPCNTA
uint16_t fakeDispcnt = softwareRenderer->dispcntB & 0xFF87;
if (!DSRegisterDISPCNTIsTileObjMapping(softwareRenderer->dispcntB)) {
softwareRenderer->engB.tileStride = 0x20;
@ -104,6 +176,25 @@ static void DSVideoSoftwareRendererUpdateDISPCNTB(struct DSVideoSoftwareRenderer
fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
}
softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
softwareRenderer->engB.dispcnt |= softwareRenderer->dispcntB & 0xFFFF000;
if (DSRegisterDISPCNTIsBgExtPalette(softwareRenderer->dispcntB)) {
int i;
for (i = 0; i < 4; ++i) {
// TODO: Regenerate on change
int slot = i;
if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->engB.bg[i].control)) {
slot += 2;
}
if (softwareRenderer->engB.bg[i].extPalette != &softwareRenderer->extPaletteB[slot * 4096] && _regenerateExtPalette(softwareRenderer, true, slot)) {
softwareRenderer->engB.bg[i].extPalette = &softwareRenderer->extPaletteB[slot * 4096];
}
}
} else {
softwareRenderer->engA.bg[0].extPalette = NULL;
softwareRenderer->engA.bg[1].extPalette = NULL;
softwareRenderer->engA.bg[2].extPalette = NULL;
softwareRenderer->engA.bg[3].extPalette = NULL;
}
}
static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
@ -161,6 +252,89 @@ static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, ui
}
}
static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
_regenerateExtPalette(softwareRenderer, engB, slot);
}
static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, int y) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
int x;
for (x = 0; x < softwareRenderer->masterEnd; ++x) {
row[x] = GBA_COLOR_WHITE;
}
return;
}
GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
int w;
unsigned priority;
for (priority = 0; priority < 4; ++priority) {
softwareRenderer->end = 0;
for (w = 0; w < softwareRenderer->nWindows; ++w) {
softwareRenderer->start = softwareRenderer->end;
softwareRenderer->end = softwareRenderer->windows[w].endX;
softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
if (spriteLayers & (1 << priority)) {
GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
}
if (TEST_LAYER_ENABLED(0)) {
GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
}
if (TEST_LAYER_ENABLED(1)) {
GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
}
if (TEST_LAYER_ENABLED(2)) {
switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
case 0:
case 1:
case 3:
GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
break;
case 2:
case 4:
GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
break;
}
}
if (TEST_LAYER_ENABLED(3)) {
switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
case 0:
GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
break;
case 1:
case 2:
GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
break;
}
}
}
}
softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
#ifdef COLOR_16_BIT
#if defined(__ARM_NEON) && !defined(__APPLE__)
_to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
#else
for (x = 0; x < softwareRenderer->masterEnd; ++x) {
row[x] = softwareRenderer->row[x];
}
#endif
#else
memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
#endif
}
static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
@ -174,7 +348,7 @@ static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int
}
return;
case 1:
softwareRenderer->engA.d.drawScanline(&softwareRenderer->engA.d, y);
DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, y);
return;
case 2: {
uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
@ -229,7 +403,7 @@ static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int
}
return;
case 1:
softwareRenderer->engB.d.drawScanline(&softwareRenderer->engB.d, y);
DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, y);
return;
}

View File

@ -20,6 +20,7 @@ static void DSVideoDummyRendererDeinit(struct DSVideoRenderer* renderer);
static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
static void DSVideoDummyRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
static void DSVideoDummyRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
static void DSVideoDummyRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
static void DSVideoDummyRendererFinishFrame(struct DSVideoRenderer* renderer);
static void DSVideoDummyRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
@ -42,60 +43,86 @@ static const uint32_t _vramSize[9] = {
0x04000
};
enum DSVRAMBankMode {
MODE_A_BG = 0,
MODE_B_BG = 1,
MODE_A_OBJ = 2,
MODE_B_OBJ = 3,
MODE_LCDC,
MODE_7_VRAM,
MODE_A_BG_EXT_PAL,
MODE_B_BG_EXT_PAL,
MODE_A_OBJ_EXT_PAL,
MODE_B_OBJ_EXT_PAL,
MODE_3D_TEX,
MODE_3D_TEX_PAL,
};
const struct DSVRAMBankInfo {
int base;
uint32_t mirrorSize;
int mode;
enum DSVRAMBankMode mode;
int offset[4];
} _vramInfo[9][8] = {
{ // A
{ 0x000, 0x40, 4 }, // LCDC
{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
{ 0x000, 0x10, 2, { 0x00, 0x08, 0x80, 0x80 } }, // A-OBJ
{ 0x000, 0x40, MODE_LCDC },
{ 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
{ 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x08, 0x80, 0x80 } },
{ 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
},
{ // B
{ 0x008, 0x40, 4 }, // LCDC
{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
{ 0x000, 0x10, 2, { 0x00, 0x08, 0x80, 0x80 } }, // A-OBJ
{ 0x008, 0x40, MODE_LCDC },
{ 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
{ 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x08, 0x80, 0x80 } },
{ 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
},
{ // C
{ 0x010, 0x40, 4 }, // LCDC
{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
{ 0x000, 0x40, 5, { 0x00, 0x08, 0x80, 0x80 } }, // 7-VRAM
{},
{ 0x000, 0x08, 1 }, // B-BG
{ 0x010, 0x40, MODE_LCDC },
{ 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
{ 0x000, 0x40, MODE_7_VRAM, { 0x00, 0x08, 0x80, 0x80 } },
{ 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
{ 0x000, 0x08, MODE_B_BG },
},
{ // D
{ 0x018, 0x40, 4 }, // LCDC
{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
{ 0x000, 0x40, 8, { 0x00, 0x08, 0x80, 0x80 } }, // 7-VRAM
{},
{ 0x000, 0x08, 3 }, // B-OBJ
{ 0x018, 0x40, MODE_LCDC },
{ 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
{ 0x000, 0x40, MODE_7_VRAM, { 0x00, 0x08, 0x80, 0x80 } },
{ 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
{ 0x000, 0x08, MODE_B_OBJ },
},
{ // E
{ 0x020, 0x40, 4 }, // LCDC
{ 0x000, 0x20, 0 }, // A-BG
{ 0x000, 0x10, 2 }, // A-OBJ
{ 0x020, 0x40, MODE_LCDC },
{ 0x000, 0x20, MODE_A_BG },
{ 0x000, 0x10, MODE_A_OBJ },
{ 0x000, 0x04, MODE_3D_TEX_PAL },
{ 0x000, 0x04, MODE_A_BG_EXT_PAL },
},
{ // F
{ 0x024, 0x40, 4 }, // LCDC
{ 0x000, 0x20, 0, { 0x00, 0x01, 0x04, 0x05 } }, // A-BG
{ 0x000, 0x10, 2, { 0x00, 0x01, 0x04, 0x05 } }, // A-OBJ
{ 0x024, 0x40, MODE_LCDC },
{ 0x000, 0x20, MODE_A_BG, { 0x00, 0x01, 0x04, 0x05 } },
{ 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x01, 0x04, 0x05 } },
{ 0x000, 0x01, MODE_3D_TEX_PAL, { 0x00, 0x01, 0x04, 0x05 } },
{ 0x000, 0x02, MODE_A_BG_EXT_PAL, { 0x00, 0x02, 0x00, 0x02 } },
{ 0x000, 0x01, MODE_A_OBJ_EXT_PAL},
},
{ // G
{ 0x025, 0x40, 4 }, // LCDC
{ 0x000, 0x20, 0 }, // A-BG
{ 0x000, 0x10, 2 }, // A-OBJ
{ 0x025, 0x40, MODE_LCDC },
{ 0x000, 0x20, MODE_A_BG, { 0x00, 0x01, 0x04, 0x05 } },
{ 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x01, 0x04, 0x05 } },
{ 0x000, 0x01, MODE_3D_TEX_PAL, { 0x00, 0x01, 0x04, 0x05 } },
{ 0x000, 0x02, MODE_A_BG_EXT_PAL, { 0x00, 0x02, 0x00, 0x02 } },
{ 0x000, 0x01, MODE_A_OBJ_EXT_PAL},
},
{ // H
{ 0x026, 0x40, 4 }, // LCDC
{ 0x000, 0x04, 1 }, // B-BG
{ 0x000, 0x10, 2 }, // A-OBJ
{ 0x026, 0x40, MODE_LCDC },
{ 0x000, 0x04, MODE_B_BG },
{ 0x000, 0x04, MODE_B_BG_EXT_PAL },
},
{ // I
{ 0x028, 0x40, 4 }, // LCDC
{ 0x002, 0x04, 1 }, // B-BG
{ 0x000, 0x01, 3 }, // B-OBJ
{ 0x028, 0x40, MODE_LCDC },
{ 0x002, 0x04, MODE_B_BG },
{ 0x000, 0x01, MODE_B_OBJ },
{ 0x000, 0x01, MODE_B_OBJ_EXT_PAL },
},
};
@ -166,8 +193,12 @@ void DSVideoAssociateRenderer(struct DSVideo* video, struct DSVideoRenderer* ren
renderer->vram = video->vram;
memcpy(renderer->vramABG, video->vramABG, sizeof(renderer->vramABG));
memcpy(renderer->vramAOBJ, video->vramAOBJ, sizeof(renderer->vramAOBJ));
memcpy(renderer->vramABGExtPal, video->vramABGExtPal, sizeof(renderer->vramABGExtPal));
memcpy(renderer->vramAOBJExtPal, video->vramAOBJExtPal, sizeof(renderer->vramAOBJExtPal));
memcpy(renderer->vramBBG, video->vramBBG, sizeof(renderer->vramBBG));
memcpy(renderer->vramBOBJ, video->vramBOBJ, sizeof(renderer->vramBOBJ));
memcpy(renderer->vramBBGExtPal, video->vramBBGExtPal, sizeof(renderer->vramBBGExtPal));
memcpy(renderer->vramBOBJExtPal, video->vramBOBJExtPal, sizeof(renderer->vramBOBJExtPal));
renderer->oam = &video->oam;
video->renderer->init(video->renderer);
}
@ -300,18 +331,83 @@ void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) {
// TODO: Does a VCounter IRQ trigger on write?
}
void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value) {
void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value, uint8_t oldValue) {
struct DSMemory* memory = &ds->memory;
if (value == oldValue) {
return;
}
uint32_t i, j;
uint32_t size = _vramSize[index] >> DS_VRAM_OFFSET;
struct DSVRAMBankInfo oldInfo = _vramInfo[index][oldValue & 0x7];
uint32_t offset = oldInfo.base + oldInfo.offset[(oldValue >> 3) & 3];
switch (oldInfo.mode) {
case MODE_A_BG:
for (i = 0; i < size; ++i) {
if (ds->video.vramABG[offset + i] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
ds->video.vramABG[offset + i] = NULL;
ds->video.renderer->vramABG[offset + i] = NULL;
}
}
break;
case MODE_B_BG:
for (i = 0; i < size; ++i) {
if (ds->video.vramBBG[offset + i] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
ds->video.vramBBG[offset + i] = NULL;
ds->video.renderer->vramBBG[offset + i] = NULL;
}
}
break;
case MODE_A_OBJ:
for (i = 0; i < size; ++i) {
if (ds->video.vramAOBJ[offset + i] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
ds->video.vramAOBJ[offset + i] = NULL;
ds->video.renderer->vramAOBJ[offset + i] = NULL;
}
}
break;
case MODE_B_OBJ:
for (i = 0; i < size; ++i) {
if (ds->video.vramBOBJ[offset + i] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
ds->video.vramBOBJ[offset + i] = NULL;
ds->video.renderer->vramBOBJ[offset + i] = NULL;
}
}
break;
case MODE_A_BG_EXT_PAL:
for (i = 0; i < oldInfo.mirrorSize; ++i) {
if (ds->video.vramABGExtPal[offset + i] == &memory->vramBank[index][i << 12]) {
ds->video.vramABGExtPal[offset + i] = NULL;
ds->video.renderer->vramABGExtPal[offset + i] = NULL;
ds->video.renderer->invalidateExtPal(ds->video.renderer, false, false, offset + i);
}
}
break;
case MODE_B_BG_EXT_PAL:
for (i = 0; i < oldInfo.mirrorSize; ++i) {
if (ds->video.vramBBGExtPal[offset + i] == &memory->vramBank[index][i << 12]) {
ds->video.vramBBGExtPal[offset + i] = NULL;
ds->video.renderer->vramBBGExtPal[offset + i] = NULL;
ds->video.renderer->invalidateExtPal(ds->video.renderer, false, true, offset + i);
}
}
break;
case MODE_7_VRAM:
for (i = 0; i < size; i += 16) {
ds->memory.vram7[(offset + i) >> 4] = NULL;
}
break;
case MODE_LCDC:
break;
}
struct DSVRAMBankInfo info = _vramInfo[index][value & 0x7];
memset(&memory->vramMirror[index], 0, sizeof(memory->vramMirror[index]));
memset(&memory->vramMode[index], 0, sizeof(memory->vramMode[index]));
if (!(value & 0x80) || !info.mirrorSize) {
return;
}
uint32_t size = _vramSize[index] >> DS_VRAM_OFFSET;
uint32_t i, j;
uint32_t offset = info.base + info.offset[(value >> 3) & 3];
if (info.mode < 4) {
offset = info.base + info.offset[(value >> 3) & 3];
if (info.mode <= MODE_LCDC) {
memory->vramMode[index][info.mode] = 0xFFFF;
for (j = offset; j < 0x40; j += info.mirrorSize) {
for (i = 0; i < size; ++i) {
@ -320,35 +416,51 @@ void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value) {
}
}
switch (info.mode) {
case 0:
case MODE_A_BG:
for (i = 0; i < size; ++i) {
ds->video.vramABG[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
ds->video.renderer->vramABG[offset + i] = ds->video.vramABG[offset + i];
}
break;
case 1:
case MODE_B_BG:
for (i = 0; i < size; ++i) {
ds->video.vramBBG[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
ds->video.renderer->vramBBG[offset + i] = ds->video.vramBBG[offset + i];
}
break;
case 2:
case MODE_A_OBJ:
for (i = 0; i < size; ++i) {
ds->video.vramAOBJ[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
ds->video.renderer->vramAOBJ[offset + i] = ds->video.vramAOBJ[offset + i];
}
break;
case 3:
case MODE_B_OBJ:
for (i = 0; i < size; ++i) {
ds->video.vramBOBJ[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
ds->video.renderer->vramBOBJ[offset + i] = ds->video.vramBOBJ[offset + i];
}
break;
case 5:
case MODE_A_BG_EXT_PAL:
for (i = 0; i < info.mirrorSize; ++i) {
ds->video.vramABGExtPal[offset + i] = &memory->vramBank[index][i << 12];
ds->video.renderer->vramABGExtPal[offset + i] = ds->video.vramABGExtPal[offset + i];
ds->video.renderer->invalidateExtPal(ds->video.renderer, false, false, offset + i);
}
break;
case MODE_B_BG_EXT_PAL:
for (i = 0; i < info.mirrorSize; ++i) {
ds->video.vramBBGExtPal[offset + i] = &memory->vramBank[index][i << 12];
ds->video.renderer->vramBBGExtPal[offset + i] = ds->video.vramBBGExtPal[offset + i];
ds->video.renderer->invalidateExtPal(ds->video.renderer, false, true, offset + i);
}
break;
case MODE_7_VRAM:
for (i = 0; i < size; i += 16) {
ds->memory.vram7[(offset + i) >> 4] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 5)];
}
break;
case MODE_LCDC:
break;
}
}
@ -385,6 +497,13 @@ static void DSVideoDummyRendererWriteOAM(struct DSVideoRenderer* renderer, uint3
// Nothing to do
}
static void DSVideoDummyRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
UNUSED(renderer);
UNUSED(obj);
UNUSED(engB);
// Nothing to do
}
static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
UNUSED(renderer);
UNUSED(y);

View File

@ -15,6 +15,9 @@
} \
screenBase = background->screenBase + yBase + (xBase >> 2); \
uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
if (UNLIKELY(!screenBlock)) { \
return; \
} \
LOAD_16(mapData, screenBase & VRAM_BLOCK_MASK, screenBlock); \
localY = inY & 0x7; \
if (GBA_TEXT_MAP_VFLIP(mapData)) { \
@ -361,6 +364,195 @@
} \
}
#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256Ext(BLEND, OBJWIN) \
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
palette = &mainPalette[paletteData]; \
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
int end2 = end - 4; \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
int shift = inX & 0x3; \
if (LIKELY(vram)) { \
if (end2 > outX) { \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
tileData >>= 8 * shift; \
shift = 0; \
for (; outX < end2; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
} \
} \
\
if (LIKELY(vram)) { \
LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
tileData >>= 8 * shift; \
for (; outX < end; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
} \
} else { \
int start = outX; \
outX = end - 1; \
pixel = &renderer->row[outX]; \
if (LIKELY(vram)) { \
if (end2 > start) { \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
for (; outX >= end2; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
charBase += 4; \
} \
} \
\
if (LIKELY(vram)) { \
LOAD_32(tileData, charBase, vram); \
for (; outX >= renderer->start; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
} \
outX = end; \
pixel = &renderer->row[outX]; \
}
#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256Ext(BLEND, OBJWIN) \
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
if (UNLIKELY(!vram)) { \
return; \
} \
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
palette = &mainPalette[paletteData]; \
int end = mod8 - 4; \
pixel = &renderer->row[outX]; \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
if (end > 0) { \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
for (; outX < renderer->end - end; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
charBase += 4; \
} \
\
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
for (; outX < renderer->end; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
} else { \
int shift = (8 - mod8) & 0x3; \
int start = outX; \
outX = renderer->end - 1; \
pixel = &renderer->row[outX]; \
if (end > 0) { \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
tileData >>= 8 * shift; \
for (; outX >= start + 4; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
shift = 0; \
} \
\
LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
tileData >>= 8 * shift; \
for (; outX >= start; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
/* Needed for consistency checks */ \
if (VIDEO_CHECKS) { \
outX = renderer->end; \
pixel = &renderer->row[outX]; \
} \
}
#define DRAW_BACKGROUND_MODE_0_TILES_256Ext(BLEND, OBJWIN) \
for (; tileX < tileEnd; ++tileX) { \
BACKGROUND_TEXT_SELECT_CHARACTER; \
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
palette = &mainPalette[paletteData]; \
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
if (UNLIKELY(!vram)) { \
pixel += 8; \
continue; \
} \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
if (tileData) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
} \
pixel += 4; \
LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
if (tileData) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
} \
pixel += 4; \
} else { \
LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
if (tileData) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
pixel += 4; \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
if (tileData) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
} \
pixel += 4; \
} \
}
#define DRAW_BACKGROUND_MODE_0_MOSAIC_256Ext(BLEND, OBJWIN) \
for (; tileX < tileEnd; ++tileX) { \
BACKGROUND_TEXT_SELECT_CHARACTER; \
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
tileData = carryData; \
for (x = 0; x < 8; ++x) { \
if (!mosaicWait) { \
if (UNLIKELY(!vram)) { \
carryData = 0; \
} else { \
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
palette = &mainPalette[paletteData]; \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
if (x >= 4) { \
LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
tileData >>= (x - 4) * 8; \
} else { \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
tileData >>= x * 8; \
} \
} else { \
if (x >= 4) { \
LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
tileData >>= (7 - x) * 8; \
} else { \
LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
tileData >>= (3 - x) * 8; \
} \
} \
tileData &= 0xFF; \
carryData = tileData; \
} \
mosaicWait = mosaicH; \
} \
tileData |= tileData << 8; \
--mosaicWait; \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
++pixel; \
} \
}
#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
uint32_t* pixel = &renderer->row[outX]; \
if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
@ -460,9 +652,18 @@ void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer
uint32_t screenBase;
uint32_t charBase;
int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
color_t* mainPalette = renderer->normalPalette;
if (variant) {
mainPalette = renderer->variantPalette;
color_t* mainPalette;
if (background->multipalette && background->extPalette) {
mainPalette = background->extPalette;
// TODO
/*if (variant) {
mainPalette = background->variantPalette;
}*/
} else {
mainPalette = renderer->normalPalette;
if (variant) {
mainPalette = renderer->variantPalette;
}
}
color_t* palette = mainPalette;
PREPARE_OBJWIN;
@ -481,28 +682,36 @@ void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer
if (!(flags & FLAG_TARGET_2)) {
if (!background->multipalette) {
DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
} else {
} else if (!background->extPalette) {
DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
} else {
DRAW_BACKGROUND_MODE_0(256Ext, NoBlend, NO_OBJWIN);
}
} else {
if (!background->multipalette) {
DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
} else {
} else if (!background->extPalette) {
DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
} else {
DRAW_BACKGROUND_MODE_0(256Ext, Blend, NO_OBJWIN);
}
}
} else {
if (!(flags & FLAG_TARGET_2)) {
if (!background->multipalette) {
DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
} else {
} else if (!background->extPalette) {
DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
} else {
DRAW_BACKGROUND_MODE_0(256Ext, NoBlend, OBJWIN);
}
} else {
if (!background->multipalette) {
DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
} else {
} else if (!background->extPalette) {
DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
} else {
DRAW_BACKGROUND_MODE_0(256Ext, Blend, OBJWIN);
}
}
}

View File

@ -135,6 +135,8 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
bg->dmy = 256;
bg->sx = 0;
bg->sy = 0;
bg->extPalette = NULL;
bg->variantPalette = NULL;
}
}