; 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
