目錄
關於本文
整體參考MIPS官方使用教學:https://dpetersanderson.github.io/Help/MarsHelpIntro.html
MIPS是什麼
https://zh.wikipedia.org/wiki/MIPS%E6%9E%B6%E6%A7%8B
記憶體與暫存器
MIPS有兩種存資料的方法,一個是記憶體,一個是暫存器。
MIPS的程式結構
.data
#數據宣告區
.text
#程式碼區
數據區的宣告方法
數據區是記憶體宣告的地方。
變數名稱: .型態 資料
範例:
如果我要宣告一個叫做a的整數,存15
a: .word 15
常見型態
- word ,差不多就是C++的int
- space,空間,後面接的值代表要多少byte(1word = 4byte)
- asciiz,字串
(其他用不到就不列了)
陣列宣告可以使用space,比如5格陣列的array就這樣寫
array: .space 20
怎麼把資料從記憶體取出來或是放進去
從暫存器放到記憶體(load word)
lw 暫存器,記憶體位址
從記憶體放到暫存器(save word)
sw 暫存器,記憶體位址
比如要把變數a的資料存進$t0
.data
a: .word 12
.text
sw $t0,a
記憶體位址
記憶體位址是一個值,除了像上面的範例中直接用數據的變數名稱。
也可以用la(load address)放到暫存器裡。
la 暫存器,記憶體名稱
此時暫存器存的不是記憶體裡的值,而是記憶體的位址。
記憶體的位址本身也是值,所以可以後面提到的運算來改變它,但要記得1word = 4 byte,所以通常會是加減4的倍數。
暫存器
MIPS有提供很多個暫存器供人使用,暫存器本身有名字跟定義。
下面是比較常會用到的暫存器:
名字 | 象徵含義 |
---|---|
$t0~9 | 臨時暫存器 |
$s0~7 | 靜止暫存器 |
$a0~3 | 函式參數 |
$v0~3 | 函式回傳值 |
t跟s在寫程式上基本上沒啥差別,不過你可以當成i、j、k跟num這種變數名稱的差別來思考。
要注意的是,以上這些暫存器他們沒有高階語言的生命週期之類的性質,你還是要當作全部都是全域變數來做思考。
另外還有f開頭的,好像是可以拿來存浮點數,不太清楚。
還有三個特殊的暫存器會被使用到,\$sp、\$hi、\$lo,等使用到時會再提到功能。
暫存器的操作
設值
load immediate
li 暫存器,數值
li $t0,12
i 是即時immediate的意思,你可以理解為「被寫在程式碼的數值」,而不是像之前存在記憶體。
複製
move A暫存器,B暫存器
把B暫存器的值存入A暫存器
運算
反正就是A = B@C,@代入你要的運算子
加法
add A暫存器,B暫存器,C暫存器
addi A暫存器,B暫存器,數值
減法
sub A暫存器,B暫存器,C暫存器
subi A暫存器,B暫存器,數值
(addi數值寫負的也可以當作減法使用)
乘法
mul A暫存器,B暫存器,C暫存器
除法
div A暫存器,B暫存器,C暫存器
如果需要餘數,可以用這個方法
div A暫存器,B暫存器
他的商會存在\$lo,餘數存在\$hi
這兩個暫存器比較特別,你只能用mflo跟mfhi取出來放到暫存器裡。
mflo 暫存器
左移、右移
左移1格(可以當成x2來理解)
sll $t0,1
右移1格(可以當成//2來理解)
srl $t0,1
址取值
如果今天\$t0存的是一個記憶體位址,可以用(\$t0)來當作記憶體位址的值。
基本上是搭配上面的lw跟sw來用。
lw $t1,($t0)
也可以在小括號寫數值,代表往後位移幾格
lw $t1,4($t0)
但是如果今天是要位移$t2格,必須要這樣寫喔
sll $t2,2
add $t0,$t0,$t2
lw $t1,($t0)
不能直接$t2($t0)
這樣寫。
syscall
你可以呼叫一些系統服務來做到讀取、寫入等等。
使用方法是先把你的參數存好,然後把$v0設成你要的服務對應的數值,然後執行syscall
$v0 | 功能 | 參數 | 回傳值 |
---|---|---|---|
1 | 整數輸出 | 把要輸出的整數存到$a0 | |
2 | 浮點數輸出 | 把要輸出的整數存到$f12 | |
3 | 雙倍精讀浮點數輸出 | 把要輸出的整數存到$f12 | |
4 | 字串輸出 | 把要輸出的字串記憶體位址存到$a0 | |
5 | 輸入整數 | 數值會存到$v0 | |
6 | 輸入浮點數 | 數值存到$v0 | |
7 | 輸入雙倍精讀浮點數 | 數值存到$v0 | |
8 | 輸入字串 | 讀到的字串記憶體位址放在$v0 | |
10 | 結束,跟C++的exit(0)同個功能 |
li $a0,10
li $v0,1
syscall
跳轉
基本上就是goto,使用方法是labal
跳轉有三種,j、jal、jr
指令 | 意思 |
---|---|
j | 跳轉到指定的label |
jal | 跳轉到指定的label並把現在位址存進$ra(具體原理不用管,但反正如果需要跳回來就用jal) |
jr | 前面用過jal,現在要跳回去 |
main:
jal other_label
j end
other_label:
jr $ra
end:
判斷
判斷基本上就是滿足條件時,j到指定label
指令 | 意思 |
---|---|
beq | 如果相等 |
bne | 如果不相等 |
blt | 如果小於 |
ble | 如果小於等於 |
bgt | 如果大於 |
bge | 如果大於等於 |
beq $t1,$t2,if_equal
函式
MIPS本身是沒有子程式或是函式的功能,但我們可以自己去實現。
堆疊
你可以直接當作MIPS有給你一個堆疊來存資料,\$sp是這個堆疊的記憶體位,使用方法是先把\$sp-4,然後放入值。
放入
addi $sp,$sp,-4
sw $t0,($sp)
取出就完全相反
lw $t0,($sp)
addi $sp,$sp,4
所以我們可以透過先把暫存器丟如堆疊來實現區域變數。(函式結束後暫存器恢復原樣)
func:
addi $sp,$sp,-4
sw $t0,($sp)
#先把$t0的值存起來,這時不管怎麼使用$t0,函式結束都可以回去
lw $t0,($sp)
addi $sp,$sp,4
jr $ra
如果這樣思考的話遞回其實就很簡單了。
總範例
sigma 1~12
.data
var: .word 12
.text
main:
lw $a0,var
jal sigma
move $a0,$v0
li $v0,1
syscall
j end
sigma:
sigma_begin:
addi $sp,$sp,-8
sw $a0,0($sp)
sw $t0,4($sp)
li $t0,1
beq $a0,t0,is_one
jal sigma
add $v0,$v0,$a0
j sigma_end
is_one:
li $v0,1
j sigma_end
sigma_end:
lw $t0,4($sp)
lw $a0,0($sp)
addi $sp,$sp,8
jr $ra
end: