Visual C++ 6.0/7.x 內建的內嵌組譯器(__asm)支援 Intel 486 指令集與 MMX,允許在 C/C++ 函數內直接編寫組語指令,無需獨立組譯/連結步驟,且能無縫存取 C/C++ 變數。本文系統整理官方文件精華,涵蓋語法規則、資料操作、流程控制、函數呼叫與實戰範例。
VC 不支援 asm,必須使用 __asm(雙底線)。
; 區塊寫法(推薦,可視化範圍、避免重複關鍵字)
__asm {
mov al, 2
mov dx, 0xD007
out al, dx
}
; 單行寫法
__asm mov al, 2
__asm mov dx, 0xD007
__asm out al, dx
DB, DW, DD, DQ, DT, DF, DUP, THISSTRUC, RECORD, WIDTH, MASKMACRO, REPT, IRC, IRP, ENDM 等ES:[BX])_emit 偽指令:相當於 DB,每次定義一 Byte,可構建字串。EVEN / ALIGN:指令對齊優化。/Zi 編譯選項支援原始碼級除錯;/FAs 輸出混合原始碼列表。| 可用元素 | 說明 |
|---|---|
| 符號 | 標號、變數名、函數名(需在作用域內) |
| 常數 | 包含 const、enum 成員 |
| 巨集/前置處理器 | #define 等 |
| 註解 | //, /* */, ; |
| 型別名稱 | 含 typedef 別名 |
C 中 array[6] 會自動乘上元素大小;組語中 [bx+6] 僅為位元組偏移。
int array[10];
__asm mov array[6], 0 ; 錯誤!寫入 array+6 bytes
__asm mov array[6 * TYPE int], 0 ; 正確!等同 C 的 array[6]=0
TYPE、SIZE、LENGTH 操作符可查詢 C/C++ 變數/型別大小資訊。
struct first_type { char *weasel; int same_name; } hal;
__asm {
mov ebx, OFFSET hal
mov ecx, [ebx]hal.same_name ; 必須加變數名限定(非唯一成員)
mov esi, [ebx].weasel ; 唯一成員可省略變數名
}
int power2(int num, int power) {
__asm {
mov eax, num ; 參數 1
mov ecx, power ; 參數 2
shl eax, cl ; eax *= 2^cl
}
; 無需 RET,編譯器自動生成結尾碼;返回值於 EAX
}
EAX;float 於 EAX,double 於 EDX:EAX。__asm {
push offset szWorld
push offset szHello
push offset szFormat
call printf
add esp, 12 ; 必須手動清理堆疊(C 呼叫慣例)
}
__asm {
push MB_OK
push offset szTitle
lea eax, szText
push eax
push 0
call DWORD PTR [MessageBoxA] ; 間接呼叫,API 為 stdcall 自動清堆疊
}
goto label 可跳往 __asm { label: };組語 jmp label 可跳往 C 標號。$ 符號代表當前位置,常用於短跳轉:JNE $+5。完整 Win32 視窗程式,內嵌兩段組語:
關鍵指令片段:
_NextByte:
mov dl, [ebx] ; 取資料位元組
xor dl, al ; 與 CRC 低位異或
movzx edx, dl ; 零擴展至 32 位
shr eax, 8 ; CRC 右移 8 位
xor eax, [dwCRC32Table + edx*4] ; 查表異或
inc ebx
loop _NextByte