GB: Super Game Boy borders

This commit is contained in:
Vicki Pfau 2017-08-01 19:01:44 -07:00
parent 55679df8fc
commit 36c1fb59be
24 changed files with 523 additions and 57 deletions

View File

@ -4,6 +4,7 @@ Features:
- Game Boy Camera support - Game Boy Camera support
- Qt: Set default Game Boy colors - Qt: Set default Game Boy colors
- Game Boy Printer support - Game Boy Printer support
- Super Game Boy borders
Bugfixes: Bugfixes:
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
- Python: Fix importing .gb or .gba before .core - Python: Fix importing .gb or .gba before .core

View File

@ -44,6 +44,34 @@ enum GBIRQVector {
GB_VECTOR_KEYPAD = 0x60, GB_VECTOR_KEYPAD = 0x60,
}; };
enum GBSGBCommand {
SGB_PAL01 = 0,
SGB_PAL23,
SGB_PAL03,
SGB_PAL12,
SGB_ATTR_BLK,
SGB_ATTR_LIN,
SGB_ATTR_DIV,
SGB_ATTR_CHR,
SGB_SOUND,
SGB_SOU_TRN,
SGB_PAL_SET,
SGB_PAL_TRN,
SGB_ATRC_EN,
SGB_TEST_EN,
SGB_PICON_EN,
SGB_DATA_SND,
SGB_DATA_TRN,
SGB_MLT_REG,
SGB_JUMP,
SGB_CHR_TRN,
SGB_PCT_TRN,
SGB_ATTR_TRN,
SGB_ATTR_SET,
SGB_MASK_EN,
SGB_OBJ_TRN
};
struct LR35902Core; struct LR35902Core;
struct mCoreSync; struct mCoreSync;
struct mAVStream; struct mAVStream;
@ -76,6 +104,10 @@ struct GB {
int32_t sramDirtAge; int32_t sramDirtAge;
bool sramMaskWriteback; bool sramMaskWriteback;
int sgbBit;
int currentSgbBits;
uint8_t sgbPacket[16];
struct mCoreCallbacksList coreCallbacks; struct mCoreCallbacksList coreCallbacks;
struct mAVStream* stream; struct mAVStream* stream;

View File

@ -34,6 +34,9 @@ struct GBVideoSoftwareRenderer {
GBRegisterLCDC lcdc; GBRegisterLCDC lcdc;
enum GBModel model; enum GBModel model;
int sgbTransfer;
uint8_t sgbPacket[16];
}; };
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*); void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);

View File

@ -30,7 +30,11 @@ enum {
GB_VIDEO_TOTAL_LENGTH = 70224, GB_VIDEO_TOTAL_LENGTH = 70224,
GB_BASE_MAP = 0x1800, GB_BASE_MAP = 0x1800,
GB_SIZE_MAP = 0x0400 GB_SIZE_MAP = 0x0400,
SGB_SIZE_CHAR_RAM = 0x2000,
SGB_SIZE_MAP_RAM = 0x1000,
SGB_SIZE_PAL_RAM = 0x1000
}; };
DECL_BITFIELD(GBObjAttributes, uint8_t); DECL_BITFIELD(GBObjAttributes, uint8_t);
@ -41,6 +45,13 @@ DECL_BIT(GBObjAttributes, XFlip, 5);
DECL_BIT(GBObjAttributes, YFlip, 6); DECL_BIT(GBObjAttributes, YFlip, 6);
DECL_BIT(GBObjAttributes, Priority, 7); DECL_BIT(GBObjAttributes, Priority, 7);
DECL_BITFIELD(SGBBgAttributes, uint16_t);
DECL_BITS(SGBBgAttributes, Tile, 0, 10);
DECL_BITS(SGBBgAttributes, Palette, 10, 3);
DECL_BIT(SGBBgAttributes, Priority, 13);
DECL_BIT(SGBBgAttributes, XFlip, 14);
DECL_BIT(SGBBgAttributes, YFlip, 15);
struct GBObj { struct GBObj {
uint8_t y; uint8_t y;
uint8_t x; uint8_t x;
@ -59,6 +70,7 @@ struct GBVideoRenderer {
void (*deinit)(struct GBVideoRenderer* renderer); void (*deinit)(struct GBVideoRenderer* renderer);
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
void (*writeSGBPacket)(struct GBVideoRenderer* renderer, uint8_t* data);
void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address); void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address);
void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value); void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value);
void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam); void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam);
@ -73,6 +85,11 @@ struct GBVideoRenderer {
union GBOAM* oam; union GBOAM* oam;
struct mTileCache* cache; struct mTileCache* cache;
uint8_t* sgbCharRam;
uint8_t* sgbMapRam;
uint8_t* sgbPalRam;
int sgbRenderMode;
bool disableBG; bool disableBG;
bool disableOBJ; bool disableOBJ;
bool disableWIN; bool disableWIN;
@ -146,6 +163,8 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value);
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color); void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color);
void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data);
struct GBSerializedState; struct GBSerializedState;
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state); void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state); void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state);

View File

@ -190,9 +190,14 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
} }
static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) { static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
UNUSED(core); struct GB* gb = core->board;
*width = GB_VIDEO_HORIZONTAL_PIXELS; if (!gb || gb->model != GB_MODEL_SGB) {
*height = GB_VIDEO_VERTICAL_PIXELS; *width = GB_VIDEO_HORIZONTAL_PIXELS;
*height = GB_VIDEO_VERTICAL_PIXELS;
} else {
*width = 256;
*height = 224;
}
} }
static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) { static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {

View File

@ -10,10 +10,12 @@
#include <mgba/internal/gb/io.h> #include <mgba/internal/gb/io.h>
#define BUFFER_OAM 1 #define BUFFER_OAM 1
#define BUFFER_SGB 2
static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer); static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value); static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value);
@ -30,6 +32,7 @@ void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GB
renderer->d.init = GBVideoProxyRendererInit; renderer->d.init = GBVideoProxyRendererInit;
renderer->d.deinit = GBVideoProxyRendererDeinit; renderer->d.deinit = GBVideoProxyRendererDeinit;
renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister; renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister;
renderer->d.writeSGBPacket = GBVideoProxyRendererWriteSGBPacket;
renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM; renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM;
renderer->d.writeOAM = GBVideoProxyRendererWriteOAM; renderer->d.writeOAM = GBVideoProxyRendererWriteOAM;
renderer->d.writePalette = GBVideoProxyRendererWritePalette; renderer->d.writePalette = GBVideoProxyRendererWritePalette;
@ -111,6 +114,7 @@ void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) { static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
struct GBVideoProxyRenderer* proxyRenderer = logger->context; struct GBVideoProxyRenderer* proxyRenderer = logger->context;
uint8_t sgbPacket[16];
switch (item->type) { switch (item->type) {
case DIRTY_REGISTER: case DIRTY_REGISTER:
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
@ -154,6 +158,11 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
return false; return false;
} }
logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true); logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true);
break;
case BUFFER_SGB:
logger->readData(logger, sgbPacket, 16, true);
proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, sgbPacket);
break;
} }
break; break;
case DIRTY_FLUSH: case DIRTY_FLUSH:
@ -179,6 +188,14 @@ uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer,
return value; return value;
} }
void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, data);
}
mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_SGB, 0, 16, data);
}
void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) { void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address); mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);

