A subroutine with its arguments in registers

Euclid's greatest common divisor as a subroutine called with jal, its arguments in $a0 and $a1 and its answer in $v0.

The greatest common divisor of two numbers, worked out by Euclid's method: replace the pair with the smaller number and the remainder of the division, and go round until the remainder is zero. The program calls it as a subroutine, with the two arguments in $a0 and $a1 and the answer coming back in $v0.

This is the first program on the ladder that calls anything. Everything before it was one block of code running once, and this one has a piece of code with a name that the rest of the program hands work to.

You need to know: the "jal, jr and the calling convention" lecture and the "Multiply and divide, with the remainder" Example. What is new here is the call itself, jal writes the address of the next instruction into $ra and jumps, and jr $ra jumps back to it.

Arguments in $a0 to $a3 and the answer in $v0 is the standard MIPS convention, and this program adds one line of its own to it: gcd destroys $t0, which a subroutine is entitled to do without telling anyone, and the comment above the label says so. Nothing in the machine enforces any of that. A calling convention is exactly this comment, agreed once for a whole program instead of once per subroutine.

gcd is written above main and .globl main is what makes the program start at main anyway. Execution begins at the first instruction in .text and a global main overrides that, so a compute-only program with a subroutine puts the subroutine first and lets main run off the end of the text section, which is how it stops. The M68K version puts the subroutine last and needs a bra over it, because there is nothing like .globl there.

jal touches no memory at all: the return address goes into $ra, where a bsr on the M68K pushes it onto the stack. Step through the call and $ra is 0 until the jal and 0040002C after it, which is the address of the move that follows the call, and jr $ra puts the pc back there. This works because gcd calls nothing: with a second jal inside it the new return address would land on top of the one it still needed, and the stack would have to hold it.

$v0 and $s0 both come out at 0000000C, which is 12: 84 and 36 are both 12 times something and nothing larger divides them both. $a0 finishes at 12 as well and $a1 at 0, since the loop works by overwriting its own arguments.

Try changing the two numbers to 1071 and 462. The answer is 21, and the loop goes round one more time to find it.