常用-汇编指令

为什么要学汇编程序:在bootloard和linux内核的初始化中都要用到汇编程序。还有一个就是汇编程序的效率更高。
-----------------------------------------
arm汇编的分类:arm标准汇编(windows下ADS),GNU汇编(linux平台)。
---------------------------------------------------
汇编程序的框架: 简化之后的框架:
.section.data .text
<初始化数据> .globle _start
.section.bss \_start:
<未初始化的数据> <汇编代码>
.section.text
.global _start
_start:
<汇编代码>
--------------------------------------------------------
建立汇编程序编程环境:
cd/home/smb/program/
mkdir start cd start
vim start.S
.text#表明是代码段
.globle _start#全局的标号
_start #表明程序的入口
move r1,#1
move r2,#2
move r3,#3
vim
因为我们仅仅只是在内存中调试这个程序,所以我们只要elf格式的文件。不使用链接器脚本的方法
all:start.o
arm-linux-ld -Ttext 0x50008000 -o start.elf
#指定代码段的起始地址
%.o : %.S
arm-linux-gcc -g -o $@ -c $^
clean:
rm *.o *.elf
--------------------------------------------------------
arm指令分类学习:
1.算术和逻辑指令.标准的为大写
mov :将一个立即数或通用寄存器另外一个寄存器中。
mov <dest>,<op_1> //op_为立即数或通用寄存器,dest只为一个通用寄存器
.text
.global _start
_start
@mov example注释
mov r1,#8
mov r2,r1
mov r3,#10
--------------------------------------------------
mvn:将一个立即数或通用寄存器中的值取反后传到另一个目的寄存器中。十进制先加1,然后取反。
mvn r0,#4
@mvn指令范例
mvn r1,#0b10 @0b表二进制
mvn r2,#5
mvn r3,r1
----------------------------------------------------------
sub:将第一个源寄存器中的值减去另一个寄存器中的值或一个立即数,放到目的寄存器中。
sub r0,r1,r2
@sub指令范例
sub r1,#4,#2//编译器报错,原因是被减数(#4)不能为立即数应该为mov r2,#4
sub r1,r2,#2
mov r0,#1
sub r3,r1,r0
--------------------------------------------------------
add:将把两个操作数加起来,把结果放置到目的寄存器中。操作数 1 是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即值.
@add范例
add r1, r2, #3
----------------------------------------------------------
and:逻辑与
@add范例:
mov r1,#5
add r2,r1,#0
mov r1,#5
add r2,r1,#1
------------------------------------------------------
bic:位清除;将掩码1对应的位置清除为0.
@bic范例
bic r0,r0,#%1011
如:r0:0b101111101
掩码为: 1011
位清除后: 0100
mov r1,#0b101011
bic r2,r1,#0b101
-----------------------------------------------------------
比较指令:
cmp:比较CMP 允许把一个寄存器的内容如另一个寄存器的内容或立即值进行比较。会将比较的结果放到状态寄存器中cpsr(r15):
N Z C V I F 若结果为负N(negative)就会被置1,结果为零Z会被置1
@cmp指令范例
mov r1,#2
cmp r1,#1 //0x2
mov r1,#2
cmp r1,#3 //0x8
mov r1,#2
cmp r1,#2 //0x6
---------------------------------------------------------
tst:测试位:按位与,如果与的结果为0,CPSR中的Z位会被置1
@tst范例
mov r1,#0b101
tst r1,#0b001 //0x2

