The Baron
The Baron

Reputation: 11

Simple fibonacci printer in 8086 assemlby

I've been starting to learn assembly a few days ago and I'm trying to make a program to print the fibonacci series for up to 5 characters, but my code prints out weird characters

I thought it's because of the ASCII conversion system, but even when I add the value 48 to the number it's still not correct.

.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

Upvotes: 1

Views: 4260

Answers (2)

Sep Roland
Sep Roland

Reputation: 39166

but my code prints out weird characters

Adding 48 is only needed to output the (small) number as a character. You mustn't allow this addition of 48 to tamper with your calculations of the fibonacci numbers. In below code I add 48 just before calling DOS, and immediately after, I take the addition back.

Currently your code does not calculate any fibonacci number at all. The basic scheme would be:

xchg dl, lastFib   ; Exchange current with previous
add  dl, lastFib   ; Add previous to current

There are 6 single digit fibonacci numbers: 1, 1, 2, 3, 5, 8.
By outputting before calculating the next fibonacci number, the below code manages to print all 6 numbers in one loop. A 7th number (13) is calculated but never displayed.

.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

Why don't we optimize the code a bit?

  • With plenty registers available, there's no reason to keep that lastFib variable in memory!
  • We could avoid the slow loop instruction as well as the costly xchg with memory.
  • Selecting a register other than DL for the current fibonacci number will shave off the extra 'take back' instruction.

None of these changes will speed up this code because of the DOS api call involved. Nonetheless there're all good optimizations for when there's no such system call present.

.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

This time the loop is only continued for as long as the number in BX remains single digit.

Upvotes: 2

W. Chang
W. Chang

Reputation: 502

I don't have your assembler (MASM?), but the code is fairly simple and should work.

A easy way to print a number in C and other languages is to use recursive functions. But with assembly this is difficult. Here I calculate the number of digits first, and then print the digits one by one. Since AX holds at most 5 digits, doing it without a loop is simple and actually better.

.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

It is more complicated to print it with loop, as we need to calculate the divisor (cx below) before using it and adjust it in each loop:

.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

Upvotes: 0

Related Questions