Windows下VC++编译器32位memcpy、memmove函数汇编代码详解

发布时间 2023-10-18 23:26:10作者: 赤勇玄心行天道

整理者:赤勇玄心行天道

QQ号:280604597

微信号:qq280604597

QQ群511046632

博客:www.cnblogs.com/gaoyaguo  blog.csdn.net/cyz7758520?type=blog

大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的!

你可以随意转载无需注明出处

写文档实属不易我希望大家能支持我捐助我,金额随意,1块也是支持,我会继续帮助大家解决问题

 

 

memcpy和memmove函数是相同的汇编代码,都支持内存重叠时的复制,这个汇编代码我认为写的比较杂乱,大体流程如下:

  1. 判断src源始缓冲区和dst目的缓冲区是否存在重叠:
    • 如果dst地址小于等于src地址,或dst地址大于等于src+len地址,表示没有重叠,进行从低到高地址复制;
    • 如果dst地址大于src地址,且dst地址小于src+len地址,表示有重叠,进行从高到低地址复制。
  2. CopyUp从低到高地址复制:
    • 如果len长度小于32,就用MovDword或MovByte指令复制。
    • 如果len长度小于128:
      • 如果当前CPU支持SSE2指令,就用MovdqXmmword指令复制。
      • 如果当前CPU不支持SSE2指令,就用RepMovsd指令复制。
    • 如果len长度大于等于128:
      • 如果当前CPU支持增强的快速字符串,就用RepMovsb指令复制。
      • 如果当前CPU不支持增强的快速字符串,就用MovdqXmmword或RepMovsd或Palign指令复制。
  1. CopyDown从低到高地址复制:
    • 如果len长度小于32,就用MovDword或MovByte指令复制。
    • 如果len长度大于等于32:
      • 如果当前CPU支持SSE2指令,就用MovdqXmmword指令复制。
      • 如果当前CPU不支持SSE2指令,就用RepMovsd指令复制。

 

memcpy函数汇编代码源文件通常在“C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\crt\src\x64\memcpy.asm”,

以下是汇编代码的详解:

       page    ,132
        title   memcpy - Copy source memory bytes to destination
;***
;memcpy.asm - contains memcpy and memmove routines
;
;       Copyright (c) Microsoft Corporation. All rights reserved.
;
;Purpose:
;       memcpy() copies a source memory buffer to a destination buffer.
;       Overlapping buffers are not treated specially, so propagation may occur.
;       memmove() copies a source memory buffer to a destination buffer.
;       Overlapping buffers are treated specially, to avoid propagation.
;
;*******************************************************************************

        .xlist
        include vcruntime.inc
        .list
        .xmm

M_EXIT  macro
        ret                     ; _cdecl return
        endm    ; M_EXIT

PALIGN_memcpy macro d
MovPalign&d&:
        movdqa  xmm1,xmmword ptr [esi-d]
        lea     esi, byte ptr [esi-d]

        align   @WordSize

PalignLoop&d&:
        movdqa  xmm3,xmmword ptr [esi+10h]
        sub     ecx,30h
        movdqa  xmm0,xmmword ptr [esi+20h]
        movdqa  xmm5,xmmword ptr [esi+30h]
        lea     esi, xmmword ptr [esi+30h]
        cmp     ecx,30h
        movdqa  xmm2,xmm3

        palignr xmm3,xmm1,d

        movdqa  xmmword ptr [edi],xmm3
        movdqa  xmm4,xmm0

        palignr xmm0,xmm2,d

        movdqa  xmmword ptr [edi+10h],xmm0
        movdqa  xmm1,xmm5

        palignr xmm5,xmm4,d

        movdqa  xmmword ptr [edi+20h],xmm5
        lea     edi, xmmword ptr [edi+30h]
        jae     PalignLoop&d&
        lea     esi, xmmword ptr [esi+d]

        endm    ; PALIGN_memcpy

        CODESEG

    extrn   __isa_available:dword
    extrn   __isa_enabled:dword
    extrn   __favor:dword

page
;***
;memcpy - Copy source buffer to destination buffer
;
;Purpose:
;       memcpy() copies a source memory buffer to a destination memory buffer.
;       This routine does NOT recognize overlapping buffers, and thus can lead
;       to propagation.
;       For cases where propagation must be avoided, memmove() must be used.
;
;       Algorithm:
;
;           Same as memmove. See Below
;
;
;memmove - Copy source buffer to destination buffer
;
;Purpose:
;       memmove() copies a source memory buffer to a destination memory buffer.
;       This routine recognize overlapping buffers to avoid propagation.
;       For cases where propagation is not a problem, memcpy() can be used.
;
;   Algorithm:
;
;       void * memmove(void * dst, void * src, size_t count)
;       {
;               void * ret = dst;
;
;               if (dst <= src || dst >= (src + count)) {
;                       /*
;                        * Non-Overlapping Buffers
;                        * copy from lower addresses to higher addresses
;                        */
;                       while (count--)
;                               *dst++ = *src++;
;                       }
;               else {
;                       /*
;                        * Overlapping Buffers
;                        * copy from higher addresses to lower addresses
;                        */
;                       dst += count - 1;
;                       src += count - 1;
;
;                       while (count--)
;                               *dst-- = *src--;
;                       }
;
;               return(ret);
;       }
;
;
;Entry:
;       void *dst = pointer to destination buffer
;       const void *src = pointer to source buffer
;       size_t count = number of bytes to copy
;
;Exit:
;       Returns a pointer to the destination buffer in AX/DX:AX
;
;Uses:
;       CX, DX
;
;Exceptions:
;*******************************************************************************

