sys7.1-doc-wip/Toolbox/SANE/FP020BD.a
2019-07-27 22:37:48 +08:00

2055 lines
48 KiB
Plaintext

;
; File: FP020BD.a
;
; Contains: xxx put contents here xxx
;
; Written by: xxx put writers here xxx
;
; Copyright: © 1990 by Apple Computer, Inc., all rights reserved.
;
; This file is used in these builds: Mac32
;
; Change History (most recent first):
;
; <5> 9/15/90 BG Removed <4>. 040s are behaving more reliably now.
; <4> 7/4/90 BG Added EclipseNOPs for flakey 040s.
; <3> 5/9/90 JJ Bug fix to fringe case of binary-to-decimal conversion.
; <2> 4/14/90 JJ Made changes to support new binary-to-decimal, 96-bit precision,
; and improved Pack 5.
; <1> 3/2/90 JJ First checked in.
;
; To Do:
;
;-----------------------------------------------------------
; File: FPBD.a
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; Binary-to-decimal and decimal-to-binary conversions
;
; Copyright Apple Computer, Inc., 1983,1984,1985,1989,1990
; All rights reserved
;
; 09 Mar 90 Correctly rounded conversion algorithms written
; by Kenton Hansen completed
; 20 Mar 90 Conversion routines installed into MC68020 SANE
; by Jon Okada
; 21 Mar 90 KLH Corrected zero NaN anomaly, & Dec2Int setting of
; inexact, overlow and invalid simultaneously
; 30 Mar 90 KLH Put tighter limits on decform.digits
; 02 Apr 90 KLH Corrected case of df.digits = -32000 while style = fixed
; 11 Apr 90 KLH Corrected bcc.s -> bhi.s on 'divide will succeed' statements
; 12 Apr 90 JPO Replaced FSETXCP macro with faster, smaller code
;
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; MACROs for quickly accessing other PACK4 routines
;-----------------------------------------------------------
MACRO
BDFP68K
BSR FP020
ENDM
MACRO
BDMULI
MOVE.W #$2004,-(SP)
BSR FP020
ENDM
;-----------------------------------------------------------
;-----------------------------------------------------------
;; PROCEDURE Num2Dec(VAR f: decform;x: Extended;VAR d: decimal);
;; { d <-- x according to format f }
;; _____________________________________|
;; | |
;; | address of df (decform) |
;; |_____________________________________|
;; | |
;; | address of x (extended) |
;; |_____________________________________|
;; | |
;; | address decimal record |
;; |_____________________________________|
;; | |
;; | return address |
;; |_____________________________________|
;-----------------------------------------------------------
;-----------------------------------------------------------
BDOPW EQU 4*15 ; opword location in A6-relative stack frame
dcAdN2D EQU 4*15+2 ; address decimal record address
BDPOP3 EQU 14 ; # of operand bytes to pop prior to return
exAdN2D EQU dcAdN2D+4 ; extended address
dfAdN2D EQU exAdN2D+4 ; address decform record address
MaxDigs EQU 19
EnvSave EQU -2 ; address of Environment
Env2 EQU -4 ; overlaps with MISCRec
MISCRec EQU -12 ; MISC record (for Halt Handler)
XTemp EQU -16 ; Intermediate location for Extended
;-----------------------------------------------------------
; Main entry point for binary to decimal conversions. Restore
; stack and registers to pristine condition, then save state
; again in new fashion. Finally, get binary source format
; into D7.
;-----------------------------------------------------------
B2D:
movem.l (SP)+,A0-A4/D0-D7 ; restore registers
unlk A6 ; undo link
movem.l A0-A4/D0-D7,-(SP) ; save state again
link a6,#-16 ; reserve 16 bytes local stack frame
move.w BDOPW(A6),D7 ; D7.W <- opword
and.w #$3820,D7 ; isolate SRC format in D7.W
move.w (FPSTATE).W,D3 ; get caller's environment
move.w D3,EnvSave(A6) ; save it
and.w #$6000,D3 ; install environment that is default
move.w D3,(FPSTATE).W ; except for caller's rounding direction
move.l exAdN2D(a6),a3 ; save original source address in A3
lea XTemp(a6),a0 ; A0 <- addr of temp extended
move.l a0,exAdN2D(a6) ; move current dst to 'exAdN2D(a6)'
move.l a3,-(sp) ; push &SRC
move.l a0,-(sp) ; push &temp
MOVE.W d7,-(SP) ; move format to stack
add.W #FOZ2X,(SP) ; create opword on stack
BDFP68K ; convert to extended
TST.B D7 ; 96-bit extended in XTemp?
BEQ.S @1 ; no
MOVE.L XTemp+4(A6),XTemp+2(A6) ; yes; convert to 80-bit format
MOVE.L XTemp+8(A6),XTemp+6(A6) ; in place
@1:
movea.l dfAdN2D(a6),A1 ; get decform into D2
move.l (A1),D2
movea.l dcAdN2D(a6),A1 ; get address of decimal record
move.w (a0)+,d0 ; get sign and exponent from stack frame
clr.w (a1)
BCLR #15,D0 ; strip sign from exponent
beq.s @4
MOVE.b #1,(a1) ; set decimal sign
@4:
move.L (a0)+,d4 ; get high 32 bits of extended from memory
move.L (a0),d5 ; get low 32 bits of extended from memory
;-----------------------------------------------------------
; ENUM TYPE: SET DEFAULT
; STRING LENGTH TO 1, USEFUL JUST BELOW AND LATER WHEN
; SCALED FIXED-PT RESULT OVERFLOWS TO '?'.
;-----------------------------------------------------------
MOVE.B #1,4(A1) ; LENGTH TO 1
;-----------------------------------------------------------
; NOW PICK OUT NAN, INF, ZERO CASES...
;-----------------------------------------------------------
cmp.w #$7fff,d0 ; d0 - $7fff
bne.s @10 ; zero denorm or normal
move.l d4,d0
ANDI.L #$7FFFFFFF,D0 ; test fractional part of significand
bne.s @28 ; NaN found
tst.l d5
bne.s @28 ; NaN found
MOVEQ #'I',D0 ; Infinity found
@16:
MOVE.B D0,5(A1) ; SAVE 1-CHAR FIELD
BRA BDFIN ; GO TO END OF CONVERSION
;-----------------------------------------------------------
; CHECK FOR 0, INF, OR (GASP) AN HONEST NUMBER.
;-----------------------------------------------------------
@10:
TST.L D4 ; IF-SPECIAL-NUMBER
bne.S BD1 ; --> FINITE, NONZERO
TST.L D5 ; IF-SPECIAL-NUMBER
bne.S BD1 ; --> FINITE, NONZERO
MOVEQ #'0',D0 ; ASSUME IT'S ZERO
bra.s @16
@28:
bset #30,d4 ; test for signaling NaN while quieting
bne.s @29 ; quiet NaN found
bset #ERRI,(FPSTATE).W ; signaling NaN raises INVALID exception
;-----------------------------------------------------------
; PUT NXXXX... FOR 16 HEXITS OF A NAN, REGARDLESS OF FORMAT
; SINCE TRAILING ZEROS WILL BE STRIPPED LATER. NOTE THAT
; NAN STRUCT IS 22 BYTES LONG: 2 WORDS FOR SIGN AND EXP,
; AND 18 BYTES FOR LENGTH, N, AND 16 HEXITS.
;-----------------------------------------------------------
@29:
ADDQ.L #4,A1 ; POINT TO RESULT STRING
MOVE.B #17,(A1)+ ; LENGTH = N PLUS 2 SETS OF 8
MOVE.B #'N',(A1)+ ; FIRST CHAR
BSR.S @31 ; FIRST 8 HEXITS FROM D4
MOVE.L D5,D4 ; MOVE LOW 8 HEXITS
BSR.S @31 ; AND CONVERT
SUBA.W #22,A1 ; POINT TO HEAD OF STRUCT
BRA BDFIN
;-----------------------------------------------------------
; ROUTINE TO DISPLAY D4 IN 0-9, A-F.
;-----------------------------------------------------------
@31:
MOVEQ #8,D0 ; LOOP COUNT
@33:
ROL.L #4,D4 ; PRINT FROM HI TO LO
MOVEQ #$0F,D1 ; NIBBLE MASK
AND.B D4,D1 ; STRIP NIBBLE
OR.B #'0',D1 ; '0' IS $30
CMPI.B #'9',D1 ; HEX LETTER?
BLE.S @35
ADDQ.B #7,D1 ; TRANSLATE TO A-F
@35:
MOVE.B D1,(A1)+ ; STUFF CHARACTER
SUBQ.W #1,D0
BNE.S @33
RTS
;-----------------------------------------------------------
; NEED NORMALIZED FORM OF NUMBER (EVEN WHEN VALUE IS
; EXTENDED DENORMALIZED) IN ORDER TO COMPUTE
; FLOOR( LOG10 ( | X | ) ).
; AS EXPLAINED IN THE B-D PAPER, WE CAN APPROXIMATE
; LOG2 ( | X | ) BY EXP.FRAC .
; SO WE PUT THIS INFORMATION TOGETHER BEFORE STORING THE
; SIGNED EXTENDED VALUE AT THE TOP OF THE STACK FRAME (A3).
; FOR CONVENIENCE, THIS INFORMATION IS KEPT EVEN IN THE
; CASE OF FIXED CONVERSIONS, IN WHICH IT IS IRRELEVENT.
;-----------------------------------------------------------
BD1:
tst.l d4
bmi.s @2 ; x normal
@1:
subq.w #1,d0 ; normalize x
add.l d5,d5
addx.l d4,d4
bpl.s @1 ; x denormal
@2:
MOVE.L D4,D1 ; INTEGER-BIT.FRAC
MOVE.W D0,D1 ; EXP IN LOW WORD
SUBI.W #$3FFF,D1 ; UNBIAS EXP
SWAP D1 ; ALIGN EXP AND INT.FRAC
ADD.W D1,D1 ; FINALLY HAVE EXP.FRAC
MOVE.L #$4D104D42,D0 ; FLOOR( LOG10 (2) )
TST.L D1 ; EXP NEGATIVE?
BPL.S @7
ADDQ.W #1,D0 ; BUMP LOG TO ASSURE FLOOR
;-----------------------------------------------------------
; COMPUTE LOG10(2) * LOG2(X) INTO D4.W. THIS IS A 32*32
; SIGNED MULTIPLY SO CANNOT USE CORE ROUTINE OF THE MULT
; OPERATION. SINCE ONLY THE LEADING 16 BITS ARE OF
; INTEREST, IT IS NOT NECESSARY TO CARRY OUT THE LOW ORDER
; 16*16 PARTIAL PRODUCT. THE SCHEME IS:
;
; A B = D0 = FLOOR( LOG10 (2) ) > 0
; * X Y = D1 = FLOOR( LOG2 |X| )
; -------
; A--Y
; B--X
; + A--X
; ------------
; ???????? = D4.W, KEEPING ONLY 16 BITS
;-----------------------------------------------------------
@7:
MOVE.L D0,D4
SWAP D4 ; D4.W = A
MULU D1,D4 ; D4.L = A--Y
CLR.W D4
SWAP D4 ; D4.W = A--Y.HI
SWAP D1 ; D1.W = X
MOVE.W D1,D5
MULS D0,D5 ; D5.L = B--X
SWAP D5
EXT.L D5 ; D5.W = B--X.HI WITH SIGN
ADD.L D5,D4 ; CANNOT CARRY OR BORROW
SWAP D0 ; D0.W = A
MULS D1,D0 ; D0.L = A--X
ADD.L D0,D4
SWAP D4 ; D4.W = FLOOR(LOG10(X))
;-----------------------------------------------------------
; ADD 1 TO D4.W YIELDING THE NUMBER OF DIGITS LEFT OF THE
; DECIMAL POINT WHEN X IS WRITTEN OUT, A HANDY VALUE.
;-----------------------------------------------------------
;if (f.style = FloatDecimal) and (f.digits < 1) then f.digits := 1;
move.w d2,d1 ; contains decform.digits
bmi.s @8 ; negative
cmpi.w #$2000,d1 ; peg positive at 8K
blt.s @9
move.w #$2000,d1
bra.s @9
@8:
cmpi.w #$e000,d1 ; peg negative at -8K
bgt.s @9
move.w #$e000,d1
@9:
swap d2 ; contains decform.style
lsr.w #8,d2 ; word --> byte
tst.b d2 ; nonzero --> fixed
bne.s loop
tst.w d1
bgt.s loop ; f.digts >= 0
moveq #1,d1 ; f.digits := 1
loop:
addq.w #1,d4 ; logx := logx + 1
move.w d1,d5 ; len := f.digits
tst.b d2 ; nonzero --> fixed
beq.s @1
add.w d4,d5 ; len := len + (logx + 1)
@1:
cmpi.w #MaxDigs,d5 ; len - MaxDigs
ble.s @2
move.w #MaxDigs,d5 ; len := MaxDigs
@2:
move.w d5,2(a1) ; d.exp := len
sub.w d4,2(a1) ; d.exp := d.exp - (logx + 1)
tst.w d5
bgt.s @3
moveq #1,d5 ; len := 1
@3:
move.l exAdN2D(a6),-(sp)
move.l a1,-(sp)
jsr BIGX2S
cmp.b 4(a1),d5 ; len - length (d.sig)
bcs.s loop
neg.w 2(a1) ; d.exp := -d.exp
BDFIN:
move.w (FPSTATE).W,d0 ; current environment word
andi.w #$1f00,d0 ; current exceptions
or.w d0,EnvSave(A6) ; set current exceptions in saved environment
move.w EnvSave(A6),(FPSTATE).W ; restore updated saved environment
lsr.w #8,d0 ; align pending exceptions into halt position
and.w EnvSave(a6),d0 ; exceptions causing halts
beq.s NoHalts
move.l 4(a6),MISCRec+4(a6) ; original d0 saved on stack
swap d0 ; prepare pending CCR/halts
clr.w d0 ; (faking CCR = 0)
move.l d0,MISCRec(a6) ; pending halt exceptions
pea MISCRec(a6) ; push mischaltinfo record addr
move.l dfAdN2D(a6),-(sp) ; push src1 (decform) addr
move.l a3,-(sp) ; push src addr
move.l dcAdN2D(a6),-(sp) ; push dst (decimal) addr
move.w BDOPW(A6),-(sp) ; push opword
movea.l (FPHV).W,A0 ; get haltvector and jsr to user
jsr (A0) ; halt handler
NoHalts:
unlk a6
movem.l (sp)+,A0-A4/D0-D7 ; restore registers
move.l (sp),BDPOP3(sp) ; move rts address to proper location
lea BDPOP3(sp),sp ; clean up stack
move #0,CCR ; zero CCR
rts ; return
;-----------------------------------------------------------
;-----------------------------------------------------------
;; PROCEDURE Dec2Num(d: decimal, VAR x: Extended);
;; _____________________________________
;; | |
;; | d address decimal record |
;; |_____________________________________|
;; | |
;; | address of x (extended) |
;; |_____________________________________|
;; | |
;; | return address |
;; |_____________________________________|
;-----------------------------------------------------------
;-----------------------------------------------------------
dbopw EQU 4*15 ; opword location relative to A6
exAddr EQU 4*15+2 ; extended address
BDPOP2 EQU 10 ; # of operand bytes to pop prior to return
dcAddr EQU exAddr+4 ; address decimal record
MxDgs EQU 19 ; 'brain dead' limit for decimal point on decimal record
; input. Scheme is backwards compatible for previous
; routines
;-----------------------------------------------------------
; Main entry point for decimal to binary conversions. Restore
; stack and registers to pristine condition, then save state
; again in new fashion. Finally, get binary source format
; into D7.
;-----------------------------------------------------------
D2B:
movem.l (SP)+,A0-A4/D0-D7 ; restore registers
unlk A6 ; undo link
movem.l A0-A4/D0-D7,-(SP) ; save state again
link a6,#-16 ; reserve 16 bytes local stack frame
move.w DBOPW(A6),D7 ; D7.W <- opword
and.w #$3800,D7 ; isolate DST format in D7.W
beq.s QDEC2X ; extended (80- or 96-bit) format
cmp.w #$1000,D7
blt.s QDEC2D ; double
beq.s QDEC2S ; single
MOVE.W #$6000,d0 ; comp or integer: mask for rounding direction only
bra.s NoRound
QDEC2S: ; single precision DST
MOVE.W #$6040,d0 ; mask for rounding precision and direction
bra.s NoRound
QDEC2D: ; double precision DST
MOVE.W #$6060,d0 ; mask for rounding precision and direction
NoRound:
move.w (FPSTATE).W,D3 ; D3 <- caller's environment
move.w D3,EnvSave(A6) ; save it
and.w d0,d3
or.w #$0080,d3 ; special bit to tell rounding routine
; to set sticky bit but don't round since
; FOX2Z will do the proper rounding, i.e.,
; this gimmick avoids double rounding.
bra.s SetIt ; set new environment
QDEC2X: ; extended precision DST
MOVE.W #$6060,d0 ; mask for rounding precision and direction
move.w (FPSTATE).W,D3 ; D3 <- caller's environment
move.w D3,EnvSave(A6) ; save it
and.w d0,d3
SetIt:
move.w D3,(FPSTATE).W ; set new environment (caller's rounding
; direction/precision with no halts enabled)
drpad:
move.l exAddr(a6),a3 ; save original destination address in A3
cmpi.w #FFEXT,d7
beq.s @1
lea XTemp(a6),a0
move.l a0,exAddr(a6) ; move XTemp(a6) to 'exAdN2D(a6)'
@1:
move.l dcAddr(a6),A2 ; get address of decimal record
LEA 4(A2),A4 ; PTR TO STRING
;-----------------------------------------------------------
; CLEAR OUT DIGIT ACCUMULATOR AND INITIALIZE COUNTERS.
;-----------------------------------------------------------
CLR.L D4 ; DIGIT BUFFER
MOVE.L D4,D5
MOVE.B (A4)+,D6 ; DIGIT STRING LENGTH COUNT
BEQ.S DBZSTO ; ZERO LENGTH --> 0.0
;-----------------------------------------------------------
; GET FIRST CHARACTER BUT DON'T AUTOINCREMENT.
;-----------------------------------------------------------
MOVE.B (A4),D0 ; FIRST CHAR
;-----------------------------------------------------------
; CHECK FOR 'I' -- INFINITY.
;-----------------------------------------------------------
CMPI.B #$49,D0 ; IS IT 'I'?
BEQ.S DBNFIN
;-----------------------------------------------------------
; CHECK FOR 'N', IF SO GET HEXITS FOR SIGNIFICAND. IF THERE
; ARE FEWER THAN THREE, FORCE LEAD ZEROS.
;-----------------------------------------------------------
CMPI.B #'N',D0 ; ALLOW ONLY CAPITAL N
BNE.S DBZER
MOVE.B -1(A4),D2 ; CHARACTER COUNT
ADDQ.L #1,A4 ; POINT TO FIRST HEXIT
SUBQ.B #1,D2 ; DON'T COUNT 'N'
MOVEQ #8,D0 ; ASSUME 8 DIGITS
CMPI.B #4,D2 ; OK IF AT LEAST 4
BGE.S @31
SUBQ.B #4,D0 ; FOUR 0'S AND WHAT'S THERE
ADD.B D2,D0
@31:
BSR.S @35
MOVE.L D5,D4
CLR.L D5
MOVEQ #8,D0
BSR.S @35
BRA.S @39
;-----------------------------------------------------------
; ROUTINE TO GET D0 DIGITS TO D5, UP TO COUNT IN D2
;-----------------------------------------------------------
@35:
ROL.L #4,D5 ; ALIGN BITS SO FAR
SUBQ.B #1,D2 ; DEC STRING COUNT
BMI.S @37
MOVE.B (A4)+,D1
CMPI.B #'9',D1
BLE.S @36
ADDI.B #9,D1 ; TRUE NIBBLE VALUE
@36:
ANDI.B #$0F,D1 ; NIBBLE MASK
OR.B D1,D5
@37:
SUBQ.W #1,D0
BNE.S @35
RTS
;-----------------------------------------------------------
; CLEAR IRRELEVANT LEAD BIT AND TEST FOR ANYTHING NONZERO.
;-----------------------------------------------------------
@39:
bset #QNANBIT,D4 ; make it a quiet NaN
move.l D4,D0
swap D0
andi.b #$FF,D0 ; test for zero NaN code
bne.s DBNFIN ; nonzero code; done
ori.w #NANZERO,D0 ; insert special NaN code
swap D0
move.l D0,D4
;-----------------------------------------------------------
; SET EXPONENT FOR INF/NaN IN D0
;-----------------------------------------------------------
DBNFIN:
MOVE.W #$7FFF,D0 ; STORE HUGE EXP
BRA.S DBSSTO
;-----------------------------------------------------------
; GET HERE IF ALL DIGITS ZERO: FORCE SIGNED 0 AND STORE
;-----------------------------------------------------------
DBZER:
CMPI.B #$30,D0 ; IS IT '0'?
BNE.S SIGDIGS
DBZSTO:
CLR.L D0
DBSSTO:
;-----------------------------------------------------------
; DECIMAL.SGN ENUM TYPE TEST USES HI BYTE ONLY
;-----------------------------------------------------------
TST.B (A2) ; CHECK OPERAND SIGN
BEQ.S @1
BSET #15,D0
@1:
move.l exAddr(a6),A0
MOVE.W D0,(A0)+
MOVE.L D4,(A0)+
MOVE.L D5,(A0)
bra.s NoDigs
BrnDd: ;save exponent, adjust exponent, call bigd2x, restore exponent
move.w 2(a2),-(sp) ; save original decimal.exp
moveq #0,d0
move.b d6,d0
subi.w #MxDgs,d0
sub.w d0,2(a2) ; adjust decimal.exp for brain dead 19 digit max
move.l exAddr(a6),-(sp)
move.l dcAddr(a6),-(sp)
jsr BIGD2X
move.w (sp)+,2(a2) ; restore original decimal.exp
bra.s NoDigs ; normal finish
SIGDIGS:
cmpi.b #MxDgs,d6
bhi.s BrnDd
move.l exAddr(a6),-(sp)
move.l dcAddr(a6),-(sp)
jsr BIGD2X
NoDigs:
cmpi.w #FFEXT,d7 ; if non-extended DST, convert
bgt.s @1
BTST #FPX96,dbopw+1(A6) ; if 80-bit extended DST, result
BEQ.S @3 ; is already delivered
MOVE.L 6(A3),8(A3) ; if 96-bit extended DST, convert
MOVE.L 2(A3),4(A3) ; in place
BRA.S @3
@1:
pea XTemp(a6) ; non-extended DST requires conversion
move.l a3,-(sp) ; put address of dest onto stack
MOVE.W d7,-(SP) ; move op to stack
add.W #FOX2Z,(SP) ; move op to stack
BDFP68K ; convert to DST format
move.w (FPSTATE).W,D0 ; Get current environment
btst #8,D0 ; INVALID exception?
beq.s @2 ; no
andi.w #$E1FF,D0 ; yes. clr spurious exceptions
bra.s @4
@2:
cmpi.w #FFSGL,D7 ; integer or comp DST?
ble.s @4 ; no. single or double
bclr #ERRU+8,D0 ; yes. clr underflow
bra.s @4
@3:
move.w (FPSTATE).W,D0 ; extended DST: D0 <- environment
@4:
andi.w #$1f00,D0 ; current exceptions
or.w D0,EnvSave(A6) ; set current exceptions in saved environment
move.w EnvSave(A6),(FPSTATE).W ; restore updated saved environment
lsr.w #8,d0 ; align pending exceptions into halt position
and.w EnvSave(a6),D0 ; exceptions causing halts
beq.s NoHlts
move.l 4(a6),MISCRec+4(a6) ; original d0 saved on stack
swap d0 ; prepare pending CCR/halts
clr.w d0 ; (faking CCR = 0)
move.l d0,MISCRec(a6) ; save in mischaltinfo record
pea MISCRec(a6) ; push mischaltinfo record addr
move.l dcAddr+4(A6),-(sp) ; push (nonexistent) SRC2 addr
move.l dcAddr(a6),-(sp) ; push SRC (decimal record) addr
move.l a3,-(sp) ; push DST addr
move.w dbopw(A6),-(sp) ; push opword
movea.l (FPHV).W,A0 ; get haltvector and jsr to
jsr (a0) ; user halt handler
NoHlts:
unlk a6
movem.l (sp)+,A0-A4/D0-D7 ; restore registers
move.l (sp),BDPOP2(sp) ; move rts address to proper location
lea BDPOP2(sp),sp ; clean up stack
move #0,CCR ; clr CCR
rts ; return
BIGD2X:
bgSz equ 784 ; 780.1=(256*21)*(Ln(5)/Ln(2)/16)
lclSz equ 4*bgSz+160
ParamSize EQU 8 ; size of all the passed parameters
MinSpace EQU 3316 ; minimum stack space in bytes
rtnAd EQU 4*13 ; rtnAd(a6) contains the return address
s EQU rtnAd+4 ; s(a6) contains the address of string or decimal record
x EQU s+4 ; x(a6) contains the address of x
AAdr equ -4 ; contains the address of aReg
BAdr equ AAdr-4 ; contains the address of bReg
CAdr equ BAdr-4 ; contains the address of cReg
DAdr equ CAdr-4 ; contains the address of dReg
EAdr equ DAdr-4 ; contains the address of Environment
xreg equ EAdr-4 ; xReg(A6) address of xReg
areg equ xreg-16 ; aReg(A6) address of aReg
MoveM.L A1-A4/D0-D7,-(SP)
link a6,#-160
movea.L s(a6),a0
addq #2,a0
move.w (a0),D0 ; hi part of D0 never used!!
blt SigDiv
bsr SizeExt
bsr a2b
bsr StoD
move.L DAdr(A6),CAdr(A6)
bsr cXb2a
movea.L a1,a3
move.w #$4f,d6 ; 63 + 16
move.w d1,d2
movea.l a3,a2
@1:
subi.w #16,d6 ; find first non zero word
move.w (a2)+,d0
bne.s @3 ; found first non zero word
dbra d2,@1
@2:
addq.w #1,d6 ; find first bit
lsl.w #1,d0
@3:
bpl.s @2 ; location of first bit not found
moveq #-1,d5 ; d5 < 0 => decimal to binary
bsr Roundit ; Roundit likes address of register in a3
move.w d1,d7
asl.w #4,d7 ; times 16 = number of bits
movea.L s(a6),a0
addq #2,a0
add.w (a0),d7
addi.w #$400e,d7 ; add extended bias
bsr.s StuffIt
tst.l d4
bmi.s @5
@4:
subq.w #1,d7 ; decrement exponent
asl.l #1,d6
roxl.l #1,d5
roxl.l #1,d4
bpl.s @4
@5:
move.l x(a6),a0
cmp.w #$7fff,d7 ; d7 - $7fff
bcc.s OverFlo
asl.w #1,d7
PICont:
asl.w (a0) ; put sign bit in 'X'
roxr.w #1,d7 ; put sign into exponent word
move.w d7,(a0)+ ; put sign and exponent into memory
move.L d4,(a0)+ ; put high 32 bits of extended into memory
move.L d5,(a0) ; put low 32 bits of extended into memory
bra DoneB2X
OverFlo:
or.w #ERRWXO,(FPSTATE).W ; signal INEXACT and OVERFLOW
MOVE.W (FPSTATE).W,D2 ; D2 <- environment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BTST #14,D2 ; bit for +/-infinity rounding directions
BNE.S DnOrTZ ; DOWNWARD or TOWARDZERO
BTST #13,D2
BEQ.S RtrnInf ; TONEAREST
MOVE.L x(A6),A4 ; UPWARD
MOVE.W (A4),D2 ; contains sign and biased exponent
BPL.S RtrnInf ; + & UPWARD, bumpit
BRA.S RtrnMax ; - & UPWARD, don't bumpit
DnOrTZ:
BTST #13,D2 ; DOWNWARD or TOWARDZERO
BNE.S RtrnMax ; TOWARD-ZERO
MOVEA.L x(A6),A4 ; x(A6) contains the address of x
MOVE.W (A4),D2 ; contains sign and biased exponent
BMI.S RtrnInf ; - & DOWNWARD, bumpit
; + & DOWNWARD, don't bumpit
RtrnMax:
move.w #$fffc,d7 ; return Max
moveq #-1,d4
moveq #-1,d5
bra.s PICont
RtrnInf:
move.w #$fffe,d7 ; return infinity
moveq #0,d4
moveq #0,d5
bra.s PICont
StuffIt:
moveq #0,d4 ; clear significand
moveq #0,d5 ; clear significand
moveq #0,d6 ; clear significand
tst.w d1
bgt.s @4
move.w (a3)+,d4 ; 1 significant word
swap d4
rts
@4:
move.l (a3)+,d4 ; 2 or more significant words
subq.w #2,d1 ; d1 - 2
bgt.s @5
blt NormIt
move.w (a3)+,d5 ; 3 significant words
swap d5
rts
@5:
move.l (a3)+,d5 ; 4 or more significant words
subq.w #2,d1 ; d1 - 2 (d1 - 4)
bgt.s @6
blt NormIt
move.w (a3)+,d6 ; 5 significant words
swap d6
rts
@6:
move.l (a3)+,d6 ; 6 or more significant words
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DivWS:
move.w d1,d4 ; d4 := max {d1, d3} <klh 11apr90>
cmp.w d3,d1 ; d1 - d3 <klh 11apr90>
ble.s @1 ; d1 <= d3 <klh 11apr90>
move.w d3,d4 ; d4 := max {d1, d3} <klh 11apr90>
@1:
tst.w d2 ; <klh 11apr90>
beq.s RtDvWS ; only one word in A1 & A3 <klh 11apr90>
lea 2(a1),a2 ; <klh 11apr90>
lea 2(a3),a4 ; <klh 11apr90>
moveq #0,d2 ; <klh 11apr90>
@2:
addq.w #1,d2 ; <klh 11apr90>
cmp.w d2,d1 ; d1 - d2 <klh 11apr90>
bge.s @3 ; d1 >= d2, don't clear (a2) <klh 11apr90>
clr.w (a2) ; <klh 11apr90>
@3:
cmp.w d2,d3 ; d3 - d2 <klh 11apr90>
bge.s @4 ; d3 >= d2, don't clear (a4) <klh 11apr90>
clr.w (a4) ; <klh 11apr90>
@4:
cmpm.w (a4)+,(a2)+ ; <klh 11apr90>
bne.s RtDvWS ; <klh 11apr90>
cmp.w d2,d4 ; d4 - d2 <klh 11apr90>
bgt.s @2 ; d4 > d2, keep looking <klh 11apr90>
bra.s RtDvWS ; note: condition code will be Z, values identical
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SigDiv:
bsr SizeExtN
bsr StoD ; D3.w = # words - 1 in (A3)
bsr Nrmlz10 ; D7.w = # bits to the right of (A1) word
movea.l a4,a3 ; a4 set by StoD
moveq #4,d6 ; D6.W + 1 = # of DIVU steps required
CMP.W (A3),D0 ; DO - (A3)
BEQ.S DivWS ; need further investigation <klh 11apr90>
RtDvWs:
BHI.S @1 ; Divide will succeed, no padding necessary <11apr90>
ADDQ.w #1,D3 ; append a zero word to A3
CLR.W -(A3)
moveq #5,d6 ; D6.W + 1 = # of DIVU steps required
@1:
move.w d3,d2
asl.w #4,d2 ; times 16
sub.w d7,d2
addi.w #$3FFE,d2 ; add extended bias
move.w d2,-(SP) ; (d3*16 - d7) + $3FFE -> stack
bsr BgnDiv
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; MakeX transforms contents of software register in a3 (d1.w = length)
; into and extended value in D0.W (sign & exponent) &
; D4/D5 (64-bit significand).
; first the register is rounded and checked for overflow and underflow
;
; find leading significant bit
;
moveq #0,d6
move.w (a3),d0
bra.s @3
@2:
addq.w #1,d6 ; find first bit
lsl.w #1,d0
@3:
bpl.s @2 ; location of first bit not found
move.w (SP)+,d7
move.l d7,a1 ; Roundit DOESN'T USE a1
sub.w d6,d7 ; if negative, necessary '0' bits appended to left
bmi.s Dnrml
addi.w #63,d6
moveq #-1,d5 ; d5 < 0 => decimal to binary
bsr Roundit ; Roundit likes address of register in a3
move.l a1,d7
bsr StuffIt
NormIt:
tst.l d4
bmi.s @2
@1:
subq.w #1,d7 ; decrement exponent
asl.l #1,d6
roxl.l #1,d5
roxl.l #1,d4
bpl.s @1
@2:
move.l x(a6),a0
asl.w #1,d7
asl.w (a0) ; put sign bit in 'X'
roxr.w #1,d7 ; put sign into exponent word
move.w d7,(a0)+ ; put sign and exponent into memory
move.L d4,(a0)+ ; put high 32 bits of extended into memory
move.L d5,(a0) ; put low 32 bits of extended into memory
bra DoneB2X
NeedWrd:
tst.w d0
bge.s @1
clr.w -(a3)
addq #1,d1
addi.w #16,d6
subi.w #16,d7
addi.w #16,d0
@1:
rts
Dnrml:
move.l a1,d0
cmpi.w #-66,d0 ; iscale(a6) - (-66)
bpl.s @0 ; not an extreme denormalized case
move.w #-66,d7
move.w d7,d0
sub.w d6,d7
@0:
add.w d7,d6
bsr.s NeedWrd
bsr.s NeedWrd
bsr.s NeedWrd
bsr.s NeedWrd
bsr.s NeedWrd
addi.w #63,d6
move.w d0,-(SP)
moveq #-1,d5 ; d5 < 0 => decimal to binary
movea.l a3,a1 ; save a3
bsr Roundit ; Roundit likes address of register in a3
movea.l a1,a3 ; restore a3
move.w (SP)+,d7
bsr StuffIt
tst.l d4
bmi.s @2
@1:
tst.w d7
ble.s @2 ; min exponent, no further normalization
subq.w #1,d7 ; decrement exponent
asl.l #1,d6
roxl.l #1,d5
roxl.l #1,d4
bpl.s @1
@2:
move.l x(a6),a0
asl.w #1,d7
asl.w (a0) ; put sign bit in 'X'
roxr.w #1,d7 ; put sign into exponent word
move.w d7,(a0)+ ; put sign and exponent into memory
move.L d4,(a0)+ ; put high 32 bits of extended into memory
move.L d5,(a0) ; put low 32 bits of extended into memory
; Note: any power of ten that turns a pascal string into a denormalized
; number will not do it exactly. Therefore it is not necessary to check
; for inexact. All denormalized results cause underflow in Decimal to Binary
tst.L d4
bmi.s noUF ; not denormalized
OR.W #ERRWXU,(FPSTATE).W ; subnormal: signal INEXACT and UNDERFLOW
noUF:
bra DoneB2X
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
StoD:
moveq #0,d3 ; initialize length
move.L DAdr(A6),A3
subq.l #2,a3 ; starting address of integer
clr.w (A3)+ ; clear initial word for integer
MOVE.L s(A6),A0 ; s(A6) contains the address of decimal record
MOVE.L x(A6),A4 ; address of x for directed roundings
;-----------------------------------------------------------
; DECIMAL.SGN ENUM TYPE TEST USES HI BYTE ONLY
;-----------------------------------------------------------
TST.B (A0) ; CHECK OPERAND SIGN
BEQ.S @10
BSET.B #7,(a4) ; store decimal.sig in x for directed roundings
bra.s @11
@10:
BCLR.B #7,(a4) ; store decimal.sig in x for directed roundings
@11:
addq.L #4,A0 ; address of decimal.sig
moveq #0,d0
move.b (a0)+,d0 ; length of string
bra.s @3
;
; Loop through string, successively multiplying by 10 and adding
; in the low nibble of the character.
;
@1:
MOVEQ #$0F,D7
AND.B (A0)+,D7 ; GET LOW NIBBLE
movea.l a3,a4
move.w d3,d4
@2:
move.w -(a4),d6 ; get word for multiply
mulu #10,d6
add.l d6,d7 ; add previous overflow or decimal digit
move.w d7,(a4) ; move word back into memory register
clr.w d7 ; clear non overflow part
swap d7 ; put overflow part in low word
dbra d4,@2 ; condition codes not affected
beq.s @3
move.w d7,-(a4) ; add new word to memory register
addq.w #1,d3
@3:
dbra d0,@1
rts
;-----------------------------------------------------------
;-----------------------------------------------------------
; Binary to String routines modified from JTC's BINDEC.a
; procedure BigX2S (var x: extended; iscale: integer; var s: string);
;
;;
;;
;; _________________________________________________________________
;;
;; after 'Link' instruction the stack should look like:
;;
;; _____________________________________
;; | |
;; | address of x (extended)|
;; |_____________________________________|
;; | |
;; | s address decimal record |
;; |_____________________________________|
;; | |
;; | return address |
;; |_____________________________________|
;; | |
;; | address (20 bytes) |
;; |_____________________________________|
;; | |
;; | xreg (12 bytes) |
;; |_____________________________________|
;; | |
;; | areg (1456 bytes) |
;; |_____________________________________|
;; | |
;; | breg (728 bytes) |
;; |_____________________________________|
;; | |
;; | creg (728 bytes) |
;; |_____________________________________|
;; | |
;; | dreg (108 bytes) |
;; |_____________________________________|
;;
;;_________________________________________________________________
;;
;-----------------------------------------------------------
;-----------------------------------------------------------
BIGX2S:
MoveM.L A1-A4/D0-D7,-(SP)
link a6,#-160
movea.L s(a6),a0
addq #2,a0
move.w (a0),D0 ;hi part of D0 never used!!
ble.s DivCase
bsr SizeExt
bsr XbyA
bra.s MStr
DivCase:
bsr SizeExtN
bsr GetX
bsr DivX
moveq #0,d5 ; d5 >= 0 => binary to decimal
bsr Roundit
MStr:
bsr.s MakeStr
Done:
UNLK A6 ; destroy the link
MoveM.L (SP)+,A1-A4/D0-D7 ; restore registers & return address
MOVEA.L (SP)+,A0 ; pull off the return address
LEA ParamSize(SP),SP ; strip all of the caller's parameters
JMP (A0) ; return to the caller
DoneB2X: ; special backend to check for rounding precision
move.w (FPSTATE).W,D4 ; D4 <- environment
and.w #$0060,d4 ; check for rounding precision
beq.s Done
pea Tbl1 ; address of integer value 1
MOVE.L x(a6),-(SP) ; push address of extended
BDMULI ; forces rounding precision control
bra.s Done
MakeStr:
movea.L CAdr(A6),A1 ; initialize bcd string address
CLR.L (A1) ; initialize string
MOVEQ #0,D3 ; initialize bcd string length
TST.W D6
MOVE.L A1,A2
BMI.s RtnZero
ADDQ.W #1,D1
BinLoop:
MOVEQ #15,D5
CMP.W D5,D6 ; D6 - D5
BGE.S @2
MOVE.W D6,D5
@2:
SUBQ.W #1,D1 ; decrement number of words remaining
BLT.S bcdAddr
MOVE.W (A3)+,D2
bcdAddr:
MOVE.L A1,A2 ; reset bcd string address
ADD.W D2,D2 ; generate X if appropriate
MOVE.L D3,D7
LEA 4(A2),A2
@5:
MOVE.L -(A2),D0 ; put long word of string into D0
ABCD D0,D0
ROR.L #8,D0
ABCD D0,D0
ROR.L #8,D0
ABCD D0,D0
ROR.L #8,D0
ABCD D0,D0
ROR.L #8,D0
MOVE.L D0,(A2) ; put it back
DBRA D7,@5 ; end of long word string loop
MOVEQ #0,D0
ABCD D0,D0
BEQ.S @6 ; no X condition found
ADDQ.L #1,D3 ; add new long word to string
MOVE.L D0,-(A2)
@6:
DBRA D5,bcdAddr
SUBI.W #16,D6 ; number of binary digits - 1 remaining
BGE.S BinLoop
MOVE.L s(A6),A0 ; s(A6) contains the address of decimal record
addq #4,a0 ; address of string
LEA 1(A0),A1 ; pointer to string of characters
;
; The hard part is delivering the ascii digits, stripping leading
; zeros. Use D6 as a marker: if minus then skipping leading zeros; if
; plus print any digit.
;
@7:
BSR.S BD1OUT
BSR.S BD1OUT
BSR.S BD1OUT
BSR.S BD1OUT
DBRA D3,@7 ; end of long word string loop
;
; Finally, stuff the string length, restore the registers, and exit.
; A0 points to the length byte, A1 points to the next character after
; the string; so (A1 - A0 - 1) is the true length.
;
MOVE.W A1,D0
SUB.W A0,D0
SUBQ.B #1,D0
BEQ.S RtnZero
MOVE.B D0,(A0)
RTS
RtnZero:
MOVE.L s(A6),A0 ; s(A6) contains the address of decimal record
addq #4,a0
MOVE.W #$0130,(A0) ; Return the following string, '0'
RTS
;
; Utility routine to print two digits from nibbles in D1, skipping
; leading zeros, according to sign of D6. Recall that ascii digits
; have the hex form $3x.
;
BD1OUT:
MOVEQ #0,D1
MOVE.B (A2)+,D1
ROR.W #4,D1 ; ALIGN D1: $0000L00H
BSR.S BD1DIGOUT
ROL.W #4,D1 ; ALIGN D1: $0000000L
; FALL THROUGH AND RETURN
;
; Place a character from D1.B into the string (A1).
; Skip a leading 0 if D6.W is negative.
;
BD1DIGOUT:
TST.W D6
BPL.S BDDOIT
TST.B D1 ; 0 NIBBLE?
BEQ.S BDSKIPIT
MOVEQ #0,D6 ; MARK NONZERO FOUND
BDDOIT:
ORI.B #$30,D1 ; MAKE ASCII DIGIT
MOVE.B D1,(A1)+
SUB.B D1,D1 ; CLEAR FOR NEXT ROTATE
BDSKIPIT:
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
XbyA:
movea.L s(a6),a0
addq #2,a0
move.w (a0),D6 ; d.exp
MOVE.L x(A6),A0 ; x(A6) contains the address of x
MOVE.W (A0)+,D7 ; contains sign and biased exponent
BCLR.L #15,D7 ; test and clear sign bit
SUBI.W #$403f,D7 ; subtract extended (bias + 64)
ADD.W D6,D7 ; location of binary point to the right
MOVE.W (A0)+,D3 ; contains 1st word of significand
MOVE.W (A0)+,D4 ; contains 2nd word of significand
MOVE.W (A0)+,D5 ; contains 3rd word of significand
MOVE.W (A0)+,D6 ; contains 4th word of significand
movea.L AAdr(A6),A3
adda.L #16,A3
movea.L AAdr(A6),A1
subq.L #2,A1
movea.L CAdr(A6),A4
subq.L #2,A4 ; use this as a scratch location to assist ADDX
MOVE.W (A1),D0
MOVE.W D0,D2
MULU D6,D2
MOVE.L D2,(A3) ; A3
MOVE.W D0,D2
MULU D4,D2
MOVE.L D2,-(A3) ; A3-4
CLR.w -(A3) ; A3-6 clears high word
LEA 4(A3),A3 ; A3-2
MOVE.W D0,D2
MULU D5,D2
ADD.L D2,(A3) ; A3-2
MULU D3,D0
MOVE.L D0,(A4)+ ; Silly but necessary
ADDX.L -(A4),-(A3) ; A3-6
MOVE.L D1,A2 ; save length & use D1 as loop counter
SUBQ.W #1,D1
BMI.s @2
@1:
LEA 4(A3),A3 ; A3-2
MOVE.W -(A1),D0
MOVE.W D0,D2
MULU D6,D2
ADD.L D2,(A3) ; A3-2
MOVE.W D0,D2
MULU D4,D2
MOVE.L D2,(A4)+ ; Silly but necessary
ADDX.L -(A4),-(A3) ; A3-6
bcc.s @98
MOVE.w #1,-(A3) ; A3-8 clears high word
BRA.S @99
@98:
MOVE.w #0,-(A3) ; A3-8 clears high word
@99:
LEA 4(A3),A3 ; A3-4
MOVE.W D0,D2
MULU D5,D2
ADD.L D2,(A3) ; A3-4
MULU D3,D0
MOVE.L D0,(A4)+ ; Silly but necessary
ADDX.L -(A4),-(A3) ; A3-8
DBRA D1,@1
@2:
MOVE.L A2,D1 ; restore length of loop
ADDQ.w #4,D1 ; number of words
MOVE.W D1,D6
ADDQ.w #1,D6
ASL.W #4,D6 ; # bits = 16 * # words
ADD.W D7,D6 ; # number of integer bits
moveq #0,d5 ; d5 >= 0 => binary to decimal
Roundit:
TST.W D6
bge.s @2 ; at least one digit to the left of binary point
;
; Make sure at least one digit is to the left of the binary point
;
cmpi.w #-66,d6 ; d6 + 66
bpl.s @1
move.w #-66,d6
@1:
clr.w -(a3) ; add zero word
addQ.w #1,D1 ; adjust length (number of words)
addI.W #16,D6 ; number - 1 of bits left of the binary point
blt.s @1 ; no digits to the left of binary point
@2:
BSR RndStk
TST.W D5
BEQ.s @8 ; no round or sticky => no rounding
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Determines if least significant bit is set
; sets lsb of D5 if lsb of significand is set
; D3 = bit location in word (A2) of lsb
MOVE.W D6,D2
LSR.W #4,D2 ; divide by 16, (round bit word location)
LSL.W #1,D2 ; multiply by 2, to obtain byte offset
LEA 0(A3,D2.W),A2
MOVE.W (A2),D4 ; put word into D reg for BTST
MOVE.W D6,D3 ; # of bits in significand
ANDI.W #15,D3 ; location of round bit within word
MOVE.W #$8000,D0 ; initialize mask
ROR.W D3,D0 ; align mask to round bit
AND.W D0,D4
BEQ.S @3 ; least significant bit clear
ADDQ.W #1,D5
@3:
bset #ERRX,(FPSTATE).W ; signal INEXACT
move.w (FPSTATE).W,D2 ; D2 <- environment
move.w d2,d4
and.w #$00e0,d4 ; check for rounding precision or type coercion '0080'
beq.s @91
or.W D0,(A2) ; or in sticky bit
bra.s @8
@91:
BTST #14,D2 ; bit for +/-infinity rounding directions
BNE.S @10 ; DOWNWARD or TOWARDZERO
BTST #13,D2
BNE.S @11 ; UPWARD
CMP.W #5,D5 ; D5 - 5
BLT.S @8 ; no rounding
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Increment the significand (BumpIt)
@4:
ADD.W D0,(A2) ; test for least significant bit
BCC.S @8
@5:
cmpa.L A2,A3
beq.s @6 ; register overflowed
ADDQ.W #1,-(A2)
BCS.S @5
bra.s @8
; MAKE SURE THIS GETS EXCERCISED ALL FOUR WAYS (bin <--> dec & mul | div)
@6:
move.w #1,-(a3) ; add overflow word
addQ.w #1,D1 ; adjust length (number of words)
addI.W #16,D6 ; number - 1 of bits left of the binary point
lea 16(a1),a1 ; for DivD case
@8:
TST.W (A3)
BNE.S @9
LEA 2(A3),A3 ; location of first word - 1
SUBQ #1,D1 ; number of words
SUBI.W #16,D6 ; number - 1 of bits left of the binary point
@9:
RTS
@10:
BTST #13,D2 ; DOWNWARD or TOWARDZERO
BNE.S @8 ; TOWARD-ZERO
MOVE.L x(A6),A4 ; x(A6) contains the address of x
MOVE.W (A4),D2 ; contains sign and biased exponent
BMI.S @4 ; - & DOWNWARD, bumpit
BRA.S @8 ; + & DOWNWARD, don't bumpit
@11:
MOVE.L x(A6),A4 ; UPWARD
MOVE.W (A4),D2 ; contains sign and biased exponent
BPL.S @4 ; + & UPWARD, bumpit
BRA.S @8 ; - & UPWARD, don't bumpit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Determines if round or sticky bits exist
; D5 = 0 => no round & no sticky
; D5 = 2 => no round & sticky
; D5 = 4 => round & no sticky
; D5 = 6 => round & sticky
RndStk:
clr.w d5 ; initialize D5
MOVE.W D6,D3 ; # of bits in significand
ADDQ.W #1,D3 ; round bit location w.r.t. left of significand
MOVE.L D3,D4
LSR.W #4,D4 ; divide by 16, (round bit word location)
CMP.W D1,D4 ; D4 - D1
BLE.S @1
RTS ; no round or sticky bits, no rounding
@1:
ANDI.W #15,D3 ; location of round bit within word
MOVE.W #$8000,D0 ; initialize mask
ROR.W D3,D0 ; align mask to round bit
MOVE.W D4,D2
LSL.W #1,D2 ; multiply by 2, to obtain byte offset
LEA 0(A3,D2.W),A2
AND.W (A2),D0 ; test for round bit
BEQ.S @2 ; no round bit found
MOVEQ #4,D5
@2:
MOVE.W #$7FFF,D0 ; initialize mask
LSR.W D3,D0 ; align mask to sticky bits
AND.W (A2)+,D0 ; test for sticky bits
BEQ.S @5 ; sticky bits not found yet
@3:
ADDQ.W #2,D5
RTS
@4:
TST.W (A2)+
BNE.S @3 ; sticky bit found
@5:
ADDQ.W #1,D4
CMP.W D1,D4 ; D4 - D1
BLE.S @4 ; keep looking for sticky bits
RTS ; no sticky bits found
GetX:
MOVE.L x(A6),A0 ; x(A6) contains the address of x
MOVE.W (A0)+,D6 ; contains sign and biased exponent
BCLR.L #15,D6 ; test and clear sign bit
SUBI.W #$400C,D6 ; adjusted location of binary point
movea.L DAdr(A6),A3
suba.w #52,A3
MOVE.L (A0)+,(A3) ; get significand of extended
MOVE.L (A0)+,4(A3) ; get significand of extended
MOVEQ #3,D3 ; initial length of D3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Extended input located at (A3), length of D3 + 1 words,
; binary point D6 bits to the right of word (A3)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; We must first normalize the power of ten.
; Then align extended value such that the last 16 bit divide will
; yield the round bit in the least significant bit.
; Then do the divide.
Nrmlz10:
MOVE.W D1,D7
ASL.W #4,D7 ; times 16
movea.L s(a6),a0
addq #2,a0
SUB.W (A0),D7 ; location of binary point to the right
; of first word (address (A1))
MOVE.W D1,D5 ; get word displacement
ASL.W #1,D5 ; set byte displacement, save for FinDiv
MOVE.W (A1),D0 ; get most significant word of divisor
BMI.S @3 ; power of ten normalized
@1:
SUBQ.W #1,D7 ; adjust binary point
MOVE.W D1,D4 ; set counter
LEA 2(A1,D5.W),A0
MOVE.W #0,CCR ; clear 'X'
@2:
ROXL.W -(A0) ; normalize power of ten
DBRA D4,@2
BPL.S @1 ; power of ten still not normalized
MOVE.W (A1),D0 ; get most significant word of divisor
@3:
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Power of ten located at (A1), length of D1 + 1 words,
; binary point D7 bits to the right of word (A1)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DivX:
SUB.W D7,D6 ; # bits to generate
BLE noDiv
MOVE.W D6,D4
ASR.W #4,D6 ; D6.W + 1 = # of DIVU steps required
ANDI.W #15,D4 ; # of shifts necessary to align final answer
BEQ.S BeginD
ADDQ.w #1,D3 ; append a zero word to A3
CLR.W -(A3)
SUBQ #1,D4 ; adjust counter for DBRA below
@4:
ASL.W 8(A3)
ROXL.W 6(A3)
ROXL.W 4(A3)
ROXL.W 2(A3)
ROXL.W (A3)
DBRA D4,@4
BRA.S BgnDiv
BeginD:
CMP.W (A3),D0 ; DO - (A3)
BHI.S @1 ; Divide will succeed, no padding necessary <klh 11apr90>
ADDQ.w #1,D3 ; append a zero word to A3
CLR.W -(A3)
BRA.S BgnDiv
@1:
SUBQ #1,D6 ; D6.W + 1 = # of DIVU steps required
BgnDiv:
BSR.s PadIt
MOVEA.L A3,A0
DivLoop:
MOVE.L (A0),D2 ; Address of quotient
DIVU D0,D2
BVS divOver
SWAP D2
MOVE.L D2,(A0) ; put result and rem back into (A3)
SWAP D2 ; used in FinDiv
LEA 2(A0),A0
CMPI.W #2,D5 ; byte offset of last word of divisor
BLT.S CtnDv
BEQ.S OneDiv
MOVE.W D5,D7 ; number of bytes - 2
BCLR.L #1,D7
BSR.s MandS
MOVEQ #0,D4
MOVE.W -(A4),D1
SUBX.W D4,D1
MOVE.W D1,(A4) ; 'C' Cleared, 'X' not affected.
NEGX.W D7 ; test 'X' bit, 'X' - D7
TST.W D7
BNE.S OneDiv ; no 'X' bit
BSR.s DecrIt
OneDiv:
MOVE.W D5,D7
BTST.L #1,D7
BNE.S @1
SUBQ.W #2,D7
@1:
BSR.S MandS
NEGX.W D7 ; test 'X' bit, 'X' - D7
TST.W D7
BNE.S CtnDv ; no 'X' bit
BSR.s DecrIt
CtnDv:
DBRA D6,DivLoop
DvFin:
SUBA.L A3,A0
MOVE.W A0,D6
MOVE.W D3,D1
ASL.W #3,D6 ; multiply by 8
SUBQ #3,D6 ; # number of integer bits
rts
noDiv:
MOVEA.L A3,A0
CLR.W -(A3)
BRA.S DvFin
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; PadIt
; D1 length of divisor
; D3 length of dividend
; D6 necessary length of DIVU required (similar to length i.e, 0 => 1
;
; Extended input located at (A3), length of D3 + 1 words,
; binary point D5 bits to the right of word (A3)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PadIt:
MOVE.W D1,D7
ADD.W D6,D7
ADDQ.W #1,D7 ; number of words necessary for dividend
SUB.W D3,D7 ; (D1 + D6 + 1) - D3
BLE.S @2 ; quotient has sufficent length
move.w d3,d2
asl.w #1,d2 ; times 2
LEA 2(A3,D2.W),A2 ; address of first block to be cleared
ADD.W D7,D3 ; adjust length
ASR.W #1,D7 ; divide by 2
@1:
CLR.L (A2)+
DBRA D7,@1
@2:
RTS
MandS:
LEA 2(A1,D7.W),A2
LEA 2(A0,D7.W),A4
SUBQ.W #2,D7
ASR.W #2,D7 ; use as a counter for DBRA
MOVE.W #0,CCR ; clear 'X'
@1:
MOVE.L -(A2),D4
MULU D2,D4
MOVE.L -(A4),D1
SUBX.L D4,D1
MOVE.L D1,(A4) ; 'C' Cleared, 'X' not affected.
DBRA D7,@1
RTS
DecrIt:
SUBQ.W #1,-2(A0)
MOVE.L D5,D1 ; #of bytes - 2 in divisor
LEA 2(A1,D1.W),A2
LEA 2(A0,D1.W),A4
ASR.W #1,D1 ; use as a counter for DBRA
MOVE.W #0,CCR ; clear 'X'
@1:
ADDX.W -(A2),-(A4)
DBRA D1,@1
BCC.S DecrIt
RTS
divOver:
CLR.W (A0)+
MOVE.W D5,D7 ; #of bytes - 2 in divisor
beq.s DecrIt
LEA 2(A1,D7.W),A2
LEA 0(A0,D7.W),A4
ASR.W #1,D7 ; use as a counter for DBRA
subq.w #1,D7
MOVE.W #0,CCR ; clear 'X'
@1:
SUBX.W -(A2),-(A4)
DBRA D7,@1
BSR.S DecrIt
BRA CtnDv
SizeExtN:
neg.w D0
cmpi.w #5208,D0 ; D0 - 5208
bcs.s allct ; D0 < 5208
MOVE.W #5207,D0 ; initialize mask
move.w d0,d1
neg.w d1
move.w d1,(a0) ; reset s(a6)
bra.s allct
SizeExt:
cmpi.w #5008,D0 ; D0 - 5008
bcs.s allct ; D0 < 5008
MOVE.W #5007,D0 ; initialize mask
move.w d0,(a0) ; reset s(a6)
allct:
moveq #0,d4 ; zero d4 (for longword comparison below)
move.w d0,d4
add.w #527,d4 ; minimum value for d4
and.b #$f8,d4 ; force multiple of 8
cmpi.w #4*bgSz,d4 ; d5 - #4*bgSz, check for max value
bcs.s @1
move.w #4*bgSz,d4
@1:
move.w d4,d5
asr.w #2,d5 ; divide by 4 [*** result must be even ***]
move.l d0,d1 ; save d0
_StackSpace ; results will be in A0 and D0
CMP.L d4,D0 ; do we have enough StackSpace?
BPL.S @2
MOVEQ #28,D0 ; Stack overflow error. The stack has expanded into the heap
_SysError
@2:
move.l d1,d0 ; restore d0
move.l (sp)+,a1 ; save return address
suba.w d4,sp ; allocate more space on the stack
move.l a1,-(sp) ; restore return address
lea areg(A6),A1
move.L A1,AAdr(A6)
suba.w d4,a1 ; -4*d5
move.L A1,DAdr(A6)
adda.w d5,a1 ; -3*d5
move.L A1,CAdr(A6)
adda.w d5,a1 ; -2*d5
move.L A1,BAdr(A6)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Algorithm for generating powers of ten. D0 intially contains
; the requested power of ten. On completion areg (see description
; below) will contain the actual integer power of 5.
;
Ten2AReg:
movea.L AAdr(A6),A1
MOVEQ #0,D2 ; make sure hi part of D2 is clear
MOVE.W D0,D2
DIVU #21,D2
MOVE.W D2,D0
SWAP D2 ; get remainder into D2.LO
CMPI.W #7,D2 ; D2 - 7
BLT.S Frst7
CMPI.W #14,D2 ; D2 - 14
BGE.S Third7
MOVEQ #1,D1 ; set length word
LEA Tbl2,A2
SUBQ.W #7,D2
ASL.W #2,D2 ; times 4, byte offset
MOVE.L 0(A2,D2.W),-(A1)
BRA.S GotTen2
Third7:
MOVEQ #2,D1 ; set length word
LEA Tbl3,A2
SUBI.W #14,D2
MULU #6,D2 ; times 6, byte offset
MOVE.L 2(A2,D2.W),-(A1)
BRA.S GotTen
Frst7:
MOVEQ #0,D1 ; set length word
LEA Tbl1,A2
ASL.W #1,D2 ; times 2, byte offset
GotTen:
MOVE.W 0(A2,D2.W),-(A1)
GotTen2:
TST.W D0
BNE.S MakeTen
RTS
MakeTen:
BSR.s a2c
MOVEQ #3,D2 ; initialize length of 10^21
movea.L BAdr(A6),A2
subq.L #4,A2
MOVE.L #$4D6E2EF5,(A2)
MOVE.L #$0001B1AE,-(A2)
bra.s BigLoop
DoItAgn:
bsr.s a2c
notOdd:
bsr.s bXb2b
BigLoop:
asr.w #1,d0
bcc.s notOdd
bsr.s cXb2a
tst.w d0
bgt.s DoItAgn
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; a, b & c reside in locations areg2(A6), breg2(A6), creg2(A6),
; respectively. They are stored in a string of words counting backwards
; from their starting location. The higher order words are appended
; to the lower address. D1, D2 & D3 contain their respective word
; lengths - 1.
;
cXb2a:
move.w d2,d1
add.w d3,d1
addq.w #1,d1 ; maximum length for areg
move.w d3,d7 ; use as counter
addq.w #1,d7 ; initial length for areg
lsr.w #1,d7 ; divide by 2, (clear 2 words at a time)
movea.L AAdr(A6),A1
@2:
clr.l -(a1) ; zero out areg(a6)
dbra d7,@2
movea.L AAdr(A6),A4
subq.L #2,A4
movea.L BAdr(A6),A2
subq.L #2,A2
move.w d2,d5 ; set outer counter
bra.s @4
@3:
clr.w -(a1)
subq.L #2,a2
subq.L #2,a4
@4:
move.L a4,a1
movea.L CAdr(A6),A3
move.w d3,d6 ; set inner loop counter for C
@5:
move.w -(a3),d7
mulu (a2),d7
suba.w #2,a1 ; adjust address of a1
add.l d7,(a1)
bcc.s @7 ; check for carry propagation
move.l a1,a0
@6:
addq.l #1,-(a0) ; propagate carry
bcs.s @6
@7:
dbra d6,@5
dbra d5,@3
tst.w (a1)
bne.s @10
lea 2(a1),a1 ; adjust address of a1
subq.w #1,d1 ; adjust length
@10:
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
a2c:
move.w d1,d3 ; areg -> creg
movea.L AAdr(A6),A1
movea.L CAdr(A6),A3
lsr.w #1,d1 ; divide by 2
@1:
move.l -(a1),-(a3)
dbra d1,@1
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
bXb2b:
move.w d2,d1
move.w d1,d7 ; use as counter
asl.w #1,d1 ; double it
addq.w #1,d1 ; new length
movea.L AAdr(A6),A1
@2:
clr.l -(a1) ; zero out areg(a6)
dbra d7,@2
move.w d2,d5 ; set counter for cross products of square
subq.w #1,d5
bmi.s doSqrs ; no cross products
movea.L AAdr(A6),A4 ; save address
subq.L #4,A4
movea.L BAdr(A6),A2
subq.L #2,A2
bra.s @4
@3:
suba.w #2,a2
suba.w #4,a4
@4:
move.l a4,a1
move.l a2,a3
move.w d5,d6 ; set inner loop counter for C
@5:
move.w -(a3),d7
mulu (a2),d7
suba.w #2,a1 ; adjust address of a1
add.l d7,(a1)
bcc.s @7
move.l a1,a0 ; COULD PUSH & RESTORE FROM STACK!!!
@6:
addq.l #1,-(a1)
bcs.s @6
move.l a0,a1 ; restore
@7:
dbra d6,@5
@8:
dbra d5,@3
dblIt:
movea.L AAdr(A6),A1
move.w #0,CCR ; clear 'X'
move.w d1,d7 ; use as counter
@1:
roxl.w -(a1)
dbra d7,@1
doSqrs:
movea.L AAdr(A6),A1
movea.L BAdr(A6),A2
@5:
move.w -(a2),d7
mulu d7,d7
add.l d7,-(a1)
bcc.s @7
move.l a1,a0 ; COULD PUSH & RESTORE FROM STACK!!!
@6:
addq.l #1,-(a1)
bcs.s @6
move.l a0,a1 ; restore
@7:
dbra d2,@5
tst.w (a1)
bne.s @10
lea 2(a1),a1 ; adjust address of a1
subq.w #1,d1 ; adjust length
@10:
tst.w d0
bne.s a2b
rts
a2b:
move.w d1,d2 ; areg -> breg
movea.L AAdr(A6),A1
movea.L BAdr(A6),A2
lsr.w #1,d1 ; divide by 2
@1:
move.l -(a1),-(a2)
dbra d1,@1
rts
Tbl1:
DC.W $0001 ; 5^0
DC.W $0005 ; 5^1
DC.W $0019 ; 5^2
DC.W $007D ; 5^3
DC.W $0271 ; 5^4
DC.W $0C35 ; 5^5
DC.W $3D09 ; 5^6
Tbl2:
DC.W $0001
DC.W $312D ; 5^7
DC.W $0005
DC.W $F5E1 ; 5^8
DC.W $001D
DC.W $CD65 ; 5^9
DC.W $0095
DC.W $02F9 ; 5^10
DC.W $02E9
DC.W $0EDD ; 5^11
DC.W $0E8D
DC.W $4A51 ; 5^12
DC.W $48C2
DC.W $7395 ; 5^13
Tbl3:
DC.W $0001
DC.W $6BCC
DC.W $41E9 ; 5^14
DC.W $0007
DC.W $1AFD
DC.W $498D ; 5^15
DC.W $0023
DC.W $86F2
DC.W $6FC1 ; 5^16
DC.W $00B1
DC.W $A2BC
DC.W $2EC5 ; 5^17
DC.W $0378
DC.W $2DAC
DC.W $E9D9 ; 5^18
DC.W $1158
DC.W $E460
DC.W $913D ; 5^19
DC.W $56BC
DC.W $75E2
DC.W $D631 ; 5^20