mirror of https://github.com/mgba-emu/mgba.git
GB: Super Game Boy borders
This commit is contained in:
parent
55679df8fc
commit
36c1fb59be
1
CHANGES
1
CHANGES
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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*);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
17
src/gb/gb.c
17
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_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;
|
||||||
}
|
}
|
||||||
|
|
34
src/gb/io.c
34
src/gb/io.c
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
126
src/gb/video.c
126
src/gb/video.c
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue