Annotation of researchv10no/libc/stdio/ostdio/atof.s, revision 1.1.1.1

1.1       root        1: #
                      2: #      atof, strtod: convert ascii to floating
                      3: #
                      4: #      C usage:
                      5: #
                      6: #              double atof (s)
                      7: #              char *s;
                      8: #
                      9: #              double strtod (s, p)
                     10: #              char *s, **p;
                     11: #
                     12: #      For strtod, if p is non-null, *p gets set to point at the
                     13: #      character after the input string.
                     14: #
                     15: #      Register usage:
                     16: #
                     17: #              r0-1:   value being developed
                     18: #              r2:     first section: pointer to the next character
                     19: #                      second section: binary exponent
                     20: #              r3:     flags
                     21: #              r4:     first section: the current character
                     22: #                      second section: scratch
                     23: #              r5:     the decimal exponent
                     24: #              r6-7:   scratch
                     25: #              r8:     a copy of p (strtod) or zero (atof)
                     26: #
                     27: #      Flag definitions
                     28: #
                     29:        .set    msign,0         # mantissa has negative sign
                     30:        .set    esign,1         # exponent has negative sign
                     31:        .set    decpt,2         # decimal point encountered
                     32: 
                     33:        .align  2
                     34: two31: .word   0x5000          # 2 ** 31
                     35:        .word   0               # (=2147483648)
                     36:        .word   0               # in floating-point
                     37:        .word   0               # (so atof doesn't have to convert it)
                     38: #
                     39: #      Entry points
                     40: #
                     41:        .text
                     42:        .align  2
                     43:        .globl  _strtod
                     44: _strtod:.word  0x01c0          # Save r8 through r6 (we use r0-r8)
                     45:        movl    8(ap),r8        # Pick up value of p
                     46:        jbr     init
                     47: 
                     48:        .align  2
                     49:        .globl  _atof
                     50: _atof: .word   0x01c0          # Save r8 through r6 (we use r0-r8)
                     51:        clrl    r8              # This is atof, not strtod
                     52: #
                     53: #      Initialization
                     54: #
                     55: init:
                     56:        clrl    r3              # All flags start out false
                     57:        movl    4(ap),r2        # Address the first character
                     58:        clrl    r5              # Clear starting exponent
                     59: #
                     60: #      Skip leading white space
                     61: #
                     62: sk0:   movzbl  (r2)+,r4        # Fetch the next (first) character
                     63:        cmpb    $' ,r4          # Is it blank?
                     64:        jeql    sk0             #   ...yes
                     65:        cmpb    r4,$8           # 8 is lowest of white-space group
                     66:        jlss    sk1             # Jump if char too low to be white space
                     67:        cmpb    r4,$13          # 13 is highest of white-space group
                     68:        jleq    sk0             # Jump if character is white space
                     69: sk1:
                     70: #
                     71: #      Check for a sign
                     72: #
                     73:        cmpb    $'+,r4          # Positive sign?
                     74:        jeql    cs1             #   ... yes
                     75:        cmpb    $'-,r4          # Negative sign?
                     76:        jneq    cs2             #   ... no
                     77:        bisb2   $1<msign,r3     # Indicate a negative mantissa
                     78: cs1:   movzbl  (r2)+,r4        # Skip the character
                     79: cs2:
                     80: #
                     81: #      Accumulate digits, keeping track of the exponent
                     82: #
                     83:        clrq    r0              # Clear the accumulator
                     84: ad0:   cmpb    r4,$'0          # Do we have a digit?
                     85:        jlss    ad4             #   ... no, too small
                     86:        cmpb    r4,$'9
                     87:        jgtr    ad4             #   ... no, too large
                     88: #
                     89: #      We got a digit.  Accumulate it
                     90: #
                     91:        cmpl    r1,$214748364   # Would this digit cause overflow?
                     92:        jgeq    ad1             #   ... yes
                     93: #
                     94: #      Multiply (r0,r1) by 10.  This is done by developing
                     95: #      (r0,r1)*2 in (r6,r7), shifting (r0,r1) left three bits,
                     96: #      and adding the two quadwords.
                     97: #
                     98:        ashq    $1,r0,r6        # (r6,r7)=(r0,r1)*2
                     99:        ashq    $3,r0,r0        # (r0,r1)=(r0,r1)*8
                    100:        addl2   r6,r0           # Add low halves
                    101:        adwc    r7,r1           # Add high halves
                    102: #
                    103: #      Add in the digit
                    104: #
                    105:        subl2   $'0,r4          # Get the digit value
                    106:        addl2   r4,r0           # Add it into the accumulator
                    107:        adwc    $0,r1           # Possible carry into high half
                    108:        jbr     ad2             # Join common code
                    109: #
                    110: #      Here when the digit won't fit in the accumulator
                    111: #
                    112: ad1:   incl    r5              # Ignore the digit, bump exponent
                    113: #
                    114: #      If we have seen a decimal point, decrease the exponent by 1
                    115: #
                    116: ad2:   jbc     $decpt,r3,ad3   # Jump if decimal point not seen
                    117:        decl    r5              # Decrease exponent
                    118: ad3:
                    119: #
                    120: #      Fetch the next character, back for more
                    121: #
                    122:        movzbl  (r2)+,r4        # Fetch
                    123:        jbr     ad0             # Try again
                    124: #
                    125: #      Not a digit.  Could it be a decimal point?
                    126: #
                    127: ad4:   cmpb    r4,$'.          # If it's not a decimal point, either it's
                    128:        jneq    ad5             #   the end of the number or the start of
                    129:                                #   the exponent.
                    130:        jbcs    $decpt,r3,ad3   # If it IS a decimal point, we record that
                    131:                                #   we've seen one, and keep collecting
                    132:                                #   digits if it is the first one.
                    133: #
                    134: #      Check for an exponent
                    135: #
                    136: ad5:   clrl    r6              # Initialize the exponent accumulator
                    137: 
                    138:        cmpb    r4,$'e          # We allow both lower case e
                    139:        jeql    ex1             #   ... and ...
                    140:        cmpb    r4,$'E          #   upper-case E
                    141:        jneq    ex7
                    142: #
                    143: #      Does the exponent have a sign?
                    144: #
                    145: ex1:   movzbl  (r2)+,r4        # Get next character
                    146:        cmpb    r4,$'+          # Positive sign?
                    147:        jeql    ex2             #   ... yes ...
                    148:        cmpb    r4,$'-          # Negative sign?
                    149:        jneq    ex3             #   ... no ...
                    150:        bisb2   $1<esign,r3     # Indicate exponent is negative
                    151: ex2:   movzbl  (r2)+,r4        # Grab the next character
                    152: #
                    153: #      Accumulate exponent digits in r6
                    154: #
                    155: ex3:   cmpb    r4,$'0          # A digit is within the range
                    156:        jlss    ex4             # '0' through
                    157:        cmpb    r4,$'9          # '9',
                    158:        jgtr    ex4             # inclusive.
                    159:        cmpl    r6,$214748364   # Exponent outrageously large already?
                    160:        jgeq    ex2             #   ... yes
                    161:        moval   (r6)[r6],r6     # r6 *= 5
                    162:        movaw   -'0(r4)[r6],r6  # r6 = r6 * 2 + r4 - '0'
                    163:        jbr     ex2             # Go 'round again
                    164: ex4:
                    165: #
                    166: #      Now get the final exponent and force it within a reasonable
                    167: #      range so our scaling loops don't take forever for values
                    168: #      that will ultimately cause overflow or underflow anyway.
                    169: #      A tight check on over/underflow will be done by ldexp.
                    170: #
                    171:        jbc     $esign,r3,ex5   # Jump if exponent not negative
                    172:        mnegl   r6,r6           # If sign, negate exponent
                    173: ex5:   addl2   r6,r5           # Add given exponent to calculated exponent
                    174:        cmpl    r5,$-100        # Absurdly small?
                    175:        jgtr    ex6             #   ... no
                    176:        movl    $-100,r5        #   ... yes, force within limit
                    177: ex6:   cmpl    r5,$100         # Absurdly large?
                    178:        jlss    ex7             #   ... no
                    179:        movl    $100,r5         #   ... yes, force within bounds
                    180: ex7:
                    181: #
                    182: #      If this is strtod and p is non-null, store a value in *p
                    183: #
                    184:        tstl    r8              # Is there a place to store it?
                    185:        jeql    ex8             #   ... no, skip store
                    186:        decl    r2
                    187:        movl    r2,(r8)         #   ... yes, store value in *p
                    188: ex8:
                    189: #
                    190: #      Our number has now been reduced to a mantissa and an exponent.
                    191: #      The mantissa is a 63-bit positive binary integer in r0,r1,
                    192: #      and the exponent is a signed power of 10 in r5.  The msign
                    193: #      bit in r3 will be on if the mantissa should ultimately be
                    194: #      considered negative.
                    195: #
                    196: #      We now have to convert it to a standard format floating point
                    197: #      number.  This will be done by accumulating a binary exponent
                    198: #      in r2, as we progressively get r5 closer to zero.
                    199: #
                    200: #      Don't bother scaling if the mantissa is zero
                    201: #
                    202:        movq    r0,r0           # Mantissa zero?
                    203:        jeql    exit            #   ... yes
                    204: 
                    205:        clrl    r2              # Initialize binary exponent
                    206:        tstl    r5              # Which way to scale?
                    207:        jleq    sd0             # Scale down if decimal exponent <= 0
                    208: #
                    209: #      Scale up by "multiplying" r0,r1 by 10 as many times as necessary,
                    210: #      as follows:
                    211: #
                    212: #      Step 1: Shift r0,r1 right as necessary to ensure that no
                    213: #      overflow can occur when multiplying.
                    214: #
                    215: su0:   cmpl    r1,$429496729   # Compare high word to (2**31)/5
                    216:        jlss    su1             # Jump out if guaranteed safe
                    217:        ashq    $-1,r0,r0       # Else shift right one bit
                    218:        incl    r2              #    bump exponent to compensate
                    219:        jbr     su0             #    and go back to test again.
                    220: #
                    221: #      Step 2: Multiply r0,r1 by 5, by appropriate shifting and
                    222: #      double-precision addition
                    223: #
                    224: su1:   ashq    $2,r0,r6        # (r6,r7) := (r0,r1) * 4
                    225:        addl2   r6,r0           # Add low-order halves
                    226:        adwc    r7,r1           #   and high-order halves
                    227: #
                    228: #      Step 3: Increment the binary exponent to take care of the final
                    229: #      factor of 2, and go back if we still need to scale more.
                    230: #
                    231:        incl    r2              # Increment the exponent
                    232:        sobgtr  r5,su0          #    and back for more (maybe)
                    233: 
                    234:        jbr     cm0             # Merge to build final value
                    235: 
                    236: #
                    237: #      Scale down.  We must "divide" r0,r1 by 10 as many times
                    238: #      as needed, as follows:
                    239: #
                    240: #      Step 0: Right now, the condition codes reflect the state
                    241: #      of r5.  If it's zero, we are done.
                    242: #
                    243: sd0:   jeql    cm0             # If finished, build final number
                    244: #
                    245: #      Step 1: Shift r0,r1 left until the high-order bit (not counting
                    246: #      the sign bit) is nonzero, so that the division will preserve
                    247: #      as much precision as possible.
                    248: #
                    249:        tstl    r1              # Is the entire high-order half zero?
                    250:        jneq    sd2             #   ...no, go shift one bit at a time
                    251:        ashq    $30,r0,r0       #   ...yes, shift left 30,
                    252:        subl2   $30,r2          #   decrement the exponent to compensate,
                    253:                                #   and now it's known to be safe to shift
                    254:                                #   at least once more.
                    255: sd1:   ashq    $1,r0,r0        # Shift (r0,r1) left one, and
                    256:        decl    r2              #   decrement the exponent to compensate
                    257: sd2:   jbc     $30,r1,sd1      # If the high-order bit is off, go shift
                    258: #
                    259: #      Step 2: Divide the high-order part of (r0,r1) by 5,
                    260: #      giving a quotient in r1 and a remainder in r7.
                    261: #
                    262: sd3:   movl    r1,r6           # Copy the high-order part
                    263:        clrl    r7              # Zero-extend to 64 bits
                    264:        ediv    $5,r6,r1,r7     # Divide (cannot overflow)
                    265: #
                    266: #      Step 3: Divide the low-order part of (r0,r1) by 5,
                    267: #      using the remainder from step 2 for rounding.
                    268: #      Note that the result of this computation is unsigned,
                    269: #      so we have to allow for the fact that an ordinary division
                    270: #      by 5 could overflow.  We make allowance by dividing by 10,
                    271: #      multiplying the quotient by 2, and using the remainder
                    272: #      to adjust the modified quotient.
                    273: #
                    274:        addl3   $2,r0,r6        # Dividend is low part of (r0,r1) plus
                    275:        adwc    $0,r7           #  2 for rounding plus
                    276:                                #  (2**32) * previous remainder
                    277:        ediv    $10,r6,r0,r6    # r0 := quotient, r6 := remainder.
                    278:        addl2   r0,r0           # Make r0 result of dividing by 5
                    279:        cmpl    r6,$5           # If remainder is 5 or greater,
                    280:        jlss    sd4             #   increment the adjustted quotient.
                    281:        incl    r0
                    282: #
                    283: #      Step 4: Increment the decimal exponent, decrement the binary
                    284: #      exponent (to make the division by 5 into a division by 10),
                    285: #      and back for another iteration.
                    286: #
                    287: sd4:   decl    r2              # Binary exponent
                    288:        aoblss  $0,r5,sd2
                    289: #
                    290: #      We now have the following:
                    291: #
                    292: #      r0:     low-order half of a 64-bit integer
                    293: #      r1:     high-order half of the same 64-bit integer
                    294: #      r2:     a binary exponent
                    295: #
                    296: #      Our final result is the integer represented by (r0,r1)
                    297: #      multiplied by 2 to the power contained in r2.
                    298: #      We will transform (r0,r1) into a floating-point value,
                    299: #      set the sign appropriately, and let ldexp do the
                    300: #      rest of the work.
                    301: #
                    302: #      Step 1: if the high-order bit (excluding the sign) of
                    303: #      the high-order half (r1) is 1, then we have 63 bits of
                    304: #      fraction, too many to convert easily.  However, we also
                    305: #      know we won't need them all, so we will just throw the
                    306: #      low-order bit away (and adjust the exponent appropriately).
                    307: #
                    308: cm0:   jbc     $30,r1,cm1      # jump if no adjustment needed
                    309:        ashq    $-1,r0,r0       # lose the low-order bit
                    310:        incl    r2              # increase the exponent to compensate
                    311: #
                    312: #      Step 2: split the 62-bit number in (r0,r1) into two
                    313: #      31-bit positive quantities
                    314: #
                    315: cm1:   ashq    $1,r0,r0        # put the high-order bits in r1
                    316:                                #   and a 0 in the bottom of r0
                    317:        rotl    $-1,r0,r0       # right-justify the bits in r0
                    318:                                #   moving the 0 from the ashq
                    319:                                #   into the sign bit.
                    320: #
                    321: #      Step 3: convert both halves to floating point
                    322: #
                    323:        cvtld   r0,r6           # low-order part in r6-r7
                    324:        cvtld   r1,r0           # high-order part in r0-r1
                    325: #
                    326: #      Step 4: multiply the high order part by 2**31 and combine them
                    327: #
                    328:        muld2   two31,r0        # multiply
                    329:        addd2   r6,r0           # combine
                    330: #
                    331: #      Step 5: if appropriate, negate the floating value
                    332: #
                    333:        jbc     $msign,r3,cm2   # Jump if mantissa not signed
                    334:        mnegd   r0,r0           # If negative, make it so
                    335: #
                    336: #      Step 6: call ldexp to complete the job
                    337: #
                    338: cm2:   pushl   r2              # Put exponent in parameter list
                    339:        movd    r0,-(sp)        #    and also mantissa
                    340:        calls   $3,_ldexp       # go combine them
                    341: 
                    342: exit:  ret

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.