ifdef MEM_MOVE
        _MEM_     equ <Sysmemmove>              ; 设置本函数名称为Sysmemmove。
else  ; MEM_MOVE
        _MEM_     equ <Sysmemcpy>               ; 设置本函数名称为Sysmemcpy。
endif  ; MEM_MOVE

%       public  _MEM_                           ; memcpy或memmove函数声明。
_MEM_   proc \
        dst:ptr byte, \
        src:ptr byte, \
        count:IWORD

        ; destination pointer
        ; source pointer
        ; number of bytes to copy

        OPTION PROLOGUE:NONE, EPILOGUE:NONE

        push    edi                             ; 保存edi到栈。
        push    esi                             ; 保存esi到栈。

; size param/4   prolog byte  #reg saved
        .FPO ( 0, 3           , $-_MEM_     , 2, 0, 0 )

        mov     esi,[esp + 010h]                ; 设置esi为src地址。
        mov     ecx,[esp + 014h]                ; 设置ecx为len长度,单位为字节。
        mov     edi,[esp + 0Ch]                 ; 设置edi为dst地址。

;
; 检查源始缓冲区和目的缓冲区是否重叠:
;       如果 (dst <= src) 或 (dst >= src + len) 则
;               没有重叠,执行从低到高地址复制
;       否则
;               有重叠,执行从高到低地址复制
;

        mov     eax,ecx                         ; 设置eax为ecx,也就是len长度。

        mov     edx,ecx                         ; 设置edx为ecx,也就是len长度。
        add     eax,esi                         ; 设置eax为esi+eax,也就是src+len地址。

        cmp     edi,esi                         ; 减法比较edi和esi,也就是dst地址和src地址。
        jbe     short CopyUp                    ; 如果edi无符号小于等于esi,也就是dst地址无符号小于等于src地址,表示src与dst缓冲区没有重叠,进行从低到高地址复制。

        cmp     edi,eax                         ; 减法比较edi和eax,也就是dst地址和src+len地址。
        jb      CopyDown                        ; 如果edi无符号小于eax,也就是dst地址无符号小于src+len地址,表示src与dst缓冲区有重叠,进行从高到低地址复制。
                                                ; 如果edi无符号大于等于eax,也就是dst地址无符号大于等于src+len地址,表示src与dst缓冲区没有重叠,进行从低到高地址复制。

CopyUp:                                         ; 从低到高地址复制。
        cmp     ecx, 020h                       ; 减法比较ecx和32,也就是len和32。
        jb      CopyUpDwordMov                  ; 如果ecx无符号小于32,也就是len无符号小于32,就进行从低到高地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。
        cmp     ecx, 080h                       ; 减法比较ecx和128,也就是len和128。
        jae     CopyUpLargeMov                  ; 如果ecx无符号大于等于128,也就是len无符号大于等于128,就进行从低到高地址Sse2或RepMovsb指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。
        bt      __isa_enabled, __ISA_AVAILABLE_SSE2 ; 检测当前CPU是否支持SSE2指令集。
        jc      XmmCopySmallTest                ; 如果当前CPU支持SSE2指令集,就进行从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的0~127字节复制。
        jmp     Dword_align                     ; 如果当前CPU不支持SSE2指令集,就进行从低到高地址RepMovsd指令复制的源始地址未对齐目的地址未对齐的大于等于4字节复制。

CopyUpLargeMov:                                 ; 从低到高地址Sse2或RepMovsb指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。
        bt      __favor, __FAVOR_ENFSTRG        ; 检测当前CPU是否支持增强的快速字符串(Enhanced Fast Strings Rep Movsb/Stosb、ERMSB,通过cpuid.7.0.ebx:D9指令获取,该特性执行rep movsb可以非常快)。
        jnc     CopyUpSSE2Check                 ; 如果当前CPU不支持增强的快速字符串,就进行从低到高地址Sse2指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。
        rep     movsb                           ; 如果当前CPU支持增强的快速字符串,就进行RepMovsb指令复制。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

;
;      Check if source and destination are equally aligned.
;
CopyUpSSE2Check:                                ; 从低到高地址Sse2指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。
        mov     eax,edi                         ; 设置eax为edi,也就是dst地址。
        xor     eax,esi                         ; 设置eax为eax与esi异或,也就是src地址与dst地址异或。
        test    eax,15                          ; 检测异或后的低4位是否为0。
        jne     AtomChk                         ; 如果不为0,表示src地址与dst地址不能同时16字节对齐,就进行Atom复制。
                                                ; 如果为0,表示src地址与dst地址可以同时16字节对齐,就可以判断是否使用xmm进行复制。
        bt      __isa_enabled, __ISA_AVAILABLE_SSE2 ; 检测当前CPU是否支持SSE2指令集。
        jc      XmmCopy                         ; 如果当前CPU支持SSE2指令集,就进行从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。
                                                ; 如果当前CPU不支持SSE2指令集,就进行Atom复制。

AtomChk:                                        ; 进行Atom复制。
        bt      __favor, __FAVOR_ATOM           ; 检测当前CPU是否支持Atom。
        jnc     Dword_align                     ; 如果当前CPU不支持Atom,就进行从低到高地址RepMovsd指令复制的源始地址未对齐目的地址未对齐的大于等于4字节复制。

        test    edi, 3                          ; 判断edi的低2位dst是否为0,也就是dst地址是否4字节对齐。
        jne     Dword_align                     ; 如果edi的低2位不为0,表示dst地址未4字节对齐,就进行从低到高地址RepMovsd指令复制的源始地址未对齐目的地址未对齐的大于等于4字节复制。

        test    esi, 3                          ; 判断esi的低2位dst是否为0,也就是src地址是否4字节对齐。
        jne     Dword_align_Ok                  ; 如果esi的低2位不为0,表示src地址未4字节对齐,就进行从低到高地址RepMovsd指令复制的源始地址未对齐目的地址已4字节对齐的任意字节复制。
                                                ; 如果当前CPU支持Atom,且dst地址已4字节对齐,且src地址已4字节对齐,就进行从低到高地址Palign指令复制。

