It is currently Thu Aug 16, 2018 5:25 am

 All times are UTC - 7 hours

 Page 1 of 2 [ 22 posts ] Go to page 1, 2  Next
 Print view Previous topic | Next topic
Author Message
 Post subject: Comparison QuestionPosted: Mon Nov 12, 2007 9:04 pm

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2149
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

 Post subject: Posted: Mon Nov 12, 2007 10:03 pm

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10708
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

 Post subject: Posted: Mon Nov 12, 2007 11:44 pm

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2149
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

 Post subject: Posted: Tue Nov 13, 2007 8:50 am

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

 Post subject: Posted: Tue Nov 13, 2007 3:01 pm

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3541
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

 Post subject: Posted: Tue Nov 13, 2007 4:00 pm

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2149
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

 Post subject: Posted: Wed Nov 14, 2007 9:41 am

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7469
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

 Post subject: Posted: Wed Nov 14, 2007 10:06 am

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2149
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

 Post subject: Posted: Wed Nov 14, 2007 10:40 am

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7469
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

 Post subject: Posted: Fri Dec 05, 2008 1:49 am

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2149
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

 Post subject: Posted: Fri Dec 05, 2008 12:19 pm

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

 Post subject: Posted: Fri Dec 05, 2008 11:22 pm

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2149
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

 Post subject: Posted: Sat Dec 06, 2008 10:08 am

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

 Post subject: Posted: Sat Dec 06, 2008 10:16 am

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
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
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
bvc label
.endif
.endmacro

Top

 Post subject: Posted: Sat Dec 06, 2008 1:21 pm

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2149
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

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 1 of 2 [ 22 posts ] Go to page 1, 2  Next

 All times are UTC - 7 hours

#### Who is online

Users browsing this forum: Majestic-12 [Bot] and 2 guests

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

Search for:
 Jump to:  Select a forum ------------------ NES / Famicom    NESdev    NESemdev    NES Graphics    NES Music    Homebrew Projects       2018 NESdev Competition       2017 NESdev Competition       2016 NESdev Competition       2014 NESdev Competition       2011 NESdev Competition    Newbie Help Center    NES Hardware and Flash Equipment       Reproduction    NESdev International       FCdev       NESdev China       NESdev Middle East Other    General Stuff    Membler Industries    Other Retro Dev       SNESdev       GBDev    Test Forum Site Issues    phpBB Issues    Web Issues    nesdevWiki