Classic Computer Magazine Archive COMPUTE! ISSUE 11 / APRIL 1981 / PAGE 66

A Floating-Point Binary To BCD Routine

Marvin L. DeJong
Department of Mathematics-Physics
The School of the Ozarks
Pt. Lookout, MO 65726

Introduction

A previous issue of COMPUTE! carried a BCD to Floating-Point Binary Routine that can be used to convert a series of decimal digits and a decimal exponent to a binary number in a floating-point format. The purpose of such a routine is to enable the user to perform floating­point arithmetic. The program described in this article performs the reverse operation; that is, it converts a floating-point binary number to a decimal number and a decimal exponent. With these two routines and an Am9511 Arithmetic Processing Unit one can do most of the functions found on scientific calculators. I hope to pro­vide a few simple arithmetic routines in the near future. In the meanwhile, you can amuse yourself by converting numbers to floating­point binary numbers and then back to decimal numbers.

Hindsight

The BCD to floating-point binary routine described previously used a divide-by-ten routine that was part of the main program. With my excellent hindsight I now realize that the divide-by-ten routine should have been written as a subroutine, to be called by both the BCD to floating-point binary routine and the binary to decimal routine described here. So my first task was to rewrite the divide-by-ten routine as a subroutine. I also discovered that the divide-by-ten routine described in the previous article did not give sufficient precision. In any case, the divide-by-ten routine was completely revised and appears in Listing 1 in this article. It uses the location $0000, called OVFLO, as a "guard" byte to give the necessary precision. It actually starts at $0EC5, but our listing starts at $0EBF to indiciate a few changes that must be made in the original listing to insert the subroutine.

Listing 1. A New Divide-by-Ten Routine.
$OEBF 20 C5 0E ONCMOR JSR DIVTEN Jump to divide-by-ten subroutine.
0EC2 B8 CLV Force a jump around the routine.
0EC3 50 51 BVC ARND The new subroutine is inserted here. Clear accumulator for use as a register. Do $28 = 40 bit divide. OVFLO will be used as "guard" byte.
0EC5 A9 00 DIVTEN LDA $00
0EC7 A0 28 LDY $28
0EC9 06 00 BRA ASL OVFLO
0ECB 26 04 ROL LSB
0ECD 26 03 ROL NLSB Roll one bit at a time into the accumulator which serves to hold the partial dividend.
0ECF 26 02 ROL NMSB
0ED1 26 01 ROL MSB
0ED3 2A ROL A Check to see if A is larger than the divisor, $0A = 10.
0ED4 C9 0A CMP $0A
0ED6 90 05 BCC BRB No. Decrease the bit counter.
0ED8 38 SEC Yes. Subtract divisor from A.
0ED9 E9 0A SBC $0A
0EDB E6 00 INC OVFLO Set a bit in the quotient.
0EDD 88 BRB DEY Decrease the bit counter.
0EDE D0 E9 BNE BRA
0EE0 C6 05 BRC DEC BEXP Division is finished, now normalize.
0EE2 06 00 ASL OVFLO For each shift left, decrease the binary exponent.
0EE4 26 04 ROL LSB
0EE6 26 03 ROL NLSB Rotate the mantissa left until a one is in the most-significant bit.
0EE8 26 02 ROL NMSB
0EEA 26 01 ROL MSB
0EEC 10 F2 BPL BRC
0EEE A5 00 LDA OVFLO If the most-significant bit in the guard byte is one, round up.
0EF0 10 12 BPL BRE
0EF2 38 SEC Add one.
0EF3 A2 04 LDX $04 X is byte counter.
0EF5 B5 00 BRD LDA ACC, X Get the LSB.
0EF7 69 00 ADC $00 Add the carry.
0EF9 95 00 STA ACC, X Result into mantissa.
0EFB CA DEX
0EFC DO F7 BNE BRD Back to complete addition.
0EFE 90 04 BCC BRE No carry from MSB so finish.
0F00 66 01 ROR MSB A carry, put in bit seven, and increase the binary exponent.
0F02 E6 05 INC BEXP
0F04 A9 00 BRE LDA $00 Clear the OVFLO position, then get out.
0F06 85 00 STA OVFLO
0F08 60 RTS
. .
. . Empty memory locations here.
. .
0F16 A9 00 ARND LDA $00 Remainder of BCD-to-floating
. .
: : point routine is here.
Listing 2. Modifications to the BCD-to-Floating-Point Binary Routine.
$0E54 18 CLC Clear carry for addition.
0E55 A5 05 LDA BEXP Get binary exponent.
0E57 69 20 ADC $20 Add $20 = 32 to place binary
0E59 85 05 STA BEXP point properly.
0E5A EA NOP
0E5B EA NOP
$0D53 A0 20 BR7 LDY $20 Y will limit the number of left shifts to 32.
0D55 A5 01 BR10 LDA MSB
0D57 30 0D BMI BR11 If mantissa has a one in its most-significant bit, get out.
0D59 18 CLC
0D5A A2 04 LDX $04
0D5C 36 00 BR9 ROL ACC,X Shift accumulator left one bit.
0D5E CA DEX
0D5F D0 FB BNE BR9
0D61 C6 05 DEC BEXP Decrement binary exponent for each left shift.
OD63 88 DEY
0D64 D0 EF BNE BR10 No more than $20 = 32 bits shifted.
0D66 60 BR11 RTS That's it.

