diff --git a/CHANGES b/CHANGES index 1d980f26a..54f8fb1f7 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ Features: - 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 diff --git a/include/mgba/internal/gb/gb.h b/include/mgba/internal/gb/gb.h index 37050e613..1d222211a 100644 --- a/include/mgba/internal/gb/gb.h +++ b/include/mgba/internal/gb/gb.h @@ -44,6 +44,34 @@ enum GBIRQVector { 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; @@ -76,6 +104,10 @@ struct GB { int32_t sramDirtAge; bool sramMaskWriteback; + int sgbBit; + int currentSgbBits; + uint8_t sgbPacket[16]; + struct mCoreCallbacksList coreCallbacks; struct mAVStream* stream; diff --git a/include/mgba/internal/gb/renderers/software.h b/include/mgba/internal/gb/renderers/software.h index 038c660b8..875973774 100644 --- a/include/mgba/internal/gb/renderers/software.h +++ b/include/mgba/internal/gb/renderers/software.h @@ -34,6 +34,9 @@ struct GBVideoSoftwareRenderer { GBRegisterLCDC lcdc; enum GBModel model; + + int sgbTransfer; + uint8_t sgbPacket[16]; }; void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*); diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index 440a90ccf..539ce2253 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -30,7 +30,11 @@ enum { 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 @@ struct GBVideoRenderer { 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); @@ -73,6 +85,11 @@ struct GBVideoRenderer { union GBOAM* oam; struct mTileCache* cache; + uint8_t* sgbCharRam; + uint8_t* sgbMapRam; + uint8_t* sgbPalRam; + int sgbRenderMode; + bool disableBG; bool disableOBJ; 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 GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data); + struct GBSerializedState; void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state); void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state); diff --git a/src/gb/core.c b/src/gb/core.c index 2e50324df..aa2b61e87 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -190,9 +190,14 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf } 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) { diff --git a/src/gb/extra/proxy.c b/src/gb/extra/proxy.c index 2b015ae6c..1f68ee0ee 100644 --- a/src/gb/extra/proxy.c +++ b/src/gb/extra/proxy.c @@ -10,10 +10,12 @@ #include #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 GB 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 @@ void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) { 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 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD 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: @@ -179,6 +188,14 @@ uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, 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) { struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address); diff --git a/src/gb/gb.c b/src/gb/gb.c index 79d438df6..66c8c2aa6 100644 --- a/src/gb/gb.c +++ b/src/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 @@ void GBReset(struct LR35902Core* cpu) { 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 @@ void GBReset(struct LR35902Core* cpu) { 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 @@ void GBDetectModel(struct GB* gb) { 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 @@ void GBDetectModel(struct GB* gb) { 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; } diff --git a/src/gb/io.c b/src/gb/io.c index b4c42f7f9..f28ba4d76 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -105,6 +105,33 @@ static const uint8_t _registerMask[] = { [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 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t 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; diff --git a/src/gb/overrides.c b/src/gb/overrides.c index d8265800b..8efd4c1b9 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -54,7 +54,7 @@ bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverri 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 diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 1a6688578..f2235c946 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -7,11 +7,13 @@ #include #include +#include #include 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 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 _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) { 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 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G softwareRenderer->currentWy = 0; softwareRenderer->wx = 0; softwareRenderer->model = model; + softwareRenderer->sgbTransfer = 0; } static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { @@ -96,6 +157,11 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* 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 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y); } } - color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; - 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]; + size_t sgbOffset = 0; + if (softwareRenderer->model == GB_MODEL_SGB) { + sgbOffset = softwareRenderer->outputBufferStride * 40 + 48; } - for (; x < endX; ++x) { - row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F]; + color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y + sgbOffset]; + 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) { ++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) { @@ -201,6 +354,34 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) 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; } diff --git a/src/gb/video.c b/src/gb/video.c index 830f71c30..61d38c0fd 100644 --- a/src/gb/video.c +++ b/src/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 @@ static struct GBVideoRenderer dummyRenderer = { 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 @@ void GBVideoInit(struct GBVideo* video) { 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 @@ void GBVideoReset(struct GBVideo* video) { 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 GBVideoReset(struct GBVideo* video) { 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 GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { 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 @@ void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t 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) { UNUSED(renderer); UNUSED(model); @@ -493,6 +612,11 @@ static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* re return value; } +static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) { + UNUSED(renderer); + UNUSED(data); +} + static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) { if (renderer->cache) { mTileCacheWriteVRAM(renderer->cache, address); diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 888825735..17a28175d 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -285,7 +285,7 @@ static void _setup(struct mGUIRunner* runner) { _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; diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index d19c5006f..934fca788 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -390,7 +390,7 @@ bool retro_load_game(const struct retro_game_info* game) { 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); diff --git a/src/platform/openemu/mGBAGameCore.m b/src/platform/openemu/mGBAGameCore.m index 1ddf5482f..9b2b93b05 100644 --- a/src/platform/openemu/mGBAGameCore.m +++ b/src/platform/openemu/mGBAGameCore.m @@ -65,13 +65,9 @@ }; 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; } diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index 87b679c6b..9f259292e 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -110,12 +110,12 @@ void mGLContextPostFrame(struct VideoBackend* v, const void* frame) { 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 } diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index 2dfa7448b..f7db81a52 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -190,13 +191,6 @@ static void mGLES2ContextClear(struct VideoBackend* v) { 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 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) { 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 mGLES2ContextDrawFrame(struct VideoBackend* v) { 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); diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 35332afa6..286f3dc4d 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -25,6 +25,7 @@ #include #include #endif +#include #include using namespace QGBA; @@ -38,9 +39,11 @@ CoreController::CoreController(mCore* core, QObject* parent) 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(m_activeBuffer->data()), size.width()); @@ -81,7 +84,15 @@ CoreController::CoreController(mCore* core, QObject* parent) } 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(controller->m_activeBuffer->data()), toPow2(size.width())); + controller->finishFrame(); }; @@ -712,7 +723,7 @@ void CoreController::finishFrame() { if (m_activeBuffer == m_completeBuffer) { m_activeBuffer = &m_buffers[1]; } - m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast(m_activeBuffer->data()), screenDimensions().width()); + m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast(m_activeBuffer->data()), toPow2(screenDimensions().width())); for (auto& action : m_frameActions) { action(); diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 07ee75dc7..113f859bd 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -14,6 +14,7 @@ #include #include +#include #ifdef BUILD_GL #include "platform/opengl/gl.h" #endif @@ -390,7 +391,7 @@ void PainterGL::enqueue(const uint32_t* backing) { 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(); } diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index 0c63e8b78..45ba297a9 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -11,6 +11,7 @@ #include #include +#include using namespace QGBA; @@ -56,12 +57,12 @@ void DisplayQt::framePosted() { } #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5 - m_backing = QImage(reinterpret_cast(buffer), m_width, m_height, QImage::Format_RGB16); + m_backing = QImage(reinterpret_cast(buffer), toPow2(m_width), m_height, QImage::Format_RGB16); #else - m_backing = QImage(reinterpret_cast(buffer), m_width, m_height, QImage::Format_RGB555); + m_backing = QImage(reinterpret_cast(buffer), toPow2(m_width), m_height, QImage::Format_RGB555); #endif #else - m_backing = QImage(reinterpret_cast(buffer), m_width, m_height, QImage::Format_ARGB32); + m_backing = QImage(reinterpret_cast(buffer), toPow2(m_width), m_height, QImage::Format_ARGB32); m_backing = m_backing.convertToFormat(QImage::Format_RGB32); #endif } diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index 618041868..ac380cc9c 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -51,6 +51,7 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent) // 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); } diff --git a/src/platform/qt/OverrideView.ui b/src/platform/qt/OverrideView.ui index 6c9b4a772..9ab598a8e 100644 --- a/src/platform/qt/OverrideView.ui +++ b/src/platform/qt/OverrideView.ui @@ -253,6 +253,11 @@ Game Boy (DMG) + + + Super Game Boy (SGB) + + Game Boy Color (CGB) diff --git a/src/platform/sdl/gl-sdl.c b/src/platform/sdl/gl-sdl.c index 2919fc2b3..d4fd0ff01 100644 --- a/src/platform/sdl/gl-sdl.c +++ b/src/platform/sdl/gl-sdl.c @@ -9,6 +9,8 @@ #include #include +#include + #include "platform/opengl/gl.h" static void _doViewport(int w, int h, struct VideoBackend* v) { @@ -31,9 +33,10 @@ void mSDLGLCreate(struct mSDLRenderer* renderer) { 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 @@ void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) { 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)) { diff --git a/src/platform/sdl/gles2-sdl.c b/src/platform/sdl/gles2-sdl.c index 9f9778c3f..b80ab33c5 100644 --- a/src/platform/sdl/gles2-sdl.c +++ b/src/platform/sdl/gles2-sdl.c @@ -98,13 +98,14 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) { 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; diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 3cdf30856..3ba08f9dc 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -220,6 +220,13 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { 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