;
; MATHLIB: a 16 Bit Arithmetic Package for 8048
; Sourced Nov-Dec '80 by Trevor Marshall
;			SYSOP, Thousand Oaks Tech RBBS
;
; This package may be freely distributed but not for profit
; (C) 1980 Trevor Marshall
;-----------------------------------------------
;
; This S/R shifts the register pair whose LSB
;   is being pointed at by R0 right one position
; All registers are preserved
ROTATERIGHTR0: CLR C
SHIFTRIGHTR0: INC R0	;R0 points to the LSB
	MOV	A,@R0	;get MSB
	RRC	A
	MOV	@R0,A	;Result MSB
	DEC	R0	;point at LSB
	MOV	A,@R0	;get it
	RRC	A
	MOV	@R0,A	;Store result LSB
	RET
;
;----------------------------------------------
;
; This S/R shifts the register pair whose LSB
;   is being pointed at by R1 right one position
; All registers are preserved
ROTATERIGHTR1: CLR C
SHIFTRIGHTR1: INC R1	;R1 points to the LSB
	MOV	A,@R1	;get MSB
	RRC	A
	MOV	@R1,A	;Result MSB
	DEC	R1	;point at LSB
	MOV	A,@R1	;get it
	RRC	A
	MOV	@R1,A	;Store result LSB
	RET
;
;-----------------------------------------------
;
; This S/R shifts the register pair whose LSB is
;  being indicated by R0 left one position
; All registers are preserved
ROTATELEFTR0: CLR C
SHIFTLEFTR0: MOV A,@R0 ;get LSB
	RLC	A
	MOV	@R0,A	;Result LSB
	INC	R0	;Point at MSB
	MOV	A,@R0
	RLC	A
	MOV	@R0,A	;store MSB
	DEC	R0	;restore R0
	RET
;
;------------------------------------------------
;
; This S/R shifts the register pair whose LSB is
;  being indicated by R1 left one position
; All registers are preserved
ROTATELEFTR1: CLR C
SHIFTLEFTR1: MOV A,@R1 ;get LSB
	RLC	A
	MOV	@R1,A	;Result LSB
	INC	R1	;Point at MSB
	MOV	A,@R1
	RLC	A
	MOV	@R1,A	;store MSB
	DEC	R1	;restore R1
	RET
;
;---------------------------------------------------
;
; This S/R, ADD, adds a 16 Bit number pointed at by R1
;  to a sum indicated by R0. The sum (@R0) is updated
;  @R1 is not changed
; All registers preserved
ADD:	CLR	C	;Add without carry, or with it:
ADDC:	MOV	A,@R0	;LSB of sum
	ADDC	A,@R1	; + LSB of number
	MOV	@R0,A	;New sum LSB to @R0
	INC	R0
	INC	R1	;point at MSBs
	MOV	A,@R0	;MSB of sum
	ADDC	A,@R1	; + MSB of number
	MOV	@R0,A	;New sum MSB to @R0
	DEC	R0	;Restore R0, R1
	DEC	R1
	RET
;
;----------------------------------------------------
;
; A S/R to complement a 16 Bit number indicated by R0
; All registers preserved
COMPLEMENTR0: INC R0
	MOV	A,@R0	;get MSB
	CPL	A
	MOV	@R0,A	;store its compl
	DEC	R0
	MOV	A,@R0	;get LSB
	CPL	A	;Have 1s complemented it, add 1
	CLR	C
	ADD	A,#1
	MOV	@R0,A	;Store LSB
	INC	R0
	MOV	A,@R0	;get MSB
	ADDC	A,#0	;use carry
	MOV	@R0,A	;store MSB
	DEC	R0	;restore R0
	RET
;
;------------------------------------------------------
;
;MULTIPLY:  16 x 16 Bit multiplication
;
;result (@R0) = multiplicand (@R1) x multiplier (@R0)
;
; It overflows safely, but without indication
; Registers R4,R5,R6,R7 are destroyed
;
;	Example: 5 x 3
;		101
;	     x  011
;	----------------
;		101	(Shifted LSBit=1)
;	       101	(Shifted LSBit=1)
;	      000	(Shifted LSBit=0,no add)
;	----------------
;	      01111	(Result)
;
MULTIPLY: MOV	R7,#16	;Loop count in R7
	CLR	A	;Clear result 
	MOV	R6,A	;R6 is result MSB
	MOV	R5,A	;R5 is LSB
