summaryrefslogtreecommitdiff
path: root/berry_fix/payload/src
diff options
context:
space:
mode:
authorGriffinR <griffin.richards@comcast.net>2019-09-26 15:02:38 -0400
committerGitHub <noreply@github.com>2019-09-26 15:02:38 -0400
commit1016b239186097361ea20f2f91adf3932026f40c (patch)
tree840a0f28046d3c66fcaf1c2bb8ab3228386a3a8a /berry_fix/payload/src
parentbb236b490d99fef02ed172e7ca86f6c70ef09527 (diff)
parent208e1c968959c781562f0b94c03368385ce7012c (diff)
Merge branch 'master' into rename-tablecmds
Diffstat (limited to 'berry_fix/payload/src')
-rw-r--r--berry_fix/payload/src/agb_flash.c296
-rw-r--r--berry_fix/payload/src/agb_flash_1m.c86
-rw-r--r--berry_fix/payload/src/agb_flash_le.c31
-rw-r--r--berry_fix/payload/src/agb_flash_mx.c193
-rw-r--r--berry_fix/payload/src/flash.c752
-rw-r--r--berry_fix/payload/src/main.c289
-rw-r--r--berry_fix/payload/src/rtc.c346
-rw-r--r--berry_fix/payload/src/siirtc.c432
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 = &REG_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;
+}