官术网_书友最值得收藏!

第3章 ARM處理器的指令系統

3.1 ARM微處理器的尋址方式

尋址方式是在指令中設置的用于處理器定位操作數位置的操作數表達方式。掌握尋址方式是編寫和閱讀匯編語言程序必須掌握的知識。

通常在高級語言的編程中是不需要關心程序語句所涉及數據的具體存放地點的,編程員只需要針對具體的變量或數據進行編程。而匯編語言與高級語言在編程方式上的一個重要不同點在于:程序語句所操作的數據許多情況下并不是數據本身,而是存放該數據的載體,如CPU內部的寄存器或外部的存儲單元,所以匯編語言編程員必須清楚程序語句中操作數的存放地點,并且能夠利用編譯系統提供的多種操作數定位方式(尋址方式)靈活地尋址位于不同數據存放點的數據。

匯編指令可尋址的數據存放點有4個,如下所述。

1)包含于指令中

這種方式的數據也稱為立即數,是直接書寫于指令中的數據。

2)包含于處理器內部的寄存器中

這種方式的數據稱為寄存器數,指令中對數據的操作不是針對數據,而是針對含有數據的寄存器。

3)包含于處理器外部的存儲器中

這種方式的數據稱為存儲器數據。盡管最終的操作數是存于存儲器中的數據,但指令中書寫的卻是代表數據的存儲單元地址。位于存儲單元中的操作數將由編譯程序按照匯編指令中給出的存儲器地址提取出來。

4)包含于外設端口中

在計算機系統的體系結構中,處理器與各種外設的的聯系都是通過外設及其接口電路中的各種信息存儲單元實現的,包括數據寄存器、控制寄存器、狀態寄存器等類型。盡管它們散落于不同的外設及其接口電路中,但處理器都必須為它們分配不同的地址加以區分,這些地址的總和稱為外設地址空間或I/O空間。在ARM處理器中,對外設端口數據的訪問沒有像X86系列處理器那樣用專門的IN/OUT指令,而是采用與訪問存儲器數據相同的指令格式,但嚴格地將I/O空間與存儲空間劃分在統一地址空間的不同區域內。

對于不同的處理器,其指令系統所能夠支持的尋址方式是不完全相同的。因為不同的處理器從編程員的角度來看,它們的主要區別在于不同CPU內部具有的寄存器類型和數量不同,另外訪問存儲器和外設端口的方式也不同,所以掌握一種處理器的匯編語言編程的首要問題是搞清楚該處理器所支持的尋址方式,以便在書寫程序指令時能夠正確地理解和運用操作數。下面將講述ARM指令系統支持的尋址方式。

3.1.1 立即尋址

立即尋址是將操作數直接書寫在匯編指令中的操作數指定方式,匯編指令被編譯為機器碼后,操作數將成為整個機器碼的組成部分,CPU在執行該指令時就可立即獲得所需的操作數,故包含的操作數也稱為立即數。

在ARM指令集中,有許多指令可以采用立即數(立即尋址),但由于RISC結構指令長度的限制,立即數在機器碼中只能占有少量的位空間,不是任意的數值都可以作為立即數,立即數必須符合一定的生成規則才會有效,這將在后續內容介紹。

采用立即數的簡單指令示例如下:

MOV R0,#0x55    ;#0x55為十六進制立即數,指令功能為:R0=0x55
ADD R0,R1,#5   ;#5為十進制立即數,指令功能為:R0=R1+5
ADD R0,R0,#0x5d ;#0x5d為十六進制立即數,指令功能為:R0←R0+0x5d

說明:匯編指令中用“#”號標示一個立即數,其后用“0x”或“&”標示立即數為十六進制,如果默認則標示一個十進制立即數。

3.1.2 寄存器尋址

寄存器尋址就是事先將需要操作的數據放置在某個寄存器中,然后在指令中指定該寄存器(攜帶操作數)參與運算或操作。由于寄存器是距離處理器核心最近的存儲單元,具有很快的讀寫操作速度,所以是匯編指令中頻繁使用的一類尋址方式。指令示例如下。

