是六個比特,最低位的funct域也是六個比特,
中間的四個域,均爲五個比特,咱們分別來看各個域的用途。
opcode域,用於指定指令的類型,對於全部的R型指令,這個域的值,均爲零,
但這並非說明R型指令只有一種,它還須要用funct域來更爲精確的指定指令的類型。因此說,對
於R型指令,實際上一共有12個比特操做碼,
那你們能夠思考一下,爲何不將opcode域和funct域合併成一個12比特的域呢?
那樣豈不是更直觀明瞭嗎?咱們再來看這些5比特的域。
RS域,這個域一般用來指定第一個源操做數所在的寄存器編號,
rt域一般用來指定第二個源操做數所在的寄存器的編號,
rd域一般用來指定目的操做數的寄存器編號,也就是保存運算結果的地方。
5個比特的域能夠表示0-31的數,正好對應MIPS的體系結構中的32個通用寄存器,
還剩下最後一個域,它指示的是一位操做的位數。由於對於
32比特的數,5比特的域正好能夠表示0-31的移位位數。
那這個域只是對於移位指令有用,對於非移位指令,這個域被設爲0,
咱們來看一個例子,這是將9號寄存器和10號寄存器中的數相加,把運算結果保存在8號寄存器中,
那咱們經過這條彙編指令的描述,如何獲得MIPS指令的二進制編碼呢?
這其實很容易。首先,咱們查詢MIPS指令編碼表,就能夠獲得
加法指令的opcode域應該是0,funct域應該是32,
由於它不是移位指令,因此移位的域被設爲0,而後咱們根據這條指令的操做數
能夠獲得目的操做數,也就說rd這個域
等於8,第一個源操做數應該是9,第二個源操做數應該是10,
這樣咱們把各個域的數值轉換成二進制數,填寫到對應的位置,就能夠獲得這條指令的二進制編碼了。
MIPS指令系統簡潔明瞭的規則可讓咱們很是容易的對指令進行這樣的手工編碼轉換,
一樣也說明了CPU對這樣的指令進行硬件的譯碼也會很是的方便。
若是指令中須要用到當即數,那麼就要用到I型指令,
由於R型指令當中只有一個5比特的域,也就說移位這個域 能夠用來表示當即數,那能表示的數的範圍爲0-31,
在程序中經常使用的當即數遠大於這個範圍,因此R型指令
並不適用,咱們須要新的指令格式。這就是I型指令,I型指令的大部分域與R型指令是相同的,
I型指令的第一個域,也是opcode域,用於
指定指令的類型,但它沒有funct域,因此不一樣的I型指令,及opcode域是不同的。
第二個域rs,指定了第一個源操做數所在的寄存器編號,
第三個數rt用於指定目的操做數,I型指令與R型指令不一樣,它只有兩個寄存器數域,
剩下的16位被整合成了一個完整的域,能夠存放16位的當即數,
能夠表示2的十六次方個不一樣的數值。對通常的訪存指令,咱們須要用一個寄存器,加上一個當即數來指示一個內存單元,那麼這個
當即數就是訪存地址的偏移量,16位的當即數,能夠訪問正負32K的空間,
對於通常的訪存指令來講,就能夠知足了。而對於運算指令,雖然沒法知足所有的需求,可是大多數狀況下,
16位也可使用了。在這一點上,就能夠體現出X86這樣的CISC指令系統的優點,對於X86指令來講,
若是它想使用更大寬度的當即數,它能夠很容易的擴展,由於它的指令原本就沒有限制長度,可是
MIPS指令就不行。它的指令總長度就是32位的,再加上各個寄存器位域的使用,
因此I型指令最多隻能使用十六位的當即數。
咱們來看一個例子,對於加法,若是咱們想讓其中的源操做數是一個當即數的話,就能夠用add
i這個指令,注意它和add指令是不同的。add指令的操做數必須都是寄存器。
咱們再來練習一下手工轉換指令的編碼。咱們經過查指令編碼表,能夠發現add
i指令的opcode域是8,從這一點咱們也能夠看出,add
i和add雖然只有一個字母的差異,可是他們指令格式是徹底不同的。
剩下的域咱們經過分析這條指令的操做數就能夠獲得,
rs域,等於22,rt域,等於21,當即數域,
等於-50,咱們將這些數轉換成二進制,就能夠獲得這條指令的編碼了。
而後咱們來看全部的分支指令,分支指令
是用於改變控制流的指令,其實就至關於X86當中的轉移指令。
在MIPS中,分支指令也分爲條件分支和非條件分支
兩種。對於條件分支有兩條指令,beq和bne,對於非條件分支,只有一條指令——j,
咱們先來看條件分支指令,條件分支指令其實是i型指令。
這就是兩條條件分支指令,他們的opcode域分別是4和5,
咱們以beq指令爲例,它共有三個操做數,前兩個是寄存器操做數,
第三個操做數是存儲器地址,也就說一個當即數,CPU會判斷第一個寄存器當中的數
和第二個寄存器當中的數是否相等。若是相等就跳轉到 LE所指向的寄存器單元取出下一條指令,不然,
順序執行deq以後的那條指令。咱們須要注意,
這裏和X86的條件轉移指令有很大的不一樣。MIPS沒有標誌寄存器,它就在
一條指令當中即進行了比較,又完成了轉移,
咱們還記得MIPS的全稱,就是爲了減小指令流水線的互鎖,也就說要儘可能
避免不一樣指令之間相互的影響。而標誌位這件事,很明顯就是前一條指令運行的結果,可能
會對後面的某一條指令產生影響,這是MIPS指令設計時要儘可能避免的。因此BEQ指令也很好的體現了MIPS的這一設計理念。
咱們來看一個例子。這段C語言代碼是咱們常常會寫的。
若是把它轉換爲MIPS指令,是這樣的,第一條BEQ指令,
若是S3寄存器和S4寄存器內容相同,則轉移到
Q所對應的這行指令。那麼S3和S4中保存了I和J這兩個變量,
若是他們內容相同,會轉移到這裏,執行加法指令,也就對應於F=G+H,若是他們不等,
則會順序的執行下一條指令,也就一條減法指令對應於F=G-H
執行完以後,會跳過這條加法指令,而後進入後面的代碼,
從條件分支指令的格式能夠看出,目標地址只能使用十六位的位移量,
這是一個很大的侷限,可是咱們還得考慮如何充分發揮這十六位的做用。
若是以當前的PC寄存器爲基準,在MIPS中,指向下一條指令地址的寄存器稱爲PC,
相似於X86中的IP寄存器。這個寄存器,是指向32位寄存地址的。
若是以它爲基準,十六位位移量能夠表示出當前指令先後2的15次方字節這麼一個範圍,
可是咱們要注意一點,MIPS的指令長度固定位32個比特,所以每條指令的位置,
必定會在四個字節對齊的地方,這樣地址,最低兩位確定爲0。
因此咱們實際上能夠用十六位的位移量去指示每四個字節爲一個單位的地址。
這樣就能夠把目標地址的範圍擴大四倍,能夠達到先後128KB。
在這樣的條件下,目標地址應該這麼計算,
當分支條件不成立時,下一條指令的地址就等於當前的pc+4。
若是分支條件成立,那下一條指令的地址就等於已經加了4的pc,再加上這個當即數乘以四。
而後咱們來看非條件分支指令,相比於條件分支指令,有兩個寄存器域用於
比較條件,那若是咱們不須要判斷條件,咱們就能夠想辦法擴大目標地址的範圍。
固然理想狀況下是直接使用32位的地址,但仍是由於MIPS的指令長度固定爲32位,而每條指令
至少須要有opcode域,指示它指令類型。
這就佔用了六個bit。那咱們把剩下的26個bit全都用於目標地址,
這就是J型指令。在考慮到MIPS指令是四字節對齊的這個狀況,
對於這一行指令,下一條指令的地址的計算方法能夠是將當前的pc加四以後,
取最高的四位,再加上J型指令編碼中的26位,
而後在末尾填上兩個零,雖然目標地址的範圍還不能達到整個4G的空間,但比以前的條件分支指令已經擴大了不少。
咱們用一個例子來進行進一步的說明。
假設咱們在高級語言中用的若干變量與寄存器的對應關係是這樣的,
那咱們就能夠用這樣一種方式來實現這段c語言的代碼,
第一條指令是判斷i和j是否相等,若是不相等,則轉移到else這個標號所對應的位置,
也就是執行一條減法指令對應於f=g-h,若是判斷條件不成立,
也就是i=j的時候,順序地執行下一條加法指令,
也就對應於f=g+h。而後用無條件分支指令
跳到else條件以後繼續執行後面的程序。
咱們如今已經知道這個J型指令的目標地址能夠是當前指令
先後256MB的範圍,那若是咱們還想跳轉到更遠的地址,應該怎麼辦呢?有一個很簡單的方法
就是兩次調用J指令,第一條J指令儘量跳到最遠的地方,
而後在那個目標地址再放一條J指令,像接力同樣再跳一次。
這個方法很簡單,可是用起來不算太方便,那麼還能夠用什麼方法呢?
你們還記得咱們曾說過間接轉移指令嗎?MIPS中也能夠用一樣的方法,
這就是jr指令。jr指令有一個寄存器操做數,
能夠把要轉移的目標地址放到寄存器當中,這樣就可使用32位的目標地址了,
可是這樣的指令顯然沒法用J型指令來實現,
那麼須要新增一種指令類行嗎?其實也不須要,咱們就用原來的r型指令就能夠很好的實現。
只用佔用其中的一個寄存器位域,而後新增一種function的編碼就能夠了。
這就是MIPS指令系統的核心內容,咱們只用熟悉這兩頁的內容就能夠輕鬆的掌握MIPS的指令了。
咱們已經介紹完了MIPS制定系統體系結構,
它不愧爲精簡指令系統的經典設計,指令簡潔,並且精巧。