【十分鐘教會你彙編】MIPS編程入門(媽媽說標題要高大上,纔會有人看>_<!)

  無心中找到一篇十分好用,並且篇幅也不是很大的入門教程,通篇閱後,再把「栗子」敲一遍,基本能夠有一個比較理性的認識,從而方便更好地進一步深刻學習。html

廢話很少說,上乾貨(英語好的直接跳過本人的渣翻譯了哈——!純本人手打原創,有錯請指教,要轉載請聲明出處,謝~~):express

 

MIPS Architecture and Assembly Language Overview

MIPS架構及其彙編初步

 

(開始以前稍微再提下,總體分爲4個結構:)數組

  1: 寄存器種類;架構

  2: 算術及尋址指令app

  3: 程序結構函數

  4: 系統調用 學習

Data Types and Literals

數據類型

  • 全部MIPS指令都是32位長的
  • 各單位:1字節=8位,半字長=2個字節,1字長=4個字節
  • 一個字符空間=1個字節
  • 一個整型=一個字長=4個字節
  • 單個字符用單引號,例如:'b'
  • 字符串用雙引號,例如:"A string"

Registers

寄存器

      • MIPS下一共有32個通用寄存器
      • 在彙編中,寄存器標誌由$符開頭
      • 寄存器表示能夠有兩種方式
      • 直接使用該寄存器對應的編號,例如:從$0到$31
      • 使用對應的寄存器名稱,例如:$t1, $sp(詳細含義,下文有表格
      對於乘法和除法分別有對應的兩個寄存器$lo, $hi
      • 對於以上兩者,不存在直接尋址;必需要經過mfhi("move from hi")以及mflo("move from lo")分別來進行訪問對應的內容
      • 棧的走向是從高地址到低地址
MIPS下各個寄存器編號及描述:

Register
Numberui

寄存器編號this

Alternative 
Name編碼

寄存器名

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.
Not preserved across procedure calls

Argument簡寫)存儲子程序的前4個參數,在子程序調用過程當中釋放

8-15
$t0 - $t7

(temporaries) Caller saved if needed. Subroutines can use w/out saving.
Not preserved across procedure calls

Temp簡寫)臨時變量,同上調用時不保存

16-23
$s0 - $s7

(saved values) - Callee saved. 
A subroutine using one of these must save original and restore it before exiting.
Preserved across procedure calls

Saved or Static簡寫?)靜態變量?調用時保存

24-25
$t8 - $t9

(temporaries) Caller saved if needed. Subroutines can use w/out saving.
These are in addition to $t0 - $t7 above.
Not preserved across procedure calls.

Temp簡寫)算是前面$0~$7的一個繼續,屬性同$t0~$t7

26-27
$k0 - $k1

reserved for use by the interrupt/trap handler

(breaK off簡寫?)中斷函數返回值,不可作其餘用途

28
$gp

global pointer. 
Points to the middle of the 64K block of memory in the static data segment.

Global Pointer簡寫)指向64k(2^16)大小的靜態數據塊的中間地址(字面上好像就是這個意思,塊的中間)

29
$sp

stack pointer 
Points to last location on the stack.

(Stack Pointer簡寫)棧指針,指向的是棧頂

30
$s8/$fp

saved value / frame pointer
Preserved across procedure calls

(Saved/Frame Pointer簡寫)幀指針

31
$ra

return address

返回地址,目測也是不可作其餘用途

 

Program Structure

程序結構

  • 本質其實就只是數據聲明+普通文本+程序編碼(文件後綴爲.s,或者.asm也行)
  • 數據聲明在代碼段以後(其實在其以前也沒啥問題,也更符合高級程序設計的習慣)

Data Declarations

數據聲明

  • 數據段以 .data爲開始標誌
  • 聲明變量後,即在主存中分配空間。

Code

代碼

  • 代碼段以 .text爲開始標誌
  • 其實就是各項指令操做
  • 程序入口爲main:標誌(這個都同樣啦)
  • 程序結束標誌(詳見下文)

Comments

註釋

  • 同C系語言
      • MIPS程序的基本模板以下:
        # 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
        # 必須多給你一行,你才歡?

  

Data Declarations