例1:ADD R0,R1,R2            ;寄存器R1和R2內容相加,結果送R0
例2:ADD R0,R1,R2,ROR #5    ;R0=R1+(R2循環右移5位)
例3:MOV R0,R1,LSL R3        ;R0=R1邏輯左移R3內容指定的位數

例1是一條加法運算指令,加數和被加數都預先存于R1和R2中。指令實現將R1和R2的內容相加,結果存于另外的寄存器R0中。其中R1和R2也稱為源操作數寄存器,R0稱為目的操作數寄存器。指令中源和目的操作數都采用了寄存器尋址方式。

例2和例3中源操作數和目的操作數同樣采用了寄存器尋址方式。與例1不同的是,源操作數的一個寄存器內容在運算或操作之前進行了移位操作。由于ARM處理器內置了桶形移位寄存器,可以實現在一條指令中進行數據移位和運算的雙重操作,所以各種采用寄存器尋址的運算或操作指令可以在一條指令內先對寄存器內容進行移位后再進行運算或操作。在ARM指令中沒有專門的移位指令,移位操作都是融合在各種采用寄存器尋址的運算或操作指令內一并完成的。有效的移位方式有邏輯左移(LSL)、邏輯右移(LSR)、算術右移(ASR)、循環右移(ROR)、帶擴展的循環右移(RRX)共5種,詳細內容請參見3.3.5節的內容。

3.1.3 單存儲器數據尋址(位于存儲器中的單字節、單字、半字等單個數據的尋址)

寄存器盡管具有高的讀寫速度,但數量有限,大量的數據只能存放在CPU外部的存儲器中,所以指令必須提供能夠對外部存儲器數據進行訪問的尋址方式。在ARM的指令系統內,對存儲器單元的尋址必須通過一個通用寄存器進行。有以下幾種可選的方式。

  • 寄存器間接尋址:以一個通用寄存器的內容作為存儲器單元的地址。
  • 基址變址尋址:以一個通用寄存器的內容作為基地址,再以一個合法的立即數或者另外一個寄存器(甚至一個移位后的寄存器)的內容為偏移地址相加/減的結果作為存儲器單元的地址。

1.寄存器間接尋址

寄存器間接尋址以一個通用寄存器的內容作為存儲器地址,并從所尋址的存儲單元(字節、字、半字等)讀取一個數據或者向內寫入一個數據。例如以下指令:

ADD R0,R1,[R2]               ;R0←R1+[R2]
LDR R0,[R1]                   ;R0←[R1]
STR R0,[R1]                   ;[R1]←R0

第一條指令中寄存器R2的內容將作為存儲器的地址并從其指向的存儲單元讀取一個字數據,然后與R1寄存器內的數據相加,結果送入寄存器R0中。

第二條指令直接以R1的內容為地址尋址存儲器,并將讀出的字數據送入R0內。

第三條指令是將R0的內容傳送到以R1的值為地址的存儲器字單元內。

2.基址變址尋址

基址變址尋址是另外一種對存儲器數據的尋址方式,其存儲器地址是由兩部分內容合成的。一個部分為某個通用寄存器,其內容稱為基地址。另一個部分為一個立即數或一個寄存器(或者一個帶移位操作的寄存器),稱為變址(偏移地址)。最終的存儲器地址是由包含基地址的寄存器(稱為基址寄存器)內容與另外的變址值(立即數或另一個寄存器內容)相加/減而得到的。變址尋址方式常用于訪問以某個固定地址(基地址)為中心的前后地址單元。采用變址尋址方式的指令常見有以下幾種形式:

