Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

;************************************************************
;
; This is a complete BCD floating point package for the 8051 micro-
; controller. It provides 8 digits of accuracy with exponents that
; range from +127 to -127. The mantissa is in packed BCD, while the
; exponent is expressed in pseudo-twos complement. A ZERO exponent
; is used to express the number ZERO. An exponent value of 80H or
; greater than means the exponent is positive, i.e. 80H = E 0,
; 81H = E+1, 82H = E+2 and so on. If the exponent is 7FH or less,
; the exponent is negative, 7FH = E-1, 7EH = E-2, and so on.
; ALL NUMBERS ARE ASSUMED TO BE NORMALIZED and all results are
; normalized after calculation. A normalized mantissa is >=.10 and
; <=.99999999.
;
; The numbers in memory assumed to be stored as follows:
;
; EXPONENT OF ARGUMENT 2   =   VALUE OF ARG_STACK+FP_NUMBER_SIZE
; SIGN OF ARGUMENT 2       =   VALUE OF ARG_STACK+FP_NUMBER_SIZE-1
; DIGIT 78 OF ARGUMENT 2   =   VALUE OF ARG_STACK+FP_NUMBER_SIZE-2
; DIGIT 56 OF ARGUMENT 2   =   VALUE OF ARG_STACK+FP_NUMBER_SIZE-3
; DIGIT 34 OF ARGUMENT 2   =   VALUE OF ARG_STACK+FP_NUMBER_SIZE-4
; DIGIT 12 OF ARGUMENT 2   =   VALUE OF ARG_STACK+FP_NUMBER_SIZE-5
;
; EXPONENT OF ARGUMENT 1   =   VALUE OF ARG_STACK
; SIGN OF ARGUMENT 1       =   VALUE OF ARG_STACK-1
; DIGIT 78 OF ARGUMENT 1   =   VALUE OF ARG_STACK-2
; DIGIT 56 OF ARGUMENT 1   =   VALUE OF ARG_STACK-3
; DIGIT 34 OF ARGUMENT 1   =   VALUE OF ARG_STACK-4
; DIGIT 12 OF ARGUMENT 1   =   VALUE OF ARG_STACK-5
;
; The operations are performed thusly:
;
; ARG_STACK+FP_NUMBER_SIZE = ARG_STACK+FP_NUMBER_SIZE # ARG_STACK
;
; Which is ARGUMENT 2 = ARGUMENT 2 # ARGUMENT 1
;
; Where # can be ADD, SUBTRACT, MULTIPLY OR DIVIDE.
;
; Note that the stack gets popped after an operation.
;
; The FP_COMP instruction POPS the ARG_STACK TWICE and returns status.
;
;**********************************************************************
;

        segment code
        newpage
        section float           ; protect symbols
;**********************************************************************
;
; STATUS ON RETURN - After performing an operation (+, -, *, /)
;                    the accumulator contains the following status
;
; ACCUMULATOR - BIT 0 - FLOATING POINT UNDERFLOW OCCURED
;
;             - BIT 1 - FLOATING POINT OVERFLOW OCCURED
;
;             - BIT 2 - RESULT WAS ZER0
;
;             - BIT 3 - DIVIDE BY ZERO ATTEMPTED
;
;             - BIT 4 - NOT USED, 0 RETURNED
;
;             - BIT 5 - NOT USED, 0 RETURNED
;
;             - BIT 6 - NOT USED, 0 RETURNED
;
;             - BIT 7 - NOT USED, 0 RETURNED
;
; NOTE: When underflow occures, a ZERO result is returned.
;       When overflow or divide by zero occures, a result of
;       .99999999 E+127 is returned and it is up to the user
;       to handle these conditions as needed in the program.
;
; NOTE: The Compare instruction returns F0 = 0 if ARG 1 = ARG 2
;       and returns a CARRY FLAG = 1 if ARG 1 is > ARG 2
;
;***********************************************************************
;
        newpage
;***********************************************************************
;
; The following values MUST be provided by the user
;
;***********************************************************************
;
ARG_STACK       EQU     9       ;ARGUMENT STACK POINTER
ARG_STACK_PAGE  EQU     1
FORMAT          EQU     23      ;LOCATION OF OUTPUT FORMAT BYTE
OUTPUT          EQU     1990H   ;CALL LOCATION TO OUTPUT A CHARACTER
CONVT           EQU     58H     ;LOCATION TO CONVERT NUMBERS
INTGRC          BIT     25      ;BIT SET IF INTGER ERROR
ZSURP           BIT     54      ;ZERO SUPRESSION FOR HEX PRINT
;
;***********************************************************************
;
; The following equates are used internally
;
;***********************************************************************
;
FP_NUMBER_SIZE  EQU     6
DIGIT           EQU     FP_NUMBER_SIZE-2
R0B0            EQU     0
R1B0            EQU     1
UNDERFLOW       EQU     0
ACC_UNDERFLOW   BIT     ACC.0           ; ******AA added
OVERFLOW        EQU     1
ACC_OVERFLOW    BIT     ACC.1           ; ******AA added
ZERO            EQU     2
ACC_ZERO        BIT     ACC.2           ; ******AA added
ZERO_DIVIDE     EQU     3
ACC_ZERO_DIVIDE BIT     ACC.3           ; ******AA added
;
;***********************************************************************
        newpage
        ;**************************************************************
        ;
        ; The following internal locations are used by the math pack
        ; ordering is important and the FP_DIGITS must be bit
        ; addressable
        ;
        ;***************************************************************
        ;
FP_STATUS       EQU     28H             ;NOT USED
FP_TEMP         EQU     FP_STATUS+1     ;NOT USED
FP_CARRY        SFRB    FP_STATUS+2     ;USED FOR BITS ******AA EQU-->SFRB
ADD_IN          BIT     35              ;DCMPXZ IN BASIC BACKAGE
XSIGN           BIT     FP_CARRY.0
FOUND_RADIX     BIT     FP_CARRY.1
FIRST_RADIX     BIT     FP_CARRY.2
DONE_LOAD       BIT     FP_CARRY.3
FP_DIG12        EQU     FP_CARRY+1
FP_DIG34        EQU     FP_CARRY+2
FP_DIG56        EQU     FP_CARRY+3
FP_DIG78        EQU     FP_CARRY+4
FP_SIGN         SFRB    FP_CARRY+5      ; ******AA EQU-->SFRB
MSIGN           BIT     FP_SIGN.0
FP_EXP          EQU     FP_CARRY+6
FP_NIB1         EQU     FP_DIG12
FP_NIB2         EQU     FP_NIB1+1
FP_NIB3         EQU     FP_NIB1+2
FP_NIB4         EQU     FP_NIB1+3
FP_NIB5         EQU     FP_NIB1+4
FP_NIB6         EQU     FP_NIB1+5
FP_NIB7         EQU     FP_NIB1+6
FP_NIB8         EQU     FP_NIB1+7
FP_ACCX         EQU     FP_NIB1+8
FP_ACCC         EQU     FP_NIB1+9
FP_ACC1         EQU     FP_NIB1+10
FP_ACC2         EQU     FP_NIB1+11
FP_ACC3         EQU     FP_NIB1+12
FP_ACC4         EQU     FP_NIB1+13
FP_ACC5         EQU     FP_NIB1+14
FP_ACC6         EQU     FP_NIB1+15
FP_ACC7         EQU     FP_NIB1+16
FP_ACC8         EQU     FP_NIB1+17
FP_ACCS         EQU     FP_NIB1+18
        ;
        newpage
        ORG     1993H
        ;
        ;**************************************************************
        ;
        ; The floating point entry points and jump table
        ;
        ;**************************************************************
        ;
        AJMP    FLOATING_ADD
        AJMP    FLOATING_SUB
        AJMP    FLOATING_COMP
        AJMP    FLOATING_MUL
        AJMP    FLOATING_DIV
        AJMP    HEXSCAN
        AJMP    FLOATING_POINT_INPUT
        AJMP    FLOATING_POINT_OUTPUT
        AJMP    CONVERT_BINARY_TO_ASCII_STRING
        AJMP    CONVERT_ASCII_STRING_TO_BINARY
        AJMP    MULNUM10
        AJMP    HEXOUT
        AJMP    PUSHR2R0
        ;
        newpage
        ;
