;
; FP and LONG functions for floating point and long packages
;

       INCLUDE "bds.lib"


       FUNCTION        fp

       CALL    arghak
       PUSH    B               ; save BC
       LXI     H,COMMON$EXIT

       PUSH    H               ; save the common exit addr in the stack
       LDA     arg1            ;Get code ptr
       RAL                     ;Multiply code by 2
       MOV     E,A
       MVI     D,0             ;Move result to DE
       LXI     H,JMPTAB        ;Get JMPTAB addr
       DAD     D               ;Add offset to it
       XCHG                    ;Store result in DE
       LDAX    D
       MOV     L,A
       INX     D
       LDAX    D
       MOV     H,A             ;Move table addr to HL
       PCHL                    ;Jump to selected routine
JMPTAB:
       DW      XNORM
       DW      XADD
       DW      XSUB
       DW      XMULT
       DW      XDIV
       DW      XFTOA

COMMON$EXIT:
       POP     B               ; restore BC
       RET                     ; return to BDS C

XNORM:
       CALL    LD$OP1
       CALL    FPNORM
EXIT0:
       CALL    ST$ACC
       RET

XADD:
       CALL    LD$OP2
       CALL    FPADD
       JMP     EXIT0
XSUB:
       CALL    LD$OP2
       CALL    FPSUB
       JMP     EXIT0
XMULT:
       CALL    LD$OP2
       CALL    FPMULT
       JMP     EXIT0

XDIV:
       CALL    LD$OP2
       CALL    FPDIV
       JMP     EXIT0

XFTOA:
       CALL    LD$OP1
       CALL    FTOA
       RET

LD$OP1:
       LHLD    arg3
       XCHG
       LXI     H,FPACC-1
       MVI     M,0
       INX     H
       MVI     C,5
       CALL    MOVE
       RET

LD$OP2:
       CALL    LD$OP1
       LHLD    arg4
       XCHG
       LXI     H,FPOP-1
       MVI     M,0
       INX     H
       MVI     C,5
       CALL    MOVE
       RET

ST$ACC:
       LHLD    arg2
       LXI     D,FPACC
       MVI     C,5
       CALL    MOVE
       RET

FPNORM:
       LDA     FPACC+3         ;Get MS byte of FPACC
       STA     SIGN            ;Save SIGN byte of FPACC
       ANA     A               ;If number is positive
       JP      NZERO$TEST      ;.. go test for zero
       LXI     H,FPACC-1       ;Load addr of FPACC (+ xtra byte)
       MVI     C,5             ;Load precision register
       CALL    NEGATE          ;Negate FPACC

NZERO$TEST:
       LXI     H,FPACC-1
       MVI     C,5
       CALL    ZERO$TEST       ;If FPACC not zero
       JNZ     NOTZERO         ;.. go normalize
       STA     FPACCX          ;make sure exponent is zero
       RET

NOTZERO:
       LXI     H,FPACC-1
       MVI     C,5
       CALL    SHIFTL          ;shift FPACC left
       LXI     H,FPACCX
       DCR     M               ;subtract 1 from FPACC exponent
       LDA     FPACC+3         ;get MS byte of FPACC
       ANA     A               ;if high order bit not no
       JP      NOTZERO         ;.. go do again

;compensate for last shift

       LXI     H,FPACCX
       INR     M
       DCX     H
       MVI     C,5
       CALL    SHIFTR
       LDA     SIGN            ;fetch original sign
       RAL                     ;shift sign bit into carry
       RNC                     ;exit if orig # was positive
       LXI     H,FPACC-1
       MVI     C,5
       CALL    NEGATE          ;2's complement FPACC
       RET                     ;Exit FPNORM
FPADD:
       LXI     H,FPACC
       MVI     C,4
       CALL    ZERO$TEST               ;if FPACC not = zero
       JNZ     TEST$FPOP               ;.. go test FPOP for zero
       LXI     H,FPACC
       LXI     D,FPOP
       MVI     C,5
       CALL    MOVE            ;Move FPOP to  FPACC
       RET                     ;Exit FPADD
TEST$FPOP:
       LXI     H,FPOP
       MVI     C,4
       CALL    ZERO$TEST       ;if FPOP = 0
       RZ                      ;.. exit FPADD
       LDA     FPACCX
       LXI     H,FPOPX
       SUB     M               ;if exponents are equal
       JZ      ADD$SETUP       ;.. go to add setup
       JP      RANGE$TEST      ;if diff of exp >=0,goto range test
       CMA
       INR     A               ;ABS of difference

