代码
首先回顾代码
/*
*点亮LED:GPF4
*/
.text //表明它是代码段
.global _start
_start:
/*配置GPF4为输出引脚
*把0x100写到地址0x56000050上,熄灭led
*/
ldr r1, =0x56000050 /*将这个地址存放到r1中*/
ldr r0, =0x100 /*或者使用 mov r0, #0x100 将0x100放入r0 */
str r0, [r1] /*将r0的值写入到r1的地址中*/
/*设置GPF4输出高电平
*把0x00写到地址0x56000054上,点亮led
*/
ldr r1, =0x56000054 /*将这个地址存放到r1中*/
ldr r0, =0 /*或者使用 mov r0, #0x100 将0放入r0 */
str r0, [r1] /*将r0的值写入到r1的地址中*/
halt: /*假设这个程序只有十几个字节*/
b halt /*这十几个字节后的内容是不确定的,所以要让他在这里死循环*/
我们使用的ldr是伪指令,将地址存放在r1和r0中
伪指令,就是假的arm指令
它对应哪些真正的arm指令呢
反汇编
要想知道这个伪指令对应的真正的arm指令
我们就需要将elf文件反汇编
arm-linux-objdump -D led.elf > led.dis
输入指令反汇编
将生成的dis文件传到windows,并使用notepad打开
对比
左侧是我们使用的伪指令
可以看到真实的arm指令,是将这个地址放在内存中,使用pc读取内存中1c和20的地址给r1
第二条指令直接使用mov,将地址存放在r0当中
解析
2440中有CPU,CPU中有r0到r15,16个寄存器。这里是如何使用PC值来表示寄存器呢
-
pc——Program Counter(程序计数器)
当你把一个地址写道R15也就是程序计数器时,程序就跳到那个地址中去
地址为当前指令的地址+8 -
lr——Link Register(返回地址)
使用函数调用的时候,执行完毕要跳回原来的地方。R14/lr就是用来保存原来的地址 -
st——Stack Pointer(栈指针)
PC是什么?
当前指令的地址使0,PC的地址是当前指令加8
因为ARM系统里面,为了使执行效率更加高,CPU是以流水线的方式执行的
- 流水线:当前执行地址A的指令;已经在对地址A+4的指令进行译码;已经在读取地址A+8(PC)的指令。
对于上面的图
r1的值,等于[pc+20]这个地址的值,等于[8+20],28对应16进制就是0x1c
即,r1的值,就是0x1c地址中存放的值,就是0x5600,0050
第三条指令,将0x100写入r1对应的内存,就是把0x100写入0x5600,0050
第四条指令,r1等于[pc+12]=[0xc+8+12]=[32]=[0x20]
即,去0x20这个地址读取值,存放到r1里面,就是0x5600,0054
总结
在程序里面,寄存器和内存没有本质的差别。CPU都把它们当作内存来使用
只是这些内存比较特殊,配置他们能控制一些引脚
dis和bin对比
dis是反汇编文件
我们知道,bin文件中都是二进制的机器码,那么我们来对比以下dis的汇编码和bin中的机器码
可以看到是完全一致的
反汇编dis文件中的汇编码的源头们就是我们编写的代码,那些伪指令和汇编码
步骤如下
- 编译器将伪指令转化为真正的汇编码,对于真正的汇编码保持不变
- 转换完成后,将所有的汇编码转换为机器码,然后存为bin文件。这些bin文件就可以直接烧写到单板上了。