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.
Related
I have a piece of code in MIPS I am failing to understand. Namely the register convention using the "read" linux syscall:
...
move a0,zero #a0 = 0
move a1,s0 #a1 = some adress in memory
li v0,4003 #v0 = 4003 = SYSCALL READ
syscall
bnez a3, label #What is in a3??
li v1,1
bne v0,v1
...
Now I get understand it as
char buffer;
int v0 = read(0, &buffer, 1);
????
What I do not get at all is what is the meaning of reg a3 in this context? v0 is the return value of read, but a3 should be parameter and not ret value. Is it errno?
It's a boolean indicating whether or not there was an error:
On a few architectures, a register is used as a boolean (0
indicating no error, and -1 indicating an error) to signal
that the system call failed.
Arch/ABI Instruction System Ret Ret Error
call # val val2
-------------------------------------------------------------
mips syscall v0 v0 v1 a3
source
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.
I need to build a program (hard-code) in MIPS that gets an array of 10 integers and finds the difference between two nearby numbers in the array. This is what I have build:
.data
array: .word 23,-2,45,67,89,12,-100,0,120,6
arrend:
comma: .asciiz ", "
# array = {23,-2,45,67,89,12,-100,0,120,6}
# Algorithm being implemented to find the difference between nearby elements of the array
# difference = 0 (use $t0 for difference)
# loop i = 0 to length-1 do (use $t1 for i)
# difference = array[i]-array[i+1]
# end loop (use $t3 for base addr. of array)
# registers:
# t0 -- difference
#
# t3 -- pointer to current array element (e.g. arrptr)
# t2 -- pointer to end of array
#
# t4 -- current value fetched from array (i)
# t5 -- value fetched from array (i+1)
.text
main:
li $t0,0 # difference = 0
la $t3,array # load base addr. of array
la $t2,arrend # load address of array end
j test
loop:
lw $t4,0($t3) # load array[i]
addi $t3,$t3,4 # increment array pointer
lw $t5,0($t3) # load array[i+1]
sub $t0, $t4, $t5 # the difference of two nearby elements
# print value of difference
li $v0,1
addi $a0,$t0,0
syscall
# print comma
li $v0,4
la $a0,comma
syscall
test:
blt $t3,$t2,loop # more to do? if yes, loop
the output should be :
25, -47, -22, -22, 77, 112, -100, -120, 114,
but I get the output 25, -47, -22, -22, 77, 112, -100, -120, 114, -8230,
I found out if I change la $t2,arrend to la $t2,0x10010024, it will work, but I don't know how to write it in the code.
furthermore, how can I improve my code?
Ok... So, you are iterating one more time then you need... Just sub 1 from t2 right after you assign the end pointer of the array.
It's also called bufferoverflow.
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.
C code
int GCD(int m, int n)
{ //Call MACRO PRINTARGS as the first line of code in GCD
//PRINTARGS($a0, $a1)
int result = 0;
// Base Case:
if(m == n)
result = m;
else if (m > n)
// Recursive Case:
result = GCD(m-n, n);
else
// Recursive Case:
result = GCD(m, n-m);
//Call MACRO PRINTARGS as the last line of code in GCD before jr $ra
//PRINTARGS($a0, $a1)
return result;
}
GCD:
my function will go infinite loop if the first argument is large than second argument
(Except the case m=2, n=1)
$a2 is m
$a3 is n
$v1 is where i store the result
The Greatest Common Denominator (GCD) is the largest positive integer that divides the two
numbers without a remainder.
(PRINTARGS is just a function i wrote to print the value of m and n every time it runs the loop)
GCD:
PRINTARGS($a2,$a3)
addi $sp $sp,-4
sw $ra 0($sp)
lb $a0,newline
li $v0,11
syscall #print new line
li $v1,0 # result = 0 result = v1
#base case
bne $a2,$a3,else2 #if m!=n, go to else2
move $v1,$a2 #move value m to v1
j GCD_DONE #jump to GCD_DONE
#Recursive Case: else if (m > n), result = GCD(m-n, n);
else2:
blt $a2,$a3,else3 #if m >n jump to else 3
sub $a2,$a2,$a3 #m-n
jal GCD
else3:
#Recursive Case: result = GCD(m, n-m);
sub $a3,$a3,$a2 #n-m
jal GCD
PRINTARGS($a2,$a3)
GCD_DONE:
addi $sp $sp,4
lw $ra 0($sp)
jr $ra
Two things:
Firstly, in the else2 clause, the function continues execution into else3 after the recursive call. Change to this:
else2:
blt $a2,$a3,else3 #if m >n jump to else 3
sub $a2,$a2,$a3 #m-n
jal GCD
j GCD_DONE
Also, you should be incrementing $sp after restoring from the stack.