用递归子程序实现如下题目
设计汇编子程序来实现如下数列计算,输入项数n,输出对应第n项的16进制数值。数列以如下被以递推的方法定义:
F(0)=0,F(1)=1, F(2)=2, F(n)=F(n - 1)+F(n - 2)(n≥ 3,n∈ N*)
方案一:用寄存器传递出入口参数
;PREPEND BEGIN
.model small
.stack
.data
.code
start:
mov ah, 08h
int 21h
sub al, '0'
xor ah,ah
call ditui1
call print_ax
;******exit******
mov ax, 4c00h
int 21h
;******exit******
;PREPEND END
;TEMPLATE BEGIN
;ax=n 寄存器ax传递参数
ditui1 proc
;ditui1
;***begin**** 注意这里开始主程序!!!
push cx
push bx
push dx
mov cx,ax ;用cx记录n的值
cmp cx,2 ;n为0、1、2则跳转
je f2
cmp cx,1
je f1
cmp cx,0
je f0
dec cx ;n-1
mov ax,cx ;将n给ax,调用子函数
call ditui1
mov bx,ax
mov ax,cx
dec ax ;n-2
call ditui1
add ax,bx ;结果
jmp done ;n非0、1、2,结束
f2:
mov ax,2
jmp done
f1:
mov ax,1
jmp done
f0:
mov ax,0
jmp done
done:
pop dx ;寄存器恢复
pop bx
pop cx
ret
;****end******
ditui1 endp
;TEMPLATE END
;APPEND BEGIN
print_ax proc
push ax ;过程中使用了AX、CX和DX
push cx
push dx
push ax ;暂存ax
mov dl,al ;转换al的高4位
mov cl,4
shr dl,cl
or dl,30h ;al高4位变成3
cmp dl,39h
jbe aldisp1
add dl,7 ;是0Ah~0Fh,还要加上7
aldisp1: mov ah,2 ;显示
int 21h
pop dx ;恢复原ax值到dx
and dl,0fh ;转换al的低4位
or dl,30h
cmp dl,39h
jbe aldisp2
add dl,7
aldisp2: mov ah,2 ;显示
int 21h
pop dx
pop cx
pop ax
ret ;过程返回
print_ax endp
end start
;//APPEND END
难点:出入口参数寄存器如何使用。
由于要求出入口参数使用同一个寄存器传递,所以每次ax传入参数n后,用cx记录入口参数,并且在调用子函数的时候,将cx的值给ax再调用子函数,由此保证入口参数正确(一直为n-1)且出口的ax为f(cx)的值。
方案二:用栈传出入口参数
;PREPEND BEGIN
.model small
.stack
.data
.code
start:
mov ah, 08h
int 21h
sub al, '0'
xor ah,ah
push ax
call ditui1
pop ax
call print_ax
;******exit******
mov ax, 4c00h
int 21h
;******exit******
;PREPEND END
;TEMPLATE BEGIN
ditui1 proc
;***begin****
push bp ;子程序在这开始!!!
mov bp,sp
push cx
push ax
push bx
mov cx,[bp+4] ;取出参数
cmp cx,2 ;n为0、1、2则跳转
je f2
cmp cx,1
je f1
cmp cx,0
je f0
dec cx ;n-1
push cx ;压入参数
call ditui1 ;调用第一次子程序
pop bx ;取出结果
dec cx ;n-1-1=n-2
push cx
call ditui1 ;调用第二次子程序
pop ax
add ax,bx ;结果用ax暂存
jmp done
f2:
mov ax,2
jmp done
f1:
mov ax,1
jmp done
f0:
mov ax,0
jmp done
done:
mov [bp+4],ax ;出口参数放入对应位置
pop bx ;寄存器恢复
pop ax
pop cx
pop bp
ret
;****end******
;TEMPLATE END
ditui1 endp
;APPEND BEGIN
print_ax proc
push ax ;过程中使用了AX、CX和DX
push cx
push dx
push ax ;暂存ax
mov dl,al ;转换al的高4位
mov cl,4
shr dl,cl
or dl,30h ;al高4位变成3
cmp dl,39h
jbe aldisp1
add dl,7 ;是0Ah~0Fh,还要加上7
aldisp1: mov ah,2 ;显示
int 21h
pop dx ;恢复原ax值到dx
and dl,0fh ;转换al的低4位
or dl,30h
cmp dl,39h
jbe aldisp2
add dl,7
aldisp2: mov ah,2 ;显示
int 21h
pop dx
pop cx
pop ax
ret ;过程返回
print_ax endp
end start
;//APPEND END
难点:
1.寄存器保护顺序,先保护bp,在用bp取sp,在保护寄存器,注意顺序,参数在【bp+4】的位置
2.堆栈传出口参数,注意ip的位置,ret会取栈中的元素,所以不能直接把出口参数压入栈,否则ret会出口参数赋值给ip。应该把出口参数放到入口参数的位置(利用bp随机访问)。
方案三:利用变量传递出入口参数
;PREPEND BEGIN
.model small
.stack
.data
N dw 0 ;
result dw 0 ;
.code
start:
mov ah, 08h
int 21h
sub al, '0'
xor ah,ah
mov N,ax
xor ax,ax
call ditui1
mov ax,result
call print_ax
;******exit******
mov ax, 4c00h
int 21h
;******exit******
;PREPEND END
;TEMPLATE BEGIN
ditui1 proc
;ditui1
;***begin**** ;子程序在这里开始!!!
push N ;一定要保护全局变量的值!!!
push ax
cmp N,2 ;n为0、1、2则跳转
je f2
cmp N,1
je f1
cmp N,0
je f0
dec N ;n-1
call ditui1
mov ax,result ;ax存第一个结果
dec N ;n-1-1=n-2
call ditui1
add result,ax ;f(n)=f(n-1)+f(n-2)
jmp done
f2:
mov result,2
jmp done
f1:
mov result,1
jmp done
f0:
mov result,0
jmp done
done:
pop ax ;寄存器恢复
pop N ;全局变量的恢复
ret
;****end******
ditui1 endp
;TEMPLATE END
;function: print_eax value in decimal;
;input:eax, use buffer (10 bytes)
print_ax proc
push ax ;过程中使用了AX、CX和DX
push cx
push dx
push ax ;暂存ax
mov dl,al ;转换al的高4位
mov cl,4
shr dl,cl
or dl,30h ;al高4位变成3
cmp dl,39h
jbe aldisp1
add dl,7 ;是0Ah~0Fh,还要加上7
aldisp1: mov ah,2 ;显示
int 21h
pop dx ;恢复原ax值到dx
and dl,0fh ;转换al的低4位
or dl,30h
cmp dl,39h
jbe aldisp2
add dl,7
aldisp2: mov ah,2 ;显示
int 21h
pop dx
pop cx
pop ax
ret ;过程返回
print_ax endp
;function end
end start
;//APPEND END
1.每一次递归都会改变入口参数的值,如果要两次递归的话(如f(n)=f(n-1)+f(n-2)),做完n-1后记得恢复全局变量的值,与方案一很类似,其实是保证每次进入的参数是正确的的。
三种方案的核心就是每次出入子程序时要保护现在的所有参数。
第二种方案注意一定要将出口参数保存在ip前面,可以放在入口参数处。
还有一个小问题,bp[4]是非法的,只能用[bp+4]。这种访问方式只对数组的偏移首地址有用。