首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

CHIPS:CPU 與數學協處理器 (NDP) 偵測的組合語言函式庫

     編輯:組合語言

前言:跨平台效能優化的基石 — 硬體能力偵測

在 8086 到 486 時代,CPU 指令集與數學協處理器 (8087/287/387) 的有無直接決定了軟體能否使用浮點指令、32 位元指令、特定優化路徑。CHIPS (由 Pat Shea 撰寫,1987-88) 提供一個標準 C 可呼叫的 FAR 函式 int chips(void),回傳編碼後的 CPU 型號與 NDP 型號,供執行期動態選擇最佳程式碼路徑。程式碼模組化 (cpu_type + ndp_type)、註解詳盡、相容 MASM 5.0,是系統程式庫設計的教科書級範例。

回傳值編碼規則

AX = CPU_Code * 10 + NDP_Code (實際為 CPU_Code + NDP_Code,CPU_Code 為 80/180/280/380/200,NDP_Code 為 0/1/2/3):

  • CPU:8088/86=80, 80188/186=180, 80286=280, 80386=380, NEC V20/V30=200
  • NDP:無=0, 8087=1, 80287=2, 80387=3
  • 範例:280 = 286 無 NDP;383 = 386 + 387;81 = 8088/86 + 8087;200 = NEC V20/V30 無 NDP

CPU 偵測邏輯 (cpu_type)

採決策樹,依序排除:

  1. FLAGS 高 4 位 (bits 12-15):8088/86/186/188/NEC 強制為 1;286+ 可清零。PUSHF/POP/AND/PUSH/POPF/POP 測試 → 可清零則 ≥286 (回傳 2),否則進入 dig。
  2. ≥286 分支:嘗試設定 FLAGS bits 12-14 (NT/IOPL) = 7000h。PUSH/POPF/PUSHF/POP/AND 檢查是否保留 → 保留則 386+ (回傳 4),否則 286 (回傳 2)。
  3. 8088/86/186/NEC 分支:
    • 移位計數遮罩:MOV AX,0FFFFH / MOV CL,33 / SHL AX,CL。8088/86 僅取 CL 低 5 位 (33→1),結果非零;186/188 取完整 CL,33 位移清零。非零 → 186/188 (回傳 180)。
    • MUL 旗標差異:XOR AL,AL / MOV AL,40H / MUL AL。NEC V20/V30 不影響 ZF (結果 0);8088/86 設定 ZF。ZF=1 → NEC (回傳 200),否則 8088/86 (回傳 80)。

NDP 偵測邏輯 (ndp_type)

採用 Ted Forgeron (PC Tech Journal, 1987) 經典演算法:

  1. FNINIT + FSTCW:初始化並儲存控制字。若位元組 1 = 03h (預設控制字高位元組) → 有 NDP,否則無 (回傳 0)。
  2. 8087 測試:設定控制字 IEM (Interrupt Enable Mask) = 1 (關中斷) → FDISI / FSTCW 檢查 IEM 是否置位。8087 支援 FDISI/FENI;287/387 不支援 (忽略)。置位 → 8087 (回傳 1)。
  3. 287 測試:FINIT / FLD1 / FLDZ / FDIV / FLD ST / FCHS / FCOMPP / FSTSW 產生 +∞ 和 -∞ 比較。8087/287 的無限大比較行為不同:8087 視為無序 (C3=1),287 視為不相等 (C3=0)。SAHF / JNZ 區分 → 287 (回傳 2)。
  4. 否則為 387 (回傳 3)。

關鍵程式碼片段

_chips PROC
control dw 0
push BP
mov BP,SP
push DI
push SI
push CX
call cpu_type ; DX = CPU code
call ndp_type ; AX = NDP code
add AX,DX ; 合併回傳\n /> pop CX
pop SI
pop DI
pop BP
ret\n />_chips ENDP\n />cpu_type PROC\n /> pushf\n /> xor DX,DX\n /> xor AX,AX\n /> push AX\n /> popf\n /> pushf\n /> pop AX\n /> and AX, 0F000h\n /> cmp AX, 0F000h\n /> jz dig\n /> mov AX, 7000h\n /> push AX\n /> popf\n /> pushf\n /> pop AX\n /> and AX, 7000h\n /> jnz got386\n /> mov DX, 280\n /> jmp SHORT CPUbye\n />got386: mov DX, 380\n /> jmp SHORT CPUbye\n />dig: mov AX, 0FFFFh\n /> mov CL, 33\n /> shl AX, CL\n /> jz digmor\n /> mov DX, 180\n /> jmp SHORT CPUbye\n />digmor: xor AL,AL\n /> mov AL, 40h\n /> mul AL\n /> jz gotNEC\n /> mov DX, 80\n /> jmp SHORT CPUbye\n />gotNEC: mov DX, 200\n />CPUbye: popf\n /> ret\n />cpu_type ENDP\n />ndp_type PROC\n />do_we: fninit\n /> mov byte ptr control+1, 0\n /> fnstcw control\n /> mov AH, byte ptr control+1\n /> cmp AH, 03h\n /> je chk_87\n /> xor AX,AX\n /> jmp SHORT NDPbye\n />chk_87: and control, NOT 0080h\n /> fldcw control\n /> fdisi\n /> fstcw control\n /> test control, 0080h\n /> jz chk287\n /> mov AX, 1\n /> jmp SHORT NDPbye\n />chk287: finit\n /> fld1\n /> fldz\n /> fdiv\n /> fld st\n /> fchs\n /> fcompp\n /> fstsw control\n /> fwait\n /> mov AX, control\n /> sahf\n /> jnz got387\n /> mov AX, 2\n /> jmp SHORT NDPbye\n />got387: mov AX, 3\n />NDPbye: ret\n />ndp_type ENDP

設計亮點

  • 模組化:cpu_type、ndp_type 獨立 PROC,單一職責,易維護擴展。
  • 暫存器保存嚴謹:所有呼叫慣例要求保存的暫存器 (BP, SI, DI, CX 等) 均正確 push/pop,符合 C 呼叫慣例。
  • 旗標暫存器保護:cpu_type 開頭 PUSHF、結尾 POPF,確保不干擾呼叫端旗標。
  • 相容性:MASM 5.0 不需 /R 選項即可組譯協處理器指令 (內建支援)。
  • 編碼效率:單一整數回傳攜帶雙重資訊,呼叫端僅需整數運算即可解碼 (cpu = ret/10; ndp = ret%10 類邏輯)。

現代映照

現代 x86 以 CPUID (EAX=1 → ECX/EDX 特徵旗標) 統一偵測 CPU 特性 (SSE/AVX/HT/VMX 等) 與 FPU 內建 (所有現代 CPU 含 x87)。但 CHIPS 的設計哲學依然適用:

  • 執行期特性偵測:而非編譯期假設,支援同一二進位檔在異構硬體上最佳化運行。
  • 函式庫抽象層:將複雜偵測邏輯封裝為簡單 API (chips()),上層應用無需知悉細節。
  • 優雅降級:無 NDP 時回傳 0,應用可切換至軟體浮點模擬庫。

CHIPS 以不到 200 行組合語言,解決了當時跨硬體平台部署的核心難題,其「偵測 → 抽象 → 適配」三階段模式,正是現代硬體抽象層 (HAL)、JIT 編譯器特性探測、WebAssembly SIMD 特性檢測的遠古雛形。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved