名称 | 说明 |
---|---|
应用程序 | app |
库 | libc.so.6、libstdc++.so、libjpeg.so、… |
GUI | QT(C++)、Android(Java)、Mini-GUI(C)、GTK() |
文件系统 | Filesystem |
内核 | Linux、Android |
bootloader | uboot、myboot |
板卡 | SOC+DDR+NAND(EMMC)+LCD+SOUND+NET… |
SOC | CPU(arm)+总线+控制器 |
Arm公司只设计处理器架构,Arm1 Arm2 …. Arm6
版本 | 内核 | 芯片 |
---|---|---|
v4 | arm7TDMI | S3C44B0 |
arm920T | S3C2440(火,资料多) | |
v5 | arm10 | |
v6 | arm1176 | S3C6410 |
arm11MP | ||
v7 | arm-cortex-a,针对以应用为主(消费电子、高端的嵌入式设备) | |
a8 | S5PV210(单核) | |
a9 | Exynos4412(4核)、Imax6.Q/D、Omap4460(TI) | |
a15 | Exynos5210(a15以上才支持硬件除法) | |
arm-cortex-r | ||
r4/r6 | ||
arm-cortex-m(m系列都支持硬件除法) | ||
m3 | STM32F10xxx(ST) | |
m4 | STM32F40xxx | |
m7 | STM32F7xxx | |
v8 | arm-cortex-a50(都是64位) | |
a53(消费电子、高端的嵌入式设备) | ||
a57(服务器) | ||
arm-cortex-a72 |
除arm外的芯片
名称 | 说明 |
---|---|
mips | 网络设备(路由) |
ppc | 航空航天,高端路由 |
51 | 仪器仪表 |
SOC=CPU(arm)+总线+控制器
i5--PIC-E--桥(芯片组)
ARM=arm_core 执行arm指令,哈弗结构
cache 高速缓存
I-cache 缓存代码
D-cache 缓存数据
TCM 要求访问速度高的,可以存在这里
I-tcm 存代码
D-tccm 存数据
名称 | 说明 |
---|---|
CP15 | cache、tcm、指令分支预测等的开关,处理器的大小端、MMU的开关、页表 |
CP14 | 调试 |
CP10 | NENO |
CP11 | VFP |
r13~r15不要随便用,所有函数按1、2、3的顺序执行,保证调用函数之后能够正确返回
1、LR->SP
2、SP->LR
3、PC=LR
名称 | 说明 |
---|---|
CPSR | 当前程序状态寄存器 |
SPSR | 保存程序状态寄存器 |
r0~r12 | 写程序时随便用 |
r13 | SP stack pointer(指向在栈中的地址) |
r14 | LR link register(函数返回时的指向) |
r15 | PC process counter(指向程序执行地方) |
arm-cortex-m 只支持thumb thumb2
名称 | 位数 | 说明 |
---|---|---|
arm指令 | 32位 | 高效,占空间比较大 |
thumb指令 | 16位 | 比arm低效,占空间比较小 |
thumb2指令 | 32/16位混合 |
名称 | 位数 |
---|---|
user | 用户模式,执行用户程序 |
system | 系统模式 |
svc | 管理模式,执行boot kernel |
abort | 模式,处理非法访问地址 |
irq | 中断模式 |
fiq | 快速中断 |
unde | 未定义模式,处理未定义指令 |
普通模式:user 只能修改NZCVQ
特权模式:system,svc,unde,abort,irq,fiq
异常模式:svc,unde,abort,irq,fiq 每个异常模式都有自己的r13,r14,SPSR
模式切换:CPSR[NZCVQ EAIFT M[4:0]]
N 负操作1,否则0
Z 零操作1,否则0
C 加法,如果有进位1,否则0 减法,如果有借位0,否则1
V 有溢出1,否则0
Q 有饱和1,否则0
E 如果小端0,否则1
A 中止禁止位,如果为1则禁止了中止异常
I 中断禁止位
F 快速禁止位
T 如果执行的thumb指令则位2,否则为0
M[4:0] 只有特权模式可以写
int main(void){
int a = 100;
int b = 30;
int c = 50;
//假设前面有C代码,把一个数放进r0,自己写的汇编改变了r0的值,这就出问题,所以自己使用前要存下r0的值
//使用完后恢复它,在保护寄存器行写一下就可以
__asm__ __volatile__(
"mov %0,#20\n" //把a的值改为20,%0代表a,不能直接写20,不然会认为是地址,加个#号
"mov r0,#123\n" //把r0寄存器的值赋值123,汇编指令都要\n结尾
"mov %1,r0\n"
"mov r1,%2\n"
"mov %0,r1\n"
:"=&r"(a),"=&r"(b) /**
* 声明输出变量(只能写不能读)和输入输出变量,第一个声明的是%0,
* 第二个是%1,=是输出变量,+是可读可写变量,&是独占寄存器,r代表是寄存器,
* =后记住加&
*/
:"r"(c) //声明输入变量,只能读不能写
:"r0","r1" //保护寄存器,使用寄存器之后还原原来寄存器的值
);
printf("hello asm %d %d %d\n",a,b,c);
/**
* out = in_out;
* in_out = in;
*/
int in_out = 200;
int out;
int in = 52;
asm volatile(
"mov %0,%1\n"
"mov %1,%2\n"
:"=&r"(out),"+r"(in_out)
:"r"(in)
:
)
printf("hello asm out=%d in_out=%d\n",out,in_out);
int d,f,e;
asm volatile(
"mov %[d],#1\n"
"mov %[f],#2\n"
:[d]"=&r"(d),[e]"=&r"(e),[f]"=&r"(f) //可以直接指定每个变量的标号,就可以用%0,%1这种方式访问
//在""前加[随便字符串]即可,解决添加变量在中间后序号改变
:
:
)
printf("hello asm d=%d f=%d\n",d,f);
//减法sub
int g,h,i;
asm volatile(
"mov r0,#9\n"
"mov r1,#8\n"
//要实现g = r0-r1;
"sub %0,r0,r1\n"
:"=&r"(g)
:
:"r0","r1"
);
printf("hello asm sub g=%d\n",g);
/**
* 32位处理器上进行64位减法,I存高位,H存低位
* 0x33333333 00000004
* 0x11111111 00000005
* 0x22222221 ffffffff
*/
asm volatile(
"mov r0,#4\n"
"mov r1,#5\n"
//C减法,如果有借位,Clear C置0,反之Set C置1,看上面模式切换那里,sub加s表示需要借位
"subs %0,r0,r1\n"
"ldr r0,=0x33333333"
"ldr r1,=0x11111111"
//%1 = r0-r1-!C
"sbc %1,r0,r1\n"
:"=&r"(h),"=&r"(i)
:
:"r0","r1"
);
printf("sub = %#x%08X\n",i,h);
//乘法
int mul;
asm volatile(
"mov r0,#3\n"
"mov r1,#4\n"
//mul的三个操作数必须都是寄存器,%0 = r0*r1
"mul %0,r0,r1\n"
:"=&r"(mul)
:
:"r0","r1"
);
printf("hello asm mul=%d\n",mul);
asm volatile(
"mov r0,#3\n"
"mov r1,#4\n"
"mov r2,#5\n"
//mla %0 = r0*r1+r2 结果是17
//mls %0 = r0*r1-r2 结果是7
"mla %0,r0,r1,r2\n"
:"=&r"(mul)
:
:"r0","r1","r2"
);
printf("hello asm mul=%d\n",mul);
/**
* 条件执行,大多数的arm指令都支持
* thumb指令只有跳转指令才支持
* 大于gt,小于lt,等于eq,不等于ne,小于等于le,大于等于ge
*/
int k=5,m=6,l;
asm volatile(
"subs r0,%1,%2\n"
//条件执行
"movgt %0,#1\n" //k-m>0则l=1
"moveq %0,#2\n" //k-m=0则l=2
"movlt %0,#3\n" //k-m<0则l=3
:"=&r"(l)
:"r"(k),"r"(m)
:"r0"
);
printf("hello asm l=%d\n",l);
//专门比较指令
asm volatile(
"mov r0,#4\n"
"mov r1,#5\n"
//cmp指令加不加s都行
"cmp r0,r1\n"
"movgt %0,#1\n" //r0>r1则l=1
"moveq %0,#2\n" //r0=r1则l=2
"movlt %0,#3\n" //r0
:"=&r"(l)
:
:"r0","r1"
);
printf("hello asm l=%d\n",l);
//专门比较是否相等指令
asm volatile(
"mov r0,#4\n"
"mov r1,#5\n"
//teq指令加不加s都行
"teq r0,r1\n"
"moveq %0,#1\n" //r0=r1则l=1
"movne %0,#2\n" //r0!=r1则l=2
:"=&r"(l)
:
:"r0","r1"
);
printf("hello asm l=%d\n",l);
//判断某一个寄存器的某一位是否为0,此题输出1
asm volatile(
//0xf0 = 1111 0000,从右边第0位开始数
"mov r0,#0xf0\n"
//r0&(1<<4)按位与的操作,判断第四位是否为0
"tst r0,#(1<<4)\n"
"moveq %0,#0\n" //如果等于0,输出0
"movne %0,#1\n" //如果不等于0,输出1
:"=&r"(l)
:
:"r0"
);
/**
* 逻辑运算
* and 两个是1才是1,其余都是0
* orr 有1个是1都是1
* eor 不同为1,相同为0
* bic
*
* mvn r0,#0xff 按位取反
* r0 = ~0x000000ff = 0xffffff00
*
* 移位运算符<<向左移,>>向右移
* 需要移位的数字 << 移位的次数
* 例如: 3 << 2,则是将数字3左移2位
* 首先把3转换为二进制数字0000 0000 0000 0000 0000 0000 0000 0011,然后把该数字高位(左侧)的两个零移出
* 其他的数字都朝左平移2位
* 最后在低位(右侧)的两个空位补零。则得到的最终结果是0000 0000 0000 0000 0000 0000 0000 1100
* 则转换为十进制是12。不溢出的情况下
* 相当于3*2的2次方
* 3*(2^2) = 12
*/
int and,orr,eor,bic;
asm volatile(
"mov r0,#5\n" //0000..0101
"mov r1,#6\n" //0000..0110
//%0 = r0&r1
"and %0,r0,r1\n"
"orr %1,r0,r1\n"
"eor %2,r0,r1\n"
"mov r0,#0xff" //想把第2位清零,从右边第0位开始数
"bic %3,r0,#4\n"//或者#4 = #(1<<2)
:"=&r"(and),"=&r"(orr),"=&r"(eor),"=&r"(bic)
:
:"r0","r1"
);
printf("hello asm and=%d orr=%d eor=%d bic=%x\n",and,orr,eor,bic);
/**
* lsl逻辑左移,lsr逻辑右移
* asr算术右移,ror循环右移
*/
int lsl,lsr,asr,ror;
asm volatile(
"mov r0,#20\n"
"mov %0,r0,lsl #1\n" //%0 = r0<<1 结果是40
"mov %0,r0,lsr #1\n" //%1 = r0>>1 结果是10
"mov r0,#-20\n" //20 = 0000...10100 -20 = 1000...10100 补码:1111...01100
//计算机中存的都是补码
"mov %2,r0,lsr #1\n" //逻辑右移一位得到0111...0110
"mov %3,r0,asr #1\n" //算术右移一位得到1111...0110
"mov r0,#0#ff\n"
"mov %4,r0,ror #4\n" //把最低位循环4次移到最高位,得到0xf000000f
:"=&r"(lsl),"=&r"(lsr),"=&r"(asr),"=&r"(ror)
:
:"r0",
);
printf("hello asm lsl=%d lsr=%d asr=%d ror=%x\n",lsl,lsr,asr,ror);
int n;
//读CPSR
asm volatile(
"mrs %0,cpsr\n"
:"=&r"(n)
:
:
);
printf("cpsr = %#x\n",n);
//CPSR中有NZCVQ EAIFT
asm volatile(
"mrs r0,cpsr\n"
"orr r0,r0,#(1<<7)\n"
"msr cprs,r0\n" //修改CPSR中的值,此时处于用户模式,只能修改NZCVQ这5位,这里修改了第7位,修改失败
:
:
:"r0"
);
return 0;
}
/*
* 直接调用内核函数
* 内核中为每一个系统调用有编号
* 在/arch/arm/include/asm/unistd.h中
* fork 2
* read 3
* write 4
*/
int main(void)
{
char *s = "hell syscall\n";
int ret;
write(1,s,strlen(s));
syscall(4,1,s,strlen(s));
//对于arm平台系统调用号有一个0x900000的偏移
asm volatile(
"mov r0,#1\n"
"mov r1,%1\n"
"mov r2,#14\n"
"swi 0x900004\n" //进入内核
"mov %0,r0\n" //返回值在r0中
:"=&r"(ret)
:"r"(s)
:"r0","r1","r2"
);
printf("ret =%d\n,ret");
return 0;
}