RANGE$TEST:
       CPI     32              ;if diff < 32
       JM      ALGN$OPRNDS     ;.. we can go align the operands
       LXI     H,FPACCX
       LDA     FPOPX
       SUB     M               ;if exp of FPACC > exp of FPOP
       RM                      ;.. exit FPADD
       LXI     D,FPOP
       LXI     H,FPACC
       MVI     C,5
       CALL    MOVE            ;move FPOP to FPACC
       RET                     ;Exit FPADD

ALGN$OPRNDS:
       LDA     FPACCX
       LXI     H,FPOPX
       SUB     M               ;subt exponents
       MOV     B,A             ;save difference of exponents
       JM      SHFT$FPACC      ;if diff neg, go shift FPACC

ALGN$FPOP:
       LXI     H,FPOPX
       CALL    SHFT$LOOP       ;shift FPOP & increment exponent
       DCR     B               ;Decrement diff register
       JNZ     ALGN$FPOP       ;loop until exponents are equal
       JMP     ADD$SETUP       ;go to add setup

SHFT$FPACC:
       LXI     H,FPACCX
       CALL    SHFT$LOOP       ;shift FPACC & increment exponent
       INR     B               ;increment difference register
       JNZ     SHFT$FPACC      ;loop until exponents are equal

ADD$SETUP:
       XRA     A
       STA     FPACC-1
       STA     FPOP-1
       LXI     H,FPACCX
       CALL    SHFT$LOOP       ;shift FPACC right
       LXI     H,FPOPX
       CALL    SHFT$LOOP       ;shift FPOP right
       LXI     H,FPACC-1
       LXI     D,FPOP-1
       MVI     C,5
       CALL    ADDER           ;add FPOP to FPACC
       CALL    FPNORM          ;normalize result
       RET                     ;exit FPADD

SHFT$LOOP:
       INR     M               ;increment exponent
       DCX     H               ;decrement ptr
       MVI     C,4
       MOV     A,M             ;get MS byte
       ANA     A               ;if negative number
       JM      SHFT$MINUS      ;.. goto negative shift
       CALL    SHIFTR          ;shift mantissa
       RET

SHFT$MINUS:
       STC                     ;set carry
       CALL    SHFTR           ;shift mantissa progatating sign
       RET                     ;exit

FPSUB:
       LXI     H,FPACC
       MVI     C,4
       CALL    NEGATE
       JMP     FPADD

FPMULT:
       CALL    SIGNJOB         ;process the signs
       LXI     H,WORK
       MVI     C,8
       CALL    ZERO$MEMORY     ;WORK := 0 (partial product)
       LXI     H,FPACCX
       LDA     FPOPX
       ADD     M
       INR     A               ;compensate for algolrithm
       MOV     M,A             ;add FPOP exp to FPACC exponent
       LXI     H,FPACC-4
       MVI     C,4
       CALL    ZERO$MEMORY     ;clear multiplicand extra bytes
       LXI     H,BITS
       MVI     M,31

MULT$LOOP:
       LXI     H,FPOP+3
       MVI     C,4
       CALL    SHIFTR          ;shift multiplier right
       CC      ADD$MULTIPLICAND ;add multiplicand if carry
       LXI     H,WORK+7
       MVI     C,8
       CALL    SHIFTR          ;shift partial product right
       LXI     H,BITS
       DCR     M               ;decrement BITS counter
       JNZ     MULT$LOOP       ;if not zero, do again
       LXI     H,WORK+7
       MVI     C,8
       CALL    SHIFTR          ;shift once more for rounding
       LXI     H,WORK+3
       MOV     A,M
       RAL                     ;fetch 32th bit
       ANA     A               ;if it is a 1
       CM      ROUND$IT        ;.. round the result
       LXI     D,WORK+3
       LXI     H,FPACC-1
       MVI     C,5
EXMLDV:
       CALL    MOVE
       LDA     SIGN            ;fetch SIGN and save it on the stack
       PUSH    PSW
       CALL    FPNORM
       POP     PSW
       ANA     A
       RP
       LXI     H,FPACC
       MVI     C,4
       CALL    NEGATE
       RET

ADD$MULTIPLICAND:
       LXI     H,WORK
       LXI     D,FPACC-4
       MVI     C,8
       CALL    ADDER
       RET
ROUND$IT:
       MVI     A,40H
       ADD     M
       MVI     C,4
RND$LOOP:
       MOV     M,A
       INX     H
       MVI     A,0
       ADC     M
       DCR     C
       JNZ     RND$LOOP
       MOV     M,A
       RET
FPDIV:
       LXI     H,FPOP
       MVI     C,4
       CALL    ZERO$TEST
       JNZ     DIV$SIGN
       LXI     H,FPACC
       MVI     C,5
       CALL    ZERO$MEMORY
       RET

