文章开头我只想问一个问题:写了很多代码,你不好奇代码在计算机底层是什么样的吗?
下面将一步一步探索这个过程。首先,大家要有个初步的认识,一次从左到右进行编译、汇编。
高级语言(C语言) 汇编语言 二进制机器语言
int add(int a,int b){ add:
int c; add $a2,$a0,$a1 //c=a+b 00000000100001010011000000100000
c = a + b; -->编译 add $v0,$a2,$zero -->汇编 00000000110000000001000000100000
return c; jr $ra 00000011111000000000000000001000
}
关于 $a2,$ra等寄存器,大家可以看看这篇博主的文章总结 MIPS 通用寄存器 。
首先大家要了解计算机的一个概念--过程,意思是根据提供的参数执行一定任务的存储的子程序。说白了,就是上述C语言add函数里面的逻辑在CPU执行的过程。MIPS在为过程调用分配32个寄存器是遵循以下约定:
- $a0~$a3:用于传递参数的4个参数寄存器。如上述的 $a0 即为 a,$a1即为b,由于参数寄存器还足够,因此 $a2即为c。
- $v0~$v1:用于返回值的两个寄存器。
- $ra:用于返回起始点的返回地址寄存器。如上述的 jr $ra,意思是无条件跳转到寄存器所制定的地址。通俗地说,$ra是存储调用过程前的指令地址,执行完过程后,跳转到调用过程前的指令地址,继续做后续的计算。int a1 = add(1,2),执行完过程add的逻辑后, jr $ra由回到a1继续计算。因为过程是可能由程序中多个点调用。即 int a2 = add(3,4);执行完 jr $ra后回到a2继续计算。代码可以如下:
#include <stdio.h>
int add(int a,int b){
int c;
c = a + b;
return c;
}void main(){
int a1 = add(1,2);
int a2 = add(3,4);
}
综上所述,大家应该知道为啥$a0 即为 a,$a1即为b,$a2即为c了。接下来,我们继续,汇编语言怎么转换成二进制程序语言。想必大家都了解到,无论什么程序,最后在计算机底层都是 0 1 存在的。
接下来我们继续,相比大家都想知道汇编语言是怎么转成二进制机器语言。以下是MIPS指令各字段的命名以及含义。分为I型以及R型两类指令格式。
R型:用于寄存器,进行逻辑运算,如加减操作。
op | rs | rt | rd | shamt | funct |
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
- op:指令的基本操作,通常称为操作码(opcode)。
- rs:第一个源操作数寄存器。
- rt:第二个源操数寄存器。
- rd:用于存放操作结果的目的寄存器。
- shamt:位移量。
- funct:功能码(function code)。
I型:用于立即数,即立即数与数据传输指令,如sw,lw等操作。
op | rs | rt | constant and adress | ||
6位 | 5位 | 5位 | 16位 |
下面列出MPIS常用指令的值给到大家看下:
指令 |
格式 |
op |
rs |
rt |
rd |
shumt |
hinct |
address |
add |
R |
0 |
reg |
reg |
reg |
0 |
32 |
n. a. |
Iw (load word) |
I |
35 |
reg |
n. a |
n. a | n. a | n. a | address |
在上表中,“reg”代表寄存器的标号(从0 ~31), “address”表示16位地址, “na.” (not applicable)表示这个字段在该指令格式中不出现。注意,add指令在硬件是根据hmct字段的值来决定所进行的操作:add (32)或substract (34)
因此针对上面转换,可以得到:
汇编语言
add:
add $a2,$a0,$a1 //c=a+b
add $v0,$a2,$zero
jr $ra
}
op |
rs |
rt |
rd |
address/shumt |
hinct |
0 |
4 |
5 |
6 |
0 |
32 |
0 |
6 |
0 |
2 | 0 | 32 |
0 | 31 | 0 | 0 | 0 | 8 |
转换为二进制:
op |
rs |
rt |
rd |
address/shumt |
hinct |
000000 |
00100 |
00101 |
00110 |
00000 |
100000 |
000000 |
00110 |
00000 |
00010 | 00000 | 100000 |
000000 | 11111 | 00000 | 00000 | 00000 | 001000 |
到这里,相必大家都有点感觉了,实际一个程序的指令是涉及很多指令计算的。远远超过以上三条,因此大家需要去强化学习,加深对计算机的理解。
参考文献:
[1]计算机组成与设计:硬件/软件接口(原书第5版).北京:机械工业出版社,2015.