400m=_______um 一介鲁夫完整过程图91的过程

>>> GNU ARM汇编 - 开篇
GNU ARM汇编 - 开篇
  在大学的时候,汇编就是学的很烂.一是对汇编这门语言没概念,二是那些指令集很难记清楚,用的机会也少,自然学的不好.但是现在觉得相当有必要重头学习一下汇编.部分原因我在上一篇写完设备模型的总结时提到了而.最近在看一本书《ARM:Assembly Language Programming》,作者是Peter Knaggs & Stephen Welsh.作者在开头也提出学习汇编的必要性和重要性,借他们的话重新说一下:
  他们首先用三个问句来引出为什么要学习汇编:
  外科医生为了知道手术刀的用法而需要学习冶金吗?飞行员需要学习热力学理论来理解飞机引擎是如何工作的?报社的记者要学习电子学来理解照相机如何工作的?答案当然是&NO&,那么为什么要让学生学习汇编语言和计算机结构呢?
  第一就是教育和培训不是一码事,大学就应该学大学里该教的内容,当然学生也得好好学.
  第二是汇编语言是计算机体系结构和计算机软件的基础
  第三:汇编可以帮助我们更好的理解数据类型,指针,堆栈、函数调用、递归调用以及参数传递,汇编可以更直接的访问IO和设备,帮助理解设备驱动等等.
  总之汇编会告诉我们计算机系统内部正在发生什么
  而按照目前我个人的体会是:
  学习汇编可以让我更好的写C,学习汇编可以让我更好的理解loader,学习汇编可以让我更好的理解进程和内存管理,虚拟存储器和存储器映射等知识.总之,好处多多.目前汇编成了我的一个瓶颈,那搞定它可以让我上一个台阶.
  接下来学习汇编的环境为:操作系统为Ubuntu,编译器是arm-linux-gcc,平台是s3c2440(ARM920).
  ARM汇编的指令集就那些,但是windows下的arm汇编和linux的arm汇编有细微区别.windows下是用ADS codewarrir等环境来开发,编译器是armasm,而linux用的是gnu arm,更个接近于AT&T风格.偏向于linux下的环境,所以就选择了GNU ARM汇编.
  学习GNU ARM汇编的计划和目标:
  ARM体系结构
  ARM下的指令集
  GNU汇编的风格
  在s3c2440平台上的验证
  当然最终的目的还是为了学习loader和linux.
  开始吧,新的学习目标~~~GNU的汇编器是GNU Tools的一部分,可以用来ARM的汇编语言源代码编译为二进制文件.关于GNU汇编器的介绍可以搜索《GNU Assembler Manual》.这里我们只是做一个简短的介绍,对GNU汇编器有一个大概的认识,同时通过两个例子了解一下GNU ARM汇编.
  给出一个模板文件:
&&&&.text&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&;&Executable&code&follows&&
_start:&.global&_start&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&;&"_start"&is&required&by&the&linker&&
&&&&.global&main&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&;&"main"&is&our&main&program&&
&&&&b&main&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&;&Start&running&the&main&program&&
main:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&;&Entry&to&the&function&"main"
;&Insert&your&code&here&&
&&&&mov&pc,lr&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&;&Return&to&the&caller&&
&&&&.end&&&&&&& 汇编器的使用:
  一种汇编器是arm-elf-as,一种是arm-linux-as之类的,这两种汇编器是有细微区别.但是一般做开发,半导体厂商都会提供特定的编译器,用那个编译器应该是没错的,而且优化效果应该是最优的,毕竟是芯片公司提供的嘛.他们对体系架构最了解,很清楚的知道怎么去优化.而我们一般的开发者也可以了解处理器的体系架构和嵌入式系统的系统的特征来对汇编代码和c代码做优化.
  编译过程:
  arm-elf-as -marm7tdmi --gdwarf2 -o filename.o filename.s
  -marm7tdmi是指定CPU,arm7tdmi是属于ARMv4T的,一般来说同是ARMv4T应该是兼容的.
  --gdwarf2是表示包含debug信息.
  链接过程:
  arm-elf-ld -o filename.elf filename.o
  和UNIX系统编程一样,我们可以根据上面的步骤写makefile,然后make一下.
  具体ARM的指令集,伪指令就不写了,资料很多.
  下面举两个ARM汇编的实例,一个是裸机下的蜂鸣器(简单的控制GPIO而已,比流水灯还简单),一个是ARM linux下的"hello world"(利用系统调用来实现的).
  蜂鸣器的例子如下:
  beep.lds &beep.S &Makefile &start.S
  start.S:
