• No results found

6.2 Function of Subroutines

A subroutine is a self-contained subprogram. Each subroutine contains one or more instructions associated with a specific task. For example, a subroutine could be written to perform a time delay, to add a list of numbers or to convert a number from ASCII to hex. Since subroutines are modular groups of code, they are not required to reside in sequential order in memory. The subroutines are executed in any order. This order is dictated by the main program. A subroutine call is a special instruction that must be used to start the execution of a subroutine. The HC11 supports two subroutine call instructions: Jump-To-Subroutine (JSR) and Branch-To-Subroutine (BSR). Each sub- routine must end with a special instruction that returns the flow to the calling program. This instruction is the Return-From-Subroutine (RTS). Figure 6.4 summarizes the instructions that manage the use of subroutines.

Figure 6.4 summarizes the function of the RTS, JSR and BSR instructions. JSR operates in the DIR, EXT, INDX and INDY addressing modes. BSR operates in the relative mode. RTS operates in the inherent addressing mode. JSR, BSR and RTS have no effect on the CCR.

1. When data is pushed onto sequential locations of the stack, is the stack pointer incremented or decremented? Why?

2. Is a pull instruction a read or a write operation?

3. What data movement instruction performs a similar operation to the PULA instruction?

Mnemonic Description Function IMM DIR EXT INDX INDY INH REL S X H I N Z V C

JSR Jump to subroutine (P) ⇒ MS:MS-1, S – 2 ⇒ S, Eff address ⇒ P - X X X X - - - - BSR Branch to subroutine (P) ⇒ MS:MS-1, S – 2 ⇒ S, Eff address ⇒ P - - - X - - - - RTS Return from subroutine (MS+1:MS+2) ⇒ P, S + 2 ⇒ S - - - X - - - -

Figure 6.4 Subroutine Instructions 141

The JSR instruction is similar to JMP in that it uses an absolute 16-bit address to point to the address of the first instruction of the subroutine. The BSR is similar to BRA in that it uses an 8-bit relative mode offset to point to the address of the first instruction of the subroutines. Because the JSR instruction uses a 16-bit address, it allows access to subroutines at any location within the memory map. The BSR instruction can only access subroutines within the +127 or –128 byte limits of the relative address. JSR and BSR cause an unconditional change in the program flow to the subroutine. The HC11 does not support conditional branch to subroutine instructions.

NOTE: The JSR or BSR must be executed to call a subroutine. Every subroutine must end with the RTS instruction in order to return to the calling program.

Steps to Execute a Subroutine

When a subroutine is used on the HC11, a sequence of three events takes place. Each step of the following sequence is described in subsequent sections.

1. Call the subroutine, using a JSR or BSR instruction. 2. Execute the instructions of the subroutine.

3. Return from the subroutine by executing the RTS instruction.

Calling the Subroutine

The only way to execute a subroutine is via the JSR or BSR instruction. These instructions cause the flow of a program to be routed to a subroutine. In chapter 4, the function of the JMP and BRA instructions is presented. JMP and BRA have the job of loading the program counter with the address of the next instruction to execute. Although JSR and BSR are similar to JMP and BRA, they perform an additional task. JSR and BSR cause a return address to be pushed onto the stack prior to loading the program counter with the address of the first instruction in the subroutine. The return address is the address of the instruction that will be executed after the program returns from the subroutine. It is always the address of the instruction immediately following the JSR or BSR in memory. This information is used by the subroutine to return to the calling program. Without the return address, the subroutine would not be able to return to the proper location in the main program.

Figure 6.5 illustrates how the return address is pushed to the stack. The low byte is written to the current address in the stack pointer. The stack pointer is then decremented and the high-order byte is written to this location. Then the stack pointer

142

Order the return address is stacked when subroutine is called Memory Location Register SP - 1 SP PCH PCL

Order the return address is un-stacked when RTS is executed

Figure 6.5 Placement of Return Address on Stack Technician’s Guide to the 68HC11 Microcontroller

is decremented one more time so that it continues to point to the next available location on the stack.

After the return address is pushed onto the stack, the program counter is loaded with the address of the first instruction of the subroutine. The JSR and BSR instructions acquire this starting address from the effective address included with the instruction. JSR uses the DIR, EXT, INDX or INDY addressing mode, and BSR uses the REL addressing mode to specify the effective address.

