计算机中指令的表示

人操作计算机的方式与计算机看到指令的方式是不同的。
指令在计算机内部是以若干或高或低的电信号的序列表示的,并且形式上和数的表示相同。
几乎所有的指令都要用到寄存器,所以必须有一套规定,以将寄存器名字映射成数字。

在MIPS汇编语言中
寄存器$t0-$t7映射到寄存器8-15
寄存器$t8-$t9映射到寄存器24-25
寄存器$s0-$s7映射到寄存器16-23

因此,$s0表示寄存器16,$s1表示寄存器17,$s2表示寄存器18……$t0表示寄存器8,$t1表示寄存器9

下面将介绍32个寄存器中其余寄存器的映射

MIPS字段

为了使讨论变得简单,给MIPS字段命名如下:


MIPS指令中各字段名称及含义如下:

  • op:指令的基本操作,通常成为操作码(opcode)
  • rs:第一个源操作数寄存器
  • rt:第二个源操作数寄存器
  • rd:用于存放操作结果的目的寄存器
  • shamt:位移量
  • funct:功能。一般称为功能码(function code),用在指明op字段中操作的特定变式

例题


对于下面的MIPS指令,首先给出其十进制表示形式,接着给出二进制表示形式。

add $t0, $s1, $s2

答案:



本例中第一个字段和最后一个字段(0和32)组合起来告诉MIPS计算机该指令要完成加法运算
第二个字段表示加法的第一个源操作数寄存器号(17= $s1)
第三个字段表示加法的另一个源操作数寄存器号(18=$s2)
第四个字段表示存放运算结果的目的寄存器号(8= $t0)
第五个字段在这条指令中没有用到,故置为0

为避免读写冗长乏味的二进制字符串,16进制表示法变得很流行。16是2的4次幂
本例题转十六进制后

下图是十六进制和二进制的转换表

000004010081000c1100
100015010191001d1101
2001060110a1010e1110
3001170111b1011f1111

例如: eca8 6420
1110 1100 1010 1000 0110 0100 0010 0000

MIPS I型

之前介绍的格式称为R型(用于寄存器),另外一种指令格式称为I型(用于立即数),立即数和数据传送指令用的就是这种格式。
I型的字段如下所示:

逻辑操作

用于简化对字中若干位进行打包或者拆包的操作,称为逻辑操作。。下图给出了C、Java和MIPS中的逻辑操作

逻辑操作CJavaMIPS
Shift left左移<<<<sll
Shift right右移>>>>>srl
Bitwise AND按位与&&and, andi
Bitwise OR按位或IIor,ori
Bitwise NOT按位取反~~nor

shift移位 sll srl

第一类逻辑操作称为移位(shift)。它们将一个字里面的所有位都向左或向右移动,并在空出来的位上填充0

例如:假设寄存器$s0中的数据是:
0000 0000 0000 0000 0000 0000 0000 1001
一条左移4位的指令执行后,得到的新指是:
0000 0000 0000 0000 0000 0000 1001 0000

在MIPS中,
逻辑左移:sll
逻辑右移:srl

例:

sll $t2,$s0,4

前面介绍R型指令时没有解释shamt字段,它在移位指令中被用于表示移位量
因此上述指令对应的机器语言是

例:

逻辑左移有个额外的好处,就是左移i位相当于乘以2
的i次方
假设左移4位,就相当于乘以2的4次方(即16)

按位与and

按位与(and):该操作仅当两个操作位均为1时结果才为1,否则为0
例如:

and $t0, $t1, $t2

按位或or

按位或(or):该操作在两个操作位中任意一位为1时结果就为1
例如:

or $t0, $t1, $t2

按位取反 NOR

按位取反:该操作仅有一个操作数,将1变成0,0变成1
在MIPS中,引入了或非NOR指令来替代NOT,即一个操作数是0,那么对另外一个操作数而言,结果就等价于NOT

例如:

nor $t0, $t1, $zero

条件操作

程序语言通常使用if语句描述决策,有时也使用go to语句和标签。

如果相等则分支 beq

该指令表示:如果register1和register2中的数值相等,则转到标签为L1的语句执行

beq register1, register2, L1

如果不相等则分支 bne

