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

MIPS_primery_zadach / dandamudi05gtr guide risc processors programmers engineers

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

176

 

Guide to RISC Processors

15:

.asciiz

"Please enter a number (<11 digits): "

16:out_msg:

17:

.asciiz

"\nThe sum of individual digits is: "

18:newline:

19: .asciiz "\n"

20:number:

21:

.space

11

# space for input string

22:

 

 

 

23:#################### Code segment #######################

24:.text

25:.globl main

26:main:

27:

la

$a0,number_prompt # prompt user for input

28:li $v0,4

29:syscall

31:

la

$a0,number

#

read

the input number

32:

li

$a1,11

#

as a

string

33:li $v0,8

34:syscall

36:

la

$a0,out_msg

# write output message

37:li $v0,4

38:syscall

40:

la

$t0,number

#

pointer to number

41:

li

$t2,0

#

init sum to zero

42:loop:

43:lb $t1,($t0)

44:

beq

$t1,0xA,exit_loop

# if linefeed or

 

45:

beqz

$t1,exit_loop

# NULL, we are done

 

46:

and

$t1,$t1,0x0F

# strip off upper 4

bits

47:

addu

$t2,$t2,$t1

# add to running total

48:

addu

$t0,$t0,1

# increment pointer

49:

b

loop

 

 

50:

exit_loop:

 

 

 

51:

move

$a0,$t2

# output sum

 

52:li $v0,1

53:syscall

55:

la

$a0,newline

# output newline

56:li $v0,4

57:syscall

59:

li

$v0,10

# exit

60:syscall

Chapter 10 Assembly Language Overview

177

The loop body to compute the sum follows the pseudocode given before. The input digit, which is in character form, must be converted to its numeric equivalent. Because ASCII assigns a special set of contiguous values to the digit characters, this conversion is straightforward. All we have to do is to mask off the upper half of the byte. This conversion is done by

and $t1,$t1,0xF

on line 46. Alternatively, we could also subtract the character code for 0 as

sub $t1,$t1,’0’

instead of masking the upper half-byte. This numeric value is added to the sum (maintained in $t2) on line 47. The string pointer in $t0 is incremented to point to the next input digit (line 48). When the loop is exited, we simply output the sum in the $t2 register.

Example 10.2 Conversion of lowercase letters to uppercase.

This program demonstrates how character manipulation can be used to convert lowercase letters to uppercase. The program receives a character string from the user and converts all lowercase letters to uppercase and displays the string. Characters other than the lowercase letters are not changed in any way. The pseudocode of Program 10.3 is shown below:

main()

prompt user for input read input string write output sum message index := 0

char := string[index] while (char =NULL)

if ((char a) AND (char z)) then

char := char + Aaend if

index := index + 1

char := string[index] end while

output string exit

end main

We can see from Program 10.3 that the compound if condition requires two conditional branch instructions. The conditional branch instruction blt on line 41 tests if the character is less than a. If so, it is not a lowercase character and conversion is not done.

178

Guide to RISC Processors

Similarly, the other condition, character > z, is tested by the bgt instruction on line 42. If both these tests are false, the character is a lowercase letter. In this case, we use

addu $t1,$t1,-32

to convert it to an uppercase letter (line 43). For example, if the character is a, the character is represented by 0x61 (or 97 in decimal). By subtracting 32 (or 0x20), we get 0x41 or 65 in decimal. As you know, this value represents A in ASCII. Of course, we can do this case conversion by using the subtract instruction as shown here:

sub $t1,$t1,32

We can also use the logical and instruction to force the 5th bit to zero. This can be easily done by

and $t1,$t1,0xDF

The original or converted character is written back into the string using the sb instruction on line 46. After the loop is terminated, we display the converted string (lines 50–52) and exit.

Program 10.3 Conversion of lowercase letters to uppercase

1: # Uppercase conversion of characters

TOUPPER.ASM

2:#

3:# Objective: To convert lowercase letters to

4: #

corresponding uppercase letters.

5:# Input: Requests a character string from the user.

6:# Output: Prints the input string in uppercase.

7:#

8:# $t0 - points to character string

9:# $t1 - used for character conversion

10:#

11:################### Data segment #####################

12:.data

13:name_prompt:

14:

.asciiz

"Please type your name: \n"

15:out_msg:

16:

.asciiz

"Your name in capitals is: "

17:in_name:

18:

.space

31

# space for input string

19:

 

 

 

20:################### Code segment #####################

21:.text

22:.globl main

23:main:

24:

la

$a0,name_prompt # prompt user for input

Chapter 10 Assembly Language Overview

179

25:li $v0,4

26:syscall

28:

la

$a0,in_name

# read the input string

29:li $a1,31

30:li $v0,8

31:syscall

32:

 

 

 

33:

la

$a0,out_msg

# write output message

34:li $v0,4

35:syscall

37:la $t0,in_name

38:loop:

39:lb $t1,($t0)

40:

beqz $t1,exit_loop

# if NULL, we are done

41:blt $t1,’a’,no_change

42:bgt $t1,’z’,no_change

43:

addu $t1,$t1,-32

#

convert

to uppercase

44:

 

#

’A’-’a’

= -32

45:no_change:

46:sb $t1,($t0)

47:

addu

$t0,$t0,1

# increment pointer

48:

b

loop

 

49:exit_loop:

50:

la

$a0,in_name

# output converted string

51:li $v0,4

52:syscall

54:

li

$v0,10

# exit

55:syscall

Example 10.3 Division of two integers using subtraction.

In this example, we use subtraction to implement the division operation. Note that the MIPS instruction set supports multiplication and division instructions, even though we have not discussed them in this chapter. These instructions are described in detail in Chapter 13. For now we show how the division operation can be implemented using repeated subtraction.

main()

prompt user for input

read the two input numbers (dividend and divisor) quotient := 0

remainder := number

while (remainder < divisor)

180

Guide to RISC Processors

quotient := quotient + 1 remainder := remainder divisor

end while output quotient output remainder exit

end main

The program, shown in Program 10.4, follows the pseudocode in a straightforward manner. The user is prompted for the dividend (numerator) and divisor (denominator) on lines 23–25. The two input numbers are read on lines 27–28 and 31–32. The divisor is stored in $t1 and the dividend (also the remainder) is stored in the $t0 register. The quotient, which is maintained in $t2, is initialized to zero on line 35.

Program 10.4 Division by repeated subtraction

1: # Division using subtraction DIVIDE.ASM

2:#

3:# Objective: Finds quotient and remainder of a division

4: #

using the subtract instruction.

5:# Input: Requests two numbers from the user.

6:# Output: Outputs the quotient and remainder.

7:#

8:################### Data segment ###################

9:.data

10:prompt:

11:

.asciiz

"Please enter dividend and divisor: "

12:quo_msg:

13:

.asciiz

"The quotient is: "

14:rem_msg:

15:

.asciiz

"The remainder is: "

16:newline:

17:

.asciiz

"\n"

18:

 

 

19:################### Code segment ###################

20:.text

21:.globl main

22:main:

23:

la

$a0,prompt

# prompt user for input

24:li $v0,4

25:syscall

27:

li

$v0,5

# read dividend into $t0

28:syscall

29:move $t0,$v0

Chapter 10

Assembly Language Overview

181

30:

 

 

 

 

31:

li

$v0,5

# read divisor into $t1

 

32:syscall

33:move $t1,$v0

35:

li

$t2,0

# quotient (in $t2) = 0

36:

loop:

 

 

37:

bltu

$t0,$t1,done

# if rem < divisor, done

38:

add

$t2,$t2,1

# increment quotient

39:

sub

$t0,$t0,$t1

# compute remainder

40:

b

loop

 

41:

 

 

 

42:

done:

 

 

43:

la

$a0,quo_msg

# write quotient message

44:li $v0,4

45:syscall

47:

move $a0,$t2

# output quotient

48:li $v0,1

49:syscall

51: la $a0,newline # write newline

52:li $v0,4

53:syscall

55:

la

$a0,rem_msg

# write remainder message

56:li $v0,4

57:syscall

59:

move $a0,$t0

# output remainder

60:li $v0,1

61:syscall

63: la $a0,newline # write newline

64:li $v0,4

65:syscall

67:

li

$v0,10

# exit

68:syscall

The division loop consists of the lines 36–40. The while loop termination condition (remainder < divisor) is tested on line 37. If the condition is not met, the quotient is incremented (line 38) and the remainder is updated by subtracting the divisor (line 39).

After the loop is terminated, we first display the quotient, which is in $t2, using the print_int system call. Then after displaying the remainder, the program terminates.

182

Guide to RISC Processors

Summary

In this chapter, we presented the basics of the MIPS assembly language programming. We discussed three types of assembly language statements:

1.Executable statements that instruct the processor as to what to do;

2.Pseudoinstructions that are supported by the assembler;

3.Assembler directives that facilitate the assembly process.

Assembler directives to allocate storage space for data variables and to define numeric and string constants were discussed in detail. The assembler extends the processor instruction set by providing several pseudoinstructions. It translates these pseudoinstructions to one or more processor instructions. We have given an example to show how the assembler does this translation. We give more examples in later chapters.

We have given an overview of the MIPS instruction set. Although we discussed in detail the load and store instructions, there was only a brief review of the remaining instructions of the MIPS instruction set. A detailed discussion of these instructions is provided in later chapters.

11

Procedures and the Stack

We have given an overview of the MIPS assembly language in the last chapter. This chapter discusses how procedures are written in the MIPS assembly language. Procedure is an important programming construct that facilitates modular programming. Procedures can be divided into leaf and nonleaf procedures. A leaf procedure does not call other procedures. On the other hand, a nonleaf procedure calls other procedures. In the MIPS architecture, we can implement simple leaf procedures without using the stack. Nonleaf procedures always need to use the stack, at least to store the return address of the calling procedure.

In this chapter, we discuss how both leaf and nonleaf procedures are implemented in the assembly language. Our initial discussion deals with simple leaf procedures that can be implemented using the registers. Then we look at how the stack is implemented in the MIPS architecture. The details about the stack implementation are useful in understanding how we can handle nonleaf procedures in the assembly language.

Parameter passing is an important aspect of procedure invocation. As you know, we can use either the call-by-value or call-by-reference mechanism for parameter passing. Here we give details on how we can implement these two types of parameter-passing mechanisms in the assembly language programs.

Introduction

A procedure is a logically self-contained unit of code designed to perform a specific task. Procedures are sometimes called subprograms and play an important role in modular program development. In high-level languages, there are two types of subprograms: procedures and functions. Each function receives a list of arguments and performs a computation based on the arguments passed onto it and returns a single value.

Procedures also receive a list of arguments just as the functions do. However, procedures, after performing their computation, may return zero or more results back to the calling procedure. In C language, both these subprogram types are combined into a single

183

184

Guide to RISC Processors

function construct. For example, in the following C function

int sum (int x, int y, int z)

{

return (x+y+z);

}

the parameters x, y, and z are called formal parameters and the function body is defined based on these parameters. When this function is called (or invoked) by a statement like

total = sum(number1,number2,number3);

the actual parameters—number1, number2, and number3—are used in the computation of the function sum.

There are two types of parameter-passing mechanisms: call-by-value and call-by- reference. In the call-by-value mechanism, the called function (sum in our example) is provided only the values of the parameters for its use. Thus, in this mechanism, the values of these actual parameters are not changed in the called function; these values can only be used as in a mathematical function. In our example, the sum function is invoked by using the call-by-value mechanism, as we simply pass the values of number1, number2, and number3 to the called function sum. Thus, if sum modifies x, y, or z, these changes are not reflected in the calling function.

To illustrate this point, let’s assume that we want to exchange two values a and b passed on to the function swap. Suppose we define our swap function as follows.

/* Incorrect swap procedure */ int swap (int a, int b)

{

int temp;

temp = a; a = b;

b = temp; return;

}

If we call swap as

swap(value1, value2);

it will not exchange the two values value1 and value2 because we are using the call- by-value mechanism. We can remedy this problem by using the call-by-reference mechanism.

In the call-by-reference mechanism, the called function actually receives the addresses (i.e., pointers) of the parameters from the calling function. The function can change the contents of these parameters—and these changes are seen by the calling function—by directly manipulating the actual parameter storage space. We can rewrite the swap function as

Chapter 11 Procedures and the Stack

185

/* Correct swap procedure */ void swap (int *a, int *b)

{

int temp;

temp = *a; *a = *b; *b = temp;

}

This procedure works fine as it passes the addresses of the two parameters from the calling function. Such a function can be invoked as

swap (&data1, &data2);

Often both types of parameter-passing mechanisms are used in the same function. As an example, consider finding the roots of the quadratic equation

ax2 + bx + c = 0 .

The two roots are defined as

 

 

 

 

 

 

 

 

 

 

b

2

4ac

 

root1 =

−b +

 

,

 

 

2a

 

 

 

 

 

 

 

 

 

 

b

2

4ac

 

root2 =

−b −

 

.

 

 

2a

 

 

 

 

The roots are real if b2 4ac, and imaginary otherwise.

Suppose that we want to write a function that receives a, b, and c and returns the values of the two roots (if real) and indicates whether the roots are real or imaginary.

int roots (double a, double b, double c, double *root1, double *root2)

{

int root_type = 1;

if (4*a*c <= b*b){ /* roots are real */ *root1 = (b + sqrt(b*b 4*a*c))/(2*a); *root2 = (b sqrt(b*b 4*a*c))/(2*a);

}

else /* roots are imaginary */ root_type = 0;

return (root_type);

}

The function receives parameters a, b, and c via the call-by-value mechanism, and root1 and root2 parameters are passed using the call-by-reference mechanism. A typical invocation of roots is

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