mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
690f716df5
3
CHANGES
3
CHANGES
|
@ -48,6 +48,9 @@ Emulation fixes:
|
||||||
- GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793)
|
- 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 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 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)
|
- GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722)
|
||||||
Other fixes:
|
Other fixes:
|
||||||
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)
|
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)
|
||||||
|
|
|
@ -110,13 +110,13 @@ typedef intptr_t ssize_t;
|
||||||
#define ATOMIC_LOAD_PTR(DST, SRC) DST = InterlockedCompareExchangePointer(&SRC, 0, 0)
|
#define ATOMIC_LOAD_PTR(DST, SRC) DST = InterlockedCompareExchangePointer(&SRC, 0, 0)
|
||||||
#else
|
#else
|
||||||
// TODO
|
// TODO
|
||||||
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
#define ATOMIC_STORE(DST, SRC) ((DST) = (SRC))
|
||||||
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
#define ATOMIC_LOAD(DST, SRC) ((DST) = (SRC))
|
||||||
#define ATOMIC_ADD(DST, OP) DST += OP
|
#define ATOMIC_ADD(DST, OP) ((DST) += (OP))
|
||||||
#define ATOMIC_SUB(DST, OP) DST -= OP
|
#define ATOMIC_SUB(DST, OP) ((DST) -= (OP))
|
||||||
#define ATOMIC_OR(DST, OP) DST |= OP
|
#define ATOMIC_OR(DST, OP) ((DST) |= (OP))
|
||||||
#define ATOMIC_AND(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_CMPXCHG(DST, EXPECTED, OP) (((DST) == (EXPECTED)) ? (((DST) = (OP)), true) : false)
|
||||||
#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
||||||
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -249,6 +249,7 @@ mSCRIPT_DECLARE_STRUCT(mScriptGamepad);
|
||||||
void mScriptContextAttachInput(struct mScriptContext* context);
|
void mScriptContextAttachInput(struct mScriptContext* context);
|
||||||
|
|
||||||
void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*);
|
void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*);
|
||||||
|
void mScriptContextClearKeys(struct mScriptContext*);
|
||||||
|
|
||||||
int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*);
|
int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*);
|
||||||
bool mScriptContextGamepadDetach(struct mScriptContext*, int pad);
|
bool mScriptContextGamepadDetach(struct mScriptContext*, int pad);
|
||||||
|
|
|
@ -127,7 +127,11 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
||||||
break;
|
break;
|
||||||
case SIO_NORMAL_8:
|
case SIO_NORMAL_8:
|
||||||
case SIO_NORMAL_32:
|
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;
|
node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -507,11 +511,28 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
||||||
|
|
||||||
if (address == REG_SIOCNT) {
|
if (address == REG_SIOCNT) {
|
||||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value);
|
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value);
|
||||||
|
int attached;
|
||||||
|
ATOMIC_LOAD(attached, node->p->attachedNormal);
|
||||||
value &= 0xFF8B;
|
value &= 0xFF8B;
|
||||||
if (!node->id) {
|
if (node->id < 3 && attached > node->id + 1) {
|
||||||
value = GBASIONormalClearSi(value);
|
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) {
|
if (!node->id) {
|
||||||
// Frequency
|
// Frequency
|
||||||
int32_t cycles;
|
int32_t cycles;
|
||||||
|
@ -524,9 +545,6 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
||||||
cycles *= 4;
|
cycles *= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum mLockstepPhase transferActive;
|
|
||||||
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
|
|
||||||
|
|
||||||
if (transferActive == TRANSFER_IDLE) {
|
if (transferActive == TRANSFER_IDLE) {
|
||||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
|
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
|
||||||
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
|
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
|
||||||
|
@ -541,7 +559,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
||||||
value &= ~0x0080;
|
value &= ~0x0080;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (address == REG_SIODATA32_LO) {
|
} else if (address == REG_SIODATA32_LO) {
|
||||||
|
|
|
@ -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) {
|
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;
|
++*io;
|
||||||
if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
|
if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
|
||||||
GBATimerUpdate(timing, nextTimer, io, cyclesLate);
|
GBATimerUpdate(timing, nextTimer, io, cyclesLate);
|
||||||
|
|
|
@ -155,6 +155,10 @@ void ScriptingController::event(QObject* obj, QEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (event->type()) {
|
switch (event->type()) {
|
||||||
|
case QEvent::FocusOut:
|
||||||
|
case QEvent::WindowDeactivate:
|
||||||
|
mScriptContextClearKeys(&m_scriptContext);
|
||||||
|
return;
|
||||||
case QEvent::KeyPress:
|
case QEvent::KeyPress:
|
||||||
case QEvent::KeyRelease: {
|
case QEvent::KeyRelease: {
|
||||||
struct mScriptKeyEvent ev{mSCRIPT_EV_TYPE_KEY};
|
struct mScriptKeyEvent ev{mSCRIPT_EV_TYPE_KEY};
|
||||||
|
|
|
@ -427,6 +427,44 @@ void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent
|
||||||
mScriptListDeinit(&args);
|
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) {
|
int mScriptContextGamepadAttach(struct mScriptContext* context, struct mScriptGamepad* pad) {
|
||||||
struct mScriptValue* input = mScriptContextGetGlobal(context, "input");
|
struct mScriptValue* input = mScriptContextGetGlobal(context, "input");
|
||||||
if (!input) {
|
if (!input) {
|
||||||
|
|
|
@ -131,6 +131,50 @@ M_TEST_DEFINE(activeKeys) {
|
||||||
mScriptContextDeinit(&context);
|
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) {
|
M_TEST_DEFINE(gamepadExport) {
|
||||||
SETUP_LUA;
|
SETUP_LUA;
|
||||||
|
|
||||||
|
@ -176,5 +220,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput,
|
||||||
cmocka_unit_test(seq),
|
cmocka_unit_test(seq),
|
||||||
cmocka_unit_test(fireKey),
|
cmocka_unit_test(fireKey),
|
||||||
cmocka_unit_test(activeKeys),
|
cmocka_unit_test(activeKeys),
|
||||||
|
cmocka_unit_test(clearKeys),
|
||||||
cmocka_unit_test(gamepadExport),
|
cmocka_unit_test(gamepadExport),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue