我正在阅读我班级的一本教科书,我偶然发现了这个问题:
以ARM汇编语言编写函数,该函数将字符串插入特定位置的另一个字符串。功能是:
char * csinsert( char * s1, char * s2, int loc ) ;
该函数有一个指向a1中s1的指针,指向a2中s2的指针,以及a3中关于插入发生位置的整数。该函数将a1中的指针返回给新字符串。
您可以使用库函数strlen和malloc。 strlen输入指向a1中字符串的指针,并返回a1中的长度。 malloc将为新字符串分配空间,其中输入上的a1是所请求空间的字节大小,输出a1是指向所请求空间的指针。 请记住,寄存器a1-a4不会在函数调用中保留它们的值。
这是我创建的字符串插入的C语言驱动程序:
#include <stdio.h>
extern char * csinsert( char * s1, char * s2, int loc ) ;
int main( int argc, char * argv[] )
{
char * s1 = "String 1 are combined" ;
char * s2 = " and string 2 " ;
int loc = 8 ;
char * result ;
result = csinsert( s1, s2, loc ) ;
printf( "Result: %s\n", result ) ;
}
到目前为止我的汇编语言代码是:
.global csinsert
csinsert:
stmfd sp!, {v1-v6, lr}
mov v1, a1
bl strlen
add a1, a1, #1
mov v2, a1
add a2, a2
mov v3, a2
add a3, a3
bl malloc
mov v3, #0
loop:
ldrb v4, [v1], #1
subs v2, v2, #1
add v4, v4, a2
strb v4, [a1], #1
bne loop
ldmfd sp!, {v1-v6, pc} @std
.end
我认为我的代码无法正常运行。当我连接两个决赛时,没有给出结果。为什么我的代码没有正确插入字符串?我相信这个问题出在装配程序中,是不是没有返回任何内容?
任何人都可以解释我的错误是什么吗?我不知道如何使用问题提示的库函数。 谢谢!
答案 0 :(得分:1)
正如问题所述,a1-a4在呼叫中不保留,与ABI匹配。你保存了你的 a1,但你没有保存你的 a2或a3。
strlen
[或任何其他函数]被允许使用a1-a4作为临时注册。因此,为了提高效率,我的猜测是strlen
[或malloc
]使用a2-a4作为临时和[从您的角度来看]破坏了一些寄存器值。
当你到达loop:
时,a2可能是虚假旅程: - )
<强>更新强>
我开始清理你的asm。样式在asm中比C重要10倍。每个 asm行都应该有侧边栏注释。并在这里或那里添加一个空行。因为你没有发布你的更新代码,我不得不猜测这些变化,稍后,我意识到你只有大约25%左右。另外,我开始搞砸了。
我将问题分成三个部分:
- C中的代码
- 获取C代码并在C中生成arm伪代码
- asm中的代码
如果你看一下C代码和伪代码,你会注意到除了指令的任何误用,你的逻辑是错误的(例如你需要在strlen
之前调用两次malloc
)
所以,这是你的汇编程序清理风格[没有太多新代码]。请注意,我可能已经破坏了一些现有逻辑,但我的版本可能更容易看到。我使用制表符来分隔事物并将所有内容排成一行。这可以帮助。此外,评论显示 intent 或说明指令或架构的限制。
.global csinsert
csinsert:
stmfd sp!,{v1-v6,lr} // preserve caller registers
// preserve our arguments across calls
mov v1,a1
mov v2,a2
mov v3,a3
// get length of destination string
mov a1,v1 // set dest addr as strlen arg
bl strlen // call strlen
add a1,a1,#1 // increment length
mov v4,a1 // save it
add v3,v3 // src = src + src (what???)
mov v5,v2 // save it
add v3,v3 // double the offset (what???)
bl malloc // get heap memory
mov v4,#0 // set index for loop
loop:
ldrb v7,[v1],#1
subs v2,v2,#1
add v7,v7,a2
strb v7,[a1],#1
bne loop
ldmfd sp!,{v1-v6,pc} @std // restore caller registers
.end
首先,你应该在真正的C中进行原型设计:
// csinsert_real -- real C code
char *
csinsert_real(char *s1,char *s2,int loc)
{
int s1len;
int s2len;
char *bp;
int chr;
char *bf;
s1len = strlen(s1);
s2len = strlen(s2);
bf = malloc(s1len + s2len + 1);
bp = bf;
// copy over s1 up to but not including the "insertion" point
for (; loc > 0; --loc, ++s1, ++bp) {
chr = *s1;
if (chr == 0)
break;
*bp = chr;
}
// "insert" the s2 string
for (chr = *s2++; chr != 0; chr = *s2++, ++bp)
*bp = chr;
// copy the remainder of s1 [if any]
for (chr = *s1++; chr != 0; chr = *s1++, ++bp)
*bp = chr;
*bp = 0;
return bf;
}
然后,你可以[直到你对手臂感到舒服],原型用C“伪代码”:
// csinsert_pseudo -- pseudo arm code
char *
csinsert_pseudo()
{
// save caller registers
v1 = a1;
v2 = a2;
v3 = a3;
a1 = v1;
strlen();
v4 = a1;
a1 = v2;
strlen();
a1 = a1 + v4 + 1;
malloc();
v5 = a1;
// NOTE: load/store may only use r0-r7
// and a1 is r0
#if 0
r0 = a1;
#endif
r1 = v1;
r2 = v2;
// copy over s1 up to but not including the "insertion" point
loop1:
if (v3 == 0) goto eloop1;
r3 = *r1;
if (r3 == 0) goto eloop1;
*r0 = r3;
++r0;
++r1;
--v3;
goto loop1;
eloop1:
// "insert" the s2 string
loop2:
r3 = *r2;
if (r3 == 0) goto eloop2;
*r0 = r3;
++r0;
++r2;
goto loop2;
eloop2:
// copy the remainder of s1 [if any]
loop3:
r3 = *r1;
if (r3 == 0) goto eloop3;
*r0 = r3;
++r0;
++r1;
goto loop3;
eloop3:
*r0 = 0;
a1 = v5;
// restore caller registers
}