The last few weeks, I've been building vtach — a Verilog implementation of CARDIAC, the old paper-based demonstration computer from Bell Labs. Granted, this isn't the most practical project in the world, but it is useful enough to be interesting and simple enough to be useful in a classroom or for self-learning.
I squashed a latent bug in the code last time. That still leaves vtach incomplete, however, since I didn't implement TAC and SFT, which implement a "jump on negative" and a shift operation. The shift is a little strange since CARDIAC (and vtach) are decimal machines. A shift left is a multiply by 10 and a shift right is a divide by 10. In addition, when I went to do the shift instruction, I realized I had made the accumulator too small (CARDIAC's accumulator has an extra overflow digit).
This time, I'll fix all of that and you'll see a few interesting design features along the way. The use of decimal numbers in a CPU is a bad idea that went out of style many years ago. However, I wanted to stay faithful to CARDIAC and — in a classroom setting — it lets you put off talking about binary and hex (at least directly) if you choose to do so.
Most of the complexity in the adder module is due to this representation. There are two issues. First, handling carry is complicated because the bit pattern is not continuous. That is, in a binary numbering scheme adding two numbers is a natural operation where adding one to number results in the next natural number. Using binary-coded decimal, adding 9 and 1 results in a bit pattern that means 16 (10 hex which is also, of course, ten in BCD). That means carry bits need special handling.
The other issue is subtraction. With a decimal machine, it is easiest to treat negative numbers using a 9’s compliment scheme. In a normal binary machine, you represent negative numbers in two’s compliment — invert the bits and add one. So for a byte, -1 is FF hex and -16 is F0 hex. Surprisingly, adding F0 to a normal number is the same as subtracting 16 if you keep the answer at a byte's length. For example, 32 is 20 hex and F0+20=10 which is 16 decimal.
Using 9's compliment is similar, but you subtract each digit from 9. For example, consider a three digit addition of 500 and -210. Subtracting each digit from 9 results in 789. You still have to add 1, so that's 790. If you compute 500+790 you get 1290, but this is a three-digit add, so the answer is 290, the correct answer.
To simplify program entry, I set vtach to use a sign bit and the digits are not stored in 9's compliment. All of that is handled internal to the CPU. To set up the math problem above for vtach, you'd load the numbers 13'h0500 and 13'h1210. The first 0 or 1 is the sign bit (1 is obviously negative). The rest of the hex digits are the BCD digits from 0-9.