BCD N
U
The rather cryptic title of this article can be translated as "BinaryCoded Decimal and You." Last month we discussed some routines for interconverting strings of numeric ASCII characters and their binary representations as integers.
I
know
you're eager to dive into
the sample program for today,
but I'm going to hold you back
a little longer.
the sample program for today,
but I'm going to hold you back
a little longer.
This time we tackle another commonly used method for storing numbers in computers: binary-coded decimal, or BCD for short. After I explain the BCD representation, well see how to change an ASCII string into a BCD storage format. I also have some examples of how to do arithmetic with numbers stored in BCD from, and some traps you can fall into if you don't keep your wits about you.
Binary-Coded
Decimal
Look at the bit patterns for digits 0-9 shown in
Table 1. Notice that they range form 0000 to 1001. The point here is
that we need only four bits to represent any one of the ten decimal
digits. You no doubt recall that the standard byte contains a grand
total of eight bits. If we think of subdividing a byte, we could make a
duplex with each unit containing four bits. A 4-bit unit is sometimes
referred to as a "nybble" (a small byte-get it?). I've seen it spelled
more conventionally as nibble, but I'll use the "y" so the noncomputer
whizzes who read this will think I'm talking about something really
obscure and hence important.Since we can store the binary representation of any one decimal digit in each nybble, the largest value that could be stored in a single byte this way is 99. This corresponds to a bit pattern of 1001 in each nybble; the entire storage contains 10011001. This two-digit-per-byte data storage method is the infamous binarycoded decimal.
There are two ways to interpret a bit pattern of 10011001. In pure hexadecimal, it is $99, which corresponds to decimal 153. But if we think of it as two decimal digits, that bit pattern means decimal 99. We need some way to tell the computer which meaning we have in mind at any given time.
Doing arithmetic on BCD numbers is different from processing binary numbers also. In binary, adding 1 to a byte containing the value 00001001 ($09) produces the value 00001010 ($0A). In BCD, adding 1 to 00001001 (09) would result in 00010000 (10). Similarly, adding 1 to 10011001 in hex terms produces 10011010 ($99 to $9A). But in BCD we should wind up with 00000000 in this byte and the carry flag set to indicate that a higher order byte must be incremented. This is a fancy way of saying that 99 plus 1 equals 100.
The 6502 microprocessor in the Atari 8-bit computers can perform either decimal or binary arithmetic, thereby handling either of the two conditions from the previous paragraph. Bit 3 in the processor status register controls whether decimal mode (bit set) or binary mode (bit cleared) is selected. So far, we've performed only binary arithmetic operations, so most of our programs have begun with a CLD (CLear Decimal mode) instruction. To choose decimal mode, use the SED (SEt Decimal mode) instruction. You will get very strange results if the decimal flag isn't set the way you think it is; so it's always a good idea to explicitly select the desired mode.
Actually, it's a little worse than "very strange." If you try to do things like print to the screen when the decimal flag is set, you can wind up in computer never-never land, with a coldstart being the only way back. Always clear decimal mode with the CLD instruction when you've finished your decimal arithmetic operations.
Interconverting
ASCII and BCD
Today's example is similar in format to last month's
discussion of how to interconnect ASCII and binary storage formats for
integers. Listing 1 contains two macros in MAC/65 format that should be
appended to your MACRO.LIB file using the line numbers shown.
Similarly, Listing 2 contains a pair of subroutines called by these
macros; append Listing 2 to your SUBS.LIB file.My MACRO.LIB file is now an even 100 single-density sectors long. If you're using a RAM disk for assemblies, this is only a minor nuisance. However, reading a file that large from a physical disk each time you do an assembly takes a long time, and it doesn't do your disk drive any good. You may want to think about splitting the MACRO.LIB file into several smaller library files, perhaps grouped logically by function. You can do this any way you like, and just .INCLUDE the ones you need for your current project. Be sure to keep the equates needed by the macros accessible (and unduplicated). In fact, you might just collect all the equates into a separate EQUATES.LIB file. I'll leave the details of the MACRO.LIB dissection to each of you.
The two new macros, and their corresponding subroutines, are named ASC2BCD and BCD2ASC. These complement the ASC2INT and INT2ASC routines from the previous Boot Camp. ASC2BCD takes a string of up to six numeric characters and converts it into a three-byte BCD number. Not surprisingly, BCD2ASC takes a three-byte BCD number and transforms it right back into a printable ASCII string. The macros themselves do some error checking and use parameters to handle ASCII strings and BCD numbers stored at any address, while their subroutine partners do most of the real work.
The
non-computer
whizzes who read this will think
I'm talking about something
really obscure and important.
whizzes who read this will think
I'm talking about something
really obscure and important.
ASCII
to BCD
We'll start at the beginning. Please turn your
attention to the ASC2BCD macro in Listing 1. ASC2BCD expects two
parameters, the address of the ASCII string to convert, and the address
where the resulting three-byte BCD number is to be stashed. An error
message appears if the number of parameters is not two (Lines
8130-8140).This macro begins just like the ASC2INT macro from last time. Lines 8160-8220 copy the characters from the input string at the address specified in parameter % 1 to a work address labeled ASCII. The ASCII address was defined in Listing 1 from last month as $0690. The input string must terminate with an end-of-line character ($9B). In our sample program today, the numeric string to convert is read from the keyboard using our INPUT macro, which automatically guarantees that an EOL character will be present.
Line 8230 calls the VALIDASC subroutine from last month, which makes sure that all the characters in the string are in fact digits in the range 0-9. If not, the carry flag is set in the subroutine to indicate an error. Line 8240 handles this condition by simply short-circuiting around the rest of the macro code. The main program that invoked this macro handles the error condition, as we'll see a little later. I don't have any provision for handling negative numbers.
Subroutine VALIDASC retains only the lower four bits from the ASCII character. That is, if you entered the digit "7" at the keyboard, the ASCII value is $37, and VALIDASC changes this back into a plain "7" after confirming that it is a legal entry.
After the conversion, the BCD number resides at a work location called NUM, defined as $0696 last month. Lines 8280-8350 copy the BCD result to the desired output address specified in parameter % 2.
After all this monkey business, we wind up with a string of characters at address ASCII which looks exactly like what we typed at the keyboard. Let's pretend we typed the number "7239." Our goal is to convert the input string, now typed in five bytes like this (showing both nybbles in each byte):
07 02 03 09 9B
into BCD format stored in three bytes like this:
00 72 39
Notice that this numeric storage format is different from the low-byte/high-byte format used for binary integers.
Line 8250 of Listing 1 calls the ASC2BCD subroutine in Listing 2 to handle the details of the conversion. Now please direct your attention to Listing 2.
First we need to know how many input digits to convert to BCD. When we get to today's example in Listing 3, you'll see that the value we need was stored in a work address labeled CHARCTR. The value in CHARCTR includes the EOL character, so it is one larger than the actual number of digits in the input number. Lines 4720-4740 of Listing 2 set up the Xregister as an offset for the characters for the BCD bytes. Here's the conversion plan.
We'll begin by zeroing the three bytes where the BCD result will be stored. Lines 4760-4790 handle this task. The conversion step will begin with the least significant (rightmost) digit in the entered ASCII string. This number becomes the low-order nybble in the least significant (rightmost) BCD byte (Lines 4810-4820).
If the ASCII string to be converted contains an odd number of digits, the highorder nybble of one of the the BCD bytes will remain zero. This should be apparent to you. Line 4830 in Listing 2 points to the next ASCII character, which is destined to go into the high-order nybble of the current BCD byte. Line 4840 checks to see if we've reached the end of the ASCII string yet. If not, fetch the contents of the next ASCII byte (Line 4850). Remember that we've already changed this from the original ASCII value to the value of the digit itself (e.g., $37 was changed to 7).
Lines 4860-4890 shift this number four bits to the left, thereby relocating it to the high-order nybble of the accumulator. Line 4900 combines the result with the low-order nybble from the previous ASCII digit, and the completed BCD byte (now containing two digits) is stored back where it belongs (Line 4910). Lines 4920-4960 check to see if were done with the ASCII string yet and loop back to continue if not.
This discussion is a little confusing. You might find it illuminating to use your debugger to trace through a stepwise processing of a sample input number after entering Listing 3, and see how the ASC2BCD subroutine does its thing.
I know you're eager to dive into the sample program for today, but I'm going to hold you back a little longer. The sample program goes through a bunch of BCD arithmetic examples, which we'll get to in a moment. But while the details of the ASCII-to-BCD conversion are fresh in your mind, I want to tackle the reverse process. Bear with me.
The
6502 microprocessor knows
how to do arithmetic on numbers
stored in both binary and
decimal modes. There are a
few differences.
how to do arithmetic on numbers
stored in both binary and
decimal modes. There are a
few differences.
BCD
to ASCII
I'm sure you can figure out what we must do to
change a number stored in BCD format into a printable ASCII string.
There are two basic steps. First, split the high and low nybbles of
each byte in the BCD number into separate bytes in the output string.
And second, convert the digits into their corresponding ASCII values.
As an additional cosmetic nicety, we'll also convert any leading zeros
to leading blanks.The BCD2ASC macro begins at line 8540 of Listing 1, and the complementary BCD2ASC subroutine starts at line 5150 of Listing 2. The macro again requires two parameters, the address of the BCD number to be converted and the address where the resulting ASCII string should be stored. Three bytes at address NUM and six bytes at address ASCII are again used as work locations. Lines 8580-8630 of the macro copy the contents of the BCD number into work location NUM. The subroutine BCD2ASC is then called. Lines 8650-8710 then copy the resulting string from address ASCII into the address specified in parameter %2. Lines 8720-8740 tack an EOL character on the end so the string can be printed.
This time the conversion proceeds from left to right (high order to low order). In the BCD2ASC subroutine, I've set aside one byte (called ZEROBLANK, defined in Line 5570 of Listing 2) to indicate whether a zero digit is to be represented as a zero ASCII character ($30) or as a blank ($20). ZEROBLANK is initially set to $20 in Lines 5160-5170 so as to print leading zeros as blanks. However, as soon as a non-zero digit is encountered, ZEROBLANK is set to $30 so that zeros in the middle of the number appear properly.
Line 5210 gets the first (leftmost) BCD digit, which is saved temporarily on the program stack (Line 5220). (The Xregister is used as an offset into the BCD number, and the Y-register as an offset into the ASCII string.) The high nybble is moved into the low nybble with a series of four right shifts; this is the opposite of the four ASLs we used in the ASC2BCD process. If the result is a zero, Lines 5290-5300 store the current value of ZEROBLANK into the next position in the output string. If the digit is not a zero, Lines 5330-5360 convert the digit to ASCII by adding $30 to it, store the result in the output string, and set the value of ZEROBLANK to an ASCII zero.
Lines 5380-5400 point to the next output character, retrieve the BCD byte, and strip off the four most significant bits. This leaves just the low nybble, which is the second of the two digits in the BCD byte. Then the same activities are performed as for the first digit, depending on whether the digit is a zero or not (Lines 5410-5440). After processing all three BCD bytes, we wind up with a printable ASCII string. Voila.
BCD
Arithmetic
Now for the interesting part. The 6502
microprocessor knows how to do arithmetic on numbers stored in both
binary and decimal modes. There are a few differences you should keep
in mind, and Listing 3 will help you out.The program in Listing 3 asks you to enter a number up to six digits long, verifies that you entered only digits, converts the string of ASCII characters to a threebyte BCD number, and performs some representative arithmetic operations in both BCD and decimal mode. The results from each operation are printed on the screen in a little table. Let's walk though Listing 3 now.
Line 160 pulls in the macros from our library file. Be sure to change this statement if you are using a real disk drive instead of the D8: RAM disk, or if you segmented the MACRO.LIB file as I suggested earlier. Some work variables are defined in Lines 280-310. BCD is the home of the BCD number. CHARCTR contains the number of ASCII characters you entered (including the EOL character). INBUF is an input buffer for the number you enter, and OUTBUF is an output buffer for the printable ASCII result.
As usual, the executable code begins at address $5000. Lines 520-590 clear decimal mode (for now), clear the screen, prompt you to enter a number, store the number at INBUF, and store the number of characters you entered at CHARCTR. Lines 650-760 set up the column and row headings for the output table; the text strings to be printed are stored in Lines 2350-2480. Line 830 converts the input string in INBUF to BCD representation at address BCD. Well have to repeat this after each sample calculation to make sure the BCD number starts out the same way every time. If there's an error in the BCD conversion, the carry flag will be set and the program terminates due to Lines 840-850.
The program has four sample calculations: increment the lowest BCD byte; add decimal 25 to the BCD number; add hex 25 to the BCD number; and add the contents of the middle BCD byte to the whole BCD number. Each calculation is done in both binary and decimal modes. I suggest you try this program with several sample entries, to see what happens. Press return after the output appears to try another number. Four interesting numbers to try are 0, 1234, 999 and 7239. On the off chance that you don't really want to spend the time typing in the listings, I've included tables to the output you would see for each of these test cases (Tables 2-5).
The
simplest arithmetic operation
you can do in 6502 assembly
language is to increment the
contents of a byte. The opcode
for this is, of course, INC
you can do in 6502 assembly
language is to increment the
contents of a byte. The opcode
for this is, of course, INC
Incrementing
The first line of Table 2 shows that INC works just fine when the target number is 0, giving the expected result of 1 in each case. Table 3 shows that INC works fine for the number 1234 also. But wait! An input value of 999 gives the bizarre result of 99:. Something similar happens in Table 5 with 7239. How can this be?
Well, for Table 4, BCD + 2 contains "99;" which is incremented to "9A." Converting to ASCII gives two bytes, containing $39 (prints as a 9) and $3A (prints as a colon, :). Hmmmm. We really wanted the BCD number "99" to increment to "00;" setting the carry flag to indicate that the next higher order byte should also be incremented. It appears that the INC instruction has the same effect in decimal mode as it does in binary. Moral: Don't use INC to add 1 to a BCD number. Instead, go through the cumbersome motions of actually adding 1.
Adding
25
Okay, so let's add something to a BCD number. Lines
1210-1330 add an immediate value of 25 decimal to the BCD number you
entered in binary mode. Lines 1370-1490 do the same in decimal mode.
These routines use a subroutine called INCREMBCD (Lines 2630-2750 of
Listing 3) to handle the case where the carry flag is set after the
addition, so that the next higher order byte must be incremented (by
adding 1 to it, of course). This in itself might reset the carry flag,
so that the highest order BCD byte also has to be incremented. These
operations should make sense to you by now.Let's do it. Now look at the second output line in Tables 2-5 to see how our sample numbers respond. A problem is immediately apparent in Table 2. Adding 25 to 0 gave 19, not 25. Why? Well, the hex equivalent of 25 is $19. Last month we added decimal 25 (using an ADC #25 instruction) to a number stored as binary, went through the binary-to-ASCII conversion, and got the right answer. But we've scrambled our conventions here. We added a decimal number (stored internally as hex, of course) to a BCD number, using binary mode, and converted the presumed BCD result to ASCII for printing. It's not surprising that the wrong result shows up.
The same thing happens with all the other input numbers. The weird characters in Tables 3 and 4 appear again because the addition results have gone out of the legal 0-9 BCD range, into values which print as other ASCII characters. Check out your table of hex codes for ASCII characters if you don't believe me.
The
correct method for adding an
immediate value to a stored BCD
number is to use the desired
decimal digits for the immediate
number, but tell the computer that
it's a hex number.
immediate value to a stored BCD
number is to use the desired
decimal digits for the immediate
number, but tell the computer that
it's a hex number.
Adding
$25
The correct method for adding an immediate value to
a stored BCD number is to use the desired decimal digits for the
immediate number, but tell the computer that it's a hex number. That
is, to add decimal 25 to a BCD number, use an ADC #$25 instruction. The
third output line in each table shows that this approach does indeed
produce the result of adding 25. The initial entry of 1234 fortuitously
gives the correct answer in either binary or decimal modes (Table 3).
However, an entry of 999 works right only in decimal mode (Table 4). In
binary mode, the computer sets the carry flag when the byte's contents
exceed $FF, not $99 as it does in decimal.Adding
Two Stored Numbers
The final line of each table shows the result of
adding the middle byte of the BCD number to the entire number, just to
show how things work when you add together two stored values. Table 2
correctly shows no output for this line, since 0 + 0 = 0, which we
print as all blanks. For the other three cases, the correct answer is
always obtained when the decimal flag is set, and only in some cases
(e.g., Table 4) when in binary mode.So
What?
Now you know more about binarycoded decimal than you
ever dreamed possible. But why should you care? Burrow back through
your archives to the yellowed, brittle pages of ANALOG #43. The Boot
Camp in that issue discussed floating point numbers and mathematics in
the Atari. Floating point numbers use the BCD representation as a
compact way to stuff several digits of precision into a minimum number
(six) of bytes. A special notation is used to keep track of the decimal
point, exponent and negative sign in floating point numbers. BCD turns
out to be a pretty efficient storage format for base-10-type numbers,
and most computers use some form of BCD for floating point storage.You may recall that the main alternative character-coding method in common computer use is called EBCDIC (pronounced ebb-see-dick), used mainly by IBM mainframe computers. That acronym stands for "Extended Binary-Coded Decimal Interchange Code." See? You can run, but you just can't hide from binarycoded decimal.
There's another advantage. In today's example program, we converted BCD numbers to ASCII strings and printed them on the screen. However, you could also take each BCD digit, convert it to the Atari internal character code by ANDing it with $10 (as opposed to $30, which converts it to ASCII), and poke the result directly into the screen RAM for the current display. This is simpler and faster than printing on the screen, and the visual result is the same. A good example of this technique can be found in James Hague's Streamliner from ANALOG #56. See the right column of page 37 in that issue.
Promise
I promise: no more hard-core computing for awhile.
We'll get back to some graphics (warm know how to draw circles?), sound
effects and real-time clocks (how about a metronome program?), and
maybe even the kernel of an adventure program; a simple vocabulary
parser. Stay tuned.Table 1. ASCII Codes
for Decimal Characters |
||
Character | ASCII Value |
Binary Values |
0 |
$30 |
0000 |
1 |
$31 | 0001 |
2 |
$32 | 0010 |
3 |
$33 | 0011 |
4 |
$34 | 0100 |
5 |
$35 | 0101 |
6 |
$36 | 0110 |
7 |
$37 | 0111 |
8 |
$38 | 1000 |
9 |
$39 | 1001 |
|
|
||||||||||||||||||||||||||||||||||||
|
|
LISTING 1: ASSEMBLY
8010 ;
8020 ;*******************************
8030 ;
8040 ;ASC2BCD macro
8050 ;
8060 ;Usage: ASC2BCD chars,number
8070 ;'chars' is address of ASCII
8080 ; string to convert,ending w/ EOL
8090 ;'number' is address of BCD
8100 ; representation of the string
8110 ;
8120 .MACRO ASC2BCD
8130 .IF X0<>2
8140 .ERROR "Error in ASC2BCD"
8150 .ELSE
8160 LDX #255
8170 @ASCLOOP2
8180 INX
8190 LDA %1,X
8200 STA ASCII,X
8210 CMP #EOL
8220 BNE @ASCLOOP2
8230 JSR VALIDASC
8240 BCS @DONE2
8250 JSR ASC2BCD
8260 BCS @BCDERROR
8270 LDX #0
8280 @ASCLOOP3
8290 LDA NUM,X
8300 STA %2,X
8310 INX
8320 CPX #3
8330 BNE @ASCLOOP3
8340 CLC
8350 BCC @DONE2
8360 @BCDERROR
8370 PRINT CONVERTMSG2
8380 SEC
8390 @DONE2
8400 .ENDIF
8410 .ENDM
8420 ;
8430 ;***************************
8440 ;
8450 ;BCD2ASC macro
8460 ;
8470 ;Usage: BCD2ASC number,chars
8480 ;'number' is address of BCD
8490 ; number to convert
8510 ;'chars' is address of resulting
8520 ; ASCII string, ending with EOL
8530 ;
8540 .MACRO BCD2ASC
8550 .IF X.802
8560 .ERROR "Error in BCD2ASC"
8570 .ELSE
8580 LDA %1+1
8590 STA NUM
8600 LDA %1+1
8610 STA NUM+1
8620 LDA %1+2
8630 STA NUM+2
8640 JSR BCD2ASC
8650 LDX #255
8660 @BCDLOOP
8670 INX
8680 LDA ASCII,X
8690 STA %2,X
8700 CPX #5
8710 BNE @BCDLOOP
8720 INX
8730 LDA #EOL
8740 STA %2,X
8750 .ENDIF
8760 .ENDM
LISTING 2: ASSEMBLY
4600 ;
4610 ;*******************************
4620 ;
4630 ;subroutine ASC2BCD
4640 ;called by ASC2BCD macro
4650 ;
4660 ;converts string of ASCII digits
4670 ;at address ASCII to a 3-byte
4680 ;binary-coded decimal represen-
4690 ;tation at address NUM
4700 ;
4710 ASC2BCD
4720 LDX CHARCTR ;how many chars
4730 DEX ;to convert?
4740 DEX
4750 LDY #2
4760 LDA #0 ;zero 3 bytes
4770 STA NUM ;where BCD value
4780 STA NUM+1 ;will go
4790 STA NUM+2
4800 NXTDIG
4810 LDA ASCII,X ;get next char
4820 STA NUM,Y ;low BCD digit
4830 DEX ;point to next
4840 BMI BCDDONE ;done yet?
4850 LDA ASCII,X ;get new char
4860 ASL A ;shift into
4870 ASL A ;high nybble
4880 ASL A
4890 ASL A
4900 ORA NUM,Y ;becomes high
4910 STA NUM,Y ;BCD digit
4920 DEX ;point to prev.
4930 BMI BCDDONE ;done yet?
4940 DEY ;Point to next
4950 CLC ;BCD digit
4960 BCC NXTDIG ;go get it
4978 BCDDONE
4980 CLC ;all done, so
4990 RTS ;leave
5000 CONVERTMSG2
5010 .BYTE "ASCII to BCD cone
5020 .BYTE "version error",EOL
5030 ;
5040 ;*******************************
5050 ;
5060 ;subroutine BCD2ASC
5070 ;called by BCD2ASC macro
5080 ;
5090 ;converts 3-byte BCD number at
5100 ;address NUM to a 6-byte ASCII
5110 ,string at address ASCII
5120 ;leading zeros are changed to
5130 ;leading blanks
5140 ;
5150 BCD2ASC
5160 LDA #S20 ;init leading
5170 STA ZEROBLANK ;char to blank
5180 LDX #0 ;pointer to digit
5190 LDY #0 ;pointer to char
5200 NXTDIG2
5210 LDA NUM,X ;get 1st digit
5220 PHA ;stash on stack
5230 CLC
5240 LSR A ;move high nybble
5250 LSR A ;into low nybble
5260 LSR A
5270 LSR A
5280 BNE NONZERO1 ;equal to 0?
5290 LDA ZEROBLANK ;yes, set to
5300 STA ASCII,Y ;leading char
5310 BPL DOLOW ;do low half
5320 NONZERO1
5330 ORA #$30 ;change to ASCII
5340 STA ASCII,Y ;add to string
5350 LDA #$30 ;set leading
5360 STA ZEROBLANK ;char to '0'
5370 DOLOW
5380 INY ;aim at next char
5390 PLA ;get BCD digit
5400 AND #$0F ;keep low nybble
5410 BNE NONZERO2 ;equal to 0?
5420 LDA ZEROBLANK ;yes, set to
5430 STA ASCII,Y ;leading char
5440 BPL BCDDONE2 ;all done
5450 NONZERO2
5460 ORA #$30 ;conver to ASCII
5470 STA ASCII,Y ;add to string
5480 LDA #$30 ;set leading char
5490 STA ZEROBLANK ;to zero
5500 BCDDONE2
5510 INY ;point to next
5520 INX ;digit and char
5530 CPX #3 ;done 3 digits?
5540 BNE NXTDIG2 ;no, continue
5550 CLC ;yes, all done
5560 RTS ;exit
5570 ZEROBLANK .DS 1
LISTING 3: ASSEMBLY
0100 ;Example 1. Interconverting ASCII
0110 ;strings and BCD numbers
0120 ;
0130 ;by Karl E. Wiegers
0140 ;
0150 .OPT NO LIST,OBJ
0160 .INCLUDE #D8:MACRO.LIB
0170 ;
0180 ;-------------------------------
0190 ;
0200 ;store some work variables at
0210 ;$4FE8 so you can examine them
0220 ;if you like
0230 ;
0240 ;-------------------------------
0250 ;
0260 *= $4FE8
0270 ;
0280 BCD .DS 3
0290 CHARCTR .DS 1
0300 INBUF .S 7
0310 OUTBUF .DS 7
0320 ;
0330 ;-------------------------------
0340 ;
0350 ; PROGRAM STARTS HERE
0360 ;
0370 ;You'll be prompted to enter a
0380 ;number with 1-6 digits. This
0390 ;is stored at address INBUF.
0400 ;The BCD number produced is
0410 ;stored in 3 bytes starting at
0420 ;address BCD. Then several
0430 ;arithmetic operations are done
0440 ;in both binary and decimal mode,
0450 ;and a table of results is
0460 ;printed out.
0470 ;
0480 ;-------------------------------
0490 ;
0500 *= $5000
0510 ;
0515 START
0520 CLD ;binary mode!
0530 JSR CLS ;clear screen
0540 PRINT PROMPT ;getsinput
0550 POSITION 2,2 ;number
0560 INPUT 0,INBUF
0570 LDX #$00 ;get number of
0580 LDA ICBLL,X ;chars entered
0590 STA CHARCTR
0600 ;
0610 ;------------------------------
0620 ; lay out the table of results
0630 ;------------------------------
0640 ;
0650 POSITION 12,5
0660 PRINT TITLE
0670 POSITION 12,6
0680 PRINT HYPHENS
0690 POSITION 2,8
0700 PRINT INCRE
0710 POSITION 2,10
0720 PRINT DEC25
0730 POSITION 2,12
0740 PRINT HEX25
0750 POSITION 2,14
0760 PRINT ADDBYTE
0770 ;
0780 ;------------------------------
0790 ;convert string to BCD, abort if
0800 ;have a conversion problem
0810 ;------------------------------
0820 ;
0830 ASC2BCD INBUF,BCD
0840 BCC NOPROBLEM
0850 JMP END
0860 ;
0870 ;------------------------------
0880 ;First line: increment the BCD
0890 ;number in binary and decimal
0900 ;modes; be sure to set back to
0910 ;binary before doing anything
0920 ;else!
0930 ;reconvert from input string to
0940 ;BCD after each operation
0950 ;------------------------------
0960 ;
0970 NOPROBLEM
0980 CLD
0990 INC BCD+2
1000 BCD2ASC BCD,OUTBUF
1010 POSITION 14,8
1020 PRINT OUTSUF
1030 ASC28CD INBUF,BCD
1040 ;
1050 ;increment in decimal mode
1060 ;
1070 SED
1080 INC BCD+2
1090 CLD
1100 BCD2ASC BCD,OUTBUF
1110 POSITION 29,8
1120 PRINT OUTBUF
1130 ASC2BCD INBUF,BCD
1140 ;
1150 ;------------------------------
1160 ;Second line: add 25 to the BCD
1170 ;number in binary and decimal
1180 ;modes
1190 ;------------------------------
1200 ;
1210 CLD
1220 CLC
1230 LDA BCD+2
1240 ADC #25
1250 STA BCD+2
1260 BCC NOINC1
1270 JSR INCREMBCD
1280 NOINC1
1290 CLD
1300 BCD2ASC BCD,OUTBUF
1310 POSITION 14,10
1320 PRINT OUTBUF
1330 ASC2BCD INBUF,BCD
1340 ;
1350 ;add 25 in decimal mode
1360 ;
1370 SED
1380 CLC
1390 LDA BCD+2
1400 ADC #25
1410 STA BCD+2
1420 BCC NOINC2
1430 JSR INCREMBCD
1440 NOINC2
1450 CLD
1460 BCD2ASC BCD,OUTBUF
1470 POSITION 29,10
1480 PRINT OUTBUF
1490 ASC25CD INBUF,BCD
1500 ;
1510 ;-------------------------------
1520 ;Third line: add hexadecimal 25
1530 ;to the BCD number in binary and
1540 ;binary modes
1550 ;-------------------------------
1560 ;
1570 CLD
1580 CLC
1590 LDA BCD+2
1600 ADC #$25
1610 STA BCD+2
1620 BCC NOINC3
1630 JSR INCREMBCD
1640 NOINC3
1650 CLD
1660 BCD2ASC BCD,OUTBUF
1670 POSITION 14,12
1680 PRINT OUTBUF
1690 ASC26CD INBUF,BCD
1700 ;
1710 ;add $25 in decimal mode
1720 ;
1730 SED
1740 CLC
1750 LDA BCD+2
1760 ADC #$25
1770 STA BCD+2
1780 BCC NOINC4
1790 JSR INCREMBCD
1800 NOINC4
1810 CLD
1820 BCD2ASC BCD,OUTBUF
1830 POSITION 29,12
1840 PRINT OUTBUF
1850 ASC28CD INBUF,BCD
1860 ;
1870 ;-------------------------------
1880 ;Fourth line: add second byte
1890 ;of BCD number to the entire
1900 ;number, in binary and decimal
1910 ;modes. If number was 1-2 digits
1920 ;long, will just add zero
1930 ;-------------------------------
1940 ;
1950 ;ADD 2ND BYTE TO 3RD - BINARY
1960 CLD
1970 CLC
1980 LDA BCD+1
1990 ADC BCD+2
2000 STA BCD+2
2010 BCC NOINC5
2020 JSR INCREMBCD
2030 NOINC5
2040 CLD
2050 BCD2ASC BCD,OUTBUF
2060 POSITION 14,14
2070 PRINT OUTBUF
2080 ASC2BCD INBUF,BCD
2090 ;
2100 ;add 2nd byte to total - decimal
2110 ;
2120 SED
2130 CLC
2140 LDA BCD+1
2150 ADC BCD+2
2160 STA BCD+2
2170 BCC NOINC6
2180 JSR INCREMBCD
2190 NOINC6
2200 CLD
2210 BCD2ASC BCD,OUTBUF
2220 POSITION 29,14
2230 PRINT OUTBUF
2240 END
2244 INPUT B,INBUF
2248 JMP START
2250 ;
2260 ;------------------------------
2270 ;text lines for prompt and for
2280 ;output table
2290 ;------------------------------
2300 ;
2310 PROMPT
2320 .BYTE "Enter a number "
2330 .BYTE "up to 6 digits "
2340 .BYTE "long:",EOL
2350 TITLE
2360 .BYTE "Binary Mode "
2370 .BYTE "Decimal Mode",EOL
2380 HYPHENS
2390 .BYTE "----------- "
2400 .BYTE "------------",EOL
2410 INCRE
2420 .BYTE "INC",EOL
2430 DEC25
2440 .BYTE "Add 25",EOL
2450 HEX25
2460 .BYTE "Add $25",EOL
2470 ADDBYTE
2480 .BYTE "Add 2nd byte",EOL
2490 ;
2500 ;-------------------------------
2510 ;don't forget the subroutines!
2520 ;-------------------------------
2530 ;
2540 .INCLUDE #D8:SUBS.LIB
2550 ;
2560 ;*******************************
2570 ;subroutine do handle carry if
2580 ;adding to the third BCD byte
2590 ;went above 99; can't increment,
2600 ;so must add 1 to higher order
2610 ;bytes as needed
2620 ;
2630 INCREMBCD
2640 SED ;still in decimal
2650 CLC
2660 LDA #1 ;add i to second
2670 ADC BCD+1 ;BCD byte
2680 STA BCD+1 ;and store
2690 BCC NOMOREINC ;cause carry?
2700 CLC
2710 LDA #1 ;yes, so add 1 to
2720 ADC BCD ;first BCD byte
2730 STA BCD ;and store
2740 NOMOREINC
2750 RTS ;all done, exit