Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2021-06-11 19:10:42 -07:00
commit 4db6d34e12
28 changed files with 6030 additions and 373 deletions

View File

@ -38,6 +38,8 @@ Features:
- e-Reader card scanning
- WebP and APNG recording
- Separate overrides for GBC games that can also run on SGB or regular GB
- Game Boy Player features can be enabled by default for all compatible games
- Frame viewer support for Game Boy
- Mute option in homebrew ports
- Status indicators for fast-forward and mute in homebrew ports
- Read-only support for MBC6 flash memory
@ -46,7 +48,7 @@ Features:
- Command scripts for CLI debugger (by ahigerd)
- ARM disassembler now resolves addresses to symbol names
- Add Game Boy Player feature support to ports
- Individual window types can now be toggled in GBA debugging views
- Individual window types can now be toggled in debugging views
Emulation fixes:
- ARM: Fix ALU reading PC after shifting
- ARM: Fix STR storing PC after address calculation
@ -72,6 +74,7 @@ Emulation fixes:
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190)
- GBA Memory: Improve robustness of Matrix memory support
- GBA Memory: Mark Famicom Mini games 22 through 28 as non-mirroring
- GBA Memory: Return correct byte for odd ROM open bus addresses
- GBA SIO: Fix copying Normal mode transfer values
- GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800)
- GBA SIO: Fix deseralizing SIO registers
@ -79,6 +82,7 @@ Emulation fixes:
- GBA Video: Fix Hblank timing
- GBA Video: Implement green swap (fixes mgba.io/i/1609)
- GBA Video: Emulate sprite cycle limits in OpenGL renderer (fixes mgba.io/i/1635)
- GBA Video: Fix OBJWIN erratic rendering in OpenGL renderer
- SM83: Emulate HALT bug
Other fixes:
- 3DS: Fix thread cleanup
@ -88,8 +92,10 @@ Other fixes:
- Core: Fix threading improperly setting paused state while interrupted
- Debugger: Don't skip undefined instructions when debugger attached
- Debugger: Close trace log when done tracing
- Debugger: Fix change watchpoints (fixes mgba.io/i/1947)
- FFmpeg: Fix some small memory leaks
- FFmpeg: Fix encoding of time base
- GB Video: Fix SGB video logs
- mGUI: Don't attempt to preload files larger than can fit in RAM
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
- Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769)

View File

@ -41,16 +41,25 @@ typedef uint32_t color_t;
#define M_COLOR_GREEN 0x0000FF00
#define M_COLOR_BLUE 0x00FF0000
#define M_COLOR_ALPHA 0xFF000000
#define M_COLOR_WHITE 0x00FFFFFF
#define M_RGB8_TO_NATIVE(X) (((X) & 0x00FF00) | (((X) & 0x0000FF) << 16) | (((X) & 0xFF0000) >> 16))
#elif defined(COLOR_5_6_5)
#define M_COLOR_RED 0x001F
#define M_COLOR_GREEN 0x07E0
#define M_COLOR_BLUE 0xF800
#define M_COLOR_ALPHA 0x0000
#define M_COLOR_WHITE 0xFFDF
#define M_RGB8_TO_NATIVE(X) ((((X) & 0xF8) << 8) | (((X) & 0xFC00) >> 5) | (((X) & 0xF80000) >> 19))
#else
#define M_COLOR_RED 0x001F
#define M_COLOR_GREEN 0x03E0
#define M_COLOR_BLUE 0x7C00
#define M_COLOR_ALPHA 0x1000
#define M_COLOR_WHITE 0x7FFF
#define M_RGB8_TO_NATIVE(X) M_RGB8_TO_BGR5(X)
#endif
#ifndef PYCPARSE
@ -70,6 +79,68 @@ static inline color_t mColorFrom555(uint16_t value) {
#endif
return color;
}
ATTRIBUTE_UNUSED static unsigned mColorMix5Bit(int weightA, unsigned colorA, int weightB, unsigned colorB) {
unsigned c = 0;
unsigned a, b;
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
a = colorA & 0xF81F;
b = colorB & 0xF81F;
a |= (colorA & 0x7C0) << 16;
b |= (colorB & 0x7C0) << 16;
c = ((a * weightA + b * weightB) / 16);
if (c & 0x08000000) {
c = (c & ~0x0FC00000) | 0x07C00000;
}
if (c & 0x0020) {
c = (c & ~0x003F) | 0x001F;
}
if (c & 0x10000) {
c = (c & ~0x1F800) | 0xF800;
}
c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
#else
a = colorA & 0x7C1F;
b = colorB & 0x7C1F;
a |= (colorA & 0x3E0) << 16;
b |= (colorB & 0x3E0) << 16;
c = ((a * weightA + b * weightB) / 16);
if (c & 0x04000000) {
c = (c & ~0x07E00000) | 0x03E00000;
}
if (c & 0x0020) {
c = (c & ~0x003F) | 0x001F;
}
if (c & 0x8000) {
c = (c & ~0xF800) | 0x7C00;
}
c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
#endif
#else
a = colorA & 0xFF;
b = colorB & 0xFF;
c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
if (c & 0x00000100) {
c = 0x000000FF;
}
a = colorA & 0xFF00;
b = colorB & 0xFF00;
c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
if (c & 0x00010000) {
c = (c & 0x000000FF) | 0x0000FF00;
}
a = colorA & 0xFF0000;
b = colorB & 0xFF0000;
c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
if (c & 0x01000000) {
c = (c & 0x0000FFFF) | 0x00FF0000;
}
#endif
return c;
}
#endif
struct blip_t;

View File

@ -38,7 +38,8 @@ enum mWatchpointType {
WATCHPOINT_WRITE = 1,
WATCHPOINT_READ = 2,
WATCHPOINT_RW = 3,
WATCHPOINT_WRITE_CHANGE = 4,
WATCHPOINT_CHANGE = 4,
WATCHPOINT_WRITE_CHANGE = 5,
};
enum mBreakpointType {

View File

@ -10,6 +10,8 @@
CXX_GUARD_START
#include <mgba/core/interface.h>
enum GBModel {
GB_MODEL_AUTODETECT = 0xFF,
GB_MODEL_DMG = 0x00,

View File

@ -14,16 +14,22 @@ CXX_GUARD_START
#include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/video.h>
struct GBVideoRendererSprite {
struct GBObj obj;
int8_t index;
};
struct GBVideoSoftwareRenderer {
struct GBVideoRenderer d;
color_t* outputBuffer;
int outputBufferStride;
uint8_t row[GB_VIDEO_HORIZONTAL_PIXELS + 8];
// TODO: Implement the pixel FIFO
uint16_t row[GB_VIDEO_HORIZONTAL_PIXELS + 8];
color_t palette[128];
uint8_t lookup[64];
color_t palette[192];
uint8_t lookup[192];
uint32_t* temporaryBuffer;
@ -40,7 +46,7 @@ struct GBVideoSoftwareRenderer {
GBRegisterLCDC lcdc;
enum GBModel model;
struct GBObj obj[10];
struct GBVideoRendererSprite obj[GB_VIDEO_MAX_LINE_OBJ];
int objMax;
int16_t objOffsetX;
@ -54,6 +60,8 @@ struct GBVideoSoftwareRenderer {
uint8_t sgbPacket[128];
uint8_t sgbCommandHeader;
bool sgbBorders;
uint8_t lastHighlightAmount;
};
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);

View File

@ -437,6 +437,9 @@ struct GBSerializedState {
bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state);
void GBSerialize(struct GB* gb, struct GBSerializedState* state);
void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state);
void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state);
CXX_GUARD_END
#endif

View File

@ -31,6 +31,9 @@ enum {
GB_VIDEO_TOTAL_LENGTH = 70224,
GB_VIDEO_MAX_OBJ = 40,
GB_VIDEO_MAX_LINE_OBJ = 10,
GB_BASE_MAP = 0x1800,
GB_SIZE_MAP = 0x0400,
@ -63,8 +66,8 @@ struct GBObj {
};
union GBOAM {
struct GBObj obj[40];
uint8_t raw[160];
struct GBObj obj[GB_VIDEO_MAX_OBJ];
uint8_t raw[GB_VIDEO_MAX_OBJ * 4];
};
struct mCacheSet;
@ -99,6 +102,12 @@ struct GBVideoRenderer {
bool disableBG;
bool disableOBJ;
bool disableWIN;
bool highlightBG;
bool highlightOBJ[GB_VIDEO_MAX_OBJ];
bool highlightWIN;
color_t highlightColor;
uint8_t highlightAmount;
};
DECL_BITFIELD(GBRegisterLCDC, uint8_t);

View File

@ -52,15 +52,6 @@ struct GBAVideoSoftwareBackground {
};
enum {
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
GBA_COLOR_WHITE = 0xFFDF,
#else
GBA_COLOR_WHITE = 0x7FFF,
#endif
#else
GBA_COLOR_WHITE = 0x00FFFFFF,
#endif
OFFSET_PRIORITY = 30,
OFFSET_INDEX = 28,
};

View File

@ -106,17 +106,24 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
}
}
uint32_t oldValue;
switch (width + 1) {
case 1:
info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
break;
case 2:
info->type.wp.oldValue = debugger->originalMemory.load16(debugger->cpu, address, 0);
oldValue = debugger->originalMemory.load16(debugger->cpu, address, 0);
break;
case 4:
info->type.wp.oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0);
oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0);
break;
default:
continue;
}
if ((watchpoint->type & WATCHPOINT_CHANGE) && newValue == oldValue) {
continue;
}
info->type.wp.oldValue = oldValue;
info->type.wp.newValue = newValue;
info->address = address;
info->type.wp.watchType = watchpoint->type;

