文章目录
- 寄存器
- 指令
- 使用技巧
- 翻译C/C++
- if/else语句
- switch语句
- for循环
- while 循环
- do...while循环
- 一维数组定义与使用
- 二维数组定义与使用
- 例 :哈密顿回路
- 注意
- 立即数被符号位扩展
- 参考链接
寄存器
Name | Reg. Num | Usage |
---|---|---|
z e r o zero zero | 0 | constant value =0(恒为0) |
a t at at | 1 | reserved for assembler(为汇编程序保留) |
v 0 – v 1 v0 – v1 v0–v1 | 2 – 3 | values for results(过程调用返回值) |
a 0 – a 3 a0 – a3 a0–a3 | 4 – 7 | Arguments(过程调用参数) |
t 0 – t 7 t0 – t7 t0–t7 | 8 – 15 | Temporaries(临时变量) |
s 0 – s 7 s0 – s7 s0–s7 | 16 – 23 | Saved(保存) |
t 8 – t 9 t8 – t9 t8–t9 | 24 – 25 | more temporaries(其他临时变量) |
k 0 – k 1 k0 – k1 k0–k1 | 26 – 27 | reserved for kernel(为OS保留) |
g p gp gp | 28 | global pointer(全局指针) |
s p sp sp | 29 | stack pointer (栈指针) |
f p fp fp | 30 | frame pointer (帧指针) |
r a ra ra | 31 | return address (过程调用返回地址) |
指令
使用技巧
翻译C/C++
if/else语句
if(a >= b) //Do something...
else if(a < c+b) //Do something...
else //Do something...
# $s0 = a, $s1 = b, $s2 = c
if_begin:
bgt $s1, $s0, if_else1
# Do something
if_else1:
add $t0, $s1, $s2
bngt $t0, $s0, if_else2
# Do something
if_else2:
# Do something
if_end:
switch语句
switch(a) {
case 2:
// Do something
break;
case 4:
// Do something
break;
default:
// Do something
}
# $s0 = a
switch_begin:
case_2:
bne $s0, 2, case_4
# Do something
j switch_end
case_4:
bne $s0, 4, default
# Do something
j switch_end
default:
# Do something
switch_end:
for循环
for(int i = 0; i < n; i++) // Do something
# $s0 = n
li $t0, 0
for_begin:
bne $t0, $s0, end_for
# Do something
addi $t0, $t0, 1
j for_begin
end_for:
while 循环
int i = a;
while(i < n) {
// Do something...
i++;
}
# $s0 = n, $s1 = a, $t0 = i
move $t0, $s1
while_begin:
bne $t0, $s0, end_while
# Do something
addi $t0, $t0, 1
j while_begin
end_while:
do…while循环
do {
i++;
//Do something
} while(i < n);
# $t0 = i, $s0 = n
li $t0, 1
dowhile:
addi $t0, $t0, 1
# Do something
beq $s0, $t0, end_dowhile
j dowhile
end_dowhile:
一维数组定义与使用
int arr[100];
for(int i = 0; i < 100; i++) arr[i] = i;
.data
arr: .space 400 #长度100的int型数组,总共使用400字节
.text
# $t0 = i
li $t0, 0;
for_begin:
beq $t0, 100, end_for
move $t1, $t0
sll $t1, $t1, 2 # i*4得到偏移的字节数,MIPS按照字节寻址,地址从x00000000, 0x00000004...以此类推
# 此外MIPS还是小端地址,如果输入0x12345678,那么0x00000002存的是0x56
sw $t0, arr($t1) # 这跟直接访问还挺像的
# 实际上,arr是指一系列空间的首地址,加上偏移量$t1,得到arr[i]的地址
addiu $t0, $t0, 1
j for_begin
end_for:
li $v0, 10 # 类似于C/C++中的return 0
syscall
二维数组定义与使用
#include <iostream>
using namespace std;
int arr[64][64];
int main() {
int m, n;
cin >> n >> m;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++) cin >> arr[i][j];
for(int i = n-1; i >= 0; i--)
for(int j = m-1; j >= 0; j--) cout << i << ' ' << j << ' ' << arr[i][j] << endl;
return 0;
}
.data
arr: .space 16384
# 下面两个宏定义与数组大小密切相关,64*64大小的数组是这么做的
# 我们约定$t7, $t8, $t9只在宏定义中使用
.macro setarr(%d, %i, %j) # 把arr[i][j]设置为d
sll $t8, $t8, 6
add $t9, $t8, %j
sll $t9, $t9, 2
sw %d, arr($t9)
.end_macro
.macro getarr(%d, %i, %j) # 把d赋值为arr[i][j]
sll $t8, $t8, 6
add $t9, $t8, %j
sll $t9, $t9, 2
lw %d, arr($t9)
.end_macro
.text
# $s0 = n, $s1 = m
li $v0, 1
syscall
move $s0, $v0
li $v0, 1
syscall
move $s0, $v0
# $t0 = i, $t1 = j
li $t0, 0
li $t1, 0
for_in_i:
beq $t0, $s0, end_for_in_i
for_in_j:
beq $t1, $s1, end_for_in_j
li $v0, 1
syscall
setarr($v0, $t0, $t1)
addi $t1, $t1, 1
j for_in_j
end_for_in_j:
addi $t0, $t0, 1
j for_in_i
end_for_in_i:
# $t0 = i, $t1 = j
subi $t0, $s0, 1
subi $t1, $s1, 1
for_out_i:
blt $t0, 0, end_for_out_i
for_out_j:
blt $t1, 0, end_for_out_j
getarr($t3, $t0, $t1)
# 输出$t0, $t1, $t2这里省略了
subi $t1, $t1, 1
j for_out_j
end_for_out_j:
subi $t0, $t0, 1
j for_out_i
end_for_out_j:
li $v0, 10 # 类似于C/C++中的return 0
syscall
例 :哈密顿回路
#include <iostream>
using namespace std;
const int MAXN = 10;
int G[MAXN][MAXN];
bool vis[MAXN];
int n, m;
int ans;
int dfs(int i, int start) {
bool flag = false;
vis[i] = true;
cout << i;
for(int j = 1; j <= n; j++) if(!vis[j] && G[i][j]) {
dfs(j, start);
}
for(int j = 1; j <= n; j++) if(!vis[j]) {
flag = true;
break;
}
if(G[i][start] && !flag) ans = 1;
vis[i] = false;
return 0;
}
int main() {
cin >> n >> m;
for(int i = 0; i < m; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u][v] = G[v][u] = 1;
}
for(int i = 1; i <= n; i++) {
dfs(i, i);
if(ans == 1) {
cout << 1 << endl;
return 0;
}
}
cout << 0 << endl;
return 0;
}
.data
graph: .space 1024
vis: .space 512
endl: .word '\n'
.macro setGraph(%data, %i, %j)
sll $t8, %i, 4
add $t8, $t8, %j
sll $t8, $t8, 2
sw %data, graph($t8)
.end_macro
.macro getGraph(%ans, %i, %j)
sll $t8, %i, 4
add $t8, $t8, %j
sll $t8, $t8, 2
lw %ans, graph($t8)
.end_macro
.text
main:
# $s0 = n
li $v0, 5
syscall
move $s0, $v0
# $s1 = m
li $v0, 5
syscall
move $s1, $v0
move $t0, $zero
for_input:
# for(i = 0; i != m; i++)
beq $t0, $s1, end_for_input
li $v0, 5
syscall
move $t1, $v0
li $v0, 5
syscall
move $t2, $v0
li $t3, 1
setGraph($t3, $t1, $t2)
setGraph($t3, $t2, $t1)
addi $t0, $t0, 1
j for_input
end_for_input:
li $t0, 1
for_iter:
# for(i = 1; i <= n; i++)
bgt $t0, $s0, end_for_iter
move $a0, $t0 # $a0 = p
move $a1, $t0 # $a1 = start
jal dfs
beq $s3, 1, print_ans
addi $t0, $t0, 1
j for_iter
end_for_iter:
print_ans:
li $v0, 1
move $a0, $s3
syscall
li $v0, 10
syscall
dfs: # $a0 = p, $a1 = start
addi $sp, $sp, -24
sw $ra, 20($sp)
sw $t0, 16($sp)
sw $t1, 12($sp)
sw $t2, 8($sp)
sw $t3, 4($sp)
sw $t4, 0($sp)
sll $t0, $a0, 2
li $t1, 1
sw $t1, vis($t0)
li $t0, 1
for_nextpoint:
# for(i = 1; i <= n; i++) if(graph[p][i] == 1 && !vis[i])
bgt $t0, $s0, end_for_nextpoint
getGraph($t1, $a0, $t0)
bne $t1, 1, nextpoint_continue
sll $t2, $t0, 2
lw $t3, vis($t2)
bne $t3, $zero, nextpoint_continue
move $t4, $a0
move $a0, $t0
jal dfs
move $a0, $t4
nextpoint_continue:
addi $t0, $t0, 1
j for_nextpoint
end_for_nextpoint:
li $t0, 1
for_check:
bgt $t0, $s0, end_for_check
sll $t1, $t0, 2
lw $t2, vis($t1)
beq $t2, $zero, end_function
addi $t0, $t0, 1
j for_check
end_for_check:
getGraph($t0, $a0, $a1)
bne $t0, 1, end_function
li $s3, 1
end_function:
sll $t0, $a0, 2
sw $zero, vis($t0)
lw $ra, 20($sp)
lw $t0, 16($sp)
lw $t1, 12($sp)
lw $t2, 8($sp)
lw $t3, 4($sp)
lw $t4, 0($sp)
addi $sp, $sp, 24
jr $ra
注意
立即数被符号位扩展
- 算术指令 :add addi sub 总是将立即数做符号位扩展即便指令是无符号的;
乘 、 除 指令 任何情况下都不进行扩展,总是当成 unsigned - 逻辑指令:(andi, ori通常处理无符号数)不对立即 数做符号位扩展
- load / store指令: 地址计算时总是扩展立即数
参考链接
-
谈一谈 MIPS 汇编 Challenge 题:找哈密顿回路:(https://flyinglandlord.github.io/2021/09/30/BUAA-CO-2021/Pre/%E8%B0%88%E4%B8%80%E8%B0%88MIPS%E6%B1%87%E7%BC%96Challenge%E9%A2%98/)
-
流水线 MIPS 处理器的设计:https://zobinhuang.github.io/sec_learning/Tech_Computer_Architerture/Architecture_6_MIPS_Pipeline/#1_intro_1
-
《初学计算机组成原理之MIPS指令集及汇编》: https://blog.csdn.net/weixin_51599896/article/details/123865620
-
MIPS指令详解:https://blog.csdn.net/weixin_46308081/article/details/115798605