Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

MIPS_primery_zadach / dandamudi05gtr guide risc processors programmers engineers

.pdf
Скачиваний:
77
Добавлен:
11.05.2015
Размер:
1.39 Mб
Скачать

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

Соседние файлы в папке MIPS_primery_zadach