1. 利用调用和返回的对应可以实现类似函数的功能:
a. 其实高级语言中函数的实现即时基于汇编的call和ret的;
b. 这样就可以设计一个个用于解决子问题的子程序,通过调用和返回将各个子程序组合成一个解决大问题的主程序;
2. 利用寄存器传参和返回:
a. 在传送的数据量不是很少的情况下可以使用,但是如果传送的批量的数据时寄存器就明显显得不够用了,此时就只能借助内存来实现参数的传递;
b. 程序示例:将第一组数据的三次方保存在第二组数据中
assume cs:code, ds:data
data segment
dw 1, 2, 3, 4, 5, 6, 7, 8
dd 8 dup(0)
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, 0
mov di, 10h
mov cx, 8
s:
mov bx, [si]
call cube
mov [di], ax
mov [di + 2], dx
add si, 2
add di, 4
loop s
mov ax, 4c00h
int 21h
cube:
mov ax, bx
mul bx
mul bx
ret
code ends
end start
3. 利用内存传递参数和返回值:
a. 当传递的参数是批量的时寄存器就不够用了;
b. 解决方法:
i. 基于寄存器传递;
ii. 将参数存在内存中;
iii. 将内存中参数的首地址作为参数放入寄存器进行寄存器传递;
c. 程序示例:设计一个子程序将一个字符串转化为大写
assume cs:code, ds:data
data segment
db 'conversation'
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0
mov al, 0dfh
mov cx, 12
call cpt
mov ax, 4c00h
int 21h
cpt:
and [bx], al
inc bx
loop cpt
ret
code ends
end start
d. 将以'\0'作为结束符的字符串转化为大写,在汇编中'\0'就是0,这是处理标准字符串的通用方法,使用jcxz指令进行跳转:
assume cs:code, ds:data
data segment
db 'conversation', 0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0
mov ch, 0
mov al, 0dfh
call cpt
mov ax, 4c00h
int 21h
cpt:
mov cl, [bx]
jcxz ok
and [bx], al
inc bx
jmp short cpt
ok: ret
code ends
end start
4. 子程序和主程序之间的寄存器冲突问题:
a. 是指在子程序中使用了调用它的主程序中正在使用的寄存器,在调用子程序之后子程序已经将主程序中的寄存器的值修改了,导致最终结果错误;
b. 为了避免这样的情况发生,就的现在子程序的开头处将发生冲突的寄存器中的值先备份到栈中,当子程序的末尾处恢复栈中的数据;
c. 但是当程序非常庞大繁杂的时候连程序员可能自己也搞不清子程序到底和主程序之间到底有哪些寄存器是冲突的,因此在这种情况下还不如将子程序中所有用到的寄存器中的值先备份到栈中;
d. 正式声明写子程序的标准结构:
i. 子程序开头部分将所有子程序中用到的寄存器按照一定顺序push进栈中;
ii. 中间为子程序的内容;
iii. 最后为子程序的返回部分,先按照push相反的顺序将栈中的备份的数据pop还原出来;
iv. 还原完最后执行ret返回指令;
e. 程序示例:将多行字符串(以0为结尾)改为大写
assume cs:code, ds:data, ss:stack
data segment
db 'word', 0
db 'unix', 0
db 'wind', 0
db 'good', 0
data ends
stack segment
dw 8 dup (0)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0
mov ax, stack
mov ss, ax
mov sp, 10h
mov cx, 4
s:
mov si, bx ;虽然si的值在每次循环后都会被重新赋值和子程序不发生冲突
call cpt ;但是还是得按照标准在子程序中将其备份,以保证安全
add bx, 5
loop s
mov ax, 4c00h
int 21h
cpt:
push cx ;将子程序中所有用到的寄存器中的值都先进行备份
push si
mov ch, 0 ;备份完后就可以无顾虑地修改这些寄存器中的值了
cpt_s:
mov cl, [si]
jcxz ok
and [si], byte ptr 0dfh
inc si
jmp short cpt_s
ok:
pop si ;在返回部分中必须先将备份的数据还原回来供主程序继续使用
pop cx
ret
code ends
end start