View File

@ -394,7 +394,7 @@ static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* rend
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
for (x = 0; x < softwareRenderer->masterEnd; ++x) {
row[x] = GBA_COLOR_WHITE;
row[x] = M_COLOR_WHITE;
}
return;
}
@ -559,7 +559,7 @@ static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int
switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
case 0:
for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
row[x] = GBA_COLOR_WHITE;
row[x] = M_COLOR_WHITE;
}
return;
case 1:
@ -631,7 +631,7 @@ static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int
switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
case 0:
for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
row[x] = GBA_COLOR_WHITE;
row[x] = M_COLOR_WHITE;
}
return;
case 1:

View File

@ -1188,6 +1188,9 @@ static bool _GBVLPLoadState(struct mCore* core, const void* buffer) {
GBVideoDeserialize(&gb->video, state);
GBIODeserialize(gb, state);
GBAudioReset(&gb->audio);
if (gb->model & GB_MODEL_SGB) {
GBSGBDeserialize(gb, state);
}
// Make sure CPU loop never spins
gb->memory.ie = 0;

View File

@ -48,6 +48,15 @@ void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GB
renderer->d.disableWIN = false;
renderer->d.disableOBJ = false;
renderer->d.highlightBG = false;
renderer->d.highlightWIN = false;
int i;
for (i = 0; i < GB_VIDEO_MAX_OBJ; ++i) {
renderer->d.highlightOBJ[i] = false;
}
renderer->d.highlightColor = M_COLOR_WHITE;
renderer->d.highlightAmount = 0;
renderer->logger->context = renderer;
renderer->logger->parsePacket = _parsePacket;
renderer->logger->vramBlock = _vramBlock;
@ -75,6 +84,17 @@ static void _reset(struct GBVideoProxyRenderer* proxyRenderer) {
mVideoLoggerRendererReset(proxyRenderer->logger);
}
static void _copyExtraState(struct GBVideoProxyRenderer* proxyRenderer) {
proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
proxyRenderer->backend->highlightBG = proxyRenderer->d.highlightBG;
proxyRenderer->backend->highlightWIN = proxyRenderer->d.highlightWIN;
memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ));
proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount;
proxyRenderer->backend->highlightColor = proxyRenderer->d.highlightColor;
}
void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
return;
@ -82,6 +102,12 @@ void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer
renderer->backend = video->renderer;
video->renderer = &renderer->d;
renderer->d.cache = renderer->backend->cache;
renderer->d.sgbRenderMode = renderer->backend->sgbRenderMode;
renderer->d.sgbCharRam = renderer->backend->sgbCharRam;
renderer->d.sgbMapRam = renderer->backend->sgbMapRam;
renderer->d.sgbPalRam = renderer->backend->sgbPalRam;
renderer->d.sgbAttributeFiles = renderer->backend->sgbAttributeFiles;
renderer->d.sgbAttributes = renderer->backend->sgbAttributes;
renderer->d.vram = video->vram;
renderer->d.oam = &video->oam;
_init(renderer);
@ -119,7 +145,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];
struct GBObj legacyBuffer[40];
struct GBObj legacyBuffer[GB_VIDEO_MAX_OBJ];
switch (item->type) {
case DIRTY_REGISTER:
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
@ -142,9 +168,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
}
break;
case DIRTY_SCANLINE:
proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
_copyExtraState(proxyRenderer);
if (item->address < GB_VIDEO_VERTICAL_PIXELS) {
proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address);
}
@ -160,7 +184,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
case DIRTY_BUFFER:
switch (item->address) {
case BUFFER_OAM:
if (item->value2 / sizeof(struct GBObj) > 40) {
if (item->value2 / sizeof(struct GBObj) > GB_VIDEO_MAX_OBJ) {
return false;
}
logger->readData(logger, legacyBuffer, item->value2, true);
@ -235,9 +259,7 @@ void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam
void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y) {
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
_copyExtraState(proxyRenderer);
proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y);
}
mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y);

View File

