Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1301311
  • 博文数量: 548
  • 博客积分: 7597
  • 博客等级: 少将
  • 技术积分: 4224
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-15 13:21
个人简介

嵌入式软件工程师&&太极拳

文章分类

全部博文(548)

文章存档

2014年(10)

2013年(76)

2012年(175)

2011年(287)

分类: LINUX

2011-02-20 19:56:32

								ARM指令

八、乘法指令
	1、	mul乘法指令
		格式:  mul{}{s} Rd,Rm,Rs
		代码:  if(cond == 1)
				{
					Rd = Rm * Rs;
				}
			
	2、	mla乘累加指令
		格式: mla{}{s} Rd,Rm,Rs,Rn
		代码:  if(cond == 1)
				{
					Rd = Rm * Rs + Rn;
				}
				
	3、	smull有符号长乘指令
		格式: smull{}{s} RdLo,RdHi,Rm,Rs
		代码: if(cond == 1)
				{	
					RdHi:RdLo = Rm * Rs;
				}
	
	
	4、smlal有符号长乘累加指令
		格式: smlal{}{s} RdLo,RdHi,Rm,Rs
		代码: if(cond == 1)
				{	
					RdHi:RdLo = Rm * Rs + RdHi:RdLo;
				}
				
	5、	umull无符号长乘指令
		格式: umull{}{s} RdLo,RdHi,Rm,Rs
		代码: if(cond == 1)
				{	
					RdHi:RdLo = Rm * Rs;
				}
	
	6、	umlal无符号长乘累加指令
		格式: umlal{}{s} RdLo,RdHi,Rm,Rs
		代码: if(cond == 1)
				{	
					RdHi:RdLo = Rm * Rs + RdHi:RdLo;
				}
				
九、乘法指令位图
	1、	32位
		xxxx 0000 00as dddd nnnn ssss 1001 mmmm
		Rm
		if(a == 0)
			乘法;
		else
			乘累加;
	
	2、	64位
		xxxx 0000 1uas hhhh llll ssss 1001 mmmm
		
		if(u == 0)
			无符号;
		else
			有符号;
		
十、分支指令
	1、	b分支指令
		格式: b{l}{} label
		代码: if(cond == 1)
				{
					if(l == 1)
					{
						lr = pc + 4 - 8;		//保存下一条指令,当把lr的值赋给pc时可实现返回(为什么要减去 8 请见第十二节 第7部分 )
					}
					pc = pc + offset - 8;
//					pc = pc + offset + 8;		offset是当前指令到lebel的偏移量,offset是正数 -8,负数 +8
				}
				
	2、b分支指令位图
		xxxx 101L jjjj jjjj jjjj jjjj jjjj jjjj
				
		if(L == 0)
			指令 = b;
		else
			指令 = bl;
		
		24 * j 用来保存地址,只能访问 0  - 16 M 的内存空间
		指令是按每 4 个字节对齐的,所以先右移 2 位保存,取出值后再左移 2 位,可以访问 0  - 64 M
		但是label的值可以达到 4 G,64 M - 4 G无法访问
		如果 24 位来表示当前指令的地址到label的偏移量,正数表示向前偏移,负数表示向后偏移
		这样就可以访问当前指令的前后 32 M了(pc的值就是当前指令的地址)
		
	3、	bx分支切换指令
		格式:	bx{} Rm
		代码:	if(cond == 1)
				{
					pc = Rm & 0xfffffffc;		//pc = Rm & 0xfffffffe;T = Rm[0];
				}
		
	注:	bx分arm版本和thumb版本,thumb版本不能条件执行,blx要在ARMv5T及以上版本才有

