Doing math
Every calculation happens between registers. An arithmetic instruction names the destination first, then the two sources it reads: add dest, src1, src2 means dest = src1 + src2. Let's take the operations one at a time.
Adding and subtracting
add takes the numbers in two registers and stores their sum in a third. sub is the same for subtraction: sub t2, t0, t1 computes t0 - t1. Both read registers only.
add t2, t0, t1 # t2 = t0 + t1 sub t2, t0, t1 # t2 = t0 - t1
One thing to know: a register only holds 32 bits, so there is a ceiling. If a result is too big to fit, it doesn't raise an error; it quietly wraps around back to very low numbers and keeps going. You won't hit this with everyday values, but it's worth knowing the ceiling is there.
To use a fixed number instead of a second register, add the letter i, for immediate: addi t0, t0, 1 adds the constant 1. There is no subi, so to subtract a constant you add a negative one: addi t0, t0, -1.
addi adds a constant; add adds two registers. Mixing them up is the #1 beginner error.Multiplying and dividing
mul multiplies two registers. div divides them as whole numbers and drops the fraction, so 7 / 2 gives 3; rem gives what's left over, so 7 % 2 gives 1. Division never crashes here: dividing by zero just returns -1.
mul t2, t0, t1 # t2 = t0 * t1 div t2, t0, t1 # t2 = t0 / t1 (integer) rem t2, t0, t1 # t2 = t0 % t1 (remainder)
Putting it together
li t0, 7 li t1, 5 add t1, t0, t1 # t1 = 7 + 5 = 12 mv a0, t1 # the print service reads a0 li a7, 1 ecall li a7, 10 ecall
Read it line by line. li t0, 7 loads 7 into t0; li t1, 5 loads 5 into t1. add t1, t0, t1 adds them and writes 12 back into t1. mv a0, t1 copies the result into a0, because the print service always reads its value from a0. Then li a7, 1 picks the print-integer service and ecall runs it; finally li a7, 10 and ecall end the program.
Bonus: multiplying with shifts
When the factor is a power of two, there's a faster way than mul. sll (shift left logical) slides a register's bits to the left, and every position you shift doubles the value: shift by 1 to multiply by 2, by 2 for 4, by 3 for 8. Shifting is cheaper than multiplying, so compilers reach for it constantly.
li t0, 5 # start with 5 # shift left by 3 bits (5 * 2^3 = 5 * 8) sll a0, t0, 3 # a0 = 40 li a7, 1 # print integer ecall li a7, 10 ecall
Step through it. li t0, 5 puts 5 in t0. sll a0, t0, 3 shifts its bits three places left, and since each place doubles the value, 5 doubles three times (5, 10, 20, 40), landing on 40 in a0. The rest is the print pattern from before. Try changing the 3 to a 1 or a 2 and predict the result before you run it.