; A software pipelining vectorized memcpy loop using PALIGN instructions 这个Palign指令复制目前我还没看懂。

; (1) copy the first bytes to align dst up to the nearest 16-byte boundary
; 4 byte align -> 12 byte copy, 8 byte align -> 8 byte copy, 12 byte align -> 4 byte copy
PalignHead4:
        bt      edi, 2
        jae     PalignHead8
        mov     eax, dword ptr [esi]
        sub     ecx, 4
        lea     esi, byte ptr [esi+4]
        mov     dword ptr [edi], eax
        lea     edi, byte ptr [edi+4]

PalignHead8:
        bt      edi, 3
        jae     PalignLoop
        movq    xmm1, qword ptr [esi]
        sub     ecx, 8
        lea     esi, byte ptr [esi+8]
        movq    qword ptr [edi], xmm1
        lea     edi, byte ptr [edi+8]

;(2) Use SSE palign loop
PalignLoop:
        test    esi, 7
        je      MovPalign8
        bt      esi, 3
        jae     MovPalign4

PALIGN_memcpy 12
        jmp     PalignTail

PALIGN_memcpy 8
        jmp     PalignTail

PALIGN_memcpy 4

;(3) Copy the tailing bytes.
PalignTail:
        cmp    ecx,10h
        jb     PalignTail4
        movdqu xmm1,xmmword ptr [esi]
        sub    ecx, 10h
        lea    esi, xmmword ptr [esi+10h]
        movdqa xmmword ptr [edi],xmm1
        lea    edi, xmmword ptr [edi+10h]
        jmp    PalignTail

PalignTail4:
        bt      ecx, 2
        jae     PalignTail8
        mov     eax, dword ptr [esi]
        sub     ecx,4
        lea     esi, byte ptr [esi+4]
        mov     dword ptr [edi], eax
        lea     edi, byte ptr [edi+4]

PalignTail8:
        bt      ecx, 3
        jae     PalignTailLE3
        movq    xmm1, qword ptr [esi]
        sub     ecx,8
        lea     esi, byte ptr [esi+8]
        movq    qword ptr [edi], xmm1
        lea     edi, byte ptr [edi+8]

PalignTailLE3:
        mov     eax, dword ptr TrailingUpVec[ecx*4]
        jmp     eax

; The algorithm for forward moves is to align the destination to a dword
; boundary and so we can move dwords with an aligned destination.  This
; occurs in 3 steps.
;
;   - move x = ((4 - Dest & 3) & 3) bytes
;   - move y = ((L-x) >> 2) dwords
;   - move (L - x - y*4) bytes
;

Dword_align:                                    ; 从低到高地址RepMovsd指令复制的源始地址未对齐目的地址未对齐的大于等于4字节复制。
        test   edi,11b                          ; 逻辑与比较edi和3,也就是dst地址的低2位。
        jz     short Dword_align_Ok             ; 如果edi的低2位为0,表示dst地址已4字节对齐,就进行从低到高地址RepMovsd指令复制的源始地址未对齐目的地址已4字节对齐的任意字节复制。
                                                ; 如果edi的低2位不为0,表示dst地址未4字节对齐,需要进行dst地址4字节对齐。

Dword_up_align_loop:                            ; 从低到高地址RepMovsd指令复制的目的地址4字节对齐。
        mov     al, byte ptr [esi]              ; 设置al为esi地址的1字节数据。
        mov     byte ptr [edi], al              ; 设置edi地址的1字节数据为al。
        dec     ecx                             ; 设置ecx递减1。
        add     esi, 1                          ; 设置esi递增1。
        add     edi, 1                          ; 设置edi递增1。
        test    edi, 11b                        ; 逻辑与比较edi和3,也就是dst地址的低2位。
        jnz     Dword_up_align_loop             ; 如果edi的低2位不为0,表示dst地址没有4字节对齐,还需要继续进行4字节对齐。
                                                ; 如果edi地低2位为0,表示dst地址已4字节对齐。

Dword_align_Ok:                                 ; 从低到高地址RepMovsd指令复制的源始地址未对齐目的地址已4字节对齐的任意字节复制。
        mov     edx, ecx                        ; 设置edx为ecx。
        cmp     ecx, 32                         ; 减法比较ecx与32。
        jb      CopyUpDwordMov                  ; 如果ecx小于32,表示len长度小于32字节,就进行从低到高地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。
        shr     ecx,2                           ; 设置ecx右移2位,也就是计算剩余dword的复制个数。
        rep     movsd                           ; 将esi地址进行dword字符串复制到edi地址,ecx为dword的复制个数。
        and     edx,11b                         ; 设置edx为len长度的低2位,也就是rep movsd复制后剩余的0~3字节len长度。
        jmp     dword ptr TrailingUpVec[edx*4]  ; 进行从低到高地址MovByte指令复制的源始地址未对齐目的地址未对齐的0~3字节复制。

;
; Code to do optimal memory copies for non-dword-aligned destinations.
;

