Assembly 8086汇编中的简易斐波那契打印机

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

几天前,我开始学习汇编,我正试图制作一个程序来打印最多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 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之外的寄存器将删除额外的“收回”指令
由于涉及DOS api调用,所有这些更改都不会加速此代码。尽管如此,在没有这样的系统调用的情况下,还是有很多很好的优化

.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。不过,我想在循环内部打印时,关键路径延迟并不重要,一个很好的例子,一个有效的方法做一位数斐波那契。