雖然機器碼和內存地址領證在一起了,但是倆個人都和彙編指令有着說不清的關係,機器碼的前任是彙編指令,而彙編指令又與內存地址暗地相通,這究竟是道德的淪喪還是人性的扭曲,今天就讓我來領大家細細分解這三者的前世今生!!!
之前我們使用匯編語言編寫了點亮LED程序,.S文件通過FTP傳到Ubuntu中,通過交叉編譯工具生成.bin文件傳回本機,然後通過oflash燒寫進裸機的Nand FLASH,從而點亮LED。
這裏分析一下彙編代碼在此過程中的意義,內存地址、機器碼與彙編指令三者之間的聯繫。
一.原彙編代碼
彙編代碼如下:
.text
.global _start
_start: ;程序從這裏開始
ldr r1, =0X56000050 ;將地址存在r1
ldr r0, =0X100 ;將值存在r0
str r0, [r1] ;將r0的值寫入[]中的地址
ldr r1, =0X56000054 ;同上
ldr r0, =0
str r0, [r1]
halt: ;死循環
b halt ;一直跳轉到halt
簡單解釋一下代碼:
要點亮LED,就要將GPF4引腳輸出低電平,通過在GPFCON和GPFDAT寄存器中的對應位寫入值來實現,即對0X56000050地址中寫入0X100,對0X56000054地址中寫入0 。
用到的彙編代碼指令如下:
ldr(load):讀內存命令
str(store):寫內存命令
b:跳轉
mov(move):賦值
二.反彙編代碼
編譯器會將彙編指令轉換成機器碼,而機器碼又存放在內存地址中!!!通過反彙編指令可以得到反彙編文件,裏面有內存地址、機器碼與彙編指令三者的對應關係!!!
機器碼就是.bin文件的十六進制形式,一組機器碼有32位,ARM一次也能夠處理32位的數據,這些知識都是相統一的。
通過將彙編代碼,傳到Linux中可以進行編譯,然後生成.bin文件,當然也可以通過交叉編譯工具的反彙編,生成機器碼和處理後的標準的彙編碼,反彙編文件爲.dis文件,反彙編文件中可以查看內存地址、機器碼與彙編指令三者之間的聯繫:
led_on.elf: file format elf32-littlearm
Disassembly of section .text:
/*地址*/ /*機器碼*/ /*彙編指令*/
00000000 <_start>:
0: e59f1014 ldr r1, [pc, #20] ; 1c <.text+0x1c>
4: e3a00c01 mov r0, #256 ; 0x100
8: e5810000 str r0, [r1]
c: e59f100c ldr r1, [pc, #12] ; 20 <.text+0x20>
10: e3a00000 mov r0, #0 ; 0x0
14: e5810000 str r0, [r1]
00000018 <halt>:
18: eafffffe b 18 <halt>
1c: 56000050 undefined
20: 56000054 undefined
在S3C2440中,CPU有各種寄存器,如圖:
左邊是各種寄存器,右邊是寄存器的別名,下面介紹一下比較重要的幾個寄存器:
-
pc(program counter)是程序計數器,當把一個地址寫入pc寄存器中,CPU就會跳轉到這個地址去取指令。
-
lr(link register)是返回地址寄存器,當程序執行完一個調用函數時,要跳轉回原來的地址,這個**lr寄存器中存放就是原來的地址,**調用函數執行完畢後,只需要轉到lr中的地址就可以繼續執行程序了。
-
sp(stack point)是棧指針
三.三角戀關係
1.彙編指令與內存地址的關係
下面分析一下彙編指令與內存地址之間的關係
說明一下:爲什麼倆條相鄰指令的內存地址差爲4?
這是因爲內存地址的單位都是Byte,也就是8位(bit),而且ARM是32位的,一次只能夠處理32位指令,也就是4Byte的指令,所以指令的內存存放都是以4Byte爲單位的。
第一條指令,要知道pc中的地址是當前指令的地址+8,因爲ARM執行指令是流水線式的,比如當前執行地址a的指令,已經在對地址a+4的指令進行譯碼,已經在讀取地址a+8的指令,也就是說,當前pc中存放的是a+8的值(第三條指令的地址)。就拿第一個指令來講([x]代表:x地址):
此時pc中的值是第三條指令的地址,也就是8,結合[pc, #20],代表[8+20],也就是28地址,即[0X1C],這條指令代表去0X1C地址讀取內存上的值,放在r1寄存器中,可以看出0X1C地址中存放的值爲:
第二條指令直接將256(0X100)的值放在了r0寄存器中;
第三條指令就是將r0寄存器中的值0X100,寫入地址爲r1(0X56000050)的內存;
接下來三條指令,與前三條原理相同;
後面的halt死循環指令中
b 18就代表程序跳轉到地址爲18的地方去 取指令,執行指令 然鵝這條指令的地址就是18,所以會陷入一個死循環。
此外,可以看出彙編指令在內存中的地址都是連續的,就連彙編指令中的一些數據的內存地址都是緊跟在代碼地址後面的,好奇妙哦。爲什麼要在最後面執行死循環呢?就是防止程序跑完之後,再跑到別的地方去。
2.機器碼與彙編指令
程序編譯後我們得到了.bin文件,這裏又有機器碼,實際上機器碼的內容與.bin文件完全一致,只是碼制不一樣,下面查看一下.bin文件的內容:
bin文件中的,這裏顯示的是按照地址從小到大的排布,所以第一個指令是:e59f1014,與上面的機器碼完全一致。
3.內存地址與機器碼
瞭解了上面的關係後,也不難理解,機器碼就是時間存放在內存地址中的數據!!!
所以,ARM一次能夠處理32位的數據或指令,這指令就是指一條彙編指令,一條彙編指令的內存量就是32位,也就是4Byte,所以彙編指令與機器碼之間必然有者某種關係,時其維持着這種對應關係(一條彙編指令始終對應32位機器碼),也就是說,彙編指令都是有相應的機器碼格式的。
可以去ARM的架構手冊中查找相應彙編指令對應的機器碼格式,比如mov指令:
可以看出,MOV指令確實是32位機器碼的格式,
用上面的代碼來說明一下:
首先搞出機器碼的二進制形式,這樣有對比性:
[15:12]=0,表示Rd寄存器是r0寄存器;
[11:0]這12位代表了mov的參數,就是這12位的內容,這裏與0X100對應,0X100叫做立即數,但我們發現這12位的內容與0X100並不一致呀,爲什麼呢?
實際上這12位數據,拆分爲了高四位[11:8]的rotate數和低八位[7:0]的immed_8數,0X100叫做立即數,他們之間的轉換關係爲:
立即數 = immed_8數 循環右移 (2xroute數)位
相當於0X1循環右移24位,最終還是0X100
所以當了解了這些知識後,會加深我們對於程序運行的理解,從機器碼,到彙編指令,再到C語言,再到其他語言,程序員使用的語言越來越多樣化,可以實現程序的方式越來越多,但最終都是回到了機器碼這一步,因爲機器碼纔是CPU使用的!!!
可以擬人化的說:
機器碼是存放在內存中的,所以機器碼和內存地址是在一起的
機器碼是由彙編指令編譯來的,所以說彙編指令是機器碼前任
彙編指令又與內存地址有着讀寫關係,所以說彙編指令和暗地相通
(滑稽臉)