一、 实验目标:
了解WinMIPS64的基本功能和作用;
熟悉MIPS指令、初步建立指令流水执行的感性认识;
掌握该工具的基本命令和操作,为流水线实验作准备。
二、实验内容
按照下面的实验步骤及说明,完成相关操作记录实验过程的截图:
1)下载WinMIPS64;运行样例代码并观察软件各个观察窗口的内容和作用,掌握软件的使用方法。(80分)
2)学会正确使用WinMIPS64的IO方法;(10分)
3)编写完整的排序程序;(10分)
三、实验环境
硬件:桌面PC
软件:Windows,WinMIPS64仿真器
四、实验步骤及说明
WinMIPS64是一款指令集模拟器,它是基于WinDLX设计的,如果你对于WinDLX这款软件十分熟悉的话,那么对于WinMIPS64也会十分的容易上手。DLX 处理器 (发音为 "DeLuXe")是Hennessy 和Patterson合著一书《Computer Architecture - A Quantitative Approach》中流水线处理器的例子。WinDLX是一个基于Windows的模拟器。
本教程通过一个实例介绍WinMIPS64的使用方法。WinMIPS64模拟器能够演示MIPS64流水线是如何工作的。
本教程使用的例子非常简单,它并没有囊括WinMIPS64的各个方面,仅仅作为使用WinMIPS64的入门级介绍。如果你想自己了解更多的资料,在给出的winmips64.zip中,有WinMIPS64 — Documentation Summary.html和winmipstut.docx两个文件可以供你随时参考,其中涵盖了WinMIPS64的指令集和模拟器的组成与使用方法。
虽然我们将详细讨论例子中的各个阶段,但你应具备基本的使用Windows的知识。现假定你知道如何启动 Windows,使用滚动条滚动,双击执行以及激活窗口。
(一)、安 装
请按以下步骤在Windows下安装WinMIPS64 :
- 为WinMIPS64 创建目录,例如D:\ WinMIPS64
- 解压给出的winmips64.zip压缩文件到创建的目录中
本文章主要讲解终端IO的简单操作以及用mips指令完成冒泡排序和快速排序。
用终端输出"Hello World!\n"
实验材料给出的CONTROL值对应的相关功能,这里输出字符串,则让CONTROL赋值为4
代码如下:
.data
CONTROL: .word32 0x10000
DATA: .word32 0x10008
HW: .asciiz "Hello World!\n" //STRING
.text
lwu $t8,DATA($zero) ;store DATA in t8
lwu $t9,CONTROL($zero);store CONTROL in t9
daddi $v0,$zero,4 ;set for ascii output
daddi $t1,$zero,HW ;load String in t1
sd $t1,0($t8) ;write address of the string to data
sd $v0,0($t9) ;print
halt
接下来是一个在终端上输出我们在键盘按下的按键的程序
.data
CONTROL: .word32 0x10000
DATA: .word32 0x10008
.text
lwu $t9,CONTROL($zero); load Control
lwu $t8,DATA($zero); load DATA
sd $v0,0($t9);
daddi $t2,$zero,13; 如果按下的按键是回车
LOOP: daddi $v0,$zero,9; CONTROL=9
sd $v0,0($t9);
ld $t1,0($t8); t1 reads the byte
beq $t1,$t2,OUT; 如果输入的是回车则退出循环
sd $t1,DATA($zero); 将输入的按键保存在DATA区
daddi $v0,$zero,4; CONTROL=4
daddi $t1,$zero,DATA;
sd $t1,0($t8);
sd $v0,0($t9); 输出
j LOOP;
OUT: halt;
排序部分:
在这一部分,我们要求编写一个排序算法,对一组int型数据进行排序。该算法使用冒泡排序法,并且在其中嵌入一个swap函数过程(该算法在课本上有完整的程序,但是其中的数据初始化、寄存器映射、命令的映射以及I/O部分还需要自己手动编写)。编写完成后,在asm.exe中进行检测,然后运行。
初始数据要求为:“array: .word 8,6,3,7,1,0,9,4,5,2”
该程序需要对0到10,十个数进行了排序,其中使用了sort和swap两个函数过程,并且swap是嵌套在sort中的,在编写程序的时候一定要注意使用栈来保留寄存器的值,嵌套时还额外需要保存$ra的值。在WinMIPS64运行上述程序,将得到如下结果(这里只给出Terminal窗口的截图即可):
冒泡排序:
.data
CONTROL: .word32 0x10000
DATA: .word32 0x10008
array: .word 8,6,3,7,1,0,9,4,5,2
before: .asciiz "before sort the array is:\n"
after: .asciiz "after sort the array is:\n"
next: .asciiz "\n"
.text
lwu $t8,DATA($zero) ;store DATA in t8
lwu $t9,CONTROL($zero);store CONTROL in t9
daddi $v0,$zero,4;
daddi $t3,$zero,before;
sd $t3,0($t8);
sd $v0,0($t9); print before...
daddi $t2,$zero,10;set t2=10,t2 counts whether is 0
daddi $t1,$zero,0;set t1 = 0
LOOP:
ld $t4,array($t1)
daddi $v0,$zero,2;
sd $t4,0($t8);
sd $v0,0($t9);
daddi $t1,$t1,8; t1+=8
daddi $t2,$t2,-1; t2=-1;
bne $t2,$zero,LOOP;
;now start bubble sorting !!!
daddi $t1,$zero,-1; t1=-1
daddi $t2,$zero,0;
LOOPi: daddi $t1,$t1,1; t1++ 外层for循环
daddi $t2,$t1,1; j = i + 1
LOOPj: dadd $a0,$zero,$t1; 内层for循环
dadd $a1,$zero,$t2;
daddi $t0,$zero,8;
dmul $a0,$t0,$a0;
dmul $a1,$t0,$a1;
jal swap;
BACK: daddi $t2,$t2,1; t2++
slti $t5,$t2,10; if j < 10 t5=1
bne $t5,$zero,LOOPj;
slti $t5,$t1,9; if i < 9 t5=1
bne $t5,$zero,LOOPi;
lwu $t8,DATA($zero) ;store DATA in t8
lwu $t9,CONTROL($zero);store CONTROL in t9
daddi $v0,$zero,4;
daddi $t3,$zero,after;
sd $t3,0($t8);
sd $v0,0($t9); print after...
daddi $t2,$zero,10;set t2=10,t2 counts whether is 0
daddi $t1,$zero,0;set t1 = 0
LOOPPRINT: ld $t4,array($t1)
daddi $v0,$zero,2;
sd $t4,0($t8);
sd $v0,0($t9);
daddi $t1,$t1,8; t1+=8
daddi $t2,$t2,-1; t2=-1;
bne $t2,$zero,LOOPPRINT;
halt
swap:
ld $s0,array($a0); 交换函数,在这里判断是否交换
ld $s1,array($a1);
slt $t0,$s0,$s1;
bne $t0,$zero,EXIT;
sd $s0,array($a1); 完成交换操作
sd $s1,array($a0);
EXIT: jr $ra;
其中,这两部分分别是 开始输出和结尾输出,用于在终端上输出。
lwu $t8,DATA($zero) ;store DATA in t8
lwu $t9,CONTROL($zero);store CONTROL in t9
daddi $v0,$zero,4;
daddi $t3,$zero,before;
sd $t3,0($t8);
sd $v0,0($t9); print before...
daddi $t2,$zero,10;set t2=10,t2 counts whether is 0
daddi $t1,$zero,0;set t1 = 0
LOOP:
ld $t4,array($t1)
daddi $v0,$zero,2;
sd $t4,0($t8);
sd $v0,0($t9);
daddi $t1,$t1,8; t1+=8
daddi $t2,$t2,-1; t2=-1;
bne $t2,$zero,LOOP;
;now start bubble sorting !!!
/
lwu $t8,DATA($zero) ;store DATA in t8
lwu $t9,CONTROL($zero);store CONTROL in t9
daddi $v0,$zero,4;
daddi $t3,$zero,after;
sd $t3,0($t8);
sd $v0,0($t9); print after...
daddi $t2,$zero,10;set t2=10,t2 counts whether is 0
daddi $t1,$zero,0;set t1 = 0
LOOPPRINT: ld $t4,array($t1)
daddi $v0,$zero,2;
sd $t4,0($t8);
sd $v0,0($t9);
daddi $t1,$t1,8; t1+=8
daddi $t2,$t2,-1; t2=-1;
bne $t2,$zero,LOOPPRINT;
快速排序:
如果本身对快速排序不太了解或根本不了解,建议直接去CSDN看相关的快速排序文章。
附上代码前,先讲解一下快速排序的思想。
快排通过以第一个成员作为哨兵位,分别从第二个往后和最后一个往前找,其中要注意,后向前先,找到一个比哨兵位小的成员然后转为前往后找一个比哨兵位大的数,两数交换,接着继续。结束标志为当双方同时到了同一个数,那么将相遇时候的数与哨兵位交换,然后开启新一轮的循环。
这里附上JAVA的快排代码,下面的mips代码在此基础上构建即可。
public static void quicksort(int l,int h,int []arr){
if(h>l){
int i=l,j=h,flag=0;
while(i<j){
if(arr[i]>arr[j]){//符合交换条件
swap(i,j,arr);
if(flag==0){//换方向
i++;
flag=1;
}
else{//换方向
flag=0;
j--;
}
}
else{
if(flag==0)
j--;//此时从后往前
else
i++;//此时从前往后
}
}
quicksort( l,i-1,arr);
quicksort( i+1, h,arr);
}
}
public static void swap(int i,int j,int []arr){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
mips指令如下:
.data
CONTROL: .word32 0x10000
DATA: .word32 0x10008
array: .word 8,6,3,7,1,0,9,4,5,2
before: .asciiz "before sort the array is:\n"
after: .asciiz "after sort the array is:\n"
next: .asciiz "\n"
.text
lwu $t8,DATA($zero) ;store DATA in t8
lwu $t9,CONTROL($zero);store CONTROL in t9
daddi $v0,$zero,4;
daddi $t3,$zero,before;
sd $t3,0($t8);
sd $v0,0($t9); print before...
daddi $t2,$zero,10;set t2=10,t2 counts whether is 0
daddi $t1,$zero,0;set t1 = 0
LOOP:
ld $t4,array($t1)
daddi $v0,$zero,2;
sd $t4,0($t8);
sd $v0,0($t9);
daddi $t1,$t1,8; t1+=8
daddi $t2,$t2,-1; t2=-1;
bne $t2,$zero,LOOP;
;now start bubble sorting !!!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
daddi $t4,$zero,0;
daddi $t5,$zero,0;clear t4andt5
;;$a0=l $a1=h quicksort(0,9)
daddi $a0,$zero,0;
daddi $a1,$zero,9;
daddi $sp,$zero,0x3f8;
jal QSORT;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lwu $t8,DATA($zero) ;store DATA in t8
lwu $t9,CONTROL($zero);store CONTROL in t9
daddi $v0,$zero,4;
daddi $t3,$zero,after;
sd $t3,0($t8);
sd $v0,0($t9); print after...
daddi $t2,$zero,10;set t2=10,t2 counts whether is 0
daddi $t1,$zero,0;set t1 = 0
LOOPPRINT: ld $t4,array($t1)
daddi $v0,$zero,2;
sd $t4,0($t8);
sd $v0,0($t9);
daddi $t1,$t1,8; t1+=8
daddi $t2,$t2,-1; t2=-1;
bne $t2,$zero,LOOPPRINT;
halt;
QSORT: daddi $sp,$sp,-24; a0=l,a1=h
sd $ra,0($sp);
sd $a0,8($sp);
sd $a1,16($sp);
slt $t0,$a0,$a1; if l<h t0=1 if(h>l)
beq $t0,$zero,OUT; if t0==0,end;
dadd $s0,$zero,$a0; s0=i
dadd $s1,$zero,$a1; s1=j
daddi $s2,$zero,0; s2=flag=0;
WHILE: slt $t0,$s0,$s1;if i<j t0=1
beq $t0,$zero,FUNC; while (i<j)
daddi $t7,$zero,8;
dmul $t1,$s0,$t7; t1=[i]
dmul $t2,$s1,$t7; t2=[j]
ld $t4,array($t1); t4=a[i]
ld $t5,array($t2); t5=a[j]
slt $t0,$t5,$t4; if(a[i]<a[j]) t0=1
beq $t0,$zero,else2;
dadd $a0,$t1,$zero; a0=[i]
dadd $a1,$t2,$zero; a1=[j]
jal SWAP; swap(i,j)
bne $s2,$zero,else1; if(flag==0)
daddi $s0,$s0,1; i++;
daddi $s2,$zero,1; flag=1;
j WHILE;
else1: daddi $s2,$zero,0; flag=0
daddi $s1,$s1,-1; j--;
j WHILE;
else2: bne $s2,$zero,else2a; if(flag==0) do;
daddi $s1,$s1,-1; j--
j WHILE;
else2a: daddi $s0,$s0,1;i++;
j WHILE;
;;$s0=i, s1=j;; $a0=l,$a1=h
FUNC: daddi $a1,$s0,-1; a1=i-1
ld $a0,8($sp); a0= l
jal QSORT; QSORT(l,i-1)
ld $a1,16($sp); a1=h
daddi $a0,$s0,1; a0=i+1
jal QSORT;
;QSORT(i+1,h);
OUT: ld $a1,16($sp);
ld $a0,8($sp);
ld $ra,0($sp);
daddi $sp,$sp,24;
jr $ra;
SWAP: ld $s3,array($a0); s3=temp=a[i]
ld $t0,array($a1);
sd $t0,array($a0); a[i]=a1=a[j]
sd $s3,array($a1); a[j]=s0=temp
jr $ra;
相关说明:
前面的输出和结尾的输出功能前面讲过了,这里不再提。
在调用QSORT函数之前,先清空了前面对$t4 $t5寄存器的影响。
对将要转递的参数$a0 $a1初始化,
下面注意对$sp 寄存器的操作,让$sp加上了0x3f8
daddi $t4,$zero,0;
daddi $t5,$zero,0;clear t4andt5
;;$a0=l $a1=h quicksort(0,9)
daddi $a0,$zero,0;
daddi $a1,$zero,9;
daddi $sp,$zero,0x3f8;
jal QSORT;
0x03f8是data窗口能看到的最后一个地址
让$sp寄存器初始化到最后一个地址,之后每次存值的时候都是从最后往上存,可以保证存的数据有效且可视。
如果没有初始化的话,$sp寄存器默认为0,那么将无法保存数据并且无法看到后续的存值情况
在调用快速排序后,一进入函数里,先保存参数和返回地址进内存里面。因为这里是64位的系统,所以寄存器需要8个字节存放。
完成函数即将返回前,需要取出之前存放在内存中的数值。
QSORT: daddi $sp,$sp,-24; a0=l,a1=h
sd $ra,0($sp);
sd $a0,8($sp);
sd $a1,16($sp);
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\PROCEDURE
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
OUT: ld $a1,16($sp);
ld $a0,8($sp);
ld $ra,0($sp);
daddi $sp,$sp,24;
jr $ra;
SWAP函数进行了优化,减少了代码量。
传递参数的时候就让两个参数*8操作,之后在SWAP函数里面可以直接用参数获取到数组成员
daddi $t7,$zero,8;
dmul $t1,$s0,$t7; t1=[i]
dmul $t2,$s1,$t7; t2=[j]
dadd $a0,$t1,$zero; a0=[i]
dadd $a1,$t2,$zero; a1=[j]
jal SWAP; swap(i,j)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
SWAP: ld $s3,array($a0); s3=temp=a[i]
ld $t0,array($a1);
sd $t0,array($a0); a[i]=a1=a[j]
sd $s3,array($a1); a[j]=s0=temp
jr $ra;
如有不懂的地方,可以在留言区咨询博主!