附錄一 CPUID


CPUID 是 80486 CPU 以後,在 x86 指令集所新增的一個指令,它的用途在於鑑別 CPU 種類或提供 CPU 特性。在 80386 末期,CPU 兩大製造廠商,INTEL 與 AMD,常推出時脈更高速度更快的 CPU 相互競爭,這本是件好事,但有不肖廠商把較低時脈的 CPU 上的標籤磨掉後重新印製較高時脈的字樣,藉此魚目混珠獲取不義之財。

小木偶記得那時還有許多文獻探討,是否能利用 CPU 的某些特性來分辨 CPU 以及是否有方法能辨識 CPU 時脈,前者常常只能分辨 8088、80286、80386 等這種不同世代的 CPU,甚至無法區分不同廠商所製作的 CPU。至於後者到目前似乎是無解的。後來 CPU 『型號』日益複雜,如果只是靠 CPU 某些特性來辨識,實在是一件不易達成的目標。因此,INTEL 在 80486 CPU 指令集中新增一個指令,CPUID,來辨識 CPU 型號,不僅僅如此,它還詳細的列製造廠商、時脈、快取等等資料,所以 CPUID 可說是 CPU 的身分證。


檢查 CPU 能否執行 CPUID?

並不是所有的 CPU 都能執行 CPUID 指令,例如 8086/88、80286、80386,當然啦,現在大概也找不到這些 CPU 了,不過還是把檢查的方法說一說。根據英特爾文獻,Intel® Processor Identification and the CPUID Instruction,的記載,如果 CPU 延伸旗標 ( EFLAG ) 的第 21 位元 ( 稱為 ID ) 可以由軟體寫入的話,那麼你的 CPU 可以執行 CPUID。底下是一段檢查的程式碼:

;如果程式可以改變 EFLAGE 的第 21 位元,那麼 CPUID 有效,否則無效
        pushfd                  ;使延伸旗標推入堆疊
        pop     eax             ;把堆疊中的延伸旗標彈出至 EAX
        mov     ecx,eax         ;保存舊的延伸旗標
        xor     eax,200000h     ;改變強制延伸旗標第 21 位元
        push    eax             ;把改變後新的延伸旗標推入堆疊
        popfd                   ;存入延伸旗標
        pushfd                  ;再取出延伸旗標
        pop     eax
        xor     eax,ecx         ;如果延伸旗標的第21位元為1,那麼
        jz      no_cpuid        ;CPUID存在,否則不存在
        ……可以執行 CPUID 指令……
no_cpuid:
        ……不能執行 CPUID 指令……

LAHF/SAHF 指令

在 80x86 指令集中有一個指令可以直接取得旗標暫存器之值,它是 LAHF,LAHF 把延伸旗標暫存器的最低八位元存入 AH 暫存器堙CSAHF 是把 AH 之值存入延伸暫存器的最低八個位元,。下圖是 8086/8088 旗標暫存器各位元分布:

旗標暫存器

由圖上可知,SAHF 會影響 CF、PF、AF、ZF、SF 五個旗標,LAHF 是把旗標暫存器之值存入 AH 中,因此不影響原來的旗標。SAHF、LAHF 不需要運算元,其語法是:

        SAHF
        LAHF

雖然 LAHF 能直接取得旗標暫存器,但是對於要存取延伸旗標的 ID 位元卻毫無幫助,只好藉助堆疊。

PUSHF/POPF 與 PUSHFD/POPFD 指令

PUSHF 是把 16 位元的 8086/8088 旗標暫存器推入堆疊,而 POPF 則是把堆疊頂的 16 位元資料彈出到 16 位元的旗標暫存器。PUSHFD 則是把 32 位元的 EFLAG 推入堆疊,32 位元的長度是雙字組,PUSHFD 的『D』是 double word 之意;POPFD 則是把堆疊頂的雙字組彈出到 EFLAG。這四個指令,也都不須運算元。


CPUID 介紹

要利用 CPUID 指令獲得 CPU 資料,一般是以 EAX 當做索引值,所給定的 EAX 索引值不同,就可以獲得不同的 CPU 資料。這些索引值可分為兩大類:基本功能 ( standard function ) 與延伸功能 ( extended function ) ,基本功能的 EAX 索引值最高位元為零,延伸功能的最高位元為 1,不管是那一家公司所生產的 x86 處理器,基本功能或延伸功能都有好幾個,所以得先取得基本功能或延伸功能的最大索引值。一般是先把 EAX 設為 0,然後執行 CPUID,就可以在 EAX 中得到基本功能的最大索引值;而要獲得延伸功能的最大索引值,先使 EAX 設為 80000000H,再執行 CPUID,在返回時,EAX 就存有最大延伸功能的索引值。得到最大索引值後,EAX 就應該在這個最大值內去『呼叫』CPUID,如果你用比最大索引值還大的數值存於 EAX,去執行 CPUID,結果和以最大索引值一樣。

