【汇编语言】call 和 ret 指令(三) —— 深度解析汇编语言中的批量数据传递与寄存器冲突-LMLPHP

前言

1. 批量数据的传递

1.1 存在的问题

前面学习的例程中,子程序 cube 只有一个参数,放在bx中。如果有两个参数,那么可以用两个寄存器来放,可是如果需要传递的数据有3个、4个或更多直至 N个,我们怎样存放呢?

1.2 如何解决这个问题

接下来我们来看下具体的落实……

1.3 示例演示

1.3.1 问题说明

下面看一个例子,

设计一个子程序,功能:将一个全是字母的字符串转化为大写。

这个子程序需要知道两件事,字符串的内容和字符串的长度。

因为字符串中的字母可能很多,所以不便将整个字符串中的所有字母都直接传递给子程序。但是,可以将字符串在内存中的。因为子程序中要用到循环,我们可以用loop指令,而循环的次数恰恰就是字符串的长度。

出于方便的考虑,可以将字符串的长度放到cx。

capital:	and byte ptr [si],11011111b		;将ds:si所指单元中的字母转化为大写
			inc si							;ds:si 指向下一个单元
			loop capital					
			ret

1.3.2 程序实现

assume cs:code

data segment
  db 'conversation'
data ends

code segment

start:	mov ax,data
      	mov ds,ax
      	mov si,0		;ds:si指向字符串(批量数据)所在空间的首地址
      
      	mov cx,12		;cx存放字符串的长度
		call capital

      	mov ax,4c00h
      	int 21h
		
capital:and byte ptr [si],11011111b
		inc si
		loop capital
      	ret
		
code ends

end start

2. 寄存器冲突问题的引入

2.1 问题引入

设计一个子程序:功能:将一个全是字母,以0结尾的字符串,转化为大写。

2.2 分析与解决问题

2.2.1 字符串定义方式

程序要处理的字符串以0作为结尾符,这个字符串可以如下定义:

db  ‘conversation’,0

2.2.2 分析子程序功能

应用这个子程序 ,字符串的内容后面定要有一个0,标记字符串的结束。

子程序可以依次读取每个字符进行检测,如果不是0,就进行大写的转化,如果是0,就结束处理。

我们可以直接。

2.2.3 得到子程序代码

;说明:将一个全是字母,以0结尾的字符串,转化为大写
;参数:ds:si指向字符串的首地址
;结果:没有返回值
capital:mov cl,[si]
		mov ch, 0
		jcxz ok							;如果(cx)=0,结束;如果不是0,处理
		and byte ptr [si], 11011111b	;将ds:si所指单元中的字母转化为大写
		inc si							;ds:si 指向下一个单元
		jmp short capital
     ok:ret

2.3 子程序的应用

来看一下这个子程序的应用。

2.3.1 示例1

assume cs:code
data segment
  db 'conversation',0
data ends

代码段中的相关程序如下。

mov ax,data
mov ds,ax
mov si,0
call capital

2.3.2 示例2

assume cs:code

data segment
  db 'word', 0
  db 'unix', 0
  db 'wind', 0
  db 'good', 0
data ends

可以看到,所有字符串的长度都是5(算上结尾符0),使用循环,重复调用子程序capital,完成对4个字符串的处理。

完整的程序如下。

code segment
start:	mov ax,data
      	mov ds,ax
      	mov bx,0

		mov cx,4
    s:	mov si,bx
		call capital
		add bx,5
		loop s

      	mov ax,4c00h
      	int 21h

capital:mov cl,[si]
		mov ch, 0
		jcxz ok
		and byte ptr [si], 11011111b
		inc si
		jmp short capital
     ok:ret

code ends

end start

3. 寄存器冲突问题的发现与解决

3.1 重看代码

上面的这个程序在思想上完全正确,但在细节上却有些错误,把错误找出来。

提示:问题在于cx的使用。

思考后看分析。

3.2 分析与解决问题

3.2.1 存在的冲突问题

问题在于cx的使用,主程序要使用cx记录循环次数,可是子程序中也使用了cx,。

3.2.2 解决方案的探讨

那么如何来避免这种冲突呢?

粗略地看,可以有以下两个方案。

(1)在编写调用子程序的程序时,注意看看子程序中有没有用到会产生冲突的寄存器,如果有,;

(2)在编写子程序的时候,不要使用会产生冲突的寄存器。

我们来分析一下上面两个方案的可行性:

(1)这将给调用子程序的程序的编写造成很大的麻烦,因为必须要小心检查所调用的子程序中是否有将产生冲突的寄存器。

比如说,在上面的例子中,我们在编写主程序的循环的时候就得检查子程序中是否用到了bx和cx,因为如果子程序中用到了这两个寄存器就会出现问题。

如果采用这种方案来解决冲突的话,那么在主程序的循环中,就不能使用cx寄存器,因为子程序中已经用到。

(2)这个方案也是不可能实现的,因为编写子程序的时候无法知道将来的调用情况。

可见,我们上面所设想的两个方案都不可行。

我们希望:

  • (1)编写调用子程序的程序的时候不必关心子程序到底使用了哪些寄存器。

  • (2)编写子程序的时候不必关心调用者使用了哪些寄存器。

  • (3)不会发生寄存器冲突。

3.2.3 给出解决方案。

3.2.4 子程序的标准框架

以后,我们编写子程序的标准框架如下:

【汇编语言】call 和 ret 指令(三) —— 深度解析汇编语言中的批量数据传递与寄存器冲突-LMLPHP

3.2.5 改进之前的程序

我们改进一下子程序 capital的设计:

capital: push cx
		 push si

 change: mov cl,[si]
		 mov ch, 0
		 jcxz ok
		 and byte ptr [si], 11011111b
		 inc si
		 jmp short capital
     
     ok: pop si
     	 pop cx
     	 ret

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

【汇编语言】call 和 ret 指令(三) —— 深度解析汇编语言中的批量数据传递与寄存器冲突-LMLPHP

12-02 06:54