380 likes | 535 Views
汇编语言程序设计. 吴 向 军. 中山大学计算机科学系. 2003.03.20. 第 9 章 宏. 9.1 宏的定义和引用. 宏是用来代表一个具有特定功能的程序段,它只需在源程序中定义一次,但可在源程序中引用多次。只要在编写程序时需要它,就可以直接使用它。. 9.1.1 宏的定义. 在使用宏之前,必须先定义宏。定义宏一般格式如下: 宏名 MACRO [ 形参 1, 形参 2, ……] … ; 宏的定义体 ENDM 在书写宏定义时,必须遵照下列规定:. 第 9 章 宏.
E N D
汇编语言程序设计 吴 向 军 中山大学计算机科学系 2003.03.20
第9章 宏 9.1 宏的定义和引用 宏是用来代表一个具有特定功能的程序段,它只需在源程序中定义一次,但可在源程序中引用多次。只要在编写程序时需要它,就可以直接使用它。 9.1.1 宏的定义 在使用宏之前,必须先定义宏。定义宏一般格式如下: 宏名 MACRO [形参1, 形参2, ……] … ;宏的定义体 ENDM 在书写宏定义时,必须遵照下列规定:
第9章 宏 • MACRO和ENDM是二个成对出现的关键字,分别表示宏定义的开始和结束; • MACRO和ENDM之间的部分是宏的定义体,它是由指令、伪指令或引用其它宏所组成的程序片段,是宏所包含的具体内容; • “宏名”是由程序员指定的一个合法的标识符,它代表该宏; • 宏名可以与指令助忆符、伪指令名相同。在这种情况下,宏指令优先,而同名的指令或伪指令都失效; • 在ENDM的前面不要再写一次宏名,这与段或子程序定义的结束方式不同; • 在宏定义的首部可列举若干形式参数(也称哑元),参数之间要用逗号分隔。
第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
第9章 宏 9.1.2 宏的引用 • 在源程序中,一旦定义了某宏,那么,在该程序的任何位置都可直接引用该宏,而不必重复编写相应的程序段。引用宏的一般格式如下: • 宏名 [实参1, 实参2, ……] • 其中:实参的位置要与形参的位置要对应,但实参个数可以与形参个数不相等。 • 当实参的个数多于形参的个数时,多出的实参被忽略; • 当实参的个数少于形参的个数时,没有实参对应的形参用“空”来对应。 在宏展开时,所得到的指令必须是合法的汇编指令,否则,汇编程序将会给出出错信息。
第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是一个对应于操作码的形式参数。
第9章 宏 9.1.5 宏与子程序的区别 宏和子程序都是为了简化源程序的编写,提高程序的可维护性,但是它们二者之间存在着以下本质的区别: • 在源程序中,通过书写宏名来引用宏,而子程序是通过CALL指令来调用; • 汇编程序对宏通过宏扩展来加入其定义体,宏引用多少次,就相应扩展多少次,所以,引用宏不会缩短目标程序;而子程序代码在目标程序中只出现一次,调用子程序是执行同一程序段,因此,目标程序也得到相应的简化; • 宏引用时,参数是通过“实参”替换“形参”来实现传递的,参数形式灵活多样,而子程序调用时,参数是通过寄存器、堆栈或约定存储单元进行传递的; • 宏引用语句扩展后,目标程序中就不再有宏引用语句。运行时,不会有额外的时间开销,而子程序调用指令存在于目标程序之中,执行调用指令需要时间。 总之,当程序片段不长,速度是关键因素时,可采用宏来简化源程序,但当程序片段较长,存储空间是关键因素时,可采用子程序来简化源程序和目标程序。
第9章 宏 9.2 宏参数的特殊运算符 9.2.1 连接运算符 在宏定义中,如果形式参数与其它字符连接在一起,或形式参数出现在字符串之中,那么,就必须使用连接运算符(&)。 例9.5:定义一个转移宏JUMP,其一个参数决定转移类别,另一个参数指定转移目标。 解: JUMP MACRO CON, here J&CON here ENDM
第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’
第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, ‘$’
第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, ‘$’ 在第一个引用宏的语句中,汇编程序会把第一个“>”字符与字符“<”相比配,而不会把它当作“大于号”字符来处理。 在第二个引用宏的语句中,由于在第一个“>”字符前面加了字符转义运算符“!”,所以,汇编程序会把第一个“>”当作“大于号”字符来处理,而把最后面的字符“>”当作是与前面“<”相比配的结束符。
第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)当作一个字符串来进行参数替换。
第9章 宏 9.3 与宏有关的伪指令 9.3.1 局部标号伪指令 在宏定义体中,如果存在标号,则该标号要用伪指令LOCAL说明为局部标号,否则,当在源程序中,有多于一次引用该宏时,汇编程序在进行宏扩展后将会给出:标号重复定义的错误。 伪指令LOCAL的一般格式如下: LOCAL 标号1, 标号2, …… 伪指令LOCAL必须是伪指令MACRO后的第一条语句,在MACRO和LOCAL之间也不允许有注释和分号标志。 汇编程序在每次进行宏扩展时,总是把由LOCAL说明的标号用一个唯一的符号(从??0000到??FFFF)来代替,从而避免标号重定义的错误。
第9章 宏 例9.7:编写求一个求绝对值的宏。 解: 方法1: ABS MACRO word1 CMP word1, 0 JGE next NEG word1 next: ENDM 假设对宏ABS有以下两次引用, ABS BX … ABS AL 汇编程序将显示“标号重复定义”的错误,我们需要用下面的方法定义该宏。
第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:
第9章 宏 伪指令LOCAL在子程序中也可起作用,但它的作用与宏定义的作用是不同的,有关该伪指令在子程序和宏定义中功能的主要差异如下表所列。 表9.1 伪指令LOCAL在子程序和宏中的比较
第9章 宏 9.3.2 取消宏定义伪指令 伪指令PURGE的一般格式如下: PURGE 宏名1, 宏名2, …… 该伪指令通知汇编程序取消“宏名1, 宏名2, ……”宏名表中的宏定义。 在此语句后,如果还有这些宏的引用语句,则汇编程序不会把它们当作宏引用来进行扩展,并且还将显示出错信息。
第9章 宏 9.3.3 中止宏扩展伪指令 伪指令EXITM的一般格式如下: EXITM 该伪指令书写在宏定义体中,用来告诉汇编程序:如果遇到该伪指令,那么,立即中止对该伪指令之下语句的扩展。如果在嵌套的内层宏中遇到了该伪指令,则退出到宏嵌套的外层。 在一般情况下,伪指令EXITM与条件伪指令一起使用,以便在不同的条件下挑选出不同的语句。
第9章 宏 9.4 重复汇编伪指令 在编写源程序时,有时会出现连续相同或相似的语句(组)。当出现这种情况时,可利用重复伪指令来重复语句,从而达到简化程序的目的。 重复汇编伪指令所定义的重复块是宏的一种特殊形式,也是由伪指令ENDM来结束重复块。用重复汇编伪指令定义的重复块也可带有参数,并在汇编过程中参数被实参代替,但重复块不会被命名,不能在程序的其它地方引用。 9.4.1 伪指令REPT 伪指令REPT的作用是把一组语句重复指定的次数,该重复次数由伪指令后面的数值表达式来确定。其一般使用格式如下: REPT 数值表达式 重复的语句组 ENDM
第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次
第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
例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条指令无疑会大大增加目标代码的长度。
第9章 宏 方法2:用循环指令LOOP来实现 … MOV AX, 0 MOV CX, 1000 again: ADD AX, CX LOOP again …
第9章 宏 伪指令REPT与循环指令起作用的时期和方式是截然不同的。 表9.2 伪指令REPT与循环指令LOOP之间的主要差异
第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
第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
第9章 宏 9.5 条件汇编伪指令 9.5.1 条件汇编伪指令的功能 条件汇编伪指令的一般格式如下: IFnnnn 条件表达式 语句组1 [ELSE 语句组2] ENDIF 其中:IFnnnn是表9.3中的伪指令,“[…]”内的语句是可选的。 条件汇编伪指令是在汇编程序把源程序转换成目标程序时起作用,其一般含义是:若条件汇编伪指令后面的“条件表达式”为真,那么,语句组1将被汇编;否则,语句组2将被汇编(如果含有ELSE伪指令)。
第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来生成不同的程序段。
第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
第9章 宏 9.6 宏的扩充 MASM 6.11编程系统对宏定义及其相关语句进行了一定程度的扩充。虽然这些扩充给编程带来了一些方便,但它们不一定能被其它的汇编语言编程系统所接受,所以,程序员在使用这些方便的扩充功能时,要注意到可能带来的限制。 有关内容见书,在此从略。
第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为结束符。
第10章 应用程序的设计 10.2 数据的分类统计程序 例10.5 统计从地址0040H:0000H开始的100个字中,把正数和负数按照它们先后出现的次序分别存储在缓冲区Data1和Data2,并把每类的个数存入相应缓冲区的第一个字单元中。 例10.6 用键盘输入任意一字符串,分类统计该字符串中每个数字和字母的出现次数。
第10章 应用程序的设计 10.3 数据转换程序 例10.7 编写一个程序,它能把字类型变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号“-”,否则,不输出符号。 例10.8 编写一个程序,它能把字类型变量的数值以二进制形式输出出来。若该数值为负数,则需要输出负号“-”,否则,不输出符号。 例10.9 编写一个子程序,该子程序能把32位二进制变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号“-”,否则,不输出符号。 例10.10 编写一个程序,它能把用键盘输入的字符串转化成相应的数值。具体功能如下: 1、输入的数据字符串可以带正、负符号,如:1234、+1234或-1234; 2、字符串的最后一个字符表示数据的进制,默认的进制为十进制,如:1234H表示十六进制数1234,1234为十进制数; 3、对于任何进制的数据,当遇到一个非进制范围内的字符时,则显示出错信息,并以数值0为其转换结果来结束该类型转换过程。
第10章 应用程序的设计 10.4 文件操作程序 例10.11 假设有一个简单的学生结构类型student,其包括:学号、姓名和年龄等信息,要求编写一个程序,该程序接受从键盘输入的学生记录信息,并把它们保存在文件students.dat之中。 例10.12 编写一个程序显示由例10.11建立的记录文件students.dat中的学生信息。 10.5 动态数据的编程 例10.13 编写一个程序用动态链表存储20,19,……,1,并用遍历链表的方法来显示每个结点的数值。
第10章 应用程序的设计 10.6 COM文件的编程 COM文件和EXE文件都是可执行文件,典型的COM文件是Command.COM。COM文件的主要特点如下: 1、COM文件只有一个段,其字节数不会超过64K; 2、当操作系统装入COM文件时,四个段寄存器(CS、DS、ES和SS)都用PSP的段值来初始化; 3、必须用伪指令ORG 100H来说明空出前256个字节。
第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。
谢 谢 计算机科学系 2003年03月20日