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;

// 8042 hardware port and bounded queue. Decoder is the same production source
// linked into the host regressions, with no injected event path in this driver.
public static unsafe class Ps2Input
{
    const uint Capacity = 256, PollLimit = 100000;
    static NativeInputReport* _reports;
    static uint _head, _tail, _lost;
    static Ps2DecodeState _decoder;
    static uint _buttons;
    public static uint ReadyBits;
    public static ulong HardwareBytes, QueuedReports;
    static bool Write(ushort port, byte value)
    {
        for (uint poll = 0; poll < PollLimit; poll++)
        {
            if ((Native.In8(0x64) & 2) == 0) { Native.Out8(port, value); return true; }
            Native.Pause();
        }
        return false;
    }
    static bool Read(out byte value, bool mouse, uint limit = PollLimit)
    {
        value = 0;
        ulong deadline = limit > PollLimit && MonotonicClock.Ready ? MonotonicClock.Nanoseconds() + 1000000000 : 0;
        for (uint poll = 0; poll < limit; poll++)
        {
            if (deadline != 0 && MonotonicClock.Nanoseconds() >= deadline) return false;
            byte status = Native.In8(0x64);
            if (status == 255) return false;
            if ((status & 1) != 0)
            {
                byte data = Native.In8(0x60);
                if ((status & 0xc0) != 0) return false;
                if (((status & 32) != 0) == mouse) { value = data; return true; }
                bool otherMouse = (status & 32) != 0;
                if ((ReadyBits & (otherMouse ? 2u : 1u)) != 0) Decode(otherMouse, data);
            }
            Native.Pause();
        }
        return false;
    }
    static bool Device(byte value, bool mouse)
    {
        for (uint attempt = 0; attempt < 3; attempt++)
        {
            if (mouse && !Write(0x64, 0xd4)) return false;
            if (!Write(0x60, value) || !Read(out byte ack, mouse)) return false;
            if (ack == 0xfa) return true;
            if (ack != 0xfe) return false;
        }
        return false;
    }
    static bool ResetMouseDevice()
    {
        // F6 does not reset an IntelliMouse's packet ID. A successful FF BAT
        // and ID0 reply are required before selecting the standard fallback.
        if (!Device(0xff, true) || !Read(out byte bat, true, PollLimit * 1000) || bat != 0xaa ||
            !Read(out byte id, true) || id != 0) return false;
        return Ps2Decoder.SetMouseDeviceId(ref _decoder, id);
    }
    static bool SampleRate(byte rate) => Device(0xf3, true) && Device(rate, true);
    static bool InitializeMouse()
    {
        if (!Write(0x64, 0xa8) || !ResetMouseDevice()) return false;
        bool identified = SampleRate(200) && SampleRate(100) && SampleRate(80) &&
            Device(0xf2, true) && Read(out byte id, true) && Ps2Decoder.SetMouseDeviceId(ref _decoder, id);
        // A failed/unknown reply leaves the hardware protocol uncertain. Never
        // parse a four-byte device as three bytes just by changing software.
        if (!identified && !ResetMouseDevice()) return false;
        if (!Device(0xea, true) || !Device(0xf4, true)) return false;
        ReadyBits |= 2;
        if (_decoder.MouseDeviceId == 3) ReadyBits |= 4;
        return true;
    }
    public static void Initialize()
    {
        // The LAPIC poller must not consume ACK/BAT bytes while the keyboard
        // is already enabled and the mouse is negotiating its packet format.
        ulong flags = Native.IrqSave();
        InitializeDevices();
        Native.IrqRestore(flags);
    }
    static void InitializeDevices()
    {
        _reports = (NativeInputReport*)Native.Storage(10);
        _head = _tail = _lost = 0; ReadyBits = 0;
        _decoder = new Ps2DecodeState();
        if (_reports == null || Native.In8(0x64) == 255) return;
        if (!Write(0x64, 0xad) || !Write(0x64, 0xa7)) return;
        for (uint i = 0; i < 64 && (Native.In8(0x64) & 1) != 0; i++) Native.In8(0x60);
        if (!Write(0x64, 0x20) || !Read(out byte config, false)) return;
        // Translation converts the keyboard's set 2 into set 1; IRQs stay masked
        // because the independent LAPIC tick services both device queues.
        config = (byte)((config | 0x40) & ~0x33);
        if (!Write(0x64, 0x60) || !Write(0x60, config) || !Write(0x64, 0xae)) return;
        if (Device(0xf5, false) && Device(0xf0, false) && Device(2, false) && Device(0xf4, false)) ReadyBits |= 1;
        InitializeMouse();
        Serial.Write("XFE_INPUT_READY keyboard="); Serial.Number((ReadyBits & 1) != 0 ? 1UL : 0UL);
        Serial.Write(" mouse="); Serial.Number((ReadyBits & 2) != 0 ? 1UL : 0UL);
        Serial.Write(" wheel="); Serial.Number((ReadyBits & 4) != 0 ? 1UL : 0UL);
        Serial.Write(" id="); Serial.Number(_decoder.MouseDeviceId); Serial.Line("");
    }
    static void Decode(bool mouse, byte value)
    {
        NativeInputReport report;
        bool complete = mouse ? Ps2Decoder.DecodeMouse(ref _decoder, value, &report) : Ps2Decoder.DecodeKeyboard(ref _decoder, value, &report);
        if (!complete) return;
        if (mouse) _buttons = report.Buttons;
        uint next = (_tail + 1) % Capacity;
        if (next == _head)
        {
            _lost++; _head = _tail = 0;
            Ps2Decoder.ResetKeyboard(ref _decoder); Ps2Decoder.ResetMouse(ref _decoder);
            return;
        }
        _reports[_tail] = report; _tail = next; QueuedReports++;
    }
    public static void Poll()
    {
        if (_reports == null || ReadyBits == 0) return;
        for (uint i = 0; i < 64; i++)
        {
            byte status = Native.In8(0x64);
            if (status == 255 || (status & 1) == 0) return;
            byte value = Native.In8(0x60); HardwareBytes++;
            if ((status & 0xc0) != 0)
            {
                _lost++; _head = _tail = 0;
                Ps2Decoder.ResetKeyboard(ref _decoder); Ps2Decoder.ResetMouse(ref _decoder);
                continue;
            }
            bool mouse = (status & 32) != 0;
            if ((ReadyBits & (mouse ? 2u : 1u)) != 0) Decode(mouse, value);
        }
    }
    [UnmanagedCallersOnly(EntryPoint = "xfe_runtime_input_ready")]
    public static int Ready() => (int)ReadyBits;
    [UnmanagedCallersOnly(EntryPoint = "xfe_runtime_input_read")]
    public static int ReadReport(NativeInputReport* output)
    {
        if (output == null || _reports == null) return 0;
        ulong flags = Native.IrqSave();
        if (_lost != 0)
        {
            *output = new NativeInputReport { Kind = 4, KeyCode = _lost, Buttons = _buttons };
            _lost = 0; Native.IrqRestore(flags); return 1;
        }
        if (_head == _tail) { Native.IrqRestore(flags); return 0; }
        *output = _reports[_head]; _head = (_head + 1) % Capacity;
        Native.IrqRestore(flags); return 1;
    }
}