インラインアセンブラを使ってCPUIDを叩く練習。
参考: http://download.intel.com/jp/developer/jpdoc/Processor_Identification_071405_i.pdf
main.c
#include <stdio.h> #include "intel_cpuid_flag.h" #define SUPPORT(f) ( (f) ? "Support" : "None") char * bits(unsigned int x, unsigned int bit){ static char buffer[1024]; int i=0; while(bit--){ buffer[i++] = (x>>bit)&1?'1':'0'; if(bit%4==0) buffer[i++] = ' '; } buffer[i] = 0; return buffer; } void cpuid_0h(void){ unsigned int _eax, _ebx, _ecx, _edx; _eax = _ebx = _ecx = _edx = 0; _asm{ MOV EAX, 01h CPUID MOV _eax, EAX; MOV _ebx, EBX; MOV _ecx, ECX; MOV _edx, EDX; } puts("CPUID(EAX=01h)"); puts (" 28 24 20 16 12 8 4 0"); printf("EAX: %s (%08X)\n", bits(_eax,32), _eax); printf("EBX: %s (%08X)\n", bits(_ebx,32), _ebx); printf("ECX: %s (%08X)\n", bits(_ecx,32), _ecx); printf("EDX: %s (%08X)\n", bits(_edx,32), _edx); // EDX printf("FPU : %s\n", SUPPORT(_edx & FPU_FLAG) ); printf("VME : %s\n", SUPPORT(_edx & VME_FLAG) ); printf("DE : %s\n", SUPPORT(_edx & DE_FLAG) ); printf("PSE : %s\n", SUPPORT(_edx & PSE_FLAG) ); printf("TSC : %s\n", SUPPORT(_edx & TSC_FLAG) ); printf("MSR : %s\n", SUPPORT(_edx & MSR_FLAG) ); printf("PAE : %s\n", SUPPORT(_edx & PAE_FLAG) ); printf("MCE : %s\n", SUPPORT(_edx & MCE_FLAG) ); printf("CX8 : %s\n", SUPPORT(_edx & CX8_FLAG) ); printf("APIC : %s\n", SUPPORT(_edx & APIC_FLAG) ); // printf("Reserve1: %s\n", SUPPORT(_edx & 0x0400) ); printf("SEP : %s\n", SUPPORT(_edx & SEP_FLAG) ); printf("MTRR : %s\n", SUPPORT(_edx & MTRR_FLAG) ); printf("PGE : %s\n", SUPPORT(_edx & PGE_FLAG) ); printf("MCA : %s\n", SUPPORT(_edx & MCA_FLAG) ); printf("CMOV : %s\n", SUPPORT(_edx & CMOV_FLAG) ); printf("PAT : %s\n", SUPPORT(_edx & PAT_FLAG) ); printf("PSE-36 : %s\n", SUPPORT(_edx & PSE36_FLAG) ); printf("PSNUM : %s\n", SUPPORT(_edx & PSNUM_FLAG) ); printf("CLFSH : %s\n", SUPPORT(_edx & CLFLUSH_FLAG) ); // printf("Reserve2: %s\n", SUPPORT(_edx & 0x100000) ); printf("DS : %s\n", SUPPORT(_edx & DTS_FLAG) ); printf("ACPI : %s\n", SUPPORT(_edx & ACPI_FLAG) ); printf("MMX : %s\n", SUPPORT(_edx & MMX_FLAG) ); printf("FXSR : %s\n", SUPPORT(_edx & FXSR_FLAG) ); printf("SSE : %s\n", SUPPORT(_edx & SSE_FLAG) ); printf("SSE2 : %s\n", SUPPORT(_edx & SSE2_FLAG) ); printf("SS : %s\n", SUPPORT(_edx & SS_FLAG) ); printf("HTT : %s\n", SUPPORT(_edx & HTT_FLAG) ); printf("TM : %s\n", SUPPORT(_edx & TM_FLAG) ); // printf("Reserve3: %s\n", SUPPORT(_edx & 0x40000000) ); printf("PBE : %s\n", SUPPORT(_edx & PBE_FLAG) ); // ECX printf("SSE3 : %s\n", SUPPORT(_ecx & SSE3_FLAG) ); // printf("Reserve1: %s\n", SUPPORT(_ecx & 0x0002) ); // printf("Reserve2: %s\n", SUPPORT(_ecx & 0x0004) ); printf("MONITOR : %s\n", SUPPORT(_ecx & MONITOR_FLAG) ); printf("DS-CPL : %s\n", SUPPORT(_ecx & DS_CPL_FLAG) ); // printf("Reserve3: %s\n", SUPPORT(_ecx & 0x0020) ); // printf("Reserve4: %s\n", SUPPORT(_ecx & 0x0040) ); printf("EIST : %s\n", SUPPORT(_ecx & EIST_FLAG) ); printf("TM2 : %s\n", SUPPORT(_ecx & TM2_FLAG) ); // printf("Reserve5: %s\n", SUPPORT(_ecx & 0x0200) ); printf("CID : %s\n", SUPPORT(_ecx & CID_FLAG) ); // printf("Reserve6: %s\n", SUPPORT(_ecx & 0x0800) ); // printf("Reserve7: %s\n", SUPPORT(_ecx & 0x1000) ); printf("CX16 : %s\n", SUPPORT(_ecx & CX16_FLAG) ); printf("xTPR : %s\n", SUPPORT(_ecx & XTPR_FLAG) ); // 以下予約領域 puts("----------------------------------------"); _eax = _ebx = _ecx = _edx = 0; } int main(){ cpuid_0h(); }
intel_cpuid_flag.h
// これは参考先から引用しました。
#define FPU_FLAG 0x0001 #define VME_FLAG 0x0002 #define DE_FLAG 0x0004 #define PSE_FLAG 0x0008 #define TSC_FLAG 0x0010 #define MSR_FLAG 0x0020 #define PAE_FLAG 0x0040 #define MCE_FLAG 0x0080 #define CX8_FLAG 0x0100 #define APIC_FLAG 0x0200 #define SEP_FLAG 0x0800 #define MTRR_FLAG 0x1000 #define PGE_FLAG 0x2000 #define MCA_FLAG 0x4000 #define CMOV_FLAG 0x8000 #define PAT_FLAG 0x10000 #define PSE36_FLAG 0x20000 #define PSNUM_FLAG 0x40000 #define CLFLUSH_FLAG 0x80000 #define DTS_FLAG 0x200000 #define ACPI_FLAG 0x400000 #define MMX_FLAG 0x800000 #define FXSR_FLAG 0x1000000 #define SSE_FLAG 0x2000000 #define SSE2_FLAG 0x4000000 #define SS_FLAG 0x8000000 #define HTT_FLAG 0x10000000 #define TM_FLAG 0x20000000 #define PBE_FLAG 0x80000000 #define SSE3_FLAG 0x0001 #define MONITOR_FLAG 0x0008 #define DS_CPL_FLAG 0x0010 #define EIST_FLAG 0x0080 #define TM2_FLAG 0x0100 #define CID_FLAG 0x0400 #define CX16_FLAG 0x2000 #define XTPR_FLAG 0x4000
結果
CPUID EAX=01h 28 24 20 16 12 8 4 0 EAX: 0000 0000 0000 0001 0000 0110 0111 0111 (00010677) EBX: 0000 0001 0000 0100 0000 1000 0000 0000 (01040800) ECX: 0000 0000 0000 1000 1110 0011 1111 1101 (0008E3FD) EDX: 1011 1111 1110 1011 1111 1011 1111 1111 (BFEBFBFF) FPU : Support VME : Support DE : Support PSE : Support TSC : Support MSR : Support PAE : Support MCE : Support CX8 : Support APIC : Support SEP : Support MTRR : Support PGE : Support MCA : Support CMOV : Support PAT : Support PSE-36 : Support PSNUM : None CLFSH : Support DS : Support ACPI : Support MMX : Support FXSR : Support SSE : Support SSE2 : Support SS : Support HTT : Support TM : Support PBE : Support SSE3 : Support MONITOR : Support DS-CPL : Support EIST : Support TM2 : Support CID : None CX16 : Support xTPR : Support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
今思ったんだが、これってAMD系のCPUでも動くのだろうか。あとで調べるか。
古い情報を使った(IA-64命令について書かれていなかったり)ので、ここで予約領域扱いにしている部分に新しい命令のサポートについて入るものがあるかもしれない。
調べるのが面倒になって投げたry
EAXに渡す値によってCPUID命令の挙動が変わるのがポイントですかね。
CPUの温度情報をcpuid命令で取れるんじゃないかなとか思ったが、甘かったですね。
マザーボードのI/Oチップがその辺の情報を持ってるらしい。
でも機種依存になるだろうし、
GCCで動かしたい
ちなみにこのコードはVC++2010で動作確認しています。このままではGCCでは動きません
MinGWなどのGCCの拡張インラインアセンブラで書いた場合はこんな感じに書けば動くっぽい。
__asm__( "movl $0x01, %%eax;" "cpuid;" : "=a"(_eax),"=b"(_ebx),"=c"(_ecx),"=d"(_edx) );
GCC拡張インラインアセンブラは分かりにくいけど記述量を減らせる。
GCCではAT&T記法なので、VCなどのIntel記法を使ってると違和感がある。
あと、この通りのコードを吐いてくれず、機械が使いやすい形に勝手に代入しだす。
例えば、ECXの部分はEDIにコピーして使いだしたりとか。まぁでも、動いてくれるので良しとする。
GCC拡張インラインアセンブラは分かりにくいので、もう少しメモすると、
MOV _eax, EAX; MOV _ebx, EBX; MOV _ecx, ECX; MOV _edx, EDX;
にあたる部分を、
: "=a"(_eax),"=b"(_ebx),"=c"(_ecx),"=d"(_edx)
の1行で行っている。
詳しい記述の解説は別で知ってもらうとして、こんな感じでカンマで区切ることで複数定義できる。
"=a"(_eax)はEAXレジスタの内容を変数_eaxにコピーする意味。
"=b"(_ebx)はEBXレジスタの内容を変数_ebxにry
以下略。
"=r"とすると、適当なレジスタを使ってくれるらしいのでラクだが、
今回は、EAX,EBX,ECX,EDXのそれぞれのレジスタがちゃんと意味を持つので、指定してあげること。
GCCインラインアセンブラ,参考
http://caspar.hazymoon.jp/OpenBSD/annex/gcc_inline_asm.html
http://sci10.org/on_gcc_asm.html