文章目录
MIPS练习合集
碎碎念
写在前面:
作为。。。考了两次P2的憨憨,不得不说对P2的各种题型也有了比较深的理解,总结如下,希望能对后来的学弟学妹有所启发吧。
yysy,把下面的题都学会学懂了p2必不挂www
必看提示
首先需要会的一些基本操作,比如用一维数组存放数据和二维矩阵存放数据。
其次P2的题型主要分为三大类。第一类是简单数学应用,如关于素数、回文数、水仙花数、阶乘的一系列求值等。第二类是简单程序转化,即将C语言的一些典型题目用mips表示出来,如约瑟夫环、高精度阶乘等。第三类是含有递归的程序,需要注意mips中各个寄存器的保护机制(强保护/弱保护),以及栈的使用方法,典型题目包括全排列、汉诺塔等。
1.1回文串判断
-
判断输入的字符串是不是回文串。
-
输出一个字符,是回文串输出1,否则输出0
-
每组数据最多执行100,000条指令。
-
请使用syscall结束程序:
-
li $v0,10 syscall
输入格式
第一行为一个整数n,代表字符串的长度。第二行开始的n行:每行一个字符(小写字母),连起来为输入的字符串。(0<n<=20)
输出格式
输出为一行,输出一个字符,是会文串输出1,否则输出0。
输入样例
5
a
b
b
d
l
输出样例
0
提交要求
- 请勿使用
.globl main
- 不考虑延迟槽
- 只需要提交.asm文件。
- 程序的初始地址设置为Compact,Data at Address 0。
注意事项
-
注意!因为评测机的行为和MARS有一些区别,你需要注意以下事项。
-
如果你采取每次读入一个字符的系统调用($v0=12)来读入数据,那么我们保证你不会读入到任何换行符。如果你采取这种方式输入,那么对于样例,你可以在MARS中首先手动输入5,打回车,然后手动在一行之中输入abbdl。
-
如果你采取一次读一行的系统调用($v0=8),那么你读入的每行有一个小写字母以及行尾的一个换行符。
-
如果你的程序长时间等不到应有的输入,则有可能提示超时或运行错误。
-
在你处理字符的时候,你需要考虑到上述情况。
思路:
- 回文串判断:从字符串的两头开始比较,即第1个字符和倒数第1个字符比较,第2个字符和倒数第2个字符比较··· 如果出现字符不相等的情况,说明不是回文,如果全部相等,说明是回文。
- 选择每次读入一个字符的方式,则需要先读入n,再一次读入每个字符并存储在地址中
- 设置两个变量a和b,a从0开始逐次加一,b从n开始逐次减一,如果a和b对应的字符一直相同则继续,反之,如果不同则直接输出0,结束程序
- 当a>=b时可以判断是回文串,程序结束
具体实现:
宏定义+数据:
.macro get_address(%des,%column)
addu %des,%des,%column
sll %des,%des,2
.end_macro
.data
string:.word 20
输入:
li $v0,5
syscall
move $s0,$v0 #n
li $t0,0
read:
li $v0,12
syscall
sll $t1,$t0,2
sw $v0,string($t1)
addi $t0,$t0,1
bne $t0,$s0,read
判断:
li $t0,0 #a
subi $t1,$s0,1 #b
check:
li $t2,0
li $t3,0
get_address($t2,$t0)
get_address($t3,$t1)
lw $t4,string($t2) #s[a]
lw $t5,string($t3) #s[b]
bne $t4,$t5,false
addi $t0,$t0,1
subi $t1,$t1,1
blt $t0,$t1,check
j true
false:
li $a0,0
li $v0,1
syscall
j end
true:
li $a0,1
li $v0,1
syscall
j end
end:
li $v0,10
syscall
坑点小结:
- emmm我觉得没啥坑点,真要算还是得到的数据得清零叭(因为这次不是矩阵,所以要得到相应地址只需要直接相加后左移,所以如果不清零得到的是累积地址)
- 另外,dl可以尝试用栈来写(蒻蒟放弃kkkk)
2.1约瑟夫环
问题描述:
N个人围成一圈,从第一个人开始报数,报到m的人出圈,剩下的人继续从1开始报数,报到m的人出圈;如此往复,直到所有人出圈。(模拟此过程,输出出圈的人的序号)。
输入:输入两个整数n,m。
输出:依次输出每次出圈的人的序号。
C代码:
#define length 100
void Joseph(int n,int m)
{
int alive=n;
int num=0;
int index=0;//下标,==编号-1
int cunzai[length];
for(int i=0;i<length;i++)
cunzai[i]=1;
while(alive>0)
{
num+=cunzai[index];//计数,每轮到一个人报数即num+1
if(num==m)//满足淘汰条件
{
print("%d ",index+1);
cunzai[index]=0;
alive--;
num=0;
}
index=(index+1)%n;//保证每次的下标都在0-n-1的范围内
}
print("\n");
}
int main()
{
scanf("%d",&n);
scanf("%d",&m);
Joseph(n,m);
return 0;
}
MIPS翻译:
.macro address(%des,%column)
li %des,0
addu %des,%des,%column
sll %des,%des,2
.end_macro
.data
cunzai:.word 0:47
space:.asciiz" "
enter:.asciiz"\n"
.text
li $v0,5
syscall
move $s0,$v0 #输入n
li $v0,5
syscall
move $s1,$v0 #输入m
li $t0,0
li $t2,1
initial:
address($t1,$t0)
sw $t2,cunzai($t1)
addi $t0,$t0,1
bne $t0,47,initial
Joseph:
move $t0,$s0 #alive,当前幸存人数,初始值为n
li $t1,0 #计数num,当$t1==$s1-1时淘汰这个人
li $t2,0 #下标index,为当前的总人数-1
while_begin:
blez $t0,while_end
address($t3,$t2)
lw $t4,cunzai($t3) #cunzai[index]
addu $t1,$t1,$t4 #number += 1- circle[index];
bne $t1,$s1,if_end #还未数到m,继续
addi $a0,$t2,1
li $v0,1
syscall #print(index+1)
la $a0,enter
li $v0,4
syscall #print(" ")
li $t4,0
sw $t4,cunzai($t3) #circle[index]=0;
subi $t0,$t0,1 #alive--;
li $t1,0 #num=0;
if_end:
addi $t4,$t2,1
divu $t4,$s0
mfhi $t2 #index = (index +1) % count;
j while_begin
while_end:
la $a0,enter
li $v0,4
syscall
li $v0,10
syscall
2.2高精度阶乘
问题描述
任务
- 使用MIPS汇编语言编写一个求n的阶乘的汇编程序(不考虑延迟槽)。
具体要求
-
第一行读取n
-
计算并输出n的阶乘,输出字符串长度小于等于1000
-
步数限制为200,000
-
请使用syscall结束程序:
li $v0, 10 syscall
样例1
-
给出以下输入:
24
-
期望的输出为:
620448401733239439360000
C语言代码
#include<stdio.h>
#define MAXSIZE 10000
void func(int n);
int main()
{
int n;
scanf("%d",&n);
func(n);
return 0;
}
void func(int n)
{
int i,j;
int tmp;
int digit;//位数
int carry;//进位
int s[MAXSIZE];
digit=1;
carry=0;
s[1]=1;
for(i=2;i<=n;i++)//从1乘到n,由于已经初始化,i从2开始取
{
for(j=1;j<=digit;j++)//逐位计算
{
tmp=s[j]*i+carry;
s[j]=tmp%10;
carry=tmp/10;
}
while(carry)
{
s[j]=carry%10;
carry/=10;
j++;
}
digit=j-1;
}
for(i=digit;i>0;i--)
printf("%d",s[i]);
printf("\n");
return;
}
mips代码
.macro address(%des,%column)
li %des,0
addu %des,%des,%column
sll %des,%des,2
.end_macro
.data
s:.word 1000000
.text
li $v0,5
syscall
move $s0,$v0 #n
li $t0,0
beq $t0,$s0,special
li $t0,1
beq $t0,$s0,special
func:
li $t2,0 #tmp
li $t3,1 #digit
li $t4,0 #carry
li $t5,1 #just 1
address($t6,$t5)
sw $t5,s($t6) #s[1]=1
li $t0,2 #i
circulation1:
li $t1,1 #j
circulation2:
address($t5,$t1)
lw $t6,s($t5) #s[j]
multu $t6,$t0
mflo $t6 #s[j]*i
addu $t2,$t6,$t4 #tmp=s[j]*i+carry
li $t6,10
divu $t2,$t6
mfhi $t6 #hi余数
sw $t6,s($t5) #s[j]=tmp%10;
mflo $t4 #carry=tmp/10;
addi $t1,$t1,1
ble $t1,$t3,circulation2
beqz $t4,part_end
while:
li $t6,10
divu $t4,$t6
mfhi $t5 #carry%10
address($t7,$t1)
sw $t5,s($t7) #s[j]=carry%10;
divu $t4,$t4,10 #carry/=10;
addi $t1,$t1,1
bnez $t4,while
part_end:
subi $t3,$t1,1
addi $t0,$t0,1
ble $t0,$s0,circulation1
move $t0,$t3 #i=digit
circulation3:
address($t1,$t0)
lw $a0,s($t1)
li $v0,1
syscall #printf("%d",s[i]);
subi $t0,$t0,1
bne $t0,0,circulation3
end:
li $v0,10
syscall #return
special:
li $a0,1
li $v0,1
syscall
3.1全排列
-
使用mips实现全排列生成算法。
-
以0x00000000为数据段起始地址。
-
输入一个小于等于6的正整数,求出n的全排列,并按照字典序输出。
-
每组数据最多执行500,000条指令。
-
请使用syscall结束程序:
-
li $v0,10 syscall
输入格式
只输入一行,输入一个整数n**(0<n<=6)**
输出格式
按照字典序输出n!行数组,每行输出n个数字,数字之间以空格隔开,每行最后一个数字后可以有空格。
寄存器分配
C语言变量 | 寄存器分配 |
---|---|
n | $s0 |
index | $t0 |
i | $t1 |
C代码提示
#include<stdio.h>
#include<stdlib.h>
int symbol[7],array[7];
int n;//n的全排列
void FullArray(int index)
{
int i;
if (index>=n)
{
for (i=0;i<n;i++)//circulation1
printf("%d ",array[i]);
printf("\n");
return;
}
//part2
for (i=0;i<n;i++)//circulation2
{
if (symbol[i]==0)//not->condition2
{
array[index]= i+1;
symbol[i]=1;
FullArray(index+1);
symbol[i]=0;
}
}
}
int main()
{
int i;
scanf("%d",&n);
FullArray(0);
return 0;
}
输入样例
4
输出样例
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
提交要求
- 请勿使用
.globl main
- 不考虑延迟槽
- 只需要提交.asm文件。
- 程序的初始地址设置为Compact,Data at Address 0。
程序实现
.macro address(%des,%column)
li %des,0
addu %des,%des,%column
sll %des,%des,2
.end_macro
.data
array:.space 40
symbol:.space 40
str_enter:.asciiz "\n"
str_space:.asciiz " "
.text
main:
li $v0,5
syscall
move $s0,$v0 #n
li $a0,0#index
jal f
li $v0,10
syscall
f:
blt $a0,$s0,part2
li $t0,0#i
circulation1:
address($t1,$t0)
lw $a0,array($t1)
li $v0,1
syscall
la $a0,str_space
li $v0,4
syscall
addi $t0,$t0,1
bne $t0,$s0,circulation1
la $a0,str_enter
li $v0,4
syscall
jr $ra
part2:
li $t0,0#i
circulation2:
address($t1,$t0)
lw $t2,symbol($t1)
bnez $t2,condition2
address($t1,$a0)
addi $t2,$t0,1
sw $t2,array($t1)
li $t2,1
address($t1,$t0)
sw $t2,symbol($t1)
subi $sp,$sp,12
sw $t0,8($sp)
sw $ra,4($sp)
sw $a0,0($sp)
addi $a0,$a0,1
jal f
lw $t0,8($sp)
lw $ra,4($sp)
lw $a0,0($sp)
addi $sp,$sp,12
address($t1,$t0)
sw $0,symbol($t1)
condition2:
addi $t0,$t0,1
bne $t0,$s0,circulation2
jr $ra
3.2汉诺塔
如果不清楚是个啥问题。。。可以自行百度。
行走次数问题
递推公式:
H
n
=
H
n
−
1
∗
2
+
1
Hn=Hn-1 * 2+1
Hn=Hn−1∗2+1
通项公式:
H
n
=
2
n
−
1
Hn=2^n-1
Hn=2n−1
行走方式问题
#include<stdio.h>
void move(char from,char tmp,char to,int n)
{
if(n==1)
{
printf("%c-->%c\n",from,to);
return;
}
move(from,to,tmp,n-1);
move(from,tmp,to,1);
move(tmp,from,to,n-1);
return;
}
int main()
{
int n;
scanf("%d",&n);
char from="A",tmp="B",to="C";
move(A,B,C,n);
return 0;
}
程序实现
.macro swap(%a,%b)
move $t0,%a
move %a,%b
move %b,$t0
.end_macro
.macro save(%a) #%a里是要存储的值
subi $sp,$sp,4
sw %a,0($sp)
.end_macro
.macro load(%a)#%a里是要得到的值
lw %a,0($sp)
addi $sp,$sp,4
.end_macro
.data
str_connect:.asciiz "-->"
str_enter:.asciiz "\n"
str_from:.asciiz "A"
str_tmp:.asciiz "B"
str_to:.asciiz "C"
.text
li $v0,5
syscall
move $s0,$v0
la $a0,str_from
la $a1,str_tmp
la $a2,str_to
move $a3,$s0
jal f_move
li $v0,10
syscall
f_move:
beq $a3,1,equal
save($ra)
save($a0)
save($a1)
save($a2)
save($a3)
subi $a3,$a3,1
swap($a1,$a2)
jal f_move
load($a3)
load($a2)
load($a1)
load($a0)
load($ra)
save($ra)
save($a0)
save($a1)
save($a2)
save($a3)
li $a3,1
jal f_move
load($a3)
load($a2)
load($a1)
load($a0)
load($ra)
save($ra)
save($a0)
save($a1)
save($a2)
save($a3)
subi $a3,$a3,1
swap($a0,$a1)
jal f_move
load($a3)
load($a2)
load($a1)
load($a0)
load($ra)
jr $ra
equal:
move $a0,$a0
li $v0,4
syscall
la $a0,str_connect
li $v0,4
syscall
move $a0,$a2
li $v0,4
syscall
la $a0,str_enter
li $v0,4
syscall
jr $ra