import from github
This commit is contained in:
296
berry_fix/payload/src/agb_flash.c
Normal file
296
berry_fix/payload/src/agb_flash.c
Normal file
@ -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;
|
||||
}
|
86
berry_fix/payload/src/agb_flash_1m.c
Normal file
86
berry_fix/payload/src/agb_flash_1m.c
Normal file
@ -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;
|
||||
}
|
31
berry_fix/payload/src/agb_flash_le.c
Normal file
31
berry_fix/payload/src/agb_flash_le.c
Normal file
@ -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
|
||||
}
|
||||
};
|
193
berry_fix/payload/src/agb_flash_mx.c
Normal file
193
berry_fix/payload/src/agb_flash_mx.c
Normal file
@ -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;
|
||||
}
|
752
berry_fix/payload/src/flash.c
Normal file
752
berry_fix/payload/src/flash.c
Normal file
@ -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;
|
||||
}
|
289
berry_fix/payload/src/main.c
Normal file
289
berry_fix/payload/src/main.c
Normal file
@ -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();
|
||||
}
|
346
berry_fix/payload/src/rtc.c
Normal file
346
berry_fix/payload/src/rtc.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
432
berry_fix/payload/src/siirtc.c
Normal file
432
berry_fix/payload/src/siirtc.c
Normal file
@ -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;
|
||||
}
|
Reference in New Issue
Block a user