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:

  1. detecting that we borrowed, and setting the borrow flag accordingly
  2. 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)