EAX=0000 0000

當 EAX 等於 0 再執行 CPUID 後,CPUID 會傳回兩筆資料,第一筆資料是存放在 EAX,EAX 會傳回基本功能的最大索引值,第二筆資料存放在 EBX、ECX、EDX 共 96 位元,它們共同表示製造廠商的名稱字串,如果是英特爾的 CPU,其分布如下圖:

EAX=0時,EBX、ECX、EDX 字串分布
由上圖可知,製造廠商的名稱字串依 EBX、ECX、EDX 順序,從低位元開始,對英特爾處理器而言,製造廠商的名稱字串是『GenuineIntel』;對美商超微的處理器而言,製造廠商的名稱字串是『AuthenticAMD』;對 Cyrix 的處理器而言,製造廠商的名稱字串是『CyrixInstead』;對 IDT 的處理器而言,製造廠商的名稱字串是『CentaurHauls』。

EAX=0000 0001

EAX=0000 0001 號的基本功能可以取得 CPU 屬於那一種等級、製程及某些 CPU 特性。

EAX 的返回值

EAX 表示 CPU 的等級 ( Family )、核心 ( Model ) 與工藝製成 ( Stepping ),下圖為 EAX 的返回值,各位元所代表的意義:

依據英特爾手冊,CPU 的 Family 應該是 EAX 中的 Family Code 與 Extended Family 之和,Model 是 Extended Model 左移 4 位元之後再加上 Model Number 之值。一般而言,Family 代表同一代生產的 CPU,大致可分為:

Model 用來區別同一代但不同核心的 CPU,例如:

Stepping 表示同一核心不同工藝的產品,例如:

EBX 的返回值

下表是英特爾的 CPU 以 EAX=1 為參數,EBX 的返回值,對 AMD 的 CPU 來說,大致相同,只有位元 4∼12 和 14∼22 均保留不用。

位元名稱 說 明
此位元為 1 時
0∼7Brand ID在 Pentium !!! 的時代堙A開始用 Brand ID 來補足 Family、Model、Stepping 的不足,因此目前為止,要辨識 CPU『身分』,必須用這四筆資料來判斷。
8∼15Chunks
16∼23Count
24∼31APIC ID

ECX 的返回值

下表是英特爾的 CPU 以 EAX=1 為參數,ECX 的返回值,對 AMD 的 CPU 來說,大致相同,只有位元 4∼12 和 14∼22 均保留不用。

位元縮寫名稱 說 明
此位元為 1 時
0SSE3Streaming SIMD Extensions 3支援 Streaming SIMD Extensions 3 指令集
1∼2保留
3MONITORMONITOR/MWAIT支援 MONITOR 與 MWAIT 指令
4DS-CPLCPL Qualified Debug Store
5VMXVirtual Machine Extensions支援 Virtualization 技術
6保留
7ESTEnhanced Intel SpeedStep® Technology提供第二代的 SpeedStep 技術
8TM2Thermal Monitor 2提供 Thermal Monitor 2
9SSSE3Supplemental Streaming SIMD Extension 3支援 SSSE3 指令集
10CIDContext ID第一階快取可被 BIOS 設為 adaptive 或 shared 模式
11∼12保留
13CX16CMPXCHG16B支援 CMPXCHG16B 指令
14xTPRSend Task Priority Message
15∼17保留
18DCADirect Cache Access
19∼31保留

EDX 的返回值

下表是英特爾的 CPU 以 EAX=1 為參數,EDX 的返回值,對 AMD 的 CPU 來說,大致相同,只有位元 18、20∼22、27、29∼31 均保留不用。

位元縮寫名稱 說 明
此位元為 1 時
0FPUFloating-point unit on-Chip處理器內含支援 80387 浮點指令的 FPU
1VMEVirtual Mode Extension此處理器支援 V86 的延伸模式 ( extended V86 mode )
2DEDebugging Extension
3PSEPage Size Extension處理器支援 4MB 的分頁
4TSCTime-Stamp Counter支援 RDTSC 指令,RDTSC 指令可以計算出 CPU 頻率
5MSRModel Specific Registers支援 RDMSR、WRMSR 指令
6PAEPhysical Address Extension支援真實位址超過 32 位元
7MCEMachine Check Extension
8CX8CMPXCHG8 Instruction Support支援 CMPXCHG8 指令
9APICOn-chip APIC Hardware Supported
10保留
11SEPFast System CallCPU 支援 Fast System Call 指令,SYSENTER 與 SYSEXIT 指令
12MTRRMemory Type Range RegistersCPU 擁有 MTRR_CAP 暫存器
13PGEPage Global Enable?
14MCAMachine Check Architecture?
15CMOVConditional Move Instruction SupportedCPU 支援 CMOVVcc
16PATPage Attribute Table支援 Page Attribute Table
17PSE-3636-bit Page Size Extension?
18PSNProcessor Serial Number支援 96 位元的 CPU 序號
19CLFSHCLFLUSH Instruction Supported支援 CLFLUSH 指令
20保留
21DSDebug Store?
22ACPIThermal Monitor and Software Controlled Clock Facilities Supported
23MMXMMX Supported支援 MMX 指令集
24FXSRFast Floating-point Save and Restore支援新的 FPU 指令,FXSAVE、FXRSTOR
25SSEStreaming SIMD Extensions Supported支援 SSE 指令集
26SSE2Streaming SIMD Extensions 2支援 SSE2 指令集
27SSSelf-Snoop?
28HTTMulti-ThreadingCPU 支援超過一個以上的邏輯處理器
29TMThermal Monitor SupportedCPU 提供溫控電路
30IA64IA64 Capabilities 此 CPU 是 Intel Itanium 家族的一員,支援 IA64 ( IA64 與 AMD 所提出來的 AMD64 不相容 )
31PBEPending Break Enable

EAX=0000 0002H

對於英特爾的 CPU 而言,EAX=00000002H 功能傳回 CPU 的快取及轉換後備緩衝器性質;但是對 AMD 的 CPU 而言,00000002H∼00000004H 功能是保留無用的,其快取與轉換後備緩衝器性質的性質是在延伸功能 80000005H、80000006H 功能。( 有關轉換後備緩衝器,即 TLB [ Translation Lookaside Buffer ],的介紹請參考 zues 網頁,『路漫漫其修遠兮, 吾將上下而求索!』的 TLB 簡介 )

當我們以 EAX=00000002 為參數執行 CPUID 後,會在 EAX、EBX、ECX、EDX 暫存器會存入快取及轉換後備緩衝器的資料,但是有時資料量太大,可能放不下,因此在 AL 中存有要執行幾次以 EAX=00000002 為參數的 CPUID 才能獲取所有的快取及轉換後備緩衝器資料。這些資料每 8 個位元一組,需對照下表,才能得知其意義:

數值快取或 TLB 性質 數值快取或 TLB 性質
00H無意義 01H指令 TLB:4 KB Pages, 4-way set associative, 32 entries
02H指令 TLB:4 MB Pages, fully associative, 2 entries 03H資料 TLB:4 KB Pages, 4-way set associative, 64 entries
04H資料 TLB:4 MB Pages, 4-way set associative, 8 entries 05H資料 TLB:4 MB Pages, 4-way set associative, 32 entries
06HL1 指令快取:8KB, 4-way set associative, 32 entries 08HL1 指令快取:16 KB, 4-way set associative, 32-byte line size
0AHL1 資料快取:8 KB, 2-way set associative, 32-byte line size 0CHL1 資料快取:16 KB, 4-way set associative, 32-byte line size
22HL3 快取:512 KB, 4-way set associative, sectored cache, 64-byte line size 23HL3 快取:1 MB, 8-way set associative, sectored cache, 64-byte line size
25HL3 快取:2 MB, 8-way set associative, sectored cache, 64-byte line size 29HL3 快取:4 MB, 8-way set associative, sectored cache, 64-byte line size
2CHL1 資料快取:32 KB, 8-way set associative, 64-byte line size 30HL1 指令快取:32 KB, 8-way set associative, 64-byte line size
39HL2 快取:128 KB, 4-way set associative, sectored cache, 64-byte line size 3AHL2 快取:192 KB, 6-way set associative, sectored cache, 64-byte line size
3BHL2 快取:128 KB, 2-way set associative, sectored cache, 64-byte line size 3CHL2 快取:256 KB, 4-way set associative, sectored cache, 64-byte line size
3DHL2 快取:384 KB, 6-way set associative, sectored cache, 64-byte line size 3EHL2 快取:512 KB, 4-way set associative, sectored cache, 64-byte line size
40H沒有 L2 快取或是有 L2 快取但無 L3 快取 41HL2 快取:128 KB, 4-way set associative, 32-byte line size
42HL2 快取:256 KB, 4-way set associative, 32-byte line size 43HL2 快取:512 KB, 4-way set associative, 32-byte line size
44HL2 快取:1 MB, 4-way set associative, 32-byte line size 45HL2 快取:2 MB, 4-way set associative, 32-byte line size
46HL3 快取:4 MB, 4-way set associative, 64-byte line size 47HL3 快取:8 MB, 8-way set associative, 64-byte line size
49HL3 快取:4 MB, 16-way set associative, 64-byte line size (Intel Xeon processor MP, Family F, Model 6)
L2 快取:4 MB, 16-way set associative, 64-byte line size
4AHL3 快取:6 MB, 12-way set associative, 64-byte line size
4BHL3 快取:8 MB, 16-way set associative, 64-byte line size 4CHL3 快取:12 MB, 12-way set associative, 64-byte line size
4DHL3 快取:16 MB, 16-way set associative, 64-byte line size 50H指令 TLB:4 KB, 2 MB or 4 MB pages, fully associative, 64 entries
51H指令 TLB:4 KB, 2 MB or 4 MB pages, fully associative, 128 entries 52H指令 TLB:4 KB, 2 MB or 4 MB pages, fully associative, 256 entries
56H資料 TLB:4 MB pages, 4-way set associative, 16 entries 57H資料 TLB:4 MB pages, 4-way set associative, 16 entries
5BH資料 TLB:4 KB or 4 MB pages, fully associative, 64 entries 5CH資料 TLB:4 KB or 4 MB pages, fully associative, 128 entries
5DH資料 TLB:4 KB or 4 MB pages, fully associative, 256 entries 60HL1 資料快取:16 KB, 8-way set associative, sectored cache, 64-byte line size
66HL1 資料快取:8 KB, 4-way set associative, sectored cache, 64-byte line size 67HL1 資料快取:16 KB, 4-way set associative, sectored cache, 64-byte line size
68HL1 資料快取:32 KB, 4 way set associative, sectored cache, 64-byte line size 70HTrace 快取:12 K-uops, 8-way set associative
71HTrace 快取:16 K-uops, 8-way set associative 72HTrace 快取:32 K-uops, 8-way set associative
73HTrace 快取:64 K-uops, 8-way set associative 78HL2 快取:1 MB, 4-way set associative, 64-byte line size
79HL2 快取:128 KB, 8-way set associative, sectored cache, 64-byte line size 7AHL2 快取:256 KB, 8-way set associative, sectored cache, 64-byte line size
7BHL2 快取:512 KB, 8-way set associative, sectored cache, 64-byte line size 7CHL2 快取:1 MB, 8-way set associative, sectored cache, 64-byte line size
7DHL2 快取:2 MB, 8-way set associative, 64-byte line size 7FHL2 快取:512 KB, 2-way set associative, 64-byte line size
82HL2 快取:256 KB, 8-way set associative, 32-byte line size 83HL2 快取:512 KB, 8-way set associative, 32-byte line size
84HL2 快取:1 MB, 8-way set associative, 32-byte line size 85HL2 快取:2 MB, 8-way set associative, 32-byte line size
86HL2 快取:512 KB, 4-way set associative, 64-byte line size 87HL2 快取:1 MB, 8-way set associative, 64-byte line size
B0H指令 TLB:4 KB Pages, 4-way set associative, 128 entries B1H指令 TLB:MB Pages, 4-way set associative, 4 entries
指令 TLB:2 MB Pages, 4-way set associative, 8 entries
B3H資料 TLB:4 KB Pages, 4-way set associative, 128 entries B4H資料 TLB:KB Pages, 4-way set associative, 256 entries

EAX=8000 0000H

當以 EAX=80000000H 為參數,執行 CPUID 指令後,EAX 會變成延伸功能的最大參數值。而 EBX、ECX、EDX 的返回值會依據製造商不同而不同,對英特爾的 CPU 來講,這三個暫存器的返回值無意義;對 AMD 來講,這三個暫存器的返回值和與 EAX=0 的返回值意義一樣。

EAX=8000 0001H

英特爾

這個延伸功能的返回值,兩大 CPU 廠商的結果不同。英特爾的 CPU 只有 ECX、EDX 有用,如下表:


ECX 返回值

位元縮寫名稱 說 明
此位元為 1 時
0LAHFLAHF/SAHF在 x86-64 模式時,LAHF 和 SAHF 指令可以使用
1∼31保留

EDX 返回值