.global&_start&&
&&&&ldr&&&&&r3,&=0x&&&&&@&WATCHDOG寄存器地址&&
&&&&mov&&&&&r4,&#0x0&&&&&&&&&&&&&&&&&&&&&&&
&&&&str&&&r4,&[r3]&&&&&&&&&&&&&&@&写入0,禁止WATCHDOG,否则CPU会不断重启&&
&&&&ldr&&&&&sp,&=1024*2&&&&&&&&&@&设置堆栈,注意:不能大于4k,&因为现在可用的内存只有4K&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@&nand&flash中的代码在复位后会移到内部ram中,此ram只有4K&&
&&&&bl&&&&&&_main&&&&&&&&&&&&&&&&@&跳转到main函数&&
halt_loop:&&
&&&&b&&&&&&&halt_loop&&beep.S
.equ&&&&GPBCON,&&&0x&&&&
.equ&&&&GPBDAT,&&&0x&&&&
.global&_main&&&&
&&&&ldr&r0,=GPBCON&&
&&&&ldr&r1,=0x1&&
&&&&str&r1,&[r0]&&
&&&&ldr&r2,=GPBDAT&&
&&&&ldr&r1,=0x1&&
&&&&str&r1,[r2]&&
&&&&bl&delay&&
&&&&ldr&r2,=GPBDAT&&
&&&&ldr&r1,=0x0&&
&&&&str&r1,[r2]&&
&&&&bl&delay&&
&&&&b&loop&&
&&&&ldr&r3,=0x4ffffff&&
&&&&sub&r3,r3,#1&&
&&&&cmp&r3,#0x0&&
&&&&bne&delay1&&
&&&&mov&pc,lr&&
.end&&&&&&& beep.lds
OUTPUT_FORMAT("elf32-littlearm",&"elf32-littlearm",&"elf32-littlearm")&&
OUTPUT_ARCH(arm)&&
ENTRY(_start)&&
SECTIONS{&&
&&&&.&=&0x;&&
&&&&.text&:&{&&
&&&&&&&&*(.text)&&
&&&&&&&&*(.rodata)&&
&&&&.data&ALIGN(4):&{&&
&&&&&&&&*(.data)&&
&&&&.bss&ALIGN(4):&{&&
&&&&&&&&*(.bss)&&
}&&&&&&&&&makefile:
CROSS&=&&arm-linux-&&
CFLAGS&=&-nostdlib&&
beep.bin:&start.S&beep.S&&
&&&&${CROSS}gcc&$(CFLAGS)&-c&-o&start.o&start.S&&
&&&&${CROSS}gcc&$(CFLAGS)&-c&-o&beep.o&beep.S&&
&&&&${CROSS}ld&-Tbeep.lds&start.o&beep.o&-o&beep.elf&&
&&&&${CROSS}objcopy&-O&binary&-S&beep.elf&beep.bin&&
&&&&rm&-f&&*.o&&
&&&&rm&-f&*.elf&*.o&&
&&&&rm&-f&beep.bin&&&&&&&&& 编译后将beep.bin文件烧写到dram中,就可以听到声音了.虽然可以运行了,但还是有两个疑问:
  1.lds编译链接文件的写法和技巧 & &//后续要继续追
  2.elf文件的格式 & & & &//elf格式是比较新的可执行文件格式,目前在很多OS上都是用这种格式.这个格式可以在有操作系统的情况下直接运行,但是对于裸机的情况,必须对elf文件 & & & & & & & & & & & & & & & & & & & & & & & &做objcopy处理 & & & &后续也要继续追
  hello world的例子如下:
  helloworld.S:
&&&&msg:&&.asciz&&"hello,&world\n"
&&&&&&&.align&&2&&
&&&&&&&.global&_start&&
&&&&&&&ldr&&&&&r1,&=msg&&&&&&&&&@&address&&
&&&&&&&mov&&&&&r0,&#1&&&&&&&&&&@&stdout&&
&&&&&&&mov&&&&&r2,&#13&&&&&&&&&@&length&&
&&&&&&&swi&&&&&#0x900004&&&&&&&@&sys_write&&
&&&&&&&mov&&&&&r0,&#0&&
&&&&&&&swi&&&&&#0x900001&&&&&&&@&sys_exit&&
&&&&&&&.align&&2&&
&&&&arm-linux-as&helloworld.S&-o&helloworld.o&&
&&&&arm-linux-ld&&helloworld.o&-o&helloworld&&将elf文件放到跑有linux的arm板子中,运行就输出hello world.也可以在Ubuntu中qemu-arm helloworld模拟.
  对比x86下同样用系统调用来输出hello world的程序:
&&&&msg:&.string&"hello\n"
&&&&len&=&.&-&msg&&
.global&_start&&
&&&&movl&$len,&%edx&&
&&&&movl&$msg,&%ecx&&
&&&&movl&$1,&%ebx&&
&&&&movl&$4,&%eax&&
int&$0x80&&
&&&&movl&$0,&%ebx&&
&&&&movl&$1,&%eax&&
int&$0x80&&&&&&&&&&&&&它们有几点不同:
  1.arm是用swi指令来进行软中断,陷入内核态来实现系统调用的.而x86是用int $0x80
  2.x86的系统调用号是用eax寄存器表示的,是第一个参数.而arm的swi直接带有系统调用号,0xx,其中0x900000是base.
  根据google,做了上面的总结,对GNU ARM汇编有了认识,并且对系统调用软中断,中断处理,uboot异常向量表等等有了探究的欲望,也对elf格式和编译链接有了兴趣,根据自己的方向和精力,后续对这些内容做一个或深或浅的学习.  这一篇的知识来源全部来自《ARM System Developer's Guide》
  从编程人员的视角来看,arm核是由数据总线连接的功能单元组成,如下图所示:
  数据通过数据总线流向处理器核心,这里的数据可以是将要执行的指令,也可以是数据项.上面的图是Von Neumann体系的arm核,数据项和指令共用同一总线.而h哈佛结构体系的arm核就会用两个不同的总线.
  就像所有的RISC处理器,arm采用load-store体系结构.也就是说它含有两条不同的指令类型来出入处理器.loar指令将数据从内存拷贝到寄存器,store指令是将数据从寄存器拷贝到内存.没有直接操作内存中数据的数据处理指令.数据的处理离不开寄存器.
  ARM指令一般有两个源寄存器,Rn和Rm,和一个目的寄存器,Rd.ARM的一个重要特性是Rm可以在送入ALU之前被桶型移位器做预处理,这样就会有很多形式的表达式和寻址方式.
  下面介绍ARM处理器的几个关键分量:寄存器,当前程序状态寄存器和流水线
  寄存器:
  通用目的寄存器可以保存数据或者地址.由字母r和编号数字来表示.所有的寄存器都是32bit大小.
  一共有18个活动的寄存器:16个数据寄存器和两个程序状态寄存器.数据寄存器由r0--r15表示.
  ARM处理器有3个寄存器分配为特殊的功能:r13,r14,r15
  r13通常用作栈指针(sp),存储当前处理器工作模式下的栈顶;
  r14被称作链接寄存器(lr),当发生调用时存放调用子程序的返回地址;
  r15被称作程序计数器(pc),保存被处理器预取的下一条指令的地址.
  除了这16个数据寄存器,还有来年各个程序状态寄存器:cpsr和spsr.处理器的当前工作模式决定了哪些寄存器是可见的.
  当前程序状态寄存器:
  ARM核使用cpsr来显示和控制内部的操作.通用程序状态寄存器的内部如下:
  cpsr分为四个域,每个域有8位的宽度:flags,status,extension和ntrol域包含处理器模式和状态以及中断屏蔽位.flags域包含condition flags.
  处理器模式:
  处理器模式决定了当前哪些寄存器是可用的以及cpsr本身的访问权限.处理器模式分为特权模式和非特权模式:特权模式对cpsr有完全的读写控制.而非特权模式
  只能读cpsr的control域但是仍可以读写condition flags.
  一共有7中处理器模式:六种特权模式(abort,fiq,irq,svc,system和undefined)和一种非特权模式(user).
  当试图访问内存失败时处理器会进入abort模式,fiq和irq对应ARM处理器的两种中断级别.svc是系统reset后进入的模式,也是os kernel工作的模式.
  system模式是user模式的特殊版本,它有对cpsr的读写控制.undefined模式在处理器遇到未定以的指令或者不支持的操作时使用.
  user模式在应用程序下使用.
  Banked registers:
  除了user模式,其它模式都可以通过直接写cpsr的模式位来改变处理器模式.当然,在异常或者中断到来时,硬件会自动切换模式.下类异常和中断会引起模式切换:
  reset,irq,fiq,swi,data abort,prefetch abort和undefined instruction.
  处理器模式:
  需要注意的是:通过直接写cpsr来改变模式的方法不会将cpsr拷贝到spsr.只有在异常或中断发生时cpsr的值才会保存.
  当ARM核上电时,处于svc特权模式.从特权模式开始是有用的:初始化代码完全控制cpsr来建立其他模式的堆栈.
  cpsr中的bit T是表明指令簇用的是ARM指令集还是thumb指令集.T为1表示thumb指令集,T为0表示ARM指令集.
  ARM指令集和thumb指令集的特性:
  [img][/img]
  中断屏蔽位:
  中断屏蔽位用来屏蔽处理器的特殊中断请求.在ARM核中有两种级别的中断请求:irq和fiq.
  cpsr中的中断屏蔽位是第7位和第6位(I和F),当I或F设为1时irq或fiq就被屏蔽了.
  condtion flags:
  condition flags可以被比较操作和带S后缀的ALU操作来更新.
  条件执行:
  条件执行控制指令是否被ARM核执行.处理器会比较指令的条件属性和cpsr中的condition flags,如果匹配,该指令执行;否则该指令被忽略.
  [img][/img]
  流水线:
  RISC处理器用流水线机制来执行指令.ARM流水线中的指令只有在完全通过执行阶段才被处理.
  上面这张图标明了流水线的使用和程序计数器pc.在执行阶段,pc总是指向该指令地址加上8字节.也就是说pc总是指向当前指令的下下条指令.当用pc来计算
  相对偏移量时这点是很重要的,并且它也是所有流水线的特征.
  异常 中断和向量表
  当异常或中断发生时,处理器会将pc指向一个特殊的内存地址.该地址所在的地址范围称为向量表.向量表的入口是跳转指令,跳转到专门处理某个异常或中断的
  子程序.
  存储器映射地址0x是为向量表预留的.在某些处理器中向量表可以放在更高的地址,从0xffff0000开始.linux等操作系统可以利用这个特性.
  当异常或者中断发生时,处理器挂起正常的处理然后从向量表中加载指令.每个向量表的入口都包含指向特殊处理例程的跳转指令.
  reset向量是处理器上电后执行的第一条指令,这条指令跳转到初始化代码处.
  undefined instruction向量是当处理器不能对指令译码时使用的.
  software interrupt向量执行SWI指令时使用的.SWI可以用来系统调用的实现.
  prefetch abort向量发生在当没有访问权限的条件下试图获取该地址的指令时,异常发生在译码阶段.
  data abort向量与prefetch abort类似,只不过是访问数据发生的异常.
  interrupt request向量被外部硬件用来中断处理器的正常执行.只有在cpsr的相应位为0时才能发生.
  fast interrupt request向量与irq类似,是为要求更短的中断响应时间的硬件保留的.只有在cpsr的相应位为0时才能发生.  在写这篇blog之前,不得不感慨一句:纸上得来终觉浅,绝知此事要躬行.作为EE出身的,虽然好久好久没用汇编写单片机的中断了,但自我感觉对中断的理解还是比较深入的,本以为在GNU ARM汇编下搞个中断会很容易,谁知道断断续续花了我几周.完全用汇编写中断和用c中的_irq写中断还是有区别的,谁用谁知道.还是那句话:深入细节是必须的,也是值得的.
  这一篇blog的理论知识主要来源于:《ARM System Developer's Guide》.
  ARM的异常和相应的模式之间的对应关系见下表:
  当一个异常导致模式的改变时,内核自动地:
  1、把cpsr保存到相应模式下的spsr
  2、把pc保存到相应模式下的lr
  3、设置cpsr为相应异常模式
  4、设置pc为相应异常处理程序的入口地址
  从异常中断处理程序返回包含下面两个操作:
  1、从spsr_mode中恢复内容到cpsr中
  2、从lr_mode中恢复内容到pc中,返回到异常中断的指令的下一条政令处执行.
  上面刚提到了异常发生时内核的一些动作,那对与IRQ或者FIQ而言,还多一项变化:禁用相关的中断IRQ或FIQ,禁止同类型的其他中断被触发.
  对于最简单的非嵌套中断处理的处理流程如下:
  下面给出汇编代码:
本文标题:
本文链接:
本文内容由网友发布,仅代表网友个人经验或观点,不代表本网站立场和观点。如果本文侵犯了您的知识产权,请与我们
取得联系,我们会及时修改或删除。
Linux技术文档
Linux就业指导
Linux练习题
价格:免费
价格:免费
价格:免费
关注希赛微信
接听时间:工作日00:00-24:00(仅收市话费)
&&|&&关于希赛&&|&&加入希赛&&|&&常见问题&&|&&免责声明&&|&&联系希赛&&|&&网站地图&&&&&&&&希赛网&&版权所有&&&&&&&&&

我要回帖

更多关于 鸟笼完整制作过程图解 的文章

 

随机推荐