Search code examples
assemblymipsdelay

Rewrite the following C function as an assembly-language subroutine with the same behavior


void delay( int ms ) /* Wait a number of milliseconds, 
                     specified by the parameter value. */
{
    int i;
    while( ms > 0 )
   {
        ms = ms – 1;
          /* Executing the following for loop should take 1 ms */
   for( i = 0; i < 4711; i = i + 1 ) 
          /* The constant 4711 must be easy to change! */
   {
          /* Do nothing. */
   }
  }
}

I have converted this as following and this subroutine is part a bigger subroutine.

delay:
PUSH    ($s0)
PUSH    ($ra)

addi    $s0,$0,0            #i = 0
addi    $t1,$0,4711
move    $t0,$a0         #move the argument to t0
while:
bgt     $t0,$s0,done        #branch to done if ms > 0
addi    $t0,$t0,-1      #decrement ms by 1  
for:
beq     $s0,$t1,done
addi    $s0,$s0,1
j       for
j       while
done:   

POP ($ra)
POP ($s0)
jr  $ra
nop

Test your program using MARS. Adjust the constant in the for loop to get a delay of 1000 ms when delay is called with a parameter value of 1000, and a delay of 3000 ms when delay is called with a parameter value of 3000.

Q1:So I'm not sure if I converted it correctly.

Q2:How to adjust that constant to get that amount of delay.

Any suggestion, hints would be great.Thanks in advance.


Solution

  • I'm not considering all the fuss about the empty observable behaviour as I consider this an assembly exercise with a misworded command text.

    So henceforward I pretend the code is not C but a new language called C pseudo-code with a syntax similar to that of C and a semantic where every statement has observable side effects.


    The assembly code is incorrect.
    I'm not going to correct your mistakes or write any code, I'll just point some of them out.


    The implementation of the for

    # $s0 = i
    # $t1 = 4711
    
    for:
      beq     $s0,$t1,done
      addi    $s0,$s0,1
    j       for
    

    Has at least two issues:

    1. It jumps to done upon exit.
      This corresponds to exiting the while loop too, instead the for in C pseudo-code do another iteration of the while on exit.
    2. The register $s0 is never re-initialized.
      The counter i is initialized on every iteration of the while - your implementation, instead, do it once at the beginning.

    The implementation of the while condition is

    # $s0 = i
    # $t0 = ms
    
    bgt $t0, $s0, done        #branch to done if ms > 0
    

    You can see for your self, with the help of the comments, that it corresponds to ms > i as your code is standing.
    You have, in MIPS, a register that is always zero.


    Some tips:

    • You can avoid using any $s register.
      There are enough $t registers available - saving a $s immediately makes the reader think of an inner call, while there is none in the code.

    • Write the C pseudo-code in simpler form.
      You can rewrite the for as

      i = 0;
      for_begin:
         if (i >= 4711)
            goto for_end
      
            /* FOR BODY */
      
            i = i + 1;
      goto for_begin
      
      for_end:
      

      This should be easier to translate into assembly.