Assembly 8086汇编中的简易斐波那契打印机
几天前,我开始学习汇编,我正试图制作一个程序来打印最多5个字符的斐波那契数列,但我的代码打印出了奇怪的字符 我认为这是因为ASCII转换系统,但即使我将值48添加到数字中,它仍然不正确Assembly 8086汇编中的简易斐波那契打印机,assembly,dos,x86-16,Assembly,Dos,X86 16,几天前,我开始学习汇编,我正试图制作一个程序来打印最多5个字符的斐波那契数列,但我的代码打印出了奇怪的字符 我认为这是因为ASCII转换系统,但即使我将值48添加到数字中,它仍然不正确 .model small .data lastFib DB 0 .code main PROC mov cx,5 mov dl,48 loopy: add dl,lastFib mov ah,2h int 21h mov las
.model small
.data
lastFib DB 0
.code
main PROC
mov cx,5
mov dl,48
loopy:
add dl,lastFib
mov ah,2h
int 21h
mov lastFib,dl
loop loopy
ENDP
end main
我没有你的汇编程序(MASM?),但代码相当简单,应该可以工作 在
C
和其他语言中打印数字的一种简单方法是使用递归函数。但对于装配来说,这是困难的。在这里,我首先计算数字的数量,然后逐个打印数字。由于AX最多可容纳5位数字,因此不使用循环执行此操作非常简单,实际上效果更好
.model small
.data
.code
main PROC
mov di, 0 ; Fibonacci(n-1); n = 1
mov si, 1 ; Fibonacci(n); n = 1
loopA: mov ax, si ; prepare for counting digits
xor cx, cx ; cx will be number of digits
mov bx, 10 ; print as decimal
loopDigit: xor dx, dx ; dx:ax is the dividend
div bx
inc cx
or ax, ax ; test if the quotient is 0
jnz loopDigit
; cx should be the number of digit
mov bp, si ; the number to be printed
cmp cx, 5 ; test if there are 5 digits
jb digit4
mov ax, bp ; get the number
mov bx, 10000
div bx
mov bp, dx ; the next number to be printed
mov dl, al ; the quotient, current digit
add dl, 48 ; convert to ASCII
mov ah, 2h
int 21h
digit4: cmp cx, 4 ; test if there are 4 or more digits
jb digit3
mov ax, bp ; get the number
mov bx, 1000
div bx
mov bp, dx ; the next number to be printed
mov dl, al ; the quotient, current digit
add dl, 48 ; convert to ASCII
mov ah, 2h
int 21h
digit3: cmp cx, 3 ; test if there are 4 or more digits
jb digit2
mov ax, bp ; get the number
mov bx, 100
div bx
mov bp, dx ; the next number to be printed
mov dl, al ; the quotient, current digit
add dl, 48 ; convert to ASCII
mov ah, 2h
int 21h
digit2: cmp cx, 2 ; test if there are 4 or more digits
jb digit1
mov ax, bp ; get the number
mov bx, 10
div bx
mov bp, dx ; the next number to be printed
mov dl, al ; the quotient, current digit
add dl, 48 ; convert to ASCII
mov ah, 2h
int 21h
digit1: mov dx, bp ; always need to print the last digit
add dl, 48 ; convert to ASCII
mov ah, 2h
int 21h
add di, si ; Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2)
jc done ; overflow, done!
xchg di, si ; to keep them in order; si = F(n); di = F(n-1)
jmp loopA
done:
ENDP
end main
使用循环打印它更复杂,因为我们需要在使用它之前计算除数(cx
),并在每个循环中调整它:
.model small
.data
.code
main PROC
mov di, 0 ; Fibonacci(n-1); n = 1
mov si, 1 ; Fibonacci(n); n = 1
mov cx, 1 ; 10^n_digits
loopA: mov ax, si ; prepare for counting digits
mov bx, 10 ; print as decimal
loopDigit: xchg ax, cx ; calculate 10^n_digits
mul bx
xchg cx, ax
xor dx, dx ; can be omitted, dx should be 0
div bx
or ax, ax ; test if the quotient is 0
jnz loopDigit
mov bx, si
mov bp, 10
loopPrint: mov ax, cx ; need to divide cx by 10 first
xor dx, dx
div bp
mov cx, ax
mov ax, bx ; get the number
xor dx, dx
div cx
mov bx, dx ; the next number to be printed
mov dl, al ; the quotient, current digit
add dl, 48 ; convert to ASCII
mov ah, 2h
int 21h
cmp cx, 1 ; should end?
jnz loopPrint
add di, si ; Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2)
jc done ; overflow, done!
xchg di, si ; to keep them in order; si = F(n); di = F(n-1)
jmp loopA
done:
ENDP
end main
但我的代码会打印出奇怪的字符
只需添加48即可将(小)数字输出为字符。你不能让48的加法干扰你对斐波那契数的计算。在下面的代码中,我在调用DOS之前添加了48,之后,我立即收回添加的内容
目前,您的代码根本不计算任何斐波那契数。基本计划是:
xchg dl, lastFib ; Exchange current with previous
add dl, lastFib ; Add previous to current
有6个单位数的斐波那契数:1、1、2、3、5、8。通过在计算下一个斐波那契数之前输出,下面的代码可以在一个循环中打印所有6个数。已计算第七个数字(13),但从未显示
.model small
.data
lastFib DB 0 ;previous
.code
main PROC
mov cx, 6
mov dl, 1 ;current
loopy:
add dl, 48 ;make character
mov ah, 02h
int 21h
sub dl, 48 ;take back
xchg dl, lastFib
add dl, lastFib
loop loopy
ENDP
end main
我们为什么不优化一下代码呢?
- 有了大量可用的寄存器,没有理由在内存中保留lastFib变量李>
- 我们还可以避免慢速
循环
指令
- 为当前斐波那契数选择除DL之外的寄存器将删除额外的“收回”指令
.model small
.code
main PROC
xor cx, cx ; Previous
mov bx, 1 ; Current
loopy:
lea dx, [bx+48] ; Make character in DL
mov ah, 02h ; DOS.PrintChar
int 21h
xchg bx, cx
add bx, cx
cmp bx, 10
jb loopy
ENDP
end main
这一次,只要
BX
中的数字保持为一位数,循环就会继续。我懒得写正确的答案。也许这会对您有所帮助:使用一些调试器查看寄存器中的值。没有MS-DOS或BIOS函数来输出数字,只有单个字符或字符串。因此,除非您制作或借用一个,然后输出数字字符串,否则只能使用单个数字0
到9
。幸运的是,前五个斐波那契项都是个位数。因此,从0
和1
(或者在某些定义中是1
和1
)开始,在第五个术语之后,添加48
并输出字符。无论如何,你甚至没有生成斐波那契序列,而是在每次循环迭代中将数字加倍。我建议您先编写一个简单的C代码,以正确地获得算法,然后在汇编程序中实现。如果要使用内存xchg
,避免loop
几乎毫无意义!(386和更高版本上的完全隔离,同一个循环
的CPU开始变慢。)也许您想要xadd dl、dh
或其他什么。(是的,与寄存器操作数一起工作。它也不是很快,但很紧凑)我想删除中间版本;一次做一个改变只会让答案浮肿。另外,使用另一个寄存器可能更好,这样您可以只执行更正常的mov
/add
或lea
来将f+'0'
写入DL进行打印,而不是add/sub。不过,我想在循环内部打印时,关键路径延迟并不重要,一个很好的例子,一个有效的方法做一位数斐波那契。