
MIPS_primery_zadach / dandamudi05gtr guide risc processors programmers engineers
.pdf
Chapter 15 • |
Logical and Shift Operations |
287 |
|
55: |
li |
$t1,0xE0000000 |
# mask = 0xE0000000 |
56: |
|
|
|
57:# print the most significant octal digit
58:srl $t2,$t0,1
59:and $t2,$t2,$t1
60:rol $a0,$t2,3
61: |
li |
$v0,1 |
# print octal digit |
62:syscall
63:sll $t0,$t0,2
65: |
li |
$t3,10 |
# loop count = 10 |
66: |
|
|
|
67:octal_loop:
68:and $t2,$t0,$t1
69:rol $a0,$t2,3
70: |
li |
$v0,1 |
# print octal digit |
71:syscall
72: |
sll $t0,$t0,3 |
# shift number by 3 bits |
73:sub $t3,$t3,1
74:bnez $t3,octal_loop
76: jr $ra
The print_octal procedure, shown in Program 15.2, follows the pseudocode with the following mapping. The number is stored in $t0 whereas the mask is in $t1. The $t2 register is used as temp. The loop count is maintained in the $t3 register.
Example 15.8 Even parity encoding.
In this example, we parity encode a byte and print its encoded value in the binary form. We use even parity in this example. The most significant bit is used for the parity bit. The idea is simple: if the number of 1s in the remaining 7 bits is even, make the parity bit zero so that the number of 1s, including the parity bit, is even. If the count is odd, we have to make the parity bit a 1 so that the number of 1s is even. We have discussed parity encoding in Examples 15.1 and 15.3.
The main program prompts the user for a character and passes it on to the parity procedure. The parity procedure receives the character via $a0. When it receives the character, the parity bit is zero. Thus, to count the number of 1s, we use all 8 bits. The procedure calls another procedure (count_bits) to count the number of 1s. This function returns the count in $v0. The count_bits procedure is based on the print_bin procedure we used in Program 15.1.
The parity procedure then tests to check if the count is even or odd. This test is done by looking at the least significant bit of the count. We do this by using the and instruction on line 58. If the count is even, we do nothing as the parity bit is zero. If it is odd, we make the parity bit 1 by using the or instruction on line 60.
288 |
|
Guide to RISC Processors |
|
Program 15.3 Even parity encoding example |
|
1: |
# Even parity encoding |
PARITY.ASM |
2:#
3:# Objective: To encode a character using even parity
4:# Input: Requests a character from the user.
5:# Output: Outputs parity encoded byte in binary.
6:#
7:# $a0 - input character
8:#
9:################### Data segment #######################
10:.data
11:ch_prompt:
12: |
.asciiz |
"Please enter a character: \n" |
13:out_msg:
14: |
.asciiz |
"\nThe parity encoded value is: " |
15:newline:
16: .asciiz "\n"
17:ch:
18: |
.space |
2 |
19: |
|
|
20:################### Code segment #######################
21:.text
22:.globl main
23:main:
24: |
la |
$a0,ch_prompt |
# prompt for a character |
25:li $v0,4
26:syscall
28: |
la |
$a0,ch |
# read the input character |
29:li $a1,2
30:li $v0,8
31:syscall
33: |
la |
$a0,out_msg |
# write output message |
34:li $v0,4
35:syscall
37: |
lb |
$a0,ch |
# |
$a0 = input character |
38: |
jal |
parity |
|
|
39: |
|
|
|
|
40: |
la |
$a0,newline |
# |
output newline |
41:li $v0,4
42:syscall
44: |
li |
$v0,10 |
# exit |
45:syscall
Chapter 15 • Logical and Shift Operations |
289 |
46:
47:#------------------------------------------------
48:# PARITY receives a character in $a0 and
49:# prints its binary value.
50:# $t0 - temporary
51:# $t1 - holds the mask byte
52:#------------------------------------------------
53:parity:
54: |
sub |
$sp,$sp,4 |
# save $ra |
55: |
sw |
$ra,0($sp) |
|
56: |
|
|
|
57: |
jal |
count_bits |
# returns count in $v0 |
58: |
and |
$t0,$v0,1 |
# count is odd or even? |
59: |
beqz |
$t0,skip_parity |
# if even, skip (no action) |
60: |
or |
$a0,$a0,0x80 |
# else, parity = 1 |
61: |
skip_parity: |
|
|
62: |
jal |
print_bin |
|
63: |
|
|
|
64: |
lw |
$ra,0($sp) |
# restore $ra |
65:add $sp,$sp,4
66:jr $ra
67:
68:#------------------------------------------------
69:# COUNT_BITS receives a character in $a0 and
70:# returns the number of 1 bits in $v0.
71:# Preserves $a0.
72:# $t0 - temporary
73:# $t1 - holds the mask byte
74:#------------------------------------------------
75:count_bits:
76: |
li |
$v0,0 |
# |
count (in $v0) = 0 |
77: |
li |
$t1,0x80 |
# |
mask = 0x80 |
78:count_loop:
79:and $t0,$a0,$t1
80:beqz $t0,skip_count
81: |
addu $v0,$v0,1 |
# increment count |
82:skip_count:
83: |
srl |
$t1,$t1,1 |
# shift mask byte |
84: |
bnez |
$t1,count_loop |
# exit loop if mask is 0 |
85: |
|
|
|
86: |
jr |
$ra |
|
87: |
|
|
|
88:#------------------------------------------------
89:# PRINT_BIN receives a character in $a0 and
90:# prints its binary value.
91:# $t0 - holds the character
92:# $t1 - holds the mask byte

