当前位置: 首页 > 科技观察

详解汇编语言B和LDR指令与相对跳转和绝对跳转的关系

时间:2023-03-19 21:15:32 科技观察

详细解释汇编语言B和LDR指令的关系,相对跳转和绝对跳转是顺序执行的。跳转执行:当指令执行到当前位置时,跳转到其他位置执行。比如在main函数中调用其他函数就是典型的跳转执行。跳转分为绝对跳转和相对跳转。绝对跳转:直接跳转到一个固定的、真实的地址。相对跳转:相对于当前pc值的跳转,跳转到pc+offset的地址。理解了上面的概念之后,我们就知道为什么会有相对跳转和绝对跳转了。各种指令相互配合,使CPU具有更高的处理效率。正是因为顺序和跳转指令,我们的cpu才能处理各种复杂的计算。程序中是否可以只有相对跳转/绝对跳转?答案肯定不是。让我们用一个例子来分析它。指令编号|指令函数--------|------|-----说明1|顺序执行指令2|顺序执行指令3|相对跳转到指令5指令4|顺序执行指令5|顺序执行指令6|绝对跳转到指令8指令7|顺序执行指令8|顺序执行假设程序放在0x00000000处开始执行,编译链接后的结果为:指令地址|指令编号|指令函数|下一条指令地址--------|------|------|------|-----0x00000000|顺序执行|顺序执行|当前地址+40x00000004|顺序执行|顺序执行|当前地址+40x00000008|跳转到指令5|跳转到指令5|当前地址+80x0000000C|顺序执行|顺序执行|当前地址+40x00000010|顺序执行|转到说明8|跳转到指令8|0xC000001C0x00000018|顺序执行|顺序执行|当前地址+40x0000001C|顺序执行|顺序执行|当前地址+4使用相对寻址的方法可以运行到指令6。在执行指令6时,也可以使用绝对寻址的方法从0xC0000014正确跳转到指令8所在的0xC00001C位置。这段代码运行正常。当这段代码放在0x00000000空间时,开始执行指令1,然后使用相对寻址方式运行到指令6,但是使用绝对寻址方式在执行指令6时从0x0000014跳转到0xC000001C,但是没有代码在0xC000001C空间,所以程序会跑掉。因此,当编译地址(加载地址)和运行地址相同时,绝对跳转和相对跳转都可以正确执行。例如,当程序存储在NORFLASH中时。但是当编译地址(加载地址)和运行地址不一样时,就会出现相对跳转的问题。例如,代码存储在NANDFLASH中。由于NANDFLASH无法运行代码,需要将代码重新定位到内部SRAM中。关于NANDFLASH和NORFLASH,可以看这篇文章S3C2440从NANDFlash启动和NORFLASH启动。B(BL)和LDR指令是如何执行的?我们分析下图中跳转代码中指令的具体执行过程。#ifndefCONFIG_SKIP_LOWLEVEL_INITblcpu_init_crit#endif??上述代码对应的反汇编代码如下:33f000ac:eb000017bl33f0011033f00110:33f00110:e3a00000movr0,#0;0x033f00114:ee070f17mcr15,0,r0,cr7,cr7,{0}当指令执行到33f000ac时,对应的机器码为eb000017(11101011000000000000000000010111),其中[31,28]的高四位为条件码,1110表示无条件执行。[25,27]位保留区,24位表示是否有返回值,1表示有返回值,即BL指令。[23,0]是指令的操作数,000000000000000000010111。按照下面的计算方法:将指令中的24位有符号补立即数扩展为32位(扩展其符号位),将原来的数字变为00000000000000000000000000010111。将这个数向左移动两位00000000000000000000001010000000变成00000000000000000000000001011100=0x0000005c将得到的值加到PC寄存器中得到目标地址。由于ARM是3级流水线,此时pc=33f000ac+8=33F000B4,pc=33F000B4+0x0000005c=33F00110等于图中cpu_init_crit的地址。在计算的过程中,我们总是使用PC的值。假设程序在地址0处执行,那么计算方法是一样的,当pc的值改变时,计算结果也会改变。所以BL的跳跃跟站位没有关系。B(BL)指令的格式如下图所示。28~31bts(cond)为条件码,表示该语句中是否存在大于、等于、非零等条件判断。这4个bts一共有16个状态,分别是:图为LDR指令的格式下面我们以下图中第一句为例分析下ldrpc,=call_board_init_f对应的反汇编代码如下:33f000d0:e59ff324ldrpc,[pc,#804];33f003fc33f003fc:33f000d4.word0x33f000d4.......33f000d4:33f000d4:e3a00000movr0,#0;0x0ldrpc,[8]这个,#8指令是一个伪指令,call_board_init_f的链接地址会存放在一个Fixedaddress中(在链接时确定),对于这条指令来说这个地址就是33f000d4。上面反汇编的ldrpc,=call_board_init_f变成了ldrpc,[pc,#804],因为ARM使用了流水线,所以在执行ldrpc的时候pc不在这里。[pc,#4]语句代码在这里,但是跑到了pc+8的地方,这段代码相当于pc=*(pc+804+8)=33f000d0+32C=33f003fc,所以会跳转到33f003fc地址取33f000d4,而33f000d4是代码段中的一个常量,不计算,不会随着程序的位置而改变,所以无论代码和pc如何改变,*(pc+804)的值都不会改变.这样绝对跳转中的固定地址就很好理解了。要跳转的地址的值在链接时已经确定并存储在一块内存中。在相对跳转的情况下,反汇编bl33f00110中的33f00110是按照pc计算的。当pc改变时,结果也会改变。所以称为相对跳转,与当前位置无关。B(BL)和LDR的跳跃范围是怎么规定的?B(BL)指令的格式如下图所示。BL指令的[23,0]位存放的是要跳转的相对地址,因为指令的地址必须是4字节对齐的,所以跳转地址的最低位必须为0,所以BL指令[23,0]bits保存的是省略最低2bts的地址。如果完成这2bits,BL指令可以代表一个26bits的正向跳转地址。在26bits中,需要用1bit来表示是向前跳还是向后跳,那么剩下的25bits可以表示32MBts的范围,225=32M。因此,B(BL)指令的跳转范围为-32MBytes~+32MBytes。LDR指令的格式如下图所示。图中LDR的跳转范围的计算方法与B指令类似,其中Rn和Address_mode共同构成了第二个操作数的内存地址,可以用Address_mode的9种格式来表示,直到是偏移地址的范围大小,即212=4K。(不明白的可以对比一下ldrpc,[pc,#804]和Address_mode这九种格式,很明显Address_mode就是当前地址的偏移范围)