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.
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.
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
.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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).
tokumaru wrote:
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).
Does this mean you have to set the carry before using the CMP instruction? I swear, everywhere I see:
lda Blah
cmp Something
bcs/bcc SomeLabel
With no SEC before it.
You don't need the SEC... I said CMP *assumes* the carry is set but that was probably a bad word to use. CMP doesn't expect you to set the carry, it just operates as if it was always set (i.e. CMP works the same regardless of the carry flag). This is why you can't use it to compare 16-bit (or larger) numbers, unless you have a branch instruction after comparing each byte.
Ahhh, so that's how you conveniently compare 16-bit values:
Code:
lda i
cmp j ; = SEC then SBC j
lda i+1
sbc j+1
Carry set if i >= j.
blargg wrote:
Ahhh, so that's how you conveniently compare 16-bit values:
This is the way to do it without branches. Only the first byte of a multi-byte number can be compared with CMP when you do it like this. The alternative to your code, using only CMP, is this:
Code:
lda i+1
cmp j+1
bne done
lda i
cmp j
done:
Which is not bad, because if the highest byte alone can tell which number is larger, you don't even need to compare the lower bytes.
EDIT: Deleted incorrect information.
Okay, thanks for clearing that up. I just thought you meant it assumes that it's set, meaning you set it before, in the same way SBC assumes that you set it. This is really good news because I have a loop comparing numbers, and it would really add up with all those SECs in there.
To compare two 16 bit numbers, I do this:
Code:
;Checks if A is greater than B. If true, go to "IfTrue"
;AH - A High
;AL - A Low
;BH - B High
;BL - B Low
lda AH
cmp BH
bcc ++
beq +
bcs IfTrue
+
lda AL
cmp BL
beq ++
bcs IfTrue
++
It first checks the high bytes of the values to save the most time, because if they're not equal, it's really easy to tell if A is greater than B. It might take less time to see if B is less than A, though.
Celius wrote:
It might take less time to see if B is less than A, though.
The following code does the same thing as yours, but is shorter:
Code:
lda BH
cmp AH
bcc IfTrue
bne +
lda BL
cmp AL
bcc IfTrue
+
Sometimes it is faster/easier to check for the opposite conditions.
Yeah, I figured it would to check for the opposite. It's simply because the carry is set when A is greater than or equal to B, so you have to check for that. If you check if A is less than B, you can get a result a lot quicker because you know it's not true when the carry is set.