290 |
Guide to RISC Processors |
93:# $t2 - temporary
94:#------------------------------------------------
95:print_bin:
96: |
move |
$t0,$a0 |
# |
char. in $t0 |
97: |
li |
$t1,0x80 |
# |
mask = 0x80 |
98:loop:
99:and $t2,$t0,$t1
100:beqz $t2,zero
101: |
li |
$a0,1 |
# print 1 |
102:b skip
103:zero:
104: |
li |
$a0,0 |
# print 0 |
105:skip:
106:li $v0,1
107:syscall
109: |
srl |
$t1,$t1,1 |
# |
shift mask byte |
110: |
bnez |
$t1,loop |
# |
exit loop if mask is 0 |
111: |
|
|
|
|
112:jr $ra
Once the byte is parity encoded, the parity procedure calls the print_bin procedure to print the parity-encoded byte in binary form. Note that the print_bin procedure is the same as that used in Program 15.1.
Summary
We discussed the logical, shift, and rotate instructions available in the MIPS assembly language. Logical instructions are useful to implement bitwise logical operators and Boolean expressions. However, in some instances Boolean expressions can also be implemented by using conditional jump instructions without using the logical instructions.
Shift and rotate instructions provide flexibility to bit manipulation operations. There are two types of shift instructions: logical shifts work on unsigned data and the arithmetic shifts are meant for signed data.
Shift instructions can be used to multiply or divide by a number that is a power of 2. Shifts for such arithmetic operations are more efficient than the corresponding arithmetic instructions. We have demonstrated the application of these instructions through several examples.

16
Recursion
We can use recursion as an alternative to iteration. Solutions to some problems can be naturally expressed using recursion. We start this chapter with an overview of recursive procedures. Next we give some example recursive procedures in the MIPS assembly language. The advantages and pitfalls associated with a recursive solution as opposed to an iterative solution are discussed towards the end of the chapter.
Introduction
A recursive procedure calls itself, either directly or indirectly through another procedure. In direct recursion, a procedure calls itself directly. In indirect recursion, procedure P makes a call to procedure Q, which in turn calls procedure P. The sequence of calls could be longer before a call is made to procedure P.
Recursion is a powerful tool that allows us to express our solution elegantly. Some solutions can be naturally expressed using recursion. Computing factorials is a classic example. Factorial n, denoted n!, is the product of positive integers from 1 to n. For example,
5! = 1 × 2 × 3 × 4 × 5.
The factorial function can be formally defined as
factorial(0) = 1
factorial(n) = n * factorial(n − 1) for n > 0.
Recursion shows up in this definition as we define factorial(n) in terms of factorial(n −1). Every recursive function should have a termination condition to end the recursion. In this example, when n = 0, recursion stops. Some other examples include the binary search, quicksort, and Fibonacci function. Towards the end of this chapter, we give examples of quicksort and the Fibonacci function.
291

292 |
Guide to RISC Processors |
How do we express such recursive functions in programming languages? Let us first look at how this function is written in C:
int fact(int n)
{
if (n == 0) return(1);
return(n * fact(n-1));
}
This is an example of direct recursion. How is this function implemented? At the conceptual level, its implementation is not any different from implementing other procedures. Once you understand that each procedure call instance is distinct from the others, the fact that a recursive procedure calls itself does not make a big difference.
Each active procedure maintains an activation record, which is stored on the stack. The activation record, which consists of the parameters, return address, local variables, and a frame pointer, comes into existence when a procedure is invoked and disappears when the procedure is terminated. Thus, for each procedure that is not terminated, an activation record that contains the state of that procedure is stored. A stack is used to keep these activation records. The number of activation records, and hence the amount of stack space required to run the program, depend on the depth of recursion.
Figure 16.1 shows the stack activation records for factorial(3). As you can see from this figure, each call to the factorial function creates an activation record. In the next two sections we look at some example recursive procedures in the MIPS assembly language.
Our First Program
In MIPS, we can write procedures without using the stack. For simple leaf procedures, we do not have to use the stack. The availability of a large number of registers allows us to use register-based parameter passing. However, when we write recursive procedures, we have to use the stack because recursive procedures are essentially nonleaf procedures (see our discussion of leaf and nonleaf procedures on page 189).
To understand how we can write a recursive procedure, let us look at the factorial function. We have defined factorial at the beginning of this chapter. We implement a slightly modified definition of factorial, as implemented in Chapter 13 (see the example on page 230 that implements the iterative version).
fact(n) = 1 for n ≤ 1.
= n * fact(n − 1) for n ≥ 2.
Program 16.1 requests an integer n from the user and passes this on to the factorial procedure (fact) via the $a0 register. After returning from the procedure, it outputs fact(n).

Chapter 16 • Recursion |
293 |
||||
|
Call |
Return |
|||
|
n = 3 |
|
|
factorial(3) = 6 |
|
|
|
|
|||
|
|
|
|
|
|
Afactorial(3) = 3 * factorial(2)
|
|
|
|
|
|
n = 2 |
|
factorial(2) = 2 |
|
||
Activation |
|||||
|
|
|
|
||
|
|
|
|
record for A |
Bfactorial(2) = 2 * factorial(1)
|
|
|
|
|
|
Activation |
|
n = 1 |
|
|
factorial(1) = 1 |
||
|
|
|
record for B |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C |
factorial(1) = 1 * factorial(0) |
|
Activation |
|||
|
|
|
|
|
|
record for C |
|
n = 0 |
|
|
factorial(0) = 1 |
||
|
|
|
|
|||
|
|
|
|
|
|
Activation |
|
|
|
|
|
|
|
D |
|
factorial(0) = 1 |
|
|
record for D |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Recursion termination |
|
|
|
||
|
|
(a) |
|
|
(b) |
Figure 16.1 Recursive computation of factorial(3).
Program 16.1 Computing factorial—an example recursive function
1: # Finds factorial of a number |
FACTORIAL.ASM |
2:#
3:# Objective: Computes factorial of an integer.
4: # |
To demonstrate recursive procedures. |
5:# Input: Requests an integer n from the user.
6:# Output: Outputs n!
7:#
8:# $a0 - used to pass n
9:# $v0 - used to return result
10:#
11:################### Data segment #######################
12:.data
13:prompt:
14: |
.asciiz |
"Please enter a positive integer: \n" |
15:out_msg:
16: |
.asciiz |
"The factorial is: " |
17:error_msg:
18: |
.asciiz |
"Not a positive number. Try again.\n" |
19:newline:
294 |
Guide to RISC Processors |
20: .asciiz "\n" 21:
22:################### Code segment #######################
23:.text
24:.globl main
25:main:
26: |
la |
$a0,prompt |
# prompt user for input |
27:li $v0,4
28:syscall
30:try_again:
31: |
li |
$v0,5 |
# read number into $a0 |
32:syscall
33:move $a0,$v0
35:bgez $a0,num_OK
36: |
la |
$a0,error_msg |
# write error message |
37:li $v0,4
38:syscall
39: |
b |
try_again |
40: |
|
|
41:num_OK:
42:jal fact
43:move $s0,$v0
45: |
la |
$a0,out_msg |
# write output message |
46:li $v0,4
47:syscall
49: |
move $a0,$s0 |
# output factorial |
50:li $v0,1
51:syscall
53: |
la |
$a0,newline |
# write newline |
54:li $v0,4
55:syscall
57: |
li |
$v0,10 |
# exit |
58: |
syscall |
|
|
59: |
|
|
|
60:#------------------------------------------------------
61:# FACT receives n in $a0 and returns the result in $v0.
62:# It uses recursion to find n!
63:#------------------------------------------------------
64:fact:
65: |
subu |
$sp,$sp,4 |
# |
allocate stack space |
66: |
sw |
$ra,0($sp) |
# |
save return address |

