mirror of
https://github.com/StrikerX3/StrikeBox.git
synced 2024-06-23 14:53:22 -04:00
Implemented enough of the CMOS to make the kernel happy
This commit is contained in:
parent
d94bdf2225
commit
25490129f2
|
@ -5,13 +5,42 @@
|
|||
|
||||
namespace vixen {
|
||||
|
||||
static inline bool IsRTCRegister(uint8_t reg) {
|
||||
switch (reg) {
|
||||
case RTCSeconds:
|
||||
case RTCMinutes:
|
||||
case RTCHours:
|
||||
case RTCDayOfWeek:
|
||||
case RTCDayOfMonth:
|
||||
case RTCMonth:
|
||||
case RTCYear:
|
||||
case RTCCentury:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CMOS::CMOS() {
|
||||
// TODO: Are IRQs needed?
|
||||
}
|
||||
|
||||
CMOS::~CMOS() {
|
||||
}
|
||||
|
||||
void CMOS::Reset() {
|
||||
// The Xbox kernel checks that the CMOS user memory has an specific pattern
|
||||
|
||||
for (int i = 0x10; i < 0x70; i++) {
|
||||
m_memory[i] = 0x55 << (i & 1);
|
||||
}
|
||||
|
||||
for (int i = 0x80; i < 0x100; i++) {
|
||||
m_memory[i] = 0x55 << (i & 1);
|
||||
}
|
||||
|
||||
// Initialize virtual RTC offset to match host RTC
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
bool CMOS::MapIO(IOMapper *mapper) {
|
||||
|
@ -21,16 +50,146 @@ bool CMOS::MapIO(IOMapper *mapper) {
|
|||
}
|
||||
|
||||
bool CMOS::IORead(uint32_t port, uint32_t *value, uint8_t size) {
|
||||
log_warning("CMOS::IORead: Unhandled read! port = 0x%x, size = %u\n", port, size);
|
||||
|
||||
*value = 0;
|
||||
if (size != 1) {
|
||||
log_warning("CMOS::IORead: Unexpected read of size %d; truncating to 1 byte", size);
|
||||
size = 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
switch (port - PORT_CMOS_BASE) {
|
||||
case PORT_CMOS_DATA:
|
||||
if (IsRTCRegister(m_regAddr)) {
|
||||
ReadRTC(m_regAddr, reinterpret_cast<uint8_t *>(value));
|
||||
}
|
||||
else {
|
||||
switch (m_regAddr) {
|
||||
case RegC: *value = m_memory[m_regAddr] & 0b11110000; break;
|
||||
case RegD: *value = m_memory[m_regAddr] & 0b10000000; break;
|
||||
default: *value = m_memory[m_regAddr]; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_warning("CMOS::IORead: Unexpected read! port = 0x%x\n", port);
|
||||
*value = 0;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMOS::IOWrite(uint32_t port, uint32_t value, uint8_t size) {
|
||||
log_warning("CMOS::IOWrite: Unhandled write! port = 0x%x, size = %u, value = 0x%x\n", port, size, value);
|
||||
return false;
|
||||
if (size != 1) {
|
||||
log_warning("CMOS::IOWrite: Unexpected write of size %d; truncating to 1 byte", size);
|
||||
size = 1;
|
||||
}
|
||||
|
||||
switch (port - PORT_CMOS_BASE) {
|
||||
case PORT_CMOS_CONTROL:
|
||||
m_regAddr = value & 0x7f;
|
||||
break;
|
||||
case PORT_CMOS_DATA:
|
||||
if (IsRTCRegister(m_regAddr)) {
|
||||
WriteRTC(m_regAddr, (uint8_t)value);
|
||||
}
|
||||
else {
|
||||
switch (m_regAddr) {
|
||||
case RegA:
|
||||
case RegB:
|
||||
case RegC:
|
||||
case RegD:
|
||||
log_debug("handle this\n");
|
||||
break;
|
||||
}
|
||||
m_memory[m_regAddr] = value;
|
||||
}
|
||||
break;
|
||||
case PORT_CMOS_EXT_CONTROL:
|
||||
m_regAddr = value;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMOS::ReadRTC(uint8_t reg, uint8_t *value) {
|
||||
// Get current host RTC and apply offset
|
||||
auto now = std::chrono::system_clock::now() + std::chrono::seconds(m_offset);
|
||||
time_t tt = std::chrono::system_clock::to_time_t(now);
|
||||
tm local = *localtime(&tt);
|
||||
|
||||
// Return requested field
|
||||
switch (reg) {
|
||||
case RTCSeconds: *value = ToBCD(local.tm_sec); break;
|
||||
case RTCMinutes: *value = ToBCD(local.tm_min); break;
|
||||
case RTCHours: *value = ToBCDHour(local.tm_hour); break;
|
||||
case RTCDayOfWeek: *value = local.tm_wday + 1; break;
|
||||
case RTCDayOfMonth: *value = ToBCD(local.tm_mday); break;
|
||||
case RTCMonth: *value = ToBCD(local.tm_mon) + 1; break;
|
||||
case RTCYear: *value = ToBCD(local.tm_year % 100); break;
|
||||
case RTCCentury: *value = ToBCD((local.tm_year + 1900) / 100); break;
|
||||
default: log_warning("CMOS::ReadRTC: Unexpected register %d\n", reg); *value = 0; break;
|
||||
}
|
||||
}
|
||||
|
||||
void CMOS::WriteRTC(uint8_t reg, uint8_t value) {
|
||||
// Get current host RTC and apply offset
|
||||
auto now = std::chrono::system_clock::now() + std::chrono::seconds(m_offset);
|
||||
time_t tt = std::chrono::system_clock::to_time_t(now);
|
||||
tm local = *localtime(&tt);
|
||||
tm new_local = local;
|
||||
|
||||
// Update requested field
|
||||
switch (reg) {
|
||||
case RTCSeconds: new_local.tm_sec = FromBCD(value); break;
|
||||
case RTCMinutes: new_local.tm_min = FromBCD(value); break;
|
||||
case RTCHours: new_local.tm_hour = FromBCDHour(value); break;
|
||||
case RTCDayOfWeek: new_local.tm_wday = FromBCD(value) - 1; break;
|
||||
case RTCDayOfMonth: new_local.tm_mday = FromBCD(value); break;
|
||||
case RTCMonth: new_local.tm_mon = FromBCD(value) - 1; break;
|
||||
case RTCYear: new_local.tm_year = (local.tm_year / 100) * 100 + FromBCD(value); break;
|
||||
case RTCCentury: new_local.tm_year = local.tm_year % 100 + (FromBCD(value) * 100 - 1900); break;
|
||||
default: log_warning("CMOS::WriteRTC: Unexpected register %d\n", reg); break;
|
||||
}
|
||||
|
||||
// Recalculate time offset
|
||||
time_t new_tt = mktime(&new_local);
|
||||
m_offset = difftime(tt, new_tt);
|
||||
}
|
||||
|
||||
uint8_t CMOS::ToBCD(uint8_t value) {
|
||||
if (m_memory[RegB] & RegB_DM) {
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
return ((value / 10) << 4) | (value % 10);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CMOS::FromBCD(uint8_t value) {
|
||||
if (m_memory[RegB] & RegB_DM) {
|
||||
return value;
|
||||
}
|
||||
return ((value >> 4) * 10) + (value & 0xf);
|
||||
}
|
||||
|
||||
uint8_t CMOS::ToBCDHour(uint8_t hour) {
|
||||
if (m_memory[RegB] & RegB_24_12) {
|
||||
return hour;
|
||||
}
|
||||
|
||||
// Convert 00 -> 12
|
||||
uint8_t newHour = (hour % 12) ? (hour % 12) : 12;
|
||||
if (hour >= 12) {
|
||||
newHour |= 0x80;
|
||||
}
|
||||
return newHour;
|
||||
}
|
||||
|
||||
uint8_t CMOS::FromBCDHour(uint8_t hour) {
|
||||
if (m_memory[RegB] & RegB_24_12) {
|
||||
return hour & 0x7f;
|
||||
}
|
||||
|
||||
uint8_t newHour = FromBCD(hour & 0x7f);
|
||||
return (hour % 12) + ((hour & 0x80) ? 12 : 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
|
||||
#include "vixen/cpu.h"
|
||||
|
||||
namespace vixen {
|
||||
|
||||
#define PORT_CMOS_CONTROL 0x70
|
||||
#define PORT_CMOS_DATA 0x71
|
||||
#define PORT_CMOS_EXT_CONTROL 0x72
|
||||
#define CMOS_IRQ 8
|
||||
|
||||
#define PORT_CMOS_BASE PORT_CMOS_CONTROL
|
||||
#define PORT_CMOS_COUNT (PORT_CMOS_EXT_CONTROL - PORT_CMOS_CONTROL + 1)
|
||||
#define PORT_CMOS_BASE 0x70
|
||||
#define PORT_CMOS_COUNT 3
|
||||
|
||||
#define PORT_CMOS_CONTROL 0
|
||||
#define PORT_CMOS_DATA 1
|
||||
#define PORT_CMOS_EXT_CONTROL 2
|
||||
|
||||
enum CMOSRegister {
|
||||
RTCSeconds = 0x0,
|
||||
RTCSecondsAlarm = 0x1,
|
||||
RTCMinutes = 0x2,
|
||||
RTCMinutesAlarm = 0x3,
|
||||
RTCHours = 0x4,
|
||||
RTCHoursAlarm = 0x5,
|
||||
|
||||
RTCDayOfWeek = 0x6,
|
||||
RTCDayOfMonth = 0x7,
|
||||
RTCMonth = 0x8,
|
||||
RTCYear = 0x9,
|
||||
RTCCentury = 0x7f, // Xbox only
|
||||
|
||||
RegA = 0xa,
|
||||
RegB = 0xb,
|
||||
RegC = 0xc,
|
||||
RegD = 0xd,
|
||||
};
|
||||
|
||||
enum CMOSRegisterBit {
|
||||
RegA_UIP = (1 << 7), // (UIP) Update in progress
|
||||
|
||||
RegB_SET = (1 << 7), // (SET)
|
||||
RegB_PIE = (1 << 6), // (PIE) Periodic interrupt enable
|
||||
RegB_AIE = (1 << 5), // (AIE) Alarm interrupt enable
|
||||
RegB_UIE = (1 << 4), // (UIE) Update-ended interrupt enable
|
||||
RegB_SQWE = (1 << 3), // (SQWE) Square wave enable
|
||||
RegB_DM = (1 << 2), // (DM) Data mode
|
||||
RegB_24_12 = (1 << 1), // (24/12) Format output to: 1 = 24-hour format, 2 = 12-hour
|
||||
RegB_DSE = (1 << 0), // (DSE) Daylight savings enable
|
||||
|
||||
RegC_IRQF = (1 << 7), // (IRQF) Interrupt request flag
|
||||
RegC_PF = (1 << 6), // (PF) Periodic interrupt flag
|
||||
RegC_AF = (1 << 5), // (AF) Alarm flag
|
||||
RegC_UF = (1 << 4), // (AF) Update-ended interrupt flag
|
||||
};
|
||||
|
||||
class CMOS : public IODevice {
|
||||
public:
|
||||
|
@ -24,6 +65,23 @@ public:
|
|||
bool IORead(uint32_t port, uint32_t *value, uint8_t size) override;
|
||||
bool IOWrite(uint32_t port, uint32_t value, uint8_t size) override;
|
||||
private:
|
||||
// Selected register address
|
||||
uint8_t m_regAddr;
|
||||
|
||||
// CMOS memory
|
||||
uint8_t m_memory[0x100];
|
||||
|
||||
// Offset from host RTC (in seconds), used to virtualize the RTC
|
||||
int64_t m_offset;
|
||||
|
||||
void ReadRTC(uint8_t reg, uint8_t *value);
|
||||
void WriteRTC(uint8_t reg, uint8_t value);
|
||||
|
||||
uint8_t ToBCD(uint8_t value);
|
||||
uint8_t FromBCD(uint8_t value);
|
||||
|
||||
uint8_t ToBCDHour(uint8_t hour);
|
||||
uint8_t FromBCDHour(uint8_t hour);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue