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

MIPS_primery_zadach / dandamudi05gtr guide risc processors programmers engineers

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

206

 

Guide to RISC Processors

 

Program 11.5 A string reverse example

1:

# Reverses a string

STRING_REVERSE.ASM

2:#

3:# Objective: Reverses a given string.

4:# Input: Requests a string from the user.

5:# Output: Outputs the reversed string.

6:#

7:# $a0 - string pointer

8:#

9:################## Data segment ######################

10:.data

11:prompt:

12:

.asciiz

"Please enter a string: \n"

13:out_msg:

14:

.asciiz

"The reversed string is: "

15:in_string:

16: .space 31 17:

18:################## Code segment ######################

19:.text

20:.globl main

21:main:

22:

la

$a0,prompt

# prompt user for input

23:li $v0,4

24:syscall

26:

la

$a0,in_string

#

read input string

27:

li

$a1,31

#

buffer length in $a1

28:li $v0,8

29:syscall

31:la $a0,in_string

32:jal string_reverse

34:

la

$a0,out_msg

# write output message

35:li $v0,4

36:syscall

38:

la

$a0,in_string

# output reversed string

39:li $v0,4

40:syscall

42:

li

$v0,10

# exit

43:

syscall

 

44:

 

 

 

45:#------------------------------------------------------

Chapter 11 Procedures and the Stack

207

46:# STRING_REVERSE receives a pointer to a string in $a0

47:# and reverses the string

48:# $a0 - front pointer

49:# $t1 - back pointer

50:# $t2, $t3 - used as temporaries

51:#------------------------------------------------------

52:string_reverse:

53:

move $t1,$a0

# init $t1 to front pointer

54:loop1:

55:lbu $t2,($t1)

56:

beq

$t2,0xA,done1

#

if

linefeed

57:

beqz

$t2,done1

#

or

NULL, we are done

58:addu $t1,$t1,1

59: b loop1

60:done1:

61:

sub $t1,$t1,1

# $t1 = back pointer

62:

 

 

63:reverse_loop:

64:

bleu

$t1,$a0,done

# if back <= front, done

65:

lbu

$t2,($a0)

#

66:

lbu

$t3,($t1)

# exchange values

67:

sb

$t2,($t1)

# at front & back

68:

sb

$t3,($a0)

#

69:

addu

$a0,$a0,1

# update front

70:

subu

$t1,$t1,1

# and back

71:

b

reverse_loop

 

72:done:

73: jr $ra

The string reverse procedure uses the $a0 and $t1 registers for front and back pointers, respectively. The first while loop is implemented on lines 54–59. The second while loop condition is implemented on line 64. The two characters, pointed to by front and back, are exchanged on lines 65–68. The two pointers are updated on lines 69 and 70. Because this is a leaf procedure, we can leave the return address in the $ra register.

Passing Variable Number of Parameters

Procedures in C can be defined to accept a variable number of parameters. The input and output functions, scanf and printf, are the two common procedures that take a variable number of parameters. In this case, the called procedure does not know the number of parameters passed onto it. Usually, the first parameter in the parameter list specifies the number of parameters passed.

In assembly language procedures, a variable number of parameters can be easily han-

208

Guide to RISC Processors

dled by the stack method of parameter passing. Only the stack size imposes a limit on the number of parameters that can be passed. The next example illustrates the use of the stack to pass a variable number of parameters in the MIPS assembly language programs.

Example 11.4 Passing a variable number of parameters via the stack.

The objective of this example is to show how easy it is to pass a variable number of parameters using the stack. The program, shown in Program 11.6, reads a number of integers and outputs their sum.

The procedure sum receives a variable number of integers via the stack. The parameter count is passed via $a0.

Program 11.6 Passing a variable number of parameters to a procedure

1: # Sum of variable number of integers VAR_PARA.ASM

2:#

3:# Objective: Finds sum of variable number of integers.

4: #

Passes variable number of integers via stack.

