MIPS 汇编指令

MIPS 汇编指令

MIPS的意思是 “无内部互锁流水级的微处理器” (Microprocessor without interlocked piped stages)

指令系统常见指令分类

  • 算术和逻辑运算指令

加、减、乘、除、开方……

移位:左移与右移、逻辑移位与算术移位……

与、或、非、异或……

格式转换……

  • 访存指令:取数、存数

不同长度和不同类型:定点/浮点,字节/半字/字/双字

不同寻址方式

  • 转移指令

相对/绝对、直接/间接、条件/无条件

  • 系统管理指令

TLB管理、Cache管理、异常管理、安全管理

指令特点

  • 所有指令都是32位长

  • 指令操作必须符合流水线

    MIPS指令一次只能修改一个寄存器的值

  • 3操作数指令

  • 32个寄存器

  • 没有条件标志位

MIPS 的寄存器

  • MIPS下一共有32个通用寄存器

  • 在汇编中,寄存器标志由 $ 符开头

  • 寄存器表示可以有两种方式

    • 直接使用该寄存器对应的编号,例如:从 $0$31

    • 使用对应的寄存器名称例如: $t1 , $sp

  • 栈的走向是从高地址到低地址

  • 寄存器编号 寄存器名 寄存器用途
    0 zero 永远返回零
    1 $at 汇编保留寄存器
    2-3 $v0 - $v1 存储表达式或者是函数的返回值
    4-7 $a0 - $a3 存储子程序的前4个参数,在子程序调用过程中释放
    8-15 $t0 - $t7 临时变量
    16-23 $s0 - $s7 静态变量
    24-25 $t8 - $t9 临时变量
    26-27 $k0 - $k1 中断函数返回值
    28 $gp 指向静态数据块的中间地址
    29 $sp 栈顶指针
    30 $fp 帧指针
    31 $ra 返回地址

三种类型指令

R-type:arithmetic instruction

寄存器寻址

I-type:data transfer, arithmetic instruction

立即寻址

J-type:branch instruction(conditional & unconditional)

相对寻址:J(I)-type

伪直接寻址:J-type

操作码操作数位数
MIPS指令编码格式
  • R-type:寄存器类指令主要完成寄存器-寄存器的ALU操作

  • I-type :立即数类指令主要用于LOAD-STORE操作,条件转移指令等。访存指令是由RS1寄存器的值+immediate得到内存地址,将 RS2的值存入内存或者从内存取数存RS2。访存指令对存储器进行读写:lw $s1,100(\$s2)

    访存对象

    条件转移指令,转移条件可能为RS1是否为0,也可能是RS1和RS2是否相等。

  • J-type:直接跳转指令。转移地址是PC的高四位和target直接拼接,后面两位补0(因为内存4字节对齐)。

常见指令

MIPSloadstore指令

其中SDC1,LDC1等是浮点类的指令

ALU常见指令
MIPS控制流指令
BEQZ 条件转移指令,当寄存器中内容为0时转移发生 BEQZ R1,0
BENZ 条件转移指令,当寄存器中内容不为0时转移发生 BNEZ R1,0

寄存器和立即计算指令数据流

image-20231001102233068

常数加载

加载立即数常数

dla、la: 用来加载程序中某些带标号的位置或者变量的地址的宏指令;

dli、li: 装入立即数常数,这是一个宏指令;

lui: 把立即数加载到寄存器高位。

ll sc指令

LL 指令的功能是从内存中读取一个字,以实现接下来的 RMW(Read-Modify-Write) 操作;

SC 指令的功能是向内存中写入一个字,以完成前面的 RMW 操作。

LL/SC 指令的独特之处在于,它们不是一个简单的内存读取/写入的函数,当使用 LL 指令从内存中读取一个字之后,比如 LL d, off(b),处理器会记住 LL 指令的这次操作(会在 CPU 的寄存器中设置一个不可见的 bit 位),同时 LL 指令读取的地址 off(b) 也会保存在处理器的寄存器中。接下来的 SC 指令,比如 SC t, off(b),会检查上次 LL 指令执行后的 RMW 操作是否是原子操作(即不存在其它对这个地址的操作),如果是原子操作,则 t 的值将会被更新至内存中,同时 t 的值也会变为1,表示操作成功;反之,如果 RMW 的操作不是原子操作(即存在其它对这个地址的访问冲突),则 t 的值不会被更新至内存中,且 t 的值也会变为0,表示操作失败。

例子:

image-20231002170111052