LDR R0,[R1,#4]               ;R0←[R1+4]
LDR R0,[R1,#4]!                 ;R0←[R1+4],R1←R1+4
LDR R0,[R1],#4               ;R0←[R1],R1←R1+4
LDR R0,[R1,-R2]                  ;R0←[R1-R2],
LDR R0,[R1,R2,LSL #4]           ;R0←[R1+R2內容左移4位]

第一條指令以寄存器R1內容為基地址再加上立即數4形成最終的存儲器地址,然后從該存儲單元讀出一個字數據送到寄存器R0內,操作完成后R1的值不變。

第二條指令同樣以寄存器R1內容為基地址再加上立即數4形成最終的存儲器地址,然后從該存儲單元讀出一個字數據送到寄存器R0內。但不同的是操作完成后基址寄存器R1的值將變為:R1=R1+4。這種在操作進行前改變基地址值的方式稱為立即數前變址尋址(Immediated pre-indexed)方式。

第三條指令先以R1的內容為存儲器地址尋址存儲單元,并在將讀到的數據傳送給寄存器R0后增值,R1的內容為:R1=R1+4。這種在操作完成后改變基地址值的方式稱為立即數后變址尋址(Immediated post-indexed)方式。

第四條指令以寄存器R1的內容為基地址,以R2的內容為變址,并以R1-R2為存儲器地址尋址存儲單元,最后將讀出的數據傳送給寄存器R0,操作完成后R1的值不變。

第五條指令以寄存器R1的內容為基地址,以寄存器R2的內容左移4位后為變址,以R1+(R2左移4位)為存儲器地址尋址存儲單元,最后將讀出的數據傳送給寄存器R0,操作完成后R1的值不變。

3.1.4 多寄存器尋址

多寄存器尋址是RISC處理器中實現寄存器與存儲器之間進行批量數據傳輸的一種特殊方式。由于RISC處理器內部設置有較多的寄存器,這些寄存器內容在進行程序轉移或異常/中斷操作時需要批量地保存起來。而當程序返回時又需要批量地將它們恢復到原來的寄存器內。為了快捷方便地實現這類操作,ARM處理器設立了一種僅需一條指令就可以實現多個寄存器與一個連續的存儲器區之間數據傳送的方式,最多可以針對16個通用寄存器進行操作。這類指令的基本格式為:

LDMXX/STMXX Rn! ,(寄存器列表)

其中的Rn是作為存儲區地址指針的寄存器,稱為基地址寄存器。寄存器列表是參與數據傳輸的寄存器組合。LDM和STM指令后面的XX代表了IA、IB、DA、DB等4個后綴選項,分別對應在多個寄存器與存儲器之間進行數據讀寫操作時,存儲器的地址指針是按遞增還是遞減的方向變化,以及是先讀寫數據后變化地址指針還是先變化地址指針后讀寫數據。這4個后綴選項的具體含義如下所述。

1)IA(Increment After):先讀寫數據后遞增存儲器地址值

各寄存器與存儲單元的對應情況及儲器地址值在數據傳輸過程中的變化情況為:

  • 序號最小的寄存器在存儲器的對應地址=基址寄存器Rn的值;
  • 序號最大的寄存器在存儲器的對應地址=Rn的值+參與傳輸的寄存器數量×4;
  • 操作結束后的Rn值(若允許Rn變化)=Rn起始值+參與傳輸的寄存器數×4。
2)IB(Increment Before):先遞增存儲器地址值后讀寫數據

各寄存器與存儲單元的對應情況及儲器地址值在數據傳輸過程中的變化情況為:

  • 序號最小的寄存器在存儲器的對應地址=基址寄存器Rn的值+4;
  • 序號最大的寄存器在存儲器的對應地址=Rn的值+參與傳輸的寄存器數量×4;
  • 操作結束后的Rn值(若允許Rn變化)=Rn起始值+參與傳輸的寄存器數×4。
3)DA(Decrement After):先讀寫數據后遞減存儲器地址值

各寄存器與存儲單元的對應情況及儲器地址值在數據傳輸過程中的變化情況為:

  • 序號最大的寄存器在存儲器的對應地址=基址寄存器Rn值;
  • 序號最小的寄存器在存儲器的對應地址=Rn值–(參與傳輸的寄存器數量×4)+4;
  • 操作結束后的Rn值(若允許Rn變化)=Rn起始值+參與傳輸的寄存器數×4。

