ARM boot, interrupt, lock常用汇编---之---boot汇编

躲不过,出来混,ARM汇编还是要学的。本文将结和bootloader中要用到的汇编语句来学习。

如果有疑问,联系Q328333568.

ARM指令在汇编程序中用助记符表示,一般ARM指令的助记符格式为: 
<opcode>{<cond>} {S} <Rd>,<Rn>,<op2> 

其中: 
<opcode> 操作码,如ADD表示算术加操作指令; 
{<cond>} 决定指令执行的条件域; 
{S} 决定指令执行是否影响CPSR寄存器的值; 
<Rd> 目的寄存器; 
<Rn> 第一个操作数,为寄存器; 
<op2> 第二个操作数。

如果一个32位立即数直接用在32位指令编码中,就有可能完全占据32位指令编码空间。
因此,ARM指令的32位立即数是通过循环右移偶数位得到的。
ARM指令中立即数是由一个8位(1~255)立即数循环右移得到的,其中循环右移位数由一个4位数乘2表示(0~30),也就是说不是所有的32位立即数是合法的。比如0x12345678,在编译如mov r0, #0x12345678的时候编译器会报错:
Error: invalid constant (0x12345678) after fixup


二、9种寻址方式

a) 立即数寻址 
     在立即数寻址中,操作数本身直接在指令中给出,取出指令也就获得了操作数,这个操作数也称为立即数。  #后接0x或&表示十六进制数,0b表示二进制数,0d或缺省表示十进制数。 
例: 
ADD R0,R1,#5
R0=R1+5 /*ARM 标准用分号,AT&T格式汇编用@*/
MOV R0,#0x55; R0=0x55 
其中:操作数5,0x55就是立即数,立即数在指令中要以“#”为前缀,后面跟实际数值。

b)寄存器寻址 
    在寄存器寻址方式下,寄存器的值即为操作数。ARM指令普遍采用此种寻址方式。 
例: 
ADD R0,R1,R2 ; R0=R1+R2, 要注意这边没有把R1, R2 用中括号括起来,因为括起来就是间接寻址了。
MOV R0,R1 ; R0=R1


c)寄存器移位寻址 
    寄存器移位寻址的操作数由寄存器的数值做相应移位而得到。移位的方式在指令中以助记符的形式给出,而移位的位数可用立即数或寄存器寻址方式表示。 
例: 
ADD R0,R1,R2,ROR #5 
R0=R1+R2循环右移5位 
MOV R0,R1,LSL R3 
;R0=R1逻辑左移R3位 
移位操作在ARM指令集中不作为单独的指令使用,ARM指令集共有5种位移操作。 

ARM指令集的5种位移操作 
LSL逻辑左移 :Rx,LSL <op1> 
向左移位,左侧移出的位丢失,右端空出位补0。
注意最后一个左移出的位放在状态寄存器的C位CPSR【29】中。这种相当于寄存器的乘法。移出n位相当于乘以2的n次幂。 

LSR逻辑右移 : Rx,LSR <op1>
向右移位,右侧移出的位丢失,左端空出位补0。注意最后一个右移出的位放在状态寄存器的C位CPSR【29】中。这种相当于寄存器的除法。移出n位相当于除以2的n次幂。

ASR算术右移 :Rx,ASR <op1> 

向右移位,移除的位丢失最左端的位保持不变注意最后一个右移出的位放在状态寄存器的C位CPSR【29】中。如图:

ARM boot, interrupt, lock常用汇编---之---boot汇编_第1张图片

ROR循环右移 :Rx,ROR <op1> 
向右移位,右侧移出的位填充在左侧的空位处,左端空出位补0。注意最后一个右移出的位放在状态寄存器的C位CPSR【29】中。如R4,ROR #03表明把R4内的数据向右移3位。。。 

RRX带扩展的循环右移:Rx,RRX
向右移位,只移一位,左侧空位由状态寄存器C位填充,右侧移出的位移进状态位C。如图:

ARM boot, interrupt, lock常用汇编---之---boot汇编_第2张图片

d)寄存器间接寻址 
    寄存器中的值为操作数的物理地址,而实际的操作数存放在存储器中。 
例: 
STR R0,[R1] ; [R1]=R0 
LDR R0,[R1] ; R0=[R1] 

e)基址变址寻址 
    将寄存器(称为基址寄存器)的值与指令中给出的偏移地址量相加,所得结果作为操作数的物理地址。 
例: 
LDR R0,[R1,#5] ; R0=[R1+5] 
LDR R0,[R1,R2] ; R0=[R1+R2]  


f)相对寻址 
    相对寻址同基址变址寻址相似,区别只是将程序计数器PC作为基址寄存器,指令中的标记作为地址偏移量。 
例: 
BEQ process1 
…… 
process1 
…… 