5:# Input: Requests integers from the user; input can be

6: #

terminated by entering a zero.

7:# Output: Outputs the sum of input numbers.

8:#

9:# $a0 - number of integers passed via the stack

10:# $v0 - sum is returned via this register

11:#

12:#################### Data segment ########################

13:.data

14:prompt:

15:

.ascii

"Please enter integers. \n"

16:

.asciiz

"Entering zero terminates the input. \n"

17:sum_msg:

18:

.asciiz

"The sum is: "

19:newline:

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:li $a0,0

31:read_more:

32:

li

$v0,5

# read a number

Chapter 11 Procedures and the Stack

209

33:syscall

34:beqz $v0,exit_read

35:

subu

$sp,$sp,4

#

reserve 4

bytes on stack

36:

sw

$v0,($sp)

#

store the

number on stack

37:addu $a0,$a0,1

38: b read_more

39:exit_read:

40:

jal

sum

#

sum is returned in $v0

41:

move

$t0,$v0

 

 

42:

 

 

 

 

43:

la

$a0,sum_msg

#

write output message

44:li $v0,4

45:syscall

47:

move $a0,$t0

# output sum

48:li $v0,1

49:syscall

51:

la

$a0,newline

# write newline

52:li $v0,4

53:syscall

55:

li

$v0,10

# exit

56:

syscall

 

57:

 

 

 

58:#----------------------------------------------------

59:# SUM receives the number of integers passed in $a0

60:# and the actual numbers via the stack. It returns

61:# the sum in $v0.

62:#----------------------------------------------------

63:sum:

64:

li

$v0,0

# init sum = 0

65:sum_loop:

66:beqz $a0,done

67:

lw

$t0,($sp)

#

pop the top value

68:

addu

$sp,$sp,4

#

into $t0

69:addu $v0,$v0,$t0

70:subu $a0,$a0,1

71: b sum_loop

72:done:

73:jr $ra

The main program reads a sequence of integers from the input. Entering a zero terminates the input. Each number read from the input is pushed directly onto the stack (lines 35 and 36). Because $sp always points to the last item pushed onto the stack, we

210

Guide to RISC Processors

can pass this value to the procedure. Thus, a simple procedure call (line 40) is sufficient to pass the parameter count and the actual values.

The procedure sum reads the numbers from the stack. As it reads, it decreases the stack size (i.e., $sp increases). The loop in the sum procedure terminates when $a0 is zero (line 66). When the loop terminates, the stack is also cleared of all the arguments.

Summary

We have introduced procedures and discussed how procedures are implemented in the MIPS assembly language. We can divide procedures into leaf and nonleaf categories. A leaf procedure does not call another procedure whereas a nonleaf procedure invokes another procedure. In the MIPS architecture, leaf procedures can be written using internal registers. As part of procedure invocation, the return address is stored in the $ra register. This value is used to transfer control back to the caller after executing the procedure. Nonleaf procedures, however, need to use the stack.

The stack is a last-in-first-out data structure that plays an important role in nonleaf procedure invocation and execution. The stack supports two operations: push and pop. Only the element at the top-of-stack is directly accessible through these operations. The $sp register points to the top of the stack. It is important to note that the stack grows downward (i.e., towards lower memory addresses). Because the MIPS architecture does not explicitly support stack operations, we need to manipulate the stack pointer ($sp) register to implement the stack push and pop operations.

When writing procedures in assembly language, parameter passing has to be explicitly handled. Parameter passing can be done via registers or the stack. Although the register method is efficient, the stack-based method is more general and flexible. Also, when the stack is used for parameter passing, passing a variable number of parameters is straightforward. We have demonstrated this by means of an example.

In this chapter we discussed direct procedure calls in which the target instruction address is directly specified. The MIPS architecture also supports indirect procedure calls. In these calls, the target is specified indirectly through a register. Even though we haven’t used this mechanism to invoke a procedure, we have used it to return from a procedure. We discuss indirect procedure calls in Chapter 14.

12

Addressing Modes

MIPS supports several ways to specify the location of the operands required by an instruction. These are called addressing modes. Most instructions expect their operands in the registers. However, load and store instructions are special in the sense that they interface with memory. These instructions require a memory address, which can be specified in several ways. This chapter gives details on the addressing modes we can use in writing MIPS assembly language programs.

Introduction

As discussed in Chapter 3, RISC processors use simple addressing modes. In contrast, CISC processors provide complex addressing modes. In this chapter, we look at the MIPS addressing modes in detail. SPIM simulates the MIPS R2000 processor, therefore our focus is on the assembly language of this processor.

As mentioned before, MIPS uses the load/store architecture. In this architecture, only the load and store instructions move data between memory and processor registers. All other instructions expect their operands in registers. Thus, they use the register addressing mode. The load and store instructions, however, need a memory address. A variety of addressing modes is available to specify the address of operands located in memory. The MIPS architecture supports the following addressing modes.

Register addressing mode

Immediate addressing mode

Memory addressing mode

We look at these three addressing modes in the next three sections. Following this description, we give details on accessing and organizing arrays. We consider both onedimensional and multidimensional arrays. We give several examples to illustrate the use the addressing modes presented here. We conclude the chapter with a summary.

211

212

Guide to RISC Processors

Addressing Modes

In this section, we briefly look at the three addressing modes. Later sections give examples that use these addressing modes.

Register Addressing Mode

This is the commonly used addressing mode. In this addressing mode, the operands are located in the registers. For example, in the instruction

add $t2,$t1,$t0

the two source operands are in $t1 and $t0. The result of the operation is also stored in a register ($t2 in this example). Most instructions, excluding the load and store, use this addressing mode. These instructions are encoded using the R-type format shown in Figure 4.2 on page 52.

Immediate Addressing Mode

In immediate addressing mode, the operand is stored in the instruction itself. This addressing mode is used for constants as shown in the following example.

addi $t0,$t0,4

This instruction is encoded using the I-type instruction format (see Figure 4.2). As you can see from this format, the immediate value is limited to 16 bits. As a consequence, the constant is limited to a signed 16-bit value. The advantage of this addressing mode is that the immediate operand is fetched along with the instruction; it does not require a separate memory access.

Memory Addressing Modes

As discussed in Chapter 4, the bare machine provides only a single memory addressing mode, disp(Rx), where displacement disp is a signed, 16-bit immediate value. The address is computed by adding disp to the contents of register Rx. The virtual machine supported by the assembler provides additional addressing modes for load and store instructions to help in assembly language programming. Table 12.1 shows the addressing modes supported by the virtual machine. Like the immediate addressing mode, the I-type instruction format is used to encode this addressing mode (see Figure 4.2).

Next we look at some examples of the memory addressing modes. The following instruction

lw $t0,($t1)

loads the 32-data at the address given by the $t1 register. In the following examples, we assume that the array is declared as

Chapter 12 Addressing Modes

213

 

Table 12.1 Addressing modes

 

 

 

 

 

 

Format

Address computed as

 

 

 

 

 

 

(Rx)

Contents of register Rx

 

 

imm

Immediate value imm

 

 

imm(Rx)

imm + contents of Rx

 

 

symbol

Address of symbol

 

 

symbol± imm

Address of symbol ±imm

 

 

symbol± imm(Rx)

Address of symbol ±(imm + contents of Rx)

 

array:

.word 15,16,17,18,19,20

We use the load instruction to illustrate the various memory addressing modes. We can also specify the address by using array as in the following example.

lw $t1,array

This instruction loads the first word (i.e., value 15) into the $t1 register. We can specify an immediate constant as in the following example.

lw $t1,4($t0)

If $t0 contains the address of array, this instruction loads the second element (i.e., value 16) into the $t1 register. Of course, we can do the same thing using the following instruction.