0∼10保留
11SYSCALLSYSCALL/SYSRET支援 SYSCALL、SYSRET 指令
12∼19保留
20XD bitExecution Disable Bit支援 XD Bit
21∼28保留
29Intel® 64Intel®64 Instruction Set Architeture支援 IA64
30∼31保留

超微

AMD 的 CPU 以 EAX=80000001H 為參數執行 CPUID 後,EAX 的返回值與以 1 執行 CPUID 的返回值相同,表示 Family、Model、Stepping……。EBX 的返回值記錄延伸的 Brand ID,ECX、EDX 返回值則是一些雜項的資料,見下表:


EBX 返回值

位元縮寫名稱 說 明
此位元為 1 時
0∼15Brand IDBrand ID
16∼27保留
28∼31PkgTypePackage Type

ECX 返回值

位元縮寫名稱 說 明
此位元為 1 時
0LahfSahfLAHF/SAHF在 x86-64 模式時,LAHF 和 SAHF 指令可以使用,與英特爾 CPU 相同
1CmpLegacy
2SVMSecure Virtual Machine
3ExtApicSpace
4AltMovCr8
5ABMAdvanced Bit Manipulation支援 LZCNT 指令
6SSE4A支援 EXTRQ、INSERTQ、MOVNTSS、MOVNTSD 指令
7MisAlignSSEMisaligned SSE Mode
83DNowPrefetch支援 PREFETCH、PREFETCHW 指令
9OSVWOS Visible Workaround支援 OS-Visible Workaround
10∼11保留
12SKINIT支援 SKINIT、STGI、DEV 指令
13WDT支援 Watch Dog Timer
14∼31保留

EDX 返回值

位元縮寫名稱 說 明
此位元為 1 時
0∼10保留
11SysCallSysRetSYSCALL/SYSRET支援 SYSCALL、SYSRET 指令,與英特爾 CPU 相同
12∼19保留
20XD bitExecution Disable Bit支援 XD Bit
21∼28保留
29LMLong Mode
30∼31保留

EAX=8000 0002H ∼ 8000 0004H

連續以 80000002H、80000003H、80000004H 為參數執行 CPUID 可以得到這顆 CPU 的製造廠商名稱與 CPU 的詳細名稱,它們返回時,會把這些文字字串存在 EAX、EBX、ECX、EDX 字串堙C以小木偶的桌上機 Athlon 64 X2 3600+ 為例:

最後把這些字串合起來,變成一個描述 CPU 製造商及 CPU 名稱的 ASCIIZ 字串,『AMD Athlon(tm) 64 X2 Dual Core Processor 3600+』。注意到,此三個延伸的 CPUID 功能所得字串會以 0 當做結尾。

EAX=80000005H

80000005H 功能被英特爾的 CPU 保留不使用,但卻被 AMD CPU 用作記錄每個核心的第一階快取與 TLB 的性質。底下的表格是這些返回值:


EAX 返回值

位元名稱說 明
0∼7L1ITlb2and4MSize2MB 分頁時的指令 TLB 大小 ( 此數值為以 2MB 分頁時,有幾個條目項可用;若欲求 4MB 分頁,則為此數的一半 )
8∼15L1ITlb2and4MAssoc2MB 或 4MB 分頁時,指令 TLB 的聯合方式 ( 若為 0FFH,表示採用全聯合方式 )
16∼23L1DTlb2and4MSize2MB 分頁時的資料 TLB 大小 ( 類似位元 8∼15 )
24∼31L1DTlb2and4MAssoc2MB 或 4MB 分頁時指令 TLB 的聯合方式

EBX 返回值

位元名稱說 明
0∼7L1ITlb4KSize4KB 分頁時的指令 TLB 大小
8∼15L1ITlb4KAssoc指令 TLB 聯合方式
16∼23L1DTlb4KSize4KB 分頁時的資料 TLB 大小
24∼31L1DTlb4KAssoc4KB 分頁時資料 TLB 的聯合方式

ECX 返回值

位元名稱說 明
0∼7L1DcLineSize第一階資料快取線大小,以位元組為單位
8∼15L1DcLinesPerTag第一階資料快取,每 tag 所含快取線多少
16∼23L1DcAssoc第一階資料快取的聯合方式
24∼31L1DcSize第一階資料快取大小,以 KB 為單位

EDX 返回值

位元名稱說 明
0∼7L1DcLineSize第一階指令快取線大小,以位元組為單位
8∼15L1DcLinesPerTag第一階指令快取,每 tag 所含快取線多少
16∼23L1DcAssoc第一階指令快取的聯合方式
24∼31L1DcSize第一階指令快取大小,以 KB 為單位

