ARM A R M cortex-a53 8个CPU 1.4GHZ arm体系结构与编程 svc fiq irq undef abort user system
特权模式与非特权模式 arm 汇编指令
arm中的寄存器有 37个, 通用寄存器 31 个, 6个状态寄存器, 其中cpsr为程序状态寄存器, 5个为 spsr
获取干净的源码 : make clean
编译: make
链接脚本: shell.lds
MakeFile 目标: 依赖
编译好后, 放在板子上执行:
setenv ipaddr 192.168.1.6
saveenv
修改pc的IP
sentenv serverip 192.168.6.1
saveenvtftp 48000000 /ftfpboot/shell.bin
执行: go 0x48000000仿真软件 : qemu, 运行在pc机上, 模拟在ARM执行指令的过程
qemu的安装: sudo apt install qemu
重新编译程序: arm-cortex_a9-linux-gnueabi-as add.o -o add.o -g arm-cortex_a9-linux-gnueabi-ld add.o -o add
启动 qemu 服务端
qemu-arm -g 1234 add
启动客户端调试程序 arm-cortex_a9-linux-gnueabi-gdb add (gdb) 1 (gdb) target remote localhost:1234 (gdb) b 7 (gdb) info reg (gdb) n (gdb) info reg ro (gdb) info reg r1
按位与: and <cond> {s} <Rd>, <Rs>, <oprand>
eg: and r0, r1, r2
@ r0 = r1&r2
eg: and r0, r1, r2 @ r0 = r1&8
eg: and r0, r1, r2, lsl #3
@r0 = r1 & (r2 << 3)
按位或: orr <cond> {s} <Rd>, <Rs>, <oprand>
eg: orrgts r0, r1, r2
按位异或: eor <cond> {s} <Rd>, <Rs>, <oprand>
eg: eor r0, r0, #8
@ r0 = r0 ^ 8
练习: 如何取反 32bit 整形变量的 bit15?
mov r1, #1
eor r0, r0, r1, lsl, #15
BIC指令: bic <cond> {s} <Rd>, <Rs>, <oprand>
第二个操作数对应的bit位为1, 结果为0
第二个操作数对应的bit位为0, 结果不变
eg : bic r0, r0, #0x08
@将r0的bit3位清0
练习: 如何清零 32bit 整形变量的 bit17, 其他位不变?
mov r1, #1
bic r0, r0, r1, lsl #7
4.比较测试指令: cmp {cond} <Rn>, <operand>
特点:
cmp r1, #0x10 @alu_out = r1 - 0x10
@r1 > 0x10 C = 1
@r1 == 0x10, Z = 0
@r0 < 0x10 C = 0>
tst {cond} <Rn>, <oprand>
tst r0, #0x08 @测试 r0 的 bit3 是否为 3
@alu_out = r0 & 0x08
@如果为0, Z = 1
@如果非0, Z = 0
teq {cond} <Rn>, <oprand>
teq r0, #0x08 @测试r0 和 0x08 是否相等
@ alu_out = r0 ^ 0x08
@相等 Z = 1
@不想等 Z = 0
ARM中所有的运算都是在通用寄存器中完成的, 操作的对象是 通用寄存器 r0~r15
mov r1, 0x48000000
ldr r0, [r1] @ 将内存 0x48000000 开始的 4byte 加载到 r0 中
@ r0 = *((int *) 0x48000000)
ldrb r0, [r1] @ 将内存 0x48000000 开始的 1byte 加载到 r0 中
@ r0 = *((unsigned char *) 0x48000000)
ldrsb r0 , [r1] @ 将内存 0x48000000 开始的 1byte 加载到 r0 中, r0中的高 24bit 补符号位
ldrh r0 , [r1] @ 将内存 0x48000000 开始的 2byte 加载到 r0 中, r0 的高16bit 补0
ldrsh r0 , [r1] @ 将内存 0x48000000 开始的 2byte 加载到 r0 中, r0 的高16bit 补符号位
ldr r0, [r1] @ [r1] ---> r0
-------------------------------
ldr r0, [r1, #0x08] @ [r1 + 0x08] --> r0
ldr r0, [r1 + r2] @ [r1 + r2] --> r0
ldr r0, [r1, r2, lsl #2] @ [r1 + r2 << 2] --> r0
-------------------------------------------------------
ldr r0, [r1, #0x08]! @ [r1 + 0x08] ---> r0 r1 = r1 + 0x08 加!会更新基址
ldr r0, [r1, r2]! @ [r1+ r2] --> r0 r1 = r1 + r2
ldr r0, [r1, r2, lsl #2]! @ [r1+r2*4] --> r0 r1 = r1 + r2*4
---------------------------------------------------------------、
ldr r0, [r1], #0x08 @[r1] --> r0 r1 = r1+o0x08
ldr r0, [r1] r2 @ [r1] --> r0 r1 = r1 + r2
ldr r0, [r1], r2, lsl #2 @ [r1] --> r0, r1 = r1 + r2*8
--------------------------------------------------------
2.存储指令 str 从寄存器 ---> 内存
mov r1, 0x48000000
str r0, [r1] @将 r0的 4Byte 数据 写入到内存0x48000000中
strb r0, [r1] @将 r0的(低 8位) 1Byte 数据 写入到内存0x48000000中
strh r0, [r1] @将 r0的(低16位) 1Byte 数据 写入到内存0x48000000中
str r0, [r1] @ r0 ----> r1
str r0, [r1+0x08] @ r0 ----> r1+0x08
str r0, [r1, r2] @ r0 ----> [r1 + r2]
str r0, [r1, r2, lsl #2] @ r0 -----> [r1 + r2 *4]
----------------------------------------------------------
str r0, [r1+0x08]! @ r0 ---> [r1 + 0x08] r1 = r1 + 0x08
str r0, [r1]!, r2 @ r0 ---> [r1 + r2] r1 = r1 + r2
str r0, [r1]!, r2, lsl #2 @ r0 ---> [r1 + r2*4] r1 = r1 + r2*4
-------------------------------------------------------------------
str r0, [r1], #0x08 @ r0 ---> [r1] r1 = r1 + 0x08
str r0, [r1], r2 @ r0 ---> [r1] r1 = r1 + r2
str r0, [r1], r2, lsl #2 @r0 ----> [r1] r1 = r1 + r2*4
练习: memcpy, 内存0x8e00开始的64字节数据拷贝到0x8f00开始的内存空间:
.text
.code 32
.globel _start
_start:
mov r0, #0x00008e00
mov r1, #1
mov r2, #16
//init memory
loop:
str r1, [r0], #4
subs r2, r2, #1
bne loop
mem_cpy:
mov r0, #0x00008e00
mov r1, #0x00008f00
mov r2, #16
mem_cpy_loop:
ldr r3, [r0], #4
str r3, [r1], #4
subs r2, r2, #1
bne mem_cpy_loop
mem_cpy_ok:
b .
注意: 满栈和空栈指的是指针上面或下面的空间有没有数据, 或者说, 接下来要存数据的时候, 先放数据还是先改变 栈指针 sp 的值.
ldmxx sp!, {r0~r5, r8}
@将 sp 指向的栈空间数据加载到r0, r1, r2, r3, r4, r5, r8中
XX: FD FA ED DA
stmxx sp!, {r0~r5, r8}
@将 r0, r1, r2, r3, r4, r5, r8 中的数据加载到 sp 指向的栈空间中
XX: FD FA ED DA
stmfd sp!, {r0~r5, r8}
等价于 push {r0~r5, r8}
ldmfd sp!, {r0~r5, r8}
等价于 pop {r0~r5, r8}
按满减栈压入栈, 也按满减栈弹出数据, 就可以恢复寄存器
局部变量如何在栈里面分配空间? C语言的运行离不开栈:
#include <stdio.h>
int add(int x, int y, int z){
return x + y + z;
}
int main(void){
int a = 10;
int b = 20;
int c = 30;
int ret = add(a, b, c);
return 0;
}
.code 32
.global_start
.text
_start:
@ mov r0, @0x1ff
ldr r0, =0x1ff //伪指令
ldr r0, [pc]
add r3, r4, r5
b.
.word 0x1ff
.end
ldr r1, =text
//将立即数本身放到r1中去
ldr r1, test
//将立即数指向的内存中的数据放到r1
2. nop伪指令
作用: mov r0, r0
3. adr伪指令
小范围的地址加载指令
忽略
汇编文件中以"."开头的都是伪操作, 伪操作是给汇编器使用的, 一旦编译完成就消失
eg: .text .bss .equ .data .global .extern .byte .word .string .ascii eg: .equ NUM, #20 //类似于C语言的宏 eg: .space 1024 //分配1024byte 的空间 eg: .code 32 == .arm .code16 == .thumb eg: .section //自定义段
作业: 用汇编语言实现: strcmp(比较两个字符串) r0 <---- "abcefg" ldrb r2, [r0], #1 r1 <---- "aabced" ldrb r3, [r1], #1 cmp r2, r3 beq
本文章使用limfx的vscode插件快速发布