日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

ARM汇编指令汇总

發(fā)布時(shí)間:2023/12/14 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ARM汇编指令汇总 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1、ARM匯編的格式:
??? 在ARM匯編里,有些字符是用來(lái)標(biāo)記行號(hào)的,這些字符要求頂格寫(xiě);有些偽碼是需要成對(duì)出現(xiàn)的,例如ENTRY和END,就需要對(duì)齊出現(xiàn),也就是說(shuō)他們要么都頂格,要么都空相等的空,否則編譯器將報(bào)錯(cuò)。常量定義需要頂格書(shū)寫(xiě),不然,編譯器同樣會(huì)報(bào)錯(cuò)。
??? 2、字符串變量的值是一系列的字符,并且使用雙引號(hào)作為分界符,如果要在字符串中使用雙引號(hào),則必須連續(xù)使用兩個(gè)雙引號(hào)。
??? 3、在使用LDR時(shí),當(dāng)格式是LDR r0,=0x022248,則第二個(gè)參數(shù)表示地址,即0x022248,同樣的,當(dāng)src變量代表一個(gè)數(shù)組時(shí),需要將r0寄存器指向src則需要這樣賦值:LDR r0,=src???? 當(dāng)格式是LDR r0,[r2],則第二個(gè)參數(shù)表示寄存器,我的理解是[]符號(hào)表示取內(nèi)容,r2本身表示一個(gè)寄存器地址,取內(nèi)容候?qū)⑵浯嫒0這個(gè)寄存器中。
??? 4、在語(yǔ)句:
?????? CMP r0,#num
?????? BHS stop
?????? 書(shū)上意思是:如果r0寄存器中的值比num大的話(huà),程序就跳轉(zhuǎn)到stop標(biāo)記的行。但是,實(shí)際測(cè)試的時(shí)候,我發(fā)現(xiàn)如果r0和num相等也能跳轉(zhuǎn)到stop標(biāo)記的行,也就是說(shuō)只要r0小于num才不會(huì)跳轉(zhuǎn)。
???
???? 下面就兩個(gè)具體的例子談?wù)凙RM匯編(這是我昨天好不容易看懂的,呵呵)。
???? 第一個(gè)是使用跳轉(zhuǎn)表解決分支轉(zhuǎn)移問(wèn)題的例程,源代碼如下(保存的時(shí)候請(qǐng)將文件后綴名改為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
??? 程序其實(shí)很簡(jiǎn)單,可見(jiàn)我有多愚笨!還是簡(jiǎn)要介紹一下這段代碼吧。首先用AREA偽代碼加上CODE,表明下面引出的將是一個(gè)代碼段(于此相對(duì)的還有數(shù)據(jù)段DATA),ENTRY 和END成對(duì)出現(xiàn),說(shuō)明他們之間的代碼是程序的主體。start段給寄存器初始化。ADR? r4, JumpTable一句是將相當(dāng)于數(shù)組的JumpTable的地址付給r4這個(gè)寄存器。

??? stop一段是用來(lái)是程序退出的,第一個(gè)語(yǔ)句“MOV r0,#0x18”將r0賦值為0x18,這個(gè)立即數(shù)對(duì)應(yīng)于宏angel_SWIreason_ReportException。表示r1中存放的執(zhí)行狀態(tài)。語(yǔ)句“LDR r1,=0x20026”將r1的值設(shè)置成ADP_Stopped_ApplicationExit,該宏表示程序正常退出。然后使用SWI,語(yǔ)句“SWI 0x123456”結(jié)束程序,將CPU的控制權(quán)交回調(diào)試器手中。

??? 在JumpTable表中,DCD類(lèi)型的數(shù)組包含四個(gè)字,所以,當(dāng)實(shí)現(xiàn)CASE跳轉(zhuǎn)的時(shí)候,需要將給出的索引乘上4,才是真正前進(jìn)的地址數(shù)。

?

??? 再看一個(gè)用匯編實(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
???? 用匯編實(shí)現(xiàn)循環(huán)需要跳轉(zhuǎn)指令,但是因?yàn)锳RM系統(tǒng)只有一個(gè)CPSR寄存器,所以要實(shí)現(xiàn)雙重循環(huán)還是有些難度。上面這個(gè)代碼還是有相當(dāng)大的借鑒意義。程序不難讀懂,和C語(yǔ)言的冒泡排序基本思路是完全一樣的。

?

?

?


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

?

?

?

?

?

?

?


??

?


從下面的一個(gè)ARM 匯編小程序要弄懂的以下三個(gè)問(wèn)題:

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呢?因?yàn)锽X指令跳轉(zhuǎn)到指定的地址執(zhí)行程序?? 時(shí),?? 若   (BX{cond}? Rm)Rm的位[0]為1,則跳轉(zhuǎn)時(shí)自動(dòng)將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為軟中斷號(hào)(服務(wù)類(lèi)型)

使用SWI指令時(shí),通常使用以下兩種方法進(jìn)行傳遞參數(shù),SWI 異常中斷處理程序就可以提供相關(guān)的服務(wù),這兩種方法均是用戶(hù)軟件協(xié)定.SWI異常中斷處理程序要通過(guò)讀取引起軟中斷的SWI指令,以取得24位立即數(shù).

(1) 指令中的24位立即數(shù)指定了用戶(hù)請(qǐng)求的服務(wù)類(lèi)型,參數(shù)通過(guò)通用寄存器傳遞.

 mov?? r0,#34??? ;設(shè)置子功能號(hào)位34

?? SWI?? 12???? ;調(diào)用12號(hào)軟中斷

(2) 指令中的24位立即數(shù)被忽略,用戶(hù)請(qǐng)求的服務(wù)類(lèi)型有寄存器RO的值決定,參數(shù)通過(guò)其他的通用寄存器傳遞.

 mov? r0,#12???????? ;調(diào)用12號(hào)軟中斷

 mov r1,#34???????? ;設(shè)置子功能號(hào)位34

 SWI  0

在SWI異常中斷處理程序中,取出SWI立即數(shù)的步驟為:首先確定引起軟中斷的SWI指令是ARM指令還是Thunb指令,這可通過(guò)對(duì)SPSR訪問(wèn)得到;然后取得該SWI指令的地址,這可通過(guò)訪問(wèn)LR寄存器得到;接著讀出指令,分解出立即數(shù).如如下程序:

T_bit????????????? EQU??????????????????? 0X20

SWI_Handler

??????????????? STMFD????? SP!,{R0-R3,R12,LR}????????????????? ;現(xiàn)場(chǎng)保護(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指令軟中斷

從下面的一個(gè)ARM 匯編小程序要弄懂的以下三個(gè)問(wèn)題:

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呢?因?yàn)锽X指令跳轉(zhuǎn)到指定的地址執(zhí)行程序?? 時(shí),?? 若   (BX{cond}? Rm)Rm的位[0]為1,則跳轉(zhuǎn)時(shí)自動(dòng)將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為軟中斷號(hào)(服務(wù)類(lèi)型)

使用SWI指令時(shí),通常使用以下兩種方法進(jìn)行傳遞參數(shù),SWI 異常中斷處理程序就可以提供相關(guān)的服務(wù),這兩種方法均是用戶(hù)軟件協(xié)定.SWI異常中斷處理程序要通過(guò)讀取引起軟中斷的SWI指令,以取得24位立即數(shù).

(1) 指令中的24位立即數(shù)指定了用戶(hù)請(qǐng)求的服務(wù)類(lèi)型,參數(shù)通過(guò)通用寄存器傳遞.

 mov?? r0,#34??? ;設(shè)置子功能號(hào)位34

?? SWI?? 12???? ;調(diào)用12號(hào)軟中斷

(2) 指令中的24位立即數(shù)被忽略,用戶(hù)請(qǐng)求的服務(wù)類(lèi)型有寄存器RO的值決定,參數(shù)通過(guò)其他的通用寄存器傳遞.

 mov? r0,#12???????? ;調(diào)用12號(hào)軟中斷

 mov r1,#34???????? ;設(shè)置子功能號(hào)位34

 SWI  0

在SWI異常中斷處理程序中,取出SWI立即數(shù)的步驟為:首先確定引起軟中斷的SWI指令是ARM指令還是Thunb指令,這可通過(guò)對(duì)SPSR訪問(wèn)得到;然后取得該SWI指令的地址,這可通過(guò)訪問(wèn)LR寄存器得到;接著讀出指令,分解出立即數(shù).如如下程序:

T_bit????????????? EQU??????????????????? 0X20

SWI_Handler

??????????????? STMFD????? SP!,{R0-R3,R12,LR}??????????? ;現(xiàn)場(chǎng)保護(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的實(shí)現(xiàn)
在需要軟件中斷處調(diào)用

__SWI? 0xNum?????????? ;Num為SWI中斷處理模塊的編號(hào),見(jiàn)表SwiFunction


;軟件中斷
SoftwareInterrupt
??????? CMP???? R0, #12???????????????????????? ;R0中的SWI編號(hào)是否大于最大值

/* 下面這句語(yǔ)句把 (LDRLO地址+ 8 + R0*4) 的地址裝載到PC寄存器,舉例如果上面的 Num="1",也就是R0 = 1, 假設(shè)LDRLO這條指令的地址是0x00008000,那么根據(jù)ARM體系的2級(jí)流水線(xiàn) PC寄存器里指向是下兩條指令 于是PC = 0x00008008? 也就是偽指令DCD???? TASK_SW 聲明的標(biāo)號(hào)TASK_SW? 的地址,注意DCD???? TASK_SW 這條指令本身不是ARM能執(zhí)行的指令,也不會(huì)占有地址,這條指令靠匯編器匯編成可執(zhí)行代碼,它的意義就是聲明 TASK_SW的地址,? , [PC, R0, LSL #2] 這個(gè)尋址方式就是 PC + R0的值左移2位的值( 0x01<<2? => 0x04 ),這樣PC的值就是0x0000800C, 即ENTER_CRITICAL的地址于是ARM執(zhí)行該標(biāo)號(hà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到堆棧
??????????????????????????????????????????????? ;因?yàn)镽0~R3沒(méi)有保存有用數(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ù)加載/存儲(chǔ)指令實(shí)驗(yàn)???? 2007-08-22 12:08:06
大 中 小
標(biāo)簽:arm指令 ldm/stm
這個(gè)程序用批量傳輸指令傳輸數(shù)據(jù),一次可傳8個(gè)字:

??????? 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

?

下面的這個(gè)程序?qū)崿F(xiàn)同樣的功能,每次只能傳一個(gè)字:


??????? 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)時(shí),幾乎所有的指令均根據(jù)CPSR中條件碼的狀態(tài)和指令的條件域有條件的執(zhí)行。當(dāng)指令的執(zhí)行條件滿(mǎn)足時(shí),指令被執(zhí)行,否則指令被忽略。
每一條ARM指令包含4位的條件碼,位于指令的最高4位[31:28]。條件碼共有16種,每種條件碼可用兩個(gè)字符表示,這兩個(gè)字符可以添加在指令助記符的后面和指令同時(shí)使用。例如,跳轉(zhuǎn)指令B可以加上后綴EQ變?yōu)锽EQ表示“相等則跳轉(zhuǎn)”,即當(dāng)CPSR中的Z標(biāo)志置位時(shí)發(fā)生跳轉(zhuǎn)。

1、 B指令
B指令的格式為:
B{條件} 目標(biāo)地址
B指令是最簡(jiǎn)單的跳轉(zhuǎn)指令。一旦遇到一個(gè) B 指令,ARM 處理器將立即跳轉(zhuǎn)到給定的目標(biāo)地址,從那里繼續(xù)執(zhí)行。注意存儲(chǔ)在跳轉(zhuǎn)指令中的實(shí)際值是相對(duì)當(dāng)前PC值的一個(gè)偏移量,而不是一個(gè)絕對(duì)地址,它的值由匯編器來(lái)計(jì)算(參考尋址方式中的相對(duì)尋址)。它是 24 位有符號(hào)數(shù),左移兩位后有符號(hào)擴(kuò)展為 32 位,表示的有效偏移為 26 位(前后32MB的地址空間)。以下指令:
B Label ;程序無(wú)條件跳轉(zhuǎn)到標(biāo)號(hào)Label處執(zhí)行
CMP R1,#0 ;當(dāng)CPSR寄存器中的Z條件碼置位時(shí),程序跳轉(zhuǎn)到標(biāo)號(hào)Label處執(zhí)行
BEQ Label

3.3.6 批量數(shù)據(jù)加載/存儲(chǔ)指令
ARM微處理器所支持批量數(shù)據(jù)加載/存儲(chǔ)指令可以一次在一片連續(xù)的存儲(chǔ)器單元和多個(gè)寄存器之間傳送數(shù)據(jù),批量加載指令用于將一片連續(xù)的存儲(chǔ)器中的數(shù)據(jù)傳送到多個(gè)寄存器,批量數(shù)據(jù)存儲(chǔ)指令則完成相反的操作。常用的加載存儲(chǔ)指令如下:
— LDM 批量數(shù)據(jù)加載指令
— STM 批量數(shù)據(jù)存儲(chǔ)指令
LDM(或STM)指令
LDM(或STM)指令的格式為:
LDM(或STM){條件}{類(lèi)型} 基址寄存器{!},寄存器列表{∧}
LDM(或STM)指令用于從由基址寄存器所指示的一片連續(xù)存儲(chǔ)器到寄存器列表所指示的多個(gè)寄存器之間傳送數(shù)據(jù),該指令的常見(jiàn)用途是將多個(gè)寄存器的內(nèi)容入棧或出棧。其中,{類(lèi)型}為以下幾種情況:
IA 每次傳送后地址加1;
IB 每次傳送前地址加1;
DA 每次傳送后地址減1;
DB 每次傳送前地址減1;
FD 滿(mǎn)遞減堆棧;
ED 空遞減堆棧;
FA 滿(mǎn)遞增堆棧;
EA 空遞增堆棧;
{!}為可選后綴,若選用該后綴,則當(dāng)數(shù)據(jù)傳送完畢之后,將最后的地址寫(xiě)入基址寄存器,否則基址寄存器的內(nèi)容不改變。
基址寄存器不允許為R15,寄存器列表可以為R0~R15的任意組合。
{∧}為可選后綴,當(dāng)指令為L(zhǎng)DM且寄存器列表中包含R15,選用該后綴時(shí)表示:除了正常的數(shù)據(jù)傳送之外,還將SPSR復(fù)制到CPSR。同時(shí),該后綴還表示傳入或傳出的是用戶(hù)模式下的寄存器,而不是當(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]??
?
從下面的一個(gè)ARM 匯編小程序要弄懂的以下三個(gè)問(wèn)題:

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呢?因?yàn)锽X指令跳轉(zhuǎn)到指定的地址執(zhí)行程序 時(shí), 若   (BX{cond} Rm)Rm的位[0]為1,則跳轉(zhuǎn)時(shí)自動(dòng)將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為軟中斷號(hào)(服務(wù)類(lèi)型)

使用SWI指令時(shí),通常使用以下兩種方法進(jìn)行傳遞參數(shù),SWI 異常中斷處理程序就可以提供相關(guān)的服務(wù),這兩種方法均是用戶(hù)軟件協(xié)定.SWI異常中斷處理程序要通過(guò)讀取引起軟中斷的SWI指令,以取得24位立即數(shù).

(1) 指令中的24位立即數(shù)指定了用戶(hù)請(qǐng)求的服務(wù)類(lèi)型,參數(shù)通過(guò)通用寄存器傳遞.

??????? mov r0,#34 ;設(shè)置子功能號(hào)位34

??????? SWI 12 ;調(diào)用12號(hào)軟中斷

(2) 指令中的24位立即數(shù)被忽略,用戶(hù)請(qǐng)求的服務(wù)類(lèi)型有寄存器R0的值決定,參數(shù)通過(guò)其他的通用寄存器傳遞.

??????? mov r0,#12 ;調(diào)用12號(hào)軟中斷

??????? mov r1,#34 ;設(shè)置子功能號(hào)位34

??????? SWI  0

在SWI異常中斷處理程序中,取出SWI立即數(shù)的步驟為:首先確定引起軟中斷的SWI指令是ARM指令還是Thunb指令,這可通過(guò)對(duì)SPSR訪問(wèn)得到;然后取得該SWI指令的地址,這可通過(guò)訪問(wèn)LR寄存器得到;接著讀出指令,分解出立即數(shù).如如下程序:

?????????? T_bit EQU 0X20
??
??????????? SWI_Handler
??
??????????? STMFD SP!,{R0-R3,R12,LR} ;現(xiàn)場(chǎng)保護(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)生一個(gè)軟件中斷異常,這為應(yīng)用程序調(diào)用系統(tǒng)例程提供了一種機(jī)制。
語(yǔ)法:
?????? 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è)置程序計(jì)數(shù)器pc為向量表的0x08偏移處,同事強(qiáng)制切換處理器模式到SVC模式,以便操作系統(tǒng)例程可以在特權(quán)模式下被調(diào)用。
?? 每個(gè)SWI指令有一個(gè)關(guān)聯(lián)的SWI號(hào)(number),用于表示一個(gè)特定的功能調(diào)用或特性。
【例子】 一個(gè)ARM工具箱中用于調(diào)試SWI的例子,是一個(gè)SWI號(hào)為0x123456的SWI調(diào)用。通常SWI指令是在用戶(hù)模式下執(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ù),這可以通過(guò)寄存器來(lái)完成。在上面的例子中,r0
用于傳遞參數(shù)0x12,返回值也通過(guò)寄存器來(lái)傳遞。
?? 處理軟件中斷調(diào)用的代碼段稱(chēng)為中斷處理程序(SWI Handler)。中斷處理程序通過(guò)執(zhí)行指令的地址獲取軟件中斷號(hào),指令地址是從lr計(jì)算出來(lái)的。
?? SWI號(hào)由下式?jīng)Q定:
?? SWI_number = <SWI instruction> AND NOT<0xff000000>
?? 其中SWI instruction就是實(shí)際處理器執(zhí)行的32位SWI指令
?
?? SWI指令編碼為:
?? 31 - 28? 27 - 24? 23 - 0
???? cond?? 1 1 1 1? immed24
?? 指令的二進(jìn)制代碼的bit23-bit0是24bit的立即數(shù),即SWI指令的中斷號(hào),通過(guò)屏蔽高8bit即可獲得中斷號(hào)。lr寄存器保存的是中斷返回指令的地址,所以 [lr - 4] 就是執(zhí)行SWI的執(zhí)行代碼。通過(guò)load指令拷貝整個(gè)SWI指令到寄存器,使用BIC屏蔽指令的高8位,獲取SWI中斷號(hào)。
??
??? ;read the SWI instruction
??? LDR? r10, [lr, #-4]
??? BIC? r10, r10, #0xff000000
?
2. 周立功移植uC/OS-II到s3c2410的軟中斷服務(wù)級(jí)的任務(wù)切換
uC/OS-II的任務(wù)調(diào)度函數(shù)
?? uC/OS-II的任務(wù)級(jí)的調(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ì)解釋可以參考《嵌入式實(shí)時(shí)操作系統(tǒng) uC/OS-II》,os_sched函數(shù)在確定所有就緒任務(wù)的最高優(yōu)先級(jí)高于當(dāng)前任務(wù)優(yōu)先級(jí)時(shí)進(jìn)行任務(wù)切換,通過(guò)OS_TASK_SW( )宏來(lái)調(diào)用。
?? OS_TASK_SW( )宏實(shí)際上定義的是SWI軟中斷指令。見(jiàn)OS_CPU.H文件的代碼:

__swi(0x00) void OS_TASK_SW(void); /* 任務(wù)級(jí)任務(wù)切換函數(shù) */
__swi(0x01) void _OSStartHighRdy(void); /* 運(yùn)行優(yōu)先級(jí)最高的任務(wù) */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 關(guān)中斷 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 開(kāi)中斷 */

__swi(0x40) void *GetOSFunctionAddr(int Index); /* 獲取系統(tǒng)服務(wù)函數(shù)入口 */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 獲取自定義服務(wù)函數(shù)入口 */
__swi(0x42) void OSISRBegin(void); /* 中斷開(kāi)始處理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判斷中斷是否需要切換 */

__swi(0x80) void ChangeToSYSMode(void); /* 任務(wù)切換到系統(tǒng)模式 */
__swi(0x81) void ChangeToUSRMode(void); /* 任務(wù)切換到用戶(hù)模式 */
__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)的代碼,通過(guò)反匯編可以看到,調(diào)用OS_TASK_SW實(shí)際上被替換成swi 0x00 軟中斷指令。執(zhí)行此執(zhí)行,pc會(huì)跳轉(zhuǎn)到向量表的0x08偏移處。