EAX=80000006

英特爾

80000006H 功能被 Intel 用來描述第二階快取的性質,這第二階快取的性質僅記錄在 ECX 堙AEAX、EBX、EDX 都保留而沒有用到。底下是 ECX 的返回值:


ECX 返回值

位元說 明
0∼7第二階快取的 Line Size,以位元組為單位
8∼11保留不用
12∼15第二階快取的 Associativity:
00:Disable  01:直接對應  02:2 way  04:4 way
06:8 way   08:16 way   0F:Full
16∼31第二階快取大小,以 KB 為單位。( 1KB=1024 個位元組 )

超微

80000006H 功能被 AMD CPU 用作記錄每個核心的第二或第三階快取與第二階 TLB 的性質。不過在 EAX=80000006H 功能的返回值中,聯合方式僅佔四個位元,不能直接以數值表示聯合方式,而是以下表表示:

數值聯合方式 數值聯合方式
0L2/L3/TLB 禁用 1直接對應
22-way associative44-way associative
68-way associative816-way associative
0Ah32-way associative0Bh48-way associative
0Ch64-way associative0Dh96-way associative
0Eh128-way associative0Fhfully associative

底下就是 EAX=80000006H 功能的返回值:


EAX 返回值

位元名稱說 明
0∼11L2ITlb2and4MSize第二階指令 TLB 2MB/4MB 的條目數
12∼15L2ITlb2and4MAssoc第二階指令 TLB 2MB/4MB 的聯合方式
16∼27L2DTlb2and4MSize第二階資料 TLB 2MB/4MB 的條目數
28∼31L2DTlb2and4MAssoc第二階資料 TLB 2MB/4MB 的聯合方式

EBX 返回值

位元名稱說 明
0∼11L2ITlb4KSize第二階指令 TLB 4KB 的條目數
12∼15L2ITlb4KAssoc第二階指令 TLB 4KB 的聯合方式
16∼27L2DTlb4KSize第二階資料 TLB 4KB 的條目數
28∼31L2DTlb4KAssoc第二階資料 TLB 4KB 的聯合方式

ECX 返回值

位元名稱說 明
0∼7L2LineSize第二階快取線大小,以位元組為單位
8∼11L2LinesPerTag第二階快取中,每 tag 所含快取線數目
12∼15L2Assoc第二階快取的聯合方式
16∼31L2Size第二階快取的大小,以 KB 為單位

EDX 返回值

位元名稱說 明
0∼7L3LineSize第三階快取線大小,以位元組為單位
8∼11L3LinesPerTag第三階快取中,每 tag 所含快取線數目
12∼15L3Assoc第三階快取的聯合方式
16∼17保留
18∼31L3Size第三階快取的大小,以 KB 為單位

CPUID.ASM 與解說

底下小木偶寫了一個程式,CPUID.ASM 能在工作區中顯示由 CPUID 指令傳回來的資料。這個程式在不同的 CPU 之下執行畫面不同,底下是在小木偶一台 CPU 為 AMD64X2 3600+ 的桌上型電腦執行 CPUID.ASM 時的畫面:

讀者可按此處下載原始碼。解壓縮後,會得一 CPUID 子目錄,以該子目錄堛 CPUID.MAK 為參數,執行 NMAKE.EXE,即可得 CPUID.EXE:

E:\HomePage\SOURCE\CPUID>nmake -f cpuid.mak [Enter]

Microsoft (R) Program Maintenance Utility   Version 1.50
Copyright (c) Microsoft Corp 1988-94. All rights reserved.

        rc CPUID.rc
        ml CPUID.asm /link CPUID.res
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: CPUID.asm
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

/SUBSYSTEM:WINDOWS
"CPUID.obj"
"/OUT:CPUID.exe"
"CPUID.res"

E:\HomePage\SOURCE\CPUID>

WM_CREATE 做了那些事

在處理 WM_CREATE 訊息堙ACPUID.ASM 先檢查 CPU 是否能執行 CPUID 指令,接著取得文字高度,存於 tm 結構體堙A這個變數將在 WM_PAINT 訊息中用著。在 WM_PAINT 訊息堙ACPUID.ASM 會把由 CPUID 所得的資料印在螢幕上,這些資料可能不只一行,每一行所佔據的高度為 tm.tmHeight 與 tm.tmExternalLeading 之和,有了文字高度才能決定第二、三、四……的位置。

