#include "cpu/CPU.h"
#include <cpuid.h>
#include <string.h>

const char* CPU::get_vendor_string()
{
    static bool cached = false;
    static char vendor[13];
    if (cached) { return vendor; }
    else
    {
        unsigned int unused, ebx, ecx, edx;
        __get_cpuid(0, &unused, &ebx, &ecx, &edx);
        memcpy(vendor, &ebx, 4);
        memcpy(&vendor[4], &edx, 4);
        memcpy(&vendor[8], &ecx, 4);
        vendor[12] = 0;
        cached = true;
        return vendor;
    }
}

const char* CPU::get_brand_string()
{
    static bool cached = false;
    static char brand[49];
    if (cached) { return brand; }
    else
    {
        unsigned int eax, ebx, ecx, edx;
        __get_cpuid(0x80000002, &eax, &ebx, &ecx, &edx);
        memcpy(brand, &eax, 4);
        memcpy(&brand[4], &ebx, 4);
        memcpy(&brand[8], &ecx, 4);
        memcpy(&brand[12], &edx, 4);
        __get_cpuid(0x80000003, &eax, &ebx, &ecx, &edx);
        memcpy(&brand[16], &eax, 4);
        memcpy(&brand[16 + 4], &ebx, 4);
        memcpy(&brand[16 + 8], &ecx, 4);
        memcpy(&brand[16 + 12], &edx, 4);
        __get_cpuid(0x80000004, &eax, &ebx, &ecx, &edx);
        memcpy(&brand[32], &eax, 4);
        memcpy(&brand[32 + 4], &ebx, 4);
        memcpy(&brand[32 + 8], &ecx, 4);
        memcpy(&brand[32 + 12], &edx, 4);
        brand[48] = 0;
        cached = true;
        return brand;
    }
}

uint64_t CPU::get_feature_bitmask()
{
    static uint64_t bitmask = 0;
    static bool cached = false;
    if (cached) return bitmask;
    unsigned int unused;
    unsigned int ecx = 0;
    unsigned int edx = 0;
    __get_cpuid(1, &unused, &unused, &ecx, &edx);
    bitmask = ((uint64_t)ecx << 32) | (uint64_t)edx;
    cached = true;
    return bitmask;
}

static bool _has_feature(int feature)
{
    return (CPU::get_feature_bitmask() & (uint64_t)(1 << feature)) > 0;
}

bool CPU::has_feature(CPU::Features feature)
{
    return _has_feature((int)feature);
}

uint64_t CPU::get_initial_apic_id()
{
    unsigned int unused;
    unsigned int ebx = 0;
    __get_cpuid(1, &unused, &ebx, &unused, &unused);
    return ebx >> 24;
}