第4章子程序设计和DOS功能调用Word文档格式.docx

上传人:b****6 文档编号:18750729 上传时间:2023-01-01 格式:DOCX 页数:52 大小:116.37KB
下载 相关 举报
第4章子程序设计和DOS功能调用Word文档格式.docx_第1页
第1页 / 共52页
第4章子程序设计和DOS功能调用Word文档格式.docx_第2页
第2页 / 共52页
第4章子程序设计和DOS功能调用Word文档格式.docx_第3页
第3页 / 共52页
第4章子程序设计和DOS功能调用Word文档格式.docx_第4页
第4页 / 共52页
第4章子程序设计和DOS功能调用Word文档格式.docx_第5页
第5页 / 共52页
点击查看更多>>
下载资源
资源描述

第4章子程序设计和DOS功能调用Word文档格式.docx

《第4章子程序设计和DOS功能调用Word文档格式.docx》由会员分享,可在线阅读,更多相关《第4章子程序设计和DOS功能调用Word文档格式.docx(52页珍藏版)》请在冰豆网上搜索。

第4章子程序设计和DOS功能调用Word文档格式.docx

ip←断点偏移量

段间返回:

cs←断点段地址

②带弹出值的返回指令,其弹出值只能是偶数,在从堆栈中弹出返回地址后,再次修改堆栈指针sp的值:

sp←sp+n

以废除调用程序装入栈中的参数恢复调用之前的栈顶。

把执行call指令前压入堆栈的参数弹出丢去.

4.1.2过程定义语句

过程名proc[near/far]

....

ret

过程名endp

说明:

①过程名不能省略,proc和endp前的过程名必须相同

②near————近调用过程,即仅能被同一逻辑代码段的程序调用.

far——远调用过程,即能被其他逻辑代码段的程序调用.

如果无类型说明,隐含near.

③过程调用与返回

主程序用call命令调用过程,执行完后,在过程中执行ret返回主程序,所以在过程中,一般最后一条语句均为ret指令

④过程的嵌套与递归——汇编程序也允许过程的嵌套调用和递归调用

过程嵌——在过程中调用过程

过程递归——过程调用自己

这两种调用方式都是依靠CALL指令和RET指令配合实现的。

子程序的编写方法

1.子程序的定义

⑴调用程序与子程序在同一代码段中,子程序的类型为near

codesegment

assume.....

mainprocfar

.....

callsub

mainendp

subprocnear

subendp

codeends

⑵调用程序与子程序不在同一代码段中,子程序必须是far型

code1segment

mainprocfar

callfarptrsub

mainendp

code1ends

code2segment

subprocfar

subendp

.....

code2ends

endmain

2.编写子程序的要求

⑴保护寄存器与存贮器工作单元

codesegment

callsub;

调用延时子程序

subprocnear;

延时子程序

pushbx;

子程序要使用的寄器中数据送堆栈保护

pushcx

movbl,20

delay:

movcx,5600;

延时循环

wait:

loopwait

decbl

jnzdelay

popcx;

由堆栈恢复保护的数据

popbx

codeends

⑵正确使用堆栈

①执行CALL指令之间后,转入子程序之前,堆栈顶保存了断点地址.

②在子程序中执行RET指令时,要从堆栈中弹出断点地址,以便正确返回调用程序.

③在子程序中使用了堆栈,必须成对地执行PUSH和POP,确保在执行RET时,栈顶能恢复刚进入子程序时的位置,使RET指令能正确恢复断点.否则程序的执行结果将无法预测.

⑶程序中加入必要的说明和注释,增加程序的可读性和可理解性,一般应说明:

子程序名、功能、入/出口地址、使用的寄存器和存贮单元,子程序中调用的其它子程序名等。

⑷处理好调用程序与子程序之间的参数传递,常用的参数传递方法有:

①借助寄存器②借助内存中建立的参数表③借助堆栈

4.1.3子程序举例

例1(p116-1.asm)写一个把用ASCII码表示的两位十进制数转换为对应二进制数的子程序.

子程序入口参数:

DH=十位ASCII码,DL=个位ASCII码

出口参数:

AL=对应二进制数

转换方法:

高位×

10+低位

datasegment

pr1db“Inputtwonumbers:

$”

pr2db0ah,0dh,”Out:

dataends

assumecs:

code,ds:

data

start:

movax,data

movds,ax

movah,09h

leadx,pr1

int21h

movah,01h

int21h

movdh,al

movdl,al

callsubr

movbl,al;

显示程序入口bl=显示值

calldisp;

调用按二进制显示子程序

movah,4ch

subrproc;

moval,dh

andal,0fh

movah,10

mulah

movah,dl

andah,0fh

addal,ah

ret

subrendp