接著在 WM_CREATE 處理的是把視窗移到螢幕的正中央。小木偶預設工作區高度是 200,故視窗高度為工作區高度再加上標題欄高度、選單高度及上下兩個邊框厚度,這些大小可參考第 14 章有關 GetSystemMetrics 的說明取得。視窗左上角的 Y 座標即為螢幕高度減去視窗高度再除以 2。同理,視窗左上角的 X 座標也是如此,只是視窗寬度變成工作區寬度加上左右兩邊的邊框厚度。

WM_CREATE 接著的工作,是以 EAX=0 執行 CPUID 指令,取得有幾項基本功能,並從 EBX 之值判斷 CPU 是那一家廠商的產品,並記錄在 bCPU 變數堙C若 bCPU=1,表示是 Intel 的 CPU;若 bCPU=2,表示是 AMD 的 CPU……。接著再依據 EAX,於主選單中建立一名為『Basci』的彈出選單,其下屬子選單選項分別是『0000 0000H』、『0000 0001H』等等。當使用者按下『0000 0000H』選項時,表示 CPU 以 00000000H 為參數執行 CPUID 指令,然後把結果顯示在工作區上。

WM_CREATE 接下來的工作是建立『Extend』彈出選單及其子選單中的選項,不管是『Basci』或『Extend』彈出選單都是呼叫 AddMenu 副程式完成。AddMenu 有三個參數:

AddMenu proto   dMax:DWORD,StrAddr:DWORD,Position:DWORD

dMax 是以 0 或 80000000H 執行 CPUID 後的數值,它被當作參數傳給 AddMenu,它決定了 AddMenu 在子選單中該產生幾個選項,以及各選項名稱、選項識別碼 ( ID )。例如 dMax 等於 2 時,應該會有『0000 0000H』、『0000 0001H』、『0000 0002H』三個選項,其 ID 分別是 100H、101H、102H。但如果是 dMax 為 80000008H,則 AddMenu 會忽略最高的位元組,且因為 80000002H 到 80000004H 這三個延伸功能共同取得 CPU 的製造廠商名稱與 CPU 的詳細名稱,因此在子選單中只用『8000 0002H』表示,故在子選單堨X現的是『8000 0000H』、『8000 0001H』、『8000 0002H』、『8000 0005H』、『8000 0006H』、『8000 0007H』、『8000 0008H』7 個子選單,其 ID 分別是 900H、901H、902H、905H、906H、907H、908H。

AddMenu 的第二個參數是指向主選單堛獐u出選項字串,亦即『Basic』與『Extend』。AddMenu 的第三個參數是彈出選項在主選單的位置,亦即『Basic』、『Extend』位置分別是 1、2,這是因為位置 0 是『退出』彈初選項。

WM_CREATE 最後的工作是呼叫 AddView 與 help 兩個副程式。前者是把『檢視』彈出選項及其子選單加進主選單堙A後者是把 szHelp 字串顯示於工作區內。

WM_PAINT 的工作

當我們以某一參數執行 CPUID 指令時,常傳回許多資料,這些資料不是用一列字串表示就可以了,這些資料被 CPUID.ASM 堛熙\多像 eax_?000_000? 副程式整理成數個字串,這些字串都存在 szString 陣列堙A爾後 WM_PAINT 再把這數個字串顯示於工作區上。每一列字串的高度是 tm.tmHeight+tm.tmExternalLeading,而每個字串的起始位址則存於 pString 陣列堙C

以使用者按下選項 eax_0000_0001H 為例子,這時在 WM_COMMAND 訊息中會呼叫副程式 eax_0000_0001,eax_0000_0001 副程式會先把『功能:00000001H』存入 szString 陣列的第零個元素,並把此位址存在 pString 陣列的第零個元素堙C接著是一個空字串以做視覺上的分隔,此空字串位址是 szAppName+5,即為 0,並把此位址存入 pString 的第一個元素,pString[4],堙C接著以 1 為參數執行 CPUID 指令,小木偶把所得資料存入 SavedEAX、SavedEBX、SavedECX、SavedEDX 四個變數堙A然後再整理成 Family、Model…等資料。當執行完後,pString 陣列之內容為:

004040C0                             74 44 40 00 55 41 40
004040D0  00 84 44 40 00 B3 44 40 00 D1 44 40 00 55 41 40
004040E0  00 DE 44 40 00 00 00 00 00 00 00 00 00 00 00 00
004040F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00404100  00 00 00 00 00 00 00 00 00

底下則是 szString 陣列部份內容,它是由不等長的字串組成的陣列:

