1..align
.align的效果在于对指令或许数据的寄存地址进行对齐,有些CPU架构要求固定的指令长度而且寄存地址相关于2的幂指数圆整,不然程序无法正常运转,比方ARM;有些体系却不需求,假如不遵照地址的圆整规矩,程序仍然能够正确履行,仅仅降低了一些履行功率,比方i386。.align的效果规模只限于紧跟它的那条指令或许数据,而接下来的指令或许数据的地址由上一条指令的地址和其长度决议。
ARM汇编器并不直接运用.align供给的参数作为对齐方针,而是运用2^n的值,比方这儿的参数为4,那么圆整方针为2^4 = 16。这也便是为什么在ARM渠道的Uboot或许Linux内核汇编中会呈现.align 5的根本原因。.align此刻的取值规模为0-15,当取值为0,2或许不供给参数时均圆整于4。假如测验运用大于15的值,将会得到编译器的err。
在指令呈现非对齐状况下,刺进.align伪指令,关于32bit的ARM会进行4byte的指令对齐。
2..rept
.rept和.endr之间的句子count次。
3..text
几个常用的段代号,根本上与编译器/处理器都没有无关系(FLAT形式):
.text- 代码段
.const – 只读数据段(有些编译器不运用此段,将只读数据并入.data段)
.data- 读写数据段
.bss – 堆
4..extern
“.extern”界说一个外部符号(能够是变量也能够是函数。
5..global
“.global”将本文件中的某个程序标号界说为大局的。
6..word
.word expression便是在当时方位放一个word型的值,这个值便是expression。
相当于用.word界说了一个16bit的数据。
举例来说,
_rWTCON:
.word 0x15300000
便是在当时地址,即_rWTCON处放一个值0x15300000
7.更多伪指令
http://www.byywee.com/page/M0/S774/774183.html
8.条件码表
条件码助记符 |
标志 |
意义 |
EQ |
Z=1 |
持平 |
NE |
Z=0 |
不持平 |
CS/HS |
C=1 |
无符号数大于或等于 |
CC/LO |
C=0 |
无符号数小于 |
MI |
N=1 |
负数 |
PL |
N=0 |
正数 |
VS |
V=1 |
溢出 |
VC |
V=0 |
没有溢出 |
HI |
C=1,Z=0 |
无符号数大于 |
LS |
C=0,Z=1 |
无符号数小于或等于 |
GE |
N=V |
带符号数大于或等于 |
LT |
N!=V |
带符号数小于 |
GT |
Z=0,N=V |
带符号数大于 |
LE |
Z=1,N!=V |
带符号数小于或等于 |
AL |
任何无条件履行(指令默许条件) |
9.ldr
伪指令LDR
大规模的地址读取伪指令.LDR伪指令用于加载32位的当即数或一个地址值到指定寄存器.在汇编编译源程序时,LDR伪指令被编译器替换成一条适宜的指令.若加载的常数未超出MOV或MVN的规模,则运用MOV或MVN指令替代该LDR伪指令,不然汇编器将常量放入字池,并运用一条程序相对偏移的LDR指令从文字池读出常量.LDR伪指令格局如下:
LDR{cond} register,=expr/label_expr
其间register加载的方针寄存器
expr 32位当即数.
label_expr依据PC的地址表达式或外部表达式.
LDR/STR指令用于对内存变量的拜访,内存缓冲区数据的拜访、查表、外围部件的操控操作等等,若运用LDR指令加载数据到PC寄存器,则完结程序跳转功用,这样也就完结了程序散转。
ldr r1, [r2, #4] /*将地址为r2+4的内存单元数据读取到r1中*/
ldr r1,[r2] /*将地址为r2的内存单元数据读取到r1中*/
ldr r1,[r2], #4/*将地址为r2的内存单元数据读取到r1中,然后r2=r2+4*/
str r1 ,[r2, #4]/*将r1的数据保存到地址为r2+4的内存单元中*/
str r1, [r2]/*将r1的数据保存到地址为r2的内存单元中。*/
str r1, [r2],#4/*将r1的数据保存到地址为r2的内存单元,然后r2= r2+4*/
ldrb:8bit=>1byte
ldrh:16bit=>2byte
LDR R0,LED_TAB
LDR R1, =LED_TAB
LED_TAB: .work 0x12345678
R0的值是0x12345678,R1的值是LED_TAB标号值,便是0x12345678在内存中寄存的地址
10.adr
转自:http://coon.blogbus.com/logs/2738861.html
ldr r0, _start
adr r0, _start
ldr r0, =_start
nop
mov pc, lr
_start:
nop
编译的时分设置 R0 为 0x0c008000
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
0c008000 <_start-0x14>:
c008000: e59f000c ldr r0, [pc, #12] ; c008014 <_start>
c008004: e28f0008 add r0, pc, #8 ; 0x8
c008008: e59f0008 ldr r0, [pc, #8] ; c008018 <_start+0x4>
c00800c: e1a00000 nop (mov r0,r0)
c008010: e1a0f00e mov pc, lr
0c008014 <_start>:
c008014: e1a00000 nop (mov r0,r0)
c008018: 0c008014 stceq 0, cr8, [r0], -#80
剖析:
ldr r0, _start
从内存地址 _start 的当地把值读入。履行这个后,r0 = 0xe1a00000
adr r0, _start
取得 _start 的地址到 r0,可是请看反编译的成果,它是与方位无关的。其实取得的时相对的方位。例如这段代码在 0x0c008000 运转,那么 adr r0, _start 得到 r0 = 0x0c008014;假如在地址 0 运转,便是 0x00000014 了。
ldr r0, =_start
这个取得标号 _start 的肯定地址。这个肯定地址是在 link 的时分确认的。看上去这仅仅一个指令,可是它要占用 2 个 32bit 的空间,一条是指令,另一条是 _start 的数据(由于在编译的时分不能确认 _start 的值,而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需求多出一个空间寄存 _start 的真实数据,在这儿便是 0x0c008014)。
因而能够看出,这个是肯定的寻址,不论这段代码在什么当地运转,它的成果都是 r0 = 0x0c008014
11.ldm
ldm和stm归于批量内存拜访指令,只用一条指令就能够读写多个数据。它们的格局如下:
ldm{cond}
stm{cond}
其间,
{cond}表明指令的履行条件,拜见下面的指令条件码。
表明地址改变形式,有以下几种办法:
1)ia(increment after): 传送后递加办法;
2)ib(increment before): 传送前递加办法;
3)da(decrement after): 传送后递减办法;
4)db(decrement before):传送前递减办法;
5)fd(full descending): 满递减仓库;
6)ed(empty descending):空递减仓库;
7)fa(full ascending): 满递加仓库;
8)ea(empty ascending): 空递加仓库;
{^}有两种意义:假如
指令中寄存器列表和内存单元的对应关系为:编号低的寄存器对应于内存中的低地址单元,编号高的寄存器对应于内存中高地址的单元。
ldmia r0!, {r3-r10} /*将基址寄存器r0开端的接连8个地址单元的值别离赋给r3,r4,r5,r6,r7,r8,r9,r10,留意的是r0指定的地址每次赋一次r0会加1,指向下一个地址单元*/
stmia r1!, {r3-r10} /*跟上面指令功用相反,将寄存器r3到r10的值顺次赋值给r1指定的地址单元,每次赋值一次r1就加1*/
仓库寻址:仓库是特定次第进行存取的存储区,仓库寻址时隐含的运用一个专门的寄存器(仓库指针),指向一块存储区域(仓库),存储器仓库可分为两种:
向上成长:向高地址方向成长,称为递加仓库。
向下成长:向低地址方向成长,称为递减仓库。
如此可结合出四种状况:
1、满递加:仓库经过增大存储器的地址向上添加,仓库指针指向内含有用数据项的最高地址,栈指针总是指向最终一个元素(最终入栈的数据),指令如 LDMFA,STMFA。
2、空递加:仓库经过增大存储器的地址向上添加,仓库指针指向仓库上的榜首个空方位,栈指针总是指向下一个即将放入数据的空方位,指令如 LDMEA,STMEA。
3、满递减:仓库经过减小存储器的地址向下添加,仓库指针指向内含有用数据项的最低地址,栈指针总是指向最终一个元素(最终入栈的数据),指令如 LDMFD,STMFD。
4、空递减:仓库经过减小存储器的地址向下添加,仓库指针指向仓库下的榜首个空方位,栈指针总是指向下一个即将放入数据的空方位,指令如 LDMED,STMED。
满:栈指针总是指向最终一个元素(最终入栈的数据)。
空:栈指针总是指向下一个即将放入数据的空方位。
增:栈首部是低地址,栈向高地址添加。
减:栈首部是高地址,栈向低地址添加。
STMFD SP!,{R1-R7,LR} ;将R1-R7,LR入栈,满递减仓库
LDMFD SP!,{R1-R7,LR} ;数据出栈,放入R1-R7,LR寄存器,满递减仓库
ARM-Thumb进程调用规范和ARM、Thumb C/C++ 编译器总是运用Full descending 类型仓库。
曾经困惑的便是STMFD 指令 关于操作数 是依照什么次第压栈的
比方:STMFD sp!{R0-R5,LR} 进栈次第是:
高地址(1办法) LR R5 R4 “““` R0 <-sp 低地址
高地址(2办法) R0 R1 “` R5 LR <-sp 低地址
寻址办法 |
阐明 |
pop |
=LDM |
push |
=STM |
FA |
递加满 |
LDMFA |
LDMDA |
STMFA |
STMIB |
FD |
递减满 |
LDMFD |
LDMIA |
STMFD |
STMDB |
EA |
递加空 |
LDMEA |
LDMDB |
STMEA |
STMIA |
ED |
递减空 |
LDMED |
LDMIB |
STMED |
STMDA |
依照图表,可知 STMFD对应的是STMDB,依据arm指令手册,可知STMDB入栈次第是1办法,而LDMFD对应的是LDMIA,这样这两个操作就能够成功配对。
在寄存器传输中,基地址能够在传输前或许传输后递加或许递减:STMIA r10,{r1,r3-r5,r8}
http://blog.csdn.net/xiaomt_rush/article/details/6501711
12.ldrex
在 include/asm-arm/spinlock.h 下有這麼一段
#if __LINUX_ARM_ARCH__ <6
#errorSMP not supported on pre-ARMv6 CPUs
#endif
好啦,条件便是:只需ARM core版别>=6才能够繼續:
all spin lock primitives 到最後都是运用下面這個根本型:
static inline void__raw_spin_lock(raw_spinlock_t *lock)
{
unsigned longtmp;
1 __asm____volatile__(
2″1: ldrex %0, [%1]\n”
3″ teq %0, #0\n”
4″ strexeq %0, %2, [%1]\n”
5″ teqeq %0, #0\n”
6″ bne 1b”
7 : “=&r” (tmp)
8 : “r” (&lock->lock), “r” (1)
9: “cc”);
smp_mb();
}
[指令重點]:
ldrex 指令是 core 6 以後才有的,跟 strex 配成一對指令,能够請 bus 監控從 ldrex 到 strex 之間有無其他的 CPU 或 DMA 來存取這個位址 ,若有的話,strex 會在榜首個 register 裡設定值為 1(non-exclusive by this CPU) 並且令 store 動作失敗,若沒有,strex 會在榜首個 register 裡設定值為 0(exclusive access by this CPU) 並且令 store 動作成功。
Code Trace Discussion:
Line 1: __volatile__ 告訴 compiler ,不要對這塊 assembly template 做最佳化動作,因為我們裡面有 loop 讀取 memory 動作,最佳化的結果或许導致 compiler 用一個 register 來 cache 它的值,不會老老實實的去讀 memory… ,這不是我們想要的動作喔!
Line 2: 把 lock 讀到 tmp,並請 bus monitor 這個 memory。
Line 3: 測試 lock 是否為 0,若非 0,表明 lock 已經被別人取得了,則 Line 4,5 都不做了,然後 Line 6 必定 branch,做 spin 的動作。若為 0,表明有機會取得 lock,繼續做 Line 4.5.。
Line 4:重點來了!,核對 bus monitor 的結果,若是exclusive access 則 tmp 設為 0 並且把 1 儲存到 lock,若是 non-exclusive access(有其他 CPU 來動過)則 tmp設為 1並且不做儲存 lock 的動作。
Line 5: 測試 tmp。
Line 6: 若 tmp 為 0 表明剛剛對 lock 動作是 exclusive,能够離開迴圈,若 tmp 為 1,則做 spin 動作。
Line 7: tmp 用 register 來操作,同時是 input 及 output 令它為 %0。
Line 8: &lock->lock 用 register 來操作 ,令它為 %1,值 1 用 register 來操作 ,令它為 %2。
Line 9: 此 template 會改到 condition code,参加 clobber list 以告訴 compiler 有這回事。
好了,終於看完了,真的很敬服那些 coding 的 kernel hackers ….
考虑問題: ARM v6 曾经有個 SWP 指令能够 lock bus and swap memory ,一樣能够用來完结 exclusive access ,可是比起 ldrex,strex 這對指令有什麼缺點呢?
SWP lock bus,其他 CPU 一切動作都不能做,可是 ldrex,strex就不會有這種現象,运用 ldrex,strex 時若其他 CPU不來 access 這個特定的 memory 就能够平行的做動作,添加平行執行的 performance。
13.swi
SWI,即software interrupt软件中止。该指令发生一个SWI反常。意思便是处理器形式改变为超级用户形式,CPSR寄存器保存到超级用户形式下的SPSR寄存器,而且跳转到SWI向量。其ARM指令格局如下:
SWI{cond} immed_24
Cond域:是可选的条件码 (拜见 ARM汇编指令条件履行详解).
immed_24域:规模从 0 到 224-1 的表达式, (即0-16777215)。用户程序能够运用该常数来进入不同的处理流程。
一、办法1:获取immed_24操作数。
为了能完结依据指令中immed_24操作数的不同,跳转到不同的处理程序,所以咱们往往需求在SWI反常处理子程序中去取得immed_24操作数的实践内容。取得该操作数内容的办法是在反常处理函数中运用下面指令:
LDR R0,[LR,#-4]
该指令将链接寄存器LR的内容减去4后所取得的值作为一个地址,然后把该地址的内容装载进R0。此刻再运用下面指令,immed_24操作数的内容就保存到了R0:
BIC R0,R0,#0xFF000000
该指令将R0的高8位清零,并把成果保存到R0,意思便是取R0的低24位。
或许仍是有人会问:为什么在SWI反常处理子程序中履行这两条指令后,immed_24操作数的内容就保存到了R0寄存器呢?之所以会有这样的疑问,根本都是由于对LR寄存器的效果没了解清楚。下面介绍一下链接寄存器LR(R14)的效果。
寄存器R14(LR寄存器)有两种特别功用:
·在任何一种处理器形式下,该形式对应的R14寄存器用来保存子程序的回来地址。当履行BL或BLX指令进行子程序调用时,子程序的回来地址被放置在R14中。这样,只需把R14内容拷贝到PC中,就完结了子程序的回来(具体的子程序回来操作,这儿不作具体介绍)。
·当某反常发生时,相应反常形式下的R14被设置成反常回来的地址(关于某些反常,或许是一个偏移量,一个较小的常量)。反常回来类似于子程序回来,但有小小的不同(这儿不作具体介绍)。
所谓的子程序的回来地址,实践便是调用指令的下一条指令的地址,也便是BL或BLX指令的下一条指令的地址。所谓的反常的回来的地址,便是反常发生前,CPU履行的最终一条指令的下一条指令的地址。
例如:(子程序回来地址示例)
指令 指令地点地址
ADD R2,R1,R3 ;0x300000
BL subC ;0x300004
MOV R1,#2 ;0x300008
BL指令履行后,R14中保存的子程序subC的回来地址是0x300008。
再例如:(反常回来地址示例)
指令 指令地点地址
ADD R2,R1,R3 ;0x300000
SWI 0x98 ;0x300004
MOV R1,#2 ;0x300008
SWI指令履行后,进入SWI反常处理程序,此刻R14中保存的回来地址为0x300008。
所以,在SWI反常处理子程序中履行LDR R0,[LR,#-4]句子,实践便是把发生本次SWI反常的SWI指令的内容(如:SWI 0x98)装进R0寄存器。又由于SWI指令的低24位保存了指令的操作数(如:0x98),所以再履行BIC R0,R0,#0xFF000000句子,就能够取得immed_24操作数的实践内容。
二、办法2:运用参数寄存器。
实践上,在SWI反常处理子程序的完结时,还能够绕开immed_24操作数的获取操作,这便是说,咱们能够不去获取immed_24操作数的实践内容,也能完结SWI反常的分支处理。这就需求运用R0-R4寄存器,其间R0-R4可恣意挑选其间一个,一般挑选R0,遵照ATPCS准则。
具体办法便是,在履行SWI指令之前,给R0赋予某个数值,然后在SWI反常处理子程序中依据R0值完结不同的分支处理。例如:
指令 指令地点地址
MOV R0,#1 ; #1给R0
SWI 0x98 ; 发生SWI中止,履行反常处理程序SoftwareInterrupt
ADD R2,R1,R3 ;
;SWI反常处理子程序如下
SoftwareInterrupt
CMP R0, #6 ; if R0 < 6
LDRLO PC, [PC, R0, LSL #2] ; if R0 < 6,PC = PC + R0*4,else next //PC-8处的指令
MOVS PC, LR //PC-4处的指令
SwiFunction
DCD function0 ;0//PC处的指令
DCD function1 ;1
DCD function2 ;2
DCD function3 ;3
DCD function4 ;4
DCD function5 ;5
Function0
反常处理分支0代码
Function1
反常处理分支1代码
function2
反常处理分支2代码
function3
反常处理分支3代码
function4
反常处理分支4代码
function5
反常处理分支5代码
在ARM体系结构中,当正确读取了PC的值时,该值为当时指令地址值加8字节,也便是说,关于ARM指令集来说,读出的PC值指向当时指令的下两条指令的地址,本例中便是指向SwiFunction 表头DCD function0这个地址,在该地址中保存了反常处理子分支function0的进口地址。所以,当进入SWI反常处理子程序SoftwareInterrupt时,假如R0=0,履行LDRLO PC, [PC, R0, LSL #2]句子后,PC的内容即为function0的进口地址,即程序跳转到了function0履行。在本例中,由于R0=1,所以,实践程序是跳转到了function1履行。R0左移2位(LDRLO PC, [PC,R0, LSL #2]),即R0*4,是由于ARM指令是字(4个字节)对齐的DCD function0等伪指令也是按4字节对齐的。
在本办法的完结中,实践指令中的24位当即数(immed_24域)被忽略了, 便是说immed_24域能够为恣意合法的值。如在本例中,不必定运用SWI 0x98,还能够为SWI 0x00或许SWI 0x01等等,程序仍是会进入SWI反常处理子程序SoftwareInterrupt,然后依据R0的内容跳转到相应的子分支。
ARM处理器运用流水线来添加处理器指令流的速度,这样可使几个操作一起进行,并使处理与存储器体系之间的操作愈加流通,接连,能供给0.9MIPS/MHZ的指令履行速度。 PC代表程序计数器,流水线运用三个阶段,因而指令分为三个阶段履行:
1.取指(从存储器装载一条指令);
2.译码(辨认即将被履行的指令);
3.履行(处理指令并将成果写回寄存器)。
而R15(PC)总是指向“正在取指”的指令,而不是指向“正在履行”的指令或正在“译码”的指令。一般来说,人们习惯性约定将“正在履行的指令作为参考点”,称之为当时榜首条指令,因而PC总是指向第三条指令。当ARM状况时,每条指令为4字节长,所以PC一直指向该指令地址加8字节的地址,即:PC值=当时程序履行方位+8;
周期1 周期2 周期3 周期4 周期5 周期6
PC-8取指 译码 履行
PC-4 取指 译码 履行
PC 取指 译码履行
14.cmp
CMP比较指令,用于把一个寄存器的内容和另一个寄存器的内容或一个当即数进行比较,一起更新CPSR中条件标志位的值。指令将榜首操作数减去第二操作数,但不存储成果,只更改条件标志位。
CMP R1, R0 ;做R1-R0的操作。
CMP R1,#10 ;做R1-10的操作。
15.txt
TST位测验指令,用于把一个寄存器的内容和另一个寄存器的内容或当即数进行按位的与运算,并依据运算成果更新CPSR中条件标志位的值。操作数1是要测验的数,而操作数2 是一个位掩码,该指令一般用来检测是否设置了特定的位。
TST {条件} 操作数1, 操作数2
例:TST R0, #0X0000 0040 ; 指令用来测验R0的位3是否为1。
TST指令一般和EQ、NE条件码合作运用,当一切测验位为0时,EQ有用,而只需有一个测验位不为0,则NE有用。
16.teq
TEQ持平测验指令,用于把一个寄存器的内容和另一个寄存器的内容或当即数进行按位的异或运算,并依据运算成果更新CPSR中的条件标志位。指令用于比较两个操作数是否持平。假如持平,则 Z = 1,不然Z = 0。指令一般和EQ、NE条件码合作运用
例:TEQ R1, R2
TST R1,#%1;测验R1中是否设置了最低位(%表明二进制数)
17.cmn
CMN — 比较取负的值
CMN{条件}{P}
status = op1 – (-op2)
CMN R0, #1 @把R0与-1进行比较
18.bic
BIC指令用于铲除操作数1的某些位,并把成果放置到意图寄存器中,假如在掩码中设置了某一位,则铲除这一位。未设置的掩码位坚持不变。
例:BIC R1, R1, #0X0F ;将R1的低四位清零,其他位不变。