View File

@ -28,6 +28,7 @@ static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
#define DMG_BIOS_CHECKSUM 0xC2F5CC97 #define DMG_BIOS_CHECKSUM 0xC2F5CC97
#define DMG_2_BIOS_CHECKSUM 0x59C8598E #define DMG_2_BIOS_CHECKSUM 0x59C8598E
#define SGB_BIOS_CHECKSUM 0xEC8A83B9
#define CGB_BIOS_CHECKSUM 0x41884E46 #define CGB_BIOS_CHECKSUM 0x41884E46
mLOG_DEFINE_CATEGORY(GB, "GB", "gb"); mLOG_DEFINE_CATEGORY(GB, "GB", "gb");
@ -384,6 +385,7 @@ bool GBIsBIOS(struct VFile* vf) {
switch (_GBBiosCRC32(vf)) { switch (_GBBiosCRC32(vf)) {
case DMG_BIOS_CHECKSUM: case DMG_BIOS_CHECKSUM:
case DMG_2_BIOS_CHECKSUM: case DMG_2_BIOS_CHECKSUM:
case SGB_BIOS_CHECKSUM:
case CGB_BIOS_CHECKSUM: case CGB_BIOS_CHECKSUM:
return true; return true;
default: default:
@ -425,11 +427,11 @@ void GBReset(struct LR35902Core* cpu) {
if (!gb->biosVf) { if (!gb->biosVf) {
switch (gb->model) { switch (gb->model) {
case GB_MODEL_DMG:
// TODO: SGB
case GB_MODEL_SGB:
case GB_MODEL_AUTODETECT: // Silence warnings case GB_MODEL_AUTODETECT: // Silence warnings
gb->model = GB_MODEL_DMG; gb->model = GB_MODEL_DMG;
// TODO: SGB registers
case GB_MODEL_SGB:
case GB_MODEL_DMG:
cpu->a = 1; cpu->a = 1;
cpu->f.packed = 0xB0; cpu->f.packed = 0xB0;
cpu->c = 0x13; cpu->c = 0x13;
@ -467,6 +469,10 @@ void GBReset(struct LR35902Core* cpu) {
gb->yankedRomSize = 0; gb->yankedRomSize = 0;
} }
gb->sgbBit = -1;
gb->currentSgbBits = 0;
memset(gb->sgbPacket, 0, sizeof(gb->sgbPacket));
mTimingClear(&gb->timing); mTimingClear(&gb->timing);
GBMemoryReset(gb); GBMemoryReset(gb);
@ -491,6 +497,9 @@ void GBDetectModel(struct GB* gb) {
case DMG_2_BIOS_CHECKSUM: case DMG_2_BIOS_CHECKSUM:
gb->model = GB_MODEL_DMG; gb->model = GB_MODEL_DMG;
break; break;
case SGB_BIOS_CHECKSUM:
gb->model = GB_MODEL_SGB;
break;
case CGB_BIOS_CHECKSUM: case CGB_BIOS_CHECKSUM:
gb->model = GB_MODEL_CGB; gb->model = GB_MODEL_CGB;
break; break;
@ -503,6 +512,8 @@ void GBDetectModel(struct GB* gb) {
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
if (cart->cgb & 0x80) { if (cart->cgb & 0x80) {
gb->model = GB_MODEL_CGB; gb->model = GB_MODEL_CGB;
} else if (cart->sgb == 0x03 && cart->oldLicensee == 0x33) {
gb->model = GB_MODEL_SGB;
} else { } else {
gb->model = GB_MODEL_DMG; gb->model = GB_MODEL_DMG;
} }

View File

@ -105,6 +105,33 @@ static const uint8_t _registerMask[] = {
[REG_IE] = 0xE0, [REG_IE] = 0xE0,
}; };
static void _writeSGBBits(struct GB* gb, int bits) {
if (!bits) {
gb->sgbBit = 0;
memset(gb->sgbPacket, 0, sizeof(gb->sgbPacket));
}
if (bits == gb->currentSgbBits) {
return;
}
gb->currentSgbBits = bits;
if (gb->sgbBit == 128 && bits == 2) {
GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket);
++gb->sgbBit;
}
if (gb->sgbBit >= 128) {
return;
}
switch (bits) {
case 1:
gb->sgbPacket[gb->sgbBit >> 3] |= 1 << (gb->sgbBit & 7);
// Fall through
case 2:
++gb->sgbBit;
default:
break;
}
}
void GBIOInit(struct GB* gb) { void GBIOInit(struct GB* gb) {
memset(gb->memory.io, 0, sizeof(gb->memory.io)); memset(gb->memory.io, 0, sizeof(gb->memory.io));
} }
@ -341,6 +368,10 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
} }
break; break;
case REG_JOYP: case REG_JOYP:
if (gb->model == GB_MODEL_SGB) {
_writeSGBBits(gb, (value >> 4) & 3);
}
break;
case REG_TIMA: case REG_TIMA:
case REG_TMA: case REG_TMA:
// Handled transparently by the registers // Handled transparently by the registers
@ -453,7 +484,8 @@ static uint8_t _readKeys(struct GB* gb) {
uint8_t keys = *gb->keySource; uint8_t keys = *gb->keySource;
switch (gb->memory.io[REG_JOYP] & 0x30) { switch (gb->memory.io[REG_JOYP] & 0x30) {
case 0x30: case 0x30:
keys = 0; // TODO: Increment
keys = gb->model == GB_MODEL_SGB ? 0xF : 0;
break; break;
case 0x20: case 0x20:
keys >>= 4; keys >>= 4;

View File

@ -54,7 +54,7 @@ bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverri
override->model = GB_MODEL_AGB; override->model = GB_MODEL_AGB;
} else if (strcasecmp(model, "SGB") == 0) { } else if (strcasecmp(model, "SGB") == 0) {
found = true; found = true;
override->model = GB_MODEL_DMG; // TODO override->model = GB_MODEL_SGB;
} else if (strcasecmp(model, "MGB") == 0) { } else if (strcasecmp(model, "MGB") == 0) {
found = true; found = true;
override->model = GB_MODEL_DMG; // TODO override->model = GB_MODEL_DMG; // TODO

View File

@ -7,11 +7,13 @@
#include <mgba/core/tile-cache.h> #include <mgba/core/tile-cache.h>
#include <mgba/internal/gb/io.h> #include <mgba/internal/gb/io.h>
#include <mgba-util/math.h>
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer); static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoSoftwareRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
@ -25,9 +27,13 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y); static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) { static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
size_t sgbOffset = 0;
if (renderer->model == GB_MODEL_SGB) {
sgbOffset = renderer->outputBufferStride * 40 + 48;
}
int y; int y;
for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) { for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y]; color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y + sgbOffset];
int x; int x;
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) { for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
row[x + 0] = renderer->palette[0]; row[x + 0] = renderer->palette[0];
@ -38,10 +44,64 @@ static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
} }
} }
static void _regenerateSGBBorder(struct GBVideoSoftwareRenderer* renderer) {
int i;
for (i = 0; i < 0x40; ++i) {
uint16_t color;
LOAD_16LE(color, 0x800 + i * 2, renderer->d.sgbMapRam);
renderer->d.writePalette(&renderer->d, i + 0x40, color);
}
int x, y;
for (y = 0; y < 224; ++y) {
for (x = 0; x < 256; x += 8) {
uint16_t mapData;
LOAD_16LE(mapData, (x >> 2) + (y & ~7) * 8, renderer->d.sgbMapRam);
if (UNLIKELY(SGBBgAttributesGetTile(mapData) > 0x100)) {
continue;
}
int localY = y & 0x7;
if (SGBBgAttributesIsYFlip(mapData)) {
localY = 7 - y;
}
uint8_t tileData[4];
tileData[0] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x00];
tileData[1] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x01];
tileData[2] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x10];
tileData[3] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x11];
if (!(tileData[0] | tileData[1] | tileData[2] | tileData[3])) {
continue;
}
size_t base = y * renderer->outputBufferStride + x;
int p = SGBBgAttributesGetPalette(mapData) * 0x10;
if (SGBBgAttributesIsXFlip(mapData)) {
renderer->outputBuffer[base + 0] = renderer->palette[p | ((tileData[0] >> 0) & 0x1) | ((tileData[1] << 1) & 0x2) | ((tileData[2] << 2) & 0x4) | ((tileData[3] << 3) & 0x8)];
renderer->outputBuffer[base + 1] = renderer->palette[p | ((tileData[0] >> 1) & 0x1) | ((tileData[1] >> 0) & 0x2) | ((tileData[2] << 1) & 0x4) | ((tileData[3] << 2) & 0x8)];
renderer->outputBuffer[base + 2] = renderer->palette[p | ((tileData[0] >> 2) & 0x1) | ((tileData[1] >> 1) & 0x2) | ((tileData[2] >> 0) & 0x4) | ((tileData[3] << 1) & 0x8)];
renderer->outputBuffer[base + 3] = renderer->palette[p | ((tileData[0] >> 3) & 0x1) | ((tileData[1] >> 2) & 0x2) | ((tileData[2] >> 1) & 0x4) | ((tileData[3] >> 0) & 0x8)];
renderer->outputBuffer[base + 4] = renderer->palette[p | ((tileData[0] >> 4) & 0x1) | ((tileData[1] >> 3) & 0x2) | ((tileData[2] >> 2) & 0x4) | ((tileData[3] >> 1) & 0x8)];
renderer->outputBuffer[base + 5] = renderer->palette[p | ((tileData[0] >> 5) & 0x1) | ((tileData[1] >> 4) & 0x2) | ((tileData[2] >> 3) & 0x4) | ((tileData[3] >> 2) & 0x8)];
renderer->outputBuffer[base + 6] = renderer->palette[p | ((tileData[0] >> 6) & 0x1) | ((tileData[1] >> 5) & 0x2) | ((tileData[2] >> 4) & 0x4) | ((tileData[3] >> 3) & 0x8)];
renderer->outputBuffer[base + 7] = renderer->palette[p | ((tileData[0] >> 7) & 0x1) | ((tileData[1] >> 6) & 0x2) | ((tileData[2] >> 5) & 0x4) | ((tileData[3] >> 4) & 0x8)];
} else {
renderer->outputBuffer[base + 0] = renderer->palette[p | ((tileData[0] >> 7) & 0x1) | ((tileData[1] >> 6) & 0x2) | ((tileData[2] >> 5) & 0x4) | ((tileData[3] >> 4) & 0x8)];
renderer->outputBuffer[base + 1] = renderer->palette[p | ((tileData[0] >> 6) & 0x1) | ((tileData[1] >> 5) & 0x2) | ((tileData[2] >> 4) & 0x4) | ((tileData[3] >> 3) & 0x8)];
renderer->outputBuffer[base + 2] = renderer->palette[p | ((tileData[0] >> 5) & 0x1) | ((tileData[1] >> 4) & 0x2) | ((tileData[2] >> 3) & 0x4) | ((tileData[3] >> 2) & 0x8)];
renderer->outputBuffer[base + 3] = renderer->palette[p | ((tileData[0] >> 4) & 0x1) | ((tileData[1] >> 3) & 0x2) | ((tileData[2] >> 2) & 0x4) | ((tileData[3] >> 1) & 0x8)];
renderer->outputBuffer[base + 4] = renderer->palette[p | ((tileData[0] >> 3) & 0x1) | ((tileData[1] >> 2) & 0x2) | ((tileData[2] >> 1) & 0x4) | ((tileData[3] >> 0) & 0x8)];
renderer->outputBuffer[base + 5] = renderer->palette[p | ((tileData[0] >> 2) & 0x1) | ((tileData[1] >> 1) & 0x2) | ((tileData[2] >> 0) & 0x4) | ((tileData[3] << 1) & 0x8)];
renderer->outputBuffer[base + 6] = renderer->palette[p | ((tileData[0] >> 1) & 0x1) | ((tileData[1] >> 0) & 0x2) | ((tileData[2] << 1) & 0x4) | ((tileData[3] << 2) & 0x8)];
renderer->outputBuffer[base + 7] = renderer->palette[p | ((tileData[0] >> 0) & 0x1) | ((tileData[1] << 1) & 0x2) | ((tileData[2] << 2) & 0x4) | ((tileData[3] << 3) & 0x8)];
}
}
}
}
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
renderer->d.init = GBVideoSoftwareRendererInit; renderer->d.init = GBVideoSoftwareRendererInit;
renderer->d.deinit = GBVideoSoftwareRendererDeinit; renderer->d.deinit = GBVideoSoftwareRendererDeinit;
renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister; renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
renderer->d.writeSGBPacket = GBVideoSoftwareRendererWriteSGBPacket;
renderer->d.writePalette = GBVideoSoftwareRendererWritePalette; renderer->d.writePalette = GBVideoSoftwareRendererWritePalette;
renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM; renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM;
renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM; renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM;
@ -67,6 +127,7 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G
softwareRenderer->currentWy = 0; softwareRenderer->currentWy = 0;
softwareRenderer->wx = 0; softwareRenderer->wx = 0;
softwareRenderer->model = model; softwareRenderer->model = model;
softwareRenderer->sgbTransfer = 0;
} }
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
@ -96,6 +157,11 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
return value; return value;
} }
static void GBVideoSoftwareRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
memcpy(softwareRenderer->sgbPacket, data, sizeof(softwareRenderer->sgbPacket));
}
static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) { static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
@ -167,20 +233,60 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y); GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);
} }
} }
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; size_t sgbOffset = 0;
int x; if (softwareRenderer->model == GB_MODEL_SGB) {
for (x = startX; x + 7 < (endX & ~7); x += 8) { sgbOffset = softwareRenderer->outputBufferStride * 40 + 48;
row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
row[x + 1] = softwareRenderer->palette[softwareRenderer->row[x + 1] & 0x7F];
row[x + 2] = softwareRenderer->palette[softwareRenderer->row[x + 2] & 0x7F];
row[x + 3] = softwareRenderer->palette[softwareRenderer->row[x + 3] & 0x7F];
row[x + 4] = softwareRenderer->palette[softwareRenderer->row[x + 4] & 0x7F];
row[x + 5] = softwareRenderer->palette[softwareRenderer->row[x + 5] & 0x7F];
row[x + 6] = softwareRenderer->palette[softwareRenderer->row[x + 6] & 0x7F];
row[x + 7] = softwareRenderer->palette[softwareRenderer->row[x + 7] & 0x7F];
} }
for (; x < endX; ++x) { color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y + sgbOffset];
row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F]; int x;
switch (softwareRenderer->d.sgbRenderMode) {
case 0:
for (x = startX; x + 7 < (endX & ~7); x += 8) {
row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
row[x + 1] = softwareRenderer->palette[softwareRenderer->row[x + 1] & 0x7F];
row[x + 2] = softwareRenderer->palette[softwareRenderer->row[x + 2] & 0x7F];
row[x + 3] = softwareRenderer->palette[softwareRenderer->row[x + 3] & 0x7F];
row[x + 4] = softwareRenderer->palette[softwareRenderer->row[x + 4] & 0x7F];
row[x + 5] = softwareRenderer->palette[softwareRenderer->row[x + 5] & 0x7F];
row[x + 6] = softwareRenderer->palette[softwareRenderer->row[x + 6] & 0x7F];
row[x + 7] = softwareRenderer->palette[softwareRenderer->row[x + 7] & 0x7F];
}
for (; x < endX; ++x) {
row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
}
break;
case 1:
return;
case 2:
for (x = startX; x + 7 < (endX & ~7); x += 8) {
row[x] = 0;
row[x + 1] = 0;
row[x + 2] = 0;
row[x + 3] = 0;
row[x + 4] = 0;
row[x + 5] = 0;
row[x + 6] = 0;
row[x + 7] = 0;
}
for (; x < endX; ++x) {
row[x] = 0;
}
return;
case 3:
for (x = startX; x + 7 < (endX & ~7); x += 8) {
row[x] = softwareRenderer->palette[0];
row[x + 1] = softwareRenderer->palette[0];
row[x + 2] = softwareRenderer->palette[0];
row[x + 3] = softwareRenderer->palette[0];
row[x + 4] = softwareRenderer->palette[0];
row[x + 5] = softwareRenderer->palette[0];
row[x + 6] = softwareRenderer->palette[0];
row[x + 7] = softwareRenderer->palette[0];
}
for (; x < endX; ++x) {
row[x] = softwareRenderer->palette[0];
}
return;
} }
} }
@ -189,6 +295,53 @@ static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* render
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) { if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) {
++softwareRenderer->currentWy; ++softwareRenderer->currentWy;
} }
if (softwareRenderer->sgbTransfer == 1) {
uint8_t* buffer = NULL;
switch (softwareRenderer->sgbPacket[0] >> 3) {
case SGB_PAL_TRN:
buffer = renderer->sgbPalRam;
break;
case SGB_CHR_TRN:
buffer = &renderer->sgbCharRam[SGB_SIZE_CHAR_RAM / 2 * (softwareRenderer->sgbPacket[1] & 1)];
break;
case SGB_PCT_TRN:
buffer = renderer->sgbMapRam;
break;
default:
break;
}
if (buffer) {
size_t offset = 2 * ((y & 7) + (y >> 3) * GB_VIDEO_HORIZONTAL_PIXELS);
if (offset < 0x1000) {
int i;
for (i = 0; i < GB_VIDEO_HORIZONTAL_PIXELS; i += 8) {
if (UNLIKELY(offset + (i << 1) + 1 >= 0x1000)) {
break;
}
uint8_t hi = 0;
uint8_t lo = 0;
hi |= (softwareRenderer->row[i + 0] & 0x2) << 6;
lo |= (softwareRenderer->row[i + 0] & 0x1) << 7;
hi |= (softwareRenderer->row[i + 1] & 0x2) << 5;
lo |= (softwareRenderer->row[i + 1] & 0x1) << 6;
hi |= (softwareRenderer->row[i + 2] & 0x2) << 4;
lo |= (softwareRenderer->row[i + 2] & 0x1) << 5;
hi |= (softwareRenderer->row[i + 3] & 0x2) << 3;
lo |= (softwareRenderer->row[i + 3] & 0x1) << 4;
hi |= (softwareRenderer->row[i + 4] & 0x2) << 2;
lo |= (softwareRenderer->row[i + 4] & 0x1) << 3;
hi |= (softwareRenderer->row[i + 5] & 0x2) << 1;
lo |= (softwareRenderer->row[i + 5] & 0x1) << 2;
hi |= (softwareRenderer->row[i + 6] & 0x2) << 0;
lo |= (softwareRenderer->row[i + 6] & 0x1) << 1;
hi |= (softwareRenderer->row[i + 7] & 0x2) >> 1;
lo |= (softwareRenderer->row[i + 7] & 0x1) >> 0;
buffer[offset + (i << 1) + 0] = lo;
buffer[offset + (i << 1) + 1] = hi;
}
}
}
}
} }
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) { static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) {
@ -201,6 +354,34 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) { if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
_clearScreen(softwareRenderer); _clearScreen(softwareRenderer);
} }
if (softwareRenderer->model == GB_MODEL_SGB) {
switch (softwareRenderer->sgbPacket[0] >> 3) {
case SGB_PAL_SET:
if (softwareRenderer->sgbPacket[9] & 0x40) {
renderer->sgbRenderMode = 0;
}
break;
case SGB_ATTR_SET:
if (softwareRenderer->sgbPacket[1] & 0x40) {
renderer->sgbRenderMode = 0;
}
break;
case SGB_PAL_TRN:
case SGB_CHR_TRN:
case SGB_PCT_TRN:
if (softwareRenderer->sgbTransfer > 0) {
// Make sure every buffer sees this if we're multibuffering
_regenerateSGBBorder(softwareRenderer);
}
++softwareRenderer->sgbTransfer;
if (softwareRenderer->sgbTransfer == 5) {
softwareRenderer->sgbTransfer = 0;
softwareRenderer->sgbPacket[0] = 0;
}
default:
break;
}
}
softwareRenderer->currentWy = 0; softwareRenderer->currentWy = 0;
} }

View File

@ -18,6 +18,7 @@
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer); static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
@ -39,6 +40,7 @@ static struct GBVideoRenderer dummyRenderer = {
.init = GBVideoDummyRendererInit, .init = GBVideoDummyRendererInit,
.deinit = GBVideoDummyRendererDeinit, .deinit = GBVideoDummyRendererDeinit,
.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
.writeSGBPacket = GBVideoDummyRendererWriteSGBPacket,
.writeVRAM = GBVideoDummyRendererWriteVRAM, .writeVRAM = GBVideoDummyRendererWriteVRAM,
.writeOAM = GBVideoDummyRendererWriteOAM, .writeOAM = GBVideoDummyRendererWriteOAM,
.writePalette = GBVideoDummyRendererWritePalette, .writePalette = GBVideoDummyRendererWritePalette,
@ -52,6 +54,7 @@ static struct GBVideoRenderer dummyRenderer = {
void GBVideoInit(struct GBVideo* video) { void GBVideoInit(struct GBVideo* video) {
video->renderer = &dummyRenderer; video->renderer = &dummyRenderer;
video->renderer->cache = NULL; video->renderer->cache = NULL;
video->renderer->sgbRenderMode = 0;
video->vram = 0; video->vram = 0;
video->frameskip = 0; video->frameskip = 0;
@ -68,6 +71,10 @@ void GBVideoInit(struct GBVideo* video) {
video->dmgPalette[1] = 0x56B5; video->dmgPalette[1] = 0x56B5;
video->dmgPalette[2] = 0x294A; video->dmgPalette[2] = 0x294A;
video->dmgPalette[3] = 0x0000; video->dmgPalette[3] = 0x0000;
video->renderer->sgbCharRam = NULL;
video->renderer->sgbMapRam = NULL;
video->renderer->sgbPalRam = NULL;
} }
void GBVideoReset(struct GBVideo* video) { void GBVideoReset(struct GBVideo* video) {
@ -89,6 +96,12 @@ void GBVideoReset(struct GBVideo* video) {
video->renderer->oam = &video->oam; video->renderer->oam = &video->oam;
memset(&video->palette, 0, sizeof(video->palette)); memset(&video->palette, 0, sizeof(video->palette));
if (video->p->model == GB_MODEL_SGB) {
video->renderer->sgbCharRam = anonymousMemoryMap(SGB_SIZE_CHAR_RAM);
video->renderer->sgbMapRam = anonymousMemoryMap(SGB_SIZE_MAP_RAM);
video->renderer->sgbPalRam = anonymousMemoryMap(SGB_SIZE_PAL_RAM);
}
video->renderer->deinit(video->renderer); video->renderer->deinit(video->renderer);
video->renderer->init(video->renderer, video->p->model); video->renderer->init(video->renderer, video->p->model);
} }
@ -96,11 +109,27 @@ void GBVideoReset(struct GBVideo* video) {
void GBVideoDeinit(struct GBVideo* video) { void GBVideoDeinit(struct GBVideo* video) {
GBVideoAssociateRenderer(video, &dummyRenderer); GBVideoAssociateRenderer(video, &dummyRenderer);
mappedMemoryFree(video->vram, GB_SIZE_VRAM); mappedMemoryFree(video->vram, GB_SIZE_VRAM);
if (video->renderer->sgbCharRam) {
mappedMemoryFree(video->renderer->sgbCharRam, SGB_SIZE_CHAR_RAM);
video->renderer->sgbCharRam = NULL;
}
if (video->renderer->sgbMapRam) {
mappedMemoryFree(video->renderer->sgbMapRam, SGB_SIZE_MAP_RAM);
video->renderer->sgbMapRam = NULL;
}
if (video->renderer->sgbPalRam) {
mappedMemoryFree(video->renderer->sgbPalRam, SGB_SIZE_PAL_RAM);
video->renderer->sgbPalRam = NULL;
}
} }
void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) { void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) {
video->renderer->deinit(video->renderer); video->renderer->deinit(video->renderer);
renderer->cache = video->renderer->cache; renderer->cache = video->renderer->cache;
renderer->sgbRenderMode = video->renderer->sgbRenderMode;
renderer->sgbCharRam = video->renderer->sgbCharRam;
renderer->sgbMapRam = video->renderer->sgbMapRam;
renderer->sgbPalRam = video->renderer->sgbPalRam;
video->renderer = renderer; video->renderer = renderer;
renderer->vram = video->vram; renderer->vram = video->vram;
video->renderer->init(video->renderer, video->p->model); video->renderer->init(video->renderer, video->p->model);
@ -372,7 +401,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) { void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
GBRegisterSTAT oldStat = video->stat; GBRegisterSTAT oldStat = video->stat;
video->stat = (video->stat & 0x7) | (value & 0x78); video->stat = (video->stat & 0x7) | (value & 0x78);
if (video->p->model == GB_MODEL_DMG && !_statIRQAsserted(video, oldStat) && video->mode < 3) { if (video->p->model < GB_MODEL_CGB && !_statIRQAsserted(video, oldStat) && video->mode < 3) {
// TODO: variable for the IRQ line value? // TODO: variable for the IRQ line value?
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p); GBUpdateIRQs(video->p);
@ -476,6 +505,96 @@ void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color) {
video->dmgPalette[index] = M_RGB8_TO_RGB5(color); video->dmgPalette[index] = M_RGB8_TO_RGB5(color);
} }
void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
switch (data[0] >> 3) {
case SGB_PAL01:
video->palette[0] = data[1] | (data[2] << 8);
video->palette[1] = data[3] | (data[4] << 8);
video->palette[2] = data[5] | (data[6] << 8);
video->palette[3] = data[7] | (data[8] << 8);
video->palette[17] = data[9] | (data[10] << 8);
video->palette[18] = data[11] | (data[12] << 8);
video->palette[19] = data[13] | (data[14] << 8);
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
video->renderer->writePalette(video->renderer, 17, video->palette[17]);
video->renderer->writePalette(video->renderer, 18, video->palette[18]);
video->renderer->writePalette(video->renderer, 19, video->palette[19]);
break;
case SGB_PAL23:
video->palette[32] = data[1] | (data[2] << 8);
video->palette[33] = data[3] | (data[4] << 8);
video->palette[34] = data[5] | (data[6] << 8);
video->palette[35] = data[7] | (data[8] << 8);
video->palette[49] = data[9] | (data[10] << 8);
video->palette[50] = data[11] | (data[12] << 8);
video->palette[51] = data[13] | (data[14] << 8);
video->renderer->writePalette(video->renderer, 32, video->palette[32]);
video->renderer->writePalette(video->renderer, 33, video->palette[33]);
video->renderer->writePalette(video->renderer, 34, video->palette[34]);
video->renderer->writePalette(video->renderer, 35, video->palette[35]);
video->renderer->writePalette(video->renderer, 49, video->palette[49]);
video->renderer->writePalette(video->renderer, 50, video->palette[50]);
video->renderer->writePalette(video->renderer, 51, video->palette[51]);
break;
case SGB_PAL03:
video->palette[0] = data[1] | (data[2] << 8);
video->palette[1] = data[3] | (data[4] << 8);
video->palette[2] = data[5] | (data[6] << 8);
video->palette[3] = data[7] | (data[8] << 8);
video->palette[49] = data[9] | (data[10] << 8);
video->palette[50] = data[11] | (data[12] << 8);
video->palette[51] = data[13] | (data[14] << 8);
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
video->renderer->writePalette(video->renderer, 49, video->palette[49]);
video->renderer->writePalette(video->renderer, 50, video->palette[50]);
video->renderer->writePalette(video->renderer, 51, video->palette[51]);
break;
case SGB_PAL12:
video->palette[16] = data[1] | (data[2] << 8);
video->palette[17] = data[3] | (data[4] << 8);
video->palette[18] = data[5] | (data[6] << 8);
video->palette[19] = data[7] | (data[8] << 8);
video->palette[33] = data[9] | (data[10] << 8);
video->palette[34] = data[11] | (data[12] << 8);
video->palette[35] = data[13] | (data[14] << 8);
video->renderer->writePalette(video->renderer, 16, video->palette[16]);
video->renderer->writePalette(video->renderer, 17, video->palette[17]);
video->renderer->writePalette(video->renderer, 18, video->palette[18]);
video->renderer->writePalette(video->renderer, 19, video->palette[19]);
video->renderer->writePalette(video->renderer, 33, video->palette[33]);
video->renderer->writePalette(video->renderer, 34, video->palette[34]);
video->renderer->writePalette(video->renderer, 35, video->palette[35]);
break;
case SGB_MLT_REG:
return;
case SGB_MASK_EN:
video->renderer->sgbRenderMode = data[1] & 0x3;
break;
case SGB_PAL_TRN:
case SGB_CHR_TRN:
case SGB_PCT_TRN:
break;
case SGB_PAL_SET:
case SGB_ATTR_SET:
mLOG(GB, STUB, "Unimplemented SGB command: %02X", data[0] >> 3);
break;
default:
mLOG(GB, STUB, "Unimplemented SGB command: %02X", data[0] >> 3);
return;
}
video->renderer->writeSGBPacket(video->renderer, data);
}
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) { static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
UNUSED(renderer); UNUSED(renderer);
UNUSED(model); UNUSED(model);
@ -493,6 +612,11 @@ static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* re
return value; return value;
} }
static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
UNUSED(renderer);
UNUSED(data);
}
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) { static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
if (renderer->cache) { if (renderer->cache) {
mTileCacheWriteVRAM(renderer->cache, address); mTileCacheWriteVRAM(renderer->cache, address);

View File

@ -285,7 +285,7 @@ static void _setup(struct mGUIRunner* runner) {
_map3DSKey(&runner->core->inputMap, KEY_L, GBA_KEY_L); _map3DSKey(&runner->core->inputMap, KEY_L, GBA_KEY_L);
_map3DSKey(&runner->core->inputMap, KEY_R, GBA_KEY_R); _map3DSKey(&runner->core->inputMap, KEY_R, GBA_KEY_R);
outputBuffer = linearMemAlign(256 * VIDEO_VERTICAL_PIXELS * 2, 0x80); outputBuffer = linearMemAlign(256 * 224 * 2, 0x80);
runner->core->setVideoBuffer(runner->core, outputBuffer, 256); runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
unsigned mode; unsigned mode;

View File

@ -390,7 +390,7 @@ bool retro_load_game(const struct retro_game_info* game) {
core->init(core); core->init(core);
core->setAVStream(core, &stream); core->setAVStream(core, &stream);
outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL); outputBuffer = malloc(256 * 224 * BYTES_PER_PIXEL);
core->setVideoBuffer(core, outputBuffer, 256); core->setVideoBuffer(core, outputBuffer, 256);
core->setAudioBufferSize(core, SAMPLES); core->setAudioBufferSize(core, SAMPLES);

View File

@ -65,13 +65,9 @@
}; };
mCoreConfigLoadDefaults(&core->config, &opts); mCoreConfigLoadDefaults(&core->config, &opts);
core->init(core); core->init(core);
outputBuffer = nil;
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
outputBuffer = malloc(width * height * BYTES_PER_PIXEL);
core->setVideoBuffer(core, outputBuffer, width);
core->setAudioBufferSize(core, SAMPLES); core->setAudioBufferSize(core, SAMPLES);
cheatSets = [[NSMutableDictionary alloc] init]; cheatSets = [[NSMutableDictionary alloc] init];
} }
@ -108,6 +104,15 @@
mCoreAutoloadSave(core); mCoreAutoloadSave(core);
core->reset(core); core->reset(core);
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
if (outputBuffer) {
free(outputBuffer);
}
outputBuffer = malloc(width * height * BYTES_PER_PIXEL);
core->setVideoBuffer(core, outputBuffer, width);
return YES; return YES;
} }

View File

@ -110,12 +110,12 @@ void mGLContextPostFrame(struct VideoBackend* v, const void* frame) {
glBindTexture(GL_TEXTURE_2D, context->tex); glBindTexture(GL_TEXTURE_2D, context->tex);
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5 #ifdef COLOR_5_6_5
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, toPow2(v->width), v->height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
#else #else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, toPow2(v->width), v->height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
#endif #endif
#else #else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_BYTE, frame); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, toPow2(v->width), v->height, GL_RGBA, GL_UNSIGNED_BYTE, frame);
#endif #endif
} }

View File

@ -8,6 +8,7 @@
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba-util/configuration.h> #include <mgba-util/configuration.h>
#include <mgba-util/formatting.h> #include <mgba-util/formatting.h>
#include <mgba-util/math.h>
#include <mgba-util/vector.h> #include <mgba-util/vector.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -190,13 +191,6 @@ static void mGLES2ContextClear(struct VideoBackend* v) {
void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) { void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
GLint viewport[4]; GLint viewport[4];
glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo); glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
if (shader->blend) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
glClear(GL_COLOR_BUFFER_BIT);
}
glGetIntegerv(GL_VIEWPORT, viewport); glGetIntegerv(GL_VIEWPORT, viewport);
int drawW = shader->width; int drawW = shader->width;
@ -222,6 +216,14 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
drawH -= drawH % context->d.height; drawH -= drawH % context->d.height;
} }
glViewport(padW, padH, drawW, drawH); glViewport(padW, padH, drawW, drawH);
if (shader->blend) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
glClear(GL_COLOR_BUFFER_BIT);
}
if (shader->tex && (shader->width <= 0 || shader->height <= 0)) { if (shader->tex && (shader->width <= 0 || shader->height <= 0)) {
GLint oldTex; GLint oldTex;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex); glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex);
@ -316,6 +318,7 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) { void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
struct mGLES2Context* context = (struct mGLES2Context*) v; struct mGLES2Context* context = (struct mGLES2Context*) v;
glBindTexture(GL_TEXTURE_2D, context->tex); glBindTexture(GL_TEXTURE_2D, context->tex);
glPixelStorei(GL_UNPACK_ROW_LENGTH, toPow2(v->width));
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5 #ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);

View File

@ -25,6 +25,7 @@
#include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/renderers/tile-cache.h> #include <mgba/internal/gb/renderers/tile-cache.h>
#endif #endif
#include <mgba-util/math.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
using namespace QGBA; using namespace QGBA;
@ -38,9 +39,11 @@ CoreController::CoreController(mCore* core, QObject* parent)
m_threadContext.core = core; m_threadContext.core = core;
m_threadContext.userData = this; m_threadContext.userData = this;
QSize size = screenDimensions(); QSize size(256, 512);
m_buffers[0].resize(size.width() * size.height() * sizeof(color_t)); m_buffers[0].resize(toPow2(size.width()) * size.height() * sizeof(color_t));
m_buffers[1].resize(size.width() * size.height() * sizeof(color_t)); m_buffers[1].resize(toPow2(size.width()) * size.height() * sizeof(color_t));
m_buffers[0].fill(0xFF);
m_buffers[1].fill(0xFF);
m_activeBuffer = &m_buffers[0]; m_activeBuffer = &m_buffers[0];
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width()); m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width());
@ -81,7 +84,15 @@ CoreController::CoreController(mCore* core, QObject* parent)
} }
controller->m_resetActions.clear(); controller->m_resetActions.clear();
controller->m_activeBuffer->fill(0xFF); QSize size = controller->screenDimensions();
controller->m_buffers[0].resize(toPow2(size.width()) * size.height() * sizeof(color_t));
controller->m_buffers[1].resize(toPow2(size.width()) * size.height() * sizeof(color_t));
controller->m_buffers[0].fill(0xFF);
controller->m_buffers[1].fill(0xFF);
controller->m_activeBuffer = &controller->m_buffers[0];
context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer->data()), toPow2(size.width()));
controller->finishFrame(); controller->finishFrame();
}; };
@ -712,7 +723,7 @@ void CoreController::finishFrame() {
if (m_activeBuffer == m_completeBuffer) { if (m_activeBuffer == m_completeBuffer) {
m_activeBuffer = &m_buffers[1]; m_activeBuffer = &m_buffers[1];
} }
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), screenDimensions().width()); m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), toPow2(screenDimensions().width()));
for (auto& action : m_frameActions) { for (auto& action : m_frameActions) {
action(); action();

View File

@ -14,6 +14,7 @@
#include <QTimer> #include <QTimer>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba-util/math.h>
#ifdef BUILD_GL #ifdef BUILD_GL
#include "platform/opengl/gl.h" #include "platform/opengl/gl.h"
#endif #endif
@ -390,7 +391,7 @@ void PainterGL::enqueue(const uint32_t* backing) {
buffer = m_free.takeLast(); buffer = m_free.takeLast();
} }
QSize size = m_context->screenDimensions(); QSize size = m_context->screenDimensions();
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); memcpy(buffer, backing, toPow2(size.width()) * size.height() * BYTES_PER_PIXEL);
m_queue.enqueue(buffer); m_queue.enqueue(buffer);
m_mutex.unlock(); m_mutex.unlock();
} }

View File

@ -11,6 +11,7 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#include <mgba-util/math.h>
using namespace QGBA; using namespace QGBA;
@ -56,12 +57,12 @@ void DisplayQt::framePosted() {
} }
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5 #ifdef COLOR_5_6_5
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB16); m_backing = QImage(reinterpret_cast<const uchar*>(buffer), toPow2(m_width), m_height, QImage::Format_RGB16);
#else #else
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB555); m_backing = QImage(reinterpret_cast<const uchar*>(buffer), toPow2(m_width), m_height, QImage::Format_RGB555);
#endif #endif
#else #else
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_ARGB32); m_backing = QImage(reinterpret_cast<const uchar*>(buffer), toPow2(m_width), m_height, QImage::Format_ARGB32);
m_backing = m_backing.convertToFormat(QImage::Format_RGB32); m_backing = m_backing.convertToFormat(QImage::Format_RGB32);
#endif #endif
} }

View File

@ -51,6 +51,7 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent)
// NB: Keep in sync with OverrideView.ui // NB: Keep in sync with OverrideView.ui
s_gbModelList.append(GB_MODEL_AUTODETECT); s_gbModelList.append(GB_MODEL_AUTODETECT);
s_gbModelList.append(GB_MODEL_DMG); s_gbModelList.append(GB_MODEL_DMG);
s_gbModelList.append(GB_MODEL_SGB);
s_gbModelList.append(GB_MODEL_CGB); s_gbModelList.append(GB_MODEL_CGB);
s_gbModelList.append(GB_MODEL_AGB); s_gbModelList.append(GB_MODEL_AGB);
} }

