6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Jun 16, 2024 4:53 pm

All times are UTC




Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sat Apr 03, 2004 7:26 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
The 65C02 data sheet shows that the processor takes an extra cycle in ADC and SBC modes when it is in decimal mode to set the flags correctly.

Does anyone have a more detailed description of the operation, particularly how the negative and zero flags are set.

Thanks

_________________
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


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 03, 2004 8:44 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8458
Location: Southern California
N reflects the high bit (even though it's not really relevant to what might be considered negative numbers in decimal), and Z is set if all resulting bits are 0 and cleared otherwise (just as in binary mode). C also works correctly on the 65c02 just as you'd expect, but V is not valid for decimal arithmetic. Because of the issues with V and N, direct signed arithmetic is not as practical in decimal mode.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 03, 2004 1:53 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
Does V get set to a consistent value or is it just random?

_________________
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


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 03, 2004 9:22 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8458
Location: Southern California
I tried a bunch of different combinations of input, and V generally seems to end up the same as N, but even then it wasn't consistent.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 03, 2004 11:24 pm 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
> Does V get set to a consistent value or is it just random?

V gets set to a consistent value. I got curious one day about the statement in the 65C02 datasheets that one of the differences between the 65C02 and the (NMOS) 6502 is that the V flag is now valid in decimal mode. I eventually worked out all of the formulas for how the accumulator and the N, Z, and C flags are affected in decimal mode on the 6502, 65C02 and 65816 (for 8-bit numbers, I haven't worked out the 16-bit case), even if one or both numbers being added or subtracted are not valid BCD numbers. (By not valid, I mean at least one of the hex digits is A through F).

Other processors have a carry flag for the 4 least significant bits. On the Intel 8086, this flag is called the Auxiliary carry Flag (AF), and on the Motorola 6808 this flag is called the Half Carry (HC). Intel publishes formulas detailing how their BCD instructions use and affect the various flags and registers. Motorola publishes a table rather than formulas. My 6502 formulas more or less follow the Intel model, but since the 6502 doesn't have that additional carry flag, my formulas aren't as easily comprehensible. I haven't come up with a way of documenting this information in an easy to understand way, and as a result, I haven't written anything up about this. I am open to any suggestions that anyone might have.

What I have written up is a tutorial on the V flag, which I submitted for the Tutorials and Aids section of 6502.org a while back, but I guess it must not have made the cut. It contains an appendix which details how to predict the value of the V flag in decimal mode, even when the values being added or subtracted are not valid BCD numbers. I can e-mail a copy of this tutorial to anyone who is interested.

Interestingly, the instruction ADC NUMBER has the same effect on the V flag on the 6502, 65C02, and 65816. The same is true for a SBC NUMBER instruction. (This is of course assuming that upon entry, the D flag was the same, as were the C flag and the accumulator.) So ironically, that seemingly innocuous statement in the 65C02 datasheets about the V flag now being valid in decimal mode ultimately led to the discovery that the effect on the V flag (in decimal mode) hadn't changed at all!

Anyway, a simple technique for getting the N and Z flags to be the same when adding or subtracting valid BCD numbers, regardless of whether you're using a 6502, 65C02 or 65816 is:

Code:
ADC NUMBER ; or SBC NUMBER
EOR #0     ; ORA #0 or AND #$FF will also work


The EOR #0 can be omitted on the 65C02 and the 65816, but by leaving that instruction in place, the object code can be ported to a 6502, 65C02, or 65816 (when in emulation mode or when the m flag is 1, of course) without modification. Of course, the cycle count will still be different, so if your routine depends on an exact number of cycles, you will have to use a compensating technique, such as the one described in the "A 65C02 Bug?" topic in the "Programming" forum.

Anyway, the fact that the EOR #0 instruction is unnecessary on the 65C02 and 65816 should give you an idea of how they affect the N and Z flags. They're based on the result in the accumulator, like any other instruction that affects the accumulator and the N and Z flags. By the way, the reason I said for valid BCD numbers is that there are some cases where SBC will give different results (in the accumulator) on a 6502 than on a 65C02 when subtracting invalid BCD numbers in decimal mode.

I've tested all of the above on a Rockwell 6502, a Synertek 6502, a GTE 65C02, and a GTE 65816, which are all the varieties that I have. I have a half-complete program for verifying that my formulas are correct, which work by computing the formulas in hex mode and then comparing them to the decimal mode result. If there is interest, I can finish the program and perhaps post it here. (When I'll finish it is another matter.) Anyone with a 6502 (or a 65C02 or a 65816) of a different vintage or manufacturer could then use this program to check the decimal mode behavior on their microprocessor.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Apr 04, 2004 12:15 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8458
Location: Southern California
> What I have written up is a tutorial on the V flag, which I submitted for
> the Tutorials and Aids section of 6502.org a while back, but I guess it
> must not have made the cut.

Just give Mike time. It'll get there. He's working on a lot of things at once. There are only two entries there now, and the V flag one is not one of them.

But something I tried before saying the V result in decimal mode was not valid was:

79+1=80, N=1, V=1. V is incorrect.
80+1=81, N=1, V=0. V is correct.
79+80=(1)59, N=0, V=0. V is correct.
80+80=(1)60, N=0, V=1. V is incorrect.
79+81=(1)60, N=0, V=0. V is correct here, even though the result is the same as with 80+80 where it was incorrect!

I'm doing this on a 65802 in emulation mode, if that makes any difference. If you can point out a pattern that's useful for something, I'd be glad to hear it. But as you can see from the above, it does not follow the same V flag rules that it does in binary mode.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Apr 04, 2004 12:08 pm 
Offline

Joined: Fri Aug 30, 2002 2:05 pm
Posts: 347
Location: UK
GARTHWILSON wrote:
79+1=80, N=1, V=1. V is incorrect.


V is correct here as there has been a carry from b6 to b7 but no carry from b7.

Quote:
80+80=(1)60, N=0, V=1. V is incorrect.


V is also correct as there has been a carry from b7 but none from b6 to b7.

The V flag should be the exclusive OR of the carry from b6 and the carry from b7.

Lee.


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 04, 2004 12:28 pm 
Offline
Site Admin
User avatar

Joined: Fri Aug 30, 2002 1:08 am
Posts: 280
Location: Northern California
dclxvi wrote:
What I have written up is a tutorial on the V flag, which I submitted for the Tutorials and Aids section of 6502.org a while back, but I guess it must not have made the cut. It contains an appendix which details how to predict the value of the V flag in decimal mode, even when the values being added or subtracted are not valid BCD numbers. I can e-mail a copy of this tutorial to anyone who is interested.

This excellent tutorial is now online:

http://www.6502.org/tutorials/compare_beyond.html

Best Regards,
Mike

_________________
- Mike Naberezny (mike@naberezny.com) http://6502.org


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Apr 04, 2004 5:42 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8458
Location: Southern California
> > 79+1=80, N=1, V=1. V is incorrect.
>
> V is correct here as there has been a carry from b6 to b7 but no carry from b7.
>
> > 80+80=(1)60, N=0, V=1. V is incorrect.
>
> V is also correct as there has been a carry from b7 but none from b6 to b7.
>
> The V flag should be the exclusive OR of the carry from b6 and the carry from b7.


Ok, I have some fixing to do.

You are right about the validity of the XORing of the b6 and b7 carries, and I was forgetting about this. In fact I wrote too hastily.

What is not valid is the effect in decimal arithmetic.

The whole purpose of V is to tell if the resulting sign is correct or not. If we used signs in decimal arithmetic, we would have to say that from 00 to 49 is positive, and from 50 to 99 is negative (ie, -50 up to -1). The processor affords no way to tell positive from negative by a single bit however like it does with hex where there's a 100% correlation between the high bit and the number's being negative. When the high bit cannot be used as the sign bit before the addition or subtraction, the XORing of the b6 and b7 carries cannot be used to determine if the resulting sign bit is correct either.

In binary (actually expressed in hex here), 79 and 1 are positive and the high bits are clear. Adding them, you get 7A, whose high bit is also clear meaning that the result is positive. There is no error here, so V=0. Even if we added 7 in hex to get a result of 80, adding the two positive numbers together should result in another positive number; but 80 is negative, so V is set to indicate that the resulting sign is incorrect. Getting V=1 is valid in hex. It is not valid in decimal though. Since 50 through 99 would have to be considered negative, 79 would actually be -21. If you add 1 to it, you get -20. The resulting N flag is set, which is correct, but V was also set saying that N was incorrect. The error here is in the V flag. Yes, the binary logic job was done correctly inside the processor, but the result is not valid or useful in decimal arithmetic.

In the next example above, 80, or actually -20 in signed decimal arithmetic, added to itself gives 160, or actually -40 in signed decimal arithmetic. -40 is negative, but N says positive, so it is incorrect, and V correctly reports this. I wrote too hastily. But my next example was:

79+81=(1)60, N=0, V=0. V is correct here, even though the result is the same as with 80+80 where it was incorrect!

Here 81, or -19, added to 79, or -21, should also be 160, or -40. The result is negative in signed decimal arithmetic, but the N flag fails to show that, and the V flag fails to report the error. So here where I said V was correct, it was actually not correct for decimal arithmetic. I goofed again, and I apologize for misleading anyone as to why V cannot be used for signed decimal arithmetic.

I also erroneously said:

79+80=(1)59, N=0, V=0. V is correct.

Here 80, or -20, added to 79, or -21, gives 159, or -41, which is negative. N fails to report the negative status of the decimal result, and V again fails to tell us that N was wrong. In every case, N correctly reflects the high bit of the result, and V always gives the XOR of the b6 carry and the b7 carry; but although it is not assumed that N particularly reflects the negative status of a decimal arithmetic result, V especially has no value in decimal arithmetic. It has no validity for any part of it as far as I can tell.

For this reason, WDC's own programming manual says, "Finally, although the carry flag is set correctly in the decimal mode allowing unsigned multiple-precision operations, the overflow flag is not, making signed decimal arithmetic, while possible, difficult."


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Apr 04, 2004 8:00 pm 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
> I'm doing this on a 65802 in emulation mode, if that makes any difference.

I don't know if it makes any difference either. Read on and you can test it for yourself.

> If you can point out a pattern that's useful for something, I'd be glad to
> hear it. But as you can see from the above, it does not follow the same V
> flag rules that it does in binary mode.

There is one set of rules for binary mode and a separate set of rules for decimal mode. However, the decimal mode rules are consistent, even if they don't make much sense. In terms of usefulness, well...the decimal mode rules satiated my curiosity. Does that count?

SBC is easy. SBC NUMBER affects the V flag in the same way, regardless of the value of the D flag, or whether the two numbers being added are valid BCD numbers or not. To determine the V flag, just subtract the numbers as though they were binary numbers. Here is a program that checks this. It returns 1 in ERROR if the test passes, and it returns 0 in ERROR if the test fails. Conveniently, it stops at the first counterexample, leaving the V flag result from the decimal subtraction in DV. The V flag result of the binary subtraction can then be deduced, since the two flags are unequal. Basically, it just tests all 131072 cases (256 possible values of N1 * 256 possible values of N2 * 2 possible value of the carry upon entry). It takes about 8 seconds to complete (if it passes) at 1 MHz.

Code:
; CHECK SBC V FLAG IN DECIMAL MODE
;
; Returns with ERROR = 0 if the test passes, ERROR = 1 if the test fails
;
; Four additional memory locations are used: DV, ERROR, N1, and N2
; These may be located anywhere convenient in RAM
;
      LDY #1    ; initialize Y (carry) to one
      STY ERROR ; store 1 in error until all tests pass
      LDA #0    ; initialize N1 and N2 to zero
      STA N1
      STA N2
LOOP  SED
      CPY #1    ; carry = 1 when Y = 1, carry = 0 when Y = 0
      LDA N1    ; subtract in decimal mode
      SBC N2
      PHP
      PLA
      STA DV    ; save decimal mode V flag
      CLD
      CPY #1    ; carry = 1 when Y = 1, carry = 0 when Y = 0
      LDA N1    ; subtract in binary mode
      SBC N2
      PHP
      PLA
      EOR DV
      AND #$40  ; mask the V flag
      BNE DONE  ; exit with ERROR = 1 if V flags are not equal
      INC N1
      BNE LOOP  ; loop through all 256 values of N1
      INC N2
      BNE LOOP  ; loop through all 256 values of N2
      DEY
      BPL LOOP  ; loop through both values of Y (carry)
      LDA #0    ; all tests pass, so store 0 in ERROR
      STA ERROR
DONE  RTS


If you change the AND #$40 to an AND #$01, you'll see that the same thing is true of the carry (for SBC). (You get the idea of how the whole formula testing program works. The V flag is one of the easier cases since it is the same on the 6502, 65C02, and 65816.)

ADC is more complicated. It's reassuring to know that all of your 65802 examples agree with what I've found. For the purposes of determining the V flag, the lower digits are added as though they were BCD digits, and the upper digits are added as though they are binary digits. More specifically, when adding N1 and N2 (all of the additions are performed as though they were binary, rather than decimal, numbers):

1. Let N1L = N1 AND $0F
2. Let N1H = N1 AND $F0
3. Let N2L = N2 AND $0F
4. Let N2H = N2 AND $F0

(L and H in this case are the low and high digits, not the low and high bytes.)

5. Let RESULTL = N1L + N2L + carry
6. The V flag is determined by the addition N1H + N2H when RESULTL <= 9
7. The V flag is determined by the addition N1H + N2H + $10 when RESULTL > 9

Here is a program which tests this. It also conveniently stops at the first counterexample. It's very similar to the SBC checking program, just modified for the ADC method. It takes about 10 seconds to complete (if it passes) at 1 MHz.

Code:
; CHECK ADC V FLAG IN DECIMAL MODE
;
; Returns with ERROR = 0 if the test passes, ERROR = 1 if the test fails
;
; Six additional memory locations are used: DV, ERROR, N1, N2, N2H, and N2L
; These may be located anywhere convenient in RAM
;
      LDY #1    ; initialize Y (carry) to one
      STY ERROR ; store 1 in error until all tests pass
      LDA #0    ; initialize N1 and N2 to zero
      STA N1
      STA N2
LOOP1 LDA N2    ; split N2 into low and high digits
      AND #$0F
      STA N2L
      LDA N2
      AND #$F0
      STA N2H
LOOP2 SED
      CPY #1    ; carry = 1 when Y = 1, carry = 0 when Y = 0
      LDA N1    ; add in decimal mode
      ADC N2
      PHP
      PLA
      STA DV    ; save decimal mode V flag
      CLD
      CPY #1    ; carry = 1 when Y = 1, carry = 0 when Y = 0
      LDA N1
      AND #$0F  ; get low digit of N1
      ADC N2L   ; add to low digit of N2
      CMP #$0A
      LDA N1
      AND #$F0  ; get high digit of N1
      BCC SKIP  ; if low digit result < $0A, then add N1H + N2H
      ORA #$0F  ; since carry = 1, the addition will be N1H + N2H + $0F + 1
SKIP  ADC N2H   ; add high digits
      PHP
      PLA
      EOR DV
      AND #$40  ; mask the V flag
      BNE DONE  ; exit with ERROR = 1 if V flags are not equal
      INC N1
      BNE LOOP2 ; loop through all 256 values of N1
      INC N2
      BNE LOOP1 ; loop through all 256 values of N2
      DEY
      BPL LOOP1 ; loop through both values of Y (carry)
      LDA #0    ; all tests pass, so store 0 in ERROR
      STA ERROR
DONE  RTS


> This excellent tutorial is now online:

The comparison tutorial isn't the tutorial I was referring to. There is a second tutorial titled "The V Flag Explained" that I sent a month ago or so. There is a bit of overlap between the two tutorials, but the V flag tutorial contains two appendices, the second of which deals with decimal mode and goes through a few ADC examples step by step. (It doesn't contain the programs above, though.)


Top
 Profile  
Reply with quote  
 Post subject: V-Flag Tutorial
PostPosted: Sun Apr 04, 2004 8:58 pm 
Offline
Site Admin
User avatar

Joined: Fri Aug 30, 2002 1:08 am
Posts: 280
Location: Northern California
dclxvi wrote:
The comparison tutorial isn't the tutorial I was referring to. There is a second tutorial titled "The V Flag Explained" that I sent a month ago or so. There is a bit of overlap between the two tutorials, but the V flag tutorial contains two appendices, the second of which deals with decimal mode and goes through a few ADC examples step by step. (It doesn't contain the programs above, though.)

Bruce,

Sorry for the confusion. I can't remember seeing this submission.

I just searched through all of my e-mail and I couldn't find it. I don't think that I received it. Please send it to me again and I'll be sure to get it online.

Regards,
Mike

_________________
- Mike Naberezny (mike@naberezny.com) http://6502.org


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 07, 2004 1:02 am 
Offline
Site Admin
User avatar

Joined: Fri Aug 30, 2002 1:08 am
Posts: 280
Location: Northern California
dclxvi wrote:
There is a second tutorial titled "The V Flag Explained" that I sent a month ago or so. There is a bit of overlap between the two tutorials, but the V flag tutorial contains two appendices, the second of which deals with decimal mode and goes through a few ADC examples step by step. (It doesn't contain the programs above, though.)

Hi,

This tutorial is now online: http://www.6502.org/tutorials/vflag.html

Regards,
Mike

_________________
- Mike Naberezny (mike@naberezny.com) http://6502.org


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 24, 2004 7:56 pm 
Offline

Joined: Sun Aug 24, 2003 7:17 pm
Posts: 111
I looked through what has been written about the V flag when in decimal mode. I think you make it too complicated! In fact one should see it as follows:

The whole idea with the V flag is that the most significant bit is the sign bit. In decimal mode this means that the largest positive value is 79 and the smallest negative value is -20 = $80. Just a matter of interpretation!

The simple rule is that the V flag gets sets if when adding two positive numbers (most significant bit unset for both) the result is negative (most significant bit set) or when adding two negative numbers (most significant bit set for both) the result is positive (most significant bit unset). This is equally true in decimal as in binary mode. It "makes sense" if and only if one considers that $80 means -20 and that $79 means +79.

This not only logical but fully verified on a W65C02! Successivly adding 1
in decimal mode the V bit gets set when 1 is added to $79 giving $80 as result. Just as expected!


Last edited by Mats on Sat Apr 24, 2004 9:04 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 24, 2004 9:00 pm 
Offline

Joined: Sun Aug 24, 2003 7:17 pm
Posts: 111
I made some more tests. There is indeed a "bug" in W65C02!

Adding two positive numbers in decimal mode is fine:

$78 + $01 = $79 with V unset (+79)
$79 + $01 = $80 with V set (-20)
$80 + $01 = $81 with V unset (-19)

but (unfortunately):

$81 + $99 = $80 BUT V IS SET!!!!!!!

In binary mode W65C02S works OK

$81 + $FF = $80 with V unset as should be!!!

Could somebody make the test $81 + $99 with another brand of 6502! Is this a known bug?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 24, 2004 11:29 pm 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
Mats wrote:
I looked through what has been written about the V flag when in decimal mode. I think you make it too complicated!


I'm certainly open to simpler explanations! But the V flag in decimal mode (after ADC) is unfortunately more complicated than you have described here.

Mats wrote:
The whole idea with the V flag is that the most significant bit is the sign bit. In decimal mode this means that the largest positive value is 79 and the smallest negative value is -20 = $80. Just a matter of interpretation!

The simple rule is that the V flag gets sets if when adding two positive numbers (most significant bit unset for both) the result is negative (most significant bit set) or when adding two negative numbers (most significant bit set for both) the result is positive (most significant bit unset). This is equally true in decimal as in binary mode. It "makes sense" if and only if one considers that $80 means -20 and that $79 means +79.


Not true! For the purposes of determining the V flag after an ADC in decimal mode, $90 acts like -112, not -10. $90 + $90 leaves V set in both binary and decimal modes, which is correct for -112 + -112, but not -10 + -10.

For ADC in decimal mode, the lower digits are added as though they were BCD, but the upper digits are added as though they are binary, which is why this is such a mess. The upper digit behavior can be isolated simply by looking at the cases where the lower digits are zero. Here is a program that verifies that when the lower digits are zero, the V flag is the same in decimal mode and binary mode.

Code:
; CHECK ADC V FLAG IN DECIMAL MODE (when lower digits are zero)
;
; Returns with ERROR = 0 if the test passes, ERROR = 1 if the test fails
;
; Four additional memory locations are used: DV, ERROR, N1, and N2
; These may be located anywhere convenient in RAM
;
      LDA #1
      STA ERROR ; store 1 in error until all tests pass
      LDA #0    ; initialize N1 and N2 to zero
      STA N1
      STA N2
LOOP  SED
      CLC
      LDA N1    ; add in decimal mode
      ADC N2
      PHP
      PLA
      STA DV    ; save decimal mode V flag
      CLD
      CLC
      LDA N1    ; add in binary mode
      ADC N2
      PHP
      PLA
      EOR DV
      AND #$40  ; mask the V flag
      BNE DONE  ; exit with ERROR = 1 if V flags are not equal
      CLC
      LDA N1
      ADC #$10
      STA N1
      BCC LOOP
      CLC
      LDA N2
      ADC #$10
      STA N2
      BCC LOOP
      LDA #0    ; all tests pass, so store 0 in ERROR
      STA ERROR
DONE  RTS


Mats wrote:
$81 + $99 = $80 BUT V IS SET!!!!!!!

In binary mode W65C02S works OK

$81 + $FF = $80 with V unset as should be!!!

Could somebody make the test $81 + $99 with another brand of 6502! Is this a known bug?


In decimal mode, $81 + $99 sets V on a 6502, 65C02 and 65816. See above.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 16 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: