MIPS CPU Simulator Exercise:
Simple Procedures (No Stack)

 


⇒ Bring up the "K Sub-MIPS Simulator referred to in these instructions in a separate, side-by-side browser window, if you haven't already.

In this exercise, you will be writing a simple Main program that acts as a "leaf" function (a function that does not call any other function) called by a simple "operating system" procedure.

Download this Markdown template to record your program and submit it to Kit.


Leaf Functions

Look over the "Main as Leaf" example in the K Sub-MIPS Simulator. It starts with a short simulation of the operating system (OS), which calls (jumps to) the Main. The Main calculates the midpoint of two values (a simplified version of the Conditions example you looked at earlier) and then returns to the simulated OS. After Main returns, the simulated operating system jumps to the end of the whole program to HALT.

This example illustrates the use of jal and jr to jump to the subprocedure and back, but does not show the passing of arguments or return values.

Here is C code that corresponds to the assembly language program:

     /* OS calls main().
      * When main() returns, it goes to End to HALT.
      */
      int main()
      {
          int x = 4;    /* Or other value in M60 */
          int y = 10;   /* Or other value in M64 */
          int mid = x;
          if ( x != y )
             mid = (x + y)/2;
          /* Store midpoint in M68 */
          return;
      }

The assembly language program assumes that the values for x and y (stored in registers $t1 and $t2) come from memory locations M60 and M64. The Main procedure stores the computed midpoint in M68.

Manually run the sample code to be sure you understand what it does.

⇒ Analysis Question: Why does the simulated operating system jump to a HALT instruction at the end, rather than just have a HALT instruction at address 4 when the Main procedure returns? Try putting the HALT instruction at address 4. What happens when you go to the Assembler to generate machine code? Why?

Program:
Pick one of the programs or code fragments you've written for a previous exercise or that has been provided to you, such as double (1st 230 sample code), the Sum 3 or Copy Memory Location programs you wrote for the First MIPS CPU Simulator exercise, or any of the three Conditions / Loops programs you wrote for the Second MIPS CPU Simulator exercise. Re-write it as a Main function called by, and returning to, the Operating System.

Test your program, then "Save" and copy it into the Markdown file.

Second Leaf Example

There is a second "Leaf" example in the simulator called "MidPt as Leaf." Save both "Leaf" examples to the "Debugging or Saved Output" section at the bottom of the page (click on "Main on Leaf" then click on "Save"; next click on "MidPt as Leaf" and click on "Save".)

This example leaves out the short bit of code representing the operating system, starting with the Main function, but breaks out the calculation of the midpoint into a leaf function. Once Midpt returns to Main with the calculated value, it stores it to memory then jumps to End (which was handled by OS in the previous example).

Note that Main is no longer a leaf function in this example, because it calls Midpt.

Here is C code that corresponds to the assembly language program:

     /* This example starts with main, ignoring the OS.
      */
      int main()
      {
          int x = 4;    /* Or other value in M60 */
          int y = 10;   /* Or other value in M64 */
          int mid = midPt(x, y);
          /* Store midpoint in M68 */
          return;
      }

      int midPt(int a, int b)
      {
          int mid = a;
          if ( a != b )
             mid = (a + b)/2;
          return mid;
      }

The assembly language program assumes that the values for x and y come from memory locations M60 and M64. The Main procedure stores the computed midpoint in M68.

⇒ Analysis Question: Why does this second example read the values for x and y into registers $a0 and $a1 instead of into $t1 and $t2, as the previous example did?

⇒ Analysis Question: What would be the problem if we had started and ended with OS, as the previous example did, with Main returning back to OS rather than jumping directly to End? What would be in $ra when Main goes to return to the OS? Where will it really go? (And what happens next?)

Here is the BAD, BROKEN, problematic code:

    # This example starts with the OS.
    OS:     jal Main             # Op sys: Start Main
            j End
    # Program to calculate midpoint of values in M60 and M64.
    Main:   addi $t0, $zero, 60  # Put address of M60 in $t0
            lw $a0, 0($t0)       # Read value from M60 into arg reg 0
            lw $a1, 4($t0)       # Read value from M64 into arg reg 1
            jal Midpt            # Call Midpt function, passing 2 mem values
            sw $v0, 8($t0)       # Store result in M68
    EndMain: jr $ra              # SHOULD return to operating system (but will it?)
    # Midpoint function calculates ($a0 + $a1)/2
    Midpt:  add $v0, $a0, $zero     # Assume midpoint is $a0 (true if $a0 == $a1)
            beq $a0, $a1, EndMP     # goto EndMP if $a0 == $a1
            add $v0, $a0, $a1       # calculate $a0 + $a1
            srl $v0, $v0, 1         # shift right to get ($a0 + $a1) / 2
    EndMP:  jr $ra                  # return; return value is in $v0
    End:    HALT         # Stop assembly and execution