XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Runtime.InteropServices;
namespace XFESystem.NativeKernel;

// Live CMOS RTC, not a compile timestamp or host clock. The virtual-machine
// launch must configure RTC base=utc. Physical firmware RTC timezone conventions
// require separate validation; CMOS itself contains no trustworthy timezone.
public static unsafe class UtcClock
{
    const uint PollLimit = 100000;
    static byte ReadRegister(byte index, byte nmiMask)
    {
        Native.Out8(0x70, (byte)(nmiMask | index));
        return Native.In8(0x71);
    }
    static bool Sample(byte nmiMask, out ulong value)
    {
        value = 0;
        bool stable = false;
        for (uint poll = 0; poll < PollLimit; poll++)
        {
            byte status = ReadRegister(0x0a, nmiMask);
            if (status == 255) return false;
            if ((status & 0x80) == 0) { stable = true; break; }
            Native.Pause();
        }
        if (!stable || (ReadRegister(0x0d, nmiMask) & 0x80) == 0) return false;
        value = ReadRegister(0, nmiMask);
        value |= (ulong)ReadRegister(2, nmiMask) << 8;
        value |= (ulong)ReadRegister(4, nmiMask) << 16;
        value |= (ulong)ReadRegister(7, nmiMask) << 24;
        value |= (ulong)ReadRegister(8, nmiMask) << 32;
        value |= (ulong)ReadRegister(9, nmiMask) << 40;
        value |= (ulong)ReadRegister(0x32, nmiMask) << 48; // Conventional PC century; invalid/unset values use documented pivot.
        value |= (ulong)ReadRegister(0x0b, nmiMask) << 56;
        return (ReadRegister(0x0a, nmiMask) & 0x80) == 0;
    }
    static uint Decode(byte value, bool binary)
    {
        if (binary) return value;
        if ((value & 15) > 9 || (value >> 4) > 9) return 0xffffffff;
        return (uint)((value >> 4) * 10 + (value & 15));
    }
    static bool Leap(uint year) => year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
    static uint DaysInMonth(uint year, uint month)
    {
        if (month == 2) return Leap(year) ? 29u : 28u;
        if (month == 4 || month == 6 || month == 9 || month == 11) return 30;
        return 31;
    }
    static ulong DaysBeforeYear(uint year)
    {
        ulong y = year - 1;
        return 365 * y + y / 4 - y / 100 + y / 400;
    }
    static ulong Convert(ulong value)
    {
        byte status = (byte)(value >> 56);
        if (status == 255 || (status & 0x80) != 0) return 0; // SET inhibits clock updates; never label a frozen RTC as live.
        bool binary = (status & 4) != 0, twentyFour = (status & 2) != 0;
        uint second = Decode((byte)value, binary), minute = Decode((byte)(value >> 8), binary);
        byte rawHour = (byte)(value >> 16);
        uint hour = Decode((byte)(rawHour & 0x7f), binary);
        if (!twentyFour)
        {
            if (hour < 1 || hour > 12) return 0;
            hour = hour % 12 + ((rawHour & 0x80) != 0 ? 12u : 0u);
        }
        else if ((rawHour & 0x80) != 0) return 0;
        uint day = Decode((byte)(value >> 24), binary), month = Decode((byte)(value >> 32), binary);
        uint shortYear = Decode((byte)(value >> 40), binary), century = Decode((byte)(value >> 48), binary);
        if (second > 59 || minute > 59 || hour > 23 || month < 1 || month > 12 || shortYear > 99) return 0;
        uint year = century >= 19 && century <= 99 ? century * 100 + shortYear : shortYear < 70 ? 2000 + shortYear : 1900 + shortYear;
        if (year < 1601 || year > 9999 || day < 1 || day > DaysInMonth(year, month)) return 0;
        ulong days = DaysBeforeYear(year) - DaysBeforeYear(1601);
        for (uint m = 1; m < month; m++) days += DaysInMonth(year, m);
        days += day - 1;
        return (days * 86400 + hour * 3600UL + minute * 60UL + second) * 10000000;
    }
    public static ulong ReadFileTime()
    {
        ulong flags = Native.IrqSave();
        byte originalSelector = Native.In8(0x70), nmiMask = (byte)(originalSelector & 0x80);
        ulong result = 0;
        for (uint retry = 0; retry < 8; retry++)
            if (Sample(nmiMask, out ulong first) && Sample(nmiMask, out ulong second) && first == second)
            { result = Convert(second); break; }
        Native.Out8(0x70, originalSelector);
        Native.IrqRestore(flags);
        return result;
    }
    [UnmanagedCallersOnly(EntryPoint = "xfe_runtime_utc_filetime")]
    public static ulong FileTime() => ReadFileTime();
}