一、指令
1. 寻址
多寄存器寻址
STMFD:保存至堆栈满递减,满:堆栈时刻都是满的,所以先减再入栈,入栈之后指针还是指向有效数据
至于多寄存器的出栈入栈顺序需要格外注意:
STMFD SP!, {R1-R12}
堆栈满递减,向下生长(下面是低地址),那么是先压进去R1呢还是先压进去R12呢,根据实验结果看,是先压R12。
重点:多寄存寻址与寄存器书写顺序无关,低地址对应编号低的寄存器,高地址对应编号高的寄存器。如果是压栈的话,无论是递增还是递减,都是低地址存编号低的寄存器。
2. 数据处理
ORR
逻辑或指令
BIC 清除寄存器某一位
MRS R0, CPSR
BIC R0, R0, #0x80 ;开IRQ中断
MSR CPSR_cxsf, R0
先从CPSR中读取数值,然后使用BIC清除掉第7位,第7位控制IRQ中断,第六位控制FIQ中断,第五位控制Thumb指令。最后使用MSR存回去,注意必须指定MSR存回的区域,用小写字母代替(必须小写)c:控制域;f:标志位域;x、s:arm9没用到。
立即寻址有效数问题
MOV R0, #0x18 ; #0x18是立即数
LDR R1, =0x20026 ; #0x20026不是立即数,使用伪指令
MSR CPSR_问题
错误:
MSR CPSR_C, #0xd0 ;1101 0000
正确:
MSR CPSR_c, #0xd0 ;1101 0000
MSR cpsr_c, #0xd0 ;1101 0000
LDR指令与LDR伪指令
1.
LDR R0, =IOSET
LDR R1, =0x00500500
STR R1, [R0]
在汇编编译器处理源程序时,如果该常数没有超过MOV可以操作的范围,则LDR指令被一条MOV替代,否则,该常数将被放在最近的一个文字池内,同时,本指令被一条基于PC的加载指令LDR替代。
从PC到文字池的偏移量必须小于4KB。
于ARM指令的LDR相比,伪指令的LDR有参数“=”号。
2.又发现一个事情
LDR SP, und_stack
LDR SP, =und_stack
usr_stack DCD usr_stack_space+(usr_stack_length-1)*4
不一样!!!
LDR SP, und_stack
是直接把und_stack的数值赋给了SP 助记: SP = und_stack
LDR SP, =und_stack
是把und_stack的地址赋给了SP 助记: SP = &und_stack
LDR和LDM、STR和STM
LDR Rd, <地址> ;从右到左读取
STR Rd, <地址> ;从左到右存储
LDMIA R0, {R4-R11} ;从左往右读取
STMIA R0, {R4-R11} ;从右往左存储
注意:LDR是寄存器在地址左边、LDM是地址在寄存器左边。
包含变量的定义的程序架构
下面程序报错,Entry point (0x00008000) does not point to an instruction.
entry
code32
dataBuf DCD 11, -2, 35, 47, 96, 63, 128, -23
start
改为
entry
code32
B start
dataBuf DCD 11, -2, 35, 47, 96, 63, 128, -23
start
或者改为
AREA init, code, readonly
dataBuf DCD 1, 2, 3, 4, 5, 6, 7, 8
entry
code32
或者改为
B .
AREA Datapool, DATA, READWRITE
dataBuf DCD 1, 2, 3, 4, 5, 6, 7, 8
END
数组的定义与引用
B start
dataBuf DCD 1, 2, 3
start
LDR R0, =dataBuf
应用的时候注意使用 =
。
数组的循环遍历
loop
LDR R4, [R0, R3] ;读取数组的值
CMP R4, R1
MOVGT R1, R4 ;如果R4大于R1,那么R4的值存储到R1中
CMP R4, R2
MOVLT R2, R4 ;如果R4小于R2,那么R4的值存储到R2中
ADD R3, R3, #4 ;每次地址偏移的步长是4
CMP R3, #32 ;遍历8个元素,需要偏移32
BNE loop ;不等于0接着循环
STR R1, [R0]
B .
注意:步进的控制。DCD是字对齐,DCB是字节
二、伪指令
1. 数据定义
任何语言都少不了数据定义,arm汇编中除了我们接触到的写进代码中的立即数寻址,算是一种数据,还有LDR伪指令算是一种数据。正常的数据定义该如何做?
指令 | 指令语法 | 指令解释 |
---|---|---|
DCB | string DCB “Hello world” | 字节分配内存单元 |
DCW | data1 DCW 0x2a*0x2a | 半字分配内存单元 |
DCWU | 非半字对准 | |
DCD | data1 DCD 4, 5, 6 | 字分配内存单元(四个字节) |
DCDU | 非字对准 | |
DCFD | flo1 DCFD 2E115, -5E-7 | 双精度浮点数分配内存单元 |
DCFDU | flo1 DCFD 2E115, -5E-7 | 非字对准(两个字) |
DCFS | flo1 DCFD 2E5, -5E-7 | 单精度浮点数分配内存单元 |
DCFSU | flo1 DCFD 2E5, -5E-7 | 非字对准(一个字) |
DCQ | data1 DCD 4, 5, 6 | 双字分配内存单元 |
DCQU | 非字对准(两个字) |
EQU 等效的意思
名称 | EQU | 表达式 | {, 类型}
这个特别像宏定义,汇编之后也没有代码生成,“名称”的使用和被等效的东西的使用方法一模一样。
ENTRY
CODE32
x EQU 45
y EQU 64
stack_top EQU 0x1000
start
MOV SP, #stack_top
MOV R0, #x
可以看出来语句初始地址还是0x8000,说明x、y、stack_top定义没有生成代码。其实就是给汇编器做替代的。
LTORG声明文字池
LTORG ;声明文字池,此地址存储0x12345678,放的位置有讲究,不能让处理器把文字池的数据当作指令执行。通常放在无条件跳转指令后面,避免PC指针指向这个区域。
END是与什么对应的?
好像end并不和什么对应。END伪指令用于指示汇编编译器源文件已结束,每一个汇编源文件均要有一个END伪指令,指示本源程序结束。
NOP
空操作伪指令,并且占用4字节的代码空间,中断向量表的定义时就可以用它来占位。
start
B Reset_Handler
Undefined_Handler B Undefined_Handler
SWI_Handler B SWT_Handler
Prefetch_Handler B Prefetch_Handler
Abort_Handler B Abort_Handler
NOP ;空操作伪指令,占用32位代码空间
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler
程序结构
循环读取数组,并且修改后存储回去
发现一个问题,就是改不回去,有点像rom只读一样,但是当数组定义的位置不同,结果不同。
只有
AREA Init, CODE, READONLY
initData DCD 1, 2, 3, 4, 5, 6, 7, 8
ENTRY
是可以改动的。
三、注意
码格式问题
新的一行的开头,除了标号,别的字段前需要加空格或tab。
AXD仿真程序复位
没有复位按钮,但是有重新加载image的按钮,第二行左边第三个按钮。
报错代码
错误:
AREA init CODE, READONLY
ENTRY
CODE32
start
MOV SP, #0x800
B .
END
正确:
AREA init, CODE, READONLY
ENTRY
CODE32
start
MOV SP, #0X800
B .
end
原因:第一行少了个逗号。。。。
报错提示也就那样吧,还是自己淌水吧。
MSR中CPSR域的问题:字母大小写。。。。
报错:
MSR CPSR_C,#0xd2
正确:
MSR CPSR_c,#0xd2
协处理器问题:字母大小写。。。。
报错:
MRC P15, 0, R0, C1, C0, 0
ORR R0, R0, #0xc0000000 ;或1101b
MCR P15, 0, R0, C1, C0, 0
正确:
MRC p15, 0, R0, c1, c0, 0
ORR R0, R0, #0xc0000000 ;或1101b
MCR p15, 0, R0, c1, c0, 0
p和c必须小写!
SWI后 PC指针的设置
由于ARM采用三级流水线结构,因此,当SWI和未定义指令异常中断产生时,程序计数器PC的值还没有更新,它指向当前指令后面第2条指令(对于ARM指令,它指向当前指令地址加8个字节的位置;对于Thumb指令,它指向当前指令地址加4个字节的位置)。这就是为什么当SWI和未定义指令异常中断发生时,处理器将(PC-4)保存到异常模式下的寄存器lr_mode中,这时(PC-4)即指向当前指令的下一条指令