第二季-专题3-汇编语言得玩转

专题3-汇编语言得玩转

 

第1课-汇编概述

1.为什么学习汇编指令

以后的工作中我们用的都是中高端的处理器,基本不会用汇编去编程完成这个产品。但是在bootloader和内核的编程中我们都是要用汇编语言的,这期间c语言的运行环境还没有搭建。在对效率有有特殊要求的地方我们还是需要汇编语言的。

2.分类

目前常用的ARM的汇编指令有两种:

(1)ARM标准汇编:适用于ARM公司的汇编器,适合在windows平台下使用,如ADS中使用。ADS是基于windows的集成程序。

(2)GNU汇编:适用于GNU交叉编译器工具链中的汇编器,适合linux开发平台。我们使用的是GNU汇编

3.汇编程序框架

.sectiom.data

<初始化的数据>

.section.bss

<未初始化的数据>

.section.text      /*代码段*/

.global _start     /*全局访问*/

_start:

<汇编代码>

简化:

.text

.global _start

_start:

<汇编代码>

4.编程准备

(1)首先在相应的文档里面建立一个名字为start.s的文件,文件内容如下:

.text

.global _start                  //定义全局变量

_start:                       //程序入口

mov r1,#1             /*将1这个立即数,放在r1寄存器里面*/

mov r2,#2

mov r3,#3

(2)建立一个Makefile,内容如下:

all:start.o

arm-linux-ld -Ttext 0x20000000 -o start.elf $^    /*指明程序运行的起止地址*/

%.o : %.S

Arm-linux-gcc -g -o $@ $^ .c

Clean

    rm *.o *.elf

         /*针对210的开发板,代码段的指定开头就是20000000,对于6410是50000000,2440是30000000*/

         连接器脚本对整个程序的地址进行排布,可以在开头进行运行地址的设置。

         以Jlink的方式连接开发板与电脑,运行Jlink发现联接上了,启动我们自己安装的eclipse软件,编译我们编译的软件。按上面章节的方法,进行一系列的配置与初始化调试。

 

第二课-指令分类学习

GUN汇编和ARM标准汇编是有区别的,一般来说GUN是小写的,标准ARM是大写的。

1. 算数和逻辑指令

(1)mov装载

mov{条件}[S]  <dest>, <op_1>

C语言转换:dest = op_1

         mov从一个寄存器、被位移的寄存器、或一个立即数装载到目的寄存器。

         dest必须是通用寄存器,op_1可以是通用寄存器、状态寄存器也可以是数字。

注:汇编语言的注释是@符号后面加注释的。

(2)mvn取反装载

mvn{条件}[S]  <dest>, <op_1>

C语言转换:dest = !op_1

         mvn从一个寄存器、被位移的寄存器、或一个立即数装载到目的寄存器。不同之处是在传送之前位被取反了,是把取反的值传给一个寄存器。这是逻辑操作不是算数操作,这个取反的值加1才是它的取负的值。

         mov R0, #4            R0变成-5

         mov R0, #0            R0变成-1

(3)sub相减

sub{条件}[S]  <dest>, <op_1> <op_2>

         dest= op_1- op_2

         sub用来执行减法操作,操作数1是一个寄存器,操作数2可以是一个寄存器、被位移的寄存器或者是一个立即数。

(4)add相加

add{条件}[S]  <dest>, <op_1> <op_2>

dest= op_1+op_2

         add用来执行加法操作,操作数1是一个寄存器,操作数2可以是一个寄存器、被位移的寄存器或者是一个立即数。

(5)and逻辑与

and{条件}[S]  <dest>, <op_1> <op_2>

dest= op_1&op_2

and用来执行逻辑与操作。操作数1是一个寄存器,操作数2可以是一个寄存器、被位移的寄存器或者是一个立即数。

(6)bic位清除

bic{条件}[S]  <dest>, <op_1> <op_2>

dest= op_1and(!op_2)

         在一个字中清除位的一种方法,与or位设置是相反的操作。操作数2是一个32位的位操作符(mask)。如果在操作码中设置了某一位,就清除这一位。未设置操作码的位保持不变。

         bic r0, r0, #1011        表示清除r0中的第0、1、3位,其余的位不变

 

