Windows下VC++编译器32位memcpy、memmove函数汇编代码详解
整理者:赤勇玄心行天道
QQ号:280604597
微信号:qq280604597
QQ群:511046632
博客:http://www.cnblogs.com/gaoyaguo http://blog.csdn.net/cyz7758520?type=blog
大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的!
你可以随意转载,无需注明出处!
写文档实属不易,我希望大家能支持我、捐助我,金额随意,1块也是支持,我会继续帮助大家解决问题!
memcpy和memmove函数是相同的汇编代码,都支持内存重叠时的复制,这个汇编代码我认为写的比较杂乱,大体流程如下:
- 判断src源始缓冲区和dst目的缓冲区是否存在重叠:
- 如果dst地址小于等于src地址,或dst地址大于等于src+len地址,表示没有重叠,进行从低到高地址复制;
- 如果dst地址大于src地址,且dst地址小于src+len地址,表示有重叠,进行从高到低地址复制。
- CopyUp从低到高地址复制:
- 如果len长度小于32,就用MovDword或MovByte指令复制。
- 如果len长度小于128:
- 如果当前CPU支持SSE2指令,就用MovdqXmmword指令复制。
- 如果当前CPU不支持SSE2指令,就用RepMovsd指令复制。
- 如果len长度大于等于128:
- 如果当前CPU支持增强的快速字符串,就用RepMovsb指令复制。
- 如果当前CPU不支持增强的快速字符串,就用MovdqXmmword或RepMovsd或Palign指令复制。
- 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