新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM簡(jiǎn)單啟動(dòng)代碼及中斷處理分析

ARM簡(jiǎn)單啟動(dòng)代碼及中斷處理分析

作者: 時(shí)間:2016-11-24 來(lái)源:網(wǎng)絡(luò) 收藏
前言:本篇分析的是一個(gè)最精簡(jiǎn)的啟動(dòng)代碼,并且包含一個(gè)簡(jiǎn)單的中斷處理,C程序部分省略,重點(diǎn)分析匯編部分,這是因?yàn)閷?duì)于我來(lái)說(shuō),匯編代碼實(shí)在是讓人厭煩,但是又不能不用。
下面是對(duì)代碼的分析,紅色部分是分析結(jié)果
.extern main //.extern 表示main在另外的文件中定義,在這里要引用,至于為什么只聲明了extern而沒(méi) //有聲明別的,目前還不知道,不過(guò)都聲明一下,應(yīng)該沒(méi)問(wèn)題
.text //.text 表示后面的內(nèi)容編譯出來(lái)放在代碼段
.global _start //.global 告訴編譯器后面聲明的是一個(gè)全局可見(jiàn)的名字,由于這是啟動(dòng)代碼,所以cpu上電后 //必須要找到_start函數(shù),所以_start必須聲明為全局的,當(dāng)然這個(gè)名字可以改,無(wú)所謂
_start: //查看反匯編地址就可以發(fā)現(xiàn),_start的地址就是0x0
b Reset //不帶返回的跳轉(zhuǎn)到Reset中,為什么不用返回呢,因?yàn)闆](méi)必要,reset中直接進(jìn)入到主函數(shù)內(nèi)了
HandleUndef: //查看反匯編地址可知HandleUndef的地址位0x04,這個(gè)很好理解,因?yàn)橐粭l指令4個(gè)字節(jié)
b HandleUndef //在這里由于是精簡(jiǎn)的啟動(dòng)代碼,所以一旦發(fā)生了未定義異常時(shí),就讓CPU自己玩死自己吧
HandleSWI: //反匯編地址0x08,其他分析同上
b HandleSWI
HandlePrefetchAbort: //反匯編地址0x0C,其他分析同上
b HandlePrefetchAbort
HandleDataAbort: //反匯編地址0x10,其他分析同上
b HandleDataAbort
HandleNotUsed: //反匯編地址0x14,其他分析同上
b HandleNotUsed
HandleIRQ:
b HandlerIRQ //反匯編地址0x18,看到這里相比就有點(diǎn)感覺(jué)了,0x18是普通中斷向量地址,這個(gè)就有很 //多地方可說(shuō)了,首先這里依然是使用B指令進(jìn)行跳轉(zhuǎn),這個(gè)之所以不直接用BL的原因 ///是,中斷還不太算是調(diào)用函數(shù)那么簡(jiǎn)單,發(fā)生中斷的時(shí)候,要保存現(xiàn)場(chǎng),而現(xiàn)場(chǎng)的數(shù) //據(jù)有很多,具體為R0-R12、PC、CPSR的內(nèi)容,這些都需要進(jìn)棧保存,所以這些的內(nèi)容不 //是BL一條命令能完成的,所以保存現(xiàn)場(chǎng)的內(nèi)容就直接放到了中斷程序中了。其次要特別 //注意這個(gè)里面跳轉(zhuǎn)的程序時(shí)HandlerIRQ不是HandleIRQ,如果還像上面那樣,那么發(fā)生中 //斷時(shí),CPU就會(huì)不停的循環(huán)來(lái)回跳了,所以這點(diǎn)需要特別留意,當(dāng)然還有一種方法,就是 //不要這條命令的標(biāo)號(hào)HandleIRQ,直接使用一條 b HandlerIRQ即可。
HandleFIQ:
b HandleFIQ //反匯編地址位0x1C,由于本程序只分析簡(jiǎn)單的普通中斷,所以認(rèn)為發(fā)生快速 //中斷的時(shí)候,讓CPU自己玩
Reset:
ldr sp,=4096 //由于要在匯編中調(diào)用C函數(shù),所以要先設(shè)置堆棧指針,具體為什么,就 //不詳細(xì)分析了,簡(jiǎn)單的說(shuō),C函數(shù)要使用很多的中間內(nèi)存存放運(yùn)算數(shù)據(jù),所以 //要開(kāi)辟一段堆棧用。這里還要特別提到一點(diǎn),由于ARM工作模式的特點(diǎn),此時(shí) //設(shè)置的堆棧指針SP,即R14,對(duì)應(yīng)的是系統(tǒng)模式下的堆棧指針寄存器,即R14_svc
bl disable_watch_dog //關(guān)閉看門(mén)狗
msr cpsr_c,# 0xd2 //設(shè)置CPU進(jìn)入到中斷模式
ldr sp,=3072 //由于發(fā)生中斷時(shí),要用到堆棧用于保存現(xiàn)場(chǎng),所以需要先設(shè)置中斷模式下的堆 //棧指針地址(設(shè)置堆棧指針就相當(dāng)于開(kāi)辟堆棧了,因?yàn)槎褩>褪怯卸褩V羔樀? //一片普通內(nèi)存區(qū)域),即設(shè)置R14_irq,特別注意R14_irq和R14_svc是不一樣 //的,只是同名,但是指向的物理地址是完全不同的。
msr cpsr_c,# 0xdf //從普通中斷模式返回到系統(tǒng)模式
bl init_led //這個(gè)非常值得分析,首先這里使用了BL指令進(jìn)行跳轉(zhuǎn),BL指令的特點(diǎn)就是,跳轉(zhuǎn) //的同時(shí),會(huì)自動(dòng)的將PC的值放到LR中,而且,如果后面跳轉(zhuǎn)的是C函數(shù)的話(huà),C函 //數(shù)中就不用設(shè)置返回命令了,而如果BL后面跟的是一個(gè)匯編子程序,那就需要程 //序員自己添加返回命令,這是為什么呢?如果用專(zhuān)業(yè)術(shù)語(yǔ)說(shuō),如果調(diào)用C函數(shù), //函數(shù)運(yùn)行結(jié)束后,編譯器會(huì)自動(dòng)的將上面保存的LR中的內(nèi)容減去4重新傳遞給 //PC(為什么減4下面分析),也就實(shí)現(xiàn)了自動(dòng)返回的功能,但是匯編函數(shù)不行,因 //為匯編的編譯器沒(méi)有這項(xiàng)功能,所以通俗的講,為什么C語(yǔ)言是高級(jí)語(yǔ)言,除了語(yǔ) //法高級(jí)人性化,配套的編譯器也比較人性化,會(huì)幫程序員做一些通用的工作。而 //匯編語(yǔ)言就比較一根筋,所有的事兒,哪怕是最簡(jiǎn)單的事兒,都要程序員一條一 //條的把代碼敲上去告訴它怎么干(這也是我為什么討厭匯編的原因,比較懶,呵 //呵),至于調(diào)用匯編子程序怎么返回怎么做,分析在下面
bl init_irq //分析同上
msr cpsr_c,# 0x5f //開(kāi)中斷,即允許普通中斷IRQ
ldr lr,=halt_loop //這個(gè)也很有意思,在這里為什么不直接用 bl main代替這兩條命令呢,因 //為。。。。。想嘚瑟一下(玩笑了),主要是想學(xué)習(xí)另外一種常見(jiàn)常用的跳轉(zhuǎn)方 //法。BL命令很好用,但是有缺點(diǎn),就是跳轉(zhuǎn)的范圍,因?yàn)樗荒芴D(zhuǎn)區(qū)區(qū)32MB, //換算成ARM的命令,也就只有8M的地址(因?yàn)橐粭l命令4個(gè)字節(jié)),也就 //是-4MB~+4MB,所以范圍有限,而嵌入式程序,尤其是有操作系統(tǒng)時(shí),程序存放地 //址跨度比較大,所以呢,需要有一個(gè)長(zhǎng)跳轉(zhuǎn)指令,所以就引出了LDR命令,這個(gè)命 //令的跳轉(zhuǎn)范圍是4G,所以知道為什么LDR長(zhǎng)用了吧,因?yàn)闊o(wú)論地址在哪,它都能跳 //到,跳轉(zhuǎn)范圍大是LDR命令的優(yōu)點(diǎn),但是它也有缺點(diǎn),就是它負(fù)責(zé)跳轉(zhuǎn),不能保存 //PC,如果在跳轉(zhuǎn)前不往LR中放返回地址,就會(huì)出現(xiàn),跳到了子程序的位置,子程序 //執(zhí)行完后,跳不回來(lái)了,所以如果使用ldr調(diào)用子程序,一定要在調(diào)用前給LR一個(gè) //返回地址。
ldr pc,=main
halt_loop:
b halt_loop //main函數(shù)的返回地址,或者說(shuō)是返回操作,讓CPU原地踏步
HandlerIRQ:
sub lr,lr,# 4 //這條指令用于計(jì)算中斷處理完畢后的返回地址,在這里有個(gè)隱含的地方,即當(dāng)發(fā)生中 //斷時(shí),處理器會(huì)自動(dòng)的向LR寄存器中存放被中斷指令的地址加4,或者也可以理解成,
//存放的是PC的值,而PC的值是當(dāng)前運(yùn)行命令地址加8,雖然發(fā)生了中斷,但是CPU仍然會(huì) //運(yùn)行完當(dāng)前命令后才去處理中斷,所以LR-4的值,正是被中斷指令的下一條命令,當(dāng)中 //斷處理完畢以后,返回到被中斷指令的下一條指令進(jìn)行執(zhí)行。
stmdb sp!,{r0-r12,lr} //入棧指令,表示將R0~R12,以及LR寄存器的內(nèi)容入棧,這個(gè)命令格式暫不分析,具體的可以參考 //相關(guān)手冊(cè)
ldr lr,=int_return //這兩條命令的分析跟上面跳轉(zhuǎn)到main函數(shù)原理是一樣的,可以使用一條命令
//bl EINT_Handle代替,因?yàn)檫@個(gè)程序本身并不大,不會(huì)超過(guò)4Kb
ldr pc,=EINT_Handle
int_return:
ldmia sp!,{r0-r12,pc}^ //出棧指令,跟上面的入棧指令相呼應(yīng),表示將sp對(duì)應(yīng)的地址內(nèi)容遞增(因?yàn)锳RM的堆棧式滿(mǎn)遞減 //形式)的依次存放到r0~r12,以及pc中,而且這個(gè)很有意思,按照順序,sp即r13肯定不包括在 //內(nèi),所以到pc的時(shí)候,賦值的內(nèi)容寄存器正好是LR,而LR已經(jīng)在中斷開(kāi)頭處理了,而且^表示將 //spsr的值復(fù)制到cpsr中,至此完成了中斷的返回



關(guān)鍵詞: ARM啟動(dòng)代碼中斷處

評(píng)論


技術(shù)專(zhuān)區(qū)

關(guān)閉