View File

@ -253,6 +253,11 @@
<string>Game Boy (DMG)</string> <string>Game Boy (DMG)</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>Super Game Boy (SGB)</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>Game Boy Color (CGB)</string> <string>Game Boy Color (CGB)</string>

View File

@ -9,6 +9,8 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#include <mgba-util/math.h>
#include "platform/opengl/gl.h" #include "platform/opengl/gl.h"
static void _doViewport(int w, int h, struct VideoBackend* v) { static void _doViewport(int w, int h, struct VideoBackend* v) {
@ -31,9 +33,10 @@ void mSDLGLCreate(struct mSDLRenderer* renderer) {
bool mSDLGLInit(struct mSDLRenderer* renderer) { bool mSDLGLInit(struct mSDLRenderer* renderer) {
mSDLGLCommonInit(renderer); mSDLGLCommonInit(renderer);
renderer->outputBuffer = malloc(renderer->width * renderer->height * BYTES_PER_PIXEL); size_t size = toPow2(renderer->width) * renderer->height * BYTES_PER_PIXEL;
memset(renderer->outputBuffer, 0, renderer->width * renderer->height * BYTES_PER_PIXEL); renderer->outputBuffer = malloc(size);
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width); memset(renderer->outputBuffer, 0, size);
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, toPow2(renderer->width));
mGLContextCreate(&renderer->gl); mGLContextCreate(&renderer->gl);
renderer->gl.d.user = renderer; renderer->gl.d.user = renderer;
@ -64,6 +67,9 @@ void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) {
renderer->player.windowUpdated = 0; renderer->player.windowUpdated = 0;
} }
#endif #endif
if (renderer->width != v->width || renderer->height != v->height) {
renderer->gl.d.setDimensions(&renderer->gl.d, renderer->width, renderer->height);
}
} }
if (mCoreSyncWaitFrameStart(&context->impl->sync)) { if (mCoreSyncWaitFrameStart(&context->impl->sync)) {

View File

@ -98,13 +98,14 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
mSDLGLCommonInit(renderer); mSDLGLCommonInit(renderer);
#endif #endif
size_t size = toPow2(renderer->width) * renderer->height * BYTES_PER_PIXEL;
#ifndef __APPLE__ #ifndef __APPLE__
renderer->outputBuffer = memalign(16, renderer->width * renderer->height * BYTES_PER_PIXEL); renderer->outputBuffer = memalign(16, size);
#else #else
posix_memalign((void**) &renderer->outputBuffer, 16, renderer->width * renderer->height * BYTES_PER_PIXEL); posix_memalign((void**) &renderer->outputBuffer, 16, size);
#endif #endif
memset(renderer->outputBuffer, 0, renderer->width * renderer->height * BYTES_PER_PIXEL); memset(renderer->outputBuffer, 0, size);
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width); renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, toPow2(renderer->width));
mGLES2ContextCreate(&renderer->gl2); mGLES2ContextCreate(&renderer->gl2);
renderer->gl2.d.user = renderer; renderer->gl2.d.user = renderer;

View File

@ -220,6 +220,13 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
bool didFail = !mCoreThreadStart(&thread); bool didFail = !mCoreThreadStart(&thread);
if (!didFail) { if (!didFail) {
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
renderer->core->desiredVideoDimensions(renderer->core, &renderer->width, &renderer->height);
unsigned width = renderer->width * renderer->ratio;
unsigned height = renderer->height * renderer->ratio;
if (width != (unsigned) renderer->viewportWidth && height != (unsigned) renderer->viewportHeight) {
SDL_SetWindowSize(renderer->window, width, height);
renderer->player.windowUpdated = 1;
}
mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver); mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver);
mSDLSuspendScreensaver(&renderer->events); mSDLSuspendScreensaver(&renderer->events);
#endif #endif