**MACHINE LANGUAGE**

Jim Butterfield, Associate Editor

**Decimal Mode**

**Part 2**

Decimal mode is quite useful in arithmetic programming such as game scoring and simple accounting. It has other uses, too—for example, in converting binary numbers to decimal for output. It also has certain bugs, pitfalls, and conventions.

**Bugs And Pitfalls**

Don't depend on the Zero and Negative (Z and N) flags immediately following a decimal addition (ADC) and subtraction (SBC). If you really need them, perform a data transfer (for example, TAX) to insure the flags are set correctly. The Carry flag is correct and has its usual meaning after the addition or subtraction.

Remember that decimal mode uses only the ADC and SBC instructions. The increment and decrement instructions (INX, INY, INC, DEX, DEY, DEC) behave in binary; and comparisons (CMP, CPX, CPY) are based as usual on binary values.

Programmers using machines with interrupt sequences must be careful of decimal mode. The interrupt can clear decimal mode with CLD (Clear Decimal); when the interrupt code finishes with RTI, the status register will be restored and decimal mode will be reinstated if it was in effect before. On Commodore machines, the interrupt sequences do not include a CLD instruction; in this case, the interrupt should be locked out using a SEI (Set Interrupt Disable) before going into decimal mode.

The VIC-20 and Commodore 64 have a useful feature: Registers may be preset before a SYS call. Addresses $030C, $030D, $030E, and $030F (decimal 780 to 783) contain values that will be transferred to registers A, X, Y, and the status register at the time of a SYS. When the machine language program returns to BASIC, these same addresses will contain the contents of the respective register. In other words, we could POKE 780,65 followed by a SYS; and the machine language program would start running with a value of $41 (decimal 65) in the A register.

What does this mean to decimal mode? Here's the possible danger: If the wrong value is contained in address 783, it will be transferred to the status register at the time of a SYS. An uncontrolled value might set decimal mode, or even worse, set the interrupt disable flag. To make things worse, these flags will not be restored when we return to BASIC. They will be neatly stored in 783, but BASIC will resume with the flags in an unworkable state. There goes BASIC.

It's probably wise to leave address 783 alone. If it worries you, POKE 783, 0 before giving a SYS command.

**Conventions**

We can handle fractions in decimal arithmetic. It's best to do this by using an "assumed decimal point." In other words, we will work dollar values as an integer number of pennies, and kilometers as integer values of meters. It's easier to stick in the decimal point at output time.

Negative numbers are a little tricky. We can use a scheme similar to that in binary numbers: That is, the "high bit" of a number represents the sign. This, however, splits positive and negative unevenly: A two-byte number will range from a low of –2000 (value 8000) up to + 7999. If you use this method, don't forget that the N flag isn't dependable after an addition or subtraction and that you'll need to take an extra step to test the flag.

A better technique is called "tens complement" and it's been used in many household devices such as counters on tape recorders. We understand that a reading of 9994 really means–6. If we want to use this technique, we might choose to try to split positive and negative more evenly, so that a two-byte number would range from –5000 to + 4999. In this case, we must remember not to use the N bit, but instead compare the high byte to 50 hex. If it is higher, the number is negative.

If "tens complement" is used, remember to invert a negative number at the time of printing. I find that the easiest way to do this is to subtract it from 0000 so that 9993 becomes 0007.

**Multiplication**

To multiply two decimal numbers we are almost forced to resort to repeated addition. As we go from one decimal digit to the next, we must "shift" either the multiplier or the product: This is a binary shift-four-places. It's awkward and we can quickly see why binary is preferred.

There's an elegant way to multiply a decimal number by a binary value, or by a fixed amount. We can use what I call a "decimal shift."

A binary shift multiplies a number by two. We can do the same thing with a decimal number by adding it to itself. Thus, to multiply by two we add the number to itself (in decimal mode). To multiply by four we multiply by two, twice. To multiply by five, we multiply by four and add the original number.

**A Multiplication Example**

We'll have the computer (PET, VIC, or 64) output a table of multiples of the number 5. (Two would be too easy.)

;set value to one033C A2 01 LDX #$01033E 8E B0 03 STX LOW0341 CA DEX0342 8E B1 03 STX MED0345 8E B2 03 STX HIGH0348 8E B6 03 STX COUNT; copy the number034B A0 02 LOOP LDY #$02034D B9 B0 03 CP LDA LOW, Y0350 99 B3 03 STA COPY, Y0353 88 DEY0354 10 F7 BPL CP;multiply by four0356 A2 02 LDX #$020358 18 FP CLC0359 A0 FD LDY #$FD035B 78 SEI035C F8 SED035D B9 B3 02 TP LDA HIGH-255, Y0360 79 B3 02 ADC HIGH-255, Y0363 99 B3 02 STA HIGH-255, Y0366 C8 INY0367 D0 F4 BNE TP0369 CA DEX036A D0 EC BNE FP;add original value036C A0 FD LDY #$FD036E 18 CLC036F B9 B3 02 AP LDA HIGH-255, Y0372 79 B6 02 ADC COPY-253, Y0375 99 B3 02 STA HIGH-255, Y0378 C8 INY0379 D0 F4 BNE AP037B D8 CLD037C 58 CLI;print the number037D A0 02 LDY #$02037F B9 B0 03 LP LDA LOW, Y0382 4A LSR A0383 4A LSR A0384 4A LSR A0385 4A LSR A0386 09 30 ORA #$300388 20 D2 FF JSR $FFD2038B B9 B0 03 LDA LOW, Y039E 29 0F AND #$0F0390 09 30 ORA #$300392 20 D2 FF JSR $FFD20395 88 DEY0396 10 E7 BPL LP;print RETURN and loop0398 A9 0D LDA #$0D039A 20 D2 FF JSR $FFD2039D EE B6 03 INC COUNT03A0 AE B6 03 LDX COUNT03A3 E0 07 CPX #$0803A5 D0 A4 BNE LOOP03A7 60 RTS

Note the peculiar addressing in lines 035D to 0363 and again in 036F to 0375. We need to have a positive-incrementing index (in this case Y), since we must start our addition at the low-order value, LOW, and work upwards. We cannot use the obvious method of starting at zero and testing to see when we have done all three values, because we want the carry flag to be preserved; CPY (Compare Y) would destroy the previous value of the carry and our addition wouldn't work right.

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

**Multiples Of 5**

100 DATA 162, 1, 142, 176, 3, 202, 142, 177, 3110 DATA 142, 178, 3, 142, 182, 3, 160, 2120 DATA 185, 176, 3, 153, 179, 3, 136, 16, 247130 DATA 162, 2, 24, 160, 253, 120, 248, 185, 179, 2140 DATA 121, 179, 2, 153, 179, 2, 200, 208, 244, 202150 DATA 208, 236, 160, 253, 24, 185, 179, 2, 121, 182, 2160 DATA 153, 179, 2, 200, 208, 244, 216, 88, 160, 2170 DATA 185, 176, 3, 74, 74, 74, 74, 9, 48, 32, 21 0, 255180 DATA 185, 176, 3, 41, 15, 9, 48, 32, 210, 255, 136, 16190 DATA 231, 169, 13, 32, 210, 255, 238, 182, 3, 174, 182, 3200 DATA 224, 8, 208, 164, 96300 FOR J = 828 TO 935310 READ X : T = T + X320 POKE J, X330 NEXT J340 IF T<>13479 THEN STOP350 SYS 828