第 4 章 汇编语言程序设计
DESCRIPTION
第 4 章 汇编语言程序设计. 4.1 顺序程序设计 4.2 分支程序设计 4.3 循环程序设计 4.4 子程序设计 4.5 宏结构程序设计 4.6 模块化程序设计 4.7 输入输出程序设计. 4.4 子程序设计. 把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序。 子程序可以实现源程序的模块化,可以简化源程序结构,可以提高编程效率。. 子程序设计要利用过程定义伪指令. 参数传递是子程序设计的重点和难点. 子程序可以嵌套; 一定条件下,还可以递归和重入. - PowerPoint PPT PresentationTRANSCRIPT
第 4 章 汇编语言程序设计第 4 章 汇编语言程序设计
4.1 顺序程序设计4.2 分支程序设计4.3 循环程序设计4.4 子程序设计4.5 宏结构程序设计4.6 模块化程序设计4.7 输入输出程序设计
把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序。
子程序可以实现源程序的模块化,可以简化源程序结构,可以提高编程效率。
4.4 子程序设计
子程序设计要利用过程定义伪指令
参数传递是子程序设计的重点和难点
子程序可以嵌套;一定条件下,还可以递归和重入
4.4.1 4.4.1 程序定义伪指令程序定义伪指令过程名 proc [near|far]...
过程名 endp
过程名(子程序名)为符合语法的标识符。•NEAR 属性(段内近调用)的过程只能被相同代码段的
其他程序调用;•FAR 属性(段间远调用)的过程可以被相同或不同代
码段的程序调用。对简化段定义格式,在微型、小型和紧凑存储模式下,
过程的缺省属性为 near ;在中型、大型和巨型存储模式下,过程的缺省属性为 far 。对完整段定义格式,过程的缺省属性为 near 。
用户可以在过程定义时用 near 或 far 改变缺省属性。
subname proc ; 具有缺省属性的 subname 过程
push ax ; 保护寄存器:顺序压入堆栈push bx ;ax/bx/cx 仅是示例push cx... ; 过程体pop cx ; 恢复寄存器:逆序弹出堆栈pop bxpop axret ; 过程返回
subname endp ; 过程结束
子程序的常见格式
例 4.10
ALdisp proc ; 实现 al 内容的显示push ax ; 过程中使用了 AX 、 CX 和 DXpush cxpush dxpush ax ; 暂存 axmov dl,al ; 转换 al 的高 4 位mov cl,4shr dl,clor dl,30h ;al 高 4 位变成 3cmp dl,39hjbe aldisp1add dl,7 ; 是 0Ah ~ 0Fh ,其 ASCII 码还要加上
7aldisp1: mov ah,2 ; 显示
int 21h
例 4.10 上
pop dx ; 恢复原 ax 值到 dxand dl,0fh ; 转换 al 的低 4 位or dl,30hcmp dl,39hjbe aldisp2add dl,7
aldisp2: mov ah,2 ; 显示int 21hpop dxpop cxpop axret ; 过程返回
ALdisp endp
例 4.10 下
... ; 主程序,同例 4.8 源程序loop outlp ; 外循环尾mov bx,offset array ; 调用程序段开始mov cx,count
displp: mov al,[bx]call ALdisp ; 调用显示过程mov dl,',' ; 显示一个逗号,以分隔两个数据mov ah,2int 21hinc bxloop displp ; 调用程序段结束.exit 0... ; 过程定义,同例 4.10 源程序end
例 4.10 应用
操作操作
入口参数(输入参数):主程序 提供给 子程序
出口参数(输出参数):子程序 返回给 主程序
4.4.2 4.4.2 子程序的参数传递子程序的参数传递
参数的形式:参数的形式:① 数据本身(传值) ② 数据的地址(传址)传递的方法:传递的方法:① 寄存器 ② 变量 ③ 堆栈
例例 4.114.11 子程序计算数组元素的“校验和” 入口参数: 数组的逻辑地址(传址),
元素个数(传值); 出口参数: 求和结果(传值)。
把参数存于约定的寄存器中,可以传值,也可以传址。
子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护)。
子程序对带有入口参数的寄存器可以保护,也可以不保护;但最好一致。
例例 4.11a4.11a•入口参数: CX =元素个数,DS:BX =数组的段地址:偏移地址•出口参数: AL =校验和
用寄存器传递参数
.startup ; 设置入口参数(含有 DS←数组的段地址)mov bx,offset array;BX←数组的偏移地址mov cx,count ;CX←数组的元素个数call checksuma ; 调用求和过程mov result,al ;处理出口参数.exit 0 主程序 a
checksumaprocxor al,al ;累加器清 0
suma: add al,[bx] ; 求和inc bx ;指向下一个字节loop sumaret
checksumaendpend
子程序 a
主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递。
不通模块间共享时,需要声明。
例例 4.11b4.11b•入口参数:count =元素个数,array =数组名(含段地址:偏移地址)•出口参数:result =校验和
用变量传递参数
checksumb procpush axpush bxpush cxxor al,al ;累加器清 0mov bx,offset array ;BX←数组的偏移地址mov cx,count;CX←数组的元素个数
sumb: add al,[bx] ; 求和inc bxloop sumbmov result,al ; 保存校验和pop cxpop bxpop axret
checksumb endp
call checksumb ; 调用求和过程主程序 b
子程序 b
主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数;
子程序将出口参数压入堆栈,主程序弹出堆栈取得它们。
例例 4.11c4.11c•入口参数:顺序压入偏移地址和元素个数•出口参数:AL =校验和
用堆栈传递参数
checksumcprocpush bpmov bp,sppush bxpush cxmov bx,[bp+6]
mov cx,[bp+4] xor al,alsumc: add al,[bx]
inc bxloop sumcpop cxpop bxpop bpret
checksumcendp
mov ax,offset arraypush axmov ax,countpush axcall checksumcadd sp,4mov result,al
主程序 c
子程序 c
•利用 BP间接寻址取出参数注意位移量•主程序实现平衡堆栈: add sp,n•子程序实现平衡堆栈: ret n
参见图
子程序的嵌套
子程序内包含有子程序的调用就是子程序嵌套。没有什么特殊要求
例题 4.10
例 4.10 :子程序嵌套ALdisp procpush axpush cxpush dxpush axmov dl,almov cl,4shr dl,clcall dldisppop dxand dl,0fhcall dldisppop dxpop cxpop axret
ALdisp endp
;将 dl 中的一位 16 进制数显示出来dldisp proc
or dl,30hcmp dl,39hjbe dldisp1add dl,7
dldisp1: mov ah,2int 21hret
dldisp endp
子程序的递归
当子程序直接或间接地嵌套调用自身时称为递归调用,含有递归调用的子程序称为递归子程序。
递归子程序必须采用寄存器或堆栈传递参数,递归深度受堆栈空间的限制。
例 4.12 :求阶乘
01
0)!1(!
N
NNNN
例 4.12 主程序
.model small
.stack 256
.dataN dw 3result dw ?
.code
.startupmov bx,Npush bxcall factpop result.exit 0
fact proc ; 计算 N!的近过程push ax ; 入口参数:压入 Npush bp ; 出口参数:弹出 N!mov bp,spmov ax,[bp+6]cmp ax,0jne fact1inc axjmp fact2
fact1: dec axpush axcall factpop axmul word ptr [bp+6]
fact2: mov [bp+6],axpop bppop axret
fact endp
递归子程序
子程序的重入
•子程序的重入是指子程序被中断后又被中断服务程序所调用,能够重入的子程序称为可重入子程序。在子程序中,注意利用寄存器和堆栈传递参数和存放临时数据,而不要使用固定的存储单元(变量),就能够实现重入。
•子程序的重入不同于子程序的递归。重入是被动地进入,而递归是主动地进入;重入的调用间往往没有关系,而递归的调用间却是密切相关的。递归子程序也是可重入子程序。
宏汇编重复汇编
条件汇编——统称宏结构
4.5 宏结构程序设计
宏( Macro )是汇编语言的一个特点,它是与子程序类似又独具特色的另一种简化源程序的方法。
宏——具有宏名的一段汇编语句序列,——宏定义时书写
宏指令——这段汇编语句序列的缩写,——宏调用时书写
宏展开——在宏指令处用这段宏代替的过程,——宏汇编时实现
宏的参数功能强大,颇具特色。配合宏,还有宏操作符和有关伪指令。宏与子程序实际上具有本质的区别。
4.5.1 宏汇编
宏定义宏定义 宏名 macro [ 形参表 ]宏定义体endm
mainbegin MACRO ;; 定义一个名为 mainbegin 的宏,无参数
mov ax,@data ;; 宏定义体mov ds,axENDM ;; 宏定义结束
mainend MACRO retnum ;; 带有形参 retnummov al,retnum ;; 宏定义中使用参数mov ah,4chint 21hENDM
宏调用宏调用 宏名 [ 实参表 ]
start: mainbegin ; 宏 调 用 ,建立 DS 内容
dispmsg string ; 宏调用,显示字符串mainend 0 ; 宏调用,返回 DOSend start
• 宏调用的实质是在汇编过程中进行宏展开。• 宏展开的具体过程是:当汇编程序扫描源程序遇到已有定义的宏调用时,即用相应的宏定义体取代源程序的宏指令,同时用位置匹配的实参对形参进行取代。
宏展开宏展开 宏展开——在汇编时,用宏定义体的代码序列替代宏指令的过程。
start: mainbegin ; 宏指令 1 mov ax,@data ; 宏展开 1 mov ds,ax
mainend 0 ; 宏指令 1 mov al,0 ; 宏展开 1 mov ah,4ch 1 int 21h
例 4.13b操作例 4.13b操作
宏的参数宏的参数 宏的参数使用非常灵活
宏定义时,可以无参数,例如 4.13a 的 mainbegin ;可以带有一个参数,例如 4.13a 的 mainend ;也可以具有多个参数;例如 4.14a 的 shlext 。参数可以是常数、变量、存储单元、指令(操
作码)或它们的一部分,也可以是表达式;例如 4.14b 的 shift 和 shrot 。
宏定义体可以是任何合法的汇编语句,既可以是硬指令序列,又可以是伪指令序列;例如 4.15 的dstring 。
;宏定义shlext macro shloprand,shlnum
push cxmov cl,shlnumshl shloprand,clpop cxendm
;宏指令shlext ax,6
;宏展开 1 push cx 1 mov cl,06 1 shl ax,cl 1 pop cx
例 4.14a
;统一 4条移位指令的宏指令shift macro soprand,snum,sopcode
push cxmov cl,snums&sopcode& soprand,clpop cxendm
;统一移位和循环移位 8条指令的宏指令shrot macro sroprand,srnum,sropcode
push cxmov cl,srnumsropcode sroprand,clpop cxendm
例 4.14b
;宏定义dstring macro string
db '&string&',0dh,0ah,'$'endm
;宏调用dstring < This is a example. >dstring < 0 !< Number !< 10 >;宏展开 1 db 'This is a example.', 0dh,0ah,'$’ 1 db '0 < Number < 10', 0dh,0ah,'$'
例 4.15
宏操作符宏操作符
&——替换操作符,用于将参数与其他字符分开。如果参数紧接在其他字符之前或之后,或者参数出现在带引号的字符串中,就必须使用该伪操作符。
< >——字符串传递操作符,用于括起字符串。在宏调用中,如果传递的字符串实参数含有逗号、空格等间隔符号,则必须用这对操作符,以保证字符串的完整。
!——转义操作符,用于指示其后的一个字符作为一般字符,而不含特殊意义。
%——表达式操作符,用在宏调用中,表示将后跟的一个表达式的值作为实参,而不是将表达式本身作为参数
;;—— 宏注释符,用于表示在宏定义中的注释。采用这个符号的注释,在宏展开时不出现。
与宏有关的伪指令与宏有关的伪指令
•局部标号伪指令LOCAL 标号列表宏定义体采用了标号,应使用局部标号伪指令 LOCAL
加以说明。它必须是宏定义 MACRO 语句之后的第一条语句。•宏定义删除伪指令PURGE 宏名表不需要某个宏定义时,可以把它删除。•宏定义退出伪指令EXITM伪指令 EXITM表示结束当前宏调用的展开。
;宏定义absol macro oprd
local nextcmp oprd,0jge nextneg oprd
next:endm
例 4.16
;宏调用absol word ptr [bx]absol bx
;宏展开 1 cmp word ptr [bx],0 1 jge ??0000 1 neg word ptr [bx] 1 ??0000: 1 cmp bx,0 1 jge ??0001 1 neg bx 1 ??0001:
宏与子程序的比较宏与子程序的比较
宏与子程序具有各自的特点,程序员应该根据具体问题选择使用那种方法。通常,当程序段较短或要求较快执行时,应选用宏;
当程序段较长或为减小目标代码时,要选用子程序。
宏——仅是源程序级的简化:宏调用在汇编时进行程序语句的展开,不需要返回;不减小目标程序,执行速度没有改变。
宏调用的参数通过形参、实参结合实现传递,简捷直观、灵活多变。
子程序——还是目标程序级的简化:子程序调用在执行时由 CALL指令转向,RET 指令返回;形成的目标代码较短,执行速度减慢。
子程序需要利用寄存器、存储单元或堆栈等传递参数。
重复汇编指在汇编过程中,重复展开一段(基本)相同的语句。
重复汇编没有名字,不能被调用;重复汇编常用在宏定义体中,也可以在一般
汇编语句中使用。重复汇编伪指令有三个:
•REPEAT——按参数值重复,•FOR——按参数个数重复,•FORC——按参数的字符个数重复;最后,用 ENDM 结束。
4.5.2 重复汇编
按参数值重复按参数值重复 REPEAT 重复次数 重复体ENDM
char = 'A'REPEAT 26 db char char = char +1ENDM
1 db char ;等效于 db 'A' 1 char = char +1 1 db char ;等效于 db 'B' 1 char = char +1
... 1 db char ;等效于 db 'Z' 1 char = char +1
按参数个数重复按参数个数重复 FOR 形参 ,〈实参表〉 重复体ENDM
FOR regad, <ax,bx,cx,dx> push regadENDM
1 push ax 1 push bx 1 push cx 1 push dx
按参数字符个数重复按参数字符个数重复 FORC 形参 , 字符串 重复体ENDM
FORC regad,dcba pop ®ad&xENDM
1 pop dx 1 pop cx 1 pop bx 1 pop ax
条件汇编伪指令在汇编过程中,根据条件决定汇编的语句。
IFxx 表达式 ;条件满足,汇编分支语句体 1
分支语句体 1
[ ELSE ;条件不满足,汇编分支语句体 2
汇编分支语句体 2 ]
ENDIF ;条件汇编结束
4.5.3 条件汇编
例 4.19 例 4.13c
pdata macro num IF num lt 100 ;;如果 num < 100 ,则汇编如下语句
db num dup (?) ELSE ;;否则,汇编如下语句
db 100 dup (?) ENDIF
endm
pdata 12 ; 宏调用①db 12 dup(?) ; 宏汇编结果①
pdata 102 ; 宏调用②db 100 dup(?) ; 宏汇编结果②
例 4.19
dstring MACRO string ;; 定义字符串db '&string&',0dh,0ah,'$'ENDM
mainbegin MACRO dsseg ;; 设置数据段地址mov ax,dssegmov ds,axENDM
dispmsg MACRO messagemov dx,offset messagemov ah,09hint 21hENDM
例 4.13c 上
mainend MACRO retnum ;; 返回 DOS ,可以不带参数ifb <retnum> mov ah,4ch ;;没有参数else mov ax,4c00h+(retnum AND 0ffh) ;; 有参数endifint 21hENDM
例 4.13c 中
.model small
.stack 256
.datamsg1 equ this bytedstring <Hello,Everybody !!>msg2 equ this bytedstring <You see,I made it.>
.codestart: mainbegin @data ;建立 DS 内容
dispmsg msg1 ; 显示 msg1字符串dispmsg msg2 ; 显示 msg2字符串mainend ; 返回 DOSend start
例 4.13c 下
宏结构的作用
宏汇编、重复汇编和条件汇编为源程序的编写提供了很多方便,灵活运用它们可以编写出非常良好
的源程序来。
汇编系统中有些以圆点起始的 伪指令(如 .startup 、 .exit等) 实际上是一种宏结构。