计组实验2 mips简单乘法器模拟实验

前言

上一次做了 MIPS实验1:阴间指令集MIPS简介:汇编,IO,过程调用与冒泡排序,如果对mips的IO等等操作还有不懂的可以康康。。。

那么今天来记录一下计组的实验。。。

本次试验分为两个部分:第一部分、用加法器设计一个不考虑溢出的乘法器;第二部分、 用加法器设计一个考虑溢出的乘法

加法原理

这可是连小学生都知道的啊

参考竖式加法,只是进制不同。我们循环做几件事,一般32位乘法就循环32次:

  1. 判断 multiplier 最末位是否为1,如果为1则结果加上 multiplicand
  2. multiplier 右移 1 bit
  3. multiplicand 左移 1 bit

如图,最终的结果就是累加产生的。。。

在这里插入图片描述

忽略溢出的乘法器

首先,我们得了解乘法器如何由加法器设计得到,此处,我们以 32 位乘法为例。 总共分为 4 步:

  1. 测试乘数最低位是否为 1,是则给乘积加上被乘数,将结果写入乘积寄存器;
  2. 被乘数寄存器左移 1 位;
  3. 乘数寄存器右移一位;
  4. 判断是否循环了 32 次,如果是,则结束,否则返回步骤 1。

算法的流程图如下:

在这里插入图片描述

我们开始编码,首先我们编写.data 节的数据并且分配一些栈空间:

.data
CONTROL:    .word 0x10000
DATA:       .word 0x10008
NUM1:       .word 0
NUM2:       .word 0
STACKBOTTOM:   .space 20
STACKTOP:       .word 0
OFSTR:      .asciiz "warning: result overflow\n"
STR:        .asciiz "please enter two numbers:\n"
RES:        .asciiz "result:\n"

然后我们编写一些帮助函数,分别是读取 int,打印 int 和打印字符串。他们的用处见注释:

#   @function readInt   : read an int from terminal
#   @param arg0         : address of int to be read
#   @return             : ----
readInt:
    daddi $sp, $sp, -4  # assign stack
    sw $ra, ($sp)       # store return address

    daddi $t0, $zero, 8     # t0 = 8
    lw $t1, DATA($zero)     # t1 = 0x10008
    lw $t2, CONTROL($zero)  # t2 = 0x10000
    sw $t0, ($t2)           # CONTROL = 8
    lw $t1, ($t1)           # t1 = DATA
    sw $t1, ($a0)           # M[arg0] = DATA

    lw $ra, ($sp)       # restore return address
    daddi $sp, $sp, 4   # backwards stack
    jr $ra

#   @function printInt  :
#   @param arg0         : value of int to be print
#   @return             : ----
printInt:
    daddi $sp, $sp, -4  # sp -= 4
    sw $ra, ($sp)       # save return address

    daddi $t0, $zero, 2     # t0 = 2
    lw $t1, DATA($zero)     # t1 = 0x10008
    lw $t2, CONTROL($zero)  # t2 = 0x10000
    sw $a0, ($t1)           # DATA = arg0
    sw $t0, ($t2)           # CONTROL = 2

    lw $ra, ($sp)       # restore return address
    daddi $sp, $sp, 4   # sp += 4
    jr $ra              # return

#   @function printStr  :
#   @param a0           : sratr address of string
#   @return             : ----
printStr:
    daddi $sp, $sp, -4      # sp -= 4
    sw $ra, ($sp)           # save return address
    daddi $t0, $zero, 4     # t0 = 4
    dadd $t1, $zero, $a0    # t1 = arg0
    lw $t2, DATA($zero)     # t2 = M[DATA] = 0x10008
    lw $t3, CONTROL($zero)  # t3 = M[CONTROL] = 0x10000
    sw $t1, ($t2)           # M[0x10008] = t1 = arg0
    sw $t0, ($t3)           # M[0x10000] = t0 = 4
    lw $ra, ($sp)           # restore return address
    daddi $sp, $sp, 4       # sp += 4
    jr $ra                  # return

然后我们开始写初始化的代码,即.text 节中的代码。首先我们打印提示字符串,然后从键盘 读取两个整数到 data 区:

.text
main:
    daddi $sp, $zero, STACKTOP  # assign stack
 
    daddi $a0, $zero, STR       # print str
    jal printStr

    daddi $a0, $zero, NUM1      # read num1
    jal readInt

    daddi $a0, $zero, NUM2      # read num2
    jal readInt

    daddi $a0, $zero, RES       # print res
    jal printStr

    daddi $s0, $zero, 32    # s0 = i
    lw $s1, NUM1($zero)     # s1 = num1
    lw $s2, NUM2($zero)     # s2 = num2

最后我们可以开始编写循环逻辑。首先读取两个整数,然后我们按照流程图进行循环。

  1. 每次循环判断被乘数的最后一位是否为 1,是则将结果加上乘数。
  2. 每次循环都需要分别对乘数和被乘数进行左移/右移
  3. 最后取结果寄存器即可:

注意我们的 num1/num2 放在 s1, s2 寄存器,此外,s4 寄存器被用来存放结果:

loop:
    beq $s0, $zero, end
    daddi $s0, $s0, -1

    andi $s3, $s1, 1        # s3 = s1 lowbit
    beq $s3, $zero, notadd  # lowbit = 0 --> not add

    dadd $s4, $s4, $s2      # ans += s2
notadd:
    dsll $s2, $s2, 1        # s3<<1
    dsrl $s1, $s1, 1        # s1>>1
    
    j loop
end:
    daddi $a0, $s4, 0       # print s4
    jal printInt
    halt 

运行显示运行结果的例子如下,由于我们这里展示的是忽略了溢出的乘法,所以结果有 两种:

