OpenCores
URL https://opencores.org/ocsvn/ft816float/ft816float/trunk

Subversion Repositories ft816float

[/] [ft816float/] [trunk/] [software/] [_sprtflt.asm] - Rev 88

Go to most recent revision | Compare with Previous | Blame | View Log

; ============================================================================
;        __
;   \\__/ o\    (C) 2022  Robert Finch, Waterloo
;    \  __ /    All rights reserved.
;     \/_//     robfinch<remove>@opencores.org
;       ||
;  
;
; BSD 3-Clause License
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice, this
;    list of conditions and the following disclaimer.
;
; 2. Redistributions in binary form must reproduce the above copyright notice,
;    this list of conditions and the following disclaimer in the documentation
;    and/or other materials provided with the distribution.
;
; 3. Neither the name of the copyright holder nor the names of its
;    contributors may be used to endorse or promote products derived from
;    this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;                                                                          
; ============================================================================
;
;==============================================================================
; Decimal-Floating point to string conversion routine.
;==============================================================================

; These variables may need to be relocated depending on the system.

fpBuf equ $402C0
_exp equ $40500
_digit equ $40504
_width equ $40508
_E equ $4050C
_digits_before_decpt equ $40510
_precision equ $40514
_fpBuf equ $40520       ; to $40560
_fpWork equ $40600

        code
;==============================================================================
; Decimal-Floating point to string conversion routine.
;
; Modifies
;               _fpWork work area
; Register Usage:
;       fp0 = input decimal-float to convert
;               fp1 = constant holder, 1.0, 10.0
;               fp2 = 1.0e<fp0 exponent> value for conversion
;               fp3 = holds digit value during significand conversion
;       a0 = pointer to string buffer, updated to point to NULL at end of string
;               a1 = pointer to "Nan" or "Inf" message string
;               d0 = temporary
;               d1 = digit value during exponent, significand conversion
;       d6 = exponent
;==============================================================================
        align 4
_dfOne  dc.l $25ff0000,$00000000,$00000000
_dfTen  dc.l $2600C000,$00000000,$00000000
_dfMil  dc.l $2606DDFA,$1C000000,$00000000

_msgNan dc.b "NaN",0
_msgInf dc.b "Inf",0
        even

;------------------------------------------------------------------------------
; Check for the special Nan and infinity values. Output the appropriate string.
;
; Modifies
;               _fpWork area
;               d0,a1,a0
; Parameters:
;               fp0 = dbl
;------------------------------------------------------------------------------

_CheckNan:
        fmove.p fp0,_fpWork
        move.b _fpWork,d0                               ; get sign+combo
        andi.b #$7C,d0                                  ; mask for combo bits
        cmpi.b #$7C,d0                                  ; is it the Nan combo?
        bne .notNan
        lea _msgNan,a1                                  ; output "Nan"
        bra .outStr
.notNan
        cmpi.b #$78,d0                                  ; is it infinity combo?
        bne .notInf
        lea _msgInf,a1
.outStr
        move.b (a1)+,(a0)+                      ; output "Inf"
        move.b (a1)+,(a0)+
        move.b (a1)+,(a0)+
        clr.b (a0)
.twoup
        addq #4,sp                                                      ; pop return address for two up return
.notInf
        rts

;------------------------------------------------------------------------------
; Check for a zero value. Output a single "0" if zero,
;
; Modifies:
;               a0
; Parameters:
;               fp0 = dbl
;------------------------------------------------------------------------------

_CheckZero:
        ftst fp0                                                                ; check if number is zero
        fbne .0003
        move.b #'0',(a0)+                               ; if zero output "0"
        clr.b (a0)
        addq #4,sp                                                      ; pop return address for two up return
.0003
        rts

;------------------------------------------------------------------------------
; Check for a negative number. This includes Nans and Infinities. Output a "-"
; if negative.
;
;       Modifies
;               a0
; Parameters:
;               fp0 = dbl
;------------------------------------------------------------------------------

_CheckNegative:
        ftst fp0                                                                ; is number negative?
        fbge .0002
        move.b #'-',(a0)+                               ; yes, output '-'
        fneg fp0                                                                ; make fp0 positive
.0002
        rts

;------------------------------------------------------------------------------
; Make the input value larger so that digits may appear before the decimal
; point.
;
; Modifies:
;               fp0,fp1,d6
; Parameters:
;               fp0 = dbl
;------------------------------------------------------------------------------

;       if (dbl < 1.0) {
;               while (dbl < 1.0) {
;                       dbl *= 1000000.0;
;                       exp -= 6;  
;               }
;       }

_MakeBig:
        fmove.w #1,fp1
