$DEBUG

LONG_DIVISION		SEGMENT	CODE

EXTRN DATA(NUMERATOR, DENOMINATOR, QUOTIENT)
EXTRN NUMBER(NUMERATOR_BYTES, QUOTIENT_BYTES)
PUBLIC LONG_DIVIDE, TIMES_TWO_AND_COMPARE

RSEG LONG_DIVISION

	BYTE_COUNT			EQU	R2
	BIT_COUNT			EQU	R3
	HIGHEST_NUMERATOR_BYTE		EQU	R4
	HIGHEST_DENOMINATOR_BYTE	EQU	R5


;******************************************************************************
;
;                              LONG_DIVIDE
;
;                                            NUMERATOR
;                   CALCULATES  QUOTIENT =  -----------
;                                           DENOMINATOR
;
;       NUMERATOR and DENOMINATOR are N-byte unsigned integers.
;
;       Requirements: 1. The most significant bit of DENOMINATOR must = 0
;                     2. NUMERATOR must be < DENOMINATOR
;
;       QUOTIENT is m bits long and of the form:
;
;                        QUOTIENT = 0. q1 q2 q3 ... qm
;
;       where qn is the coefficient of 2**(-n).
;
;
;       INPUTS: NUMERATOR    N bytes in externally defined DATA
;                    (LSByte at NUMERATOR, next byte in NUMERATOR+1, etc.)
;
;               DENOMINATOR  N bytes in externally defined DATA
;
;               NUMERATOR_BYTES externally defined numerical constant
;                          = N, number of bytes in NUMERATOR and DENOMINATOR
;
;               QUOTIENT_BYTES  externally defined numerical constant
;                          = M, number of bytes in QUOTIENT
;
;
;       OUTPUTS: QUOTIENT    M bytes in externally defined DATA
;
;
;       ENTRY POINTS:  LONG_DIVIDE (primary)
;                      TIMES_TWO_AND_COMPARE (special purpose)
;
;       VARIABLES AND REGISTERS MODIFIED:
;
;               NUMERATOR (all N bytes of it)
;               QUOTIENT  (all M bytes of it)
;               ACC, B, PSW, R0, R1, R2, R3, R4, R5
;
;       ERROR EXIT: Exit with OV = 1 indicates either/or
;
;            1. Most significant bit of denominator not 0
;            2. NUMERATOR >= DENOMINATOR
;            3. Too many bits in quotient (max is 255)
;
;
; An example of how to use this routine:
;
; Suppose you want to divide a 5-byte integer
;
;              N4 N3 N2 N1 N0
;
; by a 3-byte integer
;
;                 D2 D1 D0.
;
;
; If D2 > 0, the quotient will have at most 3 integer bytes, but can have any
; number of bytes to the right of the decimal point.  If we calculate the
; quotient to 3 bytes, then we'll have the integer part of it.  If we calculate
; the quotient to 10 bytes, then we'll have the integer part and 7 bytes to the
; right of the decimal point.  For this example, we'll calculate the quotient
; to 4 bytes, so the quotient will be of the form
;
;
;          Q3 Q2 Q1 . Q0
;
; To satisfy the requirements that
;
;       1. Most significant bit of denominator = 0
;       2. NUMERATOR < DENOMINATOR
;
; set the problem up this way:
;
;           N4 N3 N2 N1 N0     00 00 N4 N3 N2 N1 N0 
;           --------------  =  -------------------- x 1000000H
;              D2 D1 D0        00 D2 D1 D0 00 00 00
; 
; Note that requirement #2 requires also that D2 > 0.
;
; The LONG_DIVIDE routine will calculate
;
;         00 00 N4 N3 N2 N1 N0
;         --------------------  =  0. Q3 Q2 Q1 Q0
;         00 D2 D1 D0 00 00 00
;
; The calling program must then multiply this result by 1000000H, which in
; fact involves only redefining where the decimal point is:
;
;         0. Q3 Q2 Q1 Q0  x  1000000H  =  Q3 Q2 Q1 . Q0
;
; The calling program must define an DATA segment having
;
;               NUMERATOR       DS      7
;               DENOMINATOR     DS      7
;               QUOTIENT        DS      4
;
; The calling program must also define the numerical constants
;
;               NUMERATOR_BYTES EQU     7
;               QUOTIENT_BYTES  EQU     4
;
; These parameters must be declared PUBLIC:
;
;   PUBLIC NUMERATOR, DENOMINATOR, QUOTIENT, NUMERATOR_BYTES, QUOTIENT_BYTES
;
; Software in the calling program loads NUMERATOR with 00 00 N4 N3 N2 N1 N0,
; and DENOMINATOR with 00 D2 D1 D0 00 00 00.  It is verified that D2 > 0.
; Then CALL LONG_DIVIDE.  The divide routine leaves QUOTIENT holding the
; result.  It leaves the content of DENOMINATOR unchanged, but changes the
; content of NUMERATOR.  Therefore if you want to use the original value of
; NUMERATOR in subsequent calculations, you'll need to save it elsewhere.
;
; QUOTIENT can be rounded off to the nearest LSBit by calculating what the
; (m+1)th bit would be and incrementing QUOTIENT if q(m+1) = 1.
;
; The code for that would be:
;
;               CALL LONG_DIVIDE                ;CALCULATES QUOTIENT
;               CALL TIMES_TWO_AND_COMPARE      ;CALCULATES q(m+1)
;               JNC     OVER
;               MOV     R0,#QUOTIENT-1
;               MOV     R1,#QUOTIENT_BYTES
;               SETB    C
;     INCREMENT_LOOP:
;               INC     R0
;               MOV     A,@R0
;               ADDC    A,#0
;               MOV     @R0,A
;               DJNZ    R1,INCREMENT_LOOP
;     OVER:     (continue)
;
;
;
;       The algorithm used by the subroutine is:
;
;               p(0) = NUMERATOR
;               2 x p(n-1) = qn x DEMOMINATOR + p(n), for n = 1,2,...m.
;
;       The procedure is first to calculate 2 x p(n-1), then compare it with
;       DENOMINATOR.
;
;               If 2 x p(n-1) >= DENOMINATOR, then qn = 1
;               If 2 x p(n-1) <  DENOMINATOR, then qn = 0
;
;       The routine calculates the m bits in QUOTIENT.  The remainder is
;       2**(-m) x p(m).  The routine leaves p(m) in the N bytes that
;       NUMERATOR was in.
;
;******************************************************************************



