转载:http://www.cnblogs.com/thoupin/p/4018455.html?utm_source=tuicool
无意中找到一篇十分好用,而且篇幅也不是很大的入门教程,通篇阅后,再把“栗子”敲一遍,基本可以有一个比较理性的认识,从而方便更好地进一步深入学习。
废话不多说,上干货(英语好的直接跳过本人的渣翻译了哈——!纯本人手打原创,有错请指教,要转载请声明出处,谢~~):
MIPS Architecture and Assembly Language Overview
MIPS架构及其汇编初步
(开始之前稍微再提下,整体分为4个结构:)
1: 寄存器种类;
2: 算术及寻址指令
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")分别来进行访问对应的内容
- 栈的走向是从高地址到低地址
Register Number 寄存器编号 | 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. (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. 函数调用时必须保存,调用完成后需要恢复 |
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 (kernel简写)中断函数返回值,不可做其他用途 |
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 返回地址,目测也是不可做其他用途 |
Program Structure 程序结构
本质其实就只是数据声明+普通文本+程序编码(文件后缀为.s,或者.asm也行) 数据声明在代码段之后(其实在其之前也没啥问题,也更符合高级程序设计的习惯)Data Declarations 数据声明
- 数据段以 .data为开始标志
- 声明变量后,即在主存中分配空间。
Code 代码
- 代码段以 .text为开始标志
- 其实就是各项指令操作
- 程序入口为main:标志(这个都一样啦)
- 程序结束标志(详见下文)
Comments 注释
- 同C系语言
# 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,指明保留空间大小(字节)
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:
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 #var1一般为定义的Label,将标记var1的地址存入t0寄存器中
indirect addressing: 间接寻址
寻址格式为 lw/sw 寄存器 偏移(基址寄存器)
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
.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
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系列) b target #PC=target
bal target #RA=PC+8; PC=target
beq $t0,$t1,target #if t0==t1 then PC=target
beqz $t0, target #if t0==0 then PC=target
bgez $t0, target #if t0>=0 then PC=target
bgezal $t0, target #RA=PC+8; if t0>=0 then PC=target
bgtz $t0, target #if t0>0 then PC=target
blez $t0, target #if t0<=0 then PC=target
bltz $t0, target #if t0<0 then PC=target
bltzal $t0, target #RA=PC+8 if t0<0 then PC=target
bne $t0,$t1,target #if t0!=t1 then PC=target
Jumps 跳转(while, for, goto系列)
j target # PC=PC[31:28]::target[27:0]
jr $t3 # PC=t3
Subroutine Calls子程序调用
jal sub_label # RA=PC+8 ; PC=PC[31:28]::sub_label[27:0]
jalr $t3 # RA=PC+8; PC=t3
注意上述跳转指令,之遥带有AL(And Link)都会更新RA。
如果说调用的子程序中有调用了其他子程序,嵌套调用, 则需要将$ra保存到栈内,用于以后恢复$ra, 毕竟 $ra 只有一个
System Calls and I/O (SPIM Simulator)系统调用 与 输入/输出(主要针对SPIM模拟器)
- 通过系统调用实现终端的输入输出,以及声明程序结束
- 学会使用 syscall
- 参数所使用的寄存器:$v0, $a0, $a1
- 返回值使用: $v0
下表给出了系统调用中对应功能,代码,参数和返回值
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
| 你懂得![]() |
- 大概意思是要打印的字符串应该有一个终止符,估计类似C中的'\0', 在这里我们只要声明字符串为 .asciiz 类型即可。
- .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.
- 你懂得。。。
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
读取整数
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) 存入 内存中
读取字符串
.data
dataarray: .space 40
.text
li $v0, 8
syscall
mov $t0, $a0
la $t1, dataarray
1: lw $t2, $t0
sw $t2, 0($t2)
sub $a1, $a1, 4
bnez $a1 , 1b
nop
打印字符串
.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
结束程序
结束程序
li $v0, 10 # system call code for exit = 10
syscall # call operating sys
1.Mars4.4 2.PCSpim Simulator 3.《MIPS Qucik Tutorial》
除了上述基础,还需要注意编译相关的伪指令。例如:.set mips32 .set noreorder