【计算机本科补全计划】指令:计算机的语言(MIPS) Part3

正文之前

今天学的很尴尬,因为有事情,而且新认识了两个计算机学院的保研大佬,不得不感叹我找的导师之强,第一个去上交的,是被金老师推荐去的,听说是跟了目前亚洲第一人的一个做计算机系统的人,例外一个小大佬居然也是直接跟的金老师。。也就是说我们以后是同门。 前面随便问问计算机学院的情况:

  • 学长:我有个高中同学在金老师手下念博士生,我帮你问问。。。;
  • 学姐:我有个大学同学在金老师实验室读研究生,我给你推荐好友哈。。。;
  • 大佬:金老师是我的助班和学业导师,他人很好的。。。;
  • 小大佬:金老师跟我说还不急着选方向,(PS:“嗯?你也是金老师的研究生呀” ) 嗯,他也是我助班。。。(。。。。)。。。

正文


  1. 并行与指令:同步。

当不同的任务之间需要访问问一个位置的数据的时候,就会出现数据竞争的风险,这个时候急需要同步来处理,负责就会引起程序运行错误的结果。同步运行需要依赖于硬件提供的同步指令,可以由用户调用。主要是加锁和解锁的同步操作。要实现操作的原子性(不可被分割打断),需要由硬件对两个同时执行的交换操作(一种同步机制,通过交换原语实现)进行排序,一种可行的办法是:指令对,链接取数和条件存数。 关键就在于临时寄存器的特性!!这一点我也是写笔记才明白的!!!

again: addi $t0,$zero,1;
       ll   $t1,0($s1);
       sc   $t0,0($s1);
       beq  $t0,$zero,again;
       add  $s4,$zero,$t1

其中ll 和 sc 分别对应着链接取数和条件存数 这两个指令,$s1 中存放着我们的锁单元,对应着我们一种资源,当$t1 从锁单元中取出来值,如果有任何处理器插入改变了锁单元的值,指令都会将$t0 变为 0(诸位莫忘了 $t0是一个临时存储器,如果发生了别的处理器的操作,临时寄存器会被瞬间归零),那么这段指令就会重新执行。直到完全完成原子交换,$s1所指向的锁单元中的值与$s4完成 值的交换。

  1. 翻译并且执行程序

参照下面的图片:

链接器的工作分为三个步骤(这个我真不会,后面回头来看吧!):

  • 将代码和数据模块象征性的放入内存
  • 决定数据和指令标签的地址
  • 修补内部和外部引用

加载器在UNIX系统中执行的工作步骤:

  • 读取可执行文件头来确定代码段和数据段的大小
  • 为正文和数据创建一个足够大的地址空间
  • 将可执行文件中的指令和数据复制到内存中
  • 把主程序的采纳数复制到栈顶
  • 初始化机器寄存器,将栈顶指针指向的一个空位置
  • 跳转到指令例程,将参数复制到参数寄存器并且调用程序的main函数,当main函数返回时,启动例程通过调用系统exit 终止程序。

  1. 以一个C交换程序作为例子
//C语言版本:
void swap(int v[],int k)
{
    int temp;   
    temp = v[k];
    v[k] = v[k+1];
    v[k+1] = temp;
}

下面我们用以下常见的步骤将其手动翻译为汇编程序:

// 用$a0 $a1 来存储v的基址和k的值。 因为swap是叶过程(不会产生调用的过程),所以为temp分配唯一的临时寄存器$t0;
swap:
    sll $t1,$a1,2;   // 因为我们用字来存储数据,所以地址距离应该是4倍,所以要左移两位 ,等同与*4;
    add $t1,$a0,$t1; // 把v[k]的位置传入进来 到$t0 

    lw  $t0,0($t1);  // 读取v[k]的值;
    lw  $t2,4($t1);  // 读取v[k+1]的值;因为地址固定相差四位,所直接读取4($t1)即可
//交换两个值 用sw
    sw  $t2,0($t1);
    sw  $t0,4($t1);
//因为是叶过程,所以会被调用,就需要一个跳转调用者的返回指令
    jr $ra;

  1. 以一个C排序程序(用冒泡法)作为例子
//C语言版本:
void sort(int v[],int n)
{
    int i,j;   
    for(i=0;i<n;i+=1)
        {
            for(j=i-1;j>=0 && v[j]>v[j+1];j -= 1)
                {
                    swap(v,j);
                }
        }
}

下面我们用以下常见的步骤将其手动翻译为汇编程序:

为sort 的两个参数分配寄存器为:$a0和$a1 为变量i,j 分配 $s0,$s1;首先,最外层的循环,初始化:

move $s0,$zero; // 其实这个是伪指令,是一种方便操作的方式,真实的代码应该是 add $s0,$zero,$zero;

然后在for中还有一个 i++的功能需要实现,那就是在末尾加上一个:

addi $s0,$s0,1;

然后第二个判断条件需要放在每一次循环的开头,如果i>=n 就会退出,否则就继续执行下去 采用小于则置位以及等于就跳转的命令,

for1tst: slt $t0,$s0,$a1; //如果i<n就把t0置位为1;否则为0 也就是跳出最外层循环。
         beq $t0,$zero,exit1; //t0 等于0 跳转到退出。

综上,我们的第一层循环的总体结构就是:

move $s0,$zero; 
for1tst: slt $t0,$s0,$a1;
         beq $t0,$zero,exit1;

   ~~~~~~~body~~~~~~~~

         addi $s0,$s0,1; 
         j for1tst;
  exit1:

然后是第二层循环,类似的,先给定初始条件,然后准备好结束条件,同时对于swap要重新给定寄存器,或者是在进入swap之前把原来的被占用的寄存器的内容放到另外的寄存器,然后结束swap的时候在逆向的返回原来的值;


综合程序如下:(for中的对a0 a1 的引用换成了 s2(v的基址) s3(n) 方便读写)

//定义整个函数的基调:首先腾出四个地方来存放数据,当前这些寄存器可能内部有sort的调用者的数据,所以为了避免丢失,要把当前寄存器的值保存到堆栈中

 sort:addi $sp,$sp,-20;
        sw   $ra,16($sp);   //此处是将调用sort的调用者的位置保存到堆栈中;
        sw   $s3,12($sp);
        sw   $s2,8($sp);
        sw   $s1,4($sp);
        sw   $s0,0($sp);

// 在swap中要用到a0 a1 两个寄存器,所以先把其中的值保存起来比较好。

        move $s2,$a0;
        move $s3,$a1;

//对最外层for循环进行初始化,s0表示i值,s3表示n,t0 则是判定条件所需要的临时寄存器;

         move $s0,$zero; 
for1tst: slt $t0,$s0,$s3;
        beq $t0,$zero,exit1;

//内部循环的内容,初始化s1表示j=i-1; 内层for循环,分别有两个判定条件判断是否进行内层循环,否则调到exit2 结束内层循环,满足条件就进入循环体,取出数值,然后t1表示v[k]的k,t2表示v[k]的位置,取出来v[k]和v[k+1],然后进行比较,如果v[k+1]<v[k] 就可以直接跳转到exit2,否则就执行swap程序

        addi $s1,$s0,-1;
for2tst:slti $t0,$s1,0;
        bne $t0,$zero,exit2;
        sll $t1,$s1,2;
        add $t2,$s2,$t1;
        lw $t3,0($t2);
        lw $t4,4($t2);
        slt $t0,$t4,$t3;
        beq $t0,$zero,exit2;

//执行swap前要先把需要的数据保存到指定的寄存器

        move $a0,$s2;
        move $a1,$s1;
        jal swap;

//j=j-1然后继续进行内层的循环

        addi $s1,$s0,-1;
        j for2tst;

