Lab 05
Lab 05
Practical Challenge 5
SUBROUTINES AND POINTERS
Vladimir Estivill-Castro
MiPal
February 27, 2024
Objectives
1. Analyse code from MIPS behaviours and subroutines and examine how to translations of C
programs (with subroutines) into the MIPS assembly language behaves.
2. Master the notion of activation record on a stack when when we make use of subroutines,
with a special emphasis on the different types of special registers to manage the subroutine
environment.
3. Command the basic steps before and after subroutine invocation to avoid data loss when
calling a subroutine and subsequently returning to the caller function.
2. consist of a solution using in-depth computing or domain knowledge and an analytical ap-
proach that is based on well-founded principles, because students will require
(a) to construct programs that use subroutines using machine language instructions,
(b) to examine memory cells storing arrays and multi-dimensional arrays
1
(c) to operate on memory values and apply the basic instruction of machine language, and
different indexing mechanisms (including analogous to C pointers and increments on
the indexing variable commensurate with the data type)
(d) to allocate space in a subroutines’ activation vector for actual parameters and local
variables
(a) students may encounter assembly errors, or failures due to many circumstances, which
they need to interpret and correct
(b) the design of a problem-solving code is requested for some functionality that involves
nesting subroutines
4. has significant consequences in a range of contexts, because the simulator provides a con-
ceptual bridge to how software executes on specific hardware
Background
The main goal of this practical challenge is to translate basic constructs from the high-level lan-
guage into MIPS. That is to advance the expertise to construct subroutines for procedural abstrac-
tion.
Students should gain an understanding of subroutine management. The fundamental steps that
manage to ensure the forwarding of control and its return to a caller, while preserving the context of
the caller. The caller wants to have abstraction, using the subroutine for what id achieves without
knowledge of how is it accomplished.
However, is assembly language there is just the basic elements of support for subroutines. At
this level, the caller must use the convention to prepare values for parameters before invoking the
subroutine. Also, a convention needs to be used for the order of parameters and the return of
results.
The subroutine itself is responsible for preserving the context of the caller, and saving in a
FIFO data structure the return address if in itself the subroutine is realising further calls.
We revisit the formula to index an entry in a matrix. Namely mat[i][j] is found with
&mat + (i × NC + j) × size,
Where NC is the number of columns and size is the size of the data type in the matrix.
Previous Study
1. Complete Laboratory 04 that introduces you matrices in MIPS.
2
# i n c l u d e < s t d i o . h>
# i n c l u d e " gcd . h "
# i n c l u d e " gcd . h " i n t main ( )
{
i n t gcd ( i n t f i r s t _ n u m b e r , i n t s e c o n d _ n u m b e r ) { i n t n1 , n2 , t h e _ g c d ;
int result ;
f o r ( i n t i = 1 ; i <= f i r s t _ n u m b e r && i <= s e c o n d _ n u m b e r ; ++ i ) p r i n t f ( " E n t e r two i n t e g e r s : " ) ;
{ / / Checks i f i i s f a c t o r o f both i n t e g e r s s c a n f ("% d %d " , &n1 , &n2 ) ;
i f ( f i r s t _ n u m b e r%i ==0 && s e c o n d _ n u m b e r%i ==0)
result = i ; t h e _ g c d = gcd ( n1 , n2 ) ;
}
return r e s u l t ; p r i n t f ( " The G . C . D o f %d and %d i s %d \ n " , n1 , n2 , t h e _ g c d ) ;
}
return 0;
}
(a) The subroutine
(b) The main program
Figure 1: A main program that calls a subroutine to compute the greatest common divisor of two
integer numbers.
Instructions
This laboratory sessions has several parts. Complete the activities in the different sections.
Activities
A simple subroutine
Consider the C files given in Figure 1. One is a main program, and the other is a simple algorithm
to compute the greatest common divisor presented as a subroutine.
Question 1: Inspect the file test_gcd.s. Run the file test_gcd.s with MARS in a directory
where the file gcd_v1.asm is present. Take a snapshot of the result and report how many
tests does the current version of gcd_v1.asm passes.
Question 2: Write the MIPS translation of the C subroutine in Figure 1 by completing the file
gcd.asm. Change the .include directive in test_gcd.s and see if the current set of
tests is passed. Include a screen shoot of this part in your report.
Question 3: Edit the file test_gcd.s to add at least 5 more tests. In particular, test properties
such as
gcd(x, y) = gcd(y, x), ∀x, y,
gcd(x, kx) = x, ∀x, k and
gcd(x, 1) = 1, ∀x.
Submit the new files gcd.asm and test_gcd.s in your report.
3
Question 4: Create gcd_v2.asm by improving the for loop to start from 2 and not of 1.
Change the .include directive in your test_gcd.s file and include a snapshot of the
result in your report.
Question 5: Explain why the subroutine gcd does not use a stack.
Question 7: Implement the function f in MIPS. Complete the file f.asm and include it to your
submission.
Question 8: Implement a calling program that test the solution (in the basic case n = 1 and in a
couple of larger values of n). Submit this as file test_f.asm.
Question 11: Does your program uses the stack? If so how? Explain.
Question 12: Given that f (n) = 2n , use the original file f.asm, rename it f_with_interation.asm
and implement the same functionality with no stack and just iteration (submit snapshots of
also testing this version with your file test_f.asm.
where
!
a00 a01
A= .
a10 a11
4
where A−1 is the inverse of A.
In the case of a 2 × 2 matrix, there is an explicit form of the inverse.
!
−1 1 a11 −a01
A = ,
λ(A) −a10 a00
Question 13: Translate Figure 2 into the MIPS assembly language (using the template P5_E4.s).
You are required to submit the file P5_E4.s with your code. Note that, given that we work
with integers, in this case it is acceptable if your the solution will only be valid for matrices
that have an inverse with integer entries. That is, even if all entries of A are integers (which
ensures the determinant is an integer) when dividing by the determinant to calculate the in-
verse of the matrix, there could be a fractional part of the resulting numbers that would be
lost and the solution of the system of equations will not be exact.
Question 14: Visualize the evolution of the stack in the Data Segment window, selecting current
$sp from the drop-down menu. In your report, include a screen shot of the state of the stack
at every activation block of every subroutine (if it has one) and indicate the corresponding
subroutine for the image and its the data.
Question 15: Explain which of the $s registers you have used in subroutines and for what pur-
pose.
5
# i n c l u d e < s t d i o . h>
# i n c l u d e < s t d l i b . h>
i n t B[ 2 ] = { 4 , − 2 } ;
int C[2];
i n t d e t e r m i n a n t ( i n t ** m a t r i x ) {
return ( matrix [ 0 ] [ 0 ] * matrix [ 1 ] [ 1 ] − matrix [ 0 ] [ 1 ] * matrix [ 1 ] [ 0 ] ) ;
}
i n t i n v e r s e ( i n t ** m a t r i x , i n t ** m a t r i x _ i n v e r t e d , i n t N)
{
int det ;
i f (N> 0 )
{ det = determinant ( matrix ) ;
p r i n t f ( " d e t=%d \ n " , d e t ) ;
i f ( det !=0)
{ matrix_inverted [0][0]= matrix [1][1] / det ;
matrix_inverted [1][1] = matrix [0][0] / det ;
m a t r i x _ i n v e r t e d [ 0 ] [ 1 ] = −1 * m a t r i x [ 0 ] [ 1 ] / d e t ;
m a t r i x _ i n v e r t e d [ 1 ] [ 0 ] = −1 * m a t r i x [ 1 ] [ 0 ] / d e t ;
return 1;
}
}
return 0;
}
i n t main ( )
{ int invertible ;
i n t ** A = ( i n t * * ) c a l l o c ( 2 , s i z e o f ( i n t * ) ) ;
int vector_0 [2]={1 , 2};
int vector_1 [2]={3 , 7};
A[ 0 ] = v e c t o r _ 0 ;
A[ 1 ] = v e c t o r _ 1 ;
i n t ** A _ i n v e r t e d = ( i n t * * ) c a l l o c ( 2 , s i z e o f ( i n t * ) ) ;
int inverse_0 [2];
int inverse_1 [2];
A_inverted [0]= inverse_0 ;
A_inverted [1]= inverse_1 ;
i n v e r t i b l e = i n v e r s e ( A, A _ i n v e r t e d , 2 ) ;
p r i n t f ( " I [0][0]=%d , I [0][1]=% d \ n" , A_inverted [ 0 ] [ 0 ] ,
A_inverted [ 0 ] [ 1 ] ) ;
p r i n t f ( " I [1][0]=%d , I [1][1]=% d \ n" , A_inverted [ 1 ] [ 0 ] ,
A_inverted [ 1 ] [ 1 ] ) ;
i f ( i n v e r t i b l e !=0) {
C[ 0 ] = B[ 0 ]* A_inverted [ 0 ] [ 0 ] + B[ 1 ]* A_inverted [ 0 ] [ 1 ] ;
C[ 1 ] = B[ 0 ]* A_inverted [ 1 ] [ 0 ] + B[ 1 ]* A_inverted [ 1 ] [ 1 ] ;
p r i n t f ( "X=%d , Y=%d \ n " , C [ 0 ] , C [ 1 ] ) ;
}
else
p r i n t f ( " Matrix not i n v e r t i b l e \ n" ) ;
}
6
.data
x : . h a l f 65
res : .byte 0
.text
my_main :
a d d i u $sp , $sp , −4 # A l l o c a t e one s p a c e i n s t a c k
sw $ r a , 0 ( $ s p ) # s a v e t h e v a l u e o f $ r a
la $t0 , x
lh $t1 , 0( $t0 )
li $t2 , 0
li $t9 , 1
while :
b l e $ t 1 , $ t 9 , end
s r l $t1 , $t1 , 1
addiu $t2 , $t2 , 1
b while
end :
la $t9 res
sb $t2 , 0( $t9 )
lw $ r a , 0 ( $ s p )
a d d i u $sp , $sp , 4
j r $ra
DecimalToBinaryArray.c DecimalToBinary.c
# include < s t d i o . h> # include < s t d i o . h>
# include " BinaryLength . h" # include " BinaryLength . h"
# include " ToBinary . h " # include " ToBinaryPointer . h"
# include " PrintBinary . h" # include " PrintBinaryPointer . h"
i n t main ( ) { i n t main ( ) {
int decimal_value ; int decimal_value ;
s c a n f ( "%d " ,& d e c i m a l _ v a l u e ) ; s c a n f ( "%d " ,& d e c i m a l _ v a l u e ) ;
p r i n t f ( "%d= " , d e c i m a l _ v a l u e ) ; p r i n t f ( "%d= " , d e c i m a l _ v a l u e ) ;
int length = BinaryLength ( decimal_value ) ; int length = BinaryLength ( decimal_value ) ;
char b i t s [ l e n g t h ] ; char w _ p o i n t e r [ l e n g t h ] ;
ToBinary ( decimal_value , b i t s , length , 0 ) ; ToBinaryPointer ( decimal_value , w_pointer ) ;
PrintBinary ( bits , length , length −1); P r i n t B i n a r y P o i n t e r (& w _ p o i n t e r [ l e n g t h − 1 ] , w _ p o i n t e r ) ;
p r i n t f ( " \ n" ) ; p r i n t f ( " \ n" ) ;
} }
Figure 4: C programs (array and pointer versions) to convert from decimal to binary.
7
bl :
a d d i u $sp , $sp , −8
sw $ r a , 4 ( $ s p ) # r e c u r s i v e
sw $s0 , 0 ( $ s p ) # one p a r a m e t e r
bl_end_if :
lw $ r a , 4 ( $ s p )
lw $s0 , 0 ( $ s p )
a d d i u $sp , $sp , 8
j r $ra
Figure 5: C function and its MIPS equivalent that finds the binary length of value that fits in an
int
Question 17: What local variables of the function BinaryLength are stored in the stack?
Question 18: The BinaryLength subroutine receives its parameter by reference. This means
that it does not need to allocate space for the vector of decimal digits it works on. In fact, it
uses this vector to compute the new value (the decimal representation divided by two). What
does this imply for the main program? That is, if we were to complete the main program to
not only find the length of the binary representation, but actually compute it after finding the
length (like the C codes do in Figure 4)
The start up file startupLog2.s and the equivalent MIPS code to Figure 6 is provided in
the file test_log_2.asm and shown in Figure 8.
1. subroutine
8
# i n c l u d e < s t d i o . h>
# i n c l u d e < s t d l i b . h>
# include " BinaryLength . h"
i n t main ( )
{ i n t d e c i m a l _ d i g i t s =0;
do { p r i n t f ( "How many d i g i t s i s y o u r number i n d e c i m a l ? " ) ;
s c a n f ( "%d " ,& d e c i m a l _ d i g i t s ) ;
} while ( d e c i m a l _ d i g i t s <=0);
char n u m b e r _ i n _ d e c i m a l [ d e c i m a l _ d i g i t s ] ;
p r i n t f ( "We r e a d now %d d e c i m a l d i g i t s : " , d e c i m a l _ d i g i t s ) ;
g e t c ( s t d i n ) ; / / p a s s o v e r t h e e n t e r <CR> f r o m u s e r
f o r ( i n t i = 0 ; i < d e c i m a l _ d i g i t s ; i ++)
{ number_in_decimal [ i ] = getc ( s t d i n ) ;
i f ( number_in_decimal [ i ]< ’0 ’ | | number_in_decimal [ i ]> ’9 ’ )
{ f p r i n t f ( s t d e r r , " Not a d e c i m a l d i g i t i n i n p u t \ n " ) ;
exit (1);
}
i f ( ’ 0 ’ == n u m b e r _ i n _ d e c i m a l [ 0 ] )
{ f p r i n t f ( s t d e r r , " D e c i m a l d i g i t ’ 0 ’ c a n ’ t be f i r s t i n i n p u t \ n " ) ;
exit (1);
}
}
p r i n t f ( " \ nWe work w i t h t h e number : " ) ;
f o r ( i n t i = 0 ; i < d e c i m a l _ d i g i t s ; i ++)
{ p r i n t f ( "%c " , n u m b e r _ i n _ d e c i m a l [ i ] ) ;
i f ( ( ( ( d e c i m a l _ d i g i t s − i ) % 3 ) ==1) && ( ( d e c i m a l _ d i g i t s − i ) >2) )
{ printf (" ," ); }
}
p r i n t f ( " \ n" ) ;
i n t l e n g t h _ i n _ b i n a r y = BinaryLength ( number_in_decimal , d e c i m a l _ d i g i t s ) ;
p r i n t f ( " The number o f b i t s r e q u i r e d i s %d \ n " , l e n g t h _ i n _ b i n a r y ) ;
}
i n t B i n a r y L e n g t h ( char * n u m b e r _ i n _ d e c i m a l , i n t d e c i m a l _ d i g i t s )
{ i n t l a s t _ d i g i t = n u m b e r _ i n _ d e c i m a l [ d e c i m a l _ d i g i t s −1] − ’ 0 ’ ;
i f ( ( d e c i m a l _ d i g i t s <=1) && ( ( 0 = = l a s t _ d i g i t ) | | (1== l a s t _ d i g i t ) )
) { return 1; }
else { int digits_after_div_2 = decimal_digits ;
i n t c a r r y =0; i n t s t a r t =0;
i f ( ’ 1 ’ == n u m b e r _ i n _ d e c i m a l [ 0 ] )
{ d i g i t s _ a f t e r _ d i v _ 2 − −; c a r r y = 1 ; s t a r t = 1 ; }
i n t j =0;
f o r ( i n t i = s t a r t ; i < d e c i m a l _ d i g i t s ; i ++ , j ++)
{ in t f r o n t _ d i g i t = number_in_decimal [ i ]− ’0 ’ ;
i n t n e w _ f r o n t _ d i g i t = ( ( 1 0 * c a r r y ) + f r o n t _ d i g i t ) > >1;
number_in_decimal [ j ]= ’ 0 ’+ n e w _ f r o n t _ d i g i t ;
carry = ((10* carry ) + f r o n t _ d i g i t ) & 1;
} / / for
return 1 + BinaryLength ( number_in_decimal , d i g i t s _ a f t e r _ d i v _ 2 ) ;
} // else
} / / BinaryLength
Figure 7: C function to compute the length of binary representation of a very long decimal value.
9
.data bne $ t 4 , $ t 3 , skip_second_if
m e s s a g e _ 1 : . a s c i i z "How many d i g i t s i s y o u r number i n d e c i m a l ? " l a $a0 , m e s s a g e _ 5
m e s s a g e _ 2 : . a s c i i z "We r e a d now " l i $v0 , 4 # p r i n t s t r i n g
message_3 : . a s c i i z " decimal d i g i t s : " syscall
m e s s a g e _ 4 : . a s c i i z " Not a d e c i m a l d i g i t i n i n p u t \ n " li $v0 , 10 # t e r m i n a t e p r o g r a m
m e s s a g e _ 5 : . a s c i i z " D e c i m a l d i g i t ’ 0 ’ c a n ’ t be f i r s t i n i n p u t \ n " syscall
m e s s a g e _ 6 : . a s c i i z " \ nWe work w i t h t h e number : " skip_second_if :
m e s s a g e _ 7 : . a s c i i z " The number o f b i t s r e q u i r e d i s " a d d i u $ t 1 , $ t 1 , 1 # i ++
number_in_decimal_base : .word # p o i n t e r to base of a r r a y b for_loop
.text end_loop :
. i n c l u d e " BinaryLength.asm " l a $a0 , m e s s a g e _ 6
. g l o b l my_main l i $v0 , 4 # p r i n t s t r i n g
my_main : syscall
a d d i u $sp , $sp , −12 l i $ t 1 , 0 # $ t 1 i s i <− 0
sw $ r a , 8 ( $ s p ) f o r _ o u t : bge $ t 1 , $s0 , e n d _ f o r _ o u t
sw $s0 , 4 ( $ s p ) # d e c i m a l _ d i g i t s addu $ t 2 , $sp , $ t 1 # $ t 2 <− @number_in_decimal [ i ]
sw $s1 , 0 ( $ s p ) # # d e c i m a l _ d i g i t s i n words l b $a0 , 0 ( $ t 2 ) # $a0 <− n u m b e r _ i n _ d e c i m a l [ i ]
l i $v0 , 11 # p r i n t _ c h a r
l i $s0 , 0 # d e c i m a l _ d i g i t s <− 0 syscall
do_read : # a comma e v e r y 3
l a $a0 , m e s s a g e _ 1 sub $ t 4 , $s0 , $ t 1 # $ t 4 <− d e c i m a l _ d i g i t s − i
l i $v0 , 4 # p r i n t s t r i n g l i $t5 , 3
syscall div $t4 , $t5
l i $v0 , 5 # r e a d i n t e g e r mfhi $t6
syscall bne $ t 6 , 1 , s k i p _ p r i n t _ c o m a
move $s0 , $v0 l i $t5 , 2
b l e z $s0 , d o _ r e a d b l e $t4 , $t5 , s k i p _ p r i n t _ c o m a
# compute s i z e t o i n c r e a s e SP ( r o u n d t o words ) l i $a0 , 44 # comma=ASCII ( )
move $s1 , $ s 0 l i $v0 , 11 # p r i n t _ c h a r
a d d i u $s1 , $s1 , 3 syscall
s r a $s1 , $s1 , 2
s l l $s1 , $s1 , 2 skip_print_coma :
# a l l o c a t e space fo r char number_in_decimal a d d i u $ t 1 , $ t 1 , 1 # i ++
s u b u $sp , $sp , $ s 1 b for_out
l a $a0 , m e s s a g e _ 2 end_for_out :
l i $v0 , 4 # p r i n t s t r i n g l i $a0 , 10 # e n d _ o f _ l i n e
syscall l i $v0 , 11 # p r i n t _ c h a r
move $a0 , $ s 0 syscall
l i $v0 , 1
syscall move $a0 , $ s p # t h e b a s e a d d r e s s o f t h e a r r a y i s f i r s t param
l a $a0 , m e s s a g e _ 3 move $a1 , $ s 0 # t h e l e n g t h i s s e c o n d p a r a m e t e r
l i $v0 , 4 # p r i n t s t r i n g j a l BinaryLength
syscall #
# loop r e a d a l l decima f i g i t move $ t 1 , $v0 # r e t r i e v e t h e r e s u l t
l i $ t 1 , 0 # $ t 1 i s i <− 0 l a $a0 , m e s s a g e _ 7
f o r _ l o o p : bge $ t 1 , $s0 , e n d _ l o o p l i $v0 , 4 # p r i n t s t r i n g
l i $v0 , 12 # r e a d c h a r syscall
syscall move $a0 , $ t 1 # t o o u t p u t r e s u l t
addu $ t 2 , $sp , $ t 1 # $ t 2 <− @number_in_decimal [ i ] l i $v0 , 1 # print integer
s b $v0 , 0 ( $ t 2 ) syscall
l i $ t 3 , 48 # ASCII ( 4 8 ) = ’ 0 ’ l i $a0 , 10 # e n d _ o f _ l i n e
bge $v0 , $ t 2 , s k i p _ f i r s t _ i f l i $v0 , 11 # p r i n t _ c h a r
l i $ t 3 , 57 # ASCII ( 5 7 ) = ’ 9 ’ syscall
b l e $v0 , $ t 3 , s k i p _ f i r s t _ i f
l a $a0 , m e s s a g e _ 4 # d e a l l o c a t e in the stack
l i $v0 , 4 # p r i n t s t r i n g addu $sp , $sp , $ s 1
syscall lw $ r a , 8 ( $ s p )
li $v0 , 10 # t e r m i n a t e p r o g r a m lw $s0 , 4 ( $ s p ) # d e c i m a l _ d i g i t s
syscall lw $s1 , 0 ( $ s p ) # # d e c i m a l _ d i g i t s i n words
skip_first_if : a d d i u $sp , $sp , 12
l i $ t 3 , 48 # ASCII ( 4 8 ) = ’ 0 ’ j r $ra
l b $ t 4 , 0 ( $ s p ) # $ t 4 <− n u m b e r _ i n _ d e c i m a l [ 0 ]
10
2. activation record
3. stack.
The report must have answers (in your own words) to all questions.
Each pair of students (or individually) must submit a PDF document containing the answers
to all exercises and the text from the resulting code used in them. Package those files that are
solutions to programming exercises in a ZIP file and include the PDF and your MIPS files. The
name of the .ZIP must contain: Your NIA, surname and session number. Following this format:
NIA1_NIA2_surname1_surname2_L5.zip
Submit the ZIP file via the Moodle (no email) using the corresponding lab submission feature.
11