MIPS匯編語言基礎
MIPS的系統結構及設計理念比較先進,其指令系統經過通用處理器指令體系MIPS I、MIPS II、MIPS III、MIPS IV、MIPS V,以及嵌入式指令體系MIPS16、MIPS32到MIPS64的發展。
MIPS32的架構是一種基於固定長度的定期編碼指令集,並采用導入/存儲(load/store)數據模型。經改進,這種架構可支持高級語言的優化執行。在路由器中,經常使用的一種MIPS架構就是MIPS32。
MIPS寄存器
RISC的一個顯著特點就是大量使用寄存器。因為寄存器的存取可以在一個時鍾周期內完成,同時簡化了尋找方式,所以,MIPS32的指令中除了加載/存儲指令以外,都使用寄存器或者立即數作為操作數,以便讓編譯器通過保持對寄存器內數據的頻繁存取進一步優化代碼的生成性能。
MIPS32寄存器分為兩類:通用寄存器(GPR)和特殊寄存器。
1、通用寄存器(GPR)
在MIPS體系結構中有32個通用寄存器,在匯編程序中可以用編號$0~$31表示,也可以用寄存器的名字表示,如$sp、$t1、$ta等,如圖,堆棧是從內存的高地址方向向低地址方向增長的。
編號 | 寄存器名稱 | 寄存器描述 |
0 | zero | 第0號寄存器,其值始終為0 |
1 | $at | 保留寄存器 |
2~3 | $v0~v1 | values, 保存表達式或函數返回結果 |
4-7 | $a0~a3 | aruments, 作為函數的前4個參數 |
8~15 | $t0~$t7 | temporaries,供匯編程序使用的臨時寄存器 |
16~23 | $s0~$s7 | saved values,子函數使用時需要先保存原寄存器的值 |
24~25 | $t8~t9 | temporaries, 供匯編程序的臨時寄存器,補充$t0~t7 |
26~27 | $k0~$k1 | 保留,中斷處理函數使用 |
28 | $gp | global pointer,全局指針 |
29 | $sp | stack pointer, 堆棧指針,指向堆棧的棧頂 |
30 | $fp | frame pointer, 保存棧指針 |
31 | $ra | return address, 返回地址 |
- $0:即$zero,該寄存器總是返回0,為0這個有用常數提供了一個簡潔的編碼形式。在MIPS處理器的通用寄存器中,沒有任何幫助運算判斷的標志寄存器,要實現相應的功能時,都是通過測試兩個寄存器是否相等完成的。MIPS編譯器常常會使用slt、beq、bne等指令和由寄存器$0獲得0值產生比較所有的比較條件,如相等、不等、小於等於、大於、大於等於。還可以用add指令創建move偽指令,如"move $t0, $t1; $t0=$t1"實際為“add $t0,$0,$t1; $t0= $t1 + 0"。使用MIPS偽指令可以簡化任務。
- $1 ($at) : 該寄存器為匯編保留,用做匯編器的暫時變量。
- $2~$3($v0~$v1): 用於存放子程序的返回值或非浮點結果。當這兩個寄存器不夠存放返回值時,編譯器通過內存來完成。
- $4~$7($a0~$a3):用於將前4個參數傳遞給子程序,不夠的用堆棧處理。$a0~$a3、$v0~$v1和$ra 一起完成子程序函數調用過程,分別用以傳遞參數、返回結果和存放返回地址。當需要使用更多的寄存器時就需要堆棧了。MIPS編譯器總是為參數在堆棧中留有空間,以防有參數需要存儲。
- $8~$15($t0~$t7): 一個子函數可以不用保存並隨意使用這些寄存器。在進行表達式計算時,這些寄存器是非常好的臨時變量。在使用時需要注意,當調用一個子函數時,這些寄存器的值有可能被子函數破壞。
- $16~$23($s0~$s7): 子函數必須保證當函數返回時這些寄存器的內容將恢復到函數調用以前的值,或者子函數里不使用這些寄存器或把它們保存在堆棧上並保存在函數退出時恢復。這種約定使這些寄存器非常適合作為寄存器變量,或者用於存放一些函數調用期間必須保存的原值。
- $24~$25($t8~$t9): 同$t0~$t7,作為$t0~$t7寄存器補充。
- $26~$27($k0~$k1): 通常被中斷或異常處理程序使用,以保存一些系統參數。
- $28($gp): C語言中有兩種存儲類型,分別是自動型和靜態型。自動變量是一個函數中的局部變量。靜態變量在進入和退出一個函數時都是存在的。為了簡化靜態數據的訪問,MIPS保留了一個寄存器作為全局指針gp在編譯時,數據需要在以gp為基指針的64KB范圍內。
- $29($sp): MIPS硬件並不直接支持堆棧,X86有單獨的PUSH和POP指令,而MIPS沒有單獨的棧操作指令,所有對棧的操作都是統一的內存訪問方式,單這並非不影響MIPS使用堆棧。在發生函數調用時,調用者把函數調用之后要用的寄存器壓入堆棧,被調用者把返回地址寄存器$ra(並非任何時候都保存$ra)和保留寄存器壓入堆棧。同時,調整堆棧指針,並在返回時從堆棧中恢復寄存器。
- $30($fp): 不同編譯器可能對該寄存器使用方法不同。GNU MIPS C編譯器使用了棧指針(Frame Pointer)。SGI的C編譯器則沒有使用棧指針,只是把這個寄存器當成保存寄存器使用($s8),這雖然節省了調用和返回開銷,但增加了代碼生成的復雜度性。
- $31 ($ra): 存放返回地址。MIPS有一個jar(jump-and-link,跳轉並鏈接)指令,在跳轉到某個地址時可把下一條指令的地址放到$ra中,用於支持子程序。例如,調用程序把參數放到$a0~$a3中,“jar X"指令跳到X過程,被調用時需要保存的寄存器為$a0~$a3、$s0~$s7、$gp、$sp、$fp、$ra。
2、特殊寄存器
MIPS32架構中定義了3個特殊寄存器。分別為PC(程序計數器)、HI(乘除結果高位寄存器)和LO(乘除結果低位寄存器)。在進行乘法運算時,HI和LO保存乘法的運算結果,其中HI存儲高32位,LO存儲低32位;而在進行除法運算時,HI保存余數,LO存儲商。
MIPS 指令
MIPS 指令
指令 |
功能 |
應用實例 |
LB |
從存儲器中讀取一個字節的數據到寄存器中 |
LB R1, 0(R2) |
LH |
從存儲器中讀取半個字的數據到寄存器中 |
LH R1, 0(R2) |
LW |
從存儲器中讀取一個字的數據到寄存器中 |
LW R1, 0(R2) |
LD |
從存儲器中讀取雙字的數據到寄存器中 |
LD R1, 0(R2) |
L.S |
從存儲器中讀取單精度浮點數到寄存器中 |
L.S R1, 0(R2) |
L.D |
從存儲器中讀取雙精度浮點數到寄存器中 |
L.D R1, 0(R2) |
LBU |
功能與LB指令相同,但讀出的是不帶符號的數據 |
LBU R1, 0(R2) |
LHU |
功能與LH指令相同,但讀出的是不帶符號的數據 |
LHU R1, 0(R2) |
LWU |
功能與LW指令相同,但讀出的是不帶符號的數據 |
LWU R1, 0(R2) |
SB |
把一個字節的數據從寄存器存儲到存儲器中 |
SB R1, 0(R2) |
SH |
把半個字節的數據從寄存器存儲到存儲器中 |
SH R1,0(R2) |
SW |
把一個字的數據從寄存器存儲到存儲器中 |
SW R1, 0(R2) |
SD |
把兩個字節的數據從寄存器存儲到存儲器中 |
SD R1, 0(R2) |
S.S |
把單精度浮點數從寄存器存儲到存儲器中 |
S.S R1, 0(R2) |
S.D |
把雙精度數據從存儲器存儲到存儲器中 |
S.D R1, 0(R2) |
DADD |
把兩個定點寄存器的內容相加,也就是定點加 |
DADD R1,R2,R3 |
DADDI |
把一個寄存器的內容加上一個立即數 |
DADDI R1,R2,#3 |
DADDU |
不帶符號的加 |
DADDU R1,R2,R3 |
DADDIU |
把一個寄存器的內容加上一個無符號的立即數 |
DADDIU R1,R2,#3 |
ADD.S |
把一個單精度浮點數加上一個雙精度浮點數,結果是單精度浮點數 |
ADD.S F0,F1,F2 |
ADD.D |
把一個雙精度浮點數加上一個單精度浮點數,結果是雙精度浮點數 |
ADD.D F0,F1,F2 |
ADD.PS |
兩個單精度浮點數相加,結果是單精度浮點數 |
ADD.PS F0,F1,F2 |
DSUB |
兩個寄存器的內容相減,也就是定點數的減 |
DSUB R1,R2,R3 |
DSUBU |
不帶符號的減 |
DSUBU R1,R2,R3 |
SUB.S |
一個雙精度浮點數減去一個單精度浮點數,結果為單精度 |
SUB.S F1,F2,F3 |
SUB.D |
一個雙精度浮點數減去一個單精度浮點數,結果為雙精度浮點數 |
SUB.D F1,F2,F3 |
SUB.PS |
兩個單精度浮點數相減 |
SUB.SP F1,F2,F3 |
DDIV |
兩個定點寄存器的內容相除,也就是定點除 |
DDIV R1,R2,R3 |
DDIVU |
不帶符號的除法運算 |
DDIVU R1,R2,R3 |
DIV.S |
一個雙精度浮點數除以一個單精度浮點數,結果為單精度浮點數 |
DIV.S F1,F2,F3 |
DIV.D |
一個雙精度浮點數除以一個單精度浮點數,結果為雙精度浮點數 |
DIV.D F1,F2,F3 |
DIV.PS |
兩個單精度浮點數相除,結果為單精度 |
DIV.PS F1,F2,F3 |
DMUL |
兩個定點寄存器的內容相乘,也就是定點乘 |
DMUL R1,R2,R3 |
DMULU |
不帶符號的乘法運算 |
DMULU R1,R2,R3 |
MUL.S |
一個雙精度浮點數乘以一個單精度浮點數,結果為單精度浮點數 |
DMUL.S F1,F2,F3 |
MUL.D |
一個雙精度浮點數乘以一個單精度浮點數,結果為雙精度浮點數 |
DMUL.D F1,F2,F3 |
MUL.PS |
兩個單精度浮點數相乘,結果為單精度浮點數 |
DMUL.PS F1,F2,F3 |
AND |
與運算,兩個寄存器中的內容相與 |
ANDR1,R2,R3 |
ANDI |
一個寄存器中的內容與一個立即數相與 |
ANDIR1,R2,#3 |
OR |
或運算,兩個寄存器中的內容相或 |
ORR1,R2,R3 |
ORI |
一個寄存器中的內容與一個立即數相或 |
ORIR1,R2,#3 |
XOR |
異或運算,兩個寄存器中的內容相異或 |
XORR1,R2,R3 |
XORI |
一個寄存器中的內容與一個立即數異或 |
XORIR1,R2,#3 |
BEQZ |
條件轉移指令,當寄存器中內容為0時轉移發生 |
BEQZ R1,0 |
BENZ |
條件轉移指令,當寄存器中內容不為0時轉移發生 |
BNEZ R1,0 |
BEQ |
條件轉移指令,當兩個寄存器內容相等時轉移發生 |
BEQ R1,R2 |
BNE |
條件轉移指令,當兩個寄存器中內容不等時轉移發生 |
BNE R1,R2 |
J |
直接跳轉指令,跳轉的地址在指令中 |
J name |
JR |
使用寄存器的跳轉指令,跳轉地址在寄存器中 |
JR R1 |
JAL |
直接跳轉指令,並帶有鏈接功能,指令的跳轉地址在指令中,跳轉發生時要把返回地址存放到R31這個寄存器中 |
JAL R1 name |
JALR |
使用寄存器的跳轉指令,並且帶有鏈接功能,指令的跳轉地址在寄存器中,跳轉發生時指令的放回地址放在R31這個寄存器中 |
JALR R1 |
MOV.S |
把一個單精度浮點數從一個浮點寄存器復制到另一個浮點寄存器 |
MOV.S F0,F1 |
MOV.D |
把一個雙精度浮點數從一個浮點寄存器復制到另一個浮點寄存器 |
MOV.D F0,F1 |
MFC0 |
把一個數據從通用寄存器復制到特殊寄存器 |
MFC0 R1,R2 |
MTC0 |
把一個數據從特殊寄存器復制到通用寄存器 |
MTC0 R1,R2 |
MFC1 |
把一個數據從定點寄存器復制到浮點寄存器 |
MFC1 R1,F1 |
MTC1 |
把一個數據從浮點寄存器復制到定點寄存器 |
MTC1 R1,F1 |
LUI |
把一個16位的立即數填入到寄存器的高16位,低16位補零 |
LUI R1,#42 |
DSLL |
雙字邏輯左移 |
DSLL R1,R2,#2 |
DSRL |
雙字邏輯右移 |
DSRL R1,R2,#2 |
DSRA |
雙字算術右移 |
DSRA R1,R2,#2 |
DSLLV |
可變的雙字邏輯左移 |
DSLLV R1,R2,#2 |
DSRLV |
可變的雙字羅伊右移 |
DSRLV R1,R2,#2 |
DSRAV |
可變的雙字算術右移 |
DSRAV R1,R2,#2 |
SLT |
如果R2的值小於R3,那么設置R1的值為1,否則設置R1的值為0 |
SLT R1,R2,R3 |
SLTI |
如果寄存器R2的值小於立即數,那么設置R1的值為1,否則設置寄存器R1的值為0 |
SLTI R1,R2,#23 |
SLTU |
功能與SLT一致,但是帶符號的 |
SLTU R1,R2,R3 |
SLTUI |
功能與SLT一致,但不帶符號 |
SLTUI R1,R2,R3 |
MOVN |
如果第三個寄存器的內容為負,那么復制一個寄存器的內容到另外一個寄存器 |
MOVN R1,R2,R3 |
MOVZ |
如果第三個寄存器的內容為0,那么復制一個寄存器的內容到另外一個寄存器 |
MOVZ R1,R2,R3 |
TRAP |
根據地址向量轉入管態 |
|
ERET |
從異常中返回到用戶態 |
|
MADD.S |
一個雙精度浮點數與單精度浮點數相乘加,結果為單精度 |
|
MADD.D |
一個雙精度浮點數與單精度浮點數相乘加,結果為雙精度 |
|
MADD.PS |
兩個單精度浮點數相乘加,結果為單精度 |