From 3ae429fd91b06e551cd8b752ca642ddb9e385699 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:14:20 -0700 Subject: [PATCH] Implement GPIO internal write latch Regardless of direction, a write to GPIO data actually succeeds fully with all 4 bits. However, this does not directly touch the pin state. It instead places it into an internal write only latch. This latch asserts bits onto the pin state if direction allows for such, otherwise the other side (e.g. RTC) will be the one asserting bits (but this does not end up touching the internal write latch). The implementation here is likely not entirely accurate for direction changes from out to in (as that depends on each external device implementation), but it should be correct for in to out changes. --- include/mgba/internal/gba/cart/gpio.h | 1 + include/mgba/internal/gba/serialize.h | 3 ++- src/gba/cart/gpio.c | 19 ++++++++++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/mgba/internal/gba/cart/gpio.h b/include/mgba/internal/gba/cart/gpio.h index be3cd9173..9aebfa11b 100644 --- a/include/mgba/internal/gba/cart/gpio.h +++ b/include/mgba/internal/gba/cart/gpio.h @@ -68,6 +68,7 @@ struct GBACartridgeHardware { enum GPIODirection readWrite; uint16_t* gpioBase; + uint8_t writeLatch; uint16_t pinState; uint16_t direction; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 6e9a3124a..afa4569b0 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -376,7 +376,8 @@ struct GBASerializedState { } dma[4]; struct { - uint16_t pinState; + uint8_t pinState; + uint8_t writeLatch; uint16_t pinDirection; int32_t rtcBytesRemaining; int32_t rtcTransferStep; diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index 5798a636e..61b4e389c 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -49,6 +49,7 @@ void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) { void GBAHardwareReset(struct GBACartridgeHardware* hw) { hw->readWrite = GPIO_WRITE_ONLY; + hw->writeLatch = 0; hw->pinState = 0; hw->direction = 0; hw->lightCounter = 0; @@ -64,6 +65,7 @@ void GBAHardwareReset(struct GBACartridgeHardware* hw) { void GBAHardwareClear(struct GBACartridgeHardware* hw) { hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION); hw->readWrite = GPIO_WRITE_ONLY; + hw->writeLatch = 0; hw->pinState = 0; hw->direction = 0; } @@ -74,16 +76,22 @@ void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uin } switch (address) { case GPIO_REG_DATA: + hw->writeLatch = value & 0xF; if (!hw->p->vbaBugCompat) { hw->pinState &= ~hw->direction; - hw->pinState |= value & hw->direction; + hw->pinState |= hw->writeLatch & hw->direction; } else { - hw->pinState = value & 0xF; + hw->pinState = hw->writeLatch; } _readPins(hw); break; case GPIO_REG_DIRECTION: hw->direction = value & 0xF; + if (!hw->p->vbaBugCompat) { + hw->pinState &= ~hw->direction; + hw->pinState |= hw->writeLatch & hw->direction; + _readPins(hw); + } break; case GPIO_REG_CONTROL: hw->readWrite = value & 0x1; @@ -475,7 +483,8 @@ uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) { void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) { GBASerializedHWFlags1 flags1 = 0; flags1 = GBASerializedHWFlags1SetReadWrite(flags1, hw->readWrite); - STORE_16(hw->pinState, 0, &state->hw.pinState); + state->hw.writeLatch = hw->writeLatch; + state->hw.pinState = hw->pinState; STORE_16(hw->direction, 0, &state->hw.pinDirection); state->hw.devices = hw->devices; @@ -512,9 +521,9 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer GBASerializedHWFlags1 flags1; LOAD_16(flags1, 0, &state->hw.flags1); hw->readWrite = GBASerializedHWFlags1GetReadWrite(flags1); - LOAD_16(hw->pinState, 0, &state->hw.pinState); + hw->writeLatch = state->hw.writeLatch; + hw->pinState = state->hw.pinState; LOAD_16(hw->direction, 0, &state->hw.pinDirection); - hw->pinState &= 0xF; hw->direction &= 0xF; hw->devices = state->hw.devices;