ARM汇编指令汇总
??? 在ARM匯編里,有些字符是用來標(biāo)記行號的,這些字符要求頂格寫;有些偽碼是需要成對出現(xiàn)的,例如ENTRY和END,就需要對齊出現(xiàn),也就是說他們要么都頂格,要么都空相等的空,否則編譯器將報錯。常量定義需要頂格書寫,不然,編譯器同樣會報錯。
??? 2、字符串變量的值是一系列的字符,并且使用雙引號作為分界符,如果要在字符串中使用雙引號,則必須連續(xù)使用兩個雙引號。
??? 3、在使用LDR時,當(dāng)格式是LDR r0,=0x022248,則第二個參數(shù)表示地址,即0x022248,同樣的,當(dāng)src變量代表一個數(shù)組時,需要將r0寄存器指向src則需要這樣賦值:LDR r0,=src???? 當(dāng)格式是LDR r0,[r2],則第二個參數(shù)表示寄存器,我的理解是[]符號表示取內(nèi)容,r2本身表示一個寄存器地址,取內(nèi)容候?qū)⑵浯嫒0這個寄存器中。
??? 4、在語句:
?????? CMP r0,#num
?????? BHS stop
?????? 書上意思是:如果r0寄存器中的值比num大的話,程序就跳轉(zhuǎn)到stop標(biāo)記的行。但是,實際測試的時候,我發(fā)現(xiàn)如果r0和num相等也能跳轉(zhuǎn)到stop標(biāo)記的行,也就是說只要r0小于num才不會跳轉(zhuǎn)。
???
???? 下面就兩個具體的例子談?wù)凙RM匯編(這是我昨天好不容易看懂的,呵呵)。
???? 第一個是使用跳轉(zhuǎn)表解決分支轉(zhuǎn)移問題的例程,源代碼如下(保存的時候請將文件后綴名改為s):??
????? AREA JumpTest,CODE,READONLY
????? CODE32
?num? EQU? 4
?ENTRY
?
start
????? MOV? r0, #4
????? MOV? r1, #3
????? MOV? r2, #2
????? MOV? r3, #0
?
????? CMP? r0,? #num
????? BHS? stop
?
????? ADR? r4, JumpTable
?
????? CMP? r0, #2
????? MOVEQ? r3, #0
????? LDREQ? pc, [r4,r3,LSL #2]
?
????? CMP? r0, #3
????? MOVEQ? r3, #1
????? LDREQ? pc, [r4,r3,LSL #2]
?
????? CMP? r0, #4
????? MOVEQ? r3, #2
????? LDREQ? pc, [r4,r3,LSL #2]
?
????? CMP? r0, #1
????? MOVEQ? r3, #3
????? LDREQ? pc, [r4,r3,LSL #2]
?
DEFAULT
????? MOVEQ? r0, #0
?
SWITCHEND
stop
????? MOV? r0, #0x18
????? LDR? r1, =0x20026
????? SWI? 0x123456
?
JumpTable
????? DCD? CASE1
????? DCD? CASE2
????? DCD? CASE3
????? DCD? CASE4
????? DCD? DEFAULT
?
CASE1
????? ADD? r0, r1, r2
????? B? SWITCHEND
?
CASE2
????? SUB? r0, r1, r2
????? B? SWITCHEND
?
CASE3
????? ORR? r0, r1, r2
????? B? SWITCHEND
?
CASE4
????? AND? r0, r1, r2
????? B? SWITCHEND
?END
??? 程序其實很簡單,可見我有多愚笨!還是簡要介紹一下這段代碼吧。首先用AREA偽代碼加上CODE,表明下面引出的將是一個代碼段(于此相對的還有數(shù)據(jù)段DATA),ENTRY 和END成對出現(xiàn),說明他們之間的代碼是程序的主體。start段給寄存器初始化。ADR? r4, JumpTable一句是將相當(dāng)于數(shù)組的JumpTable的地址付給r4這個寄存器。
??? stop一段是用來是程序退出的,第一個語句“MOV r0,#0x18”將r0賦值為0x18,這個立即數(shù)對應(yīng)于宏angel_SWIreason_ReportException。表示r1中存放的執(zhí)行狀態(tài)。語句“LDR r1,=0x20026”將r1的值設(shè)置成ADP_Stopped_ApplicationExit,該宏表示程序正常退出。然后使用SWI,語句“SWI 0x123456”結(jié)束程序,將CPU的控制權(quán)交回調(diào)試器手中。
??? 在JumpTable表中,DCD類型的數(shù)組包含四個字,所以,當(dāng)實現(xiàn)CASE跳轉(zhuǎn)的時候,需要將給出的索引乘上4,才是真正前進(jìn)的地址數(shù)。
?
??? 再看一個用匯編實現(xiàn)冒泡排序的例程:
???? AREA Sort,CODE,READONLY
?ENTRY
?
start
???? MOV r4,#0
???? LDR r6,=src
???? ADD r6,r6,#len
?
outer
???? LDR r1,=src
?
inner
???? LDR r2,[r1]
???? LDR r3,[r1,#4]
???? CMP r2,r3
???? STRGT r3,[r1]
???? STRGT r2,[r1,#4]
???? ADD r1,r1,#4
???? CMP r1,r6
???? BLT inner
?
???? ADD r4,r4,#4
???? CMP r4,#len
???? SUBLE r6,r6,#4
???? BLE outer
?
stop
???? MOV r0,#0x18
???? LDR r1,=0x20026
???? SWI 0x123456
?
???? AREA Array,DATA,READWRITE
src DCD 2,4,10,8,14,1,20
len EQU 7*4
???? END
???? 用匯編實現(xiàn)循環(huán)需要跳轉(zhuǎn)指令,但是因為ARM系統(tǒng)只有一個CPSR寄存器,所以要實現(xiàn)雙重循環(huán)還是有些難度。上面這個代碼還是有相當(dāng)大的借鑒意義。程序不難讀懂,和C語言的冒泡排序基本思路是完全一樣的。
?
?
?
Load CodeWarrior from the Start Menu.
Create a new project (File | New), select ARM Executable Image and give it the name "hello".
Create a new assembler source file (File | New Text File) and paste the following code in it.
??????????? ; Hello world in ARM assembler
??????????? AREA text, CODE
??????????? ; This section is called "text", and contains code
??????????? ENTRY
??????????? ; Print "Hello world"
??????????? ; Get the offset to the string in r4.
??????????? adr?????? r4, hello?????????? ;; "address in register"
loop??????? ; "loop" is a label and designates an address
??????????? ; Call putchar to display each character
??????????? ; to illustrate how a loop works
??????????? ldrb????? r0, [r4], #1??????? ; Get next byte and post-index r4
??????????? cmp?????? r0, #0????????????? ; Stop when we hit a null
??????????? beq?????? outputstring??????? ;; "branch if equal" = cond. goto
??????????? bl??????? putchar????????????
??????????? b???????? loop??????????????? ;; "branch" =? goto
outputstring
??????????? ; Alternatively, use putstring to write out the
??????????? ; whole string in one go
??????????? adr?????? r0, hello
??????????? bl??????? putstring?????????? ;; "branch+link" = subroutine call
finish
??????????? ; Standard exit code: SWI 0x123456, calling routine 0x18
??????????? ; with argument 0x20026
??????????? mov?????? r0, #0x18
??????????? mov?????? r1, #0x20000??????? ; build the "difficult" number...
??????????? add?????? r1, r1, #0x26?????? ; ...in two steps
??????????? SWI?????? 0x123456??????????? ;; "software interrupt" = sys call
hello
??????????? DCB?????? "Hello World/n",0
??????????? END
?
?
?
?
?
?
?
??
?
從下面的一個ARM 匯編小程序要弄懂的以下三個問題:
1).在ARM狀態(tài)轉(zhuǎn)到THUNB狀態(tài)和BX的應(yīng)用
2).匯編的架構(gòu)
3).SWI指令的使用
AREA??? ADDREG,CODE,READONLY
? ENTRY
MAIN
??? ADR? r0,ThunbProg + 1? ;(為什么要加1呢?因為BX指令跳轉(zhuǎn)到指定的地址執(zhí)行程序?? 時,?? 若 (BX{cond}? Rm)Rm的位[0]為1,則跳轉(zhuǎn)時自動將CPSR中的標(biāo)志T置位即把目標(biāo) 代碼解釋為 Thunb代碼)
??? BX??? r0
??? CODE16
ThunbProg
??? mov r2,#2
? mov r3,#3
? add r2,r2,r3
ADR r0,ARMProg
BX? ro
CODE32
ARMProg
? mov r4,#4
mov r5,#5
add? r4,r4,r5
???????????? stop?? mov r0,#0x18
????????????? LDR? r1,=0x20026
???????????? SWI?? 0x123456
???????????? END
SWI--軟中斷指令:
SWI指令用于產(chǎn)生軟中斷,從擁護(hù)模式變換到管理模式,CPSR保存到管理模式的SPSR中.
SWI{cond}????? immed_24????? ;immed_24為軟中斷號(服務(wù)類型)
使用SWI指令時,通常使用以下兩種方法進(jìn)行傳遞參數(shù),SWI 異常中斷處理程序就可以提供相關(guān)的服務(wù),這兩種方法均是用戶軟件協(xié)定.SWI異常中斷處理程序要通過讀取引起軟中斷的SWI指令,以取得24位立即數(shù).
(1) 指令中的24位立即數(shù)指定了用戶請求的服務(wù)類型,參數(shù)通過通用寄存器傳遞.
mov?? r0,#34??? ;設(shè)置子功能號位34
?? SWI?? 12???? ;調(diào)用12號軟中斷
(2) 指令中的24位立即數(shù)被忽略,用戶請求的服務(wù)類型有寄存器RO的值決定,參數(shù)通過其他的通用寄存器傳遞.
mov? r0,#12???????? ;調(diào)用12號軟中斷
mov r1,#34???????? ;設(shè)置子功能號位34
SWI 0
在SWI異常中斷處理程序中,取出SWI立即數(shù)的步驟為:首先確定引起軟中斷的SWI指令是ARM指令還是Thunb指令,這可通過對SPSR訪問得到;然后取得該SWI指令的地址,這可通過訪問LR寄存器得到;接著讀出指令,分解出立即數(shù).如如下程序:
T_bit????????????? EQU??????????????????? 0X20
SWI_Handler
??????????????? STMFD????? SP!,{R0-R3,R12,LR}????????????????? ;現(xiàn)場保護(hù)
?????????????? MRS?????????? R0,SPSR?????????????????????????????????? ;讀取SPSR
????????????? STMFD?????? SP!,{R0}???????????????????????????????????? :保存SPSR
????????????? TST???????????? R0,#T_bit???????????????????????
??????????? LDRNEH??????? R0,[LR,#-2]?????????????????????? ;若是Thunb指令,讀取指令碼(16位)
BICNE???????????? R0,#0XFF00???????????????????? :取得Thunb指令的8位立即數(shù)
LDREQ?????????? R0,[LR,#-4]????????????????????? ;若是ARM指令,讀取指令碼(32位)
BICEQ??????????? R0,#0XFF000000?????????? ;取得ARM指令的24位立即數(shù)
....
LDMFD????????? SP!,{R0-R3,R12,PC}^??? ;SWI異常中斷返回
?
?
ARM匯編的SWI指令軟中斷
從下面的一個ARM 匯編小程序要弄懂的以下三個問題:
1).在ARM狀態(tài)轉(zhuǎn)到THUNB狀態(tài)和BX的應(yīng)用
2).匯編的架構(gòu)
3).SWI指令的使用
AREA??? ADDREG,CODE,READONLY
? ENTRY
MAIN
??? ADR? r0,ThunbProg + 1? ;(為什么要加1呢?因為BX指令跳轉(zhuǎn)到指定的地址執(zhí)行程序?? 時,?? 若 (BX{cond}? Rm)Rm的位[0]為1,則跳轉(zhuǎn)時自動將CPSR中的標(biāo)志T置位即把目標(biāo) 代碼解釋為 Thunb代碼)
??? BX??? r0
??? CODE16
ThunbProg
??? mov r2,#2
? mov r3,#3
? add r2,r2,r3
ADR r0,ARMProg
BX? ro
CODE32
ARMProg
? mov r4,#4
mov r5,#5
add? r4,r4,r5
???????????? stop?? mov r0,#0x18
????????????? LDR? r1,=0x20026
???????????? SWI?? 0x123456
???????????? END
SWI--軟中斷指令:
SWI指令用于產(chǎn)生軟中斷,從擁護(hù)模式變換到管理模式,CPSR保存到管理模式的SPSR中.
SWI{cond}????? immed_24????? ;immed_24為軟中斷號(服務(wù)類型)
使用SWI指令時,通常使用以下兩種方法進(jìn)行傳遞參數(shù),SWI 異常中斷處理程序就可以提供相關(guān)的服務(wù),這兩種方法均是用戶軟件協(xié)定.SWI異常中斷處理程序要通過讀取引起軟中斷的SWI指令,以取得24位立即數(shù).
(1) 指令中的24位立即數(shù)指定了用戶請求的服務(wù)類型,參數(shù)通過通用寄存器傳遞.
mov?? r0,#34??? ;設(shè)置子功能號位34
?? SWI?? 12???? ;調(diào)用12號軟中斷
(2) 指令中的24位立即數(shù)被忽略,用戶請求的服務(wù)類型有寄存器RO的值決定,參數(shù)通過其他的通用寄存器傳遞.
mov? r0,#12???????? ;調(diào)用12號軟中斷
mov r1,#34???????? ;設(shè)置子功能號位34
SWI 0
在SWI異常中斷處理程序中,取出SWI立即數(shù)的步驟為:首先確定引起軟中斷的SWI指令是ARM指令還是Thunb指令,這可通過對SPSR訪問得到;然后取得該SWI指令的地址,這可通過訪問LR寄存器得到;接著讀出指令,分解出立即數(shù).如如下程序:
T_bit????????????? EQU??????????????????? 0X20
SWI_Handler
??????????????? STMFD????? SP!,{R0-R3,R12,LR}??????????? ;現(xiàn)場保護(hù)
?????????????? MRS?????????? R0,SPSR?????????????????????????? ;讀取SPSR
????????????? STMFD?????? SP!,{R0}??????????????????????????? :保存SPSR
????????????? TST???????????? R0,#T_bit???????????????????????
??????????? LDRNEH??????? R0,[LR,#-2]????????????? ;若是Thunb指令,讀取指令碼(16)
BICNE???????????? R0,#0XFF00????????????????? :取得Thunb指令的8位立即數(shù)
LDREQ?????????? R0,[LR,#-4]??????????????? ;若是ARM指令,讀取指令碼(32位)
BICEQ??????????? R0,#0XFF000000?????????? ;取得ARM指令的24位立即數(shù)
....
LDMFD????????? SP!,{R0-R3,R12,PC}^??? ;SWI異常中斷返回
Thu Oct 12 2006
?
軟件中斷SWI的實現(xiàn)
在需要軟件中斷處調(diào)用
__SWI? 0xNum?????????? ;Num為SWI中斷處理模塊的編號,見表SwiFunction
;軟件中斷
SoftwareInterrupt
??????? CMP???? R0, #12???????????????????????? ;R0中的SWI編號是否大于最大值
/* 下面這句語句把 (LDRLO地址+ 8 + R0*4) 的地址裝載到PC寄存器,舉例如果上面的 Num="1",也就是R0 = 1, 假設(shè)LDRLO這條指令的地址是0x00008000,那么根據(jù)ARM體系的2級流水線 PC寄存器里指向是下兩條指令 于是PC = 0x00008008? 也就是偽指令DCD???? TASK_SW 聲明的標(biāo)號TASK_SW? 的地址,注意DCD???? TASK_SW 這條指令本身不是ARM能執(zhí)行的指令,也不會占有地址,這條指令靠匯編器匯編成可執(zhí)行代碼,它的意義就是聲明 TASK_SW的地址,? , [PC, R0, LSL #2] 這個尋址方式就是 PC + R0的值左移2位的值( 0x01<<2? => 0x04 ),這樣PC的值就是0x0000800C, 即ENTER_CRITICAL的地址于是ARM執(zhí)行該標(biāo)號下的任務(wù) */
??????? LDRLO?? PC, [PC, R0, LSL #2]???????
??????? MOVS??? PC, LR
SwiFunction
??????? DCD???? TASK_SW??????????????? ;0
??????? DCD???? ENTER_CRITICAL???????? ;1
??????? DCD???? EXIT_CRITICAL??????????? ;2
??????? DCD???? ISRBegin???????????????? ;3
??????? DCD???? ChangeToSYSMode???????? ;4
??????? DCD???? ChangeToUSRMode???????? ;5
??????? DCD???? __OSStartHighRdy??????? ;6
??????? DCD???? TaskIsARM?????????????? ;7
??????? DCD???? TaskIsTHUMB???????????? ;8
??????? DCD???? OSISRNeedSwap?????????? ;9
??????? DCD???? GetOSFunctionAddr?????? ;10
??????? DCD???? GetUsrFunctionAddr????? ;11
TASK_SW
??????? MRS???? R3, SPSR??????????????????????? ;保存任務(wù)的CPSR
??????? MOV???? R2, LR????????????????????????? ;保存任務(wù)的PC
???????
??????? MSR???? CPSR_c, #(NoInt | SYS32Mode)??? ;切換到系統(tǒng)模式
??????? STMFD?? SP!, {R2}?????????????????????? ;保存PC到堆棧
??????? STMFD?? SP!, {R0-R12, LR}?????????????? ;保存R0-R12,LR到堆棧
??????????????????????????????????????????????? ;因為R0~R3沒有保存有用數(shù)據(jù),所以可以這樣做
??????? B?????? OSIntCtxSw_0??????????????????? ;真正進(jìn)行任務(wù)切換
ENTER_CRITICAL
??????????????????????????????????????????????? ;OsEnterSum++
??????? LDR???? R1, =OsEnterSum
??????? LDRB??? R2, [R1]
??????? ADD???? R2, R2, #1
??????? STRB??? R2, [R1]
??????????????????????????????????????????????? ;關(guān)中斷
??????? MRS???? R0, SPSR
??????? ORR???? R0, R0, #NoInt
??????? MSR???? SPSR_c, R0
??????? MOVS??? PC, LR
?
?
?
批量數(shù)據(jù)加載/存儲指令實驗???? 2007-08-22 12:08:06
大 中 小
標(biāo)簽:arm指令 ldm/stm
這個程序用批量傳輸指令傳輸數(shù)據(jù),一次可傳8個字:
??????? AREA Block, CODE, READONLY????? ; name this block of code
num???? EQU???? 20????????????? ; Set number of words to be copied
??????? ENTRY?????????????????? ; mark the first instruction to call
start
??????? LDR???? r0, =src??????? ; r0 = pointer to source block
??????? LDR???? r1, =dst??????? ; r1 = pointer to destination block
??????? MOV???? r2, #num??????? ; r2 = number of words to copy
??????? MOV???? sp, #0x400????? ; set up stack pointer (r13)
blockcopy?????
??????? MOVS??? r3,r2, LSR #3?? ; number of eight word multiples
??????? BEQ???? copywords?????????????? ; less than eight words to move ?
??????? STMFD?? sp!, {r4-r11}?? ; save some working registers
octcopy
??????? LDMIA?? r0!, {r4-r11}?? ; load 8 words from the source
??????? STMIA?? r1!, {r4-r11}?? ; and put them at the destination
??????? SUBS??? r3, r3, #1????????????? ; decrement the counter
??????? BNE???? octcopy???????? ; ... copy more
??????? LDMFD?? sp!, {r4-r11}?? ; dont need these now - restore originals
copywords
??????? ANDS??? r2, r2, #7????????????? ; number of odd words to copy
??????? BEQ???? stop??????????????????? ; No words left to copy ?
wordcopy
??????? LDR???? r3, [r0], #4??? ; a word from the source
??????? STR???? r3, [r1], #4??? ; store a word to the destination
??????? SUBS??? r2, r2, #1????????????? ; decrement the counter
??????? BNE???? wordcopy??????????????? ; ... copy more
stop
??????? MOV???? r0, #0x18?????????????? ; angel_SWIreason_ReportException
??????? LDR???? r1, =0x20026??? ; ADP_Stopped_ApplicationExit
??????? SWI???? 0x123456??????????????? ; ARM semihosting SWI
?
下面的這個程序?qū)崿F(xiàn)同樣的功能,每次只能傳一個字:
??????? AREA BlockData, DATA, READWRITE
src???? DCD???? 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst???? DCD???? 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
??????? END
?
??????? AREA Word, CODE, READONLY?????? ; name this block of code
num???? EQU???? 20????????????? ; Set number of words to be copied
??????? ENTRY?????????????????? ; mark the first instruction to call
start
??????? LDR???? r0, =src??????? ; r0 = pointer to source block
??????? LDR???? r1, =dst??????? ; r1 = pointer to destination block
??????? MOV???? r2, #num??????? ; r2 = number of words to copy
??????
wordcopy
??????? LDR???? r3, [r0], #4??? ; a word from the source
??????? STR???? r3, [r1], #4??? ; store a word to the destination
??????? SUBS??? r2, r2, #1????? ; decrement the counter
??????? BNE???? wordcopy??????? ; ... copy more
stop
??????? MOV???? r0, #0x18?????? ; angel_SWIreason_ReportException
??????? LDR???? r1, =0x20026??? ; ADP_Stopped_ApplicationExit
??????? SWI???? 0x123456??????? ; ARM semihosting SWI
??????? AREA BlockData, DATA, READWRITE
src???? DCD???? 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst???? DCD???? 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
??????? END
?
?
當(dāng)處理器工作在ARM狀態(tài)時,幾乎所有的指令均根據(jù)CPSR中條件碼的狀態(tài)和指令的條件域有條件的執(zhí)行。當(dāng)指令的執(zhí)行條件滿足時,指令被執(zhí)行,否則指令被忽略。
每一條ARM指令包含4位的條件碼,位于指令的最高4位[31:28]。條件碼共有16種,每種條件碼可用兩個字符表示,這兩個字符可以添加在指令助記符的后面和指令同時使用。例如,跳轉(zhuǎn)指令B可以加上后綴EQ變?yōu)锽EQ表示“相等則跳轉(zhuǎn)”,即當(dāng)CPSR中的Z標(biāo)志置位時發(fā)生跳轉(zhuǎn)。
1、 B指令
B指令的格式為:
B{條件} 目標(biāo)地址
B指令是最簡單的跳轉(zhuǎn)指令。一旦遇到一個 B 指令,ARM 處理器將立即跳轉(zhuǎn)到給定的目標(biāo)地址,從那里繼續(xù)執(zhí)行。注意存儲在跳轉(zhuǎn)指令中的實際值是相對當(dāng)前PC值的一個偏移量,而不是一個絕對地址,它的值由匯編器來計算(參考尋址方式中的相對尋址)。它是 24 位有符號數(shù),左移兩位后有符號擴展為 32 位,表示的有效偏移為 26 位(前后32MB的地址空間)。以下指令:
B Label ;程序無條件跳轉(zhuǎn)到標(biāo)號Label處執(zhí)行
CMP R1,#0 ;當(dāng)CPSR寄存器中的Z條件碼置位時,程序跳轉(zhuǎn)到標(biāo)號Label處執(zhí)行
BEQ Label
3.3.6 批量數(shù)據(jù)加載/存儲指令
ARM微處理器所支持批量數(shù)據(jù)加載/存儲指令可以一次在一片連續(xù)的存儲器單元和多個寄存器之間傳送數(shù)據(jù),批量加載指令用于將一片連續(xù)的存儲器中的數(shù)據(jù)傳送到多個寄存器,批量數(shù)據(jù)存儲指令則完成相反的操作。常用的加載存儲指令如下:
— LDM 批量數(shù)據(jù)加載指令
— STM 批量數(shù)據(jù)存儲指令
LDM(或STM)指令
LDM(或STM)指令的格式為:
LDM(或STM){條件}{類型} 基址寄存器{!},寄存器列表{∧}
LDM(或STM)指令用于從由基址寄存器所指示的一片連續(xù)存儲器到寄存器列表所指示的多個寄存器之間傳送數(shù)據(jù),該指令的常見用途是將多個寄存器的內(nèi)容入棧或出棧。其中,{類型}為以下幾種情況:
IA 每次傳送后地址加1;
IB 每次傳送前地址加1;
DA 每次傳送后地址減1;
DB 每次傳送前地址減1;
FD 滿遞減堆棧;
ED 空遞減堆棧;
FA 滿遞增堆棧;
EA 空遞增堆棧;
{!}為可選后綴,若選用該后綴,則當(dāng)數(shù)據(jù)傳送完畢之后,將最后的地址寫入基址寄存器,否則基址寄存器的內(nèi)容不改變。
基址寄存器不允許為R15,寄存器列表可以為R0~R15的任意組合。
{∧}為可選后綴,當(dāng)指令為LDM且寄存器列表中包含R15,選用該后綴時表示:除了正常的數(shù)據(jù)傳送之外,還將SPSR復(fù)制到CPSR。同時,該后綴還表示傳入或傳出的是用戶模式下的寄存器,而不是當(dāng)前模式下的寄存器。
指令示例:
STMFD R13!,{R0,R4-R12,LR} ;將寄存器列表中的寄存器(R0,R4到R12,LR)存入堆棧。
LDMFD R13!,{R0,R4-R12,PC} ;將堆棧內(nèi)容恢復(fù)到寄存器(R0,R4到R12,LR)。
?
?
?
?
?
ARM匯編的SWI指令軟中斷 [轉(zhuǎn)貼 2007-05-25 11:21:49]??
?
從下面的一個ARM 匯編小程序要弄懂的以下三個問題:
1).在ARM狀態(tài)轉(zhuǎn)到THUNB狀態(tài)和BX的應(yīng)用
2).匯編的架構(gòu)
3).SWI指令的使用
AREA ADDREG,CODE,READONLY
ENTRY
MAIN
??????????????? ADR r0,ThunbProg 1 ;(為什么要加1呢?因為BX指令跳轉(zhuǎn)到指定的地址執(zhí)行程序 時, 若 (BX{cond} Rm)Rm的位[0]為1,則跳轉(zhuǎn)時自動將CPSR中的標(biāo)志T置位即把目標(biāo) 代碼解釋為 Thunb代碼)
??????????????? BX r0
??????????????? CODE16
ThunbProg
??????????????? mov r2,#2
mov r3,#3
add r2,r2,r3
ADR r0,ARMProg
BX ro
CODE32
ARMProg
mov r4,#4
mov r5,#5
add r4,r4,r5
stop???????? mov r0,#0x18
??????????????? LDR r1,=0x20026
???
??????????????? SWI 0x123456
END
?SWI--軟中斷指令:
?SWI指令用于產(chǎn)生軟中斷,從擁護(hù)模式變換到管理模式,CPSR保存到管理模式的SPSR中.
SWI{cond} immed_24 ;immed_24為軟中斷號(服務(wù)類型)
使用SWI指令時,通常使用以下兩種方法進(jìn)行傳遞參數(shù),SWI 異常中斷處理程序就可以提供相關(guān)的服務(wù),這兩種方法均是用戶軟件協(xié)定.SWI異常中斷處理程序要通過讀取引起軟中斷的SWI指令,以取得24位立即數(shù).
(1) 指令中的24位立即數(shù)指定了用戶請求的服務(wù)類型,參數(shù)通過通用寄存器傳遞.
??????? mov r0,#34 ;設(shè)置子功能號位34
??????? SWI 12 ;調(diào)用12號軟中斷
(2) 指令中的24位立即數(shù)被忽略,用戶請求的服務(wù)類型有寄存器R0的值決定,參數(shù)通過其他的通用寄存器傳遞.
??????? mov r0,#12 ;調(diào)用12號軟中斷
??????? mov r1,#34 ;設(shè)置子功能號位34
??????? SWI 0
在SWI異常中斷處理程序中,取出SWI立即數(shù)的步驟為:首先確定引起軟中斷的SWI指令是ARM指令還是Thunb指令,這可通過對SPSR訪問得到;然后取得該SWI指令的地址,這可通過訪問LR寄存器得到;接著讀出指令,分解出立即數(shù).如如下程序:
?????????? T_bit EQU 0X20
??
??????????? SWI_Handler
??
??????????? STMFD SP!,{R0-R3,R12,LR} ;現(xiàn)場保護(hù)
??
??????????? MRS R0,SPSR ;讀取SPSR
??
??????????? STMFD SP!,{R0} :保存SPSR
??
??????????? TST R0,#T_bit
??
??????????? LDRNEH R0,[LR,#-2] ;若是Thunb指令,讀取指令碼(16位)
BICNE R0,#0XFF00 :取得Thunb指令的8位立即數(shù)
LDREQ R0,[LR,#-4] ;若是ARM指令,讀取指令碼(32位)
BICEQ R0,#0XFF000000 ;取得ARM指令的24位立即數(shù)
....
LDMFD SP!,{R0-R3,R12,PC}^ ;SWI異常中斷返回
?
?
?
基于s3c2410軟中斷服務(wù)的uC/OS-II任務(wù)切換
?
?
?
1.關(guān)于軟中斷指令
? 軟件中斷指令(SWI)可以產(chǎn)生一個軟件中斷異常,這為應(yīng)用程序調(diào)用系統(tǒng)例程提供了一種機制。
語法:
?????? SWI?? {<cond>}? SWI_number
SWI執(zhí)行后的寄存器變化:
lr_svc = SWI指令后面的指令地址
spsr_svc = cpsr
pc = vectors + 0x08
cpsr模式 = SVC
cpsr I = 1(屏蔽IRQ中斷)
?
?? 處理器執(zhí)行SWI指令時,設(shè)置程序計數(shù)器pc為向量表的0x08偏移處,同事強制切換處理器模式到SVC模式,以便操作系統(tǒng)例程可以在特權(quán)模式下被調(diào)用。
?? 每個SWI指令有一個關(guān)聯(lián)的SWI號(number),用于表示一個特定的功能調(diào)用或特性。
【例子】 一個ARM工具箱中用于調(diào)試SWI的例子,是一個SWI號為0x123456的SWI調(diào)用。通常SWI指令是在用戶模式下執(zhí)行的。
SWI執(zhí)行前:
??? cpsr = nzcVqift_USER
??? pc = 0x00008000
??? lr = 0x003fffff?? ;lr = 4
??? r0 = 0x12
?
執(zhí)行指令:
??? 0x00008000?? SWI??? 0x123456
?
SWI執(zhí)行后:
??? cpsr = nzcVqIft_SVC
??? spsr = nzcVqift_USER
??? pc = 0x00000008
??? lr = 0x00008004
??? r0 = 0x12
?? SWI用于調(diào)用操作系統(tǒng)的例程,通常需要傳遞一些參數(shù),這可以通過寄存器來完成。在上面的例子中,r0
用于傳遞參數(shù)0x12,返回值也通過寄存器來傳遞。
?? 處理軟件中斷調(diào)用的代碼段稱為中斷處理程序(SWI Handler)。中斷處理程序通過執(zhí)行指令的地址獲取軟件中斷號,指令地址是從lr計算出來的。
?? SWI號由下式?jīng)Q定:
?? SWI_number = <SWI instruction> AND NOT<0xff000000>
?? 其中SWI instruction就是實際處理器執(zhí)行的32位SWI指令
?
?? SWI指令編碼為:
?? 31 - 28? 27 - 24? 23 - 0
???? cond?? 1 1 1 1? immed24
?? 指令的二進(jìn)制代碼的bit23-bit0是24bit的立即數(shù),即SWI指令的中斷號,通過屏蔽高8bit即可獲得中斷號。lr寄存器保存的是中斷返回指令的地址,所以 [lr - 4] 就是執(zhí)行SWI的執(zhí)行代碼。通過load指令拷貝整個SWI指令到寄存器,使用BIC屏蔽指令的高8位,獲取SWI中斷號。
??
??? ;read the SWI instruction
??? LDR? r10, [lr, #-4]
??? BIC? r10, r10, #0xff000000
?
2. 周立功移植uC/OS-II到s3c2410的軟中斷服務(wù)級的任務(wù)切換
uC/OS-II的任務(wù)調(diào)度函數(shù)
?? uC/OS-II的任務(wù)級的調(diào)度是由函數(shù)OS_Sched( )完成的。
?
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
??? OS_CPU_SR cpu_sr;
#endif
??? INT8U y;
??? OS_ENTER_CRITICAL();
??? if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Sched. only if all ISRs done & not locked */
??????? y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
??????? OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
??????? if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
??????????? OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
??????????? OSCtxSwCtr++; /* Increment context switch counter */
??????????? OS_TASK_SW(); /* Perform a context switch */
??????? }
??? }
??? OS_EXIT_CRITICAL();
}
?
?? 詳細(xì)解釋可以參考《嵌入式實時操作系統(tǒng) uC/OS-II》,os_sched函數(shù)在確定所有就緒任務(wù)的最高優(yōu)先級高于當(dāng)前任務(wù)優(yōu)先級時進(jìn)行任務(wù)切換,通過OS_TASK_SW( )宏來調(diào)用。
?? OS_TASK_SW( )宏實際上定義的是SWI軟中斷指令。見OS_CPU.H文件的代碼:
__swi(0x00) void OS_TASK_SW(void); /* 任務(wù)級任務(wù)切換函數(shù) */
__swi(0x01) void _OSStartHighRdy(void); /* 運行優(yōu)先級最高的任務(wù) */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 關(guān)中斷 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 開中斷 */
__swi(0x40) void *GetOSFunctionAddr(int Index); /* 獲取系統(tǒng)服務(wù)函數(shù)入口 */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 獲取自定義服務(wù)函數(shù)入口 */
__swi(0x42) void OSISRBegin(void); /* 中斷開始處理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判斷中斷是否需要切換 */
__swi(0x80) void ChangeToSYSMode(void); /* 任務(wù)切換到系統(tǒng)模式 */
__swi(0x81) void ChangeToUSRMode(void); /* 任務(wù)切換到用戶模式 */
__swi(0x82) void TaskIsARM(INT8U prio); /* 任務(wù)代碼是ARM代碼 */
__swi(0x83) void TaskIsTHUMB(INT8U prio); /* 任務(wù)代碼是THUMB */
?
__swi(0x00) void OS_TASK_SW(void); 是與ADS相關(guān)的代碼,通過反匯編可以看到,調(diào)用OS_TASK_SW實際上被替換成swi 0x00 軟中斷指令。執(zhí)行此執(zhí)行,pc會跳轉(zhuǎn)到向量表的0x08偏移處。
中斷向量表:(見Startup.s文件)
CODE32
??????? AREA vectors,CODE,READONLY
; 異常向量表
Reset
??????? LDR PC, ResetAddr
??????? LDR PC, UndefinedAddr
??????? LDR PC, SWI_Addr
??????? LDR PC, PrefetchAddr
??????? LDR PC, DataAbortAddr
??????? DCD IRQ_Addr
??????? LDR PC, IRQ_Addr
??????? LDR PC, FIQ_Addr
ResetAddr???? DCD ResetInit
UndefinedAddr DCD Undefined
SWI_Addr????? DCD SoftwareInterrupt
PrefetchAddr? DCD PrefetchAbort
DataAbortAddr DCD DataAbort
Nouse???????? DCD 0
IRQ_Addr????? DCD IRQ_Handler
FIQ_Addr????? DCD FIQ_Handler
?
執(zhí)行SWI 0x00指令后,pc會跳轉(zhuǎn)到SoftwareInterrupt代碼處開始執(zhí)行:
見Os_cpu_a.s文件的SoftwareInterrupt函數(shù):
?
SoftwareInterrupt
??????? LDR SP, StackSvc ; 重新設(shè)置堆棧指針
??????? STMFD {R0-R3, R12, LR}
??????? MOV R1, SP ; R1指向參數(shù)存儲位置
??????? MRS R3, SPSR
??????? TST R3, #T_bit ; 中斷前是否是Thumb狀態(tài)
??????? LDRNEH R0, [LR,#-2] ; 是: 取得Thumb狀態(tài)SWI指令
??????? BICNE R0, R0, #0xff00
??????? LDREQ R0, [LR,#-4] ; 否: 取得arm狀態(tài)SWI指令
??????? BICEQ R0, R0, #0xFF000000??? ; 如上面所述,此處通過屏蔽SWI指令的高8位來獲取SWI號,r0 = SWI號,R1指向參數(shù)存儲位置
??????? CMP R0, #1
??????? LDRLO PC, =OSIntCtxSw? ;為0時跳轉(zhuǎn)到OSIntCtxSwdi地址處
??????? LDREQ PC, =__OSStartHighRdy ; 為1時,跳轉(zhuǎn)到__OSStartHighRdy地址處。SWI 0x01為第一次任務(wù)切換
??????? BL SWI_Exception? ;進(jìn)入中斷號散轉(zhuǎn)函數(shù)
???????
??????? LDMFD {R0-R3, R12, PC}^
???????
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
?
以上就是任務(wù)切換軟中斷級服務(wù)的實現(xiàn)。
?
?
?
?
利用arm 組語的PRE-INDEX 與POST-INDEX ADDRESSING,上課時CODING完成的範(fàn)例--1+2+3+...+10之和 分類:IT技術(shù)分享2008/01/30 17:17於今日 97/01/30 上課時實作的程式範(fàn)例,先貼上程式碼。
因是上課當(dāng)場coding完成的,so沒有加上註解^^
範(fàn)例一:使用 POST-INDEX ADDRESSING實作的code
=======================================
??? AREA? ASM6,CODE,READONLY
??? ENTRY
START
??? LDR R0,=ARR1
??? MOV R1,#0
LOOP
??? LDR R2,[R0],#4
??? ADD R1,R1,R2
??? CMP R2,#0
??? BNE LOOP
STOP
??? LDR R0,=0X18
??? LDR R1,=0X20026
??? SWI 0X123456
??? AREA? ARR,DATA,READWRITE
ARR1? DCD?? 1,2,3,4,5,6,7,8,9,10,0
??? END
範(fàn)例二:使用 PRE-INDEX ADDRESSING實作的code
=======================================
AREA??? ASM8,CODE,READONLY
??? ENTRY
START
??? LDR R0,=ARR1
??? MOV R1,#0
??? MOV R3,#0
LOOP
??? LDR R2,[R0,R1,LSL #2]
??? ADD R1,R1,#1
??? ADD R3,R3,R2
??? CMP R2,#0
??? BNE LOOP
STOP
??? LDR R0,=0X18
??? LDR R1,=0X20026
??? SWI 0X123456
??? AREA? ARR,DATA,READWRITE
ARR1? DCD?? 1,2,3,4,5,6,7,8,9,10,0
??? END
?
http://www.akaedu.org/bbs/redirect.php?tid=231&goto=lastpost
?
常用ARM指令
1、??????? 內(nèi)存訪問指令
基本指令:
LDR:memory -> register (memory包括映射到內(nèi)存空間的非通用寄存器)
STR:register? -> memory
語法:
??????? op{cond }{B}{T}??????????????? Rd ,??????????????? [Rn ]
op{cond }{B}??????????????????????? Rd ,??????????????? [Rn ,??????????????? FlexOffset ]{!}
op{cond }{B}??????????????????????? Rd ,??????????????? label
op{cond }{B}{T}??????????????? Rd ,??????????????? [Rn ],??????? FlexOffset
op:基本指令,如LDR、STR
cond:條件執(zhí)行后綴
B:字節(jié)操作后綴
T:用戶指令后綴
Rd:源寄存器,對于LDR指令,Rd將保存從memory中讀取的數(shù)值;對于STR指令,Rd保存著將寫入memory的數(shù)值
Rn:指針寄存器
FlexOffset:偏移量
例子:
ldr??????????????? r0,??????????????? [r1]??????????????????????????????????????? ;r1作為指針,該指針指向的數(shù)存入r0
str??????????????? r0,??????????????? [r1,??????????????? #4]??????????????? ;r1+4作為指針,r0的值存入該地址
??????? str??????????????? r0,??????????????? [r1,??????????????? #4]!??????????????? ;同上,并且r1 = r1 + 4
??????? ldr??????????????? r1,??????????????? =0x08100000??????????????? ;立即數(shù)0x08100000存到r1
??????? ldr??????????????? r1,??????????????? [r2],??????????????? #4??????????????????????? ;r2+4作為指針,指向的值存入r1,并且r2=r2+4
【label的使用】
addr1??????????????????????????????????????????????????????? ;定義一個名為“addr1”的label,addr1 = 當(dāng)前地址
??????? dcd??????????????? 0??????????????????????????????????????? ;在當(dāng)前地址出定義一個32bit的變量
~ ~ ~
??????? ldr??????????????? r1,??????????????? label1??? ;r1 = addr1,r1即可以作為var1的指針
??????? ldr??????????????? r0,??????????????? [r1]
??????? add??????? r0,??????????????? r0,??????????????? #1
??????? str??????????????? r0,??????????????? [r1]??????????????????????? ;變量var1的值加1
【FlexOffset的使用】
??????? FlexOffset可以是立即數(shù),也可以是寄存器,還可以是簡單的表達(dá)式
???????
2、??????? 多字節(jié)存取指令(常用于堆棧操作)
基本指令:
??????? LDM:memory ――> 多個寄存器
??????? STM:多個寄存器 ――> memory
語法:
??????? op{cond }mode??????? Rn{!},??????????????? reglist {^}
??????? mode:指針更新模式,對應(yīng)于不同類型的棧。最常用的是“FD”模式,相當(dāng)于初始棧指針在高位,壓棧后指針值減小。
??????? Rn:指針寄存器
??????? !:最后的指針值將寫入Rn中
??????? reglist:要操作的寄存器列表,如{r0-r8, r10}
??????? ^ :完成存取操作后從異常模式下返回
例子:
;異常處理程序:
??????? sub??????????????? lr,??????????????? lr,??????????????? #4??????????????????????? ; lr – 4是異常處理完后應(yīng)該返回的地方
;保存r0~r12和lr寄存器的值到堆棧并更新堆棧指針。
??????? stmfd??????????????? sp!,??????????????? {r0-r12, lr}???????
;異常處理
??????????????? ldmfd??????? sp!,??????????????? {r0-r12, pc}^???????? ;從堆棧中恢復(fù)r0~r12,返回地址賦給pc指針,使程序返回到異常發(fā)生前所執(zhí)行的地方,^標(biāo)記用來使CPU退出異常模式,進(jìn)入普通狀態(tài)。
???????????????????????????????????????????????????????????????????????????????????????
3、??????? 算術(shù)運算指令
基本指令:
??????? ADD:加
??????? SUB:減
語法:
??????? op{cond }{S}??????????????? Rd,??????????????? Rn,??????????????? Operand2
??????? S:是否設(shè)置狀態(tài)寄存器(CPSR),如:N(有符號運算結(jié)果得負(fù)數(shù))、Z(結(jié)果得0)、C(運算的進(jìn)位或移位)、V(有符號數(shù)的溢出)等等。
??????? Rd:保存結(jié)果的寄存器
??????? Rn:運算的第一個操作數(shù)
??????? Operand2:運算的第二個操作數(shù),這個操作數(shù)的值有一些限定:如可以是8位立即數(shù)(例:0xa8)或一個8為立即數(shù)的移位(例:0xa800,而0xa801就不符合)。也可以是寄存器,或寄存器的移位(如“r2,? lsl? #4”)。
例子:
??????? add??????????????? r0,??????????????? r1,??????????????? r2??????????????????????????????????????? ; r0 = r1 + r2
??????? adds??????????????? r0,??????????????? r1,??????????????? #0x80??????????????????????????????? ; r0 = r1 + 0x80,并設(shè)置狀態(tài)寄存器
??????? subs??????????????? r0,??????????????? r1,??????????????? #2000??????????????????????????????? ; r0 = r1 – 2000,并設(shè)置狀態(tài)寄存器
4、??????? 邏輯運算指令
基本指令:
??????? AND:與
??????? ORR:或
??????? EOR:異或
??????? BIC:位清0
語法:
??????? op{cond }{S}??????????????? Rd,??????????????? Rn,??????????????? Operand2
??????? 語法類似算術(shù)運算指令
例子:
??????? ands??????????????? r0,??????? r1,??????? #0xff00??????????????????????? ; r0 = r1 and 0xff00,并設(shè)置狀態(tài)寄存器
??????? orr??????????????? r0,??????????????? r1,??????????????? r2??????????????????????????????????????? ; r0 = r1 and r2
??????? bics??????????????? r0,??????????????? r1,??????????????? #0xff00??????????????????????? ; r0 = r1 and ! (0xff00)
??????? ands??????????????? r0,??????????????? r1,??????????????? #0xffff00ff??????????????? ; 錯誤
5、??????? MOV指令
語法:
MOV{cond}{S}??????? Rd,??????????????? Operand2
例子:
??????? mov??????????????? r0,??????????????? #8??????????????????????????????????????????????? ; r0 = 8
??????? mov??????????????? r0,??????????????? r1??????????????????????????????????????????????? ; r0 = r1
不同于LDR、STR指令,該指令可以寄存器間賦值
6、??????? 比較指令
基本指令:
??????? CMP:比較兩個操作數(shù),并設(shè)置狀態(tài)寄存器
語法:
??????? CMP{cond }??????????????? Rn,??????????????? Operand2
例子:
??????? cmp??????????????? r0,??????????????? r1??????????????????????????????????????????????? ; 計算r0 – r1,并設(shè)置狀態(tài)寄存器,由狀態(tài)寄存器可以知r0是否大于、小于或等于r1
??????? cmp??????????????? r0,??????????????? #0??????????????????????????????????????????????? ;
7、??????? 跳轉(zhuǎn)指令
基本指令:
??????? B:跳轉(zhuǎn)
??????? BL:跳轉(zhuǎn)并將下一指令的地址存入lr寄存器
語法:
??????? op{cond}??????????????? label
??????? label:要跳向的地址
例子:
loop1
??????? ~ ~ ~
??????? b??????????????? loop1??????????????????????????????? ; 跳到地址loop1處
??????? bl??????????????? sub1??????????????????????????????? ; 將下一指令地址寫入lr,并跳至sub1
??????? ~ ~ ~
sub1
??????? ~ ~ ~
??????? mov??????? pc,??????? lr??????????????????????? ; 從sub1中返回
【使用本地label(local label)】
??????? 本地label可以在一個程序段內(nèi)多次使用,用數(shù)字作為label的名稱,也可以在數(shù)字后面跟一些字母。引用本地label的語法是:??????? %{F|B}{A|T}n{routname},其中F代表向前搜索本地label,B代表向后搜索,A/T不常使用。
例子
100??????? ; 定義本地label,名稱為“100”
~ ~ ~
100??????????????????????????????????????????????????????????????????????????????? ; 第二次定義本地label,名稱為“100”
??????? ~ ~ ~
??????? b??????????????? %f100??????????????????????????????????????????????? ; 向前跳到最近的“100”處
~ ~ ~
??????? b??????????????? %b100??????????????????????????????????????????????? ; 向后跳至最近的“100”處
100??????????????????????????????????????????????????????????????????????????????? ; 第三次定義本地label 100
8、??????? 條件執(zhí)行
條件:狀態(tài)寄存器中某一或某幾個比特的值代表條件,對應(yīng)不同的條件后綴cond,如:
后綴 (cond)???????? 狀態(tài)寄存器中的標(biāo)記???????? 意義
EQ???????????????????????????????????????????????? Z = 1???????????????????????????????????????????????????????????????????????? 相等
NE???????????????????????????????????????????????? Z = 0??????????????????????????????????????????????????????????????????????? 不相等
GE???????????????????????????????????????????????? N和V相同???????????????????????????????????????????????? >=
LT???????????????????????????????????????????????? N和V不同??????????????????????????????????????????????????????? <
GT???????????????????????????????????????????????? Z??????? = 0, 且N和V相同??????????????????????? >
LE??????????????????????????????????????????????? Z = 1, 或N和V不同???????????????? <=
例子:
??????????????? cmp??????????????? r0,??????????????? r1??????????????? ;比較r0和r1
??????????????? blgt??????????????? sub1??????????????????????? ;如果r0>r1,跳轉(zhuǎn)到sub1,否則不操作
;――――――――――――――――――――
??????????????? ;一段循環(huán)代碼
??????????????? ldr??????????????????????? r2,??????????????? =8??????????????? ;r2 = 8
??????? loop
??????????????? ;這里可以進(jìn)行一些循環(huán)內(nèi)的操作
??????????????? subs??????????????? r2,??????????????? r2,??????????????? #1??????????????? ;r2 = r2 –1,并設(shè)置狀態(tài)位
??????????????? bne??????????????? loop??????????????????????????????????????? ;如果r2不等于0,則繼續(xù)循環(huán)
;――――――――――――――――――――
??????????????? mov??????????????? r0,??????????????? #1??????????????????????????????? ; r0 = 1
??????????????? cmp??????????????? r2,??????????????? #8??????????????????????????????? ;??????? 比較r2和8
??????????????? movlt??????? r0,??????????????? #2??????????????????????????????? ; 如果r2<8,r0 = 2
???????
ARM匯編程序結(jié)構(gòu)
;――――――――――――――――――――
AREA? EX2,? CODE,? READONLY
;AREA指令定義一個程序段,名稱為EX2,屬性為:CODE、READONLY
??????? INCLUDE???????? Common.inc??????? ;包含匯編頭文件
??????? IMPORT???????? sub1??????????????????????????????????????? ;引用外部符號
??????? EXPORT??????????????? prog1??????????????????????? ;向外輸出符號
ENTRY???????????????????????????????????????????????????????? ;ENTRY指令定義程序的開始
start??????????????????????????????????????????????????????????????????????????????? ;此處定義了一個label start
MOV???????? r0,??????? #10???????????????????????????????????????
MOV???????? r1,??????? #3
ADD???????? r0,??????? r0,??????? r1???????????????????????????????? ;r0 =r0 +r1
prog1??????????????????????????????????????????????????????????????????????? ;此處定義了一個label prog1
MOV???????? r0,??????? #0x18????????????????????????????????
LDR???????? r1,??????? =0x20026????????????????????????
SWI???????? 0x123456????????????????????????????????
END???????????????????????????????????????????????????????????????? ;END指令表示程序段的結(jié)束
;――――――――――――――――――――
宏的使用
定義宏:
MACRO??????????????????????????????????????????????? ;宏的起始
{label}??????? macroname??????? para1,para2……
;代碼
MEND??????????????????????????????????????????????????????? ;宏結(jié)束
引用宏:
??????????????? marconame???????? para1,para2……
例子
;定義一個宏,完成兩個寄存器內(nèi)容交換
??????????????? MACRO
??????????????? swap??????????????? $w1,??????????????? $w2,??????????????? $w3
??????????????????????? mov??????????????? $w3,??????????????? $w1
??????????????????????? mov??????????????? $w1,??????????????? $w2
??????????????????????? mov??????????????? $w2,??????????????? $w3
??????????????? MEND
;使用這個宏
ldr??????????????????????? r0,??????????????? =1
ldr??????????????????????? r1,??????????????? =2
swap??????????????? r0,??????????????? r1,??????????????? r2??????????????? ;此處調(diào)用了宏swap,運行完后r0、r1的值交換了
一般可以把宏寫在宏文件(.mac文件)中,在程序里用INCLUDE指令包含宏文件
?
?
?
?
?
最超值的ARM7/ARM9開發(fā)板系列
AVR單片機開發(fā)板與仿真器
?
本章節(jié)主要介紹ARM 處理器的基本程序設(shè)計方法,包含ARM 指令實驗,Thumb 指令實驗和ARM 處理器工作模式實驗。
4.1 ARM 指令實驗
4.1.1 實驗說明
? 實驗?zāi)康?#xff1a;??? 透過實驗掌握ARM 組譯指令的使用方法。
???????? 實驗設(shè)備: 硬件使用PC 主機,軟件使用Embest IDE 2003 整合開發(fā)環(huán)境,Windows 98/2000/NT/XP。
???????? 實驗內(nèi)容:??? 使用簡單ARM 組譯指令,操作寄存器和內(nèi)存區(qū)作互相的數(shù)據(jù)交換。
4.1.2 實驗原理
ARM 處理器共有37個寄存器:31個通用寄存器,包括程序計數(shù)器(PC)。這些寄存器都是32 位的。6個狀態(tài)寄存器。這些寄存器也是32 位的,但是只是使用了其中的12 位。
ARM 通用寄存器
通用寄存器(R0~R15)可分為3 類:
不分組寄存器R0~R7;
分組寄存器R8~R14;
程序計數(shù)器R15。
?
1)不分組寄存器R0~R7
R0~R7 是不分組寄存器。這意味著在所有處理器模式下,它們都存取一樣的32 位寄存器。它們是真正的通用寄存器,沒有架構(gòu)所隱含的特殊用途。
?
2)分組寄存器R8~R14
R8~R14 是分組寄存器。它們存取的物理寄存器取決于當(dāng)前的處理器模式。若要存取特定的物理寄存器而不依賴當(dāng)前的處理器模式,則要使用規(guī)定的各字。
寄存器R8~R12 各有兩組物理寄存器:一組為FIQ 模式,另一組為除了FIQ以外的所有模式。寄存器R8~R12 沒有任何指定的特殊用途。只是使用R8~R14來簡單地處理中斷。寄存器R13,R14 各有6 個分組的物理寄存器。1 個用于用戶模式和系統(tǒng)模式,其它5 個分別用于5 種異常模式。寄存器R13 通常用做堆迭指標(biāo),稱為SP。每種異常模式都有自己的R13。寄存器R14 用作子程序鏈接寄存器,也稱為LR。
?
3) 程序計數(shù)器R15
寄存器R15 用做程序計數(shù)器(PC)。程序狀態(tài)寄存器在所有處理器模式下都可以存取當(dāng)前的程序狀態(tài)寄存器CPSR。CPSR 包含條件碼標(biāo)志位,中斷禁止位,當(dāng)前處理器模式以及其它狀態(tài)和控制信息。每種異常模式都有一個程序狀態(tài)保存寄存器SPSR。當(dāng)例外出現(xiàn)時,SPSR 用于保留CPSR的狀態(tài)。
CPSR 和SPSR 的格式如下:
31
?30
?29
?28
?27
?26???????? 8
?7
?6
?5
?4
?3
?2
?1
?0
?
N
?Z
?C
?V
?Q
?DNM(RAZ)
?I
?F
?T
?M
?M
?M
?M
?M
?
?
條件碼標(biāo)志:N,Z,C,V 大多數(shù)指令可以測試這些條件碼標(biāo)志以決定程序指令如何執(zhí)行
控制位:最低8 位I,F,T 和M 位用做控制位。當(dāng)異常出現(xiàn)時改變控制位。當(dāng)處理器在特權(quán)模式下也可以由軟件改變。
中斷禁止位:I 置1 則禁止IRQ 中斷。F 置1 則禁止FIQ 中斷。T 位:T=0 指示ARM 執(zhí)行。T=1 指示Thumb 執(zhí)行。在這些架構(gòu)系統(tǒng)中,可自由地使用能在ARM 和Thumb 狀態(tài)之間切換的指令。
模式位:M0, M1, M2, M3 和M4 (M[4:0]) 是模式位.這些位
決定處理器的工作模式.如表2-1 所示。
表4-1 ARM 工作模式M[4:0]
M[4:0]
?模式
?可存取的寄存器
?
0b10000
?用戶模式
?PC, R14~R0,CPSR
?
0b10001
?FIQ模式
?PC, R14_fiq~R8_fiq,R7~R0,CPSR,SPSR_fiq
?
0b10010
?IRQ模式
?PC, R14_irq~R8_fiq,R12~R0,CPSR,SPSR_irq
?
0b10011
?管理模式
?PC, R14_svc~R8_svc,R12~R0,CPSR,SPSR_svc
?
0b10111
?中止
?PC, R14_abt~R8_abt,R12~R0,CPSR,SPSR_abt
?
0b11011
?未定義
?PC, R14_und~R8_und,R12~R0,CPSR,SPSR_und
?
0b11111
?系統(tǒng)
?PC, R14~R0,CPSR
?
其它位程序狀態(tài)寄存器的其它位保留,用作以后的擴充。
?
4.1.3. 實驗操作步驟
1. 執(zhí)行ADS1.2開發(fā)環(huán)境,打開實驗系統(tǒng)例程目錄下ARMcode_test 子目錄下的ARMcode.mcp 工程文件。
2. 透過操作菜單欄或使用快捷命令編譯鏈接項目。
3. 選擇Debug 菜單Remote Connect 進(jìn)行連接軟件仿真器,執(zhí)行Download命令下載程序,并打開寄存器窗口。
4. 單步執(zhí)行程序并觀察和記錄寄存器R0-R15 的值變化。
5. 結(jié)合實驗內(nèi)容和相關(guān)數(shù)據(jù),觀察程序執(zhí)行,透過實驗加深理解ARM指令的使用。
?
?
?
4.1.4? 試驗程序代碼
;本程序?qū)?shù)據(jù)區(qū)從數(shù)據(jù)區(qū)SRC復(fù)制到目標(biāo)數(shù)據(jù)區(qū)DST。復(fù)制時,以8個字節(jié)為單位進(jìn)行。對于
;最后所剩不足的8個字節(jié)的數(shù)據(jù),以字為單位進(jìn)行復(fù)制,這時程序跳轉(zhuǎn)到copywords處執(zhí)行。
;在進(jìn)行以8個字為單位的數(shù)據(jù)復(fù)制時,保存了所有的8個工作寄存器。
?
;設(shè)置本段程序的名稱(Block)及屬性
?AREA Block,CODE,READONLY
?
????????????????????
;設(shè)置將要復(fù)制的字?jǐn)?shù)(定義變量num,并賦值為20)
num EQU 20????????????????
?
;程序入口標(biāo)志
?????? ENTRY
start
?
;r0寄存器指向源數(shù)據(jù)區(qū)(SRC 標(biāo)識的地址放入R0)
?????? LDR?????? r0,=src???
?
;r1寄存器指向目標(biāo)數(shù)據(jù)區(qū)(DST 標(biāo)識的地址放入R1)??????
?????? LDR?????? r1,=dst???
??????
;r2指定將要復(fù)制的字?jǐn)?shù)(裝載num 的值到R2)??
?????? MOV????? r2, #num
?????????????
;設(shè)置數(shù)據(jù)棧指針(R13),用于保存工作寄存器數(shù)值(設(shè)定SP堆棧開始地址為0x400)
?????? MOV????? sp, #0x400
?
?????????????
;進(jìn)行以8個字節(jié)為單位的數(shù)據(jù)復(fù)制
blockcopy
?
;需要進(jìn)行的以8個字為單位的復(fù)制次數(shù)( R2 右移3 位后的值放入R3)
?????? MOVS r3,r2, LSR #3????
;對于剩下的不足8個字的數(shù)據(jù),跳轉(zhuǎn)到copywords,以字為單位復(fù)制(判斷是否為0,為0 跳移)
??????
?????? BEQ copywords??????????????????
;保存工作寄存器(把R4 到R11 的值保存到SP 標(biāo)識的堆棧中)
?????? STMFD sp!, {r4-r11}?????????
?
octcopy
;從數(shù)據(jù)區(qū)讀取8個字節(jié)的數(shù)據(jù),放到8個寄存器中,并更新目標(biāo)數(shù)據(jù)區(qū)指針r0(把R0 中的地址標(biāo)識的內(nèi)容順序裝載到R4 到R11 中)
?
?????? LDMIA r0!, {r4-r11}??????????
;將這8個字?jǐn)?shù)據(jù)寫入到目標(biāo)數(shù)據(jù)區(qū),并更新目標(biāo)數(shù)據(jù)區(qū)指針r1(把R4 到R11 的值順序保存到以R1 起始地址的內(nèi)存中)
?????? STMIA r1!, {r4-r11}???????????
?
;將塊的復(fù)制次數(shù)減1 (R3 -1 計數(shù))
?????? SUBS r3, r3, #1??????????????????
?
;循環(huán),直到完成以8個字為單位的塊復(fù)制
?????? BNE octcopy??????????????????????
?
;需要注意的是,LDMIA 或者STMIA 指令執(zhí)行后,R0,R1 的值產(chǎn)生變化,每一次寄存器操作,R0 或者R1 的值會自動增加一個字節(jié)的量,這里操作了8 個寄存器,R0 或者R1 的值也相應(yīng)增加了8 個字節(jié)
?
?
;恢復(fù)工作寄存器值(把剛才保存的SP 堆棧中的值恢復(fù)到R4 到R11 中)
?????? LDMFD sp!, {r4-r11}??
???????
copywords
;剩下不足8個字的數(shù)據(jù)的字?jǐn)?shù)(邏輯與,把R2 前7 位扔掉)
?????? ANDS r2, r2, #7??????????
?
;數(shù)據(jù)復(fù)制完成(判斷是否為0,為0 跳移)
?????? BEQ stop???????????????????????????
?
wordcopy
;從源數(shù)據(jù)區(qū)讀取18個字的數(shù)據(jù),放到r3寄存器中,并更新目標(biāo)數(shù)據(jù)區(qū)指針r0(把R0 表示地址的內(nèi)容的后4 位全部拷貝到R3)
?????? LDR r3, [r0], #4???
?
;將這r3中數(shù)據(jù)寫入到目標(biāo)數(shù)據(jù)區(qū)中,并更新目標(biāo)數(shù)據(jù)區(qū)指針r1 (把R3 的內(nèi)容,放入以R1 為起始地址的4 位內(nèi)存中)??????
?????? STR r3, [r1], #4???????????
??????
;將字?jǐn)?shù)減1;(R2 -1 放回R2)
?????? SUBS r2, r2, #1???????????
?
;循環(huán),直到完成以字為單位的數(shù)據(jù)復(fù)制(判斷是否為0,不為0 跳移,同樣的,這里R0,R1 操作后,R0,R1 會自動加上便宜量)???
?????? BNE wordcopy??????????????????
?????????????????????????????????????????
stop
;從應(yīng)用程序中退出
?????? MOV????? r0,?? #0x18
?????? LDR?????? r1,?? =0x20026
?????? SWI 0x123456
;定義數(shù)據(jù)區(qū)bloackdata
?
?????? AREA???? Bloackdata,???? DATA,??? READWRITE
;定義源數(shù)據(jù)區(qū)src及目標(biāo)數(shù)據(jù)區(qū)dst
src?? DCD?????? 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst?? DCD?????? 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
?
;結(jié)束匯編
?????? END
4.1.5 實驗練習(xí)題
1. 撰寫程序循環(huán)對R4~R11 進(jìn)行8 次累加,R4~R11 起始值為1~8,每次加操作后把R4~R11 的內(nèi)容放入SP 堆棧中,SP 初始設(shè)定為0x800。最后把R4~R11 用LDMFD 指令清空值為0。
2. 對于每一種ARM 的尋址方法,簡短的寫出相對的應(yīng)用程序片段。
4.2 ARM 處理器工作模式實驗
4.2.1. 實驗說明
???????? 實驗?zāi)康?#xff1a;透過實驗掌握ARM 處理器工作模式的切換。
???????? 實驗設(shè)備:軟件需要ADS1.2開發(fā)環(huán)境,Windows 98/2000/NT/XP。
???????? 實驗內(nèi)容:透過ARM 組譯指令,在各種處理器模式下切換并觀察各種模式下緩存器的區(qū)別;掌握ARM 不同模式的進(jìn)入與退出。
4.2.2. 實驗原理
ARM 處理器模式
ARM 架構(gòu)支持下表2-2 所列的7 種處理器模式。
工作模式
?描述
?
用戶模式(User,usr)
?正常程序執(zhí)行的模式
?
快速中斷模式(FIQ,fig)
?用于高速數(shù)據(jù)傳輸和數(shù)據(jù)處理
?
外部中斷模式(IRQ,irq)
?用于通常的中斷處理
?
特權(quán)模式(管理模式)(Supervisor,sve)
?共操作系統(tǒng)使用的一種模式
?
數(shù)據(jù)訪問中止模式(Abort,abt)
?用于虛擬存儲及存儲保護(hù)
?
未定義指令中斷模式(Undefind,und)
?用于支持通過軟件方針的協(xié)處理器
?
系統(tǒng)模式(System,sys)
?用于運行特權(quán)的操作系統(tǒng)任務(wù)
?
?
?
大多數(shù)應(yīng)用程序在用戶模式下執(zhí)行。當(dāng)處理器工作在用戶模式時,正在執(zhí)行的程序不能存取某些被保護(hù)的系統(tǒng)資源,也不能改變模式,除非例外(exception)發(fā)生。這允許適當(dāng)撰寫操作系統(tǒng)來控制系統(tǒng)資源的使用。
除用戶模式外的其它模式稱未特權(quán)模式。它們可以自由的存取系統(tǒng)資源和改變模式。其中的5 種稱為異常模式,即
n???????? FIQ(Fash Interrupt request);
n???????? IRQ(Interrupt ReQuest);
n???????? 管理(Supervisor);
n???????? 中止(Abort) ;
n???????? 未定義(Underfined) 。
當(dāng)特定的異常出現(xiàn)時,進(jìn)入相應(yīng)的模式。每種模式都有某些附加的寄存器,以避免異常出現(xiàn)時用戶模式的狀態(tài)不可靠。
剩下的模式是系統(tǒng)模式。僅ARM 架構(gòu)V4 以及以上的版本有該模式。不能由于任何異常而進(jìn)入該模式。它與用戶模式有完全相同的寄存器。然而它是特權(quán)模式,不受用戶模式的限制。它供需要存取系統(tǒng)資源的操作系統(tǒng)工作使用,單希望避免使用與例外模式有關(guān)的附加寄存器。避免使用附加寄存器保證了當(dāng)任何異常出現(xiàn)時,都不會使工作的狀態(tài)不可靠。
4.2.3. 實驗操作步驟
1.開發(fā)環(huán)境,打開實驗系統(tǒng)例程目錄下ARMMode_test/ARMMode.MCP 項目,并編譯鏈接項目。
3. 單步執(zhí)行程序并觀察和記錄CPSP 和SPSR 緩存器值的變化;并觀察在相應(yīng)模式下執(zhí)行程序后對應(yīng)緩存器值的變化。
4. 結(jié)合實驗內(nèi)容和相關(guān)數(shù)據(jù),觀察程序執(zhí)行,透過實驗加深理解和掌握
4.2.4? 試驗程序代碼
;設(shè)置本段程序的名稱(Block)及屬性
?AREA Block,CODE,READONLY
?
;程序入口標(biāo)志
?????? ENTRY
start
?
?????? B???? Reset_Handler
??????
Undefined_Handler
?????? B Undefined_Handler
?????? B SWI_Handler
??????
Prefetch_Handler
?????? B Prefetch_Handler
??????
Abort_Handler
?????? B Abort_Handler
?????? NOP ;空操作
??????
IRQ_Handler
?????? B IRQ_Handler
??????
FIQ_Handler
?????? B FIQ_Handler
??????
SWI_Handler
?????? mov pc, lr
;前面部分是處理程序,主要處理各種模式的入端口跳移
?
Reset_Handler
?
;into System mode
?????? MRS R0,CPSR????? ;復(fù)制CPSR 到R0
?????? BIC R0,R0,#0x1F ;清除R0 的后5 位
?????? ORR R0,R0,#0x1F?????? ;設(shè)定R0 的最后5 位為11111
?????? MSR?????? CPSR_c,R0??? ;把R0 裝載到CPSR,切換到系統(tǒng)模式
?????? MOV R0, #1???????? ;對系統(tǒng)模式下的R0 賦值,下面的R1~R15 一樣
?????? MOV R1, #2
?????? MOV R2, #3
?????? MOV R3, #4
?????? MOV R4, #5
?????? MOV R5, #6
?????? MOV R6, #7
?????? MOV R7, #8
?????? MOV R8, #9
?????? MOV R9, #10
?????? MOV R10, #11
?????? MOV R11, #12
?????? MOV R12, #13
?????? MOV R13, #14
?????? MOV R14, #15
;into FIQ mode
?????? MRS R0,CPSR
?????? BIC R0,R0,#0x1F
?????? ORR R0,R0,#0x11 ;設(shè)定R0 的最后5 位為10001
?????? MSR CPSR_c,R0 ;把R0 裝載到CPSR,切換到Fiq 模式
?????? MOV R8, #16 ;給Fiq 模式的特有緩存器R8 賦值, 下面的R9~R14 一樣
?????? MOV R9, #17
?????? MOV R10, #18?????
?????? MOV R11, #19
?????? MOV R12, #20
?????? MOV R13, #21
?????? MOV R14, #22
;into SVC mode
?????? MRS R0,CPSR
?????? BIC R0,R0,#0x1F
?????? ORR R0,R0,#0x13 ;設(shè)定R0 的最后5 位為10011
?????? MSR CPSR_c,R0?? ;把R0 裝載到CPSR,切換到Svc 模式
?????? MOV R13, #23????? ;給SVC 模式的特有緩存器R13 賦值, 下面的R14 一樣
?????? MOV R14, #24
;into Abort mode
?????? MRS R0,CPSR
?????? BIC R0,R0,#0x1F
?????? ORR R0,R0,#0x17 ;設(shè)定R0 的最后5 位為10111
?????? MSR CPSR_c,R0 ;把R0 裝載到CPSR,切換到Abort 模式
?????? MOV R13, #25 ;給Abort 模式的特有緩存器R13 賦值, 下面的R14 一樣
?????? MOV R14, #26
;into IRQ mode
?????? MRS R0,CPSR
?????? BIC R0,R0,#0x1F
?????? ORR R0,R0,#0x12 ;設(shè)定R0 的最后5 位為10010
?????? MSR CPSR_c,R0 ;把R0 裝載到CPSR,切換到IRQ 模式
?????? MOV R13, #27 ;給IRQ 模式的特有緩存器R13 賦值, 下面的R14一樣
?????? MOV R14, #28
;into UNDEF mode
?????? MRS R0,CPSR
?????? BIC R0,R0,#0x1F
?????? ORR R0,R0,#0x1b??????? ;設(shè)定R0 的最后5 位為11011
?????? MSR CPSR_c,R0???????????????? ;把R0 裝載到CPSR,切換到UNDEF 模式
?????? MOV R13, #29???????????? ;給UNDEF 模式的特有緩存器R13 賦值, 下面的R14 一樣
?????? MOV R14, #30
?????? B Reset_Handler ;跳移到最開始地方循環(huán)
?????? END
4.2.5. 實驗練習(xí)題
1. 參考例子,把其中系統(tǒng)模式程序更改為使用者模式程序,編譯除錯,觀察執(zhí)行結(jié)果,檢查是否正確,如果有有錯誤分析其原因。(提示:不能從使用者模式直接切換到其它模式,可以先使用SWI 指令切換到管理模式)。
?
?
?
?
?
?
?
?
? 山東大學(xué)嵌入式系統(tǒng)原理與接口技術(shù)課程試卷(A)?????????????????????? 2007——2008 學(xué)年???? 1? 學(xué)期
題號 一 二 三 四 五 六 七 八 九 十 總分
得分??????????????????????
?
?
得分 閱卷人
???
?
?
?
?
單項選擇題(每空2分,共10分)
?
1、對寄存器R1的內(nèi)容乘以4的正確指令是( )。
?? ①LSR R1,#2??????? ②LSL R1,#2??
?? ③MOV R1,R1, LSL #2????????? ④MOV R1,R1, LSR #2
2、下面指令執(zhí)行后,改變R1寄存器內(nèi)容的指令是(???? )。
?? ①TST R1,#2? ②ORR? R1,R1,R1?? ③CMP R1,#2??? ④EOR? R1,R1,R1
3、? MOV?? R1,#0x1000??
???? LDR?? R0,[R1],#4
執(zhí)行上述指令序列后,R1寄存器的值是(??? )。
①0x1000???? ②0x1004???? ③0x0FFC????? ④0x4
4、當(dāng)進(jìn)行數(shù)據(jù)寫操作時,可能Cache未命中,根據(jù)Cache執(zhí)行的操作不同,將Cache分為兩類(?????????? )
①數(shù)據(jù)Cache和指令Cache?? ②統(tǒng)一Cache和獨立Cache? ③寫通Cache和寫回Cache? ④讀操作分配Cache和寫操作分配Cache
5、一個異步傳輸過程:設(shè)每個字符對應(yīng)8個信息位、偶校驗、2個停止位,如果波特率為2400,那么每秒鐘能傳輸?shù)淖畲笞址麛?shù)為(???????????????? )個。
① 200,② 218,③ 240,④ 2400
?得分 閱卷人
???
?
?
?
?
二、填空(共18分)
1、嵌入式處理器可分為以下4類:(???????????????????????????????????????? )。
2、ARM處理器總共有(? )個寄存器,這些寄存器按其在用戶編程中的功能可劃分為:(????????? )和(??????? ),這些寄存器根據(jù)ARM處理器不同工作模式,可將全部寄存器分成( )組,在使用中有(?????????????????????????????????????? )特點。
3、ARM 4種存儲周期的基本類型分別為:(???????????????????????????????????? )。
4、S3C44B0X UART單元發(fā)送器能夠檢測的四種異步串行通信數(shù)據(jù)錯誤為(????????????????????????????????????????????????????????????????????????? )。
得分 閱卷人
???
?
?
簡答題:(每空6分,共30分)
1、從硬件系統(tǒng)來看,嵌入式系統(tǒng)由哪幾部分組成?畫出簡圖。
?
?
?
?
?
?
?
?
?
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開發(fā)要點(一)
—— 嵌入式程序開發(fā)過程
ARM 系列微處理器作為全球16/32位RISC處理器市場的領(lǐng)先者,在許多領(lǐng)
域內(nèi)得到了成功的應(yīng)用.近年來,ARM在國內(nèi)的應(yīng)用也得到了飛速的發(fā)展,越
來越多的公司和工程師在基于ARM的平臺上面開發(fā)自己的產(chǎn)品.
與傳統(tǒng)的4/8位單片機相比,ARM的性能和處理能力當(dāng)然是遙遙領(lǐng)先的,但
與之相應(yīng),ARM的系統(tǒng)設(shè)計復(fù)雜度和難度,較之傳統(tǒng)的設(shè)計方法也大大提升了.
本文旨在通過討論系統(tǒng)程序設(shè)計中的幾個基本方面,來說明基于ARM的嵌入式
系統(tǒng)程序開發(fā)的一些特點,并提出和解決了一些常見的問題.
文章分成幾個相對獨立的章節(jié)刊載.第一部分討論基于ARM的嵌入式程序
開發(fā)和移植過程中的一些基本概念.
1.嵌入式程序開發(fā)過程
不同于通用計算機和工作站上的軟件開發(fā)工程,一個嵌入式程序的開發(fā)過程
具有很多特點和不確定性.其中最重要的一點是軟件跟硬件的緊密耦合特性.
(不帶操作系統(tǒng)支持) (帶操作系統(tǒng)支持)
圖-1:兩類不同的嵌入式系統(tǒng)結(jié)構(gòu)模型
這是兩類簡化的嵌入式系統(tǒng)層次結(jié)構(gòu)圖.由于嵌入式系統(tǒng)的靈活性和多樣
性,上面圖中各個層次之間缺乏統(tǒng)一的標(biāo)準(zhǔn),幾乎每一個獨立的系統(tǒng)都不一樣.
這樣就給上層的軟件設(shè)計人員帶來了極大地困難.第一,在軟件設(shè)計過程中過多
地考慮硬件,給開發(fā)和調(diào)試都帶來了很多不便;第二,如果所有的軟件工作都需
要在硬件平臺就緒之后進(jìn)行,自然就延長了整個的系統(tǒng)開發(fā)周期.這些都是應(yīng)該
從方法上加以改進(jìn)和避免的問題.
為了解決這個問題,工程和設(shè)計人員提出了許多對策.首先在應(yīng)用與驅(qū)動(或
API)這一層接口,可以設(shè)計成相對統(tǒng)一的一些接口函數(shù),這對于具體的某一個
開發(fā)平臺或在某個公司內(nèi)部,是完全做得到的.這樣一來,就大大提高了應(yīng)用層
應(yīng)用(Application)
驅(qū)動/板級支持包
(Driver/BSP)
硬件(Hardware)
應(yīng)用(Application)
硬件抽象層(HAL)
硬件(Hardware)
操作系統(tǒng)(OS)
標(biāo)準(zhǔn)接口函數(shù)(API)
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
軟件設(shè)計的標(biāo)準(zhǔn)化程度,方便了應(yīng)用程序在跨平臺之間的復(fù)用和移植.
對于驅(qū)動/硬件抽象這一層,因為直接驅(qū)動硬件,其標(biāo)準(zhǔn)化變得非常困難甚至
不太可能.但是為了簡化程序的調(diào)試和縮短開發(fā)周期,我們可以在特定的EDA
工具環(huán)境下面進(jìn)行開發(fā),通過后再進(jìn)行移植到硬件平臺的工作.這樣既可以保證
程序邏輯設(shè)計的正確性,同時使得軟件開發(fā)可平行甚至超前于硬件開發(fā)進(jìn)程.
我們把脫離于硬件的嵌入式軟件開發(fā)階段稱之為"PC軟件"的開發(fā),可以
用下面的圖來示意一個嵌入式系統(tǒng)程序的開發(fā)過程.
"PC軟件"開發(fā) 移植,測試 產(chǎn)品發(fā)布
圖-2:嵌入式系統(tǒng)產(chǎn)品的開發(fā)過程
在"PC軟件"開發(fā)階段,可以用軟件仿真,即指令集模擬的方法,來對用
戶程序進(jìn)行驗證.在ARM公司的開發(fā)工具中,ADS 內(nèi)嵌的ARMulator和
RealView 開發(fā)工具中的ISS,都提供了這項功能.在模擬環(huán)境下,用戶可以設(shè)
置ARM處理器的型號,時鐘頻率等,同時還可以配置存儲器訪問接口的時序參
數(shù).程序在模擬環(huán)境下運行,不但能夠進(jìn)行程序的運行流程和邏輯測試,還能夠
統(tǒng)計系統(tǒng)運行的時鐘周期數(shù),存儲器訪問周期數(shù),處理器運行時的流水線狀態(tài)(有
效周期,等待周期,連續(xù)和非連續(xù)訪問周期)等信息.這些寶貴的信息是在硬件
調(diào)試階段都無法取得的,對于程序的性能評估非常有價值.
為了更加完整和真實地模擬一個目標(biāo)系統(tǒng),ARMulator和ISS還提供了一個
開放的API編程環(huán)境.用戶可以用標(biāo)準(zhǔn)C來描述各種各樣的硬件模塊,連同工
具提供的內(nèi)核模塊一起,組成一個完整的"軟"硬件環(huán)境.在這個環(huán)境下面開發(fā)
的軟件,可以更大程度地接近最終的目標(biāo).
利用這種先進(jìn)的EDA工具環(huán)境,極大地方便了程序開發(fā)人員進(jìn)行嵌入式開
發(fā)的工作.當(dāng)完成一個"PC軟件"的開發(fā)之后,只要進(jìn)行正確的移植,一個真
正的嵌入式軟件就開發(fā)成功了.而移植過程是相對比較容易形成一套規(guī)范的流程
的,其中三個最重要的方面是:
考慮硬件對庫函數(shù)的支持
移植
移植
開發(fā)/實驗/
測試平臺
最終產(chǎn)品
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
符合目標(biāo)系統(tǒng)上的存儲器資源分布
應(yīng)用程序運行環(huán)境的初始化
2.開發(fā)工具環(huán)境里面的庫函數(shù)
如果用戶程序里調(diào)用了跟目標(biāo)相關(guān)的一些庫函數(shù),則在應(yīng)用前需要裁剪這些
函數(shù)以適合在目標(biāo)上允許的要求.主要需要考慮以下三類函數(shù):
訪問靜態(tài)數(shù)據(jù)的函數(shù)
訪問目標(biāo)存儲器的函數(shù)
使用semihosting(半主機)機制實現(xiàn)的函數(shù)
這里所指的C庫函數(shù),除了ISO C標(biāo)準(zhǔn)里面定義的函數(shù)以外,還包括由編
譯工具提供的另外一些擴展函數(shù)和編譯輔助函數(shù).
2.1 裁剪訪問靜態(tài)數(shù)據(jù)的函數(shù)
庫函數(shù)里面的靜態(tài)數(shù)據(jù),基本上都是在頭文件里面加以定義的.比如CTYPE
類庫函數(shù),其返回值都是通過預(yù)定義好的CTYPE屬性表來獲得的.比如,想要
改變isalpha() 函數(shù)的缺省判斷,則需要修改對應(yīng)CTYPE屬性表里對字符屬性的
定義.
2.2 裁減訪問目標(biāo)存儲器的函數(shù)
有一類動態(tài)內(nèi)存管理函數(shù),如malloc() 等,其本身是獨立于目標(biāo)系統(tǒng)而運行
的;但是它所使用的存儲器空間需要根據(jù)目標(biāo)來確定.所以malloc() 函數(shù)本身
并不需要裁剪或移植,但那些設(shè)置動態(tài)內(nèi)存區(qū)(地址和空間)的函數(shù)則是跟目標(biāo)
系統(tǒng)的存儲器分布直接相關(guān)的,需要進(jìn)行移植.例如堆棧的初始化函數(shù)
__user_initial_stackheap(),是用來設(shè)置堆(heap)和棧(stack)地址的函數(shù),顯
然針對每一個具體的目標(biāo)平臺,該函數(shù)都需要根據(jù)具體的目標(biāo)存儲器資源進(jìn)行正
確移植.
下面是對示例函數(shù)__user_initial_stackheap() 進(jìn)行移植的一個例子:
__value_in_regs struct __initial_stackheap __user_initial_stackheap(
unsigned R0, unsigned SP, unsigned R2, unsigned SL)
{
struct __initial_stackheap config;
config.heap_base = (unsigned int) 0x11110000;
// config.stack_base = SP; // optional
return config;
}
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
請注意上面的函數(shù)體并不完全遵循標(biāo)準(zhǔn)C的關(guān)鍵字和語法規(guī)范,使用了ARM
公司編譯器(ADS 或RealView Compilation tool) 里的C語言擴展特性.關(guān)于編譯
器特定的C語言擴展,請參考相關(guān)的編譯器說明,這里簡單介紹函數(shù)
__user_initial_stackheap() 的功能,它主要是返回堆和棧的基地址.上面的程序中
只對堆(heap) 的基地址進(jìn)行了設(shè)置(設(shè)成了0x11110000),也就是說用戶把
0x11110000開始的存儲器地址用作了動態(tài)內(nèi)存分配區(qū)(heap區(qū)).具體地址的確
定是要由用戶根據(jù)自己的目標(biāo)系統(tǒng)和應(yīng)用情況來確定的,至少要滿足以下條件:
0x11110000開始的地址空間有效且可寫(是RAM)
該存儲器空間不與其它功能區(qū)沖突(比如代碼區(qū),數(shù)據(jù)區(qū),stack區(qū)等)
因為__user_initial_stackheap() 函數(shù)的全部執(zhí)行效果就是返回一些數(shù)值,所
以只要符合接口的調(diào)用標(biāo)準(zhǔn),直接用匯編來實現(xiàn)看起來更加直觀一些:
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR r0,0x11110000
MOV pc,lr
如果不對這個函數(shù)進(jìn)行移植,編譯過程中將使用缺省的設(shè)置,這個設(shè)置適用
于ARM公司的Integrator系列平臺.
(注意:ARM的編譯/連接工具鏈也提供了繞過庫函數(shù)來設(shè)置運行時存儲器模型
的方法,請參閱ARM公司其他的相關(guān)文檔.)
2.3 裁剪使用semihosting(半主機)機制實現(xiàn)的函數(shù)
庫函數(shù)里有一大部分函數(shù)是涉及到輸入/輸出流設(shè)備的,比如文件操作函數(shù)需
要訪問磁盤I/O,打印函數(shù)需要訪問字符輸出設(shè)備等.在嵌入式調(diào)試環(huán)境下,所
有的標(biāo)準(zhǔn)C庫函數(shù)都是有效且有其缺省行為的,很多目標(biāo)系統(tǒng)硬件不能支持的
操作,都通過調(diào)試工具來完成了.比如printf() 函數(shù),缺省的輸出設(shè)備是調(diào)試器
里面的信息輸出窗口.
但是一個真實的系統(tǒng)是需要脫離調(diào)試工具而獨立運行的,所以在程序的移植
過程當(dāng)中,需先對這些庫函數(shù)的運行機制作一了解.
下圖說明了在ADS下面這類C庫函數(shù)的結(jié)構(gòu).
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
圖-3:C庫函數(shù)實現(xiàn)過程中的層次調(diào)用
如圖中例子所示,函數(shù)printf() 最終是調(diào)用了底層的輸入/輸出函數(shù)
_sys_write() 來實現(xiàn)輸出操作的,而_sys_write() 使用了調(diào)試工具的內(nèi)部機制來把
信息輸出到調(diào)試器.
顯然這樣的函數(shù)調(diào)用過程在一個真實的嵌入式系統(tǒng)里是無法實現(xiàn)的,因為獨
立運行的嵌入式系統(tǒng)將不會有調(diào)試器的參與.如果在最終系統(tǒng)中仍然要保留
printf() 函數(shù),而且在系統(tǒng)硬件中具備正確的輸出設(shè)備(如LCD等),則在移植
過程中,需要把printf() 調(diào)用的輸出設(shè)備進(jìn)行重新定向.
考察printf() 函數(shù)的完整調(diào)用過程:
圖-4:printf() 的調(diào)用過程
單純考慮printf() 的輸出重新定向,可以有三種途徑實現(xiàn):
改寫printf() 本身
改寫 fput()
改寫 _sys_write()
需要注意的是,越底層的函數(shù),被其他上層函數(shù)調(diào)用的可能性越大,改變了
一個底層函數(shù)的實現(xiàn),則所有調(diào)用該函數(shù)的上層函數(shù)的行為都被改變了.
以fputc() 的重新實現(xiàn)為例,下面是改變fputc() 輸出設(shè)備到系統(tǒng)串行通信端
口的實例:
int fputc(int ch, FILE *f)
ANSI C
Input/
output
Error
handling
Stack &
heap setup
Other
Semihosting Support
應(yīng)用程序調(diào)用的
函數(shù),如printf()
設(shè)備驅(qū)動程序級
使用semihosting
機制
如_sys_write()
由調(diào)試系統(tǒng)執(zhí)行
printf() fput() _sys_wite()輸出設(shè)備
其他函數(shù) 其他函數(shù)
C 庫函數(shù)
調(diào)試輔助環(huán)境
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
{ /* e.g. write a character to an UART */
char tempch = ch;
sendchar(&tempch); // UART driver
return ch;
}
代碼中的函數(shù)sendchar() 假定是系統(tǒng)的串口設(shè)備驅(qū)動函數(shù).只要新建函數(shù)
fput() 的接口符合標(biāo)準(zhǔn),經(jīng)過編譯連接后,該函數(shù)實現(xiàn)就覆蓋了原來缺省的函數(shù)
體,所有對該函數(shù)的調(diào)用,其行為都被新實現(xiàn)的函數(shù)所重新定向了.
具體哪些庫函數(shù)是跟目標(biāo)相關(guān)的,這些函數(shù)之間的相互調(diào)用關(guān)系等,請參考
具體的編譯器說明.
3.Semihosting (半主機) 機制
上面提到許多庫函數(shù)在調(diào)試環(huán)境下的實現(xiàn)都調(diào)用了一種叫semihosting的機
制.Semihosting具體來講是指一種讓代碼在ARM 目標(biāo)上運行,但使用運行了
ARM 調(diào)試器的主機上I/O 設(shè)備的方法;也就是讓ARM 目標(biāo)將輸入/ 輸出請求
從應(yīng)用程序代碼傳遞到運行調(diào)試器的主機的一種機制.通常這些輸入/輸出設(shè)備
包括鍵盤,屏幕和磁盤I/O.
半主機由一組已定義的SWI 操作來實現(xiàn).庫函數(shù)調(diào)用相應(yīng)的SWI(軟件中
斷),然后調(diào)試代理程序處理SWI 異常,并提供所需的與主機之間的通訊.
圖-5:Semihosting的實現(xiàn)過程
多數(shù)情況下,半主機SWI 是由庫函數(shù)內(nèi)的代碼調(diào)用的.但是應(yīng)用程序也可
以直接調(diào)用半主機SWI.半主機SWI 的接口函數(shù)是通用的.當(dāng)半主機操作在硬
件仿真器,指令集仿真器,RealMonitor或Angel下執(zhí)行時,不需要進(jìn)行移植處
理.
使用單個SWI 編號請求半主機操作.其它的SWI 編號可供應(yīng)用程序或操
printf()
printf("Hello world! ");
SWI
調(diào)試器
Hello world!
C 庫代碼
應(yīng)用程序代碼
與運行在主機上的調(diào)試器通信
主機屏幕上顯示的文本
目標(biāo)
主機
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
作系統(tǒng)使用.用于半主機的SWI號是:
在ARM 狀態(tài)下:0x123456
在Thumb 狀態(tài)下:0xAB
SWI 編號向調(diào)試代理程序指示該SWI 請求是半主機請求.要辨別具體的操
作類型,用寄存器r0 作為參數(shù)傳遞.r0 傳遞的可用半主機操作編號分配如下:
0x00-0x31:這些編號由ARM 公司使用,分別對應(yīng)32個具體的執(zhí)行函
數(shù).
0x32-0xFF:這些編號由ARM 公司保留,以備將來用作函數(shù)擴展.
0x100-0x1FF:這些編號保留給用戶應(yīng)用程序.但是,如果編寫自己的
SWI 操作,建議直接使用SWI指令和SWI編號,而不要使用半主機
SWI 編號加這些操作類型編號的方法.
0x200-0xFFFFFFFF:這些編號未定義.當(dāng)前未使用并且不推薦使用這
些編號.
半主機SWI使用的軟件中斷編號也可以由用戶自定義,但若是改變了缺省
的軟中斷編號,需要:
更改系統(tǒng)中所有代碼(包括庫代碼)的半主機SWI 調(diào)用
重新配置調(diào)試器對半主機請求的捕捉與相應(yīng)
這樣才能使用新的SWI 編號.
有關(guān)半主機SWI處理函數(shù)實現(xiàn)的更詳細(xì)信息,請參考ARM編譯器的相關(guān)
文檔.
4.應(yīng)用環(huán)境的初始化和根據(jù)目標(biāo)系統(tǒng)資源進(jìn)行的移植
在下一期中介紹應(yīng)用環(huán)境和目標(biāo)系統(tǒng)的初始化.
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開發(fā)要點(二)
—— 系統(tǒng)的初始化過程
基于ARM的芯片多數(shù)為復(fù)雜的片上系統(tǒng)集成(SoC),這種復(fù)雜的系統(tǒng)里多
數(shù)的硬件模塊都是可配置的,需要由軟件來設(shè)置其需要的工作狀態(tài).因此在用戶
的應(yīng)用程序啟動之前,需要有專門的一段啟動代碼來完成對系統(tǒng)的初始化.由于
這類代碼直接面對處理器內(nèi)核和硬件控制器進(jìn)行編程,一般都使用匯編語言.系
統(tǒng)啟動程序所執(zhí)行的操作跟具體的目標(biāo)系統(tǒng)和開發(fā)系統(tǒng)相關(guān),一般通用的內(nèi)容包
括:
中斷向量表
初始化存儲器系統(tǒng)
初始化堆棧
初始化有特殊要求的端口,設(shè)備
初始化應(yīng)用程序執(zhí)行環(huán)境
改變處理器模式
呼叫主應(yīng)用程序
1.中斷向量表
ARM要求中斷向量表必須放置在從0地址開始,連續(xù)8×4字節(jié)的空間內(nèi)
(ARM720T和ARM9/10及以后的ARM處理器也支持從0xFFFF0000開始的高
地址向量表,在本文的其他地方對此不再另加說明).各個中斷矢量在向量表中
的位置分配如下圖:
圖1:中斷向量表
每當(dāng)一個中斷發(fā)生以后,ARM處理器便強制把PC指針置為向量表中對應(yīng)中
Reset 復(fù)位中斷 0x00
Undef 未定義指令中斷 0x04
Software Interrupt 軟件中斷 0x08
Prefetch Abort 指令預(yù)取異常 0x0C
Data Abort 數(shù)據(jù)異常 0x10
(Reserved) 保留 0x14
IRQ 普通外部中斷 0x18
FIQ 外部快速中斷 0x1C
… …
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
斷類型的地址值.因為每個中斷只占據(jù)向量表中1個字的存儲器空間,只能放置
1條ARM指令,所以通常在向量表中放的是跳轉(zhuǎn)指令,使程序能從向量表里跳
轉(zhuǎn)到存儲器里的其他地方,再執(zhí)行中斷處理.
中斷向量表的程序?qū)崿F(xiàn)通常如下所示:
AREA Boot, CODE, READONLY
ENTRY
B Reset_Handler ; Reset_Handler is a label
B Undef_Handler
B SWI_Handler
B PreAbort_Handler
B DataAbort_Handler
B . ; for reserved interrupt, stop here
B IRQ_Handler
B FIQ_Handler
其中的關(guān)鍵字ENTRY是指定編譯器保留這段代碼,因為編譯器可能會認(rèn)為
這是一段冗余代碼而加以優(yōu)化.連接的時候要確保這段代碼被鏈接在0地址處,
并且作為整個程序的入口點(關(guān)鍵字ENTRY并非總是用來設(shè)置程序入口點,所
以通常需要在連接選項里顯式地指定程序入口點).
2.初始化存儲器系統(tǒng)
初始化存儲器系統(tǒng)的編程對象是系統(tǒng)的存儲器控制器.存儲器控制器并不是
ARM內(nèi)核的一部分,不同的系統(tǒng)其設(shè)計不盡相同,所以應(yīng)該針對具體的要求來
完成這部分的程序設(shè)計.一般來說,下面這兩個方面是比較通用的.
2.1.存儲器類型和時序配置
一個復(fù)雜的系統(tǒng)可能存在多種存儲器類型的接口,需要根據(jù)實際的系統(tǒng)設(shè)計
對此加以正確配置.對同一種存儲器類型來說,也因為訪問速度的差異,需要不
同的時序設(shè)置.
通常Flash 和SRAM同屬于靜態(tài)存儲器類型,可以合用同一個存儲器端口;
而DRAM 因為動態(tài)刷新和地址線復(fù)用等特性,通常配有專用的存儲器端口.
存儲器端口的接口時序優(yōu)化是非常重要的,影響到整個系統(tǒng)的性能.因為一
般系統(tǒng)運行的速度瓶頸都存在于存儲器訪問,所以存儲器訪問時序應(yīng)盡可能地
快;但同時又要考慮由此帶來的穩(wěn)定性問題.只有根據(jù)具體選定的芯片,進(jìn)行多
次的測試之后,才能確定最佳的時序配置.
2.2.存儲器地址分布(memory map)
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
有些系統(tǒng)具有非常靈活的存儲器地址分配特性,進(jìn)行存儲器初始化設(shè)計的時
候一定要根據(jù)應(yīng)用程序的具體要求來完成地址分配.
一種典型的情況是啟動ROM的地址重映射(remap).如前面第1節(jié)所述,
當(dāng)一個系統(tǒng)上電后程序?qū)⒆詣訌?地址處開始執(zhí)行,因此在系統(tǒng)的初始狀態(tài),必
須保證在0地址處存在正確的代碼,即要求0地址開始處的存儲器是非易性的
ROM或Flash等.但是因為ROM或Flash的訪問速度相對較慢,每次中斷發(fā)生
后都要從讀取ROM或Flash上面的向量表開始,影響了中斷響應(yīng)速度.因此有
的系統(tǒng)便提供一種靈活的地址重映射方法,可以把0地址重新指向到RAM中去.
在這種地址映射的變化過程當(dāng)中,程序員需要仔細(xì)考慮的是程序的執(zhí)行流程不能
被這種變化所打斷.比如下面這種情況:
圖2:啟動ROM的地址重映射對程序執(zhí)行流程的影響
系統(tǒng)上電后從Flash內(nèi)的0地址開始執(zhí)行,啟動代碼位于地址0x100開始的
空間,當(dāng)執(zhí)行到地址0x200時,完成了一次地址的重映射,把原來0開始的地址
空間由Flash轉(zhuǎn)給了RAM.接下去執(zhí)行的指令(這里為了簡化起見,忽略流水
線指令預(yù)取的模型)將來自從0x204開始的RAM空間.如果預(yù)先沒有對RAM
內(nèi)容進(jìn)行正確的設(shè)置,則里面的數(shù)據(jù)都是隨機的,這樣處理器在執(zhí)行完0x200
地址處的指令之后,再往下取指執(zhí)行就會出錯.解決的方法就是要使RAM在使
用之前準(zhǔn)備好正確的內(nèi)容,包括開頭的向量表部分.
有的系統(tǒng)不具備存儲器地址重映射的功能,所有的空間地址就相對簡單一
些,不需要考慮這方面的問題.
3.初始化堆棧
因為ARM處理器有7種執(zhí)行狀態(tài),每一種狀態(tài)的堆棧指針寄存器(SP)都
Flash
0x0100
(Reset_Handler)
B Reset_Handler
… …
.
.
.
(boot code)
.
.
(remap)
0x0000
0x0200
RAM
0x0200
remap 0x0204
Vector Table
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
是獨立的(System和User模式使用相同的SP寄存器).因此對程序中需要用到
的每一種模式都要給SP寄存器定義一個堆棧地址.方法是改變狀態(tài)寄存器CPSR
內(nèi)的狀態(tài)位,使處理器切換到不同的狀態(tài),然后給SP賦值.注意不要切換到User
模式進(jìn)行User模式的堆棧設(shè)置,因為進(jìn)入User模式后就不能再操作CPSR回到
別的模式了.可能會對接下去的程序執(zhí)行造成影響.
一般堆棧的大小要根據(jù)需要而定,但是要盡可能給堆棧分配快速和高帶寬的
存儲器.堆棧性能的提高對系統(tǒng)整體性能的影響是非常明顯的.
這是一段堆棧初始化的代碼示例,其中只定義了三種模式的SP指針:
MRS R0, CPSR ; CPSR -> R0
BIC R0, R0, #MODEMASK ; 安全起見,屏蔽模式位以外的其它位
ORR R1, R0, #IRQMODE ; 把設(shè)置模式位設(shè)置成需要的模式
MSR CPSR_cxsf, R1 ; 轉(zhuǎn)到IRQ模式
LDR SP, =UndefStack ; 設(shè)置 SP_irq
ORR R1,R0,#FIQMODE
MSR CPSR_cxsf, R1 ; FIQMode
LDR SP, =FIQStack
ORR R1, R0, #SVCMODE
MSR CPSR_cxsf, R1 ; SVCMode
LDR SP, =SVCStack
注意上面的程序中使用到的3個SP寄存器是不同的物理寄存器:SP_irq,
SP_fiq和SP_svc.引用的幾個標(biāo)號假設(shè)已經(jīng)正確定義.
4.初始化有特殊要求的端口,設(shè)備
這要由具體的系統(tǒng)和用戶需求而定.一般的外設(shè)初始化可以在系統(tǒng)初始化之
后進(jìn)行.
比較典型的應(yīng)用是驅(qū)動一些簡單的輸出設(shè)備,如LED等,來指示系統(tǒng)啟動
的進(jìn)程和狀態(tài).
5.初始化應(yīng)用程序執(zhí)行環(huán)境
一個簡單的可執(zhí)行程序的映像結(jié)構(gòu)通常如下:
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
圖3:程序映像的結(jié)構(gòu)
映像一開始總是存儲在ROM/Flash里面的,其RO部分既可以在ROM/Flash
里面執(zhí)行,也可以轉(zhuǎn)移到速度更快的RAM中去;而RW和ZI這兩部分必須是
需要轉(zhuǎn)移到可寫的RAM里去的.所謂應(yīng)用程序執(zhí)行環(huán)境的初始化,就是完成必
要的從ROM到RAM的數(shù)據(jù)傳輸和內(nèi)容清零.
不同的工具鏈會提供一些不同的機制和方法幫助用戶完成這一步操作,主要
是跟鏈接器(Linker)相關(guān).下面是在ARM開發(fā)工具環(huán)境(ADS或RVCT)下,
一種常用存儲器模型的直接實現(xiàn):
LDR r0, =|Image
LDR r1, =|Image RW Base| ; RAM copy address
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 ; ([r0] -> r2) and (r0+4)
STRCC r2, [r1], #4 ; (r2 -> [r1]) and (r1+4)
BCC %B0
1
LDR r1, =|Image ZI Limit| ; Top of zero init segment
MOV r2, #0
2
CMP r3, r1
STRCC r2, [r3], #4 ; (0 -> [r3]) and (r3+4)
BCC %B2
程序?qū)崿F(xiàn)了RW數(shù)據(jù)的拷貝和ZI區(qū)域的清零功能.其中引用到的4個符號
是由連接器(linker)定義輸出的:
|Image RO Limit|:表示RO區(qū)末地址后面的地址,即RW數(shù)據(jù)源的起始地址.
|Image RW Base|:RW區(qū)在RAM里的執(zhí)行區(qū)起始地址,也就是編譯選項
RW_Base指定的地址;程序里是RW數(shù)據(jù)拷貝的目標(biāo)地址.
ZI (Zero initialized R/W Data)
RW (R/W Data)
RO (Code + RO Data)
編譯結(jié)果
定義時帶初始值的全局變量
只定義了變量名的全局變量
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
|Image ZI Base|:ZI區(qū)在RAM里面的起始地址.
|Image ZI Limit|:ZI區(qū)在RAM里面的結(jié)束地址后面的一個地址.
程序先把ROM里 |Image RO Limit| 開始的RW初始數(shù)據(jù)拷貝到RAM里
|Image RW Base| 開始的地址,當(dāng)RAM這邊的目標(biāo)地址到達(dá)
|Image ZI Base| 后就表示RW區(qū)的結(jié)束和ZI區(qū)的開始,接下去就對這片ZI
區(qū)進(jìn)行清零操作,直到遇到結(jié)束地址 |Image ZI Limit|.
6.改變處理器模式
ARM處理器(V4架構(gòu)以后的版本)一共有7種執(zhí)行模式:
User: 用戶模式
FIQ: 快速中斷響應(yīng)模式
IRQ: 一般中斷響應(yīng)模式
Supervisor:超級模式
Abort: 出錯處理模式
Undef: 未定義模式
System: 系統(tǒng)模式
除用戶模式以外,其他6種模式都是特權(quán)模式.因為在初始化過程中許多操
作需要在特權(quán)模式下才能進(jìn)行(比如CPSR的修改),所以要特別注意不能過早
地進(jìn)入用戶模式.一般地,在初始化過程中會經(jīng)歷以下一些模式變化:
圖4:處理器模式變換過程
在最后階段才把模式轉(zhuǎn)換到最終應(yīng)用程序運行所需的模式,一般是用戶模
式.
內(nèi)核級的中斷使能(CPSR的I,F位狀態(tài))也可以考慮在這一步進(jìn)行.如果
系統(tǒng)中另外存在一個專門的中斷控制器(多數(shù)情況下是這樣的),這么做總是安
全的,否則就需要考慮過早地打開中斷可能帶來的問題,比如在系統(tǒng)初始化完成
之前就觸發(fā)了有效中斷,導(dǎo)致系統(tǒng)的死機.
7.呼叫主應(yīng)用程序
當(dāng)所有的系統(tǒng)初始化工作完成之后,就需要把程序流程轉(zhuǎn)入主應(yīng)用程序.最
簡單的一種情況是:
復(fù)位后的缺省模式 注意不要進(jìn)入用戶模式 用戶選擇
(堆棧初始化階段)
超級模式
(Supervisor)
多種特權(quán)模式
變化
設(shè)置成用戶程
序運行模式
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
IMPORT main ; get the label main if main() is defined in other files
B man ; jump to main()
直接從啟動代碼跳入應(yīng)用程序主函數(shù)入口,主函數(shù)名字可由用戶自己定義.
在ARM ADS環(huán)境中,還另外提供了一套系統(tǒng)級的呼叫機制.
IMPORT __main
B __main
__main()
圖5:在應(yīng)用程序主函數(shù)之前插入__main
__main() 是編譯系統(tǒng)提供的一個函數(shù),負(fù)責(zé)完成庫函數(shù)的初始化和第5節(jié)中
所描述的功能,最后自動跳向main() 函數(shù).這種情況下用戶程序的主函數(shù)名字
必須得是main.
用戶可以根據(jù)需要選擇是否使用__main().如果想讓系統(tǒng)自動完成系統(tǒng)調(diào)用
(如庫函數(shù))的初始化過程,可以直接使用__main();如果所有的初始化步驟都
是由用戶自己顯式地完成,則可以跳過__main().
當(dāng)然,使用__main() 的時候,可能會涉及到一些庫函數(shù)的移植和重定向問
題.在__main() 里面的程序執(zhí)行流程如下圖所示:
圖6:有系統(tǒng)調(diào)用參與的程序執(zhí)行流程
關(guān)于在__main() 里面調(diào)用到的庫函數(shù)說明,可以參閱相關(guān)的編譯器文檔,
庫函數(shù)移植和重定向的方法,可以參考上一期文章里面的相關(guān)章節(jié).
Image Entry Point
__main
·copy code and data
·zero initialize
__rt_entry
·initialize library functions
·call top-level constructors
(C++)
·Exit from application
·
Reset handler
·user boot code
User application
·main
__User_initial_stackheap
·set up stack & heap
啟動代碼 應(yīng)用程序初始化用戶應(yīng)用程序main()
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開發(fā)要點(三)
—— 如何滿足嵌入式系統(tǒng)的靈活需求
因為嵌入式應(yīng)用領(lǐng)域的多樣性,每一個系統(tǒng)都具有各自的特點.在進(jìn)行系統(tǒng)
程序設(shè)計的時候,一定要進(jìn)行具體分析,充分利用這些特點,揚長避短.
結(jié)合ARM架構(gòu)本身的一些特點,在這里討論幾個常見的要點.
1.ARM還是Thumb
在討論ARM還是Thumb之前,先說明ARM內(nèi)核型號和ARM結(jié)構(gòu)體系之
間的區(qū)別和聯(lián)系.
圖-1 ARM結(jié)構(gòu)體系和處理器家族的演變發(fā)展
如圖-1所示,ARM的結(jié)構(gòu)體系主要從版本4開始,發(fā)展到了現(xiàn)在的版本6,
結(jié)構(gòu)體系的變化,對程序員而言最直接的影響就是指令集的變化.結(jié)構(gòu)體系的演
變意味著指令集的不斷擴展,值得慶幸的是ARM結(jié)構(gòu)體系的發(fā)展一直保持了向
上兼容,不會造成老版本程序在新結(jié)構(gòu)體系上的不兼容.
在圖中的橫坐標(biāo)上,顯示了每一個體系結(jié)構(gòu)上都含有眾多的處理器型號,這
是在同一體系結(jié)構(gòu)下根據(jù)硬件配置和存儲器系統(tǒng)的不同而作的進(jìn)一步細(xì)分.需要
注意的是通常我們用來區(qū)分ARM處理器家族的ARM7,ARM9或ARM10,可
能跨越不同的體系結(jié)構(gòu).
在ARM的體系結(jié)構(gòu)版本4與5中,還可以再細(xì)分出幾個小的擴展版本:V4T,
V5TE和V5TEJ,其區(qū)別如圖-2中所示,這些后綴名也反映在各自擁有的處理器
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
型號上面,可以進(jìn)行直觀的分辨.V6結(jié)構(gòu)體系因為包含了以前版本的所有特性,
所以不需要再進(jìn)行分類.
圖-2 結(jié)構(gòu)體系特征
上面介紹了整個ARM處理器家族的分布,主要是說明在一個特定的平臺上
編寫程序的時候,一定要先弄清楚目標(biāo)的特性和一些細(xì)微的差別,特別是需要具
體優(yōu)化特征的時候.
從ARM體系結(jié)構(gòu)V4T以后,最大的變化是增加了一套16位的指令集——
Thumb.到底在一個具體應(yīng)用中要否采用Thumb呢 首先我們來分析一下ARM
和Thumb各自的特點和優(yōu)勢.先看下面一張性能分析圖:
圖-3 ARM和Thumb指令集的比較
圖中的縱坐標(biāo)是測試向量Dhrystone在20MHz頻率下運行1秒鐘的結(jié)果,其
值越大表明性能越好;橫坐標(biāo)是系統(tǒng)存儲器系統(tǒng)的數(shù)據(jù)總線寬度.結(jié)果表明:
(a) 當(dāng)系統(tǒng)具有32位的數(shù)據(jù)總線寬度時,ARM比Thumb有更好的性能表現(xiàn).
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
(b) 當(dāng)系統(tǒng)的數(shù)據(jù)總線寬度小于32位時,Thumb比ARM的性能更好.
由此可見,并不是32位的ARM指令集性能一定強于16位的Thumb指令集,
要具體情況具體分析.考察個中的原因,其實不難發(fā)現(xiàn),因為當(dāng)在一個16位存
儲器系統(tǒng)里面取1條32位指令的時候,需要耗費2個存儲器訪問周期;比之32
位的系統(tǒng),其速度正好大概下降一半左右.而16位指令在32位存儲器系統(tǒng)或
16位存儲器系統(tǒng)里的表現(xiàn)基本相同.正是存儲器造成的系統(tǒng)瓶頸導(dǎo)致了這個有
趣的差別.
除了在窄帶寬系統(tǒng)里面的性能優(yōu)勢外,Thumb指令的另外一個好處是代碼尺
寸.同樣一段C代碼,用Thumb指令編譯的結(jié)果,其長度大約只占ARM編譯
結(jié)果的65%左右,可以明顯地節(jié)省存儲器空間.在大多數(shù)情況下,緊湊的代碼和
窄帶寬的存儲器系統(tǒng),還會帶來功耗上的優(yōu)勢.
當(dāng)然,如果在32位的系統(tǒng)上面,并且對系統(tǒng)性能要求很高的情況下,ARM
是一個更好的選擇.畢竟在這種情況下,只有32位的指令集才能完全發(fā)揮32
位處理器的優(yōu)勢來.
因此,選擇ARM還是Thumb,需要從存儲器開銷和性能要求兩方面加以權(quán)
衡考慮.
2.堆棧的分配
在圖-3中,橫坐標(biāo)上還有一種情況,就是16位的存儲器寬度,但是堆棧空
間是32位的.這種情況下無論ARM還是Thumb,其性能表現(xiàn)都比單純的16位
存儲器系統(tǒng)情況下要好.這是因為ARM和Thumb其指令集雖然分32位和16
位,但是堆棧全部是采用32位的.因此在16位堆棧和32位堆棧的不同環(huán)境下,
其性能當(dāng)然都會相差很多.這種差別還跟具體的應(yīng)用程序密切相關(guān),如果一個程
序堆棧的使用頻率相當(dāng)高,則這種性能差異很大;反之則要小一些.
在基于ARM的系統(tǒng)中,堆棧不僅僅被用來進(jìn)行諸如函數(shù)調(diào)用,中斷響應(yīng)等
時候的現(xiàn)場保護(hù),還是程序局部變量和函數(shù)參數(shù)傳遞(如果大于4個)的存儲空
間.所以出于系統(tǒng)整體性能考慮,要給堆棧分配相對訪問速度最快,數(shù)據(jù)寬度最
大的存儲器空間.
一個嵌入式系統(tǒng)通常存在多種多樣的存儲器類型.設(shè)計的時候一定要先清楚
每一種存儲器的訪問速度,地址分配和數(shù)據(jù)線寬度.然后根據(jù)不同程序和目標(biāo)模
塊對存儲器的不同要求進(jìn)行合理分配,以期達(dá)到最佳配置狀態(tài).
3.ROM還是RAM在0地址處
顯然當(dāng)系統(tǒng)剛啟動的時候,0地址處肯定是某種類型的ROM,里面存儲了系
統(tǒng)的啟動代碼.但是很多靈活的系統(tǒng)設(shè)計中,0地址處的存儲器類型是可映射的.
也就是說,可以通過軟件的方法,把別的存儲器(主要是快速的RAM)分配以
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
0起始的地址.
這種做法的最主要目的之一是提高系統(tǒng)對中斷的反應(yīng)速度.因為每一個中斷
發(fā)生的時候,ARM都需要從0地址處的中斷向量表開始其中斷響應(yīng)流程,顯然
把中斷向量表放在RAM里,比放在ROM里有更快的訪問速度.因此,如果系
統(tǒng)提供了這一類的地址重映射功能,軟件設(shè)計者一定要加以利用.
下面是一個典型的經(jīng)過0地址重映射之后的存儲空間分布圖,注意盡可能把
速度要求最高的部分放置在系統(tǒng)里面訪問速度最快,帶寬最寬的RAM里面.
圖-4 系統(tǒng)存儲器分布的實例
4.存儲器地址重映射(memory remap)
存儲器地址重映射是當(dāng)前很多先進(jìn)控制器所具有的功能.在上一節(jié)中已經(jīng)提
到了0地址處存儲器重映射的例子,簡而言之,地址重映射就是可以通過軟件配
置來改變一塊存儲器物理地址的一種機制或方法.
當(dāng)一段程序?qū)\行自己的存儲器進(jìn)行重映射的時候,需要特別注意保證程序
執(zhí)行流程在重映射前后的承接關(guān)系.下面是一種典型的存儲器地址重映射情況:
Peripherals
RO
Reset Handler
Heap
RW/ZI
Stack
Exception Handlers
Vector Table
Fast32-bit RAM
16-bit RAM
Flash
0x0000 0000
0x0000 4000
0x0001 0000
0x0001 8000
0x2400 0000
0x2800 0000
0x4000 0000
可以在ROM 里運行的代碼
外設(shè)寄存器
變量區(qū)和動態(tài)內(nèi)存分配區(qū)
需要快速響應(yīng)的代碼和數(shù)據(jù)
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
圖-5 存儲器重映射舉例1
系統(tǒng)上電后的缺省狀態(tài)是0地址上放有ROM,這塊ROM有兩個地址:從0
起始和從0x10000起始,里面存儲了初始化代碼.當(dāng)進(jìn)行地址remap以后,從0
起始的地址被定向到了RAM上,ROM則只保留有唯一的從0x10000起始的地
址了.
如果存儲在ROM 里的Reset_Handler一直在0 - 0x4000的地址上運行,則
當(dāng)執(zhí)行完remap以后,下面的指令將從RAM里預(yù)取,必然會導(dǎo)致程序執(zhí)行流程
的中斷.根據(jù)系統(tǒng)特點,可以用下面的辦法來解決這個問題:
(1) 上電后系統(tǒng)從0地址開始自動執(zhí)行,設(shè)計跳轉(zhuǎn)指令在remap發(fā)生前使PC
指針指向0x10000開始的ROM地址中去,因為不同地址指向的是同一塊
ROM,所以程序能夠順利執(zhí)行.
(2) 這時候0 - 0x4000的地址空間空閑,不被程序引用,執(zhí)行remap后把RAM
引進(jìn).因為程序一直在0x10000起始的ROM空間里運行,remap對運行
流程沒有任何影響.
(3) 通過在ROM里運行的程序,對RAM進(jìn)行相應(yīng)的代碼和數(shù)據(jù)拷貝,完成
應(yīng)用程序運行的初始化.
下面是一段實現(xiàn)上述步驟的例程:
-------------------------------------------------------------------------------------------------------
ENTRY
;啟動時,從0開始,設(shè)法跳轉(zhuǎn)到"真"的ROM地址(0x10000開始的空間里)
LDR pc, =start
;insert vector table here
…
Start ;Begin of Reset_Handler
; 進(jìn)行remap設(shè)置
remap
0x10000
0x4000
=
0x4000
0x0000
Reset Handler
Vectors
0x4000
0x0000
RAMROM
0x10000
0x10400
ROM ROM
0x10400
Vectors
Reset Handler
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
LDR r1, =Ctrl_reg ;假定控制remap的寄存器
LDR r0, [r1]
ORR r0, r0, #Remap_bit ;假定對控制寄存器進(jìn)行remap設(shè)置
STR r0, [r1]
;接下去可以進(jìn)行從ROM到RAM的代碼和數(shù)據(jù)拷貝
-------------------------------------------------------------------------------------------------------
除此之外,還有另外一種常見的remap方式,如下圖:
圖-6 存儲器重映射舉例2
原來RAM和ROM各有自己的地址,進(jìn)行重映射以后RAM和ROM的地址
都發(fā)生了變化,這種情況下,可以采用以下的方案:
(1) 上電后,從0地址的ROM開始往下執(zhí)行.
(2) 根據(jù)映射前的地址,對RAM進(jìn)行必要的代碼和數(shù)據(jù)拷貝.
(3) 拷貝完成后,進(jìn)行remap操作.
(4) 因為RAM在remap前準(zhǔn)備好了內(nèi)容,使得PC指針能繼續(xù)在RAM里取
到正確的指令.
不同的系統(tǒng)可能會有多種靈活的remap方案,根據(jù)上面提到的兩個例子,可
以總結(jié)出最根本的考慮是:要使程序指針在remap以后能繼續(xù)往下得到正確的指
令.
5. 根據(jù)目標(biāo)存儲器系統(tǒng)分散加載映像(scatterloading)
Scatterloading文件是ARM的工具鏈里面的一個特性,作為程序編譯過程中
給連接器使用的一個參數(shù),用來指定最終生成的目標(biāo)映像文件運行時的分布狀
態(tài).如果用戶程序映像只是如圖7所示的最簡狀態(tài),所有的可執(zhí)行代碼都集合放
置在一起,那么可以不使用Scatterloading文件,直接用連接器的命令行選項就
remap
0x20000
0x4000
=
0x4000
0x0000
Reset Handler
Vectors
0x4000
0x0000
RAMROM
0x10000
0x10400
RAM ROM
0x20400
Vectors
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
能夠完成設(shè)置:
RO = 0x00000:表示映像的第一條指令開始地址;
RW = 0x10000:表示變量區(qū)的起始地址,變量區(qū)一定要位于RAM區(qū).
圖-7 簡單的映像分布舉例
但是一個復(fù)雜的系統(tǒng)可能會把映像分割成幾個部分.如圖8,系統(tǒng)中存在多
種類型的存儲器,不能的代碼部分根據(jù)執(zhí)行性能優(yōu)化的考慮分布與不同的地方.
圖-8 復(fù)雜的映像分布舉例
這時候不能通過簡單的RO,RW參數(shù)來完成實現(xiàn)上述配置,就要用到
scatterloading文件了.在scatterloading文件里,可以給編譯出來的各個目標(biāo)模塊
RO
RW
ZI
Stack
Heap
RAM
Flash 代碼區(qū)
變量區(qū)
0x00000
0x10000
Exception Handler
RO
Reset Handler
Heap
RW & ZI
Stack
Vector table
0x0000
0x4000
0x10000
0x18000
0x20000
0x28000
32-bit fast RAM
16-bit RAM
Flash
性能要求最苛刻的部分
變量區(qū)和動態(tài)內(nèi)存分配區(qū)
普通程序區(qū)
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
指定運行地址,下面的例子是針對圖8的.
FLASH 0x20000 0x8000
{
FLASH 0x20000 0x8000
{
init.o (Init, +First)
* (+RO)
}
32bitRAM 0x0000
{
vectors.o (Vect, +First)
handlers.o (+RO)
}
STACK 0x1000 UNINIT
{
stackheap.o (stack)
}
:
:
16bitRAM 0x10000
{
* (+RW,+ZI)
}
HEAP 0x15000 UNINIT
{
stackheap.o (heap)
}
}
關(guān)于scatterloading文件的詳細(xì)語法,請參閱ARM公司的相關(guān)手冊.
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開發(fā)要點(四)
—— 異常處理機制的設(shè)計
異常或中斷是用戶程序中最基本的一種執(zhí)行流程或形態(tài),這部分對ARM架
構(gòu)下異常處理程序的編寫作一個全面的介紹.
ARM一共有7種類型的異常,按優(yōu)先級從高到低排列如下:
Reset
Data Abort
FIQ
IRQ
Prefetch Abort
SWI
Undefined instruction
請注意在ARM的文檔中,使用術(shù)語exception 來描述異常.Exception主要
是從處理器被動接受異常的角度出發(fā)描述,而interrupt帶有向處理器主動申請的
色彩.在本文中,對"異常"和"中斷"不作嚴(yán)格區(qū)分,都是指請求處理器打斷
正常的程序執(zhí)行流程,進(jìn)入特定程序循環(huán)的一種機制.
1.異常響應(yīng)流程
如以前介紹異常向量表時所提到過的,每一個異常發(fā)生時,總是從異常向量
表開始起跳的,最簡單的一種情況是:
圖-1 異常向量表
B
B
(Reserved)
B B
B
B
B
B
0x1C
0x18
0x14
0x10
0x0C
0x08
0x04
0x00
FIQ_Handler()
IRQ_Handler()
DataAbt_Handler()
PreAbt_Handler()
SWI_Handler()
Undef_Handler()
Reset_Handler()
中斷處理函數(shù)
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
向量表里面的每一條指令直接跳向?qū)?yīng)的異常處理函數(shù).其中FIQ_Handler()
可以直接從地址0x1C處開始,省下一條跳轉(zhuǎn)指令.
但是當(dāng)執(zhí)行跳轉(zhuǎn)的時候有2個問題需要討論:跳轉(zhuǎn)范圍和異常分支.
1.1 跳轉(zhuǎn)范圍
我們知道ARM的跳轉(zhuǎn)指令(B)是有范圍限制的(±32MB),但很多情況
下不能保證所有的異常處理函數(shù)都定位在向量表的32MB范圍內(nèi),需要大于
32MB的長跳轉(zhuǎn),而且因為向量表空間的限制只能由一條指令完成.這可以通過
下面二種方法實現(xiàn).
(a) MOV PC, #imme_value
把目標(biāo)地址直接賦給PC寄存器.
但是這條指令受格式限制并不能處理任意立即數(shù),只有當(dāng)這個立即數(shù)能夠
表示為一個8-bit數(shù)值通過循環(huán)右移偶數(shù)位而得到,才是合法的.例如:
MOV PC, #0x30000000 是合法的,因為0x300000000可以通過0x03循
環(huán)右移4位而得到.
而 MOV PC, #30003000 就是非法指令.
(b) LDR PC, [PC+offset]
把目標(biāo)地址先存儲在某一個合適的地址空間,然后把這個存儲器單元上的32
位數(shù)據(jù)傳送給PC來實現(xiàn)跳轉(zhuǎn).
這種方法對目標(biāo)地址值沒有要求,可以是任意有效地址.但是存儲目標(biāo)地址
的存儲器單元必須在當(dāng)前指令的±4KB空間范圍內(nèi).
注意在計算指令中引用的offset數(shù)值的時候,要考慮處理器流水線中指令預(yù)
取對PC值的影響,以圖-2的情況為例:
offset = address location - vector address - pipeline effect
= 0xFFC - 0x4 - 0x8
= 0xFF0
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
圖-2 利Literal pool實現(xiàn)跳轉(zhuǎn)
1.2 異常分支
ARM內(nèi)核只有二個外部中斷輸入信號nFIQ和nIRQ,但對于一個系統(tǒng)來說,
中斷源可能多達(dá)幾十個.為此,在系統(tǒng)集成的時候,一般都會有一個異常控制器
來處理異常信號.
圖-3 中斷系統(tǒng)
這時候,用戶程序可能存在多個IRQ/FIQ的中斷處理函數(shù),為了從向量表
開始的跳轉(zhuǎn)最終能找到正確的處理函數(shù)入口,需要設(shè)計一套處理機制和方法.
圖-4 中斷分支
LDR PC, [PC, 0xFF0]
0x30003000
Undef_Handler()
0x00
0x04
0xFFC
32MB
0x30003000
n
1
2 多
中斷源
中斷
控制器
ARM
內(nèi)核
nIRQ
nFIQ
外設(shè)通信
配置/獲取信息
IRQ 0x14
IRQ_Handler_1()
IRQ_Handler_2()
...
...
IRQ_Handler_n()
…
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
(a) 硬件處理
有的系統(tǒng)在ARM的異常向量表之外,又增加了一張由中斷控制器控制的特
殊向量表.當(dāng)由外設(shè)觸發(fā)一個中斷以后,PC能夠自動跳到這張?zhí)厥庀蛄勘碇腥?
特殊向量表中的每個向量空間對應(yīng)一個具體的中斷源.
舉例來說,下面的系統(tǒng)一共有20個外設(shè)中斷源,特殊向量表被直接放置在
普通向量表后面.
圖-5 額外的硬件異常向量表
當(dāng)某個外部中斷觸發(fā)之后,首先觸發(fā)ARM的內(nèi)核異常,中斷控制器檢測到
ARM的這種狀態(tài)變化,再通過識別具體的中斷源,使PC自動跳轉(zhuǎn)到特殊向量
表中的對應(yīng)地址,從而開始一次異常響應(yīng).需要檢查具體的芯片說明,是否支持
這類特性.
(b) 軟件處理
多數(shù)情況下是用軟件來處理異常分支.因為軟件可以通過讀取中斷控制器來
獲得中斷源的詳細(xì)信息.
圖-6 軟件控制中斷分支
Int_20
.
.
.
Int_2
Int_1
FIQ
IRQ
.
.
Reset
0x70
0x6C
.
.
.
0x24
0x20
0x1C
0x18
.
.
0x00
Int_20_Handler()
…
…
…
Int_2_Handler()
Int_1_Handler()
(獲取狀態(tài)信息)
IRQ
… …
…
中斷控制器
IRQ_Handler:
Switch(int_source)
{
case 1:
case 2:
…
}
Int_1_Handler()
Int_2_Handler()
… …
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
因為軟件設(shè)計的靈活性,用戶可以設(shè)計出比上圖更好的流程控制方法來.下
面是一個例子:
圖-7 靈活的軟件分支設(shè)計
Int_vector_table是用戶自己開辟的一塊存儲器空間,里面按次序存放異常處
理函數(shù)的地址.IRQ_Handler()從中斷控制器獲取中斷源信息,然后再從
Int_verctor_table中的對應(yīng)地址單元得到異常處理函數(shù)的入口地址,完成一次異
常響應(yīng)的跳轉(zhuǎn).這種方法的好處是用戶程序在運行過程中,能夠很方便地動態(tài)改
變異常服務(wù)內(nèi)容.
2.異常處理函數(shù)的設(shè)計
2.1 異常發(fā)生時處理器的動作
當(dāng)任何一個異常發(fā)生并得到響應(yīng)時,ARM內(nèi)核自動完成以下動作:
拷貝 CPSR 到 SPSR_
Address of Int_n_Handler()
.
.
.
Address of Int_2_Handler()
Address of Int_1_Handler()
(獲取狀態(tài)信息)
IRQ
… …
…
中斷控制器
IRQ_Handler():
Switch(int_source)
{
case 1:
case 2:
…
case n:
}
Int_1_Handler()
Int_2_Handler()
… …
Int_vector_table
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
設(shè)置適當(dāng)?shù)?CPSR 位:
改變處理器狀態(tài)進(jìn)入 ARM 狀態(tài)
改變處理器模式進(jìn)入相應(yīng)的異常模式
設(shè)置中斷禁止位禁止相應(yīng)中斷
更新 LR_
設(shè)置 PC 到相應(yīng)的異常向量
注意當(dāng)響應(yīng)異常后,不管異常發(fā)生在ARM還是Thumb狀態(tài)下,處理器都將
自動進(jìn)入ARM狀態(tài).另一個需要注意的地方是中斷使能被自動關(guān)閉,也就是說
缺省情況下中斷是不可重入的.單純的把中斷使能位打開接受重入的中斷會帶來
新的問題,在第3部分中對此會有詳細(xì)介紹.
除這些自動完成的動作之外,如果在匯編級進(jìn)行手動編程,還需要注意保存
必要的通用寄存器.
2.2 進(jìn)入異常處理循環(huán)后軟件的任務(wù)
進(jìn)入異常處理程序以后,用戶可以完全按照自己的意愿來進(jìn)行程序設(shè)計,包
括調(diào)用Thumb狀態(tài)的函數(shù),等等.但是對于絕大多數(shù)的系統(tǒng)來說,有一個步驟
必須處理,就是要把中斷控制器中對應(yīng)的中斷狀態(tài)標(biāo)識清掉,表明該中斷請求已
經(jīng)得到響應(yīng).否則等退出中斷函數(shù)以后,又馬上會被再一次觸發(fā),從而進(jìn)入周而
復(fù)始的死循環(huán).
2.3 異常的返回
當(dāng)一個異常處理返回時,一共有3件事情需要處理:通用寄存器的恢復(fù),狀
態(tài)寄存器的恢復(fù)以及PC指針的恢復(fù).
通用寄存器的恢復(fù)采用一般的堆棧操作指令,而PC和CPSR的恢復(fù)可以通
過一條指令來實現(xiàn),下面是3個例子:
MOVS pc, lr 或 SUBS pc, lr, #4 或LDMFD sp!, {pc}^
這幾條指令都是普通的數(shù)據(jù)處理指令,特殊之處就是把PC寄存器作為了目
標(biāo)寄存器,并且?guī)Я颂厥獾暮缶Y"S"或"^",在特權(quán)模式下,"S"或"^"的作
用就是使指令在執(zhí)行時,同時完成從SPSR到CPSR的拷貝,達(dá)到恢復(fù)狀態(tài)寄存
器的目的.
異常返回時另一個非常重要的問題是返回地址的確定.在2.1節(jié)中提到進(jìn)入
異常時處理器會有一個保存LR的動作,但是該保存值并不一定是正確中斷的返
回地址.下面以一個簡單的指令執(zhí)行流水狀態(tài)圖來對此加以說明.
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
圖-8 ARM狀態(tài)下3級指令流水線執(zhí)行示例
我們知道在ARM架構(gòu)里,PC值指向當(dāng)前執(zhí)行指令的地址加8處.也就是說,
當(dāng)執(zhí)行指令A(yù)(地址0x8000)時,PC等于指令C的地址(0x8008).假如指令
A是"BL"指令,則當(dāng)執(zhí)行時,會把PC(=0x8008)保存到LR寄存器里面,但
是接下去處理器會馬上對LR進(jìn)行一個自動的調(diào)整動作:LR=LR-0x4.這樣,最
終保存在LR里面的是B指令的地址,所以當(dāng)從BL返回時,LR里面正好是正
確的返回地址.
同樣的調(diào)整機制在所有LR自動保存操作中都存在,比如進(jìn)入中斷響應(yīng)時處
理器所做的LR保存中,也進(jìn)行了一次自動調(diào)整,并且調(diào)整動作都是LR=LR-0x4.
由此我們來對不同異常類型的返回地址進(jìn)行依次比較:
假設(shè)在指令B處(地址0x8004)發(fā)生了中斷響應(yīng),進(jìn)入中斷響應(yīng)后LR上經(jīng)
過調(diào)整保存的地址值應(yīng)該是C的地址0x8008.
(a) 如果發(fā)生的是軟件中斷,即B是"SWI"指令
從SWI中斷返回后下一條執(zhí)行指令就是C,正好是LR寄存器保存的地址,
所以只要直接把LR恢復(fù)給PC.
(b) 如果發(fā)生的是"IRQ"或"FIQ"等指令
因為外部中斷請求中斷了B指令的執(zhí)行,當(dāng)中斷返回后,需要重新回到B
指令的執(zhí)行,也就是返回地址應(yīng)該是B(0x8004),需要把LR減4.
(c) 如果發(fā)生的是"Data Abort"
在B上進(jìn)入數(shù)據(jù)異常的響應(yīng),但導(dǎo)致數(shù)據(jù)異常的原因卻應(yīng)該是上一條指令A(yù).
當(dāng)中斷處理程序修復(fù)數(shù)據(jù)異常以后,要回到A上重新執(zhí)行導(dǎo)致數(shù)據(jù)異常的指令,
因此返回地址應(yīng)該是LR減8.
如果原來的指令執(zhí)行狀態(tài)是Thumb,異常返回地址的分析與此類似,對LR
的調(diào)整正好與ARM狀態(tài)完全一致.
2.4 ARM編譯器對異常處理函數(shù)編寫的擴展
F D E
F D E
F D E
F D E
0x8000 A
0x8004 B
0x8008 C
0x800C D
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
考慮到異常處理函數(shù)在現(xiàn)場保護(hù)和返回地址的處理上與普通函數(shù)的不同之
處,不能直接把普通函數(shù)體連接到異常向量表上,需要在上面加一層封裝,下面
是一個例子:
IRQ_Handler ;中斷響應(yīng),從向量表直接跳來
STMFD SP!, {R0-R12, LR} ;保護(hù)現(xiàn)場,一般只需保護(hù){r0-r3,lr}即可
BL IrqHandler ;進(jìn)入普通處理函數(shù),C或匯編均可
LDMFD SP!, {R0-R12, LR} ;恢復(fù)現(xiàn)場
SUBS PC, LR, #4 ;中斷返回,注意返回地址
為了方便使用高級語言直接編寫異常處理函數(shù),ARM編譯器對此作了特定
的擴展,可以使用函數(shù)聲明關(guān)鍵字__irq,這樣編譯出來的函數(shù)就滿足異常響應(yīng)
對現(xiàn)場保護(hù)和恢復(fù)的需要,并且自動加入對LR進(jìn)行減4的處理,符合IRQ和
FIQ中斷處理的要求.
__irq void IRQ_Handler (void)
{…}
2.5 軟件中斷處理
軟件中斷由專門的軟中斷指令SWI觸發(fā),SWI指令后面跟一個中斷編號,
以標(biāo)識可能共存的多個軟件中斷程序.
圖-9 軟件中斷處理流程
在C程序中調(diào)用軟件中斷需要用到編譯器的擴展功能,使用關(guān)鍵字"__swi"
來聲明中斷函數(shù).注意軟中斷號碼同時在函數(shù)定義時指定.
__swi(0x24) void my_swi (void);
這樣當(dāng)調(diào)用函數(shù)my_swi的時候,就會用"SWI 0x24"來代替普通的函數(shù)調(diào)
用"BL my_swi".
分析圖9的流程,可以發(fā)現(xiàn)軟件中斷同樣存在著中斷分支的問題,即需要根
據(jù)中斷號碼來決定調(diào)用不同的處理程序.軟中斷號碼只存在于SWI指令碼當(dāng)中,
因此需要在中斷處理程序中讀取觸發(fā)中斷的指令代碼,然后提取中斷號信息,再
…
…
SWI 0x01
…
…
用戶程序(C或匯編)
…
CMP swi_num
BEQ …
(Optional)
異常向量表 SWI處理程序(匯編)
SWI處理程序(C)
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
進(jìn)行進(jìn)一步處理.下面是軟中斷指令的編碼格式:
ARM狀態(tài)下的SWI指令編碼格式,32位長度,其中低24位是中斷編號.
Thumb狀態(tài)下的SWI指令編碼格式,16位長度,其中低8位是中斷編號.
圖-10 SWI指令編碼格式
為了在中斷處理程序里面得到SWI 指令的地址,可以利用LR寄存器.每
當(dāng)響應(yīng)一次SWI的時候,處理器都會自動保存并調(diào)整LR寄存器,使里面的內(nèi)
容指向SWI下一條指令的地址,所以把LR里面的地址內(nèi)容上溯一條指令就是
所需的SWI指令地址.需要注意的一點是當(dāng)SWI指令的執(zhí)行狀態(tài)不同時,其指
令地址間隔不一樣,如果進(jìn)入SWI執(zhí)行前是在ARM狀態(tài)下,需要通過LR-4來
獲得SWI指令地址,如果是在Thumb狀態(tài)下進(jìn)入,則只要LR-2就可以了.
下面是一段提取SWI中斷號碼的例程:
MRS R0, SPSR ;檢查進(jìn)入SWI響應(yīng)前的狀態(tài)
TST R0, #T_bit ;是ARM還是Thumb #T_bit=0x20
LDRNEH R0, [LR, #-2] ;是Thumb,讀回SWI指令碼
BICNE R0, R0, #0xff00 ;提取低8位
LDREQ R0, [LR, #-4] ;是ARM,讀回SWI指令碼
BICEQ R0, R0, #0xff000000 ;提取低24位
;寄存器R0中的內(nèi)容是正確的軟中斷編號了
3.可重入中斷設(shè)計
如2.1節(jié)所述,缺省情況下ARM中斷是不可重入的,因為一旦進(jìn)入異常響
應(yīng)狀態(tài),ARM自動關(guān)閉中斷使能.如果在異常處理過程中簡單地打開中斷使能
而發(fā)生中斷嵌套,顯然新的異常處理將破壞原來的中斷現(xiàn)場而導(dǎo)致出錯.但有時
候中斷的可重入又是需要的,因此要能夠通過程序設(shè)計來解決這個問題.其中有
二點是這個問題的關(guān)鍵:
(a) 新中斷使能之前必須要保護(hù)好前一個中斷的現(xiàn)場信息,比如LR_irq和
SPSR_irq等,這一點容易想到也容易做到.
(b) 中斷處理過程中對BL的保護(hù)
28 24 27
SWI number
23
15 8 7 0
1 1 0 1 1 1 1 1 SWI number
31
Cond 1 1 1 1
0
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
在中斷處理函數(shù)中發(fā)生函數(shù)調(diào)用(BL)是很常見的,假設(shè)有下面一種情況:
IRQ_Handler:
…
BL Foo -----------> Foo:
ADD … STMFD SP!, {R0-R3, LR}
… …
LDMFD SP!, {R0-R3, PC}
上述程序,在IRQ處理函數(shù)IRQ_Handler() 中調(diào)用了函數(shù)Foo(),這是一個
普通的異常處理函數(shù).但若是在IRQ_Handler() 里面中斷可重入的話,則可能發(fā)
生問題,考察下面的情況:
當(dāng)新的中斷請求恰好在"BL Foo"指令執(zhí)行完成后發(fā)生.
這時候LR寄存器(因在IRQ模式下,是LR_irq)的值將調(diào)整為BL指令的
下一條指令(ADD)地址,以期能從Foo() 正確返回;但是因為這時候發(fā)生了
中斷請求,接下去要進(jìn)行新中斷的響應(yīng),處理器為了能使新中斷處理完成后能正
確返回,也將進(jìn)行LR_irq保存.因為新中斷是在指令流
BL Foo --> STMFD SP!, {R0-R3, LR}
執(zhí)行過程中插入的,完成跳轉(zhuǎn)操作后,進(jìn)行流水線刷新,最后LR_irq保存的是
STMFD后面一條指令的地址;這樣當(dāng)新中斷利用(PC = LR - 4)操作返回時,
正好可以繼續(xù)原來的流程執(zhí)行STMFD指令.這二次對LR_irq的操作發(fā)生了沖
突,當(dāng)新中斷返回后往下執(zhí)行STMFD指令,這時候壓棧的LR已不是原來需要
的ADD指令的地址,從而使子程序Foo() 無法正確返回.
這個問題無法通過增加額外的現(xiàn)場保護(hù)指令來解決.一個巧妙的辦法是在重
新使能中斷之前改變處理器的模式,也就是使上面程序中的"BL Foo"指令不
要運行在IRQ模式下.這樣當(dāng)新中斷發(fā)生時就不會造成LR寄存器的沖突了.考
慮ARM的所有運行模式,采用System模式是最恰當(dāng)?shù)?因為它既是特權(quán)模式,
又與中斷響應(yīng)無關(guān).
所以一個完整的可重入中斷應(yīng)該遵循以下流程:
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
圖-11 可重入中斷處理流程
下面是一段實現(xiàn)的例程:
保護(hù)寄存器:LR,SPSR等
與中斷控制器通信(需要的話)
切換到System狀態(tài),開中斷使能
中斷處理(現(xiàn)在中斷可重入)
關(guān)閉中斷使能,切換回IRQ狀態(tài)
恢復(fù)寄存器:PC,CPSR等
進(jìn)入普通不可重入中斷處理
結(jié)束一次可重入中斷處理
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開發(fā)要點(五)
—— ARM/Thumb的交互工作
在前面的文章中提到過,很多情況下應(yīng)用程序需要在ARM跟Thumb狀態(tài)之
間相互切換,這部分就討論交互工作的實現(xiàn)方法和一些注意問題.
1. 需要交互的原因
前面提到過Thumb指令在某些特殊情況下具有比ARM指令更為出色的表
現(xiàn),主要是在代碼長度和窄帶寬存儲器系統(tǒng)性能兩方面.正因為Thumb指令在
特定環(huán)境下面的優(yōu)勢,它在很多方面得到了廣泛的應(yīng)用.但是因為下面一些原因,
Thumb又不可能獨立地組成一個應(yīng)用系統(tǒng),所以不可避免地會產(chǎn)生ARM與
Thumb之間交互的問題.
Thumb指令集在功能上只是ARM指令集的一個子集,某些功能只能在
ARM狀態(tài)下執(zhí)行,如CPSR和協(xié)處理器的訪問.
進(jìn)行異常響應(yīng)時,處理器會自動進(jìn)入ARM狀態(tài).
從系統(tǒng)優(yōu)化考慮,在寬帶存儲器上不應(yīng)該放置Thumb代碼,很多窄帶
系統(tǒng)具有寬帶的內(nèi)部存儲器.
即使是一個單純的Thumb應(yīng)用系統(tǒng),也必須加一個匯編的交互頭程序,
因為系統(tǒng)總是自動從ARM開始啟動.
2. 狀態(tài)切換的實現(xiàn)
處理器在ARM/Thumb之間的狀態(tài)切換是通過一條專用的跳轉(zhuǎn)交換指令BX
來實現(xiàn)的.BX指令以通用寄存器(R0-R15)為操作數(shù),通過拷貝Rn到PC來
實現(xiàn)4GB空間范圍內(nèi)的一個絕對跳轉(zhuǎn). BX利用Rn寄存器中存儲的目標(biāo)地址值
的最后一位來判斷跳轉(zhuǎn)后的狀態(tài).
圖-1 BX指令實現(xiàn)狀態(tài)切換
0 31
Rn
PC
BX
ARM/Thumb選擇位:
0 - ARM
1 - Thumb
BX Rn
當(dāng)前狀態(tài)是Thumb時
BX{Cond.} Rn
當(dāng)前狀態(tài)是ARM時
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
無論ARM還是Thumb,其指令存儲在存儲器中都是邊界對齊的(4-Byte或
2-Byte對齊),所以在執(zhí)行跳轉(zhuǎn)過程中,PC寄存器中的最低位肯定被舍棄,不起
作用.在BX指令的執(zhí)行過程中,最低位正好被用作狀態(tài)判斷的標(biāo)識,不會造成
存儲器訪問不對齊的錯誤.
圖2中是一段直接進(jìn)行狀態(tài)切換的例程:
圖-2 ARM/Thumb交互工作的例子
我們知道ARM的狀態(tài)寄存器CPSR中,bit-5是狀態(tài)控制位T-bit,決定當(dāng)前
處理器的運行狀態(tài).如果直接修改CPSR的狀態(tài)位,也能夠達(dá)到改變處理器運行
狀態(tài)的目的,但是會帶來一個問題.因為ARM采用了多級流水線的結(jié)構(gòu),所以
在程序執(zhí)行過程中指令流水線上會存在幾條預(yù)取指令(具體數(shù)目視流水線級數(shù)而
不同).當(dāng)修改CPSR的T-bit以后,狀態(tài)的轉(zhuǎn)變會造成流水線上預(yù)取指令執(zhí)行的
錯誤.而如果用BX指令,則執(zhí)行后會進(jìn)行流水線刷新動作,清除流水線上的殘
余指令,在新的狀態(tài)下重新開始指令預(yù)取,從而保證狀態(tài)轉(zhuǎn)變時候指令流的正確
銜接.
3. ARM/Thumb之間的函數(shù)調(diào)用
在無交互的子程序調(diào)用中,其過程比較簡單.實現(xiàn)調(diào)用通常只需要一條指
令:
BL function
實現(xiàn)返回也只需要從LR恢復(fù)PC即可:
MOV PC, LR
;從ARM狀態(tài)開始
CODE32 ;匯編關(guān)鍵字
ADR R0, Into_Thumb+1 ;得到目標(biāo)地址,末位置1,轉(zhuǎn)向Thumb
BX R0 ;執(zhí)行
… ;其他代碼
CODE16 ;匯編關(guān)鍵字
Into_Thumb ;Thumb代碼段起始地址
… ;Thumb代碼
ADR R5, Back_to_ARM ;得到目標(biāo)地址,末位缺省為0,轉(zhuǎn)向ARM
BX R5 ;執(zhí)行
… ;其他代碼
CODE32 ;匯編關(guān)鍵字
Back_to_ARM ;ARM代碼段起始地址
…
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
如下圖所示:
圖-3 普通函數(shù)調(diào)用
如果子函數(shù)和父函數(shù)不是在同一種狀態(tài)下執(zhí)行的,因為狀態(tài)切換,需要對
函數(shù)調(diào)用作更多的考慮.
(a) BL不能完成狀態(tài)切換,需要由BX來切換狀態(tài).
(b) BX不能自動保存返回地址到LR,需要在BX之前先保存好LR.
(c) 用"BX LR"來返回,不能使用"MOV PC, LR",因為這條指令同
樣不能實現(xiàn)狀態(tài)切換.返回時要仔細(xì)考慮保存的LR中最低位內(nèi)容是否
正確.
假如用戶直接使用匯編進(jìn)行狀態(tài)交互跳轉(zhuǎn),上述的幾個問題都需要用手工
編碼加以處理.如果用戶使用高級語言進(jìn)行開發(fā),不需要為ARM/Thumb之間的
相互調(diào)用增加額外的編碼,但是最好要對其調(diào)用過程加以了解.下面以ARM ADS
中的編譯工具為例進(jìn)行說明(圖4).
(a) 兩個函數(shù)func1()和func2()被編譯成了不同的指令集(ARM或Thumb).
注意func1()和func2()在這里位于二個不同的源文件.
(b) 編譯時必須告訴編譯器和連接器足夠的信息,一方面讓編譯器能夠使用
正確的指令碼進(jìn)行編譯,另一方面這樣當(dāng)在不同的狀態(tài)之間發(fā)生函數(shù)調(diào)
用時,連接器將插入一段連接代碼(veneers)來實現(xiàn)狀態(tài)轉(zhuǎn)換.
圖-4 不同狀態(tài)間函數(shù)調(diào)用的示例
func1
連接器生成
連接代碼
File2.c File1.c
Void func1(void)
{
…
func2();
…
}
.
.
.
BL
.
.
.
.
.
BX
func2
. .
.
BX
Void func2(void)
{
…
…
}
func2
func1
Void func1(void)
{
…
func2();
…
}
.
.
.
BL func2
.
.
.
.
.
MOV PC, LR
基于ARM的嵌入式程序開發(fā)要點
ARM-CHINA-040415A
上述過程中的一個特點是func1還是使用通常的BL指令來進(jìn)行子程序調(diào)用,
而func2返回時則直接使用"BX LR",沒有對LR進(jìn)行判斷和最低位的設(shè)置.
這是因為當(dāng)執(zhí)行BL指令對LR進(jìn)行保存時,其最低位會被自動設(shè)置,以滿足返
回時狀態(tài)切扼/td>
用并且不推薦使用這
些編號。
半主機SWI 使用的軟件中斷編號也可以由用戶自定義,但若是改變了缺省
的軟中斷編號,需要:
?? 更改系統(tǒng)中所有代碼(包括庫代碼)的半主機SWI 調(diào)用
?? 重新配置調(diào)試器對半主機請求的捕捉與相應(yīng)
這樣才能使用新的SWI 編號。
有關(guān)半主機SWI 處理函數(shù)實現(xiàn)的更詳細(xì)信息,請參考ARM 編譯器的相關(guān)
文檔。
4.應(yīng)用環(huán)境的初始化和根據(jù)目標(biāo)系統(tǒng)資源進(jìn)行的移植
在下一期中介紹應(yīng)用環(huán)境和目標(biāo)系統(tǒng)的初始化。
?
?
基于s3c2410軟中斷服務(wù)的uC/OS-II任務(wù)切換
?
?
?
1.關(guān)于軟中斷指令
? 軟件中斷指令(SWI)可以產(chǎn)生一個軟件中斷異常,這為應(yīng)用程序調(diào)用系統(tǒng)例程提供了一種機制。
語法:
?????? SWI?? {<cond>}? SWI_number
SWI執(zhí)行后的寄存器變化:
lr_svc = SWI指令后面的指令地址
spsr_svc = cpsr
pc = vectors + 0x08
cpsr模式 = SVC
cpsr I = 1(屏蔽IRQ中斷)
?
?? 處理器執(zhí)行SWI指令時,設(shè)置程序計數(shù)器pc為向量表的0x08偏移處,同事強制切換處理器模式到SVC模式,以便操作系統(tǒng)例程可以在特權(quán)模式下被調(diào)用。
?? 每個SWI指令有一個關(guān)聯(lián)的SWI號(number),用于表示一個特定的功能調(diào)用或特性。
【例子】 一個ARM工具箱中用于調(diào)試SWI的例子,是一個SWI號為0x123456的SWI調(diào)用。通常SWI指令是在用戶模式下執(zhí)行的。
SWI執(zhí)行前:
??? cpsr = nzcVqift_USER
??? pc = 0x00008000
??? lr = 0x003fffff?? ;lr = 4
??? r0 = 0x12
?
執(zhí)行指令:
??? 0x00008000?? SWI??? 0x123456
?
SWI執(zhí)行后:
??? cpsr = nzcVqIft_SVC
??? spsr = nzcVqift_USER
??? pc = 0x00000008
??? lr = 0x00008004
??? r0 = 0x12
?? SWI用于調(diào)用操作系統(tǒng)的例程,通常需要傳遞一些參數(shù),這可以通過寄存器來完成。在上面的例子中,r0
用于傳遞參數(shù)0x12,返回值也通過寄存器來傳遞。
?? 處理軟件中斷調(diào)用的代碼段稱為中斷處理程序(SWI Handler)。中斷處理程序通過執(zhí)行指令的地址獲取軟件中斷號,指令地址是從lr計算出來的。
?? SWI號由下式?jīng)Q定:
?? SWI_number = <SWI instruction> AND NOT<0xff000000>
?? 其中SWI instruction就是實際處理器執(zhí)行的32位SWI指令
?
?? SWI指令編碼為:
?? 31 - 28? 27 - 24? 23 - 0
???? cond?? 1 1 1 1? immed24
?? 指令的二進(jìn)制代碼的bit23-bit0是24bit的立即數(shù),即SWI指令的中斷號,通過屏蔽高8bit即可獲得中斷號。lr寄存器保存的是中斷返回指令的地址,所以 [lr - 4] 就是執(zhí)行SWI的執(zhí)行代碼。通過load指令拷貝整個SWI指令到寄存器,使用BIC屏蔽指令的高8位,獲取SWI中斷號。
??
??? ;read the SWI instruction
??? LDR? r10, [lr, #-4]
??? BIC? r10, r10, #0xff000000
?
2. 周立功移植uC/OS-II到s3c2410的軟中斷服務(wù)級的任務(wù)切換
uC/OS-II的任務(wù)調(diào)度函數(shù)
?? uC/OS-II的任務(wù)級的調(diào)度是由函數(shù)OS_Sched( )完成的。
?
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
??? OS_CPU_SR cpu_sr;
#endif
??? INT8U y;
??? OS_ENTER_CRITICAL();
??? if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Sched. only if all ISRs done & not locked */
??????? y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
??????? OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
??????? if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
??????????? OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
??????????? OSCtxSwCtr++; /* Increment context switch counter */
??????????? OS_TASK_SW(); /* Perform a context switch */
??????? }
??? }
??? OS_EXIT_CRITICAL();
}
?
?? 詳細(xì)解釋可以參考《嵌入式實時操作系統(tǒng) uC/OS-II》,os_sched函數(shù)在確定所有就緒任務(wù)的最高優(yōu)先級高于當(dāng)前任務(wù)優(yōu)先級時進(jìn)行任務(wù)切換,通過OS_TASK_SW( )宏來調(diào)用。
?? OS_TASK_SW( )宏實際上定義的是SWI軟中斷指令。見OS_CPU.H文件的代碼:
__swi(0x00) void OS_TASK_SW(void); /* 任務(wù)級任務(wù)切換函數(shù) */
__swi(0x01) void _OSStartHighRdy(void); /* 運行優(yōu)先級最高的任務(wù) */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 關(guān)中斷 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 開中斷 */
__swi(0x40) void *GetOSFunctionAddr(int Index); /* 獲取系統(tǒng)服務(wù)函數(shù)入口 */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 獲取自定義服務(wù)函數(shù)入口 */
__swi(0x42) void OSISRBegin(void); /* 中斷開始處理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判斷中斷是否需要切換 */
__swi(0x80) void ChangeToSYSMode(void); /* 任務(wù)切換到系統(tǒng)模式 */
__swi(0x81) void ChangeToUSRMode(void); /* 任務(wù)切換到用戶模式 */
__swi(0x82) void TaskIsARM(INT8U prio); /* 任務(wù)代碼是ARM代碼 */
__swi(0x83) void TaskIsTHUMB(INT8U prio); /* 任務(wù)代碼是THUMB */
?
__swi(0x00) void OS_TASK_SW(void); 是與ADS相關(guān)的代碼,通過反匯編可以看到,調(diào)用OS_TASK_SW實際上被替換成swi 0x00 軟中斷指令。執(zhí)行此執(zhí)行,pc會跳轉(zhuǎn)到向量表的0x08偏移處。
中斷向量表:(見Startup.s文件)
CODE32
??????? AREA vectors,CODE,READONLY
; 異常向量表
Reset
??????? LDR PC, ResetAddr
??????? LDR PC, UndefinedAddr
??????? LDR PC, SWI_Addr
??????? LDR PC, PrefetchAddr
??????? LDR PC, DataAbortAddr
??????? DCD IRQ_Addr
??????? LDR PC, IRQ_Addr
??????? LDR PC, FIQ_Addr
ResetAddr???? DCD ResetInit
UndefinedAddr DCD Undefined
SWI_Addr????? DCD SoftwareInterrupt
PrefetchAddr? DCD PrefetchAbort
DataAbortAddr DCD DataAbort
Nouse???????? DCD 0
IRQ_Addr????? DCD IRQ_Handler
FIQ_Addr????? DCD FIQ_Handler
?
執(zhí)行SWI 0x00指令后,pc會跳轉(zhuǎn)到SoftwareInterrupt代碼處開始執(zhí)行:
見Os_cpu_a.s文件的SoftwareInterrupt函數(shù):
?
SoftwareInterrupt
??????? LDR SP, StackSvc ; 重新設(shè)置堆棧指針
??????? STMFD {R0-R3, R12, LR}
??????? MOV R1, SP ; R1指向參數(shù)存儲位置
??????? MRS R3, SPSR
??????? TST R3, #T_bit ; 中斷前是否是Thumb狀態(tài)
??????? LDRNEH R0, [LR,#-2] ; 是: 取得Thumb狀態(tài)SWI指令
??????? BICNE R0, R0, #0xff00
??????? LDREQ R0, [LR,#-4] ; 否: 取得arm狀態(tài)SWI指令
??????? BICEQ R0, R0, #0xFF000000??? ; 如上面所述,此處通過屏蔽SWI指令的高8位來獲取SWI號,r0 = SWI號,R1指向參數(shù)存儲位置
??????? CMP R0, #1
??????? LDRLO PC, =OSIntCtxSw? ;為0時跳轉(zhuǎn)到OSIntCtxSwdi地址處
??????? LDREQ PC, =__OSStartHighRdy ; 為1時,跳轉(zhuǎn)到__OSStartHighRdy地址處。SWI 0x01為第一次任務(wù)切換
??????? BL SWI_Exception? ;進(jìn)入中斷號散轉(zhuǎn)函數(shù)
???????
??????? LDMFD {R0-R3, R12, PC}^
???????
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
?
以上就是任務(wù)切換軟中斷級服務(wù)的實現(xiàn)。
?
?淺析arm匯編中指令使用學(xué)習(xí)
本帖被 smd801124 設(shè)置為精華(2008-05-08)
macro restore_user_regs
? ldr r1,[sp, #S_PSR]
? ldr lr,[sp, #S_PC]!? @ !用來控制基址變址尋址的最終新地址是否進(jìn)行回寫操作,
????????????????????? @ 執(zhí)行l(wèi)dr之后sp被回寫成sp+#S_PC基址變址尋址的新地址
? msr spsr,r1????????? @ 把cpsr的值保存到spsr中
? ldmdb sp,{r0 - lr}^? @ lr=[sp-1*4],r13=[sp-2*4],r12=[sp-3*4],......,r0=[sp-15*4]
????????????????????? @ 因為沒對pc賦值,所以^的表示將數(shù)據(jù)恢復(fù)到User模式的[r0-lr]寄存器組中[gliethttp]
? mov r0,r0
? add sp,sp,#S_FRAME_SIZE - S_PC
? movs pc,lr
.endm
其他指令正在學(xué)習(xí)中[隨時補充gliethttp]
-----------------------------
1.ldr ip,[sp],#4 將sp中內(nèi)容存入ip,之后sp=sp+4;
? ldr ip,[sp,#4] 將sp+4這個新地址下內(nèi)容存入ip,之后sp值保持不變
? ldr ip,[sp,#4]!將sp+4這個新地址下內(nèi)容存入ip,之后sp=sp+4將新地址值賦給sp
? str ip,[sp],#4 將ip存入sp地址處,之后sp=sp+4;
? str ip,[sp,#4] 將ip存入sp+4這個新地址,之后sp值保持不變
? str ip,[sp,#4]!將ip存入sp+4這個新地址,之后sp=sp+4將新地址值賦給sp
-----------------------------
2.movs r1,#3 ;movs將導(dǎo)致ALU被更改,因為r1賦值非0,即操作結(jié)果r0非0,所以ALU的Z標(biāo)志清0
? bne 1f??? ;因為Z=0,說明不等,所以向前跳到標(biāo)號1:所在處繼續(xù)執(zhí)行其他語句
-----------------------------
3.LDM表示裝載,STM表示存儲.
? LDMED LDMIB 預(yù)先增加裝載
? LDMFD LDMIA 過后增加裝載
? LDMEA LDMDB 預(yù)先減少裝載
? LDMFA LDMDA 過后減少裝載
? STMFA STMIB 預(yù)先增加存儲
? STMEA STMIA 過后增加存儲
? STMFD STMDB 預(yù)先減少存儲
? STMED STMDA 過后減少存儲
注意ED不同于IB;只對于預(yù)先減少裝是相同的.在存儲的時候,ED是過后減少的.
FD、ED、FA、和 EA 指定是滿棧還是空棧,是升序棧還是降序棧.
對于存儲STM而言
先加后存 FA 姑且這么來記,先加(first add),存數(shù)據(jù)
后加先存 EA 姑且這么來記,存數(shù)據(jù),后加end add
先減后存 FD 姑且這么來記,先減first dec,存數(shù)據(jù)
后減先存 ED 姑且這么來記,存數(shù)據(jù),后減end dec
然后記憶LDM,LDM是STM的反相彈出動作,所以
因為是先加后存,所以后減先取 FA 就成了與STM對應(yīng)的取數(shù)據(jù),后減
因為是后加先存,所以先減后取 EA 就成了與STM對應(yīng)的先減,取數(shù)據(jù)
因為是先減后存,所以后加先取 FD 就成了與STM對應(yīng)的取數(shù)據(jù),后加
因為是后減先存,所以先加后取 ED 就成了與STM對應(yīng)的先加,取數(shù)據(jù)
我想通過上面的變態(tài)方式可以比較容易的記住這套指令[gliethttp]
一個滿棧的棧指針指向上次寫的最后一個數(shù)據(jù)單元,而空棧的棧指針指向第一個空閑單元.
一個降序棧是在內(nèi)存中反向增長(就是說,從應(yīng)用程序空間結(jié)束處開始反向增長)而升序棧在內(nèi)存中正向增長.
其他形式簡單的描述指令的行為,意思分別是
IA過后增加(Increment After)、
IB預(yù)先增加(Increment Before)、
DA過后減少(Decrement After)、
DB預(yù)先減少(Decrement Before).
RISC OS使用傳統(tǒng)的滿降序棧.在使用符合APCS規(guī)定的編譯器的時候,它通常把你的棧指針設(shè)置在應(yīng)用程序空間的
結(jié)束處并接著使用一個FD(滿降序-Full Descending)棧.如果你與一個高級語言(BASIC或C)一起工作,你將別無選擇.
棧指針(傳統(tǒng)上是R13)指向一個滿降序棧.你必須繼續(xù)這個格式,或則建立并管理你自己的棧.
4.
teq r1,#0??? //r1-0,將結(jié)果送入狀態(tài)標(biāo)志,如果r1和0相減的結(jié)果為0,那么ALU的Z置位,否則Z清0
bne reschedule//ne表示Z非0,即:不等,那么執(zhí)行reschedule函數(shù)
-----------------------------
5.使用tst來檢查是否設(shè)置了特定的位
tst r1,#0x80 //按位and操作,檢測r1的0x1<<7,即第7位是否置1,按位與之后結(jié)果為0,那么ALU的Z置位
beq reset??? //如果Z置位,即:以上按位與操作結(jié)果是0,那么跳轉(zhuǎn)到reset標(biāo)號執(zhí)行
-----------------------------
6.'^'的理解
'^'是一個后綴標(biāo)志,不能在User模式和Sys系統(tǒng)模式下使用該標(biāo)志.該標(biāo)志有兩個存在目的:
6.1.對于LDM操作,同時恢復(fù)的寄存器中含有pc(r15)寄存器,那么指令執(zhí)行的同時cpu自動將spsr拷貝到cpsr中
如:在IRQ中斷返回代碼中[如下為ads環(huán)境下的代碼gliethttp]
ldmfd {r4}????????? //讀取sp中保存的的spsr值到r4中
msr spsr_cxsf,r4??? //對spsr的所有控制為進(jìn)行寫操作,將r4的值全部注入spsr
ldmfd {r0-r12,lr,pc}^//當(dāng)指令執(zhí)行完畢,pc跳轉(zhuǎn)之前,將spsr的值自動拷貝到cpsr中[gliethttp]
6.2.數(shù)據(jù)的送入、送出發(fā)生在User用戶模式下的寄存器,而非當(dāng)前模式寄存器
如:ldmdb sp,{r0 - lr}^;表示sp棧中的數(shù)據(jù)回復(fù)到User分組寄存器r0-lr中,而不是恢復(fù)到當(dāng)前模式寄存器r0-lr? 當(dāng)然對于User,System,IRQ,SVC,Abort,Undefined這6種模式來說[gliethttp]r0-r12是共用的,只是r13和r14
? 為分別獨有,對于FIQ模式,僅僅r0-r7是和前6中模式的r0-r7共用,r8-r14都是FIQ模式下專有.
7.spsr_cxsf,cpsr_cxsf的理解
c - control field mask byte(PSR[7:0])
x - extension field mask byte(PSR[15:8])
s - status field mask byte(PSR[23:16)
f - flags field mask byte(PSR[31:24]).
老式聲明方式:cpsr_flg,cpsr_all在ADS中已經(jīng)不在支持
cpsr_flg對應(yīng)cpsr_f
cpsr_all對應(yīng)cpsr_cxsf
需要使用專用指令對cpsr和spsr操作:mrs,msr
mrs tmp,cpsr????? //讀取CPSR的值
bic tmp,tmp,#0x80 //如果第7位為1,將其清0
msr cpsr_c,tmp??? //對控制位區(qū)psr[7:0]進(jìn)行寫操作
-----------------------------
8.cpsr的理解
CPSR = Current Program Status Register
SPSR = Saved Program Status Registers
CPSR寄存器(和保存它的SPSR寄存器)
本文來自: 電子論壇[url]http://www.eehome.cn[/url]電子工程師之家!
?
?
?
?
?
?
?
?
?
ARM的堆棧學(xué)習(xí)筆記
來源:http://www.hzlitai.com.cn/?? 作者:藍(lán)石頭
字體大小:[大][中][小]
以下是我在學(xué)習(xí)ARM指令中記錄的關(guān)于堆棧方面的知識:
1、寄存器 R13 在 ARM 指令中常用作堆棧指針
2、對于 R13 寄存器來說,它對應(yīng)6個不同的物理寄存器,其中的一個是用戶模式與系統(tǒng)模式共用,另外5個物理寄存器對應(yīng)于其他5種不同的運行模式。采用以下的記號來區(qū)分不同的物理寄存器: R13_<mode> 其中,mode為以下幾種模式之一:usr、fiq、irq、svc、abt、und。
3、寄存器R13在ARM指令中常用作堆棧指針,但這只是一種習(xí)慣用法,用戶也可使用其他的寄存器作為堆棧指針。而在Thumb指令集中,某些指令強制性的要求使用R13作為堆棧指針。由于處理器的每種運行模式均有自己獨立的物理寄存器R13,在用戶應(yīng)用程序的初始化部分,一般都要初始化每種模式下的R13,使其指向該運行模式的棧空間,這樣,當(dāng)程序的運行進(jìn)入異常模式時,可以將需要保護(hù)的寄存器放入R13所指向的堆棧,而當(dāng)程序從異常模式返回時,則從對應(yīng)的堆棧中恢復(fù),采用這種方式可以保證異常發(fā)生后程序的正常執(zhí)行。
4、有四種類型的堆棧:
堆棧是一種數(shù)據(jù)結(jié)構(gòu),按先進(jìn)后出(First In Last Out,FILO)的方式工作,使用一個稱作堆棧指針的專用寄存器指示當(dāng)前的操作位置,堆棧指針總是指向棧頂。
當(dāng)堆棧指針指向最后壓入堆棧的數(shù)據(jù)時,稱為滿堆棧(Full Stack),而當(dāng)堆棧指針指向下一個將要放入數(shù)據(jù)的空位置時,稱為空堆棧(Empty Stack)。
同時,根據(jù)堆棧的生成方式,又可以分為遞增堆棧(Ascending Stack)和遞減堆棧(DecendingStack),當(dāng)堆棧由低地址向高地址生成時,稱為遞增堆棧,當(dāng)堆棧由高地址向低地址生成時,稱為遞減堆棧。這樣就有四種類型的堆棧工作方式,ARM 微處理器支持這四種類型的堆棧工作方式,即: ◎ Full descending 滿遞減堆棧堆棧首部是高地址,堆棧向低地址增長。棧指針總是指向堆棧最后一個元素(最后一個元素是最后壓入的數(shù)據(jù))。 ARM-Thumb過程調(diào)用標(biāo)準(zhǔn)和ARM、Thumb C/C++ 編譯器總是使用Full descending 類型堆棧。
◎ Full ascending 滿遞增堆棧堆棧首部是低地址,堆棧向高地址增長。棧指針總是指向堆棧最后一個元素(最后一個元素是最后壓入的數(shù)據(jù))。
◎ Empty descending 空遞減堆棧堆棧首部是低地址,堆棧向高地址增長。棧指針總是指向下一個將要放入數(shù)據(jù)的空位置。
◎ Empty ascending 空遞增堆棧堆棧首部是高地址,堆棧向低地址增長。棧指針總是指向下一個將要放入數(shù)據(jù)的空位置。
5、操作堆棧的匯編指令堆棧類型 入棧指令 出棧指令 Full descending STMFD (STMDB) LDMFD (LDMIA) Full ascending STMFA (STMIB) LDMFA (LDMDA) Empty descending STMED (STMDA) LDMED (LDMIB) Empty ascending STMEA (STMIA) LDMEA (LDMDB)
例子: STMFD r13!, {r0-r5} ; Push onto a Full Descending Stack LDMFD r13!, {r0-r5} ; Pop from a Full Descending Stack.
總結(jié)
- 上一篇: 醉酒仙
- 下一篇: excel2016 android,Ex