00404470              A5 5C AF E0 A1 47 30 30 30 30 30 30      功能:000000
00404480  30 31 48 00 20 20 20 20 46 61 6D 69 6C 79 3A 30  01H.    Family:0
00404490  46 48 20 20 20 20 20 20 20 4D 6F 64 65 6C 3A 34  FH       Model:4
004044A0  42 48 20 20 20 20 20 53 74 65 70 70 69 6E 67 3A  BH     Stepping:
004044B0  32 48 00 45 78 74 2E 46 61 6D 69 6C 79 3A 30 30  2H.Ext.Family:00
004044C0  48 20 20 20 45 78 74 2E 4D 6F 64 65 6C 3A 34 48  H   Ext.Model:4H
004044D0  00 42 72 61 6E 64 20 49 44 3A 30 30 48 00 A4 E4  .Brand ID:00H.支
004044E0  B4 A9 20 46 50 55 20 4D 4D 58 20 53 53 45 20 53  援 FPU MMX SSE S
004044F0  53 45 32 20 53 53 45 33 00                       SE2 SSE3.

pString 是一個位址所組成的陣列,第零個元素,00404474,就是 WM_PAINT 將在工作區中印出的第零列字串位址,找到 00404474 位址的內容就是『功能:00000001H』字串。第一個元素,00404155,就是要印出的第一列字串位址,雖然沒在上面顯示,但是就是一個空字串。第二個元素,00404484,所指的位址是『    Family:0FH       Model:4BH     Stepping:2H』字串,也就是在工作區的第二列字串。其餘依此類推了。至於要顯示的列述有幾列,則是存於 wLine 堙C

eax_0000_0002 副程式決定 Intel 快取性質

雖然以 2 為參數,執行 CPUID 指令,可以得很詳盡的 CPU 快取性質,不過此處小木偶只撰寫能夠找到 L1、L2、L3 快取性質。因為 Intel CPU 所傳回的數值是以一個位元組為搜尋值去搜尋一個很大的表格來決定快取性質,同時每一種快取 ( L1、L2、L3 等 ) 表格的資料項數都不同,例如 L1 指令快取在表格中僅三項;而 L1 的資料快取則有 7 項,底下就是 CPUID.ASM 原始碼中有關表格的資料:

;英特爾CPU的L1指令/資料快取 暫存器值 大小 way lines
IntelL1i        BYTE            6h,    8,  4,  32
                BYTE            8h,   16,  4,  32
                BYTE            30h,  32,  8,  64
IntelL1d        BYTE            0ah,   8,  2,  32
                BYTE            0ch,  16,  4,  32
                BYTE            2ch,  32,  8,  64
                BYTE            60h,  16,  8,  64
                BYTE            66h,   8,  4,  64
                BYTE            67h,  16,  4,  64
                BYTE            68h,  32,  4,  64

小木偶在 eax_0000_0002 副程式堙A每以都 32 位元的暫存器去比對表格中的資料。表格中有幾項資料、表格位址分別存在 CL、ESI 堙A再去呼叫 find_cache_data 副程式。在 find_cache_data 副程式堙A呼叫 find 副程式四次,這四次分別是以 SavedEAX、SavedEBX、SavedECX、SavedEDX 為參數去比對 ESI 所指的表格資料。而在 find 副程式中則更進一步把 32 位元的 EAX 分成四個位元組,去呼叫 check_al 副程式去搜尋表格。

如果找到的話,ESI 則會存入快取資料,例如快取大小、聯合方式、Line size 等等,check_al 還會設定 ZF,然後再一層一層的把 ZR、ESI 傳回到 eax_0000_0002『主』程式中,eax_0000_0002 中再呼叫 save_intel_cache_data 副程式,把找到的快取資料存入一個叫做 dCacheValue 的陣列堙C dCacheValue 陣列由四個雙字組所組成,其偏移位址 0 存入快取大小;偏移位址 4 存入聯合方式;偏移位址 8 存入快取線大小;偏移位址 0CH 存入 Lines per Tag。( Intel 的 CPU 並沒有 Lines per Tag 這一項;但是 AMD 的 CPU 有此項。)

接下來 eax_0000_0002 會呼叫 set_cache 副程式,set_cache 副程式用來把 dCacheValue 中的快取性質,藉由呼叫 wsprintf API 變成字串,存在 szString 字串堙C不過快取資料不只一列,每一列的開始位址都會被記錄在 pString 陣列堙A而要印出的資料有幾列,則存於 wLine 變數堙C

換句話說,在呼叫完 eax_0000_0002 之後,wLine 會存放工作區需要印出幾列字串,字串內容則存於 szString 陣列。等到 WM_PAINT 訊息發生時,再把這些資料顯示於工作區堙C