第六章 子程序结构

37
1/37 第第第 第第第第第 6.1 第第第第第第第第 第第 第第第第 第第第第第 () procedure_name PROC NEAR ( FAR ) …… procedure_name ENDP 第第第第第第第 第第第第第第第 第第第第 第第第第第第第第第第第第第第第第第第 第第第 第第第第第第第第第第第第第第 ,一, 第第第第第第 第第第第第第第第第第第第第第 第第第第第第第第第 第第第第第第第第 ,,

Upload: vivian

Post on 27-Jan-2016

131 views

Category:

Documents


5 download

DESCRIPTION

第六章 子程序结构. 6.1 子程序的设计方法 过程(子程序)定义伪操作 procedure_name PROC NEAR ( FAR ) …… procedure_name ENDP 过程名为标识符,是子程序入口的 符号地址 。. 把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成 子程序 子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率. 过程属性: ( 1 ) NEAR 属性: 调用程序和子程序 在同一代码段 中 (段内调用) - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第六章  子程序结构

1/37

第六章 子程序结构6.1 子程序的设计方法

过程(子程序)定义伪操作

procedure_name PROC NEAR ( FAR ) ……procedure_name ENDP

过程名为标识符,是子程序入口的符号地址。

把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序

子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率

Page 2: 第六章  子程序结构

2/37

过程属性:( 1 ) NEAR 属性:调用程序和子程序在同一代码段中 (段内调用)

( 2 ) FAR 属性:调用程序和子程序不在同一代码段中 (段间调用)

对简化段定义格式,在微型、小型和紧凑存储模式下,过程的缺省属性为 near ;在中型、大型和巨型存储模式下,过程的缺省属性为 far

对完整段定义格式,过程的缺省属性为 near

用户可以在过程定义时用 near 或 far 改变缺省属性

Page 3: 第六章  子程序结构

3/37

code segment main proc far ...... call subr1 ...... retmain endp

subr1 proc near ...... retsubr1 endp

code ends

code segment main proc far ...... call subr1 ...... ret

subr1 proc near ...... retsubr1 endp

main endpcode ends

例 6.1 调用程序和子程序在同一代码段中

一般主程序 MAIN

应定义为 FAR 属性

过程定义可嵌套

Page 4: 第六章  子程序结构

4/37

例 6.2 调用程序和子程序不在同一代码段中

segx segment subt proc far ...... ret subt endp ...... call subt ;段内调用 ...... segx ends

segy segment ...... call subt ;段间调用 ...... segy ends

Page 5: 第六章  子程序结构

5/37

保存与恢复寄存器 由于调用程序和子程序经常是分别编制的,所以它们使用的寄存器往往会发生冲突。

subt proc far push ax push bx push cx push dx ...... ;过程体 pop dx pop cx pop bx pop ax ret ;过程返回subt endp

主程序中使用的寄存器内容保留在堆栈中

在退出子程序前把寄存器内容恢复原状。

通常,子程序中用到的寄存器是应该保持的。如果使用寄存器在主程序和子程序之间传送参数的话,则不需要保留和恢复的过程。

Page 6: 第六章  子程序结构

6/37

子程序调用:隐含使用堆栈保存返回地址

call near ptr subp (段内) (1) 保存返回地址 (2) 转子程序 (IP) ← subp 的偏移地址

call far ptr subp (段间)

(1) 保存返回地址 (2) 转子程序

(CS) ← subp 的段地址 (IP) ← subp 的偏移地址

(IP)(SP)→

(IP)(SP)→

(CS)

子程序的调用和返回

Page 7: 第六章  子程序结构

7/37

(IP)(SP)→

(CS)

(flags)

int n ( n : 中断类型号) (1) 保存现场和返回地址 (2) 转中断处理程序 (IP) ← (n*4) (CS) ← (n*4+2)

子程序返回(中断返回):

( 1 ) ret

( 2 ) iret

int 21H P103

Page 8: 第六章  子程序结构

8/37

子程序的变量(参数)传送:调用程序在调用子程序时需要传送一些参数给子程序,子程序运行后也经常需要回送一些信息给调用程序。

入口参数(输入参数):主程序提供给子程序 出口参数(输出参数):子程序返回给主程序 参数的形式:

① 数据本身(传值)② 数据的地址(传址)

传递的方法:① 寄存器 ② 变量 ③ 堆栈

Page 9: 第六章  子程序结构

9/37

参数传送方式:

( 1 )通过寄存器传送变量