dispproc

leadx,pr2

movcx,08h

lp1:

rolbl,1

movdl,bl

anddl,01h

adddl,30h

movah,02h

looplp1

dispendp

endstart

例2(p116-2.asm)写一个把16位二进制数转换为4位十六进制数ASCII码的子程序,并用此子程序显示地址为F000:

0000H的字单元内容.

①把要转换的字数据循环左移4位,把最高4位移到最低4位

②析出最低4位,相当于一位十六进制数

③把析出的一位十六进制数转换为ASCII码

④重复上述操作4次.

入口参数:

DX=待转换的二进制数

DS:

BX=存放转换所得ASCII码串的缓冲区首地址

dsegsegment

buffdb4dup(0),"

H"

0dh,0ah,"

$"

dsegends

csegsegment

cseg,ds:

dseg

movax,dseg

movds,ax

movax,0f000h

moves,ax

movdx,es:

[0000h]

movbx,offsetbuff

callhtascs

movdx,offsetbuff

htascsproc

movcx,0404h

lp:

roldx,cl

moval,dl

addal,30h

cmpal,39h

jbenext

addal,07h

next:

mov[bx],al

incbx

decch

jnzlp

htascsendp

csegends

例3(P118-1.ASM)写一个把16位二进制无符号数转换为5位十进制数ASCII码的子程序,并用该子程序转换地址为FFFF:

0000h的一个字

转换方法:

除10取余数,并把余数转换ASCII码,先取得的余数在低位,后取得的余数在高位。

入口参数:

AX=欲转换的二进制数

出口参数:

转换得到的十进制ASCII码串按由高位到低位的顺序存放在缓冲区

dsegsegment

