using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; using XFESystem.Platform; using XFESystem.NativeKernel; namespace XFESystem.NativeRuntime.Application; /// Exact 56-byte value-only ABI provided by XFESystem.NativeKernel.RuntimeBridge. [StructLayout(LayoutKind.Sequential, Size = 56)] internal struct NativeFramebufferInfo { public ulong Address, MappedBytes; public uint Width, Height, PitchBytes, BitsPerPixel; public uint RedMaskSize, RedMaskShift, GreenMaskSize, GreenMaskShift, BlueMaskSize, BlueMaskShift; internal readonly FramebufferSurface ToSurface() => new(checked((nuint)Address), checked((nuint)MappedBytes), checked((int)Width), checked((int)Height), checked((int)PitchBytes), checked((int)BitsPerPixel), Mask(RedMaskSize, RedMaskShift), Mask(GreenMaskSize, GreenMaskShift), Mask(BlueMaskSize, BlueMaskShift)); private static uint Mask(uint size, uint shift) { if (size is 0 or > 32 || shift >= 32 || size + shift > 32) throw new InvalidDataException("Native framebuffer channel mask is invalid."); return (uint)((size == 32 ? uint.MaxValue : (1UL << (int)size) - 1) << (int)shift); } } internal static unsafe class NativePlatform { private static readonly object outputGate = new(); // Direct PInvoke during ELF compilation; no dynamic loader, strings or object marshalling crosses this ABI. private const string Library = "xfe_pal"; [DllImport(Library, EntryPoint = "xfe_runtime_framebuffer_info", CallingConvention = CallingConvention.Cdecl)] private static extern int FramebufferInfo(NativeFramebufferInfo* information); [DllImport(Library, EntryPoint = "xfe_runtime_write", CallingConvention = CallingConvention.Cdecl)] private static extern void WriteNative(byte* bytes, ulong length); [DllImport(Library, EntryPoint = "xfe_runtime_mono_ns", CallingConvention = CallingConvention.Cdecl)] private static extern ulong MonotonicNanoseconds(); [DllImport(Library, EntryPoint = "xfe_runtime_clock_ready", CallingConvention = CallingConvention.Cdecl)] private static extern int ClockReady(); [DllImport(Library, EntryPoint = "xfe_runtime_halt", CallingConvention = CallingConvention.Cdecl)] private static extern void HaltNative(byte* zeroTerminatedReason); [DllImport(Library, EntryPoint = "xfe_runtime_input_ready", CallingConvention = CallingConvention.Cdecl)] private static extern uint InputReady(); [DllImport(Library, EntryPoint = "xfe_runtime_input_read", CallingConvention = CallingConvention.Cdecl)] private static extern int InputRead(NativeInputReport* report); internal static uint InputCapabilities() => InputReady() & 7; internal static bool ReadInput(out NativeInputReport report) { if (sizeof(NativeInputReport) != 32) throw new InvalidOperationException("Native input ABI size mismatch."); NativeInputReport value = default; int result = InputRead(&value); if (result is not (0 or 1)) throw new InvalidOperationException("Native input read failed its ABI contract."); report = value; return result == 1; } internal static FramebufferSurface ReadFramebuffer() { if (sizeof(NativeFramebufferInfo) != 56) throw new InvalidOperationException("Native framebuffer ABI size mismatch."); NativeFramebufferInfo information = default; if (FramebufferInfo(&information) != 1) throw new InvalidOperationException("The independent kernel did not supply a mapped framebuffer."); return information.ToSurface(); } internal static void RequireClock() { if (ClockReady() != 1) throw new InvalidOperationException("The independent kernel's monotonic clock is unavailable."); } internal static long Nanoseconds() => (long)Math.Min(MonotonicNanoseconds(), (ulong)long.MaxValue); internal static long Milliseconds() => (long)(MonotonicNanoseconds() / 1_000_000); internal static void Write(string text) { byte[] bytes = Encoding.UTF8.GetBytes(text); // Background document/editor diagnostics must stay complete records // when the independently scheduled managed threads are preempted. lock (outputGate) fixed (byte* pointer = bytes) WriteNative(pointer, (ulong)bytes.Length); } [DoesNotReturn] internal static void Halt(string reason) { byte[] encoded = Encoding.UTF8.GetBytes(reason); byte[] bounded = new byte[Math.Min(encoded.Length, 255) + 1]; encoded.AsSpan(0, bounded.Length - 1).CopyTo(bounded); fixed (byte* pointer = bounded) HaltNative(pointer); // A broken native contract must never let Main continue or return into an uninitialised scheduler. for (;;) { } } }