MIPS快速入門(原文+翻譯):MIPS Architecture and Assembly Language Overview(持續更新中)

前言

發佈該文章的網站已經無法訪問,無法獲得相關翻譯授權,本人的翻譯僅供大家參考學習,儘可能使用直譯,並加上一些譯者注(使用“ [1] ”的形式),以減少信息損失,水平有限,不妥之處還請見諒。

本文不得用於其他用途,侵刪,轉載請加上原文鏈接。

Adapted from: http://edge.mcs.dre.g.el.edu/GICL/people/sevy/architecture/MIPSRef(SPIM).html

點此獲取 >>> 英文原文資源鏈接

其實我並不建議你看翻譯,因爲無論如何,翻譯過後都會造成信息損失甚至錯誤,強烈建議直接看英文原文。

0 MIPS架構及彙編語言概述

0.1 數據類型及字面含義

0.1.1數據類型

  • 所有指令都是32位
  • 基本數據類型
    • 字節型:byte(8 bits)
    • 半字:halfword(2 bytes)
    • 字型:word(4 bytes)
  • 一個字符(character)需要1個字節的存儲空間
  • 一個整數(integer)需要1個字(4個字節)的存儲空間

譯者注:在32位環境下,C語言的char是1個字節,short是2個字節,int是4個字節,可以對比學習,關於有無符號的問題,請先忽略,不妨之後深入學習MIPS數據及指令再瞭解。

0.1.2 字面含義[1]

  • 數字(numbers)按照原樣輸入,就是,例如:4
  • 字符(characters)被單引號括起來,例如:'b'
  • 字符串(strings)被雙引號括起來,例如:"A string"

譯者注
[1] 字面含義的英文爲Literal,中文不妨理解爲:不同類型數據的表示方法。

0.2 寄存器(registers)

  • 32個通用寄存器
  • 在彙編指令中,寄存器以$開頭。訪問(addressing)寄存器的形式有2種:
    • 使用寄存器的編號,例如,從$0$31[1]
    • 使用與編號等價的寄存器名稱,例如,$t1$sp
  • 特殊的寄存器LoHi用於存儲乘法或除法運算的結果
    • 不能直接訪問LoHi寄存器,它們的內容通過特殊的指令訪問:mfhi(move from Hi)和mflo(move from Lo)[2]
  • 棧的增長方向是從存儲器的高地址走向低地址[3]