( 2 )子程序和调用程序在同一程序模块中,则子程序可直接访问模块中的变量。(通过存储器传送变量)

( 3 )通过地址表传送变量地址

( 4 )通过堆栈传送变量或变量地址

( 5 )子程序和调用程序不在同一程序模块中,则有两种传送方式:建立公共数据区和使用外部符号。 ( 通过存储器传送变量 )

Page 10: 第六章  子程序结构

10/37

例 6.3 十进制到十六进制的转换程序(通过寄存器传送变量)

decihex segment ; 1016 assume cs: decihex

main proc far push ds sub ax, ax push axrepeat: call decibin ; 102 ,取数存在 BX 中 call crlf ;显示回车和换行 call binihex ; 216 ,用十六进制显示 BX 中的数 call crlf ;显示回车和换行 jmp repeat retmain endp

Page 11: 第六章  子程序结构

11/37

decibin proc near ; 102 ,存数在 BX 中 mov bx, 0newchar: mov ah, 1 ; AH=1 时,是带回显的键盘 int 21h ;输入, AL=输入字符

sub al, 30h jl exit ; <0 退出 cmp al, 9d jg exit ; >9 退出 cbw ; AL符号扩展到 AX xchg ax, bx mov cx, 10d mul cx xchg ax, bx add bx, ax jmp newchar

exit: retdecibin endp

Page 12: 第六章  子程序结构

12/37

binihex proc near ; 216 ,参考例 5.1 mov ch, 4 ; CH为循环次数rotate: mov cl, 4 ; CL为移位次数 rol bx, cl mov al, bl and al, 0fh add al, 30h cmp al, 3ah jl printit add al, 7h ; ‘A’~’F’ printit: mov dl, al mov ah, 2 ; AH=2 ,为显示输出 int 21h dec ch jnz rotate retbinihex endp

Page 13: 第六章  子程序结构

13/37

crlf proc near mov dl, 0dh mov ah, 2 int 21h ;显示输出回车 mov dl, 0ah mov ah, 2 int 21h ;显示输出换行 retcrlf endp

decihex ends end main

Page 14: 第六章  子程序结构

14/37

例 6.4 累加数组中的元素(直接访问变量)data segment ary dw 1,2,3,4,5,6,7,8,9,10 ;数组初值 count dw 10 ;个数 sum dw ?data endscode segmentmain proc far assume cs:code, ds:datastart: push ds sub ax, ax push ax mov ax, data mov ds, ax call near ptr proadd retmain endp

Page 15: 第六章  子程序结构

15/37

proadd proc near push ax push cx ;保留寄存器 push si lea si, ary mov cx, count;循环次数 xor ax, ax ; ax 寄存器清零

next: add ax, [si] add si, 2 loop next mov sum, ax pop si pop cx ;恢复寄存器 pop ax ret

proadd endpcode ends end start

Page 16: 第六章  子程序结构

16/37

data segment

ary dw 1,2,3,4,5,6,7,8,9,10count dw 10sum dw ?

ary1 dw 10,20,30,40,50,60,70,80,90,100count1 dw 10sum1 dw ?

data ends

如果数据段定义如下:

* 如果直接访问内存变量,那么累加数组 ary 和数组 ary1中的元素不能用同一个子程序 proadd

Page 17: 第六章  子程序结构

17/37

例 6.4 累加数组中的元素(通过地址表传送变量地址)data segment ary dw 10,20,30,40,50,60,70,80,90,100 count dw 10 sum dw ? table dw 3 dup (?) ; 地址表data ends

code segmentmain proc far assume cs:code, ds:datastart: push ds sub ax, ax push ax mov ax, data mov ds, ax mov table, offset ary ;首地址放在 table mov table+2, offset count ; count 的地址放在 table+2 mov table+4, offset sum ; sum 的地址放在 table+4 mov bx, offset table ;地址表的首地址通过 bx 传送 call proadd ret main endp

在主程序中建立一个地址表,把要传送给子程序的参数放在地址表中

Page 18: 第六章  子程序结构

18/37

proadd proc near push ax push cx push si push di mov si, [bx] mov di, [bx+2] mov cx, [di] mov di, [bx+4] xor ax, ax

next: add ax, [si] add si, 2 loop next mov [di], ax pop di pop si pop cx pop ax ret

proadd endpcode ends

end start

ary 10 0000

20 0002 30 40 50 60 70 80 90 100

count 10 0014

sum ? 0016 table 0000 0018 (bx)

0014

0016

(si)

(di)

Page 19: 第六章  子程序结构

19/37

data segmentary dw 1,2,3,4,5,6,7,8,9,10count dw 10sum dw ?ary1 dw 10,20,30,40,50,60,70,80,90,100count1 dw 10sum1 dw ?table dw 3 dup (?) ;地址表data ends

如果数据段定义如下:

代码段中两次调用 proadd mov table, offset ary mov table+2, offset count mov table+4, offset sum mov bx, offset table call proadd

mov table, offset ary1 mov table+2, offset count1 mov table+4, offset sum

1 mov bx, offset table call proadd

Page 20: 第六章  子程序结构

20/37

例 6.4 累加数组中的元素(通过堆栈传送变量地址)

data segment ary dw 10,20,30,40,50,60,70,80,90,100 count dw 10 sum dw ?data ends

stack segment dw 100 dup (?) tos label word ;栈底类型stack ends

Page 21: 第六章  子程序结构

21/37

code1 segmentmain proc far assume cs:code1, ds:data, ss:stackstart: mov ax, stack mov ss, ax mov sp, offset tos push ds sub ax, ax push ax mov ax, data mov ds, ax

mov bx, offset ary push bx ;数组首地址入栈 mov bx, offset count push bx ;数组个数地址入栈 mov bx, offset sum push bx ;和地址入栈

call far ptr proadd ret

main endpcode1 ends

Page 22: 第六章  子程序结构

22/37

code2 segment assume cs: code2proadd proc far

push bp mov bp, sp

push ax push cx push si push di

mov si, [bp+0ah] mov di, [bp+8] mov cx, [di] mov di, [bp+6]

(bp)+0a 0000

(sp) (di) (si) (cx) (ax)

(bp) (bp) (ip) (cs)

(bp)+6 0016

(bp)+8 0014

0

(ds)

xor ax, axnext: add ax, [si]

add si, 2 loop next mov [di], ax

pop di pop si pop cx pop ax

pop bp

ret 6proadd endpcode2 ends end start

Call 调用

Page 23: 第六章  子程序结构

23/37

从连接的角度看,把用户定义的符号分为外部符号和局部符号

局部符号:在模块中定义,又在本模块中使用的符号。

外部符号:在模块中定义,可在另一个模块中使用的符号。

外部符号定义的两种方式:

PUBLIC 伪操作:

功能:在一个模块中定义的符号可提供给其他模块使用

格式: PUBLIC symbol[ ,…… ]

EXTRN 伪操作:

功能:在另一个模块中定义的符号,而在本模块中使用该符号。

格式: EXTRN symbol name : type [ ,…… ]

其中符号可以是变量、标号、过程名等。

Page 24: 第六章  子程序结构

24/37

增强功能的过程定义伪操作

格式:过程名 PROC [ 属性字段 ] [USES字段 ] [ ,参数字段 ]

……

过程名 ENDP 属性字段组成:

★distance :类型属性 NEAR 或 FAR

★language type :说明该过程可作为某高级语言程序的子过程

★visibility :说明该过程的可见性,可用 private 或 public 。

private 使该过程的可见性只能在当前的源文件。

public 则允许其他模块调用该过程。

★prologue :是一个宏名,允许用宏来控制过程的入口和出口

Page 25: 第六章  子程序结构

25/37

USES字段:允许用户指定所需要保存和恢复的寄存器表

MASM将在过程入口自动生成 push指令来保存这些寄存器,并在过程出口的 ret指令前自动生成 pop指令来恢复这些寄存器。

参数字段:允许用户指定该过程所用参数

格式:参数名:类型 [ ,参数名:类型 ]

MASM规定,在过程内可用 LOCAL 为局部变量申请空间。 格式: LACAL 变量定义 [ ,变量定义 ]

变量定义格式: label ;默认为 word 型

label : type

label[count] : type ;申请数组 count 为个数

Page 26: 第六章  子程序结构

26/37

例 6.4 用增强功能的过程定义伪操作实现.CODE CODE2

PROADD PROC PASCAL USES AX CX SI DI

PARA:WORD,PARC:WORD,PARS:WORD

MOV SI , PARA ; 数组的首地址 ( 第一次入栈的数 )

MOV DI , PARC ;count 的地址 ( 第二次入栈的数 )

MOV CX , [DI]

MOV DI , PARS ;sum 的地址 ( 第三次入栈的数 )

XOR AX , AX

NEXT: ADD AX , [SI]

ADD SI , 2

LOOP NEXT

MOV [DI] , AX

