1 / 38

汇编语言程序设计

汇编语言程序设计. 吴 向 军. 中山大学计算机科学系. 2003.03.20. 第 9 章 宏. 9.1 宏的定义和引用.  宏是用来代表一个具有特定功能的程序段,它只需在源程序中定义一次,但可在源程序中引用多次。只要在编写程序时需要它,就可以直接使用它。. 9.1.1 宏的定义. 在使用宏之前,必须先定义宏。定义宏一般格式如下: 宏名 MACRO [ 形参 1, 形参 2, ……] … ; 宏的定义体 ENDM 在书写宏定义时,必须遵照下列规定:. 第 9 章 宏.

Download Presentation

汇编语言程序设计

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 汇编语言程序设计 吴 向 军 中山大学计算机科学系 2003.03.20

  2. 第9章 宏 9.1 宏的定义和引用  宏是用来代表一个具有特定功能的程序段,它只需在源程序中定义一次,但可在源程序中引用多次。只要在编写程序时需要它,就可以直接使用它。 9.1.1 宏的定义 在使用宏之前,必须先定义宏。定义宏一般格式如下: 宏名 MACRO [形参1, 形参2, ……] … ;宏的定义体 ENDM 在书写宏定义时,必须遵照下列规定:

  3. 第9章 宏 • MACRO和ENDM是二个成对出现的关键字,分别表示宏定义的开始和结束; • MACRO和ENDM之间的部分是宏的定义体,它是由指令、伪指令或引用其它宏所组成的程序片段,是宏所包含的具体内容; • “宏名”是由程序员指定的一个合法的标识符,它代表该宏; • 宏名可以与指令助忆符、伪指令名相同。在这种情况下,宏指令优先,而同名的指令或伪指令都失效; • 在ENDM的前面不要再写一次宏名,这与段或子程序定义的结束方式不同; • 在宏定义的首部可列举若干形式参数(也称哑元),参数之间要用逗号分隔。

  4. 第9章 宏 例9.1:定义一个把16位数据寄存器压栈的宏。 PUSHR MACRO PUSH AX PUSH BX PUSH CX PUSH DX ENDM 例9.2:定义二个字存储变量相加的宏。 MADDM MACRO OPRD1, OPRD2 MOV AX, OPRD2 ADD OPRD1, AX ENDM

  5. 第9章 宏 9.1.2 宏的引用 • 在源程序中,一旦定义了某宏,那么,在该程序的任何位置都可直接引用该宏,而不必重复编写相应的程序段。引用宏的一般格式如下: • 宏名 [实参1, 实参2, ……] • 其中:实参的位置要与形参的位置要对应,但实参个数可以与形参个数不相等。 • 当实参的个数多于形参的个数时,多出的实参被忽略; • 当实参的个数少于形参的个数时,没有实参对应的形参用“空”来对应。 在宏展开时,所得到的指令必须是合法的汇编指令,否则,汇编程序将会给出出错信息。

  6. 第9章 宏 9.1.3 宏的参数传递方式 引用宏时,参数是通过“实参”替换“形参”的方式来实现传递的。参数形式灵活多样,参数可以是常数、寄存器、存储单元和表达式,还可以是指令的操作码。 例9.3:定义二个字存储变量相加和相减的宏。 方法1:定义二个宏,分别实现存储变量的加操作和减操作 MADDM MACRO OPRD1, OPRD2 MOV AX, OPRD2 ADD OPRD1, AX ENDM MSUBM MACRO OPRD1, OPRD2 MOV AX, OPRD2 SUB OPRD1, AX ENDM 方法2:定义一个宏,把存储变量的“加”和“减”操作合并在一起 MOPM MACRO OP, OPRD1, OPRD2 MOV AX, OPRD2 OP OPRD1, AX ENDM 其中:参数OP是一个对应于操作码的形式参数。

  7. 第9章 宏 9.1.5 宏与子程序的区别 宏和子程序都是为了简化源程序的编写,提高程序的可维护性,但是它们二者之间存在着以下本质的区别: • 在源程序中,通过书写宏名来引用宏,而子程序是通过CALL指令来调用; • 汇编程序对宏通过宏扩展来加入其定义体,宏引用多少次,就相应扩展多少次,所以,引用宏不会缩短目标程序;而子程序代码在目标程序中只出现一次,调用子程序是执行同一程序段,因此,目标程序也得到相应的简化; • 宏引用时,参数是通过“实参”替换“形参”来实现传递的,参数形式灵活多样,而子程序调用时,参数是通过寄存器、堆栈或约定存储单元进行传递的; • 宏引用语句扩展后,目标程序中就不再有宏引用语句。运行时,不会有额外的时间开销,而子程序调用指令存在于目标程序之中,执行调用指令需要时间。 总之,当程序片段不长,速度是关键因素时,可采用宏来简化源程序,但当程序片段较长,存储空间是关键因素时,可采用子程序来简化源程序和目标程序。

  8. 第9章 宏 9.2 宏参数的特殊运算符 9.2.1 连接运算符 在宏定义中,如果形式参数与其它字符连接在一起,或形式参数出现在字符串之中,那么,就必须使用连接运算符(&)。 例9.5:定义一个转移宏JUMP,其一个参数决定转移类别,另一个参数指定转移目标。 解: JUMP MACRO CON, here J&CON here ENDM

  9. 第9章 宏 例9.6:定义一个问候性的字符串宏GREETING,其一个参数说明字符串的变量名,另一个参数指名问候的对象。 解: GREETING MACRO MSG, name MSG DB ‘Hello, &name’ ENDM 假设有下面引用语句, GREETING STR1, 张三 GREETING MSG1, John 那么,它们宏扩展时将会得到如下三个问候性的字符串定义。 GREETING STR1, 张三 1 STR1 DB ‘Hello, 张三’ GREETING MSG1, John 1 MSG1 DB ‘Hello, John’

  10. 第9章 宏 9.2.2 字符串整体传递运算符  字符串整体传递运算符是一对尖括号<>,用它括起来的内容将作为一个字符串来进行形式参数的整体替换。  在宏引用时,如果实参内包含逗号、空格等间隔符,则必须使用该操作符,以保证实参的完整性。如果实参是某个具有特殊含义的字符,为了使它只表示该字符本身,也需要用该运算符括起来。 假设有下面定义字符串的宏DEFMSG, DEFMSG MACRO MSG DB ‘&MSG’, 0DH, 0AH, ‘$’ ENDM 那么,使用和不使用该运算符的引用宏及其宏扩展如下所示: DEFMSG <Are you ready?> 1 DB ‘Are you ready?’, 0DH, 0AH, ‘$’ … DEFMSG Are you ready? 1 DB ‘Are’, 0DH, 0AH, ‘$’

  11. 第9章 宏 9.2.3 字符转义运算符 在引用宏时,如果实参中含有特殊字符,而又要该特殊字符当作普通字符来出来,那么,就必须在该特殊字符前加上字符转义运算符“!”。 DEFMSG <Input one number(>90):> 1 DB ‘Input one number(90):’, 0DH, 0AH, ‘$’ … DEFMSG <Input one number(!>90):> 1 DB ‘Input one number(>90):’, 0DH, 0AH, ‘$’ 在第一个引用宏的语句中,汇编程序会把第一个“>”字符与字符“<”相比配,而不会把它当作“大于号”字符来处理。 在第二个引用宏的语句中,由于在第一个“>”字符前面加了字符转义运算符“!”,所以,汇编程序会把第一个“>”当作“大于号”字符来处理,而把最后面的字符“>”当作是与前面“<”相比配的结束符。

  12. 第9章 宏 9.2.4 计算表达式运算符 在引用宏时,使用计算表达式运算符“%”表示把其后面表达式的结果当作实参进行替换,而不是该表达式的整个式子。 DEFMSG %200+23-100 1 DB ‘123’, 0DH, 0AH, ‘$’ … DEFMSG (200+23-100) 1 DB ‘(200+23-100)’, 0DH, 0AH, ‘$’ 前者是先计算出表达式200+23-100的值,然后再把该值作为参数进行替换,而后者是把表达式(200+23-100)当作一个字符串来进行参数替换。

  13. 第9章 宏 9.3 与宏有关的伪指令 9.3.1 局部标号伪指令 在宏定义体中,如果存在标号,则该标号要用伪指令LOCAL说明为局部标号,否则,当在源程序中,有多于一次引用该宏时,汇编程序在进行宏扩展后将会给出:标号重复定义的错误。 伪指令LOCAL的一般格式如下: LOCAL 标号1, 标号2, …… 伪指令LOCAL必须是伪指令MACRO后的第一条语句,在MACRO和LOCAL之间也不允许有注释和分号标志。 汇编程序在每次进行宏扩展时,总是把由LOCAL说明的标号用一个唯一的符号(从??0000到??FFFF)来代替,从而避免标号重定义的错误。

  14. 第9章 宏 例9.7:编写求一个求绝对值的宏。 解: 方法1: ABS MACRO word1 CMP word1, 0 JGE next NEG word1 next: ENDM 假设对宏ABS有以下两次引用, ABS BX … ABS AL 汇编程序将显示“标号重复定义”的错误,我们需要用下面的方法定义该宏。

  15. 第9章 宏 方法2: ABS MACRO word1 LOCAL next CMP word1, 0 JGE next NEG word1 next: ENDM 假设有下面两次宏ABS的引用, ABS BX 汇编程序对它们进行宏扩展时,将得到下列程序片段: ABS BX 1 CMP BX, 0 1 JGE ??0000 1 NEG BX 1 ??0000:

  16. 第9章 宏 伪指令LOCAL在子程序中也可起作用,但它的作用与宏定义的作用是不同的,有关该伪指令在子程序和宏定义中功能的主要差异如下表所列。 表9.1 伪指令LOCAL在子程序和宏中的比较

  17. 第9章 宏 9.3.2 取消宏定义伪指令 伪指令PURGE的一般格式如下: PURGE 宏名1, 宏名2, …… 该伪指令通知汇编程序取消“宏名1, 宏名2, ……”宏名表中的宏定义。 在此语句后,如果还有这些宏的引用语句,则汇编程序不会把它们当作宏引用来进行扩展,并且还将显示出错信息。

  18. 第9章 宏 9.3.3 中止宏扩展伪指令 伪指令EXITM的一般格式如下: EXITM 该伪指令书写在宏定义体中,用来告诉汇编程序:如果遇到该伪指令,那么,立即中止对该伪指令之下语句的扩展。如果在嵌套的内层宏中遇到了该伪指令,则退出到宏嵌套的外层。 在一般情况下,伪指令EXITM与条件伪指令一起使用,以便在不同的条件下挑选出不同的语句。

  19. 第9章 宏 9.4 重复汇编伪指令 在编写源程序时,有时会出现连续相同或相似的语句(组)。当出现这种情况时,可利用重复伪指令来重复语句,从而达到简化程序的目的。 重复汇编伪指令所定义的重复块是宏的一种特殊形式,也是由伪指令ENDM来结束重复块。用重复汇编伪指令定义的重复块也可带有参数,并在汇编过程中参数被实参代替,但重复块不会被命名,不能在程序的其它地方引用。 9.4.1 伪指令REPT 伪指令REPT的作用是把一组语句重复指定的次数,该重复次数由伪指令后面的数值表达式来确定。其一般使用格式如下: REPT 数值表达式 重复的语句组 ENDM

  20. 第9章 宏 例9.8:定义100个初值为32的字节单元,该存储单元的起始符号地址为Table。 解: 方法1:用伪指令REPT来实现 Table LABEL TYPE REPT 100 DB 32 ENDM 上述重复块的汇编结果如下: Table LABEL TYPE DB 32 DB 32 … DB 32 ;上述字节定义重复100次

  21. 第9章 宏 例9.9:定义100个初值分别为1,2,…,100的字节单元,该存储单元的起始符号地址为Table。 解: Table LABEL TYPE COUNT = 1 REPT 100 DB COUNT COUNT = COUNT + 1 ENDM 上述重复块的汇编结果相当于: Table LABEL TYPE DB 1 DB 2 … DB 100

  22. 例9.10:计算1+2+…+1000,并把其值存入寄存器AX。例9.10:计算1+2+…+1000,并把其值存入寄存器AX。 解: 方法1:用伪指令REPT来实现 MOV AX, 0 COUNT = 1 REPT 1000 ADD AX, COUNT COUNT = COUNT + 1 ENDM … 上述重复块的汇编结果与下面程序段相一致: MOV AX, 0 ADD AX, 1 ADD AX, 2 … ADD AX, 1000 … 虽然上面这些语句的执行能完成本例所指定的功能,但它是用1000条加法指令来直接计算的,这1000条指令无疑会大大增加目标代码的长度。

  23. 第9章 宏 方法2:用循环指令LOOP来实现 … MOV AX, 0 MOV CX, 1000 again: ADD AX, CX LOOP again …

  24. 第9章 宏 伪指令REPT与循环指令起作用的时期和方式是截然不同的。 表9.2 伪指令REPT与循环指令LOOP之间的主要差异

  25. 第9章 宏 9.4.2 伪指令IRP 伪指令IRP的作用是用每个参数创建一组语句,其重复次数由伪指令后面参数表中参数的个数来确定。其一般使用格式如下: IRP 形式参数, <实参1, 实参2, ……, 实参n> 重复的语句组 ENDM 例9.11:把16位通用寄存器之值相加,并把结果存入寄存器AX。 解:由于16位通用寄存器名是一些不同的符号,不能用计数的方法来依次访问它们,所以,我们需要用伪指令IRP来实现。 IRP REG, <BX, CX, DX, SP, BP, SI, DI> ADD AX, REG ENDM

  26. 第9章 宏 9.4.3 伪指令IRPC 伪指令IRPC的作用与IRP相似,其实参表是一个字符串,并对字符串中的每个字符创建一组语句,所以,其重复次数是由该字符串中的字符数来确定。 IRPC 形式参数, 字符串 重复的语句组 ENDM 例9.13:把16位数据寄存器之值相加,并把结果存入寄存器DI。 解:由于16位数据寄存器是AX、BX、CX和DX,它们的名称中只有第一个字符不同,所以,可以用伪指令IRPC来实现。 XOR DI, DI IRPC REG, ABCD ADD DI, REG&X ;符号&是连接运算符 ENDM

  27. 第9章 宏 9.5 条件汇编伪指令 9.5.1 条件汇编伪指令的功能 条件汇编伪指令的一般格式如下: IFnnnn 条件表达式 语句组1 [ELSE 语句组2] ENDIF 其中:IFnnnn是表9.3中的伪指令,“[…]”内的语句是可选的。 条件汇编伪指令是在汇编程序把源程序转换成目标程序时起作用,其一般含义是:若条件汇编伪指令后面的“条件表达式”为真,那么,语句组1将被汇编;否则,语句组2将被汇编(如果含有ELSE伪指令)。

  28. 表9.3条件汇编伪指令及其功能一览表

  29. 第9章 宏 9.5.2 条件汇编伪指令的举例 例9.14:编写一个可用DOS或BIOS功能调用输入字符的宏定义。 解: 方法1:使用条件汇编伪指令IF INPUT MACRO IF DOS ;当符号DOS不为0时,则使用DOS的功能调用 MOV AH, 1H INT 21H ELSE ;否则,将使用BIOS的功能调用 MOV AH, 10H INT 16H ENDIF ENDM 在引用宏INPUT时,汇编程序会根据DOS是否为0来生成不同的程序段。

  30. 第9章 宏 例9.15:编写一个可用功能调用输入字符的宏定义。 解: READCH MACRO char MOV AH, 1H INT 21H ;接受一个字符,并存入AL中 IFNB <char> ;若参数char有实参与之对应 IFDIF <char>, <AL> ;若参数char≠AL,则把字符保存到实参中 MOV char, AL ENDIF ENDIF ENDM

  31. 第9章 宏 9.6 宏的扩充 MASM 6.11编程系统对宏定义及其相关语句进行了一定程度的扩充。虽然这些扩充给编程带来了一些方便,但它们不一定能被其它的汇编语言编程系统所接受,所以,程序员在使用这些方便的扩充功能时,要注意到可能带来的限制。 有关内容见书,在此从略。

  32. 第10章 应用程序的设计 10.1 字符串的处理程序 例10.1 编写一个求字符串长度的子程序Strlen,要求字符串的首地址为入口参数,且以ASCII码0为结束符,CX为出口参数,其存放该字符串的长度。 例10.2 编写一个把字符串中的所有小写字符转换成大写字符的子程序Strupr,要求字符串的首地址和结束符为其入口参数。 例10.3 编写一个从字符串中拷贝子串的子程序Strncpy,它有四个参数str1、str2、idx和num,其具体功能为把字符串str2中从第idx个(从0开始记数)字符开始、num个字符传送给str1,字符串str1和str2都是以ASCII码0为结束符。 例10.4 编写一个把字符串中空格和TAB压缩掉的子程序Compress,字符串String是以ASCII码0为结束符。

  33. 第10章 应用程序的设计 10.2 数据的分类统计程序 例10.5 统计从地址0040H:0000H开始的100个字中,把正数和负数按照它们先后出现的次序分别存储在缓冲区Data1和Data2,并把每类的个数存入相应缓冲区的第一个字单元中。 例10.6 用键盘输入任意一字符串,分类统计该字符串中每个数字和字母的出现次数。

  34. 第10章 应用程序的设计 10.3 数据转换程序 例10.7 编写一个程序,它能把字类型变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号“-”,否则,不输出符号。 例10.8 编写一个程序,它能把字类型变量的数值以二进制形式输出出来。若该数值为负数,则需要输出负号“-”,否则,不输出符号。 例10.9 编写一个子程序,该子程序能把32位二进制变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号“-”,否则,不输出符号。 例10.10 编写一个程序,它能把用键盘输入的字符串转化成相应的数值。具体功能如下: 1、输入的数据字符串可以带正、负符号,如:1234、+1234或-1234; 2、字符串的最后一个字符表示数据的进制,默认的进制为十进制,如:1234H表示十六进制数1234,1234为十进制数; 3、对于任何进制的数据,当遇到一个非进制范围内的字符时,则显示出错信息,并以数值0为其转换结果来结束该类型转换过程。

  35. 第10章 应用程序的设计 10.4 文件操作程序 例10.11 假设有一个简单的学生结构类型student,其包括:学号、姓名和年龄等信息,要求编写一个程序,该程序接受从键盘输入的学生记录信息,并把它们保存在文件students.dat之中。 例10.12 编写一个程序显示由例10.11建立的记录文件students.dat中的学生信息。 10.5 动态数据的编程 例10.13 编写一个程序用动态链表存储20,19,……,1,并用遍历链表的方法来显示每个结点的数值。

  36. 第10章 应用程序的设计 10.6 COM文件的编程 COM文件和EXE文件都是可执行文件,典型的COM文件是Command.COM。COM文件的主要特点如下: 1、COM文件只有一个段,其字节数不会超过64K; 2、当操作系统装入COM文件时,四个段寄存器(CS、DS、ES和SS)都用PSP的段值来初始化; 3、必须用伪指令ORG 100H来说明空出前256个字节。

  37. 第10章 应用程序的设计 例10.14 编写一个显示字符串“Hello”的COM类型的程序。 解: CSEG SEGMENT 'CODE' ORG 100H ;空出前256个字节 start: LEA DX, MSG MOV AH, 09H INT 21H MOV AX, 4C00H INT 21H MSG DB "Hello$" ;定义字符串 CSEG ENDS END start 对上面程序,其生成的COM文件只有23个字节,而其EXE文件的字节数会超过1K。

  38. 谢 谢 计算机科学系 2003年03月20日

More Related