Branch on compare

An if in C becomes a goto and a goto becomes one MIPS branch, because the comparison is inside the branch instruction. The condition you invert, the else if chain, and the two instructions that pick a value without jumping anywhere.

An if in C becomes a goto, and on MIPS a goto becomes one instruction, because the comparison happens inside the branch. There is no flag to set first and nothing between the two lines to get in the way.

The condition you write is the one you invert

Flattening an if was the Assembly basics lecture on branching, and the shape it arrived at is this: jump over the true branch when the condition is false.

int x = 50;
if (x > 10) {
    x = 100;
} else {
    x = 200;
}
    int x = 50;
    if (x <= 10) goto else_branch;
    x = 100;
    goto end;
else_branch:
    x = 200;
end:

The > in the C became a <= in the flat version, because the branch is taken when the if is not. In MIPS that inverted condition is the mnemonic you write:

$t0 comes out at 100. Change li $t0, 50 to li $t0, 5 and it comes out at 200 instead.

The j end is the flat version's goto end, and leaving it out is the most common bug in hand written control flow: the program runs the true branch, walks straight into the false one and the second answer wins.

Which branch says which condition

Six of these are real instructions and the rest are pseudo-instructions built out of slt, which "Comparing without flags" takes apart. What matters when you are writing a program is the table.

the C conditionsignedunsigned
a == bbeq $a, $b, labelthe same
a != bbne $a, $b, labelthe same
a < bbltbltu
a <= bblebleu
a > bbgtbgtu
a >= bbgebgeu
a == 0beqz $a, labelthe same
a != 0bnez $a, labelthe same
a < 0bltz $a, labelnever true
a > 0bgtz $a, labelbnez

Every one of them takes a label, and the assembler works out the distance. A branch reaches about 32 kilobytes either way, which is thousands of instructions, so in practice you write the label and forget about it.

The right hand column is the reminder to pick the family your numbers belong to. An address or a count of bytes compared with blt is being read as a signed number, and one of them above two billion comes out negative.

else if

else if is a second comparison at the label the first branch fell through to, and every answer ends with a jump to the end.

char grade;
if (score >= 90)      grade = 'A';
else if (score >= 60) grade = 'B';
else                  grade = 'C';

$t2 comes out at 00000042, which is 0x42, the ASCII code of B. The li $t1, 90 and li $t1, 60 are there because bge compares two registers and 90 and 60 have to be in one; bge also accepts a constant directly, and then the assembler puts it in $at for you.

Try changing li $t0, 75 to li $t0, 95 and to li $t0, 12 and watching $t2.

An if with no branch at all

When the two sides of an if are one value each, three instructions do it without jumping anywhere. slt puts the condition in a register, and movn and movz copy a register only if a third one is or is not zero.

$t2 comes out at 1, and $t3 and $t4 both at 3, the larger of the two. movn $t3, $t1, $t2 reads "move if not zero": it writes $t1 into $t3 when $t2 is not zero and does nothing when it is. That is C's t3 = t2 ? t1 : t3, and a CPU likes it because there is no jump to guess about.

slt on its own is already x = (a < b) with no branch: when what you want is the 1 or the 0 rather than two different pieces of code, that one instruction is the whole if.

Testing one bit

The M68K has btst, which tests a bit and sets a flag. MIPS has no such instruction and no such flag, so you compute the and into a register and branch on whether it came out zero.

$t2 comes out at 1, because bit 3 of 1010 is set, and $t4 at 0, because bit 0 is not. andi with a single bit set is C's x & 8, and beqz on the answer is C's if (!(x & 8)).

For a bit whose position the program worked out, shift instead: srlv $t1, $t0, $t2 brings bit $t2 down to the bottom and andi $t1, $t1, 1 keeps it.

j, b and jr

Three ways to go somewhere unconditionally.

  • j label is the jump, and it carries the target's address in the instruction.
  • b label is the assembler's name for beq $zero, $zero, label, a branch that is always taken. It reaches 32 kilobytes where j reaches 256 megabytes, and inside one program they are interchangeable.
  • jr $t0 jumps to the address in a register, which is how a program returns from a subroutine (jr $ra) and how it jumps through a table of addresses it computed.

A label is an address like any other, so la $t0, done puts one in a register and jr $t0 goes there. That is the whole of a computed jump, and a switch with a jump table is a .word of labels, an index scaled by four, an lw and a jr.

Your turn

The test starts $t0 at -7. Leave its sign in $t1: -1 when $t0 is negative, 0 when it is zero and 1 when it is positive. For -7 that is -1.

Show solution

The second one starts $t0 at 75 and wants the grade of the chain above in $t1: 'A' for 90 and over, 'B' for 60 and over, 'C' otherwise. 'B' is 0x42.

Show solution