十一、单寄存器load-store指令
	1、	ldr 32 位数据读取指令
	格式:		ldr{} Rd,addr					//addr用法请见第十二节内存寻址方式
	代码:		if(cond == 1)
				{
					val = *(int *)addr;
											
					if(Rd == pc)
						pc = val & 0xfffffffc;		//pc = val & 0xfffffffe;T = val[0];
					else
						Rd = val;
				}
		
	2、	ldr 16 位数据读取指令
	格式:		ldr{}h Rd,addr
	代码:		if(cond == 1)
				{
					Rd = *(short *)addr & 0x0000ffff;
				}
				
	3、	ldr 16 位有符号数据读取指令
	格式:		ldr{}sh Rd,addr
	代码:		if(cond == 1)
				{
					val = *(short *)addr & 0x0000ffff;
					if(val[15] == 1)
					{
							Rd = 0xffff0000;
							Rd |= val;
					}
					else
					{
							Rd = val;
					}
				}
				
	4、	ldr 8 位数据读取指令
	格式:		ldr{}b Rd,addr
	代码:		if(cond == 1)
				{
					val = *(char *)addr;
					Rd  = val & 0x000000ff;
				}
			
	5、	ldr 8 位有符号数据读取指令
	格式:		ldr{}b Rd,addr
	代码:		if(cond == 1)
				{
					val = *(char *)addr & 0x000000ff;
					if(val[7] == 1)
					{
							Rd = 0xffffff00;
							Rd |= val;
					}
					else
					{
							Rd = val;
					}
				}
		
	6、	str 32 位数据写入指令
	格式:		str{} Rd,addr
	代码:		if(cond == 1)
				{
				
					*(int *)addr = Rd;
				}
	
	7、	str 16 位数据写入指令
	格式:		str{}h Rd,addr
	代码:		if(cond == 1)
				{
					*(short *)addr = Rd[15:0];
				}
	
	8、	str 8 位数据写入指令
	格式:		str{}b Rd,addr
	代码:		if(cond == 1)
				{
					*(char *)addr = Rd[7:0];
				}
				
	注:	没有str有符号数据写入指令
	
