10.8 ~ 10.12

2020-11-08
2020-11-08
4 min read
Hits

  《汇编语言(第3版)》10.8 ~ 10.12、《零基础入门学习汇编语言》P49 ~ 51

10.8 mul 指令

  因下面要用到,我们介绍一下 mul 指令,mul 是乘法指令,使用 mul 做乘法的时候

  1. 相乘的两个数:要么是 8 位,要么是 16 位。
    1. 8 位:AL 中和 8 位寄存器或内存字节单元中;
    2. 16 位:AX 中和 16 位寄存器或内存单元中。
  2. 结果
    1. 8 位:AX 中;
    2. 16 位:DX(高位)和 AX(低位)中。

  格式如下

mul reg
mul 内存单元

  内存单元可以用不同的寻址方式给出,比如

mul byte ptr ds:[0]     # 含义为:(ax)=(al)×((ds)×16+0)
mul word ptr [bx+si+8]
含义为:
(ax)=(ax)×((ds)×16+(bx)+(si)+8) 结果的低 16 位;
(dx)=(ax)×((ds)×16+(bx)+(si)+8) 结果的高 16 位;

  例如

  1. 计算 100×10 100 和 10 小于 255,可以做 8 位乘法,程序如下

    mov al,100
    mov bl,10
    mul bl
    结果:(ax)=1000(03E8H)
    
  2. 计算 100×10000 100 小于 255,可 10000 大于 255,所以必须做 16 位乘法,程序如下

    mov ax,100
    mov bx,10000
    mul bx
    结果:(ax)=4240H,(dx)=000FH(F4240H=1000000)
    

10.9 模块化程序设计

  从上面我们看到,call 与 ret 指令共同支持了汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。

  因为现实的问题比较复杂,对现实问题进行分析时,把它转化成相互联系、不同层次的子问题,是必须的解决方法。

  而 call 和 ret 指令对这种分析方法提供了程序实现上的支持。利用 call 和 ret 指令,我们可以用简洁的方法,实现多个互相联系、功能独立的子程序来解决一个夫复杂的问题。

10.10 参数和结果传递的问题

  子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者。

  其实,我们讨论参数和返回值传递的问题,实际上就是在探讨,应该如何存储子程序需要的参数和产生的返回值。

  我们设计一个子程序,可以根据提供的 N,来计算 N 的 3 次方。

  这里有两个问题

  1. 我们将参数 N 存储在什么地方?
  2. 计算得到的数值,我们存储在什么地方?

  很显然,我们可以用寄存器来存储,可以将参数放到 bx 中;

  因为子程序中要计算 N×N×N,可以使用多个 mul 指令,为了方便,可将结果放到 dx 和 ax 中。

  子程序

  1. 说明:计算 N 的 3 次方

  2. 参数:(bx)=N

  3. 结果:(dx:ax)= $ N^3 $

    cube:mov ax,bx
    	mul bx
    	mul bx
    	ret
    

我们在编程的时候要注意良好的风格,对于程序应有详细的注释。子程序的注释信息应该包含对子程序的功能、参数和结果的说明。

  用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反

  1. 调用者将参数送入参数寄存器,从结果寄存器中取到返回值;
  2. 子程序从参数寄存器中取到参数,将返回值送入结果寄存器。

10.11 批量数据的传递

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

  寄存器的数量终究有限,我们不可能简单地用寄存器来存放多个需要传递的数据。对于返回值,也有同样的问题。

  在这种时候,我们将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。

  对于具有批量数据的返回结果,也可用同样的方法。

  编程:将 data 段中的字符串转化为大写。

assume cs:code

data segment
	db 'conversation'
date 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

除了寄存器、内存传递参数外,还有一种通用的方法使用栈来传递参数。关于这种技巧请参看附注 4。

10.12 寄存器冲突的问题

  设计一个子程序

  1. 功能:将一个全是字母,以 0 结尾的字符串,转化为大写。

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

    db 'conversation',0
    

  分析

  1. 应用这个子程序,字符串的内容后面定要有一个 0,标记字符串的结束。子程序可以依次读取每个字符进行检测,如果不是 0,就进行大写的转化,如果是 0,就结束处理。
  2. 由于可通过检测 0 而知道是否已经处理完整个字符串,所以子程序可以不需要字符串的长度作为参数。我们可以直接用 jcxz 来检测 0。

  子程序实现代码

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

  子程序的应用

  将 data 段中的字符串全部转化为大写

assume cs:code
data segment
	db 'word',0
	db 'unix',0
	db 'wind',0
	db 'good'.0
data ends
Avatar

Hui.Ke

❤ Cyber Security | Safety is a priority.