## From Binary To BCD…And Back Again (Part 1)

Binary coded decimal (BCD) is a way of representing numbers in a computer as decimal rather than binary digits. Most commonly 4 bits are used to represent each digit since 4 bits can contain the numbers 0-9 and it allows two digits to be packed into a single byte.

For those who still enjoy assembly language programming the conversion from binary to BCD is not an uncommon operation. The algorithm is well known and is usually referred to as Add 3 – Shift.

While I’ve seen the algorithm described in many places, I haven’t yet run across an explanation of how it’s derived. Turns out it’s not very difficult.

The Basic Algorithm

The best description I’ve run across for this algorithm is “you calculate the value of the binary number but do the calculations using decimal values.”

You may recall that a binary number can be represented as a polynomial in the form:

$\Large y = {p}_{1}{x}^{n} + {p}_{2}{x}^{n-1} + ... + {p}_{n}{x} + {p}_{n-1}$

In PIC assembly language, for an 8 bit value and a binary accumulator, the code to calculate this value woud look like:

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    ACC_REGISTER    ; clear the result accumulator
movlw   d'8             ; load the counter
movwf   COUNTER         ;

; Rotate bits
ROTATE
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     ACC_REGISTER, 1 ; rotate carry into accumulator low bit

decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

For a decimal accumulator, the code is similar but you need a separate register for each digit. You shift the individual digits to the left to multiply by 2, then check for carrys. If a digit it greater than 10, subtract 10 and add 1 to the next higher digit.

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    DEC0_REGISTER   ; clear decimal digit 0
clrf    DEC1_REGISTER   ; clear decimal digit 1
clrf    DEC2_REGISTER   ; clear decimal digit 2
movlw   d'8             ; load the counter

; Rotate bits
ROTATE
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     DEC0_REGISTER, 1; rotate decimal digit 0
rlf     DEC1_REGISTER, 1; rotate decimal digit 1
rlf     DEC2_REGISTER, 1; rotate decimal digit 2

DIGIT0
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfss   status, c       ; skip if >= 10
incf    DEC1_REGISTER   ; increment next digit
movwf   DEC0_REGISTER   ; move value back to the register

DIGIT1
movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfss   status, c       ; skip if >= 10
incf    DEC2_REGISTER   ; increment next digit
movwf   DEC1_REGISTER   ; move value back to the register

DIGIT2
decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

You could stop here but there are some optimizations that can be done. For example, we know that any accumulator holding a digit of 5 or greater will have a carry once it’s multiplied by 2. To take advantage of this, check the value of each digit. If it’s 5 or greater set that register’s most significant bit (MSB). When you perform the shift, that flag will rotate into the carry and the carry flag from the previous digit will rotate into the least significant bit (LSB). This same trick can be used to shift the MSB of the original binary value into the LSB of the lowest decimal digit. Making this optimization, the code becomes:

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    DEC0_REGISTER   ; clear decimal digit 0
clrf    DEC1_REGISTER   ; clear decimal digit 1
clrf    DEC2_REGISTER   ; clear decimal digit 2
movlw   d'8             ; load the counter

; Set the flag bits
ROTATE
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfsc   status, c       ; skip if < 5
bsf     DEC0_REGISTER, 7; set the MSB

movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfsc   status, c       ; skip if < 5
bsf     DEC1_REGISTER, 7; set the MSB

; Rotate the bits
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     DEC0_REGISTER, 1; rotate decimal digit 0
rlf     DEC1_REGISTER, 1; rotate decimal digit 1
rlf     DEC2_REGISTER, 1; rotate decimal digit 2

decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

; Check for digits > 10
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfsc   status, c       ; skip if < 10
movwf   DEC0_REGISTER   ; move value back to the register

movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'246           ; is it >= 10?
btfsc   status, c       ; skip if < 10
movwf   DEC1_REGISTER   ; move value back to the register

; Update the counter
decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

The next optimization is to combine the digit adjustment with setting the flag. We already said any register with a value of 5 or greater will have a carry once it’s multiplied by 2. It will also have 10 subtracted after the shift. So we’ll combine the two and, rather than subtract 10 after the shift, we’ll subtract 5 before the shift. The code becomes:

```; Load the registers
movlw   d'204           ; load the binary register
movwf   BIN_REGISTER    ;
clrf    DEC0_REGISTER   ; clear decimal digit 0
clrf    DEC1_REGISTER   ; clear decimal digit 1
clrf    DEC2_REGISTER   ; clear decimal digit 2
movlw   d'7             ; load the counter

; Set the flag bits
ROTATE
DIGIT0
movf    DEC0_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfss   status, c       ; skip if >= 5
iorlw   d'128           ; set the MSB
movwf   DEC0_REGISTER   ; move value back to register

DIGIT1
movf    DEC1_REGISTER, 0; move decimal digit to acc
addlw   d'251           ; is it >= 5?
btfss   status, c       ; skip if >= 5
iorlw   d'128           ; set the MSB
movwf   DEC1_REGISTER   ; move value back to register

; Shift the bits
SHIFT_BITS
rlf     BIN_REGISTER, 1 ; rotate number to the left,
; putting hi bit into carry
rlf     DEC0_REGISTER, 1; rotate decimal digit 0
rlf     DEC1_REGISTER, 1; rotate decimal digit 1
rlf     DEC2_REGISTER, 1; rotate decimal digit 2

decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

; Update the counter
decfsz  COUNTER, 1      ; decrement the counter
goto    ROTATE          ; back to the top of the loop

END
...
```

