「MIPS」语法
变量的声明与定义
使用的是伪指令,这些伪指令主要用途是标识数据段和代码段的位置,并为声明的数据分配空间。
1 |
|
Help文档MIPS->Directives
一栏,对所有的伪指令都做了简要的介绍
1) .data
格式:.data [address]
- 定义程序的数据段,初始地址为address,若无address参数,初始地址为设置的默认地址。
- 需要用伪指令声明的程序变量需要紧跟着该指令。比如该程序中的fibs, size, space, head这四个变量。
2) .text
格式:.text [address]
- 定义程序的代码段,初始地址为address,若无address参数,初始地址为设置的默认地址。
- 该指令后面就是程序代码。
- 在MARS中如果前面没有使用.data伪指令,可以不使用.text直接编写程序代码,代码将放置在前面设置的代码段默认地址中,但如果前面使用了.data伪指令,务必在代码段开始前使用.text进行标注。
3) .space
格式:[name]: .space [n]
说明:
- 申请n个字节未初始化的内存空间,类似于其他语言中的数组声明。
- 这段数据的初始地址保存在标签name中。
- name的地址是由.data段的初始地址加上前面所申请的数据大小计算得出的。由于前面申请的空间大小不定,有可能会出现后来申请的空间没有字对齐的情况,从而在使用sw,lw一类指令时出现错误,所以在申请空间时尽可能让n为4的倍数,防止在数据存取过程中出现问题。
- 在本例中,事先申请了48个字节也就是12个字的内存空间,用来保存我们之后计算出来的12个Fibonacci数,地址标签为fibs。
4) .word
格式:[name]: .word [data1],[data2] ….
- 在内存数据段中以字为单位连续存储数据data1,data2,… (也就是将datax写入对应的1个字的空间,注意.word和.space的区别)
- 这段数据的初始地址保存在标签name中。计算方式与上面相同。
- 在本例中,把需要计算的Fibonacci数的个数保存在了内存数据段,地址标签为size。
3) .asciiz
格式:[name]: . asciiz “[content]”
- 以字节为单位存储字符串,末尾以NULL结尾。
这个字符串在内存数据区的初始地址保存在标签name中。
注意.asciiz
与.ascii
这两条伪指令的区别。.ascii
,字符串的末尾没有结束符’\0’ -
.asciiz
由于是按字节存储,可能会导致之后分配的空间首地址无法字对齐的情况发生 - 本例中,声明了两个字符串,一个是输出时需要用到的空格符,一个是输出语句,地址标签分别为space和head。
宏
可实现代码的复用。宏分为两种,不带参数的宏和带参数的宏。
无参数
不带参数的宏,定义的方式如下:
1 | macro_name |
例如:
1 | done |
此时,在需要程序停止运行的地方,使用done语句,就可以让程序在那里退出。汇编器会把所有的macro_name语句替换成代码段;
带参数
1 | j) getindex(%ans,%i,% |
%i代表行数,%j代表列数,%ans就是计算出来的结果((%i*8+%j)*4)。使用getindex($t2,$t0,$t1)来调用这个宏,汇编器会用这段代码替换它,同时%ans被替换成$t2,%i被替换成$t0,%j被替换成$t1
.eqv
1 | .eqv EQV_NAME string |
汇编器会把所有EQV_NAME的地方替换成string,这可以用来定义一些常量。
分配寄存器
循环运算
系统调用
1. 字符串输出
1 | la $a0,addr |
- 首先把要输出的字符串在内存中的首地址赋给$a0寄存器,然后汇编器就会根据$a0中的地址将字符串输出。
- 在内存中存储的字符串是以NULL(‘\0’)作为结束符,输出时遇到这个结束符就会停止。
2. 整数输出
1 | li $v0,1 |
- 这个系统调用的功能就是把$a0寄存器中的数据以整数的形式输出。
3. 结束程序
1 | li $v0,10 |
注意
$v0 = 8时
1 | $a0 = address of input buffer $a1 = maximum number of characters to read |
则最多读取字符是$a1-1
条件语句
1 | 1 |
第5行判断是否跳转决定了到底是执行do something还是执行do something else。如果判断条件($t1<$t2)
为真,即$t3==1
,则不会跳转,继续执行do something,否则$t3==0
,会跳转到if_1_else
继续执行do something else
循环语句
1 | 1 |
对任何一个跳传指令后面,要加上一个空转指令(NOP)。从而使得CPU的PIPELINE不会错误的执行一个预取(PRE_FETCH)得指令。当然这个NOP可以替换为别的,放一个NOP是最简单和安全的