; ; 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 ;