@ -11,6 +11,15 @@
#include <mgba-util/math.h>
#include <mgba-util/memory.h>
#define PAL_BG 0
#define PAL_OBJ 0x20
#define PAL_HIGHLIGHT 0x80
#define PAL_HIGHLIGHT_BG (PAL_HIGHLIGHT | PAL_BG)
#define PAL_HIGHLIGHT_OBJ (PAL_HIGHLIGHT | PAL_OBJ)
#define PAL_SGB_BORDER 0x40
#define OBJ_PRIORITY 0x100
#define OBJ_PRIO_MASK 0x0FF
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
@ -25,8 +34,8 @@ static void GBVideoSoftwareRendererEnableSGBBorder(struct GBVideoRenderer* rende
static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy);
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy, bool highlight);
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBVideoRendererSprite* obj, int startX, int endX, int y);
static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
size_t sgbOffset = 0;
@ -51,7 +60,7 @@ static void _regenerateSGBBorder(struct GBVideoSoftwareRenderer* renderer) {
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);
renderer->d.writePalette(&renderer->d, i + PAL_SGB_BORDER, color);
}
int x, y;
for (y = 0; y < 224; ++y) {
@ -180,6 +189,15 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
renderer->d.disableOBJ = false;
renderer->d.disableWIN = false;
renderer->d.highlightBG = false;
renderer->d.highlightWIN = false;
int i;
for (i = 0; i < GB_VIDEO_MAX_OBJ; ++i) {
renderer->d.highlightOBJ[i] = false;
}
renderer->d.highlightColor = M_COLOR_WHITE;
renderer->d.highlightAmount = 0;
renderer->temporaryBuffer = 0;
}
@ -206,8 +224,8 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G
softwareRenderer->offsetWx = 0;
softwareRenderer->offsetWy = 0;
int i;
for (i = 0; i < 64; ++i) {
size_t i;
for (i = 0; i < (sizeof(softwareRenderer->lookup) / sizeof(*softwareRenderer->lookup)); ++i) {
softwareRenderer->lookup[i] = i;
softwareRenderer->lookup[i] = i;
softwareRenderer->lookup[i] = i;
@ -215,6 +233,8 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G
}
memset(softwareRenderer->palette, 0, sizeof(softwareRenderer->palette));
softwareRenderer->lastHighlightAmount = 0;
}
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
@ -280,18 +300,30 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
softwareRenderer->lookup[1] = (value >> 2) & 3;
softwareRenderer->lookup[2] = (value >> 4) & 3;
softwareRenderer->lookup[3] = (value >> 6) & 3;
softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 0] = PAL_HIGHLIGHT + (value & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 1] = PAL_HIGHLIGHT + ((value >> 2) & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 2] = PAL_HIGHLIGHT + ((value >> 4) & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 3] = PAL_HIGHLIGHT + ((value >> 6) & 3);
break;
case GB_REG_OBP0:
softwareRenderer->lookup[0x20 + 0] = value & 3;
softwareRenderer->lookup[0x20 + 1] = (value >> 2) & 3;
softwareRenderer->lookup[0x20 + 2] = (value >> 4) & 3;
softwareRenderer->lookup[0x20 + 3] = (value >> 6) & 3;
softwareRenderer->lookup[PAL_OBJ + 0] = value & 3;
softwareRenderer->lookup[PAL_OBJ + 1] = (value >> 2) & 3;
softwareRenderer->lookup[PAL_OBJ + 2] = (value >> 4) & 3;
softwareRenderer->lookup[PAL_OBJ + 3] = (value >> 6) & 3;
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 0] = PAL_HIGHLIGHT + (value & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 1] = PAL_HIGHLIGHT + ((value >> 2) & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 2] = PAL_HIGHLIGHT + ((value >> 4) & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 3] = PAL_HIGHLIGHT + ((value >> 6) & 3);
break;
case GB_REG_OBP1:
softwareRenderer->lookup[0x24 + 0] = value & 3;
softwareRenderer->lookup[0x24 + 1] = (value >> 2) & 3;
softwareRenderer->lookup[0x24 + 2] = (value >> 4) & 3;
softwareRenderer->lookup[0x24 + 3] = (value >> 6) & 3;
softwareRenderer->lookup[PAL_OBJ + 4] = value & 3;
softwareRenderer->lookup[PAL_OBJ + 5] = (value >> 2) & 3;
softwareRenderer->lookup[PAL_OBJ + 6] = (value >> 4) & 3;
softwareRenderer->lookup[PAL_OBJ + 7] = (value >> 6) & 3;
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 4] = PAL_HIGHLIGHT + (value & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 5] = PAL_HIGHLIGHT + ((value >> 2) & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 6] = PAL_HIGHLIGHT + ((value >> 4) & 3);
softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 7] = PAL_HIGHLIGHT + ((value >> 6) & 3);
break;
}
return value;
@ -442,8 +474,10 @@ static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer
if (softwareRenderer->model & GB_MODEL_SGB) {
if (index < 0x10 && index && !(index & 3)) {
color = softwareRenderer->palette[0];
} else if (index >= 0x40 && !(index & 0xF)) {
} else if (index >= PAL_SGB_BORDER && !(index & 0xF)) {
color = softwareRenderer->palette[0];
} else if (index > PAL_HIGHLIGHT && index < PAL_HIGHLIGHT_OBJ && !(index & 3)) {
color = softwareRenderer->palette[PAL_HIGHLIGHT_BG];
}
}
if (renderer->cache) {
@ -472,6 +506,9 @@ static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer
#endif
}
softwareRenderer->palette[index] = color;
if (index < PAL_SGB_BORDER && (index < PAL_OBJ || (index & 3))) {
softwareRenderer->palette[index + PAL_HIGHLIGHT] = mColorMix5Bit(0x10 - softwareRenderer->lastHighlightAmount, color, softwareRenderer->lastHighlightAmount, renderer->highlightColor);
}
if (softwareRenderer->model & GB_MODEL_SGB && !index && GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
renderer->writePalette(renderer, 0x04, value);
@ -508,13 +545,14 @@ static void _cleanOAM(struct GBVideoSoftwareRenderer* renderer, int y) {
}
int o = 0;
int i;
for (i = 0; i < 40 && o < 10; ++i) {
for (i = 0; i < GB_VIDEO_MAX_OBJ && o < GB_VIDEO_MAX_LINE_OBJ; ++i) {
uint8_t oy = renderer->d.oam->obj[i].y;
if (y < oy - 16 || y >= oy - 16 + spriteHeight) {
continue;
}
// TODO: Sort
renderer->obj[o] = renderer->d.oam->obj[i];
renderer->obj[o].obj = renderer->d.oam->obj[i];
renderer->obj[o].index = i;
++o;
if (o == 10) {
break;
@ -542,16 +580,16 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
}
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->hasWindow && wx <= endX && !softwareRenderer->d.disableWIN) {
if (wx > 0 && !softwareRenderer->d.disableBG) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy);
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy, renderer->highlightBG);
}
maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, wx, endX, -wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy);
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, wx, endX, -wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy, renderer->highlightWIN);
} else if (!softwareRenderer->d.disableBG) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy);
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy, renderer->highlightBG);
}
} else if (!softwareRenderer->d.disableBG) {
memset(&softwareRenderer->row[startX], 0, endX - startX);
@ -567,6 +605,18 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
}
}
unsigned highlightAmount = (renderer->highlightAmount + 6) >> 4;
if (softwareRenderer->lastHighlightAmount != highlightAmount) {
softwareRenderer->lastHighlightAmount = highlightAmount;
int i;
for (i = 0; i < PAL_SGB_BORDER; ++i) {
if (i >= PAL_OBJ && (i & 3) == 0) {
continue;
}
softwareRenderer->palette[i + PAL_HIGHLIGHT] = mColorMix5Bit(0x10 - highlightAmount, softwareRenderer->palette[i], highlightAmount, renderer->highlightColor);
}
}
size_t sgbOffset = 0;
if (softwareRenderer->model & GB_MODEL_SGB && softwareRenderer->sgbBorders) {
sgbOffset = softwareRenderer->outputBufferStride * 40 + 48;
@ -583,7 +633,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
p <<= 2;
}
for (; x < ((startX + 7) & ~7) && x < endX; ++x) {
row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]];
row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & OBJ_PRIO_MASK]];
}
for (; x + 7 < (endX & ~7); x += 8) {
if (softwareRenderer->model & GB_MODEL_SGB) {
@ -592,14 +642,14 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
p &= 3;
p <<= 2;
}
row[x + 0] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]];
row[x + 1] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 1] & 0x7F]];
row[x + 2] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 2] & 0x7F]];
row[x + 3] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 3] & 0x7F]];
row[x + 4] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 4] & 0x7F]];
row[x + 5] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 5] & 0x7F]];
row[x + 6] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 6] & 0x7F]];
row[x + 7] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 7] & 0x7F]];
row[x + 0] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & OBJ_PRIO_MASK]];
row[x + 1] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 1] & OBJ_PRIO_MASK]];
row[x + 2] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 2] & OBJ_PRIO_MASK]];
row[x + 3] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 3] & OBJ_PRIO_MASK]];
row[x + 4] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 4] & OBJ_PRIO_MASK]];
row[x + 5] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 5] & OBJ_PRIO_MASK]];
row[x + 6] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 6] & OBJ_PRIO_MASK]];
row[x + 7] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 7] & OBJ_PRIO_MASK]];
}
if (softwareRenderer->model & GB_MODEL_SGB) {
p = softwareRenderer->d.sgbAttributes[(x >> 5) + 5 * (y >> 3)];
@ -608,7 +658,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
p <<= 2;
}
for (; x < endX; ++x) {
row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]];
row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & OBJ_PRIO_MASK]];
}
break;
case 1:
@ -769,7 +819,7 @@ static void GBVideoSoftwareRendererEnableSGBBorder(struct GBVideoRenderer* rende
}
}
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy) {
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy, bool highlight) {
uint8_t* data = renderer->d.vram;
uint8_t* attr = &maps[GB_SIZE_VRAM_BANK0];
if (!GBRegisterLCDCIsTileData(renderer->lcdc)) {
@ -794,12 +844,12 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
} else {
bgTile = ((int8_t*) maps)[topX + topY];
}
int p = 0;
int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG;
if (renderer->model >= GB_MODEL_CGB) {
GBObjAttributes attrs = attr[topX + topY];
p = GBObjAttributesGetCGBPalette(attrs) * 4;
p |= GBObjAttributesGetCGBPalette(attrs) * 4;
if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
p |= 0x80;
p |= OBJ_PRIORITY;
}
if (GBObjAttributesIsBank(attrs)) {
localData += GB_SIZE_VRAM_BANK0;
@ -829,12 +879,12 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
} else {
bgTile = ((int8_t*) maps)[topX + topY];
}
int p = 0;
int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG;
if (renderer->model >= GB_MODEL_CGB) {
GBObjAttributes attrs = attr[topX + topY];
p = GBObjAttributesGetCGBPalette(attrs) * 4;
p |= GBObjAttributesGetCGBPalette(attrs) * 4;
if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
p |= 0x80;
p |= OBJ_PRIORITY;
}
if (GBObjAttributesIsBank(attrs)) {
localData += GB_SIZE_VRAM_BANK0;
@ -869,8 +919,8 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
}
}
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y) {
int objX = obj->x + renderer->objOffsetX;
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBVideoRendererSprite* obj, int startX, int endX, int y) {
int objX = obj->obj.x + renderer->objOffsetX;
int ix = objX - 8;
if (endX < ix || startX >= ix + 8) {
return;
@ -887,8 +937,8 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende
uint8_t* data = renderer->d.vram;
int tileOffset = 0;
int bottomY;
int objY = obj->y + renderer->objOffsetY;
if (GBObjAttributesIsYFlip(obj->attr)) {
int objY = obj->obj.y + renderer->objOffsetY;
if (GBObjAttributesIsYFlip(obj->obj.attr)) {
bottomY = 7 - ((y - objY - 16) & 7);
if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - objY < -8) {
++tileOffset;
@ -899,115 +949,113 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende
++tileOffset;
}
}
if (GBRegisterLCDCIsObjSize(renderer->lcdc) && obj->tile & 1) {
if (GBRegisterLCDCIsObjSize(renderer->lcdc) && obj->obj.tile & 1) {
--tileOffset;
}
uint8_t mask = GBObjAttributesIsPriority(obj->attr) ? 0x63 : 0x60;
uint8_t mask2 = GBObjAttributesIsPriority(obj->attr) ? 0 : 0x83;
int p;
unsigned mask = GBObjAttributesIsPriority(obj->obj.attr) ? 0x63 : 0x60;
unsigned mask2 = GBObjAttributesIsPriority(obj->obj.attr) ? 0 : (OBJ_PRIORITY | 3);
int p = renderer->d.highlightOBJ[obj->index] ? PAL_HIGHLIGHT_OBJ : PAL_OBJ;
if (renderer->model >= GB_MODEL_CGB) {
p = (GBObjAttributesGetCGBPalette(obj->attr) + 8) * 4;
if (GBObjAttributesIsBank(obj->attr)) {
p |= GBObjAttributesGetCGBPalette(obj->obj.attr) * 4;
if (GBObjAttributesIsBank(obj->obj.attr)) {
data += GB_SIZE_VRAM_BANK0;
}
if (!GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
mask = 0x60;
mask2 = 0x83;
mask2 = OBJ_PRIORITY | 3;
}
} else {
p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4;
p |= (GBObjAttributesGetPalette(obj->obj.attr) + 8) * 4;
}
int bottomX;
int x = startX;
int objTile = obj->obj.tile + tileOffset;
if ((x - objX) & 7) {
for (; x < endX; ++x) {
if (GBObjAttributesIsXFlip(obj->attr)) {
if (GBObjAttributesIsXFlip(obj->obj.attr)) {
bottomX = (x - objX) & 7;
} else {
bottomX = 7 - ((x - objX) & 7);
}
int objTile = obj->tile + tileOffset;
uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
tileDataUpper >>= bottomX;
tileDataLower >>= bottomX;
color_t current = renderer->row[x];
if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
unsigned current = renderer->row[x];
if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
}
}
} else if (GBObjAttributesIsXFlip(obj->attr)) {
int objTile = obj->tile + tileOffset;
} else if (GBObjAttributesIsXFlip(obj->obj.attr)) {
uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
color_t current;
unsigned current;
current = renderer->row[x];
if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
}
current = renderer->row[x + 1];
if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 1] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
}
current = renderer->row[x + 2];
if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 2] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
}
current = renderer->row[x + 3];
if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 3] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
}
current = renderer->row[x + 4];
if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 4] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
}
current = renderer->row[x + 5];
if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 5] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
}
current = renderer->row[x + 6];
if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 6] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
}
current = renderer->row[x + 7];
if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 7] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
}
} else {
int objTile = obj->tile + tileOffset;
uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
color_t current;
unsigned current;
current = renderer->row[x + 7];
if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 7] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
}
current = renderer->row[x + 6];
if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 6] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
}
current = renderer->row[x + 5];
if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 5] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
}
current = renderer->row[x + 4];
if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 4] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
}
current = renderer->row[x + 3];
if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 3] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
}
current = renderer->row[x + 2];
if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 2] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
}
current = renderer->row[x + 1];
if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x + 1] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
}
current = renderer->row[x];
if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) {
if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) {
renderer->row[x] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
}
}