RET

PROADD ENDP

Page 27: 第六章  子程序结构

27/37

6.2 子程序嵌套

子程序的嵌套

主程序 子程序 A 子程序 B

proc_A proc_B

…... …… …...

call proc_A call proc_B …...

…… …… ret

ret

子程序的递归:一个子程序调用的子程序是它自身.

Page 28: 第六章  子程序结构

28/37

堆栈溢出

  堆栈区域是在堆栈定义时就确定了,因而堆栈工作过程中有可能产生溢出.

 堆栈上溢:堆栈已满,但还想再存入信息.

 堆栈下溢:堆栈已空,但还想取出信息.

Page 29: 第六章  子程序结构

29/37

6.3 子程序举例例 6.9 十六进制到十进制的转换程序(通过寄存器传送变量)

hexidec segment ; 1610 main proc far assume cs: hexidecstart:

push ds sub ax, ax push ax

repeat: call hexibin ; 162 ,放在 bx 中 call crlf call binidec ; 210 call crlf jmp repeat ret

main endp

Page 30: 第六章  子程序结构

30/37

hexibin proc near ; 162 mov bx, 0newchar:

mov ah, 1 int 21h sub al, 30h    jl exit cmp al, 10d jl add_to sub al, 27h ; ‘ a ’ ~ ‘ f ’ cmp al, 0ah jl exit cmp al, 10h jge exit

add_to: mov cl, 4 shl bx, cl mov ah, 0 add bx, ax jmp newchar

exit: rethexibin endp

Page 31: 第六章  子程序结构

31/37

binidec proc near ; 210 mov cx, 10000d call dec_div mov cx, 1000d call dec_div mov cx, 100d call dec_div mov cx, 10d call dec_div mov cx, 1d call dec_div ret

binidec endp

dec_div proc near mov ax, bx mov dx, 0 div cx mov bx, dx mov dl, al add dl, 30h mov ah, 2 int 21h retdec_div endp

Page 32: 第六章  子程序结构

32/37

crlf proc near mov dl, 0ah mov ah, 2 int 21h mov dl, 0dh mov ah, 2 int 21h retcrlf endp

hexidec ends end start

Page 33: 第六章  子程序结构

33/37

DOS 功能调用 INT 21H 用户在程序中调用 DOS 提供的一些子功能:

( 1 )一般设备的输入输出 ( 2 )磁盘的输入输出及磁盘文件的管理 ( 3 )其它 调用方法:

( 1 )设置调用参数( 2 )MOV AH, 功能号( 3 ) INT 21H

Page 34: 第六章  子程序结构

34/37

AH=1 :键盘输入并回显(可用 ctrl+c 或 ctrl+break自动退出 )

AH=8 : 不回显键盘输入(可用 ctrl+c 或 ctrl+break自动退出 )

AH=7 :直接键盘输入 ( 不能退出 , 也不回显 )

入口参数:无

出口参数: AL= 输入字符的 ASCII 码

AH=2 :显示输出

入口参数: DL= 输出字符的 ASCII

出口参数:无

AH=9:显示字符串

入口参数: DS:DX=串地址,字符串以‘ $’ 结尾

出口参数:无

Page 35: 第六章  子程序结构

35/37

( 1 ) DOS键盘功能调用

例:单字符输入 ( AH=1 )

get-key: mov ah, 1 int 21h cmp al, ‘Y’ je yes cmp al, ‘N’ je no jne get_key yes: …… no: ……

Page 36: 第六章  子程序结构

36/37

例:输入字符串 ( AH=0ah ) 定义缓冲区:

( 1 ) maxlen db 32 actlen db ? string db 32 dup (?)

( 2 ) maxlen db 32, 0, 32 dup (?)

( 3 ) maxlen db 32, 33 dup (?)

lea dx, maxlen mov ah, 0ah int 21h

20 maxlen actlen

‘H’

‘O’

‘W’

‘A’

‘R’

‘E’

‘Y’

‘O’

‘U’

20

20

0d

string0b

(DX)

Page 37: 第六章  子程序结构

37/37

( 2 ) DOS 显示功能调用 ( AH=2,5 ,9 )

例:显示单个字符 ( AH=2 )mov ah, 2mov dl, ‘A’int 21h

例:显示字符串 ( AH=9 )string db ‘HELLO’, 0dh, 0ah, ‘$’mov dx, offset stringmov ah, 9int 21h

( 3 ) DOS打印功能 ( AH=5 )

DL= 打印字符返回