Some other minor modifications to the program are given in Listing 2. Although the BCD to Floating-Point Binary program will work without these changes, it will work better if you introduce the changes shown in Listing 2. The development of the program described in this article enabled me to find some places to improve the other routine. The modifications are simple and short.

The Conversion Routine

The program to convert a normalized floating-point binary number and its exponent to a BCD number and then output the result is given in Listing 3. A 32-bit binary to BCD conversion subroutine is called by this program and it is found in Listing 5. A flowchart of the entire process is given in Figure 1. The normalized floating-point binary mantissa is operated on by a series of "times ten" or "divide by ten" operations until the binary point is moved from the left of the mantissa to the right of the 32 bit mantissa. In other words, we multiply by ten or divide by ten until the binary exponent is 32. Then the mantissa represents an integer and can be converted to a BCD number using the subroutine in Listing 5. The algorithm for this latter routine is from Peatman's (John B)

Listing 3. A Floating-Point Binary to BCD Routine.
$0B00 A5 01 BEGIN LDA MSB Test MSB to see if mantissa is zero.
0B02 D0 0E BNE BRT If it is, print a zero and then get out. Clear display.
0B04 20 9B 0F JSR CLDISP
0B07 A9 30 LDA $30 Get ASCII zero.
0B09 20 A6 0F JSR OUTCH Jump to output subroutine.
0B0C A9 0D LDA $0D Get "carriage return."
0B0E 20 A6 0F JSR OUTCH Output it.
0B11 60 RTS Return to calling routine.
0B12 A9 00 BRT LDA $00 Clear OVFLO location.
0B14 85 00 STA OVFLO
0B16 A5 05 BRY LDA BEXP Is the binary exponent negative?
0B18 10 0B BPL BRZ No.
0B1A 20 00 0D JSR TENX Yes. Multiply by ten until the exponent is not negative.
0B1D 20 30 0D JSR NORM
0B20 C6 17 DEC DEXP Decrement decimal exponent.
0B22 B8 CLV Force a jump.
0B23 50 Fl BVC BRY Repeat.
0B25 A5 05 BRZ LDA BEXP Compare the binary exponent to
0B27 C9 20 CMP $20 $20 = 32.
0B29 F0 48 BEQ BCD Equal. Convert binary to BCD.
0B2B 90 08 BCC BRX Less than.
0B2D 20 C5 0E JSR DIVTEN Greater than. Divide by ten until BEXP is less than 32.
0B30 E6 17 INC DEXP
0B32 B8 CLV Force a jump.
0B33 50 F0 BVC BRZ
0B35 A9 00 LDA $00 Clear OVFLO
0B37 85 00 STA OVFLO
0B39 20 00 0D BRW JSR TENX Multiply by ten.
0B3C 20 30 0D JSR NORM Then normalize.
0B3F C6 17 DEC DEXP Decrement decimal exponent.
0B41 A5 05 LDA BEXP Test binary exponent.
0B43 C9 20 CMP $20 Is it 32?
0B45 F0 2C BEQ BCD Yes.
0B47 90 F0 BCC BRW It's less than 32 so multiply by 10.
0B49 20 C5 0E JSR DIVTEN It's greater than 32 so divide.
0B4C E6 17 INC DEXP Increment decimal exponent.
0B4E A5 05 BRU LDA BEXP Test binary exponent.
0B50 C9 20 CMP $20 Compare with 32.
0B52 F0 0F BEQ BRV Shift mantissa right until exponent
0B54 46 01 LSR MSB is 32.
0B56 66 02 ROR NMSB
0B58 66 03 ROR NLSB
0B5A 66 04 ROR LSB
0B5C 66 0B ROR TEMP Least-significant bit into TEMP.
0B5E E6 05 INC BEXP Increment exponent for each shift
0B60 B8 CLV right.
0B61 50 EB BVC BRU
0B63 A5 0B BRV LDA TEMP Test to see if we need to round
0B65 10 0C BPL BCD up. No.
0B67 38 SEC Yes. Add one to mantissa.
0B68 A2 04 LDX $04
0B6A B5 00 BRS LDA ACC,X
0B6C 69 00 ADC $00
0B6E 95 00 STA ACC,X
0B70 CA DEX
0B71 D0 F7 BNE BRS
0B 73 20 67 0D BCD JSR CONVD Jump to 32 bit binary-to-BCD routine.
0B76 A0 04 BRM LDY $04 Rotate BCD accumulator right until non-significant zeros are shifted out or DEXP is zero, whichever comes first.
0B78 A2 04 BRP LDX $04
0B7A 18 CLC
0B7B 76 20 BRQ ROR BCDN.X
0B7D CA DEX
0B7E 10 FB BPL BRQ
0B80 88 DEY
0B81 D0 F5 BNE BRP
0B83 E6 17 INC DEXP Increment exponent for each shift right. Get out when DEXP = 0.
0B85 F0 06 BEQ BRO
0B87 A5 20 LDA LBCDN Has a non-zero digit been shifted into the least-significant place?
0B89 29 0F AND $0F
0B8B F0 E9 BEQ BRM No. Shift another digit.
0B8D EA BRO NOP Oops. These NOPs cover an earlier mistake.
0B8E EA NOP
0B8F EA NOP
0B90 EA NOP
0B91 EA NOP
0B92 20 9B 0F JSR CLDISP This routine simply clears the AIM 65 20-character display.
0B95 A5 07 LDA MFLAG
0B97 F0 05 BEQ BRN If the sign of the number is minus, output a minus sign first.
0B99 A9 2D LDA $2D
0B9B 20 A6 0F JSR OUTCH ASCII " - " = $2D. Output character.
0B9E A9 0B BRN LDA $0B Set digit counter to eleven.
0BA0 85 0B STA TEMP
0BA2 A0 04 BRI LDY $04 Rotate BCD accumulator left to output most-significant digits first. But first bypass zeros.
0BA4 18 BRH CLC
0BA5 A2 FB LDX $FB
0BA7 36 25 BRG ROL BCDN
0BA9 E8 INX
0BAA D0 FB BNE BRG
0BAC 26 00 ROL OVFLO Rotate digit into OVFLO.
0BAE 88 DEY
0BAF D0 F3 BNE BRH
0BB1 C6 0B DEC TEMP Decrement digit counter.
0BB3 A5 00 LDA OVFLO Is the rotated digit zero?
0BB5 F0 Eb BEQ BRI Yes. Rotate again.
0BB7 18 BRX CLC Convert digit to ASCII and output it.
0BB8 69 30 ADC $30
0BBA 20 A6 0F JSR OUTCH
0BBD A9 00 LDA $00 Clear OVFLO for next digit.
0BBF 85 00 STA OVFLO
0BC1 A0 04 LDY $04 Output the remaining digits.
0BC3 18 BRL CLC
0BC4 A2 $FB LDX $FB
0BC6 36 25 BRJ ROL BCDN,X Rotate a digit at a time into
0BC8 E8 INX OVFLO, then output it. One digit is four bits or one nibble.
0BC9 D0 FB BNE BRJ
0BCB 26 00 ROL OVFLO
0BCD 88 DEY
0BCE D0 F3 BNE BRL
0BD0 A5 00 LDA OVFLO Get digit.
0BD2 C6 0B DEC TEMP Decrement digit counter.
0BD4 D0 E1 BNE BRX
0BD6 A5 17 LDA DEXP Is the decimal exponent zero?
0BD8 F0 48 BEQ ARND Yes. No need to output exponent.
0BDA A9 2E LDA $2E Get ASCII decimal point.
0BDC 20 A6 2E JSR OUTCH Output it.
0BDF A9 45 LDA $45 Get ASCII "E".
0BE1 20 A6 0F JSR OUTCH
0BE4 A5 17 LDA DEXP Is the decimal exponent plus?
0BE6 10 0D BPL THERE Yes.
0BE8 A9 2D LDA $2D No. Output ASCII " - "
0BEA 20 A6 0F JSR OUTCH
0BED A5 17 LDA DEXP It's minus, so complement it and add one to form the twos complement.
0BEF 49 FF EOR $FF
0BF1 85 17 STA DEXP
0BF3 E6 17 INC DEXP
0BF5 A9 00 THERE LDA $00 Clear OVFLO.
0BF7 85 00 STA OVFLO
0BF9 F8 SED Convert exponent to BCD.
0BFA A0 08 LDY $08
0BFC 26 17 BR1 ROL DEXP
0BFE A5 00 LDA OVFLO
$0C00 65 00 ADC OVFLO
0C02 85 00 STA OVFLO
0C04 88 DEY
0C05 D0 F5 BNE BR1
0C07 D8 CLD
0C08 18 CLC
0C09 A5 00 LDA OVFLO Get BCD exponent.
0C0B 29 F0 AND $F0 $Mask low-order nibble (digit).
0COD F0 09 BEQ BR2
0C0F 6A ROR A Rotate nibble to the right.
0C10 6A ROR A
0C11 6A ROR A
0C12 6A ROR A
0C13 69 30 ADC $30 Convert to ASCII.
0C15 20 A6 OF JSR OUTCH Output the most-significant digit.
0C18 A5 00 BR2 LDA OVFLO Get the least-significant digit.
0C1A 29 0F AND $0F Mast the high nibble.
0C1C 18 CLC
0C1D 69 30 ADC $30 Convert to ASCII.
0C1F 20 A6 0F JSR OUTCH
0C22 A9 0D ARND LDA $0D Get an ASCII carriage return.
0C24 20 A6 0F JSR OUTCH
0C27 60 RTS All finished.

Microprocessor Based Design

(McGraw-Hill).

Of course, each time the binary number is multiplied by ten or divided by ten the decimal exponent is adjusted. Thus, we are left with a BCD number in locations $0020 - $0024 (five locations for ten digits) and a decimal exponent in $0017. The rest of the routine is largely processing required to give a reasonable output format. Since we don't want to print a group of non-significant zeros, the BCD number is rotated right until all the zeros are shifted out or the decimal exponent is zero, whichever comes first.

Next the routine starts examining the BCD number from the left and skips any leading zeros. Thus, the first non-zero digit is the first digit printed. Of course, if the number is minus (a non-zero result in location $0007) a minus sign is printed. Next the decimal point is printed, and finally the exponent is printed in the form "E XX." Thus, the format chosen always has the decimal point to the right of the significant digits, 3148159. E-6 for example. If you want scientific notation for non-integer results you can modify the output routine. It's simply a matter of moving the decimal point. The flowchart and the comments should allow you to understand and modify the code.

Figure 1. Flowchart of the Floating-Point Binary to BCS Routine.
Listing 4. Subroutine OUTCH For the AIM 65.
$OFA6 20 00 F0 OUTCH JSR PRINT AIM 65 monitor subroutine.
OFA9 20 72 0F JSR MODIFY See previous article in COMPUTE!
0FAC 20 60 0F JSR DISPLAY See previous article in COMPUTE!
0FAF 60 RTS RTS
Listing 5. A 32 Bit Binary-to-BCD Subroutine.
$0D67 A2 05 CONVD LDX $05 Clear BCD accumulator.
0D69 A9 00 LDA $00
0D6B 95 20 BRM STA BCDA,X Zeros into BCD accumulator.
0D6D CA DEX
0D6E 10 FB BPL BRM
0D70 F8 SED Decimal mode for add.
0D71 A0 20 LDY $20 Y has number of bits to be converted. Rotate binary number into carry.
0D73 06 04 BRN ASL LSB
0D75 26 03 ROL NLSB
0D77 26 02 ROL NMSB
0D79 26 01 ROL MSB
D7B A2 FB LDX $FB X will control a five byte addition. Get least-significant byte of the BCD accumulator, add is to itself, then store.
0D7D B5 25 BRO LDA BCDA,X
0D7F 75 25 ADC BCDA,X
0D81 95 25 STA BCDA,X
0D83 E8 INX Repeat until all five bytes have been added.
0D84 D0 F7 BNE BRO
0D86 88 DEY Get another bit from the binary number.
0D87 D0 EA BNE BRN
0D89 D8 CLD Back to binary mode.
0D8A 60 RTS And back to the program.