View File

@ -16,9 +16,6 @@ mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize");
const uint32_t GB_SAVESTATE_MAGIC = 0x00400000;
const uint32_t GB_SAVESTATE_VERSION = 0x00000002;
static void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state);
static void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state);
void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic);
STORE_32LE(gb->romCrc32, 0, &state->romCrc32);

View File

@ -409,7 +409,7 @@ static void _cleanOAM(struct GBVideo* video, int y) {
}
int o = 0;
int i;
for (i = 0; i < 40 && o < 10; ++i) {
for (i = 0; i < GB_VIDEO_MAX_OBJ && o < GB_VIDEO_MAX_LINE_OBJ; ++i) {
uint8_t oy = video->oam.obj[i].y;
if (y < oy - 16 || y >= oy - 16 + spriteHeight) {
continue;

View File

@ -103,6 +103,16 @@ const uint16_t EREADER_ADDRESS_CODES[] = {
54679
};
static const uint8_t DUMMY_HEADER_STRIP[2][0x10] = {
{ 0x00, 0x30, 0x01, 0x01, 0x00, 0x01, 0x05, 0x10, 0x00, 0x00, 0x10, 0x13, 0x00, 0x00, 0x02, 0x00 },
{ 0x00, 0x30, 0x01, 0x02, 0x00, 0x01, 0x08, 0x10, 0x00, 0x00, 0x10, 0x12, 0x00, 0x00, 0x01, 0x00 }
};
static const uint8_t DUMMY_HEADER_FIXED[0x16] = {
0x00, 0x00, 0x10, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x08, 0x4e, 0x49, 0x4e, 0x54, 0x45, 0x4e,
0x44, 0x4f, 0x00, 0x22, 0x00, 0x09
};
static const uint8_t BLOCK_HEADER[2][0x18] = {
{ 0x00, 0x02, 0x00, 0x01, 0x40, 0x10, 0x00, 0x1c, 0x10, 0x6f, 0x40, 0xda, 0x39, 0x25, 0x8e, 0xe0, 0x7b, 0xb5, 0x98, 0xb6, 0x5b, 0xcf, 0x7f, 0x72 },
{ 0x00, 0x03, 0x00, 0x19, 0x40, 0x10, 0x00, 0x2c, 0x0e, 0x88, 0xed, 0x82, 0x50, 0x67, 0xfb, 0xd1, 0x43, 0xee, 0x03, 0xc6, 0xc6, 0x2b, 0x2c, 0x93 }
@ -327,12 +337,18 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s
memset(hw->eReaderDots, 0, EREADER_DOTCODE_SIZE);
uint8_t blockRS[44][0x10];
uint8_t block0[0x30];
bool parsed = false;
bool bitmap = false;
bool reducedHeader = false;
size_t blocks;
int base;
switch (size) {
// Raw sizes
case 2076:
memcpy(block0, DUMMY_HEADER_STRIP[1], sizeof(DUMMY_HEADER_STRIP[1]));
reducedHeader = true;
// Fallthrough
case 2112:
parsed = true;
// Fallthrough
@ -340,6 +356,10 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s
base = 25;
blocks = 28;
break;
case 1308:
memcpy(block0, DUMMY_HEADER_STRIP[0], sizeof(DUMMY_HEADER_STRIP[0]));
reducedHeader = true;
// Fallthrough
case 1344:
parsed = true;
// Fallthrough
@ -355,11 +375,12 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s
return;
}
const uint8_t* cdata = data;
size_t i;
if (bitmap) {
size_t x;
for (i = 0; i < 40; ++i) {
const uint8_t* line = &((const uint8_t*) data)[(i + 2) * 124];
const uint8_t* line = &cdata[(i + 2) * 124];
uint8_t* origin = &hw->eReaderDots[EREADER_DOTCODE_STRIDE * i + 200];
for (x = 0; x < 124; ++x) {
uint8_t byte = line[x];
@ -386,8 +407,49 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s
_eReaderAddress(origin, base + i);
}
if (parsed) {
for (i = 0; i < size / 48; ++i) {
_eReaderReedSolomon(&((const uint8_t*) data)[i * 48], blockRS[i]);
if (reducedHeader) {
memcpy(&block0[0x10], DUMMY_HEADER_FIXED, sizeof(DUMMY_HEADER_FIXED));
block0[0x0D] = cdata[0x0];
block0[0x0C] = cdata[0x1];
block0[0x10] = cdata[0x2];
block0[0x11] = cdata[0x3];
block0[0x26] = cdata[0x4];
block0[0x27] = cdata[0x5];
block0[0x28] = cdata[0x6];
block0[0x29] = cdata[0x7];
block0[0x2A] = cdata[0x8];
block0[0x2B] = cdata[0x9];
block0[0x2C] = cdata[0xA];
block0[0x2D] = cdata[0xB];
for (i = 0; i < 12; ++i) {
block0[0x2E] ^= cdata[i];
}
unsigned dataChecksum = 0;
int j;
for (i = 1; i < (size + 36) / 48; ++i) {
const uint8_t* block = &cdata[i * 48 - 36];
_eReaderReedSolomon(block, blockRS[i]);
unsigned fragmentChecksum = 0;
for (j = 0; j < 0x30; j += 2) {
uint16_t halfword;
fragmentChecksum ^= block[j];
fragmentChecksum ^= block[j + 1];
LOAD_16BE(halfword, j, block);
dataChecksum += halfword;
}
block0[0x2F] += fragmentChecksum;
}
block0[0x13] = (~dataChecksum) >> 8;
block0[0x14] = ~dataChecksum;
for (i = 0; i < 0x2F; ++i) {
block0[0x2F] += block0[i];
}
block0[0x2F] = ~block0[0x2F];
_eReaderReedSolomon(block0, blockRS[0]);
} else {
for (i = 0; i < size / 48; ++i) {
_eReaderReedSolomon(&cdata[i * 48], blockRS[i]);
}
}
}
size_t blockId = 0;
@ -407,23 +469,29 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s
parsedBlockData[1] = header[(2 * i) % 0x18 + 1];
int j;
for (j = 2; j < 104; ++j) {
if (byteOffset >= 0x40) {
break;
}
if (byteOffset >= 0x30) {
parsedBlockData[j] = blockRS[blockId][byteOffset - 0x30];
} else {
parsedBlockData[j] = ((const uint8_t*) data)[blockId * 0x30 + byteOffset];
}
++blockId;
if (blockId * 0x30 >= size) {
blockId = 0;
++byteOffset;
}
if (byteOffset >= 0x40) {
break;
}
if (byteOffset >= 0x30) {
parsedBlockData[j] = blockRS[blockId][byteOffset - 0x30];
} else if (!reducedHeader) {
parsedBlockData[j] = cdata[blockId * 0x30 + byteOffset];
} else {
if (blockId > 0) {
parsedBlockData[j] = cdata[blockId * 0x30 + byteOffset - 36];
} else {
parsedBlockData[j] = block0[byteOffset];
}
}
++blockId;
if (blockId * 0x30 >= size) {
blockId = 0;
++byteOffset;
}
}
blockData = parsedBlockData;
} else {
blockData = &((const uint8_t*) data)[i * 104];
blockData = &cdata[i * 104];
}
int b;
for (b = 0; b < 104; ++b) {
@ -646,4 +714,4 @@ void GBAEReaderQueueCard(struct GBA* gba, const void* data, size_t size) {
gba->memory.hw.eReaderCards[i].size = size;
return;
}
}
}

View File

@ -55,7 +55,7 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct
for (i = 0; i < 128; ++i) {
renderer->d.highlightOBJ[i] = false;
}
renderer->d.highlightColor = 0xFFFFFF;
renderer->d.highlightColor = M_COLOR_WHITE;
renderer->d.highlightAmount = 0;
renderer->logger->context = renderer;
@ -100,6 +100,24 @@ static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
mVideoLoggerRendererReset(proxyRenderer->logger);
}
static void _copyExtraState(struct GBAVideoProxyRenderer* proxyRenderer) {
proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0];
proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1];
proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN;
proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0];
proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1];
proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2];
proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3];
memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ));
proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount;
proxyRenderer->backend->highlightColor = proxyRenderer->d.highlightColor;
}
void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
return;
@ -214,20 +232,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
}
break;
case DIRTY_SCANLINE:
proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0];
proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1];
proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN;
proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0];
proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1];
proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2];
proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3];
memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ));
proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount;
_copyExtraState(proxyRenderer);
if (item->address < GBA_VIDEO_VERTICAL_PIXELS) {
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
}
@ -321,18 +326,7 @@ void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t o
void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0];
proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1];
proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN;
proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0];
proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1];
proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2];
proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3];
_copyExtraState(proxyRenderer);
proxyRenderer->backend->drawScanline(proxyRenderer->backend, y);
}
mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);

