GBA SIO: Improve RCNT emulation

This commit is contained in:
Vicki Pfau 2024-09-15 23:26:58 -07:00
parent 0425dadee9
commit 435c4aa243
1 changed files with 32 additions and 8 deletions

View File

@ -201,12 +201,23 @@ void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASI
}
void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) {
sio->rcnt &= 0xF;
sio->rcnt |= value & ~0xF;
sio->rcnt &= 0x1FF;
sio->rcnt |= value & 0xC000;
_switchMode(sio);
if (sio->activeDriver && sio->activeDriver->writeRCNT) {
switch (sio->mode) {
case GBA_SIO_GPIO:
sio->rcnt = (sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01FF) | (sio->rcnt & 0xC000);
break;
default:
sio->rcnt = (sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01F0) | (sio->rcnt & 0xC00F);
}
} else if (sio->mode == GBA_SIO_GPIO) {
sio->rcnt &= 0xC000;
sio->rcnt |= sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01FF;
sio->rcnt |= value & 0x1FF;
} else {
sio->rcnt &= 0xC00F;
sio->rcnt |= value & 0x1F0;
}
}
@ -235,6 +246,24 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
value = GBASIOMultiplayerSetSlave(value, id || !connected);
value = GBASIOMultiplayerSetId(value, id);
value |= sio->siocnt & 0x00FC;
// SC appears to float in multi mode when not doing a transfer. While
// it does spike at the end of a transfer, it appears to die down after
// around 20-30 microseconds. However, the docs on akkit.org
// (http://www.akkit.org/info/gba_comms.html) say this is high until
// a transfer starts and low while active. Further, the Mario Bros.
// multiplayer expects SC to be high in multi mode. This needs better
// investigation than I managed, apparently.
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
break;
case GBA_SIO_NORMAL_8:
case GBA_SIO_NORMAL_32:
// This line is pulled up by the clock owner while the clock is idle.
// If there is no clock owner it's just hi-Z.
if (GBASIONormalGetSc(value)) {
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
}
break;
default:
// TODO
@ -417,11 +446,6 @@ void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint3
sio->siocnt = GBASIOMultiplayerClearBusy(sio->siocnt);
sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, id);
// This SC level is actually a transient pulse, and probably a hardware glitch.
// Based on analog sampling it seems to just be a spike when the other lines deassert.
// It rapidly falls down to GND but it's high enough that it's read out as a 1 for
// several microseconds, likely around 300-500 cycles. I have not measured if and when
// it returns to 0 afterwards.
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
if (GBASIOMultiplayerIsIrq(sio->siocnt)) {