table of Contents
Preface
The last time I did MIPS experiment 1: Underworld instruction set MIPS introduction: assembly, IO, procedure call and bubble sorting , if you still don’t understand mips IO and other operations, you can Kangkang. . .
So today, let’s record the experiment of the team. . .
This experiment is divided into two parts: the first part, use an adder to design a multiplier that does not consider overflow; the second part, use an adder to design a multiplication that considers overflow
Principle of addition
This is something even elementary school students know
Refer to vertical addition, but the base is different. We do a few things in a loop, generally 32-bit multiplication loops 32 times:
- Analyzing
multiplier
last bit is 1, if the result is a plusmultiplicand
multiplier
1 bit rightmultiplicand
1 bit left
As shown in the figure, the final result is cumulative. . .
Ignore overflowed multipliers
First, we have to understand how the multiplier is designed by the adder. Here, we take 32-bit multiplication as an example. There are 4 steps in total:
- Test whether the lowest bit of the multiplier is 1, if yes, add the multiplicand to the product, and write the result to the product register;
- The multiplicand register is shifted to the left by 1 bit;
- The multiplier register is shifted one bit to the right;
- Judge whether it has looped 32 times, if yes, end, otherwise return to step 1.
The flow chart of the algorithm is as follows:
We start coding, first we write the data in the .data section and allocate some stack space:
.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"
Then we write some helper functions to read int, print int and print string. See notes for their usefulness:
# @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
Then we start to write the initialization code, which is the code in the .text section. First, we print the prompt string, and then read two integers from the keyboard to the data area:
.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
Finally we can start writing the loop logic. First read two integers, and then we loop through the flowchart.
- Each cycle determines whether the last digit of the multiplicand is 1, if yes, the result is added to the multiplier.
- Each cycle needs to shift the multiplier and the multiplicand to the left/right
- Finally fetch the result register:
Note that our num1/num2 are placed in the s1, s2 registers. In addition, the s4 register is used to store the result:
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
An example of running and displaying the running result is as follows. Since we are showing a multiplication that ignores overflow, there are two results:
1. Less than 32 bits;
2. More than 32 bits. Screenshot of the first case:
Screenshot of the second case:
According to the above program code and screenshots, we can clearly see that when the result is less than 32 bits, the result is normal; when the result is greater than 32 bits, the result only intercepts the low 32-bit result, and the high 32-bit result is directly Ignore it.
Multiplier with overflow hint
The above program implements 32-bit multiplication by addition, but the lack of consideration for overflow is a drawback. Here, let's improve the above multiplier so that the multiplier will prompt when the result overflows.
In fact, this small optimization is very simple. It only needs to check the upper 32 bits of the 64-bit register. When the upper 32 bits are 0, the result does not overflow , otherwise, the result overflows.
Let's write the judgment logic: when we get the result, we additionally judge whether the high 32 bits are not zero. We shift s4 to the right by 32 bits, and then determine whether it is 0. Before the end of the halt, add:
# 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
There are also two results of the above code, one is the result of no overflow, and the other is the result of overflow. First, let's look at the results without overflow:
The result is correct. Secondly, if we look at the result of the overflow, we can see that R4 is still not zero after being shifted by 32 bits, indicating overflow:
As you can see, when the result overflows, the program will give a prompt "warning: result overflow".
Complete code
.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