📄 arm 指令格式和时序.htm
字号:
等不再工作)。</P>
<DT>ADC, ADD, RSB, RSC, SBC, SUB
<DD>如果设置了 S 位,则在结果上设置 N 和 Z 标志,从 ALU 的得到 C 和 V 标志。
<DT>
<DT>AND, BIC, EOR, ORR
<DD>如果设置了 S 位,则在结果上设置 N 和 Z 标志,如果使用了移位器则从它得到 C 标志(在这种情况下它变成被移出的最后一位),不影响 V 标志。
</DD></DL>
<P>可以使用 ADD 和 SUB 以与位置无关的方式使寄存器指向数据,例如 ADD R0,PC,#24。这很有用,一些汇编器有一个叫做 ADR
的特殊宏指令(directive),它自动生成恰当的 ADD 或 SUB 指令。(ADR R0, fred 典型的把 fred 的地址放置到 R0 中,假定
fred 在范围内)。
<P>在 26-bit 模式下,在 R15 是使用的寄存器之一的时候发生一种特殊情况:
<UL>
<LI>如果 Rn = R15 则使用的 R15 值屏蔽掉了所有 PSR 位。
<LI>如果 Op2 涉及 R15,则使用所有的 32 位。 </LI></UL>
<P>在 32-bit 模式下,使用 R15 的所有的位。
<P>在 26-bit 模式下,如果 Rd = R15 则:
<UL>
<LI>如果未设置 S 位,则只设置 PC 的 24 位。
<LI>如果设置了 S 位,则覆写 PC 和 PSR 二者(除非在非用户模式下,否则不改变模式位、I 和 F 位。) </LI></UL>
<P>对于 32-bit 模式, 如果 Rd=15,则覆写 PC 的所有的位,不包括最低的那两个有效位,它们总是零。如果未设置 S
位,则只进行上面这些;如果设置了 S 位,把当前模式的 SPSR 复制到 CPSR 中。在 32-bit 用户模式下,你不应该执行把 PC
作为目的寄存器并设置了 S 位的指令,因为用户模式没有 SPSR。(顺便说一句,你这样做不会打断处理器 -
这样做的结果只是未定义而已,且在不同的处理器上可能不同。)
<P>执行这些指令使用下列数目的周期: 1S + (1S 如果使用了寄存器控制的移位) + (1S + 1N 如果改变了 PC)
<P><A name=Branch>
<H3>分支指令</H3></A><PRE>xxxx101L oooooooo oooooooo oooooooo
</PRE>
<P>典型的汇编语法: <PRE> BEQ 地址
BLNE 子例程
</PRE>
<P>使用这些指令强制跳转到一个新地址,用相对于执行这个指令时 PC 值的以字为单位的偏移量给出这个新地址。
<P>因为流水线的缘故,PC 总是超出存储这个指令的地址 2 个指令(8 字节),所以分支的偏移量 = (位 0-23 的有符号扩展): <PRE> 目的地址 = 当前地址 + 8 + (4 * 偏移量)
</PRE>在 26-bit 模式下,清除目的地址的顶端 6 位。
<P>如果设置了 L 位,则在进行这个分支之前把 PC 的当前内容复制到 R14。所以 R14 持有在这个分支后面的指令的地址,被调用的例程可以用 MOV
PC,R14 返回。
<P>在 26-bit 模式下,使用 MOVS PC,R14 来从一个带连接的分支返回,在返回时可以自动恢复 PSR 标志。 在 32-bit 模式下 MOVS
PC,R14 的行为是不同的,并只适合于从例外返回。
<P>执行分支和带连接的分支二者都使用 2S+1N 个周期。
<P> </P><A name=Multiplication>
<H3>乘法</H3></A><PRE>xxxx0000 00ASdddd nnnnssss 1001mmmm
</PRE>
<P>典型汇编语法: <PRE> MULEQS Rd, Rm, Rs
MLA Rd, Rm, Rs, Rn
</PRE>
<P>这些指令做两个操作数的乘法,并且可以选择加上第三个操作数,把结果放置到另一个寄存器中。
<P>如果设置了 S 位,则在结果是设置 N 和 Z 标志,未定义 C 标志,不影响 V 标志。
<P>如果设置了 A 位,则操作的效果是 Rd = Rm*Rs + Rn,否则是 Rd = Rm*Rs。
<P>目的寄存器不应该与操作数寄存器 Rm 相同。R15 不应该用于操作数或目的寄存器。
<P>执行这些指令在最坏的情况下使用 1S + 16I 个周期,并依赖于实际参数的值可以更小。实际时间依赖于 Rs 的值,依照下表: <! center BOXED ; c s lr1 l l. ><PRE> Rs 的范围 周期数
&0 - &1 1S + 1I
&2 - &7 1S + 2I
&8 - &1F 1S + 3I
&20 - &7F 1S + 4I
&80 - &1FF 1S + 5I
&200 - &7FF 1S + 6I
&800 - &1FFF 1S + 7I
&2000 - &7FFF 1S + 8I
&8000 - &1FFFF 1S + 9I
&20000 - &7FFFF 1S + 10I
&80000 - &1FFFFF 1S + 11I
&200000 - &7FFFFF 1S + 12I
&800000 - &1FFFFFF 1S + 13I
&2000000 - &7FFFFFF 1S + 14I
&8000000 - &1FFFFFFF 1S + 15I
&20000000 - &FFFFFFFF 1S + 16I
</PRE>
<P>这些乘法时序不适用于 ARM7DM。 ARM7DM 时序由下表给出: <! center BOXED ; c s l l l l lc s l l l l lr1 l l l l l l. ><PRE> MLA/
Rs 的范围 MUL SMULL SMLAL UMULL UMLAL
&0 - &FF 1S+1I 1S+2I 1S+3I 1S+2I 1S+3I
&100 - &FFFF 1S+2I 1S+3I 1S+4I 1S+3I 1S+4I
&10000 - &FFFFFF 1S+3I 1S+4I 1S+5I 1S+4I 1S+5I
&1000000 - &FEFFFFFF 1S+4I 1S+5I 1S+6I 1S+5I 1S+6I
&FF000000 - &FFFEFFFF 1S+3I 1S+4I 1S+5I 1S+5I 1S+6I
&FFFF0000 - &FFFFFEFF 1S+2I 1S+3I 1S+4I 1S+5I 1S+6I
&FFFFFF00 - &FFFFFFFF 1S+1I 1S+2I 1S+3I 1S+5I 1S+6I
</PRE><A name=Long>
<H3>长乘法(ARM7DM)</A> </H3><PRE>xxxx0000 1UAShhhh llllssss 1001mmmm
</PRE>
<P>典型的汇编语法: <PRE> UMULL Rl,Rh,Rm,Rs
UMLAL Rl,Rh,Rm,Rs
SMULL Rl,Rh,Rm,Rs
SMLAL Rl,Rh,Rm,Rs
</PRE>
<P>这些指令做寄存器 Rm 和 Rs 的值的乘法并获得一个 64-bit 乘积。
<P>在清除了 U 位的时候乘法是无符号的(UMULL 或 UMLAL),否则是有符号的(SMULL, SMLAL)。在清除了 A
位的时候,把结果的低有效的那一半存储在 Rl 中并把它的高有效的那一半存储到 Rh 中。在设置了 A 位的时候,转而把结果加到 Rh,Rl 的内容上。
<P>不应该使用程序计数器 R15。Rh、Rl 和 Rm 应该不同。
<P>如果设置了 S 位,则在 64-bit 位结果是设置 N 和 Z 标志,未定义 C 和 V 标志。
<P>它们的时序可以在上面的乘法段落中找到。
<P> </P><A name=Transfer>
<H3>单一数据传送</H3></A><PRE>xxxx010P UBWLnnnn ddddoooo oooooooo Immediate form
xxxx011P UBWLnnnn ddddcccc ctt0mmmm Register form
</PRE>
<P>典型的汇编语法: <PRE> LDR Rd, [Rn, Rm, ASL#1]!
STR Rd, [Rn],#2
LDRT Rd, [Rn]
LDRB Rd, [Rn]
</PRE>
<P>这些指令装载/存储内存的一个字从/到一个寄存器。在指定地址时使用的第一个寄存器在术语上叫做基址寄存器。
<P>如果设置了 L 位,则进行装载,否则进行存储。
<P>如果设置了 P 位,则使用预先变址寻址,否则使用过后变址寻址。
<P>如果设置了 U 位,则给出的偏移量被加到基址寄存器上 - 否则从中减去偏移量。
<P>如果设置了 B 位,传送内存的一个字节,否则传送一个字。这在汇编器中表示为给根助记符的加上后缀‘B’。
<P>W 位的解释依赖于使用的地址模式:
<UL>
<LI>对于预先变址寻址,设置 W 位强制把用做地址转换的最终地址写回基址寄存器中。(例如,传送的副作用是 Rn := Rn +/-
offset。这在汇编器中表示为给指令加上后缀 ‘!’。)
<LI>对于过后变址寻址,地址总是写回,设置 W 位指示在进行传送之前强制地址转换。这在汇编器中表示为给指令加上后缀‘T’。 </LI></UL>
<P>地址转换导致芯片告知内存系统这是一个用户模式传送,而不管此时芯片是处于用户模式中还是处于特权模式中。这是有用的,例如在写模拟器的时候:
假如一个用户模式程序一个内存区域执行了一个 STF 指令,而用户模式代码不可以写这个内存区域。如果由一个 FPA 来指令它,它将异常终止。如果由一个
FPE 来执行它,它也应该异常终止。但是 FPE
运行在一个特权模式下,所以如果它使用普通存储指令,则它不会异常终止。为了使异常终止正确工作,在一个特权模式调用它时使用 STRT
替代普通存储指令,使其如同调用自用户模式。
<P>如果使用这个指令的立即数形式,o 字段给出一个 12-bit
偏移量。如果使用了寄存器形式,则按对数据处理指令那样解码它,限制是不允许使用寄存器指令移位量。
<P>如果 R15 被用做 Rd,不修改 PSR。PC 不应该被用在 Op2 中。
<P>其他限制:
<UL>
<LI>在基址寄存器是 PC 的时候不要使用写回或过后变址。
<LI>不要使用 PC 作为给 LDRB 或 STRB 的 Rd。
<LI>在使用带有寄存器偏移量的过后变址时,不要让 Rn 和 Rm 是同一个寄存器(这样做导致不可能从异常终止中恢复)。 </LI></UL>
<P>装载使用 1S + 1N + 1I + (1S + 1N 如果改变了 PC)个周期,而存储使用 2N 个周期。
<P> </P><A name=Block>
<H3>块数据传送</H3></A><PRE>xxxx100P USWLnnnn llllllll llllllll
</PRE>
<P>典型的汇编语法: <PRE> LDMFD Rn!, {R0-R4, R8, R12}
STMEQIA Rn, {R0-R3}
STMIB Rn, {R0-R3}^
</PRE>
<P>使用这些指令来同时装载/存储多个寄存器从/到内存。使用的内存地址从在基址寄存器 Rn
中持有的值指定的内存地址要么增加要么减少地址,(可以存储基址寄存器自身),并且最终的地址可以被写回到基址寄存器中。这些指令适合于实现栈,在进入/退出一个子例程时存储/恢复寄存器的内容。
<P>U 位指示对每个寄存器地址将被 +4 (设置)所修改,还是被 -4 (清除)所修改。
<P>W 位总是指示写回。
<P>如果设置了 L 位,则指示进行一个装载操作,如果清除了,则指示存储。
<P>使用 P 位指示在每次装载/存储之前还是之后增加/减少基址寄存器(参见下面表格)。
<P>如果这个操作要装载/存储 Rl 则设置位 l。
<P>汇编器典型的用条件代码跟随助记符根,并随后用两个字母代码指示 U 和 W 位的设置。 <! center BOXED ; l l l l. ><PRE>根 意思 P U
DA 在每次存储/装载之后减少 Rn 0 0
DB 在每次存储/装载之前减少 Rn 1 0
IA 在每次存储/装载之后增加 Rn 0 1
IB 在每次存储/装载之前增加 Rn 1 1
</PRE>
<P>在实现栈的时候有更清楚的同义词: <! center BOXED ; l l. ><PRE>根 意思
EA 空升序栈
ED 空降序栈
FA 满升序栈
FD 满降序栈
</PRE>
<P>在一个空栈中,栈指针指向下一个空位置。在一个满栈中栈指针指向最顶端满位置。升序栈向高位置增长,而降序栈向低位置增长。
<P>存储的寄存器总是最低编号的寄存器在内存中在最低地址。这可以影响入栈和出栈代码。例如,如果我想把 R1-R4
压入栈中,接着每次把它们中的两个装载回来,要使它们回到原先的寄存器,对于降序栈我需要做类似下面的事: <PRE> STMFD R13!,{R1,R2,R3,R4} ;放置 R1 在内存低端,就是说在栈顶
LDMFD R13!,{R1,R2}
LDMFD R13!,{R3,R4}
</PRE>对于升序栈则是: <PRE> STMFA R13!,{R1,R2,R3,R4} ; 放置 R4 在内存高端,就是说在栈顶
LDMFA R13!,{R3,R4}
LDMFA R13!,{R1,R2}
</PRE>
<P>同义的代码如下: <! center BOXED ; c c c. ><PRE>代码 装载 存储
EA DB IA
ED IB DA
FA DA IB
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -