pub const CPUID = extern struct {
    eax: u32,
    ebx: u32,
    edx: u32,
    ecx: u32,
};

pub inline fn cpuid(leaf: u32, subleaf: u32) CPUID {
    var eax: u32 = undefined;
    var ebx: u32 = undefined;
    var edx: u32 = undefined;
    var ecx: u32 = undefined;

    asm volatile (
        \\cpuid
        : [eax] "={eax}" (eax),
          [ebx] "={ebx}" (ebx),
          [edx] "={edx}" (edx),
          [ecx] "={ecx}" (ecx),
        : [leaf] "{eax}" (leaf),
          [subleaf] "{ecx}" (subleaf),
        : "memory"
    );

    return CPUID{
        .eax = eax,
        .ebx = ebx,
        .edx = edx,
        .ecx = ecx,
    };
}

pub const Spinlock = enum(u8) {
    released = 0,
    acquired = 1,

    pub inline fn acquire(spinlock: *volatile Spinlock) void {
        asm volatile (
            \\0:
            \\xchgb %[value], %[spinlock]
            \\test %[value], %[value]
            \\jz 2f
            // If not acquire, go to spinloop
            \\1:
            \\pause
            \\cmp %[value], %[spinlock]
            // Retry
            \\jne 0b
            \\jmp 1b
            \\2:
            :
            : [spinlock] "*p" (spinlock),
              [value] "r" (Spinlock.acquired),
            : "memory"
        );
    }

    pub inline fn release(spinlock: *volatile Spinlock) void {
        @atomicStore(Spinlock, spinlock, .released, .Release);
    }
};