中斷向量表:(見(jiàn)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會(huì)跳轉(zhuǎn)到SoftwareInterrupt代碼處開(kāi)始執(zhí)行:

見(jiàn)Os_cpu_a.s文件的SoftwareInterrupt函數(shù):

?

SoftwareInterrupt
??????? LDR SP, StackSvc ; 重新設(shè)置堆棧指針
??????? STMFD {R0-R3, R12, LR}
??????? MOV R1, SP ; R1指向參數(shù)存儲(chǔ)位置

??????? 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??? ; 如上面所述,此處通過(guò)屏蔽SWI指令的高8位來(lái)獲取SWI號(hào),r0 = SWI號(hào),R1指向參數(shù)存儲(chǔ)位置
??????? CMP R0, #1
??????? LDRLO PC, =OSIntCtxSw? ;為0時(shí)跳轉(zhuǎn)到OSIntCtxSwdi地址處
??????? LDREQ PC, =__OSStartHighRdy ; 為1時(shí),跳轉(zhuǎn)到__OSStartHighRdy地址處。SWI 0x01為第一次任務(wù)切換

??????? BL SWI_Exception? ;進(jìn)入中斷號(hào)散轉(zhuǎn)函數(shù)
???????
??????? LDMFD {R0-R3, R12, PC}^
???????
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

?

以上就是任務(wù)切換軟中斷級(jí)服務(wù)的實(shí)現(xiàn)。
?
?


?

?

利用arm 組語(yǔ)的PRE-INDEX 與POST-INDEX ADDRESSING,上課時(shí)CODING完成的範(fàn)例--1+2+3+...+10之和 分類(lèi):IT技術(shù)分享2008/01/30 17:17於今日 97/01/30 上課時(shí)實(shí)作的程式範(fàn)例,先貼上程式碼。

因是上課當(dāng)場(chǎng)coding完成的,so沒(méi)有加上註解^^

範(fàn)例一:使用 POST-INDEX ADDRESSING實(shí)作的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實(shí)作的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)存訪問(wèn)指令
基本指令:
LDR:memory -> register (memory包括映射到內(nèi)存空間的非通用寄存器)
STR:register? -> memory
語(yǔ)法:
??????? 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:用戶(hù)指令后綴
Rd:源寄存器,對(duì)于LDR指令,Rd將保存從memory中讀取的數(shù)值;對(duì)于STR指令,Rd保存著將寫(xiě)入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??????????????????????????????????????????????????????? ;定義一個(gè)名為“addr1”的label,addr1 = 當(dāng)前地址
??????? dcd??????????????? 0??????????????????????????????????????? ;在當(dāng)前地址出定義一個(gè)32bit的變量
~ ~ ~
??????? ldr??????????????? r1,??????????????? label1??? ;r1 = addr1,r1即可以作為var1的指針
??????? ldr??????????????? r0,??????????????? [r1]
??????? add??????? r0,??????????????? r0,??????????????? #1
??????? str??????????????? r0,??????????????? [r1]??????????????????????? ;變量var1的值加1
【FlexOffset的使用】
??????? FlexOffset可以是立即數(shù),也可以是寄存器,還可以是簡(jiǎn)單的表達(dá)式
???????
2、??????? 多字節(jié)存取指令(常用于堆棧操作)
基本指令:
??????? LDM:memory ――> 多個(gè)寄存器
??????? STM:多個(gè)寄存器 ――> memory
語(yǔ)法:
??????? op{cond }mode??????? Rn{!},??????????????? reglist {^}
??????? mode:指針更新模式,對(duì)應(yīng)于不同類(lèi)型的棧。最常用的是“FD”模式,相當(dāng)于初始棧指針在高位,壓棧后指針值減小。
??????? Rn:指針寄存器
??????? !:最后的指針值將寫(xiě)入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)記用來(lái)使CPU退出異常模式,進(jìn)入普通狀態(tài)。
???????????????????????????????????????????????????????????????????????????????????????

3、??????? 算術(shù)運(yùn)算指令
基本指令:
??????? ADD:加
??????? SUB:減
語(yǔ)法:
??????? op{cond }{S}??????????????? Rd,??????????????? Rn,??????????????? Operand2
??????? S:是否設(shè)置狀態(tài)寄存器(CPSR),如:N(有符號(hào)運(yùn)算結(jié)果得負(fù)數(shù))、Z(結(jié)果得0)、C(運(yùn)算的進(jìn)位或移位)、V(有符號(hào)數(shù)的溢出)等等。
??????? Rd:保存結(jié)果的寄存器
??????? Rn:運(yùn)算的第一個(gè)操作數(shù)
??????? Operand2:運(yùn)算的第二個(gè)操作數(shù),這個(gè)操作數(shù)的值有一些限定:如可以是8位立即數(shù)(例:0xa8)或一個(gè)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、??????? 邏輯運(yùn)算指令
基本指令:
??????? AND:與
??????? ORR:或
??????? EOR:異或
??????? BIC:位清0
語(yǔ)法:
??????? op{cond }{S}??????????????? Rd,??????????????? Rn,??????????????? Operand2
??????? 語(yǔ)法類(lèi)似算術(shù)運(yùn)算指令
例子:
??????? 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??????????????? ; 錯(cuò)誤

5、??????? MOV指令
語(yǔ)法:
MOV{cond}{S}??????? Rd,??????????????? Operand2
例子:
??????? mov??????????????? r0,??????????????? #8??????????????????????????????????????????????? ; r0 = 8
??????? mov??????????????? r0,??????????????? r1??????????????????????????????????????????????? ; r0 = r1
不同于LDR、STR指令,該指令可以寄存器間賦值

6、??????? 比較指令
基本指令:
??????? CMP:比較兩個(gè)操作數(shù),并設(shè)置狀態(tài)寄存器
語(yǔ)法:
??????? CMP{cond }??????????????? Rn,??????????????? Operand2
例子:
??????? cmp??????????????? r0,??????????????? r1??????????????????????????????????????????????? ; 計(jì)算r0 – r1,并設(shè)置狀態(tài)寄存器,由狀態(tài)寄存器可以知r0是否大于、小于或等于r1
??????? cmp??????????????? r0,??????????????? #0??????????????????????????????????????????????? ;

7、??????? 跳轉(zhuǎn)指令
基本指令:
??????? B:跳轉(zhuǎn)
??????? BL:跳轉(zhuǎn)并將下一指令的地址存入lr寄存器
語(yǔ)法:
??????? op{cond}??????????????? label
??????? label:要跳向的地址
例子:
loop1
??????? ~ ~ ~
??????? b??????????????? loop1??????????????????????????????? ; 跳到地址loop1處
??????? bl??????????????? sub1??????????????????????????????? ; 將下一指令地址寫(xiě)入lr,并跳至sub1
??????? ~ ~ ~
sub1
??????? ~ ~ ~
??????? mov??????? pc,??????? lr??????????????????????? ; 從sub1中返回
【使用本地label(local label)】
??????? 本地label可以在一個(gè)程序段內(nèi)多次使用,用數(shù)字作為label的名稱(chēng),也可以在數(shù)字后面跟一些字母。引用本地label的語(yǔ)法是:??????? %{F|B}{A|T}n{routname},其中F代表向前搜索本地label,B代表向后搜索,A/T不常使用。
例子
100??????? ; 定義本地label,名稱(chēng)為“100”
~ ~ ~
100??????????????????????????????????????????????????????????????????????????????? ; 第二次定義本地label,名稱(chēng)為“100”
??????? ~ ~ ~
??????? b??????????????? %f100??????????????????????????????????????????????? ; 向前跳到最近的“100”處
~ ~ ~
??????? b??????????????? %b100??????????????????????????????????????????????? ; 向后跳至最近的“100”處
100??????????????????????????????????????????????????????????????????????????????? ; 第三次定義本地label 100

8、??????? 條件執(zhí)行
條件:狀態(tài)寄存器中某一或某幾個(gè)比特的值代表?xiàng)l件,對(duì)應(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指令定義一個(gè)程序段,名稱(chēng)為EX2,屬性為:CODE、READONLY
??????? INCLUDE???????? Common.inc??????? ;包含匯編頭文件
??????? IMPORT???????? sub1??????????????????????????????????????? ;引用外部符號(hào)
??????? EXPORT??????????????? prog1??????????????????????? ;向外輸出符號(hào)
ENTRY???????????????????????????????????????????????????????? ;ENTRY指令定義程序的開(kāi)始
start??????????????????????????????????????????????????????????????????????????????? ;此處定義了一個(gè)label start
MOV???????? r0,??????? #10???????????????????????????????????????
MOV???????? r1,??????? #3
ADD???????? r0,??????? r0,??????? r1???????????????????????????????? ;r0 =r0 +r1
prog1??????????????????????????????????????????????????????????????????????? ;此處定義了一個(gè)label prog1
MOV???????? r0,??????? #0x18????????????????????????????????
LDR???????? r1,??????? =0x20026????????????????????????
SWI???????? 0x123456????????????????????????????????
END???????????????????????????????????????????????????????????????? ;END指令表示程序段的結(jié)束
;――――――――――――――――――――
宏的使用
定義宏:
MACRO??????????????????????????????????????????????? ;宏的起始
{label}??????? macroname??????? para1,para2……
;代碼
MEND??????????????????????????????????????????????????????? ;宏結(jié)束
引用宏:
??????????????? marconame???????? para1,para2……
例子
;定義一個(gè)宏,完成兩個(gè)寄存器內(nèi)容交換
??????????????? MACRO
??????????????? swap??????????????? $w1,??????????????? $w2,??????????????? $w3
??????????????????????? mov??????????????? $w3,??????????????? $w1
??????????????????????? mov??????????????? $w1,??????????????? $w2
??????????????????????? mov??????????????? $w2,??????????????? $w3
??????????????? MEND

;使用這個(gè)宏
ldr??????????????????????? r0,??????????????? =1
ldr??????????????????????? r1,??????????????? =2
swap??????????????? r0,??????????????? r1,??????????????? r2??????????????? ;此處調(diào)用了宏swap,運(yùn)行完后r0、r1的值交換了
一般可以把宏寫(xiě)在宏文件(.mac文件)中,在程序里用INCLUDE指令包含宏文件

?

?

?

?


?
最超值的ARM7/ARM9開(kāi)發(fā)板系列
AVR單片機(jī)開(kāi)發(fā)板與仿真器
?
本章節(jié)主要介紹ARM 處理器的基本程序設(shè)計(jì)方法,包含ARM 指令實(shí)驗(yàn),Thumb 指令實(shí)驗(yàn)和ARM 處理器工作模式實(shí)驗(yàn)。

4.1 ARM 指令實(shí)驗(yàn)
4.1.1 實(shí)驗(yàn)說(shuō)明
? 實(shí)驗(yàn)?zāi)康?#xff1a;??? 透過(guò)實(shí)驗(yàn)掌握ARM 組譯指令的使用方法。


???????? 實(shí)驗(yàn)設(shè)備: 硬件使用PC 主機(jī),軟件使用Embest IDE 2003 整合開(kāi)發(fā)環(huán)境,Windows 98/2000/NT/XP。

???????? 實(shí)驗(yàn)內(nèi)容:??? 使用簡(jiǎn)單ARM 組譯指令,操作寄存器和內(nèi)存區(qū)作互相的數(shù)據(jù)交換。

4.1.2 實(shí)驗(yàn)原理
ARM 處理器共有37個(gè)寄存器:31個(gè)通用寄存器,包括程序計(jì)數(shù)器(PC)。這些寄存器都是32 位的。6個(gè)狀態(tài)寄存器。這些寄存器也是32 位的,但是只是使用了其中的12 位。

ARM 通用寄存器

通用寄存器(R0~R15)可分為3 類(lèi):

不分組寄存器R0~R7;

分組寄存器R8~R14;

程序計(jì)數(shù)器R15。

?

1)不分組寄存器R0~R7

R0~R7 是不分組寄存器。這意味著在所有處理器模式下,它們都存取一樣的32 位寄存器。它們是真正的通用寄存器,沒(méi)有架構(gòu)所隱含的特殊用途。

?

2)分組寄存器R8~R14

R8~R14 是分組寄存器。它們存取的物理寄存器取決于當(dāng)前的處理器模式。若要存取特定的物理寄存器而不依賴(lài)當(dāng)前的處理器模式,則要使用規(guī)定的各字。

寄存器R8~R12 各有兩組物理寄存器:一組為FIQ 模式,另一組為除了FIQ以外的所有模式。寄存器R8~R12 沒(méi)有任何指定的特殊用途。只是使用R8~R14來(lái)簡(jiǎn)單地處理中斷。寄存器R13,R14 各有6 個(gè)分組的物理寄存器。1 個(gè)用于用戶(hù)模式和系統(tǒng)模式,其它5 個(gè)分別用于5 種異常模式。寄存器R13 通常用做堆迭指標(biāo),稱(chēng)為SP。每種異常模式都有自己的R13。寄存器R14 用作子程序鏈接寄存器,也稱(chēng)為L(zhǎng)R。

?

3) 程序計(jì)數(shù)器R15

寄存器R15 用做程序計(jì)數(shù)器(PC)。程序狀態(tài)寄存器在所有處理器模式下都可以存取當(dāng)前的程序狀態(tài)寄存器CPSR。CPSR 包含條件碼標(biāo)志位,中斷禁止位,當(dāng)前處理器模式以及其它狀態(tài)和控制信息。每種異常模式都有一個(gè)程序狀態(tài)保存寄存器SPSR。當(dāng)例外出現(xiàn)時(shí),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ù)指令可以測(cè)試這些條件碼標(biāo)志以決定程序指令如何執(zhí)行

控制位:最低8 位I,F,T 和M 位用做控制位。當(dāng)異常出現(xiàn)時(shí)改變控制位。當(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
?用戶(hù)模式
?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)寄存器的其它位保留,用作以后的擴(kuò)充。

?

4.1.3. 實(shí)驗(yàn)操作步驟
1. 執(zhí)行ADS1.2開(kāi)發(fā)環(huán)境,打開(kāi)實(shí)驗(yàn)系統(tǒng)例程目錄下ARMcode_test 子目錄下的ARMcode.mcp 工程文件。

2. 透過(guò)操作菜單欄或使用快捷命令編譯鏈接項(xiàng)目。

3. 選擇Debug 菜單Remote Connect 進(jìn)行連接軟件仿真器,執(zhí)行Download命令下載程序,并打開(kāi)寄存器窗口。

4. 單步執(zhí)行程序并觀察和記錄寄存器R0-R15 的值變化。

5. 結(jié)合實(shí)驗(yàn)內(nèi)容和相關(guān)數(shù)據(jù),觀察程序執(zhí)行,透過(guò)實(shí)驗(yàn)加深理解ARM指令的使用。

?

?

?

4.1.4? 試驗(yàn)程序代碼
;本程序?qū)?shù)據(jù)區(qū)從數(shù)據(jù)區(qū)SRC復(fù)制到目標(biāo)數(shù)據(jù)區(qū)DST。復(fù)制時(shí),以8個(gè)字節(jié)為單位進(jìn)行。對(duì)于

;最后所剩不足的8個(gè)字節(jié)的數(shù)據(jù),以字為單位進(jìn)行復(fù)制,這時(shí)程序跳轉(zhuǎn)到copywords處執(zhí)行。

;在進(jìn)行以8個(gè)字為單位的數(shù)據(jù)復(fù)制時(shí),保存了所有的8個(gè)工作寄存器。