FLOATING_SUB:
        ;
        MOV     P2,#ARG_STACK_PAGE
        MOV     R0,ARG_STACK
        DEC     R0              ;POINT TO SIGN
        MOVX    A,@R0           ;READ SIGN
        CPL     ACC.0
        MOVX    @R0,A
        ;
        ;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
        ;
FLOATING_ADD:
        ;
        ;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
        ;
        ;
        ACALL   MDES1           ;R7=TOS EXP, R6=TOS-1 EXP, R4=TOS SIGN
                                ;R3=TOS-1 SIGN, OPERATION IS R1 # R0
        ;
        MOV     A,R7            ;GET TOS EXPONENT 
        JZ      POP_AND_EXIT    ;IF TOS=0 THEN POP AND EXIT
        CJNE    R6,#0,LOAD1     ;CLEAR CARRY EXIT IF ZERO
        ;
        ;**************************************************************
        ;
SWAP_AND_EXIT:  ; Swap external args and return
        ;
        ;**************************************************************
        ;
        ACALL   LOAD_POINTERS
        MOV     R7,#FP_NUMBER_SIZE
        ;
SE1:    MOVX    A,@R0           ;SWAP THE ARGUMENTS
        MOVX    @R1,A
        DEC     R0
        DEC     R1
        DJNZ    R7,SE1
        ;
POP_AND_EXIT:
        ;
        MOV     A,ARG_STACK     ;POP THE STACK
        ADD     A,#FP_NUMBER_SIZE
        MOV     ARG_STACK,A
        CLR     A
        RET
        ;
        ;
LOAD1:  SUBB    A,R6            ;A = ARG 1 EXP - ARG 2 EXP
        MOV     FP_EXP,R7       ;SAVE EXPONENT AND SIGN
        MOV     FP_SIGN,R4
        JNC     LOAD2           ;ARG1 EXPONENT IS LARGER OR SAME
        MOV     FP_EXP,R6
        MOV     FP_SIGN,R3
        CPL     A
        INC     A               ;COMPENSATE FOR EXP DELTA
        XCH     A,R0            ;FORCE R0 TO POINT AT THE LARGEST
        XCH     A,R1            ;EXPONENT
        XCH     A,R0
        ;
LOAD2:  MOV     R7,A            ;SAVE THE EXPONENT DELTA IN R7
        CLR     ADD_IN
        CJNE    R5,#0,$+5
        SETB    ADD_IN
        ;
        newpage
        ; Load the R1 mantissa
        ;
        ACALL   LOADR1_MANTISSA ;LOAD THE SMALLEST NUMBER
        ;
        ; Now align the number to the delta exponent
        ; R4 points to the string of the last digits lost
        ;
        CJNE    R7,#DIGIT+DIGIT+3,$+3
        JC      $+4
        MOV     R7,#DIGIT+DIGIT+2
        ;
        MOV     FP_CARRY,#00    ;CLEAR THE CARRY
        ACALL   RIGHT           ;SHIFT THE NUMBER
        ;
        ; Set up for addition and subtraction
        ;
        MOV     R7,#DIGIT       ;LOOP COUNT
        MOV     R1,#FP_DIG78
        MOV     A,#9EH
        CLR     C
        SUBB    A,R4
        DA      A
        XCH     A,R4
        JNZ     $+3
        MOV     R4,A
        CJNE    A,#50H,$+3      ;TEST FOR SUBTRACTION
        JNB     ADD_IN,SUBLP    ;DO SUBTRACTION IF NO ADD_IN
        CPL     C               ;FLIP CARRY FOR ADDITION
        ACALL   ADDLP           ;DO ADDITION
        ;
        JNC     ADD_R
        INC     FP_CARRY
        MOV     R7,#1
        ACALL   RIGHT
        ACALL   INC_FP_EXP      ;SHIFT AND BUMP EXPONENT
        ;
ADD_R:  AJMP    STORE_ALIGN_TEST_AND_EXIT
        ;
ADDLP:  MOVX    A,@R0
        ADDC    A,@R1
        DA      A
        MOV     @R1,A
        DEC     R0
        DEC     R1
        DJNZ    R7,ADDLP        ;LOOP UNTIL DONE
        RET
        ;
        newpage
        ;
SUBLP:  MOVX    A,@R0           ;NOW DO SUBTRACTION
        MOV     R6,A
        CLR     A
        ADDC    A,#99H
        SUBB    A,@R1
        ADD     A,R6
        DA      A
        MOV     @R1,A
        DEC     R0
        DEC     R1
        DJNZ    R7,SUBLP
        JC      FSUB6
        ;
        newpage
        ;
        ; Need to complement the result and sign because the floating
        ; point accumulator mantissa was larger than the external
        ; memory and their signs were equal.
        ;
        CPL     FP_SIGN.0
        MOV     R1,#FP_DIG78
        MOV     R7,#DIGIT       ;LOOP COUNT
        ;
FSUB5:  MOV     A,#9AH
        SUBB    A,@R1
        ADD     A,#0
        DA      A
        MOV     @R1,A
        DEC     R1
        CPL     C
        DJNZ    R7,FSUB5        ;LOOP
        ;
        ; Now see how many zeros their are
        ;
FSUB6:  MOV     R0,#FP_DIG12
        MOV     R7,#0
        ;
FSUB7:  MOV     A,@R0
        JNZ     FSUB8
        INC     R7
        INC     R7
        INC     R0
        CJNE    R0,#FP_SIGN,FSUB7
        AJMP    ZERO_AND_EXIT
        ;
FSUB8:  CJNE    A,#10H,$+3
        JNC     FSUB9
        INC     R7
        ;
        ; Now R7 has the number of leading zeros in the FP ACC
        ;
FSUB9:  MOV     A,FP_EXP        ;GET THE OLD EXPONENT
        CLR     C
        SUBB    A,R7            ;SUBTRACT FROM THE NUMBER OF ZEROS
        JZ      FSUB10
        JC      FSUB10
        ;
        MOV     FP_EXP,A        ;SAVE THE NEW EXPONENT
        ;
        ACALL   LEFT1           ;SHIFT THE FP ACC
        MOV     FP_CARRY,#0
        AJMP    STORE_ALIGN_TEST_AND_EXIT
        ;
FSUB10: AJMP    UNDERFLOW_AND_EXIT
        ;
        newpage
        ;***************************************************************
        ;
FLOATING_COMP:  ; Compare two floating point numbers
                ; used for relational operations and is faster
                ; than subtraction. ON RETURN, The carry is set
                ; if ARG1 is > ARG2, else carry is not set
                ; if ARG1 = ARG2, F0 gets set
        ;
        ;***************************************************************
        ;
        ACALL   MDES1           ;SET UP THE REGISTERS
        MOV     A,ARG_STACK
        ADD     A,#FP_NUMBER_SIZE+FP_NUMBER_SIZE
        MOV     ARG_STACK,A     ;POP THE STACK TWICE, CLEAR THE CARRY
        MOV     A,R6            ;CHECK OUT EXPONENTS
        CLR     F0
        SUBB    A,R7
        JZ      EXPONENTS_EQUAL
        JC      ARG1_EXP_IS_LARGER
        ;
        ; Now the ARG2 EXPONENT is > ARG1 EXPONENT
        ;
SIGNS_DIFFERENT:
        ;
        MOV     A,R3            ;SEE IF SIGN OF ARG2 IS POSITIVE
        SJMP    $+3
        ;
