Skip to content

Commit

Permalink
Further implementation of RTC
Browse files Browse the repository at this point in the history
  • Loading branch information
Gericom committed Oct 6, 2024
1 parent 4ea794f commit ac48f9d
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 53 deletions.
15 changes: 15 additions & 0 deletions code/core/arm7/source/IpcServices/RtcIpcService.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "common.h"
#include <libtwl/sio/sioRtc.h>
#include "RtcIpcService.h"

void RtcIpcService::Start()
{
rtc_init();
ThreadIpcService::Start();
}

void RtcIpcService::HandleMessage(u32 data)
{
rtc_readDateTime(reinterpret_cast<rtc_datetime_t*>(data << 2));
SendResponseMessage(1);
}
15 changes: 15 additions & 0 deletions code/core/arm7/source/IpcServices/RtcIpcService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once
#include "ThreadIpcService.h"
#include "ipcChannels.h"

class RtcIpcService : public ThreadIpcService
{
u32 _threadStack[128];

public:
RtcIpcService()
: ThreadIpcService(IPC_CHANNEL_RTC, 10, _threadStack, sizeof(_threadStack)) { }

void Start() override;
void HandleMessage(u32 data) override;
};
5 changes: 4 additions & 1 deletion code/core/arm7/source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "IpcServices/GbaSoundIpcService.h"
#include "IpcServices/SystemIpcService.h"
#include "IpcServices/GbaSaveIpcService.h"
#include "IpcServices/RtcIpcService.h"
#include "Arm7State.h"
#include "ExitMode.h"
#include "FramerateAdjustment.h"
Expand All @@ -24,6 +25,7 @@ static FsIpcService sFsIpcService;
static GbaSoundIpcService sGbaSoundIpcService;
static SystemIpcService sSystemIpcService;
static GbaSaveIpcService sGbaSaveIpcService;
static RtcIpcService sRtcIpcService;
static rtos_event_t sVBlankEvent;
static volatile u8 sMcuIrqFlag = false;
static Arm7State sState;
Expand Down Expand Up @@ -76,6 +78,7 @@ static void initializeIpcServices()
sGbaSoundIpcService.Start();
sSystemIpcService.Start();
sGbaSaveIpcService.Start();
sRtcIpcService.Start();
}

static void initializeVBlankIrq()
Expand Down Expand Up @@ -192,6 +195,6 @@ int main()
rtos_waitEvent(&sVBlankEvent, true, true);
updateArm7();
}

return 0;
}
210 changes: 165 additions & 45 deletions code/core/arm9/source/Peripherals/RomGpio/RomGpioRtc.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "common.h"
#include <libtwl/mem/memSwap.h>
#include <libtwl/ipc/ipcFifoSystem.h>
#include <libtwl/ipc/ipcFifo.h>
#include "cp15.h"
#include "IpcChannels.h"
#include "RomGpio.h"
#include "RomGpioRtc.h"

Expand All @@ -24,13 +28,21 @@

#define RIO_RTC_STATUS_WRITE_MASK 0b01101010

[[gnu::section(".ewram.bss")]]
RomGpioRtc::rio_rtc_datetime_t RomGpioRtc::sDSRtcDateTime alignas(32);

void RomGpioRtc::Update(RomGpio& romGpio)
{
if (!romGpio.GetPinState(ROM_GPIO_PIN_CS))
{
_state = RtcTransferState::CommandWaitFallingEdge;
_shiftRegister = 0;
_bitCount = 0;
if (_offsetUpdateRequired)
{
_offsetUpdateRequired = false;
UpdateRtcOffset();
}
}
else
{
Expand Down Expand Up @@ -111,7 +123,13 @@ void RomGpioRtc::CommandWaitRisingEdge(RomGpio& romGpio)
}
else
{
_state = _shiftRegister & 1
bool isRead = _shiftRegister & 1;
if (isRead &&
(_command == RIO_RTC_COMMAND_DATE_TIME || _command == RIO_RTC_COMMAND_TIME))
{
UpdateDateTime();
}
_state = isRead
? RtcTransferState::OutDataWaitFallingEdge
: RtcTransferState::InDataWaitFallingEdge;
_shiftRegister = 0;
Expand Down Expand Up @@ -147,6 +165,7 @@ void RomGpioRtc::HandleInDataWaitRisingEdge(RomGpio& romGpio)
}
case RIO_RTC_COMMAND_DATE_TIME:
{
_offsetUpdateRequired = true;
switch (_byteIndex)
{
case 0:
Expand Down Expand Up @@ -179,6 +198,7 @@ void RomGpioRtc::HandleInDataWaitRisingEdge(RomGpio& romGpio)
}
case RIO_RTC_COMMAND_TIME:
{
_offsetUpdateRequired = true;
switch (_byteIndex)
{
case 0:
Expand Down Expand Up @@ -289,6 +309,38 @@ void RomGpioRtc::RtcReset()
mem_swapByte(0, &_dateTime.time.second);
_statusRegister = 0;
_intRegister = 0;
UpdateRtcOffset();
}

void RomGpioRtc::UpdateDSDateTime()
{
dc_invalidateRange(&sDSRtcDateTime, sizeof(sDSRtcDateTime));
ipc_sendWordDirect(
((((u32)&sDSRtcDateTime) >> 2) << IPC_FIFO_MSG_CHANNEL_BITS) |
IPC_CHANNEL_RTC);
while (ipc_isRecvFifoEmpty());
ipc_recvWordDirect();
}

void RomGpioRtc::UpdateDateTime()
{
UpdateDSDateTime();
u32 dsRtcSecondsSince2000 = ToSecondsSinceJanuary2000(sDSRtcDateTime, true);
u32 secondsSince2000 = dsRtcSecondsSince2000 + _rtcOffset;
FromSecondsSinceJanuary2000(secondsSince2000, _dateTime, _statusRegister & RIO_RTC_STATUS_24H);
int weekDay = (_dateTime.date.weekDay + _weekDayOffset) % 7;
_dateTime.date.weekDay = weekDay < 0 ? weekDay + 7 : weekDay;
}

void RomGpioRtc::UpdateRtcOffset()
{
UpdateDSDateTime();
u32 dsRtcSecondsSince2000 = ToSecondsSinceJanuary2000(sDSRtcDateTime, true);
u32 gbaRtcSecondsSince2000 = ToSecondsSinceJanuary2000(_dateTime, _statusRegister & RIO_RTC_STATUS_24H);
rio_rtc_datetime_t newDateTime;
FromSecondsSinceJanuary2000(gbaRtcSecondsSince2000, newDateTime, true);
_rtcOffset = gbaRtcSecondsSince2000 - dsRtcSecondsSince2000;
_weekDayOffset = (_dateTime.date.weekDay - newDateTime.date.weekDay) % 7;
}

void RomGpioRtc::SetYear(u8 value)
Expand Down Expand Up @@ -323,47 +375,12 @@ void RomGpioRtc::SetDayOfMonth(u8 value)
value = 1;
}

u32 daysInMonth = 0;
switch (_dateTime.date.month)
{
case 0x01:
case 0x03:
case 0x05:
case 0x07:
case 0x08:
case 0x10:
case 0x12:
{
daysInMonth = 31;
break;
}
case 0x04:
case 0x06:
case 0x09:
case 0x11:
{
daysInMonth = 30;
break;
}
case 0x02:
{
u32 year = 2000 + FromBcd(_dateTime.date.year);
// It is sufficient here to check if the year is divisible by 4
if ((year & 3) == 0)
{
daysInMonth = 29;
}
else
{
daysInMonth = 28;
}
break;
}
}
u32 year = FromBcd(_dateTime.date.year);
u32 month = FromBcd(_dateTime.date.month);
u32 daysInMonth = GetNumberOfDaysInMonth(2000 + year, month);
if (FromBcd(value) > daysInMonth)
{
value = 1;
u32 month = FromBcd(_dateTime.date.month);
if (++month == 13)
{
month = 1;
Expand Down Expand Up @@ -435,14 +452,117 @@ void RomGpioRtc::SetSecond(u8 value)
mem_swapByte(value, &_dateTime.time.second);
}

u8 RomGpioRtc::FromBcd(u8 bcdValue)
u32 RomGpioRtc::FromBcd(u32 bcdValue) const
{
return bcdValue - 6 * (bcdValue >> 4);
}

u32 RomGpioRtc::ToBcd(u32 value) const
{
static const u8 sToBcd[100] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
};

return sToBcd[value];
}

u32 RomGpioRtc::ToSecondsSinceJanuary2000(const rio_rtc_datetime_t& dateTime, bool time24h) const
{
return (bcdValue >> 4) * 10 + (bcdValue & 0xF);
u32 yearsSince2000 = FromBcd(dateTime.date.year);
u32 leapDays = yearsSince2000 / 4;
if ((yearsSince2000 & 3) != 0)
{
leapDays++;
}

u32 days = leapDays + yearsSince2000 * 365 + FromBcd(dateTime.date.monthDay) - 1;
u32 month = FromBcd(dateTime.date.month);
for (u32 i = 1; i < month; i++)
{
days += GetNumberOfDaysInMonth(2000 + yearsSince2000, i);
}

u32 hours = FromBcd(dateTime.time.hour & 0x1F);
if (!time24h && (dateTime.time.hour & 0x80))
{
hours += 12;
}

return ((days * 24u + hours) * 60u
+ FromBcd(dateTime.time.minute & 0x3F)) * 60u
+ FromBcd(dateTime.time.second & 0x3F);
}

u8 RomGpioRtc::ToBcd(u8 value)
void RomGpioRtc::FromSecondsSinceJanuary2000(u32 secondsSinceJanuary2000, rio_rtc_datetime_t& dateTime, bool time24h) const
{
u32 lowDigit = value % 10;
u32 highDigit = value / 10;
return (highDigit << 4) | lowDigit;
dateTime.time.second = ToBcd(secondsSinceJanuary2000 % 60);
u32 minutesSinceJanuary2000 = secondsSinceJanuary2000 / 60;
dateTime.time.minute = ToBcd(minutesSinceJanuary2000 % 60);
u32 hoursSinceJanuary2000 = minutesSinceJanuary2000 / 60;

u32 hours = hoursSinceJanuary2000 % 24;
u32 amPmFlag = hours >= 12 ? 0x80 : 0;
if (!time24h && hours >= 12)
{
hours -= 12;
}
dateTime.time.hour = ToBcd(hours) | amPmFlag;

u32 daysSinceJanuary2000 = hoursSinceJanuary2000 / 24;
dateTime.date.weekDay = ((daysSinceJanuary2000 + 5) % 7); // 1 January 2000 was a Saturday

u32 year = 2000 + daysSinceJanuary2000 * 4 / 1461;
u32 remainingDays = ((daysSinceJanuary2000 * 4) % 1461) / 4;

dateTime.date.year = ToBcd(year - 2000);

u32 month = 1;
u32 daysInMonth = GetNumberOfDaysInMonth(year, month);
if (remainingDays >= daysInMonth)
{
remainingDays -= daysInMonth;
month++;
daysInMonth = GetNumberOfDaysInMonth(year, month);
if (remainingDays >= daysInMonth)
{
remainingDays -= daysInMonth;
month++;
for (; month < 12; month++)
{
u32 daysInMonth = GetNumberOfDaysInMonth(year, month);
if (remainingDays < daysInMonth)
{
break;
}
remainingDays -= daysInMonth;
}
}
}

dateTime.date.month = ToBcd(month);
dateTime.date.monthDay = ToBcd(remainingDays + 1);
}

u32 RomGpioRtc::GetNumberOfDaysInMonth(u32 year, u32 month) const
{
static const u8 sDaysPerMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

u32 result = sDaysPerMonth[month - 1];
if (month == 2)
{
// It is sufficient here to check if the year is divisible by 4
result += ((year & 3) == 0);
}

return result;
}
22 changes: 15 additions & 7 deletions code/core/arm9/source/Peripherals/RomGpio/RomGpioRtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,13 @@ class RomGpioRtc
public:
RomGpioRtc()
: _state(RtcTransferState::CommandWaitFallingEdge), _shiftRegister(0), _bitCount(0)
, _statusRegister(0x40), _intRegister(0)
{
*(u32*)&_dateTime.date = 0x030A0224;
*(u32*)&_dateTime.time = 0x00300910;
}
, _statusRegister(0x40), _intRegister(0), _rtcOffset(0), _weekDayOffset(0), _offsetUpdateRequired(false) { }

void Update(RomGpio& romGpio);

private:
static rio_rtc_datetime_t sDSRtcDateTime;

RtcTransferState _state;
u32 _shiftRegister;
u32 _bitCount;
Expand All @@ -58,12 +56,18 @@ class RomGpioRtc
u16 _intRegister;
rio_rtc_datetime_t _dateTime;
u8 _padding;
s32 _rtcOffset;
s16 _weekDayOffset;
u16 _offsetUpdateRequired;

void CommandWaitRisingEdge(RomGpio& romGpio);
void HandleInDataWaitRisingEdge(RomGpio& romGpio);
void HandleOutDataWaitFallingEdge(RomGpio& romGpio);

void RtcReset();
void UpdateDSDateTime();
void UpdateDateTime();
void UpdateRtcOffset();

void SetYear(u8 value);
void SetMonth(u8 value);
Expand All @@ -73,6 +77,10 @@ class RomGpioRtc
void SetMinute(u8 value);
void SetSecond(u8 value);

u8 FromBcd(u8 bcdValue);
u8 ToBcd(u8 value);
u32 FromBcd(u32 bcdValue) const;
u32 ToBcd(u32 value) const;

u32 ToSecondsSinceJanuary2000(const rio_rtc_datetime_t& dateTime, bool time24h) const;
void FromSecondsSinceJanuary2000(u32 secondsSinceJanuary2000, rio_rtc_datetime_t& dateTime, bool time24h) const;
u32 GetNumberOfDaysInMonth(u32 year, u32 month) const;
};
1 change: 1 addition & 0 deletions code/core/common/IpcChannels.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
#define IPC_CHANNEL_SYSTEM 15
#define IPC_CHANNEL_GBA_SOUND 16
#define IPC_CHANNEL_FS 17
#define IPC_CHANNEL_RTC 18

0 comments on commit ac48f9d

Please sign in to comment.