Chapter 16 • |
Recursion |
295 |
67: |
|
|
68: |
bgt $a0,1,one_up |
# recursion termination |
69:li $v0,1
70: |
b |
return |
71: |
|
|
72:one_up:
73: |
subu $a0,$a0,1 |
# recursion with (n-1) |
74:jal fact
75:addu $a0,$a0,1
76: |
mulou $v0,$a0,$v0 |
# $v0 = $a0*$v0 |
77: |
|
|
78:return:
79: |
lw |
$ra,0($sp) |
# |
restore return address |
80: |
addu |
$sp,$sp,4 |
# |
clear stack space |
81:jr $ra
First we have to determine the state information that needs to be saved (i.e., our activation record). In all procedures, we need to store the return address. This is what we did for nonleaf procedures (see Example 11.2 on page 202). In our factorial example, we need to keep track of the current value in $a0. However, we don’t really have to save $a0 on the stack as we can restore its value by adding 1, as shown on line 75. Thus, we save just the return address (lines 65 and 66) and restore it on lines 79 and 80. The body of the procedure can be divided into two parts: recursion termination and recursive call. The recursion termination condition is implemented on lines 68–70.
If the value is more than 1, a recursive call is made with (n − 1) (lines 73 and 74). After the call is returned, $a0 is incremented to make it n before multiplying it with the values returned for (n − 1)! in $v0 (lines 75 and 76).
In this example, each recursive call takes four bytes of stack space. Thus, the amount of stack space required to run the program is 4n bytes. In general, the stack space required by recursive procedures is proportional to the number of recursive calls.
Illustrative Examples
In the last section, we introduced a simple program to illustrate how recursion is implemented in the MIPS assembly language. In this section, we present two more examples to further illustrate the concepts introduced in this chapter.
Example 16.1 Compute the Fibonacci function.
We have seen an iterative version of this function we write a recursive procedure to compute fib(n). defined as follows.
in Chapter 11 (see page 200). Here Recall that the Fibonacci function is
296 |
Guide to RISC Processors |
fib(1) = 1, fib(2) = 1,
fib(n) = fib(n − 1) + fib(n − 2) for n > 2.
The main program takes care of the user interface. It requests n and invokes the fib procedure and outputs the fib(n) value returned by the procedure. It invokes the procedure on line 32 after copying n into the $a0 register.
The fib procedure stores the return address on the stack as in the last example. In addition, we need to keep two values on the stack: the last and second to last Fibonacci values that are in $v0 and $s0, respectively. Thus, our stack frame consists of three values: $ra, $v0, and $s0 (see lines 57–60). These three registers are restored on lines 80–83. The fib procedure is recursively called on lines 69 and 74 to compute fib(n − 1) and fib(n − 2), respectively. The rest of the code is straightforward to follow.
Program 16.2 Fibonacci function using recursion
1: # Computes Fibonacci number |
FIB_RECUR.ASM |
2:#
3:# Objective: Computes the Fibonacci number.
4: # |
Uses recursion. |
5:# Input: Requests a number n.
6:# Output: Outputs Fibonacci number fib(n).
7:#
8:# $a0 - number n is passed
9:# $v0 - fib(n) is returned
10:#
11:################### Data segment ###################
12:.data
13:prompt:
14: |
.asciiz |
"Please enter a number n: \n" |
15:out_msg:
16: |
.asciiz |
"Fib(n) = " |
17:newline:
18: .asciiz "\n" 19:
20:################### Code segment ###################
21:.text
22:.globl main
23:main:
24: |
la |
$a0,prompt |
# prompt user for input |
25:li $v0,4
26:syscall
28: |
li |
$v0,5 |
# read number into $a0 |