無心中找到一篇十分好用,並且篇幅也不是很大的入門教程,通篇閱後,再把「栗子」敲一遍,基本能夠有一個比較理性的認識,從而方便更好地進一步深刻學習。html
廢話很少說,上乾貨(英語好的直接跳過本人的渣翻譯了哈——!純本人手打原創,有錯請指教,要轉載請聲明出處,謝~~):express
(開始以前稍微再提下,總體分爲4個結構:)數組
1: 寄存器種類;架構
2: 算術及尋址指令app
3: 程序結構函數
4: 系統調用 學習
Register 寄存器編號this |
Alternative 寄存器名 |
Description 寄存器用途 |
---|---|---|
0
|
zero
|
the value 0 永遠返回零 |
1
|
$at
|
(assembler temporary) reserved by the assembler 彙編保留寄存器(不可作其餘用途) |
2-3
|
$v0 - $v1
|
(values) from expression evaluation and function results (Value簡寫)存儲表達式或者是函數的返回值 |
4-7
|
$a0 - $a3
|
(arguments) First four parameters for subroutine. (Argument簡寫)存儲子程序的前4個參數,在子程序調用過程當中釋放 |
8-15
|
$t0 - $t7
|
(temporaries) Caller saved if needed. Subroutines can use w/out saving. (Temp簡寫)臨時變量,同上調用時不保存 |
16-23
|
$s0 - $s7
|
(saved values) - Callee saved. (Saved or Static簡寫?)靜態變量?調用時保存 |
24-25
|
$t8 - $t9
|
(temporaries) Caller saved if needed. Subroutines can use w/out saving. (Temp簡寫)算是前面$0~$7的一個繼續,屬性同$t0~$t7 |
26-27
|
$k0 - $k1
|
reserved for use by the interrupt/trap handler (breaK off簡寫?)中斷函數返回值,不可作其餘用途 |
28
|
$gp
|
global pointer. (Global Pointer簡寫)指向64k(2^16)大小的靜態數據塊的中間地址(字面上好像就是這個意思,塊的中間) |
29
|
$sp
|
stack pointer (Stack Pointer簡寫)棧指針,指向的是棧頂 |
30
|
$s8/$fp
|
saved value / frame pointer (Saved/Frame Pointer簡寫)幀指針 |
31
|
$ra
|
return address 返回地址,目測也是不可作其餘用途 |
# Comment giving name of program and description of function
# 說明下程序的目的和做用(其實和高級語言都差很少了) # Template.s #Bare-bones outline of MIPS assembly language program
.data # variable declarations follow this line
# 數據變量聲明 # ... .text # instructions follow this line # 代碼段部分 main: # indicates start of code (first instruction to execute)
# 主程序 # ... # End of program, leave a blank line afterwards to make SPIM happy
# 必須多給你一行,你才歡?
format for declarations:
聲明的格式:
name: storage_type value(s)
變量名:(冒號別少了) 數據類型 變量值
Note: labels always followed by colon ( : )
example var1: .word 3 # create a single integer variable with initial value 3
# 聲明一個 word 類型的變量 var1, 同時給其賦值爲 3 array1: .byte 'a','b' # create a 2-element character array with elements initialized # to a and b
# 聲明一個存儲2個字符的數組 array1,並賦值 'a', '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!
# 爲變量 array2 分配 40字節(bytes)未使用的連續空間,固然,對於這個變量
# 到底要存放什麼類型的值, 最好事先聲明註釋下!
load:
lw register_destination, RAM_source
#copy word (4 bytes) at source RAM location to destination register.
從內存中 複製 RAM_source 的內容到 對應的寄存器中
(lw中的'w'意爲'word',即該數據大小爲4個字節)
lb register_destination, RAM_source
#copy byte at source RAM location to low-order byte of destination register,
# and sign-e.g.tend to higher-order bytes同上, lb 意爲 load byte
store word:
sw register_source, RAM_destination
#store word in source register into RAM destination
#將指定寄存器中的數據 寫入 到指定的內存中
sb register_source, RAM_destination
#store byte (low-order) in source register into RAM destination
load immediate:
li register_destination, value
#load immediate value into destination register
顧名思義,這裏的 li 意爲 load immediate
example: .data var1: .word 23 # declare storage for var1; initial value is 23 # 先聲明一個 word 型的變量 var1 = 3; .text __start: lw $t0, var1 # load contents of RAM location into register $t0: $t0 = var1
# 令寄存器 $t0 = var1 = 3; li $t1, 5 # $t1 = 5 ("load immediate")
# 令寄存器 $t1 = 5; sw $t1, var1 # store contents of register $t1 into RAM: var1 = $t1
# 將var1的值修改成$t1中的值: var1 = $t1 = 5; done
load address:
直接給地址
la $t0, var1
indirect addressing:
地址是寄存器的內容(能夠理解爲指針)
lw $t2, ($t0)
sw $t2, ($t0)
based or indexed addressing:
+偏移量
lw $t2, 4($t0)
sw $t2, -12($t0)
Note: based addressing is especially useful for:
沒必要多說,要用到偏移量的尋址,基本上使用最多的場景無非兩種:數組,棧。
example:
栗子: .data array1: .space 12 # declare 12 bytes of storage to hold array of 3 integers
# 定義一個 12字節 長度的數組 array1, 容納 3個整型 .text __start: la $t0, array1 # load base address of array into register $t0
# 讓 $t0 = 數組首地址 li $t1, 5 # $t1 = 5 ("load immediate") sw $t1, ($t0) # first array element set to 5; indirect addressing
# 對於 數組第一個元素賦值 array[0] = $1 = 5 li $t1, 13 # $t1 = 13 sw $t1, 4($t0) # second array element set to 13
# 對於 數組第二個元素賦值 array[1] = $1 = 13
# (該數組中每一個元素地址相距長度就是自身數據類型長度,即4字節, 因此對於array+4就是array[1]) li $t1, -7 # $t1 = -7 sw $t1, 8($t0) # third array element set to -7
# 同上, array+8 = (address[array[0])+4)+ 4 = address(array[1]) + 4 = address(array[2]) done
add $t0,$t1,$t2 # $t0 = $t1 + $t2; add as signed (2's complement) integers
sub $t2,$t3,$t4 # $t2 = $t3 Ð $t4 addi $t2,$t3, 5 # $t2 = $t3 + 5; "add immediate" (no sub immediate) addu $t1,$t6,$t7 # $t1 = $t6 + $t7; add as unsigned integers subu $t1,$t6,$t7 # $t1 = $t6 + $t7; subtract as unsigned integers mult $t3,$t4 # multiply 32-bit quantities in $t3 and $t4, and store 64-bit # result in special registers Lo and Hi: (Hi,Lo) = $t3 * $t4
運算結果存儲在hi,lo(hi高位數據, lo地位數據) div $t5,$t6 # Lo = $t5 / $t6 (integer quotient) # Hi = $t5 mod $t6 (remainder)
商數存放在 lo, 餘數存放在 hi mfhi $t0 # move quantity in special register Hi to $t0: $t0 = Hi
不能直接獲取 hi 或 lo中的值, 須要mfhi, mflo指令傳值給寄存器 mflo $t1 # move quantity in special register Lo to $t1: $t1 = Lo # used to get at result of product or quotient
move $t2,$t3 # $t2 = $t3
Branches
分支(if else系列)
- comparison for conditional branches is built into instruction
b target # unconditional branch to program label target beq $t0,$t1,target # branch to target if $t0 = $t1 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
Jumps
跳轉(while, for, goto系列)
j target # unconditional jump to program label target
看到就跳, 不用考慮任何條件
jr $t3 # jump to address contained in $t3 ("jump register")
相似相對尋址,跳到該寄存器給出的地址處Subroutine Calls
子程序調用
subroutine call: "jump and link" instruction
jal sub_label # "jump and link"
- copy program counter (return address) to register $ra (return address register)
- 將當前的程序計數器保存到 $ra 中
- jump to program statement at sub_label
subroutine return: "jump register" instruction
jr $ra # "jump register"
- jump to return address in $ra (stored by jal instruction)
- 經過上面保存在 $ra 中的計數器返回調用前
Note: return address stored in register $ra; if subroutine will call other subroutines, or is recursive, return address should be copied from $ra onto stack to preserve it, since jal always places return address in this register and hence will overwrite previous value
若是說調用的子程序中有調用了其餘子程序,如此往復, 則返回地址的標記就用 棧(stack) 來存儲, 畢竟 $ra 只有一個, (哥哥我分身乏術啊~~)。
下表給出了系統調用中對應功能,代碼,參數機返回值
Service | Code 對應功能的調用碼 |
Arguments 所需參數 |
Results 返回值 |
---|---|---|---|
print_int 打印一個整型 |
$v0 = 1
|
$a0 = integer to be printed 將要打印的整型賦值給 $a0 |
|
print_float 打印一個浮點 |
$v0 = 2
|
$f12 = float to be printed 將要打印的浮點賦值給 $f12 |
|
print_double 打印雙精度 |
$v0 = 3
|
$f12 = double to be printed 將要打印的雙精度賦值給 $f12 |
|
print_string |
$v0 = 4
|
$a0 = address of string in memory 將要打印的字符串的地址賦值給 $a0 |
|
read_int |
$v0 = 5
|
integer returned in $v0 將讀取的整型賦值給 $v0 |
|
read_float 讀取浮點 |
$v0 = 6
|
float returned in $v0 將讀取的浮點賦值給 $v0 |
|
read_double 讀取雙精度 |
$v0 = 7
|
double returned in $v0 將讀取的雙精度賦值給 $v0 |
|
read_string 讀取字符串 |
$v0 = 8
|
$a0 = memory address of string input buffer 將讀取的字符串地址賦值給 $a0 將讀取的字符串長度賦值給 $a1 |
|
sbrk 應該同C中的sbrk()函數 動態分配內存 |
$v0 = 9
|
$a0 = amount 須要分配的空間大小(單位目測是字節 bytes) |
address in $v0 將分配好的空間首地址給 $v0 |
exit 退出 |
$v0 =10
|
你懂得 |
e.g. Print out integer value contained in register $t2
栗子: 打印一個存儲在寄存器 $2 裏的整型
li $v0, 1 # load appropriate system call code into register $v0;
聲明須要調用的操做代碼爲 1 (print_int) 並賦值給 $v0 # code for printing integer is 1 move $a0, $t2 # move integer to be printed into $a0: $a0 = $t2
將要打印的整型賦值給 $a0 syscall # call operating system to perform operation
e.g. Read integer value, store in RAM location with label int_value (presumably declared in data section)
栗子: 讀取一個數,而且存儲到內存中的 int_value 變量中
li $v0, 5 # load appropriate system call code into register $v0; # code for reading integer is 5
聲明須要調用的操做代碼爲 5 (read_int) 並賦值給 $v0 syscall # call operating system to perform operation、
通過讀取操做後, $v0 的值已經變成了 輸入的 5 sw $v0, int_value # value read from keyboard returned in register $v0; # store this in desired location
經過寫入(store_word)指令 將 $v0的值(5) 存入 內存中 e.g. Print out string (useful for prompts)
栗子: 打印一個字符串(這是完整的,其實上面栗子均可以直接替換main: 部分,都能直接運行) .data string1 .asciiz "Print this.\n" # declaration for string variable, # .asciiz directive makes string null terminated .text main: li $v0, 4 # load appropriate system call code into register $v0; # code for printing string is 4
打印字符串, 賦值對應的操做代碼 $v0 = 4 la $a0, string1 # load address of string to be printed into $a0
將要打印的字符串地址賦值 $a0 = address(string1) syscall # call operating system to perform print operation
e.g. To indicate end of program, use exit system call; thus last lines of program should be:
執行到這裏, 程序結束, 立馬走人, 管他後邊洪水滔天~~ li $v0, 10 # system call code for exit = 10 syscall # call operating sys
-------------------------------------------------我是那個分呀分呀分割線--------------------------------------------------------------------------
OK, 十分輕鬆又愉快的MIPS入門之旅到此告一段落, 下面我把用到的一些軟件和這篇文章的原文連接貼到下邊,有須要的, 各位客官自取哈~~~
1.Mars4.4
2.PCSpim Simulator
3.《MIPS Qucik Tutorial》你都看到這裏了, 難道還怕點那麼一個贊麼~~~~~