SWP
它们可能是能获得的最有用的指令。其他指令都操纵寄存器,所以必须把数据从内存装载寄存器并把寄存器中的数据存储到内存中。
传送单一数据
使用单一数据传送指令(STR 和 LDR)来装载和存储单一字节或字的数据从/到内存。寻址是非常灵活的。
首先让我们查看指令格式:
LDR{条件} Rd, <地址> STR{条件} Rd, <地址> LDR{条件}B Rd, <地址> STR{条件}B Rd, <地址>
这些指令装载和存储 Rd 的值从/到指定的地址。如果象后面两个指令那样还指定了‘B’,则只装载或存储一个单一的字节;对于装载,寄存器中高端的三个字节被置零(zeroed)。
地址可以是一个简单的值、或一个偏移量、或者是一个被移位的偏移量。可以还可以把合成的有效地址写回到基址寄存器(去除了对加/减操作的需要)。
各种寻址方式的示例:
译注:下文中的 Rbase 是表示基址寄存器,Rindex 表示变址寄存器,index 表示偏移量,偏移量为 12 位的无符号数。用移位选项表示比例因子。标准寻址方式 - 用 AT&T 语法表示为 disp(base, index, scale),用 Intel 语法表示为 [base + index*scale + disp],中的变址(连带比例因子)与偏移量不可兼得。
STR Rd, [Rbase] 存储 Rd 到 Rbase 所包含的有效地址。 STR Rd, [Rbase, Rindex] 存储 Rd 到 Rbase + Rindex 所合成的有效地址。 STR Rd, [Rbase, #index] 存储 Rd 到 Rbase + index 所合成的有效地址。 index 是一个立即值。 例如,STR Rd, [R1, #16] 将把 Rd 存储到 R1+16。 STR Rd, [Rbase, Rindex]! 存储 Rd 到 Rbase + Rindex 所合成的有效地址, 并且把这个新地址写回到 Rbase。 STR Rd, [Rbase, #index]! 存储 Rd 到 Rbase + index 所合成的有效地址, 并且并且把这个新地址写回到 Rbase。 STR Rd, [Rbase], Rindex 存储 Rd 到 Rbase 所包含的有效地址。 把 Rbase + Rindex 所合成的有效地址写回 Rbase。 STR Rd, [Rbase, Rindex, LSL #2] 存储 Rd 到 Rbase + (Rindex * 4) 所合成的有效地址。 STR Rd, place 存储 Rd 到 PC + place 所合成的有效地址。
你当然可以在这些指令上使用条件执行。但要注意条件标志要先于字节标志,所以如果你希望在结果是等于的时候装载一个字节,要用的指令是 LDREQB Rx, <address>
(不是 LDRBEQ...
)。
如果你指定预先变址寻址(这里的基址和变址都在方括号中),用是否存在‘!’来控制写回操作。上面的第4和第5个例子中使用了这个标志。你可以使用它来在内存中自动正向或反向移动。一个字符串打印例程将变成:
.loop LDRB R0, [R1, #1]! SWI "OS_WriteC" CMP R0, #0 BNE loop
而不是:
.loop LDRB R0, [R1] SWI "OS_WriteC" ADD R1, R1, #1 CMP R0, #0 BNE loop
对于过后变址寻址‘!’是无效的(这里的变址在方括号外面,比如上面的例子6),因为写回是暗含的。
如同你见到的那样,变址可以被移位来实现比例缩放。除此之外,可以从基址上减去偏移量。在这种情况下,你可以使用如下代码:
LDRB R0, [R1, #-1]
尽管你可以存储或装载 PC,但你不可以用装载或存储指令来修改 PSR。要装载一个被存储的‘状态’并正确的恢复它,请使用:
LDR R0, [Rbase] MOVS R15, R0
假如你在有特权的模式下,MOVS 将导致 PSR 的位被更改。
对 PC 使用 MOVS
不遵从 32-bit 体系,你需要使用 MRS 和 MSR 来处理 PSR。
依照 ARM 汇编手册:
译注:下文所叙述内容针对的是小端字节序配置,对大端字节序配置在手册中另有专门叙述。
- 如果提供的地址在一个字边界上,则字节装载(LDRB)使用在 0 至 7 位上的数据,如果在一个字地址加上一个字节上,则使用 8 至 15 位,以此类推。选择的字节被放入目标寄存器的低端 8 位中,并把寄存器中其余的位用零填充。
- 字节存储(STRB)在数据总线上重复源寄存器的的低端 8 位 4 次。由外部的内存系统来激活适当的字节子系统来存储数据。
- 字装载(LDR)或字存储(STR)将生成一个字对齐的地址。使用一个非字对齐的地址将有不明显和未规定的结果。实际上提示的是你不能使用 LDR 从一个非对齐的地址装载一个字。
传送多个数据
使用多数据传送指令(LDM 和 STM)来装载和存储多个字的数据从/到内存。
LDM/STM 的主要用途是把需要保存的寄存器复制到栈上。如我们以前见到过的 STMFD R13!, {R0-R12, R14}
。
指令格式是:
xxM{条件}{类型} Rn{!}, <寄存器列表>{^}
‘xx’是 LD 表示装载,或 ST 表示存储。
再加 4 种‘类型’就变成了 8 个指令:
栈 其他 LDMED LDMIB 预先增加装载 LDMFD LDMIA 过后增加装载 LDMEA LDMDB 预先减少装载 LDMFA LDMDA 过后减少装载 STMFA STMIB 预先增加存储 STMEA STMIA 过后增加存储 STMFD STMDB 预先减少存储 STMED STMDA 过后减少存储
汇编器关照如何映射这些助记符。注意 ED 不同于 IB;只对于预先减少装载是相同的。在存储的时候,ED 是过后减少的。
FD、ED、FA、和 EA 指定是满栈还是空栈,是升序栈还是降序栈。一个满栈的栈指针指向上次写的最后一个数据单元,而空栈的栈指针指向第一个空闲单元。一个降序栈是在内存中反向增长(就是说,从应用程序空间结束处开始反向增长)而升序栈在内存中正向增长。
其他形式简单的描述指令的行为,意思分别是过后增加(Increment After)、预先增加(Increment Before)、过后减少(Decrement After)、预先减少(Decrement Before)。
RISC OS 使用传统的满降序栈。在使用符合 APCS 规定的编译器的时候,它通常把你的栈指针设置在应用程序空间的结束处并接着使用一个 FD (满降序 - Full Descending)栈。如果你与一个高级语言(BASIC 或 C)一起工作,你将别无选择。栈指针(传统上是 R13)指向一个满降序栈。你必须继续这个格式,或则建立并管理你自己的栈(如果你是死硬派人士那么你可能喜欢这样做!)。
‘基址’是包含开始地址的寄存器。在传统的 RISC OS 下,它是栈指针 R13,但你可以使用除了 R15 之外的任何可获得的寄存器。
如果你想把复制操作后栈顶的当前的内存地址保存到栈指针中,可以寄存器按从最低到最高的编号次序与到从低端到高端的内存之间传送数据。并且因为用指令中的一个单一的位来表示是否保存一个寄存器,不可能指定某个寄存器两次。它的副作用是不能用下面这样的代码:
STMFD R13!, {R0, R1} LDMFD R13!, {R1, R0}
来交换两个寄存器的内容。
提供了一个有用的简写。要包含一个范围的寄存器,可以简单的只写第一个和最后一个,并在其间加一个横杠。例如 R0-R3
等同与 R0, R1, R2, R3
,只是更加整齐和理智而已...
在把 R15 存储到内存中的时候,还保存了 PSR 位。在重新装载 R15 的时候,除非你要求否则不恢复 PSR 位。要求的方法是在寄存器列表后跟随一个‘^’。
STMFD R13!, {R0-R12, R14} ... LDMFD R13!, {R0-R12, PC}
这保存所有的寄存器,做一些事情,接着重新装载所有的寄存器。从 R14 装载 PC,它由一个 BL 或此类指令所设置。不触及 PSR 标志。
STMFD R13!, {R0-R12, R14} ... LDMFD R13!, {R0-R12, PC}^
这保存所有的寄存器,做一些事情,接着重新装载所有的寄存器。从 R14 装载 PC,它由一个 BL 或此类指令所设置。变更 PSR 标志。
警告: 这些代码不遵从 32 bit 体系。你需要使用 MRS 和 MSR 来处理 PSR,你不能使用‘^’后缀。
注意在这两个例子中,R14 被直接装载到 PC 中。这节省了对 MOV(S) R14 到 R15 中的需要。
警告: 使用 MOVS PC,... 不遵从 32 bit 体系。你需要使用 MRS 和 MSR 来处理 PSR。
SWP : 单一数据交换
(Swap)
SWP{条件}{B} <dest>, <op 1>, [<op 2>]
SWP
将:
- 从操作数 2 所指向的内存装载一个字并把这个字放置到目的寄存器中。
- 把寄存器操作数 1 的内容存储到同一个地址中。
如果目的和操作数 1 是同一个寄存器,则把寄存器的内容和给定内存位置的内容进行交换。
如果提供了 B
后缀,则将传送一个字节,否则传送一个字。