8086组装的简单斐波那契打印机

Simple fibonacci printer in 8086 assemlby

几天前我开始学习汇编,我正在尝试编写一个程序来打印最多 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

but my code prints out weird characters

只需将(小)数输出为字符,只需添加 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 个数字。计算出第 7 个数字 (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 变量!
  • 我们可以避免缓慢的 loop 指令以及
  • 为当前斐波那契数选择 DL 以外的寄存器将减少额外的 'take back' 指令。
由于涉及 DOS api 调用,

None 这些更改将加快 这段代码 的速度。 None尽管如此,当不存在此类系统调用时,还是有很多好的优化。

.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 中的数字保持个位数。