【十分钟教会你汇编】MIPS编程入门(妈妈说标题要高大上,才会有人看>_<!)
来源:互联网 发布:极点五笔linux版 编辑:程序博客网 时间:2024/05/17 01:19
转载: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
寄存器用途
the value 0
永远返回零
(assembler temporary) reserved by the assembler
汇编保留寄存器(不可做其他用途)
(values) from expression evaluation and function results
(Value简写)存储表达式或者是函数的返回值
(arguments) First four parameters for subroutine.
Not preserved across procedure calls
(Argument简写)存储子程序的前4个参数,在子程序调用过程中释放
(temporaries) Caller saved if needed. Subroutines can use w/out saving.
Not preserved across procedure calls
(Temp简写)临时变量,同上调用时不保存
(saved values) - Callee saved.
A subroutine using one of these must save original and restore it before exiting.
Preserved across procedure calls
函数调用时必须保存,调用完成后需要恢复
(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
reserved for use by the interrupt/trap handler
(kernel简写)中断函数返回值,不可做其他用途
global pointer.
Points to the middle of the 64K block of memory in the static data segment.
(Global Pointer简写)指向64k(2^16)大小的静态数据块的中间地址(字面上好像就是这个意思,块的中间)
stack pointer
Points to last location on the stack.
(Stack Pointer简写)栈指针,指向的是栈顶
saved value / frame pointer
Preserved across procedure calls
(Saved/Frame Pointer简写)帧指针
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_typevalue(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,指明保留空间大小(字节)
examplevar1:.word3# create a single integer variable with initial value 3 # 声明一个 word 类型的变量 var1, 同时给其赋值为 3array1:.byte'a','b'# create a 2-element character array with elements initialized# to a and b # 声明一个存储2个字符的数组 array1,并赋值 'a', 'b'array2:.space40# 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:lwregister_destination, RAM_source #copy word (4 bytes) at source RAM location to destination register. #从内存中 复制 RAM_source 的内容到 对应的寄存器中(lw中的'w'意为'word',即该数据大小为4个字节)lbregister_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 bytestore:swregister_source, RAM_destination #store word in source register into RAM destination #将指定寄存器中的数据 写入 到指定的内存中sbregister_source, RAM_destination #store byte (low-order) in source register into RAM destinationload immediate:liregister_destination, value #load immediate value into destination register 顾名思义,这里的 li 意为 load immediate
example:.datavar1:.word23# 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
.dataarray1:.space12# 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 = 5li $t1, 13# $t1 = 13sw $t1, 4($t0)# second array element set to 13 # 对于 数组第二个元素赋值 array[1] = $1 = 13 # (该数组中每个元素地址相距长度就是自身数据类型长度,即4字节, 所以对于array+4就是array[1])li $t1, -7# $t1 = -7sw $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 Ð $t4addi$t2,$t3, 5# $t2 = $t3 + 5; "add immediate" (no sub immediate)addu$t1,$t6,$t7# $t1 = $t6 + $t7; add as unsigned integerssubu$t1,$t6,$t7# $t1 = $t6 + $t7; subtract as unsigned integersmult$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, 余数存放在 himfhi$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 quotientmove$t2,$t3# $t2 = $t3
Control Structures 控制流Branches
分支(if else系列)btarget #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系列)
jtarget # PC=PC[31:28]::target[27:0] jr$t3 # PC=t3
Subroutine Calls子程序调用
jalsub_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
下表给出了系统调用中对应功能,代码,参数和返回值
Code
in $v0
对应功能的调用码
Arguments
所需参数
Results
返回值
print_int
打印一个整型
$a0 = integer to be printed
将要打印的整型赋值给 $a0
print_float
打印一个浮点
$f12 = float to be printed
将要打印的浮点赋值给 $f12
print_double
打印双精度
$f12 = double to be printed
将要打印的双精度赋值给 $f12
print_string$a0 = address of string in memory
将要打印的字符串的地址赋值给 $a0
read_intinteger returned in $v0
将读取的整型赋值给 $v0
read_float
读取浮点
float returned in $v0
将读取的浮点赋值给 $v0
read_double
读取双精度
double returned in $v0
将读取的双精度赋值给 $v0
read_string
读取字符串
$a0 = memory address of string input buffer
将读取的字符串地址赋值给 $a0
$a1 = length of string buffer (n)
将读取的字符串长度赋值给 $a1
sbrk
应该同C中的sbrk()函数
动态分配内存
$a0 = amount
需要分配的空间大小(单位目测是字节 bytes)
address in $v0
将分配好的空间首地址给 $v0
exit
退出
- 大概意思是要打印的字符串应该有一个终止符,估计类似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 的值已经变成了 输入的 5sw$v0, int_value# value read from keyboard returned in register $v0;# store this in desired location 通过写入(store_word)指令 将 $v0的值(5) 存入 内存中
读取字符串
.datadataarray: .space 40 .text li $v0, 8 syscall mov $t0, $a0 la $t1, dataarray1: lw $t2, $t0 sw $t2, 0($t2) sub $a1, $a1, 4 bnez $a1 , 1b nop
打印字符串
.datastring1.asciiz"Print this.\n"# declaration for string variable, # .asciiz directive makes string null terminated.textmain:li$v0, 4# load appropriate system call code into register $v0;# code for printing string is 4 打印字符串, 赋值对应的操作代码 $v0 = 4la$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 = 10syscall# call operating sys
1.Mars4.42.PCSpim Simulator3.《MIPS Qucik Tutorial》
除了上述基础,还需要注意编译相关的伪指令。例如:.set mips32 .set noreorder
- 【十分钟教会你汇编】MIPS编程入门(妈妈说标题要高大上,才会有人看>_<!)
- 只有十句话,有人却看了十分钟
- 3分钟教会你如何看eclipse中的崩溃信息
- 三分钟教会你唱大长今韩文版!! (转)
- 10分钟教会你Mac上搭建Web服务器
- 10分钟教会你简单的matlab和C/C++混合编程(mex文件)
- 有人说寂寞了才会写日记,其实写博客是不是也一样呢?
- 关于Matlab的MEX技术 10分钟教会你简单的matlab和C/C++混合编程(mex文件)
- 如果有人问你数据库的原理,叫他看这篇文章(上)
- 如果有人让你推荐编程技术书,请叫他看这个列表(转)
- 10分钟教会你Apache Shiro
- 10分钟教会你Apache Shiro
- 10分钟教会你Apache Shiro
- 10分钟教会你Apache Shiro
- 10分钟教会你Apache Shiro
- 10分钟教会你Apache Shiro
- 10分钟教会你Apache Shiro
- 分分钟教会你使用Lambda表达式
- Android 延时操作
- matlab查找一行数组除0以外的最小数
- 关于SAP条件表
- 什么是Provisioning Profile
- php---curl
- 【十分钟教会你汇编】MIPS编程入门(妈妈说标题要高大上,才会有人看>_<!)
- Halcon学习 资料归总
- Hadoop 2.6 远程开发环境搭建
- sdwebimage 新版
- css-元素水平、竖直居中
- SVN 总 结 一
- hdu 5234 Happy birthday
- 数据结构之图
- 448A - Rewards