用 ARM 汇编语言编写一个函数,将一个字符串插入到另一个字符串中的特定位置 [英] writing a function in ARM assembly language which inserts a string into another string at a specific location

查看:14
本文介绍了用 ARM 汇编语言编写一个函数,将一个字符串插入到另一个字符串中的特定位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读我班的一本教科书,偶然发现了这个问题:

<块引用>

用 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 extern char * csinsert( char * s1, char * s2, int loc ) ;int main( int argc, char * argv[] ){char * s1 = "字符串 1 合并" ;字符 * s2 = " 和字符串 2 " ;int loc = 8 ;字符 * 结果;结果 = csinsert(s1, s2, loc);printf( "结果:%s\n", 结果 ) ;}

到目前为止我的汇编语言代码是:

 .global csinsert插入:stmfd sp!, {v1-v6, lr}mov v1, a1bl strlen添加 a1, a1, #1mov v2, a1添加a2,a2mov v3, a2添加a3,a3bl mallocmov v3,#0环形:ldrb v4, [v1], #1订阅者 v2, v2, #1添加 v4、v4、a2strb v4, [a1], #1循环ldmfd sp!, {v1-v6, pc} @std.结尾

我认为我的代码运行不正常.当我将两个决赛联系起来时,没有返回任何结果.为什么我的代码没有正确插入字符串?我认为问题出在汇编程序中,它没有返回任何内容吗?

谁能解释一下我的错误是什么?我不确定如何使用问题提示的库函数.谢谢!

解决方案

警告:我已经做了 40+ 年的 asm,我看过一点 arm,但没有使用它.然而,我拉了arm ABI文档.

正如问题所述,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伪代码
- 汇编代码

如果您查看 C 代码和伪代码,您会注意到除了任何指令的误用之外,您的逻辑都是错误的(例如,您需要在 之前调用两次 strlenmalloc)

所以,这里是你的汇编器风格[没有多少新代码].请注意,我可能破坏了您现有的一些逻辑,但我的版本可能看起来更容易.我使用制表符来分隔事物并将所有内容对齐.那可以提供帮助.此外,评论显示意图或说明指令或架构的限制.

.global csinsert插入:stmfd sp!,{v1-v6,lr}//保留调用者寄存器//在调用之间保留我们的参数mov v1,a1mov v2,a2mov v3,a3//获取目标字符串的长度mov a1,v1//设置目标地址为 strlen argbl strlen//调用 strlenadd a1,a1,#1//增加长度mov v4,a1//保存添加 v3,v3//src = src + src(什么???)mov v5,v2//保存添加 v3,v3//偏移量加倍(什么???)bl malloc//获取堆内存mov v4,#0//设置循环索引环形:ldrb v7,[v1],#1订阅者 v2,v2,#1添加 v7,v7,a2strb v7,[a1],#1循环ldmfd sp!,{v1-v6,pc} @std//恢复调用者寄存器.结尾

首先,你应该在真正的 C 中进行原型设计:

//csinsert_real -- 真正的C代码字符 *csinsert_real(char *s1,char *s2,int loc){int s1len;int s2len;字符 *bp;国际字符;字符 *bf;s1len = strlen(s1);s2len = strlen(s2);bf = malloc(s1len + s2len + 1);bp = bf;//将 s1 复制到但不包括插入"点for (; loc > 0; --loc, ++s1, ++bp) {chr = *s1;如果 (chr == 0)休息;*bp = chr;}//插入" s2 字符串for (chr = *s2++; chr != 0; chr = *s2++, ++bp)*bp = chr;//复制 s1 的剩余部分 [如果有]for (chr = *s1++; chr != 0; chr = *s1++, ++bp)*bp = chr;*bp = 0;返回 bf;}

然后,您可以[直到您对 arm 感到满意],在 C伪代码"中创建原型:

//csinsert_pseudo -- 伪手臂代码字符 *csinsert_pseudo(){//保存调用者寄存器v1 = a1;v2 = a2;v3 = a3;a1 = v1;斯特伦();v4 = a1;a1 = v2;斯特伦();a1 = a1 + v4 + 1;malloc();v5 = a1;//注意:加载/存储只能使用 r0-r7//并且 a1 是 r0#如果 0r0 = a1;#万一r1 = v1;r2 = v2;//将 s1 复制到但不包括插入"点循环1:如果(v3 == 0)转到eloop1;r3 = *r1;如果(r3 == 0)转到eloop1;*r0 = r3;++r0;++r1;--v3;转到循环1;循环1://插入" s2 字符串循环2:r3 = *r2;如果(r3 == 0)转到eloop2;*r0 = r3;++r0;++r2;转到循环2;eloop2://复制 s1 的剩余部分 [如果有]循环3:r3 = *r1;如果(r3 == 0)转到eloop3;*r0 = r3;++r0;++r1;转到循环3;eloop3:*r0 = 0;a1 = v5;//恢复调用者寄存器}

I was going through one of my class's textbooks and I stumbled upon this problem:

Write a function in ARM assembly language which will insert a string into another string at a specific location. The function is: char * csinsert( char * s1, char * s2, int loc ) ;

The function has a pointer to s1 in a1, a pointer to s2 in a2, and an integer in a3 as to where the insertion takes place. The function returns a pointer in a1 to the new string.

You can use the library functions strlen and malloc. strlen has as input the pointer to the string in a1 and returns the length in a1. malloc will allocate space for the new string where a1 on input is the size in bytes of the space requested and on output a1 is a pointer to the requested space. Remember the registers a1-a4 do not retain their values across function calls.

This is the C language driver for the string insert I created:

 #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 ) ;
}

My assembly language code so far is:

        .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

I don't think my code works properly. When I link the two finals, there is no result given back. Why does my code not insert the string properly? I believe the issue is in the assembly program, is it not returning anything?

Can anyone explain what my mistake is? I'm not sure how to use the library functions the question hints to. Thanks!

解决方案

Caveat: I've been doing asm for 40+, I've looked at arm a bit, but not used it. However, I pulled the arm ABI document.

As the problem stated, a1-a4 are not preserved across a call, which matches the ABI. You saved your a1, but you did not save your a2 or a3.

strlen [or any other function] is permitted to use a1-a4 as scratch regs. So, for efficiency, my guess is that strlen [or malloc] is using a2-a4 as scratch and [from your perspective] corrupting some of the register values.

By the time you get to loop:, a2 is probably a bogus journey :-)

UPDATE

I started to clean up your asm. Style is 10x more important in asm than C. Every asm line should have a sidebar comment. And add a blank line here or there. Because you didn't post your updated code, I had to guess at the changes and after a bit, I realized you only had about 25% or so. Plus, I started to mess things up.

I split the problem into three parts:
- Code in C
- Take C code and generate arm pseudo code in C
- Code in asm

If you take a look at the C code and pseudo code, you'll notice that any misuse of instructions aside, your logic was wrong (e.g. you needed two strlen calls before the malloc)

So, here is your assembler cleaned for style [not much new code]. Notice that I may have broken some of your existing logic, but my version may be easier on the eyes. I used tabs to separate things and got everything to line up. That can help. Also, the comments show intent or note limitations of instructions or architecture.

.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

At first, you should prototype in real 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;
}

Then, you can [until you're comfortable with arm], prototype in C "pseudocode":

// 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
}

这篇关于用 ARM 汇编语言编写一个函数,将一个字符串插入到另一个字符串中的特定位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