當前位置:首頁 » 編程軟體 » edi編譯器

edi編譯器

發布時間: 2023-07-05 09:52:30

A. 如何在各個版本的VC及64位下使用CPUID指令

、推薦使用__cpuid、__cpuidex等Intrinsics函數

32位模式我使用內嵌匯編調用cpuid指令64位模式VC編譯器支持內嵌匯編
於微軟提供Intrinsics函數——編譯器Intrinsics函數編譯應機器指令且同支持32位64位
例CPUID指令應Intrinsics函數——

[cpp] view plain
//
void __cpuid(
int CPUInfo[4],
int InfoType
);

void __cpuidex(
int CPUInfo[4],
int InfoType,
int ECXValue
);

__cpuidex函數InfoType參數CPUID指令eax參數即功能IDECXValue參數CPUID指畝備消令ecx參數即功能IDCPUInfo參數用於接收輸eax, ebx, ecx, edx四寄存器
早期CPUID功能需要功能ID參數(eax)使用__cpuid函數
CPUID功能越越強功能ID參數(eax)參數夠用於加功能ID(ecx)參數應該採用__cpuidex

二、用條件編譯判斷VC編譯器Intrinsics函數支持性(_MSC_VER)

__cpuid、__cpuidex等Intrinsics函數遇問題——
1.低版本VC編譯器沒intrin.h文件【注】:VC2005(或更高)才擁intrin.h支持__cpuid
2.低版本VC編譯器支持__cpuidex【注】:VC2008部版本及VS2011(或更高)intrin.h才__cpuidex

使用條件編譯判斷VC編譯器版本
_MSC_VER微軟C/C++編譯器——cl.exe編譯代碼預定義宏值表示cl版本類型int例——
#if _MSC_VER >=1200 // VC++6.0
#if _MSC_VER >=1300 // VC2003
#if _MSC_VER >=1400 // VC2005
#if _MSC_VER >=1500 // VC2008
#if _MSC_VER >滾鬧=1600 // VC2011

例發現_MSC_VER於等於1400我#include 再利用_MSC_VER進步判斷__cpuid、__cpuidex支持性

三、用條件編譯判斷64位模式(_WIN64)

使用_WIN64預處理宏用判斷目標平台64位
雖編譯x64平台程序編譯器自推導_WIN64Visual Studio語高亮清楚些能仍按32位代碼做語高亮所建議手項目預處理宏增加_WIN64

四、32位用內嵌匯編實現__cpuidex函數

32位模式我使用內嵌匯編實現__cpuidex函數代碼——

[cpp] view plain
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;
}
}

五、全部代碼

全部代碼——

[cpp] view plain
#include
#include
#include

#if _MSC_VER >=1400 // VC2005才支持intrin.h
#include // 所Intrinsics函數
#endif

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

#if defined(_WIN64)
// 64位支持內聯匯編. 應使用__cpuid、__cpuidex等Intrinsics函數
#else
#if _MSC_VER < 1600 // VS2011. 據說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 // VS2011. 據說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)

// 取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;
}

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);

return 0;
}

六、兼容性說明

VC編譯器32/64位支持性——
32位:VC6早支持編譯32位Intrinsics函數
64位:VC2005早支持編譯64位Intrinsics函數

本文32位編譯器兼容性——
__cpuid:兼容VC6(或更高)
__cpuidex:兼容VC6(或更高)

本文64位編譯器兼容性——
__cpuid:兼容VC2005(或更高)
__cpuidex:兼容VC2011(或更高)

B. C語言中怎麼嵌入匯編

在 Visual C++ 中使用內聯匯編- -

使用內聯匯編可以在 C/C++ 代碼中嵌入匯編語言指令,帶衫而且不需要額外的匯編和連接步驟。在 Visual C++ 中,內聯匯編是內置的編譯器,因此不需要配置諸如 MASM 一類的獨立匯編工具。這里,我們就以 Visual Studio .NET 2003 為背景,介紹在 Visual C++ 中使用內聯匯的相關知識(如果是早期的版本,可能會有些許出入)。

內聯匯編代碼可以使用 C/C++ 變數和函數,因此它能非常容易地整合到 C/C++ 代碼中。它能做一些對於單獨使用 C/C++ 來說非常笨重或不可能完成的任務。

