//---------------------------------------------------------------- // dbl2asc.s // // This program displays a double-precision floating-point // value as a signed decimal fraction rounded to 8 places. // It executes on a Linux EM64T platform (e.g., on 'din'). // // assemble using: $ as dbl2asc.s -o dbl2asc.o // and link using: $ ld dbl2asc.o -o dbl2asc // // programmer: ALLAN CRUSE // written on: 28 MAR 2006 //---------------------------------------------------------------- .equ PLACES, 8 # count of decimal places .equ BIAS, 1023 # double-precision's bias .section .data real: .double -2.718281828 # double-precision number ten: .quad 10 # radix of decimal-system sign: .ascii "+-" # list of sign characters msg: .ascii "\n\treal-number is " # output string's legend buf: .ascii " \n\n" # output-string's digits len: .int . - msg # length of output string .section .text _start: # prepare to format the character-sequence in 'buf' lea buf, %rdi # point RDI to output buffer mov real, %rax # load floating-point value # extract the mantissa and save it in register RBP mov $0x10000000000000, %rbp # setup 2**52 as a divisor xor %rdx, %rdx # extend RAX to an octaword div %rbp # remainder is the mantissa or %rdx, %rbp # if leading 1 is prepended # separate the sign-bit from the biased exponent mov $0x800, %rbx # setup 2**11 as a divisor xor %rdx, %rdx # extend RAX to an octaword div %rbx # remainder is biased expo sub $BIAS, %rdx # so adjust the remainder # output the sign-character mov sign(%rax), %al # fetch the sign-character mov %al, (%rdi) # store the sign-character inc %rdi # advance buffer pointer # next we need to multiply the mantissa by 10**PLACES, then # divide the (octaword) product by 2**(52-expo) and finally # round our quotient upward in case 2*remainder >= divisor. # prepare 2**(52-expo) in RBX using logical left-shifts mov $1, %rbx # setup 2**0 in RBX mov $52, %rcx # setup loop-count in RCX sub %rdx, %rcx # minus unbiased exponent js exit # needs nonnegative count test $~63, %rcx # does count surpass 63? jnz exit # yes, out-of-range shift shl %cl, %rbx # else RBX = 2**(52-expo) # prepare 10**PLACES in RAX using multiplications by ten mov $1, %rax # setup 10**0 in RAX mov $PLACES, %rcx # setup loop-count in RCX mul10: mulq ten # multiply RAX by ten loop mul10 # according to loop-count # do the multiplication, then the division, then rounding mul %rbp # 10**PLACES times mantissa div %rbx # divided by 2**(52-expo) add %rdx, %rdx # double the remainder sub %rbx, %rdx # subtract the divisor cmc # flip the 'borrow' bit adc $0, %rax # add bit to quotient # convert RAX to decimal with at least 1+PLACES digits xor %rcx, %rcx # initial digit-count nxdivx: xor %rdx, %rdx # extend RAX to octaword divq ten # divide by the radix push %rdx # push the remainder inc %rcx # count this digit or %rax, %rax # quotient is zero? jnz nxdivx # no, divide again cmp $PLACES, %rcx # enough digits yet? jbe nxdivx # no, divide again nxdgtx: pop %rdx # recover remainder or $'0', %dl # convert to numeral mov %dl, (%rdi) # put digit in buffer inc %rdi # advance the pointer loop nxdgtx # again if more digits # shift a decimal-point leftward past PLACES digits movb $'.', (%rdi) # append decimal-point mov $PLACES, %rcx # setup shift counter nxrol: dec %rdi # back up the pointer rolw $8, (%rdi) # swap character bytes loop nxrol # again if more places # display the signed decimal number mov $4, %rax # service-ID for 'write' mov $1, %rbx # device-ID for screen lea msg, %rcx # message's address mov len, %rdx # message's length int $0x80 # invoke Linux service exit: # terminate this program mov $1, %rax # service-ID for 'exit' mov $0, %rbx # value of exit-code int $0x80 # invoke Linux service .global _start .end