购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.3.3 源程序框架

汇编程序为汇编语言制定了严格的语法规范,例如,语句格式、标识符定义、保留字、注释符等。同样,汇编程序也为源程序书写设计了框架结构,包括数据段和代码段等的定义、程序起始执行的位置、汇编结束的标识等。

MASM各版本支持多种汇编语言源程序格式。本书使用MASM 6.x版本的简化段定义格式,利用作者创建的包含文件和子程序库,引出一个简单的源程序框架。程序模板如下:

随着教学内容的深入,本书将逐渐展开源程序框架的每个部分。学习初期,大家可以暂时不去深究。在后面的大部分示例程序中,本书也只是表明如何在数据段中定义数据以及如何在代码段中编写程序。大家只要根据这个程序模板(EG0000.ASM)填入内容就可以形成源程序文件。另外请大家注意,利用这个程序模板需要在当前目录保存有本书提供的IO32.INC和IO32.LIB文件。

1.包含伪指令

MASM提供源文件包含伪指令INCLUDE,用于声明常用的常量定义、过程说明、共享的子程序库等内容(相当于C和C++语言中包含头文件的作用)。IO32.INC是配合本书的包含文件,它是文本类型的文件,可以用任何一个文本编辑软件打开,它的主要作用是声明用于键盘输入和显示器输出的子程序,并给出必要的说明性语句。其中前3个语句是:

第一个语句是MASM汇编程序的处理器选择伪指令,声明采用Pentium Pro(原被称为80686处理器)支持的指令系统。这是因为,MASM在默认情况下只汇编16位8086处理器的指令,如果程序员需要使用80186及以后处理器增加的指令,必须使用处理器选择伪指令,如表1-3所示。本书讲解IA-32处理器,所以源程序必须加上“.386”或其他32位处理器的选择伪指令。

表1-3 处理器选择伪指令

注:.586/.586P由MASM 6.11引入;
.686/.686P/.MMX由MASM 6.12引入;
.K3D由MASM 6.13引入;
.XMM由MASM 6.14引入,MASM 6.15支持SSE2指令,MASM 8.0支持SSE3指令。

第二个语句“.MODEL”确定程序采用的存储模型。编写Windows操作系统下的32位程序时,只能选择FLAT(平展)模型。如果编写DOS操作系统下的应用程序,还可以选择其他6种模型:小型程序选择SMALL模型、大型程序选择LARGE模型,还有TINY(微型)、MEDIUM(中型)、COMPACT(紧凑)和HUGE(巨型)模型。

程序需要使用Windows提供的系统函数,它的应用程序接口(Application Program Interface,API)采用标准调用规范“STDCALL”。MASM汇编程序还支持C语言调用规范,其关键字是“C”。

使用简化段定义的源程序格式,必须有存储模型语句,且位于所有简化段定义语句之前。另外,程序默认采用32位地址和操作数长度,需要将.386或其他32位处理器选择伪指令书写在MODEL存储模型语句之前。如果将32位处理器选择伪指令书写在存储模型语句之后,该程序将默认采用16位地址和操作数长度。

MASM汇编语言默认不区分大小写,但选项伪指令“OPTION CASEMAP:NONE”告知MASM要区分标识符的大小写,这是因为Windows的API函数区分大小写。汇编程序ML.EXE的参数“/Cp”具有同样的效果,也是告知MASM不要更改用户定义的标识符的大小写。在这种情况下,虽然MASM保留字使用大小写均可,但用户自定义的符号不能随意使用大小写。用户可以按照高级语言的要求使用标识符,本书的汇编语言程序中基本都使用小写字母。

2.段的简化定义

对应存储空间的分段管理,用汇编语言编程时也常将源程序分成代码段、数据段或堆栈段。需要独立运行的程序必须包含一个代码段,并指示程序执行的起始位置。需要执行的可执行性语句必须位于某一个代码段内。说明性语句通常放在数据段,或根据需要位于其他段。

在简化段定义(Simplified Segment Definition)的源程序格式中,“.DATA”和“.CODE”伪指令分别定义了数据段和代码段,一个段的开始自动结束上一个段。堆栈段用伪指令“.STACK”创建。通常堆栈由Windows操作系统维护,用户不必设置,但如果程序使用的堆栈空间较大,也可以设置。

3.程序的开始和结束

程序模板中定义了一个标号START(也可以使用其他标识符),最后的汇编结束END指令需要利用它作为参数,以指明程序开始执行的位置。

应用程序执行终止,应该将控制权交还操作系统。另外,还可以给操作系统提供一个返回代码,可以用语句“EXIT 0”实现此功能,其中数值0就是返回代码,通常用0表示执行正确。实际上,这个“EXIT 0”语句是包含文件IO32.INC中的一个宏,汇编时将被替换为如下两条指令:

其中,RET是返回指令,通过EAX提供返回操作系统的代码。源程序框架完全可以用这两条指令替代“EXIT 0”语句。

