当前位置:首页 » 编程软件 » 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?寄存器(测试寄存器)用于某些条件测试。

热点内容
android设置静音 发布:2025-02-07 20:11:53 浏览:696
bin存储 发布:2025-02-07 20:00:50 浏览:202
android加载界面 发布:2025-02-07 19:55:28 浏览:870
好矿云服务器 发布:2025-02-07 19:54:31 浏览:949
java电话簿 发布:2025-02-07 19:49:26 浏览:797
超级脚本制作 发布:2025-02-07 19:31:30 浏览:487
怎么查看支付宝的账号密码 发布:2025-02-07 19:26:48 浏览:17
惠普服务器查看ip指令 发布:2025-02-07 19:26:47 浏览:435
算法设计模式 发布:2025-02-07 19:15:52 浏览:746
服务器1u能连接几台电脑 发布:2025-02-07 18:50:02 浏览:154