In MIPS, how do I divide register contents by two? - mips

Let's say I have $t0, and I'd like to divide its integer contents by two, and store it in $t1.
My gut says: srl $t1, $t0, 2
... but wouldn't that be a problem if... say... the right-most bit was 1? Or does it all come out in the wash because the right-most bit (if positive) makes $t0 an odd number, which becomes even when divided?
Teach me, O wise ones...

Use instruction sra: Shift right arithmetic !!
sra $t1, $t0, 1
Divides the content of $t0 by the first power of 2.
Description: Shifts a register value
right by the shift amount (shamt) and
places the value in the destination
register. The sign bit is shifted in.
Operation: $d = $t >> h;
advance_pc (4);
Syntax: sra $d, $t, h
Encoding:
0000 00-- ---t tttt dddd dhhh hh00
0011
Why is this important? Check this simple program that divides an integer number (program's input) by 2.
#include <stdio.h>
/*
* div divides by 2 using sra
* udiv divides by 2 using srl
*/
int div(int n);//implemented in mips assembly.
int udiv(int n);
int main(int argc,char** argv){
if (argc==1) return 0;
int a = atoi(argv[1]);
printf("div:%d udiv:%d\n",div(a),udiv(a));
return 1;
}
//file div.S
#include <mips/regdef.h>
//int div(int n)
.globl div
.text
.align 2
.ent div
div:
sra v0,a0,1
jr ra //Returns value in v0 register.
.end div
//int udiv(int n)
.globl udiv
.text
.align 2
.ent udiv
udiv:
srl v0,a0,1
jr ra //Returns value in v0 register.
.end udiv
Compile
root#:/tmp#gcc -c div.S
root#:/tmp#gcc -c main.c
root#:/tmp#gcc div.0 main.o -o test
Test drives:
root#:~# ./test 2
div:1 udiv:1
root#:~# ./test 4
div:2 udiv:2
root#:~# ./test 8
div:4 udiv:4
root#:~# ./test 16
div:8 udiv:8
root#:~# ./test -2
div:-1 udiv:2147483647
root#:~# ./test -4
div:-2 udiv:2147483646
root#:~# ./test -8
div:-4 udiv:2147483644
root#:~# ./test -16
div:-8 udiv:2147483640
root#:~#
See what happens? The srl instruction is shifting the sign bit
-2 = 0xfffffffe
if we shift one bit to the right, we get 0x7fffffff
0x7ffffffff = 2147483647
Of course this is not a problem when the number is a positive integer, because the sign bit is 0.

To do unsigned integer division, thats right. This only works for unsigned integers and if you don't care about the fractional part.

You will want to use a shift amount of 1, not 2:
srl $t1, $t0, 1
If you use 2, you will end up dividing by 4. In general, shifting right by x divides by 2x.

If you are concerned about "rounding" and you want to round up, you can just increment by 1 before doing the logical (unsigned) shift.
And other have stated it previously but you only shift by 1 to divide by 2. A right shift by N bits divides by 2^N.
To use rounding (rounding up at 0.5 or greater) with shift values of N other than 1, just add 1<<(N-1) prior to the shift.

Related

Translating the following high level code into MIPS Assembly code

I have translated this C program into MIPS assembly code.
So, here I want to know:
Have I done something wrong?
If my assembly code is correct, can I do it more precisely by reducing the
number of instructions. I have used 14 instructions here
Here is my approach for converting this C program
This is my MIPS assembly code
#s0 = res
main:
addi $a0 , $0 , 27
addi $a1 , $0 , 3
jal division # call function
add $s0 , $v0 , $0 # res = returned value
division:
addi $t0 , $0 , 0 # $t0 = 0
addi $s0 , $0 , 0 # $s0 = val and val = 0
add $s1 , $0 , $a0 # $s1 = i and i = 27
loop:
slt $t1 , $s1 , $t0 # checking if i>0
bne $t1 , $0 , break # if $t1 = 0 then break
addi $s0 , $s0 , 1 # else val = val + 1
sub $s1 , $s1 , $a1 # i = i - y
add $v0 , $s0 , $0 # put return value in $v0
j loop
break:
jr $ra # return to caller
This is the high level C program
int main(){
int res;
res = division (27, 3);
}
int division(int x, int y)
{
int i;
int val = 0;
for (i = x; i>0; i = i-y){
val = val + 1;
}
return val;
}
Errors:
The relation is negated incorrectly.  You have translated it as
for ( i = x; i >= 0; i -= y )
So, it will loop one time more than you want (when the integer division is exact).  You are aware that loop condition test requires negation for the if-goto-label style of assembly:
if ( ! (i > 0) ) goto break;
However ! (i > 0) is i <= 0 where you have i < 0.
You don't properly terminate the program when the main is finished, so it will accidentally fall into the divide subroutine.  The solution is to add an exit syscall to the main.
Suggestions:
The register usage is excessive, and using fewer registers will also decrease the number of instructions required.  Further, using the $s registers without preserving their original values is a violation of the calling convention.  However, the best solution is to simply avoid using them in that function — just use $a0 and $a1 directly instead of copying them elsewhere.
(It is ok to use s registers in main without preserving their original values, b/c main is at the top of the call chain — it has no caller — by contrast we might consider division as a general purpose function that could be called from any caller, and so should follow the calling convention.)
We not only want to minimize the instructions, but also the number of instructions in the loop.  You're doing the work of "return val" by copying $s0 into $v0, but every time in the loop whereas only once is necessary, so that is better left for after the loop.  Even better still, simply use $v0 for the counter (val) in the first place.
The slt instruction has an equivalent slti that is handy when comparing to a constant.  With slti, no need to load a constant 0 into $t0 register.  If you did need a constant 0 in a register, there is always the $0 register as well.
The MIPS instruction set has relational branch instructions that compare with zero, so you can branch on register <= 0 (or < 0) directly without slt/slti.

MIPS instructions - addiu $sp, $sp, -4 [duplicate]

Currently I'm studying for my computer organization midterm, and I'm trying to fully understand the stack pointer and the stack. I know these following facts that surround the concept:
It follows the first in last out principle
And adding something to the stack takes a two step process:
addi $sp, $sp, -4
sw $s0, 0($sp)
What I think is stopping me from fully understanding is that I can't come up with a relevant, self apparent situation where I would need and/or want to keep track of data with a stack pointer.
Could someone elaborate on the concept as a whole and give me some useful code examples?
An important use of stack is nesting subroutine calls.
Each subroutine may have a set of variables local to that subroutine. These variables can be conveniently stored on a stack in a stack frame. Some calling conventions pass arguments on the stack as well.
Using subroutines also means you have to keep track of the caller, that is the return address.
Some architectures have a dedicated stack for this purpose, while others implicitly use the "normal" stack. MIPS by default only uses a register, but in non-leaf functions (ie. functions that call other functions) that return address is overwritten. Hence you have to save the original value, typically on the stack among your local variables. The calling conventions may also declare that some register values must be preserved across function calls, you can similarly save and restore them using the stack.
Suppose you have this C fragment:
extern void foo();
extern int bar();
int baz()
{
int x = bar();
foo();
return x;
}
MIPS assembly may then look like:
addiu $sp, $sp, -8 # allocate 2 words on the stack
sw $ra, 4($sp) # save $ra in the upper one
jal bar # this overwrites $ra
sw $v0, ($sp) # save returned value (x)
jal foo # this overwrites $ra and possibly $v0
lw $v0, ($sp) # reload x so we can return it
lw $ra, 4($sp) # reload $ra so we can return to caller
addiu $sp, $sp, 8 # restore $sp, freeing the allocated space
jr $ra # return
The MIPS calling convention requires first four function parameters to be in registers a0 through a3 and the rest, if there are more, on the stack. What's more, it also requires the function caller to allocate four slots on the stack for the first four parameters, despite those being passed in the registers.
So, if you want to access parameter five (and further parameters), you need to use sp. If the function in turn calls other functions and uses its parameters after the calls, it will need to store a0 through a3 in those four slots on the stack to avoid them being lost/overwritten. Again, you use sp to write these registers to the stack.
If your function has local variables and can't keep all of them in registers (like when it can't keep a0 through a3 when it calls other functions), it will have to use the on-stack space for those local variables, which again necessitates the use of sp.
For example, if you had this:
int tst5(int x1, int x2, int x3, int x4, int x5)
{
return x1 + x2 + x3 + x4 + x5;
}
its disassembly would be something like:
tst5:
lw $2,16($sp) # r2 = x5; 4 slots are skipped
addu $4,$4,$5 # x1 += x2
addu $4,$4,$6 # x1 += x3
addu $4,$4,$7 # x1 += x4
j $31 # return
addu $2,$4,$2 # r2 += x1
See, sp is used to access x5.
And then if you have code something like this:
int binary(int a, int b)
{
return a + b;
}
void stk(void)
{
binary(binary(binary(1, 2), binary(3, 4)), binary(binary(5, 6), binary(7, 8)));
}
this is what it looks in disassembly after compilation:
binary:
j $31 # return
addu $2,$4,$5 # r2 = a + b
stk:
subu $sp,$sp,32 # allocate space for local vars & 4 slots
li $4,0x00000001 # 1
li $5,0x00000002 # 2
sw $31,24($sp) # store return address on stack
sw $17,20($sp) # preserve r17 on stack
jal binary # call binary(1,2)
sw $16,16($sp) # preserve r16 on stack
li $4,0x00000003 # 3
li $5,0x00000004 # 4
jal binary # call binary(3,4)
move $16,$2 # r16 = binary(1,2)
move $4,$16 # r4 = binary(1,2)
jal binary # call binary(binary(1,2), binary(3,4))
move $5,$2 # r5 = binary(3,4)
li $4,0x00000005 # 5
li $5,0x00000006 # 6
jal binary # call binary(5,6)
move $17,$2 # r17 = binary(binary(1,2), binary(3,4))
li $4,0x00000007 # 7
li $5,0x00000008 # 8
jal binary # call binary(7,8)
move $16,$2 # r16 = binary(5,6)
move $4,$16 # r4 = binary(5,6)
jal binary # call binary(binary(5,6), binary(7,8))
move $5,$2 # r5 = binary(7,8)
move $4,$17 # r4 = binary(binary(1,2), binary(3,4))
jal binary # call binary(binary(binary(1,2), binary(3,4)), binary(binary(5,6), binary(7,8)))
move $5,$2 # r5 = binary(binary(5,6), binary(7,8))
lw $31,24($sp) # restore return address from stack
lw $17,20($sp) # restore r17 from stack
lw $16,16($sp) # restore r16 from stack
addu $sp,$sp,32 # remove local vars and 4 slots
j $31 # return
nop
I hope I've annotated the code without making mistakes.
So, note that the compiler chooses to use r16 and r17 in the function but preserves them on the stack. Since the function calls another one, it also needs to preserve its return address on the stack instead of simply keeping it in r31.
PS Remember that all branch/jump instructions on MIPS effectively execute the immediately following instruction before actually transferring control to a new location. This might be confusing.

