ARM彙編之合法立即數的快速判斷方法

ARM彙編之立即數的快速判斷方法

2014-11-30   北京海淀區  張俊浩

在ARM彙編的數據處理指令中經常會使用到常數,而ARM彙編中規定使用的常數必須是立即數

在討論什麼是立即數,爲什麼有立即數,如何快速判斷立即數之前,我們先來弄明白一個問題:什麼不是立即數。

彙編指令是對數據(指令、數據)對象的操作,很關鍵的一個問題我們如何尋找我們的操作對象?彙編指令是一門關於尋址的藝術。ARM有九種尋址方式:1.寄存器尋址2.立即尋址3.寄存器移位尋址4.寄存器間接尋址5.基址尋址6.多寄存器尋址7.堆棧尋址8.塊拷貝尋址9.相對尋址。 九中尋址方式都是再告訴我們如何尋找我們的操作對象,其中立即數尋址相對於其他尋址方式有什麼不同?

爲了理解ARM指令是如何實現對數據對象操作,我們需要知道ARM指令的機器碼格式。(熟悉的可以直接跳到如何快速判斷合法立即數)(ARM指令的典型編碼格式如下:)


ARM指令語法格式如下:

opcode{<cond>}  {s}  rd,  rn, <opcode2>

各個部分解釋如下:

* cond(bit[31:28]):指令的條件碼助記符,默認是al(無條件執行)

*type(bit[27:32]):指令碼類型,根據其編碼的不同,所代表的類型:00:數據處理指令及雜類Load/Store指令;01:Load/Store指令;10:批量Load/Store指令及分支指令;11:協處理指令與軟中斷指令

*X(bit[25])         :第二操作數類型標誌碼。(X=0表示第二操作數是移位寄存器,X=1表示第二操作數是立即數)

* opcode(bit[24:21]) :指令助記符,如mov

* S(bit[20]): 指令的執行是否影響CPSR(當前程序狀態寄存器)的值(S:state)

*  Rn(bit[19:16]): 包含第一個操作數的寄存器編碼(Register Num)

*  Rd(bit[15:12]): 目標寄存器編碼(Register Destination

* opcode2(bit[11:0]): 第二操作數

ARM指令的第二操作數用法比較靈活,有如下幾種情況。

* 立即數

* 寄存器

*  寄存器移位

ARM指令裏有兩個操作數Rnopcode2和一個目的寄存器Rd(用於存放操作結果),Rn爲包含第一個操作數的寄存器地址,opcode2有三種形式:立即數方式、寄存器方式、寄存器移位方式,其中寄存器和寄存器位移方式也都是存儲的寄存器地址所以要通過寄存器間接獲得數據對象,也就是非立即而立即數形式不同,指令中opcode2數據域不是地址而是數據本身,所以叫立即數也就是說其它尋值方式最終操作的數據對象是放在Rnopcode2所指向的寄存器地址中的數據,顯然立即數操作對象也需要根據地址尋找,但它所在的地址比較特殊,或者說它存儲的位置比較特殊,因爲它直接存儲在指令opcode2中

opcode2只有12位,也就是說opcode2所表示的立即數有一定限制0-4095,爲了進一步擴大12bit數據所能表示數的範圍,ARM規定了數據的格式:也即

立即數是由一個8位的常數循環右移偶數位得到的,其中循環右移 的位數由一個4位2進制的兩倍表示,公式如下:

immediate=immed_8<<(2*rotate_imm4) “<<”表示循環右移 簡單的說一個常數如果可以由一個8位的常數循環移位偶數位得到,那麼就是立即數。


爲什麼會有立即數這樣的規定呢?這是由於所有的ARM指令是精簡指令集,指令長度固定都是32位,對於ARM數據處理指令自然也是一樣。數據處理指令大致可包含3類,數據傳送指令、數據算術邏輯運算指令和數據比較指令。在一條ARM數據處理指令中,除了要包含處理的數據值外,還要標識ARM命令名稱,控制位,寄存器等其他信息。這樣在一條ARM數據處理指令中,能用於表示要處理的數據值的位數只能小於32位。

ARM在指令格式中設定,只能用指令機器碼32位中的低12位來表示要操作的常數。ARM處理器是按32位來處理數據的,ARM處理器處理的數據是32位,如果簡單的用這12位來表示,顯然範圍太小了,爲了擴展到32位,因此使用了構造的方法,在12位中用8位表示基本數據值,用4位表示位移值,通過用8位基本數據值往右循環移動4位位移值*2次,來表示要操作的常數。這裏要強調最終的循環次數是4位位移值乘以2得到的,所以得到的最終循環次數肯定是一個偶數,爲什麼要乘以2呢,實質還是因爲範圍不夠,4位表示位移次數,最大才15次,加上8位數據還是不夠32位,這樣只能通過ALU的內部結構設計將4位位移次數乘以2,這樣就能用12位表示32位常數了。

通過循環偶數位得的到操作數,擴大了操作數的範圍,但也帶來了問題,並不是每個數據都能通過8位基本數據循環移動偶數爲得到,如果你在ARM數據處理指令中使用的操作數,不是立即數,比如MOV R1,#0x12345678,編譯器就會報錯,所以我們在使用前必須進行判斷,這也是很多ARM相關求職筆試中常考的一道題目。

那怎樣怎麼快速判斷一個數是否是立即數?對於簡單的數字我們可以直接判斷,比如小於255的數字肯定是立即數。對相對複雜的數字進行判斷就需要先把它轉換爲2進制形式,然後根據定義進行判斷了。我這裏總結了個比較快速的方法:(簡而言之,就是利用立即數的生成方法,迅速逆推到合法的8位常熟)

1、把數據轉換成二進制形式,從低位到高位寫成4位1組的形式,最高位一組不夠四位的,在最高位前面補0。

2、數1的個數,如果大於8個肯定不是立即數,如果小於等於8進行下面步驟。

3、如果數據中間有連續的大於等於24個0,循環左移4的倍數,使高位全爲0。

4、找到最高位的1,去掉前面最大偶數個0。

5、找到最低位的1,去掉後面最大偶數個0。

6、數剩下的位數,如果小於等於8位,那麼這個數就是立即數,反之就不是立即數。

針對可能現的情況,舉5個典型例子:

(1)0x4FF (2)0x122 (3)0x234 (4)0xF000000F (5)0x8000007F

例1: 0x4FF

第一步:0100 1111 1111

第二步:其中1的個數是9個,大於8個,判定不是立即數

例2: 0x122

第一步: 0001 0010 0010

第二步: 其中1的個數4個,小於8,繼續

第三步: 其中沒有連續大於等於24個0,繼續

第四部: xx01 0010 0010 (最高位前面有3個0,最大偶數2,去掉2個0)

第五步: xx10 0011 0010 (最低位後面只有1個0,最大偶數0)

第六部: 剩下10 0011 0010 共10位,大於8,判定0x122不是立即數

例3: 0x234

第一步: 0010 0011 0100

第二步: 其中1的個數4個,小於8,繼續

第三步: 其中沒有連續大於等於24個0,繼續

第四部: xx10 0011 0100

第五步: xx10 0011 01xx

第六部: 剩下10 0011 01 共8位,等於8,判定0x234是立即數

例4: 0xF000000F

第一步: 1111 0000 0000 0000 0000 0000 0000 1111

第二步: 其中1的個數8個,沒有大於8,繼續

第三步: 其中有連續24個0,循環左移4位,使高位全爲0

0000 0000 0000 0000 0000 0000 0000 1111 1111

第四部: xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 1111

第五步: xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 1111

第六部: 剩下1111 1111共8位,等於8,判定0xF000000F是立即數

例5: 0x8000007F

第一步: 1000 0000 0000 0000 0000 0000 0111 1111

第二步: 其中1的個數8個,沒有大於8,繼續

第三步: 其中有連續24個0,循環左移4位,使高位全爲0

0000 0000 0000 0000 0000 0000 0111 1111 1000

第四部: xxxx xxxx xxxx xxxx xxxx xxxx 0111 1111 10xx

第五步: xxxx xxxx xxxx xxxx xxxx xxxx 0111 1111 10xx

第六部: 剩下0111 1111 10共10位,等於8,判定0x7000008F是立即數

問題還沒有結束,我們在ARM彙編中如何規避立即數這個問題呢,其實可以使用ARM彙編LDR僞指令,例如直接把MOV指令變爲, LDR R1,=0x12345678這樣編譯器就不會報錯了。但這種方法也有弊端會增加開銷和影響執行效率。同時ARM彙編中還有有效數的概念,比如 MOV R1,#0xFFFFFFFF 指令中 0xFFFFFFFF 不是立即數,但是是有效數,編譯器最自動把原指令變換爲 MVN R1,#0,也不會報錯。有效數判定:原數是立即數或者原數反碼是立即數。

希望對大家有幫助。


發佈了54 篇原創文章 · 獲贊 56 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章