diff --git a/include/mgba/internal/gba/hardware.h b/include/mgba/internal/gba/hardware.h index 2ba1fbbcb..67dba530d 100644 --- a/include/mgba/internal/gba/hardware.h +++ b/include/mgba/internal/gba/hardware.h @@ -96,6 +96,40 @@ struct GBAGBPSIODriver { DECL_BITFIELD(GPIOPin, uint16_t); +DECL_BITFIELD(EReaderControl0, uint8_t); +DECL_BIT(EReaderControl0, Data, 0); +DECL_BIT(EReaderControl0, Clock, 1); +DECL_BIT(EReaderControl0, Direction, 2); +DECL_BIT(EReaderControl0, LedEnable, 3); +DECL_BIT(EReaderControl0, Scan, 4); +DECL_BIT(EReaderControl0, Phi, 5); +DECL_BIT(EReaderControl0, PowerEnable, 6); +DECL_BITFIELD(EReaderControl1, uint8_t); +DECL_BIT(EReaderControl1, Scanline, 1); +DECL_BIT(EReaderControl1, Unk1, 4); +DECL_BIT(EReaderControl1, Voltage, 5); + +enum EReaderStateMachine { + EREADER_SERIAL_INACTIVE = 0, + EREADER_SERIAL_STARTING, + EREADER_SERIAL_BIT_0, + EREADER_SERIAL_BIT_1, + EREADER_SERIAL_BIT_2, + EREADER_SERIAL_BIT_3, + EREADER_SERIAL_BIT_4, + EREADER_SERIAL_BIT_5, + EREADER_SERIAL_BIT_6, + EREADER_SERIAL_BIT_7, + EREADER_SERIAL_END_BIT, +}; + +enum EReaderCommand { + EREADER_COMMAND_IDLE = 0, // TODO: Verify on hardware + EREADER_COMMAND_WRITE_DATA = 1, + EREADER_COMMAND_SET_INDEX = 0x22, + EREADER_COMMAND_READ_DATA = 0x23, +}; + struct GBACartridgeHardware { struct GBA* p; uint32_t devices; @@ -125,10 +159,19 @@ struct GBACartridgeHardware { struct GBAGBPSIODriver gbpDriver; uint16_t eReaderData[44]; + uint8_t eReaderSerial[92]; uint16_t eReaderRegisterUnk; uint16_t eReaderRegisterReset; - uint16_t eReaderRegisterControl; + EReaderControl0 eReaderRegisterControl0; + EReaderControl1 eReaderRegisterControl1; uint16_t eReaderRegisterLed; + + // TODO: Serialize these + enum EReaderStateMachine eReaderState; + enum EReaderCommand eReaderCommand; + uint8_t eReaderActiveRegister; + uint8_t eReaderByte; + uint8_t eReaderDelay; }; void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase); diff --git a/src/gba/hardware.c b/src/gba/hardware.c index 95ae9918f..f0cd579e8 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -37,6 +37,8 @@ static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t addre static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _eReaderReset(struct GBACartridgeHardware* hw); +static void _eReaderWriteControl0(struct GBACartridgeHardware* hw, uint8_t value); +static void _eReaderWriteControl1(struct GBACartridgeHardware* hw, uint8_t value); static const int RTC_BYTES[8] = { 0, // Force reset @@ -622,7 +624,16 @@ void GBAHardwareEReaderWrite(struct GBACartridgeHardware* hw, uint32_t address, void GBAHardwareEReaderWriteFlash(struct GBACartridgeHardware* hw, uint32_t address, uint8_t value) { address &= 0xFFFF; - mLOG(GBA_HW, STUB, "Unimplemented e-Reader write to flash: %04X:%02X", address, value); + switch (address) { + case 0xFFB0: + _eReaderWriteControl0(hw, value); + break; + case 0xFFB1: + _eReaderWriteControl1(hw, value); + break; + default: + mLOG(GBA_HW, STUB, "Unimplemented e-Reader write to flash: %04X:%02X", address, value); + } } uint16_t GBAHardwareEReaderRead(struct GBACartridgeHardware* hw, uint32_t address) { @@ -644,14 +655,102 @@ uint16_t GBAHardwareEReaderRead(struct GBACartridgeHardware* hw, uint32_t addres uint8_t GBAHardwareEReaderReadFlash(struct GBACartridgeHardware* hw, uint32_t address) { address &= 0xFFFF; - mLOG(GBA_HW, STUB, "Unimplemented e-Reader read from flash: %04X", address); - return 0; + switch (address) { + case 0xFFB0: + return hw->eReaderRegisterControl0; + case 0xFFB1: + return hw->eReaderRegisterControl1; + default: + mLOG(GBA_HW, STUB, "Unimplemented e-Reader read from flash: %04X", address); + return 0; + } } void _eReaderReset(struct GBACartridgeHardware* hw) { memset(hw->eReaderData, 0, sizeof(hw->eReaderData)); hw->eReaderRegisterUnk = 0; hw->eReaderRegisterReset = 4; + hw->eReaderRegisterControl0 = 0; + hw->eReaderRegisterControl1 = 0x80; + hw->eReaderRegisterLed = 0; + hw->eReaderState = false; + hw->eReaderActiveRegister = 0; +} + +void _eReaderWriteControl0(struct GBACartridgeHardware* hw, uint8_t value) { + EReaderControl0 control = value & 0x7F; + EReaderControl0 oldControl = hw->eReaderRegisterControl0; + ++hw->eReaderDelay; + if (hw->eReaderDelay > 5) { + // Timed out + hw->eReaderState = EREADER_SERIAL_INACTIVE; + } + if (hw->eReaderState == EREADER_SERIAL_INACTIVE) { + if (EReaderControl0IsClock(oldControl) && EReaderControl0IsData(oldControl) && !EReaderControl0IsData(control)) { + hw->eReaderState = EREADER_SERIAL_STARTING; + hw->eReaderDelay = 0; + } + } else if (hw->eReaderState == EREADER_SERIAL_STARTING) { + if (EReaderControl0IsClock(oldControl) && !EReaderControl0IsData(oldControl) && !EReaderControl0IsClock(control)) { + hw->eReaderState = EREADER_SERIAL_BIT_0; + hw->eReaderCommand = EREADER_COMMAND_IDLE; + hw->eReaderDelay = 0; + } + } else if (EReaderControl0IsClock(oldControl) && !EReaderControl0IsClock(control)) { + mLOG(GBA_HW, DEBUG, "[e-Reader] Serial falling edge: %c %i", EReaderControl0IsDirection(control) ? '>' : '<', EReaderControl0GetData(control)); + // TODO: Improve direction control + if (EReaderControl0IsDirection(control)) { + hw->eReaderByte |= EReaderControl0GetData(control) << (7 - (hw->eReaderState - EREADER_SERIAL_BIT_0)); + ++hw->eReaderState; + if (hw->eReaderState == EREADER_SERIAL_END_BIT) { + mLOG(GBA_HW, DEBUG, "[e-Reader] Wrote serial byte: %02x", hw->eReaderByte); + switch (hw->eReaderCommand) { + case EREADER_COMMAND_IDLE: + hw->eReaderCommand = hw->eReaderByte; + break; + case EREADER_COMMAND_SET_INDEX: + hw->eReaderActiveRegister = hw->eReaderByte; + hw->eReaderCommand = EREADER_COMMAND_WRITE_DATA; + break; + case EREADER_COMMAND_WRITE_DATA: + switch (hw->eReaderActiveRegister & 0x7F) { + case 0: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + // Read-only + mLOG(GBA_HW, GAME_ERROR, "Writing to read-only e-Reader serial register: %02X", hw->eReaderActiveRegister); + break; + default: + if ((hw->eReaderActiveRegister & 0x7F) > 0x5A) { + mLOG(GBA_HW, GAME_ERROR, "Writing to non-existent e-Reader serial register: %02X", hw->eReaderActiveRegister); + break; + } + hw->eReaderSerial[hw->eReaderActiveRegister & 0x7F] = hw->eReaderByte; + break; + } + ++hw->eReaderActiveRegister; + } + hw->eReaderState = EREADER_SERIAL_BIT_0; + hw->eReaderByte = 0; + } + } else { + if (hw->eReaderCommand != EREADER_COMMAND_READ_DATA) { + // Clear the error bit + control = EReaderControl0ClearData(control); + } + } + hw->eReaderDelay = 0; + } + hw->eReaderRegisterControl0 = control; + mLOG(GBA_HW, STUB, "Unimplemented e-Reader Control0 write: %02X", value); +} + +void _eReaderWriteControl1(struct GBACartridgeHardware* hw, uint8_t value) { + EReaderControl1 control = (value & 0x32) | 0x80; + hw->eReaderRegisterControl1 = control; + mLOG(GBA_HW, STUB, "Unimplemented e-Reader Control1 write: %02X", value); } // == Serialization