Keil5 内联汇编
问题
最近工程使用协处理器,需要用到内联汇编。
keil5使用GCC风格内联汇编时,会在返回R1
寄存器值之前,对R1
做压栈处理,导致得不到正确的返回值。因此必须按照Keil指定格式对返回值
做定义。
百度了一下的,都是各种离谱答案,所以做个笔记.
其实官网就有,用法如下,测试已通过。
例子
编译器提供了内联汇编器,使您能够在 C 或 C++ 源代码中编写汇编代码,例如访问 C 或 C++ 不可用的目标处理器功能。
__asm 关键字可以使用 GNU 内联汇编语法将内联汇编代码合并到一个函数中。 例如:
#include <stdio.h>
int add(int i, int j)
{
int res = 0;
__asm ("ADD %[result], %[input_i], %[input_j]"
: [result] "=r" (res)
: [input_i] "r" (i), [input_j] "r" (j)
);
return res;
}
int main(void)
{
int a = 1;
int b = 2;
int c = 0;
c = add(a,b);
printf("Result of %d + %d = %d\n", a, b, c);
}
这个例子是真的很经典,a
是输入,c
是输出,多个输入按上面例子用冒号打头新起一行完事。
内联汇编器不支持以 armasm 汇编器语法编写的旧式汇编代码。 有关将 armasm 语法汇编代码迁移到 GNU 语法的更多信息,请参阅迁移和兼容性指南
。
说明
__asm
内联汇编语句的一般形式是:
__asm [volatile] (code); /* Basic inline assembly syntax */
/* Extended inline assembly syntax */
__asm [volatile] (code_template
: output_operand_list
[: input_operand_list
[: clobbered_register_list]]
);
对具有处理器副作用的汇编指令使用 volatile
限定符,编译器可能不知道这些副作用。 volatile
限定符禁用某些编译器优化,否则可能会导致编译器删除代码块。 volatile
限定符是可选的,但您应该考虑在汇编代码块周围使用它,以确保编译器在使用 -O1
或更高版本进行编译时不会删除它们。
code
是汇编指令,例如“ADD R0, R1, R2”
。 code_template
是汇编指令的模板,例如“ADD %[result], %[input_i], %[input_j]”
。
如果您指定的是 code_template
而不是代码,那么您必须在指定可选的 input_operand_list
和 clobbered_register_list
之前指定 output_operand_list
。
output_operand_list
是输出操作数列表,以逗号分隔。每个操作数由方括号中的符号名称、约束字符串和括号中的 C 表达式组成。在这个例子中,有一个输出操作数:[result] "=r" (res)
。该列表可以为空。例如:
__asm ("ADD R0, %[input_i], %[input_j]"
: /* This is an empty output operand list */
: [input_i] "r" (i), [input_j] "r" (j)
);
input_operand_list
是输入操作数的可选列表,以逗号分隔。 输入操作数使用与输出操作数相同的语法。 在这个例子中,有两个输入操作数:[input_i] "r" (i), [input_j] "r" (j)
。 该列表可以为空。
clobbered_register_list
是一个逗号分隔的字符串列表。 每个字符串都是汇编代码可能修改的寄存器的名称,但最终值并不重要。 为了防止编译器在内联汇编字符串中为模板字符串使用寄存器,请将寄存器添加到 clobber 列表中。
例如,如果寄存器保存一个临时值,请将其包含在 clobber 列表中。 编译器避免使用此列表中的寄存器作为输入或输出操作数,或在执行汇编代码时使用它来存储另一个值。
该列表可以为空。 除了寄存器之外,该列表还可以包含特殊参数:
- “cc”
该指令影响条件代码标志。 - “memory”
该指令访问未知的内存地址。
clobbered_register_list
中的寄存器必须使用小写字母而不是大写字母。 带有 clobbered_register_list
的示例指令是:
__asm ("ADD R0, %[input_i], %[input_j]"
: /* This is an empty output operand list */
: [input_i] "r" (i), [input_j] "r" (j)
: "r5","r6","cc","memory" /*Use "r5" instead of "R5" */
);
定义符号和标签
您可以使用内联汇编来定义符号。 例如:
__asm (".global __use_no_semihosting\n\t");
要定义标签,请在标签名称后使用 :
。 例如:
__asm ("my_label:\n\t");
多条指令
您可以在同一个__asm
语句中编写多个指令。 此示例显示了用一个 __asm
语句为 Arm®v8‑M 主线架构编写的中断处理程序。
void HardFault_Handler(void)
{
asm (
"TST LR, #0x40\n\t"
"BEQ from_nonsecure\n\t"
"from_secure:\n\t"
"TST LR, #0x04\n\t"
"ITE EQ\n\t"
"MRSEQ R0, MSP\n\t"
"MRSNE R0, PSP\n\t"
"B hard_fault_handler_c\n\t"
"from_nonsecure:\n\t"
"MRS R0, CONTROL_NS\n\t"
"TST R0, #2\n\t"
"ITE EQ\n\t"
"MRSEQ R0, MSP_NS\n\t"
"MRSNE R0, PSP_NS\n\t"
"B hard_fault_handler_c\n\t"
);
}
将上面的处理程序代码复制到 file.c
中,然后您可以使用以下命令编译它:
armclang --target=arm-arm-none-eabi -march=armv8-m.main -S file.c -o file.s
嵌入式汇编
您可以使用 __attribute__((naked))
编写嵌入式程序集。 有关更多信息,请参阅 Arm 编译器参考指南中的 __attribute__((naked))
。
本文实际来自于Keil官方教程
下一篇准备写 《尽可能简单的将VSCODE变成IDE》