[VC] CPUIDFIELD:CPUID字段的統一編號、讀取方案。范例:檢查SSE4A、AES、PCLMULQDQ指令


除了基本的MMX和SSE系列指令集外,x86體系還有其他擴展指令集,例如SSE4A、AES、PCLMULQDQ等,它們也可以利用CPUID指令來檢測。但是,這些指令集細碎雜多。如果像以前那樣分別編寫檢測函數的話,那工作量太大,不值得。而且大量的函數名也會給使用帶來麻煩。於是文篇探討如何設計一套通用的檢測方案。


零、指令簡介

  SSE4A指令:是AMD提出的,最早出現在2007年的K10微架構的處理器上。它針對Intel的SSE4指令集修改而來,去除其中對I64優化的指令,保留圖形、影音編碼、3D運算、游戲等多媒體指令,並完全兼容。
  AES指令:是Intel提出的,最早出現在2010年的Westmere微架構的 Core i7/i5 處理器上。能提高AES(Advanced Encryption Standard,高級加密標准)加解密性能。
  PCLMULQDQ指令:是Intel提出的,最早出現在2010年的Westmere微架構處理器上。它是不進位乘法(Carryless multiply)運算,主要用於加解密處理。

  檢查以下CPUID標志位可判斷硬件是否支持——
CPUID(80000001).ECX.SSE4A[bit 6]=1 // 硬件支持SSE4A
CPUID(1).ECX.AES[bit 25]=1 // 硬件支持AES
CPUID(1).ECX.PCLMULQDQ[bit 1]=1 // 硬件支持PCLMULQDQ

  SSE4A、AES、PCLMULQDQ是基於XMM寄存器的,所以在使用前還應該檢查操作系統是否支持SSE指令集。
  對於支持AVX指令集的處理器(2011年的Sandy Bridge微架構),AES、PCLMULQDQ也能使用YMM寄存器。同理,在使用YMM寄存器前,應檢查操作系統是否支持AVX指令集。


一、基本思路

  對於這種情況,最常見的處理辦法是定義一個檢測函數和一堆檢測類型常數。
  例如可以將檢測類型常數順序編號——

#define CHECKTYPE_SSE4A    0
#define CHECKTYPE_AES    1
#define CHECKTYPE_PCLMULQDQ    2
...
BOOL simd_check(int checktype)
{
    switch(checktype)
    {
        case CHECKTYPE_SSE4A:
            檢測SSE4A
            break;
        case CHECKTYPE_AES:
            檢測AES
            break;
        case CHECKTYPE_PCLMULQDQ:
            檢測PCLMULQDQ
            break;
    }
    return FALSE;
}

 


  例如檢測硬件是否支持SSE4A,就調用“simd_check(CHECKTYPE_SSE4A)”。
  在實際使用這些指令時,還應該檢測操作系統是否支持SSE,即這樣做——

if (simd_sse_level(NULL)>0)
{
    if (simd_check(CHECKTYPE_SSE4A))
    {
        使用SSE4A指令
    }
}

 


  有了上述函數后,使用起來的確是方便了一些。但是該方案存在以下缺陷——
1.編碼量大。檢測類型常數沒有規律性,對於每一種檢測類型,都得在switch的case分支中寫一段監測代碼。這些代碼很相似,只是使用的常數不同。
2.擴充不易。萬一以后Intel或AMD又增加基於XMM/YMM的新指令,那么又需要增加常數、修改simd_check函數。
3.功能單一。除了SIMD類指令的位標識外,CPUID還有很多豐富的信息,比如基於通用寄存器的運算指令(CRC32、POPCNT等)、系統標識等字段。對於這么多東西,如果分別編寫不同的檢測函數、定義好幾套常數的話,那么不僅代碼量大,而且用起來不方便。


二、CPUIDFIELD編號方案

  CPUID字段數據類型大致可分為4類——
1.位。如是否支持某種指令(MMX、SSE1/2/3/3S/4.1/4.2/4A……),是否具有某種功能(PSE、PAE、APIC……)等。
2.整數。如處理器型號的Model/Family/BrandId信息,物理地址長度等。
3.字符串。如廠商、商標字符串。
4.其他。如CPUID的功能2獲取緩存描述。

  第1類是最常見的,第2類也很多,而第3類、第4類就只有寥寥幾種。
  於是我想,有沒有辦法將第1類和第2類信息進行統一編號。這樣就可以用一個函數獲取CPUID的絕大多數信息了。

  觀察CPUID文檔,發現定位一個字段需要這些參數——
