all repos — mgba @ 36c1fb59be4710543c4a5476a554757efe306ccd

mGBA Game Boy Advance Emulator

GB: Super Game Boy borders
Vicki Pfau vi@endrift.com
Tue, 01 Aug 2017 19:01:44 -0700
commit

36c1fb59be4710543c4a5476a554757efe306ccd

parent

55679df8fc49fca3d5f8584d16b90e9bb734a922

M CHANGESCHANGES

@@ -4,6 +4,7 @@ - ELF support

- Game Boy Camera support - Qt: Set default Game Boy colors - Game Boy Printer support + - Super Game Boy borders Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - Python: Fix importing .gb or .gba before .core
M include/mgba/internal/gb/gb.hinclude/mgba/internal/gb/gb.h

@@ -44,6 +44,34 @@ GB_VECTOR_SIO = 0x58,

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 mCoreSync; struct mAVStream;

@@ -75,6 +103,10 @@ uint32_t sramSize;

int sramDirty; int32_t sramDirtAge; bool sramMaskWriteback; + + int sgbBit; + int currentSgbBits; + uint8_t sgbPacket[16]; struct mCoreCallbacksList coreCallbacks; struct mAVStream* stream;
M include/mgba/internal/gb/renderers/software.hinclude/mgba/internal/gb/renderers/software.h

@@ -34,6 +34,9 @@ uint8_t currentWy;

GBRegisterLCDC lcdc; enum GBModel model; + + int sgbTransfer; + uint8_t sgbPacket[16]; }; void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);
M include/mgba/internal/gb/video.hinclude/mgba/internal/gb/video.h

@@ -30,7 +30,11 @@ GB_VIDEO_MODE_1_LENGTH = 65664,

GB_VIDEO_TOTAL_LENGTH = 70224, 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);

@@ -41,6 +45,13 @@ DECL_BIT(GBObjAttributes, XFlip, 5);

DECL_BIT(GBObjAttributes, YFlip, 6); 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 { uint8_t y; uint8_t x;

@@ -59,6 +70,7 @@ void (*init)(struct GBVideoRenderer* renderer, enum GBModel model);

void (*deinit)(struct GBVideoRenderer* renderer); 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 (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value); void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam);

@@ -72,6 +84,11 @@

uint8_t* vram; union GBOAM* oam; struct mTileCache* cache; + + uint8_t* sgbCharRam; + uint8_t* sgbMapRam; + uint8_t* sgbPalRam; + int sgbRenderMode; bool disableBG; bool disableOBJ;

@@ -145,6 +162,8 @@ void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value);

void GBVideoSwitchBank(struct GBVideo* video, uint8_t value); void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color); + +void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data); struct GBSerializedState; void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
M src/gb/core.csrc/gb/core.c

@@ -190,9 +190,14 @@ #endif

} static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) { - UNUSED(core); - *width = GB_VIDEO_HORIZONTAL_PIXELS; - *height = GB_VIDEO_VERTICAL_PIXELS; + struct GB* gb = core->board; + if (!gb || gb->model != GB_MODEL_SGB) { + *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) {
M src/gb/extra/proxy.csrc/gb/extra/proxy.c

@@ -10,10 +10,12 @@ #include <mgba/internal/gb/gb.h>

#include <mgba/internal/gb/io.h> #define BUFFER_OAM 1 +#define BUFFER_SGB 2 static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer); 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 GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value);

@@ -30,6 +32,7 @@ void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend) {

renderer->d.init = GBVideoProxyRendererInit; renderer->d.deinit = GBVideoProxyRendererDeinit; renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister; + renderer->d.writeSGBPacket = GBVideoProxyRendererWriteSGBPacket; renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM; renderer->d.writeOAM = GBVideoProxyRendererWriteOAM; renderer->d.writePalette = GBVideoProxyRendererWritePalette;

@@ -111,6 +114,7 @@ }

static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) { struct GBVideoProxyRenderer* proxyRenderer = logger->context; + uint8_t sgbPacket[16]; switch (item->type) { case DIRTY_REGISTER: proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);

@@ -154,6 +158,11 @@ proxyRenderer->oamMax = 0;

return false; } 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; case DIRTY_FLUSH:

@@ -177,6 +186,14 @@ if (!proxyRenderer->logger->block) {

proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, 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) {
M src/gb/gb.csrc/gb/gb.c

@@ -28,6 +28,7 @@ static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};

#define DMG_BIOS_CHECKSUM 0xC2F5CC97 #define DMG_2_BIOS_CHECKSUM 0x59C8598E +#define SGB_BIOS_CHECKSUM 0xEC8A83B9 #define CGB_BIOS_CHECKSUM 0x41884E46 mLOG_DEFINE_CATEGORY(GB, "GB", "gb");