DIV$SIGN:
       CALL    SIGNJOB
       LXI     H,WORK
       MVI     C,12
       CALL    ZERO$MEMORY
       MVI     A,31
       STA     BITS
       LXI     H,FPACCX
       LDA     FPOPX
       MOV     B,A
       MOV     A,M
       SUB     B
       INR     A
       MOV     M,A
DIVIDE:
       CALL    SETSUB          ;WORK2 := dividend - divisor
       JM      NOGO            ;if minus, go put 0 in quotient
       LXI     H,FPACC
       LXI     D,WORK2
       MVI     C,4
       CALL    MOVE            ;move subt results to dividend
       STC
       JMP     QUOROT

NOGO:
       ANA     A
QUOROT:
       LXI     H,WORK+4
       MVI     C,4
       CALL    SHFTL           ;Insert carry flag into quotient
       LXI     H,FPACC
       MVI     C,4
       CALL    SHFTL           ;shift dividend left
       LXI     H,BITS
       DCR     M               ;decrement BITS counter
       JNZ     DIVIDE          ;loop until BITS = zero
       CALL    SETSUB          ;1 more time for rounding
       JM      DVEXIT          ;if 24th bit = 0, goto exit
       LXI     H,WORK+4
       LXI     D,ONE
       MVI     C,4
       CALL    ADDER
       LXI     H,WORK+7
       MOV     A,M
       ANA     A
       JP      DVEXIT
       MVI     C,4
       CALL    SHIFTR
       LXI     H,FPACCX
       INR     M
DVEXIT:
       LXI     H,FPACC
       LXI     D,WORK+4
       MVI     C,4
       JMP     EXMLDV

SETSUB:
       LXI     D,FPACC
       LXI     H,WORK2
       MVI     C,4
       CALL    MOVE            ;move dividend to work2
       LXI     H,WORK2
       LXI     D,FPOP
       MVI     C,4
       CALL    SUBBER          ;subtract divisor from work2
       LDA     WORK2+3
       ANA     A
       RET

FTOA:
       LHLD    arg2
       SHLD    ASCII$PTR
       MVI     M,' '
       LDA     FPACC+3
       ANA     A
       JP      BYSIGN
       MVI     M,'-'
       LXI     H,FPACC
       MVI     C,4
       CALL    NEGATE
BYSIGN:
       LHLD    ASCII$PTR
       INX     H
       MVI     M,'0'
       INX     H
       MVI     M,'.'
       INX     H
       SHLD    ASCII$PTR
       XRA     A
       STA     EXP
       LXI     H,FPACC
       MVI     C,4
       CALL    ZERO$TEST
       JNZ     SU$FTOA
       MVI     C,7
       LHLD    ASCII$PTR
ZERO$LOOP:
       MVI     M,'0'
       INX     H
       DCR     C
       JNZ     ZERO$LOOP
       SHLD    ASCII$PTR
       JMP     EXPOUT
SU$FTOA:
       LXI     H,FPACCX
       DCR     M
DECEXT:
       JP      DECEXD
       MVI     A,4
       ADD     M
       JP      DECOUT
       CALL    FPX10
DECREP:
       LXI     H,FPACCX
       MOV     A,M
       ANA     A
       JMP     DECEXT

DECEXD:
       CALL    FPD10
       JMP     DECREP

DECOUT:
       LXI     H,FPACC
       LXI     D,ADJ
       MVI     C,4
       CALL    ADDER
       LXI     H,OUTAREA
       LXI     D,FPACC
       MVI     C,4
       CALL    MOVE
       LXI     H,OUTAREA+4
       MVI     M,0
       LXI     H,OUTAREA
       MVI     C,4
       CALL    SHIFTL
       CALL    OUTX10
COMPEN:
       LXI     H,FPACCX
       INR     M
       JZ      OUTDIG
       LXI     H,OUTAREA+4
       MVI     C,5
       CALL    SHIFTR
       JMP     COMPEN
OUTDIG:
       MVI     A,7
       STA     DIGCNT
       LXI     H,OUTAREA+4
       MOV     A,M
       ANA     A
       JZ      ZERODG
OUTDGS:
       LXI     H,OUTAREA+4
       MVI     A,'0'
       ADD     M
       LHLD    ASCII$PTR
       MOV     M,A
       INX     H
       SHLD    ASCII$PTR
DECRDG:
       LXI     H,DIGCNT
       DCR     M
       JZ      EXPOUT
       CALL    OUTX10
       JMP     OUTDGS

ZERODG:
       LXI     H,EXP
       DCR     M
       LXI     H,OUTAREA
       MVI     C,5
       CALL    ZERO$TEST
       JNZ     DECRDG
       XRA     A
       STA     DIGCNT
       JMP     DECRDG