1.功能號:即CPUID指令的EAX參數。常見范圍是0~0Dh、80000000h~8000001Eh。(如AES是1)
2.子功能號:即CPUID指令的ECX參數。大多數時候為0,目前的最大值是62(功能0Dh)。(如AES是0)
3.寄存器:即CPUID指令返回的寄存器。是EAX、EBX、ECX、EDX這4個32位寄存器中的某一個。(如AES是ECX)
4.位偏移:該字段的最低位是32位寄存器中的哪一位。范圍是0~31。(如AES是25)
5.位長:該字段的位長。對於位標識來說,位長總是1(如AES)。而對於整數型(如處理器型號的Model/Family/BrandId信息),范圍是2~32。

  使用一個32位整數來對它們編號——
typedef INT32 CPUIDFIELD;

  分析一下上述參數需要多少位——
1.功能號:理論上需要32位。
2.子功能號:理論上需要32位。但現在一般在0~62的范圍內,即6位。
3.寄存器:4個寄存器,需要2位。
4.位偏移:0~31,需要5位。
5.位長:1~32,需要5位(+1編碼。如0代表1,31代表32)。

  第3、4、5參數的位數已經確定,共2+5+5=12位。
  對於第2個參數,雖然目前只用到6位,但考慮到十六進制的書寫問題與未來發展,定為8位較好。(注:書寫十六進制時,一個字符是4位,8位是兩個4位)。
  現在還剩下12位,可以對第1個參數進行編碼。可以將高4位映射到功能號的高4位,以區分標准功能與擴展功能。然后再將低8位映射到功能號的低8位,以支持各個功能。
  具體的編碼方案為——
bits 31:28:功能號的高4位(bits 31:28)。
bits 27:20:功能號的低8位(bits 7:0)。
bits 19:12:子功能號的低8位(bits 7:0)。
bits 11:10:寄存器編號。0=EAX, 1=EBX, 2=ECX, 3=EDX。
bits 9:5:位長(+1編碼)。
bits 4:0:位偏移。將位偏移放在最低位是為了十六進制的可讀性。因為很多字段的位長為1,編碼為0,這時看十六進制的最低2個字符就知道位偏移是多少。

  在C語言中定義它們的掩碼與位移量——

#define  CPUIDFIELD_MASK_POS    0x0000001F    // 位偏移. 0~31.
#define  CPUIDFIELD_MASK_LEN    0x000003E0    // 位長. 1~32
#define  CPUIDFIELD_MASK_REG    0x00000C00    // 寄存器. 0=EAX, 1=EBX, 2=ECX, 3=EDX.
#define  CPUIDFIELD_MASK_FIDSUB    0x000FF000    // 子功能號(低8位).
#define  CPUIDFIELD_MASK_FID    0xFFF00000    // 功能號(最高4位 和 低8位).

#define CPUIDFIELD_SHIFT_POS    0
#define CPUIDFIELD_SHIFT_LEN    5
#define CPUIDFIELD_SHIFT_REG    10
#define CPUIDFIELD_SHIFT_FIDSUB    12
#define CPUIDFIELD_SHIFT_FID    20

 


  然后再編寫一些宏,用於參數的組成與拆解——

#define CPUIDFIELD_MAKE(fid,fidsub,reg,pos,len)    (((fid)&0xF0000000) \
    | ((fid)<<CPUIDFIELD_SHIFT_FID & 0x0FF00000) \
    | ((fidsub)<<CPUIDFIELD_SHIFT_FIDSUB & CPUIDFIELD_MASK_FIDSUB) \
    | ((reg)<<CPUIDFIELD_SHIFT_REG & CPUIDFIELD_MASK_REG) \
    | ((pos)<<CPUIDFIELD_SHIFT_POS & CPUIDFIELD_MASK_POS) \
    | (((len)-1)<<CPUIDFIELD_SHIFT_LEN & CPUIDFIELD_MASK_LEN) \
    )
