#include "stdafx.h" #include #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 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::system_clock::now().time_since_epoch()).count(); } } void Rtc4513::SaveBattery() { vector 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::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 regs = { _regs, 0x10 }; s.Stream(_lastTime, _enabled, _mode, _index, regs); }