第三章 一些ARM指令
今天我们开始学习一些ARM指令,不过之前有些事情你要记住(和执行速度有关):
·对齐你的数据。使用.align命令,比如.align 4,.align 2。如果你使用了没有对齐的地址,CPU会做一些汗死人的操作绝对把速度降下来。
·不访问内存的指令总是快一些,最大限度地使用寄存器,不要从内存载入数据。用MOV和ADD指令载入32位立即数,不要用LDR指令。
指令LDR
指令LDR还蛮多用途的,尽管它主要的功能是从内存中load(读取)一个值。它有多种寻址模式,有些我都不太清楚...你去翻手册吧。我挺走运地在一家特价书店里淘到一本《ARM Architecture Reference Manual 2nd Edition》,GbaDev.org上也有一些手册。LDR最简单的形式是从ROM的“池”(或者你运行程序的什么地方)中读取一个值。看起来像是这样:
ldr r0,=0x32bitnumber(0x32bitnumber是一个32位数,比如0x403, 0xCAFEBABE, 0xBAD00FED, 0x6000000...).
这个32位数是保存在“池”中的,汇编器会自动处理。一般来说,你要明确地在每一个子程序后面(或者文件末尾,如果程序不大的话)使用.ltorg指令告诉汇编器在哪里放置“池”。
(译注,这里恐怕还是不甚明了,放一张图给点感性认识)
下一个形式是从寄存器寻址,如:
ldr r0, [r1]
它会将r1所指的地址处的32位数载入r0。(类似于C里面的r0 = *r1;)
这里提一个语法点,如果寄存器外面有个方括号,代表的是寄存器指向的地址处的内容,而不是寄存器中的值本身。
下面是一些其他你需要了解的寻址方式:(我一直使用r0,其实随便哪个寄存器都能用。寄存器也能用在#offset处)
[r0, #offset] 地址是r0+#offset。这个叫做Pre-indexed。
如果你在闭括号后面加个!,那么地址还要写入r0。这个叫做“写回”,在循环的时候很有用,这样你就不用写另外一条更新地址指针的指令了。
[r0], #offset 地址是r0,然后r0+#offset写入r0。这个叫做Post-Indexed。
写回是自动进行的。
这些只是寻址模式,完整的指令应该是“ldr r1, address_mode”。你可以随便写一些“ldr r1, [r0, r2]”、“ldr r3, [r2], #4”之类的东西。第一条的意思是“r1 = *(r0 + r2)”,第二条的意思是“r3 = *r2; r2 += 4;”。ARM指令很强大吧?相信我,你其实还没看到什么呢!
指令STR
指令STR用起来和LDR几乎一样(除了 ,=0x32bitnumber形式)。唯一的区别在于:它把一个数值store(写入)到内存中。这个很简单,就不说了。
指令MOV
指令MOV用于寄存器之间移动数据,它也包括一个位移指令。语法如下:(reg1,reg2可以是任何寄存器,reg2的值不会变化)
mov reg1, reg2 {,LSL/LSR/ASR/ROR/RRX #} 把reg2的值复制到reg1,如果有一个可选的位移参数就会对reg2位移。如:mov r0, r1, LSL #2,就是r0 = r1 << 2。
mov reg1, #0xnumber 将一个立即数存储到reg1中,不过这个数是有限制的,它必须要能够由一个byte大的数通过位移得到。
还能做一些如mov r0, r0之类的操作来当做NOP(不做任何事情),如果你想要位移一个寄存器值,指令MOV就是你想要的:
mov r0, r0, LSL #3,r0 = r0 << 3;ARM指令集里面没有单独的位移指令。
很多指令的位移的语法是一样的(除了算术指令,ADD,SUB,EOR,ORR,AND,BIC之类的),都和MOV一样,所以之后我不会再提位移语法了。
我希望这些东西你都掌握了,这些指令都很重要。接下来,我们来尝试从RAM中读取比较小的数,然后学习分支指令,最后分析一下第二章的程序。