Classic Computer Magazine Archive COMPUTE! ISSUE 50 / JULY 1984 / PAGE 112

MACHINE LANGUAGE

Jim Butterfield, Associate Editor

Decimal Mode

Part 1

The 6502 has an option which affects only the add (ADC) and subtract (SBC) instructions: decimal mode.

Decimal mode is invoked with the Set Decimal (SED) command, and canceled with Clear Decimal (CLD). It may be affected by stack activities that pull the status register—PLP for Pull Processor status, and RTI—but this is unusual. In most computer environments you can assume that decimal mode is not in force when your program is invoked; but if you're not sure, it won't hurt to give a CLD.

Decimal mode is intended to help with certain types of numbers: Binary Coded Decimal (BCD) numbers. You might want to use this type of number system when the values are used mostly for input and output with little calculation involved.

Binary numbers—the computer's usual numeric values—are good for advanced calculations. Multiplication and division are easy to do in binary, and more advanced calculations can readily be developed. The only problem with binary numbers is this: They must be converted to decimal at the time of input or output.

Decimal numbers, or more accurately BCD numbers, are easy to input and output since they are held in the same decimal notation as was entered or will be seen by the user. With decimal mode, we may add or subtract these numbers without converting them to binary. But if we want to do more advanced mathematics, we'll certainly go to binary.

Accounting programs often use decimal mode. Similarly, many games keep scores in decimal format, since the only activities are adding points as they are scored and displaying the results.

What Is BCD?

The easiest way to describe a number held in Binary Coded Decimal is this: When you display it in hexadecimal format, you see the correct decimal value. Let's explain this with a few examples.

A value of 9 is held within a byte as binary 00001001. This is true whether you are using binary or BCD numbering. If we print the contents of this byte in hexadecimal, it is displayed as 09. Now, this not only represents the value nine, it looks like nine.

If we are in binary mode and add one to the above value, we'll get 00001010. The value is ten but the number displays in hex as 0A. This doesn't look like ten to those of us who are not trained to read hex. Worse: If we add six, we'll get a value of 16, which prints as hex value 10. This doesn't look like 16—if we didn't know it was a hexadecimal number, we might think it was ten.

Let's go back to our original value of nine, but switch to decimal mode. If we add one, using the ADC instruction, we'll end up with binary 00010000. We know that the value must represent ten, and when we print the hexadecimal it shows up as 10—which looks like ten. We must ignore the usual binary rules, which would tell us that binary 00010000 is equivalent to decimal 16. In BCD, this binary number has a value of 10. If we add a six in decimal mode, we'll get 00010110 which has a value of 16 and prints out as hexadecimal 16.

We've decided to use the bits in a different way. The four high bits—the high nybble, as it's sometimes called—represent a tens digit; the four low bits, or low nybble, represent units. Each nybble may have a value from 0 to 9, but the six highest combinations corresponding to hex A, B, C, D, E, and F will never be used.

This makes BCD less efficient than binary for storing numbers. The highest BCD number that we can store within a single byte is 99, as compared to 255 for binary. We can use several bytes together to hold larger numbers, but BCD always holds less: A two-byte BCD number can go from 0000 to 9999, compared to a two-byte unsigned binary number which can range from 0 to 65535.

But it's convenient. When we wish to output such a number, we extract each digit, convert it to ASCII with an ORA #$30, and print it. (We get the left digit by using four LSR instructions, and the right digit with AND #$0F.) An equivalent binary number would need a divide-by-ten routine before it could be output.

Similarly, input is a snap. As each ASCII digit arrives, it has its high bits stripped (with AND #$0F) and gets packed together with another digit to generate the two-to-a-byte BCD value.

An Example

Here's a sample program to show the power of BCD numbers and ease of programming with them. We'll have the computer (PET, VIC, or 64) output a table of multiples of the number 142857. This is a favorite peculiar number of mine; you'll see why when we print the table.

              ;set value to zero
033C A2 00    LDX #$00
033E 8E 90 03     STX LOW
0341 8E 91 03     STX MED
0344 8E 92 03     STX HIGH
              ;do the addition
0347 18       LOOP CLC
0348 78            SEI
0349 F8            SED
034A AD 90 03      LDA LOW
034D 69 57         ADC #$57
034F 8D 90 03      STA LOW
0352 Ad 91 03      LDA MED
0355 69 28         ADC #$28
0357 8D 91 03      STA MED
035a AD 92 03      LDA HIGH
035D 69 14         ADC #$14
0362 D8            CLD
0363 58            CLI
              ;print the number
0364 A0 02         LDY #$02
0366 B9 90 03 LP   LDA LOW, Y
0369 4A            LSR A
036A 4A            LSR A
036B 4A            LSR A
036C 4A            LSR A
036D 09 30         ORA #$30
036F 20 D2 FF      JSR $FFD2
0372 B9 90 03      LDA LOW,Y
0375 29 0F         AND #$0F
0377 09 30         ORA #$30
0379 20 D2 FF      JSR $FFD2
037C 88            DEY
037D 10 E7         BPL LP
              ;print RETURN and loop
0381 A9 0D       LDA #$0D
0381 20 D2 FF    JSR $FFD2
0384 E8          INX
0385 E0 07       CPX #$07
0389 60          RTS

Note that we hold the value we are calculating in three bytes; called LOW, MED, and HIGH; we add starting at the low byte and working up. The Carry flag works the same way as is usual for addition. While we're in decimal mode, we lock out the interrupt so that the interrupt routines won't do their arithmetic in the wrong mode. The addition sequences could have been written as a loop; for the sake of clarity, it was done using "straight line" coding.

For printing, we start from the high byte, of course. The output routine for BCD is simple compared to what we would need to do with binary values.

If you'd rather enter the program from BASIC, here's the same program in DATA statements. It will work on all Commodore machines.

100 DATA 162, 0, 142, 144, 3, 142, 145, 3
110 DATA 142, 146, 3, 24, 120, 248, 173, 144, 3
120 DATA 105, 87, 141, 144, 3, 173, 145, 3
130 DATA 105, 40, 141, 145, 3, 173, 146, 3
140 DATA 105, 20, 141, 146, 3, 216, 88, 160, 2
150 DATA 185, 144, 3, 74, 74, 74, 74, 9, 48
160 DATA 32, 210, 255, 185, 144, 3, 41, 15, 9, 48
170 DATA 32, 210, 255, 136, 16, 231, 169, 13
180 DATA 32, 210, 255, 232, 224, 7, 208, 190, 96
200 FOR J = 828 TO 905
210 READ X: T = T + X
220 POKE J, X
230 NEXT J
240 SYS 828

You might like to examine the output of the program to see what's so special about the first seven multiples of the number 142857.

Next month, we'll discuss special features and wrinkles of decimal mode.