mirror of https://github.com/mgba-emu/mgba.git
Compare commits
8 Commits
37d10385a3
...
53adae6a5a
Author | SHA1 | Date |
---|---|---|
Felix Jones | 53adae6a5a | |
Vicki Pfau | 23531e24b8 | |
Vicki Pfau | 72ee6f9840 | |
Vicki Pfau | d55ee872e3 | |
Vicki Pfau | abb9bec571 | |
Vicki Pfau | 6b5638efda | |
Felix Jones | 536c34c9ad | |
Felix Jones | ac6f3a3060 |
2
CHANGES
2
CHANGES
|
@ -19,7 +19,7 @@ Other fixes:
|
|||
- Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963)
|
||||
- Debugger: Fix writing to specific segment in command-line debugger
|
||||
- FFmpeg: Fix failing to record videos with CRF video (fixes mgba.io/i/3368)
|
||||
- GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1
|
||||
- GB Core: Fix cloning savedata when backing file is outdated (fixes mgba.io/i/3388)
|
||||
- GBA: Fix getting game info for multiboot ROMs
|
||||
- GBA Core: Fix booting into BIOS when skip BIOS is enabled
|
||||
- GBA Hardware: Fix loading states unconditionally overwriting GPIO memory
|
||||
|
|
|
@ -769,7 +769,7 @@ static int64_t _addCallbackToBreakpoint(struct mScriptDebugger* debugger, struct
|
|||
return cbid;
|
||||
}
|
||||
|
||||
static void _runCallbacks(struct mScriptDebugger* debugger, struct mScriptBreakpoint* point) {
|
||||
static void _runCallbacks(struct mScriptDebugger* debugger, struct mScriptBreakpoint* point, struct mScriptValue* info) {
|
||||
struct TableIterator iter;
|
||||
if (!HashTableIteratorStart(&point->callbacks, &iter)) {
|
||||
return;
|
||||
|
@ -778,6 +778,7 @@ static void _runCallbacks(struct mScriptDebugger* debugger, struct mScriptBreakp
|
|||
struct mScriptValue* fn = HashTableIteratorGetValue(&point->callbacks, &iter);
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.stack, WTABLE, info);
|
||||
mScriptContextInvoke(debugger->p->context, fn, &frame);
|
||||
mScriptFrameDeinit(&frame);
|
||||
} while (HashTableIteratorNext(&point->callbacks, &iter));
|
||||
|
@ -826,7 +827,50 @@ static void _scriptDebuggerEntered(struct mDebuggerModule* debugger, enum mDebug
|
|||
return;
|
||||
}
|
||||
|
||||
_runCallbacks(scriptDebugger, point);
|
||||
struct mScriptValue cbInfo = {
|
||||
.refs = mSCRIPT_VALUE_UNREF,
|
||||
.flags = 0,
|
||||
.type = mSCRIPT_TYPE_MS_TABLE,
|
||||
};
|
||||
cbInfo.type->alloc(&cbInfo);
|
||||
|
||||
static struct mScriptValue keyAddress = mSCRIPT_MAKE_CHARP("address");
|
||||
static struct mScriptValue keyWidth = mSCRIPT_MAKE_CHARP("width");
|
||||
static struct mScriptValue keySegment = mSCRIPT_MAKE_CHARP("segment");
|
||||
static struct mScriptValue keyOldValue = mSCRIPT_MAKE_CHARP("oldValue");
|
||||
static struct mScriptValue keyNewValue = mSCRIPT_MAKE_CHARP("newValue");
|
||||
static struct mScriptValue keyAccessType = mSCRIPT_MAKE_CHARP("accessType");
|
||||
|
||||
struct mScriptValue valAddress = mSCRIPT_MAKE_U32(info->address);
|
||||
struct mScriptValue valWidth = mSCRIPT_MAKE_S32(info->width);
|
||||
struct mScriptValue valSegment = mSCRIPT_MAKE_S32(info->segment);
|
||||
struct mScriptValue valOldValue;
|
||||
struct mScriptValue valNewValue;
|
||||
struct mScriptValue valAccessType;
|
||||
|
||||
mScriptTableInsert(&cbInfo, &keyAddress, &valAddress);
|
||||
if (info->width > 0) {
|
||||
mScriptTableInsert(&cbInfo, &keyWidth, &valWidth);
|
||||
}
|
||||
if (info->segment >= 0) {
|
||||
mScriptTableInsert(&cbInfo, &keySegment, &valSegment);
|
||||
}
|
||||
|
||||
if (reason == DEBUGGER_ENTER_WATCHPOINT) {
|
||||
valOldValue = mSCRIPT_MAKE_S32(info->type.wp.oldValue);
|
||||
valNewValue = mSCRIPT_MAKE_S32(info->type.wp.newValue);
|
||||
valAccessType = mSCRIPT_MAKE_S32(info->type.wp.accessType);
|
||||
|
||||
mScriptTableInsert(&cbInfo, &keyOldValue, &valOldValue);
|
||||
if (info->type.wp.accessType != WATCHPOINT_READ) {
|
||||
mScriptTableInsert(&cbInfo, &keyNewValue, &valNewValue);
|
||||
}
|
||||
mScriptTableInsert(&cbInfo, &keyAccessType, &valAccessType);
|
||||
}
|
||||
|
||||
_runCallbacks(scriptDebugger, point, &cbInfo);
|
||||
|
||||
cbInfo.type->free(&cbInfo);
|
||||
debugger->isPaused = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -249,9 +249,16 @@ static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) {
|
|||
}
|
||||
const uint8_t* data = chunk->data;
|
||||
data += sizeof(uint32_t) * 2;
|
||||
uncompress((Bytef*) item.data, &len, data, chunk->size);
|
||||
item.size = len;
|
||||
mStateExtdataPut(extdata, tag, &item);
|
||||
if (uncompress((Bytef*) item.data, &len, data, chunk->size) == Z_OK) {
|
||||
if (item.size != len) {
|
||||
mLOG(SAVESTATE, WARN, "Mismatched decompressed extdata %i size (%"PRIz"u vs %"PRIz"u)", tag, item.size, (size_t) len);
|
||||
item.size = len;
|
||||
}
|
||||
mStateExtdataPut(extdata, tag, &item);
|
||||
} else {
|
||||
mLOG(SAVESTATE, WARN, "Failed to decompress extdata chunk");
|
||||
free(item.data);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -320,7 +327,7 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx
|
|||
return state;
|
||||
}
|
||||
|
||||
static bool _loadPNGExtadata(struct VFile* vf, struct mStateExtdata* extdata) {
|
||||
static bool _loadPNGExtdata(struct VFile* vf, struct mStateExtdata* extdata) {
|
||||
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
|
||||
png_infop info = png_create_info_struct(png);
|
||||
png_infop end = png_create_info_struct(png);
|
||||
|
@ -511,7 +518,7 @@ void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtda
|
|||
bool mCoreExtractExtdata(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata) {
|
||||
#ifdef USE_PNG
|
||||
if (isPNG(vf)) {
|
||||
return _loadPNGExtadata(vf, extdata);
|
||||
return _loadPNGExtdata(vf, extdata);
|
||||
}
|
||||
#endif
|
||||
if (!core) {
|
||||
|
|
|
@ -365,8 +365,10 @@ M_TEST_DEFINE(basicBreakpointGBA) {
|
|||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
"address = nil\n"
|
||||
"function bkpt(info)\n"
|
||||
" hit = hit + 1\n"
|
||||
" address = info.address\n"
|
||||
"end"
|
||||
);
|
||||
TEST_PROGRAM("cbid = emu:setBreakpoint(bkpt, 0x020000C4)");
|
||||
|
@ -379,6 +381,7 @@ M_TEST_DEFINE(basicBreakpointGBA) {
|
|||
|
||||
assert_int_equal(debugger.state, DEBUGGER_RUNNING);
|
||||
TEST_PROGRAM("assert(hit >= 1)");
|
||||
TEST_PROGRAM("assert(address == 0x020000C4)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
|
@ -404,8 +407,10 @@ M_TEST_DEFINE(basicBreakpointGB) {
|
|||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
"address = nil\n"
|
||||
"function bkpt(info)\n"
|
||||
" hit = hit + 1\n"
|
||||
" address = info.address\n"
|
||||
"end"
|
||||
);
|
||||
TEST_PROGRAM("cbid = emu:setBreakpoint(bkpt, 0xF0)");
|
||||
|
@ -418,6 +423,7 @@ M_TEST_DEFINE(basicBreakpointGB) {
|
|||
|
||||
assert_int_equal(debugger.state, DEBUGGER_RUNNING);
|
||||
TEST_PROGRAM("assert(hit >= 1)");
|
||||
TEST_PROGRAM("assert(address == 0xF0)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
|
@ -441,11 +447,22 @@ M_TEST_DEFINE(multipleBreakpoint) {
|
|||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt1()\n"
|
||||
"address = nil\n"
|
||||
"function bkpt1(info)\n"
|
||||
" hit = hit + 1\n"
|
||||
" if address then\n"
|
||||
" address = (address + info.address) / 2\n"
|
||||
" else\n"
|
||||
" address = info.address\n"
|
||||
" end\n"
|
||||
"end\n"
|
||||
"function bkpt2()\n"
|
||||
"function bkpt2(info)\n"
|
||||
" hit = hit + 100\n"
|
||||
" if address then\n"
|
||||
" address = (address + info.address) / 2\n"
|
||||
" else\n"
|
||||
" address = info.address\n"
|
||||
" end\n"
|
||||
"end"
|
||||
);
|
||||
#ifdef M_CORE_GBA
|
||||
|
@ -465,6 +482,13 @@ M_TEST_DEFINE(multipleBreakpoint) {
|
|||
|
||||
assert_int_equal(debugger.state, DEBUGGER_RUNNING);
|
||||
TEST_PROGRAM("assert(hit >= 101)");
|
||||
#ifdef M_CORE_GBA
|
||||
TEST_PROGRAM("assert(address >= 0x020000C4)");
|
||||
TEST_PROGRAM("assert(address <= 0x020000C8)");
|
||||
#else
|
||||
TEST_PROGRAM("assert(address >= 0xF0)");
|
||||
TEST_PROGRAM("assert(address <= 0xF1)");
|
||||
#endif
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
|
@ -479,13 +503,28 @@ M_TEST_DEFINE(basicWatchpoint) {
|
|||
core->reset(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
core->busWrite8(core, RAM_BASE + i, i + 1);
|
||||
}
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
"address = nil\n"
|
||||
"width = nil\n"
|
||||
"oldValue = nil\n"
|
||||
"newValue = nil\n"
|
||||
"accessType = nil\n"
|
||||
"function bkpt(info)\n"
|
||||
" hit = hit + 1\n"
|
||||
" address = info.address\n"
|
||||
" width = info.width\n"
|
||||
" oldValue = info.oldValue\n"
|
||||
" newValue = info.newValue\n"
|
||||
" accessType = info.accessType\n"
|
||||
"end"
|
||||
);
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
|
@ -499,7 +538,13 @@ M_TEST_DEFINE(basicWatchpoint) {
|
|||
uint8_t value;
|
||||
|
||||
// Read
|
||||
TEST_PROGRAM("hit = 0");
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"address = nil\n"
|
||||
"width = nil\n"
|
||||
"oldValue = nil\n"
|
||||
"newValue = nil\n"
|
||||
"accessType = nil\n");
|
||||
value = core->rawRead8(core, RAM_BASE, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
|
@ -508,31 +553,71 @@ M_TEST_DEFINE(basicWatchpoint) {
|
|||
TEST_PROGRAM("assert(hit == 1)");
|
||||
core->busWrite8(core, RAM_BASE, ~value);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
TEST_PROGRAM("assert(address == base)");
|
||||
TEST_PROGRAM("assert(width == 1)");
|
||||
TEST_PROGRAM("assert(oldValue == 1)");
|
||||
TEST_PROGRAM("assert(newValue == nil)");
|
||||
TEST_PROGRAM("assert(accessType == C.WATCHPOINT_TYPE.READ)");
|
||||
|
||||
// Write
|
||||
TEST_PROGRAM("hit = 0");
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"address = nil\n"
|
||||
"width = nil\n"
|
||||
"oldValue = nil\n"
|
||||
"newValue = nil\n"
|
||||
"accessType = nil\n");
|
||||
value = core->rawRead8(core, RAM_BASE + 1, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE + 1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busWrite8(core, RAM_BASE + 1, value);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
TEST_PROGRAM("assert(oldValue == 2)");
|
||||
TEST_PROGRAM("assert(newValue == 2)");
|
||||
core->busWrite8(core, RAM_BASE + 1, ~value);
|
||||
TEST_PROGRAM("assert(hit == 2)");
|
||||
TEST_PROGRAM("assert(address == base + 1)");
|
||||
TEST_PROGRAM("assert(width == 1)");
|
||||
TEST_PROGRAM("assert(oldValue == 2)");
|
||||
TEST_PROGRAM("assert(newValue == -3)");
|
||||
TEST_PROGRAM("assert(accessType == C.WATCHPOINT_TYPE.WRITE)");
|
||||
|
||||
// RW
|
||||
TEST_PROGRAM("hit = 0");
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"address = nil\n"
|
||||
"width = nil\n"
|
||||
"oldValue = nil\n"
|
||||
"newValue = nil\n"
|
||||
"accessType = nil\n");
|
||||
value = core->rawRead8(core, RAM_BASE + 2, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE + 2);
|
||||
TEST_PROGRAM("assert(accessType == C.WATCHPOINT_TYPE.READ)");
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
TEST_PROGRAM("assert(oldValue == 3)");
|
||||
TEST_PROGRAM("assert(newValue == nil)");
|
||||
core->busWrite8(core, RAM_BASE + 2, value);
|
||||
TEST_PROGRAM("assert(hit == 2)");
|
||||
TEST_PROGRAM("assert(oldValue == 3)");
|
||||
TEST_PROGRAM("assert(newValue == 3)");
|
||||
core->busWrite8(core, RAM_BASE + 2, ~value);
|
||||
TEST_PROGRAM("assert(hit == 3)");
|
||||
TEST_PROGRAM("assert(address == base + 2)");
|
||||
TEST_PROGRAM("assert(width == 1)");
|
||||
TEST_PROGRAM("assert(oldValue == 3)");
|
||||
TEST_PROGRAM("assert(newValue == -4)");
|
||||
TEST_PROGRAM("assert(accessType == C.WATCHPOINT_TYPE.WRITE)");
|
||||
|
||||
// Change
|
||||
TEST_PROGRAM("hit = 0");
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"address = nil\n"
|
||||
"width = nil\n"
|
||||
"oldValue = nil\n"
|
||||
"newValue = nil\n"
|
||||
"accessType = nil\n");
|
||||
value = core->rawRead8(core, RAM_BASE + 3, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE + 3);
|
||||
|
@ -541,6 +626,11 @@ M_TEST_DEFINE(basicWatchpoint) {
|
|||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busWrite8(core, RAM_BASE + 3, ~value);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
TEST_PROGRAM("assert(address == base + 3)");
|
||||
TEST_PROGRAM("assert(width == 1)");
|
||||
TEST_PROGRAM("assert(oldValue == 4)");
|
||||
TEST_PROGRAM("assert(newValue == -5)");
|
||||
TEST_PROGRAM("assert(accessType == C.WATCHPOINT_TYPE.WRITE)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
|
@ -728,8 +818,10 @@ M_TEST_DEFINE(rangeWatchpoint) {
|
|||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
"address = nil\n"
|
||||
"function bkpt(info)\n"
|
||||
" hit = hit + 1\n"
|
||||
" address = info.address\n"
|
||||
"end"
|
||||
);
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
|
@ -741,10 +833,13 @@ M_TEST_DEFINE(rangeWatchpoint) {
|
|||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
TEST_PROGRAM("assert(address == base)");
|
||||
core->busRead8(core, RAM_BASE + 1);
|
||||
TEST_PROGRAM("assert(hit == 3)");
|
||||
TEST_PROGRAM("assert(address == base + 1)");
|
||||
core->busRead8(core, RAM_BASE + 2);
|
||||
TEST_PROGRAM("assert(hit == 4)");
|
||||
TEST_PROGRAM("assert(address == base + 2)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
|
|
|
@ -1167,19 +1167,32 @@ static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
|
|||
|
||||
static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
|
||||
struct GB* gb = core->board;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (vf) {
|
||||
*sram = malloc(vf->size(vf));
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
return vf->read(vf, *sram, vf->size(vf));
|
||||
size_t sramSize = gb->sramSize;
|
||||
size_t vfSize = 0;
|
||||
size_t size = sramSize;
|
||||
uint8_t* view = NULL;
|
||||
|
||||
if (gb->sramVf) {
|
||||
vfSize = gb->sramVf->size(gb->sramVf);
|
||||
if (vfSize > size) {
|
||||
size = vfSize;
|
||||
}
|
||||
}
|
||||
if (gb->sramSize) {
|
||||
*sram = malloc(gb->sramSize);
|
||||
memcpy(*sram, gb->memory.sram, gb->sramSize);
|
||||
return gb->sramSize;
|
||||
if (!size) {
|
||||
*sram = NULL;
|
||||
return 0;
|
||||
}
|
||||
*sram = NULL;
|
||||
return 0;
|
||||
|
||||
view = malloc(size);
|
||||
if (sramSize) {
|
||||
memcpy(view, gb->memory.sram, gb->sramSize);
|
||||
}
|
||||
if (vfSize > sramSize) {
|
||||
gb->sramVf->seek(gb->sramVf, sramSize, SEEK_SET);
|
||||
gb->sramVf->read(gb->sramVf, &view[sramSize], vfSize - sramSize);
|
||||
}
|
||||
*sram = view;
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
|
||||
|
|
|
@ -430,6 +430,23 @@ void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overr
|
|||
GBAOverrideApply(gba, &override);
|
||||
} else if (GBAOverrideFind(overrides, &override)) {
|
||||
GBAOverrideApply(gba, &override);
|
||||
} else {
|
||||
// Apply Everdrive UTTD save detection
|
||||
switch (override.id[0]) {
|
||||
case '1':
|
||||
override.savetype = SAVEDATA_EEPROM;
|
||||
break;
|
||||
case '2':
|
||||
override.savetype = SAVEDATA_SRAM;
|
||||
break;
|
||||
case '3':
|
||||
override.savetype = SAVEDATA_FLASH512;
|
||||
break;
|
||||
case '4':
|
||||
override.savetype = SAVEDATA_FLASH1M;
|
||||
break;
|
||||
}
|
||||
GBAOverrideApply(gba, &override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue