Comparison Question

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Comparison Question

Post by Celius »

So I've tested my code in FCEUXD, and I encountered a problem. I have to admit that as much as I program for the NES, I really never used BMI, BPL, or BCC to compare if something was greater, equal to, or less than something else, which is really sad, I know. So I'm not so sure about this problem I have. Look at the following code:

Code: Select all

 lda #$68
 cmp #$F0
 bmi +
 
 (Blah code)
+
If someone could tell me why this doesn't branch, I'd be very greatful. If I compare #$78 to #$F0, it will branch. I just don't understand why #$68 won't. And I'm absolutely positive that's the problem.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

The problem is you are using a instruction designed for comparing signed bytes when you're really comparing unsigned bytes. Use BMI and BPL for signed numbers, and BCC and BCS for unsigned numbers.

BMI and BPL make a decision based on bit 7 of the result, which when set indicates that a signed number is negative. However, if your numbers are not signed, that bit will be misinterpreted.

The number $68 is positive no matter if the byte is signed or not. But $F0 can be seen as $F0 (if the byte is unsigned) or as -$10 (if it is signed).

The CMP instruction will subtract $F0 from $68 and will find the number $78, and the carry flag will be clear. This result is correct, if the numbers are signed or not. It's all about how you look at the result:

Signed:
$68 = 104 decimal
$F0 = -16 decimal
104 - (-16) = 104 + 16 = 120
120 decimal = $78, which is a positive number (bit 7 is clear), so the BMI instruction will not branch. The value of the carry should not matter when comparing unsigned numbers, bit 7 of the result is the decisive factor here.

Unsigned:
$68 = 104 decimal
$F0 = 240 decimal
104 - 240 = -136
Now, to understand this part you must understand how subtraction works on the 6502. You know that we always set the carry flag before a subtraction (and that the CMP instruction assumes that the carry is set). I like to think of that carry bit as a bit you place there to be borrowed during the operation in case the second number is larger than the first. Then, to know if a number is larger than the other, you check the state of the carry flag afterwards. If it's clear, the 1 was borrowed, meaning that the second number was larger than the first. If it's set, the first number is larger or they are equal.

Anyway, since 240 is larger than 104, the carry bit is borrowed to turn 104 into 360 ($100 + $68). After that borrow, the subtraction is possible, and 360 - 240 = 120, which is exactly the same value we obtained before. This result may seem wrong, since in the calculation above it was -136. but this error is due to the fact that -136 can not be represented with only 8 bits, and when -136 is truncated to 8 bits it looks just like 120. After all, if you are working with unsigned numbers, how do you expect to have space for a signed result? If you only want to know what number is larger, this result is not important, just the state of the carry.

This topic can be complicated, but if you keep in mind that you should use BMI and BPL only for comparing signed numbers, and BCC and BCS when comparing unsigned numbers, you should be fine.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Thank you very much for your explanation, Tokumaru. When I switched it to be BCC instead of BMI, everything worked out fine. I do have a bit of a newbie confession. I don't really understand signed/unsigned numbers... But I'm sure I will. It's getting a little late, so tommorow I'll read up on them. Thank you, once again, for your help :) .
dvdmth
Posts: 354
Joined: Wed Mar 22, 2006 8:00 am

Post by dvdmth »

Some assemblers may accept BLT (branch on less than) as a synonym for BCC, as well as BGE (branch on greater or equal) as a synonym for BCS.

I'm not a fan of using CMP + BMI/BPL for signed comparisons. If the subtraction overflows, the negate bit will not be set properly, and there's no way to test if this happened or not since CMP doesn't update the overflow bit. Either use SBC instead of CMP, or test the sign bits manually and do an unsigned compare if the sign bits match.
"Last version was better," says Floyd. "More bugs. Bugs make game fun."
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

BCC and BCS were the last instructions I learned how to use properly, and they turned out to be insanely useful. Though I must admit I never used signed numbers so haven't used BMI/BPL except for checking bit 7.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Memblers wrote:BCC and BCS were the last instructions I learned how to use properly, and they turned out to be insanely useful. Though I must admit I never used signed numbers so haven't used BMI/BPL except for checking bit 7.
Same here. I don't really know too much about signed numbers, so I pretty much only use BMI/BPL to check bit 7.
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

The only time I have actually used a such thing as signed numbers was in a sound code I wrote, to handle the vibrato and pitchbend values. If I remember corectly it has given me a good headache.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

I'm sure that will be fun when I get to it. I've never really dealt with making my own sound engine. I've only tried to do stuff with NSF files, but I can see that being not such a good idea. I'd rather know how my sound engine works, and be able to adjust it when I want. I don't know if I'll be using signed numbers. That sounds like a headache I can do without.
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

