2020-02-16 21:11:01 -05:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include <time.h>
|
|
|
|
#include "Rtc4513.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "MessageManager.h"
|
|
|
|
#include "BatteryManager.h"
|
|
|
|
#include "../Utilities/HexUtilities.h"
|
|
|
|
|
|
|
|
//TODO: Partial implementation
|
|
|
|
//Missing stuff: most flags e.g: 30ADJ, 24/12, CAL/HW, WRAP, etc.
|
|
|
|
|
|
|
|
Rtc4513::Rtc4513(Console* console)
|
|
|
|
{
|
|
|
|
_console = console;
|
|
|
|
}
|
|
|
|
|
|
|
|
Rtc4513::~Rtc4513()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Rtc4513::LoadBattery()
|
|
|
|
{
|
|
|
|
vector<uint8_t> rtcData = _console->GetBatteryManager()->LoadBattery(".rtc");
|
|
|
|
|
|
|
|
if(rtcData.size() == sizeof(_regs) + sizeof(uint64_t)) {
|
|
|
|
memcpy(_regs, rtcData.data(), sizeof(_regs));
|
|
|
|
uint64_t time = 0;
|
|
|
|
for(uint32_t i = 0; i < sizeof(uint64_t); i++) {
|
|
|
|
time <<= 8;
|
|
|
|
time |= rtcData[sizeof(_regs) + i];
|
|
|
|
}
|
|
|
|
_lastTime = time;
|
|
|
|
} else {
|
|
|
|
_lastTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Rtc4513::SaveBattery()
|
|
|
|
{
|
|
|
|
vector<uint8_t> rtcData;
|
|
|
|
rtcData.resize(sizeof(_regs) + sizeof(uint64_t), 0);
|
|
|
|
|
|
|
|
memcpy(rtcData.data(), _regs, sizeof(_regs));
|
|
|
|
uint64_t time = _lastTime;
|
|
|
|
for(uint32_t i = 0; i < sizeof(uint64_t); i++) {
|
|
|
|
rtcData[sizeof(_regs) + i] = (time >> 56) & 0xFF;
|
|
|
|
time <<= 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
_console->GetBatteryManager()->SaveBattery(".rtc", rtcData.data(), (uint32_t)rtcData.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Rtc4513::UpdateTime()
|
|
|
|
{
|
|
|
|
if(IsReset()) {
|
|
|
|
//Reset seconds to 0
|
|
|
|
_regs[0] = 0;
|
|
|
|
_regs[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t currentTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
|
|
uint32_t elapsedSeconds = (uint32_t)(currentTime - _lastTime);
|
|
|
|
if(elapsedSeconds <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(IsStop() || IsReset() || IsHold()) {
|
|
|
|
_lastTime = currentTime;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::tm tm = { };
|
|
|
|
tm.tm_sec = GetSeconds();
|
|
|
|
tm.tm_min = GetMinutes();
|
|
|
|
tm.tm_hour = GetHours();
|
|
|
|
tm.tm_mday = GetDay();
|
|
|
|
tm.tm_mon = GetMonth() - 1;
|
|
|
|
tm.tm_year = (GetYear() >= 90 ? 0 : 100) + GetYear();
|
|
|
|
|
|
|
|
std::time_t tt = mktime(&tm);
|
2020-02-22 00:30:03 -05:00
|
|
|
if(tt == -1 || GetMonth() == 0) {
|
2020-02-16 21:11:01 -05:00
|
|
|
_lastTime = currentTime;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-22 00:30:03 -05:00
|
|
|
int8_t dowGap = 0;
|
|
|
|
if(tm.tm_wday != GetDoW()) {
|
|
|
|
//The DoW on the RTC can be set to any arbitrary value for a specific date
|
|
|
|
//Check the gap between the value set by the game & the real dow for that date
|
|
|
|
dowGap = (int8_t)tm.tm_wday - (int8_t)GetDoW();
|
|
|
|
}
|
|
|
|
|
2020-02-16 21:11:01 -05:00
|
|
|
std::chrono::system_clock::time_point timePoint = std::chrono::system_clock::from_time_t(tt);
|
|
|
|
timePoint += std::chrono::seconds((uint32_t)elapsedSeconds);
|
|
|
|
|
|
|
|
std::time_t newTime = system_clock::to_time_t(timePoint);
|
|
|
|
std::tm newTm;
|
2020-02-22 12:11:51 -05:00
|
|
|
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
2020-02-16 21:11:01 -05:00
|
|
|
localtime_s(&newTm, &newTime);
|
|
|
|
#else
|
|
|
|
localtime_r(&newTime, &newTm);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
_regs[0] = newTm.tm_sec % 10;
|
|
|
|
_regs[1] = newTm.tm_sec / 10;
|
|
|
|
|
|
|
|
_regs[2] = newTm.tm_min % 10;
|
|
|
|
_regs[3] = newTm.tm_min / 10;
|
|
|
|
|
|
|
|
_regs[4] = newTm.tm_hour % 10;
|
|
|
|
_regs[5] = newTm.tm_hour / 10;
|
|
|
|
|
|
|
|
_regs[6] = newTm.tm_mday % 10;
|
|
|
|
_regs[7] = newTm.tm_mday / 10;
|
|
|
|
|
|
|
|
_regs[8] = (newTm.tm_mon + 1) % 10;
|
|
|
|
_regs[9] = (newTm.tm_mon + 1) / 10;
|
|
|
|
|
|
|
|
int year = newTm.tm_year + 1900;
|
|
|
|
year -= year >= 2000 ? 2000 : 1900;
|
|
|
|
|
|
|
|
_regs[10] = year % 10;
|
|
|
|
_regs[11] = year / 10;
|
|
|
|
|
2020-02-22 00:30:03 -05:00
|
|
|
_regs[12] = (newTm.tm_wday - dowGap) % 7;
|
2020-02-16 21:11:01 -05:00
|
|
|
|
|
|
|
_lastTime = currentTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Rtc4513::Read(uint16_t addr)
|
|
|
|
{
|
|
|
|
UpdateTime();
|
|
|
|
|
|
|
|
switch(addr) {
|
|
|
|
case 0x4840: break;
|
|
|
|
|
|
|
|
case 0x4841:
|
|
|
|
if(_mode == 0x0C) {
|
|
|
|
//Read mode
|
|
|
|
//LogDebug("Read: " + HexUtilities::ToHex(_index) + " = " + HexUtilities::ToHex(_regs[_index]));
|
|
|
|
uint8_t index = _index;
|
|
|
|
_index = (_index + 1) & 0x0F;
|
|
|
|
return _regs[index];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4842:
|
|
|
|
//Ready
|
|
|
|
return 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Rtc4513::Write(uint16_t addr, uint8_t value)
|
|
|
|
{
|
|
|
|
UpdateTime();
|
|
|
|
|
|
|
|
switch(addr) {
|
|
|
|
case 0x4840:
|
|
|
|
_enabled = value;
|
|
|
|
if(!(_enabled & 0x01)) {
|
|
|
|
_mode = -1;
|
|
|
|
_index = -1;
|
|
|
|
|
|
|
|
//Turn off reset ($01) and test ($08) bits when disabled
|
|
|
|
_regs[0x0F] &= 0x06;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4841:
|
|
|
|
if(_mode == -1) {
|
|
|
|
_mode = value & 0x0F;
|
|
|
|
} else if(_index == -1) {
|
|
|
|
_index = value & 0x0F;
|
|
|
|
} else if(_mode == 0x03) {
|
|
|
|
//Write mode
|
|
|
|
//LogDebug(HexUtilities::ToHex(_index) + " = " + HexUtilities::ToHex(value & 0x0F));
|
|
|
|
uint8_t index = _index;
|
|
|
|
_index = (_index + 1) & 0x0F;
|
|
|
|
_regs[index] = value & 0x0F;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x4842: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Rtc4513::Serialize(Serializer& s)
|
|
|
|
{
|
|
|
|
ArrayInfo<uint8_t> regs = { _regs, 0x10 };
|
|
|
|
s.Stream(_lastTime, _enabled, _mode, _index, regs);
|
|
|
|
}
|