概念问题整理
1.汇编中的R0-R15是什么?
在汇编语言中,R0 到 R15 是 ARM 处理器中的通用寄存器。它们用于存储临时数据、计算结果和地址。以下是这些寄存器的常见用途:
-
R0-R3: 通常用作函数参数传递和返回值。大多数 ARM 函数的前四个参数通过这四个寄存器传递,函数返回值也通过 R0 寄存器返回。
-
R4-R11: 通常用作局部变量和临时数据。这些寄存器在函数调用期间保持值,不会被调用的函数修改(如果没有特别的保护措施的话)。
-
R12: 通常称为 IP(Intra-Procedure-call scratch register),用于在函数调用中作为临时寄存器使用。
-
R13: 通常称为 SP(Stack Pointer),指向当前的栈顶。它用于管理堆栈。
-
R14: 通常称为 LR(Link Register),用于存储函数返回地址。
-
R15: 通常称为 PC(Program Counter),存储当前执行指令的地址,并自动更新以指向下一条指令。
2.汇编中的LR是什么
在许多处理器架构中,LR 是 Link Register 的缩写,它是一个专用寄存器,主要用于存储子程序调用返回地址。
ARM 架构
在 ARM 架构中,LR 是一个专用寄存器,用于存储子程序调用返回地址。当执行一个子程序调用指令(如 BL 或 BLX)时,处理器会将返回地址存储在 LR 中。子程序执行完毕后,可以使用 BX LR 指令返回到调用该子程序的地址
3.汇编中的PUSH
汇编语言中,PUSH 指令用于将数据压入栈(stack)。栈是一种后进先出(LIFO, Last In First Out)的数据结构,PUSH 操作会将数据放入栈顶,并使栈指针(stack pointer)向下移动以指向新的栈顶位置。PUSH 指令在许多处理器架构中都有类似的功能,但具体的实现和行为可能有所不同。
PUSH 的常见用途
-
保存寄存器的值:
在函数调用之前,程序可能需要保存寄存器的值,以便在函数返回时恢复它们。这可以通过将寄存器的值 PUSH 到栈中来实现。(即使改变寄存器的值,栈内的值不变) -
函数调用和返回:
PUSH 常用于在函数调用时保存返回地址和参数,函数结束时恢复这些值。 -
局部变量:
在函数内部,局部变量可以使用栈来存储,它们在函数调用时被 PUSH 到栈中,并在函数返回时从栈中 POP 出来。; ARM汇编中的示例 PUSH {r0-r3} ; 将寄存器 r0, r1, r2, r3 的值压入栈 ; 执行一些操作 POP {r0-r3} ; 从栈中弹出值到寄存器 r0, r1, r2, r3
4.汇编中的LDR
LDR 指令用于从内存中加载数据到寄存器。在许多处理器架构中,LDR 指令的基本功能是将指定内存地址处的数据加载到指定的寄存器中。这个指令在处理器架构如 ARM 中广泛使用。
ARM 架构中的 LDR
在 ARM 架构中,LDR 指令有几种常见形式,包括加载立即数、寄存器间接寻址等。以下是一些常见用法:
(1) 基本形式:
LDR Rd, [Rn]
这个指令将地址 Rn 指向的内存位置中的数据加载到寄存器 Rd 中。
例如:
LDR R0, [R1]
这里,R1 中存储的是内存地址,LDR 将该地址处的数据加载到 R0 中。
(2)加载带偏移量:
LDR Rd, [Rn, #offset]
这个指令将地址 Rn + offset 指向的内存位置中的数据加载到寄存器 Rd 中。
例如:
LDR R0, [R1, #4]
这里,R1 中存储的是基地址,4 是偏移量,LDR 将地址 R1 + 4 处的数据加载到 R0 中。
(3)加载带寄存器间接寻址:
LDR Rd, [Rn, Rm]
这个指令将地址 Rn + Rm 指向的内存位置中的数据加载到寄存器 Rd 中。
例如:
LDR R0, [R1, R2]
这里,R1 存储基地址,R2 存储偏移量,LDR 将地址 R1 + R2 处的数据加载到 R0 中。
(4)加载字节:
LDRB Rd, [Rn]
这个指令将地址 Rn 指向的内存位置中的一个字节(8 位)加载到寄存器 Rd 中(通常扩展到 32 位)。
例如:
LDRB R0, [R1]
这里,R1 中存储的是内存地址,LDRB 将该地址处的字节数据加载到 R0 中。
(5)总结
- LDR 指令的功能:从内存中加载数据到寄存器。
- 使用形式:可以是直接从寄存器地址、带偏移量、或带寄存器间接寻址。
- 扩展指令:如 LDRB 用于加载字节数据。
- 在不同的处理器架构中,LDR 的具体实现和语法可能会有所不同,但其核心功能基本上是从内存中加载数据到寄存器。
5. 内存和寄存器的关系
内存和寄存器都是计算机系统中存储数据的组件,但它们的功能、速度和用途有所不同。以下是它们的主要区别和功能:
内存(Memory)
定义:
内存是计算机系统中用于存储程序和数据的硬件组件。内存可以分为多个类型,包括 RAM(随机存取内存)、ROM(只读内存)、Flash 存储等。
类型:
- RAM(随机存取内存):用于临时存储程序执行时的数据和指令。RAM 是易失性的,当系统断电时数据会丢失。
- ROM(只读内存):用于存储固化的程序代码(如引导程序或固件),这些数据在系统运行时是只读的,且通常在断电后仍能保持。
- Flash 存储:一种非易失性存储器,常用于存储固件、操作系统、应用程序等。Flash 存储在断电后数据仍然保持。
速度:
内存的访问速度比寄存器慢,但比外部存储(如硬盘)快。
用途:
内存用于存储正在执行的程序和数据。例如,操作系统、应用程序、和中间计算结果都存储在内存中,以便快速访问。
寄存器(Register)
定义:
寄存器是计算机处理器内部的小型、高速存储单元,用于暂时存储数据和指令。寄存器的数量和类型由处理器架构决定。
类型:
- 通用寄存器:用于存储临时数据、运算结果等。
- 专用寄存器:用于特定用途,如程序计数器(PC)、堆栈指针(SP)、链接寄存器(LR)等。
速度: - 寄存器是计算机中最快的存储设备,因为它们位于处理器内部,与处理器核心的操作速度同步。
用途:
寄存器用于存储正在处理的数据、指令、地址等,是 CPU 操作的核心部分。寄存器用于计算、数据移动、指令执行等操作。
6. 汇编中的SP
SP 是 Stack Pointer 的缩写,指的是栈指针寄存器。在计算机系统中,栈指针寄存器用于管理栈的操作。栈是一种后进先出(LIFO, Last In First Out)的数据结构,用于临时存储数据,如函数调用的返回地址、局部变量、保存的寄存器状态等。
栈指针(SP)的主要功能
1. 指向栈顶:
SP 寄存器保存当前栈顶的位置。每次向栈中压入数据(PUSH)时,SP 会调整以指向新的栈顶;每次从栈中弹出数据(POP)时,SP 会调整以指向新的栈顶。
2. 栈操作:
- 压栈(Push):将数据放入栈中。SP 通常会减小(在向下增长的栈中)以反映栈顶的新位置。
- 弹栈(Pop):从栈中取出数据。SP 通常会增大(在向下增长的栈中)以反映栈顶的新位置。
3. 函数调用:
在函数调用中,栈用来保存函数的返回地址、参数、局部变量等。SP 用于管理这些数据的存取。例如,在函数调用时,返回地址会被压入栈中,函数返回时从栈中弹出返回地址。
示例(ARM 架构)
在 ARM 架构中,栈指针寄存器通常是 SP(Stack Pointer)
PUSH {r0, r1} ; 将寄存器 r0 和 r1 的值压入栈中
; 执行一些操作
POP {r0, r1} ; 从栈中弹出值到寄存器 r0 和 r1
在这个示例中,PUSH 指令将寄存器 r0 和 r1 的值压入栈中,SP 会减少以反映栈顶的位置。POP 指令从栈中弹出值到寄存器 r0 和 r1,SP 会增加以反映栈顶的新位置。
总结
- SP(Stack Pointer):栈指针寄存器,用于管理栈的操作。
- 功能:指向栈顶,管理栈的数据存取。
- 操作:PUSH 和 POP 操作通过调整 SP 来管理栈中的数据。
7. 汇编中的CMP
CMP 指令计算两个操作数的差值,并更新处理器的标志寄存器,特别是状态寄存器中的标志位。\
ARM 中的 CMP 指令
(1)操作:
CMP 指令计算第一个操作数减去第二个操作数的结果,并更新状态寄存器的标志位。
CMP Rn, Operand
其中,Rn 是第一个操作数(寄存器),Operand 是第二个操作数(可以是寄存器、立即数或带偏移量的寄存器)。
(2)标志寄存器:
CMP 指令会更新 ARM 处理器中的状态寄存器(CPSR)中的标志位:
- 零标志(Z, Zero Flag):如果结果为零,则设置零标志。
- 进位标志(C, Carry Flag):在无符号数的比较中,如果第一个操作数小于第二个操作数,则设置进位标志(借位标志)。
- 符号标志(N, Negative Flag):结果的最高有效位(MSB)表示负数时,设置符号标志。
- 溢出标志(V, Overflow Flag):如果操作数的减法导致溢出,则设置溢出标志。
(3)条件跳转:
CMP 指令通常与条件跳转指令一起使用,根据状态寄存器中的标志位决定是否跳转。常见的条件跳转指令包括 BEQ(等于时跳转)、BNE(不等于时跳转)、BGT(大于时跳转)、**BLT(小于时跳转)**等。
MOV R0, #10 ; 将 10 加载到寄存器 R0
MOV R1, #20 ; 将 20 加载到寄存器 R1
CMP R0, R1 ; 比较 R0 和 R1(计算 R0 - R1)
BEQ equal ; 如果结果为零,则跳转到标签 'equal'
BGT greater ; 如果 R0 > R1,则跳转到标签 'greater'
BLT less ; 如果 R0 < R1,则跳转到标签 'less'
equal:
; 如果 R0 等于 R1 执行的代码
...
greater:
; 如果 R0 大于 R1 执行的代码
...
less:
; 如果 R0 小于 R1 执行的代码
...
8. 常用条件跳转指令
ARM 汇编语言中的条件跳转指令允许程序根据处理器状态寄存器(CPSR)中的标志位来决定是否执行跳转,可以根据不同的条件来改变程序的执行流程。
1. BEQ (Branch if Equal)
- 条件:如果零标志(Z)被设置,即比较结果为零(两个操作数相等)。
- 语法:BEQ label
- 用途:跳转到指定的标签,如果之前的比较操作结果为相等。
2. BNE (Branch if Not Equal)
- 条件:如果零标志(Z)未设置,即比较结果不为零(两个操作数不相等)。
- 语法:BNE label
- 用途:跳转到指定的标签,如果之前的比较操作结果不相等。
3.BGT (Branch if Greater Than)
- 条件:如果零标志(Z)未设置,且符号标志(N)与溢出标志(V)相同,即有符号数比较时第一个操作数大于第二个操作数。
- 语法:BGT label
- 用途:跳转到指定的标签,如果之前的比较操作结果表示第一个操作数大于第二个操作数。
4. BLT (Branch if Less Than)
- 条件:如果零标志(Z)未设置,且符号标志(N)与溢出标志(V)不同,即有符号数比较时第一个操作数小于第二个操作数。
- 语法:BLT label
- 用途:跳转到指定的标签,如果之前的比较操作结果表示第一个操作数小于第二个操作数。
5. BGE (Branch if Greater Than or Equal)
- 条件:如果零标志(Z)设置,或符号标志(N)与溢出标志(V)相同,即有符号数比较时第一个操作数大于或等于第二个操作数。
- 语法:BGE label
- 用途:跳转到指定的标签,如果之前的比较操作结果表示第一个操作数大于或等于第二个操作数。
6.BLE (Branch if Less Than or Equal)
- 条件:如果零标志(Z)设置,或符号标志(N)与溢出标志(V)不同,即有符号数比较时第一个操作数小于或等于第二个操作数。
- 语法:BLE label
- 用途:跳转到指定的标签,如果之前的比较操作结果表示第一个操作数小于或等于第二个操作数。
7.BHI (Branch if Higher)
- 条件:如果进位标志(C)设置,且零标志(Z)未设置,即无符号数比较时第一个操作数大于第二个操作数。
- 语法:BHI label
- 用途:跳转到指定的标签,如果之前的比较操作结果表示第一个操作数无符号数大于第二个操作数。
8.BLO (Branch if Lower)
- 条件:如果进位标志(C)未设置,即无符号数比较时第一个操作数小于第二个操作数。
- 语法:BLO label
- 用途:跳转到指定的标签,如果之前的比较操作结果表示第一个操作数无符号数小于第二个操作数。
9. BCC (Branch if Carry Clear)
- 条件:如果进位标志(C)未设置,即在无符号数比较中第一个操作数小于第二个操作数。
- 语法:BCC label
用途:跳转到指定的标签,如果进位标志未设置。
10.BCS (Branch if Carry Set)
- 条件:如果进位标志(C)设置,即在无符号数比较中第一个操作数大于或等于第二个操作数。
- 语法:BCS label
- 用途:跳转到指定的标签,如果进位标志设置。
9.汇编中的BL
在 ARM 汇编语言中,BL 是 “Branch with Link” 的缩写。BL 指令用于跳转到指定的标签或地址,同时保存当前的程序计数器(PC)值到链接寄存器(LR,Link Register)中。这个指令通常用于函数调用或子程序调用。
BL 指令的功能
1.跳转到子程序:
BL 指令跳转到指定的地址,同时将当前指令的下一条地址(即返回地址)保存到链接寄存器 LR 中。这个返回地址可以用来在子程序执行完毕后返回到调用点。
2.保存返回地址:
在跳转到目标地址之前,BL 指令将当前的 PC 值(加上指令的长度,通常为 4 字节)存储到链接寄存器 LR。这样,子程序可以通过 LR 来返回到调用点。
3.用法:
调用子程序:在执行子程序时,使用 BL 指令来跳转到子程序的起始地址,并通过 LR 保存返回地址。
返回:在子程序中,通常会使用 BX LR 或类似指令来返回到调用点,即链接寄存器中保存的地址。
; 主程序
MOV R0, #5 ; 将 5 加载到寄存器 R0
BL my_function ; 调用 my_function 子程序
; 子程序调用后继续执行的代码
; 这里是子程序定义
my_function:
PUSH {LR} ; 保存链接寄存器 (LR) 到栈中
ADD R0, R0, #1 ; 对寄存器 R0 的值进行加 1 操作
POP {LR} ; 从栈中恢复链接寄存器 (LR)
BX LR ; 返回到调用点(链接寄存器中保存的地址)
10.汇编中的EOR
EOR 是 ARM 汇编语言中的一条指令,代表 “Exclusive OR”。它用于执行按位异或操作。EOR 指令的功能是将两个操作数逐位进行异或运算,并将结果存储在目的寄存器中。异或运算(XOR)是一种逻辑运算,两个位相同则结果为 0,不同则结果为 1。
EOR 指令的语法
EOR <dest>, <src1>, <src2>
<dest>:目的寄存器,存储运算结果。
<src1>:第一个操作数寄存器。
<src2>:第二个操作数寄存器或立即数。
EOR 指令的功能
EOR 指令计算 src1 和 src2 的逐位异或,并将结果存储在 寄存器中。按位异或的规则如下:
如果两个位都相同(0 ⊕ 0 或 1 ⊕ 1),结果为 0。
如果两个位不同(0 ⊕ 1 或 1 ⊕ 0),结果为 1。
寄存器与寄存器的异或
EOR R0, R1, R2
功能:将寄存器 R1 和 R2 的值进行按位异或操作,并将结果存储在寄存器 R0 中。
寄存器与立即数的异或
EOR R0, R1, #0xFF
功能:将寄存器 R1 的值与立即数 0xFF 进行按位异或操作,并将结果存储在寄存器 R0 中。
使用场景
- 数据加密和解密:异或操作常用于简单的加密算法,如流加密中的伪随机序列生成。
- 错误检测:在 CRC 校验和等算法中使用异或操作来检测数据传输中的错误。
- 位操作:在处理特定的位模式或控制位时,异或操作可以用来翻转某些位或进行位掩码操作。
11.汇编中的LSL
LSL 是 ARM 汇编语言中的指令,代表 “Logical Shift Left”。它用于将操作数的位向左逻辑移位。逻辑左移(LSL)会将操作数的每一位向左移动指定的位数,右侧填充零。
LSL 指令的语法
LSL <dest>, <src>, <shift>
<dest>:目的寄存器,存储移位后的结果。
<src>:源寄存器,提供要移位的数据。
<shift>:移位量,指定要移动的位数,可以是立即数或者寄存器值。
功能
逻辑左移:将源寄存器 的值向左移位 位,右侧填充零,并将结果存储在目的寄存器 中。
1. 寄存器与立即数的左移
LSL R0, R1, #2
功能:将寄存器 R1 的值逻辑左移 2 位,结果存储在寄存器 R0 中。
假设 R1 中的值是 0b0001 0101(十进制 21),左移 2 位后变成 0b0101 0100(十进制 84),结果存储在 R0 中。
2.寄存器与寄存器的左移
LSL R0, R1, R2
功能:将寄存器 R1 的值逻辑左移 R2 寄存器中指定的位数,结果存储在寄存器 R0 中。
如果 R1 的值是 0b0001 0101,R2 的值是 3,左移 3 位后变成 0b1010 1000,结果存储在 R0 中。
使用场景
- 位操作:逻辑左移通常用于位操作,如计算位字段、生成掩码、处理数据等。
- 乘法操作:逻辑左移等效于乘以 2 的指定次方。例如,左移 3 位相当于乘以 2^3(即 8)。
- 数据处理:在处理数字、编码和解码数据时,逻辑左移操作可以用于调整数据格式和位置。
MOV R1, #5 ; 将 5 加载到 R1 寄存器 (二进制 0000 0101)
LSL R0, R1, #3 ; 将 R1 的值逻辑左移 3 位,结果存储在 R0 中
MOV R1, #5:将值 5(即 0b0000 0101)加载到寄存器 R1。
LSL R0, R1, #3:将寄存器 R1 中的值(0b0000 0101)向左逻辑移位 3 位,结果是 0b0010 1000(即 40),存储在寄存器 R0 中。
12. 汇编中的RRX
RRX 是 ARM 汇编语言中的一条指令,用于将一个寄存器的值通过循环右移(Rotate Right with Extend)操作来处理。RRX 指令特别用于对寄存器中的数据进行循环右移,并且会考虑进位标志(C标志)的值。
RRX 指令的功能
操作:将一个寄存器的值循环右移一位,并将位移后的最低位与进位标志(C 标志)结合。
结果:将结果存储回目标寄存器。
语法
RRX <dest>, <src>
<dest>:目标寄存器,存储操作结果。
<src>:源寄存器,提供操作的数据。
操作说明
1. 位移:
将寄存器<src>
的值右移一位,最低位(第 0 位)会移到进位标志(C 标志)的位置。
位移后的最高位将被移到最低位。
2. 进位标志:
位移后的最低位将取代原来进位标志的值。
进位标志在操作后会被更新,反映新的最低位值。
假设寄存器的值如下:
- R1 的值是 0b1010 1100(即 172)。
- 进位标志 C 的值是 1(假设进位标志为 1)。
使用 RRX 指令进行操作:
RRX R0, R1
操作:将 R1 的值 1010 1100 右移一位,并将进位标志 1 放到新的最低位位置:
右移与进位操作:1010 1100 右移一位得到 1101 0110。
(对 R1 的值 1010 1100 进行右移一位操作。右移一位意味着每个位都会移动到它右边的位置,最右边的位将丢弃对应0101 0110;最左边的位置将填入一个新的位(该位为进位标志的值)对应1101 0110。)
结果:R0 的值是 0b1101 0110(即 214),并且进位标志 C 会被更新为 0(因为 R1 的最低位是 0)。
使用场景
- 加密算法:在一些加密算法中,RRX 用于处理数据的循环右移操作。
- 数据处理:当需要对数据进行位循环操作并考虑进位标志时,可以使用 RRX。
13.汇编中的BIC
BIC 是 ARM 汇编语言中的一条指令,代表 “Bit Clear”。按位与操作用于清除寄存器中某些特定位的值。具体来说,它的作用是将寄存器中的位与一个掩码进行按位与操作,同时掩码中为 1 的位会使寄存器中的相应位被清除为 0,而掩码中为 0 的位不会改变寄存器中的相应位。
BIC 指令的语法
BIC <dest>, <src1>, <src2>
<dest>:目的寄存器,存储操作结果。
<src1>:源寄存器,提供操作的目标数据。
<src2>:源寄存器或立即数,提供掩码数据。
功能
- 如果掩码的位为 1,则目标寄存器的对应位被清除(即置为 0)。
- 如果掩码的位为 0,则目标寄存器的对应位保持不变。
-
假设寄存器 R0 的初始值是 0xAB,即:
R0 = 0xAB // 二进制表示为 10101011
-
假设我们要使用掩码 0xF0(即二进制的 11110000),执行 BIC 指令:
BIC R0, R0, #0xF0
-
掩码 0xF0 的按位非(取反)是 0x0F(即二进制的 00001111)。
-
按位与 R0 和按位非掩码 0x0F:
10101011 (R0) AND 00001111 (NOT 0xF0) = 00001011
在这个操作中,0xF0 中的 1 位使得 R0 对应的位被清零,而 0 位则保持 R0 中的值不变。
用途
- 清除特定标志位:在处理状态寄存器或控制寄存器时,常常需要清除某些位,以便重置标志或控制状态。
- 禁用特定功能:例如,在设置寄存器时,有时需要禁用特定的功能或选项,通过将相应的位清零来实现。
14.汇编中的SUB
SUB 是 ARM 汇编语言中的一条指令,用于执行简单的减法操作。与 SUBS 不同,SUB 指令不更新条件标志(N、Z、C、V),它只是执行减法操作并将结果存储在目标寄存器中。
SUB 指令的功能
SUB 指令用于从一个寄存器的值中减去另一个寄存器或立即数的值,并将结果存储在目标寄存器中。它不对条件标志进行任何修改,因此不会影响程序的条件判断逻辑。
语法
SUB <dest>, <src1>, <src2>
<dest>:目标寄存器,存储操作结果。
<src1>:第一个源寄存器,被减数。
<src2>:第二个源寄存器或立即数,减数。
1: 寄存器之间的减法
MOV R1, #10 ; R1 = 10
MOV R2, #5 ; R2 = 5
SUB R0, R1, R2 ; R0 = R1 - R2 (即 10 - 5 = 5)
操作:
R1 的值是 10。
R2 的值是 5。
SUB R0, R1, R2 计算 10 - 5,结果是 5。
R0 的值是 5。
标志:
SUB 不会更新条件标志(N、Z、C、V)。
2: 使用立即数进行减法
MOV R1, #7 ; R1 = 7
SUB R0, R1, #3 ; R0 = R1 - 3 (即 7 - 3 = 4)
操作:
R1 的值是 7。
立即数是 3。
SUB R0, R1, #3 计算 7 - 3,结果是 4。
R0 的值是 4。
标志:
SUB 不会更新条件标志(N、Z、C、V)。
使用场景
- 数据处理:SUB 指令常用于执行普通的减法计算,例如在算法中减去某个值或调整变量。
- 无条件操作:当不需要对条件标志进行检查或更新时,可以使用 SUB 指令以减少不必要的标志更新。
15.汇编中的SUBS
在 ARM 汇编语言中,SUBS 是一条用于执行减法操作的指令,同时更新条件标志(CPSR 寄存器中的 N、Z、C 和 V 标志)。SUBS 指令执行减法操作并影响标志寄存器,这在后续的条件判断或跳转中非常有用。
SUBS 指令的功能
SUBS 指令用于从一个寄存器的值中减去另一个寄存器或立即数的值,并将结果存储在目标寄存器中。与 SUB 指令不同的是,SUBS 会更新标志寄存器,用于条件判断或其他控制逻辑。
语法
SUBS <dest>, <src1>, <src2>
<dest>:目标寄存器,存储操作结果。
<src1>:第一个源寄存器,被减数。
<src2>:第二个源寄存器或立即数,减数。
标志寄存器更新
SUBS 指令在执行减法操作后会更新以下条件标志:
- N(Negative):如果结果是负数,则 N 标志被置为 1,否则为 0。
- Z(Zero):如果结果是 0,则 Z 标志被置为 1,否则为 0。
- C(Carry):表示借位。SUBS 指令的 C 标志用于指示是否有借位产生。如果减法操作结果无借位,则 C 标志被置为 1,否则为 0。
- V(Overflow):表示溢出。SUBS 指令的 V 标志用于指示减法操作是否产生溢出。如果结果超出了表示范围,则 V 标志被置为 1,否则为 0。
1: 寄存器之间的减法
MOV R1, #10 ; R1 = 10
MOV R2, #5 ; R2 = 5
SUBS R0, R1, R2 ; R0 = R1 - R2 (即 10 - 5 = 5)
操作:
R1 的值是 10。
R2 的值是 5。
SUBS R0, R1, R2 计算 10 - 5,结果是 5。
R0 的值是 5。
标志更新:
- N 标志:5 不是负数,所以 N 标志是 0。
- Z 标志:结果不是 0,所以 Z 标志是 0。
- C 标志:减法操作没有借位,C 标志是 1。
- V 标志:没有溢出,V 标志是 0。
2: 使用立即数进行减法
MOV R1, #7 ; R1 = 7
SUBS R0, R1, #3 ; R0 = R1 - 3 (即 7 - 3 = 4)
操作:
R1 的值是 7。
立即数是 3。
SUBS R0, R1, #3 计算 7 - 3,结果是 4。
R0 的值是 4。
标志更新:
- N 标志:4 不是负数,所以 N 标志是 0。
- Z 标志:结果不是 0,所以 Z 标志是 0。
- C 标志:减法操作没有借位,C 标志是 1。
- V 标志:没有溢出,V 标志是 0。
使用场景
- 条件跳转:SUBS 常用于计算后更新条件标志,以便后续使用条件跳转指令(如 BEQ、BNE)来控制程序的流向。
- 标志测试:通过更新标志寄存器,SUBS 可以帮助测试特定条件,如结果是否为负数或零,是否有借位,等。
16.汇编中的BX
BX 是 ARM 汇编语言中的一条指令,用于跳转到指定的地址。BX 的全称是 “Branch and Exchange”(分支并交换)。它不仅可以用来跳转到指定的地址,还可以在跳转过程中切换处理器的执行模式(例如,ARM 模式与 Thumb 模式之间的切换)。
BX 指令的功能
- 功能:BX 指令用于将程序控制流转移到寄存器中指定的地址,并且可以选择性地切换处理器的执行模式。
- 处理器模式切换:BX 指令的地址寄存器的最低位(最低有效位)可以用来指示是否切换到 Thumb 模式(如果最低位是 1 则切换到 Thumb 模式,最低位是 0 则保持在 ARM 模式)。
语法
BX <reg>
<reg>:寄存器,包含要跳转到的地址。
1: 基本跳转
MOV R0, #0x2000 ; 将地址 0x2000 加载到 R0 中
BX R0 ; 跳转到 R0 中指定的地址
MOV R0, #0x2000 将地址 0x2000 加载到寄存器 R0 中。
BX R0 将程序跳转到 0x2000 地址执行。
2: 模式切换
MOV R0, #0x2000 ; 将地址 0x2000 加载到 R0 中
ORR R0, R0, #1 ; 将最低位设置为 1,表示切换到 Thumb 模式
BX R0 ; 跳转到 R0 中指定的地址并切换到 Thumb 模式
MOV R0, #0x2000 将地址 0x2000 加载到寄存器 R0 中。
ORR R0, R0, #1 设置 R0 的最低位为 1,表示目标地址 0x2000 的最低有效位是 1,意味着执行跳转时将切换到 Thumb 模式。
BX R0 跳转到 0x2001 地址(最低有效位是 1),并且切换到 Thumb 模式。
使用场景
- 函数调用:BX 可以用于跳转到一个函数或子例程的地址。
- 模式切换:在 ARM 处理器中,BX 可以用来在 ARM 模式和 Thumb 模式之间切换。
- 动态跳转:BX 可以用于根据程序状态动态决定跳转目标地址,适用于实现函数指针或动态调用。
17.汇编中的LSR
LSR(Logical Shift Right)指令在 ARM 汇编语言中用于将寄存器中的值进行逻辑右移操作。它会将所有的位向右移动,并在左边填充零。这个操作会影响目标寄存器的内容,并且更新进位标志(Carry flag)。
语法
LSR destination, source, shift_amount
- destination:目标寄存器,存储移位后的结果。
- source:源寄存器,包含要移位的数据。
- shift_amount:移位的位数,可以是一个立即数或者由另一个寄存器提供。
详细操作
1. 移位操作:
- 将 source 寄存器中的每一位右移 shift_amount 位。
- 在左边填充零。
2. 进位标志:
- 移出的位(即右移操作中被移出的最低位)会存入进位标志(Carry flag)。
3.例子
假设寄存器 R0 的初始值为 0b11001100,执行 LSR R0, R0, #2
后:
-
初始值:R0 = 0b11001100
-
右移 2 位:
- 移位结果:0b00110011
- 移出的最低位(LSB):0b00(最低的 2 位),因此进位标志会被设置为 0(因为移出的最低位是 0)。
-
结果:寄存器 R0 的新值为 0b00110011,进位标志被更新为 0。
MOV R0, #0xCC ; R0 = 11001100 (0b11001100)
LSR R0, R0, #2 ; R0 = 00110011 (0b00110011),进位标志 = 0
4. 应用
- 除法运算:逻辑右移可以用来执行无符号整数的除法运算。例如,右移 1 位相当于除以 2,右移 2 位相当于除以 4,依此类推。
- 位字段操作:在处理位字段或清理位时,逻辑右移是一个常用操作。
注意事项
- 符号扩展:LSR 是逻辑右移,不会考虑符号位,始终在左边填充零。对于有符号整数的右移操作,应使用算术右移指令(如 ASR)。
- 移位量:shift_amount 可以是立即数或由另一个寄存器提供的值。shift_amount 的有效范围通常在 0 到 31 之间,超出这个范围的移位量会被模 32 处理。
18. 汇编中的ROR
ROR(Rotate Right)是 ARM 架构中的一种指令,用于将寄存器的位向右旋转。它的功能是将寄存器中的所有位向右移动,并将移出的最低位填入寄存器的最高位,同时移入的位从最低位开始。这种操作不会影响进位标志,和RRX(Rotate Right with Extend)指令不同,ROR不会使用或影响进位标志。
ROR 指令的功能
假设寄存器 R0 的值为 0b11001010,我们对其执行 ROR 操作:
ROR R0, R0, #1
:将 R0 右旋转一位。
示例
初始状态:
R0 = 0b11001010
执行 ROR R0, R0, #1
:
右旋转一位的结果是 0b01100101
旋转后的结果是将原来的最低位 0 移入到最高位的位置,同时其余位右移一位。
.section .text
.global rotate_right_example
rotate_right_example:
MOV R0, #0b11001010 ; 将初始值加载到R0
ROR R0, R0, #1 ; 将R0的值右旋转1位
; R0的值现在是0b01100101
BX LR ; 返回
主要区别
ROR:右旋转寄存器的内容,移出位进入最高位。
RRX:右旋转寄存器内容,并将进位标志值移入最高位,移出位存入进位标志。
这两者都可以用于位操作,但适用于不同的情况,具体取决于是否需要处理进位标志
19.汇编中的LDRH
LDRH 是 ARM 汇编语言中的一条指令,用于从内存中加载一个半字(16 位)到寄存器中。
LDRH <寄存器>, [<基址寄存器>, #<偏移量>]
- <寄存器>:要加载半字的目标寄存器。
- <基址寄存器>:包含基地址的寄存器。
- <偏移量>:可选的偏移量,会加到基地址上。
LDRH R0, [R1, #4]
这条指令将会从内存地址 R1 + 4 处加载一个 16 位的值到寄存器 R0 中。
20.汇编中的RBIT
在ARM汇编语言中,RBIT指令是“Reverse Bits”的缩写,用于反转一个寄存器中所有位的顺序。该指令将指定寄存器中的每一位都反转,即把第0位和第31位交换,第1位和第30位交换,以此类推。
以下是一个使用RBIT指令的示例:
MOV R0, #0x12345678 ; R0中存放的是0x12345678
RBIT R1, R0 ; R1中存放的是0x1E6A2C48,即0x12345678的位反转结果
在这个例子中:
- MOV R0, #0x12345678将十六进制数0x12345678加载到寄存器R0中。
- RBIT R1, R0将R0中的值进行位反转,并将结果存储在寄存器R1中。
位反转的结果是:
- 输入值0x12345678的二进制表示为00010010001101000101011001111000。
- 反转后的二进制表示为00011110011010100010110001001000,对应的十六进制值为0x1E6A2C48。