//如果一开始就结束了内层循环,那么就可以 i=i+1 后直接跳转到外层循环,进

  exit2:addi $s0,$s0,1;
        j for1tst;

//如果内层循环也已经结束了,那么sort函数也就可以返回数值给他的调用者了。并且,要对调用者的寄存器进行还原,不然会干扰程序的正常运作。

  exit1:lw $s0,0($sp);
        lw $s1,4($sp);
        lw $s2,8($sp);
        lw $s3,12($sp);
        lw $ra,16($sp);
        addi $sp,20;

// ra寄存器经过还原后指向调用sort的程序的下一指令,直接返回即可。
        jr $ra;

//swap程序。

swap:
    sll $t1,$a1,2;  
    add $t1,$a0,$t1; 
    lw  $t0,0($t1);  
    lw  $t2,4($t1);  
    sw  $t2,0($t1);
    sw  $t0,4($t1);
    jr $ra;

正文之后

今晚要进行推免的复试报名(其实对于本校的同学貌似就是水一波,但是是必须要走的流程 而且很紧急!)所以现在回到住处,写完文以后就要去找齐所有的证书,幸亏是前阵子统计加分的时候已经整理好了。所以直接拿着用就行了 但是!!为什么缺了一张四级证书!!我还要回去拿!!心疼

原文发布于微信公众号 - 工科狗和生物喵(gh_3507b116a1f8)

原文发表时间:2017-10-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大闲人柴毛毛

柴毛毛大话设计模式——开发常用的设计模式梳理

写在最前 本文是笔者的一点经验总结,主要介绍几种在Web开发中使用频率较高的设计模式。 本文篇幅较长,建议各位同学挑选感兴趣的设计模式阅读。 在阅读的同时,也...

4857
来自专栏Urahara Blog

Using get_defined_functions To Hidden A PHP Backdoor

1762
来自专栏Golang语言社区

Go语言的 10 个实用技术--转

十条有用的 Go 技术   这里是我过去几年中编写的大量 Go 代码的经验总结而来的自己的最佳实践。我相信它们具有弹性的。这里的弹性是指:   某个应用需要适配...

3647
来自专栏程序人生

Promise: 给我一个承诺,我还你一个承诺

处理concurrent programming,除了threading/multi-processing外,各家语言都有自己的绝活:erlang/elixir...

2904
来自专栏Android机动车

设计模式——代理模式

现在有个非常流行的程序叫做面向切面编程(AOP),其核心就是采用了动态代理的方式。怎么用?Java为我们提供了一个便捷的动态代理接口 InvocationHan...

1021
来自专栏java一日一条

编写难于测试的代码的5种方式

有一次,我在一个讲座上听到主持人问听众如何故意编写难于测试的代码。在场的小伙伴都惊呆了,因为没有任何人会故意写这种糟糕的代码。我记得他们甚至给不出一个好的答案。

1152
来自专栏carven

浅谈js的date对象对时间字符串的解析

最近的时间都在开发社团内部的应用–隧道口,虽然只有简单的几个页面,但是依然是遇到了不少坑。 其中 date 的时间处理就是一个。

890
来自专栏有趣的Python

4-C++远征之起航篇-学习笔记

链接: https://pan.baidu.com/s/1SgdThGYaLDyXDFKvaBSa5A 密码: 2333

1294
来自专栏Golang语言社区

无辜的goroutine

简介: 本文主要是针对一些对于goroutine的“指控”提出我自己的看法,特别是轩脉刃的一篇博客文章《论go语言中goroutine的使用》提出了gorout...

36411
来自专栏葡萄城控件技术团队

C#开发人员应该知道的13件事情

本文讲述了C#开发人员应该了解到的13件事情,希望对C#开发人员有所帮助。 1. 开发过程 开发过程是错误和缺陷开始的地方。使用工具可以帮助你在发布之后,解决掉...

2249

扫码关注云+社区

领取腾讯云代金券