@@ -384,6 +385,7 @@ bool GBIsBIOS(struct VFile* vf) {

switch (_GBBiosCRC32(vf)) { case DMG_BIOS_CHECKSUM: case DMG_2_BIOS_CHECKSUM: + case SGB_BIOS_CHECKSUM: case CGB_BIOS_CHECKSUM: return true; default:

@@ -425,11 +427,11 @@ cpu->d = 0;

if (!gb->biosVf) { switch (gb->model) { - case GB_MODEL_DMG: - // TODO: SGB - case GB_MODEL_SGB: case GB_MODEL_AUTODETECT: // Silence warnings gb->model = GB_MODEL_DMG; + // TODO: SGB registers + case GB_MODEL_SGB: + case GB_MODEL_DMG: cpu->a = 1; cpu->f.packed = 0xB0; cpu->c = 0x13;

@@ -467,6 +469,10 @@ gb->memory.romSize = gb->yankedRomSize;

gb->yankedRomSize = 0; } + gb->sgbBit = -1; + gb->currentSgbBits = 0; + memset(gb->sgbPacket, 0, sizeof(gb->sgbPacket)); + mTimingClear(&gb->timing); GBMemoryReset(gb);

@@ -491,6 +497,9 @@ case DMG_BIOS_CHECKSUM:

case DMG_2_BIOS_CHECKSUM: gb->model = GB_MODEL_DMG; break; + case SGB_BIOS_CHECKSUM: + gb->model = GB_MODEL_SGB; + break; case CGB_BIOS_CHECKSUM: gb->model = GB_MODEL_CGB; break;

@@ -503,6 +512,8 @@ if (gb->model == GB_MODEL_AUTODETECT && gb->memory.rom) {

const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; if (cart->cgb & 0x80) { gb->model = GB_MODEL_CGB; + } else if (cart->sgb == 0x03 && cart->oldLicensee == 0x33) { + gb->model = GB_MODEL_SGB; } else { gb->model = GB_MODEL_DMG; }
M src/gb/io.csrc/gb/io.c

@@ -105,6 +105,33 @@ [REG_UNK75] = 0x8F,

[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) { memset(gb->memory.io, 0, sizeof(gb->memory.io)); }

@@ -341,6 +368,10 @@ gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1] = value;

} break; case REG_JOYP: + if (gb->model == GB_MODEL_SGB) { + _writeSGBBits(gb, (value >> 4) & 3); + } + break; case REG_TIMA: case REG_TMA: // Handled transparently by the registers

@@ -453,7 +484,8 @@ static uint8_t _readKeys(struct GB* gb) {

uint8_t keys = *gb->keySource; switch (gb->memory.io[REG_JOYP] & 0x30) { case 0x30: - keys = 0; + // TODO: Increment + keys = gb->model == GB_MODEL_SGB ? 0xF : 0; break; case 0x20: keys >>= 4;
M src/gb/overrides.csrc/gb/overrides.c

@@ -54,7 +54,7 @@ found = true;

override->model = GB_MODEL_AGB; } else if (strcasecmp(model, "SGB") == 0) { found = true; - override->model = GB_MODEL_DMG; // TODO + override->model = GB_MODEL_SGB; } else if (strcasecmp(model, "MGB") == 0) { found = true; override->model = GB_MODEL_DMG; // TODO
M src/gb/renderers/software.csrc/gb/renderers/software.c

@@ -7,11 +7,13 @@ #include <mgba/internal/gb/renderers/software.h>

#include <mgba/core/tile-cache.h> #include <mgba/internal/gb/io.h> +#include <mgba-util/math.h> #include <mgba-util/memory.h> static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer); 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 GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);

@@ -25,9 +27,13 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy);

static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y); static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) { + size_t sgbOffset = 0; + if (renderer->model == GB_MODEL_SGB) { + sgbOffset = renderer->outputBufferStride * 40 + 48; + } int 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; for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) { row[x + 0] = renderer->palette[0];

@@ -38,10 +44,64 @@ }

} } +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) { renderer->d.init = GBVideoSoftwareRendererInit; renderer->d.deinit = GBVideoSoftwareRendererDeinit; renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister; + renderer->d.writeSGBPacket = GBVideoSoftwareRendererWriteSGBPacket; renderer->d.writePalette = GBVideoSoftwareRendererWritePalette; renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM; renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM;

@@ -67,6 +127,7 @@ softwareRenderer->wy = 0;

softwareRenderer->currentWy = 0; softwareRenderer->wx = 0; softwareRenderer->model = model; + softwareRenderer->sgbTransfer = 0; } static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {

@@ -96,6 +157,11 @@ }

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) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; #ifdef COLOR_16_BIT

@@ -167,20 +233,60 @@ for (i = 0; i < oamMax; ++i) {

GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y); } } - color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; + size_t sgbOffset = 0; + if (softwareRenderer->model == GB_MODEL_SGB) { + sgbOffset = softwareRenderer->outputBufferStride * 40 + 48; + } + color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y + sgbOffset]; int x; - 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]; + 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 @@ struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;

