; a fast MEMMOVE routine that uses the 80836 if present
; this function can replace the memmove routine supplied with
; Microsoft and Borland C runtime libraries (sample coded in
; (small model)
;
; Author:		M. Steven Baker
; Date:			January 19, 1989
; Last Revision:	January 31, 1989
;
;	added test to force word alignment of source

; PROCEDURE cputype
;
; find out if we have an 80386 CPU to use
; This routine can be used from the real or virtual
; 8086 mode only.  Otherwise will likely cause a
; protection violation (PUSHF instruction is
; privileged)
;
; Based on COMPAQ DESKPRO 386/20 Technical Reference Guide
; (originally suggested by INTEL)
;
; Entry:	none
; Exit:		AX = CPU type
;			0086h if 8088/8086
;			0286h if 80286
;			0386h if 80386
; Stack:	8 bytes
;
	.MODEL small
	.CODE

	public	_cputype

_cputype	proc near
	pushf			;save the real flags register
;
	pop	ax		;get register off stack
	push	ax		;and save it again
;
	and	ax,0fffh	;zero out bits 12-15
	push	ax
	popf			;try to put into flags
	pushf	
	pop	ax		;let's see what came out
				;of flags
	and	ax,0F000h	;mask off bits 12-15
	cmp	ax,0F000h	;were these bits all 1's
	je	is_86		;if so, it's an 8086
;
	pop	ax		;get flags register to AX
	push	ax		;and save it again
;
	or	ax,0F000h	;now try to set bits 12-15
	push	ax
	popf			;of the flags register
	pushf
	pop	ax		;and see what came out
	and	ax,0F000h	;are high bits set
	je	is_286		;if so, we have a 386
is_386:	mov	ax,0386h
	jmp	short	cpu_exit
;
is_286:	mov	ax,0286h
	jmp	short	cpu_exit
;
is_86:	mov	ax,86h
;
cpu_exit:
	popf			;restore flags
	ret
_cputype	endp	
;

; PROCEDURE memmove
;
; void * memmove(void *dest,const void *src,size_t count);
; returns *dest

	public	_memmove

_memmove	proc near
	push	bp		
	mov	bp,sp		;use BP to get variables
	mov	dx,di		;save register variables
	mov	bx,si		;into BX and DX
	mov	ax,ds
	mov	es,ax		;force ES to DS (small model
	mov	di,[bp+4]	;get destination ptr
	mov	si,[bp+6]	;and source ptr
	mov	cx,[bp+8]	;and byte count
;
	mov	ax,di		;memmove returns dest ptr
	jcxz	mmov_ret	;if count=0, where done
;
	cmp	di,si		;do we have possible overlap
	jb	forward1	;if not go do it
;
	je	mmov_ret	;if dest=src, then do nothing
	mov	ax,si		;
	add	ax,cx		;find end point of source
	cmp	di,ax		;do we overlap with dest ?
	mov	ax,di		;memmove must return dest ptr
	jae	forward		;no overlap
;
	add	si,cx		;copy from end of string back
	add	di,cx
	std			;set reverse direction
	dec	si
	dec	di
;
	test	si,1		;are we at least word aligned?
	jz	back1
	movsb			;get word aligned	
	dec	cx
;
back1:	shr	cx,1		;convert to words
	jnc	back2		;carry if odd count
	movsb
back2:	dec	si		;set index for word
	dec	di
	shr	cx,1
patch_o1:
	jnc	back4
	movsw
back4:	sub	di,2
	sub	si,2
	db	66h		;operand size [refix
patch_i1:
	rep	movsw
	cld			;force forward direction
mmov_ret:
	mov	si,bx		;restore register variables
	mov	di,dx
	pop	bp
	ret
forward1:
	test	si,1		;are we at least word aligned?
	jz	forward
	movsb			;get word aligned	
	dec	cx
forward:
	shr	cx,1		;byte count to words
	jnc	forwd4		;if no carry, then even count
	movsb
forwd4: shr	cx,1		;word count to dwords
patch_o2:
	db	66h		;operand size prefix
	rep	movsw		;(rep movsd) move double words
	adc	cx,cx		;if carry was odd
	rep	movsw		;if CX<>0 movsw
;
	mov	si,bx		;restore register variables
	mov	di,dx
	pop	bp
	ret
_memmove	endp

	public	_cpupatch

_cpupatch	proc near
	call	_cputype
	cmp	ax,0386h
	jne	patch_mmove
	ret
;
patch_mmove:
	push	ds		;save registers
	push	es	
	push	si
	push	di
	push	cx
;
	mov	ax,cs		;set DS=ES to CS
	mov	ds,ax
	mov	es,ax
	mov	si,offset patch_i1	;input patch
	mov	di,offset patch_o1
	mov	cx,(offset forward- offset patch_i1)
	rep	movsb
;
	mov	si,offset patch_i1
	mov	di,offset patch_o2
	mov	cx,(offset forward - offset patch_i1)
	rep	movsb
;
	pop	cx
	pop	di
	pop	si
	pop	es
	pop	ds
	ret

_cpupatch	endp

end