Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2024-06-21 00:22:10 -07:00
commit 690f716df5
8 changed files with 125 additions and 16 deletions

View File

@ -48,6 +48,9 @@ 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 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:
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)

View File

@ -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

View File

@ -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);

View File

@ -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,11 +511,28 @@ 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);
}
if (value & 0x0080) {
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 & 0x0081) == 0x0081) {
if (!node->id) {
// Frequency
int32_t cycles;
@ -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);
@ -541,7 +559,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
value &= ~0x0080;
}
} else {
// TODO
}
}
} else if (address == REG_SIODATA32_LO) {

View File

@ -37,7 +37,7 @@ static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLat
}
bool GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
if (GBATimerFlagsIsCountUp(nextTimer->flags) && GBATimerFlagsIsEnable(nextTimer->flags)) {
++*io;
if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
GBATimerUpdate(timing, nextTimer, io, cyclesLate);

View File

@ -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};

View File

@ -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) {

View File

@ -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),
)