汇编之子程序设计

汇编之子程序设计

1. 子程序的概念

1.1 子程序的引入

  • 在程序设计中,我们会发现一些多次无规律重复的程序段或语句序列。解决此类问题一个行之有效的方法就是将它们设计成可供反复调用的独立的子程序结构,以便在需要时调用。

1.2 子程序和主程序

  1. 子程序(过程):是指功能相对独立的一段程序。
  2. 主程序:调用子程序的程序称为主调程序或主程序。
  3. 子程序与主程序的关系:调用与被调用的关系。

1.3 子程序的优点

  • 子程序作为一个功能性模块,供一个程序甚至多个程序使用;
  • 可以简化源程序结构;
  • 提高程序的可读性与可维护性;
  • 有利于代码复用;
  • 提高程序的设计效率。

2. 子程序的调用与返回

  • 过程定义后,可在主程序中用CALLCALL指令,反复调用。过程结束,由返回指令RETRET返回主程序。

2.1 调用指令CALLCALL

2.1.1 段内调用

  1. 段内直接调用
    (1)格式:CALL OPR
    (2)执行操作:先保存断点:SPSP2SP←SP-2,将CALLCALL的下一条指令的IPIP入栈;在将子程序名OPROPR代表的偏移地址IP\to IP,转到子程序执行。
    (3)功能:子程序名直接写在指令中,作段内调用。
  2. 段内间接调用
    (1)格式:CALL WORD PTR OPR
    (2)执行操作:将断点处的IP入栈保存;如果子程序的偏移地址在16位寄存器中则把寄存器的内容IP\to IP;如果其偏移地址是用存储器中的一个字指出,则把改存储器单元的内容IP\to IP
    (3)功能:子程序的偏移地址由寄存器或存储单元指出,作段内调用。

2.1.2 段间调用

  1. 段间直接远调用
    (1)格式:CALL FAR PTR OPR
    (2)执行操作:先将CALLCALL的下一条指令的CS和IP分别入栈;再把子程序的偏移地址IP\to IP,子程序所在段的段地址CS\to CS
    (3)功能:子程序名用FAR PTRFAR\ PTR属性直接写在指令中,作跨段调用。
  2. 段间间接调用
    (1)格式:CALL DWORD PTR OPR
    (2)执行操作:先将CALL的下一条指令的CS和IP分别入栈;再把存储单元的(EA)IP(EA)\to IPEA+2CS(EA+2)\to CS
    (3)功能:子程序名保存在双字单元中,第一个字作偏移地址,第二个字作为段地址,做跨段调用。

2.2 返回指令RETRET

  1. 格式:RET [n]
  2. 执行操作:
    (1)段内返回(近返回)时,从堆栈段中弹出的断点仅修改IPIP
    (2)段间返回(远返回)时,从堆栈段中弹出断点的偏移地址IP\to IP,再弹出断点的段地址CS\to CS
    (3)如果是RET n指令,表示弹出断点后,再将堆栈段指针SP+nSP +n之后再返回。
  3. 功能:用于子程序中,返回到主程序的断点处继续执行。执行时,将断点从堆栈中弹出,修改IP或修改IPIPCSCS

3. 子程序的过程定义

3.1 伪指令PROC

  1. 定义:由子程序定义伪指令PROCPROCENDPENDP来完成。
  2. 格式:
子程序名  PROC [NEAR/FAR]
          ┆    ;过程体
子程序名  ENDP
  1. 说明:
    (1)程序名是子程序入口地址的符号表示。同标号一样,具有三种属性,即段属性、偏移地址属性以及类型属性。
    (2)PROCPROC表示子程序定义开始,ENDPENDP表示子程序定义结束。
    (3)类型属性分为NEAR进程属性和FAR远程属性。如果不写属性,系统默认为NEAR属性。

3.2 过程属性

  • 主程序与子程序在同一个代码段中则子程序使用NEAR属性。
  • 主程序与子程序不在同一个代码段中则子程序使用FAR属性。
  • CALL指令执行时,系统根据子程序名的属性决定保存断点的段地址和偏移地址。

