MIPS小總結
MIPS
讀入輸出
字串
輸出
.ascii
與.asciiz
.ascii
不會在字串後加上'\0'
,而.asciiz
會在字串加'\0'
。兩者均以位元組為單位儲存資料,這會對我們帶來一些小麻煩,.asciiz
之後分配的空間首地址有可能無法字對齊,因此我們在定義.ascii
與.asciiz
時儘量寫在最後面
#正確寫法 .data array_int: .space 28 space: .asciiz " " #錯誤示範 .data space: .asciiz " " array_int: .space 28 #由於.data後面的變數宣告在記憶體中是緊密有序儲存的,所以後面獲取array的地址時會報錯“fetch address not aligned on word boundary 0x00000002”
syscall($v0=4)
以$a0暫存器所存地址為首地址輸出,直到遇上'\0'
停止
#輸出一個空格並換行
.data
space: .asciiz " "
enter: .asciiz "\n"
.text
la $a0,space #將space的首地址傳給$a0
li $v0,4
syscall
la $a0,enter #將enter的首地址傳給$a0
li $v0,4
syscall
讀入
syscall($v0=8)
讀入一個字串,其中$a0表示讀入的首地址,$a1表示讀入的字元數n,與fget類似,會在讀入的字串最後加'\n'
,因此實際上最多讀入n-1個字元
syscall($v0=12)
讀入一個字元,將讀入的字元存在$v0中。
.data
str: .space 20
.text
#$t0 = i
li $v0,12
syscall
sb $v0,str($t0) # 將讀入的字元存在str[i]中(sb指令僅將暫存器的低8位儲存)
整數
讀入
使用syscall($v0=5),將讀入的整數存在$v0中
輸出
使用syscall($v0=5),將$a0中的整數輸出
條件語句
單條件
相等條件if-else
if(i==j){
THEN語句塊
}else{
ELSE語句塊
}
- 用beq
#t0=i,t1=j beq $t0,$t1,then #若i==j,那麼跳到THEN語句塊,不相等則進行執行下一條語句,即ELSE語句塊 ELSE語句塊 j end #不跳轉到end的話將繼續執行THEN語句塊 then: THEN語句塊 end:
- 用bne
該寫法與C的THEN與ELSE塊順序一樣,所以我一般都是將if中的條件取反後用轉移指令,這樣就保持了與c語言差不多的寫法(老菜雞行為)。
#t0=i,t1=j
bne $t0,$t1,else #若i!=j,那麼跳到ELSE語句塊,相等則進行執行下一條語句,即THEN語句塊
THEN語句塊
j end #不跳轉到end的話將繼續執行ELSE語句塊
else:
ELSE語句塊
end:
與0比較的if-else
使用bxxx rs,label。(同理我為了保持與c語言一樣的寫法,將條件取反後再找指令)
if(a<=0){
THEN塊
}else{
ELSE塊
}
#$t0=a
bgtz $t0,else #當a>0時跳轉
THEN塊
j end
else:
ELSE塊
end:
非0值比較的if-else
使用slt使其轉化為與0比較的if-else,若條件中含=號,則將條件取反(如條件為i<=j,那麼slt判斷的為j<i)。下面列表表示($t0=i,$t1=j,$t2為儲存slt結果的暫存器)
初始條件 | slt | $t2所代表的含義 | beq/bne |
---|---|---|---|
i<j | slt $t2,$t0,$t1 | 0:初始條件為假 1:初始條件為真 | beq $t2,$0,else |
i>j | slt $t2,$t1,$t0 | 0:初始條件為假 1:初始條件為真 | beq $t2,$0,else |
i<=j | slt $t2,$t1,$t0 | 0:初始條件為真 1:初始條件為假 | bne $t2,$0,else |
i>=j | slt $t2,$t0,$t1 | 0:初始條件為真 1:初始條件為假 | bne $t2,$0,else |
(注:均為了保持與c語言順序一致,只寫了該寫法的beq/bne,事實上i<j的beq/bne也可以寫為bne $t2,$0,then,但是這種寫法的THEN塊與ELSE塊與c語言的順序相反)
eg:i<=j,將上表格的對應的slt和beq/bne複製即可
if(i<=j){
THEN塊
}else{
ELSE塊
}
#$t0=i,$t1=j
slt $t2,$t1,$t0
bne $t2,$0,else
THEN塊
j end
else:
ELSE塊
end:
多條件
&&
可以先判斷第一個條件,若不成立直接跳至else,否則判斷第2個條件。
if(a<b&&i<j){
THEN塊
}else{
ELSE塊
}
# $t0=a,$t1=b,$t2=i,$t3=j
slt $t4,$t0,$t1
beq $t4,$0,else#判斷條件1
slt $t4,$t3,$t2
beq $t4,$0,else#判斷條件2
THEN塊
j end
else:
ELSE塊
end:
||
不能判斷了第1個條件就跳轉,應該將兩個條件得出的結果做一次或運算,再判斷是否跳轉。
迴圈語句
c語言: MIPS:
for(i=0;i<n;i++) li $t0,0 # 賦值i=0
{ for_loop:
beq $t0,$s0,end_loop # $s0=n
loop語句塊 loop語句塊
addi $t0,$t0,1
} j for_loop
end_loop:
更多層的就把loop語句塊換成下一層的迴圈即可。
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
loop塊
}
}
# 為了把層次看的更清楚,這裡採用了不同的縮排代表不同的迴圈
li $t0,0 #i=0
for_loopi:
beq $t0,$s0,end_loopi
li $t1,0 #j=0
for_loopj:
beq $t1,$s1,end_loopj
loop語句塊
addi $t1,$t1,1
j for_loopj
end_loopj:
addi $t0,$t0,1
j for_loopi
end_loopi:
一維陣列的使用
字元陣列
宣告
[name]: .space [n]
eg:str: .space 20
使用
- set
li $v0,12
syscall
sb $v0,str($t0) # 讀入一個字元並存到str[i]
- get
lb $t2,str($t0) #將str[i]讀入暫存器$t2
整型陣列
宣告
[name]: .space [n]
其中n應為4*陣列大小
eg:a .space 200
相當於int a[50]
使用
- set
sll $t1,$t0,2 #一定記得地址是i*4
sw $v0,a($t1) # 讀入一個字元並存到a[i]
- get
sll $t1,$t0,2
lb $t2,a($t1) #將a[i]讀入暫存器$t2
二維陣列的使用
宣告
.data
a: .space 256 # int a[8][8]
使用
#使用巨集來簡化
.macro getindex(%ans,%i,%j)
sll %ans,%i,3 # %ans=%i*8,若不是8*8的二維陣列,如是10*10的,那麼這條指令應改為mul %ans,%i,10
add %ans,%ans,%j # %ans=%ans+%j
sll %ans,%ans,2 # %ans=%ans*4
.end_macro
.text
#$t0=i,$t1=j
#存陣列操作:
li $v0,5
syscall
getindex($t2,$t0,$t1)
sw $v0,a($t2) #將讀入的整數存入a[i][j]
#讀陣列操作:
getindex($t2,$t0,$t1)
lw $s0,a($t2) #將a[i][j]的值存至$s0
遞迴函式
按c語言一步一步翻譯就可以,遞迴呼叫的時候把$ra和函式的引數壓棧即可。如c語言中func_name(n+1);
這一條語句就對應MIPS裡的
sw $ra,0($sp) #存$ra
subi $sp,$sp,4
sw $t0,0($sp) #存這一層函式的引數
subi $sp,$sp,4
addi $t1,$t0,1 #將n+1存入$t1
move $a0,$t1 #傳值
jal factorial #下一層函式的引數便是n+1了,當下一層函式執行到return(jr $31)時將回到這一層
addi $sp,$sp,4
lw $t0,0($sp) #讀回這一層的引數
addi $sp,$sp,4
lw $ra,0($sp) #讀回這一層的$ra
下面看一個求階乘的遞迴問題。
int factorial(int n)
{
if(n==1) return 1;
else return n*factorial(n-1);
}
int main()
{
int n;
scanf("%d",&n);
printf("%d",factorial(n));
return 0;
}
main: #int main()
li $v0, 5
syscall
move $s0,$v0 #scanf("%d",&n);
move $a0,$s0 #讓$a0=n,傳入引數
jal factorial #factorial(n);
move $a0,$v0
li $v0,1
syscall #printf();
li $v0,10
syscall #return 0;
factorial: #int factorial (int n)
bne $a0,1,else #if(n==1){
li $v0,1 #return 1;
jr $31 #}
else: #else{
move $t0,$a0
###########
sw $ra,0($sp)
subi $sp,$sp,4
sw $t0,0($sp)
subi $sp,$sp,4 #要壓入棧的東西:$ra和遞迴函式的引數
subi $t1,$t0,1
move $a0,$t1 #這一大段等價於factorial(n-1)
jal factorial
addi $sp,$sp,4
lw $t0,0($sp)
addi $sp,$sp,4
lw $ra,0($sp) #將$ra和這層的引數讀回
############
mult $t0,$v0
mflo $v0
jr $31 #return n*factorial(n-1)