源程序的最后需要有一条汇编结束语句,这之后的语句不会被汇编程序所汇编。所以,汇编结束表示汇编程序到此结束将源程序翻译成目标模块代码的过程,而不是指程序终止执行。END伪指令后面可以有一个“标号”性质的参数,用于指定程序开始执行于该标号所指示的指令。

现在用上述程序格式,编写一个在屏幕上显示信息的程序。

[例1-1]信息显示程序(使用输入/输出子程序)

大家一定记得学习C语言时编写的第一个问候世界(Hello,world!)的显示程序,只要使用语句:

就可以实现。

在汇编语言中,要显示的字符串需要在数据段进行定义,采用字节定义伪指令DB实现:

其中,参数13和10是回车和换行控制字符,对应C语句中的“\ n”,实现光标回到下行首列的功能。而汇编语言定义的字符串最后有一个0作为结尾字符,这是不可少的。在C语句中,虽然没有显式写出这个0,但C语言编译程序会自动为每个字符串在最后添加这个结尾字符,因此实际上存在这个结尾字符。汇编程序并不区别字符或字符串,不会为字符串自动添加结尾字符,所以,程序员需要自行定义结尾字符。

对应C语言printf函数的功能,汇编语言需要在代码段编写显示字符串的程序:

这里使用了字符串显示子程序DISPMSG,它需要在调用前设置EAX等于字符串在主存的偏移地址(详见“输入/输出子程序库”部分)。

将语句纳入主函数,形成完整的源程序文件,就是经典的C语言信息显示程序:

同样,将上述汇编语言语句填入程序模板(EG0000.ASM)预留的位置,即将数据段内容填入数据段定义指令.DATA之后,将代码段内容填入程序的START标号之后,就编制了一个完整的MASM汇编语言源程序EG0101.ASM(除非有特别说明,否则本书示例程序都可以如此处理):

特别提醒大家,本书前5章的例题程序都将如此处理,教材只给出数据段的变量定义、主程序和子程序代码等部分(除非有特别说明),以便将注意力集中于编程本身(而不是被烦琐的程序格式所困扰)。读者可以基于模板文件的源程序框架创建完整的源程序文件,也可以参考配套软件包中提供的所有例题程序的源程序文件。

现在,绝大多数读者都是从高级语言开始熟悉计算机程序设计的。虽然汇编语言不是高级语言,但两者都是程序设计语言,有许多本质上相同或相通的方面。所以,读者在学习过程中不妨做些简单对比,这样,既可以巩固高级语言的知识,也有利于熟悉汇编语言,通过汇编语言还可以进一步加深对高级语言的理解。

4.输入/输出子程序库

使用一种编程语言进行程序设计时,程序员需要利用其开发环境提供的各种功能,如函数、程序库。如果这些功能无法满足程序员的要求,还可以直接利用操作系统提供的程序库,再不行的话就只有自己编写特定的程序了。

汇编语言作为一种低级程序设计语言,通常并没有提供任何函数或程序库,所以必须利用操作系统的编程资源。显然,这是进行程序设计尤其是采用汇编语言进行程序设计应该掌握的技巧。但是,对于初学者来说,就有些勉为其难了。这是因为,Windows的系统函数是为了方便高级语言调用而编写的,本身比较复杂,需要了解较多内容才能在汇编语言中使用。为此,本书提供了一个简单易用的输入/输出(I/O)子程序库,实现主要的键盘输入和显示器输出功能。

开发32位Windows控制台应用程序使用IO32.LIB子程序库文件和IO32.INC包含文件,注意在源程序开始时使用包含命令INCLUDE声明。常用的(I/O)子程序如表1-4所示,详见附录B。本书的输入/输出子程序库以READ开头表示键盘输入、以DISP开头表示显示器输出,通过后缀字母区别数据类型。C语言支持格式输入scanf和格式输出printf函数,利用格式符控制输入/输出数据的类型,表中进行了对比,其中a表示相应类型的变量。

表1-4 常用I/O子程序

注:在16位编译程序中,需要在格式符X、u和d前加一个字母“l”,表示长整型(long)类型,即32位。

调用外部变量、子程序、函数等时,汇编语言需要使用EXTERN伪指令声明其来自外部。由于输入/输出子程序较多,为免去重复声明,IO32.INC文件一并进行了声明,例如对字符和字符串的输入/输出子程序的声明语句如下:

其中,NEAR是调用的类型属性。声明后就可以进行调用,格式如下:

例如,在当前光标位置显示字符串的功能是DISPMSG子程序。使用这个子程序,需要定义以0结尾的字符串,调用前将EAX赋值为该字符串的偏移地址,使用CALL指令实现调用:

在学习初期,大家可以利用这个子程序库实现简单的输入/输出。在后续章节中,本书将展开介绍这些子程序的编写技术和方法。最后,大家还可以补充和完善这个子程序库,或者创建其他子程序库。

5.C语言标准函数

