计算机组成原理笔记05-MIPS练习合集

MIPS练习合集

碎碎念

写在前面:

作为。。。考了两次P2的憨憨,不得不说对P2的各种题型也有了比较深的理解,总结如下,希望能对后来的学弟学妹有所启发吧。

yysy,把下面的题都学会学懂了p2必不挂www

必看提示

首先需要会的一些基本操作,比如用一维数组存放数据和二维矩阵存放数据

其次P2的题型主要分为三大类。第一类是简单数学应用,如关于素数、回文数、水仙花数、阶乘的一系列求值等。第二类是简单程序转化,即将C语言的一些典型题目用mips表示出来,如约瑟夫环、高精度阶乘等。第三类是含有递归的程序,需要注意mips中各个寄存器的保护机制(强保护/弱保护),以及栈的使用方法,典型题目包括全排列、汉诺塔等

1.1回文串判断

  1. 判断输入的字符串是不是回文串。

  2. 输出一个字符,是回文串输出1,否则输出0

  3. 每组数据最多执行100,000条指令。

  4. 请使用syscall结束程序:

  5. li $v0,10
    syscall
    

输入格式

第一行为一个整数n,代表字符串的长度。第二行开始的n行:每行一个字符(小写字母),连起来为输入的字符串。(0<n<=20)

输出格式

输出为一行,输出一个字符,是会文串输出1,否则输出0。

输入样例

 5
 a
 b
 b
 d
 l

输出样例

 0 

提交要求

  1. 请勿使用 .globl main
  2. 不考虑延迟槽
  3. 只需要提交.asm文件。
  4. 程序的初始地址设置为Compact,Data at Address 0

注意事项

  1. 注意!因为评测机的行为和MARS有一些区别,你需要注意以下事项。

  2. 如果你采取每次读入一个字符的系统调用($v0=12)来读入数据,那么我们保证你不会读入到任何换行符。如果你采取这种方式输入,那么对于样例,你可以在MARS中首先手动输入5,打回车,然后手动在一行之中输入abbdl。

  3. 如果你采取一次读一行的系统调用($v0=8),那么你读入的每行有一个小写字母以及行尾的一个换行符。

  4. 如果你的程序长时间等不到应有的输入,则有可能提示超时或运行错误。

  5. 在你处理字符的时候,你需要考虑到上述情况。

思路:

  • 回文串判断:从字符串的两头开始比较,即第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全排列

  1. 使用mips实现全排列生成算法。

  2. 以0x00000000为数据段起始地址。

  3. 输入一个小于等于6的正整数,求出n的全排列,并按照字典序输出。

  4. 每组数据最多执行500,000条指令。

  5. 请使用syscall结束程序:

  6. 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

提交要求

  1. 请勿使用 .globl main
  2. 不考虑延迟槽
  3. 只需要提交.asm文件。
  4. 程序的初始地址设置为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=Hn12+1
通项公式:
H n = 2 n − 1 Hn=2^n-1 Hn=2n1

行走方式问题

#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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值