实验1 Windows汇编语言开发环境
Windows、Linux等现代操作系统都运行于CPU的保护模式下。学习保护模式的汇编语言编程,要选用合适的编译、调试工具,编译工具决定了汇编程序的语法、结构,而调试工具则能够帮助我们迅速查找程序中的错误,提高调试效率。
本实验指导书采用Microsoft公司的MASM 6.14作为编译工具,Microsoft Visual C/C++作为开发调试环境。
1.1 汇编程序结构
和其他语言一样,汇编语言的源程序也要符合一定的格式,才能被编译程序所识别和处理。学习和掌握这些格式,是进行汇编编程的第一步。
1.1.1 一个显示字符串的汇编程序
下面是一个简单的汇编程序。
;程序清单:test.asm(在控制台上显示一个字符串)
.386
.model flat, stdcall
option casemap:none
; 说明程序中用到的库、函数原型和常量
includelib msvcrt.lib
printf PROTO C :ptr sbyte, :vararg
; 数据区
.data
szMsg byte “Hello World!”, 0ah, 0
; 代码区
.code
start:
mov eax, OFFSET szMsg
invoke printf, eax
ret
end start
1.1.2 程序格式
在源程序test.asm中,以分号(;)开始的行是注释行。注释行对程序的编译和执行没有影响。
一、模式定义
程序的第一部分是有关模式定义的3条语句:
.386
.model flat, stdcall
option casemap:none
这些语句定义了程序使用的指令集、工作模式。
(1) 指令集
.386语句是汇编语言的伪指令,说明本程序使用的指令集是哪一种CPU的。还可以使用:.8086、.186、.286、.386、.386p、.486、.486p、.586、.586p等。
后面带p的伪指令则表示程序中可以使用特权指令。
(2) 工作模式
.model语句用来定义程序工作的模式,它的格式是:
.model 内存模式[, 调用规则][, 其他模式]
内存模式的定义影响最后生成的可执行文件,可执行文件的规模可以有很多种类型,在Windows环境下,内存模式为flat,可执行文件最大可以用4 GB内存。
在test.asm中,.model语句指明使用stdcall调用规则。调用规则就是子程序的调用方式,即调用子程序时参数传递的次序和堆栈平衡的方法。
(3). option语句
option语句有许多选项,这里介绍一种:
option casemap:none
这条语句说明程序中的变量和子程序名是否对大小写敏感。对大小写敏感表示区分大写、小写形式,例如变量XYZ和xyz是两个不同的变量。对大小写不敏感则不区分大写、小写形式,变量XYZ和xyz是同一个变量。
由于Windows API函数中的函数名称是区分大小写的,所以应该指定这个选项“casemap:none”,否则在调用函数的时候会出现问题。
二、includelib语句
和C程序一样,在汇编程序中也需要调用一些外部模块(子程序/函数)来完成部分功能。例如,在hello.asm中,就需要调用printf函数将字符串显示在屏幕上。
printf函数属于C语言的库函数。它的执行代码放在一个动态链接库DLL(dynamic-load library)中,这个动态库的名字叫msvcrt.dll。
在汇编源程序中,需要用includelib语句指出库文件的名称,链接时LINK就从库文件中找出了函数的位置,避免出现上面的错误提示。这种库文件也叫导入库(import library)。例如:
includelib msvcrt.lib
一个DLL文件对应一个导入库,如msvcrt.dll的导入库是msvcrt.lib;kernel32.dll的导入库是kernel32.lib;user32.dll的导入库是user32.lib等。导入库文件在Visual C/C++的库文件目录中,在链接生成可执行文件时使用。
可执行文件执行时,只需要DLL文件,不需要导入库。
三、函数声明语句
对于所有要用到的库函数(或Windows API函数),在程序的开始部分必须预先声明。包括函数的名称、参数的类型等,如:
在汇编语言程序中,函数声明为:
函数名称 PROTO [调用规则] :[第一个参数类型] [,:后续参数类型]
其中,PROTO后的调用规则是可选项。如果不写,则使用model语句中指定的调用规则。
如果函数使用C调用规则,则PROTO后跟一个C。接下来是参数的说明。如果参数个数、类型不定,则用VARARG说明(varible argument)。
先看在C语言头文件stdio.h中printf的函数声明:
_CRTIMP int __cdecl printf(const char *, ...);
可知printf函数的调用规则为C调用规则(__cdecl, 即c declare),第一个参数是字符串指针,后面的参数数量及类型不定。
这里,用ptr sbyte代表const char *。
printf PROTO C :ptr sbyte,:vararg
四、数据和代码部分
程序中的数据部分和代码部分是分开定义的,数据部分从这一行开始:
.data
代码部分从这一行开始:
.code
遇到end语句时,代码部分结束。
end语句一般是整个程序的最后一条语句。end语句后面跟的是起始标号。它指出了程序执行的第一条指令的位置。在例子中,使用start作为起始标号,程序从start处开始执行。注意,程序并不一定要从代码部分的第一行开始执行。例如,start前面可以写一些子程序等。
end 起始标号
如果要定义堆栈部分,可以使用堆栈定义语句:
.stack [堆栈大小]
1.2 Visual C/C++环境
Microsoft Visual C/C++(简称VC)是一个典型的集成开发环境(IDE,integrated development environment),在国内外十分流行。集成开发环境大大地提高了程序开发过程的效率,而且它还能够动态地调试程序。除了可以编写调试C/C++程序外,VC还可以用来编辑、修改、编译、调试汇编程序。本书使用的版本是Microsoft Visual C/C++ 6.0。
1.2.1 建立工程
首先,按照以下步骤建立一个能编译、调试汇编程序的工程:
(1) 启动VC后,从菜单中选择“File”→“New”。
(2) 如图1-1所示,在打开的“New”对话框顶部,单击“Projects”,再选中“Win32 Console Application”。在Location编辑框中输入“c:/asm”,再在“Project name”中输入“test”。输入“test”时,它自动地添加到Location编辑框中“c:/asm”的后面。
图1-1 建立汇编程序工程之一
(3) 单击“OK”键后,出现一个新的对话框,单击“Finish”。
(4) 接下来,VC的窗口的左边显示出“test classes”,下面有“ClassView”和“FileView<