前言
發佈該文章的網站已經無法訪問,無法獲得相關翻譯授權,本人的翻譯僅供大家參考學習,儘可能使用直譯,並加上一些譯者注(使用“ [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
- 使用寄存器的編號,例如,從
- 特殊的寄存器
Lo
和Hi
用於存儲乘法或除法運算的結果- 不能直接訪問
Lo
和Hi
寄存器,它們的內容通過特殊的指令訪問: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條
- 創建一個
integer類型
的變量,變量名爲var2
,變量的值初始化爲3
- 創建一個字符數組
array1
,包含2個字符類型元素,它們的值分別被初始化爲a
和b
- 創建一個空的空間
array2
,在內存中分配40個連續的字節,這個沒有被初始化的storage可能是一個含有40個元素的字符數組,也可能是一個含有10個元素的integer數組,由於不確定,因此建議在註釋中說明space的用途。
3 load/store[1]指令
- RAM的訪問,只能使用
load
和store
指令[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 間接尋址和基址尋址
僅被用於load
和store
指令。
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
譯者注:
這裏不翻譯了,表達式能夠看懂,注意觀察指令的規律,使用分治思想理解記憶:
- 指令是英文全稱的縮寫,按照意思即可記憶
- 指令的後綴代表特殊的含義,例如
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模擬器。