.0002
        fcmp fp1,fp0                                            ; is fp0 > 1?
        fbge .0001                                                      ; yes, return
        fscale.l #6,fp0                                 ; multiply fp0 by a million
        subi.w #6,d6                                            ; decrement exponent by six
        bra .0002                                                               ; keep trying until number is > 1
.0001
        fmove.p fp0,_fpWork+24  ; debugging
        rts
        
;------------------------------------------------------------------------------
;       Create a number dbl2 on the same order of magnitude as dbl, but
;       less than dbl. The number will be 1.0e<dbl's exponent>
;
; Modifies:
;               d6,fp2
; Parameters:
;               fp0 = dbl
;------------------------------------------------------------------------------

;       // The following is similar to using log10() and pow() functions.
;       // Now dbl is >= 1.0
;       // Create a number dbl2 on the same order of magnitude as dbl, but
;       // less than dbl.
;       dbl2 = 1.0;
;       dbla = dbl2;
;       if (dbl > dbl2) {       // dbl > 1.0 ?
;               while (dbl2 <= dbl) {
;                       dbla = dbl2;
;                       dbl2 *= 10.0;   // increase power of 10
;                       exp++;
;               }
;               // The above loop goes one too far, we want the last value less
;               // than dbl.
;               dbl2 = dbla;
;               exp--;
;       }

_LessThanDbl:
        fmove.w #1,fp2                  ; setup fp2 = 1
        fcmp fp2,fp0                            ; if (dbl > dbl2)
        fble .0004
.0006
        fcmp fp0,fp2                            ; while (dbl2 <= dbl)
        fbgt .0005
        fscale.w #1,fp2                 ; dbl2 *= 10 (increase exponent by one)
        addi.w #1,d6                            ; exp++
        bra .0006
.0005
        fscale.l #-1,fp2                ; dbl2 /= 10 (decrease exponent by one)
        subi.w #1,d6                            ; exp--;
.0004   
        fmove.p fp0,_fpWork     ; debugging
        fmove.p fp2,_fpWork+12
        rts

;------------------------------------------------------------------------------
; Compute the number of digits before the decimal point.
;
; Modifies:
;               d0,d6,_digits_before_decpt
; Parameters:
;               d6 = exponent
;------------------------------------------------------------------------------

; if (exp >= 0 && exp < 6) {
;   digits_before_decpt = exp+1;
;               exp = 0;
;       }
;       else if (exp >= -6)
;               digits_before_decpt = 1;
;       else
;               digits_before_decpt = -1;

_ComputeDigitsBeforeDecpt:
        tst.w d6
        bmi .0007
        cmpi.w #6,d6
        bge .0007
        move.w d6,d0
        addi.w #1,d0
        move.w d0,_digits_before_decpt  
        clr.w d6
        rts
.0007
        cmpi.w #-6,d6
        blt .0009
        move.w #1,_digits_before_decpt
        rts
.0009
        move.w #-1,_digits_before_decpt
        rts

;------------------------------------------------------------------------------
;       Spit out a leading zero before the decimal point for a small number.
;
; Parameters:
;               d6 = exponent
;------------------------------------------------------------------------------

;  if (exp < -6) {
;                buf[ndx] = '0';
;                ndx++;
;    buf[ndx] = '.';
;    ndx++;
;  }

_LeadingZero:
        cmpi.w #-6,d6
        bge .0010
        move.b #'0',(a0)+
        move.b #'.',(a0)+
.0010
        rts

;------------------------------------------------------------------------------
; Extract the digits of the significand.
;
; Modifies:
;               _precision variable
; Register Usage
;               d1 = digit
;               fp0 = dbl
;               fp2 = dbl2
;               fp3 = digit as decimal float
;               fp7 = dbla
; Parameters:
;               fp0
;------------------------------------------------------------------------------

;       // Now loop processing one digit at a time.
;  for (nn = 0; nn < 25 && precision > 0; nn++) {
;    digit = 0;
;               dbla = dbl;
;               // dbl is on the same order of magnitude as dbl2 so
;               // a repeated subtract can be used to find the digit.
;    while (dbl >= dbl2) {
;      dbl -= dbl2;
;      digit++;
;    }
;    buf[ndx] = digit + '0';
;               // Now go back and perform just a single subtract and
;               // a multiply to find out how much to reduce dbl by.
;               // This should improve the accuracy
;               if (digit > 2)
;                       dbl = dbla - dbl2 * digit;
;    ndx++;
;    digits_before_decpt--;
;    if (digits_before_decpt==0) {
;                       buf[ndx] = '.';
;                       ndx++;
;    }
;    else if (digits_before_decpt < 0)
;      precision--;
;               // Shift the next digit to be tested into position.
;    dbl *= 10.0;
;  }
        