數據聲明

format for declarations:

聲明的格式:

name:	                storage_type	value(s)	
變量名:(冒號別少了) 數據類型 變量值
    • create storage for variable of specified type with given name and specified value
    • value(s) usually gives initial value(s); for storage type .space, gives number of spaces to be allocated
    • 一般給變量賦一個初始值;對於.space,須要指明須要多少大小空間(bytes)

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 / Store Instructions

加載/保存(也許這裏寫成讀取/寫入 可能更易理解一點) 指令集

  • 若是要訪問內存,很差意思,你只能用 load 或者 store 指令
  • 其餘的只能都一概是寄存器操做

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

 


 

Indirect and Based Addressing

當即與間接尋址

load address:

直接給地址

	la	$t0, var1
  • copy RAM address of var1 (presumably a label defined in the program) into register $t0

indirect addressing:

地址是寄存器的內容(能夠理解爲指針)

	lw	$t2, ($t0)
  • load word at RAM address contained in $t0 into $t2
	sw	$t2, ($t0)
  • store word in register $t2 into RAM at address contained in $t0

based or indexed addressing:

+偏移量

	lw	$t2, 4($t0)
  • load word at RAM address ($t0+4) into register $t2
  • "4" gives offset from address in register $t0
	sw	$t2, -12($t0)
  • store word in register $t2 into RAM at address ($t0 - 12)
  • negative offsets are fine

Note: based addressing is especially useful for:

沒必要多說,要用到偏移量的尋址,基本上使用最多的場景無非兩種:數組,棧。

  • arrays; access elements as offset from base address
  • stacks; easy to access elements at offset from stack pointer or frame pointer

 

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

 


 

Arithmetic Instructions

算術指令集

  • 最多3個操做數
  • 再說一遍,在這裏,操做數只能是寄存器,絕對不容許出現地址
  • 全部指令統一是32位 = 4 * 8 bit = 4bytes = 1 word

  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

 


 

Control Structures

控制流

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 只有一個, (哥哥我分身乏術啊~~)。

 


 

System Calls and I/O (SPIM Simulator)

  系統調用 與 輸入/輸出(主要針對SPIM模擬器)

 (本人使用的是Mars 4.4,也通用--!)

 

  • 經過系統調用實現終端的輸入輸出,以及聲明程序結束
  • 學會使用 syscall
  • 參數所使用的寄存器:$v0, $a0,  $a1
  • 返回值使用: $v0

下表給出了系統調用中對應功能,代碼,參數機返回值

Service

Code
in $v0

對應功能的調用碼

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 = length of string buffer (n)

將讀取的字符串長度賦值給 $a1

 

sbrk

應該同C中的sbrk()函數

動態分配內存

$v0 = 9

$a0 = amount

須要分配的空間大小(單位目測是字節 bytes)

address in $v0

將分配好的空間首地址給 $v0

exit

退出

$v0 =10
 你懂得  
    • 大概意思是要打印的字符串應該有一個終止符,估計相似C中的'\0', 在這裏咱們只要聲明字符串爲 .asciiz 類型便可。下面給個我用Mars4.4的提示:
    • .ascii 與 .asciiz惟一區別就是 後者會在字符串最後自動加上一個終止符, 僅此而已
    • The read_int, read_float and read_double services read an entire line of input up to and including the newline character.
    • 對於讀取整型, 浮點型,雙精度的數據操做, 系統會讀取一整行,(也就是說以換行符爲標誌 '\n')
    • The read_string service has the same semantices as the UNIX library routine fgets.
      • It reads up to n-1 characters into a buffer and terminates the string with a null character.
      • If fewer than n-1 characters are in the current line, it reads up to and including the newline and terminates the string with a null character.
      • 這個很少說了,反正就是輸入過長就截取,太短就這樣,最後都要加一個終止符。
    • The sbrk service returns the address to a block of memory containing n additional bytes. This would be used for dynamic memory allocation.
    • 上邊的表裏已經說得很清楚了。
    • The exit service stops a program from running.
    • 你懂得。。。
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》

你都看到這裏了, 難道還怕點那麼一個贊麼~~~~~  
相關文章
相關標籤/搜索