ARG1_EXP_IS_LARGER:
        ;
        MOV     A,R4            ;GET THE SIGN OF ARG1 EXPONENT
        JZ      $+3
        CPL     C
        RET
        ;
EXPONENTS_EQUAL:
        ;
        ; First, test the sign, then the mantissa
        ;
        CJNE    R5,#0,SIGNS_DIFFERENT
        ;
BOTH_PLUS:
        ;
        MOV     R7,#DIGIT       ;POINT AT MS DIGIT
        DEC     R0
        DEC     R0
        DEC     R0
        DEC     R1
        DEC     R1
        DEC     R1
        ;
        ; Now do the compare
        ;
CLOOP:  MOVX    A,@R0
        MOV     R6,A
        MOVX    A,@R1
        SUBB    A,R6
        JNZ     ARG1_EXP_IS_LARGER
        INC     R0
        INC     R1
        DJNZ    R7,CLOOP
        ;
        ; If here, the numbers are the same, the carry is cleared
        ;
        SETB    F0
        RET                     ;EXIT WITH EQUAL
        ;
        newpage
;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
;
FLOATING_MUL:   ; Floating point multiply
;
;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
;
        ACALL   MUL_DIV_EXP_AND_SIGN
        ;
        ; check for zero exponents
        ;
        CJNE    R6,#00,$+5      ;ARG 2 EXP ZERO?
        AJMP    ZERO_AND_EXIT
        ;
        ; calculate the exponent
        ;
FMUL1:  MOV     FP_SIGN,R5      ;SAVE THE SIGN, IN CASE OF FAILURE
        ;
        MOV     A,R7
        JZ      FMUL1-2
        ADD     A,R6            ;ADD THE EXPONENTS
        JB      ACC.7,FMUL_OVER
        JBC     CY,FMUL2        ;SEE IF CARRY IS SET
        ;
        AJMP    UNDERFLOW_AND_EXIT
        ;
FMUL_OVER:
        ;
        JNC     FMUL2           ;OK IF SET
        ;
FOV:    AJMP    OVERFLOW_AND_EXIT
        ;
FMUL2:  SUBB    A,#129          ;SUBTRACT THE EXPONENT BIAS
        MOV     R6,A            ;SAVE IT FOR LATER
        ;
        ; Unpack and load R0
        ;
        ACALL   UNPACK_R0
        ;
        ; Now set up for loop multiply
        ;
        MOV     R3,#DIGIT
        MOV     R4,R1B0
        ;
        newpage
        ;
        ; Now, do the multiply and accumulate the product
        ;
FMUL3:  MOV     R1B0,R4
        MOVX    A,@R1
        MOV     R2,A
        ACALL   MUL_NIBBLE
        ;
        MOV     A,R2
        SWAP    A
        ACALL   MUL_NIBBLE
        DEC     R4
        DJNZ    R3,FMUL3
        ;
        ; Now, pack and restore the sign
        ;
        MOV     FP_EXP,R6
        MOV     FP_SIGN,R5
        AJMP    PACK            ;FINISH IT OFF
        ;
        newpage
        ;DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
        ;
FLOATING_DIV:
        ;
        ;DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
        ;
        ACALL   MDES1
        ;
        ; Check the exponents
        ;
        MOV     FP_SIGN,R5      ;SAVE THE SIGN
        CJNE    R7,#0,DIV0      ;CLEARS THE CARRY
        ACALL   OVERFLOW_AND_EXIT
        CLR     A
        SETB    ACC_ZERO_DIVIDE
        RET
        ;
DIV0:   MOV     A,R6            ;GET EXPONENT
        JZ      FMUL1-2         ;EXIT IF ZERO
        SUBB    A,R7            ;DELTA EXPONENT
        JB      ACC.7,D_UNDER
        JNC     DIV3
        AJMP    UNDERFLOW_AND_EXIT
        ;
D_UNDER:JNC     FOV
        ;
DIV3:   ADD     A,#129          ;CORRECTLY BIAS THE EXPONENT
        MOV     FP_EXP,A        ;SAVE THE EXPONENT
        ACALL   LOADR1_MANTISSA ;LOAD THE DIVIDED
        ;
        MOV     R2,#FP_ACCC     ;SAVE LOCATION
        MOV     R3,R0B0         ;SAVE POINTER IN R3
        MOV     FP_CARRY,#0     ;ZERO CARRY BYTE
        ;
DIV4:   MOV     R5,#0FFH        ;LOOP COUNT
        SETB    C
        ;
DIV5:   MOV     R0B0,R3         ;RESTORE THE EXTERNAL POINTER
        MOV     R1,#FP_DIG78    ;SET UP INTERNAL POINTER
        MOV     R7,#DIGIT       ;LOOP COUNT
        JNC     DIV7            ;EXIT IF NO CARRY
        ;
DIV6:   MOVX    A,@R0           ;DO ACCUMLATION
        MOV     R6,A
        CLR     A
        ADDC    A,#99H
        SUBB    A,R6
        ADD     A,@R1
        DA      A
        MOV     @R1,A
        DEC     R0
        DEC     R1
        DJNZ    R7,DIV6         ;LOOP
        ;
        INC     R5              ;SUBTRACT COUNTER
        JC      DIV5            ;KEEP LOOPING IF CARRY
        MOV     A,@R1           ;GET CARRY
        SUBB    A,#1            ;CARRY IS CLEARED
        MOV     @R1,A           ;SAVE CARRY DIGIT
        CPL     C
        SJMP    DIV5            ;LOOP
        ;
        ; Restore the result if carry was found
        ;
DIV7:   ACALL   ADDLP           ;ADD NUMBER BACK
        MOV     @R1,#0          ;CLEAR CARRY
        MOV     R0B0,R2         ;GET SAVE COUNTER
        MOV     @R0,5           ;SAVE COUNT BYTE
        ;
        INC     R2              ;ADJUST SAVE COUNTER
        MOV     R7,#1           ;BUMP DIVIDEND
        ACALL   LEFT
        CJNE    R2,#FP_ACC8+2,DIV4
        ;
        DJNZ    FP_EXP,DIV8
        AJMP    UNDERFLOW_AND_EXIT
        ;
DIV8:   MOV     FP_CARRY,#0
        ;
        newpage
        ;***************************************************************
        ;
PACK:   ; Pack the mantissa
        ;
        ;***************************************************************
        ;
        ; First, set up the pointers
        ;
        MOV     R0,#FP_ACCC
        MOV     A,@R0           ;GET FP_ACCC
        MOV     R6,A            ;SAVE FOR ZERO COUNT
        JZ      PACK0           ;JUMP OVER IF ZERO
        ACALL   INC_FP_EXP      ;BUMP THE EXPONENT
        DEC     R0
        ;
PACK0:  INC     R0              ;POINT AT FP_ACC1
        ;
PACK1:  MOV     A,#8            ;ADJUST NIBBLE POINTER
        MOV     R1,A
        ADD     A,R0
        MOV     R0,A
        CJNE    @R0,#5,$+3      ;SEE IF ADJUSTING NEEDED
        JC      PACK3+1
        ;
PACK2:  SETB    C
        CLR     A
        DEC     R0
        ADDC    A,@R0
        DA      A
        XCHD    A,@R0           ;SAVE THE VALUE
        JNB     ACC.4,PACK3
        DJNZ    R1,PACK2
        ;
        DEC     R0
        MOV     @R0,#1
        ACALL   INC_FP_EXP
        SJMP    PACK4
        ;
PACK3:  DEC     R1
        MOV     A,R1
        CLR     C
        XCH     A,R0
        SUBB    A,R0
        MOV     R0,A
        ;
PACK4:  MOV     R1,#FP_DIG12
        ;
        ; Now, pack
        ;
