MIPS快速入门(原文+翻译):MIPS Architecture and Assembly Language Overview(持续更新中)
前言
發(fā)布該文章的網(wǎng)站已經(jīng)無法訪問,無法獲得相關(guān)翻譯授權(quán),本人的翻譯僅供大家參考學(xué)習(xí),盡可能使用直譯,并加上一些譯者注(使用“ [1] ”的形式),以減少信息損失,水平有限,不妥之處還請(qǐng)見諒。
本文不得用于其他用途,侵刪,轉(zhuǎn)載請(qǐng)加上原文鏈接。
Adapted from: http://edge.mcs.dre.g.el.edu/GICL/people/sevy/architecture/MIPSRef(SPIM).html
點(diǎn)此獲取 >>> 英文原文資源鏈接
其實(shí)我并不建議你看翻譯,因?yàn)闊o論如何,翻譯過后都會(huì)造成信息損失甚至錯(cuò)誤,強(qiáng)烈建議直接看英文原文。
0 MIPS架構(gòu)及匯編語言概述
0.1 數(shù)據(jù)類型及字面含義
0.1.1數(shù)據(jù)類型
- 所有指令都是32位
- 基本數(shù)據(jù)類型
- 字節(jié)型:byte(8 bits)
- 半字:halfword(2 bytes)[1]
- 字型:word(4 bytes)
- 一個(gè)字符(character)需要1個(gè)字節(jié)的存儲(chǔ)空間
- 一個(gè)整數(shù)(integer)需要1個(gè)字(4個(gè)字節(jié))的存儲(chǔ)空間
譯者注:
[1] 在MARS模擬器,半字用half
[補(bǔ)充] 在32位環(huán)境下,C語言的char是1個(gè)字節(jié),short是2個(gè)字節(jié),int是4個(gè)字節(jié),可以對(duì)比學(xué)習(xí),關(guān)于有無符號(hào)的問題,請(qǐng)先忽略,不妨之后深入學(xué)習(xí)MIPS數(shù)據(jù)及指令再了解。
0.1.2 字面值[1]
- 數(shù)字(numbers)按照原樣輸入,就是,例如:4
- 字符(characters)被單引號(hào)括起來,例如:'b'
- 字符串(strings)被雙引號(hào)括起來,例如:"A string"
譯者注
[1] 英文為Literal,中文不妨理解為:不同類型數(shù)據(jù)的表示方法。
0.2 寄存器(registers)
- 32個(gè)通用寄存器
- 在匯編指令中,寄存器以$開頭。訪問(addressing)寄存器的形式有2種:
- 使用寄存器的編號(hào),例如,從$0到$31[1]
- 使用與編號(hào)等價(jià)的寄存器名稱,例如,$t1,$sp
- 特殊的寄存器Lo和Hi用于存儲(chǔ)乘法或除法運(yùn)算的結(jié)果
- 不能直接訪問Lo和Hi寄存器,它們的內(nèi)容通過特殊的指令訪問:mfhi(move from Hi)和mflo(move from Lo)[2]。
- 棧的增長方向是從存儲(chǔ)器的高地址走向低地址[3]
譯者注
[1] MIPS有32個(gè)通用寄存器,編號(hào)為0~31。
[2] 這里不是完全直譯,直譯的話看不懂,給出你原文對(duì)比一下:not directly addressable; contents accessed with special instruction mfhi (“move
from Hi”) and mflo (“move from Lo”).
[3] 這意味著棧底在高地址,棧頂在低地址;數(shù)據(jù)入棧的時(shí)候,棧頂指針是從高地址往低地址方向走的,出棧反之。
[補(bǔ)充] 在此再補(bǔ)充一下“字節(jié)序”,它與硬件的設(shè)計(jì)有關(guān),通常x86系列的硬件采用小端序,MIPS大部分與網(wǎng)絡(luò)字節(jié)序一樣,采用大端序,也有采用小端序的,不過后來增強(qiáng)了可移植性,采用雙端序,既可以使用小端模式也可以使用大端模式。(參考鏈接在此)
這是來自Goodman&Miller的圖9.9[1](注:功能描述請(qǐng)參考原文,這里沒有完全翻譯,對(duì)于初學(xué)者來說沒有意義。)
| 0 | $zero | zero | 值恒為0 |
| 1 | $at | assembler temporary | 匯編器保留寄存器 |
| 2-3 | $v0 , $v1 | values | 值來自于表達(dá)式求值和函數(shù)結(jié)果 |
| 4-7 | $a0 - $a3 | arguments | 存儲(chǔ)子程序調(diào)用的前4個(gè)非浮點(diǎn)參數(shù),在子程序中不會(huì)跨子程序保存 |
| 8-15 | $t0 - $t7 | temporaries | 暫存寄存器 |
| 16-23 | $s0 - $s7 | saved values | 通用寄存器 |
| 24-25 | $t8 - $t9 | temporaries | 臨時(shí)變量,與$t0 - $t7一樣 |
| 26-27 | $k0 , $k1 | kernel reserved | 操作系統(tǒng)內(nèi)核保留寄存器,用于中斷處理 |
| 28 | $gp | global pointer | 全局指針 |
| 29 | $sp | stack pointer | 棧指針,指向棧頂 |
| 30 | $s8 / $fp | saved values / frame pointer | 幀指針,用于過程調(diào)用 |
| 31 | $ra | return address | 返回地址 |
也參考了Britton的章節(jié)1.9,Sweetman的章節(jié)2.21,Larus的附錄章節(jié)A.6。
譯者注
[1] 譯者此處將圖片以表格形式表現(xiàn)了出來。
[2] 原文Alternative name,中文意思就是:與寄存器編號(hào)等價(jià)的名字,比如,$2與$v0等價(jià)。
[補(bǔ)充1] 這里我們可以注意到,32個(gè)寄存器被分成了不同類別,它們都有不同的用途,雖然它們都是通用寄存器,但是使用的時(shí)候,還是應(yīng)該盡量按照實(shí)際功能去使用,以免發(fā)生不可知的錯(cuò)誤。
[補(bǔ)充2] 學(xué)習(xí)建議:這部分內(nèi)容,看一看有大概印象即可,不要強(qiáng)行記憶,因?yàn)槌鯇W(xué)者不太可能看得懂這些都是什么,后面需要的時(shí)候再回看,再查閱即可。
1 程序結(jié)構(gòu)
- 純文本文件中只有數(shù)據(jù)聲明和程序代碼[1](文件名的后綴為.s,以能夠在SPIM模擬器運(yùn)行[2])
- 數(shù)據(jù)聲明部分后面跟著程序代碼部分[3]
譯者注:
[1] 意思是:MIPS源程序要在文本編輯器進(jìn)行編輯,包含數(shù)據(jù)聲明和程序代碼。
[2] 推薦使用MARS模擬器來進(jìn)行運(yùn)行MIPS程序,這款軟件一定程度兼容SPIM,并且提供了更加豐富的插件。另外,MIPS源程序文件的后綴可以是.s,也可以是.asm。
[3] 先聲明數(shù)據(jù),然后再寫處理數(shù)據(jù)的代碼。
1.1 數(shù)據(jù)聲明
- 放置在程序段中,使用編譯器指令.data來標(biāo)識(shí)
- 聲明在程序中使用的變量名;存儲(chǔ)(的變量)會(huì)被分配到主內(nèi)存(RAM)中
譯者注:直譯后表示很難受,可能我水平不夠吧……我用漢語描述一遍
- 在MIPS源程序進(jìn)行數(shù)據(jù)聲明的時(shí)候,需要先加上.data,再進(jìn)行數(shù)據(jù)聲明
- 在程序中需要用到的數(shù)據(jù),需要先在數(shù)據(jù)段進(jìn)行聲明,并為這些數(shù)據(jù)起名
- 這些你聲明好的變量(的值),在程序運(yùn)行的時(shí)候,會(huì)被系統(tǒng)分配到主內(nèi)存中
譯者注:數(shù)據(jù)聲明在源程序中應(yīng)該是這樣的
.data # 我要開始寫數(shù)據(jù)聲明了!variable_name_1: .word 11 # 聲明了一個(gè)變量(先不用管語法)# 值:11# 變量名:variable_name_1# <其他數(shù)據(jù)聲明>……1.2 代碼
- (代碼需要)放在文本(text)部分,并用.text來標(biāo)識(shí)
- (.text后面的文本)包含程序代碼指令
- 代碼執(zhí)行的起始點(diǎn)要給定一個(gè)標(biāo)簽,例如main
- 主代碼的結(jié)束點(diǎn)應(yīng)該使用“退出系統(tǒng)調(diào)用(功能)”,看下面的系統(tǒng)調(diào)用篇
1.3 注釋
在一行上,任何在#之后的內(nèi)容,將會(huì)被(編譯器)認(rèn)為是注釋[1]。
譯者注:
[1] 這里的注釋,也就是所謂的“單行注釋”。
[補(bǔ)充] 下面的1.4節(jié)在原文中是包含在1.3節(jié)的,譯者這里將其單獨(dú)拿出來,以強(qiáng)調(diào)MIPS源程序結(jié)構(gòu)框架。
1.4 MIPS源程序結(jié)構(gòu)框架示例
下面是MIPS匯編語言程序的模板:
# 注釋部分給出了程序名和函數(shù)描述 # 文件名:Template.s # 這是最基本的MIPS匯編語言程序框架.data # 這行之后是變量聲明 # ... .text # 這行之后的是指令 main: # 指出代碼的起始點(diǎn)(第一個(gè)要執(zhí)行的指令) # ... # End of program, leave a blank line afterwards to make SPIM happy # 程序的結(jié)尾留出一個(gè)空行,讓SPIM高興[1]譯者注:
[1] make SPIM happy……外國人太有趣了,這句話含義就是,程序結(jié)束的時(shí)候多一個(gè)空行,這樣看起來程序美觀好看。
[補(bǔ)充] 原文程序的注釋太多了,程序結(jié)構(gòu)都看不見了…讓我們留下最寶貴的部分,刪掉冗余部分
2 數(shù)據(jù)聲明
數(shù)據(jù)聲明的形式:
name: storage_type value(s)以上語句的含義是:
- 為變量創(chuàng)建一個(gè)倉庫(storage)[1],它指定了數(shù)據(jù)類型,給定了變量名和變量的值
- 變量的值通常會(huì)給定初始值;對(duì)于存儲(chǔ)類型.space,給出要被分配的空間的數(shù)字[2]
注意:標(biāo)簽[3]后面總是跟著冒號(hào):
譯者注:
[1] 可以理解為存儲(chǔ)變量的容器
[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條
3 load/store[1]指令
- RAM的訪問,只能使用load和store指令[2]
- 其他的指令只能使用寄存器操作數(shù)
譯者注:
[1] load意為“加載”,可以理解為“讀取”,CPU從內(nèi)存中讀取信息;store意為“存儲(chǔ)”,可以理解為“寫入”,CPU向內(nèi)存寫入信息。
[2] MIPS為RICS指令集,相比x86,它的尋址方式很少,只有這兩條指令可以訪問內(nèi)存,也就是說,其他指令的操作數(shù),不允許出現(xiàn)內(nèi)存操作數(shù)
load:
# 從RAM源位置,復(fù)制1個(gè)字(4個(gè)字節(jié))到目標(biāo)寄存器 lw register_destination, RAM_source# 從RAM源位置,復(fù)制1個(gè)字節(jié)到目標(biāo)寄存器的低序字節(jié) # 并且對(duì)字節(jié)進(jìn)行符號(hào)擴(kuò)展,填充到高位字節(jié)中 lb register_destination, RAM_sourcestore:
# 將源寄存器中的字(型數(shù)據(jù)),存到目標(biāo)RAM(地址)中 sw register_source, RAM_destination# 將源寄存器中的低位字節(jié),存到目標(biāo)RAM中 sb register_source, RAM_destinationload immediate:
# 加載立即數(shù)到目標(biāo)寄存器 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] 這里不再翻譯,根據(jù)前面的內(nèi)容相信你能理解
[補(bǔ)充] 注意,這些指令其實(shí)就是英文全稱的縮寫,記憶起來非常容易,其他指令也一樣,要關(guān)注其英文全稱。
4 間接尋址和基址尋址
僅被用于load和store指令。
load address:
la $t0, var1- 將var1(可能是在程序中被定義的標(biāo)簽)的RAM地址復(fù)制到寄存器$0
indirect addressing:
lw $t2, ($t0)- 加載1個(gè)字大小的數(shù)據(jù)到$t2中,數(shù)據(jù)的地址在$t0中,數(shù)據(jù)在數(shù)據(jù)地址指向的內(nèi)存單元中
未完成
5 算數(shù)運(yùn)算指令
- 最多使用3個(gè)操作數(shù)
- 所有操作數(shù)都是寄存器;沒有RAM或者直接尋址
- 操作數(shù)的大小都是1個(gè)字
譯者注:
這里不翻譯了,表達(dá)式能夠看懂,注意觀察指令的規(guī)律,使用分治思想理解記憶:
6 流程控制
- 條件分支的比較,被內(nèi)嵌到了指令之中
branches:
b target # unconditional branch to program label target # 無條件跳轉(zhuǎn)到程序標(biāo)簽targetbeq $t0,$t1,target # branch to target if $t0 = $t1 # 如果$t0 = $t1,就跳轉(zhuǎn)到target標(biāo)簽# 下面的同理,不再一一翻譯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 # 無條件跳轉(zhuǎn)到程序標(biāo)簽targetjr $t3 # jump to address contained in $t3 ("jump register")# 跳轉(zhuǎn)到某地址,地址的值在寄存器$t3中未完成
subroutine calls:
subroutine call: “jump and link” instruction
jal sub_label # "jump and link"subroutine return: “jump register” instruction
jr $ra # "jump register"注意:
譯者注:原文沒有解釋程序標(biāo)簽target,譯者在這里補(bǔ)充一下
我給出你MIPS源程序的.text部分,我們知道,程序的入口是main標(biāo)簽,事實(shí)上,還可以類似地定義很多標(biāo)簽,跳轉(zhuǎn)指令中的target指的就是這些,以下為示例:
.text # 程序代碼部分 main: <代碼 1>j target_1 # 無條件地跳轉(zhuǎn)到標(biāo)簽target_1的位置# 再從該位置繼續(xù)向下執(zhí)行<代碼 2>target_1:<代碼 2>target_2:<代碼 3> ……7 系統(tǒng)調(diào)用與I/O(SPIM模擬器[1])
譯者注:
[1] MARS模擬器兼容了一部分SPIM的系統(tǒng)調(diào)用,基本是夠用的,依然推薦使用MARS模擬器。
總結(jié)
以上是生活随笔為你收集整理的MIPS快速入门(原文+翻译):MIPS Architecture and Assembly Language Overview(持续更新中)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 长虹chiq电视可以用鼠标控制吗
- 下一篇: Visual Studio 编译优化选项