Signed numbers aren't a headache at all, just compute two's complement (invert all bits and add one, more specifically xor with #$ff and increment if you prefer) and you get the negative number, ranging from -128 to 127 for one single byte. So $ff is -$01, $fe is -$02, etc... $81 is -$7f and $80 is -$80, the smaller number you can represent that way. You can add and substracts numbers without caring with their sign, you won't have problems. In fact yes, if you do something like $7e + $04, you will end up with $82, wich is an overflow since you dont want to have -$7e as a result, so that's why the overflow flag is here. To compare two signed numbers I'm not sure how to do this, but I guess it's not THAT complicated if you carefully read your opcodes's doccumentation carefully.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

This is a very old topic, I know. But I have a question that fits the topic, so no need to start a new one.

I'm working with comparing two signed numbers now. So I think in order to compare signed numbers to see if one's greater than the other, you'd do:

lda A
cmp B
bpl GreaterThan

This seems like it'd work, but it won't work with something like this:

lda #$80 (-127)
cmp #$20 (32)
bpl GreaterThan

This branches. Because $80-$20 = $60, in which case bit 7 is not set, the code is allowed to branch. Obviously, -127 is not greater than 32. So is there something I'm missing here? I've never really dealt with signed numbers, but I see that they're really handy so I'll have to learn how to effectively compare them. Thanks in advance.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Heh, now you get to learn the most obscure instructions that test the overflow flag: BVC and BVS. Overflow is the true signed equivalent to the carry flag. Er and you must use ADC or SBC, since only they set/clear the overflow flag based on the result.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Okay, sorry, I found some info with google (I know, I know, just f-ing google it XD). It seems like you could do signed comparisons with BVC and BVS, but another way, though it may be time wasting, is to just convert signed numbers to unsigned numbers to compare them. You can do:

lda Num1
eor #$80
sta TempVar
lda Num2
eor #$80
cmp TempVar

It's at least less thinking.
doynax
Posts: 162
Joined: Mon Nov 22, 2004 3:24 pm
Location: Sweden
Contact:

Post by doynax »

Celius wrote:Okay, sorry, I found some info with google (I know, I know, just f-ing google it XD). It seems like you could do signed comparisons with BVC and BVS, but another way, though it may be time wasting, is to just convert signed numbers to unsigned numbers to compare them. You can do:

lda Num1
eor #$80
sta TempVar
lda Num2
eor #$80
cmp TempVar

It's at least less thinking.
Yeah, this works nicely. It's especially fast if you can store one or both of the values in biased form to begin with (such as when comparing to a constant.)

Nevertheless the "proper" way to do signed comparisons comes in handy every once in a while. The idea here is to check whether the overflow and sign flags differ from each other or not. Unfortunately the naive implementation through a set of branches results in some rather unmanageable control flow, though it's the fastest way if you can afford to duplicate some code. I was taught to use this cute trick instead:

Code: Select all

	lda a
	sec
	sbc b
	bvc *+4
	eor #$80
	bmi less
Admittedly I can just barely manage to remember what the carry flag is supposed to mean, so I always end up having to work out just what BMI means but aside from that it's hard to get it wrong. Besides, you could always write it down..
By the way, 6502.org has rather nice tutorial on this and many other details and tricks involving comparisons.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

OK, I got this figured out. The first way avoids using overflow. It's the easiest to understand. The next uses overflow by adding a signed value that would cause overflow in the interesting case (ADC/SBC set/clear the overflow flag when the signed result would be outside the -128 to +127 range a signed 8-bit value can hold). Finally, some ca65 macros to encapsulate the method.

Code: Select all

; Avoiding overflow flag:

; If A >= n, branch to GE, where n < 0
	cmp #0 ; can eliminate if you last operation was on A
    bpl GE
    cmp #n
    bcc GE
    ...

; If A < n, branch to LT, where n >= 0
	cmp #0 ; can eliminate if you last operation was on A
    bmi LT
    cmp #n
    bcc LT
    ...

; Using overflow flag:

; If A >= n, branch to GE, where n < 0
    sec
    sbc #n - -128
    bvc GE
    ...

; If A < n, branch to GE, where n > 0
    clc
    adc #128 - n
    bvs GE
    ...

; ca65 macros:

; If A >= n, branches to label. A and n are 8-bit signed.
; Preserved: X, Y
.macro scmp_bge n,label
    .if (n) < 0
        sec
        sbc #(n) - -128
        bvc label
    .elseif (n) = 0
        cmp #0
        bpl label
    .else
        clc
        adc #128 - (n)
        bvs label
    .endif
.endmacro

; If A < n, branches to label. A and n are 8-bit signed.
; Preserved: X, Y
.macro scmp_blt n,label
    .if (n) < 0
        sec
        sbc #(n) - -128
        bvs label
    .elseif (n) = 0
        cmp #0
        bmi label
    .else
        clc
        adc #128 - (n)
        bvc label
    .endif
.endmacro
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Thanks for the responses guys. I'll study what you were saying blargg. I am actually going to create a nice macro for this (I've created macros for lots of other branch conditions with WLA-DX), so I don't have to think about typing it in. It's obviously more intuitive to write something like:

checkAgreaterthanBsigned(A,B,Label)

Where it branches to "Label" if A is greater than B. Though I have a system where I shorten the words to:

csAgtB

Which stands for: check (signed) if A is greater than B. I have these handy macros for 16 bit comparisons where I can just quickly type that in without thinking (and sometimes, I get confused with comparisons, so I'll sit there and waste coding time thinking about how to compare two numbers).
Post Reply