一、 優點
使用內聯匯編可以蔽碧在 C/C++ 代碼中嵌入匯編語言指令,而且不需要額外的匯編和連接步驟。在 Visual C++ 中,內聯匯編是內置的編譯器,因此不需要配置諸如 MASM 一類的獨立匯編工具。這里,我們就以 Visual Studio .NET 2003 為背景,介紹在 Visual C++ 中使用內聯匯的相關知識(如果是早期的版本,可能會有些許出入)。

內聯匯編代碼可以使用 C/C++ 變數和函數,因此它能非常容易地整合到 C/C++ 代碼中。它能做一些對於單獨使用 C/C++ 來說非常笨重或不可能完成的任務。

內聯匯編的用途包括:

使用匯編語言編寫特定的函數;
編寫對速度要求非常較高的代碼;
在設備驅動程序中直接訪問硬體;
編寫 naked 函數的初始化和結束代碼。

二、 關鍵字

使用內聯匯編要用到 __asm 關鍵字,它可以出現在任何允許 C/C++ 語句出現的地方。我們來看一些例子:

簡單的 __asm 塊:
__asm
{
MOV AL, 2
MOV DX, 0xD007
OUT AL, DX
}

在每條匯編指令之前加 __asm 關鍵字:
__asm MOV AL, 2
__asm MOV DX, 0xD007
__asm OUT AL, DX

因為 __asm 關鍵字是語句分隔符,所以可以把多條蠢並腔匯編指令放在同一行:
__asm MOV AL, 2 __asm MOV DX, 0xD007 __asm OUT AL, DX

顯然,第一種方法與 C/C++ 的風格很一致,並且把匯編代碼和 C/C++ 代碼清楚地分開,還避免了重復輸入 __asm 關鍵字,因此推薦使用第一種方法。

不像在 C/C++ 中的"{ }",__asm 塊的"{ }"不會影響 C/C++ 變數的作用范圍。同時,__asm 塊可以嵌套,而且嵌套也不會影響變數的作用范圍。

為了與低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意義。另外,Visual C++ 支持標准 C++ 的 asm 關鍵字,但是它不會生成任何指令,它的作用僅限於使編譯器不會出現編譯錯誤。要使用內聯匯編,必須使用 __asm 而不是 asm 關鍵字。

三、 匯編語言

1. 指令集

內聯匯編支持 Intel Pentium 4 和 AMD Athlon 的所有指令。更多其它處理器的指令可以通過 _EMIT 偽指令來創建(_EMIT 偽指令說明見下文)。

2. MASM 表達式

在內聯匯編代碼中,可以使用所有的 MASM 表達式(MASM 表達式是指用來計算一個數值或一個地址的操作符和操作數的組合)。

3. 數據指示符和操作符

雖然 __asm 塊中允許使用 C/C++ 的數據類型和對象,但它不能使用 MASM 指示符和操作符來定義數據對象。這里特別指出,__asm 塊中不允許 MASM 中的定義指示符(DB、DW、DD、DQ、DT 和 DF),也不允許使用 DUP 和 THIS 操作符。MASM 中的結構和記錄也不再有效,內聯匯編不接受 STRUC、RECORD、WIDTH 或者 MASK。

4. EVEN 和 ALIGN 指示符

盡管內聯匯編不支持大多數 MASM 指示符,但它支持 EVEN 和 ALIGN。當需要的時候,這些指示符在匯編代碼裡面加入 NOP 指令(空操作)使標號對齊到特定邊界。這樣可以使某些處理器取指令時具有更高的效率。

5. MASM 宏指示符

內聯匯編不是宏匯編,不能使用 MASM 宏指示符(MACRO、REPT、IRC、IRP 和 ENDM)和宏操作符(>、!、&、% 和 .TYPE)。

6. 段

必須使用寄存器而不是名稱來指明段(段名稱"_TEXT"是無效的)。並且,段跨越必須顯式地說明,如 ES:[EBX]。

7. 類型和變數大小

在內聯匯編中,可以用 LENGTH、SIZE 和 TYPE 來獲取 C/C++ 變數和類型的大大小。
* LENGTH 操作符用來取得 C/C++ 中數組的元素個數(如果不是一個數組,則結果為 1)。
* SIZE 操作符可以獲取 C/C++ 變數的大小(一個變數的大小是 LENGTH 和 TYPE 的乘積)。
* TYPE 操作符可以返回 C/C++ 類型和變數的大小(如果變數是一個數組,它得到的是數組中單個元素的大小)。

