理解启动代码
嵌入式启动流程 汇编代码解析

嵌入式启动流程:汇编代码解析1.加载引导程序嵌入式系统在加电后,第一个执行的程序通常是引导程序(Bootloader)。
它负责从存储设备中加载并执行后续的程序。
引导程序通常在启动时进行硬件设备的自检,然后从特定的存储位置(如闪存或RAM)加载后续程序。
引导程序通常使用汇编语言编写,因为它需要在硬件级别进行操作。
它负责初始化CPU、内存、硬盘等硬件设备,并确保系统环境满足后续程序的要求。
2.初始化硬件设备在引导程序之后,接下来的任务是初始化硬件设备。
这包括初始化CPU、内存、硬盘、显示器等设备。
初始化硬件设备的过程包括设置设备的寄存器、配置设备的接口等。
在这个过程中,硬件设备被配置为适合后续程序运行的状态。
3.设置内存管理器在硬件设备初始化完成后,接下来需要设置内存管理器。
内存管理器负责管理系统的内存资源,包括内存的分配、释放和保护。
内存管理器通常由操作系统内核提供,因此在加载操作系统内核之前,需要先初始化内存管理器。
4.加载操作系统内核在内存管理器初始化完成后,可以加载操作系统内核。
操作系统内核是系统的核心部分,负责管理系统资源、调度应用程序的运行等。
操作系统内核通常被压缩并保存在存储设备中,因此需要先解压缩并加载到内存中。
然后,内核会进行自身的初始化,包括设置系统时钟、配置设备驱动等。
5.启动内核并初始化系统服务在操作系统内核加载并初始化完成后,可以启动内核并初始化系统服务。
系统服务是指为应用程序提供支持的底层服务,如文件系统、网络服务等。
启动内核后,会执行一系列的系统初始化过程,包括设置系统环境变量、加载系统服务等。
这些过程完成后,系统就可以接受应用程序的请求并为其提供服务。
6.加载文件系统和应用程序在系统服务初始化完成后,可以加载文件系统和应用程序。
文件系统是存储和管理文件数据的系统,应用程序则是为用户提供服务的程序。
文件系统通常被加载到内存中,并初始化为可用的状态。
然后,可以按需加载应用程序到内存中并执行。
电脑启动时出现错误代码该怎么办

电脑启动时出现错误代码该怎么办在我们日常使用电脑的过程中,电脑启动时出现错误代码是一件让人颇为头疼的事情。
当面对这种情况时,很多朋友可能会感到茫然无措,不知道从何处下手解决。
别担心,接下来我将为您详细介绍一些应对电脑启动错误代码的方法和步骤。
首先,我们要明白错误代码的出现通常意味着电脑在启动过程中检测到了某种问题或故障。
这些错误代码可能是由硬件故障、软件冲突、驱动程序问题、系统文件损坏等多种原因引起的。
当电脑启动时出现错误代码,第一步要做的是冷静下来,仔细观察错误代码的提示信息。
有些错误代码会直接给出比较明确的问题描述,比如“硬盘错误”、“内存故障”等,这能为我们后续的排查提供重要线索。
如果错误代码没有给出明确的问题指向,我们可以尝试通过互联网搜索该错误代码。
很多时候,其他用户可能也遇到过相同的问题,并在网上分享了他们的解决方法。
在搜索时,尽量输入完整的错误代码和相关的关键词,例如“电脑启动错误代码 0x0000007B 解决方法”,以获取更准确和有用的结果。
接下来,我们可以根据常见的错误类型进行初步排查。
硬件方面,如果错误代码提示与硬盘有关,可能是硬盘连接不良、硬盘损坏或者硬盘分区表出现问题。
我们可以检查硬盘数据线和电源线是否连接牢固,或者尝试使用硬盘检测工具检查硬盘是否存在坏道。
内存问题也可能导致启动错误。
可以尝试重新插拔内存条,清理内存条金手指上的灰尘,如果有多个内存条,可以逐个测试,以确定是否有某个内存条出现故障。
对于显卡故障,如果电脑有集成显卡和独立显卡,可以先将独立显卡拔掉,使用集成显卡启动电脑,看看是否能正常启动,以判断是否是独立显卡的问题。
在软件方面,系统文件损坏是比较常见的原因之一。
可以尝试使用系统自带的修复工具,如 Windows 系统中的“系统还原”、“自动修复”等功能。
如果这些方法不奏效,可能需要重新安装操作系统,但在这之前一定要备份重要的数据。
驱动程序不兼容或损坏也可能引发错误。
三菱 PLC 定位模块、简单运动模块中定位起动编号应用和 M 代码含义