mov r1,#0b101
tst r1,#0b10 //0x6
-----------------------------------------------------
为什么要有跳转的操作:gt(great than 大于)
if(a>b) mov r1,#6
a-b; mov r2,#5
else cmp r1,r2
a+b; gt branch1 //若条件满足跳转到branch1分支
跳转指令: add r3,r1,r2
b end //结束跳转操作
b ranch1:
sub r3,r1,r2
end:
op //空操作
--------------------------------------------------
bl:带链接返回的跳转
mov r1,#2 //lr=0x0
cmp r1,#1
b func1 //b指令无法让lr寄存器记录下条指令的地址,bl可以 。bl func1 //lr=0x000800c
mov r1,#2
cmp r1,#3
func1:
mov r1,#2
mov r2,#3
mov pc,lr //赋值给pc指针,使用bl指令可返回到mov r1,#2,b却不行。
-----------------------------------------------------------
移位指令:
lsl:逻辑左移
@lsl指令范例
mov r1,#0b1
mov r1,r1,lsl#2 //0b100
---------------------------------------------------
ror:循环右移
@ror范例
mov r1,#0b11
mov r1,r1,ror#1 //0b100000000000000000000000001
--------------------------------------------------
程序状态字访问指令
msr:搬回 mrs:搬出;s为状态寄存器,源在后,所以它是搬出
如何访问程序状态寄存器:首先把程序状态寄存器通过专业指令把状态寄存器中的值搬移到通用寄存器当中,然后再通用寄存器中修改值,修改完了之后再用专用指令将该值搬回程序状态寄存器当中。
@程序状态字寄存器
mrs r0,cpsr
orr r0,#0b100 //逻辑或
msr cpsr,r0
-----------------------------------------------------
存储器访问指令
ldr:将内存中的内容导出到寄存器中
str:将寄存器中的值保存到内存中
mov r0,#0xff
str r0,[r1] 将寄存器r0中的值保存到r1内存中
ldr r2,[r1] //将r1中的值保存到r2中。//0xff
arm-linux-objdump -D -S start.elf
30008008 e58100000(arm机器码) str r0,[r1]
-------------------------------------------------
打开文档ARM Architecture Reference Manual
先提取两条汇编指令的机器码
mov r0,r1
机器码:e1a00001将其转成二进制位:1110 00 0 1101 0 0000 0000 000000000001,用的r1寄存器。
moveq r0,#0xff`
机器码:03a000ff将其转成二进制位:0000 00 1 1101 0 0000 0000 000011111111
将机器码和文档中116页机器码格式对比112
cond uncondition:1110 EQ:0000
保留位
i:如果0-11位存的是一个立即数i=1,寄存器i=0:相对于标志位。
opcode:区别不同的指令(mov,ldr等)
S:表明是否影响状态寄存器(cpsr)
Rn:mov并没有使用Rn
Rd:目的寄存器,寄存器的编号(如r1:0001)
shifter_operand:源操作数
易知我们源操作数的大小是有限制的,因此立即数的大小超过了这个限制以后,这个指令就会报错,如何解决呢?我们引入了伪指令。
-----------------------------------------------------
定义类伪指令:它是一个指令,在编译的时候起作用(或转化为其它实际指令运行),但不会产生对应的机器码。
-----------------------------------------------------
.global:表明一个全局的符号;
.data:定义数据段,表明接下来的数据将存入数据段
oscii:
实例:
.data
hello: //标号
.ascii "hello world"
bh:
byte 0x1
ADD:
.word 0xff
--------------------------------------
equ:
_start:
.equ DA.0x89 //定义了一个宏DA,它的值为0x89
mov r0,#DA
----------------------------------------------
align:接下来的地址按4字节对齐
.align 4
------------------------------------------------------
操作类伪指令

.text
.global _start
_start
mov r0,#0x1ff(mov能使用的立即数不能超过8位)此处会报错。
shift_operand中的12位,其中有4位要留下了用于左右移位用的,因此立即数不能超过4位。解决办法:
ldr r0,=0x1ff //在ldr中用=表示立即数
arm-linux-objdump -D -S start.elf
找到:ldr伪指令,经过转换之后变成了ldr r0,[pc,#-4(当前的pc指针减去4)]
----------------------------------------------------
nop:空操作,反汇编可在实际执行的是mov r0,r0
------------------------------------------------
协处理器访问指令
什么是协处理器(CP15:co-processor):用于执行特定的处理任务,以减轻处理器的负担。如:数学协处理器可以控制数字处理。ARM可支持多大16组协处理器,CP15最重要。
CP15作用:起到系统控制的作业,通过cpu提供的16组寄存器来对他进行访问
----------------------------------------------------------
协处理器访问:
mcr:将通用寄存器中的值复制到协处理器中
mrc:把协处理器中的搬到通用寄存器中。
r:表示通用寄存器,c:co-processor,
访问格式:
mcr{cond} P15,<Opcode_1>,<Rd>,<CRm>,<Opcode_2>
mrc{}............................................
Rd:目的寄存器
其他几个参数的确定:见手册:Arm1176 p146页
如访问Main ID寄存器:mcr p15,0,r0,c0,c0,0

原文地址:https://www.cnblogs.com/defen/p/4755683.html