From e95bd06321d485369987acf868f3c2066992d97c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 2 Feb 2023 21:34:14 -0800 Subject: [PATCH 1/5] Scripting: Clear down keys when the window is deactivated --- include/mgba/script/input.h | 1 + .../qt/scripting/ScriptingController.cpp | 4 ++ src/script/input.c | 38 ++++++++++++++++ src/script/test/input.c | 45 +++++++++++++++++++ 4 files changed, 88 insertions(+) diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h index ae1260ad1..fea9f3320 100644 --- a/include/mgba/script/input.h +++ b/include/mgba/script/input.h @@ -249,6 +249,7 @@ mSCRIPT_DECLARE_STRUCT(mScriptGamepad); void mScriptContextAttachInput(struct mScriptContext* context); void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*); +void mScriptContextClearKeys(struct mScriptContext*); int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*); bool mScriptContextGamepadDetach(struct mScriptContext*, int pad); diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index d8babf06d..b0fbd51ef 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -155,6 +155,10 @@ void ScriptingController::event(QObject* obj, QEvent* event) { } switch (event->type()) { + case QEvent::FocusOut: + case QEvent::WindowDeactivate: + mScriptContextClearKeys(&m_scriptContext); + return; case QEvent::KeyPress: case QEvent::KeyRelease: { struct mScriptKeyEvent ev{mSCRIPT_EV_TYPE_KEY}; diff --git a/src/script/input.c b/src/script/input.c index 23fc498e9..31f1f806a 100644 --- a/src/script/input.c +++ b/src/script/input.c @@ -427,6 +427,44 @@ void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent mScriptListDeinit(&args); } +void mScriptContextClearKeys(struct mScriptContext* context) { + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return; + } + struct mScriptInputContext* inputContext = input->value.opaque; + size_t keyCount = TableSize(&inputContext->activeKeys); + uint32_t* keys = calloc(keyCount, sizeof(uint32_t)); + struct TableIterator iter; + size_t i = 0; + if (!TableIteratorStart(&inputContext->activeKeys, &iter)) { + free(keys); + return; + } + do { + keys[i] = TableIteratorGetKey(&inputContext->activeKeys, &iter); + ++i; + } while (TableIteratorNext(&inputContext->activeKeys, &iter)); + + struct mScriptKeyEvent event = { + .d = { + .type = mSCRIPT_EV_TYPE_KEY + }, + .state = mSCRIPT_INPUT_STATE_UP, + .modifiers = 0 + }; + for (i = 0; i < keyCount; ++i) { + event.key = keys[i]; + intptr_t value = (intptr_t) TableLookup(&inputContext->activeKeys, event.key); + if (value > 1) { + TableInsert(&inputContext->activeKeys, event.key, (void*) 1); + } + mScriptContextFireEvent(context, &event.d); + } + + free(keys); +} + int mScriptContextGamepadAttach(struct mScriptContext* context, struct mScriptGamepad* pad) { struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); if (!input) { diff --git a/src/script/test/input.c b/src/script/test/input.c index 08b6a3bf8..825e005dd 100644 --- a/src/script/test/input.c +++ b/src/script/test/input.c @@ -131,6 +131,50 @@ M_TEST_DEFINE(activeKeys) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(clearKeys) { + SETUP_LUA; + + TEST_PROGRAM("assert(not input:isKeyActive('a'))"); + TEST_PROGRAM("assert(not input:isKeyActive('b'))"); + TEST_PROGRAM("assert(#input:activeKeys() == 0)"); + + struct mScriptKeyEvent keyEvent = { + .d = { .type = mSCRIPT_EV_TYPE_KEY }, + .state = mSCRIPT_INPUT_STATE_DOWN, + .key = 'a' + }; + mScriptContextFireEvent(&context, &keyEvent.d); + // This changes it to STATE_HELD, but increments the down counter + mScriptContextFireEvent(&context, &keyEvent.d); + keyEvent.state = mSCRIPT_INPUT_STATE_DOWN; + keyEvent.key = 'b'; + mScriptContextFireEvent(&context, &keyEvent.d); + + TEST_PROGRAM("assert(input:isKeyActive('a'))"); + TEST_PROGRAM("assert(input:isKeyActive('b'))"); + TEST_PROGRAM("assert(#input:activeKeys() == 2)"); + + TEST_PROGRAM( + "up = {}\n" + "function cb(ev)\n" + " assert(ev.type == C.EV_TYPE.KEY)\n" + " assert(ev.state == C.INPUT_STATE.UP)\n" + " table.insert(up, ev.key)\n" + "end\n" + "id = callbacks:add('key', cb)\n" + ); + + mScriptContextClearKeys(&context); + + TEST_PROGRAM("assert(not input:isKeyActive('a'))"); + TEST_PROGRAM("assert(not input:isKeyActive('b'))"); + TEST_PROGRAM("assert(#input:activeKeys() == 0)"); + TEST_PROGRAM("assert(#up == 2)"); + + mScriptContextDeinit(&context); +} + + M_TEST_DEFINE(gamepadExport) { SETUP_LUA; @@ -176,5 +220,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput, cmocka_unit_test(seq), cmocka_unit_test(fireKey), cmocka_unit_test(activeKeys), + cmocka_unit_test(clearKeys), cmocka_unit_test(gamepadExport), ) From e4707952586292d85724ebcb67b47bf0c9bc8a49 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 3 Feb 2023 01:43:34 -0800 Subject: [PATCH 2/5] GBA Timers: Cascading timers don't tick when disabled (fixes #2812) --- CHANGES | 1 + src/gba/timer.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 7045e46b6..556830b6e 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Emulation fixes: - GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793) - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805) + - GBA Timers: Cascading timers don't tick when disabled (fixes mgba.io/i/2812) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: - Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784) diff --git a/src/gba/timer.c b/src/gba/timer.c index 8a3a3e8f6..da054e12a 100644 --- a/src/gba/timer.c +++ b/src/gba/timer.c @@ -34,7 +34,7 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) { if (timerId < 3) { struct GBATimer* nextTimer = &gba->timers[timerId + 1]; - if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled? + if (GBATimerFlagsIsCountUp(nextTimer->flags) && GBATimerFlagsIsEnable(nextTimer->flags)) { ++gba->memory.io[REG_TMCNT_LO(timerId + 1) >> 1]; if (!gba->memory.io[REG_TMCNT_LO(timerId + 1) >> 1] && GBATimerFlagsIsEnable(nextTimer->flags)) { GBATimerUpdate(gba, timerId + 1, cyclesLate); From 4e85de3a42a96f6a1265dcf2d02563d086dfb41e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 3 Feb 2023 01:54:30 -0800 Subject: [PATCH 3/5] Util: Fix some macro best practices --- include/mgba-util/common.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index 4583ac0ed..ae86c8b3c 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -110,13 +110,13 @@ typedef intptr_t ssize_t; #define ATOMIC_LOAD_PTR(DST, SRC) DST = InterlockedCompareExchangePointer(&SRC, 0, 0) #else // TODO -#define ATOMIC_STORE(DST, SRC) DST = SRC -#define ATOMIC_LOAD(DST, SRC) DST = SRC -#define ATOMIC_ADD(DST, OP) DST += OP -#define ATOMIC_SUB(DST, OP) DST -= OP -#define ATOMIC_OR(DST, OP) DST |= OP -#define ATOMIC_AND(DST, OP) DST &= OP -#define ATOMIC_CMPXCHG(DST, EXPECTED, OP) ((DST == EXPECTED) ? ((DST = OP), true) : false) +#define ATOMIC_STORE(DST, SRC) ((DST) = (SRC)) +#define ATOMIC_LOAD(DST, SRC) ((DST) = (SRC)) +#define ATOMIC_ADD(DST, OP) ((DST) += (OP)) +#define ATOMIC_SUB(DST, OP) ((DST) -= (OP)) +#define ATOMIC_OR(DST, OP) ((DST) |= (OP)) +#define ATOMIC_AND(DST, OP) ((DST) &= (OP)) +#define ATOMIC_CMPXCHG(DST, EXPECTED, OP) (((DST) == (EXPECTED)) ? (((DST) = (OP)), true) : false) #define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC) #define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC) #endif From f046596ca7c80dfc52ec163555fbd0a4794fc91d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 3 Feb 2023 02:06:20 -0800 Subject: [PATCH 4/5] GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes #2810) --- CHANGES | 1 + src/gba/sio/lockstep.c | 30 ++++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 556830b6e..1ee88d5fd 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Emulation fixes: - GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793) - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805) + - GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes mgba.io/i/2810) - GBA Timers: Cascading timers don't tick when disabled (fixes mgba.io/i/2812) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index a03e88e30..db92c50a5 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -127,7 +127,11 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { break; case SIO_NORMAL_8: case SIO_NORMAL_32: - ATOMIC_ADD(node->p->attachedNormal, 1); + if (ATOMIC_ADD(node->p->attachedNormal, 1) > node->id + 1 && node->id < 3) { + node->d.p->siocnt = GBASIONormalSetSi(node->d.p->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id + 1]->d.p->siocnt)); + } else { + node->d.p->siocnt = GBASIONormalFillSi(node->d.p->siocnt); + } node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister; break; default: @@ -507,9 +511,26 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive if (address == REG_SIOCNT) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); + int attached; + ATOMIC_LOAD(attached, node->p->attachedNormal); value &= 0xFF8B; - if (!node->id) { - value = GBASIONormalClearSi(value); + if (node->id < 3 && attached > node->id + 1) { + value = GBASIONormalSetSi(value, GBASIONormalGetIdleSo(node->p->players[node->id + 1]->d.p->siocnt)); + } else { + value = GBASIONormalFillSi(value); + } + + enum mLockstepPhase transferActive; + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + if (node->id > 0 && transferActive == TRANSFER_IDLE) { + int try; + for (try = 0; try < 3; ++try) { + GBASIONormal parentSiocnt; + ATOMIC_LOAD(parentSiocnt, node->p->players[node->id - 1]->d.p->siocnt); + if (ATOMIC_CMPXCHG(node->p->players[node->id - 1]->d.p->siocnt, parentSiocnt, GBASIONormalSetSi(parentSiocnt, GBASIONormalGetIdleSo(value)))) { + break; + } + } } if (value & 0x0080) { if (!node->id) { @@ -524,9 +545,6 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive cycles *= 4; } - enum mLockstepPhase transferActive; - ATOMIC_LOAD(transferActive, node->p->d.transferActive); - if (transferActive == TRANSFER_IDLE) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); From 527313bafc2e6be15c250f64490f08e3c01eb63b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 3 Feb 2023 02:08:14 -0800 Subject: [PATCH 5/5] GBA SIO: Normal mode transfers with no clock should not finish (fixes #2811) --- CHANGES | 1 + src/gba/sio/lockstep.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 1ee88d5fd..028537466 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,7 @@ Emulation fixes: - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805) - GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes mgba.io/i/2810) + - GBA SIO: Normal mode transfers with no clock should not finish (fixes mgba.io/i/2811) - GBA Timers: Cascading timers don't tick when disabled (fixes mgba.io/i/2812) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index db92c50a5..3ea1b5cc9 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -532,7 +532,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive } } } - if (value & 0x0080) { + if ((value & 0x0081) == 0x0081) { if (!node->id) { // Frequency int32_t cycles; @@ -559,7 +559,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive value &= ~0x0080; } } else { - + // TODO } } } else if (address == REG_SIODATA32_LO) {