C++直译MIPS汇编指南

您所在的位置:网站首页 汇编转换成c语言 C++直译MIPS汇编指南

C++直译MIPS汇编指南

2024-05-17 03:28| 来源: 网络整理| 查看: 265

1      概述

电子系8字班的汇编大作业已经布置了一些天了,这些天同学们在尝试中遇到了很多困难。在昨天晚上,我本人也终于de完了程序最后一个bug(没错,又是个愚蠢的智障错误),完成了大作业全部代码的编写。我是怀着对整个过程中帮助我的人的感恩之心,以及帮助更多人渡过此劫的责任,以及帮本人B站账号拉一波关注的一点私心(是的,我打算今天开始入坑做一名B站科技UP主),来写这篇教程的。绝对详细,绝对说人话,希望对各位有点帮助。

阅读此教程需要有基本的C++基础,以及对MIPS汇编语言有一个大致的了解。基本上如果你大致复习了一下老师的课件的话都能达到要求的。相信我。如果你吃通了这篇教程,你的大作业写起来将会如丝般顺滑。

 

2      MIPS汇编

在写汇编的时候,一定要时刻记住,MIPS汇编是一个可以直接转成机器码的指令性语言。所有的命令都是跟MIPS硬件直接挂钩的。下面就带大家来认识一下,MIPS中最重要的两个部件:寄存器和内存。

2.1      寄存器

寄存器的结构如下:

说明以下几点:

    1. 一个寄存器就是一个连续的32bit的存储空间,32bit为一个word。

    2. MIPS有32个寄存器(其实不止,但写这个大作业知道更多也没必要)

    3. 4bit是一位16进制数,如上图中的内容用16进制表示为0x854ac921,其中0x表示16进制,后面8位16进制数分别对应寄存器中的4个bit,如8对应第一个1000,5对应第二个0101,4对应第三个0100… 一个MIPS寄存器对应8位16进制数。

    4. 8bit为一个byte,因此一个MIPS寄存器一共有4个byte。

2.2      内存

内存的结构如下:

说明以下几点:

    1. 内存是很大一段存储空间,以byte为基本计量单位

    2. 每一个byte有一个相应的内存地址,通常以16进制表示,相邻的内存空间之间相差1. 如果一段内存空间的地址为连续的整数序列,我们就称这段内存空间为连续的。

    3. 内存的地址为一个8位的16进制数,刚好为32bit(一位16进制=4个bit,4*8=32),即一个寄存器存储空间的大小。

    4. 由于内存地址需要由寄存器来表示,而一个寄存器32bit总共可以表示4G个字节的内存空间。因此MIPS的内存空间总量就为4G byte。

2.3      MIPS寄存器

前面说过MIPS中一共有32个寄存器,每个寄存器都有自己唯一的名称,还有一个自己唯一的编号。在调用该寄存器时,可以使用该寄存器的名称(如$s0),也可以使用该寄存器的编号(如$16)。这里说明一下,MIPS中是没有定义变量这一说的,东西就在那里,用就是了。

下面的内容翻译自David A. Patterson和John L. Hennessy的Computer Organization and Design, The Hardware/ Software Interface,Figure 2.14。向两位前辈致敬。

说明以下几点:

    1. $zero里面的值一直是零,貌似里面的值不能改,我没试过

    2. $t8-$t9为更多的临时变量。虽然叫临时变量,但是如果保存的变量不够用了拿来当保存的变量用也是没有问题的。

    3. $gp在本次作业中用不到。

    4. $fp这件事情不是很重要,除非你要拿汇编写超级大的程序。欲知详情看一下这张图便知:(截自两位先生的书,希望此举不侵犯版权…)

    5. 关于是否要保存这件事,可以这样理解:主程序调用子程序。有些变量是主程序在调用子程序之后还要正常使用的,那这些值子程序就不能改,子程序需要先保存再改最后再恢复,这样从外界看来子程序就没有修改这些值。有些变量是主程序临时使用的,在调用子程序前后不需要保持不变,那子程序也可以临时使用,就可以随便改。究竟哪些变量是主程序需要的,哪些变量是临时使用的,看一下定义理解一下,便知。(实在不行硬记也行)

2.4      MIPS指令

MIPS的汇编指令非常非常多,这些指令可以分为两大类。

第一类是基础指令,也称源码。这些指令构成了一个完备的指令集,任何操作都可以由这些指令的组合得到。

第二类是扩充指令,也称伪码,简直要多少有多少。助教给的那个MIPS工具书里面那么多扩充指令实际上也不是全部的扩充指令。扩充指令本质上是若干基础指令的组合,在汇编代码的预编译阶段,所有的扩充指令都会被替换为基础指令。这点大家在实践的过程中就能看到,这里就不再赘述。

我们实际在编程的过程中,本着哪个香用哪个的精神,是不管用的是基础指令还是扩充指令的。况且,如果只用基础指令的话,有些操作怎么实现还真得动动脑筋。

下面给出一些常用的MIPS指令。

说明以下几点:

    1. lw和sw是寄存器和内存之间进行数据交换。以lw为例,lw $s0, 2($s1)是指,以$s1中保存的32个bit的内容为地址,在内存中找到该地址再加上2个byte的偏移,顺序读取一个word的内容(1个word是4个byte),将这一个word的内容存储到$s0寄存器中。注意,2这里指代的 偏移量只能是一个常数,不可以是一个寄存器的值。这句话的意思是不可以出现lw $s0, $t0($s1)这样的写法。而且必须有这个常数,不能把lw $s0, 0($s1)写成lw $s0, ($s1)。

    2. and, or, nor等,都是按位运算,也就是说所有的位都要进行运算。因此如果$t0 = 1,我想求!$t0,即把1变成0,是不可以用nor $t0, $t0, $zero这个命令来实现的,这样会使$t0的高位全都变成1… 一件恐怖的事情。

    3. 这个表里很多东西不是很好查到,但是很好用,希望不要跳过它,好好看,会有帮助的。

    4. 每一条指令都是存在内存中的,也有对应的地址(冯诺依曼架构)。存一条指令占4个byte空间。

    5. syscall会向系统发出指令,要求系统进行一些操作。在syscall之前需要在$a0-$a3中准备好给系统传递的参数,在$v0中准备好系统操作的代码。在syscall命令执行时,系统会按照$a0-$a3和$v0中的内容做出相应的操作。

操作代码和内容如下表:

3      MIPS程序的结构

MIPS程序大致的一个结构图如下

说明以下几点:

    1. 预编译数据的定义方式为:

        name:   

        下面给出几个类型名的预编译数据的说明:

    2. 标签可以跟指令写在一行,也可以把指令另起一行。我比较喜欢另起一行,这样看起来更清楚。

    3. 程序的入口可以是MAIN标签,也可以是第一行,这取决于编译器的设定。

    4. 程序退出于下列语句:

        li $v0, 10           #把10传递给$v0,代表无返回值退出

        syscall

        或者:

        li $v0, 17           #把17传递给$v0,代表返回$a0退出

        syscall

        或者程序执行到末尾结束

4      C++结构转汇编结构

终于到激动人心的时刻了。下面讲介绍本教程的精髓——C++直译汇编。请看第一部分,C++结构转汇编结构。

4.1      if

C++中if的基本结构为:

if (statement1) {

       statement2;

}

else if (statement3) {

       statement4;

}

else {

       statement5;

}

这段代码转换成的汇编代码的结构为:

get $t0, statement1                             # 获得条件

beq $t0, $zero, ENDIF                        # 判断条件

IF:

statement2

j END_ELSE                                       # 跳转到结束

END_IF:

get $t0, statement3                             # 获得条件

beq $t0, $zero, ENDELSEIF               # 判断条件

ELSE_IF:

statement4

j END_ELSE                                       # 跳转到结束

END_ELSE_IF:

ELSE:

statement5

END_ELSE:

大家在这里可能已经明白意思了。直译成汇编就要从机器执行C++语句的角度出发去理解。机器无外乎是先计算if括号里的条件,将其与$zero相比较,从而判断是否跳转。这里get命令是我为了表意写上去的,表示一系列的指令得到statement1的值存到$t0中。在实际操作的时候,建议先写好一系列的标签,空出相应的行距,再往里面补内容,这样不容易出错。

下面是一个例子:

if (a >= b && c == d) {

   c = a;

   a = b;

}

直译成汇编。$s0-$s3代表a-d

sge $t0, $s0, $s1            #a >= b

seq $t1, $s2, $s3            #c == d

and $t0, $t0, $t1             #a >= b && c  == d

beq $t0, $zero, END_IF

IF:

move $s2, $s0

move $s0, $s1

END_IF:

4.2      while

C++中while的基本结构为:

while (statement1)  {

       statement2;

}

直译成汇编代码为:

J_WHILE:

get $t0, statement1                        #获得条件

beq $t0, $zero, END_WHILE         #判断条件

WHILE:

statement2

j J_WHILE

END_WHILE:

有了前面if的基础,相信现在while会变得很清晰。这里给while循环设置了三个标签:J_WHILE,WHILE和END_WHILE。J_WHILE负责判断,WHILE和END_WHILE之前为循环体,循环体最后负责跳转回J_WHILE进行下一次判断。

例子:

while(a



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3