譯者注
[1] MIPS有32個通用寄存器,編號爲0~31。
[2] 這裏不是完全直譯,直譯的話看不懂,給出你原文對比一下:not directly addressable; contents accessed with special instruction mfhi (“move
from Hi”) and mflo (“move from Lo”).
[3] 這意味着棧底在高地址,棧頂在低地址;數據入棧的時候,棧頂指針是從高地址往低地址方向走的,出棧反之。
[補充] 在此再補充一下“字節序”,它與硬件的設計有關,通常x86系列的硬件採用小端序,MIPS大部分與網絡字節序一樣,採用大端序,也有采用小端序的,不過後來增強了可移植性,採用雙端序,既可以使用小端模式也可以使用大端模式。(參考鏈接在此

這是來自Goodman&Miller的圖9.9[1]注:功能描述請參考原文,這裏沒有完全翻譯,對於初學者來說沒有意義。

寄存器編號 可代替的名字[2] 英文全稱 (功能)描述
0 $zero zero 值恆爲0
1 $at assembler temporary 彙編器保留寄存器
2-3 $v0 , $v1 values 值來自於表達式求值和函數結果
4-7 $a0 - $a3 arguments 存儲子程序調用的前4個非浮點參數,在子程序中不會跨子程序保存
8-15 $t0 - $t7 temporaries 暫存寄存器
16-23 $s0 - $s7 saved values 通用寄存器
24-25 $t8 - $t9 temporaries 臨時變量,與$t0 - $t7一樣
26-27 $k0 , $k1 kernel reserved 操作系統內核保留寄存器,用於中斷處理
28 $gp global pointer 全局指針
29 $sp stack pointer 棧指針,指向棧頂
30 $s8 / $fp saved values / frame pointer 幀指針,用於過程調用
31 $ra return address 返回地址

也參考了Britton的章節1.9,Sweetman的章節2.21,Larus的附錄章節A.6。

譯者注
[1] 譯者此處將圖片以表格形式表現了出來。
[2] 原文Alternative name,中文意思就是:與寄存器編號等價的名字,比如,$2$v0等價。
[補充1] 這裏我們可以注意到,32個寄存器被分成了不同類別,它們都有不同的用途,雖然它們都是通用寄存器,但是使用的時候,還是應該儘量按照實際功能去使用,以免發生不可知的錯誤。
[補充2] 學習建議:這部分內容,看一看有大概印象即可,不要強行記憶,因爲初學者不太可能看得懂這些都是什麼,後面需要的時候再回看,再查閱即可。

1 程序結構

  • 純文本文件中只有數據聲明和程序代碼[1](文件名的後綴爲.s,以能夠在SPIM模擬器運行[2]
  • 數據聲明部分後面跟着程序代碼部分[3]

譯者注:
[1] 意思是:MIPS源程序要在文本編輯器進行編輯,包含數據聲明和程序代碼。
[2] 推薦使用MARS模擬器來進行運行MIPS程序,這款軟件一定程度兼容SPIM,並且提供了更加豐富的插件。另外,MIPS源程序文件的後綴可以是.s,也可以是.asm
[3] 先聲明數據,然後再寫處理數據的代碼。

1.1 數據聲明

  • 放置在程序段中,使用編譯器指令.data來標識
  • 聲明在程序中使用的變量名;存儲(的變量)會被分配到主內存(RAM)中

譯者注:直譯後表示很難受,可能我水平不夠吧……我用漢語描述一遍

  • 在MIPS源程序進行數據聲明的時候,需要先加上.data,再進行數據聲明
  • 在程序中需要用到的數據,需要先在數據段進行聲明,併爲這些數據起名
  • 這些你聲明好的變量(的值),在程序運行的時候,會被系統分配到主內存中

譯者注:數據聲明在源程序中應該是這樣的

.data  # 我要開始寫數據聲明瞭!
	variable_name_1: .word 11 # 聲明瞭一個變量(先不用管語法)
							  # 值:11
							  # 變量名:variable_name_1
	# <其他數據聲明>……

1.2 代碼

  • (代碼需要)放在文本(text)部分,並用.text來標識
  • .text後面的文本)包含程序代碼指令
  • 代碼執行的起始點要給定一個標籤,例如main
  • 主代碼的結束點應該使用“退出系統調用(功能)”,看下面的系統調用篇

1.3 註釋

在一行上,任何在#之後的內容,將會被(編譯器)認爲是註釋[1]

譯者注:
[1] 這裏的註釋,也就是所謂的“單行註釋”。
[補充] 下面的1.4節在原文中是包含在1.3節的,譯者這裏將其單獨拿出來,以強調MIPS源程序結構框架。

1.4 MIPS源程序結構框架示例

下面是MIPS彙編語言程序的模板:

# 註釋部分給出了程序名和函數描述
# 文件名:Template.s 
# 這是最基本的MIPS彙編語言程序框架

 .data 	# 這行之後是變量聲明
# ... 

.text	 # 這行之後的是指令 
 
main: 	# 指出代碼的起始點(第一個要執行的指令) 
# ... 
 
# End of program, leave a blank line afterwards to make SPIM happy
# 程序的結尾留出一個空行,讓SPIM高興[1]

譯者注:
[1] make SPIM happy……外國人太有趣了,這句話含義就是,程序結束的時候多一個空行,這樣看起來程序美觀好看。
[補充] 原文程序的註釋太多了,程序結構都看不見了…讓我們留下最寶貴的部分,刪掉冗餘部分

.data 
	# 此處進行數據聲明

.text
main:	# 這是程序入口
	# 此處編輯程序代碼

2 數據聲明

數據聲明的形式:

name:	storage_type	value(s)

以上語句的含義是:

  • 爲變量創建一個倉庫(storage)[1],它指定了數據類型,給定了變量名和變量的值
  • 變量的值通常會給定初始值;對於存儲類型.space,給出要被分配的空間的數字[2]

注意:標籤[3]後面總是跟着冒號:

譯者注:
[1] 可以理解爲存儲變量的容器
[2] 也就是空間的大小
[3] 指的是變量名

例子:

var1: .word 3			# create a single integer variable with initial value 3 
array1: .byte 'a','b' 	# create a 2-element character array with elements initialized to a and b 
array2: .space 40 		# allocate 40 consecutive bytes, with storage uninitialized could be used as a 40-element character array
 					    # or a 10-element integer array; a comment should indicate which!

譯者注:分別解釋一下這3條

  1. 創建一個integer類型的變量,變量名爲var2,變量的值初始化爲3
  2. 創建一個字符數組array1,包含2個字符類型元素,它們的值分別被初始化爲ab
  3. 創建一個空的空間array2,在內存中分配40個連續的字節,這個沒有被初始化的storage可能是一個含有40個元素的字符數組,也可能是一個含有10個元素的integer數組,由於不確定,因此建議在註釋中說明space的用途。

3 load/store[1]指令

  • RAM的訪問,只能使用loadstore指令[2]
  • 其他的指令只能使用寄存器操作數

譯者注:
[1] load意爲“加載”,可以理解爲“讀取”,CPU從內存中讀取信息;store意爲“存儲”,可以理解爲“寫入”,CPU向內存寫入信息。
[2] MIPS爲RICS指令集,相比x86,它的尋址方式很少,只有這兩條指令可以訪問內存,也就是說,其他指令的操作數,不允許出現內存操作數

load

# 從RAM源位置,複製1個字(4個字節)到目標寄存器
lw register_destination, RAM_source

# 從RAM源位置,複製1個字節到目標寄存器的低序字節
# 並且對字節進行符號擴展,填充到高位字節中
lb register_destination, RAM_source

store

# 將源寄存器中的字(型數據),存到目標RAM(地址)中
sw register_source, RAM_destination

# 將源寄存器中的低位字節,存到目標RAM中
sb register_source, RAM_destination

load immediate

# 加載立即數到目標寄存器
li register_destination, value

舉例[1]

 .data 
	var1: .word 23 		# declare storage for var1; initial value is 23 

 .text 
__start: 
	 lw $t0, var1 		# load contents of RAM location into register $t0: $t0 = var1 
 	 li $t1, 5 			# $t1 = 5 ("load immediate") 
	 sw $t1, var1 		# store contents of register $t1 into RAM: var1 = $t1  done

譯者注:
[1] 這裏不再翻譯,根據前面的內容相信你能理解
[補充] 注意,這些指令其實就是英文全稱的縮寫,記憶起來非常容易,其他指令也一樣,要關注其英文全稱。

4 間接尋址和基址尋址

僅被用於loadstore指令。

load address

la	$t0, var1
  • var1(可能是在程序中被定義的標籤)的RAM地址複製到寄存器$0

indirect addressing

lw	$t2, ($t0)
  • 加載1個字大小的數據到$t2中,數據的地址在$t0中,數據在數據地址指向的內存單元中
sw	$t2, -12($t0)

未完成

5 算數運算指令

  • 最多使用3個操作數
  • 所有操作數都是寄存器;沒有RAM或者直接尋址
  • 操作數的大小都是1個字
add	 $t0,$t1,$t2 	# $t0 = $t1 + $t2; add as signed (2's complement) integers 
sub	  $t2,$t3,$t4 	# $t2 = $t3 Ð $t4 
addi  $t2,$t3, 5 	# $t2 = $t3 + 5; "add immediate" (no sub immediate) 
addu  $t1,$t6,$t7   # $t1 = $t6 + $t7; add as unsigned integers 
subu  $t1,$t6,$t7   # $t1 = $t6 + $t7; subtract as unsigned integers 

mult  $t3,$t4 		
# multiply 32-bit quantities in $t3 and $t4, and store 64-bit 
# result in special registers Lo and Hi: (Hi,Lo) = $t3 * $t4 

div	  $t5,$t6 		
# Lo = $t5 / $t6 (integer quotient) 
# Hi = $t5 mod $t6 (remainder) 

mfhi   $t0	 # move quantity in special register Hi to $t0: $t0 = Hi 
mflo   $t1	 # move quantity in special register Lo to $t1: $t1 = Lo 
			 # used to get at result of product or quotient 

move   $t2,$t3 		# $t2 = $t3

譯者注:
這裏不翻譯了,表達式能夠看懂,注意觀察指令的規律,使用分治思想理解記憶:

  1. 指令是英文全稱的縮寫,按照意思即可記憶
  2. 指令的後綴代表特殊的含義,例如i代表立即數,u代表無符號數

6 流程控制

  • 條件分支的比較,被內嵌到了指令之中

branches

b	target 		# unconditional branch to program label target 
				# 無條件跳轉到程序標籤target

beq 	$t0,$t1,target 		# branch to target if $t0 = $t1 
							# 如果$t0 = $t1,就跳轉到target標籤
							# 下面的同理,不再一一翻譯

blt 	$t0,$t1,target 		# branch to target if $t0 < $t1 
ble 	$t0,$t1,target 		# branch to target if $t0 <= $t1 
bgt 	$t0,$t1,target 		# branch to target if $t0 > $t1 
bge 	$t0,$t1,target 		# branch to target if $t0 >= $t1 
bne 	$t0,$t1,target 		# branch to target if $t0 <> $t1

譯者注:這裏的指令,都是英文全稱,比如beq就是branch equal,其餘的讀者自行查閱。

jumps

j	    target 		# unconditional jump to program label target 
					# 無條件跳轉到程序標籤target
					
jr 		$t3 		# jump to address contained in $t3 ("jump register")
					# 跳轉到某地址,地址的值在寄存器$t3中

未完成
subroutine calls

subroutine call: “jump and link” instruction

jal		sub_label		 # "jump and link"

subroutine return: “jump register” instruction

jr	$ra		 # "jump register"

注意:

譯者注:原文沒有解釋程序標籤target,譯者在這裏補充一下

我給出你MIPS源程序的.text部分,我們知道,程序的入口是main標籤,事實上,還可以類似地定義很多標籤,跳轉指令中的target指的就是這些,以下爲示例:

.text            # 程序代碼部分 

main:  
	<代碼 1>
	j	target_1		# 無條件地跳轉到標籤target_1的位置
						# 再從該位置繼續向下執行<代碼 2>

target_1:
	<代碼 2>

target_2:
	<代碼 3>
……    

7 系統調用與I/O(SPIM模擬器[1]

譯者注:
[1] MARS模擬器兼容了一部分SPIM的系統調用,基本是夠用的,依然推薦使用MARS模擬器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章