; The following length check is done for two reasons:
;
;    1. to ensure that the actual move length is greater than any possible
;       alignment move, and
;
;    2. to skip the multiple move logic for small moves where it would
;       be faster to move the bytes with one instruction.
;


        align   @WordSize
ByteCopyUp:
        jmp     dword ptr TrailingUpVec[ecx*4+16]  ; process just bytes


;-----------------------------------------------------------------------------

        align   @WordSize
TrailingUpVec      dd      TrailingUp0, TrailingUp1, TrailingUp2, TrailingUp3 ; 从低到高地址MovByte指令复制的源始地址未对齐目的地址未对齐的0~3字节跳转表。

        align   @WordSize
TrailingUp0:                                    ; 从低到高地址MovByte指令复制的源始地址未对齐目的地址未对齐的0字节复制。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
                                                ; 空闲备用行。
        M_EXIT                                  ; 本函数返回。

        align   @WordSize
TrailingUp1:                                    ; 从低到高地址MovByte指令复制的源始地址未对齐目的地址未对齐的1字节复制。
        mov     al,[esi]                        ; 设置al为esi地址的1字节数据。
                                                ; 空闲备用行。
        mov     [edi],al                        ; 设置edi地址的1字节数据为al。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

        align   @WordSize
TrailingUp2:                                    ; 从低到高地址MovByte指令复制的源始地址未对齐目的地址未对齐的2字节复制。
        mov     al,[esi]                        ; 设置al为esi地址的1字节数据。
                                                ; 空闲备用行。
        mov     [edi],al                        ; 设置edi地址的1字节数据为al。
        mov     al,[esi+1]                      ; 设置al为esi+1地址的1字节数据。
        mov     [edi+1],al                      ; 设置edi+1地址的1字节数据为al。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

        align   @WordSize
TrailingUp3:                                    ; 从低到高地址MovByte指令复制的源始地址未对齐目的地址未对齐的3字节复制。
        mov     al,[esi]                        ; 设置al为esi地址的1字节数据。
                                                ; 空闲备用行。
        mov     [edi],al                        ; 设置edi地址的1字节数据为al。
        mov     al,[esi+1]                      ; 设置al为esi+1地址的1字节数据。
        mov     [edi+1],al                      ; 设置edi+1地址的1字节数据为al。
        mov     al,[esi+2]                      ; 设置al为esi+2地址的1字节数据。
        mov     [edi+2],al                      ; 设置edi+2地址的1字节数据为al。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------

align   @WordSize
CopyDown:                                       ; 从高到低地址复制。
; inserting check for size. For < 16 bytes, use dwords without checking for alignment

        lea     esi, [esi+ecx]                  ; 设置esi为esi+ecx,也就是src结束地址。
        lea     edi,  [edi+ecx]                 ; 设置edi为edi+ecx,也就是dst结束地址。
        cmp     ecx, 32                         ; 减法比较ecx和32。
        jb      CopyDownSmall                   ; 如果ecx无符号小于32,表示len长度小于32,就进行从高到低地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。
        bt      __isa_enabled, __ISA_AVAILABLE_SSE2 ; 检测当前CPU是否支持SSE2指令集。
        jc      XmmMovLargeAlignTest            ; 如果当前CPU支持SSE2指令集,就进行从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的大于等于16字节复制。
                                                ; 如果当前CPU不支持SSE2指令集,就进行从高到低地址RepMovsd指令复制的源始地址未对齐目的地址未对齐的大于等于4字节复制。
; See if the destination start is dword aligned

        test    edi,11b                         ; 逻辑与比较edi和3。
        jz      CopyDownAligned                 ; 如果为0,表示dst地址低2位为0,表示dst地址已4字节对齐,就进行从高到低地址RepMovsd指令复制的源始地址未对齐目的地址已4字节对齐的任意字节复制。

CopyDownNotAligned:                             ; 从高到低地址RepMovsd指令复制的源始地址未对齐目的地址未对齐的大于等于4字节复制。
        mov     edx,edi                         ; 设置edx为edi,也就是dst地址。
        and     edx, 11b                        ; 设置edx为edx逻辑与3,也就是dst地址的低2位。
        sub     ecx, edx                        ; 设置ecx为ecx减edx,也就是len长度减dst地址4字节对齐的长度。
CopyDownAlignLoop:
        mov     al, byte ptr [esi-1]            ; 设置al为esi-1地址的1字节数据。
        mov     byte ptr[edi-1], al             ; 设置edi-1地址的1字节数据为al。
        dec     esi                             ; 设置esi为esi-1。
        dec     edi                             ; 设置edi为edi-1。
        sub     edx, 1                          ; 设置edx为edx-1。
        jnz     CopyDownAlignLoop               ; 如果edx不为0,表示dst地址未4字节对齐,继续进行4字节对齐。
                                                ; 如果edx为0,表示dst地址已4字节对齐,进行从高到低地址RepMovsd指令复制的源始地址未对齐目的地址已4字节对齐的任意字节复制。

CopyDownAligned:                                ; 从高到低地址RepMovsd指令复制的源始地址未对齐目的地址已4字节对齐的任意字节复制。
        cmp     ecx,32                          ; 减法比较ecx和32。
        jb      CopyDownSmall                   ; 如果ecx无符号小于32,表示len长度小于32,就进行从高到低地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。
        mov     edx, ecx                        ; 设置edx为ecx。
        shr     ecx,2                           ; 设置ecx为ecx无符号逻辑右移2位,也就是ecx除以4,表示RepMovsd指令复制Dword个数。
        and     edx,11b                         ; 设置edx为edx逻辑与3,也就是len长度的低2位。
        sub     esi, 4                          ; 设置esi为esi-4,也就是src-4地址,因为要进行从高到低地址RepMovsd指令复制。
        sub     edi, 4                          ; 设置edi为edi-4,也就是dst-4地址,因为要进行从高到低地址RepMovsd指令复制。
        std                                     ; 设置RepMovsd指令的方向为从高到低地址。
        rep     movsd                           ; 进行从高到低地址RepMovsd指令复制。
        cld                                     ; 设置RepMovsd指令的方向为从低到高地址。

        jmp     dword ptr TrailingDownVec[edx*4]; 进行从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的0~3字节复制。


