diff options
author | GriffinR <griffin.richards@comcast.net> | 2019-09-26 15:02:38 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-26 15:02:38 -0400 |
commit | 1016b239186097361ea20f2f91adf3932026f40c (patch) | |
tree | 840a0f28046d3c66fcaf1c2bb8ab3228386a3a8a /berry_fix/payload/src | |
parent | bb236b490d99fef02ed172e7ca86f6c70ef09527 (diff) | |
parent | 208e1c968959c781562f0b94c03368385ce7012c (diff) |
Merge branch 'master' into rename-tablecmds
Diffstat (limited to 'berry_fix/payload/src')
-rw-r--r-- | berry_fix/payload/src/agb_flash.c | 296 | ||||
-rw-r--r-- | berry_fix/payload/src/agb_flash_1m.c | 86 | ||||
-rw-r--r-- | berry_fix/payload/src/agb_flash_le.c | 31 | ||||
-rw-r--r-- | berry_fix/payload/src/agb_flash_mx.c | 193 | ||||
-rw-r--r-- | berry_fix/payload/src/flash.c | 752 | ||||
-rw-r--r-- | berry_fix/payload/src/main.c | 289 | ||||
-rw-r--r-- | berry_fix/payload/src/rtc.c | 346 | ||||
-rw-r--r-- | berry_fix/payload/src/siirtc.c | 432 |
8 files changed, 2425 insertions, 0 deletions
diff --git a/berry_fix/payload/src/agb_flash.c b/berry_fix/payload/src/agb_flash.c new file mode 100644 index 000000000..2c2c96e6e --- /dev/null +++ b/berry_fix/payload/src/agb_flash.c @@ -0,0 +1,296 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +static u8 sTimerNum; +static u16 sTimerCount; +static vu16 *sTimerReg; +static u16 sSavedIme; + +u8 gFlashTimeoutFlag; +u8 (*PollFlashStatus)(u8 *); +const struct FlashType *gFlash; +u16 gFlashNumRemainingBytes; +const u16 *gFlashMaxTime; + +u16 (*ProgramFlashByte)(u16, u32, u8); +u16 (*ProgramFlashSector)(u16, void *); +u16 (*EraseFlashChip)(void); +u16 (*EraseFlashSector)(u16); +u16 (*WaitForFlashWrite)(u8, u8 *, u8); + +void SetReadFlash1(u16 *dest); + +void SwitchFlashBank(u8 bankNum) +{ + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0xB0); + FLASH_WRITE(0x0000, bankNum); +} + +#define DELAY() \ +do { \ + vu16 i; \ + for (i = 20000; i != 0; i--) \ + ; \ +} while (0) + +u16 ReadFlashId(void) +{ + u16 flashId; + u16 readFlash1Buffer[0x20]; + u8 (*readFlash1)(u8 *); + + SetReadFlash1(readFlash1Buffer); + readFlash1 = (u8 (*)(u8 *))((s32)readFlash1Buffer + 1); + + // Enter ID mode. + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0x90); + DELAY(); + + flashId = readFlash1(FLASH_BASE + 1) << 8; + flashId |= readFlash1(FLASH_BASE); + + // Leave ID mode. + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0xF0); + FLASH_WRITE(0x5555, 0xF0); + DELAY(); + + return flashId; +} + +void FlashTimerIntr(void) +{ + if (sTimerCount != 0 && --sTimerCount == 0) + gFlashTimeoutFlag = 1; +} + +u16 SetFlashTimerIntr(u8 timerNum, void (**intrFunc)(void)) +{ + if (timerNum >= 4) + return 1; + + sTimerNum = timerNum; + sTimerReg = ®_TMCNT(sTimerNum); + *intrFunc = FlashTimerIntr; + return 0; +} + +void StartFlashTimer(u8 phase) +{ + const u16 *maxTime = &gFlashMaxTime[phase * 3]; + sSavedIme = REG_IME; + REG_IME = 0; + sTimerReg[1] = 0; + REG_IE |= (INTR_FLAG_TIMER0 << sTimerNum); + gFlashTimeoutFlag = 0; + sTimerCount = *maxTime++; + *sTimerReg++ = *maxTime++; + *sTimerReg-- = *maxTime++; + REG_IF = (INTR_FLAG_TIMER0 << sTimerNum); + REG_IME = 1; +} + +void StopFlashTimer(void) +{ + REG_IME = 0; + *sTimerReg++ = 0; + *sTimerReg-- = 0; + REG_IE &= ~(INTR_FLAG_TIMER0 << sTimerNum); + REG_IME = sSavedIme; +} + +u8 ReadFlash1(u8 *addr) +{ + return *addr; +} + +void SetReadFlash1(u16 *dest) +{ + u16 *src; + u16 i; + + PollFlashStatus = (u8 (*)(u8 *))((s32)dest + 1); + + src = (u16 *)ReadFlash1; + src = (u16 *)((s32)src ^ 1); + + i = ((s32)SetReadFlash1 - (s32)ReadFlash1) >> 1; + + while (i != 0) + { + *dest++ = *src++; + i--; + } +} + +void ReadFlash_Core(u8 *src, u8 *dest, u32 size) +{ + while (size-- != 0) + { + *dest++ = *src++; + } +} + +void ReadFlash(u16 sectorNum, u32 offset, void *dest, u32 size) +{ + u8 *src; + u16 i; + u16 readFlash_Core_Buffer[0x40]; + u16 *funcSrc; + u16 *funcDest; + void (*readFlash_Core)(u8 *, u8 *, u32); + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + + if (gFlash->romSize == FLASH_ROM_SIZE_1M) + { + SwitchFlashBank(sectorNum / SECTORS_PER_BANK); + sectorNum %= SECTORS_PER_BANK; + } + + funcSrc = (u16 *)ReadFlash_Core; + funcSrc = (u16 *)((s32)funcSrc ^ 1); + funcDest = readFlash_Core_Buffer; + + i = ((s32)ReadFlash - (s32)ReadFlash_Core) >> 1; + + while (i != 0) + { + *funcDest++ = *funcSrc++; + i--; + } + + readFlash_Core = (void (*)(u8 *, u8 *, u32))((s32)readFlash_Core_Buffer + 1); + + src = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset; + + readFlash_Core(src, dest, size); +} + +u32 VerifyFlashSector_Core(u8 *src, u8 *tgt, u32 size) +{ + while (size-- != 0) + { + if (*tgt++ != *src++) + return (u32)(tgt - 1); + } + + return 0; +} + +u32 VerifyFlashSector(u16 sectorNum, u8 *src) +{ + u16 i; + u16 verifyFlashSector_Core_Buffer[0x80]; + u16 *funcSrc; + u16 *funcDest; + u8 *tgt; + u16 size; + u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32); + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + + if (gFlash->romSize == FLASH_ROM_SIZE_1M) + { + SwitchFlashBank(sectorNum / SECTORS_PER_BANK); + sectorNum %= SECTORS_PER_BANK; + } + + funcSrc = (u16 *)VerifyFlashSector_Core; + funcSrc = (u16 *)((s32)funcSrc ^ 1); + funcDest = verifyFlashSector_Core_Buffer; + + i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1; + + while (i != 0) + { + *funcDest++ = *funcSrc++; + i--; + } + + verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1); + + tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift); + size = gFlash->sector.size; + + return verifyFlashSector_Core(src, tgt, size); +} + +u32 VerifyFlashSectorNBytes(u16 sectorNum, u8 *src, u32 n) +{ + u16 i; + u16 verifyFlashSector_Core_Buffer[0x80]; + u16 *funcSrc; + u16 *funcDest; + u8 *tgt; + u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32); + + if (gFlash->romSize == FLASH_ROM_SIZE_1M) + { + SwitchFlashBank(sectorNum / SECTORS_PER_BANK); + sectorNum %= SECTORS_PER_BANK; + } + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + + funcSrc = (u16 *)VerifyFlashSector_Core; + funcSrc = (u16 *)((s32)funcSrc ^ 1); + funcDest = verifyFlashSector_Core_Buffer; + + i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1; + + while (i != 0) + { + *funcDest++ = *funcSrc++; + i--; + } + + verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1); + + tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift); + + return verifyFlashSector_Core(src, tgt, n); +} + +u32 ProgramFlashSectorAndVerify(u16 sectorNum, u8 *src) +{ + u8 i; + u32 result; + + for (i = 0; i < 3; i++) + { + result = ProgramFlashSector(sectorNum, src); + if (result != 0) + continue; + + result = VerifyFlashSector(sectorNum, src); + if (result == 0) + break; + } + + return result; +} + +u32 ProgramFlashSectorAndVerifyNBytes(u16 sectorNum, void *src, u32 n) +{ + u8 i; + u32 result; + + for (i = 0; i < 3; i++) + { + result = ProgramFlashSector(sectorNum, src); + if (result != 0) + continue; + + result = VerifyFlashSectorNBytes(sectorNum, src, n); + if (result == 0) + break; + } + + return result; +} diff --git a/berry_fix/payload/src/agb_flash_1m.c b/berry_fix/payload/src/agb_flash_1m.c new file mode 100644 index 000000000..7f8bdeb5f --- /dev/null +++ b/berry_fix/payload/src/agb_flash_1m.c @@ -0,0 +1,86 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +static const char AgbLibFlashVersion[] = "FLASH1M_V103"; + +const struct FlashSetupInfo * const sSetupInfos[] = +{ + &MX29L010, + &LE26FV10N1TS, + &DefaultFlash +}; + +u32 IdentifyFlash(void) +{ + u16 result; + u16 flashId; + const struct FlashSetupInfo * const *setupInfo; + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + + flashId = ReadFlashId(); + + setupInfo = sSetupInfos; + result = 1; + + for (;;) + { + if ((*setupInfo)->type.ids.separate.makerId == 0) + break; + + if (flashId == (*setupInfo)->type.ids.joined) + { + result = 0; + break; + } + + setupInfo++; + } + + ProgramFlashByte = (*setupInfo)->programFlashByte; + ProgramFlashSector = (*setupInfo)->programFlashSector; + EraseFlashChip = (*setupInfo)->eraseFlashChip; + EraseFlashSector = (*setupInfo)->eraseFlashSector; + WaitForFlashWrite = (*setupInfo)->WaitForFlashWrite; + gFlashMaxTime = (*setupInfo)->maxTime; + gFlash = &(*setupInfo)->type; + + return result; +} + +u16 WaitForFlashWrite_Common(u8 phase, u8 *addr, u8 lastData) +{ + u16 result = 0; + u8 status; + + StartFlashTimer(phase); + + while ((status = PollFlashStatus(addr)) != lastData) + { + if (status & 0x20) + { + // The write operation exceeded the flash chip's time limit. + + if (PollFlashStatus(addr) == lastData) + break; + + FLASH_WRITE(0x5555, 0xF0); + result = phase | 0xA000u; + break; + } + + if (gFlashTimeoutFlag) + { + if (PollFlashStatus(addr) == lastData) + break; + + FLASH_WRITE(0x5555, 0xF0); + result = phase | 0xC000u; + break; + } + } + + StopFlashTimer(); + + return result; +} diff --git a/berry_fix/payload/src/agb_flash_le.c b/berry_fix/payload/src/agb_flash_le.c new file mode 100644 index 000000000..39d956e27 --- /dev/null +++ b/berry_fix/payload/src/agb_flash_le.c @@ -0,0 +1,31 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +const u16 leMaxTime[] = +{ + 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +}; + +const struct FlashSetupInfo LE26FV10N1TS = +{ + ProgramFlashByte_MX, + ProgramFlashSector_MX, + EraseFlashChip_MX, + EraseFlashSector_MX, + WaitForFlashWrite_Common, + leMaxTime, + { + 131072, // ROM size + { + 4096, // sector size + 12, // bit shift to multiply by sector size (4096 == 1 << 12) + 32, // number of sectors + 0 // appears to be unused + }, + { 3, 1 }, // wait state setup data + { { 0x62, 0x13 } } // ID + } +}; diff --git a/berry_fix/payload/src/agb_flash_mx.c b/berry_fix/payload/src/agb_flash_mx.c new file mode 100644 index 000000000..68eb00cd8 --- /dev/null +++ b/berry_fix/payload/src/agb_flash_mx.c @@ -0,0 +1,193 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" + +const u16 mxMaxTime[] = +{ + 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, + 2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK, +}; + +const struct FlashSetupInfo MX29L010 = +{ + ProgramFlashByte_MX, + ProgramFlashSector_MX, + EraseFlashChip_MX, + EraseFlashSector_MX, + WaitForFlashWrite_Common, + mxMaxTime, + { + 131072, // ROM size + { + 4096, // sector size + 12, // bit shift to multiply by sector size (4096 == 1 << 12) + 32, // number of sectors + 0 // appears to be unused + }, + { 3, 1 }, // wait state setup data + { { 0xC2, 0x09 } } // ID + } +}; + +const struct FlashSetupInfo DefaultFlash = +{ + ProgramFlashByte_MX, + ProgramFlashSector_MX, + EraseFlashChip_MX, + EraseFlashSector_MX, + WaitForFlashWrite_Common, + mxMaxTime, + { + 131072, // ROM size + { + 4096, // sector size + 12, // bit shift to multiply by sector size (4096 == 1 << 12) + 32, // number of sectors + 0 // appears to be unused + }, + { 3, 1 }, // wait state setup data + { { 0x00, 0x00 } } // ID of 0 + } +}; + +u16 EraseFlashChip_MX(void) +{ + u16 result; + u16 readFlash1Buffer[0x20]; + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0x80); + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0x10); + + SetReadFlash1(readFlash1Buffer); + + result = WaitForFlashWrite(3, FLASH_BASE, 0xFF); + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + + return result; +} + +u16 EraseFlashSector_MX(u16 sectorNum) +{ + u16 numTries; + u16 result; + u8 *addr; + u16 readFlash1Buffer[0x20]; + + if (sectorNum >= gFlash->sector.count) + return 0x80FF; + + SwitchFlashBank(sectorNum / SECTORS_PER_BANK); + sectorNum %= SECTORS_PER_BANK; + + numTries = 0; + +try_erase: + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + + addr = FLASH_BASE + (sectorNum << gFlash->sector.shift); + + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0x80); + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + *addr = 0x30; + + SetReadFlash1(readFlash1Buffer); + + result = WaitForFlashWrite(2, addr, 0xFF); + + if (!(result & 0xA000) || numTries > 3) + goto done; + + numTries++; + + goto try_erase; + +done: + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8; + + return result; +} + +u16 ProgramFlashByte_MX(u16 sectorNum, u32 offset, u8 data) +{ + u8 *addr; + u16 readFlash1Buffer[0x20]; + + if (offset >= gFlash->sector.size) + return 0x8000; + + SwitchFlashBank(sectorNum / SECTORS_PER_BANK); + sectorNum %= SECTORS_PER_BANK; + + addr = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset; + + SetReadFlash1(readFlash1Buffer); + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0xA0); + *addr = data; + + return WaitForFlashWrite(1, addr, data); +} + +static u16 ProgramByte(u8 *src, u8 *dest) +{ + FLASH_WRITE(0x5555, 0xAA); + FLASH_WRITE(0x2AAA, 0x55); + FLASH_WRITE(0x5555, 0xA0); + *dest = *src; + + return WaitForFlashWrite(1, dest, *src); +} + +u16 ProgramFlashSector_MX(u16 sectorNum, void *src) +{ + u16 result; + u8 *dest; + u16 readFlash1Buffer[0x20]; + + if (sectorNum >= gFlash->sector.count) + return 0x80FF; + + result = EraseFlashSector_MX(sectorNum); + + if (result != 0) + return result; + + SwitchFlashBank(sectorNum / SECTORS_PER_BANK); + sectorNum %= SECTORS_PER_BANK; + + SetReadFlash1(readFlash1Buffer); + + REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0]; + + gFlashNumRemainingBytes = gFlash->sector.size; + dest = FLASH_BASE + (sectorNum << gFlash->sector.shift); + + while (gFlashNumRemainingBytes > 0) + { + result = ProgramByte(src, dest); + + if (result != 0) + break; + + gFlashNumRemainingBytes--; + src++; + dest++; + } + + return result; +} diff --git a/berry_fix/payload/src/flash.c b/berry_fix/payload/src/flash.c new file mode 100644 index 000000000..1f09d0b8f --- /dev/null +++ b/berry_fix/payload/src/flash.c @@ -0,0 +1,752 @@ +#include "gba/gba.h" +#include "gba/flash_internal.h" +#include "constants/vars.h" +#include "global.h" +#include "main.h" +#include "flash.h" +#include "rtc.h" + +struct SaveBlockChunk +{ + u8 * data; + u16 size; +}; + +u8 WriteSaveBlockChunks(u16 a0, const struct SaveBlockChunk * a1); +u8 WriteSingleChunk(u16 a0, const struct SaveBlockChunk * a1); +u8 TryWriteSector(u8, u8 *); +u8 EraseCurrentChunk(u16 a0, const struct SaveBlockChunk * a1); +u8 TryReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1); +u8 ReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1); +u8 GetSaveValidStatus(const struct SaveBlockChunk * a1); +u32 DoReadFlashWholeSection(u8 a0, struct SaveSector * a1); +u16 CalculateChecksum(const void *, u16); + +u16 gFirstSaveSector; +u32 gPrevSaveCounter; +u16 gLastKnownGoodSector; +u32 gDamagedSaveSectors; +u32 gSaveCounter; +struct SaveSector * gFastSaveSection; +u16 gCurSaveChunk; +bool32 gFlashIdentIsValid; + +EWRAM_DATA struct SaveBlock2 gSaveBlock2 = {}; +EWRAM_DATA struct SaveBlock1 gSaveBlock1 = {}; +EWRAM_DATA struct PokemonStorage gPokemonStorage = {}; + +// Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer +#define SECTOR_DATA_SIZE 3968 +#define SECTOR_FOOTER_SIZE 128 + +#define SAVEBLOCK_CHUNK(structure, chunkNum) \ +{ \ + (u8 *)&structure + chunkNum * SECTOR_DATA_SIZE, \ + min(sizeof(structure) - chunkNum * SECTOR_DATA_SIZE, SECTOR_DATA_SIZE) \ +} \ + +static const struct SaveBlockChunk sSaveBlockChunks[] = +{ + SAVEBLOCK_CHUNK(gSaveBlock2, 0), + + SAVEBLOCK_CHUNK(gSaveBlock1, 0), + SAVEBLOCK_CHUNK(gSaveBlock1, 1), + SAVEBLOCK_CHUNK(gSaveBlock1, 2), + SAVEBLOCK_CHUNK(gSaveBlock1, 3), + + SAVEBLOCK_CHUNK(gPokemonStorage, 0), + SAVEBLOCK_CHUNK(gPokemonStorage, 1), + SAVEBLOCK_CHUNK(gPokemonStorage, 2), + SAVEBLOCK_CHUNK(gPokemonStorage, 3), + SAVEBLOCK_CHUNK(gPokemonStorage, 4), + SAVEBLOCK_CHUNK(gPokemonStorage, 5), + SAVEBLOCK_CHUNK(gPokemonStorage, 6), + SAVEBLOCK_CHUNK(gPokemonStorage, 7), + SAVEBLOCK_CHUNK(gPokemonStorage, 8), +}; + +const u16 gInfoMessagesPal[] = INCBIN_U16("graphics/msg_box.gbapal"); +const u8 gInfoMessagesTilemap[] = INCBIN_U8("graphics/msg_box.tilemap.lz"); +const u8 gInfoMessagesGfx[] = INCBIN_U8("graphics/msg_box.4bpp.lz"); + +bool32 flash_maincb_ident_is_valid(void) +{ + gFlashIdentIsValid = TRUE; + if (!IdentifyFlash()) + { + SetFlashTimerIntr(0, &((IntrFunc *)gIntrFuncPointers)[9]); + return TRUE; + } + gFlashIdentIsValid = FALSE; + return FALSE; +} + +void Call_ReadFlash(u16 sectorNum, ptrdiff_t offset, void * dest, size_t size) +{ + ReadFlash(sectorNum, offset, dest, size); +} + +u8 Call_WriteSaveBlockChunks(u16 a0, const struct SaveBlockChunk * a1) +{ + return WriteSaveBlockChunks(a0, a1); +} + +u8 Call_TryReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1) +{ + return TryReadAllSaveSectorsCurrentSlot(a0, a1); +} + +u32 * GetDamagedSaveSectorsPtr(void) +{ + return &gDamagedSaveSectors; +} + +s32 flash_write_save_block_chunks(u8 a0) +{ + u8 i; + + switch (a0) + { + case 0: + default: + Call_WriteSaveBlockChunks(0xFFFF, sSaveBlockChunks); + break; + case 1: + for (i = 0; i < 5; i++) + { + Call_WriteSaveBlockChunks(i, sSaveBlockChunks); + } + break; + case 2: + Call_WriteSaveBlockChunks(0, sSaveBlockChunks); + break; + } + + return 0; +} + +u8 flash_write_save_block_chunks_check_damage(u8 a0) +{ + flash_write_save_block_chunks(a0); + if (*GetDamagedSaveSectorsPtr() == 0) + return 1; + return 0xFF; +} + +u8 flash_maincb_read_save(u32 unused) +{ + return Call_TryReadAllSaveSectorsCurrentSlot(0xFFFF, sSaveBlockChunks); +} + +void msg_load_gfx(void) +{ + REG_DISPCNT = 0; + REG_BG0HOFS = 0; + REG_BG0VOFS = 0; + REG_BLDCNT = 0; + LZ77UnCompVram(gInfoMessagesGfx, (void *)BG_VRAM); + LZ77UnCompVram(gInfoMessagesTilemap, (void *)BG_SCREEN_ADDR(28)); + CpuCopy16(gInfoMessagesPal, (void *)BG_PLTT, 0x200); + REG_BG0CNT = BGCNT_SCREENBASE(28) | BGCNT_TXT512x512; + REG_DISPCNT = DISPCNT_BG0_ON; +} + +void msg_display(enum MsgBoxUpdateMessage a0) +{ + switch (a0) + { + case MSGBOX_WILL_NOW_UPDATE: + REG_BG0HOFS = 0; + REG_BG0VOFS = 0; + break; + case MSGBOX_HAS_BEEN_UPDATED: + REG_BG0HOFS = 0x100; + REG_BG0VOFS = 0; + break; + case MSGBOX_UNABLE_TO_UPDATE: + REG_BG0HOFS = 0x100; + REG_BG0VOFS = 0xB0; + break; + case MSGBOX_NO_NEED_TO_UPDATE: + REG_BG0HOFS = 0; + REG_BG0VOFS = 0xB0; + break; + case MSGBOX_UPDATING: + REG_BG0HOFS = 0; + REG_BG0VOFS = 0x160; + break; + } +} + +void Save_EraseAllData(void) +{ + u16 i; + for (i = 0; i < 32; i++) + EraseFlashSector(i); +} + +void Save_ResetSaveCounters(void) +{ + gSaveCounter = 0; + gFirstSaveSector = 0; + gDamagedSaveSectors = 0; +} + +bool32 SetSectorDamagedStatus(u8 op, u8 sectorNum) +{ + bool32 retVal = FALSE; + + switch (op) + { + case SECTOR_DAMAGED: + gDamagedSaveSectors |= (1 << sectorNum); + break; + case SECTOR_OK: + gDamagedSaveSectors &= ~(1 << sectorNum); + break; + case SECTOR_CHECK: // unused + if (gDamagedSaveSectors & (1 << sectorNum)) + retVal = TRUE; + break; + } + + return retVal; +} + +u8 WriteSaveBlockChunks(u16 chunkId, const struct SaveBlockChunk *chunks) +{ + u32 retVal; + u16 i; + + gFastSaveSection = eSaveSection; + + if (chunkId != 0xFFFF) // write single chunk + { + retVal = WriteSingleChunk(chunkId, chunks); + } + else // write all chunks + { + gLastKnownGoodSector = gFirstSaveSector; + gPrevSaveCounter = gSaveCounter; + gFirstSaveSector++; + gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT; + gSaveCounter++; + retVal = SAVE_STATUS_OK; + + for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++) + WriteSingleChunk(i, chunks); + + // Check for any bad sectors + if (gDamagedSaveSectors != 0) // skip the damaged sector. + { + retVal = SAVE_STATUS_ERROR; + gFirstSaveSector = gLastKnownGoodSector; + gSaveCounter = gPrevSaveCounter; + } + } + + return retVal; +} + +u8 WriteSingleChunk(u16 chunkId, const struct SaveBlockChunk * chunks) +{ + u16 i; + u16 sectorNum; + u8 *chunkData; + u16 chunkSize; + + // select sector number + sectorNum = chunkId + gFirstSaveSector; + sectorNum %= NUM_SECTORS_PER_SAVE_SLOT; + // select save slot + sectorNum += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2); + + chunkData = chunks[chunkId].data; + chunkSize = chunks[chunkId].size; + + // clear save section. + for (i = 0; i < sizeof(struct SaveSector); i++) + ((u8 *)gFastSaveSection)[i] = 0; + + gFastSaveSection->id = chunkId; + gFastSaveSection->signature = FILE_SIGNATURE; + gFastSaveSection->counter = gSaveCounter; + for (i = 0; i < chunkSize; i++) + gFastSaveSection->data[i] = chunkData[i]; + gFastSaveSection->checksum = CalculateChecksum(chunkData, chunkSize); + + return TryWriteSector(sectorNum, gFastSaveSection->data); +} + +u8 HandleWriteSectorNBytes(u8 sectorNum, u8 *data, u16 size) +{ + u16 i; + struct SaveSector *section = eSaveSection; + + for (i = 0; i < sizeof(struct SaveSector); i++) + ((char *)section)[i] = 0; + + section->signature = FILE_SIGNATURE; + for (i = 0; i < size; i++) + section->data[i] = data[i]; + section->id = CalculateChecksum(data, size); // though this appears to be incorrect, it might be some sector checksum instead of a whole save checksum and only appears to be relevent to HOF data, if used. + + return TryWriteSector(sectorNum, section->data); +} + +u8 TryWriteSector(u8 sectorNum, u8 *data) +{ + if (ProgramFlashSectorAndVerify(sectorNum, data) != 0) // is damaged? + { + SetSectorDamagedStatus(SECTOR_DAMAGED, sectorNum); // set damaged sector bits. + return SAVE_STATUS_ERROR; + } + else + { + SetSectorDamagedStatus(SECTOR_OK, sectorNum); // unset damaged sector bits. it's safe now. + return SAVE_STATUS_OK; + } +} + +u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *chunk) // chunk is unused +{ + gFastSaveSection = eSaveSection; + gLastKnownGoodSector = gFirstSaveSector; + gPrevSaveCounter = gSaveCounter; + gFirstSaveSector++; + gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT; + gSaveCounter++; + gCurSaveChunk = 0; + gDamagedSaveSectors = 0; + return 0; +} + +u32 RestoreSaveBackupVars(const struct SaveBlockChunk *chunk) +{ + gFastSaveSection = eSaveSection; + gLastKnownGoodSector = gFirstSaveSector; + gPrevSaveCounter = gSaveCounter; + gCurSaveChunk = 0; + gDamagedSaveSectors = 0; + return 0; +} + +u8 WriteSingleChunkAndIncrement(u16 a1, const struct SaveBlockChunk * chunk) +{ + u8 retVal; + + if (gCurSaveChunk < a1 - 1) + { + retVal = SAVE_STATUS_OK; + WriteSingleChunk(gCurSaveChunk, chunk); + gCurSaveChunk++; + if (gDamagedSaveSectors) + { + retVal = SAVE_STATUS_ERROR; + gFirstSaveSector = gLastKnownGoodSector; + gSaveCounter = gPrevSaveCounter; + } + } + else + { + retVal = SAVE_STATUS_ERROR; + } + + return retVal; +} + +u8 ErasePreviousChunk(u16 a1, const struct SaveBlockChunk *chunk) +{ + u8 retVal = SAVE_STATUS_OK; + + EraseCurrentChunk(a1 - 1, chunk); + + if (gDamagedSaveSectors) + { + retVal = SAVE_STATUS_ERROR; + gFirstSaveSector = gLastKnownGoodSector; + gSaveCounter = gPrevSaveCounter; + } + return retVal; +} + +u8 EraseCurrentChunk(u16 chunkId, const struct SaveBlockChunk *chunks) +{ + u16 i; + u16 sector; + u8 *data; + u16 size; + u8 status; + + // select sector number + sector = chunkId + gFirstSaveSector; + sector %= NUM_SECTORS_PER_SAVE_SLOT; + // select save slot + sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2); + + data = chunks[chunkId].data; + size = chunks[chunkId].size; + + // clear temp save section. + for (i = 0; i < sizeof(struct SaveSector); i++) + ((char *)gFastSaveSection)[i] = 0; + + gFastSaveSection->id = chunkId; + gFastSaveSection->signature = FILE_SIGNATURE; + gFastSaveSection->counter = gSaveCounter; + + // set temp section's data. + for (i = 0; i < size; i++) + gFastSaveSection->data[i] = data[i]; + + // calculate checksum. + gFastSaveSection->checksum = CalculateChecksum(data, size); + + EraseFlashSector(sector); + + status = SAVE_STATUS_OK; + + for (i = 0; i < sizeof(struct UnkSaveSection); i++) + { + if (ProgramFlashByte(sector, i, gFastSaveSection->data[i])) + { + status = SAVE_STATUS_ERROR; + break; + } + } + + if (status == SAVE_STATUS_ERROR) + { + SetSectorDamagedStatus(SECTOR_DAMAGED, sector); + return SAVE_STATUS_ERROR; + } + else + { + status = SAVE_STATUS_OK; + + for (i = 0; i < 7; i++) + { + if (ProgramFlashByte(sector, 0xFF9 + i, ((u8 *)gFastSaveSection)[0xFF9 + i])) + { + status = SAVE_STATUS_ERROR; + break; + } + } + + if (status == SAVE_STATUS_ERROR) + { + SetSectorDamagedStatus(SECTOR_DAMAGED, sector); + return SAVE_STATUS_ERROR; + } + else + { + SetSectorDamagedStatus(SECTOR_OK, sector); + return SAVE_STATUS_OK; + } + } +} + +u8 WriteSomeFlashByteToPrevSector(u16 a1, const struct SaveBlockChunk *chunk) +{ + u16 sector; + + // select sector number + sector = a1 + gFirstSaveSector - 1; + sector %= NUM_SECTORS_PER_SAVE_SLOT; + // select save slot + sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2); + + if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), ((u8 *)gFastSaveSection)[sizeof(struct UnkSaveSection)])) + { + // sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter. + SetSectorDamagedStatus(SECTOR_DAMAGED, sector); + gFirstSaveSector = gLastKnownGoodSector; + gSaveCounter = gPrevSaveCounter; + return SAVE_STATUS_ERROR; + } + else + { + SetSectorDamagedStatus(SECTOR_OK, sector); + return SAVE_STATUS_OK; + } +} + +u8 WriteSomeFlashByte0x25ToPrevSector(u16 a1, const struct SaveBlockChunk *chunk) +{ + u16 sector; + + sector = a1 + gFirstSaveSector - 1; + sector %= NUM_SECTORS_PER_SAVE_SLOT; + sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2); + + if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), 0x25)) + { + // sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter. + SetSectorDamagedStatus(SECTOR_DAMAGED, sector); + gFirstSaveSector = gLastKnownGoodSector; + gSaveCounter = gPrevSaveCounter; + return SAVE_STATUS_ERROR; + } + else + { + SetSectorDamagedStatus(SECTOR_OK, sector); + return SAVE_STATUS_OK; + } +} + +u8 TryReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunk) +{ + u8 retVal; + gFastSaveSection = eSaveSection; + if (a1 != 0xFFFF) + { + retVal = SAVE_STATUS_ERROR; + } + else + { + retVal = GetSaveValidStatus(chunk); + ReadAllSaveSectorsCurrentSlot(0xFFFF, chunk); + } + + return retVal; +} + +u8 ReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunks) +{ + u16 i; + u16 checksum; + u16 sector = NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2); + u16 id; + + for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++) + { + DoReadFlashWholeSection(i + sector, gFastSaveSection); + id = gFastSaveSection->id; + if (id == 0) + gFirstSaveSector = i; + checksum = CalculateChecksum(gFastSaveSection->data, chunks[id].size); + if (gFastSaveSection->signature == FILE_SIGNATURE + && gFastSaveSection->checksum == checksum) + { + u16 j; + for (j = 0; j < chunks[id].size; j++) + chunks[id].data[j] = gFastSaveSection->data[j]; + } + } + + return 1; +} + +u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks) +{ + u16 sector; + bool8 signatureValid; + u16 checksum; + u32 slot1saveCounter = 0; + u32 slot2saveCounter = 0; + u8 slot1Status; + u8 slot2Status; + u32 validSectors; + const u32 ALL_SECTORS = (1 << NUM_SECTORS_PER_SAVE_SLOT) - 1; // bitmask of all saveblock sectors + + // check save slot 1. + validSectors = 0; + signatureValid = FALSE; + for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++) + { + DoReadFlashWholeSection(sector, gFastSaveSection); + if (gFastSaveSection->signature == FILE_SIGNATURE) + { + signatureValid = TRUE; + checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size); + if (gFastSaveSection->checksum == checksum) + { + slot1saveCounter = gFastSaveSection->counter; + validSectors |= 1 << gFastSaveSection->id; + } + } + } + + if (signatureValid) + { + if (validSectors == ALL_SECTORS) + slot1Status = SAVE_STATUS_OK; + else + slot1Status = SAVE_STATUS_ERROR; + } + else + { + slot1Status = SAVE_STATUS_EMPTY; + } + + // check save slot 2. + validSectors = 0; + signatureValid = FALSE; + for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++) + { + DoReadFlashWholeSection(NUM_SECTORS_PER_SAVE_SLOT + sector, gFastSaveSection); + if (gFastSaveSection->signature == FILE_SIGNATURE) + { + signatureValid = TRUE; + checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size); + if (gFastSaveSection->checksum == checksum) + { + slot2saveCounter = gFastSaveSection->counter; + validSectors |= 1 << gFastSaveSection->id; + } + } + } + + if (signatureValid) + { + if (validSectors == ALL_SECTORS) + slot2Status = SAVE_STATUS_OK; + else + slot2Status = SAVE_STATUS_ERROR; + } + else + { + slot2Status = SAVE_STATUS_EMPTY; + } + + if (slot1Status == SAVE_STATUS_OK && slot2Status == SAVE_STATUS_OK) + { + // Choose counter of the most recent save file + if ((slot1saveCounter == -1 && slot2saveCounter == 0) || (slot1saveCounter == 0 && slot2saveCounter == -1)) + { + if ((unsigned)(slot1saveCounter + 1) < (unsigned)(slot2saveCounter + 1)) + gSaveCounter = slot2saveCounter; + else + gSaveCounter = slot1saveCounter; + } + else + { + if (slot1saveCounter < slot2saveCounter) + gSaveCounter = slot2saveCounter; + else + gSaveCounter = slot1saveCounter; + } + return SAVE_STATUS_OK; + } + + if (slot1Status == SAVE_STATUS_OK) + { + gSaveCounter = slot1saveCounter; + if (slot2Status == SAVE_STATUS_ERROR) + return SAVE_STATUS_ERROR; + else + return SAVE_STATUS_OK; + } + + if (slot2Status == SAVE_STATUS_OK) + { + gSaveCounter = slot2saveCounter; + if (slot1Status == SAVE_STATUS_ERROR) + return SAVE_STATUS_ERROR; + else + return SAVE_STATUS_OK; + } + + if (slot1Status == SAVE_STATUS_EMPTY && slot2Status == SAVE_STATUS_EMPTY) + { + gSaveCounter = 0; + gFirstSaveSector = 0; + return SAVE_STATUS_EMPTY; + } + + gSaveCounter = 0; + gFirstSaveSector = 0; + return 2; +} + +u8 ReadSomeUnknownSectorAndVerify(u8 sector, u8 *data, u16 size) +{ + u16 i; + struct SaveSector *section = eSaveSection; + + DoReadFlashWholeSection(sector, section); + if (section->signature == FILE_SIGNATURE) + { + u16 checksum = CalculateChecksum(section->data, size); + if (section->id == checksum) + { + for (i = 0; i < size; i++) + data[i] = section->data[i]; + return SAVE_STATUS_OK; + } + else + { + return 2; + } + } + else + { + return SAVE_STATUS_EMPTY; + } +} + +u32 DoReadFlashWholeSection(u8 sector, struct SaveSector *section) +{ + ReadFlash(sector, 0, section->data, sizeof(struct SaveSector)); + return 1; +} + +u16 CalculateChecksum(const void *data, u16 size) +{ + u16 i; + u32 checksum = 0; + + for (i = 0; i < (size / 4); i++) + { + checksum += *((u32 *)data); + data += sizeof(u32); + } + + return ((checksum >> 16) + checksum); +} + +void nullsub_0201182C() +{ +} + +void nullsub_02011830() +{ +} + +void nullsub_02011834() +{ +} + +u16 * get_var_addr(u16 a0) +{ + if (a0 < VARS_START) + return NULL; + if (a0 < VAR_SPECIAL_0) + return &gSaveBlock1.vars[a0 - VARS_START]; + return NULL; +} + +bool32 flash_maincb_check_need_reset_pacifidlog_tm(void) +{ + u8 sp0; + u16 * data = get_var_addr(VAR_PACIFIDLOG_TM_RECEIVED_DAY); + rtc_maincb_is_time_since_last_berry_update_positive(&sp0); + if (*data <= gRtcUTCTime.days) + return TRUE; + else + return FALSE; +} + +bool32 flash_maincb_reset_pacifidlog_tm(void) +{ + u8 sp0; + if (flash_maincb_check_need_reset_pacifidlog_tm() == TRUE) + return TRUE; + rtc_maincb_is_time_since_last_berry_update_positive(&sp0); + if (gRtcUTCTime.days < 0) + return FALSE; + *get_var_addr(VAR_PACIFIDLOG_TM_RECEIVED_DAY) = 1; + if (flash_write_save_block_chunks_check_damage(0) != TRUE) + return FALSE; + return TRUE; +} diff --git a/berry_fix/payload/src/main.c b/berry_fix/payload/src/main.c new file mode 100644 index 000000000..325d3830e --- /dev/null +++ b/berry_fix/payload/src/main.c @@ -0,0 +1,289 @@ +#include "gba/gba.h" +#include "global.h" +#include "main.h" +#include "rtc.h" +#include "flash.h" + +static s32 gInitialWaitTimer; +IntrFunc gIntrTable[16]; +u16 gHeldKeys; +u16 gNewKeys; +u8 gIntrVector[0x100]; +u32 gUpdateSuccessful; +u32 gUnknown_3001194; +u32 gUnknown_30011A0[0x19]; +u32 gMainCallbackState; +u32 gGameVersion; + +EWRAM_DATA u8 gSharedMem[0x8000] = {}; + +void IntrMain(void); +void ReadKeys(void); +void dummy_intr_0(void); +void dummy_intr_1(void); +void main_callback(u32 *, void *, void *); + + +const char gBerryFixGameCode[] = "AGBJ"; +const IntrFunc gIntrFuncPointers[] = { + dummy_intr_0, + dummy_intr_1, + dummy_intr_0, + dummy_intr_0, + dummy_intr_0, + dummy_intr_0, + dummy_intr_0, + dummy_intr_0, + dummy_intr_0, + dummy_intr_0, + NULL, + NULL, + NULL +}; +const char gVersionData[][2] = { + {'J', 1}, + {'E', 2}, + {'D', 1}, + {'F', 1}, + {'I', 1}, + {'S', 1} +}; +const char gRubyTitleAndCode[] = "POKEMON RUBYAXV"; +const char gSapphireTitleAndCode[] = "POKEMON SAPPAXP"; +const u16 sDebugPals[20] = { + RGB(00, 00, 00), + RGB(31, 00, 00), + RGB(00, 31, 00), + RGB(00, 00, 31) +}; +const u16 sDebugDigitsGfx[] = INCBIN_U16("graphics/debug_digits.4bpp"); + +void AgbMain(void) +{ + RegisterRamReset(0x1E); + DmaCopy32(3, gIntrFuncPointers, gIntrTable, sizeof gIntrFuncPointers); + DmaCopy32(3, IntrMain, gIntrVector, sizeof(gIntrVector)); + INTR_VECTOR = gIntrVector; + REG_IE = INTR_FLAG_VBLANK; + if (*RomHeaderMagic == 0x96 && *(u32 *)RomHeaderGameCode == *(u32 *)gBerryFixGameCode) + REG_IE |= INTR_FLAG_GAMEPAK; + REG_DISPSTAT = DISPSTAT_VBLANK_INTR; + REG_IME = INTR_FLAG_VBLANK; + msg_load_gfx(); + gMainCallbackState = MAINCB_INIT; + gUnknown_3001194 = 0; + for (;;) + { + VBlankIntrWait(); + ReadKeys(); + main_callback(&gMainCallbackState, gUnknown_30011A0, gSharedMem); + } +} + +void dummy_intr_1(void) +{} + +void dummy_intr_0(void) +{} + +void ReadKeys(void) +{ + u16 keyInput = REG_KEYINPUT ^ KEYS_MASK; + gNewKeys = keyInput & ~gHeldKeys; + gHeldKeys = keyInput; +} + +void fill_palette(const u8 * src, u16 * dest, u8 value) +{ + s32 i; + for (i = 0; src[i] != 0; i++) + dest[i] = src[i] | value << 12; +} + +bool32 berry_fix_memcmp(const char * src1, const char * src2, size_t size) +{ + s32 i; + for (i = 0; i < size; i++) + { + if (src1[i] != src2[i]) + return FALSE; + } + return TRUE; +} + +s32 validate_rom_header_internal(void) +{ + char languageCode = *(RomHeaderGameCode + 3); + s32 softwareVersion = *RomHeaderSoftwareVersion; + s32 shouldUpdate = -1; + s32 i; + for (i = 0; i < ARRAY_COUNT(gVersionData); i++) + { + if (languageCode == gVersionData[i][0]) + { + if (softwareVersion >= gVersionData[i][1]) + { + shouldUpdate = 0; + } + else + { + shouldUpdate = 1; + } + break; + } + } + if (shouldUpdate != -1) + { + if (berry_fix_memcmp(RomHeaderGameTitle, gRubyTitleAndCode, 15) == TRUE) + { + if (shouldUpdate == 0) + return RUBY_NONEED; + else + { + gGameVersion = VERSION_RUBY; + return RUBY_UPDATABLE; + } + } + else if (berry_fix_memcmp(RomHeaderGameTitle, gSapphireTitleAndCode, 15) == TRUE) + { + if (shouldUpdate == 0) + return SAPPHIRE_NONEED; + else + { + gGameVersion = VERSION_SAPPHIRE; + return SAPPHIRE_UPDATABLE; + } + } + } + return INVALID; +} + +s32 validate_rom_header(void) +{ + if (*RomHeaderMakerCode == '0' && *(RomHeaderMakerCode + 1) == '1' && *RomHeaderMagic == 0x96) + return validate_rom_header_internal(); + else + return INVALID; +} + +void main_callback(u32 * state, void * unused1, void * unused2) +{ + u8 year; + switch (*state) + { + case MAINCB_INIT: + msg_display(MSGBOX_WILL_NOW_UPDATE); + if (++gInitialWaitTimer >= 180) + { + gInitialWaitTimer = 0; + gUpdateSuccessful = 0; + switch (validate_rom_header()) + { + case SAPPHIRE_UPDATABLE: + case RUBY_UPDATABLE: // Should Update Ruby + ++(*state); // MAINCB_CHECK_RTC + break; + case INVALID: // Invalid header + *state = MAINCB_ERROR; + break; + case SAPPHIRE_NONEED: // Should not update Sapphire + case RUBY_NONEED: // Should not update Ruby + *state = MAINCB_NO_NEED_TO_FIX; + break; + } + } + break; + case MAINCB_CHECK_RTC: + if (!rtc_maincb_is_rtc_working()) + *state = MAINCB_ERROR; + else + ++(*state); // MAINCB_CHECK_FLASH + break; + case MAINCB_CHECK_FLASH: + if (flash_maincb_ident_is_valid() == TRUE) + ++(*state); // MAINCB_READ_SAVE + else + *state = MAINCB_ERROR; + break; + case MAINCB_READ_SAVE: + if (flash_maincb_read_save(0) == SAVE_STATUS_OK) + ++(*state); // MAINCB_CHECK_TIME + else + *state = MAINCB_ERROR; + break; + case MAINCB_CHECK_TIME: + if (rtc_maincb_is_time_since_last_berry_update_positive(&year) == TRUE) + { + if (year == 0) + ++(*state); // MAINCB_FIX_DATE + else + *state = MAINCB_CHECK_PACIFIDLOG_TM; + } + else + { + if (year != 1) + *state = MAINCB_YEAR_MAKES_NO_SENSE; + else + ++(*state); // MAINCB_FIX_DATE + } + break; + case MAINCB_FIX_DATE: + rtc_maincb_fix_date(); + gUpdateSuccessful |= 1; + *state = MAINCB_CHECK_PACIFIDLOG_TM; + break; + case MAINCB_CHECK_PACIFIDLOG_TM: + if (flash_maincb_check_need_reset_pacifidlog_tm() == TRUE) + *state = MAINCB_FINISHED; + else + *state = MAINCB_FIX_PACIFIDLOG_TM; + break; + case MAINCB_FIX_PACIFIDLOG_TM: + msg_display(MSGBOX_UPDATING); + if (flash_maincb_reset_pacifidlog_tm() == TRUE) + { + gUpdateSuccessful |= 1; + *state = MAINCB_FINISHED; + } + else + *state = MAINCB_ERROR; + break; + case MAINCB_FINISHED: + if (gUpdateSuccessful == 0) + *state = MAINCB_NO_NEED_TO_FIX; + else + msg_display(MSGBOX_HAS_BEEN_UPDATED); + break; + case MAINCB_NO_NEED_TO_FIX: + msg_display(MSGBOX_NO_NEED_TO_UPDATE); + break; + case MAINCB_YEAR_MAKES_NO_SENSE: + msg_display(MSGBOX_UNABLE_TO_UPDATE); + break; + case MAINCB_ERROR: + msg_display(MSGBOX_UNABLE_TO_UPDATE); + break; + } +} + +void DBG_LoadDigitsPal(void) +{ + const u16 * src; + s32 i; + register vu16 * dest asm("r3") = (vu16 *)BG_PLTT + 1; + DmaFill16(3, RGB(31, 31, 31), (vu16 *)BG_PLTT, BG_PLTT_SIZE); + src = sDebugPals; + for (i = 0; i < 4; i++) + { + *dest = *src; + dest += 16; + src++; + } +} + +void DBG_LoadDigits(void) +{ + DmaFill16(3, 0x1111, (void *)VRAM + 0x8420, 0x1800); + DmaCopy32(3, sDebugDigitsGfx, (void *)VRAM + 0x8600, 0x200); + DBG_LoadDigitsPal(); +} diff --git a/berry_fix/payload/src/rtc.c b/berry_fix/payload/src/rtc.c new file mode 100644 index 000000000..e73f522aa --- /dev/null +++ b/berry_fix/payload/src/rtc.c @@ -0,0 +1,346 @@ +#include "gba/gba.h" +#include "siirtc.h" +#include "global.h" +#include "main.h" + +struct Time gTimeSinceBerryUpdate; +struct Time gRtcUTCTime; + +static u16 sRtcProbeStatus; +static struct SiiRtcInfo sRtcInfoBuffer; +static u8 sRtcProbeCode; +static u16 sImeBak; +static struct SiiRtcInfo sRtcInfoWork; + +const struct SiiRtcInfo sDefaultRTC = { + .year = 0, // 2000 + .month = 1, // January + .day = 1, // 01 + .dayOfWeek = 0, + .hour = 0, + .minute = 0, + .second = 0, + .status = 0, + .alarmHour = 0, + .alarmMinute = 0 +}; +const s32 sDaysPerMonth[] = { + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 +}; + +void rtc_get_status_and_datetime(struct SiiRtcInfo *); +u16 rtc_validate_datetime(struct SiiRtcInfo *); + + +void rtc_intr_disable(void) +{ + sImeBak = REG_IME; + REG_IME = 0; +} + +void rtc_intr_enable(void) +{ + REG_IME = sImeBak; +} + +s32 bcd_to_hex(u8 a0) +{ + if (a0 >= 0xa0 || (a0 & 0xF) >= 10) + return 0xFF; + return ((a0 >> 4) & 0xF) * 10 + (a0 & 0xF); +} + +bool8 is_leap_year(u8 year) +{ + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) + return TRUE; + return FALSE; +} + +u16 rtc_count_days_parameterized(u8 year, u8 month, u8 day) +{ + u16 numDays = 0; + s32 i; + for (i = year - 1; i > 0; i--) + { + numDays += 365; + if (is_leap_year(i) == TRUE) + numDays++; + } + for (i = 0; i < month - 1; i++) + numDays += sDaysPerMonth[i]; + if (month > MONTH_FEB && is_leap_year(year) == TRUE) + numDays++; + numDays += day; + return numDays; +} + +u16 rtc_count_days_from_info(struct SiiRtcInfo *info) +{ + return rtc_count_days_parameterized(bcd_to_hex(info->year), bcd_to_hex(info->month), bcd_to_hex(info->day)); +} + +static void rtc_probe_status(void) +{ + sRtcProbeStatus = 0; + rtc_intr_disable(); + SiiRtcUnprotect(); + sRtcProbeCode = SiiRtcProbe(); + rtc_intr_enable(); + if ((sRtcProbeCode & 0xF) != 1) + sRtcProbeStatus = 1; + else + { + if (sRtcProbeCode & 0xF0) + sRtcProbeStatus = 2; + else + sRtcProbeStatus = 0; + rtc_get_status_and_datetime(&sRtcInfoBuffer); + sRtcProbeStatus = rtc_validate_datetime(&sRtcInfoBuffer); + } +} + +u16 rtc_get_probe_status(void) +{ + return sRtcProbeStatus; +} + +void sub_020106EC(struct SiiRtcInfo * info) +{ + if (sRtcProbeStatus & 0xFF0) + *info = sDefaultRTC; + else + rtc_get_status_and_datetime(info); +} + +void rtc_get_datetime(struct SiiRtcInfo * info) +{ + rtc_intr_disable(); + SiiRtcGetDateTime(info); + rtc_intr_enable(); +} + +void rtc_get_status(struct SiiRtcInfo * info) +{ + rtc_intr_disable(); + SiiRtcGetStatus(info); + rtc_intr_enable(); +} + +void rtc_get_status_and_datetime(struct SiiRtcInfo * info) +{ + rtc_get_status(info); + rtc_get_datetime(info); +} + +u16 rtc_validate_datetime(struct SiiRtcInfo * info) +{ + s32 year, month, day; + u16 r4 = (info->status & SIIRTCINFO_POWER) ? 0x20 : 0; + if (!(info->status & SIIRTCINFO_24HOUR)) + r4 |= 0x10; + year = bcd_to_hex(info->year); + if (year == 0xFF) + r4 |= 0x40; + month = bcd_to_hex(info->month); + if (month == 0xFF || month == 0 || month > 12) + r4 |= 0x80; + day = bcd_to_hex(info->day); + if (day == 0xFF) + r4 |= 0x100; + if (month == MONTH_FEB) + { + if (day > is_leap_year(year) + sDaysPerMonth[1]) + r4 |= 0x100; + } + else + { + if (day > sDaysPerMonth[month - 1]) + r4 |= 0x100; + } + day = bcd_to_hex(info->hour); + if (day > 24) + r4 |= 0x200; + day = bcd_to_hex(info->minute); + if (day > 60) + r4 |= 0x400; + day = bcd_to_hex(info->second); + if (day > 60) + r4 |= 0x800; + return r4; +} + +void rtc_reset(void) +{ + rtc_intr_disable(); + SiiRtcReset(); + rtc_intr_enable(); +} + +void rtc_sub_time_from_datetime(struct SiiRtcInfo * datetime, struct Time * dest, struct Time * timediff) +{ + u16 r4 = rtc_count_days_from_info(datetime); + dest->seconds = bcd_to_hex(datetime->second) - timediff->seconds; + dest->minutes = bcd_to_hex(datetime->minute) - timediff->minutes; + dest->hours = bcd_to_hex(datetime->hour) - timediff->hours; + dest->days = r4 - timediff->days; + if (dest->seconds < 0) + { + dest->seconds += 60; + dest->minutes--; + } + if (dest->minutes < 0) + { + dest->minutes += 60; + dest->hours--; + } + if (dest->hours < 0) + { + dest->hours += 24; + dest->days--; + } +} + +void rtc_sub_time_from_time(struct Time * dest, struct Time * diff, struct Time * src) +{ + dest->seconds = src->seconds - diff->seconds; + dest->minutes = src->minutes - diff->minutes; + dest->hours = src->hours - diff->hours; + dest->days = src->days - diff->days; + if (dest->seconds < 0) + { + dest->seconds += 60; + dest->minutes--; + } + if (dest->minutes < 0) + { + dest->minutes += 60; + dest->hours--; + } + if (dest->hours < 0) + { + dest->hours += 24; + dest->days--; + } +} + +bool32 rtc_maincb_is_rtc_working(void) +{ + rtc_probe_status(); + if (rtc_get_probe_status() & 0xFF0) + return FALSE; + return TRUE; +} + +void rtc_set_datetime(struct SiiRtcInfo * info) +{ + vu16 imeBak = REG_IME; + REG_IME = 0; + SiiRtcSetDateTime(info); + REG_IME = imeBak; +} + +bool32 rtc_maincb_is_time_since_last_berry_update_positive(u8 * a0) +{ + rtc_get_status_and_datetime(&sRtcInfoWork); + *a0 = bcd_to_hex(sRtcInfoWork.year); + rtc_sub_time_from_datetime(&sRtcInfoWork, &gRtcUTCTime, LocalTimeOffset); + rtc_sub_time_from_time(&gTimeSinceBerryUpdate, LastBerryTreeUpdate, &gRtcUTCTime); + if (gTimeSinceBerryUpdate.days * 1440 + gTimeSinceBerryUpdate.hours * 60 + gTimeSinceBerryUpdate.minutes >= 0) + return TRUE; + return FALSE; +} + +u32 hex_to_bcd(u8 a0) +{ + u32 r4; + if (a0 > 99) + return 0xFF; + r4 = Div(a0, 10) << 4; + r4 |= Mod(a0, 10); + return r4; +} + +void sii_rtc_inc(u8 * a0) +{ + *a0 = hex_to_bcd(bcd_to_hex(*a0) + 1); +} + +void sii_rtc_inc_month(struct SiiRtcInfo * a0) +{ + sii_rtc_inc(&a0->month); + if (bcd_to_hex(a0->month) > 12) + { + sii_rtc_inc(&a0->year); + a0->month = MONTH_JAN; + } +} + +void sii_rtc_inc_day(struct SiiRtcInfo * a0) +{ + sii_rtc_inc(&a0->day); + if (bcd_to_hex(a0->day) > sDaysPerMonth[bcd_to_hex(a0->month) - 1]) + { + if (!is_leap_year(bcd_to_hex(a0->year)) || bcd_to_hex(a0->month) != MONTH_FEB || bcd_to_hex(a0->day) != 29) + { + a0->day = 1; + sii_rtc_inc_month(a0); + } + } +} + +bool32 rtc_is_past_feb_28_2000(struct SiiRtcInfo * a0) +{ + if (bcd_to_hex(a0->year) == 0) + { + if (bcd_to_hex(a0->month) == MONTH_JAN) + return FALSE; + if (bcd_to_hex(a0->month) > MONTH_FEB) + return TRUE; + if (bcd_to_hex(a0->day) == 29) + return TRUE; + return FALSE; + } + if (bcd_to_hex(a0->year) == 1) + return TRUE; + return FALSE; +} + +void rtc_maincb_fix_date(void) +{ + rtc_get_status_and_datetime(&sRtcInfoWork); + if (bcd_to_hex(sRtcInfoWork.year) == 0 || bcd_to_hex(sRtcInfoWork.year) == 1) + { + if (bcd_to_hex(sRtcInfoWork.year) == 1) + { + sRtcInfoWork.year = 2; + sRtcInfoWork.month = MONTH_JAN; + sRtcInfoWork.day = 2; + rtc_set_datetime(&sRtcInfoWork); + } + else + { + if (rtc_is_past_feb_28_2000(&sRtcInfoWork) == TRUE) + { + sii_rtc_inc_day(&sRtcInfoWork); + sii_rtc_inc(&sRtcInfoWork.year); + } + else + { + sii_rtc_inc(&sRtcInfoWork.year); + } + rtc_set_datetime(&sRtcInfoWork); + } + } +} diff --git a/berry_fix/payload/src/siirtc.c b/berry_fix/payload/src/siirtc.c new file mode 100644 index 000000000..965a068f1 --- /dev/null +++ b/berry_fix/payload/src/siirtc.c @@ -0,0 +1,432 @@ +// Ruby/Sapphire/Emerald cartridges contain a Seiko Instruments Inc. (SII) +// S-3511A real-time clock (RTC). This library ("SIIRTC_V001") is for +// communicating with the RTC. + +#include "gba/gba.h" +#include "siirtc.h" + +#define STATUS_INTFE 0x02 // frequency interrupt enable +#define STATUS_INTME 0x08 // per-minute interrupt enable +#define STATUS_INTAE 0x20 // alarm interrupt enable +#define STATUS_24HOUR 0x40 // 0: 12-hour mode, 1: 24-hour mode +#define STATUS_POWER 0x80 // power on or power failure occurred + +#define TEST_MODE 0x80 // flag in the "second" byte + +#define ALARM_AM 0x00 +#define ALARM_PM 0x80 + +#define OFFSET_YEAR offsetof(struct SiiRtcInfo, year) +#define OFFSET_MONTH offsetof(struct SiiRtcInfo, month) +#define OFFSET_DAY offsetof(struct SiiRtcInfo, day) +#define OFFSET_DAY_OF_WEEK offsetof(struct SiiRtcInfo, dayOfWeek) +#define OFFSET_HOUR offsetof(struct SiiRtcInfo, hour) +#define OFFSET_MINUTE offsetof(struct SiiRtcInfo, minute) +#define OFFSET_SECOND offsetof(struct SiiRtcInfo, second) +#define OFFSET_STATUS offsetof(struct SiiRtcInfo, status) +#define OFFSET_ALARM_HOUR offsetof(struct SiiRtcInfo, alarmHour) +#define OFFSET_ALARM_MINUTE offsetof(struct SiiRtcInfo, alarmMinute) + +#define INFO_BUF(info, index) (*((u8 *)(info) + (index))) + +#define DATETIME_BUF(info, index) INFO_BUF(info, OFFSET_YEAR + index) +#define DATETIME_BUF_LEN (OFFSET_SECOND - OFFSET_YEAR + 1) + +#define TIME_BUF(info, index) INFO_BUF(info, OFFSET_HOUR + index) +#define TIME_BUF_LEN (OFFSET_SECOND - OFFSET_HOUR + 1) + +#define WR 0 // command for writing data +#define RD 1 // command for reading data + +#define CMD(n) (0x60 | (n << 1)) + +#define CMD_RESET CMD(0) +#define CMD_STATUS CMD(1) +#define CMD_DATETIME CMD(2) +#define CMD_TIME CMD(3) +#define CMD_ALARM CMD(4) + +#define GPIO_PORT_DATA (*(vu16 *)0x80000C4) +#define GPIO_PORT_DIRECTION (*(vu16 *)0x80000C6) +#define GPIO_PORT_READ_ENABLE (*(vu16 *)0x80000C8) + +extern vu16 GPIOPortDirection; + +static u16 sDummy; // unused variable +static bool8 sLocked; + +static int WriteCommand(u8 value); +static int WriteData(u8 value); +static u8 ReadData(); +static void EnableGpioPortRead(); +static void DisableGpioPortRead(); + +static const char AgbLibRtcVersion[] = "SIIRTC_V001"; + +void SiiRtcUnprotect() +{ + EnableGpioPortRead(); + sLocked = FALSE; +} + +void SiiRtcProtect() +{ + DisableGpioPortRead(); + sLocked = TRUE; +} + +u8 SiiRtcProbe() +{ + u8 errorCode; + struct SiiRtcInfo rtc; + + if (!SiiRtcGetStatus(&rtc)) + return 0; + + errorCode = 0; + + if ((rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == SIIRTCINFO_POWER + || (rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == 0) + { + // The RTC is in 12-hour mode. Reset it and switch to 24-hour mode. + + // Note that the conditions are redundant and equivalent to simply + // "(rtc.status & SIIRTCINFO_24HOUR) == 0". It's possible that this + // was also intended to handle resetting the clock after power failure + // but a mistake was made. + + if (!SiiRtcReset()) + return 0; + + errorCode++; + } + + SiiRtcGetTime(&rtc); + + if (rtc.second & TEST_MODE) + { + // The RTC is in test mode. Reset it to leave test mode. + + if (!SiiRtcReset()) + return (errorCode << 4) & 0xF0; + + errorCode++; + } + + return (errorCode << 4) | 1; +} + +bool8 SiiRtcReset() +{ + u8 result; + struct SiiRtcInfo rtc; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_RESET | WR); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + rtc.status = SIIRTCINFO_24HOUR; + + result = SiiRtcSetStatus(&rtc); + + return result; +} + +bool8 SiiRtcGetStatus(struct SiiRtcInfo *rtc) +{ + u8 statusData; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_STATUS | RD); + + GPIO_PORT_DIRECTION = 5; + + statusData = ReadData(); + + rtc->status = (statusData & (STATUS_POWER | STATUS_24HOUR)) + | ((statusData & STATUS_INTAE) >> 3) + | ((statusData & STATUS_INTME) >> 2) + | ((statusData & STATUS_INTFE) >> 1); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetStatus(struct SiiRtcInfo *rtc) +{ + u8 statusData; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + statusData = STATUS_24HOUR + | ((rtc->status & SIIRTCINFO_INTAE) << 3) + | ((rtc->status & SIIRTCINFO_INTME) << 2) + | ((rtc->status & SIIRTCINFO_INTFE) << 1); + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_STATUS | WR); + + WriteData(statusData); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcGetDateTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_DATETIME | RD); + + GPIO_PORT_DIRECTION = 5; + + for (i = 0; i < DATETIME_BUF_LEN; i++) + DATETIME_BUF(rtc, i) = ReadData(); + + INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetDateTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_DATETIME | WR); + + for (i = 0; i < DATETIME_BUF_LEN; i++) + WriteData(DATETIME_BUF(rtc, i)); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcGetTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_TIME | RD); + + GPIO_PORT_DIRECTION = 5; + + for (i = 0; i < TIME_BUF_LEN; i++) + TIME_BUF(rtc, i) = ReadData(); + + INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetTime(struct SiiRtcInfo *rtc) +{ + u8 i; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIO_PORT_DIRECTION = 7; + + WriteCommand(CMD_TIME | WR); + + for (i = 0; i < TIME_BUF_LEN; i++) + WriteData(TIME_BUF(rtc, i)); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +bool8 SiiRtcSetAlarm(struct SiiRtcInfo *rtc) +{ + u8 i; + u8 alarmData[2]; + + if (sLocked == TRUE) + return FALSE; + + sLocked = TRUE; + + // Decode BCD. + alarmData[0] = (rtc->alarmHour & 0xF) + 10 * ((rtc->alarmHour >> 4) & 0xF); + + // The AM/PM flag must be set correctly even in 24-hour mode. + + if (alarmData[0] < 12) + alarmData[0] = rtc->alarmHour | ALARM_AM; + else + alarmData[0] = rtc->alarmHour | ALARM_PM; + + alarmData[1] = rtc->alarmMinute; + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 5; + + GPIOPortDirection = 7; // Why is this the only instance that uses a symbol? + + WriteCommand(CMD_ALARM | WR); + + for (i = 0; i < 2; i++) + WriteData(alarmData[i]); + + GPIO_PORT_DATA = 1; + GPIO_PORT_DATA = 1; + + sLocked = FALSE; + + return TRUE; +} + +static int WriteCommand(u8 value) +{ + u8 i; + u8 temp; + + for (i = 0; i < 8; i++) + { + temp = ((value >> (7 - i)) & 1); + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 5; + } + + // control reaches end of non-void function +} + +static int WriteData(u8 value) +{ + u8 i; + u8 temp; + + for (i = 0; i < 8; i++) + { + temp = ((value >> i) & 1); + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 4; + GPIO_PORT_DATA = (temp << 1) | 5; + } + + // control reaches end of non-void function +} + +static u8 ReadData() +{ + u8 i; + u8 temp; + u8 value; + + for (i = 0; i < 8; i++) + { + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 4; + GPIO_PORT_DATA = 5; + + temp = ((GPIO_PORT_DATA & 2) >> 1); + value = (value >> 1) | (temp << 7); // UB: accessing uninitialized var + } + + return value; +} + +static void EnableGpioPortRead() +{ + GPIO_PORT_READ_ENABLE = 1; +} + +static void DisableGpioPortRead() +{ + GPIO_PORT_READ_ENABLE = 0; +} |