?

;設(shè)置本段程序的名稱(chēng)(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)識(shí)的地址放入R0)

?????? LDR?????? r0,=src???

?

;r1寄存器指向目標(biāo)數(shù)據(jù)區(qū)(DST 標(biāo)識(shí)的地址放入R1)??????

?????? LDR?????? r1,=dst???

??????

;r2指定將要復(fù)制的字?jǐn)?shù)(裝載num 的值到R2)??

?????? MOV????? r2, #num

?????????????

;設(shè)置數(shù)據(jù)棧指針(R13),用于保存工作寄存器數(shù)值(設(shè)定SP堆棧開(kāi)始地址為0x400)

?????? MOV????? sp, #0x400

?

?????????????

;進(jìn)行以8個(gè)字節(jié)為單位的數(shù)據(jù)復(fù)制

blockcopy

?

;需要進(jìn)行的以8個(gè)字為單位的復(fù)制次數(shù)( R2 右移3 位后的值放入R3)

?????? MOVS r3,r2, LSR #3????

;對(duì)于剩下的不足8個(gè)字的數(shù)據(jù),跳轉(zhuǎn)到copywords,以字為單位復(fù)制(判斷是否為0,為0 跳移)

??????

?????? BEQ copywords??????????????????

;保存工作寄存器(把R4 到R11 的值保存到SP 標(biāo)識(shí)的堆棧中)

?????? STMFD sp!, {r4-r11}?????????

?

octcopy

;從數(shù)據(jù)區(qū)讀取8個(gè)字節(jié)的數(shù)據(jù),放到8個(gè)寄存器中,并更新目標(biāo)數(shù)據(jù)區(qū)指針r0(把R0 中的地址標(biāo)識(shí)的內(nèi)容順序裝載到R4 到R11 中)

?

?????? LDMIA r0!, {r4-r11}??????????

;將這8個(gè)字?jǐn)?shù)據(jù)寫(xiě)入到目標(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 計(jì)數(shù))

?????? SUBS r3, r3, #1??????????????????

?

;循環(huán),直到完成以8個(gè)字為單位的塊復(fù)制

?????? BNE octcopy??????????????????????

?

;需要注意的是,LDMIA 或者STMIA 指令執(zhí)行后,R0,R1 的值產(chǎn)生變化,每一次寄存器操作,R0 或者R1 的值會(huì)自動(dòng)增加一個(gè)字節(jié)的量,這里操作了8 個(gè)寄存器,R0 或者R1 的值也相應(yīng)增加了8 個(gè)字節(jié)

?

?

;恢復(fù)工作寄存器值(把剛才保存的SP 堆棧中的值恢復(fù)到R4 到R11 中)

?????? LDMFD sp!, {r4-r11}??

???????

copywords

;剩下不足8個(gè)字的數(shù)據(jù)的字?jǐn)?shù)(邏輯與,把R2 前7 位扔掉)

?????? ANDS r2, r2, #7??????????

?

;數(shù)據(jù)復(fù)制完成(判斷是否為0,為0 跳移)

?????? BEQ stop???????????????????????????

?

wordcopy

;從源數(shù)據(jù)區(qū)讀取18個(gè)字的數(shù)據(jù),放到r3寄存器中,并更新目標(biāo)數(shù)據(jù)區(qū)指針r0(把R0 表示地址的內(nèi)容的后4 位全部拷貝到R3)

?????? LDR r3, [r0], #4???

?

;將這r3中數(shù)據(jù)寫(xiě)入到目標(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 會(huì)自動(dòng)加上便宜量)???

?????? 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 實(shí)驗(yàn)練習(xí)題
1. 撰寫(xiě)程序循環(huán)對(duì)R4~R11 進(jìn)行8 次累加,R4~R11 起始值為1~8,每次加操作后把R4~R11 的內(nèi)容放入SP 堆棧中,SP 初始設(shè)定為0x800。最后把R4~R11 用LDMFD 指令清空值為0。

2. 對(duì)于每一種ARM 的尋址方法,簡(jiǎn)短的寫(xiě)出相對(duì)的應(yīng)用程序片段。

4.2 ARM 處理器工作模式實(shí)驗(yàn)
4.2.1. 實(shí)驗(yàn)說(shuō)明
???????? 實(shí)驗(yàn)?zāi)康?#xff1a;透過(guò)實(shí)驗(yàn)掌握ARM 處理器工作模式的切換。

???????? 實(shí)驗(yàn)設(shè)備:軟件需要ADS1.2開(kāi)發(fā)環(huán)境,Windows 98/2000/NT/XP。

???????? 實(shí)驗(yàn)內(nèi)容:透過(guò)ARM 組譯指令,在各種處理器模式下切換并觀察各種模式下緩存器的區(qū)別;掌握ARM 不同模式的進(jìn)入與退出。

4.2.2. 實(shí)驗(yàn)原理
ARM 處理器模式

ARM 架構(gòu)支持下表2-2 所列的7 種處理器模式。

工作模式
?描述
?
用戶(hù)模式(User,usr)
?正常程序執(zhí)行的模式
?
快速中斷模式(FIQ,fig)
?用于高速數(shù)據(jù)傳輸和數(shù)據(jù)處理
?
外部中斷模式(IRQ,irq)
?用于通常的中斷處理
?
特權(quán)模式(管理模式)(Supervisor,sve)
?共操作系統(tǒng)使用的一種模式
?
數(shù)據(jù)訪問(wèn)中止模式(Abort,abt)
?用于虛擬存儲(chǔ)及存儲(chǔ)保護(hù)
?
未定義指令中斷模式(Undefind,und)
?用于支持通過(guò)軟件方針的協(xié)處理器
?
系統(tǒng)模式(System,sys)
?用于運(yùn)行特權(quán)的操作系統(tǒng)任務(wù)
?

?

?

大多數(shù)應(yīng)用程序在用戶(hù)模式下執(zhí)行。當(dāng)處理器工作在用戶(hù)模式時(shí),正在執(zhí)行的程序不能存取某些被保護(hù)的系統(tǒng)資源,也不能改變模式,除非例外(exception)發(fā)生。這允許適當(dāng)撰寫(xiě)操作系統(tǒng)來(lái)控制系統(tǒng)資源的使用。

除用戶(hù)模式外的其它模式稱(chēng)未特權(quán)模式。它們可以自由的存取系統(tǒng)資源和改變模式。其中的5 種稱(chēng)為異常模式,即

n???????? FIQ(Fash Interrupt request);

n???????? IRQ(Interrupt ReQuest);

n???????? 管理(Supervisor);

n???????? 中止(Abort) ;

n???????? 未定義(Underfined) 。

當(dāng)特定的異常出現(xiàn)時(shí),進(jìn)入相應(yīng)的模式。每種模式都有某些附加的寄存器,以避免異常出現(xiàn)時(shí)用戶(hù)模式的狀態(tài)不可靠。

剩下的模式是系統(tǒng)模式。僅ARM 架構(gòu)V4 以及以上的版本有該模式。不能由于任何異常而進(jìn)入該模式。它與用戶(hù)模式有完全相同的寄存器。然而它是特權(quán)模式,不受用戶(hù)模式的限制。它供需要存取系統(tǒng)資源的操作系統(tǒng)工作使用,單希望避免使用與例外模式有關(guān)的附加寄存器。避免使用附加寄存器保證了當(dāng)任何異常出現(xiàn)時(shí),都不會(huì)使工作的狀態(tài)不可靠。

4.2.3. 實(shí)驗(yàn)操作步驟
1.開(kāi)發(fā)環(huán)境,打開(kāi)實(shí)驗(yàn)系統(tǒng)例程目錄下ARMMode_test/ARMMode.MCP 項(xiàng)目,并編譯鏈接項(xiàng)目。

3. 單步執(zhí)行程序并觀察和記錄CPSP 和SPSR 緩存器值的變化;并觀察在相應(yīng)模式下執(zhí)行程序后對(duì)應(yīng)緩存器值的變化。

4. 結(jié)合實(shí)驗(yàn)內(nèi)容和相關(guān)數(shù)據(jù),觀察程序執(zhí)行,透過(guò)實(shí)驗(yàn)加深理解和掌握

4.2.4? 試驗(yàn)程序代碼
;設(shè)置本段程序的名稱(chēng)(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???????? ;對(duì)系統(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 ;跳移到最開(kāi)始地方循環(huán)

?????? END

4.2.5. 實(shí)驗(yàn)練習(xí)題
1. 參考例子,把其中系統(tǒng)模式程序更改為使用者模式程序,編譯除錯(cuò),觀察執(zhí)行結(jié)果,檢查是否正確,如果有有錯(cuò)誤分析其原因。(提示:不能從使用者模式直接切換到其它模式,可以先使用SWI 指令切換到管理模式)。

?

?

?

?

?

?

?

?


? 山東大學(xué)嵌入式系統(tǒng)原理與接口技術(shù)課程試卷(A)?????????????????????? 2007——2008 學(xué)年???? 1? 學(xué)期

題號(hào) 一 二 三 四 五 六 七 八 九 十 總分
得分??????????????????????


?
?

得分 閱卷人
???


?
?
?
?

單項(xiàng)選擇題(每空2分,共10分)
?
1、對(duì)寄存器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ù)寫(xiě)操作時(shí),可能Cache未命中,根據(jù)Cache執(zhí)行的操作不同,將Cache分為兩類(lèi)(?????????? )

①數(shù)據(jù)Cache和指令Cache?? ②統(tǒng)一Cache和獨(dú)立Cache? ③寫(xiě)通Cache和寫(xiě)回Cache? ④讀操作分配Cache和寫(xiě)操作分配Cache

5、一個(gè)異步傳輸過(guò)程:設(shè)每個(gè)字符對(duì)應(yīng)8個(gè)信息位、偶校驗(yàn)、2個(gè)停止位,如果波特率為2400,那么每秒鐘能傳輸?shù)淖畲笞址麛?shù)為(???????????????? )個(gè)。

① 200,② 218,③ 240,④ 2400
?得分 閱卷人
???


?
?
?
?
二、填空(共18分)
1、嵌入式處理器可分為以下4類(lèi):(???????????????????????????????????????? )。

2、ARM處理器總共有(? )個(gè)寄存器,這些寄存器按其在用戶(hù)編程中的功能可劃分為:(????????? )和(??????? ),這些寄存器根據(jù)ARM處理器不同工作模式,可將全部寄存器分成( )組,在使用中有(?????????????????????????????????????? )特點(diǎn)。

3、ARM 4種存儲(chǔ)周期的基本類(lèi)型分別為:(???????????????????????????????????? )。

4、S3C44B0X UART單元發(fā)送器能夠檢測(cè)的四種異步串行通信數(shù)據(jù)錯(cuò)誤為(????????????????????????????????????????????????????????????????????????? )。


得分 閱卷人
???


?
?

簡(jiǎn)答題:(每空6分,共30分)
1、從硬件系統(tǒng)來(lái)看,嵌入式系統(tǒng)由哪幾部分組成?畫(huà)出簡(jiǎn)圖。

?

?

?

?

?

?

?

?

?


