# 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 過程的指令。 ```