PLOOP:  MOV     A,@R0
        SWAP    A               ;FLIP THE DIGITS
        INC     R0
        XCHD    A,@R0
        ORL     6,A             ;ACCUMULATE THE OR'ED DIGITS
        MOV     @R1,A
        INC     R0
        INC     R1
        CJNE    R1,#FP_SIGN,PLOOP
        MOV     A,R6
        JNZ     STORE_ALIGN_TEST_AND_EXIT
        MOV     FP_EXP,#0       ;ZERO EXPONENT
        ;
        ;**************************************************************
        ;
STORE_ALIGN_TEST_AND_EXIT:      ;Save the number align carry and exit
        ;
        ;**************************************************************
        ;
        ACALL   LOAD_POINTERS
        MOV     ARG_STACK,R1    ;SET UP THE NEW STACK
        MOV     R0,#FP_EXP
        ;
        ; Now load the numbers
        ;
STORE2: MOV     A,@R0
        MOVX    @R1,A           ;SAVE THE NUMBER
        DEC     R0
        DEC     R1
        CJNE    R0,#FP_CARRY,STORE2
        ;
        CLR     A               ;NO ERRORS
        ;
PRET:   RET                     ;EXIT
        ;
        newpage
INC_FP_EXP:
        ;
        INC     FP_EXP
        MOV     A,FP_EXP
        JNZ     PRET            ;EXIT IF NOT ZERO
        POP     ACC             ;WASTE THE CALLING STACK
        POP     ACC
        AJMP    OVERFLOW_AND_EXIT
        ;
;***********************************************************************
;
UNPACK_R0:      ; Unpack BCD digits and load into nibble locations
;
;***********************************************************************
        ;
        PUSH    R1B0
        MOV     R1,#FP_NIB8
        ;
ULOOP:  MOVX    A,@R0
        ANL     A,#0FH
        MOV     @R1,A           ;SAVE THE NIBBLE
        MOVX    A,@R0
        SWAP    A
        ANL     A,#0FH
        DEC     R1
        MOV     @R1,A           ;SAVE THE NIBBLE AGAIN
        DEC     R0
        DEC     R1
        CJNE    R1,#FP_NIB1-1,ULOOP
        ;
        POP     R1B0
        ;
LOAD7:  RET
        ;
        newpage
        ;**************************************************************
        ;
OVERFLOW_AND_EXIT:      ;LOAD 99999999 E+127,  SET OV BIT, AND EXIT
        ;
        ;**************************************************************
        ;
        MOV     R0,#FP_DIG78
        MOV     A,#99H
        ;
OVE1:   MOV     @R0,A
        DEC     R0
        CJNE    R0,#FP_CARRY,OVE1
        ;
        MOV     FP_EXP,#0FFH
        ACALL   STORE_ALIGN_TEST_AND_EXIT
        ;
        SETB    ACC_OVERFLOW            ; ******AA
        RET
        ;
        newpage
        ;**************************************************************
        ;
UNDERFLOW_AND_EXIT:     ;LOAD 0, SET UF BIT, AND EXIT
        ;
        ;**************************************************************
        ;
        ACALL   ZERO_AND_EXIT
        CLR     A
        SETB    ACC_UNDERFLOW           ; ******AA
        RET
        ;
        ;**************************************************************
        ;
ZERO_AND_EXIT:          ;LOAD 0, SET ZERO BIT, AND EXIT
        ;
        ;**************************************************************
        ;
        ACALL   FP_CLEAR
        ACALL   STORE_ALIGN_TEST_AND_EXIT
        SETB    ACC_ZERO                ; ******AA
        RET                     ;EXIT
        ;
        ;**************************************************************
        ;
FP_CLEAR:
        ;
        ; Clear internal storage
        ;
        ;**************************************************************
        ;
        CLR     A
        MOV     R0,#FP_ACC8+1
        ;
FPC1:   MOV     @R0,A
        DEC     R0
        CJNE    R0,#FP_TEMP,FPC1
        RET
        ;
        newpage
        ;**************************************************************
        ;
RIGHT:  ; Shift ACCUMULATOR RIGHT the number of nibbles in R7
        ; Save the shifted values in R4 if SAVE_ROUND is set
        ;
        ;**************************************************************
        ;
        MOV     R4,#0           ;IN CASE OF NO SHIFT
        ;
RIGHT1: CLR     C
        MOV     A,R7            ;GET THE DIGITS TO SHIFT
        JZ      RIGHT5-1        ;EXIT IF ZERO
        SUBB    A,#2            ;TWO TO DO?
        JNC     RIGHT5          ;SHIFT TWO NIBBLES
        ;
        ; Swap one nibble then exit
        ;
RIGHT3: PUSH    R0B0            ;SAVE POINTER REGISTER
        PUSH    R1B0
        ;
        MOV     R1,#FP_DIG78    ;LOAD THE POINTERS
        MOV     R0,#FP_DIG56
        MOV     A,R4            ;GET THE OVERFLOW REGISTER
        XCHD    A,@R1           ;GET DIGIT 8
        SWAP    A               ;FLIP FOR LOAD
        MOV     R4,A
        ;
RIGHTL: MOV     A,@R1           ;GET THE LOW ORDER BYTE
        XCHD    A,@R0           ;SWAP NIBBLES
        SWAP    A               ;FLIP FOR STORE
        MOV     @R1,A           ;SAVE THE DIGITS
        DEC     R0              ;BUMP THE POINTERS
        DEC     R1
        CJNE    R1,#FP_DIG12-1,RIGHTL   ;LOOP
        ;
        MOV     A,@R1           ;ACC = CH8
        SWAP    A               ;ACC = 8CH
        ANL     A,#0FH          ;ACC = 0CH
        MOV     @R1,A           ;CARRY DONE
        POP     R1B0            ;EXIT
        POP     R0B0            ;RESTORE REGISTER
        RET
        ;
RIGHT5: MOV     R7,A            ;SAVE THE NEW SHIFT NUMBER
        CLR     A
        XCH     A,FP_CARRY      ;SWAP THE NIBBLES
        XCH     A,FP_DIG12
        XCH     A,FP_DIG34
        XCH     A,FP_DIG56
        XCH     A,FP_DIG78
        MOV     R4,A            ;SAVE THE LAST DIGIT SHIFTED
        SJMP    RIGHT1+1
        ;
        newpage
        ;***************************************************************
        ;
LEFT:   ; Shift ACCUMULATOR LEFT the number of nibbles in R7
        ;
        ;***************************************************************
        ;
        MOV     R4,#00H         ;CLEAR FOR SOME ENTRYS
        ;
LEFT1:  CLR     C
        MOV     A,R7            ;GET SHIFT VALUE
        JZ      LEFT5-1         ;EXIT IF ZERO
        SUBB    A,#2            ;SEE HOW MANY BYTES TO SHIFT
        JNC     LEFT5
        ;
LEFT3:  PUSH    R0B0            ;SAVE POINTER
        PUSH    R1B0
        MOV     R0,#FP_CARRY
        MOV     R1,#FP_DIG12
        ;
        MOV     A,@R0           ;ACC=CHCL
        SWAP    A               ;ACC = CLCH
        MOV     @R0,A           ;ACC = CLCH, @R0 = CLCH
        ;
LEFTL:  MOV     A,@R1           ;DIG 12
        SWAP    A               ;DIG 21
        XCHD    A,@R0
        MOV     @R1,A           ;SAVE IT
        INC     R0              ;BUMP POINTERS
        INC     R1
        CJNE    R0,#FP_DIG78,LEFTL
        ;
        MOV     A,R4
        SWAP    A
        XCHD    A,@R0
        ANL     A,#0F0H
        MOV     R4,A
        ;
        POP     R1B0
        POP     R0B0            ;RESTORE
        RET                     ;DONE
        ;