基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開(kāi)發(fā)要點(diǎn)(一)
—— 嵌入式程序開(kāi)發(fā)過(guò)程
ARM 系列微處理器作為全球16/32位RISC處理器市場(chǎng)的領(lǐng)先者,在許多領(lǐng)
域內(nèi)得到了成功的應(yīng)用.近年來(lái),ARM在國(guó)內(nèi)的應(yīng)用也得到了飛速的發(fā)展,越
來(lái)越多的公司和工程師在基于ARM的平臺(tái)上面開(kāi)發(fā)自己的產(chǎn)品.
與傳統(tǒng)的4/8位單片機(jī)相比,ARM的性能和處理能力當(dāng)然是遙遙領(lǐng)先的,但
與之相應(yīng),ARM的系統(tǒng)設(shè)計(jì)復(fù)雜度和難度,較之傳統(tǒng)的設(shè)計(jì)方法也大大提升了.
本文旨在通過(guò)討論系統(tǒng)程序設(shè)計(jì)中的幾個(gè)基本方面,來(lái)說(shuō)明基于ARM的嵌入式
系統(tǒng)程序開(kāi)發(fā)的一些特點(diǎn),并提出和解決了一些常見(jiàn)的問(wèn)題.
文章分成幾個(gè)相對(duì)獨(dú)立的章節(jié)刊載.第一部分討論基于ARM的嵌入式程序
開(kāi)發(fā)和移植過(guò)程中的一些基本概念.
1.嵌入式程序開(kāi)發(fā)過(guò)程
不同于通用計(jì)算機(jī)和工作站上的軟件開(kāi)發(fā)工程,一個(gè)嵌入式程序的開(kāi)發(fā)過(guò)程
具有很多特點(diǎn)和不確定性.其中最重要的一點(diǎn)是軟件跟硬件的緊密耦合特性.
(不帶操作系統(tǒng)支持) (帶操作系統(tǒng)支持)
圖-1:兩類(lèi)不同的嵌入式系統(tǒng)結(jié)構(gòu)模型
這是兩類(lèi)簡(jiǎn)化的嵌入式系統(tǒng)層次結(jié)構(gòu)圖.由于嵌入式系統(tǒng)的靈活性和多樣
性,上面圖中各個(gè)層次之間缺乏統(tǒng)一的標(biāo)準(zhǔn),幾乎每一個(gè)獨(dú)立的系統(tǒng)都不一樣.
這樣就給上層的軟件設(shè)計(jì)人員帶來(lái)了極大地困難.第一,在軟件設(shè)計(jì)過(guò)程中過(guò)多
地考慮硬件,給開(kāi)發(fā)和調(diào)試都帶來(lái)了很多不便;第二,如果所有的軟件工作都需
要在硬件平臺(tái)就緒之后進(jìn)行,自然就延長(zhǎng)了整個(gè)的系統(tǒng)開(kāi)發(fā)周期.這些都是應(yīng)該
從方法上加以改進(jìn)和避免的問(wèn)題.
為了解決這個(gè)問(wèn)題,工程和設(shè)計(jì)人員提出了許多對(duì)策.首先在應(yīng)用與驅(qū)動(dòng)(或
API)這一層接口,可以設(shè)計(jì)成相對(duì)統(tǒng)一的一些接口函數(shù),這對(duì)于具體的某一個(gè)
開(kāi)發(fā)平臺(tái)或在某個(gè)公司內(nèi)部,是完全做得到的.這樣一來(lái),就大大提高了應(yīng)用層
應(yīng)用(Application)
驅(qū)動(dòng)/板級(jí)支持包
(Driver/BSP)
硬件(Hardware)
應(yīng)用(Application)
硬件抽象層(HAL)
硬件(Hardware)
操作系統(tǒng)(OS)
標(biāo)準(zhǔn)接口函數(shù)(API)
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
軟件設(shè)計(jì)的標(biāo)準(zhǔn)化程度,方便了應(yīng)用程序在跨平臺(tái)之間的復(fù)用和移植.
對(duì)于驅(qū)動(dòng)/硬件抽象這一層,因?yàn)橹苯域?qū)動(dòng)硬件,其標(biāo)準(zhǔn)化變得非常困難甚至
不太可能.但是為了簡(jiǎn)化程序的調(diào)試和縮短開(kāi)發(fā)周期,我們可以在特定的EDA
工具環(huán)境下面進(jìn)行開(kāi)發(fā),通過(guò)后再進(jìn)行移植到硬件平臺(tái)的工作.這樣既可以保證
程序邏輯設(shè)計(jì)的正確性,同時(shí)使得軟件開(kāi)發(fā)可平行甚至超前于硬件開(kāi)發(fā)進(jìn)程.
我們把脫離于硬件的嵌入式軟件開(kāi)發(fā)階段稱(chēng)之為"PC軟件"的開(kāi)發(fā),可以
用下面的圖來(lái)示意一個(gè)嵌入式系統(tǒng)程序的開(kāi)發(fā)過(guò)程.
"PC軟件"開(kāi)發(fā) 移植,測(cè)試 產(chǎn)品發(fā)布
圖-2:嵌入式系統(tǒng)產(chǎn)品的開(kāi)發(fā)過(guò)程
在"PC軟件"開(kāi)發(fā)階段,可以用軟件仿真,即指令集模擬的方法,來(lái)對(duì)用
戶(hù)程序進(jìn)行驗(yàn)證.在ARM公司的開(kāi)發(fā)工具中,ADS 內(nèi)嵌的ARMulator和
RealView 開(kāi)發(fā)工具中的ISS,都提供了這項(xiàng)功能.在模擬環(huán)境下,用戶(hù)可以設(shè)
置ARM處理器的型號(hào),時(shí)鐘頻率等,同時(shí)還可以配置存儲(chǔ)器訪問(wèn)接口的時(shí)序參
數(shù).程序在模擬環(huán)境下運(yùn)行,不但能夠進(jìn)行程序的運(yùn)行流程和邏輯測(cè)試,還能夠
統(tǒng)計(jì)系統(tǒng)運(yùn)行的時(shí)鐘周期數(shù),存儲(chǔ)器訪問(wèn)周期數(shù),處理器運(yùn)行時(shí)的流水線(xiàn)狀態(tài)(有
效周期,等待周期,連續(xù)和非連續(xù)訪問(wèn)周期)等信息.這些寶貴的信息是在硬件
調(diào)試階段都無(wú)法取得的,對(duì)于程序的性能評(píng)估非常有價(jià)值.
為了更加完整和真實(shí)地模擬一個(gè)目標(biāo)系統(tǒng),ARMulator和ISS還提供了一個(gè)
開(kāi)放的API編程環(huán)境.用戶(hù)可以用標(biāo)準(zhǔn)C來(lái)描述各種各樣的硬件模塊,連同工
具提供的內(nèi)核模塊一起,組成一個(gè)完整的"軟"硬件環(huán)境.在這個(gè)環(huán)境下面開(kāi)發(fā)
的軟件,可以更大程度地接近最終的目標(biāo).
利用這種先進(jìn)的EDA工具環(huán)境,極大地方便了程序開(kāi)發(fā)人員進(jìn)行嵌入式開(kāi)
發(fā)的工作.當(dāng)完成一個(gè)"PC軟件"的開(kāi)發(fā)之后,只要進(jìn)行正確的移植,一個(gè)真
正的嵌入式軟件就開(kāi)發(fā)成功了.而移植過(guò)程是相對(duì)比較容易形成一套規(guī)范的流程
的,其中三個(gè)最重要的方面是:
考慮硬件對(duì)庫(kù)函數(shù)的支持
移植
移植
開(kāi)發(fā)/實(shí)驗(yàn)/
測(cè)試平臺(tái)
最終產(chǎn)品
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
符合目標(biāo)系統(tǒng)上的存儲(chǔ)器資源分布
應(yīng)用程序運(yùn)行環(huán)境的初始化
2.開(kāi)發(fā)工具環(huán)境里面的庫(kù)函數(shù)
如果用戶(hù)程序里調(diào)用了跟目標(biāo)相關(guān)的一些庫(kù)函數(shù),則在應(yīng)用前需要裁剪這些
函數(shù)以適合在目標(biāo)上允許的要求.主要需要考慮以下三類(lèi)函數(shù):
訪問(wèn)靜態(tài)數(shù)據(jù)的函數(shù)
訪問(wèn)目標(biāo)存儲(chǔ)器的函數(shù)
使用semihosting(半主機(jī))機(jī)制實(shí)現(xiàn)的函數(shù)
這里所指的C庫(kù)函數(shù),除了ISO C標(biāo)準(zhǔn)里面定義的函數(shù)以外,還包括由編
譯工具提供的另外一些擴(kuò)展函數(shù)和編譯輔助函數(shù).
2.1 裁剪訪問(wèn)靜態(tài)數(shù)據(jù)的函數(shù)
庫(kù)函數(shù)里面的靜態(tài)數(shù)據(jù),基本上都是在頭文件里面加以定義的.比如CTYPE
類(lèi)庫(kù)函數(shù),其返回值都是通過(guò)預(yù)定義好的CTYPE屬性表來(lái)獲得的.比如,想要
改變isalpha() 函數(shù)的缺省判斷,則需要修改對(duì)應(yīng)CTYPE屬性表里對(duì)字符屬性的
定義.
2.2 裁減訪問(wèn)目標(biāo)存儲(chǔ)器的函數(shù)
有一類(lèi)動(dòng)態(tài)內(nèi)存管理函數(shù),如malloc() 等,其本身是獨(dú)立于目標(biāo)系統(tǒng)而運(yùn)行
的;但是它所使用的存儲(chǔ)器空間需要根據(jù)目標(biāo)來(lái)確定.所以malloc() 函數(shù)本身
并不需要裁剪或移植,但那些設(shè)置動(dòng)態(tài)內(nèi)存區(qū)(地址和空間)的函數(shù)則是跟目標(biāo)
系統(tǒng)的存儲(chǔ)器分布直接相關(guān)的,需要進(jìn)行移植.例如堆棧的初始化函數(shù)
__user_initial_stackheap(),是用來(lái)設(shè)置堆(heap)和棧(stack)地址的函數(shù),顯
然針對(duì)每一個(gè)具體的目標(biāo)平臺(tái),該函數(shù)都需要根據(jù)具體的目標(biāo)存儲(chǔ)器資源進(jìn)行正
確移植.
下面是對(duì)示例函數(shù)__user_initial_stackheap() 進(jìn)行移植的一個(gè)例子:
__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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
請(qǐng)注意上面的函數(shù)體并不完全遵循標(biāo)準(zhǔn)C的關(guān)鍵字和語(yǔ)法規(guī)范,使用了ARM
公司編譯器(ADS 或RealView Compilation tool) 里的C語(yǔ)言擴(kuò)展特性.關(guān)于編譯
器特定的C語(yǔ)言擴(kuò)展,請(qǐng)參考相關(guān)的編譯器說(shuō)明,這里簡(jiǎn)單介紹函數(shù)
__user_initial_stackheap() 的功能,它主要是返回堆和棧的基地址.上面的程序中
只對(duì)堆(heap) 的基地址進(jìn)行了設(shè)置(設(shè)成了0x11110000),也就是說(shuō)用戶(hù)把
0x11110000開(kāi)始的存儲(chǔ)器地址用作了動(dòng)態(tài)內(nèi)存分配區(qū)(heap區(qū)).具體地址的確
定是要由用戶(hù)根據(jù)自己的目標(biāo)系統(tǒng)和應(yīng)用情況來(lái)確定的,至少要滿(mǎn)足以下條件:
0x11110000開(kāi)始的地址空間有效且可寫(xiě)(是RAM)
該存儲(chǔ)器空間不與其它功能區(qū)沖突(比如代碼區(qū),數(shù)據(jù)區(qū),stack區(qū)等)
因?yàn)開(kāi)_user_initial_stackheap() 函數(shù)的全部執(zhí)行效果就是返回一些數(shù)值,所
以只要符合接口的調(diào)用標(biāo)準(zhǔn),直接用匯編來(lái)實(shí)現(xiàn)看起來(lái)更加直觀一些:
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR r0,0x11110000
MOV pc,lr
如果不對(duì)這個(gè)函數(shù)進(jìn)行移植,編譯過(guò)程中將使用缺省的設(shè)置,這個(gè)設(shè)置適用
于ARM公司的Integrator系列平臺(tái).
(注意:ARM的編譯/連接工具鏈也提供了繞過(guò)庫(kù)函數(shù)來(lái)設(shè)置運(yùn)行時(shí)存儲(chǔ)器模型
的方法,請(qǐng)參閱ARM公司其他的相關(guān)文檔.)
2.3 裁剪使用semihosting(半主機(jī))機(jī)制實(shí)現(xiàn)的函數(shù)
庫(kù)函數(shù)里有一大部分函數(shù)是涉及到輸入/輸出流設(shè)備的,比如文件操作函數(shù)需
要訪問(wèn)磁盤(pán)I/O,打印函數(shù)需要訪問(wèn)字符輸出設(shè)備等.在嵌入式調(diào)試環(huán)境下,所
有的標(biāo)準(zhǔn)C庫(kù)函數(shù)都是有效且有其缺省行為的,很多目標(biāo)系統(tǒng)硬件不能支持的
操作,都通過(guò)調(diào)試工具來(lái)完成了.比如printf() 函數(shù),缺省的輸出設(shè)備是調(diào)試器
里面的信息輸出窗口.
但是一個(gè)真實(shí)的系統(tǒng)是需要脫離調(diào)試工具而獨(dú)立運(yùn)行的,所以在程序的移植
過(guò)程當(dāng)中,需先對(duì)這些庫(kù)函數(shù)的運(yùn)行機(jī)制作一了解.
下圖說(shuō)明了在ADS下面這類(lèi)C庫(kù)函數(shù)的結(jié)構(gòu).
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
圖-3:C庫(kù)函數(shù)實(shí)現(xiàn)過(guò)程中的層次調(diào)用
如圖中例子所示,函數(shù)printf() 最終是調(diào)用了底層的輸入/輸出函數(shù)
_sys_write() 來(lái)實(shí)現(xiàn)輸出操作的,而_sys_write() 使用了調(diào)試工具的內(nèi)部機(jī)制來(lái)把
信息輸出到調(diào)試器.
顯然這樣的函數(shù)調(diào)用過(guò)程在一個(gè)真實(shí)的嵌入式系統(tǒng)里是無(wú)法實(shí)現(xiàn)的,因?yàn)楠?dú)
立運(yùn)行的嵌入式系統(tǒng)將不會(huì)有調(diào)試器的參與.如果在最終系統(tǒng)中仍然要保留
printf() 函數(shù),而且在系統(tǒng)硬件中具備正確的輸出設(shè)備(如LCD等),則在移植
過(guò)程中,需要把printf() 調(diào)用的輸出設(shè)備進(jìn)行重新定向.
考察printf() 函數(shù)的完整調(diào)用過(guò)程:
圖-4:printf() 的調(diào)用過(guò)程
單純考慮printf() 的輸出重新定向,可以有三種途徑實(shí)現(xiàn):
改寫(xiě)printf() 本身
改寫(xiě) fput()
改寫(xiě) _sys_write()
需要注意的是,越底層的函數(shù),被其他上層函數(shù)調(diào)用的可能性越大,改變了
一個(gè)底層函數(shù)的實(shí)現(xiàn),則所有調(diào)用該函數(shù)的上層函數(shù)的行為都被改變了.
以fputc() 的重新實(shí)現(xiàn)為例,下面是改變fputc() 輸出設(shè)備到系統(tǒng)串行通信端
口的實(shí)例:
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ū)動(dòng)程序級(jí)
使用semihosting
機(jī)制
如_sys_write()
由調(diào)試系統(tǒng)執(zhí)行
printf() fput() _sys_wite()輸出設(shè)備
其他函數(shù) 其他函數(shù)
C 庫(kù)函數(shù)
調(diào)試輔助環(huán)境
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
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ū)動(dòng)函數(shù).只要新建函數(shù)
fput() 的接口符合標(biāo)準(zhǔn),經(jīng)過(guò)編譯連接后,該函數(shù)實(shí)現(xiàn)就覆蓋了原來(lái)缺省的函數(shù)
體,所有對(duì)該函數(shù)的調(diào)用,其行為都被新實(shí)現(xiàn)的函數(shù)所重新定向了.
具體哪些庫(kù)函數(shù)是跟目標(biāo)相關(guān)的,這些函數(shù)之間的相互調(diào)用關(guān)系等,請(qǐng)參考
具體的編譯器說(shuō)明.
3.Semihosting (半主機(jī)) 機(jī)制
上面提到許多庫(kù)函數(shù)在調(diào)試環(huán)境下的實(shí)現(xiàn)都調(diào)用了一種叫semihosting的機(jī)
制.Semihosting具體來(lái)講是指一種讓代碼在ARM 目標(biāo)上運(yùn)行,但使用運(yùn)行了
ARM 調(diào)試器的主機(jī)上I/O 設(shè)備的方法;也就是讓ARM 目標(biāo)將輸入/ 輸出請(qǐng)求
從應(yīng)用程序代碼傳遞到運(yùn)行調(diào)試器的主機(jī)的一種機(jī)制.通常這些輸入/輸出設(shè)備
包括鍵盤(pán),屏幕和磁盤(pán)I/O.
半主機(jī)由一組已定義的SWI 操作來(lái)實(shí)現(xiàn).庫(kù)函數(shù)調(diào)用相應(yīng)的SWI(軟件中
斷),然后調(diào)試代理程序處理SWI 異常,并提供所需的與主機(jī)之間的通訊.
圖-5:Semihosting的實(shí)現(xiàn)過(guò)程
多數(shù)情況下,半主機(jī)SWI 是由庫(kù)函數(shù)內(nèi)的代碼調(diào)用的.但是應(yīng)用程序也可
以直接調(diào)用半主機(jī)SWI.半主機(jī)SWI 的接口函數(shù)是通用的.當(dāng)半主機(jī)操作在硬
件仿真器,指令集仿真器,RealMonitor或Angel下執(zhí)行時(shí),不需要進(jìn)行移植處
理.
使用單個(gè)SWI 編號(hào)請(qǐng)求半主機(jī)操作.其它的SWI 編號(hào)可供應(yīng)用程序或操
printf()
printf("Hello world! ");
SWI
調(diào)試器
Hello world!
C 庫(kù)代碼
應(yīng)用程序代碼
與運(yùn)行在主機(jī)上的調(diào)試器通信
主機(jī)屏幕上顯示的文本
目標(biāo)
主機(jī)
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
作系統(tǒng)使用.用于半主機(jī)的SWI號(hào)是:
在ARM 狀態(tài)下:0x123456
在Thumb 狀態(tài)下:0xAB
SWI 編號(hào)向調(diào)試代理程序指示該SWI 請(qǐng)求是半主機(jī)請(qǐng)求.要辨別具體的操
作類(lèi)型,用寄存器r0 作為參數(shù)傳遞.r0 傳遞的可用半主機(jī)操作編號(hào)分配如下:
0x00-0x31:這些編號(hào)由ARM 公司使用,分別對(duì)應(yīng)32個(gè)具體的執(zhí)行函
數(shù).
0x32-0xFF:這些編號(hào)由ARM 公司保留,以備將來(lái)用作函數(shù)擴(kuò)展.
0x100-0x1FF:這些編號(hào)保留給用戶(hù)應(yīng)用程序.但是,如果編寫(xiě)自己的
SWI 操作,建議直接使用SWI指令和SWI編號(hào),而不要使用半主機(jī)
SWI 編號(hào)加這些操作類(lèi)型編號(hào)的方法.
0x200-0xFFFFFFFF:這些編號(hào)未定義.當(dāng)前未使用并且不推薦使用這
些編號(hào).
半主機(jī)SWI使用的軟件中斷編號(hào)也可以由用戶(hù)自定義,但若是改變了缺省
的軟中斷編號(hào),需要:
更改系統(tǒng)中所有代碼(包括庫(kù)代碼)的半主機(jī)SWI 調(diào)用
重新配置調(diào)試器對(duì)半主機(jī)請(qǐng)求的捕捉與相應(yīng)
這樣才能使用新的SWI 編號(hào).
有關(guān)半主機(jī)SWI處理函數(shù)實(shí)現(xiàn)的更詳細(xì)信息,請(qǐng)參考ARM編譯器的相關(guān)
文檔.
4.應(yīng)用環(huán)境的初始化和根據(jù)目標(biāo)系統(tǒng)資源進(jìn)行的移植
在下一期中介紹應(yīng)用環(huán)境和目標(biāo)系統(tǒng)的初始化.
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開(kāi)發(fā)要點(diǎn)(二)
—— 系統(tǒng)的初始化過(guò)程
基于ARM的芯片多數(shù)為復(fù)雜的片上系統(tǒng)集成(SoC),這種復(fù)雜的系統(tǒng)里多
數(shù)的硬件模塊都是可配置的,需要由軟件來(lái)設(shè)置其需要的工作狀態(tài).因此在用戶(hù)
的應(yīng)用程序啟動(dòng)之前,需要有專(zhuān)門(mén)的一段啟動(dòng)代碼來(lái)完成對(duì)系統(tǒng)的初始化.由于
這類(lèi)代碼直接面對(duì)處理器內(nèi)核和硬件控制器進(jìn)行編程,一般都使用匯編語(yǔ)言.系
統(tǒng)啟動(dòng)程序所執(zhí)行的操作跟具體的目標(biāo)系統(tǒng)和開(kāi)發(fā)系統(tǒng)相關(guān),一般通用的內(nèi)容包
括:
中斷向量表
初始化存儲(chǔ)器系統(tǒng)
初始化堆棧
初始化有特殊要求的端口,設(shè)備
初始化應(yīng)用程序執(zhí)行環(huán)境
改變處理器模式
呼叫主應(yīng)用程序
1.中斷向量表
ARM要求中斷向量表必須放置在從0地址開(kāi)始,連續(xù)8×4字節(jié)的空間內(nèi)
(ARM720T和ARM9/10及以后的ARM處理器也支持從0xFFFF0000開(kāi)始的高
地址向量表,在本文的其他地方對(duì)此不再另加說(shuō)明).各個(gè)中斷矢量在向量表中
的位置分配如下圖:
圖1:中斷向量表
每當(dāng)一個(gè)中斷發(fā)生以后,ARM處理器便強(qiáng)制把PC指針置為向量表中對(duì)應(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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
斷類(lèi)型的地址值.因?yàn)槊總€(gè)中斷只占據(jù)向量表中1個(gè)字的存儲(chǔ)器空間,只能放置
1條ARM指令,所以通常在向量表中放的是跳轉(zhuǎn)指令,使程序能從向量表里跳
轉(zhuǎn)到存儲(chǔ)器里的其他地方,再執(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是指定編譯器保留這段代碼,因?yàn)榫幾g器可能會(huì)認(rèn)為
這是一段冗余代碼而加以?xún)?yōu)化.連接的時(shí)候要確保這段代碼被鏈接在0地址處,
并且作為整個(gè)程序的入口點(diǎn)(關(guān)鍵字ENTRY并非總是用來(lái)設(shè)置程序入口點(diǎn),所
以通常需要在連接選項(xiàng)里顯式地指定程序入口點(diǎn)).
2.初始化存儲(chǔ)器系統(tǒng)
初始化存儲(chǔ)器系統(tǒng)的編程對(duì)象是系統(tǒng)的存儲(chǔ)器控制器.存儲(chǔ)器控制器并不是
ARM內(nèi)核的一部分,不同的系統(tǒng)其設(shè)計(jì)不盡相同,所以應(yīng)該針對(duì)具體的要求來(lái)
完成這部分的程序設(shè)計(jì).一般來(lái)說(shuō),下面這兩個(gè)方面是比較通用的.
2.1.存儲(chǔ)器類(lèi)型和時(shí)序配置
一個(gè)復(fù)雜的系統(tǒng)可能存在多種存儲(chǔ)器類(lèi)型的接口,需要根據(jù)實(shí)際的系統(tǒng)設(shè)計(jì)
對(duì)此加以正確配置.對(duì)同一種存儲(chǔ)器類(lèi)型來(lái)說(shuō),也因?yàn)樵L問(wèn)速度的差異,需要不
同的時(shí)序設(shè)置.
通常Flash 和SRAM同屬于靜態(tài)存儲(chǔ)器類(lèi)型,可以合用同一個(gè)存儲(chǔ)器端口;
而DRAM 因?yàn)閯?dòng)態(tài)刷新和地址線(xiàn)復(fù)用等特性,通常配有專(zhuān)用的存儲(chǔ)器端口.
存儲(chǔ)器端口的接口時(shí)序優(yōu)化是非常重要的,影響到整個(gè)系統(tǒng)的性能.因?yàn)橐?br /> 般系統(tǒng)運(yùn)行的速度瓶頸都存在于存儲(chǔ)器訪問(wèn),所以存儲(chǔ)器訪問(wèn)時(shí)序應(yīng)盡可能地
快;但同時(shí)又要考慮由此帶來(lái)的穩(wěn)定性問(wèn)題.只有根據(jù)具體選定的芯片,進(jìn)行多
次的測(cè)試之后,才能確定最佳的時(shí)序配置.
2.2.存儲(chǔ)器地址分布(memory map)
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
有些系統(tǒng)具有非常靈活的存儲(chǔ)器地址分配特性,進(jìn)行存儲(chǔ)器初始化設(shè)計(jì)的時(shí)
候一定要根據(jù)應(yīng)用程序的具體要求來(lái)完成地址分配.
一種典型的情況是啟動(dòng)ROM的地址重映射(remap).如前面第1節(jié)所述,
當(dāng)一個(gè)系統(tǒng)上電后程序?qū)⒆詣?dòng)從0地址處開(kāi)始執(zhí)行,因此在系統(tǒng)的初始狀態(tài),必
須保證在0地址處存在正確的代碼,即要求0地址開(kāi)始處的存儲(chǔ)器是非易性的
ROM或Flash等.但是因?yàn)镽OM或Flash的訪問(wèn)速度相對(duì)較慢,每次中斷發(fā)生
后都要從讀取ROM或Flash上面的向量表開(kāi)始,影響了中斷響應(yīng)速度.因此有
的系統(tǒng)便提供一種靈活的地址重映射方法,可以把0地址重新指向到RAM中去.
在這種地址映射的變化過(guò)程當(dāng)中,程序員需要仔細(xì)考慮的是程序的執(zhí)行流程不能
被這種變化所打斷.比如下面這種情況:
圖2:啟動(dòng)ROM的地址重映射對(duì)程序執(zhí)行流程的影響
系統(tǒng)上電后從Flash內(nèi)的0地址開(kāi)始執(zhí)行,啟動(dòng)代碼位于地址0x100開(kāi)始的
空間,當(dāng)執(zhí)行到地址0x200時(shí),完成了一次地址的重映射,把原來(lái)0開(kāi)始的地址
空間由Flash轉(zhuǎn)給了RAM.接下去執(zhí)行的指令(這里為了簡(jiǎn)化起見(jiàn),忽略流水
線(xiàn)指令預(yù)取的模型)將來(lái)自從0x204開(kāi)始的RAM空間.如果預(yù)先沒(méi)有對(duì)RAM
內(nèi)容進(jìn)行正確的設(shè)置,則里面的數(shù)據(jù)都是隨機(jī)的,這樣處理器在執(zhí)行完0x200
地址處的指令之后,再往下取指執(zhí)行就會(huì)出錯(cuò).解決的方法就是要使RAM在使
用之前準(zhǔn)備好正確的內(nèi)容,包括開(kāi)頭的向量表部分.
有的系統(tǒng)不具備存儲(chǔ)器地址重映射的功能,所有的空間地址就相對(duì)簡(jiǎn)單一
些,不需要考慮這方面的問(wèn)題.
3.初始化堆棧
因?yàn)锳RM處理器有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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
是獨(dú)立的(System和User模式使用相同的SP寄存器).因此對(duì)程序中需要用到
的每一種模式都要給SP寄存器定義一個(gè)堆棧地址.方法是改變狀態(tài)寄存器CPSR
內(nèi)的狀態(tài)位,使處理器切換到不同的狀態(tài),然后給SP賦值.注意不要切換到User
模式進(jìn)行User模式的堆棧設(shè)置,因?yàn)檫M(jìn)入U(xiǎn)ser模式后就不能再操作CPSR回到
別的模式了.可能會(huì)對(duì)接下去的程序執(zhí)行造成影響.
一般堆棧的大小要根據(jù)需要而定,但是要盡可能給堆棧分配快速和高帶寬的
存儲(chǔ)器.堆棧性能的提高對(duì)系統(tǒng)整體性能的影響是非常明顯的.
這是一段堆棧初始化的代碼示例,其中只定義了三種模式的SP指針:
MRS R0, CPSR ; CPSR -> R0
BIC R0, R0, #MODEMASK ; 安全起見(jiàn),屏蔽模式位以外的其它位
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個(gè)SP寄存器是不同的物理寄存器:SP_irq,
SP_fiq和SP_svc.引用的幾個(gè)標(biāo)號(hào)假設(shè)已經(jīng)正確定義.
4.初始化有特殊要求的端口,設(shè)備
這要由具體的系統(tǒng)和用戶(hù)需求而定.一般的外設(shè)初始化可以在系統(tǒng)初始化之
后進(jìn)行.
比較典型的應(yīng)用是驅(qū)動(dòng)一些簡(jiǎn)單的輸出設(shè)備,如LED等,來(lái)指示系統(tǒng)啟動(dòng)
的進(jìn)程和狀態(tài).
5.初始化應(yīng)用程序執(zhí)行環(huán)境
一個(gè)簡(jiǎn)單的可執(zhí)行程序的映像結(jié)構(gòu)通常如下:
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
圖3:程序映像的結(jié)構(gòu)
映像一開(kāi)始總是存儲(chǔ)在ROM/Flash里面的,其RO部分既可以在ROM/Flash
里面執(zhí)行,也可以轉(zhuǎn)移到速度更快的RAM中去;而RW和ZI這兩部分必須是
需要轉(zhuǎn)移到可寫(xiě)的RAM里去的.所謂應(yīng)用程序執(zhí)行環(huán)境的初始化,就是完成必
要的從ROM到RAM的數(shù)據(jù)傳輸和內(nèi)容清零.
不同的工具鏈會(huì)提供一些不同的機(jī)制和方法幫助用戶(hù)完成這一步操作,主要
是跟鏈接器(Linker)相關(guān).下面是在ARM開(kāi)發(fā)工具環(huán)境(ADS或RVCT)下,
一種常用存儲(chǔ)器模型的直接實(shí)現(xiàn):
LDR r0, =|Image