_SpitOutDigits:
        move.w #24,d0                   ; d0 = nn
        fmove.l #10,fp1         ; fp1 = 10.0
.0017   
        tst.l _precision
        ble .0011
        moveq #0,d1                             ; digit = 0
        fmove fp0,fp7                   ; dbla = dbl
.0013
        fcmp fp2,fp0
        fblt .0012
        fsub fp2,fp0                    ; dbl -= dbl2
        addi.b #1,d1                    ; digit++
        bra .0013
.0012
        addi.b #'0',d1          ; convert digit to ascii
        move.b d1,(a0)+         ; and store
        subi.b #'0',d1          ; d1 = binary digit again
.0014
        subi.w #1,_digits_before_decpt
        bne .0015
        move.b #'.',(a0)+
.0015
        tst.w _digits_before_decpt
        bge .0016
        subi.l #1,_precision
.0016
        fscale.l #-1,fp2                ; dbl *= 10.0
        dbra d0,.0017
.0011   
        rts

;------------------------------------------------------------------------------
; If the number ends in a decimal point, trim off the point.
;
; Registers Modified:
;               none
; Parameters:
;               a0 = pointer to end of number
; Returns:
;               a0 = updated to point just past last digit.
;------------------------------------------------------------------------------

_TrimTrailingPoint:
        cmpi.b #'.',-1(a0)
        bne .0001
        clr.b -(a0)
        rts
.0001
        cmpi.b #'.',(a0)
        bne .0002
        cmpi.b #0,1(a0)
        bne .0002
        clr.b (a0)
        subq #1,a0
.0002
        rts
        
;------------------------------------------------------------------------------
; If the number ends in .0 get rid of the .0
;
; Registers Modified:
;               none
; Parameters:
;               a0 = pointer to last digits of number
; Returns:
;               a0 = updated to point just past last digit.
;------------------------------------------------------------------------------

_TrimDotZero:
        tst.b (a0)
        bne .0004
        cmpi.b #'0',-1(a0)
        bne .0004
        cmpi.b #'.',-2(a0)
        bne .0004
        clr.b -2(a0)
        subq #2,a0
.0004
        rts

;------------------------------------------------------------------------------
; Trim trailing zeros from the number. Generally there is no need to display
; trailing zeros.
; Turns a number like 652.000000000000000000000 into 650.0
;
; Registers Modified:
;               none
; Parameters:
;               a0 = pointer to last digits of number
; Returns:
;               a0 = updated to point just past last digit.
;------------------------------------------------------------------------------

;       // Trim trailing zeros from the number
;  do {
;      ndx--;
;  } while(buf[ndx]=='0');
;  ndx++;

_TrimTrailingZeros:
.0018   
        cmpi.b #'0',-(a0)               ; if the last digit was a zero, backup
        beq .0018
        addq #1,a0                                      ; now advance by one
        move.b #0,(a0)                  ; NULL terminate string
        rts

;------------------------------------------------------------------------------
; Output 'e+' or 'e-'
;
; Registers Modified:
;               d6.w (if negative)
; Parameters:
;               a0 = pointer to last digits of number
; Returns:
;               a0 = updated to point just past '+' or '-'.
;------------------------------------------------------------------------------

;       // Spit out +/-E
;  buf[ndx] = E;
;  ndx++;
;  if (exp < 0) {
;    buf[ndx]='-';
;    ndx++;
;    exp = -exp;
;  }
;  else {
;               buf[ndx]='+';
;               ndx++;
;  }

_SpitOutE:      
        move.b _E,(a0)+
        tst.w d6
        bge .0021
        move.b #'-',(a0)+
        neg.w d6
        bra .0022
.0021
        move.b #'+',(a0)+
.0022
        rts

;------------------------------------------------------------------------------
; Extract a single digit of the exponent. Extract works from the leftmost digit
; to the rightmost.
;
; Register Usage
;               d2 = history of zeros
;               d3 = digit
; Modifies
;               d2,d3,d6,a0
; Parameter
;       d1.w = power of ten
;               d6.w = exponent
;------------------------------------------------------------------------------

_ExtExpDigit:
        ext.l d6                                ; make d6 a long
        divu d1,d6                      ; divide by power of ten
        move.b d6,d3            ; d3 = quotient (0 to 9)
        swap d6                                 ; d6 = remainder, setup for next digit
        or.b d3,d2
        tst.b d3
        bne .0003
        tst.b d2        
        beq .0004
.0003
        addi.b #'0',d3  ; convert to ascii
        move.b d3,(a0)+
.0004
        rts

