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.
This commit is contained in:
CasualPokePlayer 2025-04-05 17:14:20 -07:00 committed by Vicki Pfau
parent e95b81f1f7
commit 3ae429fd91
3 changed files with 17 additions and 6 deletions

View File

@ -68,6 +68,7 @@ struct GBACartridgeHardware {
enum GPIODirection readWrite;
uint16_t* gpioBase;
uint8_t writeLatch;
uint16_t pinState;
uint16_t direction;

View File

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

View File

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