;       SCCSID = @(#)cutil.asm	6.2 91/08/26
;      SCCSID = @(#)cutil.asm	6.2 91/08/26
;
; Utilities called by C programs in doscall1:
;
;	strlenf
;	strcmpf
;	strncmpf
;	strcpyf
;	memsetf
;	(the above procedures adopted from lanman group's netlib, which
;	in turn was adtoped from language group's C runtime lib)

	.286p
	.xlist
include basemaca.inc
include mi.inc
include dc1segs.inc						; DCR 704
	.list

CODE16	  SEGMENT						; DCR 704
CODE16	  ENDS

	ASSUME	CS: CODE16 , DS: nothing, SS: nothing

;***	unsigned int strlenf(char far *pstr)
;
;	Returns number of bytes in a null-terminated string. The null does
;	not count.
;
;	DEF:	unsigned int strlenf(char far *pstr)
;
;	ENTRY:	pstr = far (long) pointer to string
;
;	EXIT:	returns length of string

CODE16	     SEGMENT

Procedure _strlenf

ArgVar slf_pstr,DWORD		; pointer to string

	EnterProc
	mov	dx,di		; Preserve DI
	les	di,slf_pstr	; ES:DI -> string
	xor	ax,ax		; null byte
	mov	cx,-1
	repne	scasb		; scan for null, CX = -(1+strlen(str))
	not	cx
	dec	cx
	xchg	ax,cx		; AX = strlen(str)
	mov	di,dx		; Restore DI
	LeaveProc
	ret

EndProc _strlenf


;***	int strcmp(char far *pstr1, char far *pstr2)
;
;	Compares two strings.
;
;	ENTER	pstr1 -> first string
;		pstr2 -> second string
;
;	EXIT	if both strings are ASCII strings then
;		  returns -1 if first string < second string
;			   0 if strings are equal
;			   1 if first string > second string
;		if either string contains DBCS then
;		  returns  0	  if strings are equal
;			  -1 or 1 if strings are not equal

Procedure _strcmpf

ArgVar scf_pstr1,DWORD
ArgVar scf_pstr2,DWORD

	EnterProc
	mov	dx,di		; preserve SI and DI
	mov	bx,si
	push	ds		; preserve DS
	lds	si,scf_pstr1	; ds:si -> str1
	les	di,scf_pstr2	; es:di -> str2
	xor	ax,ax
	mov	cx,-1
	repne	scasb		; compute length of str2
	not	cx		; CX = strlen(str2)+1
	sub	di,cx		; restore ES:DI -> str2
	repe	cmpsb		; compare while equal, at most length of str2
	je	sc10		; str2 == str1?  (AX = 0)
				;	str2 < str1	str2 > str1
	sbb	ax,ax		;	AX=-1, CY=1	AX=0, CY=0
	sbb	ax,-1		;	AX=-1		AX=1

sc10:	pop	ds		; restore DS
	mov	si,bx		; restore SI and DI
	mov	di,dx
	LeaveProc
	ret

EndProc _strcmpf


;***	int strncmpf(char far *pstr1, char far *pstr2, unsigned int count)
;
;	Compares two strings (similar to strcmp), but examines at most
;	count bytes. The effect is like truncating both strings to count
;	bytes long and then do strcmpf.
;
;	ENTER	pstr1 -> first string
;		pstr2 -> second string
;		count  = maximum number of bytes to examine
;
;	EXIT	if both strings are ASCII strings then
;		  returns -1 if first string < second string
;			   0 if strings are equal
;			   1 if first string > second string
;		if either string contains DBCS then
;		  returns  0	  if strings are equal
;			  -1 or 1 if strings are not equal

Procedure _strncmpf


ArgVar snc_pstr1,DWORD
ArgVar snc_pstr2,DWORD
ArgVar snc_count,WORD

	EnterProc
	SaveReg <di,si,ds>

	mov	cx,snc_count	; cx = max number of bytes to compare
	jcxz	snc20		; it's as if strings are equal

	mov	bx,cx		; bx saves count
	les	di,snc_pstr1	; es:di -> str1

	mov	si,di		; si saves first pointer
	xor	ax,ax		; ax=0
	repne	scasb		; count bytes
	neg	cx		; cx=count - strlen
	add	cx,bx		; strlen + count - strlen

	mov	di,si		; restore first pointer

	lds	si,snc_pstr2	; ds:si -> str2

	repe	cmpsb		; compare strings
	mov	al,[si-1]
	xor	cx,cx

	cmp	al,es:[di-1]	; last-first
	ja	snc10
	je	snc20

	dec	cx		; first string is bigger
	dec	cx		; make FFFE so 'not' will give 0001

snc10:	not	cx		; return -1

snc20:	mov	ax,cx		; return value

	RestoreReg <ds,si,di>
	LeaveProc
	ret
EndProc _strncmpf



;*** unsigned long int ulongmul (unsigned long int ulong_1, unsigned long int ulong_2)
;
;	Multiplies two unsigned long integers.
;
;	ENTER	ulong_1 -> first unsigned long integer
;		ulong_2 -> second unsigned long integer
;
;	EXIT	returns (unsigned long) (ulong_1 * ulong_2)
;

Procedure _ulongmul

ArgVar ulong_1,DWORD
ArgVar ulong_2,DWORD

	EnterProc
	SaveReg <cx,di,si,ds>
                                                       
        mov    ax, word ptr ulong_2+2   ;hi2        
        mov    cx, word ptr ulong_1     ;lo1           
        mul    cx                       ;lo1 * hi2
        mov    si, ax
        
        mov    ax, word ptr ulong_2     ;lo2
        mov    cx, word ptr ulong_1+2   ;hi1
        mul    cx                       ;hi1 * lo2
        mov    di, ax
        
        mov    ax, word ptr ulong_2     ;lo2 
        mov    cx, word ptr ulong_1     ;lo1           
        mul    cx                       ;lo1 * lo2

        add   dx, si 
        adc   dx, di 

	RestoreReg <ds,si,di,cx>
	LeaveProc
	ret

EndProc _ulongmul



;*** unsigned long int ushortmul (unsigned int ushort_1, unsigned int ushort_2)
;
;	Multiplies two unsigned short integers.
;
;	ENTER	ushort_1 -> first unsigned short integer
;		ushort_2 -> second unsigned short integer
;
;	EXIT	returns (unsigned long) (ushort_1 * ushort_2)
;

Procedure _ushortmul

ArgVar ushort_1,WORD
ArgVar ushort_2,WORD

	EnterProc
	SaveReg <cx,ds>

        mov ax, ushort_1
        mov cx, ushort_2
        mul cx

	RestoreReg <ds,cx>
	LeaveProc
	ret

EndProc _ushortmul


;*** unsigned udiv (unsigned long int Numerator, unsigned int Denominator)
;
;	Divides an unsigned long integer by an unsigned short
;       integer.
;
;	ENTER	Numerator   ->  unsigned long NUMR operand
;		Denominator ->  unsigned short DIVR operand
;
;	EXIT	returns (unsigned) (Numerator / Denominator)
;               Note: remainder is not returned
;

Procedure _udiv

ArgVar Numerator,DWORD
ArgVar Denominator,WORD

	EnterProc
	SaveReg <cx,ds>

        mov ax, word ptr Numerator
        mov dx, word ptr Numerator+2
        mov cx, Denominator
        div cx
        xor dx, dx

	RestoreReg <ds,cx>
	LeaveProc
	ret

EndProc _udiv

;***	char far *strcpyf(char far *pDest, char far *pSrc)
;
;	Copies source string to destination.
;
;	ENTER	pDest -> points to destination
;		pSrc  -> points to source string
;
;	EXIT	returns pDest


Procedure _strcpyf

ArgVar scy_pDest,DWORD
ArgVar scy_pSrc,DWORD

	EnterProc
	mov	dx,di		; Preserve SI and DI
	mov	bx,si
	push	ds		; Preserve DS
	lds	si,scy_pSrc	; ds:si -> Src
	mov	di,si		; set ES:DI to DS:SI for first part
	mov	ax,ds
	mov	es,ax

	xor	ax,ax
	mov	cx,-1		; find length of src
	repne	scasb
	not	cx		; CX=strlen(src)+1

	les	di,scy_pDest	; es:di -> Dest
	mov	ax,di		; AX = return value (offset)
;
; There are 4 situations as far as word alignment of "src" and "dst":
;	1. src and dst are both even	(best case)
;	2. src is even and dst is odd
;	3. src is odd and dst is even
;	4. src and dst are both odd	(worst case)
;
; Case #4 is much faster if a single byte is copied before the
; REP MOVSW instruction.  Cases #2 and #3 are effectively unaffected
; by such an operation.  To maximum the speed of this operation,
; only DST is checked for alignment.  For cases #2 and #4, the first
; byte will be copied before the REP MOVSW.
;
	test	al,1		; fast check for dst being odd address
	jz	scy10

	movsb			; move a byte to improve alignment
	dec	cx
;
; Now the bulk of the copy is done using REP MOVSW.  This is much
; faster than a REP MOVSB if the src and dst addresses are both
; word aligned and the processor has a 16-bit bus.  Depending on
; the initial alignment and the size of the region moved, there
; may be an extra byte left over to be moved.  This is handled
; by the REP MOVSB, which moves either 0 or 1 bytes.
;
scy10:	shr	cx,1		; Shift CX for count of words
	rep	movsw		; CF set if one byte left over
	adc	cx,cx		; CX = 1 or 0, depending on Carry Flag
	rep	movsb		; possible final byte
;
; Return the Dest address in DX:AX
;
	mov	si,bx
	mov	di,dx		; Restore SI and DI

	pop	ds		; restore ds
	mov	dx,es		; segment part of dest address
	LeaveProc
	ret

EndProc _strcpyf


;***	char far *memsetf(char far *pDest, char val, unsigned int count)
;
;	Fill count bytes in destination buffer with value val.
;
;	ENTER	pDest -> destination buffer
;		val    = fill value
;		count  = number of bytes to fill
;
;	EXIT	returns pDest


Procedure _memsetf

ArgVar msf_pDest,DWORD
ArgVar msf_val,DBYTE		; C passes a char in a word
ArgVar msf_count,WORD

	EnterProc
	push	di

	les	di,msf_pDest	; es:di -> dest

	mov	bx,di		; bx saves dest pointer
	mov	cx,msf_count	; cx = number of bytes to fill
	jcxz	msf20		; if no work to do

	mov	al,msf_vall	; al = fill value
	mov	ah,al		;   put it in ah too
	mov	dx,di
	shr	dx,1		; on word boundary?
	jnc	msf10		;   yes, store by words
				;   no, do the leading (odd) byte first
	stosb
	dec	cx		; 1 byte down, cx-1 to go

msf10:	mov	dx,cx		; odd byte to move or not? dx remembers
	shr	cx,1		; divide 2; we're moving words, not bytes

	rep	stosw

	shr	dx,1		; see if an odd byte to move
	jnc	msf20

	mov	es:[di],al	; store the odd byte

msf20:	mov	ax,bx		; return value: dest addr

	mov	dx,es		; segment part of addr
	pop	di
	LeaveProc
	ret

EndProc _memsetf

CODE16	  ends
	end