;------------------------------------------------------------------------------
; Extract all the digits of the exponent.
;
; Register Usage
;               d1 = power of 10
;               d2 = history of zeros
; Parameters
;               a0 = pointer to string buffer
;               d6 = exponent
;------------------------------------------------------------------------------

;       // If the number is times 10^0 don't output the exponent
;  if (exp==0) {
;    buf[ndx]='\0';
;    goto prt;
;  }

_ExtExpDigits:
        tst.w d6                                                        ; is exponent zero?
        beq .0002
        bsr _SpitOutE                                   ; exponent is non-zero e+
        clr.b d2                                                        ; d2 = history of zeros
        move.w #1000,d1
        bsr _ExtExpDigit
        move.w #100,d1
        bsr _ExtExpDigit
        move.w #10,d1
        bsr _ExtExpDigit
        move.w #1,d1
        bsr _ExtExpDigit
.0002:
        move.b #0,(a0)                          ; NULL terminate string
        rts                                                                             ; and return

;------------------------------------------------------------------------------
; Pad the left side of the output string.
;
; Modifies:
;               d0,d1,d2,d3
;------------------------------------------------------------------------------

;  // pad left
;  if (width > 0) {
;    if (ndx < width) {
;      for (nn = 39; nn >= width-ndx; nn--)
;        buf[nn] = buf[nn-(width-ndx)];
;      for (; nn >= 0; nn--)
;        buf[nn] = ' ';
;    }
;  }
        
_PadLeft:
        tst.b _width
        ble .0041
        move.l a0,d0
        sub.l #_fpBuf,d0        ; d0 = ndx
        cmp.b _width,d0
        bge .0041
        move.w #49,d1                   ; d1 = nn
.0040
        move.b _width,d2
        ext.w d2
        sub.w d0,d2                             ; d2 = width-ndx
        cmp.w d2,d1
        blt .0039
        move.w d1,d3                    ; d3 = nn
        sub.w d2,d3                             ; d3 = nn-(width-ndx)
        move.b (a0,d3.w),(a0,d1.w)
        subi.w #1,d1
        bra .0040
.0039
        tst.w d1
        bmi .0041
        move.b #' ',(a0,d1.w)
        subi.w #1,d1
        bra .0039
.0041
        rts

;------------------------------------------------------------------------------
; Pad the right side of the output string.
;
; Modifies:
;               d0
; Returns:
;               d0 = length of string
;------------------------------------------------------------------------------

;  // pad right
;  if (width < 0) {
;    width = -width;
;    while (ndx < width) {
;      buf[ndx]=' ';
;      ndx++;
;    }
;    buf[ndx]='\0';
;  }
;  return (ndx);

_PadRight:
        tst.b _width
        bpl .0042
        neg.b _width
        move.l a0,d0
        sub.l #_fpBuf,d0        ; d0 = ndx
.0044
        cmp.b _width,d0
        bge .0043
        move.b #' ',(a0,d0.w)
        addi.w #1,d0
        bra .0044
.0043
        move.b #0,(a0,d0.w)
.0042
        ext.w d0
        ext.l d0
        rts

;------------------------------------------------------------------------------
; Output a string representation of a decimal floating point number to a 
; buffer.
;
; Register Usage
;               a0 = pointer to string buffer
;               d6 = exponent
; Parameters:
;               fp0 = number to convert
;------------------------------------------------------------------------------

_sprtflt:
        bsr _CheckNegative                      ; is number negative?
        bsr _CheckZero                                  ; check for zero
        bsr _CheckNan                                           ; check for Nan or infinity
        ; Now the fun begins
        clr.l d6
        bsr _MakeBig
        bsr _LessThanDbl
        bsr _ComputeDigitsBeforeDecpt
        bsr _LeadingZero
        bsr _SpitOutDigits
        bsr _TrimTrailingZeros
        bsr _TrimTrailingPoint
        bsr _TrimDotZero
        bsr _ExtExpDigits                               ; extract exponent digits
        bsr _PadLeft                                            ; pad the number to the left or right
        bra _PadRight

;------------------------------------------------------------------------------
; Trap #15, function 39 - convert floating-point to string and display
;
; Parameters
;               a0 = pointer to buffer
;               fp0 = number to print
;               d1 = width of print field
;               d2 = precision
;               d3 = 'E' or 'e'
;------------------------------------------------------------------------------

prtflt:
        movem.l d0/d1/d2/d3/d6/a1,-(a7)
;       fmove.p fp0,-(a7)
        move.l a1,a0                                            ; a0 = pointer to buffer to use
        move.b d1,_width
        move.l d2,_precision
        move.b d3,_E
        bsr _sprtflt
        bsr DisplayString
;       fmove.p (a7)+,fp0
        movem.l (a7)+,d0/d1/d2/d3/d6/a1
        rts

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.