g)多寄存器寻址 
    在多寄存器寻址方式中,一条指令可实现一组寄存器值的传送。连续的寄存器间用“-”连接,否则用“,”分隔。 允许一条指令传送16个寄存器的任何子集或所有寄存器。  
例: 
LDMIA R0,{R1-R5} 
;R1=[R0],R2=[R0+4],R3=[R0+8] 
;R4=[R0+12],R5=[R0+16] 

指令中IA表示在执行完一次Load操作后,R0自增4。该指令将以R0为起始地址的5个字数据分别装入R1,R2,R3,R4,R5中。

h)块拷贝寻址 
    块拷贝寻址可实现连续地址数据从存储器的某一位置拷贝到另一位置。 
例: 
LDMIA R0,{R1-R5}; 
STMIA R1,{R1-R5}; 
第一条指令从以R0的值为起始地址的存储单元中取出5个字的数据,第二条指令将取出的数据存入以R1的值为起始地址的存储单元中。 
实际上是多寄存器寻址的组合。????

j)堆栈寻址
    堆栈寻址用于数据栈与寄存器组之间批量数据传输。 当数据写入和读出内存的顺序不同时,使用堆栈寻址可以很好的解决这问题。 
例: 
STMFD R13!,{R0,R1,R2,R3,R4}; 
LDMFD R13!,{R0,R1,R2,R3,R4} 
第一条指令,将R0-R4中的数据压入堆栈,R13为堆栈指针; 
第二条指令,将数据出栈,恢复R0-R4原先的值。 
存储器堆栈可分为4种: 
向上生长:向高地址方向生长,称为递增满(空)堆栈 
向下生长:向低地址方向生长,称为递减满(空)堆栈 


 


1 init armclk

mov  r4, #0xE00

add r4, r4, #0x11

ldr r5, apld_base

