Recursion

Purpose

To illustrate recursive procedures and to give you practice using the debugger to follow the behavior of a program:

Due dates

You must hand in answers to the questions at the beginning of your laboratory session on Thursday, September 24. This part of the assignment is worth 20 points.

You must hand in your stack frame diagrams at the beginning of your laboratory session on Tuesday, September 29, and demonstrate your use of the debugger to your TA during that period. This part of the assignment is worth 20 points.

Background

A recursive procedure provides a simple demonstration of the power of an interface specification. Click here for the interface specification of a recursive integer conversion procedure. It tells you that recurse is given a value v and a radix r and must output a sequence of digits. (A ``radix'' is a number base. For hexadecimal numbers, the radix is 16; for binary numbers, the radix is 2; for decimal numbers, the radix is 10.) This interface specification is a contract, guaranteeing that whenever the routine named recurse is invoked with a value and a radix, it will output the value as a sequence of radix digits. (Radix digits are numbers in a particular radix. For example, for the radix 16, i.e., hexadecimal numbers, the radix digits are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F.)

How can you implement this operation? The remainder from dividing v by r is the last digit in the sequence. If the quotient from the division is nonzero, then that quotient is a value representing the other digits of the sequence. (If the quotient is zero, the sequence has only a single digit.)

As an example, consider the value 12 and assume that the radix of the output digits should be 10. Dividing 12 by 10 yields a quotient of 1 and a remainder of 2. 2 is clearly the last digit, and 1 is a value representing the remaining digits in the sequence.

If the value were 12 and the radix were 16, division would yield a quotient of 0 and a remainder of 12. In this case, the sequence has only one digit: C, the hexadecimal digit representing 12.

This discussion shows that you can implement the operation by dividing the value by the radix. If the quotient is nonzero, then you should output the sequence of radix digits corresponding to the quotient. Finally, you should output the radix digit corresponding to the remainder.

It happens that you know the name of a routine (recurse) whose interface promises that, if it is called with a value and a radix, it will output the value as a sequence of radix digits. Because you understand the interface of this routine, you can invoke it without thinking about how it might be implemented. Therefore you can invoke it to output the sequence of radix digits corresponding to a nonzero quotient. The fact that you are in the midst of writing recurse is totally irrelevant because the interface specification characterizes it completely.

This example shows the importance of being able to understand the interface specification independent of the routine's implementation. Because the interface specification is a contract, the functionality it describes is guaranteed. The routine it describes can be invoked in any context, including that of the routine itself, and it will deliver as promised.

The XRAY Monitor

In previous assignments, you have used the XRAY Simulator running on the Dell to allow you to observe your program as it is running. This is a useful debugging aid, because you see what actually happens when you execute an instruction rather than relying on what you think will happen. You can also poke around in the registers and memory to verify that interface specifications are being met. The only problem with this approach is that the program is being simulated on the Dell. Thus it runs much more slowly than it would on the MB5, and it cannot make use of the special hardware attached to the MB5.

The XRAY Monitor provides the same functionality for a program running on the MB5 that the XRAY Simulator provides for a program running on the Dell. It has two parts: a controller that runs on the Dell and provides exactly the same user interface as the XRAY Simulator, and an interface stored in the MB5's ROM that interacts with the program being monitored. These two parts of the monitor communicate via the COM1 port of the Dell and the MB5. A monitored program can use the COM1 port only through the XRAY Monitor itself, where COM1 implements the streams stdin and stdout.

When the MB5 is reset, it assumes that you will communicate with it via MooseLoad. Thus, in order to use the XRAY Monitor, you need to first give the F command to MooseLoad via TeraTerm. Also, if you reset the board with an XRAY Monitor command, or push the red reset button, the MB5 will revert to MooseLoad and the XRAY Monitor will no longer be able to interact with your program.

There is some evidence that the more esoteric features of the XRAY Monitor, such as the ability to interrupt the program when a variable gets a particular value, don't work with the MB5. It would be useful if you could report any such anomalies, so that we could determine the scope of the problem and perhaps fix it.

Breakpoints set at code locations, as well as displays of register, stack, and memory contents work fine. Almost all of the procedures for using the XRAY Monitor are identical to those you have been following for using the simulator. We will expand on the differences below, in the discussion of the task for this assignment.

Task

Write a module in assembly language to implement integer conversion recursively, using the algorithm discussed above. Your routine must conform to the contract stated in the interface specification recurse.h, and appear in a file named recurse.src. The purpose of this homework is not to get you to invent a new algorithm for integer conversion. Rather you should concentrate on understanding the assembly language implementation of the algorithm given above: how the instructions are written and how they operate.

Use the DIVU instruction (Section 7.3, p 221, and page 483 of the text) to obtain the quotient and remainder when v is divided by r. If the quotient is not zero, then convert and print it.

The integer remainder is a binary value that must be converted to an ASCII character code in the appropriate radix. (Figure 7.16 on page 249 of the text shows how a table can be used for such conversions.) Write the resulting digit by invoking the outchar operation.