C语言有一系列的标准函数,称为C标准库(C Standard Library)。例如,printf函数和scanf函数是C语言常用的格式化输出和输入函数,它们存放在MSVCRT.DLL动态链接库中,开发时需要使用导入库MSVCRT.LIB。汇编语言也可以调用C语言标准函数实现键盘输入和显示器输出。本书的软件包在BIN目录下提供MSVCRT.LIB文件,通过连接程序的参数“/defaultlib:msvcrt.lib”指明。不过,源程序中还需要声明这些函数来自外部,MASM的声明语句是:

(1)printf函数

printf函数实现格式化输出,C语言原型是:

该函数的第一个参数是字符串指针,即字符串地址,字符串包括输出格式符。后面的参数是输出值,个数可变,由字符串的输出格式符确定输出值个数及输出格式。

(2)scanf函数

scanf函数实现格式化输入,C语言原型是:

该函数的第一个参数是字符串指针(地址),字符串主要是输入格式符。后面的参数是保存输入值的变量地址,个数可变,由输入格式符确定输入值个数及类型。

汇编语言调用C语言函数的一般步骤是:

按照80x86处理器的C语言函数调用规范,参数需要通过堆栈传递。汇编语言使用堆栈压入指令PUSH将参数压入堆栈,如果有多个参数,则以参数的书写顺序从右到左依次传入。在32位系统中,每条PUSH指令压入一个32位参数。C语言函数调用后的整数、地址返回值通过寄存器EAX返回。函数调用后,需要撤销参数占用的堆栈空间,汇编语言可以用加法指令ADD调整堆栈指针实现。一个32位参数占用4字节堆栈空间,堆栈指针加4即可,如果多个参数,则需要乘以参数的个数。

[例1-2]信息显示程序(调用C语言函数)

对于例1-1的信息显示程序,如果调用C语言printf函数实现,只需要将代码段的语句修改为:

将上述代码段指令语句以及与例1-1同样的数据段字符串定义语句填入程序模板文件,就形成一个完整的汇编语言程序。

需要注意的是,C语言函数并不保护EAX、EBX、ECX和EDX寄存器的内容。因此调用C语言函数后,如果还要使用这4个寄存器原来的内容,则在调用前需要保护寄存器内容并在调用后恢复寄存器内容。要保护寄存器内容,可以将其压入堆栈或存入变量;恢复寄存器内容时,则将其从堆栈弹出或从变量中取出。

【NASM】

本书以微软MASM汇编程序为主介绍基于IA-32处理器的汇编语言。对于使用NASM汇编程序的读者,则在适当位置以“旁白插叙”的方式简要介绍NASM与MASM的主要区别。由于NASM同样采用Intel语法规则,新版本提供一定的MASM兼容性,本书前5章的内容基本相同,需要特别注意的地方将指出,附录G则以对比方式进行汇总说明。有关两个汇编程序更详尽的语法不是本书的主题,初识汇编语言的读者也没有必要深究。有兴趣的读者可以研读其使用文档。

使用NASM的汇编语言程序框架可以是:

使用这个程序模板(EGN000.ASM),在其中填入数据定义、程序代码就可以编辑为使用NASM进行汇编的源程序文件。另外,使用本程序模板需要配合本书提供的IO32N.INC,同时也就可以使用本书提供的输入/输出子程序库(IO32.LIB),调用方法与MASM相同。

(1)兼容MASM语法

源程序首先使用了“%use masm”说明性语句。这样可以尽量保持与MASM语法兼容,当然有些NASM语法仍不相同。例如,NASM默认字母大小写敏感,汇编32位程序时默认支持32位IA-32指令集,如果不支持,也不需要指示存储模型。

(2)包含伪指令

NASM的预处理语句通常以“%”开头。源文件包含语句“%include”将当前目录下的IO32N.INC文件内容纳入汇编语言程序一起进行汇编,其主要作用也是声明输入/输出子程序,例如对字符和字符串的输入/输出子程序的声明语句如下:

NASM也使用EXTERN语句声明外部变量、函数、子程序等,但不需要类型属性。

如果不使用本书提供的32位输入/输出子程序,可以不用这个包含语句。

(3)段定义

NASM使用SECTION语句指示一个段的开始,代码段通常用.TEXT说明,数据段通常用.DATA说明。

(4)程序开始和结束

NASM没有为32位Windows应用程序设置起始执行的语句或符号,使用Visual C++的32位连接程序用main作为主程序(函数)名称,但需要声明为全局(global)标号。本书的NASM源程序框架使用全局标号start,因此start标号指示程序起始执行位置,并需要在连接程序的命令行中加入“\ entry:start”参数指明。作为函数,可以使用返回指令ret退出程序执行。NASM不支持也不需要end伪指令,汇编语言程序结束就是汇编结束。

对NASM源程序框架有了初步了解后,就可以将例1-1和例1-2的字符串定义和显示语句填入模板文件,分别形成NASM语法的汇编语言源程序文件(EGN101.ASM和EGN102.ASM)了。 Y/2gLIT0gwV96Wbl0hcTnFalu67BJEW2uWJIqxwRDMsJrFbaUlPgSvlnS15BV1rQ

点击中间区域
呼出菜单
上一章
目录
下一章
×