Close

2BCD, Or Not 2BCD, That Is the Question

A project log for WDC-1 a "Working Digital Computer"

Make a computer based on the book "How to Build a Working Digital Computer" by Edward Alcosser, James P. Phillips, and Allen M. Wolk

michael-gardiMichael Gardi 01/09/2020 at 15:230 Comments

When I first started looking at the book "How to Build a Working Digital Computer" I noticed with interest that the Input Panel converted decimal numbers to Binary Coded Decimal (BCD) and the Output Panel in turn converted BCD results back to decimal numbers.  In between the ALU is pure binary. This was new for me as all of my other projects were pretty much binary only. Cool. The encoders and decoders for these panels were pretty straight forward as they only had to deal with single digit decimal numbers and their BCD equivalents.  

What I didn't give a lot of thought to was this, "How does a binary result that is greater than 9 get converted into the two BCD inputs that the Output Panel is expecting?". Well the answer is simple, run a program.  Here is the "Readout Program" from the book.

Wait there's more...

So if I'm demonstrating WDC-1 and want to show something basic like adding 8 + 7 I would run a program something like this (assumes the two addends are in Address A and B):

RIN Address A  ; Read first addend into A
TRA            ; Transfer A to X
RIN Address B  ; Read second addend into A
ADD            ; Add A and X. Result in A

With the result now in the accumulator at this point I say "Please bear with me as I perform 43 more complex and potentially error prone instructions so I can show you the answer.".  

This felt like a gap to me so I started looking into binary to BCD conversion. How hard could it be? Well it turns out that while BIN2BCD conversion is not really that hard, it's not easy to do with a simple circuit.  It requires computation, hence the 43 instruction "Readout Program". So I think I understand why the authors relegated their solution to the very back of the book.

I learned that there are a few ways to perform binary to BCD conversion. You can create a circuit to do this. Here is a design I found on Stack Exchange with thanks to jonk that employs the Double Dabble (love the name) algorithm:

This first schematic accepts a binary value and leaves it unchanged if the value is ≤ 4. Otherwise, it adds 3 to the binary input. 

schematic

The second schematic uses the above circuit (named PLUS3 below), repeatedly:

schematic

Like I said, binary to BCD is not an easy get with logic circuits. I can't imagine how this could have been done with "household" items.

Another clever way to do a binary to BCD conversion is to use a binary up/down  counter (SN74LS191 say) and a BCD counter (SN74LS192 say). The binary number is in parallel jammed into the up/down counter. Then the counter is clocked to count down to zero. The same clock is used to clock the BCD counter up from zero. When the binary counter reaches zero, the BCD counter will contain the binary number in BCD format.

A third option is to use a microprocessor.  Now at this point I had already decided that I really needed (wanted) a BIN2BCD component to get me past the awkward 8 + 7 show the result issue. I was tempted by option number 2, the counters solution, which was more in line with my use of 7400 series ICs in other modules, but I literally had an Arduino Nano sitting on the desk in front of me. So microprocessor it is. Here is my BIN2BCD module:

The first thing you'll notice is that I chose different colors for this component. I wanted to make it clear that this module, with it's definitely not from the 60s microprocessor, was somehow "outside" of the Working Digital Computer book implementation.  At a minimum the different color should initiate a discussion as to why. 

I wrote a "brute force" but hopefully easy to understand sketch for the Arduino:

#include <avr/pgmspace.h>

/**
 * Debug. Uncomment the following line for debugging.
 */
// #define DEBUG 1

// Only recalculate if different.
int oldDecimal = -1;

void setup() {

  // Initialize serial debugging.
#ifdef DEBUG
  Serial.begin(115000);
  Serial.println("Setup Binary to BCD.");
#endif

  // Setup A0-A5,11,12 to output the BCD. A0-A3 represent 
  //  the tens value and the rest the ones value.
  pinMode(A0, OUTPUT);
  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT);
  pinMode(A4, OUTPUT);
  pinMode(A5, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);

  // Clear the BCD display.
  clearBCD();

  // Set D3-D10 to input the Binary were D3 = 1 and 
  //  D10 = 128.
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);  
  pinMode(5, INPUT_PULLUP);  
  pinMode(6, INPUT_PULLUP);  
  pinMode(7, INPUT_PULLUP);  
  pinMode(8, INPUT_PULLUP);  
  pinMode(9, INPUT_PULLUP);  
  pinMode(10, INPUT_PULLUP); 

  // Clear the inputs.
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH);
}

void loop() {
  // Calculate the BCD values the delay a second.
  calculateBCD();
  delay(1000);
}

/**
 * Nothing fancy here. Convert the binary inputs to a decimal
 * number. If the number is greater than 99 emit a zero as an
 * invalid indicator. Otherwise emit the tens and ones as BCD
 * values.
 */
void calculateBCD() {

  // Find the decimal number.
  int decimal = 0;
  if (digitalRead(3) == LOW) {
    decimal += 1;
  }
  if (digitalRead(4) == LOW) {
    decimal += 2;
  } 
  if (digitalRead(5) == LOW) {
    decimal += 4;
  } 
  if (digitalRead(6) == LOW) {
    decimal += 8;
  } 
  if (digitalRead(7) == LOW) {
    decimal += 16;
  } 
  if (digitalRead(8) == LOW) {
    decimal += 32;
  } 
  if (digitalRead(9) == LOW) {
    decimal += 64;
  } 
  if (digitalRead(10) == LOW) {
    decimal += 128;
  }

#ifdef DEBUG
  Serial.print("Decimal = ");
  Serial.println(decimal);
#endif

  // Only change if inputs have changed.
  if (decimal != oldDecimal) {
    oldDecimal = decimal;
    
    //  Check for value out of bounds.
    if (decimal > 99) {
      decimal = 0;
    }

    // Clear the old values.
    clearBCD();
  
    // Then output the new BCD values.
    int tens = decimal / 10;
    int ones = decimal % 10;

#ifdef DEBUG
    Serial.print("Tens = ");
    Serial.println(tens);
    Serial.print("Ones = ");
    Serial.println(ones);
#endif

    if ((tens & 1) > 0) {
      digitalWrite(A4, HIGH);
    } 
    if ((tens & 2) > 0) {
      digitalWrite(A5, HIGH);
    } 
    if ((tens & 4) > 0) {
      digitalWrite(11, HIGH);
    }
    if ((tens & 8) > 0) {
      digitalWrite(12, HIGH);
    }
  
    if ((ones & 1) > 0) {
      digitalWrite(A0, HIGH);
    }
    if ((ones & 2) > 0) {
      digitalWrite(A1, HIGH);
    } 
    if ((ones & 4) > 0) {
      digitalWrite(A2, HIGH);
    }
    if ((ones & 8) > 0) {
      digitalWrite(A3, HIGH);
    }
  }
}

/**
 * Turn off all the BCD lights.
 */
void clearBCD() {
  digitalWrite(A0, LOW);
  digitalWrite(A1, LOW);
  digitalWrite(A2, LOW);
  digitalWrite(A3, LOW);
  digitalWrite(A4, LOW);
  digitalWrite(A5, LOW);
  digitalWrite(11, LOW);
  digitalWrite(12, LOW);
}

That's it. I think I now appreciate why the authors did not tackle this as part of their build. For me though, 2BCD is the answer. 

Discussions