;-----------------------------------------------------------------------------

        align   @WordSize
TrailingDownVec    dd      TrailingDown0, TrailingDown1, TrailingDown2, TrailingDown3   ; 从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的0~3字节跳转表。

        align   @WordSize
TrailingDown0:                                  ; 从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的0字节复制。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
                                                ; 空闲备用行。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

        align   @WordSize
TrailingDown1:                                  ; 从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的1字节复制。
        mov     al,[esi+3]                      ; 设置al为esi+3地址的1字节数据。
                                                ; 空闲备用行。
        mov     [edi+3],al                      ; 设置edi+3地址的1字节数据为al。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

        align   @WordSize
TrailingDown2:                                  ; 从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的2字节复制。
        mov     al,[esi+3]                      ; 设置al为esi+3地址的1字节数据。
                                                ; 空闲备用行。
        mov     [edi+3],al                      ; 设置edi+3地址的1字节数据为al。
        mov     al,[esi+2]                      ; 设置al为esi+2地址的1字节数据。
        mov     [edi+2],al                      ; 设置edi+2地址的1字节数据为al。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

        align   @WordSize
TrailingDown3:                                  ; 从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的3字节复制。
        mov     al,[esi+3]                      ; 设置al为esi+3地址的1字节数据。
                                                ; 空闲备用行。
        mov     [edi+3],al                      ; 设置edi+3地址的1字节数据为al。
        mov     al,[esi+2]                      ; 设置al为esi+2地址的1字节数据。
        mov     [edi+2],al                      ; 设置edi+2地址的1字节数据为al。
        mov     al,[esi+1]                      ; 设置al为esi+1地址的1字节数据。
        mov     [edi+1],al                      ; 设置edi+1地址的1字节数据为al。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。

; Copy overlapping buffers using XMM registers
XmmMovLargeAlignTest:                           ; 从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的大于等于16字节复制。
        test    edi, 0Fh                        ; 逻辑与比较edi和15。
        jz      XmmMovLargeLoop                 ; 如果为0,表示edi地址的低4位为0,表示dst地址已16字节对齐,就进行从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址已16字节对齐的任意字节复制。
                                                ; 如果不为0,表示edi地址的低4位不为0,表示dst地址未16字节对齐,就进行16字节对齐。
XmmMovAlignLoop:
        dec     ecx                             ; 设置ecx为ecx-1。
        dec     esi                             ; 设置esi为esi-1。
        dec     edi                             ; 设置edi为edi-1。
        mov     al, [esi]                       ; 设置al为esi地址的1字节数据。
        mov     [edi], al                       ; 设置edi地址的1字节数据为al。
        test    edi, 0Fh                        ; 检测edi地址的低4位是否为0。
        jnz     XmmMovAlignLoop                 ; 如果edi地址的低4位不为0,表示edi地址未16字节对齐,就继续进行16字节对齐。
                                                ; 如果edi地址的低4位为0,表示edi地址已16字节对齐,就进行从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址已16字节对齐的任意字节复制。

XmmMovLargeLoop:                                ; 从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址已16字节对齐的任意字节复制。
        cmp     ecx, 128                        ; 减法比较ecx和128。
        jb      XmmMovSmallTest                 ; 如果ecx无符号小于128,就进行从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址已16字节对齐的0~127字节复制。
        sub         esi, 128                    ; 设置esi为esi-128,也就是src-128地址。
        sub         edi, 128                    ; 设置edi为edi-128,也就是dst-128地址。
        movdqu     xmm0, xmmword ptr[esi]       ; 设置xmm0为esi地址的16字节数据。
        movdqu     xmm1, xmmword ptr[esi+16]    ; 设置xmm1为esi+16地址的16字节数据。
        movdqu     xmm2, xmmword ptr[esi+32]    ; 设置xmm2为esi+32地址的16字节数据。
        movdqu     xmm3, xmmword ptr[esi+48]    ; 设置xmm3为esi+48地址的16字节数据。
        movdqu     xmm4, xmmword ptr[esi+64]    ; 设置xmm4为esi+64地址的16字节数据。
        movdqu     xmm5, xmmword ptr[esi+80]    ; 设置xmm5为esi+80地址的16字节数据。
        movdqu     xmm6, xmmword ptr[esi+96]    ; 设置xmm6为esi+96地址的16字节数据。
        movdqu     xmm7, xmmword ptr[esi+112]   ; 设置xmm7为esi+112地址的16字节数据。
        movdqu     xmmword ptr[edi], xmm0       ; 设置edi地址的16字节数据为xmm0。
        movdqu     xmmword ptr[edi+16], xmm1    ; 设置edi+16地址的16字节数据为xmm1。
        movdqu     xmmword ptr[edi+32], xmm2    ; 设置edi+32地址的16字节数据为xmm2。
        movdqu     xmmword ptr[edi+48], xmm3    ; 设置edi+48地址的16字节数据为xmm3。
        movdqu     xmmword ptr[edi+64], xmm4    ; 设置edi+64地址的16字节数据为xmm4。
        movdqu     xmmword ptr[edi+80], xmm5    ; 设置edi+80地址的16字节数据为xmm5。
        movdqu     xmmword ptr[edi+96], xmm6    ; 设置edi+96地址的16字节数据为xmm6。
        movdqu     xmmword ptr[edi+112], xmm7   ; 设置edi+112地址的16字节数据为xmm7。
        sub     ecx, 128                        ; 设置ecx为ecx-128,也就是len长度-128。
        test    ecx, 0FFFFFF80h                 ; 逻辑与比较ecx和4294967168。
        jnz     XmmMovLargeLoop                 ; 如果不为0,表示len长度的高25位不为0,表示len长度大于等于128,就继续进行从高到低地址MovdqXmmword指令复制。
                                                ; 如果为0,表示len长度的高25位为0,表示len长度小于128,就进行从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址已16字节对齐的0~127字节复制。

XmmMovSmallTest:                                ; 从高到低地址MovdqXmmword指令复制的源始地址未对齐目的地址已16字节对齐的0~127字节复制。
        cmp     ecx, 32                         ; 减法比较ecx和32。
        jb     CopyDownSmall                    ; 如果ecx无符号小于32,就进行从高到低地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。

XmmMovSmallLoop:
        sub        esi, 32                      ; 设置esi为esi-32,也就是src-32地址。
        sub        edi, 32                      ; 设置edi为edi-32,也就是dst-32地址。
        movdqu     xmm0, xmmword ptr[esi]       ; 设置xmm0为esi地址的16字节数据。
        movdqu     xmm1, xmmword ptr[esi+16]    ; 设置xmm1为esi+16地址的16字节数据。
        movdqu     xmmword ptr[edi], xmm0       ; 设置edi地址的16字节数据为xmm0。
        movdqu     xmmword ptr[edi+16], xmm1    ; 设置edi+16地址的16字节数据为xmm1。
        sub        ecx, 32                      ; 设置ecx为ecx-32,也就是len长度-32。
        test       ecx, 0FFFFFFE0h              ; 逻辑与比较ecx和4294967264。
        jnz        XmmMovSmallLoop              ; 如果不为0,表示len长度的高27位不为0,表示len长度大于等于32,就继续进行从高到低地址MovdqXmmword指令复制。
                                                ; 如果为0,表示len长度的高27位为0,表示len长度小于32,就进行从高到低地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。

CopyDownSmall:                                  ; 从高到低地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。
        test    ecx, 0FFFFFFFCh                 ; 逻辑与比较ecx和4294967292。
        jz      CopyDownByteTest                ; 如果为0,表示len长度的高30位为0,表示len长度小于4,就进行从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的0~3字节复制。
                                                ; 如果不为0,表示len长度的高30位不为0,表示len长度大于等于4,就进行从高到低地址MovDword指令复制。
CopyDownDwordLoop:
        sub     edi, 4                          ; 设置edi为edi-4。
        sub     esi, 4                          ; 设置esi为esi-4。
        mov     eax, [esi]                      ; 设置eax为esi地址的4字节数据。
        mov     [edi], eax                      ; 设置edi地址的4字节数据为eax。
        sub     ecx, 4                          ; 设置ecx为ecx-4,也就是len长度-4。
        test    ecx, 0FFFFFFFCh                 ; 逻辑与比较ecx和4294967292。
        jnz     CopyDownDwordLoop               ; 如果不为0,表示len长度的高30位不为0,表示len长度大于等于4,就继续进行从高到低地址MovDword指令复制。
                                                ; 如果为0,表示len长度的高30位为0,表示len长度小于4,就进行从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的0~3字节复制。
CopyDownByteTest:                               ; 从高到低地址MovByte指令复制的源始地址未对齐目的地址未对齐的0~3字节复制。
        test    ecx, ecx                        ; 逻辑与比较ecx和ecx。
        jz      CopyDownReturn                  ; 如果为0,表示len长度为0,就进行函数返回。
CopyDownByteLoop:
        sub     edi, 1                          ; 设置edi为edi-1。
        sub     esi, 1                          ; 设置esi为esi-1。
        mov     al, [esi]                       ; 设置al为esi地址的1字节数据。
        mov     [edi], al                       ; 设置edi地址的1字节数据为al。
        sub     ecx, 1                          ; 设置ecx为ecx-1。
        jnz     CopyDownByteLoop                ; 如果不为0,表示len长度不为0,就继续进行从高到低地址MovByte指令复制。
CopyDownReturn:
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
                                                ; 空闲备用行。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT


; Using XMM registers for non-overlapping buffers

align       16
XmmCopy:                                        ; 从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。src地址与dst地址可以同时16字节对齐。
        mov         eax, esi                    ; 设置eax为esi,也就是src地址。
        and         eax, 0Fh                    ; 设置eax为eax逻辑与15,也就是计算src地址是否16字节对齐。
        ; eax = src and dst alignment (src mod 16)
        test        eax, eax                    ; 检测eax是否等于0。
        jne         XmmCopyUnaligned            ; 如果eax不等于0,表示src地址未16字节对齐,就进行从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。

        ; in:
        ; edi = dst (16 byte aligned)
        ; esi = src (16 byte aligned)
        ; ecx = len is >= (128 - head alignment bytes)
        ; do block copy using SSE2 stores