RO Limit ; Get pointer to ROM data
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個(gè)符號(hào)
是由連接器(linker)定義輸出的:
|Image RO Limit|:表示RO區(qū)末地址后面的地址,即RW數(shù)據(jù)源的起始地址.
|Image RW Base|:RW區(qū)在RAM里的執(zhí)行區(qū)起始地址,也就是編譯選項(xiàng)
RW_Base指定的地址;程序里是RW數(shù)據(jù)拷貝的目標(biāo)地址.
ZI (Zero initialized R/W Data)
RW (R/W Data)
RO (Code + RO Data)
編譯結(jié)果
定義時(shí)帶初始值的全局變量
只定義了變量名的全局變量
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
|Image ZI Base|:ZI區(qū)在RAM里面的起始地址.
|Image ZI Limit|:ZI區(qū)在RAM里面的結(jié)束地址后面的一個(gè)地址.
程序先把ROM里 |Image RO Limit| 開(kāi)始的RW初始數(shù)據(jù)拷貝到RAM里
|Image RW Base| 開(kāi)始的地址,當(dāng)RAM這邊的目標(biāo)地址到達(dá)
|Image ZI Base| 后就表示RW區(qū)的結(jié)束和ZI區(qū)的開(kāi)始,接下去就對(duì)這片ZI
區(qū)進(jìn)行清零操作,直到遇到結(jié)束地址 |Image ZI Limit|.
6.改變處理器模式
ARM處理器(V4架構(gòu)以后的版本)一共有7種執(zhí)行模式:
User: 用戶(hù)模式
FIQ: 快速中斷響應(yīng)模式
IRQ: 一般中斷響應(yīng)模式
Supervisor:超級(jí)模式
Abort: 出錯(cuò)處理模式
Undef: 未定義模式
System: 系統(tǒng)模式
除用戶(hù)模式以外,其他6種模式都是特權(quán)模式.因?yàn)樵诔跏蓟^(guò)程中許多操
作需要在特權(quán)模式下才能進(jìn)行(比如CPSR的修改),所以要特別注意不能過(guò)早
地進(jìn)入用戶(hù)模式.一般地,在初始化過(guò)程中會(huì)經(jīng)歷以下一些模式變化:
圖4:處理器模式變換過(guò)程
在最后階段才把模式轉(zhuǎn)換到最終應(yīng)用程序運(yùn)行所需的模式,一般是用戶(hù)模
式.
內(nèi)核級(jí)的中斷使能(CPSR的I,F位狀態(tài))也可以考慮在這一步進(jìn)行.如果
系統(tǒng)中另外存在一個(gè)專(zhuān)門(mén)的中斷控制器(多數(shù)情況下是這樣的),這么做總是安
全的,否則就需要考慮過(guò)早地打開(kāi)中斷可能帶來(lái)的問(wèn)題,比如在系統(tǒng)初始化完成
之前就觸發(fā)了有效中斷,導(dǎo)致系統(tǒng)的死機(jī).
7.呼叫主應(yīng)用程序
當(dāng)所有的系統(tǒng)初始化工作完成之后,就需要把程序流程轉(zhuǎn)入主應(yīng)用程序.最
簡(jiǎn)單的一種情況是:
復(fù)位后的缺省模式 注意不要進(jìn)入用戶(hù)模式 用戶(hù)選擇
(堆棧初始化階段)
超級(jí)模式
(Supervisor)
多種特權(quán)模式
變化
設(shè)置成用戶(hù)程
序運(yùn)行模式
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
IMPORT main ; get the label main if main() is defined in other files
B man ; jump to main()
直接從啟動(dòng)代碼跳入應(yīng)用程序主函數(shù)入口,主函數(shù)名字可由用戶(hù)自己定義.
在ARM ADS環(huán)境中,還另外提供了一套系統(tǒng)級(jí)的呼叫機(jī)制.
IMPORT __main
B __main
__main()
圖5:在應(yīng)用程序主函數(shù)之前插入__main
__main() 是編譯系統(tǒng)提供的一個(gè)函數(shù),負(fù)責(zé)完成庫(kù)函數(shù)的初始化和第5節(jié)中
所描述的功能,最后自動(dòng)跳向main() 函數(shù).這種情況下用戶(hù)程序的主函數(shù)名字
必須得是main.
用戶(hù)可以根據(jù)需要選擇是否使用__main().如果想讓系統(tǒng)自動(dòng)完成系統(tǒng)調(diào)用
(如庫(kù)函數(shù))的初始化過(guò)程,可以直接使用__main();如果所有的初始化步驟都
是由用戶(hù)自己顯式地完成,則可以跳過(guò)__main().
當(dāng)然,使用__main() 的時(shí)候,可能會(huì)涉及到一些庫(kù)函數(shù)的移植和重定向問(wèn)
題.在__main() 里面的程序執(zhí)行流程如下圖所示:
圖6:有系統(tǒng)調(diào)用參與的程序執(zhí)行流程
關(guān)于在__main() 里面調(diào)用到的庫(kù)函數(shù)說(shuō)明,可以參閱相關(guān)的編譯器文檔,
庫(kù)函數(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
啟動(dòng)代碼 應(yīng)用程序初始化用戶(hù)應(yīng)用程序main()
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開(kāi)發(fā)要點(diǎn)(三)
—— 如何滿(mǎn)足嵌入式系統(tǒng)的靈活需求
因?yàn)榍度胧綉?yīng)用領(lǐng)域的多樣性,每一個(gè)系統(tǒng)都具有各自的特點(diǎn).在進(jìn)行系統(tǒng)
程序設(shè)計(jì)的時(shí)候,一定要進(jìn)行具體分析,充分利用這些特點(diǎn),揚(yáng)長(zhǎng)避短.
結(jié)合ARM架構(gòu)本身的一些特點(diǎn),在這里討論幾個(gè)常見(jiàn)的要點(diǎn).
1.ARM還是Thumb
在討論ARM還是Thumb之前,先說(shuō)明ARM內(nèi)核型號(hào)和ARM結(jié)構(gòu)體系之
間的區(qū)別和聯(lián)系.
圖-1 ARM結(jié)構(gòu)體系和處理器家族的演變發(fā)展
如圖-1所示,ARM的結(jié)構(gòu)體系主要從版本4開(kāi)始,發(fā)展到了現(xiàn)在的版本6,
結(jié)構(gòu)體系的變化,對(duì)程序員而言最直接的影響就是指令集的變化.結(jié)構(gòu)體系的演
變意味著指令集的不斷擴(kuò)展,值得慶幸的是ARM結(jié)構(gòu)體系的發(fā)展一直保持了向
上兼容,不會(huì)造成老版本程序在新結(jié)構(gòu)體系上的不兼容.
在圖中的橫坐標(biāo)上,顯示了每一個(gè)體系結(jié)構(gòu)上都含有眾多的處理器型號(hào),這
是在同一體系結(jié)構(gòu)下根據(jù)硬件配置和存儲(chǔ)器系統(tǒng)的不同而作的進(jìn)一步細(xì)分.需要
注意的是通常我們用來(lái)區(qū)分ARM處理器家族的ARM7,ARM9或ARM10,可
能跨越不同的體系結(jié)構(gòu).
在ARM的體系結(jié)構(gòu)版本4與5中,還可以再細(xì)分出幾個(gè)小的擴(kuò)展版本:V4T,
V5TE和V5TEJ,其區(qū)別如圖-2中所示,這些后綴名也反映在各自擁有的處理器
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
型號(hào)上面,可以進(jìn)行直觀的分辨.V6結(jié)構(gòu)體系因?yàn)榘艘郧鞍姹镜乃刑匦?
所以不需要再進(jìn)行分類(lèi).
圖-2 結(jié)構(gòu)體系特征
上面介紹了整個(gè)ARM處理器家族的分布,主要是說(shuō)明在一個(gè)特定的平臺(tái)上
編寫(xiě)程序的時(shí)候,一定要先弄清楚目標(biāo)的特性和一些細(xì)微的差別,特別是需要具
體優(yōu)化特征的時(shí)候.
從ARM體系結(jié)構(gòu)V4T以后,最大的變化是增加了一套16位的指令集——
Thumb.到底在一個(gè)具體應(yīng)用中要否采用Thumb呢 首先我們來(lái)分析一下ARM
和Thumb各自的特點(diǎn)和優(yōu)勢(shì).先看下面一張性能分析圖:
圖-3 ARM和Thumb指令集的比較
圖中的縱坐標(biāo)是測(cè)試向量Dhrystone在20MHz頻率下運(yùn)行1秒鐘的結(jié)果,其
值越大表明性能越好;橫坐標(biāo)是系統(tǒng)存儲(chǔ)器系統(tǒng)的數(shù)據(jù)總線(xiàn)寬度.結(jié)果表明:
(a) 當(dāng)系統(tǒng)具有32位的數(shù)據(jù)總線(xiàn)寬度時(shí),ARM比Thumb有更好的性能表現(xiàn).
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
(b) 當(dāng)系統(tǒng)的數(shù)據(jù)總線(xiàn)寬度小于32位時(shí),Thumb比ARM的性能更好.
由此可見(jiàn),并不是32位的ARM指令集性能一定強(qiáng)于16位的Thumb指令集,
要具體情況具體分析.考察個(gè)中的原因,其實(shí)不難發(fā)現(xiàn),因?yàn)楫?dāng)在一個(gè)16位存
儲(chǔ)器系統(tǒng)里面取1條32位指令的時(shí)候,需要耗費(fèi)2個(gè)存儲(chǔ)器訪問(wèn)周期;比之32
位的系統(tǒng),其速度正好大概下降一半左右.而16位指令在32位存儲(chǔ)器系統(tǒng)或
16位存儲(chǔ)器系統(tǒng)里的表現(xiàn)基本相同.正是存儲(chǔ)器造成的系統(tǒng)瓶頸導(dǎo)致了這個(gè)有
趣的差別.
除了在窄帶寬系統(tǒng)里面的性能優(yōu)勢(shì)外,Thumb指令的另外一個(gè)好處是代碼尺
寸.同樣一段C代碼,用Thumb指令編譯的結(jié)果,其長(zhǎng)度大約只占ARM編譯
結(jié)果的65%左右,可以明顯地節(jié)省存儲(chǔ)器空間.在大多數(shù)情況下,緊湊的代碼和
窄帶寬的存儲(chǔ)器系統(tǒng),還會(huì)帶來(lái)功耗上的優(yōu)勢(shì).
當(dāng)然,如果在32位的系統(tǒng)上面,并且對(duì)系統(tǒng)性能要求很高的情況下,ARM
是一個(gè)更好的選擇.畢竟在這種情況下,只有32位的指令集才能完全發(fā)揮32
位處理器的優(yōu)勢(shì)來(lái).
因此,選擇ARM還是Thumb,需要從存儲(chǔ)器開(kāi)銷(xiāo)和性能要求兩方面加以權(quán)
衡考慮.
2.堆棧的分配
在圖-3中,橫坐標(biāo)上還有一種情況,就是16位的存儲(chǔ)器寬度,但是堆棧空
間是32位的.這種情況下無(wú)論ARM還是Thumb,其性能表現(xiàn)都比單純的16位
存儲(chǔ)器系統(tǒng)情況下要好.這是因?yàn)锳RM和Thumb其指令集雖然分32位和16
位,但是堆棧全部是采用32位的.因此在16位堆棧和32位堆棧的不同環(huán)境下,
其性能當(dāng)然都會(huì)相差很多.這種差別還跟具體的應(yīng)用程序密切相關(guān),如果一個(gè)程
序堆棧的使用頻率相當(dāng)高,則這種性能差異很大;反之則要小一些.
在基于ARM的系統(tǒng)中,堆棧不僅僅被用來(lái)進(jìn)行諸如函數(shù)調(diào)用,中斷響應(yīng)等
時(shí)候的現(xiàn)場(chǎng)保護(hù),還是程序局部變量和函數(shù)參數(shù)傳遞(如果大于4個(gè))的存儲(chǔ)空
間.所以出于系統(tǒng)整體性能考慮,要給堆棧分配相對(duì)訪問(wèn)速度最快,數(shù)據(jù)寬度最
大的存儲(chǔ)器空間.
一個(gè)嵌入式系統(tǒng)通常存在多種多樣的存儲(chǔ)器類(lèi)型.設(shè)計(jì)的時(shí)候一定要先清楚
每一種存儲(chǔ)器的訪問(wèn)速度,地址分配和數(shù)據(jù)線(xiàn)寬度.然后根據(jù)不同程序和目標(biāo)模
塊對(duì)存儲(chǔ)器的不同要求進(jìn)行合理分配,以期達(dá)到最佳配置狀態(tài).
3.ROM還是RAM在0地址處
顯然當(dāng)系統(tǒng)剛啟動(dòng)的時(shí)候,0地址處肯定是某種類(lèi)型的ROM,里面存儲(chǔ)了系
統(tǒng)的啟動(dòng)代碼.但是很多靈活的系統(tǒng)設(shè)計(jì)中,0地址處的存儲(chǔ)器類(lèi)型是可映射的.
也就是說(shuō),可以通過(guò)軟件的方法,把別的存儲(chǔ)器(主要是快速的RAM)分配以
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
0起始的地址.
這種做法的最主要目的之一是提高系統(tǒng)對(duì)中斷的反應(yīng)速度.因?yàn)槊恳粋€(gè)中斷
發(fā)生的時(shí)候,ARM都需要從0地址處的中斷向量表開(kāi)始其中斷響應(yīng)流程,顯然
把中斷向量表放在RAM里,比放在ROM里有更快的訪問(wèn)速度.因此,如果系
統(tǒng)提供了這一類(lèi)的地址重映射功能,軟件設(shè)計(jì)者一定要加以利用.
下面是一個(gè)典型的經(jīng)過(guò)0地址重映射之后的存儲(chǔ)空間分布圖,注意盡可能把
速度要求最高的部分放置在系統(tǒng)里面訪問(wèn)速度最快,帶寬最寬的RAM里面.
圖-4 系統(tǒng)存儲(chǔ)器分布的實(shí)例
4.存儲(chǔ)器地址重映射(memory remap)
存儲(chǔ)器地址重映射是當(dāng)前很多先進(jìn)控制器所具有的功能.在上一節(jié)中已經(jīng)提
到了0地址處存儲(chǔ)器重映射的例子,簡(jiǎn)而言之,地址重映射就是可以通過(guò)軟件配
置來(lái)改變一塊存儲(chǔ)器物理地址的一種機(jī)制或方法.
當(dāng)一段程序?qū)\(yùn)行自己的存儲(chǔ)器進(jìn)行重映射的時(shí)候,需要特別注意保證程序
執(zhí)行流程在重映射前后的承接關(guān)系.下面是一種典型的存儲(chǔ)器地址重映射情況:
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 里運(yùn)行的代碼
外設(shè)寄存器
變量區(qū)和動(dòng)態(tài)內(nèi)存分配區(qū)
需要快速響應(yīng)的代碼和數(shù)據(jù)
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
圖-5 存儲(chǔ)器重映射舉例1
系統(tǒng)上電后的缺省狀態(tài)是0地址上放有ROM,這塊ROM有兩個(gè)地址:從0
起始和從0x10000起始,里面存儲(chǔ)了初始化代碼.當(dāng)進(jìn)行地址remap以后,從0
起始的地址被定向到了RAM上,ROM則只保留有唯一的從0x10000起始的地
址了.
如果存儲(chǔ)在ROM 里的Reset_Handler一直在0 - 0x4000的地址上運(yùn)行,則
當(dāng)執(zhí)行完remap以后,下面的指令將從RAM里預(yù)取,必然會(huì)導(dǎo)致程序執(zhí)行流程
的中斷.根據(jù)系統(tǒng)特點(diǎn),可以用下面的辦法來(lái)解決這個(gè)問(wèn)題:
(1) 上電后系統(tǒng)從0地址開(kāi)始自動(dòng)執(zhí)行,設(shè)計(jì)跳轉(zhuǎn)指令在remap發(fā)生前使PC
指針指向0x10000開(kāi)始的ROM地址中去,因?yàn)椴煌刂分赶虻氖峭粔K
ROM,所以程序能夠順利執(zhí)行.
(2) 這時(shí)候0 - 0x4000的地址空間空閑,不被程序引用,執(zhí)行remap后把RAM
引進(jìn).因?yàn)槌绦蛞恢痹?x10000起始的ROM空間里運(yùn)行,remap對(duì)運(yùn)行
流程沒(méi)有任何影響.
(3) 通過(guò)在ROM里運(yùn)行的程序,對(duì)RAM進(jìn)行相應(yīng)的代碼和數(shù)據(jù)拷貝,完成
應(yīng)用程序運(yùn)行的初始化.
下面是一段實(shí)現(xiàn)上述步驟的例程:
-------------------------------------------------------------------------------------------------------
ENTRY
;啟動(dòng)時(shí),從0開(kāi)始,設(shè)法跳轉(zhuǎn)到"真"的ROM地址(0x10000開(kāi)始的空間里)
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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
LDR r1, =Ctrl_reg ;假定控制remap的寄存器
LDR r0, [r1]
ORR r0, r0, #Remap_bit ;假定對(duì)控制寄存器進(jìn)行remap設(shè)置
STR r0, [r1]
;接下去可以進(jìn)行從ROM到RAM的代碼和數(shù)據(jù)拷貝
-------------------------------------------------------------------------------------------------------
除此之外,還有另外一種常見(jiàn)的remap方式,如下圖:
圖-6 存儲(chǔ)器重映射舉例2
原來(lái)RAM和ROM各有自己的地址,進(jìn)行重映射以后RAM和ROM的地址
都發(fā)生了變化,這種情況下,可以采用以下的方案:
(1) 上電后,從0地址的ROM開(kāi)始往下執(zhí)行.
(2) 根據(jù)映射前的地址,對(duì)RAM進(jìn)行必要的代碼和數(shù)據(jù)拷貝.
(3) 拷貝完成后,進(jìn)行remap操作.
(4) 因?yàn)镽AM在remap前準(zhǔn)備好了內(nèi)容,使得PC指針能繼續(xù)在RAM里取
到正確的指令.
不同的系統(tǒng)可能會(huì)有多種靈活的remap方案,根據(jù)上面提到的兩個(gè)例子,可
以總結(jié)出最根本的考慮是:要使程序指針在remap以后能繼續(xù)往下得到正確的指
令.
5. 根據(jù)目標(biāo)存儲(chǔ)器系統(tǒng)分散加載映像(scatterloading)
Scatterloading文件是ARM的工具鏈里面的一個(gè)特性,作為程序編譯過(guò)程中
給連接器使用的一個(gè)參數(shù),用來(lái)指定最終生成的目標(biāo)映像文件運(yùn)行時(shí)的分布狀
態(tài).如果用戶(hù)程序映像只是如圖7所示的最簡(jiǎn)狀態(tài),所有的可執(zhí)行代碼都集合放
置在一起,那么可以不使用Scatterloading文件,直接用連接器的命令行選項(xiàng)就
remap
0x20000
0x4000
=
0x4000
0x0000
Reset Handler
Vectors
0x4000
0x0000
RAMROM
0x10000
0x10400
RAM ROM
0x20400
Vectors
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
能夠完成設(shè)置:
RO = 0x00000:表示映像的第一條指令開(kāi)始地址;
RW = 0x10000:表示變量區(qū)的起始地址,變量區(qū)一定要位于RAM區(qū).
圖-7 簡(jiǎn)單的映像分布舉例
但是一個(gè)復(fù)雜的系統(tǒng)可能會(huì)把映像分割成幾個(gè)部分.如圖8,系統(tǒng)中存在多
種類(lèi)型的存儲(chǔ)器,不能的代碼部分根據(jù)執(zhí)行性能優(yōu)化的考慮分布與不同的地方.
圖-8 復(fù)雜的映像分布舉例
這時(shí)候不能通過(guò)簡(jiǎn)單的RO,RW參數(shù)來(lái)完成實(shí)現(xiàn)上述配置,就要用到
scatterloading文件了.在scatterloading文件里,可以給編譯出來(lái)的各個(gè)目標(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ū)和動(dòng)態(tài)內(nèi)存分配區(qū)
普通程序區(qū)
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
指定運(yùn)行地址,下面的例子是針對(duì)圖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ì)語(yǔ)法,請(qǐng)參閱ARM公司的相關(guān)手冊(cè).
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開(kāi)發(fā)要點(diǎn)(四)
—— 異常處理機(jī)制的設(shè)計(jì)
異常或中斷是用戶(hù)程序中最基本的一種執(zhí)行流程或形態(tài),這部分對(duì)ARM架
構(gòu)下異常處理程序的編寫(xiě)作一個(gè)全面的介紹.
ARM一共有7種類(lèi)型的異常,按優(yōu)先級(jí)從高到低排列如下:
Reset
Data Abort
FIQ
IRQ
Prefetch Abort
SWI
Undefined instruction
請(qǐng)注意在ARM的文檔中,使用術(shù)語(yǔ)exception 來(lái)描述異常.Exception主要
是從處理器被動(dòng)接受異常的角度出發(fā)描述,而interrupt帶有向處理器主動(dòng)申請(qǐng)的
色彩.在本文中,對(duì)"異常"和"中斷"不作嚴(yán)格區(qū)分,都是指請(qǐng)求處理器打斷
正常的程序執(zhí)行流程,進(jìn)入特定程序循環(huán)的一種機(jī)制.
1.異常響應(yīng)流程
如以前介紹異常向量表時(shí)所提到過(guò)的,每一個(gè)異常發(fā)生時(shí),總是從異常向量
表開(kāi)始起跳的,最簡(jiǎn)單的一種情況是:
圖-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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
向量表里面的每一條指令直接跳向?qū)?yīng)的異常處理函數(shù).其中FIQ_Handler()
可以直接從地址0x1C處開(kāi)始,省下一條跳轉(zhuǎn)指令.
但是當(dāng)執(zhí)行跳轉(zhuǎn)的時(shí)候有2個(gè)問(wèn)題需要討論:跳轉(zhuǎn)范圍和異常分支.
1.1 跳轉(zhuǎn)范圍
我們知道ARM的跳轉(zhuǎn)指令(B)是有范圍限制的(±32MB),但很多情況
下不能保證所有的異常處理函數(shù)都定位在向量表的32MB范圍內(nèi),需要大于
32MB的長(zhǎng)跳轉(zhuǎn),而且因?yàn)橄蛄勘砜臻g的限制只能由一條指令完成.這可以通過(guò)
下面二種方法實(shí)現(xiàn).
(a) MOV PC, #imme_value
把目標(biāo)地址直接賦給PC寄存器.
但是這條指令受格式限制并不能處理任意立即數(shù),只有當(dāng)這個(gè)立即數(shù)能夠
表示為一個(gè)8-bit數(shù)值通過(guò)循環(huán)右移偶數(shù)位而得到,才是合法的.例如:
MOV PC, #0x30000000 是合法的,因?yàn)?x300000000可以通過(guò)0x03循
環(huán)右移4位而得到.
而 MOV PC, #30003000 就是非法指令.
(b) LDR PC, [PC+offset]
把目標(biāo)地址先存儲(chǔ)在某一個(gè)合適的地址空間,然后把這個(gè)存儲(chǔ)器單元上的32
位數(shù)據(jù)傳送給PC來(lái)實(shí)現(xiàn)跳轉(zhuǎn).
這種方法對(duì)目標(biāo)地址值沒(méi)有要求,可以是任意有效地址.但是存儲(chǔ)目標(biāo)地址
的存儲(chǔ)器單元必須在當(dāng)前指令的±4KB空間范圍內(nèi).
注意在計(jì)算指令中引用的offset數(shù)值的時(shí)候,要考慮處理器流水線(xiàn)中指令預(yù)
取對(duì)PC值的影響,以圖-2的情況為例:
offset = address location - vector address - pipeline effect
= 0xFFC - 0x4 - 0x8
= 0xFF0
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
圖-2 利Literal pool實(shí)現(xiàn)跳轉(zhuǎn)
1.2 異常分支
ARM內(nèi)核只有二個(gè)外部中斷輸入信號(hào)nFIQ和nIRQ,但對(duì)于一個(gè)系統(tǒng)來(lái)說(shuō),
中斷源可能多達(dá)幾十個(gè).為此,在系統(tǒng)集成的時(shí)候,一般都會(huì)有一個(gè)異常控制器
來(lái)處理異常信號(hào).
圖-3 中斷系統(tǒng)
這時(shí)候,用戶(hù)程序可能存在多個(gè)IRQ/FIQ的中斷處理函數(shù),為了從向量表
開(kāi)始的跳轉(zhuǎn)最終能找到正確的處理函數(shù)入口,需要設(shè)計(jì)一套處理機(jī)制和方法.
圖-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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
(a) 硬件處理
有的系統(tǒng)在ARM的異常向量表之外,又增加了一張由中斷控制器控制的特
殊向量表.當(dāng)由外設(shè)觸發(fā)一個(gè)中斷以后,PC能夠自動(dòng)跳到這張?zhí)厥庀蛄勘碇腥?
特殊向量表中的每個(gè)向量空間對(duì)應(yīng)一個(gè)具體的中斷源.
舉例來(lái)說(shuō),下面的系統(tǒng)一共有20個(gè)外設(shè)中斷源,特殊向量表被直接放置在
普通向量表后面.
圖-5 額外的硬件異常向量表
當(dāng)某個(gè)外部中斷觸發(fā)之后,首先觸發(fā)ARM的內(nèi)核異常,中斷控制器檢測(cè)到
ARM的這種狀態(tài)變化,再通過(guò)識(shí)別具體的中斷源,使PC自動(dòng)跳轉(zhuǎn)到特殊向量
表中的對(duì)應(yīng)地址,從而開(kāi)始一次異常響應(yīng).需要檢查具體的芯片說(shuō)明,是否支持
這類(lèi)特性.
(b) 軟件處理
多數(shù)情況下是用軟件來(lái)處理異常分支.因?yàn)檐浖梢酝ㄟ^(guò)讀取中斷控制器來(lái)
獲得中斷源的詳細(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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
因?yàn)檐浖O(shè)計(jì)的靈活性,用戶(hù)可以設(shè)計(jì)出比上圖更好的流程控制方法來(lái).下
面是一個(gè)例子:
圖-7 靈活的軟件分支設(shè)計(jì)
Int_vector_table是用戶(hù)自己開(kāi)辟的一塊存儲(chǔ)器空間,里面按次序存放異常處
理函數(shù)的地址.IRQ_Handler()從中斷控制器獲取中斷源信息,然后再?gòu)?
Int_verctor_table中的對(duì)應(yīng)地址單元得到異常處理函數(shù)的入口地址,完成一次異
常響應(yīng)的跳轉(zhuǎn).這種方法的好處是用戶(hù)程序在運(yùn)行過(guò)程中,能夠很方便地動(dòng)態(tài)改
變異常服務(wù)內(nèi)容.
2.異常處理函數(shù)的設(shè)計(jì)
2.1 異常發(fā)生時(shí)處理器的動(dòng)作
當(dāng)任何一個(gè)異常發(fā)生并得到響應(yīng)時(shí),ARM內(nèi)核自動(dòng)完成以下動(dòng)作:
拷貝 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的嵌入式程序開(kāi)發(fā)要點(diǎn)
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)下,處理器都將
自動(dòng)進(jìn)入ARM狀態(tài).另一個(gè)需要注意的地方是中斷使能被自動(dòng)關(guān)閉,也就是說(shuō)
缺省情況下中斷是不可重入的.單純的把中斷使能位打開(kāi)接受重入的中斷會(huì)帶來(lái)
新的問(wèn)題,在第3部分中對(duì)此會(huì)有詳細(xì)介紹.
除這些自動(dòng)完成的動(dòng)作之外,如果在匯編級(jí)進(jìn)行手動(dòng)編程,還需要注意保存
必要的通用寄存器.
2.2 進(jìn)入異常處理循環(huán)后軟件的任務(wù)
進(jìn)入異常處理程序以后,用戶(hù)可以完全按照自己的意愿來(lái)進(jìn)行程序設(shè)計(jì),包
括調(diào)用Thumb狀態(tài)的函數(shù),等等.但是對(duì)于絕大多數(shù)的系統(tǒng)來(lái)說(shuō),有一個(gè)步驟
必須處理,就是要把中斷控制器中對(duì)應(yīng)的中斷狀態(tài)標(biāo)識(shí)清掉,表明該中斷請(qǐng)求已
經(jīng)得到響應(yīng).否則等退出中斷函數(shù)以后,又馬上會(huì)被再一次觸發(fā),從而進(jìn)入周而
復(fù)始的死循環(huán).
2.3 異常的返回
當(dāng)一個(gè)異常處理返回時(shí),一共有3件事情需要處理:通用寄存器的恢復(fù),狀
態(tài)寄存器的恢復(fù)以及PC指針的恢復(fù).
通用寄存器的恢復(fù)采用一般的堆棧操作指令,而PC和CPSR的恢復(fù)可以通
過(guò)一條指令來(lái)實(shí)現(xiàn),下面是3個(gè)例子:
MOVS pc, lr 或 SUBS pc, lr, #4 或LDMFD sp!, {pc}^
這幾條指令都是普通的數(shù)據(jù)處理指令,特殊之處就是把PC寄存器作為了目
標(biāo)寄存器,并且?guī)Я颂厥獾暮缶Y"S"或"^",在特權(quán)模式下,"S"或"^"的作
用就是使指令在執(zhí)行時(shí),同時(shí)完成從SPSR到CPSR的拷貝,達(dá)到恢復(fù)狀態(tài)寄存
器的目的.
異常返回時(shí)另一個(gè)非常重要的問(wèn)題是返回地址的確定.在2.1節(jié)中提到進(jìn)入
異常時(shí)處理器會(huì)有一個(gè)保存LR的動(dòng)作,但是該保存值并不一定是正確中斷的返
回地址.下面以一個(gè)簡(jiǎn)單的指令執(zhí)行流水狀態(tài)圖來(lái)對(duì)此加以說(shuō)明.
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
圖-8 ARM狀態(tài)下3級(jí)指令流水線(xiàn)執(zhí)行示例
我們知道在ARM架構(gòu)里,PC值指向當(dāng)前執(zhí)行指令的地址加8處.也就是說(shuō),
當(dāng)執(zhí)行指令A(yù)(地址0x8000)時(shí),PC等于指令C的地址(0x8008).假如指令
A是"BL"指令,則當(dāng)執(zhí)行時(shí),會(huì)把PC(=0x8008)保存到LR寄存器里面,但
是接下去處理器會(huì)馬上對(duì)LR進(jìn)行一個(gè)自動(dòng)的調(diào)整動(dòng)作:LR=LR-0x4.這樣,最
終保存在LR里面的是B指令的地址,所以當(dāng)從BL返回時(shí),LR里面正好是正
確的返回地址.
同樣的調(diào)整機(jī)制在所有LR自動(dòng)保存操作中都存在,比如進(jìn)入中斷響應(yīng)時(shí)處
理器所做的LR保存中,也進(jìn)行了一次自動(dòng)調(diào)整,并且調(diào)整動(dòng)作都是LR=LR-0x4.
由此我們來(lái)對(duì)不同異常類(lèi)型的返回地址進(jìn)行依次比較:
假設(shè)在指令B處(地址0x8004)發(fā)生了中斷響應(yīng),進(jìn)入中斷響應(yīng)后LR上經(jīng)
過(guò)調(diào)整保存的地址值應(yīng)該是C的地址0x8008.
(a) 如果發(fā)生的是軟件中斷,即B是"SWI"指令
從SWI中斷返回后下一條執(zhí)行指令就是C,正好是LR寄存器保存的地址,
所以只要直接把LR恢復(fù)給PC.
(b) 如果發(fā)生的是"IRQ"或"FIQ"等指令
因?yàn)橥獠恐袛嗾?qǐng)求中斷了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.
如果原來(lái)的指令執(zhí)行狀態(tài)是Thumb,異常返回地址的分析與此類(lèi)似,對(duì)LR
的調(diào)整正好與ARM狀態(tài)完全一致.
2.4 ARM編譯器對(duì)異常處理函數(shù)編寫(xiě)的擴(kuò)展
F D E
F D E
F D E
F D E
0x8000 A
0x8004 B
0x8008 C
0x800C D
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
考慮到異常處理函數(shù)在現(xiàn)場(chǎng)保護(hù)和返回地址的處理上與普通函數(shù)的不同之
處,不能直接把普通函數(shù)體連接到異常向量表上,需要在上面加一層封裝,下面
是一個(gè)例子:
IRQ_Handler ;中斷響應(yīng),從向量表直接跳來(lái)
STMFD SP!, {R0-R12, LR} ;保護(hù)現(xiàn)場(chǎng),一般只需保護(hù){r0-r3,lr}即可
BL IrqHandler ;進(jìn)入普通處理函數(shù),C或匯編均可
LDMFD SP!, {R0-R12, LR} ;恢復(fù)現(xiàn)場(chǎng)
SUBS PC, LR, #4 ;中斷返回,注意返回地址
為了方便使用高級(jí)語(yǔ)言直接編寫(xiě)異常處理函數(shù),ARM編譯器對(duì)此作了特定
的擴(kuò)展,可以使用函數(shù)聲明關(guān)鍵字__irq,這樣編譯出來(lái)的函數(shù)就滿(mǎn)足異常響應(yīng)
對(duì)現(xiàn)場(chǎng)保護(hù)和恢復(fù)的需要,并且自動(dòng)加入對(duì)LR進(jìn)行減4的處理,符合IRQ和
FIQ中斷處理的要求.
__irq void IRQ_Handler (void)
{…}
2.5 軟件中斷處理
軟件中斷由專(zhuān)門(mén)的軟中斷指令SWI觸發(fā),SWI指令后面跟一個(gè)中斷編號(hào),
以標(biāo)識(shí)可能共存的多個(gè)軟件中斷程序.
圖-9 軟件中斷處理流程
在C程序中調(diào)用軟件中斷需要用到編譯器的擴(kuò)展功能,使用關(guān)鍵字"__swi"
來(lái)聲明中斷函數(shù).注意軟中斷號(hào)碼同時(shí)在函數(shù)定義時(shí)指定.
__swi(0x24) void my_swi (void);
這樣當(dāng)調(diào)用函數(shù)my_swi的時(shí)候,就會(huì)用"SWI 0x24"來(lái)代替普通的函數(shù)調(diào)
用"BL my_swi".
分析圖9的流程,可以發(fā)現(xiàn)軟件中斷同樣存在著中斷分支的問(wèn)題,即需要根
據(jù)中斷號(hào)碼來(lái)決定調(diào)用不同的處理程序.軟中斷號(hào)碼只存在于SWI指令碼當(dāng)中,
因此需要在中斷處理程序中讀取觸發(fā)中斷的指令代碼,然后提取中斷號(hào)信息,再