#define CPUIDFIELD_FID(cpuidfield)    ( ((cpuidfield)&0xF0000000) | (((cpuidfield) & 0x0FF00000)>>CPUIDFIELD_SHIFT_FID) )
#define CPUIDFIELD_FIDSUB(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_FIDSUB)>>CPUIDFIELD_SHIFT_FIDSUB )
#define CPUIDFIELD_REG(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_REG)>>CPUIDFIELD_SHIFT_REG )
#define CPUIDFIELD_POS(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_POS)>>CPUIDFIELD_SHIFT_POS )
#define CPUIDFIELD_LEN(cpuidfield)    ( (((cpuidfield) & CPUIDFIELD_MASK_LEN)>>CPUIDFIELD_SHIFT_LEN) + 1 )

 

  為了檢查這些宏是否正常工作,在main函數中編寫一些測試代碼——

    //CPUIDFIELD cpuf = CPUIDFIELD_MAKE(0x8000000D,62,0,0,32);
    //printf("0x%.8X\n", cpuf);
    //printf("fid:\t0x%X\n", CPUIDFIELD_FID(cpuf));
    //printf("fidsub:\t0x%X\n", CPUIDFIELD_FIDSUB(cpuf));
    //printf("reg:\t0x%X\n", CPUIDFIELD_REG(cpuf));
    //printf("pos:\t0x%X\n", CPUIDFIELD_POS(cpuf));
    //printf("len:\t0x%X\n", CPUIDFIELD_LEN(cpuf));

 

 

  現在可以為SSE4A、AES、PCLMULQDQ定義常數了——

#define CPUF_SSE4A    CPUIDFIELD_MAKE(0x80000001,0,2,6,1)
#define CPUF_AES    CPUIDFIELD_MAKE(1,0,2,25,1)
#define CPUF_PCLMULQDQ    CPUIDFIELD_MAKE(1,0,2,1,1)

 



三、讀取函數

  有了CPUIDFIELD編號方案后,讀取函數就很容易編寫了。
  雖然可以將代碼全部寫在一個函數中。但是為了提高代碼的可讀性和可復用性,將它分成2個函數與1個宏會更好——

// 取得位域
#ifndef __GETBITS32
#define __GETBITS32(src,pos,len)    ( ((src)>>(pos)) & (((UINT32)-1)>>(32-len)) )
#endif

// 根據CPUIDFIELD從緩沖區中獲取字段.
UINT32    getcpuidfield_buf(const INT32 dwBuf[4], CPUIDFIELD cpuf)
{
    return __GETBITS32(dwBuf[CPUIDFIELD_REG(cpuf)], CPUIDFIELD_POS(cpuf), CPUIDFIELD_LEN(cpuf));
}

// 根據CPUIDFIELD獲取CPUID字段.
UINT32    getcpuidfield(CPUIDFIELD cpuf)
{
    INT32 dwBuf[4];
    __cpuidex(dwBuf, CPUIDFIELD_FID(cpuf), CPUIDFIELD_FIDSUB(cpuf));
    return getcpuidfield_buf(dwBuf, cpuf);
}

 


  說明——
__GETBITS32:專門用於提取位域。它是常用的位運算操作,為了避免重復定義,用宏比較好。
getcpuidfield:標准的獲取CPUID字段函數。用法很簡單,只需傳遞一個CPUIDFIELD參數就行了。
getcpuidfield_buf:有時候需要一次獲得多個CPUID字段,並且已經知道它們屬於同一套功能號。這時為了提高效率,可以先用__cpuidex獲得那4個寄存器的信息,然后分別調用getcpuidfield_buf。

  范例——

    printf("SSE4A: %d\n", getcpuidfield(CPUF_SSE4A));
    printf("AES: %d\n", getcpuidfield(CPUF_AES));
    printf("PCLMULQDQ: %d\n", getcpuidfield(CPUF_PCLMULQDQ));

 



四、全部代碼

  全部代碼——

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#if _MSC_VER >=1400    // VC2005才支持intrin.h
#include <intrin.h>    // 所有Intrinsics函數
#else
#include <emmintrin.h>    // MMX, SSE, SSE2
#endif


// CPUIDFIELD
typedef INT32 CPUIDFIELD;

#define  CPUIDFIELD_MASK_POS    0x0000001F    // 位偏移. 0~31.
#define  CPUIDFIELD_MASK_LEN    0x000003E0    // 位長. 1~32
#define  CPUIDFIELD_MASK_REG    0x00000C00    // 寄存器. 0=EAX, 1=EBX, 2=ECX, 3=EDX.
#define  CPUIDFIELD_MASK_FIDSUB    0x000FF000    // 子功能號(低8位).
#define  CPUIDFIELD_MASK_FID    0xFFF00000    // 功能號(最高4位 和 低8位).