1、小于 32 位;
2、大于 32 位。 第一种情况截图:

在这里插入图片描述
第二种情况截图:

在这里插入图片描述

根据上面的程序代码和截图,我们可以很清楚的看出,当结果小于32位时,结果正常;当结 果大于32位时,结果只截取了低32位的结果,而高32位的结果直接忽略掉了。

溢出提示的乘法器

上述的程序,用加法实现了 32 位乘法,但是,其中,对溢出情况没有进行考虑是其中的弊 端。这里,我们来完善上述的乘法器,使得该乘法器会在结果溢出时候提示。

其实,这个小优化是十分简单的,只需要对 64 位的寄存器中的高 32 位进行检测即可。当高 32 位为 0 时,说明结果没有溢出,否则,结果溢出。

我们来编写判断的逻辑即可:我们在得出结果的时候,额外判断其高 32 位是否不为零。我 们将 s4 右移 32 位,然后判断其是否为 0 即可。在 halt 结束之前,加上:

# overflow detection
    dsrl $s4, $s4, 8        # high 32 bit
    dsrl $s4, $s4, 8        
    dsrl $s4, $s4, 8        
    dsrl $s4, $s4, 8        
    beq $s4, $zero, halt    # high 32 = 0 --> halt
    daddi $a0, $zero, OFSTR # print overflow
    jal printStr
halt:
    halt 

在这里插入图片描述

上述代码运行结果也有两个,一个是没有溢出的情况下的结果,一个是溢出了的情况下 的结果。 首先,我们看没有溢出的情况结果:

在这里插入图片描述

结果正确,其次,我们看溢出的情况结果如何,可以看到 R4 移位 32 位之后任然不为 零,说明溢出:

在这里插入图片描述

可以看到,当结果溢出时,程序会给出提示“warning:result overflow”。

完整代码

.data
CONTROL:    .word 0x10000
DATA:       .word 0x10008
NUM1:       .word 0
NUM2:       .word 0
STACKBOTTOM:   .space 20
STACKTOP:       .word 0
OFSTR:      .asciiz "warning: result overflow\n"
STR:        .asciiz "please enter two numbers:\n"
RES:        .asciiz "result:\n"

.text
main:
    daddi $sp, $zero, STACKTOP  # assign stack
 
    daddi $a0, $zero, STR       # print str
    jal printStr

    daddi $a0, $zero, NUM1      # read num1
    jal readInt

    daddi $a0, $zero, NUM2      # read num2
    jal readInt

    daddi $a0, $zero, RES       # print res
    jal printStr

    daddi $s0, $zero, 32    # s0 = i
    lw $s1, NUM1($zero)     # s1 = num1
    lw $s2, NUM2($zero)     # s2 = num2

loop:
    beq $s0, $zero, end
    daddi $s0, $s0, -1

    andi $s3, $s1, 1        # s3 = s1 lowbit
    beq $s3, $zero, notadd  # lowbit = 0 --> not add

    dadd $s4, $s4, $s2      # ans += s2
notadd:
    dsll $s2, $s2, 1        # s3<<1
    dsrl $s1, $s1, 1        # s1>>1
    
    j loop
end:
    daddi $a0, $s4, 0       # print s4
    jal printInt

    # overflow detection
    dsrl $s4, $s4, 8        # high 32 bit
    dsrl $s4, $s4, 8        
    dsrl $s4, $s4, 8        
    dsrl $s4, $s4, 8        
    beq $s4, $zero, halt    # high 32 = 0 --> halt
    daddi $a0, $zero, OFSTR # print overflow
    jal printStr
halt:
    halt 

#   @function readInt   : read an int from terminal
#   @param arg0         : address of int to be read
#   @return             : ----
readInt:
    daddi $sp, $sp, -4  # assign stack
    sw $ra, ($sp)       # store return address

    daddi $t0, $zero, 8     # t0 = 8
    lw $t1, DATA($zero)     # t1 = 0x10008
    lw $t2, CONTROL($zero)  # t2 = 0x10000
    sw $t0, ($t2)           # CONTROL = 8
    lw $t1, ($t1)           # t1 = DATA
    sw $t1, ($a0)           # M[arg0] = DATA

    lw $ra, ($sp)       # restore return address
    daddi $sp, $sp, 4   # backwards stack
    jr $ra

#   @function printInt  :
#   @param arg0         : value of int to be print
#   @return             : ----
printInt:
    daddi $sp, $sp, -4  # sp -= 4
    sw $ra, ($sp)       # save return address

    daddi $t0, $zero, 2     # t0 = 2
    lw $t1, DATA($zero)     # t1 = 0x10008
    lw $t2, CONTROL($zero)  # t2 = 0x10000
    sw $a0, ($t1)           # DATA = arg0
    sw $t0, ($t2)           # CONTROL = 2

    lw $ra, ($sp)       # restore return address
    daddi $sp, $sp, 4   # sp += 4
    jr $ra              # return

#   @function printStr  :
#   @param a0           : sratr address of string
#   @return             : ----
printStr:
    daddi $sp, $sp, -4      # sp -= 4
    sw $ra, ($sp)           # save return address
    daddi $t0, $zero, 4     # t0 = 4
    dadd $t1, $zero, $a0    # t1 = arg0
    lw $t2, DATA($zero)     # t2 = M[DATA] = 0x10008
    lw $t3, CONTROL($zero)  # t3 = M[CONTROL] = 0x10000
    sw $t1, ($t2)           # M[0x10008] = t1 = arg0
    sw $t0, ($t3)           # M[0x10000] = t0 = 4
    lw $ra, ($sp)           # restore return address
    daddi $sp, $sp, 4       # sp += 4
    jr $ra                  # return

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/109500195
今日推荐