Example 6.2

Problem: For the following program, identify the return address and the starting address of the subroutine and show the effect on the stack when the subroutine is called.

Address Machine Code Label Source Code

0100 8E 01 E5 LDS #$01E5 0103 BD 01 09 JSR DELAY 0106 86 29 LDAA #$29 0108 3F SWI 0109 CE 0D 06 DELAY LDX #$0D06 010C 09 LOOP DEX 010D 26 FD BNE LOOP 010F 39 RTS

Solution: The Return Address is always the address of the instruction immediately following the JSR or BSR instruction. In this program it is the address of the LDAA instruction, which is $0106.

The starting address of the delay subroutine is $0109.

The starting stack pointer is $01E5, because of the LDS instruction. The low byte of the return address will be written to this location, then the stack pointer will be decremented to $01E4. The high byte of the return address is written to this location of the stack, and the stack pointer is decremented again to $01E3.

$06 → $01E5, $01E4 → S

$01 → $01E4, $01E3 → S

Returning from the Subroutine

After the subroutine is executed, the flow of the program must return to the calling

program. Remember, the return address was pushed onto the stack by the calling 143

instruction. This return address must be pulled from the stack in order to know the address of the next instruction to execute. The last instruction of each subroutine must be the RTS instruction, because the RTS instruction pulls the return address from the stack and loads it into the program counter.

Example 6.3

Problem: Given the following memory block and stack pointer, what is the address of the next instruction to be executed following an RTS instruction? What is the effect on the stack pointer?

01E0 00 01 FF 38 01 06 2A 4B S = 01E3

Solution: The stack pointer is first incremented to $01E4. The high byte of the return address is pulled from this location ($01). The stack pointer is then incremented to $01E5, and the low byte of the return address is pulled ($06). Therefore the return address is $0106, and the stack pointer is changed to $01E5. Efficiency Trade-offs

Subroutines offer a programmer the ability to modularize the code within a program. A function that is performed several times throughout the program can be written as a subroutine. Instead of having one continuous set of instructions, subroutines allow

codereuse. The instructions contained in the subroutine can be executed many times, from various parts of the program. Thus, subroutines provide a means of using memory more efficiently. Without subroutines, all code would have to be in-line. In-line code contains all functions in the order they are used, and the program is one continuous set of instructions. In-line code executes very fast because it does not use the subroutine control instructions (JSR, BSR and RTS). Because in-line code must duplicate a function each time it is used, it does not use memory resources efficiently.

Each time a subroutine is executed, two extra instructions are required in the code. The JSR or BSR instruction is required to call the subroutine. Moreover, each subroutine must be terminated by the RTS instruction. The RTS retrieves the return address from the stack so that the program returns to the proper location in the calling program. The time it takes to execute these two instructions, as well as the memory space they require, is called subroutine overhead. In other words, there is a cost in execution speed (time) and memory usage (space) associated with the use of subroutines. If the overhead of using subroutines is greater than the equivalent in-line implementation, there is no advantage in using subroutines.

Some programmers feel that the main program should be nothing but calls to subroutines. In theory this is a good practice, but it can be taken a bit too far. Subroutines should be used for tasks that are executed more than once within a program. These tasks should be carefully structured to minimize the impact of the extra instructions required to call and return from subroutines.

144

NOTE: In-line code executes faster than the equivalent application using subroutines. Therefore, the in-line code uses time more efficiently. Subroutines typically use less memory than the equivalent in-line implementation. Thus, subroutines are more memory efficient.

Subroutines are typically used when a program has a need to do a group of tasks repeat- edly. As a subroutine, the tasks are called by various parts of the program, as shown in Figure 6.6. The DELAY subroutine is needed in more than one place in the program. Without the ability to modularize the code into a subroutine, the DELAY task would have to be imbedded into the main program repeatedly to form one set of in-line code. It should be apparent that using the DELAY subroutine is a more efficient use of memory than would be required with an in-line implementation. Since this program has time delays built in, there is no concern for the extra time it requires to execute the DELAY subroutine.

Self-Test Questions 6.2