buffdb18dup("

csegsegment

movax,0ffffh

moves,ax

movax,es:

movbx,offsetbuff

callbtoasc

movdx,offsetbuff

movah,09h

int21h

movah,4ch

btoascproc

xorcx,cx

movsi,10

xordx,dx

divsi

adddl,30h

pushdx

inccx

orax,ax

jzlp1

jmplp

popdx

mov[bx],dl

looplp1

btoascendp

4.1.4子程序说明信息

为了方便子程序的使用,应给简明确切的说明,上面已经讲述到这方面的要求,下面给出一总结性的说明:

前三点:

即子程序名、功能描述、入口和出口参数是必须给出来的,否则难以使用。

⑴子程序名

⑵功能描述

⑶入口和出口叁数

⑷所用的寄存器和存储单元

⑸使用的算法和重要的性能指标

⑹其它调用注意事项和说明信息

⑺调用实例

4.1.5寄存器的保护与恢复

为了在子程序执行结束后,返回调用程序(主程序),程序能正常继续运行,应该在执行子程序的操作前,保护寄存器中原来的数据,当子程序操作结束后恢复寄存器中原有的数据,以确保程序的顺利运行。

方法一:

在调用程序(主程序)中保护寄存器(压入堆栈)和恢复寄存器(弹出堆栈)中的数据。

即,在调用子程序前,把相关的寄存器的内容用PUSH指令压入堆栈,当由子程序返回调用程序时,又用POP指令把这些寄存器原来的内容由堆栈中取回到对应的寄存器中。

优点:

仅需要保护和恢复程序在主、子程序中都要用到的寄存器,不保护就会影响程序正常运行的寄存器。

缺点:

使主程序中出现大量的PUSH、POP指令,如果子程序调用频繁应就会使主程序编写极为繁琐,且不易理解,甚至会漏掉某些应该保护的寄存器。

方法二:

在调用子程序中保护寄存器(压入堆栈)和恢复寄存器(弹出堆栈)中的数据。

即在子程序的开始,用PUSH指令所在子程序中要使用改变内容的寄存中的数据送堆栈中保护起来,在子程序返回主程序之前,即在RET指令前,用POP指令把保护在堆栈中的数据送回到相应的寄存器中。

主程序编写方便,不必考虑保护哪些寄存器,且不会造成数据保护的因遗漏。

更为重要的是,这样写出来的子程序相对独立,与主程序没有关系,是模块化程序设计的基础。

pushax

pushbx

pushcx

pushdx

movcx,10

divcx

cmpdx,0

jenext

popdx

popcx

popbx

popax

说明:

⑴在.286、.386等模式下可以使用PUSHA和POPA指令简程序的编写。

格式:

PUSHA

功能:

把通用寄存器AX、BX、CX、DX、BX、SP、BP、SI、DI的内容顺序推入堆栈。

POPA

把用PUSHA保存到堆栈中的8个通用寄存器的值弹回到相应的寄存中,并丢去原先SP的值

用这两条指令,上述子程序可以简化为:

pusha

popa

⑵如果程序需要,可以在主程序中用PUSHF和POPF保护和恢复标志寄存器的标志。

各标志位中,常作为入口、出口参数的是CF

⑶一般来讲,不需要保护含有子程序入口参的寄存器。

⑷当然,若有必要,也可以象保护和恢复寄存器的数据一样,可以保护和恢复某些关键的存储单元中的数据。

4.2主程序与子程序间的参数传递

主程序与子程序间的参数传递有多种方法:

寄存器传递法、约定内存单元传递法、堆栈传递法、CALL后续区传递法。

4.2.1利用寄存器传递参数

利用寄存器传递数据简单方便,但由于寄存器的个数较少,只适用于所要传递的数较少的情况。

上面的大多数例子都是利用寄存器传递的参数。

例1(p121-1.ASM)写一个把大写字母改为小写字母的子程序。

在第三章我们介绍过:

由ASCII码表(P6)可知:

A、B、C……的ASCII码:

01000001B、01000010B、01000011B、……

a、b、c……的ASCII码:

01100001B、01100010B、01100011B、……

所以,只要把大写字母ASCII码中的D5位由0改为1,便实现了由大写转换为小写。

要实现这个操作只需要用一个D5=1,其它各位均为0的字节与大写字母的ASCII码进行或操作(OR)。

这个字节=00100000B=20H

由此可知,程序用不需要字符是大写还是小写进行判断。

pr1db"

Inputastring:

pr2db0ah,0dh,"

Capitalstring:

buffdb100

nudb0

stringdb100dup("

movah,0ah

leadx,buff

xorch,ch

movcl,nu

movsi,offsetstring

moval,[si]

calluptolw

mov[si],al

incsi

looplp

leadx,pr2

leadx,string

uptolwproc

pushf

oral,20h

popf

uptolwendp

例2(p122-1.asm)写一个判别字符是否为数字字符的子程序,并利用该子程序把一个字符串中的所有数字字符删除。

方法:

用ASCII码进行比较,其ASCII码值在30H与39H之间是数字字符。

AL=字符的ASCII码

是数字字符,CF=0;

否则CF=1

pr2db0dh,0ah,"

Out:

buffdb100

movdx,offsetpr1

movdi,si

incsi

callisdecm

jncnext

mov[di],al

incdi

looplp

moval,[si];

前移一个”$”

isdecmproc

cmpal,30h

jbisdecm1;

al<

30h:

cf=1,非数字

cmpal,3ah;

cf=1:

3ah,是数字

cmc;

CF取反,使用其与要求一致

isdecm1:

isdecmendp

4.2.2利用约定的存储单元传递参数

利用数据段或附加段中的变量或参数表来传递参数,程序编写方便,且一般不会因为参数传递问题出错。

存在的问题是子程序的通用性降低。

例3(P123-1.ASM)实现32位(4字节)数相加的子程序。

用两次字加法实现双字数据的加法操作,第二次加法操作用ADC。

两个加数存放在双字缓冲区DATA1和DATA2

双字缓冲区DATA3,在DATA3后的第5个字节存放进位

注:

下面程序中增加了按十六进制ASCII码显示加数、和的操作。

data1dd12345678h

data2dd98765432h

data3dd?

data4dd?

;

显示缓冲区

callmadd

movax,wordptrdata1

movwordptrdata4,ax

movax,wordptrdata1+2

movwordptrdata4+2,ax

calldisp

movax,wordptrdata2

movax,wordptrdata2+2

movax,wordptrdata3

movax,wordptrdata3+2

maddproc;

双字加法子程序

pushcx

pushsi

movcx,2

xorsi,si

madd1:

movax,wordptrdata1[si]

adcax,wordptrdata2[si]

movwordptrdata3[si],ax

loopmadd1

moval,0

adcal,0

movbyteptrdata3+4,al

popsi

popcx

popax

maddendp

dispproc;

双字显示子程序

movcx,0804h

roldata4,cl

movdl,byteptrdata4

anddl,0fh

cmpdl,3ah

jbnext

adddl,07h

movdl,0ah

movah,02h

movdl,0dh

更常的方法是在存储区建立参数表,参数表的首地址通过寄存器传递给子程序,这样可以提高子程序的通用性。

[例4](p124-1.asm)设计一个把以ASCII码表示的十进制数字串转换为二进进制数的子程序。

假设十进制不大于65535。

10+低位

DS:

BX=缓冲区首地址,首字节为字串字符数

AX=转换得到的二进制数。

Inputanumberstring:

buffdb6

stringdb6dup("

0"

movax,data

leadx,pr1

movah,0ah

leabx,nu

calldtobin

movbx,ax

movcx,16

rolbx,1

anddl,01h

ad

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1