BCD is an acronym for Binary Coded Decimal. A BCD clock shows the time as a series of six columns where each column is a binary number. When the binary number in each column is converted into a decimal digit, it tells you the time. This article shows you two ways to write both a VB 6 and a VB.NET program to do the same thing. The inspiration to write this article came from an earlier article that I wrote about Bitwise Operations in VB.NET. You might want to check that one out for more about binary numbers. If you need a more comprehensive introduction, try Computer Number Systems.
After I completed my first binary clock, and published the first version of this article about it, About Visual Basic reader (and my co-author for the Computer Number Systems article linked above) Dr. Peter Zilahy Ingerman sent me an email which said (my interpretation), "Well, that's all very nice, but if you were using VB6, there's a much better way to do it using control arrays, which Microsoft took away from us in VB.NET."
I had to admit that Dr. Ingerman had a very compelling case. First, his actual algorithm had some serious advantages compared to mine. Second, he was right about control arrays. That VB6 tool does make it easier. Dr. Ingerman's program and an explanation of why it works is included in the article next.
Finally, I decided to see what it would take to translate Dr. Ingerman's program into VB.NET. Control arrays couldn't be used, of course, so I programmed around the problem. As before, I invite any suggestions about a better way to do the job.
First, here's the actual clock that inspired me to write this program:
--------
Click Here to display the illustration
Click the Back button on your browser to return
--------
To duplicate this clock with a screen display, I decided to use three GroupBox components to contain three groups of CheckBox components representing "Hours", "Minutes", and "Seconds". Each column of CheckBoxes is one BCD digit. Here's the resulting clock:
--------
Click Here to display the illustration
Click the Back button on your browser to return
--------
Here's the entire code for the program. I added a Timer object with an Interval property of 1000 to trigger the event subroutine once each second.
Private Sub Timer1_Tick( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Timer1.Tick
TimeDisplay.Text = Format(Now, "HH:mm:ss")
Dim I As Date
I = #11:58:59 PM#
' Hours - Zero Column
HrsZeroZero.Checked = (Hour(Now) Mod 10) Mod 2
HrsZeroOne.Checked = ((Hour(Now) Mod 10) \ 2) Mod 2
HrsZeroTwo.Checked = ((Hour(Now) Mod 10) \ 4) Mod 2
HrsZeroThree.Checked = ((Hour(Now) Mod 10) \ 8)
' Hours - One Column
HrsOneZero.Checked = (Hour(Now) \ 10) Mod 2
HrsOneOne.Checked = ((Hour(Now) \ 10) \ 2)
' Minutes - Zero Column
MinZeroZero.Checked = (Minute(Now) Mod 10) Mod 2
MinZeroOne.Checked = ((Minute(Now) Mod 10) \ 2) Mod 2
MinZeroTwo.Checked = ((Minute(Now) Mod 10) \ 4) Mod 2
MinZeroThree.Checked = ((Minute(Now) Mod 10) \ 8)
' Minutes - One Column
MinOneZero.Checked = (Minute(Now) \ 10) Mod 2
MinOneOne.Checked = ((Minute(Now) \ 10) \ 2) Mod 2
MinOneTwo.Checked = ((Minute(Now) \ 10) \ 4)
' Seconds - Zero Column
SecZeroZero.Checked = (Second(Now) Mod 10) Mod 2
SecZeroOne.Checked = ((Second(Now) Mod 10) \ 2) Mod 2
SecZeroTwo.Checked = ((Second(Now) Mod 10) \ 4) Mod 2
SecZeroThree.Checked = ((Second(Now) Mod 10) \ 8)
' Seconds - One Column
SecOneZero.Checked = (Second(Now) \ 10) Mod 2
SecOneOne.Checked = ((Second(Now) \ 10) \ 2) Mod 2
SecOneTwo.Checked = ((Second(Now) \ 10) \ 4)
End Sub
The time is captured using the VB.NET Now object. The Hour, Minute, and Second functions take Now as a parameter and return an integer. That takes care of what to use for each GroupBox.
The next problem is how to get the first digit and second digit of each time integer. The "zero" digit can be calculated by using the Mod (modulo) operation. This returns just the remainder after division by the second number. So, for example, 59 Mod 10 returns just 9. The second digit can be returned using the integer divide operation, \ (the backslash) with 10. This returns just the integer part of a number after division by the second number. So 59 \ 10 returns just 5.
It's easier to see with decimal numbers (because most of us are more used to looking at them), but the process of isolating each binary digit is exactly the same. The only thing that changes is that we use 2 instead of 10. The key to fully understanding the operation, however, is to realize that when the higher order digits are returned, it's the decimal equivalent of a binary number.
Using the example of 59, the first step is to isolate 9. That's 1001 in binary. An integer division by 2 gives us the higher order digits without the first one or 100 in binary. That's 4 in decimal. We can then use the Mod operation with 2 to discover what the lowest order binary digit actually is. In this case, it's 0. Integer divide using 4 (that is, 2 to the second power) isolates the top two binary digits (10, or 2 in decimal) and Mod 2 tells us what the first one is (0 again). We don't have use the Mod 2 operation on the last digit because there's only one binary digit, 1.
Since the operation returns a 0 or 1, we can use that as a pseudo true/false value to change the CheckBox.Checked property directly. VB.NET actually uses -1 and 0 for True and False, but it will interpret any non-zero number as True.
On the next page, Dr. Ingerman's VB6 version of the BCD Clock is revealed.