该指令表示:如果register1和register2的数值不相等,则转到标签为L1的语句执行

bne register1, register2, L1

无条件分支 j

当遇到这个指令时,程序必须分支

例如:

j Exit

无条件直接跳转到Exit标签

例题

在下面这段代码中,f、g、h、i、j都是变量,设该变量依次对应于从$s0到$s4的寄存器,求这条C语言if语句编译后形成的MIPS代码

if (i==j) f = g+h;
else f = g-h;

下图是MIPS代码执行过程的流程图。


第一个表达式表示i和j是否相等,需要一条beq指令
通常,通过测试分支的相反条件来跳过if语句后面的then部分,代码的效率会更高,所以我们使用bne指令

bne $s3, $s4, Else

下一个赋值语句执行一个单操作,如果所有的操作数都分配给寄存器,那么它只是一条指令:

add $s0, $s1, $s2

然后使用无条件分支指令转Exit标签

j   Exit

if语句中else部分的赋值语句也可编译成一条指令。我们只需将标签Else加在这条指令前、标签Exit加在这条指令后面

Else: sub $s0, $s1, $s2
Exit: …

最后本例完整的代码:

bne $s3, $s4, Else
    add $s0, $s1, $s2
    j   Exit

Else: sub $s0, $s1, $s2
Exit: …

小于则置位 slt

该指令在比较两个寄存器内容之后,若第一个寄存器小于第二个寄存器,则将第三个寄存器设置为1,否则设置为0
例如:

slt rd, rs, rt

该指令表示:如果 (rs < rt) 则rd = 1; 否则rd = 0;
与bne结合代码:

slt $t0, $s1, $s2  # if ($s1 < $s2)
bne $t0, $zero, L  #   branch to L

立即数版本的小于则置位slti

在比较中常常使用常数操作数,所以有立即数版本的小于则置位指令。
例如,为了测试寄存器$s2的值是否小于常数10,可以使用如下指令:

slti    $t0, $t2, 10

该指令表示,如果s2小于10则t0=1,否则t0等于0

跳转和链接指令 jal

跳转到某个地址的同时将下一条指令的地址保存在寄存器$ra中
格式为:

jal ProcedureLabel

指令中的链接部分表示指向调用点的地址或链接,以允许国产返回到合适的地址。存在在寄存器$ra(31号寄存器)中的链接部分称为返回地址

寄存器跳转 jr

用于case语句,表示无条件跳转到寄存器所指定的地址:

jr $ra

寄存器跳转指令跳转到存储在$ra寄存器中的地址
因此,调用程序或成为调用者,将参数值放在$a0-$a3,然后使用jal x跳转到国产x
被调用者执行运算,将结果放在$v0和$v1,然后使用jr $ra指令将控制返回给调用者

循环

例题:
下面是用C语言编写的传统循环程序;

while (save[i] == k) 
    i += 1;

假设i和k存放在寄存器$s3 和 $s5中,数组save的基址存放在寄存器$s6中。求这段C程序对应的MIPS汇编代码

解:
第一步需要将save[i] 读入一个临时寄存器中。在读入之前,需要计算它的地址
在将i加到save数组基址以形成访存地址前,由于系统按照字节寻址的缘故,先要将i乘以4;这里我们可以使用逻辑左移指令实现这一乘法,因为左移2位等价于乘4

Loop: sll  $t1, $s3, 2

为了得到save[i]的地址,需要将$t1和$s6中save的基址相加

add  $t1, $t1, $s6

现在可用该地址将save[i]读入一个临时寄存器中

lw   $t0, 0($t1)

下一条指令执行循环判断,如果save[i]≠k则退出循环

bne  $t0, $s5, Exit

再下一条指令将i加1

addi $s3, $s3, 1

在循环的末尾,程序跳转到循环开始。随后增加了一个Exit标签

j    Loop
Exit: …

完整的代码如下:

Loop: sll  $t1, $s3, 2
    add  $t1, $t1, $s6
    lw   $t0, 0($t1)
    bne  $t0, $s5, Exit
    addi $s3, $s3, 1
    j    Loop
Exit: …
最后修改:2022 年 04 月 15 日
如果觉得我的文章对你有用,请随意赞赏