mac-rom/Toolbox/InSANE/FPOPS.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

2907 lines
80 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

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

;
; File: FPOPS.a
;
; Contains: xxx put contents here xxx
;
; Written by: The Apple Numerics Group
;
; Copyright: © 1990-1993 by Apple Computer, Inc., all rights reserved.
;
; This file is used in these builds: Mac32
;
; Change History (most recent first):
;
; <SM2> 2/3/93 CSS Update from Horror:
; <H2> 9/29/92 BG Rolling in Jon Okada's latest fixes.
; <1> 11/14/90 BG Added to BBS for the first time.
;
; To Do:
;
;-----------------------------------------------------------
; PACK4 FP operations for MC68020
; Copyright Apple Computer, Inc., 1983,1984,1985,1989,1990,1991,1992,1993
; All rights reserved
;-----------------------------------------------------------
; CHANGE LOG:
; 23 Mar 92 JPO Modified subroutines IPALIGN and IALIGN to fix rounding
; error for FTINTX and FRINTX when rounding precision is
; less than extended.
;-----------------------------------------------------------
; Entry points for individual operations or clusters of
; operations with different data formats.
;
; Stack has identical form as at the main entry point of the
; package:
; ret < opword < addr1 < addr2 < addr3,
; where the number of addresses (1, 2, or 3) depends on the
; operation.
;-----------------------------------------------------------
;-----------------------------------------------------------
; MACRO performs setup with numerous entry points (functions
; which require all D and A registers).
;-----------------------------------------------------------
MACRO
FINIT
LINK A6,#0
MOVEM.L D0-D7/A0-A4,-(SP) ; save all registers
ENDM
;-----------------------------------------------------------
;-----------------------------------------------------------
; FP ADDITION/SUBTRACTION
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QADDX---Add extended to extended
;-----------------------------------------------------------
QADDX:
FINIT
LEA ADDTOP,A0 ; continue below
BRA UNPACKXX ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; QADDB---Add non-extended SRC to extended DST
;-----------------------------------------------------------
QADDB:
FINIT
LEA ADDTOP,A0 ; continue below
BRA UNPACKXB ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; QSUBX---Subtract extended SRC from extended DST
;-----------------------------------------------------------
QSUBX:
FINIT
LEA SUBTOP,A0 ; continue below
BRA UNPACKXX ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; QSUBB---Subtract non-extended SRC from extended DST
;-----------------------------------------------------------
QSUBB:
FINIT
LEA SUBTOP,A0 ; continue below
BRA UNPACKXB ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; To subtract, just flip the sign and XOR-sign bits in D6.B
;-----------------------------------------------------------
SUBTOP:
EORI.B #$A0,D6
;-----------------------------------------------------------
; Add 2 numbers. Dispatch to one of 9 cases depending on
; class of operands: [finite] number, zero, or infinite.
; NaNs have already been filtered out.
;-----------------------------------------------------------
ADDTOP:
LEA FINI2OPS,A0 ; finish-up routine
MOVE.W ADDCASE(D0),D0 ; index to D0
JMP ADDTOP(D0) ; do special case
ADDCASE: ; DST + SRC
DC.W ADDNUM - ADDTOP ; NUM + NUM
DC.W ADDS0 - ADDTOP ; NUM + 0
DC.W PACKX - ADDTOP ; NUM + INF
DC.W ADDD0 - ADDTOP ; 0 + NUM
DC.W ADD00 - ADDTOP ; 0 + 0
DC.W PACKX - ADDTOP ; 0 + INF
DC.W RDSTSGN- ADDTOP ; INF + NUM
DC.W RDSTSGN- ADDTOP ; INF + 0
DC.W ADDINF - ADDTOP ; INF + INF
;-----------------------------------------------------------
; First align so value in DST position has smaller exponent.
; Then shift with special cases. "Larger" digits are in D3/A2.
; Assume SRC exponent is "larger", so swap its significand with DST.
;-----------------------------------------------------------
ADDNUM:
EXG D4,D3
EXG D5,A2
MOVE.W A4,D0 ; SRC exponent
SUB.W A3,D0 ; SRC exp - DST exp
BEQ ADDEM ; no shift if exponents equal
BGT.S @1 ; just shift DST in D4/5
;-----------------------------------------------------------
; SRC exp < DST exp, so swap operands. Move DST sign to bit
; 7 of D6 byte without moving the XOR (bit 5)
;-----------------------------------------------------------
EXG D5,A2 ; swap significands
EXG D4,D3
MOVEA.L A3,A4 ; larger exponent in A4
ADD.B D6,D6 ; shift src sign out
NEG.W D0 ; negate exponent difference
ASR.B #1,D6 ; duplicate DST sign and restore XOR to bit 5
;-----------------------------------------------------------
; Do fast right shift of D4/D5 into D4/D5/D7.
;-----------------------------------------------------------
@1:
MOVE.L D0,D2 ; D2 used to dispatch to fast shift routine
CMPI #66,D0
BHI ADDSHLOTS ; special fast routine if > 66 bits to shift
ADD D2,D2 ; jmp to appropriate routine
MOVE ADDSHCASE(D2),D2
JMP ADDNUM(D2)
ADDSHCASE:
DC.W ADDEM - ADDNUM
DC.W ADDSH1 - ADDNUM
DC.W ADDSH2 - ADDNUM
DC.W ADDSH3 - ADDNUM
DC.W ADDSH4 - ADDNUM
DC.W ADDSH5 - ADDNUM
DC.W ADDSH6 - ADDNUM
DC.W ADDSH7 - ADDNUM
DC.W ADDSH8 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH16 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH2TO31 - ADDNUM
DC.W ADDSH32 - ADDNUM
DC.W ADDSH33 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH33TO63 - ADDNUM
DC.W ADDSH64 - ADDNUM
DC.W ADDSH65 - ADDNUM
DC.W ADDSH66 - ADDNUM
ADDSHLOTS:
MOVEQ #0,D4
MOVEQ #0,D5
NOT.B D7
BRA ADDEM
ADDSH66:
OR D4,D5
TST.L D5
SNE D7
LSR.L #2,D4
BRA.S BIGSTICK
ADDSH65:
OR D4,D5
TST.L D5
SNE D7
LSR.L #1,D4
BRA.S BIGSTICK
ADDSH64:
TST.L D5
SNE D7 ; gather stickies
BIGSTICK:
OR.L D4,D7
MOVEQ #0,D4
MOVEQ #0,D5
BRA ADDEM ; fix stickies
;-----------------------------------------------------------
; Shift of 33-63 bits
;-----------------------------------------------------------
ADDSH33TO63:
MOVE.L D5,D7 ; D7 <- D5
SUBI #32,D0 ; decr shift count by 32
MOVE.L D4,D5 ; D5 <- D4
ROR.L D0,D7 ; rotate D7 right by new count
LSR.L D0,D5 ; shift D5 right by new count
BFTST D7{0:D0} ; test for low sticky
BEQ.S @3
ORI #$FF,D7
@3:
BFINS D4,D7{0:D0} ; shift high sticky bits in
MOVEQ #0,D4 ; zero D4
BRA ADDEM ; fix stickies
ADDSH33:
MOVE.L D5,D7 ; shift right 32 bits
MOVE.L D4,D5
MOVEQ #0,D4
LSR.L #1,D5 ; shift right one more bit
ROXR.L #1,D7
SCS D7 ; keep low sticky
BRA ADDEM ; fix stickies
ADDSH32:
MOVE.L D5,D7 ; 32-bit shift is easy
MOVE.L D4,D5
MOVEQ #0,D4
BRA ADDEM ; fix stickies
;-----------------------------------------------------------
; Shift of 2-31 bits
;-----------------------------------------------------------
ADDSH2TO31:
BFINS D5,D7{0:D0} ; shift bits into D7 from D5
LSR.L D0,D5 ; shift D5 right
BFINS D4,D5{0:D0} ; shift bits into D5 from D4
LSR.L D0,D4 ; shift D4
BRA.S ADDEM
;-----------------------------------------------------------
; Special quick shift routines
;-----------------------------------------------------------
ADDSH16:
MOVE.W D5,D7
MOVE.W D4,D5
CLR.W D4
SWAP D5
SWAP D4
SWAP D7
BRA.S ADDEM
ADDSH8:
BFINS D5,D7{0:8}
MOVE.B D4,D5
LSR.L #8,D4
ROR.L #8,D5
BRA.S ADDEM
ADDSH7:
BFINS D5,D7{0:7}
LSR.L #7,D5
BFINS D4,D5{0:7}
LSR.L #7,D4
BRA.S ADDEM
ADDSH6:
BFINS D5,D7{0:6}
LSR.L #6,D5
BFINS D4,D5{0:6}
LSR.L #6,D4
BRA.S ADDEM
ADDSH5:
BFINS D5,D7{0:5}
LSR.L #5,D5
BFINS D4,D5{0:5}
LSR.L #5,D4
BRA.S ADDEM
ADDSH4:
BFINS D5,D7{0:4}
LSR.L #4,D5
BFINS D4,D5{0:4}
LSR.L #4,D4
BRA.S ADDEM
ADDSH3:
BFINS D5,D7{0:3}
LSR.L #3,D5
BFINS D4,D5{0:3}
LSR.L #3,D4
BRA.S ADDEM
ADDSH2:
BFINS D5,D7{0:2}
LSR.L #2,D5
BFINS D4,D5{0:2}
LSR.L #2,D4
BRA.S ADDEM
ADDSH1:
LSR.L #1,D4
ROXR.L #1,D5
ROXR.L #1,D7
;-----------------------------------------------------------
; Operands are now aligned; test for +/- then do it
;-----------------------------------------------------------
ADDEM:
BTST #5,D6 ; test XOR of signs
BNE.S SUBMAG
;-----------------------------------------------------------
; Add magnitudes: add the words and check for carry-out.
;-----------------------------------------------------------
ADD.L A2,D5
ADDX.L D3,D4
BCC COERCE
ROXR.L #1,D4 ; adjust right
ROXR.L #1,D5
ROXR.L #1,D7 ; no stickies can be lost
SCS D1
OR.B D1,D7
ADDQ.L #1,A4 ; bump exponent
@15:
BRA COERCE
;-----------------------------------------------------------
; Simplify by subtracting large op in D3,A2 from small in
; D4,5,7 and then checking for special cases. If zero, jump
; out to 0+0 code. If greater, flip sign. If less (usual)
; just negate.
;-----------------------------------------------------------
SUBMAG:
NOT.B D6 ; assume >, with sign change
SUB.L A2,D5
SUBX.L D3,D4
BEQ.S ZEROSUM ; store zero with sign
BCC NORMCOERCE ; got it right
NEG.L D7 ; flip digits
NEGX.L D5
NEGX.L D4
NOT.B D6 ; flip sign back
@7:
BRA NORMCOERCE
;-----------------------------------------------------------
; Now set exponent to 0 and fix sign according to rounding
; mode.
; In the special case of two 0's, avoid the underflow.
; Coercion will signal in single/double restriction.
;-----------------------------------------------------------
ADD00:
BTST #5,D6 ; same sign?
BEQ PACKX ; yes, easy
ZEROSUM:
SUBA.L A4,A4 ; 0 exp
MOVEA.W #FPSTATE,A1 ; A1 points to environment
CLR.B D6 ; assume positive
BTST #RNDHI,(A1) ; 10 -- RND MINUS
BEQ.S ADDQ00
BTST #RNDLO,(A1)
BNE.S ADDQ00
NOT.B D6 ; make negative
ADDQ00:
BRA PACKX ; don't coerce 0
;-----------------------------------------------------------
; If DST = 0, have result = SRC. If SRC=0, must set
; result = DST.
;-----------------------------------------------------------
ADDS0:
MOVE.L A2,D5 ; LO DIGS
MOVE.L D3,D4 ; HI DIGS
MOVE.L A3,A4 ; EXP
ADD.B D6,D6 ; SIGN
ADDD0:
BRA COERCE
;-----------------------------------------------------------
; Sum of two INFs depends on their signs.
;-----------------------------------------------------------
ADDINF:
BTST #5,D6 ; same sign?
BNE.S @25
BRA PACKX ; yes
@25:
MOVEQ #NANADD,D0 ; mark error
BRA INVALIDOP
;-----------------------------------------------------------
;-----------------------------------------------------------
; FP MULTIPLICATION
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QMULX---Multiply extended with extended
;-----------------------------------------------------------
QMULX:
FINIT
LEA MULTOP,A0 ; continue below
BRA UNPACKXX ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; QMULB---Multiply non-extended SRC with extended DST
;-----------------------------------------------------------
QMULB:
FINIT
LEA MULTOP,A0 ; continue below
BRA UNPACKXB ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; Multiply 2 numbers. Dispatch to one of 9 cases depending on
; class of operands: [finite] number, zero, or infinite.
; NaNs have already been filtered out.
;-----------------------------------------------------------
MULTOP:
LEA FINI2OPS,A0 ; continuation address
LSL.B #2,D6 ; get XOR of signs in D6 bit 7
MOVE.W MULCASE(D0),D0
JMP MULTOP(D0)
MULCASE: ; DST * SRC
DC.W MULNUM - MULTOP ; NUM * NUM
DC.W PACKX - MULTOP ; NUM * 0
DC.W PACKX - MULTOP ; NUM * INF
DC.W RDST - MULTOP ; 0 * NUM
DC.W PACKX - MULTOP ; 0 * 0
DC.W INVMUL - MULTOP ; 0 * INF
DC.W RDST - MULTOP ; INF * NUM
DC.W INVMUL - MULTOP ; INF * 0
DC.W PACKX - MULTOP ; INF * INF
MULNUM:
;-----------------------------------------------------------
; Have: X.XXXXX * Y.YYYYYY --> ZZ.ZZZZZZZ before
; normalization and coercion. So subtract (bias-1) to
; account for binary point one bit to right. For example,
; 1 * 1 comes out: 2^1 * 0.10000000... which in turn
; is normalized to 2^0 * 1.000000...
;-----------------------------------------------------------
ADDA.L A3,A4 ; add exponents
SUBA.W #$3FFE,A4 ; subtract (bias - 1)
;-----------------------------------------------------------
; Multiply is a D register hog, so some state must be saved.
;
; 64*64 multiply is accomplished in 4 32*32 products, using
; the MULU.L 32*32 instruction of the MC68020.
;
; Special provisions are made for the three special cases:
; both operands have 32 trailing zeros or any one operand
; has 32 trailing zeros.
;
; The basic register mask throughout is:
; A1: D6 save
; A2,A3: SRC bits
; A4: result exponent
; D0,D1: used to pass operands to 32*32 mult and return results
; D2,3: DST bits
; D4,5,7: 64-bit product and round bits
; D6: zero
;-----------------------------------------------------------
MOVE.L D3,D2 ; D2 <- DST.HI
MOVE.L A2,D3 ; D3 <- DST.LO
MOVEA.L D6,A1 ; save D6 in A1
MOVEA.L D4,A2 ; A2 <- SRC.HI
MOVEA.L D5,A3 ; A3 <- SRC.LO
MOVEQ #0,D7 ; exact at first
MOVEQ #0,D6 ; D6 <- 0
MOVE.L A2,D5 ; D4/5 = SRC.HI * DST.HI
MULU.L D2,D4:D5
MOVE.L A3,D7 ; SRC.LO * DST.HI
BEQ.S HILO ; skip if SRC.LO = 0
MULU.L D2,D0:D7 ; RESULT.LO in D7
ADD.L D0,D5 ; RESULT.HI added to D4/5
ADDX.L D6,D4
HILO:
MOVE.L D3,D0 ; SRC.HI * DST.LO
BEQ.S MULDONE ; done if DST.LO = 0
MOVE.L A2,D1
MULU.L D0,D0:D1
ADD.L D1,D7 ; result added to D4/5/7
ADDX.L D0,D5
ADDX.L D6,D4
;-----------------------------------------------------------
; Fourth 32*32 product is SRC.LO * DST.LO. High result is
; added into D7 with carry propagating through D5/4. Nonzero
; low result causes low order stickies to be set in D7.
;-----------------------------------------------------------
MOVE.L A3,D1 ; SRC.LO
BEQ.S MULDONE ; done if SRC.LO = 0
MULU.L D3,D0:D1
ADD.L D0,D7 ; add to D7
ADDX.L D6,D5 ; propagate carry to D4/5
ADDX.L D6,D4
TST.L D1 ; set low sticky if D1 != 0
SNE D1
OR.B D1,D7
;-----------------------------------------------------------
; Clean up after multiplication. Restore D6.
;-----------------------------------------------------------
MULDONE:
MOVE.L A1,D6 ; restore D6
BRA NORMCOERCE ; normalize if necessary & coerce
;-----------------------------------------------------------
; Cases 0*INF and INF*0 are invalid operations.
;-----------------------------------------------------------
INVMUL:
MOVEQ #NANMUL,D0 ; NaN code in D0
BRA INVALIDOP ; Output NaN and signal invalid
;-----------------------------------------------------------
;-----------------------------------------------------------
; FP DIVISION
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QDIVX---Divide extended DST by extended SRC
;-----------------------------------------------------------
QDIVX:
FINIT
LEA DIVTOP,A0 ; continue below
BRA UNPACKXX ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; QDIVB---Divide extended DST by non-extended SRC
;-----------------------------------------------------------
QDIVB:
FINIT
LEA DIVTOP,A0 ; continue below
BRA UNPACKXB ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; Division. Dispatch to one of 9 cases depending on
; class of operands: [finite] number, zero, or infinite.
; NaNs have already been filtered out.
;-----------------------------------------------------------
DIVTOP:
LEA FINI2OPS,A0 ; continuation address
LSL.B #2,D6 ; XOR of signs in D6 bit 7
MOVE.W DIVTBL(D0),D0
JMP DIVTOP(D0)
DIVTBL: ; DST / SRC
DC.W DIVNUM - DIVTOP ; NUM / NUM
DC.W DIVBY0 - DIVTOP ; NUM / 0
DC.W DIVBYI - DIVTOP ; NUM / INF
DC.W DIVBYI - DIVTOP ; 0 / NUM
DC.W INVDIV - DIVTOP ; 0 / 0
DC.W DIVBYI - DIVTOP ; 0 / INF
DC.W RDST - DIVTOP ; INF / NUM
DC.W RDST - DIVTOP ; INF / 0
DC.W INVDIV - DIVTOP ; INF / INF
;-----------------------------------------------------------
; Case NUM/0: set the DIV-BY-ZERO error bit, stuff INF, pack.
;-----------------------------------------------------------
DIVBY0:
BSET #ERRZ+8,D6
MOVEA.W #$7FFF,A4 ; stuff infinity
MOVEQ #0,D4
MOVEQ #0,D5
BRA PACKX
;-----------------------------------------------------------
; Cases NUM/INF, 0/NUM, and 0/INF: store zero.
;-----------------------------------------------------------
DIVBYI:
SUBA.L A4,A4 ; ZERO EXP
MOVE.L A4,D4 ; AND DIGS...
MOVE.L D4,D5
BRA PACKX
;-----------------------------------------------------------
; Cases 0/0 and INF/INF: signal invalid and store NaN.
;-----------------------------------------------------------
INVDIV:
MOVEQ #NANDIV,D0 ; NaN code in D0
BRA INVALIDOP ; output NaN result
;-----------------------------------------------------------
; Dividing numbers involves the nonrestoring divide subroutine
; DIV32 shared with the REMAINDER algorithm. This subroutine
; essentially calculates 32 bits of quotient of a 64 / 64
; division and also returns a shifted remainder.
;-----------------------------------------------------------
DIVNUM:
;-----------------------------------------------------------
; FIGURE RESULT EXPONENT AS THOUGH DST >= SRC. WILL COMPUTE
; AN EXTRA QUOTIENT BIT JUST IN CASE DST < SRC, IN WHICH
; CASE EXP WILL BE DECREMENTED.
;-----------------------------------------------------------
EXG A3,A4 ; SWAP EXPS
SUBA.L A3,A4 ; DEXP - SEXP
ADDA.W #$3FFF,A4 ; REBIAS
;-----------------------------------------------------------
; DST >= SRC: 64+1 QUO BITS, LAST IS ROUND.
; DST < SRC: 64+1 QUO BITS, FIRST IS 1, LAST IS ROUND.
; TRICK: IN ORDER TO GET EXTRA (ROUND) BIT IN D4,5, LET
; LEADING BIT (KNOWN TO BE 1) BE SHIFTED OUT OF
; D4,5 DURING DIVISION. THEN PUT IT BACK ON RETURN.
; USE SPECIAL CASE STARTUP CODE TO DISTINGUISH THE DST < SRC
; CASE THAT REQUIRES TWEAKS OF REMAINDER AND EXPONENT.
;
; Set up funny register mask for nonrestoring division
; A2 - quotient high longword
; A3 - D6 save
; A4 - exponent of result
; D2,D3 - dividend cum shifted remainder
; D4,D5 - divisor
; D1 - holds 0
; scratch registers are D0,D6,D7
; *** NOTE CAN DO BETTER ON FIRST STEP BECAUSE OF TEST ABOVE
;-----------------------------------------------------------
DIVNONRESTORING:
MOVE.L D6,A3 ; save D6 contents in A3 for duration
MOVE.L D3,D2 ; D2 <- DST.HI
MOVE.L A2,D3 ; D3 <- DST.LO
MOVEQ #0,D1 ; D1 <- 0
MOVE.L D2,D6 ; save DST.HI for case DST < SRC below
SUB.L D5,D3 ; get leading 1 bit in quotient via subtraction
SUBX.L D4,D2 ; of SRC (divisor) from DST (dividend)
BCC.S BGNDV ; DST >= SRC; begin division steps
SUBQ.L #1,A4 ; DST < SRC; decrement exponent and
ADD.L A2,D3 ; correct remainder by adding DST to it
ADDX.L D6,D2
BGNDV:
BSR.S DIV32 ; get first quotient longword
MOVE.L D0,A2 ; save in A2
BSR.S DIV32 ; get second quotient longword
MOVE.L D0,D5 ; put quotient in D4/D5
MOVE.L A2,D4
MOVE.L A3,D6 ; restore D6
;-----------------------------------------------------------
; Clean up prior to return.
; Remainder is in D2/D3 and shifted quotient is in D4/5.
; Adjust quotient and put round/stickies in D7.
;-----------------------------------------------------------
MOVEQ #1,D7 ; it's almost zero
LSR.L #1,D7 ; D7 = 0 and X bit set
ROXR.L #1,D4 ; shift leading 1 bit into quotient
ROXR.L #1,D5
ROXR.L #1,D7 ; shift round bit into D7.W
OR.L D2,D3 ; test all remainder bits
SNE D7 ; set stickies if nonzero
BRA COERCE ; coerce result
;-----------------------------------------------------------
; Subroutine DIV32 calculates a 32-bit quotient from a 64-bit
; dividend and a 64-bit divisor. It also returns a shifted
; (by 32 bits) remainder. This subroutine uses the MULU.L and
; DIVU.L instructions of the MC68020.
;
; D2,D3 - dividend cum shifted remainder
; D1 - bits to be shifted into low half of remainder (usually zero)
; D4,D5 - divisor
; D0 - 32-bit quotient
; D6,D7 - scratch registers
;-----------------------------------------------------------
DIV32:
DIVU.L D4,D2:D3 ; divide step (64-bit / 32-bit)
BVS.S DIVOFL ; rare overflow handler
MOVE.L D3,D0 ; initialize quotient word
MOVE.L D3,D7 ; multiply quotient by rest
MULU.L D5,D6:D7 ; of divisor (32 bits)
CTNDIV:
MOVE.L D1,D3 ; shifted remainder in D1/D2
SUB.L D7,D3 ; subtract correction from remainder
SUBX.L D6,D2
BCC.S DIVOK ; OK if no carry
ONEMORE:
SUBQ.L #1,D0 ; correction produced carry; decr quotient
ADD.L D5,D3 ; and adjust remainder upward until positive
ADDX.L D4,D2
BCC.S ONEMORE
DIVOK:
RTS ; return
;-----------------------------------------------------------
; Division has produced an overflow (very rare case). Fix
; it up.
;-----------------------------------------------------------
DIVOFL:
MOVE.L D5,D6 ; DIVU.L overflow
MOVEQ #0,D7 ; set D6/7 to $100000000 * D5
MOVE.L D3,D2 ; simulate remainder for quotient of $100000000
MOVEQ #0,D0 ; quotient effectively $100000000
BRA.S CTNDIV ; adjust remainder and quotient
;-----------------------------------------------------------
;-----------------------------------------------------------
; FP REMAINDER
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QREMX---Extended DST REM extended SRC
;-----------------------------------------------------------
QREMX:
FINIT
LEA REMTOP,A0 ; continue below
BRA UNPACKXX ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; QREMB---Extended DST REM non-extended SRC
;-----------------------------------------------------------
QREMB:
FINIT
LEA REMTOP,A0 ; continue below
BRA UNPACKXB ; unpack opword and operands, NaN check
;-----------------------------------------------------------
; ******** IMPORTANT STACK DEPENDENCY -- SEE BELOW ********
;
; THE REMAINDER OPERATION DIVIDES DST/SRC TO GET ----ALL---
; QUOTIENT BITS (POSSIBLY THOUSANDS OF THEM) AND THEN
; RETURNS THE RESULTING REMAINDER, REDUCED TO LESS THAN OR
; EQUAL TO (1/2)*DVR. IT ALSO RETURNS THE SIGN AND LOW
; SEVEN INTEGER QUOTIENT BITS IN REGISTER D0.W AS A
; TWO'S-COMPLEMENT INTEGER. THIS KLUGE IS
; EXTREMELY USEFUL FOR ELEMENTARY FUNCTION EVALUATION
; WHERE, SAY, REMAINDER BY (PI/4) IS NOT USEFUL WITHOUT
; AN INDICATION OF THE OCTANT (GIVEN BY THE QUOTIENT) AS
; WELL AS THE REMAINDER.
;
; TO GET THE PROPERLY REDUCED QUOTIENT, IT IS EASIEST TO
; DIVIDE ALL THE WAY THROUGH THE FIRST FRACTION QUOTIENT
; BIT, AND THEN PATCH UP. IF THE QUOTIENT TURNS OUT TO BE
; ZERO, ITS SIGN IS ARBITRARILY SET TO THAT OF THE DST.
;
; Integer quotient reduction is accomplished in one of two
; ways, depending upon the difference in exponent value for
; the two operands. For small values (< 9) of this difference,
; a standard restoring division algorithm is used. For larger
; values, repeated calls are made to the DIV32 subroutine,
; which chews off 32 bits of quotient at a time. In the latter
; case, the original dividend is preshifted to accommodate extra
; bits (MOD 32)
;
; ASSUME THE MASK: DO-ARITHMETIC, WITH D7=0 FOR THE
; CCR AND ROUND INFO.
;
; Some assumptions about the stack are necessary.
; When the registers were saved with the MOVEM.L, D0 was
; left at the top of stack. Thus, DO.W, which gets the
; integer quotient, is at 2(SP).
;-----------------------------------------------------------
;-----------------------------------------------------------
; DO SOME BOOKKEEPING FIRST. PLACE DEFAULT 0 QUO IN D0.
; ASSUME THE RESULT WILL HAVE DST SIGN, AND NOTE THAT QUO
; SIGN IS MOVED TO BIT #6 OF D6.
;-----------------------------------------------------------
REMTOP:
LEA FINI2OPS,A0 ; continuation addr
CLR.W 2(SP) ; QUO set to 0 (D0.W on stack)
ADD.B D6,D6 ; move DST sign to D6 bit 7 and
; QUO sign to bit 6
MOVE.W REMCASE(D0),D0
JMP REMTOP(D0)
REMCASE: ; DST REM SRC
DC.W REMNUM - REMTOP ; NUM REM NUM
DC.W INVREM - REMTOP ; NUM REM 0
DC.W REMDST - REMTOP ; NUM REM INF
DC.W RDST - REMTOP ; 0 REM NUM
DC.W INVREM - REMTOP ; 0 REM 0
DC.W RDST - REMTOP ; 0 REM INF
DC.W INVREM - REMTOP ; INF REM NUM
DC.W INVREM - REMTOP ; INF REM 0
DC.W INVREM - REMTOP ; INF REM INF
;-----------------------------------------------------------
; Invalid REMAINDER operands
;-----------------------------------------------------------
INVREM:
MOVEQ #NANREM,D0 ; NaN code in D0
BRA INVALIDOP ; output NaN result
;-----------------------------------------------------------
; DEXP - SEXP + 1 = NUMBER OF INTEGER QUO BITS. GET ONE
; MORE TO AID IN ROUNDING. CASES ON (DEXP - SEXP + 1):
; >= 0 -- RUN DIVDE AND RESTORE TO GET THOSE BITS
; < 0 -- DST IS ALREADY LESS THAN HALF SRC, SO JUST
; COERCE (AND QUO = 0).
;-----------------------------------------------------------
REMNUM:
MOVE.L A3,D0 ; DST EXP
ADDQ.L #1,D0
SUB.L A4,D0 ; DEXP - SEXP + 1
BPL.S REMDIV ; MUST DO IT ALL...
REMDST:
MOVE.L D3,D4 ; RESULT IS DST
MOVE.L A2,D5
MOVEA.L A3,A4
BRA.S REMFIN
;-----------------------------------------------------------
; Set tentative REM exponent to SEXP-1, since REM will be reduced
; to at most half of SRC. Then determine from size of exponent
; difference in D0 which algorithm to use.
;-----------------------------------------------------------
REMDIV:
SUBQ.L #1,A4 ; tentative exponent
CMPI.L #9,D0
BGT.S REMSHIFT ; many integer bits to chew off
;-----------------------------------------------------------
; OFF TO RESTORE WITH ITS REGISTER MASK:
; D0: MAGNITUDE COUNT D1,D2: DIVIDEND
; D4,D5: QUOTIENT D3,A2: DIVISOR
;-----------------------------------------------------------
MOVE.L D3,D1 ; DST IS DIVIDEND
MOVE.L A2,D2
MOVE.L D4,D3 ; SRC IS DIVISOR
MOVEA.L D5,A2
ADDQ.L #1,D0 ; INITIALIZE LOOP COUNT
BSR.S RESTORE
;-----------------------------------------------------------
; AFTER ALL QUOTIENT BITS AND FIRST FRACTION BIT HAVE BEEN
; EVALUATED INTO D4,5 (LEADING BITS ARE LOST OFF THE LEFT)
; THERE ARE THREE CASES ("REM" IS RESULT OF DIV LOOP):
;
; LOW QUO BIT = 0 --> REM < HALF DVR, ALL DONE
;
; LOW QUO BIT = 1 AND REM = 0 --> HALF-WAY CASE, WHERE
; SIGN OF REM (= HALF DIVISOR) IS DETERMINED
; SO LOW INT QUO BIT WILL BE 0
;
; LOW QUO BIT = 1 AND REM > 0 --> TRUE REM > HALF DVR,
; SO FLIP SIGN AND SUBTACT. THIS IS TRICKY
; AND RATHER NONINTUITIVE. THE POINT IS THAT
; DIVIDING THROUGH TO THE FIRST FRAC QUO BIT
; REDUCES THE EXP OF REM TO DVR-1; BUT THE
; DIV ALGORITHM DOES NOT SHIFT ON THE LAST
; STEP, SO THE REM LINES UP PROPERLY WITH
; THE DVR FOR THE SUBTRACTION (THOUGH THEIR
; EXPONENTS SEEM TO DIFFER BY ONE). AND THE
; DIV ALGORITHM GUARANTEES THAT THE REM IT
; LEAVES IS LESS THAN THE DVR, SO THERE CAN
; BE NO CARRY OUT.
;-----------------------------------------------------------
REMPOSTRESTORING:
BTST #0,D5 ; LOW QUO BIT
BEQ.S REMQUO ; 0 --> JUST STUFF QUO
TST.L D1
BNE.S @3 ; CASE 3
TST.L D2
BNE.S @3 ; CASE 3
BTST #1,D5 ; CASE 2 DECIDED ON LO INT
BEQ.S @5 ; IF EVEN, LEAVE QUO BUT SET REM
@3:
BCHG #7,D6 ; FLIP REM SIGN
ADDQ.W #2,D5 ; INCREMENT QUO BY 1 (IN SECOND BIT)
@5:
EXG D2,A2 ; SWAP DVR AND REM
EXG D1,D3
SUB.L A2,D2 ; DVR - REM
SUBX.L D3,D1
;-----------------------------------------------------------
; NOW EXTRACT LOW 7 INTEGER BITS (REMEMBER GOT FIRST FRAC),
; NEGATE IF NECESSARY, EXTEND TO WORD, AND STORE.
;-----------------------------------------------------------
REMQUO:
LSR.B #1,D5 ; KILL FRAC BIT
BTST #6,D6 ; TEST QUO SIGN
BEQ.S @9
NEG.B D5
@9:
EXT.W D5 ; EXTEND SIGNED BYTE TO WORD
MOVE.W D5,2(SP) ; STORE IN SAVED D0.W
MOVE.L D1,D4 ; STUFF REM BITS
MOVE.L D2,D5
REMFIN:
BRA ZNORMCOERCEX ; STORE THE RESULT
;-----------------------------------------------------------
; ASSUME FUNNY REGISTER MASK: RESTORING-DIVISION
; D0 - QUO BIT COUNT
; D1,2 - DIVIDEND CUM REMAINDER
; D3,A2 - DIVISOR (CAN ADD, NOT ADDX FROM A-REG)
; D4,5 - WILL BE QUOTIENT
;-----------------------------------------------------------
RESTORE:
MOVEQ #0,D4 ; CLEAR QUOTIENT
MOVE.L D4,D5
BRA.S @2 ; SKIP SHIFT ON 1ST STEP
@1:
ADD.L D5,D5 ; SHIFT QUO
ADDX.L D4,D4 ; IGNORE CARRY ON LAST STEP
ADD.L D2,D2 ; SHIFT REM
ADDX.L D1,D1
BCS.S @4 ; HAVE TO SUBTRACT
@2:
CMP.L D3,D1 ; DVD.HI - DVR.HI
BNE.S @3
CMP.L A2,D2
@3:
BCS.S @5 ; SKIP SUB IF DVD < DVR
@4:
ADDQ.B #1,D5 ; SET QUO BIT (NO CARRY)
SUB.L A2,D2
SUBX.L D3,D1
@5:
SUBQ.W #1,D0 ; LOOP COUNT
BNE.S @1
RTS
;-----------------------------------------------------------
; Remainder algorithm using DIV32 subroutine handles larger
; exponent differences much faster than RESTORE algorithm.
;
; D2/D3/D1: dividend/shifted remainder (96 bits)
; D4/D5: divisor
; D0/D6/D7: scratch
; A1: loop count
;-----------------------------------------------------------
REMSHIFT:
MOVEA.L D6,A3 ; A3 <- D6 value
MOVE.L A2,D1 ; D1 <- dividend.LO
MOVEQ #0,D2 ; D2 <- 0
MOVEA.L D0,A2 ; A2 <- shift count (ÆEXP + 1)
ANDI.L #$1F,D0 ; D0 <- alignment shift count (0 to 31 possible)
BEQ.S @1 ; if zero, do first DIV32
; shift dividend in D3/D1 left into D2/D3/D1
BFEXTU D3{0:D0},D2 ; shift bits from D3 high to D2 low
LSL.L D0,D3 ; shift D3 left
BFEXTU D1{0:D0},D6 ; extract D1 high bits
LSL.L D0,D1 ; shift D1 left
OR.L D6,D3 ; insert extracted D1 bits into shifted D3
;-----------------------------------------------------------
; Do initial division of D2:D3:D1 by D4/D5. 32-bit quotient in
; D0, remainder (shifted by 32 bits) in D2/D3.
;-----------------------------------------------------------
@1:
BSR DIV32
;-----------------------------------------------------------
; Remaining number of REM steps, if any, are done 32 at a time,
; using DIV32. Final D0 value is lowest 32 bits of the
; quotient, and the REM result is in D2/D3.
;-----------------------------------------------------------
MOVE.L A2,D6 ; Get number of remaining 32-bit steps
BFEXTU D6{16:11},D6
BEQ.S REMDIVDONE ; If zero, clean up
MOVE.L D6,A1 ; A1 <- # OF 32-bit REM steps
MOVEQ #0,D1 ; zero trailing remainder bits
REMLP32:
BSR DIV32
SUBQ.L #1,A1
MOVE.L A1,D6
BNE.S REMLP32
REMDIVDONE:
MOVE.L D2,D1 ; remainder to D1/D2
MOVE.L D3,D2
MOVE.L D4,D3 ; divisor to D3/A2
MOVEA.L D5,A2
MOVE.L D0,D5 ; quotient to D5
MOVE.L A3,D6 ; restore A6
BRA REMPOSTRESTORING ; finish up REM
;-----------------------------------------------------------
;-----------------------------------------------------------
; FP COMPARISONS---Common entry points for routines which
; signal or do not signal on unordered.
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QCMPX---Compare two extendeds.
;-----------------------------------------------------------
QCMPX:
FINIT
LEA CMPTOP,A0 ; continue below
BRA UNPACKXX ; unpack opword and operands, unordered check
;-----------------------------------------------------------
; QCMPB---Compare a non-extended SRC with an extended DST
;-----------------------------------------------------------
QCMPB:
FINIT
LEA CMPTOP,A0 ; continue below
BRA UNPACKXB ; unpack opword and operands, unordered check
;-----------------------------------------------------------
; Comparison. Dispatch to one of 9 cases depending on
; class of operands: [finite] number, zero, or infinite.
; NaNs have already been filtered out.
;-----------------------------------------------------------
CMPTOP:
MOVE.W CMPCASE(D0),D0
JMP CMPTOP(D0)
CMPCASE: ; DST - SRC
DC.W CMPNUM - CMPTOP ; NUM - NUM
DC.W CMPS0 - CMPTOP ; NUM - 0
DC.W CMPD0 - CMPTOP ; NUM - INF
DC.W CMPD0 - CMPTOP ; 0 - NUM
DC.W CMP0 - CMPTOP ; 0 - 0
DC.W CMPD0 - CMPTOP ; 0 - INF
DC.W CMPS0 - CMPTOP ; INF - NUM
DC.W CMPS0 - CMPTOP ; INF - 0
DC.W CMPINF - CMPTOP ; INF - INF
;-----------------------------------------------------------
; NUM VS. 0: DISGUISE AS (0 VS. -NUM) AND FALL THROUGH.
;-----------------------------------------------------------
CMPS0:
ADD.B D6,D6 ; DST SGN -> SRC SLOT
NOT.B D6
;-----------------------------------------------------------
; 0 VS. NUM: SIGN OF NUM DETERMINES >.
;-----------------------------------------------------------
CMPD0:
MOVEQ #CMPG,D7 ; ASSUME >
TST.B D6 ; TST SRC SIGN
BMI.S @1
MOVEQ #CMPL,D7 ; 0 < POSITIVE
@1:
BRA FINICMPS
;-----------------------------------------------------------
; INF VS. INF: EITHER =, OR SAME AS 0 VS. NUM.
;-----------------------------------------------------------
CMPINF:
BTST #5,D6 ; EQ -> SIGNS =
BNE.S CMPD0
CMP0: ; 0 = 0 regardless of sign
MOVEQ #CMPE,D7
BRA FINICMPS
;-----------------------------------------------------------
; NUM VS. NUM: IF SIGNS DIFFER, SAME AS 0 VS. NUM.
; IF SAME JUST COMPARE THE WORDS, TAKING ACCOUNT FOR COMMON
; SIGN.
;-----------------------------------------------------------
CMPNUM:
BTST #5,D6 ; NE -> TRIVIAL
BNE.S CMPD0
CMPA.L A4,A3 ; DST - SRC EXP'S
BGT.S @1
BLT.S @2
CMP.L D4,D3 ; DST.HI - SRC.HI
BHI.S @1 ; HI -> UNSIGNED GREATER
BCS.S @2 ; CS -> UNSIGNED LESS
CMPA.L D5,A2
BEQ.S CMP0 ; THEY ARE =
BCS.S @2
;-----------------------------------------------------------
; THEY'RE > UNLESS NEGATIVE.
;-----------------------------------------------------------
@1:
NOT.B D6
;-----------------------------------------------------------
; THEY'RE < UNLESS NEGATIVE.
;-----------------------------------------------------------
@2:
MOVEQ #CMPL,D7
TST.B D6
BPL FINICMPS
MOVEQ #CMPG,D7
@21:
BRA FINICMPS
;-----------------------------------------------------------
;-----------------------------------------------------------
; CONVERSION to extended format
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QX2X---Convert extended to extended
;-----------------------------------------------------------
QX2X:
FINIT
LEA FINI2OPS,A0 ; finish up routine
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 LKADR2(A6),A1 ; unpack extended SRC
BSR UNPXOP
C2XCOMMON:
MOVEQ #0,D7 ; zero D7
TST.W D0 ; check if NaN input
BNE FPNANOUT ; output NaN
TST.L D0 ; check if zero or INF
BEQ COERCE ; coerce if not
BRA PACKX ; pack if zero or INF
;-----------------------------------------------------------
; QB2X---Convert non-extended binary format to extended
;-----------------------------------------------------------
QB2X:
FINIT
LEA FINI2OPS,A0 ; finish up routine
MOVE.W LKOP(A6),D6 ; get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6 ; zero D6.LO
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 nonextended SRC
BSR UNPBOP
BRA.S C2XCOMMON
;-----------------------------------------------------------
;-----------------------------------------------------------
; FP SQUARE ROOT
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QSQRTX---extended square root of the extended DST operand
;-----------------------------------------------------------
QSQRTX:
FINIT
LEA FINI1OP,A0 ; finish up routine
MOVE.W LKOP(A6),D6 ; get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6
MOVEA.L LKADR1(A6),A1 ; unpack extended DST
BSR UNPXOP
TST.L D0 ; check if finite nonzero input
BEQ.S @10 ; yes; check sign next
TST.W D0 ; check if NaN input
BNE FPNANOUT ; yes; output NaN
BTST #17,D0 ; check if zero
BNE PACKX ; yes; SQRT(+/-0) = +/- 0
TST.B D6 ; INF input
BPL PACKX ; pack if +INF
@1:
MOVEQ #NANSQRT,D0 ; invalid op: SQRT(negative nonzero number)
BRA INVALIDOP
@10:
TST.B D6 ; test sign of finite, nonzero input
BMI.S @1 ; negative; invalid op
;--------------------------------------------------------------------
; Square root of a normalized positive extended number is evaluated
; using a modified version of the K. C. Johnson algorithm. This bit
; chop method attempts to find the largest extended number whose square
; is no larger than the radicand (operand). The rood is evaluated to
; 65 bits in order to determine the rounding bit, and the sticky bit is
; obtained from any residual remainder.
;
; The basic register mask is:
; radicand/remainder significand: D1.B D2 D3
; square root exponent: A4.W
; square root significand: D0.B D4 D5
; loop: bit mask/counter D6
; scratch: D7
; D6 save register: A3
;
; First step is to halve the exponent and adjust the bias, keeping
; track of whether the true exponent is odd or even. Cases, after
; right shift are:
; C=1---(2K) + $3FFF -> K + 1FFF, so rebias by $2000
; C=0---(2K + 1) + $3FFF -> K + 2000, so rebias by $1FFF
; and shift radicand 1 extra bit left.
;--------------------------------------------------------------------
MOVE.W A4,D0 ; divide exponent by 2
ASR.W #1,D0
MOVE SR,D7 ; save carry for later
BCC.S @2
ADDQ.W #1,D0
@2:
ADDI.W #$1FFF,D0 ; rebias
MOVEA.W D0,A4 ; store result exponent
;--------------------------------------------------------------------
; Initialize radicand for rooting. Use A3 as temp for D6
;--------------------------------------------------------------------
MOVEA.L D6,A3 ; save D6
MOVE.L D5,D3 ; radicand in D2/D3 initially
MOVE.L D4,D2
;--------------------------------------------------------------------
; Now shift radicand to align binary point between D0 and D2.
; Requires 1 shift for even exp, 2 shifts for odd, for which
; we saved flags above in D7.
;--------------------------------------------------------------------
ADD.L D3,D3
ADDX.L D2,D2
MOVEQ #1,D1
MOVE D7,CCR ; CARRY=0 -> odd exp -> extra shift
BCS.S @4
ADD.L D3,D3
ADDX.L D2,D2
ADDX.W D1,D1
;--------------------------------------------------------------------
; Now initialize remainder by subtracting 1 from D1.B
;--------------------------------------------------------------------
@4:
SUBQ.W #1,D1
;--------------------------------------------------------------------
; Initialize root in D0.B (low bit only), D4/D5. After J
; iterations of the bit chop loop, these 65 bits will hold:
; <CURRENT ROOT (J BITS)>0, followed by 64 - J zeros.
; At the end of each iteration, bit J+1 (from MSB) is evaluated:
; <NEW ROOT (J+1 BITS)>0, followed by 63 - J zeros.
;
; Since the root will be normalized, we can bypass the first iteration
; of the loop and initialize the root for the second iteration:
; D0 <- 1, D4 <- 0, AND D5 <- 0.
;--------------------------------------------------------------------
MOVEQ #1,D0
MOVEQ #0,D4
MOVE.L D4,D5
;--------------------------------------------------------------------
; Initialize bit mask D6 to $40000000
;--------------------------------------------------------------------
MOVE.L #$40000000,D6
BRA.S @8 ; branch into loop
;--------------------------------------------------------------------
; Top of loop for high longword (D4)
;--------------------------------------------------------------------
@6:
ADD.L D3,D3 ; double remainder
ADDX.L D2,D2
ADDX.W D1,D1
@8:
OR.L D6,D4 ; create <CURRENT ROOT>01000....
;--------------------------------------------------------------------
; Try remainder - root (short version since D5 = 0)
;--------------------------------------------------------------------
SUB.L D4,D2
SUBX.W D0,D1
BCC.S @11 ; no carry -> new root bit = 1
ADD.L D4,D2 ; carry -> new root bit = 0
ADDX.W D0,D1 ; restore previous positive remainder
EOR.L D6,D4 ; clr final 1 bit in D4
BRA.S @12
@11:
ADD.L D6,D4
@12:
LSR.L #1,D6 ; shift mask bit right for next iteration
BCC.S @6
;--------------------------------------------------------------------
; Evaluate 33rd bit by brute force (transition between D4 and D5).
; Set D6 = $80000000.
;--------------------------------------------------------------------
MOVE.L #$80000000,D6 ; D6 <- $80000000
ADD.L D3,D3 ; double remainder
ADDX.L D2,D2
ADDX.W D1,D1
SUB.L D6,D3 ; remainder - root
SUBX.L D4,D2
SUBX.W D0,D1
BCC.S @14 ; no carry; set D4 bit 0
ADD.L D6,D3 ; carry; restore positive remainder
ADDX.L D4,D2
ADDX.W D0,D1
BRA.S @16 ; D4 bit 0 remains clear
@14:
ADDQ.W #1,D4
;--------------------------------------------------------------------
; Quick exit if remainder after 33 bits is zero
;--------------------------------------------------------------------
@16:
MOVE.L D3,D7 ; current remainder zero?
OR.W D1,D7
OR.L D2,D7
BNE.S @18 ; no; do 33 more bits (inexact with sticky set)
LSR.W #1,D0 ; yes; shift result into D4/D5
ROXR.L #1,D4
ROXR.L #1,D5
BRA.S ROOTDONE ; finish up SQRT with round/stickies clear
;--------------------------------------------------------------------
; Set up for 34th-64th bits of root (D5 bits 31 through 1)
;--------------------------------------------------------------------
@18:
ROR.L #1,D6 ; bit mask (D6) = $40000000
;--------------------------------------------------------------------
; Loop for root bits 31 through 1 in D5
;--------------------------------------------------------------------
@20:
ADD.L D3,D3 ; double remainder
ADDX.L D2,D2
ADDX.W D1,D1
OR.L D6,D5 ; create <CURRENT ROOT>010000...
SUB.L D5,D3 ; remainder - root (wider subtraction)
SUBX.L D4,D2
SUBX.W D0,D1
BCC.S @22 ; no carry; new root bit = 1
ADD.L D5,D3 ; carry; restore positive remainder
ADDX.L D4,D2
ADDX.W D0,D1
EOR.L D6,D5 ; zero trailing 1 bit in D5
BRA.S @24 ; new root bit remains clear
@22:
ADD.L D6,D5
@24:
LSR.L #1,D6 ; update bit mask
BCC.S @20
;--------------------------------------------------------------------
; Evaluate round bit by brute force. First shift root 1 bit right
; into D4/D5. Next force the setting of the X bit. Then subtract
; (with extend) D7(zero)/D4/D5 from unshifted remainder in D1/D2/D3.
; Set round bit if no carry results. Sticky bits will always be set
; because root is irrational.
;--------------------------------------------------------------------
MOVEQ #0,D7 ; clr D7
ADDQ.W #1,D5 ; set lowest bit
LSR.W #1,D0 ; shift 64-bit root into D4/D5,
ROXR.L #1,D4 ; setting X bit on final shift
ROXR.L #1,D5
SUBX.L D5,D3 ; subtract root from unshifted remainder
SUBX.L D4,D2 ; with initial borrow
SUBX.W D7,D1
BCS.S @26 ; carry -> round bit is zero
BSET #31,D7 ; no carry -> round bit is one
@26:
ADD.W #$00FF,D7 ; set sticky bits
ROOTDONE:
MOVE.L A3,D6 ; restore D6
BRA COERCE ; coerce result
;-----------------------------------------------------------
;-----------------------------------------------------------
; ROUND TO INTEGER
;-----------------------------------------------------------
;-----------------------------------------------------------
QRINTX:
FINIT
LEA FINI1OP,A0 ; finish up routine
MOVE.W LKOP(A6),D6 ; get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6
MOVEA.L LKADR1(A6),A1 ; unpack extended DST
BSR UNPXOP
TST.L D0 ; check if finite nonzero input
BEQ.S @1 ; yes; do rounding
TST.W D0 ; check if NaN input
BNE FPNANOUT ; yes; output NaN
BRA PACKX ; no; zero or INF propagate
@1:
BSR IPALIGN ; align binary point to right of D5
BGT.S RINT1 ; shift was done
COMINT1: ; no shift was done. Coercion will do rounding.
BFTST D1{25:2} ; check rounding precision
BEQ COERCEX ; default (extended)
BPL COERCED ; double
BRA COERCES ; single
RINT1: ; round extended integral value
BSR RNDINT
TST.L D4 ; check if normalized
BMI PACKX ; done if so
;-----------------------------------------------------------
; After shifting and rounding, may have 0 or unnormalized. Normalize, then
; stuff result
;-----------------------------------------------------------
COMINT2:
BFFFO D4{0:0},D0 ; find first one in D4
BEQ.S @5 ; D4 = 0
SUBA.W D0,A4 ; decrease exponent
LSL.L D0,D4 ; shift D4 left
BFEXTU D5{0:D0},D2 ; extract D5 high bits
LSL.L D0,D5 ; shift D5 left
OR.L D2,D4 ; put D5 high bits into D4 low
BRA PACKX ; stuff result
@5:
SUBA.W #32,A4 ; D4 = 0, decrease exponent by 32
EXG D4,D5 ; exchange D4/D5
BFFFO D4{0:0},D0 ; find first one in D4
BEQ.S @7 ; zero result
BMI PACKX ; normalized; stuff result
SUBA.W D0,A4 ; subnormal; decrease exponent
LSL.L D0,D4 ; shift D4 left
BRA PACKX ; stuff result
@7:
SUBA.L A4,A4 ; zero result
BRA PACKX ; stuff result
;-----------------------------------------------------------
;-----------------------------------------------------------
; TRUNCATE TO INTEGER
;-----------------------------------------------------------
;-----------------------------------------------------------
QTINTX:
FINIT
LEA FINI1OP,A0 ; finish up routine
MOVE.W LKOP(A6),D6 ; get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6
MOVEA.L LKADR1(A6),A1 ; unpack extended DST
BSR UNPXOP
TST.L D0 ; check if finite nonzero input
BEQ.S @1 ; yes; do rounding
TST.W D0 ; check if NaN input
BNE FPNANOUT ; yes; output NaN
BRA PACKX ; no; zero or INF propagate
@1:
;-----------------------------------------------------------
; Align binary point to right of D5 and chop to integer value,
; setting INEXACT if necessary. If number is too big,
; force chop mode and coerce. IPALIGN places environment in
; D1.W.
;-----------------------------------------------------------
BSR.S IPALIGN ; align binary pt to right of D5
BGT.S @3 ; shift was done
OR.W #$6000,D1 ; big number, force truncation mode
BRA COMINT1
@3:
TST.L D7 ; flag inexact if any stickies set
BEQ.S COMINT2
BSET #ERRX+8,D6
BRA.S COMINT2 ; exit through normalization routine
;-----------------------------------------------------------
; Subroutine IPALIGN sets up binary point no further right than 24,
; 53, 64 bits as specified by the coercion info. The environment
; word is read into D1.W. Scratch register are D0/D2
;-----------------------------------------------------------
;IPALIGN: ; old IPALIGN and IALIGN DELETED <3/23/92, JPO>
; MOVE.W (FPSTATE).W,D1 ; environment in D1
; BFTST D1{25:2} ; check rounding precision
; BEQ.S IALIGN ; default (extended)
; BMI.S @1 ; single
;
; MOVEQ #52,D0 ; double precision
; BRA.S FINALIGN
;@1:
; MOVEQ #23,D0
; BRA.S FINALIGN
;IALIGN:
; MOVEQ #63,D0
;FINALIGN:
; MOVEQ #0,D7 ; zero D7
; ADDI.W #$3FFF,D0 ; adjust exponent
; MOVE.W D0,D2 ; save possible new exponent
; SUB.L A4,D0 ; INT exp - exp
; BGT.S @7
; RTS ; return LE in CCR if too big
;@7:
; MOVEA.W D2,A4 ; use new exponent
; BSR RTSHIFT
; MOVE #0000,CCR ; fudge CCR = GT
; RTS
;-----------------------------------------------------------
; Subroutine IPALIGN sets up binary point just to the right of D5
; (64th significand bit) by unnormalizing input value in A4/D4/D5
; if its magnitude is less than a limit determined by the current
; rounding precision.
;
; INPUT: normalized extended value in A4/D4/D5
; OUTPUT: CCR value LE (nonzero) and D7 cleared if input too large and
; CCR value GT (zero) if right shift took place and
; unnormalized (shifted) value in A4/D4/D5 with
; fractional stickies in D7.
; Environment setting in D1.W in both cases
; USES: D0
;-----------------------------------------------------------
IPALIGN: ; new IPALIGN and IALIGN ADDED <3/23/92, JPO>
MOVE.W (FPSTATE).W,D1 ; environment in D1
BFTST D1{25:2} ; check rounding precision
BEQ.B IALIGN ; default (extended)
BMI.B @1 ; single
MOVE.W #$4033,D0 ; double precision: set exponent limit (biased 52)
BRA.B @2
@1: ; single precision
MOVE.W #$4016,D0 ; set exponent limit for single precision (biased 23)
@2:
MOVEQ #0,D7 ; zero stickies
CMP.W A4,D0 ; is magnitude small enough for right shifting?
BGT.B @3 ; yes
RTS ; no. return with "LE" in CCR
@3:
MOVE.W #$403E,D0 ; yes. D0 <- count for right shifting of
SUB.W A4,D0 ; binary point to right of D5
BRA.B DOSHIFT
IALIGN: ; extended rounding precision
MOVE.W #$403E,D0 ; D0 <- exponent limit (biased 63)
MOVEQ #0,D7 ; zero stickies
SUB.W A4,D0 ; D0 <- right shift count
BGT.B DOSHIFT ; do shift
RTS ; magnitude too big. return with "LE" in CCR
DOSHIFT: ; unnormalize via right shift, putting fractional part into D7
MOVEA.W #$403E,A4 ; set post-shift exponent (biased 63)
BSR RTSHIFT ; do the shift (count in D0): D4/D5 -> D4/D5/D7
MOVE.W #$0000,CCR ; fudge "GT" in CCR
RTS ; return
;-----------------------------------------------------------
; Subroutine RNDINT rounds a subnormal significand in D4/D5
; and D7 (stickies) according to the environment in D1.W
; Scratch register are D0/D2
;-----------------------------------------------------------
RNDINT:
TST.L D7 ; exact result?
BNE.S @1 ; no
RTS ; yes; done.
;-----------------------------------------------------------
; Inexact result: signal and round
;-----------------------------------------------------------
@1:
BSET #ERRX+8,D6 ; signal inexact
BFTST D1{17:2} ; round to nearest?
BEQ.S @4 ; yes
BMI.S @2 ; chop or round downward
;-----------------------------------------------------------
; Round toward +°
;-----------------------------------------------------------
TST.B D6 ; bump significand if positive
BPL.S RNDUPI
RTS ; otherwise, done
@2:
BTST #13,D1 ; chop or round downward?
BEQ.S @3 ; downward
RTS ; done if chop
;-----------------------------------------------------------
; Round toward -°
;-----------------------------------------------------------
@3:
TST.B D6 ; bump significand if negative
BMI.S RNDUPI
RTS ; otherwise, return
;-----------------------------------------------------------
; Default rounding (to nearest)
;-----------------------------------------------------------
@4:
ADD.L D7,D7 ; round bit set?
BCS.S @5 ; yes
RTS ; no; done
@5:
BNE.S RNDUPI ; stickies set so round up
BTST #0,D5 ; halfway case gets bumped
BNE.S RNDUPI ; if lowest SIG bit is 1
RTS ; otherwise, done
RNDUPI:
MOVEQ #0,D0 ; increment significand
ADDQ.L #1,D5
ADDX.L D0,D4 ; cannot carry out of D4
RTS ; done
;-----------------------------------------------------------
;-----------------------------------------------------------
; CONVERSIONS from extended format
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QX2B---Convert extended SRC to nonextended DST
;-----------------------------------------------------------
QX2B:
FINIT
LEA FINI2OPS,A0 ; finish up routine
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 LKADR2(A6),A1 ; unpack extended SRC
BSR UNPXOP
MOVEQ #26,D7 ; extract offset from format
MOVE.L D6,D1 ; code in opword
LSR.L D7,D1
ANDI.W #$000E,D1
SUBQ.W #2,D1
MOVEQ #0,D7
MOVE.W X2BCASE(D1),D1 ; get addr of specific routine
JMP QX2B(D1) ; and jump there
X2BCASE:
DC.W QX2D - QX2B ; double precision
DC.W QX2S - QX2B ; single precision
DC.W QX2X - QX2B ; ---illegal format
DC.W QX2I - QX2B ; int16
DC.W QX2L - QX2B ; int32
DC.W QX2C - QX2B ; comp
;-----------------------------------------------------------
; QX2D---convert extended SRC to double precision DST
;-----------------------------------------------------------
QX2D:
MOVEA.L LKADR1(A6),A2 ; A2 <- DST addr for PACKD
TST.L D0 ; finite nonzero input?
BEQ.S @1 ; yes; must coerce
TST.W D0 ; no; is it a NaN?
BEQ.S PACKD ; zero or INF need no coercion
ANDI.W #$0F800,D5
BSR FPNANIN ; NaN input needs valid code
BRA.S PACKD ; and no coercion
@1:
MOVE.W (FPSTATE).W,D1 ; D1.W <- environment
BTST #6,D1 ; Coerce to double precision
BEQ.S @3 ; unless single precision is set
BSR SCOERCE ; in the environment
BRA.S PACKD
@3:
BSR DCOERCE
;-----------------------------------------------------------
; PACKD---pack and deliver a double precision result to (A2)
;-----------------------------------------------------------
PACKD:
MOVE.W A4,D3 ; D3.W <- exponent
CMPI.W #$7FFF,D3 ; INF or NAN?
BNE.S @1 ; no
MOVE.W #$4400,D3 ; exp too big, will decr below
BRA.S @5
@1:
TST.W D3 ; exp = 0?
BNE.S @5
MOVE.W #$3C01,D3
@5:
SUBI.W #$3C00,D3
TST.L D4 ; test lead bit
BMI.S @7 ; decr exp unless normalized
SUBQ.W #1,D3
@7:
;-----------------------------------------------------------
; SET UP LOW 32 BITS WITH TRAILING 11 BITS FROM HI BITS.
;-----------------------------------------------------------
LSR.L #8,D5 ; shift low half right 11 bits
LSR.L #3,D5
BFINS D4,D5{0:11} ; insert low 11 bits of high half
LSR.L #8,D4 ; shift high half right 10 bits
LSR.L #2,D4
BFINS D3,D4{0:11} ; insert exponent, killing lead bit
ADD.B D6,D6 ; insert sign
ROXR.L #1,D4
MOVE.L D4,(A2)+ ; write result
MOVE.L D5,(A2)
JMP (A0) ; continuation routine
;-----------------------------------------------------------
; QX2S---convert extended SRC to single precision DST
;-----------------------------------------------------------
QX2S:
MOVEA.L LKADR1(A6),A2 ; A2 <- DST addr for PACKS
TST.L D0 ; finite nonzero input?
BEQ.S @1 ; yes; must coerce
TST.W D0 ; no; is it a NaN?
BEQ.S PACKS ; zero or INF need no coercion
MOVEQ #0,D5
CLR.B D4
BSR FPNANIN ; NaN input needs valid code
BRA.S PACKS ; and no coercion
@1:
MOVE.W (FPSTATE).W,D1 ; D1.W <- environment
BSR SCOERCE ; coerce to single precision
;-----------------------------------------------------------
; PACKS---pack and deliver a single precision result to (A2)
;-----------------------------------------------------------
PACKS:
MOVE.W A4,D3 ; D3.W <- exponent
CMPI.W #$7FFF,D3 ; INF or NAN?
BNE.S @1 ; no
MOVE.W #$4080,D3 ; exp too big, will decr below
BRA.S @5
@1:
TST.W D3 ; exp = 0?
BNE.S @5
MOVE.W #$3F81,D3
@5:
SUBI.W #$3F80,D3
ADD.L D4,D4 ; kill lead bit and test
BCS.S @7 ; decr exponent unless normalized
SUBQ.W #1,D3
@7:
OR.W D3,D4 ; stuff exponent in low bits
ROR.L #8,D4 ; rotate to high bits
ADD.B D6,D6 ; insert sign
ROXR.L #1,D4
MOVE.L D4,(A2) ; deliver result
JMP (A0) ; continuation routine
;-----------------------------------------------------------
; QX2C---convert extended SRC to comp DST
;-----------------------------------------------------------
QX2C:
TST.L D0 ; any special input?
BEQ.S @3 ; no, normalized value
BTST #17,D0 ; zero?
BNE.S PACKC ; yes, deliver it
TST.B D0 ; NaN input?
BNE.S @2 ; yes, invalid flag OK
@1: ; invalid result (INF, comp overflow)
BSET #ERRI+8,D6 ; set invalid flag
BCLR #ERRX+8,D6 ; clr inexact
@2:
MOVEQ #1,D4 ; deliver COMP NaN
MOVEQ #0,D5
ROR.L #1,D4
BRA.S PACKC ; deliver result
@3:
BSR IALIGN ; align binary point to right of D5
BLE.S @1 ; overflow
MOVE.W (FPSTATE).W,D1 ; round to integer value
BSR RNDINT
TST.L D4 ; overflow?
BMI.S @1 ; yes
TST.B D6 ; negative?
BPL.S PACKC ; no; done
NEG.L D5 ; yes; make it so
NEGX.L D4
PACKC: ; deliver the comp result to DST addr
MOVEA.L LKADR1(A6),A2
MOVE.L D4,(A2)+
MOVE.L D5,(A2)
JMP (A0) ; finish up
;-----------------------------------------------------------
; QX2L---convert extended SRC to long integer DST
;-----------------------------------------------------------
QX2L:
TST.L D0 ; any special input?
BEQ.S @3 ; no, normalized value
BTST #17,D0 ; zero?
BNE.S PACKL ; yes, deliver it
@1: ; invalid result (INF, NaN, longint overflow)
MOVEQ #1,D5 ; deliver default result ($80000000)
BSET #ERRI+8,D6 ; set invalid flag
ROR.L #1,D5
BCLR #ERRX+8,D6 ; clr inexact
BRA.S PACKL ; deliver result
@3:
BSR IALIGN ; align binary point to right of D5
BLE.S @1 ; overflow
MOVE.W (FPSTATE).W,D1 ; round to integer value
BSR RNDINT
TST.L D4 ; overflow?
BNE.S @1 ; yes
TST.L D5
BPL.S @5 ; no overflow
NEG.L D5 ; most likely
BPL.S @1 ; overflow
TST.B D6 ; $80000000 OK if negative integer
BPL.S @1
@5
TST.B D6 ; negative?
BPL.S PACKL ; no; done
NEG.L D5 ; yes; make it so
PACKL: ; deliver the longint result to DST addr
MOVEA.L LKADR1(A6),A2
MOVE.L D5,(A2)
JMP (A0) ; finish up
;-----------------------------------------------------------
; QX2I---convert extended SRC to 16-bit integer DST
;-----------------------------------------------------------
QX2I:
TST.L D0 ; any special input?
BEQ.S @3 ; no, normalized value
BTST #17,D0 ; zero?
BNE.S PACKI ; yes, deliver it
@1: ; invalid result (INF, NaN, overflow)
MOVE.W #$8000,D5 ; deliver default result ($8000)
BSET #ERRI+8,D6 ; set invalid flag
BCLR #ERRX+8,D6 ; clr inexact
BRA.S PACKI ; deliver result
@3:
BSR IALIGN ; align binary point to right of D5
BLE.S @1 ; overflow
MOVE.W (FPSTATE).W,D1 ; round to integer value
BSR RNDINT
TST.L D4 ; overflow?
BNE.S @1 ; yes
CMPI.L #$08000,D5
BCS.S @5 ; no overflow
BHI.S @1 ; overflow
TST.B D6 ; $8000 OK if negative integer
BPL.S @1
@5
TST.B D6 ; negative?
BPL.S PACKI ; no; done
NEG.W D5 ; yes; make it so
PACKI: ; deliver the longint result to DST addr
MOVEA.L LKADR1(A6),A2
MOVE.W D5,(A2)
JMP (A0) ; finish up
;-----------------------------------------------------------
;-----------------------------------------------------------
; QLOGBX---binary logarithm [ DST <- logb(DST) ]
;-----------------------------------------------------------
QLOGBX:
FINIT
LEA FINI1OP,A0 ; finish up routine
MOVE.W LKOP(A6),D6 ; get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6
MOVEA.L LKADR1(A6),A1 ; unpack extended DST
BSR UNPXOP
CLR.B D6 ; sign is irrelevant
TST.L D0 ; check if finite nonzero input
BEQ.S @10 ; yes; evaluate logb
TST.W D0 ; check if NaN input
BNE FPNANOUT ; yes; output NaN
BTST #17,D0 ; check if zero input
BEQ PACKX ; no; INF propagates (with + sign)
;-----------------------------------------------------------
; LOGB(+-0) --> DIV BY ZERO --> ERROR BIT, STUFF -INF, RET.
;-----------------------------------------------------------
ORI.W #$0880,D6 ; POOR MAN'S BSET'S
MOVEA.W #$7FFF,A4 ; BIG EXP
MOVEQ #0,D4 ; ZERO DIGS
MOVE.L D4,D5
BRA PACKX
;-----------------------------------------------------------
; LOGB(finite and nonzero) --> exponent, normalized as a
; floating-point number. Never exceptional. Uses fast
; normalization.
;-----------------------------------------------------------
@10:
MOVEQ #0,D5 ; clear the low significant bits
SUBA.W #$3FFF,A4 ; unbias exponent
MOVE.L A4,D4 ; move as integer
BGT.S @12 ; positive
BLT.S @11 ; negative
SUBA.L A4,A4 ; zero result
MOVE.L D5,D4
BRA PACKX
@11:
ORI.B #$80,D6 ; negative; set sign
NEG.L D4 ; magnitude of value
@12:
MOVEA.W #$401E,A4 ; exponent = 31, biased
BFFFO D4{0:0},D0 ; find first one bit in D4
LSL.L D0,D4 ; shift left to normalize
SUBA.W D0,A4 ; adjust exponent
BRA PACKX
;-----------------------------------------------------------
;-----------------------------------------------------------
; QSCALBX---binary scaling [ DST <- DST * 2**SRC ]
;-----------------------------------------------------------
;-----------------------------------------------------------
QSCALBX:
FINIT
LEA FINI2OPS,A0 ; finish up routine
MOVE.W LKOP(A6),D6 ; get opword into D6.HI
MOVEQ #0,D0 ; and zero D0
SWAP D6
CLR.W D6
MOVEA.L LKADR1(A6),A1 ; unpack extended DST
BSR UNPXOP
TST.L D0 ; check if finite nonzero input
BEQ.S @1 ; yes; evaluate scalb
TST.W D0 ; check if NaN input
BNE FPNANOUT ; yes; output NaN
BRA PACKX ; zero and INF propagate with sign
;-----------------------------------------------------------
; SCALB is evaluated by adding the integer adjustment into
; the exponent in A4 and checking for over/underflow via the
; COERCE routine.
;-----------------------------------------------------------
@1:
MOVEA.L LKADR2(A6),A2 ; SRC addr
ADDA.W (A2),A4 ; adjust exponent
MOVEQ #0,D7 ; zero stickies
BRA COERCE ; coerce
;-----------------------------------------------------------
;-----------------------------------------------------------
; CLASSIFY routines place an integer code of SRC class at
; the DST address. Classify is unexceptional. Codes are:
; SNAN 1
; QNAN 2
; INF 3
; ZERO 4
; NORMAL 5
; SUBNORM 6
;-----------------------------------------------------------
;-----------------------------------------------------------
;-----------------------------------------------------------
; QCLASSX---DST <- class of extended SRC
;-----------------------------------------------------------
QCLASSX:
MOVEM.L A0/D0-D1,-(SP) ; save small # of registers
MOVEA.L 22(SP),A0 ; SRC addr
MOVE.W (A0)+,D0 ; get sign/exp in D0.W
BTST #5,17(SP) ; 96-bit extended SRC?
BEQ.S @1 ; no
ADDQ #2,A0 ; yes, bump pointer to significand
@1:
ADD.L D0,D0 ; sign in D0 bit 16
LSR.W #1,D0 ; positive exp in D0.W
BSET #31,D0 ; sign flag is D0 bit 31
BCLR #15,D0 ; clr sign in exp
CMPI.W #$7FFF,D0 ; max exp?
BEQ.S @5 ; yes, NAN or INF class
MOVE.L (A0)+,D1 ; normalized?
BPL.S @8 ; zero or unnormalized
@2:
MOVEQ #5,D1 ; NORMAL
@3:
BTST #16,D0 ; negate class code if
BEQ.S @4 ; sign bit is set
NEG.W D1
@4:
MOVEA.L 18(SP),A0 ; DST addr
MOVE.W D1,(A0) ; deliver classify result
MOVEM.L (SP)+,A0/D0-D1 ; restore registers
RTD #STKREM2 ; done
@5: ; INF or NAN class
MOVE.L (A0)+,D1 ; read high half of significand
ADD.L D1,D1 ; clr explicit bit of sig
LSR.L #1,D1
BEQ.S @7 ; INF or SNAN class since sig high is zero
BTST #QNANBIT,D1 ; QNAN or SNAN
BEQ.S @6 ;
MOVEQ #2,D1 ; QNAN
BRA.S @3
@6:
MOVEQ #1,D1 ; SNAN
BRA.S @3
@7:
MOVE.L (A0),D1 ; INF or SNAN?
BNE.S @6 ; SNAN
MOVEQ #3,D1 ; INF
BRA.S @3
@8:
BEQ.S @11 ; ZERO or SUBNORM
@9:
SUBQ.W #1,D0 ; normalization loop
ADD.L D1,D1
BPL.S @9
@10:
TST.W D0 ; negative exponent means SUBNORM
BPL.S @2 ; nonnegative means NORMAL
MOVEQ #6,D1 ; SUBNORM
BRA.S @3
@11: ; high significand is zero
SUB.W #32,D0 ; decrease exponent
MOVE.L (A0),D1 ; D1 <- low significand
BEQ.S @12 ; ZERO
BPL.S @9 ; still unnormalized
BRA.S @10 ; check exponent
@12:
MOVEQ #4,D1 ; ZERO
BRA.S @3
;-----------------------------------------------------------
; QCLASSB---DST <- class of single/double/comp SRC
; X bit trick thanks to S. McDonald
;-----------------------------------------------------------
QCLASSB:
MOVEM.L A0/D0-D1,-(SP) ; save small # of registers
MOVE.W 16(SP),D0 ; D0.W <- opcode
MOVEA.L 22(SP),A0 ; A0 <- SRC addr
BTST #11,D0 ; double precision classify?
BNE.S QCLASSD ; yes
BTST #13,D0 ; classify comp?
BNE.S QCLASSC ; yes
;-----------------------------------------------------------
; Classify single precision SRC
;-----------------------------------------------------------
MOVE.L (A0),D0 ; D0 <- SRC
ADD.L D0,D0 ; sign in X bit with shifted SRC (S. McDonald)
BEQ.S CLBZ ; ZERO
BFEXTU D0{0:8},D1 ; D1.B <- exponent
BEQ.S CLBSUB ; SUBNORMAL
CMPI.B #$FF,D1
BNE.S CLBN ; NORMAL
BFTST D0{8:23} ; NAN or INF
BEQ.S CLBI ; INF
BTST #23,D0 ; SNAN or QNAN
BNE.S CLBQN ; QNAN
BRA.S CLBSN
;-----------------------------------------------------------
; QCLASSD --- classify double precision SRC
;-----------------------------------------------------------
QCLASSD:
MOVE.L (A0)+,D0 ; D0 <- high half of SRC
OR.W (A0)+,D0 ; OR low half of SRC into D0.W
OR.W (A0),D0 ; (S. McDonald)
ADD.L D0,D0 ; save sign in X bit (SRC shifted)
BEQ.S CLBZ ; ZERO
BFEXTU D0{0:11},D1 ; D1 low <- exponent
BEQ.S CLBSUB ; SUBNORMAL
CMPI.W #$07FF,D1
BNE.S CLBN ; NORMAL
BFTST D0{11:20} ; NAN or INF
BEQ.S CLBI ; INF
BTST #20,D0 ; SNAN or QNAN
BEQ.S CLBSN ; SNAN
CLBQN:
MOVEQ #2,D0 ; QNAN case
BRA.S CLBOUT ; exit
CLBZ:
MOVEQ #4,D0 ; ZERO case
BRA.S CLBOUT
CLBSUB:
MOVEQ #6,D0 ; SUBNORMAL case
BRA.S CLBOUT
CLBN:
MOVEQ #5,D0 ; NORMAL case
CLBOUT: ; common exit code
MOVEA.L 18(SP),A0 ; DST addr
ROXR.W #1,D1 ; get sign bit back from X
BPL.S @1
NEG.W D0 ; negate class code if SRC negative
@1:
MOVE.W D0,(A0)
MOVEM.L (SP)+,A0/D0-D1 ; restore registers
RTD #STKREM2 ; done
CLBI:
MOVEQ #3,D0 ; INF case
BRA.S CLBOUT
CLBSN:
MOVEQ #1,D0 ; SNAN case
BRA.S CLBOUT
;-----------------------------------------------------------
; QCLASSC --- classify comp SRC (only NORM, ZERO, or QNAN
; are possible)
;-----------------------------------------------------------
QCLASSC:
MOVE.L (A0)+,D0 ; D0 <- high half of comp
ADD.L D0,D0 ; sign bit shifted out of D0 into X
OR.L (A0),D0 ; OR in low half of comp
BNE.S CLBN ; NORMAL with sign in X
ADDX.W D0,D0 ; test and clear X bit
BEQ.S CLBZ ; ZERO
BRA.S CLBQN ; QNAN
;-----------------------------------------------------------
;-----------------------------------------------------------
; QSETENV---set environment to word value at DST addr
;-----------------------------------------------------------
;-----------------------------------------------------------
QSETENV:
MOVE.L A0,-(SP) ; save single register
MOVEA.L 10(SP),A0 ; DST addr
MOVE.W (A0),(FPSTATE).W ; set environment
MOVEA.L (SP)+,A0 ; restore register
RTD #STKREM1 ; done
;-----------------------------------------------------------
;-----------------------------------------------------------
; QGETENV---write environment word to DST addr
;-----------------------------------------------------------
;-----------------------------------------------------------
QGETENV:
MOVE.L A0,-(SP)
MOVEA.L 10(SP),A0
MOVE.W (FPSTATE).W,(A0) ; get environment
MOVEA.L (SP)+,A0
RTD #STKREM1
;-----------------------------------------------------------
;-----------------------------------------------------------
; QSETHV---set haltvector to longword at DST addr
;-----------------------------------------------------------
;-----------------------------------------------------------
QSETHV:
MOVE.L A0,-(SP) ; save single register
MOVEA.L 10(SP),A0 ; DST addr
MOVE.L (A0),(FPSTATE+2).W ; set haltvector
MOVEA.L (SP)+,A0 ; restore register
RTD #STKREM1 ; done
;-----------------------------------------------------------
;-----------------------------------------------------------
; QGETHV---write haltvector to DST addr
;-----------------------------------------------------------
;-----------------------------------------------------------
QGETHV:
MOVE.L A0,-(SP)
MOVEA.L 10(SP),A0
MOVE.L (FPSTATE+2).W,(A0) ; get haltvector
MOVEA.L (SP)+,A0
RTD #STKREM1
;-----------------------------------------------------------
;-----------------------------------------------------------
; QNEG---negate floating point number at DST addr
;-----------------------------------------------------------
;-----------------------------------------------------------
QNEG:
MOVE.L A0,-(SP)
MOVEA.L 10(SP),A0 ; DST addr
BCHG #7,(A0) ; negate sign bit
MOVEA.L (SP)+,A0
RTD #STKREM1
;-----------------------------------------------------------
;-----------------------------------------------------------
; QABS---absolute value of floating point number at DST addr
;-----------------------------------------------------------
;-----------------------------------------------------------
QABS:
MOVE.L A0,-(SP)
MOVEA.L 10(SP),A0 ; DST addr
BCLR #7,(A0) ; clear sign bit
MOVEA.L (SP)+,A0
RTD #STKREM1
;-----------------------------------------------------------
;-----------------------------------------------------------
; QCPYSGN---copies sign of DST operand to SRC operand
;-----------------------------------------------------------
;-----------------------------------------------------------
QCPYSGN:
MOVEM.L A0,-(SP) ; stack: A0sv < ret < opword < &DST < &SRC
MOVEA.L 14(SP),A0 ; A0 <- &SRC
BCLR #7,(A0) ; clr sign of SRC
TST.B ([10,SP]) ; test sign of DST
BPL.S @1
BSET #7,(A0) ; DST is negative, so echo sign in SRC
@1:
MOVEA.L (SP)+,A0
RTD #STKREM2
;-----------------------------------------------------------
;-----------------------------------------------------------
; QPROCENTRY---saves environment word at DST address, then
; sets default environment
;-----------------------------------------------------------
;-----------------------------------------------------------
QPROCENTRY:
MOVE.L A0,-(SP) ; stack: A0sv < ret < opword < &DST
MOVEA.L 10(SP),A0 ; DST addr
MOVE.W (FPSTATE).W,(A0) ; save environment in DST
CLR.W (FPSTATE).W ; set default environment
MOVEA.L (SP)+,A0
RTD #STKREM1
;-----------------------------------------------------------
;-----------------------------------------------------------
; QTESTXCP---test an exception whose index is in low byte
; of word DST. Set result of test (1 for TRUE and 0 for FALSE)
; in high byte of DST.
;-----------------------------------------------------------
;-----------------------------------------------------------
QTESTXCP:
MOVEM.L D0/A0,-(SP)
MOVEA.L 14(SP),A0
MOVE.W (A0),D0 ; fetch input index
BTST D0,(FPSTATE).W ; test exception bit in high
SNE D0 ; byte of environment word
NEG.B D0
MOVE.B D0,(A0) ; boolean result in DST high byte
MOVEM.L (SP)+,D0/A0
RTD #STKREM1
;-----------------------------------------------------------
; QSETXCP---set an exception whose index is in low byte
; of word DST and halt if enabled.
;-----------------------------------------------------------
QSETXCP:
MOVEM.L D0/D6/A0,-(SP) ; Use 3 register
MOVE.W #$0015,D6 ; opword into D6.HI
SWAP D6
CLR.W D6 ; clr D6.LO
MOVEA.L 18(SP),A0 ; DST addr
MOVE.W (A0),D0 ; fetch exception index
ADDQ.W #8,D0 ; align to second byte
BSET D0,D6 ; set exception in D6.W
MOVE.W (FPSTATE).W,D0 ; environment into D0.W
BRA.S FASTFIN ; share back end with PROCEXIT routine
;-----------------------------------------------------------
; QPROCEXIT---restore old environment at DST address with
; current exceptions ORed in. Halt if any of the current
; exceptions are enabled in the old environment. Back end
; is shared with SETEXCEPTION routine.
;-----------------------------------------------------------
QPROCEXIT:
MOVEM.L D0/D6/A0,-(SP) ; Use 3 registers
MOVEA.L 18(SP),A0 ; old environment addr
MOVE.W #$0019,D6 ; opword into D6.HI
SWAP D6
MOVE.W #$1F00,D6 ; exception mask in D6.W
AND.W ($0A4A).W,D6 ; current exceptions in D6.W
MOVE.W (A0),D0 ; old environment into D0.W for restoration
FASTFIN:
OR.W D6,D0 ; OR new exceptions with old environment
MOVE.W D0,($0A4A).W ; store resulting environment
LSR.W #8,D6 ; check for halt
AND.W D6,D0
BNE.S FASTHALT ; handle halt
FASTEX:
MOVE D0,CCR ; zero CCR
MOVEM.L (SP)+,D0/D6/A0 ; restore registers
RTD #STKREM1 ; done
;-----------------------------------------------------------
; Fast halt vectoring routine for SETEXCEPTION and PROCEXIT
;-----------------------------------------------------------
FASTHALT:
LEA 18(SP),A0 ; A0 points to DST addr on stack
CLR.W -(SP) ; push CCR = 0 below D0 save
MOVE.W D0,-(SP) ; push HALT exceptions
PEA (SP) ; push MISCHALTINFO record pointer
MOVE.L 8(A0),-(SP) ; push bogus SRC2 addr
MOVE.L 4(A0),-(SP) ; push bogus SRC addr
MOVE.L (A0),-(SP) ; push DST addr
SWAP D6 ; push opword
MOVE.W D6,-(SP)
MOVEA.L ($0A4C).W,A0 ; call user halt handler
JSR (A0)
MOVE.L (SP)+,D0 ; pop HALT exception/CCR off stack
BRA.S FASTEX ; exit
;-----------------------------------------------------------
;-----------------------------------------------------------
; QNEXTB---NEXTAFTER function changes the SRC operand by one
; ulp in the direction of the DST operand. This function
; behaves like nonarithmetic operations, but it may set
; exceptions. This function calls via BSR routines for
; comparison, multiplication, classification, and conversion.
;-----------------------------------------------------------
;-----------------------------------------------------------
QNEXTB:
FINIT
MOVE.W LKOP(A6),D6 ; get opword into D6.W
MOVEM.L LKADR1(A6),A1-A2 ; A1 <- &DST, A2 <- &SRC
SUBA.W #22,SP ; reserve stack for 2 extendeds and one int
MOVEA.L SP,A4 ; A4 is local frame pointer
MOVE.W (FPSTATE).W,D3 ; save old environment in D3 for duration
CLR.W (FPSTATE).W ; set default environment
MOVEQ #0,D2 ; zero D2
MOVE.W D6,D5 ; copy opcode into D5
ANDI.W #$3800,D5 ; isolate format bits in D5
BNE.S @2 ; single or double format
MOVE.B #$20,D2 ; isolate FPX96 bit in D2
AND.B D6,D2
BEQ.S @2 ; 80-bit extended
MOVE.W (A2),2(A2) ; convert 96-bit SRC to 80-bit and
ADDQ.L #2,A2 ; update pointer to latter
@2:
SWAP D6 ; opword in D6.HI like arith ops
CLR.W D6 ; zero flag and sign bits
;-----------------------------------------------------------
; CONVERT SRC TO EXTENDED
;-----------------------------------------------------------
PEA (A2) ; src operand address
PEA 12(A4) ; stack frame addr
MOVEQ #OP2EXT,D0 ; convert to 80-bit extended
OR.W D5,D0 ; ...with format
MOVE.W D0,-(SP)
BSR FP020
TST.W D5 ; if extended format, overwrite
BNE.S @3 ; SRC with converted SRC to
; avoid problem of unnormalized input
MOVE.L 12(A4),(A2)
MOVE.L 16(A4),4(A2)
MOVE.W 20(A4),8(A2)
;-----------------------------------------------------------
; COMPARE SRC WITH ZERO. IF IT'S EQUAL, ADJUSTMENTS WILL
; BE MADE IN DECREMENT ROUTINES BELOW.
;-----------------------------------------------------------
@3:
BFEXTU 12(A4){1:15},D1 ; extract EXP field from SRC
OR.L 14(A4),D1 ; OR in SIG.HI and SIG.LO
OR.L 18(A4),D1
SNE D4 ; D4.BYTE IS 1'S IF SRC IS ZERO
;-----------------------------------------------------------
; CONVERT DST TO EXTENDED
;-----------------------------------------------------------
PEA (A1)
PEA (A4)
OR.B D2,D0 ; may be 96-bit DST
MOVE.W D0,-(SP)
BSR FP020
TST.B D2 ; if 96-bit DST, shrink to 80-bit
BEQ.S @4 ; in stack frame
MOVE.L 4(A4),2(A4)
MOVE.L 8(A4),6(A4)
;-----------------------------------------------------------
; COMPARE THE TWO EXTENDED OPERANDS
;-----------------------------------------------------------
@4:
PEA (A4) ; DST OPERAND
PEA 12(A4) ; SRC OPERAND
MOVE.W #OPCMP,-(SP)
BSR FP020
;-----------------------------------------------------------
; IF OVERFLOW IS SET, THE OPERANDS ARE UNORDERED, THAT IS,
; ONE OF THEM IS A NAN. USE THE MULTIPLY OPERATION TO FORCE
; THE PRECEDENT NAN (IF THERE ARE TWO) TO THE SRC
;-----------------------------------------------------------
BVC.S NXORD
PEA (A4) ; DST OPERAND
PEA 12(A4) ; SRC OPERAND
MOVE.W #OPMUL,-(SP)
BSR FP020
;-----------------------------------------------------------
; NOW CONVERT THE PRECEDENT NAN BACK TO INPUT FORMAT.
;-----------------------------------------------------------
PEA 12(A4) ; SRC OPERAND IS OUTPUT
PEA (A2) ; SRC ADDRESS
MOVEQ #OPEXT2,D0 ; CVT FROM 80-BIT EXT OPCODE
OR.W D5,D0 ; OVERLAY THE FORMAT
MOVE.W D0,-(SP)
BSR FP020
BRA NXFIN
;-----------------------------------------------------------
; GET HERE IF THE TWO OPERANDS ARE ORDERED. IF THEY ARE
; EQUAL, THERE IS NOTHING TO DO; OTHERWISE MUST INC OR DEC
; THE SRC OP AS APPROPRIATE. NOTE THE ONE *****FUNNY*****
; CASE: IF THE SRC IS ZERO, THEN ITS SIGN MAY BE MISLEADING.
; FOR INSTANCE, NEXT(-0, 3) SHOULD BE +0INC1. BUT THE MINUS
; SIGN ON 0 CAUSES A DEC TO BE ISSUED INSTEAD. THE FIX IS
; TO MAKE DEC SMART ENOUGH TO KNOW THAT IF 0 IS DEC-ED, THE
; SIGN SHOULD BE FLIPPED AND THE OPERAND SHOULD BE INC-ED
; INSTEAD.
;-----------------------------------------------------------
NXORD:
BEQ NXFIN
BCC.S NXGREAT
;-----------------------------------------------------------
; GET HERE WHEN SRC < DST. INC IF SRC IS +, DEC IF -
;-----------------------------------------------------------
BTST #7,(A2) ; SIGN BIT OF SRC OPERAND
BEQ.S NXINC
BRA NXDEC
;-----------------------------------------------------------
; GET HERE WHEN SRC > DST. DEC IF SRC IS +, INC IF -
;-----------------------------------------------------------
NXGREAT:
BTST #7,(A2)
BEQ NXDEC
;-----------------------------------------------------------
; INCREMENT BY A UNIT IN THE LAST PLACE, ACCORDING TO THE
; FORMAT MASK IN D5. THE FORMAT IS IN BITS $3800. THE ONLY
; POSSIBLE CASES ARE:
; $1000 -- SINGLE
; $0800 -- DOUBLE
; $0000 -- EXTENDED
;-----------------------------------------------------------
NXINC:
;-----------------------------------------------------------
; SINGLE CASE:
;-----------------------------------------------------------
BTST #SRCMD,D5 ; TEST $1000 BIT
BEQ.S @11
ADDQ.L #1,(A2)
BRA.S NXERR
;-----------------------------------------------------------
; DOUBLE CASE:
;-----------------------------------------------------------
@11:
BTST #SRCLO,D5 ; TEST $0800 BIT
BEQ.S @15
ADDQ.L #1,4(A2)
BCC.S @13
ADDQ.L #1,(A2)
@13:
BRA.S NXERR
;-----------------------------------------------------------
; EXTENDED CASE: BE SURE OUTPUT INFINITY HAS LEADING 0 BIT.
;-----------------------------------------------------------
@15:
ADDQ.L #1,6(A2)
BCC.S NXERR
ADDQ.L #1,2(A2)
BCC.S NXERR
ROXR 2(A2)
ADDQ.W #1,(A2)
CMPI.W #$7FFF,(A2)
BEQ.S @16
CMPI.W #$FFFF,(A2)
BNE.S NXERR
@16:
BCLR #7,2(A2) ; Clr explicit bit in infinite result
;-----------------------------------------------------------
; TEST FOR EXCEPTIONS ACCORDING TO IEEE. NEXT(HUGE, INF)
; YIELDS INF WITH OVERFLOW AND INEXACT SIGNALED.
; NEXT(TINY, 0) YIELDS SOME DENORMAL WITH UNDERFLOW
; AND INEXACT. JUST SET THE APPROPRIATE BITS IN D6.LO AND
; EXIT AS THOUGH A TRUE ARITHMETIC OPERATION. THE FIRST
; STEP IS TO FIND THE CLASS OF THE INC/DEC-ED SRC OPERAND.
;-----------------------------------------------------------
NXERR:
PEA (A2)
PEA 10(A4) ; ADDRESS OF INTEGER
MOVEQ #OPCLASS,D0
OR.W D5,D0
MOVE.W D0,-(SP)
BSR FP020
;-----------------------------------------------------------
; KILL THE SIGN OF THE CLASS RESULT AND PLACE IN REGISTER
; THE CODES ARE:
; 1 SNAN -- CAN'T HAPPEN
; 2 QNAN -- CAN'T HAPPEN
; 3 INF -- OVERFLOW AND INEXACT
; 4 ZERO -- UNDERFLOW AND INEXACT
; 5 NORMAL -- OK
; 6 DENORMAL -- UNDERFLOW AND INEXACT
;-----------------------------------------------------------
MOVE.W 10(A4),D1
BPL.S @1
NEG.W D1
@1:
;-----------------------------------------------------------
; CHECK FOR INFINITE RESULT (WHICH MUST HAVE COME FROM FIN).
;-----------------------------------------------------------
CMPI.W #CLINF,D1
BNE.S @3
ORI.W #ERRWXO,D6 ; SET INEXACT AND OVERFLOW
BRA.S NXFIN
@3:
CMPI.W #CLNORM,D1
BEQ.S NXFIN
ORI.W #ERRWXU,D6 ; SET INEXACT AND UNDERFLOW
;-----------------------------------------------------------
; EXIT THROUGH POINT IN FPCONTROL AFTER CLEANING STACK
;-----------------------------------------------------------
NXFIN:
ADDA.W #22,SP ; RESTORE STACK
TST.B D2 ; if 96-bit extended result,
BEQ.S @1 ; expand result
MOVE.W (A2),-2(A2)
@1:
MOVE.W (FPSTATE).W,D7 ; D7 <- flags due to conversions
MOVE.W D3,(FPSTATE).W ; restore old environment
OR.W D7,D6 ; OR all flags together
BRA FINI2OPS
;-----------------------------------------------------------
; DECREMENT, WATCHING FOR ZERO VALUE. BRANCH TREE IS LIKE
; THAT OF INC ABOVE.
;-----------------------------------------------------------
NXDEC:
BTST #SRCMD,D5 ; CHECK $1000 BIT FOR SINGLE
BEQ.S @21
TST.B D4 ; D4.B IS NON0 IF OPERAND IS
BNE.S @201
BCHG #7,(A2)
ADDQ.L #1,(A2)
BRA.S NXERR
@201:
SUBQ.L #1,(A2)
BRA.S NXERR
;-----------------------------------------------------------
; DOUBLE CASE
;-----------------------------------------------------------
@21:
BTST #SRCLO,D5 ; CHECK $0800 BIT FOR DOUBLE
BEQ.S @25
TST.B D4 ; D4.B IS NON0 IF OP IS
BNE.S @211
BCHG #7,(A2)
ADDQ.W #1,6(A2)
BRA.S NXERR
@211:
SUBQ.L #1,4(A2)
BCC.S @213
SUBQ.L #1,(A2)
@213:
BRA.S NXERR
;-----------------------------------------------------------
; EXTENDED CASE
;-----------------------------------------------------------
@25:
TST.B D4
BNE.S @251
BCHG #7,(A2)
ADDQ.W #1,8(A2)
BRA NXERR
@251:
SUBQ.L #1,6(A2) ; DEC LOW LONG
BCC.S @259 ; NO C MEANS FINE
SUBQ.L #1,2(A2)
BMI.S @257 ; MAY HAVE BORROWED
TST.W (A2) ; MIN EXP?
BEQ.S @259 ; YES --> DONE
CMPI.W #$8000,(A2)
BEQ.S @259
ADDI.W #$8000,2(A2)
BRA.S @258
@257:
BCC.S @259 ; NO CARRY --> DONE
@258:
SUBQ.W #1,(A2)
@259:
BRA NXERR