Reputation: 11
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
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
loop
instruction as well as the costly xchg
with memory.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
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