第三章汇编语言程序设计本章内容汇编语言源程序的格式伪操作命令DOS及BIOS功能调用计算机程序设计语言的演变1.机器语言直接用机器指令来编制计算机程序的方法。2.汇编语言可以用助记符来表示指令的操作和操作数,也可以用标号和符号来代替地址、常量和变量。3.高级语言更接近于人们的自然语言和习惯的教学语言来描述算法的执行过程,从而使编写的过程更加直观和简练。为什么要用汇编语言汇编语言非常接近机器语言程序,通过编制汇编语言程序,可以清楚地了解计算机的工作过程。现在的微机系统中,底层的一些功能仍然靠汇编语言程序来实现。汇编语言程序的效率通常高于高级语言程序。举例dataSEGMENTmsgDB'Hello,World!$'dataENDScodeSEGMENTASSUMECS:code,DS:dataMAINPROCNEARstart:MOVAX,dataMOVDS,AXLEADX,msgMOVAH,9hINT21hMOVAX,4C00hINT21hMAINENDPcodeENDSENDstart数据段代码段在屏幕上显示Hello,World!本例将在下文中多次提及,为方便计,下文简称其为“Hello例”。结束语句分析1—分段结构可以看出,汇编语言源程序是分段结构的形式。一个汇编源程序由若干个段(Segment)组成。每个段以SEGMENT语句开始,以ENDS结束。整个源程序以END语句结尾。这里所说的汇编语言源程序的段和第一章中的CPU管理的存储器的段是不同的概念。汇编语言的段是逻辑段;8086CPU管理的存储器的段是物理段(共有4个:数据段、附加段、堆栈段、代码段,对应4个段寄存器:DS、ES、SS和CS)。一个汇编语言源程序中可以有多个逻辑段。上例中共有两个逻辑段:data和code。分析2注:功能号09H的int21中断功能描述:输出一个字符串到标准输出设备上。入口参数:AH=09HDS:DX=待输出字符的地址说明:待显示的字符串以’$’作为其结束标志代码段开头执行了一次功能号09H的21号DOS中断,用于在屏幕上显示字符串。关于DOS功能调用,后文将有详细解释。此处先列出功能号09H的21号DOS中断的相关资料:最后两行(MOVAX,4C00h、INT21h)也是一个DOS功能调用。注:功能号4CH的int21中断功能描述:终止程序的执行,并可返回一个代码入口参数:AH=4CHAL=返回的代码汇编语言开发过程源程序:文件名.asm目标程序:文件名.obj可执行文件:文件名.exeFinish编辑器,如notepad.exe汇编程序,如masm.exe链接程序,如link.exe调试程序,如debug.exe开发工具(了解)X86+Windows平台下常用的汇编编译器有:Microsoft公司的MASMBorland公司的TASM开源社区的NASMMASM是微软推出的宏汇编语言,自发布以来已有多次版本更新,下页表格中列出了几个较为典型的版本。注:容易与之混淆的是MASM32,它是SteveHutchesson以个人名义发布、基于MASM而构建的软件包。其版本号和MASM的版本号是不同的,比如MASM32V8使用的汇编编译器是MASM6。MASM32的最新版本为MASM32V12。MASM版本历史(了解)MASM4.00最先广泛使用的一个MASM版本,适用于DOS下的汇编编程MASM5.00开始支持.code.data写法的段定义格式(即“简化段定义格式”)MASM6.001992年发布,可执行文件名从Masm.exe改为Ml.exe开始支持.if/.endif这样的高级语法开始支持invoke伪指令来简化带参数的子程序调用MASM6.152000年4月发布MASM8.00随VS2005一起发布开发工具(了解)Masm5适合进行DOS程序开发;Masm6以后的版本开始支持Win32程序开发。(微软官方发布的Masm6所附的link.exe是SegmentedExecutableLinker,只能开发DOS程序;要进行Win32开发必须配备IncrementalLinker,该类型的link.exe可以从VisualStudio中获取。开发Win32程序推荐使用MASM32开发包)开发步骤演示(了解)使用MASM5.0开发步骤演示(了解)使用MASM6.15ForDOS语句的类型汇编语言源程序中的语句主要有以下两种类型:指令性语句指示性语句指令性语句主要由CPU指令组成,对应实际的机器指令;(比如“Hello例”中的MOVDS,AX)指示性语句又称伪操作语句,主要由伪操作指令组成。(比如“Hello例”中的codeSEGMENT)语句的组成汇编语言的语句可以有1~4个组成部分,如下所示:[名字]操作码/伪操作码[操作数][;注释]带方括号的部分表示可选项。以“Hello例”中的几条语句为例:名字操作码/伪操作码操作数注释dataSEGMENTmsgDB'Hello,World!$'start:MOVAX,dataENDstart语句的组成—名字名字在指令性语句中,名字是一个标号,实际上就是指令的符号地址。比如“start:MOVAX,data”中的start:。并非每条指令性语句都必须有标号。但如果有了这个标号,程序中其他地方就可引用这个标号,比如执行跳转或者CALL调用。指令性语句中的标号后面通常有一个冒号。标号有三种属性:段、偏移量和类型。段属性是定义标号的程序段的段地址。偏移量表示标号所在段的起始地址到定义该标号的地址之间的字节数。标号的类型有两种:NEAR和FAR。前者可以在段内被引用,地址指针为两个字节;后者可以在其他段中被引用,地址指针为4个字节。语句的组成—名字在指示性语句中,名字可以是变量名、段名、过程名。比如“Hello例”中“dataSEGMENT”中的data是段名,“msgDB‘Hello,World!$‘”中的msg是变量名。指示性语句中的标号后面通常没有冒号。变量也有三种属性:段、偏移量和类型。段属性是变量所代表的数据所在段的段地址。偏移量表示变量所在段的起始地址与变量的地址之间的字节数。变量的类型有:BYTE、WORD、DWORD(四字节)、QWORD(八字节)和TBYTE(十字节)等,表示数据区中存取操作对象的大小。语句的组成—操作码/伪操作码操作码/伪操作码在汇编语言中操作码以助记符的形式存在。8086/8088CPU的助记符总共约有90多种,比如MOV、ADC等。关于所有的助记符,参见第二章指令系统。指示性语句中的DB、SEGMENT、ENDS、ASSUME、END等都是伪操作码,而不是CPU指令的助记符。它们在程序中的作用是定义变量的类型、定义段以及命令汇编程序(masm.exe)结束汇编等。它们是指示汇编程序(masm.exe)完成汇编,本身不产生对应的机器码。关于伪操作码的具体作用和使用方法,下文有专门章节讨论。语句的组成—操作数操作数对于CPU指令,可能有单操作数和双操作数,也可能无操作数;伪指令可能有更多个操作数。可以用作操作数的有:常数、寄存器、标号、变量和表达式。常数十进制数,如99D或99。后面加字母“D”,或者什么也不加。十六进制数,如64H,0F800H,后面加一个字母“H”;如果最高位数值不是0-9,前面要再加一个数字0。(以避免和寄存器名称如“AH”冲突)ASCII常数,例如’A’、’8’、‘cat’,字符应该放在单引号中。语句的组成—操作数寄存器8086/8088的寄存器可以用作指令的操作数。8086/8088CPU的寄存器有:8位寄存器:AH、AL、BH、BL、CH、CL、DH、DL。16位寄存器:AX、BX、CX、DX、SI、DI、BP、SP、DS、ES、SS、CS。标号标号代表一条指令的符号地址,因此可以作为转移、过程调用CALL以及循环控制LOOP等指令的操作数。比如“HELLO例”中“ENDstart”,start就是一个标号。语句的组成—操作数变量变量是存储器中某个数据区的名字,因此在指令中可以作为存储器操作数。如“Hello例”中的:LEADX,msg其中msg就是一个在数据区定义的变量(msgDB‘Hello,World!$’)。表达式汇编语言中的表达式按其性质可以分为两种:数值表达式和地址表达式。数值表达式产生一个数值结果,只有大小,没有属性。地址表达式的结果不是一个单纯的数值,它有三种属性:段、偏移量和类型。语句的组成—操作数构成表达式必然有运算符。表达式中常用运算符有以下几种:①算术运算符,如+、-、*、/和MOD(模除)这些算术运算符可用于数值表达式,运算结果是一个数值。在地址表达式中通常只使用其中的+和–两种运算符。②逻辑运算符,如AND、OR、XOR和NOT逻辑运算符只用于数值表达式中对数值进行按位逻辑运算。对地址进行逻辑运算是没有意义的。不要把逻辑运算符如AND、OR、XOR和NOT等与同样名称的CPU指令相混淆。前者可对整常数进行按位逻辑运算,是在汇编时进行;后者的操作数可以是寄存器、存储器和立即数,是在程序运行时由CPU执行。比如:ANDAL,01011010B;这里的AND是指令助记符MOVAL,01011010BAND11110000B;这里的AND是逻辑运算符语句的组成—操作数③关系运算符如EQ(等于)、NE(不等)、LT(小于)、GT(大于)、LE(小于或等于)、GE(大于或等于)等。参与关系运算的必须是两个数值,或同一段中的两个存储单元地址,但运算结果只能是两个特定的数值之一。当关系不成立(假)时,结果为0;当关系成立(真)时,结果为0FFFFH(-1)。例如:MOVAX,4EQ3;关系不成立,故(AX)←0MOVAX,4NE3;关系成立,故(AX)←0FFFFH语句的组成—操作数④分析运算符和合成运算符如OFFSET、SEG、TYPE、SIZE和LENGTH等;合成运算符有PTR、THIS、SHORT等。分析运算符用以分析一个在存储器操作数的属性,如段、偏移量或类型等。合成运算符则可以规定存储器操作数的某个属性,例如类型。OFFSET用于获取一个标号或变量的偏移地址,如:MOVSI,OFFSETDATA1是将变量DATA1的偏移地址送至SI寄存器。其效果等同于:LEASI,DATA1语句的组成—操作数SEG用于获取标号或变量的段址,如:MOVAX,SEGARRAYMOVDS,AX是将变量ARRAY的段地址送入DS寄存器。TYPE的运算结果是一个数值,这个数值与存储器操作数类型属性的关系如下:TYPE返回值操作数类型TYPE返回值操作数类型1BYTE-1NEAR2WORD-2FAR4DWORD语句的组成—操作数TYPE运算符的例子VARDW?ARRAYDD10DUP(?)STRDB‘Thisisatest’MOVAX,TYPEVAR;(AX)←2MOVBX,TYPEARRAY;(BX)←4MOVCX,TYPESTR;(CX)←1LENGTH如果一个变量已用重复操作符DUP说明其变量的个数,则利用LENGTH获取这个变量的个数。如果未用DUP说明,则得到的结果为1。比如上例中,LENGTHARRAY运算结果为10。语句的组成—操作数SIZE如果一个变量已用重复操作符DUP说明,则利用SIZE运算符可得到分配给该变量的字节总数。如果未用DUP说明,则得到的结果是TYPE运算的结果。比如上例中,SIZEARRAY运算结果为10×4=40。由此可知,SIZE的运算结果等于LENGTH的运算结果乘以TYPE的运算结果。PTR是一个合成运算符,用于指定存储器操作数的类型。比如INCBYTEPTR[BX][SI],指令中利用PTR运算符明确规定存储器操作数的类型为BYTE(字节),因此,本指令将一个8位存储器的内容加1。语句的组成—操作数THIS也可以指定存储器操作数的类型。使用THIS运算符可以使标号或变量的类型具有灵活性。例如要求对同一个数据区,既可以字节作为单位,又可以字作