Adding ADDC and SUBB instructions
Posted on Sun 22 December 2019 in MUPS16
Currently we don't have add-with-carry or subtract-with-borrow instructions, but they would be useful for doing 32-bit maths, and easy to implement. If nothing else, the ALU should support them in case I want to add them later.
Control unit changes
We need to add a carry flag to the control unit's flags register. In addition, we need the ability
to selectively latch the ALU's carry_out
output into this flag (previously the only flags we took
from the ALU were zero
and less_than
, both of which we used on the next cycle (within the same
instruction), so we didn't care if they got overwritten on every cycle. To make ADDC
and SUBB
work we need to only snap the carry flag when we end the calculation cycle of an ADD
or SUB
instruction.
ADDC
This is easy: we feed the carry_flag
to the carry in of the lowest bit if the operation is 8
(ADDC
), and output the carry-out from the top bit back to the control unit.
SUBB
This one confuses me sometimes, so I'm writing it out in the hope that it clarifies things for me. Subtract-with-borrow is the second part of a pair of subtract instructions:
sub dest_low, a_low, b_low
subb dest_hi, a_hi, b_hi
If b_low
is greater than a_low
then we will have borrowed a bit from a_hi
to compute the
result. For example, if we pretend we only have 4 bits in each for simplicity, we might have:
a = 1010 0110
b = 0010 1101
To compute a_low - b_low
we need to borrow 1 from the low bit of a_hi
. When we calculate a_hi -
b_hi
we need to subtract an additional 1 to make the result correct.
There are two parts to this:
- detecting that we borrowed, and setting the borrow flag accordingly
- propagating the borrow into the second subtraction
Since we implement basic subtraction as a + -b
, and -b
is ~b + 1
(basic two's-complement),
detecting borrowing is simple: if we don't get a carry out of the result, then there was a
borrowing.
The second part turns out to be equally easy: since we implemented implemented the final + 1
in
the normal subtraction above by feeding in a fixed 1 value to the carry-in of the lowest adder, to
handle adjusting for borrowing we can just feed in 0 to the lowest carry-in if the borrow flag is
set. However, we can simplify even more: if the first subtraction doesn't result in a borrow, carry
out is 1, and we want to feed 1 in as the carry in of the subb
. If the first subtraction does
result in a borrow, carry out is 0, and we want to feed 0 in as the carry in of subb
. So, we can
just feed the carry flag in directly. Effectively, the control unit's carry
flag is also
~borrow
.
Summary:
Operation | carry_flag | carry_in | Note |
---|---|---|---|
add |
0 | 0 | |
add |
1 | 0 | |
sub |
0 | 1 | |
sub |
1 | 1 | |
addc |
0 | 0 | |
addc |
1 | 1 | |
subb |
0 | 0 | (indicates borrow in previous sub ) |
subb |
1 | 1 | (indicates no borrow in previous sub ) |