//---------------------------------------------------------------- // zeta2.s // // This is a modification of our 'flt2asc.s' demo-program. // It uses x86 floating-point instructions to calculate an // approximation for the number PI by adding up terms from // the series of reciprocal-squares for positive integers. // // assemble using: $ as zeta2.s -o zeta2.o // and link using: $ ld zeta2.o -o zeta2 // // programmer: ALLAN CRUSE // written on: 27 MAR 2006 -- originally named 'flt2asc.s' // revised on: 28 MAR 2006 -- reorganized algorithm steps // revised on: 25 MAR 2006 -- gets PI by series-summation //---------------------------------------------------------------- .equ PLACES, 4 # count of decimal places .equ BIAS, 127 # single-precision's bias .equ N_TERMS, 10000 # number of terms to add .section .data real: .single 0.0 # single-precision number ten: .long 10 # radix of decimal-system sign: .ascii "+-" # list of sign characters msg: .ascii "\n real-number is " # output string's legend buf: .ascii " \n\n" # output-string's digits len: .int . - msg # length of output string n: .int 0 # for current term-number six: .int 6 # a multiplication factor .section .text _start: # add up the first ten thousand reciprocal-squares finit # initialize coprocessor fldz # initial total is zero mov $N_TERMS, %ecx # setup the loop-counter again: incl n # increment term-number fild n # load the term-number fmul %st # square the term-number fld1 # load the constant 1 fdivp # compute reciprocal faddp # add term to the total loop again # again for other terms fstp real # store total to memory fwait # synchronize processors # now take the square-root of six times that total fld real # load the former total fimul six # multiply total by six fsqrt # compute square-root fstp real # save result to memory fwait # synchronize processors # prepare to format the character-sequence in 'buf' lea buf, %edi # point RDI to output buffer mov real, %eax # load floating-point value # extract the mantissa and save it in register EBP mov $0x00800000, %ebp # setup 2**23 as a divisor xor %edx, %edx # extend EAX to a quadword div %ebp # remainder is the mantissa or %edx, %ebp # if leading 1 is prepended # separate the sign-bit from the biased exponent mov $0x100, %ebx # setup 2**8 as a divisor xor %edx, %edx # extend EAX to a quadword div %ebx # remainder is biased expo sub $BIAS, %edx # so adjust the remainder # output the sign-character mov sign(%eax), %al # fetch the sign-character mov %al, (%edi) # store the sign-character inc %edi # advance buffer pointer # next we need to multiply the mantissa by 10**PLACES, then # divide the (quadword) product by 2**(23-expo) and finally # round our quotient upward in case 2*remainder >= divisor. # prepare 2**(23-expo) in RBX using logical left-shifts mov $1, %ebx # setup 2**0 in EBX mov $23, %ecx # setup loop-count in ECX sub %edx, %ecx # minus unbiased exponent js exit # needs nonnegative count test $~31, %ecx # does count surpass 31? jnz exit # yes, out-of-range shift shl %cl, %ebx # else EBX = 2**(31-expo) # prepare 10**PLACES in EAX using multiplications by ten mov $1, %eax # setup 10**0 in EAX mov $PLACES, %ecx # setup loop-count in ECX mul10: mull ten # multiply EAX by ten loop mul10 # according to loop-count # do the multiplication, then the division, then rounding mul %ebp # 10**PLACES times mantissa div %ebx # divided by 2**(23-expo) add %edx, %edx # double the remainder sub %ebx, %edx # subtract the divisor cmc # flip the 'borrow' bit adc $0, %eax # add bit to quotient # convert EAX to decimal with at least 1+PLACES digits xor %ecx, %ecx # initial digit-count nxdivx: xor %edx, %edx # extend EAX to octaword divl ten # divide by the radix push %edx # push the remainder inc %ecx # count this digit or %eax, %eax # quotient is zero? jnz nxdivx # no, divide again cmp $PLACES, %ecx # enough digits yet? jbe nxdivx # no, divide again nxdgtx: pop %edx # recover remainder or $'0', %dl # convert to numeral mov %dl, (%edi) # put digit in buffer inc %edi # advance the pointer loop nxdgtx # again if more digits # shift a decimal-point leftward past PLACES digits movb $'.', (%edi) # append decimal-point mov $PLACES, %ecx # setup shift counter nxrol: dec %edi # back up the pointer rolw $8, (%edi) # swap character bytes loop nxrol # again if more places # display the signed decimal number mov $4, %eax # service-ID for 'write' mov $1, %ebx # device-ID for screen lea msg, %ecx # message's address mov len, %edx # message's length int $0x80 # invoke Linux service exit: # terminate this program mov $1, %eax # service-ID for 'exit' mov $0, %ebx # value of exit-code int $0x80 # invoke Linux service .global _start .end