ARM指令第二操作数#immed_8r常量问题
在ARM处理器的汇编语言中,对指令语法格式中的<shifter_operand>的常数表达式有这样的规定:“该常数必须对应8位位图,即常数是由一个8位的常数循环移位偶数位得到的。”
首先从ARM指令系统的语法格式说起。
一条ARM指令语法格式分为如下几个部分:
<opcode>{<cond>}{S} <Rd>,<Rn>{,<shifter_operand>}
其中,<>内的项是必须的,{}内的项是可选的,如<opcode>是指令助记符,是必须的,而{<cond>}为指令执行条件,是可选的,如果不写则使用默认条件AL(无条件执行)。
Opcode 指令助记符,如LDR,STR 等
Cond 执行条件,如EQ,NE 等
S 是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响
奶粉英文
Rd 目标寄存器
Rn 第一个操作数的寄存器
shifter_operand 第二个操作数
其指令编码格式如下:
31-28 | 27-25 | 24-21 | 20 | 19-16 | 15-12 | 11-0 (12位) |
cond | 001 | opcode | S | Rn | 如何解决心理问题Rd | shifter_operand |
| | | | | | |
当第2 个操作数的形式为:#immed_8r常数表达式时“该常数必须对应8位位图,即常数是由一个8位的常数循环移位偶数位得到的。”
假如我是风其意思是这样:#immed_8r在芯片处理时表示一个32位数,但是它是由一个8位数(比如:01011010,即0x5A)通过循环移位偶数位得到(1000 0000 0000 0000 0000 0000 0001 0110,就是0x5A通过循环右移2位(偶数位)的到的)。
而1010 0000 0000 0000 0000 0000 0001 0110,就不符合这样的规定,编译时一定出错。因为你可能通过将1011 0101循环右移位得到它,但是不可能通过循环移位偶数位得到。
1011 0000 0000 0000 0000 0000 0001 0110,也不符合这样的规定,很明显:1 0110 1011 有9位。
为什么要有这样的规定?
要从指令编码格式来解释(这就是我为什么一开始讲的是指令编码格式),仔细看表格中的shifter_operand所占的位数:12位。要用一个12位的编码来表示任意的32位数是绝对不可能的(12位数有2^12种可能,而32位数有2^32种)。
但是又要用12位的编码来表示32位数,怎么办?
只有在表示数的数量上做限制。通过编码来实现用12位的编码来表示32位数。
在12位的shifter_operand中:8位存数据,4位存移位的次数。
8位存数据:解释了“该常数必须对应8位位图”。
4位存移位的次数:解释了为什么只能移偶数位。4位只有16种可能值,而32位数可以循环移位32次(32种可能),那就只好限制:只能移偶数位(两位两位地移,好像一个16位数在移位,16种移位可能)。这样就解决了能表示的情况是实际情况一半的矛盾。
所以对#immed_8r常数表达式的限制是解决指令编码的第二个操作数位数不足以表示32位操作数的无奈之举,但这个可以说是很好的做法。因为如果直接用12位数来表示32位操作数,只能表示0 到(2^12-1)。大于(2^12-1)的数就没办法表示了。而且细细想来“8位存数据,4位存移位的次数”,应该是最好的组合了。
ARM指令第二操作数#immed_8r:
大多数ARM通用数据处理指令有一个灵活的第2操作数(flexible cond operand),这里这解释一下其中的一种格式,#immed_8r常量的表达式。常量必须对应于8位位图(pattern)。该位图在32位字中,被循环移位偶数位(0,2,4,...28,30)。合法常量0xff,0xff000,0xf000000f。非法常量:0x101,0xff04
ARM 在32位模式下,一条指令长度为32位,在上述数据处理指令中,操作数2为12位。所以像0x7f02这样的数,要两条指令才能完成。
MOV R3, #0x7F00 ;E3 A0 3C 7F 该指令自己完成0x7f移位
ORR R1, R3, #2
所以直接是找不到0x7f02的#immed_8r那个看明白了,但是请问图片里的那个8~11位的循环移位数,0000是不移位;0001是移动2位;0010是移动4位;0011是移动6位这样子吗?那么 0x7F00是7F左移8位指令中变成4 7F请问为什么给出的是C 7F呢?
关于循环移位,其实arm中只有循环右移(ROR)。0x7f到0x7f00是通过循环右移24次才实现的,这里每次移动2位所以是12次(0xc)
溢出位(V)置位有四种情况:
█ 两个整数相加结果为负数时
█ 两个负数相加结果为正数时
█ 一个正数减一个负数结果为负数时
█ 一个负数减一个正数结果为正数时
1、寄存器使用规则
子程序间通过寄存器 r0 ~ r3 来传递参数,这时可以使用它们的别名 a1 ~ a4 。被调用的子程序返回前无需恢复 r0 ~ r3 的内容。
在子程序中,使用 r4 ~ r11 来保存局部变量,这时可以使用它们的别名 v1 ~ v8 。如果在子程序中使用了它们的某些寄存器,子程序进入时要保存这些寄存器的值,在返回前恢复它们;对于子程序中没有使用到的寄存器,则不必进行这些操作。在THUMB程序中,通常只能使用寄存器 r4 ~ r7 来保存局部变量。
寄存器 r12 用作子程序间scratch寄存器,别名为ip。
寄存器 r13 用作数据栈指针,别名为 sp 。在子程序中寄存器 r13 不能用作其他用途。它的值在进入、退出子程序时必须相等。
寄存器 r14 称为连接寄存器,别名为 lr 。它用于保存子程序的返回地址。如果在子程序中保存了返回地址(比如将 lr 值保存到数据栈中), r14 可以用作其他用途。
寄存器 r15 是程序计数器,别名为 pc 。它不能用作其他用途。
寄存器 | 别名 | 使用规则 |
r15 | pc | 程序计数器 |
r14 | lr | 连接寄存器 |
卜字怎么读r13 | sp | 数据栈指针林清玄的作品 |
r12 | ip | 子程序内部调用的scratch寄存器 |
峨眉山海拔r11 | fp(v8) | ARM状态局部变量寄存器8 |
r10 | 休字组词 s1(v7) | ARM状态局部变量寄存器7、在支持数据栈检查的ATPCS中为数据栈限制指针 |
r9 | sb(v6) | ARM状态局部变量寄存器6、在支持RWPI的ATPCS中为静态基址寄存器 |
r8 | v5 | ARM状态局部变量寄存器5 |
r7 | wr(v4) | ARM状态局部变量寄存器4、THUMB状态工作寄存器 |
r6 | v3 | ARM状态局部变量寄存器3 |
r5 | v2 | ARM状态局部变量寄存器2 |
r4 | v1 | ARM状态局部变量寄存器1 |
r3 | a4 | 参数/结果/scratch寄存器4 |
r2 | a3 | 参数/结果/scratch寄存器3 |
r1 | a2 | 参数/结果/scratch寄存器2 |
r0 | a1 | 参数/结果/scratch寄存器1 |
| | |
2、数据栈使用规则
数据栈有两个增长方向:向内存地址减小的方向增长时,称为 DESCENDING栈 ;向内存地址增加的方向增长时,称为 ASCENDING栈 。
所谓数据栈的增长就是移动栈指针。当栈指针指向栈顶元素(最后一个入栈的数据)时,称为 FULL栈 ;当栈指针指向栈顶元素(最后一个入栈的数据)相邻的一个空的数据单元时,称为 EMPTY栈 。
则数据栈可以分为4种:
FD:Full Descending 满递减
ED:Empty Descending 空递减
FA :Full Ascending 满递增
EA:Empty Ascending 空递增
ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐的。使用 stmdb / ldmia 批量内存访问指令来操作FD数据栈。
使用stmdb命令往数据栈中保存内容时,先递减sp指针,再保存数据,使用ldmia命令从数据栈中恢复数据时,先获得数据,再递增sp指针,sp指针总是指向栈顶元素,这刚好是FD栈的定义。
3、参数传递规则
一般地,当参数个数不超过 4 个时,使用 r0 ~ r3 这4个寄存器来传递参数;如果参数个数超过 4 个,剩余的参数通过数据栈来传递。
对于一般的返回结果,通常使用 r0 ~ r3 来传递。
例:
假设CopyCode2SDRAM函数是用C语言实现的,它的数据原型如下:
int CopyCode2SDRAM( unsigned char *buf, unsigned long start_addr, int size )
在汇编代码中,使用下面的代码调用它,并判断返回值:
ldr r0, =0x30000000 @1. 目标地址 = 0x30000000,这是SDRAM的起始地址
mov r1, #0 @2. 源地址 = 0
mov r2, #16*1024 @3. 复制长度 = 16K
bl CopyCode2SDRAM @调用C函数CopyCode2SDRAM
cmp a0, #0 @判断函数返回值
状态切换和BX指令
AREA ADDREG,CODE,READONLY
ENTRY
MAIN
ADR r0,ThunbProg + 1
BX r0
CODE16
ThunbProg
mov r2,#2
mov r3,#3
add r2,r2,r3
ADR r0,ARMProg
BX r0
CODE32
ARMProg
短信怎么发 mov r4,#4
mov r5,#5
add r4,r4,r5
mov r0,#0x18
LDR r1,=0x20026
SWI 0x123456
END
解释:ADR r0,ThunbProg + 1为什么要加1呢?这是因为BX指令中若目标地址的bit[0]为0,则跳转时自动将CPSR中的标志位T复位,即把目标地址的代码解释为ARM代码,若目标地址的bit[0]为1,则跳转时自动将CPSR中的标志位T置位,即把目标代码解释为Thumb代码。
SWI,即software interrupt软件中断。该指令产生一个SWI异常。意思就是处理器模式改变为管理模式,CPSR寄存器保存到管理模式下的SPSR寄存器,并且跳转到SWI向量。其ARM指令格式如下:
SWI{cond} immed_24
Cond域:是可选的条件码 (参见 ARM汇编指令条件执行详解).
immed_24域:范围从 0 到 224-1的表达式, (即0-16777215),immed_24为软中断号(服务类型)。