261 lines
8.4 KiB
NASM
261 lines
8.4 KiB
NASM
; fibo.asm
|
|
; assemble using nasm:
|
|
; nasm -o fibo.com -f bin fibo.asm
|
|
;
|
|
;****************************************************************************
|
|
; Alterable Constant
|
|
;****************************************************************************
|
|
; You can adjust this upward but the upper limit is around 150000 terms.
|
|
; the limitation is due to the fact that we can only address 64K of memory
|
|
; in a DOS com file, and the program is about 211 bytes long and the
|
|
; address space starts at 100h. So that leaves roughly 65000 bytes to
|
|
; be shared by the two terms (num1 and num2 at the end of this file). Since
|
|
; they're of equal size, that's about 32500 bytes each, and the 150000th
|
|
; term of the Fibonacci sequence is 31349 digits long.
|
|
;
|
|
maxTerms equ 15000 ; number of terms of the series to calculate
|
|
|
|
;****************************************************************************
|
|
; Number digits to use. This is based on a little bit of tricky math.
|
|
; One way to calculate F(n) (i.e. the nth term of the Fibonacci seeries)
|
|
; is to use the equation int(phi^n/sqrt(5)) where ^ means exponentiation
|
|
; and phi = (1 + sqrt(5))/2, the "golden number" which is a constant about
|
|
; equal to 1.618. To get the number of decimal digits, we just take the
|
|
; base ten log of this number. We can very easily see how to get the
|
|
; base phi log of F(n) -- it's just n*lp(phi)+lp(sqrt(5)), where lp means
|
|
; a base phi log. To get the base ten log of this we just divide by the
|
|
; base ten log of phi. If we work through all that math, we get:
|
|
;
|
|
; digits = terms * log(phi) + log(sqrt(5))/log(phi)
|
|
;
|
|
; the constants below are slightly high to assure that we always have
|
|
; enough room. As mentioned above the 150000th term has 31349 digits,
|
|
; but this formula gives 31351. Not too much waste there, but I'd be
|
|
; a little concerned about the stack!
|
|
;
|
|
digits equ (maxTerms*209+1673)/1000
|
|
|
|
; this is just the number of digits for the term counter
|
|
cntDigits equ 6 ; number of digits for counter
|
|
|
|
org 100h ; this is a DOS com file
|
|
;****************************************************************************
|
|
;****************************************************************************
|
|
main:
|
|
; initializes the two numbers and the counter. Note that this assumes
|
|
; that the counter and num1 and num2 areas are contiguous!
|
|
;
|
|
mov ax,'00' ; initialize to all ASCII zeroes
|
|
mov di,counter ; including the counter
|
|
mov cx,digits+cntDigits/2 ; two bytes at a time
|
|
cld ; initialize from low to high memory
|
|
rep stosw ; write the data
|
|
inc ax ; make sure ASCII zero is in al
|
|
mov [num1 + digits - 1],al ; last digit is one
|
|
mov [num2 + digits - 1],al ;
|
|
mov [counter + cntDigits - 1],al
|
|
|
|
jmp .bottom ; done with initialization, so begin
|
|
|
|
.top
|
|
; add num1 to num2
|
|
mov di,num1+digits-1
|
|
mov si,num2+digits-1
|
|
mov cx,digits ;
|
|
call AddNumbers ; num2 += num1
|
|
mov bp,num2 ;
|
|
call PrintLine ;
|
|
dec dword [term] ; decrement loop counter
|
|
jz .done ;
|
|
|
|
; add num2 to num1
|
|
mov di,num2+digits-1
|
|
mov si,num1+digits-1
|
|
mov cx,digits ;
|
|
call AddNumbers ; num1 += num2
|
|
.bottom
|
|
mov bp,num1 ;
|
|
call PrintLine ;
|
|
dec dword [term] ; decrement loop counter
|
|
jnz .top ;
|
|
.done
|
|
call CRLF ; finish off with CRLF
|
|
mov ax,4c00h ; terminate
|
|
int 21h ;
|
|
|
|
|
|
;****************************************************************************
|
|
;
|
|
; PrintLine
|
|
; prints a single line of output containing one term of the
|
|
; Fibonacci sequence. The first few lines look like this:
|
|
;
|
|
; Fibonacci(1): 1
|
|
; Fibonacci(2): 1
|
|
; Fibonacci(3): 2
|
|
; Fibonacci(4): 3
|
|
;
|
|
; INPUT: ds:bp ==> number string, cx = max string length
|
|
; OUTPUT: CF set on error, AX = error code if carry set
|
|
; DESTROYED: ax, bx, cx, dx, di
|
|
;
|
|
;****************************************************************************
|
|
PrintLine:
|
|
mov dx,eol ; print combined CRLF and msg1
|
|
mov cx,msg1len+eollen ;
|
|
call PrintString ;
|
|
|
|
mov di,counter ; print counter
|
|
mov cx,cntDigits ;
|
|
call PrintNumericString
|
|
|
|
call IncrementCount ; also increment the counter
|
|
|
|
mov dx,msg2 ; print msg2
|
|
mov cx,msg2len ;
|
|
call PrintString ;
|
|
|
|
mov di,bp ; recall address of number
|
|
mov cx,digits ;
|
|
; deliberately fall through to PrintNumericString
|
|
|
|
;****************************************************************************
|
|
;
|
|
; PrintNumericString
|
|
; prints the numeric string at DS:DI, suppressing leading zeroes
|
|
; max length is CX
|
|
;
|
|
; INPUT: ds:di ==> number string, cx = max string length
|
|
; OUTPUT: CF set on error, AX = error code if carry set
|
|
; DESTROYED: ax, bx, cx, dx, di
|
|
;
|
|
;****************************************************************************
|
|
PrintNumericString:
|
|
; first scan for the first non-zero byte
|
|
mov al,'0' ; look for ASCII zero
|
|
cld ; scan from MSD to LSD
|
|
repe scasb ;
|
|
mov dx,di ; points to one byte after
|
|
dec dx ; back up one character
|
|
inc cx ;
|
|
; deliberately fall through to PrintString
|
|
|
|
;****************************************************************************
|
|
;
|
|
; PrintString
|
|
; prints the string at DS:DX with length CX to stdout
|
|
;
|
|
; INPUT: ds:dx ==> string, cx = string length
|
|
; OUTPUT: CF set on error, AX = error code if carry set
|
|
; DESTROYED: ax, bx
|
|
;
|
|
;****************************************************************************
|
|
PrintString:
|
|
mov bx, 1 ; write to stdout
|
|
mov ah, 040h ; write to file handle
|
|
int 21h ; ignore return value
|
|
ret ;
|
|
|
|
;****************************************************************************
|
|
;
|
|
; AddNumbers
|
|
; add number 2 at ds:si to number 1 at es:di of width cx
|
|
;
|
|
;
|
|
; INPUT: es:di ==> number1, ds:si ==> number2, cx= max width
|
|
; OUTPUT: CF set on overflow
|
|
; DESTROYED: ax, si, di
|
|
;
|
|
;****************************************************************************
|
|
AddNumbers:
|
|
std ; go from LSB to MSB
|
|
clc ;
|
|
pushf ; save carry flag
|
|
.top
|
|
mov ax,0f0fh ; convert from ASCII BCD to BCD
|
|
and al,[si] ; get next digit of number2 in al
|
|
and ah,[di] ; get next digit of number1 in ah
|
|
popf ; recall carry flag
|
|
adc al,ah ; add these digits
|
|
aaa ; convert to BCD
|
|
pushf ;
|
|
add al,'0' ; convert back to ASCII BCD digit
|
|
stosb ; save it and increment both counters
|
|
dec si ;
|
|
loop .top ; keep going until we've got them all
|
|
popf ; recall carry flag
|
|
ret ;
|
|
|
|
;****************************************************************************
|
|
;
|
|
; IncrementCount
|
|
; increments a multidigit term counter by one
|
|
;
|
|
; INPUT: none
|
|
; OUTPUT: CF set on overflow
|
|
; DESTROYED: ax, cx, di
|
|
;
|
|
;****************************************************************************
|
|
IncrementCount:
|
|
mov cx,cntDigits ;
|
|
mov di,counter+cntDigits-1
|
|
std ; go from LSB to MSB
|
|
stc ; this is our increment
|
|
pushf ; save carry flag
|
|
.top
|
|
mov ax,000fh ; convert from ASCII BCD to BCD
|
|
and al,[di] ; get next digit of counter in al
|
|
popf ; recall carry flag
|
|
adc al,ah ; add these digits
|
|
aaa ; convert to BCD
|
|
pushf ;
|
|
add al,'0' ; convert back to ASCII BCD digit
|
|
stosb ; save and increment counter
|
|
loop .top ;
|
|
popf ; recall carry flag
|
|
ret ;
|
|
|
|
;****************************************************************************
|
|
;
|
|
; CRLF
|
|
; prints carriage return, line feed pair to stdout
|
|
;
|
|
; INPUT: none
|
|
; OUTPUT: CF set on error, AX = error code if carry set
|
|
; DESTROYED: ax, bx, cx, dx
|
|
;
|
|
;****************************************************************************
|
|
CRLF: mov dx,eol ;
|
|
mov cx,eollen ;
|
|
jmp PrintString ;
|
|
|
|
;****************************************************************************
|
|
; static data
|
|
;****************************************************************************
|
|
eol db 13,10 ; DOS-style end of line
|
|
eollen equ $ - eol
|
|
|
|
msg1 db 'Fibonacci(' ;
|
|
msg1len equ $ - msg1
|
|
|
|
msg2 db '): ' ;
|
|
msg2len equ $ - msg2
|
|
;****************************************************************************
|
|
; initialized data
|
|
;****************************************************************************
|
|
term dd maxTerms ;
|
|
;****************************************************************************
|
|
; unallocated data
|
|
;
|
|
; A better way to do this would be to actually ask for a memory
|
|
; allocation and use that memory space, but this is a DOS COM file
|
|
; and so we are given the entire 64K of space. Technically, this
|
|
; could fail since we *might* be running on a machine which doesn't
|
|
; have 64K free. If you're running on such a memory poor machine,
|
|
; my advice would be to not run this program.
|
|
;
|
|
;****************************************************************************
|
|
; static data
|
|
counter: ;
|
|
num1 equ counter+cntDigits ;
|
|
num2 equ num1+digits ; |