How to load an integer value to a double register in MIPS?

I have this code :
void test(int x)
{
cout<<x;
double y=x+4.0;
cout<<y;
}
void main ()
{
test(7); // call the test in main
}
In MIPS :
after I put the value of parameter x in 0($fp) in stack and jump to test :
lw $a0,0($fp) // load value of x and print it
li $v0,1
syscall
lw $t1,0($fp)
sw $t1,0($sp) // put the value of x in stack and point it by $sp
li.d $f0,4.0
s.d $f0,4($sp) // put the value 4.0 in stack and point it by $sp
l.d $f0,0($sp)
l.d $f2,4($sp)
add.d $f4,$f0,$f2
s.d $f4,8($sp) // put the result of add
l.d $f12,8($sp) // print the value of y
li $v0,3
syscall
My problem is the result of y in QTSPIM is 4 .... the problem because I load an integer value in a double register ... How I can solve this problem ???
You need to load the integer value into an fp register and then convert it to floating point format. You have an 8-byte load (which will load the 4 bytes of your value, plus the following 4 bytes, whatever they happen to be), so you probably want to change that, and then do a cvt:
l.w $f0,0($fp)
cvt.d.w $f0,$f0

Get/Swap Array Elements in MIPS

I am very new to MIPS and this is a homework assignment, so I'm really just looking for a clue to get me going. :) I have looked around for similar questions but didn't find anything that was what I needed, so I'm sorry that this has been asked before. I am writing a bubble sort in PCSpim, and I think I am pretty good with the code, except for swapping.
Here is the pseudocode:
lastUnsorted = length - 1
sortedFlag = 0
while(lastUnsorted >= 1 and sortedFlag == 0) do
sortedFlag = 1
for test = 0 to lastUnsorted-1 do
if(numbers[test] > numbers[test+1] then
temp = numbers[test]
numbers[test] = numbers[test+1]
numbers[test+1] = temp
sortedFlag = 0
end if
end for
lastUnsorted = lastUnsorted -1
end while
I think it involves getting the addresses of each element, checking to see if the first is greater than the second, and completing a swap via a swap function. Here is what I have:
.data
numbers: .word 20, 30, 10, 40, 50, 40, 30, 25, 10, 5
length: .word 10
sortedFlag: .word 0
lastUnsorted: .word 9
test: .word 0
.text
.globlmain
main:
la $11, numbers
lw $12, length
lw $13, sortedFlag
lw $14, lastUnsorted
lw $15, test
j while
end_main:
#####################################################################
while:
li $16, 1
blt $14, $16, end_while #program is completed
li $17, 0
bne $13, $17, end_while #program is completed
addi #13, 1 #make sortedFlag == 1
j bubble_sort #jump to the bubble sort
bubble_sort:
bge $15, $14, go_back #if test is greater than or equal to lastUnsorted, end
####stuff for swap
# find address of first, then second and store each in t0 and t1
# check the value at $t0 is greater than val at $t1; if so, call swap function
addi $15, $15, 1 #add one to test after swap or no swap
j go_back #jump out of sorting loop
go_back:
addi $14, $14, -1 #subtract one from lastUnsorted
j while #jump back to while loop to start again
swap:
move $t2, $t0 #swap with temporary registers??
move $t0, $t1
move $t1, $t2
addi $13, $13, -1 #make sortedFlag == 0 IF a swap is completed
end_while:
li $vo, 10 #prog is done
syscall
As you can see, I've left a piece of the middle blank, because I am not sure what to do to get the two elements and compare them. I found something online that said I could swap elements via move and temporary registers, but I don't see the connection between getting these elements and then swapping them. I realize the code is likely not optimal and there may be shortcuts, but I am very much a beginner on this.
Any advice would be appreciated! Thank you in advance.

MIPS assembler memory alignment issue (detailed code included)

I am trying to store a integer that i am reading from the user into a array, however when i try to store it into my array my data becomes unaligned.
This first block of code is where i initialize all the data. (Under the 1. is where i am trying to store the integer )
'#Constants
P_INT = 1 #Syscall to print integer(value)
P_STRING = 4 #Syscall to print a string(addr)
P_CHAR = 11 #Syscall to print a char(char)
R_INT = 5 #Syscall to read a integer(none)
EXIT = 10 #Exit program(none)
'#Data
.data
newline:
.asciiz "\n"
'#Space for the bored.
1.
board_rep:
.space 578
'#The current node to be read in
cur_node:
.word 0
'#Size of the bored
size:
.space 4
'#Plus sign
plus:
.asciiz "+"
'#dash
dash:
.asciiz "-"
Right here is where it becomes unaligned (the sw right after 2.) . The strange thing is that i am doing the exact same thing later (in the third code block) except that i am storing it in the size array.
'#Grabs user input for the bored and stores it
get_board_rep:
li $v0,R_INT '#Read next node and store it
syscall
2.
sw $v0,0($s1)
addi $s1,$s1,4 ' #Increment next node addr
lw $a0,0($s1)
j prnt_node
At the store word (under the 3. ) it stores the read in integer fine.
la $s0, size ' #Store the variable addr's
la $s1, board_rep
li $v0,R_INT ' #Get user input(size of bored)
syscall
3.
sw $v0,0($s0) ' #Store the size of bored
jal get_board_rep
I thought maybe the array was too large but i changed it to 4 (the same size that the other array that worked). But it still was unaligned.
Thanks in advance . This is a project and i know some people don't like helping with stuff like this. But i have done my homework and i cannot find a answer anywhere.
That doesn't look aligned to me, and if i'm wrong try explicitly aligning it anyway.