目录
- 前言
- 加法原理
- 忽略溢出的乘法器
- 溢出提示的乘法器
- 完整代码
前言
上一次做了 MIPS实验1:阴间指令集MIPS简介:汇编,IO,过程调用与冒泡排序,如果对mips的IO等等操作还有不懂的可以康康。。。
那么今天来记录一下计组的实验。。。
本次试验分为两个部分:第一部分、用加法器设计一个不考虑溢出的乘法器;第二部分、 用加法器设计一个考虑溢出的乘法
加法原理
这可是连小学生都知道的啊
参考竖式加法,只是进制不同。我们循环做几件事,一般32位乘法就循环32次:
- 判断
multiplier
最末位是否为1,如果为1则结果加上multiplicand
multiplier
右移 1 bitmultiplicand
左移 1 bit
如图,最终的结果就是累加产生的。。。
忽略溢出的乘法器
首先,我们得了解乘法器如何由加法器设计得到,此处,我们以 32 位乘法为例。 总共分为 4 步:
- 测试乘数最低位是否为 1,是则给乘积加上被乘数,将结果写入乘积寄存器;
- 被乘数寄存器左移 1 位;
- 乘数寄存器右移一位;
- 判断是否循环了 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 stacksw $ra, ($sp) # store return addressdaddi $t0, $zero, 8 # t0 = 8lw $t1, DATA($zero) # t1 = 0x10008lw $t2, CONTROL($zero) # t2 = 0x10000sw $t0, ($t2) # CONTROL = 8lw $t1, ($t1) # t1 = DATAsw $t1, ($a0) # M[arg0] = DATAlw $ra, ($sp) # restore return addressdaddi $sp, $sp, 4 # backwards stackjr $ra# @function printInt :
# @param arg0 : value of int to be print
# @return : ----
printInt:daddi $sp, $sp, -4 # sp -= 4sw $ra, ($sp) # save return addressdaddi $t0, $zero, 2 # t0 = 2lw $t1, DATA($zero) # t1 = 0x10008lw $t2, CONTROL($zero) # t2 = 0x10000sw $a0, ($t1) # DATA = arg0sw $t0, ($t2) # CONTROL = 2lw $ra, ($sp) # restore return addressdaddi $sp, $sp, 4 # sp += 4jr $ra # return# @function printStr :
# @param a0 : sratr address of string
# @return : ----
printStr:daddi $sp, $sp, -4 # sp -= 4sw $ra, ($sp) # save return addressdaddi $t0, $zero, 4 # t0 = 4dadd $t1, $zero, $a0 # t1 = arg0lw $t2, DATA($zero) # t2 = M[DATA] = 0x10008lw $t3, CONTROL($zero) # t3 = M[CONTROL] = 0x10000sw $t1, ($t2) # M[0x10008] = t1 = arg0sw $t0, ($t3) # M[0x10000] = t0 = 4lw $ra, ($sp) # restore return addressdaddi $sp, $sp, 4 # sp += 4jr $ra # return
然后我们开始写初始化的代码,即.text 节中的代码。首先我们打印提示字符串,然后从键盘 读取两个整数到 data 区:
.text
main:daddi $sp, $zero, STACKTOP # assign stackdaddi $a0, $zero, STR # print strjal printStrdaddi $a0, $zero, NUM1 # read num1jal readIntdaddi $a0, $zero, NUM2 # read num2jal readIntdaddi $a0, $zero, RES # print resjal printStrdaddi $s0, $zero, 32 # s0 = ilw $s1, NUM1($zero) # s1 = num1lw $s2, NUM2($zero) # s2 = num2
最后我们可以开始编写循环逻辑。首先读取两个整数,然后我们按照流程图进行循环。
- 每次循环判断被乘数的最后一位是否为 1,是则将结果加上乘数。
- 每次循环都需要分别对乘数和被乘数进行左移/右移
- 最后取结果寄存器即可:
注意我们的 num1/num2 放在 s1, s2 寄存器,此外,s4 寄存器被用来存放结果:
loop:beq $s0, $zero, enddaddi $s0, $s0, -1andi $s3, $s1, 1 # s3 = s1 lowbitbeq $s3, $zero, notadd # lowbit = 0 --> not adddadd $s4, $s4, $s2 # ans += s2
notadd:dsll $s2, $s2, 1 # s3<<1dsrl $s1, $s1, 1 # s1>>1j loop
end:daddi $a0, $s4, 0 # print s4jal printInthalt
运行显示运行结果的例子如下,由于我们这里展示的是忽略了溢出的乘法,所以结果有 两种:
1、小于 32 位;
2、大于 32 位。 第一种情况截图:
第二种情况截图:
根据上面的程序代码和截图,我们可以很清楚的看出,当结果小于32位时,结果正常;当结 果大于32位时,结果只截取了低32位的结果,而高32位的结果直接忽略掉了。
溢出提示的乘法器
上述的程序,用加法实现了 32 位乘法,但是,其中,对溢出情况没有进行考虑是其中的弊 端。这里,我们来完善上述的乘法器,使得该乘法器会在结果溢出时候提示。
其实,这个小优化是十分简单的,只需要对 64 位的寄存器中的高 32 位进行检测即可。当高 32 位为 0 时,说明结果没有溢出,否则,结果溢出。
我们来编写判断的逻辑即可:我们在得出结果的时候,额外判断其高 32 位是否不为零。我 们将 s4 右移 32 位,然后判断其是否为 0 即可。在 halt 结束之前,加上:
# overflow detectiondsrl $s4, $s4, 8 # high 32 bitdsrl $s4, $s4, 8 dsrl $s4, $s4, 8 dsrl $s4, $s4, 8 beq $s4, $zero, halt # high 32 = 0 --> haltdaddi $a0, $zero, OFSTR # print overflowjal 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 stackdaddi $a0, $zero, STR # print strjal printStrdaddi $a0, $zero, NUM1 # read num1jal readIntdaddi $a0, $zero, NUM2 # read num2jal readIntdaddi $a0, $zero, RES # print resjal printStrdaddi $s0, $zero, 32 # s0 = ilw $s1, NUM1($zero) # s1 = num1lw $s2, NUM2($zero) # s2 = num2loop:beq $s0, $zero, enddaddi $s0, $s0, -1andi $s3, $s1, 1 # s3 = s1 lowbitbeq $s3, $zero, notadd # lowbit = 0 --> not adddadd $s4, $s4, $s2 # ans += s2
notadd:dsll $s2, $s2, 1 # s3<<1dsrl $s1, $s1, 1 # s1>>1j loop
end:daddi $a0, $s4, 0 # print s4jal printInt# overflow detectiondsrl $s4, $s4, 8 # high 32 bitdsrl $s4, $s4, 8 dsrl $s4, $s4, 8 dsrl $s4, $s4, 8 beq $s4, $zero, halt # high 32 = 0 --> haltdaddi $a0, $zero, OFSTR # print overflowjal 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 stacksw $ra, ($sp) # store return addressdaddi $t0, $zero, 8 # t0 = 8lw $t1, DATA($zero) # t1 = 0x10008lw $t2, CONTROL($zero) # t2 = 0x10000sw $t0, ($t2) # CONTROL = 8lw $t1, ($t1) # t1 = DATAsw $t1, ($a0) # M[arg0] = DATAlw $ra, ($sp) # restore return addressdaddi $sp, $sp, 4 # backwards stackjr $ra# @function printInt :
# @param arg0 : value of int to be print
# @return : ----
printInt:daddi $sp, $sp, -4 # sp -= 4sw $ra, ($sp) # save return addressdaddi $t0, $zero, 2 # t0 = 2lw $t1, DATA($zero) # t1 = 0x10008lw $t2, CONTROL($zero) # t2 = 0x10000sw $a0, ($t1) # DATA = arg0sw $t0, ($t2) # CONTROL = 2lw $ra, ($sp) # restore return addressdaddi $sp, $sp, 4 # sp += 4jr $ra # return# @function printStr :
# @param a0 : sratr address of string
# @return : ----
printStr:daddi $sp, $sp, -4 # sp -= 4sw $ra, ($sp) # save return addressdaddi $t0, $zero, 4 # t0 = 4dadd $t1, $zero, $a0 # t1 = arg0lw $t2, DATA($zero) # t2 = M[DATA] = 0x10008lw $t3, CONTROL($zero) # t3 = M[CONTROL] = 0x10000sw $t1, ($t2) # M[0x10008] = t1 = arg0sw $t0, ($t3) # M[0x10000] = t0 = 4lw $ra, ($sp) # restore return addressdaddi $sp, $sp, 4 # sp += 4jr $ra # return