SWI 0x01


用戶(hù)程序(C或匯編)

CMP swi_num
BEQ …
(Optional)
異常向量表 SWI處理程序(匯編)
SWI處理程序(C)
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
進(jìn)行進(jìn)一步處理.下面是軟中斷指令的編碼格式:
ARM狀態(tài)下的SWI指令編碼格式,32位長(zhǎng)度,其中低24位是中斷編號(hào).
Thumb狀態(tài)下的SWI指令編碼格式,16位長(zhǎng)度,其中低8位是中斷編號(hào).
圖-10 SWI指令編碼格式
為了在中斷處理程序里面得到SWI 指令的地址,可以利用LR寄存器.每
當(dāng)響應(yīng)一次SWI的時(shí)候,處理器都會(huì)自動(dòng)保存并調(diào)整LR寄存器,使里面的內(nèi)
容指向SWI下一條指令的地址,所以把LR里面的地址內(nèi)容上溯一條指令就是
所需的SWI指令地址.需要注意的一點(diǎn)是當(dāng)SWI指令的執(zhí)行狀態(tài)不同時(shí),其指
令地址間隔不一樣,如果進(jìn)入SWI執(zhí)行前是在ARM狀態(tài)下,需要通過(guò)LR-4來(lái)
獲得SWI指令地址,如果是在Thumb狀態(tài)下進(jìn)入,則只要LR-2就可以了.
下面是一段提取SWI中斷號(hào)碼的例程:
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)容是正確的軟中斷編號(hào)了
3.可重入中斷設(shè)計(jì)
如2.1節(jié)所述,缺省情況下ARM中斷是不可重入的,因?yàn)橐坏┻M(jìn)入異常響
應(yīng)狀態(tài),ARM自動(dòng)關(guān)閉中斷使能.如果在異常處理過(guò)程中簡(jiǎn)單地打開(kāi)中斷使能
而發(fā)生中斷嵌套,顯然新的異常處理將破壞原來(lái)的中斷現(xiàn)場(chǎng)而導(dǎo)致出錯(cuò).但有時(shí)
候中斷的可重入又是需要的,因此要能夠通過(guò)程序設(shè)計(jì)來(lái)解決這個(gè)問(wèn)題.其中有
二點(diǎn)是這個(gè)問(wèn)題的關(guān)鍵:
(a) 新中斷使能之前必須要保護(hù)好前一個(gè)中斷的現(xiàn)場(chǎng)信息,比如LR_irq和
SPSR_irq等,這一點(diǎn)容易想到也容易做到.
(b) 中斷處理過(guò)程中對(duì)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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
在中斷處理函數(shù)中發(fā)生函數(shù)調(diào)用(BL)是很常見(jiàn)的,假設(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(),這是一個(gè)
普通的異常處理函數(shù).但若是在IRQ_Handler() 里面中斷可重入的話(huà),則可能發(fā)
生問(wèn)題,考察下面的情況:
當(dāng)新的中斷請(qǐng)求恰好在"BL Foo"指令執(zhí)行完成后發(fā)生.
這時(shí)候LR寄存器(因在IRQ模式下,是LR_irq)的值將調(diào)整為BL指令的
下一條指令(ADD)地址,以期能從Foo() 正確返回;但是因?yàn)檫@時(shí)候發(fā)生了
中斷請(qǐng)求,接下去要進(jìn)行新中斷的響應(yīng),處理器為了能使新中斷處理完成后能正
確返回,也將進(jìn)行LR_irq保存.因?yàn)樾轮袛嗍窃谥噶盍?
BL Foo --> STMFD SP!, {R0-R3, LR}
執(zhí)行過(guò)程中插入的,完成跳轉(zhuǎn)操作后,進(jìn)行流水線(xiàn)刷新,最后LR_irq保存的是
STMFD后面一條指令的地址;這樣當(dāng)新中斷利用(PC = LR - 4)操作返回時(shí),
正好可以繼續(xù)原來(lái)的流程執(zhí)行STMFD指令.這二次對(duì)LR_irq的操作發(fā)生了沖
突,當(dāng)新中斷返回后往下執(zhí)行STMFD指令,這時(shí)候壓棧的LR已不是原來(lái)需要
的ADD指令的地址,從而使子程序Foo() 無(wú)法正確返回.
這個(gè)問(wèn)題無(wú)法通過(guò)增加額外的現(xiàn)場(chǎng)保護(hù)指令來(lái)解決.一個(gè)巧妙的辦法是在重
新使能中斷之前改變處理器的模式,也就是使上面程序中的"BL Foo"指令不
要運(yùn)行在IRQ模式下.這樣當(dāng)新中斷發(fā)生時(shí)就不會(huì)造成LR寄存器的沖突了.考
慮ARM的所有運(yùn)行模式,采用System模式是最恰當(dāng)?shù)?因?yàn)樗仁翘貦?quán)模式,
又與中斷響應(yīng)無(wú)關(guān).
所以一個(gè)完整的可重入中斷應(yīng)該遵循以下流程:
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
圖-11 可重入中斷處理流程
下面是一段實(shí)現(xiàn)的例程:
保護(hù)寄存器:LR,SPSR等
與中斷控制器通信(需要的話(huà))
切換到System狀態(tài),開(kāi)中斷使能
中斷處理(現(xiàn)在中斷可重入)
關(guān)閉中斷使能,切換回IRQ狀態(tài)
恢復(fù)寄存器:PC,CPSR等
進(jìn)入普通不可重入中斷處理
結(jié)束一次可重入中斷處理
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
基于ARM的嵌入式系統(tǒng)程序開(kāi)發(fā)要點(diǎn)(五)
—— ARM/Thumb的交互工作
在前面的文章中提到過(guò),很多情況下應(yīng)用程序需要在ARM跟Thumb狀態(tài)之
間相互切換,這部分就討論交互工作的實(shí)現(xiàn)方法和一些注意問(wèn)題.
1. 需要交互的原因
前面提到過(guò)Thumb指令在某些特殊情況下具有比ARM指令更為出色的表
現(xiàn),主要是在代碼長(zhǎng)度和窄帶寬存儲(chǔ)器系統(tǒng)性能兩方面.正因?yàn)門(mén)humb指令在
特定環(huán)境下面的優(yōu)勢(shì),它在很多方面得到了廣泛的應(yīng)用.但是因?yàn)橄旅嬉恍┰?
Thumb又不可能獨(dú)立地組成一個(gè)應(yīng)用系統(tǒng),所以不可避免地會(huì)產(chǎn)生ARM與
Thumb之間交互的問(wèn)題.
Thumb指令集在功能上只是ARM指令集的一個(gè)子集,某些功能只能在
ARM狀態(tài)下執(zhí)行,如CPSR和協(xié)處理器的訪問(wèn).
進(jìn)行異常響應(yīng)時(shí),處理器會(huì)自動(dòng)進(jìn)入ARM狀態(tài).
從系統(tǒng)優(yōu)化考慮,在寬帶存儲(chǔ)器上不應(yīng)該放置Thumb代碼,很多窄帶
系統(tǒng)具有寬帶的內(nèi)部存儲(chǔ)器.
即使是一個(gè)單純的Thumb應(yīng)用系統(tǒng),也必須加一個(gè)匯編的交互頭程序,
因?yàn)橄到y(tǒng)總是自動(dòng)從ARM開(kāi)始啟動(dòng).
2. 狀態(tài)切換的實(shí)現(xiàn)
處理器在ARM/Thumb之間的狀態(tài)切換是通過(guò)一條專(zhuān)用的跳轉(zhuǎn)交換指令BX
來(lái)實(shí)現(xiàn)的.BX指令以通用寄存器(R0-R15)為操作數(shù),通過(guò)拷貝Rn到PC來(lái)
實(shí)現(xiàn)4GB空間范圍內(nèi)的一個(gè)絕對(duì)跳轉(zhuǎn). BX利用Rn寄存器中存儲(chǔ)的目標(biāo)地址值
的最后一位來(lái)判斷跳轉(zhuǎn)后的狀態(tài).
圖-1 BX指令實(shí)現(xiàn)狀態(tài)切換
0 31
Rn
PC
BX
ARM/Thumb選擇位:
0 - ARM
1 - Thumb
BX Rn
當(dāng)前狀態(tài)是Thumb時(shí)
BX{Cond.} Rn
當(dāng)前狀態(tài)是ARM時(shí)
基于ARM的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
無(wú)論ARM還是Thumb,其指令存儲(chǔ)在存儲(chǔ)器中都是邊界對(duì)齊的(4-Byte或
2-Byte對(duì)齊),所以在執(zhí)行跳轉(zhuǎn)過(guò)程中,PC寄存器中的最低位肯定被舍棄,不起
作用.在BX指令的執(zhí)行過(guò)程中,最低位正好被用作狀態(tài)判斷的標(biāo)識(shí),不會(huì)造成
存儲(chǔ)器訪問(wèn)不對(duì)齊的錯(cuò)誤.
圖2中是一段直接進(jìn)行狀態(tài)切換的例程:
圖-2 ARM/Thumb交互工作的例子
我們知道ARM的狀態(tài)寄存器CPSR中,bit-5是狀態(tài)控制位T-bit,決定當(dāng)前
處理器的運(yùn)行狀態(tài).如果直接修改CPSR的狀態(tài)位,也能夠達(dá)到改變處理器運(yùn)行
狀態(tài)的目的,但是會(huì)帶來(lái)一個(gè)問(wèn)題.因?yàn)锳RM采用了多級(jí)流水線(xiàn)的結(jié)構(gòu),所以
在程序執(zhí)行過(guò)程中指令流水線(xiàn)上會(huì)存在幾條預(yù)取指令(具體數(shù)目視流水線(xiàn)級(jí)數(shù)而
不同).當(dāng)修改CPSR的T-bit以后,狀態(tài)的轉(zhuǎn)變會(huì)造成流水線(xiàn)上預(yù)取指令執(zhí)行的
錯(cuò)誤.而如果用BX指令,則執(zhí)行后會(huì)進(jìn)行流水線(xiàn)刷新動(dòng)作,清除流水線(xiàn)上的殘
余指令,在新的狀態(tài)下重新開(kāi)始指令預(yù)取,從而保證狀態(tài)轉(zhuǎn)變時(shí)候指令流的正確
銜接.
3. ARM/Thumb之間的函數(shù)調(diào)用
在無(wú)交互的子程序調(diào)用中,其過(guò)程比較簡(jiǎn)單.實(shí)現(xiàn)調(diào)用通常只需要一條指
令:
BL function
實(shí)現(xiàn)返回也只需要從LR恢復(fù)PC即可:
MOV PC, LR
;從ARM狀態(tài)開(kā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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
如下圖所示:
圖-3 普通函數(shù)調(diào)用
如果子函數(shù)和父函數(shù)不是在同一種狀態(tài)下執(zhí)行的,因?yàn)闋顟B(tài)切換,需要對(duì)
函數(shù)調(diào)用作更多的考慮.
(a) BL不能完成狀態(tài)切換,需要由BX來(lái)切換狀態(tài).
(b) BX不能自動(dòng)保存返回地址到LR,需要在BX之前先保存好LR.
(c) 用"BX LR"來(lái)返回,不能使用"MOV PC, LR",因?yàn)檫@條指令同
樣不能實(shí)現(xiàn)狀態(tài)切換.返回時(shí)要仔細(xì)考慮保存的LR中最低位內(nèi)容是否
正確.
假如用戶(hù)直接使用匯編進(jìn)行狀態(tài)交互跳轉(zhuǎn),上述的幾個(gè)問(wèn)題都需要用手工
編碼加以處理.如果用戶(hù)使用高級(jí)語(yǔ)言進(jìn)行開(kāi)發(fā),不需要為ARM/Thumb之間的
相互調(diào)用增加額外的編碼,但是最好要對(duì)其調(diào)用過(guò)程加以了解.下面以ARM ADS
中的編譯工具為例進(jìn)行說(shuō)明(圖4).
(a) 兩個(gè)函數(shù)func1()和func2()被編譯成了不同的指令集(ARM或Thumb).
注意func1()和func2()在這里位于二個(gè)不同的源文件.
(b) 編譯時(shí)必須告訴編譯器和連接器足夠的信息,一方面讓編譯器能夠使用
正確的指令碼進(jìn)行編譯,另一方面這樣當(dāng)在不同的狀態(tài)之間發(fā)生函數(shù)調(diào)
用時(shí),連接器將插入一段連接代碼(veneers)來(lái)實(shí)現(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的嵌入式程序開(kāi)發(fā)要點(diǎn)
ARM-CHINA-040415A
上述過(guò)程中的一個(gè)特點(diǎn)是func1還是使用通常的BL指令來(lái)進(jìn)行子程序調(diào)用,
而func2返回時(shí)則直接使用"BX LR",沒(méi)有對(duì)LR進(jìn)行判斷和最低位的設(shè)置.
這是因?yàn)楫?dāng)執(zhí)行BL指令對(duì)LR進(jìn)行保存時(shí),其最低位會(huì)被自動(dòng)設(shè)置,以滿(mǎn)足返
回時(shí)狀態(tài)切扼/td>

