程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 計算機系統原理(十五) 數據傳送(復制)指令詳解

計算機系統原理(十五) 數據傳送(復制)指令詳解

編輯:匯編語言

引言

上一章我們已經介紹了匯編語言的基礎部分,包括數據格式、寄存器以及操作數的標識方式,接下來我們就應該去認識一下匯編語言當中的各個指令了。這些指令大多數都非常簡單,但是組合在一起卻能模擬出我們程序當中想要的任何效果,確實是十分神奇的一件事。  

數據傳送指令

數據傳送指令的目的是為了將一個數據從一個位置復制到另外一個位置。既然如此,那麼數據傳送指令就會包含一個源操作數和一個目的操作數,指令會將原操作數的值復制到目的操作數並覆蓋。

數據傳送指令一共可分為五種,分別是mov、movs、movz、push以及pop,下面LZ依次介紹一下這五個指令的作用。

mov指令

mov指令的作用是將源操作數S中的數據復制到目的操作數D中,mov指令有一個數據格式和兩個操作數,因此一般的形式為[movx S D]。其中x為數據格式,S為源操作數,D為目的操作數。

這裡舉一個簡單的例子,比如我們有一條指令為movl %edx %eax。那麼它的執行過程就如下圖所示。

可以看到,在指令執行之後,%edx寄存器當中的內容會被復制到%eax寄存器。需要一提的是,mov指令可以在後面加上任何數據格式,比如上面這一過程中,數據格式則為四個字節,也就是雙字。因此不難推斷出,我們還可以使用movb和movw去復制一個字節或者兩個字節。

movs指令

movs指令的作用是將源操作數S中的數據做符號擴展後,再復制到目的操作數D中,movs指令有兩個數據格式和兩個操作數,因此一般的形式為[movsxy S D]。其中x、y為數據格式,S為源操作數,D為目的操作數。其中x、y的組合一共有三種,分別是bw、bl、wl,這三個組合代表的意思分別是單字節到雙字節,單字節到雙字以及雙字節到雙字。

這裡LZ依然舉一個例子,對於指令movswl %dx %eax來講,它的作用如下圖所示。

這裡為了可以看出符號位的擴展,因此LZ這裡使用了十六進制的整數表示方式。可以看到,movs指令將0x8FFF擴展以後存入%eax寄存器,其中%dx為寄存器%edx的後16位表示。

movz指令

movz指令的作用是將源操作數S做零擴展後,再復制到目的操作數中。它與movs指令十分相似,也有兩個數據格式和兩個操作數,因此一般的形式為[movzxy S D]。其中x、y為數據格式,S為源操作數,D為目的操作數。其中x、y的組合一共有三種,分別是bw、bl、wl,這三個組合代表的意思分別是單字節到雙字節,單字節到雙字以及雙字節到雙字。

這裡依然采用相似的示例,我們來看看對於指令movzwl %dx %eax來講,它的作用與上面的movs有何不同。

可以看出,movz與movs指令是十分相似的,只是這裡擴展後,目標寄存器%eax的前16位為0而不再是1。

push指令

push指令與上面的mov族指令有著不同,它的目的操作數被固定為棧頂,因此它的指令當中沒有目的操作數。另外有一點需要注意的是,它在進行復制操作之前,需要移動棧頂指針(-4)。push指令的一般形式為[pushl S],其中l代表數據格式為雙字,S為源操作數,目的操作數默認為棧頂。

這裡LZ舉一個簡單的例子,比如pushl %edx這條命令,它的任務是將%edx寄存器的值復制到棧頂。我們首先來看一下命令執行前,寄存器以及存儲器的狀態。

可以看到,寄存器%ebp和%esp分別指向幀指針和棧指針,而%esp實際上就是指向的棧頂。由於現在棧頂位於-16的位置,因此若要將%edx壓入棧,則先需要將棧頂移動到-20的位置,然後再進行復制,移動後的狀態如下圖所示。

可以看到,這裡棧指針的位置已經發生了變化,向下移動了四位,並且將%edx寄存器的值放入新的棧頂,因此pushl %edx指令就相當於下面兩條指令。

                subl $4,%esp

                movl %edx,(%esp)

這裡可以看出來,其實pushl指令做了一個隱藏操作,就是移動棧指針(-4),這一點希望各位猿友們注意。

pop指令

pop指令與push指令是做的相反的操作,一個是入棧一個是出棧。對於pop指令來講,它的源操作數被固定為棧頂,相反,它會先進行復制操作,然後再移動棧指針。pop指令的一般形式為[popl D],其中l代表數據格式為雙字,D為目的操作數,源操作數默認為棧頂。

接下來我們舉一個例子,與上面的例子類似,我們考慮popl %edx這條指令的效果,它會將棧頂的值彈出到寄存器%edx。首先來看執行之前,寄存器以及存儲器的狀態。

接下來執行pop指令時,會先將棧頂的值復制到%edx,然後再將棧指針移動(+4)。我們來看一下它執行後的狀態。

可以看到,之前棧頂的內容已經被彈出到%edx寄存器,並且當前棧頂已經移動到了-16的位置,也就是進行了+4操作。因此popl %edx指令就相當於下面兩條指令。

                movl (%esp),%edx

                addl $4,%esp     

這裡可以看出來,其實popl指令也同樣做了一個隱藏操作,就是移動棧指針(+4)。

查看本欄目

分析這段匯編代碼的時候,我們應該分為三個部分來看待,首先是棧的建立、然後是使用、最後是完成部分。可以看到,裡面幾乎全是數據復制指令,我們先來看看棧的建立部分。

其實對於一開始pushl和movl指令來講,它主要做了兩件事。第一個是將原來的幀指針備份到棧頂,然後再將幀指針和棧指針統一指向這個新的棧頂,也就是完成了一個新棧的建立。它在完成後,棧的狀態如下所示。

可以看到,寄存器%ebp和寄存器%esp都指向當前幀指針的位置,其中變量xp位於+8的位置,而y位於+12的位置。由於xp是一個指針變量,因此它會指向一個內存中的區域,其中的值為*xp。

了解完寄存器和存儲器的狀態,此時棧已經建立完畢,接下來我們看緊接著的一句匯編代碼的作用。

    movl    8(%ebp), %edx

這一句將內存地址為%ebp+8的值復制到%edx,很明顯,從上面的圖中可以看出,%ebp+8這個位置存儲著xp變量。這一句指令做了一個簡單的操作,就是將xp提取到%edx寄存器,如下所示。

此時已經將%edx的值改為了變量xp,看接下來的一句操作。

movl (%edx), %eax

這一句將內存地址為%edx的值賦給寄存器%eax,並准備返回值。此時%edx寄存器的值已經改為了xp變量,因此(%edx)其實就是*xp,而%eax寄存器一般會作為函數的返回值,因此它其實替代了臨時變量t。執行後的狀態如下所示。

此時其實已經完成了程序中的int t = *xp以及為return t准備好了返回值,接下來的一句匯編代碼作用也非常簡單,如下。

    movl    12(%ebp), %ecx

它的作用是將地址為%ebp+12的值復制到寄存器%ecx,從圖中可以看出,%ebp+12就是存儲的變量y。因此它的作用就是將y復制到寄存器%ecx,如下所示。

上面這一步挺簡單,我們來看最後一步操作,如下。

    movl    %ecx, (%edx)

它的作用是將%ecx寄存器的值復制到內存中%edx的位置。此時%ecx的值為y,而%edx中為xp,因此目的操作數則為xp指向的位置,也就是*xp。這一句話執行的就是程序代碼當中,*xp=y這個操作,它執行後的狀態如下所示。

查看本欄目

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved