!pr3
Short Binary-to-Decimal Conversion in 65802...........Bob S-C

Since the 65802 supports 16-bit registers, it is possible to write a very tiny loop that will convert 16-bit binary numbers to four- or five-digit decimal values.  Jim Poponoe called today and suggested the idea to me.

The idea is to count down the binary number in binary mode while incrementing a four-digit decimal value in the A-register.  It certainly isn't very fast, but it is small.

The two programs below illustrate the technique.  CONV.1 converts a two-byte value at $0000 (and $0001) and stores the four-digit result in $0002 (and $0003).  CONV.1 goes one step further, handling a fifth digit which is stored in $0004.

You could use CONV.1 inside the CATALOG command to convert volume numbers and sector counts.  It is probably shorter than the existing code.  Since the numbers converted are less than 500, the maximum time is still less than half a millisecond.

Lines 1080 and 1090 put the 65802 into "native" mode, so that we can use the 16-bit features.  Lines 1210,1220 put the 65802 back into 6502 "emulation" mode, since the subroutine was written under the assumption that the caller would be in emulation mode.  If you plan to use the subroutine within a program that runs entirely in native mode, you could leave these four lines out.  If you plan to call it from both native mode and emulation mode, you need to save the E status and restore it at the end.  You can do that like this:

       CONV.1 CLC      ENTER NATIVE MODE
              XCE
              PHP      SAVE CALLER'S MODE (IN C-BIT)
               .
               .
               .
              PLP      GET CALLER'S MODE
              XCE      RESTORE CALLER'S MODE
              RTS

Line 1100 clears both the X- and M-bits, so that all 16-bit features are on.  Note that when either of these bits are cleared, immediate-mode operands are two bytes long.  The assembler doesn't keep track of the state of these two bits, because it would be impossible in the general case without a complete flow analysis of the program.  It is up to the programmer to tell the assembler whether to assemble one- or two-byte immediate operands.  You do this in S-C Macro Assembler by using a double pound-sign notation, as in lines 1110 and 1160.

Line 1110 loads a full 16-bit value zero into the A-register.  Line 1120 loads the 16-bit value from location $0000 and $0001.  the low byte of the value is in $0000, and the high byte in $0001.  If all 16-bits of this value are zero, line 1130 will branch around the conversion loop.  If not, it will not branch.

Line 1140 sets the decimal mode, which affects only the ADC and SBC instructions.  Line 1190 turns it back to binary.  If you use the PHP and PLP steps shown above in the discussion about native and emulation modes, you could leave out the CLD in line 1190:  the PHP would restore the D-bit properly.

The loop in lines 1160-1180 adds one to the A-register and subtracts one from the X-register, until the X-register reaches zero.  Since we are in decimal mode, the A-register counts up in BCD format.  The largest number the loop can handle correctly is 9999 decimal ($270F).  Larger values will not even have the correct lower four digits, since CARRY gets set when 9999 is incremented.

After the loop finishes, line 1200 stores the result low-byte- first at $0002 and $0003.

CONV.2 is almost identical to CONV.1, on purpose.  There are five new lines of code, at lines 1330, 1390-1410, and 1480.  We use the Y-register to keep track of the fifth digit, so that we can convert numbers larger than 9999.  Line 1330 sets Y=0.  Line 1390 checks for the carry that occurs when 9999 is incremented.  If there is no carry, the loop is the same as in CONV.1.  If there is a carry, line 1400 increments the Y-register and line 1410 clears carry.  (We could save one byte at the expense of slower operation by including the CLC on line 1370 inside the conversion loop.)

Line 1480 stores the fifth digit in location $0004.  I put it after the switch back to emulation mode, since I only wanted to store one byte.

I timed these subroutines by counting cycles, as shown in the comments in lines 1040,1050 and 1250,1260.  In the process I was suprised to learn that the DEX opcode still takes only two cycles, even when in 16-bit mode.  Of course, the same goes for INX, DEY, INY.  It is also true of ASL, LSR, ROL, ROR, INC, and DEC when the operand is the A-register.
1
