内存参考视频和知乎文章https://zhuanlan.zhihu.com/p/
复制内容到md编辑器应该就可读了。
> 点开头的是伪指令,例如`.file` `.global`
> 冒号结尾的是标签,例如`main:`、`_start`
寄存器
在ARM64架构下,CPU提供了33个寄存器, 其中前31个(0~30)是通用寄存器,最后2个(31,32)是专用寄存器(sp 寄存器和 pc 寄存器)
前面0~30个通用寄存器的访问方式有2种:
- 当将其作为 32bit 寄存器的时候,使用 W0 ~ W30 来引用它们。(数据保存在寄存器的低32位)
- 当将其作为 64bit 寄存器的时候,使用 X0 ~ X30 来引用它们。
第31个专用寄存器的访问方式有4种:
- 当将其作为 32bit 栈帧指针寄存器(stack pointer) 的时候,使用 WSP 来引用它。
- 当将其作为 64bit 栈帧指针寄存器(stack pointer) 的时候,使用 SP 来引用它。
- 当将其作为 32bit 零寄存器( zero register )的时候,使用 WZR 来引用它。
- 当将其作为 64bit 零寄存器( zero register )的时候,使用 ZR 来引用它。
另外需要注意的,像 FP (X29) ,LR(X30) 寄存器都不能和 SP(x31) 寄存器一样用名字来访问,而只能使用数字索引来访问它们。
其实还有第32个专用寄存器,它就是 PC ( x32)寄存器,但是在ARM的汇编文档里面说明了,你无法在汇编中使用 PC 名称的方式或者用 X32 数字索引的访问它,因为它不是给汇编用的,而是给CPU执行汇编指令时用的,它永远记录着当前CPU正在执行哪一句指令的地址。
| 寄存器 | 说明 |
| -------------- | ---------------------------- |
| X0 寄存器 | 用来保存返回值(或传参) |
| X1 ~ X7 寄存器 | 用来保存函数的传参 |
| X8寄存器 | 也可以用来保存返回值 |
| X9 ~ X28寄存器 | 一般寄存器,无特殊用途 |
| x29(FP)寄存器 | 用来保存栈底地址 |
| X30 (LR)寄存器 | 用来保存返回地址 |
| X31(SP) 寄存器 | 用来保存栈顶地址 |
| X31(ZR)寄存器 | 零寄存器,恒为0 |
| X32(PC)寄存器 | 用来保存当前执行的指令的地址 |
内存布局
一个ARM64的进行会拥有一个非常大的虚拟内存映射空间,其中又分为两大块:
- 内核地址(0xffff_ffff_ffff_ffff ~ 0xffff_0000_0000_0000范围的256TB的寻址空间),
- 用户地址 (0x0000_ffff_ffff_ffff ~ 0x0000000000000_0000范围的256TB的寻址空间) 。
这里我们只关心用户地址,其中有分为两大块:
- 栈内存( Stack),从高位向低位生长。
- 堆内存 ( Heap ), 从低位向高位生长。
其中我们知道栈内存首先是按照线程为单元的,每个线程都有自己的栈内存块,然后每个线程的栈内存又可以根据函数的调用层级关系分为不同的栈帧( Stack Frame )。
寻址模式
立即数寻址
将数据直接存放的指令中发给CPU,首先由于ARM的一条指令占了32bit,而操作码本身也要占据一些位,所以留给立即数的位数肯定不到32bit,其次并不是满足指定位数的数字都是立即数,ARM中的立即数必须可以通过某个8bit的数据经过循环右移得到。
```asm
ADD R0, R0, #1 ;R0 <- (R0 + #1)
MOV R7, #1 ;R7 <- #1
SWI 0
```
寄存器直接寻址
将寄存器中的数据用作操作数
```asm
MOV R0, R1 ;R0 <- R1
ADD R0, R1, R2 ;R0 <- (R1 + R2)
```
```asm
.global _start
_start:
LDR R0, =list;=list是list标签第一个元素的内存地址,将这个内存地址赋值给R0寄存器
.data
list:
.word 4, 5, -5, 1, 0, 2, -3
;以字为元素类型的列表
```
寄存器间接寻址
将寄存器中的数据作为主存中操作数的地址,去到相应的主存地址取得操作数,用`[R0]`表示将`R0`中的数据当作操作数的地址,`[R0]!`表示将`R0`中的数据当作操作数的地址并将操作后的结果地址给`R0`
```asm
LDR R0,[R1] ;将R1指向的数据加载到R0中
STR R0, [R1]! ;将R0存储的数据加载到R1指向的主存地址中,加载完毕R1中为操作后的地址
ADD R0,R1,[R2]
```
```asm
.global _start
_start:
LDR R0, =list;=list是list标签第一个元素的内存地址,将这个内存地址赋值给R0寄存器
LDR R1, [R0] ;将R0寄存器中数据作为内存地址,从内存中取数据赋值给R1
LDR R2, [R0, #4] ;将R0寄存器中数据加4作为内存地址,从内存中取数据赋值给R2(R0寄存器中数据不变)
LDR R2, [R0, #4]! ;将R0寄存器中数据加4作为内存地址,从内存中取数据赋值给R2。(R0寄存器中数据也加4),R2 = list[++i]
LDR R2, [R0], #4 ;将R0寄存器中数据作为内存地址,从内存中取数据赋值给R2。然后将R0寄存器中值加4, R2 = list[i++]
; LDR R2, [R0, #4]! 与 LDR R2, [R0], #4 的关系类似于++i与i++
.data
list:
.word 4, 5, -5, 1, 0, 2, -3
;以字为元素类型的列表
```
算术与CPSR标志
```asm
.global _start
_start:
MOV R0, #5
MOV R1, #7
ADD R2, R0, R1 ; R2 = R0 + R1
ADDS R2, R0, R1
ADC R2, R0, R1
SUB R2, R0, R1 ; R2 = R0 - R1
SUBS R2, R0, R1 ; R2 = R0 - R1,设置CPSR
SUBS R2, R1, R0 ; R2 = R1 - R0,设置CPSR
MUL R2, R0, R1 ; R2 = R0 * R1
.data
list:
.word 4, 5, -5, 1, 0, 2, -3
```


