四:ARM汇编语言程序格式
ARM汇编语言是以段(section)为单位来组织源文件的。段是相对独立的、具有特定名称的、不可分割的指令或者数据序列。
段又可以分为代码段和数据段,代码段存放执行代码,数据段存放代码运行时的数据,A搜素解释快速润色总一个ARM源程序至少需要一个代码段,大的程序可以包含多个代码段和数据段。
在汇编文件中,我们需要先定义一个段,在段中添加我们实现的汇编程序语句
一:六大指令集
1.分别为数据处理指令 //(完成CPU内部的计算)
2.Load/Store指令 //(完成CPU与内存IO外设之间的数据传输)
3.跳转指令 //(完成程序的跳转)
4.程序状态寄存器处理指令 //(完成 CPSR的管理)
5.协处理器指令 //(完成CPU扩展功能的实现)
6.异常产生指令 //(用户程序异常触发)
二:指令格式中符号说明
〈opcode〉{〈cond〉}{S} 〈Rd〉,〈Rn〉{,〈operand2〉}
opcode //操作码;指令助记符,如LDR、STR等。
cond //可选的条件码;执行条件,如EQ(相同)、NE(uneq 不相等)等。
S //可选后缀;若指定“S”,
//则根据指令执行结果更新CPSR中的条件码。
Rd //目标寄存器。
Rn //存放第1操作数的寄存器。
Operand2 //第2个操作数
三:数据处理指令
算术指令: ADD ADC SUB SBC RSB(反向减,就是2 - 1 => 1 - 2) RSC
位 运 算 指令: AND(&) ORR(|) EOR(异或) BIC(位清零)
比较指令: CMP CMN TST TEQ
数据搬移: MOV MVN(按位取反放进去)
语法:
<操作> {<cond>} {S} Rd, Rn, Operand2
有比较指令影响标志位 -不指定Rd
数据搬移(MOV指令)不指定Rn
area reset, code, readonly
code32
entry
mov r0,#1 ;1搬移到r0中 # 不超过256
mov r1,#2 ;2搬移到r1中
add r2, r1, r0
sub r3, r1, r0
rsb r4, r0, r1
and r5, r0, #0xff
orr r6, r1, #0x01
eor r7, r0, #0xfe
bic r8, r7, #0xfe
mvn r9,#0x01
adc r10, r1, r0, LSL#1
;sbc r11, r1, r1, LSL#1
;rsc r12, r1, r0, LSL#1
end
3.1进位的问题
;进位adc的使用
area reset, code, readonly
code32
entry
;0x1 ffff ffff
;0x2 ffff ffff
mov r0, #0x01
mov r1, #0xffffffff
mov r2, #0x02
mov r3, #0xffffffff
adds r5, r1, r3 ;产生进位
;产生进位需要+s
adc r4, r0, r2 ;使用进位
end
3.4比较的问题
;比较的使用有进位问题,也不需要+s
cmp r0, r1 ;只能两两比较
movgt r2,r0 ;大于
moveq ;等于
movgt ;小于
;所有指令都将可以 + eq 等。。。可选的条件码
;执行为真才能执行 本质上先运算(真?/假?),在放入
;图很重要,是判断标志位的启示,可以视为if判断的真假之说
;if判断
mov r0 #4
mov rl #5
mov r2 #3
cmp r0 rl ;先判断r0,r1
cmpgt r0 r2 ;上句成立,接着比较
;为什么用gt,因为涉及到截断特性,不加gt,就随便放入,随便执行了
movgt r3 r0
;1 ~ 100 相加
area reset, code, readonly
code32
entry
mov r0, #1
mov r1, #0
loop
add r1, r1, r0
add r0, r0 ,#1
cmp r0, #101
bne loop ;这里用blt还是其他都合理,还是要看标志位
end
3.5立即数
一个数(或按位取反)循环右移2^n(移动偶次)位后中所有的1能放进低8位中 范围0~255
;放非立即数或超过范围
ldr sp, =0x40001000 ;将一个地址加载到寄存器中
mov r0, #1 ;将立即数1搬移到r0中, #代表立即数
3.6入栈操作指令
ldr sp, =0x40001000 ;栈顶
;Load/Store指令 入栈只是其中之一
;(完成CPU与内存 IO外设之间的数据传输)
STMFD (Push) 块存储- Full Descending stack [STMDB]
LDMFD (Pop) 块装载-Full Descending stack [LDMIA]
------------------------------------------------
;用法
STMFD sp!,{r4-r7,lr} ;表示为r4 ~ r7 再加上一个lr
LDMFD sp!,{r4-r7,pc} ;入栈出栈的顺序要对 先进后出
3.7函数应用(栈)
ldr sp, =0x40001000 ;栈顶
;例子
fun
stmfd sp!,{lr}:;保护现场
;代码。。。。。。。
ldmfd sp!,{lr} ;恢复现场
3.8声明外部函数.c
preserve8 ;当c和汇编语言需要对齐8字节,放在最上面,是关键字
;函数调用规则
;前四个参数,使用r0,r1,r2,r3存放参数,剩余参数使用栈传递
;返回值存放在r0中;
ldr sp, =0x40001000 ;栈顶
mov r0, #1
mov r1, #1
mov r2, #1
mov r3, #1
mov r4, #1
mov r5, #1
stmfd sp!, {r4,r5} ;用站
import c_add ;声明一个外部符号,给汇编文件使用
bl c_add ;汇编语言不能带参数,不像c
int asm_add(int x,int y); //c中调汇编,声明
int c_add(int a,int b)
{
int sum = asm_add(a,b);
return sum;
}
export asm_add ;声明一个内部符号给外部文件使用
ldr sp, =0x40001000 ;栈顶
ams_add
stmfd sp!,{r4 - r12,lr} ;保险,防止数据篡改
add r0,r0,r1
ldmfd sp!,{r4 - r12,pc}
3.9出栈代码简化问题
ldmfd sp!, {pc}
{lr} → 出栈到 LR,相当于“把栈顶值存到返回地址寄存器”,并不直接跳转。
{pc} → 出栈到 PC,相当于“出栈并跳转到该地址”,这常用于函数返回。
3.10更改工作状态
mrs r0 cpsr
bic r0 r0 #0xlf
orr r0 r0 #0x10
msr cpsr_c r0
3.11软中断
swi #5 ;软中断指令 进入svc模式
;pc会默认跳到8
3.12总测试
int asm_add(int x,int y);//c中调汇编,声明
int c_add(int a,int b)
{
int sum = asm_add(a,b);
return sum;
}
preserve8
area reset, code, readonly
code32
entry
ldr sp, =0x40001000 ;初始化svc模式的栈
;进入C程序,里面也涉及到栈的使用
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x10
msr cpsr_c, r0
ldr sp, =0x4000c000 ;初始化user模式的栈
import c_add
bl c_add
export asm_add ;声明一个内部符号给外部文件使用
asm_add
stmfd sp!,{r4-r12,lr} ;保险,防止数据篡改
mov r0, #1
mov r1, #1
add r0,r0,r1
ldmfd sp!,{r4-r12,pc}
end
五:异常处理
ARM 有两级外部中断 FIQ,IRQ.
可是大多数的基于ARM 的系统有有>2个的中断源,因此需要一个中断控制器
ps
:通常中断处理程序总是应该包含清除中断源的代码
为什么:
外设或事件(比如定时器溢出、GPIO 变化、串口接收)会设置一个中断挂起标志位(pending flag)。
CPU 检测到这个标志后,进入中断服务程序(ISR)。如果这个标志位不被清除,那么:ISR 执行结束时,CPU 发现中断源还在挂起 → 马上再次进入 ISR。程序就可能死在中断里,永远不回到主程序。
其中保存返回地址到LR ,软中断pc会默认跳到8
看图的右边偏移量对应就是每种异常pc默认回到的地址
一:异常规范函数
preserve8
area reset, code, readonly
code32
entry
;这里就是异常向量表 预设
b start ; reset
nop ; undef
b deal_swi ; swi
nop ; prefetch abort
nop ; data abort
nop ; reserved
nop ; irq
nop ; fiq
deal_swi ;预设
stmfd sp!, {r4-r12, lr}
add r0, r0, r1
ldmfd sp!, {r4-r12, pc}
start
ldr sp, =0x40001000
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x10
msr cpsr_c, r0
ldr sp, =0x40000C00
swi #5 ;ÈíÖжÏÖ¸Áî
mov r0, #1
mov r1, #2
import c_add
bl c_add
nop
b start
export asm_add
asm_add
stmfd sp!, {r4-r12, lr}
add r0, r0, r1
ldmfd sp!, {r4-r12, pc}
end