简介
咱们知道x86架构cpu用于PC端和工作站较多,ARM架构cpu常见于手机和单片机,那么MIPS架构的cpu主要在哪些设备可以找到它们的身影呢?
- 中国龙芯
- PS游戏机
学习环境搭建
- 安装JDK, 主要用于运行mips模拟器mars
- MARS模拟器:https://courses.missouristate.edu/KenVollmar/mars/download.htm
寄存器
在mips中通用寄存器用$开头表示,一共有32个
寄存器编号 | 寄存器名 | 寄存器用途 |
---|---|---|
$0 | $zero | 永远返回0 |
$1 | $at | 保留寄存器 |
$2-$3 | $v0-$v1 | 一般用于存储表达式或者函数的返回值(value的简写) |
$4-$7 | $a0-$a3 | 参数寄存器(Argument简写) |
$8-$15 | $t0-$t7 | 一般用于存储临时变量(temp简写) |
$16-$23 | $s0-$s7 | 存放子函数调用过程需要被保留的数据(saved values) |
$24-$25 | $t8-$t9 | 属性同$t0-$t7 |
$26-$27 | $k0-$k1 | 一般存储中断函数返回值 |
$28 | $gp | GlobalPointer简写 |
$29 | $sp | 栈指针,指向栈顶(Stack Pointer简写) |
$30 | $s8/$fp | (Save / Frame Pointer)帧指针 |
$31 | $ra | 一般用于存储函数返回地址(return address简写) |
寄存器编号和别名一一对应,同一个寄存器可以有两种不同表示方法:$0或者$zero
- program counter (PC) 无法直接修改,通过跳转指令可以改动
- HI 和 LO :这两个寄存器特别用来保存乘法、除法、乘法累加的结果。
MIPS汇编中的分段处理
1 | .data #数据段 |
传送指令
- 加载立即数指令
li
li
(load immediate) :用于将立即数传送给寄存器
1 | li $t0,1 ;十六进制数据使用0x前缀表示 |
- 加载地址指令
la
la
(load address) :用于将地址传送至寄存器中, 多用于通过地址获取数据段中的地址
1 | .data |
- 寄存器数据传送指令
move
用于将一个寄存器中的数据传送至另一个寄存器当中
1 | move $t0,$t1 # 将寄存器$t1中的数据传送至$t0 |
系统服务指令 syscall
在C语言中输出文本可以使用printf
函数,但是汇编中没有printf这么一说,如果想要输出文本,需要借助syscall
指令
如果想要输出一个数字1,那么syscall
指令从$a0寄存器中取出需要输出的数据
因此, 你在执行syscall
指令之前需要将数据提前放入$a0
之中:
1 | li $a0,1 |
同时,还需要指定输出的数据类型,数据类型的指定保存在$v0寄存器中
1 | # $v0=1, syscall--->print_int |
$v0
存入1,表示syscall
将$a0
中的数据当做数字输出
$v0
存入4,表示syscall
将$a0
中的数据当做数据的地址,然后输出对应的数据
syscall指令读写对照表
Service | Code in $v0 | Arguments | Result |
---|---|---|---|
print integer | 1 | $a0 = integer to print | |
print float | 2 | $f12 = float to print | |
print double | 3 | $f12 = double to print | |
print string | 4 | $a0 = address of null-terminated string to print | |
read integer | 5 | $v0 contains integer read | |
read float | 6 | $f0 contains float read | |
read double | 7 | $f0 contains double read | |
read string | 8 | $a0 = address of input buffer $a1 = maximum number of characters to read | See note below table |
sbrk (allocate heap memory) | 9 | $a0 = number of bytes to allocate | $v0 contains address of allocated memory |
exit (terminate execution) | 10 | ||
print character | 11 | $a0 = character to print | See note below table |
read character | 12 | $v0 contains character read | |
open file | 13 | $a0 = address of null-terminated string containing filename $a1 = flags $a2 = mode | $v0 contains file descriptor (negative if error). See note below table |
read from file | 14 | $a0 = file descriptor $a1 = address of input buffer $a2 = maximum number of characters to read | $v0 contains number of characters read (0 if end-of-file, negative if error). See note below table |
write to file | 15 | $a0 = file descriptor $a1 = address of output buffer $a2 = number of characters to write | $v0 contains number of characters written (negative if error). See note below table |
close file | 16 | $a0 = file descriptor | |
exit2 (terminate with value) | 17 | $a0 = termination result | See note below table |
Services 1 through 17 are compatible with the SPIM simulator, other than Open File (13) as described in the Notes below the table. Services 30 and higher are exclusive to MARS. | |||
time (system time) | 30 | $a0 = low order 32 bits of system time $a1 = high order 32 bits of system time. See note below table | |
MIDI out | 31 | $a0 = pitch (0-127) $a1 = duration in milliseconds $a2 = instrument (0-127) $a3 = volume (0-127) | Generate tone and return immediately. See note below table |
sleep | 32 | $a0 = the length of time to sleep in milliseconds. | Causes the MARS Java thread to sleep for (at least) the specified number of milliseconds. This timing will not be precise, as the Java implementation will add some overhead. |
MIDI out synchronous | 33 | $a0 = pitch (0-127) $a1 = duration in milliseconds $a2 = instrument (0-127) $a3 = volume (0-127) | Generate tone and return upon tone completion. See note below table |
print integer in hexadecimal | 34 | $a0 = integer to print | Displayed value is 8 hexadecimal digits, left-padding with zeroes if necessary. |
print integer in binary | 35 | $a0 = integer to print | Displayed value is 32 bits, left-padding with zeroes if necessary. |
print integer as unsigned | 36 | $a0 = integer to print | Displayed as unsigned decimal value. |
(not used) | 37-39 | ||
set seed | 40 | $a0 = i.d. of pseudorandom number generator (any int). $a1 = seed for corresponding pseudorandom number generator. | No values are returned. Sets the seed of the corresponding underlying Java pseudorandom number generator (java.util.Random ). See note below table |
random int | 41 | $a0 = i.d. of pseudorandom number generator (any int). | $a0 contains the next pseudorandom, uniformly distributed int value from this random number generator’s sequence. See note below table |
random int range | 42 | $a0 = i.d. of pseudorandom number generator (any int). $a1 = upper bound of range of returned values. | $a0 contains pseudorandom, uniformly distributed int value in the range 0 = [int] [upper bound], drawn from this random number generator’s sequence. See note below table |
random float | 43 | $a0 = i.d. of pseudorandom number generator (any int). | $f0 contains the next pseudorandom, uniformly distributed float value in the range 0.0 = f 1.0 from this random number generator’s sequence. See note below table |
random double | 44 | $a0 = i.d. of pseudorandom number generator (any int). | $f0 contains the next pseudorandom, uniformly distributed double value in the range 0.0 = f 1.0 from this random number generator’s sequence. See note below table |
(not used) | 45-49 | ||
ConfirmDialog | 50 | $a0 = address of null-terminated string that is the message to user | $a0 contains value of user-chosen option 0: Yes 1: No 2: Cancel |
InputDialogInt | 51 | $a0 = address of null-terminated string that is the message to user | $a0 contains int read $a1 contains status value 0: OK status -1: input data cannot be correctly parsed -2: Cancel was chosen -3: OK was chosen but no data had been input into field |
InputDialogFloat | 52 | $a0 = address of null-terminated string that is the message to user | $f0 contains float read $a1 contains status value 0: OK status -1: input data cannot be correctly parsed -2: Cancel was chosen -3: OK was chosen but no data had been input into field |
InputDialogDouble | 53 | $a0 = address of null-terminated string that is the message to user | $f0 contains double read $a1 contains status value 0: OK status -1: input data cannot be correctly parsed -2: Cancel was chosen -3: OK was chosen but no data had been input into field |
InputDialogString | 54 | $a0 = address of null-terminated string that is the message to user $a1 = address of input buffer $a2 = maximum number of characters to read | See Service 8 note below table $a1 contains status value 0: OK status. Buffer contains the input string. -2: Cancel was chosen. No change to buffer. -3: OK was chosen but no data had been input into field. No change to buffer. -4: length of the input string exceeded the specified maximum. Buffer contains the maximum allowable input string plus a terminating null. |
MessageDialog | 55 | $a0 = address of null-terminated string that is the message to user $a1 = the type of message to be displayed: 0: error message, indicated by Error icon 1: information message, indicated by Information icon 2: warning message, indicated by Warning icon 3: question message, indicated by Question icon other: plain message (no icon displayed) | N/A |
MessageDialogInt | 56 | $a0 = address of null-terminated string that is an information-type message to user $a1 = int value to display in string form after the first string | N/A |
MessageDialogFloat | 57 | $a0 = address of null-terminated string that is an information-type message to user $f12 = float value to display in string form after the first string | N/A |
MessageDialogDouble | 58 | $a0 = address of null-terminated string that is an information-type message to user $f12 = double value to display in string form after the first string | N/A |
MessageDialogString | 59 | $a0 = address of null-terminated string that is an information-type message to user $a1 = address of null-terminated string to display after the first string | N/A |
使用syscall指令输出helloworld示例:
1 | .data |
数据定义
定义整型数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# 打印Integer数据
.data
age: .word 23 #一个字长数据32位
.text
li $v0,1
lw $a0,age
syscall
#========================#
#加法运算
.data
number1: .word 2
number2: .word 5
.text
lw $t0,number1
lw $t1,number2
add $t2,$t0,$t1
li $v0,1
move $a0 $t2
syscall
# 乘法运算 使用mul定义Float数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#打印float数据
.data
PI: .float 3.14
.text
li $v0,2
lwc1 $f12,PI
syscall
#float数据 算数运算
.data
f1: .float 3.14
f2: .float 3.15
.text
li $v0,2
lwc1 $f1,f1
lwc1 $f2,f2
add.s $f3,$f2,$f1 #带.s后缀的指令皆为浮点单精度指令
mov.s $f12,$f3
syscall定义Double数据
1
2
3
4
5
6
7
8
9
10
11#打印double数据
.data
ayDouble: .double 7.20
zeroDouble: .double 0.0
.text
ldc1 $f2,ayDouble
ldc1 $f0,zeroDouble
li $v0,3
add.d $f12,$f2,$f0 #带.d后缀的指令皆为浮点双精度指令
syscall定义字符串数据
1
2
3
4
5
6
7#打印double数据
.data
msg: .ascii "hello world"
.text
li $v0,4
la $a0,msg
syscall
用户输入
字符串输入
1
2
3
4
5
6
7.data
userInput: .space 20 #声明一块空间 默认存放0
.text
li $v0,8
la $a0,userInput #将用户的输入存放至userInput中
li $a1,20 #限制用户输入, 一旦超过20个默认回车
syscall整型数据输入
1
2
3
4# 输入的结果系统会存放在$v0寄存器
.text
li $v0,5
syscall
浮点型数据输入
1
2
3
4#以float为例 输入的结果会存放在$f0寄存器
.text
li $v0,6
syscall
单精度和双精度
单精度数(float型)在32位计算机中存储占用4字节,也就是32位,有效位数为7位,小数点后6位。
双精度数(double型)在32位计算机中存储占用8字节,也就是64位,有效位数为16位,小数点后15位。
浮点寄存器
在mips中一共有32个浮点寄存器(其中包含16个双精度浮点寄存器),用于单独处理浮点数
函数声明和调用
函数声明
格式
1
2
3函数名:
函数体
jr $ra #$ra寄存器中保存着调用指令下一条代码所在的地址示例
1
2
3
4
5
6
7
8
9.data
.text
show:
li $v0,1
li $a0,3
syscall
jr $ra ;jump registers
函数调用
格式
1
jal 函数名
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.data
.text
jal show #调用函数时将下一条指令的地址存放至$ra寄存器
#结束程序
li $v0,10
syscall
show:
li $v0,1
li $a0,3
syscall
jr $ra
函数传参和返回值
在mips汇编中,函数的参数一般放在
$a
系列寄存器当中,最多对应4个参数,超过4个部分使用栈储存. 函数的变量和返回值一般放在$v
系列寄存器当中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#需求:定义加法函数 并调用获取返回值int sum(int v,int b)
main:
addi $a1,$zero,50
addi $a2,$zero,100
jal add
li $v0,1
move $a0,$v1
syscall
#结束程序
li $v0,10
syscall
add:
add $v1,$a1,$a2
jr $ra嵌套函数
由于每执行
jal
调用一次函数, 就会刷新$ra
寄存器中的值,因此,在嵌套函数调用之前,需要临时保存上一次$ra中的值,使用栈空间临时保护即可
栈操作
栈空间拉伸和平衡
1
2
3addi $sp,$sp,-4 #栈拉伸 拉伸4个字节空间
addi $sp,$sp,4 #栈平衡入栈和出栈
1
2
3sw $s0 ,0($sp) #入栈 往内存中写入数据
lw $s0, 0($sp) #出栈 从内存中读取数据嵌套函数使用栈保护
$ra
代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28main:
addi $a1,$zero,50
addi $a2,$zero,100
jal add
li $v0,1
move $a0,$v1
syscall
#结束程序
li $v0,10
syscall
add:
addi $sp,$sp,-4 #栈拉伸
sw $ra ,0($sp) #入栈
jal sub
add $v1,$a1,$v1
lw $ra, 0($sp) #出栈
addi $sp,$sp,4 #栈平衡
jr $ra
sub:
sub $v1,$a1,$a2
jr $ra
内存空间布局
从mars中可以查看到内存分布起始物理地址
转成图后:
栈的伸缩在mips和x86架构中是由高地址往低地址进行伸缩, 在arm架构中可升序也可降序
内存碎片
在内存动态分配(heap区)过程中容易出现一些小且不连续的空闲内存区域,这些未被使用的内存称作内存碎片
分类:
- 内部碎片:比如数据在内存中采用4个字节对齐的方式进行存储, 比如我们申请一块3个字节的空间用于存储一个数据,但是系统给我们分配了4个字节空间,这时多出来的一个字节的空间就被称之为内部碎片
- 外部碎片:在我们进行内存回收和分配的时候容易出现外部碎片,比如我连续申请了三块4个字节的内存空间,当我释放第二块内存空间然后紧接着申请一块8个字节的空间,此时由于之前释放的4个字节空间太小无法使用,这就造成了内存块空闲,这种碎片叫做外部碎片
PC 寄存器
称作 程序计数寄存器(Program Counter Register) :用于存储程序即将要执行的指令所对应在内存中的实际物理地址, 如果改变该值可以让指令跳转到我们想要跳转的地方
如何修改pc寄存器中的值
使用以下转移指令
jr
指令1
jr 寄存器 #$ra寄存器实际上就是保存着函数调用前的pc寄存器的值
jal
指令1
jal 标号 #跳转的同时给$ra寄存器赋值
j
指令1
j 标号 #直接跳转
内存数据的读写
从指定内存中读取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#整型数据
lw $t0,0x10010000 #读取4个字节数据 赋值给t0寄存器
ld $t0,0x10010000 #读取8个字节数据 赋值给t0和t1寄存器
#单精度浮点数据
lwc1 $f0,0x10010000
#双精度浮点数据
ldc1 $f0,0x10010000
#字符数据
### 由于字符数据是以ascii码16进制的形式存放到内存中,因此只能获取到ascii码值,该值属于整型数据,直接使用lw或者ld即可
lw $t0,0x10010000 #读取4个字节数据 赋值给t0寄存器
ld $t0,0x10010000 #读取8个字节数据 赋值给t0和t1寄存器从内存中读取数据的宽度取决于寄存器的大小,由于32位cpu寄存器最大存储32位数据,因此
lw $t0
表示一次性读取4个字节的数据到$t0
寄存器, 如果想要连续读取八个字节的数据,那么需要使用ld $t0
,表示一次性读取8个字节的数据到$t0,$t1
连个连续的寄存器,往指定内存中写入数据
- 第一种 数据定义的同时指定物理地址
1
2
3
4
5
6.data 0x10010020 ;将以下定义的数据存放在0x10010020这个物理地址
.ascii "a"
.ascii "b"
.data 0x10010000
.ascii "c"- 第二种 在代码段中使用指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#整型数据
li $s1,4
sw $s1,0x10010000 ;将$s1寄存器中的数据存入0x10010000这个物理地址
#单精度浮点数
.data
f1: .float 3.14
.text
lwc1 $f2,f1
swc1 $f2,0x10010000
#双精度浮点数
.data
d1: .double 3.14
.text
ldc1 $f2,d1
sdc1 $f2,0x10010000
以上直接使用的是简单粗暴的十六进制表示物理地址,很多时候内存的地址会保存在寄存器中,你可能会看到以下写法:
1 | lw $s1, $s2 |
注意: 往指定内存中读取写入数据时,代码段不允许直接写入和读取
一维数组的定义
数组本质上就是多个数据的集合,在内存中按照一定顺序排列,角标即为每个数据的偏移值,在mips中内存数据是按照4个字节进行对齐的,也就是说一个数据最少占用4个字节内存空间,因此数组中数据之间的偏移量固定为n*4
1 | .data |
数组的打印
1 | .data |
快速初始化数组数据的方法
1 | .data |
分支跳转指令
- 整型数据分支比较跳转
bgt
(branch if greater than):用于大于比较
1 | bgt $t0,$t1,sub # 如果$t0中的数据大于$t1,则跳转到sub分支,执行sub中的代码,否则,按照顺序执行bgt下面的代码, sub是一个代号,可以自定义 |
beq
(branch equal):用于等于比较
1 | beq $t0,$t1,sub # 如果$t0中的数据等于$t1,则跳转到sub分支,执行sub中的代码,否则,按照顺序执行bgt下面的代码, sub是一个代号,可以自定义 |
ble
(branch if less than):用于小于比较
1 | ble $t0,$t1,sub # 如果$t0中的数据小于$t1,则跳转到sub分支,执行sub中的代码,否则,按照顺序执行bgt下面的代码, sub是一个代号,可以自定义 |
练习1: 将以下c代码转换成mips汇编代码:
1 | scanf("%d",$a); |
汇编代码:
1 | # 用$t0指代a ,$t1指代b |
练习2: 将以下c代码转换成mips汇编代码:
1 | //求累加之和 |
汇编代码:
1 | # 用$t0指代i ,$t1指代s |
- 浮点型数据分支比较
小于
1
2
3
4
5
6
7
8
9
10
11
12
13
14.data
num1: .float 3.14
num2: .float 3.16
.text
lwc1 $f0,num1
lwc1 $f1,num2
c.lt.s $f0,$f1 #关键代码
bc1t sub #bc1t表示条件满足 bc1f表示不满足条件
sub:等于
1
2
3
4
5
6
7
8
9
10
11
12
13
14.data
num1: .float 3.14
num2: .float 3.16
.text
lwc1 $f0,num1
lwc1 $f1,num2
c.eq.s $f0,$f1 #关键代码 c表示coproc协处理寄存器 s表示single单精度
bc1t sub #bc1t表示条件满足 bc1f表示不满足条件
sub:小于等于
1
2
3
4
5
6
7
8
9
10
11
12
13
14.data
num1: .float 3.14
num2: .float 3.16
.text
lwc1 $f0,num1
lwc1 $f1,num2
c.le.s $f0,$f1 #关键代码
bc1t sub #bc1t表示条件满足 bc1f表示不满足条件
sub:以上是单精度浮点数据的比较示例,如果是双精度,只需将结尾
.s
改成.d
即可
mips多文件开发
在文件A中定义函数
1 | fun: |
在文件B中使用关键字.include
引用A文件中的函数
1 | .text |
所有文件必须在同一目录下
宏
宏替换
全局替换,使用我们之前学过的
.include
伪指令进行替换宏匹配
在汇编中,如果我们要依次打印1,2,3三个整数,那么汇编如下:
1 | print1: |
我们发现使用标签的方式定义函数,当函数体内容存在不确定变量值时,代码非常冗余, 如果使用高级语言进行封装的话,我们一般一个函数就搞定了:
1 | void print(int a){ |
有没有办法使得汇编能像高级语言一样简洁呢?
在MARS中给我们提供了一个扩展伪指令,叫做宏匹配
宏匹配使用的格式如下:
1 | .macro 别名 |
示例:
1 | li $v0,10 |
如果我们要封装一个打印整型数据的函数,那么我们可以:
1 | #封装结果为 |
这样是不是和高级语言没什么区别啦
打印字符串封装示例:
1 | .macro print_str (%str) |
然后结合我们之前学过的多文件开发,完全可以将这个封装好的函数单独放在一个文件中,直接在头部.include
就行
宏定义
全局定义,如果我们想给一个数据或者寄存器,甚至是一行代码取个别名,然后在代码中使用别名的方式指代,那么可以使用宏定义指令
.eqv
别名的好处就是方便我们进行记忆1
2
3.eqv LIMIT 20 #给20这个立即数取个别名为LIMIT
.eqv CTR $t2
.eqv CLEAR_CTR add CTR, $zero, 0当我们有以下代码:
1
2
3
4.text
li $v0,1
add $t2, $zero, 0
li $t0,20如果我们使用宏定义,我们可以写成如下:
1
2
3
4
5
6
7
8.eqv LIMIT 20 #给20这个立即数取个别名为LIMIT
.eqv CTR $t2
.eqv CLEAR_CTR add CTR, $zero, 0
.text
li $v0,1
CLEAR_CTR
li $t0,LIMIT
注:宏定义和宏匹配必须先定义后使用,也就是说定义的代码需要放在前头
二维数组的定义
二维数组其实就类似于我们数学中的二维坐标系,我们如果要定位一个点的话可以使用(x,y)来表示,在计算机的世界里,二维中所有的点都按照顺序依次存放在内存当中
假设我们将第一维当做行,第二维当做列,那么排列的方式有以下两种:
第一种是 行不动,列轮动
内存地址 | 二维坐标 |
---|---|
0x00000020 | arr[2][2] |
0x0000001C | arr[2][1] |
0x00000018 | arr[2][0] |
0x00000014 | arr[1][2] |
0x00000010 | arr[1][1] |
0x0000000C | arr[1][0] |
0x00000008 | arr[0][2] |
0x00000004 | arr[0][1] |
0x00000000 | arr[0][0] |
这种方式获取实际地址的公式为:
1 | addr=baseAddr+(rowIndex*colSize+colIndex)*dataSize |
第二种是 列不动,行轮动
内存地址 | 二维坐标 |
---|---|
0x00000020 | arr[2][2] |
0x0000001C | arr[1][2] |
0x00000018 | arr[0][2] |
0x00000014 | arr[2][1] |
0x00000010 | arr[1][1] |
0x0000000C | arr[0][1] |
0x00000008 | arr[2][0] |
0x00000004 | arr[1][0] |
0x00000000 | arr[0][0] |
这种方式获取实际地址的公式为:
1 | addr=baseAddr+(colIndex*rowSize+rowIndex)*dataSize |
使用mips汇编实现二维数组定义
1 | #需求:实现int a[3][3] = {{1, 2, 3}, {5, 6, 7}, { 10, 11, 12}}; |
列不不动 行轮动方式:
1 | #需求:实现int a[3][3] = {{1, 2, 3}, {5, 6, 7}, { 10, 11, 12}}; |
更为简便的方法实现二维数组的搭建
由于数组中数据是在内存中连续进行排列存储的,那么我们可以之间将数据 依次存入内存之中,然后使用算法进行数据获取即可(以下示例皆采用 行不动,列动 的方式)
1 | #需求:实现int a[3][3] = {{1, 2, 3}, {5, 6, 7}, { 10, 11, 12}}; |
再简化一下:
1 | #需求:实现int a[3][3] = {{1, 2, 3}, {5, 6, 7}, { 10, 11, 12}}; |
按照正常的编程思维,我们一般使用第一种行不动 列动的存储方式 第一维为行,第二维为列,如果你想改成行动存储方式,有两种方法:要么将数据的存储顺序进行变动,配合第二种算法,要么将第二维当成行,第一维当成列,配合第二种算法进行处理
Mips汇编指令汇总表
类别 | 指令名称 | 实例 | 含义 | 注释 | 英文注解 |
算 数 | 加法 | add $s1, $s2, $s3 | $s1 = $s2 + $s3 | 三个寄存器操作数 | addition 加法 |
减法 | sub $s1, $s2, $s3 | $s1 = $s2 - $s3 | 三个寄存器操作数 | subtraction 减法 | |
立即数加法 | addi $s1, $s2, 20 | $s1 = $s2 + 20 | 用于加常数数据 | add immediate 立即加法 | |
数 据 传 输 | 取字 | lw $s1, 20 ($s2) | $s1 = Memory[$s2 + 20] | 将一个字从内存中取到寄存器中 | load word 加载字 |
存字 | sw $s1, 20 ($s2) | Memory[$s2 + 20] = $s1 | 将一个字从寄存器中取到内存中 | store word 存储字 | |
取半字 | lh $s1, 20 ($s2) | $s1 = Memory[$s2 + 20] | 将半个字从内存中取到寄存器中 | load halfword 加载半字 | |
取无符号半字 | lhu $s1, 20 ($s2) | $s1 = Memory[$s2 + 20] | 将半个字从内存中取到寄存器中 | load halfword unsigned | |
存半字 | sh $s1, 20 ($s2) | Memory[$s2 + 20] = $s1 | 将半个字从寄存器中取到内存中 | stroe halfword 存储半字 | |
取字节 | lb $s1, 20 ($s2) | $s1 = Memory[$s2 + 20] | 将一字节从内存中取到寄存器中 | load byte | |
取无符号字节 | lbu $s1, 20 ($s2) | $s1 = Memory[$s2 + 20] | 将一字节从内存中取到寄存器中 | load byte unsigned | |
存字节 | sb $s1, 20 ($s2) | Memory[$s2 + 20] = $s1 | 将一字节从寄存器中取到内存中 | store byte | |
取链接字 | ll $s1, 20 ($s2) | $s1 = Memory[$s2 + 20] | 取字作为原子交换的前半部 | load linked | |
存条件字 | sc $s1, 20 ($s2) | Memory[$s2 + 20] = $s1; $s1 = 0 or 1 | 存字作为原子交换的后半部分 | store conditional | |
取立即数的高位 | lui $s1, 20 | $s1 = 20 * 216 | 取立即数并放到高16位 | load upper immediate | |
逻 辑 | 与 | and $s1, $s2, $s3 | $s1 = $s2 & $s3 | 三个寄存器操作数按位与 | and |
或 | or $s1, $s2, $s3 | $s1 = $s2 | $s3 | 三个寄存器操作数按位或 | or | |
或非 | nor $s1, $s2, $s3 | $s1 = ~ ($s2 | $s3) | 三个寄存器操作数按位或非 | not or | |
立即数与 | andi $s1, $s2, 20 | $s1 = $s2 & 20 | 和常数按位与 | and immediate | |
立即数或 | ori $s1, $s2, 20 | $s1 = $s2 | 20 | 和常数按位或 | or immediate | |
逻辑左移 | sll $s1, $s2, 10 | $s1 = $s2 << 20 | 根据常数左移相应位 | set left logical | |
逻辑右移 | srl $s1, $s2, 10 | $s1 = $s2 >> 20 | 根据常数右移相应位 | set right logical | |
条 件 分 支 | 相等时跳转 | beq $s1, $s2, 25 | if ($s1 == $s2) go to PC + 4 + 25 * 4 | 相等检测: 和PC相关的跳转 | branch on equal |
不相等时跳转 | bne $s1, $s2, 25 | if ($s1 != $s2) go to PC + 4 + 25 * 4 | 不相等检测: 和PC相关的跳转 | branch on not equal | |
小于时跳转 | slt $1, $s2, $3 | if ($s2 < $s3) $s1 = 1; else $s1 = 0 | 比较是否小于 | set less than | |
无符号数比较小时置位 | sltu $1, $s2, $3 | if ($s2 < $s3) $s1 = 1; else $s1 = 0 | 比较是否小于无符号数 | set less than unsigned | |
无符号数小于立即数时置位 | slti $1, $s2, 20 | if ($s2 < 20) $s1 = 1; else $s1 = 0 | 比较是否小于常数 | set less than immediate | |
无符号数比较小于无符号立即数时置位 | sltiu $1, $s2, 20 | if ($s2 < 20) $s1 = 1; else $s1 = 0 | 比较是否小于无符号常数 | set less than immediate unsigned | |
无 条 件 跳 转 | 跳转 | j 2500 | go to 2500 * 4 | 跳转到目标地址 | jump |
跳转至寄存器所指位置 | jr $ra | go to $ra | 用于switch语句,以及过程调用 | jump register | |
跳转并链接 | jal 2500 | $ra = PC + 4; go to 2500 * 4; | 用于过程调用(方法) 正常的执行流程执行完A自然要执行B指令,现在要跳转执行C方法,这时就把B地址存入寄存器中,执行完C后跳转到B | jump and link |
Mips内存结构图:
本文为作者原创 转载时请注明出处 谢谢
乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站