- 论坛徽章:
- 0
|
本帖最后由 lilinly225 于 2013-10-21 18:00 编辑
目前正在Windows下学习ARM, 用的开发工具是MDK, 没有用Linux下的交叉编译链工具。现在我在学习启动代码相关的内容。
环境为:裸机,没有操作系统和bootloader。
在我看到的的一个代码中有一段关于入口的代码感觉很诡异,不知道怎么理解。- ;定义arm汇编程序段,段名叫init段,为只读段
- AREA RESET,CODE,READONLY
- ; ENTRY
- EXPORT __ENTRY ;导出__ENTRY标号
- __ENTRY
- ResetEntry
- ;1)The code, which converts to Big-endian, should be in little endian code.
- ;2)The following little endian code will be compiled in Big-Endian mode.
- ; The code byte order should be changed as the memory bus width.
- ;3)The pseudo instruction,DCD can not be used here because the linker generates error.
-
- ;判断模式改变是否定义过(ASSERT是伪指令,:DEF:lable判断lable是否定义过了)
- ASSERT :DEF:ENDIAN_CHANGE
- [ ENDIAN_CHANGE ;如果ENDIAN_CHANGE为真,则执行以下
- ASSERT :DEF:ENTRY_BUS_WIDTH ;判断是否定义了总线宽度
- [ ENTRY_BUS_WIDTH=32 ;如果存储器是32位的总线宽度
- b ChangeBigEndian ;DCD 0xea000007
- ]
- [ ENTRY_BUS_WIDTH=16 ;如果存储器是16位的总线宽度
- andeq r14,r7,r0,lsl #20 ;DCD 0x0007ea00
- ]
- [ ENTRY_BUS_WIDTH=8 ;如果是存储器是8位总线宽度
- streq r0,[r0,-r10,ror #1] ;DCD 0x070000ea
- ]
- | ;如果总线宽度没有定义的话,就直接跳转到复位中断
- b ResetHandler ;程序执行的地跳跳转指令;正常模式下第一条语句就是这条语句
- ]
- b HandlerUndef ;handler for Undefined mode
- b HandlerSWI ;handler for SWI interrupt
- b HandlerPabort ;handler for PAbort
- b HandlerDabort ;handler for DAbort
- b . ;reserved
- b HandlerIRQ ;handler for IRQ interrupt
- b HandlerFIQ ;handler for FIQ interrupt
- ;@0x20
- b EnterPWDN ; Must be @0x20. ;进入powerdown模式
复制代码 按照ARM开发者指南的说法: ENTRY 伪指令表示程序代码的最开始地方(相当于C语言中的main函数,表示程序的入口); 但是这段代码将ENTRY给注释掉
以一个__ENTRY的标识符替代了,并且将这个符号给导出了。
然后我搜索整个工程只在 mmu.c(MMU.c 文件为MMU功能的文件)发现了下面一句:- extern char __ENTRY[]; //hzh
复制代码 很显然,这里只是声明了一个外部变量。
后面这个变量名符号 __ENTRY 被用来建立MMU转换页表的时候用了一下。- //MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
- //MMU_SetMTT(0x00000000,0x07f00000,0x00000000,RW_CNB); //bank0
- //虚拟地址 虚拟地址结束 物理地址 映射区间属性
- MMU_SetMTT(0x00000000,0x03f00000, (int)__ENTRY, RW_CB); //bank0, hzh
复制代码 然后就没有在任何地方用过了。
而MMU初始化是在 C语言代码的main函数中开始的,也就是说汇编代码的起始点运行的时候,MMU不可能运行,我的理解就是这个设置虚拟地址映射的
过程不可能与汇编代码的起始位置有关。- int main(void)
- {
- rGPBDAT = 0x1e0; //关LED
- rGPBCON =0x00015400; //设置GPB5~8为输出
- rGPBUP =0x3ff; //禁止上拉
- LCD_Init();
- Brush_Background(0xffff);
- Paint_Bmp(0,0,240,320,gImage_fhand);
- MMU_Init();
- KeyScan_Test() ;
复制代码 这里还有一个信息是:
上面那个汇编代码段定义是第一个代码段定义 ,这个代码段前面有两个数据段。
我想问的的是:
是不是汇编文件的第一个代码段默认的地址就是为0x00000000,是否是链接到代码段的0x00000000???
否则为什么不需要 ENTRY 宏汇编 伪指令。
在ARM DUI 0204IC Real View 4.0版汇编器指南的7-69页中,明确的表示一个程序至少需要一个 ENTRY 入口的。
但是上面的代码没有也可以运行。
下面为整个汇编代码到 __ENTEY 之间的汇编代码。- ;=========================================
- ; NAME: 2440INIT.S
- ; DESC: C start up codes
- ; Configure memory, ISR ,stacks
- ; Initialize C-variables
- ; HISTORY:
- ; 2002.02.25:kwtark: ver 0.0
- ; 2002.03.20:purnnamu: Add some functions for testing STOP,Sleep mode
- ; 2003.03.14:DonGo: Modified for 2440.
- ;=========================================
- GET option.inc
- GET memcfg.inc
- GET 2440addr.inc
-
- BIT_SELFREFRESH EQU (1<<22)
-
- ;Pre-defined constants
- USERMODE EQU 0x10 ;用户模式
- FIQMODE EQU 0x11 ;快速中断模式
- IRQMODE EQU 0x12 ;中断模式
- SVCMODE EQU 0x13 ;
- ABORTMODE EQU 0x17 ;
- UNDEFMODE EQU 0x1b ; 未定义模式
- MODEMASK EQU 0x1f ; 模式掩码
- NOINT EQU 0xc0 ;
- ;定义GPB口的,寻址地址
- GPBCON EQU 0x56000010
- GPBDAT EQU 0x56000014
- ;各个异常模式的堆栈
- UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~
- SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~
- UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~
- AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~
- IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~
- FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~ 堆栈的顶部
-
- ;------------------------------------------------------------------------------
- UND_Stack_Size EQU 0x00000400 ;
- SVC_Stack_Size EQU 0x00000400
- ABT_Stack_Size EQU 0x00000400
- FIQ_Stack_Size EQU 0x00000400
- IRQ_Stack_Size EQU 0x00000400
- USR_Stack_Size EQU 0x00004000
- ;堆栈的总大小为 0x5400 个字节 = 21504 字节
- 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
- Heap_Size EQU 0x00000000
- AREA HEAP, NOINIT, READWRITE, ALIGN=3
- Heap_Mem SPACE Heap_Size
- ;-------------------------------------------------------------------------------
- ;这一段是统一arm的工作状态和对应的软件编译方式(16位编译环境使用tasm.exe编译)。
- ;arm处理器的工作状态分为两种:32位,arm执行字对齐的arm指令集;16位,arm执行半字
- ;对齐的Thumb指令集。不同的工作状态,编译方式也不一样。所以下面的程序就是判断
- ;arm的工作方式来确定它的编译方式。
- ;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.
- GBLL THUMBCODE ;定义THUMBCODE 这个变量GBLL 声明一个全局逻辑变量并初始化为{FALSE}
- [ {CONFIG} = 16 ;"["表示"if","|"表示"else","]"表示"endif",对于CONFIG是在ADS编译中定义的内部变量。
- THUMBCODE SETL {TRUE}
- CODE32
- |
- THUMBCODE SETL {FALSE}
- ] ;如果ARM是在16位的工作状态的话,就使全局变量THUMBCODE设置为ture。
- MACRO ;这个是宏定义的关键字
- MOV_PC_LR ;作用是子程序返回
- [ THUMBCODE ;当目标程序是Thumb时,就要使用BX跳转返回,并转换模式。
- bx lr
- |
- ; mov pc,lr ;目标程序是ARM指令集,直接把lr赋给pc就可以了。
- bx lr
- ]
- MEND ;宏定义的结束标志。
- MACRO
- MOVEQ_PC_LR ;这个是带“相等”条件的子程序返回。和上面说的类似。
- [ THUMBCODE
- bxeq lr
- |
- moveq pc,lr
- ]
- MEND
- ;//////////////////////////////////////////////////////////////////////////
- MACRO ;这个地方定义一个宏处理器,HANDLER 为宏标识符
- $HandlerLabel HANDLER $HandleLabel
- $HandlerLabel
- sub sp,sp,#4
- stmfd sp!,{r0}
- ldr r0,=$HandleLabel ;load the address of HandleXXX to r0
- ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
- str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
- ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
- MEND
-
- ;编译器使用下列段来记录各段的起始地址和结束地址
- ;|Image$RO$Base|(ADS用这种写法), |Image$ER_ROM1$RO$Base| (MDK用这种写法) ; RO 段起始地址
- ;|Image$RO$Limit|(ADS用这种写法),|Image$ER_ROM1$RO$Limit| (MDK用这种写法) ;RO 段结束地址加1
- ;|Image$RW$Base| (ADS用这种写法),|Image$RW_RAM1$RW$Base| (MDK用这种写法) ;RW 段起始地址
- ;|Image$RW$Limit| (ADS用这种写法),|Image$RW_RAM1$ZI$Limit|(MDK用这种写法) ;RW 段结束地址加1
- ;|Image$ZI$Base| (ADS用这种写法),|Image$RW_RAM1$ZI$Base| (MDK用这种写法) ; ZI 段起始地址
- ;|Image$ZI$Limit| (ADS用这种写法),|Image$RW_RAM1$ZI$Limit|(MDK用这种写法) ; ZI 段结束地址加1
- IMPORT |Image$ER_ROM1$RO$Base| ; Base of ROM code
- IMPORT |Image$ER_ROM1$RO$Limit| ; End of ROM code (=start of ROM data)
- IMPORT |Image$RW_RAM1$RW$Base| ; Base of RAM to initialise
- IMPORT |Image$RW_RAM1$ZI$Base| ; Base and limit of area
- IMPORT |Image$RW_RAM1$ZI$Limit| ; to zero initialise
- ;引入外部变量mmu的快速总线模式和同步总线模式两个变量
- IMPORT MMU_SetAsyncBusMode
- IMPORT MMU_SetFastBusMode ;hzh
- ;引入外部标号我们所熟知的main函数
- IMPORT __main ; The main entry of mon program
- PRESERVE8
-
- ;定义arm汇编程序段,段名叫init段,为只读段
- AREA RESET,CODE,READONLY
- ; ENTRY
- EXPORT __ENTRY ;导出__ENTRY标号
- __ENTRY
- ResetEntry
- ;1)The code, which converts to Big-endian, should be in little endian code.
- ;2)The following little endian code will be compiled in Big-Endian mode.
- ; The code byte order should be changed as the memory bus width.
- ;3)The pseudo instruction,DCD can not be used here because the linker generates error.
-
- ;判断模式改变是否定义过(ASSERT是伪指令,:DEF:lable判断lable是否定义过了)
- ASSERT :DEF:ENDIAN_CHANGE
- [ ENDIAN_CHANGE ;如果ENDIAN_CHANGE为真,则执行以下
- ASSERT :DEF:ENTRY_BUS_WIDTH ;判断是否定义了总线宽度
- [ ENTRY_BUS_WIDTH=32 ;如果存储器是32位的总线宽度
- b ChangeBigEndian ;DCD 0xea000007
- ]
- [ ENTRY_BUS_WIDTH=16 ;如果存储器是16位的总线宽度
- andeq r14,r7,r0,lsl #20 ;DCD 0x0007ea00
- ]
- [ ENTRY_BUS_WIDTH=8 ;如果是存储器是8位总线宽度
- streq r0,[r0,-r10,ror #1] ;DCD 0x070000ea
- ]
- | ;如果总线宽度没有定义的话,就直接跳转到复位中断
- b ResetHandler ;程序执行的地跳跳转指令;正常模式下第一条语句就是这条语句
- ]
- b HandlerUndef ;handler for Undefined mode
- b HandlerSWI ;handler for SWI interrupt
- b HandlerPabort ;handler for PAbort
- b HandlerDabort ;handler for DAbort
- b . ;reserved
- b HandlerIRQ ;handler for IRQ interrupt
- b HandlerFIQ ;handler for FIQ interrupt
- ;@0x20
- b EnterPWDN ; Must be @0x20. ;进入powerdown模式
-
复制代码 求各位大神解释一下。
另外各位大神,有没有关于MMU和堆栈初始化的相关比较系统的资料,关于裸机设置MMU和堆栈设置的系统的解释的资料, 我搜到的资料都是
一些比较零散的资料,理解起来很难。 |
|