CS 273: Euclid's Algorithm

We all know what the Greatest Common Divisor is: given two integers m and n, it is the largest integer g such that g*i = m and g*j = n, where i and j are also integers.

Here's an interesting fact about the GCD: if m >= n, then gcd(m, n) = gcd(m - n, n). We can show this by showing, first, that any common divisor of m and n is also a common divisor of m-n and n, and then that there is no larger common divisor of m-n and n than g.

  1. g*i - g*j = g*(i - j), and i - j is still a positive integer. So g is a divisor of m-n and n.
  2. Suppose the GCD of m-n and n were some integer g' > g. Then there would be integers i' and j' such that i' * g' = m-n and j' * g' = n. Since m = m-n + n, m = i'*g' + j'*g' = (i' + j') * g', so g' would be the GCD of m and n. Since it wasn't, there is no such g'.
A second fact we need to remember here is that gcd(m, 0) = m.

The last fact we will need is that gcd(m, n) == gcd(n, m).

Euclid found an algorithm (it appears in a book he wrote in about 300BC) that exploits these facts to calculate the GCD of two numbers M and N, as follows:


int gcd, m, n;
#define M 16
#define N 12
void euclid() {
    m = M;
    n = N;

    // gcd(m, n) == gcd(M, N)
    while (n != 0) {
        // gcd(m, n) == gcd(M, N)
        if (m >= n)
            // gcd(m-n, n) == gcd(M, N)
            m = m - n;
            // gcd(m, n) == gcd(M, N)
            // gcd(m, n) == gcd(M, N)
        else {
            // (gcd(m, n) == gcd(M, N)) && (m > n)
            tmp = m;
            // gcd(tmp, n) == gcd(M, N)
            m = n;
            // gcd(tmp, m) == gcd(M, N)
            n = tmp;
            // gcd(n, m) == gcd(M, N)
            // gcd(m, n) == gcd(M, N)
        }
    }
    // (gcd(m, n) == gcd(M, N)) && (n == 0)
    // gcd(m, 0) == gcd(M, N)
    // n = gcd(M, N)
    gcd = m;
}

This algorithm has become one of the Grand Old Examples, because it's simple enough to use as an example, and at the same time complicated enough to require some thought. How can we convert this to assembly?

The main thing is to go through it line by line and construct by construct, and see how each construct we encounter can be converted into assembly code. Looking at the code, we need to reserve space for four variables (gcd, m, n, and tmp), and we need to create two constants (M and N).

We've already seen how to reserve space for memory variables:


      org    0
gcd   rmb    1
m     rmb    1
n     rmb    1
tmp   rmb    1

Something new is how to create a constant:


M     equ    16
N     equ    12

We normally want to do this up with the variable declarations — or maybe even above them. Now we need to start the executable code, and perform the initializations:


      org    $f800
start
      ldaa   #M     * m = M
      staa   m

      ldaa   #N     * n = N
      staa   n

Now we've got a while loop to consider. We can consider the while loop in isolation from the rest of the program! So our assembly code looks like this:


while  tst   n          * while (n != 0) {
       beq   done

* lots of other stuff goes here

       bra   while      * }
done

Similarly, we can look at the if-then-else in isolation, without thinking about either the while loop around it nor the assignment statements within it. If we do that, the if-then-else turns into:


     ldaa    m      * if (m >= n) {
     cmpa    n
     blt     else

* if-stuff goes here

     bra     endif  * } else {
else

* else-stuff goes here

endif               * }

Let's take a look at all the HC11 code we've created so far:


M     equ    16
N     equ    12

      org    0
gcd   rmb    1
m     rmb    1
n     rmb    1
tmp   rmb    1

      org    $f800
start
      ldaa    #M        * m = M
      staa    m

      ldaa    #N        * n = N
      staa    n

while tst     n          * while (n != 0) {
      beq     done

      ldaa    m        *     if (m >= n) {
      cmpa    n
      blt     else

* if-stuff goes here

     bra      endif     *     } else {
else

* else-stuff goes here

endif                   *     }

      bra     while     * }
done

Now, we have to fill in the if-stuff and the else-stuff. Here's the if-stuff:


      ldaa    m       * m = m - n
      suba    n
      staa    m

And the else-stuff:


      ldaa    m       * tmp = m
      staa    tmp

      ldaa    n       * m = n
      staa    m

      ldaa    tmp     * n = tmp
      staa    n

If we put all our code together, and stick an end statement on it, we get:


M     equ     16
N     equ     12

      org     0
gcd   rmb     1
m     rmb     1
n     rmb     1
tmp   rmb     1

      org     $f800
start
      ldaa    #M      * m = M
      staa    m

      ldaa    #N      * n = N
      staa    n

while tst     n       * while (n != 0) {
      beq     done

      ldaa    m       *     if (m >= n) {
      cmpa    n
      blt     else

      ldaa    m       *         m = m - n
      suba    n
      staa    m

      bra      endif  *     } else {
else
      ldaa    m       *         tmp = m
      staa    tmp

      ldaa    n       *         m = n
      staa    m

      ldaa    tmp     *         n = tmp
      staa    n

endif                 *     }

      bra     while   * }
done  bra     done

      end     start

Last modified: Mon Feb 11 09:23:11 MST 2008

Valid HTML 4.01 Transitional