; shift and test if multiplier LSBit = 1 
ZZMULT:	CALL	ROTATERIGHTR0 ;0-> MSBit,LSBit-> C
	JNC	ZZNOADD ;LSBit not 1, Don't add
;
	MOV	A,R0	;Save R0 in
	MOV	R4,A	; R4
	MOV	R0,#5	;Point at the result R5,R6
	CALL	ADD	;Add result, multiplicand @R1
	MOV	A,R4	;Restore
	MOV	R0,A	;R0
; (Could test for overflow here:)
;
;Now we shift left the multiplicand
ZZNOADD: CALL	ROTATELEFTR1 ;0->LSBit, MSBit->Carry
;
	DJNZ	R7,ZZMULT	;Loop for 16 Bits
; Now shift the result to @R0
	MOV	A,R1	;Save R1
	MOV	R4,A	;in R4
	MOV	R1,#5	;Point to R5,R6
	MOV	A,@R1	;Get LSB
	MOV	@R0,A	;Put LSB
	INC	R0
	INC	R1
	MOV	A,@R1	;Get MSB
	MOV	@R0,A	;Put MSB
	DEC	R0	;Restore R0
	MOV	A,R4	;Restore R1
	MOV	R1,A
;
	RET	; ***** DONE *****
;
;-------------------------------------------------------
; MPY8X8, 8 x 8 multiply, 16 Bit product
;
; On Entry
;	R6	contains multiplicand
;	R7	   "	multiplier
; On Exit
;	A	contains the result LSB
;	R2	   "		    MSB
;
; DESTROYS R5, USES A,R5,R6,R7
;
MPY8X8:	CLR	A	;Clear Result LSB
	MOV	R2,A	;  "	 "    MSB
	MOV	R5,#8	;Bit cntr
;
SHIFT:	CLR	C	;Shift multiplicand 16 Bits
	RLC	A
	XCH	A,R2	;Prepare to
	RLC	A	;RLC R2
	XCH	A,R2
; Shift left multiplicand
	XCH	A,R6
	RLC	A
	XCH	A,R6
;Test carry
	JNC	DECR
;Else add multiplier
	ADD	A,R7
	XCH	A,R6
	ADDC	A,#0
	XCH	A,R6
DECR:	DJNZ	R5,SHIFT
	RET	
;
;-------------------------------------------------------
; DIVIDE  Low byte count 15 / 15 divide
; 		the MSBs must not be 1
;
;			divisor(@R1)
;	result(R6,R7) = ---------------
;			dividend(@R0)
;
;Operands are in reverse order to improve efficiency
;DESTROYS divisor(@R0),dividend(@R1),R0,R1,R6,R7
;The dividend is successively subtracted fromthe divisor
;First check if dividend is 0
DIVIDE: MOV	A,@R0	;Is LSByte=0 ?
	JNZ	OK	;Skip if OK
	INC	R0	;Otherwise test MSByte
	ORL	A,@R0	;A was 0
	JZ	DIVIDEBYZERO
	DEC	R0
;First form the complement of @R0 IN @R0
OK:	CALL	COMPLEMENT	;@R0
;Now clear result area
	CLR	A
	MOV	R7,A
	MOV	R6,A
;ADD destroys @R0, so swap R1,R0
	MOV	A,R0
	XCH	A,R1
	MOV	R0,A
;
DIVLOOP: CALL	ADD ;Add (subtract) once
	INC	R0
	MOV	A,@R0	;if MSBit is 1, negative, done
	DEC	R0
	JB7	DONEDIVIDE   ;Done if -ve result
;Else increment R6,R7
; (Trick courtesy of SIGNETICS 2650)
	INC	R6	;Increment LSB, but dont
	MOV	A,R6
	JNZ	NXT9
	INC	R7	;incr MSB unless new LSB is 0
