1 / 37

第六章 子程序结构

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

vivian
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. 第六章 子程序结构 • 6.1 子程序的设计方法 • 过程(子程序)定义伪操作 • procedure_name PROC NEAR ( FAR) • …… • procedure_name ENDP • 过程名为标识符,是子程序入口的符号地址。 • 把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序 • 子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率

  2. 过程属性: • (1)NEAR属性:调用程序和子程序在同一代码段中 • (段内调用) • (2)FAR属性:调用程序和子程序不在同一代码段中 • (段间调用) • 对简化段定义格式,在微型、小型和紧凑存储模式下,过程的缺省属性为near;在中型、大型和巨型存储模式下,过程的缺省属性为far • 对完整段定义格式,过程的缺省属性为near • 用户可以在过程定义时用near或far改变缺省属性

  3. 例6.1 调用程序和子程序在同一代码段中 code segment main proc far ...... call subr1 ...... ret main endp subr1 proc near ...... ret subr1 endp code ends code segment main proc far ...... call subr1 ...... ret subr1 proc near ...... ret subr1 endp main endp code ends 一般主程序MAIN应定义为FAR属性 过程定义可嵌套

  4. 例6.2 调用程序和子程序不在同一代码段中 • segx segment • subt proc far • ...... • ret • subt endp • ...... • call subt ;段内调用 • ...... • segx ends • segy segment • ...... • call subt ;段间调用 • ...... • segy ends

  5. 保存与恢复寄存器 • 由于调用程序和子程序经常是分别编制的,所以它们使用的寄存器往往会发生冲突。 • subt proc far • push ax • push bx • push cx • push dx • ...... ;过程体 • pop dx • pop cx • pop bx • pop ax • ret ;过程返回 • subt endp 主程序中使用的寄存器内容保留在堆栈中 在退出子程序前把寄存器内容恢复原状。 通常,子程序中用到的寄存器是应该保持的。如果使用寄存器在主程序和子程序之间传送参数的话,则不需要保留和恢复的过程。

  6. (SP)→ (IP) (SP)→ (IP) (CS) 子程序的调用和返回 • 子程序调用:隐含使用堆栈保存返回地址 • call near ptr subp (段内) • (1) 保存返回地址 • (2) 转子程序 • (IP) ← subp的偏移地址 • call far ptr subp (段间) • (1) 保存返回地址 • (2) 转子程序 • (CS) ← subp的段地址 • (IP) ← subp的偏移地址

  7. (SP)→ (IP) (CS) (flags) int 21HP103 int n(n : 中断类型号) (1) 保存现场和返回地址 (2) 转中断处理程序 (IP) ← (n*4) (CS) ← (n*4+2) 子程序返回(中断返回): (1)ret (2)iret

  8. 子程序的变量(参数)传送:调用程序在调用子程序时需要传送一些参数给子程序,子程序运行后也经常需要回送一些信息给调用程序。子程序的变量(参数)传送:调用程序在调用子程序时需要传送一些参数给子程序,子程序运行后也经常需要回送一些信息给调用程序。 • 入口参数(输入参数):主程序提供给子程序 • 出口参数(输出参数):子程序返回给主程序 • 参数的形式: • ① 数据本身(传值) • ② 数据的地址(传址) • 传递的方法: • ① 寄存器 ② 变量 ③ 堆栈

  9. 参数传送方式: (1)通过寄存器传送变量 (2)子程序和调用程序在同一程序模块中,则子程序可直接访问模块中的变量。(通过存储器传送变量) (3)通过地址表传送变量地址 (4)通过堆栈传送变量或变量地址 (5)子程序和调用程序不在同一程序模块中,则有两种传送方式:建立公共数据区和使用外部符号。(通过存储器传送变量)

  10. 例6.3 十进制到十六进制的转换程序(通过寄存器传送变量) • decihex segment ; 1016 • assume cs: decihex • main proc far • push ds • sub ax, ax • push ax • repeat: call decibin ; 102,取数存在BX中 • call crlf ;显示回车和换行 • call binihex • ; 216,用十六进制显示BX中的数 • call crlf ;显示回车和换行 • jmp repeat • ret • main endp

  11. decibin proc near ; 102,存数在BX中 • mov bx, 0 • newchar: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: ret • decibin endp

  12. binihex proc near ; 216,参考例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 ret binihex endp

  13. crlf proc near mov dl, 0dh mov ah, 2 int 21h ;显示输出回车 mov dl, 0ah mov ah, 2 int 21h ;显示输出换行 ret crlf endp decihex ends end main

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

  15. 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 endp • code ends • end start

  16. 如果数据段定义如下: data segment ary dw 1,2,3,4,5,6,7,8,9,10 count dw 10 sum dw ? ary1 dw 10,20,30,40,50,60,70,80,90,100 count1 dw 10 sum1 dw ? data ends *如果直接访问内存变量,那么累加数组ary和数组ary1中的元素不能用同一个子程序proadd

  17. 例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 segment • main proc far • assume cs:code, ds:data • start: 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 在主程序中建立一个地址表,把要传送给子程序的参数放在地址表中

  18. 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 endp • code ends • end start ary 10 0000 60 30 40 80 50 90 100 0014 0016 70 20 0002 count 10 0014 sum  ? 0016 table 0000 0018 (bx) (si) (di)

  19. 如果数据段定义如下: data segment ary dw 1,2,3,4,5,6,7,8,9,10 count dw 10 sum dw ? ary1 dw 10,20,30,40,50,60,70,80,90,100 count1 dw 10 sum1 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

  20. 例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

  21. code1 segment • main proc far • assume cs:code1, ds:data, ss:stack • start: • 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 endp • code1 ends

  22. (si) (cx) (ax) (ip) (cs) 0 (ds) (sp)  (di) (bp)  (bp) (bp)+6 0016 (bp)+8  0014 (bp)+0a  0000 • xor ax, ax • next: add ax, [si] • add si, 2 • loop next • mov [di], ax • pop di • pop si • pop cx • pop ax • pop bp • ret 6 • proadd endp • code2 ends • end start code2 segment assume cs: code2 proadd 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] Call调用

  23. 从连接的角度看,把用户定义的符号分为外部符号和局部符号从连接的角度看,把用户定义的符号分为外部符号和局部符号 局部符号:在模块中定义,又在本模块中使用的符号。 外部符号:在模块中定义,可在另一个模块中使用的符号。 外部符号定义的两种方式: PUBLIC伪操作: 功能:在一个模块中定义的符号可提供给其他模块使用 格式: PUBLIC symbol[,……] EXTRN伪操作: 功能:在另一个模块中定义的符号,而在本模块中使用该符号。 格式: EXTRN symbol name:type [,……] 其中符号可以是变量、标号、过程名等。

  24. 增强功能的过程定义伪操作 格式:过程名 PROC [属性字段] [USES字段] [,参数字段] …… 过程名 ENDP 属性字段组成: ★distance:类型属性NEAR或FAR ★language type:说明该过程可作为某高级语言程序的子过程 ★visibility:说明该过程的可见性,可用private或public。 private使该过程的可见性只能在当前的源文件。 public则允许其他模块调用该过程。 ★prologue:是一个宏名,允许用宏来控制过程的入口和出口

  25. USES字段:允许用户指定所需要保存和恢复的寄存器表USES字段:允许用户指定所需要保存和恢复的寄存器表 MASM将在过程入口自动生成push指令来保存这些寄存器,并在过程出口的ret指令前自动生成pop指令来恢复这些寄存器。 参数字段:允许用户指定该过程所用参数 格式:参数名:类型 [ ,参数名:类型 ] MASM规定,在过程内可用LOCAL为局部变量申请空间。 格式:LACAL 变量定义 [,变量定义] 变量定义格式:label ;默认为word型 label:type label[count]:type ;申请数组count为个数

  26. 例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

  27. 6.2 子程序嵌套 子程序的嵌套 主程序子程序A子程序B proc_Aproc_B …... …… …... call proc_A call proc_B…... …… …… ret ret 子程序的递归:一个子程序调用的子程序是它自身.

  28. 堆栈溢出   堆栈区域是在堆栈定义时就确定了,因而堆栈工作过程中有可能产生溢出.  堆栈上溢:堆栈已满,但还想再存入信息.  堆栈下溢:堆栈已空,但还想取出信息.

  29. 6.3 子程序举例 • 例6.9 十六进制到十进制的转换程序(通过寄存器传送变量) • hexidec segment; 1610 • main proc far • assume cs: hexidec • start: • push ds • sub ax, ax • push ax • repeat: • call hexibin ; 162,放在bx中 • call crlf • call binidec ; 210 • call crlf • jmp repeat • ret • main endp

  30. hexibin proc near ; 162 • mov bx, 0 • newchar: • 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: ret • hexibin endp

  31. binidec proc near ; 210 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 ret dec_div endp

  32. crlf proc near mov dl, 0ah mov ah, 2 int 21h mov dl, 0dh mov ah, 2 int 21h ret crlf endp hexidec ends end start

  33. DOS功能调用 INT 21H • 用户在程序中调用DOS提供的一些子功能: • (1)一般设备的输入输出 • (2)磁盘的输入输出及磁盘文件的管理 • (3)其它 • 调用方法: (1)设置调用参数 (2)MOV AH, 功能号 (3)INT 21H

  34. AH=1:键盘输入并回显(可用ctrl+c或ctrl+break自动退出)AH=1:键盘输入并回显(可用ctrl+c或ctrl+break自动退出) AH=8 :不回显键盘输入(可用ctrl+c或ctrl+break自动退出) AH=7:直接键盘输入(不能退出,也不回显) 入口参数:无 出口参数:AL=输入字符的ASCII码 AH=2:显示输出 入口参数:DL=输出字符的ASCII 出口参数:无 AH=9:显示字符串 入口参数:DS:DX=串地址,字符串以‘$’结尾 出口参数:无

  35. (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: • ……

  36. maxlen 20 actlen string ‘H’ ‘O’ ‘W’ 20 ‘A’ ‘R’ ‘E’ 20 ‘Y’ ‘O’ ‘U’ 0d (DX) • 例:输入字符串 ( 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 0b

  37. (2) DOS显示功能调用 ( AH=2,5,9 ) • 例:显示单个字符 ( AH=2 ) • mov ah, 2 • mov dl, ‘A’ • int 21h • 例:显示字符串 ( AH=9 ) • string db ‘HELLO’, 0dh, 0ah, ‘$’ • mov dx, offset string • mov ah, 9 • int 21h • (3) DOS打印功能 ( AH=5 ) • DL=打印字符 返回

More Related