這是Samsung的S3C44B0(ARM7TDMI)芯片的經典bootloader,轉自木瓜老C的註解,很值得參考。
http://blog.chinaunix.net/u1/58640/showart_494549.html
; *******************************************************
; * NAME : 44BINIT.S *
; * Version : 10.JAn.2003 *
; * Description: *
; * C start up codes *
; * Configure memory, Initialize ISR ,stacks *
; * Initialize C-variables *
; * Fill zeros into zero-initialized C-variables *
; * 思瑞訊電子增加註釋說明 2007-03-30
; *******************************************************
GET ../inc/option.a
GET ../inc/memcfg.a
;Interrupt Control ;聲明符號常量定義寄存器的對應地址
INTPND EQU 0x01e00004
INTMOD EQU 0x01e00008
INTMSK EQU 0x01e0000c
I_ISPR EQU 0x01e00020
I_CMST EQU 0x01e0001c
;Watchdog timer
WTCON EQU 0x01d30000
;Clock Controller
PLLCON EQU 0x01d80000
CLKCON EQU 0x01d80004
LOCKTIME EQU 0x01d8000c
;Memory Controller
REFRESH EQU 0x01c80024
;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
;check if tasm.exe is used.
GBLL THUMBCODE ;定義全局邏輯變量THUMBCODE
[ {CONFIG} = 16 ;相當於if (CONFIG==16)
THUMBCODE SETL {TRUE} ;THUMBCODE=TRUE;
CODE32 ;聲明爲32位指令集,即使用ARM指令進行編譯
| ;else
THUMBCODE SETL {FALSE} ;THUMBCODE=FALSE;
]
[ THUMBCODE ;if THUMBCODE=TRUE
CODE32 ;轉入32位編譯模式
]
;以下位宏定義,任何調用HandlerXXX HANDLER HandleXXX都將被下面的程序展開
;該宏定義的代碼用於將對應中斷服務程序ISR的入口地址裝載到PC中,可稱之爲“加載程序”
;本初始化程序定義了一個34個字空間的數據區(在文件最後),用於存放相應中斷服務程序的首地址。
;每個字空間都有一個標號,以HandleXXX命名。
;在向量中斷模式下使用“加載程序”來執行中斷服務程序。
;******************************************************************************
;**********向量(矢量)中斷和非向量(非矢量)中斷模式的概念與區別******************
;(一)www.chinasrx.com
;向量中斷模式是當CPU讀取位於0x18處的IRQ中斷指令的時候,系統自動讀取對應於該中斷源確定地址上的指令取代0x18處的指令,
;通過跳轉指令系統就直接跳轉到對應地址函數中,節省了中斷處理時間提高了中斷處理速度。
;例如 ADC 中斷的向量地址爲0xC0,則在0xC0處放如下代碼:ldr PC,=HandlerADC 當ADC中斷產生的時候系統會
;自動跳轉到HandlerADC函數中處理中斷。
;(二)www.chinasrx.com
;非向量中斷模式處理方式是一種傳統的中斷處理方法,當系統產生中斷的時候,系統將INTPND寄存器中對應標誌位置位,
;然後跳轉到位於0x18處的統一中斷函數中;
;該函數通過讀取INTPND寄存器中對應標誌位來判斷中斷源,並根據優先級關係再跳到對應中斷源的處理代碼中處理中斷。
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
;由於ADS僅支持FD(滿遞減)型堆棧
sub sp,sp,#4 ;將堆棧退一個字用於保存下面用到的R0
stmfd sp!,{r0} ;將R0壓入堆棧
ldr r0,=$HandleLabel ;將HandleLabel的地址賦給R0
ldr r0,[r0] ;將HandleLabel的地址指向的內容(實際的執行地址)賦給R0
str r0,[sp,#4] ;將對應的中斷函數首地址入棧保護
ldmfd sp!,{r0,pc} ;將中斷函數的首地址出棧,放入PC中,系統將跳轉到對應中斷處理函數
MEND
;ARM的系統軟件開發中主要包含RO,RW,ZI三個段組成。
;其中RO爲代碼段;RW爲已經初始化的全局變量;ZI是未初始化的全局變量
;(對於GNU來說,依次對.text .data .bss段)
;值得說明的是:
;Bootloader要將RW段複製到RAM中並將ZI段清零;
;編譯器要使用下列段來記錄各段的起始和結束地址。
; |Image$$RO$$Base| ; RO段起始地址
; |Image$$RO$$Limit| ; RO段結束地址+1
; |Image$$RW$$Base| ; RW段起始地址
; |Image$$RW$$Limit| ; RW段結束地址+1
; |Image$$ZI$$Base| ; ZI段起始地址
; |Image$$ZI$$Limit| ; ZI段結束地址+1
;這些標號的值是通過編譯器的設定來確定的,如編譯軟件中對RO-base和RW-Base的設定
IMPORT |Image$$RO$$Limit| ; ROM代碼的結束地址(rom數據的起始地址)
IMPORT |Image$$RW$$Base| ; RAM中需初始化的首地址
IMPORT |Image$$ZI$$Base| ; 需初始化爲0的全局變量的首地址
IMPORT |Image$$ZI$$Limit| ; 結束地址
IMPORT Main ;主程序的入口
;以下爲代碼段
AREA Init,CODE,READONLY
;說明:
;關鍵字ENTRY告訴編譯器保留這段代碼。
;從代碼看Init段就是要寫入0x00地址的原始中斷向量,
;因此把這個文件編譯生成的44binit.O和Init填入ADS-Linker-Layout頁對應項中。
;【這樣編譯器會把該段代碼編譯到0X0地址。】
;異常中斷向量表【每個表項佔4個字節】
;一旦系統運行時有中斷髮生,即使移植了操作系統,如linux。
;處理器已經把控制權交給了操作系統,一旦發生中斷,處理器還是會跳轉到從0x0開始執行
;中斷向量表中的對應中斷。
;具體中斷向量佈局參考S3C44B0X的數據手冊
;例如 ADC 的中斷向量爲 0x000000c0 下面對應表中第49項位置
;對應向量地址爲 0x0+4*(49-1)= 0x000000c0
ENTRY ;ENTRY程序入口標號需要頂格式寫,否則出錯。
b ResetHandler ;復位異常
b HandlerUndef ;未定義異常
b HandlerSWI ;軟件中斷異常
b HandlerPabort ;指令預取異常
b HandlerDabort ;數據預取異常
b . ;保留
b HandlerIRQ ;外部中斷 外設中斷都是在這裏擴展的
b HandlerFIQ ;快速中斷
;***IMPORTANT NOTE***
;If the H/W vectored interrutp mode is enabled, The above two instructions should
;be changed like below, to work-around with H/W bug of S3C44B0X interrupt controller.
; b HandlerIRQ -> subs pc,lr,#4
; b HandlerIRQ -> subs pc,lr,#4
;說明:原文註明當使用向量中斷模式時,需用subs pc,lr,#4來代替前面的語句
; 這是原來S3C44B0X硬件中的一個BUG,後期推出的S3C44B0X已經解決此BUG,但是爲了兼容
; 早期的版本,用subs pc,lr,#4替換後也可以正常工作,相當於現在的硬件中兩條語句都
; 可以兼容,後期的器件手冊中已經註明可以直接採用b HandlerIRQ的方式
;www.chinasrx.com
VECTOR_BRANCH
ldr pc,=HandlerEINT0 ;mGA H/W interrupt vector table
ldr pc,=HandlerEINT1 ;
ldr pc,=HandlerEINT2 ;
ldr pc,=HandlerEINT3 ;
ldr pc,=HandlerEINT4567 ;
ldr pc,=HandlerTICK ;mGA
b .
b .
ldr pc,=HandlerZDMA0 ;mGB
ldr pc,=HandlerZDMA1 ;
ldr pc,=HandlerBDMA0 ;
ldr pc,=HandlerBDMA1 ;
ldr pc,=HandlerWDT ;
ldr pc,=HandlerUERR01 ;mGB
b .
b .
ldr pc,=HandlerTIMER0 ;mGC
ldr pc,=HandlerTIMER1 ;
ldr pc,=HandlerTIMER2 ;
ldr pc,=HandlerTIMER3 ;
ldr pc,=HandlerTIMER4 ;
ldr pc,=HandlerTIMER5 ;mGC
b .
b .
ldr pc,=HandlerURXD0 ;mGD
ldr pc,=HandlerURXD1 ;
ldr pc,=HandlerIIC ;
ldr pc,=HandlerSIO ;
ldr pc,=HandlerUTXD0 ;
ldr pc,=HandlerUTXD1 ;mGD
b .
b .
ldr pc,=HandlerRTC ;mGKA
b . ;
b . ;
b . ;
b . ;
b . ;mGKA
b .
b .
ldr pc,=HandlerADC ;mGKB
b . ;
b . ;
b . ;
b . ;
b . ;mGKB
b .
b .
;0xe0=EnterPWDN
ldr pc,=EnterPWDN
LTORG
;下面是具體的中斷處理函數跳轉的宏,通過上面的$HandlerLabel的宏定義
;展開後跳轉到對應的中斷處理函數(ISR)處理中斷(對於向量中斷)
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
HandlerADC HANDLER HandleADC
HandlerRTC HANDLER HandleRTC
HandlerUTXD1 HANDLER HandleUTXD1
HandlerUTXD0 HANDLER HandleUTXD0
HandlerSIO HANDLER HandleSIO
HandlerIIC HANDLER HandleIIC
HandlerURXD1 HANDLER HandleURXD1
HandlerURXD0 HANDLER HandleURXD0
HandlerTIMER5 HANDLER HandleTIMER5
HandlerTIMER4 HANDLER HandleTIMER4
HandlerTIMER3 HANDLER HandleTIMER3
HandlerTIMER2 HANDLER HandleTIMER2
HandlerTIMER1 HANDLER HandleTIMER1
HandlerTIMER0 HANDLER HandleTIMER0
HandlerUERR01 HANDLER HandleUERR01
HandlerWDT HANDLER HandleWDT
HandlerBDMA1 HANDLER HandleBDMA1
HandlerBDMA0 HANDLER HandleBDMA0
HandlerZDMA1 HANDLER HandleZDMA1
HandlerZDMA0 HANDLER HandleZDMA0
HandlerTICK HANDLER HandleTICK
HandlerEINT4567 HANDLER HandleEINT4567
HandlerEINT3 HANDLER HandleEINT3
HandlerEINT2 HANDLER HandleEINT2
HandlerEINT1 HANDLER HandleEINT1
HandlerEINT0 HANDLER HandleEINT0
;One of the following two routines can be used for non-vectored interrupt.
;下面這段程序是用來處理非向量中斷,具體判斷I_ISPR中各位是否置1 置1表示目前此中斷等待響應(每次只能有一位置1),從最高優先級中斷位開始判斷,檢測到等待服務
;中斷就將pc置爲中斷服務函數首地址
IsrIRQ ;using I_ISPR register.
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
;IMPORTANT CAUTION
;if I_ISPC isn't used properly, I_ISPR can be 0 in this routine.
ldr r9,=I_ISPR
ldr r9,[r9]
mov r8,#0x0
0
movs r9,r9,lsr #1
bcs %F1
add r8,r8,#4
b %B0
1
ldr r9,=HandleADC
add r9,r9,r8
ldr r9,[r9]
str r9,[sp,#8]
ldmfd sp!,{r8-r9,pc}
;****************************************************
;* START *www.chinasrx.com
;****************************************************
;板子上電和復位後,程序開始從位於0x0處執行b ResetHandler ,從而跳轉到這裏執行程序。
;板子上電覆位後,執行以下幾個步驟,這裏通過標號在註釋中加1,2,3....表示,標號表示執行順序
;1.禁止看門狗,屏蔽所有中斷
ResetHandler
ldr r0,=WTCON ;watch dog disable
ldr r1,=0x0
str r1,[r0]
ldr r0,=INTMSK
ldr r1,=0x07ffffff ;all interrupt disable
str r1,[r0]
;2.根據工作頻率設置PLL
;這裏介紹一下計算公式
; Fpllo=(m*Fin)/(p*2^s)
; m=MDIV+8,p=PDIV+2,s=SDIV
; Fpllo必須大於20Mhz小於66Mhz
; Fpllo*2^s必須小於170Mhz
;如下面的PLLCON設定中的M_DIV P_DIV S_DIV是取自option.h中
;#elif (MCLK==40000000)
;#define PLL_M (0x48)
;#define PLL_P (0x3)
;#define PLL_S (0x2)
;所以m=MDIV+8=80,p=PDIV+2=5,s=SDIV=2
;硬件使用晶振爲10Mhz,即Fin=10Mhz
;Fpllo=80*10/5*2^2=40Mhz
;****************************************************
;* Set clock control registers *
;****************************************************
ldr r0,=LOCKTIME
ldr r1,=800 ; count = t_lock * Fin (t_lock=200us, Fin=4MHz) = 800
str r1,[r0]
[ PLLONSTART
ldr r0,=PLLCON ;temporary setting of PLL
ldr r1,=((M_DIV<<12)+(P_DIV<<4)+S_DIV) ;Fin=10MHz,Fout=40MHz
str r1,[r0]
]
ldr r0,=CLKCON
ldr r1,=0x7ff8 ;All unit block CLK enable
str r1,[r0]
;3.設置存儲相關寄存器的程序
;主要設置SDRAM,flash ROM 存儲器連接和工作時序的程序,以及片選定義的程序
;SMRDATA map在下面的程序中定義
;SMRDATA中涉及的值請參考memcfg.s程序
;具體寄存器各位含義請參考S3C44B0X Specification
;www.chinasrx.com
;****************************************************
;* Set memory control registers *
;****************************************************
ldr r0,=SMRDATA
ldmia r0,{r1-r13}
ldr r0,=0x01c80000 ;BWSCON Address
stmia r0,{r1-r13}
;****************************************************
;* Initialize stacks *
;****************************************************
ldr sp, =SVCStack ;Why?
bl InitStacks
;
;5.設置缺省中斷處理函數
;****************************************************
;* Setup IRQ handler *
;****************************************************
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
;6.將數據段拷貝到RAM中,將ZI數據段清零,跳入C語言的main函數執行。
;到這裏Bootloader初步引導結束。拷貝|Image$$RO$$Limit|起始的大小爲(|Image$$ZI$$Base|-|Image$$RW$$Base|)
;的數據拷貝到|Image$$RW$$Base|對應的數據單元處。
;********************************************************
;* Copy and paste RW data/zero initialized data *
;********************************************************
LDR r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
LDR r1, =|Image$$RW$$Base| ; and RAM copy
LDR r3, =|Image$$ZI$$Base|
;Zero init base => top of initialised data
CMP r0, r1 ; Check that they are different
BEQ %F1
0
CMP r1, r3 ; Copy init data
LDRCC r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4
STRCC r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4
BCC %B0
1
LDR r1, =|Image$$ZI$$Limit| ; Top of zero init segment
MOV r2, #0
2
CMP r3, r1 ; Zero init
STRCC r2, [r3], #4
BCC %B2
[ :LNOT:THUMBCODE
BL Main ;Don't use main() because ......
;跳入main()函數
B .
]
[ THUMBCODE ;for start-up code for Thumb mode
orr lr,pc,#1
bx lr
CODE16
bl Main ;Don't use main() because ......
b .
CODE32
]
;4.初始化各模式下的堆棧指針
;****************************************************
;* The function for initializing stack *
;****************************************************
InitStacks
;Don't use DRAM,such as stmfd,ldmfd......
;SVCstack is initialized before
;Under toolkit ver 2.50, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'
mrs r0,cpsr
bic r0,r0,#MODEMASK
orr r1,r0,#UNDEFMODE|NOINT
msr cpsr_cxsf,r1 ;UndefMode
ldr sp,=UndefStack
orr r1,r0,#ABORTMODE|NOINT
msr cpsr_cxsf,r1 ;AbortMode
ldr sp,=AbortStack
orr r1,r0,#IRQMODE|NOINT
msr cpsr_cxsf,r1 ;IRQMode
ldr sp,=IRQStack
orr r1,r0,#FIQMODE|NOINT
msr cpsr_cxsf,r1 ;FIQMode
ldr sp,=FIQStack
bic r0,r0,#MODEMASK|NOINT
orr r1,r0,#SVCMODE
msr cpsr_cxsf,r1 ;SVCMode
ldr sp,=SVCStack
;USER mode is not initialized.
mov pc,lr ;The LR register may be not valid for the mode changes.
;
;下面是PWDN模式下的相關寄存器的定義
;****************************************************
;* The function for entering power down mode *
;****************************************************
;void EnterPWDN(int CLKCON);
EnterPWDN
mov r2,r0 ;r0=CLKCON
ldr r0,=REFRESH
ldr r3,[r0]
mov r1, r3
orr r1, r1, #0x400000 ;self-refresh enable
str r1, [r0]
nop ;Wait until self-refresh is issued. May not be needed.
nop ;If the other bus master holds the bus, ...
nop ; mov r0, r0
nop
nop
nop
nop
;enter POWERDN mode
ldr r0,=CLKCON
str r2,[r0]
;wait until enter SL_IDLE,STOP mode and until wake-up
mov r0,#0xff
0 subs r0,r0,#1
bne %B0
;exit from DRAM/SDRAM self refresh mode.
ldr r0,=REFRESH
str r3,[r0]
mov pc,lr
LTORG
;
;
;這是上面提到的對存儲寄存器初始化的數據映射表(DATA Map)
SMRDATA DATA
;*****************************************************************
;* Memory configuration has to be optimized for best performance *
;* The following parameter is not optimized. *
;*****************************************************************
;*** memory access cycle parameter strategy ***
; 1) Even FP-DRAM, EDO setting has more late fetch point by half-clock
; 2) The memory settings,here, are made the safe parameters even at 66Mhz.
; 3) FP-DRAM Parameters:tRCD=3 for tRAC, tcas=2 for pad delay, tcp=2 for bus load.
; 4) DRAM refresh rate is for 40Mhz.
DCD 0x11110090 ;Bank0=OM[1:0], Bank1~Bank7=16bit, bank2=8bit;
DCD
((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
;GCS0
DCD
((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
;GCS1
DCD
((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
;GCS2
DCD
((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
;GCS3
DCD
((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
;GCS4
DCD
((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
;GCS5
DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) ;GCS6
DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) ;GCS7
DCD
((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
;REFRESH RFEN=1, TREFMD=0, trp=3clk, trc=5clk, tchr=3clk,count=1019
DCD 0x16 ;SCLK power mode, BANKSIZE 32M/32M
DCD 0x20 ;MRSR6 CL=2clk
DCD 0x20 ;MRSR7
ALIGN
;下面是對RAM區域數據映射表的(DATA Map)的定義
AREA RamData, DATA, READWRITE
;從地址(_ISR_STARTADDRESS-0x500)處保留指定字節數的空間
^ (_ISR_STARTADDRESS-0x500)
UserStack # 256 ;c1(c7)ffa00 ;以地址(_ISR_STARTADDRESS-0x500)起始保留連續256個字節
SVCStack # 256 ;c1(c7)ffb00 ;緊接着保留256個字節
UndefStack # 256 ;c1(c7)ffc00
AbortStack # 256 ;c1(c7)ffd00
IRQStack # 256 ;c1(c7)ffe00
FIQStack # 0 ;c1(c7)fff00
;從地址_ISR_STARTADDRESS處保留指定字節數的空間
^ _ISR_STARTADDRESS ;該值在OPTION.h中定義
HandleReset # 4 ;保留4個連續字節
HandleUndef # 4
HandleSWI # 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ # 4
HandleFIQ # 4
;Don't use the label 'IntVectorTable',
;because armasm.exe cann't recognize this label correctly.
;the value is different with an address you think it may be.
;IntVectorTable
HandleADC # 4
HandleRTC # 4
HandleUTXD1 # 4
HandleUTXD0 # 4
HandleSIO # 4
HandleIIC # 4
HandleURXD1 # 4
HandleURXD0 # 4
HandleTIMER5 # 4
HandleTIMER4 # 4
HandleTIMER3 # 4
HandleTIMER2 # 4
HandleTIMER1 # 4
HandleTIMER0 # 4
HandleUERR01 # 4
HandleWDT # 4
HandleBDMA1 # 4
HandleBDMA0 # 4
HandleZDMA1 # 4
HandleZDMA0 # 4
HandleTICK # 4
HandleEINT4567 # 4
HandleEINT3 # 4
HandleEINT2 # 4
HandleEINT1 # 4
HandleEINT0 # 4 ;0xc1(c7)fff84
END
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
44B0的初始化程序就是初始化各個關鍵的寄存器,建立中斷向量,然後轉移到主函數去執行程序。
不過44B0不支持地址映射,所以程序不COPY到RAM種執行。44B0初始化對我們廣大
初學者來說,比較難理解的是中斷的處理和一些少見的操作符號,44b0的中斷子程序地址存放在初始化程序最後就是
HandleADC # 4
HandleRTC # 4
HandleUTXD1 # 4
HandleUTXD0 # 4
HandleSIO # 4
HandleIIC # 4
HandleURXD1 # 4
HandleURXD0 # 4
這一段,它的其實地址是ISR_STARTADDRESS,個人寫中斷程序的時候,子程序地址被編譯器連放在相應的位置。初始化完成後,程序轉通過BL Main 轉到用戶定義的主程序上執行。以下是我個人的一些理解:
GBLL THUMBCODE
[ {CONFIG} = 16
THUMBCODE SETL {TRUE}
CODE32
|
THUMBCODE SETL {FALSE}
]
[ THUMBCODE
CODE32 ;for start-up code for Thumb mode
]
×××××××××××××××××××××××
其中[=IF ,|=ELSE ,]= ENDIF, CODE32 表明以下操作都在ARM狀態。這些都是僞操作
這段我理解爲設定THUMCODE的值,然後確定,用戶的程序是在ARM狀態還是THUM狀態。不過不管THUMCODE是何值,下面代碼都是ARM狀態這段沒有什麼很複雜的,就是這三個[,|,]操作符讓我迷惑了半天,翻了半天書才找到解釋
MACRO 宏 僞操作
$HandlerLabel HANDLER(宏的名稱) $HandleLabel(宏的參數)
$HandlerLabel
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does't push because it return to original address)
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
*******************************
這
段當初我覺得比較難理解,不過通過看各種程序,對這段有了一個基本的理解。這個宏的作用是把各個中斷程序的地址裝入當前的PC,44B0有兩種裝斷模式
一種是沒有中斷向量表,一種是使用中斷向量表的使用中斷向量表只能是IRQ方式,當使用中斷向量表的時候,中斷髮生時由44B0的中斷控制器自動跳轉到相
應的位置。比如在中斷向量表的模式下,一個外部中斷0發生程序自動跳轉到 地址0X20處,0X20地址單元的指令時ldr
pc,=HandlerEINT0因而程序PC跳到HandlerEINT0處,執行這個宏操作,把外部中斷的函數的地址賦給PC。
44B0裏面定義了一個#define pISR_EINT0 (*(unsigned
*)(_ISR_STARTADDRESS+0x84))
,_ISR_STARTADDRES是中斷程序地址的起始地址,_ISR_STARTADDRESS+0X84是HandleEINT0的地址
例如一個外部中斷函數名void EXINT(),程序裏執行 pISR_EINT0=(unsigned)EXIT,就把自己的函數地址賦給了標號爲HandleEINT0處的內存單元
IMPORT |Image$$RO$$Limit| ; End of ROM code (=start of ROM data)
IMPORT |Image$$RW$$Base| ; Base of RAM to initialise
IMPORT |Image$$ZI$$Base| ; Base and limit of area
IMPORT |Image$$ZI$$Limit| ; to zero initialise
××××××××××××××××××××××××××××××××××
這段我個人的理解爲這些是連接器生成的於輸出段相關的符號,是在沒有使用SCATTER文件的情況可以調用。
這段指出了在ROM和RAM種的數據的地址,這些地址應該是連接器生成的,不過爲什麼能調用
連接器生產的符號,我不大明白其中的原因,還希望各位說說自己的理解
IsrIRQ ;using I_ISPR register.
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
;IMPORTANT CAUTION
;if I_ISPC isn't used properly, I_ISPR can be 0 in this routine.
ldr r9,=I_ISPR
ldr r9,[r9]
mov r8,#0x0
0
movs r9,r9,lsr #1
bcs %F1
add r8,r8,#4
b %B0
1
ldr r9,=HandleADC
add r9,r9,r8
ldr r9,[r9]
str r9,[sp,#8]
ldmfd sp!,{r8-r9,pc}
×××××××××××××××××××××××
這段是沒有使用裝斷向量模式下如何裝載中斷子程序,因爲44B0有30箇中斷源,所以需要程序處理以確定調用那個中斷程序
0,1是局部標號,%B是向後搜索局部標號, %F是向前搜索局部標號 。都是僞操作
I_ISPR寄存器各位表明發生了應該調用那個中斷子程序。只能1位置位,其它位爲0,比如說串口1發送中斷髮生,這時I_ISPR的
值爲0X04,
ldr r9,=I_ISPR
ldr r9,[r9] 兩條指令後,r9的內容爲0X4 ,
movs r9,r9,lsr #1 r9內容右移一位
bcs %F1 判斷是否把置位是否轉移到C位,
add r8,r8,#4 如果沒有的R8加4
如
果r9內容爲0x04 需要右移3次 ,之後r8的內容爲8 然後HandleADC的地址 加上r8的值
就是串口1發送中斷的地址,這個地址的內容是中斷子程序的地址再說明幾個僞操作:^=MAP.
#=field別的方面我覺得比較容易理解了,就不多講了。
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
ARM彙編程序分析過程中,比較難理解的是他的僞操作、宏指令和僞指令。本文是結合44B0X引導程序中出現僞操作、宏指令和僞指令進行總結,便於進一步分析44B0X的引導。
一、GET option.s
功能:引進一個被編譯過的文件。
格式:GET filename
其中:fiename 彙編時引入的文件名,可以有路徑名。
GET符號在彙編時對宏定義,EQU符號以及存儲映射時是很有用的,在引入文件彙編完以後,彙編將從GET符號後開始。在被引入的文件中可能有GET符號再引入其他的文件。GET符號不能用來引入目標文件。
二、INTPND EQU 0x01e00004
功能:對一個數字常量賦予一個符號名。
格式:name EQU expression
其中:name 符號名。Expression 寄存器相關或者程序相關的固定值。
使用EQU定義常量,與C語言中用#define定義一個常量相同。
例:num EQU 2 ; 數字2賦予符號num
三、GBLL THUMBCODE
[ {CONFIG} = 16
THUMBCODE SETL {TRUE}
CODE32
|
THUMBCODE SETL {FALSE}
]
CODE32 ;for start-up code for Thumb mode
]
四、MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack
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
功能:標誌以下宏的定義。
格式:MACRO
Macro_prototype
MEND
宏表達式的格式如下:
{$label} macroname {$ parameter{,parameter2}…}
其中:
$ label 參數,在宏使用時,被給定的符號替代。
Macroname 宏的名稱,並不一定以一條指令或者符號名開始。
$parameter 在宏使用時,被替代的參數,格式爲:$parameter=“default value”
在 宏體中,參數如:$parameter和變量一樣使用,在被宏引用時,被賦於新值,參數必須用“$”符號加於區別。$label在宏定義內部符號時很有 用,可以看作宏的參數。使用“|”符號作爲使用一個參數缺省值的變量,如果使用的是一個空格符串,將省去該變量。在使用內部標誌的宏定義中,將內部標誌定 義爲帶後綴的標誌,將會很有用。如果在擴展中空間不夠,可以作爲參數和後繼文字之間或者參數之間使用圓點隔開,但在文本和後繼參數之間不能使用圓點。宏可 以定義局部變量的範圍。宏還可以嵌套使用。
例:
MACRO
$label xmac $p1,$p2
LCLS err
$labell,loopl
BGE $pl
$labell,loop2
BL $p1
BEG $p1
BEG $labell,loop2
MEND
五、$和$$
GBLS STR1
GBLS STR2
STR1 SETS "AAA"
STR2 SETS "BBB$$STR1.CCC" //彙編後STR2的值爲bbAAACCC
六、 IMPORT Main ; The main entry of mon program
格式:
IMPORT symbol {[WEAK]}
symbol 引用的符號的名稱,他是區分大小寫的,[WEAK]指定這個選項後,如果symbol所在的源文件中沒有被定義,編譯器也不會報錯。他和EXTERN作用 相同,不同之處在於,如果本源文件沒有實際引用該符號,該符號將不會被加入到本源文件的符號表中。
*****************************************************
七、AREA Init,CODE,READONLY
ENTRY
格式:
Attr 段名屬性,下列屬性是有效的:
ALIGN=expression
缺省狀態下,AOF段將按4個字節對準,expression可以是2~31之間的整數,該段將按2(上標爲expression)字節對準。例如,espression等於
COMDEF 通用段定義。該AOF段可能包括代碼和數據,但必須與其他段名相區別。
COMMON 通用數據段,無須再註釋定義任何代碼和數據,通常由鏈接器初始化爲零。
DATA 包含數據,但是不包含指令,缺省爲READWRITE
INTERWORK 表明代碼段可以適用ARM/Thumb interworking功能。
NOINIT 表明數據段可以初始化爲零,只包含指示符。
PIC 表明定位獨立段,可以不修改情況下,在任意地址執行。
READONLY 表明該段可讀可寫。
八、LTORG
ldr r0, =instruction 將地址instruction載入r0
此時編譯器將ldr儘可能的轉變成mov或mvn指令。 如果轉變不成, 將產生一個ldr指令,通過pc相對地址從一塊保存常數的內存區讀出instruction的值。此內存區既是文本池。一般的, 文本池放在END指令之後的地方。但是, 如果偏移地址大於4k空間, ldr指令會出錯(因爲ldr的相對偏移地址爲12-bit的值). 此時使用LTORG放到會出錯的ldr指令附近,以解決此問題。 編譯器會收集沒有分配的ldr的值放到此文本池中
九、LDR r0,=WTCON ;watch dog disable
LDR r1,=0x0
格式:
LDR{condition} register,=[expression|Label-expression]
其中:
condition 可選的條件代碼。
register 讀取的寄存器。
expression 數字常量:
如果該數字常量在MOV或MVN指令的範圍中,彙編器會產生合適的指令;
如果該數字量不在MOV或MVN指令的範圍中,彙編器把該常量於程序後,用程序相關的LDR僞指令讀取,PC與該常量的偏移量不得超過4KB。
Label-expression 程序相關的或外部的表達式。彙編器將其存放在程序後的常量庫(稱爲文字池(literal pool))中,用程序相關的LDR僞指令讀取,PC與與該常量的偏移量不得超過4KB。
對於不能被MOV和MVN指令所讀取的立即數,將其變成常量,進行讀取。
將一個程序相關的或外部的表達式讀取進寄存器中。
例:
LDR R1, =0xfff
LDR R2, =place
十、DCD 0x11110090
功能: 分配一個或多個字,從4個字節邊界開始。
格式:
{label}DCD expression{,expression}…
其中:
expression 可以是:
一個數學表達式;
一個程序相關的表達式。
按4個字節對準時,DCD符號會在第一個字節之前插入3個字節的空字符,如果無須對準的話,可以使用DCDU符號。
例:
datal DCD 1,5,20
data2 DCD mem06
data3 DCD glb+4
十一、ALIGN
格式:
ALIGN {expression {,offset-expression} }
其中:
expression 2(上標爲0)到2(上標爲31)之間的任意數冪,當前按2(上標爲n)字節對準,如果該參數沒有指定,ALIGN將按字對準。
Offset-expression 定義expression指定的對準方式的字節偏移量。
在代碼段出現數據定義符時,使用ALIGE符號。當在代碼段使用數據定義符(DCB,DCW,DCWU,DCDU和%),程序計數器PC並不一定按字對準。
ARM狀態下按字對準;
Thumb狀態下按半字對準。
在Thumb狀態下,可以使用ALIGN2對Thumb代碼按半字對準。
使用ALIGN狀態下,還可以充分利用一些ARM處理器的Cache,例如,ARM940T有一個每行4字的Cache,使用ALIGN16按16字節對準,從而最大限度使用Cache。
十二、^ _ISR_STARTADDRESS
MAP用於定義一個結構化的內存表(StorageMAP)的首地址。此時,內存表的位置計數器{VAR}(彙編器的內置變量)設置成該地址值。MAP可以用”^”代替。
語法:MAP expr {,base-register}
其中,expr爲數字表達式或者是程序中已經定義過的標號。Base-register爲一個寄存器。當指令中沒有Base-register 時,expr爲結構化內存表的首地址。此時,內存表的位置計數器{VAR}設置成該地址值。當指令中包含這一項時,結構化內存表的首地址爲expr和 Base-register寄存器內容的和。
使用說明:MAP僞操作和FIELD僞操作配合使用來定義結構化的內存表結構。
MAP fun ;fun就是內存表的首地址
MAP 0x100,R9 ;內存表的首地址爲 R9+0x100
十三、HandleReset # 4
HandleUndef # 4
HandleSWI # 4
FIELD 用於定義一個結構化的內存表中的數據域。FIELD 可用“#”代替。
語法:{label} FIELD expr
其中:{label}爲可選的。當指令中包含這一項時,label的值爲當前內存表的位置計數器{VAR}的值。彙編編譯器處理了這條FIELD僞操作後。
Consta FIELD 4 ; //consta 長爲4字節,相對位置爲0
Constb FIELD 4; //constb長爲4字節,相對位置爲4
X FIELD 8; // X長爲8字節,相對位置爲8
Y FIELD 8; // y長爲8字節,相對位置爲16
String FIELD 16 ;// String爲16字節,相對位置爲24
Consta FIELD 4 ; //consta 長爲4字節,相對位置爲0
Constb FIELD 4; //constb長爲4字節,相對位置爲4
X FIELD 8; // X長爲8字節,相對位置爲8
Y FIELD 8; // y長爲8字節,相對位置爲16
String FIELD 16;// String爲16字節,相對位置爲24
LDR R5,Constb;//相當於LDR R5,[R9,#4]
MAP Data;//內存表的首地址爲Datastruc內存單元
Consta FIELD 4 ; //consta 長爲4字節,相對位置爲0
Constb FIELD 4; //constb長爲4字節,相對位置爲4
X FIELD 8; // X長爲8字節,相對位置爲8
Y FIELD 8; // y長爲8字節,相對位置爲16
String FIELD 16;// String爲16字節,相對位置爲24
十四、RN
在局部標號中:
%表示引用操作
F指示編譯器只向前搜索。
B指示編譯器只向後搜索。
A指示編譯器搜索宏的所有嵌套層。
T指示編譯器搜索宏的當前層次。
若F、B沒有指定則先向前搜索,再向後搜索。
若A、T都沒有指定則先從當前層到最高層,比當前層低的不再搜索。
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
ADS 中的配置:
以一般的 S3C44B0X 的板子爲例:
1、 RO: 0x0, RW: 0xC000000 (Debug 模式下是:RO: 0xC000000, RW: ---)
2、 ARM LINKER:
Layout 中指明整個 IMAGE 的入口,這一般和你的程序有關。
Place at beginning of image
1) Object/Symbol: 放程序的初始化的彙編文件,比如 44binit.o 或 boot.o. 你的工程不是有一個 .S
文件嗎,比如 44binit.s 或 boot.s 或你自己到的一個文件名,你只要把後綴名改成 .o , 填進去就可以了
2) 還是剛纔那個 .s 文件, 你找一下,是不是有以下的代碼 IMPORT Main ; The main entry of mon
program AREA Init,CODE,READONLY ;這就是入口的 AREA, 這裏的名字叫
Init,也可以是你自己取的名字,把這個名字填到 Section中。
3) ENTRY b ResetHandler
;for debug ;這是debug時ADS停下來的第一條語句。還有,就是確認一個你的程序是否正確固化到Flash中,你可以把工程編譯成
.bin 格式的映像。然後通過編程器或 ads 讀取 Flash 的內容,與 .bin 文件比較.