//---------------------------------------------------------------- // flt2asc.s // // This program displays a single-precision floating-point // value as a signed decimal fraction rounded to 4 places. // // assemble using: $ as flt2asc.s -o flt2asc.o // and link using: $ ld flt2asc.o -o flt2asc // // programmer: ALLAN CRUSE // written on: 27 MAR 2006 // revised on: 28 MAR 2006 -- reorganized algorithm steps //---------------------------------------------------------------- .equ PLACES, 4 # count of decimal places .equ BIAS, 127 # single-precision's bias .section .data real: .single -3.14159 # 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 .section .text _start: # 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