例如,程序中定義了一個 8 維的整數型變數:

int iArray[8];

下面是 C 和匯編表達式中得到的 iArray 及其元素的相關值:

__asm C Size

LENGTH iArray sizeof(iArray)/sizeof(iArray[0]) 8
SIZE iArray sizeof(iArray) 32
TYPE iArray sizeof(iArray[0]) 4

8. 注釋

內聯匯編中可以使用匯編語言的注釋,即";"。例如:

__asm MOV EAX, OFFSET pbBuff ; Load address of pbBuff

因為 C/C++ 宏將會展開到一個邏輯行中,為了避免在宏中使用匯編語言注釋帶來的混亂,內聯匯編也允許使用 C/C++ 風格的注釋。

9. _EMIT 偽指令

_EMIT 偽指令相當於 MASM 中的 DB,但是 _EMIT 一次只能在當前代碼段(.text 段)中定義一個位元組。例如:

__asm
{
JMP _CodeLabel

_EMIT 0x00 ; 定義混合在代碼段的數據
_EMIT 0x01

_CodeLabel: ; 這里是代碼
_EMIT 0x90 ; NOP指令
}

10. 寄存器使用

一般來說,不能假定某個寄存器在 __asm 塊開始的時候有已知的值。寄存器的值將不能保證會從 __asm 塊保留到另外一個 __asm 塊中。

如果一個函數聲明為 __fastcall 調用方式,則其參數將通過寄存器而不是堆棧來傳遞。這將會使 __asm 塊產生問題,因為函數無法被告知哪個參數在哪個寄存器中。如果函數接收了 EAX 中的參數並立即儲存一個值到 EAX 中的話,原來的參數將丟失掉。另外,在所有聲明為 __fastcall 的函數中,ECX 寄存器是必須一直保留的。為了避免以上的沖突,包含 __asm 塊的函數不要聲明為 __fastcall 調用方式。

提示:如果使用 EAX、EBX、ECX、EDX、ESI 和 EDI 寄存器,你不需要保存它。但如果你用到了 DS、SS、SP、BP 和標志寄存器,那就應該用 PUSH 保存這些寄存器。
提示:如果程序中改變了用於 STD 和 CLD 的方向標志,必須將其恢復到原來的值。

四、 使用 C/C++ 元素

1. 可用的 C/C++ 元素

C/C++ 與匯編語言可以混合使用,在內聯匯編中可以使用 C/C++ 變數以及很多其它的 C/C++ 元素,包括:

符號,包括標號、變數和函數名;
常量,包括符號常量和枚舉型成員;
宏定義和預處理指示符;
注釋,包括"/**/"和"//";
類型名,包括所有 MASM 中合法的類型;
typedef 名稱,通常使用 PTR 和 TYPE 操作符,或者使用指定的的結構或枚舉成員。
在內聯匯編中,可以使用 C/C++ 或匯編語言的基數計數法。例如,0x100 和 100H 是相等的。

2. 操作符使用

內聯匯編中不能使用諸如"<<"一類的 C/C++ 操作符。但是,C/C++ 和 MASM 共有的操作符(比如"*"和"[]"操作符),都被認為是匯編語言的操作符,是可以使用的。舉個例子:

int iArray[10];

__asm MOV iArray[6], BX ; Store BX at iArray + 6 (Not scaled)
iArray[6] = 0; // Store 0 at iArray+12 (Scaled)

提示:在內聯匯編中,可以使用 TYPE 操作符使其與 C/C++ 一致。比如,下面兩條語句是一樣的:
__asm MOV iArray[6 * TYPE int], 0 ; Store 0 at iArray + 12
iArray[6] = 0; // Store 0 at iArray + 12

3. C/C++ 符號使用

在 __asm 塊中可以引用所有在作用范圍內的 C/C++ 符號,包括變數名稱、函數名稱和標號。但是不能訪問 C++ 類的成員函數。

下面是在內聯匯編中使用 C/C++ 符號的一些限制:

