(sp)=(sp)-2
((ss)*16+(sp))=(IP)(IP与CS压栈)
(2) (CS)=标号处所在的段地址
(IP)=标号处所在的偏移地址
call far ptr 标号相当于:
push CS
push IP
jmp far ptr 标号
10.5转移地址在寄存器中的call指令
指令格式:call 16位寄存器
执行步骤:
(SP)=(SP)-2
((SS)*16+(SP))=(IP)
(IP)=(16位寄存器)
call 16位寄存器相当于:
PUSH IP
jmp 16位寄存器
10.6转移地址在内存中的call指令
两种格式:
一 call word ptr 内存地址
相当于: push IP
jmp word ptr 内存地址
二 call dword ptr 内存地址
相当于:push cs
push ip
jmp dword ptr 内存地址
10.7 call和ret的配合使用
如下代码执行后,bx寄存器中的值为多少:
assume cs:code
code segment
start:mov ax,1
mov cx,3
call s
mov bx,ax
mov ax,4c00h
int 21h
s:add ax,ax
loop s
ret
code ends
end start
debug单步执行结果看出(bx)=8
分析步骤如下:
1.cpu将call s的机器码读入,IP指向了 call后的指令mov bx,ax,然后CPU执行call s指令,将当前IP(指令mov bx,ax的偏移地址)压栈,并将IP改为标号S的偏移地址。
2.cpu从标号s开始执行,循环结束后,(ax)=8
3.cpu将ret的机器码读入,IP指向了ret指令后的内存单元,然后CPU执行ret指令,从栈中(即先前压入栈的mov bx,ax的偏移地址)弹出一个值存入IP,此时CS:IP指向mov bx,ax
4.cpu 从mov bx,ax开始执行,直至结束
10.8 mul指令
mul作为乘法指令,由如下规则
1.均为8位的乘数,一个放在AL中,另外一个放在8位寄存器或内存中
均为16位的乘数,一个放在AX中,另外一个放在16位寄存器或内存中
2.结果:如果乘数为8位,结果放在AX中,如果乘数为16位,高位放在DX,低位放在AX
格式:
mul reg
mul 内存单元
比如:
mul byte ptr ds:[0] 含义:(AX)=(AL)*((DS)*16+0);
mul word ptr [bx+si+8] 含义:(DX)=(AX)*((DS)*16+(bx)+(si)+8)结果的高16位
(DX)=(AX)*((DS)*16+(bx)+(si)+8)结果的低16位
10.9模块化程序设计及参数和结果传递问题
call与ret配合使用可以使得程序达到模块化设计的问题,这在编写功能庞大的程序时候是很用的,模块化程序的逻辑清楚,且可以重复复用模块代码。
子程序需要将程序功能执行完成后将结果返回给调用者。这里我们需要考虑如何存储和传递子程序需要的参数和返回值。
比如一个问题,设计一个子程序,提供一个参数N,计算N的三次方。
这里存在两个问题:
(1)将参数N存储在什么地方
(2)计算得到的数值,存储在什么地方。
这里我们可以将参数N放入寄存器BX中,将结果放到DX与AX中,因为是求三次方,我们可以用两次mul指令完成。
代码如下:
cube:mov ax,bx
mul bx
mul bx
ret
用寄存器是常用的传递参数和结果的方法,在调用的时候将参数传入参数寄存器。在调用返回时候,将结果存入结果寄存器。这种方式虽然常用,但是有个缺陷就是传递的参数和结果的个数有限,因为寄存的个数是有限的。
10.10 批量数据的传递
如何解决前面通过寄存器传递参数和结果的时候寄存器数量的限制,导致有很多参数的时候这种寄存器传递参数或者结果的方式不可取。
这种时候,我们需要将要传递的参数放到内存中,然后将内存地址的首地址放在寄存器中,传递给子程序。对于批量返回结果也使用此方法。
有如下例子,需要将一个字符串中字符全部转换为大写字符。
我们可以将字符串存储于内存中的一段连续区域,然后将该内存地址存入寄存器。通过LOOP指令循环遍历内存取出每个字符然后进行大小写转换。结果如下:
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
captical:add byte ptr [si],11011111b
loop captical
ret
code ends
end start
10.11寄存器冲突的问题
寄存器冲突是指在子程序使用的寄存器很有可能在主程序中也有使用,这会造成寄存器在使用上的冲突。
解决这个问题是在执行子程序开始时,将子程序中所有用到的寄存器的值全部保存起来,在子程序返回前,再将其恢复。可以用栈来保存寄存器的内容。
以后编写子程序的框架如下:
子程序开始:子程序使用的寄存器压栈
子程序内容
子程序使用的寄存器出栈
返回(ret,retf)
我们对10.10中的子程序capital修改以防止寄存冲突,修改后如下:
captical: push cx
push si
change: mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
imp short change
ok: pop si
pop cx
ret