It is currently Thu Oct 18, 2018 10:40 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Comparison Question
PostPosted: Mon Nov 12, 2007 9:04 pm 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2150
Location: Minneapolis, Minnesota, United States
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:
 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.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 12, 2007 10:03 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10900
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 12, 2007 11:44 pm 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2150
Location: Minneapolis, Minnesota, United States
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 :) .


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 13, 2007 8:50 am 
Offline

Joined: Wed Mar 22, 2006 8:00 am
Posts: 354
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."


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 13, 2007 3:01 pm 
Offline
Site Admin
User avatar

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3569
Location: Indianapolis
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.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 13, 2007 4:00 pm 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2150
Location: Minneapolis, Minnesota, United States
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.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 14, 2007 9:41 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7548
Location: Chexbres, VD, Switzerland
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.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 14, 2007 10:06 am 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2150
Location: Minneapolis, Minnesota, United States
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.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 14, 2007 10:40 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7548
Location: Chexbres, VD, Switzerland
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.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 05, 2008 1:49 am 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2150
Location: Minneapolis, Minnesota, United States
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.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 05, 2008 12:19 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
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.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 05, 2008 11:22 pm 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2150
Location: Minneapolis, Minnesota, United States
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.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Dec 06, 2008 10:08 am 
Offline

Joined: Mon Nov 22, 2004 3:24 pm
Posts: 162
Location: Sweden
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:
   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.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Dec 06, 2008 10:16 am 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
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:
; 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


Top
 Profile  
 
 Post subject:
PostPosted: Sat Dec 06, 2008 1:21 pm 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2150
Location: Minneapolis, Minnesota, United States
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).


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 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:  
Powered by phpBB® Forum Software © phpBB Group