自学内容网 自学内容网

计算机组成原理(计算机系统3)--实验二:MIPS64乘法实现实验

一、实验目标:

实际运用WinMIPS64进行试验,以期更了解WinMIPS64的操作;

更加深入地了解MIPS程序的语法;

深入地了解在计算机中乘法的实现以及加法与乘法之间的关系。

二、实验内容

按照实验报告指导,完成相关操作记录实验结束后每个窗口的截图,以及提交源代码:

首先,我们使用加法操作设计一个不检测溢出的乘法操作;

完成后,我们对此进行优化,以期获得一个可以对溢出进行检测的乘法操作。

三、实验环境

硬件:桌面PC

软件:Windows,WinMIPS64仿真器

四、作业一:忽略溢出的乘法器

1)程序设计

忽略溢出的乘法器(以32位乘法为例)的步骤流程如下:

  1. 初始化寄存器:设置乘数、被乘数和积的寄存器(设A 为被乘数,B 为乘数,P 为积)。
  2. 循环操作:
  • 检查乘数最低位:检测乘数寄存器的最低位(B[0])。如果最低位为1,将被乘数(A)加到积(P)。
  • 左移被乘数:将被乘数寄存器左移1位(A = A << 1),相当于乘以2。
  • 右移乘数:将乘数寄存器右移1位(B = B >> 1),相当于整除2。
  • 循环判断:判断是否已循环32次(即乘数寄存器是否为0),如果是,则结束;否则返回到步骤1。

例如:计算6*3:

初始化:被乘数a=6(0110),乘数b=3(0011),结果res=0

第一次循环:

  • 检查 b[0]=1 → res=res+a=0+6=6
  • 左移被乘数 a → a=12 (1100)
  • 右移乘数b → b=1 (0001)

第二次循环:

  • 检查 b[0]=1 → res=res+a=6+12=18
  • 左移被乘数 a → a=24 (11000)
  • 右移乘数b → b=0 (0000)
  • 结束:结果 res=18(6*3=18)

2)代码实现

(1)数据段定义(.data):定义程序使用的常量和变量,包括控制地址、数据地址、两个待乘数、栈空间和输出字符串。

.data

CONTROL:    .word 0x10000                      # CONTROL address

DATA:         .word 0x10008                      # DATA address

NUM1:         .word 0                            # the first number

NUM2:         .word 0                            # the second number

STACKBOTTOM:   .space 20                       # the size of stack

STACKTOP:       .word 0                         # stackTop

OVERSTR:    .asciiz "Warning: multiplied result overflow!\n"     # Overflow warning

STR:          .asciiz "Please enter two numbers:\n"                # Input Tip

RES:          .asciiz "Multiplied result:\n"                       # Output Tip

(2)主程序 (main):

  1. 初始化栈指针,将其指向栈顶。
  2. 加载数据和控制地址到寄存器 $a1 和 $a2。
  3. 打印输入提示信息("Please enter two numbers:")。
  4. 读取第一个数(NUM1)和第二个数(NUM2),调用 readInt 函数。
  5. 打印输出提示信息("Multiplied result:")。
  6. 初始化循环变量 $t0 为 32(即要处理的位数,i=32)。
  7. 加载 NUM1 和 NUM2 的值到寄存器 $t1 和 $t2。

.text

main:

    daddi $sp, $zero, STACKTOP                  # init the size of stack

    lw $a1, DATA($zero)                          # $al = 0x10008

    lw $a2, CONTROL($zero)                     # $a2 = 0x10000

    daddi $a0, $zero, STR                         # set InputString as parameter

    jal printStr                                # print Input Tip

    daddi $a0, $zero, NUM1                     # set NUM1 as parameter

    jal readInt                                # get NUM1

    daddi $a0, $zero, NUM2                     # set NUM2 as parameter

    jal readInt                                # get NUM2

    daddi $a0, $zero, RES                      # set OutputString as parameter

    jal printStr                               # print Output Tip

    daddi $t0, $zero, 32                         # $t0 = i , i = 32

    lw $t1, NUM1($zero)                        # $t1 = NUM1

    lw $t2, NUM2($zero)                        # $t2 = NUM2

(3)乘法循环 (loop):

  1. 检查 $t0 (即i)是否为 0,如果是,则跳转到结束部分。
  2. 每次循环减少 $t0 的值(i--)。
  3. 通过与操作获取 $t1 的最低位(最后一位)。
  4. 如果最低位为 1,则将 $t2 加到结果寄存器 $t4 中。
  5. 不管最后一位是否为 1,都对被乘数左移一位(乘以 2)和对乘数右移一位(除以 2)。
  6. 一直循环32次。

loop:

    beq $t0, $zero, end                          # if i == 0 then end

    daddi $t0, $t0, -1                            # else i--

    andi $t3, $t1, 1                              # $t3 = the last bit of $t1

    beq $t3, $zero, notAdd                       # if $t3 != 1 then notAdd

    dadd $t4, $t4, $t2                           # else res += $t2

notAdd:

    dsll $t2, $t2, 1                           # $t2 << 1

    dsrl $t1, $t1, 1                           # $t1 >> 1

    j loop                                     # Continue

(4)结束部分 (end):

  1. 将结果寄存器 $t4 的值放入参数 $a0,准备打印。
  2. 调用 printInt 函数打印结果。

end:

    daddi $a0, $t4, 0                          # set RES as parameter

    jal printInt                               # print RES

halt:

     halt

(5)读取和打印函数:

  1. readInt: 从数据段中读取一个整数,并存入指定地址。
  2. printInt: 将整数打印到输出中。
  3. printStr: 打印字符串到输出中。

#   @function readInt   : read an Int

#   @param arg0         : the address of the Int

#   @param arg1         : DATA

#   @param arg2         : CONTROL

#   @return             : ----

readInt:

    daddi $sp, $sp, -4                         # init the size of stack

    sw $ra, ($sp)                              # store the return address

    daddi $t0, $zero, 8                        # $t0 = 8

    sw $t0, ($a2)                              # CONTROL = 8

    lw $t1, ($a1)                              # $t1 = DATA

    sw $t1, ($a0)                              # M[arg0] = DATA

    lw $ra, ($sp)                              # restore the return address

    daddi $sp, $sp, 4                          # release stack

    jr $ra                                     # return

#   @function printInt  : print an Int

#   @param arg0         : the Int

#   @param arg1         : DATA

#   @param arg2         : CONTROL

#   @return             : ----

printInt:

    daddi $sp, $sp, -4                         # init the size of stack

    sw $ra, ($sp)                              # store the return address

    daddi $t0, $zero, 2                        # $t0 = 2

    sw $a0, ($a1)                              # DATA = arg0

    sw $t0, ($a2)                              # CONTROL = 2

    lw $ra, ($sp)                              # restore the return address

    daddi $sp, $sp, 4                          # release stack

    jr $ra                                     # return

#   @function printStr  : print a String

#   @param arg0         : the address of the String

#   @param arg1         : DATA

#   @param arg2         : CONTROL

#   @return             : ----

printStr:

    daddi $sp, $sp, -4

    sw $ra, ($sp)                             # init the size of stack

    

    daddi $t0, $zero, 4                       # $t0 = 4

    sw $a0, ($a1)                             # DATA = arg0

    sw $t0, ($a2)                             # CONTROL = 4

    

    lw $ra, ($sp)                             # restore the return address

    daddi $sp, $sp, 4                         # release stack

jr $ra                                    # return

3)检查合法性

用asm.exe检验一下程序的正确性,在终端中输入.\asm.exe .\Multiply.s,得到如下的结果,没有错误(0 errors),即Multiply.s编译非常顺利。

4)结果运行

将Multiply.s加载到winmips64中,不断地按下F7之后进行单步运行,得到下图的结果。得到cycles顺利地进行了流水执行指令,没有raw stalls的情况发生,然后看终端,可以得到打印出了输入提示。

输入12和12,进行12*12的乘法运算,结果为144,如图4所示。

输入10000000和10000000,进行10000000*10000000的乘法运算,结果并不正确,发生了溢出现象,但此时并无警告功能,如图5所示。

五、作业二:溢出提示的乘法器

1)程序设计

溢出检查的设计在代码的末尾部分,主要通过对乘法结果的高32位进行检测。

具体步骤如下:

  1. 结果存储:在完成乘法运算后,结果被存储在 $t4 中。
  2. 提取高位:使用 dsrl $t4, $t4, 16 两次,目的是将 $t4 右移32位,获取乘法结果的高32位。这一步是因为我们假设乘法的结果超过了32位,会将多余的位数存放在高位中。
  3. 检查高位:使用 beq $t4, $zero, halt 来判断高32位是否为0。如果高32位为0,说明乘法结果没有溢出,程序正常结束;如果不为0,则说明发生了溢出。
  4. 输出警告:如果发现溢出,程序将 $a0 设置为溢出警告字符串 OVERSTR,并调用 printStr 函数打印警告信息。

2)代码实现

将溢出检查并提示的代码放在end结束部分。

end:

    daddi $a0, $t4, 0                          # set RES as parameter

    jal printInt                               # print RES

    # check overflow

    dsrl $t4, $t4, 16                         

    dsrl $t4, $t4, 16                          # get high 32 bits

    beq $t4, $zero, halt                       # high 32 bits = 0 , valid  

    daddi $a0, $zero, OVERSTR                  # high 32 bits != 0 , notValid

    jal printStr                               # print Overflow warnin

halt:

     halt

3)检查合法性

用asm.exe检验一下程序的正确性,在终端中输入.\asm.exe .\MultiplyCheck.s,得到如下的结果,没有错误(0 errors),即MultiplyCheck.s编译非常顺利。

4)结果运行

将MultiplyCheck.s加载到winmips64中,按下Run to之后运行,得到下图的结果。得到cycles顺利地进行了流水执行指令,没有raw stalls的情况发生,然后看终端,可以得到打印出了输入提示。

输入12和12,进行12*12的乘法运算,结果为144,没有发生溢出,如图9所示。

输入10000000和10000000,进行10000000*10000000的乘法运算,结果不正确,发生了溢出现象,此时在终端中显示溢出警告提示,如图10所示。

同理,输入1000000和1000000,进行1000000*1000000的乘法运算,结果不正确,也发生了溢出现象,此时在终端中也会显示溢出警告提示,如图11所示。


原文地址:https://blog.csdn.net/gyeolhada/article/details/145194200

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!