#define CPUIDFIELD_SHIFT_POS    0
#define CPUIDFIELD_SHIFT_LEN    5
#define CPUIDFIELD_SHIFT_REG    10
#define CPUIDFIELD_SHIFT_FIDSUB    12
#define CPUIDFIELD_SHIFT_FID    20

#define CPUIDFIELD_MAKE(fid,fidsub,reg,pos,len)    (((fid)&0xF0000000) \
    | ((fid)<<CPUIDFIELD_SHIFT_FID & 0x0FF00000) \
    | ((fidsub)<<CPUIDFIELD_SHIFT_FIDSUB & CPUIDFIELD_MASK_FIDSUB) \
    | ((reg)<<CPUIDFIELD_SHIFT_REG & CPUIDFIELD_MASK_REG) \
    | ((pos)<<CPUIDFIELD_SHIFT_POS & CPUIDFIELD_MASK_POS) \
    | (((len)-1)<<CPUIDFIELD_SHIFT_LEN & CPUIDFIELD_MASK_LEN) \
    )
#define CPUIDFIELD_FID(cpuidfield)    ( ((cpuidfield)&0xF0000000) | (((cpuidfield) & 0x0FF00000)>>CPUIDFIELD_SHIFT_FID) )
#define CPUIDFIELD_FIDSUB(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_FIDSUB)>>CPUIDFIELD_SHIFT_FIDSUB )
#define CPUIDFIELD_REG(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_REG)>>CPUIDFIELD_SHIFT_REG )
#define CPUIDFIELD_POS(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_POS)>>CPUIDFIELD_SHIFT_POS )
#define CPUIDFIELD_LEN(cpuidfield)    ( (((cpuidfield) & CPUIDFIELD_MASK_LEN)>>CPUIDFIELD_SHIFT_LEN) + 1 )

// 取得位域
#ifndef __GETBITS32
#define __GETBITS32(src,pos,len)    ( ((src)>>(pos)) & (((UINT32)-1)>>(32-len)) )
#endif


#define CPUF_SSE4A    CPUIDFIELD_MAKE(0x80000001,0,2,6,1)
#define CPUF_AES    CPUIDFIELD_MAKE(1,0,2,25,1)
#define CPUF_PCLMULQDQ    CPUIDFIELD_MAKE(1,0,2,1,1)


// SSE系列指令集的支持級別. simd_sse_level 函數的返回值。
#define SIMD_SSE_NONE    0    // 不支持
#define SIMD_SSE_1    1    // SSE
#define SIMD_SSE_2    2    // SSE2
#define SIMD_SSE_3    3    // SSE3
#define SIMD_SSE_3S    4    // SSSE3
#define SIMD_SSE_41    5    // SSE4.1
#define SIMD_SSE_42    6    // SSE4.2

const char*    simd_sse_names[] = {
    "None",
    "SSE",
    "SSE2",
    "SSE3",
    "SSSE3",
    "SSE4.1",
    "SSE4.2",
};




char szBuf[64];
INT32 dwBuf[4];

#if defined(_WIN64)
// 64位下不支持內聯匯編. 應使用__cpuid、__cpuidex等Intrinsics函數。
#else
#if _MSC_VER < 1600    // VS2010. 據說VC2008 SP1之后才支持__cpuidex
void __cpuidex(INT32 CPUInfo[4], INT32 InfoType, INT32 ECXValue)
{
    if (NULL==CPUInfo)    return;
    _asm{
        // load. 讀取參數到寄存器
        mov edi, CPUInfo;    // 准備用edi尋址CPUInfo
        mov eax, InfoType;
        mov ecx, ECXValue;
        // CPUID
        cpuid;
        // save. 將寄存器保存到CPUInfo
        mov    [edi], eax;
        mov    [edi+4], ebx;
        mov    [edi+8], ecx;
        mov    [edi+12], edx;
    }
}
#endif    // #if _MSC_VER < 1600    // VS2010. 據說VC2008 SP1之后才支持__cpuidex

#if _MSC_VER < 1400    // VC2005才支持__cpuid
void __cpuid(INT32 CPUInfo[4], INT32 InfoType)
{
    __cpuidex(CPUInfo, InfoType, 0);
}
#endif    // #if _MSC_VER < 1400    // VC2005才支持__cpuid

#endif    // #if defined(_WIN64)

// 根據CPUIDFIELD從緩沖區中獲取字段.
UINT32    getcpuidfield_buf(const INT32 dwBuf[4], CPUIDFIELD cpuf)
{
    return __GETBITS32(dwBuf[CPUIDFIELD_REG(cpuf)], CPUIDFIELD_POS(cpuf), CPUIDFIELD_LEN(cpuf));
}