为啥后面还要加一个nop指令?

延迟槽填充(Delay Slot Filling): 在MIPS体系结构中,条件分支指令(如beq)的执行是延迟的,意味着在分支判断之前会执行分支指令后的一条指令。为了最大程度地利用流水线处理器的性能,程序员通常需要填充延迟槽,以确保流水线中始终有有效的指令在执行。

lwl lwr swl swr 大小端问题

https://courses.cs.duke.edu/fall02/cps104/homework/lwswlr.html

lw sw用于word对齐情况下的一个字节的移动,lwl lwr swl swr用于字节没有对齐情况下的移动

LWR

This instruction reads the right portion of a register (low-order bytes).

这条指令从寄存器的右边(低地址字节)开始读

Conceptually, LWR starts at the specified byte in memory and loads that byte into the low-order (right most) byte of the destination register.

Then, it proceeds (进入)to the ==high-order byte of the word in memory== (since SPARC is big-endian, this is toward lower memory addresses) and the high-order byte of the register, loading bytes from memory into the register until it reaches the high-order byte of the memory word. 在加载完一个字节以后,进入内存中下一个高字节部分(对于大端模式,高字节部分在低地址位,小端模式反之)并存入寄存器中直到到达内存最高字节

So, start at some specified address, and start copying bytes from memory and decrement the memory byte address until you reach the next lowest memory address that is word aligned, including the byte at this last address. The most significant (left-most) byte(s) of the register will not be changed.

例子:lwr $4, 2($0)

lwr $t, offset($s)

从计算得到的地址开始,加载4个字节的数据(一个字)到目标寄存器 $t 中,同时将目标寄存器中的其余3个字节保持不变(即不受影响)。

image-20231002112606615

从地址0+2处开始取u,到2放到寄存器的右端,然后因为大端模式向高位继续读取1,0依次放到寄存器中,直到遇到了字对齐的地址(内存中四个字一对齐)

lwl

同理lwl 放到寄存器的左端

LWL starts at the specified byte in memory and loads that byte into the high-order (left most) byte of the destination register. Then, it proceeds to the ==low-order byte== of the word in memory (since SPARC is big-endian, this is toward higher memory addresses) and the low-order byte of the register, loading bytes from memory into the register until it reaches the low-order byte of the memory word.

lwl $4, 2($0)

image-20231002114134669

SWR

swr $4, 5($0)

image-20231002114423677

从寄存器4的最有端的值取值,存到地址为5($0) 的内存中,其中$0+5原来存放的是5,现在存寄存器最右端的D,然后向高位移动。Bytes are copied toward the ==left== until the word aligned address is reached (byte 0 of word at memory address 4, which has the initial value of 4 in this example).

SWL

swL $4, 5($0).Bytes are copied toward the ==right== until the the last byte of this memory word is reached (byte 3 of word at memory address 4, which has the initial value of 7 in this example).

image-20231002115214583

作业例题

在一台小尾端的MIPS机器上,用LWL/LWR/SWL/SWR指令编写一段程序,把内存单元1005-1008的值取到寄存器R1,再存到内存单元2005~2008中

  • 首先,注意由于内存对齐的问题,1005~1008不是一个完整的字,4字节对齐后,开头都是4的倍数:

    因为是小尾端,lwr向高字节走就是向高地址走。lwl向低字节走就是向低地址走。如果是大尾端,lwr向高字节就是向低地址走,lwl就是向高地址走

    1008 1009 100A 100B
    1004 1005 1006 1007

    因此,首先我们需要将基地址1005这个立即数存到寄存器当中:

    dli \$2 ,1005

    ,下一步,将内存单元的值加载到寄存器r1当中。

    lwr \$1 , 0x0($2)

    此时R2:

    X 1007 1006 1005

    lwl \$1 , 0x3($2)

    此时R2:

    1008 1007 1006 1005

    下一步,将r1的内容存到内存2005-2008

    同理该部分内存也不属于一个字

    2008 2009 200A 200B
    2004 2005 2006 2007
1
2
3
dli \$2 ,2005 #装载基地址
swr $1, 0x0($2)
swl $1, 0x3($2)

思考:如果变成大尾端会有什么变化

改为大尾端后:

1
2
3
4
5
6
dli \$2 ,1005 #装载基地址
lwl $1, 0x0($2)
lwr $1, 0x3($2)
dli \$2 ,2005
swl $1, 0x0($2)
swr $1, 0x3($2)

常见指令汇总

MIPS 指令集(共31条)
助记符 指令格式 示例 示例含义 操作及其解释
Bit # 31..26 25..21 20..16 15..11 10..6 5..0
R-type op rs rt rd shamt func
add 000000 rs rt rd 00000 100000 add $1,$2,$3 $1=$2+$3 rd <- rs + rt ;其中rs=$2,rt=$3, rd=$1
addu 000000 rs rt rd 00000 100001 addu $1,$2,$3 $1=$2+$3 rd <- rs + rt ;其中rs=$2,rt=$3, rd=$1,无符号数
sub 000000 rs rt rd 00000 100010 sub $1,$2,$3 $1=$2-$3 rd <- rs - rt ;其中rs=$2,rt=$3, rd=$1
subu 000000 rs rt rd 00000 100011 subu $1,$2,$3 $1=$2-$3 rd <- rs - rt ;其中rs=$2,rt=$3, rd=$1,无符号数
and 000000 rs rt rd 00000 100100 and $1,$2,$3 $1=$2 & $3 rd <- rs & rt ;其中rs=$2,rt=$3, rd=$1
or 000000 rs rt rd 00000 100101 or $1,$2,$3 $1=$2 $3
xor 000000 rs rt rd 00000 100110 xor $1,$2,$3 $1=$2 ^ $3 rd <- rs xor rt ;其中rs=$2,rt=$3, rd=$1(异或)
nor 000000 rs rt rd 00000 100111 nor $1,$2,$3 $1=~($2 $3)
slt 000000 rs rt rd 00000 101010 slt $1,$2,$3 if($2<$3) $1=1 else $1=0 if (rs < rt) rd=1 else rd=0 ;其中rs=$2,rt=$3, rd=$1
sltu 000000 rs rt rd 00000 101011 sltu $1,$2,$3 if($2<$3) $1=1 else $1=0 if (rs < rt) rd=1 else rd=0 ;其中rs=$2,rt=$3, rd=$1 (无符号数)
sll 000000 00000 rt rd shamt 000000 sll $1,$2,10 $1=$2<<10 rd <- rt << shamt ;shamt存放移位的位数, 也就是指令中的立即数,其中rt=$2, rd=$1
srl 000000 00000 rt rd shamt 000010 srl $1,$2,10 $1=$2>>10 rd <- rt >> shamt ;(logical) ,其中rt=$2, rd=$1
sra 000000 00000 rt rd shamt 000011 sra $1,$2,10 $1=$2>>10 rd <- rt >> shamt ;(arithmetic) 注意符号位保留 其中rt=$2, rd=$1
sllv 000000 rs rt rd 00000 000100 sllv $1,$2,$3 $1=$2<<$3 rd <- rt << rs ;其中rs=$3,rt=$2, rd=$1
srlv 000000 rs rt rd 00000 000110 srlv $1,$2,$3 $1=$2>>$3 rd <- rt >> rs ;(logical)其中rs=$3,rt=$2, rd=$1
srav 000000 rs rt rd 00000 000111 srav $1,$2,$3 $1=$2>>$3 rd <- rt >> rs ;(arithmetic) 注意符号位保留 其中rs=$3,rt=$2, rd=$1
jr 000000 rs 00000 00000 00000 001000 jr $31 goto $31 PC <- rs
I-type op rs rt immediate
addi 001000 rs rt immediate addi $1,$2,100 $1=$2+100 rt <- rs + (sign-extend)immediate ;其中rt=$1,rs=$2
addiu 001001 rs rt immediate addiu $1,$2,100 $1=$2+100 rt <- rs + (zero-extend)immediate ;其中rt=$1,rs=$2
andi 001100 rs rt immediate andi $1,$2,10 $1=$2 & 10 rt <- rs & (zero-extend)immediate ;其中rt=$1,rs=$2
ori 001101 rs rt immediate andi $1,$2,10 $1=$2 | 10 rt <- rs | (zero-extend)immediate ;其中rt=$1,rs=$2
xori 001110 rs rt immediate andi $1,$2,10 $1=$2 ^ 10 rt <- rs xor (zero-extend)immediate ;其中rt=$1,rs=$2
lui 001111 00000 rt immediate lui $1,100 $1=100*65536 rt <- immediate*65536 ;将16位立即数放到目标寄存器高16 位,目标寄存器的 低16位填0
lw 100011 rs rt immediate lw $1,10($2) $1=memory[$2 +10] rt <- memory[rs + (sign-extend)immediate] ;rt=$1,rs=$2
sw 101011 rs rt immediate sw $1,10($2) memory[$2+10] =$1 memory[rs + (sign-extend)immediate] <- rt ;rt=$1,rs=$2
beq 000100 rs rt immediate beq $1,$2,10 if($1==$2) goto PC+4+40 if (rs == rt) PC <- PC+4 + (sign-extend)immediate<<2
bne 000101 rs rt immediate bne $1,$2,10 if($1!=$2) goto PC+4+40 if (rs != rt) PC <- PC+4 + (sign-extend)immediate<<2
slti 001010 rs rt immediate slti $1,$2,10 if($2<10) $1=1 else $1=0 if (rs <(sign-extend)immediate) rt=1 else rt=0 ; 其中rs=$2,rt=$1
sltiu 001011 rs rt immediate sltiu $1,$2,10 if($2<10) $1=1 else $1=0 if (rs <(zero-extend)immediate) rt=1 else rt=0 ; 其中rs=$2,rt=$1
J-type op address
j 000010 address j 10000 goto 10000 PC <- (PC+4)[31..28],address,0,0 ;address=10000/4
jal 000011 address jal 10000 $31<-PC+4; goto 10000 $31<-PC+4;PC <- (PC+4)[31..28],address,0,0 ;address=10000/4
BEQZ 条件转移指令,当寄存器中内容为0时转移发生 BEQZ R1,0
BENZ 条件转移指令,当寄存器中内容不为0时转移发生 BNEZ R1,0