View File

@ -87,12 +87,6 @@ static const struct GBACartridgeOverride _overrides[] = {
// Metal Slug Advance
{ "BSME", SAVEDATA_EEPROM, HW_NONE, 0x8000290, false },
// Pokemon Pinball: Ruby & Sapphire
{ "BPPJ", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false },
{ "BPPE", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false },
{ "BPPP", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false },
{ "BPPU", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false },
// Pokemon Ruby
{ "AXVJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false },
{ "AXVE", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false },

View File

@ -426,7 +426,7 @@ static const char* const _renderObj =
"uniform ivec4 inflags;\n"
"uniform mat2x2 transform;\n"
"uniform ivec4 dims;\n"
"uniform ivec4 objwin;\n"
"uniform ivec3 objwin;\n"
"uniform ivec4 mosaic;\n"
"uniform int cyclesRemaining[160];\n"
"OUT(0) out vec4 color;\n"
@ -459,7 +459,7 @@ static const char* const _renderObj =
" color = pix;\n"
" flags = inflags;\n"
" gl_FragDepth = float(flags.x) / 16.;\n"
" window = ivec4(objwin.yzw, 0);\n"
" window = ivec4(objwin, 0);\n"
"}";
static const struct GBAVideoGLUniform _uniformsObjPriority[] = {
@ -533,20 +533,15 @@ static const char* const _renderWindow =
"}\n"
"void main() {\n"
" int dispflags = (dispcnt & 0x1F) | 0x20;\n"
" if ((dispcnt & 0xE0) == 0) {\n"
" window = ivec4(dispflags, blend, 0);\n"
" } else {\n"
" ivec4 windowFlags = ivec4(flags.z, blend, 0);\n"
" int top = int(texCoord.y);\n"
" int bottom = max(top - 1, 0);\n"
" if ((dispcnt & 0x20) != 0 && crop(interpolate(vec4(win0[top]), vec4(win0[bottom])))) { \n"
" windowFlags.x = flags.x;\n"
" } else if ((dispcnt & 0x40) != 0 && crop(interpolate(vec4(win1[top]), vec4(win1[bottom])))) {\n"
" windowFlags.x = flags.y;\n"
" }\n"
" window = windowFlags;\n"
" ivec4 windowFlags = ivec4(flags.z, blend, 0);\n"
" int top = int(texCoord.y);\n"
" int bottom = max(top - 1, 0);\n"
" if ((dispcnt & 0x20) != 0 && crop(interpolate(vec4(win0[top]), vec4(win0[bottom])))) { \n"
" windowFlags.x = flags.x;\n"
" } else if ((dispcnt & 0x40) != 0 && crop(interpolate(vec4(win1[top]), vec4(win1[bottom])))) {\n"
" windowFlags.x = flags.y;\n"
" }\n"
" window = windowFlags;\n"
"}\n";
static const struct GBAVideoGLUniform _uniformsFinalize[] = {
@ -677,7 +672,7 @@ void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {
for (i = 0; i < 128; ++i) {
renderer->d.highlightOBJ[i] = false;
}
renderer->d.highlightColor = 0xFFFFFF;
renderer->d.highlightColor = M_COLOR_WHITE;
renderer->d.highlightAmount = 0;
renderer->scale = 1;
@ -735,7 +730,7 @@ static void _initFramebufferTextureEx(GLuint tex, GLenum internalFormat, GLenum
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 8, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, type, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
}
static void _initFramebufferTexture(GLuint tex, GLenum format, GLenum attachment, int scale) {
@ -754,7 +749,7 @@ static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) {
_initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, 0);
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]);
_initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT0, glRenderer->scale);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_WINDOW], 0);
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
_initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
@ -1422,19 +1417,17 @@ void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) {
glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 });
int i;
for (i = 0; i < 4; ++i) {
glScissor(i + 1, glRenderer->firstY, i + 2, y - glRenderer->firstY + 1);
glScissor(i + 1, glRenderer->firstY, 1, y - glRenderer->firstY + 1);
glClearBufferiv(GL_COLOR, 1, (GLint[]) { glRenderer->bg[i].priority,
glRenderer->bg[i].target1 | (glRenderer->bg[i].target2 << 1) | (glRenderer->blendEffect << 2),
glRenderer->blda, 0 });
}
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
GBAVideoGLRendererDrawWindow(glRenderer, y);
if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) {
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glEnable(GL_STENCIL_TEST);
glDepthFunc(GL_LESS);
for (i = 0; i < glRenderer->oamMax; ++i) {
struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i];
@ -1722,20 +1715,21 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { flipX, 0, 0, flipY });
}
glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight);
glDisable(GL_STENCIL_TEST);
if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) {
// OBJWIN writes do not affect pixel priority
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glStencilMask(GL_FALSE);
glStencilMask(0);
int window = renderer->objwin & 0x3F;
glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 1, window, renderer->bldb, renderer->bldy);
glUniform3i(uniforms[GBA_GL_OBJ_OBJWIN], window, renderer->bldb, renderer->bldy);
glDrawBuffers(3, (GLenum[]) { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 });
} else {
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glStencilMask(GL_TRUE);
glStencilMask(1);
glStencilFunc(GL_ALWAYS, 1, 1);
glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0, 0);
glUniform3i(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0);
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
}
if (GBAObjAttributesAIsMosaic(sprite->a) && GBAObjAttributesAGetMode(sprite->a) != OBJ_MODE_OBJWIN) {
@ -1755,6 +1749,7 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
// Update the pixel priority for already-written pixels
shader = &renderer->objShader[2];
uniforms = shader->uniforms;
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 1, 1);
glUseProgram(shader->program);
glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 });
@ -1874,17 +1869,29 @@ void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) {
glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]);
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale * (y - renderer->firstY + 1));
glUseProgram(shader->program);
glBindVertexArray(shader->vao);
glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY);
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
glUniform1i(uniforms[GBA_GL_WIN_DISPCNT], renderer->dispcnt >> 8);
glUniform2i(uniforms[GBA_GL_WIN_BLEND], renderer->bldb, renderer->bldy);
glUniform3i(uniforms[GBA_GL_WIN_FLAGS], renderer->winN[0].control, renderer->winN[1].control, renderer->winout);
glUniform4iv(uniforms[GBA_GL_WIN_WIN0], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[0]);
glUniform4iv(uniforms[GBA_GL_WIN_WIN1], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[1]);
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
switch (renderer->dispcnt & 0xE000) {
case 0x0000:
// No windows are enabled
glClearBufferiv(GL_COLOR, 0, (GLint[]) { ((renderer->dispcnt >> 8) & 0x1F) | 0x20, renderer->bldb, renderer->bldy, 0 });
break;
case 0x8000:
// Only OBJWIN is enabled
glClearBufferiv(GL_COLOR, 0, (GLint[]) { renderer->winout, renderer->bldb, renderer->bldy, 0 });
break;
default:
glUseProgram(shader->program);
glBindVertexArray(shader->vao);
glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY);
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
glUniform1i(uniforms[GBA_GL_WIN_DISPCNT], renderer->dispcnt >> 8);
glUniform2i(uniforms[GBA_GL_WIN_BLEND], renderer->bldb, renderer->bldy);
glUniform3i(uniforms[GBA_GL_WIN_FLAGS], renderer->winN[0].control, renderer->winN[1].control, renderer->winout);
glUniform4iv(uniforms[GBA_GL_WIN_WIN0], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[0]);
glUniform4iv(uniforms[GBA_GL_WIN_WIN1], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[1]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
break;
}
}
void GBAVideoGLRendererSetScale(struct GBAVideoGLRenderer* renderer, int scale) {

View File

@ -35,8 +35,6 @@ int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRendere
static inline unsigned _brighten(unsigned color, int y);
static inline unsigned _darken(unsigned color, int y);
static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);
// We stash the priority on the top bits so we can do a one-operator comparison
// The lower the number, the higher the priority, and sprites take precedence over backgrounds
@ -45,7 +43,7 @@ static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB)
static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) {
if (color >= current) {
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
color = mColorMix5Bit(renderer->alphaA[x], current, renderer->alphaB[x], color);
} else {
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
}
@ -58,7 +56,7 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render
static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) {
if (color >= current) {
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
color = mColorMix5Bit(renderer->alphaA[x], current, renderer->alphaB[x], color);
} else {
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
}
@ -285,66 +283,4 @@ static inline unsigned _darken(unsigned color, int y) {
return c;
}
static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
unsigned c = 0;
unsigned a, b;
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
a = colorA & 0xF81F;
b = colorB & 0xF81F;
a |= (colorA & 0x7C0) << 16;
b |= (colorB & 0x7C0) << 16;
c = ((a * weightA + b * weightB) / 16);
if (c & 0x08000000) {
c = (c & ~0x0FC00000) | 0x07C00000;
}
if (c & 0x0020) {
c = (c & ~0x003F) | 0x001F;
}
if (c & 0x10000) {
c = (c & ~0x1F800) | 0xF800;
}
c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
#else
a = colorA & 0x7C1F;
b = colorB & 0x7C1F;
a |= (colorA & 0x3E0) << 16;
b |= (colorB & 0x3E0) << 16;
c = ((a * weightA + b * weightB) / 16);
if (c & 0x04000000) {
c = (c & ~0x07E00000) | 0x03E00000;
}
if (c & 0x0020) {
c = (c & ~0x003F) | 0x001F;
}
if (c & 0x8000) {
c = (c & ~0xF800) | 0x7C00;
}
c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
#endif
#else
a = colorA & 0xFF;
b = colorB & 0xFF;
c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
if (c & 0x00000100) {
c = 0x000000FF;
}
a = colorA & 0xFF00;
b = colorB & 0xFF00;
c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
if (c & 0x00010000) {
c = (c & 0x000000FF) | 0x0000FF00;
}
a = colorA & 0xFF0000;
b = colorB & 0xFF0000;
c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
if (c & 0x01000000) {
c = (c & 0x0000FFFF) | 0x00FF0000;
}
#endif
return c;
}
#endif

