我有下一个糟糕的代码:
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
)。我尝试了很多变体,但都成功失败了。
此代码是为 aarch64 (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 和 aarch64-elf-binutils 编译器和链接器来构建 kernel.elf
。
首先是解决方案:
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!");
}
它是如何工作的?
0x9000000
。
address
和
text
变量加载到
X10
和
X11
寄存器中(对于将使用哪个寄存器没有严格的规则)。注意
text
是一个指针,所以实际上我们将字符串的地址加载到寄存器中,而不是一个值。
X9
寄存器中,由
X11
寄存器中的地址定位的下一个字符串字符。
0x9000000
。
X11
寄存器的值,换句话说,我们只是将指针切换到字符串中的下一个字符。
PRINT_STR
寄存器包含非零值(C 字符串以
W9
结尾),则跳转到
\0
。