注意:本方式可以理解為存儲器地址遞減變化過程中是先讀寫序號大的寄存器。

4)DB(Decrement Before):先遞減存儲器地址值,后讀寫數據

各寄存器與存儲單元的對應情況及儲器地址值在數據傳輸過程中的變化情況為:

  • 序號最大的寄存器在存儲器的對應地址=基址寄存器Rn值-4;
  • 序號最小的寄存器在存儲器的對應地址=Rn值-參與傳輸的寄存器數量×4;
  • 操作結束后的Rn值(若允許Rn變化)=Rn值-參與傳輸的寄存器數×4。

注意:本方式可以理解為存儲器地址遞減變化過程中是先讀寫序號大的寄存器。

地址遞增方式示例如圖3-1所示,假設操作前R0的初值為0x0010。

圖3-1 兩種地址遞增方式下的多寄存器尋址指令執行示例圖

地址遞減方式示例如圖3-2所示,假設操作前R0的初值為0x0020。

圖3-2 兩種地址遞減方式下的多寄存器尋址指令執行示例圖

注意:LDM/STM指令與LD/STR指令格式的以下不同點:

①LDM/STM指令中提供存儲器地址的寄存器沒有方括號[]。

②LDM/STM指令中前面操作數都是提供存儲器地址的寄存器,后面是寄存器列表。

LDM/STM指令的機器碼格式如圖3-3所示。

圖3-3 LDM/STM指令的機器碼格式

說明:P=0對應先變化Rn的值再讀寫數據方式,且與U值相關,U=0時Rn的值指向本次操作數據最高地址加4的地址;U=1時Rn的值指向本次操作數據最低地址減4的地址。

P=1對應先讀寫數據再變化Rn值的方式,且與U值相關,U=0時Rn的值指向本次操作數據最高地址;U=1時Rn的值指向本次操作數據最低地址。

3.1.5 堆棧尋址及其若干模式

堆棧是定義于存儲區內的一塊特殊區域,專用于保存當程序發生轉移運行(如異常、中斷、調用子程序、任務切換等)時的原場景信息,如處理器內部的各類寄存器值。這些保存在堆棧中的寄存器值在程序返回時必須準確地恢復到它們原來的寄存器內。為了準確地對應寄存器與它們內容所存入的堆棧單元,規定了一種特殊的堆棧操作方式:向堆棧存數(入棧)和從堆棧取數(出棧)嚴格遵循先進后出(First In Last Out,FILO)的順序操作原則(堆棧區數據的操作就類同沖鋒槍彈夾中的子彈裝填)。入棧操作結束時堆棧指針指向的單元是最后一個寄存器內容存入的地址,這樣就能夠在出棧操作時以第一個出棧內容恢復到原來的寄存器內。但是必須保證入棧操作完成后到出棧操作前的時間段內保持堆棧指針值不變。

堆棧尋址是一組寄存器與若干連續存儲單元間的操作,也可以看成多寄存器尋址的一種特殊運用方式。由于被標識為堆棧區的存儲單元處理器必須按照FILO的方式進行存取操作,而且要求在完成入棧操作后保持堆棧指針不變,始終指向棧頂的位置,這樣才能保證在出棧操作時正確地將堆棧內容恢復到原有寄存器內。但是與入棧操作STM指令配合的出棧指令LDM必須采用與入棧相反的順序從堆棧取數,這就要求從堆棧區讀取數據的LDM操作要與向堆棧區寫入數據的STM操作配合成對運用。