每條匯編語句只能包含一個 C/C++ 符號。在一條匯編指令中,多個符號只能出現在 LENGTH、TYPE 或 SIZE 表達式中。
在 __asm 塊中引用函數必須先聲明。否則,編譯器將不能區別 __asm 塊中的函數名和標號。
在 __asm 塊中不能使用對於 MASM 來說是保留字的 C/C++ 符號(不區分大小寫)。MASM 保留字包含指令名稱(如 PUSH)和寄存器名稱(如 ESI)等。
在 __asm 塊中不能識別結構和聯合標簽。
4. 訪問 C/C++ 中的數據

內聯匯編的一個非常大的方便之處是它可以使用名稱來引用 C/C++ 變數。例如,如果 C/C++ 變數 iVar 在作用范圍內:

__asm MOV EAX, iVar ; Stores the value of iVar in EAX

如果 C/C++ 中的類、結構或者枚舉成員具有唯一的名稱,則在 __asm 塊中可以只通過成員名稱來訪問(省略"."操作符之前的變數名或 typedef 名稱)。然而,如果成員不是唯一的,你必須在"."操作符之前加上變數名或 typedef 名稱。例如,下面的兩個結構都具有 SameName 這個成員變數:

struct FIRST_TYPE
{
char *pszWeasel;
int SameName;
};

struct SECOND_TYPE
{
int iWonton;
long SameName;
};

如果按下面方式聲明變數:

struct FIRST_TYPE ftTest;
struct SECOND_TYPE stTemp;

那麼,所有引用 SameName 成員的地方都必須使用變數名,因為 SameName 不是唯一的。另外,由於上面的 pszWeasel 變數具有唯一的名稱,你可以僅僅使用它的成員名稱來引用它:

__asm
{
MOV EBX, OFFSET ftTest
MOV ECX, [EBX]ftTest.SameName ; 必須使用"ftTest"
MOV ESI, [EBX]. pszWeasel ; 可以省略"ftTest"
}

提示:省略變數名僅僅是為了書寫代碼方便,生成的匯編指令還是一樣的。
5. 用內聯匯編寫函數

如果用內聯匯編寫函數的話,要傳遞參數和返回一個值都是非常容易的。看下面的例子,比較一下用獨立匯編和內聯匯編寫的函數:

; PowerAsm.asm
; Compute the power of an integer

PUBLIC GetPowerAsm
_TEXT SEGMENT WORD PUBLIC 'CODE'
GetPowerAsm PROC
PUSH EBP ; Save EBP
MOV EBP, ESP ; Move ESP into EBP so we can refer
; to arguments on the stack
MOV EAX, [EBP+4] ; Get first argument
MOV ECX, [EBP+6] ; Get second argument
SHL EAX, CL ; EAX = EAX * (2 ^ CL)
POP EBP ; Restore EBP
RET ; Return with sum in EAX
GetPowerAsm ENDP
_TEXT ENDS
END

C/C++ 函數一般用堆棧來傳遞參數,所以上面的函數中需要通過堆棧位置來訪問它的參數(在 MASM 或其它一些匯編工具中,也允許通過名稱來訪問堆棧參數和局部堆棧變數)。

下面的程序是使用內聯匯編寫的:

// PowerC.c

#include

int GetPowerC(int iNum, int iPower);

int main()
{
printf("3 times 2 to the power of 5 is %d\n", GetPowerC( 3, 5));
}

int GetPowerC(int iNum, int iPower)
{
__asm
{
MOV EAX, iNum ; Get first argument
MOV ECX, iPower ; Get second argument
SHL EAX, CL ; EAX = EAX * (2 to the power of CL)
}
// Return with result in EAX
}

使用內聯匯編寫的 GetPowerC 函數可以通過參數名稱來引用它的參數。由於 GetPowerC 函數沒有執行 C 的 return 語句,所以編譯器會給出一個警告信息,我們可以通過 #pragma warning 禁止生成這個警告。

內聯匯編的其中一個用途是編寫 naked 函數的初始化和結束代碼。對於一般的函數,編譯器會自動幫我們生成函數的初始化(構建參數指針和分配局部變數等)和結束代碼(平衡堆棧和返回一個值等)。使用內聯匯編,我們可以自己編寫乾乾凈凈的函數。當然,此時我們必須自己動手做一些有關函數初始化和掃尾的工作。例如:

