文章目录
前言
使用不同的编译器,指导编译器编译链接的文件lds稍许不一样,现在分arm-linux-gnueabi-gcc、IAR、Keil分开记录
提示:以下是本篇文章正文内容,下面案例可供参考
一、GCC
语法
marco
macro
为定义一个宏,.macro
为首,.endm
为尾,。
在其他文件调用此宏定义,即可用定义的代码段替换宏。下面是一个宏定义示例。
.macro CPWAIT reg
mrc p15, 0, \reg, c2, c0, 0
mov \reg, \reg
sub pc, pc, #4
.endm
rept
在汇编语言中,rept
和 endr
通常是用于创建重复的代码块(重复宏)。rept
指令用于开始一个重复块,然后可以在其中重复执行一段代码,直到遇到 endr
指令为止。这样可以方便地生成重复的汇编代码。
例如,以下是一个使用 rept
和 endr
的示例:
.section .mmutable, "a"
mmutable:
.align 14
/* 0x00000000 - 0xffe00000 : 1:1, uncached mapping */
.set __base, 0
.rept 0xfff
.word (__base << 20) | 0xc12
.set __base, __base + 1
.endr
/* 0xfff00000 : 1:1, cached mapping */
.word (0xfff << 20) | 0x1c1e
在上面的示例中,rept 0xfff
表示下面的代码块将被重复执行 0xfff 次,然后 endr
表示重复块的结束。因此,其中的代码会被复制和粘贴 0xfff 次,作用就是建立了一张mmu
表。
section
.section
是汇编语言中的一个指令,通常用于将汇编代码分组为不同的节(sections)。在汇编器中,节是指代码或数据的逻辑分区,用于将不同类型的内容分开存放。每个节可以包含代码、数据、常量等不同类型的信息。
在大多数汇编语言中,.section
指令用于创建不同类型的节,并且可以指定节的属性和名称。具体的语法和属性取决于所使用的汇编器和体系结构。
以下是一个示例,展示了在某些汇编语言中使用 .section
的方式:
.section .data
data_value: .word 42
.section .text
.global main
main:
mov r0, r1
bx lr
在这个示例中,.section .data
表示接下来的代码将位于名为 .data
的节中,这通常用于存放数据。.section .text
表示接下来的代码将位于名为 .text
的节中,这通常用于存放指令代码。结合lds
文件,便可以把section存放在指定区域了。
.extern
.extern用于在汇编文件中声明外部变量
.set
在GCC中,.set
汇编伪指令用于为汇编代码中的符号(如标签或变量)分配一个特定的数值。这通常用于在汇编代码中定义常数或指定地址。(ps:相当于c语言中的宏定义#define
)
.set BUFFER_SIZE, 100
以上是一个示例,假设你要在汇编代码中定义一个名为 BUFFER_SIZE 的常数,其大小为 100。然后,在你的汇编代码中,你可以使用 BUFFER_SIZE 而不必在每个地方使用数字 100。
.align
//.align 指令的一般形式是
.align <alignment>
// 对齐到4字节边界
.align 2
// 对齐到8字节边界
.align 3
// 对齐到16字节边界
.align 4
其中, 表示要对齐的边界,通常是2的幂。例如,.align 4 将当前位置对齐到4字节边界,.align 8 将当前位置对齐到8字节边界,以此类推。
.org
.org
是一种汇编伪指令,通常用于设置程序计数器(PC)或地址计数器(AC),以指示汇编器生成的代码或数据应该放置在特定的内存地址或位置。这在编写汇编程序时非常有用,因为它允许你控制生成的二进制代码的位置。
例如,假设你正在编写一个汇编程序,想要将一段代码放置在内存的地址0x1000处,你可以使用以下指令:
.org 0x1000
//后续跟随代码块,即生成在0x1000位置
专业名词
Post-index
Post-index
是一种内存地址计算方式,它表示在访问内存后,将索引添加到基地址。这是在ARM汇编语言中的一种寻址模式,通常与加载(Load)和存储(Store)指令一起使用。
以下是一个示例,使用 Post-indexing 进行加载操作的 ARM 汇编代码:
ldr x0, [x1], #4
在这个例子中:
ldr
是 Load 指令,用于从内存中加载数据到寄存器。
x0
是目标寄存器,用于存储加载的数据。
[x1], #4
是 Post-index
寻址模式。它表示从存储器地址 x1 处加载数据到 x0,然后将 x1 的值增加 4 个字节(32位)。
这个指令的效果是,首先加载 x1 指向的内存位置的数据到 x0,然后将 x1 的值增加 4,使其指向下一个数据元素。(ps :类似于c语言中的i++中的++)
Pre-Index
Pre-indexing
是一种内存地址计算方式,与 Post-indexing 相对,它表示在访问内存之前将索引添加到基地址。在ARM汇编语言中,Pre-indexing通常与加载(Load)和存储(Store)指令一起使用。
以下是一个使用 Pre-indexing 进行加载操作的 ARM 汇编代码示例:
ldr x0, [x1, #4]!
在这个例子中:
ldr 是 Load 指令,用于从内存中加载数据到寄存器。
x0 是目标寄存器,用于存储加载的数据。
[x1, #4]! 是 Pre-indexing 寻址模式。它表示将 x1 的值增加 4 个字节(32位)后,再从存储器地址 x1 处加载数据到 x0。
这个指令的效果是,在访问内存之前,将 x1 的值增加 4,然后加载这个新地址处的数据到 x0。
二、IAR
汇编
关键字 | 详解 |
---|---|
section | section关键字一般放在s文件首部,它的一般形式如下SECTION .vectors:CODE:NOROOT(2),section代表一个段的开始,.vector代表段名,后面跟的是属性,具体含义要参考文档 |
PUBLIC | ![]() |
EXTERN | 声明一个外部函数,然后可在本文件中调用,如在c文件中实现了一个PrintHello,可在本文件中调用 |
扩展内联汇编
//内敛汇编基本格式
asm ( assembler template
: output operands /*在MOV R0,R1中,R1->R0,R1为output*/
: input operands /*在MOV R0,R1中,R1->R0,R0为input*/
: list of clobbered registers /* optional */
);
output operands和input operands分别代表输入输出函数
clobbered registers指明修改的寄存器或者内存,如果某个指令改变了某个寄存器的值,我们就必须在asm中第三个冒号后的Clobber List中标示出该寄存器。为的是通知GCC,让其不再假定之前存入这些寄存器中的值依然合法。输入输出寄存器不用放Clobber List中,因为GCC能知道asm将使用这些寄存器。(因为它们已经显式被指定输入输出标出在输入输出部分) 。其他使用到的寄存器,无论是显示还是隐式的使用,必须在clobbered list中标明。
//具体代码
void print_r0r1(int r0, int r1,int r2,int r3)
{
int value,addr;
fmsh_print("r0 0x%x r1 0x%x\n",r0,r1);
asm(
"MOV %[op1], SP\n"
"LDR %[op2], [SP]\n"
:
:[op1]"r"(addr),[op2]"r"(value)
:
);
fmsh_print("SP addr 0x%x value 0x%x\n",addr,value);
}
// 32 位写操作
void write32(uint32_t value, volatile uint32_t *address) {
__asm__ __volatile__(
"str %w[value], [%[addr]]"
: : [value] "r" (value), [addr] "r" (address)
: "memory"
);
}
// 32 位读操作
uint32_t read32(volatile uint32_t *address) {
uint32_t result;
__asm__ __volatile__(
"ldr %w[result], [%[addr]]"
: [result] "=r" (result)
: [addr] "r" (address)
: "memory"
);
return result;
}
IAR汇编帮助文档
在IAR IDE内,如下图操作,可获得IAR提供汇编指导文档
三、KEIL
四、链接脚本
https://sourceware.org/binutils/docs/ld/Scripts.html#Scripts
相关参考资料:
https://www.jianshu.com/p/1782e14a0766