10.8 ~ 10.12
《汇编语言(第3版)》10.8 ~ 10.12、《零基础入门学习汇编语言》P49 ~ 51
10.8 mul 指令
因下面要用到,我们介绍一下 mul 指令,mul 是乘法指令,使用 mul 做乘法的时候
- 相乘的两个数:要么都是 8 位,要么都是 16 位。
- 8 位:AL 中和 8 位寄存器或内存字节单元中;
- 16 位:AX 中和 16 位寄存器或内存字单元中。
- 结果
- 8 位:AX 中;
- 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 位;
例如
计算 100×10 100 和 10 小于 255,可以做 8 位乘法,程序如下
mov al,100 mov bl,10 mul bl 结果:(ax)=1000(03E8H)
计算 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 次方。
这里有两个问题
- 我们将参数 N 存储在什么地方?
- 计算得到的数值,我们存储在什么地方?
很显然,我们可以用寄存器来存储,可以将参数放到 bx 中;
因为子程序中要计算 N×N×N,可以使用多个 mul 指令,为了方便,可将结果放到 dx 和 ax 中。
子程序
说明:计算 N 的 3 次方
参数:(bx)=N
结果:(dx:ax)= $ N^3 $
cube:mov ax,bx mul bx mul bx ret
我们在编程的时候要注意良好的风格,对于程序应有详细的注释。子程序的注释信息应该包含对子程序的功能、参数和结果的说明。
用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反
- 调用者将参数送入参数寄存器,从结果寄存器中取到返回值;
- 子程序从参数寄存器中取到参数,将返回值送入结果寄存器。
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 寄存器冲突的问题
设计一个子程序
功能:将一个全是字母,以 0 结尾的字符串,转化为大写。
程序要处理的字符串以 0 作为结尾符,这个字符串可以如下定义
db 'conversation',0
分析
- 应用这个子程序,字符串的内容后面定要有一个 0,标记字符串的结束。子程序可以依次读取每个字符进行检测,如果不是 0,就进行大写的转化,如果是 0,就结束处理。
- 由于可通过检测 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