|
|
nono 1.7.0
//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//
//
// Goldfish (Qemu) RTC+Timer (の RTC のほう)
//
// RTC デバイスのレジスタは次の通り。(QEMU 仕様)
//
// +$00.L TIME_LOW RW: ホスト時刻 [nsec] の下位 32 ビット
// +$04.L TIME_HIGH RW: ホスト時刻 [nsec] の上位 32 ビット
//
// TIME_LOW の動作は Timer デバイスと同じだが、こちらはホストの RTC 時刻
// (実世界時間) を秒の粒度で返す。つまり time() * 1e9 だと仕様書にはある。
// 書き込みも可能 (当然 VM の RTC を書き換える)。
// アラームと割り込み機能は一切ないが、レジスタマップは Timer デバイスと
// 同じものを使うらしい。
#include "goldfish_rtc.h"
#include "event.h"
#include "mainbus.h"
#include "mytime.h"
#include "textscreen.h"
//
// GFRTC
//
// コンストラクタ
GFRTCDevice::GFRTCDevice()
: inherited()
{
}
// デストラクタ
GFRTCDevice::~GFRTCDevice()
{
}
// リセット
void
GFRTCDevice::ResetHard(bool poweron)
{
time_nsec.q = 0;
}
busdata
GFRTCDevice::ReadPort(uint32 offset)
{
busdata data;
switch (offset) {
case GFRTC::TIME_LOW:
time_nsec.q = sec_to_nsec(MkTime());
data = time_nsec.l;
if (__predict_false(loglevel >= 1)) {
if (loglevel >= 3) {
putlogn("TIME_LOW -> $%08x", data.Data());
} else {
putlogn("TIME -> $%08x'%08x", time_nsec.h, data.Data());
}
if (loglevel >= 2) {
struct tm tm, tr;
time_t t = (time_t)nsec_to_sec(time_nsec.q);
gmtime_r(&t, &tm);
putlogn("TIME / 1e9: %4u/%02u/%02u %02u:%02u:%02u",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec);
time_t u = (time_t)get_hosttime_sec();
gmtime_r(&u, &tr);
putlogn("system_clock: %4u/%02u/%02u %02u:%02u:%02u",
tr.tm_year + 1900,
tr.tm_mon + 1,
tr.tm_mday,
tr.tm_hour,
tr.tm_min,
tr.tm_sec);
}
}
break;
case GFRTC::TIME_HIGH:
data = time_nsec.h;
putlog(3, "TIME_HIGH -> $%08x", data.Data());
break;
case GFRTC::INTR_STATUS:
putlog(2, "Read INTR_STATUS (No function)");
data = 0;
break;
case GFRTC::ALARM_STATUS:
putlog(2, "Read ALARM_STATUS (No function)");
data = 0;
break;
default:
putlog(1, "Read unknown $%08x", GetMainbusDevice()->GetPaddr());
data.SetBusErr();
break;
}
data |= BusData::Size4;
return data;
}
busdata
GFRTCDevice::WritePort(uint32 offset, uint32 data)
{
busdata r;
switch (offset) {
case GFRTC::TIME_LOW:
{
time_nsec.l = data;
if (__predict_false(loglevel >= 1)) {
if (loglevel >= 2) {
putlogn("TIME_LOW <- $%08x", time_nsec.l);
} else {
putlogn("TIME <- $%08x'%08x", time_nsec.h, time_nsec.l);
}
}
time_t t = nsec_to_sec(time_nsec.q);
struct tm tm;
gmtime_r(&t, &tm);
SetYear(tm.tm_year + 1900);
SetMon(tm.tm_mon + 1);
SetMday(tm.tm_mday);
SetHour(tm.tm_hour);
SetMin(tm.tm_min);
SetSec(tm.tm_sec);
break;
}
case GFRTC::TIME_HIGH:
putlog(2, "TIME_HIGH <- $%08x", data);
time_nsec.h = data;
break;
case GFRTC::ALARM_LOW:
putlog(2, "ALARM_LOW <- $%08x (No function)", data);
break;
case GFRTC::ALARM_HIGH:
putlog(2, "ALARM_HIGH <- $%08x (No function)", data);
break;
case GFRTC::INTR_STATUS:
putlog(2, "INTR_STATUS <- $%08x (No function)", data);
break;
case GFRTC::ALARM_CLEAR:
putlog(2, "ALARM_CLEAR <- $%08x (No function)", data);
break;
case GFRTC::INTR_CLEAR:
putlog(2, "INTR_CLEAR <- $%08x (No function)", data);
break;
default:
putlog(1, "Write unknown $%08x <- $%08x",
GetMainbusDevice()->GetPaddr(), data);
r.SetBusErr();
break;
}
r |= BusData::Size4;
return r;
}
busdata
GFRTCDevice::PeekPort(uint32 offset)
{
switch (offset) {
case GFRTC::TIME_LOW:
{
uint64 now = sec_to_nsec(MkTime());
return now & 0xffffffffU;
}
case GFRTC::TIME_HIGH:
{
uint64 now = sec_to_nsec(MkTime());
return now >> 32;
}
case GFRTC::INTR_STATUS:
case GFRTC::ALARM_STATUS:
return 0;
default:
return BusData::BusErr;
}
}
// モニタの下請け。(GFTimer から呼ばれる)
int
GFRTCDevice::MonitorScreenRTC(TextScreen& screen, int y) const
{
screen.Puts(0, y++, "<RTC>");
screen.Print(0, y++, "%04u/%02u/%02u(%s) %02u:%02u:%02u",
year, mon, day, wdays[GetWday()], hour, min, sec);
screen.Puts(0, y++, "TimeZone: UTC");
return y;
}
void
GFRTCDevice::Tick1Hz()
{
CountUpSec();
}
// 内部時刻から time_t を作成する。
time_t
GFRTCDevice::MkTime() const
{
struct tm tm;
tm.tm_year = year - 1900;
tm.tm_mon = mon - 1;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
return timegm(&tm);
}
uint
GFRTCDevice::GetWday() const
{
// 曜日は保持していないので一旦日付を作ってから求める。
struct tm tm;
time_t t = MkTime();
gmtime_r(&t, &tm);
return tm.tm_wday;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.