Click here to get a zip file containing the source code for a driver, the outchar routine, and an appropriate Makefile. (This Makefile gives the -Zp2 option to the C compiler, which will pass short arguments as 16-bit values. Do not change this option, but write your program accordingly!) Unpack that zip file just as you did in Homework #1. Start a command-line window, go to the recurse directory unpacked from the zip file, and add your file recurse.src. After successfully compiling the program, use the XRAY monitor to execute the program on the MB5, control that execution, and inspect the internal state.

You must first enable the ROM interface on the MB5: Double-click on the Tera Term icon that you created earlier, or use the Windows Start menu to start Tera Term Pro. Select the Serial connection via COM1, and click OK.

Turn on the MB5 board, or, if the board is already on, press the red reset button. In the Tera Term window, you will see text describing the five single-letter commands, A through F. You want to enable the XDM monitor in the MB5's ROM, so type an upper-case F on the Dell's keyboard and then exit Tera Term. (If you leave Tera Term running, the Dell portion of the monitor cannot connect to the portion running on the MB5.)

The Dell portion of the monitor can be started via the Windows Start menu, but it's useful to have a shortcut for it on the desktop: Click the Start menu on the Windows toolbar at the bottom of the screen, and go to Programs->MGC Embedded->68K XRAY Monitor. Right-click on this, and select Create Shortcut. Left-click, and a line 68K XRAY Monitor (2) will appear. Drag this line to the desktop to get the shortcut icon.

When you start the monitor, the display should be identical to that of the simulator you have used in previous assignments. Select the Managers window and then click on the Connect tab (it may already be selected by default). Under Available Connections, select the localhost associated with the Server named XDM. Selection of the XDM server rather than the SIM68K_MEM_CONNECTION is the only difference between this procedure and the procedure you used in previous assignments.

Once you have selected the server, you must change your directory and load your program as usual. When you load the driver.abs file, you will probably notice that it takes quite a bit longer to complete the load than it did when you were using the simulator. The reason for the delay is that the program must be sent over the serial link to the MB5.

Select Open from the Code window's File menu and open file outint.c. Set a breakpoint at the line that calls recurse, and run the program to that breakpoint. (Review the simulator control buttons if necessary.) Use the Low-Level Step Into button to get to the JSR instruction that calls the routine, but do not execute that instruction. Notice that there is a breakpoint set on the instruction that pushes the second argument onto the stack.

Go to the Windows pull-down menu of the Code window, and select Register. Again go to the Windows pull-down menu of the Code window, but this time select Stack. Another window, which displays the contents of the stack, will appear. At this point, note the values of A7 (SP), A6 (FP) and where (SP) is pointing in the Stack window.

As you step through your program, draw a sequence of diagrams in the same style as those appearing in Figure 9-17 on page 320 of the text. In addition to SP, specify the content of FP and an arrow to the stack location it addresses in each diagram. Your sequence should consist of diagrams showing the state of the stack at the following points:

  1. Just before execution of the first call of _recurse (from another routine).
  2. Upon entry to the first call of _recurse, before any instructions of the procedure body have been executed.
  3. Just before execution of the second call of _recurse (from within the routine).
  4. Just before the first execution of the RTS instruction at the end of _recurse.
  5. Just after the first return from _recurse.
(Note that diagrams in the book have low addresses at the top of the page, whereas the simulator's Stack display has low addresses at the bottom of the window. You may use either form of diagram on assignments and exams, but be certain to label them with an arrow pointing from low addresses to high addresses.)

Choose one of your diagrams and highlight one complete stack frame (also known as an activation record) for the _recurse procedure. Carefully identify every component of that frame, explaining what it is and why it is present. You may find the discussion associated with Figure 9-17 on page 320 of the text helpful in organizing your explanation. This discussion starts with Section 9.4.2 on page 316.

Hand your TA a single copy of your diagrams and stack frame description, signed by each member of your group, when you give your demonstration.

Questions

Suppose that a variable is being passed to a procedure as one of its arguments. There are two ways of doing this: If the caller transmits the address of the variable, we say that the variable is being passed ``by reference''; if the caller transmits the value of the variable, we say that the variable is being passed ``by value''.

Assume that procedure P has one argument, and the caller passes the variable X to P via the stack. The symbol X appears in the location (label) field of a DS.L directive.

  1. (2 points) Write the code necessary in the caller to pass X by value.
  2. (2 points) Write the code necessary in the procedure to increment the value of the argument by 1, assuming that X has been passed by value.
  3. (2 points) Write the code necessary in the caller to pass X by reference.
  4. (2 points) Write the code necessary in the procedure to increment the value of the argument by 1, assuming that X has been passed by reference.
  5. (2 points) In which of the two cases (pass by value or pass by reference) will the increment operation affect the caller? Explain briefly.
  6. (2 points) Why are the MOVEM instructions that save registers placed in the procedure instead of in the caller?
  7. (3 points) Why should a procedure execute a LINK instruction with a6 before saving any other registers? Be specific!
  8. (5 points) Give a convincing argument that the restrictions imposed by the DIVU instruction on its operands and results are always satisfied in your routine.

Demonstration

You must demonstrate your use of the debugger to your TA on one of the machines in the Microlab. Be sure to have a copy of your diagrams and stack frame description to help you in explaining the debugger output.
Instructor Revision $Revision: 1.38 $ ($Date: 2007/08/27 00:15:44 $)