; ; 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. ;; 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 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