MIPS指令系统及汇编语言

  1. 存储程序概念和MIPS指令字

MIPS Instruction Reference MIPS Assembly Instructions

How to use SYSCALL system services

SYSCALL functions available in MARS

Step 1. Load the service number in register $v0. Step 2. Load argument values, if any, in $a0, $a1, $a2, or $f12 as specified. Step 3. Issue the SYSCALL instruction. Step 4. Retrieve return values, if any, from result registers as specified.

函数调用 寄存器规范

返回地址
$ra
参数
$a0, $a1, $a2, $a3
返回值
$v0, $v1
局部变量
$s0, $s1, …, $s7

Segments

*p 访存

C

int x = 5;
*p = *p + x + 10;

基址映射: p, x 在 $s0, $s1

Assembly

li $s1, 5
lw $t0, 0($s0)
add $t0, $t0, $s1
addi $t0, $t0, 10
sw $t0, 0($s0)

循环 & 数组

C

do {
    g = g + A[i];
    i = i + j;
} while (i != h)

基址映射:g, h, i, j, A[] 分别对应 $s1,$s2,$s3,$s4,%5

Assembly

Loop:
    # load A[i] to register $t1.
    sll $t1, $s3, 2   # $t1 = 4*i ?
    add $t1, $t1, $s5 # $t1 = addr A[i]
    lw $t1, 0($t1)    # $t1 = A[i]
    add $s1, $s1, $t1
    add $s3, $s3, $s4
    bneq $s3, $s2, Loop

不等式

$0
注意 $0 寄存器值 0,可以用来做是否等于 0 的判断。
slt
set less than

C

if (g<h) goto Less;

基址映射: g:$s0, h:$s1

Assembly

slt $t0, $s0, $s1 # set less than, $t0=1 if $s0 < $s1
bne $t0, $0, Less

嵌套函数调用

TODO

整数求和

TODO

Tutorial Demo

		.data

var1:	.word 23
array1:	.byte 'a','b'
array2:	.space 12 

		.text

__start:

		lw $t0, var1	// load word 
		li $t1, 5		// load immidiate
		sw $t1, var1	// store word

	/* Indirect and Based Addressing 立即与间接寻址 */

		// load address
		la	$t0, var1	// **copy RAM address** of var1 into register $t0
		// indirect addressing
		lw	$t2, ($t0)	// **load word** at RAM address contained in $t0 into $t2
		sw	$t2, ($t0) 	// store word in register $t2 into RAM at address contained in $t0
		// based or indexed addressing:
		lw	$t2, 4($t0)	// load word at RAM address ($t0+4) into register $t2
		sw	$t2, -12($t0) // store word in register $t2 into RAM at address ($t0 - 12)
		/* Note: based addressing is especially useful for:
				arrays; access elements as offset from base address
				stacks; easy to access elements at offset from stack pointer or frame pointer */

	/* Arithmetic Instructions 算术指令集
	最多3个操作数
	再说一遍,在这里,操作数只能是寄存器,绝对不允许出现地址
	所有指令统一是32位 = 4 * 8 bit = 4bytes = 1 word */

		add		$t0,$t1,$t2 # $t0 = $t1 + $t2; add as signed (2's complement) integers
		sub		$t2,$t3,$t4	#  $t2 = $t3 Ð $t4
		addi	$t2,$t3, 5	#  $t2 = $t3 + 5; "add immediate" (no sub immediate)
		addu	$t1,$t6,$t7	#  $t1 = $t6 + $t7; add as unsigned integers
		subu	$t1,$t6,$t7	#  $t1 = $t6 + $t7;   subtract as unsigned integers
		mult	$t3,$t4		#  multiply 32-bit quantities in $t3 and $t4, and store 64-bit
							#  result in special registers Lo and Hi:  (Hi,Lo) = $t3 * $t4
		div		$t5,$t6		#  Lo = $t5 / $t6   (integer quotient)
							#  Hi = $t5 mod $t6   (remainder)
	            #  商数存放在 lo, 余数存放在 hi
		mfhi	$t0			#  move from hi, move quantity in special register Hi to $t0:   $t0 = Hi
	          	#  不能直接获取 hi 或 lo中的值, 需要mfhi, mflo指令传值给寄存器
		mflo	$t1			#  move from lo, move quantity in special register Lo to $t1:   $t1 = Lo
							#  used to get at result of product or quotient
		move	$t2,$t3	#  $t2 = $t3

	/* Control Structures 控制流 */

		// Branches 分支(if else系列)
		// comparison for conditional branches is built into instruction
		b		target		#  unconditional branch to program label target
		beq		$t0,$t1,target	#  branch to target if  $t0 = $t1
		blt		$t0,$t1,target	#  branch to target if  $t0 < $t1
		ble		$t0,$t1,target	#  branch to target if  $t0 <= $t1
		bgt		$t0,$t1,target	#  branch to target if  $t0 > $t1
		bge		$t0,$t1,target	#  branch to target if  $t0 >= $t1
		bne		$t0,$t1,target	#  branch to target if  $t0 <> $t1
