指令:计算机的语言(MIPS) --计算机组成原理(3)

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 完成 值的交换。

2.翻译并且执行程序
参照下面的图片:

指令:计算机的语言(MIPS) --计算机组成原理(3)

链接器的工作分为三个步骤:
• 将代码和数据模块象征性的放入内存
• 决定数据和指令标签的地址
• 修补内部和外部引用

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

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

3.以一个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;

4.以一个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

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

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]

作者:HustWolf
来源:简书

--电子创新网--
粤ICP备12070055号