if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) { ++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) {

@@ -200,6 +353,34 @@ softwareRenderer->temporaryBuffer = 0;

} if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) { _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; }
M src/gb/video.csrc/gb/video.c

@@ -18,6 +18,7 @@

static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer); 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 GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);

@@ -39,6 +40,7 @@ static struct GBVideoRenderer dummyRenderer = {

.init = GBVideoDummyRendererInit, .deinit = GBVideoDummyRendererDeinit, .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, + .writeSGBPacket = GBVideoDummyRendererWriteSGBPacket, .writeVRAM = GBVideoDummyRendererWriteVRAM, .writeOAM = GBVideoDummyRendererWriteOAM, .writePalette = GBVideoDummyRendererWritePalette,

@@ -52,6 +54,7 @@

void GBVideoInit(struct GBVideo* video) { video->renderer = &dummyRenderer; video->renderer->cache = NULL; + video->renderer->sgbRenderMode = 0; video->vram = 0; video->frameskip = 0;

@@ -68,6 +71,10 @@ video->dmgPalette[0] = 0x7FFF;

video->dmgPalette[1] = 0x56B5; video->dmgPalette[2] = 0x294A; video->dmgPalette[3] = 0x0000; + + video->renderer->sgbCharRam = NULL; + video->renderer->sgbMapRam = NULL; + video->renderer->sgbPalRam = NULL; } void GBVideoReset(struct GBVideo* video) {

@@ -89,6 +96,12 @@ memset(&video->oam, 0, sizeof(video->oam));

video->renderer->oam = &video->oam; 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->init(video->renderer, video->p->model); }

@@ -96,11 +109,27 @@

void GBVideoDeinit(struct GBVideo* video) { GBVideoAssociateRenderer(video, &dummyRenderer); 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) { video->renderer->deinit(video->renderer); 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; renderer->vram = video->vram; video->renderer->init(video->renderer, video->p->model);

@@ -372,7 +401,7 @@

void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) { GBRegisterSTAT oldStat = video->stat; 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? video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p);

@@ -476,6 +505,96 @@ }

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) { UNUSED(renderer); UNUSED(model);

@@ -491,6 +610,11 @@ static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {

UNUSED(renderer); UNUSED(address); return value; +} + +static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) { + UNUSED(renderer); + UNUSED(data); } static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
M src/platform/3ds/main.csrc/platform/3ds/main.c

@@ -285,7 +285,7 @@ _map3DSKey(&runner->core->inputMap, KEY_RIGHT, GBA_KEY_RIGHT);

_map3DSKey(&runner->core->inputMap, KEY_L, GBA_KEY_L); _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); unsigned mode;
M src/platform/libretro/libretro.csrc/platform/libretro/libretro.c

@@ -390,7 +390,7 @@ mCoreInitConfig(core, NULL);

core->init(core); 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->setAudioBufferSize(core, SAMPLES);
M src/platform/openemu/mGBAGameCore.msrc/platform/openemu/mGBAGameCore.m

@@ -65,13 +65,9 @@ .useBios = true,

}; mCoreConfigLoadDefaults(&core->config, &opts); 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); - cheatSets = [[NSMutableDictionary alloc] init]; }

@@ -108,6 +104,15 @@ }

mCoreAutoloadSave(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; }
M src/platform/opengl/gl.csrc/platform/opengl/gl.c

@@ -110,12 +110,12 @@ struct mGLContext* context = (struct mGLContext*) v;

glBindTexture(GL_TEXTURE_2D, context->tex); #ifdef COLOR_16_BIT #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 - 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 #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 }
M src/platform/opengl/gles2.csrc/platform/opengl/gles2.c

@@ -8,6 +8,7 @@

#include <mgba/core/log.h> #include <mgba-util/configuration.h> #include <mgba-util/formatting.h> +#include <mgba-util/math.h> #include <mgba-util/vector.h> #include <mgba-util/vfs.h>

@@ -190,13 +191,6 @@

void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) { GLint viewport[4]; 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); int drawW = shader->width;

@@ -222,6 +216,14 @@ drawW -= drawW % context->d.width;

drawH -= drawH % context->d.height; } 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)) { GLint oldTex; glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex);

@@ -316,6 +318,7 @@