View File

@ -80,7 +80,7 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
for (i = 0; i < 128; ++i) {
renderer->d.highlightOBJ[i] = false;
}
renderer->d.highlightColor = GBA_COLOR_WHITE;
renderer->d.highlightColor = M_COLOR_WHITE;
renderer->d.highlightAmount = 0;
renderer->temporaryBuffer = 0;
@ -96,7 +96,7 @@ static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
int x;
for (x = 0; x < softwareRenderer->masterEnd; ++x) {
row[x] = GBA_COLOR_WHITE;
row[x] = M_COLOR_WHITE;
}
}
}
@ -578,7 +578,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
int x;
for (x = 0; x < softwareRenderer->masterEnd; ++x) {
row[x] = GBA_COLOR_WHITE;
row[x] = M_COLOR_WHITE;
}
return;
}
@ -944,7 +944,7 @@ void GBAVideoSoftwareRendererPostprocessBuffer(struct GBAVideoSoftwareRenderer*
for (; x < end; ++x) {
uint32_t color = softwareRenderer->row[x];
if (color & FLAG_TARGET_1) {
softwareRenderer->row[x] = _mix(softwareRenderer->alphaB[x], backdrop, softwareRenderer->alphaA[x], color);
softwareRenderer->row[x] = mColorMix5Bit(softwareRenderer->alphaB[x], backdrop, softwareRenderer->alphaA[x], color);
}
}
}
@ -1044,8 +1044,8 @@ static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
if (highlightAmount) {
for (i = 0; i < 512; ++i) {
renderer->highlightPalette[i] = _mix(0x10 - highlightAmount, renderer->normalPalette[i], highlightAmount, renderer->d.highlightColor);
renderer->highlightVariantPalette[i] = _mix(0x10 - highlightAmount, renderer->variantPalette[i], highlightAmount, renderer->d.highlightColor);
renderer->highlightPalette[i] = mColorMix5Bit(0x10 - highlightAmount, renderer->normalPalette[i], highlightAmount, renderer->d.highlightColor);
renderer->highlightVariantPalette[i] = mColorMix5Bit(0x10 - highlightAmount, renderer->variantPalette[i], highlightAmount, renderer->d.highlightColor);
}
}
}

View File