4. 子程序参数传递

  • 主程序与子程序的参数传递:
    (1)入口参数(也称入口条件):是指主程序调用子程序前,为子程序内部数据处理准备所需的预置值;
    (2)出口参数:(也称出口条件):是子程序返回主程序后,把子程序处理的结果传递给主程序的数据。
  • 参数传递的基本方法有一下三种:

4.1 寄存器传参

  • 通过CPU寄存器传递参数。传递数据方便、快捷,但所能传递的数据长度和个数都有限。
  • 举例:求1+2的和的程序。要求将结果送到内存单元,并显示。(通过寄存器传送)
DATA SEGMENT
	SUM DB 0
DATA ENDS
STACK SEGMENT
	DB 100 DUP(?)
STACK ENDS
CODE SEGMENT
	ASSUME DS:DATA,SS:STACK,CS:CODE           
START:
	MOV AX,DATA
	MOV DS,AX
	MOV AL, 1
	MOV BL, 2
	CALL subprog
	mov ah,4cH
	int 21h
Subprog PROC
	ADD AL, BL
	OR AL, 30H
	MOV SUM, AL
	Mov dl,al
	Mov ah,2
	Int 21h
	RET	
subprog ENDP
CODE ENDS
	END START

4.2 存储单元传参

  • 通过内存单元(组)传递参数。传递数据的长度和个数可不受限制,程序设计比较灵活。
  • 举例:求1+2的和的程序。要求将结果送到内存单元,并显示。(通过存储单元传送)
DATA SEGMENT
	SUM DB 0
    D1 DB ?
    D2 DB ? 
DATA ENDS
STACK SEGMENT
	DB 100 DUP(?)
STACK ENDS
CODE SEGMENT
	ASSUME DS:DATA,SS:STACK,CS:CODE           
START:
	MOV AX,DATA
	MOV DS,AX
	MOV D1, 1
	MOV D2, 2
	CALL SPROG
	mov ah,4cH
	int 21h
SPROG PROC
	MOV AL, D1
	ADD AL, D2
	OR AL, 30H
	MOV SUM, AL
	Mov dl,al
	Mov ah,2
	Int 21h
	RET	
SPROG ENDP
CODE ENDS
	END START

4.3 堆栈传参

  • 堆栈法:通过堆栈传递参数。用堆栈保存所要传递的数据或存储地址,利用堆栈数据存取的特点,是常用的参数传递方法。
  • 举例:求1+2的和的程序。要求将结果送到内存单元,并显示。(通过堆栈传送:功能最强/最灵活/最复杂)
DATA SEGMENT
	SUM DB 0
DATA ENDS
STACK SEGMENT
	DB 100 DUP(?)
STACK ENDS
CODE SEGMENT
	ASSUME DS:DATA,SS:STACK,CS:CODE           
START:
	MOV AX,DATA
	MOV DS,AX
	MOV AL, 1
	MOV BL, 2
	MOV AH,0
	MOV BH,0
	PUSH AX
	PUSH BX
	CALL SPR
	POP BX
	POP AX
	mov ah,4cH
	int 21h
Spr	PROC
	PUSH BP
	MOV BP, SP
	MOV AX, [BP+6]
	MOV BX, [BP+4]
	ADD AL, BL
	OR AL, 30H
	MOV DL,AL
	MOV AH,2
	INT 21H 
	MOV SUM, AL
	POP BP
	RET	
Spr ENDP
CODE ENDS
	END START

4.4 现场保护

  • 主程序用到某些寄存器保存数据,转到子程序后,可能会修改寄存器中的值。因此,在进入子程序时,先要将这些寄存器保护起来,称为现场保护。
  • 一般采用PUSH指令入栈保存的方法。

5. 子程序的嵌套与递归