void __declspec(naked) MyNakedFunction()
{
// Naked functions must provide their own prolog.
__asm
{
PUSH EBP
MOV ESP, EBP
SUB ESP, __LOCAL_SIZE
}

.
.
.

// And we must provide epilog.
__asm
{
POP EBP
RET
}
}

6. 調用 C/C++ 函數

內聯匯編中調用聲明為 __cdecl 方式(默認)的 C/C++ 函數必須由調用者清除參數堆棧,下面是一個調用 C/C++ 函數例子:

#include

char szFormat[] = "%s %s\n";
char szHello[] = "Hello";
char szWorld[] = " world";

void main()
{
__asm
{
MOV EAX, OFFSET szWorld
PUSH EAX
MOV EAX, OFFSET szHello
PUSH EAX
MOV EAX, OFFSET szFormat
PUSH EAX
CALL printf

// 壓入了 3 個參數在堆棧中,調用函數之後要調整堆棧
ADD ESP, 12
}
}

提示:參數是按從右往左的順序壓入堆棧的。
如果調用 __stdcall 方式的函數,則不需要自己清除堆棧。因為這種函數的返回指令是 RET n,會自動清除堆棧。大多數 Windows API 函數均為 __stdcall 調用方式(僅除 wsprintf 等幾個之外),下面是一個調用 MessageBox 函數的例子:

#include

TCHAR g_tszAppName[] = TEXT("API Test");

void main()
{
TCHAR tszHello[] = TEXT("Hello, world!");

__asm
{
PUSH MB_OK OR MB_ICONINFORMATION
PUSH OFFSET g_tszAppName ; 全局變數用 OFFSET
LEA EAX, tszHello ; 局部變數用 LEA
PUSH EAX
PUSH 0
CALL DWORD PTR [MessageBox] ; 注意這里不是 CALL MessageBox,而是調用重定位過的函數地址
}
}

提示:可以不受限制地訪問 C++ 成員變數,但是不能訪問 C++ 的成員函數。
7. 定義 __asm 塊為 C/C++ 宏

使用 C/C++ 宏可以方便地把匯編代碼插入到源代碼中。但是這其中需要額外地注意,因為宏將會擴展到一個邏輯行中。
為了不會出現問題,請按以下規則編寫宏:

使用花括弧把 __asm 塊包圍住;
把 __asm 關鍵字放在每條匯編指令之前;
使用經典 C 風格的注釋("/* comment */"),不要使用匯編風格的注釋("; comment")或單行的 C/C++ 注釋("// comment");
舉個例子,下面定義了一個簡單的宏:

#define PORTIO __asm \
/* Port output */ \
{ \
__asm MOV AL, 2 \
__asm MOV DX, 0xD007 \
__asm OUT DX, AL \
}

乍一看來,後面的三個 __asm 關鍵字好像是多餘的。其實它們是需要的,因為宏將被擴展到一個單行中:

__asm /* Port output */ { __asm MOV AL, 2 __asm MOV DX, 0xD007 __asm OUT DX, AL }

從擴展後的代碼中可以看出,第三個和第四個 __asm 關鍵字是必須的(作為語句分隔符)。在 __asm 塊中,只有 __asm 關鍵字和換行符會被認為是語句分隔符,又因為定義為宏的一個語句塊會被認為是一個邏輯行,所以必須在每條指令之前使用 __asm 關鍵字。

括弧也是需要的,如果省略了它,編譯器將不知道匯編代碼在哪裡結束,__asm 塊後面的 C/C++ 語句看起來會被認為是匯編指令。

同樣是由於宏展開的原因,匯編風格的注釋("; comment")和單行的 C/C++ 注釋("// commen")也可能會出現錯誤。為了避免這些錯誤,在定義 __asm 塊為宏時請使用經典 C 風格的注釋("/* comment */")。

和 C/C++ 宏一樣 __asm 塊寫的宏也可以擁有參數。和 C/C++ 宏不一樣的是,__asm 宏不能返回一個值,因此,不能使用這種宏作為 C/C++ 表達式。

不要不加選擇地調用這種類型的宏。比如,在聲明為 __fastcall 的函數中調用匯編語言宏可能會導致不可預料的結果(請參看前文的說明)。