XmmCopyAligned:                                 ; 从低到高地址MovdqXmmword指令复制的源始地址已16字节对齐目的地址已16字节对齐的任意字节复制。
        mov         edx, ecx                    ; 设置edx为ecx,也就是剩余长度。
        and         ecx, 7Fh                    ; 设置ecx为ecx逻辑与127,也就是movdq xmmword复制后的剩余长度。
        shr         edx, 7                      ; 设置edx为edx逻辑右移7位,也就是movdq xmmword的复制个数。
        je          XmmCopySmallTest            ; 如果edx为0,表示movdq xmmword的复制个数为0,就进行从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的0~127字节复制。

align       16
XmmCopyLargeLoop:
        movdqa      xmm0,xmmword ptr [esi]      ; 设置xmm0为esi地址的16字节数据。
        movdqa      xmm1,xmmword ptr [esi + 10h]; 设置xmm1为esi+16地址的16字节数据。
        movdqa      xmm2,xmmword ptr [esi + 20h]; 设置xmm2为esi+32地址的16字节数据。
        movdqa      xmm3,xmmword ptr [esi + 30h]; 设置xmm3为esi+48地址的16字节数据。
        movdqa      xmmword ptr [edi],xmm0      ; 设置esi地址的16字节数据为xmm0。
        movdqa      xmmword ptr [edi + 10h],xmm1; 设置esi+16地址的16字节数据为xmm1。
        movdqa      xmmword ptr [edi + 20h],xmm2; 设置esi+32地址的16字节数据为xmm2。
        movdqa      xmmword ptr [edi + 30h],xmm3; 设置esi+48地址的16字节数据为xmm3。
        movdqa      xmm4,xmmword ptr [esi + 40h]; 设置xmm4为esi+64地址的16字节数据。
        movdqa      xmm5,xmmword ptr [esi + 50h]; 设置xmm5为esi+80地址的16字节数据。
        movdqa      xmm6,xmmword ptr [esi + 60h]; 设置xmm6为esi+96地址的16字节数据。
        movdqa      xmm7,xmmword ptr [esi + 70h]; 设置xmm7为esi+112地址的16字节数据。
        movdqa      xmmword ptr [edi + 40h],xmm4; 设置esi+64地址的16字节数据为xmm4。
        movdqa      xmmword ptr [edi + 50h],xmm5; 设置esi+80地址的16字节数据为xmm5。
        movdqa      xmmword ptr [edi + 60h],xmm6; 设置esi+96地址的16字节数据为xmm6。
        movdqa      xmmword ptr [edi + 70h],xmm7; 设置esi+112地址的16字节数据为xmm7。
        lea         esi,[esi + 80h]             ; 设置esi为esi+128。
        lea         edi,[edi + 80h]             ; 设置edi为edi+128。
        dec         edx                         ; 设置edx递减1。
        jne         XmmCopyLargeLoop            ; 如果edx不为0,表示movdq xmmword的剩余复制个数不为0,就继续循环movdq xmmword复制。
                                                ; 如果edx为0,表示movdq xmmword的剩余复制个数为0,就进行从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的0~127字节复制。

XmmCopySmallTest:                               ; 从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的0~127字节复制。
        test        ecx, ecx                    ; 比较ecx和ecx。
        je          CopyUpReturn                ; 如果ecx等于0,表示没有剩余长度,本函数返回。

        ; ecx = length (< 128 bytes)
        mov         edx, ecx                    ; 设置edx为剩余长度ecx。
        shr         edx, 5                      ; 设置edx右移5位,也就是edx除以32,计算xmmword的复制个数。
        test        edx, edx                    ; 比较edx和edx。
        je          CopyUpDwordMov              ; 如果edx等于0,表示xmmword的复制个数为0,就进行从低到高地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。
                                                ; 如果edx不等于0,表示xmmword的复制个数不为0,就进行movdq xmmword复制。

align 16
XmmCopySmallLoop:
        movdqu      xmm0, xmmword ptr [esi]     ; 复制esi地址的16字节数据到xmm0。
        movdqu      xmm1, xmmword ptr [esi + 10h] ; 复制esi+16地址的16字节数据到xmm1。
        movdqu      xmmword ptr [edi], xmm0     ; 复制xmm0的16字节数据到edi地址。
        movdqu      xmmword ptr [edi + 10h], xmm1 ; 复制xmm1的16字节数据到edi+16地址。
        lea         esi, [esi + 20h]            ; 设置esi地址递增32。
        lea         edi, [edi + 20h]            ; 设置edi地址递增32。
        dec         edx                         ; 设置edx递减1。
        jne         XmmCopySmallLoop            ; 如果edx不等于0,则继续循环复制xmmword数据。
                                                ; 如果edx等于0,则进行从低到高地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。

CopyUpDwordMov:                                 ; 从低到高地址MovDword或MovByte指令复制的源始地址未对齐目的地址未对齐的0~31字节复制。esi为src地址,edi为dst地址,ecx为len长度。
        and         ecx, 1Fh                    ; 将剩余长度ecx与31做逻辑与运算。
        je          CopyUpReturn                ; 如果ecx的低5位为0,表示没有剩余长度,本函数返回。

CopyUpDwordTest:
        mov     eax, ecx                        ; 设置eax为剩余长度ecx。
        shr     ecx, 2                          ; 设置ecx右移2位,也就是ecx除以4,计算dword的复制个数。
        je      CopyUpByteTest                  ; 如果dword的复制个数为0,则使用byte进行复制。

CopyUpDwordLoop:
        mov     edx, dword ptr [esi]            ; 设置edx为esi地址的dword数据。
        mov     dword ptr [edi], edx            ; 设置edi地址的dword数据为edx。也就是将src地址的dword数据复制到dst地址。
        add     edi, 4                          ; 设置edi地址递增4。
        add     esi, 4                          ; 设置esi地址递增4。
        sub     ecx, 1                          ; 设置ecx递减1。
        jne     CopyUpDwordLoop                 ; 如果ecx不等于0,则继续循环复制dword数据。
                                                ; 如果ecx等于0,则对剩余长度进行复制byte数据。

CopyUpByteTest:
        mov     ecx, eax                        ; 设置ecx为剩余长度eax。
        and     ecx, 03h                        ; 将剩余长度ecx与3做逻辑与运算。
        je      CopyUpReturn                    ; 如果ecx的低2位为0,表示没有剩余长度,本函数返回。

CopyUpByteLoop:
        mov     al, byte ptr [esi]              ; 设置al为esi地址的byte数据。
        mov     byte ptr [edi], al              ; 设置esi地址的byte数据为al。也就是将src地址的byte数据复制到dst地址。
        inc     esi                             ; 设置esi地址递增1。
        inc     edi                             ; 设置edi地址递增1。
        dec     ecx                             ; 设置ecx递减1。
        jne     CopyUpByteLoop                  ; 如果ecx不等于0,则继续循环复制byte数据。
                                                ; 如果ecx等于0,则数据已经全部复制完毕,本函数返回。

align 16
CopyUpReturn:                                   ; 本函数返回。
        mov     eax,[esp + 0Ch]                 ; 设置本函数返回值为最初的dst地址。
        pop     esi                             ; 从栈恢复esi。
        pop     edi                             ; 从栈恢复edi。
        M_EXIT                                  ; 本函数返回。


; dst addr is not 16 byte aligned
align 16
XmmCopyUnaligned:                               ; 从低到高地址MovdqXmmword指令复制的源始地址未对齐目的地址未对齐的大于等于128字节复制。src地址与dst地址可以同时16字节对齐。
        mov     edx, 010h                       ; 设置edx为16。
        sub     edx, eax                        ; 设置edx为edx减eax,eax为src地址逻辑与15,也就是计算src地址进行16字节对齐需要复制多少字节。
        sub     ecx, edx                        ; 设置ecx为ecx减edx,也就是src地址进行16字节对齐后的剩余长度。
        push    ecx                             ; 保存ecx到栈。
        mov     eax, edx                        ; 设置eax为edx,也就是src地址进行16字节对齐需要复制多少字节。
        mov     ecx, eax                        ; 设置ecx为eax,也就是src地址进行16字节对齐需要复制多少字节。
        and     ecx, 03h                        ; 设置ecx为ecx逻辑与3,也就是进行从低到高地址MovdqXmmword指令复制的源始地址16字节对齐目的地址16字节对齐的MovDword指令复制后的的剩余长度。
        je      XmmAlignDwordTest               ; 如果为0,表示进行mov dword复制的剩余长度为0,就进行mov dword复制。
                                                ; 如果不为0,表示进行mov dword复制的剩余长度不为0,就进行从低到高地址MovdqXmmword指令复制的源始地址16字节对齐目的地址16字节对齐的MovDword指令复制后的剩余长度1~3字节复制。

XmmAlignByte:                                   ; 从低到高地址MovdqXmmword指令复制的源始地址16字节对齐目的地址16字节对齐的MovDword指令复制后的剩余长度1~3字节复制。
        mov     dl, byte ptr [esi]              ; 设置dl为esi地址的1字节数据。
        mov     byte ptr [edi], dl              ; 设置edi地址的1字节数据为dl。
        inc     esi                             ; 设置esi地址递增1。
        inc     edi                             ; 设置edi地址递增1。
        dec     ecx                             ; 设置ecx递减1。
        jne     XmmAlignByte                    ; 如果ecx不为0,则继续循环复制byte数据。
                                                ; 如果ecx为0,表示进行mov dword复制的剩余长度为0,则进行从低到高地址MovdqXmmword指令复制的源始地址16字节对齐目的地址16字节对齐的MovDword指令复制。

XmmAlignDwordTest:                              ; 从低到高地址MovdqXmmword指令复制的源始地址16字节对齐目的地址16字节对齐的MovDword指令复制。
        shr     eax, 2                          ; 设置eax为eax逻辑右移2位,也就是计算src地址进行16字节对齐需要复制多少字节的进行mov dword复制的剩余个数。
        je      XmmAlignAdjustCnt               ; 如果eax为0,表示src地址进行16字节对齐需要复制多少字节的进行MovDword指令复制的剩余个数为0,就进行从低到高地址MovdqXmmword指令复制的源始地址16字节对齐目的地址16字节对齐完毕。
                                                ; 如果eax不为0,就进行src地址进行16字节对齐需要复制多少字节的进行MovDword指令复制。

XmmAlignDwordLoop:
        mov     edx, dword ptr [esi]            ; 设置edx为esi地址的4字节数据。
        mov     dword ptr [edi], edx            ; 设置edi地址的4字节数据为edx。
        lea     esi, [esi+4]                    ; 设置esi地址递增4。
        lea     edi, [edi+4]                    ; 设置edi地址递增4。
        dec     eax                             ; 设置eax递减一。
        jne     XmmAlignDwordLoop               ; 如果eax不为0,就继续循环复制dword数据。

XmmAlignAdjustCnt:                              ; 从低到高地址MovdqXmmword指令复制的源始地址16字节对齐目的地址16字节对齐完毕。
        pop     ecx                             ; 从栈恢复ecx。
        jmp     XmmCopyAligned                  ; 进行从低到高地址MovdqXmmword指令复制的源始地址已16字节对齐目的地址已16字节对齐的任意字节复制。


_MEM_   endp
        end