summaryrefslogtreecommitdiff
path: root/berry_fix/payload/src
diff options
context:
space:
mode:
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.c4
-rw-r--r--berry_fix/payload/src/main.c2
-rw-r--r--berry_fix/payload/src/rtc.c4
-rw-r--r--berry_fix/payload/src/siirtc.c432
8 files changed, 1043 insertions, 5 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..cda3b73f6
--- /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
index 3a0369dda..1f09d0b8f 100644
--- a/berry_fix/payload/src/flash.c
+++ b/berry_fix/payload/src/flash.c
@@ -1,5 +1,5 @@
-#include <gba/gba.h>
-#include <agb_flash.h>
+#include "gba/gba.h"
+#include "gba/flash_internal.h"
#include "constants/vars.h"
#include "global.h"
#include "main.h"
diff --git a/berry_fix/payload/src/main.c b/berry_fix/payload/src/main.c
index 249150665..325d3830e 100644
--- a/berry_fix/payload/src/main.c
+++ b/berry_fix/payload/src/main.c
@@ -1,4 +1,4 @@
-#include <gba/gba.h>
+#include "gba/gba.h"
#include "global.h"
#include "main.h"
#include "rtc.h"
diff --git a/berry_fix/payload/src/rtc.c b/berry_fix/payload/src/rtc.c
index 97692e205..e73f522aa 100644
--- a/berry_fix/payload/src/rtc.c
+++ b/berry_fix/payload/src/rtc.c
@@ -1,5 +1,5 @@
-#include <gba/gba.h>
-#include <siirtc.h>
+#include "gba/gba.h"
+#include "siirtc.h"
#include "global.h"
#include "main.h"
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;
+}