OUTX10:
       XRA     A
       STA     OUTAREA+4
       LXI     H,WORK
       LXI     D,OUTAREA
       MVI     C,5
       CALL    MOVE
       LXI     H,OUTAREA
       MVI     C,5
       CALL    SHIFTL
       LXI     H,OUTAREA
       MVI     C,5
       CALL    SHIFTL
       LXI     D,WORK
       LXI     H,OUTAREA
       MVI     C,5
       CALL    ADDER
       LXI     H,OUTAREA
       MVI     C,5
       CALL    SHIFTL
       RET
EXPOUT:
       LHLD    ASCII$PTR
       MVI     M,'E'
       INX     H
       LDA     EXP
       ANA     A
       JP      EXPOT
       CMA
       INR     A
       STA     EXP
       MVI     M,'-'
       INX     H
       LDA     EXP
EXPOT:
       MVI     C,0
EXPLOOP:
       SUI     10
       JM      TOMUCH
       STA     EXP
       INR     C
       JMP     EXPLOOP

TOMUCH:
       MVI     A,'0'
       ADD     C
       MOV     M,A
       INX     H
       LDA     EXP
       ADI     '0'
       MOV     M,A
       INX     H
       MVI     M,0
       RET
FPX10:
       LXI     H,FPOP
       LXI     D,TEN
       MVI     C,5
       CALL    MOVE
       CALL    FPMULT
       LXI     H,EXP
       DCR     M
       RET

FPD10:
       LXI     H,FPOP
       LXI     D,ONE$TENTH
       MVI     C,5
       CALL    MOVE
       CALL    FPMULT
       LXI     H,EXP
       INR     M
       RET

NEGATE:
       STC                     ;CARRY forces an add of 1
NEGAT$LOOP:
       MOV     A,M             ;fetch byte
       CMA                     ;complement it
       ACI     0               ;make it two's complement
       MOV     M,A             ;store the result
       INX     H               ;bump ptr
       DCR     C               ;decrement precision register
       JNZ     NEGAT$LOOP      ;if not done, go do it again
       RET                     ;Return to caller

ZERO$TEST:
       XRA     A               ;clear A
       ORA     M               ;'OR' A with next byte
       INX     H               ;bump ptr
       DCR     C               ;decrement precision register
       JNZ     ZERO$TEST+1     ;loop until done
       ANA     A               ;set flags
       RET

SHIFTL:
       ANA     A               ;clear CARRY
SHFTL:
       MOV     A,M             ;get next byte
       RAL                     ;shift it left
       MOV     M,A             ;store result
       INX     H               ;bump ptr
       DCR     C               ;decrement precision register
       JNZ     SHFTL           ;loop until done
       RET

SHIFTR:
       ANA     A
SHFTR:
       MOV     A,M
       RAR
       MOV     M,A
       DCX     H
       DCR     C
       JNZ     SHFTR
       RET

ADDER:
       ANA     A
ADD$LOOP:
       LDAX    D
       ADC     M
       MOV     M,A
       INX     D
       INX     H
       DCR     C
       JNZ     ADD$LOOP
       RET

SUBBER:
       ANA     A
       XCHG
SUB$LOOP:
       LDAX    D
       SBB     M
       STAX    D
       INX     D
       INX     H
       DCR     C
       JNZ     SUB$LOOP
       XCHG
       RET

ZERO$MEMORY:
       MVI     M,0
       INX     H
       DCR     C
       JNZ     ZERO$MEMORY
       RET

MOVE:
       LDAX    D
       MOV     M,A
       INX     D
       INX     H
       DCR     C
       JNZ     MOVE
       RET

SIGNJOB:
       LDA     FPACC+3
       STA     SIGN
       ANA     A
       JP      CKFPOP
       LXI     H,FPACC
       MVI     C,4
       CALL    NEGATE
CKFPOP:
       LXI     H,SIGN
       LDA     FPOP+3
       XRA     M
       MOV     M,A
       LDA     FPOP+3
       ANA     A
       RP
       LXI     H,FPOP
       MVI     C,4
       CALL    NEGATE
       RET

       DS      4
FPACC:  DS      4
FPACCX: DS      1
       DS      4
FPOP:   DS      4
FPOPX:  DS      1
SIGN:   DS      1
WORK:   DS      8
WORK2:  DS      4
BITS:   DS      1
ASCII$PTR:      DS      2
EXP:    DS      1
OUTAREA:        DS      5
DIGCNT: DS      1
ONE$TENTH:      DB      66H,66H,66H,66H,0FDH
TEN:    DB      0,0,0,50H,4
ADJ:    DB      5,0,0,0
ONE:    DB      80H,0,0,0

       ENDFUNC