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.
The overview of this topic is in Assembly basics. The same topic in M68K, Z80.
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 condition | signed | unsigned |
|---|---|---|
a == b | beq $a, $b, label | the same |
a != b | bne $a, $b, label | the same |
a < b | blt | bltu |
a <= b | ble | bleu |
a > b | bgt | bgtu |
a >= b | bge | bgeu |
a == 0 | beqz $a, label | the same |
a != 0 | bnez $a, label | the same |
a < 0 | bltz $a, label | never true |
a > 0 | bgtz $a, label | bnez |
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 labelis the jump, and it carries the target's address in the instruction.b labelis the assembler's name forbeq $zero, $zero, label, a branch that is always taken. It reaches 32 kilobytes wherejreaches 256 megabytes, and inside one program they are interchangeable.jr $t0jumps 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.