mac-rom/Toolbox/InSANE/FPCTRL.a
Elliot Nunn 4325cdcc78 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 09:52:23 +08:00

1210 lines
32 KiB
Plaintext

;
; File: FPCTRL.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):
;
; Terror Change History:
;
; <1> 11/14/90 BG Added to BBS for the first time.
;
; To Do:
;
;-----------------------------------------------------------
; FPCONTROL for MC68020
; Copyright Apple Computer, Inc., 1983,1984,1985,1989,1990
; All rights reserved
;-----------------------------------------------------------
;-----------------------------------------------------------
; 9 JAN 90: WRITTEN BY JON OKADA FOR MC68020, BASED UPON
; LISA CODE BY JEROME COONEN
; 11 JUN 90: MODIFIED TO SUPPORT 96-BIT EXTENDED OPERANDS (JPO)
;-----------------------------------------------------------
;-----------------------------------------------------------
; This is the main entry point of the package. The stack has
; the form:
; ret < opword < addr1 < addr2 < addr3,
; where the number of addresses (1, 2, or 3) depends on the
; operation.
;
; Dispatching is done to entry points for individual operations
; or for groups of similar operations.
;-----------------------------------------------------------
FP020 PROC EXPORT
;-----------------------------------------------------------
; Prepare for quick dispatching for operations
;-----------------------------------------------------------
; dc.w $a9ff
SUBQ.L #4,SP ; space for dispatching via RTS
MOVEM.L D0/A0,-(SP) ; save minimum # of registers
;-----------------------------------------------------------
; Get opword into D0.LO and dispatch quickly if format is
; extended or opword is odd.
;-----------------------------------------------------------
DISPBASE:
MOVEQ #0,D0 ; zero D0
MOVE.W 16(SP),D0 ; D0.W <- opcode
BFTST D0{18:3} ; test operation format:
BEQ.S DISP1 ; quick dispatch if extended
BTST #0,D0 ; test opcode for odd/even:
BEQ.S DISP2 ; even case uses second dispatch table
;-----------------------------------------------------------
; Dispatch via table for extended format or odd-numbered operations
;-----------------------------------------------------------
DISP1:
ANDI.W #$001F,D0 ; D0.W <- operation index
ADD D0,D0 ; double index to obtain table index
MOVE.W D1CASE(D0),D0 ; D0 <- operation addr offset
LEA DISPBASE(D0),A0 ; A0 <- operation addr
;-----------------------------------------------------------
; Accomplish entry to routine address in A0 via RTS. At this
; point, stack is:
; (SAVED D0/A0) < longword for addr < ret < opword < arg addresses
;-----------------------------------------------------------
VECTOROFF:
MOVE.L A0,8(SP) ; stuff addr in A0 into stack
MOVEM.L (SP)+,D0/A0 ; restore two registers
RTS ; quick entry to implementation
;-----------------------------------------------------------
; Dispatch via table for remaining even-numbered operations
;-----------------------------------------------------------
DISP2:
ANDI.W #$001E,D0 ; D0.W <- table index
MOVE.W D2CASE(D0),D0 ; D0 <- operation addr offset
LEA DISPBASE(D0),A0 ; A0 <- operation addr
BRA.S VECTOROFF ; dispatch after restoring registers
;-----------------------------------------------------------
; Tables for dispatching to operations
;-----------------------------------------------------------
D1CASE:
DC.W QADDX - DISPBASE
DC.W QSETENV - DISPBASE
DC.W QSUBX - DISPBASE
DC.W QGETENV - DISPBASE
DC.W QMULX - DISPBASE
DC.W QSETHV - DISPBASE
DC.W QDIVX - DISPBASE
DC.W QGETHV - DISPBASE
DC.W QCMPX - DISPBASE
DC.W QD2B - DISPBASE
DC.W QCMPX - DISPBASE
DC.W QB2D - DISPBASE
DC.W QREMX - DISPBASE
DC.W QNEG - DISPBASE
DC.W QX2X - DISPBASE
DC.W QABS - DISPBASE
DC.W QX2X - DISPBASE
DC.W QCPYSGN - DISPBASE
DC.W QSQRTX - DISPBASE
DC.W QNEXTB - DISPBASE
DC.W QRINTX - DISPBASE
DC.W QSETXCP - DISPBASE
DC.W QTINTX - DISPBASE
DC.W QPROCENTRY- DISPBASE
DC.W QSCALBX - DISPBASE
DC.W QPROCEXIT - DISPBASE
DC.W QLOGBX - DISPBASE
DC.W QTESTXCP - DISPBASE
DC.W QCLASSX - DISPBASE
D2CASE:
DC.W QADDB - DISPBASE
DC.W QSUBB - DISPBASE
DC.W QMULB - DISPBASE
DC.W QDIVB - DISPBASE
DC.W QCMPB - DISPBASE
DC.W QCMPB - DISPBASE
DC.W QREMB - DISPBASE
DC.W QB2X - DISPBASE
DC.W QX2B - DISPBASE
DC.W QSQRTX - DISPBASE
DC.W QRINTX - DISPBASE
DC.W QTINTX - DISPBASE
DC.W QSCALBX - DISPBASE
DC.W QLOGBX - DISPBASE
DC.W QCLASSB - DISPBASE
;-----------------------------------------------------------
;-----------------------------------------------------------
; Code to store opword and unpack pairs of operands, assuming
; A6 link entry. Register usage:
; A0 continuation jmp address
; A3 DST exponent
; A4 SRC exponent
; D0 operand classification
; D3/A2 DST significand
; D4/D5 SRC significand
; D6 opword/exceptions/operand signs
; D7 round/stickies (initialized to zero)
; A1/D1 scratch
;
; D6 will contain the following information:
; opword in bits 16-31 (HI word)
; exceptions in bits 8-12 (initialized to zero)
; SRC sign in bit 7
; DST sign in bit 6
; XOR of signs in bit 5
;
; D0 low word will contain NaN information for easy dispatching:
; 0 neither operand is a NaN
; 2 SRC is a NaN
; 4 DST is a NaN
; 6 both operands are NaNs
;
; If the low word in D0 is zero, the high word contains further
; classification information:
; 0 both operands are finite nonzero numbers
; 2 DST is finite and nonzero; SRC is zero
; 4 DST is finite and nonzero; SRC is infinite
; 6 DST is zero; SRC is finite and nonzero
; 8 both operands are zero
; 10 DST is zero; SRC is infinite
; 12 DST is infinite; SRC is finite and nonzero
; 14 DST is infinite; SRC is zero
; 16 both operands are infinite
; In this case (no NaN input, the high and low words of D0 are
; swapped before the operation continues.
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; Extended SRC and DST
;-----------------------------------------------------------
UNPACKXX:
MOVE.W LKOP(A6),D6 ; get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6 ; zero D6.LO
MOVEA.L LKADR1(A6),A1 ; unpack extended DST
BSR UNPXOP
MOVEA.L A4,A3 ; move to A3/D3/A2
MOVE.L D4,D3
MOVEA.L D5,A2
LSR.B #1,D6 ; move sign to D6 bit 6
MOVE.L D0,D1 ; double NaN index (D0.LO) and
CLR.W D1
ADD.L D0,D0 ; and triple number index (D0.HI)
ADD.L D1,D0
MOVEA.L LKADR2(A6),A1 ; unpack extended SRC
BSR UNPXOP
;-----------------------------------------------------------
; Convenient to put XOR of signs in D6 bit 5
;-----------------------------------------------------------
UNPACKED2:
ASL.B #1,D6 ; V = XOR of signs
BVC.S @1
BSET #6,D6
@1:
ROXR.B #1,D6
MOVEQ #0,D7 ; zero D7
;-----------------------------------------------------------
; Branch to special code if NaN(s) unpacked
;-----------------------------------------------------------
TST.W D0 ; any NaN input?
BNE.S NAN2OPS ; yes; special processing
SWAP D0 ; no; swap D0 high/low
JMP (A0) ; do operation
;-----------------------------------------------------------
; Non-extended SRC and extended DST
;-----------------------------------------------------------
UNPACKXB:
MOVE.W LKOP(A6),D6 ; Get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6 ; zero D6.LO
MOVEA.L LKADR1(A6),A1 ; unpack extended DST
BSR UNPXOP
MOVEA.L A4,A3 ; move to A3/D3/A2
MOVE.L D4,D3
MOVEA.L D5,A2
LSR.B #1,D6 ; move sign to D6 bit 6
MOVE.L D0,D1 ; double NaN index (D0.LO) and
CLR.W D1
ADD.L D0,D0 ; and triple number index (D0.HI)
ADD.L D1,D0
MOVE.L D6,D1 ; extract offset from format
CLR.W D1 ; code in opword
ROL.L #6,D1
ANDI.W #$000E,D1
SUBQ.W #2,D1
MOVEA.L LKADR2(A6),A1 ; unpack non-extended SRC
BSR UNPBOP
BRA.S UNPACKED2 ; get XOR of signs & check for NaNs
;-----------------------------------------------------------
; This is the target of all invalid operations with extended
; results. Bits in D0 000000XX must go to 00XX0000.
;-----------------------------------------------------------
INVALIDOP:
BSET #ERRI+8,D6
SWAP D0 ; align code byte
BSET #QNANBIT,D0 ; mark it quiet
MOVE.L D0,D4
MOVEQ #0,D5 ; clear low half
MOVEA.W #$7FFF,A4 ; set exponent
BRA PACKX ; pack it
;-----------------------------------------------------------
; NaN input detected for binary operation. Take as preliminary
; result any single NaN input or, if both inputs are NaN, the
; NaN with the larger code. At this point, unpacked extended
; operands are in A3/D3/A2 (DST) and A4/D4/D5 (SRC), and the
; input NaN flags are in D0.W.
;-----------------------------------------------------------
NAN2OPS:
LEA FINI2OPS,A0 ; continuation addr
NANCOMMON:
SUBQ.W #2,D0
BEQ.S NANSRC ; SRC NaN only
SUBQ.W #2,D0
BEQ.S NANDST ; DST NaN only
MOVE.L D3,D7 ; two NaN inputs
MOVE.L #$00FF0000,D1 ; NaN code mask
AND.L D1,D7 ; DST code
AND.L D4,D1 ; SRC code
CMP.L D7,D1
BGT.S NANSRC ; SRC code >= DST code
NANDST: ; move DST NaN to SRC position
ADD.B D6,D6 ; sign
MOVEA.L A3,A4 ; exponent
MOVE.L D3,D4 ; significand
MOVE.L A2,D5
NANSRC:
BFEXTU D6{11:5},D1 ; D1 <- opcode
SUBQ #8,D1
BEQ.S NANCMP ; FCMP opcode (no signal on unordered)
SUBQ #2,D1
BNE.S FPNANOUT ; output extended NaN
BSET #ERRI+8,D6 ; FCPX signals invalid on unordered
NANCMP:
MOVEQ #CMPU,D7 ; mark unordered
BRA FINICMPS ; finish comparison
;-----------------------------------------------------------
; Output extended NaN after checking for interesting NaN bits,
; giving special code if there is none
;-----------------------------------------------------------
FPNANOUT:
BSR.S FPNANIN ; check for interesting NaN bits
BRA PACKX ; stuff NaN result
;-----------------------------------------------------------
; Subroutine FPNANIN checks for interesting NaN bits and gives
; a special NaN code if there is none. Input NaN significand
; is in D4/D5. Uses D1 as scratch register
;-----------------------------------------------------------
FPNANIN:
MOVE.L D4,D1 ; check for all zeros
BCLR #QNANBIT,D1 ; disregard quiet bit
OR.L D5,D1
BNE.S @1
MOVEQ #NANZERO,D4 ; special NaN if no code
SWAP D4
BSET #QNANBIT,D4
@1:
RTS
;-----------------------------------------------------------
; Subroutine UNPXOP unpacks an extended operand. Register usage
; is as follows:
; ENTRY:
; A1 operand addr
; D6.HI opword (includes 96-bit extended format bit (#21)
; EXIT:
; A4 exponent of operand sign-extended to 32 bits
; D4/D5 extended significand
; D0 classification code for operand (ADDed in)
; D6 sign of operand in bit 7 (ORed in)
; A1,D1 trashed
;-----------------------------------------------------------
UNPXOP:
MOVE.W (A1)+,D1 ; D1 <- sign/exp
BPL.S @1 ; + case
BSET #7,D6 ; - case; set flag in D6
BCLR #15,D1 ; and clr sign bit
@1:
BTST #21,D6 ; 96-bit extended?
BEQ.S @2 ; no. 80-bit
ADDQ #2,A1 ; yes. bump pointer by 2
@2:
MOVE.L (A1)+,D4 ; significand bits into D4/D5
MOVE.L (A1),D5
CMPI.W #$7FFF,D1 ; max exp?
BEQ.S UNPNIN ; yes; special handling
TST.L D4 ; normalized?
BPL.S UNPZUN ; no; special handling
UNPNRM: ; normalized case
EXT.L D1 ; 32-bit exponent in A4
MOVEA.L D1,A4
RTS ; return
;-----------------------------------------------------------
; Distinguish special case and set appropriate bit in D0:
; bit 17 for zero;
; bit 18 for infinite
; bit 1 for NaN
;-----------------------------------------------------------
UNPZUN:
TST.L D4 ; unnormalized or zero
BNE.S UNPUNR
TST.L D5
BNE.S UNPUNR ; do normalization
UNP0:
SUBA.L A4,A4 ; ZERO unpacked; zero exponent
ADD.L #$00020000,D0 ; mark in D0
RTS ; done
UNPUNR: ; enter here to normalize quietly
SUBQ.W #1,D1 ; decrement exponent
ADD.L D5,D5 ; shift significand left
ADDX.L D4,D4
BPL.S UNPUNR ; loop until high bit of D4 set, then
BRA.S UNPNRM ; treat as normalized
UNPNIN: ; NaN or infinity
MOVEA.W #$7FFF,A4 ; max exponent
BCLR #31,D4 ; ignore explicit bit
TST.L D4
BNE.S UNPNAN ; NaN if any other significand
TST.L D5 ; bit is set
BNE.S UNPNAN
ADD.L #$00040000,D0 ; INFINITE; mark INF operand in D0
RTS
;-----------------------------------------------------------
; NaN unpacked. Test and set the quiet NaN bit (#30 of upper
; half of the significand); if it was clear, then signal invalid.
;-----------------------------------------------------------
UNPNAN:
BSET #QNANBIT,D4 ; quiet the NaN
BNE.S @2 ; it was already quiet
BSET #ERRI+8,D6 ; signaling NaN raises invalid flag
@2:
ADDQ.W #2,D0 ; mark NaN in D0
RTS
;-----------------------------------------------------------
; Subroutine UNPBOP unpacks a non-extended binary operand. Register
; usage is as follows:
; ENTRY:
; A1 operand addr
; D1 offset into jump table for unpack routine
; EXIT:
; A4 exponent of operand sign-extended to 32 bits
; D4/D5 extended significand
; D0 classification code for operand (ADDed in)
; D6 sign of operand in bit 7 (ORed in)
; A1,D1 trashed
; D2 scratch register for some formats
;-----------------------------------------------------------
UNPBOP:
MOVE.W UNPCASE(D1),D1 ; get addr of specific routine
JMP UNPBOP(D1) ; and jump there
UNPCASE:
DC.W UNPDBL - UNPBOP ; double precision
DC.W UNPSGL - UNPBOP ; single precision
DC.W UNPXOP - UNPBOP ; ---illegal format
DC.W UNPI16 - UNPBOP ; int16
DC.W UNPI32 - UNPBOP ; int32
DC.W UNPC64 - UNPBOP ; comp
;-----------------------------------------------------------
; 16-bit integer unpacking has special case of zero; else
; normalize and return
;-----------------------------------------------------------
UNPI16:
MOVEQ #0,D4 ; zero D4
MOVE.W #$400E,D1 ; set exponent for short integer
MOVE.L D4,D5 ; zero D5
MOVE.W (A1),D4 ; get operand
SWAP D4 ; left align in register
BRA.S UNPIGEN ; continue below
;-----------------------------------------------------------
; 32-bit integer unpacking has special case of zero; else
; normalize and return
;-----------------------------------------------------------
UNPI32:
MOVE.W #$401E,D1 ; set exponent for long integer
MOVEQ #0,D5 ; zero D5
MOVE.L (A1),D4 ; get operand
UNPIGEN:
BEQ.S UNP0 ; zero
BPL.S UNPIUNR ; positive. normalize
BSET #7,D6 ; negative. set sign in D6
NEG.L D4 ; negate D4
BMI.S UNPNRM ; already normalized if = $80000000
;-----------------------------------------------------------
; Normalization for D4 > 0 and D5 = 0
;-----------------------------------------------------------
UNPIUNR:
BFFFO D4{0:0},D2 ; find first one bit
SUB.W D2,D1 ; adjust exponent
LSL.L D2,D4 ; shift significand
BRA UNPNRM ; NORMALIZED
;-----------------------------------------------------------
; 64-bit comp unpacking has special cases of zero and NaN; else
; normalize and return
;-----------------------------------------------------------
UNPC64:
MOVE.W #$403E,D1 ; initialize exponent
MOVE.L (A1)+,D4 ; get operand into D4/D5
MOVE.L (A1),D5
BNE.S @1 ; low half nonzero
TST.L D4 ; test high half
BEQ UNP0 ; comp ZERO
BPL.S UNPIUNR ; normalize positive
BSET #7,D6 ; flag negative in D6
NEG.L D4 ; negate high significand
BPL.S UNPIUNR ; normalize if not NaN
MOVEA.W #$7FFF,A4 ; comp NaN; set exponent
BCLR #7,D6 ; clear sign bit
MOVEQ #NANCOMP,D4 ; NaN code in significand high word
SWAP D4
BSET #QNANBIT,D4 ; make it quiet
ADDQ.W #2,D0 ; mark NaN in D0
RTS ; done
@1: ; comp low half nonzero
TST.L D4 ; test high half
BPL.S @2 ; nonnegative
BSET #7,D6 ; mark as negative in D6
NEG.L D5 ; negate
NEGX.L D4
TST.L D4 ; test high half
@2:
BNE UNPUNR ; nonzero; normalize D4/D5
SUBI.W #$0020,D1 ; high half zero; reduce exponent
EXG D4,D5 ; exchange high/low halves
TST.L D4
BPL.S UNPIUNR ; normalize if necessary
BRA UNPNRM
;-----------------------------------------------------------
; Single-precision unpacking has special cases of zero, NaN,
; and infinite
;-----------------------------------------------------------
UNPSGL:
MOVEQ #0,D5 ; zero significand low half
MOVE.L (A1),D4 ; read single-precision into D4
BPL.S @3 ; not negative
BSET #7,D6 ; negative; mark in D6
@3:
BFEXTU D4{1:8},D1 ; extract exponent into D1
BEQ.S @4 ; ZERO or subnormal single
LSL.L #8,D4 ; shift significand just short of bit 31
CMPI.B #$FF,D1 ; max exp?
BEQ UNPNIN ; yes; NaN or INFINITE
ADDI.W #$3F80,D1 ; normalized; bias exponent
BSET #31,D4 ; set explicit bit
BRA UNPNRM
@4:
LSL.L #8,D4 ; shift significand
BEQ UNP0 ; ZERO
MOVE.W #$3F81,D1 ; single subnormal; bias exponent
BRA UNPIUNR ; normalize
;-----------------------------------------------------------
; Double-precision unpacking has special cases of zero, NaN,
; and infinite
;-----------------------------------------------------------
UNPDBL:
MOVE.L (A1),D4 ; high bits into D4
BPL.S @5
BSET #7,D6 ; set sign in D6
@5:
MOVE.L 4(A1),D5 ; low bits in D5
;-----------------------------------------------------------
; Double operands appear as: (1) (11) (1 implicit) (52), so
; must align bits left by 11 places and insert explicit lead
; bit. Do this via shifts and bit field instructions.
;-----------------------------------------------------------
BFEXTU D4{1:11},D1 ; extract exponent into D1
BFEXTU D5{0:11},D2 ; extract 11 high bits of D5
LSL.L #8,D4 ; shift D4 and D5 left 11 places
LSL.L #8,D5
LSL.L #3,D4
LSL.L #3,D5
OR.W D2,D4 ; move 11 bits to D4 low end
BCLR #31,D4 ; clr explicit bit initially
TST.L D1 ; test exponent
BNE.S @6 ; normalized, infinite, or NaN
MOVE.W #$3C01,D1 ; zero or unnormalized
BRA UNPZUN
@6:
CMPI.W #$07FF,D1 ; max exp?
BEQ UNPNIN ; yes, NaN or INF
BSET #31,D4 ; normalized number; set explicit bit
ADDI.W #$3C00,D1 ; bias exponent
BRA UNPNRM
;-----------------------------------------------------------
; Subroutine RTSHIFT.
;
; This is the right shifter used in subnormal coercion, IPALIGN ...
; Shift count in D0 > 0; Shift registers are D4/D5/D7 (stickies)
; Uses D3 as scratch register.
;-----------------------------------------------------------
RTSHIFT:
CMPI.W #66,D0 ; high shift counts pin to 66
BLS.S @1
MOVE.W #66,D0
@1:
CMPI.W #32,D0 ; count < 32?
BLT.S @3 ; yes. do shift
TST.L D7 ; no. set stickies if D7 nonzero
SNE D3
MOVE.L D5,D7 ; shift D4/D5 into D5/D7
MOVE.L D4,D5
OR.B D3,D7 ; OR in low stickies
MOVEQ #0,D4 ; zero D4
SUBI.W #32,D0 ; decr count by 32
BNE.S @1 ; loop if nonzero
RTS ; otherwise, done
@3: ; right shift of 1-31 bits
BFINS D7,D3{0:D0} ; test low bits
SNE D3 ; set sticky state in D3
LSR.L D0,D7 ; shift D7 right
BFINS D5,D7{0:D0} ; shift bits from D5 low to D7 high
LSR.L D0,D5 ; shift D5 right
BFINS D4,D5{0:D0} ; shift bits from D4 low to D5 high
LSR.L D0,D4 ; shift D4
OR.B D3,D7 ; OR in low stickies
RTS ; done
;-----------------------------------------------------------
; COERCION routines assume post-operation register mask with
; result in:
; A4 exponent
; D4 significand high 32 bits
; D5 significand
; D7 round/stickies.
;-----------------------------------------------------------
;-----------------------------------------------------------
; Check value first, stuff if zero (with exp fix), otherwise,
; normalize and coerce. Called only by remainder routine,
; which is exact and which has default (extended) rounding
; precision.
;-----------------------------------------------------------
ZNORMCOERCEX:
MOVE.L #0,D7 ; make it exact
TST.L D4
BNE.S NORMX ; nonzero
TST.L D5
BNE.S NORMX ; nonzero
SUBA.L A4,A4 ; zero
BRA PACKX ; never coerce
NORMX:
TST.L D4 ; check for lead 1 bit
BMI COERCEX ; normalized, so coerce
@1:
SUBQ.L #1,A4 ; decr exponent
ADD.L D5,D5 ; shift significand
ADDX.L D4,D4
BPL.S @1 ; loop until normalized
BRA COERCEX ; coerce with default precision
;-----------------------------------------------------------
; ASSUME, AS AFTER SUBTRACT THAT VALUE IS NONZERO. USE 1ST
; BRANCH TO SHORTEN ACTUAL LOOP BY A BRANCH.
;-----------------------------------------------------------
NORMCOERCE:
TST.L D4 ; CHECK FOR LEAD 1
BMI.S COERCE
@1:
SUBQ.L #1,A4 ; DECREMENT EXP
ADD.L D7,D7 ; SHIFT RND
ADDX.L D5,D5 ; LO BITS
ADDX.L D4,D4
BPL.S @1 ; WHEN NORM, FALL THROUGH
;-----------------------------------------------------------
; COERCE MILESTONE +++++++++++++++++++++++++++++++++++++++ .
;
; RUN SEPARATE SEQUENCES FOR EXT, SGL, DBL TO SAVE TESTS.
; NOTE THAT FOR CONVENIENCE IN BRANCHING, THE SGL AND DBL
; COERCE SEQUENCES FOLLOW THE COERCE ROUTINES.
; SINCE OVERFLOW RESULTS IN A VALUE DEPENDING ON THE
; PRECISION CONTROL BITS, RETURN CCR KEY FROM OFLOW:
; EQ: OK NE: HUGE
;
; Extended result is in D6 bit 7 (sign), A4 (exp), D4/D5 (sig),
; and D7 (round/stickies).
; Uses D1.W to store environment word for rounding control info.
;-----------------------------------------------------------
COERCE:
MOVE.W (FPSTATE).W,D1 ; environment in D1
BFTST D1{25:2} ; check rounding precision
BEQ COERCEX ; default (extended)
BPL COERCED ; double
;-----------------------------------------------------------
; Coerce to single precision, obeying prescribed
; rounding direction.
;-----------------------------------------------------------
COERCES:
BSR.S SCOERCE ; coerce to single precision
BRA COERCESDONE
;-----------------------------------------------------------
; Subroutine SCOERCE coerces extended value in A4/D4/D5/D7 to
; single precision, honoring prescribed rounding direction
; in environment word (D1.W). Uses D0, clobbers D1
;-----------------------------------------------------------
SCOERCE:
MOVE.L #$3F81,D0 ; single underflow threshold
SUB.L A4,D0 ; threshold - exponent
BLE.S @1 ; no underflow
BSET #ERRU+8,D6 ; signal underflow
MOVEA.W #$3F81,A4 ; minimum exponent
BSR RTSHIFT ; shift
@1:
TST.L D5 ; get single precision bits/stickies
SNE D0 ; nonzero D5 sets low stickies
OR.B D0,D7
ADD.B D4,D4 ; round bit to X
ROXR.L #1,D7 ; X to D7 high bit
OR.B D4,D7 ; last stickies to D7 low byte
MOVEQ #0,D5 ; clear low bits
CLR.B D4
TST.L D7 ; exact result?
BNE.S @3 ; no
BCLR #ERRU+8,D6 ; yes. suppress underflow
BRA.S RNDOVERS
@3:
BSET #ERRX+8,D6 ; signal inexact
BFTST D1{17:2} ; round to nearest?
BEQ.S @7 ; yes
BMI.S @5 ; chop or round downward
TST.B D6 ; round toward +°
BPL.S RNDUPS ; increment significand if positive
BRA.S RNDOVERS
@5:
BTST #13,D1 ; chop or round toward -°
BNE.S RNDOVERS ; chop requires no incrementing
TST.B D6 ; round toward -°
BPL.S RNDOVERS
BRA.S RNDUPS ; increment significand if negative
@7:
ADD.L D7,D7 ; round bit set?
BCC.S RNDOVERS ; no; significand OK
BNE.S RNDUPS ; yes, with stickies, so round up
BTST #8,D4 ; halfway case gets bumped if low single
BEQ.S RNDOVERS ; bit is set
RNDUPS:
ADD.L #$0100,D4 ; bump significand
BCC.S RNDOVERS
ROXR.L #1,D4 ; if overflow, shift right and bump exponent
ADDQ.L #1,A4
RNDOVERS:
MOVEA.W #$407E,A3 ; single overflow exponent threshold
CMPA.L A4,A3
BLT.S @9
RTS ; no overflow
@9: ; single precision overflow
BSET #ERRO+8,D6 ; signal overflow and inexact
BSET #ERRX+8,D6
MOVEA.W #$7FFF,A4 ; store INF initially
SUB.L D4,D4
SUB.L D5,D5
LSR.W #8,D1 ; check rounding direction
AND.B #RNDMSK,D1
BNE.S @11
RTS ; default rounding returns INF
@11:
CMPI.B #RNDMSK,D1 ; chopping returns single HUGE
BEQ.S HUGES
TST.B D6 ; check sign
BMI.S @15
CMPI.B #RNDUP,D1 ; positive
BNE.S HUGES ; round down returns single HUGE
RTS ; round up returns INF
@15:
CMPI.B #RNDDN,D1 ; negative
BNE.S HUGES ; round up returns single HUGE
RTS ; round down returns -INF
HUGES:
MOVEQ #-1,D4 ; set maximum single exponent and significand
MOVEA.L A3,A4
CLR.B D4
RTS
;-----------------------------------------------------------
; Coerce to double precision, obeying prescribed
; rounding direction.
;-----------------------------------------------------------
COERCED:
BSR.S DCOERCE ; coerce to double precision
COERCESDONE:
BTST #ERRU+8,D6 ; underflow?
BEQ PACKX ; no; pack extended format
MOVE.W A4,D3 ; zero exponent?
BEQ PACKX ; yes; OK
TST.L D4 ; normalized or nonzero?
BNE.S @3 ; yes
TST.L D5
BNE.S @1 ; unnormalized
SUBA.L A4,A4 ; zero (inexact result); zero exponent
BRA PACKX ; pack extended format
@1:
SUBA.W #1,A4 ; normalize subnormal single/double
ADD.L D5,D5
ADDX.L D4,D4
@3:
BPL.S @1
BRA PACKX ; pack extended format
;-----------------------------------------------------------
; Subroutine DCOERCE coerces extended value in A4/D4/D5/D7 to
; double precision, honoring prescribed rounding direction
; in environment word (D1.W). Uses D0/D2/A3, clobbers D1
;-----------------------------------------------------------
DCOERCE:
MOVE.L #$3C01,D0 ; double underflow threshold
SUB.L A4,D0 ; threshold - exponent
BLE.S @1 ; no underflow
BSET #ERRU+8,D6 ; signal underflow
MOVEA.W #$3C01,A4 ; minimum exponent
BSR RTSHIFT ; shift
@1:
MOVE.L #$07FF,D0 ; low bit mask
AND.W D5,D0
ANDI.W #$0F800,D5 ; clr low bits
LSL.W #5,D0 ; left align round/stickies
SWAP D0
TST.L D7 ; stickies from D7
SNE D0
MOVE.L D0,D7 ; round/stickies into D7
TST.L D7 ; exact result?
BNE.S @3 ; no
BCLR #ERRU+8,D6 ; yes. suppress underflow
BRA.S RNDOVERD
@3:
BSET #ERRX+8,D6 ; signal inexact
BFTST D1{17:2} ; round to nearest?
BEQ.S @7 ; yes
BMI.S @5 ; chop or round downward
TST.B D6 ; round toward +°
BPL.S RNDUPD ; increment significand if positive
BRA.S RNDOVERD
@5:
BTST #13,D1 ; chop or round toward -°
BNE.S RNDOVERD ; chop requires no incrementing
TST.B D6 ; round toward -°
BPL.S RNDOVERD
BRA.S RNDUPD ; increment significand if negative
@7:
ADD.L D7,D7 ; round bit set?
BCC.S RNDOVERD ; no; significand OK
BNE.S RNDUPD ; yes, with stickies, so round up
BTST #11,D5 ; halfway case gets bumped if low double
BEQ.S RNDOVERD ; bit is set
RNDUPD:
MOVEQ #0,D0
MOVE.L #$00000800,D2
ADD.L D2,D5 ; bump significand
ADDX.L D0,D4
BCC.S RNDOVERD
ROXR.L #1,D4 ; if overflow, shift right and bump exponent
ADDQ.L #1,A4
RNDOVERD:
MOVEA.W #$43FE,A3 ; double overflow exponent threshold
CMPA.L A4,A3
BLT.S @9
RTS ; no overflow
@9: ; double precision overflow
BSET #ERRO+8,D6 ; signal overflow and inexact
BSET #ERRX+8,D6
MOVEA.W #$7FFF,A4 ; store INF initially
SUB.L D4,D4
SUB.L D5,D5
LSR.W #8,D1 ; check rounding direction
AND.B #RNDMSK,D1
BNE.S @11
RTS ; default rounding returns INF
@11:
CMPI.B #RNDMSK,D1 ; chopping returns double HUGE
BEQ.S HUGED
TST.B D6 ; check sign
BMI.S @15
CMPI.B #RNDUP,D1 ; positive
BNE.S HUGED ; round down returns double HUGE
RTS ; round up returns INF
@15:
CMPI.B #RNDDN,D1 ; negative
BNE.S HUGED ; round up returns double HUGE
RTS ; round down returns -INF
HUGED:
MOVEQ #-1,D4 ; set maximum double exponent and significand
MOVEA.L A3,A4
MOVE.L #$FFFFF800,D5
RTS
;-----------------------------------------------------------
; Coerce to default (extended) precision, obeying prescribed
; rounding direction.
;-----------------------------------------------------------
COERCEX:
MOVE.L A4,D0 ; exponent >= 0?
BPL.S ROUNDX ; yes, not subnormal
NEG.L D0 ; underflow; get count
BSET #ERRU+8,D6 ; signal underflow
SUBA.L A4,A4 ; zero exponent
BSR RTSHIFT ; shift significand right
ROUNDX:
TST.L D7 ; exact result?
BNE.S @1 ; no
BCLR #ERRU+8,D6 ; yes; suppress underflow
BRA.S RNDOVERX ; no rounding needed
;-----------------------------------------------------------
; Inexact result: signal and round
;-----------------------------------------------------------
@1:
BSET #ERRX+8,D6 ; signal inexact
BFTST D1{17:2} ; round to nearest?
BEQ.S @3 ; yes
BMI.S @2 ; chop or round downward
;-----------------------------------------------------------
; Round toward +°
;-----------------------------------------------------------
TST.B D6 ; bump significand if positive
BPL.S RNDUPX
BRA.S RNDOVERX
@2:
BTST #13,D1 ; chop or round downward?
BNE.S RNDOVERX ; chop requires no rounding
;-----------------------------------------------------------
; Round toward -°
;-----------------------------------------------------------
TST.B D6 ; bump significand if negative
BPL.S RNDOVERX
BRA.S RNDUPX
;-----------------------------------------------------------
; Default rounding (to nearest)
;-----------------------------------------------------------
@3:
ADD.L D7,D7 ; round bit set?
BCC.S RNDOVERX ; no; significand OK
BNE.S RNDUPX ; stickies set so round up
BTST #0,D5 ; halfway case gets bumped
BEQ.S RNDOVERX ; if lowest SIG bit is 1
RNDUPX:
MOVEQ #0,D0 ; increment significand
ADDQ.L #1,D5
ADDX.L D0,D4
BCC.S RNDOVERX
ROXR.L #1,D4 ; if overflow, shift right and
ADDQ.L #1,A4 ; bump exponent
;-----------------------------------------------------------
; Check for overflow result
;-----------------------------------------------------------
RNDOVERX:
MOVEA.W #$7FFF,A3 ; overflow?
CMPA.L A3,A4
BLT.S PACKX ; no; stuff result
;-----------------------------------------------------------
; Overflow detected. Return HUGE or INF depending on
; rounding direction and sign.
;-----------------------------------------------------------
BSET #ERRO+8,D6 ; signal overflow and inexact
BSET #ERRX+8,D6
MOVEA.W A3,A4 ; set max exp
SUB.L D4,D4 ; zero significand
SUB.L D5,D5
LSR.W #8,D1 ; check rounding direction
AND.B #RNDMSK,D1
BEQ.S PACKX ; default returns INF
CMPI.B #RNDMSK,D1 ; chopping returns HUGE
BEQ.S HUGEOUT
TST.B D6 ; check sign
BMI.S @5
CMPI.B #RNDUP,D1 ; positive
BEQ.S PACKX ; round up returns INF
BRA.S HUGEOUT ; round down returns huge
@5:
CMPI.B #RNDDN,D1 ; negative
BEQ.S PACKX ; round down returns INF
HUGEOUT:
SUBQ #1,A4 ; exponent = $7FFE
NOT.L D4 ; set all significand bits
NOT.L D5
;-----------------------------------------------------------
; Pack an extended result into destination address
;-----------------------------------------------------------
PACKX:
MOVE.W A4,D3 ; exponent into D3
MOVEA.L LKADR1(A6),A3 ; get DST addr
TST.B D6 ; sign into D3
BPL.S @1 ; positive
BSET #15,D3 ; negative
@1:
MOVE.W D3,(A3)+ ; stuff result
BTST #21,D6 ; bump pointer if 96-bit extended
BEQ.S @2
ADDQ #2,A3
@2:
MOVE.L D4,(A3)+
MOVE.L D5,(A3)
JMP (A0) ; finish-up routine
;-----------------------------------------------------------
; Finish up and return after stack cleanup. Check for halts here.
;-----------------------------------------------------------
FINI2OPS:
CLR D7 ; clear CCR bits
FINICMPS:
MOVEA.W #FPSTATE,A1 ; A1 <- environment addr
MOVE.W (A1),D0 ; get environment word
CLR.B D6 ; kill signs
OR.W D6,D0 ; OR in new exceptions
MOVE.W D0,(A1)+ ; store new exceptions and bump A1 pointer
LSR.W #8,D6 ; align exceptions with trap enables
AND.W D6,D0
BNE.S HALT2OP ; halt enabled
FINISH2:
MOVE D7,CCR ; prepare CCR
MOVEM.L (SP)+,D0-D7/A0-A4 ; restore registers
UNLK A6 ; unlink
RTD #STKREM2 ; return, popping parameters
;-----------------------------------------------------------
; TO SET UP FOR TRAP:
; HAVE D0 ON TOP OF STACK.
; PUSH CCR TO HAVE 3-WORD STRUCTURE
; PUSH ADDRESS OF 3-WORD STRUCTURE
; BLOCK MOVE OPCODE < ADR1 < ADR2 < ADR3 < REGADR
; TO STACK
; CALL HALT PROCEDURE, EXPECTING PASCAL CONVENTIONS TO
; BE HONORED.
; THE BLOCK MOVE CAN BE DONE WITH A PAIR OF MOVEM'S SO LONG
; AS AN EXTRA WORD IS COPIED (TO HAVE A WHOLE NUMBER OF
; LONGS).
;-----------------------------------------------------------
HALT2OP:
LEA FINISH2,A4
HALTCOMMON:
MOVE.W D7,-(SP) ; push pending CCR
MOVE.W D0,-(SP) ; push halt exceptions
PEA (SP) ; ADDRESS OF CCR/D0
MOVEM.L LKRET+2(A6),D0-D3 ; PUSH ADDRESSES & OPCODE ON STACK
MOVEM.L D0-D3,-(SP)
ADDQ #2,SP ; adjust SP to point to opcode
MOVEA.L (A1),A1 ; CALL USER HALT HANDLER
JSR (A1)
MOVE.L (SP)+,D7 ; RESTORE CCR BITS AND STACK
JMP (A4)
;-----------------------------------------------------------
;-----------------------------------------------------------
FINI1OP:
MOVEA.W #FPSTATE,A1
CLR D7
MOVE.W (A1),D0 ; GET STATE WORD
CLR.B D6 ; KILL SIGNS
OR.W D6,D0
MOVE.W D0,(A1)+ ; BUMP ADRS TO VECTOR
ROR.W #8,D6 ; ALIGN BYTES
AND.W D6,D0
BNE.S HALT1OP ; ZERO IF NO TRAP
FINISH1:
MOVE D7,CCR
MOVEM.L (SP)+,D0-D7/A0-A4
UNLK A6
RTD #STKREM1
;-----------------------------------------------------------
;-----------------------------------------------------------
HALT1OP:
LEA FINISH1,A4
BRA.S HALTCOMMON
;-----------------------------------------------------------
; These routines handle the special cases in operations
; when the DST operand is the exact extended result.
; Subcase depends on whether the sign should also be stuffed.
; (The SRC-is-result case is always trivial).
;-----------------------------------------------------------
RDSTSGN:
ADD.B D6,D6 ; shift DST sign to bit 7
RDST:
MOVE.L A2,D5
MOVE.L D3,D4
MOVEA.L A3,A4 ; exponent, too
BRA PACKX ; coerce and pack result