LEFT5:  MOV     R7,A            ;RESTORE COUNT
        CLR     A
        XCH     A,R4            ;GET THE RESTORATION BYTE
        XCH     A,FP_DIG78      ;DO THE SWAP
        XCH     A,FP_DIG56
        XCH     A,FP_DIG34
        XCH     A,FP_DIG12
        XCH     A,FP_CARRY
        SJMP    LEFT1+1
        ;
        newpage
MUL_NIBBLE:
        ;
        ; Multiply the nibble in R7 by the FP_NIB locations
        ; accumulate the product in FP_ACC
        ;
        ; Set up the pointers for multiplication
        ;
        ANL     A,#0FH          ;STRIP OFF MS NIBBLE
        MOV     R7,A
        MOV     R0,#FP_ACC8
        MOV     R1,#FP_NIB8
        CLR     A
        MOV     FP_ACCX,A
        ;
MNLOOP: DEC     R0              ;BUMP POINTER TO PROPAGATE CARRY
        ADD     A,@R0           ;ATTEMPT TO FORCE CARRY
        DA      A               ;BCD ADJUST
        JNB     ACC.4,MNL0      ;DON'T ADJUST IF NO NEED
        DEC     R0              ;PROPAGATE CARRY TO THE NEXT DIGIT
        INC     @R0             ;DO THE ADJUSTING
        INC     R0              ;RESTORE R0
        ;
MNL0:   XCHD    A,@R0           ;RESTORE INITIAL NUMBER
        MOV     B,R7            ;GET THE NUBBLE TO MULTIPLY
        MOV     A,@R1           ;GET THE OTHER NIBBLE
        MUL     AB              ;DO THE MULTIPLY
        MOV     B,#10           ;NOW BCD ADJUST
        DIV     AB
        XCH     A,B             ;GET THE REMAINDER
        ADD     A,@R0           ;PROPAGATE THE PARTIAL PRODUCTS
        DA      A               ;BCD ADJUST
        JNB     ACC.4,MNL1      ;PROPAGATE PARTIAL PRODUCT CARRY
        INC     B
        ;
MNL1:   INC     R0
        XCHD    A,@R0           ;SAVE THE NEW PRODUCT
        DEC     R0
        MOV     A,B             ;GET BACK THE QUOTIENT
        DEC     R1
        CJNE    R1,#FP_NIB1-1,MNLOOP
        ;
        ADD     A,FP_ACCX       ;GET THE OVERFLOW
        DA      A               ;ADJUST
        MOV     @R0,A           ;SAVE IT
        RET                     ;EXIT
        ;
        newpage
        ;***************************************************************
        ;
LOAD_POINTERS:  ; Load the ARG_STACK into R0 and bump R1
        ;
        ;***************************************************************
        ;
        MOV     P2,#ARG_STACK_PAGE
        MOV     R0,ARG_STACK
        MOV     A,#FP_NUMBER_SIZE
        ADD     A,R0
        MOV     R1,A
        RET
        ;
        ;***************************************************************
        ;
MUL_DIV_EXP_AND_SIGN:
        ;
        ; Load the sign into R7, R6. R5 gets the sign for
        ; multiply and divide.
        ;
        ;***************************************************************
        ;
        ACALL   FP_CLEAR        ;CLEAR INTERNAL MEMORY
        ;
MDES1:  ACALL   LOAD_POINTERS   ;LOAD REGISTERS
        MOVX    A,@R0           ;ARG 1 EXP
        MOV     R7,A            ;SAVED IN R7
        MOVX    A,@R1           ;ARG 2 EXP
        MOV     R6,A            ;SAVED IN R6
        DEC     R0              ;BUMP POINTERS TO SIGN
        DEC     R1
        MOVX    A,@R0           ;GET THE SIGN
        MOV     R4,A            ;SIGN OF ARG1
        MOVX    A,@R1           ;GET SIGN OF NEXT ARG
        MOV     R3,A            ;SIGN OF ARG2
        XRL     A,R4            ;ACC GETS THE NEW SIGN
        MOV     R5,A            ;R5 GETS THE NEW SIGN
        ;
        ; Bump the pointers to point at the LS digit
        ;
        DEC     R0
        DEC     R1
        ;
        RET
        ;
        newpage
        ;***************************************************************
        ;
LOADR1_MANTISSA:
        ;
        ; Load the mantissa of R0 into FP_Digits
        ;
        ;***************************************************************
        ;
        PUSH    R0B0            ;SAVE REGISTER 1
        MOV     R0,#FP_DIG78    ;SET UP THE POINTER
        ;
LOADR1: MOVX    A,@R1
        MOV     @R0,A
        DEC     R1
        DEC     R0
        CJNE    R0,#FP_CARRY,LOADR1
        ;
        POP     R0B0
        RET
        ;
        newpage
        ;***************************************************************
        ;
HEXSCAN:        ; Scan a string to determine if it is a hex number
                ; set carry if hex, else carry = 0
        ;
        ;***************************************************************
        ;
        ACALL   GET_DPTR_CHARACTER
        PUSH    DPH
        PUSH    DPL             ;SAVE THE POINTER
        ;
HEXSC1: MOVX    A,@DPTR         ;GET THE CHARACTER
        ACALL   DIGIT_CHECK     ;SEE IF A DIGIT
        JC      HS1             ;CONTINUE IF A DIGIT
        ACALL   HEX_CHECK       ;SEE IF HEX
        JC      HS1
        ;
        CLR     ACC.5           ;NO LOWER CASE
        CJNE    A,#'H',HEXDON
        SETB    C
        SJMP    HEXDO1          ;NUMBER IS VALID HEX, MAYBE
        ;
HEXDON: CLR     C
        ;
HEXDO1: POP     DPL             ;RESTORE POINTER
        POP     DPH
        RET
        ;
HS1:    INC     DPTR            ;BUMP TO NEXT CHARACTER
        SJMP    HEXSC1          ;LOOP
        ;
HEX_CHECK:      ;CHECK FOR A VALID ASCII HEX, SET CARRY IF FOUND
        ;
        CLR     ACC.5           ;WASTE LOWER CASE
        CJNE    A,#'F'+1,$+3    ;SEE IF F OR LESS
        JC      HC1
        RET
        ;
HC1:    CJNE    A,#'A',$+3      ;SEE IF A OR GREATER
        CPL     C
        RET
        ;
        newpage
        ;
PUSHR2R0:
        ; 
        MOV     R3,#HI(CONVT)   ;CONVERSION LOCATION
        MOV     R1,#LO(CONVT)
        ACALL   CONVERT_BINARY_TO_ASCII_STRING
        MOV     A,#0DH          ;A CR TO TERMINATE
        MOVX    @R1,A           ;SAVE THE CR
        MOV     DPTR,#CONVT
        ;
        ; Falls thru to FLOATING INPUT
        ;
        newpage
        ;***************************************************************
        ;
FLOATING_POINT_INPUT:   ; Input a floating point number pointed to by
                        ; the DPTR
        ;
        ;***************************************************************
        ;
        ACALL   FP_CLEAR        ;CLEAR EVERYTHING
        ACALL   GET_DPTR_CHARACTER
        ACALL   PLUS_MINUS_TEST
        MOV     MSIGN,C         ;SAVE THE MANTISSA SIGN
        ;
        ; Now, set up for input loop
        ;
        MOV     R0,#FP_ACCC
        MOV     R6,#7FH         ;BASE EXPONENT
        SETB    F0              ;SET INITIAL FLAG
        ;
INLOOP: ACALL   GET_DIGIT_CHECK
        JNC     GTEST           ;IF NOT A CHARACTER, WHAT IS IT?
        ANL     A,#0FH          ;STRIP ASCII
        ACALL   STDIG           ;STORE THE DIGITS
        ;
INLPIK: INC     DPTR            ;BUMP POINTER FOR LOOP
        SJMP    INLOOP          ;LOOP FOR INPUT
        ;
GTEST:  CJNE    A,#'.',GT1      ;SEE IF A RADIX
        JB      FOUND_RADIX,INERR
        SETB    FOUND_RADIX
        CJNE    R0,#FP_ACCC,INLPIK
        SETB    FIRST_RADIX     ;SET IF FIRST RADIX
        SJMP    INLPIK          ;GET ADDITIONAL DIGITS
        ;
GT1:    JB      F0,INERR        ;ERROR IF NOT CLEARED
        CJNE    A,#'e',$+5      ;CHECK FOR LOWER CASE
        SJMP    $+5
        CJNE    A,#'E',FINISH_UP
        ACALL   INC_AND_GET_DPTR_CHARACTER
        ACALL   PLUS_MINUS_TEST
        MOV     XSIGN,C         ;SAVE SIGN STATUS
        ACALL   GET_DIGIT_CHECK
        JNC     INERR
        ;
        ANL     A,#0FH          ;STRIP ASCII BIAS OFF THE CHARACTER
        MOV     R5,A            ;SAVE THE CHARACTER IN R5
        ;
GT2:    INC     DPTR
        ACALL   GET_DIGIT_CHECK
        JNC     FINISH1
        ANL     A,#0FH          ;STRIP OFF BIAS
        XCH     A,R5            ;GET THE LAST DIGIT
        MOV     B,#10           ;MULTIPLY BY TEN
        MUL     AB
        ADD     A,R5            ;ADD TO ORIGINAL VALUE
        MOV     R5,A            ;SAVE IN R5
        JNC     GT2             ;LOOP IF NO CARRY
        MOV     R5,#0FFH        ;FORCE AN ERROR
        ;
FINISH1:MOV     A,R5            ;GET THE SIGN
        JNB     XSIGN,POSNUM    ;SEE IF EXPONENT IS POS OR NEG
        CLR     C
        SUBB    A,R6
        CPL     A
        INC     A
        JC      FINISH2
        MOV     A,#01H
        RET
        ;
POSNUM: ADD     A,R6            ;ADD TO EXPONENT
        JNC     FINISH2
        ;
POSNM1: MOV     A,#02H
        RET
        ;
FINISH2:XCH     A,R6            ;SAVE THE EXPONENT
        ;
FINISH_UP:
        ;
        MOV     FP_EXP,R6       ;SAVE EXPONENT
        CJNE    R0,#FP_ACCC,$+5
        ACALL   FP_CLEAR        ;CLEAR THE MEMORY IF 0
        MOV     A,ARG_STACK     ;GET THE ARG STACK
        CLR     C
        SUBB    A,#FP_NUMBER_SIZE+FP_NUMBER_SIZE
        MOV     ARG_STACK,A     ;ADJUST FOR STORE
        AJMP    PACK
        ;
STDIG:  CLR     F0              ;CLEAR INITIAL DESIGNATOR
        JNZ     STDIG1          ;CONTINUE IF NOT ZERO
        CJNE    R0,#FP_ACCC,STDIG1
        JNB     FIRST_RADIX,RET_X
        ;
DECX:   DJNZ    R6,RET_X
        ;
INERR:  MOV     A,#0FFH
        ;
RET_X:  RET
        ;
STDIG1: JB      DONE_LOAD,FRTEST
        CLR     FIRST_RADIX
        ;
FRTEST: JB      FIRST_RADIX,DECX
        ;
FDTEST: JB      FOUND_RADIX,FDT1
        INC     R6
        ;
FDT1:   JB      DONE_LOAD,RET_X
        CJNE    R0,#FP_ACC8+1,FDT2
        SETB    DONE_LOAD
        ;
FDT2:   MOV     @R0,A           ;SAVE THE STRIPPED ACCUMULATOR
        INC     R0              ;BUMP THE POINTER
        RET                     ;EXIT
        ;
        newpage
        ;***************************************************************
        ;
        ; I/O utilities
        ;
        ;***************************************************************
        ;
INC_AND_GET_DPTR_CHARACTER:
        ;
        INC     DPTR
        ;
GET_DPTR_CHARACTER:
        ;
        MOVX    A,@DPTR         ;GET THE CHARACTER
        CJNE    A,#' ',PMT1     ;SEE IF A SPACE
        ;
        ; Kill spaces
        ;
        SJMP    INC_AND_GET_DPTR_CHARACTER
        ;
PLUS_MINUS_TEST:
        ;
        CJNE    A,#0E3H,$+5     ;SEE IF A PLUS, PLUS TOKEN FROM BASIC
        SJMP    PMT3
        CJNE    A,#'+',$+5
        SJMP    PMT3
        CJNE    A,#0E5H,$+5     ;SEE IF MINUS, MINUS TOKEN FROM BASIC
        SJMP    PMT2
        CJNE    A,#'-',PMT1
        ;
PMT2:   SETB    C
        ;
PMT3:   INC     DPTR
        ;
PMT1:   RET
        ;
        newpage
        ;***************************************************************
        ;
FLOATING_POINT_OUTPUT:  ; Output the number, format is in location 23
        ;
        ; IF FORMAT = 00 - FREE FLOATING
        ;           = FX - EXPONENTIAL (X IS THE NUMBER OF SIG DIGITS)
        ;           = NX - N = NUM BEFORE RADIX, X = NUM AFTER RADIX
        ;                  N + X = 8 MAX
        ;
        ;***************************************************************
        ;
        ACALL   MDES1           ;GET THE NUMBER TO OUTPUT, R0 IS POINTER
        ACALL   POP_AND_EXIT    ;OUTPUT POPS THE STACK
        MOV     A,R7
        MOV     R6,A            ;PUT THE EXPONENT IN R6
        ACALL   UNPACK_R0       ;UNPACK THE NUMBER
        MOV     R0,#FP_NIB1     ;POINT AT THE NUMBER
        MOV     A,FORMAT        ;GET THE FORMAT
        MOV     R3,A            ;SAVE IN CASE OF EXP FORMAT
        JZ      FREE            ;FREE FLOATING?
        CJNE    A,#0F0H,$+3     ;SEE IF EXPONENTIAL
        JNC     EXPOUT
        ;
        ; If here, must be integer USING format
        ;
        MOV     A,R6            ;GET THE EXPONENT
        JNZ     $+4
        MOV     R6,#80H
        MOV     A,R3            ;GET THE FORMAT
        SWAP    A               ;SPLIT INTEGER AND FRACTION
        ANL     A,#0FH
        MOV     R2,A            ;SAVE INTEGER
        ACALL   NUM_LT          ;GET THE NUMBER OF INTEGERS
        XCH     A,R2            ;FLIP FOR SUBB
        CLR     C
        SUBB    A,R2
        MOV     R7,A
        JNC     $+8
        MOV     R5,#'?'         ;OUTPUT A QUESTION MARK
        ACALL   SOUT1           ;NUMBER IS TOO LARGE FOR FORMAT
        AJMP    FREE
        CJNE    R2,#00,USING0   ;SEE IF ZERO
        DEC     R7
        ACALL   SS7
        ACALL   ZOUT            ;OUTPUT A ZERO
        SJMP    USING1
        ;
USING0: ACALL   SS7             ;OUTPUT SPACES, IF NEED TO
        MOV     A,R2            ;OUTPUT DIGITS
        MOV     R7,A
        ACALL   OUTR0
        ;
USING1: MOV     A,R3
        ANL     A,#0FH          ;GET THE NUMBER RIGHT OF DP
        MOV     R2,A            ;SAVE IT
        JZ      PMT1            ;EXIT IF ZERO
        ACALL   ROUT            ;OUTPUT DP
        ACALL   NUM_RT
        CJNE    A,2,USINGX      ;COMPARE A TO R2
        ;
USINGY: MOV     A,R2
        AJMP    Z7R7
        ;
USINGX: JNC     USINGY
        ;
