M EQU Byte Ptr 0[BX] TITLE 'INTEGER MULTIPLY DIVIDE AND 16-BIT OPERATIONS' ;***************************************************************************** ; * ; 16-BIT OPERATIONS * ; * ;***************************************************************************** ; Revision : Jan. 05, 1983 File "XREG.A86" PUBLIC ABSHL ;Absolute PUBLIC NEGHL ;Negate PUBLIC SUBDH ;Subtract = - PUBLIC CMPS ;Signed Compare >= ? PUBLIC CMPDH ;Binary (unsigned) Compare >= ? PUBLIC GETMAX ;Returns larger value of , in PUBLIC GETMIN ;Returns smaller value of , in PUBLIC GETMN0 ;Returns Min (hl,de) or ;Zero if Min (hl,de) is a Negative value PUBLIC MUL8 ; = * 8 PUBLIC MIDH ;Integer Multiply = * PUBLIC DIDH ;Integer Devide = div PUBLIC IMOD ;Integer Mod = Mod ( div ) PUBLIC MULFRA ;Fraction Multiply = * ; = Integer, = Fraction PUBLIC DIVFRA ;Fraction Devide = div ; / must be a fraction ;----------------------------------------------------------------------------+ ; ABSHL ABSOLUTE AND NEGATE FUNCTIONS : BX=ABS(BX), BX=-BX + ;----------------------------------------------------------------------------+ ;Input : source value ;Output : the absolute or negative (2's complement) of input value abshl: INC BH ;test positive hl value DEC BH JS L@1 RET L@1: NEG BX ;negate BX RET ; ;----------------------------------------------------------------------------+ ; SUBDH SUBTRACT BX FROM DX : BX = DX - BX + ;----------------------------------------------------------------------------+ ;Input : = minuend ; = subtrahend ;Output : = result ; carry = on, de < hl ; = off, de >= hl subdh: MOV AL,DL SUB AL,BL ;subtract l from e MOV BL,AL ;store partial result MOV AL,DH SBB AL,BH ;subtract h from d MOV BH,AL ;store result RET ; ;----------------------------------------------------------------------------+ ; CMPS COMPARE DE AND HL (SIGNED VALUES - ) + ;----------------------------------------------------------------------------+ ;Input : = value 1 ; = value 2 ;Output : carry = on, de < hl ; = off, de >= hl ; Uses cmps: ; Test sing bits MOV AL,DH AND AL,BH JNS cmpj01 ; Both negative MOV AL,DL SUB AL,BL ;subtract l from e MOV AL,DH SBB AL,BH ;subtract h from d ; if carry set ==> dl > hl JNZ L@2 RET ;if de=hl leave carry off L@2: CMC ;complement carry RET ; Test if both positive or different signs cmpj01: MOV AL,DH OR AL,BH JNS cmpj02 ; de and hl have different signs : carry off if de>=0 (de>hl) XOR AL,AL OR AL,DH JS L@3 RET L@3: CMC RET ;carry on if hl>=0 (de=hl ;----------------------------------------------------------------------------+ ; CMPDH COMPARE DE AND HL (UNSIGNED BINARY) + ;----------------------------------------------------------------------------+ ;Input : = value 1 ; = value 2 ;Output : carry = on, de < hl ; = off, de >= hl ; zero = on, de = hl cmpdh: MOV AL,DH CMP AL,BH ;compare higher byte d,h JZ L@4 RET ;carry set for dh L@4: MOV AL,DL ;d=h CMP AL,BL ;compare lower byte e,l RET ;carry set for e=l ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; GETMIN : RETURNS IN THE SMALLER VALUE OF AND ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;on entry: ; values in , ;on exit: Smaller value in GETMIN: CMP BX,DX JNLE L@5 RET L@5: MOV BX,DX RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; GETMN0 : RETURNS IN THE SMALLER VALUE OF AND ; ; IF SMALLER VALUE IS NAGATIVE, THEN RETURNS ZERO ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;on entry: ; values in , ;on exit: Smaller value of , or zero in GETMN0: CMP BX,DX JLE GMINJ1 MOV BX,DX GMINJ1: ;Check < 0 ? MOV AL,BH OR AL,BH JS L@6 RET L@6: MOV BX,0 RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; GETMAX : RETURNS IN THE LARGER VALUE OF AND ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;on entry: ; values in , ;on exit: larger value in GETMAX: CMP BX,DX JL L@7 RET L@7: MOV BX,DX RET EJECT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; INTEGER MULTIPLICATION = * 8 ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;on entry: ; has multiplicant ;on exit: has product ; used MUL8: PUSH CX ;Stack MOV CH,3 ;Left shift 3 times MUL01: XOR AL,AL MOV AL,BL RCL AL,1 ;Left shift Thru carry MOV BL,AL MOV AL,BH RCL AL,1 MOV BH,AL DEC CH JNZ MUL01 POP CX RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; INTEGER MULTIPLICATION ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;on entry: has multiplicand ; has multiplier ;on exit: has product ; has multiplicand ; unchanged ;registers used: and flags ;this routine version ensures that the multiplier is ;the smaller of the two operands, so that if either is ;a one byte value, execution time is halved. ;code length: 64 bytes ;execution time: 257 clock cycles (8080) - best case ; 613 clock cycles (8080) - worst case ; 539 clock cycles (8080) - typical midh: PUSH CX ;save MOV AL,BH ;is less than ? CMP AL,DH JB mpy3 ;don't exchange if so XCHG BX,DX ;smallest operand now in mpy3: MOV CH,BH ; gets multiplier MOV CL,BL MOV BX,0 ;clear partial product MOV AL,CH ;get hi byte of multiplier OR AL,AL ;see if it is zero PUSH CX ;save the multiplier JZ L@8 CALL mpy ;multiply hi byte if non-zero L@8: POP CX ;get the saved multiplier MOV AL,CL ;now multiply by lo byte CALL mpy POP CX ;Restore , cleanup stack RET ; Replace this macro to reduce code size ;--------------------------------------------------------------| ;mpy: | ; rept 7 ;in-line for speed | ; dad h ;shift partial product | ; ral ;next multiplier bit | ; jnc $+4 ;add multiplicand if... | ; dad d ;...multiplier set | ; endm | ; dad h ;break out of macro on last | ; ral ;...iteration so we can do a... | ; rnc ;...return instead of jump | ; dad d | ; ret | ;--------------------------------------------------------------| mpy: MOV CL,8 mpy1: SHL BX,1 RCL AL,1 ;next multiplier bit JNB mpy7 ;add multiplicand if ... LAHF ;... multiplier set ADD BX,DX RCR SI,1 SAHF RCL SI,1 mpy7: DEC CL ;done ? JNZ mpy1 RET EJECT ; ;----------------------------------------------------------------------------+ ; integer divide routine + ;----------------------------------------------------------------------------+ ;on entry: has dividend ; has divisor ;on exit: has quotient ; has remainder ;all registers used didh: MOV AL,BH XOR AL,DH ;sgn(result) = sgn(op1) xor sgn(op2) LAHF ;save sign of result in sign flag XCHG AL,AH PUSH AX XCHG AL,AH XOR AL,DH ;test sign of divisor JNS L@9 CALL neghl ;negate it if neg L@9: XCHG BX,DX ;divisor to , =dividend MOV AL,BH OR AL,AL ;test sign of dividend JNS L@10 CALL neghl ;negate dividend if neg L@10: ;now test for small divisor and/or small dividend and ;take appropriate shortcut JZ id1 ;if 1-byte dividend, gen 8 dig quot MOV CH,16 ;else 16-bit quotient MOV AL,DH ;is divisor 1-byte value? OR AL,AL JNZ id4 ;long-loop if not OR AL,DL ;less than 128? (7-bit value) JNS id5 ;short loop if so id4: MOV AL,CH ; counts bits for long-divisor MOV CX,0 ;clear remainder idiv1: LAHF ;save # digits to go XCHG AL,AH PUSH AX SHL BX,1 ;this starts a 32-bit left shift... MOV AL,CL ;... with as lo order word... RCL AL,1 ;and as hi order word MOV CL,AL ;carry bit communicates between bytes MOV AL,CH RCL AL,1 MOV CH,AL MOV AL,CL ;now compare divisor, dividend SUB AL,DL MOV AL,CH SBB AL,DH ;carry bit tells story JB idiv2 ;carry set if divisor larger MOV CH,AL ;otherwise subtract divisor... MOV AL,CL ;...from current dividend SUB AL,DL MOV CL,AL INC BX ;increment quotient idiv2: POP AX ;retrieve # digits remaining XCHG AL,AH SAHF DEC AL ;one less to go JNZ idiv1 ;loop till done id3: POP AX ;get sign of result XCHG AL,AH SAHF JS L@11 RET ;done if positive L@11: neghl: SUB AL,AL ;else, negate SUB AL,BL MOV BL,AL SBB AL,BH SUB AL,BL ;remove from hi byte MOV BH,AL RET id1: MOV CH,8 ;only 8-bit quotient MOV BH,BL ;put 8-bit dividend in MOV BL,AL ;clear OR AL,DH ;=0. test hi divisor JNZ id4 ;do long-divisor loop if 2 bytes OR AL,DL ;7-bit value? JS id4 ;do long-divisor loop if > 127 id5: SUB AL,AL ;clear remainder id2: SHL BX,1 ;short divisor loop. next dividend... RCL AL,1 ;...bit to CMP AL,DL ;exceeds divisor? JB id6 ;don't subtract if not SUB AL,DL ;else subtract divisor INC BX ;update quotient id6: DEC CH ;one less quotient bit to go JNZ id2 MOV CL,AL ;remainder to (=0) JMPS id3 ;take care of quotient sign ;routine to return in the stacked integer modulo ;the integer passed in . imod: POP CX ;get return address POP DX ;dividend to PUSH CX ;restore return addr CALL didh ;divide by , remainder in MOV BH,CH ;move remainder to MOV BL,CL RET EJECT TITLE 'FRACTION MULTIPLY AND DIVIDE ROUTINES' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; MULFRA : Multiply 16-bits fraction and integer, returns 16-bits result ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Revision : Dec. 15, 1982 ; CALL MULFRA ; = Fraction Multiplier ; = Integer Multiplicant ; RET ; = 16-bits Result ; = Unchanged MULFRA: PUSH CX ;Save ; test sign of integer, ensure that it is positive before doing ; the multiplication XOR AL,AL OR AL,BH ;**** ; push psw ;Saves the sign for result ;**** JNS L@12 CALL neghl ;if negative then make it positive L@12: PUSH BX MOV CL,16 ;do 16-bit multiply XOR AL,AL ;Clear work registers MOV BX,0 FRAL01: MOV AL,DH RCR AL,1 MOV DH,AL MOV AL,DL RCR AL,1 MOV DL,AL JNB NOADD ; No bit, no add. MOV AL,CL ; Save the counter for a moment. POP CX ; Get operator number 2. LAHF ; Add this to the temp result. ADD BX,CX RCR SI,1 SAHF RCL SI,1 PUSH CX ; Save op2. MOV CL,AL ; Move the counter back. NOADD: MOV AL,BH RCR AL,1 MOV BH,AL MOV AL,BL RCR AL,1 MOV BL,AL DEC CL JNZ FRAL01 JNB FRAJ01 ;If Carry is set, LAHF ;add one to result INC BX SAHF ;----------------------------------------------------------------------------+ ; Reset the sign for the result + ; + ; POP D ; Op 2 no longer needed + ; POP PSW ; the sign of multiplicant + ; CM NEGHL ; get correct sign for the result + ; XCHG ; Move the result to DE. + ; + ;----------------------------------------------------------------------------+ FRAJ01: ; clean up stack POP DX ; Op 2 no longer needed POP CX ; Restore register RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; DIVFRA : Fractional Divide 2 16-bit integers, returns fractional result ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CALL DIVFRA ; = DIVIDEND, must be smaller than the divisor ; = DIVISOR ; RET ; = The Fraction result DIVFRA: ; make both operands positive CALL abshl ; set dividend positive PUSH BX MOV BH,CH MOV BL,CL CALL abshl ; set divisor positive MOV CH,BH MOV CL,BL POP BX MOV AL,16 ; hl/bc => de (must be a fraction) LAHF XCHG AL,AH PUSH AX MOV DX,0 ;clear work register divl01: XCHG BX,DX SHL BX,1 ; temp=temp*2 XCHG BX,DX SHL BX,1 MOV AL,BL SUB AL,CL MOV BL,AL MOV AL,BH SBB AL,CH MOV BH,AL JNB gezero ; op1-op2 >= 0 ADD BX,CX JMPS divj02 gezero: INC DX divj02: POP AX XCHG AL,AH SAHF DEC AL LAHF XCHG AL,AH PUSH AX JNZ divl01 POP AX ; clear the junk XCHG AL,AH SAHF RET END