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.
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