openEuler汇编语言(1)
一、ARM 汇编语言
在之前的连载中我们介绍了 ARMv8-A 架构的指令集和寄存器。指令集就如同高级程序设计语言中的语句,寄存器就如同语句中的变量。然而只了解语句也无法编写出完整的程序,我们还需要了解语句的组织形式。就像在高级语言中一样,ARM 汇编语言也可以有条件判断、分支、循环和函数等组织形式。想要了解常用汇编指令助记符的含义,可以查看第15期《ARM 体系结构基础(1)》。
下图展示了一个ARM汇编语言中使用条件判断的例子[1]:
在这段代码中,r0 寄存器首先保存了立即数2,然后 cmp 指令将 r0 中保存的值与立即数3进行比较。由于 r0 中的值小于立即数3,因此CPSR寄存器中的 N 位被置为1。接着 addlt 指令被执行,因为条件码lt所需的条件( V!=N,V为 CPSR 中的溢出位)满足了。寄存器 r0 中的值自加一从而变成3。这样在第二次运行 cmp 指令时 N 位就变成0,所以第二个 addlt 指令就不会执行。上面这段代码实现了程序设计中常见的 IF 判断。
一般程序设计中还可以使用分支(Branch)的结构,下面这段ARM汇编语言展示了一个分支的例子[1]:
在这个例子中,程序首先比较寄存器 r1 和寄存器 r2 中的值,如果 r1 中的值小于 r2 中的值,那么跳转到 r1_lower 程序段,否则执行 blt 指令下面的 mov 指令。
借助分支跳转指令还可以完成循环功能,下图展示了一个用汇编代码实现循环的例子[1]:
在循环的开始程序将 r0 中的值和立即数4比较,如果 r0 中的值与4相等则跳转到 end 程序段,否则将 r0 中的值自加一并继续循环,因为 r0 中的初始值为0因此循环要运行5次,最后一次跳转到 end 程序段。
函数是高级语言编程时常用的语句组织形式,用 ARM 汇编语言也可以实现函数式编程,下图展示了一个这样的例子[2]:
在这个例子中 main 函数可以被分为三个部分:
前段:保存函数主体执行前的状态,创建函数主体执行的环境;
主体:执行函数主要功能;
末段:恢复函数调用之前的状态。
下图[2]为 main 函数的前段,在这段代码中寄存器 r11 里面保存着函数栈的地址。前段首先通过 push 指令保存栈指针和寄存器lr,然后通过 add 指令将栈底的地址存入寄存器 r11 中,最后用 sub 指令为栈分配空间。
main 函数的中段主要调用了 max 函数,max 函数比较了寄存器 r0 和寄存器 r1 中值的大小,且如果寄存器 r0 中的值小于寄存器 r1 中的值,那么将 r1 中的值保存到 r0 中。函数 max 的参数是通过寄存器传递的,若参数过多也可以用栈传递。下图显示了 main 函数的主体[2]:
main 函数的末段恢复了将保存在 r11 的栈指针恢复到了 sp 寄存器中,并将在函数开头保存的 r11 值恢复。在函数开头保存的 lr 寄存器中的内容则被恢复到 pc 寄存器中,从而通过该地址返回了调用 main 函数的地方。下图展示了 main 函数的后段[2]:
max 函数也可分为前段、主体和后段,其中前段和后段的功能与 main 函数是类似的。不同的是 max 函数的前段没有保存 lr 寄存器,这是因为 main 函数需要调用其他函数,lr 寄存器中的内容可能改变而 max 函数不需要调用其他函数。我们可以注意到 main 函数调用 max 函数时使用了 bl 指令,这个指令会将 bl 指令下一条指令的地址存入 lr 寄存器,用于调用返回。max 函数返回 main 函数时使用了 bx 指令,这条指令可以在 ARM 模式和 Thumb 模式间进行切换,从而实现不同指令模式的函数进行互相调用。
二、结语
本期我们介绍了一些 ARM 汇编语言的编程方法,下一期中我们分析一下 ARM 内嵌汇编器。
- 点赞
- 收藏
- 关注作者
评论(0)