在上一篇博客 程序編碼以及數據格式 中咱們給出了一個簡單的C程序,而後編譯成了彙編代碼。你們看不懂不要緊,後面的博客咱們將逐漸揭開一些彙編指令的神祕面紗。本篇博客咱們將對操做數指示符和數據傳送指令進行詳細的介紹。html
上一篇博客咱們講了在彙編語言中,以下的幾個處理器狀態是可見的:數組
1、程序計數器(在 IA32 中一般稱爲 PC,用 %eip 表示):指示將要執行的下一條指令在存儲器中的地址。數據結構
2、整數寄存器文件:包含8個命名的位置,能夠存儲一些地址或者整數的數據。有的用來記錄某些重要的程序狀態,有的則用來保存臨時數據。post
3、條件碼寄存器:保存最近執行的算數或邏輯指令的狀態信息,它們用來實現控制或數據流中的條件變化,好比用來實現 if 和 while 語句。編碼
4、浮點寄存器:存儲浮點數。指針
這裏咱們要講的就是第三個整數寄存器,在 32 位 CPU 中包含一組 8 個存儲 32 位值的寄存器。這些寄存器用來存儲整數數據或指針。下圖是 IA32 的整數寄存器:htm
上述八個寄存器主要功能以下:blog
%eax,可存放通常數據,並且可做爲累加器使用; %ebx,可存放通常數據,並且可用來存放數據的指針(偏移地址); %ecx,可存放通常數據,並且可用來作計數器,經常將循環次數用它來存放; %edx,可存放通常數據,並且可用來存放乘法運算產生的部分積,或用來存放輸入輸出的端口地址(指針); %esi,可存放通常數據,還可用於串操做中,存放源地址,對一串數據訪問; %edi,可存放通常數據,還可用於串操做中,存放目的地址,對一串數據訪問; %esp,用於尋址一個稱爲堆棧的存儲區,經過它來訪問堆棧數據; %ebp,可存放通常數據,用來存放訪問堆棧段的一個數據區,做爲基地址;
在大多數狀況下,%eax、%ecx、%edx、%ebx、%esi、%edi等6個寄存器能夠看作通用寄存器,對它們的使用沒有限制;%esp、%ebp兩個寄存器保存着指向程序棧中重要位置的指針,只有根據棧管理的標準慣例才能修改這兩個寄存器中的值。ip
這8個寄存器均可以做爲16位(字)或32位(雙字)來訪問。字節操做指令能夠獨立的讀或者寫%eax、%ecx、%edx、%ebx等4個寄存器的2個低位字節,由於%ax、%cx、%dx、%bx這4個16位寄存器又可分別分紅ah,al ;bh,bl;ch,cl;dh,dl的8位寄存器。內存
這裏你們也只須要有個眼熟就行了,後面咱們將對這個8個寄存器進行詳細講解。
咱們知道大多數指令都有一個或多個操做數(operand),指示出執行一個操做中要引用的源數據值,以及放置結果的目標位置。下圖是 IA32 支持的多種操做數格式:
上圖咱們能夠看出源數據值能夠是常數形式給出,或者是從寄存器或存儲器中讀出。而結果能夠存放在寄存器或存儲器中。咱們將不一樣的操做數分爲以下三種類型:
①、當即數(immediate):書寫方式是$符號後跟一個標準C表示的整數,好比$52,$0x1F等等。任何能放進一個32位的字裏面的數值均可以作當即數。
②、寄存器(register):它表示某個寄存器的內容,能夠是8個32位寄存器中的一個(好比%eax),也能夠是8個16位寄存器中的一個(好比%ax),還能夠是8個單字節寄存器寄存器(好比%al)。上圖是用Ea來表示任意寄存器a,用引用 R[Ea]來表示它的值。
③、存儲器(memory):它會根據計算出來的地址(一般稱爲有效地址)來訪問某個存儲器位置。咱們將存儲器當作一個很大的字節數組,用符號Mb[Addr] 表示對存儲在存儲器中從地址 Addr 開始的 b 個字節值的引用。上圖省略了下方的 b.
從上圖咱們知道,第一行是當即數,第二行則是寄存器,剩下的所有是存儲器。其中最後一行存儲器語法 Imm(Eb,Ei,s),表示的是最經常使用的形式,分爲四個部分,
1、Imm 是當即偏移數
2、Eb 是基址寄存器
3、Ei 是變址寄存器
4、s 是比例因子,必須是 一、二、4或8
而後有效地址計算公式爲: Imm + R[Eb]+R[Ei]*s。好比對於2(%esp,%eax,4)這個操做數來說,它表明的是內存地址爲2+%esp+4*%eax的存儲器區域的值。
數據傳送指令:將數據從一個位置複製到另外一個位置的指令。簡單來講就是複製指令。
將源操做數的值複製到目的操做數中並覆蓋。源操做數指定的值是一個當即數,存儲在寄存器或存儲器中。目的操做數指定一個位置,要麼是一個寄存器,要麼是一個存儲器地址。在 IA32 中還有一條限制,傳送指令的兩個操做數不能都執行存儲器位置。
將一個值從一個存儲器位置複製到另外一個存儲器位置須要兩條指令:(就和宋丹丹把大象送進冰箱的步驟同樣)
①、第一條指令將源值加載到寄存器中
②、第二條指令將該寄存器值寫入到目的位置。
下圖是許多不一樣的指令類:
MOV 類由三條指令組成:movb,movw和 movl。指令格式爲 [movx S D],表示將源操做數S中的數據複製到目的操做數D中。三種指令的區別是它們分別是在大小爲 1,2和4個字節的數據上進行操做。
這裏舉一個簡單的例子,好比咱們有一條指令爲movl %edx %eax。那麼它的執行過程就以下圖所示。
上圖引用至:http://www.cnblogs.com/zuoxiaolong/p/computer15.html
在指令執行以後,%edx寄存器當中的內容會被複制到%eax寄存器。數據格式則爲四個字節,也就是雙字。咱們還可使用movb和movw去複製一個字節或者兩個字節。
MOVS指令格式爲 [movsxy S D],其中x、y爲數據格式,S爲源操做數,D爲目的操做數。x、y的組合有三種,分別是bw,bl,wl,分別表示字節(8位)傳送到字(16位),字節(16位)傳送到雙字(32位),字(16位)傳送到雙字(32位)。
將較小的源數據複製到一個較大的數據位置。高位用符號位擴展,即目的位置的全部高位用源值的最高位數值進行填充。
好比對於指令movswl %dx %eax來說,它的做用以下圖所示。
上圖引用至:http://www.cnblogs.com/zuoxiaolong/p/computer15.html
這裏使用了十六進制的整數表示方式。能夠看到,movs指令將0x8FFF擴展之後存入%eax寄存器,其中%dx爲寄存器%edx的後16位表示。
MOVZ 指令和上面的MOVS 指令十分類似。指令格式爲 [movzxy S D],其中x、y爲數據格式,S爲源操做數,D爲目的操做數。x、y的組合有三種,分別是bw,bl,wl,分別表示字節(8位)傳送到字(16位),字節(16位)傳送到雙字(32位),字(16位)傳送到雙字(32位)。
將較小的源數據複製到一個較大的數據位置。高位用0擴展,即目的位置的全部高位用0進行填充。
好比對於指令movzwl %dx %eax來說,它的做用以下圖所示。
上圖引用至:http://www.cnblogs.com/zuoxiaolong/p/computer15.html
擴展後,目標寄存器%eax的前16位爲0而再也不是1。
咱們知道 棧 是一個數據結構,能夠添加或刪除值,遵循「後進先出」的原則。
push:把數據壓入棧中,添加數據。
pop:把數據移出棧,刪除數據。注意移出的值老是最近被壓入而仍然在棧中的值。
棧能夠實現爲一個數組,老是從數組的一端插入或刪除元素。而這一端稱爲棧頂,在 IA32 中,程序棧存放在存儲器某個區域,以下圖所示:
注意因爲操做數字節的不一樣,pushl 是將雙字(32位)壓入棧中;popl 是移出雙字。
將一個雙字值壓入棧中,首先要將棧指針減4,而後將值寫到新的棧頂地址。所以指令 pushl %ebp 等價於下面兩條指令:
subl $4,%esp
movl %ebp,(%esp)
上圖所示,當 %esp 爲0x108,%eax爲0x123時,執行指令 pushl %ebp 的效果。首先 %esp 會減4,獲得0x104,而後會將 0x123 存放到存儲器地址 0x104處。
將一個雙字值從棧頂移出,首先要從棧頂位置讀出數據,而後將棧指針加4。所以指令 popl %eax 等價於下面兩條指令:
movl (%esp),%eax
addl $4,%esp
上圖所示,先從存儲器中讀取值 0x123,再寫到寄存器 %edx中,而後寄存器%esp的值將增長回到0x108。
注意值0x123仍然會保存在存儲器0x104中,直到被覆蓋(好比被另外一條入棧操做覆蓋)。不管如何,%esp 指向的地址老是棧頂,任何存儲在棧頂以外的數據都認爲是無效的。
本章主要介紹了操做數指示符,須要咱們理解幾種表達式的計算方法。接着介紹了幾種數據傳送指令,包括MOV,MOVS,MOVZ,PUSH和POP等,整體上來看不難理解,咱們在瞭解這些指令後,再回頭看那些彙編代碼,應該會理解不少了。下一篇博客咱們將進一步介紹彙編指令——算術和邏輯操做。