ARM7启动代码
1:PRESERVE8:
Reguire8和Preserve8
C和汇编有8位对齐的要求,这两个伪指令可以满足此要求,存在REQUIRE8<——> PRESERVE8的对应关系,但不是说有一个REQUIRE8就要有一个 PRESERVE8,如果是一个c文件和一个汇编文件的调用,也就涉及一个PRESERVE8或者是一个REQUIRE8.
另外,REQUIRE8和PRESERVE8并不完成8 byte 对齐的操作,对齐由ALIGN完成。
将ADS的代码移植到KEIL MDK上需要做的修改:
当用户拥有ADS遗留工程的所有源代码时,使用MDK重新编译链接全部代码是最好的解决方法,MDK中的新版本编译工具会重新生成满足堆栈8BYTE对齐要求的目标文件,避免由于堆栈不对齐引起的链接错误.
从ADS到KEIL很重要的一个修改的地方就是这里的8BYTE对齐,想要编译通过,在startup.s里面我们必须加入PRESERVE8指令,使得寄存器8BYTE对齐.
代码:
CODE32
PRESERVE8 ;这个在KEIL里面是必须的,要求8BYTE对齐.在ADS的启动代码中就没有.
AREA vectors,CODE,READONLY
2: ARM的处理器可工作于多种模式,下面设置个模式的一些参数.
Mode_USR EQU 0x10 用户模式
Mode_FIQ EQU 0x11 快中断模式
Mode_IRQ EQU 0x12 中断模式
Mode_SVC EQU 0x13 管理模式
Mode_ABT EQU 0x17 中止模式
Mode_UND EQU 0x1B 未定义模式
Mode_SYS EQU 0x1F 系统模式
参数的由来:这里各个模式的参数是由寄存器CPSR的模式位设置M[4:0]得来的,比如这里的用户模式,CPSR的M[4:0]设置为10000就是0x10,同理其他.详见<<ARM嵌入式系统基础教程>>P47页,CPSR设置很关键!
3:
I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled
也和CPSR寄存器的设置有关,这里两位是禁止/开启快速中断和一般中断的设置.
4: 各模式下定义的堆栈地址.
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000100
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000100
USR_Stack_Size EQU 0x00000200
设置堆栈大小
Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size +
FIQ_Stack_Size + IRQ_Stack_Size + USR_Stack_Size)
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
Stack_Top EQU Stack_Mem + Stack_Size
堆栈大小的设置,各公司写的启动代码有所不同,但是不影响大局,可以借鉴一些你认为比较简单的启动代码,然后写自己的堆栈地址和大小设置程序.
5:堆的设置
Heap_Size EQU 0x00000000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem SPACE Heap_Size
AREA Init,CODE,READONLY,ALIGN=3 //指定后面的指令为8位对齐(2的3次方)
align n
它的含义就是使得下面的代码按一定规则对齐,.align n 指令的对齐值有两种方案,n 或 2^n ,各种平台最初的汇编器一般都不是gas,采取方案1或2的都很多,gas的目标是取代原来的汇编器,必然要保持和原来汇编器的兼容,因此在gas中如何解释 .align指令会显得有些混乱,原因在于保持兼容。arm-linux是按照2^n的方案对齐的. ARM的.align 5就是2的5次方(32位)对齐,也就是4字节(32位)对齐.
6: AREA RESET, CODE, READONLY
ARM
下面的是ARM的代码,不是THUMB.
基于ARM的芯片多数为复杂的片上系统,这种复杂系统里的多数硬件模块都是可配置的,需要由软件来设置其需要的工作状态。因此在用户的应用程序之前,需要由专门的一段代码来完成对系统的初始化。由于这类代码直接面对处理器内核和硬件控制器进行编程,一般都是用汇编语言。一般通用的内容包括:
中断向量表
初始化存储器系统
初始化堆栈
初始化有特殊要求的断口,设备
初始化用户程序执行环境
改变处理器模式
呼叫主应用程序
中断向量表
ARM芯片上电或复位后,系统进入管理模式、ARM状态、PC(R15)指向0x00000000地址处。中断向量表为每一个中断设置1个字的存储空间,存放一条跳转指令,通过这条指令使PC指针指向相应的中断服务程序入口,继而执行相应的中断处理程序。
ARM要求中断向量表必须放置在从0地址开始,连续8X4字节的空间内。每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理。
中断向量表的程序实现通常如下表示:
CODE32
AREA StartUp,CODE,READONLY
ENTRY
;中断向量表
Vectors
LDR PC, ResetAddr //把Reset_Addr地址处的内容放入PC中
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD 0xb9205f80
LDR PC, [PC, #-0xff0] //LDR PC, IRQ_Addr 当前PC值减去0xFF0等于IRQ中断入口地址
LDR PC, FIQ_Addr
Reset_Addr DCD Reset_Handler //#该地址标号存放Reset_Handler程序段的入口地址
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler
注:
1、其中关键字ENTRY是指定编译器保留这段代码,因为编译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码被链接在0地址处,并且作为整个程序的入口。
2、异常是由内部或外部源产生的以引起处理器处理的一个事件。ARM处理器核支持7种类型的异常。异常出现后,CPU强制从异常类型对应的固定存储地址开始执行程序。这个固定的地址就是异常向量。向量从上到下依次为复位、未定义指令异常、软件中断、预取指令中止、预取数据中止、保留的异常、IRQ和 FIQ。
3、IRQ向量“LDR PC, [PC, #-0xff0]” 使用的指令与其它向量不同。在正常情况下这条指令所在地址0X00000018。当CPU执行这条指令但还没有跳转时,PC的值为0X00000020,0X00000020减去 0X00000FF0为 0XFFFFF030,这是向量中断控制器(VIC)的特殊寄存器VICVectAddr。这个寄存器保存当前将要服务的IRQ的中断服务程序的入口,用这一条指令就可以直接跳转到需要的中断服务程序中。
VICVectAddr寄存器的地址0xFFFFF030:PC当 前位置是0x18,读出的值为0x18+8 = 0x20,0x20-0xff0 = 0xFFFFF030
4、至于在保留的异常向量“DCD 0xb9205f80”位置填数据0xb9205f8是为了使向量表中所有的数据32位累加和为0。
初始化存储器系统
1、存储器类型和时序配置
通常Flash和SRAM同属于静态存储器类型,可以合用同一个存储器端口;而DRAM因为有动态刷新和地址线复用等特性,通常配有专用的存储器端口。存储器端口的接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要考虑到由此带来的稳定性问题。
2、存储器地址分布
一种典型的情况是启动ROM的地址重映射。
初始化堆栈
因为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给SP定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,让后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就不能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。
InitStack
MOV R0, LR ;使用R0来保存返回地址
MSR CPSR_c, #0xd2 ;设置中断模式堆栈 /切换到IRQ模式,并初始化IRQ模式的堆栈/
LDR SP, StackIrq
MSR CPSR_c, #0xd1 ;设置快速中断模式堆栈
LDR SP, StackFiq
MSR CPSR_c, #0xd7 ;设置中止模式堆栈
LDR SP, StackAbt
MSR CPSR_c, #0xdb ;设置未定义模式堆栈
LDR SP, StackUnd
MSR CPSR_c, #0xdf ;设置系统模式堆栈
LDR SP, StackSys
MOV PC, R0 ;使用R0来返回
注:
1、MSR CPSR_c, #0xdf, 这一句把ARM的工作模式设置为系统模式,或者也可以说是用户模式, 因为系统模式与用户模式是共享相同的寄存器组. 用0xdf对CPSR寄存器赋值,就把IRQ中断关闭了(可以查一下CRSR的详细说明), 代码正常执行时处理器是处在用户模式的,所以IRQ中断是不会执行的.
2、并不是每一种模式下的堆栈都用设置的, 比如说如果你的程序中不会用到FIQ,就可以不用设置快速中断下的堆栈.
3、LDR SP, =StackUsr这个语句, 其它都是没有=号的, 为什么这个要用等号呢? 这就是LDR伪指令与LDR指令的区别了, LDR SP, =StackUsr是把StackUsr表示的地址装载到sp, LDR SP, StackUnd是把StackUnd表示地址的内容装载到sp
StackIrq DCD (IrqStackSpace + IRQ_STACK_LEGTH * 4 - 4)
StackFiq DCD (FiqStackSpace + FIQ_STACK_LEGTH * 4 - 4)
StackAbt DCD (AbtStackSpace + ABT_STACK_LEGTH * 4 - 4)
StackUnd DCD (UndtStackSpace + UND_STACK_LEGTH * 4 - 4)
StackSys DCD (SysStackSpace + SYS_STACK_LEGTH * 4 -4 )
可以看到,没有”=”的标号都已经用DCD初始化了, 而StackUsr到底是什么呢, 它是由下面的语句决定的
/* 分配堆栈空间 */
AREA MyStacks, DATA, NOINIT
IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;中断模式堆栈
FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ;快速中断模式堆栈
AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ;中止义模式堆栈
UndtStackSpace SPACE UND_STACK_LEGTH * 4 ;未定义模式堆栈
SysStackSpace SPACE SYS_STACK_LEGTH * 4 ;系统模式堆栈
因为程序需要切换模式,而且程序退出时CPU的模式已经不再是管理模式而是系统模式LR已经不再保存返回程序地址,所以程序首先把返回地址保存到 R0中,同时使用R0返回。然后程序把处理器模式转化为IRQ模式,并设置IRQ模式的堆栈指针。其中变量Stacklrq保存着IRQ模式的堆栈指针的初始值,Irqstackspace是分配给 IRQ模式的堆栈空间的开始地址,IRQ_STACK_LEGTH是用户定义的常量,用于设置 IRQ模式的堆栈空间的大小。程序使用同样的方法设置FIQ模式堆栈指针、中止模式堆栈指针、未定义堆栈指针和系统模式堆栈指针。
程序使用编译器分配的空间作为堆栈,而不是按照通常的做法把堆栈分配到 RAM的顶端,之所以这样是因为这样做不必知道RAM顶端位置,移植更加方便;编译器给出的占用RAM空间的大小就是实际占用的大小,便于控制RAM的分配。
初始化有特殊要求的端口,设备
初始化应用程序执行环境
映像一开始总是存储在ROM/Flash里面的,其RO部分即可以在ROM/Flash里面执行,也可以转移到速度更快的RAM中执行;而RW和ZI这两部分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容清零。
下面是在ADS下,一种常用存储器模型的直接实现:
LDR r0,=|Image$$RO$$Limit| //得到RW数据源的起始地址
LDR r1,=|Image$$RW$$Base| //RW区在RAM里的执行区起始地址
LDR r2,=|Image$$ZI$$Base| //ZI区在RAM里面的起始地址
CMP r0,r1 比较它们是否相等
BEQ %F1
0 CMP r1,r3
LDRCC r2,[r0],#4
STRCC r2,[r1],#4
BCC %B0
1 LDR r1,=|Image$$ZI$$Limit|
MOV r2,#0
2 CMP r3,r1
STRCC r2,[r3],#4
BCC %B2
程序实现了RW数据的拷贝和ZI区域的清零功能。其中引用到的4个符号是由链接器第一输出的。
|Image$$RO$$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址
|Image$$RW$$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址
|Image$$ZI$$Base|:ZI区在RAM里面的起始地址
|Image$$ZI$$Limit|:ZI区在RAM里面的结束地址后面的一个地址
程序先把ROM里|Image$$RO$$Limt|开始的RW初始数据拷贝到RAM里面|Image$$RW$$Base|开始的地址,当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地址|Image$$ZI$$Limit|
改变处理器模式
因为在初始化过程中,许多操作需要在特权模式下才能进行(比如对CPSR的修改),所以要特别注意不能过早的进入用户模式。
内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做总是安全的。
呼叫主应用程序
当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:
IMPORT main
B main
直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。
IMPORT __main
B __main
__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()函数。
进入C应用程序
至此,系统各部分的初始化基本完成,可以直接从启动代码转入到应用程序的main()函数入口。从启动代码转入到应用程序的实例代码如下:
IMPORT main
LDR R0,=main
BX R0
转载于:https://blog.51cto.com/2412315/421372
相关文章:

一次完整请求的日志
一次完整请求的日志:各种配置文件:spring-mvc.xml<?xml version"1.0" encoding"UTF-8"?><beans xmlns"http://www.springframework.org/schema/beans" rel"nofollow"" target"_blank"…

Aveva Marine C# 二次开发入门001
1# 引用 C:\AVEVA\Marine\OH12.1.SP4\Aveva.ApplicationFramework.dll C:\AVEVA\Marine\OH12.1.SP4\Aveva.ApplicationFramework.Presentation.dll 2# 引用命名空间, using Aveva.ApplicationFramework.Presentation;using Aveva.ApplicationFramework; 3# 继承接口…

搜集《ASP.NET中常用的26个优化性能方法》
1. 数据库访问性能优化 a.数据库的连接和关闭 访问数据库资源需要创建连接、打开连接和关闭连接几个操作。这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源。ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响…

【matlab】我要自学网笔记总结 1.3
1.3 1、在matlab的命令行窗口可以直接进行数学运算 2、π 和平方的使用 S pi*r^2 3、如果输入一个多位小数,输出时只显示小数点后四位,但计算的时候使用的是真实值。 如果要改变显示的位数 (1)可以在 预设 - 命令行窗口 - 数值…

IT规划的企业应用实践(6)研究背景 之 企业信息化建设的诉求
研究背景 之 企业信息化建设的诉求 从实践角度看,企业信息化建设的诸多问题和诉求,可以归纳为以下几个方面: 1. IT系统本身: l 面对成本的压力和客户的要求,在流程方面、运作方面离不开IT支持,IT系统如何支…

Codeforces Gym100812 L. Knights without Fear and Reproach-扩展欧几里得(exgcd)
补一篇以前的扩展欧几里得的题,发现以前写错了竟然也过了,可能数据水??? 这个题还是很有意思的,和队友吵了两天,一边吵一边发现问题??? L. Knights without F…

Tarjan无向图连通性
割点:去掉某点x,该无向图分割成两部分(及以上) 割边:去掉某条边x,该无向图分割成两部分(及以上) 时间戳:在搜索树上的遍历序号dfn 追溯值:subtree子树和非搜索…

php去除字符串首尾空格(包括全角)(转)
<? $str" dfdfdf曊壷顳 道德观第三附属 "; $str mb_ereg_replace(^( | ), , $str); $str mb_ereg_replace(( | )$, , $str); echo mb_ereg_replace( , "\n ", $str); ?>转载于:ht…

【单片机】写电子钟时遇到的问题
1、<> 与""的区别 1、<> 先去系统目录中找头文件,如果没找到再去当前目录下找。 所以一般用于向标准的头文件如 studio.h 和 stdlib.h 等方法。 2、"" 首先在当前目录下寻找,如果找不到在去系统目录下寻找。这个用于自…

什么是业务组件?
海浪给大家分享了一些关于业务组件的文章,但是什么是业务组件呢?海浪转载了yongtree写的这篇文章。业务组件是一系列不可分割的业务活动,是构建专业化企业的功能模块。业务组件的优势在很大程度上来源于其具备两个相关但截然不同的特性&#…

3.3.2 函数参数不得不说的几件事
函数定义时圆括号内是使用逗号分隔开的形式参数列表(parameters),一个函数可以没有参数,但是定义和调用时一对圆括号必须要有,表示这是一个函数并且不接受参数。函数调用时向其传递实参(argumentsÿ…

wpf 对控件进行截图,获取快照
有时候我们项目,在执行某个操作后,会生成一些数据结果,如报表一类的东西,我们需要对结果进行保存,甚至是生成word文档。 那么首先获取到控件快照就最基本的条件。 生成快照的静态方法类 using System; using System.Co…

【java】兴唐第二十一节(LinkedList和泛型)
LinkedList知识点 1、实现了Iterable接口的类具有迭代功能。 2、List接口为Collection的子类,表示线形数据列表,其实现类有:ArrayList(数组线性表)与LinkedList(链表) 算了不多说了,上图吧 3、ArrayList是一个可变数组ÿ…

Elgg网站迁移指南
转载地址: http://blog.sina.com.cn/s/blog_84068de60100vr21.html Elgg官方文档上的网站迁移部分是有问题的——缺少了一些重要步骤,而且过程更麻烦。正确的方法如下: 备份网站文件,包括uploads文件夹导出数据库在数据库文件中&a…

INFO:在InstallShield中修改安装包压缩.cab包的大小
如果我们用InstallShield打包一个数据非常大的安装包(Basic MSI和InstallScript MSI工程类型),默认情况下安装包会产生多个.cab文件,这里简单说明我们如何修改安装包中.cab文件的大小。首先,有个信息大家需要知道&…

MEF依赖注入实例
什么是MEF 先来看msdn上面的解释:MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬…

Data - 数据思维 - 上篇
1 - 概念与定义 如果分析思维是一种结构化思考的体现,那么数据分析思维(简称数据思维)则是以数据为依托的结构化分析方式。 不同于“我觉得”、“以前是怎样”、“其他人如何”这些直觉化、经验化、类比化的思考方式,数据思维是以…

新生选课系统使用指南
建议选用IE6或者IE7浏览器。 打开浏览器,地址栏输入202.200.112.200, 或者202.200.112.202, 或者202.200.112.210。按回车键。 输入学号和身份证号(如果修改过密码,则输入新密码)。点“登录”。 点 “学生…

【java】兴唐第二十三节课(暑期第一节TreeSet)
预警:进入暑期培训的博主即将高产似母猪,敬请博友期待。 1、给类添加构造方法 alt shift s 选择Generate Construct using Fields 2、map两种遍历方法 方法一: 获取所有的key值,根据key值获取value值 代码实现: Se…

程序设计分析(开篇)——混沌初开,顿悟设计
一直以来,不断的进行着项目的设计、开发,然而,差的设计,痛苦的维护、编码,让我不断的审视自己的设计是否有问题,在最近的一些思考、理解中,终于有了一些领悟,总结一下过去的设计&…

源代码管理工具调查
任务说明: 一、找出并了解当前较为流行的几种源代码管理工具(至少三种); 1、 Visual Source Safe( 简称 VSS )2、 SVN(Subversion) - CVS(Concurrent Version System)的替代和升级版本3、 ClearCase 二、建立表格对这些源代码管理…

从零开始学Go之接口(一):接口
接口是双方约定的一种合作协议。接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节。 接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式、类型及结构。 声明: 接口类型是由一组方法签名定义的集合 …

【java】兴唐第二十四节课
HashMap中put函数的源码分析: (一)知识点: 1、方法resize()的作用是扩容 2、 if ((p tab[i (n - 1) & hash]) null)其中n-1代表最后一个元素的下标,经过和hash的&后获取当前存储nod…

找不到可安装的ISAM”的问题
问题描述: 在 Access 或Sql Server中收到“Could not find installable ISAM”(找不到可安装的 ISAM)错误信息或者丢失某些文件类 解决方法: 1.注册表编辑器使用不当可能导致严重问题,可能需要重新安装操作系统。Micro…

修改mysql的时间/时区
# 背景 往db中insert数据发现时间不对,因为是新DB,所以猜测是mysql设置不对 # 解决方法 方法一:通过mysql命令行模式下动态修改 show variables like "%time_zone%"; 查看时区 --------------------------| Variable_name | Value…

【java】兴唐第二十五节课(异常和log4j的使用)
异常 1、try catch finally语法(附带多重catch) 代码实现: public static void main(String[] args) {try {int i 1/0;}catch(ArithmeticException e){System.out.println("出现数学运算异常:" e);}catch(ArrayIndex…

CentOS 命令提示符颜色及样式详解
命令提示符:prompt CentOS下查看当前命令提示符格式: 1 [rootlocalhost ~]# echo $PS1 #显示当前使用的PS1样式 2 [\u\h \W]\$ 命令提示符参数如下: \d :#代表日期,格式为weekday month date,例如&#…

Max_user_connections 与Max_connections 与max_connect_errors
对于连接数的设置,show variables里有三个参数可以对它进行控制,max_connections与max_user_connections以及max_connect_errors。下面对这三个参数相关描述。 max_connections:针对所有的账号所有的客户端并行连接到MYSQL服务的最大并行连接…

压力变动力,存储追求高效率
企业的数据存储量每年都要大幅增长,但是IT预算呈现紧缩趋势。这就是企业面临的最大存储难题,即如何平衡数据增长与提高存储利用率和降低成本之间的关系。 非结构化数据带来的难题 存储最直接的压力来自于不断增长的数据量。今天,我们面对的是…

Hadoop学习之路(三)Hadoop-2.7.5在CentOS-6.7上的编译
下载Hadoop源码 1、登录官网 2、确定你要安装的软件的版本 一个选取原则: 不新不旧的稳定版本 几个标准: 1)一般来说,刚刚发布的大版本都是有很多问题 2)应该选择某个大版本中的最后一个小版本 阅读编译文档 1、准备一…