# ISA進階操作(Cont.)
## MIPS Decision Instructions
* `beq`(Branch if Equal):當兩個 register 的值相等時,執行分支。語法如下:
* `beq register1, register2, L1` 相當於c語言 `if (register1==register2) goto L1`
* `bne`(Branch if Not Equal):當兩個 register 的值不相等時,執行分支。語法如下:
* `bne register1, register2, L1` 相當於c語言 `if (register1!=register2) goto L1`
這些稱為 conditional branches(有條件分支)
* `j label`
* 這條指令的意義是直接跳轉到給定的 `label`,可以視為無條件的 goto 操作
* 這條指令採用J-format指令,詳情可見上一篇筆記
這稱為 unconditional branches(無條件分支)
以下`if-else`程式碼舉例:
```
# C code
if (i == j) f = g + h;
else f = g - h;
# 對應的 MIPS assembly code
bne $s2, $s3, Else # 如果 i != j,跳轉到 Else
add $s0, $s1, $s4 # f=g+h
j Exit # 跳轉到 Exit
Else:
sub $s0, $s1, $s4 # f=g-h
Exit:
```
以及`while` 舉例
```
# C code:
while (save[i] == k) i += 1;
# i in $s3, k in $s5, address of save in $s6
# 對應的 MIPS code:
# Compiled MIPS 代碼:
Loop:
sll $t1, $s3, 2 # 將$s3 的值左移兩位,相當於乘以 4(2^2)。結果存儲在 $t1 中。
add $t1, $t1, $s6 # $t1 = save[5] 的地址,將 (3 + 2)的 offset 加上 $s6中save[0] 的位址得到 save[5]的位址,並存儲在$t1
lw $t0, 0($t1) # $t0 = save[i],載入 save[i] 的值
bne $t0, $s5, Exit # 如果 save[i] 不等於 k,跳轉到 Exit 標籤
addi $s3, $s3, 1 # i = i + 1,增加索引
j Loop # 跳轉回 Loop 標籤,重新執行迴圈
Exit:
```
進行小於(<)的比較可以使用 `slt` (Set on Less Than)指令。
* `slt rd, rs, rt`
* 如果 rs 小於 rt,則將目的 register rd 設置為 1,否則設置為 0。
* `slti rt, rs, constant`
* 如果 rs 小於Immediate constant,則將目的 register rt 設置為 1,否則設置為 0。
以下舉例
```
# c語言
if (g < h) goto Less;
# g: $s0, h: $s1
slt $t0, $s0, $s1 # $t0 = 1 if g < h
bne $t0, $zero, Less # 如果 $t0 不等於 0(即 g < h 成立),則跳轉到 Less
```
### 但需要注意在assembly language中,要區分 signed 以及 unsigned
例子:
$s0 = 1111 1111 1111 1111 1111 1111 1111 1111
$s1 = 0000 0000 0000 0000 0000 0000 0000 0001
```
# signed 比較,檢查 $s0 是否小於 $s1
slt $t0, $s0, $s1
# 結果:-1 < +1,因此 $t0 被設置為 1
# unsigned 比較,檢查 $s0 是否小於 $s1
sltu $t0, $s0, $s1
# 結果:+4,294,967,295(unsigned最大值) > +1,因此 $t0 被設置為 0
```
在signed 以及 unsigned 的情境下,相同的二進位表示可以有不同的解釋。
## Procedure Calling
相當於程式語言中的函式調用
以下是涉及到呼叫方(Caller)和被呼叫方(Callee)之間的一系列步驟:
**Caller(呼叫方):**
1. Caller將需要傳遞給被Caller的參數放入指定的 register 中。通常,前幾個參數會被放入特定的 register (例如 $a0, $a1 等)。
2. Caller使用 jal(Jump and Link)指令將控制權轉移到被Caller的起始地址。在轉移之前,jal 指令會將返回地址存儲在 register $ra 中
**被呼叫方(Callee):**
3. Callee 為程序的local variable和其他存儲需求分配空間。這通常通過修改 stack pointer `$sp` 來實現。
4. Callee 執行程序的操作,包括對參數的處理、local variable的使用等。
5. 如果程序有返回值,Callee 將結果放入呼叫方指定的 register 中。通常,這是通過將結果存儲在 $v0(返回值 register )中來完成的。
6. Callee 使用 jr $ra 指令將控制權返回到呼叫方。這個指令從 $ra register 中載入返回地址,並跳轉到該地址。
舉例來說:
```
c code
int leaf_example (int g, h, i, j)
{ int f;
f = (g + h) - (i + j);
return f;
}
leaf_example:
addi $sp, $sp, -12 # 1. 調整stack pointer,為局local variable的使用等分配12個 bits 的空間
sw $s0, 0($sp) # 2. 將 $s0 register 的值存入 stack
sw $t1, 4($sp) # 3. 將 $t1 register 的值存入 stack
sw $t2, 8($sp) # 4. 將 $t2 register 的值存入 stack
add $t1, $a0, $a1 # 5. 計算 $t1 = g + h
add $t2, $a2, $a3 # 6. 計算 $t2 = i + j
sub $s0, $t1, $t2 # 7. 計算 $s0 = $t1 - $t2
move $v0, $s0 # 8. 將結果 $s0 存入Result register $v0
lw $s0, 0($sp) # 9. 從 stack 中還原 $s0 的值
lw $t1, 4($sp) # 10. 從 stack 中還原 $t1 的值
lw $t2, 8($sp) # 11. 從 stack 中還原 $t2 的值
addi $sp, $sp, 12 # 12. 調整 stack pointer,釋放局部變數佔用的空間
jr $ra # 13. Result,跳轉到返回地址
Program Counter (PC) 的作用:
# 在步驟 1-12 中,PC 在這段代碼中沒有被直接修改,因此保持不變。
# 在步驟 13 中,`jr $ra` 指令將 PC 設置為返回地址,即返回到调用 leaf_example 過程的指令。
```