LONG_DIVIDE:
	MOV	A,#NUMERATOR
	ADD	A,#NUMERATOR_BYTES
	DEC	A
	MOV	HIGHEST_NUMERATOR_BYTE,A
	MOV	A,#DENOMINATOR
	ADD	A,#NUMERATOR_BYTES
	DEC	A
	MOV	HIGHEST_DENOMINATOR_BYTE,A
	MOV	A,#QUOTIENT_BYTES
	MOV	B,#8
	MUL	AB
	JNB	OV,$+4
	RET
	MOV	BIT_COUNT,A		;ESTABLISH NUMBER OF BITS IN QUOTIENT

ALGORITHM:
	CALL 	TIMES_TWO_AND_COMPARE

;  NUMERATOR = 2 X NUMERATOR
;  Then, if NUMERATOR < DENOMINATOR then qn = 0
;        if NUMERATOR >= DENOMINATOR then qn = 1

;  TIMES_TWO_AND_COMPARE LEAVES qn IN CY

	MOV	F0,C			;TEMP SAVE qn IN F0

;  SHIFT qn INTO QUOTIENT:

	MOV	BYTE_COUNT,#QUOTIENT_BYTES
	MOV	R0,#QUOTIENT
Q_SHIFT:
	MOV	A,@R0
	RLC	A
	MOV	@R0,A
	INC	R0
	DJNZ	BYTE_COUNT,Q_SHIFT

;  CALCULATE p(n) = NUMERATOR IF qn = 0
;                 = NUMERATOR - DENOMINATOR IF qn = 1

	JNB	F0,BIT_COUNT_TEST
	MOV	R0,#NUMERATOR
	MOV	R1,#DENOMINATOR
	MOV	BYTE_COUNT,#NUMERATOR_BYTES
	CLR	C
SUBTRACT:
	MOV	A,@R0
	SUBB	A,@R1
	MOV	@R0,A
	INC	R0
	INC	R1
	DJNZ	BYTE_COUNT,SUBTRACT
BIT_COUNT_TEST:		
	DJNZ	BIT_COUNT,ALGORITHM
	CLR	OV
	RET


TIMES_TWO_AND_COMPARE:
	MOV	BYTE_COUNT,#NUMERATOR_BYTES
	MOV	R0,#NUMERATOR
	CLR	C

LEFT_SHIFT:
	MOV	A,@R0
	RLC	A
	MOV	@R0,A
	INC	R0
	DJNZ	BYTE_COUNT,LEFT_SHIFT
	JC	ERROR		;CY = 1 AFTER TIMES_TWO INDICATES OVERFLOW

	MOV	A,HIGHEST_NUMERATOR_BYTE
	MOV	R0,A
	MOV	A,HIGHEST_DENOMINATOR_BYTE
	MOV	R1,A
	MOV	BYTE_COUNT,#NUMERATOR_BYTES

COMPARISON:
	MOV	A,@R0
	MOV	B,@R1
	CJNE	A,B,DONE	;SETS CY IF NUMERATOR < DENOMINATOR
				;CLEARS CY IF NUMERATOR >= DENOMINATOR
	DEC	R0
	DEC	R1
	DJNZ	BYTE_COUNT,COMPARISON

DONE:	CPL	C		;CLEARS CY IF NUMERATOR < DENOMINATOR
				;SETS CY IF NUMERATOR >= DENOMINATOR
	RET			;THUS CY = qn

ERROR:	SETB	OV
	POP	ACC
	POP	ACC
	RET
END
                                                                                                          