如图可以看出,如何解释寄存器中值是正数还是负数需要额外标志位。cpsr寄存器就存储这些标志位。
`SUB`命令是不设置`CPSR`寄存器的,`SUBS`会设置`CPSR`寄存器。当所得结果可能是负数时应该使用`SUBS`。
逻辑运算与移位
```asm
AND R2, R0, R1
ORR R2, R0, R1
EOR R2, R0, R1
MVN R0, R0
LSL ; 逻辑左移
LSR ; 逻辑右移
ROR ; ROR(循环右移)
```
`ROR`(循环右移指令把所有位都向右移,最低位复制到进位标志位和最高位。

```asm
MOV R0, #10
MOV R1, R0
LSL R1, #1
; 以上两句等价于 MOV R1, R0, LSL #1
ROR R0, #1
```
条件与分支
假设现在AX寄存器中的数是0002H,BX寄存器中的数是0003H。
执行的指令是:CMP AX, BX
执行这条指令时,先做用AX中的数减去BX中的数的减法运算。
列出二进制运算式子:

所以,运算结果是 0FFFFH
根据这个结果,各标志位将会被分别设置成以下值:
CF=1,因为有借位
OF=0,未溢出
SF=1,结果是负数
ZF=0,结果不全是零
还有AF, PF等也会相应地被设置。CMP 比较指令做了减法运算以后,根据运算结果设置了各个标志位。标志位设置过以后,0FFFFH这个减法运算的结果就没用了,它被丢弃,不保存。执行过了CMP指令以后,除了CF,ZF,OF, SF,等各个标志位变化外,其它的数据不变。
控制流指令
```asm
cmp r0, #0
beq LABEL1 ; r0==0那么向前跳转到LABEL1处执行
bne LABEL2 ; 否则向后跳转到LABEL2处执行
```
无条件转移指令:`BAL`
条件转移指令
```asm
BEQ 相等
BNE 不等
BPL 非负
BMI 负
BCC 无进位
BCS 有进位
BLO 小于(无符号数)
BHS 大于等于(无符号数)
BHI 大于(无符号数)
BLS 小于等于(无符号数)
BVC 无溢出(有符号数)
BVS 有溢出(有符号数)
BGT 大于(有符号数)
BGE 大于等于(有符号数)
BLT 小于(有符号数)
BLE 小于等于(有符号数)
```
带有分支的循环
```asm
.global _start
.equ endlist, 0xaaaaaaaa
_start:
LDR R0, =list
MOV R1, #0
LDR R2, =endlist
LDR R3, [R0]
ADD R1, R1, R3
loop:
LDR R3, [R0, #4]!
CMP R2, R3
BEQ exit
ADD R1, R1, R3
BAL loop
exit:
.data
list:
.word 1,2,3,4,5,6,7,8,9,10
```
条件指令执行
```asm
CMP R0, R1
ADDLT R2, R2, #1 ;如果R0小于R1,那么将R2加1
;类似的有MOVEGE R2, #1
```
使用LR寄存器进行分支并返回
```asm
.global _start
_start:
MOV R0, #1
MOV R1, #2
BL add2 ;存储下一个要执行的指令的地址到lr寄存器,并跳转到标签add2
MOV R3, #3
add2:
ADD R2, R0, R1
bx lr ;返回到lr保存的地址继续执行
```
从堆栈内存中保存和检索数据
```asm
.global _start
_start:
MOV R0, #1
MOV R1, #2
PUSH {R0, R1}
BL get_value
POP {R0, R1}
BAL exit
get_value:
MOV R0, #5
MOV R1, #7
ADD R2, R0, R1
BX lr
exit:
```
到此这篇ldr指令和str指令(ldr arm指令)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/36280.html