8. 轉跳

可以在 C/C++ 裡面使用 goto 轉跳到 __asm 塊中的標號處,也可以在 __asm 塊中轉跳到 __asm 塊裡面或外面的標號處。__asm 塊內的標號是不區分大小寫的(指令、指示符等也是不區分大小寫的)。例如:

void MyFunction()
{
goto C_Dest; /* 正確 */
goto c_dest; /* 錯誤 */

goto A_Dest; /* 正確 */
goto a_dest; /* 正確 */

__asm
{
JMP C_Dest ; 正確
JMP c_dest ; 錯誤

JMP A_Dest ; 正確
JMP a_dest ; 正確

a_dest: ; __asm 標號
}

C_Dest: /* C/C++ 標號 */
return;
}

不要使用函數名稱當作標號,否則將轉跳到函數中執行,而不是標號處。例如,由於 exit 是 C/C++ 的函數,下面的轉跳將不會到 exit 標號處:

; 錯誤:使用函數名作為標號
JNE exit
.
.
.
exit:
.
.
.

美元符號"$"用於指定當前指令位置,常用於條件跳轉中,例如:

JNE $+5 ; 下面這條指令的長度是 5 個位元組
JMP _Label
NOP ; $+5,轉跳到了這里
.
.
.
_Label:
.
.
.

五、在 Visual C++ 工程中使用獨立匯編

內聯匯編代碼不易於移植,如果你的程序打算在不同類型的機器(比如 x86 和 Alpha)上運行,你可能需要在不同的模塊中使用特定的機器代碼。這時候你可以使用 MASM(Microsoft Macro Assembler),因為 MASM 支持更多方便的宏指令和數據指示符。

這里簡單介紹一下在 Visual Studio .NET 2003 中調用 MASM 編譯獨立匯編文件的步驟。

在 Visual C++ 工程中,添加按 MASM 的要求編寫的 .asm 文件。在解決方案資源管理器中,右擊這個文件,選擇"屬性"菜單項,在屬性對話框中,點擊"自定義生成步驟",設置如下項目:

命令行:ML.exe /nologo /c /coff "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)"
輸出:$(IntDir)\$(InputName).obj

如果要生成調試信息,可以在命令行中加入"/Zi"參數,還可以根據需要生成 .lst 和 .sbr 文件。

如果要在匯編文件中調用 Windows API,可以從網上下載 MASM32 包(包含了 MASM 匯編工具、非常完整的 Windows API 頭文件/庫文件、實用宏以及大量的 Win32 匯編例子等)。相應地,應該在命令行中加入"/I X:\MASM32\INCLUDE"參數指定 Windows API 匯編頭文件(.inc)的路徑。MASM32 的主頁是:http://www.masm32.com,裡面可以下載最新版本的 MASM32 包。

C. 寄存器的具體舉例

