MIPS(Microprocessor without Interlocked Pipeline Stages)是一种精简指令集计算机(RISC)架构,由MIPS计算机系统(现在是MIPS Technologies)开发。它以其简单性和效率而闻名,特别适用于嵌入式系统、网络设备和其他专业应用。
在MIPS汇编语言中,.data
和 .text
是两种伪指令,用于指示汇编器如何处理代码的不同部分。
-
.data:
.data
伪指令用于声明数据段(data segment),即用于存储程序中静态数据的部分。- 在
.data
部分,程序员可以定义各种数据,如变量、数组、字符串等。 - 数据定义通常使用
.word
、.byte
等指令,以指定数据的类型和初始值。 - 在程序执行时,
.data
部分的数据会被加载到内存中的数据段,供程序使用。
-
.text:
.text
伪指令用于声明代码段(text segment),即存储程序执行指令的部分。- 在
.text
部分,程序员编写程序的指令序列,定义程序的逻辑和功能。 - 代码段通常包括各种指令,如算术运算、逻辑运算、条件分支、函数调用等。
- 在程序执行时,CPU会从代码段中逐条读取指令,并执行相应的
一、常见指令
当谈及MIPS指令集架构时,可以将指令分为三种主要类型:R型指令(Register)、I型指令(Immediate)、J型指令(Jump)。每种类型的指令都有不同的功能和用途。下面简要介绍一下每种类型的指令及其常见的指令:
1.R型指令(寄存器型指令):
这种指令类型主要用于在寄存器之间执行算术运算、逻辑运算和数据移动操作。
类型
- add:将两个寄存器中的值相加并将结果存储在目标寄存器中。
- sub:将一个寄存器中的值减去另一个寄存器中的值,并将结果存储在目标寄存器中。
- and:对两个寄存器中的值执行按位与操作,并将结果存储在目标寄存器中。
- or:对两个寄存器中的值执行按位或操作,并将结果存储在目标寄存器中。
- slt:如果一个寄存器中的值小于另一个寄存器中的值,则将1存储在目标寄存器中;否则,将0存储在目标寄存器中。
格式
opcode
:操作码,对于R型指令,这个字段总是0。rs
:第一个源寄存器。rt
:第二个源寄存器。rd
:目标寄存器。shamt
:移位量,对于算术和逻辑运算,这个字段通常是0。funct
:功能码,决定具体的操作。-
R型指令的格式如下:
add 指令将两个寄存器中的值相加,并将结果存储在目标寄存器中,将寄存器$t1和$t2中的值相加,并将结果存储在寄存器$t0中。add rd, rs, rt
- 例子:
add $t0, $t1, $t2
- 二进制表示(假设$t0=$8,$t1=$9,$t2=$10):
2. I型指令(立即数型指令):
这种指令类型主要用于执行带有立即数(常数)的操作,例如加载和存储数据、条件分支等典型的I型指令包括:
类型
- lw:从内存中加载一个字(32位)数据到寄存器中。
- sw:将寄存器中的字(32位)数据存储到内存中。
- beq:如果两个寄存器中的值相等,则跳转到指定的目标地址。
- bne:如果两个寄存器中的值不相等,则跳转到指定的目标地址。
- addi:将一个寄存器中的值与一个立即数相加,并将结果存储在目标寄存器中。
格式
opcode
:操作码,决定指令的类型。rs
:源寄存器。rt
:目标寄存器。immediate
:立即数,用于算术运算或地址偏移。-
addi
指令:将寄存器rs
中的值与立即数相加,并将结果存储在目标寄存器rt
中。格式:addi rt, rs, immediate
二进制表示addi $t0, $t1, 10
(假设$t0=$8,$t1=$9):
3. J型指令(跳转型指令):
这种指令类型主要用于实现无条件跳转和跳转到子程序的操作。
类型
- j:无条件跳转到指定的目标地址。
- jal:跳转并将当前指令的地址存储在$ra寄存器中,通常用于函数调用。
格式
-
示例:
j
指令 - 功能:无条件跳转到指定的目标地址。
- 格式:
j address
- 例子:
j 0x00400020
- 解释:跳转到地址
0x00400020
处继续执行指令。
4. Function的定义与使用
- 函数定义:在MIPS汇编语言中,函数的定义通常包括两部分:函数的标签和函数的代码体。函数标签用于标识函数的入口点,而函数的代码体则包含了实现函数功能的具体指令序列。
func_name: # 函数代码体 # ...
- 函数调用:要调用函数,可以使用
jal
(Jump and Link)指令来实现。jal
指令会跳转到指定函数的入口点,并将下一条指令的地址(即函数返回地址)存储在寄存器$ra中。在函数返回时,可以使用jr(Jump Register)指令来返回到函数调用点。jal func_name # 调用函数func_name # 函数返回后的代码
- 参数传递:函数可以接收参数,参数通常通过寄存器传递。在调用函数前,需要将参数值存储在指定的参数寄存器中。在函数内部,可以通过读取这些寄存器来获取参数值。
# 调用函数时,将参数存储在参数寄存器中 li $a0, 10 # 将参数值10存储在$a0寄存器中 jal func_name # 调用函数func_name
- 返回值传递:函数可以返回值,返回值通常通过寄存器传递。在函数结束时,需要将返回值存储在指定的返回值寄存器中,通常是$v0和$v1寄存器。
func_name: # 函数代码体 # 将返回值存储在$v0寄存器中 li $v0, 42 # 将返回值设置为42 jr $ra # 返回到函数调用点
- 保存和恢复现场:在调用函数时,为了避免函数修改调用者的寄存器值,通常需要保存和恢复现场。可以使用栈(stack)来保存和恢复寄存器的值。
func_name: # 保存现场 addi $sp, $sp, -4 # 栈指针减4 sw $ra, 0($sp) # 将返回地址存储在栈中 # 函数代码体 # ... # 恢复现场 lw $ra, 0($sp) # 将返回地址从栈中加载到$ra寄存器 addi $sp, $sp, 4 # 栈指针加4 jr $ra # 返回到函数调用点
二、MIPS与机器语言的相互转换
MIPS指令是通过将指令的操作码(opcode)和操作数字段编码成二进制形式来转换成对应的机器码。具体的转换过程取决于指令的类型(R型、I型、J型等)以及指令的具体格式。
-
R型指令转换:
- R型指令的机器码通常由以下字段组成:操作码(opcode)、源寄存器(rs)、目标寄存器(rt)、操作数(rd)、移位量(shamt)和功能码(funct)。
- 操作码(opcode)通常是6位,用于指示指令类型。
- 源寄存器(rs)、目标寄存器(rt)、操作数(rd)和移位量(shamt)通常是5位,用于指示操作数寄存器的编号或立即数。
- 功能码(funct)通常是6位,用于指示具体的操作,如加法、按位与等。
- 按照MIPS指令集规范,每个指令的各个字段的位数和含义是固定的,因此根据指令的具体类型和操作,可以将这些字段按照指定的位数编码成对应的机器码。
-
I型指令转换:
- I型指令的机器码通常由以下字段组成:操作码(opcode)、源寄存器(rs)、目标寄存器(rt)和立即数(immediate)。
- 立即数(immediate)通常是16位,用于指示立即数操作的具体值。
- 其他字段的位数和含义与R型指令类似。
- 在转换过程中,操作码、寄存器编号和立即数按照指定的位数进行编码。
-
J型指令转换:
- J型指令的机器码通常由以下字段组成:操作码(opcode)和目标地址(target address)。
- 目标地址(target address)通常是26位,用于指示跳转目标的地址。
- 操作码的位数和含义与其他指令类型类似。
- 在转换过程中,操作码和目标地址按照指定的位数进行编码。