CS 3723
 Programming Languages 
   MIPS Example:  
  Loop  


Calculate Euler's Series

The following page contains code in C, Tiny®, and MIPS assembly code to calculate the sum of the series: Σ 1/(n^2). The MIPS code is what my compiler output as it translated the Tiny code shown.

The great Swiss mathematician Leonhard Euler proved that this series converges (slowly) to π2/6 = 1.644934066848226.

C, Tiny, Output MIPS Assembly Code
#include <stdio.h>
int main() {
   double n, s, m;
   n = 1; s = 0;
   scanf("%lf", &m);
   while (n - m != 0) {
      s = s + 1/(n*n);
      printf("%.0f\t%.17f\n", n, s);
      n = n + 1;
   }
}

# euler.t: Euler series n = 1; s = 0; > m; { n - m ? s = s + 1/(n*n); < n; < T; < s; < N; n = n + 1; } $
Locations of vars, consts: 1: M[1] = 8($s1) 0: M[0] = 0($s1) n: M[23] = 184($s1) m: M[22] = 176($s1) s: M[28] = 224($s1) M[36], ... are temporaries
% which spim /usr/bin/spim % spim euler.s 50 1 1.00000000000000000 2 1.25000000000000000 3 1.36111111111111116 4 1.42361111111111116 5 1.46361111111111120 6 1.49138888888888888 7 1.51179705215419502 8 1.52742205215419502 9 1.53976773116654075 10 1.54976773116654076 11 1.55803219397645809 12 1.56497663842090251 13 1.57089379818421615 14 1.57599583900054263 15 1.58044028344498710 16 1.58434653344498710 17 1.58780674105744390 18 1.59089316081053034 19 1.59366324391302339 20 1.59616324391302333 21 1.59843081760916839 22 1.60049693331164766 23 1.60238729247988965 24 1.60412340359100081 25 1.60572340359100085 26 1.60720269353182932 27 1.60857443564431213 28 1.60984994584839369 29 1.61103900649048648 30 1.61215011760159754 31 1.61319070032792422 32 1.61416726282792422 33 1.61508553647347064 34 1.61595058837658478 35 1.61676691490719704 36 1.61753851984546859 37 1.61826898003538822 38 1.61896150081101142 39 1.61961896300693509 40 1.62024396300693518 41 1.62083884700455561 42 1.62140574042859198 43 1.62194657331123127 44 1.62246310223685120 45 1.62295692939734493 46 1.62342951918940548 47 1.62388221271588806 48 1.62431624049366574 49 1.62473273362152915
### Compiled on: Tue Sep 24, 2013
main:   addu    $s7, $ra, $zero
# addr of M: constants, vars, temps
        la      $s1, M
### Start of compiled code
# M[23] = M[1]
        l.d     $f2, 8($s1)
        s.d     $f2, 184($s1)
# M[28] = M[0]
        l.d     $f2, 0($s1)
        s.d     $f2, 224($s1)
# Read M[22] as double
        li      $v0, 7
        syscall
        s.d     $f0, 176($s1)
WhileStart0:
# M[36] = M[23] - M[22]
        l.d     $f2, 184($s1)
        l.d     $f4, 176($s1)
        sub.d   $f6, $f2, $f4
        s.d     $f6, 288($s1)
        l.d     $f2, 288($s1)
        l.d     $f4, 0($s1)
        c.eq.d  $f2, $f4
        bc1t    WhileEnd0
# M[37] = M[23] * M[23]
        l.d     $f2, 184($s1)
        l.d     $f4, 184($s1)
        mul.d   $f6, $f2, $f4
        s.d     $f6, 296($s1)
# M[38] = M[1] / M[37]
        l.d     $f2, 8($s1)
        l.d     $f4, 296($s1)
        div.d   $f6, $f2, $f4
        s.d     $f6, 304($s1)
# M[39] = M[28] + M[38]
        l.d     $f2, 224($s1)
        l.d     $f4, 304($s1)
        add.d   $f6, $f2, $f4
        s.d     $f6, 312($s1)
# M[28] = M[39]
        l.d     $f2, 312($s1)
        s.d     $f2, 224($s1)
# Print M[23]
        li      $v0, 3
        l.d     $f12, 184($s1)
        syscall
# Print Tab as ASCII char
        li      $v0, 4
        la      $a0, Tab
        syscall
# Print M[28]
        li      $v0, 3
        l.d     $f12, 224($s1)
        syscall
# Print NewL as ASCII char
        li      $v0, 4
        la      $a0, NewL
        syscall
# M[40] = M[23] + M[1]
        l.d     $f2, 184($s1)
        l.d     $f4, 8($s1)
        add.d   $f6, $f2, $f4
        s.d     $f6, 320($s1)
# M[23] = M[40]
        l.d     $f2, 320($s1)
        s.d     $f2, 184($s1)
        j       WhileStart0
WhileEnd0:
### End of complied code
        addu    $ra, $s7, $zero
        jr      $ra
        .data
        .align  3
M:      .double 0.,1.,2.,3.,4.,5.,6.
        .double 7.,8.,9. # constants
        .space  208  # variables a to z
        .space  1000 # 125 temporaries
Blank:  .asciiz " "
NewL:   .asciiz "\n"
Tab:    .asciiz "\t"
### End of MIPS source


Details of Features illustrated:
  • The Loop: From the Tiny code, this loop has the termination condition n - m == 0, where as with C the "equals zero" part is implied. The loop starts with the "WhileStart0" label and ends with "WhileEnd0". The instruction bc1t WhileEnd0 is the last part of the while condition, just before the loop body.

    You should check for yourself where in the code the value of n - m is placed into a register and how it is subsequently checked against 0.0 for equality.

    Loops using the integer features of MIPS can use branch instructions that directly work based on a comparison of registers. The floating point loops require branches that require and earlier instruction to set a "condition code" and then do a conditional branch based on the code. (So the floating point branches are more like those of a "normal" assembly language.

    Two instructions carry out the conditional branch based on whether or not n - m is 0.0:

             c.eq.d  $f2, $f4
             bc1t    WhileEnd0

    [Beware: in bc1t, the third character is a "one".]

    The c.eq.d instruction is described on page A-74 of Appendix A. There, is says, among other things, in describing

             c.eq.d  cc fs, ft

    "Compare the floating-point double in register fs against the one in ft and set the floating-point condition flag cc to 1 if they are equal. If cc is omitted, condition code flag 0 is assumed." In our case the cc is omitted.

    The bc1t instruction is described on page A-60 of Appendix A. There, is says, among other things, in describing

             bc1t  cc label

    "Conditionally branch [to label] if the floating point coprocessor's condition flag numbered cc is true. If cc is omitted from the instruction, condition code flag 0 is assumed." Again in our case the cc is omitted.

    You can now work out (with some thought and struggle) that these instructions do what we want. Notice that the instruction bc1f is available to reverse true and false.

  • The Comments: My compiler added comments to assist in debugging the compiler itself. These use notation like: M[40], which means an offset of 8*40=320 from the start of M

  • The while loop: The while is self-documenting because of the labels: WhileStart0:, bc1t WhileEnd0, and WhileEnd0:. [These names were my own choice and can be any identifier that isn't reserved.] Additional while loops in the same program can use numbers besides 0 at the end.

(Revision date: 2014-10-08. Please use ISO 8601, the International Standard.)