内存对齐

在32位微处理器中,处理器访问内存都是按照32位进行的,即一次读取或写入都是4个字节,比如,地址0x0 ~ 0xF这16字节的内存,对于微处理器来说,不是将其看作16个单一字节,而是4个块,每块4个字节

image-20231002131220203

假设要求变量的内存空间按照4字节对齐,则内存空间的首地址必须是4的整数倍,满足条件的地址有0x0、0x4、0x8、0xC……

大尾端和小尾端的字节序问题

big-endian又名大尾序,就是数值的尾巴存储在大地址上。尾是相对我们认识的变量值,大是指地址; 相对应,little-endian又名小尾序,数值的尾巴存储在小地址上。 一句话大小是尾巴的地址,尾巴是数值的尾巴

image-20231002152257410

计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合比如网络传输和文件储存,几乎都是用的大端字节序。正是因为这些原因才有了字节序。

计算机处理字节序的时候,如果是大端字节序,先读到的就是高位字节,后读到的就是低位字节。小端字节序则正好相反v

浮点运算问题

https://gaozhiyuan.net/assembly/mips-floating-point-arithmetic.html

在MIPS体系结构中,最多支持4个协处理器(Co-Processor)。其中,CP0用作系统控制,CP1和CP3用于FPU,CP2用于特点实现。CP0是必须具备的协处理器,其余三个协处理器是选配的。

浮点数加载

下面的代码就可以分别将位于内存中的 number1和 number2 加载到浮点寄存器:

1
2
3
4
5
6
7
.data
number1: .float 2.5
number2: .float 3.5
.text
main:
lwc1 $f1, number1
lwc1 $f2, number2

浮点数运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.data
number1: .float 3.6
number2: .float 3.5
.text
main:
lwc1 $f1, number1
lwc1 $f2, number2

# 浮点数相加
add.s $f3, $f1, $f2
# 浮点数相减
sub.s $f4, $f1, $f2
# 浮点数相乘
mul.s $f5, $f1, $f2
# 浮点数相除
div.s $f6, $f1, $f2

例子:

**对于向量运算x(i)=a*X(i)+Y(i),假设X和Y的首地址分别存在定点寄存器R1,R2,a存在浮点寄存器f0中,写出mips代码**,假设数组的长度是n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ADDIU     R3,R0,N;    #首先用一个寄存器记录数组的末端地址,这里0寄存器就是0
SLL R3,R3,2; #R3左移2位=》R3=R3*4
ADDU R3,R3,R1; #基址+偏移
Loop:
LWC1 F1,(0)R1;#将X,y加载到浮点寄存器里面
LWC1 F2,(0)R2;
MUL.S F3,F1,F0;
ADD.S F4,F3,F2;
#数组指针移动
ADDIU R1,R1,4;
ADDIU R2,R2,4;
#判断数组是否已经遍历完了
BNE R3,R1,Loop;
#将结果存储,这里利用延迟槽所以会先执行这一句
SWC1 F4,-4(R1);