// 根據CPUIDFIELD獲取CPUID字段.
UINT32    getcpuidfield(CPUIDFIELD cpuf)
{
    INT32 dwBuf[4];
    __cpuidex(dwBuf, CPUIDFIELD_FID(cpuf), CPUIDFIELD_FIDSUB(cpuf));
    return getcpuidfield_buf(dwBuf, cpuf);
}

// 取得CPU廠商(Vendor)
//
// result: 成功時返回字符串的長度(一般為12)。失敗時返回0。
// pvendor: 接收廠商信息的字符串緩沖區。至少為13字節。
int cpu_getvendor(char* pvendor)
{
    INT32 dwBuf[4];
    if (NULL==pvendor)    return 0;
    // Function 0: Vendor-ID and Largest Standard Function
    __cpuid(dwBuf, 0);
    // save. 保存到pvendor
    *(INT32*)&pvendor[0] = dwBuf[1];    // ebx: 前四個字符
    *(INT32*)&pvendor[4] = dwBuf[3];    // edx: 中間四個字符
    *(INT32*)&pvendor[8] = dwBuf[2];    // ecx: 最后四個字符
    pvendor[12] = '\0';
    return 12;
}

// 取得CPU商標(Brand)
//
// result: 成功時返回字符串的長度(一般為48)。失敗時返回0。
// pbrand: 接收商標信息的字符串緩沖區。至少為49字節。
int cpu_getbrand(char* pbrand)
{
    INT32 dwBuf[4];
    if (NULL==pbrand)    return 0;
    // Function 0x80000000: Largest Extended Function Number
    __cpuid(dwBuf, 0x80000000);
    if (dwBuf[0] < 0x80000004)    return 0;
    // Function 80000002h,80000003h,80000004h: Processor Brand String
    __cpuid((INT32*)&pbrand[0], 0x80000002);    // 前16個字符
    __cpuid((INT32*)&pbrand[16], 0x80000003);    // 中間16個字符
    __cpuid((INT32*)&pbrand[32], 0x80000004);    // 最后16個字符
    pbrand[48] = '\0';
    return 48;
}


// 是否支持MMX指令集
BOOL    simd_mmx(BOOL* phwmmx)
{
    const INT32    BIT_D_MMX = 0x00800000;    // bit 23
    BOOL    rt = FALSE;    // result
    INT32 dwBuf[4];

    // check processor support
    __cpuid(dwBuf, 1);    // Function 1: Feature Information
    if ( dwBuf[3] & BIT_D_MMX )    rt=TRUE;
    if (NULL!=phwmmx)    *phwmmx=rt;

    // check OS support
    if ( rt )
    {
#if defined(_WIN64)
        // VC編譯器不支持64位下的MMX。
        rt=FALSE;
#else
        __try 
        {
            _mm_empty();    // MMX instruction: emms
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            rt=FALSE;
        }
#endif    // #if defined(_WIN64)
    }

    return rt;
}

// 檢測SSE系列指令集的支持級別
int    simd_sse_level(int* phwsse)
{
    const INT32    BIT_D_SSE = 0x02000000;    // bit 25
    const INT32    BIT_D_SSE2 = 0x04000000;    // bit 26
    const INT32    BIT_C_SSE3 = 0x00000001;    // bit 0
    const INT32    BIT_C_SSSE3 = 0x00000100;    // bit 9
    const INT32    BIT_C_SSE41 = 0x00080000;    // bit 19
    const INT32    BIT_C_SSE42 = 0x00100000;    // bit 20
    int    rt = SIMD_SSE_NONE;    // result
    INT32 dwBuf[4];

    // check processor support
    __cpuid(dwBuf, 1);    // Function 1: Feature Information
    if ( dwBuf[3] & BIT_D_SSE )
    {
        rt = SIMD_SSE_1;
        if ( dwBuf[3] & BIT_D_SSE2 )
        {
            rt = SIMD_SSE_2;
            if ( dwBuf[2] & BIT_C_SSE3 )
            {
                rt = SIMD_SSE_3;
                if ( dwBuf[2] & BIT_C_SSSE3 )
                {
                    rt = SIMD_SSE_3S;
                    if ( dwBuf[2] & BIT_C_SSE41 )
                    {
                        rt = SIMD_SSE_41;
                        if ( dwBuf[2] & BIT_C_SSE42 )
                        {
                            rt = SIMD_SSE_42;
                        }
                    }
                }
            }
        }
    }
    if (NULL!=phwsse)    *phwsse=rt;

    // check OS support
    __try 
    {
        __m128 xmm1 = _mm_setzero_ps();    // SSE instruction: xorps
        if (0!=*(int*)&xmm1)    rt = SIMD_SSE_NONE;    // 避免Release模式編譯優化時剔除上一條語句
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        rt = SIMD_SSE_NONE;
    }

    return rt;
}


int _tmain(int argc, _TCHAR* argv[])
{
    //__cpuidex(dwBuf, 0,0);
    //__cpuid(dwBuf, 0);
    //printf("%.8X\t%.8X\t%.8X\t%.8X\n", dwBuf[0],dwBuf[1],dwBuf[2],dwBuf[3]);

    cpu_getvendor(szBuf);
    printf("CPU Vendor:\t%s\n", szBuf);

    cpu_getbrand(szBuf);
    printf("CPU Name:\t%s\n", szBuf);

    BOOL bhwmmx;    // 硬件支持MMX.
    BOOL bmmx;    // 操作系統支持MMX.
    bmmx = simd_mmx(&bhwmmx);
    printf("MMX: %d\t// hw: %d\n", bmmx, bhwmmx);

    int    nhwsse;    // 硬件支持SSE.
    int    nsse;    // 操作系統支持SSE.
    nsse = simd_sse_level(&nhwsse);
    printf("SSE: %d\t// hw: %d\n", nsse, nhwsse);
    for(int i=1; i<sizeof(simd_sse_names); ++i)
    {
        if (nhwsse>=i)    printf("\t%s\n", simd_sse_names[i]);
    }

    // test CPUIDFIELD
    //CPUIDFIELD cpuf = CPUIDFIELD_MAKE(0x8000000D,62,0,0,32);
    //printf("0x%.8X\n", cpuf);
    //printf("fid:\t0x%X\n", CPUIDFIELD_FID(cpuf));
    //printf("fidsub:\t0x%X\n", CPUIDFIELD_FIDSUB(cpuf));
    //printf("reg:\t0x%X\n", CPUIDFIELD_REG(cpuf));
    //printf("pos:\t0x%X\n", CPUIDFIELD_POS(cpuf));
    //printf("len:\t0x%X\n", CPUIDFIELD_LEN(cpuf));

    // test SSE4A/AES/PCLMULQDQ
    printf("SSE4A: %d\n", getcpuidfield(CPUF_SSE4A));
    printf("AES: %d\n", getcpuidfield(CPUF_AES));
    printf("PCLMULQDQ: %d\n", getcpuidfield(CPUF_PCLMULQDQ));

    return 0;
}

 

  在以下編譯器中成功編譯——
VC6(32位)
VC2003(32位)
VC2005(32位)
VC2010(32位、64位)


五、測試結果

  在64位的win7中運行“x64\Release\getcpuidfield_2010.exe”,運行效果——

  利用cmdarg_ui運行“Debug\getcpuidfield.exe”,順便測試WinXP與VC6——

 

參考文獻——
《Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes:1, 2A, 2B, 2C, 3A, 3B, and 3C》. May 2012. http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
《Intel® Processor Identification and the CPUID Instruction》. April 2012. http://developer.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html
《AMD64 Architecture Programmer's Manual Volume 3: General Purpose and System Instructions》. December 2011. http://support.amd.com/us/Processor_TechDocs/24594_APM_v3.pdf
《AMD CPUID Specification》. September 2010. http://support.amd.com/us/Embedded_TechDocs/25481.pdf
《x86 architecture CPUID》. http://www.sandpile.org/x86/cpuid.htm
《[x86]SIMD指令集發展歷程表(MMX、SSE、AVX等)》. http://www.cnblogs.com/zyl910/archive/2012/02/26/x86_simd_table.html
《如何在各個版本的VC及64位下使用CPUID指令》. http://www.cnblogs.com/zyl910/archive/2012/05/21/vcgetcpuid.html
《[VC兼容32位和64位] 檢查MMX和SSE系列指令集的支持級別》. http://www.cnblogs.com/zyl910/archive/2012/05/25/checksimd64.html
《[C#] cmdarg_ui:“簡單參數命令行程序”的通用圖形界面》.  http://www.cnblogs.com/zyl910/archive/2012/06/19/cmdarg_ui.html


源碼下載——
http://files.cnblogs.com/zyl910/getcpuidfield.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM