ARM启动代码分析-philips的LPC2xxx系列<br />/**********************************************************************************************<br />*File: startup.s<br />*Author: Embest w.h.xie 2005.02.21<br />*Desc: lpc22xxlpc212xlpc211xlpc210x startup code<br />*History: <br />* note modify: cui jian jie 2006-4-25<br />*comment:<br />**********************************************************************************************/<br /># 处理器的七种工作方式的常量定义<br />.EQU Mode_USR, 0x10 #用户模式<br />.EQU Mode_FIQ, 0x11 #FIQ模式<br />.EQU Mode_IRQ, 0x12 #IRQ模式<br />.EQU Mode_SVC, 0x13 #超级用户模式<br />.EQU Mode_ABT, 0x17 #终止模式<br />.EQU Mode_UND, 0x1B #未定义模式<br />.EQU Mode_SYS, 0x1F #系统模式<br /><br /># 中断屏蔽位<br />.EQU I_Bit, 0x80 //IRQ中断控制位,当被置位时,IRQ中断被禁止<br />.EQU F_Bit, 0x40 //FIQ中断控制位,当被置位时,FIQ中断被禁止<br /><br /># 状态屏蔽位<br />.EQU T_bit, 0x20 //T位,置位时在Thumb模式下运行,清零时在ARM下运行<br /><br /># 定义程序入口点<br />.globl _start<br /> .code 32<br /><br /> .TEXT <br /><br /><br />_start:<br /><br /># 中断向量表<br /><br />Vectors:<br /> LDR PC, Reset_Addr //把Reset_Addr地址处的内容放入PC中<br /> LDR PC, Undef_Addr<br /> LDR PC, SWI_Addr<br /> LDR PC, PAbt_Addr<br /> LDR PC, DAbt_Addr<br /> .long 0xb9205f80 @ keep interrupt vectors sum is 0<br /> LDR PC, [PC, #-0xff0] //当前PC值减去0xFF0等于IRQ中断入口地址<br /> LDR PC, FIQ_Addr<br />#地址表<br />Reset_Addr: #该地址标号存放Reset_Handler程序段的入口地址<br /> .long Reset_Handler<br />Undef_Addr: #该地址标号存放Undef_Handler程序段的入口地址<br /> .long Undef_Handler<br />SWI_Addr: #该地址标号存放SWI_Handler程序段的入口地址<br /> .long SWI_Handler<br />PAbt_Addr: #该地址标号存放PAbt_Handler程序段的入口地址<br /> .long PAbt_Handler<br />DAbt_Addr:<br /> .long DAbt_Handler<br /> .long 0<br />IRQ_Addr: #地址标号处存放一个无效的数据<br /> .long 0<br />FIQ_Addr: #该地址标号存放FIQ_Handler程序段的入口地址<br /> .long FIQ_Handler<br /><br /><br />Undef_Handler:<br /> B Undef_Handler<br />PAbt_Handler:<br /> B PAbt_Handler<br />DAbt_Handler:<br />B DAbt_Handler<br /><br /><br /><br /> <br /><br /><br /><br />#软中断的中断服务子程序入口地址<br />SWI_Handler: <br /> STMFD sp!, {r0-r3, r12, lr} //入栈,现场数据保护<br /> MOV r1, sp //把堆栈指针SP存入R1中<br /> MRS r0, spsr //把SPSR值存入R0,SPSR值为产生软中断时的CPSR<br /> TST r0, #T_bit //判断R0(SPSR)的T位是否为0<br /> #SPSR的T位不为0,工作在Thumb模式下<br /> LDRNEH r0, [lr,#-2] //SPSR的T位不为0,则[lr-2]-〉r0<br /> BICNE r0, r0, #0xFF00 // SPSR的T位不为0,清除r0的Bit8~Bit15位<br /> # SPSR的T位为0,工作在ARM模式下<br /> LDREQ r0, [lr,#-4] // SPSR的T位为0,则[lr-4] -〉r0<br /> BICEQ r0, r0, #0xFF000000 // SPSR的T位为0,清除r0的Bit24~Bit131位<br /><br /> # R0 is interrupt number //R0是中断号<br /> # R1 is stack point //R1是堆栈指针<br /><br /> BL SWI_Exception //进入软中断处理程序<br /> LDMFD sp!, {r0-r3, r12, pc}^ //出栈,现场数据恢复<br /><br /><br /># 快速响应中断的中断服务自程序的入口地址<br />FIQ_Handler: <br /> STMFD SP!, {R0-R3, LR} //入栈的现场保护<br /># BL FIQ_Exception //进入FIQ的中断处理程序<br /> LDMFD SP!, {R0-R3, LR} //出栈,恢复现场<br /> SUBS PC, LR, #4 //返回到主程序<br /><br /># 复位后程序处理的入口地址<br />Reset_Handler:<br /> BL RemapSRAM //进行存储器映射的操作<br />#下面几行代码用来判断当前的工作模式<br /> MRS R0, CPSR //读CPSR到寄存器R0<br /> AND R0, R0, #0x1F //R0 = R0 AND 0x1F<br /> CMP R0, #Mode_USR //比较R0 和 #Mode_USR,二者相减<br />//如果相等则说明当前处在用户模式下,需要通过产生11号软中断进入系统模式。因为下面的初始化堆栈<br />//需要在不同的工作模式下切换,而在用户模式下不能直接切换,只有系统模式可以,所以要通过产生11<br />//号软中断切换到用户模式。<br /> SWIEQ #11 <br /><br /> BL InitStack //进行堆栈初始化工作<br /><br /><br /><br /><br />ARM启动代码分析-philips的LPC2xxx系列32006-7-24 14:33:00<br />#------------------------------------------------------------------------------<br />#- 初始化C变量<br />#------------------------<br />#- 下表由连接器自动产生<br />#- RO: 只读=代码区。<br />#- RW: 可读可写=预先初始化的数据(初始化的全局变量)和预先被清零的数据(未初始化的全局变量)。.<br />#- ZI: 预先被清零的数据区(未初始化的全局变量)<br />#- 预先被初始化的数据区定位在代码区之后。<br />#- 预先被清零的数据区定位在预先被初始化的数据区之后。<br />#- 注意数据区的位置 : <br />#- I如果用 ARM SDT, 当链接器选择no -rw-base时, 数据区被映射在代码区之后<br />#- 你可以把数据区房子内部的SRAM( -rw-base=0x40 or 0x34)中<br />#- 或者放在外部的SRAM( -rw-base=0x2000000 )中。<br />#- 注意:为了提高代码的密度,预先被初始化的数据必须尽可能的少。<br />#------------------------------------------------------------------------------<br />#该部分程序功能:先判断当前是在RAM中运行还是在FLASH中运行,如果在FLASH中运行,先把FLASH<br />#中的预先赋值的RW段数据和未赋值的ZI段数据都搬移到RAM区中,再把ZI段数据全部清零;如果程#序就是在RAM中运行,则直接把ZI段数据清零。<br /> .extern Image_RO_Limit /* ROM区中数据段的起始地址*/<br /> .extern Image_RW_Base /* RW段起始地址 */ <br /> .extern Image_ZI_Base /* ZI段的起始地址*/ <br /> .extern Image_ZI_Limit /* ZI段的结束地址加1 */ <br /><br /><br /> ldr r0, =Image_RO_Limit /* 取ROM区中数据段的首地址 */<br /> ldr r1, =Image_RW_Base /* 取RAM区中RW段的目标首地址*/<br /> ldr r3, =Image_ZI_Base /*取RAM区中ZI段的首地址 */<br /> cmp r0, r1 /* 比较ROM区中数据段首地址和RAM区中RW段目标首地址 */<br /> beq NoRW /*相等代表当前是在RAM中运行*/<br />LoopRw: cmp r1, r3 /*不相等则和RAM区中ZI段的目标地址比较*/<br /> ldrcc r2, [r0], #4 /*如果r1<r3,则把r0地址上的数据读出到r2中,然后r0=r0+4*/<br /> strcc r2, [r1], #4 /*如果r1<r3,则把r2内数据写入道r1地址中,然后r1=r1+4*/<br /> bcc LoopRw /*如果r1<r3,则跳转到LoopRw 继续执行*/<br />NoRW: ldr r1, =Image_ZI_Limit /* 取ZI段的结束地址 */<br /> mov r2, #0 /*将r2赋0*/<br />LoopZI: cmp r3, r1 /* 将ZI段清零*/<br /> strcc r2, [r3], #4 /*如果r3<r1,将r2内容写入到r3地址单元中,然后r3=r3+1*/<br /> bcc LoopZI /*如果r3<r1(即C=0),则跳转到LoopZI */<br /><br /> .extern Main /*声明外部变量*/<br /> B Main /*t跳转到用户的主程序入口*/<br /><br /><br /><br /><br /><br /><br /># 为每一种模式建立堆栈,ARM堆栈指针向下生长<br />InitStack:<br /> MOV R1, LR //把该子程序返回地址保留在R1中<br /><br /> LDR R0, =Top_Stack //取栈定地址到R0中<br />#进入未定义模式,并禁止FIQ中断和IRQ中断<br /> MSR CPSR_c, #Mode_UND|I_Bit|F_Bit<br />#设置未定义模式下堆栈的栈顶指针<br /> MOV SP, R0 <br /> SUB R0, R0, #UND_Stack_Size #未定义模式下堆栈深度<br /><br />#进入终止模式,并禁止禁止FIQ中断和IRQ中断<br /> MSR CPSR_c, #Mode_ABT|I_Bit|F_Bit<br />#紧接着未定义模式下的堆栈,设置终止模式下栈顶指针<br /> MOV SP, R0 <br /> SUB R0, R0, #ABT_Stack_Size #终止模式下堆栈深度<br /><br />#进入FIQ模式,并禁止FIQ中断和IRQ中断<br /> MSR CPSR_c, #Mode_FIQ|I_Bit|F_Bit<br />#紧接着终止模式下的堆栈,设置下FIQ模式下栈顶指针<br /> MOV SP, R0<br /> SUB R0, R0, #FIQ_Stack_Size #FIQ模式下的堆栈深度<br /><br />#进入IRQ模式,并禁止FIQ中断和IRQ中断<br /> MSR CPSR_c, #Mode_IRQ|I_Bit|F_Bit<br />#紧接着FIQ模式下的堆栈,设置IRQ模式下的栈顶指针<br /> MOV SP, R0<br /> SUB R0, R0, #IRQ_Stack_Size #IRQ模式下的堆栈深度<br /><br />#进入超级用户模式,并禁止FIQ中断和IRQ中断<br /> MSR CPSR_c, #Mode_SVC|I_Bit|F_Bit<br />#紧接着IRQ模式下的堆栈,设置超级用户下的栈顶指针<br /> MOV SP, R0<br /> SUB R0, R0, #SVC_Stack_Size #超级用户下的堆栈深度<br /><br />#设置进入用户模式<br /> MSR CPSR_c, #Mode_USR<br />#紧接着超级用户模式下的堆栈,设置用户模式下的栈顶指针,剩余的空间都开辟为堆栈<br /> MOV SP, R0<br /><br /> MOV PC, R1 #堆栈初始化子程序返回<br /><br /><br /><br /><br /><br /><br /># 重映射SRAM区<br />RemapSRAM:<br /><br /><br /> MOV R0, #0x40000000 //RAM区首地址<br /> LDR R1, =Vectors //向量表首地址<br />#下面一段程序是把从0x00000000开始的64个字节(FLASH中的中断向量表和地址表)搬移到以<br />#0x40000000为首地址的RAM区中<br /> LDMIA R1!, {R2-R9} //把以[R1]为首地址的32个字节数据装载到R2-R9中<br /> STMIA R0!, {R2-R9} //把R2-R9中的数据存入以[R0]为首地址的单元中<br /> LDMIA R1!, {R2-R9} //把以[R1]为首地址的32个字节数据装载到R2-R9中<br /> STMIA R0!, {R2-R9} ////把R2-R9中的数据存入以[R0]为首地址的单元中<br />#下面几行代码设置存储器映射控制寄存器<br /> LDR R0, =MEMMAP //取MEMMAP地址到R0<br /> MOV R1, #0x02 <br /> STR R1, [R0] //给MEMMAP赋值为0x02,设置中断向量从RAM区从新映射<br /><br /><br /> mov pc, lr //跳转到主程序<br /><br /><br />#下面一段程序代码是进入软中断来切换系统的工作模式,当希望从一种模式切换入另一种模式时,可以通<br />#过调用下面对应标号的程序段进入软中断。在软中断处理程序中会根据所给定的中断号处理,执行SWI #num后软中断号被存入R0中。<br />.globl disable_IRQ<br />.globl restore_IRQ<br />.globl ToSys<br />.globl ToUser<br /><br /># 禁止IRQ<br /><br />disable_IRQ: <br /> STMFD SP!, {LR} //把LR值压入堆栈<br /> swi #0 //产生0号软中断, 0 -〉R0<br /> LDMFD SP!, {pc} //恢复PC值,返回<br /><br /># 恢复IRQ<br /><br />restore_IRQ:<br /> STMFD SP!, {LR} //把LR值压入堆栈<br /> swi #1 //产生1号软中断,1 –〉R0<br /> LDMFD SP!, {pc} //恢复PC值,返回<br /><br />#进入系统工作模式<br /><br />ToSys:<br /> STMFD SP!, {LR} //把LR值压入堆栈<br /> swi #11 //产生11号软中断,11 –〉R0<br /> LDMFD SP!, {pc} //恢复PC值,返回<br /><br /># 进入用户工作模式<br /><br />ToUser:<br /> STMFD SP!, {LR} //把LR值压入堆栈<br /> swi #12 //产生12号软中断,11 –〉R0<br /> LDMFD SP!, {pc} //恢复PC值,返回<br /><br /><br /><br /><br /> |
|