USING2: XCH     A,R2
        CLR     C
        SUBB    A,R2
        XCH     A,R2
        ACALL   Z7R7            ;OUTPUT ZEROS IF NEED TO
        MOV     A,R2
        MOV     R7,A
        AJMP    OUTR0
        ;
        ; First, force exponential output, if need to
        ;
FREE:   MOV     A,R6            ;GET THE EXPONENT
        JNZ     FREE1           ;IF ZERO, PRINT IT
        ACALL   SOUT
        AJMP    ZOUT
        ;
FREE1:  MOV     R3,#0F0H        ;IN CASE EXP NEEDED
        MOV     A,#80H-DIGIT-DIGIT-1
        ADD     A,R6
        JC      EXPOUT
        SUBB    A,#0F7H
        JC      EXPOUT
        ;
        ; Now, just print the number
        ;
        ACALL   SINOUT          ;PRINT THE SIGN OF THE NUMBER
        ACALL   NUM_LT          ;GET THE NUMBER LEFT OF DP
        CJNE    A,#8,FREE4
        AJMP    OUTR0
        ;
FREE4:  ACALL   OUTR0
        ACALL   ZTEST           ;TEST FOR TRAILING ZEROS
        JZ      U_RET           ;DONE IF ALL TRAILING ZEROS
        ACALL   ROUT            ;OUTPUT RADIX
        ;
FREE2:  MOV     R7,#1           ;OUTPUT ONE DIGIT
        ACALL   OUTR0
        JNZ     U_RET
        ACALL   ZTEST
        JZ      U_RET
        SJMP    FREE2           ;LOOP
        ;
EXPOUT: ACALL   SINOUT          ;PRINT THE SIGN
        MOV     R7,#1           ;OUTPUT ONE CHARACTER
        ACALL   OUTR0
        ACALL   ROUT            ;OUTPUT RADIX
        MOV     A,R3            ;GET FORMAT
        ANL     A,#0FH          ;STRIP INDICATOR
        JZ      EXPOTX
        ;
        MOV     R7,A            ;OUTPUT THE NUMBER OF DIGITS
        DEC     R7              ;ADJUST BECAUSE ONE CHAR ALREADY OUT
        ACALL   OUTR0
        SJMP    EXPOT4
        ;
EXPOTX: ACALL   FREE2           ;OUTPUT UNTIL TRAILING ZEROS
        ;
EXPOT4: ACALL   SOUT            ;OUTPUT A SPACE
        MOV     R5,#'E'
        ACALL   SOUT1           ;OUTPUT AN E
        MOV     A,R6            ;GET THE EXPONENT
        JZ      XOUT0           ;EXIT IF ZERO
        DEC     A               ;ADJUST FOR THE DIGIT ALREADY OUTPUT
        CJNE    A,#80H,XOUT2    ;SEE WHAT IT IS
        ;
XOUT0:  ACALL   SOUT
        CLR     A
        SJMP    XOUT4
        ;
XOUT2:  JC      XOUT3           ;NEGATIVE EXPONENT
        MOV     R5,#'+'         ;OUTPUT A PLUS SIGN
        ACALL   SOUT1
        SJMP    XOUT4
        ;
XOUT3:  ACALL   MOUT
        CPL     A               ;FLIP BITS
        INC     A               ;BUMP
        ;
XOUT4:  CLR     ACC.7
        MOV     R0,A
        MOV     R2,#0
        MOV     R1,#LO(CONVT)   ;CONVERSION LOCATION
        MOV     R3,#HI(CONVT)
        ACALL   CONVERT_BINARY_TO_ASCII_STRING
        MOV     R0,#LO(CONVT)   ;NOW, OUTPUT EXPONENT
        ;
EXPOT5: MOVX    A,@R0           ;GET THE CHARACTER
        MOV     R5,A            ;OUTPUT IT
        ACALL   SOUT1
        INC     R0              ;BUMP THE POINTER
        MOV     A,R0            ;GET THE POINTER
        CJNE    A,R1B0,EXPOT5   ;LOOP
        ;
U_RET:  RET                     ;EXIT
        ;
OUTR0:  ; Output the characters pointed to by R0, also bias ascii
        ;
        MOV     A,R7            ;GET THE COUNTER
        JZ      OUTR            ;EXIT IF DONE
        MOV     A,@R0           ;GET THE NUMBER
        ORL     A,#30H          ;ASCII BIAS
        INC     R0              ;BUMP POINTER AND COUNTER
        DEC     R7
        MOV     R5,A            ;PUT CHARACTER IN OUTPUT REGISTER
        ACALL   SOUT1           ;OUTPUT THE CHARACTER
        CLR     A               ;JUST FOR TEST
        CJNE    R0,#FP_NIB8+1,OUTR0
        MOV     A,#55H          ;KNOW WHERE EXIT OCCURED
        ;
OUTR:   RET
        ;
ZTEST:  MOV     R1,R0B0         ;GET POINTER REGISTER
        ;
ZT0:    MOV     A,@R1           ;GET THE VALUE
        JNZ     ZT1
        INC     R1              ;BUMP POINTER
        CJNE    R1,#FP_NIB8+1,ZT0
        ;
ZT1:    RET
        ;
NUM_LT: MOV     A,R6            ;GET EXPONENT
        CLR     C               ;GET READY FOR SUBB
        SUBB    A,#80H          ;SUB EXPONENT BIAS
        JNC     NL1             ;OK IF NO CARRY
        CLR     A               ;NO DIGITS LEFT
        ;
NL1:    MOV     R7,A            ;SAVE THE COUNT
        RET
        ;
NUM_RT: CLR     C               ;SUBB AGAIN
        MOV     A,#80H          ;EXPONENT BIAS
        SUBB    A,R6            ;GET THE BIASED EXPONENT
        JNC     NR1
        CLR     A
        ;
NR1:    RET                     ;EXIT
        ;
SPACE7: MOV     A,R7            ;GET THE NUMBER OF SPACES
        JZ      NR1             ;EXIT IF ZERO
        ACALL   SOUT            ;OUTPUT A SPACE
        DEC     R7              ;BUMP COUNTER
        SJMP    SPACE7          ;LOOP
        ;
Z7R7:   MOV     R7,A
        ;
ZERO7:  MOV     A,R7            ;GET COUNTER
        JZ      NR1             ;EXIT IF ZERO
        ACALL   ZOUT            ;OUTPUT A ZERO
        DEC     R7              ;BUMP COUNTER
        SJMP    ZERO7           ;LOOP
        ;
SS7:    ACALL   SPACE7
        ;
SINOUT: MOV     A,R4            ;GET THE SIGN
        JZ      SOUT            ;OUTPUT A SPACE IF ZERO
        ;
MOUT:   MOV     R5,#'-'
        SJMP    SOUT1           ;OUTPUT A MINUS IF NOT
        ;
ROUT:   MOV     R5,#'.'         ;OUTPUT A RADIX
        SJMP    SOUT1
        ;
ZOUT:   MOV     R5,#'0'          ;OUTPUT A ZERO
        SJMP    SOUT1
        ;
SOUT:   MOV     R5,#' '         ;OUTPUT A SPACE
        ;
SOUT1:  AJMP    OUTPUT
        ;
        newpage
        ;***************************************************************
        ;
CONVERT_ASCII_STRING_TO_BINARY:
        ;
        ;DPTR POINTS TO ASCII STRING
        ;PUT THE BINARY NUMBER IN R2:R0, ERROR IF >64K
        ;
        ;***************************************************************
        ;
CASB:   ACALL   HEXSCAN         ;SEE IF HEX NUMBER
        MOV     ADD_IN,C        ;IF ADD_IN IS SET, THE NUMBER IS HEX
        ACALL   GET_DIGIT_CHECK
        CPL     C               ;FLIP FOR EXIT
        JC      RCASB
        MOV     R3,#00H         ;ZERO R3:R1 FOR LOOP
        MOV     R1,#00H
        SJMP    CASB5
        ;
