Multiply bug

From C64-Wiki
Jump to navigationJump to search

Most Commodore BASIC interpreters have a bug in the floating point multiply routine. The bug can be demonstrated with the following BASIC program:

10 X = 1 + 255/2↑31
20 PRINT X * 1 - X
30 PRINT 1 * X - X

All Commodore BASICs, except the BASIC 7.0 found in the Commodore 128, produce the following incorrect output:

 0
-5.91389835E-08

Only BASIC 7.0 produces the correct output:

0
0

This bug is not due to the roundoff error intrinsic to floating point arithmetic, because multiplying by 1 does not introduce rounding.

Cause of the bug[edit]

The 6502 does not have a multiply instruction. The floating point multiply must be done in software, entirely from adds, subtracts and shifts. The routine MLTPLY, located in the C64 BASIC at $BA59, performs part of the floating point multiply. It multiplies the mantissa in the "ARG" area ($69-$6E, also called floating point accumulator 2) by the byte in the A register and adds the product to the scratch area at $26-$29, spilling overflow bits into $70 so the final product can be rounded. The byte in A is derived from the floating point accumulator, which is the second operand in a BASIC multiply expression.

MLTPLY has an optimization for speed. If the byte in the A register is zero, it will bypass its multiply loop and jump instead to MULSHF ($B983). MULSHF is not exclusive to the multiply routine; it is shared with the add routine, which uses a shift to equalize the exponents of the addends. MULSHF entered from the multiply is always supposed to have A equal to zero, and shift by exactly eight bits. But it uses the ADC instruction carelessly, not making sure that the carry flag is in the proper state, and so it can sometimes shift by nine bits.

A patch[edit]

BASIC 7.0 avoids the bug by bypassing MLTPLY's zero byte optimization at certain points; it calls MLTPL1 instead. MLTPL1 in the C64 BASIC is located at $BA5E.

The C64 BASIC can be patched to remove the bug, implementing the BASIC 7.0 fix, by changing the following bytes. The bytes at offset $1A40 in the ROM image read as follows:

00001a40: 59ba a565 2059 baa5 6420 59ba a563 2059  Y..e Y..d Y..c Y

To patch the bug, change the first and last instances of $59 to $5E:

00001a40: 5eba a565 2059 baa5 6420 59ba a563 205e  Y..e Y..d Y..c Y