开启arm汇编学习。
环境介绍:使用的是TI TDA4平台。该平台是基于ARMV8架构,64bit的CPU。
先上一段C代码:
#include <stdio.h>
int main(void)
{
char ch1 = 10;
char ch2 = 11;
ch2 = ch1;
unsigned short sh1 = 20;
unsigned short sh2 = 22;
sh1 = sh2;
int num1 = 30;
int num2 = 33;
num1 = num2;
long long lnum1 = 40;
long long lnum2 = 44;
lnum1 = lnum2;
return 0;
}
把C代码编译生成汇编代码:
.arch armv8-a
.file "main.c"
.text
.align 2
.global main
.type main, %function
main:
.LFB0:
.cfi_startproc
sub sp, sp, #32
.cfi_def_cfa_offset 32
mov w0, 10
strb w0, [sp, 31]
mov w0, 11
strb w0, [sp, 30]
ldrb w0, [sp, 31]
strb w0, [sp, 30]
mov w0, 20
strh w0, [sp, 28]
mov w0, 22
strh w0, [sp, 26]
ldrh w0, [sp, 26]
strh w0, [sp, 28]
mov w0, 30
str w0, [sp, 20]
mov w0, 33
str w0, [sp, 16]
ldr w0, [sp, 16]
str w0, [sp, 20]
mov x0, 40
str x0, [sp, 8]
mov x0, 44
str x0, [sp]
ldr x0, [sp]
str x0, [sp, 8]
mov w0, 0
add sp, sp, 32
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU Toolchain for the A-profile Architecture 9.2-2019.12 (arm-9.10)) 9.2.1 20191025"
.section .note.GNU-stack,"",@progbits
C代码编译成汇编代码的脚本:
#!/bin/sh
export PATH=${PATH}:/home/CN/qidong.liu/ti/tda4/server/tda4vl/camera_ok/rtos/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin
aarch64-none-linux-gnu-gcc -E main.c -o main.i
aarch64-none-linux-gnu-gcc -S main.i -o main.S
对生成的汇编代码做一个简单的解释:
main:
.LFB0:
.cfi_startproc
sub sp, sp, #32 //申请32字节的栈空间。注:本验证发现,栈的大小似乎总是16的整数倍。
.cfi_def_cfa_offset 32
mov w0, 10 //将10保存到寄存器w0里面
strb w0, [sp, 31] //将寄存器w0的内容,保存到栈偏移31字节处。这里是以字节为操作单位。strb的b,表示byte
mov w0, 11
strb w0, [sp, 30]
ldrb w0, [sp, 31] //将栈偏移31字节处的内容,加载到寄存器w0里面
strb w0, [sp, 30]
mov w0, 20
strh w0, [sp, 28] //将寄存器w0的内容,保存到栈偏移28字节处。这里是以半字为操作单位。strh的h,表示half word
mov w0, 22
strh w0, [sp, 26]
ldrh w0, [sp, 26] //将栈偏移26字节处的内容加载到w0寄存器。这里是以半字为操作单位。
strh w0, [sp, 28]
mov w0, 30
str w0, [sp, 20] //将寄存器w0的内容,保存到栈偏移20字节处。这里是以字为操作单位。str的操作单位和寄存器有关 这里的单位为字
mov w0, 33
str w0, [sp, 16]
ldr w0, [sp, 16] //将栈偏移16字节处的内容加载到w0寄存器。这里是以字为操作单位。ldr的操作单位和寄存器有关 这里的单位为字
str w0, [sp, 20]
mov x0, 40 //x0和w0寄存器为同一个寄存器,w0寄存器为x0寄存器的低32位
str x0, [sp, 8] //将寄存器x0的内容保存到栈偏移8字节处。这里是以双字为操作单位。str的操作单位和寄存器有关 这里的单位为双字
mov x0, 44
str x0, [sp]
ldr x0, [sp] //将栈偏移0字节处的内容加载到x0寄存器。这里是以双字为操作单位。ldr的操作单位和寄存器有关 这里的单位为双字
str x0, [sp, 8]
mov w0, 0
add sp, sp, 32
.cfi_def_cfa_offset 0
ret
.cfi_endproc
上面对部分指令做了一个简单的解释。下面进行一点扩展。
mov指令:
mov x0, 123
将立即数123保存到寄存器x0里面
mov x1, x2
将寄存器x2里面的内容保存到寄存器x1里面
注:ARM架构中,内存单元上的数据不允许被直接操作。因此mov指令作用于寄存器与立即数直接的操作。
ldr指令:将内存上的内容加载到寄存器里面
ldr x0, [x1]
将x1寄存器里面的内容作为内存地址,把该内存地址的值加载到x0寄存器里面
注:ldr指令还有其他一些类型,如:ldrb、ldrh
ldrb:操作单位为字节
ldrh:操作单位为半字
ldr:操作单位和寄存器类型有关。w0这一类的寄存器操作单位就是字。x0这一类的寄存器操作单位就是双字。
str指令:将寄存器的内容保存到内存上面去
str x0, [x1]
将x0寄存器里面的内容保存到x1寄存器里面的值的对应的内存空间。
举例:str x0, [x1] //x0=123 x1=456789
将123保存到内存地址为456789的地址处。
str x0, [x1], #8
将x0寄存器里面的内容保存到x1寄存器里面的值的对应的内存空间。同时x1寄存器的内容更新为x1+8
str也有一些其他类似指令:strb、strh。这些指令的操作单位和ldr那一类指令类似
add指令:加法指令
add x0, x1, x2
x0=x1+x2
add x0, x1, #123
x0=x1+123
注:add指令操作的对象也是寄存器和立即数。似乎不能直接操作内存。
sub指令:减法指令
sub x0, x1, x2
x0=x1-x2
sub x0, x1, #123
x0=x1-123
注:sub指令操作的对象也是寄存器和立即数。似乎不能直接操作内存。