CASB2:  INC     DPTR
        MOV     R0B0,R1         ;SAVE THE PRESENT CONVERTED VALUE
        MOV     R0B0+2,R3       ;IN R2:R0
        ACALL   GET_DIGIT_CHECK
        JC      CASB5
        JNB     ADD_IN,RCASB    ;CONVERSION COMPLETE
        ACALL   HEX_CHECK       ;SEE IF HEX NUMBER
        JC      CASB4           ;PROCEED IF GOOD
        INC     DPTR            ;BUMP PAST H
        SJMP    RCASB
        ;
CASB4:  ADD     A,#9            ;ADJUST HEX ASCII BIAS
        ;
CASB5:  MOV     B,#10
        JNB     ADD_IN,CASB6
        MOV     B,#16           ;HEX MODE
        ;
CASB6:  ACALL   MULNUM          ;ACCUMULATE THE DIGITS
        JNC     CASB2           ;LOOP IF NO CARRY
        ;
RCASB:  CLR     A               ;RESET ACC
        MOV     ACC_OVERFLOW,C  ;IF OVERFLOW, SAY SO ******AA
        RET                     ;EXIT
        ;
        newpage
        ;
MULNUM10:MOV    B,#10
        ;
        ;***************************************************************
        ;
MULNUM: ; Take the next digit in the acc (masked to 0FH)
        ; accumulate in R3:R1
        ;
        ;***************************************************************
        ;
        PUSH    ACC             ;SAVE ACC
        PUSH    B               ;SAVE MULTIPLIER
        MOV     A,R1            ;PUT LOW ORDER BITS IN ACC
        MUL     AB              ;DO THE MULTIPLY
        MOV     R1,A            ;PUT THE RESULT BACK
        MOV     A,R3            ;GET THE HIGH ORDER BYTE
        MOV     R3,B            ;SAVE THE OVERFLOW
        POP     B               ;GET THE MULTIPLIER
        MUL     AB              ;DO IT
        MOV     C,OV            ;SAVE OVERFLOW IN F0
        MOV     F0,C
        ADD     A,R3            ;ADD OVERFLOW TO HIGH RESULT
        MOV     R3,A            ;PUT IT BACK
        POP     ACC             ;GET THE ORIGINAL ACC BACK
        ORL     C,F0            ;OR CARRY AND OVERFLOW
        JC      MULX            ;NO GOOD IF THE CARRY IS SET
        ;
MUL11:  ANL     A,#0FH          ;MASK OFF HIGH ORDER BITS
        ADD     A,R1            ;NOW ADD THE ACC
        MOV     R1,A            ;PUT IT BACK
        CLR     A               ;PROPAGATE THE CARRY
        ADDC    A,R3
        MOV     R3,A            ;PUT IT BACK
        ;
MULX:   RET                     ;EXIT WITH OR WITHOUT CARRY
        ;
        ;***************************************************************
        ;
CONVERT_BINARY_TO_ASCII_STRING:
        ;
        ;R3:R1 contains the address of the string
        ;R2:R0 contains the value to convert
        ;DPTR, R7, R6, and ACC gets clobbered
        ;
        ;***************************************************************
        ;
        CLR     A               ;NO LEADING ZEROS
        MOV     DPTR,#10000     ;SUBTRACT 10000
        ACALL   RSUB            ;DO THE SUBTRACTION
        MOV     DPTR,#1000      ;NOW 1000
        ACALL   RSUB
        MOV     DPTR,#100       ;NOW 100
        ACALL   RSUB
        MOV     DPTR,#10        ;NOW 10
        ACALL   RSUB
        MOV     DPTR,#1         ;NOW 1
        ACALL   RSUB
        JZ      RSUB2           ;JUMP OVER RET
        ;
RSUB_R: RET
        ;
RSUB:   MOV     R6,#-1          ;SET UP THE COUNTER
        ;
RSUB1:  INC     R6              ;BUMP THE COUNTER
        XCH     A,R2            ;DO A FAST COMPARE
        CJNE    A,DPH,$+3
        XCH     A,R2
        JC      FAST_DONE
        XCH     A,R0            ;GET LOW BYTE
        SUBB    A,DPL           ;SUBTRACT, CARRY IS CLEARED
        XCH     A,R0            ;PUT IT BACK
        XCH     A,R2            ;GET THE HIGH BYTE
        SUBB    A,DPH           ;ADD THE HIGH BYTE
        XCH     A,R2            ;PUT IT BACK
        JNC     RSUB1           ;LOOP UNTIL CARRY
        ;
        XCH     A,R0
        ADD     A,DPL           ;RESTORE R2:R0
        XCH     A,R0
        XCH     A,R2
        ADDC    A,DPH
        XCH     A,R2
        ;
FAST_DONE:
        ;
        ORL     A,R6            ;OR THE COUNT VALUE
        JZ      RSUB_R          ;RETURN IF ZERO
        ;
RSUB2:  MOV     A,#'0'           ;GET THE ASCII BIAS
        ADD     A,R6            ;ADD THE COUNT
        ;
RSUB4:  MOV     P2,R3           ;SET UP P2
        MOVX    @R1,A           ;PLACE THE VALUE IN MEMORY
        INC     R1
        CJNE    R1,#00H,RSUB3   ;SEE IF RAPPED AROUND
        INC     R3              ;BUMP HIGH BYTE
        ;
RSUB3:  RET                     ;EXIT
        ;
        newpage
        ;***************************************************************
        ;
HEXOUT: ; Output the hex number in R3:R1, supress leading zeros, if set
        ;
        ;***************************************************************
        ;
        ACALL   SOUT            ;OUTPUT A SPACE
        MOV     C,ZSURP         ;GET ZERO SUPPRESSION BIT
        MOV     ADD_IN,C
        MOV     A,R3            ;GET HIGH NIBBLE AND PRINT IT
        ACALL   HOUTHI
        MOV     A,R3
        ACALL   HOUTLO
        ;
HEX2X:  CLR     ADD_IN          ;DON'T SUPPRESS ZEROS
        MOV     A,R1            ;GET LOW NIBBLE AND PRINT IT
        ACALL   HOUTHI
        MOV     A,R1
        ACALL   HOUTLO
        MOV     R5,#'H'         ;OUTPUT H TO INDICATE HEX MODE
        ;
SOUT_1: AJMP    SOUT1
        ;
HOUT1:  CLR     ADD_IN          ;PRINTED SOMETHING, SO CLEAR ADD_IN
        ADD     A,#90H          ;CONVERT TO ASCII
        DA      A
        ADDC    A,#40H
        DA      A               ;GOT IT HERE
        MOV     R5,A            ;OUTPUT THE BYTE
        SJMP    SOUT_1
        ;
HOUTHI: SWAP    A               ;SWAP TO OUTPUT HIGH NIBBLE
        ;
HOUTLO: ANL     A,#0FH          ;STRIP
        JNZ     HOUT1           ;PRINT IF NOT ZERO
        JNB     ADD_IN,HOUT1    ;OUTPUT A ZERO IF NOT SUPRESSED
        RET
        ;
        newpage
        ORG     1FEBH           ;FOR LINK COMPATABILITY
        ;
        ;
GET_DIGIT_CHECK:        ; Get a character, then check for digit
        ;
        ACALL   GET_DPTR_CHARACTER
        ;
DIGIT_CHECK:    ;CHECK FOR A VALID ASCII DIGIT, SET CARRY IF FOUND
        ;
        CJNE    A,#'9'+1,$+3    ;SEE IF ASCII 9 OR LESS
        JC      DC1
        RET
        ;
DC1:    CJNE    A,#'0',$+3       ;SEE IF ASCII 0 OR GREATER
        CPL     C
        RET

        endsection