尋址方式指的是確定操作數位置的方式。
尋址方式:
立即數尋址
直接尋址(絕對尋址),ARM不支持這種尋址方式,但所有CISC處理器都支持
寄存器間接尋址
3種尋址方式總結如下:
助記符 RTL格式 描述
ADD r0,r1,#Q [r0]<-[r1] + Q 立即數尋址:把整數Q與寄存器r1的內容相加
LDR r0,Mem [r0]<-[Mem] 絕對尋址:存儲單元內容加到寄存器r0中
LDR r0,[r1] [r0]<-[[r1]] 間接尋址:把r1所指存儲單元的值加載到r0中
立即數尋址
在介紹ARM如何實現立即數尋址之前,先來看一個使用立即數尋址的實例。高級語言
常用立即數尋址來指定一個常量而不是變量,如:
IF I > 25
THEN J = K + 12
常量12和25由立即數尋址指定。
假設I在r0中,J在r1中,K在r2中,可表示為:CMP r0,#25 ; 把I與25比較BLE Exit ; 如果 I <= 25,則退出ADD r1,r2,#12 ; 否則K+12Exit ;
使用條件執行表示:CMP r0,#25 ; I與25比較ADDGT r1,r2,#12 ; 如果I > 25,則J = K + 12
ARM的立即數實現方法
ARM在指令中提供了12位的立即數字段,但不支持值在0~4095之間的12位無符號立即數或
值在-2048~2047之間的12位有符號立即數。實際上ARM提供的是可按2的冪縮放的8位立即
數。
下圖,給出了ARM立即數的編碼結構,當操作碼的第25位為0時,ARM將進行一次移位操
作,當第25位為1時,操作數2字段將編碼12位立即數,它被分成兩部分:8位立即數和4位
對齊碼。
立即數字段中最高4位知道了立即數在32位字中的對齊方式。
如果8位立即數為N,4位對齊碼為 n(范圍在0~15之間),則立即數的值為N x 22n,因
此ARM提供了一個可按2的冪縮放的8位立即數,這與浮點數的表示和存儲方式相似。
下圖描述了一個ARM立即數縮放,該圖說明了對齊碼是如何在32位框架移動立即數的。為
了得到立即數循環右移的位數,必須將對齊碼的數字翻倍。
盡管不能直接指定32位立即數,但ARM縮放結構可以表示常量FF00000016,即用8位FF16
左移24位(即右移8位)來表示。
取反傳送指令MVN r0,r1,#literal,能夠指定一個不必移位且范圍在0xFFFFFF00到
0xFFFFFFFF的常量。程序員不用擔心如何產生移位常量,這是匯編器的工作。立即數的縮
放不是偽操作。
如,指令MOV r1,#0x0000FF00的二進制代碼為E3A01CFF16:
MOV r0,#0xFF
MOV r1,#0x0000FF00
MOV r2,#0xFF000000
以指令MOV r1,#0xFF00為例,立即數編碼為CFF16(1100111111112),對齊碼為C16或1210。
實際移位位數是這個數的2倍,即2410,通過循環右移來實現。24次循環右移操作等于8次
循環左移操作,即將FF16移位為FF0016,最后來看0xFF000000,將十六進制數左移6次,
相當于將二進制左移24次,不過這里使用循環右移,等價于循環右移8次。要保存的縮放
常數(即對齊碼)是移位次數的一半,即4。上圖中第6行指令的常量編碼為4FF。
寄存器的間接尋址
操作數的地址保存在寄存器中,這種尋址方式叫作寄存器間接尋址,也叫索引尋址或基址
尋址。ARM的立即數偏移量為12位。它確實是12位立即數,不是8位可縮放的值。
什么時候是真正的12位
ARM處理器指定12位常量作為立即操作數(如ADD r0,r1,#123)。常量為8位數并通過4位
對齊碼進行放大。
然而,當ARM處理器指定某個立即數偏移量作為索引地址的一部分時,它就是真正的12位
數,如LDR r0,[r1,#123]。
寄存器間接尋址
寄存器間接尋址通過3個讀操作來訪問一個操作數:
(1)讀指令得到指針寄存器
(2)讀指針寄存器得到操作數地址
(3)讀操作數地址所指的存儲器單元得到操作數
寄存器間接尋址可以在運行時修改寄存器的內容,而寄存器中含有指向實際操作數的指針
(地址),因此地址是變量,允許訪問如數組、列表、矩陣、向量、表格等數據結構。
下圖,通過ARM的加載指令LDR r1,[r0]說明了寄存器間接尋址,指針寄存器r0的值為n,則
指向或引用存儲單元n:
LDR r1,[r0]和ADD r0,r0,#4這兩個操作的RTL定義:
[r1] <- [[r0]] ; 讀取r0所指存儲單元的值,[[r0]]就是r0所指存儲單元的值
[r0] <- [r0] + 4 ; 指針遞增指向下一個存儲單元
考慮下面的例子,表格中的7個項分別代表一個星期的每一天。D1代表星期一,D2代表星
期二,等等。如果Di是第i天,則Di+1就表示下一天,從一天移到下一天,只需將索引i加1,
這就是需要變址的原因:
ADR r0,Week ; r0指向數組WeekADD r0,r0,r1, LSL #2 ; r0現在指向r1所含那一天LDR r2,[r0] ; 讀取這一天的數據到r2Week DCD ; 第1天的數據DCD ; 第2天的數據.DCD ; 第7天的數據
假定天數的索引為0~6,天數的索引必須乘以4,因為這里的數組是字的數組(每個字4個
字節),連續兩個元素的地址之差為4。
字符串是一個很好的使用基于指針尋址的例子。假設要找到某個特定字符在字符串中的位
置,可以寫出下面代碼。這不是ARM代碼,因為還沒有介紹字節操作。使用下標表明操作
數的大小:
LDR32 r0,#String ; r0指向StringLoop LDR8 r1,[r0] ; REPEAT 讀取字符ADD32 r0,r0,#1 ; 更新字符指針CMP8 r1,#Terminator ; UNTIL 發現終止符(終止符為行結束字符)BNE Loop ;
有些計算機將寄存器間接尋址與指針更新結合在一起,這樣指針在被使用之后就可以自動
地指向下一個存儲單元。
考慮下面的C代碼段:
程序中數0、21、10是匯編期間有立即數尋址方式指定的常量。轉為下面的ARM匯編語言
帶偏移量的寄存器間接尋址
操作數有效地址是寄存器的內容加上編碼在load/store指令的立即數偏移量,這種尋址方式
也叫基址加位移尋址。
下圖,用指令LDR r0,[r1,#4]說明,圖中有效地址是指針寄存器r1的內容加上偏移量4的和,
即操作數距離指針所指的地址4個字節:
下述代碼段說明了如何使用偏移量實現數組訪問,偏移量是常量,在運行時不能改變:
Sun EQU 0 ; 一星期中每一天的偏移量Mon EQU 4 Tue EQU 8 .Sat EQU 24 ADR r0,Week ; r0指向數組WeekLDR r2,[r0,#Tue] ; 讀取星期二的數據到r2LDR r3,[r0,#Wed] ; 讀取星期三的數據到r3ADD r4,r2,r3 ; 星期二的數據與星期三的數據相加STR r4,[r0,#Mon] ; 把結果存放到星期一Week DCD ; 第1天的數據(星期天)DCD ; 第2天的數據(星期一)DCD ; 第3天的數據(星期二)DCD ; 第4天的數據(星期三)DCD ; 第5天的數據(星期四)DCD ; 第6天的數據(星期五)DCD ; 第7天的數據(星期六)
ARM允許指定第二個寄存器作為偏移量,這樣就可以使用運行時可以修改的動態偏移量,
如下圖:
LDR r2,[r0,r1] ; [r2]<-[[r0] + [r1]]把r0加r1所指存儲單元的值加載到r2中
LDR r2,[r0,r1,LSL #2] ; [r2]<-[[r0] + 4 x [r1]]將r1乘4
寄存器r1擴大了4倍。當處理數組時,允許使用一個被縮放的偏移量。如果r0指向數組X且
r1包括索引i,則元素i的地址為X+4i。這樣就可以使用指令LDR r2,[r0,r1,LSL #2]訪問該元素。
可以通過向基址寄存器增加立即數偏移量或寄存器偏移量來擴展寄存器間接尋址方式。在
ARM術語中,基址寄存器加偏移量的尋址方式叫作前索引,因為是在訪問操作數之前把偏
移量加到指針上。指令LDR r0,[r1,#8]指定了前索引尋址方式,操作數的有效地址由[r1]+8給
出。這里,前索引表示偏移量#8在load操作的讀階段訪問寄存器之前就被加到基址寄存器
r1上。
前索引尋址方式可以來訪問數組X的元素i,如:
ADR r0,X ; 寄存器r0指向數組X
LDR r1,[r0,i] ; 讀出元素i
ARM的自動前索引尋址方式
通過將偏移量加到基址寄存器(指針寄存器)上ARM實現了兩種自動索引方式。這兩種方
式的差別在于基址寄存器遞增的時機——要么在訪問寄存器之前,要么在之后。
ARM的自動前索引尋址方式是在有效地址后面添加后綴!來表示。如:
LDR r0,[r1,#8]! ; 將寄存器r1+8所指存儲單元中的字加載到r0中
; 然后將r1加8以更新指針
RTL定義如下:
[r0] <- [[r1] + 8] 訪問地址為基址寄存器r1+8的存儲單元
[r1] <- [r1] + 8 加上偏移量更新指針(基址寄存器)
考慮下面兩個數組相加的例子:
Len BQU 8 ; 數組長度為8個字ADR r0,A-4 ; 寄存器r0指向數組AADR r1,B-4 ; 寄存器r1指向數組BADR r2,C-4 ; 寄存器r2指向數組CMOV r5,#Len ; 寄存器r5用作循環計數器
Loop LDR r3,[r0,#4]! ; 取出A的元素LDR r4,[r1,#4]! ; 取出B的元素ADD r3,r3,r4 ; 兩元素相加STR r3,[r2,#4]! ; 和保存到C中SUBS r5,r5,#1 ; 測試循環是否結束BNE Loop ; 重復直到全部完成
ARM自動后索引尋址方式
自動后索引尋址方式首先訪問基址寄存器所指的存儲單元中的操作數,然后將基址寄存器
遞增。如:
LDR r0,[r1],#8 ; 將r1所指的字加載到r0中,然后完成后索引,即r1加8
后索引把偏移量放在方括號的外面(如[r1],#8),RTL定義為:
[r0] <- [[r1]] 訪問基址寄存器r1所指存儲單元
[r1] <- [r1] + 8 加上偏移量更新指針(基址寄存器)
程序計數器相對尋址
寄存器r15是一個程序計數器,把r15用作訪問操作數的指針寄存器,這種尋址方式叫作程
序計數器(PC)相對尋址。操作數地址由其與當前代碼的相對位置確定。這意味著可以將
代碼及與之相關的數據移動到存儲器中的不同地方,而無需重新計算操作數地址。
假設執行指令LDR r0,[r15,#100],操作數地址距離寄存器r15的內容的相對偏移為100字
節(25個字),因此,操作數位于當前位置偏移100字節處。
ARM的load與store指令編碼
訪存操作有一條條件執行字段,即操作碼的第28~31位,它們可以像其他ARM指令一樣條
件執行:
; if(a==b) then x = p else x = qCMP r1,r2 ; if(a==b)LDREQ r3,[r4] ;then x = pLDRNE r3,[r5] ;else x = q
操作碼第20位選擇數據傳送方向,即指令是load還是store
第25位(#位)決定了偏移量是帶可選移位的寄存器內容還是12位常量
第22位選擇操作數大小,并確定ARM是傳送32位字還是8位字節,當字節被加載到32位寄存器中時,
寄存器的第8~31位被置0。
基址寄存器r基址是存儲器指針
U位定義了有效地址的計算機是加上還是減去偏移量
W位決定了當前指令結束時基址寄存器是否會被更新,W=1,則會更新基址寄存器
P位控制偏移量是計算機有效地址之前還是在之后被加到基址寄存器上
因為U位決定了對偏移量進行加法還是減法,ARM能使用以下尋址方式:
LDR r0,[r1,+r2] ; 有效地址是[r1] + [r2]
LDR r0,[r1,-r2] ; 有效地址是[r1] – [r2]
下表總結了ARM基于寄存器的尋址方式:
考慮二進制字符串01010111001000100100000100000110表示一條ARM指令,把這條指令分
解,得到下表中的編碼,得到的指令就是STRPL r4,[r2,-r6,LSL #2]!