2. 比较指令

(1)cmp比较

cmp{条件}[S]  <op_1> <op_2>

status= op_1-op_2

         status为1表示操作数1大,为-1表示操作数2大,为0表示一样大。它改变的是CPSR寄存器的31位(M位)于30位(N位)。

例子

mov r1, #2

cmp r1, #1

cpsr为200001d3 

mov r1, #2

cmp r1, #1

         cpsr为800001d3

mov r1, #2

cmp r1, #2

         cpsr为600001d3

(2)tst按位与

tst{条件}[S]  <op_1> <op_2>

status= op_1 AND op_2

         按位与之后的结果是0,CPSR的30位(Z位)置1,否则CPSR的30位(Z位)不置1。

例子

mov r1, #0b101

tst  r1, #0b001

cpsr为200001d3

mov r1, #0b101

tst  r1, #0b101

cpsr为600001d3

 

3. 跳转指令

         汇编的程序需要分支指令完成C语言中的if…else操作

(1)b分支

b{条件} <地址>

存储在分支指令中的实际的值是相当于当前的r15的一个偏移量,并不是一个绝对地址。它的值由汇编器来计算,它是24位有符号量,左移两位后有符号扩展为32位,表示的有效偏移量是26位(+/-32M)。

例子:

mov r1,#6

mov r2,#5

cmp r1,r2

bgt branch1:

add r3, r1, r2

b end

branch1:

sub r3,r1,r2

end:

nop

 

EQ : 等于

如果一次比较之后设置了 Z 标志。

NE : 不等于

如果一次比较之后清除了 Z 标志。

VS : 溢出设置

如果在一次算术操作之后设置了 V 标志,计算的结果不适合放入一个 32bit 目标寄存器中。

VC : 溢出清除

如果清除了 V 标志,与 VS 相反。

HI : 高于(无符号)

如果一次比较之后设置了 C 标志并清除了 Z 标志。

LS : 低于或同于(无符号)

如果一次比较操作之后清除了 C 标志或设置了 Z 标志。

PL : 正号

如果一次算术操作之后清除了 N。出于定义‘正号’的目的,零是正数的原因是它不是负数...

MI : 负号

如果一次算术操作之后设置了 N 标志。

CS : 进位设置

如果一次算术操作或移位操作之后设置了 C 标志,操作的结果不能表示为 32bit。你可以把 C 标志当作结果的第 33 位。

CC : 进位清除

与 CS 相反。

GE : 大于或等于(有符号)

如果一次比较之后...
设置了 N 标志并设置了 V 标志
或者...
清除了 N 标志并清除了 V 标志。

GT : 大于(有符号)

如果一次比较之后...
设置了 N 标志并设置了 V 标志
或者...
清除了 N 标志并清除了 V 标志
并且...
清除了 Z 标志。

LE : 小于或等于(有符号)

如果一次比较之后...
设置了 N 标志并清除了 V 标志
或者...
清除了 N 标志并设置了 V 标志
并且...
设置了 Z 标志。

LT : 小于(有符号)

如果一次比较之后...
设置了 N 标志并清除了 V 标志。
或者...
清除了 N 标志并设置了 V 标志。

AL : 总是

缺省条件,所以不用明显声明。

NV : 从不

不是特别有用,它表示应当永远不执行这个指令。是穷人的 NOP。
包含 NV 是为了完整性(与 AL 相对),你不应该在你的代码中使用它。

(2)bl带链接返回的分支

         b指令在跳转之前不能将返回地址保存,bl就可以将返回地址保存,可以起到类似C语言的对函数进行封装的效果。

例如:

mov r1, #6

cmp r1, #5

bl func1:

mov r1, #2

cmp r1, #3

func1:

mov r1, #r2

mov r2, #3

mov pc, lr

 

4. 移位指令

(1)lsl逻辑(算数)左移

例如:

mov r1, #0b1

mov r1,r1, lsl#2

对r1左移两位,将结果再存在r1中,r1从0b1变成0b100

(2)ror循环右移

例如:

mov r1, #0b11

mov r1,r1, ror#1

         r1的值从0b11变成0b1000000000000000000000000000001,一共32位。

 

5. 程序状态字访问指令

         程序状态寄存器所用的访问指令和前面的指令不能是一样的,对它们的修改,需要将CPSR或者SPSR中的指令搬移到通用寄存器,改好后再搬回程序状态寄存器。

msr(搬入)和mrs(搬出)两条指令:

例子:

mrs r0, cpsr

orr r0, 0b100

msr cpsr, r0

         将cpsr的第三位设置为1

 

6. 存储器访问指令

(1)ldr将内存中的值导入寄存器

str    rd, [Rbase]          存储 rd 到 Rbase 所包含的有效地址。

   str    rd, [Rbase, Rindex]  存储 rd 到 Rbase + Rindex 所合成的有效地址。

   str    rd, [Rbase, #index]  存储 rd 到 Rbase + index 所合成的有效地址。

                               index 是一个立即值。

                               例如,rTR 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 所合成的有效地址。

(2)str将寄存器中的值保存到内存

 

第3课-伪指令

一.ARM机器码

         汇编程序通过汇编器转化成机器码,机器码才能够在嵌入式芯片上运行。Arm机器码是32位的整数,机器码被分成不同大小的段,每个段都有各自独特的功能

         mov r0, r1的机器码是e1a00001

二进制是:1110 00 0 1101 0 0000 0000 000000000001

         moveq r0, #255的机器码是03a000ff

二进制是:0000 00 1 1101 0 0000 0000 000011111111

         对于mov指令它的机器码格式如下:

        

         第一段前4位表示条件,1110表示无条件,0000表示等于的条件

         第二段是保留位,是00

         第三段表示0-11存的是立即数的话就是1,存的是寄存器的话就是0

         第四段opcode,它的作用是区分指令,mov指令就是1101

         第五段是S,程序运行后影响CPSR寄存器就是1,不影响就是0

         第六段是Rn,

         第七段是目的寄存器,我们用的是r0,用的是0来标号,r1是0001

         第八段是源操作数,寄存器r1是00000000001,#255是00001111111

         这样我们可以观测到,第八段中存放数的大小是有限的,这样引出伪指令的概念。

 

二.定义类伪指令

         伪指令本身并没有对应的机器码,它只是在编译而定时候起作用,或者转化为其他的实际指令来运行。

(1)       global标明全局符号

(2)       data标明数据段

(3)       ascii标明字符串指令

(4)       byte标明字节的指令

(5)       word标明字的指令

例子:

.data

hello:

.ascii “hellloworld”

bh:

.byte 0x1

ADD

.word 0xff

(6)       equ相当于宏定义

.equ DA,0x89

mov r0, DA

(7)       align对齐,在文件前面加上.align就会表示按四字节对齐

 

三.操作类伪指令

(1)nop

空操作,主要的作用是延时的作用。

(2)ldr(与存储器的访问指令不同)

mov r0, 0x1ff运行的时候会出错,因为mov指令只能处理8位的二进制数,0x1ff是9位,机器码的0-11位中有四位是存储左移与右移操作的,所以存储的内容不能超过8位。

它可以在r0里面填充超过8位的立即数,但是格式特殊:

ldr r0, =0x1ff

 

第4课-协处理访问指令

一.什么是协处理器

         协处理器用于执行特定的处理任务,如:数学协处理器可以控制数字处理,以减轻处理器的负担。ARM可支持最多16个协处理器,其中CP15是最重要的一个。

  1. CP15的作用

CP15是系统控制协处理器,提供了额外的寄存器,通过这些寄存器,可以达到配置与控制caches、MMU以及时钟参数。

  1. CP15寄存器

CP15提供了16组寄存器,具体的使用可以在Arm公司提供的芯片手册中找到。我们通过它提供的16组寄存器,可以访问cp15寄存器。

 

二.协处理器访问

1. mcr

r表示通用寄存器,c表示协处理器寄存器

2.mrc

格式:

 

例子:读取mainID

         mrc p15, 0, r0, c0, c0, 0             表示读取mianID寄存器的值

         

posted @ 2019-09-03 11:27  free-锻炼身体  阅读(397)  评论(0编辑  收藏  举报