void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) { struct mGLES2Context* context = (struct mGLES2Context*) v; glBindTexture(GL_TEXTURE_2D, context->tex); + glPixelStorei(GL_UNPACK_ROW_LENGTH, toPow2(v->width)); #ifdef COLOR_16_BIT #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);
M src/platform/qt/CoreController.cppsrc/platform/qt/CoreController.cpp

@@ -25,6 +25,7 @@ #ifdef M_CORE_GB

#include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/renderers/tile-cache.h> #endif +#include <mgba-util/math.h> #include <mgba-util/vfs.h> using namespace QGBA;

@@ -38,9 +39,11 @@ {

m_threadContext.core = core; m_threadContext.userData = this; - QSize size = screenDimensions(); - m_buffers[0].resize(size.width() * size.height() * sizeof(color_t)); - m_buffers[1].resize(size.width() * size.height() * sizeof(color_t)); + QSize size(256, 512); + m_buffers[0].resize(toPow2(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_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width());

@@ -81,7 +84,15 @@ action();

} 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(); };

@@ -712,7 +723,7 @@ m_activeBuffer = &m_buffers[0];

if (m_activeBuffer == m_completeBuffer) { 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) { action();
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -14,6 +14,7 @@ #include <QResizeEvent>

#include <QTimer> #include <mgba/core/core.h> +#include <mgba-util/math.h> #ifdef BUILD_GL #include "platform/opengl/gl.h" #endif

@@ -390,7 +391,7 @@ } else {

buffer = m_free.takeLast(); } 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_mutex.unlock(); }
M src/platform/qt/DisplayQt.cppsrc/platform/qt/DisplayQt.cpp

@@ -11,6 +11,7 @@ #include <QPainter>

#include <mgba/core/core.h> #include <mgba/core/thread.h> +#include <mgba-util/math.h> using namespace QGBA;

@@ -56,12 +57,12 @@ return;

} #ifdef COLOR_16_BIT #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 - 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 #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); #endif }
M src/platform/qt/OverrideView.cppsrc/platform/qt/OverrideView.cpp

@@ -51,6 +51,7 @@ if (s_gbModelList.isEmpty()) {

// NB: Keep in sync with OverrideView.ui s_gbModelList.append(GB_MODEL_AUTODETECT); s_gbModelList.append(GB_MODEL_DMG); + s_gbModelList.append(GB_MODEL_SGB); s_gbModelList.append(GB_MODEL_CGB); s_gbModelList.append(GB_MODEL_AGB); }
M src/platform/qt/OverrideView.uisrc/platform/qt/OverrideView.ui

@@ -255,6 +255,11 @@ </property>

</item> <item> <property name="text"> + <string>Super Game Boy (SGB)</string> + </property> + </item> + <item> + <property name="text"> <string>Game Boy Color (CGB)</string> </property> </item>
M src/platform/sdl/gl-sdl.csrc/platform/sdl/gl-sdl.c

@@ -9,6 +9,8 @@ #include "gl-common.h"

#include <mgba/core/core.h> #include <mgba/core/thread.h> +#include <mgba-util/math.h> + #include "platform/opengl/gl.h" static void _doViewport(int w, int h, struct VideoBackend* v) {

@@ -31,9 +33,10 @@

bool mSDLGLInit(struct mSDLRenderer* renderer) { mSDLGLCommonInit(renderer); - renderer->outputBuffer = malloc(renderer->width * renderer->height * BYTES_PER_PIXEL); - memset(renderer->outputBuffer, 0, renderer->width * renderer->height * BYTES_PER_PIXEL); - renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width); + size_t size = toPow2(renderer->width) * renderer->height * BYTES_PER_PIXEL; + renderer->outputBuffer = malloc(size); + memset(renderer->outputBuffer, 0, size); + renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, toPow2(renderer->width)); mGLContextCreate(&renderer->gl); renderer->gl.d.user = renderer;

@@ -64,6 +67,9 @@ _doViewport(renderer->viewportWidth, renderer->viewportHeight, v);

renderer->player.windowUpdated = 0; } #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)) {
M src/platform/sdl/gles2-sdl.csrc/platform/sdl/gles2-sdl.c

@@ -98,13 +98,14 @@ #else

mSDLGLCommonInit(renderer); #endif + size_t size = toPow2(renderer->width) * renderer->height * BYTES_PER_PIXEL; #ifndef __APPLE__ - renderer->outputBuffer = memalign(16, renderer->width * renderer->height * BYTES_PER_PIXEL); + renderer->outputBuffer = memalign(16, size); #else - posix_memalign((void**) &renderer->outputBuffer, 16, renderer->width * renderer->height * BYTES_PER_PIXEL); + posix_memalign((void**) &renderer->outputBuffer, 16, size); #endif - memset(renderer->outputBuffer, 0, renderer->width * renderer->height * BYTES_PER_PIXEL); - renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width); + memset(renderer->outputBuffer, 0, size); + renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, toPow2(renderer->width)); mGLES2ContextCreate(&renderer->gl2); renderer->gl2.d.user = renderer;
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -220,6 +220,13 @@

bool didFail = !mCoreThreadStart(&thread); if (!didFail) { #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); mSDLSuspendScreensaver(&renderer->events); #endif