注:参考学习,共同进步。
普通拷贝方式:所有内容都用ldrb/strb拷贝,即1字节拷贝。
快速拷贝原理:尽可能的用4字节ldr/str指令拷贝内容,剩余的内容用1字节拷贝。
地址需要修正原因:因为ldr/str是4字节拷贝的指令,address需要是4字节对齐的才能正常加载/存储.
memmove函数拷贝时需要考虑的因素:
- 源地址和目的地址大小的比较(内存重叠),决定前序还是后序拷贝。
- 源地址和目的地址地址非4,判断能不能修正地址。
- 1字节拷贝的三种情况:
地址非4且不能修正;
修正地址的部分内容用1字节拷贝;
剩余的内容(修正地址后,或4字节拷贝结束后剩余的内容);
c测试代码:
#include <stdio.h>
extern char* mem(void* dst,void*src,int len);
int compare(void* dst,void* src);
void test(void*array,void*dst,void*src,int len);
int main()
{
char test1[30]="123456789***abcdfeghi";
char test2[30]="123456789***abcdfeghi";
char test3[30]="*1234567890**abcdfeghij";
char test4[30]="*1234567890**abcdfeghij";
char test5[30]="*******12345";
char test6[30]="*12345**abcdf";
test(test1, test1, test1+12,9);//dst<src 地址4字节对齐,内容非4测试(从前往后拷贝)
test(test2, test2+12, test2,9);//dst>src 地址4字节对齐,内容非4测试(从后向前拷贝)
test(test3, test3+1, test3+13,10);//dst<src 地址非4字节对齐但可修正+内容非4测试(从前往后拷贝)
test(test4, test4+13, test4+1,10);//dst>src 地址非4字节对齐但可修正+内容非4测试(从后向前拷贝)
test(test5, test5+1, test5+7,5);//dst<src 地址非4字节对齐且不可修正,内容非4测试(从前往后拷贝)
test(test6, test6+8, test6+1,5);//dst>src 地址非4字节对齐且不可修正,内容非4测试(从后向前拷贝)
return 0;
}
int compare(void* dst,void* src)//返回值的正负代表拷贝顺序;返回+/-4代表可以4字节拷贝;返回+/-1代表地址不能修正,只能单字节拷贝
{
int res=((int)dst-(int)src)%4;
if (res==0){
if(dst<=src) return -4;//前序
else return 4;//后序
}
else{
if(dst<=src) return -1;
else return 1;
}
}
void test(void*array,void*dst,void*src,int len)
{
char * const show=array;
printf("before:%s\n",show);
mem( dst, src,len);//调用汇编实现的mem函数
printf(" after:%s\n\n",show);
}
memmove功能实现的汇编代码:
AREA MEMM,CODE,READONLY
EXPORT mem
IMPORT compare
mem
STMFD sp!,{r0-r12,lr};保存进入函数前的现场资源,保存的函数返回地址
mov r9,r0;备份目的地址
mov r7,r0;备份目的地址
mov r5,r1;备份源地址
mov r4,r2;备份拷贝长度
bl compare;调用c
mov r8,r0
mov r0,r7;恢复目的地址
mov r1,r5;恢复源地址
mov r2,r4;恢复拷贝长度
cmp r8,#-4;返回值比较跳转,共4种情况
beq M1;地址修正后可以4字节拷贝(从前往后)的情况
cmp r8,#4
beq M2;地址修正后可以4字节拷贝(从后向前)的情况
cmp r8,#-1
beq L2;地址修正无效,只可以单字节拷贝(从前往后)的情况
cmp r8,#1
beq L5;地址修正无效,只可以单字节拷贝(从后向前)的情况
M1 ;从前向后复制
mvn r8,#3;
bics r7,r1,r8;
beq L1;
rsb r7,r7,#4;修正部分
sub r2,r2,r7
L0 ;地址修正的内容一字节拷贝
subs r7,r7,#1
ldrplb r4,[r1],#1
strplb r4,[r0],#1
bgt L0
L1 ;四字节拷贝
subs r2,r2,#4 ;从前向后复制
ldrpl r4,[r1],#4
strpl r4,[r0],#4
bgt L1
add r2,r2,#4;
L2;剩余的内容一字节拷贝
subs r2,r2,#1
ldrplb r4,[r1],#1
strplb r4,[r0],#1
bgt L2
b out
M2 ;从后往前复制,把地址指针放到最后
mvn r8,#3;
bics r7,r1,r8;修正地址
rsbne r7,r7,#4
sub r5,r2,r7 ;尾数处理
bic r5,r5,r8
sub r2,r2,#1;指针移到最后
add r0,r0,r2
add r1,r1,r2
add r2,r2,#1
sub r2,r2,r5
L3 ;剩余的内容一字节拷贝
subs r5,r5,#1
ldrplb r4,[r1],#-1
strplb r4,[r0],#-1
bgt L3
sub r1,r1,#3;
sub r0,r0,#3;
L4 ;四字节拷贝
subs r2,r2,#4 ;从后往前复制
ldrpl r4,[r1],#-4
strpl r4,[r0],#-4
bgt L4
addmi r2,r2,#4;
add r1,r1,#3;
add r0,r0,#3;
b L6
L5
sub r2,r2,#1;指针移到最后
add r0,r0,r2
add r1,r1,r2
add r2,r2,#1
L6 ;地址修正的内容一字节拷贝
subs r2,r2,#1
ldrplb r4,[r1],#-1
strplb r4,[r0],#-1
bgt L6
b out
out ;函数出口
LDMFD sp!,{r0-r12,pc};恢复进入函数前的现场资源,把先前保存的函数返回地址传给pc
end
运行结果截图:
比较拷贝前后(内存重叠)的内容变化,可得memmove快速拷贝功能实现。