程序目标
从键盘输入10个无符号字数并从大到小进行排序,排序结果在屏幕上显示出来。
准备工作
- 编程的入门级知识:循环、冒泡排序、内存和堆栈的概念
- MIPS语法:程序基本结构
- 汇编语言:不同寄存器作用、数据存储、系统调用
- 编辑器:最低级的记事本就够了,保存为.asm文件即可
- PCSpim模拟器:用于运行代码
- MIPS指令的参照表:不会表达的语义随手一查
写在前面
汇编代码的可读性比较差,它的操作变量是寄存器,有时候要用到很多,就有点反应不过来。不过,代码读百遍,其义自现,如果这是你的第一个汇编程序,弄懂之后收获还是很大的。
另,如果你知道小火龙的话,hmmm,那么校友你好!网上有很多这样的程序参考啦,但是不要照搬代码呀,完全理解只需要一个下午,加油!
写汇编的时候一小件事都要写一行代码,就会很吝啬去做一些不必要的操作。比如,在以前用C/C++语言写冒泡内存循环的时候总是会在声明j变量同时初始化为0,现在一点也不想这样做了,因为不是什么顺手就能完成的事。我觉得汇编语言程序员一定是短码编程的忠实粉丝。
思路
首先回忆了一下高级语言中冒泡排序的做法,脑海中有个大致的印象
//输入,存到数组
for(int i = 0; i < 10; ++i) // 从大到小排的冒泡
for(int j = i; j < 10; ++j)
if(a[j] < a[j + 1]) swap(a[j], a[j + 1]);
第一个问题
汇编语言是如何实现从键盘录入数据的?以及它存在哪?
这个问题比较简单,这涉及一组系统调用命令,形如li $v0, 5(读入一个int),不同的数字编号对应着不同的数据类型,读进来的数就放在$v0中,随用随取。
第二个问题
连续读进来一组数放到哪里?
答案是内存,并用堆栈指针寄存器保存数组的起始地址,之后来一个数就把这个地址往后挪4个字节用sw命令保存到内存,形如sw $v0, 0($t1)。
第三个问题
怎样做循环和比较大小?
汇编语言是没有loop这样的循环语句的,但是有跳转语句和条件判断语句。又可以给每个代码块分区,在该段代码前面加上取的名字和“:”,在跳转的时候就可以用上这个名字来指示跳转位置了。比较大小有一个sltu命令,它可以比较后两个操作数的相等关系,并把比较结果(1/0)存到第一个操作数。
其中关于跳转指令,有三种:j XXX 就是单纯的跳转到XXX的位置;jr和jal则与程序调用函数有关。程序调用函数,当函数调用结束后需要重新继续执行原来的程序,所以在调用函数之前,必须先存储函数返回起始点地址,用于存储这一地址的寄存器在MIPS中是$ra。jal的意思就是跳转到某个地址同时把返回调用点的地址存储在$ra中。而jr用法常见jr $ra,一般是函数调用结束后,用于跳转到返回地址。
第四个问题
有结构有调用函数的程序怎么写?
这里头除了跳转的时候要传参数还有一个比较重要的事情就是开辟栈空间来保存需要使用的局部变量。堆栈向内存地址低的方向增长,所以要用几个参数,一般就*4,让堆栈寄存器保存的指针减掉这个值,形如addi $sp, $sp, -20,意为在栈中开辟5个新地址。
图片说明
现在有了上面那些知识,我们已经可以完成程序的大体了。我的程序的结构:
排序的时候时候会发现最好画张图,因为用到的变量比较多,容易忘记寄存器里面装的是什么。
运行结果
写好程序之后放到仿真软件里面里面运行,略微改了几个语法错误之后就可以正常跑了。但是输入数据的时候一开始有点懵逼,不知道要在英文输入法下输。下面是正常运行的截图: