Questions
I don't know of any processor that would convert an invalid BCD number (i.e. a number that contains at least one digit in the range A-F) to a valid BCD number before performing BCD arithmetic. Basically, what the 6502 and other processors do is use an algorithm that gives the correct results for valid BCD numbers, and the results from invalid BCD numbers are "don't care". For example, to add two BCD digits to one another, the following steps could be used:
1. Add the two digits using binary arithmetic
2. If the result is greater than 9, add 6 to the result using binary arithemetic
So, when the algorithm above is used to add 5 and 8, the result from the first step is 5+8 = $0D (in hex). This is greater than 9, so the second step adds 6, and the result from the second step is $0D+$06 = $13.
Notice that this BCD arithmetic algorithm is defined in terms of binary arithmetic. BCD arithmetic is not defined for digits in the range A-F, but binary arithmetic is defined for all digits 0-F, so digits in the range A-F can be plugged into the algorithm above, and the result will be consistent and predictable. For example, when $E is added to $F, the result of the first step is $E+$F = $1D. This is greater than 9, so the second step adds 6, and the result from the second step is $1D+6 = $23. There is no correct answer when at least one of the numbers is an invalid BCD number, so it doesn't matter if the result was $23, $FF, zero, or random (just to list a few examples). In fact, a third step could be added stating: "If either digit is in the range A-F, return a random number", and the algorithm would still be correct, because it still gives the correct result when both numbers are valid BCD numbers.
There are not many uses for performing BCD arithmetic on invalid BCD numbers, so it makes sense for a processor to cater to the common case and use an algorithm as simple as possible while still giving correct results for valid BCD numbers, and just let the chips fall where they may for invalid BCD numbers. Keeping the algorithm simple help keeps the circuitry simple. Generally speaking, simpler circuitry keeps cost lower and allows higher clock speeds, so there is a strong incentive for simplicity. Checking for digits in the range A-F so that they can be handled specially would only add a lot more unnecessary work for processor.
Incidentally, when zero is added to an invalid BCD digit using the (two step) algorithm above, the result will be $10-$15. For example, $A+0 = $0A, which is greater than 9, so adding 6 gives the result $0A+6 = $10. Thus, the algorithm can be used to convert an invalid BCD digit to a valid BCD number by adding zero. The algorithm was not designed with this special case in mind, it just happens to be an unintended benefit.
1. Add the two digits using binary arithmetic
2. If the result is greater than 9, add 6 to the result using binary arithemetic
So, when the algorithm above is used to add 5 and 8, the result from the first step is 5+8 = $0D (in hex). This is greater than 9, so the second step adds 6, and the result from the second step is $0D+$06 = $13.
Notice that this BCD arithmetic algorithm is defined in terms of binary arithmetic. BCD arithmetic is not defined for digits in the range A-F, but binary arithmetic is defined for all digits 0-F, so digits in the range A-F can be plugged into the algorithm above, and the result will be consistent and predictable. For example, when $E is added to $F, the result of the first step is $E+$F = $1D. This is greater than 9, so the second step adds 6, and the result from the second step is $1D+6 = $23. There is no correct answer when at least one of the numbers is an invalid BCD number, so it doesn't matter if the result was $23, $FF, zero, or random (just to list a few examples). In fact, a third step could be added stating: "If either digit is in the range A-F, return a random number", and the algorithm would still be correct, because it still gives the correct result when both numbers are valid BCD numbers.
There are not many uses for performing BCD arithmetic on invalid BCD numbers, so it makes sense for a processor to cater to the common case and use an algorithm as simple as possible while still giving correct results for valid BCD numbers, and just let the chips fall where they may for invalid BCD numbers. Keeping the algorithm simple help keeps the circuitry simple. Generally speaking, simpler circuitry keeps cost lower and allows higher clock speeds, so there is a strong incentive for simplicity. Checking for digits in the range A-F so that they can be handled specially would only add a lot more unnecessary work for processor.
Incidentally, when zero is added to an invalid BCD digit using the (two step) algorithm above, the result will be $10-$15. For example, $A+0 = $0A, which is greater than 9, so adding 6 gives the result $0A+6 = $10. Thus, the algorithm can be used to convert an invalid BCD digit to a valid BCD number by adding zero. The algorithm was not designed with this special case in mind, it just happens to be an unintended benefit.
In decimal mode on the 65C02, N means "the most significant bit" (i.e. bit 7 for an 8-bit number), just as it does in binary mode. Thus N is zero when the (8-bit) accumulator result is $00-$7F, and N is one when the accumulator result is $80-$FF in decimal mode. For the most part, in decimal mode it is more convenient for $80 to $99 represent 80 to 99 than -20 to -1.
The V flag in decimal mode is exactly the same on the 65C02 as the 6502. The seven step method in the other thread can be used to predict/calculate its value using only binary arithmetic, even for invalid BCD numbers. In other words, the V flag after
will be the same on both the 6502 and the 65C02 assuming the accumulator and carry flag upon entry were the same for both processors. Likewise, the V flag after
will be the same on both the 6502 and the 65C02, again assuming the accumulator and carry flag upon entry were the same for both processors. So despite the statements that the V flag is now "valid" on the 65C02 (and 65816) in decimal mode, its behavior hasn't actually changed at all!
The V flag in decimal mode is exactly the same on the 65C02 as the 6502. The seven step method in the other thread can be used to predict/calculate its value using only binary arithmetic, even for invalid BCD numbers. In other words, the V flag after
Code: Select all
SED
ADC NUMBER
Code: Select all
SED
SBC NUMBER
I have recently purchased an Atari 2600 and a Supercharger and have been testing the 6507 inside for my 6502 emulator. I have answered all of my own questions but have raised one. In decimal mode if the Accumulator or Operand is not a BCD number then this has a big affect on ADC/SBC. Does anybody know the formula? I have performed test after test and sometimes the Accumulator is BCD converted before the ADC/SBC and other times it seems to be converted afterwards (the same goes for the Operand). All I get are strange results. Please Help!!!
Alucard,
There is no such thing as an 'undocumented feature'. You are really asking about the side effects of improper use.
These so called features are inheritly circumstantial, and no one is familiar with the exact circumstances. So you'll never get reliable answers.
There is no such thing as an 'undocumented feature'. You are really asking about the side effects of improper use.
These so called features are inheritly circumstantial, and no one is familiar with the exact circumstances. So you'll never get reliable answers.
I trust my somewhat flawed English is comprehensible to all.
Sigh. Once again, the 6502 (or 65C02 or 65816) NEVER converts to BCD before performing an addition or subtraction.
Do you understand how, for example, the V flag is determined in decimal mode? The accumulator follows a similar process. But it differs on the 6502 and 65C02, e.g. $20-$0F is $0B on the 65C02, but $1B on the 6502 (and 65816). I've got a program that can compute the decimal mode results (flags and accumulator) using only binary arithmetic, but my program is not documented. If you basically understand the 7 step method for determining the V flag and the accompanying program in the other thread, you'll probably be able to follow my program. Otherwise, you can always wait until I finish my decimal mode write-up. I can't say when that will be, as I am doing it in my free time. But it is near the top of my list of 6502-related projects to do.
Do you understand how, for example, the V flag is determined in decimal mode? The accumulator follows a similar process. But it differs on the 6502 and 65C02, e.g. $20-$0F is $0B on the 65C02, but $1B on the 6502 (and 65816). I've got a program that can compute the decimal mode results (flags and accumulator) using only binary arithmetic, but my program is not documented. If you basically understand the 7 step method for determining the V flag and the accompanying program in the other thread, you'll probably be able to follow my program. Otherwise, you can always wait until I finish my decimal mode write-up. I can't say when that will be, as I am doing it in my free time. But it is near the top of my list of 6502-related projects to do.
Alucard wrote:
dclxvi wrote:
Sigh. Once again, the 6502 (or 65C02 or 65816) NEVER converts to BCD before performing an addition or subtraction.
Code: Select all
LDA #$0F
CLC
SED
STA ShouldBe15
However:
Code: Select all
LDA #$0F
CLC
SED
ADC #$00
STA ShouldBe15
There are examples where the result produced by the 6502 in decimal mode will be the same as the result you would get if you converted to BCD beforehand. Such as $00+$0A=$10. That does not mean that this is what the 6502 actually does. The fact that you've found that the "convert to BCD beforehand" theory doesn't always hold true suggests that this is not what the 6502 is really doing.
The cases the 6502 has to cover are for valid BCD numbers. It does not need to convert these to BCD beforehand, of course. The cases with invalid BCD numbers are "don't care". There is no reason why the 6502 would add an EXTRA step which would not affect the cases it has to cover when it is free to do whatever it likes in the "don't care" cases.
The cases the 6502 has to cover are for valid BCD numbers. It does not need to convert these to BCD beforehand, of course. The cases with invalid BCD numbers are "don't care". There is no reason why the 6502 would add an EXTRA step which would not affect the cases it has to cover when it is free to do whatever it likes in the "don't care" cases.
Here is proof; (performed on an Atari 2600 Jr. with a Starpath SuperCharger)
I then execute code that displays the accumulator as 1's and 0's on the screen. The result is $66 (01100110).
Code: Select all
SED
CLC
LDA #$FF
ADC #$01
or
F8 18 A9 FF 69 01
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Alucard, can you try some more combinations of numbers to try to figure out what's going on; because the answer you're getting, as far as anything I can think of so far, is wrong any way you look at it. I don't have any hardware I can try an NMOS 6502 (or derivative) on, and I don't have documentation on what it does in this kind of situation.
If the accumulator is $FF and you add, say $01 then the result is $66. $42 + $01 = $43. Here is what I am sure of;
The immediate data is NOT converted.
Code: Select all
SED
CLC
LDA #$FF
// ADC
if( (A & 0xF0) > 0x90 )
A += 0x60;
if( (A & 0x0F) > 0x09 )
A += 0x06;
// A = $65
A += Immediate; ($01)
// A = $66
- BitWise
- In Memoriam
- Posts: 996
- Joined: 02 Mar 2004
- Location: Berkshire, UK
- Contact:
Thats doesn't make sense. The ALU would have to decimal correct twice under your scheme, once before addition and again after.
Consider what would happen if you added $F9 and $01. In your scheme the $F9 would become $69 before the $01 was added leaving $6A. Another round of decimal correction would be needed to correct the least nybble.
I've always understood BCD correction to be a phase applied after a binary addition/subtraction has been performed.
Can you rerun your tests using different memory locations just incase the ones you chose are trashed by an interrupt or operating system routine to double check your results.
Consider what would happen if you added $F9 and $01. In your scheme the $F9 would become $69 before the $01 was added leaving $6A. Another round of decimal correction would be needed to correct the least nybble.
I've always understood BCD correction to be a phase applied after a binary addition/subtraction has been performed.
Can you rerun your tests using different memory locations just incase the ones you chose are trashed by an interrupt or operating system routine to double check your results.
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
Alucard wrote:
If the accumulator is $FF and you add, say $01 then the result is $66.
You just proved that the 6507 does not, in fact, do BCD pre-conversion, but rather instead, responds by adding $FF + $60 + $06 + $01 + carry, all at once. I think this is a significant difference, because it proves that merely enabling and disabling the D bit doesn't "pre-convert" the contents of A, but rather, you need an ADC to trigger it.
It also proves that the ALU has 5 inputs to its adder, which is pretty amazing. That's a big-ass adder.
I verified this on the Commodore +4 emulator and the Commodore 128 emulators as well -- so its behavior exists on the MOS Technology CPUs as well. Also, as a quick test, I typed the following quick program into the Commodore Plus/4 emulator:
Code: Select all
MONITOR
A 033C F8 SED
A 033D 18 CLC
A 033E A9 FD LDA #$FD
A 0340 69 01 ADC #$01
A 0342 D8 CLD
A 0343 00 BRK
A 0344
G 033C
BREAK
PC SR AC XR YR SP
; 0345 31 64 FF FF F9
Code: Select all
MONITOR
A 033C F8 SED
A 033D 18 CLC
A 033E A9 F0 LDA #$F0
A 0340 69 0F ADC #$0F
A 0342 D8 CLD
A 0343 00 BRK
A 0344
G 033C
BREAK
PC SR AC XR YR SP
; 0345 31 65 FF FF F9
Code: Select all
// SED
// CLC
// LDA #$F0
// ADC #$0F
A += 0x0F;
if( (A & 0xF0) > 0x90 ) A += 0x60;
if( (A & 0x0F) > 0x09 ) A += 0x06;
I'm sure that this artifact can be exploited in some beneficial manner, though, especially when doing binary manipulation of bits 6, 5, 2, or 1.