一、实验目标:
实际运用WinMIPS64进行试验,以期更了解WinMIPS64的操作;
更加深入地了解MIPS程序的语法;
深入地了解在计算机中乘法的实现以及加法与乘法之间的关系。
二、实验内容
按照实验报告指导,完成相关操作记录实验结束后每个窗口的截图,以及提交源代码:
首先,我们使用加法操作设计一个不检测溢出的乘法操作;
完成后,我们对此进行优化,以期获得一个可以对溢出进行检测的乘法操作。
三、实验环境
硬件:桌面PC
软件:Windows,WinMIPS64仿真器
四、作业一:忽略溢出的乘法器
1)程序设计
忽略溢出的乘法器(以32位乘法为例)的步骤流程如下:
- 初始化寄存器:设置乘数、被乘数和积的寄存器(设A 为被乘数,B 为乘数,P 为积)。
- 循环操作:
- 检查乘数最低位:检测乘数寄存器的最低位(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):
- 初始化栈指针,将其指向栈顶。
- 加载数据和控制地址到寄存器 $a1 和 $a2。
- 打印输入提示信息("Please enter two numbers:")。
- 读取第一个数(NUM1)和第二个数(NUM2),调用 readInt 函数。
- 打印输出提示信息("Multiplied result:")。
- 初始化循环变量 $t0 为 32(即要处理的位数,i=32)。
- 加载 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):
- 检查 $t0 (即i)是否为 0,如果是,则跳转到结束部分。
- 每次循环减少 $t0 的值(i--)。
- 通过与操作获取 $t1 的最低位(最后一位)。
- 如果最低位为 1,则将 $t2 加到结果寄存器 $t4 中。
- 不管最后一位是否为 1,都对被乘数左移一位(乘以 2)和对乘数右移一位(除以 2)。
- 一直循环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):
- 将结果寄存器 $t4 的值放入参数 $a0,准备打印。
- 调用 printInt 函数打印结果。
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| end : daddi **** ****a0,**** **** t4, 0 # set RES as parameter jal printInt # print RES halt : halt |
(5)读取和打印函数:
- readInt: 从数据段中读取一个整数,并存入指定地址。
- printInt: 将整数打印到输出中。
- 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位进行检测。
具体步骤如下:
- 结果存储:在完成乘法运算后,结果被存储在 $t4 中。
- 提取高位:使用 dsrl $t4, $t4, 16 两次,目的是将 $t4 右移32位,获取乘法结果的高32位。这一步是因为我们假设乘法的结果超过了32位,会将多余的位数存放在高位中。
- 检查高位:使用 beq $t4, $zero, halt 来判断高32位是否为0。如果高32位为0,说明乘法结果没有溢出,程序正常结束;如果不为0,则说明发生了溢出。
- 输出警告:如果发现溢出,程序将 $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所示。