UxCTL寄存器是一個8位的寄存器。UASRT模塊的基本操作由該寄存器的控制位確定的,它包含了通信協議、通信模式和校驗位等的選擇。圖給出了寄存器的各個位。
圖UxCTL寄存器
由圖可以看出,UxCTL寄存器主要包括8個有效的控制位。為了增加對UxCTL寄存器的了解,知道怎樣對該寄存器進行正確的設置,下面對UxCTL寄存器的各個位進行詳細介紹。
PENA:校驗使能位。當該位為0時,不允許校驗;當該位為1時,允許校驗。如果允許校驗,則發送時產生校驗位,在接收時希望接收到校驗位。.當在地址位多機模式中¨地址位包括在校驗計算中。
PEV:奇偶校驗位。當該位為0時,進行奇校驗;當該位為1時,進行偶校驗。
SPB:停止位。該位用來選擇發送時停止位的個數,但接收時停止位只有一個。當該位為0時,發送時只有1個停止位;當該位為1時,發送時有2個停止位。
CHAR:字元長度位。該位用來選擇發送時數據的長度。當該位為0時,發送的數據為7位;當該位為1時,發送的數據為8位。
LISTEN:監聽使能位。該位用來選擇反饋模式。當該位為0時,沒有反饋;當該位為1時,有反饋,發送的數據被送到接收器,這樣可以進行自環測試。
SYNC:該位用於同步模式選擇和非同步模式選擇。當該位為0時,USART模塊為非同步通信(UART)模式;當該位為1時,USART模塊為同步通信(SPI)模式。
MM:多機模式選擇位。當該位為0時,多機模式選擇線路空閑多機協議;當該位為1時,多機模式選擇地址位多機協議。
SWRST:軟體復位使能位。當該位為0時,UASRT模塊被允許;當該位為1時,UASRT模塊被禁止。
通過以上對UxCTL寄存器的各個位的介紹,可以完成對通信模式和通信數據格式等的選擇。 顧名思義,通用寄存器是那些你可以根據自己的意願使用的寄存器,修改他們的值通常不會對計算機的運行造成很大的影響。通用寄存器最多的用途是計算。
EAX:通用寄存器。相對其他寄存器,在進行運算方面比較常用。在保護模式中,也可以作為內存偏移指針(此時,DS作為段寄存器或選擇器)
EBX:通用寄存器。通常作為內存偏移指針使用(相對於EAX、ECX、EDX),DS是默認的段寄存器或選擇器。在保護模式中,同樣可以起這個作用。
ECX:通用寄存器。通常用於特定指令的計數。在保護模式中,也可以作為內存偏移指針(此時,DS作為寄存器或段選擇器)。
EDX:通用寄存器。在某些運算中作為EAX的溢出寄存器(例如乘、除)。
同AX分為AH&AL一樣,上述寄存器包括對應的16-bit分組和8-bit分組。 ESI:通常在內存操作指令中作為「源地址指針」使用。當然,ESI可以被裝入任意的數值,但通常沒有人把它當作通用寄存器來用。DS是默認段寄存器或選擇器。
EDI:通常在內存操作指令中作為「目的地址指針」使用。當然,EDI也可以被裝入任意的數值,但通常沒有人把它當作通用寄存器來用。ES是默認段寄存器或選擇器。
EBP和ESP:作為指針的寄存器,也可作為16位寄存器BP, SP使用,常用於椎棧操作。通常,它被高級語言編譯器用以建造『堆棧幀'來保存函數或過程的局部變數,不過,還是那句話,你可以在其中保存你希望的任何數據。SS是它的默認段寄存器或選擇器。
注意,這四個寄存器沒有對應的8-bit分組。換言之,你可以通過SI、DI、BP、SP作為別名訪問他們的低16位,卻沒有辦法直接訪問他們的低8位。 實模式下的段寄存器到保護模式下搖身一變就成了選擇器。不同的是,實模式下的「段寄存器」是16-bit的,而保護模式下的選擇器是32-bit的。
CS代碼段,或代碼選擇器。同IP寄存器(稍後介紹)一同指向當前正在執行的那個地址。處理器執行時從這個寄存器指向的段(實模式)或內存(保護模式)中獲取指令。除了跳轉或其他分支指令之外,你無法修改這個寄存器的內容。
DS數據段,或數據選擇器。這個寄存器的低16 bit連同ESI一同指向的指令將要處理的內存。同時,所有的內存操作指令默認情況下都用它指定操作段(實模式)或內存(作為選擇器,在保護模式。這個寄存器可以被裝入任意數值,然而在這么做的時候需要小心一些。方法是,首先把數據送給AX,然後再把它從AX傳送給DS(當然,也可以通過堆棧來做).
ES 附加段,或附加選擇器。這個寄存器的低16 bit連同EDI一同指向的指令將要處理的內存。同樣的,這個寄存器可以被裝入任意數值,方法和DS類似。
FS F段或F選擇器(推測F和下面的G正好是上面CS,DS,ES的字母順延)。可以用這個寄存器作為默認段寄存器或選擇器的一個替代品。它可以被裝入任何數值,方法和DS類似。
GS G段或G選擇器(G的意義和F一樣,沒有在Intel的文檔中解釋)。它和FS幾乎完全一樣。
SS堆棧段或堆棧選擇器。這個寄存器的低16 bit連同ESP一同指向下一次堆棧操作(push和pop)所要使用的堆棧地址。這個寄存器也可以被裝入任意數值,你可以通過入棧和出棧操作來給他賦值,不過由於堆棧對於很多操作有很重要的意義,因此,不正確的修改有可能造成對堆棧的破壞。
* 注意一定不要在初學匯編的階段把這些寄存器弄混。他們非常重要,而一旦你掌握了他們,你就可以對他們做任意的操作了。段寄存器,或選擇器,在沒有指定的情況下都是使用默認的那個。這句話在現在看來可能有點稀里糊塗,不過你很快就會在後面知道如何去做。 EIP 這個寄存器非常的重要。這是一個32位寬的寄存器,同CS一同指向即將執行的那條指令的地址,存放指令的偏移地址。微處理器工作於實模式下,EIP是IP(16位)寄存器。不能夠直接修改這個寄存器的值,修改它的唯一方法是跳轉或分支指令。(CS是默認的段或選擇器)
E、標志寄存器EFR
EFR(extra flags register)包括狀態位、控制位和系統標志位,用於指示微處理器的狀態並控制微處理器的操作。80486 CPU標志寄存器如圖2.12所示。
①狀態標志位:包括進位標志CF、奇偶標志PF、輔助進位標志AF、零標志ZF 、符號標志SF和溢出標志OF。
② 控制標志位:包括陷阱標志(單步操作標志)TF、中斷標志IF和方向標志DF。80486 CPU標志寄存器中的狀態標志位和控制標志位與8086 CPU標志寄存器中的狀態標志位和控制標志位的功能完全一樣,這里就不再贅述。
③ 系統標志位和IOPL欄位:在EFR寄存器中的系統標志和IOPL欄位,用於控制操作系統或執行某種操作。它們不能被應用程序修改。
IOPL(I/O privilege level field):輸入/輸出特權級標志位。它規定了能使用I/O敏感指令的特權級。在保護模式下,利用這兩位編碼可以分別表示0, 1, 2, 3這四種特權級,0級特權最高,3級特權最低。在80286以上的處理器中有一些I/O敏感指令,如CLI(關中斷指令)、STI(開中斷指令)、IN(輸入)、OUT(輸出)。IOPL的值規定了能執行這些指令的特權級。只有特權高於IOPL的程序才能執行I/O敏感指令,而特權低於IOPL的程序,若企圖執行敏感指令,則會引起異常中斷。
NT(nested task flag):任務嵌套標志。在保護模式下,指示當前執行的任務嵌套於另一任務中。當任務被嵌套時,NT=1,否則NT=0。
RF(resume flag):恢復標志。與調試寄存器一起使用,用於保證不重復處理斷點。當RF=1時,即使遇到斷點或故障,也不產生異常中斷。
VM(virtual 8086 mode flag):虛擬8086模式標志。用於在保護模式系統中選擇虛擬操作模式。VM=1,啟用虛擬8086模式;VM=0,返回保護模式。
AC(alignment check flag):隊列檢查標志。如果在不是字或雙字的邊界上定址一個字或雙字,隊列檢查標志將被激活。 上面是最基本的寄存器。下面是一些其他的寄存器,你甚至可能沒有聽說過它們。(都是32位寬):
CR0, CR2, CR3(控制寄存器)。舉一個例子,CR0的作用是切換實模式和保護模式。
還有其他一些寄存器,D0, D1, D2, D3, D6和D7(調試寄存器)。他們可以作為調試器的硬體支持來設置條件斷點。
TR3, TR4, TR5, TR6 和TR?寄存器(測試寄存器)用於某些條件測試。

熱點內容
我的世界怎麼進2s2t伺服器 發布:2025-02-07 23:08:47 瀏覽:924
丁霞訪問 發布:2025-02-07 22:56:19 瀏覽:855
java中set集合 發布:2025-02-07 22:43:34 瀏覽:31
播放這個wifi密碼是多少 發布:2025-02-07 22:34:54 瀏覽:100
視頻存儲時間長了有雪花 發布:2025-02-07 22:24:34 瀏覽:569
哈佛f7x怎麼區分配置 發布:2025-02-07 22:22:34 瀏覽:772
廣州python培訓 發布:2025-02-07 22:22:26 瀏覽:200
陸金所的交易密碼是什麼 發布:2025-02-07 22:19:25 瀏覽:321
如何刪除平板儲存密碼 發布:2025-02-07 22:10:29 瀏覽:748
php微信授權登錄 發布:2025-02-07 22:10:27 瀏覽:379