Mesen-S/Core/Rtc4513.cpp
2020-02-22 12:11:51 -05:00

193 lines
4.3 KiB
C++

#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);
if(tt == -1 || GetMonth() == 0) {
_lastTime = currentTime;
return;
}
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();
}
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;
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
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;
_regs[12] = (newTm.tm_wday - dowGap) % 7;
_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);
}