lw $t1,array+4

In our next example, we look at the last addressing mode given in Table 12.1. In this addressing mode, the address is computed from three components: symbol±imm(Rx). Here is an example that uses these three components:

lw $t2,array+4($t0) sw $t2,array+0($t0)

This two-instruction sequence copies the second element into the first element, assuming that $t0 contains 0. We use this instruction sequence in an example later (see Example 12.1 on page 219).

Note that most load and store instructions operate only on aligned data. The MIPS, however, provides some instructions for manipulating unaligned data.

214

Guide to RISC Processors

Processing Arrays

Arrays are useful in organizing a collection of related data items, such as test marks of a class, salaries of employees, and so on. We have used arrays of characters to represent strings. Such arrays are one-dimensional: only a single subscript is necessary to access a character in the array. Next we discuss one-dimensional arrays. High-level languages support multidimensional arrays, which are discussed towards the end of this chapter.

One-Dimensional Arrays

A one-dimensional array of test marks can be declared in C as

int

test_marks [10];

In C, the subscript always starts at zero. Thus, the mark of the first student is given by test_marks[0] and that of the last student by test_marks[9].

Array declaration in high-level languages specifies the five attributes:

Name of the array (test_marks),

Number of the elements (10),

Element size (4 bytes),

Type of element (integer), and

Index range (0 to 9).

From this information, the amount of storage space required for the array can be easily calculated. Storage space in bytes is given by

Storage space = number of elements * element size in bytes.

In our example, it is equal to 10 * 4 = 40 bytes. In assembly language, arrays are implemented by allocating the required amount of storage space. For example, we can declare the test_marks array as

test_marks: .space 40

An array name can be assigned to this storage space. But that is all the support you get in assembly language! It is up to you as a programmer to “properly” access the array taking into account the element size and the range of subscripts.

You need to know how the array is stored in memory in order to access its elements. For one-dimensional arrays, representation of the array in memory is rather direct: array elements are stored linearly in the same order, as shown in Figure 12.1. In the remainder of this section, we use the convention used for arrays in C (i.e., subscripts are assumed to begin with 0).

To access an element we need to know its displacement in bytes relative to the beginning of the array. Because we know the element size in bytes, it is rather straightforward to compute the displacement from the subscript:

Chapter 12 Addressing Modes

 

215

High memory

test_marks[9]

 

 

 

 

 

 

test_marks[8]

 

 

 

test_marks[7]

 

 

 

test_marks[6]

 

 

 

test_marks[5]

 

 

 

test_marks[4]

 

 

 

test_marks[3]

 

 

 

test_marks[2]

 

 

 

test_marks[1]

 

 

Low memory

test_marks[0]

 

test_marks

 

 

Figure 12.1 One-dimensional array storage representation.

displacement = subscript * element size in bytes.

For example, to access the sixth student’s mark (i.e., subscript is 5), you have to use 5 * 4 = 20 as the displacement into the test_marks array. Later we present an example that computes the sum of a one-dimensional integer array.

Multidimensional Arrays

Programs often require arrays of more than one dimension. For example, we need a two-dimensional array of size 50 × 3 to store test marks of a class of 50 students taking three tests during a semester. In this section, we discuss how two-dimensional arrays are represented and manipulated in the assembly language. Our discussion can be generalized to higher-dimensional arrays.

For example, a 5 × 3 array to store test marks can be declared in C as

int class_marks[5][3]; /* 5 rows and 3 columns */

Storage representation of such arrays is not as direct as that for one-dimensional arrays. Because the memory is one-dimensional (i.e., linear array of bytes), we need to transform the two-dimensional structure to a one-dimensional structure. This transformation can be done in one of two common ways:

Order the array elements row-by-row, starting with the first row,

Order the array elements column-by-column, starting with the first column.

The first method, called the row-major ordering, is shown in Figure 12.2a. Row-major ordering is used in most high-level languages including C. The other method, called the

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