📄 fp52.asm
字号:
; 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.
;
;**********************************************************************
;
$EJECT
;**********************************************************************
;
; 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
;
;***********************************************************************
;
$EJECT
;***********************************************************************
;
; The following values MUST be provided by the user
;
;***********************************************************************
;
ARG_STACK_PAGE EQU 01h ;External memory page for arg stack
ARG_STACK EQU 24H ;ARGUMENT STACK POINTER
FORMAT EQU 25H ;LOCATION OF OUTPUT FORMAT BYTE
;OUTPUT EQU R5OUT ;CALL LOCATION TO OUTPUT A CHARACTER in R5
CONVT EQU 0048H ;String addr TO CONVERT NUMBERS
INTGRC BIT 26H.1 ;BIT SET IF INTEGER ERROR
ADD_IN BIT 26H.3 ;DCMPXZ IN BASIC BACKAGE
ZSURP BIT 26H.6 ;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
OVERFLOW EQU 1
ZERO EQU 2
ZERO_DIVIDE EQU 3
;
;***********************************************************************
$EJECT
;**************************************************************
;
; 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 data pointer me
FP_TEMP EQU FP_STATUS+1 ;NOT USED
FP_CARRY EQU FP_STATUS+2 ;USED FOR BITS
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 EQU FP_CARRY+5
FP_EXP EQU FP_CARRY+6
MSIGN BIT FP_SIGN.0
XSIGN BIT FP_CARRY.0
FOUND_RADIX BIT FP_CARRY.1
FIRST_RADIX BIT FP_CARRY.2
DONE_LOAD BIT FP_CARRY.3
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
;
$EJECT
FP_BASE EQU $
;**************************************************************
;
; 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
;
; the remaining jump to routines were extracted from basic52
; by me to make the floating point software stand alone
;
AJMP PUSHR2R0 ; INTEGER to FLOAT
AJMP IFIX ; FLOAT to INTEGER
AJMP PUSHAS ; PUSH R2:R0 TO ARGUMENT
AJMP POPAS ; POP ARGUMENT TO R3:R1
AJMP MOVAS ; COPY ARGUMENT
AJMP AINT ; INT FUNCTION
AJMP PUSHC ; PUSH ARG IN DPTR TO STACK
;
$EJECT
;
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
;
$EJECT
; 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
;
$EJECT
;
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
;
$EJECT
;
; 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
;
$EJECT
;***************************************************************
;
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
CLR C
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
;
$EJECT
;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
;
$EJECT
;
; 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
;
$EJECT
;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
;
$EJECT
;***************************************************************
;
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
;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -