1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-09 17:29:36 +00:00
CLK/Machines/PCCompatible/RTC.hpp
2024-01-16 23:34:46 -05:00

123 lines
3.3 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// RTC.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/12/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#pragma once
#include <ctime>
namespace PCCompatible {
class RTC {
public:
template <int address>
void write(uint8_t value) {
switch(address) {
default: break;
case 0:
selected_ = value & 0x7f;
// NMI not yet supported.
break;
case 1:
write_register(value);
break;
}
}
uint8_t read() {
std::time_t now = std::time(NULL);
std::tm *time_date = std::localtime(&now);
switch(selected_) {
default:
if(ram_selected()) {
return ram_[ram_address()];
}
return 0xff;
case 0x00: return bcd(time_date->tm_sec); // Seconds [0-59]
case 0x01: return 0; // Seconds alarm
case 0x02: return bcd(time_date->tm_min); // Minutes [0-59]
case 0x03: return 0; // Minutes alarm
case 0x04:
// Hours [1-12 or 0-23]
if(is_24hour()) {
return bcd(time_date->tm_hour);
}
return
((time_date->tm_hour >= 12) ? 0x80 : 0x00) |
bcd(1 + (time_date->tm_hour + 11)%12);
break;
case 0x05: return 0; // Hours alarm
case 0x06: return bcd(time_date->tm_wday + 1); // Day of the week [Sunday = 1]
case 0x07: return bcd(time_date->tm_mday); // Date of the Month [1-31]
case 0x08: return bcd(time_date->tm_mon + 1); // Month [1-12]
case 0x09: return bcd(time_date->tm_year % 100); // Year [0-99]
case 0x32: return bcd(19 + time_date->tm_year / 100); // Century
case 0x0a: return statusA_ & 0x7f; // Exclude the update-in-progress bit.
case 0x0b: return statusB_;
}
}
private:
std::size_t selected_;
std::array<uint8_t, 50> ram_{};
uint8_t statusA_ = 0x00;
uint8_t statusB_ = 0x02;
// Status A.
// b7: update-in-progress.
// b6b4: selects condition of the divider chain (?);
// b3b0: selects rate of the divider chain.
// Status B.
bool disable_updates() const { return statusB_ & 0x80; }
bool periodic_interrupt_enabled() const { return statusB_ & 0x40; }
bool alarm_interrupt_enabled() const { return statusB_ & 0x20; }
bool update_ended_interrupt_enabled() const { return statusB_ & 0x10; }
bool square_wave_enabled() const { return statusB_ & 0x08; }
bool is_decimal() const { return statusB_ & 0x04; }
bool is_24hour() const { return statusB_ & 0x02; }
bool daylight_savings_enabled() const { return statusB_ & 0x01; }
// Helpers for differentiating RAM accesses from the more meaningful registers.
bool ram_selected() const { return selected_ >= 0xe && selected_ < 0xe + ram_.size(); }
std::size_t ram_address() const { return selected_ - 0xe; }
/// Converts @c input to BCD if BCD mode is enabled; otherwise returns @c input unaltered.
template <typename IntT>
uint8_t bcd(IntT input) {
// If calendar is in binary format, don't convert.
if(is_decimal()) {
return uint8_t(input);
}
// Convert a one or two digit number to BCD.
return uint8_t(
(input % 10) +
((input / 10) * 16)
);
}
/// Writes @c value to the register @c selected_ .
void write_register(uint8_t value) {
switch(selected_) {
default:
if(ram_selected()) {
ram_[ram_address()] = value;
}
break;
case 0x0a: statusA_ = value; break;
case 0x0b: statusB_ = value; break;
}
}
};
}