So after all that, where does Add 3 – Shift come from? Each time through the loop, if a register’s value is greather than 5, 5 is subtracted and the MSB set. Setting the MSB is the same as adding 128 so we’re really adding 123.

This is for an 8 bit register. But as I mentioned at the beginning, BCD is normally done using 4 bit registers. In that case, setting the MSB is the same as adding 8. So add 8, subtract 5 (in other words, add 3) and shift.

## Fixing CH340 Problems On A NodeMCU V3 Board

A while back I bought a couple of NodeMCU V3 boards via EBay. They’ve sat among my “projects waiting to happen” until I pulled them out this past week intending to install MicroPython on one of them. However, when I connected the boards to my Linux laptop’s USB port they weren’t recognized. This board uses the CH340G USB-to-UART chip and when I ran dmesg I saw the errors shown below:

This is not an uncommon problem if the number of people posting questions with similar errors is any indication. Some saw it as a software driver problem. Some indicated changing to a different USB cable or using a powered hub fixed the problem. I believed it was a hardware problem since removing and reinserting the USB cable would occasionally result in different errors.

This was confirmed when I noticed the laptop would recognize the CH340G with no errors if I flexed the board’s microUSB connector while pressing the reset button. I suspect the problem was caused by a solder bridge on the board below the USB connector or a problem in the connector itself but ultimately the question was, how to fix it?

First I tried to reflow the connector’s solder using a hot air gun. When that didn’t work I decided to replace the connector with something a bit more substantial.

The result is shown below. The microUSB connector was removed and replaced with a 4 pin header attached to the board using super glue. The header pins were connected to appropriate locations on the NodeMCU board using wire-wrap wire as shown in the photo and specified in the accompanying list.

Header Pin Name USB Wire Color Board Connection
1 USB GND Black AMS1117 (3.3V Reg) Pin 1
2 USB D+ Green CH340G Pin 5
3 USB D- White CH340G Pin 6
4 USB 5V Red AMS1117 (3.3V Reg) Pin 3

Replacing the NodeMCU’s microUSB connector means a custom programming cable is needed for the board. That was easily accomplished by cutting the end off an old USB cable and replacing it with a 1X4 crimp connector. Now when the board is plugged in the laptop’s USB port the CH340G is recognized with no errors.

If you’re careful you may be able to just remove the existing microUSB connector and replace it with a new one. However, the board traces below the connector are tiny and pull up easily. Ultimately, it may be easier (and lesss frustrating) to go with with pin header replacement in the first place.

## Creating A Portable Linux Installation On A Flash Drive (Part 2)

In the previous post I described how to create a Linux bootable USB flash drive. However, sometimes you may want to run this installation in conjunction with your normal desktop rather than have it take over the whole system. In that case, you can boot the flash drive from within VirtualBox.

Granting The Proper Permissions

For this to work, VirtualBox must have the proper flash drive permissions. There are multiple ways to approach this but the one I like best is described in the answer to this question on Stack Exchange.

All it takes is to change to the `/etc/udev/rules.d` directory and create a new rules file (e.g., `usb_virtualbox.rules`) containing the line:

```SUBSYSTEM=="block", ATTRS{idVendor}=="1d6b", ATTRS{idProduct}=="0003", ACTION=="add", RUN+="/bin/setfacl -m g:vboxusers:rw- /dev/\$name"
```

The `idProduct` and `idVendor` must be changed to match the flash drive’s PID and VID. In short, this rule grants members of the vboxusers group read/write permission for the USB flash drive with the corresponding PID/VID when the drive is plugged in.

Attaching The USB Flash Drive

VirtualBox does not have the capability to boot directly from a USB drive. Instead, you’ll need to create a virtual hard drive that redirects access. These instructions are based upon those provided in this thread.

Insert the flash drive. Once it mounts open a terminal and execute the command:

```\$ lsblk
```

You’ll be able to identify the device name from the resulting output. In the case shown below, the flash drive device is `/dev/sdb`.

In the terminal, switch to the directory where you want to store the virtual hard drive and execute the command:

```\$ sudo vboxmanage internalcommands createrawvmdk -filename vd_filename.vmdk -rawdisk device_name
```

where `vd_filename` is the name you want to give the virtual disk and `device_name` is the device name obtained from the output of the `lsblk` command.

Use the `chown` command to change the created file’s owner to the currently logged in user:

```\$ sudo chown user:user vd_filename.vmdk
```

where `user` is the currently logged in user.

To attach the drive to the virtual machine, go back to the VirtualBox main menu, select the virtual machine you created previously, and click Storage. Click the ‘+’ sign next to Controller: IDE and when prompted click Choose existing disk and select the virtual drive you just created.

Go back to the VirtualBox main form and click Start to start the virtual machine. It should boot from the USB flash drive just as if you had restarted the system and selected it from the boot menu.

rtl-sdr.com

DuWayne's Place

Computers, Electronics, and Amateur Radio from KC3XM

QRP HomeBuilder - QRPHB -

Computers, Electronics, and Amateur Radio from KC3XM

Open Emitter

Computers, Electronics, and Amateur Radio from KC3XM

Ripples in the Ether

Emanations from Amateur Radio Station NT7S

m0xpd's 'Shack Nasties'

Computers, Electronics, and Amateur Radio from KC3XM