用并且不推薦使用這
些編號(hào)。
半主機(jī)SWI 使用的軟件中斷編號(hào)也可以由用戶(hù)自定義,但若是改變了缺省
的軟中斷編號(hào),需要:
?? 更改系統(tǒng)中所有代碼(包括庫(kù)代碼)的半主機(jī)SWI 調(diào)用
?? 重新配置調(diào)試器對(duì)半主機(jī)請(qǐng)求的捕捉與相應(yīng)
這樣才能使用新的SWI 編號(hào)。
有關(guān)半主機(jī)SWI 處理函數(shù)實(shí)現(xiàn)的更詳細(xì)信息,請(qǐng)參考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)生一個(gè)軟件中斷異常,這為應(yīng)用程序調(diào)用系統(tǒng)例程提供了一種機(jī)制。
語(yǔ)法:
?????? 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è)置程序計(jì)數(shù)器pc為向量表的0x08偏移處,同事強(qiáng)制切換處理器模式到SVC模式,以便操作系統(tǒng)例程可以在特權(quán)模式下被調(diào)用。
?? 每個(gè)SWI指令有一個(gè)關(guān)聯(lián)的SWI號(hào)(number),用于表示一個(gè)特定的功能調(diào)用或特性。
【例子】 一個(gè)ARM工具箱中用于調(diào)試SWI的例子,是一個(gè)SWI號(hào)為0x123456的SWI調(diào)用。通常SWI指令是在用戶(hù)模式下執(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ù),這可以通過(guò)寄存器來(lái)完成。在上面的例子中,r0
用于傳遞參數(shù)0x12,返回值也通過(guò)寄存器來(lái)傳遞。
?? 處理軟件中斷調(diào)用的代碼段稱(chēng)為中斷處理程序(SWI Handler)。中斷處理程序通過(guò)執(zhí)行指令的地址獲取軟件中斷號(hào),指令地址是從lr計(jì)算出來(lái)的。
?? SWI號(hào)由下式?jīng)Q定:
?? SWI_number = <SWI instruction> AND NOT<0xff000000>
?? 其中SWI instruction就是實(shí)際處理器執(zhí)行的32位SWI指令
?
?? SWI指令編碼為:
?? 31 - 28? 27 - 24? 23 - 0
???? cond?? 1 1 1 1? immed24
?? 指令的二進(jìn)制代碼的bit23-bit0是24bit的立即數(shù),即SWI指令的中斷號(hào),通過(guò)屏蔽高8bit即可獲得中斷號(hào)。lr寄存器保存的是中斷返回指令的地址,所以 [lr - 4] 就是執(zhí)行SWI的執(zhí)行代碼。通過(guò)load指令拷貝整個(gè)SWI指令到寄存器,使用BIC屏蔽指令的高8位,獲取SWI中斷號(hào)。
??
??? ;read the SWI instruction
??? LDR? r10, [lr, #-4]
??? BIC? r10, r10, #0xff000000
?
2. 周立功移植uC/OS-II到s3c2410的軟中斷服務(wù)級(jí)的任務(wù)切換
uC/OS-II的任務(wù)調(diào)度函數(shù)
?? uC/OS-II的任務(wù)級(jí)的調(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ì)解釋可以參考《嵌入式實(shí)時(shí)操作系統(tǒng) uC/OS-II》,os_sched函數(shù)在確定所有就緒任務(wù)的最高優(yōu)先級(jí)高于當(dāng)前任務(wù)優(yōu)先級(jí)時(shí)進(jìn)行任務(wù)切換,通過(guò)OS_TASK_SW( )宏來(lái)調(diào)用。
?? OS_TASK_SW( )宏實(shí)際上定義的是SWI軟中斷指令。見(jiàn)OS_CPU.H文件的代碼:

__swi(0x00) void OS_TASK_SW(void); /* 任務(wù)級(jí)任務(wù)切換函數(shù) */
__swi(0x01) void _OSStartHighRdy(void); /* 運(yùn)行優(yōu)先級(jí)最高的任務(wù) */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 關(guān)中斷 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 開(kāi)中斷 */

__swi(0x40) void *GetOSFunctionAddr(int Index); /* 獲取系統(tǒng)服務(wù)函數(shù)入口 */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 獲取自定義服務(wù)函數(shù)入口 */
__swi(0x42) void OSISRBegin(void); /* 中斷開(kāi)始處理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判斷中斷是否需要切換 */

__swi(0x80) void ChangeToSYSMode(void); /* 任務(wù)切換到系統(tǒng)模式 */
__swi(0x81) void ChangeToUSRMode(void); /* 任務(wù)切換到用戶(hù)模式 */
__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)的代碼,通過(guò)反匯編可以看到,調(diào)用OS_TASK_SW實(shí)際上被替換成swi 0x00 軟中斷指令。執(zhí)行此執(zhí)行,pc會(huì)跳轉(zhuǎn)到向量表的0x08偏移處。