@ -275,7 +275,7 @@ void FrameView::injectGBA() {
gba->video.renderer->highlightOBJ[i] = false;
}
QPalette palette;
gba->video.renderer->highlightColor = palette.color(QPalette::HighlightedText).rgb();
gba->video.renderer->highlightColor = M_RGB8_TO_NATIVE(palette.color(QPalette::Highlight).rgb());
gba->video.renderer->highlightAmount = sin(m_glowFrame * M_PI / 30) * 48 + 64;
if (!m_overrideBackdrop.isValid()) {
QRgb backdrop = M_RGB5_TO_RGB8(gba->video.palette[0]) | 0xFF000000;
@ -318,7 +318,12 @@ void FrameView::updateTilesGB(bool) {
m_queue.clear();
{
CoreController::Interrupter interrupter(m_controller);
uint8_t* io = static_cast<GB*>(m_controller->thread()->core->board)->memory.io;
QPointF origin;
GB* gb = static_cast<GB*>(m_controller->thread()->core->board);
if (gb->video.sgbBorders && (gb->model & GB_MODEL_SGB)) {
origin = QPointF(48, 40);
}
uint8_t* io = gb->memory.io;
GBRegisterLCDC lcdc = io[GB_REG_LCDC];
for (int sprite = 0; sprite < 40; ++sprite) {
@ -338,7 +343,7 @@ void FrameView::updateTilesGB(bool) {
{ LayerId::SPRITE, sprite },
!m_disabled.contains({ LayerId::SPRITE, sprite }),
QPixmap::fromImage(obj),
{}, offset, false, false
{}, offset + origin, false, false
});
if (m_queue.back().image.hasAlpha()) {
m_queue.back().mask = QRegion(m_queue.back().image.mask());
@ -352,7 +357,7 @@ void FrameView::updateTilesGB(bool) {
{ LayerId::WINDOW },
!m_disabled.contains({ LayerId::WINDOW }),
{},
{}, {0, 0}, false, false
{}, origin, false, false
});
}
@ -360,7 +365,7 @@ void FrameView::updateTilesGB(bool) {
{ LayerId::BACKGROUND },
!m_disabled.contains({ LayerId::BACKGROUND }),
{},
{}, {0, 0}, false, false
{}, origin, false, false
});
updateRendered();
@ -370,6 +375,15 @@ void FrameView::updateTilesGB(bool) {
void FrameView::injectGB() {
mVideoLogger* logger = m_vl->videoLogger;
GB* gb = static_cast<GB*>(m_vl->board);
gb->video.renderer->highlightBG = false;
gb->video.renderer->highlightWIN = false;
for (int i = 0; i < 40; ++i) {
gb->video.renderer->highlightOBJ[i] = false;
}
QPalette palette;
gb->video.renderer->highlightColor = M_RGB8_TO_NATIVE(palette.color(QPalette::Highlight).rgb());
gb->video.renderer->highlightAmount = sin(m_glowFrame * M_PI / 30) * 48 + 64;
m_vl->reset(m_vl);
for (const Layer& layer : m_queue) {
@ -378,12 +392,21 @@ void FrameView::injectGB() {
if (!layer.enabled) {
mVideoLoggerInjectOAM(logger, layer.id.index << 2, 0);
}
if (layer.id == m_active) {
gb->video.renderer->highlightOBJ[layer.id.index] = true;
}
break;
case LayerId::BACKGROUND:
m_vl->enableVideoLayer(m_vl, GB_LAYER_BACKGROUND, layer.enabled);
if (layer.id == m_active) {
gb->video.renderer->highlightBG = true;
}
break;
case LayerId::WINDOW:
m_vl->enableVideoLayer(m_vl, GB_LAYER_WINDOW, layer.enabled);
if (layer.id == m_active) {
gb->video.renderer->highlightWIN = true;
}
break;
}
}
@ -510,6 +533,12 @@ void FrameView::newVl() {
m_vl->loadROM(m_vl, m_currentFrame);
m_currentFrame = nullptr;
mCoreInitConfig(m_vl, nullptr);
#ifdef M_CORE_GB
if (m_controller->platform() == PLATFORM_GB) {
mCoreConfigSetIntValue(&m_vl->config, "sgb.borders", static_cast<GB*>(m_controller->thread()->core->board)->video.sgbBorders);
m_vl->reloadConfigOption(m_vl, "sgb.borders", nullptr);
}
#endif
unsigned width, height;
m_vl->desiredVideoDimensions(m_vl, &width, &height);
m_framebuffer = QImage(width, height, QImage::Format_RGBX8888);

View File

@ -412,6 +412,7 @@ void SettingsView::updateConfig() {
saveSetting("gba.audioHle", m_ui.audioHle);
saveSetting("dynamicTitle", m_ui.dynamicTitle);
saveSetting("videoScale", m_ui.videoScale);
saveSetting("gba.forceGbp", m_ui.forceGbp);
if (m_ui.audioBufferSize->currentText().toInt() > 8192) {
m_ui.audioBufferSize->setCurrentText("8192");
@ -606,6 +607,7 @@ void SettingsView::reloadConfig() {
loadSetting("useDiscordPresence", m_ui.useDiscordPresence);
loadSetting("gba.audioHle", m_ui.audioHle);
loadSetting("dynamicTitle", m_ui.dynamicTitle);
loadSetting("gba.forceGbp", m_ui.forceGbp);
m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());

View File

@ -873,21 +873,21 @@
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<item row="10" column="0" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="10" column="0">
<item row="11" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Savestate extra data:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="11" column="1">
<widget class="QCheckBox" name="saveStateScreenshot">
<property name="text">
<string>Screenshot</string>
@ -897,7 +897,7 @@
</property>
</widget>
</item>
<item row="11" column="1">
<item row="12" column="1">
<widget class="QCheckBox" name="saveStateSave">
<property name="text">
<string>Save data</string>
@ -907,7 +907,7 @@
</property>
</widget>
</item>
<item row="12" column="1">
<item row="13" column="1">
<widget class="QCheckBox" name="saveStateCheats">
<property name="text">
<string>Cheat codes</string>
@ -917,21 +917,21 @@
</property>
</widget>
</item>
<item row="13" column="0" colspan="2">
<item row="14" column="0" colspan="2">
<widget class="Line" name="line_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="14" column="0">
<item row="15" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Load extra data:</string>
</property>
</widget>
</item>
<item row="14" column="1">
<item row="15" column="1">
<widget class="QCheckBox" name="loadStateScreenshot">
<property name="text">
<string>Screenshot</string>
@ -941,20 +941,27 @@
</property>
</widget>
</item>
<item row="15" column="1">
<item row="16" column="1">
<widget class="QCheckBox" name="loadStateSave">
<property name="text">
<string>Save data</string>
</property>
</widget>
</item>
<item row="16" column="1">
<item row="17" column="1">
<widget class="QCheckBox" name="loadStateCheats">
<property name="text">
<string>Cheat codes</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="forceGbp">
<property name="text">
<string>Enable Game Boy Player features by default</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="enhancements">

View File

@ -1597,7 +1597,6 @@ void Window::setupMenu(QMenuBar* menubar) {
addGameAction(tr("View &tiles..."), "tileWindow", openControllerTView<TileView>(), "tools");
addGameAction(tr("View &map..."), "mapWindow", openControllerTView<MapView>(), "tools");
#ifdef M_CORE_GBA
Action* frameWindow = addGameAction(tr("&Frame inspector..."), "frameWindow", [this]() {
if (!m_frameView) {
m_frameView = new FrameView(m_controller);
@ -1613,8 +1612,6 @@ void Window::setupMenu(QMenuBar* menubar) {
}
m_frameView->show();
}, "tools");
m_platformActions.insert(PLATFORM_GBA, frameWindow);
#endif
addGameAction(tr("View memory..."), "memoryView", openControllerTView<MemoryView>(), "tools");
addGameAction(tr("Search memory..."), "memorySearch", openControllerTView<MemorySearch>(), "tools");

View File

@ -306,8 +306,8 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<name>GIFView</name>
<message>
<location filename="../GIFView.ui" line="14"/>
<source>Record GIF/APNG</source>
<translation>GIF/APNG aufzeichnen</translation>
<source>Record GIF/WebP/APNG</source>
<translation>GIF/WebP/APNG aufzeichnen</translation>
</message>
<message>
<location filename="../GIFView.ui" line="30"/>
@ -1392,22 +1392,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<context>
<name>QGBA::CoreController</name>
<message>
<location filename="../CoreController.cpp" line="601"/>
<location filename="../CoreController.cpp" line="605"/>
<source>Failed to open save file: %1</source>
<translation>Fehler beim Öffnen der Speicherdatei: %1</translation>
</message>
<message>
<location filename="../CoreController.cpp" line="630"/>
<location filename="../CoreController.cpp" line="634"/>
<source>Failed to open game file: %1</source>
<translation>Fehler beim Öffnen der Spieldatei: %1</translation>
</message>
<message>
<location filename="../CoreController.cpp" line="728"/>
<location filename="../CoreController.cpp" line="732"/>
<source>Failed to open snapshot file for reading: %1</source>
<translation>Konnte Snapshot-Datei %1 nicht zum Lesen öffnen</translation>
</message>
<message>
<location filename="../CoreController.cpp" line="744"/>
<location filename="../CoreController.cpp" line="748"/>
<source>Failed to open snapshot file for writing: %1</source>
<translation>Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen</translation>
</message>
@ -1433,52 +1433,52 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<context>
<name>QGBA::FrameView</name>
<message>
<location filename="../FrameView.cpp" line="530"/>
<location filename="../FrameView.cpp" line="548"/>
<source>Export frame</source>
<translation>Bild exportieren</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="531"/>
<location filename="../FrameView.cpp" line="549"/>
<source>Portable Network Graphics (*.png)</source>
<translation>Portable Network Graphics (*.png)</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="549"/>
<location filename="../FrameView.cpp" line="567"/>
<source>None</source>
<translation>Keine</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="551"/>
<location filename="../FrameView.cpp" line="569"/>
<source>Background</source>
<translation>Hintergrund</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="554"/>
<location filename="../FrameView.cpp" line="572"/>
<source>Window</source>
<translation>Fenster</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="557"/>
<location filename="../FrameView.cpp" line="575"/>
<source>Objwin</source>
<translation>Objwin</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="562"/>
<location filename="../FrameView.cpp" line="580"/>
<source>Sprite</source>
<translation>Sprite</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="565"/>
<location filename="../FrameView.cpp" line="583"/>
<source>Backdrop</source>
<translation>Hintergrund</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="568"/>
<location filename="../FrameView.cpp" line="586"/>
<source>Frame</source>
<translation>Frame</translation>
</message>
<message>
<location filename="../FrameView.cpp" line="574"/>
<location filename="../FrameView.cpp" line="592"/>
<source>%1 %2</source>
<translation>%1 %2</translation>
</message>
@ -1494,22 +1494,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<context>
<name>QGBA::GBAKeyEditor</name>
<message>
<location filename="../GBAKeyEditor.cpp" line="68"/>
<location filename="../GBAKeyEditor.cpp" line="67"/>
<source>Clear Button</source>
<translation>Button löschen</translation>
</message>
<message>
<location filename="../GBAKeyEditor.cpp" line="80"/>
<location filename="../GBAKeyEditor.cpp" line="79"/>
<source>Clear Analog</source>
<translation>Analog löschen</translation>
</message>
<message>
<location filename="../GBAKeyEditor.cpp" line="91"/>
<location filename="../GBAKeyEditor.cpp" line="90"/>
<source>Refresh</source>
<translation>Aktualisieren</translation>
</message>
<message>
<location filename="../GBAKeyEditor.cpp" line="101"/>
<location filename="../GBAKeyEditor.cpp" line="100"/>
<source>Set all</source>
<translation>Alle belegen</translation>
</message>
@ -3267,12 +3267,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<translation>Fehler beim Laden der Eingabedatei: %1</translation>
</message>
<message>
<location filename="../MemoryModel.cpp" line="343"/>
<location filename="../MemoryModel.cpp" line="342"/>
<source>TBL</source>
<translation>TBL</translation>
</message>
<message>
<location filename="../MemoryModel.cpp" line="343"/>
<location filename="../MemoryModel.cpp" line="342"/>
<source>ISO-8859-1</source>
<translation>ISO-8859-1</translation>
</message>
@ -3280,22 +3280,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<context>
<name>QGBA::MemorySearch</name>
<message>
<location filename="../MemorySearch.cpp" line="222"/>
<location filename="../MemorySearch.cpp" line="221"/>
<source> (%0/%1×)</source>
<translation> (%0/%1×)</translation>
</message>
<message>
<location filename="../MemorySearch.cpp" line="224"/>
<location filename="../MemorySearch.cpp" line="223"/>
<source> (%0×)</source>
<translation> (%0×)</translation>
</message>
<message>
<location filename="../MemorySearch.cpp" line="227"/>
<location filename="../MemorySearch.cpp" line="226"/>
<source> (%0×)</source>
<translation> (%0×)</translation>
</message>
<message>
<location filename="../MemorySearch.cpp" line="231"/>
<location filename="../MemorySearch.cpp" line="230"/>
<source>%1 byte%2</source>
<translation>%1 byte%2</translation>
</message>
@ -3701,12 +3701,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<translation>Video-Logs (*.mvl)</translation>
</message>
<message>
<location filename="../Window.cpp" line="851"/>
<location filename="../Window.cpp" line="850"/>
<source>Crash</source>
<translation>Absturz</translation>
</message>
<message>
<location filename="../Window.cpp" line="852"/>
<location filename="../Window.cpp" line="851"/>
<source>The game has crashed with the following error:
%1</source>
@ -3715,52 +3715,52 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
%1</translation>
</message>
<message>
<location filename="../Window.cpp" line="874"/>
<location filename="../Window.cpp" line="873"/>
<source>Unimplemented BIOS call</source>
<translation>Nicht implementierter BIOS-Aufruf</translation>
</message>
<message>
<location filename="../Window.cpp" line="875"/>
<location filename="../Window.cpp" line="874"/>
<source>This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience.</source>
<translation>Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS.</translation>
</message>
<message>
<location filename="../Window.cpp" line="974"/>
<location filename="../Window.cpp" line="973"/>
<source>Really make portable?</source>
<translation>Portablen Modus wirklich aktivieren?</translation>
</message>
<message>
<location filename="../Window.cpp" line="975"/>
<location filename="../Window.cpp" line="974"/>
<source>This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?</source>
<translation>Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren?</translation>
</message>
<message>
<location filename="../Window.cpp" line="987"/>
<location filename="../Window.cpp" line="986"/>
<source>Restart needed</source>
<translation>Neustart benötigt</translation>
</message>
<message>
<location filename="../Window.cpp" line="988"/>
<location filename="../Window.cpp" line="987"/>
<source>Some changes will not take effect until the emulator is restarted.</source>
<translation>Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde.</translation>
</message>
<message>
<location filename="../Window.cpp" line="1043"/>
<location filename="../Window.cpp" line="1042"/>
<source> - Player %1 of %2</source>
<translation> - Spieler %1 von %2</translation>
</message>
<message>
<location filename="../Window.cpp" line="1054"/>
<location filename="../Window.cpp" line="1053"/>
<source>%1 - %2</source>
<translation>%1 - %2</translation>
</message>
<message>
<location filename="../Window.cpp" line="1056"/>
<location filename="../Window.cpp" line="1055"/>
<source>%1 - %2 - %3</source>
<translation>%1 - %2 - %3</translation>
</message>
<message>
<location filename="../Window.cpp" line="1058"/>
<location filename="../Window.cpp" line="1057"/>
<source>%1 - %2 (%3 fps) - %4</source>
<translation>%1 - %2 (%3 Bilder/Sekunde) - %4</translation>
</message>
@ -4151,12 +4151,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<translation>e-Reader-Karte (*.raw *.bin *.bmp)</translation>
</message>
<message>
<location filename="../Window.cpp" line="859"/>
<location filename="../Window.cpp" line="858"/>
<source>Couldn&apos;t Start</source>
<translation>Konnte nicht gestartet werden</translation>
</message>
<message>
<location filename="../Window.cpp" line="860"/>
<location filename="../Window.cpp" line="859"/>
<source>Could not start game.</source>
<translation>Spiel konnte nicht gestartet werden.</translation>
</message>
@ -4748,7 +4748,13 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
<translation>Bildwiederholrate in der Titelleiste anzeigen</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1851"/>
<location filename="../SettingsView.ui" line="961"/>
<source>Enable Game Boy Player features by default</source>
<translation>Game Boy Player-Features
standardmäßig aktivieren</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1858"/>
<source>Super Game Boy/Game Boy Color model:</source>
<translation>Super Game Boy/Game Boy Color-Modell:</translation>
</message>
@ -4799,107 +4805,107 @@ Titelleiste anzeigen</translation>
<translation>Vorlauf-Geschwindigkeit (halten):</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="965"/>
<location filename="../SettingsView.ui" line="972"/>
<source>Video renderer:</source>
<translation>Video-Renderer:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="973"/>
<location filename="../SettingsView.ui" line="980"/>
<source>Software</source>
<translation>Software</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="978"/>
<location filename="../SettingsView.ui" line="985"/>
<source>OpenGL</source>
<translation>OpenGL</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="986"/>
<location filename="../SettingsView.ui" line="993"/>
<source>OpenGL enhancements</source>
<translation>OpenGL-Verbesserungen</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="992"/>
<location filename="../SettingsView.ui" line="999"/>
<source>High-resolution scale:</source>
<translation>Hochauflösende Skalierung:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1055"/>
<location filename="../SettingsView.ui" line="1062"/>
<source>XQ GBA audio (experimental)</source>
<translation>XQ GBA-Audio (experimentell)</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1394"/>
<location filename="../SettingsView.ui" line="1401"/>
<source>Cheats</source>
<translation>Cheats</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1451"/>
<location filename="../SettingsView.ui" line="1458"/>
<source>Log to file</source>
<translation>In Datei protokollieren</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1458"/>
<location filename="../SettingsView.ui" line="1465"/>
<source>Log to console</source>
<translation>Auf die Konsole protokollieren</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1472"/>
<location filename="../SettingsView.ui" line="1479"/>
<source>Select Log File</source>
<translation>Protokoll-Datei auswählen</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1485"/>
<location filename="../SettingsView.ui" line="1492"/>
<source>Game Boy-only model:</source>
<translation>Game Boy-Modell:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1505"/>
<location filename="../SettingsView.ui" line="1512"/>
<source>Game Boy Color-only model:</source>
<translation>Game Boy Color-Modell:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1515"/>
<location filename="../SettingsView.ui" line="1522"/>
<source>Game Boy/Game Boy Color model:</source>
<translation>Game Boy/Game Boy Color-Modell:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1828"/>
<location filename="../SettingsView.ui" line="1835"/>
<source>Camera:</source>
<translation>Kamera:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1529"/>
<location filename="../SettingsView.ui" line="1536"/>
<source>Default BG colors:</source>
<translation>Standard-Hintergrundfarben:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1616"/>
<location filename="../SettingsView.ui" line="1623"/>
<source>Default sprite colors 1:</source>
<translation>Standard-Sprite-Farben 1:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1703"/>
<location filename="../SettingsView.ui" line="1710"/>
<source>Default sprite colors 2:</source>
<translation>Standard-Sprite-Farben 2:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1790"/>
<location filename="../SettingsView.ui" line="1797"/>
<source>Use GBC colors in GB games</source>
<translation>Verwende GBC-Farben in GB-Spielen</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1797"/>
<location filename="../SettingsView.ui" line="1804"/>
<source>Super Game Boy borders</source>
<translation>Super Game Boy-Rahmen</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1495"/>
<location filename="../SettingsView.ui" line="1502"/>
<source>Super Game Boy model:</source>
<translation>Super Game Boy-Modell:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1811"/>
<location filename="../SettingsView.ui" line="1818"/>
<source>Camera driver:</source>
<translation>Kamera-Treiber:</translation>
</message>
@ -4930,15 +4936,15 @@ Titelleiste anzeigen</translation>
in Arbeitsspeicher vorladen</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1085"/>
<location filename="../SettingsView.ui" line="1123"/>
<location filename="../SettingsView.ui" line="1158"/>
<location filename="../SettingsView.ui" line="1186"/>
<location filename="../SettingsView.ui" line="1227"/>
<location filename="../SettingsView.ui" line="1275"/>
<location filename="../SettingsView.ui" line="1323"/>
<location filename="../SettingsView.ui" line="1371"/>
<location filename="../SettingsView.ui" line="1419"/>
<location filename="../SettingsView.ui" line="1092"/>
<location filename="../SettingsView.ui" line="1130"/>
<location filename="../SettingsView.ui" line="1165"/>
<location filename="../SettingsView.ui" line="1193"/>
<location filename="../SettingsView.ui" line="1234"/>
<location filename="../SettingsView.ui" line="1282"/>
<location filename="../SettingsView.ui" line="1330"/>
<location filename="../SettingsView.ui" line="1378"/>
<location filename="../SettingsView.ui" line="1426"/>
<source>Browse</source>
<translation>Durchsuchen</translation>
</message>
@ -4953,20 +4959,20 @@ in Arbeitsspeicher vorladen</translation>
<translation>DS-BIOS 9:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1077"/>
<location filename="../SettingsView.ui" line="1101"/>
<source>Use BIOS file if found</source>
<translation>BIOS-Datei verwenden,
wenn vorhanden</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1104"/>
<location filename="../SettingsView.ui" line="1111"/>
<source>Skip BIOS intro</source>
<translation>BIOS-Intro überspringen</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="709"/>
<location filename="../SettingsView.ui" line="752"/>
<location filename="../SettingsView.ui" line="1001"/>
<location filename="../SettingsView.ui" line="1008"/>
<source>×</source>
<translation>×</translation>
</message>
@ -5065,22 +5071,22 @@ wenn vorhanden</translation>
<translation>Autofeuer-Intervall:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1030"/>
<location filename="../SettingsView.ui" line="1037"/>
<source>(240×160)</source>
<translation>(240×160)</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1066"/>
<location filename="../SettingsView.ui" line="1073"/>
<source>GB BIOS file:</source>
<translation>Datei mit GB-BIOS:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1132"/>
<location filename="../SettingsView.ui" line="1139"/>
<source>GBA BIOS file:</source>
<translation>Datei mit GBA-BIOS:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1139"/>
<location filename="../SettingsView.ui" line="1146"/>
<source>GBC BIOS file:</source>
<translation>Datei mit GBC-BIOS:</translation>
</message>
@ -5090,36 +5096,36 @@ wenn vorhanden</translation>
<translation>DS-Firmware:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1150"/>
<location filename="../SettingsView.ui" line="1174"/>
<source>SGB BIOS file:</source>
<translation>Datei mit SGB-BIOS:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1202"/>
<location filename="../SettingsView.ui" line="1209"/>
<source>Save games</source>
<translation>Spielstände</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1236"/>
<location filename="../SettingsView.ui" line="1284"/>
<location filename="../SettingsView.ui" line="1332"/>
<location filename="../SettingsView.ui" line="1380"/>
<location filename="../SettingsView.ui" line="1428"/>
<location filename="../SettingsView.ui" line="1243"/>
<location filename="../SettingsView.ui" line="1291"/>
<location filename="../SettingsView.ui" line="1339"/>
<location filename="../SettingsView.ui" line="1387"/>
<location filename="../SettingsView.ui" line="1435"/>
<source>Same directory as the ROM</source>
<translation>Verzeichnis der ROM-Datei</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1250"/>
<location filename="../SettingsView.ui" line="1257"/>
<source>Save states</source>
<translation>Savestates</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1298"/>
<location filename="../SettingsView.ui" line="1305"/>
<source>Screenshots</source>
<translation>Screenshots</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="1346"/>
<location filename="../SettingsView.ui" line="1353"/>
<source>Patches</source>
<translation>Patches</translation>
</message>

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,11 @@ static bool _checkWatchpoints(struct SM83Debugger* debugger, uint16_t address, s
return false;
}
}
info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address);
uint8_t oldValue = debugger->originalMemory.load8(debugger->cpu, address);
if ((watchpoint->type & WATCHPOINT_CHANGE) && newValue == oldValue) {
continue;
}
info->type.wp.oldValue = oldValue;
info->type.wp.newValue = newValue;
info->address = address;
info->type.wp.watchType = watchpoint->type;