str r4, [r5, #APLD_LOCK]

 

2 init gpio

ldr r5, gpacom

mov r6,#2200

add r6

str r6, [r5]

3 init  uart0, 这边都比较好理解,只是为什么是配置这些值得对应DATA SHEET, 如果有问题,联系Q328333568

ldr r5, ucon_base

mov r6, #03

str r6, [r5, #ULCON0]

mov r6, #0xc00

add r6, r6, #0x05

str r6, [r5, #UCON0]

mov r6, #0xx67

str r6, [r5, #CFCON0]

 

@设置波特率, 我们看到,其实很简单,就是用到两个寄存器,一个存值,一个存地址,把值存入寄存器指向的地址。

ldr r5, =UBRDIV0

mov r6, #35

str r6, [r5]

ldr r5,  =UDIVSLPTP

mov r6 #0

str r6, [r5]

ldr r0, =msg

bl  my_printf


4 init nand flash

参考:http://www.360doc.com/content/10/1019/16/4026366_62240627.shtml

@init  NFCONF 用来设置NAND FLASH  的时序, 数据位宽,是否支持其他大小的页

@下面三个数值如何确定的要参看我后面转的一篇文章。

@tacls = tcls -twp = 21 -21 = 0ns(1.8v) :表示地址锁存到 nWE 有效的时间;

@twrph0= twp = 21ns

@twrph1= tclh = 5ns : 表示nWE无效 到数据或地址无效的延时;

ldr r5,  =NAND_BASE

mov r6, #0x200

add r6, r6, #0x4:  到这边又忘记立即数是不是有限制?那寄存器里存了多大就不管?

str r6, [r5, #NFCONF]: 到这里str往哪里放数据都不确定啦,没过几天就不确定,做孽。

 

@nfcont   用来使能flash 控制器

mov r6, #01

str r6, [r5, #NFCONT]

@read id

@发出命令字90h,发出4个地址序列(都设定为0),然后就可以连续读入5个数据,分别表示厂商代码(对一SAMSUNG公司为Ech)、设备代码(对于K9F1208U0M76h)、保留的字节(对于K9F1208U0MA5h)、多层操作代码(C0h表示支持多层操作)。

mov r6, 0x90

str r6, [r5, #NFCMMD]  : 写id 命令

mov r6, 0x0:

str r6, [r5, #NFADDR]

 

@get 5 cycle

mov r6, #5

@test busy

ldr r7, [r5, #NFSTAT]: 只要读一次就可以,不是每读一个BYTE 都要判断

tst r7, #01  : 0表示BUSY,按位与之后结果为0, 那么ALU 的Z置位,beq 将跳回重新判断。其实这个真别扭,beq会让人觉得是要相等,那么tst结果要为1才有eq的感觉。

beq 1b

 

@show data

2:

ldrb r0, [r5, #NFDATA]

bl show_reg

sub r6, r6, #1

cmp r6, #0  :  这边应该可以不用这句的,因为sub本身会影响全部标志位

bne 2b :如果标志位ZF 不为1,就跳转回2处。

@这边插入sub用法

SUB:不带借位的减法指令。
【指令格式】SUB OP1,OP2
【指令功能】(OP1)←(OP1)-(OP2),将OP1-OP2的值,保存在OP1中,如:
SUB [EAX],1 以EAX寄存器为 内存地址,将该地址的值减1,类似C/C++中的 i--;
目的操作数减去源操作数,结果放在目的操作数中。源操作数原有内容不变,并根据运算结果置标志位SF,ZF,AF,PF,CF,OF
SUB指令可以进行字节或字的减法运算,源操作数和目的操作数的约定与ADD指令相同。
操作数的类型可以根据程序员的要求约定为带符号数或者无符号数。当无符号数的较小数减去较大数时,因不够减而产生借位,此时进位标志CF置1.当带符号数的较小数减去较大数时,将会得到负的结果,则符号位SF置1.带符号数相减,如果溢出,则OF置1. [1]
【例】
1.SUB BL,AL
设(BL)=23H,(AL)=78H,(BL)=23H-78H=ABH(1010101)
根据运算结果,各标志位为:CF=1,ZF=0,SF=1,OF=0,PF=0,AF=1
2. SUB SI,SI
寄存器自身相减,则结果为零,此时:
OF=0,SF=0,ZF=1,PF=1,CF=0


@init sdram, 比较复杂,比较专业,待查


@读取kernel

mov r3, #0x100

mov r6, #0x400

mov r0, #0x53000000


ldr r5, =NAND_BASE

mov r4, #0x00

str r4, [r5, #NFCMMD]


mov r4, #0x00

str r4, [r5, #NFADDR]


strb  r3, [r5, #NFADDR]

lsr r4, r3, #8

strb  r4, [r5, #NFADDR]

lsr r4, r3, #16

strb r4, [r5, #NFADDR]
这里对flash的操作进行一个插播:

K9F1208U0M容量为528Mbit,分为131072行(页)、528列,每一页大小为512字节,外加16字节的额外空间,这16字节额外空间的列地址为512 - 527

所以从上面这句从别人处的引用可以看出,每页的大小跟列数是有关系的。我们用的是2048个字节。

一片NAND Flash为一个设备, 其数据存储分层为:1设备=4 096块;1块=32页;1页=528字节=数据块大小(512字节)+OOB块大小(16字节)。在每一页中,最后16字节(又称OOB,Out of Band)用于NAND Flash命令执行完后设置状态用,剩余512字节又分为前半部分和后半部分。可以通过NAND Flash命令00h/01h/50h分别对前半部、后半部、OOB进行定位,通过NAND Flash内置的指针指向各自的首地址。

1:

ldr r7, [r5, #NFSTAT]

tst r7, #0x1: 发现这样还是很吻合的,r7为0,表示busy, 相与结果为0, 置位ZF,然后beq 成功往回跳转。

beq 1b

2:

ldrb r1, [r5, #NFDATA]

strb r1, [r0]:r0, 就象个指针

add r0, r0, #1: SDRAM地址加1

sub r8, r8, #1

cmp r8, #0

bne 2b :从下面可以知道, 如果r8不为0, 那么不会置位ZF,那么ZF = 0, NE成立,跳到2b处。

插播:

问题:

EQ Z置位 相等
NE Z清零 不相等
CS/HS C置位 无符号数大于或等于

请问标志位的set或者clear是根据什么带条件码之前的语句结果得出的么?
比如说以下代码:
MOVS R0,R0
BNE Label ;书上的注释为:如果R0不为0,则跳转到Label标号处

解答:

MOVS 目的寄存器,源操作数
其中S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
ARM 中很多指令在其后面加上 "S " 都会影响 CPSR中条件标志位, 如 MVNS, ANDS, ADDS...


这个得看具体是什么指令,根据执行的结果,来判断:
a,Z标志,是否为0;如比较指令产生相等的结果、计算结果为0;
b,N标志,是否为负;如果指令的结果被认为是有符号数,而它的第31位为1,那就会被设置为1;
c,C标志,是否有产生进位/借位,以及进行移位操作时作移出位使用;
d,V标志,有符号数进行运算,结果是否有溢出。

 可以分成两种情况来考虑 movs 指令对标志位的影响:
a,假如目标寄存器非r15,也即非PC。那么看所移动的值是否为零或者是否为负来影响 cpsr 中的z标志和n标志;假如 movs 指令中还有移位操作,那么移出位还为被设置到 c标志中。
b,假如目标寄存器是 r15,那不管是 mov 还是 movs ,都会用 spsr 中的值来更新cpsr,其中的标志位自然会受到影响。
这两种情况下,都不会影响到 cpsr 中的 v标志。您可以直接参考 arm_arch_ref_manual 或者参与 JulianTec 的邮件列表像师兄师姐们请教。


 

你可能感兴趣的:(ARM boot, interrupt, lock常用汇编---之---boot汇编)