Comparing without flags

MIPS has no condition code register, so a comparison either happens inside the branch or leaves a 1 or a 0 in a register you named. The six real branches, slt and its family, and the conditions the assembler builds out of them.

There is no flags register on MIPS. No zero bit, no carry bit, no sign bit, no status register anywhere: the panel the M68K and Z80 courses put above the registers is missing from every program on this page because there is nothing to show in it.

That changes how a condition is written. On the M68K a cmp throws its answer away and leaves five bits behind, and the branch on the next line reads them. Here a comparison either is the branch, or it writes its answer into a register you named, the same as any other instruction.

The branches that compare for you

Six real branch instructions, and between them they cover equality and everything against zero.

writtenbranches when
beq $t0, $t1, labelthe two registers are equal
bne $t0, $t1, labelthey are not equal
bltz $t0, label$t0 < 0, signed
blez $t0, label$t0 <= 0, signed
bgtz $t0, label$t0 > 0, signed
bgez $t0, label$t0 >= 0, signed

beq and bne are the only two that look at two registers, and all they ask is whether the bits are the same, which needs no notion of signed or unsigned. The other four compare one register with zero, and there they do read it as a signed number.

$s0 and $s3 come out at 1, because those two branches were not taken and the line under each of them ran. $s1, $s2 and $s4 stay 0. Step through it and watch the pc register jump over the lines the taken branches skipped.

slt, a comparison that is a value

slt $t2, $t0, $t1 means set on less than: it writes 1 into $t2 when $t0 is less than $t1 and 0 when it is not. That is C's t2 = (t0 < t1), and it is where every comparison MIPS does not have as a branch comes from.

Four of them:

  • slt $rd, $rs, $rt, signed, both operands registers.
  • sltu $rd, $rs, $rt, the same read as unsigned numbers.
  • slti $rd, $rs, imm and sltiu $rd, $rs, imm, with a constant on the right.

$t2 and $t4 come out at 1, $t3, $t5 and $t6 at 0. The same two registers, the same question, and the signed and unsigned instructions disagree about the answer, because FFFFFFFF is two different numbers depending on who is reading it.

slt writes a whole word holding 0 or 1, not a byte and not all ones. That matters when you use the answer as a number: add $t3, $t3, $t2 after a slt counts how many times the condition held.

The branches the assembler builds

blt, bgt, ble, bge and their unsigned forms are pseudo-instructions, and each is an slt into $at and a branch on the result.

you writewhat it becomes
blt $t0, $t1, labelslt $at, $t0, $t1 then bne $at, $zero, label
bge $t0, $t1, labelslt $at, $t0, $t1 then beq $at, $zero, label
bgt $t0, $t1, labelslt $at, $t1, $t0 then bne $at, $zero, label
ble $t0, $t1, labelslt $at, $t1, $t0 then beq $at, $zero, label
bltu, bgeu, bgtu, bleuthe same with sltu
beqz $t0, labelbeq $t0, $zero, label, one instruction
bnez $t0, labelbne $t0, $zero, label, one instruction

Two patterns are in that table and they are worth reading off it. Greater than is less than with the operands swapped, since a > b is b < a. And greater or equal is the negation of less than, so the same slt is branched on with beq instead of bne.

$s0 and $s1 both stay 0 and $s2 comes out at 1: the pseudo-instruction and the two real ones below it do the same thing. Click on the blt line after building and the editor prints exactly those two instructions underneath.

Writing blt is fine, and knowing it costs $at is what stops you from keeping something there.

Picking the wrong family

0xFFFFFFFF is 4294967295 unsigned and -1 signed. Compared against 1, one of those is bigger and the other is smaller, so bltu and blt disagree about the same two registers.

$s0 stays 0 and $s1 comes out at 1, from the same two registers. The rule of thumb: addresses, sizes and counts of bytes are unsigned, so compare them with sltu, bltu and bgeu. Differences, coordinates and anything that can go below zero are signed.

The conditions of a flags machine, written here

Everything the M68K's fourteen conditions do has a shape on MIPS:

in Con MIPS
if (a == b)beq $a, $b, label
if (a != b)bne $a, $b, label
if (a < b) signedslt $at, $a, $b and bne $at, $zero, label
if (a < b) unsignedsltu and the same branch
if (a == 0)beq $a, $zero, label
if (a < 0)bltz $a, label
x = (a < b)slt $x, $a, $b, with nothing to branch on
if (a & 8)andi $at, $a, 8 and bne $at, $zero, label

The last row is the one with no instruction of its own. btst on the M68K tests one bit and sets a flag; here you compute the and into a register and branch on whether it came out zero.

The carry that is not there

An unsigned addition that does not fit sets the carry flag on a machine that has one. MIPS has neither the flag nor a trap for it, since addu wraps silently, so a program that needs to know works it out: when an unsigned sum wraps, it comes out smaller than either operand.

$t2 comes out at 1 and $t3 at 1, which is the carry the hardware did not keep. $t6 is 7 and $t7 is 0. Signed overflow, the same question asked of signed numbers, MIPS does answer: add and addi raise an arithmetic overflow exception where addu and addiu wrap.

Nothing gets in the way

On a flags machine an instruction between the comparison and the branch destroys the comparison, because a move writes the flags too. Here the answer to a comparison is a word in a register you chose, so it survives anything that does not write that register, and you can compute an address, load something or call a subroutine between the slt and the branch that reads it.

The one register that is not safe is $at, which every pseudo-instruction in between will take.

Your turn

The test starts $t0 at -5 and $t1 at 3, and wants the larger of the two, read as signed numbers, in $t2. Use slt and a real branch, without blt or bgt.

Show solution

The second one starts $t0 at -1 and $t1 at 1, and asks the same question twice without branching. Leave 1 in $t2 when $t0 is the higher of the two read as unsigned numbers, and 1 in $t3 when it is the greater read as signed. Since $t0 is FFFFFFFF, $t2 comes out at 1 and $t3 at 0.

Show solution