5.1 子程序的嵌套

  1. 定义:一个程序中可以有多个子程序,而且一个子程序也可以作为调用程序去调用另一个子程序,这称为子程序的嵌套。
  2. 子程序可以多次嵌套,套结构如下:
主程序
  ┋
CALL SUB1 
  ┋ 
子程序1
SUB1
  ┋
CALL SUB2
  ┋ 	
子程序2
SUB2
  ┋
CALL SUB3
  ┋  
一直嵌套下去 
  1. 说明:
    (1)子程序嵌套的层数原则上没有限制,只受堆栈容量大小的约束。子程序的调用和返回应正确使用CALL和RET;
    (2)各子程序之间所使用的寄存器合理保护和恢复,以避免各层子程序之间发生因寄存器冲突而出现错误的情况。
    (3)如果程序中使用堆栈来传递参数,则对堆栈的操作要格外小心,堆栈使用不当,可能造成程序不能正确返回。

5.2 子程序的递归

  • 定义:如果一个子程序调用的子程序就是它自身,这就称为子程序的递归。
  • 【例】 编程实现,计算两个矩阵中每行元素之和的程序,假设每行元素之和仍然是字数据。
    分析与要求:计算每个矩阵中各行元素之和的方法是相同的,所以可把它独立出来用子程序实现。主程序和子程序之间的参数传递通过堆栈实现,注意主程序与子程序中参数的读取及返回应当配合好。
DATA     SEGMENT
A           DW  12223344      ;A矩阵有3行,每行4个元素
			DW  55667788
			DW  99101112
B           DW  54321       ;B矩阵有2行,每行5个元素
			DW  109876
AI          DW  3                  ;A矩阵的行数
AJ          DW  4                  ;A矩阵的列数
BI          DW  2                  ;B矩阵的行数
BJ          DW  5                  ;B矩阵的列数
LINESUM  	DW  5   DUP( ? )	   ;A与B共5行,有5个行相加之和
DATA     ENDS
STAK     SEGMENT  ‘STACK’
			DB  100  DUP( ? )
STAK     ENDS
CODE     SEGMENT
	ASSUME   CS : CODE,DS:DATA, SS : STACK
START:
	MOV  AX,DATA
	MOV   DS,AX
	LEA   DI,LINESUM
	LEA   BX,A
	PUSH  BX; A矩阵元素首址进栈
	MOV  AX,AI; 取A阵行数
	PUSH   AX
	MOV  AX,AJ; 取A阵列数
	PUSH  AX
	CALL   SUMLINE
	LEA   BX,B
	PUSH  BX
	MOV  AX,BI; B矩阵行数取回AX中
	PUSH  AX; 进栈
	MOV  AX,BJ; B矩阵列数取回AX中
	PUSH   AX
	CALL  SUMLINE
	MOV  AX, 4C00H
	INT  21H
;----------------------------------------------------
;子程序名:SUMLINE
;功能:计算矩阵每行元素之和
;入口参数:矩阵偏移地址、矩阵行、列数进栈传递
;出口参数:矩阵每行之和存入LINESUM数组中
;占用寄存器:AX,BX,CX,DX,BP,FLAG
;----------------------------------------------------
SUMLINE  PROC  NEAR
	PUSH  AX        ;保护现场
	PUSH  BX
	PUSH  CX
	PUSH  DX
	PUSH  BP
	MOV  BP,SP
	PUSHF
	MOV  BX,[BP + 16]; 取矩阵元素的首地址
	MOV  CX,[BP + 14]; 取矩阵行数
LOP : MOV  DX,[BP + 12]; 取矩阵列数,即每行元素个数
	XOR   AX,AX
	NEXT : ADD  AX,[BX]
	ADD  BX,2
	DEC  DX
	JNZ   NEXT
	MOV[DI],AX; 存矩阵每行之和
	ADD   DI,2
	LOOP  LOP
	POPF                   ;恢复现场
	POP  BP
	POP  DX
	POP  CX
	POP  BX
	POP  AX
	RET  6
SUMLINE ENDP
CODE ENDS
	END START
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章