三菱PLC定位模块、简单运动模块中定位起动编号应用和M代码含义--个人的理解(1)M代码是在0 和65535 之间可以分配给各个定位数据(Da.10 )的数字。
M代码应用含义:PLC内部定义的一个顺序开关操作过程(或理解成指令)。
因为在现场不能每一个地址位置完成就用一个接近开关,告知PLC进行下一步的运动,那会需要很多个接近开关,所以用M代码操作,那么上一个运动完成到位后,由(指令)PLC内部通知进行下一项运动,去控制顺序运动。
(2)三菱运动控制(QD75M4)的应用中,轴控制区内的缓存地址1500、1600、1700、1800(分别代表1、2、3、4#的缓存地址),其定位起动编号1-600可以用于预读启动功能。
定位数据编号1-600,在指令中的设置是和Configurator-QP有联系的,可以理解为指令块(但要区别于块启动功能)。
(3)1-600之间的数字,没有什么区别(但是在设置Configurator-QP参数时各自轴彼此的编号要错开,不要覆盖设置参数)。
在设置Configurator-QP参数时,与PLC程序中各轴赋予的编号一一对应,对应的指令块参数设置合适即可,方便M代码的调用。
(4)举个例子说明用一下定位数据编号和M代码的联系。
这个例子用M代码和1-600定位起动编号联用,最能说明问题。
以1#为例说明:①首先1#M代码ON: FROM H0 K808 D10 K1(read 1#M code,M代码ON执行命令,D10指代M代码),此时说明1#的M 代码执行有效。
②提前已经在Configurator-QP设置好Positioning data Axis #1中的定位起动编号(例如编号10)对应的参数和对应的M代码编号(例如编号1)。
③把定位数据编号10写入1#的缓存地址K1500:TO H0 K1500 K10 K1-----将Configurator-QP对应的定位数据编号10的参数写入1#。
keil下C51启动代码详解

由于CPU和程序启动代码文件STARTUP.a51的重要性,一些8051派生的CPU产品要求初始化CPU来满足设计中的相应的硬件,因此,有时候用户需要对STARTUP.a51进行修改,所以进行注释一下:;---------------------------------------------------;startup.A51: 用户上电初始化程序;----------------------------------------------------;;使用以下EQU命令可定义在CPU复位时需要用0进行初始化的内存空间;;IDA TA存储器的空间的绝对起始地址总是零IDA TALEN EQU 80H ;需用0进行初始化的IDA TA存储器空间的字节数;XDA TASTART EQU 0H ;XDA TA存储器空间的绝对起始地址XDA TALEN EQU 0H ;需用0进行初始化的XDA TA存储器的空间字节数;PDA TASTART EQU 0H ;PDA TA存储器的空间的绝对起始地址PDA TALEN EQU 0H ;需用0进行初始化的PDA TA存储器的空间字节数;注意:IDA TA存储器的空间在物理上包括了8051单片机的DA TA和BIT存储空间;至少要保证与C51编译器运行库有关的存储器的空间进行0初始化;;再入函数模拟初始化;-----------------------------------------------------------;以下用EQU指令定义了再入函数模拟堆栈指针的初始化;;使用SMALL存储器模式时再入函数的堆栈空间IBPSACK EQU 0 ;使用SMALL存储器模式再入函数时将其设置成1IBPSTACKTOP EQU 0FFH+1 ;将堆栈顶设置为最高地址加1;;使用LARGE存储器模式时再入函数的堆栈空间XBPSTACK EQU 0 ;使用LARGE存储器模式再入函数时将其设置成1XBPSTACKTOP WQU 0FFFFH+1 ;将堆栈顶设置为最高地址加1;;使用COMPACT存储器模式时再入函数的堆栈空间PBPSTACK EQU 0 ; 使用COMPACT存储器模式再入函数时将其设置成1PBPSTACKTOP WQU 0FFFFH+1 ;将堆栈顶设置为最高地址加1;;----------------------------------------------------;使用COMPACT存储器模式时,64KB X DA TA存储器空间的分页定义;;以下用EQU指令定义PDA TA类型变量在XDA TA存储器空间的页地址;使用EQU指令定义PFAGE时必须与L51连接定位器PDA TA指令的控制参数一致;PPAGEENABLE EQU 0 ;使用PDA TA类型变量时将其设置成1PPAGE EQU 0 ;定义页号;;------------------------------------------------NAME ? C_STARTUP ;模块名为? C_STARTUP? C_51STARTUP SEGMENT CODE ;代码段? STACK SEGMENT IDA TA;堆栈段RSEG ? STACK ;堆栈DS 1EXTRN COE(? C_START) ;程序开始地址PUBLIC ? C_STARTUPCSEG A T 0x8000 ;定义用户程序的起始地址,用MON51仿真器时可能有用? C_STARTUP: LFMP STARTUP1RSEG ? C_51STARTUPSTARTUP1:;;初始化串口MOV SCOM, #40HMOV TMOD, #20HMOV TH1, #0FDHSETB TR1CLR T1;单片机上电IDA TA内存清零,如果不需要上电清零IDA TA,可以注销IF到IFEDN之间的;语句,或者修改IDTALEN的长度,为了让CPU具有掉电保护功能,需要确定IDTALEN的长度IF IDA TALEN <> 0MOV R0, # IDA TALEN-1CLR AIDA TALOOP: MOV @R0,ADJNZ R0,IDA TALOOPENDIF;;单片机上电XDA TA内存清零,如果不需要上电清零XDA TA,可以注销IF到IFEDN之间的;语句,或者修改XDTALEN的长度IF XDA TALEN <> 0MOV DPTR, #XDA TASTARTMOV R7,#LOW (XDA TALEN)IF (LOW(XDA TALEN)) <> 0MOV R6, #(HIGH(XDA TALEN))+1ELSEMOV R6, #HIGH (XDA TALEN)ENDIFCLR AXDA TALOOP: MOVX @DPTR, AINC DPTRDJNZ R7, XDA TALOOPDJNZ R6, XDA TALOOPEND IF;;送PDA TA存储器页面高位地址IF PPAGEENABLE <> 0MOV P2, #PPAGEENDIF;;单片机上电PDA TA内存清零,如果不需要上电清零XDA TA,可注销IF到IFEDN之;间的语句或者修改PDA TALEN的长度IF PDA TALEN <> 0MOV R0, #PDA TASTARTMOV R7, #LOW (PDA TALEN)CLR APDA TALOOP: MOV @R0, AINC R0DJNZ R7,PDA TALOOPENDIF;;设置使用SMALL存储器模式时再入函数的堆栈空间IF IBPSTACK <> 0EXTRN DA TA(? C_IBP)MOV ? C_IBP, #LOW IBPSTACKTOPENDIF;;设置使用LARGE存储器模式时再入函数的堆栈空间IF XBPSTACK <> 0EXTRN DA TA (? C_XBP)MOV ? C_XBP, #HIGH XBPSTACKTOPMOV ? C_XBP +1, #LOW XBPSTACKTOPENDIF;;设置使用COMPACT存储器模式时再入函数的堆栈空间IF PBPSTACK <> 0EXTRN DA TA(? C_PBP)MOV ? C_PBP, #LOW PBPSTACKTOPEND IF;;设置堆栈的起始地址MOV SP, #? STACK-1 ;例如MOV SP, #4FH;;如果程序超过64K,则使用程序分组技术,启动下面的程序;EXTRN CODE(? B_SWITCH0);CALL ? B_SWITCH0;程序从第一组bank 0 块开始执行;跳转到用户程序MAIN函数LJMP ? C_STARTEND。
UBOOT源码分析

UBOOT源码分析UBOOT是一种开放源码的引导加载程序。
作为嵌入式系统启动的第一阶段,它负责初始化硬件设备、设置系统环境变量、加载内核镜像以及跳转到内核开始执行。
Uboot的源码是开放的,让我们可以深入了解其内部工作机制和自定义一些功能。
Uboot源码的文件组织结构非常清晰,主要分为三个大类:目录、文件和配置。
其中目录包含了一系列相关的文件,文件存放具体的源码实现代码,配置文件包含了针对特定硬件平台的配置选项。
Uboot源码的核心部分是启动代码,位于arch目录下的CPU架构相关目录中。
不同的CPU架构拥有不同的启动代码实现,如arm、x86等。
这些启动代码主要包括以下几个关键功能:1. 初始化硬件设备:Uboot首先需要初始化硬件设备,例如设置时钟、中断控制器、串口等设备。
这些初始化操作是在启动代码中完成的。
通过查看该部分代码,我们可以了解硬件的初始化过程,以及如何配置相关寄存器。
2. 设置启动参数:Uboot启动参数存储在一个称为"bd_info"的数据结构中,它包含了一些关键的设备和内存信息,例如DRAM大小、Flash 大小等。
这些参数是在启动代码中设置的,以便内核启动时能够正确识别硬件情况。
3. 加载内核镜像:Uboot负责加载内核镜像到内存中,以便内核可以正确执行。
在启动代码中,会通过读取Flash设备或者网络等方式,将内核镜像加载到指定的内存地址处。
加载过程中,可能会进行一些校验和修正操作,以确保内核数据的完整性。
4. 启动内核:在内核镜像加载完成后,Uboot会设置一些寄存器的值,并执行一个汇编指令,跳转到内核开始执行。
此时,Uboot的使命即结束,控制权交由内核处理。
除了启动代码,Uboot源码中还包含了许多其他功能模块,如命令行解析器、存储设备驱动、网络协议栈等。
这些功能模块可以根据需求进行配置和编译,以满足不同平台的需求。
例如,可以通过配置文件选择启用一些功能模块,或者自定义一些新的功能。
电脑启动时出现错误代码怎么处理

电脑启动时出现错误代码怎么处理在使用电脑的过程中,我们可能会遇到电脑启动时出现错误代码的情况。
这无疑会让人感到十分困扰,尤其是当我们急需使用电脑工作或娱乐时。
那么,当遇到这种情况,我们应该如何处理呢?首先,我们需要了解错误代码的含义。
错误代码通常是由一系列数字、字母或符号组成,它们是电脑系统向我们传递故障信息的一种方式。
不同的错误代码代表着不同的问题。
常见的错误代码类型包括硬件相关的错误代码和软件相关的错误代码。
硬件相关的错误代码,比如“0x0000007B”,可能表示硬盘驱动器出现问题,如硬盘故障、硬盘数据线连接不良等。
软件相关的错误代码,如“0x80070005”,可能与系统更新失败、权限问题或驱动程序冲突有关。
当电脑启动时出现错误代码,第一步要做的是冷静下来,不要惊慌失措。
然后,仔细记录下错误代码的具体内容。
这对于后续的故障排查非常重要。
接下来,我们可以通过以下几种常见的方法来尝试解决问题。
第一种方法是重启电脑。
有时候,电脑可能只是出现了暂时的故障,简单的重启就能解决问题。
在重启电脑时,我们可以观察电脑的启动过程,看错误代码是否再次出现,以及是否有其他异常情况。
如果重启后错误代码仍然存在,我们可以尝试进入安全模式。
进入安全模式的方法因操作系统而异。
在 Windows 系统中,通常在开机时按 F8 键(有些电脑可能需要按其他键,如 F2 或 Del 键),然后选择“安全模式”。
安全模式只会加载最基本的系统驱动和服务,可以帮助我们排除一些第三方软件或驱动程序引起的问题。
在安全模式下,我们可以尝试进行系统还原,将系统恢复到之前正常的状态。
如果错误代码与硬件相关,我们可以检查硬件设备的连接情况。
比如,检查硬盘数据线、内存条是否插好,显卡是否松动等。
对于不太熟悉电脑硬件的用户,建议在操作前先了解相关的知识,或者寻求专业人士的帮助。
更新驱动程序也是解决问题的一个重要方法。
我们可以通过设备管理器查看哪些设备的驱动程序需要更新。
电脑系统常见错误代码解读与修复
电脑系统常见错误代码解读与修复随着计算机技术的飞速发展,电脑系统已经成为我们日常生活中不可或缺的一部分。
然而,在使用电脑的过程中,我们不可避免地会遇到各种错误代码,给我们的使用体验带来困扰。
本文将针对电脑系统常见错误代码进行解读,并提供相应的修复方法,帮助读者解决电脑故障问题。
错误代码1:蓝屏错误代码0x0000007E这是Windows系统中常见的错误代码之一,通常表示系统内部发生了严重错误。
出现这个错误代码的原因可能是因为硬件或驱动程序的问题,例如过时的或不兼容的驱动程序。
要解决这个问题,我们可以采取以下步骤:第一步,检查并更新驱动程序。
我们可以通过访问计算机制造商的网站查找并下载最新的驱动程序。
安装这些驱动程序可以解决与硬件兼容性相关的问题。
第二步,使用系统还原。
我们可以尝试将操作系统还原到之前的一个已知正常工作状态,消除系统中可能出现的错误。
第三步,进行磁盘检查。
使用Windows自带的磁盘检查工具,检查并修复可能存在的硬盘错误。
错误代码2:启动错误代码0xc0000225这个错误代码一般出现在Windows系统的启动过程中。
它通常表示引导文件损坏或丢失,使系统无法正常启动。
要解决这个问题,可以尝试以下方法:第一步,重新启动系统。
有时候,系统的启动错误可能是临时的,重新启动计算机可以解决此问题。
第二步,修复启动文件。
使用Windows安装光盘或USB启动盘,进入修复模式,并运行启动文件修复工具,以修复可能损坏或丢失的文件。
第三步,修复启动选项。
使用命令提示符工具,输入命令“bootrec /fixmbr”和“bootrec /fixboot”,重新配置启动选项。
错误代码3:驱动错误代码Code 39这个错误代码通常在设备管理器中显示,表示驱动程序出现问题,无法正常加载设备。
要解决此问题,可以尝试以下方法:第一步,卸载驱动程序。
在设备管理器中找到出现问题的设备,右键点击并选择“卸载设备”。
AMI BIOS启动代码中英文对照表
3C
Mid POST initialization of chipset registers.
在上电自检期间初始化芯片寄存器
40
Detect different devices (Parallel ports, serial ports, and coprocessor in CPU, … etc.) successfully installed in the system and update the BDA,EBDA…etc.
2C
Initializes different devices. Detects and initializes the video adapter installed in the system that have optional ROMs.
继续初始化设备,检测并初始化所安装的显示适配器(内建基本调用程序).
进入BIOS设置程序如果需要或者用户选择,检查启动密码.
7C
Generate and write contents of ESCD in NVRam.
产生并重写ESCD(位于NVRam中).
8C
Late POST initialization of chipset registers.
为上电自检末期初始化芯片寄存器.
重新安排所有CPU要用的系统管理中断向量.
24
Uncompress and initialize any platform specific BIOS modules. GPNV is initialized at this checkpoint.
51单片机启动代码
; Version 8.01
;
; *** <<< Use Configuration Wizard in Context Menu >>> ***
;------------------------------------------------------------------------------
表1
Name
Description
IDATALEN
Specifies the number of bytes of idata to clear to 0. The default is 80h because most 8051 derivatives contain at least 128 bytes of internal data memory. Use a value of 100h for the 8052 and other derivatives that have 256 bytes of internal data memory.
;IDATALEN:IDATA存储区的大小<0-256>,可以根据自己的选择修改
; <i> Note: The absolute start-address of IDATA memory is always 0
; <i> The IDATA space overlaps physically the DATA and BIT areas.
XBPSTACK
Specifies whether or not the large model reentrant stack pointer (?C_XBP) should be initialized. A value of 1 causes this pointer to be initialized. A value of 0 prevents initialization of this pointer. The default is 0.
STM32启动代码分析、简化、实战
本文通过对STM32的官方固件库STM32F10x_StdPeriph_Lib_V3.5.0里的MDK启动文件分析,简化部分不需要的代码,并从繁杂的固件库里,精炼出一个类似于“hello world”的入门实战小程序——点亮一个LED。
该工程仅仅包含一个启动文件和一个有main函数的C文件。
本文初衷:不用固件库建立自己的工程!实验软件:Keil uVision4实验硬件:神舟IV号开发板芯片型号:STM32F107VCSTM32启动代码分析、简化、实战汇编基础:1.伪指令:EQU语法格式:名称EQU表达式{,类型}EQU伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言的#define。
其中EQU可以用“*”代替。
名称为EQU伪指令定义的字符名称,当表达式为32位的常量时,可以指定表达式的数据类型,可以有一下三种类型:CODE16、CODE32和DA TA2.伪指令:AREA语法格式:AREA段名{,属性1}{,属性2}……AREA命令指示汇编程序汇编一个新的代码段或数据段。
段是独立的、指定的、不可见的代码或数据块,它们由链接程序处理。
段名:可以为段选择任何段名。
但是,以一个数字开始的名称必须包含在竖杠号内,否则会产生一个缺失段名错误。
例如,|1_DataArea|。
有些名称是习惯性的名称。
例如:|.text|用于表示由C编译程序产生的代码段,或用于以某种方式与C库关联的代码段。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。
常用的属性如下:——CODE属性:用于定义代码段,默认为READONLY。
——DA TA属性:用于定义数据段,默认为READWRITE。
——READONLY属性:指定本段为只读,代码段默认为READONLY。
——READWRITE属性:指定本段为可读可写,数据段的默认属性为READWRITE。
——ALIGN属性:使用方式为ALIGN表达式。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
理解启动代码(ADS)所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初始化处理器模式,设置堆栈,初始化变量等等.由于以上的操作均与处理器体系结构和系统配置密切相关,所以一般由汇编来编写.具体到S64,启动代码分成两部分,一是与ARM7TDMI内核相关的部分,包括处理器各异常向量的配置,各处理器模式的堆栈设置,如有必要,复制向量到RAM,以便remap之后处理器正确处理异常,初始化数据(包括RW与ZI),最后跳转到Main.二是与处理器外部设备相关的部分,这和厂商的联系比较大.虽然都采用了ARM7TDMI的内核,但是不同的厂家整合了不同的片上外设,需要不同的初始化,其中比较重要的是初始化WDT,初始化各子系统时钟,有必要的话,进行remap.这一部分与一般控制器的初始化类似,因此,本文不作重点描述.在进行分析之前,请确认如下相关概念:S64片上FLASH起始于0x100000,共64kB,片上RAM起始于0x200000,共16kB.S64复位之后,程序会从0开始执行,此时FLASH被映射到0地址,因此,S64可以取得指令并执行.显然,此时还是驻留在0x100000地址.如果使用remap命令,将会把RAM映射到0地址,同样的这时0地址的内容也只是RAM的镜像.S64的FLASH可以保证在最差情况时以30MHz进行单周期访问,而RAM可以保证在最大速度时的单周期访问.OK,以下开始分析启动代码.一,处理器异常S64将异常向量至于0地址开始的几个直接,这些是必需要处理的.由于复位向量位于0,也需要一条跳转指令.具体代码如下:RESETB SYSINIT ; ResetB UDFHANDLER ; UNDEFINEDB SWIHANDLER ; SWIB PABTHANDLER ; PREFETCH ABORTB DABTHANDLER ; DATA ABORTB . ; RESERVEDB VECTORED_IRQ_HANDLERB . ; ADD FIQ CODE HEREUDFHANDLERB .SWIHANDLERB .PABTHANDLERB .DABTHANDLERB .请注意,B指令经汇编后会替换为当前PC值加上一个修正值(+/-),所以这条指令是代码位置无关的,也就是不管这条指令是在0地址还是在0x100000执行,都能跳转到指定的位置,而LDR PC,=???将向PC直接装载一个标号的值,请注意,标号在编译过后将被替换为一个与RO相对应的值,也就是说,这样的指令无论在哪里执行,都只会跳转到一个指定的位置.下面举一个具体的例子来说明两者的区别:假定有如下程序:RESETB INIT 或者LDR PC,=INIT…INIT…其中RESET为起始时的代码,也就是这条代码的偏移为0,设INIT的偏移量为offset.如果将这段程序按照RO=0x1000000编译, 那么B INIT可理解为ADD PC, PC, #offset,而LDR PC,=INIT可被理解为MOV PC,#(RO+offset) .显然当系统复位时,程序从0开始运行,而0地址有FLASH的副本,执行B INIT将把PC指向位于0地址处的镜像代码位置,也即INIT;如果执行LDR PC,=INIT将会将PC直接指向位于FLASH中的原始代码.因此以上两者都能正确运行.下面将RO设置为0x200000,编译后生成代码,还是得烧写到FLASH中,也就是还是0x100000,系统复位后从0地址执行,还是FLASH的副本,此时执行B INIT,将跳到副本中的INIT位置执行,此处有对应的代码;但是如果执行LDR PC,=INIT,将向PC加载0x200000+offset,这将使得PC跳到RAM中,而此时由于代码没有复制,RAM 中的指定位置并没有代码,程序无法运行.二,处理器模式ARM的处理器可工作于多种模式,不同模式有不同的堆栈,以下设置各模式及其堆栈.预定义一些参数:MODUSR EQU 0x10MODSYS EQU 0x1FMODSVC EQU 0x13MODABT EQU 0x17MODUDF EQU 0x1BMODIRQ EQU 0x12MODFIQ EQU 0x11IRQBIT EQU 0x80FIQBIT EQU 0x40RAMEND EQU 0x00204000 ; S64 : 16KB RAMVECTSIZE EQU 0x100 ;UsrStkSz EQU 8 ; size of USR stack SysStkSz EQU 128 ; size of SYS stack SvcStkSz EQU 8 ; size of SVC stack UdfStkSz EQU 8 ; size of UDF stack AbtStkSz EQU 8 ; size of ABT stack IrqStkSz EQU 128 ; size of IRQ stack FiqStkSz EQU 16 ; size of FIQ stack修改这些值即可修改相应模式堆栈的尺寸.以下为各模式代码:SYSINIT;MRS R0,CPSRBIC R0,R0,#0x1FMOV R2,#RAMENDORR R1,R0,#(MODSVC :OR: IRQBIT :OR: FIQBIT)MSR cpsr_cxsf,R1 ; ENTER SVC MODEMOV sp,R2SUB R2,R2,#SvcStkSzORR R1,R0,#(MODFIQ :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER FIQ MODEMOV sp,R2SUB R2,R2,#FiqStkSzORR R1,R0,#(MODIRQ :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER IRQ MODEMOV sp,R2SUB R2,R2,#IrqStkSzORR R1,R0,#(MODUDF :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER UDF MODEMOV sp,R2SUB R2,R2,#UdfStkSzORR R1,R0,#(MODABT :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER ABT MODEMOV sp,R2SUB R2,R2,#AbtStkSz;ORR R1,R0,#(MODUSR :OR: IRQBIT :OR: FIQBIT);MSR CPSR_cxsf,R1 ; ENTER USR MODE;MOV sp,R2;SUB R2,R2,#UsrStkSzORR R1,R0,#(MODSYS :OR: IRQBIT :OR: FIQBIT)MSR CPSR_cxsf,R1 ; ENTER SYS MODEMOV sp,R2 ;三,初始化变量编译完成之后,连接器会生成三个基本的段,分别是RO,RW,ZI,并会在image中顺序摆放.显然,RW,ZI在运行开始时并不位于指定的RW位置,因此必须初始化LDR R0,=|Image$$RO$$Limit|LDR R1,=|Image$$RW$$Base|LDR R2,=|Image$$ZI$$Base|1CMP R1,R2LDRLO R3,[R0],#4STRLO R3,[R1],#4BLO %B1MOV R3,#0LDR R1,=|Image$$ZI$$Limit|2CMP R2,R1STRLO R3,[R2],#4BLO %B2四,复制异常向量由于代码于RAM运行时,有明显的速度优势,而且变量可以动态配置,因此可以通过remap将RAM映射到0,使得出现异常时ARM从RAM中取得向量.IMPORT |Image$$RO$$Base|IMPORT |Image$$RO$$Limit|IMPORT |Image$$RW$$Base|IMPORT |Image$$RW$$Limit|IMPORT |Image$$ZI$$Base|IMPORT |Image$$ZI$$Limit|COPY_VECT_TO_RAMLDR R0,=|Image$$RO$$Base|LDR R1,=SYSINITLDR R2,=0x200000 ; RAM STARTCMP R0,R1LDRLO R3,[R0],#4STRLO R3,[R2],#4BLO %B0这段程序将SYSINIT之前的代码,也就是异常处理函数,全部复制到RAM中, 这就意味着不能将RW设置为0x200000,这样会使得向量被冲掉.四,在RAM中运行如果有必要,且代码足够小,可以将代码置于RAM中运行,由于RAM中本身没有代码,就需要将代码复制到RAM中:COPY_BEGINLDR R0,=0x200000LDR R1,=RESET ; =|Image$$RO$$Base|CMP R1,R0 ;BLO COPY_END ;ADR R0,RESETADR R2,COPY_ENDSUB R0,R2,R0ADD R1,R1,R0LDR R3,=|Image$$RO$$Limit|3CMP R1,R3LDRLO R4,[R2],#4STRLO R4,[R1],#4BLO %B3LDR PC,=COPY_ENDCOPY_END程序首先取得RESET的连接地址,判断程序是否时是在RAM中运行,方法是与RAM起始地址比较,如果小于,那么就跳过代码复制.在复制代码的时候需要注意,在这段程序结束之前的代码没有必要复制,因为这些代码都已经执行过了,所以,先取得COPY_END,作为复制起始地址,然后计算其相对RESET的偏移,然后以RO的值加上这个偏移,就是复制目的地的起始地址,然后开始复制.五,开始主程序以上步骤完成,就可以跳转到main运行IMPORT MainLDR PC,=MainB .六,器件初始化主程序首先要进行器件的初始化,对S64而言,应该先初始化WDT,因为默认情况下,WDT是打开的,然后是各设备的时钟分配,最后应该remap。