Blame | Last modification | View Log | Download | RSS feed
;************************************************************;; 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 codenewpagesection 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 POINTERARG_STACK_PAGE EQU 1FORMAT EQU 23 ;LOCATION OF OUTPUT FORMAT BYTEOUTPUT EQU 1990H ;CALL LOCATION TO OUTPUT A CHARACTERCONVT EQU 58H ;LOCATION TO CONVERT NUMBERSINTGRC BIT 25 ;BIT SET IF INTGER ERRORZSURP BIT 54 ;ZERO SUPRESSION FOR HEX PRINT;;***********************************************************************;; The following equates are used internally;;***********************************************************************;FP_NUMBER_SIZE EQU 6DIGIT EQU FP_NUMBER_SIZE-2R0B0 EQU 0R1B0 EQU 1UNDERFLOW EQU 0ACC_UNDERFLOW BIT ACC.0 ; ******AA addedOVERFLOW EQU 1ACC_OVERFLOW BIT ACC.1 ; ******AA addedZERO EQU 2ACC_ZERO BIT ACC.2 ; ******AA addedZERO_DIVIDE EQU 3ACC_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 USEDFP_TEMP EQU FP_STATUS+1 ;NOT USEDFP_CARRY SFRB FP_STATUS+2 ;USED FOR BITS ******AA EQU-->SFRBADD_IN BIT 35 ;DCMPXZ IN BASIC BACKAGEXSIGN BIT FP_CARRY.0FOUND_RADIX BIT FP_CARRY.1FIRST_RADIX BIT FP_CARRY.2DONE_LOAD BIT FP_CARRY.3FP_DIG12 EQU FP_CARRY+1FP_DIG34 EQU FP_CARRY+2FP_DIG56 EQU FP_CARRY+3FP_DIG78 EQU FP_CARRY+4FP_SIGN SFRB FP_CARRY+5 ; ******AA EQU-->SFRBMSIGN BIT FP_SIGN.0FP_EXP EQU FP_CARRY+6FP_NIB1 EQU FP_DIG12FP_NIB2 EQU FP_NIB1+1FP_NIB3 EQU FP_NIB1+2FP_NIB4 EQU FP_NIB1+3FP_NIB5 EQU FP_NIB1+4FP_NIB6 EQU FP_NIB1+5FP_NIB7 EQU FP_NIB1+6FP_NIB8 EQU FP_NIB1+7FP_ACCX EQU FP_NIB1+8FP_ACCC EQU FP_NIB1+9FP_ACC1 EQU FP_NIB1+10FP_ACC2 EQU FP_NIB1+11FP_ACC3 EQU FP_NIB1+12FP_ACC4 EQU FP_NIB1+13FP_ACC5 EQU FP_NIB1+14FP_ACC6 EQU FP_NIB1+15FP_ACC7 EQU FP_NIB1+16FP_ACC8 EQU FP_NIB1+17FP_ACCS EQU FP_NIB1+18;newpageORG 1993H;;**************************************************************;; The floating point entry points and jump table;;**************************************************************;AJMP FLOATING_ADDAJMP FLOATING_SUBAJMP FLOATING_COMPAJMP FLOATING_MULAJMP FLOATING_DIVAJMP HEXSCANAJMP FLOATING_POINT_INPUTAJMP FLOATING_POINT_OUTPUTAJMP CONVERT_BINARY_TO_ASCII_STRINGAJMP CONVERT_ASCII_STRING_TO_BINARYAJMP MULNUM10AJMP HEXOUTAJMP PUSHR2R0;newpage;FLOATING_SUB:;MOV P2,#ARG_STACK_PAGEMOV R0,ARG_STACKDEC R0 ;POINT TO SIGNMOVX A,@R0 ;READ SIGNCPL ACC.0MOVX @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 EXPONENTJZ POP_AND_EXIT ;IF TOS=0 THEN POP AND EXITCJNE R6,#0,LOAD1 ;CLEAR CARRY EXIT IF ZERO;;**************************************************************;SWAP_AND_EXIT: ; Swap external args and return;;**************************************************************;ACALL LOAD_POINTERSMOV R7,#FP_NUMBER_SIZE;SE1: MOVX A,@R0 ;SWAP THE ARGUMENTSMOVX @R1,ADEC R0DEC R1DJNZ R7,SE1;POP_AND_EXIT:;MOV A,ARG_STACK ;POP THE STACKADD A,#FP_NUMBER_SIZEMOV ARG_STACK,ACLR ARET;;LOAD1: SUBB A,R6 ;A = ARG 1 EXP - ARG 2 EXPMOV FP_EXP,R7 ;SAVE EXPONENT AND SIGNMOV FP_SIGN,R4JNC LOAD2 ;ARG1 EXPONENT IS LARGER OR SAMEMOV FP_EXP,R6MOV FP_SIGN,R3CPL AINC A ;COMPENSATE FOR EXP DELTAXCH A,R0 ;FORCE R0 TO POINT AT THE LARGESTXCH A,R1 ;EXPONENTXCH A,R0;LOAD2: MOV R7,A ;SAVE THE EXPONENT DELTA IN R7CLR ADD_INCJNE R5,#0,$+5SETB 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,$+3JC $+4MOV R7,#DIGIT+DIGIT+2;MOV FP_CARRY,#00 ;CLEAR THE CARRYACALL RIGHT ;SHIFT THE NUMBER;; Set up for addition and subtraction;MOV R7,#DIGIT ;LOOP COUNTMOV R1,#FP_DIG78MOV A,#9EHCLR CSUBB A,R4DA AXCH A,R4JNZ $+3MOV R4,ACJNE A,#50H,$+3 ;TEST FOR SUBTRACTIONJNB ADD_IN,SUBLP ;DO SUBTRACTION IF NO ADD_INCPL C ;FLIP CARRY FOR ADDITIONACALL ADDLP ;DO ADDITION;JNC ADD_RINC FP_CARRYMOV R7,#1ACALL RIGHTACALL INC_FP_EXP ;SHIFT AND BUMP EXPONENT;ADD_R: AJMP STORE_ALIGN_TEST_AND_EXIT;ADDLP: MOVX A,@R0ADDC A,@R1DA AMOV @R1,ADEC R0DEC R1DJNZ R7,ADDLP ;LOOP UNTIL DONERET;newpage;SUBLP: MOVX A,@R0 ;NOW DO SUBTRACTIONMOV R6,ACLR AADDC A,#99HSUBB A,@R1ADD A,R6DA AMOV @R1,ADEC R0DEC R1DJNZ R7,SUBLPJC 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.0MOV R1,#FP_DIG78MOV R7,#DIGIT ;LOOP COUNT;FSUB5: MOV A,#9AHSUBB A,@R1ADD A,#0DA AMOV @R1,ADEC R1CPL CDJNZ R7,FSUB5 ;LOOP;; Now see how many zeros their are;FSUB6: MOV R0,#FP_DIG12MOV R7,#0;FSUB7: MOV A,@R0JNZ FSUB8INC R7INC R7INC R0CJNE R0,#FP_SIGN,FSUB7AJMP ZERO_AND_EXIT;FSUB8: CJNE A,#10H,$+3JNC FSUB9INC R7;; Now R7 has the number of leading zeros in the FP ACC;FSUB9: MOV A,FP_EXP ;GET THE OLD EXPONENTCLR CSUBB A,R7 ;SUBTRACT FROM THE NUMBER OF ZEROSJZ FSUB10JC FSUB10;MOV FP_EXP,A ;SAVE THE NEW EXPONENT;ACALL LEFT1 ;SHIFT THE FP ACCMOV FP_CARRY,#0AJMP 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 REGISTERSMOV A,ARG_STACKADD A,#FP_NUMBER_SIZE+FP_NUMBER_SIZEMOV ARG_STACK,A ;POP THE STACK TWICE, CLEAR THE CARRYMOV A,R6 ;CHECK OUT EXPONENTSCLR F0SUBB A,R7JZ EXPONENTS_EQUALJC ARG1_EXP_IS_LARGER;; Now the ARG2 EXPONENT is > ARG1 EXPONENT;SIGNS_DIFFERENT:;MOV A,R3 ;SEE IF SIGN OF ARG2 IS POSITIVESJMP $+3;ARG1_EXP_IS_LARGER:;MOV A,R4 ;GET THE SIGN OF ARG1 EXPONENTJZ $+3CPL CRET;EXPONENTS_EQUAL:;; First, test the sign, then the mantissa;CJNE R5,#0,SIGNS_DIFFERENT;BOTH_PLUS:;MOV R7,#DIGIT ;POINT AT MS DIGITDEC R0DEC R0DEC R0DEC R1DEC R1DEC R1;; Now do the compare;CLOOP: MOVX A,@R0MOV R6,AMOVX A,@R1SUBB A,R6JNZ ARG1_EXP_IS_LARGERINC R0INC R1DJNZ R7,CLOOP;; If here, the numbers are the same, the carry is cleared;SETB F0RET ;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,R7JZ FMUL1-2ADD A,R6 ;ADD THE EXPONENTSJB ACC.7,FMUL_OVERJBC 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 BIASMOV R6,A ;SAVE IT FOR LATER;; Unpack and load R0;ACALL UNPACK_R0;; Now set up for loop multiply;MOV R3,#DIGITMOV R4,R1B0;newpage;; Now, do the multiply and accumulate the product;FMUL3: MOV R1B0,R4MOVX A,@R1MOV R2,AACALL MUL_NIBBLE;MOV A,R2SWAP AACALL MUL_NIBBLEDEC R4DJNZ R3,FMUL3;; Now, pack and restore the sign;MOV FP_EXP,R6MOV FP_SIGN,R5AJMP PACK ;FINISH IT OFF;newpage;DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD;FLOATING_DIV:;;DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD;ACALL MDES1;; Check the exponents;MOV FP_SIGN,R5 ;SAVE THE SIGNCJNE R7,#0,DIV0 ;CLEARS THE CARRYACALL OVERFLOW_AND_EXITCLR ASETB ACC_ZERO_DIVIDERET;DIV0: MOV A,R6 ;GET EXPONENTJZ FMUL1-2 ;EXIT IF ZEROSUBB A,R7 ;DELTA EXPONENTJB ACC.7,D_UNDERJNC DIV3AJMP UNDERFLOW_AND_EXIT;D_UNDER:JNC FOV;DIV3: ADD A,#129 ;CORRECTLY BIAS THE EXPONENTMOV FP_EXP,A ;SAVE THE EXPONENTACALL LOADR1_MANTISSA ;LOAD THE DIVIDED;MOV R2,#FP_ACCC ;SAVE LOCATIONMOV R3,R0B0 ;SAVE POINTER IN R3MOV FP_CARRY,#0 ;ZERO CARRY BYTE;DIV4: MOV R5,#0FFH ;LOOP COUNTSETB C;DIV5: MOV R0B0,R3 ;RESTORE THE EXTERNAL POINTERMOV R1,#FP_DIG78 ;SET UP INTERNAL POINTERMOV R7,#DIGIT ;LOOP COUNTJNC DIV7 ;EXIT IF NO CARRY;DIV6: MOVX A,@R0 ;DO ACCUMLATIONMOV R6,ACLR AADDC A,#99HSUBB A,R6ADD A,@R1DA AMOV @R1,ADEC R0DEC R1DJNZ R7,DIV6 ;LOOP;INC R5 ;SUBTRACT COUNTERJC DIV5 ;KEEP LOOPING IF CARRYMOV A,@R1 ;GET CARRYSUBB A,#1 ;CARRY IS CLEAREDMOV @R1,A ;SAVE CARRY DIGITCPL CSJMP DIV5 ;LOOP;; Restore the result if carry was found;DIV7: ACALL ADDLP ;ADD NUMBER BACKMOV @R1,#0 ;CLEAR CARRYMOV R0B0,R2 ;GET SAVE COUNTERMOV @R0,5 ;SAVE COUNT BYTE;INC R2 ;ADJUST SAVE COUNTERMOV R7,#1 ;BUMP DIVIDENDACALL LEFTCJNE R2,#FP_ACC8+2,DIV4;DJNZ FP_EXP,DIV8AJMP UNDERFLOW_AND_EXIT;DIV8: MOV FP_CARRY,#0;newpage;***************************************************************;PACK: ; Pack the mantissa;;***************************************************************;; First, set up the pointers;MOV R0,#FP_ACCCMOV A,@R0 ;GET FP_ACCCMOV R6,A ;SAVE FOR ZERO COUNTJZ PACK0 ;JUMP OVER IF ZEROACALL INC_FP_EXP ;BUMP THE EXPONENTDEC R0;PACK0: INC R0 ;POINT AT FP_ACC1;PACK1: MOV A,#8 ;ADJUST NIBBLE POINTERMOV R1,AADD A,R0MOV R0,ACJNE @R0,#5,$+3 ;SEE IF ADJUSTING NEEDEDJC PACK3+1;PACK2: SETB CCLR ADEC R0ADDC A,@R0DA AXCHD A,@R0 ;SAVE THE VALUEJNB ACC.4,PACK3DJNZ R1,PACK2;DEC R0MOV @R0,#1ACALL INC_FP_EXPSJMP PACK4;PACK3: DEC R1MOV A,R1CLR CXCH A,R0SUBB A,R0MOV R0,A;PACK4: MOV R1,#FP_DIG12;; Now, pack;PLOOP: MOV A,@R0SWAP A ;FLIP THE DIGITSINC R0XCHD A,@R0ORL 6,A ;ACCUMULATE THE OR'ED DIGITSMOV @R1,AINC R0INC R1CJNE R1,#FP_SIGN,PLOOPMOV A,R6JNZ STORE_ALIGN_TEST_AND_EXITMOV FP_EXP,#0 ;ZERO EXPONENT;;**************************************************************;STORE_ALIGN_TEST_AND_EXIT: ;Save the number align carry and exit;;**************************************************************;ACALL LOAD_POINTERSMOV ARG_STACK,R1 ;SET UP THE NEW STACKMOV R0,#FP_EXP;; Now load the numbers;STORE2: MOV A,@R0MOVX @R1,A ;SAVE THE NUMBERDEC R0DEC R1CJNE R0,#FP_CARRY,STORE2;CLR A ;NO ERRORS;PRET: RET ;EXIT;newpageINC_FP_EXP:;INC FP_EXPMOV A,FP_EXPJNZ PRET ;EXIT IF NOT ZEROPOP ACC ;WASTE THE CALLING STACKPOP ACCAJMP OVERFLOW_AND_EXIT;;***********************************************************************;UNPACK_R0: ; Unpack BCD digits and load into nibble locations;;***********************************************************************;PUSH R1B0MOV R1,#FP_NIB8;ULOOP: MOVX A,@R0ANL A,#0FHMOV @R1,A ;SAVE THE NIBBLEMOVX A,@R0SWAP AANL A,#0FHDEC R1MOV @R1,A ;SAVE THE NIBBLE AGAINDEC R0DEC R1CJNE 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_DIG78MOV A,#99H;OVE1: MOV @R0,ADEC R0CJNE R0,#FP_CARRY,OVE1;MOV FP_EXP,#0FFHACALL STORE_ALIGN_TEST_AND_EXIT;SETB ACC_OVERFLOW ; ******AARET;newpage;**************************************************************;UNDERFLOW_AND_EXIT: ;LOAD 0, SET UF BIT, AND EXIT;;**************************************************************;ACALL ZERO_AND_EXITCLR ASETB ACC_UNDERFLOW ; ******AARET;;**************************************************************;ZERO_AND_EXIT: ;LOAD 0, SET ZERO BIT, AND EXIT;;**************************************************************;ACALL FP_CLEARACALL STORE_ALIGN_TEST_AND_EXITSETB ACC_ZERO ; ******AARET ;EXIT;;**************************************************************;FP_CLEAR:;; Clear internal storage;;**************************************************************;CLR AMOV R0,#FP_ACC8+1;FPC1: MOV @R0,ADEC R0CJNE R0,#FP_TEMP,FPC1RET;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 CMOV A,R7 ;GET THE DIGITS TO SHIFTJZ RIGHT5-1 ;EXIT IF ZEROSUBB A,#2 ;TWO TO DO?JNC RIGHT5 ;SHIFT TWO NIBBLES;; Swap one nibble then exit;RIGHT3: PUSH R0B0 ;SAVE POINTER REGISTERPUSH R1B0;MOV R1,#FP_DIG78 ;LOAD THE POINTERSMOV R0,#FP_DIG56MOV A,R4 ;GET THE OVERFLOW REGISTERXCHD A,@R1 ;GET DIGIT 8SWAP A ;FLIP FOR LOADMOV R4,A;RIGHTL: MOV A,@R1 ;GET THE LOW ORDER BYTEXCHD A,@R0 ;SWAP NIBBLESSWAP A ;FLIP FOR STOREMOV @R1,A ;SAVE THE DIGITSDEC R0 ;BUMP THE POINTERSDEC R1CJNE R1,#FP_DIG12-1,RIGHTL ;LOOP;MOV A,@R1 ;ACC = CH8SWAP A ;ACC = 8CHANL A,#0FH ;ACC = 0CHMOV @R1,A ;CARRY DONEPOP R1B0 ;EXITPOP R0B0 ;RESTORE REGISTERRET;RIGHT5: MOV R7,A ;SAVE THE NEW SHIFT NUMBERCLR AXCH A,FP_CARRY ;SWAP THE NIBBLESXCH A,FP_DIG12XCH A,FP_DIG34XCH A,FP_DIG56XCH A,FP_DIG78MOV R4,A ;SAVE THE LAST DIGIT SHIFTEDSJMP RIGHT1+1;newpage;***************************************************************;LEFT: ; Shift ACCUMULATOR LEFT the number of nibbles in R7;;***************************************************************;MOV R4,#00H ;CLEAR FOR SOME ENTRYS;LEFT1: CLR CMOV A,R7 ;GET SHIFT VALUEJZ LEFT5-1 ;EXIT IF ZEROSUBB A,#2 ;SEE HOW MANY BYTES TO SHIFTJNC LEFT5;LEFT3: PUSH R0B0 ;SAVE POINTERPUSH R1B0MOV R0,#FP_CARRYMOV R1,#FP_DIG12;MOV A,@R0 ;ACC=CHCLSWAP A ;ACC = CLCHMOV @R0,A ;ACC = CLCH, @R0 = CLCH;LEFTL: MOV A,@R1 ;DIG 12SWAP A ;DIG 21XCHD A,@R0MOV @R1,A ;SAVE ITINC R0 ;BUMP POINTERSINC R1CJNE R0,#FP_DIG78,LEFTL;MOV A,R4SWAP AXCHD A,@R0ANL A,#0F0HMOV R4,A;POP R1B0POP R0B0 ;RESTORERET ;DONE;LEFT5: MOV R7,A ;RESTORE COUNTCLR AXCH A,R4 ;GET THE RESTORATION BYTEXCH A,FP_DIG78 ;DO THE SWAPXCH A,FP_DIG56XCH A,FP_DIG34XCH A,FP_DIG12XCH A,FP_CARRYSJMP LEFT1+1;newpageMUL_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 NIBBLEMOV R7,AMOV R0,#FP_ACC8MOV R1,#FP_NIB8CLR AMOV FP_ACCX,A;MNLOOP: DEC R0 ;BUMP POINTER TO PROPAGATE CARRYADD A,@R0 ;ATTEMPT TO FORCE CARRYDA A ;BCD ADJUSTJNB ACC.4,MNL0 ;DON'T ADJUST IF NO NEEDDEC R0 ;PROPAGATE CARRY TO THE NEXT DIGITINC @R0 ;DO THE ADJUSTINGINC R0 ;RESTORE R0;MNL0: XCHD A,@R0 ;RESTORE INITIAL NUMBERMOV B,R7 ;GET THE NUBBLE TO MULTIPLYMOV A,@R1 ;GET THE OTHER NIBBLEMUL AB ;DO THE MULTIPLYMOV B,#10 ;NOW BCD ADJUSTDIV ABXCH A,B ;GET THE REMAINDERADD A,@R0 ;PROPAGATE THE PARTIAL PRODUCTSDA A ;BCD ADJUSTJNB ACC.4,MNL1 ;PROPAGATE PARTIAL PRODUCT CARRYINC B;MNL1: INC R0XCHD A,@R0 ;SAVE THE NEW PRODUCTDEC R0MOV A,B ;GET BACK THE QUOTIENTDEC R1CJNE R1,#FP_NIB1-1,MNLOOP;ADD A,FP_ACCX ;GET THE OVERFLOWDA A ;ADJUSTMOV @R0,A ;SAVE ITRET ;EXIT;newpage;***************************************************************;LOAD_POINTERS: ; Load the ARG_STACK into R0 and bump R1;;***************************************************************;MOV P2,#ARG_STACK_PAGEMOV R0,ARG_STACKMOV A,#FP_NUMBER_SIZEADD A,R0MOV R1,ARET;;***************************************************************;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 REGISTERSMOVX A,@R0 ;ARG 1 EXPMOV R7,A ;SAVED IN R7MOVX A,@R1 ;ARG 2 EXPMOV R6,A ;SAVED IN R6DEC R0 ;BUMP POINTERS TO SIGNDEC R1MOVX A,@R0 ;GET THE SIGNMOV R4,A ;SIGN OF ARG1MOVX A,@R1 ;GET SIGN OF NEXT ARGMOV R3,A ;SIGN OF ARG2XRL A,R4 ;ACC GETS THE NEW SIGNMOV R5,A ;R5 GETS THE NEW SIGN;; Bump the pointers to point at the LS digit;DEC R0DEC R1;RET;newpage;***************************************************************;LOADR1_MANTISSA:;; Load the mantissa of R0 into FP_Digits;;***************************************************************;PUSH R0B0 ;SAVE REGISTER 1MOV R0,#FP_DIG78 ;SET UP THE POINTER;LOADR1: MOVX A,@R1MOV @R0,ADEC R1DEC R0CJNE R0,#FP_CARRY,LOADR1;POP R0B0RET;newpage;***************************************************************;HEXSCAN: ; Scan a string to determine if it is a hex number; set carry if hex, else carry = 0;;***************************************************************;ACALL GET_DPTR_CHARACTERPUSH DPHPUSH DPL ;SAVE THE POINTER;HEXSC1: MOVX A,@DPTR ;GET THE CHARACTERACALL DIGIT_CHECK ;SEE IF A DIGITJC HS1 ;CONTINUE IF A DIGITACALL HEX_CHECK ;SEE IF HEXJC HS1;CLR ACC.5 ;NO LOWER CASECJNE A,#'H',HEXDONSETB CSJMP HEXDO1 ;NUMBER IS VALID HEX, MAYBE;HEXDON: CLR C;HEXDO1: POP DPL ;RESTORE POINTERPOP DPHRET;HS1: INC DPTR ;BUMP TO NEXT CHARACTERSJMP HEXSC1 ;LOOP;HEX_CHECK: ;CHECK FOR A VALID ASCII HEX, SET CARRY IF FOUND;CLR ACC.5 ;WASTE LOWER CASECJNE A,#'F'+1,$+3 ;SEE IF F OR LESSJC HC1RET;HC1: CJNE A,#'A',$+3 ;SEE IF A OR GREATERCPL CRET;newpage;PUSHR2R0:;MOV R3,#HI(CONVT) ;CONVERSION LOCATIONMOV R1,#LO(CONVT)ACALL CONVERT_BINARY_TO_ASCII_STRINGMOV A,#0DH ;A CR TO TERMINATEMOVX @R1,A ;SAVE THE CRMOV 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 EVERYTHINGACALL GET_DPTR_CHARACTERACALL PLUS_MINUS_TESTMOV MSIGN,C ;SAVE THE MANTISSA SIGN;; Now, set up for input loop;MOV R0,#FP_ACCCMOV R6,#7FH ;BASE EXPONENTSETB F0 ;SET INITIAL FLAG;INLOOP: ACALL GET_DIGIT_CHECKJNC GTEST ;IF NOT A CHARACTER, WHAT IS IT?ANL A,#0FH ;STRIP ASCIIACALL STDIG ;STORE THE DIGITS;INLPIK: INC DPTR ;BUMP POINTER FOR LOOPSJMP INLOOP ;LOOP FOR INPUT;GTEST: CJNE A,#'.',GT1 ;SEE IF A RADIXJB FOUND_RADIX,INERRSETB FOUND_RADIXCJNE R0,#FP_ACCC,INLPIKSETB FIRST_RADIX ;SET IF FIRST RADIXSJMP INLPIK ;GET ADDITIONAL DIGITS;GT1: JB F0,INERR ;ERROR IF NOT CLEAREDCJNE A,#'e',$+5 ;CHECK FOR LOWER CASESJMP $+5CJNE A,#'E',FINISH_UPACALL INC_AND_GET_DPTR_CHARACTERACALL PLUS_MINUS_TESTMOV XSIGN,C ;SAVE SIGN STATUSACALL GET_DIGIT_CHECKJNC INERR;ANL A,#0FH ;STRIP ASCII BIAS OFF THE CHARACTERMOV R5,A ;SAVE THE CHARACTER IN R5;GT2: INC DPTRACALL GET_DIGIT_CHECKJNC FINISH1ANL A,#0FH ;STRIP OFF BIASXCH A,R5 ;GET THE LAST DIGITMOV B,#10 ;MULTIPLY BY TENMUL ABADD A,R5 ;ADD TO ORIGINAL VALUEMOV R5,A ;SAVE IN R5JNC GT2 ;LOOP IF NO CARRYMOV R5,#0FFH ;FORCE AN ERROR;FINISH1:MOV A,R5 ;GET THE SIGNJNB XSIGN,POSNUM ;SEE IF EXPONENT IS POS OR NEGCLR CSUBB A,R6CPL AINC AJC FINISH2MOV A,#01HRET;POSNUM: ADD A,R6 ;ADD TO EXPONENTJNC FINISH2;POSNM1: MOV A,#02HRET;FINISH2:XCH A,R6 ;SAVE THE EXPONENT;FINISH_UP:;MOV FP_EXP,R6 ;SAVE EXPONENTCJNE R0,#FP_ACCC,$+5ACALL FP_CLEAR ;CLEAR THE MEMORY IF 0MOV A,ARG_STACK ;GET THE ARG STACKCLR CSUBB A,#FP_NUMBER_SIZE+FP_NUMBER_SIZEMOV ARG_STACK,A ;ADJUST FOR STOREAJMP PACK;STDIG: CLR F0 ;CLEAR INITIAL DESIGNATORJNZ STDIG1 ;CONTINUE IF NOT ZEROCJNE R0,#FP_ACCC,STDIG1JNB FIRST_RADIX,RET_X;DECX: DJNZ R6,RET_X;INERR: MOV A,#0FFH;RET_X: RET;STDIG1: JB DONE_LOAD,FRTESTCLR FIRST_RADIX;FRTEST: JB FIRST_RADIX,DECX;FDTEST: JB FOUND_RADIX,FDT1INC R6;FDT1: JB DONE_LOAD,RET_XCJNE R0,#FP_ACC8+1,FDT2SETB DONE_LOAD;FDT2: MOV @R0,A ;SAVE THE STRIPPED ACCUMULATORINC R0 ;BUMP THE POINTERRET ;EXIT;newpage;***************************************************************;; I/O utilities;;***************************************************************;INC_AND_GET_DPTR_CHARACTER:;INC DPTR;GET_DPTR_CHARACTER:;MOVX A,@DPTR ;GET THE CHARACTERCJNE 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 BASICSJMP PMT3CJNE A,#'+',$+5SJMP PMT3CJNE A,#0E5H,$+5 ;SEE IF MINUS, MINUS TOKEN FROM BASICSJMP PMT2CJNE 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 POINTERACALL POP_AND_EXIT ;OUTPUT POPS THE STACKMOV A,R7MOV R6,A ;PUT THE EXPONENT IN R6ACALL UNPACK_R0 ;UNPACK THE NUMBERMOV R0,#FP_NIB1 ;POINT AT THE NUMBERMOV A,FORMAT ;GET THE FORMATMOV R3,A ;SAVE IN CASE OF EXP FORMATJZ FREE ;FREE FLOATING?CJNE A,#0F0H,$+3 ;SEE IF EXPONENTIALJNC EXPOUT;; If here, must be integer USING format;MOV A,R6 ;GET THE EXPONENTJNZ $+4MOV R6,#80HMOV A,R3 ;GET THE FORMATSWAP A ;SPLIT INTEGER AND FRACTIONANL A,#0FHMOV R2,A ;SAVE INTEGERACALL NUM_LT ;GET THE NUMBER OF INTEGERSXCH A,R2 ;FLIP FOR SUBBCLR CSUBB A,R2MOV R7,AJNC $+8MOV R5,#'?' ;OUTPUT A QUESTION MARKACALL SOUT1 ;NUMBER IS TOO LARGE FOR FORMATAJMP FREECJNE R2,#00,USING0 ;SEE IF ZERODEC R7ACALL SS7ACALL ZOUT ;OUTPUT A ZEROSJMP USING1;USING0: ACALL SS7 ;OUTPUT SPACES, IF NEED TOMOV A,R2 ;OUTPUT DIGITSMOV R7,AACALL OUTR0;USING1: MOV A,R3ANL A,#0FH ;GET THE NUMBER RIGHT OF DPMOV R2,A ;SAVE ITJZ PMT1 ;EXIT IF ZEROACALL ROUT ;OUTPUT DPACALL NUM_RTCJNE A,2,USINGX ;COMPARE A TO R2;USINGY: MOV A,R2AJMP Z7R7;USINGX: JNC USINGY;USING2: XCH A,R2CLR CSUBB A,R2XCH A,R2ACALL Z7R7 ;OUTPUT ZEROS IF NEED TOMOV A,R2MOV R7,AAJMP OUTR0;; First, force exponential output, if need to;FREE: MOV A,R6 ;GET THE EXPONENTJNZ FREE1 ;IF ZERO, PRINT ITACALL SOUTAJMP ZOUT;FREE1: MOV R3,#0F0H ;IN CASE EXP NEEDEDMOV A,#80H-DIGIT-DIGIT-1ADD A,R6JC EXPOUTSUBB A,#0F7HJC EXPOUT;; Now, just print the number;ACALL SINOUT ;PRINT THE SIGN OF THE NUMBERACALL NUM_LT ;GET THE NUMBER LEFT OF DPCJNE A,#8,FREE4AJMP OUTR0;FREE4: ACALL OUTR0ACALL ZTEST ;TEST FOR TRAILING ZEROSJZ U_RET ;DONE IF ALL TRAILING ZEROSACALL ROUT ;OUTPUT RADIX;FREE2: MOV R7,#1 ;OUTPUT ONE DIGITACALL OUTR0JNZ U_RETACALL ZTESTJZ U_RETSJMP FREE2 ;LOOP;EXPOUT: ACALL SINOUT ;PRINT THE SIGNMOV R7,#1 ;OUTPUT ONE CHARACTERACALL OUTR0ACALL ROUT ;OUTPUT RADIXMOV A,R3 ;GET FORMATANL A,#0FH ;STRIP INDICATORJZ EXPOTX;MOV R7,A ;OUTPUT THE NUMBER OF DIGITSDEC R7 ;ADJUST BECAUSE ONE CHAR ALREADY OUTACALL OUTR0SJMP EXPOT4;EXPOTX: ACALL FREE2 ;OUTPUT UNTIL TRAILING ZEROS;EXPOT4: ACALL SOUT ;OUTPUT A SPACEMOV R5,#'E'ACALL SOUT1 ;OUTPUT AN EMOV A,R6 ;GET THE EXPONENTJZ XOUT0 ;EXIT IF ZERODEC A ;ADJUST FOR THE DIGIT ALREADY OUTPUTCJNE A,#80H,XOUT2 ;SEE WHAT IT IS;XOUT0: ACALL SOUTCLR ASJMP XOUT4;XOUT2: JC XOUT3 ;NEGATIVE EXPONENTMOV R5,#'+' ;OUTPUT A PLUS SIGNACALL SOUT1SJMP XOUT4;XOUT3: ACALL MOUTCPL A ;FLIP BITSINC A ;BUMP;XOUT4: CLR ACC.7MOV R0,AMOV R2,#0MOV R1,#LO(CONVT) ;CONVERSION LOCATIONMOV R3,#HI(CONVT)ACALL CONVERT_BINARY_TO_ASCII_STRINGMOV R0,#LO(CONVT) ;NOW, OUTPUT EXPONENT;EXPOT5: MOVX A,@R0 ;GET THE CHARACTERMOV R5,A ;OUTPUT ITACALL SOUT1INC R0 ;BUMP THE POINTERMOV A,R0 ;GET THE POINTERCJNE A,R1B0,EXPOT5 ;LOOP;U_RET: RET ;EXIT;OUTR0: ; Output the characters pointed to by R0, also bias ascii;MOV A,R7 ;GET THE COUNTERJZ OUTR ;EXIT IF DONEMOV A,@R0 ;GET THE NUMBERORL A,#30H ;ASCII BIASINC R0 ;BUMP POINTER AND COUNTERDEC R7MOV R5,A ;PUT CHARACTER IN OUTPUT REGISTERACALL SOUT1 ;OUTPUT THE CHARACTERCLR A ;JUST FOR TESTCJNE R0,#FP_NIB8+1,OUTR0MOV A,#55H ;KNOW WHERE EXIT OCCURED;OUTR: RET;ZTEST: MOV R1,R0B0 ;GET POINTER REGISTER;ZT0: MOV A,@R1 ;GET THE VALUEJNZ ZT1INC R1 ;BUMP POINTERCJNE R1,#FP_NIB8+1,ZT0;ZT1: RET;NUM_LT: MOV A,R6 ;GET EXPONENTCLR C ;GET READY FOR SUBBSUBB A,#80H ;SUB EXPONENT BIASJNC NL1 ;OK IF NO CARRYCLR A ;NO DIGITS LEFT;NL1: MOV R7,A ;SAVE THE COUNTRET;NUM_RT: CLR C ;SUBB AGAINMOV A,#80H ;EXPONENT BIASSUBB A,R6 ;GET THE BIASED EXPONENTJNC NR1CLR A;NR1: RET ;EXIT;SPACE7: MOV A,R7 ;GET THE NUMBER OF SPACESJZ NR1 ;EXIT IF ZEROACALL SOUT ;OUTPUT A SPACEDEC R7 ;BUMP COUNTERSJMP SPACE7 ;LOOP;Z7R7: MOV R7,A;ZERO7: MOV A,R7 ;GET COUNTERJZ NR1 ;EXIT IF ZEROACALL ZOUT ;OUTPUT A ZERODEC R7 ;BUMP COUNTERSJMP ZERO7 ;LOOP;SS7: ACALL SPACE7;SINOUT: MOV A,R4 ;GET THE SIGNJZ SOUT ;OUTPUT A SPACE IF ZERO;MOUT: MOV R5,#'-'SJMP SOUT1 ;OUTPUT A MINUS IF NOT;ROUT: MOV R5,#'.' ;OUTPUT A RADIXSJMP SOUT1;ZOUT: MOV R5,#'0' ;OUTPUT A ZEROSJMP 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 NUMBERMOV ADD_IN,C ;IF ADD_IN IS SET, THE NUMBER IS HEXACALL GET_DIGIT_CHECKCPL C ;FLIP FOR EXITJC RCASBMOV R3,#00H ;ZERO R3:R1 FOR LOOPMOV R1,#00HSJMP CASB5;CASB2: INC DPTRMOV R0B0,R1 ;SAVE THE PRESENT CONVERTED VALUEMOV R0B0+2,R3 ;IN R2:R0ACALL GET_DIGIT_CHECKJC CASB5JNB ADD_IN,RCASB ;CONVERSION COMPLETEACALL HEX_CHECK ;SEE IF HEX NUMBERJC CASB4 ;PROCEED IF GOODINC DPTR ;BUMP PAST HSJMP RCASB;CASB4: ADD A,#9 ;ADJUST HEX ASCII BIAS;CASB5: MOV B,#10JNB ADD_IN,CASB6MOV B,#16 ;HEX MODE;CASB6: ACALL MULNUM ;ACCUMULATE THE DIGITSJNC CASB2 ;LOOP IF NO CARRY;RCASB: CLR A ;RESET ACCMOV ACC_OVERFLOW,C ;IF OVERFLOW, SAY SO ******AARET ;EXIT;newpage;MULNUM10:MOV B,#10;;***************************************************************;MULNUM: ; Take the next digit in the acc (masked to 0FH); accumulate in R3:R1;;***************************************************************;PUSH ACC ;SAVE ACCPUSH B ;SAVE MULTIPLIERMOV A,R1 ;PUT LOW ORDER BITS IN ACCMUL AB ;DO THE MULTIPLYMOV R1,A ;PUT THE RESULT BACKMOV A,R3 ;GET THE HIGH ORDER BYTEMOV R3,B ;SAVE THE OVERFLOWPOP B ;GET THE MULTIPLIERMUL AB ;DO ITMOV C,OV ;SAVE OVERFLOW IN F0MOV F0,CADD A,R3 ;ADD OVERFLOW TO HIGH RESULTMOV R3,A ;PUT IT BACKPOP ACC ;GET THE ORIGINAL ACC BACKORL C,F0 ;OR CARRY AND OVERFLOWJC MULX ;NO GOOD IF THE CARRY IS SET;MUL11: ANL A,#0FH ;MASK OFF HIGH ORDER BITSADD A,R1 ;NOW ADD THE ACCMOV R1,A ;PUT IT BACKCLR A ;PROPAGATE THE CARRYADDC A,R3MOV 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 ZEROSMOV DPTR,#10000 ;SUBTRACT 10000ACALL RSUB ;DO THE SUBTRACTIONMOV DPTR,#1000 ;NOW 1000ACALL RSUBMOV DPTR,#100 ;NOW 100ACALL RSUBMOV DPTR,#10 ;NOW 10ACALL RSUBMOV DPTR,#1 ;NOW 1ACALL RSUBJZ RSUB2 ;JUMP OVER RET;RSUB_R: RET;RSUB: MOV R6,#-1 ;SET UP THE COUNTER;RSUB1: INC R6 ;BUMP THE COUNTERXCH A,R2 ;DO A FAST COMPARECJNE A,DPH,$+3XCH A,R2JC FAST_DONEXCH A,R0 ;GET LOW BYTESUBB A,DPL ;SUBTRACT, CARRY IS CLEAREDXCH A,R0 ;PUT IT BACKXCH A,R2 ;GET THE HIGH BYTESUBB A,DPH ;ADD THE HIGH BYTEXCH A,R2 ;PUT IT BACKJNC RSUB1 ;LOOP UNTIL CARRY;XCH A,R0ADD A,DPL ;RESTORE R2:R0XCH A,R0XCH A,R2ADDC A,DPHXCH A,R2;FAST_DONE:;ORL A,R6 ;OR THE COUNT VALUEJZ RSUB_R ;RETURN IF ZERO;RSUB2: MOV A,#'0' ;GET THE ASCII BIASADD A,R6 ;ADD THE COUNT;RSUB4: MOV P2,R3 ;SET UP P2MOVX @R1,A ;PLACE THE VALUE IN MEMORYINC R1CJNE R1,#00H,RSUB3 ;SEE IF RAPPED AROUNDINC 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 SPACEMOV C,ZSURP ;GET ZERO SUPPRESSION BITMOV ADD_IN,CMOV A,R3 ;GET HIGH NIBBLE AND PRINT ITACALL HOUTHIMOV A,R3ACALL HOUTLO;HEX2X: CLR ADD_IN ;DON'T SUPPRESS ZEROSMOV A,R1 ;GET LOW NIBBLE AND PRINT ITACALL HOUTHIMOV A,R1ACALL HOUTLOMOV R5,#'H' ;OUTPUT H TO INDICATE HEX MODE;SOUT_1: AJMP SOUT1;HOUT1: CLR ADD_IN ;PRINTED SOMETHING, SO CLEAR ADD_INADD A,#90H ;CONVERT TO ASCIIDA AADDC A,#40HDA A ;GOT IT HEREMOV R5,A ;OUTPUT THE BYTESJMP SOUT_1;HOUTHI: SWAP A ;SWAP TO OUTPUT HIGH NIBBLE;HOUTLO: ANL A,#0FH ;STRIPJNZ HOUT1 ;PRINT IF NOT ZEROJNB ADD_IN,HOUT1 ;OUTPUT A ZERO IF NOT SUPRESSEDRET;newpageORG 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 LESSJC DC1RET;DC1: CJNE A,#'0',$+3 ;SEE IF ASCII 0 OR GREATERCPL CRETendsection