中斷向量表:(見(jiàn)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會(huì)跳轉(zhuǎn)到SoftwareInterrupt代碼處開(kāi)始執(zhí)行:

見(jiàn)Os_cpu_a.s文件的SoftwareInterrupt函數(shù):

?

SoftwareInterrupt
??????? LDR SP, StackSvc ; 重新設(shè)置堆棧指針
??????? STMFD {R0-R3, R12, LR}
??????? MOV R1, SP ; R1指向參數(shù)存儲(chǔ)位置

??????? 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??? ; 如上面所述,此處通過(guò)屏蔽SWI指令的高8位來(lái)獲取SWI號(hào),r0 = SWI號(hào),R1指向參數(shù)存儲(chǔ)位置
??????? CMP R0, #1
??????? LDRLO PC, =OSIntCtxSw? ;為0時(shí)跳轉(zhuǎn)到OSIntCtxSwdi地址處
??????? LDREQ PC, =__OSStartHighRdy ; 為1時(shí),跳轉(zhuǎn)到__OSStartHighRdy地址處。SWI 0x01為第一次任務(wù)切換

??????? BL SWI_Exception? ;進(jìn)入中斷號(hào)散轉(zhuǎn)函數(shù)
???????
??????? LDMFD {R0-R3, R12, PC}^
???????
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

?

以上就是任務(wù)切換軟中斷級(jí)服務(wù)的實(shí)現(xiàn)。
?
?淺析arm匯編中指令使用學(xué)習(xí)
本帖被 smd801124 設(shè)置為精華(2008-05-08)
macro restore_user_regs
? ldr r1,[sp, #S_PSR]
? ldr lr,[sp, #S_PC]!? @ !用來(lái)控制基址變址尋址的最終新地址是否進(jìn)行回寫(xiě)操作,
????????????????????? @ 執(zhí)行l(wèi)dr之后sp被回寫(xiě)成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]
????????????????????? @ 因?yàn)闆](méi)對(duì)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í)中[隨時(shí)補(bǔ)充gliethttp]
-----------------------------
1.ldr ip,[sp],#4 將sp中內(nèi)容存入ip,之后sp=sp+4;
? ldr ip,[sp,#4] 將sp+4這個(gè)新地址下內(nèi)容存入ip,之后sp值保持不變
? ldr ip,[sp,#4]!將sp+4這個(gè)新地址下內(nèi)容存入ip,之后sp=sp+4將新地址值賦給sp
? str ip,[sp],#4 將ip存入sp地址處,之后sp=sp+4;
? str ip,[sp,#4] 將ip存入sp+4這個(gè)新地址,之后sp值保持不變
? str ip,[sp,#4]!將ip存入sp+4這個(gè)新地址,之后sp=sp+4將新地址值賦給sp
-----------------------------
2.movs r1,#3 ;movs將導(dǎo)致ALU被更改,因?yàn)閞1賦值非0,即操作結(jié)果r0非0,所以ALU的Z標(biāo)志清0
? bne 1f??? ;因?yàn)閆=0,說(shuō)明不等,所以向前跳到標(biāo)號(hào)1:所在處繼續(xù)執(zhí)行其他語(yǔ)句
-----------------------------
3.LDM表示裝載,STM表示存儲(chǔ).
? LDMED LDMIB 預(yù)先增加裝載
? LDMFD LDMIA 過(guò)后增加裝載
? LDMEA LDMDB 預(yù)先減少裝載
? LDMFA LDMDA 過(guò)后減少裝載

? STMFA STMIB 預(yù)先增加存儲(chǔ)
? STMEA STMIA 過(guò)后增加存儲(chǔ)
? STMFD STMDB 預(yù)先減少存儲(chǔ)
? STMED STMDA 過(guò)后減少存儲(chǔ)

注意ED不同于IB;只對(duì)于預(yù)先減少裝是相同的.在存儲(chǔ)的時(shí)候,ED是過(guò)后減少的.
FD、ED、FA、和 EA 指定是滿(mǎn)棧還是空棧,是升序棧還是降序棧.
對(duì)于存儲(chǔ)STM而言
先加后存 FA 姑且這么來(lái)記,先加(first add),存數(shù)據(jù)
后加先存 EA 姑且這么來(lái)記,存數(shù)據(jù),后加end add
先減后存 FD 姑且這么來(lái)記,先減first dec,存數(shù)據(jù)
后減先存 ED 姑且這么來(lái)記,存數(shù)據(jù),后減end dec
然后記憶LDM,LDM是STM的反相彈出動(dòng)作,所以
因?yàn)槭窍燃雍蟠?所以后減先取 FA 就成了與STM對(duì)應(yīng)的取數(shù)據(jù),后減
因?yàn)槭呛蠹酉却?所以先減后取 EA 就成了與STM對(duì)應(yīng)的先減,取數(shù)據(jù)
因?yàn)槭窍葴p后存,所以后加先取 FD 就成了與STM對(duì)應(yīng)的取數(shù)據(jù),后加
因?yàn)槭呛鬁p先存,所以先加后取 ED 就成了與STM對(duì)應(yīng)的先加,取數(shù)據(jù)
我想通過(guò)上面的變態(tài)方式可以比較容易的記住這套指令[gliethttp]
一個(gè)滿(mǎn)棧的棧指針指向上次寫(xiě)的最后一個(gè)數(shù)據(jù)單元,而空棧的棧指針指向第一個(gè)空閑單元.
一個(gè)降序棧是在內(nèi)存中反向增長(zhǎng)(就是說(shuō),從應(yīng)用程序空間結(jié)束處開(kāi)始反向增長(zhǎng))而升序棧在內(nèi)存中正向增長(zhǎng).
其他形式簡(jiǎn)單的描述指令的行為,意思分別是
IA過(guò)后增加(Increment After)、
IB預(yù)先增加(Increment Before)、
DA過(guò)后減少(Decrement After)、
DB預(yù)先減少(Decrement Before).

RISC OS使用傳統(tǒng)的滿(mǎn)降序棧.在使用符合APCS規(guī)定的編譯器的時(shí)候,它通常把你的棧指針設(shè)置在應(yīng)用程序空間的
結(jié)束處并接著使用一個(gè)FD(滿(mǎn)降序-Full Descending)棧.如果你與一個(gè)高級(jí)語(yǔ)言(BASIC或C)一起工作,你將別無(wú)選擇.
棧指針(傳統(tǒng)上是R13)指向一個(gè)滿(mǎn)降序棧.你必須繼續(xù)這個(gè)格式,或則建立并管理你自己的棧.

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來(lái)檢查是否設(shè)置了特定的位
tst r1,#0x80 //按位and操作,檢測(cè)r1的0x1<<7,即第7位是否置1,按位與之后結(jié)果為0,那么ALU的Z置位
beq reset??? //如果Z置位,即:以上按位與操作結(jié)果是0,那么跳轉(zhuǎn)到reset標(biāo)號(hào)執(zhí)行
-----------------------------
6.'^'的理解
'^'是一個(gè)后綴標(biāo)志,不能在User模式和Sys系統(tǒng)模式下使用該標(biāo)志.該標(biāo)志有兩個(gè)存在目的:
6.1.對(duì)于LDM操作,同時(shí)恢復(fù)的寄存器中含有pc(r15)寄存器,那么指令執(zhí)行的同時(shí)cpu自動(dòng)將spsr拷貝到cpsr中
如:在IRQ中斷返回代碼中[如下為ads環(huán)境下的代碼gliethttp]
ldmfd {r4}????????? //讀取sp中保存的的spsr值到r4中
msr spsr_cxsf,r4??? //對(duì)spsr的所有控制為進(jìn)行寫(xiě)操作,將r4的值全部注入spsr
ldmfd {r0-r12,lr,pc}^//當(dāng)指令執(zhí)行完畢,pc跳轉(zhuǎn)之前,將spsr的值自動(dòng)拷貝到cpsr中[gliethttp]
6.2.數(shù)據(jù)的送入、送出發(fā)生在User用戶(hù)模式下的寄存器,而非當(dāng)前模式寄存器
如:ldmdb sp,{r0 - lr}^;表示sp棧中的數(shù)據(jù)回復(fù)到User分組寄存器r0-lr中,而不是恢復(fù)到當(dāng)前模式寄存器r0-lr? 當(dāng)然對(duì)于User,System,IRQ,SVC,Abort,Undefined這6種模式來(lái)說(shuō)[gliethttp]r0-r12是共用的,只是r13和r14
? 為分別獨(dú)有,對(duì)于FIQ模式,僅僅r0-r7是和前6中模式的r0-r7共用,r8-r14都是FIQ模式下專(zhuān)有.
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對(duì)應(yīng)cpsr_f
cpsr_all對(duì)應(yīng)cpsr_cxsf

需要使用專(zhuān)用指令對(duì)cpsr和spsr操作:mrs,msr
mrs tmp,cpsr????? //讀取CPSR的值
bic tmp,tmp,#0x80 //如果第7位為1,將其清0
msr cpsr_c,tmp??? //對(duì)控制位區(qū)psr[7:0]進(jìn)行寫(xiě)操作
-----------------------------
8.cpsr的理解
CPSR = Current Program Status Register
SPSR = Saved Program Status Registers
CPSR寄存器(和保存它的SPSR寄存器)
本文來(lái)自: 電子論壇[url]http://www.eehome.cn[/url]電子工程師之家!

?

?

?

?

?

?

?

?

?

ARM的堆棧學(xué)習(xí)筆記
來(lái)源:
http://www.hzlitai.com.cn/?? 作者:藍(lán)石頭
字體大小:[大][中][小]

  以下是我在學(xué)習(xí)ARM指令中記錄的關(guān)于堆棧方面的知識(shí):

  1、寄存器 R13 在 ARM 指令中常用作堆棧指針

  2、對(duì)于 R13 寄存器來(lái)說(shuō),它對(duì)應(yīng)6個(gè)不同的物理寄存器,其中的一個(gè)是用戶(hù)模式與系統(tǒng)模式共用,另外5個(gè)物理寄存器對(duì)應(yīng)于其他5種不同的運(yùn)行模式。采用以下的記號(hào)來(lái)區(qū)分不同的物理寄存器: R13_<mode> 其中,mode為以下幾種模式之一:usr、fiq、irq、svc、abt、und。

  3、寄存器R13在ARM指令中常用作堆棧指針,但這只是一種習(xí)慣用法,用戶(hù)也可使用其他的寄存器作為堆棧指針。而在Thumb指令集中,某些指令強(qiáng)制性的要求使用R13作為堆棧指針。由于處理器的每種運(yùn)行模式均有自己獨(dú)立的物理寄存器R13,在用戶(hù)應(yīng)用程序的初始化部分,一般都要初始化每種模式下的R13,使其指向該運(yùn)行模式的棧空間,這樣,當(dāng)程序的運(yùn)行進(jìn)入異常模式時(shí),可以將需要保護(hù)的寄存器放入R13所指向的堆棧,而當(dāng)程序從異常模式返回時(shí),則從對(duì)應(yīng)的堆棧中恢復(fù),采用這種方式可以保證異常發(fā)生后程序的正常執(zhí)行。

  4、有四種類(lèi)型的堆棧:

  堆棧是一種數(shù)據(jù)結(jié)構(gòu),按先進(jìn)后出(First In Last Out,FILO)的方式工作,使用一個(gè)稱(chēng)作堆棧指針的專(zhuān)用寄存器指示當(dāng)前的操作位置,堆棧指針總是指向棧頂。

  當(dāng)堆棧指針指向最后壓入堆棧的數(shù)據(jù)時(shí),稱(chēng)為滿(mǎn)堆棧(Full Stack),而當(dāng)堆棧指針指向下一個(gè)將要放入數(shù)據(jù)的空位置時(shí),稱(chēng)為空堆棧(Empty Stack)。

  同時(shí),根據(jù)堆棧的生成方式,又可以分為遞增堆棧(Ascending Stack)和遞減堆棧(DecendingStack),當(dāng)堆棧由低地址向高地址生成時(shí),稱(chēng)為遞增堆棧,當(dāng)堆棧由高地址向低地址生成時(shí),稱(chēng)為遞減堆棧。這樣就有四種類(lèi)型的堆棧工作方式,ARM 微處理器支持這四種類(lèi)型的堆棧工作方式,即: ◎ Full descending 滿(mǎn)遞減堆棧堆棧首部是高地址,堆棧向低地址增長(zhǎng)。棧指針總是指向堆棧最后一個(gè)元素(最后一個(gè)元素是最后壓入的數(shù)據(jù))。 ARM-Thumb過(guò)程調(diào)用標(biāo)準(zhǔn)和ARM、Thumb C/C++ 編譯器總是使用Full descending 類(lèi)型堆棧。

  ◎ Full ascending 滿(mǎn)遞增堆棧堆棧首部是低地址,堆棧向高地址增長(zhǎng)。棧指針總是指向堆棧最后一個(gè)元素(最后一個(gè)元素是最后壓入的數(shù)據(jù))。

  ◎ Empty descending 空遞減堆棧堆棧首部是低地址,堆棧向高地址增長(zhǎng)。棧指針總是指向下一個(gè)將要放入數(shù)據(jù)的空位置。

  ◎ Empty ascending 空遞增堆棧堆棧首部是高地址,堆棧向低地址增長(zhǎng)。棧指針總是指向下一個(gè)將要放入數(shù)據(jù)的空位置。

  5、操作堆棧的匯編指令堆棧類(lèi)型 入棧指令 出棧指令 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é)

以上是生活随笔為你收集整理的ARM汇编指令汇总的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。