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

計算機系統原理(十六) 算數與邏輯運算指令詳解

日期:2017/1/4 16:24:37      編輯:匯編語言

引言

上一章我們已經著重討論了數據傳送(或者說復制)指令,相信各位猿友現在都已經對此有一些了解了。說真的,LZ在看第三章的過程中,不斷的被匯編的魅力深深的震撼,這些看似簡單的匯編指令,卻可以將復雜的程序井然有序的執行完畢,實在是讓人驚歎。時至今日,這本看似枯燥無比卻實則魅力十足的書,已經深深的將LZ吸引了。

希望各位猿友也有這樣的感覺,這是一種非常好的感覺,接下來,各位就一起和LZ來認識認識新的指令吧。

算術與邏輯運算指令

算術與邏輯運算包括很多種,估計各位猿友也能很快的想出來,比如最常見的加減乘除、與或非、左移右移等等。這裡可能還有一個各位猿友不太容易想到的,就是取地址運算符,不過這個運算指令卻是LZ看過這一部分之後,覺得最精妙的一個指令。

接下來LZ將書中的一個表格貼上來,各位猿友可以先大致浏覽下裡面的指令。

查看本欄目

可以看到,此時在存儲器中,地址為5x+4的區域的值為1000。那麼此時若是進行movl 4(%edx,%edx,4),%eax操作,很顯然,%eax的值應該為1000,也就是下圖。

但是如果進行leal 4(%edx,%edx,4),%eax操作的話,%eax的值就不是1000了,因為leal指令不會去取存儲器當中的值,因此寄存器%eax的值應該是5x+4。

試想一下,倘若在地址為5x+4的位置存儲的是變量i,那麼其實這條指令就相當於&i操作,這也就是C語言當中的&取地址操作的匯編級做法。各位猿友感覺如何,是否很神奇呢。

一個示例

由於其它的指令都相對比較簡單,因此LZ這裡就不一一介紹了,這裡我們用一個小程序來做一個示例,順便也去看一下上面的算術與邏輯運算指令都是被如何使用的。我們就考慮書上的一個小例子,其中的C程序代碼如下。

int arith(int x, int y , int z){
    int t1 = x+y;
    int t2 = z*48;
    int t3 = t1&0xFFFF;
    int t4 = t2*t3;
    return t4;
}

這裡面包含了加、乘、與運算,我們使用-O1和-S參數編譯sum.c這個文件,使用cat sum.s查看它,會得到如下的匯編代碼。

.file    "sum.c"
    .text
.globl arith
    .type    arith, @function
arith:
    pushl    %ebp
    movl    %esp, %ebp
  //以上為棧幀建立
    movl    16(%ebp), %eax
    leal    (%eax,%eax,2), %edx
    sall    $4, %edx
    movl    12(%ebp), %eax
    addl    8(%ebp), %eax
    andl    $65535, %eax
    imull    %edx, %eax
  //以下為棧幀完成
    popl    %ebp
    ret
    .size    arith, .-arith
    .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

這裡面還有leal指令,可以看到程序當中並沒有取地址&操作,所以這裡的leal指令不是用來取地址的,LZ使用圖示來給各位演示這個程序的運行過程。首先便是棧幀的建立過程,棧幀建立好以後,寄存器和存儲器的狀態如下所示。

以上便是建立好的棧幀,同上一次一樣,幀指針和棧指針都指向一個新的位置,在幀指針偏移量為8、12、16的地方存儲著傳遞進來的參數x、y、z。接下來我們就開始分析,在匯編代碼層次,是如何完成上述C語言程序當中的一系列動作的。

首先是一個mov指令,它的作用很簡單,就是將參數z取入寄存器,下面是它的匯編代碼以及圖示。

movl    16(%ebp), %eax

上面的指令比較簡單,接下來的這條指令就比較特別了,是一條leal指令。這裡的leal指令不是用來取地址的,而是用來進行乘法運算的,它的目的是將%eax寄存器當中的值乘以3,然後發送至%edx寄存器。而采用的方式則是2*x + x的方式,這正是我們之前講過的乘法優化算法,使用移位和加法來計算乘法。接下來看看它的指令與圖示。

    leal    (%eax,%eax,2), %edx

上面計算3z的目的,在接下來這一條指令就看出來了。接下來的一條指令是sal左移操作,位數為4,左移4位其實就相當於乘以16,因此接下來的一條指令其實就相當於將寄存器%edx當中的值乘以16,這其實剛好是在計算48*z。從這裡也可以看出來,在執行C程序的時候,並不一定會按照程序當中的順序去計算。以下是sal指令的內容與圖示。

    sall    $4, %edx

接下來的指令依然是簡單的取參數y,因此LZ這裡就不再多解釋了,直接上內容和圖示。

    movl    12(%ebp), %eax

下面的一條指令是add加法指令,它是將左邊操作數的值加到右邊的目的操作數。也就是將內存地址為8(%ebp)的值加到%eax寄存器,而8(%ebp)這個位置存的剛好是x,因此這裡計算的便是x+y的值,而結果會存入%eax寄存器。以下是指令的內容和圖示。

    addl    8(%ebp), %eax

接下來是一條與運算指令and,它計算的則是t1與0xFFFF(十進制就是65535)的與運算,t1的值為x+y,此時就存在%eax寄存器。我們來看下這條指令的內容與圖示。

    andl    $65535, %eax

接下來是最後一個計算過程的指令imul乘法指令,它的作用也是將左邊操作數的值乘到右邊的目的操作數上。也就是將%edx寄存器的值乘到%eax寄存器上面去,而%edx此時的值為48*z(也就是t2),而%eax的值為(x+y)&0xFFFF(也就是t3),兩者相乘則得到t4的值,結果將存在%eax寄存器,並且作為返回值返回。以下為內容與圖示。

    imull    %edx, %eax

查看本欄目

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