; mem.asm:
;
; masm -Mx -Zi -DSEGNAME=????? asm.asm
;
	TITLE MEM.ASM

;****************************************************************
;* MEM.ASM - Assembly mem-copy routines				*
;*		for 80286 and 80386				*
;****************************************************************
;

?PLM=1	    ; PASCAL Calling convention is DEFAULT
?WIN=0      ; Windows calling convention

.xlist
include cmacros.inc
include windows.inc
.list

	externA	    __WinFlags	    ; in KERNEL
	externA	    __AHINCR	    ; in KERNEL
	externA	    __AHSHIFT	    ; in KERNEL

; The following structure should be used to access high and low
; words of a DWORD.  This means that "word ptr foo[2]" -> "foo.hi".

LONG	struc
lo	dw	?
hi	dw	?
LONG	ends

FARPOINTER	struc
off	dw	?
sel	dw	?
FARPOINTER	ends

; -------------------------------------------------------
;		DATA SEGMENT DECLARATIONS
; -------------------------------------------------------

ifndef SEGNAME
    SEGNAME equ <_TEXT>
endif

createSeg %SEGNAME, CodeSeg, word, public, CODE

sBegin Data
sEnd Data

sBegin CodeSeg
        assumes cs,CodeSeg
        assumes ds,DATA

cProc fstrrchr,<NEAR,PASCAL,PUBLIC,NODATA>,<di>
	ParmD	lsz
	ParmB	c
cBegin
	les	di, lsz
	xor	al, al			; Search for terminating NULL
	mov	cx, -1			; Search forever
	cld				; Moving forward
	repne	scasb			; Look for the NULL
	not	cx			; Negative value minus 1 gives length
	dec	cx			; CX is always incremented
	jcxz	fstrrchr_fail		; Zero length string fails
	dec	di			; DI is one past character found
	dec	di			; Back up to last character in string
	mov	al, c			; Get character to search for
	std				; Moving backwards
	repne	scasb			; Look for the character
	cld				; Reset direction
	jne	fstrrchr_fail		; Fail if not found
	inc	di			; Back up to actual character found
	mov	ax, di			; Return pointer to that character
	mov	dx, es
	jmp	fstrrchr_exit
fstrrchr_fail:
	xor	ax, ax			; Return NULL on failure
	cwd
fstrrchr_exit:
cEnd

;---------------------------Public-Routine------------------------------;
; MemCopy
;
;   copy memory, dons *not* handle overlaped copies.
;
; Entry:
;	lpSrc	HPSTR to copy from
;	lpDst	HPSTR to copy to
;	cbMem	DWORD count of bytes to move
;
; Returns:
;	destination pointer
; Error Returns:
;	None
; Registers Preserved:
;	BP,DS,SI,DI
; Registers Destroyed:
;	AX,BX,CX,DX,FLAGS
; Calls:
;	nothing
; History:
;
;	Wed 04-Jan-1990 13:45:58 -by-  Todd Laney [ToddLa]
;	Created.
;	Tue 16-Oct-1990 16:41:00 -by-  David Maymudes [DavidMay]
;	Modified 286 case to work correctly with cbMem >= 64K.
;	Changed name to hmemcpy.
;	Changed 386 case to copy by longwords
;-----------------------------------------------------------------------;

cProc MemCopy,<NEAR,PASCAL,PUBLIC,NODATA>,<>
;	 ParmD	 lpDst
;	 ParmD	 lpSrc
;	 ParmD	 cbMem
cBegin	<nogen>
	mov	ax,__WinFlags
	test	ax,WF_CPU286
        jz      MemCopy386
        jmp     NEAR PTR MemCopy286
cEnd <nogen>

cProc MemCopy386,<NEAR,PASCAL,PUBLIC,NODATA>,<ds>
	ParmD	lpDst
	ParmD	lpSrc
	ParmD	cbMem
cBegin
	.386
	push	edi
	push	esi
	cld

	mov	ecx,cbMem
	jecxz	mc386_exit

	movzx	edi,di
	movzx	esi,si
	lds	si,lpSrc
	les	di,lpDst

	push	ecx
	shr	ecx,2		; get count in DWORDs
	rep	movs dword ptr es:[edi], dword ptr ds:[esi]
	db	67H
	pop	ecx
	and	ecx,3
	rep	movs byte ptr es:[edi], byte ptr ds:[esi]
	db	67H
	nop
mc386_exit:
	cld
	pop	esi
	pop	edi
	mov	dx,lpDst.sel	; return destination address
	mov	ax,lpDst.off
	.286
cEnd

cProc MemCopy286,<NEAR,PASCAL,PUBLIC,NODATA>,<ds,si,di>
	ParmD	lpDst
	ParmD	lpSrc
	ParmD	cbMem
cBegin
	mov	cx,cbMem.lo	; CX holds count
	or	cx,cbMem.hi	; or with high word
	jnz	@f
	jmp	empty_copy
@@:
	lds	si,lpSrc	  ; DS:SI = src
	les	di,lpDst	  ; ES:DI = dst

next:
	mov	ax,cx
	dec	ax

	mov	ax,di
	not	ax		; AX = 65535-DI

	mov	dx,si
	not	dx		; DX = 65535-SI

	sub	ax,dx
	sbb	bx,bx
	and	ax,bx
	add	ax,dx		; AX = MIN(AX,DX) = MIN(65535-SI,65535-DI)

	; problem: ax might have wrapped to zero

	test	cbMem.hi,-1
	jnz	plentytogo	; at least 64k still to copy
	
	dec	cx		; this is ok, since high word is zero
	sub	ax,cx
	sbb	bx,bx
	and	ax,bx
	add	ax,cx		; AX = MIN(AX,CX)
	inc	cx

plentytogo:
	xor	bx,bx
	add	ax,1		; AX = Num = MIN(count,65536-SI,65536-DI)
				; we must check the carry here!
	adc	bx,0		; BX could be 1 here, if CX==0 indicating
				; exactly 64k to copy
	xchg	ax,cx
	sub	ax,cx		; Count -= Num
	sbb	cbMem.hi,bx

	shr	bx,1
	rcr	cx,1		; if bx==1, then cx ends up 0x8000
	rep	movsw
	jnc	@f
	movsb			; move last byte, if necessary
@@:
	mov	cx,ax		; put low word of count back in cx
	or	ax,cbMem.hi

	jz	done		; If Count == 0 Then BREAK

	or	si,si		; if SI wraps, update DS
	jnz	@f
;
	mov	ax,ds
	add	ax,__AHINCR
	mov	ds,ax		; update DS if appropriate
@@:
	or	di,di		; if DI wraps, update ES
	jnz	next
;
	mov	ax,es
	add	ax,__AHINCR
	mov	es,ax		; update ES if appropriate
	jmp	next
;
; Restore registers and return
;
done:
empty_copy:
	mov	dx,lpDst.sel	; return destination address
	mov	ax,lpDst.off
cEnd

sEnd

sEnd CodeSeg
end