Jumps

		// 跳转(while, for, goto系列)
		j		target			#  unconditional jump to program label target
								#  看到就跳, 不用考虑任何条件
		jr		$t3				#  jump register: jump to address contained in $t3 ("jump register")
                #  类似相对?间接寻址,跳到该寄存器给出的地址处

		// Subroutine Calls 子程序调用
		// subroutine call: "jump and link" instruction
		jal	sub_label	#  "jump and link"
		/* 	1. copy program counter (return address) to register $ra (return address register)
			将当前的程序计数器保存到 $ra 中
			2. jump to program statement at sub_label */
		// subroutine return: "jump register" instruction
		jr	$ra	#  "jump register"
		/* jump to return address in $ra (stored by jal instruction)
			通过上面保存在  $ra 中的计数器返回调用前
			Note: return address stored in register $ra; if subroutine will call other subroutines, or is recursive, return address should be copied from $ra onto stack to preserve it, since jal always places return address in this register and hence will overwrite previous value 
			如果说调用的子程序中有调用了其他子程序,如此往复, 则返回地址的标记就用 栈(stack) 来存储, 毕竟 $ra 只有一个, (哥哥我分身乏术啊~~)。 */

	/* System Calls and I/O  系统调用 与 输入/输出 
	通过系统调用实现终端的输入输出,以及声明程序结束
	- 学会使用 syscall
	- 参数所使用的寄存器:$v0, $a0,  $a1
	- 返回值使用: $v0 

	- The print_string service expects the address to start a null-terminated character string. The directive .asciiz creates a null-terminated character string.
	大概意思是要打印的字符串应该有一个终止符,估计类似C中的'\0', 在这里我们只要声明字符串为 .asciiz 类型即可。下面给个我用Mars4.4的提示:
	.ascii 与 .asciiz唯一区别就是 后者会在字符串最后自动加上一个终止符, 仅此而已
	- The read_int, read_float and read_double services read an entire line of input up to and including the newline character.
	对于读取整型, 浮点型,双精度的数据操作, 系统会读取一整行,(也就是说以换行符为标志 '\n')
	- The read_string service has the same semantices as the UNIX library routine fgets.
		- It reads up to n-1 characters into a buffer and terminates the string with a null character.
		- If fewer than n-1 characters are in the current line, it reads up to and including the newline and terminates the string with a null character.
	这个不多说了,反正就是输入过长就截取,过短就这样,最后都要加一个终止符。
	- The sbrk service returns the address to a block of memory containing n additional bytes. This would be used for dynamic memory allocation.
	上边的表里已经说得很清楚了。
	- The exit service stops a program from running.
	*/

		// e.g. Print out integer value contained in register $t2
		// 栗子:  打印一个存储在寄存器 $2 里的整型

		li		$v0, 1		# load appropriate system call code into register $v0;
       		    # 声明需要调用的操作代码为 1 (print_int) 并赋值给 $v0
							# code for printing integer is 1
		move	$a0, $t2	# move integer to be printed into $a0:  $a0 = $t2
							# 将要打印的整型赋值给 $a0
		syscall				# call operating system to perform operation


		// e.g.   Read integer value, store in RAM location with label int_value (presumably declared in data section)
		// 栗子:  读取一个数,并且存储到内存中的 int_value 变量中

		li		$v0, 5			# load appropriate system call code into register $v0;
								# code for reading integer is 5
              	# 声明需要调用的操作代码为 5 (read_int) 并赋值给 $v0 
		syscall					# call operating system to perform operation、
              	# 经过读取操作后, $v0 的值已经变成了 输入的 5
		sw		$v0, int_value	# value read from keyboard returned in register $v0;
								# store this in desired location
                # 通过写入(store_word)指令 将 $v0的值(5) 存入 内存中         

		// e.g.   Print out string (useful for prompts)
		// 栗子: 打印一个字符串(这是完整的,其实上面栗子都可以直接替换main: 部分,都能直接运行)

		.data
string1		.asciiz	"Print this.\n"	# declaration for string variable, 
									# .asciiz directive makes string null terminated

		.text
main:		li		$v0, 4			# load appropriate system call code into register $v0;
									# code for printing string is 4
                  # 打印字符串, 赋值对应的操作代码 $v0 = 4
			la	$a0, string1		# load address of string to be printed into $a0
      						# 将要打印的字符串地址赋值  $a0 = address(string1)
			syscall					# call operating system to perform print operation


	// e.g. To indicate end of program, use exit system call; thus last lines of program should be:
	// 执行到这里, 程序结束, 立马走人, 管他后边洪水滔天~~

		li	$v0, 10		    		# system call code for exit = 10
		syscall						# call operating sys