计算机组成原理(计算机系统3)--实验二:MIPS64乘法实现实验
一、实验目标:
实际运用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所示。
原文地址:https://blog.csdn.net/gyeolhada/article/details/145194200
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!