在ARM指令模式下每執行一次堆棧操作堆棧指針可自動遞增或遞減4,以指向棧頂位置。在遵循這樣的基本操作原則的前提下,在不同的計算機系統中,有的采用堆棧數據按照增地址方式增長(存入數據后堆棧指針遞增),而有的則采用堆棧數據按照減地址方式增長(存入數據后堆棧指針遞減);另外,在堆棧指針的變化上,有些采用先存入數據再變化堆棧指針,有些則采用先變化堆棧指針再存入數據的方式,這就造成了堆棧操作的多樣性。ARM處理器為了適應各種軟件環境的需要,設置了多種堆棧操作模式供程序選擇。按照堆棧的生長方向和堆棧指針的變化方式設置了四種堆棧方式并以指令后綴形式供用戶選擇。它們分別為:

  • FD(Full Decending Stack),滿遞減堆棧操作(STMFD=壓棧,LDMFD=出棧);
  • FA(Full Ascending Stack),滿遞增堆棧操作(STMFA=壓棧,LDMFA=出棧);
  • ED(Empty Decending Stack),空遞減堆棧操作(STMED=壓棧,LDMED=出棧);
  • EA(Empty Ascending Stack),空遞減堆棧操作(STMEA=壓棧,LDMEA=出棧)。

滿堆棧的操作特點是:先移動4字節的偏移地址(減4或加4,即先變址)再寫入4字節的字數據。由于堆棧操作結束后的當前堆棧指針所指向的存儲單元存有最后一個壓入堆棧的數據,故稱為滿堆棧(Full Stack)。

空堆棧的操作特點是:先寫入字數據,然后移動4字節的偏移地址(減4或加4,即后變址)。由于堆棧操作結束后的當前堆棧指針所指向的存儲單元沒有本次存入的數據(距最后一個壓入堆棧的數據相差4字節),故稱為空堆棧(Empty Stack)。

同時,根據堆棧的生長方式又可以分為遞增堆棧(Ascending Stack)和遞減堆棧(Decending Stack),若堆棧指針隨著數據的存入不斷由低地址向高地址增長,稱為遞增堆棧;反之則稱為遞減堆棧。這樣就有四種類型的堆棧工作方式,ARM微處理器支持這四種類型的堆棧工作方式。

  • 滿遞增堆棧:堆棧指針指向最后壓入的數據,堆棧由低地址向高地址增長;
  • 滿遞減堆棧:堆棧指針指向最后壓入的數據,堆棧由高地址向低地址增長;
  • 空遞增堆棧:堆棧指針指向下一個將放入數據的空位置,且由低地址向高地址增長;
  • 空遞減堆棧:堆棧指針指向下一個將放入數據的空位置,且由高地址向低地址增長;

將字數據0x12345678壓入堆棧后的滿堆棧和空堆棧(小端格式)分別如圖3-4和圖3-5所示。

由于堆棧操作必須按照先進后出,以及壓棧時的源寄存器必須是出棧時的目的寄存器的原則,所以遞減堆棧在進行壓棧操作時堆棧指針按遞減進行,而對應的出棧操作堆棧指針則必須按遞增進行。例如,滿遞減堆棧的出棧操作LDMFD堆棧指針實際上是遞增的,而不是遞減,FD只表明它所配合的壓棧操作是FD。LDMFD等效于LDMIA。

圖3-4 滿堆棧示意圖

圖3-5 空堆棧示意圖

多寄存器尋址與堆棧尋址的對應關系如表3-1所示。

表3-1 多寄存器尋址與堆棧尋址的對應關系

關于堆棧尋址的應用例子請參見3.4.2節。

主站蜘蛛池模板: 同江市| 南川市| 响水县| 格尔木市| 米脂县| 台山市| 商都县| 竹溪县| 阜康市| 郴州市| 昌江| 宿州市| 旌德县| 鹤峰县| 嫩江县| 承德县| 桑日县| 封丘县| 邹城市| 高州市| 防城港市| 锡林浩特市| 榆社县| 鞍山市| 澄城县| 吉首市| 新乡县| 双辽市| 松桃| 陵水| 绵阳市| 饶平县| 商河县| 庄浪县| 太康县| 永顺县| 大厂| 湖南省| 奈曼旗| 西吉县| 来安县|