說明:本系列文章將主要以ARMv7和ARMv8架構為例,介紹ARM匯編語言的一些基礎知識。關于ARM匯編語言的學習,這里我要推薦一本書和一個網站,其中書是由宋巖翻譯的《Cortex-M3權威指南》,其文筆風趣幽默,引人入勝,網站則是azeria-labs。當然,ARM官方的Architecture Reference Manual更是重要的參考。
說起與系統結構相關的匯編語言,自然要先介紹該體系結構的寄存器組成。ARMv7相較于同為32位的x86,寄存器的數量要多一些,名稱和配置也不盡相同,但兩者還是有一個基本的對照關系:
ARMv7-A在設計之初,就有和之前系列的處理器(比如以ARM9系列為代表的ARMv5)兼容的七種處理器模式,后來在向ARMv8過渡的過程中,又增加了"MON"和"HYP"。
為了減少模式切換時的寄存器保存和恢復,同名寄存器在多種模式下各有一份,稱為bank register。某些模式會有自己專有的寄存器,比如FIQ就比IRQ多一些寄存器(R8到R12),這樣FIQ在進入和退出中斷的時候,所需要做的寄存器保存和恢復就可以減少,這也是它比IRQ更"Fast"的原因。
自從ARMv8出現以后,ARM的寄存器就全面進入了64位時代,通用寄存器的數量從13個(R0-R12)變成了30個(X0-X29) ,其名稱中的"R"也被"X"所取代了,但為了保持和32位系統的兼容性,每個ARMv8/ARM64通用寄存器都可被當做2個32位寄存器來使用,這樣的32位寄存器用"Wn"來表示。
ARMv8支持兩種執行狀態(execution state),分別是AArch64和AArch32,在AArch64狀態下執行的是A64指令集,在AArch32狀態下執行的是與ARMv7前向兼容的A32/T32指令集。
A64指令集看起來和前代的指令集差別不大,但其具有更高的編碼效率,別看它叫A64就以為它的指令長度是64位的,依然是32位,也就是4個字節。通常一條指令不會占據太多字節,而為了方便流水線的操作,ARM中指令的字節數通常是保持一致的(最多就是T32/Thumb-2這種2字節和4字節混合的指令),都設計成8個字節話的確挺浪費代碼空間。
ARMv8中的A32/T32指令集也不是和ARMv7中A32/T32一模一樣的,它做了一些改進和增強,如果你使用了這些強化的特性,當然可以獲得更好的性能,但是就不能和ARMv7完全兼容了。如果你希望同樣的一套匯編代碼在ARMv7和ARMv8中都能直接運行,就不能使用這部分額外的特性。
ARM雖說是RISC架構的,但RISC和CISC并不是涇渭分明的,雙方都在互相學習,取長補短。現在ARM支持的指令也是越來越多,本系列文章將僅介紹其中的一部分指令。
【數據傳送指令】
基礎的LDR/STR
在x86架構中,不管是寄存器之間,還是寄存器和內存之間,都可以使用MOV指令,并且直接操作內存單元上的數據是被允許的。
在ARM架構中,寄存器間傳送數據的指令依然是MOV,比如"MOV Ra Rb" 就是把Rb里存放的數據傳送給Ra,但內存單元上的數據不允許被直接操作,而是必須先放到寄存器中,為此就有了把內存的內容傳送到寄存器的指令LDR(Load),以及把寄存器的內容傳送回內存的指令STR(Store)。
傳送的時候,內存單元的地址存放在一個寄存器中(比如R1),用[R1]表示,"[]"在這里就對應C語言里的"*",表示取地址里的內容。假設R1里存放的是0x200,內存中地址0x200處的內容是0x5,那么"ldr r0, [r1]"就是將0x5放入r0中。
通用寄存器的數量一共就那么多,直接用寄存器的值來獲取內存地址的數量實在太有限了,更多的時候,是通過寄存器的值(基址)加上一個偏移/索引(offset/index)來指向內存對應的單元,索引的大小可以由立即數提供,也可以由寄存器存儲的值提供:
STR R0,[R1, #12] // R0 --> [R1+12]
LDR R4,[R5, R6] // R4 <-- [R5+R6]
如果索引對基址的更改發生在數據傳輸之前,則稱為"預索引"(pre-index),傳輸前后寄存器R1的值都不會改變。
如果索引對基址的更改發生在數據傳輸之后(注意下圖"[]"位置的改變),則稱為"后索引"(post-index),傳輸后寄存器R1的內容將變為加上其原來的值加上索引后的值。"后索引"其實算是一種二合一的指令,比如"str r0, [r1], #12"就等同于"str r0, [r1]"加上"r1 = r1+12"。
好像缺了點什么,沒有既更改R1的值為R1+12,同時把*(R1+12)的值送入R0的?不急,這將在本文的后半段給出答案。
LDR和STR后面可以接一些后綴,比如"B", "H"和"W"分別表示從給定的內存地址取1個字節,2個字節和4個字節。
如果一次傳送的不是8個字節,那么64位的寄存器是填不滿的,為了保持負數的數值不變,這剩余的字節可能就需要進行符號位擴展(Signed),由后綴"S"表示,其配合"W"使用表示只進行低32位空余字節的擴展,配合"X"則表示進行整個64位的符號位擴展:
LDM/STM與三個問題
一個字節一個字節的傳送那是“螞蟻搬家”,如果要復制大批量的數據,效率實在不高,為此ARMv7還提供了用于批量傳輸的LDM和STM指令,"M"在這里代表Multiple。STM是把多個寄存器的值傳送到內存相鄰的位置,LDM反之。多個寄存器在ARM匯編語言中用"{}"圈起來,表示待傳送的寄存器列表。
比如"STM R0, {R4,R5}" 就表示將R4的值傳送到R0指向的內存單元,R5的值傳送到下一個內存單元。批量傳輸其實是存在一個方向問題的,為了區分下一個內存單元是在上一個單元的后面還是前面(地址更大還是更小),需要加上后綴"I"和"D"來分別表示"Increase"和"Decrease"。
還有一個問題,要將R5的值傳送到下一個內存單元,需要首先獲得這“下一個”內存單元的地址,這就涉及到地址的增減。假設R0的值是0,如果先增加"0"這個值(在32位系統中,一次增加的值是4),再傳送R4,那么就是[0x4]=R4, [0x8]=R5;如果是傳送完R4后再增加"0"這個值,那么就是[0x0]=R4, [0x4]=R5。所以還需要加上后綴"A"和"B"來分別表示"After"(傳送后增加)和"Before"(傳送前增加)。
因此,LDM/STM家族一共有"IA", "IB", "DA"和"DB"四個變種(variant),"LDM"和"STM"什么后綴都不接也可以直接使用,但它其實包含一個隱式規則,即默認為"IA",也就是說"LDM"和"STM"其實分別等同于"LDMIA"和"STMIA"。
在函數調用中,進入子函數的時候要用"PUSH"指令,把存儲在CPU寄存器中的局部變量/上下文保存到內存的棧中,退出子函數的時候要用"POP"指令,將棧中保存的內容恢復到對應的寄存器中,因為棧通常是自頂向下生長的,所以"PUSH"和"POP"其實可以分別用"STMDB"和"LDMIA"來替代。
STR R0,[R1, #12] // R0 --> [R1+12]
LDR R4,[R5, R6] // R4 <-- [R5+R6]
這里出現了一個"!"符號,那就是我們要解決的第三個問題:在增加/減少"SP"表示的這個數值(比如前面假設的"0")的時候,"SP"本身存儲的內容是否跟著一起變化?加上"!"就表示在傳送過程中"SP"會自增/自減,傳送完成后"SP"的值已經不再是傳送前的那個值了,不加"!"就是在傳送前后保持"SP"的內容不變。"SP"作為stack pointer,在入棧和出棧的時候自然是要移動的,所以這里用了"!"。
"!"是表示寄存器自增/自減的,所以它并不局限于配合LDM/STM使用,如果它用在STR指令中,比如"str r0, [r1, #12]!",就相當于"str r0 [r1, #12]"加上"r1 = r1+12",這也解決了本文前半段介紹LDR/STR指令時留下的那個問題。
新一代的LDP/STP
在ARMv8中,LDM/STM被新一代的指令LDP(Load Pair)和STP(Store Pair)所取代了,LDM/STM對寄存器列表里包含的寄存器數量并沒有什么限制,而LDP/STP要求和內存之間傳送數據的寄存器不超過2個。因為"PUSH"和"POP"完全可以用LDM/STM表示,所以他倆也被一并干掉了。兩代指令的對應關系大概是這樣的:
小結一下,本文主要介紹了ARMv7和ARMv8的數據傳送指令,并在其中穿插了ARM匯編語言中"[]", "{}", "!"符號的含義和用法。下文將介紹移位、序轉和位操作等數據處理指令。
上一篇:ARM匯編語言 - 簡介 [二]
下一篇:ARM匯編語言入門(七)
推薦閱讀
史海拾趣
作為一家有社會責任感的企業,奇力新始終關注環境保護和可持續發展。公司積極推廣綠色生產技術和資源循環利用方案,減少生產過程中的能源消耗和廢棄物排放。同時,奇力新還積極參與社會公益活動,為社區和環境貢獻自己的力量。這些舉措不僅體現了奇力新的社會責任擔當,也為其贏得了更多客戶和合作伙伴的信任和支持。
在激烈的市場競爭中,德崧電子始終堅持品質為先的經營理念。公司從原材料采購到生產加工,再到成品檢驗,每一個環節都嚴格把控,確保產品質量。這種對品質的堅守不僅贏得了客戶的信任,也為公司贏得了良好的口碑。正是憑借著過硬的產品質量和優質的服務,德崧電子在電子開關行業中樹立了良好的品牌形象。
進入20世紀90年代,電子行業的技術革新日新月異。Connor-Winfield敏銳地捕捉到了市場的變化,開始將產品線擴展到其他領域,以滿足更多客戶的需求。除了石英計時電路和振蕩器,公司還開始研發和生產一系列與電子應用緊密相關的產品。這些新產品的推出,不僅進一步鞏固了公司在行業內的地位,也為其開拓了更廣闊的市場空間。
為了進一步擴大市場份額,Cellergy公司積極尋求與其他電子企業的戰略合作。通過與一家知名電子產品制造商的合作,Cellergy公司的電容器產品得以進入更廣闊的市場。雙方共同研發新產品,共享技術和市場資源,實現了互利共贏。這一合作不僅提升了Cellergy公司的知名度,也為其帶來了更多的商業機會。
在全球化的浪潮下,EUtech公司積極尋求國際合作。他們與德國的氫探新能源公司建立了長期穩定的合作關系,共同研發燃料電池系統控制技術。通過共享資源、互補優勢,雙方不僅提高了產品的競爭力,還推動了整個行業的技術進步。
在電源適配器和服務器電源市場,EUtech公司憑借技術創新迅速崛起。他們不斷優化產品設計,提高產品性能,同時注重節能環保。隨著下游AI行業的快速發展,公司及時抓住機遇,推出了一系列高性能、高可靠性的電源產品,贏得了市場的廣泛認可。
請大家幫忙說下DA轉換器后面的電路一般接什么?我用的是TLV5619,12路數字量轉模擬量,出來的模擬信號是不是要加什么射隨器、采樣保持器什么的?麻煩說詳細點,謝謝… 查看全部問答∨ |
|
首先申明一下,我不是搞開發的也不是程序員,是一個用戶,發帖的目的就是因為我總的車載導航最大只支持2G的SD卡,機器也沒有usb口,是和車載dvd一體式的設計,用過網上的一些辦法,將SDHC的驅動考到windows目錄,將注冊表文件合并,可是這樣一重新 ...… 查看全部問答∨ |
|
一個工程里的一個部分,程序中出現如下表述: BEGIN HS_ein = Bereit # Betrieb; --on all the time, except for AUS or FEHLER or LADEN ...… 查看全部問答∨ |
目前很多藍牙產品越來越精小,對于板上空間來講就要處處考慮了,PCB天線是最常見的,但是比較占用大的PCB面積,陶瓷天線開始應用了。 有誰用過陶瓷天線的?可以分享一下應用效果么?一致性如何?… 查看全部問答∨ |