十二、内存寻址方式
	1、	label
	举例:			ldr r0,label		//ldr r0,label + 0
			lebel:
					.word 0x12345
	代码:	r0 = *(int *)label;		//r0 == 0x12345

	2、	前变址
	格式:	[Rn,op]
	代码:	addr = Rn + op;
	
	注:	addr只是地址,还没有取值
	举例:	ldr R0,[R1,#8]
	代码:	addr = R1 + 8;
			R0   = *(int *)addr;
				
	3、	回写前变址
	格式:	[Rn,op]!
	代码:	addr = Rn + op;
			Rn   = Rn + op;
			
	4、	后变址
	格式:	[Rn],op
	代码:	addr = Rn;
			Rn   = Rn + op;
				
	5、	ldr、ldrb、str、strb指令的op
	立即数:		#+/-offset_12
	寄存器:		+/-Rm
	寄存器移位:	+/-Rm,<移位符> #number
	注:			#+/-offset_12 = +/-(offset_12[7:0] ror offset_12[11:8])(与前面的立即数构造方法一样)
					number最大为 0x1f,没有 +/-Rm,<移位符> Rs方式的寄存器移位
					+号可以省略
					立即数等于 0 可以省略
					
	6、	ldrh、ldrsh、ldrsb、strh指令的op
	立即数:		#+/-offset_8
	寄存器:		+/-Rm
	注:			offset_8最大为255,没有寄存器移位
	
	7、Rn == pc
	说明:	当Rn == pc时,addr = 当前指令的地址 + 8 + op
			这是流水线的问题,因为执行当前这条指令时pc已经指向下两条指令了,所有我们计算op时应该减去 8 
	举例:	0			ldr r0,[pc,#-4]
			4 .word	0x12345
			8
			
			这两行指令等于 mov r0,#0x12345,但是 0x12345 不能构造成立即数,所以可以通过这种方式来赋值
			我们不能写成 ldr r0,[pc,#4],因为执行这条指令时pc已经执行地址 8 的指令,应该减去 4 而不是加 4
			本来按道理应该加 4 的现在变成减 4,所以等于减去了 8
									
十三、多寄存器方式
	1、	ldm / stm
	格式:	{}{ia|ib|da|db}  Rn{!},R_list 	//{r0 - r15}^ / {r0,r5,r9,pc},如果顺序不对,编译器会自动按从R0-R15调整
	代码:	if(cond == 1)										//										^表示cpsr --> spsr或spsr -->cpsr
			{
				if(ia == 1)				//执行后增加,	如果寻址模式为空,默认是ia
					addr = Rn;
				else if(ib == 1)		//执行前增加
					addr = Rn + 4;
				else if(da == 1)		//执行后减少
					addr = Rn - sizeof(R_list) + 4;		//sizeof(R_list) == 寄存器数 * 4
				else //db == 1			//执行前减少
					addr = Rn - sizeof(R_list);
				
				if(ldm == 1)			
					ldm_func(addr,R_list);
				else //stm == 1
					stm_func(addr,R_list);
				
				if(! == 1)
				{
					if(ia == 1 || ib == 1)
						Rn += sizeof(R_list);		
					else //da == 1 || db == 1
						Rn -= sizeof(R_list);
				}
			}
			
			void ldm_func(addr,R_list)
			{
				for(i = 0;i <= 14;i++)
				{
					if(R_list[i] == 1)				//R_list[i] 表示是否有该寄存器
						R_list[i] = *(int *)addr;	//R_list[i] 表示该寄存器的值
					addr += 4;
				}
				if(R_list[15] == 1)				//R_list有pc
				{
					pc = *(int *)addr & 0xfffffffc;		//pc = *(int *)addr & 0xfffffffe;T = *(int *)addr[0];
				}
				if(^ == 1)
				{
					cpsr = spsr;
				}
			}
			
			void stm_func(addr,R_list)
			{
				for(i = 0;i <= 15;i++)
				{
					if(R_list[i] == 1)
						*(int *)addr = R_list[i];
					addr += 4;
				}
				if(^ == 1)
				{
					spsr = cpsr;
				}
			}
			
	2 、更新基地址的指令对,可实现栈的push和pop操作
		stmia <=======> ldmdb	
		stmib <=======> ldmda
		stmda <=======> ldmib
		stmdb <=======> ldmia
		举例: 	stmdb sp!,{r1,r2,r3}
				ldmia sp!,{r1,r2,r3}
				执行后 sp,r1,r2,r3 的值保持不变
	
	3、	栈操作
		栈向高地址扩展的为递增栈(A,ascending),向低地址扩展的为递减栈(D,descending)
		栈指针(sp)指向的是有效数据称为满栈(F,full),指向的是无效数据称为空栈(E,empty)
		在ARM-Thumb过程调用标准(ATPCS)中,栈被定义为递减式满栈
		
		寻址方式		说明		push		=stm		pop		=ldm
		FA				递增满		stmfa		stmib		ldmfa		ldmda		
//		FD				递减满		stmfd		stmdb		ldmfd		ldmia					//arm汇编的push和pop操作指令
		EA				递增空		stmea		stmia		ldmea		ldmdb		
		ED				递减空		stmed		stmda		ldmed		ldmib		
				
十四、交换指令
	格式:	swp{}{b} Rd,Rm,[Rn]		//[Rn]不能加立即数和其它方式
	代码:	if(cond == 1)
			{
				if(b == 1)
				{
					Rd = *(int *)Rn;
					*(int *)addr = Rm;
				}
				else // b == 0
				{
					Rd = *(char *)Rn & 0x000000ff;
					*(char *)Rn = Rm & 0x000000ff;
				}
			}
			
	注:	交换数据是在一条指令内完成的,可以保证在交换的过程中不会被任何中断打断

十五、swi(software interrupt)软件中断指令
	格式:	swi{} SWI_number
	代码:	if(cond == 1)
	 		{
	 			lr_svc = pc + 4 - 8;		//当前指令的下一条指令
	 			spsr_svc = cpsr;			//保存cpsr
	 			cpsr[4:0] = 0b10011;		//进入管理模式
	 			cpsr[5] = 0;				//执行arm状态
	 			cpsr[7] = 1;				//屏蔽irq中断
	 			pc = 0x00000008			//跳转中断向量表的swi
	 		}

	SWI_number是中断号,用 24 位表示,最大为 #0xffffff,#号可以省略,直接把中断号存在指令里,不用按立即数方式构造
	arm-linux的中断号见内核源码include/arch/arm/include/asm/unistd.h		//一般从0x900000开始编号
	x86-linux的中断号见系统目录/usr/include/asm/unistd.h						//一般从0x0开始编号

十六、程序状态寄存器指令
	1、	mrs
	格式:	mrs{} Rd,
			
	2、	msr
	格式:	msr{} {_fields},
	
	fields可以是f、x、s、f的任意组合,表示只对该域操作,默认全部
	
	f 表示标志域 psr[31:24]
	s 表示状态域 psr[23:16]
	x 表示扩展域 psr[15:8]
	c 表示控制域 psr[7:0]

十七、协处理器指令
	ldp
	mcr
	mrc
	ldc
	stc
	




阅读(695) | 评论(0) | 转发(0) |
0

上一篇:ARMv4T_指令集详解1

下一篇:GNU汇编

给主人留下些什么吧!~~