如有错误,欢迎指正~
目录
4)比较指令(没有Rd,只用作更新条件标志位N、Z、C、V,所以不用加S)
4、在ARM汇编程序如何实现子程序的调用和返回?试举例说明。
6、试编写实现2+4+6+8+……+100的汇编程序,并在ADS1.2或MDK环境下调试运行。
一、知识点总结
1、ARM的指令分类及格式
(1)ARM指令分类
ARM指令可分为6种:数据处理指令、程序状态访问指令、分支指令、访存指令、异常指令、协处理器指令。
(2)ARM指令格式
格式以数据处理指令为例:
书写格式也以数据处理指令为例:
<操作码>{<条件码>}{s} <目标寄存器或编码>,<第一操作数或编码>,<第二操作数>{<;注释>}
其中<>中的为必需的,{}为可选的。
(3)条件码
条件码一共有16种,如下表所示:
条件码 | 助记符 | 含义 | 标志 |
---|---|---|---|
0000 | EQ | 相等 | Z=1 |
0001 | NE | 不相等 | Z=0 |
0010 | CS/HS | 无符号大于或等于 | C=1 |
0011 | CC/LO | 无符号小于 | C=0 |
0100 | MI | 负数 | N=1 |
0101 | PL | 非负数 | N=0 |
0110 | VS | 溢出 | V=1 |
0111 | VC | 没有溢出 | V=0 |
1000 | HL | 无符号大于 | C=1且Z=0 |
1001 | LS | 无符号小于或等于 | C=0或Z=1 |
1010 | GE | 有符号大于或等于 | N=V |
1011 | LT | 有符号小于 | N!=V |
1100 | GT | 有符号大于 | Z=0且N=V |
1101 | LE | 有符号小于或等于 | Z=1或N!=V |
1110 | AL | 无条件执行 | 任意 |
1111 | 保留 | v5以下版本总执行,v5及以上版本有用 |
2、ARM指令寻址方式
(1)寄存器寻址
指令中给出的是操作数所在的寄存器编号。
例:mov r0,r1
(2)立即寻址
指令中给出的就是操作数本身(立即数要以#为前缀)。
例:mov r0,#0
这里的立即数并不是任意的数,要作为立即数也要判断一下是否合法,那么该如何去判断呢?
结合老师讲的,总结下来就是:判断里面的二进制数是否<=8位,这8位数可能不是连起来的,但是只要是能够向右移动偶数位从而得到8位数,即可。
也可以总结为:
- 判断里面的二进制数是否<=8位
- 若<8位,则合法。
- 若=8位,则判断最左侧的1在奇数位还是偶数位,若为奇数位,则不合法;若为偶数位,则合法。
- 若>8,则不合法。
例1:0x0103C000
因为要判断的是二进制数,所以将上面给出的十六进制数转换为二进制数,则为:
0000 0001 0000 0011 1100 0000 0000 0000
分析上面的数,发现有效位超过8位,所以这个立即数一定不合法。
例2:0x102
将该数转为二进制为:0001 0000 0010
分析上面的数,发现有效位数为8位,但无法通过移动偶数位使其8位数作为最后的8位(或者可以理解为有效位数在奇数位)(因为对于该数,若要补充位32位,那么应该往前面填充,要求是移动偶数位,后面只有一个0,无法移动,所以并不满足条件)
所以,这个立即数也是不合法的。
例3:0x104
将该数转为二进制为:0001 0000 0100
分析上面的数,发现有效位小于8位,所以这个立即数合法。
那么,既然合法,就应该能向右移动偶数位,得到8位图(ARM中立即数又称8位位图),得到的数是多少呢?
- 右移两位,可以得到,0000 0100 0001,即0x41
例4:0x3680
将该数转换为二进制,为:0011 0110 1000 0000
- 右移6位,得到0000 0000 1101 1010,即0xDA
(3)寄存器移位寻址
ARM指令集特有的寻址方式。首先将第二操作数按要求进行移位操作,然后在进行寄存器寻址,这里的移位位数可以是立即数,也可以是寄存器方式。
ARM中常用的移位操作指令如下(图片来自百度搜索):
- 算数右移ASR:该操作数为带符号数,移位操作不变符号。所以,当移位操作数为正数时,寄存器的高端位补0;为负数时,寄存器的低端位补1。
- 逻辑左移LSL:寄存器中的高端位送至C标志位,低端补0。
- 逻辑右移LSR:寄存器中的低端位送至C标志位,高端补0。
- 循环右移ROR:寄存器低端移出的位填入到寄存器高端的空出位上(a图)。
- 扩展的循环右移RRX:进行带进位位的循环右移(d图)。与循环右移的区别就是,带C一起移。
例:
- mov r0,r1,LSL #3 ;LSL为逻辑左移,即先将r1循环左移三位(左移乘以2的相应次方,右移除以二的相应次方,所以这里是*2的3次方,即*8),然后将r1的值给r0。
- mov r0,r1,ROR r2 ;ROR为循环右移,即先将r1中的值循环右移r2中的值位,然后将r1的值给r0。
(4)寄存器间接寻址
指令中给出的为寄存器的编号,而该编号下的寄存器里存放的是操作数所在的地址,而不是操作数,通过该地址,可以找到相应的操作数。
例:LDR R0,[R1] ;[R1]→R0
(5)变址寻址
就是将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址。寄存器间接寻址是偏移量为0的基址加便宜寻址。
有3种加偏移量(偏移地址)的变址寻址方式。
1)前变址方式
先将基地址加上偏移地址,然后在进行指令指定的操作。
例: LDR R0,[R1,#4] ;将R1
寄存器中的值(作为地址)加上偏移量#4
,然后从得到的地址处加载一个32位数据到R0
寄存器中
2)自动变址方式
先执行前变址的操作,然后再自动修改基址寄存器。
例:LDR R0,[R1,#4] !
!表示写回或更新基址寄存器。
3)后变址方式
先进行指令指定的操作,再将及地址加上偏移地址。
例: LDR R0,[R1] ,#4 ;将R1中的地址处加载一个32位数据到R0
寄存器中,然后将R1
寄存器中的值(作为地址)加上偏移量#4
(6)多寄存器寻址
一条指令传送多个寄存器的值。
例:LDM R1,{R0,R2,R5} ;将R1所指的连续的3个存储单元中的内容分别送到R0,R2,R5中。
(7)堆栈寻址
堆栈是一种按照顺序进行存取的存储区。
堆栈可以分为两种:递增堆栈(地址向高地址方向生长)和递减堆栈(地址向低地址方向生长)。
根据SP指向的位置,又可以分为满堆栈(SP指向最后压入的有效数据单元)和空堆栈(SP指向最后压入的有效数据单元的下一个空单元)。
ARM处理器支持4中堆栈工作模式:
- 满递增堆栈FA:SP指向最后压入的数据单元,且有低地址向高地址生成。
- 满递减堆栈FD:SP指向最后压入的数据单元,且有高地址向低地址生成。
- 空递增堆栈EA:SP指向下一个将要存放数据的空单元,且由低地址向高地址生成。
- 空递减堆栈ED:SP指向下一个将要存放数据的空单元,且由高地址向低地址生成。
例:STMFD sp!,{r4-r7,lr} ;将r4~r7、lr入栈,满递减堆栈
LDMFD sp!,{r4-r7,pc} ;数据出栈,放入r4~r7、pc中。
在使用满递减堆栈时,STMFD相当于STMFD
(8)块复制寻址
把存储器中的一个数据块加载到多个多个寄存器中,或者是把多个寄存器中的内容保存到存储器中。
- IA:先传后加4
- IB:先加4后传
- DA:先传后减4
- DB:先减4后传
例:STMIA r10,{r0,r1,r4}
(9)相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值(R15中的值)为基地址,指令中的地址标号作为偏移量,将两者相加后得到操作数的有效地址。
3、常用ARM指令
(1)数据处理指令(除比较指令,其余均可加S)
1)数据传送指令
- mov mov{<cond>}{S} rd,operand2
- mvn:先取反后传送
2)算术运算指令
- ADD:加 ADD{<cond>}{S} Rd,Rn,Operand2
- SUB:减
- RSB:逆向减法 例:RSB R1,R2,R3 ;R3-R2的值传送给R1
- ADC:带进位加法
- SBC:带借位减法
- RSC:带借位逆向减法
注意:后有S,那么根据运算结果更新标志N,Z,C,V
若R15作Rn使用,则使用的值是当前指令的地址加8
若R15作Rd使用,则执行完指令后,程序将转移到结果对应的地址处,若还有S,则将CPSR的值复制到SPSR。利用该特性从异常返回。
再有寄存器控制移位的任何数据处理指令中,不能将R15作为任何操作数来使用。
3)逻辑运算指令
- AND:与
- ORR:或
- EOR:异或
- BIC:位清除 BIC{<cond>}{S} Rd,Rn,Operand2 ;Rn&(not Operand2)的结果给Rd
注意:若加有后缀S,这些指令执行完后将根据结果更新标志N和Z,在计算operand2时会更新标志C,不影响V。
4)比较指令(没有Rd,只用作更新条件标志位N、Z、C、V,所以不用加S)
- CMP:比较 CMP{<cond>} Rn,operand2
- CMN:负数比较
- TST:位测试,Rn和operand2的各位按位与。常与条件码EQ,NE配合使用,测试位均为0,EQ有效,否则,NE有效。
- TEQ:相等测试,Rn和operand2的各位按位异或。与EORS的区别在于,TEQ不保存运算结果。常与条件码EQ,NE配合使用,两个数据相等,EQ有效,否则,NE有效。
5)乘法指令
ARM有两类乘法指令:32位的乘法指令(乘法操作为32位)和64位的乘法指令。
例:MUL R1,R2,R3 ;R1=R2*R3
MLA R1,R2,R3,R0 ;R1=R2*R3+R0
UMULL R0,R1,R5,R8 ;R5*R8→(R1,R0)
(2)程序(PSR)访问指令
1)读状态寄存器指令
2)写状态寄存器指令
(3)分支指令
1)转移指令
B{<cond>} label ;默认为无条件跳转,跳转限制在当前指令的+-32MB的范围内。
2)带链接的转移指令
BL{<cond>} label ;将BL下一条指令给LR,label给PC。常用于子程序的调用。
3)带状态切换的转移指令
BX{cond} Rm ;Rm&0xfffffffe,T=Rm[0]&1
这个转移指令是为了进行工作状态的切换。若当前为ARM状态,则将T标志位置1,转为 Thumb状态;若当前状态为Thumb,则将T标志位置0,转为ARM状态。详细的解释我也不是很懂,这样理解应该足够了。
还有一个是书上没有,但是老师提到的。
4)BLX指令
BLX 目标地址
- 如果目标地址的最低位(T位)为0,BLX指令将转移到ARM状态下的地址,并将下一条指令的地址保存在LR寄存器中。
- 如果目标地址的最低位(T位)为1,BLX指令将转移到Thumb状态下的地址,并将下一条指令的地址保存在LR寄存器中。
总结一下,BLX指令在ARM处理器中用于实现带链接的分支跳转,并且可以根据目标地址的状态位(T位)自动切换ARM状态或Thumb状态。
(4)访存指令
1)单数据访存
在ARM指令中,读指令又叫加载指令,是从存储器到寄存器;写指令又叫存储指令,是从寄存器到存储器。
第一类用于存储或加载字、无符号字节。
- LDR:加载字数据
- LDRB:加载无符号字数据
- LDRT:以用户模式加载字数据
- LDRBT:以用户模式加载无符号字数据
- STR :存储字数据
- STRB:存储无符号字数据
- STRT:以用户模式存储字数据
- STRBT:以用户模式存储无符号字数据
带T后缀说明:处于特权模式,存储系统也会看作是用户模式下;
用于存储器保护;
不能与前变址寻址、自动变址寻址一起使用;
在用户模式下无效。
LDR/STR属于变址寻址。
第二类是指加载或存储无符号半字,加载有符号半字、有符号字节。
- LDRH:加载无符号半字
- STRH:存储无符号半字
- LDRSB:加载有符号字节
- LDRSH:加载有符号半字
2)多数据访存
可以实现一组(1~16)寄存器和一块(4~64字节)连续内存单元之间的数据传输。
LDM|STM{<cond>}<type> <Rn>{!},<Regs>{^}
type表类型,用于数据的存储与加载时有一下4种方式:
- IA:事后递增(每次传送后地址值增加)
- IB:先增
- DA:后减
- DB:先减
用于堆栈操作时有以下4种方式:
- FD:满递减堆栈
- ED:空递减堆栈
- FA:满递增堆栈
- EA:空递增堆栈
{^}为可选后缀,当指令为LDM且寄存器列表包含PC,表除了正常的多寄存器传送外,还要将SPSR复制到CPSR中,;当不包含时,加载/存储的是用户模式的寄存器,而非当前模式。
LDM/STM指令寻址是按字对齐的,即忽略地址为[1:0]。LDM/STM用途是现场保护、数据复制和参数传送等。
3)数据交换访存
- SWP:字数据交换指令 ,SWP{<cond>}{S} Rd,Rm,[Rn],实现将Rn的值给Rd,Rm给Rn。
- SWPB:字节数据交换指令
例:SWP R1,R2,[R3]
SWP R1,R1,[R2] ;将R1寄存器中的内容与R3存储器中的内容互换。
SWPB R1,R2,[R0] ;将R0指的存储单元的内容读取1字节到R1(高24位置0),并将R2的内容写入到该内存单元中(最低字节有效)。
(5)异常指令
- SWI:软件中断指令
- BKPT:断点指令(v5T及以上版本)
- CLZ:前导0计数(v5T及以上版本)
(6)协处理器指令
4、ARM汇编伪指令与伪操作
伪指令:是汇编程序例的特殊指令助记符,是为了方便编程而定义的,在汇编时被合适的机器指令替代;
伪操作:为汇编程序所用,在源程序进行汇编时由汇编程序处理,只在汇编过程起作用,不参与程序运行。
(1)ARM汇编伪指令
- ADR:小范围的地址读取伪指令。ADR{cond}reg,expr
- ADRL:中等范围的地址读取伪指令
- LDR:大范围的地址读取伪指令。 LDR{cond} reg,=expr,用于加载32位立即数或一个地址值到指定的寄存器。
- NOP:空操作伪指令,可用于软件延时操作。
(2)ARM汇编伪操作
1)符号定义伪操作
详情可阅读这篇文章(侵权联系删除):
嵌入式:ARM符号定义伪操作详解-腾讯云开发者社区-腾讯云 (tencent.com)
- GBLA,GBLL,GBLS 声明全局变量
GBLA伪操作声明一个全局的算术变量,并将其初始化成0
GBLL伪操作声明一个全局的逻辑变量,并将其初始化成{FALSE}
GBLS伪操作声明一个全局的字符串变量,并将其初始化成空串“”
- LCLA,LCLL,LCLS 声明局部变量
- SETA,SETL,SETS 给变量赋值
- RLIST 给通用寄存器列表定义名称
例:
2)数据定义伪操作
- LTORG:声明一个数据缓冲池
- SPACE:分配一块字节内存单元,并用0初始化
- DCB:定义且初始化一个或多个字节的内存区域。DCB也可用=代替。
- DCD,DCDU:DCD分配一段字对齐的内存单元,并初始化。DCD可用&表示。DCDU分配一段非严格字对齐的内存单元。
- DCW:分配半字存储单元
3)汇编控制常用伪操作
- IF,ELSE及ENDIF
- WHILE 及WEND
- MACRO,MEND及MEXIT 宏定义
4)其他常用伪操作
- AREA:定义一个代码段或数据段。例:AREA 名称,CODE,READONLY
- CODE16和CODE32:CODE16表后面为Thumb指令;CODE32表后面为ARM指令。
- ENTRY:指定程序入口点。一个程序至少有一个,一个源程序最多只能有一个。
- ALIGN:通过填充0将当前位置以某种形式对齐。
- END:源程序结尾
- EXPORT和GLOBAL:声明一个源程序中的符号,以便使该符号能被其他源文件引用。
- IMPORT和EXTERN:声明一个符号是在其他源文件中定义的。
- GET和INCLUDE:将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理。
- INCBIN:将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置不进行汇编处理。
二、课后习题
1、ARM指令的寻址方式有几种?试分别举例说明。
有9种,分别为寄存器寻址方式、立即数寻址、寄存器移位寻址、寄存器间接寻址、变址寻址、多寄存器寻址、块复制寻址、堆栈寻址、相对寻址。
分别举例如下:mov r0,r1 、 mov r1,#1 、mov r0,r1,LSL #2、mov r0,[r1]、mov r0,[r1,#2]、
LDM R1,{R0,R2,R5}、STMIA r10,{r0,r1,r4}、STMFD sp!,{r4-r7,lr} 、BLE label_less(并没有给出直接跳转的地址,所以采用了相对寻址的方式)。
2、ARM指令的条件码有多少个?默认条件码是什么?
16个,默认为AL(无条件跳转)
3、ARM指令中的第二操作数有哪几种形式?试举例说明。
立即数、寄存器、寄存器移位。
mov r1,#1 、 mov r0,r1 、 mov r0,r1,LSL #2、
4、在ARM汇编程序如何实现子程序的调用和返回?试举例说明。
在ARM汇编语言中,子程序的调用和返回主要涉及到BL
(Branch with Link)指令和BX LR
或MOV PC, LR
指令。以下是具体的说明和示例:
子程序的调用
子程序的调用通常使用BL
指令。BL
指令会跳转到指定的地址执行,并将当前的指令地址(即返回地址)保存到链接寄存器LR
(Link Register)中,以便子程序执行完毕后能够返回到正确的位置。
子程序的返回
子程序的返回可以通过BX LR
或MOV PC, LR
指令来实现。这两条指令都会将程序计数器PC
(Program Counter)设置为链接寄存器LR
中的值,从而实现返回到调用点的功能。
示例
下面是一个简单的ARM汇编程序示例,展示了如何调用和返回子程序:
AREA MyProgram, CODE, READONLY
ENTRY
START
; 调用子程序
BL MySubroutine
; 子程序返回后的代码
B . ; 无限循环,防止程序跑飞
; 子程序定义
MySubroutine
; 子程序的代码,比如打印一条消息等
; ...
; 子程序返回
BX LR
END
在这个示例中:
START
标签下的代码是主程序部分。- 使用
BL MySubroutine
指令调用名为MySubroutine
的子程序。 - 在子程序
MySubroutine
中执行必要的操作。 - 使用
BX LR
指令从子程序返回。
注意:在实际应用中,子程序可能会更复杂,并且可能会涉及到参数传递和返回值处理等问题。此外,为了优化性能和代码结构,还可以使用其他高级技术,如寄存器传递参数、堆栈处理等。但这个示例提供了一个基本的框架来理解ARM汇编中子程序的调用和返回机制。
5、汇编程序设计中常用的伪操作有哪几类?各有什么作用?
有符号定义伪操作、数据定义伪操作、汇编控制伪操作和其他的伪操作。符号定义伪操作用于声明全局变量和局部变量、给变量赋值、给通用寄存器列表定义名称等;数据定义伪操作用于声明数据缓冲池、分配内存单元等;汇编控制伪操作用以设置条件选择、循环汇编、宏定义汇编等;其他的伪操作可以用来定义数据段或代码段、指定程序入口点、结束】或者声明符号等。
6、试编写实现2+4+6+8+……+100的汇编程序,并在ADS1.2或MDK环境下调试运行。
area ZY3_6,code,readonly
entry
code32
mov r0,#0
mov r1,#2
loop
add r0,r0,r1
add r1,r1,#2
cmp r1,#102
blt loop
end
ok,暂时就先这样吧。