boot3/Toolbox/SANE/FP881B2DC.a
Elliot Nunn 5b0f0cc134 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 10:02:57 +08:00

1299 lines
34 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; File: FP881B2DC.a
;
; Contains: Floating Point Binary-To-Decimal conversions
;
; Written by: Clayton Lewis; adapted from FPBD by Jerome Coonen
;
; Copyright: © 1985-1990 by Apple Computer, Inc., all rights reserved.
;
; This file is used in these builds: Mac32
;
; Change History (most recent first):
;
; <3> 9/17/90 BG Removed <2>. 040s are behaving more reliably now.
; <2> 7/4/90 BG Added EclipseNOPs for flakey 040s.
; <1.1> 11/11/88 CCH Fixed Header.
; <1.0> 11/9/88 CCH Adding to EASE.
; <1.0> 2/12/88 BBM Adding file for the first time into EASEÉ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; File: FP881b2dc.a
;; Implementation of FP881 binary <-> decimal conversions.
;; Copyright Apple Computer, Inc. 1985,1986,1987
;; All Rights Reserved
;; Confidential and Proprietary to Apple Computer,Inc.
;;
;; Written by Clayton Lewis from FPBD by J. Coonen; begun 1 Jul 85.
;; Debugged by Stuart McDonald.
;;
;; Modification history:
;; Rev11 : First version of file
;; Rev20 : First version with fairly solid bin-dec conversions REV20 2 Oct 85
;; Rev21 : 16Jun86 CRL moved to MPW
;; Rev22 : 17Nov86 -S.McD. Wrong machine code for FNEG F2,F2.
;; Rev23 : 23Nov86 -S.McD. Filled in all the missing B2D conversions.
;; Rev24 : 15Dec86 -S.McD. Logic bug in B2D conversions fixed.
;; 30Dec86 -S.McD. Zeroed FPSR for NonArith codes exiting thru halt check.
;; 13Jan87 -S.McD. Now save/restore any of the registers FP2-FP7 used.
;; 15Jan87 -S.McD. Status and copyright notice changed.
;; 21Jan87 -S.McD. Deleted some commented out code in BD85.
;;<PB172/02Jun87> JTC Rolled in patch to get D3 flag cleared in B2D.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;*************************************************************
;; DECIMAL --> BINARY CONVERSION
;*************************************************************
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Use of registers:
;; D0 = working register
;; D1 = working register
;; D2 = working register
;; D3.W = d.exp
;; D3 = bit 31 - a little trick at PTMUL; bit 30 - inexact flag
;; D5 = user control register (with halts disabled), always unchanged
;; D7 = control register (as currently set)
;;
;; A0 = address of decimal record
;; A1 = ptr within d.sig
;;
;; FP0 = accumulating value, eventually final result
;; FP1 = the constant 10
;; FP2 = 10^|exp| as returned from POW10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
D2B
FMOVE #0,FPSR ; IEEE default status -S.McD.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Initialize registers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVEM.L D2/D3/D5/D7,-(SP) ; save more registers
FMOVEM FP2,-(SP) ; must save/restore FP2-FP7 -S.McD.
ANDI.L #$FF,D5 ; disable halts in D5 copy of control
MOVEQ #0,D3 ; 0 -> loop and inexact flags in D3.hi
FMOVECR #$0F,FP0 ; 0 -> FP0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set up pointers, get d.exp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVE.L LKADR2(A6),A0 ; address of decimal, src
MOVE.W 2(A0),D3 ; d.exp
LEA 4(A0),A1 ; pointer to d.sig
MOVEQ #19,D2 ; max number of digits allowed from d.sig
FMOVECR #$33,FP1 ; 10 -> FP1
MOVE.B (A1)+,D1 ; d.sig length byte
BEQ DBZSTO ; if d.sig has no characters, stuff 0.0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; First character
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVE.B (A1),D0 ; no autoincrement
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check for infinity
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CMPI.B #$49,D0 ; is it I?
BEQ.S DBINF ; if so, branch
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check for NaN
;; If found, construct extended NaN on the stack
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CMPI.B #'N',D0 ; capital N?
BNE.S DBDigits ; if not, branch
ADDQ.L #1,A1 ; next character after 'N'
SUBQ.B #1,D1 ; # characters after 'N' in d.sig
MOVEQ #8,D2 ; assume 8 digits in nancode
CMPI.B #4,D1 ; are there at least 4?
BGE.S @31 ; if so, branch
SUBQ.B #4,D2 ; else prepare for nancode followed by 0000
ADD.B D1,D2 ; total number of digits in extended format
@31
BSR.S @35
SUBQ.L #8,SP
MOVE.L D3,(SP)
CLR.L D3
MOVEQ #8,D2
BSR.S @35
MOVE.L D3,4(SP)
BRA.S @39
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Move max(D1,D2) digits to D3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@35
ROL.L #4,D3 ; align bits so far
SUBQ.B #1,D1 ; #characters left in string
BMI.S @37 ; if none, branch
MOVE.B (A1)+,D0 ; next character
CMPI.B #'9',D0 ; is it 1..9?
BLE.S @36 ; if so, branch
ADDI.B #9,D0 ; else adjust A..F
@36
ANDI.B #$0F,D0 ; mask to nibble
OR.B D0,D3
@37
SUBQ.W #1,D2 ; decrement char count
BNE.S @35 ; continue if characters left
RTS ; else return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Clear lead bit (irrelevant) and
;; test for nonzero nancode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@39
ANDI.L #$3FFFFFFF,(SP) ; temporarily ignore quiet bit
OR.L (SP),D3 ; and check for zero nan
BNE.S @40
MOVEQ #NaNZero,D3
SWAP D3 ; align lead bits
MOVE.L D3,(SP)
@40
OR.B #$40,(SP) ; set quiet nan bit
DBNFIN
MOVE.W #$7FFF,D0 ; max exponent
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set sign and move resulting extended to FP0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DBSSTO
TST.B (A0) ; sign in hi byte of word
BEQ.S @1
BSET #15,D0
@1
SWAP D0 ; prepare for 96 bit format
MOVE.L D0,-(SP) ; 96 bit extended on stack
FMOVE.X (SP)+,FP0 ; extended in FP0
BRA.S DBStore
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INFINITY found. signif = 0, exit thru nan code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DBINF
MOVEQ #0,D0
MOVE.L D0,-(SP)
MOVE.L D0,-(SP)
BRA.S DBNFIN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; NOT INFINITY, NOT NAN.
;; Zero or truly digits.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DBDigits
CMPI.B #$30,D0 ; is it '0'?
BNE.S SigDigs ; if not, then truly a number
DBZSTO
TST.B (A0) ; if zero, check d.sgn (hi byte of word)
BEQ.S @1
FNEG FP0,FP0
@1
BRA.S DBStore
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SIGDIGS
;; Build significand (in FP0) from significant digits
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SigDigTop
FMUL FP1,FP0 ; 10*temp -> temp
SigDigs
MOVEQ #$0F,D0 ; nibble mask
AND.B (A1)+,D0 ; next character of d.sig
FADD.B D0,FP0 ; temp + next char -> temp
SUBQ.B #1,D1
BEQ.S Canon ; continue unless end of d.sig
SUBQ.B #1,D2
BNE.S SigDigTop ; continue unless 20th digit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; More than 19 input digits.
;; Set bit #30 of D3 if nonzero last char (inexact).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVEQ #$0F,D0 ; nibble mask
AND.B (A1),D0 ; last character
BEQ.S DoneDig
BSET #30,D3
BRA.S DoneDig
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CANON - 19 or fewer input digits.
;; Enforce smallest positive
;; exponent. Append trailing 0's and
;; decrement exponent. Exponent OK up
;; to 27 since no inexact until 28.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CanonTop
CMPI.W #27,D3 ; d.exp is in D3
BLE.S DoneDig
FMUL FP1,FP0 ; 10*signif -> signif
SUBQ.W #1,D3
Canon
SUBQ.W #1,D2 ; max digit count
BNE.S CanonTop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DONEDIG - Pass correct sign to FP0,
;; and go to core routine for scaling.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DoneDig
TST.B (A0) ; d.sgn (hi byte of word)
BEQ.S @1
FNEG FP0,FP0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Scale the value in FP0 (source)
;; by the exponent in D3
;; and return the result in FP0.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@1
BSR.S DBCore
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DBStore
;; Restore registers.
;; Check requested data format and return FP0 to caller.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DBStore
FMOVEM (SP)+,FP2 ; must save/restore FP2-FP7 -S.McD.
MOVEM.L (SP)+,D2/D3/D5/D7 ; restore working registers
MOVE.L LKADR1(A6),A1 ; dest address
MOVE.W LKOP(A6),D1 ; opword
LSR.W #6,D1 ; format info to hi bits of lo byte
ADD.B D1,D1 ; 'not-float' -> carry bit
BCS.S IntFormat
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Here if a floating point format
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ADD.B D1,D1 ; S -> carry bit, D -> hi bit
BCS.S Sformat
BPL Return2Args ; X format
FMOVE.D FP0,(A1) ; D format
BRA Exit2Args
Sformat
FMOVE.S FP0,(A1)
BRA Exit2Args
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Here if an integer format
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IntFormat
ADD.B D1,D1 ; C -> carry bit, L -> hi bit
BCS FromD2B ; finish off in CVTE2Comp for Comp format
BMI.S Lformat
FMOVE.W FP0,(A1) ;I format
BRA Exit2Args
Lformat
FMOVE.L FP0,(A1)
BRA Exit2Args
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DBCORE
;; When entered from D2B:
;; D3.W has exponent, bit 30 holds inexact (& >19 digits) flag
;; D5 holds user's control register setting
;; A0 points to source value (decimal or extended)
;; FP0 has signed significand.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DBCore
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set rounding up, down or nearest depending on current mode,
;; sign of operand, and sign of exponent.
;; Recall that the control register rounding bits are 4 & 5.
;; 00=tonearest 01=towardzero 10=downward 11=upward
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVE.L D5,D7 ; clean copy of control register
MOVEQ #$30,D1 ; rounding bit mask
AND.B D7,D1 ; caller's rounding bits -> D1
BEQ.S @51 ; ToNearest => leave rounding alone, clear flags
TST.B (A0) ; operand +?
BPL.S @99 ; if so, branch
CMPI.B #$20,D1 ; rounding now downward?
BEQ.S @1 ; if so, branch
@0
TST.W D3
BMI.S @20 ; if exp neg, set upward; else...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set rounding downward
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@10
ANDI.L #$FFDF,D7 ; clear current rounding
ORI.B #$20,D7
BRA.S @30
@99
CMPI.B #$30,D1 ; rounding now upward?
BNE.S @0 ; if operand pos & not upward, branch
@1
TST.W D3
BMI.S @10 ; if exp neg, set downward; else...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set rounding upward
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@20
ORI.B #$30,D7
@30
; and install in 881
FMOVE D7,FPCR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compute 10^|exp|, put into FP2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@51
BSR POW10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Force toward-zero rounding
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVE.L D5,D7 ; copy of control
ANDI.L #$FFDF,D7 ; control with cleared rounding
ORI.B #$10,D7 ; set towardzero rounding
FMOVE D7,FPCR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Scale by 10^|exp|. ( 10^|exp| in FP2 )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TST.W D3 ; pos or neg exponent?
BPL.S @101 ; if pos, branch
FDIV FP2,FP0
BRA.S @102
@101
FMUL FP2,FP0
@102
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; If inexact, adjust result.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FMOVE FPSR,D0
BTST #9,D0 ; did div or mul set inexact?
BNE.S Tweak ; if so, adjust for it
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check inexact flag from earlier operations, bit 30 of D3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BTST #30,D3 ; inexact flag
BNE.S Tweak
DBCoreReturn
BRA.S DBExit
Tweak
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set inexact flag on 881.
;; Check caller's rounding mode and tweak a bit if needed.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BSET #3,D0 ; set inexact even in lost digit case
FMOVE D0,FPSR
TST.B D1 ; D1 has caller's rounding bits
BEQ.S Jam1LSB ; ToNearest is hopeless, just set operand LSB
CMPI.B #$10,D1 ; TowardZero rounding?
BEQ.S DBExit ; if so, result OK, return to caller
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Directed rounding. Add 1/2 ulp.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FGETEXP FP0,FP1
FMOVECR #$32,FP2
FTEST FP0
FBUN @2
FBOLT @1
FNEG FP2,FP2
@1
FSCALE.W #-64,FP2
FSCALE FP1,FP2
FADD FP2,FP0
@2
BRA.S DBExit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the operand LSB
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Jam1LSB
FMOVE.X FP0,-(SP) ; copy to stack
MOVE.W (SP),D0 ; sign & exp
ADDQ.W #1,D0 ; 7FFF -> 8000 & FFFF -> 0000
ADD.W D0,D0 ; 8000 -> 0000 & 0000 -> 0000
BNE.S @1 ; continue if neither Inf nor NaN
ADD.L #12,SP ; else kill copy of FP0 on stack
BRA.S DBExit ; and return
@1
BSET #0,11(SP) ; if genuine number, turn on LSB
FMOVE.X (SP)+,FP0 ; and move back to FP0
DBExit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Restore user value of control register.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FMOVE D5,FPCR
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compute power of 10.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TAB10SZ EQU 9
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Table entries are 8 words long:
;; 1 -- n, where table value is 10^n
;; 2 -- rounding error, +1 if rounded up, -1 if down
;; 3-8 -- 96-bit extended value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TAB10
DC.W 14
DC.W 0
DC.W $402D
DC.W $0000
DC.W $B5E6
DC.W $20F4
DC.W $8000
DC.W $0000
DC.W 27
DC.W 0
DC.W $4058
DC.W $0000
DC.W $CECB
DC.W $8F27
DC.W $F420
DC.W $0F3A
DC.W 55
DC.W 1
DC.W $40B5
DC.W $0000
DC.W $D0CF
DC.W $4B50
DC.W $CFE2
DC.W $0766
DC.W 108
DC.W 1
DC.W $4165
DC.W $0000
DC.W $DA01
DC.W $EE64
DC.W $1A70
DC.W $8DEA
DC.W 206
DC.W -1
DC.W $42AB
DC.W $0000
DC.W $9F79
DC.W $A169
DC.W $BD20
DC.W $3E41
DC.W 412
DC.W 1
DC.W $4557
DC.W $0000
DC.W $C6B0
DC.W $A096
DC.W $A952
DC.W $02BE
DC.W 824
DC.W 1
DC.W $4AB0
DC.W $0000
DC.W $9A35
DC.W $B246
DC.W $41D0
DC.W $5953
DC.W 1648
DC.W 1
DC.W $5561
DC.W $0000
DC.W $B9C9
DC.W $4B7F
DC.W $A8D7
DC.W $6515
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Table is accessed from high exponents to low. It is
;; addressed from bottom up for historical reasons.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TAB10E
DC.W 3296
DC.W 1
DC.W $6AC4
DC.W $0000
DC.W $86D4
DC.W $8D66
DC.W $26C2
DC.W $7EEC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Start code. Two special cases:
;; exp < 15 - just pull value from table below.
;; exp > 5000 - outrageous values are set to 5000
;;
;; A0 = pointer to decimal record, save for later use
;; D1.B = rounding bits as set by caller, save for later use
;; D2.W = local use-position in tens table, not to be saved
;; D3.W = exp on input, D3 bit 30 = inexact flag. Save D3.
;; D7 = rounding direction bits and working values of exponent
;; FP2 = register expecting return value 10^|exp|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
POW10
MOVEQ #26,D2
LSL.L D2,D7 ; shift rounding direction bits to #31,30
MOVEQ #TAB10SZ,D2 ; note D2.hi=0
MOVE.W D3,D7 ; compute |exp|
BPL.S @1
NEG.W D7
@1
CMPI.W #15,D7 ; easy case when |exp| < 15
BCS.S PTQUICK
CMPI.W #5000,D7 ; |exp| > 4999?
BCS.S @10
MOVE.W #5000,D7 ; if so, just use 5000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Loop through table from hi values to lo,
;; accumulating powers of ten.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@10
LEA TAB10E(PC),A1 ; pointer to a power of 10
PTLOOP
CMP.W (A1),D7 ; table power vs. exp
BCS.S PTSKIP ; carry set => D7<(A1) <=> exp<table power
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; When exponent exceeds current table entry, decrement the
;; exponent and multiply/store by the power of ten. If
;; rounding is required on the power of ten,the table entry
;; is moved to the stack and adjusted there.
;;
;; Note: If an inexact power of ten is used, the inexact
;; exception bit is arbitrarily forced on, regardless of
;; the corresponding halt bit.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SUB.W (A1)+,D7 ; decrement exp and set A1 to point to error
TST.W (A1) ; check direction of error
BEQ.S @31 ; if exact, skip over error routine
BSET #30,D3 ; inexact flag.
CMPI.L #$3FFFFFFF,D7; hi bits: 00 nearest, 01 zero, 10 down, 11 up
BMI.S @13 ; nearest or upward
TST.W (A1) ; (A1)²0 => error OK since downward or TowardZero
BLE.S @31
BRA.S @15
@13
BCS.S @31 ; nearest
TST.W (A1)
BGE.S @31 ; (A1)³0 => error OK since Upward
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; If the value must be rounded, copy it to the stack.
;; Rounding: error=1 => subtract 1 from LSB. error=-1 => add.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@15
MOVE.L 10(A1),-(SP); low significant long
TST.W (A1)+ ; A1 now points to extended value
BPL.S @21 ; if error=1, then subtract
ADDQ.L #1,(SP) ; force opposite rounding
BRA.S @23
@21
SUBQ.L #1,(SP)
@23
MOVE.L 4(A1),-(SP) ; middle 32 sig bits
MOVE.L (A1),-(SP) ; exp and hi 16 sig bits
FMOVE.X (SP)+,FP1
BRA.S @33
@31
ADDQ.L #2,A1 ; skip over error, point to extended value
FMOVE.X (A1),FP1
@33
BSR.S PTMUL
SUBQ.L #4,A1 ; return to start of table entry
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Next table entry is 8 words (16 bytes) up.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PTSKIP
SUBA.W #16,A1 ; skip to next table entry
SUBQ.W #1,D2 ; decrement num counter
BNE.S PTLOOP ; zero when finished
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Now have exp <= 14. If D1<0 then must
;; multiply in, else just move it from table.
;; Compute POWER * 12, offset into table below
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PTQUICK
LSL.W #2,D7 ; 4*D7
MOVE.W D7,D2
ADD.W D7,D7 ; 8*D7
ADD.W D2,D7 ; = 12*D7
FMOVE.X TAB10S(PC,D7),FP1 ; correct power of 10 into FP1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; This sequence is used as a subroutine by table
;; code at start. It is also fallen into from PTQUICK.
;;
;; Trick with high bit of D3: 0 => 1st pass thru code,
;; so load value rather than multiply.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PTMUL
TST.L D3 ; positive means do not multiply
BPL.S @42
FMUL FP1,FP2
RTS
@42
FMOVE FP1,FP2
BSET #31,D3 ; set negative to force subsequent multiplies
RTS
TAB10S
DC.W $3FFF ; 10 ^ 0
DC.W $0000
DC.W $8000
DC.W $0000
DC.W $0000
DC.W $0000
DC.W $4002 ; 10 ^ 1
DC.W $0000
DC.W $A000
DC.W $0000
DC.W $0000
DC.W $0000
DC.W $4005 ; 10 ^ 2
DC.W $0000
DC.W $C800
DC.W $0000
DC.W $0000
DC.W $0000
DC.W $4008 ; 10 ^ 3
DC.W $0000
DC.W $FA00
DC.W $0000
DC.W $0000
DC.W $0000
DC.W $400C ; 10 ^ 4
DC.W $0000
DC.W $9C40
DC.W $0000
DC.W $0000
DC.W $0000
DC.W $400F ; 10 ^ 5
DC.W $0000
DC.W $C350
DC.W $0000
DC.W $0000
DC.W $0000
DC.W $4012 ; 10 ^ 6
DC.W $0000
DC.W $F424
DC.W $0000
DC.W $0000
DC.W $0000
DC.W $4016 ; 10 ^ 7
DC.W $0000
DC.W $9896
DC.W $8000
DC.W $0000
DC.W $0000
DC.W $4019 ; 10 ^ 8
DC.W $0000
DC.W $BEBC
DC.W $2000
DC.W $0000
DC.W $0000
DC.W $401C ; 10 ^ 9
DC.W $0000
DC.W $EE6B
DC.W $2800
DC.W $0000
DC.W $0000
DC.W $4020 ; 10 ^ 10
DC.W $0000
DC.W $9502
DC.W $F900
DC.W $0000
DC.W $0000
DC.W $4023 ; 10 ^ 11
DC.W $0000
DC.W $BA43
DC.W $B740
DC.W $0000
DC.W $0000
DC.W $4026 ; 10 ^ 12
DC.W $0000
DC.W $E8D4
DC.W $A510
DC.W $0000
DC.W $0000
DC.W $402A ; 10 ^ 13
DC.W $0000
DC.W $9184
DC.W $E72A
DC.W $0000
DC.W $0000
DC.W $402D ; 10 ^ 14
DC.W $0000
DC.W $B5E6
DC.W $20F4
DC.W $8000
DC.W $0000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;*************************************************************
;; BINARY --> DECIMAL CONVERSION
;*************************************************************
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Use of registers:
;; D0 = copy of status register for input classification
;; D1 = working register
;; D2 = working register
;; D3.W = tentative exponent
;; D3 = bit 31 - a little trick at PTMUL;
;; D4 = fixed formatting: current best guess for #digits left of the decimal point
;; D5 = user control register with halts disabled, always unchanged
;; D6 = f.digits, unchanged until last step at BD9
;; D7 = control register (as currently set)
;;
;; A0 = address of source (binary)
;; A1 = ptr within d.sig
;; A2 = address of destination (decimal record)
;; A3 = address of decform record
;;
;; FP0 = working register
;; FP1 = used for comparisons and to hold 10^19 just before final scaling
;; FP2 = exp.frac and also 10^|exp| returned from POW10
;; FP3 = binary source value, always unchanged
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
B2D
FMOVE #0,FPSR ; IEEE default status -S.McD.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set up registers,
;; Preset zero result
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVEM.L D2-D7/A2-A3,-(SP) ; save working registers
FMOVEM FP2-FP3,-(SP) ; must save/restore FP2-FP7 -S.McD.
ANDI.L #$FF,D5 ; disable halts on D5 copy of control
MOVEQ #0,D3 ; clear all internal flags <02Jun87>
MOVE.L D5,D7 ; working copy of control
MOVE.L LKADR2(A6),A0
MOVE.L LKADR1(A6),A2
MOVE.L LKADR3(A6),A3
CLR.L (A2)+ ; positive, exponent=0, A2 pts to d.sig
MOVE.W #$0130,(A2) ; d.sig = '0'
MACHINE MC68020
BFEXTU D1{18:3},D0 ; move the 3 opcode size bits to D0. -S.McD.
MACHINE MC68000
ADD.W D0,D0 ; scale by two to address into word table
ANDI.W #OPMASK2,D0 ; clear spurious bits
B2Dop
MOVE.W B2DTab(D0),D0
JMP B2Dop(D0)
B2DTab
DC.W B2Dext-B2Dop ; ... case 0
DC.W B2Ddbl-B2Dop
DC.W B2Dsgl-B2Dop
DC.W B2Dint-B2Dop ; ... case 3 not used
DC.W B2Dint-B2Dop
DC.W B2Dlong-B2Dop
DC.W B2Dcomp-B2Dop
DC.W B2Dint-B2Dop ; ... case 7 not used
B2Dext
MOVE.L 6(A0),-(SP) ; move extended source to FP0
MOVE.L 2(A0),-(SP)
MOVE.L (A0),-(SP)
BPL.S @1 ; branch if positive
MOVE.B #1,-4(A2) ; else 1 -> d.sgn
@1
FMOVE.X (SP)+,FP0 ; source to FP0
BRA.S B2Dfp
B2Ddbl
MOVE.L 4(A0),-(SP) ; move double source to FP0
MOVE.L (A0),-(SP)
BPL.S @1 ; branch if positive
MOVE.B #1,-4(A2) ; else 1 -> d.sgn
@1
FMOVE.D (SP)+,FP0 ; source to FP0
BRA.S B2Dfp
B2Dsgl
MOVE.L (A0),-(SP) ; move single source to FP0
BPL.S @1 ; branch if positive
MOVE.B #1,-4(A2) ; else 1 -> d.sgn
@1
FMOVE.S (SP)+,FP0 ; source to FP0
BRA.S B2Dfp
B2Dint
MOVE.W (A0),-(SP) ; move word source to FP0
BPL.S @1 ; branch if positive
MOVE.B #1,-4(A2) ; else 1 -> d.sgn
@1
FMOVE.W (SP)+,FP0 ; source to FP0
BRA.S B2Dfp
B2Dlong
MOVE.L (A0),-(SP) ; move long source to FP0
BPL.S @1 ; branch if positive
MOVE.B #1,-4(A2) ; else 1 -> d.sgn
@1
FMOVE.L (SP)+,FP0 ; source to FP0
BRA.S B2Dfp
B2Dcomp
BSR Comp2X
BPL.S @1 ; branch if positive
MOVE.B #1,-4(A2) ; else 1 -> d.sgn
@1
FMOVE.X FP1,FP0 ; source to FP0
B2Dfp
FMOVE.X FP0,-(SP) ; PUSH temp src extended -S.McD.
FABS.X FP0 ; |source| to FP0
FMOVE FPSR,D0
FMOVE FP0,FP3 ; save original copy of input x
LSL.L #6,D0 ; Z -> carry bit, Inf -> #31, Nan -> #30
BCS BDFin ; if zero, already done
BMI.S Inf ; branch if value is an infinity
BTST #30,D0 ; check nan bit
BEQ.S BD1 ; if not nan, then finite nonzero
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Handle NaN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVE.W #$114E,(A2)+; length 17, 'N'
MOVE.L 4(SP),D6 ; significand, hi word -S.McD.
BSR.S @2 ; convert to ascii, place in d.sig
MOVE.L 8(SP),D6 ; significand, lo word -S.McD.
BSR.S @2 ; convert to ascii, place in d.sig
BRA BDFin
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Short subroutine called by NaN handler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@2
MOVEQ #8,D0 ; loop count
@3
ROL.L #4,D6 ; print from hi to lo
MOVEQ #$0F,D1 ; nibble mask
AND.B D6,D1 ; strip nibble
OR.B #'0',D1 ; make ascii
CMPI.B #'9',D1 ; should it be a letter?
BLE.S @4 ; if not, done
ADDQ.B #7,D1 ; else add 7 to force A..F
@4
MOVE.B D1,(A2)+ ; stuff character
SUBQ.W #1,D0
BNE.S @3
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Handle Infinity
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Inf
MOVE.W #$0149,(A2) ; set d.sig to 'I'
BRA BDFin
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Handle nonzero finite number
;;
;; Input registers:
;; FP0 - binary source value
;; A0 - source (binary #) address
;; A2 - address of d.sig (significand of destination)
;;
;; In case of fixed formatting, skip to BD3
;; In case of float formatting, compute floor(log10(|x|))
;; by using Jerome's approximation: exp.frac
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BD1
TST.B (A3) ; examine decform.style
BEQ.S @2 ; branch if FloatDecimal
MOVEQ.L #0,D4 ; else clear D4=(#digits left of decimal for float)
BRA.S BD3 ; and skip the Float code
@2
MOVE.L #$4D104D42,D6; floor ( log10(2) )
MOVE.L 4(SP),D1 ; 1.frac -S.McD.
MOVE.W (SP),D1 ; sign and exp -S.McD.
BCLR #15,D1 ; shut off sign bit
SUB.W #$3FFF,D1 ; unbias exponent
BPL.S @3 ; branch if positive exponent
ADDQ.L #1,D6 ; else bump log10(2)
@3
SWAP D1 ; put in the order exp, then frac
ADD.W D1,D1 ; now have exp.frac
; D1*D6 -> D4,D6, implied pt at middle of D4
MACHINE MC68020
MULS.L D1,D4:D6 ; log10(exp.frac) into D4.h (junk in D6)
MACHINE MC68000
SWAP D4 ; move result down to low word
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Add 1, giving the number of digits
;; left of the decimal point
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ADDQ.W #1,D4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compute the scaling amount: Scale in -2(A2).
;; -Scale = tentative decimal exponent.
;; Note that code may loop back on itself.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BD3
FMOVE D5,FPCR ; restore control regiser
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Deliver digit count in D6. D6 unchanged until no longer needed (BD9).
;; If FloatDecimal, deliver 1..19.
;; If FixedDecimal, deliver stated count.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVE.L (A3),D6 ; count value from decform (D6.L now pos)
TST.B (A3) ; f.style, nonzero=fixed
BNE.S @2
TST.W D6 ; if count <= 0 then force 1
BLE.S @1
CMPI.W #19,D6 ; count > 19?
BLE.S @2 ; if not, skip
MOVEQ #19,D6 ; else force 19
BRA.S @2
@1
MOVEQ #1,D6
@2
MOVE.W D6,D3
BCLR #31,D3 ; first pass thru POW10 sets hi bit, be sure it's off
SUB.W D4,D3 ; D4=0 (fixed) or 1+log10(x) (float)
MOVE.W D3,-2(A2) ; deliver exponent to dest
NEG.W -2(A2) ; with correct sign
MOVEQ #0,D0 ; DBCore flag: no 20th digit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Scale the value in FP0 (source),
;; by the exponent in D3 (tentative exp),
;; and return result in FP0.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BSR DBCore ; compute scaled value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Round result to integer in FP0 according to user rounding.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FINT FP0,FP0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check scaled.x>10^#digits & check whether fixed overflow
;; (return '?') or rounding error in Floor(log10(x)).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TST.B (A3) ; fixed or float?
BNE.S @3
MOVE.L D6,D3 ; get f.digits
BRA.S @5
@3
MOVEQ #19,D3
@5
MOVE.L D5,D7 ; POW10 expects current control register in D7
BSR POW10
FCMP FP2,FP0
FBGE.W @9
BRA.S BD85
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Scaled.x value is out of range.
;; Distinguish 2 cases:
;; Float: fix log10(x) and recompute (even if exactly 10^N)
;; Fixed: store '?' and give up
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@9
TST.B (A3) ; fixed or float?
BNE.S @15 ; branch if fixed
ADDQ.W #1,D4 ; fix log10(x)
FMOVE FP3,FP0 ; restore original binary source value
BRA.S BD3
@15
MOVE.W #$013F,(A2) ; store '?' in d.sig
BRA BDFIN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Now check result against lower
;; bound: 10^(N-1).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BD85
TST.B (A3) ; nonzero => FixedDecimal
BNE.S BD9 ; branch if fixed
MOVE.L D6,D3 ; get f.digits
SUBQ.W #1,D3 ; want N-1
MOVE.L D5,D7 ; POW10 expects current control register in D7
BSR POW10
FCMP FP2,FP0
FBLT.W @7
BRA.S BD9
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Scaled value too small, force 10^(N-1).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@7
FMOVE FP2,FP0 ; store 10^(N-1) in place of x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compute the digit string.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BD9
FINT FP0,FP0 ; round final result to integer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check for zero, easy if found.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FBNE.W @1
MOVE.W #$0130,(A2) ; if zero, return '0'
BRA BDFIN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Divide scaled value by 10^19.
;; Note that use of D5 for control register ends
;; here. Now D5 holds status.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@1
MOVE.L #$60913D00,-(SP) ; exact double precision 10^19
MOVE.L #$43E158E4,-(SP)
FMOVE D5,FPCR
FMOVE FPSR,D5
FMOVE.D (SP)+,FP1
FDIV FP1,FP0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Collect the significand and set inexact if appropriate
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FMOVE.X FP0,-(SP) ; retrieve fractional value
MOVE.W (SP)+,D0 ; collect exponent, which cannot be positive
ADDQ.L #2,SP ; disregard junk word
MOVE.L (SP)+,D3 ; move significand to D3/4 (hi/lo)
MOVE.L (SP)+,D4 ; two moves are faster than movem
SUB.W #$3FFE,D0 ; unbias exponent
BEQ.S @4 ; skip un-normalizing if exp=-1 (D0=0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Un-normalize until D0 = 0
;; D3 D4
;; --------------------- ---------------------
;; |a b c d e f | |g h i j k l|
;; --------------------- ---------------------
;; \ \ \
;; --------------------- ---------------------
;; |0 0 0 0 a b | |c d e f g h |
;; --------------------- ---------------------
;;
;; Steps:
;; --------------------- ---------------------
;; |a b c d e f | |0 0 0 0 g h| shift lo
;; --------------------- ---------------------
;; --------------------- ---------------------
;; |c d e f 0 0 | |0 0 0 0 g h| backshift hi
;; --------------------- ---------------------
;; --------------------- ---------------------
;; |a b c d e f | |c d e f g h | OR and restore hi
;; --------------------- ---------------------
;; --------------------- ---------------------
;; |0 0 0 0 a b | |c d e f g h | shift hi
;; --------------------- ---------------------
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@2
CMPI.W #-32,D0 ; can we un-normalize by a whole long?
BPL.S @3 ; if not, branch
MOVE.L D3,D4 ; if so, shift significand by 32
MOVEQ #0,D3
ADD.W #32,D0 ; and adjust exponent accordingly
@3
MOVE.W D0,D2 ; copy of negative shift count
ADD.W #32,D2 ; 32 minus shift count ( ³ 0 )
NEG.W D0 ; positive shift count
LSR.L D0,D4 ; shift low bits of significand, pad with 0's
MOVE.L D3,D6 ; copy of significand.hi
LSL.L D2,D6 ; capture new hi bits for D4
OR.L D6,D4 ; and move them into place
LSR.L D0,D3 ; shift down remaining bits of D3
@4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Perform the actual conversion to string
;; First bump significand by one bit to avoid strings of 9's
;;
;; Input:
;; D3/D4 = significand.hi/significand.lo
;; A2 = ptr to d.sig length byte
;; To be used below:
;; D0 = next digit reclaimed from significand
;; D1 = decrementing digit counter (initially 19)
;; A0 = pointer to d.sig length byte
;; A2 = pointer to current ascii digit (as string is constructed)
;; D2/D6/D7 = temp storage for multiply-by-10 loop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FMOVE D5,FPSR ; restore status register
MOVEQ #0,D0
ADDQ.L #1,D4
ADDX.L D0,D3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Write the digit string, guaranteed nonzero, skipping leading zeros.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOVEA.L A2,A0 ; copy ptr to output string
CLR.B (A2)+ ; zero out length byte
MOVEQ #19,D1 ; digit counter
@11
ADD.L D4,D4 ; double fraction
ADDX.L D3,D3
ADDX.W D0,D0
MOVE.L D4,D2 ; save 2 * dig
MOVE.L D3,D6
MOVE.W D0,D7
ADD.L D4,D4 ; 4 * dig
ADDX.L D3,D3
ADDX.W D0,D0
ADD.L D4,D4 ; 8 * dig
ADDX.L D3,D3
ADDX.W D0,D0
ADD.L D2,D4 ; 10 * dig
ADDX.L D6,D3
ADDX.W D7,D0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; D0 is guaranteed nonzero if any nonzero digits have been seen.
;; The high byte of D0 contains a bit marking 'first nonzero digit seen'.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TST.W D0 ; ADDX.W won't set the Z bit
BEQ.S @12 ; 0 --> lead 0
ORI.L #$0130,D0 ; turn to ascii (flagging nonzero-digit-seen)
MOVE.B D0,(A2)+ ; and place into output string
CLR.B D0 ; leave nonzero-digit-seen mark
ADDQ.B #1,(A0) ; increment string length byte
@12
SUBQ.B #1,D1
BNE.S @11
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Restore registers and exit.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BDFIN
ADDA.W #12,SP ; POP temp src extended -S.McD.
FMOVEM (SP)+,FP2-FP3 ; must save/restore FP2-FP7 -S.McD.
MOVEM.L (SP)+,D2-D7/A2-A3 ; restore working registers
BRA Exit3Args