MIPS汇编程序设计——冒泡排序
2017-04-20 09:59
302 查看
实验内容
从键盘输入10个无符号字数并从大到小进行排序,排序结果在屏幕上显示出来。实验步骤
设计遵循以下步骤:①C语言设计 ②单元代码转写及变量映射 ③单元伪汇编设计 ④单元测试及debug ⑤回到步骤①,直到程序完成
代码设计
整个程序可以分为三个单元:读入读入单元,排序单元,输出单元。由于各单元的设计方法,下面以读入单元为例展开叙述。1.首先我们进行C语言设计
//read in int arr[10]; for (int i =0; i <10; i++) { scanf("%d", &arr[i]); }
可以看到,读入单元十分简单,一个for循环,然后读入数据到arr[i]就好了。
2.变量映射及代码转写
变量映射,就是把涉及到的变量对应为寄存器。代码转写,就是把其中的循环,及分支判断用goto转写成顺序结构语句,虽然实际运行的时候是有循环的。各个涉及变量映射到对应寄存器,如表1所示。
而对于上一个步骤的C代码,我们可以如下转写:
申请空间,基准地址为arr,长度为10 int,即4 word int i = 0; Loop: 偏移量bias = i * 4; 实际地址 addr = arr + bias; 读入数据到 tmp; arr[addr] = tmp; i = i + 1; if (i < 10) goto Loop;
3.单元伪代码设计
3.单元伪汇编设计根据上一步骤的伪代码转写和变量映射,我们查询相关的MIPS指令集和系统调用后,可以写出以下MIPS指令。
li $v0, 9 #9号syscall,请求内存空间 li $a0, 40 #申请40byte,4word的空间大小 syscall #系统调用 add $s1, $v0, $zero #加载基准内存地址 add $s0, $zero, $zero # $s0 就是 int i read: #读数开始 li $v0, 5 #从键盘读整数 syscall #系统调用,读到$v0中 sll $t0, $s0, 2 #偏移量 $t0 =i*4 add $t1, $t0, $s1 #$t1 实际地址 = 偏移量$t0 + 基准地址 sw $v0, 0($t1) #写入到实际地址 $t1 #读数结束 addi $s0, $s0, 1 #i = i + 1 slti $t0, $s0, 10 #若i<10 bne $t0, $zero, read #则继续循环
4.单元测试与debug
单元测试就是根据我们写出来的单元,输入数据,运行程序,观察输出结果是否与预期一致,如一致则说明MIPS代码正确;否则,单步执行,观察寄存器变量与预期不符的代码行,对该行代码进行检查及修改。如图4所示,我们开始运行程序,并输入10个数字。
观察DATA区,如图5,发现内存存的数与我们输入的一致,说明该单元没有问题。
5.完成其他模块,组合成最终程序
同样的道理,我们可以对其他的单元进行设计、实现、测试。完成所有模块并测试debug完成后,最终运行结果如图6.完整代码如下:
#************************** #从键盘读入数据、排序并输出 #************************** .text #代码段 .globl main #程序从此开始 main: #主程序 li $v0, 9 #9号syscall,请求内存空间 li $a0, 40 #申请40byte,4word的空间大小 syscall #系统调用 add $s1, $v0, $zero #加载基准内存地址 add $s0, $zero, $zero # $s0 就是 int i read: #读数开始 li $v0, 5 #从键盘读整数 syscall #系统调用,读到$v0中 sll $t0, $s0, 2 #偏移量 $t0 =i*4 add $t1, $t0, $s1 #$t1 实际地址 = 偏移量$t0 + 基准地址 sw $v0, 0($t1) #写入到实际地址 $t1 #读数结束 addi $s0, $s0, 1 #i = i + 1 slti $t0, $s0, 10 #若i<10 bne $t0, $zero, read #则继续循环 sort: add $s0, $zero, $zero # $s0 就是 int i = 0 oLop: #外层循环开始 addi $s2, $zero, 9 #$s2 就是 int j = 9 iLop: #内层循环开始 sll $t0, $s2, 2 #偏移量j*4 add $t1, $s1, $t0 #A[j]的实际内存地址 addi $t2, $t1, -4 #A[j-1]的实际内存地址 lw $t3, 0($t1) #$t3=A[j]的值 lw $t4, 0($t2) #$t4=A[j-1]的值 slt $t5, $t4, $t3 #若A[j-1] < A[j] beq $t5, $zero, afterSwap #为真交换,否则跳过 #swap lw $t6, 0($t1) # tmp=A[j] sw $t4, 0($t1) # A[j] = A[j-1] sw $t6, 0($t2) # A[j-1]=tmp afterSwap: addi $s2, $s2, -1 # j = j - 1 slt $t0, $s0, $s2 #若i < j bne $t0, $zero, iLop #继续内层循环 addi $s0, $s0, 1 # i = i + 1 slti $t0, $s0, 9 #若i<9 bne $t0, $zero, oLop #则继续外层循环 #外层循环结束 #print #基准地址是$s1 add $s0, $zero, $zero # $s0 就是 int i print: # 输出开始 li $v0, 1 #置输出int函数ID sll $t0, $s0, 2 #偏移量 $t0 =i*4 add $t1, $t0, $s1 #$t1 实际地址 = 偏移量$t0 + 基准地址 lw $a0, 0($t1) #写入输出参数到 $a0 syscall #系统调用,输出排序后整数 li $v0, 11 #置输出char函数ID add $a0, $zero, 32 #置输出参数: 空格 syscall #输出空格 addi $s0, $s0, 1 #i = i + 1 slti $t0, $s0, 10 #若i<10 bne $t0, $zero, print #则继续循环 li $v0, 10 #停 syscall #机
实验心得
1.查阅文档,忌臆想
在写出伪代码转写后,我需要对应的伪汇编指令及系统调用来实现,但是我不知道这些指令,以及他们的用法。我开始尝试看着示例尝试性地打,发现频频出错,甚至有时语法检查都不通过。做了许多无用功后开始反思,去查指令集,看课本对指令的说明解析,包括参数的含义及写法。2.清晰的设计实现流程:C代码-伪代码转写-伪汇编-测试及修正
一开始不知道如何想,因为汇编代码不像C那样有着清晰的循环,分支结构,反而是指令的顺序排列,这并不易读不易想。遵循上述设计流程,则将任务分化了,写伪汇报时只要照着伪代码打就好了。3.模块化设计
C语言中,循环语句和循环块是可以分离的。同样的道理,在对应的伪汇编中,循环语句和循环块也可以分离。这样,我们在输入单元中成功设计了一个循环,只要将循环语句提取出来,两个嵌套,便可得到排序单元中的双重循环框架。这样,在排序单元中,我们便避免了对双重循环的重新设计。4.注释
注释具有十分重要作用,可以说明个个寄存器的作用,设计时减少了记忆负担。可以注释每一条MIPS指令的作用,debug时可以通过检查注释与MIPS指令是否一致来排查错误。相关文章推荐
- 计算机组成原理之MIPS汇编:冒泡排序
- MIPS汇编:冒泡排序
- 用汇编语言实现冒泡排序——基于MIPS指令系统
- 汇编之分支程序设计
- Intel汇编程序设计-整数算术指令(上)
- 用汇编与C实现冒泡排序以及一点思考
- 第四章 指令系统与汇编程序设计
- mips汇编打印 hello world
- Coursera - 计算机组成 - W3 - MIPS汇编程序设计
- mips 汇编入门——helloworld
- 汇编循环程序设计小实习
- 汇编语言的模块化程序设计(1004)
- ARM汇编程序设计之--数据搬移
- 汇编学习---分支结构程序设计
- as汇编基础程序设计--调用C库和内存访问
- 汇编——多模块程序设计(主模块)
- C程序设计的常用算法汇编
- MIPS体系下的汇编
- 汇编语言——冒泡排序
- 汇编语言程序设计:计数1+2+3+…+100的值并打印