函数调用 一.调用初印象 最早接触到函数调用是在选择排序程序中,教学视频中代码块来换回拼接导致我看了好几遍视频! 下面附上源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 .data array: .space 400 // 申请数组空间 message_input_n: .asciiz "please input an integer as the length of the sequence\n" message_input_array: .asciiz "please input an integer followed with a line breaker\n" message_output_array: .asciiz "the sorted sequence is:\n" space: .asciiz " " stack: .space 100 // 申请栈空间 .globl main // 在代码段起始位置声明main为全局符号 .text input: la $a0,message_input_n li $v0,4 syscall li $v0,5 // number of integers to be sorted syscall // the number is stored in $v0 move $t0, $v0 // set $t0 to the contents of $v0 li $t1, 0 // 循环变量 for_1_begin: slt $t2, $t1, $t0 // t2=1 if t1 < t0 beq $t2, $zero, for_1_end nop // 目标指令紧跟分支指令 增加延时槽防止并行性引起错误 //下面这段代码实际上是在计算存入地址 // 存入地址 = 首地址 + 循环变量 * 4 la $t2, array / 将数组首地址存入t2 li $t3, 4 mult $t3, $t1 // t3 * t1 高位存入 hi,低位存入lo 事实上一般的乘法,只要结果不超过32位,lo中的值就是完整的答案 mflo $t3 // 将lo寄存器中移动到t3(所有的move指令都相当于赋值语句,复制后原值不会改变,而不是“移动”) addu $t2, $t2, $t3 //在首地址t2基础上加上偏移量t3 la $a0,message_input_array //输出前导字符串 li $v0,4 syscall li $v0, 5 //输入数字 syscall sw $v0, 0($t2) // 从寄存器存入数组中对应的地址 这里的t2就是刚刚计算过的地址 addi $t1, $t1, 1 // 循环变量++ j for_1_begin nop for_1_end: move $v0, $t0 jr $ra //跳回到主程序,跳转语句的下一条语句 nop //与input同理 无需多言! output: move $t0, $a0 li $t1,0 la $a0,message_output_array li $v0,4 syscall for_2_begin: slt $t2, $t1, $t0 beq $t2, $zero, for_2_end nop la $t2, array li $t3, 4 mult $t3, $t1 mflo $t3 addu $t2, $t2, $t3 lw $a0,0($t2) li $v0,1 syscall la $a0,space li $v0,4 syscall addi $t1, $t1, 1 j for_2_begin nop for_2_end: jr $ra nop sort: addiu $sp,$sp,-32 //向低地址移动32字节 move $t0,$a0 //此时a0值即为元素个数 li $t1,0 //循环变量 for_4_begin: slt $t2, $t1, $t0 //选择排序外层循环n-1趟 beq $t2, $zero, for_4_end nop //计算地址 la $t2, array li $t3, 4 mult $t1, $t3 mflo $t3 addu $t2, $t2, $t3 move $a0, $t0 move $a1, $t1 //父函数维护t寄存器 入栈 需要注意的是多层调用时$ra的维护 sw $t2, 28($sp) sw $t1, 24($sp) sw $t0, 20($sp) sw $ra, 16($sp) //这时的ra值需要保存 这里的ra值记录的是返回到主函数的指令地址 经过调用findmin后会变为返回到sort的地址! jal findmin //调用子函数 nop //出栈 lw $ra, 16($sp) lw $t0, 20($sp) lw $t1, 24($sp) lw $t2, 28($sp) //交换值 v0地址与t2地址处存储的值,两个寄存器分别存储 lw $t3, 0($v0) lw $t4, 0($t2) sw $t3, 0($t2) sw $t4, 0($v0) addi $t1,$t1,1 //更新循环变量 j for_4_begin nop for_4_end: addiu $sp,$sp,32 //将申请的栈空间退回,栈指针回到高地址 jr $ra //回到主程序 nop findmin: //从sort中传入 a0值为n, a1值为外层循环变量i la $t0,array sll $a0,$a0,2 subi $a0,$a0,4 //需要注意的是在 * 4的基础上需要减去4, addu $t0,$t0,$a0 //当前的地址是数组中最后一个元素的地址 lw $t1, 0($t0) // t1=a[n-1] move $t2,$t0 move $t3,$t0 // t3 = t2 = t0 = 最后一个元素地址 // a[i+1] la $t0,array sll $a1,$a1,2 addu $t0,$t0,$a1// t0此时为a[i+1]地址 for_3_begin: sge $t4,$t3,$t0 // t4 = 1 if t3 >= t0 beq $t4,$zero,for_3_end nop lw $t5,0($t3) // t5=a[n-1] // 进入查找时 t3为最后一个元素地址 ,t0为a[i+1]地址 //这里寻找最小值的操作实际上是从末尾开始的,先记t1=a[n-1]为最小值,之后由哨兵 t5=遍历到的值 从n-1逐步向前遍历直到 i+1 //不断更新t1的值作为新的最小值,并在t2中保存最小值地址 t3 //这里t1被设置为保存最小值 如果当前遍历的元素小于t1最小值则进入下方更新最小值操作,否则进入if_1_else,顺序进入if_1_end遍历 //下一个元素 slt $t6,$t5,$t1 // t6 = 1 if t5 < t1 第一次运行时 t5 = t1 直接跳转到 if_1_else beq $t6,$zero,if_1_else nop move $t1,$t5 //更新最小值 move $t2,$t3 //保存最小值在数组中的的地址 j if_1_end nop if_1_else:// if_1_else后误操作则直接执行 if_1_end 在标签之间无跳转时按照顺序执行 if_1_end: subi $t3,$t3,4 //t3从末尾向前移动一个元素,更新遍历元素 j for_3_begin nop for_3_end: move $v0,$t2 jr $ra //回到主程序 nop main: la $sp, stack addiu $sp,$sp,100 //此处sp+100是将指针向高地址移动 addiu $sp,$sp,-20 //-20向低地址移动 jal input //跳转到input nop move $t0,$v0 //回到位置 move $a0,$t0 sw $t0, 16($sp) //向高地址偏移量为16 调用者维护t寄存器,将t0入栈 jal sort nop lw $t0,16($sp) // t0出栈 move $a0,$t0 jal output nop addiu $sp,$sp,20 li $v0,10 //程序结束 syscall 上述选择排序算法较为复杂,主体结构为main调用input,sort,output函数,在sort中又调用findmin子函数
...