;
NXT9:	JMP	DIVLOOP	;And try to subtract again
;
DIVIDEBYZERO: MOV	R6,#0FCH ;nearly infinite
	MOV	R7,#0FFH
DONEDIVIDE:	RET
;
;------------------------------------------------
;
; BINTOBCD: Converts an 8 Bit Binary value to BCD
;
;------------------------------------------------
;
; The Binary value is in A
; R0 is pointer to the packed BCD string storage
; DESTROYS R1, R5, R6, R7
BINTOBCD: XCH	A,R0	;Get R0 to R1
	MOV	R1,A	;without changing A
	XCH	A,R0
;
	MOV	R7,#2	;2 is digit byte count
;			;  extension register
;
BINLP1: MOV	@R1,#0	;Clear the BCD area
	INC	R1
	DJNZ	R7,BINLP1
;
	MOV	R6,#8	;Bit count
; now do A = A*2
; to get the MSBit into the carry
BINLP2:	CLR	C
	RLC	A
; now do  BCD = BCD*2 + Carry
	XCH	A,R0	;Get R0
	MOV	R1,A	;to R1
	XCH	A,R0
	MOV	R7,#2	;2 is digit byte count
	MOV	R5,A	;save A in R5
BINLP3:	MOV	A,@R1	;fetch LSByte of BCD
	ADDC	A,@R1	;double it + carry
	DA	A
	MOV	@R1,A	;store it back
	INC	R1	;point at next BCD byte
	DJNZ	R7,BINLP3 ;process it too
	MOV	A,R5	;Retrieve A
; If carry from BCDacc could GOTO error exit
	JC	BINERROR
; Count=count-1, until count=0 :
	DJNZ	R6,BINLP2
	CLR	C ;Reset Indicates normal termination
BINERROR: RET	  ; C set if error termination
;
;-------------------------------------------------------
;
; BCDTOBIN: Converts a 3 digit BCD value to 8 Bit Binary
;
;-------------------------------------------------------
;
;at entry R0 points to the LSByte of a packed BCD string
;
;at exit A has the result
;	 Carry is set if overflow has occurred
; DESTROYS R4,R5,R6,R7
;
; Point at most significant digitpair
; R0 = R0 + #digitpairs - 1
	MOV	A,R0
	ADD	A,#1	; 2 packed bytes - 1
	MOV	R0,A
; count = #digitpairs
	MOV	R7,#2
; BIN = 0
	CLR	A
	MOV	R4,A
; BIN = BIN*10
BCDLP1:	CALL	MUL10
	JC	CONVERSIONERROR
; BIN = BIN + MSNibble(@R0)
	MOV	R5,A	;save BIN*10
	MOV	A,@R0	;get MSDigitpair
	SWAP	A	;mask except
	ANL	A,#0FH	;the MSNibble
; Now have the most significant BCD nibble in A
	ADD	A,R5	;Add BIN*10
	XCH	A,R4	;to A in
	ADDC	A,#0	;double
	XCH	A,R4	;precision
	JC	CONVERSIONERROR
; BIN = BIN * 10
	CALL	MUL10
	JC	CONVERSIONERROR
; bin = bin + LSNibble(@R0)
	MOV	R5,A	;save R5
	MOV	A,@R0
	ANL	A,#0FH ;get LSNibble of MSDigit
	ADD	A,R5	;add it to BIN
	XCH	A,R4	;in
	ADDC	A,#0	;double
	XCH	A,R4	;precision
	JC	CONVERSIONERROR
; point now to the next lower digitpair
	DEC	R0
; count = count-1 until 0 repeat proceedure
	DJNZ	R7,BCDLP1
CONVERSIONERROR: RET	; C cleared normal return
;			;   set if error return
;
; Utility to multiply BIN by 10
MUL10:	MOV	R5,A	;save A
	XCH	A,R4	;save R4
	MOV	R6,A
	XCH	A,R4
; BIN = BIN * 2
	CLR	C
	RLC	A
	XCH	A,R4
	RLC	A
	XCH	A,R4
	JC	MUL10ERROR
; BIN = BIN * 4
	RLC	A
	XCH	A,R4
	RLC	A
	XCH	A,R4
	JC	MUL10ERROR
; BIN = BIN * 5
	ADD	A,R5
	XCH	A,R4
	ADDC	A,R6
	XCH	A,R4
	JC	MUL10ERROR
; BIN = BIN * 10
	RLC	A
	XCH	A,R4
	RLC	A
	XCH	A,R4
;
MUL10ERROR: RET	; C set if error on return
;
;--------------------------------------------
;
;*************THIS ROUTINE DID NOT WORK*****************
;
;DIVIDE: 16 Bit by 16 Bit Integer Division
;
;		  dividend (@R0)
;  result (@R0) = --------------  + remainder (@R6)
;		  divisor (@R1)
;
; DESTROYS ALL REGISTERS except R0,R1, in MB0
;	and DESTROYS  R7' (R31) in MB1
; DO NOT CALL THIS ROUTINE WHILST IN MB1
;
; The divisor is successively subtracted from the high 
;   order bits of the dividend. After each subtraction 
;   the result is used instead of the initial dividend
; The result is increased by 1 each time.
; When the result of the subtraction is negative the 
;   partial result is restored by adding the divisor 
;   back to it.
; The result is simulataneously decremented by 1
;
;First check if divisor is 0
;
;*************THIS ROUTINE DID NOT WORK*****************
;
DIVIDE:	MOV	A,@R1	;Is LSByte=0 ?
	JNZ	OK	;Skip if OK
	INC	R1	;Otherwise test MSByte
	ORL	A,@R1	;A was 0
	JZ	DIVIDEBYZEROERROR
	DEC	R1
; Dividend is @R0
;clear result register pair
OK:	CLR	A
	MOV	R7,A ;Use R6, R7 as result pair
	MOV	R6,A
;loop counter,R5
	MOV	R5,#16
;
	CLR	C
;Rotate Dividend left 
ZZDIV1:	CALL	SHIFTLEFTR0 ; C  -> LSBit
;				MSBit -> Carry
;Rotate Remainder left
	MOV	A,R0	;Save R0 in 
	MOV	R4,A	; R4
	MOV	R0,#6	;Point at R6,R7
	CALL	SHIFTLEFTR0 ;C -> LSBit, MSBit-> C
;Trial subtraction of divisor from result
; Problems here, as need to complement (& change)
;  divisor before we can subtract it.
; So have to use R2,R3 as scratch
	MOV	A,@R1 ;get divisor in R2,R3
	MOV	R2,A
	INC	R1
	MOV	A,@R1
	MOV	R3,A
	DEC	R1  ;still points at divisor
; Now we could do:
;	MOV	R0,#2	;Point at R2,R3
;	MOV	R1,#6	;Point at R6,R7
; !!!! CLANG !!!! have destroyed pointer R1
; So we must destroy R7' (R31)
	MOV	R0,#31
	MOV	A,R1	;Save R1
	MOV	@R0,A	; in R31
	MOV	R0,#2
	MOV	R1,#6
	CALL	COMPLEMENTR0
	CALL	ADD	;Result in scratch R2,R3
	MOV	A,R2	;Copy result to R6,7
	MOV	R6,A
	MOV	A,R3
	MOV	R7,A
;
	MOV	R0,#31
	MOV	A,@R0	;Get divisor pointer
	MOV	R1,A	;to R1 again
; Carry must be 1 if borrow occurred (@R0 >= @R1)
	JNC	ZZPOS
;otherwise negative, restore dividend
	MOV	R0,#6	;remainder regs
	CALL	ADD
;	
ZZPOS:	CPL	C ;Calc result bit
;Restore R0
	MOV	A,R4
	MOV	R0,A
;
	DJNZ	R5,ZZDIV1 ;Loop for 16 bits
;
; Shift in last result bit
	CALL	SHIFTLEFTR0
; On exit the result is in @R0, the remainder in R6,R7
;
	RET	; ***** DONE ****
;
DIVIDEBYZEROERROR: MOV A,#0FFH ;Move #infinity to
	MOV	@R0,A	;@R0
	INC	R0
	MOV	@R0,A
	DEC	R0
; Output a diagnostic message if desired
	RET
;
