如何使用嵌入式汇编程序(aarch64)循环C数组?

gorkyyrv  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(159)

我有下一个可怕的代码:

void print(const char* text) {
    unsigned long address = 0x9000000;
    unsigned long counter = 0;
    char ch = *text;
    while (ch != '\0') {
        address += counter;
        asm volatile(
            "MOV X10, %[address];"
            "MOV W9, %w[ch];"
            "STRB W9, [X10];"
            :[address]"+r"(address), [ch]"+r"(ch)
        );
        ++counter;
        ch = *(text + counter);
    }
}

void start(void) {
    print("Hello, World!");
}

不幸的是,即使这段代码也不能正确工作,并打印:Hel而不是Hello, World!
然而,我更愿意重写嵌入/内联汇编器中的所有循环逻辑,但我不知道如何在汇编器中读取和重写C数组(text。我尝试了很多变种,都失败了。
这段代码是为aarch 64(cortex-a72)创建的,它在Qemu中通过命令启动:

qemu-system-aarch64 -M virt \
    -cpu cortex-a72 \
    -bios "/opt/homebrew/Cellar/qemu/$(QEMU_VERSION)/share/qemu/edk2-aarch64-code.fd" \
    -m 128M \
    -nographic \
    -device loader,file=$(BUILD_DIR)/kernel.elf \
    -device loader,addr=0x40100000,cpu-num=0

**P.S.**我在macOS(M1 Pro)上使用LLVM clang和aarch 64-elf-binutils编译器和链接器来构建kernel.elf

7vux5j2d

7vux5j2d1#

address未正确递增。
让我们通过打印到stdout来查看这个问题。

#include <stdio.h>

void my_print(const char* text) {
    unsigned long address = 0x9000000;
    unsigned long counter = 0;
    char ch = *text;
    while (ch != '\0') {
        address += counter;
#if 0
        asm volatile(
            "MOV X10, %[address];"
            "MOV W9, %w[ch];"
            "STRB W9, [X10];"
            :[address]"+r"(address), [ch]"+r"(ch)
        );
#endif
        printf("ch:%3d %c address:%lX counter:%lX\n", ch, ch, address, counter);
        ++counter;
        ch = *(text + counter);
    }
}

//void start(void) {
int main(void) {
  my_print("Hello, World!");
}

输出

ch: 72 H address:9000000 counter:0
ch:101 e address:9000001 counter:1
ch:108 l address:9000003 counter:2
ch:108 l address:9000006 counter:3
ch:111 o address:900000A counter:4
ch: 44 , address:900000F counter:5
ch: 32   address:9000015 counter:6
ch: 87 W address:900001C counter:7
ch:111 o address:9000024 counter:8
ch:114 r address:900002D counter:9
ch:108 l address:9000037 counter:A
ch:100 d address:9000042 counter:B
ch: 33 ! address:900004E counter:C

我怀疑OP只想在打印后将address更改为1 *。

// address += counter;
++address;  // Move to after "asm volatile" printing.
while (ch != '\0') {
        asm volatile(
            "MOV X10, %[address];"
            "MOV W9, %w[ch];"
            "STRB W9, [X10];"
            :[address]"+r"(address), [ch]"+r"(ch)
        );
        ++address;
        ++counter;
        ch = *(text + counter);
    }
2exbekwf

2exbekwf2#

哦,也许有人是像我一样的菜鸟,这将帮助他。
首先,解决方案:

void print(const char* text) {
    unsigned long address = 0x9000000;
    asm volatile(
        "MOV X10, %[address];"
        "MOV X11, %[text];"
        :[address]"+r"(address), [text]"+r"(text)
    );
PRINT_STR:
    asm volatile(
        "LDR X9, [X11];"
        "STRB W9, [X10];"
        "ADD X11, X11, #0x1;"
    );
    asm goto ("CBNZ W9, %l0"::::PRINT_STR);
}

void start(void) {
    print("Hello, World!");
}

它是如何工作的?

1.解压缩视频存储器的地址(如果我错了,请纠正我)。在我们的例子中是0x9000000
1.将addresstext变量加载到X10X11寄存器中(没有严格的规则可以使用哪些寄存器)。注意text是一个指针,所以实际上我们将字符串的地址加载到寄存器中,而不是值。

  1. Declare一个标签,以便稍后跳转到它。
    1.将X11寄存器中的地址所定位的下一个字符串字符加载到X9寄存器中。
    1.将加载的值复制到0x9000000
    1.递增X11寄存器的值,换句话说,我们只是将指针切换到字符串中的下一个字符。
    1.最后,如果W9寄存器包含非零值(C字符串以\0结束),则跳转到PRINT_STR

**UPD:**第二个想法是最好的......我想使用汇编程序,因为我认为C代码将是丑陋的,仍然与汇编程序混合。但我忘了这是先生。C,指针就是力量!

void print(const char * text) {
    char * address = (char *)0x9000000;
    const char * ch = text;
    while (*ch != '\0') {
        *address = *ch;
        ++ch;
    }
}

相关问题